[
  {
    "path": ".clang-tidy",
    "content": "# All rules should have a comment associated\n# Directives that do not have any effect (e.g. disabling a rule that is not enabled) can be done to add an explanation comment. \n# Or at least an empty comment # to show they were put here explicitely,\n# and not as part of the historical CLion-generated rules\n# Note: `- -X` means disable X\n# CLI usage: go to the build directory and run: `run-clang-tidy -allow-no-checks -source-filter \".*/lib/.*\" -fix -j`\n\nChecks:\n- -*\n- mpi-*\n- bugprone-*\n- -bugprone-signal-handler\n- -bugprone-narrowing-conversions\n- -bugprone-redundant-branch-condition\n- -bugprone-exception-escape\n- -bugprone-shared-ptr-array-mismatch\n- -bugprone-implicit-widening-of-multiplication-result\n- -bugprone-signed-char-misuse\n- -bugprone-unhandled-exception-at-new\n- -bugprone-infinite-loop\n- -bugprone-easily-swappable-parameters\n- -bugprone-float-loop-counter #\n- -bugprone-unchecked-string-to-number-conversion # Unfortunately no alternative\n- -bugprone-branch-clone # Mostly warns about one-line duplicates\n- cert-err52-cpp\n- cert-err60-cpp\n- cert-str34-c\n- cert-dcl21-cpp\n- cert-msc50-cpp\n- cert-msc51-cpp\n- cert-dcl58-cpp\n- cppcoreguidelines-avoid-const-or-ref-data-members\n- cppcoreguidelines-pro-type-member-init # We want to use default member initializers\n- cppcoreguidelines-slicing\n- cppcoreguidelines-interfaces-global-init\n- -cppcoreguidelines-pro-type-static-cast-downcast # dynamic_cast has a runtime overhead\n- -cppcoreguidelines-narrowing-conversions #\n- google-runtime-operator\n- google-explicit-constructor\n- -google-default-arguments # Provider and ViewProvider read() is a good example of why this is useful\n- hicpp-multiway-paths-covered\n- hicpp-exception-baseclass\n- misc-*\n- -misc-definitions-in-headers\n- -misc-unused-parameters\n- -misc-unused-alias-decls\n- -misc-use-anonymous-namespace\n- -misc-misleading-identifier\n- -misc-confusable-identifiers\n- -misc-misleading-bidirectional\n- -misc-static-assert\n- -misc-no-recursion\n- -misc-const-correctness\n- -misc-use-internal-linkage # False positives if header where function is defined is not included\n- -misc-include-cleaner # Allow indirect includes\n- -misc-non-private-member-variables-in-classes #\n- modernize-*\n- -modernize-use-trailing-return-type\n- -modernize-use-std-print # We want to use fmt::print instead\n- -modernize-use-integer-sign-comparison # Too much occurrences to change\n- openmp-use-default-none\n- performance-*\n- -performance-no-int-to-ptr\n- portability-*\n- -portability-restrict-system-includes\n- readability-*\n- -readability-redundant-preprocessor\n- -readability-named-parameter\n- -readability-function-size\n- -readability-use-anyofallof\n- -readability-identifier-length\n- -readability-magic-numbers\n- -readability-braces-around-statements\n- -readability-suspicious-call-argument\n- -readability-isolate-declaration\n- -readability-else-after-return\n- -readability-redundant-access-specifiers\n- -readability-function-cognitive-complexity\n- -readability-identifier-naming\n- -readability-qualified-auto\n- -readability-use-std-min-max # Less readable imo\n- -readability-math-missing-parentheses # Basic math\n- -readability-implicit-bool-conversion # Not much of a problem ?\n- -readability-convert-member-functions-to-static #\n- -readability-use-concise-preprocessor-directives # We do not use #ifdef\n- -readability-uppercase-literal-suffix # Not important enough\n- -readability-redundant-string-cstr # Sometimes used to stop at first null byte\n- -readability-static-accessed-through-instance #\n- -readability-ambiguous-smartptr-reset-call # Fix is hard to read\n\n# Will fix later\n- -modernize-avoid-c-arrays\n- -readability-make-member-function-const # idk + lots of occurences\n- -readability-misleading-indentation # We need to handle cases with #if defined()\n- -bugprone-unchecked-optional-access\n- -performance-unnecessary-value-param # idk\n- -readability-avoid-nested-conditional-operator\n"
  },
  {
    "path": ".dockerignore",
    "content": "cmake-build-*/\nbuild*/\n\nlocal/\n**/Dockerfile\n"
  },
  {
    "path": ".gdbinit",
    "content": "# Skip all std:: and __gnu_debug:: symbols\nskip -rfu ^std::\nskip -rfu ^__gnu_debug::\n\n# Skip all ImGui:: symbols\nskip -rfu ^ImGui::\n\n# Trigger breakpoint when execution reaches triggerSafeShutdown()\nbreak triggerSafeShutdown\nbreak __glibcxx_assert_fail\n\n# Print backtrace after execution jumped to an invalid address\ndefine fixbt\n    set $pc = *(void **)$rsp\n    set $rsp = $rsp + 8\n    bt\nend"
  },
  {
    "path": ".gitattributes",
    "content": "lib/external/** linguist-vendored\n\ndist/*.sh eol=lf\ndist/**/*Dockerfile eol=lf"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# Sponsor links\n\ngithub: WerWolv\nko_fi: WerWolv\ncustom: \"https://werwolv.net/donate\"\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yaml",
    "content": "name: Bug Report\r\ndescription: Something doesn't work correctly in ImHex.\r\ntitle: \"[Bug] \"\r\nlabels: bug\r\nbody:\r\n- type: dropdown\r\n  id: os\r\n  attributes:\r\n    label: Operating System\r\n    description: What Operating System are you using ImHex on?\r\n    options:\r\n      - Windows\r\n      - Linux\r\n      - MacOS\r\n  validations:\r\n    required: true\r\n- type: textarea\r\n  attributes:\r\n    label: What's the issue you encountered?\r\n    description: |\r\n      Describe the issue in detail and what you were doing beforehand.\r\n  validations:\r\n    required: true\r\n- type: textarea\r\n  attributes:\r\n    label: How can the issue be reproduced?\r\n    description: Include a detailed step by step process for recreating your issue.\r\n  validations:\r\n    required: true\r\n- type: input\r\n  attributes:\r\n    label: ImHex Version\r\n    description: |\r\n      The version of ImHex you've been using when encountering the bug. If using a nightly, please add the commit hash as well\r\n    placeholder: X.X.X\r\n  validations:\r\n    required: true\r\n- type: checkboxes\r\n  attributes:\r\n    label: ImHex Build Type\r\n    options:\r\n    - label: Nightly or built from sources\r\n- type: input\r\n  attributes:\r\n    label: Installation type\r\n    description: |\r\n      How did you install ImHex ? MSI/Portable/DMG/AppImage/Fedora package/etc..\r\n  validations:\r\n    required: true\r\n- type: textarea\r\n  attributes:\r\n    label: Additional context?\r\n    placeholder: |\r\n      - Additional information about your environment.\r\n      - If possible and useful, please upload the binary you've been editing when the bug occurred.\r\n  validations:\r\n    required: false\r\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yaml",
    "content": "name: Feature Request\r\ndescription: Something you'd like to see added to ImHex in the future\r\ntitle: \"[Feature] \"\r\nlabels: feature request\r\nbody:\r\n- type: textarea\r\n  attributes:\r\n    label: What feature would you like to see?\r\n    description: |\r\n      Describe in detail what the feature should do and how it should work.\r\n  validations:\r\n    required: true\r\n- type: textarea\r\n  attributes:\r\n    label: How will this feature be useful to you and others?\r\n    description: Describe how everybody will benefit from this feature if it gets added.\r\n  validations:\r\n    required: true\r\n- type: checkboxes\r\n  attributes:\r\n    label: Request Type\r\n    options:\r\n      - label: I can provide a PoC for this feature or am willing to work on it myself and submit a PR\r\n- type: textarea\r\n  attributes:\r\n    label: Additional context?\r\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nPlease provide as much information as possible about what your PR aims to do.\nPRs with no description will most likely be closed until more information is provided.\nIf you're planing on changing fundamental behaviour or add big new features, please open a GitHub Issue first before starting to work on it.\nIf it's not something big and you still want to contact us about it, feel free to do so !\n-->\n\n### Problem description\n<!-- Describe the bug that you fixed/feature request that you implemented, or link to an existing issue describing it -->\n\n### Implementation description\n<!-- Explain what you did to correct the problem -->\n\n### Screenshots\n<!-- If your change is visual, take a screenshot showing it. Ideally, make before/after sceenshots -->\n\n### Additional things\n<!-- Anything else you would like to say -->\n"
  },
  {
    "path": ".github/codecov.yml",
    "content": "comment: false\nignore:\n  - \"lib/third_party\" # Third party libraries should be ignored\n  - \"lib/external\" # Our own libraries should be checked in their own repositories\n  - \"tests\" # https://about.codecov.io/blog/should-i-include-test-files-in-code-coverage-calculations/\n"
  },
  {
    "path": ".github/scripts/delete-artifact.sh",
    "content": "#!/bin/sh\nset -xe\nARTIFACT_NAME=\"$1\"\n\nARTIFACT_ID=$(gh api repos/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID/artifacts --jq \".artifacts[] | select(.name==\\\"$ARTIFACT_NAME\\\") | .id\")\ngh api -X DELETE repos/$GITHUB_REPOSITORY/actions/artifacts/$ARTIFACT_ID\necho \"Deleted artifact $ARTIFACT_NAME with ID $ARTIFACT_ID\"\n"
  },
  {
    "path": ".github/workflows/analysis.yml",
    "content": "name: \"CodeQL\"\n\non:\n  schedule:\n    - cron: '0 0 * * *'\n  workflow_dispatch:\n\njobs:\n  codeql:\n    name: 🐛 CodeQL\n    runs-on: ubuntu-24.04\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    steps:\n    - name: 🧰 Checkout\n      uses: actions/checkout@v4\n      with:\n        submodules: recursive\n\n    - name: ✋ Initialize CodeQL\n      uses: github/codeql-action/init@v2\n      with:\n        languages: 'cpp'\n\n    - name: 📜 Setup ccache\n      uses: hendrikmuhs/ccache-action@v1\n      with:\n        key: ${{ runner.os }}-analysis-build-${{ github.run_id }}\n        restore-keys: ${{ runner.os }}-analysis-build\n        max-size: 50M\n\n    - name: 📜 Restore CMakeCache\n      uses: actions/cache@v4\n      with:\n        path: |\n          build/CMakeCache.txt\n        key: ${{ runner.os }}-analysis-build-${{ hashFiles('**/CMakeLists.txt') }}\n\n    - name: ⬇️ Install dependencies\n      run: |\n        sudo apt update\n        sudo bash dist/get_deps_debian.sh\n\n    - name: 🛠️ Build\n      run: |\n        set -x\n        mkdir -p build\n        cd build\n        CC=gcc-14 CXX=g++-14 cmake                \\\n          -DCMAKE_BUILD_TYPE=$BUILD_TYPE          \\\n          -DCMAKE_INSTALL_PREFIX=\"$PWD/install\"   \\\n          -DCMAKE_C_COMPILER_LAUNCHER=ccache      \\\n          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache    \\\n          -DCMAKE_C_FLAGS=\"-fuse-ld=lld\"          \\\n          -DCMAKE_CXX_FLAGS=\"-fuse-ld=lld\"        \\\n          -DIMHEX_PATTERNS_PULL_MASTER=ON         \\\n          -DIMHEX_EXCLUDE_PLUGINS=\"script_loader\" \\\n          -G Ninja                                \\\n          ..\n        ninja install\n\n    - name: 🗯️ Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v2"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}\n  cancel-in-progress: true\n\non:\n  push:\n    branches:\n      - 'master'\n      - 'releases/**'\n      - 'tests/**'\n      - 'feature/**'\n  pull_request:\n  workflow_dispatch:\n\nenv:\n  BUILD_TYPE: RelWithDebInfo\n\njobs:\n\n  # Windows MINGW build\n  win_mingw:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - architecture_name: \"x86_64\"\n            msystem: \"mingw64\"\n            runner_os: \"windows-2025\"\n          - architecture_name: \"arm64\"\n            msystem: \"clangarm64\"\n            runner_os: \"windows-11-arm\"\n\n    runs-on: ${{ matrix.runner_os }}\n    name: 🪟 Windows MSYS2 ${{ matrix.architecture_name }}\n\n    defaults:\n      run:\n        shell: msys2 {0}\n\n    env:\n      CCACHE_DIR:      \"${{ github.workspace }}/.ccache\"\n\n    permissions:\n      id-token: write\n      attestations: write\n\n    steps:\n    - name: 🧰 Checkout\n      uses: actions/checkout@v4\n      with:\n        submodules: recursive\n\n    - name: 📜 Setup ccache\n      uses:  hendrikmuhs/ccache-action@v1\n      id:    cache-ccache\n      with:\n        key: ${{ runner.os }}-mingw-ccache-${{ github.run_id }}\n        restore-keys: ${{ runner.os }}-mingw-ccache\n        max-size: 1G\n\n    - name: 🟦 Install msys2\n      uses: msys2/setup-msys2@v2\n      with:\n        msystem: ${{ matrix.msystem }}\n\n    - name: ⬇️ Install dependencies\n      run: |\n        set -x\n        dist/get_deps_msys2.sh\n\n    - name: ⬇️ Install .NET\n      uses: actions/setup-dotnet@v4\n      with:\n        dotnet-version: '8.0.100'\n\n    - name: 📜 Set version variable\n      run: |\n        echo \"IMHEX_VERSION=`cat VERSION`\" >> $GITHUB_ENV\n\n    # Windows cmake build\n    - name: 🛠️ Configure CMake\n      run: |\n        set -x\n        mkdir -p build\n        cd build\n\n        cmake -G \"Ninja\"                                                          \\\n          -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}                                \\\n          -DCMAKE_INSTALL_PREFIX=\"$PWD/install\"                                   \\\n          -DIMHEX_GENERATE_PACKAGE=ON                                             \\\n          -DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON                                   \\\n          -DIMHEX_PATTERNS_PULL_MASTER=ON                                         \\\n          -DIMHEX_COMMIT_HASH_LONG=\"${GITHUB_SHA}\"                                \\\n          -DIMHEX_COMMIT_BRANCH=\"${GITHUB_REF##*/}\"                               \\\n          -DUSE_SYSTEM_CAPSTONE=ON                                                \\\n          -DUSE_SYSTEM_MD4C=ON                                                    \\\n          -DIMHEX_GENERATE_PDBS=ON                                                \\\n          -DIMHEX_REPLACE_DWARF_WITH_PDB=ON                                       \\\n          -DDOTNET_EXECUTABLE=\"C:/Program Files/dotnet/dotnet.exe\"                \\\n          -DCPACK_WIX_ROOT=\"$(echo \"$USERPROFILE\" | tr '\\\\' '/')/.dotnet/tools\"   \\\n          ..\n\n    - name: 🛠️ Build\n      run: |\n        cd build\n        ninja install\n\n    - name: 🕯️ Install WiX Toolkit\n      run: |\n        \"C:/Program Files/dotnet/dotnet.exe\" tool install --global wix@6.0.2\n        \"$(echo \"$USERPROFILE\" | tr '\\\\' '/')/.dotnet/tools/wix\" extension add --global WixToolset.UI.wixext/6.0.2\n\n    - name: 🪲 Create PDBs for MSI\n      run: |\n        cd build\n        \n        mkdir cv2pdb\n        cd cv2pdb\n        wget https://github.com/rainers/cv2pdb/releases/download/v0.52/cv2pdb-0.52.zip\n        unzip cv2pdb-0.52.zip\n        cd ..\n        \n        cv2pdb/cv2pdb.exe imhex.exe\n        cv2pdb/cv2pdb.exe imhex-gui.exe\n        cv2pdb/cv2pdb.exe libimhex.dll\n        \n        for plugin in plugins/*.hexplug; do\n          cv2pdb/cv2pdb.exe $plugin\n        done\n        \n        rm -rf cv2pdb\n\n    - name: 📦 Bundle MSI\n      run: |\n        cd build\n        cpack\n        mv ImHex-*.msi ../imhex-${{ env.IMHEX_VERSION }}-Windows-${{ matrix.architecture_name }}.msi\n\n        echo \"ImHex checks for the existence of this file to determine if it is running in portable mode. You should not delete this file\" > $PWD/install/PORTABLE\n\n    - name: 🪲 Create PDBs for ZIP\n      run: |\n        cd build/install\n\n        mkdir cv2pdb\n        cd cv2pdb\n        wget https://github.com/rainers/cv2pdb/releases/download/v0.52/cv2pdb-0.52.zip\n        unzip cv2pdb-0.52.zip\n        cd ..\n        \n        cv2pdb/cv2pdb.exe imhex.exe\n        cv2pdb/cv2pdb.exe imhex-gui.exe\n        cv2pdb/cv2pdb.exe libimhex.dll\n        \n        for plugin in plugins/*.hexplug; do\n          cv2pdb/cv2pdb.exe $plugin\n        done\n        \n        rm -rf cv2pdb\n\n    - name: 🗝️ Generate build provenance attestations\n      uses: actions/attest-build-provenance@v2\n      if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }}\n      with:\n        subject-path: |\n          imhex-*.msi\n\n    - name: ⬆️ Upload Windows Installer\n      uses: actions/upload-artifact@v4\n      id: upload-installer\n      with:\n        if-no-files-found: error\n        name: Windows Installer ${{ matrix.architecture_name }}\n        path: |\n          imhex-*.msi\n\n    - name: ⬆️ Upload Portable ZIP\n      uses: actions/upload-artifact@v4\n      with:\n        if-no-files-found: error\n        name: Windows Portable ${{ matrix.architecture_name }}\n        path: |\n          build/install/*\n\n    - name: ⬇️ Download Mesa3D for NoGPU version\n      if: ${{ matrix.architecture_name == 'x86_64' }}\n      shell: bash\n      run: |\n        set -x\n        echo \"NoGPU version Powered by Mesa 3D : https://fdossena.com/?p=mesa%2Findex.frag\" > build/install/MESA.md\n        curl --connect-timeout 30 --retry 5 --retry-delay 0 --retry-max-time 30 https://downloads.fdossena.com/geth.php?r=mesa64-latest -L -o mesa.7z\n        7z e mesa.7z\n        mv opengl32.dll build/install\n\n    - name: ⬆️ Upload NoGPU Portable ZIP\n      if: ${{ matrix.architecture_name == 'x86_64' }}\n      uses: actions/upload-artifact@v4\n      with:\n        if-no-files-found: error\n        name: Windows Portable NoGPU ${{ matrix.architecture_name }}\n        path: |\n          build/install/*\n\n  win_msvc:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - architecture_name: \"x86_64\"\n            vs_arch: \"amd64\"\n            runner_os: \"windows-2025\"\n          - architecture_name: \"arm64\"\n            vs_arch: \"amd64_arm64\"\n            runner_os: \"windows-11-arm\"\n\n    runs-on: ${{ matrix.runner_os }}\n    name: 🪟 Windows MSVC ${{ matrix.architecture_name }}\n\n    env:\n      CCACHE_DIR:      \"${{ github.workspace }}/.ccache\"\n\n    permissions:\n      id-token: write\n      attestations: write\n\n    steps:\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: 🫧 Setup Visual Studio Dev Environment\n        uses: ilammy/msvc-dev-cmd@v1\n        with:\n          arch: ${{ matrix.vs_arch }}\n\n      - name: 📜 Setup ccache\n        uses:  hendrikmuhs/ccache-action@v1\n        id:    cache-ccache\n        with:\n          key: ${{ runner.os }}-msvc-${{ matrix.vs_arch }}-ccache-${{ github.run_id }}\n          restore-keys: ${{ runner.os }}-msvc-${{ matrix.vs_arch }}-ccache\n          max-size: 1G\n\n      - name: 📦 Install vcpkg\n        uses: friendlyanon/setup-vcpkg@v1\n        with: { committish: 66c0373dc7fca549e5803087b9487edfe3aca0a1 }\n\n      - name: ⬇️ Install dependencies\n        run: |\n          cp dist/vcpkg.json vcpkg.json\n\n      - name: ⬇️ Install CMake and Ninja\n        uses: lukka/get-cmake@latest\n\n      - name: ⬇️ Install .NET\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: '8.0.100'\n\n      - name: 📜 Set version variable\n        run: |\n          \"IMHEX_VERSION=$(Get-Content VERSION -Raw)\" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append\n\n      # Windows cmake build\n      - name: 🛠️ Configure CMake\n        run: |\n          mkdir -p build\n                    \n          cmake -G \"Ninja\" -B build                                                   `\n            --preset vs2022                                                           `\n            -DCMAKE_TOOLCHAIN_FILE=\"$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake\" `\n            -DCMAKE_C_COMPILER=\"$($(Get-Command cl.exe).Path)\"                        `\n            -DCMAKE_CXX_COMPILER=\"$($(Get-Command cl.exe).Path)\"                      `\n            -DCMAKE_C_COMPILER_LAUNCHER=ccache                                        `\n            -DCMAKE_CXX_COMPILER_LAUNCHER=ccache                                      `\n            -DCMAKE_INSTALL_PREFIX=\"$(Join-Path $PWD 'install')\"                      `\n            -DIMHEX_GENERATE_PACKAGE=ON                                               `\n            -DCMAKE_BUILD_TYPE=\"$env:BUILD_TYPE\"                                      `\n            -DIMHEX_PATTERNS_PULL_MASTER=ON                                           `\n            -DIMHEX_COMMIT_HASH_LONG=\"$env:GITHUB_SHA\"                                `\n            -DIMHEX_COMMIT_BRANCH=\"$($env:GITHUB_REF -replace '.*/', '')\"             `\n            -DDOTNET_EXECUTABLE=\"C:/Program Files/dotnet/dotnet.exe\"                  `\n            -DCPACK_WIX_ROOT=\"$($env:USERPROFILE -replace '\\\\','/')/.dotnet/tools\"    `\n            .\n\n      - name: 🛠️ Build\n        run: |\n          cd build\n          ninja install\n\n      - name: 🕯️ Install WiX Toolkit\n        run: |\n          & \"C:/Program Files/dotnet/dotnet.exe\" tool install --global wix@6.0.2\n          & \"$($env:USERPROFILE -replace '\\\\','/')/.dotnet/tools/wix\" extension add --global WixToolset.UI.wixext/6.0.2\n\n      - name: 📦 Bundle MSI\n        run: |\n          cd build\n          cpack\n          mv ImHex-*.msi ../imhex-${{ env.IMHEX_VERSION }}-Windows-MSVC-${{ matrix.architecture_name }}.msi\n\n      - name: 🗝️ Generate build provenance attestations\n        uses: actions/attest-build-provenance@v2\n        if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }}\n        with:\n          subject-path: |\n            imhex-*.msi\n\n      - name: ⬆️ Upload Windows Installer\n        uses: actions/upload-artifact@v4\n        if: false # The MSVC builds should not really be used, they're still packaged for testing’s sake though\n        with:\n          if-no-files-found: error\n          name: Windows Installer MSVC ${{ matrix.architecture_name }}\n          path: |\n            imhex-*.msi\n\n  win-plugin-template-test:\n    runs-on: windows-2022\n    name: 🧪 Plugin Template Test\n\n    defaults:\n      run:\n        shell: msys2 {0}\n\n    needs: win_mingw\n\n    env:\n      IMHEX_SDK_PATH: \"${{ github.workspace }}/out/sdk\"\n\n    steps:\n      - name: 🧰 Checkout ImHex\n        uses: actions/checkout@v4\n        with:\n          path: imhex\n\n      - name: 🟦 Install msys2\n        uses: msys2/setup-msys2@v2\n        with:\n          msystem: mingw64\n\n      - name: ⬇️ Install dependencies\n        run: |\n          set -x\n          imhex/dist/get_deps_msys2.sh\n\n      - name: 🧰 Checkout ImHex-Plugin-Template\n        uses: actions/checkout@v4\n        with:\n          repository: WerWolv/ImHex-Plugin-Template\n          submodules: recursive\n          path: template\n\n      - name: ⬇️ Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: Windows Portable x86_64\n          path: out\n\n      - name: 🛠️ Build\n        run: |\n          set -x\n          cd template\n          mkdir -p build\n          cd build\n  \n          cmake -G \"Ninja\"                                          \\\n          -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}                  \\\n          -DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON                     \\\n          -DUSE_SYSTEM_CAPSTONE=ON                                  \\\n          -DUSE_SYSTEM_MD4C=ON                                      \\\n          ..\n          \n          ninja\n\n      - name: 🧪 Test if plugin can be loaded\n        run: |\n          export WORKSPACE=$(echo \"${{ github.workspace }}\" | tr '\\\\' '/')\n          ${WORKSPACE}/out/imhex.exe --validate-plugin ${WORKSPACE}/template/build/example_plugin.hexplug\n\n  # MacOS build\n  macos-x86:\n    runs-on: macos-15-intel\n\n    permissions:\n      id-token: write\n      attestations: write\n\n    name: 🍎 macOS 10.15 x86_64\n\n    steps:\n    - name: 🧰 Checkout\n      uses: actions/checkout@v4\n      with:\n        submodules: recursive\n\n    - name: 📜 Set version variable\n      run: |\n        echo \"IMHEX_VERSION=`cat VERSION`\" >> $GITHUB_ENV\n\n    - name: 📜 Setup ccache\n      uses: hendrikmuhs/ccache-action@v1\n      with:\n        key: ${{ runner.os }}-ccache-${{ github.run_id }}\n        restore-keys: ${{ runner.os }}-ccache\n        max-size: 1G\n\n    - name: Set Xcode version\n      run: |\n        sudo xcode-select --install || true\n        sudo xcode-select -s /Library/Developer/CommandLineTools\n\n    - name: 📦 Install MacPorts\n      run: |\n        wget https://github.com/macports/macports-base/releases/download/v2.11.6/MacPorts-2.11.6-15-Sequoia.pkg\n        sudo installer -pkg MacPorts-2.11.6-15-Sequoia.pkg -target /\n        export PATH=/opt/local/bin:/opt/local/sbin:$PATH\n        echo \"PATH=/opt/local/bin:/opt/local/sbin:$PATH\" >> $GITHUB_ENV\n        echo \"MACOSX_DEPLOYMENT_TARGET=10.15\" >> $GITHUB_ENV\n        echo \"universal_target  10.15\" | sudo tee -a /opt/local/etc/macports/macports.conf\n        echo \"macos_deployment_target  10.15\" | sudo tee -a /opt/local/etc/macports/macports.conf\n        echo \"macosx_sdk_version  10.15\" | sudo tee -a /opt/local/etc/macports/macports.conf\n        sudo port selfupdate\n\n    - name: ⬇️ Install dependencies\n      env:\n        # Make brew not display useless errors\n        HOMEBREW_TESTS: 1\n      run: |\n        brew install llvm@21 automake\n        sudo -E port install mbedtls3 nlohmann-json ccache freetype libmagic pkgconfig curl glfw ninja zlib xz bzip2 zstd libssh2 md4c\n\n    - name: ⬇️ Install .NET\n      uses: actions/setup-dotnet@v4\n      with:\n        dotnet-version: '8.0.100'\n\n    # MacOS cmake build\n    - name: 🛠️ Configure CMake\n      run: |\n        set -x\n        mkdir -p build\n        cd build\n        CC=$(brew --prefix llvm@21)/bin/clang                                                       \\\n        CXX=$(brew --prefix llvm@21)/bin/clang++                                                    \\\n        OBJC=$(brew --prefix llvm@21)/bin/clang                                                     \\\n        OBJCXX=$(brew --prefix llvm@21)/bin/clang++                                                 \\\n        cmake -G \"Ninja\"                                                                            \\\n          -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15                                                       \\\n          -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}                                                  \\\n          -DIMHEX_GENERATE_PACKAGE=ON                                                               \\\n          -DIMHEX_SYSTEM_LIBRARY_PATH=\"$(brew --prefix llvm@21)/lib;$(brew --prefix llvm@21)/lib/unwind;$(brew --prefix llvm@21)/lib/c++;$(brew --prefix)/lib\" \\\n          -DCMAKE_C_COMPILER_LAUNCHER=ccache                                                        \\\n          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache                                                      \\\n          -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache                                                     \\\n          -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache                                                   \\\n          -DCMAKE_INSTALL_PREFIX=\"./install\"                                                        \\\n          -DIMHEX_PATTERNS_PULL_MASTER=ON                                                           \\\n          -DIMHEX_COMMIT_HASH_LONG=\"${GITHUB_SHA}\"                                                  \\\n          -DIMHEX_COMMIT_BRANCH=\"${GITHUB_REF##*/}\"                                                 \\\n          ..\n\n    - name: 🛠️ Build\n      run: cd build && ninja install\n\n    - name: ✒️ Fix Signature\n      run: |\n        set -x\n        cd build/install\n        codesign --remove-signature ImHex.app\n        codesign --force --deep --sign - ImHex.app\n\n    - name: 📁 Fix permissions\n      run: |\n        set -x\n        cd build/install\n        chmod -R 755 ImHex.app/\n\n    - name: 🔫 Kill XProtect\n      run: |\n        # See https://github.com/actions/runner-images/issues/7522\n        echo Killing XProtect...; sudo pkill -9 XProtect >/dev/null || true;\n        echo Waiting for XProtect process...; while pgrep XProtect; do sleep 3; done;\n\n    - name: 📦 Create DMG\n      run: |\n        brew install graphicsmagick imagemagick\n        git clone https://github.com/sindresorhus/create-dmg\n        cd create-dmg\n        npm i && npm -g i\n        cd ../build/install\n        for i in $(seq 1 10); do\n          create-dmg ImHex.app || true\n          if ls -d *.dmg 1>/dev/null 2>/dev/null; then\n            break;\n          fi\n        done\n        mv *.dmg ../../imhex-${{ env.IMHEX_VERSION }}-macOS-x86_64.dmg\n\n    - name: 🗝️ Generate build provenance attestations\n      uses: actions/attest-build-provenance@v2\n      if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }}\n      with:\n        subject-path: |\n          ./*.dmg\n\n    - name: ⬆️ Upload DMG\n      uses: actions/upload-artifact@v4\n      with:\n        if-no-files-found: error\n        name: macOS DMG x86_64\n        path: ./*.dmg\n\n  macos-arm64:\n    runs-on: ubuntu-24.04\n    name: 🍎 macOS 11 arm64\n\n    outputs:\n      IMHEX_VERSION: ${{ steps.build.outputs.IMHEX_VERSION }}\n\n    steps:\n    - name: 🧰 Checkout\n      uses: actions/checkout@v4\n      with:\n        submodules: recursive\n\n    - name: 📁 Restore docker /cache\n      uses: actions/cache@v4\n      with:\n        path: cache\n        key: macos-arm64-cache-${{ github.run_id }}\n        restore-keys: macos-arm64-cache\n\n    - name: 🐳 Inject /cache into docker\n      uses: reproducible-containers/buildkit-cache-dance@v2\n      with:\n        cache-source: cache\n        cache-target: /cache\n\n    - name: 🛠️ Build using docker\n      id: build\n      run: |\n        echo \"IMHEX_VERSION=`cat VERSION`\" >> $GITHUB_OUTPUT\n        docker buildx build . -f dist/macOS/arm64.Dockerfile --progress=plain --build-arg 'JOBS=4' --build-arg \"BUILD_TYPE=$(BUILD_TYPE)\" --build-context imhex=$(pwd) --output out\n        cp resources/dist/macos/Entitlements.plist out/Entitlements.plist\n\n    - name: ⬆️ Upload artifacts\n      uses: actions/upload-artifact@v4\n      with:\n        name: macos_arm64_intermediate\n        path: out/\n\n      # See https://github.com/actions/cache/issues/342#issuecomment-1711054115\n    - name: 🗑️ Delete old cache\n      continue-on-error: true\n      env:\n        GH_TOKEN: ${{ github.token }}\n      run: |\n          gh extension install actions/gh-actions-cache\n          gh cache delete \"macos-arm64-cache\" --confirm || true\n\n  macos-arm64-package:\n    runs-on: macos-15-intel\n    name: 🍎 macOS 11 arm64 Packaging\n    needs: macos-arm64\n\n    env:\n      IMHEX_VERSION: ${{ needs.macos-arm64.outputs.IMHEX_VERSION }}\n\n    permissions:\n      id-token: write\n      attestations: write\n\n    steps:\n      - name: ⬇️ Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: macos_arm64_intermediate\n          path: out\n\n      - name: 🗑️ Delete artifact\n        uses: geekyeggo/delete-artifact@v5\n        with:\n          name: macos_arm64_intermediate\n\n      - name: ✒️ Fix Signature\n        run: |\n          set -x\n          cd out\n          codesign --remove-signature ImHex.app\n          codesign --force --deep --entitlements Entitlements.plist --sign - ImHex.app\n\n      - name: 📁 Fix permissions\n        run: |\n          set -x\n          cd out\n          chmod -R 755 ImHex.app/\n\n      - name: 🔫 Kill XProtect\n        run: |\n          # See https://github.com/actions/runner-images/issues/7522\n          echo Killing XProtect...; sudo pkill -9 XProtect >/dev/null || true;\n          echo Waiting for XProtect process...; while pgrep XProtect; do sleep 3; done;\n\n      - name: 📦 Create DMG\n        run: |\n          brew install graphicsmagick imagemagick\n          git clone https://github.com/sindresorhus/create-dmg\n          cd create-dmg\n          npm i && npm -g i\n          cd ../out\n          for i in $(seq 1 10); do\n            create-dmg ImHex.app || true\n            if ls -d *.dmg 1>/dev/null 2>/dev/null; then\n              break;\n            fi\n          done\n          mv *.dmg ../imhex-${{ env.IMHEX_VERSION }}-macOS${{ matrix.file_suffix }}-arm64.dmg\n\n      - name: 🗝️ Generate build provenance attestations\n        uses: actions/attest-build-provenance@v2\n        if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }}\n        with:\n          subject-path: |\n            ./*.dmg\n\n      - name: ⬆️ Upload DMG\n        uses: actions/upload-artifact@v4\n        with:\n          if-no-files-found: error\n          name: macOS DMG arm64\n          path: ./*.dmg\n\n  # Ubuntu build\n  ubuntu:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - name: \"Ubuntu\"\n            release_num: \"24.04\"\n            image: \"ubuntu:24.04\"\n          - name: \"Ubuntu\"\n            release_num: \"25.10\"\n            image: \"ubuntu:25.10\"\n          - name: \"Debian\"\n            release_num: \"13\"\n            image: \"debian:13\"\n\n    name: 🐧 ${{ matrix.name }} ${{ matrix.release_num }} x86_64\n    runs-on: ubuntu-24.04\n\n    container:\n      image: \"${{ matrix.image }}\"\n      options: --privileged\n\n    permissions:\n      id-token: write\n      attestations: write\n\n    steps:\n      - name: ⬇️ Install setup dependencies\n        run: apt update && apt install -y git curl\n\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: 📜 Setup ccache\n        uses: hendrikmuhs/ccache-action@v1\n        with:\n          key: ${{ matrix.image }}-ccache-${{ github.run_id }}\n          restore-keys: ${{ matrix.image }}-ccache\n          max-size: 1G\n\n      - name: ⬇️ Install dependencies\n        run: |\n          apt update\n          bash dist/get_deps_debian.sh\n\n      - name: ⬇️ Install .NET\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: '8.0.100'\n\n      # Ubuntu cmake build\n      - name: 🛠️ Configure CMake\n        shell: bash\n        run: |\n          set -x\n          git config --global --add safe.directory '*'\n          mkdir -p build\n          cd build\n          CC=gcc-14 CXX=g++-14 cmake -G \"Ninja\"                       \\\n            -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}                  \\\n            -DCMAKE_INSTALL_PREFIX=\"/usr\"                             \\\n            -DCMAKE_C_COMPILER_LAUNCHER=ccache                        \\\n            -DCMAKE_CXX_COMPILER_LAUNCHER=ccache                      \\\n            -DIMHEX_PATTERNS_PULL_MASTER=ON                           \\\n            -DIMHEX_COMMIT_HASH_LONG=\"${GITHUB_SHA}\"                  \\\n            -DIMHEX_COMMIT_BRANCH=\"${GITHUB_REF##*/}\"                 \\\n            -DIMHEX_ENABLE_LTO=ON                                     \\\n            -DIMHEX_USE_GTK_FILE_PICKER=ON                            \\\n            -DDOTNET_EXECUTABLE=\"dotnet\"                              \\\n            ..\n\n      - name: 🛠️ Build\n        run: cd build && DESTDIR=DebDir ninja install\n\n      - name: 📜 Set version variable\n        run: |\n          echo \"IMHEX_VERSION=`cat VERSION`\" >> $GITHUB_ENV\n\n      - name: 📦 Bundle DEB\n        run: |\n          cp -r build/DEBIAN build/DebDir\n          dpkg-deb -Zzstd --build build/DebDir\n          mv build/DebDir.deb imhex-${{ env.IMHEX_VERSION }}-${{ matrix.name }}-${{ matrix.release_num }}-x86_64.deb\n\n      - name: 🗝️ Generate build provenance attestations\n        uses: actions/attest-build-provenance@v2\n        if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }}\n        with:\n          subject-path: |\n            ./*.deb\n\n      - name: ⬆️ Upload DEB\n        uses: actions/upload-artifact@v4\n        with:\n          if-no-files-found: error\n          name: ${{ matrix.name }} ${{ matrix.release_num }} DEB x86_64\n          path: '*.deb'\n\n  # AppImage build\n  appimage:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - architecture: \"x86_64\"\n            architecture_package: \"amd64\"\n            architecture_appimage_builder: \"x86_64\"\n            image: ubuntu-24.04\n          - architecture: \"arm64\"\n            architecture_package: \"arm64\"\n            architecture_appimage_builder: \"aarch64\"\n            image: ubuntu-24.04-arm\n\n    runs-on: ${{ matrix.image }}\n    name: ⬇️ AppImage ${{ matrix.architecture }}\n\n    permissions:\n      id-token: write\n      attestations: write\n\n    steps:\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: 📁 Restore docker /cache\n        uses: actions/cache@v4\n        with:\n          path: cache\n          key: appimage-ccache-${{ matrix.architecture }}-${{ github.run_id }}\n          restore-keys: appimage-ccache-${{ matrix.architecture }}\n\n      - name: 🐳 Inject /cache into docker\n        uses: reproducible-containers/buildkit-cache-dance@v2\n        with:\n          cache-source: cache\n          cache-target: /cache\n\n      - name: 🛠️ Build using docker\n        run: |\n          docker buildx build . -f dist/AppImage/Dockerfile --progress=plain --build-arg \"BUILD_TYPE=$BUILD_TYPE\" \\\n          --build-arg \"GIT_COMMIT_HASH=$GITHUB_SHA\" --build-arg \"GIT_BRANCH=${GITHUB_REF##*/}\" \\\n          --build-arg \"ARCHITECTURE_PACKAGE=${{ matrix.architecture_package }}\" --build-arg \"ARCHITECTURE_FILE_NAME=${{ matrix.architecture }}\" --build-arg \"ARCHITECTURE_APPIMAGE_BUILDER=${{ matrix.architecture_appimage_builder }}\" \\\n          --output out\n\n      - name: 🗝️ Generate build provenance attestations\n        uses: actions/attest-build-provenance@v2\n        if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }}\n        with:\n          subject-path: |\n            out/*.AppImage\n            out/*.AppImage.zsync\n\n      - name: ⬆️ Upload AppImage\n        uses: actions/upload-artifact@v4\n        with:\n          if-no-files-found: error\n          name: Linux AppImage ${{ matrix.architecture }}\n          path: 'out/*.AppImage'\n\n      - name: ⬆️ Upload AppImage zsync\n        uses: actions/upload-artifact@v4\n        with:\n          if-no-files-found: error\n          name: Linux AppImage zsync ${{ matrix.architecture }}\n          path: 'out/*.AppImage.zsync'\n\n  # ArchLinux build\n  archlinux-build:\n    name: 🐧 ArchLinux x86_64\n    runs-on: ubuntu-24.04\n\n    container:\n      image: archlinux:base-devel\n\n    permissions:\n      id-token: write\n      attestations: write\n\n    steps:\n      - name: ⬇️ Update all packages\n        run: |\n          pacman -Syyu --noconfirm\n\n      - name: ⬇️ Install setup dependencies\n        run: |\n          pacman -Syu git ccache --noconfirm\n\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: ⬇️ Install ImHex dependencies\n        run: |\n          dist/get_deps_archlinux.sh --noconfirm\n\n      - name: ⬇️ Install .NET\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: '8.0.100'\n\n      - name: 📜 Setup ccache\n        uses: hendrikmuhs/ccache-action@v1\n        with:\n          key: archlinux-ccache-${{ github.run_id }}\n          restore-keys: archlinux-ccache\n          max-size: 1G\n\n      # ArchLinux cmake build\n      - name: 🛠️ Configure CMake\n        run: |\n          set -x\n          mkdir -p build\n          cd build\n          CC=gcc CXX=g++ cmake -G \"Ninja\"               \\\n          -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}      \\\n          -DCMAKE_INSTALL_PREFIX=\"/usr\"                 \\\n          -DCMAKE_C_COMPILER_LAUNCHER=ccache            \\\n          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache          \\\n          -DUSE_SYSTEM_FMT=ON                           \\\n          -DUSE_SYSTEM_YARA=ON                          \\\n          -DUSE_SYSTEM_NLOHMANN_JSON=ON                 \\\n          -DUSE_SYSTEM_CAPSTONE=OFF                     \\\n          -DUSE_SYSTEM_MD4C=ON                          \\\n          -DIMHEX_PATTERNS_PULL_MASTER=ON               \\\n          -DIMHEX_COMMIT_HASH_LONG=\"${GITHUB_SHA}\"      \\\n          -DIMHEX_COMMIT_BRANCH=\"${GITHUB_REF##*/}\"     \\\n          -DIMHEX_ENABLE_LTO=ON                         \\\n          -DIMHEX_USE_GTK_FILE_PICKER=ON                \\\n          ..\n\n      - name: 🛠️ Build\n        run: cd build && DESTDIR=installDir ninja install\n\n      - name: 📜 Set version variable\n        run: |\n          echo \"IMHEX_VERSION=`cat VERSION`\" >> $GITHUB_ENV\n\n      - name: ✒️ Prepare PKGBUILD\n        run: |\n          cp dist/Arch/PKGBUILD build\n          sed -i 's/%version%/${{ env.IMHEX_VERSION }}/g' build/PKGBUILD\n\n    # makepkg doesn't want to run as root, so I had to chmod 777 all over\n      - name: 📦 Package ArchLinux .pkg.tar.zst\n        run: |\n          set -x\n          cd build\n\n          # the name is a small trick to make makepkg recognize it as the source\n          # else, it would try to download the file from the release\n          tar -cvf imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst -C installDir .\n\n          chmod -R 777 .\n\n          sudo -u nobody makepkg\n\n          # Replace the old file\n          rm imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst\n          rm *imhex-bin-debug* # rm debug package which is created for some reason\n          mv *.pkg.tar.zst imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst\n\n      - name: 🗝️ Generate build provenance attestations\n        uses: actions/attest-build-provenance@v2\n        if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }}\n        with:\n          subject-path: |\n            build/imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst\n\n      - name: ⬆️ Upload imhex-archlinux.pkg.tar.zst\n        uses: actions/upload-artifact@v4\n        with:\n          if-no-files-found: error\n          name: ArchLinux .pkg.tar.zst x86_64\n          path: |\n            build/imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst\n\n  # RPM distro builds\n  rpm-build:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - name: Fedora\n            release_num: 43\n            mock_config: fedora-43\n\n    name: 🐧 ${{ matrix.name }} ${{ matrix.release_num }} x86_64\n    runs-on: ubuntu-24.04\n\n    container:\n      image: \"almalinux:10\"\n      options: --privileged --pid=host --security-opt apparmor=unconfined\n\n    permissions:\n      id-token: write\n      attestations: write\n\n    steps:\n      # This, together with the `--pid=host --security-opt apparmor=unconfined` docker options is required to allow\n      # mock to work inside a Docker container running on Ubuntu again.\n      # GitHub seems to have enabled AppArmor on their Ubuntu CI runners which limits Docker in ways that cause\n      # programs inside it to fail.\n      # Without this, mock will throw the unhelpful error message 'Insufficient Rights'\n      # This step uses nsenter to execute commands on the host that disable AppArmor entirely.\n      - name: 🛡️ Disable AppArmor on Host\n        run: |\n          nsenter -t 1 -m -u -n -i sudo systemctl disable --now apparmor.service\n          nsenter -t 1 -m -u -n -i sudo aa-teardown || true\n          nsenter -t 1 -m -u -n -i sudo sysctl --write kernel.apparmor_restrict_unprivileged_unconfined=0\n          nsenter -t 1 -m -u -n -i sudo sysctl --write kernel.apparmor_restrict_unprivileged_userns=0\n\n      - name: ⬇️ Install git-core and EPEL repo\n        run: dnf install git-core epel-release -y\n\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          path: ImHex\n          submodules: recursive\n\n      - name: 📜 Setup DNF Cache\n        if: false # Disabled for now since it fills up the cache very quickly\n        uses: actions/cache@v4\n        with:\n          path: /var/cache/dnf\n          key: dnf-ccache-${{ matrix.mock_config }}-${{ github.run_id }}\n          restore-keys: dnf-ccache-${{ matrix.mock_config }}\n\n      - name: ⬇️ Update all packages and install dependencies\n        run: |\n          set -x\n          dnf upgrade -y\n          dnf install -y \\\n          mock \\\n          ccache\n\n      - name: ⬇️ Install .NET\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: '8.0.100'\n\n      - name: 📜 Setup ccache\n        uses: hendrikmuhs/ccache-action@v1\n        with:\n          key: ${{ matrix.mock_config }}-rpm-${{ github.run_id }}\n          restore-keys: ${{ matrix.mock_config }}-rpm\n          max-size: 1G\n\n      - name: 📜 Set version variable\n        run: |\n          echo \"IMHEX_VERSION=`cat ImHex/VERSION`\" >> $GITHUB_ENV\n\n      - name: 🗜️ Create tarball from sources with dependencies\n        run: tar --exclude-vcs -czf $GITHUB_WORKSPACE/imhex-$IMHEX_VERSION.tar.gz ImHex\n\n      - name: ✒️ Modify spec file\n        run: |\n          sed -i \\\n          -e 's/Version:        VERSION$/Version:        ${{ env.IMHEX_VERSION }}/g'  \\\n          -e 's/IMHEX_OFFLINE_BUILD=ON/IMHEX_OFFLINE_BUILD=OFF/g'                     \\\n          -e '/IMHEX_OFFLINE_BUILD=OFF/a -D IMHEX_PATTERNS_PULL_MASTER=ON \\\\'         \\\n          -e '/BuildRequires:  cmake/a BuildRequires:  git-core'                      \\\n          -e '/%files/a %{_datadir}/imhex/'                                           \\\n          $GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec\n\n      - name: 📜 Fix ccache on EL9\n        if: matrix.mock_config == 'alma+epel-9'\n        run: sed -i '/\\. \\/opt\\/rh\\/gcc-toolset-14\\/enable/a PATH=/usr/lib64/ccache:$PATH' $GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec\n\n      - name: 🟩 Copy spec file to build root\n        run: mv $GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec $GITHUB_WORKSPACE/imhex.spec\n\n      # Fedora cmake build (in imhex.spec)\n      - name: 📦 Build RPM\n        run: |\n          mock -r ${{ matrix.mock_config }}-x86_64               \\\n            --define 'debug_package %{nil}'                      \\\n            --enable-network -N -v                               \\\n            --enable-plugin=ccache                               \\\n            --plugin-option=ccache:compress=True                 \\\n            --plugin-option=ccache:max_cache_size=200M           \\\n            --plugin-option=ccache:dir=$GITHUB_WORKSPACE/.ccache \\\n            --spec $GITHUB_WORKSPACE/imhex.spec                  \\\n            --sources $GITHUB_WORKSPACE                          \\\n            --resultdir $GITHUB_WORKSPACE/results\n\n      - name: 🟩 Move and rename finished RPM\n        run: |\n          mv $GITHUB_WORKSPACE/results/imhex-${{ env.IMHEX_VERSION }}-0.*.x86_64.rpm \\\n          $GITHUB_WORKSPACE/imhex-${{ env.IMHEX_VERSION }}-${{ matrix.name }}-${{ matrix.release_num }}-x86_64.rpm\n\n      - name: 🗝️ Generate build provenance attestations\n        uses: actions/attest-build-provenance@v2\n        if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }}\n        with:\n          subject-path: |\n            imhex-${{ env.IMHEX_VERSION }}-${{ matrix.name }}-${{ matrix.release_num }}-x86_64.rpm\n\n      - name: ⬆️ Upload RPM\n        uses: actions/upload-artifact@v4\n        with:\n          if-no-files-found: error\n          name: ${{ matrix.name }} ${{ matrix.release_num }} RPM x86_64\n          path: |\n            imhex-${{ env.IMHEX_VERSION }}-${{ matrix.name }}-${{ matrix.release_num }}-x86_64.rpm\n\n  snap-build:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - architecture: \"x86_64\"\n            image: ubuntu-24.04\n          - architecture: \"arm64\"\n            image: ubuntu-24.04-arm\n    name: 🐧 Snap ${{ matrix.architecture }}\n    runs-on: ${{ matrix.image }}\n\n    permissions:\n      id-token: write\n      attestations: write\n    steps:\n      - name: ⬇️ Install setup dependencies\n        run: |\n          sudo apt update && sudo apt install -y git curl snapd ccache\n          \n          for i in $(seq 1 5); do\n            if sudo snap install snapcraft --classic; then\n              break;\n            fi\n            echo \"Retrying snap install...\"\n            sleep 10\n          done\n\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: 📜 Set version variable\n        run: |\n          export IMHEX_VERSION=$(cat VERSION)\n          echo \"IMHEX_VERSION=$IMHEX_VERSION\" >> $GITHUB_ENV\n          \n          if [[ \"$IMHEX_VERSION\" == *.WIP ]]; then\n              echo \"IMHEX_VERSION_STRING=$IMHEX_VERSION-${GITHUB_RUN_NUMBER}\" >> $GITHUB_ENV\n          else\n              echo \"IMHEX_VERSION_STRING=$IMHEX_VERSION\" >> $GITHUB_ENV\n          fi\n          echo \"CCACHE=ccache\" >> $GITHUB_ENV\n\n      - name: 📜 Move snap directory to root\n        run: |\n          mkdir -p ./snap\n          envsubst '${IMHEX_VERSION_STRING},${CCACHE}' < ./dist/snap/snapcraft.yaml > ./snap/snapcraft.yaml\n\n      - name: 📜 Setup ccache\n        uses: hendrikmuhs/ccache-action@v1\n        with:\n          key: ${{ matrix.architecture }}-snap-${{ github.run_id }}\n          restore-keys: ${{ matrix.architecture }}-snap\n          max-size: 1G\n\n      - name: 🛠️ Build\n        run: |\n          sudo snapcraft --destructive-mode\n\n      - name: 🟩 Rename Snap\n        run: |\n          mv *.snap imhex-${{ env.IMHEX_VERSION }}-${{ matrix.architecture }}.snap\n\n      - name: 🗝️ Generate build provenance attestations\n        uses: actions/attest-build-provenance@v2\n        if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }}\n        with:\n          subject-path: |\n            *.snap\n\n      - name: ⬆️ Upload Snap\n        uses: actions/upload-artifact@v4\n        with:\n          if-no-files-found: error\n          name: Snap ${{ matrix.architecture }}\n          path: |\n            *.snap\n\n  flatpak-build:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - architecture: \"x86_64\"\n            flatpak_arch: \"x86_64\"\n            image: ubuntu-24.04\n          - architecture: \"arm64\"\n            flatpak_arch: \"aarch64\"\n            image: ubuntu-24.04-arm\n    name: 🐧 Flatpak ${{ matrix.architecture }}\n    runs-on: ${{ matrix.image }}\n\n    permissions:\n      id-token: write\n      attestations: write\n\n    steps:\n      - name: ⬇️ Install setup dependencies\n        run: |\n          sudo apt update && sudo apt install -y git curl flatpak-builder\n          sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo\n          sudo flatpak remote-modify --enable flathub\n          sudo flatpak install --noninteractive --system flathub org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08\n\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: 📜 Set version variable\n        run: |\n          echo \"IMHEX_VERSION=`cat VERSION`\" >> $GITHUB_ENV\n\n      - name: 🛠️ Build\n        uses: flatpak/flatpak-github-actions/flatpak-builder@v6\n        with:\n          bundle: imhex-${{ env.IMHEX_VERSION }}-${{ matrix.architecture }}.flatpak\n          manifest-path: dist/flatpak/net.werwolv.ImHex.yaml\n          cache-key: flatpak-builder-${{ matrix.architecture }}\n          arch: ${{ matrix.flatpak_arch }}\n          upload-artifact: false\n\n\n      - name: 🗝️ Generate build provenance attestations\n        uses: actions/attest-build-provenance@v2\n        if: ${{ github.event.repository.fork == false && github.event_name != 'pull_request' }}\n        with:\n          subject-path: |\n            imhex-${{ env.IMHEX_VERSION }}-${{ matrix.architecture }}.flatpak\n\n      - name: ⬆️ Upload Flatpak\n        uses: actions/upload-artifact@v4\n        with:\n          if-no-files-found: error\n          name: Flatpak ${{ matrix.architecture }}\n          path: |\n            imhex-${{ env.IMHEX_VERSION }}-${{ matrix.architecture }}.flatpak\n\n  webassembly-build:\n    runs-on: ubuntu-24.04\n    name: 🌍 Web\n    steps:\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: 📁 Restore docker /cache\n        uses: actions/cache@v4\n        with:\n          path: cache\n          key: webassembly-ccache-${{ github.run_id }}\n          restore-keys: webassembly-ccache\n\n      - name: 🐳 Inject /cache into docker\n        uses: reproducible-containers/buildkit-cache-dance@v2\n        with:\n          cache-source: cache\n          cache-target: /cache\n\n      - name: 🔨 Copy necessary files\n        run: |\n          mkdir -p out/nightly\n          cp dist/web/serve.py out/nightly/start_imhex_web.py\n\n      - name: 🛠️ Build using docker\n        run: |\n          docker buildx build . -f dist/web/Dockerfile --progress=plain --build-arg 'JOBS=4' --output out/nightly --target raw\n\n      - name: ⬇️ Download Release artifact\n        if: ${{ github.event.repository.fork == false }}\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: gh --repo $GITHUB_REPOSITORY release download --pattern \"imhex-*-Web.zip\"\n\n      - name: 🔨 Fix permissions\n        if: ${{ github.event.repository.fork == false }}\n        run: |\n          unzip imhex-*-Web.zip -d out\n          chmod -c -R +rX \"out/\"\n\n      - name: ⬆️ Upload artifacts\n        uses: actions/upload-pages-artifact@v3\n        with:\n          path: out/\n\n      - name: ⬆️ Upload package\n        uses: actions/upload-artifact@v4\n        with:\n          if-no-files-found: error\n          name: ImHex Web\n          path: out/nightly/*\n\n        # See https://github.com/actions/cache/issues/342#issuecomment-1711054115\n      - name: 🗑️ Delete old cache\n        continue-on-error: true\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          gh extension install actions/gh-actions-cache || true\n          gh actions-cache delete \"build-web-cache\" --confirm || true\n\n  webassembly-deploy:\n    environment:\n      name: ImHex Web\n      url: ${{ steps.deployment.outputs.page_url }}\n    permissions:\n      pages: write\n      id-token: write\n      actions: write\n    name: 📃 Deploy to GitHub Pages\n    runs-on: ubuntu-24.04\n\n    if: ${{ github.ref == 'refs/heads/master' && github.event.repository.fork == false }}\n    needs: webassembly-build\n\n    steps:\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n\n      - name: 🌍 Deploy WebAssembly Build to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4\n\n      - name: 🗑️ Delete artifact\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          .github/scripts/delete-artifact.sh \"github-pages\"\n\n  webassembly-docker-image-deploy:\n    runs-on: ubuntu-latest\n    if: github.ref == 'refs/heads/master'\n    needs: webassembly-build\n    name: 🐋 Deploy to ghcr.io\n    permissions:\n      contents: read\n      packages: write\n\n    steps:\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n\n      - name: ⬇️ Download artifact\n        uses: actions/download-artifact@v4\n        with:\n          name: ImHex Web\n          path: out\n\n      - name: 📜 Login to ghcr.io\n        uses: docker/login-action@v3\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: ⛓️ Extract metadata\n        id: meta\n        uses: docker/metadata-action@v5\n        with:\n          images: ghcr.io/${{ github.repository }}/imhex-web\n          tags: |\n            type=ref,event=branch\n            type=ref,event=pr\n            type=semver,pattern={{version}}\n            type=semver,pattern={{major}}.{{minor}}\n            type=sha\n\n      - name: 🔨 Build and push Docker image\n        uses: docker/build-push-action@v6\n        env:\n          DOCKER_BUILD_RECORD_UPLOAD: false\n        with:\n          context: .\n          file: dist/web/Host.Dockerfile\n          push: ${{ github.event_name != 'pull_request' }}\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n"
  },
  {
    "path": ".github/workflows/dl-cache.yml",
    "content": "# https://gist.github.com/iTrooz/d5bacca32c0974edc6c1ac3ad3ee82f3\n# See https://github.com/cli/cli/issues/9125\n# Extract archive with `tar -xf cache.tzst --transform 's@\\.\\./@#@g' -P` to avoid ../ errors\nname: Download cache key\n\non:\n  workflow_dispatch:\n    inputs:\n      cache_key:\n        description: 'Cache key'\n        required: true\n        type: string\njobs:\n  cache-download:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Query cache version\n        id: version\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          VERSION=$(gh api repos/$GITHUB_REPOSITORY/actions/caches \\\n          --jq \"\n            .actions_caches[]\n            | select(.ref == \\\"refs/heads/$GITHUB_REF_NAME\\\")\n            | select(.key == \\\"${{ github.event.inputs.cache_key }}\\\")\n            | .version\n          \")\n          echo \"version=$VERSION\" | tee $GITHUB_OUTPUT\n\n      - name: Restore cache\n        uses: iTrooz/cache/restore@restore_with_version\n        with:\n          # Path won't be actually used, we will match by 'version'.\n          path: .\n          key: ${{ github.event.inputs.cache_key }}\n          version: ${{ steps.version.outputs.version }}\n\n      - name: Upload cached folder as artifact\n        uses: actions/upload-artifact@v4\n        with:\n          name: cache-artifact\n          path: |\n            /home/runner/work/**/*.tzst"
  },
  {
    "path": ".github/workflows/nightly_release.yml",
    "content": "permissions:\n  contents: write\n\nname: Nightly Release\n\non:\n  schedule:\n    - cron: '0 0 * * *'\n  workflow_dispatch:\n\njobs:\n  nightly-release:\n    runs-on: ubuntu-24.04\n    name: 🌃 Update Nightly Release\n    steps:\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          path: ImHex\n          fetch-depth: 0\n          fetch-tags: true\n\n      - name: 🌃 Check for new commits\n        id: check_commits\n        run: |\n          cd ImHex\n          git config --global --add safe.directory $(pwd)\n          if [ -z \"$(git log nightly..HEAD --oneline)\" ]; then\n            echo \"No new commits since last nightly. Exiting.\"\n            echo \"::set-output name=should_run::false\"\n          else\n            echo \"::set-output name=should_run::true\"\n          fi\n\n      - name: 📜 Set version variable\n        if: ${{ steps.check_commits.outputs.should_run == 'true' }}\n        run: |\n          project_version=`cat ImHex/VERSION`          \n          echo \"IMHEX_VERSION=$project_version\" >> $GITHUB_ENV\n      \n      # TODO: Replace by Github CLI when github.com/cli/cli/pull/12435 is closed\n      - name: ⬇️ Download artifacts from latest workflow\n        uses: dawidd6/action-download-artifact@v6\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          workflow: build.yml\n          branch: master\n          workflow_conclusion: success\n          skip_unpack: true\n\n      - name: 🗜️ Unzip files when needed\n        if: ${{ steps.check_commits.outputs.should_run == 'true' }}\n        run: |\n          set -x\n          for zipfile in ./*.zip\n          do\n              if [ `zipinfo -1 \"$zipfile\" | wc -l` -eq 1 ];\n              then\n                echo \"unzipping $zipfile\"\n                unzip \"$zipfile\"\n                rm \"$zipfile\"\n              else\n                echo \"keeping $zipfile zipped\"\n              fi\n          done\n\n      - name: 🟩 Rename artifacts when needed\n        if: ${{ steps.check_commits.outputs.should_run == 'true' }}\n        run: |\n          mv \"Windows Portable x86_64.zip\" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-x86_64.zip\n          mv \"Windows Portable arm64.zip\" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-arm64.zip\n          mv \"Windows Portable NoGPU x86_64.zip\" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip\n          mv \"ImHex Web.zip\" imhex-${{ env.IMHEX_VERSION }}-Web.zip\n          rm artifact.tar || true\n\n      - name: 📖 Generate Release Notes\n        if: ${{ steps.check_commits.outputs.should_run == 'true' }}\n        id: release_notes\n        continue-on-error: true\n        run: |\n          cd ImHex\n          echo \"## Nightly ${GITHUB_SHA::7} Changelog\" > changelog.md\n          git fetch --tags --recurse-submodules=no\n          git log nightly..origin/master --oneline --no-merges --pretty=format:'* %s' >> changelog.md\n\n      - name: 📦 Update Pre-Release\n        if: ${{ steps.check_commits.outputs.should_run == 'true' }}\n        run: |\n          set -e\n          \n          cd ImHex\n          \n          # Move nightly tag to latest commit\n          git tag -f nightly origin/master\n          git push origin nightly --force\n\n          # Auth for GitHub CLI\n          echo \"${{ github.token }}\" | gh auth login --with-token\n\n          # Delete existing assets\n          for asset in $(gh release view nightly --json assets --jq '.assets[].name'); do\n            gh release delete-asset nightly \"$asset\" --yes\n          done\n\n          # Update release notes\n          gh release edit nightly --notes-file changelog.md\n\n          # Upload new assets\n          gh release upload nightly ../*.* --clobber\n\n      - name: ⬆️ Publish x86_64 Snap package\n        if: ${{ steps.check_commits.outputs.should_run == 'true' }}\n        continue-on-error: true\n        uses: snapcore/action-publish@v1\n        env:\n          SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}\n        with:\n          snap: imhex-${{ env.IMHEX_VERSION }}-x86_64.snap\n          release: edge\n\n      - name: ⬆️ Publish arm64 Snap package\n        if: ${{ steps.check_commits.outputs.should_run == 'true' }}\n        continue-on-error: true\n        uses: snapcore/action-publish@v1\n        env:\n          SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}\n        with:\n          snap: imhex-${{ env.IMHEX_VERSION }}-arm64.snap\n          release: edge\n\n  website_update:\n    name: 🌍 Update ImHex Landing Website\n    needs: nightly-release\n    runs-on: ubuntu-24.04\n    env:\n      WEBSITE_DISPATCH_TOKEN: ${{ secrets.WEBSITE_DISPATCH_TOKEN }}\n    steps:\n     - name: ✉️ Dispatch Landing page update\n       if: ${{ env.WEBSITE_DISPATCH_TOKEN != '' }}\n       uses: peter-evans/repository-dispatch@v4\n       with:\n         token: ${{ secrets.WEBSITE_DISPATCH_TOKEN }}\n         repository: WerWolv/ImHexWebsite\n         event-type: update_page"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "permissions:\n  contents: write\n\nname: Release\n\non:\n  release:\n    types: [published]\n  workflow_dispatch:\n    inputs:\n      commit_hash:\n        type: string\n        description: 'The commit hash to build (defaults to the latest commit on the default branch)'\n        required: false\n        default: ''\n\njobs:\n  release-update-repos:\n    runs-on: ubuntu-24.04\n    name: Release Update Repos\n\n    steps:\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          path: ImHex\n\n      - name: 📜 Verify version and set version variable\n        run: |\n          set -x\n          project_version=`cat ImHex/VERSION`\n          tag_version=\"${{github.event.release.tag_name}}\"\n          tag_version=\"${tag_version:1}\"\n          if [ \"$project_version\" != \"$tag_version\" ] && [ ! -z \"$tag_version\" ]; then\n            echo \"::warning::$project_version and $tag_version are not the same ! Refusing to populate release\"\n            exit 1\n          fi\n\n          echo \"IMHEX_VERSION=$project_version\" >> $GITHUB_ENV\n\n      - name: 🎫 Create PatternLanguage release\n        uses: ncipollo/release-action@v1\n        env:\n          RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}\n        if: \"${{ env.RELEASE_TOKEN != '' }}\"\n        with:\n          tag: ImHex-v${{ env.IMHEX_VERSION }}\n          repo: PatternLanguage\n          token: ${{ secrets.RELEASE_TOKEN }}\n          skipIfReleaseExists: true\n\n      - name: 🎫 Create ImHex-Patterns release\n        uses: ncipollo/release-action@v1\n        env:\n          RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}\n        if: \"${{ env.RELEASE_TOKEN != '' }}\"\n        with:\n          tag: ImHex-v${{ env.IMHEX_VERSION }}\n          repo: ImHex-Patterns\n          token: ${{ secrets.RELEASE_TOKEN }}\n          skipIfReleaseExists: true\n\n      - name: 🎫 Create imhex-download-sdk release\n        uses: ncipollo/release-action@v1\n        env:\n          RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}\n        if: \"${{ env.RELEASE_TOKEN != '' }}\"\n        with:\n          tag: v${{ env.IMHEX_VERSION }}\n          repo: imhex-download-sdk\n          token: ${{ secrets.RELEASE_TOKEN }}\n          skipIfReleaseExists: true\n\n  release-upload-artifacts:\n    runs-on: ubuntu-24.04\n    name: Release Upload Artifacts\n    outputs:\n      IMHEX_VERSION: ${{ steps.verify_version.outputs.IMHEX_VERSION }}\n    steps:\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          path: ImHex\n          submodules: recursive\n\n      - name: 📜 Verify version and set version variable\n        id: verify_version\n        run: |\n          set -x\n          project_version=`cat ImHex/VERSION`\n          tag_version=\"${{github.event.release.tag_name}}\"\n          tag_version=\"${tag_version:1}\"\n          if [ \"$project_version\" != \"$tag_version\" ] && [ ! -z \"$tag_version\" ]; then\n            echo \"::warning::$project_version and $tag_version are not the same ! Refusing to populate release\"\n            exit 1\n          fi\n\n          echo \"IMHEX_VERSION=$project_version\" >> $GITHUB_ENV\n          echo \"IMHEX_VERSION=$project_version\" >> $GITHUB_OUTPUT\n\n      - name: 🗜️ Create tarball from sources with dependencies\n        run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex\n\n      - name: ⬇️ Download artifacts from latest workflow\n        uses: dawidd6/action-download-artifact@v6\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          workflow: build.yml\n          branch: ${{ github.event.release.target_commitish }}\n          workflow_conclusion: success\n          skip_unpack: true\n          commit: ${{ github.event.inputs.commit_hash }}\n\n      - name: 🗜️ Unzip files when needed\n        run: |\n          set -x\n          for zipfile in ./*.zip\n          do\n              if [ `zipinfo -1 \"$zipfile\" | wc -l` -eq 1 ];\n              then\n                echo \"unzipping $zipfile\"\n                unzip \"$zipfile\"\n                rm \"$zipfile\"\n              else\n                echo \"keeping $zipfile zipped\"\n              fi\n          done\n\n      - name: 🟩 Rename artifacts when needed\n        run: |\n          mv \"Windows Portable x86_64.zip\" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-x86_64.zip || true\n          mv \"Windows Portable arm64.zip\" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-arm64.zip || true\n          mv \"Windows Portable NoGPU x86_64.zip\" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip || true\n          mv \"ImHex Web.zip\" imhex-${{ env.IMHEX_VERSION }}-Web.zip || true\n          rm artifact.tar || true\n\n      - name: ⬆️ Upload Unsigned x86_64 Windows Installer\n        uses: actions/upload-artifact@v4\n        id: upload-installer-x86_64\n        with:\n          if-no-files-found: error\n          name: Windows Installer x86_64\n          path: |\n            imhex-*-x86_64.msi\n\n      - name: ⬆️ Upload Unsigned ARM64 Windows Installer\n        if: false\n        uses: actions/upload-artifact@v4\n        id: upload-installer-arm64\n        with:\n          if-no-files-found: error\n          name: Windows Installer ARM64\n          path: |\n            imhex-*-arm64.msi\n\n      - name: 🗑️ Delete unsigned installers\n        run: |\n          rm imhex-*-x86_64.msi\n\n      - name: 🗝️ Sign x86_64 Installer\n        uses: signpath/github-action-submit-signing-request@v1\n        with:\n          api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'\n          organization-id: 'f605a0e8-86cd-411c-bb6f-e05025afcc33'\n          project-slug: 'ImHex'\n          signing-policy-slug: 'release-signing'\n          github-artifact-id: '${{ steps.upload-installer-x86_64.outputs.artifact-id }}'\n          wait-for-completion: true\n          output-artifact-directory: '.'\n\n      - name: 🗝️ Sign ARM64 Installer\n        if: false\n        uses: signpath/github-action-submit-signing-request@v1\n        with:\n          api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'\n          organization-id: 'f605a0e8-86cd-411c-bb6f-e05025afcc33'\n          project-slug: 'ImHex'\n          signing-policy-slug: 'release-signing'\n          github-artifact-id: '${{ steps.upload-installer-arm64.outputs.artifact-id }}'\n          wait-for-completion: true\n          output-artifact-directory: '.'\n\n      - name: ⬆️ Upload everything to release\n        uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981\n        with:\n          files: '*'\n\n  release-update-aur:\n    name: Release update AUR package\n    needs: release-upload-artifacts\n    runs-on: ubuntu-24.04\n    steps:\n      - name: 🧰 Checkout\n        uses: actions/checkout@v4\n        with:\n          path: ImHex\n\n      - name: ⬇️ Download artifacts\n        run: |\n          tagname=${GITHUB_REF#refs/tags/}\n          version=${tagname#v}\n          wget https://github.com/WerWolv/ImHex/releases/download/${tagname}/imhex-${version}-ArchLinux-x86_64.pkg.tar.zst \n\n      - name: ✒️ Prepare PKGBUILD\n        run: |\n          set -x\n          cp ImHex/dist/Arch/PKGBUILD .\n          \n          hash=`md5sum imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst | cut -d ' ' -f 1`\n\n          sed -i 's/%version%/${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}/g' PKGBUILD\n          sed -i \"s/(SKIP)/($hash)/g\" PKGBUILD\n\n      - name: ⬆️ Publish AUR package\n\n        env:\n          AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}\n        if: \"${{ env.AUR_SSH_PRIVATE_KEY != '' }}\"\n\n        uses: KSXGitHub/github-actions-deploy-aur@v2\n        with:\n          pkgname: imhex-bin\n          pkgbuild: ./PKGBUILD\n          commit_username: iTrooz\n          commit_email: hey@itrooz.fr\n          ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}\n          commit_message: Bump to version ${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}\n          ssh_keyscan_types: rsa,ecdsa,ed25519\n\n  release-update-winget:\n    name: Release update winget package\n    needs: release-upload-artifacts\n    runs-on: windows-latest\n    steps:\n      - name: ⬇️ Download dependencies\n        shell: pwsh\n        run: |\n          iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe\n\n      - name: ⬆️ Update winget manifest\n        shell: pwsh\n        env:\n          WINGET_GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}\n        if: \"${{ env.WINGET_GITHUB_TOKEN != '' }}\"\n        run: |\n          $tagname = $env:GITHUB_REF.Replace(\"refs/tags/\", \"\")\n          $version = $tagname.Replace(\"v\", \"\")\n          $url = \"https://github.com/WerWolv/ImHex/releases/download/${tagname}/imhex-${version}-Windows-x86_64.msi\"\n          .\\wingetcreate.exe update WerWolv.ImHex -u $url --version $version\n          if ($version -notmatch \"-\") {\n            .\\wingetcreate.exe submit .\\manifests\\w\\WerWolv\\ImHex\\${version}\\ --token $env:WINGET_GITHUB_TOKEN\n          }\n\n  release-update-snapstore:\n    name: Release update snapstore package\n    needs: release-upload-artifacts\n    runs-on: ubuntu-24.04\n    steps:\n      - name: ⬇️ Download artifacts\n        run: |\n          tagname=${GITHUB_REF#refs/tags/}\n          version=${tagname#v}\n          wget https://github.com/WerWolv/ImHex/releases/download/${tagname}/imhex-${version}-x86_64.snap\n          wget https://github.com/WerWolv/ImHex/releases/download/${tagname}/imhex-${version}-arm64.snap\n\n      - name: ⬆️ Publish x86_64 Snap package\n        continue-on-error: true\n        uses: snapcore/action-publish@v1\n        env:\n          SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}\n        with:\n          snap: imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-x86_64.snap\n          release: stable\n\n      - name: ⬆️ Publish arm64 Snap package\n        continue-on-error: true\n        uses: snapcore/action-publish@v1\n        env:\n          SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}\n        with:\n          snap: imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-arm64.snap\n          release: stable"
  },
  {
    "path": ".github/workflows/stale_issues.yml",
    "content": "name: Close inactive issues\non:\n  schedule:\n    - cron: \"30 1 * * 0\"\n  workflow_dispatch:\n\njobs:\n  close-issues:\n    if: false # Disabled for now until I actually have time to take care of all these issues\n    runs-on: ubuntu-24.04\n    permissions:\n      issues: write\n      pull-requests: write\n    steps:\n      - uses: actions/stale@v5\n        with:\n          operations-per-run: '200'\n          ascending: true\n          days-before-issue-stale: 334\n          days-before-issue-close: 30\n          stale-issue-label: \"stale\"\n          stale-issue-message: |\n              This issue is marked stale as it has been open for 11 months without activity. \n              Please try the latest ImHex version. (Avaiable here:  https://imhex.download/ for release and https://imhex.download/#nightly for development version)\n              If the issue persists on the latest version, please make a comment on this issue again\n\n              Without response, this issue will be closed in one month.\n          close-issue-message: \"\"\n          days-before-pr-stale: -1\n          days-before-pr-close: -1\n          repo-token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: \"Unit Tests\"\n\non:\n  push:\n    branches:\n      - 'master'\n      - 'releases/**'\n      - 'tests/**'\n      - 'feature/**'\n  pull_request:\n    branches:\n      - 'master'\n      - 'releases/**'\n  workflow_dispatch:\n\njobs:\n  tests:\n    name: 🧪 Unit Tests\n    runs-on: ubuntu-24.04\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    steps:\n    - name: 🧰 Checkout\n      uses: actions/checkout@v4\n      with:\n        submodules: recursive\n\n    - name: 📜 Setup ccache\n      uses: hendrikmuhs/ccache-action@v1\n      with:\n        key: ${{ runner.os }}-tests-build-${{ github.run_id }}\n        restore-keys: ${{ runner.os }}-tests-build\n        max-size: 50M\n\n    - name: ⬇️ Install dependencies\n      run: |\n        sudo apt update\n        sudo bash dist/get_deps_debian.sh\n\n    - name: 🛠️ Build\n      run: |\n        set -x\n        mkdir -p build\n        cd build\n        CC=gcc-14 CXX=g++-14 cmake                                                                                 \\\n          -DCMAKE_BUILD_TYPE=Debug                                                                                 \\\n          -DIMHEX_ENABLE_UNIT_TESTS=ON                                                                             \\\n          -DIMHEX_ENABLE_PLUGIN_TESTS=ON                                                                           \\\n          -DCMAKE_C_COMPILER_LAUNCHER=ccache                                                                       \\\n          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache                                                                     \\\n          -DCMAKE_C_FLAGS=\"-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all --coverage\"    \\\n          -DCMAKE_CXX_FLAGS=\"-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all --coverage\"  \\\n          -DIMHEX_OFFLINE_BUILD=ON                                                                                 \\\n          -G Ninja                                                                                                 \\\n          ..\n        ninja unit_tests\n        ninja imhex_all\n\n    - name: 🧪 Perform plcli Integration Tests\n      run: |\n        cd lib/external/pattern_language\n        python tests/integration/integration.py ../../../build/imhex --pl\n\n    - name: 🧪 Perform Unit Tests\n      run: |\n        cd build\n        ctest --output-on-failure\n\n    # Generate report from all gcov .gcda files\n    #- name: 🧪 Generate coverage report\n    #  run: |\n    #    sudo apt install python3-pip python3-venv\n    #    python3 -m venv venv\n    #    . venv/bin/activate\n    #    pip3 install gcovr\n    #    cd build\n    #    gcovr --gcov-executable /usr/bin/gcov-14 --exclude '.*/yara_rules/' --exclude '.*/third_party/' --exclude '.*/external/' --root .. --xml coverage_report.xml --verbose --gcov-ignore-errors all\n    #\n    #- name: Upload coverage reports to Codecov\n    #  env:\n    #      CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n    #  if: ${{ env.CODECOV_TOKEN }}\n    #  uses: codecov/codecov-action@v4\n    #  with:\n    #    fail_ci_if_error: true\n    #    token: ${{ secrets.CODECOV_TOKEN }}\n    #    file: build/coverage_report.xml\n\n  langs:\n    name: 🧪 Langs\n    runs-on: ubuntu-22.04\n\n    steps:\n    - name: 🧰 Checkout\n      uses: actions/checkout@v4\n      with:\n        submodules: recursive\n\n    - name: Check langs\n      run:\n        python3 tests/check_langs.py\n"
  },
  {
    "path": ".gitignore",
    "content": "/.vscode/\n/.idea/\n/.kdev4/\n/.vs/\n.venv/\n\n/cmake-build-*/\n/build*/\n/local/\n/venv/\n/.cache/\n/install/\n/out/\n\n/dist/ImHex.run.xml\n\n*.mgc\n*.kdev4\nimgui.ini\n.DS_Store\nCMakeUserPresets.json\nBrewfile.lock.json\n\nvcpkg.json\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"lib/third_party/nativefiledialog\"]\n\tpath = lib/third_party/nativefiledialog\n\turl = https://github.com/btzy/nativefiledialog-extended\n\tignore = dirty\n[submodule \"lib/third_party/yara/yara\"]\n\tpath = lib/third_party/yara/yara\n\turl = https://github.com/VirusTotal/yara\n\tignore = dirty\n[submodule \"lib/third_party/xdgpp\"]\n\tpath = lib/third_party/xdgpp\n\turl = https://github.com/WerWolv/xdgpp\n\tignore = dirty\n[submodule \"lib/third_party/fmt\"]\n\tpath = lib/third_party/fmt\n\turl = https://github.com/fmtlib/fmt\n\tignore = dirty\n[submodule \"lib/third_party/capstone\"]\n\tpath = lib/third_party/capstone\n\turl = https://github.com/capstone-engine/capstone\n\tignore = dirty\n[submodule \"lib/third_party/edlib\"]\n\tpath = lib/third_party/edlib\n\turl = https://github.com/Martinsos/edlib\n\tignore = dirty\n[submodule \"lib/third_party/lunasvg\"]\n\tpath = lib/third_party/lunasvg\n\turl = https://github.com/sammycage/lunasvg\n\tignore = dirty\n\n[submodule \"lib/external/libromfs\"]\n\tpath = lib/external/libromfs\n\turl = https://github.com/WerWolv/libromfs\n[submodule \"lib/external/pattern_language\"]\n\tpath = lib/external/pattern_language\n\turl = https://github.com/WerWolv/PatternLanguage\n[submodule \"lib/external/libwolv\"]\n\tpath = lib/external/libwolv\n\turl = https://github.com/WerWolv/libwolv\n\n[submodule \"lib/third_party/HashLibPlus\"]\n\tpath = lib/third_party/HashLibPlus\n    url = https://github.com/WerWolv/HashLibPlus\n[submodule \"lib/external/disassembler\"]\n\tpath = lib/external/disassembler\n\turl = https://github.com/WerWolv/Disassembler\n[submodule \"lib/third_party/md4c\"]\n\tpath = lib/third_party/md4c\n\turl = https://github.com/mity/md4c"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.25)\n\n# Options\n## General\noption(IMHEX_STRIP_RELEASE              \"Strip the release builds\"                                                              ON )\noption(IMHEX_OFFLINE_BUILD              \"Enable offline build\"                                                                  OFF)\noption(IMHEX_IGNORE_BAD_CLONE           \"Disable the bad clone prevention checks\"                                               OFF)\noption(IMHEX_PATTERNS_PULL_MASTER       \"Download latest files from master branch of the ImHex-Patterns repo\"                   OFF)\noption(IMHEX_IGNORE_BAD_COMPILER        \"Allow compiling with an unsupported compiler\"                                          OFF)\noption(IMHEX_USE_GTK_FILE_PICKER        \"Use GTK file picker instead of xdg-desktop-portals (Linux only)\"                       OFF)\noption(IMHEX_BUNDLE_DOTNET              \"Bundle .NET runtime\"                                                                   ON )\noption(IMHEX_ENABLE_LTO                 \"Enables Link Time Optimizations if possible\"                                           OFF)\noption(IMHEX_USE_DEFAULT_BUILD_SETTINGS \"Use default build settings\"                                                            OFF)\noption(IMHEX_BUILD_HARDENING            \"Enable hardening flags for build\"                                                      ON )\noption(IMHEX_GENERATE_PACKAGE           \"Specify if a cpack package should be built. (Windows only)\"                            OFF)\noption(IMHEX_MACOS_CREATE_BUNDLE        \"Creates a macOS .app bundle when building (macOS only)\"                                ON )\noption(IMHEX_ENABLE_UNITY_BUILD         \"Enables building ImHex as a unity build.\"                                              OFF)\noption(IMHEX_ENABLE_PRECOMPILED_HEADERS \"Enable precompiled headers\"                                                            OFF)\noption(IMHEX_ENABLE_CXX_MODULES         \"Enable C++20 Module compilation. Testing only!\"                                        OFF)\noption(IMHEX_ENABLE_CPPCHECK            \"Enable cppcheck static analysis\"                                                       OFF)\noption(IMHEX_BUNDLE_PLUGIN_SDK          \"Enable bundling of Plugin SDK into install package\"                                    ON )\n## Testing\noption(IMHEX_ENABLE_UNIT_TESTS          \"Enable building unit tests\"                                                            ON )\noption(IMHEX_ENABLE_IMGUI_TEST_ENGINE   \"Enable the ImGui Test Engine\"                                                          OFF)\noption(IMHEX_ENABLE_STD_ASSERTS         \"Enable debug asserts in the C++ std library. (Breaks Plugin ABI!)\"                     OFF)\n## Debug info\noption(IMHEX_COMPRESS_DEBUG_INFO        \"Compress debug information\"                                                            ON )\noption(IMHEX_GENERATE_PDBS              \"Enable generating PDB files in non-debug builds (Windows only)\"                        OFF)\noption(IMHEX_REPLACE_DWARF_WITH_PDB     \"Remove DWARF information from binaries when generating PDBS (Windows only)\"            OFF)\noption(IMHEX_STRICT_WARNINGS            \"Enable most available warnings and treat them as errors\"                               ON )\noption(IMHEX_DISABLE_STACKTRACE         \"Disables support for printing stack traces\"                                            OFF)\n## Plugins\noption(IMHEX_STATIC_LINK_PLUGINS        \"Statically link all plugins into the main executable\"                                  OFF)\noption(IMHEX_ENABLE_PLUGIN_TESTS        \"Enable building plugin tests\"                                                          ON )\noption(IMHEX_INCLUDE_PLUGINS            \"Semicolon-separated list of plugins to include in the build (empty = build all)\"       \"\" )\noption(IMHEX_EXCLUDE_PLUGINS            \"Semicolon-separated list of plugins to exclude from the build\"                         \"\" )\n\nset(IMHEX_BASE_FOLDER \"${CMAKE_CURRENT_SOURCE_DIR}\")\nset(CMAKE_MODULE_PATH \"${IMHEX_BASE_FOLDER}/cmake/modules\")\n\n# Optional IDE support\ninclude(\"${IMHEX_BASE_FOLDER}/cmake/ide_helpers.cmake\")\n\n# Basic compiler and cmake configurations\nset(CMAKE_CXX_STANDARD 23)\nset(CMAKE_CXX_SCAN_FOR_MODULES ${IMHEX_ENABLE_CXX_MODULES})\nset(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON)\nset(CMAKE_EXPORT_COMPILE_COMMANDS ON)\ninclude(\"${IMHEX_BASE_FOLDER}/cmake/build_helpers.cmake\")\n\n# Setup project\nloadVersion(IMHEX_VERSION IMHEX_VERSION_PLAIN)\nsetVariableInParent(IMHEX_VERSION ${IMHEX_VERSION})\n\nconfigureCMake()\nproject(ImHex\n        LANGUAGES       C CXX\n        VERSION         ${IMHEX_VERSION_PLAIN}\n        DESCRIPTION     \"The ImHex Hex Editor\"\n        HOMEPAGE_URL    \"https://imhex.werwolv.net\"\n)\nconfigureProject()\n\n# Add ImHex sources\nadd_custom_target(imhex_all ALL)\n\n# Make sure project is configured correctly\nsetDefaultBuiltTypeIfUnset()\ndetectBadClone()\nverifyCompiler()\n\ndetectBundledPlugins()\n\n# Add various defines\ndetectOS()\naddDefines()\n\n# Configure packaging and install targets\nconfigurePackingResources()\nsetUninstallTarget()\naddBundledLibraries()\n\nadd_subdirectory(lib/libimhex)\nadd_subdirectory(main)\naddPluginDirectories()\nadd_subdirectory(lib/trace)\n\n# Add unit tests\nif (IMHEX_ENABLE_UNIT_TESTS)\n    if (NOT TARGET unit_tests)\n        enable_testing()\n        add_custom_target(unit_tests)\n        add_subdirectory(tests EXCLUDE_FROM_ALL)\n    endif ()\nendif ()\n\n# Configure more resources that will be added to the install package\nif (IMHEX_BUNDLE_PLUGIN_SDK)\n    generateSDKDirectory()\nendif()\n\n# Handle package generation\ncreatePackage()\n\n# Accommodate IDEs with FOLDER support\ntweakTargetsForIDESupport()\n"
  },
  {
    "path": "CMakePresets.json",
    "content": "{\n  \"version\": 2,\n  \"cmakeMinimumRequired\": {\n    \"major\": 3,\n    \"minor\": 20,\n    \"patch\": 0\n  },\n  \"configurePresets\": [\n    {\n      \"name\": \"base\",\n      \"displayName\": \"Base\",\n      \"description\": \"Base configuration for all builds\",\n      \"hidden\": true,\n      \"binaryDir\": \"${sourceDir}/build/${presetName}\",\n      \"generator\": \"Ninja\",\n      \"cacheVariables\": {\n        \"CMAKE_BUILD_TYPE\": \"Debug\",\n        \"CMAKE_C_COMPILER\": \"gcc\",\n        \"CMAKE_CXX_COMPILER\": \"g++\",\n        \"IMHEX_PATTERNS_PULL_MASTER\": \"ON\",\n        \"CMAKE_INSTALL_PREFIX\": \"./install\",\n        \"USE_SYSTEM_CAPSTONE\": \"ON\",\n        \"IMHEX_USE_DEFAULT_BUILD_SETTINGS\": \"ON\"\n      }\n    },\n    {\n      \"name\": \"x86_64\",\n      \"displayName\": \"x86_64 Build\",\n      \"description\": \"x86_64 build\",\n      \"inherits\": [ \"base\" ]\n    },\n    {\n      \"name\": \"xcode\",\n      \"inherits\": [ \"base\" ],\n      \n      \"displayName\": \"Xcode\",\n      \"description\": \"Xcode with external compiler override\",\n      \"generator\": \"Xcode\",\n      \n      \"cacheVariables\": {\n        \"CMAKE_C_COMPILER\": \"clang\",\n\n        \"CMAKE_CXX_COMPILER\": \"clang++\",\n        \"CMAKE_CXX_FLAGS\": \"-fexperimental-library -Wno-shorten-64-to-32 -Wno-deprecated-declarations\",\n\n        \"IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER\": \"ON\"\n      }\n    },\n    {\n      \"name\": \"vs2022\",\n      \"displayName\": \"Visual Studio 2022\",\n      \"generator\": \"Ninja\",\n      \"binaryDir\": \"${sourceDir}/build/${presetName}\",\n      \"cacheVariables\": {\n        \"CMAKE_TOOLCHAIN_FILE\": \"$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake\",\n        \"VCPKG_MANIFEST_DIR\": \"${sourceDir}/dist\"\n      }\n    },\n  {\n      \"name\": \"vs2022-x86\",\n      \"displayName\": \"Visual Studio 2022 x86\",\n      \"generator\": \"Ninja\",\n      \"binaryDir\": \"${sourceDir}/build/${presetName}\",\n      \"cacheVariables\": {\n          \"CMAKE_TOOLCHAIN_FILE\": \"$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake\",\n          \"VCPKG_MANIFEST_DIR\": \"${sourceDir}/dist\"\n      },\n      \"environment\": {\n          \"VSCMD_ARG_TGT_ARCH\": \"x86\"\n      }\n  }\n  ],\n  \"buildPresets\": [\n    {\n        \"name\": \"x86_64\",\n        \"description\": \"x86_64 build\",\n        \"configurePreset\": \"x86_64\",\n        \"targets\": [ \"imhex_all\" ]\n    }\n  ],\n  \"testPresets\": [\n\n  ]\n}"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\nDiscord @WerWolv#1337.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contribution guide\n\n## Introduction\n\nThis document is a guide for developers who want to contribute to ImHex in any way. It contains information about the codebase, the build process and the general workflow.\n\n## Making Changes\n\n### Adding new features\n\nIf you'd like to add new features, the best way to start is by joining our Discord and telling us about your idea. We can then discuss the best way to implement it and how it should be integrated into ImHex or if it should be done in a separate plugin.\n\nThere are standalone plugin templates that use ImHex as a submodule. You can find them located in the README's [Plugin Development](README.md#plugin-development) section.\n\n### Adding a new language\n\nIf you'd like to support a new language in ImHex, the best way is by using the `dist/langtool.py` tool. It will create the necessary file for you and help you fill them out.\nFirst, run the tool with `python3 dist/langtool.py create plugins/builtin/romfs/lang <iso_code>` where `<iso_code>` is the ISO 639-1 code of your language. This will create a new file in the language directory.\nAfterwards follow the prompts of the program to populate the entire file. Once you're done, rerun cmake and rebuild ImHex. Your language should now be available in the settings.\n\n### Updating an existing language\n\nIf you'd like to add missing keys to an existing language, you can also use the `dist/langtool.py` tool. Run it with `python3 dist/langtool.py translate plugins/builtin/romfs/lang <iso_code>` where `<iso_code>` is the ISO 639-1 code of the language.\nThis will one by one list all the missing translation keys that are present in the default translation file, and you can fill them in with the correct translation for your language.\n\n## Codebase\n\nImHex is written in C++ and usually uses the latest compiler and standard library features available in gcc on all supported OSes. At the time of writing this is C++23.\n\n### Structure\n\n- `main`: Contains the main application code\n    - Important to understand here is that the main ImHex application is basically just an empty shell.\n    - All it does is create a Window and a OpenGL context using GLFW, load all available plugins, properly configure ImGui and render it to the screen.\n    - Everything else is done inside of plugins. ImHex comes with a few plugins by default, most notably the `builtin` plugin which contains the majority of the application code.\n    - In most cases, this code doesn't need to be modified. Most features should be self-contained inside a plugin.\n- `lib`\n    - `libimhex`: Contains all helper utilities as well as various APIs for plugins to interact with ImHex.\n        - The library's main purpose is for Dependency Inversion. The ImHex main application as well as libimhex do not know about the existence of plugins at build time. Plugins and the main application instead link against libimhex and use it as a common API to interact with each other.\n        - Since libimhex itself doesn't know about the existence of plugins, it cannot depend on any of them. This includes localizations and things that get registered by plugins after launch.\n        - Even if the builtin plugin is technically always available, it is still a plugin and should be treated that way.\n        - All important APIs can be found in the `hex/api` include directory and are documented in the respective header file.\n    - `external`: All libraries that need custom patches or aren't typically available in package managers go into here.\n        - If you'd like to add new features to the Pattern language, please make a PR to https://github.com/WerWolv/PatternLanguage instead. ImHex usually depends on the latest commit of the master branch of this repo.\n- `plugins`\n    - `builtin`: The builtin plugin. Contains the majority of the application code.\n        - It's the heart of ImHex's functionality. It contains most of the default views, providers, etc. so if you want to add new functionality to ImHex, this is the place to start.\n- `tests`: Contains all unit tests for ImHex. These are run automatically by the CI and should be kept up to date, especially when adding new helper functions to libimhex.\n\n### RomFS\n\nImHex uses a custom library called [libromfs](https://github.com/WerWolv/libromfs). It's a simple static library which uses CMake's code generation feature to embed files into the binary at compile time so they can be accessed at runtime.\nAll plugins have a `romfs` folder which contains all files that should be embedded into the binary. Resources that need to be embedded into the main application (this is usually not necessary), go into the `resources/romfs` folder.\nWhen adding, changing files or removing files, make sure to re-run CMake to update the generated code. Otherwise, the changes might not be reflected in the binary.\n\n## Development Environment\n\nI personally use CLion for development since it makes configuring and building the project very easy on all platforms.\n\n### Windows\n- Install MSYS2 from https://www.msys2.org/ and use the `dist/get_deps_msys2.sh` script to install all dependencies.\n### Linux\n- Install all dependencies using one of the `dist/get_deps_*.sh` scripts depending on your distribution or install them manually with your package manager.\n### macOS\n- Install all dependencies using brew and the `dist/Brewfile` script.\n\n"
  },
  {
    "path": "INSTALL.md",
    "content": "# Installing ImHex\n\n## Official Releases\n\nThe easiest way to install ImHex is to download the latest release from the [GitHub Releases page](https://github.com/WerWolv/ImHex/releases/latest).\n\nThere's also a NoGPU version available for users who don't have a GPU or want to run ImHex in a VM without GPU passthrough.\n\n### Windows\n\n#### Installer\nSimply run the installer to install ImHex on your system\n\n#### Portable\nExtract the zip file to any location on your system.\n\n### macOS\nSimply use the drag-n-drop dmg package to install ImHex on your system. It's possible that you need to allow the app to run in the security settings.\n\n### Linux\n\n#### AppImage\nTo run the AppImage, make sure it's executable. Then simply run it.\n\n```bash\nchmod +x imhex-*.AppImage\n./imhex-*.AppImage\n```\n\n#### Flatpak\nTo install the Flatpak, make sure you have the Flathub repository added to your system. Then simply run the following command:\n\n```bash\nflatpak install flathub net.werwolv.ImHex\n# or install the file directly\nflatpak install ./imhex-*.flatpak\n```\n\n#### Snap\n```bash\nsnap install ./imhex-*.snap\n```\n\n#### Ubuntu DEB Package\nTo install the DEB package, simply run the following command:\n\n```bash\nsudo apt install ./imhex-*.deb\n```\n\n#### Arch Linux\nTo install the Arch Linux package, simply run the following command:\n\n```bash\nsudo pacman -U imhex-*.pkg.tar.zst\n```\n\n#### Fedora / RHEL / AlmaLinux RPM Package\nTo install the RPM package, simply run the following command:\n\n```bash\nsudo dnf install ./imhex-*.rpm\n```\n\n## Nightly Builds\n\nThe GitHub Actions CI builds a new release package on every commit made to repository. These builds are available on the [GitHub Actions page](https://github.com/WerWolv/ImHex/actions?query=workflow%3A%22Build%22).\nThese builds are not guaranteed to be stable and may contain bugs, however they also contain new features that are not yet available in the official releases.\n\n## Building from source\n\nBuild instructions for Windows, Linux and macOS can be found under `/dist/compiling`:\n- Windows: [Link](dist/compiling/windows.md)\n- macOS: [Link](dist/compiling/macos.md)\n- Linux: [Link](dist/compiling/linux.md)\n\n## Package managers\n\nImHex is also available on various package managers. The officially supported ones are listed here:\n\n### Windows\n\n- **Chocolatey**\n  - [imhex](https://community.chocolatey.org/packages/imhex) (Thanks to @Jarcho)\n    - `choco install imhex`\n- **Winget**\n  - [WerWolv.ImHex](https://github.com/microsoft/winget-pkgs/tree/master/manifests/w/WerWolv/ImHex)\n    - `winget install WerWolv.ImHex`\n\n### Linux\n- **Arch Linux AUR**\n    - [imhex-bin](https://aur.archlinux.org/packages/imhex-bin/) (Thanks to @iTrooz)\n      - `yay -S imhex-bin`\n    - [imhex](https://aur.archlinux.org/packages/imhex/) (Thanks to @KokaKiwi)\n      - `yay -S imhex`\n- **Fedora**\n  - [imhex](https://src.fedoraproject.org/rpms/imhex/) (Thanks to @jonathanspw)\n    - `dnf install imhex`\n- **Flatpak**\n  - [net.werwolv.Imhex](https://flathub.org/apps/details/net.werwolv.ImHex) (Thanks to @Mailaender)\n    - `flatpak install flathub net.werwolv.ImHex`\n\n### Available on other package managers\n\nPackages that aren't explicitly mentioned above are not officially supported but they will most likely still work.\nContact the maintainer of the package if you have any issues.\n\n[![Packaging status](https://repology.org/badge/vertical-allrepos/imhex.svg)](https://repology.org/project/imhex/versions)\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "PLUGINS.md",
    "content": "# Plugins\r\n\r\nImHex is entirely built around the possibility to easily load plugins (most of it's features are actually implemented as a plugin!).\r\n\r\nTo install plugins, simply download the relevant `.hexplug` file and drop it in your `plugins` folder. The location of that folder can be found under `Help -> About -> ImHex Directories`.\r\n\r\n## Available Plugins\r\n\r\n(If you're developing a Plugin on your own, please feel free to add it to this list)\r\n\r\n### Official Plugins\r\n- [Extra Hashes Plugin](https://github.com/WerWolv/ImHex-Hashes-Plugin)\r\n  - Adds support for a variety of new hashes to the Hashes view including Blake, Adler32, Murmur and Tiger\r\n- [Discord RPC Plugin](https://github.com/WerWolv/ImHex-Plugin-DiscordRPC)\r\n  - Adds support for Discord Rich Presence\r\n\r\n### Third-Party Plugins\r\n- [Pcap Plugin](https://github.com/Professor-plum/ImHex-Plugin-Pcap)\r\n  - Adds support for reading packet capture files\r\n"
  },
  {
    "path": "PRIVACY.md",
    "content": "# Privacy Policy\n\nImHex collects **anonymous** user statistics based on the user's preferences which are set on first launch and can be opted in or out at any moment through the settings interface.\nThese statistics contain basic system information such as: ImHex Version, System Architecture, OS, OS Version or Linux Distro version of the GPU in use. This information is linked to a randomly generated ID which cannot be used to identify a specific user.\n\nAdditionally, we allow uploading of anonymized crash log files in case of an error. These are never uploaded automatically but only after explicit consent by the user. This decision is not saved so logs can be uploaded on a per-error basis.\n\nInformation collected may be analyzed by members of our development team and will never be shared with third parties outside of the team. We may occasionally share general usage statistics publically in a summarized manner (For example a graph stating 70% of users are using a specific OS). We will never share information about individual users, even if they are anonymous."
  },
  {
    "path": "README.md",
    "content": "<a href=\"https://imhex.werwolv.net\">\n  <h1 align=\"center\">\n    <picture>\n      <img height=\"300px\" style=\"margin: 0; padding: 0\" src=\"./resources/dist/common/logo/ImHexLogoSVGBG.svg\">\n    </picture>\n  </h1>\n</a>\n\n<p align=\"center\">\n    A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.\n    <br>\n    <a href=\"https://itinerarium.github.io/phoneme-synthesis/?w=/'ˈɪmhɛks/\"><strong>/ˈɪmhɛks/</strong></a>\n</p>\n<p align=\"center\">\n  <a title=\"'Build' workflow Status\" href=\"https://github.com/WerWolv/ImHex/actions?query=workflow%3ABuild\"><img alt=\"'Build' workflow Status\" src=\"https://img.shields.io/github/actions/workflow/status/WerWolv/ImHex/build.yml?longCache=true&style=for-the-badge&label=Build&logoColor=fff&logo=GitHub%20Actions&branch=master\"></a>\n  <a title=\"Discord Server\" href=\"https://discord.gg/X63jZ36xBY\"><img alt=\"Discord Server\" src=\"https://img.shields.io/discord/789833418631675954?label=Discord&logo=Discord&logoColor=fff&style=for-the-badge\"></a>\n  <a title=\"Total Downloads\" href=\"https://github.com/WerWolv/ImHex/releases/latest\"><img alt=\"Total Downloads\" src=\"https://img.shields.io/github/downloads/WerWolv/ImHex/total?longCache=true&style=for-the-badge&label=Downloads&logoColor=fff&logo=GitHub\"></a>\n  <a title=\"Code Quality\" href=\"https://www.codefactor.io/repository/github/werwolv/imhex\"><img alt=\"Code Quality\" src=\"https://img.shields.io/codefactor/grade/github/WerWolv/ImHex?longCache=true&style=for-the-badge&label=Code%20Quality&logoColor=fff&logo=CodeFactor&branch=master\"></a>\n  <a title=\"Translation\" href=\"https://weblate.werwolv.net/projects/imhex/\"><img alt=\"Translation\" src=\"https://img.shields.io/weblate/progress/imhex?logo=weblate&logoColor=%23FFFFFF&server=https%3A%2F%2Fweblate.werwolv.net&style=for-the-badge\"></a>\n  <a title=\"Plugins\" href=\"https://github.com/WerWolv/ImHex/blob/master/PLUGINS.md\"><img alt=\"Plugins\" src=\"https://img.shields.io/badge/Plugins-Supported-brightgreen?logo=stackedit&logoColor=%23FFFFFF&style=for-the-badge\"></a>\n</p>\n\n<p align=\"center\">\n  <a title=\"Download the latest version of ImHex\" href=\"https://imhex.download\"><img alt=\"Download the latest version of ImHex!\" src=\"resources/dist/common/get_release_banner.png\"></a>\n  <a title=\"Download the latest nightly pre-release version of ImHex\" href=\"https://imhex.download/#nightly\"><img alt=\"Download the latest nightly pre-release version of ImHex\" src=\"resources/dist/common/get_nightly_banner.png\"></a>\n  <a title=\"Use the Web version of ImHex right in your browser!\" href=\"https://web.imhex.werwolv.net\"><img alt=\"Use the Web version of ImHex right in your browser!\" src=\"resources/dist/common/try_online_banner.png\"></a>\n  <a title=\"Read the documentation of ImHex!\" href=\"https://docs.werwolv.net\"><img alt=\"Read the documentation of ImHex!\" src=\"resources/dist/common/read_docs_banner.png\"></a>\n</p>\n\n## Supporting\n\nIf you like my work, please consider supporting me on GitHub Sponsors, Ko-Fi or PayPal. Thanks a lot!\n\n<p align=\"center\">\n<a href=\"https://github.com/sponsors/WerWolv\"><img src=\"https://werwolv.net/assets/github_banner.png\" alt=\"GitHub donate button\" /></a>\n<a href=\"https://ko-fi.com/WerWolv\"><img src=\"https://werwolv.net/assets/kofi_banner.png\" alt=\"Ko-Fi donate button\" /></a>\n<a href=\"https://werwolv.net/donate\"><img src=\"https://werwolv.net/assets/paypal_banner.png\" alt=\"PayPal donate button\" /></a>\n</p>\n\n### Notable Sponsors\n|                                                                                                     |                                                                                   |\n|:---------------------------------------------------------------------------------------------------:|-----------------------------------------------------------------------------------|\n| [![JetBrains logo](https://avatars.githubusercontent.com/u/878437?s=48)](https://www.jetbrains.com) | JetBrains, providing us with free All Products Pack licenses for development      |\n|   [![SignPath logo](https://avatars.githubusercontent.com/u/34448643?s=48)](https://signpath.io/)   | SignPath, providing us with free Code Signing Certificates for our Windows builds |\n|     [![AWS logo](https://avatars.githubusercontent.com/u/2232217?s=48)](https://aws.amazon.com)     | Amazon, providing us with free AWS Cloud Credits for our CI                       |\n\nWould you like to appear here as well? Contact us at [imhex@werwolv.net](mailto:imhex@werwolv.net)!\n\n## Screenshots\n![Hex editor, patterns and data information](https://github.com/user-attachments/assets/902a7c4c-410d-490f-999e-14c856fec027)\n![Bookmarks, data information, find view and data processor](https://github.com/user-attachments/assets/58eefa1f-31c9-4bb8-a1c1-8cdd8ddbd29f)\n\n<details>\n<summary><strong>More Screenshots</strong></summary>\n\n![Data Processor decrypting some data and displaying it as an image](https://github.com/WerWolv/ImHex/assets/10835354/d0623081-3094-4840-a8a8-647b38724db8)\n![STL Parser written in the Pattern Language visualizing a 3D model](https://github.com/WerWolv/ImHex/assets/10835354/62cbcd18-1c3f-4dd6-a877-2bf2bf4bb2a5)\n![Data Information view displaying various stats about the file](https://github.com/WerWolv/ImHex/assets/10835354/d4706c01-c258-45c9-80b8-fe7a10d5a1de)\n\n</details>\n\n## Features\n\n<details>\n  <summary><strong>Featureful hex view</strong></summary>\n\n  - Byte patching\n  - Patch management\n  - Infinite Undo/Redo\n  - \"Copy bytes as...\"\n    - Bytes\n    - Hex string\n    - C, C++, C#, Rust, Python, Java & JavaScript array\n    - ASCII-Art hex view\n    - HTML self-contained div\n  - Simple string and hex search\n  - Goto from start, end and current cursor position\n  - Colorful highlighting\n    - Configurable foreground highlighting rules\n    - Background highlighting using patterns, find results and bookmarks\n  - Displaying data as a list of many different types\n    - Hexadecimal integers (8, 16, 32, 64 bit)\n    - Signed and unsigned decimal integers (8, 16, 32, 64 bit)\n    - Floats (16, 32, 64 bit)\n    - RGBA8 Colors\n    - HexII\n    - Binary\n  - Decoding data as ASCII and custom encodings\n    - Built-in support for UTF-8, UTF-16, ShiftJIS, most Windows encodings and many more\n  - Paged data view\n</details>\n<details>\n  <summary><strong>Custom C++-like pattern language for parsing highlighting a file's content</strong></summary>\n  \n  - Automatic loading based on MIME types and magic values\n  - Arrays, pointers, structs, unions, enums, bitfields, namespaces, little and big endian support, conditionals and much more!\n  - Useful error messages, syntax highlighting and error marking\n  - Support for visualizing many different types of data\n    - Images\n    - Audio\n    - 3D Models\n    - Coordinates\n    - Time stamps\n</details>\n<details>\n  <summary><strong>Theming support</strong></summary>\n\n  - Doesn't burn out your retinas when used in late-night sessions\n    - Dark mode by default, but a light mode is available as well\n  - Customizable colors and styles for all UI elements through shareable theme files\n  - Support for custom fonts\n</details>\n<details>\n  <summary><strong>Importing and Exporting data</strong></summary>\n  \n  - Base64 files\n  - IPS and IPS32 patches\n  - Markdown reports\n  - Binary arrays for various programming languages\n</details>\n<details>\n  <summary><strong>Data Inspector</strong></summary>\n\n  - Interpreting data as many different types with endianness, decimal, hexadecimal and octal support and bit inversion\n    - Unsigned and signed integers (8, 16, 24, 32, 48, 64 bit)\n    - Floats (16, 32, 64 bit)\n    - Signed and Unsigned LEB128\n    - ASCII, Wide and UTF-8 characters and strings\n    - time32_t, time64_t, DOS date and time\n    - GUIDs\n    - RGBA8 and RGB65 Colors\n  - Copying and modifying bytes through the inspector\n  - Adding new data types through the pattern language\n  - Support for hiding rows that aren't used\n</details>\n<details>\n  <summary><strong>Node-based data pre-processor</strong></summary>\n\n  - Modify, decrypt and decode data before it's being displayed in the hex editor\n  - Modify data without touching the underlying source\n  - Support for adding custom nodes\n</details>\n<details>\n  <summary><strong>Loading data from many different data sources</strong></summary>\n\n  - Local Files\n    - Support for huge files with fast and efficient loading\n  - Raw Disks\n    - Loading data from raw disks and partitions\n  - GDB Server\n    - Access the RAM of a running process or embedded devices through GDB\n  - Intel Hex and Motorola SREC data\n  - Base64 encoded data\n  - UDP Packets\n    - Support for displaying raw data received over UDP\n  - Process Memory\n    - Inspect the entire address space of a running process\n  - Remote Files over SSH with SFTP\n    - Support for loading files from remote servers using SSH and SFTP\n</details>\n<details>\n  <summary><strong>Data searching</strong></summary>\n  \n  - Support for searching the entire file or only a selection\n  - String extraction\n    - Option to specify minimum length and character set (lower case, upper case, digits, symbols)\n    - Option to specify encoding (ASCII, UTF-8, UTF-16 big and little endian)\n  - Sequence search\n    - Search for a sequence of bytes or characters\n    - Option to ignore character case\n  - Regex search\n    - Search for strings using regular expressions\n  - Binary Pattern\n    - Search for sequences of bytes with optional wildcards\n  - Numeric Value search\n    - Search for signed/unsigned integers and floats\n    - Search for ranges of values\n    - Option to specify size and endianness\n    - Option to ignore unaligned values\n</details>\n<details>\n  <summary><strong>Data hashing support</strong></summary>\n\n  - Many different algorithms available\n    - CRC8, CRC16 and CRC32 with custom initial values and polynomials\n      - Many default polynomials available\n    - MD5\n    - SHA-1, SHA-224, SHA-256, SHA-384, SHA-512\n    - Adler32\n    - AP\n    - BKDR\n    - Bernstein, Bernstein1\n    - DEK, DJB, ELF, FNV1, FNV1a, JS, PJW, RS, SDBM\n    - OneAtTime, Rotating, ShiftAndXor, SuperFast\n    - Murmur2_32, MurmurHash3_x86_32, MurmurHash3_x86_128, MurmurHash3_x64_128\n    - SipHash64, SipHash128\n    - XXHash32, XXHash64\n    - Tiger, Tiger2\n    - Blake2B, Blake2S\n  - Hashing of specific regions of the loaded data\n  - Hashing of arbitrary strings\n</details>\n<details>\n  <summary><strong>Diffing support</strong></summary>\n\n  - Compare data of different data sources\n  - Difference highlighting\n  - Table view of differences\n</details>\n<details>\n  <summary><strong>Integrated disassembler</strong></summary>\n  \n  - Support for all architectures supported by Capstone\n    - ARM32 (ARM, Thumb, Cortex-M, AArch32)\n    - ARM64\n    - MIPS (MIPS32, MIPS64, MIPS32R6, Micro)\n    - x86 (16-bit, 32-bit, 64-bit)\n    - PowerPC (32-bit, 64-bit)\n    - SPARC\n    - IBM SystemZ\n    - xCORE\n    - M68K\n    - TMS320C64X\n    - M680X\n    - Ethereum\n    - RISC-V\n    - WebAssembly\n    - MOS65XX\n    - Berkeley Packet Filter\n  - Support for writing custom disassemblers for your own architectures\n</details>\n<details>\n  <summary><strong>Bookmarks</strong></summary>\n\n  - Support for bookmarks with custom names and colors\n  - Highlighting of bookmarked region in the hex editor\n  - Jump to bookmarks\n  - Open content of bookmark in a new tab\n  - Add comments to bookmarks\n</details>\n<details>\n  <summary><strong>Featureful data analyzer and visualizer</strong></summary>\n\n  - File magic-based file parser and MIME type database\n  - Byte type distribution graph\n  - Entropy graph\n  - Highest and average entropy\n  - Encrypted / Compressed file detection\n  - Digram and Layered distribution graphs\n</details>\n<details>\n  <summary><strong>YARA Rule support</strong></summary>\n\n  - Scan a file for vulnerabilities with official yara rules\n  - Highlight matches in the hex editor\n  - Jump to matches\n  - Apply multiple rules at once\n</details>\n<details>\n  <summary><strong>Helpful tools</strong></summary>\n\n  - Itanium, MSVC, Rust and D-Lang demangler based on LLVM\n  - ASCII table\n  - Regex replacer\n  - Mathematical expression evaluator (Calculator)\n  - Graphing calculator\n  - Hexadecimal Color picker with support for many different formats\n  - Base converter\n  - Byte swapper\n  - UNIX Permissions calculator\n  - Wikipedia term definition finder\n  - File utilities\n    - File splitter\n    - File combiner\n    - File shredder\n  - IEEE754 Float visualizer\n  - Division by invariant multiplication calculator\n  - TCP Client/Server\n  - Euclidean algorithm calculator\n  - HTTP Requests\n</details>\n<details>\n  <summary><strong>Built-in Content updater</strong></summary>\n\n  - Download all files found in the database directly from within ImHex\n    - Pattern files for decoding various file formats\n    - Libraries for the pattern language\n    - Magic files for file type detection\n    - Custom data processor nodes\n    - Custom encodings\n    - Custom themes\n    - Yara rules\n</details>\n<details>\n  <summary><strong>Modern Interface</strong></summary>\n\n  - Support for multiple workspaces\n  - Support for custom layouts\n  - Detachable windows\n</details>\n<details>\n  <summary><strong>Easy to get started</strong></summary>\n\n  - Support for many different languages\n  - Simplified mode for beginners\n  - Extensive documentation\n  - Many example files available on [the Database](https://github.com/WerWolv/ImHex-Patterns)\n  - Achievements guiding you through the features of ImHex\n  - Interactive tutorials\n</details>\n\n## Pattern Language\n\nThe Pattern Language is the completely custom programming language developed for ImHex.\nIt allows you to define structures and data types in a C-like syntax and then use them to parse and highlight a file's content.\n\n- Source Code: [Link](https://github.com/WerWolv/PatternLanguage/)\n- Documentation: [Link](https://docs.werwolv.net/pattern-language/)\n\n## Database\n\nFor format patterns, libraries, magic and constant files, check out the [ImHex-Patterns](https://github.com/WerWolv/ImHex-Patterns) repository. \n\n**Feel free to PR your own files there as well!**\n\n## Requirements\n\nTo use ImHex, the following minimal system requirements need to be met.\n\n> [!IMPORTANT]\n> ImHex requires a GPU with OpenGL 3.0 support in general.\n> There are releases available (with the `-NoGPU` suffix) that are software rendered and don't require a GPU, however these can be a lot slower than the GPU accelerated versions.\n> \n> If possible at all, make ImHex use the dedicated GPU on your system instead of the integrated one.\n> ImHex will usually run fine with integrated GPUs as well but certain Intel HD GPU drivers on Windows are known to cause graphical artifacts.\n\n- **OS**: \n  - **Windows**: Windows 7 or higher (Windows 10/11 recommended)\n  - **macOS**: macOS 15 (Sequoia) or higher, \n    - Lower versions should still work too, but you'll need to compile ImHex yourself. The release binaries will NOT work due to GitHub not having any macOS 15 or lower CI runners available.\n    - The macOS build is not signed and will require you to manually allow them in the Security & Privacy settings.\n  - **Linux**: \"Modern\" Linux. The following distributions have official releases available. Other distros are supported through the AppImage, Flatpak and Snap releases.\n    - Ubuntu and Debian\n    - Fedora\n    - RHEL/AlmaLinux\n    - Arch Linux\n    - Basically any other distro will work as well when compiling ImHex from sources.\n  - **FreeBSD**: Tested on FreeBSD 14.3\n    - Other versions will most likely work too but are untested\n- **CPU**: Officially supported are x86, AMD64 and ARM64, though any Little Endian CPU should work.\n- **GPU**: OpenGL 3.0 or higher \n  - Integrated Intel HD iGPUs are supported, however certain drivers are known to cause various graphical artifacts, especially on Windows. Use at your own risk.\n  - In case you don't have a GPU available, there are software rendered releases available for Windows and macOS\n- **RAM**: ~50MiB, more is required for more complex analysis\n- **Storage**: ~100MiB\n\n## Installing\n\nInformation on how to install ImHex can be found in the [Install](/INSTALL.md) guide\n\n## Compiling\n\nTo compile ImHex on any platform, GCC (or Clang) is required with a version that supports C++23 or higher. \nWindows and Linux releases are being built using latest available GCC.\nMacOS releases are being built using latest available LLVM Clang.\n\nImportant to note is, the MSVC and AppleClang compilers are both **NOT** supported since they're both generally severely outdated and lack features GCC and LLVM Clang have.\n\n> [!NOTE]\n> Many dependencies are bundled into the repository using submodules so make sure to clone it using the `--recurse-submodules` option.\n> All dependencies that aren't bundled, can be installed using the dependency installer scripts found in the `/dist` folder.\n\nFor more information, check out the [Compiling](/dist/compiling) guide.\n\n## Contributing\nSee [Contributing](/CONTRIBUTING.md)\n\n\n## Plugin development\nTo develop plugins for ImHex, use the following template project to get started. You then have access to the entirety of libimhex as well as the ImHex API and the Content Registry to interact with ImHex or to add new content.\nTo build a plugin, you will need to use our SDK\n\n### Getting the SDK locally\nYou can build the SDK by compiling ImHex like this:\n- `cmake -G Ninja -DIMHEX_BUNDLE_PLUGIN_SDK=ON -B build`\n- `cd build`\n- `DESTDIR=install ninja install`\nThe SDK will then be available at `install/usr/local/share/imhex/sdk`. You will need to set the variable `IMHEX_SDK_PATH` to that (absolute) path.\n\n### Getting the SDK in a Github Actions CI\nYou can use [this action](https://github.com/WerWolv/imhex-download-sdk) to automatically download the SDK to your Github Runner\n- [ImHex Plugin Template](https://github.com/WerWolv/ImHex-Plugin-Template)\n\n\n## Credits\n\n### Contributors\n\n- [AxCut](https://github.com/paxcut) for a gigantic amount of contributions to the Pattern Text Editor and tons of other parts of ImHex\n- [iTrooz](https://github.com/iTrooz) for getting ImHex onto the Web as well as hundreds of contributions in every part of the project\n- [jumanji144](https://github.com/jumanji144) for huge contributions to the Pattern Language and ImHex's infrastructure\n- [Mary](https://github.com/marysaka) for her immense help porting ImHex to macOS and help during development\n- [Roblabla](https://github.com/Roblabla) for adding MSI Installer support to ImHex\n- [Mailaender](https://github.com/Mailaender) for getting ImHex onto Flathub\n- Everybody else who has reported issues on Discord or GitHub that I had great conversations with :)\n\n### Dependencies\n\n- Thanks a lot to ocornut for their amazing [Dear ImGui](https://github.com/ocornut/imgui) which is used for building the entire interface\n  - Thanks to epezent for [ImPlot](https://github.com/epezent/implot) used to plot data in various places\n  - Thanks to Nelarius for [ImNodes](https://github.com/Nelarius/imnodes) used as base for the data processor \n  - Thanks to BalazsJako for [ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit) used for the pattern language syntax highlighting\n- Thanks to nlohmann for their [json](https://github.com/nlohmann/json) library used for configuration files\n- Thanks to vitaut for their [libfmt](https://github.com/fmtlib/fmt) library which makes formatting and logging so much better\n- Thanks to btzy for [nativefiledialog-extended](https://github.com/btzy/nativefiledialog-extended) and their great support, used for handling file dialogs on all platforms\n- Thanks to danyspin97 for [xdgpp](https://sr.ht/~danyspin97/xdgpp) used to handle folder paths on Linux\n- Thanks to aquynh for [capstone](https://github.com/aquynh/capstone) which is the base of the disassembly window\n- Thanks to rxi for [microtar](https://github.com/rxi/microtar) used for extracting downloaded store assets \n- Thanks to VirusTotal for [Yara](https://github.com/VirusTotal/yara) used by the Yara plugin\n- Thanks to Martinsos for [edlib](https://github.com/Martinsos/edlib) used for sequence searching in the diffing view\n- Thanks to ron4fun for [HashLibPlus](https://github.com/ron4fun/HashLibPlus) which implements every hashing algorithm under the sun\n- Thanks to mackron for [miniaudio](https://github.com/mackron/miniaudio) used to play audio files\n- Thanks to all other groups and organizations whose libraries are used in ImHex\n\n### License\n\nThe biggest part of ImHex is under the GPLv2-only license. \nNotable exceptions to this are the following parts which are under the LGPLv2.1 license:\n- **/lib/libimhex**: The library that allows Plugins to interact with ImHex.\n- **/plugins/ui**: The UI plugin library that contains some common UI elements that can be used by other plugins\n\nThe reason for this is to allow for proprietary plugins to be developed for ImHex.\n\n### Code Signing Policy\n\nFree code signing provided by [SignPath.io](https://about.signpath.io/),\ncertificate by [SignPath Foundation](https://signpath.org/).\n\nThis program will not transfer any information to other networked systems\nunless specifically requested by the user or the person installing or\noperating it.\n\n#### People with direct push access\n- [WerWolv](https://github.com/WerWolv)\n- [iTrooz](https://github.com/iTrooz)\n- [jumanji144](https://github.com/jumanji144)\n- [AxCut](https://github.com/paxcut)\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nSupported is generally only the latest version that can be downloaded from the Release tab. If you have issues, you might get instructed to use the Nightly release version instead.\nIf you built ImHex yourself and experience issues that are not present in the version built by GitHub, you're on your own.\n\n## Reporting a Vulnerability\n\nAny critical vulnerabilities can be reported through Discord (@werwolv).\n"
  },
  {
    "path": "VERSION",
    "content": "1.39.0.WIP"
  },
  {
    "path": "changelog.md",
    "content": ""
  },
  {
    "path": "cmake/build_helpers.cmake",
    "content": "# Some libraries we use set the BUILD_SHARED_LIBS variable to ON, which causes CMake to\n# display a warning about options being set using set() instead of option().\n# Explicitly set the policy to NEW to suppress the warning.\nset(CMAKE_POLICY_DEFAULT_CMP0077 NEW)\n\nset(CMAKE_POLICY_DEFAULT_CMP0063 NEW)\nset(CMAKE_POLICY_DEFAULT_CMP0141 NEW)\n\nif (POLICY CMP0177)\n    set(CMAKE_POLICY_DEFAULT_CMP0177 OLD)\n    cmake_policy(SET CMP0177 OLD)\nendif()\n\nfunction(getTarget target type)\n    get_target_property(IMPORTED_TARGET ${target} IMPORTED)\n    if (IMPORTED_TARGET)\n        set(${type} INTERFACE PARENT_SCOPE)\n    else()\n        set(${type} PRIVATE PARENT_SCOPE)\n    endif()\nendfunction()\n\nfunction(addCFlag)\n    if (ARGC EQUAL 1)\n        add_compile_options($<$<COMPILE_LANGUAGE:C>:${ARGV0}>)\n    elseif (ARGC EQUAL 2)\n        getTarget(${ARGV1} TYPE)\n        target_compile_options(${ARGV1} ${TYPE} $<$<COMPILE_LANGUAGE:C>:${ARGV0}>)\n    endif()\nendfunction()\n\nfunction(addCXXFlag)\n    if (ARGC EQUAL 1)\n        add_compile_options($<$<COMPILE_LANGUAGE:CXX>:${ARGV0}>)\n    elseif (ARGC EQUAL 2)\n        getTarget(${ARGV1} TYPE)\n        target_compile_options(${ARGV1} ${TYPE} $<$<COMPILE_LANGUAGE:CXX>:${ARGV0}>)\n    endif()\nendfunction()\n\nfunction(addObjCFlag)\n    if (ARGC EQUAL 1)\n        add_compile_options($<$<COMPILE_LANGUAGE:OBJC>:${ARGV0}>)\n    elseif (ARGC EQUAL 2)\n        getTarget(${ARGV1} TYPE)\n        target_compile_options(${ARGV1} ${TYPE} $<$<COMPILE_LANGUAGE:OBJC>:${ARGV0}>)\n    endif()\nendfunction()\n\nfunction(addLinkerFlag)\n    if (ARGC EQUAL 1)\n        add_link_options(${ARGV0})\n    elseif (ARGC EQUAL 2)\n        getTarget(${ARGV1} TYPE)\n        target_link_options(${ARGV1} ${TYPE} ${ARGV0})\n    endif()\nendfunction()\n\nfunction(addCCXXFlag)\n    addCFlag(${ARGV0} ${ARGV1})\n    addCXXFlag(${ARGV0} ${ARGV1})\nendfunction()\n\nfunction(addCommonFlag)\n    addCFlag(${ARGV0} ${ARGV1})\n    addCXXFlag(${ARGV0} ${ARGV1})\n    addObjCFlag(${ARGV0} ${ARGV1})\nendfunction()\n\nfunction(addCppCheck target)\n    if (NOT IMHEX_ENABLE_CPPCHECK)\n        return()\n    endif()\n\n    find_program(cppcheck_exe NAMES cppcheck REQUIRED)\n    if (NOT cppcheck_exe)\n        return()\n    endif()\n\n    set(target_build_dir $<TARGET_FILE_DIR:${target}>)\n    set(cppcheck_opts\n            --enable=all\n            --inline-suppr\n            --quiet\n            --std=c++23\n            --check-level=exhaustive\n            --error-exitcode=10\n            --suppressions-list=${CMAKE_SOURCE_DIR}/dist/cppcheck.supp\n            --checkers-report=${target_build_dir}/cppcheck-report.txt\n    )\n    set_target_properties(${target} PROPERTIES\n        CXX_CPPCHECK \"${cppcheck_exe};${cppcheck_opts}\"\n    )\nendfunction()\n\nset(CMAKE_WARN_DEPRECATED OFF CACHE BOOL \"Disable deprecated warnings\" FORCE)\n\ninclude(FetchContent)\n\nif(IMHEX_STRIP_RELEASE)\n    if(CMAKE_BUILD_TYPE STREQUAL \"Release\")\n        set(CPACK_STRIP_FILES TRUE)\n    endif()\n    if(CMAKE_CXX_COMPILER_ID MATCHES \"GNU|Clang\")\n        add_link_options($<$<CONFIG:RELEASE>:-s>)\n    endif()\nendif()\n\nmacro(addDefines)\n    if (NOT IMHEX_VERSION)\n        message(FATAL_ERROR \"IMHEX_VERSION is not defined\")\n    endif ()\n\n    set(CMAKE_RC_FLAGS \"${CMAKE_RC_FLAGS} -DPROJECT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} -DPROJECT_VERSION_MINOR=${PROJECT_VERSION_MINOR} -DPROJECT_VERSION_PATCH=${PROJECT_VERSION_PATCH} \")\n\n    set(IMHEX_VERSION_STRING ${IMHEX_VERSION})\n    if (CMAKE_BUILD_TYPE STREQUAL \"Release\")\n        set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING})\n        add_compile_definitions(NDEBUG)\n    elseif (CMAKE_BUILD_TYPE STREQUAL \"Debug\")\n        set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-Debug)\n        add_compile_definitions(DEBUG)\n    elseif (CMAKE_BUILD_TYPE STREQUAL \"RelWithDebInfo\")\n        set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING})\n        add_compile_definitions(NDEBUG)\n    elseif (CMAKE_BUILD_TYPE STREQUAL \"MinSizeRel\")\n        set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-MinSizeRel)\n        add_compile_definitions(NDEBUG)\n    endif ()\n\n    if (IMHEX_ENABLE_STD_ASSERTS)\n        add_compile_definitions(_GLIBCXX_DEBUG _GLIBCXX_VERBOSE)\n    endif()\n\n    if (IMHEX_STATIC_LINK_PLUGINS)\n        add_compile_definitions(IMHEX_STATIC_LINK_PLUGINS)\n    endif ()\nendmacro()\n\nfunction(addDefineToSource SOURCE DEFINE)\n    set_property(\n            SOURCE ${SOURCE}\n            APPEND\n            PROPERTY COMPILE_DEFINITIONS \"${DEFINE}\"\n    )\n\n    # Disable precompiled headers for this file\n    set_source_files_properties(${SOURCE} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)\nendfunction()\n\n# Detect current OS / System\nmacro(detectOS)\n    if (WIN32)\n        add_compile_definitions(OS_WINDOWS)\n        set(CMAKE_INSTALL_BINDIR \".\")\n        set(CMAKE_INSTALL_LIBDIR \".\")\n        set(PLUGINS_INSTALL_LOCATION \"plugins\")\n        add_compile_definitions(WIN32_LEAN_AND_MEAN)\n        add_compile_definitions(NOMINMAX)\n        add_compile_definitions(UNICODE)\n        add_compile_definitions(_CRT_SECURE_NO_WARNINGS)\n    elseif (APPLE)\n        add_compile_definitions(OS_MACOS)\n        set(CMAKE_INSTALL_BINDIR \".\")\n        set(CMAKE_INSTALL_LIBDIR \".\")\n        set(PLUGINS_INSTALL_LOCATION \"plugins\")\n        enable_language(OBJC)\n        enable_language(OBJCXX)\n    elseif (EMSCRIPTEN)\n        add_compile_definitions(OS_WEB)\n    elseif (UNIX AND NOT APPLE)\n        add_compile_definitions(OS_LINUX)\n        if (BSD AND BSD STREQUAL \"FreeBSD\")\n            add_compile_definitions(OS_FREEBSD)\n        endif()\n        include(GNUInstallDirs)\n\n        set(PLUGINS_INSTALL_LOCATION \"${CMAKE_INSTALL_LIBDIR}/imhex/plugins\")\n\n        # Add System plugin location for plugins to be loaded from\n        # IMPORTANT: This does not work for Sandboxed or portable builds such as the Flatpak or AppImage release\n        add_compile_definitions(SYSTEM_PLUGINS_LOCATION=\"${CMAKE_INSTALL_FULL_LIBDIR}/imhex\")\n\n    else ()\n        message(FATAL_ERROR \"Unknown / unsupported system!\")\n    endif()\n\nendmacro()\n\nmacro(configurePackingResources)\n    set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)\n\n    if (WIN32)\n        if (NOT (CMAKE_BUILD_TYPE STREQUAL \"Debug\"))\n            set(APPLICATION_TYPE WIN32)\n        endif ()\n\n        set(IMHEX_ICON \"${IMHEX_BASE_FOLDER}/resources/resource.rc\")\n\n        if (IMHEX_GENERATE_PACKAGE)\n            set(CPACK_GENERATOR \"WIX\")\n            set(CPACK_PACKAGE_NAME \"ImHex\")\n            set(CPACK_PACKAGE_VENDOR \"WerWolv\")\n            set(CPACK_WIX_VERSION 4)\n            set(CPACK_WIX_PRODUCT_GUID \"*\")\n            set(CPACK_WIX_UPGRADE_GUID \"05000E99-9659-42FD-A1CF-05C554B39285\")\n            set(CPACK_WIX_PRODUCT_ICON \"${PROJECT_SOURCE_DIR}/resources/dist/windows/icon.ico\")\n            set(CPACK_WIX_UI_BANNER \"${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_banner.png\")\n            set(CPACK_WIX_UI_DIALOG \"${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_dialog.png\")\n            set(CPACK_WIX_CULTURES \"en-US;de-DE;ja-JP;it-IT;pt-BR;zh-CN;zh-TW;ru-RU\")\n            set(CPACK_WIX_TEMPLATE \"${PROJECT_SOURCE_DIR}/resources/dist/windows/WIX.template.in\")\n            set(CPACK_WIX_EXTENSIONS \"WixToolset.UI.wixext\")\n\n            file(GLOB_RECURSE CPACK_WIX_EXTRA_SOURCES \"${PROJECT_SOURCE_DIR}/resources/dist/windows/wix/*.wxs\")\n\n            set(CPACK_PACKAGE_INSTALL_DIRECTORY \"ImHex\")\n            set_property(INSTALL \"$<TARGET_FILE_NAME:main>\"\n                    PROPERTY CPACK_START_MENU_SHORTCUTS \"ImHex\"\n            )\n            set(CPACK_RESOURCE_FILE_LICENSE \"${PROJECT_SOURCE_DIR}/resources/dist/windows/LICENSE.rtf\")\n        endif()\n    elseif (APPLE OR ${CMAKE_HOST_SYSTEM_NAME} MATCHES \"Darwin\")\n        set(IMHEX_ICON \"${IMHEX_BASE_FOLDER}/resources/dist/macos/AppIcon.icns\")\n        set(BUNDLE_NAME \"ImHex.app\")\n\n        if (IMHEX_MACOS_CREATE_BUNDLE)\n            set(APPLICATION_TYPE MACOSX_BUNDLE)\n            set_source_files_properties(${IMHEX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION \"Resources\")\n            set(MACOSX_BUNDLE_ICON_FILE \"AppIcon.icns\")\n            set(MACOSX_BUNDLE_INFO_STRING \"WerWolv\")\n            set(MACOSX_BUNDLE_BUNDLE_NAME \"ImHex\")\n            set(MACOSX_BUNDLE_INFO_PLIST \"${CMAKE_CURRENT_SOURCE_DIR}/resources/dist/macos/Info.plist.in\")\n            set(MACOSX_BUNDLE_BUNDLE_VERSION \"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\")\n            set(MACOSX_BUNDLE_GUI_IDENTIFIER \"net.WerWolv.ImHex\")\n            string(SUBSTRING \"${IMHEX_COMMIT_HASH_LONG}\" 0 7 COMMIT_HASH_SHORT)\n            set(MACOSX_BUNDLE_LONG_VERSION_STRING \"${PROJECT_VERSION}-${COMMIT_HASH_SHORT}\")\n            set(MACOSX_BUNDLE_SHORT_VERSION_STRING \"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}\")\n\n            string(TIMESTAMP CURR_YEAR \"%Y\")\n            set(MACOSX_BUNDLE_COPYRIGHT \"Copyright © 2020 - ${CURR_YEAR} WerWolv. All rights reserved.\" )\n            if (\"${CMAKE_GENERATOR}\" STREQUAL \"Xcode\")\n                set(IMHEX_BUNDLE_PATH \"${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${BUNDLE_NAME}\")\n            else ()\n                set(IMHEX_BUNDLE_PATH \"${CMAKE_BINARY_DIR}/${BUNDLE_NAME}\")\n            endif()\n\n            set(PLUGINS_INSTALL_LOCATION \"${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins\")\n            set(CMAKE_INSTALL_LIBDIR \"${IMHEX_BUNDLE_PATH}/Contents/Frameworks\")\n        endif()\n    endif()\nendmacro()\n\nmacro(addPluginDirectories)\n    file(MAKE_DIRECTORY \"plugins\")\n    foreach (plugin IN LISTS PLUGINS)\n        add_subdirectory(\"plugins/${plugin}\")\n        if (TARGET ${plugin})\n            set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY \"${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins\")\n            set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY \"${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins\")\n\n            if (APPLE)\n                if (IMHEX_MACOS_CREATE_BUNDLE)\n                    set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGINS_INSTALL_LOCATION})\n                endif ()\n            else ()\n                if (WIN32)\n                    get_target_property(target_type ${plugin} TYPE)\n                    if (target_type STREQUAL \"SHARED_LIBRARY\")\n                        install(TARGETS ${plugin} RUNTIME DESTINATION ${PLUGINS_INSTALL_LOCATION})\n                    else ()\n                        install(TARGETS ${plugin} LIBRARY DESTINATION ${PLUGINS_INSTALL_LOCATION})\n                    endif()\n                else()\n                    install(TARGETS ${plugin} LIBRARY DESTINATION ${PLUGINS_INSTALL_LOCATION})\n                endif()\n\n            endif()\n\n            add_dependencies(imhex_all ${plugin})\n        endif ()\n    endforeach()\nendmacro()\n\nmacro(createPackage)\n    if (WIN32)\n        # Install binaries directly in the prefix, usually C:\\Program Files\\ImHex.\n        set(CMAKE_INSTALL_BINDIR \".\")\n\n        set(PLUGIN_TARGET_FILES \"\")\n        foreach (plugin IN LISTS PLUGINS)\n            list(APPEND PLUGIN_TARGET_FILES \"$<TARGET_FILE:${plugin}>\")\n        endforeach ()\n\n        if (DEFINED VCPKG_TARGET_TRIPLET)\n            set(VCPKG_DEPS_FOLDER \"\")\n            if (CMAKE_BUILD_TYPE STREQUAL \"Debug\")\n                set(VCPKG_DEPS_FOLDER \"${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/debug/bin\")\n            else()\n                set(VCPKG_DEPS_FOLDER \"${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/bin\")\n            endif()\n\n            install(CODE \"set(VCPKG_DEPS_FOLDER \\\"${VCPKG_DEPS_FOLDER}\\\")\")\n        endif()\n\n        # Grab all dynamically linked dependencies.\n        install(CODE \"set(CMAKE_INSTALL_BINDIR \\\"${CMAKE_INSTALL_BINDIR}\\\")\")\n        install(CODE \"set(PLUGIN_TARGET_FILES \\\"${PLUGIN_TARGET_FILES}\\\")\")\n        install(CODE [[\n        file(GET_RUNTIME_DEPENDENCIES\n            EXECUTABLES ${PLUGIN_TARGET_FILES} $<TARGET_FILE:libimhex> $<TARGET_FILE:main>\n            RESOLVED_DEPENDENCIES_VAR _r_deps\n            UNRESOLVED_DEPENDENCIES_VAR _u_deps\n            CONFLICTING_DEPENDENCIES_PREFIX _c_deps\n            DIRECTORIES ${DEP_FOLDERS} $ENV{PATH}\n            POST_EXCLUDE_REGEXES \".*system32/.*\\\\.dll\"\n        )\n\n        if(_c_deps_FILENAMES AND _c_deps AND NOT (_c_deps STREQUAL \"\"))\n            message(WARNING \"Conflicting dependencies for library: \\\"${_c_deps}\\\"!\")\n        endif()\n\n        if (DEFINED VCPKG_DEPS_FOLDER)\n            file(GLOB VCPKG_DEPS \"${VCPKG_DEPS_FOLDER}/*.dll\")\n            list(APPEND _r_deps ${VCPKG_DEPS})\n        endif()\n\n        foreach(_file ${_r_deps})\n            file(INSTALL\n                DESTINATION \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}\"\n                TYPE SHARED_LIBRARY\n                FOLLOW_SYMLINK_CHAIN\n                FILES \"${_file}\"\n                )\n        endforeach()\n        ]])\n\n        downloadImHexPatternsFiles(\".\")\n    elseif(UNIX AND NOT APPLE)\n        set_target_properties(libimhex PROPERTIES SOVERSION ${IMHEX_VERSION})\n\n        configure_file(${IMHEX_BASE_FOLDER}/dist/DEBIAN/control.in ${CMAKE_BINARY_DIR}/DEBIAN/control)\n\n        install(FILES ${IMHEX_BASE_FOLDER}/LICENSE              DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex)\n        install(FILES ${IMHEX_BASE_FOLDER}/dist/imhex.desktop   DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)\n        install(FILES ${IMHEX_BASE_FOLDER}/dist/imhex.mime.xml  DESTINATION ${CMAKE_INSTALL_PREFIX}/share/mime/packages RENAME imhex.xml)\n        install(FILES ${IMHEX_BASE_FOLDER}/resources/icon.svg   DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps       RENAME imhex.svg)\n        downloadImHexPatternsFiles(\"./share/imhex\")\n\n        # install AppStream file\n        install(FILES ${IMHEX_BASE_FOLDER}/dist/net.werwolv.ImHex.metainfo.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo)\n    endif()\n\n    if (APPLE)\n        if (IMHEX_MACOS_CREATE_BUNDLE)\n            set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS} \"${IMHEX_SYSTEM_LIBRARY_PATH}\")\n            include(PostprocessBundle)\n\n            set_target_properties(libimhex PROPERTIES SOVERSION ${IMHEX_VERSION})\n\n            set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})\n            set_property(TARGET main PROPERTY MACOSX_BUNDLE_BUNDLE_NAME \"${MACOSX_BUNDLE_BUNDLE_NAME}\")\n\n            # Fix rpath\n            install(CODE \"execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \\\"@executable_path/../Frameworks/\\\" $<TARGET_FILE:main>)\")\n            install(CODE \"execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \\\"@executable_path/../Frameworks/\\\" $<TARGET_FILE:updater>)\")\n\n            downloadImHexPatternsFiles(\"${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS\")\n\n            install(TARGETS main BUNDLE DESTINATION \".\")\n            install(TARGETS updater DESTINATION \"${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS\")\n            install(\n                FILES ${IMHEX_BASE_FOLDER}/dist/cli/imhex.sh\n                DESTINATION \"${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS/cli\"\n                RENAME imhex\n                PERMISSIONS\n                    OWNER_READ OWNER_WRITE OWNER_EXECUTE\n                    GROUP_READ GROUP_EXECUTE\n                    WORLD_READ WORLD_EXECUTE\n            )\n\n            # Update library references to make the bundle portable\n            postprocess_bundle(imhex_all main)\n\n            # Enforce DragNDrop packaging.\n            set(CPACK_GENERATOR \"DragNDrop\")\n\n            set(CPACK_BUNDLE_ICON \"${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns\")\n            set(CPACK_BUNDLE_PLIST \"${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Info.plist\")\n\n            if (IMHEX_RESIGN_BUNDLE)\n                find_program(CODESIGN_PATH codesign)\n                if (CODESIGN_PATH)\n                    install(CODE \"message(STATUS \\\"Signing bundle '${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}'...\\\")\")\n                    install(CODE \"execute_process(COMMAND ${CODESIGN_PATH} --force --deep --entitlements ${CMAKE_SOURCE_DIR}/resources/macos/Entitlements.plist --sign - ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME} COMMAND_ERROR_IS_FATAL ANY)\")\n                endif()\n            endif()\n\n            install(CODE [[ message(STATUS \"MacOS Bundle finalized. DO NOT TOUCH IT ANYMORE! ANY MODIFICATIONS WILL BREAK IT FROM NOW ON!\") ]])\n        else()\n            downloadImHexPatternsFiles(\"${IMHEX_MAIN_OUTPUT_DIRECTORY}\")\n        endif()\n    else()\n        install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})\n        if (TARGET updater)\n            install(TARGETS updater RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})\n        endif()\n        if (TARGET main-forwarder)\n            install(TARGETS main-forwarder BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR})\n        endif()\n\n        if (WIN32)\n            install(\n                FILES ${IMHEX_BASE_FOLDER}/dist/cli/imhex.bat\n                DESTINATION ${CMAKE_INSTALL_BINDIR}/cli\n                RENAME imhex.bat\n                PERMISSIONS\n                    OWNER_READ OWNER_WRITE OWNER_EXECUTE\n                    GROUP_READ GROUP_EXECUTE\n                    WORLD_READ WORLD_EXECUTE\n            )\n        else()\n            install(\n                FILES ${IMHEX_BASE_FOLDER}/dist/cli/imhex.sh\n                DESTINATION ${CMAKE_INSTALL_PREFIX}/share/imhex\n                RENAME imhex\n                PERMISSIONS\n                    OWNER_READ OWNER_WRITE OWNER_EXECUTE\n                    GROUP_READ GROUP_EXECUTE\n                    WORLD_READ WORLD_EXECUTE\n            )\n        endif()\n    endif()\n\n    if (IMHEX_MACOS_CREATE_BUNDLE)\n        set(CPACK_BUNDLE_NAME \"ImHex\")\n\n        include(CPack)\n    endif()\nendmacro()\n\nfunction(JOIN OUTPUT GLUE)\n    set(_TMP_RESULT \"\")\n    set(_GLUE \"\") # effective glue is empty at the beginning\n    foreach(arg ${ARGN})\n        set(_TMP_RESULT \"${_TMP_RESULT}${_GLUE}${arg}\")\n        set(_GLUE \"${GLUE}\")\n    endforeach()\n    set(${OUTPUT} \"${_TMP_RESULT}\" PARENT_SCOPE)\nendfunction()\n\nmacro(configureCMake)\n    message(STATUS \"Configuring ImHex v${IMHEX_VERSION}\")\n\n    if (DEFINED CMAKE_TOOLCHAIN_FILE)\n        message(STATUS \"Using toolchain file: \\\"${CMAKE_TOOLCHAIN_FILE}\\\"\")\n    endif()\n\n    set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL \"Enable position independent code for all targets\" FORCE)\n\n    set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT \"$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>\")\n\n    # Configure use of recommended build tools\n    if (IMHEX_USE_DEFAULT_BUILD_SETTINGS)\n        message(STATUS \"Configuring CMake to use recommended build tools...\")\n\n        find_program(CCACHE_PATH ccache)\n        find_program(NINJA_PATH ninja)\n        find_program(LD_LLD_PATH ld.lld)\n        find_program(AR_LLVMLIBS_PATH llvm-ar)\n        find_program(RANLIB_LLVMLIBS_PATH llvm-ranlib)\n\n        if (CCACHE_PATH)\n            set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PATH})\n            set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PATH})\n        else ()\n            message(WARNING \"ccache not found!\")\n        endif ()\n\n        if (AR_LLVMLIBS_PATH)\n            set(CMAKE_AR ${AR_LLVMLIBS_PATH})\n        else ()\n            message(WARNING \"llvm-ar not found, using default ar!\")\n        endif ()\n\n        if (RANLIB_LLVMLIBS_PATH)\n            set(CMAKE_RANLIB ${RANLIB_LLVMLIBS_PATH})\n        else ()\n            message(WARNING \"llvm-ranlib not found, using default ranlib!\")\n        endif ()\n\n        if (LD_LLD_PATH)\n            set(CMAKE_LINKER ${LD_LLD_PATH})\n\n            if (NOT XCODE AND NOT MSVC)\n                add_link_options(\"-fuse-ld=lld\")\n                add_link_options(\"-fuse-ld=lld\")\n            endif()\n        else ()\n            message(WARNING \"lld not found, using default linker!\")\n        endif ()\n\n        if (NINJA_PATH)\n            set(CMAKE_GENERATOR Ninja)\n        else ()\n            message(WARNING \"ninja not found, using default generator!\")\n        endif ()\n    endif()\nendmacro()\n\nfunction(configureProject)\n    # Enable C and C++ languages\n    enable_language(C CXX)\n\n    if (XCODE)\n        # Support Xcode's multi configuration paradigm by placing built artifacts into separate directories\n        set(IMHEX_MAIN_OUTPUT_DIRECTORY \"${CMAKE_BINARY_DIR}/Configs/$<CONFIG>\" PARENT_SCOPE)\n    else()\n        set(IMHEX_MAIN_OUTPUT_DIRECTORY \"${CMAKE_BINARY_DIR}\" PARENT_SCOPE)\n    endif()\n\n    # Enable LTO if desired and supported\n    if (IMHEX_ENABLE_LTO)\n        include(CheckIPOSupported)\n\n        check_ipo_supported(RESULT result OUTPUT output_error)\n        if (result OR WIN32)\n            set(CMAKE_INTERPROCEDURAL_OPTIMIZATION $<$<CONFIG:Release,RelWithDebInfo,MinSizeRel>:ON>)\n            message(STATUS \"LTO enabled!\")\n        else ()\n            message(WARNING \"LTO is not supported: ${output_error}\")\n        endif ()\n    endif ()\nendfunction()\n\nmacro(setDefaultBuiltTypeIfUnset)\n    if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n        set(CMAKE_BUILD_TYPE \"RelWithDebInfo\" CACHE STRING \"Using RelWithDebInfo build type as it was left unset\" FORCE)\n        set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS \"Debug\" \"RelWithDebInfo\")\n    endif()\nendmacro()\n\nfunction(loadVersion version plain_version)\n    set(VERSION_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/VERSION\")\n    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${VERSION_FILE})\n    file(READ \"${VERSION_FILE}\" read_version)\n    string(STRIP ${read_version} read_version)\n    string(REPLACE \".WIP\" \"\" read_version_plain ${read_version})\n    set(${version} ${read_version} PARENT_SCOPE)\n    set(${plain_version} ${read_version_plain} PARENT_SCOPE)\n\n    if (read_version MATCHES \".+\\.WIP\")\n        set(IMHEX_PATTERNS_PULL_MASTER ON PARENT_SCOPE)\n    endif()\nendfunction()\n\nfunction(detectBadClone)\n    if (IMHEX_IGNORE_BAD_CLONE)\n        return()\n    endif()\n\n    file (GLOB EXTERNAL_DIRS \"lib/external/*\" \"lib/third_party/*\")\n    foreach (EXTERNAL_DIR ${EXTERNAL_DIRS})\n        if(NOT IS_DIRECTORY \"${EXTERNAL_DIR}\")\n            continue()\n        endif()\n        file(GLOB_RECURSE RESULT \"${EXTERNAL_DIR}/*\")\n        list(LENGTH RESULT ENTRY_COUNT)\n        if(ENTRY_COUNT LESS_EQUAL 1)\n            message(FATAL_ERROR \"External dependency ${EXTERNAL_DIR} is empty!\\nMake sure to correctly clone ImHex using the --recurse-submodules git option or initialize the submodules manually.\")\n        endif()\n    endforeach ()\nendfunction()\n\nfunction(verifyCompiler)\n    if (IMHEX_IGNORE_BAD_COMPILER)\n        return()\n    endif()\n\n    if (CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS \"12.0.0\")\n        message(FATAL_ERROR \"ImHex requires GCC 12.0.0 or newer. Please use the latest GCC version.\")\n    elseif (CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS \"17.0.0\")\n        message(FATAL_ERROR \"ImHex requires Clang 17.0.0 or newer. Please use the latest Clang version.\")\n    elseif(CMAKE_CXX_COMPILER_ID STREQUAL \"MSVC\")\n        \n    elseif (NOT (CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\" OR CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\"))\n        message(FATAL_ERROR \"ImHex can only be compiled with GCC or Clang. ${CMAKE_CXX_COMPILER_ID} is not supported.\")\n    endif()\nendfunction()\n\nmacro(detectBundledPlugins)\n    file(GLOB PLUGINS_DIRS \"plugins/*\")\n\n    if (IMHEX_INCLUDE_PLUGINS)\n        set(PLUGINS ${IMHEX_INCLUDE_PLUGINS})\n    else()\n        foreach(PLUGIN_DIR ${PLUGINS_DIRS})\n            if (EXISTS \"${PLUGIN_DIR}/CMakeLists.txt\")\n                get_filename_component(PLUGIN_NAME ${PLUGIN_DIR} NAME)\n                if (NOT (${PLUGIN_NAME} IN_LIST IMHEX_EXCLUDE_PLUGINS))\n                    list(APPEND PLUGINS ${PLUGIN_NAME})\n                endif ()\n            endif()\n        endforeach()\n    endif()\n\n    foreach(PLUGIN_NAME ${PLUGINS})\n        message(STATUS \"Enabled bundled plugin '${PLUGIN_NAME}'\")\n    endforeach()\n\n    if (NOT PLUGINS)\n        message(FATAL_ERROR \"No bundled plugins enabled\")\n    endif()\n\n    set(REQUIRED_PLUGINS builtin fonts ui)\n    foreach(PLUGIN ${REQUIRED_PLUGINS})\n        list(FIND PLUGINS ${PLUGIN} PLUGIN_INDEX)\n        if (PLUGIN_INDEX EQUAL -1)\n            message(FATAL_ERROR \"Required plugin '${PLUGIN}' is not enabled!\")\n        endif()\n    endforeach()\nendmacro()\n\nmacro(setVariableInParent variable value)\n    get_directory_property(hasParent PARENT_DIRECTORY)\n\n    if (hasParent)\n        set(${variable} \"${value}\" PARENT_SCOPE)\n    else ()\n        set(${variable} \"${value}\")\n    endif ()\nendmacro()\n\nfunction(downloadImHexPatternsFiles dest)\n    if (NOT IMHEX_OFFLINE_BUILD)\n        if (IMHEX_PATTERNS_PULL_MASTER)\n            set(PATTERNS_BRANCH master)\n        else ()\n            set(PATTERNS_BRANCH ImHex-v${IMHEX_VERSION})\n        endif ()\n\n        set(imhex_patterns_SOURCE_DIR \"${CMAKE_CURRENT_BINARY_DIR}/ImHex-Patterns\")\n        install(CODE \"set(PATTERNS_BRANCH \\\"${PATTERNS_BRANCH}\\\")\")\n        install(CODE \"set(imhex_patterns_SOURCE_DIR \\\"${imhex_patterns_SOURCE_DIR}\\\")\")\n        install(CODE [[\n            message(STATUS \"Downloading ImHex patterns from branch '${PATTERNS_BRANCH}'...\")\n            if (NOT EXISTS \"${imhex_patterns_SOURCE_DIR}\")\n                file(MAKE_DIRECTORY \"${imhex_patterns_SOURCE_DIR}\")\n                \n                execute_process(\n                    COMMAND\n                        git clone --recurse-submodules --branch ${PATTERNS_BRANCH} https://github.com/WerWolv/ImHex-Patterns.git \"${imhex_patterns_SOURCE_DIR}\"\n                    COMMAND_ERROR_IS_FATAL ANY\n                )\n            endif()\n        ]])\n    else ()\n        set(imhex_patterns_SOURCE_DIR \"\")\n\n        # Maybe patterns are cloned to a subdirectory\n        if (NOT EXISTS ${imhex_patterns_SOURCE_DIR})\n            set(imhex_patterns_SOURCE_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ImHex-Patterns\")\n        endif()\n\n        # Or a sibling directory\n        if (NOT EXISTS ${imhex_patterns_SOURCE_DIR})\n            set(imhex_patterns_SOURCE_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/../ImHex-Patterns\")\n        endif()\n    endif ()\n\n    install(CODE \"set(imhex_patterns_SOURCE_DIR \\\"${imhex_patterns_SOURCE_DIR}\\\")\")\n\n    if(XCODE)\n        install(CODE [[\n            # The Xcode build has multiple configurations, which each need a copy of these files\n            file(GLOB_RECURSE sourceFilePaths LIST_DIRECTORIES NO CONFIGURE_DEPENDS RELATIVE \"${imhex_patterns_SOURCE_DIR}\"\n                \"${imhex_patterns_SOURCE_DIR}/constants/*\"\n                \"${imhex_patterns_SOURCE_DIR}/encodings/*\"\n                \"${imhex_patterns_SOURCE_DIR}/includes/*\"\n                \"${imhex_patterns_SOURCE_DIR}/patterns/*\"\n                \"${imhex_patterns_SOURCE_DIR}/magic/*\"\n                \"${imhex_patterns_SOURCE_DIR}/nodes/*\"\n            )\n            list(FILTER sourceFilePaths EXCLUDE REGEX \"_schema.json$\")\n\n            foreach(relativePath IN LISTS sourceFilePaths)\n                file(GENERATE OUTPUT \"${dest}/${relativePath}\" INPUT \"${imhex_patterns_SOURCE_DIR}/${relativePath}\")\n            endforeach()\n        ]])\n    else()\n        if (NOT (imhex_patterns_SOURCE_DIR STREQUAL \"\"))\n            set(PATTERNS_FOLDERS_TO_INSTALL constants encodings includes patterns magic nodes)\n            foreach (FOLDER ${PATTERNS_FOLDERS_TO_INSTALL})\n                install(DIRECTORY \"${imhex_patterns_SOURCE_DIR}/${FOLDER}\" DESTINATION \"${dest}\" PATTERN \"**/_schema.json\" EXCLUDE)\n            endforeach ()\n        endif()\n    endif ()\n\nendfunction()\n\n# Compress debug info. See https://github.com/WerWolv/ImHex/issues/1714 for relevant problem\nmacro(setupDebugCompressionFlag)\n    include(CheckCXXCompilerFlag)\n    include(CheckLinkerFlag)\n\n    check_cxx_compiler_flag(-gz=zstd ZSTD_AVAILABLE_COMPILER)\n    check_linker_flag(CXX -gz=zstd ZSTD_AVAILABLE_LINKER)\n    check_cxx_compiler_flag(-gz COMPRESS_AVAILABLE_COMPILER)\n    check_linker_flag(CXX -gz COMPRESS_AVAILABLE_LINKER)\n\n    if (NOT DEBUG_COMPRESSION_FLAG) # Cache variable\n        if (ZSTD_AVAILABLE_COMPILER AND ZSTD_AVAILABLE_LINKER)\n            message(\"Using Zstd compression for debug info because both compiler and linker support it\")\n            set(DEBUG_COMPRESSION_FLAG \"-gz=zstd\" CACHE STRING \"Cache to use for debug info compression\")\n        elseif (COMPRESS_AVAILABLE_COMPILER AND COMPRESS_AVAILABLE_LINKER)\n            message(\"Using default compression for debug info because both compiler and linker support it\")\n            set(DEBUG_COMPRESSION_FLAG \"-gz\" CACHE STRING \"Cache to use for debug info compression\")\n        else()\n            set(DEBUG_COMPRESSION_FLAG \"\" CACHE STRING \"Cache to use for debug info compression\")\n        endif()\n    endif()\n\n    addCommonFlag(${DEBUG_COMPRESSION_FLAG})\nendmacro()\n\nmacro(setupCompilerFlags target)\n    if (CMAKE_CXX_COMPILER_ID MATCHES \"MSVC\")\n        addCommonFlag(\"/W4\" ${target})\n        addCommonFlag(\"/wd4127\" ${target}) # conditional expression is constant\n        addCommonFlag(\"/wd4242\" ${target}) # 'identifier': conversion from 'type1' to 'type2', possible loss of data\n        addCommonFlag(\"/wd4244\" ${target}) # 'conversion': conversion from 'type1' to 'type2', possible loss of data\n        addCommonFlag(\"/wd4267\" ${target}) # 'var': conversion from 'size_t' to 'type', possible loss of data\n        addCommonFlag(\"/wd4305\" ${target}) # truncation from 'double' to 'float'\n        addCommonFlag(\"/wd4996\" ${target}) # 'function': was declared deprecated\n        addCommonFlag(\"/wd5244\" ${target}) # 'include' in the purview of module 'module' appears erroneous\n\n        if (IMHEX_STRICT_WARNINGS)\n            addCommonFlag(\"/WX\" ${target})\n        endif()\n    elseif (CMAKE_CXX_COMPILER_ID MATCHES \"GNU|Clang\")\n        addCommonFlag(\"-Wall\" ${target})\n        addCommonFlag(\"-Wextra\" ${target})\n        addCommonFlag(\"-Wpedantic\" ${target})\n\n        # Define strict compilation flags\n        if (IMHEX_STRICT_WARNINGS)\n             addCommonFlag(\"-Werror\" ${target})\n        endif()\n\n        if (UNIX AND NOT APPLE AND CMAKE_CXX_COMPILER_ID MATCHES \"GNU\")\n            addCommonFlag(\"-rdynamic\" ${target})\n        endif()\n\n        addCXXFlag(\"-fexceptions\" ${target})\n        addCXXFlag(\"-frtti\" ${target})\n        addCommonFlag(\"-fno-omit-frame-pointer\" ${target})\n\n        # Disable some warnings\n        addCCXXFlag(\"-Wno-array-bounds\" ${target})\n        addCCXXFlag(\"-Wno-deprecated-declarations\" ${target})\n        addCCXXFlag(\"-Wno-unknown-pragmas\" ${target})\n        addCXXFlag(\"-Wno-include-angled-in-module-purview\" ${target})\n\n        # Enable hardening flags\n        if (IMHEX_BUILD_HARDENING)\n            if (NOT CMAKE_BUILD_TYPE STREQUAL \"Debug\")\n                addCommonFlag(\"-U_FORTIFY_SOURCE\" ${target})\n                addCommonFlag(\"-D_FORTIFY_SOURCE=3\" ${target})\n\n                if (NOT EMSCRIPTEN)\n                    addCommonFlag(\"-fstack-protector-strong\" ${target})\n                endif()\n            endif()\n        endif()\n\n    endif()\n\n    if (CMAKE_CXX_COMPILER_ID MATCHES \"GNU\")\n        if (WIN32)\n            addLinkerFlag(\"-Wa,mbig-obj\" ${target})\n        endif ()\n    endif()\n\n    if (CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" AND APPLE)\n        addCCXXFlag(\"-Wno-unknown-warning-option\" ${target})\n\n        # On macOS, when using clang from Homebrew, properly setup the libc++ library path so\n        # it's using the one from Homebrew instead of the system one.\n        execute_process(COMMAND brew --prefix llvm OUTPUT_VARIABLE LLVM_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE)\n        if (NOT LLVM_PREFIX STREQUAL \"\" AND ${CMAKE_CXX_COMPILER} STREQUAL \"${LLVM_PREFIX}/bin/clang++\")\n            set(CMAKE_EXE_LINKER_FLAGS     \"${CMAKE_EXE_LINKER_FLAGS}    -L${LLVM_PREFIX}/lib/c++\")\n            set(CMAKE_SHARED_LINKER_FLAGS  \"${CMAKE_SHARED_LINKER_FLAGS} -L${LLVM_PREFIX}/lib/c++\")\n            set(CMAKE_MODULE_LINKER_FLAGS  \"${CMAKE_MODULE_LINKER_FLAGS} -L${LLVM_PREFIX}/lib/c++\")\n        endif()\n\n        if (CMAKE_BUILD_TYPE STREQUAL \"Debug\")\n            add_compile_definitions(_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG)\n        endif()\n    endif()\n\n    if (CMAKE_CXX_COMPILER_ID MATCHES \"MSVC\")\n        addCommonFlag(\"/bigobj\" ${target})\n        addCFlag(\"/std:clatest\" ${target})\n        addCXXFlag(\"/std:c++latest\" ${target})\n    endif()\n\n    # Disable some warnings for gcc\n    if (CMAKE_CXX_COMPILER_ID MATCHES \"GNU\")\n        addCCXXFlag(\"-Wno-restrict\" ${target})\n        addCCXXFlag(\"-Wno-stringop-overread\" ${target})\n        addCCXXFlag(\"-Wno-stringop-overflow\" ${target})\n        addCCXXFlag(\"-Wno-dangling-reference\" ${target})\n    endif()\n\n    # Define emscripten-specific disabled warnings\n    if (EMSCRIPTEN)\n        addCCXXFlag(\"-pthread\" ${target})\n        addCCXXFlag(\"-Wno-dollar-in-identifier-extension\" ${target})\n        addCCXXFlag(\"-Wno-pthreads-mem-growth\" ${target})\n    endif ()\n\n    if (IMHEX_COMPRESS_DEBUG_INFO)\n        setupDebugCompressionFlag()\n    endif()\n\n    # Only generate minimal debug information for stacktraces in RelWithDebInfo builds\n    if (CMAKE_BUILD_TYPE STREQUAL \"RelWithDebInfo\")\n        if (CMAKE_CXX_COMPILER_ID MATCHES \"GNU|Clang\")\n            addCCXXFlag(\"-g1\" ${target})\n        endif()\n\n        if (CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\")\n            # Add flags for debug info in inline functions\n            addCCXXFlag(\"-gstatement-frontiers\" ${target})\n            addCCXXFlag(\"-ginline-points\" ${target})\n        endif()\n    endif()\nendmacro()\n\n# uninstall target\nmacro(setUninstallTarget)\n    if(NOT TARGET uninstall)\n        configure_file(\n                \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in\"\n                \"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake\"\n                IMMEDIATE @ONLY)\n\n        add_custom_target(uninstall\n                COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)\n    endif()\nendmacro()\n\nmacro(addBundledLibraries)\n    # Make sure the build is using vcpkg on Windows and Emscripten, otherwise none of these dependencies will be found\n    if (MSVC OR EMSCRIPTEN)\n        if (NOT (CMAKE_TOOLCHAIN_FILE MATCHES \"vcpkg\"))\n            message(AUTHOR_WARNING \"Your current environment probably needs to be setup to use vcpkg, otherwise none of the dependencies will be found!\")\n        endif()\n    endif()\n\n    set(EXTERNAL_LIBS_FOLDER \"${CMAKE_CURRENT_SOURCE_DIR}/lib/external\")\n    set(THIRD_PARTY_LIBS_FOLDER \"${CMAKE_CURRENT_SOURCE_DIR}/lib/third_party\")\n    set(BUILD_SHARED_LIBS OFF)\n\n    add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/microtar EXCLUDE_FROM_ALL)\n\n    add_subdirectory(${EXTERNAL_LIBS_FOLDER}/libwolv EXCLUDE_FROM_ALL)\n\n    set(XDGPP_INCLUDE_DIRS \"${THIRD_PARTY_LIBS_FOLDER}/xdgpp\")\n    set(FPHSA_NAME_MISMATCHED ON CACHE BOOL \"\")\n\n    if(NOT USE_SYSTEM_FMT)\n        set(FMT_INSTALL OFF CACHE BOOL \"Disable install targets for libfmt\" FORCE)\n        add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/fmt EXCLUDE_FROM_ALL)\n        set(FMT_LIBRARIES fmt::fmt-header-only)\n    else()\n        find_package(fmt REQUIRED)\n        set(FMT_LIBRARIES fmt::fmt)\n    endif()\n\n    if (IMHEX_USE_GTK_FILE_PICKER)\n        set(NFD_PORTAL OFF CACHE BOOL \"Use Portals for Linux file dialogs\" FORCE)\n    else ()\n        set(NFD_PORTAL ON CACHE BOOL \"Use GTK for Linux file dialogs\" FORCE)\n    endif ()\n\n    if (NOT EMSCRIPTEN)\n        # curl\n        find_package(CURL REQUIRED)\n\n        # nfd\n        if (NOT USE_SYSTEM_NFD)\n            add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/nativefiledialog EXCLUDE_FROM_ALL)\n            set(NFD_LIBRARIES nfd)\n        else()\n            find_package(nfd)\n            set(NFD_LIBRARIES nfd)\n        endif()\n    endif()\n\n    if(NOT USE_SYSTEM_NLOHMANN_JSON)\n        add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/nlohmann_json EXCLUDE_FROM_ALL)\n        set(NLOHMANN_JSON_LIBRARIES nlohmann_json)\n    else()\n        find_package(nlohmann_json 3.10.2 REQUIRED)\n        set(NLOHMANN_JSON_LIBRARIES nlohmann_json::nlohmann_json)\n    endif()\n\n    if (NOT USE_SYSTEM_LUNASVG)\n        add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/lunasvg EXCLUDE_FROM_ALL)\n        set(LUNASVG_LIBRARIES lunasvg)\n    else()\n        find_package(lunasvg REQUIRED)\n        set(LUNASVG_LIBRARIES lunasvg::lunasvg)\n    endif()\n\n    if (NOT USE_SYSTEM_MD4C)\n        set(BUILD_MD2HTML_EXECUTABLE OFF CACHE BOOL \"Disable md2html executable\" FORCE)\n        add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/md4c EXCLUDE_FROM_ALL)\n        add_library(md4c_lib INTERFACE)\n        add_library(md4c::md4c ALIAS md4c_lib)\n        target_include_directories(md4c_lib INTERFACE ${THIRD_PARTY_LIBS_FOLDER}/md4c/src)\n        target_link_libraries(md4c_lib INTERFACE md4c)\n    else()\n        find_package(md4c REQUIRED)\n    endif()\n\n    if (NOT USE_SYSTEM_LLVM)\n        add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/llvm-demangle EXCLUDE_FROM_ALL)\n    else()\n        find_package(LLVM REQUIRED Demangle)\n    endif()\n\n    if (USE_SYSTEM_BOOST)\n        find_package(Boost REQUIRED CONFIG COMPONENTS regex)\n        set(BOOST_LIBRARIES Boost::regex)\n    else()\n        add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/boost ${CMAKE_CURRENT_BINARY_DIR}/boost EXCLUDE_FROM_ALL)\n        set(BOOST_LIBRARIES boost::regex)\n    endif()\n\n    set(LIBPL_BUILD_CLI_AS_EXECUTABLE OFF CACHE BOOL \"\" FORCE)\n    set(LIBPL_ENABLE_PRECOMPILED_HEADERS ${IMHEX_ENABLE_PRECOMPILED_HEADERS} CACHE BOOL \"\" FORCE)\n\n    set(LIBPL_SHARED_LIBRARY OFF CACHE BOOL \"\" FORCE)\n\n    add_subdirectory(${EXTERNAL_LIBS_FOLDER}/pattern_language EXCLUDE_FROM_ALL)\n    add_subdirectory(${EXTERNAL_LIBS_FOLDER}/disassembler EXCLUDE_FROM_ALL)\n\n    add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/imgui)\n\n    if (LIBPL_SHARED_LIBRARY)\n        install(\n            TARGETS\n                libpl\n            DESTINATION\n                \"${CMAKE_INSTALL_LIBDIR}\"\n            PERMISSIONS\n                OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE\n        )\n    endif()\n\n    if (WIN32)\n        set_target_properties(\n                libpl\n                PROPERTIES\n                    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}\n                    LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}\n        )\n    endif()\n    enableUnityBuild(libpl)\n\n    find_package(mbedTLS 3.4.0 REQUIRED)\n    find_package(Magic 5.39 REQUIRED)\nendmacro()\n\nfunction(enableUnityBuild TARGET)\n    if (IMHEX_ENABLE_UNITY_BUILD)\n        set_target_properties(${TARGET} PROPERTIES UNITY_BUILD ON UNITY_BUILD_MODE BATCH)\n    endif ()\nendfunction()\n\nfunction(setSDKPaths)\n    if (WIN32)\n        set(SDK_PATH \"./sdk\" PARENT_SCOPE)\n    elseif (APPLE)\n        set(SDK_PATH \"${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources/sdk\" PARENT_SCOPE)\n    else()\n        set(SDK_PATH \"share/imhex/sdk\" PARENT_SCOPE)\n    endif()\n\n    set(SDK_BUILD_PATH \"${CMAKE_BINARY_DIR}/sdk\" PARENT_SCOPE)\nendfunction()\n\nfunction(generateSDKDirectory)\n    setSDKPaths()\n    install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/libimhex DESTINATION \"${SDK_PATH}/lib\" PATTERN \"**/source/*\" EXCLUDE)\n    install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/trace    DESTINATION \"${SDK_PATH}/lib\" PATTERN \"**/source/*\" EXCLUDE)\n    install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/external DESTINATION \"${SDK_PATH}/lib\")\n    install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/imgui DESTINATION \"${SDK_PATH}/lib/third_party\" PATTERN \"**/source/*\" EXCLUDE)\n    if (NOT USE_SYSTEM_FMT)\n        install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/fmt DESTINATION \"${SDK_PATH}/lib/third_party\")\n    endif()\n    if (NOT USE_SYSTEM_NLOHMANN_JSON)\n        install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/nlohmann_json DESTINATION \"${SDK_PATH}/lib/third_party\")\n    endif()\n    if (NOT USE_SYSTEM_BOOST)\n        install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/boost DESTINATION \"${SDK_PATH}/lib/third_party\")\n    endif()\n\n    install(DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/modules DESTINATION \"${SDK_PATH}/cmake\")\n    install(FILES ${CMAKE_SOURCE_DIR}/cmake/build_helpers.cmake DESTINATION \"${SDK_PATH}/cmake\")\n    install(DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/sdk/ DESTINATION \"${SDK_PATH}\")\n    install(TARGETS libimhex ARCHIVE DESTINATION \"${SDK_PATH}/lib\")\n    install(TARGETS tracing ARCHIVE DESTINATION \"${SDK_PATH}/lib\")\n\n    install(DIRECTORY ${CMAKE_SOURCE_DIR}/plugins/ui/include DESTINATION \"${SDK_PATH}/lib/ui/include\")\n    install(FILES ${CMAKE_SOURCE_DIR}/plugins/ui/CMakeLists.txt DESTINATION \"${SDK_PATH}/lib/ui/\")\n    if (WIN32)\n        install(TARGETS ui ARCHIVE DESTINATION \"${SDK_PATH}/lib\")\n    endif()\n\n    install(DIRECTORY ${CMAKE_SOURCE_DIR}/plugins/fonts/include DESTINATION \"${SDK_PATH}/lib/fonts/include\")\n    install(FILES ${CMAKE_SOURCE_DIR}/plugins/fonts/CMakeLists.txt DESTINATION \"${SDK_PATH}/lib/fonts/\")\n    if (WIN32)\n        install(TARGETS fonts ARCHIVE DESTINATION \"${SDK_PATH}/lib\")\n    endif()\nendfunction()\n\nfunction(addIncludesFromLibrary target library)\n    get_target_property(library_include_dirs ${library} INTERFACE_INCLUDE_DIRECTORIES)\n    target_include_directories(${target} PRIVATE ${library_include_dirs})\nendfunction()\n\nfunction(precompileHeaders target includeFolder)\n    if (NOT IMHEX_ENABLE_PRECOMPILED_HEADERS)\n        return()\n    endif()\n\n    file(GLOB_RECURSE TARGET_INCLUDES \"${includeFolder}/**/*.hpp\")\n    file(GLOB_RECURSE LIBIMHEX_INCLUDES \"${CMAKE_SOURCE_DIR}/lib/libimhex/include/**/*.hpp\")\n    set(SYSTEM_INCLUDES \"<algorithm>;<array>;<atomic>;<chrono>;<cmath>;<cstddef>;<cstdint>;<cstdio>;<cstdlib>;<cstring>;<exception>;<filesystem>;<functional>;<iterator>;<limits>;<list>;<map>;<memory>;<optional>;<ranges>;<set>;<stdexcept>;<string>;<string_view>;<thread>;<tuple>;<type_traits>;<unordered_map>;<unordered_set>;<utility>;<variant>;<vector>\")\n    set(INCLUDES \"${SYSTEM_INCLUDES};${TARGET_INCLUDES};${LIBIMHEX_INCLUDES}\")\n    string(REPLACE \">\" \"$<ANGLE-R>\" INCLUDES \"${INCLUDES}\")\n    target_precompile_headers(${target}\n            PUBLIC\n            \"$<$<COMPILE_LANGUAGE:CXX>:${INCLUDES}>\"\n    )\nendfunction()\n"
  },
  {
    "path": "cmake/cmake_uninstall.cmake.in",
    "content": "if(NOT EXISTS \"@CMAKE_BINARY_DIR@/install_manifest.txt\")\n  message(FATAL_ERROR \"Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt\")\nendif()\n\nfile(READ \"@CMAKE_BINARY_DIR@/install_manifest.txt\" files)\nstring(REGEX REPLACE \"\\n\" \";\" files \"${files}\")\nforeach(file ${files})\n  message(STATUS \"Uninstalling $ENV{DESTDIR}${file}\")\n  if(IS_SYMLINK \"$ENV{DESTDIR}${file}\" OR EXISTS \"$ENV{DESTDIR}${file}\")\n    exec_program(\n      \"@CMAKE_COMMAND@\" ARGS \"-E remove \\\"$ENV{DESTDIR}${file}\\\"\"\n      OUTPUT_VARIABLE rm_out\n      RETURN_VALUE rm_retval\n      )\n    if(NOT \"${rm_retval}\" STREQUAL 0)\n      message(FATAL_ERROR \"Problem when removing $ENV{DESTDIR}${file}\")\n    endif()\n  else(IS_SYMLINK \"$ENV{DESTDIR}${file}\" OR EXISTS \"$ENV{DESTDIR}${file}\")\n    message(STATUS \"File $ENV{DESTDIR}${file} does not exist.\")\n  endif()\nendforeach()\n"
  },
  {
    "path": "cmake/ide_helpers.cmake",
    "content": "\noption(IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER \"Enable choice of compiler for Xcode builds, despite CMake's best efforts\" OFF)\noption(IMHEX_IDE_HELPERS_INTRUSIVE_IDE_TWEAKS    \"Enable intrusive CMake tweaks to better support IDEs with folder support\" OFF)\n\n# The CMake infrastructure silently ignores the CMAKE_<>_COMPILER settings when\n#  using the `Xcode` generator. \n#\n# A particularly nasty (and potentially only) way of getting around this is to \n#  temporarily lie about the generator being used, while CMake determines and\n#  locks in the compiler to use.\n#\n# Needless to say, this is hacky and fragile. Use at your own risk!\nif (IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER AND CMAKE_GENERATOR STREQUAL \"Xcode\")\n    set(CMAKE_GENERATOR \"Unknown\")\n    enable_language(C CXX)\n    \n    set(CMAKE_GENERATOR \"Xcode\")\n\n    set(CMAKE_XCODE_ATTRIBUTE_CC  \"${CMAKE_C_COMPILER}\")\n    set(CMAKE_XCODE_ATTRIBUTE_CXX \"${CMAKE_CXX_COMPILER}\")\n\n    if (CLANG)\n        set(CMAKE_XCODE_ATTRIBUTE_LD         \"${CMAKE_C_COMPILER}\")\n        set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS \"${CMAKE_CXX_COMPILER}\")\n    endif()\n\n    # By default Xcode passes a `-index-store-path=<...>` parameter to the compiler\n    #  during builds to build code completion indexes. This is not supported by\n    #  anything other than AppleClang\n    set(CMAKE_XCODE_ATTRIBUTE_COMPILER_INDEX_STORE_ENABLE \"NO\")\nendif()\n\n# Generate a launch/build scheme for all targets\nset(CMAKE_XCODE_GENERATE_SCHEME YES)\n\n# Utility function that helps avoid messing with non-standard targets\nmacro(returnIfTargetIsNonTweakable target)\n    get_target_property(targetIsAliased ${target} ALIASED_TARGET)\n    get_target_property(targetIsImported ${target} IMPORTED)\n\n    if (targetIsAliased OR targetIsImported)\n        return()\n    endif()\n\n    get_target_property(targetType ${target} TYPE)\n    if (targetType MATCHES \"INTERFACE_LIBRARY|UNKNOWN_LIBRARY\")\n        return()\n    endif()\nendmacro()\n\n# Targets usually don't specify their private headers, nor group their source files\n#  which results in very spotty coverage by IDEs with folders support\n#\n# Unfortunately, CMake does not have a `target_source_group` like construct yet, therefore \n#  we have to play by the limitations of `source_group`. \n#\n# A particularly problematic part is that the function must be called within the directoryies\n#  scope for the grouping to take effect.\n#\n# See: https://discourse.cmake.org/t/topic/7388\nfunction(tweakTargetForIDESupport target)\n    returnIfTargetIsNonTweakable(${target})\n\n    # Don't assume directory structure of third parties\n    get_target_property(targetSourceDir ${target} SOURCE_DIR)\n    if (targetSourceDir MATCHES \"third_party\")\n        return()\n    endif()\n\n    # Add headers to target\n    get_target_property(targetSourceDir ${target} SOURCE_DIR)\n    if (targetSourceDir)\n        file(GLOB_RECURSE targetPrivateHeaders CONFIGURE_DEPENDS \"${targetSourceDir}/include/*.hpp\")\n\n        target_sources(${target} PRIVATE \"${targetPrivateHeaders}\")\n    endif()\n\n    # Organize target sources into directory tree\n    get_target_property(sources ${target} SOURCES)\n    foreach(file IN LISTS sources)\n        get_filename_component(path \"${file}\" ABSOLUTE)\n\n        if (NOT path MATCHES \"^${targetSourceDir}\")\n            continue()\n        endif()\n\n        source_group(TREE \"${targetSourceDir}\" PREFIX \"Source Tree\" FILES \"${file}\")\n    endforeach()\nendfunction()\n\nif (IMHEX_IDE_HELPERS_INTRUSIVE_IDE_TWEAKS)\n    # See tweakTargetForIDESupport for rationale\n\n    function(add_library target)\n        _add_library(${target} ${ARGN})\n\n        tweakTargetForIDESupport(${target})\n    endfunction()\n\n    function(add_executable target)\n        _add_executable(${target} ${ARGN})\n\n        tweakTargetForIDESupport(${target})\n    endfunction()\nendif()\n\n# Adjust target's FOLDER property, which is an IDE only preference\nfunction(_tweakTarget target path)\n    get_target_property(targetType ${target} TYPE)\n\n    if (TARGET generator-${target})\n        set_target_properties(generator-${target} PROPERTIES FOLDER \"romfs/${target}\")\n    endif()\n    if (TARGET romfs_file_packer-${target})\n        set_target_properties(romfs_file_packer-${target} PROPERTIES FOLDER \"romfs/${target}\")\n    endif()\n    if (TARGET libromfs-${target})\n        set_target_properties(libromfs-${target} PROPERTIES FOLDER \"romfs/${target}\")\n    endif()\n\n    if (${targetType} MATCHES \"EXECUTABLE|LIBRARY\")\n        set_target_properties(${target} PROPERTIES FOLDER \"${path}\")\n    endif()\nendfunction()\n\nmacro(_tweakTargetsRecursive dir)\n    get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES)\n    foreach(subdir IN LISTS subdirectories)\n        _tweakTargetsRecursive(\"${subdir}\")\n    endforeach()\n\n    if(${dir} STREQUAL ${CMAKE_SOURCE_DIR})\n        return()\n    endif()\n\n    get_property(targets DIRECTORY \"${dir}\" PROPERTY BUILDSYSTEM_TARGETS)\n    file(RELATIVE_PATH rdir ${CMAKE_SOURCE_DIR} \"${dir}/..\")\n\n    foreach(target ${targets})\n        _tweakTarget(${target} \"${rdir}\")\n    endforeach()\nendmacro()\n\n# Tweak all targets this CMake build is aware about\nfunction(tweakTargetsForIDESupport)\n    set_property(GLOBAL PROPERTY USE_FOLDERS ON)\n\n    _tweakTargetsRecursive(\"${CMAKE_SOURCE_DIR}\")\nendfunction()\n"
  },
  {
    "path": "cmake/modules/FindBacktrace.cmake",
    "content": "# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying\n# file Copyright.txt or https://cmake.org/licensing for details.\n\n#[=======================================================================[.rst:\nFindBacktrace\n-------------\n\nFind provider for `backtrace(3) <https://man7.org/linux/man-pages/man3/backtrace.3.html>`__.\n\nChecks if OS supports ``backtrace(3)`` via either ``libc`` or custom library.\nThis module defines the following variables:\n\n``Backtrace_HEADER``\n  The header file needed for ``backtrace(3)``. Cached.\n  Could be forcibly set by user.\n``Backtrace_INCLUDE_DIRS``\n  The include directories needed to use ``backtrace(3)`` header.\n``Backtrace_LIBRARIES``\n  The libraries (linker flags) needed to use ``backtrace(3)``, if any.\n``Backtrace_FOUND``\n  Is set if and only if ``backtrace(3)`` support detected.\n\nThe following cache variables are also available to set or use:\n\n``Backtrace_LIBRARY``\n  The external library providing backtrace, if any.\n``Backtrace_INCLUDE_DIR``\n  The directory holding the ``backtrace(3)`` header.\n\nTypical usage is to generate of header file using :command:`configure_file`\nwith the contents like the following::\n\n #cmakedefine01 Backtrace_FOUND\n #if Backtrace_FOUND\n # include <${Backtrace_HEADER}>\n #endif\n\nAnd then reference that generated header file in actual source.\n#]=======================================================================]\n\ninclude(CMakePushCheckState)\ninclude(CheckSymbolExists)\ninclude(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)\n\n# List of variables to be provided to find_package_handle_standard_args()\nset(_Backtrace_STD_ARGS Backtrace_INCLUDE_DIR)\n\nif(Backtrace_HEADER)\n  set(_Backtrace_HEADER_TRY \"${Backtrace_HEADER}\")\nelse(Backtrace_HEADER)\n  set(_Backtrace_HEADER_TRY \"execinfo.h\")\nendif(Backtrace_HEADER)\n\nfind_path(Backtrace_INCLUDE_DIR \"${_Backtrace_HEADER_TRY}\")\nset(Backtrace_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})\n\nif (NOT DEFINED Backtrace_LIBRARY)\n  # First, check if we already have backtrace(), e.g., in libc\n  cmake_push_check_state(RESET)\n  set(CMAKE_REQUIRED_INCLUDES ${Backtrace_INCLUDE_DIRS})\n  set(CMAKE_REQUIRED_QUIET ${Backtrace_FIND_QUIETLY})\n  check_symbol_exists(\"backtrace\" \"${_Backtrace_HEADER_TRY}\" _Backtrace_SYM_FOUND)\n  cmake_pop_check_state()\nendif()\n\nif(_Backtrace_SYM_FOUND)\n  # Avoid repeating the message() call below each time CMake is run.\n  if(NOT Backtrace_FIND_QUIETLY AND NOT DEFINED Backtrace_LIBRARY)\n    message(STATUS \"backtrace facility detected in default set of libraries\")\n  endif()\n  set(Backtrace_LIBRARY \"\" CACHE FILEPATH \"Library providing backtrace(3), empty for default set of libraries\")\nelse()\n  # Check for external library, for non-glibc systems\n  if(Backtrace_INCLUDE_DIR)\n    # OpenBSD has libbacktrace renamed to libexecinfo\n    find_library(Backtrace_LIBRARY \"execinfo\")\n  else()     # respect user wishes\n    set(_Backtrace_HEADER_TRY \"backtrace.h\")\n    find_path(Backtrace_INCLUDE_DIR ${_Backtrace_HEADER_TRY})\n    find_library(Backtrace_LIBRARY \"backtrace\")\n  endif()\n\n  # Prepend list with library path as it's more common practice\n  set(_Backtrace_STD_ARGS Backtrace_LIBRARY ${_Backtrace_STD_ARGS})\nendif()\n\nset(Backtrace_LIBRARIES ${Backtrace_LIBRARY})\nset(Backtrace_HEADER \"${_Backtrace_HEADER_TRY}\" CACHE STRING \"Header providing backtrace(3) facility\")\n\nfind_package_handle_standard_args(Backtrace FOUND_VAR Backtrace_FOUND REQUIRED_VARS ${_Backtrace_STD_ARGS})\nmark_as_advanced(Backtrace_HEADER Backtrace_INCLUDE_DIR Backtrace_LIBRARY)"
  },
  {
    "path": "cmake/modules/FindCapstone.cmake",
    "content": "find_path(CAPSTONE_INCLUDE_DIR capstone.h PATH_SUFFIXES capstone)\nfind_library(CAPSTONE_LIBRARY NAMES capstone)\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package_handle_standard_args(Capstone DEFAULT_MSG CAPSTONE_LIBRARY CAPSTONE_INCLUDE_DIR)\n\nmark_as_advanced(CAPSTONE_INCLUDE_DIR CAPSTONE_LIBRARY)"
  },
  {
    "path": "cmake/modules/FindCoreClrEmbed.cmake",
    "content": "set(CoreClrEmbed_FOUND FALSE)\n\nset(CORECLR_ARCH \"linux-x64\")\nset(CORECLR_SUBARCH \"x64\")\nif (WIN32)\n    set(CORECLR_ARCH \"win-x64\")\nendif()\nif (UNIX)\n    if (CMAKE_SYSTEM_PROCESSOR STREQUAL \"aarch64\")\n        set(CORECLR_ARCH \"linux-arm64\")\n        set(CORECLR_SUBARCH \"arm64\")\n    endif()\nendif()\nif (APPLE)\n    if (CMAKE_SYSTEM_PROCESSOR STREQUAL \"arm64\")\n        set(CORECLR_ARCH \"osx-arm64\")\n        set(CORECLR_SUBARCH \"arm64\")\n    else()\n        set(CORECLR_ARCH \"osx-x64\")\n    endif()\nendif()\n\nif (NOT DOTNET_EXECUTABLE)\n    set(DOTNET_EXECUTABLE dotnet)\nendif ()\n\nset(CORECLR_VERSION \"8.0\")\n\nexecute_process(COMMAND ${DOTNET_EXECUTABLE} \"--list-runtimes\" OUTPUT_VARIABLE CORECLR_LIST_RUNTIMES_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE)\nif (CORECLR_LIST_RUNTIMES_OUTPUT STREQUAL \"\")\n    message(STATUS \"Unable to find any .NET runtimes\")\n    return()\nendif ()\n\nset(_ALL_RUNTIMES ${CORECLR_LIST_RUNTIMES_OUTPUT})\nstring(REPLACE \"\\n\" \";\" _ALL_RUNTIMES_LIST ${_ALL_RUNTIMES})\n\nforeach(X ${_ALL_RUNTIMES_LIST})\n    string(REGEX MATCH \"Microsoft\\.NETCore\\.App ([0-9]+)\\.([0-9]+)\\.([a-zA-Z0-9.-]+) [\\[](.*)Microsoft\\.NETCore\\.App[\\]]\"\n    CORECLR_VERSION_REGEX_MATCH ${X})\n\n    set(_RUNTIME_VERSION ${CMAKE_MATCH_1}.${CMAKE_MATCH_2})\n\n    if (CMAKE_MATCH_1 AND CMAKE_MATCH_4)\n        if (${_RUNTIME_VERSION} STREQUAL ${CORECLR_VERSION})\n            set(CORECLR_RUNTIME_VERSION ${_RUNTIME_VERSION})\n            set(CORECLR_RUNTIME_VERSION_FULL ${CORECLR_VERSION}.${CMAKE_MATCH_3})\n            set(CORECLR_RUNTIME_ROOT_PATH ${CMAKE_MATCH_4})\n            message(STATUS \"Found matching .NET runtime version '${CORECLR_RUNTIME_VERSION_FULL}' path='${CORECLR_RUNTIME_ROOT_PATH}'\")\n        endif()\n    endif()\nendforeach()\n\nif (CORECLR_RUNTIME_ROOT_PATH)\n    get_filename_component(CORECLR_RUNTIME_ROOT_PATH ${CORECLR_RUNTIME_ROOT_PATH} DIRECTORY)\nendif()\nset(CoreClrEmbed_ROOT_PATH \"${CORECLR_RUNTIME_ROOT_PATH}\")\n\n\nfile(GLOB _CORECLR_HOST_ARCH_PATH_LIST \"${CORECLR_RUNTIME_ROOT_PATH}/packs/Microsoft.NETCore.App.Host.*-${CORECLR_SUBARCH}\")\nif (_CORECLR_HOST_ARCH_PATH_LIST)\n    foreach(_CORECLR_HOST_ARCH_PATH ${_CORECLR_HOST_ARCH_PATH_LIST})\n        get_filename_component(_CORECLR_HOST_ARCH_FILENAME ${_CORECLR_HOST_ARCH_PATH} NAME)\n        string(REPLACE \"Microsoft.NETCore.App.Host.\" \"\" _CORECLR_COMPUTED_ARCH \"${_CORECLR_HOST_ARCH_FILENAME}\")\n        if (_CORECLR_COMPUTED_ARCH)\n            set(CORECLR_ARCH \"${_CORECLR_COMPUTED_ARCH}\")\n            break()\n        endif()\n    endforeach()\nendif()\n\nset(CORECLR_HOST_BASE_PATH \"${CORECLR_RUNTIME_ROOT_PATH}/packs/Microsoft.NETCore.App.Host.${CORECLR_ARCH}/${CORECLR_RUNTIME_VERSION_FULL}\")\nfile(GLOB _CORECLR_FOUND_PATH ${CORECLR_HOST_BASE_PATH})\nif (_CORECLR_FOUND_PATH)\n    set(CORECLR_NETHOST_ROOT \"${_CORECLR_FOUND_PATH}/runtimes/${CORECLR_ARCH}/native\")\nendif()\n\nfind_library(CoreClrEmbed_LIBRARY nethost PATHS\n    ${CORECLR_NETHOST_ROOT}\n)\nfind_path(CoreClrEmbed_INCLUDE_DIR nethost.h PATHS\n    ${CORECLR_NETHOST_ROOT}\n)\nfind_file(CoreClrEmbed_SHARED_LIBRARY nethost.dll nethost.so libnethost.so nethost.dylib libnethost.dylib PATHS\n    ${CORECLR_NETHOST_ROOT})\n\nif (CoreClrEmbed_INCLUDE_DIR AND CoreClrEmbed_LIBRARY)\n    set(CoreClrEmbed_FOUND TRUE)\n    set(CoreClrEmbed_LIBRARIES \"${CoreClrEmbed_LIBRARY}\" CACHE STRING \"CoreClrEmbed libraries\" FORCE)\n    set(CoreClrEmbed_SHARED_LIBRARIES \"${CoreClrEmbed_SHARED_LIBRARY}\" CACHE STRING \"CoreClrEmbed shared libraries\" FORCE)\n    set(CoreClrEmbed_INCLUDE_DIRS \"${CoreClrEmbed_INCLUDE_DIR}\" CACHE STRING \"CoreClrEmbed include directories\" FORCE)\n    set(CoreClrEmbed_VERSION \"${CORECLR_RUNTIME_VERSION_FULL}\" CACHE STRING \"CoreClrEmbed version\" FORCE)\nendif()"
  },
  {
    "path": "cmake/modules/FindGLFW.cmake",
    "content": "#.rst:\n# Find GLFW\n# ---------\n#\n# Finds the GLFW library using its cmake config if that exists, otherwise\n# falls back to finding it manually. This module defines:\n#\n#  GLFW_FOUND               - True if GLFW library is found\n#  GLFW::GLFW               - GLFW imported target\n#\n# Additionally, in case the config was not found, these variables are defined\n# for internal usage:\n#\n#  GLFW_LIBRARY             - GLFW library\n#  GLFW_DLL_DEBUG           - GLFW debug DLL on Windows, if found\n#  GLFW_DLL_RELEASE         - GLFW release DLL on Windows, if found\n#  GLFW_INCLUDE_DIR         - Root include dir\n#\n\n#\n#   This file is part of Magnum.\n#\n#   Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,\n#               2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>\n#   Copyright © 2016 Jonathan Hale <squareys@googlemail.com>\n#\n#   Permission is hereby granted, free of charge, to any person obtaining a\n#   copy of this software and associated documentation files (the \"Software\"),\n#   to deal in the Software without restriction, including without limitation\n#   the rights to use, copy, modify, merge, publish, distribute, sublicense,\n#   and/or sell copies of the Software, and to permit persons to whom the\n#   Software is furnished to do so, subject to the following conditions:\n#\n#   The above copyright notice and this permission notice shall be included\n#   in all copies or substantial portions of the Software.\n#\n#   THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n#   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n#   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n#   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n#   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n#   DEALINGS IN THE SOFTWARE.\n#\n\n# GLFW installs cmake package config files which handles dependencies in case\n# GLFW is built statically. Try to find first, quietly, so it doesn't print\n# loud messages when it's not found, since that's okay. If the glfw target\n# already exists, it means we're using it through a CMake subproject -- don't\n# attempt to find the package in that case.\nif(NOT TARGET glfw)\n    find_package(glfw3 CONFIG QUIET)\nendif()\n\n# If either a glfw config file was found or we have a subproject, point\n# GLFW::GLFW to that and exit -- nothing else to do here.\nif(TARGET glfw)\n    if(NOT TARGET GLFW::GLFW)\n        # Aliases of (global) targets are only supported in CMake 3.11, so we\n        # work around it by this. This is easier than fetching all possible\n        # properties (which are impossible to track of) and then attempting to\n        # rebuild them into a new target.\n        add_library(GLFW::GLFW INTERFACE IMPORTED)\n        set_target_properties(GLFW::GLFW PROPERTIES INTERFACE_LINK_LIBRARIES glfw)\n    endif()\n\n    # Just to make FPHSA print some meaningful location, nothing else\n    get_target_property(_GLFW_INTERFACE_INCLUDE_DIRECTORIES glfw INTERFACE_INCLUDE_DIRECTORIES)\n    include(FindPackageHandleStandardArgs)\n    find_package_handle_standard_args(\"GLFW\" DEFAULT_MSG\n            _GLFW_INTERFACE_INCLUDE_DIRECTORIES)\n\n    if(CORRADE_TARGET_WINDOWS)\n        # .dll is in LOCATION, .lib is in IMPLIB. Yay, useful!\n        get_target_property(GLFW_DLL_DEBUG glfw IMPORTED_LOCATION_DEBUG)\n        get_target_property(GLFW_DLL_RELEASE glfw IMPORTED_LOCATION_RELEASE)\n    endif()\n\n    return()\nendif()\n\nif(CORRADE_TARGET_WINDOWS)\n    if(MSVC)\n        if(MSVC_VERSION VERSION_LESS 1910)\n            set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2015)\n        elseif(MSVC_VERSION VERSION_LESS 1920)\n            set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2017)\n        elseif(MSVC_VERSION VERSION_LESS 1930)\n            set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2019)\n        elseif(MSVC_VERSION VERSION_LESS 1940)\n            set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2022)\n        else()\n            message(FATAL_ERROR \"Unsupported MSVC version\")\n        endif()\n    elseif(MINGW)\n        set(_GLFW_LIBRARY_PATH_SUFFIX lib-mingw-w64)\n    else()\n        message(FATAL_ERROR \"Unsupported compiler\")\n    endif()\nendif()\n\n# In case no config file was found, try manually finding the library. Prefer\n# the glfw3dll as it's a dynamic library.\nfind_library(GLFW_LIBRARY\n        NAMES glfw glfw3dll glfw3\n        PATH_SUFFIXES ${_GLFW_LIBRARY_PATH_SUFFIX})\n\nif(CORRADE_TARGET_WINDOWS AND GLFW_LIBRARY MATCHES \"glfw3dll.(lib|a)$\")\n    # TODO: debug?\n    find_file(GLFW_DLL_RELEASE\n            NAMES glfw3.dll\n            PATH_SUFFIXES ${_GLFW_LIBRARY_PATH_SUFFIX})\nendif()\n\n# Include dir\nfind_path(GLFW_INCLUDE_DIR\n        NAMES GLFW/glfw3.h)\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\"GLFW\" DEFAULT_MSG\n        GLFW_LIBRARY\n        GLFW_INCLUDE_DIR)\n\nif(NOT TARGET GLFW::GLFW)\n    add_library(GLFW::GLFW UNKNOWN IMPORTED)\n\n    # Work around BUGGY framework support on macOS\n    # https://cmake.org/Bug/view.php?id=14105\n    if(CORRADE_TARGET_APPLE AND GLFW_LIBRARY MATCHES \"\\\\.framework$\")\n        set_property(TARGET GLFW::GLFW PROPERTY IMPORTED_LOCATION ${GLFW_LIBRARY}/GLFW)\n    else()\n        set_property(TARGET GLFW::GLFW PROPERTY IMPORTED_LOCATION ${GLFW_LIBRARY})\n    endif()\n\n    set_property(TARGET GLFW::GLFW PROPERTY\n            INTERFACE_INCLUDE_DIRECTORIES ${GLFW_INCLUDE_DIR})\nendif()\n\nmark_as_advanced(GLFW_LIBRARY GLFW_INCLUDE_DIR)"
  },
  {
    "path": "cmake/modules/FindLZ4.cmake",
    "content": "find_path(LZ4_INCLUDE_DIR\n        NAMES lz4.h\n        HINTS \"${LZ4_INCLUDEDIR}\" \"${LZ4_HINTS}/include\"\n        PATHS\n        /usr/local/include\n        /usr/include\n)\n\nfind_library(LZ4_LIBRARY\n        NAMES lz4 liblz4\n        HINTS \"${LZ4_LIBDIR}\" \"${LZ4_HINTS}/lib\"\n        PATHS\n        /usr/local/lib\n        /usr/lib\n)\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args( LZ4 DEFAULT_MSG LZ4_LIBRARY LZ4_INCLUDE_DIR )\n\nif( LZ4_FOUND )\n    include( CheckIncludeFile )\n    include( CMakePushCheckState )\n\n    set( LZ4_INCLUDE_DIRS ${LZ4_INCLUDE_DIR} )\n    set( LZ4_LIBRARIES ${LZ4_LIBRARY} )\n\n    cmake_push_check_state()\n    set( CMAKE_REQUIRED_INCLUDES ${LZ4_INCLUDE_DIRS} )\n    check_include_file( lz4frame.h HAVE_LZ4FRAME_H )\n    cmake_pop_check_state()\n\n    if (WIN32)\n        set ( LZ4_DLL_DIR \"${LZ4_HINTS}/bin\"\n                CACHE PATH \"Path to LZ4 DLL\"\n        )\n        file( GLOB _lz4_dll RELATIVE \"${LZ4_DLL_DIR}\"\n                \"${LZ4_DLL_DIR}/lz4*.dll\"\n        )\n        set ( LZ4_DLL ${_lz4_dll}\n                # We're storing filenames only. Should we use STRING instead?\n                CACHE FILEPATH \"LZ4 DLL file name\"\n        )\n        file( GLOB _lz4_pdb RELATIVE \"${LZ4_DLL_DIR}\"\n                \"${LZ4_DLL_DIR}/lz4*.pdb\"\n        )\n        set ( LZ4_PDB ${_lz4_pdb}\n                CACHE FILEPATH \"LZ4 PDB file name\"\n        )\n        mark_as_advanced( LZ4_DLL_DIR LZ4_DLL LZ4_PDB )\n    endif()\nelse()\n    set( LZ4_INCLUDE_DIRS )\n    set( LZ4_LIBRARIES )\nendif()\n\nmark_as_advanced( LZ4_LIBRARIES LZ4_INCLUDE_DIRS )\n\nadd_library( LZ4::lz4 INTERFACE IMPORTED )\nset_property( TARGET LZ4::lz4 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${LZ4_INCLUDE_DIRS} )\nset_property( TARGET LZ4::lz4 PROPERTY INTERFACE_LINK_LIBRARIES ${LZ4_LIBRARIES} )"
  },
  {
    "path": "cmake/modules/FindMagic.cmake",
    "content": "find_path(LIBMAGIC_INCLUDE_DIR magic.h)\n\nfind_library(LIBMAGIC_LIBRARY NAMES magic)\n\nfind_package_handle_standard_args(Magic DEFAULT_MSG\n        LIBMAGIC_LIBRARY\n        LIBMAGIC_INCLUDE_DIR\n)\n\nmark_as_advanced(\n        LIBMAGIC_INCLUDE_DIR\n        LIBMAGIC_LIBRARY\n        Magic_FOUND\n)\n"
  },
  {
    "path": "cmake/modules/FindPackageHandleStandardArgs.cmake",
    "content": "# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying\n# file Copyright.txt or https://cmake.org/licensing for details.\n\n#[=======================================================================[.rst:\nFindPackageHandleStandardArgs\n-----------------------------\n\nThis module provides functions intended to be used in :ref:`Find Modules`\nimplementing :command:`find_package(<PackageName>)` calls.\n\n.. command:: find_package_handle_standard_args\n\n  This command handles the ``REQUIRED``, ``QUIET`` and version-related\n  arguments of :command:`find_package`.  It also sets the\n  ``<PackageName>_FOUND`` variable.  The package is considered found if all\n  variables listed contain valid results, e.g. valid filepaths.\n\n  There are two signatures:\n\n  .. code-block:: cmake\n\n    find_package_handle_standard_args(<PackageName>\n      (DEFAULT_MSG|<custom-failure-message>)\n      <required-var>...\n      )\n\n    find_package_handle_standard_args(<PackageName>\n      [FOUND_VAR <result-var>]\n      [REQUIRED_VARS <required-var>...]\n      [VERSION_VAR <version-var>]\n      [HANDLE_VERSION_RANGE]\n      [HANDLE_COMPONENTS]\n      [CONFIG_MODE]\n      [NAME_MISMATCHED]\n      [REASON_FAILURE_MESSAGE <reason-failure-message>]\n      [FAIL_MESSAGE <custom-failure-message>]\n      )\n\n  The ``<PackageName>_FOUND`` variable will be set to ``TRUE`` if all\n  the variables ``<required-var>...`` are valid and any optional\n  constraints are satisfied, and ``FALSE`` otherwise.  A success or\n  failure message may be displayed based on the results and on\n  whether the ``REQUIRED`` and/or ``QUIET`` option was given to\n  the :command:`find_package` call.\n\n  The options are:\n\n  ``(DEFAULT_MSG|<custom-failure-message>)``\n    In the simple signature this specifies the failure message.\n    Use ``DEFAULT_MSG`` to ask for a default message to be computed\n    (recommended).  Not valid in the full signature.\n\n  ``FOUND_VAR <result-var>``\n    .. deprecated:: 3.3\n\n    Specifies either ``<PackageName>_FOUND`` or\n    ``<PACKAGENAME>_FOUND`` as the result variable.  This exists only\n    for compatibility with older versions of CMake and is now ignored.\n    Result variables of both names are always set for compatibility.\n\n  ``REQUIRED_VARS <required-var>...``\n    Specify the variables which are required for this package.\n    These may be named in the generated failure message asking the\n    user to set the missing variable values.  Therefore these should\n    typically be cache entries such as ``FOO_LIBRARY`` and not output\n    variables like ``FOO_LIBRARIES``.\n\n    .. versionchanged:: 3.18\n      If ``HANDLE_COMPONENTS`` is specified, this option can be omitted.\n\n  ``VERSION_VAR <version-var>``\n    Specify the name of a variable that holds the version of the package\n    that has been found.  This version will be checked against the\n    (potentially) specified required version given to the\n    :command:`find_package` call, including its ``EXACT`` option.\n    The default messages include information about the required\n    version and the version which has been actually found, both\n    if the version is ok or not.\n\n  ``HANDLE_VERSION_RANGE``\n    .. versionadded:: 3.19\n\n    Enable handling of a version range, if one is specified. Without this\n    option, a developer warning will be displayed if a version range is\n    specified.\n\n  ``HANDLE_COMPONENTS``\n    Enable handling of package components.  In this case, the command\n    will report which components have been found and which are missing,\n    and the ``<PackageName>_FOUND`` variable will be set to ``FALSE``\n    if any of the required components (i.e. not the ones listed after\n    the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are\n    missing.\n\n  ``CONFIG_MODE``\n    Specify that the calling find module is a wrapper around a\n    call to ``find_package(<PackageName> NO_MODULE)``.  This implies\n    a ``VERSION_VAR`` value of ``<PackageName>_VERSION``.  The command\n    will automatically check whether the package configuration file\n    was found.\n\n  ``REASON_FAILURE_MESSAGE <reason-failure-message>``\n    .. versionadded:: 3.16\n\n    Specify a custom message of the reason for the failure which will be\n    appended to the default generated message.\n\n  ``FAIL_MESSAGE <custom-failure-message>``\n    Specify a custom failure message instead of using the default\n    generated message.  Not recommended.\n\n  ``NAME_MISMATCHED``\n    .. versionadded:: 3.17\n\n    Indicate that the ``<PackageName>`` does not match\n    ``${CMAKE_FIND_PACKAGE_NAME}``. This is usually a mistake and raises a\n    warning, but it may be intentional for usage of the command for components\n    of a larger package.\n\nExample for the simple signature:\n\n.. code-block:: cmake\n\n  find_package_handle_standard_args(LibXml2 DEFAULT_MSG\n    LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR)\n\nThe ``LibXml2`` package is considered to be found if both\n``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid.\nThen also ``LibXml2_FOUND`` is set to ``TRUE``.  If it is not found\nand ``REQUIRED`` was used, it fails with a\n:command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was\nused or not.  If it is found, success will be reported, including\nthe content of the first ``<required-var>``.  On repeated CMake runs,\nthe same message will not be printed again.\n\n.. note::\n\n  If ``<PackageName>`` does not match ``CMAKE_FIND_PACKAGE_NAME`` for the\n  calling module, a warning that there is a mismatch is given. The\n  ``FPHSA_NAME_MISMATCHED`` variable may be set to bypass the warning if using\n  the old signature and the ``NAME_MISMATCHED`` argument using the new\n  signature. To avoid forcing the caller to require newer versions of CMake for\n  usage, the variable's value will be used if defined when the\n  ``NAME_MISMATCHED`` argument is not passed for the new signature (but using\n  both is an error)..\n\nExample for the full signature:\n\n.. code-block:: cmake\n\n  find_package_handle_standard_args(LibArchive\n    REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR\n    VERSION_VAR LibArchive_VERSION)\n\nIn this case, the ``LibArchive`` package is considered to be found if\nboth ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid.\nAlso the version of ``LibArchive`` will be checked by using the version\ncontained in ``LibArchive_VERSION``.  Since no ``FAIL_MESSAGE`` is given,\nthe default messages will be printed.\n\nAnother example for the full signature:\n\n.. code-block:: cmake\n\n  find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4)\n  find_package_handle_standard_args(Automoc4  CONFIG_MODE)\n\nIn this case, a ``FindAutmoc4.cmake`` module wraps a call to\n``find_package(Automoc4 NO_MODULE)`` and adds an additional search\ndirectory for ``automoc4``.  Then the call to\n``find_package_handle_standard_args`` produces a proper success/failure\nmessage.\n\n.. command:: find_package_check_version\n\n  .. versionadded:: 3.19\n\n  Helper function which can be used to check if a ``<version>`` is valid\n  against version-related arguments of :command:`find_package`.\n\n  .. code-block:: cmake\n\n    find_package_check_version(<version> <result-var>\n      [HANDLE_VERSION_RANGE]\n      [RESULT_MESSAGE_VARIABLE <message-var>]\n      )\n\n  The ``<result-var>`` will hold a boolean value giving the result of the check.\n\n  The options are:\n\n  ``HANDLE_VERSION_RANGE``\n    Enable handling of a version range, if one is specified. Without this\n    option, a developer warning will be displayed if a version range is\n    specified.\n\n  ``RESULT_MESSAGE_VARIABLE <message-var>``\n    Specify a variable to get back a message describing the result of the check.\n\nExample for the usage:\n\n.. code-block:: cmake\n\n  find_package_check_version(1.2.3 result HANDLE_VERSION_RANGE\n    RESULT_MESSAGE_VARIABLE reason)\n  if (result)\n    message (STATUS \"${reason}\")\n  else()\n    message (FATAL_ERROR \"${reason}\")\n  endif()\n#]=======================================================================]\n\ninclude(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake)\n\n\ncmake_policy(PUSH)\n# numbers and boolean constants\ncmake_policy (SET CMP0012 NEW)\n# IN_LIST operator\ncmake_policy (SET CMP0057 NEW)\n\n\n# internal helper macro\nmacro(_FPHSA_FAILURE_MESSAGE _msg)\n  set (__msg \"${_msg}\")\n  if (FPHSA_REASON_FAILURE_MESSAGE)\n    string(APPEND __msg \"\\n    Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\\n\")\n  endif()\n  if (${_NAME}_FIND_REQUIRED)\n    message(FATAL_ERROR \"${__msg}\")\n  else ()\n    if (NOT ${_NAME}_FIND_QUIETLY)\n      message(STATUS \"${__msg}\")\n    endif ()\n  endif ()\nendmacro()\n\n\n# internal helper macro to generate the failure message when used in CONFIG_MODE:\nmacro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE)\n  # <PackageName>_CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found:\n  if(${_NAME}_CONFIG)\n    _FPHSA_FAILURE_MESSAGE(\"${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})\")\n  else()\n    # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version.\n    # List them all in the error message:\n    if(${_NAME}_CONSIDERED_CONFIGS)\n      set(configsText \"\")\n      list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount)\n      math(EXPR configsCount \"${configsCount} - 1\")\n      foreach(currentConfigIndex RANGE ${configsCount})\n        list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename)\n        list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version)\n        string(APPEND configsText \"\\n    ${filename} (version ${version})\")\n      endforeach()\n      if (${_NAME}_NOT_FOUND_MESSAGE)\n        if (FPHSA_REASON_FAILURE_MESSAGE)\n          string(PREPEND FPHSA_REASON_FAILURE_MESSAGE \"${${_NAME}_NOT_FOUND_MESSAGE}\\n    \")\n        else()\n          set(FPHSA_REASON_FAILURE_MESSAGE \"${${_NAME}_NOT_FOUND_MESSAGE}\")\n        endif()\n      else()\n        string(APPEND configsText \"\\n\")\n      endif()\n      _FPHSA_FAILURE_MESSAGE(\"${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}\")\n\n    else()\n      # Simple case: No Config-file was found at all:\n      _FPHSA_FAILURE_MESSAGE(\"${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}\")\n    endif()\n  endif()\nendmacro()\n\n\nfunction(FIND_PACKAGE_CHECK_VERSION version result)\n  cmake_parse_arguments (PARSE_ARGV 2 FPCV \"HANDLE_VERSION_RANGE;NO_AUTHOR_WARNING_VERSION_RANGE\" \"RESULT_MESSAGE_VARIABLE\" \"\")\n\n  if (FPCV_UNPARSED_ARGUMENTS)\n    message (FATAL_ERROR \"find_package_check_version(): ${FPCV_UNPARSED_ARGUMENTS}: unexpected arguments\")\n  endif()\n  if (\"RESULT_MESSAGE_VARIABLE\" IN_LIST FPCV_KEYWORDS_MISSING_VALUES)\n    message (FATAL_ERROR \"find_package_check_version(): RESULT_MESSAGE_VARIABLE expects an argument\")\n  endif()\n\n  set (${result} FALSE PARENT_SCOPE)\n  if (FPCV_RESULT_MESSAGE_VARIABLE)\n    unset (${FPCV_RESULT_MESSAGE_VARIABLE} PARENT_SCOPE)\n  endif()\n\n  if (_CMAKE_FPHSA_PACKAGE_NAME)\n    set (package \"${_CMAKE_FPHSA_PACKAGE_NAME}\")\n  elseif (CMAKE_FIND_PACKAGE_NAME)\n    set (package \"${CMAKE_FIND_PACKAGE_NAME}\")\n  else()\n    message (FATAL_ERROR \"find_package_check_version(): Cannot be used outside a 'Find Module'\")\n  endif()\n\n  if (NOT FPCV_NO_AUTHOR_WARNING_VERSION_RANGE\n      AND ${package}_FIND_VERSION_RANGE AND NOT FPCV_HANDLE_VERSION_RANGE)\n    message(AUTHOR_WARNING\n      \"`find_package()` specify a version range but the option \"\n      \"HANDLE_VERSION_RANGE` is not passed to `find_package_check_version()`. \"\n      \"Only the lower endpoint of the range will be used.\")\n  endif()\n\n\n  set (version_ok FALSE)\n  unset (version_msg)\n\n  if (FPCV_HANDLE_VERSION_RANGE AND ${package}_FIND_VERSION_RANGE)\n    if ((${package}_FIND_VERSION_RANGE_MIN STREQUAL \"INCLUDE\"\n          AND version VERSION_GREATER_EQUAL ${package}_FIND_VERSION_MIN)\n        AND ((${package}_FIND_VERSION_RANGE_MAX STREQUAL \"INCLUDE\"\n            AND version VERSION_LESS_EQUAL ${package}_FIND_VERSION_MAX)\n          OR (${package}_FIND_VERSION_RANGE_MAX STREQUAL \"EXCLUDE\"\n            AND version VERSION_LESS ${package}_FIND_VERSION_MAX)))\n      set (version_ok TRUE)\n      set(version_msg \"(found suitable version \\\"${version}\\\", required range is \\\"${${package}_FIND_VERSION_RANGE}\\\")\")\n    else()\n      set(version_msg \"Found unsuitable version \\\"${version}\\\", required range is \\\"${${package}_FIND_VERSION_RANGE}\\\"\")\n    endif()\n  elseif (DEFINED ${package}_FIND_VERSION)\n    if(${package}_FIND_VERSION_EXACT)       # exact version required\n      # count the dots in the version string\n      string(REGEX REPLACE \"[^.]\" \"\" version_dots \"${version}\")\n      # add one dot because there is one dot more than there are components\n      string(LENGTH \"${version_dots}.\" version_dots)\n      if (version_dots GREATER ${package}_FIND_VERSION_COUNT)\n        # Because of the C++ implementation of find_package() ${package}_FIND_VERSION_COUNT\n        # is at most 4 here. Therefore a simple lookup table is used.\n        if (${package}_FIND_VERSION_COUNT EQUAL 1)\n          set(version_regex \"[^.]*\")\n        elseif (${package}_FIND_VERSION_COUNT EQUAL 2)\n          set(version_regex \"[^.]*\\\\.[^.]*\")\n        elseif (${package}_FIND_VERSION_COUNT EQUAL 3)\n          set(version_regex \"[^.]*\\\\.[^.]*\\\\.[^.]*\")\n        else()\n          set(version_regex \"[^.]*\\\\.[^.]*\\\\.[^.]*\\\\.[^.]*\")\n        endif()\n        string(REGEX REPLACE \"^(${version_regex})\\\\..*\" \"\\\\1\" version_head \"${version}\")\n        if (NOT ${package}_FIND_VERSION VERSION_EQUAL version_head)\n          set(version_msg \"Found unsuitable version \\\"${version}\\\", but required is exact version \\\"${${package}_FIND_VERSION}\\\"\")\n        else ()\n          set(version_ok TRUE)\n          set(version_msg \"(found suitable exact version \\\"${_FOUND_VERSION}\\\")\")\n        endif ()\n      else ()\n        if (NOT ${package}_FIND_VERSION VERSION_EQUAL version)\n          set(version_msg \"Found unsuitable version \\\"${version}\\\", but required is exact version \\\"${${package}_FIND_VERSION}\\\"\")\n        else ()\n          set(version_ok TRUE)\n          set(version_msg \"(found suitable exact version \\\"${version}\\\")\")\n        endif ()\n      endif ()\n    else()     # minimum version\n      if (${package}_FIND_VERSION VERSION_GREATER version)\n        set(version_msg \"Found unsuitable version \\\"${version}\\\", but required is at least \\\"${${package}_FIND_VERSION}\\\"\")\n      else()\n        set(version_ok TRUE)\n        set(version_msg \"(found suitable version \\\"${version}\\\", minimum required is \\\"${${package}_FIND_VERSION}\\\")\")\n      endif()\n    endif()\n  else ()\n    set(version_ok TRUE)\n    set(version_msg \"(found version \\\"${version}\\\")\")\n  endif()\n\n  set (${result} ${version_ok} PARENT_SCOPE)\n  if (FPCV_RESULT_MESSAGE_VARIABLE)\n    set (${FPCV_RESULT_MESSAGE_VARIABLE} \"${version_msg}\" PARENT_SCOPE)\n  endif()\nendfunction()\n\n\nfunction(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG)\n\n  # Set up the arguments for `cmake_parse_arguments`.\n  set(options  CONFIG_MODE  HANDLE_COMPONENTS NAME_MISMATCHED HANDLE_VERSION_RANGE)\n  set(oneValueArgs  FAIL_MESSAGE  REASON_FAILURE_MESSAGE VERSION_VAR  FOUND_VAR)\n  set(multiValueArgs REQUIRED_VARS)\n\n  # Check whether we are in 'simple' or 'extended' mode:\n  set(_KEYWORDS_FOR_EXTENDED_MODE  ${options} ${oneValueArgs} ${multiValueArgs} )\n  list(FIND _KEYWORDS_FOR_EXTENDED_MODE \"${_FIRST_ARG}\" INDEX)\n\n  unset(FPHSA_NAME_MISMATCHED_override)\n  if (DEFINED FPHSA_NAME_MISMATCHED)\n    # If the variable NAME_MISMATCHED variable is set, error if it is passed as\n    # an argument. The former is for old signatures, the latter is for new\n    # signatures.\n    list(FIND ARGN \"NAME_MISMATCHED\" name_mismatched_idx)\n    if (NOT name_mismatched_idx EQUAL \"-1\")\n      message(FATAL_ERROR\n        \"The `NAME_MISMATCHED` argument may only be specified by the argument or \"\n        \"the variable, not both.\")\n    endif ()\n\n    # But use the variable if it is not an argument to avoid forcing minimum\n    # CMake version bumps for calling modules.\n    set(FPHSA_NAME_MISMATCHED_override \"${FPHSA_NAME_MISMATCHED}\")\n  endif ()\n\n  if(${INDEX} EQUAL -1)\n    set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG})\n    set(FPHSA_REQUIRED_VARS ${ARGN})\n    set(FPHSA_VERSION_VAR)\n  else()\n    cmake_parse_arguments(FPHSA \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\"  ${_FIRST_ARG} ${ARGN})\n\n    if(FPHSA_UNPARSED_ARGUMENTS)\n      message(FATAL_ERROR \"Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \\\"${FPHSA_UNPARSED_ARGUMENTS}\\\"\")\n    endif()\n\n    if(NOT FPHSA_FAIL_MESSAGE)\n      set(FPHSA_FAIL_MESSAGE  \"DEFAULT_MSG\")\n    endif()\n\n    # In config-mode, we rely on the variable <PackageName>_CONFIG, which is set by find_package()\n    # when it successfully found the config-file, including version checking:\n    if(FPHSA_CONFIG_MODE)\n      list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG)\n      list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS)\n      set(FPHSA_VERSION_VAR ${_NAME}_VERSION)\n    endif()\n\n    if(NOT FPHSA_REQUIRED_VARS AND NOT FPHSA_HANDLE_COMPONENTS)\n      message(FATAL_ERROR \"No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()\")\n    endif()\n  endif()\n\n  if (DEFINED FPHSA_NAME_MISMATCHED_override)\n    set(FPHSA_NAME_MISMATCHED \"${FPHSA_NAME_MISMATCHED_override}\")\n  endif ()\n\n  if (DEFINED CMAKE_FIND_PACKAGE_NAME\n      AND NOT FPHSA_NAME_MISMATCHED\n      AND NOT _NAME STREQUAL CMAKE_FIND_PACKAGE_NAME)\n    message(AUTHOR_WARNING\n      \"The package name passed to `find_package_handle_standard_args` \"\n      \"(${_NAME}) does not match the name of the calling package \"\n      \"(${CMAKE_FIND_PACKAGE_NAME}). This can lead to problems in calling \"\n      \"code that expects `find_package` result variables (e.g., `_FOUND`) \"\n      \"to follow a certain pattern.\")\n  endif ()\n\n  if (${_NAME}_FIND_VERSION_RANGE AND NOT FPHSA_HANDLE_VERSION_RANGE)\n    message(AUTHOR_WARNING\n      \"`find_package()` specify a version range but the module ${_NAME} does \"\n      \"not support this capability. Only the lower endpoint of the range \"\n      \"will be used.\")\n  endif()\n\n  # to propagate package name to FIND_PACKAGE_CHECK_VERSION\n  set(_CMAKE_FPHSA_PACKAGE_NAME \"${_NAME}\")\n\n  # now that we collected all arguments, process them\n\n  if(\"x${FPHSA_FAIL_MESSAGE}\" STREQUAL \"xDEFAULT_MSG\")\n    set(FPHSA_FAIL_MESSAGE \"Could NOT find ${_NAME}\")\n  endif()\n\n  if (FPHSA_REQUIRED_VARS)\n    list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR)\n  endif()\n\n  string(TOUPPER ${_NAME} _NAME_UPPER)\n  string(TOLOWER ${_NAME} _NAME_LOWER)\n\n  if(FPHSA_FOUND_VAR)\n    set(_FOUND_VAR_UPPER ${_NAME_UPPER}_FOUND)\n    set(_FOUND_VAR_MIXED ${_NAME}_FOUND)\n    if(FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_MIXED  OR  FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_UPPER)\n      set(_FOUND_VAR ${FPHSA_FOUND_VAR})\n    else()\n      message(FATAL_ERROR \"The argument for FOUND_VAR is \\\"${FPHSA_FOUND_VAR}\\\", but only \\\"${_FOUND_VAR_MIXED}\\\" and \\\"${_FOUND_VAR_UPPER}\\\" are valid names.\")\n    endif()\n  else()\n    set(_FOUND_VAR ${_NAME_UPPER}_FOUND)\n  endif()\n\n  # collect all variables which were not found, so they can be printed, so the\n  # user knows better what went wrong (#6375)\n  set(MISSING_VARS \"\")\n  set(DETAILS \"\")\n  # check if all passed variables are valid\n  set(FPHSA_FOUND_${_NAME} TRUE)\n  foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS})\n    if(NOT ${_CURRENT_VAR})\n      set(FPHSA_FOUND_${_NAME} FALSE)\n      string(APPEND MISSING_VARS \" ${_CURRENT_VAR}\")\n    else()\n      string(APPEND DETAILS \"[${${_CURRENT_VAR}}]\")\n    endif()\n  endforeach()\n  if(FPHSA_FOUND_${_NAME})\n    set(${_NAME}_FOUND TRUE)\n    set(${_NAME_UPPER}_FOUND TRUE)\n  else()\n    set(${_NAME}_FOUND FALSE)\n    set(${_NAME_UPPER}_FOUND FALSE)\n  endif()\n\n  # component handling\n  unset(FOUND_COMPONENTS_MSG)\n  unset(MISSING_COMPONENTS_MSG)\n\n  if(FPHSA_HANDLE_COMPONENTS)\n    foreach(comp ${${_NAME}_FIND_COMPONENTS})\n      if(${_NAME}_${comp}_FOUND)\n\n        if(NOT DEFINED FOUND_COMPONENTS_MSG)\n          set(FOUND_COMPONENTS_MSG \"found components:\")\n        endif()\n        string(APPEND FOUND_COMPONENTS_MSG \" ${comp}\")\n\n      else()\n\n        if(NOT DEFINED MISSING_COMPONENTS_MSG)\n          set(MISSING_COMPONENTS_MSG \"missing components:\")\n        endif()\n        string(APPEND MISSING_COMPONENTS_MSG \" ${comp}\")\n\n        if(${_NAME}_FIND_REQUIRED_${comp})\n          set(${_NAME}_FOUND FALSE)\n          string(APPEND MISSING_VARS \" ${comp}\")\n        endif()\n\n      endif()\n    endforeach()\n    set(COMPONENT_MSG \"${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}\")\n    string(APPEND DETAILS \"[c${COMPONENT_MSG}]\")\n  endif()\n\n  # version handling:\n  set(VERSION_MSG \"\")\n  set(VERSION_OK TRUE)\n\n  # check that the version variable is not empty to avoid emitting a misleading\n  # message (i.e. `Found unsuitable version \"\"`)\n  if (DEFINED ${_NAME}_FIND_VERSION)\n    if(DEFINED ${FPHSA_VERSION_VAR})\n      if(NOT \"${${FPHSA_VERSION_VAR}}\" STREQUAL \"\")\n        set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}})\n        if (FPHSA_HANDLE_VERSION_RANGE)\n          set (FPCV_HANDLE_VERSION_RANGE HANDLE_VERSION_RANGE)\n        else()\n          set(FPCV_HANDLE_VERSION_RANGE NO_AUTHOR_WARNING_VERSION_RANGE)\n        endif()\n        find_package_check_version (\"${_FOUND_VERSION}\" VERSION_OK RESULT_MESSAGE_VARIABLE VERSION_MSG\n          ${FPCV_HANDLE_VERSION_RANGE})\n      else()\n        set(VERSION_OK FALSE)\n      endif()\n    endif()\n    if(\"${${FPHSA_VERSION_VAR}}\" STREQUAL \"\")\n      # if the package was not found, but a version was given, add that to the output:\n      if(${_NAME}_FIND_VERSION_EXACT)\n        set(VERSION_MSG \"(Required is exact version \\\"${${_NAME}_FIND_VERSION}\\\")\")\n      elseif (FPHSA_HANDLE_VERSION_RANGE AND ${_NAME}_FIND_VERSION_RANGE)\n        set(VERSION_MSG \"(Required is version range \\\"${${_NAME}_FIND_VERSION_RANGE}\\\")\")\n      else()\n        set(VERSION_MSG \"(Required is at least version \\\"${${_NAME}_FIND_VERSION}\\\")\")\n      endif()\n    endif()\n  else ()\n    # Check with DEFINED as the found version may be 0.\n    if(DEFINED ${FPHSA_VERSION_VAR})\n      set(VERSION_MSG \"(found version \\\"${${FPHSA_VERSION_VAR}}\\\")\")\n    endif()\n  endif ()\n\n  if(VERSION_OK)\n    string(APPEND DETAILS \"[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]\")\n  else()\n    set(${_NAME}_FOUND FALSE)\n  endif()\n\n\n  # print the result:\n  if (${_NAME}_FOUND)\n    FIND_PACKAGE_MESSAGE(${_NAME} \"Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}\" \"${DETAILS}\")\n  else ()\n\n    if(FPHSA_CONFIG_MODE)\n      _FPHSA_HANDLE_FAILURE_CONFIG_MODE()\n    else()\n      if(NOT VERSION_OK)\n        set(RESULT_MSG)\n        if (_FIRST_REQUIRED_VAR)\n          string (APPEND RESULT_MSG \"found ${${_FIRST_REQUIRED_VAR}}\")\n        endif()\n        if (COMPONENT_MSG)\n          if (RESULT_MSG)\n            string (APPEND RESULT_MSG \", \")\n          endif()\n          string (APPEND RESULT_MSG \"${FOUND_COMPONENTS_MSG}\")\n        endif()\n        _FPHSA_FAILURE_MESSAGE(\"${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (${RESULT_MSG})\")\n      else()\n        _FPHSA_FAILURE_MESSAGE(\"${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}\")\n      endif()\n    endif()\n\n  endif ()\n\n  set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE)\n  set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE)\nendfunction()\n\n\ncmake_policy(POP)"
  },
  {
    "path": "cmake/modules/FindPackageMessage.cmake",
    "content": "# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying\n# file Copyright.txt or https://cmake.org/licensing for details.\n\n#[=======================================================================[.rst:\nFindPackageMessage\n------------------\n\n.. code-block:: cmake\n\n  find_package_message(<name> \"message for user\" \"find result details\")\n\nThis function is intended to be used in FindXXX.cmake modules files.\nIt will print a message once for each unique find result.  This is\nuseful for telling the user where a package was found.  The first\nargument specifies the name (XXX) of the package.  The second argument\nspecifies the message to display.  The third argument lists details\nabout the find result so that if they change the message will be\ndisplayed again.  The macro also obeys the QUIET argument to the\nfind_package command.\n\nExample:\n\n.. code-block:: cmake\n\n  if(X11_FOUND)\n    find_package_message(X11 \"Found X11: ${X11_X11_LIB}\"\n      \"[${X11_X11_LIB}][${X11_INCLUDE_DIR}]\")\n  else()\n   ...\n  endif()\n#]=======================================================================]\n\nfunction(find_package_message pkg msg details)\n  # Avoid printing a message repeatedly for the same find result.\n  if(NOT ${pkg}_FIND_QUIETLY)\n    string(REPLACE \"\\n\" \"\" details \"${details}\")\n    set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg})\n    if(NOT \"${details}\" STREQUAL \"${${DETAILS_VAR}}\")\n      # The message has not yet been printed.\n      message(STATUS \"${msg}\")\n\n      # Save the find details in the cache to avoid printing the same\n      # message again.\n      set(\"${DETAILS_VAR}\" \"${details}\"\n        CACHE INTERNAL \"Details about finding ${pkg}\")\n    endif()\n  endif()\nendfunction()"
  },
  {
    "path": "cmake/modules/FindYara.cmake",
    "content": "find_library(YARA_LIBRARIES NAMES yara)\nfind_file(yara.h YARA_INCLUDE_DIRS)\n\nmark_as_advanced(YARA_LIBRARIES YARA_INCLUDE_DIRS)"
  },
  {
    "path": "cmake/modules/FindZSTD.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n#\n# - Try to find Facebook zstd library\n# This will define\n# ZSTD_FOUND\n# ZSTD_INCLUDE_DIR\n# ZSTD_LIBRARY\n#\n\nfind_path(ZSTD_INCLUDE_DIR NAMES zstd.h)\n\nfind_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd_staticd)\nfind_library(ZSTD_LIBRARY_RELEASE NAMES zstd zstd_static)\n\ninclude(SelectLibraryConfigurations)\nSELECT_LIBRARY_CONFIGURATIONS(ZSTD)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n        ZSTD DEFAULT_MSG\n        ZSTD_LIBRARY ZSTD_INCLUDE_DIR\n)\n\nif (ZSTD_FOUND)\n    message(STATUS \"Found Zstd: ${ZSTD_LIBRARY}\")\nendif()\n\nmark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY)\n\nadd_library(ZSTD::zstd INTERFACE IMPORTED)\nset_property(TARGET ZSTD::zstd PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIR})\nset_property(TARGET ZSTD::zstd PROPERTY INTERFACE_LINK_LIBRARIES ${ZSTD_LIBRARY})"
  },
  {
    "path": "cmake/modules/Findlibssh2.cmake",
    "content": "find_path(LIBSSH2_INCLUDE_DIR libssh2.h)\n\nfind_library(LIBSSH2_LIBRARY NAMES ssh2 libssh2)\n\nif(LIBSSH2_INCLUDE_DIR)\n    file(STRINGS \"${LIBSSH2_INCLUDE_DIR}/libssh2.h\" libssh2_version_str REGEX \"^#define[\\t ]+LIBSSH2_VERSION[\\t ]+\\\"(.*)\\\"\")\n    string(REGEX REPLACE \"^.*\\\"([^\\\"]+)\\\"\" \"\\\\1\"  LIBSSH2_VERSION \"${libssh2_version_str}\")\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(LibSSH2\n        REQUIRED_VARS LIBSSH2_LIBRARY LIBSSH2_INCLUDE_DIR\n        VERSION_VAR LIBSSH2_VERSION)\n\nmark_as_advanced(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY)\n"
  },
  {
    "path": "cmake/modules/FindmbedTLS.cmake",
    "content": "# - Try to find mbedTLS\n# Once done this will define\n#\n# Read-Only variables\n#  MBEDTLS_FOUND - system has mbedTLS\n#  MBEDTLS_INCLUDE_DIR - the mbedTLS include directory\n#  MBEDTLS_LIBRARY_DIR - the mbedTLS library directory\n#  MBEDTLS_LIBRARIES - Link these to use mbedTLS\n#  MBEDTLS_LIBRARY - path to mbedTLS library\n#  MBEDX509_LIBRARY - path to mbedTLS X.509 library\n#  MBEDCRYPTO_LIBRARY - path to mbedTLS Crypto library\n\n# Copyright (c) 2004-2007 Sara Golemon <sarag@libssh2.org>\n# Copyright (c) 2005,2006 Mikhail Gusarov <dottedmag@dottedmag.net>\n# Copyright (c) 2006-2007 The Written Word, Inc.\n# Copyright (c) 2007 Eli Fant <elifantu@mail.ru>\n# Copyright (c) 2009-2019 Daniel Stenberg\n# Copyright (C) 2008, 2009 Simon Josefsson\n# All rights reserved.\n\n\nFIND_PATH(MBEDTLS_INCLUDE_DIR mbedtls/version.h)\n\nSET(MBEDTLS_FIND_QUIETLY TRUE)\n\nFIND_LIBRARY(MBEDTLS_LIBRARY NAMES mbedtls libmbedtls libmbedx509)\nFIND_LIBRARY(MBEDX509_LIBRARY NAMES mbedx509 libmbedx509)\nFIND_LIBRARY(MBEDCRYPTO_LIBRARY NAMES mbedcrypto libmbedcrypto)\nFIND_LIBRARY(TFPSACRYPTO_LIBRARY NAMES libtfpsacrypto tfpsacrypto)\n\nIF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARY AND MBEDX509_LIBRARY AND (MBEDCRYPTO_LIBRARY OR TFPSACRYPTO_LIBRARY))\n    SET(MBEDTLS_FOUND TRUE)\nENDIF()\n\nIF(MBEDTLS_FOUND)\n    # split mbedTLS into -L and -l linker options, so we can set them for pkg-config\n    GET_FILENAME_COMPONENT(MBEDTLS_LIBRARY_DIR ${MBEDTLS_LIBRARY} PATH)\n    GET_FILENAME_COMPONENT(MBEDTLS_LIBRARY_FILE ${MBEDTLS_LIBRARY} NAME_WE)\n    GET_FILENAME_COMPONENT(MBEDX509_LIBRARY_FILE ${MBEDX509_LIBRARY} NAME_WE)\n    GET_FILENAME_COMPONENT(MBEDCRYPTO_LIBRARY_FILE ${MBEDCRYPTO_LIBRARY} NAME_WE)\n    GET_FILENAME_COMPONENT(TFPSACRYPTO_LIBRARY_FILE ${TFPSACRYPTO_LIBRARY} NAME_WE)\n    STRING(REGEX REPLACE \"^lib\" \"\" MBEDTLS_LIBRARY_FILE ${MBEDTLS_LIBRARY_FILE})\n    STRING(REGEX REPLACE \"^lib\" \"\" MBEDX509_LIBRARY_FILE ${MBEDX509_LIBRARY_FILE})\n    STRING(REGEX REPLACE \"^lib\" \"\" MBEDCRYPTO_LIBRARY_FILE ${MBEDCRYPTO_LIBRARY_FILE})\n    STRING(REGEX REPLACE \"^lib\" \"\" TFPSACRYPTO_LIBRARY_FILE ${TFPSACRYPTO_LIBRARY_FILE})\n\n    if (TFPSACRYPTO_LIBRARY)\n        SET(MBEDTLS_CRYPTO_LIBRARY_FILE ${TFPSACRYPTO_LIBRARY_FILE})\n    elseif (MBEDCRYPTO_LIBRARY)\n        SET(MBEDTLS_CRYPTO_LIBRARY_FILE ${MBEDCRYPTO_LIBRARY_FILE})\n    else ()\n        MESSAGE(FATAL_ERROR \"Could not find mbedTLS Crypto library\")\n    endif()\n\n    if (MSVC)\n        SET(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY_FILE}.lib ${MBEDX509_LIBRARY_FILE}.lib ${MBEDTLS_CRYPTO_LIBRARY_FILE}.lib)\n    else()\n        SET(MBEDTLS_LIBRARIES \"-L${MBEDTLS_LIBRARY_DIR} -l${MBEDTLS_LIBRARY_FILE} -l${MBEDX509_LIBRARY_FILE} -l${MBEDTLS_CRYPTO_LIBRARY_FILE}\")\n    endif()\n\n    IF(NOT MBEDTLS_FIND_QUIETLY)\n        MESSAGE(STATUS \"Found mbedTLS:\")\n        FILE(READ ${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h MBEDTLSCONTENT)\n        STRING(REGEX MATCH \"MBEDTLS_VERSION_STRING +\\\"[0-9|.]+\\\"\" MBEDTLSMATCH ${MBEDTLSCONTENT})\n        IF (MBEDTLSMATCH)\n            STRING(REGEX REPLACE \"MBEDTLS_VERSION_STRING +\\\"([0-9|.]+)\\\"\" \"\\\\1\" MBEDTLS_VERSION ${MBEDTLSMATCH})\n            MESSAGE(STATUS \"  version ${MBEDTLS_VERSION}\")\n        ENDIF(MBEDTLSMATCH)\n        MESSAGE(STATUS \"  TLS: ${MBEDTLS_LIBRARY}\")\n        MESSAGE(STATUS \"  X509: ${MBEDX509_LIBRARY}\")\n        MESSAGE(STATUS \"  Crypto: ${MBEDCRYPTO_LIBRARY}\")\n    ENDIF(NOT MBEDTLS_FIND_QUIETLY)\nELSE(MBEDTLS_FOUND)\n    IF(mbedTLS_FIND_REQUIRED)\n        MESSAGE(FATAL_ERROR \"Could not find mbedTLS\")\n    ENDIF(mbedTLS_FIND_REQUIRED)\nENDIF(MBEDTLS_FOUND)\n\nMARK_AS_ADVANCED(\n        MBEDTLS_INCLUDE_DIR\n        MBEDTLS_LIBRARY_DIR\n        MBEDTLS_LIBRARIES\n        MBEDTLS_LIBRARY\n        MBEDX509_LIBRARY\n        MBEDCRYPTO_LIBRARY\n)\n"
  },
  {
    "path": "cmake/modules/ImHexPlugin.cmake",
    "content": "macro(add_imhex_plugin)\n    setSDKPaths()\n    # Parse arguments\n    set(options LIBRARY_PLUGIN)\n    set(oneValueArgs NAME IMHEX_VERSION)\n    set(multiValueArgs SOURCES INCLUDES LIBRARIES FEATURES)\n    cmake_parse_arguments(IMHEX_PLUGIN \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})\n\n    if (IMHEX_PLUGIN_IMHEX_VERSION)\n        message(STATUS \"Compiling plugin ${IMHEX_PLUGIN_NAME} for ImHex Version ${IMHEX_PLUGIN_IMHEX_VERSION}\")\n        set(IMHEX_VERSION_STRING \"${IMHEX_PLUGIN_IMHEX_VERSION}\")\n    endif()\n\n    if (IMHEX_STATIC_LINK_PLUGINS)\n        set(IMHEX_PLUGIN_LIBRARY_TYPE STATIC)\n\n        target_link_libraries(libimhex PUBLIC ${IMHEX_PLUGIN_NAME})\n\n        configure_file(${CMAKE_SOURCE_DIR}/dist/web/plugin-bundle.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/plugin-bundle.cpp @ONLY)\n        target_sources(main PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/plugin-bundle.cpp)\n        set(IMHEX_PLUGIN_SUFFIX \".hexplug\")\n    else()\n        if (IMHEX_PLUGIN_LIBRARY_PLUGIN)\n            set(IMHEX_PLUGIN_LIBRARY_TYPE SHARED)\n            set(IMHEX_PLUGIN_SUFFIX \".hexpluglib\")\n        else()\n            set(IMHEX_PLUGIN_LIBRARY_TYPE MODULE)\n            set(IMHEX_PLUGIN_SUFFIX \".hexplug\")\n        endif()\n    endif()\n\n    if (IMHEX_PLUGIN_LIBRARY_PLUGIN)\n        install(DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/include/\" DESTINATION \"${SDK_PATH}/lib/plugins/${IMHEX_PLUGIN_NAME}\")\n    endif()\n\n    # Define new project for plugin\n    project(${IMHEX_PLUGIN_NAME})\n\n    if (IMHEX_PLUGIN_IMPORTED)\n        add_library(${IMHEX_PLUGIN_NAME} SHARED IMPORTED GLOBAL)\n\n        if (WIN32)\n            set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES\n                    IMPORTED_LOCATION \"${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/${IMHEX_PLUGIN_NAME}${IMHEX_PLUGIN_SUFFIX}\"\n                    IMPORTED_IMPLIB \"${CMAKE_CURRENT_SOURCE_DIR}/../lib${IMHEX_PLUGIN_NAME}.dll.a\"\n                    INTERFACE_INCLUDE_DIRECTORIES \"${CMAKE_CURRENT_SOURCE_DIR}/include/include\")\n        elseif (APPLE)\n            set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES\n                    IMPORTED_LOCATION \"${CMAKE_CURRENT_SOURCE_DIR}/../../../../MacOS/plugins/${IMHEX_PLUGIN_NAME}${IMHEX_PLUGIN_SUFFIX}\"\n                    INTERFACE_INCLUDE_DIRECTORIES \"${CMAKE_CURRENT_SOURCE_DIR}/include/include\")\n        else()\n            set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES\n                    IMPORTED_LOCATION \"${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/${IMHEX_PLUGIN_NAME}${IMHEX_PLUGIN_SUFFIX}\"\n                    INTERFACE_INCLUDE_DIRECTORIES \"${CMAKE_CURRENT_SOURCE_DIR}/include/include\")\n        endif()\n    else()\n        # Create a new shared library for the plugin source code\n        add_library(${IMHEX_PLUGIN_NAME} ${IMHEX_PLUGIN_LIBRARY_TYPE} ${IMHEX_PLUGIN_SOURCES})\n\n        # Add include directories and link libraries\n        target_include_directories(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_INCLUDES})\n        target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_LIBRARIES})\n        target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${FMT_LIBRARIES} imgui_all_includes libwolv)\n        addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl)\n        addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl-gen)\n\n        precompileHeaders(${IMHEX_PLUGIN_NAME} \"${CMAKE_CURRENT_SOURCE_DIR}/include\")\n\n        # Add IMHEX_PROJECT_NAME and IMHEX_VERSION define\n        target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PROJECT_NAME=\"${IMHEX_PLUGIN_NAME}\")\n        target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_VERSION=\"${IMHEX_VERSION_STRING}\")\n        target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PLUGIN_NAME=${IMHEX_PLUGIN_NAME})\n\n        # Enable required compiler flags\n        enableUnityBuild(${IMHEX_PLUGIN_NAME})\n        setupCompilerFlags(${IMHEX_PLUGIN_NAME})\n        addCppCheck(${IMHEX_PLUGIN_NAME})\n\n        # Configure build properties\n        set_target_properties(${IMHEX_PLUGIN_NAME}\n                PROPERTIES\n                RUNTIME_OUTPUT_DIRECTORY \"${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins\"\n                CXX_STANDARD 23\n                PREFIX \"\"\n                SUFFIX ${IMHEX_PLUGIN_SUFFIX}\n        )\n\n        # Set rpath of plugin libraries to the plugins folder\n        if (WIN32)\n            if (IMHEX_PLUGIN_LIBRARY_PLUGIN)\n                set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)\n            endif()\n        elseif (APPLE)\n            set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES BUILD_RPATH \"@executable_path/../Frameworks;@executable_path/plugins\")\n        endif()\n\n        # Setup a romfs for the plugin\n        list(APPEND LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/romfs)\n        set(LIBROMFS_PROJECT_NAME ${IMHEX_PLUGIN_NAME})\n        add_subdirectory(${IMHEX_BASE_FOLDER}/lib/external/libromfs ${CMAKE_CURRENT_BINARY_DIR}/libromfs)\n        target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE ${LIBROMFS_LIBRARY})\n\n        set(FEATURE_DEFINE_CONTENT)\n\n        if (IMHEX_PLUGIN_FEATURES)\n            list(LENGTH IMHEX_PLUGIN_FEATURES IMHEX_FEATURE_COUNT)\n            math(EXPR IMHEX_FEATURE_COUNT \"${IMHEX_FEATURE_COUNT} - 1\" OUTPUT_FORMAT DECIMAL)\n            foreach(index RANGE 0 ${IMHEX_FEATURE_COUNT} 2)\n                list(SUBLIST IMHEX_PLUGIN_FEATURES ${index} 2 IMHEX_PLUGIN_FEATURE)\n                list(GET IMHEX_PLUGIN_FEATURE 0 feature_define)\n                list(GET IMHEX_PLUGIN_FEATURE 1 feature_description)\n\n                string(TOUPPER ${feature_define} feature_define)\n                add_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature_define}=0)\n                set(FEATURE_DEFINE_CONTENT \"${FEATURE_DEFINE_CONTENT}{ \\\"${feature_description}\\\", IMHEX_FEATURE_ENABLED(${feature_define}) },\")\n            endforeach()\n        endif()\n\n        target_compile_options(${IMHEX_PLUGIN_NAME} PRIVATE -DIMHEX_PLUGIN_FEATURES_CONTENT=${FEATURE_DEFINE_CONTENT})\n\n        # Add the new plugin to the main dependency list so it gets built by default\n        if (TARGET imhex_all)\n            add_dependencies(imhex_all ${IMHEX_PLUGIN_NAME})\n        endif()\n\n        if (IMHEX_EXTERNAL_PLUGIN_BUILD)\n            install(TARGETS ${IMHEX_PLUGIN_NAME} DESTINATION \".\")\n        endif()\n\n        # Fix rpath\n        if (APPLE)\n            set_target_properties(\n                    ${IMHEX_PLUGIN_NAME}\n                    PROPERTIES\n                    INSTALL_RPATH \"@executable_path/../Frameworks;@executable_path/plugins\"\n            )\n        elseif (UNIX)\n            set(PLUGIN_RPATH \"\")\n            list(APPEND PLUGIN_RPATH \"$ORIGIN\")\n\n            if (IMHEX_PLUGIN_ADD_INSTALL_PREFIX_TO_RPATH)\n                list(APPEND PLUGIN_RPATH \"${CMAKE_INSTALL_PREFIX}/lib\")\n            endif()\n\n            set_target_properties(\n                    ${IMHEX_PLUGIN_NAME}\n                    PROPERTIES\n                    INSTALL_RPATH_USE_ORIGIN ON\n                    INSTALL_RPATH \"${PLUGIN_RPATH}\"\n            )\n        endif()\n\n        if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt AND IMHEX_ENABLE_UNIT_TESTS AND IMHEX_ENABLE_PLUGIN_TESTS)\n            add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests)\n            target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_NAME}_tests)\n            target_compile_definitions(${IMHEX_PLUGIN_NAME}_tests PRIVATE IMHEX_PROJECT_NAME=\"${IMHEX_PLUGIN_NAME}-tests\")\n        endif()\n    endif()\nendmacro()\n\nmacro(add_romfs_resource input output)\n    if (NOT EXISTS ${input})\n        message(WARNING \"Resource file ${input} does not exist\")\n    endif()\n\n    configure_file(${input} ${CMAKE_CURRENT_BINARY_DIR}/romfs/${output} COPYONLY)\n\n    list(APPEND LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/romfs)\nendmacro()\n\nmacro (enable_plugin_feature feature)\n    string(TOUPPER ${feature} feature)\n    if (NOT (feature IN_LIST IMHEX_PLUGIN_FEATURES))\n        message(FATAL_ERROR \"Feature ${feature} is not enabled for plugin ${IMHEX_PLUGIN_NAME}\")\n    endif()\n\n    remove_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature}=0)\n    add_definitions(-DIMHEX_PLUGIN_${IMHEX_PLUGIN_NAME}_FEATURE_${feature}=1)\nendmacro()\n"
  },
  {
    "path": "cmake/modules/PostprocessBundle.cmake",
    "content": "# Adapted from the Dolphin project: https://dolphin-emu.org/\n# This module can be used in two different ways.\n#\n# When invoked as `cmake -P PostprocessBundle.cmake`, it fixes up an\n# application folder to be standalone. It bundles all required libraries from\n# the system and fixes up library IDs. Any additional shared libraries, like\n# plugins, that are found under Contents/MacOS/ will be made standalone as well.\n#\n# When called with `include(PostprocessBundle)`, it defines a helper\n# function `postprocess_bundle` that sets up the command form of the\n# module as a post-build step.\n\nif(CMAKE_GENERATOR)\n\t# Being called as include(PostprocessBundle), so define a helper function.\n\tset(_POSTPROCESS_BUNDLE_MODULE_LOCATION \"${CMAKE_CURRENT_LIST_FILE}\")\n\tfunction(postprocess_bundle out_target in_target)\n\n\t\tinstall(CODE \"set(BUNDLE_PATH ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME})\")\n\t\tinstall(CODE \"set(CODE_SIGN_CERTIFICATE_ID ${CODE_SIGN_CERTIFICATE_ID})\")\n\t\tinstall(CODE \"set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS})\")\n\t\tinstall(SCRIPT ${_POSTPROCESS_BUNDLE_MODULE_LOCATION})\n\tendfunction()\n\treturn()\nendif()\n\n# IMHEX PATCH BEGIN\n# The function defined above doesn't keep in mind that if we are cross-compiling to MacOS, APPLE must be 1,\n# so we force it here (where else would this script be run anyway ? This seems to be MacOS-specific code)\nSET(APPLE 1)\n# IMHEX PATCHE END\n\nget_filename_component(BUNDLE_PATH \"${BUNDLE_PATH}\" ABSOLUTE)\nmessage(STATUS \"Fixing up application bundle: ${BUNDLE_PATH}\")\n\n\n# Make sure to fix up any included ImHex plugin.\nfile(GLOB_RECURSE plugins \"${BUNDLE_PATH}/Contents/MacOS/plugins/*.hexplug\")\n\n\n# BundleUtilities doesn't support DYLD_FALLBACK_LIBRARY_PATH behavior, which\n# makes it sometimes break on libraries that do weird things with @rpath. Specify\n# equivalent search directories until https://gitlab.kitware.com/cmake/cmake/issues/16625\n# is fixed and in our minimum CMake version.\nset(extra_dirs \"/usr/local/lib\" \"/lib\" \"/usr/lib\" ${EXTRA_BUNDLE_LIBRARY_PATHS} \"${BUNDLE_PATH}/Contents/MacOS/plugins\" \"${BUNDLE_PATH}/Contents/Frameworks\")\nmessage(STATUS \"Fixing up application bundle: ${extra_dirs}\")\n\n# BundleUtilities is overly verbose, so disable most of its messages\n#function(message)\n#\tif(NOT ARGV MATCHES \"^STATUS;\")\n#\t\t_message(${ARGV})\n#\tendif()\n#endfunction()\n\ninclude(BundleUtilities)\nset(BU_CHMOD_BUNDLE_ITEMS ON)\n\nfixup_bundle(\"${BUNDLE_PATH}\" \"${plugins}\" \"${extra_dirs}\")\n\nif (CODE_SIGN_CERTIFICATE_ID)\n\t# Hack around Apple Silicon signing bugs by copying the real app, signing it and moving it back.\n\t# IMPORTANT: DON'T USE ${CMAKE_COMMAND} -E copy_directory HERE (this follow symbolic links).\n\texecute_process(COMMAND cp -R \"${BUNDLE_PATH}\" \"${BUNDLE_PATH}.temp\")\n\texecute_process(COMMAND codesign --deep --force --sign \"${CODE_SIGN_CERTIFICATE_ID}\" \"${BUNDLE_PATH}.temp\")\n\texecute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory \"${BUNDLE_PATH}\")\n\texecute_process(COMMAND ${CMAKE_COMMAND} -E rename \"${BUNDLE_PATH}.temp\" \"${BUNDLE_PATH}\")\nendif()\n\n# Add a necessary rpath to the imhex binary\nget_bundle_main_executable(\"${BUNDLE_PATH}\" IMHEX_EXECUTABLE)\n\nfile(GLOB_RECURSE plugin_libs \"${BUNDLE_PATH}/Contents/MacOS/*.hexpluglib\")\nforeach(plugin_lib ${plugin_libs})\n\tget_filename_component(plugin_lib_name ${plugin_lib} NAME)\n\tset(plugin_lib_dest \"${BUNDLE_PATH}/Contents/MacOS/plugins/${plugin_lib_name}\")\n\n\tconfigure_file(${plugin_lib} \"${plugin_lib_dest}\" COPYONLY)\n\tmessage(STATUS \"Copying plugin library: ${plugin_lib} to ${plugin_lib_dest}\")\nendforeach ()"
  },
  {
    "path": "cmake/sdk/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.20)\nproject(ImHexSDK)\n\nset(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules\")\nset(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL \"\" FORCE)\ninclude(\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_helpers.cmake\")\n\nset(IMHEX_BASE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)\ninclude(ImHexPlugin)\n\nfunction(add_subdirectory_if_exists folder)\n    if (EXISTS \"${CMAKE_CURRENT_SOURCE_DIR}/${folder}/CMakeLists.txt\")\n        add_subdirectory(\"${folder}\" EXCLUDE_FROM_ALL)\n    endif()\nendfunction()\n\nset(IMHEX_EXTERNAL_PLUGIN_BUILD ON PARENT_SCOPE)\nset(IMHEX_EXTERNAL_PLUGIN_BUILD ON)\nadd_custom_target(imhex_all)\n\nadd_subdirectory(lib/third_party/imgui EXCLUDE_FROM_ALL)\n\nset(FMT_INSTALL OFF CACHE BOOL \"\" FORCE)\nadd_subdirectory_if_exists(lib/third_party/fmt)\nset(FMT_LIBRARIES fmt::fmt-header-only PARENT_SCOPE)\nset(FMT_LIBRARIES fmt::fmt-header-only)\n\nadd_subdirectory_if_exists(lib/third_party/nlohmann_json)\nset(NLOHMANN_JSON_LIBRARIES nlohmann_json PARENT_SCOPE)\nset(NLOHMANN_JSON_LIBRARIES nlohmann_json)\n\nadd_subdirectory_if_exists(lib/third_party/boost)\nset(BOOST_LIBRARIES boost::regex PARENT_SCOPE)\nset(BOOST_LIBRARIES boost::regex)\n\nadd_subdirectory(lib/external/libwolv EXCLUDE_FROM_ALL)\n\nset(LIBPL_ENABLE_CLI OFF CACHE BOOL \"\" FORCE)\nadd_subdirectory(lib/external/pattern_language EXCLUDE_FROM_ALL)\n\nset(IMHEX_PLUGIN_IMPORTED ON)\n\nadd_subdirectory(lib/libimhex)\nadd_subdirectory(lib/trace)\nadd_subdirectory(lib/fonts)\nadd_subdirectory(lib/ui)\n\nset(IMHEX_PLUGIN_IMPORTED OFF)\n\nif (WIN32)\n    set_target_properties(libimhex PROPERTIES\n            IMPORTED_LOCATION \"${CMAKE_CURRENT_SOURCE_DIR}/../libimhex.dll\"\n            IMPORTED_IMPLIB \"${CMAKE_CURRENT_SOURCE_DIR}/lib/liblibimhex.dll.a\"\n            INTERFACE_INCLUDE_DIRECTORIES \"${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include\")\n    set_target_properties(tracing PROPERTIES\n            IMPORTED_IMPLIB \"${CMAKE_CURRENT_SOURCE_DIR}/lib/libtracing.a\"\n            INTERFACE_INCLUDE_DIRECTORIES \"${CMAKE_CURRENT_SOURCE_DIR}/lib/trace/include\")\nelseif (APPLE)\n    file(GLOB LIBIMHEX_DYLIB \"${CMAKE_CURRENT_SOURCE_DIR}/../../Frameworks/libimhex.*.dylib\")\n    set_target_properties(libimhex PROPERTIES\n            IMPORTED_LOCATION \"${LIBIMHEX_DYLIB}\"\n            INTERFACE_INCLUDE_DIRECTORIES \"${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include\")\n    set_target_properties(tracing PROPERTIES\n            IMPORTED_LOCATION \"${CMAKE_CURRENT_SOURCE_DIR}/../libtracing.a\"\n            INTERFACE_INCLUDE_DIRECTORIES \"${CMAKE_CURRENT_SOURCE_DIR}/lib/trace/include\")\nelse()\n    set_target_properties(libimhex PROPERTIES\n            IMPORTED_LOCATION \"${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/libimhex.so\"\n            INTERFACE_INCLUDE_DIRECTORIES \"${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include\")\n    set_target_properties(tracing PROPERTIES\n            IMPORTED_LOCATION \"${CMAKE_CURRENT_SOURCE_DIR}/lib/libtracing.a\"\n            INTERFACE_INCLUDE_DIRECTORIES \"${CMAKE_CURRENT_SOURCE_DIR}/lib/trace/include\")\nendif()\n"
  },
  {
    "path": "cmake/sdk/template/CMakeLists.txt",
    "content": "# ImHex Plugin Template\n# =====================\n\n# This is the official CMake template for making your own ImHex plugins\n# To use this template, copy the file into its own directory and modify it to your needs\n# For the most part, this is a regular CMake project with some extra functions provided by the ImHex SDK\n#\n# [NOTE FOR NON-C++ PLUGINS]\n# The template is laid out for a C++ plugin, however you can write your plugin in any language you want\n# and just make the plugin statically link against your code. The only thing that's required is a .cpp file with\n# the IMHEX_PLUGIN_SETUP() macro used in it. This macro is used to setup the plugin and register it with ImHex\n#\n# [CMAKE FUNCTIONS]\n# add_imhex_plugin(): Registers a new plugin\n#   NAME:           The name of the plugin\n#   IMHEX_VERSION:  The ImHex version this plugin is compatible with. If unset, the plugin will be loaded on all versions (this may not work though)\n#   SOURCES:        Source files of the plugin\n#   INCLUDES:       Include directories of the plugin\n#   LIBRARIES:      Libraries to link against\n#   FEATURES:       Optional features that can be enabled or disabled\n#   LIBRARY_PLUGIN: If set, turns this plugin into a library plugin. Library plugins can be linked against by other plugins\n#\n# add_romfs_resource(filePath romfsPath): Adds a file to the romfs of the plugin\n# The RomFS is a virtual filesystem whose files can be accessed by the plugin using the functions in the `romfs::` namespace\n# This function is used to add a single file to the romfs. You can however also simply create a `romfs` directory in your plugin directory and place your files and folders in there\n#   filePath:   The path to the file on the disk\n#   romfsPath:  The path to the file in the romfs\n#\n# enable_plugin_feature(feature): Enables a plugin feature\n# Features are optional parts of the plugin that may or may not be available depending on build settings\n# When a feature is enabled, `IMHEX_FEATURE_ENABLED(feature)` will be defined to true. Otherwise, it will be defined to false\n# Use the `IMHEX_PLUGIN_FEATURES` macro in the main plugin file to define names to each feature and have them be listed in the plugin list\n#   feature:    The name of the feature to enable\n\ncmake_minimum_required(VERSION 3.20)\nproject(ImHexPlugin)\n\n# Include the ImHex SDK\n# For this to work, you need to set the IMHEX_SDK_PATH environment variable to the path of the ImHex SDK\n#\n# On Windows, the SDK is next to the ImHex executable\n# On Linux, the SDK is usually in /usr/share/imhex/sdk but this may vary depending on your distribution\n# On MacOS, the SDK is located inside of the ImHex.app bundle under ImHex.app/Contents/Resources/sdk\nif (NOT EXISTS $ENV{IMHEX_SDK_PATH})\n    message(FATAL_ERROR \"The IMHEX_SDK_PATH environment variable is not set\")\nendif()\nadd_subdirectory($ENV{IMHEX_SDK_PATH} ImHexSDK)\n\n# Register the plugin\n# This will configure everything you need to make your plugin work\n# Modify the arguments to your needs. Right now it defines a plugin called `example_plugin`\n# with a single source file called `example_plugin.cpp` in the `source` directory\n# By default you have access to the libimhex library to interact with ImHex\n# as well as libwolv, libromfs, libfmt and ImGui, but you can link against any libraries you want\nadd_imhex_plugin(\n    NAME\n        example_plugin\n    SOURCES\n        source/example_plugin.cpp\n)\n"
  },
  {
    "path": "cmake/sdk/template/source/example_plugin.cpp",
    "content": "#include <hex/plugin.hpp>\n\n// Browse through the headers in lib/libimhex/include/hex/api/ to see what you can do with the API.\n// Most important ones are the things under imhex_api and content_registry\n\n// This is the main entry point of your plugin. The code in the body of this construct will be executed\n// when ImHex starts up and loads the plugin.\n// The strings in the header are used to display information about the plugin in the UI.\nIMHEX_PLUGIN_SETUP(\"Example Plugin\", \"Author\", \"Description\") {\n    // Put your init code here\n}"
  },
  {
    "path": "dist/AppImage/AppImageBuilder.yml",
    "content": "# appimage-builder recipe see https://appimage-builder.readthedocs.io for details\nversion: 1\nAppDir:\n  path: .AppDir\n  app_info:\n    id: imhex\n    name: ImHex\n    icon: imhex\n    version: \"{{VERSION}}\"\n    exec: usr/bin/imhex\n    exec_args: $@\n  apt:\n    arch:\n    - all\n    - \"{{ARCHITECTURE_PACKAGE}}\"\n    allow_unauthenticated: true\n    sources:\n    - sourceline: 'deb [arch=amd64] https://us.archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse'\n    - sourceline: 'deb [arch=arm64] https://ports.ubuntu.com/ubuntu-ports/ noble main restricted universe multiverse'\n    include:\n    - libgdk-pixbuf2.0-0\n    - libgdk-pixbuf2.0-common\n    - shared-mime-info\n    - librsvg2-common\n    - libbz2-1.0\n    - libcap2\n    - libdbus-1-3\n    - libfontconfig1\n    - libgpg-error0\n    - liblzma5\n    - libnss-mdns\n    - libpcre3\n    - libselinux1\n    - libtinfo6\n    - libmd4c-dev\n    - libmd4c-html0-dev\n  files:\n    include:\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libLLVM-13.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libOpenGL.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libX11.so.6\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXau.so.6\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXcomposite.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXcursor.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXdamage.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXdmcp.so.6\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXext.so.6\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXfixes.so.3\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXi.so.6\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXinerama.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXrandr.so.2\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXrender.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libXxf86vm.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libatk-1.0.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libatk-bridge-2.0.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libatspi.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libblkid.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libbrotlicommon.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libbrotlidec.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libbsd.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libcairo-gobject.so.2\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libcairo.so.2\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libcurl-gnutls.so.4\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libdatrie.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libedit.so.2\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libelf.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libepoxy.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libffi.so.8\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libfontconfig.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libfreetype.so.6\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libfribidi.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgcrypt.so.20\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgdk-3.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgdk_pixbuf-2.0.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgio-2.0.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libglfw.so.3\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libglib-2.0.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgmodule-2.0.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgmp.so.10\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgnutls.so.30\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgobject-2.0.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libgraphite2.so.3\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libharfbuzz.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libhogweed.so.6\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libicudata.so.70\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libicuuc.so.70\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libidn2.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libjpeg.so.8\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/liblber-2.5.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libldap-2.5.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/liblz4.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmagic.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmbedcrypto.so.7\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmbedtls.so.14\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmbedx509.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmd.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmount.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libnettle.so.8\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libp11-kit.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpango-1.0.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpangocairo-1.0.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpangoft2-1.0.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpcre2-8.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpixman-1.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libpng16.so.16\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libsasl2.so.2\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libsensors.so.5\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libstdc++.so.6\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libsystemd.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libtasn1.so.6\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libthai.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libunistring.so.2\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libuuid.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libvulkan.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libwayland-client.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libwayland-cursor.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libwayland-egl.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxcb-dri2.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxcb-dri3.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxcb-present.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxcb-sync.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxkbcommon.so.0\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxml2.so.2\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libxshmfence.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libzstd.so.1\"\n    - \"/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/libmd4c.so\"\n    exclude:\n    - usr/share/man\n    - usr/share/doc/*/README.*\n    - usr/share/doc/*/changelog.*\n    - usr/share/doc/*/NEWS.*\n    - usr/share/doc/*/TODO.*\n\n  runtime:\n    env:\n      APPDIR_LIBRARY_PATH: '$APPDIR/usr/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu:$APPDIR/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu:$APPDIR/usr/lib:$APPDIR/usr/lib/{{ARCHITECTURE_APPIMAGE_BUILDER}}-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders'\n      GTK_EXE_PREFIX: $APPDIR/usr\n      GTK_DATA_PREFIX: $APPDIR\n      XDG_DATA_DIRS: '/usr/local/share:/usr/share:$APPDIR/usr/share:$XDG_DATA_DIRS:$APPDIR/usr/local/share:$APPDIR/usr/local/lib:$APPDIR/usr/local/share'\n      XDG_CONFIG_DIRS: '$XDG_CONFIG_DIRS:$APPDIR/usr/local/share'\n\nAppImage:\n  arch: \"{{ARCHITECTURE_APPIMAGE_BUILDER}}\"\n  comp: zstd\n  update-information: gh-releases-zsync|WerWolv|ImHex|latest|imhex-*-{{ARCHITECTURE_FILE_NAME}}.AppImage.zsync\n  file_name: imhex-{{VERSION}}-{{ARCHITECTURE_FILE_NAME}}.AppImage\n"
  },
  {
    "path": "dist/AppImage/Dockerfile",
    "content": "FROM ubuntu:24.04 as build\n\n# Used to invalidate layer cache but not mount cache\n# See https://github.com/moby/moby/issues/41715#issuecomment-733976493\nARG UNIQUEKEY 1\n\nCOPY dist/get_deps_debian.sh /tmp\nRUN --mount=type=cache,target=/var/apt/cache <<EOF\n# Install apt dependencies\nset -xe\n\napt update\n\n# general deps\napt install -y ccache git wget\n# appimage tools deps\napt install -y python3-pip python3-venv python3-setuptools desktop-file-utils libgdk-pixbuf2.0-dev fuse ninja-build\napt install -y squashfs-tools zsync\n\n# imhex deps\n/tmp/get_deps_debian.sh\nEOF\n\nENV PATH=\"/cache/bin/:${PATH}\"\n\n# Copy Imhex source\nCOPY . /imhex\n\nARG LTO=ON\nARG BUILD_TYPE=RelWithDebInfo\nARG GIT_COMMIT_HASH\nARG GIT_BRANCH\nARG ARCHITECTURE_PACKAGE=x86_64\nARG ARCHITECTURE_FILE_NAME=amd64\nARG ARCHITECTURE_APPIMAGE_BUILDER=x86_64\nWORKDIR /build\n\n# Ubuntu sh doesnt support string substitution\nSHELL [\"bash\", \"-c\"]\n\nRUN <<EOF\n# Prepare ImHex build\nset -xe\n\nCC=gcc-14 CXX=g++-14 cmake -G \"Ninja\"                                \\\n    -DCMAKE_BUILD_TYPE=${BUILD_TYPE}                                 \\\n    -DCMAKE_INSTALL_PREFIX=\"/usr\"                                    \\\n    -DCMAKE_C_COMPILER_LAUNCHER=ccache                               \\\n    -DCMAKE_CXX_COMPILER_LAUNCHER=ccache                             \\\n    -DIMHEX_PATTERNS_PULL_MASTER=ON                                  \\\n    -DIMHEX_COMMIT_HASH_LONG=\"${GIT_COMMIT_HASH}\"                    \\\n    -DIMHEX_COMMIT_BRANCH=\"${GIT_BRANCH}\"                            \\\n    -DIMHEX_ENABLE_LTO=${LTO}                                        \\\n    -DIMHEX_BUNDLE_PLUGIN_SDK=OFF                                    \\\n    `# To prevent using a libdir with an architecture-specific name` \\\n    -DCMAKE_INSTALL_LIBDIR=\"lib\"                                     \\\n    /imhex\nEOF\n\nENV CCACHE_DIR /cache/ccache\nRUN --mount=type=cache,target=/cache <<EOF\n# Build Imhex\nset -xe\n\nccache -zs\nDESTDIR=AppDir ninja install\nccache -s\nEOF\n\nRUN <<EOF\n# Download appimage-builder\nset -xe\n\nmkdir -p /cache/bin\nwget -nc https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage -O /cache/bin/appimagetool || true\nchmod +x /cache/bin/appimagetool\n\npython3 -m venv venv\n. venv/bin/activate\npip3 install git+https://github.com/AppImageCrafters/appimage-builder@e995e8e\n\n# Package ImHex as AppImage\nexport VERSION=$(cat /imhex/VERSION)\nexport ARCHITECTURE_PACKAGE=${ARCHITECTURE_PACKAGE}\nexport ARCHITECTURE_FILE_NAME=${ARCHITECTURE_FILE_NAME}\nexport ARCHITECTURE_APPIMAGE_BUILDER=${ARCHITECTURE_APPIMAGE_BUILDER}\nappimage-builder --recipe /imhex/dist/AppImage/AppImageBuilder.yml\nEOF\n\nFROM scratch\n\n# Copy build artifact\nCOPY --from=build /build/*.AppImage /build/*.AppImage.zsync ./\n"
  },
  {
    "path": "dist/Arch/Dockerfile",
    "content": "FROM archlinux:latest\n\nLABEL maintainer=\"hey@werwolv.net WerWolv\"\n\n# Install dependencies\nRUN pacman -Syy --needed --noconfirm\nRUN pacman -S --needed --noconfirm      \\\n    git                                 \\\n    cmake                               \\\n    base-devel                          \\\n    gcc                                 \\\n    pkg-config                          \\\n    glfw-x11                            \\\n    file                                \\\n    mbedtls                             \\\n    fontconfig                          \\\n    freetype2                           \\\n    curl                                \\\n    dbus                                \\\n    xdg-desktop-portal                  \\\n    libssh2                             \\\n    md4c\n\n# Clone ImHex\nRUN git clone https://github.com/WerWolv/ImHex --recurse-submodules /root/ImHex\n\n# Build ImHex\nRUN mkdir /root/ImHex/build\nWORKDIR /root/ImHex/build\nRUN cmake .. && make -j\nWORKDIR /root/ImHex\n"
  },
  {
    "path": "dist/Arch/PKGBUILD",
    "content": "# Maintainer: iTrooz_ <aur at itrooz dot fr>\n# Contributor: Morten Linderud <foxboron@archlinux.org>\n\npkgname=imhex-bin\npkgver=%version%\npkgrel=1\npkgdesc=\"A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM. \"\narch=(\"x86_64\")\nurl=\"https://github.com/WerWolv/ImHex\"\nlicense=('GPL2')\ndepends=(glfw mbedtls fontconfig freetype2 libglvnd dbus gtk3 curl fmt yara zlib bzip2 xz zstd libssh2)\nmakedepends=(git nlohmann-json)\nprovides=(imhex)\nconflicts=(imhex)\nsource=(\"$url/releases/download/v$pkgver/imhex-$pkgver-ArchLinux-x86_64.pkg.tar.zst\")\nmd5sums=(SKIP)\n\npackage() {\n    install -Dm755 \"$srcdir/usr/bin/imhex\" \"$pkgdir/usr/bin/imhex\"\n    install -Dm755 \"$srcdir/usr/bin/imhex-updater\" \"$pkgdir/usr/bin/imhex-updater\"\n    install -Dm644 \"$srcdir/usr/lib/libimhex.so.$pkgver\" \"$pkgdir/usr/lib/libimhex.so.$pkgver\"\n\n    for plugin in \"$srcdir/usr/lib/imhex/plugins/\"*.hexplug*; do\n        install -Dm644 \"$plugin\" \"$pkgdir/usr/lib/imhex/plugins/${plugin##*/}\"\n    done\n\n    install -d \"$pkgdir/usr/share/imhex\"\n    cp -r \"$srcdir/usr/share/imhex/\"{constants,encodings,includes,magic,patterns} \"$pkgdir/usr/share/imhex\"\n    cp -r \"$srcdir/usr/share/\"{applications,licenses,pixmaps,mime} \"$pkgdir/usr/share\"\n}\n"
  },
  {
    "path": "dist/DEBIAN/control.in",
    "content": "Package: ImHex\nVersion: ${PROJECT_VERSION}\nSection: editors\nPriority: optional\nArchitecture: amd64\nLicense: GNU GPL-2\nDepends: libfontconfig1, libglfw3 | libglfw3-wayland, libmagic1, libmbedtls14, libfreetype6, libopengl0, libdbus-1-3, xdg-desktop-portal, libssh2-1, libmd4c0\nMaintainer: WerWolv <hey@werwolv.net>\nDescription: ImHex Hex Editor\n A Hex Editor for Reverse Engineers, Programmers and\n people who value their retinas when working at 3 AM.\n\n"
  },
  {
    "path": "dist/ImHex-9999.ebuild",
    "content": "# app-editors/ImHex\n# Copyright 2020 Gentoo Authors\n# Distributed under the terms of the GNU General Public License v2\n\nEAPI=7\n\nDESCRIPTION=\"A hex editor for reverse engineers, programmers, and eyesight\"\nHOMEPAGE=\"https://github.com/WerWolv/ImHex\"\nSRC_URI=\"\"\nEGIT_REPO_URI=\"https://github.com/WerWolv/ImHex.git\"\n\ninherit git-r3 cmake\n\nLICENSE=\"GPL-2\"\nSLOT=\"0\"\nKEYWORDS=\"~amd64\"\nIUSE=\"\"\n\nDEPEND=\"\"\nRDEPEND=\"${DEPEND}\n\t\tmedia-libs/glfw\n\t\tsys-apps/file\n\t\tnet-libs/mbedtls\n\t\tsys-apps/dbus\n\t\tsys-apps/xdg-desktop-portal\n\t\tsys-libs/zlib\n\t\tapp-arch/bzip2\n\t\tapp-arch/lzma\n\t\tapp-arch/zstd\n\t\tapp-arch/lz4\n\t\tnet-libs/libssh2\n\t\tdev-libs/md4c\n\t\t\"\nBDEPEND=\"${DEPEND}\n\t\tdev-cpp/nlohmann_json\n\t\t\"\n"
  },
  {
    "path": "dist/cli/imhex.bat",
    "content": "@echo off\n\nstart \"\" \"%~dp0..\\imhex.exe\" %*"
  },
  {
    "path": "dist/cli/imhex.sh",
    "content": "#!/bin/sh\n\nscript_path=$(readlink -f \"$0\")\nscript_dir=$(dirname \"${script_path}\")\n\"${script_dir}/../imhex\" \"$@\" > /dev/null 2>&1 &"
  },
  {
    "path": "dist/compiling/docker.md",
    "content": "For a TLDR of commands see [How to build](#How-to-build)\n\n# Introduction\n\nThe original CI we used (vanilla Github Actions) was great for specifying what steps to execute to build packages. It could even do some custom steps with reusable actions. But it had problem: no local reproducibility. This meant that:\n- We couldn't test code properly locally, we were dependent on GitHub to do it\n- If something was wrong and we had to debug the build script, it was *long and painful* because we had to wait for Github runners to finish builds, and couldn't quickly iterate\n\nTo solve this, we are now trying to move the CI build script to docker containers (so using Dockerfiles)\n\n# How to build\n\nCommands are available in the [CI](../../.github/workflows/build.yml) and you should prefer copying them from there.\nBut here is a general command that should work for every build we have:\n```\ndocker buildx build . -f <DOCKERFILE_PATH> --progress plain --build-arg 'JOBS=4' --build-arg 'BUILD_TYPE=Debug' --build-context imhex=$(pwd) --output local\n```\n\nwhere `<DOCKERFILE_PATH>` should be replaced by the wanted Dockerfile base d on the build you want to do:\n\n| Wanted build | Dockerfile path             | Target |\n|--------------|-----------------------------|--------|\n| MacOS M1     | dist/macOS/arm64.Dockerfile | -      |\n| AppImage     | dist/appimage/Dockerfile    | -      |\n| Web version  | dist/web/Dockerfile         | raw    |\n\nWe'll explain this command in the next section\n\n# Useful knowledge about Docker builds\n\nDocker-based builds work with a Dockerfile. You run the Dockerfile, it builds the package.\n\nWe are using a base environment (often given to us by dockerhub) (e.g. ubuntu:22.04) which is really just a root filesystem, and we then run shell commands in that env, just like a shell script\n\nDocker-based builds have two kind of caches used:\n- layer cache, which mean that if a layer (instruction) hasn't been changed, and previous layers haven't changed, it will not be run again\n    - a `COPY` layer will be invalidated if one of the file copied has changed\n- mount cache, which are per-instructions mounts that will be cached and restored in the next run. Mounts on different folders will not collide\n\nDocker cache tends to grow very quickly when constantly making changes in the Dockerfile and rebuilding (a.k.a debugging what's going on), you can clear it with something like `docker system prune -a`\n\nIn the command saw earlier:\n- `.` is the base folder that the Dockerfile will be allowed to see\n- `-f <path>` is to specify the Dockerfile path\n- `--progress plain` is to allow you to see the output of instructions\n- `--build-arg <key>=<value>` is to allow to specify arguments to the build (like -DKEY=VALUE in CMake)\n- `--build-context key=<folder>` is to specify folders other than the base folder that the Dockerfile is allowed to see\n- `--output <path>` is the path to write the output package to. If not specified, Docker will create an image as the output (probably not what you want)\n- `--target <target>` specifies which docker target to build\n"
  },
  {
    "path": "dist/compiling/linux.md",
    "content": "### Compiling ImHex on Linux\n\nOn Linux, ImHex is built through regular GCC (or optionally Clang).\n\n1. Clone the repo using `git clone https://github.com/WerWolv/ImHex --recurse-submodules`\n2. Install the dependencies using one of the `dist/get_deps_*.sh` scripts. Choose the one that matches your distro.\n3. Build ImHex itself using the following commands:\n```sh\ncd ImHex\nmkdir -p build\ncd build\nCC=gcc-14 CXX=g++-14                          \\\ncmake -G \"Ninja\"                              \\\n    -DCMAKE_BUILD_TYPE=Release                \\\n    -DCMAKE_INSTALL_PREFIX=\"/usr\"             \\\n    ..\nninja install\n```\n\nAll paths follow the XDG Base Directories standard, and can thus be modified\nwith the environment variables `XDG_CONFIG_HOME`, `XDG_CONFIG_DIRS`,\n`XDG_DATA_HOME` and `XDG_DATA_DIRS`."
  },
  {
    "path": "dist/compiling/macos.md",
    "content": "### Compiling ImHex on macOS\n\nOn macOS, ImHex is built through regular GCC and LLVM clang.\n\n1. Clone the repo using `git clone https://github.com/WerWolv/ImHex --recurse-submodules`\n2. Install all the dependencies using `brew bundle --file dist/macOS/Brewfile`\n3. Build ImHex itself using the following commands:\n```sh\ncd ImHex\nmkdir -p build\ncd build\nCC=$(brew --prefix llvm)/bin/clang        \\\nCXX=$(brew --prefix llvm)/bin/clang++     \\\nOBJC=$(brew --prefix llvm)/bin/clang      \\\nOBJCXX=$(brew --prefix llvm)/bin/clang++  \\\ncmake -G \"Ninja\"                          \\\n  -DCMAKE_BUILD_TYPE=Release              \\\n  -DCMAKE_INSTALL_PREFIX=\"./install\"      \\\n  -DIMHEX_GENERATE_PACKAGE=ON             \\\n  ..\nninja install\n```\n\nIf your MacOS installation doesn't have graphic acceleration, you can check the [MacOS NoGPU guide](./macos_nogpu.md)"
  },
  {
    "path": "dist/compiling/macos_nogpu.md",
    "content": "### Compiling and running ImHex on macOS without a GPU\n\nIn order to run ImHex on a macOS installation without a GPU, you need a custom build of GLFW. You can build it this way:\n\nNote: only tested on macOS x86\n\n1. `git clone --depth 1 https://github.com/glfw/glfw`\n2. `git apply {IMHEX_DIR}/dist/macOS/0001-glfw-SW.patch` (file is [here](../macOS/0001-glfw-SW.patch) in the ImHex repository. [Source](https://github.com/glfw/glfw/issues/2080).)\n3. `cmake -G \"Ninja\" -DBUILD_SHARED_LIBS=ON ..`\n4. `ninja install`, or `ninja` and figure out how to make ImHex detect the shared library\n"
  },
  {
    "path": "dist/compiling/windows.md",
    "content": "### Compiling ImHex on Windows\n\nOn Windows, ImHex is built through [msys2 / mingw](https://www.msys2.org/)'s gcc.\n\n1. Download and install msys2 from their [website](https://www.msys2.org/).\n2. Open the `MSYS2 MinGW x64` shell\n3. Clone the repo using `git clone https://github.com/WerWolv/ImHex --recurse-submodules`\n4. Install all the dependencies using `./ImHex/dist/get_deps_msys2.sh`\n5. Build ImHex itself using the following commands:\n\n```sh\ncd ImHex\nmkdir build\ncd build\ncmake -G \"Ninja\"                          \\\n  -DCMAKE_BUILD_TYPE=Release              \\\n  -DCMAKE_INSTALL_PREFIX=\"./install\"      \\\n  -DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON   \\\n  ..\nninja install\n```\n\nImHex will look for any extra resources either in various folders directly next to the executable or in `%localappdata%/imhex`\n\nFor low RAM-usage system, you can use `mingw32-make -j N install` instead, to reduce RAM usage at compile time. Where `N` is amount of jobs you are willling to run at once. Roughly ~1 GB of RAM usage per job.\n"
  },
  {
    "path": "dist/cppcheck.supp",
    "content": "missingIncludeSystem\nconstParameter\nunusedFunction\npreprocessorErrorDirective\ncheckersReport\nnoExplicitConstructor\nunmatchedSuppression\nuseInitializationList\nuseStlAlgorithm\nknownConditionTrueFalse\ninternalAstError\nunsignedPositive\nvariableScope\nunusedPrivateFunction\nconstParameterCallback\n\n*:*/lib/third_party/*\n*:*/external/pattern_language/external/*\n*:*/external/disassembler/external/*\n*:*/lib/libimhex/source/ui/imgui_imhex_extensions.cpp"
  },
  {
    "path": "dist/flake.nix",
    "content": "{\n  description = \"ImHex\";\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixos-unstable\";\n  };\n\n  outputs = { self, nixpkgs }:\n    let\n      supportedSystems = [ \"x86_64-linux\" \"aarch64-linux\" \"aarch64-darwin\" \"x86_64-darwin\" ];\n      forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system);\n    in {\n      devShells = forAllSystems (system:\n        let\n          pkgs = import nixpkgs { inherit system; };\n        in\n          {\n            default = pkgs.mkShell {\n              buildInputs = [\n                pkgs.cmake\n                pkgs.clang\n                pkgs.lld\n\n                pkgs.nghttp3\n                pkgs.pkg-config\n                pkgs.glfw\n                pkgs.fontconfig\n                pkgs.file\n                pkgs.mbedtls\n                pkgs.freetype\n                pkgs.dbus\n                pkgs.gtk3\n                pkgs.curl\n                pkgs.fmt\n                pkgs.yara\n                pkgs.nlohmann_json\n                pkgs.ninja\n                pkgs.zlib\n                pkgs.bzip2\n                pkgs.xz\n                pkgs.zstd\n                pkgs.lz4\n                pkgs.libssh2\n                pkgs.md4c\n              ];\n\n              shellHook = ''\n                export CC=${pkgs.clang}/bin/clang\n                export CXX=${pkgs.clang}/bin/clang++\n              '';\n            };\n          });\n    };\n}\n"
  },
  {
    "path": "dist/flatpak/net.werwolv.ImHex.yaml",
    "content": "app-id: net.werwolv.ImHex\nruntime: org.freedesktop.Platform\nruntime-version: '24.08'\nsdk: org.freedesktop.Sdk\ncommand: imhex\nrename-desktop-file: imhex.desktop\n\nfinish-args:\n  - --share=ipc\n  - --share=network\n  - --socket=wayland\n  - --socket=fallback-x11\n  - --filesystem=host\n  - --device=dri\n\nmodules:\n  - name: glfw\n    buildsystem: cmake-ninja\n    builddir: true\n    config-opts:\n      - -DBUILD_SHARED_LIBS=ON\n      - -DGLFW_BUILD_EXAMPLES=OFF\n      - -DGLFW_BUILD_TESTS=OFF\n      - -DGLFW_BUILD_DOCS=OFF\n    sources:\n      - type: archive\n        url: https://github.com/glfw/glfw/releases/download/3.4/glfw-3.4.zip\n        sha256: b5ec004b2712fd08e8861dc271428f048775200a2df719ccf575143ba749a3e9\n    cleanup:\n      - /include\n      - /lib/pkgconfig\n      - /lib/pkgconfig\n      - /lib64/pkgconfig\n      - /lib/cmake\n      - /lib64/cmake\n\n  - name: mbedtls\n    buildsystem: cmake-ninja\n    config-opts:\n      - -DCMAKE_C_FLAGS=-fPIC\n      - -DENABLE_TESTING=OFF\n      - -DENABLE_PROGRAMS=OFF\n    sources:\n      - type: archive\n        url: https://github.com/ARMmbed/mbedtls/archive/refs/tags/v3.4.0.tar.gz\n        sha256: 1b899f355022e8d02c4d313196a0a16af86c5a692456fa99d302915b8cf0320a\n    cleanup:\n      - /include\n      - /lib/pkgconfig\n      - /lib64/pkgconfig\n      - /lib/cmake\n      - /lib64/cmake\n\n  - name: fmt\n    buildsystem: cmake-ninja\n    config-opts:\n      - -DBUILD_SHARED_LIBS=ON\n      - -DFMT_TEST=OFF\n    sources:\n      - type: archive\n        url: https://github.com/fmtlib/fmt/releases/download/10.0.0/fmt-10.0.0.zip\n        sha256: 4943cb165f3f587f26da834d3056ee8733c397e024145ca7d2a8a96bb71ac281\n    cleanup:\n      - /include\n      - /lib/pkgconfig\n      - /lib64/pkgconfig\n      - /lib/cmake\n      - /lib64/cmake\n\n  - name: yara\n    buildsystem: autotools\n    sources:\n      - type: git\n        url: https://github.com/VirusTotal/yara.git\n        tag: v4.3.1\n        commit: a6f6ce1d6d74a03c396660db25765f2a794d9e30\n    cleanup:\n      - /include\n      - /lib/pkgconfig\n      - /lib64/pkgconfig\n      - /lib/cmake\n      - /lib64/cmake\n\n  - name: libssh2\n    buildsystem: cmake-ninja\n    config-opts:\n      - -DBUILD_SHARED_LIBS=ON\n      - -DENABLE_ZLIB_COMPRESSION=OFF\n      - -DENABLE_MANUAL=OFF\n      - -DENABLE_EXAMPLES=OFF\n      - -DENABLE_TESTING=OFF\n    sources:\n      - type: git\n        url: https://github.com/libssh2/libssh2.git\n        tag: libssh2-1.11.1\n    cleanup:\n      - /include\n      - /lib/pkgconfig\n      - /lib64/pkgconfig\n      - /lib/cmake\n      - /lib64/cmake\n\n  - name: md4c\n    buildsystem: cmake-ninja\n    config-opts:\n      - -DBUILD_SHARED_LIBS=ON\n      - -DMD4C_BUILD_TESTS=OFF\n      - -DMD4C_BUILD_EXAMPLES=OFF\n      - -DMD4C_BUILD_DOCS=OFF\n    sources:\n      - type: git\n        url: https://github.com/mity/md4c.git\n        tag: release-0.5.2\n\n  - name: imhex\n    buildsystem: cmake-ninja\n    builddir: true\n    config-opts:\n      - -DUSE_SYSTEM_CURL=ON\n      - -DUSE_SYSTEM_FMT=ON\n      - -DUSE_SYSTEM_YARA=ON\n      - -DIMHEX_OFFLINE_BUILD=ON\n      - -DIMHEX_BUNDLE_PLUGIN_SDK=OFF\n      - -DCMAKE_INSTALL_LIBDIR=lib\n      - -DCMAKE_INSTALL_RPATH='$ORIGIN/../lib:$ORIGIN/../lib64'\n    sources:\n      - type: dir\n        path: ../..\n      - type: git\n        url: https://github.com/WerWolv/ImHex-Patterns.git\n        tag: master\n        dest: ImHex-Patterns\n        x-checker-data:\n          type: git\n          tag-pattern: ^ImHex-v([\\d.]+)$\n    post-install:\n      - mkdir -p ${FLATPAK_DEST}/share/icons/hicolor/scalable/apps\n      - cp ${FLATPAK_DEST}/share/pixmaps/imhex.svg ${FLATPAK_DEST}/share/icons/hicolor/scalable/apps/${FLATPAK_ID}.svg\n      - desktop-file-edit --set-key=\"Icon\" --set-value=\"${FLATPAK_ID}\" \"${FLATPAK_DEST}/share/applications/imhex.desktop\"\n"
  },
  {
    "path": "dist/fonts/move_private_use_area.py",
    "content": "from fontTools.ttLib import TTFont\nfrom fontTools.ttLib.tables._c_m_a_p import CmapSubtable\nimport argparse\n\n# Default PUAs\nSOURCE_PUA_START = 0xEA00\nSOURCE_PUA_END = 0x100F2\nTARGET_PUA_START = 0xF0000\n\ndef move_pua_glyphs(input_font_path, output_font_path):\n    font = TTFont(input_font_path)\n\n    cmap_table = font['cmap']\n    glyph_set = font.getGlyphSet()\n\n    # Track moved glyphs\n    moved = 0\n    new_mapping = {}\n\n    # Collect original mappings in the PUA\n    for cmap in cmap_table.tables:\n        if cmap.isUnicode():\n            for codepoint, glyph_name in cmap.cmap.items():\n                if SOURCE_PUA_START <= codepoint <= SOURCE_PUA_END:\n                    offset = codepoint - SOURCE_PUA_START\n                    new_codepoint = TARGET_PUA_START + offset\n                    new_mapping[new_codepoint] = glyph_name\n                    moved += 1\n\n    if moved == 0:\n        print(\"No glyphs found in the source Private Use Area.\")\n        return\n\n    # Remove old PUA entries from existing cmap subtables\n    for cmap in cmap_table.tables:\n        if cmap.isUnicode():\n            cmap.cmap = {\n                cp: gn for cp, gn in cmap.cmap.items()\n                if not (SOURCE_PUA_START <= cp <= SOURCE_PUA_END)\n            }\n\n    # Create or update a format 12 cmap subtable\n    found_format12 = False\n    for cmap in cmap_table.tables:\n        if cmap.format == 12 and cmap.platformID == 3 and cmap.platEncID in (10, 1):\n            cmap.cmap.update(new_mapping)\n            found_format12 = True\n            break\n\n    if not found_format12:\n        # Create a new format 12 subtable\n        cmap12 = CmapSubtable.newSubtable(12)\n        cmap12.platformID = 3\n        cmap12.platEncID = 10  # UCS-4\n        cmap12.language = 0\n        cmap12.cmap = new_mapping\n        cmap_table.tables.append(cmap12)\n\n    print(f\"Moved {moved} glyphs from U+{SOURCE_PUA_START:X}–U+{SOURCE_PUA_END:X} to U+{TARGET_PUA_START:X}+\")\n    font.save(output_font_path)\n    print(f\"Saved modified font to {output_font_path}\")\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser(description=\"Move PUA glyphs in a TTF file to another Unicode range.\")\n    parser.add_argument(\"input\", help=\"Input TTF file path\")\n    parser.add_argument(\"output\", help=\"Output TTF file path\")\n\n    args = parser.parse_args()\n    move_pua_glyphs(args.input, args.output)\n"
  },
  {
    "path": "dist/fonts/ttf_to_header_file.py",
    "content": "import argparse\nfrom fontTools.ttLib import TTFont\nimport os\n\ndef unicode_to_utf8_escape(codepoint):\n    return ''.join([f'\\\\x{b:02x}' for b in chr(codepoint).encode('utf-8')])\n\ndef format_macro_name(prefix, glyph_name):\n    # Convert names like 'repo-forked' -> 'ICON_VS_REPO_FORKED'\n    return \"ICON_\" + prefix + \"_\" + glyph_name.upper().replace('-', '_')\n\ndef generate_font_header(font_path, output_path, font_macro_name, font_file_macro):\n    font = TTFont(font_path)\n\n    # Use cmap to get Unicode to glyph mapping\n    codepoint_to_names = {}\n    for table in font[\"cmap\"].tables:\n        if table.isUnicode():\n            for codepoint, glyph_name in table.cmap.items():\n                codepoint_to_names.setdefault(codepoint, []).append(glyph_name)\n\n    if not codepoint_to_names:\n        print(\"No Unicode-mapped glyphs found in the font.\")\n        return\n\n    # Remove any glyph that is lower than 0xFF\n    codepoint_to_names = {cp: names for cp, names in codepoint_to_names.items() if cp >= 0xFF}\n\n    min_cp = min(codepoint_to_names)\n    max_cp = max(codepoint_to_names)\n\n    with open(output_path, \"w\", encoding=\"utf-8\") as out:\n        out.write(\"#pragma once\\n\\n\")\n        out.write(f'#define FONT_ICON_FILE_NAME_{font_macro_name} \"{font_file_macro}\"\\n\\n')\n        out.write(f\"#define ICON_MIN_{font_macro_name} 0x{min_cp:04x}\\n\")\n        out.write(f\"#define ICON_MAX_16_{font_macro_name} 0x{max_cp:04x}\\n\")\n        out.write(f\"#define ICON_MAX_{font_macro_name} 0x{max_cp:04x}\\n\")\n\n        written = set()\n        for codepoint in sorted(codepoint_to_names):\n            utf8 = unicode_to_utf8_escape(codepoint)\n            comment = f\"// U+{codepoint:04X}\"\n            glyph_names = sorted(set(codepoint_to_names[codepoint]))\n            for i, glyph_name in enumerate(glyph_names):\n                macro = format_macro_name(font_macro_name, glyph_name)\n                if macro in written:\n                    continue\n                out.write(f\"#define {macro} \\\"{utf8}\\\"\\t{comment}\\n\")\n                written.add(macro)\n\n    print(f\"Header generated at {output_path}\")\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser(description=\"Generate C header file from TTF glyphs.\")\n    parser.add_argument(\"font\", help=\"Input .ttf font file\")\n    parser.add_argument(\"output\", help=\"Output .h file\")\n    parser.add_argument(\"macro_name\", help=\"Macro prefix\")\n    args = parser.parse_args()\n\n    generate_font_header(args.font, args.output, args.macro_name, os.path.basename(args.font))\n"
  },
  {
    "path": "dist/gen_release_notes.py",
    "content": "import subprocess\nimport sys\n\ndef get_commits(branch: str, start_tag: str, end_tag: str) -> list[str]:\n    try:\n        commits_raw = subprocess.check_output([ \"git\", \"--no-pager\", \"log\", branch, \"--no-color\", \"--pretty=oneline\", \"--abbrev-commit\", f\"{start_tag}..{end_tag}\"], stderr=subprocess.DEVNULL).decode(\"UTF-8\").split(\"\\n\")\n    except:\n        return []\n\n    commits = []\n    for line in commits_raw:\n        commits.append(line[9:])\n\n    return commits\n\ndef main(args: list) -> int:\n    if len(args) != 2:\n        print(f\"Usage: {args[0]} prev_minor\")\n        return 1\n    \n    last_minor_version = f\"v1.{args[1]}\"\n\n    master_commits = get_commits(\"master\", f\"{last_minor_version}.0\", \"master\")\n\n    for i in range(1, 100):\n        branch_commits = get_commits(f\"releases/{last_minor_version}.X\", f\"{last_minor_version}.0\", f\"{last_minor_version}.{i}\")\n\n        if len(branch_commits) == 0:\n            break\n\n        master_commits = [commit for commit in master_commits if commit not in branch_commits]\n\n    sorted_commits = {}\n    for commit in master_commits:\n        if commit == \"\":\n            continue\n\n        try:\n            category, commit_name = commit.split(\":\", 1)\n\n            if category not in sorted_commits:\n                sorted_commits[category] = []\n            sorted_commits[category].append(commit_name)\n        except:\n            print(f\"Failed to parse commit: {commit}\")\n\n    for category in sorted_commits:\n        print(f\"## {category}\\n\")\n        for commit in sorted_commits[category]:\n            print(f\"- {commit}\")\n        print(f\"\\n\")\n\nif __name__ == \"__main__\":\n    exit(main(sys.argv))"
  },
  {
    "path": "dist/get_deps_archlinux.sh",
    "content": "#!/usr/bin/env sh\n\npacman -S $@ --needed \\\n  cmake         \\\n  gcc           \\\n  lld           \\\n  glfw          \\\n  fontconfig    \\\n  file          \\\n  mbedtls       \\\n  freetype2     \\\n  dbus          \\\n  gtk3          \\\n  curl          \\\n  fmt           \\\n  yara          \\\n  nlohmann-json \\\n  ninja         \\\n  zlib          \\\n  bzip2         \\\n  xz            \\\n  zstd          \\\n  lz4           \\\n  libssh2       \\\n  md4c\n"
  },
  {
    "path": "dist/get_deps_debian.sh",
    "content": "#!/usr/bin/env sh\n\n# Install pkgconf (adds minimum dependencies) only if the equivalent pkf-config is not already installed.\nif ! which pkg-config\nthen\n    PKGCONF=\"pkgconf\"\nfi\n\napt install -y \\\n  build-essential       \\\n  gcc-14                \\\n  g++-14                \\\n  lld                   \\\n  ${PKGCONF:-}          \\\n  cmake                 \\\n  ccache                \\\n  libglfw3-dev          \\\n  libglm-dev            \\\n  libmagic-dev          \\\n  libmbedtls-dev        \\\n  libfontconfig-dev     \\\n  libfreetype-dev       \\\n  libdbus-1-dev         \\\n  libcurl4-gnutls-dev   \\\n  libgtk-3-dev          \\\n  ninja-build           \\\n  zlib1g-dev            \\\n  libbz2-dev            \\\n  liblzma-dev           \\\n  libzstd-dev           \\\n  liblz4-dev            \\\n  libssh2-1-dev         \\\n  libmd4c-dev           \\\n  libmd4c-html0-dev\n"
  },
  {
    "path": "dist/get_deps_fedora.sh",
    "content": "#!/usr/bin/env sh\n\ndnf install -y      \\\n  cmake             \\\n  dbus-devel        \\\n  file-devel        \\\n  fontconfig-devel  \\\n  freetype-devel    \\\n  libcurl-devel     \\\n  gcc-c++           \\\n  git               \\\n  mesa-libGL-devel  \\\n  glfw-devel        \\\n  lld               \\\n  mbedtls-devel     \\\n  gtk3-devel        \\\n  libzstd-devel     \\\n  zlib-devel        \\\n  bzip2-devel       \\\n  xz-devel          \\\n  lz4-devel         \\\n  libssh2-devel\n"
  },
  {
    "path": "dist/get_deps_msys2.sh",
    "content": "#!/usr/bin/env sh\n\npacman -S --needed --noconfirm pactoys unzip git\npacboy -S --needed --noconfirm  \\\n  gcc:p             \\\n  lld:p             \\\n  cmake:p           \\\n  ccache:p          \\\n  glfw:p            \\\n  file:p            \\\n  curl-winssl:p     \\\n  mbedtls:p         \\\n  freetype:p        \\\n  dlfcn:p           \\\n  ninja:p           \\\n  capstone:p        \\\n  zlib:p            \\\n  bzip2:p           \\\n  xz:p              \\\n  zstd:p            \\\n  lz4:p             \\\n  libssh2-wincng:p  \\\n  md4c:p\n"
  },
  {
    "path": "dist/get_deps_tumbleweed.sh",
    "content": "#!/usr/bin/env sh\n\nzypper install     \\\n  cmake            \\\n  ninja            \\\n  gcc14            \\\n  gcc14-c++        \\\n  fontconfig-devel \\\n  freetype2-devel  \\\n  libcurl-devel    \\\n  dbus-1-devel     \\\n  file-devel       \\\n  Mesa-libGL-devel \\\n  libglfw-devel    \\\n  mbedtls-devel    \\\n  gtk3-devel       \\\n  libzstd-devel    \\\n  zlib-devel       \\\n  bzip3-devel      \\\n  xz-devel         \\\n  lz4-dev          \\\n  libssh2-devel    \\\n  md4c-devel\n"
  },
  {
    "path": "dist/imhex.desktop",
    "content": "[Desktop Entry]\nVersion=1.0\nName=ImHex\nComment=ImHex Hex Editor\nGenericName=Hex Editor\nExec=imhex %U\nIcon=imhex\nType=Application\nStartupNotify=true\nCategories=Development;IDE;\nStartupWMClass=imhex\nKeywords=static-analysis;reverse-engineering;disassembler;disassembly;hacking;forensics;hex-editor;cybersecurity;security;binary-analysis;\nMimeType=application/vnd.imhex.proj;\nActions=NewFile;\n\n[Desktop Action NewFile]\nExec=imhex --new\nName=Create New File"
  },
  {
    "path": "dist/imhex.mime.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n    <mime-type type=\"application/vnd.imhex.proj\">\n        <comment>ImHex Project</comment>\n        <glob pattern=\"*.hexproj\"/>\n    </mime-type>\n</mime-info>\n"
  },
  {
    "path": "dist/langtool.py",
    "content": "#!/usr/bin/env python3\nfrom pathlib import Path\nimport argparse\nimport json\n\n# This fixes a CJK full-width character input issue\n#  which makes left halves of deleted characters displayed on screen\n# pylint: disable=unused-import\nimport re\nimport readline\n\nDEFAULT_LANG = \"en_US\"\nDEFAULT_LANG_PATH = \"plugins/*/romfs/lang/\"\nINVALID_TRANSLATION = \"\"\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        prog=\"langtool\",\n        description=\"ImHex translate tool\",\n    )\n    parser.add_argument(\n        \"command\",\n        choices=[\n            \"check\",\n            \"translate\",\n            \"update\",\n            \"create\",\n            \"retranslate\",\n            \"untranslate\",\n            \"fmtzh\",\n        ],\n    )\n    parser.add_argument(\n        \"-c\", \"--langdir\", default=DEFAULT_LANG_PATH, help=\"Language folder glob\"\n    )\n    parser.add_argument(\"-l\", \"--lang\", default=\"\", help=\"Language to translate\")\n    parser.add_argument(\n        \"-r\", \"--reflang\", default=\"\", help=\"Language for reference when translating\"\n    )\n    parser.add_argument(\n        \"-k\", \"--keys\", help=\"Keys to re-translate (only in re/untranslate mode)\"\n    )\n    args = parser.parse_args()\n\n    command = args.command\n    lang = args.lang\n\n    print(f\"Running in {command} mode\")\n    lang_files_glob = f\"{lang}.json\" if lang != \"\" else \"*.json\"\n\n    lang_folders = set(Path(\".\").glob(args.langdir))\n    if len(lang_folders) == 0:\n        print(f\"Error: {args.langdir} matches nothing\")\n        return 1\n\n    for lang_folder in lang_folders:\n        if not lang_folder.is_dir():\n            print(f\"Error: {lang_folder} is not a folder\")\n            return 1\n\n        default_lang_data = {}\n        default_lang_path = lang_folder / Path(DEFAULT_LANG + \".json\")\n        if not default_lang_path.exists():\n            print(\n                f\"Error: Default language file {default_lang_path} does not exist in {lang_folder}\"\n            )\n            return 1\n        with default_lang_path.open(\"r\", encoding=\"utf-8\") as file:\n            default_lang_data = json.load(file)\n\n        reference_lang_data = None\n        reference_lang_path = lang_folder / Path(args.reflang + \".json\")\n        if reference_lang_path.exists():\n            with reference_lang_path.open(\"r\", encoding=\"utf-8\") as file:\n                reference_lang_data = json.load(file)\n\n        if command == \"create\" and lang != \"\":\n            lang_file_path = lang_folder / Path(lang + \".json\")\n            if lang_file_path.exists():\n                continue\n\n            exist_lang_data = None\n            for lang_folder1 in lang_folders:\n                lang_file_path1 = lang_folder1 / Path(lang + \".json\")\n                if lang_file_path1.exists():\n                    with lang_file_path1.open(\"r\", encoding=\"utf-8\") as file:\n                        exist_lang_data = json.load(file)\n                    break\n\n            print(f\"Creating new language file '{lang_file_path}'\")\n\n            with lang_file_path.open(\"w\", encoding=\"utf-8\") as new_lang_file:\n                new_lang_data = {\n\n                }\n                json.dump(new_lang_data, new_lang_file, indent=4, ensure_ascii=False)\n\n        lang_files = set(lang_folder.glob(lang_files_glob))\n        if len(lang_files) == 0:\n            print(f\"Warn: Language file for '{lang}' does not exist in '{lang_folder}'\")\n        for lang_file_path in lang_files:\n            if (\n                lang_file_path.stem == f\"{DEFAULT_LANG}.json\"\n                or lang_file_path.stem == f\"{args.reflang}.json\"\n            ):\n                continue\n\n            print(f\"\\nProcessing '{lang_file_path}'\")\n            if not (command == \"update\" or command == \"create\"):\n                print(\"\\n----------------------------\\n\")\n\n            with lang_file_path.open(\"r+\", encoding=\"utf-8\") as target_lang_file:\n                lang_data = json.load(target_lang_file)\n\n                for key, value in default_lang_data.items():\n                    has_translation = (\n                        key in lang_data\n                        and lang_data[key] != INVALID_TRANSLATION\n                    )\n                    if (\n                        has_translation\n                        and not (\n                            (command == \"retranslate\" or command == \"untranslate\")\n                            and re.compile(args.keys).fullmatch(key)\n                        )\n                        and not command == \"fmtzh\"\n                    ):\n                        continue\n                    if command == \"check\":\n                        print(\n                            f\"Error: Translation {lang_file_path} is missing translation for key '{key}'\"\n                        )\n                    elif (\n                        command == \"translate\"\n                        or command == \"retranslate\"\n                        or command == \"untranslate\"\n                    ):\n                        if command == \"untranslate\" and not has_translation:\n                            continue\n                        reference_tranlsation = (\n                            \" '%s'\" % reference_lang_data[key]\n                            if (\n                                reference_lang_data\n                                and key in reference_lang_data\n                            )\n                            else \"\"\n                        )\n                        print(\n                            f\"\\033[1m'{key}' '{value}'{reference_tranlsation}\\033[0m => \",\n                            end=\"\",\n                        )\n                        if has_translation:\n                            translation = lang_data[key]\n                            print(f\" <= \\033[1m'{translation}'\\033[0m\")\n                        print()  # for a new line\n                        if command == \"untranslate\":\n                            lang_data[key] = INVALID_TRANSLATION\n                            continue\n                        try:\n                            new_value = input(\"=> \")\n                            lang_data[key] = new_value\n                        except KeyboardInterrupt:\n                            break\n                    elif command == \"update\" or command == \"create\":\n                        lang_data[key] = INVALID_TRANSLATION\n                    elif command == \"fmtzh\":\n                        if has_translation:\n                            lang_data[key] = fmtzh(\n                                lang_data[key]\n                            )\n\n                keys_to_remove = []\n                for key, value in lang_data.items():\n                    if key not in default_lang_data:\n                        keys_to_remove.append(key)\n                for key in keys_to_remove:\n                    lang_data.pop(key)\n                    print(\n                        f\"Removed unused key '{key}' from translation '{lang_file_path}'\"\n                    )\n\n                target_lang_file.seek(0)\n                target_lang_file.truncate()\n                json.dump(\n                    lang_data,\n                    target_lang_file,\n                    indent=4,\n                    sort_keys=True,\n                    ensure_ascii=False,\n                )\n\n\ndef fmtzh(text: str) -> str:\n    text = re.sub(r\"(\\.{3}|\\.{6})\", \"……\", text)\n    text = text.replace(\"!\", \"！\")\n    text = re.sub(r\"([^\\.\\na-zA-Z\\d])\\.$\", \"\\1。\", text, flags=re.M)\n    text = text.replace(\"?\", \"？\")\n    return text\n\n\nif __name__ == \"__main__\":\n    exit(main())\n"
  },
  {
    "path": "dist/macOS/0001-glfw-SW.patch",
    "content": "From 9c8665af4c2e2ce66555c15c05c72027bfdf0cb6 Mon Sep 17 00:00:00 2001\nFrom: iTrooz <hey@itrooz.fr>\nDate: Mon, 29 Aug 2022 17:29:38 +0200\nSubject: [PATCH] Use software rendering on MacOS\n\n---\n src/nsgl_context.m | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/src/nsgl_context.m b/src/nsgl_context.m\nindex fc1f7521..e5906575 100644\n--- a/src/nsgl_context.m\n+++ b/src/nsgl_context.m\n@@ -198,7 +198,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,\n     NSOpenGLPixelFormatAttribute attribs[40];\n     int index = 0;\n \n-    ADD_ATTRIB(NSOpenGLPFAAccelerated);\n+    ADD_ATTRIB(NSOpenGLPFARendererID);ADD_ATTRIB(kCGLRendererGenericFloatID);\n     ADD_ATTRIB(NSOpenGLPFAClosestPolicy);\n \n     if (ctxconfig->nsgl.offline)\n-- \n2.37.2\n\n"
  },
  {
    "path": "dist/macOS/Brewfile",
    "content": "brew \"mbedtls\"\nbrew \"nlohmann-json\"\nbrew \"cmake\"\nbrew \"ccache\"\nbrew \"freetype2\"\nbrew \"libmagic\"\nbrew \"pkg-config\"\nbrew \"curl\"\nbrew \"llvm\"\nbrew \"glfw\"\nbrew \"ninja\"\nbrew \"zlib\"\nbrew \"xz\"\nbrew \"bzip2\"\nbrew \"zstd\"\nbrew \"libssh2\"\nbrew \"md4c\""
  },
  {
    "path": "dist/macOS/arm64.Dockerfile",
    "content": "# This base image is also known as \"crosscompile\". See arm64.crosscompile.Dockerfile\nFROM ghcr.io/werwolv/macos-crosscompile:6d89b20ac5ebedb6f680f94637591c94cb36f40b as build\n\nENV MACOSX_DEPLOYMENT_TARGET 11.0\n\n# -- DOWNLOADING STUFF\n\n# Update vcpkg\nRUN <<EOF\ncp /vcpkg/triplets/community/arm-osx-mytriplet.cmake /tmp/arm-osx-mytriplet.cmake\ngit -C /vcpkg clean -ffdx\ngit -C /vcpkg checkout origin/master\ngit -C /vcpkg reset --hard\ngit -C /vcpkg pull\n/vcpkg/bootstrap-vcpkg.sh\ncp /tmp/arm-osx-mytriplet.cmake /vcpkg/triplets/community/arm-osx-mytriplet.cmake\nEOF\n\n## Install make\nRUN --mount=type=cache,target=/var/lib/apt/lists/ apt update && apt install -y make cmake\n\n## fix environment\n### add install_name_tool for cmake command that won't have the right env set (see PostprocessBundle.cmake function postprocess_bundle())\nRUN cp /osxcross/build/cctools-port/cctools/misc/install_name_tool /usr/bin/install_name_tool\n### a cmake thing wants 'otool' and not '' apparently\nRUN cp /osxcross/target/bin/aarch64-apple-darwin24-otool /usr/bin/otool\n\n## Clone glfw\nRUN <<EOF\nset -xe\nif [ \"$CUSTOM_GLFW\" ]; then\n    git clone https://github.com/glfw/glfw /mnt/glfw\nfi\nEOF\n\nRUN --mount=type=cache,target=/cache <<EOF\n## Download SDK is missing (it may have been removed from the image)\nset -xe\nif [ ! -d /osxcross/target/SDK/MacOSX15.0.sdk ]; then\n    wget https://github.com/joseluisq/macosx-sdks/releases/download/15.0/MacOSX15.0.sdk.tar.xz -O /cache/MacOSX15.0.sdk.tar.xz -nc || true\n    mkdir -p /osxcross/target/SDK\n    tar -C /osxcross/target/SDK -xf /cache/MacOSX15.0.sdk.tar.xz\nfi\nEOF\n\n\n## Download libmagic\n### Clone libmagic\nRUN git clone --depth 1 --branch FILE5_46 https://github.com/file/file /mnt/file\n### Download libmagic dependencies\nRUN --mount=type=cache,target=/var/lib/apt/lists/ apt update && apt install -y libtool autoconf\n\n# -- DOWNLOADING + BUILDING STUFF\n\nENV VCPKG_DEFAULT_BINARY_CACHE /cache/vcpkg\nRUN --mount=type=cache,target=/cache <<EOF\n## Install dependencies with vcpkg\nset -xe\n\nmkdir -p $VCPKG_DEFAULT_BINARY_CACHE\n\nvcpkg install --triplet=arm-osx-mytriplet curl\nvcpkg install --triplet=arm-osx-mytriplet mbedtls\nvcpkg install --triplet=arm-osx-mytriplet freetype\nvcpkg install --triplet=arm-osx-mytriplet josuttis-jthread\nvcpkg install --triplet=arm-osx-mytriplet zlib\nvcpkg install --triplet=arm-osx-mytriplet bzip2\nvcpkg install --triplet=arm-osx-mytriplet liblzma\nvcpkg install --triplet=arm-osx-mytriplet zstd\nvcpkg install --triplet=arm-osx-mytriplet openssl\nvcpkg install --triplet=arm-osx-mytriplet libssh2\nEOF\n\n## Install glfw3 dep\nARG CUSTOM_GLFW\nRUN --mount=type=cache,target=/cache <<EOF\nset -xe\nif [ \"$CUSTOM_GLFW\" ]; then\n    echo \"Flag confirmation: using custom GLFW for software rendering\"\nelse\n    echo \"Flag confirmation: using system GLFW\"\n    vcpkg install --triplet=arm-osx-mytriplet glfw3\nfi\nEOF\n\n# -- BUILDING STUFF\nARG JOBS 1\nARG BUILD_TYPE Debug\n\n## Build libmagic\nRUN --mount=type=cache,target=/cache <<EOF\n    ccache -zs\n    set -xe\n    \n    cd /mnt/file\n    autoreconf -is\n\n    # when cross-compiling, libmagic needs to have an the same version installed in the system.\n    # So we install it normally first\n    ./configure --prefix /usr\n    make -j $JOBS install\n\n    # Now, we cross-compile it and install it in the libraries folder\n    CC=/osxcross/target/bin/aarch64-apple-darwin24-clang CXX=/osxcross/target/bin/aarch64-apple-darwin24-clang++ ./configure --prefix /vcpkg/installed/arm-osx-mytriplet --host $OSXCROSS_HOST\n    make -j $JOBS\n    make install\n\n    ccache -s\n    \nEOF\n\n## Patch glfw\nCOPY --from=imhex /dist/macOS/0001-glfw-SW.patch /tmp\nRUN <<EOF\nset -xe\nif [ \"$CUSTOM_GLFW\" ]; then\n    cd /mnt/glfw\n    git apply /tmp/0001-glfw-SW.patch\nfi\nEOF\n\nRUN mkdir -p /vcpkg/installed/arm-osx-mytriplet/lib/pkgconfig\nRUN mkdir -p /osxcross/target/macports/pkgs/vcpkg/installed/arm-osx-mytriplet/lib/pkgconfig\n\n## Build glfw\nRUN --mount=type=cache,target=/cache <<EOF\nset -xe\nif [ \"$CUSTOM_GLFW\" ]; then\n    ccache -zs\n\n    cd /mnt/glfw\n    mkdir build\n    cd build\n    CC=o64-clang CXX=o64-clang++ cmake -G \"Ninja\"       \\\n          -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0            \\\n          -DCMAKE_BUILD_TYPE=$BUILD_TYPE                \\\n          -DBUILD_SHARED_LIBS=ON                        \\\n          -DCMAKE_C_COMPILER_LAUNCHER=ccache            \\\n          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache          \\\n          -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache         \\\n          -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache       \\\n          -DCMAKE_INSTALL_PREFIX=/vcpkg/installed/arm-osx-mytriplet \\\n          -DVCPKG_TARGET_TRIPLET=arm-osx-mytriplet -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/osxcross/target/toolchain.cmake -DCMAKE_OSX_SYSROOT=/osxcross/target/SDK/MacOSX14.0.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=13.3 \\\n        ..\n    ninja -j $JOBS install\n\n    ccache -s\nfi\nEOF\n\n# Build ImHex\n## Copy ImHex\nCOPY --from=imhex / /mnt/ImHex\n## Configure ImHex build\nRUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/_deps \\\n    cd /mnt/ImHex && \\\n    # compilers\n    CC=o64-clang CXX=o64-clang++ OBJC=/osxcross/target/bin/aarch64-apple-darwin24-clang OBJCXX=/osxcross/target/bin/aarch64-apple-darwin24-clang++ \\\n        cmake -G \"Ninja\" \\\n        `# ccache flags` \\\n        -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \\\n        `# MacOS cross-compiling flags` \\\n        -DVCPKG_TARGET_TRIPLET=arm-osx-mytriplet -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/osxcross/target/toolchain.cmake -DCMAKE_OSX_SYSROOT=/osxcross/target/SDK/MacOSX14.0.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=13.3 \\\n        `# Override compilers for code generators` \\\n        -DNATIVE_CMAKE_C_COMPILER=/usr/bin/clang -DNATIVE_CMAKE_CXX_COMPILER=/usr/bin/clang++ \\\n        `# Normal ImHex flags` \\\n        -DIMHEX_GENERATE_PACKAGE=ON -DCMAKE_BUILD_TYPE=$BUILD_TYPE \\\n        `# other flags` \\\n        -DIMHEX_STRICT_WARNINGS=OFF \\\n        -DIMHEX_PATTERNS_PULL_MASTER=ON \\\n        -DCMAKE_INSTALL_PREFIX=/mnt/ImHex/build/install \\\n        -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0            \\\n        -B build\n## Build ImHex\nRUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/_deps <<EOF\n    ccache -zs\n    set -xe\n\n    cd /mnt/ImHex\n    cmake --build build --parallel $JOBS --target install\n\n    ccache -s\nEOF\n\n\nFROM scratch\nCOPY --from=build /mnt/ImHex/build/install/ImHex.app ImHex.app\n"
  },
  {
    "path": "dist/macOS/arm64.crosscompile.Dockerfile",
    "content": "# This image is provided for reference, but a (probably more up to date) image should be available at https://github.com/iTrooz/macos-crosscompile\nFROM ubuntu:22.04\n\nENV PATH                $PATH:/osxcross/target/bin\nENV LD_LIBRARY_PATH     /osxcross/target/lib\nENV OSXCROSS_SDK        /osxcross/target/SDK/MacOSX14.0.sdk\nENV OSXCROSS_TARGET     darwin24\nENV OSXCROSS_TARGET_DIR /osxcross/target\nENV OSXCROSS_HOST       aarch64-apple-darwin24\n\n# -- DOWNLOADING STUFF\n\n# Install common stuff\nRUN --mount=type=cache,target=/var/lib/apt/lists/ export DEBIAN_FRONTEND=noninteractive &&\\\n    export TZ=Etc/UTC &&\\\n    dpkg --add-architecture i386 &&\\\n    apt update &&\\\n    apt -y install lsb-release build-essential python3 python3-pip git wget zip unzip pkg-config curl ninja-build software-properties-common gnupg libssl-dev ccache\n\n# Install clang 17\nRUN --mount=type=cache,target=/var/lib/apt/lists/ wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && ./llvm.sh 17 &&\\\n    ln -s /usr/bin/clang-17 /usr/bin/clang &&\\\n    ln -s /usr/bin/clang++-17 /usr/bin/clang++\n\n# Install vcpkg\nRUN cd / &&\\\n    git clone --depth 1 https://github.com/Microsoft/vcpkg.git vcpkg &&\\\n    cd /vcpkg &&\\\n    ./bootstrap-vcpkg.sh -disableMetrics &&\\\n    ln -s /vcpkg/vcpkg /usr/bin/ &&\\\n    vcpkg install vcpkg-cmake &&\\\n    ln -s /vcpkg/downloads/tools/cmake-*/cmake-*/bin/cmake /usr/bin/\n\nRUN --mount=type=cache,target=/cache <<EOF\n## Clone osxcross\nset -xe\ngit clone https://github.com/tpoechtrager/osxcross /cache/osxcross || true\ncd /cache/osxcross\ngit pull\ncp -r /cache/osxcross /osxcross\nEOF\n\nRUN --mount=type=cache,target=/cache <<EOF\n## Download SDK\nset -xe\nwget https://github.com/joseluisq/macosx-sdks/releases/download/14.0/MacOSX14.0.sdk.tar.xz -O /cache/MacOSX14.0.sdk.tar.xz -nc || true\ncp /cache/MacOSX14.0.sdk.tar.xz /osxcross/tarballs\nEOF\n\n# Init stuff\n## setup ccache dir\nENV CCACHE_DIR /cache/ccache\n\n# Install triplet file\nCOPY arm-osx-mytriplet.cmake /vcpkg/triplets/community\n\n# -- BUILDING STUFF\nARG JOBS 1\n\n# Install osxcross\n## Build cross-compiler clang-17\nRUN --mount=type=cache,target=/cache <<EOF\n    set -xe\n    ccache -zs\n    \n    cd /osxcross\n    UNATTENDED=1 CC=/usr/lib/ccache/clang-17 CXX=/usr/lib/ccache/clang++-17 ./build.sh\n    \n    ccache -s\nEOF\n\nARG DELETE_SDK=1\nRUN <<EOF\n# Conditionally delete the SDK from the image\nset -xe\n \nif [ \"$DELETE_SDK\" ]; then\n    rm -r /osxcross/target/SDK\n    echo \"Deleted the SDK from the image\"\nelse\n    echo \"NOT deleting the SDK from the image\"\nfi\nEOF\n"
  },
  {
    "path": "dist/macOS/osx_10_15/x64-osx.cmake",
    "content": "set(VCPKG_TARGET_ARCHITECTURE x64)\nset(VCPKG_BUILD_TYPE release)\nset(VCPKG_CRT_LINKAGE dynamic)\nset(VCPKG_LIBRARY_LINKAGE static)\n\nset(VCPKG_CMAKE_SYSTEM_NAME Darwin)\nset(VCPKG_OSX_ARCHITECTURES x86_64)\nset(VCPKG_OSX_DEPLOYMENT_TARGET \"10.15\" CACHE STRING \"\" FORCE)\nset(VCPKG_C_FLAGS \"-mmacosx-version-min=10.15\")\nset(VCPKG_CXX_FLAGS \"-mmacosx-version-min=10.15\")\nset(ENV{MACOSX_DEPLOYMENT_TARGET} \"10.15\")"
  },
  {
    "path": "dist/msys2/PKGBUILD",
    "content": "_realname=ImHex\npkgbase=mingw-w64-${_realname}\npkgname=\"${MINGW_PACKAGE_PREFIX}-${_realname}\"\npkgver=ci\npkgrel=1\npkgdesc=\"${_realname}: a Hex Editor for Reverse Engineers, Programmers and people that value their eye sight when working at 3 AM (mingw-w64)\"\narch=('any')\nurl=\"https://github.com/WerWolv/ImHex\"\nlicense=('GPLv2')\nmakedepends=(\"${MINGW_PACKAGE_PREFIX}-gcc\"\n             \"${MINGW_PACKAGE_PREFIX}-lld\"\n             \"${MINGW_PACKAGE_PREFIX}-cmake\"\n             \"${MINGW_PACKAGE_PREFIX}-make\"\n             \"${MINGW_PACKAGE_PREFIX}-dlfcn\"\n             \"${MINGW_PACKAGE_PREFIX}-glfw\"\n             \"${MINGW_PACKAGE_PREFIX}-file\"\n             \"${MINGW_PACKAGE_PREFIX}-mbedtls\"\n             \"${MINGW_PACKAGE_PREFIX}-polly\"\n             \"${MINGW_PACKAGE_PREFIX}-freetype\"\n             \"${MINGW_PACKAGE_PREFIX}-zlib\"\n             \"${MINGW_PACKAGE_PREFIX}-bzip2\"\n             \"${MINGW_PACKAGE_PREFIX}-xz\"\n             \"${MINGW_PACKAGE_PREFIX}-zstd\"\n             \"${MINGW_PACKAGE_PREFIX}-libssh2-wincng\"\n             \"${MINGW_PACKAGE_PREFIX}-md4c\")\n\nsource=()\nsha256sums=()\n\nbuild() {\n  cd \"${srcdir}\"/../..\n  mkdir build\n  cd build\n  export BUILD_TYPE='Release'\n  MSYS2_ARG_CONV_EXCL=- cmake \\\n    -G \"MinGW Makefiles\" \\\n    -DCMAKE_INSTALL_PREFIX=\"${MINGW_PREFIX}\" \\\n    ../../\n  mingw32-make\n}\n\npackage() {\n  cd \"${srcdir}\"/../../build\n  mingw32-make DESTDIR=\"${pkgdir}\" install\n\n  _licenses=\"${pkgdir}${MINGW_PREFIX}/share/licenses/${_realname}\"\n  mkdir -p \"${_licenses}\"\n  install -m 644 ../LICENSE \"${_licenses}\"\n}\n"
  },
  {
    "path": "dist/net.werwolv.ImHex.metainfo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<component type=\"desktop\">\n    <id>net.werwolv.ImHex</id>\n    <name>ImHex</name>\n    <summary>Modern REing Hex Editor</summary>\n    <description>\n        <p>\n            ImHex is a modern Hex Editor for Reverse Engineers, Developers, Malware Analysists and Hackers who value\n            their retinas when working at 3 AM once again.\n        </p>\n        <p>\n            Features:\n        </p>\n        <ul>\n            <li>Featureful hex view</li>\n            <li>Custom C++-like pattern language for parsing and highlighting a file's content</li>\n            <li>Data importing and exporting</li>\n            <li>Data inspector allowing interpretation of data as many different types</li>\n            <li>Huge file support with fast and efficient loading</li>\n            <li>Strings search</li>\n            <li>File hashing support</li>\n            <li>Disassembler supporting many different architectures: ARM, x86, PowerPC, MIPS, and more</li>\n            <li>Bookmarks</li>\n            <li>Data analyzer</li>\n            <li>\n                Helpful tools such as an Itanium and MSVC demangler, ASCII table, Regex\n                replacer, mathematical expression evaluator (calculator), hexadecimal\n                color picker and many more\n            </li>\n            <li>Doesn't burn out your retinas when used in late-night sessions</li>\n        </ul>\n    </description>\n    <metadata_license>CC0-1.0</metadata_license>\n    <project_license>GPL-2.0-only</project_license>\n    <developer id=\"net.werwolv\">\n        <name>WerWolv</name>\n    </developer>\n    <url type=\"homepage\">https://imhex.werwolv.net</url>\n    <url type=\"bugtracker\">https://github.com/WerWolv/ImHex/issues</url>\n    <url type=\"help\">https://docs.werwolv.net/imhex</url>\n    <url type=\"donation\">https://github.com/sponsors/WerWolv</url>\n    <url type=\"contact\">https://imhex.werwolv.net/discord</url>\n    <url type=\"vcs-browser\">https://github.com/WerWolv/ImHex</url>\n    <url type=\"contribute\">https://github.com/WerWolv/ImHex/blob/master/CONTRIBUTING.md</url>\n    <launchable type=\"desktop-id\">imhex.desktop</launchable>\n    <screenshots>\n        <screenshot type=\"default\" width=\"1920\" height=\"1017\">\n            <image type=\"source\">https://raw.githubusercontent.com/flathub/net.werwolv.ImHex/master/screenshots/screenshot1.png</image>\n            <caption>Using a pattern to parse and highlight different sections of an ELF executable file</caption>\n        </screenshot>\n        <screenshot type=\"default\" width=\"1920\" height=\"1017\">\n            <image type=\"source\">https://raw.githubusercontent.com/flathub/net.werwolv.ImHex/master/screenshots/screenshot2.png</image>\n            <caption>Managing bookmarks, diffing two files and decrypting a region of data using the data preprocessor</caption>\n        </screenshot>\n    </screenshots>\n    <content_rating type=\"oars-1.1\"/>\n    <branding>\n        <color type=\"primary\" scheme_preference=\"light\">#babec9</color>\n        <color type=\"primary\" scheme_preference=\"dark\">#0f0f0f</color>\n    </branding>\n    <releases>\n        <release version=\"1.0.0\" date=\"2025-01-01\">\n            <description></description>\n        </release>\n    </releases>\n    <update_contact>hey@werwolv.net</update_contact>\n    <recommends>\n        <control>keyboard</control>\n        <control>pointing</control>\n    </recommends>\n</component>"
  },
  {
    "path": "dist/rpm/imhex.spec",
    "content": "%define source_date_epoch_from_changelog 0\n\nName:           imhex\nVersion:        VERSION\nRelease:        0%{?dist}\nSummary:        A hex editor for reverse engineers and programmers\n\nLicense:        GPL-2.0-only AND Zlib AND MIT AND Apache-2.0\n# imhex is gplv2.  capstone is custom.\n# see license dir for full breakdown\nURL:            https://imhex.werwolv.net/\n# We need the archive with deps bundled\nSource0:        https://github.com/WerWolv/%{name}/releases/download/v%{version}/Full.Sources.tar.gz#/%{name}-%{version}.tar.gz\n\nBuildRequires:  cmake\nBuildRequires:  desktop-file-utils\nBuildRequires:  dbus-devel\nBuildRequires:  file-devel\nBuildRequires:  freetype-devel\nBuildRequires:  fmt-devel\nBuildRequires:  gcc-c++\nBuildRequires:  libappstream-glib\nBuildRequires:  libglvnd-devel\nBuildRequires:  glfw-devel\nBuildRequires:  json-devel\nBuildRequires:  libcurl-devel\nBuildRequires:  libarchive-devel\nBuildRequires:  libzstd-devel\nBuildRequires:  zlib-devel\nBuildRequires:  bzip2-devel\nBuildRequires:  xz-devel\nBuildRequires:  llvm-devel\nBuildRequires:  mbedtls-devel\nBuildRequires:  yara-devel\nBuildRequires:  nativefiledialog-extended-devel\nBuildRequires:  lz4-devel\nBuildRequires:  libssh2-devel\n%if 0%{?rhel} == 9\nBuildRequires:  gcc-toolset-14\n%endif\n%if 0%{?fedora} || 0%{?rhel} > 9\nBuildRequires:  capstone-devel\n%endif\nBuildRequires:  lunasvg-devel\n\n\nProvides:       bundled(gnulib)\n%if 0%{?rhel} == 10\nProvides:       bundled(capstone) = 5.0.1\n%endif\nProvides:       bundled(imgui) = 1.90.8\nProvides:       bundled(libromfs)\nProvides:       bundled(microtar)\nProvides:       bundled(libpl) = %{version}\nProvides:       bundled(xdgpp)\n# working on packaging this, bundling for now as to now delay updates\nProvides:       bundled(miniaudio) = 0.11.11\n\n# [7:02 PM] WerWolv: We're not supporting 32 bit anyways soooo\n# [11:38 AM] WerWolv: Officially supported are x86_64 and aarch64\nExclusiveArch:  x86_64 %{arm64}\n\n%description\nImHex is a Hex Editor, a tool to display, decode and analyze binary data to\nreverse engineer their format, extract informations or patch values in them.\n\nWhat makes ImHex special is that it has many advanced features that can often\nonly be found in paid applications. Such features are a completely custom binary\ntemplate and pattern language to decode and highlight structures in the data, a\ngraphical node-based data processor to pre-process values before they're\ndisplayed, a disassembler, diffing support, bookmarks and much much more. At the\nsame time ImHex is completely free and open source under the GPLv2 language.\n\n\n%package devel\nSummary:        Development files for %{name}\nLicense:        GPL-2.0-only\n%description devel\n%{summary}\n\n\n%prep\n%autosetup -n ImHex -p1\n# remove bundled libs we aren't using\nrm -rf lib/third_party/{curl,fmt,llvm,nlohmann_json,yara}\n%if 0%{?fedora} || 0%{?rhel} > 9\nrm -rf lib/third_party/capstone\n%endif\n\n# rhel 9 doesn't support all of the new appstream metainfo tags\n%if 0%{?rhel} && 0%{?rhel} < 10\nsed -i -e '/url type=\"vcs-browser\"/d' \\\n\t-e '/url type=\"contribute\"/d' \\\n\tdist/net.werwolv.ImHex.metainfo.xml\n%endif\n\n%build\n%if 0%{?rhel} == 9\n. /opt/rh/gcc-toolset-14/enable\n%set_build_flags\nCXXFLAGS+=\" -std=gnu++2b\"\n%endif\n%cmake \\\n -D CMAKE_BUILD_TYPE=Release             \\\n -D IMHEX_STRIP_RELEASE=OFF              \\\n -D IMHEX_OFFLINE_BUILD=ON               \\\n -D USE_SYSTEM_NLOHMANN_JSON=ON          \\\n -D USE_SYSTEM_FMT=ON                    \\\n -D USE_SYSTEM_CURL=ON                   \\\n -D USE_SYSTEM_LLVM=ON                   \\\n -D USE_SYSTEM_MD4C=OFF                  \\\n%if 0%{?fedora} || 0%{?rhel} > 9\n -D USE_SYSTEM_CAPSTONE=ON               \\\n%endif\n -D USE_SYSTEM_LUNASVG=ON                \\\n -D USE_SYSTEM_YARA=ON                   \\\n -D USE_SYSTEM_NFD=ON                    \\\n -D IMHEX_ENABLE_UNIT_TESTS=ON           \\\n%if 0%{?rhel}\n -D IMHEX_BUILD_HARDENING=OFF\n%endif\n# disable built-in build hardening because it is already\n# done in rhel buildroots.  adding the flags again from\n# upstream generates build errors\n\n%cmake_build\n\n\n%check\n# build binaries required for tests\n%cmake_build --target unit_tests\n%ctest --exclude-regex '(Helpers/StoreAPI|Helpers/TipsAPI|Helpers/ContentAPI)'\n# Helpers/*API exclude tests that require network access\n\n\n%install\n%cmake_install\ndesktop-file-validate %{buildroot}%{_datadir}/applications/%{name}.desktop\n\n# this is a symlink for the old appdata name that we don't need\nrm -f %{buildroot}%{_metainfodir}/net.werwolv.ImHex.appdata.xml\n\n# AppData\nappstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/net.werwolv.ImHex.metainfo.xml\n\n# install licenses\n%if 0%{?rhel} == 9\ncp -a lib/third_party/capstone/LICENSES/LICENSE.TXT                  %{buildroot}%{_datadir}/licenses/%{name}/capstone-LICENSE\n%endif\ncp -a lib/third_party/microtar/LICENSE                               %{buildroot}%{_datadir}/licenses/%{name}/microtar-LICENSE\ncp -a lib/third_party/xdgpp/LICENSE                                  %{buildroot}%{_datadir}/licenses/%{name}/xdgpp-LICENSE\n\n\n%files\n%license %{_datadir}/licenses/%{name}/\n%doc README.md\n%{_bindir}/imhex\n%{_datadir}/imhex/imhex\n%{_datadir}/pixmaps/%{name}.*\n%{_datadir}/applications/%{name}.desktop\n%{_libdir}/libimhex.so.*\n%{_libdir}/%{name}/\n%{_metainfodir}/net.werwolv.ImHex.metainfo.xml\n%exclude %{_bindir}/imhex-updater\n%{_datadir}/mime/packages/%{name}.xml\n\n\n%files devel\n%{_libdir}/libimhex.so\n%{_datadir}/%{name}/sdk/\n"
  },
  {
    "path": "dist/snap/snapcraft.yaml",
    "content": "name: imhex\ntitle: ImHex\nbase: core24\nversion: ${IMHEX_VERSION_STRING}\nsummary: Hex editor for reverse engineering\ndescription: ImHex is a hex editor for reverse engineering, reverse engineering, and analyzing binary files. It provides a powerful and flexible interface for working with binary data, including features like pattern matching, scripting, and a customizable user interface.\ngrade: stable\nconfinement: classic\ncontact: https://github.com/WerWolv/ImHex/discussions\nissues: https://github.com/WerWolv/ImHex/issues\nsource-code: https://github.com/WerWolv/ImHex\nwebsite: https://imhex.werwolv.net\ndonation: https://github.com/sponsors/WerWolv\nlicense: GPL-2.0-only\nicon: resources/icon.svg\nadopt-info: imhex\n\nplatforms:\n  amd64:\n  arm64:\n\napps:\n  imhex:\n    command: usr/local/bin/imhex\n    desktop: usr/local/share/applications/imhex.desktop\n    environment:\n      LD_LIBRARY_PATH: '$SNAP/usr/local/lib:$SNAP/usr/local/lib/imhex:$SNAP/usr/lib/x86_64-linux-gnu:$SNAP/usr/lib/aarch64-linux-gnu:$LD_LIBRARY_PATH'\n      XDG_DATA_DIRS: '$XDG_DATA_DIRS:$SNAP/usr/local/share:$SNAP/usr/local/lib:$SNAP/usr/local/share'\n      XDG_CONFIG_DIRS: '$XDG_CONFIG_DIRS:$SNAP/usr/local/share'\n      XDG_DATA_HOME: '$XDG_DATA_HOME:$SNAP_DATA'\n\nparts:\n  imhex:\n    plugin: cmake\n    source: .\n    build-environment:\n      - CC: /usr/bin/gcc-14\n      - CXX: /usr/bin/g++-14\n    cmake-parameters:\n      - -DCMAKE_BUILD_TYPE=Release\n      - -DCMAKE_C_COMPILER_LAUNCHER=${CCACHE}\n      - -DCMAKE_CXX_COMPILER_LAUNCHER=${CCACHE}\n      - -DIMHEX_PATTERNS_PULL_MASTER=ON\n      - -DIMHEX_BUNDLE_PLUGIN_SDK=OFF\n    cmake-generator: Ninja\n    build-packages:\n      - cmake\n      - ninja-build\n      - gcc-14\n      - g++-14\n      - git\n      - pkg-config\n      - libglfw3-dev\n      - libmagic-dev\n      - libmbedtls-dev\n      - libfontconfig-dev\n      - libfreetype-dev\n      - libdbus-1-dev\n      - libcurl4-gnutls-dev\n      - libgtk-3-dev\n      - zlib1g-dev\n      - libbz2-dev\n      - liblzma-dev\n      - libzstd-dev\n      - liblz4-dev\n      - libssh2-1-dev\n      - libmd4c-dev\n      - libmd4c-html0-dev\n    stage-packages:\n      - libglfw3\n      - libmagic1\n      - libmbedtls14\n      - libfontconfig1\n      - libfreetype6\n      - libdbus-1-3\n      - libcurl4-gnutls-dev\n      - libgtk-3-0\n      - zlib1g\n      - libbz2-1.0\n      - liblzma5\n      - libzstd1\n      - liblz4-1\n      - libssh2-1\n    prime:\n      - -usr/include/*\n      - -usr/local/include/*\n      - -usr/lib/**/*.a\n      - -usr/local/lib/**/*.a\n      - -usr/lib/**/*.la\n      - -usr/local/lib/**/*.la\n      - -usr/share/doc/*\n      - -usr/share/man/*\n"
  },
  {
    "path": "dist/web/Dockerfile",
    "content": "FROM emscripten/emsdk:4.0.21 AS build\n\n# Used to invalidate layer cache but not mount cache\n# See https://github.com/moby/moby/issues/41715#issuecomment-733976493\nARG UNIQUEKEY=1\n\nRUN apt update\nRUN apt install -y git ccache autoconf automake libtool pkg-config ninja-build\n\nRUN <<EOF\n# Install vcpkg\n# Note: we are a patch on the libmagic port\nset -xe\n\ngit clone --depth 1 https://github.com/microsoft/vcpkg /vcpkg\n/vcpkg/bootstrap-vcpkg.sh\nsed -i 's/vcpkg_install_make(${EXTRA_ARGS})/vcpkg_install_make(${EXTRA_ARGS} SUBPATH src)/g' /vcpkg/ports/libmagic/portfile.cmake\nEOF\n\n# Patch vcpkg build instructions to add -pthread flag\n# Even dependencies must be built with -pthread to be able to use USE_PTHREADS=1\nRUN <<EOF\nset -xe\n\necho '\nset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -pthread\")\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -pthread\")\n' >> /emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake\nEOF\n\nENV VCPKG_DEFAULT_BINARY_CACHE=/cache/vcpkg\nRUN --mount=type=cache,target=/cache <<EOF\n# Install dependencies with vcpkg\nset -xe\n\nmkdir -p $VCPKG_DEFAULT_BINARY_CACHE\n\n/vcpkg/vcpkg install --triplet=wasm32-emscripten libmagic\n/vcpkg/vcpkg install --triplet=wasm32-emscripten freetype\n/vcpkg/vcpkg install --triplet=wasm32-emscripten mbedtls\n/vcpkg/vcpkg install --triplet=wasm32-emscripten zlib\n/vcpkg/vcpkg install --triplet=wasm32-emscripten bzip2\n/vcpkg/vcpkg install --triplet=wasm32-emscripten liblzma\n/vcpkg/vcpkg install --triplet=wasm32-emscripten zstd\nEOF\n\n# Build ImHex\nARG JOBS=4\nENV CCACHE_DIR=/cache/ccache\n\nRUN mkdir /build\nWORKDIR /build\nARG BUILD_TYPE=Release\nRUN --mount=type=cache,target=/cache \\\n    --mount=type=bind,source=.,target=/imhex <<EOF\n\nset -xe\nccache -zs\n\n/vcpkg/downloads/tools/cmake-*/cmake-*/bin/cmake /imhex                                                                                            \\\n    -G \"Ninja\"                                                                                          \\\n    -DIMHEX_OFFLINE_BUILD=ON                                                                            \\\n    -DIMHEX_STATIC_LINK_PLUGINS=ON                                                                      \\\n    -DIMHEX_EXCLUDE_PLUGINS=\"script_loader;remote\"                                                      \\\n    -DIMHEX_COMPRESS_DEBUG_INFO=OFF                                                                     \\\n    -DNATIVE_CMAKE_C_COMPILER=gcc                                                                       \\\n    -DNATIVE_CMAKE_CXX_COMPILER=g++                                                                     \\\n    -DCMAKE_C_COMPILER_LAUNCHER=ccache                                                                  \\\n    -DCMAKE_CXX_COMPILER_LAUNCHER=ccache                                                                \\\n    -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake                                      \\\n    -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake \\\n    -DLIBROMFS_COMPRESS_RESOURCES=OFF                                                                   \\\n    -DIMHEX_ENABLE_PLUGIN_TESTS=OFF                                                                     \\\n    -DCMAKE_BUILD_TYPE=${BUILD_TYPE}\n\nninja -j $JOBS\n\ncp /imhex/dist/web/source/* /build\nccache -s\nEOF\n\n# Create a file dedicated to store wasm size, because I know no way to get the wasm content length if the web server uses compression\n# See https://stackoverflow.com/questions/41701849/cannot-modify-accept-encoding-with-fetch https://github.com/AnthumChris/fetch-progress-indicators/issues/13\nRUN du -b /build/imhex.wasm | cut -f1 > imhex.wasm.size\n\nFROM scratch AS raw\nCOPY --from=build [             \\\n    # ImHex                     \\\n    \"/build/imhex.wasm\",        \\\n    \"/build/imhex.wasm.size\",   \\\n    \"/build/imhex.js\",          \\\n                                \\\n    # Static files              \\\n    \"/build/index.html\",        \\\n    \"/build/style.css\",         \\\n    \"/build/wasm-config.js\",    \\\n    \"/build/enable-threads.js\", \\\n    \"/build/favicon.ico\",       \\\n    \"/build/icon.svg\",          \\\n    \"/build/manifest.json\",     \\\n    \"/build/robots.txt\",        \\\n    \"/build/sitemap.xml\",       \\\n                                \\\n    # Destination               \\\n    \"./\"                        \\\n]\n\nFROM nginx\nCOPY --from=raw . /usr/share/nginx/html\nRUN chmod -R 755 /usr/share/nginx/html\n"
  },
  {
    "path": "dist/web/Host.Dockerfile",
    "content": "FROM python:3.12-slim\n\nWORKDIR /imhex\n\nCOPY ./out/ .\n\nEXPOSE 9090\n\nCMD [ \"python\", \"/imhex/start_imhex_web.py\" ]"
  },
  {
    "path": "dist/web/compose.yml",
    "content": "# docker compose -f dist/web/compose.yml up --build\nservices:\n  imhex_web:\n    image: imhex_web:latest\n    build:\n      context: ../../ # ImHex folder\n      dockerfile: ./dist/web/Dockerfile\n    ports:\n      - 8080:80\n"
  },
  {
    "path": "dist/web/plugin-bundle.cpp.in",
    "content": "#include <hex/helpers/logger.hpp>\n\nextern \"C\" void forceLinkPlugin_@IMHEX_PLUGIN_NAME@();\n\nnamespace {\nstruct StaticLoad {\n    StaticLoad() {\n        forceLinkPlugin_@IMHEX_PLUGIN_NAME@();\n    }\n};\n}\n\nstatic StaticLoad staticLoad;\n"
  },
  {
    "path": "dist/web/serve.py",
    "content": "import http.server\nimport os\n\nclass MyHttpRequestHandler(http.server.SimpleHTTPRequestHandler):\n\n    def end_headers(self):\n        self.send_header(\"Cross-Origin-Embedder-Policy\", \"require-corp\")\n        self.send_header(\"Cross-Origin-Opener-Policy\", \"same-origin\")\n        http.server.SimpleHTTPRequestHandler.end_headers(self)\n\nif __name__ == '__main__':\n    os.chdir(\".\")\n    httpd = http.server.HTTPServer((\"0.0.0.0\", 9090), MyHttpRequestHandler)\n    print(f\"Serving {os.getcwd()} on http://{httpd.server_address[0]}:{httpd.server_address[1]}\")\n    httpd.serve_forever()\n"
  },
  {
    "path": "dist/web/source/enable-threads.js",
    "content": "// NOTE: This file creates a service worker that cross-origin-isolates the page (read more here: https://web.dev/coop-coep/) which allows us to use wasm threads.\n// Normally you would set the COOP and COEP headers on the server to do this, but Github Pages doesn't allow this, so this is a hack to do that.\n\n/* Edited version of: coi-serviceworker v0.1.6 - Guido Zuidhof, licensed under MIT */\n// From here: https://github.com/gzuidhof/coi-serviceworker\nif(typeof window === 'undefined') {\n    self.addEventListener(\"install\", () => self.skipWaiting());\n    self.addEventListener(\"activate\", e => e.waitUntil(self.clients.claim()));\n\n    async function handleFetch(request) {\n        if(request.cache === \"only-if-cached\" && request.mode !== \"same-origin\") {\n            return;\n        }\n\n        if(request.mode === \"no-cors\") { // We need to set `credentials` to \"omit\" for no-cors requests, per this comment: https://bugs.chromium.org/p/chromium/issues/detail?id=1309901#c7\n            request = new Request(request.url, {\n                cache: request.cache,\n                credentials: \"omit\",\n                headers: request.headers,\n                integrity: request.integrity,\n                destination: request.destination,\n                keepalive: request.keepalive,\n                method: request.method,\n                mode: request.mode,\n                redirect: request.redirect,\n                referrer: request.referrer,\n                referrerPolicy: request.referrerPolicy,\n                signal: request.signal,\n            });\n        }\n\n        let r = await fetch(request).catch(e => console.error(e));\n\n        if(r.status === 0) {\n            return r;\n        }\n\n        const headers = new Headers(r.headers);\n        headers.set(\"Cross-Origin-Embedder-Policy\", \"require-corp\"); // or: require-corp\n        headers.set(\"Cross-Origin-Opener-Policy\", \"same-origin\");\n\n        return new Response(r.body, { status: r.status, statusText: r.statusText, headers });\n    }\n\n    self.addEventListener(\"fetch\", function(e) {\n        e.respondWith(handleFetch(e.request)); // respondWith must be executed synchonously (but can be passed a Promise)\n    });\n\n} else {\n    (async function() {\n        if(window.crossOriginIsolated !== false) return;\n\n        if (!('serviceWorker' in navigator)) {\n            alert(\"Your browser doesn't support service workers.\\nIf you're using Firefox, you need to not be in a private window.\")\n        }\n\n        let registration = await navigator.serviceWorker.register(window.document.currentScript.src).catch(e => console.error(\"COOP/COEP Service Worker failed to register:\", e));\n        if(registration) {\n            console.log(\"COOP/COEP Service Worker registered\", registration.scope);\n\n            registration.addEventListener(\"updatefound\", () => {\n                console.log(\"Reloading page to make use of updated COOP/COEP Service Worker.\");\n                window.location.reload();\n            });\n\n            // If the registration is active, but it's not controlling the page\n            if(registration.active && !navigator.serviceWorker.controller) {\n                console.log(\"Reloading page to make use of COOP/COEP Service Worker.\");\n                window.location.reload();\n            }\n        }\n    })();\n}\n\n// Code to deregister:\n// let registrations = await navigator.serviceWorker.getRegistrations();\n// for(let registration of registrations) {\n//   await registration.unregister();\n// }"
  },
  {
    "path": "dist/web/source/index.html",
    "content": "<!doctype html>\n<html lang=\"en-us\">\n<head>\n    <title>ImHex Web - Free Online Hex Editor for Reverse Engineers</title>\n\n    <link rel=\"manifest\" href=\"manifest.json\">\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n    <!-- Primary Meta Tags -->\n    <meta name=\"title\" content=\"ImHex\">\n    <meta name=\"description\" content=\"Free and extremely powerful Online Hex Editor for your Web Browser. ImHex is a free and open source Hex Editor for Reverse Engineers and Developers and Data Analysts.\">\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\">\n    <link rel=\"apple-touch-icon\" href=\"icon.svg\">\n\n    <!-- Open Graph / Facebook -->\n    <meta property=\"og:type\" content=\"website\">\n    <meta property=\"og:url\" content=\"https://web.imhex.werwolv.net/\">\n    <meta property=\"og:title\" content=\"ImHex Web - Online Hex Editor\">\n    <meta property=\"og:image\" content=\"splash_wasm.png\">\n\n    <!-- Twitter -->\n    <meta property=\"twitter:card\" content=\"summary_large_image\">\n    <meta property=\"twitter:url\" content=\"https://web.imhex.werwolv.net/\">\n    <meta property=\"twitter:title\" content=\"ImHex Web - Online Hex Editor\">\n    <meta property=\"twitter:description\"\n          content=\"A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.\">\n    <meta property=\"twitter:image\" content=\"splash_wasm.png\">\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">\n\n    <script type=\"application/ld+json\">\n        {\n            \"@context\": \"https://schema.org\",\n            \"@type\": \"Organization\",\n            \"alumni\": \"WerWolv\",\n            \"email\": \"hey@werwolv.net\",\n            \"founder\": \"WerWolv\",\n            \"slogan\": \"A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.\",\n            \"url\": \"https://web.imhex.werwolv.net\",\n            \"logo\": \"https://web.imhex.werwolv.net/icon.svg\"\n        }\n    </script>\n\n    <script type=\"application/ld+json\">\n        {\n            \"@context\": \"https://schema.org\",\n            \"@type\": \"SoftwareApplication\",\n            \"name\": \"ImHex Web\",\n            \"operatingSystem\": \"Windows, MacOS, Linux\",\n            \"applicationCategory\": \"DeveloperApplication\",\n            \"offers\": {\n                \"@type\": \"Offer\",\n                \"price\": \"0\",\n                \"priceCurrency\": \"USD\"\n            }\n        }\n    </script>\n\n    <script src=\"enable-threads.js\"></script>\n    <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n<body>\n<div id=\"loading\" class=\"centered\">\n    <img src=\"https://raw.githubusercontent.com/WerWolv/ImHex/master/plugins/builtin/romfs/assets/dark/banner.svg\" id=\"logo\" alt=\"ImHex Logo\">\n    <h1>A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.</h1>\n    <h2>Available both natively and on the web</h2>\n    <h5>ImHex runs directly in your web browser with the help of Emscripten and WebAssembly.</h5>\n\n    <div style=\"height: 50%\">\n        <div style=\"height: 30%\"> </div>\n        <h2 id=\"not_working\">\n            Not loading in your Browser? <a href=\"https://imhex.werwolv.net\">Try the native version</a>\n        </h2>\n        <div class=\"progress-bar-container\">\n            <div class=\"progress progress-moved\">\n                <div class=\"progress-bar\" id=\"progress-bar-content\">\n                </div>\n            </div>\n        </div>\n    </div>\n\n\n\n    <div class=\"loading_ripple\">\n        <div class=\"lds-ripple\"><div></div><div></div></div>\n    </div>\n\n    <div style=\"height: 10%\">\n    </div>\n\n    <div class=\"footer\">\n        <a href=\"https://imhex.werwolv.net\">Homepage</a>\n        <p>Made with ♥️ by the ImHex Team</p>\n        <a href=\"https://github.com/WerWolv/ImHex\">GitHub</a>\n    </div>\n</div>\n\n<div id=\"canvas-wrapper\" class=\"imhex-web-canvas-wrapper\">\n    <canvas class=\"imhex-web-canvas canvas-fixed\" id=\"canvas\" ></canvas>\n</div>\n\n<script src=\"wasm-config.js\"></script>\n<script async src=\"imhex.js\"></script>\n</body>\n</html>"
  },
  {
    "path": "dist/web/source/manifest.json",
    "content": "{\n    \"name\": \"ImHex\",\n    \"description\": \"🔍 A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.\",\n    \"background_color\": \"#0F0F0F\",\n    \"theme_color\": \"#0F0F0F\",\n    \"categories\": [\n        \"education\",\n        \"productivity\",\n        \"utilities\"\n    ],\n    \"icons\": [\n        {\n            \"src\": \"icon.svg\",\n            \"type\": \"image/svg\",\n            \"sizes\": \"640x640\"\n        }\n    ],\n    \"start_url\": \".\",\n    \"display\": \"standalone\"\n}\n"
  },
  {
    "path": "dist/web/source/robots.txt",
    "content": "User-agent: *\nAllow: /\n\nSitemap: https://imhex.werwolv.net/sitemap.xml"
  },
  {
    "path": "dist/web/source/sitemap.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset\n      xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\n      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n      xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9\n            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\">\n\n  <url>\n    <loc>https://web.imhex.werwolv.net/</loc>\n    <lastmod>2024-01-02T11:44:00+00:00</lastmod>\n    <priority>1.00</priority>\n  </url>\n  <url>\n    <title>English</title>\n    <loc>https://web.imhex.werwolv.net?lang=en-US</loc>\n    <lastmod>2024-01-02T11:44:00+00:00</lastmod>\n    <priority>1.00</priority>\n  </url>\n  <url>\n    <title>Deutsch</title>\n    <loc>https://web.imhex.werwolv.net?lang=de-DE</loc>\n    <lastmod>2024-01-02T11:44:00+00:00</lastmod>\n    <priority>1.00</priority>\n  </url>\n  <url>\n    <title>Português</title>\n    <loc>https://web.imhex.werwolv.net?lang=pt-BR</loc>\n    <lastmod>2024-01-02T11:44:00+00:00</lastmod>\n  </url>\n  <url>\n    <title>中国</title>\n    <loc>https://web.imhex.werwolv.net?lang=zh-CN</loc>\n    <lastmod>2024-01-02T11:44:00+00:00</lastmod>\n  </url>\n  <url>\n    <title>國語</title>\n    <loc>https://web.imhex.werwolv.net?lang=zh-TW</loc>\n    <lastmod>2024-01-02T11:44:00+00:00</lastmod>\n  </url>\n  <url>\n    <title>日本語</title>\n    <loc>https://web.imhex.werwolv.net?lang=ja-JP</loc>\n    <lastmod>2024-01-02T11:44:00+00:00</lastmod>\n  </url>\n  <url>\n    <title>한국어</title>\n    <loc>https://web.imhex.werwolv.net?lang=ko-KR</loc>\n    <lastmod>2024-01-02T11:44:00+00:00</lastmod>\n  </url>\n  <url>\n    <title>Español</title>\n    <loc>https://web.imhex.werwolv.net?lang=es-ES</loc>\n    <lastmod>2024-01-02T11:44:00+00:00</lastmod>\n  </url>\n  <url>\n    <title>Italiano</title>\n    <loc>https://web.imhex.werwolv.net?lang=it-IT</loc>\n    <lastmod>2024-01-02T11:44:00+00:00</lastmod>\n  </url>\n  <url>\n    <title>Русский</title>\n    <loc>https://web.imhex.werwolv.net?lang=ru-RU</loc>\n    <lastmod>2024-01-02T11:44:00+00:00</lastmod>\n  </url>\n\n\n</urlset>"
  },
  {
    "path": "dist/web/source/style.css",
    "content": "html, body {\n    height: 100%;\n    margin: 0;\n    user-select: none;\n}\n\nbody {\n    display: flex;\n    align-items: center;\n    background-color: #121212;\n    overflow: hidden;\n}\n\n.emscripten {\n    padding-right: 0;\n    margin-left: auto;\n    margin-right: auto;\n    display: none;\n    border: 0 none;\n    image-rendering: smooth;\n}\n\nh1, h2, h5 {\n    color: #F0F0F0;\n    font-size: 20px;\n    font-family: monospace;\n    width: 100%;\n    text-align: center;\n    margin-top: 60px;\n    margin-bottom: 10px;\n}\n\nh2 {\n    margin-top: 15px;\n    font-size: 17px;\n}\n\nh5 {\n    margin-top: 0;\n    font-size: 17px;\n}\n\n#not_working {\n    opacity: 0;\n}\n\n#not_working.visible {\n    opacity: 1;\n    transition: opacity 2s ease;\n}\n\na {\n    color: #7893ff;\n    text-decoration: none;\n}\n\na:hover {\n    text-shadow: #3a4677 0 0 10px;\n}\n\n.footer {\n    width: 100%;\n    height: 20px;\n    position: absolute;\n    bottom: 0;\n    text-align: center;\n    color: #F0F0F0;\n    background-color: #0A0A0A;\n    padding: 10px;\n    font-family: monospace;\n    font-size: 15px;\n\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    flex-direction: row;\n    gap: 10%;\n}\n\n.centered {\n    display: flex;\n    flex-direction: column;\n    justify-content: center;\n    align-items: center;\n\n    width: 100%;\n    height: 100%;\n}\n\n\n.lds-ripple {\n    display: inline-block;\n    position: relative;\n    width: 80px;\n    height: 80px;\n}\n.lds-ripple div {\n    position: absolute;\n    border: 4px solid #fff;\n    opacity: 1;\n    border-radius: 50%;\n    animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;\n}\n.lds-ripple div:nth-child(2) {\n    animation-delay: -0.5s;\n}\n@keyframes lds-ripple {\n    0% {\n        top: 36px;\n        left: 36px;\n        width: 0;\n        height: 0;\n        opacity: 0;\n    }\n    4.9% {\n        top: 36px;\n        left: 36px;\n        width: 0;\n        height: 0;\n        opacity: 0;\n    }\n    5% {\n        top: 36px;\n        left: 36px;\n        width: 0;\n        height: 0;\n        opacity: 1;\n    }\n    100% {\n        top: 0;\n        left: 0;\n        width: 72px;\n        height: 72px;\n        opacity: 0;\n    }\n}\n\n\n:root {\n    --progress: 0%;\n}\n\n.progress-bar-container {\n    margin: 100px auto;\n    width: 600px;\n    text-align: center;\n}\n\n.progress {\n    padding: 6px;\n    border-radius: 30px;\n    background: rgba(0, 0, 0, 0.25);\n    box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.25),\n    0 1px rgba(255, 255, 255, 0.08);\n}\n\n.progress-bar {\n    color: rgba(240, 240, 240, 0.9);\n    height: 18px;\n    border-radius: 30px;\n    font-size: 13px;\n    font-family: monospace;\n    font-weight: bold;\n    text-wrap: avoid;\n    white-space: nowrap;\n    overflow: hidden;\n    background-image: linear-gradient(\n            to bottom,\n            rgba(255, 255, 255, 0.2),\n            rgba(255, 255, 255, 0.0)\n    );\n}\n\n.progress-moved .progress-bar {\n    width: var(--progress);\n    background-color: #3864cb;\n}\n\n#logo {\n    height: 25%;\n    margin-top: 50px;\n}\n\n.canvas-fixed {\n    position: absolute;\n    top: 0;\n    left: 0;\n}\n\n.imhex-web-canvas {\n    width: 100%;\n    height: 100%;\n    display: block;\n    overflow: hidden;\n    image-rendering: smooth;\n    margin: 0;\n    padding: 0;\n    z-index: 1;\n}\n\n.imhex-web-canvas-wrapper {\n    position: relative;\n    width: 100%;\n    height: 100%;\n    overflow: hidden;\n    background-size: 100% 100%;\n}"
  },
  {
    "path": "dist/web/source/wasm-config.js",
    "content": "let wasmSize = null;\n// See comment in dist/web/Dockerfile about imhex.wasm.size\nfetch(\"imhex.wasm.size\").then(async (resp) => {\n    wasmSize = parseInt((await resp.text()).trim());\n    console.log(`Real WASM binary size is ${wasmSize} bytes`);\n});\n\n// Monkeypatch WebAssembly to have a progress bar\n// inspired from: https://github.com/WordPress/wordpress-playground/pull/46 (but had to be modified)\nfunction monkeyPatch(progressFun) {\n    const _instantiateStreaming = WebAssembly.instantiateStreaming;\n    WebAssembly.instantiateStreaming = async (responsePromise, ...args) => {\n        // Do not collect wasm content length here see above\n        let response = await responsePromise\n        const file = response.url.substring(\n            new URL(response.url).origin.length + 1\n        );\n        const reportingResponse = new Response(\n            new ReadableStream(\n                {\n                    async start(controller) {\n                        const reader = response.clone().body.getReader();\n                        let loaded = 0;\n                        for (; ;) {\n                            const { done, value } = await reader.read();\n                            if (done) {\n                                if(wasmSize) progressFun(file, wasmSize);\n                                break;\n                            }\n                            loaded += value.byteLength;\n                            progressFun(file, loaded);\n                            controller.enqueue(value);\n                        }\n                        controller.close();\n                    }\n                },\n                {\n                    status: response.status,\n                    statusText: response.statusText\n                }\n            )\n        );\n        for (const pair of response.headers.entries()) {\n            reportingResponse.headers.set(pair[0], pair[1]);\n        }\n\n        return _instantiateStreaming(reportingResponse, ...args);\n    }\n}\nmonkeyPatch((file, done) =>  {\n    if (!wasmSize) return;\n    if (done > wasmSize) {\n        console.warn(`Downloaded binary size ${done} is larger than expected WASM size ${wasmSize}`);\n        return;\n    }\n\n    const percent  = ((done / wasmSize) * 100).toFixed(0);\n    const mibNow   = (done / 1024**2).toFixed(1);\n    const mibTotal = (wasmSize / 1024**2).toFixed(1);\n\n    let root = document.querySelector(':root');\n    if (root != null) {\n        root.style.setProperty(\"--progress\", `${percent}%`)\n        let progressBar = document.getElementById(\"progress-bar-content\");\n\n        if (progressBar != null) {\n            progressBar.innerHTML = `${percent}% &nbsp;[${mibNow} MiB / ${mibTotal} MiB]`;\n        }\n    }\n});\n\nfunction glfwSetCursorCustom(wnd, shape) {\n    let body = document.getElementsByTagName(\"body\")[0]\n    switch (shape) {\n        case 0x00036001: // GLFW_ARROW_CURSOR\n            body.style.cursor = \"default\";\n            break;\n        case 0x00036002: // GLFW_IBEAM_CURSOR\n            body.style.cursor = \"text\";\n            break;\n        case 0x00036003: // GLFW_CROSSHAIR_CURSOR\n            body.style.cursor = \"crosshair\";\n            break;\n        case 0x00036004: // GLFW_HAND_CURSOR\n            body.style.cursor = \"pointer\";\n            break;\n        case 0x00036005: // GLFW_HRESIZE_CURSOR\n            body.style.cursor = \"ew-resize\";\n            break;\n        case 0x00036006: // GLFW_VRESIZE_CURSOR\n            body.style.cursor = \"ns-resize\";\n            break;\n        default:\n            body.style.cursor = \"default\";\n            break;\n    }\n\n}\n\nfunction glfwCreateStandardCursorCustom(shape) {\n    return shape\n}\n\nvar notWorkingTimer = setTimeout(() => {\n    document.getElementById(\"not_working\").classList.add(\"visible\")\n}, 5000);\n\nvar Module = {\n    preRun:  () => {\n        ENV.IMHEX_SKIP_SPLASH_SCREEN = \"1\";\n    },\n    postRun: function() {\n\n    },\n    onRuntimeInitialized: function() {\n        // Triggered when the wasm module is loaded and ready to use.\n        let loading = document.getElementById(\"loading\");\n        if (loading != null)\n            document.getElementById(\"loading\").style.display = \"none\"\n        document.getElementById(\"canvas\").style.display = \"initial\"\n\n        clearTimeout(notWorkingTimer);\n    },\n    print:   (function() { })(),\n    printErr: function(text) {  },\n    canvas: (function() {\n        const canvas = document.getElementById('canvas');\n        canvas.addEventListener(\"webglcontextlost\", function(e) {\n            alert('WebGL context lost, please reload the page');\n            e.preventDefault();\n        }, false);\n\n        // Turn long touches into right-clicks\n        let timer = null;\n        canvas.addEventListener('touchstart', event => {\n            timer = setTimeout(() => {\n                let eventArgs = {\n                    bubbles: true,\n                    cancelable: true,\n                    view: window,\n                    screenX: event.touches[0].screenX,\n                    screenY: event.touches[0].screenY,\n                    clientX: event.touches[0].clientX,\n                    clientY: event.touches[0].clientY,\n                    button: 2,\n                    buttons: 2,\n                    relatedTarget: event.target,\n                    region: event.region\n                }\n\n                canvas.dispatchEvent(new MouseEvent('mousedown', eventArgs));\n                canvas.dispatchEvent(new MouseEvent('mouseup', eventArgs));\n            }, 400);\n        });\n\n        canvas.addEventListener('touchend', event => {\n            if (timer) {\n                clearTimeout(timer);\n                timer = null;\n            }\n        });\n\n        if (typeof WebGL2RenderingContext !== 'undefined') {\n            let gl = canvas.getContext('webgl2', { stencil: true });\n            if (!gl) {\n                console.error('WebGL 2 not available, falling back to WebGL');\n                gl = canvas.getContext('webgl', { stencil: true });\n            }\n            if (!gl) {\n                alert('WebGL not available with stencil buffer');\n            }\n            return canvas;\n        } else {\n            alert('WebGL 2 not supported by this browser');\n        }\n    })(),\n    setStatus: function(text) { },\n    totalDependencies: 0,\n    monitorRunDependencies: function(left) {\n    },\n    instantiateWasm: async function(imports, successCallback) {\n        imports.env.glfwSetCursor = glfwSetCursorCustom\n        imports.env.glfwCreateStandardCursor = glfwCreateStandardCursorCustom\n        let result = await instantiateAsync(null, findWasmBinary(), imports);\n        successCallback(result.instance, result.module)\n    },\n    arguments: []\n};\n\n// Handle passing arguments to the wasm module\nconst queryString = window.location.search;\nconst urlParams = new URLSearchParams(queryString);\nif (urlParams.has(\"lang\")) {\n    Module[\"arguments\"].push(\"--language\");\n    Module[\"arguments\"].push(urlParams.get(\"lang\"));\n} else if (urlParams.has(\"save-editor\")) {\n    Module[\"arguments\"].push(\"--save-editor\");\n    Module[\"arguments\"].push(\"gist\");\n    Module[\"arguments\"].push(urlParams.get(\"save-editor\"));\n}\n\n// Prevent some default browser shortcuts from preventing ImHex ones to work\ndocument.addEventListener('keydown', e => {\n    if (e.ctrlKey) {\n        if (e.which == 83) e.preventDefault();\n    }\n})"
  },
  {
    "path": "lib/libimhex/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\nproject(libimhex)\n\nset(CMAKE_CXX_STANDARD 23)\n\nset(LIBIMHEX_SOURCES\n        source/api/imhex_api.cpp\n        source/api/content_registry.cpp\n        source/api/event_manager.cpp\n        source/api/task_manager.cpp\n        source/api/shortcut_manager.cpp\n        source/api/plugin_manager.cpp\n        source/api/project_file_manager.cpp\n        source/api/theme_manager.cpp\n        source/api/layout_manager.cpp\n        source/api/workspace_manager.cpp\n        source/api/achievement_manager.cpp\n        source/api/localization_manager.cpp\n        source/api/tutorial_manager.cpp\n\n        source/data_processor/attribute.cpp\n        source/data_processor/link.cpp\n        source/data_processor/node.cpp\n\n        source/helpers/utils.cpp\n        source/helpers/utils_linux.cpp\n        source/helpers/fs.cpp\n        source/helpers/magic.cpp\n        source/helpers/crypto.cpp\n        source/helpers/http_requests.cpp\n        source/helpers/http_requests_native.cpp\n        source/helpers/http_requests_emscripten.cpp\n        source/helpers/opengl.cpp\n        source/helpers/patches.cpp\n        source/helpers/encoding_file.cpp\n        source/helpers/logger.cpp\n        source/helpers/tar.cpp\n        source/helpers/debugging.cpp\n        source/helpers/default_paths.cpp\n        source/helpers/imgui_hooks.cpp\n        source/helpers/semantic_version.cpp\n        source/helpers/keys.cpp\n        source/helpers/udp_server.cpp\n        source/helpers/scaling.cpp\n        source/helpers/binary_pattern.cpp\n\n        source/test/tests.cpp\n\n        source/providers/provider.cpp\n        source/providers/cached_provider.cpp\n        source/providers/memory_provider.cpp\n        source/providers/undo/stack.cpp\n\n        source/ui/imgui_imhex_extensions.cpp\n        source/ui/view.cpp\n        source/ui/popup.cpp\n        source/ui/toast.cpp\n        source/ui/banner.cpp\n\n        source/mcp/client.cpp\n        source/mcp/server.cpp\n\n        source/subcommands/subcommands.cpp\n)\n\nif (APPLE)\n    set(LIBIMHEX_SOURCES ${LIBIMHEX_SOURCES}\n            source/helpers/utils_macos.m\n            source/helpers/macos_menu.m\n    )\nendif ()\n\nif (IMHEX_EXTERNAL_PLUGIN_BUILD)\n    add_library(libimhex IMPORTED SHARED GLOBAL)\n    set(LIBIMHEX_LIBRARY_TYPE INTERFACE)\nelse()\n    if (IMHEX_STATIC_LINK_PLUGINS)\n        add_library(libimhex STATIC ${LIBIMHEX_SOURCES})\n    else()\n        add_library(libimhex SHARED ${LIBIMHEX_SOURCES})\n    endif()\n\n    if (IMHEX_ENABLE_CXX_MODULES)\n        target_sources(libimhex\n                PUBLIC\n                FILE_SET cxx_modules TYPE CXX_MODULES\n                FILES\n                include/hex.cppm\n        )\n    endif()\n\n    set(LIBIMHEX_LIBRARY_TYPE PUBLIC)\n    target_compile_definitions(libimhex PRIVATE IMHEX_PROJECT_NAME=\"${PROJECT_NAME}\")\nendif()\n\naddCppCheck(libimhex)\n\n\nif (DEFINED IMHEX_COMMIT_HASH_LONG AND DEFINED IMHEX_COMMIT_BRANCH)\n    set(GIT_COMMIT_HASH_LONG \"${IMHEX_COMMIT_HASH_LONG}\")\n    set(GIT_BRANCH \"${IMHEX_COMMIT_BRANCH}\")\nelse()\n    # Get the current working branch\n    execute_process(\n            COMMAND git rev-parse --abbrev-ref HEAD\n            WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n            OUTPUT_VARIABLE GIT_BRANCH\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            RESULT_VARIABLE RESULT_BRANCH\n            ERROR_QUIET\n    )\n\n    execute_process(\n            COMMAND git log -1 --format=%H\n            WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n            OUTPUT_VARIABLE GIT_COMMIT_HASH_LONG\n            OUTPUT_STRIP_TRAILING_WHITESPACE\n            RESULT_VARIABLE RESULT_HASH_LONG\n            ERROR_QUIET\n    )\nendif ()\n\nif (GIT_COMMIT_HASH_LONG STREQUAL \"\" OR GIT_BRANCH STREQUAL \"\")\n    message(WARNING \"Failed to to determine commit hash/branch\")\nelse()\n    addDefineToSource(source/api/imhex_api.cpp \"GIT_COMMIT_HASH_LONG=\\\"${GIT_COMMIT_HASH_LONG}\\\"\")\n    addDefineToSource(source/api/imhex_api.cpp \"GIT_BRANCH=\\\"${GIT_BRANCH}\\\"\")\nendif ()\n\naddDefineToSource(source/api/imhex_api.cpp \"IMHEX_VERSION=\\\"${IMHEX_VERSION_STRING}\\\"\")\n\nstring(TIMESTAMP IMHEX_BUILD_DATE UTC)\naddDefineToSource(source/api/imhex_api.cpp \"IMHEX_BUILD_DATE=\\\"${IMHEX_BUILD_DATE}\\\"\")\n\nenableUnityBuild(libimhex)\nsetupCompilerFlags(libimhex)\n\ninclude(GenerateExportHeader)\ngenerate_export_header(libimhex)\n\ntarget_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} include ${XDGPP_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS})\n\nif (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)\n    if (WIN32)\n        set_target_properties(libimhex PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)\n        if (NOT MSVC)\n            target_link_options(libimhex PRIVATE -Wl,--export-all-symbols)\n        endif()\n        target_link_libraries(libimhex PRIVATE Netapi32.lib)\n        target_compile_definitions(libimhex PRIVATE EXPORT_SYMBOLS=1)\n    elseif (APPLE)\n        find_library(FOUNDATION NAMES Foundation)\n        find_library(USERNOTIFICATIONS NAMES UserNotifications)\n        target_link_libraries(libimhex PUBLIC ${FOUNDATION} ${USERNOTIFICATIONS})\n    endif ()\n\n    target_link_libraries(libimhex PRIVATE libpl microtar ${NFD_LIBRARIES} magic)\n    target_link_libraries(libimhex PUBLIC libwolv libpl_includes libpl-gen ${IMGUI_LIBRARIES} ${JTHREAD_LIBRARIES})\n\n    if (IMHEX_ENABLE_IMGUI_TEST_ENGINE)\n        target_link_libraries(libimhex PUBLIC imgui_test_engine)\n    endif()\n\n    if (NOT WIN32)\n        target_link_libraries(libimhex PRIVATE dl)\n    endif()\n\n    if (NOT EMSCRIPTEN)\n        # curl is only used in non-emscripten builds\n        target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} CURL::libcurl)\n    endif()\n\n    target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_INCLUDE_DIR} ${LIBBACKTRACE_INCLUDE_DIRS} ${MAGIC_INCLUDE_DIRS})\n    target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARIES})\n    target_link_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARY_DIR} ${MAGIC_LIBRARY_DIRS})\n\n    precompileHeaders(libimhex \"${CMAKE_CURRENT_SOURCE_DIR}/include\")\nendif()\n\ntarget_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${FMT_LIBRARIES} ${LUNASVG_LIBRARIES} ${BOOST_LIBRARIES} tracing)\n\nset_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)\n\nadd_dependencies(imhex_all libimhex)\n\ninstall(FILES \"$<TARGET_FILE:libimhex>\" DESTINATION \"${CMAKE_INSTALL_LIBDIR}\" PERMISSIONS ${LIBRARY_PERMISSIONS})\nset_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})\nset_target_properties(libimhex PROPERTIES PREFIX \"\")"
  },
  {
    "path": "lib/libimhex/LICENSE",
    "content": "                  GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 2.1, February 1999\n\n Copyright (C) 1991, 1999 Free Software Foundation, Inc.\n 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n[This is the first released version of the Lesser GPL.  It also counts\n as the successor of the GNU Library Public License, version 2, hence\n the version number 2.1.]\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicenses are intended to guarantee your freedom to share and change\nfree software--to make sure the software is free for all its users.\n\n  This license, the Lesser General Public License, applies to some\nspecially designated software packages--typically libraries--of the\nFree Software Foundation and other authors who decide to use it.  You\ncan use it too, but we suggest you first think carefully about whether\nthis license or the ordinary General Public License is the better\nstrategy to use in any particular case, based on the explanations below.\n\n  When we speak of free software, we are referring to freedom of use,\nnot price.  Our General Public Licenses are designed to make sure that\nyou have the freedom to distribute copies of free software (and charge\nfor this service if you wish); that you receive source code or can get\nit if you want it; that you can change the software and use pieces of\nit in new free programs; and that you are informed that you can do\nthese things.\n\n  To protect your rights, we need to make restrictions that forbid\ndistributors to deny you these rights or to ask you to surrender these\nrights.  These restrictions translate to certain responsibilities for\nyou if you distribute copies of the library or if you modify it.\n\n  For example, if you distribute copies of the library, whether gratis\nor for a fee, you must give the recipients all the rights that we gave\nyou.  You must make sure that they, too, receive or can get the source\ncode.  If you link other code with the library, you must provide\ncomplete object files to the recipients, so that they can relink them\nwith the library after making changes to the library and recompiling\nit.  And you must show them these terms so they know their rights.\n\n  We protect your rights with a two-step method: (1) we copyright the\nlibrary, and (2) we offer you this license, which gives you legal\npermission to copy, distribute and/or modify the library.\n\n  To protect each distributor, we want to make it very clear that\nthere is no warranty for the free library.  Also, if the library is\nmodified by someone else and passed on, the recipients should know\nthat what they have is not the original version, so that the original\nauthor's reputation will not be affected by problems that might be\nintroduced by others.\n\f\n  Finally, software patents pose a constant threat to the existence of\nany free program.  We wish to make sure that a company cannot\neffectively restrict the users of a free program by obtaining a\nrestrictive license from a patent holder.  Therefore, we insist that\nany patent license obtained for a version of the library must be\nconsistent with the full freedom of use specified in this license.\n\n  Most GNU software, including some libraries, is covered by the\nordinary GNU General Public License.  This license, the GNU Lesser\nGeneral Public License, applies to certain designated libraries, and\nis quite different from the ordinary General Public License.  We use\nthis license for certain libraries in order to permit linking those\nlibraries into non-free programs.\n\n  When a program is linked with a library, whether statically or using\na shared library, the combination of the two is legally speaking a\ncombined work, a derivative of the original library.  The ordinary\nGeneral Public License therefore permits such linking only if the\nentire combination fits its criteria of freedom.  The Lesser General\nPublic License permits more lax criteria for linking other code with\nthe library.\n\n  We call this license the \"Lesser\" General Public License because it\ndoes Less to protect the user's freedom than the ordinary General\nPublic License.  It also provides other free software developers Less\nof an advantage over competing non-free programs.  These disadvantages\nare the reason we use the ordinary General Public License for many\nlibraries.  However, the Lesser license provides advantages in certain\nspecial circumstances.\n\n  For example, on rare occasions, there may be a special need to\nencourage the widest possible use of a certain library, so that it becomes\na de-facto standard.  To achieve this, non-free programs must be\nallowed to use the library.  A more frequent case is that a free\nlibrary does the same job as widely used non-free libraries.  In this\ncase, there is little to gain by limiting the free library to free\nsoftware only, so we use the Lesser General Public License.\n\n  In other cases, permission to use a particular library in non-free\nprograms enables a greater number of people to use a large body of\nfree software.  For example, permission to use the GNU C Library in\nnon-free programs enables many more people to use the whole GNU\noperating system, as well as its variant, the GNU/Linux operating\nsystem.\n\n  Although the Lesser General Public License is Less protective of the\nusers' freedom, it does ensure that the user of a program that is\nlinked with the Library has the freedom and the wherewithal to run\nthat program using a modified version of the Library.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.  Pay close attention to the difference between a\n\"work based on the library\" and a \"work that uses the library\".  The\nformer contains code derived from the library, whereas the latter must\nbe combined with the library in order to run.\n\f\n                  GNU LESSER GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License Agreement applies to any software library or other\nprogram which contains a notice placed by the copyright holder or\nother authorized party saying it may be distributed under the terms of\nthis Lesser General Public License (also called \"this License\").\nEach licensee is addressed as \"you\".\n\n  A \"library\" means a collection of software functions and/or data\nprepared so as to be conveniently linked with application programs\n(which use some of those functions and data) to form executables.\n\n  The \"Library\", below, refers to any such software library or work\nwhich has been distributed under these terms.  A \"work based on the\nLibrary\" means either the Library or any derivative work under\ncopyright law: that is to say, a work containing the Library or a\nportion of it, either verbatim or with modifications and/or translated\nstraightforwardly into another language.  (Hereinafter, translation is\nincluded without limitation in the term \"modification\".)\n\n  \"Source code\" for a work means the preferred form of the work for\nmaking modifications to it.  For a library, complete source code means\nall the source code for all modules it contains, plus any associated\ninterface definition files, plus the scripts used to control compilation\nand installation of the library.\n\n  Activities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning a program using the Library is not restricted, and output from\nsuch a program is covered only if its contents constitute a work based\non the Library (independent of the use of the Library in a tool for\nwriting it).  Whether that is true depends on what the Library does\nand what the program that uses the Library does.\n\n  1. You may copy and distribute verbatim copies of the Library's\ncomplete source code as you receive it, in any medium, provided that\nyou conspicuously and appropriately publish on each copy an\nappropriate copyright notice and disclaimer of warranty; keep intact\nall the notices that refer to this License and to the absence of any\nwarranty; and distribute a copy of this License along with the\nLibrary.\n\n  You may charge a fee for the physical act of transferring a copy,\nand you may at your option offer warranty protection in exchange for a\nfee.\n\f\n  2. You may modify your copy or copies of the Library or any portion\nof it, thus forming a work based on the Library, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) The modified work must itself be a software library.\n\n    b) You must cause the files modified to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    c) You must cause the whole of the work to be licensed at no\n    charge to all third parties under the terms of this License.\n\n    d) If a facility in the modified Library refers to a function or a\n    table of data to be supplied by an application program that uses\n    the facility, other than as an argument passed when the facility\n    is invoked, then you must make a good faith effort to ensure that,\n    in the event an application does not supply such function or\n    table, the facility still operates, and performs whatever part of\n    its purpose remains meaningful.\n\n    (For example, a function in a library to compute square roots has\n    a purpose that is entirely well-defined independent of the\n    application.  Therefore, Subsection 2d requires that any\n    application-supplied function or table used by this function must\n    be optional: if the application does not supply it, the square\n    root function must still compute square roots.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Library,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Library, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote\nit.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Library.\n\nIn addition, mere aggregation of another work not based on the Library\nwith the Library (or with a work based on the Library) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may opt to apply the terms of the ordinary GNU General Public\nLicense instead of this License to a given copy of the Library.  To do\nthis, you must alter all the notices that refer to this License, so\nthat they refer to the ordinary GNU General Public License, version 2,\ninstead of to this License.  (If a newer version than version 2 of the\nordinary GNU General Public License has appeared, then you can specify\nthat version instead if you wish.)  Do not make any other change in\nthese notices.\n\f\n  Once this change is made in a given copy, it is irreversible for\nthat copy, so the ordinary GNU General Public License applies to all\nsubsequent copies and derivative works made from that copy.\n\n  This option is useful when you wish to copy part of the code of\nthe Library into a program that is not a library.\n\n  4. You may copy and distribute the Library (or a portion or\nderivative of it, under Section 2) in object code or executable form\nunder the terms of Sections 1 and 2 above provided that you accompany\nit with the complete corresponding machine-readable source code, which\nmust be distributed under the terms of Sections 1 and 2 above on a\nmedium customarily used for software interchange.\n\n  If distribution of object code is made by offering access to copy\nfrom a designated place, then offering equivalent access to copy the\nsource code from the same place satisfies the requirement to\ndistribute the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  5. A program that contains no derivative of any portion of the\nLibrary, but is designed to work with the Library by being compiled or\nlinked with it, is called a \"work that uses the Library\".  Such a\nwork, in isolation, is not a derivative work of the Library, and\ntherefore falls outside the scope of this License.\n\n  However, linking a \"work that uses the Library\" with the Library\ncreates an executable that is a derivative of the Library (because it\ncontains portions of the Library), rather than a \"work that uses the\nlibrary\".  The executable is therefore covered by this License.\nSection 6 states terms for distribution of such executables.\n\n  When a \"work that uses the Library\" uses material from a header file\nthat is part of the Library, the object code for the work may be a\nderivative work of the Library even though the source code is not.\nWhether this is true is especially significant if the work can be\nlinked without the Library, or if the work is itself a library.  The\nthreshold for this to be true is not precisely defined by law.\n\n  If such an object file uses only numerical parameters, data\nstructure layouts and accessors, and small macros and small inline\nfunctions (ten lines or less in length), then the use of the object\nfile is unrestricted, regardless of whether it is legally a derivative\nwork.  (Executables containing this object code plus portions of the\nLibrary will still fall under Section 6.)\n\n  Otherwise, if the work is a derivative of the Library, you may\ndistribute the object code for the work under the terms of Section 6.\nAny executables containing that work also fall under Section 6,\nwhether or not they are linked directly with the Library itself.\n\f\n  6. As an exception to the Sections above, you may also combine or\nlink a \"work that uses the Library\" with the Library to produce a\nwork containing portions of the Library, and distribute that work\nunder terms of your choice, provided that the terms permit\nmodification of the work for the customer's own use and reverse\nengineering for debugging such modifications.\n\n  You must give prominent notice with each copy of the work that the\nLibrary is used in it and that the Library and its use are covered by\nthis License.  You must supply a copy of this License.  If the work\nduring execution displays copyright notices, you must include the\ncopyright notice for the Library among them, as well as a reference\ndirecting the user to the copy of this License.  Also, you must do one\nof these things:\n\n    a) Accompany the work with the complete corresponding\n    machine-readable source code for the Library including whatever\n    changes were used in the work (which must be distributed under\n    Sections 1 and 2 above); and, if the work is an executable linked\n    with the Library, with the complete machine-readable \"work that\n    uses the Library\", as object code and/or source code, so that the\n    user can modify the Library and then relink to produce a modified\n    executable containing the modified Library.  (It is understood\n    that the user who changes the contents of definitions files in the\n    Library will not necessarily be able to recompile the application\n    to use the modified definitions.)\n\n    b) Use a suitable shared library mechanism for linking with the\n    Library.  A suitable mechanism is one that (1) uses at run time a\n    copy of the library already present on the user's computer system,\n    rather than copying library functions into the executable, and (2)\n    will operate properly with a modified version of the library, if\n    the user installs one, as long as the modified version is\n    interface-compatible with the version that the work was made with.\n\n    c) Accompany the work with a written offer, valid for at\n    least three years, to give the same user the materials\n    specified in Subsection 6a, above, for a charge no more\n    than the cost of performing this distribution.\n\n    d) If distribution of the work is made by offering access to copy\n    from a designated place, offer equivalent access to copy the above\n    specified materials from the same place.\n\n    e) Verify that the user has already received a copy of these\n    materials or that you have already sent this user a copy.\n\n  For an executable, the required form of the \"work that uses the\nLibrary\" must include any data and utility programs needed for\nreproducing the executable from it.  However, as a special exception,\nthe materials to be distributed need not include anything that is\nnormally distributed (in either source or binary form) with the major\ncomponents (compiler, kernel, and so on) of the operating system on\nwhich the executable runs, unless that component itself accompanies\nthe executable.\n\n  It may happen that this requirement contradicts the license\nrestrictions of other proprietary libraries that do not normally\naccompany the operating system.  Such a contradiction means you cannot\nuse both them and the Library together in an executable that you\ndistribute.\n\f\n  7. You may place library facilities that are a work based on the\nLibrary side-by-side in a single library together with other library\nfacilities not covered by this License, and distribute such a combined\nlibrary, provided that the separate distribution of the work based on\nthe Library and of the other library facilities is otherwise\npermitted, and provided that you do these two things:\n\n    a) Accompany the combined library with a copy of the same work\n    based on the Library, uncombined with any other library\n    facilities.  This must be distributed under the terms of the\n    Sections above.\n\n    b) Give prominent notice with the combined library of the fact\n    that part of it is a work based on the Library, and explaining\n    where to find the accompanying uncombined form of the same work.\n\n  8. You may not copy, modify, sublicense, link with, or distribute\nthe Library except as expressly provided under this License.  Any\nattempt otherwise to copy, modify, sublicense, link with, or\ndistribute the Library is void, and will automatically terminate your\nrights under this License.  However, parties who have received copies,\nor rights, from you under this License will not have their licenses\nterminated so long as such parties remain in full compliance.\n\n  9. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Library or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Library (or any work based on the\nLibrary), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Library or works based on it.\n\n  10. Each time you redistribute the Library (or any work based on the\nLibrary), the recipient automatically receives a license from the\noriginal licensor to copy, distribute, link with or modify the Library\nsubject to these terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties with\nthis License.\n\f\n  11. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Library at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Library by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Library.\n\nIf any portion of this section is held invalid or unenforceable under any\nparticular circumstance, the balance of the section is intended to apply,\nand the section as a whole is intended to apply in other circumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  12. If the distribution and/or use of the Library is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Library under this License may add\nan explicit geographical distribution limitation excluding those countries,\nso that distribution is permitted only in or among countries not thus\nexcluded.  In such case, this License incorporates the limitation as if\nwritten in the body of this License.\n\n  13. The Free Software Foundation may publish revised and/or new\nversions of the Lesser General Public License from time to time.\nSuch new versions will be similar in spirit to the present version,\nbut may differ in detail to address new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Library\nspecifies a version number of this License which applies to it and\n\"any later version\", you have the option of following the terms and\nconditions either of that version or of any later version published by\nthe Free Software Foundation.  If the Library does not specify a\nlicense version number, you may choose any version ever published by\nthe Free Software Foundation.\n\f\n  14. If you wish to incorporate parts of the Library into other free\nprograms whose distribution conditions are incompatible with these,\nwrite to the author to ask for permission.  For software which is\ncopyrighted by the Free Software Foundation, write to the Free\nSoftware Foundation; we sometimes make exceptions for this.  Our\ndecision will be guided by the two goals of preserving the free status\nof all derivatives of our free software and of promoting the sharing\nand reuse of software generally.\n\n                            NO WARRANTY\n\n  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO\nWARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.\nEXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR\nOTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY\nKIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE\nLIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME\nTHE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN\nWRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY\nAND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU\nFOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR\nCONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE\nLIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING\nRENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A\nFAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF\nSUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\f\n           How to Apply These Terms to Your New Libraries\n\n  If you develop a new library, and you want it to be of the greatest\npossible use to the public, we recommend making it free software that\neveryone can redistribute and change.  You can do so by permitting\nredistribution under these terms (or, alternatively, under the terms of the\nordinary General Public License).\n\n  To apply these terms, attach the following notices to the library.  It is\nsafest to attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least the\n\"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the library's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This library is free software; you can redistribute it and/or\n    modify it under the terms of the GNU Lesser General Public\n    License as published by the Free Software Foundation; either\n    version 2.1 of the License, or (at your option) any later version.\n\n    This library is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n    Lesser General Public License for more details.\n\n    You should have received a copy of the GNU Lesser General Public\n    License along with this library; if not, write to the Free Software\n    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\n\nAlso add information on how to contact you by electronic and paper mail.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the library, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the\n  library `Frob' (a library for tweaking knobs) written by James Random Hacker.\n\n  <signature of Ty Coon>, 1 April 1990\n  Ty Coon, President of Vice\n\nThat's all there is to it!\n"
  },
  {
    "path": "lib/libimhex/include/hex/api/achievement_manager.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <functional>\n#include <list>\n#include <memory>\n#include <span>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include <imgui.h>\n#include <hex/ui/imgui_imhex_extensions.h>\n#include <hex/api/localization_manager.hpp>\n#include <hex/helpers/auto_reset.hpp>\n\nEXPORT_MODULE namespace hex {\n\n    class AchievementManager;\n\n    class Achievement {\n    public:\n        explicit Achievement(UnlocalizedString unlocalizedCategory, UnlocalizedString unlocalizedName) : m_unlocalizedCategory(std::move(unlocalizedCategory)), m_unlocalizedName(std::move(unlocalizedName)) { }\n\n        /**\n         * @brief Returns the unlocalized name of the achievement\n         * @return Unlocalized name of the achievement\n         */\n        [[nodiscard]] const UnlocalizedString &getUnlocalizedName() const {\n            return m_unlocalizedName;\n        }\n\n        /**\n         * @brief Returns the unlocalized category of the achievement\n         * @return Unlocalized category of the achievement\n         */\n        [[nodiscard]] const UnlocalizedString &getUnlocalizedCategory() const {\n            return m_unlocalizedCategory;\n        }\n\n        /**\n         * @brief Returns whether the achievement is unlocked\n         * @return Whether the achievement is unlocked\n         */\n        [[nodiscard]] bool isUnlocked() const {\n            return m_progress == m_maxProgress;\n        }\n\n        /**\n         * @brief Sets the description of the achievement\n         * @param description Description of the achievement\n         * @return Reference to the achievement\n         */\n        Achievement& setDescription(std::string description) {\n            m_unlocalizedDescription = std::move(description);\n\n            return *this;\n        }\n\n        /**\n         * @brief Adds a requirement to the achievement. The achievement will only be unlockable if all requirements are unlocked.\n         * @param requirement Unlocalized name of the requirement\n         * @return Reference to the achievement\n         */\n        Achievement& addRequirement(std::string requirement) {\n            m_requirements.emplace_back(std::move(requirement));\n\n            return *this;\n        }\n\n        /**\n         * @brief Adds a visibility requirement to the achievement. The achievement will only be visible if all requirements are unlocked.\n         * @param requirement Unlocalized name of the requirement\n         * @return Reference to the achievement\n         */\n        Achievement& addVisibilityRequirement(std::string requirement) {\n            m_visibilityRequirements.emplace_back(std::move(requirement));\n\n            return *this;\n        }\n\n        /**\n         * @brief Marks the achievement as blacked. Blacked achievements are visible but their name and description are hidden.\n         * @return Reference to the achievement\n         */\n        Achievement& setBlacked() {\n            m_blacked = true;\n\n            return *this;\n        }\n\n        /**\n         * @brief Marks the achievement as invisible. Invisible achievements are not visible at all.\n         * @return Reference to the achievement\n         */\n        Achievement& setInvisible() {\n            m_invisible = true;\n\n            return *this;\n        }\n\n        /**\n         * @brief Returns whether the achievement is blacked\n         * @return Whether the achievement is blacked\n         */\n        [[nodiscard]] bool isBlacked() const {\n            return m_blacked;\n        }\n\n        /**\n         * @brief Returns whether the achievement is invisible\n         * @return Whether the achievement is invisible\n         */\n        [[nodiscard]] bool isInvisible() const {\n            return m_invisible;\n        }\n\n        /**\n         * @brief Returns the list of requirements of the achievement\n         * @return List of requirements of the achievement\n         */\n        [[nodiscard]] const std::vector<std::string> &getRequirements() const {\n            return m_requirements;\n        }\n\n        /**\n         * @brief Returns the list of visibility requirements of the achievement\n         * @return List of visibility requirements of the achievement\n         */\n        [[nodiscard]] const std::vector<std::string> &getVisibilityRequirements() const {\n            return m_visibilityRequirements;\n        }\n\n        /**\n         * @brief Returns the unlocalized description of the achievement\n         * @return Unlocalized description of the achievement\n         */\n        [[nodiscard]] const UnlocalizedString &getUnlocalizedDescription() const {\n            return m_unlocalizedDescription;\n        }\n\n        /**\n         * @brief Returns the icon of the achievement\n         * @return Icon of the achievement\n         */\n        [[nodiscard]] const char* getIcon() const {\n            return m_icon.c_str();\n        }\n\n        /**\n         * @brief Sets the icon of the achievement\n         * @param icon Icon glyph\n         * @return Reference to the achievement\n         */\n        Achievement& setIcon(std::string icon) {\n            m_icon = std::move(icon);\n\n            return *this;\n        }\n\n        /**\n         * @brief Specifies the required progress to unlock the achievement. This is the number of times this achievement has to be triggered to unlock it. The default is 1.\n         * @param progress Required progress\n         * @return Reference to the achievement\n         */\n        Achievement& setRequiredProgress(u32 progress) {\n            m_maxProgress = progress;\n\n            return *this;\n        }\n\n        /**\n         * @brief Returns the required progress to unlock the achievement\n         * @return Required progress to unlock the achievement\n         */\n        [[nodiscard]] u32 getRequiredProgress() const {\n            return m_maxProgress;\n        }\n\n        /**\n         * @brief Returns the current progress of the achievement\n         * @return Current progress of the achievement\n         */\n        [[nodiscard]] u32 getProgress() const {\n            return m_progress;\n        }\n\n        /**\n         * @brief Sets the callback to call when the achievement is clicked\n         * @param callback Callback to call when the achievement is clicked\n         */\n        void setClickCallback(const std::function<void(Achievement &)> &callback) {\n            m_clickCallback = callback;\n        }\n\n        /**\n         * @brief Returns the callback to call when the achievement is clicked\n         * @return Callback to call when the achievement is clicked\n         */\n        [[nodiscard]] const std::function<void(Achievement &)> &getClickCallback() const {\n            return m_clickCallback;\n        }\n\n        /**\n         * @brief Returns whether the achievement is temporary. Temporary achievements have been added by challenge projects for example and will be removed when the project is closed.\n         * @return Whether the achievement is temporary\n         */\n        [[nodiscard]] bool isTemporary() const {\n            return m_temporary;\n        }\n\n        /**\n         * @brief Sets whether the achievement is unlocked\n         * @param unlocked Whether the achievement is unlocked\n         */\n        void setUnlocked(bool unlocked) {\n            if (unlocked) {\n                if (m_progress < m_maxProgress)\n                    m_progress++;\n            } else {\n                m_progress = 0;\n            }\n        }\n\n    protected:\n        void setProgress(u32 progress) {\n            m_progress = progress;\n        }\n\n    private:\n        UnlocalizedString m_unlocalizedCategory, m_unlocalizedName;\n        UnlocalizedString m_unlocalizedDescription;\n\n        bool m_blacked = false;\n        bool m_invisible = false;\n        std::vector<std::string> m_requirements, m_visibilityRequirements;\n\n        std::function<void(Achievement &)> m_clickCallback;\n\n        std::string m_icon;\n\n        u32 m_progress = 0;\n        u32 m_maxProgress = 1;\n\n        bool m_temporary = false;\n\n        friend class AchievementManager;\n    };\n\n    class AchievementManager {\n        static bool s_initialized;\n    public:\n        AchievementManager() = delete;\n\n        struct AchievementNode {\n            Achievement *achievement;\n            std::vector<AchievementNode*> children, parents;\n            std::vector<AchievementNode*> visibilityParents;\n            ImVec2 position;\n\n            [[nodiscard]] bool hasParents() const {\n                return !this->parents.empty();\n            }\n\n            [[nodiscard]] bool isUnlockable() const {\n                return std::ranges::all_of(this->parents, [](const auto &parent) {\n                    return parent->achievement->isUnlocked();\n                });\n            }\n\n            [[nodiscard]] bool isVisible() const {\n                return std::ranges::all_of(this->visibilityParents, [](const auto &parent) {\n                    return parent->achievement->isUnlocked();\n                });\n            }\n\n            [[nodiscard]] bool isUnlocked() const {\n                return this->achievement->isUnlocked();\n            }\n        };\n\n        /**\n         * @brief Adds a new achievement\n         * @tparam T Type of the achievement\n         * @param args Arguments to pass to the constructor of the achievement\n         * @return Reference to the achievement\n         */\n        template<std::derived_from<Achievement> T = Achievement>\n        static Achievement& addAchievement(auto && ... args) {\n            auto newAchievement = std::make_unique<T>(std::forward<decltype(args)>(args)...);\n\n            return addAchievementImpl(std::move(newAchievement));\n        }\n\n        /**\n         * @brief Adds a new temporary achievement\n         * @tparam T Type of the achievement\n         * @param args Arguments to pass to the constructor of the achievement\n         * @return Reference to the achievement\n         */\n        template<std::derived_from<Achievement> T = Achievement>\n        static Achievement& addTemporaryAchievement(auto && ... args) {\n            auto &achievement = addAchievement<T>(std::forward<decltype(args)>(args)...);\n\n            achievement.m_temporary = true;\n\n            return achievement;\n        }\n\n        /**\n         * @brief Unlocks an achievement\n         * @param unlocalizedCategory Unlocalized category of the achievement\n         * @param unlocalizedName Unlocalized name of the achievement\n         */\n        static void unlockAchievement(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName);\n\n        /**\n         * @brief Returns all registered achievements\n         * @return All achievements\n         */\n        static const std::unordered_map<UnlocalizedString, std::unordered_map<UnlocalizedString, std::unique_ptr<Achievement>>>& getAchievements();\n\n        /**\n         * @brief Returns all achievement start nodes\n         * @note Start nodes are all nodes that don't have any parents\n         * @param rebuild Whether to rebuild the list of start nodes\n         * @return All achievement start nodes\n         */\n        static const std::unordered_map<UnlocalizedString, std::vector<AchievementNode*>>& getAchievementStartNodes(bool rebuild = true);\n\n        /**\n         * @brief Returns all achievement nodes\n         * @param rebuild Whether to rebuild the list of nodes\n         * @return All achievement nodes\n         */\n        static const std::unordered_map<UnlocalizedString, std::list<AchievementNode>>& getAchievementNodes(bool rebuild = true);\n\n        /**\n         * @brief Loads the progress of all achievements from the achievements save file\n         */\n        static void loadProgress();\n\n        /**\n         * @brief Stores the progress of all achievements to the achievements save file\n         */\n        static void storeProgress();\n\n        /**\n         * @brief Removes all temporary achievements from the tree\n         */\n        static void clearTemporary();\n\n        /**\n         * \\brief Returns the current progress of all achievements\n         * \\return A pair containing the number of unlocked achievements and the total number of achievements\n         */\n        static std::pair<u32, u32> getProgress();\n\n    private:\n        static void achievementAdded();\n\n        static Achievement& addAchievementImpl(std::unique_ptr<Achievement> &&newAchievement);\n    };\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/background_services.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <functional>\n#include <hex/api/localization_manager.hpp>\n\nEXPORT_MODULE namespace hex {\n\n    /* Background Service Registry. Allows adding new background services */\n    namespace ContentRegistry::BackgroundServices {\n\n        namespace impl {\n            using Callback = std::function<void()>;\n\n            void stopServices();\n        }\n\n        void registerService(const UnlocalizedString &unlocalizedName, const impl::Callback &callback);\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/command_palette.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n\n#include <string>\n#include <functional>\n#include <optional>\n#include <vector>\n\nEXPORT_MODULE namespace hex {\n\n    /* Command Palette Command Registry. Allows adding of new commands to the command palette */\n    namespace ContentRegistry::CommandPalette {\n\n        enum class Type : u32 {\n            SymbolCommand,\n            KeywordCommand\n        };\n\n        namespace impl {\n\n            using QueryResultCallback = std::function<void(std::string)>;\n\n            struct QueryResult {\n                std::string name;\n                QueryResultCallback callback;\n            };\n\n            using ContentDisplayCallback = std::function<void()>;\n            using DisplayCallback = std::function<std::string(std::string)>;\n            using ExecuteCallback = std::function<std::optional<std::string>(std::string)>;\n            using QueryCallback   = std::function<std::vector<QueryResult>(std::string)>;\n\n            struct Entry {\n                Type type;\n                std::string command;\n                UnlocalizedString unlocalizedDescription;\n                DisplayCallback displayCallback;\n                ExecuteCallback executeCallback;\n            };\n\n            struct Handler {\n                Type type;\n                std::string command;\n                QueryCallback queryCallback;\n                DisplayCallback displayCallback;\n            };\n\n            struct ContentDisplay {\n                bool showSearchBox;\n                ContentDisplayCallback callback;\n            };\n\n            const std::vector<Entry>& getEntries();\n            const std::vector<Handler>& getHandlers();\n\n            std::optional<ContentDisplay>& getDisplayedContent();\n\n        }\n\n        /**\n         * @brief Adds a new command to the command palette\n         * @param type The type of the command\n         * @param command The command to add\n         * @param unlocalizedDescription The description of the command\n         * @param displayCallback The callback that will be called when the command is displayed in the command palette\n         * @param executeCallback The callback that will be called when the command is executed\n         */\n        void add(\n            Type type,\n            const std::string &command,\n            const UnlocalizedString &unlocalizedDescription,\n            const impl::DisplayCallback &displayCallback,\n            const impl::ExecuteCallback &executeCallback = [](auto) { return std::nullopt; });\n\n        /**\n         * @brief Adds a new command handler to the command palette\n         * @param type The type of the command\n         * @param command The command to add\n         * @param queryCallback The callback that will be called when the command palette wants to load the name and callback items\n         * @param displayCallback The callback that will be called when the command is displayed in the command palette\n         */\n        void addHandler(\n            Type type,\n            const std::string &command,\n            const impl::QueryCallback &queryCallback,\n            const impl::DisplayCallback &displayCallback);\n\n        /**\n         * @brief Specify UI content that will be displayed inside the command palette\n         * @param displayCallback Display callback that will be called to display the content\n         */\n        void setDisplayedContent(const impl::ContentDisplayCallback &displayCallback);\n\n        /**\n         * @brief Opens the command palette window, displaying a user defined interface\n         * @param displayCallback Display callback that will be called to display the content\n         */\n        void openWithContent(const impl::ContentDisplayCallback &displayCallback);\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/communication_interface.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <nlohmann/json_fwd.hpp>\n\n#include <map>\n#include <string>\n\n#include <hex/mcp/server.hpp>\n\nEXPORT_MODULE namespace hex {\n\n    /* Network Communication Interface Registry. Allows adding new communication interface endpoints */\n    namespace ContentRegistry::CommunicationInterface {\n\n        namespace impl {\n            using NetworkCallback = std::function<nlohmann::json(const nlohmann::json &)>;\n\n            const std::map<std::string, NetworkCallback>& getNetworkEndpoints();\n        }\n\n        void registerNetworkEndpoint(const std::string &endpoint, const impl::NetworkCallback &callback);\n\n    }\n\n    namespace ContentRegistry::MCP {\n\n        namespace impl {\n            std::unique_ptr<mcp::Server>& getMcpServerInstance();\n\n            void setEnabled(bool enabled);\n        }\n\n        bool isEnabled();\n        bool isConnected();\n\n        void registerTool(std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json &params)> function);\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/data_formatter.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n\n#include <string>\n#include <functional>\n#include <vector>\n\nEXPORT_MODULE namespace hex {\n\n    #if !defined(HEX_MODULE_EXPORT)\n        namespace prv { class Provider; }\n    #endif\n\n    /* Data Formatter Registry. Allows adding formatters that are used in the Copy-As menu for example */\n    namespace ContentRegistry::DataFormatter {\n\n        namespace impl {\n\n            using Callback = std::function<std::string(prv::Provider *provider, u64 address, size_t size, bool preview)>;\n            struct ExportMenuEntry {\n                UnlocalizedString unlocalizedName;\n                Callback callback;\n            };\n\n            struct FindOccurrence {\n                Region region;\n                std::endian endian = std::endian::native;\n                enum class DecodeType : u8 { ASCII, UTF8, Binary, UTF16, Unsigned, Signed, Float, Double } decodeType;\n                bool selected;\n                std::string string;\n            };\n\n            using FindExporterCallback = std::function<std::vector<u8>(const std::vector<FindOccurrence>&, std::function<std::string(FindOccurrence)>)>;\n            struct FindExporterEntry {\n                UnlocalizedString unlocalizedName;\n                std::string fileExtension;\n                FindExporterCallback callback;\n            };\n\n            /**\n             * @brief Retrieves a list of all registered data formatters used by the 'File -> Export' menu\n             */\n            const std::vector<ExportMenuEntry>& getExportMenuEntries();\n\n            /**\n             * @brief Retrieves a list of all registered data formatters used in the Results section of the 'Find' view\n             */\n            const std::vector<FindExporterEntry>& getFindExporterEntries();\n\n        }\n\n\n        /**\n         * @brief Adds a new data formatter\n         * @param unlocalizedName The unlocalized name of the formatter\n         * @param callback The function to call to format the data\n         */\n        void addExportMenuEntry(const UnlocalizedString &unlocalizedName, const impl::Callback &callback);\n\n        /**\n         * @brief Adds a new data exporter for Find results\n         * @param unlocalizedName The unlocalized name of the formatter\n         * @param fileExtension The file extension to use for the exported file\n         * @param callback The function to call to format the data\n         */\n        void addFindExportFormatter(const UnlocalizedString &unlocalizedName, const std::string &fileExtension, const impl::FindExporterCallback &callback);\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/data_information.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/task_manager.hpp>\n#include <hex/api/localization_manager.hpp>\n\n#include <nlohmann/json_fwd.hpp>\n\n#include <atomic>\n#include <functional>\n#include <memory>\n#include <vector>\n\nEXPORT_MODULE namespace hex {\n\n    #if !defined(HEX_MODULE_EXPORT)\n        namespace prv { class Provider; }\n    #endif\n\n    /* Data Information Registry. Allows adding new analyzers to the data information view */\n    namespace ContentRegistry::DataInformation {\n\n        class InformationSection {\n        public:\n            InformationSection(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription = \"\", bool hasSettings = false)\n                : m_unlocalizedName(unlocalizedName), m_unlocalizedDescription(unlocalizedDescription),\n                  m_hasSettings(hasSettings) { }\n            virtual ~InformationSection() = default;\n\n            [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; }\n            [[nodiscard]] const UnlocalizedString& getUnlocalizedDescription() const { return m_unlocalizedDescription; }\n\n            virtual void process(Task &task, prv::Provider *provider, Region region) = 0;\n            virtual void reset() = 0;\n\n            virtual void drawSettings() { }\n            virtual void drawContent() = 0;\n\n            [[nodiscard]] bool isValid() const { return m_valid; }\n            void markValid(bool valid = true) { m_valid = valid; }\n\n            [[nodiscard]] bool isEnabled() const { return m_enabled; }\n            void setEnabled(bool enabled) { m_enabled = enabled; }\n\n            [[nodiscard]] bool isAnalyzing() const { return m_analyzing; }\n            void setAnalyzing(bool analyzing) { m_analyzing = analyzing; }\n\n            virtual void load(const nlohmann::json &data);\n            [[nodiscard]] virtual nlohmann::json store();\n\n            [[nodiscard]] bool hasSettings() const { return m_hasSettings; }\n\n        private:\n            UnlocalizedString m_unlocalizedName, m_unlocalizedDescription;\n            bool m_hasSettings;\n\n            std::atomic<bool> m_analyzing = false;\n            std::atomic<bool> m_valid     = false;\n            std::atomic<bool> m_enabled   = true;\n        };\n\n        namespace impl {\n\n            using CreateCallback = std::function<std::unique_ptr<InformationSection>()>;\n\n            const std::vector<CreateCallback>& getInformationSectionConstructors();\n            void addInformationSectionCreator(const CreateCallback &callback);\n\n        }\n\n        template<typename T>\n        void addInformationSection(auto && ...args) {\n            impl::addInformationSectionCreator([args...] {\n                return std::make_unique<T>(std::forward<decltype(args)>(args)...);\n            });\n        }\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/data_inspector.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n\n#include <functional>\n#include <optional>\n#include <string>\n#include <vector>\n#include <bit>\n\nEXPORT_MODULE namespace hex {\n\n    /* Data Inspector Registry. Allows adding of new types to the data inspector */\n    namespace ContentRegistry::DataInspector {\n\n        enum class NumberDisplayStyle : u8 {\n            Decimal,\n            Hexadecimal,\n            Octal\n        };\n\n        namespace impl {\n\n            struct DoNotUseThisByItselfTag {};\n\n            using DisplayFunction   = std::function<std::string()>;\n            using EditingFunction   = std::function<std::optional<std::vector<u8>>(std::string&, std::endian, DoNotUseThisByItselfTag)>;\n            using GeneratorFunction = std::function<DisplayFunction(const std::vector<u8> &, std::endian, NumberDisplayStyle)>;\n\n            struct Entry {\n                UnlocalizedString unlocalizedName;\n                size_t requiredSize;\n                size_t maxSize;\n                GeneratorFunction generatorFunction;\n                std::optional<EditingFunction> editingFunction;\n            };\n\n            const std::vector<Entry>& getEntries();\n\n        }\n\n        namespace EditWidget {\n\n            class Widget {\n            public:\n                using Function = std::function<std::vector<u8>(const std::string&, std::endian)>;\n\n                explicit Widget(const Function &function) : m_function(function) {}\n\n                virtual ~Widget() = default;\n                virtual std::optional<std::vector<u8>> draw(std::string &value, std::endian endian) = 0;\n                std::optional<std::vector<u8>> operator()(std::string &value, std::endian endian, impl::DoNotUseThisByItselfTag) {\n                    return draw(value, endian);\n                }\n\n                std::vector<u8> getBytes(const std::string &value, std::endian endian) const {\n                    return m_function(value, endian);\n                }\n\n            private:\n                Function m_function;\n            };\n\n            struct TextInput : Widget {\n                explicit TextInput(const Function &function) : Widget(function) {}\n                std::optional<std::vector<u8>> draw(std::string &value, std::endian endian) override;\n            };\n\n        }\n\n        /**\n         * @brief Adds a new entry to the data inspector\n         * @param unlocalizedName The unlocalized name of the entry\n         * @param requiredSize The minimum required number of bytes available for the entry to appear\n         * @param displayGeneratorFunction The function that will be called to generate the display function\n         * @param editingFunction The function that will be called to edit the data\n         */\n        void add(\n            const UnlocalizedString &unlocalizedName,\n            size_t requiredSize,\n            impl::GeneratorFunction displayGeneratorFunction,\n            std::optional<impl::EditingFunction> editingFunction = std::nullopt\n        );\n\n        /**\n         * @brief Adds a new entry to the data inspector\n         * @param unlocalizedName The unlocalized name of the entry\n         * @param requiredSize The minimum required number of bytes available for the entry to appear\n         * @param maxSize The maximum number of bytes to read from the data\n         * @param displayGeneratorFunction The function that will be called to generate the display function\n         * @param editingFunction The function that will be called to edit the data\n         */\n        void add(\n            const UnlocalizedString &unlocalizedName,\n            size_t requiredSize,\n            size_t maxSize,\n            impl::GeneratorFunction displayGeneratorFunction,\n            std::optional<impl::EditingFunction> editingFunction = std::nullopt\n        );\n\n        /**\n         * @brief Allows adding new menu items to data inspector row context menus. Call this function inside the\n         * draw function of the data inspector row definition.\n         * @param function Callback that will draw menu items\n         */\n        void drawMenuItems(const std::function<void()> &function);\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/data_processor.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n\n#include <functional>\n#include <memory>\n#include <vector>\n\nEXPORT_MODULE namespace hex {\n\n    #if !defined(HEX_MODULE_EXPORT)\n        namespace dp { class Node; }\n    #endif\n\n    /* Data Processor Node Registry. Allows adding new processor nodes to be used in the data processor */\n    namespace ContentRegistry::DataProcessor {\n\n        namespace impl {\n\n            using CreatorFunction = std::function<std::unique_ptr<dp::Node>()>;\n\n            struct Entry {\n                UnlocalizedString unlocalizedCategory;\n                UnlocalizedString unlocalizedName;\n                CreatorFunction creatorFunction;\n            };\n\n            void add(const Entry &entry);\n\n            const std::vector<Entry>& getEntries();\n        }\n\n\n        /**\n         * @brief Adds a new node to the data processor\n         * @tparam T The custom node class that extends dp::Node\n         * @tparam Args Arguments types\n         * @param unlocalizedCategory The unlocalized category name of the node\n         * @param unlocalizedName The unlocalized name of the node\n         * @param args Arguments passed to the constructor of the node\n         */\n        template<std::derived_from<dp::Node> T, typename... Args>\n        void add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, Args &&...args) {\n            add(impl::Entry {\n                unlocalizedCategory,\n                unlocalizedName,\n                [unlocalizedName, ...args = std::forward<Args>(args)]() mutable {\n                    auto node = std::make_unique<T>(std::forward<Args>(args)...);\n                    node->setUnlocalizedName(unlocalizedName);\n                    return node;\n                }\n            });\n        }\n\n        /**\n         * @brief Adds a separator to the data processor right click menu\n         */\n        void addSeparator();\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/diffing.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n#include <wolv/container/interval_tree.hpp>\n\n#include <vector>\n#include <memory>\n\n\nEXPORT_MODULE namespace hex {\n\n    #if !defined(HEX_MODULE_EXPORT)\n        namespace prv { class Provider; }\n    #endif\n\n    /* Diffing Registry. Allows adding new diffing algorithms */\n    namespace ContentRegistry::Diffing {\n\n        enum class DifferenceType : u8 {\n            Match       = 0,\n            Insertion   = 1,\n            Deletion    = 2,\n            Mismatch    = 3\n        };\n\n        using DiffTree = wolv::container::IntervalTree<DifferenceType>;\n\n        class Algorithm {\n        public:\n            explicit Algorithm(UnlocalizedString unlocalizedName, UnlocalizedString unlocalizedDescription)\n                : m_unlocalizedName(std::move(unlocalizedName)),\n                  m_unlocalizedDescription(std::move(unlocalizedDescription)) { }\n\n            virtual ~Algorithm() = default;\n\n            virtual std::vector<DiffTree> analyze(prv::Provider *providerA, prv::Provider *providerB) const = 0;\n            virtual void drawSettings() { }\n\n            const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; }\n            const UnlocalizedString& getUnlocalizedDescription() const { return m_unlocalizedDescription; }\n\n        private:\n            UnlocalizedString m_unlocalizedName, m_unlocalizedDescription;\n        };\n\n        namespace impl {\n\n            const std::vector<std::unique_ptr<Algorithm>>& getAlgorithms();\n\n            void addAlgorithm(std::unique_ptr<Algorithm> &&hash);\n\n        }\n\n        /**\n         * @brief Adds a new hash\n         * @tparam T The hash type that extends hex::Hash\n         * @param args The arguments to pass to the constructor of the hash\n         */\n        template<typename T, typename ... Args>\n        void addAlgorithm(Args && ... args) {\n            impl::addAlgorithm(std::make_unique<T>(std::forward<Args>(args)...));\n        }\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/disassemblers.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n\n#include <string>\n#include <map>\n#include <memory>\n#include <functional>\n#include <optional>\n#include <span>\n\nEXPORT_MODULE namespace hex {\n\n    /* Disassembler Registry. Allows adding new disassembler architectures */\n    namespace ContentRegistry::Disassemblers {\n\n        struct Instruction {\n            u64 address;\n            u64 offset;\n            size_t size;\n            std::string bytes;\n            std::string mnemonic;\n            std::string operators;\n        };\n\n        class Architecture {\n        public:\n            explicit Architecture(std::string name) : m_name(std::move(name)) {}\n            virtual ~Architecture() = default;\n\n            virtual bool start() = 0;\n            virtual void end() = 0;\n\n            virtual std::optional<Instruction> disassemble(u64 imageBaseAddress, u64 instructionLoadAddress, u64 instructionDataAddress, std::span<const u8> code) = 0;\n            virtual void drawSettings() = 0;\n\n            [[nodiscard]] const std::string& getName() const { return m_name; }\n\n        private:\n            std::string m_name;\n        };\n\n        namespace impl {\n\n            using CreatorFunction = std::function<std::unique_ptr<Architecture>()>;\n\n            void addArchitectureCreator(CreatorFunction function);\n\n            const std::map<std::string, CreatorFunction>& getArchitectures();\n\n        }\n\n        template<std::derived_from<Architecture> T>\n        void add(auto && ...args) {\n            impl::addArchitectureCreator([...args = std::move(args)] {\n                return std::make_unique<T>(args...);\n            });\n        }\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/experiments.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n\n#include <map>\n#include <string>\n\nEXPORT_MODULE namespace hex {\n\n    /* Experiments Registry. Allows adding new experiments */\n    namespace ContentRegistry::Experiments {\n\n        namespace impl {\n\n            struct Experiment {\n                UnlocalizedString unlocalizedName, unlocalizedDescription;\n                bool enabled;\n            };\n\n            const std::map<std::string, Experiment>& getExperiments();\n        }\n\n        void addExperiment(\n            const std::string &experimentName,\n            const UnlocalizedString &unlocalizedName,\n            const UnlocalizedString &unlocalizedDescription = \"\"\n        );\n\n        void enableExperiement(const std::string &experimentName, bool enabled);\n\n        [[nodiscard]] bool isExperimentEnabled(const std::string &experimentName);\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/file_type_handler.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/helpers/fs.hpp>\n\n#include <vector>\n#include <string>\n#include <functional>\n\nEXPORT_MODULE namespace hex {\n\n    /* File Handler Registry. Allows adding handlers for opening files specific file types */\n    namespace ContentRegistry::FileTypeHandler {\n\n        namespace impl {\n\n            using Callback = std::function<bool(std::fs::path)>;\n            struct Entry {\n                std::vector<std::string> extensions;\n                Callback callback;\n            };\n\n            const std::vector<Entry>& getEntries();\n\n        }\n\n        /**\n         * @brief Adds a new file handler\n         * @param extensions The file extensions to handle\n         * @param callback The function to call to handle the file\n         */\n        void add(const std::vector<std::string> &extensions, const impl::Callback &callback);\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/hashes.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n\n#include <vector>\n#include <string>\n#include <memory>\n#include <functional>\n\n#include <nlohmann/json_fwd.hpp>\n\nEXPORT_MODULE namespace hex {\n\n    #if !defined(HEX_MODULE_EXPORT)\n        namespace prv { class Provider; }\n    #endif\n\n    /* Hash Registry. Allows adding new hashes to the Hash view */\n    namespace ContentRegistry::Hashes {\n\n        class Hash {\n        public:\n            explicit Hash(UnlocalizedString unlocalizedName) : m_unlocalizedName(std::move(unlocalizedName)) {}\n            virtual ~Hash() = default;\n\n            class Function {\n            public:\n                using Callback = std::function<std::vector<u8>(const Region&, prv::Provider *)>;\n\n                Function(Hash *type, std::string name, Callback callback)\n                    : m_type(type), m_name(std::move(name)), m_callback(std::move(callback)) {\n\n                }\n\n                [[nodiscard]] Hash *getType() { return m_type; }\n                [[nodiscard]] const Hash *getType() const { return m_type; }\n                [[nodiscard]] const std::string& getName() const { return m_name; }\n\n                std::vector<u8> get(const Region& region, prv::Provider *provider) const {\n                    return m_callback(region, provider);\n                }\n\n            private:\n                Hash *m_type;\n                std::string m_name;\n                Callback m_callback;\n            };\n\n            virtual void draw() { }\n            [[nodiscard]] virtual Function create(std::string name) = 0;\n\n            [[nodiscard]] virtual nlohmann::json store() const = 0;\n            virtual void load(const nlohmann::json &json) = 0;\n\n            [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const {\n                return m_unlocalizedName;\n            }\n\n        protected:\n            [[nodiscard]] Function create(const std::string &name, const Function::Callback &callback) {\n                return { this, name, callback };\n            }\n\n        private:\n            UnlocalizedString m_unlocalizedName;\n        };\n\n        namespace impl {\n\n            const std::vector<std::unique_ptr<Hash>>& getHashes();\n\n            void add(std::unique_ptr<Hash> &&hash);\n\n        }\n\n\n        /**\n         * @brief Adds a new hash\n         * @tparam T The hash type that extends hex::Hash\n         * @param args The arguments to pass to the constructor of the hash\n         */\n        template<typename T, typename ... Args>\n        void add(Args && ... args) {\n            impl::add(std::make_unique<T>(std::forward<Args>(args)...));\n        }\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/hex_editor.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <imgui.h>\n\n#include <hex/api/localization_manager.hpp>\n\n#include <functional>\n#include <memory>\n#include <string>\n#include <vector>\n#include <span>\n\nEXPORT_MODULE namespace hex {\n\n    /* Hex Editor Registry. Allows adding new functionality to the hex editor */\n    namespace ContentRegistry::HexEditor {\n\n        class DataVisualizer {\n        public:\n            DataVisualizer(UnlocalizedString unlocalizedName, u16 bytesPerCell, u16 maxCharsPerCell)\n                : m_unlocalizedName(std::move(unlocalizedName)),\n                  m_bytesPerCell(bytesPerCell),\n                  m_maxCharsPerCell(maxCharsPerCell) { }\n\n            virtual ~DataVisualizer() = default;\n\n            virtual void draw(u64 address, const u8 *data, size_t size, bool upperCase) = 0;\n            virtual bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) = 0;\n\n            [[nodiscard]] u16 getBytesPerCell() const { return m_bytesPerCell; }\n            [[nodiscard]] u16 getMaxCharsPerCell() const { return m_maxCharsPerCell; }\n\n            [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; }\n\n            [[nodiscard]] static int DefaultTextInputFlags();\n\n        protected:\n            bool drawDefaultScalarEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const;\n            bool drawDefaultTextEditingTextBox(u64 address, std::string &data, ImGuiInputTextFlags flags) const;\n\n        private:\n            UnlocalizedString m_unlocalizedName;\n            u16 m_bytesPerCell;\n            u16 m_maxCharsPerCell;\n        };\n\n        struct MiniMapVisualizer {\n            using Callback = std::function<void(u64, std::span<const u8>, std::vector<ImColor>&)>;\n\n            UnlocalizedString unlocalizedName;\n            Callback callback;\n        };\n\n        namespace impl {\n\n            void addDataVisualizer(std::shared_ptr<DataVisualizer> &&visualizer);\n\n            const std::vector<std::shared_ptr<DataVisualizer>>& getVisualizers();\n            const std::vector<std::shared_ptr<MiniMapVisualizer>>& getMiniMapVisualizers();\n\n        }\n\n        /**\n         * @brief Adds a new cell data visualizer\n         * @tparam T The data visualizer type that extends hex::DataVisualizer\n         * @param args The arguments to pass to the constructor of the data visualizer\n         */\n        template<std::derived_from<DataVisualizer> T, typename... Args>\n        void addDataVisualizer(Args &&...args) {\n            return impl::addDataVisualizer(std::make_shared<T>(std::forward<Args>(args)...));\n        }\n\n        /**\n         * @brief Gets a data visualizer by its unlocalized name\n         * @param unlocalizedName Unlocalized name of the data visualizer\n         * @return The data visualizer, or nullptr if it doesn't exist\n         */\n        std::shared_ptr<DataVisualizer> getVisualizerByName(const UnlocalizedString &unlocalizedName);\n\n        /**\n         * @brief Adds a new minimap visualizer\n         * @param unlocalizedName Unlocalized name of the minimap visualizer\n         * @param callback The callback that will be called to get the color of a line\n         */\n        void addMiniMapVisualizer(UnlocalizedString unlocalizedName, MiniMapVisualizer::Callback callback);\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/pattern_language.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <pl/pattern_language.hpp>\n\n#include <functional>\n#include <span>\n#include <string>\n#include <map>\n#include <vector>\n#include <mutex>\n\nEXPORT_MODULE namespace hex {\n\n    #if !defined(HEX_MODULE_EXPORT)\n        namespace prv { class Provider; }\n    #endif\n\n    /* Pattern Language Function Registry. Allows adding of new functions that may be used inside the pattern language */\n    namespace ContentRegistry::PatternLanguage {\n\n        namespace impl {\n\n            using VisualizerFunctionCallback = std::function<void(pl::ptrn::Pattern&, bool, std::span<const pl::core::Token::Literal>)>;\n\n            struct FunctionDefinition {\n                pl::api::Namespace ns;\n                std::string name;\n\n                pl::api::FunctionParameterCount parameterCount;\n                pl::api::FunctionCallback callback;\n\n                bool dangerous;\n            };\n\n            struct TypeDefinition {\n                pl::api::Namespace ns;\n                std::string name;\n\n                pl::api::FunctionParameterCount parameterCount;\n                pl::api::TypeCallback callback;\n            };\n\n            struct Visualizer {\n                pl::api::FunctionParameterCount parameterCount;\n                VisualizerFunctionCallback callback;\n            };\n\n            const std::map<std::string, Visualizer>& getVisualizers();\n            const std::map<std::string, Visualizer>& getInlineVisualizers();\n            const std::map<std::string, pl::api::PragmaHandler>& getPragmas();\n            const std::vector<FunctionDefinition>& getFunctions();\n            const std::vector<TypeDefinition>& getTypes();\n\n        }\n\n        /**\n         * @brief Provides access to the current provider's pattern language runtime\n         * @return Runtime\n         */\n        pl::PatternLanguage& getRuntime();\n\n        /**\n         * @brief Provides access to the current provider's pattern language runtime's lock\n         * @return Lock\n         */\n        std::mutex& getRuntimeLock();\n\n        /**\n         * @brief Configures the pattern language runtime using ImHex's default settings\n         * @param runtime The pattern language runtime to configure\n         * @param provider The provider to use for data access\n         */\n        void configureRuntime(pl::PatternLanguage &runtime, prv::Provider *provider);\n\n        /**\n         * @brief Adds a new pragma to the pattern language\n         * @param name The name of the pragma\n         * @param handler The handler that will be called when the pragma is encountered\n         */\n        void addPragma(const std::string &name, const pl::api::PragmaHandler &handler);\n\n        /**\n         * @brief Adds a new function to the pattern language\n         * @param ns The namespace of the function\n         * @param name The name of the function\n         * @param parameterCount The amount of parameters the function takes\n         * @param func The function callback\n         */\n        void addFunction(\n            const pl::api::Namespace &ns,\n            const std::string &name,\n            pl::api::FunctionParameterCount parameterCount,\n            const pl::api::FunctionCallback &func\n        );\n\n        /**\n         * @brief Adds a new dangerous function to the pattern language\n         * @note Dangerous functions are functions that require the user to explicitly allow them to be used\n         * @param ns The namespace of the function\n         * @param name The name of the function\n         * @param parameterCount The amount of parameters the function takes\n         * @param func The function callback\n         */\n        void addDangerousFunction(\n            const pl::api::Namespace &ns,\n            const std::string &name,\n            pl::api::FunctionParameterCount parameterCount,\n            const pl::api::FunctionCallback &func\n        );\n\n        /**\n         * @brief Adds a new type to the pattern language\n         * @param ns The namespace of the type\n         * @param name The name of the type\n         * @param parameterCount The amount of non-type template parameters the type takes\n         * @param func The type callback\n         */\n        void addType(\n            const pl::api::Namespace &ns,\n            const std::string &name,\n            pl::api::FunctionParameterCount parameterCount,\n            const pl::api::TypeCallback &func\n        );\n\n        /**\n         * @brief Adds a new visualizer to the pattern language\n         * @note Visualizers are extensions to the [[hex::visualize]] attribute, used to visualize data\n         * @param name The name of the visualizer\n         * @param function The function callback\n         * @param parameterCount The amount of parameters the function takes\n         */\n        void addVisualizer(\n            const std::string &name,\n            const impl::VisualizerFunctionCallback &function,\n            pl::api::FunctionParameterCount parameterCount\n        );\n\n        /**\n         * @brief Adds a new inline visualizer to the pattern language\n         * @note Inline visualizers are extensions to the [[hex::inline_visualize]] attribute, used to visualize data\n         * @param name The name of the visualizer\n         * @param function The function callback\n         * @param parameterCount The amount of parameters the function takes\n         */\n        void addInlineVisualizer(\n            const std::string &name,\n            const impl::VisualizerFunctionCallback &function,\n            pl::api::FunctionParameterCount parameterCount\n        );\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/provider.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n#include <hex/providers/provider.hpp>\n\n#include <functional>\n#include <memory>\n#include <string>\n#include <vector>\n\nEXPORT_MODULE namespace hex {\n\n    /* Provider Registry. Allows adding new data providers to be created from the UI */\n    namespace ContentRegistry::Provider {\n\n        namespace impl {\n\n            void addProviderName(const UnlocalizedString &unlocalizedName, const char *icon);\n\n            using ProviderCreationFunction = std::function<std::shared_ptr<prv::Provider>()>;\n            void add(const std::string &typeName, ProviderCreationFunction creationFunction);\n\n            struct Entry {\n                UnlocalizedString unlocalizedName;\n                const char *icon;\n            };\n\n            const std::vector<Entry>& getEntries();\n\n        }\n\n        /**\n         * @brief Adds a new provider to the list of providers\n         * @tparam T The provider type that extends hex::prv::Provider\n         * @param addToList Whether to display the provider in the Other Providers list in the welcome screen and File menu\n         */\n        template<std::derived_from<prv::Provider> T>\n        void add(bool addToList = true) {\n            const T provider;\n            auto typeName = provider.getTypeName();\n\n            impl::add(typeName, []() -> std::unique_ptr<prv::Provider> {\n                return std::make_unique<T>();\n            });\n\n            if (addToList)\n                impl::addProviderName(typeName, provider.getIcon());\n        }\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/reports.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <functional>\n#include <string>\n#include <vector>\n\nEXPORT_MODULE namespace hex {\n\n    #if !defined(HEX_MODULE_EXPORT)\n        namespace prv { class Provider; }\n    #endif\n\n    /* Reports Registry. Allows adding new sections to exported reports */\n    namespace ContentRegistry::Reports {\n\n        namespace impl {\n\n            using Callback = std::function<std::string(prv::Provider*)>;\n\n            struct ReportGenerator {\n                Callback callback;\n            };\n\n            const std::vector<ReportGenerator>& getGenerators();\n\n        }\n\n        void addReportProvider(impl::Callback callback);\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/settings.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n#include <hex/helpers/fs.hpp>\n\n#include <string>\n#include <vector>\n#include <memory>\n#include <functional>\n#include <optional>\n\n#include <nlohmann/json.hpp>\n#include <imgui.h>\n\nEXPORT_MODULE namespace hex {\n\n    /* Settings Registry. Allows adding of new entries into the ImHex preferences window. */\n    namespace ContentRegistry::Settings {\n\n        namespace Widgets {\n\n            class Widget {\n            public:\n                virtual ~Widget() = default;\n\n                virtual bool draw(const std::string &name) = 0;\n\n                virtual void load(const nlohmann::json &data) = 0;\n                virtual nlohmann::json store() = 0;\n\n                class Interface {\n                public:\n                    friend class Widget;\n\n                    Interface& requiresRestart() {\n                        m_requiresRestart = true;\n\n                        return *this;\n                    }\n\n                    Interface& setEnabledCallback(std::function<bool()> callback) {\n                        m_enabledCallback = std::move(callback);\n\n                        return *this;\n                    }\n\n                    Interface& setChangedCallback(std::function<void(Widget&)> callback) {\n                        m_changedCallback = std::move(callback);\n\n                        return *this;\n                    }\n\n                    Interface& setTooltip(const UnlocalizedString &tooltip) {\n                        m_tooltip = tooltip;\n\n                        return *this;\n                    }\n\n                    [[nodiscard]]\n                    Widget& getWidget() const {\n                        return *m_widget;\n                    }\n\n                private:\n                    explicit Interface(Widget *widget) : m_widget(widget) {}\n                    Widget *m_widget;\n\n                    bool m_requiresRestart = false;\n                    std::function<bool()> m_enabledCallback;\n                    std::function<void(Widget&)> m_changedCallback;\n                    std::optional<UnlocalizedString> m_tooltip;\n                };\n\n                [[nodiscard]]\n                bool doesRequireRestart() const {\n                    return m_interface.m_requiresRestart;\n                }\n\n                [[nodiscard]]\n                bool isEnabled() const {\n                    return !m_interface.m_enabledCallback || m_interface.m_enabledCallback();\n                }\n\n                [[nodiscard]]\n                const std::optional<UnlocalizedString>& getTooltip() const {\n                    return m_interface.m_tooltip;\n                }\n\n                void onChanged() {\n                    if (m_interface.m_changedCallback)\n                        m_interface.m_changedCallback(*this);\n                }\n\n                [[nodiscard]]\n                Interface& getInterface() {\n                    return m_interface;\n                }\n\n            private:\n                Interface m_interface = Interface(this);\n            };\n\n            class Checkbox : public Widget {\n            public:\n                explicit Checkbox(bool defaultValue) : m_value(defaultValue) { }\n\n                bool draw(const std::string &name) override;\n\n                void load(const nlohmann::json &data) override;\n                nlohmann::json store() override;\n\n                [[nodiscard]] bool isChecked() const { return m_value; }\n\n            protected:\n                bool m_value;\n            };\n\n            class SliderInteger : public Widget {\n            public:\n                SliderInteger(i32 defaultValue, i32 min, i32 max) : m_value(defaultValue), m_min(min), m_max(max) { }\n                bool draw(const std::string &name) override;\n\n                void load(const nlohmann::json &data) override;\n                nlohmann::json store() override;\n\n                [[nodiscard]] i32 getValue() const { return m_value; }\n\n            protected:\n                int m_value;\n                i32 m_min, m_max;\n            };\n\n            class SliderFloat : public Widget {\n            public:\n                SliderFloat(float defaultValue, float min, float max) : m_value(defaultValue), m_min(min), m_max(max) { }\n                bool draw(const std::string &name) override;\n\n                void load(const nlohmann::json &data) override;\n                nlohmann::json store() override;\n\n                [[nodiscard]] float getValue() const { return m_value; }\n\n            protected:\n                float m_value;\n                float m_min, m_max;\n            };\n\n            class SliderDataSize : public Widget {\n            public:\n                SliderDataSize(u64 defaultValue, u64 min, u64 max, u64 stepSize) : m_value(defaultValue), m_min(min), m_max(max), m_stepSize(stepSize) { }\n                bool draw(const std::string &name) override;\n\n                void load(const nlohmann::json &data) override;\n                nlohmann::json store() override;\n\n                [[nodiscard]] i32 getValue() const { return m_value; }\n\n            protected:\n                u64 m_value;\n                u64 m_min, m_max;\n                u64 m_stepSize;\n            };\n\n            class ColorPicker : public Widget {\n            public:\n                explicit ColorPicker(ImColor defaultColor, ImGuiColorEditFlags flags = 0);\n\n                bool draw(const std::string &name) override;\n\n                void load(const nlohmann::json &data) override;\n                nlohmann::json store() override;\n\n                [[nodiscard]] ImColor getColor() const;\n\n            protected:\n                std::array<float, 4> m_value = {}, m_defaultValue = {};\n                ImGuiColorEditFlags m_flags;\n            };\n\n            class DropDown : public Widget {\n            public:\n                explicit DropDown(const std::vector<std::string> &items, const std::vector<nlohmann::json> &settingsValues, const nlohmann::json &defaultItem) : m_items(items.begin(), items.end()), m_settingsValues(settingsValues), m_defaultItem(defaultItem) { }\n                explicit DropDown(const std::vector<UnlocalizedString> &items, const std::vector<nlohmann::json> &settingsValues, const nlohmann::json &defaultItem) : m_items(items), m_settingsValues(settingsValues), m_defaultItem(defaultItem) { }\n\n                bool draw(const std::string &name) override;\n\n                void load(const nlohmann::json &data) override;\n                nlohmann::json store() override;\n\n                [[nodiscard]]\n                const nlohmann::json& getValue() const;\n\n            protected:\n                std::vector<UnlocalizedString> m_items;\n                std::vector<nlohmann::json> m_settingsValues;\n                nlohmann::json m_defaultItem;\n\n                int m_value = -1;\n            };\n\n            class TextBox : public Widget {\n            public:\n                explicit TextBox(std::string defaultValue) : m_value(std::move(defaultValue)) { }\n\n                bool draw(const std::string &name) override;\n\n                void load(const nlohmann::json &data) override;\n                nlohmann::json store() override;\n\n                [[nodiscard]]\n                const std::string& getValue() const { return m_value; }\n\n            protected:\n                std::string m_value;\n            };\n\n            class FilePicker : public Widget {\n            public:\n                bool draw(const std::string &name) override;\n\n                void load(const nlohmann::json &data) override;\n                nlohmann::json store() override;\n\n                [[nodiscard]] const std::fs::path& getPath() const {\n                    return m_path;\n                }\n\n            protected:\n                std::fs::path m_path;\n            };\n\n            class Label : public Widget {\n            public:\n                bool draw(const std::string &name) override;\n\n                void load(const nlohmann::json &) override {}\n                nlohmann::json store() override { return {}; }\n            };\n\n            class Spacer : public Widget {\n            public:\n                bool draw(const std::string &name) override;\n\n                void load(const nlohmann::json &) override {}\n                nlohmann::json store() override { return {}; }\n            };\n\n        }\n\n        namespace impl {\n\n            struct Entry {\n                UnlocalizedString unlocalizedName;\n                std::unique_ptr<Widgets::Widget> widget;\n            };\n\n            struct SubCategory {\n                UnlocalizedString unlocalizedName;\n                std::vector<Entry> entries;\n            };\n\n            struct Category {\n                UnlocalizedString unlocalizedName;\n                UnlocalizedString unlocalizedDescription;\n                std::vector<SubCategory> subCategories;\n            };\n\n            void load();\n            void store();\n            void clear();\n\n            const std::vector<Category>& getSettings();\n            nlohmann::json& getSetting(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue);\n            const nlohmann::json& getSettingsData();\n\n            Widgets::Widget* add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedSubCategory, const UnlocalizedString &unlocalizedName, std::unique_ptr<Widgets::Widget> &&widget);\n\n            void printSettingReadError(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json::exception &e);\n\n            void runOnChangeHandlers(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &value);\n        }\n\n        template<std::derived_from<Widgets::Widget> T>\n        Widgets::Widget::Interface& add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedSubCategory, const UnlocalizedString &unlocalizedName, auto && ... args) {\n            return impl::add(\n                    unlocalizedCategory,\n                    unlocalizedSubCategory,\n                    unlocalizedName,\n                    std::make_unique<T>(std::forward<decltype(args)>(args)...)\n                )->getInterface();\n        }\n\n        void setCategoryDescription(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedDescription);\n\n        class SettingsValue {\n        public:\n            SettingsValue(nlohmann::json value) : m_value(std::move(value)) {}\n\n            template<typename T> requires (!(std::is_reference_v<T> || std::is_const_v<T>))\n            [[nodiscard]] T get(T defaultValue) const {\n                try {\n                    auto result = m_value;\n                    if (result.is_number() && std::same_as<T, bool>)\n                        result = m_value.get<int>() != 0;\n                    if (m_value.is_null())\n                        result = defaultValue;\n\n                    return result.get<T>();\n                } catch (const nlohmann::json::exception &) {\n                    return defaultValue;\n                }\n            }\n        private:\n            nlohmann::json m_value;\n        };\n\n        template<typename T> requires (!(std::is_reference_v<T> || std::is_const_v<T>))\n        [[nodiscard]] T read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, T defaultValue) {\n            auto setting = impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue);\n\n            try {\n                if (setting.is_number() && std::same_as<T, bool>)\n                    setting = setting.template get<int>() != 0;\n                if (setting.is_null())\n                    setting = defaultValue;\n\n                return setting.template get<T>();\n            } catch (const nlohmann::json::exception &e) {\n                impl::printSettingReadError(unlocalizedCategory, unlocalizedName, e);\n\n                return defaultValue;\n            }\n        }\n\n        template<typename T> requires (!(std::is_reference_v<T> || std::is_const_v<T>))\n        void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, T value) {\n            impl::getSetting(unlocalizedCategory, unlocalizedName, value) = value;\n            impl::runOnChangeHandlers(unlocalizedCategory, unlocalizedName, value);\n\n            impl::store();\n        }\n\n        using OnChangeCallback = std::function<void(const SettingsValue &)>;\n        u64 onChange(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const OnChangeCallback &callback);\n        void removeOnChangeHandler(u64 id);\n\n        using OnSaveCallback = std::function<void()>;\n        u64 onSave(const OnSaveCallback &callback);\n\n        template<typename T, wolv::type::StaticString UnlocalizedCategory, wolv::type::StaticString UnlocalizedName>\n        requires (!(std::is_reference_v<T> || std::is_const_v<T>))\n        class SettingsVariable {\n        public:\n            explicit(false) SettingsVariable(T defaultValue) noexcept : m_defaultValue(std::move(defaultValue)) { }\n\n            SettingsVariable(const SettingsVariable&) = delete;\n            SettingsVariable& operator=(const SettingsVariable&) = delete;\n\n            SettingsVariable(SettingsVariable&&) = delete;\n            SettingsVariable& operator=(SettingsVariable&&) = delete;\n\n            ~SettingsVariable() {\n                if (m_onChangeId > 0)\n                    removeOnChangeHandler(m_onChangeId);\n            }\n\n            [[nodiscard]] T get() const {\n                registerChangeHandler();\n                if (!m_value.has_value()) {\n                    m_value = read<T>(\n                        UnlocalizedCategory.value.data(),\n                        UnlocalizedName.value.data(),\n                        m_defaultValue\n                    );\n                }\n\n                return m_value.value_or(m_defaultValue);\n            }\n\n            void set(T value) {\n                registerChangeHandler();\n                write<T>(\n                    UnlocalizedCategory.value.data(),\n                    UnlocalizedName.value.data(),\n                    std::move(value)\n                );\n            }\n\n            explicit(false) operator T() const {\n                return get();\n            }\n\n            SettingsVariable& operator=(T value) {\n                set(std::move(value));\n                return *this;\n            }\n\n        private:\n            void registerChangeHandler() const {\n                if (m_onChangeId > 0)\n                    return;\n\n                m_onChangeId = onChange(UnlocalizedCategory.value.data(), UnlocalizedName.value.data(), [this](const SettingsValue &value) {\n                    m_value = value.get<T>(m_defaultValue);\n                });\n            }\n\n        private:\n            mutable std::optional<T> m_value;\n            T m_defaultValue;\n            mutable u64 m_onChangeId = 0;\n        };\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/tools.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n\n#include <functional>\n#include <vector>\n\nEXPORT_MODULE namespace hex {\n\n    /* Tools Registry. Allows adding new entries to the tools window */\n    namespace ContentRegistry::Tools {\n\n        namespace impl {\n\n            using Callback = std::function<void()>;\n\n            struct Entry {\n                UnlocalizedString unlocalizedName;\n                const char *icon;\n                Callback function;\n            };\n\n            const std::vector<Entry>& getEntries();\n\n        }\n\n        /**\n         * @brief Adds a new tool to the tools window\n         * @param unlocalizedName The unlocalized name of the tool\n         * @param function The function that will be called to draw the tool\n         */\n        void add(const UnlocalizedString &unlocalizedName, const char *icon, const impl::Callback &function);\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/user_interface.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/shortcut_manager.hpp>\n#include <hex/ui/imgui_imhex_extensions.h>\n\n#include <string>\n#include <vector>\n#include <map>\n#include <functional>\n\nEXPORT_MODULE namespace hex {\n\n    /* User Interface Registry. Allows adding new items to various interfaces */\n    namespace ContentRegistry::UserInterface {\n\n        struct Icon {\n            Icon(const char *glyph, ImGuiCustomCol color = ImGuiCustomCol(0x00)) : glyph(glyph), color(color) {}\n\n            std::string glyph;\n            ImGuiCustomCol color;\n        };\n\n        namespace impl {\n\n            using DrawCallback      = std::function<void()>;\n            using MenuCallback      = std::function<void()>;\n            using EnabledCallback   = std::function<bool()>;\n            using SelectedCallback  = std::function<bool()>;\n            using ClickCallback     = std::function<void()>;\n            using ToggleCallback    = std::function<void(bool)>;\n\n            struct MainMenuItem {\n                UnlocalizedString unlocalizedName;\n            };\n\n            struct MenuItem {\n                std::vector<UnlocalizedString> unlocalizedNames;\n                Icon icon;\n                Shortcut shortcut;\n                View *view;\n                MenuCallback callback;\n                EnabledCallback enabledCallback;\n                SelectedCallback selectedCallback;\n                i32 toolbarIndex;\n            };\n\n            struct SidebarItem {\n                std::string icon;\n                DrawCallback callback;\n                EnabledCallback enabledCallback;\n            };\n\n            struct TitleBarButton {\n                std::string icon;\n                ImGuiCustomCol color;\n                UnlocalizedString unlocalizedTooltip;\n                ClickCallback callback;\n            };\n\n            struct WelcomeScreenQuickSettingsToggle {\n                std::string onIcon, offIcon;\n                UnlocalizedString unlocalizedTooltip;\n                ToggleCallback callback;\n                mutable bool state;\n            };\n\n            constexpr static auto SeparatorValue = \"$SEPARATOR$\";\n            constexpr static auto SubMenuValue = \"$SUBMENU$\";\n            constexpr static auto TaskBarMenuValue = \"$TASKBAR$\";\n\n            const std::multimap<u32, MainMenuItem>& getMainMenuItems();\n\n            const std::multimap<u32, MenuItem>& getMenuItems();\n            const std::vector<MenuItem*>& getToolbarMenuItems();\n            std::multimap<u32, MenuItem>& getMenuItemsMutable();\n\n            const std::vector<DrawCallback>& getWelcomeScreenEntries();\n            const std::vector<DrawCallback>& getFooterItems();\n            const std::vector<DrawCallback>& getToolbarItems();\n            const std::vector<SidebarItem>& getSidebarItems();\n            const std::vector<TitleBarButton>& getTitlebarButtons();\n            const std::vector<WelcomeScreenQuickSettingsToggle>& getWelcomeScreenQuickSettingsToggles();\n\n        }\n\n        /**\n         * @brief Adds a new top-level main menu entry\n         * @param unlocalizedName The unlocalized name of the entry\n         * @param priority The priority of the entry. Lower values are displayed first\n         */\n        void registerMainMenuItem(const UnlocalizedString &unlocalizedName, u32 priority);\n\n        /**\n         * @brief Adds a new main menu entry\n         * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries\n         * @param icon The icon to use for the entry\n         * @param priority The priority of the entry. Lower values are displayed first\n         * @param shortcut The shortcut to use for the entry\n         * @param function The function to call when the entry is clicked\n         * @param enabledCallback The function to call to determine if the entry is enabled\n         * @param view The view to use for the entry. If nullptr, the shortcut will work globally\n         */\n        void addMenuItem(\n            const std::vector<UnlocalizedString> &unlocalizedMainMenuNames,\n            const Icon &icon,\n            u32 priority,\n            const Shortcut &shortcut,\n            const impl::MenuCallback &function,\n            const impl::EnabledCallback& enabledCallback, View *view\n        );\n\n        /**\n         * @brief Adds a new main menu entry\n         * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries\n         * @param icon The icon to use for the entry\n         * @param priority The priority of the entry. Lower values are displayed first\n         * @param shortcut The shortcut to use for the entry\n         * @param function The function to call when the entry is clicked\n         * @param enabledCallback The function to call to determine if the entry is enabled\n         * @param selectedCallback The function to call to determine if the entry is selected\n         * @param view The view to use for the entry. If nullptr, the shortcut will work globally\n         */\n        void addMenuItem(\n            const std::vector<UnlocalizedString> &unlocalizedMainMenuNames,\n            const Icon &icon,\n            u32 priority,\n            Shortcut shortcut,\n            const impl::MenuCallback &function,\n            const impl::EnabledCallback& enabledCallback = []{ return true; },\n            const impl::SelectedCallback &selectedCallback = []{ return false; },\n            View *view = nullptr\n        );\n\n        /**\n         * @brief Adds a new main menu entry\n         * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries\n         * @param priority The priority of the entry. Lower values are displayed first\n         * @param shortcut The shortcut to use for the entry\n         * @param function The function to call when the entry is clicked\n         * @param enabledCallback The function to call to determine if the entry is enabled\n         * @param selectedCallback The function to call to determine if the entry is selected\n         * @param view The view to use for the entry. If nullptr, the shortcut will work globally\n         */\n        void addMenuItem(\n            const std::vector<UnlocalizedString> &unlocalizedMainMenuNames,\n            u32 priority,\n            const Shortcut &shortcut,\n            const impl::MenuCallback &function,\n            const impl::EnabledCallback& enabledCallback = []{ return true; },\n            const impl::SelectedCallback &selectedCallback = []{ return false; },\n            View *view = nullptr\n        );\n\n        /**\n         * @brief Adds a new main menu sub-menu entry\n         * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries\n         * @param priority The priority of the entry. Lower values are displayed first\n         * @param function The function to call when the entry is clicked\n         * @param enabledCallback The function to call to determine if the entry is enabled\n         * @param view The view to use for the entry. If nullptr, the item will always be visible\n         * @param showOnWelcomeScreen If this entry should be shown on the welcome screen\n         */\n        void addMenuItemSubMenu(\n            std::vector<UnlocalizedString> unlocalizedMainMenuNames,\n            u32 priority,\n            const impl::MenuCallback &function,\n            const impl::EnabledCallback& enabledCallback = []{ return true; },\n            View *view = nullptr,\n            bool showOnWelcomeScreen = false\n        );\n\n        /**\n         * @brief Adds a new main menu sub-menu entry\n         * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries\n         * @param icon The icon to use for the entry\n         * @param priority The priority of the entry. Lower values are displayed first\n         * @param function The function to call when the entry is clicked\n         * @param enabledCallback The function to call to determine if the entry is enabled\n         * @param view The view to use for the entry. If nullptr, the item will always be visible\n         * @param showOnWelcomeScreen If this entry should be shown on the welcome screen\n         */\n        void addMenuItemSubMenu(\n            std::vector<UnlocalizedString> unlocalizedMainMenuNames,\n            const char *icon,\n            u32 priority,\n            const impl::MenuCallback &function,\n            const impl::EnabledCallback& enabledCallback = []{ return true; },\n            View *view = nullptr,\n            bool showOnWelcomeScreen = false\n        );\n\n\n        /**\n         * @brief Adds a new main menu separator\n         * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries\n         * @param priority The priority of the entry. Lower values are displayed first\n         * @param view The view to use for the entry. If nullptr, the item will always be visible\n         */\n        void addMenuItemSeparator(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, View *view = nullptr);\n\n        /**\n         * @brief Adds a new main menu entry\n         * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries\n         * @param priority The priority of the entry. Lower values are displayed first\n         * @param function The function to call when the entry is clicked\n         * @param enabledCallback The function to call to determine if the entry is enabled\n         */\n        void addTaskBarMenuItem(\n            std::vector<UnlocalizedString> unlocalizedMainMenuNames,\n            u32 priority,\n            const impl::MenuCallback &function,\n            const impl::EnabledCallback& enabledCallback\n        );\n\n        /**\n         * @brief Adds a new welcome screen entry\n         * @param function The function to call to draw the entry\n         */\n        void addWelcomeScreenEntry(const impl::DrawCallback &function);\n\n        /**\n         * @brief Adds a new footer item\n         * @param function The function to call to draw the item\n         */\n        void addFooterItem(const impl::DrawCallback &function);\n\n        /**\n         * @brief Adds a new toolbar item\n         * @param function The function to call to draw the item\n         */\n        void addToolbarItem(const impl::DrawCallback &function);\n\n        /**\n         * @brief Adds a menu item to the toolbar\n         * @param unlocalizedNames Unlocalized name of the menu item\n         * @param color Color of the toolbar icon\n         */\n        void addMenuItemToToolbar(const std::vector<UnlocalizedString> &unlocalizedNames, ImGuiCustomCol color);\n\n        /**\n         * @brief Reconstructs the toolbar items list after they have been modified\n         */\n        void updateToolbarItems();\n\n        /**\n         * @brief Adds a new sidebar item\n         * @param icon The icon to use for the item\n         * @param function The function to call to draw the item\n         * @param enabledCallback The function\n         */\n        void addSidebarItem(\n            const std::string &icon,\n            const impl::DrawCallback &function,\n            const impl::EnabledCallback &enabledCallback = []{ return true; }\n        );\n\n        /**\n         * @brief Adds a new title bar button\n         * @param icon The icon to use for the button\n         * @param color The color of the icon\n         * @param unlocalizedTooltip The unlocalized tooltip to use for the button\n         * @param function The function to call when the button is clicked\n         */\n        void addTitleBarButton(\n            const std::string &icon,\n            ImGuiCustomCol color,\n            const UnlocalizedString &unlocalizedTooltip,\n            const impl::ClickCallback &function\n        );\n\n        /**\n         * @brief Adds a new welcome screen quick settings toggle\n         * @param icon The icon to use for the button\n         * @param unlocalizedTooltip The unlocalized tooltip to use for the button\n         * @param defaultState The default state of the toggle\n         * @param function The function to call when the button is clicked\n         */\n        void addWelcomeScreenQuickSettingsToggle(\n            const std::string &icon,\n            const UnlocalizedString &unlocalizedTooltip,\n            bool defaultState,\n            const impl::ToggleCallback &function\n        );\n\n        /**\n         * @brief Adds a new welcome screen quick settings toggle\n         * @param onIcon The icon to use for the button when it's on\n         * @param offIcon The icon to use for the button when it's off\n         * @param unlocalizedTooltip The unlocalized tooltip to use for the button\n         * @param defaultState The default state of the toggle\n         * @param function The function to call when the button is clicked\n         */\n        void addWelcomeScreenQuickSettingsToggle(\n            const std::string &onIcon,\n            const std::string &offIcon,\n            const UnlocalizedString &unlocalizedTooltip,\n            bool defaultState,\n            const impl::ToggleCallback &function\n        );\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/content_registry/views.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n#include <hex/ui/view.hpp>\n\n#include <map>\n#include <memory>\n#include <functional>\n\nEXPORT_MODULE namespace hex {\n\n    /* View Registry. Allows adding of new windows */\n    namespace ContentRegistry::Views {\n\n        namespace impl {\n\n            void add(std::unique_ptr<View> &&view);\n            void setFullScreenView(std::unique_ptr<View> &&view);\n\n            const std::map<UnlocalizedString, std::unique_ptr<View>>& getEntries();\n            const std::unique_ptr<View>& getFullScreenView();\n\n        }\n\n        /**\n         * @brief Adds a new view to ImHex\n         * @tparam T The custom view class that extends View\n         * @tparam Args Arguments types\n         * @param args Arguments passed to the constructor of the view\n         */\n        template<std::derived_from<View> T, typename... Args>\n        void add(Args &&...args) {\n            return impl::add(std::make_unique<T>(std::forward<Args>(args)...));\n        }\n\n        /**\n         * @brief Sets a view as a full-screen view. This will cause the view to take up the entire ImHex window\n         * @tparam T The custom view class that extends View\n         * @tparam Args Arguments types\n         * @param args Arguments passed to the constructor of the view\n         */\n        template<std::derived_from<View> T, typename... Args>\n        void setFullScreenView(Args &&...args) {\n            return impl::setFullScreenView(std::make_unique<T>(std::forward<Args>(args)...));\n        }\n\n        /**\n         * @brief Gets a view by its unlocalized name\n         * @param unlocalizedName The unlocalized name of the view\n         * @return The view if it exists, nullptr otherwise\n         */\n        View* getViewByName(const UnlocalizedString &unlocalizedName);\n\n        /**\n         * @brief Gets the currently focused view\n         * @return The view that is focused right now. nullptr if none is focused\n         */\n        View* getFocusedView();\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/event_manager.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <algorithm>\n#include <functional>\n#include <list>\n#include <mutex>\n#include <map>\n#include <string_view>\n\n#include <hex/helpers/logger.hpp>\n\n#include <wolv/types/type_name.hpp>\n\n#define EVENT_DEF_IMPL(event_name, event_name_string, should_log, ...)                                                          \\\n    struct event_name final : public hex::impl::Event<__VA_ARGS__> {                                                            \\\n        constexpr static auto Id = [] { return hex::impl::EventId(event_name_string); }();                                      \\\n        constexpr static auto ShouldLog = (should_log);                                                                         \\\n        explicit event_name(Callback func) noexcept : Event(std::move(func)) { }                                                \\\n                                                                                                                                \\\n        static EventManager::EventList::iterator subscribe(Event::Callback function) {                                          \\\n            return EventManager::subscribe<event_name>(std::move(function));                                                    \\\n        }                                                                                                                       \\\n        template<typename = void>                                                                                               \\\n        static EventManager::EventList::iterator subscribe(Event::BaseCallback function)                                        \\\n        requires (!std::same_as<Event::Callback, Event::BaseCallback>) {                                                        \\\n            return EventManager::subscribe<event_name>([function = std::move(function)](auto && ...) { function(); });          \\\n        }                                                                                                                       \\\n        static void subscribe(void *token, Event::Callback function) {                                                          \\\n            EventManager::subscribe<event_name>(token, std::move(function));                                                    \\\n        }                                                                                                                       \\\n        template<typename = void>                                                                                               \\\n        static void subscribe(void *token, Event::BaseCallback function)                                                        \\\n        requires (!std::same_as<Event::Callback, Event::BaseCallback>) {                                                        \\\n            return EventManager::subscribe<event_name>(token, [function = std::move(function)](auto && ...) { function(); });   \\\n        }                                                                                                                       \\\n        static void unsubscribe(const EventManager::EventList::iterator &token) noexcept {                                      \\\n            EventManager::unsubscribe(token);                                                                                   \\\n        }                                                                                                                       \\\n        static void unsubscribe(void *token) noexcept {                                                                         \\\n            EventManager::unsubscribe<event_name>(token);                                                                       \\\n        }                                                                                                                       \\\n        static void post(auto &&...args) {                                                                                      \\\n            EventManager::post<event_name>(std::forward<decltype(args)>(args)...);                                              \\\n        }                                                                                                                       \\\n    }\n\n#define EVENT_DEF(event_name, ...)          EVENT_DEF_IMPL(event_name, #event_name, true, __VA_ARGS__)\n#define EVENT_DEF_NO_LOG(event_name, ...)   EVENT_DEF_IMPL(event_name, #event_name, false, __VA_ARGS__)\n\n\nEXPORT_MODULE namespace hex {\n\n    namespace impl {\n\n        class EventId {\n        public:\n            explicit constexpr EventId(const char *eventName) {\n                m_hash = 0x811C'9DC5;\n                for (const char c : std::string_view(eventName)) {\n                    m_hash = (m_hash >> 5) | (m_hash << 27);\n                    m_hash ^= c;\n                }\n            }\n\n            constexpr bool operator==(const EventId &other) const {\n                return m_hash == other.m_hash;\n            }\n\n            constexpr auto operator<=>(const EventId &other) const {\n                return m_hash <=> other.m_hash;\n            }\n\n        private:\n            u32 m_hash;\n        };\n\n        struct EventBase {\n            EventBase() noexcept = default;\n            virtual ~EventBase() = default;\n        };\n\n        template<typename... Params>\n        struct Event : EventBase {\n            using Callback = std::function<void(Params...)>;\n            using BaseCallback = std::function<void()>;\n\n            explicit Event(Callback func) noexcept : m_func(std::move(func)) { }\n\n            template<typename E>\n            void call(auto&& ... params) const {\n                #if defined(DEBUG)\n                    m_func(std::forward<decltype(params)>(params)...);\n                #else\n                    try {\n                        m_func(std::forward<decltype(params)>(params)...);\n                    } catch (const std::exception &e) {\n                        log::error(\"An exception occurred while handling event {}: {}\", wolv::type::getTypeName<E>(), e.what());\n                        throw;\n                    }\n                #endif\n            }\n\n        private:\n            Callback m_func;\n        };\n\n        template<typename T>\n        concept EventType = std::derived_from<T, EventBase>;\n\n    }\n\n\n    /**\n     * @brief The EventManager allows subscribing to and posting events to different parts of the program.\n     * To create a new event, use the EVENT_DEF macro. This will create a new event type with the given name and parameters.\n     * Events should be created in an `events_*.hpp` category file under the `events` folder, and never directly here.\n     */\n    class EventManager {\n    public:\n        using EventList = std::multimap<impl::EventId, std::unique_ptr<impl::EventBase>>;\n\n        /**\n         * @brief Subscribes to an event\n         * @tparam E Event\n         * @param function Function to call when the event is posted\n         * @return Token to unsubscribe from the event\n         */\n        template<impl::EventType E>\n        static EventList::iterator subscribe(E::Callback function) {\n            std::lock_guard lock(getEventMutex());\n\n            auto &events = getEvents();\n            return events.insert({ E::Id, std::make_unique<E>(function) });\n        }\n\n        /**\n         * @brief Subscribes to an event\n         * @tparam E Event\n         * @param token Unique token to register the event to. Later required to unsubscribe again\n         * @param function Function to call when the event is posted\n         */\n        template<impl::EventType E>\n        static void subscribe(void *token, E::Callback function) {\n            std::lock_guard lock(getEventMutex());\n\n            if (isAlreadyRegistered(token, E::Id)) {\n                log::fatal(\"The token '{}' has already registered the same event ('{}')\", token, wolv::type::getTypeName<E>());\n                return;\n            }\n\n            getTokenStore().insert({ token, subscribe<E>(std::move(function)) });\n        }\n\n        /**\n         * @brief Unsubscribes from an event\n         * @param token Token returned by subscribe\n         */\n        static void unsubscribe(const EventList::iterator &token) noexcept {\n            std::lock_guard lock(getEventMutex());\n\n            getEvents().erase(token);\n        }\n\n        /**\n         * @brief Unsubscribes from an event\n         * @tparam E Event\n         * @param token Token passed to subscribe\n         */\n        template<impl::EventType E>\n        static void unsubscribe(void *token) noexcept {\n            std::lock_guard lock(getEventMutex());\n\n            unsubscribe(token, E::Id);\n        }\n\n        /**\n         * @brief Posts an event to all subscribers of it\n         * @tparam E Event\n         * @param args Arguments to pass to the event\n         */\n        template<impl::EventType E>\n        static void post(auto && ...args) {\n            std::lock_guard lock(getEventMutex());\n\n            const auto &[begin, end] = getEvents().equal_range(E::Id);\n            for (auto it = begin; it != end; ++it) {\n                const auto &[id, event] = *it;\n                (*static_cast<E *const>(event.get())).template call<E>(std::forward<decltype(args)>(args)...);\n            }\n\n            #if defined (DEBUG)\n                if constexpr (E::ShouldLog)\n                    log::debug(\"Event posted: '{}'\", wolv::type::getTypeName<E>());\n            #endif\n        }\n\n        /**\n         * @brief Unsubscribe all subscribers from all events\n         */\n        static void clear() noexcept {\n            std::lock_guard lock(getEventMutex());\n\n            getEvents().clear();\n            getTokenStore().clear();\n        }\n\n    private:\n        static std::multimap<void *, EventList::iterator>& getTokenStore();\n        static EventList& getEvents();\n        static std::recursive_mutex& getEventMutex();\n\n        static bool isAlreadyRegistered(void *token, impl::EventId id);\n        static void unsubscribe(void *token, impl::EventId id);\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/events/events_gui.hpp",
    "content": "#pragma once\n\n#include <hex/api/event_manager.hpp>\n\n/* Forward declarations */\nstruct GLFWwindow;\nusing ImGuiID = unsigned int;\nnamespace hex { class View; }\n\n/* GUI events definitions */\nnamespace hex {\n    /**\n     * @brief Signals a newly opened view\n     *\n     * This event is sent when the view has just been opened by the Window manager.\n     *\n     * FIXME: This is currently only used for the introduction tutorial.\n     *  If the event's only purpose is this, maybe rename it?\n     *\n     * @param view the new view reference\n     */\n    EVENT_DEF(EventViewOpened, View*);\n\n    /**\n     * @brief Signals a newly closed view\n     *\n     * This event is sent when the view has just been closed.\n     *\n     * @param view the closed view reference\n     */\n    EVENT_DEF(EventViewClosed, View*);\n\n    /**\n     * @brief Signals a change in the DPI scale.\n     *\n     * This event is called once at startup to signal native scale definition (by passing the same value twice).\n     * On Windows OS, this event can also be posted if the window DPI has been changed.\n     *\n     * @param oldScale the old scale\n     * @param newScale the current scale that's now in use\n     */\n    EVENT_DEF(EventDPIChanged, float, float);\n\n    /**\n     * @brief Signals the focus of the ImHex main window.\n     *\n     * This is directly tied as a GLFW window focus callback, and will be called accordingly when GLFW detects\n     * a change in focus.\n     *\n     * @param isFocused true if the window is focused\n     */\n    EVENT_DEF(EventWindowFocused, bool);\n\n    /**\n     * @brief Signals a window being closed.\n     *\n     * Allows reactive clean up of running tasks, and prevents ImHex from closing\n     * by displaying an exit confirmation popup.\n     *\n     * @param window The window reference\n     */\n    EVENT_DEF(EventWindowClosing, GLFWwindow*);\n\n    /**\n     * @brief Informs that the main window is deinitializing\n     *\n     * Allows for lifecycle cleanup before ImHex shutdown.\n     *\n     * @param window The window reference\n     */\n    EVENT_DEF(EventWindowDeinitializing, GLFWwindow*);\n\n    /**\n     * @brief Signals a theme change in the host OS\n     *\n     * Allows ImHex to react to OS theme changes dynamically during execution.\n     */\n    EVENT_DEF(EventOSThemeChanged);\n\n}\n\n/* silent (no-logging) GUI events definitions */\nnamespace hex {\n\n    /**\n     * @brief Signals the start of a new ImGui frame\n     */\n    EVENT_DEF_NO_LOG(EventFrameBegin);\n\n    /**\n     * @brief Signals the end of an ImGui frame\n     */\n    EVENT_DEF_NO_LOG(EventFrameEnd);\n\n    /**\n     * @brief Windows OS: Sets the taskbar icon state\n     *\n     * This event is used on Windows OS to display progress through the taskbar icon (the famous \"green loading bar\"\n     * in the taskbar).\n     *\n     * @param progressState the progress state (converted from the TaskProgressState enum)\n     * @param progressType the type of progress (converted from the TaskProgressType enum)\n     * @param percentage actual progress percentage (expected from 0 to 100)\n     *\n     * @see hex::ImHexApi::System::TaskProgressState\n     * @see hex::ImHexApi::System::TaskProgressType\n     */\n    EVENT_DEF_NO_LOG(EventSetTaskBarIconState, u32, u32, u32);\n\n    /**\n     * @brief Informs of an ImGui element being rendered\n     *\n     * @param elementId the element's ID\n     * @param boundingBox the bounding box (composed of 4 floats)\n     */\n    EVENT_DEF_NO_LOG(EventImGuiElementRendered, ImGuiID, const std::array<float, 4>&);\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/api/events/events_interaction.hpp",
    "content": "#pragma once\n\n#include <hex/api/imhex_api/bookmarks.hpp>\n#include <hex/api/imhex_api/hex_editor.hpp>\n#include <hex/api/event_manager.hpp>\n#include <hex/helpers/patches.hpp>\n\n/* Forward declarations */\nnamespace hex { class Achievement; }\n\n/* Interaction events definitions */\nnamespace hex {\n\n    /**\n     * @brief Signals a file was loaded\n     *\n     * FIXME: this event is unused and should be scrapped.\n     *\n     * @param path the loaded file's path\n     */\n    EVENT_DEF(EventFileLoaded, std::fs::path);\n\n    /**\n     * @brief Signals a change in the current data\n     *\n     * Enables provider reaction to data change, especially the data inspector.\n     *\n     * This is caused by the following:\n     * - an explicit provider reload, requested by the user (Ctrl+R)\n     * - any user action that results in the creation of an \"undo\" stack action (generally a data modification)\n     *\n     * @param provider the Provider subject to the data change\n     */\n    EVENT_DEF(EventDataChanged, prv::Provider *);\n\n    /**\n     * @brief Signals a change in highlighting\n     *\n     * The event's only purpose is for the Hex editor to clear highlights when receiving this event.\n     */\n    EVENT_DEF(EventHighlightingChanged);\n\n    /**\n     * @brief Informs of a provider region being selected\n     *\n     * This is very generally used to signal user actions that select a specific region within the provider.\n     * It is also used to pass on regions when the provider changes.\n     *\n     * @param providerRegion the provider-aware region being selected\n     */\n    EVENT_DEF(EventRegionSelected, ImHexApi::HexEditor::ProviderRegion);\n\n    /**\n     * @brief Signals a theme change\n     *\n     * On Windows OS, this is used to reflect the theme color onto the window frame.\n     */\n    EVENT_DEF(EventThemeChanged);\n\n    /**\n     * @brief Signals that a bookmark was created\n     *\n     * For now, this event's only purpose is to unlock an achievement.\n     *\n     * @param entry the new bookmark\n     */\n    EVENT_DEF(EventBookmarkCreated, ImHexApi::Bookmarks::Entry&);\n\n    /**\n     * @brief Called upon creation of an IPS patch.\n     * As for now, the event only serves a purpose for the achievement unlock.\n     *\n     * @param data the pointer to the patch content's start\n     * @param size the patch data size\n     * @param kind the patch's kind\n     */\n    EVENT_DEF(EventPatchCreated, const u8*, u64, const PatchKind);\n\n    /**\n     * @brief Signals the beginning of evaluation of the current pattern\n     *\n     * This allows resetting the drawer view for the pattern data while we wait for the execution completion.\n     */\n    EVENT_DEF(EventPatternEvaluating);\n\n    /**\n     * @brief Signals the completion of the pattern evaluation\n     *\n     * This causes another reset in the drawer view, to refresh the table displayed to the user.\n     *\n     * @param code the execution's status code\n     */\n    EVENT_DEF(EventPatternExecuted, const std::string&);\n\n    /**\n     * @brief Denotes when pattern editor has changed\n     *\n     * FIXME: this event is unused and should be scrapped.\n     */\n    EVENT_DEF(EventPatternEditorChanged, const std::string&);\n\n    /**\n     * @brief Signals that a Content Store item was downloaded\n     *\n     * FIXME: this event is unused and should be scrapped.\n     *\n     * @param path the item's path on the filesystem\n     */\n    EVENT_DEF(EventStoreContentDownloaded, const std::fs::path&);\n\n    /**\n     * @brief Signals the removal of a Content Store item\n     *\n     * Note: at the time of the event firing, the item has already been removed from the filesystem.\n     *\n     * FIXME: this event is unused and should be scrapped.\n     *\n     * @param path the item's old file path where it used to be in the filesystem\n     */\n    EVENT_DEF(EventStoreContentRemoved, const std::fs::path&);\n\n    /**\n     * @brief Signals the unlocking of an achievement\n     *\n     * This is used by the achievement manager to refresh the achievement display, as well as store progress to\n     * the appropriate storage file.\n     *\n     * @param achievement the achievement that was unlocked\n     */\n    EVENT_DEF(EventAchievementUnlocked, const Achievement&);\n\n    /**\n     * @brief Signals a click on the search box\n     *\n     * As there are different behaviours depending on the click (left or right) done by the user,\n     * this allows the consequences of said click to be registered in their own components.\n     *\n     * @param button the ImGuiMouseButton's value\n     */\n    EVENT_DEF(EventSearchBoxClicked, u32);\n\n    /**\n     * @brief Updates on whether a file is being dragged into ImHex\n     *\n     * Allows ImGUi to display a file dragging information on screen when a file is being dragged.\n     *\n     * @param isFileDragged true if a file is being dragged\n     */\n    EVENT_DEF(EventFileDragged, bool);\n\n    /**\n     * @brief Triggers loading when a file is dropped\n     *\n     * The event fires when a file is dropped into ImHex, which passes it to file handlers to load it.\n     *\n     * @param path the dropped file's path\n     */\n    EVENT_DEF(EventFileDropped, std::fs::path);\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/events/events_lifecycle.hpp",
    "content": "#pragma once\n\n#include <hex/api/event_manager.hpp>\n#include <hex/helpers/semantic_version.hpp>\n\nstruct ImGuiTestEngine;\n\n/* Lifecycle events definitions */\nnamespace hex {\n\n    /**\n     * @brief Called when Imhex finished startup, and will enter the main window rendering loop\n     */\n    EVENT_DEF(EventImHexStartupFinished);\n\n    /**\n     * @brief Called when the user presses the close button on the main window\n     *\n     * This is currently only used and implemented on macOS\n     */\n    EVENT_DEF(EventCloseButtonPressed);\n\n    /**\n     * @brief Called when ImHex is closing, to trigger the last shutdown hooks\n     *\n     * This is the last event to fire before complete graceful shutdown.\n     */\n    EVENT_DEF(EventImHexClosing);\n\n    /**\n     * @brief Signals that it's ImHex first launch ever\n     *\n     * This event allows for the launch of the ImHex tutorial (also called Out of Box experience).\n     */\n    EVENT_DEF(EventFirstLaunch);\n\n    /**\n     * FIXME: this event is unused and should be scrapped.\n     */\n    EVENT_DEF(EventAnySettingChanged);\n\n    /**\n     * @brief Ensures correct plugin cleanup on crash\n     *\n     * This event is fired when catching an unexpected error that cannot be recovered and\n     * which forces Imhex to close immediately.\n     *\n     * Subscribing to this event ensures that the plugin can correctly clean up any mission-critical tasks\n     * before forceful shutdown.\n     *\n     * @param signal the POSIX signal code\n     */\n    EVENT_DEF(EventAbnormalTermination, int);\n\n    /**\n     * @brief Informs of the ImHex versions (and difference, if any)\n     *\n     * Called on every startup to inform subscribers of the two versions picked up:\n     * - the version of the previous launch, gathered from the settings file\n     * - the current version, gathered directly from C++ code\n     *\n     * In most cases, and unless ImHex was updated, the two parameters will be the same.\n     *\n     * FIXME: Maybe rename the event to signal a startup information, instead of the misleading\n     *  title that the event could be fired when ImHex detects that it was updated since last launch?\n     *\n     * @param previousLaunchVersion ImHex's version during the previous launch\n     * @param currentVersion ImHex's current version for this startup\n     */\n    EVENT_DEF(EventImHexUpdated, SemanticVersion, SemanticVersion);\n\n    /**\n     * @brief Called when ImHex managed to catch an error in a general try/catch to prevent/recover from a crash\n    */\n    EVENT_DEF(EventCrashRecovered, const std::exception &);\n\n    /**\n     * @brief Called when a project has been loaded\n     */\n    EVENT_DEF(EventProjectOpened);\n\n    /**\n     * @brief Called when a project is saved/saved as\n     */\n    EVENT_DEF(EventProjectSaved);\n\n    /**\n     * @brief Called when a native message was received from another ImHex instance\n     * @param rawData Raw bytes received from other instance\n     */\n    EVENT_DEF(EventNativeMessageReceived, std::vector<u8>);\n\n    /**\n     * @brief Called when ImGui is initialized to register tests\n     * @param testEngine Pointer to the ImGui Test Engine Context\n     */\n    EVENT_DEF(EventRegisterImGuiTests, ImGuiTestEngine*);\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/events/events_provider.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <hex/api/event_manager.hpp>\n\n/* Provider events definitions */\nnamespace hex {\n\n    namespace prv {\n        class Provider;\n    }\n\n    /**\n     * @brief Called when the provider is created.\n     * This event is responsible for (optionally) initializing the provider and calling EventProviderOpened\n     * (although the event can also be called manually without problem)\n     */\n    EVENT_DEF(EventProviderCreated, std::shared_ptr<prv::Provider>);\n\n    /**\n     * @brief Called as a continuation of EventProviderCreated\n     * this event is normally called immediately after EventProviderCreated successfully initialized the provider.\n     * If no initialization (Provider::skipLoadInterface() has been set), this event should be called manually\n     * If skipLoadInterface failed, this event is not called\n     *\n     * @note this is not related to Provider::open()\n     */\n    EVENT_DEF(EventProviderOpened,  prv::Provider *);\n\n    /**\n     * @brief Signals a change in provider (in-place)\n     *\n     * Note: if the provider was deleted, the new (\"current\") provider will be `nullptr`\n     *\n     * @param oldProvider the old provider\n     * @param currentProvider the current provider\n     */\n    EVENT_DEF(EventProviderChanged, prv::Provider *, prv::Provider *);\n\n    /**\n     * @brief Signals that a provider was saved\n     *\n     * @param provider the saved provider\n     */\n    EVENT_DEF(EventProviderSaved,   prv::Provider *);\n\n    /**\n     * @brief Signals a provider is closing\n     *\n     * FIXME: as for now, this behaves as a request more than an event. Also, the boolean is always set to true,\n     *  and serves no purpose. This should be moved into the Provider requests section and declared accordingly.\n     *\n     * @param provider the closing provider\n     * @param shouldClose whether the provider should close\n     */\n    EVENT_DEF(EventProviderClosing, prv::Provider *, bool *);\n\n    /**\n     * @brief Signals that a provider was closed\n     *\n     * As this is a closure information broadcast, the provider should generally not be accessed, as it could\n     * result in problems.\n     *\n     * @param provider the now-closed provider\n     */\n    EVENT_DEF(EventProviderClosed,  prv::Provider *);\n\n    /**\n     * @brief Signals that a provider is being deleted\n     *\n     * Provider's data should not be accessed.\n     *\n     * @param provider the provider\n     */\n    EVENT_DEF(EventProviderDeleted, prv::Provider *);\n\n}\n\n/* Provider data events definitions */\nnamespace hex {\n\n    /**\n     * @brief Signals the dirtying of a provider\n     *\n     * Any data modification that occurs in a provider dirties it, until its state is either saved or restored.\n     * This event signals that fact to subscribers so additional code can be executed for certain cases.\n     */\n    EVENT_DEF(EventProviderDirtied, prv::Provider *);\n\n    /**\n     * @brief Signals an insertion of new data into a provider\n     *\n     * @param provider the provider\n     * @param offset the start of the insertion\n     * @param size the new data's size\n     */\n    EVENT_DEF(EventProviderDataInserted, prv::Provider *, u64, u64);\n\n    /**\n     * @brief Signals a modification in the provider's data\n     *\n     * @param provider the provider\n     * @param offset the data modification's offset (start address)\n     * @param size the buffer's size\n     * @param buffer the modified data written at this address\n     */\n    EVENT_DEF(EventProviderDataModified, prv::Provider *, u64, u64, const u8*);\n\n    /**\n     * @brief Signals a removal of some of the provider's data\n     *\n     * @param provider the provider\n     * @param offset the deletion offset (start address)\n     * @param size the deleted data's size\n     */\n    EVENT_DEF(EventProviderDataRemoved, prv::Provider *, u64, u64);\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/api/events/requests_gui.hpp",
    "content": "#pragma once\n\n#include <hex/api/event_manager.hpp>\n\n/* GUI requests definitions */\nnamespace hex {\n\n    /**\n     * @brief Requests the opening of a new window.\n     *\n     * @param name the window's name\n     */\n    EVENT_DEF(RequestOpenWindow, std::string);\n\n    /**\n     * @brief Centralized request to update ImHex's main window title\n     *\n     * This request can be called to make ImHex refresh its main window title, taking into account a new project\n     * or file opened/closed.\n     */\n    EVENT_DEF(RequestUpdateWindowTitle);\n\n    /**\n     * @brief Requests a theme type (light or dark) change\n     *\n     * @param themeType either `Light` or `Dark`\n     */\n    EVENT_DEF(RequestChangeTheme, std::string);\n\n    /**\n     * @brief Requests the opening of a popup\n     *\n     * @param name the popup's name\n     */\n    EVENT_DEF(RequestOpenPopup, std::string);\n\n    /**\n     * @brief Requests updating of the active post-processing shader\n     *\n     * @param vertexShader the vertex shader source code\n     * @param fragmentShader the fragment shader source code\n     */\n    EVENT_DEF(RequestSetPostProcessingShader, std::string, std::string);\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/api/events/requests_interaction.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <hex/api/imhex_api/hex_editor.hpp>\n#include <hex/api/event_manager.hpp>\n\n/* Forward declarations */\nnamespace pl::ptrn { class Pattern; }\n\n/* Interaction requests definitions */\nnamespace hex {\n\n    /**\n     * @brief Requests a selection change in the Hex editor\n     *\n     * This request is handled by the Hex editor, which proceeds to check if the selection is valid.\n     * If it is invalid, the Hex editor fires the `EventRegionSelected` event with nullptr region info.\n     *\n     * @param region the region that should be selected\n     */\n    EVENT_DEF(RequestHexEditorSelectionChange, ImHexApi::HexEditor::ProviderRegion);\n\n    /**\n     * @brief Requests the Pattern editor to move selection\n     *\n     * Requests the Pattern editor to move the cursor's position to reflect the user's click or movement.\n     *\n     * @param line the target line\n     * @param column the target column\n     */\n    EVENT_DEF(RequestPatternEditorSelectionChange, u32, u32);\n\n    /**\n     * @brief Requests a jump to a given pattern\n     *\n     * This request is fired by the Hex editor when the user asks to jump to the pattern.\n     * It is then caught and reflected by the Pattern data component.\n     *\n     * @param pattern the pattern to jump to\n     */\n    EVENT_DEF(RequestJumpToPattern, const pl::ptrn::Pattern*);\n\n    /**\n     * @brief Requests to add a bookmark\n     *\n     * @param region the region to be bookmarked\n     * @param name the bookmark's name\n     * @param comment a comment\n     * @param color the color\n     * @param id the bookmark's unique ID\n     */\n    EVENT_DEF(RequestAddBookmark, Region, std::string, std::string, color_t, u64*);\n\n    /**\n     * @brief Requests a bookmark removal\n     *\n     * @param id the bookmark's unique ID\n     */\n    EVENT_DEF(RequestRemoveBookmark, u64);\n\n    /**\n     * @brief Request the Pattern editor to set its code\n     *\n     * This request allows the rest of ImHex to interface with the Pattern editor component, by setting its code.\n     * This allows for `.hexpat` file loading, and more.\n     *\n     * @param code the code's string\n     */\n    EVENT_DEF(RequestSetPatternLanguageCode, std::string);\n\n    /**\n     * @brief Requests the Pattern editor to run the current code\n     *\n     */\n    EVENT_DEF(RequestTriggerPatternEvaluation);\n\n    /**\n     * @brief Requests ImHex to open and process a file\n     *\n     * @param path the file's path\n     */\n    EVENT_DEF(RequestOpenFile, std::fs::path);\n\n    /**\n     * @brief Adds a virtual file in the Pattern editor\n     *\n     * @param path the file's path\n     * @param data the file's data\n     * @param region the impacted region\n     */\n    EVENT_DEF(RequestAddVirtualFile, std::fs::path, std::vector<u8>, Region);\n\n    /**\n     * @brief Requests the command palette to be opened\n     */\n    EVENT_DEF(RequestOpenCommandPalette);\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/api/events/requests_lifecycle.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <hex/api/event_manager.hpp>\n\n/* Lifecycle requests definitions */\nnamespace hex {\n\n    /**\n     * @brief Emit a request to add an initialization task to the list\n     *\n     * These tasks will be executed at startup.\n     *\n     * @param name Name of the init task\n     * @param isAsync Whether the task is asynchronous (true if yes)\n     * @param callbackFunction The function to call to execute the task\n     */\n    EVENT_DEF(RequestAddInitTask, std::string, bool, std::function<bool()>);\n\n    /**\n     * @brief Emit a request to add an exit task to the list\n     *\n     * These tasks will be executed during the exit phase.\n     *\n     * FIXME: request is unused and should be scrapped.\n     *\n     * @param name Name of the exit task\n     * @param callbackFunction The function to call to execute the task\n     */\n    EVENT_DEF(RequestAddExitTask, std::string, std::function<bool()>);\n\n    /**\n     * @brief Requests ImHex's graceful shutdown\n     *\n     * If there are no questions (bool set to true), ImHex closes immediately.\n     * If set to false, there is a procedure run to prompt a confirmation to the user.\n     *\n     * @param noQuestions true if no questions\n     */\n    EVENT_DEF(RequestCloseImHex, bool);\n\n    /**\n     * @brief Requests ImHex's restart\n     *\n     * This event is necessary for ImHex to restart in the main loop for native and web platforms,\n     * as ImHex cannot simply close and re-open.\n     *\n     * This event serves no purpose on Linux, Windows and macOS platforms.\n     */\n    EVENT_DEF(RequestRestartImHex);\n\n    /**\n     * @brief Requests the initialization of theme handlers\n     *\n     * This is called during ImGui bootstrapping, and should not be called at any other time.\n     */\n    EVENT_DEF(RequestInitThemeHandlers);\n\n    /**\n     * @brief Send a subcommand to the main Imhex instance\n     *\n     * This request is called to send a subcommand to the main ImHex instance.\n     * This subcommand will then be executed by a handler when ImHex finishing initializing\n     * (`EventImHexStartupFinished`).\n     *\n     * FIXME: change the name so that it is prefixed with \"Request\" like every other request.\n     *\n     * @param name the subcommand's name\n     * @param data the subcommand's data\n     */\n    EVENT_DEF(SendMessageToMainInstance, const std::string, const std::vector<u8>&);\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/api/events/requests_provider.hpp",
    "content": "#pragma once\n\n#include <hex/api/event_manager.hpp>\n\n/* Provider requests definitions */\nnamespace hex {\n\n    /**\n     * @brief Creates a provider from its unlocalized name, and add it to the provider list\n    */\n    EVENT_DEF(RequestCreateProvider, std::string, bool, bool, std::shared_ptr<hex::prv::Provider> *);\n\n    /**\n     * @brief Used internally when opening a provider through the API\n    */\n    EVENT_DEF(RequestOpenProvider, std::shared_ptr<prv::Provider>);\n\n    /**\n     * @brief Move the data from all PerProvider instances from one provider to another\n     *\n     * The 'from' provider should not have any per provider data after this, and should be immediately deleted\n     *\n     * FIXME: rename with the \"Request\" prefix to apply standard naming convention.\n    */\n    EVENT_DEF(MovePerProviderData, prv::Provider *, prv::Provider *);\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/api/imhex_api/bookmarks.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <string>\n\nEXPORT_MODULE namespace hex {\n\n    /* Functions to interact with Bookmarks */\n    namespace ImHexApi::Bookmarks {\n\n        struct Entry {\n            Region region;\n\n            std::string name;\n            std::string comment;\n            u32 color;\n            bool locked;\n            u64 id;\n        };\n\n        /**\n         * @brief Adds a new bookmark\n         * @param address The address of the bookmark\n         * @param size The size of the bookmark\n         * @param name The name of the bookmark\n         * @param comment The comment of the bookmark\n         * @param color The color of the bookmark or 0x00 for the default color\n         * @return Bookmark ID\n         */\n        u64 add(u64 address, size_t size, const std::string &name, const std::string &comment, color_t color = 0x00000000);\n\n        /**\n        * @brief Adds a new bookmark\n        * @param region The region of the bookmark\n        * @param name The name of the bookmark\n        * @param comment The comment of the bookmark\n        * @param color The color of the bookmark or 0x00 for the default color\n        * @return Bookmark ID\n        */\n        u64 add(Region region, const std::string &name, const std::string &comment, color_t color = 0x00000000);\n\n        /**\n        * @brief Removes a bookmark\n        * @param id The ID of the bookmark to remove\n        */\n        void remove(u64 id);\n\n    }\n\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/imhex_api/fonts.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n\n#include <span>\n#include <optional>\n#include <vector>\n\n#if !defined(HEX_MODULE_EXPORT)\n    struct ImFont;\n#endif\n\nEXPORT_MODULE namespace hex {\n\n    /* Functions for adding new font types */\n    namespace ImHexApi::Fonts {\n\n        struct Offset { float x, y; };\n\n        struct MergeFont {\n            std::string name;\n            std::span<const u8> fontData;\n            Offset offset;\n            std::optional<float> fontSizeMultiplier;\n        };\n\n        class Font {\n        public:\n            explicit Font(UnlocalizedString fontName);\n\n            void push(float size = 0.0F) const;\n            void pushBold(float size = 0.0F) const;\n            void pushItalic(float size = 0.0F) const;\n\n            void pop() const;\n\n            [[nodiscard]] operator ImFont*() const;\n            [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { return m_fontName; }\n\n        private:\n            void push(float size, ImFont *font) const;\n\n        private:\n            UnlocalizedString m_fontName;\n        };\n\n        struct FontDefinition {\n            ImFont* regular;\n            ImFont* bold;\n            ImFont* italic;\n        };\n\n        namespace impl {\n\n            const std::vector<MergeFont>& getMergeFonts();\n            std::map<UnlocalizedString, FontDefinition>& getFontDefinitions();\n\n        }\n\n        void registerMergeFont(const std::string &name, const std::span<const u8> &data, Offset offset = {}, std::optional<float> fontSizeMultiplier = std::nullopt);\n\n        void registerFont(const Font& font);\n        FontDefinition getFont(const UnlocalizedString &fontName);\n\n        void setDefaultFont(const Font& font);\n        const Font& getDefaultFont();\n\n        float getDpi();\n        float pixelsToPoints(float pixels);\n        float pointsToPixels(float points);\n\n    }\n\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/imhex_api/hex_editor.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <functional>\n#include <string>\n#include <map>\n#include <optional>\n#include <set>\n#include <vector>\n\nEXPORT_MODULE namespace hex {\n\n    #if !defined(HEX_MODULE_EXPORT)\n        namespace prv { class Provider; }\n    #endif\n\n    /* Functions to query information from the Hex Editor and interact with it */\n    namespace ImHexApi::HexEditor {\n\n        using TooltipFunction = std::function<void(u64, const u8*, size_t)>;\n\n        class Highlighting {\n        public:\n            Highlighting() = default;\n            Highlighting(Region region, color_t color);\n\n            [[nodiscard]] const Region& getRegion() const { return m_region; }\n            [[nodiscard]] const color_t& getColor() const { return m_color; }\n\n        private:\n            Region m_region = {};\n            color_t m_color = 0x00;\n        };\n\n        class Tooltip {\n        public:\n            Tooltip() = default;\n            Tooltip(Region region, std::string value, color_t color);\n\n            [[nodiscard]] const Region& getRegion() const { return m_region; }\n            [[nodiscard]] const color_t& getColor() const { return m_color; }\n            [[nodiscard]] const std::string& getValue() const { return m_value; }\n\n        private:\n            Region m_region = {};\n            std::string m_value;\n            color_t m_color = 0x00;\n        };\n\n        struct ProviderRegion : Region {\n            prv::Provider *provider;\n\n            [[nodiscard]] prv::Provider *getProvider() const { return this->provider; }\n\n            [[nodiscard]] Region getRegion() const { return { this->address, this->size }; }\n        };\n\n        namespace impl {\n\n            using HighlightingFunction = std::function<std::optional<color_t>(u64, const u8*, size_t, bool)>;\n            using HoveringFunction = std::function<std::set<Region>(const prv::Provider *, u64, size_t)>;\n\n            const std::map<u32, Highlighting>& getBackgroundHighlights();\n            const std::map<u32, HighlightingFunction>& getBackgroundHighlightingFunctions();\n            const std::map<u32, Highlighting>& getForegroundHighlights();\n            const std::map<u32, HighlightingFunction>& getForegroundHighlightingFunctions();\n            const std::map<u32, HoveringFunction>& getHoveringFunctions();\n            const std::map<u32, Tooltip>& getTooltips();\n            const std::map<u32, TooltipFunction>& getTooltipFunctions();\n\n            void setCurrentSelection(const std::optional<ProviderRegion> &region);\n            void setHoveredRegion(const prv::Provider *provider, const Region &region);\n        }\n\n        /**\n         * @brief Adds a background color highlighting to the Hex Editor\n         * @param region The region to highlight\n         * @param color The color to use for the highlighting\n         * @return Unique ID used to remove the highlighting again later\n         */\n        u32 addBackgroundHighlight(const Region &region, color_t color);\n\n        /**\n         * @brief Removes a background color highlighting from the Hex Editor\n         * @param id The ID of the highlighting to remove\n         */\n        void removeBackgroundHighlight(u32 id);\n\n\n        /**\n         * @brief Adds a foreground color highlighting to the Hex Editor\n         * @param region The region to highlight\n         * @param color The color to use for the highlighting\n         * @return Unique ID used to remove the highlighting again later\n         */\n        u32 addForegroundHighlight(const Region &region, color_t color);\n\n        /**\n         * @brief Removes a foreground color highlighting from the Hex Editor\n         * @param id The ID of the highlighting to remove\n         */\n        void removeForegroundHighlight(u32 id);\n\n        /**\n         * @brief Adds a hover tooltip to the Hex Editor\n         * @param region The region to add the tooltip to\n         * @param value Text to display in the tooltip\n         * @param color The color of the tooltip\n         * @return Unique ID used to remove the tooltip again later\n         */\n        u32 addTooltip(Region region, std::string value, color_t color);\n\n        /**\n         * @brief Removes a hover tooltip from the Hex Editor\n         * @param id The ID of the tooltip to remove\n         */\n        void removeTooltip(u32 id);\n\n\n        /**\n         * @brief Adds a background color highlighting to the Hex Editor using a callback function\n         * @param function Function that draws the highlighting based on the hovered region\n         * @return Unique ID used to remove the highlighting again later\n         */\n        u32 addTooltipProvider(TooltipFunction function);\n\n        /**\n         * @brief Removes a background color highlighting from the Hex Editor\n         * @param id The ID of the highlighting to remove\n         */\n        void removeTooltipProvider(u32 id);\n\n\n        /**\n         * @brief Adds a background color highlighting to the Hex Editor using a callback function\n         * @param function Function that draws the highlighting based on the hovered region\n         * @return Unique ID used to remove the highlighting again later\n         */\n        u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function);\n\n        /**\n         * @brief Removes a background color highlighting from the Hex Editor\n         * @param id The ID of the highlighting to remove\n         */\n        void removeBackgroundHighlightingProvider(u32 id);\n\n\n        /**\n         * @brief Adds a foreground color highlighting to the Hex Editor using a callback function\n         * @param function Function that draws the highlighting based on the hovered region\n         * @return Unique ID used to remove the highlighting again later\n         */\n        u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function);\n\n        /**\n         * @brief Removes a foreground color highlighting from the Hex Editor\n         * @param id The ID of the highlighting to remove\n         */\n        void removeForegroundHighlightingProvider(u32 id);\n\n        /**\n         * @brief Adds a hovering provider to the Hex Editor using a callback function\n         * @param function Function that draws the highlighting based on the hovered region\n         * @return Unique ID used to remove the highlighting again later\n         */\n        u32 addHoverHighlightProvider(const impl::HoveringFunction &function);\n\n        /**\n         * @brief Removes a hovering color highlighting from the Hex Editor\n         * @param id The ID of the highlighting to remove\n         */\n        void removeHoverHighlightProvider(u32 id);\n\n        /**\n         * @brief Checks if there's a valid selection in the Hex Editor right now\n         */\n        bool isSelectionValid();\n\n        /**\n         * @brief Clears the current selection in the Hex Editor\n         */\n        void clearSelection();\n\n        /**\n         * @brief Gets the current selection in the Hex Editor\n         * @return The current selection\n         */\n        std::optional<ProviderRegion> getSelection();\n\n        /**\n         * @brief Sets the current selection in the Hex Editor\n         * @param region The region to select\n         * @param provider The provider to select the region in\n         */\n        void setSelection(const Region &region, prv::Provider *provider = nullptr);\n\n        /**\n         * @brief Sets the current selection in the Hex Editor\n         * @param region The region to select\n         */\n        void setSelection(const ProviderRegion &region);\n\n        /**\n         * @brief Sets the current selection in the Hex Editor\n         * @param address The address to select\n         * @param size The size of the selection\n         * @param provider The provider to select the region in\n         */\n        void setSelection(u64 address, size_t size, prv::Provider *provider = nullptr);\n\n        /**\n         * @brief Adds a virtual file to the list in the Hex Editor\n         * @param path The path of the file\n         * @param data The data of the file\n         * @param region The location of the file in the Hex Editor if available\n         */\n        void addVirtualFile(const std::string &path, std::vector<u8> data, Region region = Region::Invalid());\n\n        /**\n         * @brief Gets the currently hovered cell region in the Hex Editor\n         * @return\n         */\n        const std::optional<Region>& getHoveredRegion(const prv::Provider *provider);\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/imhex_api/messaging.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <functional>\n#include <string>\n#include <map>\n#include <vector>\n\nEXPORT_MODULE namespace hex {\n\n    /**\n     * Cross-instance messaging system\n     * This allows you to send messages to the \"main\" instance of ImHex running, from any other instance\n     */\n    namespace ImHexApi::Messaging {\n\n        namespace impl {\n\n            using MessagingHandler = std::function<void(const std::vector<u8> &)>;\n\n            const std::map<std::string, MessagingHandler>& getHandlers();\n            void runHandler(const std::string &eventName, const std::vector<u8> &args);\n\n        }\n\n        /**\n         * @brief Register the handler for this specific event name\n         */\n        void registerHandler(const std::string &eventName, const impl::MessagingHandler &handler);\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/imhex_api/provider.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n#include <hex/providers/provider.hpp>\n\n#include <set>\n#include <vector>\n#include <memory>\n#include <concepts>\n\nEXPORT_MODULE namespace hex {\n\n    /**\n     * Helper methods about the providers\n     * @note the \"current provider\" or \"currently selected provider\" refers to the currently selected provider in the UI;\n     * the provider the user is actually editing.\n     */\n    namespace ImHexApi::Provider {\n\n        namespace impl {\n\n            void resetClosingProvider();\n            std::set<prv::Provider*> getClosingProviders();\n\n        }\n\n        /**\n         * @brief Gets the currently selected data provider\n         * @return The currently selected data provider, or nullptr is there is none\n         */\n        prv::Provider *get();\n\n        /**\n         * @brief Gets a list of all currently loaded data providers\n         * @return The currently loaded data providers\n         */\n        std::vector<prv::Provider*> getProviders();\n\n        /**\n         * @brief Sets the currently selected data provider\n         * @param index Index of the provider to select\n         */\n        void setCurrentProvider(i64 index);\n\n        /**\n         * @brief Sets the currently selected data provider\n         * @param provider The provider to select\n         */\n        void setCurrentProvider(NonNull<prv::Provider*> provider);\n\n        /**\n         * @brief Gets the index of the currently selected data provider\n         * @return Index of the selected provider\n         */\n        i64 getCurrentProviderIndex();\n\n        /**\n         * @brief Checks whether the currently selected data provider is valid\n         * @return Whether the currently selected data provider is valid\n         */\n        bool isValid();\n\n\n        /**\n         * @brief Marks the **currently selected** data provider as dirty\n         */\n        void markDirty();\n\n        /**\n         * @brief Marks **all data providers** as clean\n         */\n        void resetDirty();\n\n        /**\n         * @brief Checks whether **any of the data providers** is dirty\n         * @return Whether any data provider is dirty\n         */\n        bool isDirty();\n\n\n        /**\n         * @brief Adds a newly created provider to the list of providers, and mark it as the selected one.\n         * @param provider The provider to add\n         * @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)\n         * @param select Whether to select the provider after adding it\n         */\n        void add(std::shared_ptr<prv::Provider> &&provider, bool skipLoadInterface = false, bool select = true);\n\n        /**\n         * @brief Creates a new provider and adds it to the list of providers\n         * @tparam T The type of the provider to create\n         * @param args Arguments to pass to the provider's constructor\n         */\n        template<std::derived_from<prv::Provider> T>\n        void add(auto &&...args) {\n            add(std::make_unique<T>(std::forward<decltype(args)>(args)...));\n        }\n\n        /**\n         * @brief Removes a provider from the list of providers\n         * @param provider The provider to remove\n         * @param noQuestions Whether to skip asking the user for confirmation\n         */\n        void remove(prv::Provider *provider, bool noQuestions = false);\n\n        /**\n         * @brief Creates a new provider using its unlocalized name and add it to the list of providers\n         * @param unlocalizedName The unlocalized name of the provider to create\n         * @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)\n         * @param select Whether to select the provider after adding it\n         */\n        std::shared_ptr<prv::Provider> createProvider(\n            const UnlocalizedString &unlocalizedName,\n            bool skipLoadInterface = false,\n            bool select = true\n        );\n\n        /**\n         * @brief Opens a provider, making its data available to ImHex and handling any error that may occur\n         * @param provider The provider to open\n         */\n        void openProvider(std::shared_ptr<prv::Provider> provider);\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/imhex_api/system.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/helpers/semantic_version.hpp>\n#include <hex/helpers/fs.hpp>\n\n#include <chrono>\n#include <functional>\n#include <optional>\n#include <string>\n#include <map>\n\n#if !defined(HEX_MODULE_EXPORT)\n    using ImGuiID = unsigned int;\n    struct ImVec2;\n    struct ImFontAtlas;\n#endif\n\nstruct GLFWwindow;\n\nEXPORT_MODULE namespace hex {\n\n    #if !defined(HEX_MODULE_EXPORT)\n        namespace impl {\n            class AutoResetBase;\n        }\n    #endif\n\n    /* Functions to interact with various ImHex system settings */\n    namespace ImHexApi::System {\n\n        struct ProgramArguments {\n            int argc;\n            char **argv;\n            char **envp;\n        };\n\n        struct InitialWindowProperties {\n            i32 x, y;\n            u32 width, height;\n            bool maximized;\n        };\n\n        enum class TaskProgressState {\n            Reset,\n            Progress,\n            Flash\n        };\n\n        enum class TaskProgressType {\n            Normal,\n            Warning,\n            Error\n        };\n\n        namespace impl {\n\n            void setMainInstanceStatus(bool status);\n\n            void setMainWindowPosition(i32 x, i32 y);\n            void setMainWindowSize(u32 width, u32 height);\n            void setMainDockSpaceId(ImGuiID id);\n            void setMainWindowHandle(GLFWwindow *window);\n            void setMainWindowFocusState(bool focused);\n\n            void setGlobalScale(float scale);\n            void setNativeScale(float scale);\n\n            void setBorderlessWindowMode(bool enabled);\n            void setMultiWindowMode(bool enabled);\n            void setInitialWindowProperties(InitialWindowProperties properties);\n\n            void setGPUVendor(const std::string &vendor);\n            void setGLRenderer(const std::string &renderer);\n            void setGLVersion(SemanticVersion version);\n\n            void addInitArgument(const std::string &key, const std::string &value = { });\n\n            void setLastFrameTime(double time);\n\n            bool isWindowResizable();\n\n            void addAutoResetObject(hex::impl::AutoResetBase *object);\n            void removeAutoResetObject(hex::impl::AutoResetBase *object);\n\n            void cleanup();\n\n            bool frameRateUnlockRequested();\n            void resetFrameRateUnlockRequested();\n\n        }\n\n        /**\n         * @brief Closes ImHex\n         * @param noQuestions Whether to skip asking the user for confirmation\n         */\n        void closeImHex(bool noQuestions = false);\n\n        /**\n         * @brief Restarts ImHex\n         */\n        void restartImHex();\n\n        /**\n         * @brief Sets the progress bar in the task bar\n         * @param state The state of the progress bar\n         * @param type The type of the progress bar progress\n         * @param progress The progress of the progress bar\n         */\n        void setTaskBarProgress(TaskProgressState state, TaskProgressType type, u32 progress);\n\n\n        /**\n         * @brief Gets the current target FPS\n         * @return The current target FPS\n         */\n        float getTargetFPS();\n\n        /**\n         * @brief Sets the target FPS\n         * @param fps The target FPS\n         */\n        void setTargetFPS(float fps);\n\n\n        /**\n         * @brief Gets the current global scale\n         * @return The current global scale\n         */\n        float getGlobalScale();\n\n        /**\n         * @brief Gets the current native scale\n         * @return The current native scale\n         */\n        float getNativeScale();\n\n        float getBackingScaleFactor();\n\n        /**\n         * @brief Gets the current main window position\n         * @return Position of the main window\n         */\n        ImVec2 getMainWindowPosition();\n\n        /**\n         * @brief Gets the current main window size\n         * @return Size of the main window\n         */\n        ImVec2 getMainWindowSize();\n\n        /**\n         * @brief Gets the current main dock space ID\n         * @return ID of the main dock space\n         */\n        ImGuiID getMainDockSpaceId();\n\n        /**\n         * @brief Gets the main window's GLFW window handle\n         * @return GLFW window handle\n         */\n        GLFWwindow* getMainWindowHandle();\n\n        /**\n         * @brief Checks if the main window is currently focused\n         * @return Whether the main window is focused\n         */\n        bool isMainWindowFocused();\n\n        /**\n         * @brief Checks if borderless window mode is enabled currently\n         * @return Whether borderless window mode is enabled\n         */\n        bool isBorderlessWindowModeEnabled();\n\n        /**\n         * @brief Checks if multi-window mode is enabled currently\n         * @return Whether multi-window mode is enabled\n         */\n        bool isMultiWindowModeEnabled();\n\n        /**\n         * @brief Gets the init arguments passed to ImHex from the splash screen\n         * @return Init arguments\n         */\n        const std::map<std::string, std::string>& getInitArguments();\n\n        /**\n         * @brief Gets a init arguments passed to ImHex from the splash screen\n         * @param key The key of the init argument\n         * @return Init argument\n        */\n        std::string getInitArgument(const std::string &key);\n\n        /**\n         * @brief Sets if ImHex should follow the system theme\n         * @param enabled Whether to follow the system theme\n         */\n        void enableSystemThemeDetection(bool enabled);\n\n        /**\n         * @brief Checks if ImHex follows the system theme\n         * @return Whether ImHex follows the system theme\n         */\n        bool usesSystemThemeDetection();\n\n\n        /**\n         * @brief Gets the currently set additional folder paths\n         * @return The currently set additional folder paths\n         */\n        const std::vector<std::fs::path>& getAdditionalFolderPaths();\n\n        /**\n         * @brief Sets the additional folder paths\n         * @param paths The additional folder paths\n         */\n        void setAdditionalFolderPaths(const std::vector<std::fs::path> &paths);\n\n\n        /**\n         * @brief Gets the current GPU vendor\n         * @return The current GPU vendor\n         */\n        const std::string& getGPUVendor();\n\n        /**\n         * @brief Gets the current GPU vendor\n         * @return The current GPU vendor\n         */\n        const std::string& getGLRenderer();\n\n        /**\n         * @brief Gets the current OpenGL version\n         * @return The current OpenGL version\n         */\n        const SemanticVersion& getGLVersion();\n\n        /**\n         * @brief Checks if ImHex is being run in a \"Corporate Environment\"\n         * This function simply checks for common telltale signs such as if the machine is joined a\n         * domain. It's not super accurate, but it's still useful for statistics\n         * @return True if it is\n         */\n        bool isCorporateEnvironment();\n\n        /**\n         * @brief Checks if ImHex is running in portable mode\n         * @return Whether ImHex is running in portable mode\n         */\n        bool isPortableVersion();\n\n        /**\n         * @brief Gets the current Operating System name\n         * @return Operating System name\n         */\n        std::string getOSName();\n\n        /**\n         * @brief Gets the current Operating System version\n         * @return Operating System version\n         */\n        std::string getOSVersion();\n\n        /**\n         * @brief Gets the current CPU architecture\n         * @return CPU architecture\n         */\n        std::string getArchitecture();\n\n\n        struct LinuxDistro {\n            std::string name;\n            std::string version;\n        };\n        /**\n         * @brief Gets information related to the Linux distribution, if running on Linux\n         */\n        std::optional<LinuxDistro> getLinuxDistro();\n\n        /**\n         * @brief Gets the current ImHex version\n         * @return ImHex version\n         */\n        const SemanticVersion& getImHexVersion();\n\n        /**\n         * @brief Gets the current git commit hash\n         * @param longHash Whether to return the full hash or the shortened version\n         * @return Git commit hash\n         */\n        std::string getCommitHash(bool longHash = false);\n\n        /**\n         * @brief Gets the current git commit branch\n         * @return Git commit branch\n         */\n        std::string getCommitBranch();\n\n        /**\n         * @brief Gets the time ImHex was built\n         * @return The time ImHex was built\n         */\n        std::optional<std::chrono::system_clock::time_point> getBuildTime();\n\n        /**\n         * @brief Checks if ImHex was built in debug mode\n         * @return True if ImHex was built in debug mode, false otherwise\n         */\n        bool isDebugBuild();\n\n        /**\n         * @brief Checks if this version of ImHex is a nightly build\n         * @return True if this version is a nightly, false if it's a release\n         */\n        bool isNightlyBuild();\n\n        /**\n         * @brief Checks if there's an update available for the current version of ImHex\n         * @return Optional string returning the version string of the new version, or std::nullopt if no update is available\n         */\n        std::optional<std::string> checkForUpdate();\n\n        enum class UpdateType {\n            Stable,\n            Nightly\n        };\n\n        /**\n         * @brief Triggers the update process\n         * @param updateType The update channel\n         * @return If the update process was successfully started\n         */\n        bool updateImHex(UpdateType updateType);\n\n        /**\n         * @brief Add a new startup task that will be run while ImHex's splash screen is shown\n         * @param name Name to be shown in the UI\n         * @param async Whether to run the task asynchronously\n         * @param function The function to run\n         */\n        void addStartupTask(const std::string &name, bool async, const std::function<bool()> &function);\n\n        /**\n         * @brief Gets the time the previous frame took\n         * @return Previous frame time\n         */\n        double getLastFrameTime();\n\n        /**\n         * @brief Sets the window resizable\n         * @param resizable Whether the window should be resizable\n         */\n        void setWindowResizable(bool resizable);\n\n        /**\n         * @brief Checks if this window is the main instance of ImHex\n         * @return True if this is the main instance, false if another instance is already running\n         */\n        bool isMainInstance();\n\n        /**\n         * @brief Gets the initial window properties\n         * @return Initial window properties\n         */\n        std::optional<InitialWindowProperties> getInitialWindowProperties();\n\n        /**\n         * @brief Gets the module handle of libimhex\n         * @return Module handle\n         */\n        void* getLibImHexModuleHandle();\n\n        /**\n         * Adds a new migration routine that will be executed when upgrading from a lower version than specified in migrationVersion\n         * @param migrationVersion Upgrade point version\n         * @param function Function to run\n         */\n        void addMigrationRoutine(SemanticVersion migrationVersion, std::function<void()> function);\n\n        /**\n         * @brief Unlocks the frame rate temporarily, allowing animations to run smoothly\n         */\n        void unlockFrameRate();\n\n        /**\n         * @brief Sets the current post-processing shader to use\n         * @param vertexShader The vertex shader to use\n         * @param fragmentShader The fragment shader to use\n         */\n        void setPostProcessingShader(const std::string &vertexShader, const std::string &fragmentShader);\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/layout_manager.hpp",
    "content": "#pragma once\n\n#include <hex/helpers/fs.hpp>\n\n#include <string>\n\n#if !defined(HEX_MODULE_EXPORT)\n    struct ImGuiTextBuffer;\n#endif\n\nEXPORT_MODULE namespace hex {\n\n    class LayoutManager {\n    public:\n        struct Layout {\n            std::string name;\n            std::fs::path path;\n        };\n\n        using LoadCallback = std::function<void(std::string_view)>;\n        using StoreCallback = std::function<void(ImGuiTextBuffer *)>;\n\n        /**\n         * @brief Save the current layout\n         * @param name Name of the layout\n         */\n        static void save(const std::string &name);\n\n        /**\n         * @brief Load a layout from a file\n         * @param path Path to the layout file\n         */\n        static void load(const std::fs::path &path);\n\n        /**\n         * @brief Saves the current layout to a string\n         * @return String containing the layout\n         */\n        static std::string saveToString();\n\n        /**\n         * @brief Load a layout from a string\n         * @param content Layout string\n         */\n        static void loadFromString(const std::string &content);\n\n        /**\n         * @brief Get a list of all layouts\n         * @return List of all added layouts\n         */\n        static const std::vector<Layout> &getLayouts();\n\n        /**\n         * @brief Removes the layout with the given name\n         * @param name Name of the layout\n         */\n        static void removeLayout(const std::string &name);\n\n        /**\n         * @brief Handles loading of layouts if needed\n         * @note This function should only be called by ImHex\n         */\n        static void process();\n\n        /**\n         * @brief Reload all layouts\n         */\n        static void reload();\n\n        /**\n         * @brief Reset the layout manager\n         */\n        static void reset();\n\n        /**\n         * @brief Checks is the current layout is locked\n         */\n        static bool isLayoutLocked();\n\n        /**\n         * @brief Locks or unlocks the current layout\n         * @note If the layout is locked, it cannot be modified by the user anymore\n         * @param locked True to lock the layout, false to unlock it\n         */\n        static void lockLayout(bool locked);\n\n        /**\n         * @brief Closes all views\n         */\n        static void closeAllViews();\n\n        static void registerLoadCallback(const LoadCallback &callback);\n        static void registerStoreCallback(const StoreCallback &callback);\n\n        static void onStore(ImGuiTextBuffer *buffer);\n        static void onLoad(std::string_view line);\n\n    private:\n        LayoutManager() = default;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/localization_manager.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <map>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <functional>\n\n#include <fmt/core.h>\n#include <wolv/types/static_string.hpp>\n\nEXPORT_MODULE namespace hex {\n\n    struct UnlocalizedString;\n    using LanguageId = std::string;\n\n    namespace LocalizationManager {\n\n        struct PathEntry {\n            std::string path;\n            std::function<std::string_view(const std::string &path)> callback;\n        };\n\n        struct LanguageDefinition {\n            LanguageId id;\n            std::string name, nativeName;\n            LanguageId fallbackLanguageId;\n            bool hidden;\n\n            std::vector<PathEntry> languageFilePaths;\n        };\n\n        void addLanguages(const std::string_view &languageList, std::function<std::string_view(const std::string &path)> callback);\n        void setLanguage(const LanguageId &languageId);\n        [[nodiscard]] const LanguageId& getSelectedLanguageId();\n        [[nodiscard]] const std::string& get(const LanguageId& languageId, const UnlocalizedString &unlocalizedString);\n        [[nodiscard]] const std::map<LanguageId, LanguageDefinition>& getLanguageDefinitions();\n        [[nodiscard]] const LanguageDefinition& getLanguageDefinition(const LanguageId &languageId);\n\n    }\n\n    class LangConst;\n\n    class Lang {\n    public:\n        explicit Lang(const char *unlocalizedString);\n        explicit Lang(const std::string &unlocalizedString);\n        explicit(false) Lang(const LangConst &localizedString);\n        explicit Lang(const UnlocalizedString &unlocalizedString);\n        explicit Lang(std::string_view unlocalizedString);\n\n        [[nodiscard]] operator std::string() const;\n        [[nodiscard]] operator std::string_view() const;\n        [[nodiscard]] operator const char *() const;\n\n        const char* get() const;\n\n    private:\n        std::size_t m_entryHash;\n    };\n\n    class LangConst {\n    public:\n        [[nodiscard]] operator std::string() const;\n        [[nodiscard]] operator std::string_view() const;\n        [[nodiscard]] operator const char *() const;\n\n        const char* get() const;\n\n        constexpr static size_t hash(std::string_view string) {\n            constexpr u64 p = 131;\n            constexpr u64 m = std::numeric_limits<std::uint32_t>::max() - 4;\n            u64 total = 0;\n            u64 currentMultiplier = 1;\n\n            for (char c : string) {\n                total = (total + currentMultiplier * c) % m;\n                currentMultiplier = (currentMultiplier * p) % m;\n            }\n\n            return total;\n        }\n\n    private:\n        constexpr explicit LangConst(std::size_t hash, const char *unlocalizedString) : m_entryHash(hash), m_unlocalizedString(unlocalizedString) {}\n\n        template<wolv::type::StaticString>\n        friend consteval LangConst operator\"\"_lang();\n        friend class Lang;\n\n    private:\n        std::size_t m_entryHash;\n        const char *m_unlocalizedString = nullptr;\n    };\n\n    struct UnlocalizedString {\n    public:\n        UnlocalizedString() = default;\n\n        UnlocalizedString(const std::string &string) : m_unlocalizedString(string) { }\n        UnlocalizedString(const char *string) : m_unlocalizedString(string) { }\n        UnlocalizedString(const Lang& arg) = delete;\n        UnlocalizedString(std::string &&string) : m_unlocalizedString(std::move(string)) { }\n        UnlocalizedString(UnlocalizedString &&) = default;\n        UnlocalizedString(const UnlocalizedString &) = default;\n\n        UnlocalizedString &operator=(const UnlocalizedString &) = default;\n        UnlocalizedString &operator=(UnlocalizedString &&) = default;\n        UnlocalizedString &operator=(const std::string &string) { m_unlocalizedString = string; return *this; }\n        UnlocalizedString &operator=(std::string &&string) { m_unlocalizedString = std::move(string); return *this; }\n\n        [[nodiscard]] operator std::string() const {\n            return m_unlocalizedString;\n        }\n\n        [[nodiscard]] operator std::string_view() const {\n            return m_unlocalizedString;\n        }\n\n        [[nodiscard]] operator const char *() const {\n            return m_unlocalizedString.c_str();\n        }\n\n        [[nodiscard]] const std::string &get() const {\n            return m_unlocalizedString;\n        }\n\n        [[nodiscard]] bool empty() const {\n            return m_unlocalizedString.empty();\n        }\n\n        auto operator<=>(const UnlocalizedString &) const = default;\n        auto operator<=>(const std::string &other) const {\n            return m_unlocalizedString <=> other;\n        }\n\n    private:\n        std::string m_unlocalizedString;\n    };\n\n    template<wolv::type::StaticString String>\n    [[nodiscard]] consteval LangConst operator\"\"_lang() {\n        return LangConst(LangConst::hash(String.value.data()), String.value.data());\n    }\n\n    // {fmt} formatter for hex::Lang and hex::LangConst\n    inline auto format_as(const hex::Lang &entry) {\n        return entry.get();\n    }\n    inline auto format_as(const hex::LangConst &entry) {\n        return entry.get();\n    }\n\n}\n\ntemplate<>\nstruct std::hash<hex::UnlocalizedString> {\n    std::size_t operator()(const hex::UnlocalizedString &string) const noexcept {\n        return std::hash<std::string>{}(string.get());\n    }\n};\n\nnamespace fmt {\n\n    template<typename ... Args>\n    auto format(const hex::Lang &entry, Args &&... args) {\n        return fmt::format(fmt::runtime(entry.get()), std::forward<Args>(args)...);\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/plugin_manager.hpp",
    "content": "#pragma once\n\n#include <functional>\n#include <list>\n#include <span>\n#include <string>\n\n#include <wolv/io/fs.hpp>\n\n#include <hex/helpers/logger.hpp>\n#include <hex/helpers/auto_reset.hpp>\n\n#if !defined(HEX_MODULE_EXPORT)\n    struct ImGuiContext;\n#endif\n\nEXPORT_MODULE namespace hex {\n\n    struct SubCommand {\n        enum class Type : u8 {\n            Option,\n            SubCommand\n        };\n\n        std::string commandLong;\n        std::string commandShort;\n\n        std::string commandDescription;\n        std::function<void(const std::vector<std::string>&)> callback;\n        Type type = Type::Option;\n    };\n\n    struct Feature {\n        std::string name;\n        bool enabled;\n    };\n\n    struct PluginFunctions {\n        using InitializePluginFunc     = void (*)();\n        using InitializeLibraryFunc    = void (*)();\n        using GetPluginNameFunc        = const char *(*)();\n        using GetLibraryNameFunc       = const char *(*)();\n        using GetPluginAuthorFunc      = const char *(*)();\n        using GetPluginDescriptionFunc = const char *(*)();\n        using GetCompatibleVersionFunc = const char *(*)();\n        using SetImGuiContextFunc      = void (*)(ImGuiContext *);\n        using GetSubCommandsFunc       = void* (*)();\n        using GetFeaturesFunc          = void* (*)();\n        using IsBuiltinPluginFunc      = bool (*)();\n\n        InitializePluginFunc        initializePluginFunction        = nullptr;\n        InitializeLibraryFunc       initializeLibraryFunction       = nullptr;\n        GetPluginNameFunc           getPluginNameFunction           = nullptr;\n        GetLibraryNameFunc          getLibraryNameFunction          = nullptr;\n        GetPluginAuthorFunc         getPluginAuthorFunction         = nullptr;\n        GetPluginDescriptionFunc    getPluginDescriptionFunction    = nullptr;\n        GetCompatibleVersionFunc    getCompatibleVersionFunction    = nullptr;\n        SetImGuiContextFunc         setImGuiContextFunction         = nullptr;\n        SetImGuiContextFunc         setImGuiContextLibraryFunction  = nullptr;\n        GetSubCommandsFunc          getSubCommandsFunction          = nullptr;\n        GetFeaturesFunc             getFeaturesFunction             = nullptr;\n        IsBuiltinPluginFunc         isBuiltinPluginFunction         = nullptr;\n    };\n\n    class Plugin {\n    public:\n        explicit Plugin(const std::fs::path &path);\n        explicit Plugin(const std::string &name, const PluginFunctions &functions);\n\n        Plugin(const Plugin &) = delete;\n        Plugin(Plugin &&other) noexcept;\n        ~Plugin();\n\n        Plugin& operator=(const Plugin &) = delete;\n        Plugin& operator=(Plugin &&other) noexcept;\n\n        [[nodiscard]] bool initializePlugin() const;\n        [[nodiscard]] std::string getPluginName() const;\n        [[nodiscard]] std::string getPluginAuthor() const;\n        [[nodiscard]] std::string getPluginDescription() const;\n        [[nodiscard]] std::string getCompatibleVersion() const;\n        void setImGuiContext(ImGuiContext *ctx) const;\n\n        [[nodiscard]] const std::fs::path &getPath() const;\n\n        [[nodiscard]] bool isLoaded() const;\n        [[nodiscard]] bool isValid() const;\n        [[nodiscard]] bool isInitialized() const;\n        [[nodiscard]] bool isBuiltinPlugin() const;\n\n        [[nodiscard]] std::span<SubCommand> getSubCommands() const;\n        [[nodiscard]] std::span<Feature> getFeatures() const;\n\n        [[nodiscard]] bool isLibraryPlugin() const;\n\n        [[nodiscard]] bool wasAddedManually() const;\n\n        void setEnabled(bool enabled);\n\n    private:\n        uintptr_t m_handle = 0;\n        std::fs::path m_path;\n\n        mutable bool m_initialized = false;\n        bool m_addedManually = false;\n        bool m_enabled = true;\n\n        PluginFunctions m_functions = {};\n\n        template<typename T>\n        [[nodiscard]] auto getPluginFunction(const std::string &symbol) {\n            return reinterpret_cast<T>(this->getPluginFunction(symbol));\n        }\n\n        [[nodiscard]] void *getPluginFunction(const std::string &symbol) const;\n    };\n\n    class PluginManager {\n    public:\n        PluginManager() = delete;\n\n        static bool load();\n        static bool load(const std::fs::path &pluginFolder);\n\n        static bool loadLibraries();\n        static bool loadLibraries(const std::fs::path &libraryFolder);\n\n        static void unload();\n        static void reload();\n        static void initializeNewPlugins();\n        static void addLoadPath(const std::fs::path &path);\n\n        static void addPlugin(const std::string &name, PluginFunctions functions);\n\n        static Plugin* getPlugin(const std::string &name);\n        static const std::list<Plugin>& getPlugins();\n        static const std::vector<std::fs::path>& getPluginPaths();\n        static const std::vector<std::fs::path>& getPluginLoadPaths();\n\n        static bool isPluginLoaded(const std::fs::path &path);\n\n        static void setPluginEnabled(const Plugin &plugin, bool enabled);\n\n    private:\n        static std::list<Plugin>& getPluginsMutable();\n\n        static AutoReset<std::vector<std::fs::path>> s_pluginPaths, s_pluginLoadPaths;\n        static AutoReset<std::vector<uintptr_t>> s_loadedLibraries;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/project_file_manager.hpp",
    "content": "#pragma once\n\n#include <hex/helpers/tar.hpp>\n\n/**\n * @brief Project file manager\n *\n * The project file manager is used to load and store project files. It is used by all features of ImHex\n * that want to store any data to a Project File.\n *\n */\nEXPORT_MODULE namespace hex {\n\n    namespace prv {\n        class Provider;\n    }\n\n    class ProjectFile {\n    public:\n        struct Handler {\n            using Function = std::function<bool(const std::fs::path &, Tar &tar)>;\n\n            std::fs::path basePath; //< Base path for where to store the files in the project file\n            bool required;          //< If true, ImHex will display an error if this handler fails to load or store data\n            Function load, store;   //< Functions to load and store data\n        };\n\n        struct ProviderHandler {\n            using Function = std::function<bool(prv::Provider *provider, const std::fs::path &, Tar &tar)>;\n\n            std::fs::path basePath; //< Base path for where to store the files in the project file\n            bool required;          //< If true, ImHex will display an error if this handler fails to load or store data\n            Function load, store;   //< Functions to load and store data\n        };\n\n        /**\n         * @brief Set implementations for loading and restoring a project\n         *\n         * @param loadFun function to use to load a project in ImHex\n         * @param storeFun function to use to store a project to disk\n         */\n        static void setProjectFunctions(\n            const std::function<bool(const std::fs::path&)> &loadFun,\n            const std::function<bool(std::optional<std::fs::path>, bool)> &storeFun\n        );\n\n        /**\n         * @brief Load a project file\n         *\n         * @param filePath Path to the project file\n         * @return true if the project file was loaded successfully\n         * @return false if the project file was not loaded successfully\n         */\n        static bool load(const std::fs::path &filePath);\n\n        /**\n         * @brief Store a project file\n         *\n         * @param filePath Path to the project file\n         * @param updateLocation update the project location so subssequent saves will save there\n         * @return true if the project file was stored successfully\n         * @return false if the project file was not stored successfully\n         */\n        static bool store(std::optional<std::fs::path> filePath = std::nullopt, bool updateLocation = true);\n\n        /**\n         * @brief Check if a project file is currently loaded\n         *\n         * @return true if a project file is currently loaded\n         * @return false if no project file is currently loaded\n         */\n        static bool hasPath();\n\n        /**\n         * @brief Clear the currently loaded project file\n         */\n        static void clearPath();\n\n        /**\n         * @brief Get the path to the currently loaded project file\n         * @return Path to the currently loaded project file\n         */\n        static std::fs::path getPath();\n\n        /**\n         * @brief Set the path to the currently loaded project file\n         * @param path Path to the currently loaded project file\n         */\n        static void setPath(const std::fs::path &path);\n\n        /**\n         * @brief Register a handler for storing and loading global data from a project file\n         *\n         * @param handler The handler to register\n         */\n        static void registerHandler(const Handler &handler);\n\n        /**\n         * @brief Register a handler for storing and loading per-provider data from a project file\n         *\n         * @param handler The handler to register\n         */\n        static void registerPerProviderHandler(const ProviderHandler &handler);\n\n        /**\n         * @brief Get the list of registered handlers\n         * @return List of registered handlers\n         */\n        static const std::vector<Handler>& getHandlers();\n\n        /**\n         * @brief Get the list of registered per-provider handlers\n         * @return List of registered per-provider handlers\n         */\n        static const std::vector<ProviderHandler>& getProviderHandlers();\n\n    private:\n        ProjectFile() = default;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/shortcut_manager.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <hex/api/localization_manager.hpp>\n#include <hex/helpers/keys.hpp>\n\n#include <functional>\n#include <optional>\n#include <set>\n#include <string>\n\n#if !defined(HEX_MODULE_EXPORT)\n    struct ImGuiWindow;\n#endif\n\nstruct KeyEquivalent {\n    bool valid;\n    bool ctrl, opt, cmd, shift;\n    int key;\n};\n\nEXPORT_MODULE namespace hex {\n\n    class View;\n\n    class Key {\n    public:\n        constexpr Key() = default;\n        constexpr Key(Keys key) : m_key(static_cast<u32>(key)) { }\n\n        bool operator==(const Key &) const = default;\n        auto operator<=>(const Key &) const = default;\n\n        [[nodiscard]] constexpr u32 getKeyCode() const { return m_key; }\n    private:\n        u32 m_key = 0;\n    };\n\n\n    constexpr static auto CTRL                  = Key(static_cast<Keys>(0x0100'0000));\n    constexpr static auto ALT                   = Key(static_cast<Keys>(0x0200'0000));\n    constexpr static auto SHIFT                 = Key(static_cast<Keys>(0x0400'0000));\n    constexpr static auto SUPER                 = Key(static_cast<Keys>(0x0800'0000));\n    constexpr static auto CurrentView           = Key(static_cast<Keys>(0x1000'0000));\n    constexpr static auto AllowWhileTyping      = Key(static_cast<Keys>(0x2000'0000));\n    constexpr static auto CTRLCMD               = Key(static_cast<Keys>(0x4000'0000));\n    constexpr static auto ShowOnWelcomeScreen   = Key(static_cast<Keys>(0x8000'0000));\n\n    class Shortcut {\n    public:\n        Shortcut() = default;\n        Shortcut(Keys key);\n        explicit Shortcut(std::set<Key> keys);\n        Shortcut(const Shortcut &other) = default;\n        Shortcut(Shortcut &&) noexcept = default;\n\n        constexpr static auto None = Keys(0);\n\n        Shortcut& operator=(const Shortcut &other) = default;\n        Shortcut& operator=(Shortcut &&) noexcept = default;\n\n        Shortcut operator+(const Key &other) const;\n        Shortcut &operator+=(const Key &other);\n        bool operator<(const Shortcut &other) const;\n        bool operator==(const Shortcut &other) const;\n\n        bool isLocal() const;\n        std::string toString() const;\n        KeyEquivalent toKeyEquivalent() const;\n        const std::set<Key>& getKeys() const;\n        bool has(Key key) const;\n        bool matches(const Shortcut &other) const;\n\n    private:\n        friend Shortcut operator+(const Key &lhs, const Key &rhs);\n\n        std::set<Key> m_keys;\n    };\n\n    Shortcut operator+(const Key &lhs, const Key &rhs);\n\n    /**\n     * @brief The ShortcutManager handles global and view-specific shortcuts.\n     * New shortcuts can be constructed using the + operator on Key objects. For example: CTRL + ALT + Keys::A\n     */\n    class ShortcutManager {\n    public:\n        using Callback = std::function<void()>;\n        using EnabledCallback = std::function<bool()>;\n        struct ShortcutEntry {\n            Shortcut shortcut;\n            std::vector<UnlocalizedString> unlocalizedName;\n            Callback callback;\n            EnabledCallback enabledCallback;\n        };\n\n        /**\n         * @brief Add a global shortcut. Global shortcuts can be triggered regardless of what view is currently focused\n         * @param shortcut The shortcut to add.\n         * @param unlocalizedName The unlocalized name of the shortcut\n         * @param callback The callback to call when the shortcut is triggered.\n         * @param enabledCallback Callback that's called to check if this shortcut is enabled\n         */\n        static void addGlobalShortcut(const Shortcut &shortcut, const std::vector<UnlocalizedString> &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; });\n        static void addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; });\n\n        /**\n         * @brief Add a view-specific shortcut. View-specific shortcuts can only be triggered when the specified view is focused.\n         * @param view The view to add the shortcut to.\n         * @param shortcut The shortcut to add.\n         * @param unlocalizedName The unlocalized name of the shortcut\n         * @param callback The callback to call when the shortcut is triggered.\n         * @param enabledCallback Callback that's called to check if this shortcut is enabled\n         */\n        static void addShortcut(View *view, const Shortcut &shortcut, const std::vector<UnlocalizedString> &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; });\n        static void addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const Callback &callback, const EnabledCallback &enabledCallback = []{ return true; });\n\n\n        /**\n         * @brief Process a key event. This should be called from the main loop.\n         * @param currentView Current view to process\n         * @param ctrl Whether the CTRL key is pressed\n         * @param alt Whether the ALT key is pressed\n         * @param shift Whether the SHIFT key is pressed\n         * @param super Whether the SUPER key is pressed\n         * @param focused Whether the current view is focused\n         * @param keyCode The key code of the key that was pressed\n         */\n        static void process(const View *currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode);\n\n        /**\n         * @brief Process a key event. This should be called from the main loop.\n         * @param ctrl Whether the CTRL key is pressed\n         * @param alt Whether the ALT key is pressed\n         * @param shift Whether the SHIFT key is pressed\n         * @param super Whether the SUPER key is pressed\n         * @param keyCode The key code of the key that was pressed\n         */\n        static void processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode);\n\n        /**\n         * @brief Runs the callback of a shortcut as if it was pressed on the keyboard\n         * @param shortcut Shortcut to run\n         * @param view View the shortcut belongs to or nullptr to run a global shortcut\n         * @return True if a callback was executed, false if not\n         */\n        static bool runShortcut(const Shortcut &shortcut, const View *view = nullptr);\n\n        /**\n         * @brief Clear all shortcuts\n         */\n        static void clearShortcuts();\n\n        static Shortcut getShortcutByName(const std::vector<UnlocalizedString> &unlocalizedName, const View *view = nullptr);\n\n        static void resumeShortcuts();\n        static void pauseShortcuts();\n\n        static void enableMacOSMode();\n\n        [[nodiscard]] static std::optional<UnlocalizedString> getLastActivatedMenu();\n        static void resetLastActivatedMenu();\n\n        [[nodiscard]] static std::optional<Shortcut> getPreviousShortcut();\n\n        [[nodiscard]] static std::vector<ShortcutEntry> getGlobalShortcuts();\n        [[nodiscard]] static std::vector<ShortcutEntry> getViewShortcuts(const View *view);\n\n        [[nodiscard]] static bool updateShortcut(const Shortcut &oldShortcut, Shortcut newShortcut, View *view = nullptr);\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/task_manager.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <hex/api/localization_manager.hpp>\n\n#include <functional>\n#include <mutex>\n#include <memory>\n#include <list>\n#include <condition_variable>\n#include <source_location>\n#include <thread>\n#include <hex/trace/exceptions.hpp>\n\nEXPORT_MODULE namespace hex {\n\n    class TaskHolder;\n    class TaskManager;\n\n    /**\n     * @brief A type representing a running asynchronous task\n     */\n    class Task {\n    public:\n        Task() = default;\n        Task(UnlocalizedString unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function);\n\n        Task(const Task&) = delete;\n        Task(Task &&other) noexcept;\n        ~Task();\n\n        /**\n         * @brief Updates the current process value of the task\n         * @param value Current value\n         */\n        void update(u64 value);\n        void update() const;\n        void increment();\n\n        /**\n         * @brief Sets the maximum value of the task\n         * @param value Maximum value of the task\n         */\n        void setMaxValue(u64 value);\n\n\n        /**\n         * @brief Interrupts the task\n         * For regular Tasks, this just throws an exception to stop the task.\n         * If a custom interrupt callback is set, an exception is thrown and the callback is called.\n         */\n        void interrupt();\n\n        /**\n         * @brief Sets a callback that is called when the task is interrupted\n         * @param callback Callback to be called\n         */\n        void setInterruptCallback(std::function<void()> callback);\n\n        [[nodiscard]] bool isBackgroundTask() const;\n        [[nodiscard]] bool isBlocking() const;\n        [[nodiscard]] bool isFinished() const;\n        [[nodiscard]] bool hadException() const;\n        [[nodiscard]] bool wasInterrupted() const;\n        [[nodiscard]] bool shouldInterrupt() const;\n\n        void clearException();\n        [[nodiscard]] std::string getExceptionMessage() const;\n\n        [[nodiscard]] const UnlocalizedString &getUnlocalizedName();\n        [[nodiscard]] u64 getValue() const;\n        [[nodiscard]] u64 getMaxValue() const;\n\n        void wait() const;\n\n    private:\n        void finish();\n        void interruption();\n        void exception(const char *message);\n\n    private:\n        mutable std::mutex m_mutex;\n\n        UnlocalizedString m_unlocalizedName;\n        std::atomic<u64> m_currValue = 0, m_maxValue = 0;\n        std::function<void()> m_interruptCallback;\n        std::function<void(Task &)> m_function;\n\n        std::atomic<bool> m_shouldInterrupt = false;\n        std::atomic<bool> m_background = true;\n        std::atomic<bool> m_blocking = false;\n\n        std::atomic_flag m_interrupted;\n        std::atomic_flag m_finished;\n        std::atomic_flag m_hadException;\n        std::string m_exceptionMessage;\n\n        struct TaskInterruptor: public std::exception {\n            TaskInterruptor() {\n                trace::disableExceptionCaptureForCurrentThread();\n            }\n            virtual ~TaskInterruptor() = default;\n\n            [[nodiscard]] const char* what() const noexcept override {\n                return \"Task Interrupted\";\n            }\n        };\n\n        friend class TaskHolder;\n        friend class TaskManager;\n    };\n\n    /**\n     * @brief A type holding a weak reference to a Task\n     */\n    class TaskHolder {\n    public:\n        TaskHolder() = default;\n        explicit TaskHolder(std::weak_ptr<Task> task) : m_task(std::move(task)) { }\n\n        [[nodiscard]] bool isRunning() const;\n        [[nodiscard]] bool hadException() const;\n        [[nodiscard]] bool wasInterrupted() const;\n        [[nodiscard]] bool shouldInterrupt() const;\n\n        [[nodiscard]] u32 getProgress() const;\n\n        void interrupt() const;\n        void wait() const;\n    private:\n        std::weak_ptr<Task> m_task;\n    };\n\n    /**\n     * @brief The Task Manager is responsible for running and managing asynchronous tasks\n     */\n    class TaskManager {\n    public:\n        TaskManager() = delete;\n\n        static void init();\n        static void exit();\n\n        constexpr static auto NoProgress = 0;\n\n        /**\n         * @brief Creates a new asynchronous task that gets displayed in the Task Manager in the footer\n         * @param unlocalizedName Name of the task\n         * @param maxValue Maximum value of the task\n         * @param function Function to be executed\n         * @return A TaskHolder holding a weak reference to the task\n         */\n        static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function<void(Task &)> function);\n\n        /**\n        * @brief Creates a new asynchronous task that gets displayed in the Task Manager in the footer\n        * @param unlocalizedName Name of the task\n        * @param maxValue Maximum value of the task\n        * @param function Function to be executed\n        * @return A TaskHolder holding a weak reference to the task\n        */\n        static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function<void()> function);\n\n        /**\n         * @brief Creates a new asynchronous task that does not get displayed in the Task Manager\n         * @param unlocalizedName Name of the task\n         * @param function Function to be executed\n         * @return A TaskHolder holding a weak reference to the task\n         */\n        static TaskHolder createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function<void(Task &)> function);\n\n        /**\n         * @brief Creates a new asynchronous task that does not get displayed in the Task Manager\n         * @param unlocalizedName Name of the task\n         * @param function Function to be executed\n         * @return A TaskHolder holding a weak reference to the task\n         */\n        static TaskHolder createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function<void()> function);\n\n        /**\n         * @brief Creates a new asynchronous task that shows a blocking modal window\n         * @param unlocalizedName Name of the task\n         * @param maxValue Maximum value of the task\n         * @param function Function to be executed\n         * @return A TaskHolder holding a weak reference to the task\n         */\n        static TaskHolder createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function<void(Task &)> function);\n\n        /**\n        * @brief Creates a new asynchronous task that shows a blocking modal window\n        * @param unlocalizedName Name of the task\n        * @param maxValue Maximum value of the task\n        * @param function Function to be executed\n        * @return A TaskHolder holding a weak reference to the task\n        */\n        static TaskHolder createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function<void()> function);\n\n        /**\n         * @brief Creates a new synchronous task that will execute the given function at the start of the next frame\n         * @param function Function to be executed\n         */\n        static void doLater(const std::function<void()> &function);\n\n        /**\n         * @brief Creates a new synchronous task that will execute the given function at the start of the next frame\n         * @param function Function to be executed\n         * @param location Source location of the function call. This is used to make sure repeated calls to the function at the same location are only executed once\n         */\n        static void doLaterOnce(const std::function<void()> &function, std::source_location location = std::source_location::current());\n\n        /**\n         * @brief Creates a callback that will be executed when all tasks are finished\n         * @param function Function to be executed\n         */\n        static void runWhenTasksFinished(const std::function<void()> &function);\n\n        /**\n         * @brief Sets the name of the current thread\n         * @param name Name of the thread\n         */\n        static void setCurrentThreadName(const std::string &name);\n\n        /**\n         * @brief Gets the name of the current thread\n         * @return Name of the thread\n         */\n        static std::string_view getCurrentThreadName();\n\n        /**\n         * @brief Sets the ID of the main thread\n         * @param threadId ID of the main thread\n         */\n        static void setMainThreadId(std::thread::id threadId);\n\n        /**\n         * @brief Checks if the current thread is the main thread\n         * @return True if the current thread is the main thread, false otherwise\n         */\n        static bool isMainThread();\n\n        /**\n         * @brief Cleans up finished tasks\n         */\n        static void collectGarbage();\n\n        static Task& getCurrentTask();\n\n        static size_t getRunningTaskCount();\n        static size_t getRunningBackgroundTaskCount();\n        static size_t getRunningBlockingTaskCount();\n\n        static const std::list<std::shared_ptr<Task>>& getRunningTasks();\n        static void runDeferredCalls();\n\n        static void addTaskCompletionCallback(const std::function<void(Task&)>& function);\n\n    private:\n        static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function);\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/theme_manager.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <hex/helpers/fs.hpp>\n\n#include <string>\n#include <variant>\n\n#include <nlohmann/json.hpp>\n#include <imgui.h>\n\nEXPORT_MODULE namespace hex {\n\n    /**\n     * @brief The Theme Manager takes care of loading and applying themes\n     */\n    class ThemeManager {\n    public:\n        constexpr static auto NativeTheme = \"Native\";\n\n        using ColorMap = std::map<std::string, u32>;\n\n        struct Style {\n            std::variant<ImVec2*, float*> value;\n            float min;\n            float max;\n            bool needsScaling;\n        };\n        using StyleMap = std::map<std::string, Style>;\n\n        /**\n         * @brief Changes the current theme to the one with the given name\n         * @param name Name of the theme to change to\n         */\n        static void changeTheme(std::string name);\n\n        /**\n         * @brief Adds a theme from json data\n         * @param content JSON data of the theme\n         */\n        static void addTheme(const std::string &content);\n\n        /**\n         * @brief Adds a theme handler to handle color values loaded from a theme file\n         * @param name Name of the handler\n         * @param colorMap Map of color names to their respective constants\n         * @param getFunction Function to get the color value of a constant\n         * @param setFunction Function to set the color value of a constant\n         */\n        static void addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function<ImColor(u32)> &getFunction, const std::function<void(u32, ImColor)> &setFunction);\n\n        /**\n         * @brief Adds a style handler to handle style values loaded from a theme file\n         * @param name Name of the handler\n         * @param styleMap Map of style names to their respective constants\n         */\n        static void addStyleHandler(const std::string &name, const StyleMap &styleMap);\n\n        static void reapplyCurrentTheme();\n\n        static std::vector<std::string> getThemeNames();\n        static const std::string &getImageTheme();\n\n        static std::optional<ImColor> parseColorString(const std::string &colorString);\n\n        static nlohmann::json exportCurrentTheme(const std::string &name);\n\n        static void reset();\n\n        static void setAccentColor(const ImColor &color);\n\n    public:\n        struct ThemeHandler {\n            ColorMap colorMap;\n            std::function<ImColor(u32)> getFunction;\n            std::function<void(u32, ImColor)> setFunction;\n        };\n\n        struct StyleHandler {\n            StyleMap styleMap;\n        };\n\n        static const std::map<std::string, ThemeHandler>& getThemeHandlers();\n        static const std::map<std::string, StyleHandler>& getStyleHandlers();\n\n\n    private:\n        ThemeManager() = default;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/tutorial_manager.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/localization_manager.hpp>\n\n#include <string>\n#include <list>\n#include <variant>\n\n#include <hex/ui/imgui_imhex_extensions.h>\n\nstruct ImRect;\n\nEXPORT_MODULE namespace hex {\n\n    class TutorialManager {\n    public:\n        enum class Position : u8 {\n            None    = 0,\n            Top     = 1,\n            Bottom  = 2,\n            Left    = 4,\n            Right   = 8\n        };\n\n        using DrawFunction = std::function<void()>;\n\n        struct Tutorial {\n            Tutorial() = delete;\n            Tutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) :\n                m_unlocalizedName(unlocalizedName),\n                m_unlocalizedDescription(unlocalizedDescription) { }\n\n            struct Step {\n                explicit Step(Tutorial *parent) : m_parent(parent) { }\n\n                /**\n                 * @brief Adds a highlighting with text to a specific element\n                 * @param unlocalizedText Unlocalized text to display next to the highlighting\n                 * @param ids ID of the element to highlight\n                 * @return Current step\n                 */\n                Step& addHighlight(const UnlocalizedString &unlocalizedText, std::initializer_list<std::variant<Lang, std::string, int>> &&ids);\n\n                /**\n                 * @brief Adds a highlighting to a specific element\n                 * @param ids ID of the element to highlight\n                 * @return Current step\n                 */\n                Step& addHighlight(std::initializer_list<std::variant<Lang, std::string, int>> &&ids);\n\n                /**\n                 * @brief Sets the text that will be displayed in the tutorial message box\n                 * @param unlocalizedTitle Title of the message box\n                 * @param unlocalizedMessage Main message of the message box\n                 * @param position Position of the message box\n                 * @return Current step\n                 */\n                Step& setMessage(const UnlocalizedString &unlocalizedTitle, const UnlocalizedString &unlocalizedMessage, Position position = Position::None);\n\n                /**\n                 * @brief Allows this step to be skipped by clicking on the advance button\n                 * @return Current step\n                 */\n                Step& allowSkip();\n\n                Step& onAppear(std::function<void()> callback);\n                Step& onComplete(std::function<void()> callback);\n\n\n                /**\n                 * @brief Checks if this step is the current step\n                 * @return True if this step is the current step\n                 */\n                bool isCurrent() const;\n\n                /**\n                 * @brief Completes this step if it is the current step\n                 */\n                void complete() const;\n\n            private:\n                struct Highlight {\n                    UnlocalizedString unlocalizedText;\n                    std::vector<std::variant<Lang, std::string, int>> highlightIds;\n                };\n\n                struct Message {\n                    Position position;\n                    UnlocalizedString unlocalizedTitle;\n                    UnlocalizedString unlocalizedMessage;\n                    bool allowSkip;\n                };\n\n            private:\n                void addHighlights() const;\n                void removeHighlights() const;\n\n                void advance(i32 steps = 1) const;\n\n                friend class TutorialManager;\n\n                Tutorial *m_parent;\n                std::vector<Highlight> m_highlights;\n                std::optional<Message> m_message;\n                std::function<void()> m_onAppear, m_onComplete;\n                DrawFunction m_drawFunction;\n            };\n\n            Step& addStep();\n\n            const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; }\n            const UnlocalizedString& getUnlocalizedDescription() const { return m_unlocalizedDescription; }\n\n        private:\n            friend class TutorialManager;\n\n            void start();\n\n            UnlocalizedString m_unlocalizedName;\n            UnlocalizedString m_unlocalizedDescription;\n            std::list<Step> m_steps;\n            decltype(m_steps)::iterator m_currentStep, m_latestStep;\n        };\n\n        static void init();\n\n        /**\n         * @brief Gets a list of all tutorials\n         * @return List of all tutorials\n         */\n        static const std::map<std::string, Tutorial>& getTutorials();\n\n        /**\n         * @brief Gets the currently running tutorial\n         * @return Iterator pointing to the current tutorial\n         */\n        static std::map<std::string, Tutorial>::iterator getCurrentTutorial();\n\n        /**\n         * @brief Creates a new tutorial that can be started later\n         * @param unlocalizedName Name of the tutorial\n         * @param unlocalizedDescription\n         * @return Reference to created tutorial\n         */\n        static Tutorial& createTutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription);\n\n        /**\n         * @brief Starts the tutorial with the given name\n         * @param unlocalizedName Name of tutorial to start\n         */\n        static void startTutorial(const UnlocalizedString &unlocalizedName);\n        static void stopCurrentTutorial();\n\n        static void startHelpHover();\n        static void addInteractiveHelpText(std::initializer_list<std::variant<Lang, std::string, int>> &&ids, UnlocalizedString unlocalizedString);\n        static void addInteractiveHelpLink(std::initializer_list<std::variant<Lang, std::string, int>> &&ids, std::string link);\n\n        static void setLastItemInteractiveHelpPopup(std::function<void()> callback);\n        static void setLastItemInteractiveHelpLink(std::string link);\n\n\n        /**\n         * @brief Draws the tutorial\n         * @note This function should only be called by the main GUI\n         */\n        static void drawTutorial();\n\n        /**\n         * @brief Resets the tutorial manager\n         */\n        static void reset();\n\n        static void setRenderer(std::function<DrawFunction(const std::string &)> renderer);\n\n        static void postElementRendered(ImGuiID id, const ImRect &boundingBox);\n\n    private:\n        TutorialManager() = delete;\n\n        static void drawHighlights();\n        static void drawMessageBox(std::optional<Tutorial::Step::Message> message);\n    };\n\n    inline TutorialManager::Position operator|(TutorialManager::Position a, TutorialManager::Position b) {\n        return static_cast<TutorialManager::Position>(static_cast<u8>(a) | static_cast<u8>(b));\n    }\n\n    inline TutorialManager::Position operator&(TutorialManager::Position a, TutorialManager::Position b) {\n        return static_cast<TutorialManager::Position>(static_cast<u8>(a) & static_cast<u8>(b));\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api/workspace_manager.hpp",
    "content": "#pragma once\n\n#include <wolv/io/fs.hpp>\n#include <hex/helpers/auto_reset.hpp>\n\n#include <map>\n#include <string>\n\nEXPORT_MODULE namespace hex {\n\n    class WorkspaceManager {\n    public:\n        struct Workspace {\n            std::string layout;\n            std::fs::path path;\n            bool builtin;\n        };\n\n        static void createWorkspace(const std::string &name, const std::string &layout = \"\");\n        static void switchWorkspace(const std::string &name);\n\n        static void importFromFile(const std::fs::path &path);\n        static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {}, bool builtin = false);\n\n        static void removeWorkspace(const std::string &name);\n\n        static const std::map<std::string, Workspace>& getWorkspaces();\n        static const std::map<std::string, Workspace>::iterator& getCurrentWorkspace();\n\n        static void reset();\n        static void reload();\n\n        static void process();\n\n    private:\n        WorkspaceManager() = default;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/api_urls.hpp",
    "content": "#pragma once\n\nconstexpr static auto ImHexApiURL  = \"https://api.werwolv.net/imhex\";\nconstexpr static auto GitHubApiURL = \"https://api.github.com/repos/WerWolv/ImHex\";\n"
  },
  {
    "path": "lib/libimhex/include/hex/data_processor/attribute.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <hex/api/localization_manager.hpp>\n\n#include <string>\n#include <string_view>\n#include <map>\n#include <vector>\n\nnamespace hex::dp {\n\n    class Node;\n\n    class Attribute {\n    public:\n        enum class Type {\n            Integer,\n            Float,\n            Buffer\n        };\n\n        enum class IOType {\n            In,\n            Out\n        };\n\n        Attribute(IOType ioType, Type type, UnlocalizedString unlocalizedName);\n        ~Attribute();\n\n        [[nodiscard]] int getId() const { return m_id; }\n        void setId(int id) { m_id = id; }\n\n        [[nodiscard]] IOType getIOType() const { return m_ioType; }\n        [[nodiscard]] Type getType() const { return m_type; }\n        [[nodiscard]] const UnlocalizedString &getUnlocalizedName() const { return m_unlocalizedName; }\n\n        void addConnectedAttribute(int linkId, Attribute *to) { m_connectedAttributes.insert({ linkId, to }); }\n        void removeConnectedAttribute(int linkId) { m_connectedAttributes.erase(linkId); }\n        [[nodiscard]] std::map<int, Attribute *> &getConnectedAttributes() { return m_connectedAttributes; }\n\n        [[nodiscard]] Node *getParentNode() const { return m_parentNode; }\n\n        [[nodiscard]] std::vector<u8>& getOutputData() {\n            if (!m_outputData.empty())\n                return m_outputData;\n            else\n                return m_defaultData;\n        }\n\n        void clearOutputData() { m_outputData.clear(); }\n\n        [[nodiscard]] std::vector<u8>& getDefaultData() { return m_defaultData; }\n\n        static void setIdCounter(int id);\n\n    private:\n        int m_id;\n        IOType m_ioType;\n        Type m_type;\n        UnlocalizedString m_unlocalizedName;\n        std::map<int, Attribute *> m_connectedAttributes;\n        Node *m_parentNode = nullptr;\n\n        std::vector<u8> m_outputData;\n        std::vector<u8> m_defaultData;\n\n        friend class Node;\n        void setParentNode(Node *node) { m_parentNode = node; }\n\n        static int s_idCounter;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/data_processor/link.hpp",
    "content": "#pragma once\n\nnamespace hex::dp {\n\n    class Link {\n    public:\n        Link(int from, int to);\n\n        [[nodiscard]] int getId() const { return m_id; }\n        void setId(int id) { m_id = id; }\n\n        [[nodiscard]] int getFromId() const { return m_from; }\n        [[nodiscard]] int getToId() const { return m_to; }\n\n        static void setIdCounter(int id);\n\n    private:\n        int m_id;\n        int m_from, m_to;\n\n        static int s_idCounter;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/data_processor/node.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <hex/api/localization_manager.hpp>\n\n#include <hex/data_processor/attribute.hpp>\n\n#include <set>\n#include <span>\n#include <utility>\n#include <vector>\n\n#include <nlohmann/json_fwd.hpp>\n#include <imgui.h>\n#include <hex/providers/provider_data.hpp>\n\nnamespace hex::prv {\n    class Provider;\n    class Overlay;\n}\n\nnamespace hex::dp {\n\n    class Node {\n    public:\n        Node(UnlocalizedString unlocalizedTitle, std::vector<Attribute> attributes);\n\n        virtual ~Node() = default;\n\n        [[nodiscard]] int getId() const { return m_id; }\n        void setId(int id) { m_id = id; }\n\n        [[nodiscard]] const UnlocalizedString &getUnlocalizedName() const { return m_unlocalizedName; }\n        void setUnlocalizedName(const UnlocalizedString &unlocalizedName) { m_unlocalizedName = unlocalizedName; }\n\n        [[nodiscard]] const UnlocalizedString &getUnlocalizedTitle() const { return m_unlocalizedTitle; }\n        void setUnlocalizedTitle(std::string title) { m_unlocalizedTitle = std::move(title); }\n\n        [[nodiscard]] std::vector<Attribute> &getAttributes() { return m_attributes; }\n        [[nodiscard]] const std::vector<Attribute> &getAttributes() const { return m_attributes; }\n\n        void setCurrentOverlay(prv::Overlay *overlay) {\n            m_overlay = overlay;\n        }\n\n        void draw();\n        virtual void process() = 0;\n        virtual void reset() { }\n\n        virtual void store(nlohmann::json &j) const { std::ignore = j; }\n        virtual void load(const nlohmann::json &j) { std::ignore = j; }\n\n        struct NodeError: public std::exception {\n            Node *node;\n            std::string message;\n\n            NodeError(Node *node, std::string  message) : node(node), message(std::move(message)) {}\n\n            [[nodiscard]] const char* what() const noexcept override {\n                return this->message.c_str();\n            }\n        };\n\n        void resetOutputData() {\n            for (auto &attribute : m_attributes)\n                attribute.clearOutputData();\n        }\n\n        void resetProcessedInputs() {\n            m_processedInputs.clear();\n        }\n\n        void setPosition(ImVec2 pos) {\n            m_position = pos;\n        }\n\n        [[nodiscard]] ImVec2 getPosition() const {\n            return m_position;\n        }\n\n        static void setIdCounter(int id);\n\n        const std::vector<u8>& getBufferOnInput(u32 index);\n        const i128& getIntegerOnInput(u32 index);\n        const double& getFloatOnInput(u32 index);\n\n        void setBufferOnOutput(u32 index, std::span<const u8> data);\n        void setIntegerOnOutput(u32 index, i128 integer);\n        void setFloatOnOutput(u32 index, double floatingPoint);\n\n        static void interrupt();\n\n    protected:\n        virtual void drawNode() { }\n\n    private:\n        int m_id;\n        UnlocalizedString m_unlocalizedTitle, m_unlocalizedName;\n        std::vector<Attribute> m_attributes;\n        std::set<u32> m_processedInputs;\n        prv::Overlay *m_overlay = nullptr;\n        ImVec2 m_position;\n\n        static int s_idCounter;\n\n        Attribute& getAttribute(u32 index);\n        Attribute *getConnectedInputAttribute(u32 index);\n        void markInputProcessed(u32 index);\n        void unmarkInputProcessed(u32 index);\n\n    protected:\n        [[noreturn]] void throwNodeError(const std::string &msg);\n\n        void setOverlayData(u64 address, const std::vector<u8> &data);\n        void setAttributes(std::vector<Attribute> attributes);\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/auto_reset.hpp",
    "content": "#pragma once\n\n#include <hex/api/imhex_api/system.hpp>\n#include <hex/helpers/logger.hpp>\n\nnamespace hex {\n\n    namespace impl {\n\n        class AutoResetBase {\n        public:\n            virtual ~AutoResetBase() = default;\n\n        private:\n            friend void ImHexApi::System::impl::cleanup();\n            virtual void reset() = 0;\n        };\n\n    }\n\n    template<typename T>\n    class AutoReset : public impl::AutoResetBase {\n    public:\n        using Type = T;\n\n        AutoReset() noexcept {\n            try {\n                ImHexApi::System::impl::addAutoResetObject(this);\n            } catch (std::exception &e) {\n                log::error(\"Failed to register AutoReset object: {}\", e.what());\n            }\n        }\n\n        explicit(false) AutoReset(const T &value) : AutoReset() {\n            m_value = value;\n            m_valid = true;\n        }\n\n        explicit(false) AutoReset(T &&value) noexcept : AutoReset() {\n            m_value = std::move(value);\n            m_valid = true;\n        }\n\n        ~AutoReset() override {\n            ImHexApi::System::impl::removeAutoResetObject(this);\n        }\n\n        T* operator->() {\n            return &m_value;\n        }\n\n        const T* operator->() const {\n            return &m_value;\n        }\n\n        T& operator*() {\n            return m_value;\n        }\n\n        const T& operator*() const {\n            return m_value;\n        }\n\n        operator T&() {\n            return m_value;\n        }\n\n        operator const T&() const {\n            return m_value;\n        }\n\n        AutoReset& operator=(const T &value) {\n            m_value = value;\n            m_valid = true;\n            return *this;\n        }\n\n        AutoReset& operator=(T &&value) noexcept {\n            m_value = std::move(value);\n            m_valid = true;\n            return *this;\n        }\n\n        [[nodiscard]] bool isValid() const {\n            return m_valid;\n        }\n\n    private:\n        void reset() override {\n            if constexpr (requires(T t) { t.reset(); }) {\n                m_value.reset();\n            } else if constexpr (requires(T t) { t.clear(); }) {\n                m_value.clear();\n            } else if constexpr (std::is_pointer_v<T>) {\n                m_value = nullptr; // cppcheck-suppress nullPointer\n            } else {\n                m_value = { };\n            }\n\n            m_valid = false;\n        }\n\n    private:\n        bool m_valid = true;\n        T m_value;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/binary_pattern.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/helpers/utils.hpp>\n\n#include <vector>\n\nnamespace hex {\n\n    class BinaryPattern {\n    public:\n        BinaryPattern() = default;\n        explicit BinaryPattern(const std::string &pattern);\n\n        [[nodiscard]] bool isValid() const;\n        [[nodiscard]] u64 getSize() const;\n\n        [[nodiscard]] bool matches(const std::vector<u8> &bytes) const;\n        [[nodiscard]] bool matchesByte(u8 byte, u32 offset) const;\n\n        struct Pattern {\n            u8 mask, value;\n        };\n\n    private:\n        std::vector<Pattern> m_patterns;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/concepts.hpp",
    "content": "#pragma once\n\n#include <type_traits>\n#include <memory>\n\n#include <concepts>\n\nnamespace hex {\n\n    template<typename T>\n    struct always_false : std::false_type { };\n\n    template<typename T, size_t Size>\n    concept has_size = sizeof(T) == Size;\n\n    template<typename T>\n    class ICloneable {\n    public:\n        virtual ~ICloneable() = default;\n        [[nodiscard]] virtual std::unique_ptr<T> clone() const = 0;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/crypto.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <wolv/utils/expected.hpp>\n\n#include <array>\n#include <string>\n#include <vector>\n\n#define CRYPTO_ERROR_INVALID_KEY_LENGTH (-1)\n#define CRYPTO_ERROR_INVALID_MODE (-2)\n\nnamespace hex::prv {\n    class Provider;\n}\n\nnamespace hex::crypt {\n\n    void initialize();\n    void exit();\n\n    u8 crc8(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut);\n    u16 crc16(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut);\n    u32 crc32(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut);\n\n    std::array<u8, 16> md5(prv::Provider *&data, u64 offset, size_t size);\n    std::array<u8, 20> sha1(prv::Provider *&data, u64 offset, size_t size);\n    std::array<u8, 28> sha224(prv::Provider *&data, u64 offset, size_t size);\n    std::array<u8, 32> sha256(prv::Provider *&data, u64 offset, size_t size);\n    std::array<u8, 48> sha384(prv::Provider *&data, u64 offset, size_t size);\n    std::array<u8, 64> sha512(prv::Provider *&data, u64 offset, size_t size);\n\n    std::array<u8, 16> md5(const std::vector<u8> &data);\n    std::array<u8, 20> sha1(const std::vector<u8> &data);\n    std::array<u8, 28> sha224(const std::vector<u8> &data);\n    std::array<u8, 32> sha256(const std::vector<u8> &data);\n    std::array<u8, 48> sha384(const std::vector<u8> &data);\n    std::array<u8, 64> sha512(const std::vector<u8> &data);\n\n    std::vector<u8> decode64(const std::vector<u8> &input);\n    std::vector<u8> encode64(const std::vector<u8> &input);\n    std::vector<u8> decode16(const std::string &input);\n    std::string encode16(const std::vector<u8> &input);\n\n    i128 decodeSleb128(const std::vector<u8> &bytes);\n    u128 decodeUleb128(const std::vector<u8> &bytes);\n    std::vector<u8> encodeSleb128(i128 value);\n    std::vector<u8> encodeUleb128(u128 value);\n\n    enum class AESMode : u8 {\n        ECB    = 0,\n        CBC    = 1,\n        CFB128 = 2,\n        CTR    = 3,\n        GCM    = 4,\n        CCM    = 5,\n        OFB    = 6,\n        XTS    = 7\n    };\n\n    enum class KeyLength : u8 {\n        Key128Bits = 0,\n        Key192Bits = 1,\n        Key256Bits = 2\n    };\n\n    wolv::util::Expected<std::vector<u8>, int> aesDecrypt(AESMode mode, KeyLength keyLength, const std::vector<u8> &key, std::array<u8, 8> nonce, std::array<u8, 8> iv, const std::vector<u8> &input);\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/debugging.hpp",
    "content": "#pragma once\n\n#include <wolv/utils/preproc.hpp>\n\n#include <hex/ui/imgui_imhex_extensions.h>\n\n\n#if defined(DEBUG)\n    #define DBG_DEFINE_DEBUG_VARIABLE(type, name)                       \\\n        static type name;                                               \\\n        hex::dbg::impl::drawDebugVariable(name, WOLV_STRINGIFY(name));\n#else\n    #define DBG_DEFINE_DEBUG_VARIABLE(type, name)                       \\\n        static_assert(false, \"Debug variables are only intended for use during development.\");\n#endif\n\nnamespace hex::trace {\n    struct StackTraceResult;\n}\n\nnamespace hex::dbg {\n\n    namespace impl {\n        bool &getDebugWindowState();\n\n        template<typename T>\n        static void drawDebugVariable(T &variable, std::string_view name) {\n            if (!getDebugWindowState())\n                return;\n\n            if (ImGui::Begin(\"Debug Variables\", &getDebugWindowState(), ImGuiWindowFlags_AlwaysAutoResize)) {\n                using Type = std::remove_cvref_t<T>;\n                if constexpr (std::same_as<Type, bool>) {\n                    ImGui::Checkbox(name.data(), &variable);\n                } else if constexpr (std::integral<Type> || std::floating_point<Type>) {\n                    ImGui::DragScalar(name.data(), ImGuiExt::getImGuiDataType<Type>(), &variable);\n                } else if constexpr (std::same_as<Type, ImVec2>) {\n                    ImGui::DragFloat2(name.data(), &variable.x);\n                } else if constexpr (std::same_as<Type, std::string>) {\n                    ImGui::InputText(name.data(), variable);\n                } else if constexpr (std::same_as<Type, ImColor>) {\n                    ImGui::ColorEdit4(name.data(), &variable.Value.x, ImGuiColorEditFlags_AlphaBar);\n                } else {\n                    static_assert(hex::always_false<Type>::value, \"Unsupported type\");\n                }\n            }\n            ImGui::End();\n        }\n    }\n\n    bool debugModeEnabled();\n    void setDebugModeEnabled(bool enabled);\n\n    void printStackTrace(const trace::StackTraceResult &stackTrace);\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/default_paths.hpp",
    "content": "#pragma once\n\n#include <hex/helpers/fs.hpp>\n\n#include <array>\n#include <vector>\n\nnamespace hex::paths {\n\n    namespace impl {\n\n        class DefaultPath {\n        protected:\n            constexpr DefaultPath() = default;\n            virtual ~DefaultPath() = default;\n\n        public:\n            DefaultPath(const DefaultPath&) = delete;\n            DefaultPath(DefaultPath&&) = delete;\n            DefaultPath& operator=(const DefaultPath&) = delete;\n            DefaultPath& operator=(DefaultPath&&) = delete;\n\n            virtual std::vector<std::fs::path> all() const = 0;\n            virtual std::vector<std::fs::path> read() const;\n            virtual std::vector<std::fs::path> write() const;\n        };\n\n        class ConfigPath : public DefaultPath {\n        public:\n            explicit ConfigPath(std::fs::path postfix) : m_postfix(std::move(postfix)) {}\n\n            std::vector<std::fs::path> all() const override;\n\n        private:\n            std::fs::path m_postfix;\n        };\n\n        class DataPath : public DefaultPath {\n        public:\n            explicit DataPath(std::fs::path postfix) : m_postfix(std::move(postfix)) {}\n\n            std::vector<std::fs::path> all() const override;\n            std::vector<std::fs::path> write() const override;\n\n        private:\n            std::fs::path m_postfix;\n        };\n\n        class PluginPath : public DefaultPath {\n        public:\n            explicit PluginPath(std::fs::path postfix) : m_postfix(std::move(postfix)) {}\n\n            std::vector<std::fs::path> all() const override;\n\n        private:\n            std::fs::path m_postfix;\n        };\n\n    }\n\n    std::vector<std::fs::path> getDataPaths(bool includeSystemFolders);\n    std::vector<std::fs::path> getConfigPaths(bool includeSystemFolders);\n\n    const static inline impl::ConfigPath Config(\"config\");\n    const static inline impl::ConfigPath Recent(\"recent\");\n    const static inline impl::ConfigPath Updates(\"updates\");\n\n    const static inline impl::PluginPath Libraries(\"lib\");\n    const static inline impl::PluginPath Plugins(\"plugins\");\n\n    const static inline impl::DataPath Patterns(\"patterns\");\n    const static inline impl::DataPath PatternsInclude(\"includes\");\n    const static inline impl::DataPath Magic(\"magic\");\n    const static inline impl::DataPath Yara(\"yara\");\n    const static inline impl::DataPath YaraAdvancedAnalysis(\"yara/advanced_analysis\");\n    const static inline impl::DataPath Backups(\"backups\");\n    const static inline impl::DataPath Resources(\"resources\");\n    const static inline impl::DataPath Constants(\"constants\");\n    const static inline impl::DataPath Encodings(\"encodings\");\n    const static inline impl::DataPath Logs(\"logs\");\n    const static inline impl::DataPath Scripts(\"scripts\");\n    const static inline impl::DataPath Inspectors(\"scripts/inspectors\");\n    const static inline impl::DataPath Themes(\"themes\");\n    const static inline impl::DataPath Nodes(\"scripts/nodes\");\n    const static inline impl::DataPath Layouts(\"layouts\");\n    const static inline impl::DataPath Workspaces(\"workspaces\");\n    const static inline impl::DataPath Disassemblers(\"disassemblers\");\n\n    constexpr static inline std::array<const impl::DefaultPath*, 22> All = {\n        &Config,\n        &Recent,\n        &Updates,\n\n        &Libraries,\n        &Plugins,\n\n        &Patterns,\n        &PatternsInclude,\n        &Magic,\n        &Yara,\n        &YaraAdvancedAnalysis,\n        &Backups,\n        &Resources,\n        &Constants,\n        &Encodings,\n        &Logs,\n        &Scripts,\n        &Inspectors,\n        &Themes,\n        &Nodes,\n        &Layouts,\n        &Workspaces,\n        &Disassemblers\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/encoding_file.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <map>\n#include <string_view>\n#include <vector>\n#include <span>\n\n#include <wolv/io/fs.hpp>\n\nnamespace hex {\n\n    class EncodingFile {\n    public:\n        enum class Type\n        {\n            Thingy\n        };\n\n        EncodingFile();\n        EncodingFile(const EncodingFile &other);\n        EncodingFile(EncodingFile &&other) noexcept;\n        EncodingFile(Type type, const std::fs::path &path);\n        EncodingFile(Type type, const std::string &content);\n\n        EncodingFile& operator=(const EncodingFile &other);\n        EncodingFile& operator=(EncodingFile &&other) noexcept;\n\n        [[nodiscard]] std::pair<std::string_view, size_t> getEncodingFor(std::span<const u8> buffer) const;\n        [[nodiscard]] u64 getEncodingLengthFor(std::span<u8> buffer) const;\n        [[nodiscard]] u64 getShortestSequence() const { return m_shortestSequence; }\n        [[nodiscard]] u64 getLongestSequence()  const { return m_longestSequence;  }\n        [[nodiscard]] std::string decodeAll(std::span<const u8> buffer) const;\n\n        [[nodiscard]] bool valid() const { return m_valid; }\n\n        [[nodiscard]] const std::string& getTableContent() const { return m_tableContent; }\n\n        [[nodiscard]] const std::string& getName() const { return m_name; }\n\n    private:\n        void parse(const std::string &content);\n\n        bool m_valid = false;\n\n        std::string m_name;\n        std::string m_tableContent;\n        std::unique_ptr<std::map<size_t, std::map<std::vector<u8>, std::string>>> m_mapping;\n\n        u64 m_shortestSequence = std::numeric_limits<u64>::max();\n        u64 m_longestSequence  = std::numeric_limits<u64>::min();\n    };\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/fmt.hpp",
    "content": "#pragma once\n\n#include <string_view>\n#include <fmt/core.h>\n#include <fmt/ranges.h>\n#include <fmt/ostream.h>\n\n#include <fmt/format.h>\n\n#if !defined(LIBWOLV_BUILTIN_UINT128)\n    #include <pl/helpers/types.hpp>\n#endif"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/fs.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <string>\n#include <vector>\n#include <filesystem>\n#include <functional>\n\n#include <wolv/io/fs.hpp>\n\nEXPORT_MODULE namespace hex::fs {\n\n    enum class DialogMode {\n        Open,\n        Save,\n        Folder\n    };\n\n    struct ItemFilter {\n        // Human-friendly name\n        std::string name;\n        // Extensions that constitute this filter\n        std::string spec;\n    };\n\n    void setFileBrowserErrorCallback(const std::function<void(const std::string&)> &callback);\n    bool openFileBrowser(DialogMode mode, const std::vector<ItemFilter> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath = {}, bool multiple = false);\n\n    void openFileExternal(std::fs::path filePath);\n    void openFolderExternal(std::fs::path dirPath);\n    void openFolderWithSelectionExternal(std::fs::path selectedFilePath);\n\n\n    bool isPathWritable(const std::fs::path &path);\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/http_requests.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <future>\n#include <map>\n#include <string>\n#include <vector>\n\n#include <wolv/io/file.hpp>\n#include <wolv/utils/guards.hpp>\n\n#if defined(OS_WEB)\n    #include <emscripten/fetch.h>\n#endif\n\ntypedef void CURL;\n\nnamespace hex {\n\n    class HttpRequest {\n    public:\n\n        class ResultBase {\n        public:\n            ResultBase() = default;\n            explicit ResultBase(u32 statusCode) : m_statusCode(statusCode), m_valid(true) { }\n\n            [[nodiscard]] u32 getStatusCode() const {\n                return m_statusCode;\n            }\n\n            [[nodiscard]] bool isSuccess() const {\n                return this->getStatusCode() == 200;\n            }\n\n            [[nodiscard]] bool isValid() const {\n                return m_valid;\n            }\n\n        private:\n            u32 m_statusCode = 0;\n            bool m_valid = false;\n        };\n\n        template<typename T>\n        class Result : public ResultBase {\n        public:\n            Result() = default;\n            Result(u32 statusCode, T data) : ResultBase(statusCode), m_data(std::move(data)) { }\n\n            [[nodiscard]]\n            const T& getData() const {\n                return m_data;\n            }\n\n        private:\n            T m_data;\n        };\n\n        HttpRequest(std::string method, std::string url);\n        ~HttpRequest();\n\n        HttpRequest(const HttpRequest&) = delete;\n        HttpRequest& operator=(const HttpRequest&) = delete;\n\n        HttpRequest(HttpRequest &&other) noexcept;\n\n        HttpRequest& operator=(HttpRequest &&other) noexcept;\n\n        static void setProxyState(bool enabled);\n        static void setProxyUrl(std::string proxy);\n\n        void setMethod(std::string method) {\n            m_method = std::move(method);\n        }\n\n        void setUrl(std::string url) {\n            m_url = std::move(url);\n        }\n\n        void addHeader(std::string key, std::string value) {\n            m_headers[std::move(key)] = std::move(value);\n        }\n\n        void setBody(std::string body) {\n            m_body = std::move(body);\n        }\n\n        void setTimeout(u32 timeout) {\n            m_timeout = timeout;\n        }\n\n        float getProgress() const {\n            return m_progress;\n        }\n\n        void cancel() {\n            m_canceled = true;\n        }\n\n        template<typename T = std::string>\n        std::future<Result<T>> downloadFile(const std::fs::path &path);\n\n        std::future<Result<std::vector<u8>>> downloadFile();\n\n        template<typename T = std::string>\n        std::future<Result<T>> uploadFile(const std::fs::path &path, const std::string &mimeName = \"filename\");\n\n        template<typename T = std::string>\n        std::future<Result<T>> uploadFile(std::vector<u8> data, const std::string &mimeName = \"filename\", const std::fs::path &fileName = \"data.bin\");\n\n        template<typename T = std::string>\n        std::future<Result<T>> execute();\n\n        static std::string urlEncode(const std::string &input);\n\n        static std::string urlDecode(const std::string &input);\n\n        void setProgress(float progress) { m_progress = progress; }\n        bool isCanceled() const { return m_canceled; }\n\n        static size_t writeToVector(void *contents, size_t size, size_t nmemb, void *userdata);\n        static size_t writeToFile(void *contents, size_t size, size_t nmemb, void *userdata);\n\n        template<typename T>\n        Result<T> executeImpl(std::vector<u8> &data);\n\n    private:\n        static void checkProxyErrors();\n        void setDefaultConfig();\n\n    private:\n        #if defined(OS_WEB)\n        emscripten_fetch_attr_t m_attr;\n        #else\n        CURL *m_curl;\n        #endif\n\n        std::mutex m_transmissionMutex;\n\n        std::string m_method;\n        std::string m_url;\n        std::string m_body;\n        std::promise<std::vector<u8>> m_promise;\n        std::map<std::string, std::string> m_headers;\n        u32 m_timeout = 1000;\n\n        std::atomic<float> m_progress = 0.0F;\n        std::atomic<bool> m_canceled = false;\n    };\n}\n\n\n#if defined(OS_WEB)\n#include <hex/helpers/http_requests_emscripten.hpp>\n#else\n#include <hex/helpers/http_requests_native.hpp>\n#endif\n"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/http_requests_emscripten.hpp",
    "content": "#pragma once\n\n#if defined(OS_WEB)\n\n    #include <future>\n\n    #include <emscripten/fetch.h>\n\n    namespace hex {\n        template<typename T>\n        std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {\n            return std::async(std::launch::async, [this, path] {\n                std::vector<u8> response;\n\n                // Execute the request\n                auto result = this->executeImpl<T>(response);\n\n                // Write the result to the file\n                wolv::io::File file(path, wolv::io::File::Mode::Create);\n                file.writeBuffer(reinterpret_cast<const u8*>(result.getData().data()), result.getData().size());\n\n                return result;\n            });\n        }\n\n        template<typename T>\n        std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {\n            std::ignore = path;\n            std::ignore = mimeName;\n            throw std::logic_error(\"Not implemented\");\n        }\n\n        template<typename T>\n        std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {\n            std::ignore = data;\n            std::ignore = mimeName;\n            std::ignore = fileName;\n            throw std::logic_error(\"Not implemented\");\n        }\n\n        template<typename T>\n        std::future<HttpRequest::Result<T>> HttpRequest::execute() {\n            return std::async(std::launch::async, [this] {\n                std::vector<u8> responseData;\n\n                return this->executeImpl<T>(responseData);\n            });\n        }\n\n        template<typename T>\n        HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {\n            strcpy(m_attr.requestMethod, m_method.c_str());\n            m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;\n\n            if (!m_body.empty()) {\n                m_attr.requestData = m_body.c_str();\n                m_attr.requestDataSize = m_body.size();\n            }\n\n            std::vector<const char*> headers;\n            for (auto it = m_headers.begin(); it != m_headers.end(); it++) {\n                headers.push_back(it->first.c_str());\n                headers.push_back(it->second.c_str());\n            }\n            headers.push_back(nullptr);\n            m_attr.requestHeaders = headers.data();\n\n            // Send request\n            emscripten_fetch_t* fetch = emscripten_fetch(&m_attr, m_url.c_str());\n\n            data.resize(fetch->numBytes);\n            std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin());\n\n            return Result<T>(fetch->status, { data.begin(), data.end() });\n        }\n\n    }\n\n#endif"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/http_requests_native.hpp",
    "content": "#pragma once\n\n#if !defined(OS_WEB)\n\n    #include <string>\n    #include <future>\n    #include <mutex>\n\n    #include <hex/helpers/logger.hpp>\n    #include <hex/helpers/fmt.hpp>\n\n    #include <wolv/utils/string.hpp>\n\n    namespace hex {\n\n        namespace impl {\n\n            void setWriteFunctions(CURL *curl, wolv::io::File &file);\n            void setWriteFunctions(CURL *curl, std::vector<u8> &data);\n            void setupFileUpload(CURL *curl, wolv::io::File &file, const std::string &fileName, const std::string &mimeName);\n            void setupFileUpload(CURL *curl, const std::vector<u8> &data, const std::fs::path &fileName, const std::string &mimeName);\n            int executeCurl(CURL *curl, const std::string &url, const std::string &method, const std::string &body, std::map<std::string, std::string> &headers);\n            long getStatusCode(CURL *curl);\n            std::string getStatusText(int result);\n\n        }\n\n\n        template<typename T>\n        std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {\n            return std::async(std::launch::async, [this, path] {\n                std::vector<u8> response;\n\n                wolv::io::File file(path, wolv::io::File::Mode::Create);\n                impl::setWriteFunctions(m_curl, file);\n\n                return this->executeImpl<T>(response);\n            });\n        }\n\n        template<typename T>\n        std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {\n            return std::async(std::launch::async, [this, path, mimeName]{\n                auto fileName = wolv::util::toUTF8String(path.filename());\n                wolv::io::File file(path, wolv::io::File::Mode::Read);\n\n                impl::setupFileUpload(m_curl, file, fileName, mimeName);\n\n                std::vector<u8> responseData;\n                impl::setWriteFunctions(m_curl, responseData);\n\n                return this->executeImpl<T>(responseData);\n            });\n        }\n\n        template<typename T>\n        std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {\n            return std::async(std::launch::async, [this, data = std::move(data), mimeName, fileName]{\n                impl::setupFileUpload(m_curl, data, fileName, mimeName);\n\n                std::vector<u8> responseData;\n                impl::setWriteFunctions(m_curl, responseData);\n\n                return this->executeImpl<T>(responseData);\n            });\n        }\n\n        template<typename T>\n        std::future<HttpRequest::Result<T>> HttpRequest::execute() {\n            return std::async(std::launch::async, [this] {\n\n                std::vector<u8> responseData;\n                impl::setWriteFunctions(m_curl, responseData);\n\n                return this->executeImpl<T>(responseData);\n            });\n        }\n\n        template<typename T>\n        HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {\n            setDefaultConfig();\n\n            std::scoped_lock lock(m_transmissionMutex);\n\n            if (auto result = impl::executeCurl(m_curl, m_url, m_method, m_body, m_headers); result != 0) {\n                log::error(\"Http request '{0} {1}' failed with error {2}: '{3}'\", m_method, m_url, u32(result), impl::getStatusText(result));\n                checkProxyErrors();\n            }\n\n            return Result<T>(impl::getStatusCode(m_curl), { data.begin(), data.end() });\n        }\n\n    }\n\n#endif"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/keys.hpp",
    "content": "#pragma once\n\n#if defined(__cplusplus)\nenum class Keys {\n#else\nenum Keys {\n#endif\n    Invalid,\n    Space,\n    Apostrophe,\n    Comma,\n    Minus,\n    Period,\n    Slash,\n    Num0,\n    Num1,\n    Num2,\n    Num3,\n    Num4,\n    Num5,\n    Num6,\n    Num7,\n    Num8,\n    Num9,\n    Semicolon,\n    Equals,\n    A,\n    B,\n    C,\n    D,\n    E,\n    F,\n    G,\n    H,\n    I,\n    J,\n    K,\n    L,\n    M,\n    N,\n    O,\n    P,\n    Q,\n    R,\n    S,\n    T,\n    U,\n    V,\n    W,\n    X,\n    Y,\n    Z,\n    LeftBracket,\n    Backslash,\n    RightBracket,\n    GraveAccent,\n    World1,\n    World2,\n    Escape,\n    Enter,\n    Tab,\n    Backspace,\n    Insert,\n    Delete,\n    Right,\n    Left,\n    Down,\n    Up,\n    PageUp,\n    PageDown,\n    Home,\n    End,\n    CapsLock,\n    ScrollLock,\n    NumLock,\n    PrintScreen,\n    Pause,\n    F1,\n    F2,\n    F3,\n    F4,\n    F5,\n    F6,\n    F7,\n    F8,\n    F9,\n    F10,\n    F11,\n    F12,\n    F13,\n    F14,\n    F15,\n    F16,\n    F17,\n    F18,\n    F19,\n    F20,\n    F21,\n    F22,\n    F23,\n    F24,\n    F25,\n    KeyPad0,\n    KeyPad1,\n    KeyPad2,\n    KeyPad3,\n    KeyPad4,\n    KeyPad5,\n    KeyPad6,\n    KeyPad7,\n    KeyPad8,\n    KeyPad9,\n    KeyPadDecimal,\n    KeyPadDivide,\n    KeyPadMultiply,\n    KeyPadSubtract,\n    KeyPadAdd,\n    KeyPadEnter,\n    KeyPadEqual,\n    Menu\n};\n\nenum Keys scanCodeToKey(int scanCode);\nint keyToScanCode(enum Keys key);\n"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/literals.hpp",
    "content": "#pragma once\n\nnamespace hex::literals {\n\n    /* Byte literals */\n\n    constexpr static unsigned long long operator\"\"_Bytes(unsigned long long bytes) noexcept {\n        return bytes;\n    }\n\n    constexpr static unsigned long long operator\"\"_KiB(unsigned long long kiB) noexcept {\n        return operator\"\"_Bytes(kiB * 1024);\n    }\n\n    constexpr static unsigned long long operator\"\"_MiB(unsigned long long MiB) noexcept {\n        return operator\"\"_KiB(MiB * 1024);\n    }\n\n    constexpr static unsigned long long operator\"\"_GiB(unsigned long long GiB) noexcept {\n        return operator\"\"_MiB(GiB * 1024);\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/logger.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <fmt/core.h>\n#include <fmt/color.h>\n\n#include <wolv/io/file.hpp>\n#include <wolv/utils/guards.hpp>\n\nEXPORT_MODULE namespace hex::log {\n\n    namespace impl {\n\n        [[nodiscard]] FILE *getDestination();\n        [[nodiscard]] wolv::io::File& getFile();\n        [[nodiscard]] bool isRedirected();\n        [[maybe_unused]] void redirectToFile();\n        [[maybe_unused]] void enableColorPrinting();\n\n        [[nodiscard]] bool isLoggingSuspended();\n        [[nodiscard]] bool isDebugLoggingEnabled();\n\n        void lockLoggerMutex();\n        void unlockLoggerMutex();\n\n        struct LogEntry {\n            std::string_view project;\n            std::string_view level;\n            std::string message;\n        };\n\n        const std::vector<LogEntry>& getLogEntries();\n        void addLogEntry(std::string_view project, std::string_view level, std::string message);\n\n        [[maybe_unused]] void printPrefix(FILE *dest, fmt::text_style ts, std::string_view level, std::string_view projectName);\n\n        template<typename ... Args>\n        [[maybe_unused]] void print(fmt::text_style ts, std::string_view level, fmt::format_string<Args...> fmt, Args && ... args) {\n            if (isLoggingSuspended()) [[unlikely]]\n                return;\n\n            lockLoggerMutex();\n            ON_SCOPE_EXIT { unlockLoggerMutex(); };\n\n            auto dest = getDestination();\n            try {\n                printPrefix(dest, ts, level, IMHEX_PROJECT_NAME);\n\n                auto message = fmt::format(fmt, std::forward<Args>(args)...);\n                fmt::print(dest, \"{}\\n\", message);\n                std::fflush(dest);\n\n                addLogEntry(IMHEX_PROJECT_NAME, level, std::move(message));\n            } catch (const std::exception&) {\n                /* Ignore any exceptions, we can't do anything anyway */\n            }\n        }\n\n        namespace color {\n\n            fmt::color debug();\n            fmt::color info();\n            fmt::color warn();\n            fmt::color error();\n            fmt::color fatal();\n\n        }\n\n    }\n\n    void suspendLogging();\n    void resumeLogging();\n    void enableDebugLogging();\n\n    template<typename ... Args>\n    [[maybe_unused]] void debug(fmt::format_string<Args...> fmt, Args && ... args) {\n        if (impl::isDebugLoggingEnabled()) [[unlikely]] {\n            impl::print(fg(impl::color::debug()) | fmt::emphasis::bold, \"[DEBUG]\", fmt, std::forward<Args>(args)...);\n        } else {\n            impl::addLogEntry(IMHEX_PROJECT_NAME, \"[DEBUG]\", fmt::format(fmt, std::forward<Args>(args)...));\n        }\n    }\n\n    template<typename ... Args>\n    [[maybe_unused]] void info(fmt::format_string<Args...> fmt, Args && ... args) {\n        impl::print(fg(impl::color::info()) | fmt::emphasis::bold, \"[INFO] \", fmt, std::forward<Args>(args)...);\n    }\n\n    template<typename ... Args>\n    [[maybe_unused]] void warn(fmt::format_string<Args...> fmt, Args && ... args) {\n        impl::print(fg(impl::color::warn()) | fmt::emphasis::bold, \"[WARN] \", fmt, std::forward<Args>(args)...);\n    }\n\n    template<typename ... Args>\n    [[maybe_unused]] void error(fmt::format_string<Args...> fmt, Args && ... args) {\n        impl::print(fg(impl::color::error()) | fmt::emphasis::bold, \"[ERROR]\", fmt, std::forward<Args>(args)...);\n    }\n\n    template<typename ... Args>\n    [[maybe_unused]] void fatal(fmt::format_string<Args...> fmt, Args && ... args) {\n        impl::print(fg(impl::color::fatal()) | fmt::emphasis::bold, \"[FATAL]\", fmt, std::forward<Args>(args)...);\n    }\n\n    template<typename ... Args>\n    [[maybe_unused]] void print(fmt::format_string<Args...> fmt, Args && ... args) {\n        impl::lockLoggerMutex();\n        ON_SCOPE_EXIT { impl::unlockLoggerMutex(); };\n\n        try {\n            auto dest = impl::getDestination();\n            fmt::print(dest, fmt, std::forward<Args>(args)...);\n            std::fflush(dest);\n        } catch (const std::exception&) {\n            /* Ignore any exceptions, we can't do anything anyway */\n        }\n    }\n\n    template<typename ... Args>\n    [[maybe_unused]] void println(fmt::format_string<Args...> fmt, Args && ... args) {\n        impl::lockLoggerMutex();\n        ON_SCOPE_EXIT { impl::unlockLoggerMutex(); };\n\n        try {\n            auto dest = impl::getDestination();\n            fmt::print(dest, fmt, std::forward<Args>(args)...);\n            fmt::print(\"\\n\");\n            std::fflush(dest);\n        } catch (const std::exception&) {\n            /* Ignore any exceptions, we can't do anything anyway */\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/magic.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/api/task_manager.hpp>\n#include <hex/helpers/literals.hpp>\n#include <hex/helpers/fs.hpp>\n\n#include <string>\n#include <vector>\n\nnamespace hex::prv {\n    class Provider;\n}\n\nnamespace hex::magic {\n\n    using namespace hex::literals;\n\n    bool compile();\n    std::string getDescription(const std::vector<u8> &data, bool firstEntryOnly = false);\n    std::string getDescription(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);\n    std::string getMIMEType(const std::vector<u8> &data, bool firstEntryOnly = false);\n    std::string getMIMEType(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);\n    std::string getExtensions(const std::vector<u8> &data, bool firstEntryOnly = false);\n    std::string getExtensions(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);\n    std::string getAppleCreatorType(const std::vector<u8> &data, bool firstEntryOnly = false);\n    std::string getAppleCreatorType(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);\n\n    bool isValidMIMEType(const std::string &mimeType);\n\n    struct FoundPattern {\n        std::fs::path patternFilePath;\n        std::string author;\n        std::string description;\n        std::optional<std::string> mimeType;\n        std::optional<u64> magicOffset;\n    };\n\n    std::vector<FoundPattern> findViablePatterns(prv::Provider *provider, Task* task = nullptr);\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/menu_items.hpp",
    "content": "#pragma once\n#include <hex/api/shortcut_manager.hpp>\n\nnamespace hex::menu {\n\n    void enableNativeMenuBar(bool enabled);\n    bool isNativeMenuBarUsed();\n\n    bool beginMainMenuBar();\n    void endMainMenuBar();\n\n    bool beginMenu(const char *label, bool enabled = true);\n    void endMenu();\n\n    bool beginTaskBarMenu();\n    void endTaskBarMenu();\n\n    bool beginMenuEx(const char* label, const char* icon, bool enabled = true);\n\n    bool menuItem(const char *label, const Shortcut &shortcut = Shortcut::None, bool selected = false, bool enabled = true);\n    bool menuItem(const char *label, const Shortcut &shortcut, bool *selected, bool enabled = true);\n\n    bool menuItemEx(const char *label, const char *icon, const Shortcut &shortcut = Shortcut::None, bool selected = false, bool enabled = true);\n    bool menuItemEx(const char *label, const char *icon, const Shortcut &shortcut, bool *selected, bool enabled = true);\n\n    void menuSeparator();\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/opengl.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/helpers/concepts.hpp>\n\n#include <cmath>\n#include <vector>\n#include <map>\n#include <span>\n#include <string>\n#include <numbers>\n#include <array>\n\n#include <opengl_support.h>\n#include \"imgui.h\"\n\nnamespace hex::gl {\n\n    namespace impl {\n\n        template<typename T>\n        GLuint getType() {\n            if constexpr (std::is_same_v<T, float>)\n                return GL_FLOAT;\n            else if constexpr (std::is_same_v<T, u8>)\n                return GL_UNSIGNED_BYTE;\n            else if constexpr (std::is_same_v<T, u16>)\n                return GL_UNSIGNED_SHORT;\n            else if constexpr (std::is_same_v<T, u32>)\n                return GL_UNSIGNED_INT;\n            else {\n                static_assert(hex::always_false<T>::value, \"Unsupported type\");\n                return 0;\n            }\n        }\n\n    }\n\n    template<typename T, size_t Size>\n    class Vector {\n    public:\n        Vector() = default;\n        Vector(const T val) {\n            for (size_t i = 0; i < Size; i++)\n                m_data[i] = val;\n        }\n\n        Vector(std::array<T, Size> data) : m_data(data) { }\n        Vector(Vector &&other) noexcept : m_data(std::move(other.m_data)) { }\n        Vector(const Vector &other) : m_data(other.m_data) { }\n\n        T &operator[](size_t index) { return m_data[index]; }\n        const T &operator[](size_t index) const { return m_data[index]; }\n\n        std::array<T, Size> &asArray()  { return m_data; }\n\n        T *data() { return m_data.data(); }\n        const T *data() const { return m_data.data(); }\n\n        [[nodiscard]] size_t size() const { return m_data.size(); }\n\n        auto operator=(const Vector& other) {\n            for (size_t i = 0; i < Size; i++)\n                m_data[i] = other[i];\n            return *this;\n        }\n\n        auto operator+=(const Vector& other) {\n            for (size_t i = 0; i < Size; i++)\n                m_data[i] += other.m_data[i];\n\n            return *this;\n        }\n\n        auto operator+=(const T scalar) {\n            for (size_t i = 0; i < Size; i++)\n                m_data[i] += scalar;\n\n            return *this;\n        }\n\n        auto operator-=(const Vector &other) {\n            for (size_t i = 0; i < Size; i++)\n                m_data[i] -= other.m_data[i];\n\n            return *this;\n        }\n\n\n        auto operator-=(const T scalar) {\n            for (size_t i = 0; i < Size; i++)\n                m_data[i] -= scalar;\n\n            return *this;\n        }\n\n        Vector operator*=(const T scalar) {\n            for (size_t i = 0; i < Size; i++)\n                m_data[i] *= scalar;\n\n            return *this;\n        }\n\n        auto operator*(const T scalar) {\n            auto copy = *this;\n            for (size_t i = 0; i < Size; i++)\n                copy[i] *= scalar;\n            return copy;\n        }\n\n        auto operator+(const Vector& other) {\n            auto copy = *this;\n            for (size_t i = 0; i < Size; i++)\n                copy[i] += other[i];\n            return copy;\n        }\n\n        auto operator-(const Vector& other) {\n            auto copy = *this;\n            for (size_t i = 0; i < Size; i++)\n                copy[i] -= other[i];\n            return copy;\n        }\n\n        auto dot(const Vector& other) {\n            T result = 0;\n            for (size_t i = 0; i < Size; i++)\n                result += m_data[i] * other[i];\n            return result;\n        }\n\n        auto cross(const Vector& other) {\n            static_assert(Size == 3, \"Cross product is only defined for 3D vectors\");\n            return Vector({m_data[1] * other[2] - m_data[2] * other[1],\n                           m_data[2] * other[0] - m_data[0] * other[2],\n                           m_data[0] * other[1] - m_data[1] * other[0]});\n        }\n\n        auto magnitude() {\n            return std::sqrt(this->dot(*this));\n        }\n\n        auto normalize() {\n            auto copy = *this;\n             auto length = copy.magnitude();\n            for (size_t i = 0; i < Size; i++)\n                copy[i] /= length;\n            return copy;\n        }\n\n        auto operator==(const Vector& other) {\n            for (size_t i = 0; i < Size; i++)\n                if (m_data[i] != other[i])\n                    return false;\n            return true;\n        }\n\n    private:\n        std::array<T, Size> m_data = { };\n    };\n\n    template<typename T, size_t Rows, size_t Columns>\n    class Matrix {\n    public:\n        Matrix(const T &init) {\n            for (size_t i = 0; i < Rows; i++)\n                for (size_t j = 0; j < Columns; j++)\n                    mat[i * Columns + j] = init;\n        }\n\n        Matrix(const Matrix &A) {\n            mat = A.mat;\n        }\n\n        virtual ~Matrix() {}\n\n        size_t getRows() const {\n            return Rows;\n        }\n\n        size_t getColumns() const {\n            return Columns;\n        }\n\n        T *data() { return this->mat.data(); }\n\n        const T *data() const { return this->mat.data(); }\n\n\n        T &getElement(int row,int col) {\n            return this->mat[row * Columns + col];\n        }\n\n        Vector<T,Rows> getColumn(int col) {\n            Vector<T,Rows> result;\n            for (size_t i = 0; i < Rows; i++)\n                result[i] = this->mat[i * Columns + col];\n            return result;\n        }\n\n        Vector<T,Columns> getRow(int row) {\n            Vector<T,Columns> result;\n            for (size_t i = 0; i < Columns; i++)\n                result[i] = this->mat[row * Columns+i];\n            return result;\n        }\n\n        void updateRow(int row, const Vector<T, Columns> &values) {\n            for (size_t i = 0; i < Columns; i++)\n                this->mat[row * Columns + i] = values[i];\n        }\n\n        void updateColumn(int col, const Vector<T, Rows> &values) {\n            for (size_t i = 0; i < Rows; i++)\n                this->mat[i * Columns + col] = values[i];\n        }\n\n        void updateElement(int row, int col, T value) {\n            this->mat[row * Columns + col] = value;\n        }\n\n        T &operator()(const unsigned &row, const unsigned &col) {\n            return this->mat[row * Columns + col];\n        }\n\n        const T &operator()(const unsigned &row, const unsigned &col) const {\n            return this->mat[row * Columns + col];\n        }\n\n        Matrix& operator=(const Matrix& A) {\n            if (&A == this)\n                return *this;\n\n            for (size_t i = 0; i < Rows; i++)\n                for (size_t j = 0; j < Columns; j++)\n                    mat[i*Columns+j] = A(i, j);\n            return *this;\n        }\n\n        Matrix operator+(const Matrix& A) {\n            Matrix result(0.0);\n\n            for (size_t i = 0; i < Rows; i++)\n                for (size_t j = 0; j < Columns; j++)\n                    result(i, j) = this->mat[i * Columns + j] + A(i, j);\n            return result;\n        }\n\n        Matrix operator-(const Matrix& A) {\n            Matrix result(0.0);\n\n            for (size_t i = 0; i < Rows; i++)\n                for (size_t j = 0; j < Columns; j++)\n                    result(i, j) = this->mat[i * Columns + j] - A(i, j);\n            return result;\n        }\n\n        static Matrix identity() {\n            Matrix  I(0);\n            for (size_t i = 0; i < Rows; i++)\n                for (size_t j = 0; j < Columns; j++)\n                    if(i == j)\n                        I.updateElement(i, j, 1);\n            return I;\n        }\n\n        Matrix transpose() {\n            Matrix  t(0);\n            for (size_t i = 0; i < Columns; i++)\n                for (size_t j = 0; j < Rows; j++)\n                    t.updateElement(i, j, this->mat[j * Rows + i]);\n\n            return t;\n        }\n\n    private:\n        std::array<T,Rows * Columns> mat;\n    };\n\n    template<typename T, size_t Rows, size_t Columns, size_t OtherDimension>\n    Matrix<T,Rows, Columns> operator*(const Matrix<T, Rows, OtherDimension> &A, const Matrix<T, OtherDimension, Columns> &B) {\n        Matrix<T, Rows, Columns> result(0.0);\n\n        for (size_t i = 0; i < Rows; i++)\n            for (size_t j = 0; j < Columns; j++)\n                for (size_t k = 0; k < OtherDimension; k++)\n                    result(i, j) += A(i,k) * B(k, j);\n\n        return result;\n    }\n\n    template<typename T, size_t Rows, size_t Columns>\n    Matrix<T, Rows, Columns> operator*(const Vector<T, Rows> &a, const Vector<T, Columns> &b) {\n        Matrix<T, Rows, Columns> result(0);\n\n        for (size_t i = 0; i < Rows; i++)\n            for (size_t j = 0; j < Columns; j++)\n                result.updateElement(i, j, a[i] * b[j]);\n\n        return result;\n    }\n\n    template<typename T, size_t Rows, size_t Columns>\n    Vector<T, Rows> operator*(const Matrix<T, Rows, Columns> &A, const Vector<T, Columns> &b) {\n        Vector<T, Rows> result(0);\n\n        for (size_t i = 0; i < Rows; i++)\n            for (size_t j = 0; j < Columns; j++)\n                result[i] += A(i, j) * b[j];\n\n        return result;\n    }\n\n    template<typename T, size_t Rows, size_t Columns>\n    Vector<T, Columns> operator*(const Vector<T, Rows> &b, const Matrix<T,Rows,Columns> &A) {\n        Vector<T, Columns> result(0);\n\n        for (size_t i = 0; i < Rows; i++)\n            for (size_t j = 0; j < Columns; j++)\n                result[j] += b[i] * A(i, j);\n\n        return result;\n    }\n\n// Convert horizontal (Xh) vertical (Yv)  and spin (Zs) angles to a rotation matrix.\n// Xh: Horizontal rotation, also known as heading or yaw.\n// Yv: Vertical rotation, also known as pitch or elevation.\n// Zs: Spin rotation, also known as intrinsic rotation, roll or bank.\n// Each column of the rotation matrix represents left, up and forward axis.\n// Angles of rotation are lowercase (x,y,z) in radians and the rotation matrix is uppercase (X,Y,Z).\n// S = sin, C = cos\n// The order of rotation is Yaw->Pitch->Roll (Zs*Yv*Xh)\n//        Zs             Yv          Xh\n// | Cz -Sz  0  0| |Cy  0 Sy  0| |1  0   0  0|   | Cz -Sz 0 0| | Cy Sy*Sx  Sy*Cx 0|\n// | Sz  Cz  0  0|*| 0  1  0  0|*|0 Cx -Sx  0| = | Sz  Cz 0 0|*| 0  Cx    -Sx    0|\n// |  0   0  1  0| |-Sy 0 Cy  0| |0 Sx  Cx  0|   | 0   0  1 0| |-Sy Sx*Cy  Cx*Cy 0|\n// |  0   0  0  1| | 0  0  0  1| |0  0   0  1|   | 0   0  0 1| | 0  0      0     1|\n//    Left      Up         Forward\n// | Cz*Cy Cz*Sy*Sx-Sz*Cx Sz*Sx+Cz*Sy*Cx 0|\n// | Sz*Cy Sz*Sy*Sx+Cz*Cx Cz*Sy*Sx-Sz*Cx 0|\n// |-Sy*Cx Cy*Cx          Sy             0|\n// | 0     0              0              1|\n\n// The order of rotation is Pitch->Yaw->Roll (Zs*Xh*Yv)\n//        Zs           Xh              Yv\n// | Cz -Sz  0  0| |1  0   0  0| |Cy  0 Sy  0|   | Cz -Sz 0 0| | Cy    0   Sy    0|\n// | Sz  Cz  0  0|*|0 Cx -Sx  0|*| 0  1  0  0| = | Sz  Cz 0 0|*| Sx*Sy Cx -Sx*Cy 0|=\n// |  0   0  1  0| |0 Sx  Cx  0| |-Sy 0 Cy  0|   | 0   0  1 0| |-Cx*Sy Sx  Cx*Cy 0|\n// |  0   0  0  1| |0  0   0  1| | 0  0  0  1|   | 0   0  0 1| | 0     0   0     1|\n//    Left           Up     Forward\n// | Cz*Cy-Sz*Sx*Sy -Sz*Cx Cz*Sy+Sz*Sx*Cy 0|\n// | Sz*Cy+Cz*Sx*Sy  Cz*Cx Sz*Sy-Cz*Sx*Cy 0|\n// |-Cx*Sy           Sx    Cx*Cy          0|\n// | 0                0    0              1|\n\n\n// The order of rotation is Roll->Pitch->Yaw (Xh*Yv*Zs)\n//      Xh             Yv            Zs\n// |1  0   0  0| | Cy  0 Sy  0| |Cz -Sz 0  0|   |1  0   0  0| | Cy*Cz -Cy*Sz  Sy  0|\n// |0 Cx -Sx  0|*|  0  1  0  0|*|Sz  Cz 0  0| = |0 Cx -Sx  0|*| Sz     Cz      0  0|\n// |0 Sx  Cx  0| |-Sy  0 Cy  0| | 0   0 1  0|   |0 Sx  Cx  0| |-Sy*Cz  Sy*Sz  Cy  0|\n// |0  0   0  1| |  0  0  0  1| | 0   0 0  1|   |0  0   0  1| |  0      0     0   1|\n//    Left              Up              Forward\n//    | Cy*Cz          -Cy*Sz           Sy     0|\n//   =| Sx*Sy*Cz+Cx*Sz -Sx*Sy*Sz+Cx*Cz -Sx*Cy  0|\n//    |-Cx*Sy*Cz+Sx*Sz  Cx*Sy*Sz+Sx*Cz  Cx*Cy  0|\n//    |  0              0               0      1|\n// just write final answer from here on\n// The order of rotation is Pitch->Roll->Yaw (Xh*Zs*Yv)\n//         Left           Up    Forward\n//         |Cz*Cy         -Sz    Cz*Sy          0|\n//Xh*Zs*Yv=|Cx*Cy*Sz+Sx*Sy Cx*Cz Cx*Sz*Sy-Cy*Sx 0|\n//         |Cy*Sx*Sz-Cx*Sy Cz*Sx Sx*Sz*Sy+Cx*Cy 0|\n//         |0\t\t\t   0\t 0              1|\n// The order of rotation is Roll->Yaw->Pitch (Yv*Xh*Zs)\n//         Left           Up              Forward\n//         |Cy*Cz+Sy*Sx*Sz  Cz*Sy*Sx-Cy*Sz Cx*Sy  0|\n//Yv*Xh*Zs=|Cx*Sz\t  \t\t Cx*Cz\t\t   -Sx    0|\n//         |Cy*Sx*Sz-Cz*Sy\t Cy*Cz*Sx+Sy*Sz Cy*Cx 0|\n//         |0\t\t\t\t 0\t\t\t    0     1|\n// The order of rotation is Yaw->Roll->Pitch (Yv*Zs*Xh)\n//           Left    Up             Forward\n//          |Cy*Cz\tSy*Sx-Cy*Cx*Sz\tCx*Sy+Cy*Sz*Sx 0|\n//Yv*Zs*Xh= |Sz\t\tCz*Cx\t\t   -Cz*Sx          0|\n//          |-Cz*Sy\tCy*Sx+Cx*Sy*Sz\tCy*Cx-Sy*Sz*Sx 0|\n//          |0\t\t0\t\t\t\t0              1|\n\n    enum RotationSequence {\n       XYZ,\n       XZY,\n       YXZ,\n       YZX,\n       ZXY,\n       ZYX\n    };\n\n    template<typename T>\n    Matrix<T, 4, 4> getRotationMatrix(Vector<T,3> ypr, bool radians, RotationSequence rotationSequence) {\n        Matrix<T,4,4> rotation(0);\n\n        T Sx, Cx, Sy, Cy, Sz, Cz;\n        Vector<T,3> angles = ypr;\n        if (!radians)\n            angles *= std::numbers::pi_v<T> / 180;\n\n        Sx = -sin(angles[0]); Cx = cos(angles[0]);\n        Sy = -sin(angles[1]); Cy = cos(angles[1]);\n        Sz = -sin(angles[2]); Cz = cos(angles[2]);\n\n        switch (rotationSequence) {\n            case ZXY:\n                // | Cz*Cy-Sz*Sx*Sy -Sz*Cx Cz*Sy+Sz*Sx*Cy 0|\n                // | Sz*Cy+Cz*Sx*Sy  Cz*Cx Sz*Sy-Cz*Sx*Cy 0|\n                // |-Cx*Sy           Sx    Cx*Cy          0|\n                // | 0                0    0              1|\n                rotation.updateElement(0, 0, Cz * Cy - Sz * Sx * Sy);\n                rotation.updateElement(0, 1, -Sz * Cx);\n                rotation.updateElement(0, 2, Cz * Sy + Sz * Sx * Cy);\n\n                rotation.updateElement(1, 0, Sz * Cy + Cz * Sx * Sy);\n                rotation.updateElement(1, 1, Cz * Cx);\n                rotation.updateElement(1, 2, Sz * Sy - Cz * Sx * Cy);\n\n                rotation.updateElement(2, 0, -Cx * Sy);\n                rotation.updateElement(2, 1, Sx);\n                rotation.updateElement(2, 2, Cx * Cy);\n                break;\n            case ZYX:\n                // | Cz*Cy Cz*Sy*Sx-Sz*Cx Sz*Sx+Cz*Sy*Cx 0|\n                // | Sz*Cy Sz*Sy*Sx+Cz*Cx Sz*Sy*Cx-Cz*Sx 0|\n                // |-Sy    Cy*Sx          Cy*Cx          0|\n                // | 0     0              0              1|\n                rotation.updateElement(0, 0, Cz * Cy);\n                rotation.updateElement(0, 1, Sx * Sy * Cz - Sz * Cx);\n                rotation.updateElement(0, 2, Sz * Sx + Cz * Sy * Cx);\n\n                rotation.updateElement(1, 0, Sz * Cy);\n                rotation.updateElement(1, 1, Sz * Sy * Sx + Cz * Cx);\n                rotation.updateElement(1, 2, Sz * Sy * Cx - Cz * Sx);\n\n                rotation.updateElement(2, 0, -Sy);\n                rotation.updateElement(2, 1, Cy * Sx);\n                rotation.updateElement(2, 2, Cy*Cx);\n                break;\n            case XYZ:\n                //    | Cy*Cz          -Cy*Sz           Sy     0|\n                //   =| Sx*Sy*Cz+Cx*Sz -Sx*Sy*Sz+Cx*Cz -Sx*Cy  0|\n                //    |-Cx*Sy*Cz+Sx*Sz  Cx*Sy*Sz+Sx*Cz  Cx*Cy  0|\n                //    |  0              0               0      1|\n                rotation.updateElement(0, 0,  Cy * Cz);\n                rotation.updateElement(0, 1, -Cy * Sz);\n                rotation.updateElement(0, 2,  Sy);\n\n                rotation.updateElement(1, 0,  Sx * Sy * Cz + Cx * Sz);\n                rotation.updateElement(1, 1, -Sx * Sy * Sz + Cx * Cz);\n                rotation.updateElement(1, 2, -Sx * Cy);\n\n                rotation.updateElement(2, 0, -Cx * Sy * Cz + Sx * Sz);\n                rotation.updateElement(2, 1,  Cx * Sy * Sz + Sx * Cz);\n                rotation.updateElement(2, 2,  Cx * Cy);\n                break;\n            case XZY:\n                //         |Cz*Cy         -Sz    Cz*Sy          0|\n                //Xh*Zs*Yv=|Cx*Cy*Sz+Sx*Sy Cx*Cz Cx*Sz*Sy-Cy*Sx 0|\n                //         |Cy*Sx*Sz-Cx*Sy Cz*Sx Sx*Sz*Sy+Cx*Cy 0|\n                //         |0\t\t\t   0\t 0              1|\n                rotation.updateElement(0, 0, Cy * Cz);\n                rotation.updateElement(0, 1, -Sz);\n                rotation.updateElement(0, 2, Cz * Sy);\n\n                rotation.updateElement(1, 0, Cx * Cy * Sz + Sx * Sy);\n                rotation.updateElement(1, 1, Cx * Cz);\n                rotation.updateElement(1, 2, Cx * Sy * Sz - Sx * Cy);\n\n                rotation.updateElement(2, 0, Sx * Cy * Sz - Cx * Sy);\n                rotation.updateElement(2, 1, Sx * Cz);\n                rotation.updateElement(2, 2, Sx * Sy * Sz + Cx * Cy);\n                break;\n            case YXZ:\n                //         |Cy*Cz+Sy*Sx*Sz  Cz*Sy*Sx-Cy*Sz Cx*Sy  0|\n                //Yv*Xh*Zs=|Cx*Sz\t  \t\t Cx*Cz\t\t   -Sx    0|\n                //         |Cy*Sx*Sz-Cz*Sy\t Cy*Cz*Sx+Sy*Sz Cy*Cx 0|\n                //         |0\t\t\t\t 0\t\t\t    0     1|\n                rotation.updateElement(0, 0,  Cy*Cz+Sy*Sx*Sz );\n                rotation.updateElement(0, 1,  Cz*Sy*Sx-Cy*Sz);\n                rotation.updateElement(0, 2,  Sy*Cx);\n\n                rotation.updateElement(1, 0,  Cx*Sz);\n                rotation.updateElement(1, 1,  Cx*Cz);\n                rotation.updateElement(1, 2, -Sx);\n\n                rotation.updateElement(2, 0,  Cy*Sx*Sz-Cz*Sy);\n                rotation.updateElement(2, 1,  Cy*Cz*Sx+Sy*Sz);\n                rotation.updateElement(2, 2,  Cy*Cx);\n                break;\n            case YZX:\n                //          |Cy*Cz\tSy*Sx-Cy*Cx*Sz\tCx*Sy+Cy*Sz*Sx 0|\n                //Yv*Zs*Xh= |Sz\t\tCz*Cx\t\t   -Cz*Sx          0|\n                //          |-Cz*Sy\tCy*Sx+Cx*Sy*Sz\tCy*Cx-Sy*Sz*Sx 0|\n                //          |0\t\t0\t\t\t\t0              1|\n                rotation.updateElement(0, 0,  Cy*Cz);\n                rotation.updateElement(0, 1,  Sy*Sx-Cy*Cx*Sz);\n                rotation.updateElement(0, 2,  Cx*Sy+Cy*Sz*Sx);\n\n                rotation.updateElement(1, 0,  Sz);\n                rotation.updateElement(1, 1,  Cz*Cx);\n                rotation.updateElement(1, 2, -Cz*Sx);\n\n                rotation.updateElement(2, 0, -Cz*Sy);\n                rotation.updateElement(2, 1,  Cy*Sx+Cx*Sy*Sz);\n                rotation.updateElement(2, 2,  Cy*Cx-Sy*Sz*Sx);\n                break;\n\n\n        }\n\n        rotation.updateElement(3, 3, 1);\n\n        return rotation;\n    }\n\n    template<typename T>\n    Matrix<T, 4, 4> getRotationMatrixFromVectorAngle(Vector<T, 4> rotationVector, bool radians) {\n        Vector<T,3> rotationVector3 = {{rotationVector[0], rotationVector[1], rotationVector[2]}};\n        T theta = rotationVector3.magnitude();\n        if (!radians)\n            theta *= std::numbers::pi / 180;\n        Vector<T,3> axis = rotationVector3;\n        if (theta != 0)\n            axis = axis.normalize();\n        Matrix<T,4,4> rotation = Matrix<T,4,4>::identity();\n        T S = sin(theta);\n        T C = cos(theta);\n        T OMC = 1 - C;\n        T a00 = axis[0] * axis[0] * OMC;\n        T a01 = axis[0] * axis[1] * OMC;\n        T a02 = axis[0] * axis[2] * OMC;\n        T a10 = axis[1] * axis[0] * OMC;\n        T a11 = axis[1] * axis[1] * OMC;\n        T a12 = axis[1] * axis[2] * OMC;\n        T a20 = axis[2] * axis[0] * OMC;\n        T a21 = axis[2] * axis[1] * OMC;\n        T a22 = axis[2] * axis[2] * OMC;\n        T a0S = axis[0] * S;\n        T a1S = axis[1] * S;\n        T a2S = axis[2] * S;\n\n        rotation.updateElement(0, 0, C + a00);\n        rotation.updateElement(0, 1, a01 - a2S);\n        rotation.updateElement(0, 2, a02 + a1S);\n        rotation.updateElement(1, 0, a10 + a2S);\n        rotation.updateElement(1, 1, C + a11);\n        rotation.updateElement(1, 2, a12 - a0S);\n        rotation.updateElement(2, 0, a20 - a1S);\n        rotation.updateElement(2, 1, a21 + a0S);\n        rotation.updateElement(2, 2, C + a22);\n        return rotation;\n\n    }\n\n    enum class MatrixElements {\n        r00, r01, r02,\n        r10, r11, r12,\n        r20, r21, r22,\n    };\n\n    template<typename T>\n    T findValue(Vector<T,3> ypr, MatrixElements matrixElement, RotationSequence rotationSequence) {\n        T Sx, Cx, Sy, Cy, Sz, Cz;\n        Vector<T,3> angles = ypr;\n\n\n        Sx = sin(angles[0]); Cx = cos(angles[0]);\n        Sy = sin(angles[1]); Cy = cos(angles[1]);\n        Sz = sin(angles[2]); Cz = cos(angles[2]);\n\n        switch (rotationSequence) {\n            case ZXY:\n                switch (matrixElement) {\n                    case MatrixElements::r00:\n                        return Cz * Cy - Sz * Sx * Sy;\n                    case MatrixElements::r01:\n                        return -Sz * Cx;\n                    case MatrixElements::r02:\n                        return Cz * Sy + Sz * Sx * Cy;\n                    case MatrixElements::r10:\n                        return Sz * Cy + Cz * Sx * Sy;\n                    case MatrixElements::r11:\n                        return Cz * Cx;\n                    case MatrixElements::r12:\n                        return Sz * Sy - Cz * Sx * Cy;\n                    case MatrixElements::r20:\n                        return -Cx * Sy;\n                    case MatrixElements::r21:\n                        return Sx;\n                    case MatrixElements::r22:\n                        return Cx * Cy;\n                }\n                break;\n            case ZYX:\n                switch (matrixElement) {\n                    case MatrixElements::r00:\n                        return Cz * Cy;\n                    case MatrixElements::r01:\n                        return Sx * Sy * Cz + Cx * Sz;\n                    case MatrixElements::r02:\n                        return -Cx * Sy * Cz + Sx * Sz;\n                    case MatrixElements::r10:\n                        return Cz * Sy;\n                    case MatrixElements::r11:\n                        return Sx * Sy * Sz - Cx * Cz;\n                    case MatrixElements::r12:\n                        return Cx * Sy * Sz + Sx * Cz;\n                    case MatrixElements::r20:\n                        return -Sy;\n                    case MatrixElements::r21:\n                        return Cy * Sx;\n                    case MatrixElements::r22:\n                        return Cy * Cx;\n                }\n                break;\n            case XYZ:\n                switch (matrixElement) {\n                    case MatrixElements::r00:\n                        return Cy * Cz;\n                    case MatrixElements::r01:\n                        return -Cy * Sz;\n                    case MatrixElements::r02:\n                        return Sy;\n                    case MatrixElements::r10:\n                        return Sx * Sy * Cz + Cx * Sz;\n                    case MatrixElements::r11:\n                        return -Sx * Sy * Sz + Cx * Cz;\n                    case MatrixElements::r12:\n                        return -Sx * Cy;\n                    case MatrixElements::r20:\n                        return -Cx * Sy * Cz + Sx * Sz;\n                    case MatrixElements::r21:\n                        return Cx * Sy * Sz + Sx * Cz;\n                    case MatrixElements::r22:\n                        return Cx * Cy;\n                }\n                break;\n            case XZY:\n                switch (matrixElement) {\n                    case MatrixElements::r00:\n                        return Cy * Cz;\n                    case MatrixElements::r01:\n                        return -Sz;\n                    case MatrixElements::r02:\n                        return Cz * Sy;\n                    case MatrixElements::r10:\n                        return Cx * Cy * Sz + Sx * Sy;\n                    case MatrixElements::r11:\n                        return Cx * Cz;\n                    case MatrixElements::r12:\n                        return Cx * Sy * Sz - Sx * Cy;\n                    case MatrixElements::r20:\n                        return Sx * Cy * Sz - Cx * Sy;\n                    case MatrixElements::r21:\n                        return Sx * Cz;\n                    case MatrixElements::r22:\n                        return Sx * Sy * Sz + Cx * Cy;\n                }\n                break;\n            case YXZ:\n                switch (matrixElement) {\n                    case MatrixElements::r00:\n                        return Cy * Cz + Sy * Sx * Sz;\n                    case MatrixElements::r01:\n                        return Cz * Sy * Sx - Cy * Sz;\n                    case MatrixElements::r02:\n                        return Cx * Sy;\n                    case MatrixElements::r10:\n                        return Cx * Sz;\n                    case MatrixElements::r11:\n                        return Cx * Cz;\n                    case MatrixElements::r12:\n                        return -Sx;\n                    case MatrixElements::r20:\n                        return -Cz * Sy + Cy * Sx * Sz;\n                    case MatrixElements::r21:\n                        return Cy * Cz * Sx + Sy * Sz;\n                    case MatrixElements::r22:\n                        return Cy * Cx;\n                }\n                break;\n            case YZX:\n                switch (matrixElement) {\n                    case MatrixElements::r00:\n                        return Cy * Cz;\n                    case MatrixElements::r01:\n                        return Sy * Sx - Cy * Cx * Sz;\n                    case MatrixElements::r02:\n                        return Cx * Sy + Cy * Sz * Sx;\n                    case MatrixElements::r10:\n                        return Sz;\n                    case MatrixElements::r11:\n                        return Cx * Cz;\n                    case MatrixElements::r12:\n                        return -Cz * Sx;\n                    case MatrixElements::r20:\n                        return -Cz * Sy;\n                    case MatrixElements::r21:\n                        return Cy * Sx + Cx * Sy * Sz;\n                    case MatrixElements::r22:\n                        return Cy * Cx - Sy * Sz * Sx;\n                }\n                break;\n        }\n        return 0;\n    }\n\n    template<typename T>\n    Matrix<T, 4,4 > getTransformMatrix(Vector<T,3> xyz, Vector<T,3> ypr, bool radians) {\n        Matrix<T,4,4> transform( 0);\n\n        Matrix<T,3,3> rotation = getRotationMatrix(ypr, radians);\n\n        for(int i=0; i<3; i++)\n            for(int j=0; j<3; j++)\n                transform.updateElement(i, j, rotation.getElement(i, j));\n\n        transform.updateElement(0,3, xyz[0]);\n        transform.updateElement(1,3, xyz[1]);\n        transform.updateElement(2,3, xyz[2]);\n        transform.updateElement(3,3, 1);\n\n        return transform;\n    }\n\n    template<typename T>\n    Vector<T,3> getTranslationVector(Matrix<T, 4,4 > transform_matrix) {\n        Vector<T,3> xyz;\n\n        xyz.push_back(transform_matrix.getElement(0,3));\n        xyz.push_back(transform_matrix.getElement(1,3));\n        xyz.push_back(transform_matrix.getElement(2,3));\n\n        return xyz;\n    }\n\n    template<typename T>\n    Vector<T,3> getYprVector(Matrix<T, 4,4 > transform_matrix) {\n        Vector<T,3> result;\n\n        Matrix<T,3,3> rotation(0);\n        for(int i=0; i<3; i++)\n            for(int j=0; j<3; j++)\n                rotation.updateElement(i, j, transform_matrix.getElement(i, j));\n\n        T sy = sqrt(rotation.getElement(0,0) * rotation.getElement(0,0) +  rotation.getElement(1,0) * rotation.getElement(1,0) );\n\n        bool singular = sy < 1e-6;\n\n        T x, y, z;\n        if (!singular) {\n            x =  atan2(rotation.getElement(1,0), rotation.getElement(0,0));\n            y = atan2(-rotation.getElement(2,0), sy);\n            z = atan2(rotation.getElement(2,1), rotation.getElement(2,2));\n        }\n        else {\n            x = 0;\n            y = atan2(-rotation.getElement(2,0), sy);\n            z =  atan2(-rotation.getElement(1,2), rotation.getElement(1,1));\n        }\n\n        result.push_back(x);\n        result.push_back(y);\n        result.push_back(z);\n\n        return result;\n    }\n\n    Matrix<float,4,4> GetPerspectiveMatrix(\tfloat viewWidth, float viewHeight, float nearVal, float farVal, bool actionType = false);\n    Matrix<float,4,4> GetOrthographicMatrix( float viewWidth, float viewHeight, float nearVal, float farVal, bool actionType = false);\n\n\n    template<typename T>\n    static Matrix<T,4,4> GetObliqueMatrix(\tT width, T height,T nearVal,T farVal, bool actionType = false) {\n        int sign =1;\n        if (actionType)\n            sign=-1;\n        Matrix<T,4,4> result(0);\n        result.updateElement(0,0,sign * nearVal/width);\n        result.updateElement(1,1, sign *  nearVal/height);\n        result.updateElement(2,2,sign * (farVal + nearVal)/( farVal - nearVal ));\n        result.updateElement(3,2,sign * 2*farVal * nearVal/( farVal - nearVal ));\n        result.updateElement(2,3,-sign);\n\n        return result;\n    }\n\n\n    class Shader {\n    public:\n        Shader() = default;\n        Shader(std::string_view vertexSource, std::string_view fragmentSource);\n        ~Shader();\n\n        Shader(const Shader&) = delete;\n        Shader(Shader&& other) noexcept;\n\n        Shader& operator=(const Shader&) = delete;\n        Shader& operator=(Shader&& other) noexcept;\n\n        void bind() const;\n        void unbind() const;\n\n        bool isValid() const { return m_program != 0; }\n\n        void setUniform(std::string_view name, const int &value);\n        void setUniform(std::string_view name, const float &value);\n        bool hasUniform(std::string_view name);\n\n        template<size_t N>\n        void setUniform(std::string_view name, const Vector<float, N> &value) {\n            if constexpr (N == 2)\n                glUniform2f(getUniformLocation(name), value[0], value[1]);\n            else if constexpr (N == 3)\n                glUniform3f(getUniformLocation(name), value[0], value[1], value[2]);\n            else if constexpr (N == 4)\n                glUniform4f(getUniformLocation(name), value[0], value[1], value[2],value[3]);\n        }\n\n        template<size_t N>\n        void setUniform(std::string_view name, Matrix<float, N, N> &value){\n            glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, value.data());\n        }\n\n    private:\n        void compile(GLuint shader, std::string_view source) const;\n        GLint getUniformLocation(std::string_view name);\n\n    private:\n        GLuint m_program = 0;\n        std::map<std::string, GLint> m_uniforms;\n    };\n\n    enum class BufferType {\n        Vertex = GL_ARRAY_BUFFER,\n        Index = GL_ELEMENT_ARRAY_BUFFER\n    };\n\n    template<typename T>\n    class Buffer {\n    public:\n        Buffer() = default;\n        Buffer(BufferType type, std::span<const T> data);\n        ~Buffer();\n        Buffer(const Buffer&) = delete;\n        Buffer(Buffer&& other) noexcept;\n\n        Buffer& operator=(const Buffer&) = delete;\n        Buffer& operator=(Buffer&& other) noexcept;\n\n        void bind() const;\n        void unbind() const;\n\n        void draw(unsigned primitive) const;\n        size_t getSize() const;\n        void update(std::span<const T> data);\n    private:\n        GLuint m_buffer = 0;\n        size_t m_size = 0;\n        GLuint m_type = 0;\n    };\n\n    extern template class Buffer<float>;\n    extern template class Buffer<u32>;\n    extern template class Buffer<u16>;\n    extern template class Buffer<u8>;\n\n    class VertexArray {\n    public:\n        VertexArray();\n        ~VertexArray();\n        VertexArray(const VertexArray&) = delete;\n        VertexArray(VertexArray&& other) noexcept;\n\n        VertexArray& operator=(const VertexArray&) = delete;\n        VertexArray& operator=(VertexArray&& other) noexcept;\n\n        template<typename T>\n        void addBuffer(u32 index, const Buffer<T> &buffer, u32 size = 3) const {\n            glEnableVertexAttribArray(index);\n            buffer.bind();\n            glVertexAttribPointer(index, size, gl::impl::getType<T>(), GL_FALSE, size * sizeof(T), nullptr);\n            buffer.unbind();\n        }\n\n        void bind() const;\n        void unbind() const;\n\n    private:\n        GLuint m_array = 0;\n    };\n\n    class Texture {\n    public:\n        Texture(u32 width, u32 height);\n        ~Texture();\n        Texture(const Texture&) = delete;\n        Texture(Texture&& other) noexcept;\n\n        Texture& operator=(const Texture&) = delete;\n        Texture& operator=(Texture&& other) noexcept;\n\n        void bind() const;\n        void unbind() const;\n\n        GLuint getTexture() const;\n        u32 getWidth() const;\n        u32 getHeight() const;\n        GLuint release();\n\n    private:\n        GLuint m_texture;\n        u32 m_width, m_height;\n    };\n\n    class FrameBuffer {\n    public:\n        FrameBuffer(u32 width, u32 height);\n        ~FrameBuffer();\n        FrameBuffer(const FrameBuffer&) = delete;\n        FrameBuffer(FrameBuffer&& other) noexcept;\n\n        FrameBuffer& operator=(const FrameBuffer&) = delete;\n        FrameBuffer& operator=(FrameBuffer&& other) noexcept;\n\n        void bind() const;\n        void unbind() const;\n\n        void attachTexture(const Texture &texture) const;\n\n    private:\n        GLuint m_frameBuffer = 0, m_renderBuffer = 0;\n    };\n\n    class AxesVectors {\n    public:\n        AxesVectors();\n\n        const std::vector<float>& getVertices() const {\n            return m_vertices;\n        }\n\n        const std::vector<float>& getColors() const {\n            return m_colors;\n        }\n\n        const std::vector<u8>& getIndices() const {\n            return m_indices;\n        }\n\n    private:\n        std::vector<float> m_vertices;\n        std::vector<float> m_colors;\n        std::vector<u8> m_indices;\n\n    };\n\n    class AxesBuffers {\n    public:\n        AxesBuffers(const VertexArray& axesVertexArray, const AxesVectors &axesVectors);\n\n        const gl::Buffer<float>& getVertices() const {\n            return m_vertices;\n        }\n\n        const gl::Buffer<float>& getColors() const {\n            return m_colors;\n        }\n\n        const gl::Buffer<u8>& getIndices() const {\n            return m_indices;\n        }\n\n    private:\n        gl::Buffer<float> m_vertices;\n        gl::Buffer<float> m_colors;\n        gl::Buffer<u8> m_indices;\n    };\n\n    class GridVectors {\n    public:\n        GridVectors(int sliceCount);\n\n        u32 getSlices() const {\n            return m_slices;\n        }\n\n        const std::vector<float>& getVertices() const {\n            return m_vertices;\n        }\n\n        const std::vector<float>& getColors() const {\n            return m_colors;\n        }\n\n        const std::vector<u8>& getIndices() const {\n            return m_indices;\n        }\n\n    private:\n        u32 m_slices;\n        std::vector<float> m_vertices;\n        std::vector<float> m_colors;\n        std::vector<u8> m_indices;\n    };\n\n    class GridBuffers {\n    public:\n        GridBuffers(const VertexArray &gridVertexArray, const GridVectors &gridVectors);\n\n        const gl::Buffer<float>& getVertices() const {\n            return m_vertices;\n        }\n\n        const gl::Buffer<float>& getColors() const {\n            return m_colors;\n        }\n\n        const gl::Buffer<u8>& getIndices() const {\n            return m_indices;\n        }\n\n    private:\n        gl::Buffer<float> m_vertices;\n        gl::Buffer<float> m_colors;\n        gl::Buffer<u8> m_indices;\n    };\n\n    class LightSourceVectors {\n    public:\n        LightSourceVectors(int res);\n\n        void moveTo(const Vector<float, 3> &position);\n\n        const std::vector<float>& getVertices() const {\n            return m_vertices;\n        }\n\n        const std::vector<float>& getNormals() const {\n            return m_normals;\n        }\n\n        const std::vector<float>& getColors() const {\n            return m_colors;\n        }\n\n        const std::vector<u16>& getIndices() const {\n            return m_indices;\n        }\n\n        void setColor(float r, float g, float b) {\n            for (u32 i = 4; i < m_colors.size(); i += 4) {\n                m_colors[i - 4] = r;\n                m_colors[i - 3] = g;\n                m_colors[i - 2] = b;\n                m_colors[i - 1] = 1.0F;\n            }\n        }\n\n    private:\n        int m_resolution;\n        float m_radius;\n\n        std::vector<float> m_vertices;\n        std::vector<float> m_normals;\n        std::vector<float> m_colors;\n        std::vector<u16> m_indices;\n    };\n\n    class LightSourceBuffers {\n    public:\n        LightSourceBuffers(const VertexArray &sourceVertexArray, const LightSourceVectors &sourceVectors);\n\n        void moveVertices(const VertexArray &sourceVertexArray, const LightSourceVectors& sourceVectors);\n        void updateColors(const VertexArray& sourceVertexArray, const LightSourceVectors& sourceVectors);\n\n        const gl::Buffer<float>& getVertices() const {\n            return m_vertices;\n        }\n\n        const gl::Buffer<float>& getNormals() const {\n            return m_normals;\n        }\n\n        const gl::Buffer<float>& getColors() const {\n            return m_colors;\n        }\n\n        const gl::Buffer<u16>& getIndices() const {\n            return m_indices;\n        }\n\n    private:\n        gl::Buffer<float> m_vertices;\n        gl::Buffer<float> m_normals;\n        gl::Buffer<float> m_colors;\n        gl::Buffer<u16> m_indices;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/patches.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <map>\n#include <vector>\n\n#include <wolv/utils/expected.hpp>\n\nnamespace hex {\n\n    namespace prv {\n        class Provider;\n    }\n\n    enum class IPSError {\n        AddressOutOfRange,\n        PatchTooLarge,\n        InvalidPatchHeader,\n        InvalidPatchFormat,\n        MissingEOF\n    };\n\n    enum class PatchKind {\n        IPS,\n        IPS32\n    };\n\n    class Patches {\n    public:\n        Patches() = default;\n        Patches(std::map<u64, u8> &&patches) : m_patches(std::move(patches)) {}\n\n        static wolv::util::Expected<Patches, IPSError> fromProvider(hex::prv::Provider *provider);\n        static wolv::util::Expected<Patches, IPSError> fromIPSPatch(const std::vector<u8> &ipsPatch);\n        static wolv::util::Expected<Patches, IPSError> fromIPS32Patch(const std::vector<u8> &ipsPatch);\n\n        wolv::util::Expected<std::vector<u8>, IPSError> toIPSPatch() const;\n        wolv::util::Expected<std::vector<u8>, IPSError> toIPS32Patch() const;\n\n        const auto& get() const { return m_patches; }\n        auto& get() { return m_patches; }\n\n    private:\n        std::map<u64, u8> m_patches;\n    };\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/scaling.hpp",
    "content": "#pragma once\n\n#include <imgui.h>\n\nnamespace hex {\n\n    [[nodiscard]] float operator\"\"_scaled(long double value);\n    [[nodiscard]] float operator\"\"_scaled(unsigned long long value);\n    [[nodiscard]] ImVec2 scaled(const ImVec2 &vector);\n    [[nodiscard]] ImVec2 scaled(float x, float y);\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/semantic_version.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <compare>\n#include <string>\n#include <vector>\n\nEXPORT_MODULE namespace hex {\n\n    class SemanticVersion {\n    public:\n        SemanticVersion() = default;\n        SemanticVersion(u32 major, u32 minor, u32 patch);\n        SemanticVersion(std::string version);\n        SemanticVersion(std::string_view version);\n        SemanticVersion(const char *version);\n\n        std::strong_ordering operator<=>(const SemanticVersion &) const;\n        bool operator==(const SemanticVersion &other) const;\n\n        u32 major() const;\n        u32 minor() const;\n        u32 patch() const;\n        bool nightly() const;\n        const std::string& buildType() const;\n\n        bool isValid() const;\n\n        std::string get(bool withBuildType = true) const;\n\n    private:\n        std::vector<std::string> m_parts;\n        std::string m_buildType;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/tar.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/helpers/fs.hpp>\n\n#include <memory>\n\nstruct mtar_t;\n\nnamespace hex {\n\n    class Tar {\n    public:\n        enum class Mode {\n            Read,\n            Write,\n            Create\n        };\n\n        Tar() = default;\n        Tar(const std::fs::path &path, Mode mode);\n        ~Tar();\n        Tar(const Tar&) = delete;\n        Tar(Tar&&) noexcept;\n\n        Tar &operator=(Tar &&other) noexcept;\n\n        void close();\n\n        /**\n         * @brief get the error string explaining the error that occurred when opening the file.\n         * This error is a combination of the tar error and the native file open error\n         */\n        std::string getOpenErrorString() const;\n\n        [[nodiscard]] std::vector<u8> readVector(const std::fs::path &path) const;\n        [[nodiscard]] std::string readString(const std::fs::path &path) const;\n\n        void writeVector(const std::fs::path &path, const std::vector<u8> &data) const;\n        void writeString(const std::fs::path &path, const std::string &data) const;\n\n        [[nodiscard]] std::vector<std::fs::path> listEntries(const std::fs::path &basePath = \"/\") const;\n        [[nodiscard]] bool contains(const std::fs::path &path) const;\n\n        void extract(const std::fs::path &path, const std::fs::path &outputPath) const;\n        void extractAll(const std::fs::path &outputPath) const;\n\n        [[nodiscard]] bool isValid() const { return m_valid; }\n\n    private:\n        std::unique_ptr<mtar_t> m_ctx;\n        std::fs::path m_path;\n\n        bool m_valid = false;\n        \n        // These will be updated when the constructor is called\n        int m_tarOpenErrno  = 0;\n        int m_fileOpenErrno = 0;\n    };\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/types.hpp",
    "content": "#pragma once\n\n#include <cstddef>\n#include <cstdint>\n\n#include <concepts>\n#include <type_traits>\n\n#include <wolv/types.hpp>\n\nusing namespace wolv::unsigned_integers;\nusing namespace wolv::signed_integers;\n\nusing color_t = u32;\n\nnamespace hex {\n\n    struct Region {\n        u64 address;\n        u64 size;\n\n        [[nodiscard]] constexpr bool isWithin(const Region &other) const {\n            if (*this == Invalid() || other == Invalid())\n                return false;\n\n            if (this->getStartAddress() >= other.getStartAddress() && this->getEndAddress() <= other.getEndAddress())\n                return true;\n\n            return false;\n        }\n\n        [[nodiscard]] constexpr bool overlaps(const Region &other) const {\n            if (*this == Invalid() || other == Invalid())\n                return false;\n\n            if (this->getEndAddress() >= other.getStartAddress() && this->getStartAddress() <= other.getEndAddress())\n                return true;\n\n            return false;\n        }\n\n        [[nodiscard]] constexpr u64 getStartAddress() const { return this->address; }\n        [[nodiscard]] constexpr u64 getEndAddress() const {\n            if (this->size == 0)\n                return this->address;\n            else\n                return this->address + this->size - 1;\n        }\n        [[nodiscard]] constexpr size_t getSize() const { return this->size; }\n\n        [[nodiscard]] constexpr bool operator==(const Region &other) const {\n            return this->address == other.address && this->size == other.size;\n        }\n\n        constexpr static Region Invalid() {\n            return { 0, 0 };\n        }\n\n        constexpr bool operator<(const Region &other) const {\n            return this->address < other.address;\n        }\n    };\n\n\n    template<typename T>\n    concept Pointer = std::is_pointer_v<T>;\n\n    template<Pointer T>\n    struct NonNull {\n        NonNull(T ptr) : pointer(ptr) { }\n        NonNull(std::nullptr_t) = delete;\n        NonNull(std::integral auto) = delete;\n        NonNull(bool) = delete;\n\n        [[nodiscard]] T get()        const { return pointer; }\n        [[nodiscard]] T operator->() const { return pointer; }\n        [[nodiscard]] std::remove_pointer_t<T> operator*()  const { return *pointer; }\n        [[nodiscard]] operator T()   const { return pointer; }\n\n        T pointer;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/udp_server.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <functional>\n#include <thread>\n#include <atomic>\n#include <span>\n\nnamespace hex {\n\n    class UDPServer {\n    public:\n        using Callback = std::function<void(std::span<const u8> data)>;\n        UDPServer() = default;\n        UDPServer(u16 port, Callback callback);\n        ~UDPServer();\n\n        UDPServer(const UDPServer&) = delete;\n        UDPServer& operator=(const UDPServer&) = delete;\n        UDPServer(UDPServer &&other) noexcept {\n            m_port = other.m_port;\n            m_callback = std::move(other.m_callback);\n            m_thread = std::move(other.m_thread);\n            m_running = other.m_running.load();\n            other.m_running = false;\n            m_socketFd = other.m_socketFd;\n            other.m_socketFd = -1;\n        }\n        UDPServer& operator=(UDPServer &&other) noexcept {\n            if (this != &other) {\n                m_port = other.m_port;\n                m_callback = std::move(other.m_callback);\n                m_thread = std::move(other.m_thread);\n                m_running = other.m_running.load();\n                other.m_running = false;\n                m_socketFd = other.m_socketFd;\n                other.m_socketFd = -1;\n            }\n\n            return *this;\n        }\n\n        void start();\n        void stop();\n\n        [[nodiscard]] u16 getPort() const { return m_port; }\n\n    private:\n        void run();\n\n        u16 m_port = 0;\n        Callback m_callback;\n        std::thread m_thread;\n        std::atomic<bool> m_running;\n        int m_socketFd = -1;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/utils.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <hex/helpers/concepts.hpp>\n#include <hex/helpers/fs.hpp>\n\n#include <array>\n#include <bit>\n#include <cstring>\n#include <cctype>\n#include <concepts>\n#include <functional>\n#include <limits>\n#include <map>\n#include <memory>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <variant>\n#include <vector>\n\n#if defined(OS_MACOS)\n    #include <hex/helpers/utils_macos.hpp>\n#elif defined(OS_LINUX)\n    #include <hex/helpers/utils_linux.hpp>\n#endif\n\n#include <imgui.h>\n\n#include <wolv/utils/charconv.hpp>\n\nnamespace hex {\n\n    #if !defined(HEX_MODULE_EXPORT)\n        namespace prv {\n            class Provider;\n        }\n    #endif\n\n    template<typename T>\n    [[nodiscard]] std::vector<std::vector<T>> sampleChannels(const std::vector<T> &data, size_t count, size_t channels) {\n        if (channels == 0) return {};\n        size_t signalLength = std::max<double>(1.0, double(data.size()) / channels);\n\n        size_t stride = std::max(1.0, double(signalLength) / count);\n\n        std::vector<std::vector<T>> result;\n        result.resize(channels);\n        for (size_t i = 0; i < channels; i++) {\n            result[i].reserve(count);\n        }\n        result.reserve(count);\n\n        for (size_t i = 0; i < data.size(); i += stride) {\n            for (size_t j = 0; j < channels; j++) {\n                result[j].push_back(data[i + j]);\n            }\n        }\n\n        return result;\n    }\n\n    template<typename T>\n    [[nodiscard]] std::vector<T> sampleData(const std::vector<T> &data, size_t count) {\n        size_t stride = std::max(1.0, double(data.size()) / count);\n\n        std::vector<T> result;\n        result.reserve(count);\n\n        for (size_t i = 0; i < data.size(); i += stride) {\n            result.push_back(data[i]);\n        }\n\n        return result;\n    }\n\n    template<typename T>\n    [[nodiscard]] std::vector<T> operator|(const std::vector<T> &lhs, const std::vector<T> &rhs) {\n        std::vector<T> result;\n\n        std::copy(lhs.begin(), lhs.end(), std::back_inserter(result));\n        std::copy(rhs.begin(), rhs.end(), std::back_inserter(result));\n\n        return result;\n    }\n\n    [[nodiscard]] std::string to_string(u128 value);\n    [[nodiscard]] std::string to_string(i128 value);\n\n    [[nodiscard]] std::string toLower(std::string string);\n    [[nodiscard]] std::string toUpper(std::string string);\n\n    [[nodiscard]] std::vector<u8> parseHexString(std::string string);\n    [[nodiscard]] std::optional<u8> parseBinaryString(const std::string &string);\n    [[nodiscard]] std::string toByteString(u64 bytes);\n    [[nodiscard]] std::string makePrintable(u8 c);\n\n    void startProgram(const std::vector<std::string> &command);\n    int executeCommand(const std::string &command);\n    std::optional<std::string> executeCommandWithOutput(const std::string &command);\n    void executeCommandDetach(const std::string &command);\n    void openWebpage(std::string url);\n\n    extern \"C\" void registerFont(const char *fontName, const char *fontPath);\n    const std::map<std::fs::path, std::string>& getFonts();\n\n    [[nodiscard]] std::string encodeByteString(const std::vector<u8> &bytes);\n    [[nodiscard]] std::vector<u8> decodeByteString(const std::string &string);\n\n    [[nodiscard]] std::wstring utf8ToUtf16(const std::string& utf8);\n    [[nodiscard]] std::string utf16ToUtf8(const std::wstring& utf16);\n\n    [[nodiscard]] constexpr u64 extract(u8 from, u8 to, const auto &value) {\n        if (from < to) std::swap(from, to);\n\n        using ValueType = std::remove_cvref_t<decltype(value)>;\n        ValueType mask  = (std::numeric_limits<ValueType>::max() >> (((sizeof(value) * 8) - 1) - (from - to))) << to;\n\n        return u64((value & mask) >> to);\n    }\n\n    [[nodiscard]] inline u64 extract(u32 from, u32 to, const std::vector<u8> &bytes) {\n        u8 index = 0;\n        while (from > 32 && to > 32) {\n            from -= 8;\n            to -= 8;\n            index++;\n        }\n\n        u64 value = 0;\n        std::memcpy(&value, &bytes[index], std::min(sizeof(value), bytes.size() - index));\n        u64 mask = (std::numeric_limits<u64>::max() >> (64 - (from + 1)));\n\n        return (value & mask) >> to;\n    }\n\n    [[nodiscard]] constexpr i128 signExtend(size_t numBits, i128 value) {\n        i128 mask = 1ULL << (numBits - 1);\n        return (value ^ mask) - mask;\n    }\n\n    template<std::integral T>\n    [[nodiscard]] constexpr T swapBitOrder(size_t numBits, T value) {\n        T result = 0x00;\n\n        for (size_t bit = 0; bit < numBits; bit++) {\n            result <<= 1;\n            result |= (value & (1 << bit)) != 0;\n        }\n\n        return result;\n    }\n\n    [[nodiscard]] constexpr size_t strnlen(const char *s, size_t n) {\n        size_t i = 0;\n        while (i < n && s[i] != '\\x00') i++;\n\n        return i;\n    }\n\n    template<size_t>\n    struct SizeTypeImpl { };\n\n    template<>\n    struct SizeTypeImpl<1> { using Type = u8; };\n    template<>\n    struct SizeTypeImpl<2> { using Type = u16; };\n    template<>\n    struct SizeTypeImpl<4> { using Type = u32; };\n    template<>\n    struct SizeTypeImpl<8> { using Type = u64; };\n    template<>\n    struct SizeTypeImpl<16> { using Type = u128; };\n\n    template<size_t Size>\n    using SizeType = typename SizeTypeImpl<Size>::Type;\n\n    template<typename T>\n    [[nodiscard]] constexpr T changeEndianness(const T &value, size_t size, std::endian endian) {\n        if (endian == std::endian::native)\n            return value;\n\n        size = std::min(size, sizeof(T));\n\n        std::array<uint8_t, sizeof(T)> data = { 0 };\n        std::memcpy(&data[0], &value, size);\n\n        for (uint32_t i = 0; i < size / 2; i++) {\n            std::swap(data[i], data[size - 1 - i]);\n        }\n\n        T result = { };\n        std::memcpy(&result, &data[0], size);\n\n        return result;\n    }\n\n    template<typename T>\n    [[nodiscard]] constexpr T changeEndianness(const T &value, std::endian endian) {\n        return changeEndianness(value, sizeof(value), endian);\n    }\n\n    [[nodiscard]] constexpr u128 bitmask(u8 bits) {\n        return u128(-1) >> (128 - bits);\n    }\n\n    template<class T>\n    [[nodiscard]] constexpr T bit_width(T x) noexcept {\n        return std::numeric_limits<T>::digits - std::countl_zero(x);\n    }\n\n    template<typename T>\n    [[nodiscard]] constexpr T bit_ceil(T x) noexcept {\n        if (x <= 1u)\n            return T(1);\n\n        return T(1) << bit_width(T(x - 1));\n    }\n\n    template<std::integral T, std::integral U>\n    [[nodiscard]] auto powi(T base, U exp) {\n        using ResultType = decltype(T{} * U{});\n\n        if (exp < 0)\n            return ResultType(0);\n\n        ResultType result = 1;\n\n        while (exp != 0) {\n            if ((exp & 0b1) == 0b1)\n                result *= base;\n            exp >>= 1;\n            base *= base;\n        }\n        return result;\n    }\n\n    template<typename T, typename... Args>\n    void moveToVector(std::vector<T> &buffer, T &&first, Args &&...rest) {\n        buffer.push_back(std::move(first));\n\n        if constexpr (sizeof...(rest) > 0)\n            moveToVector(buffer, std::move(rest)...);\n    }\n\n    template<typename T, typename... Args>\n    [[nodiscard]] std::vector<T> moveToVector(T &&first, Args &&...rest) {\n        std::vector<T> result;\n        moveToVector(result, T(std::move(first)), std::move(rest)...);\n\n        return result;\n    }\n\n    [[nodiscard]] std::string toEngineeringString(double value);\n\n    [[nodiscard]] inline std::vector<u8> parseByteString(const std::string &string) {\n        auto byteString = std::string(string);\n        std::erase(byteString, ' ');\n\n        if ((byteString.length() % 2) != 0) return {};\n\n        std::vector<u8> result;\n        for (u32 i = 0; i < byteString.length(); i += 2) {\n            if (!std::isxdigit(byteString[i]) || !std::isxdigit(byteString[i + 1]))\n                return {};\n\n            auto value = wolv::util::from_chars<u64>(byteString.substr(i, 2), 16);\n            if (!value.has_value())\n                return {};\n\n            result.push_back(*value);\n        }\n\n        return result;\n    }\n\n    [[nodiscard]] std::string toBinaryString(std::unsigned_integral auto number) {\n        if (number == 0) return \"0\";\n\n        std::string result;\n        for (i16 bit = hex::bit_width(number) - 1; bit >= 0; bit -= 1)\n            result += (number & (0b1LLU << bit)) == 0 ? '0' : '1';\n\n        return result;\n    }\n\n    template<u8 ExponentBits, u8 MantissaBits>\n    [[nodiscard]] constexpr float customFloatToFloat32(u32 value) {\n        static_assert(ExponentBits <= 8, \"ExponentBits must be less than 8\");\n        static_assert(ExponentBits + MantissaBits + 1 <= 32, \"Format doesn't fit into a 32-bit float\");\n\n        const u32 sign = value >> (ExponentBits + MantissaBits);\n        const u32 exponent = (value >> MantissaBits) & ((1u << ExponentBits) - 1);\n        u32 mantissa = value & ((1u << MantissaBits) - 1);\n\n        // Calculate the bias for the input format and IEEE-754 float32\n        i32 inputBias = (1 << (ExponentBits - 1)) - 1;\n        i32 float32Bias = 127;\n\n        u32 result = 0;\n\n        if (exponent == 0) {\n            if (mantissa == 0) {\n                // Zero\n                result = sign << 31;\n            } else {\n                // Subnormal\n                int shift = 0;\n                while ((mantissa & (1u << MantissaBits)) == 0) {\n                    mantissa <<= 1;\n                    shift++;\n                }\n                mantissa &= ((1u << MantissaBits) - 1); // clear implicit bit\n                int adjustedExp = float32Bias - inputBias - shift + 1;\n                result = (sign << 31) | (adjustedExp << 23) | (mantissa << (23 - MantissaBits));\n            }\n        } else if (exponent == ((1u << ExponentBits) - 1)) {\n            // Inf or NaN\n            result = (sign << 31) | (0xFF << 23) | (mantissa << (23 - MantissaBits));\n        } else {\n            // Normalized number\n            int adjustedExp = exponent - inputBias + float32Bias;\n            result = (sign << 31) | (adjustedExp << 23) | (mantissa << (23 - MantissaBits));\n        }\n\n        float floatResult;\n        std::memcpy(&floatResult, &result, sizeof(float));\n\n        return floatResult;\n    }\n\n    [[nodiscard]] constexpr float float16ToFloat32(u16 float16) {\n        return customFloatToFloat32<5, 10>(float16);\n    }\n\n    [[nodiscard]] inline bool equalsIgnoreCase(std::string_view left, std::string_view right) {\n        return std::equal(left.begin(), left.end(), right.begin(), right.end(), [](char a, char b) {\n            return tolower(a) == tolower(b);\n        });\n    }\n\n    [[nodiscard]] inline bool containsIgnoreCase(std::string_view a, std::string_view b) {\n        auto iter = std::search(a.begin(), a.end(), b.begin(), b.end(), [](char ch1, char ch2) {\n            return std::toupper(ch1) == std::toupper(ch2);\n        });\n\n        return iter != a.end();\n    }\n\n    template<typename T, typename... VariantTypes>\n    [[nodiscard]] T get_or(const std::variant<VariantTypes...> &variant, T alt) {\n        const T *value = std::get_if<T>(&variant);\n        if (value == nullptr)\n            return alt;\n        else\n            return *value;\n    }\n\n    template<std::integral T>\n    [[nodiscard]] T alignTo(T value, T alignment) {\n        T remainder = value % alignment;\n\n        return remainder != 0 ? value + (alignment - remainder) : value;\n    }\n\n    [[nodiscard]] std::optional<u8> hexCharToValue(char c);\n\n    [[nodiscard]] bool isProcessElevated();\n\n    [[nodiscard]] std::optional<std::string> getEnvironmentVariable(const std::string &env);\n\n    [[nodiscard]] std::string limitStringLength(const std::string &string, size_t maxLength, bool fromBothEnds = true);\n\n    [[nodiscard]] std::optional<std::fs::path> getInitialFilePath();\n\n    [[nodiscard]] std::string generateHexView(u64 offset, u64 size, prv::Provider *provider);\n    [[nodiscard]] std::string generateHexView(u64 offset, const std::vector<u8> &data);\n\n    [[nodiscard]] std::string formatSystemError(i32 error);\n\n    /**\n     * Gets the shared library handle for a given pointer\n     * @param symbol Pointer to any function or variable in the shared library\n     * @return The module handle\n     * @warning Important! Calling this function on functions defined in other modules will return the handle of the current module!\n     *          This is because you're not actually passing a pointer to the function in the other module but rather a pointer to a thunk\n     *          that is defined in the current module.\n     */\n    [[nodiscard]] void* getContainingModule(void* symbol);\n\n    [[nodiscard]] std::optional<ImColor> blendColors(const std::optional<ImColor> &a, const std::optional<ImColor> &b);\n    std::optional<std::chrono::system_clock::time_point> parseTime(std::string_view format, const std::string &timeString);\n\n    std::optional<std::string> getOSLanguage();\n\n    void showErrorMessageBox(const std::string &message);\n    void showToastMessage(const std::string &title, const std::string &message);\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/utils_linux.hpp",
    "content": "#pragma once\n\n#if defined(OS_LINUX)\n\nnamespace hex {\n    void executeCmd(const std::vector<std::string> &argsVector);\n}\n\n#endif\n"
  },
  {
    "path": "lib/libimhex/include/hex/helpers/utils_macos.hpp",
    "content": "#pragma once\n\n#include <hex/helpers/keys.hpp>\n\n#if defined(OS_MACOS)\n\n    #if !defined(HEX_MODULE_EXPORT)\n        struct GLFWwindow;\n    #endif\n\n    extern \"C\" {\n\n        void errorMessageMacos(const char *message);\n        void openWebpageMacos(const char *url);\n        bool isMacosSystemDarkModeEnabled();\n        bool isMacosFullScreenModeEnabled(GLFWwindow *window);\n        float getBackingScaleFactor();\n\n        void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode);\n\n        void enumerateFontsMacos();\n    \n        void macosHandleTitlebarDoubleClickGesture(GLFWwindow *window);\n        void macosSetWindowMovable(GLFWwindow *window, bool movable);\n        bool macosIsWindowBeingResizedByUser(GLFWwindow *window);\n        void macosMarkContentEdited(GLFWwindow *window, bool edited = true);\n\n        void macosGetKey(Keys key, int *output);\n\n        bool macosIsMainInstance();\n        void macosSendMessageToMainInstance(const unsigned char *data, size_t size);\n        void macosInstallEventListener();\n\n        void toastMessageMacos(const char *title, const char *message);\n        void macosSetupDockMenu(void);\n    }\n\n#endif\n"
  },
  {
    "path": "lib/libimhex/include/hex/mcp/client.hpp",
    "content": "#pragma once\n\n#include <iostream>\n\nnamespace hex::mcp {\n\n    class Client {\n    public:\n        Client() = default;\n        ~Client() = default;\n\n        int run(std::istream &input, std::ostream &output);\n    };\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/mcp/server.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <functional>\n#include <nlohmann/json.hpp>\n#include <wolv/net/socket_server.hpp>\n\nnamespace hex::mcp {\n\n    class JsonRpc {\n    public:\n        explicit JsonRpc(std::string request) : m_request(std::move(request)){ }\n\n        struct MethodNotFoundException : std::exception {};\n        struct InvalidParametersException : std::exception {};\n\n        enum class ErrorCode: i16 {\n            ParseError     = -32700,\n            InvalidRequest = -32600,\n            MethodNotFound = -32601,\n            InvalidParams  = -32602,\n            InternalError  = -32603,\n        };\n\n        using Callback = std::function<nlohmann::json(const std::string &method, const nlohmann::json &params)>;\n        std::optional<std::string> execute(const Callback &callback);\n        void setError(ErrorCode code, std::string message);\n\n    private:\n        std::optional<nlohmann::json> handleMessage(const nlohmann::json &request, const Callback &callback);\n        std::optional<nlohmann::json> handleBatchedMessages(const nlohmann::json &request, const Callback &callback);\n\n        nlohmann::json createDefaultMessage();\n        nlohmann::json createErrorMessage(ErrorCode code, const std::string &message);\n        nlohmann::json createResponseMessage(const nlohmann::json &result);\n\n    private:\n        std::string m_request;\n        std::optional<int> m_id;\n\n        struct Error {\n            ErrorCode code;\n            std::string message;\n        };\n        std::optional<Error> m_error;\n    };\n\n    struct TextContent {\n        std::string text;\n\n        operator nlohmann::json() const {\n            nlohmann::json result;\n            result[\"content\"] = nlohmann::json::array({\n                nlohmann::json::object({\n                    { \"type\", \"text\" },\n                    { \"text\", text }\n                })\n            });\n\n            return result;\n        }\n    };\n\n    struct StructuredContent {\n        std::string text;\n        nlohmann::json data;\n\n        operator nlohmann::json() const {\n            nlohmann::json result;\n            result[\"content\"] = nlohmann::json::array({\n                nlohmann::json::object({\n                    { \"type\", \"text\" },\n                    { \"text\", text }\n                })\n            });\n            result[\"structuredContent\"] = data;\n\n            return result;\n        }\n    };\n\n    class Server {\n    public:\n        constexpr static auto McpInternalPort = 19743;\n\n        Server();\n        ~Server();\n\n        void listen();\n        void shutdown();\n        void disconnect();\n        bool isConnected();\n\n        void addPrimitive(std::string type, std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json &params)> function);\n\n        struct ClientInfo {\n            std::string name;\n            std::string version;\n            std::string protocolVersion;\n        };\n\n        const ClientInfo& getClientInfo() const {\n            return m_clientInfo;\n        }\n\n    private:\n        nlohmann::json handleInitialize(const nlohmann::json &params);\n        void handleNotifications(const std::string &method, const nlohmann::json &params);\n\n        struct Primitive {\n            nlohmann::json capabilities;\n            std::function<nlohmann::json(const nlohmann::json &params)> function;\n        };\n\n        std::map<std::string, std::map<std::string, Primitive>> m_primitives;\n\n        wolv::net::SocketServer m_server;\n        bool m_connected = false;\n        ClientInfo m_clientInfo;\n    };\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/plugin.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <hex/api/plugin_manager.hpp>\n#include <hex/helpers/logger.hpp>\n\n#include <string>\n\n#include <imgui.h>\n#include <imgui_internal.h>\n\n#include <wolv/utils/string.hpp>\n#include <wolv/utils/preproc.hpp>\n#include <wolv/utils/guards.hpp>\n\n#if defined(_MSC_VER)\n    #include <windows.h>\n    #define PLUGIN_ENTRY_POINT extern \"C\" BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; }\n#else\n    #define PLUGIN_ENTRY_POINT\n#endif\n\nnamespace {\n    struct PluginFunctionHelperInstantiation {};\n}\n\ntemplate<typename T>\nstruct PluginFeatureFunctionHelper {\n    static void* getFeatures();\n};\n\ntemplate<typename T>\nstruct PluginSubCommandsFunctionHelper {\n    static void* getSubCommands();\n};\n\ntemplate<typename T>\nvoid* PluginFeatureFunctionHelper<T>::getFeatures() {\n    return nullptr;\n}\n\ntemplate<typename T>\nvoid* PluginSubCommandsFunctionHelper<T>::getSubCommands() {\n    return nullptr;\n}\n\n[[maybe_unused]] static auto& getFeaturesImpl() {\n    static hex::AutoReset<std::vector<hex::Feature>> features;\n    return *features;\n}\n\n#if defined (IMHEX_STATIC_LINK_PLUGINS)\n    #define IMHEX_PLUGIN_VISIBILITY_PREFIX static\n#else\n    #if defined(_MSC_VER)\n        #define IMHEX_PLUGIN_VISIBILITY_PREFIX extern \"C\" __declspec(dllexport)\n    #else\n        #define IMHEX_PLUGIN_VISIBILITY_PREFIX extern \"C\" [[gnu::visibility(\"default\")]]\n    #endif\n#endif\n\n#define IMHEX_FEATURE_ENABLED(feature) WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(IMHEX_PLUGIN_, IMHEX_PLUGIN_NAME), _FEATURE_), feature)\n#define IMHEX_DEFINE_PLUGIN_FEATURES() IMHEX_DEFINE_PLUGIN_FEATURES_IMPL()\n#define IMHEX_DEFINE_PLUGIN_FEATURES_IMPL()                                                 \\\n    template<>                                                                              \\\n    struct PluginFeatureFunctionHelper<PluginFunctionHelperInstantiation> {                 \\\n        static void* getFeatures();                                                         \\\n    };                                                                                      \\\n    void* PluginFeatureFunctionHelper<PluginFunctionHelperInstantiation>::getFeatures() {   \\\n        return &getFeaturesImpl();                                                          \\\n    }                                                                                       \\\n    static auto initFeatures = [] { getFeaturesImpl() = std::vector<hex::Feature>({ IMHEX_PLUGIN_FEATURES_CONTENT }); return 0; }()\n\n#define IMHEX_PLUGIN_FEATURES ::getFeaturesImpl()\n\n/**\n * This macro is used to define all the required entry points for a plugin.\n * Name, Author and Description will be displayed in the plugin list on the Welcome screen.\n */\n#define IMHEX_PLUGIN_SETUP(name, author, description) \\\n    IMHEX_PLUGIN_SETUP_IMPL(name, author, description, nullptr)\n#define IMHEX_LIBRARY_SETUP(name) \\\n    IMHEX_LIBRARY_SETUP_IMPL(name)\n\n#define IMHEX_PLUGIN_SETUP_BUILTIN(name, author, description)               \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX bool isBuiltinPlugin() { return true; }  \\\n    IMHEX_PLUGIN_SETUP_IMPL(name, author, description, isBuiltinPlugin)\n\n#define IMHEX_LIBRARY_SETUP_IMPL(name)                                                                                          \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME)();                             \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX const char *WOLV_TOKEN_CONCAT(getLibraryName_, IMHEX_PLUGIN_NAME)() { return name; }         \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(setImGuiContext_, IMHEX_PLUGIN_NAME)(ImGuiContext *ctx) {             \\\n        ImGui::SetCurrentContext(ctx);                                                                                          \\\n        GImGui = ctx;                                                                                                           \\\n    }                                                                                                                           \\\n    extern \"C\" void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() {                                                  \\\n        hex::PluginManager::addPlugin(name, hex::PluginFunctions {                                                              \\\n            nullptr,                                                                                                            \\\n            WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME),                                                           \\\n            nullptr,                                                                                                            \\\n            WOLV_TOKEN_CONCAT(getLibraryName_, IMHEX_PLUGIN_NAME),                                                              \\\n            nullptr,                                                                                                            \\\n            nullptr,                                                                                                            \\\n            nullptr,                                                                                                            \\\n            WOLV_TOKEN_CONCAT(setImGuiContext_, IMHEX_PLUGIN_NAME),                                                             \\\n            nullptr,                                                                                                            \\\n            nullptr,                                                                                                            \\\n            nullptr                                                                                                             \\\n        });                                                                                                                     \\\n    }                                                                                                                           \\\n    PLUGIN_ENTRY_POINT                                                                                                          \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME)()\n\n#define IMHEX_PLUGIN_SETUP_IMPL(name, author, description, builtinPluginFunc)                                                                      \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginName() { return name; }                                                 \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginAuthor() { return author; }                                             \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginDescription() { return description; }                                   \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getCompatibleVersion() { return IMHEX_VERSION; }                                 \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *ctx) {                                                    \\\n        ImGui::SetCurrentContext(ctx);                                                                                          \\\n        GImGui = ctx;                                                                                                           \\\n    }                                                                                                                           \\\n    IMHEX_DEFINE_PLUGIN_FEATURES();                                                                                             \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX void* getFeatures() {                                                                        \\\n        return PluginFeatureFunctionHelper<PluginFunctionHelperInstantiation>::getFeatures();                                   \\\n    }                                                                                                                           \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX void* getSubCommands() {                                                                     \\\n        return PluginSubCommandsFunctionHelper<PluginFunctionHelperInstantiation>::getSubCommands();                            \\\n    }                                                                                                                           \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin();                                                                     \\\n    extern \"C\" void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() {                                                  \\\n        hex::PluginManager::addPlugin(name, hex::PluginFunctions {                                                              \\\n            initializePlugin,                                                                                                   \\\n            nullptr,                                                                                                            \\\n            getPluginName,                                                                                                      \\\n            nullptr,                                                                                                            \\\n            getPluginAuthor,                                                                                                    \\\n            getPluginDescription,                                                                                               \\\n            getCompatibleVersion,                                                                                               \\\n            setImGuiContext,                                                                                                    \\\n            nullptr,                                                                                                            \\\n            getSubCommands,                                                                                                     \\\n            getFeatures,                                                                                                        \\\n            builtinPluginFunc                                                                                                   \\\n        });                                                                                                                     \\\n    }                                                                                                                           \\\n    PLUGIN_ENTRY_POINT                                                                                                          \\\n    IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin()\n\n/**\n * This macro is used to define subcommands defined by the plugin\n * A subcommand consists of a key, a description, and a callback\n * The key is what the first argument to ImHex should be, prefixed by `--`\n * For example, if the key if `help`, ImHex should be started with `--help` as its first argument to trigger the subcommand\n * when the subcommand is triggerred, it's callback will be executed. The callback is executed BEFORE most of ImHex initialization\n * so to do anything meaningful, you should subscribe to an event (like EventImHexStartupFinished) and run your code there.\n */\n#define IMHEX_PLUGIN_SUBCOMMANDS() IMHEX_PLUGIN_SUBCOMMANDS_IMPL()\n\n#define IMHEX_PLUGIN_SUBCOMMANDS_IMPL()                                                             \\\n    extern std::vector<hex::SubCommand> g_subCommands;                                              \\\n    template<>                                                                                      \\\n    struct PluginSubCommandsFunctionHelper<PluginFunctionHelperInstantiation> {                     \\\n        static void* getSubCommands();                                                              \\\n    };                                                                                              \\\n    void* PluginSubCommandsFunctionHelper<PluginFunctionHelperInstantiation>::getSubCommands() {    \\\n        return &g_subCommands;                                                                      \\\n    }                                                                                               \\\n    std::vector<hex::SubCommand> g_subCommands\n"
  },
  {
    "path": "lib/libimhex/include/hex/providers/buffered_reader.hpp",
    "content": "#pragma once\n\n#include <hex/providers/provider.hpp>\n#include <hex/helpers/literals.hpp>\n\n#include <wolv/io/buffered_reader.hpp>\n\nnamespace hex::prv {\n\n    using namespace hex::literals;\n\n    inline void providerReaderFunction(Provider *provider, void *buffer, u64 address, size_t size) {\n        provider->read(address, buffer, size);\n    }\n\n    class ProviderReader : public wolv::io::BufferedReader<prv::Provider, providerReaderFunction> {\n    public:\n        using BufferedReader::BufferedReader;\n\n        explicit ProviderReader(Provider *provider, size_t bufferSize = 0x100000) : BufferedReader(provider, provider->getActualSize(), bufferSize) {\n            this->setEndAddress(provider->getBaseAddress() + provider->getActualSize() - 1);\n            this->seek(provider->getBaseAddress());\n        }\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/providers/cached_provider.hpp",
    "content": "#pragma once\n\n#include <hex/providers/provider.hpp>\n\n#include <unordered_map>\n#include <vector>\n#include <mutex>\n#include <shared_mutex>\n#include <cstddef>\n#include <cstdint>\n\nnamespace hex::prv {\n\n    /**\n     * @brief A base class for providers that want to cache data in memory.\n     *        Thread-safe for concurrent reads/writes. Reads are cached in memory.\n     *        Subclasses must implement readFromSource and writeToSource.\n     */\n    class CachedProvider : public Provider {\n    public:\n        CachedProvider(size_t cacheBlockSize = 4096, size_t maxBlocks = 1024);\n        ~CachedProvider() override;\n\n        OpenResult open() override;\n        void close() override;\n\n        void readRaw(u64 offset, void *buffer, size_t size) override;\n        void writeRaw(u64 offset, const void *buffer, size_t size) override;\n        void resizeRaw(u64 newSize) override;\n\n        u64 getActualSize() const override;\n\n    protected:\n        virtual void readFromSource(uint64_t offset, void* buffer, size_t size) = 0;\n        virtual void writeToSource(uint64_t offset, const void* buffer, size_t size) = 0;\n        virtual void resizeSource(uint64_t newSize) { std::ignore = newSize; }\n        virtual u64 getSourceSize() const = 0;\n\n        void clearCache();\n\n        struct Block {\n            uint64_t index;\n            std::vector<uint8_t> data;\n            bool dirty = false;\n        };\n\n        size_t m_cacheBlockSize;\n        size_t m_maxBlocks;\n        mutable std::shared_mutex m_cacheMutex;\n        std::vector<std::optional<Block>> m_cache;\n        mutable u64 m_cachedSize = 0;\n\n        constexpr u64 calcBlockIndex(u64 offset) const { return offset / m_cacheBlockSize; }\n        constexpr size_t calcBlockOffset(u64 offset) const { return offset % m_cacheBlockSize; }\n\n        void evictIfNeeded();\n    };\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/providers/memory_provider.hpp",
    "content": "#pragma once\n\n#include <hex/providers/provider.hpp>\n\nnamespace hex::prv {\n\n    /**\n     * This is a simple mock provider that can be used to pass in-memory data to APIs that require a provider.\n     * It's NOT a provider that can be loaded by the user.\n     */\n    class MemoryProvider : public hex::prv::Provider {\n    public:\n        MemoryProvider() = default;\n        explicit MemoryProvider(std::vector<u8> data, std::string name = \"\") : m_data(std::move(data)), m_name(std::move(name)) { }\n        ~MemoryProvider() override = default;\n\n        MemoryProvider(const MemoryProvider&) = delete;\n        MemoryProvider& operator=(const MemoryProvider&) = delete;\n\n        MemoryProvider(MemoryProvider &&provider) noexcept = default;\n        MemoryProvider& operator=(MemoryProvider &&provider) noexcept = default;\n\n        [[nodiscard]] bool isAvailable()        const override { return true;           }\n        [[nodiscard]] bool isReadable()         const override { return true;           }\n        [[nodiscard]] bool isWritable()         const override { return true;           }\n        [[nodiscard]] bool isResizable()        const override { return true;           }\n        [[nodiscard]] bool isSavable()          const override { return m_name.empty(); }\n        [[nodiscard]] bool isSavableAsRecent()  const override { return false;          }\n\n        [[nodiscard]] OpenResult open() override;\n        void close() override { }\n\n        void readRaw(u64 offset, void *buffer, size_t size) override;\n        void writeRaw(u64 offset, const void *buffer, size_t size) override;\n        [[nodiscard]] u64 getActualSize() const override { return m_data.size(); }\n\n        void resizeRaw(u64 newSize) override;\n\n        [[nodiscard]] std::string getName() const override { return m_name; }\n\n        [[nodiscard]] UnlocalizedString getTypeName() const override { return \"MemoryProvider\"; }\n\n        [[nodiscard]] const char* getIcon() const override {\n            return \"\";\n        }\n    private:\n        void renameFile();\n\n    private:\n        std::vector<u8> m_data;\n        std::string m_name;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/providers/overlay.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <vector>\n\nnamespace hex::prv {\n\n    class Overlay {\n    public:\n        Overlay() = default;\n\n        void setAddress(u64 address) { m_address = address; }\n        [[nodiscard]] u64 getAddress() const { return m_address; }\n\n        [[nodiscard]] u64 getSize() const { return m_data.size(); }\n        [[nodiscard]] std::vector<u8> &getData() { return m_data; }\n\n    private:\n        u64 m_address = 0;\n        std::vector<u8> m_data;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/providers/provider.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <list>\n#include <optional>\n#include <string>\n#include <variant>\n#include <vector>\n\n#include <hex/providers/overlay.hpp>\n#include <hex/helpers/fs.hpp>\n\n#include <nlohmann/json_fwd.hpp>\n\n#include <hex/providers/undo_redo/stack.hpp>\n\nnamespace hex::prv {\n    /**\n     * @brief Interface for providers that need to draw a config interface when being created\n     */\n    class IProviderLoadInterface {\n    public:\n        virtual ~IProviderLoadInterface() = default;\n        virtual bool drawLoadInterface() = 0;\n    };\n\n    /**\n     * @brief Interface for providers that want to provide a custom sidebar interface\n     */\n    class IProviderSidebarInterface {\n    public:\n        virtual ~IProviderSidebarInterface() = default;\n        virtual void drawSidebarInterface() = 0;\n    };\n\n    /**\n     * @brief Interface for providers that need to show a file picker dialog when being created\n     */\n    class IProviderFilePicker {\n    public:\n        virtual ~IProviderFilePicker() = default;\n        virtual bool handleFilePicker() = 0;\n    };\n\n    /**\n     * @brief Interface for providers that want to display custom menu items in the provider context menu\n     */\n    class IProviderMenuItems {\n    public:\n        struct MenuEntry {\n            std::string name;\n            const char *icon;\n            std::function<void()> callback;\n        };\n\n        virtual ~IProviderMenuItems() = default;\n        virtual std::vector<MenuEntry> getMenuEntries() = 0;\n    };\n\n    /**\n     * @brief Interface for providers that want to show some extra information in the information view\n     */\n    class IProviderDataDescription {\n    public:\n        struct Description {\n            std::string name;\n            std::string value;\n        };\n\n        virtual ~IProviderDataDescription() = default;\n        [[nodiscard]] virtual std::vector<Description> getDataDescription() const = 0;\n    };\n\n    class IProviderDataBackupable {\n    public:\n        explicit IProviderDataBackupable(Provider *provider);\n        virtual ~IProviderDataBackupable() = default;\n\n        void createBackupIfNeeded(const std::fs::path &inputFilePath);\n    private:\n        Provider *m_provider = nullptr;\n        bool m_backupCreated = false;\n\n        bool m_shouldCreateBackups = true;\n        u64 m_maxSize;\n        std::string m_backupExtension;\n    };\n\n    /**\n     * @brief Represent the data source for a tab in the UI\n     */\n    class Provider {\n    public:\n        constexpr static u64 MaxPageSize = 0xFFFF'FFFF'FFFF'FFFF;\n\n        class OpenResult {\n        public:\n            OpenResult() : m_result(std::monostate{}) {}\n\n            [[nodiscard]] static OpenResult failure(std::string errorMessage) {\n                OpenResult result;\n                result.m_result = std::move(errorMessage);\n                return result;\n            }\n\n            [[nodiscard]] static OpenResult warning(std::string warningMessage) {\n                OpenResult result;\n                result.m_result = std::move(warningMessage);\n                result.m_warning = true;\n                return result;\n            }\n\n            [[nodiscard]] static OpenResult redirect(Provider *provider) {\n                OpenResult result;\n                result.m_result = provider;\n                return result;\n            }\n\n            [[nodiscard]] bool isSuccess() const {\n                return std::holds_alternative<std::monostate>(m_result);\n            }\n\n            [[nodiscard]] bool isFailure() const {\n                return std::holds_alternative<std::string>(m_result) && !m_warning;\n            }\n\n            [[nodiscard]] bool isWarning() const {\n                return std::holds_alternative<std::string>(m_result) && m_warning;\n            }\n\n            [[nodiscard]] bool isRedirecting() const {\n                return std::holds_alternative<Provider*>(m_result);\n            }\n\n            [[nodiscard]] Provider* getRedirectProvider() const {\n                if (std::holds_alternative<Provider*>(m_result)) {\n                    return std::get<Provider*>(m_result);\n                }\n                return nullptr;\n            }\n\n            [[nodiscard]] std::string_view getErrorMessage() const {\n                if (std::holds_alternative<std::string>(m_result)) {\n                    return std::get<std::string>(m_result);\n                }\n\n                return \"\";\n            }\n\n        private:\n            std::variant<std::monostate, std::string, Provider*> m_result;\n            bool m_warning = false;\n        };\n\n        Provider();\n        virtual ~Provider();\n        Provider(const Provider&) = delete;\n        Provider& operator=(const Provider&) = delete;\n\n        Provider(Provider &&provider) noexcept = default;\n        Provider& operator=(Provider &&provider) noexcept = default;\n\n        /**\n         * @brief Opens this provider\n         * @note The return value of this function allows to ensure the provider is available,\n         * so calling Provider::isAvailable() just after a call to open() that returned true is redundant.\n         * @note This is not related to the EventProviderOpened event\n         * @return true if the provider was opened successfully, else false\n         */\n        [[nodiscard]] virtual OpenResult open() = 0;\n\n        /**\n         * @brief Closes this provider\n         * @note This function is called when the user requests for a provider to be closed, e.g. by closing a tab.\n         * In general, this function should close the underlying data source but leave the provider in a state where\n         * it can be opened again later by calling the open() function again.\n         */\n        virtual void close() = 0;\n\n        /**\n         * @brief Checks if this provider is open and can be used to access data\n         * @return Generally, if the open() function succeeded and the data source was successfully opened, this\n         * function should return true\n         */\n        [[nodiscard]] virtual bool isAvailable() const = 0;\n\n        /**\n         * @brief Checks if the data in this provider can be read\n         * @return True if the provider is readable, false otherwise\n         */\n        [[nodiscard]] virtual bool isReadable() const  = 0;\n\n        /**\n         * @brief Controls if the user can write data to this specific provider.\n         *   This may be false for e.g. a file opened in read-only\n         */\n        [[nodiscard]] virtual bool isWritable() const  = 0;\n\n        /**\n         * @brief Controls if the user can resize this provider\n         * @return True if the provider is resizable, false otherwise\n         */\n        [[nodiscard]] virtual bool isResizable() const = 0;\n\n        /**\n         * @brief Controls whether the provider can be saved (\"saved\", not \"saved as\")\n         *   This is mainly used by providers that aren't buffered, and so don't need to be saved\n         *   This function will usually return false for providers that aren't writable, but this isn't guaranted\n         */\n        [[nodiscard]] virtual bool isSavable() const = 0;\n\n        /**\n         * @brief Controls whether we can dump data from this provider (e.g. \"save as\", or \"export -> ..\").\n         *   Typically disabled for process with sparse data, like the Process memory provider\n         *   where the virtual address space is several TiBs large.\n         *   Default implementation returns true.\n         */\n        [[nodiscard]] virtual bool isDumpable() const;\n\n        /**\n         * @brief Controls whether this provider can be saved as a recent entry\n         *   Typically used for providers that do not retain data, e.g. the memory provider\n         */\n        [[nodiscard]] virtual bool isSavableAsRecent() const { return true; }\n\n        /**\n         * @brief Read data from this provider, applying overlays and patches\n         * @param offset offset to start reading the data\n         * @param buffer buffer to write read data\n         * @param size number of bytes to read\n         * @param overlays apply overlays and patches is true. Same as readRaw() if false\n         */\n        virtual void read(u64 offset, void *buffer, size_t size, bool overlays = true);\n        \n        /**\n         * @brief Write data to the patches of this provider. Will not directly modify provider.\n         * @param offset offset to start writing the data\n         * @param buffer buffer to take data to write from\n         * @param size number of bytes to write\n         */\n        virtual void write(u64 offset, const void *buffer, size_t size);\n\n        /**\n         * @brief Read data from this provider, without applying overlays and patches\n         * @param offset offset to start reading the data\n         * @param buffer buffer to write read data\n         * @param size number of bytes to read\n         */\n        virtual void readRaw(u64 offset, void *buffer, size_t size) = 0;\n        /**\n         * @brief Write data directly to this provider\n         * @param offset offset to start writing the data\n         * @param buffer buffer to take data to write from\n         * @param size number of bytes to write\n         */\n        virtual void writeRaw(u64 offset, const void *buffer, size_t size) = 0;\n\n        /**\n         * @brief Get the full size of the data in this provider\n         * @return The size of the entire available data of this provider\n         */\n        [[nodiscard]] virtual u64 getActualSize() const = 0;\n\n        /**\n         * @brief Gets the type name of this provider\n         * @note This is mainly used to be stored in project files and recents to be able to later on\n         * recreate this exact provider type. This needs to be unique across all providers, this is usually something\n         * like \"hex.builtin.provider.mem_file\" or \"hex.builtin.provider.file\"\n         * @return The provider's type name\n         */\n        [[nodiscard]] virtual UnlocalizedString getTypeName() const = 0;\n\n        /**\n         * @brief Gets a human-readable representation of the current provider\n         * @note This is mainly used to display the provider in the UI. For example, the file provider\n         * will return the file name here\n         * @return The name of the current provider\n         */\n        [[nodiscard]] virtual std::string getName() const = 0;\n\n        /**\n         * @brief Gets the icon of this provider\n         * @return The icon string\n         */\n        [[nodiscard]] virtual const char* getIcon() const = 0;\n\n        bool resize(u64 newSize);\n        void insert(u64 offset, u64 size);\n        void remove(u64 offset, u64 size);\n\n        virtual void resizeRaw(u64 newSize) { std::ignore = newSize; }\n        virtual void insertRaw(u64 offset, u64 size);\n        virtual void removeRaw(u64 offset, u64 size);\n\n        virtual void save();\n        virtual void saveAs(const std::fs::path &path);\n\n        [[nodiscard]] Overlay *newOverlay();\n        void deleteOverlay(Overlay *overlay);\n        void applyOverlays(u64 offset, void *buffer, size_t size) const;\n        [[nodiscard]] const std::list<std::unique_ptr<Overlay>> &getOverlays() const;\n\n        [[nodiscard]] u64 getPageSize() const;\n        void setPageSize(u64 pageSize);\n\n        [[nodiscard]] u32 getPageCount() const;\n        [[nodiscard]] u32 getCurrentPage() const;\n        void setCurrentPage(u32 page);\n\n        virtual void setBaseAddress(u64 address);\n        [[nodiscard]] virtual u64 getBaseAddress() const;\n        [[nodiscard]] virtual u64 getCurrentPageAddress() const;\n        [[nodiscard]] virtual u64 getSize() const;\n        [[nodiscard]] virtual std::optional<u32> getPageOfAddress(u64 address) const;\n\n        [[nodiscard]] virtual std::variant<std::string, i128> queryInformation(const std::string &category, const std::string &argument);\n\n        virtual void undo();\n        virtual void redo();\n\n        [[nodiscard]] virtual bool canUndo() const;\n        [[nodiscard]] virtual bool canRedo() const;\n\n        [[nodiscard]] u32 getID() const;\n        void setID(u32 id);\n\n        [[nodiscard]] virtual nlohmann::json storeSettings(nlohmann::json settings) const;\n        virtual void loadSettings(const nlohmann::json &settings);\n\n        void markDirty(bool dirty = true) { m_dirty = dirty; }\n        [[nodiscard]] bool isDirty() const { return m_dirty; }\n\n        [[nodiscard]] virtual std::pair<Region, bool> getRegionValidity(u64 address) const;\n\n        void skipLoadInterface() { m_skipLoadInterface = true; }\n        [[nodiscard]] bool shouldSkipLoadInterface() const { return m_skipLoadInterface; }\n\n        template<std::derived_from<undo::Operation> T>\n        bool addUndoableOperation(auto && ... args) {\n            return m_undoRedoStack.add<T>(std::forward<decltype(args)...>(args)...);\n        }\n\n        [[nodiscard]] virtual undo::Stack& getUndoStack() { return m_undoRedoStack; }\n\n    protected:\n        u32 m_currPage    = 0;\n        u64 m_baseAddress = 0;\n\n        undo::Stack m_undoRedoStack;\n\n        std::list<std::unique_ptr<Overlay>> m_overlays;\n\n        u32 m_id;\n\n        /**\n         * @brief true if there is any data that needs to be saved\n         */\n        bool m_dirty = false;\n\n        /**\n         * @brief Control if provider initialization should be skipped.\n         * Initialization may be asking the user for information related to the provider,\n         * e.g. a process ID for the process memory provider\n         * this is used mainly when restoring a provider with already known initialization information\n         * for example when loading a project or loading a provider from the \"recent\" lsit\n         */\n        bool m_skipLoadInterface = false;\n\n        u64 m_pageSize = MaxPageSize;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/providers/provider_data.hpp",
    "content": "#pragma once\n\n#include <hex/api/imhex_api/provider.hpp>\n#include <hex/api/events/events_provider.hpp>\n#include <hex/api/events/events_lifecycle.hpp>\n#include <hex/api/events/requests_provider.hpp>\n\n\n#include <map>\n#include <ranges>\n#include <utility>\n\nnamespace hex {\n\n    #if !defined(HEX_MODULE_EXPORT)\n        namespace prv {\n            class Provider;\n        }\n    #endif\n\n    template<typename T>\n    class PerProvider {\n    public:\n        PerProvider() { this->onCreate(); }\n        PerProvider(const PerProvider&) = delete;\n        PerProvider(PerProvider&&) = delete;\n        PerProvider& operator=(const PerProvider&) = delete;\n        PerProvider& operator=(PerProvider &&) = delete;\n\n        ~PerProvider() { this->onDestroy(); }\n\n        T* operator->() {\n            return &this->get();\n        }\n\n        const T* operator->() const {\n            return &this->get();\n        }\n\n        T& get(const prv::Provider *provider = ImHexApi::Provider::get()) {\n            if (provider == nullptr) [[unlikely]]\n                throw std::invalid_argument(\"PerProvider::get called with nullptr\");\n\n            return m_data[provider];\n        }\n\n        const T& get(const prv::Provider *provider = ImHexApi::Provider::get()) const {\n            if (provider == nullptr) [[unlikely]]\n                throw std::invalid_argument(\"PerProvider::get called with nullptr\");\n\n            return m_data.at(provider);\n        }\n\n        void set(const T &data, const prv::Provider *provider = ImHexApi::Provider::get()) {\n            if (provider == nullptr) [[unlikely]]\n                throw std::invalid_argument(\"PerProvider::set called with nullptr\");\n\n            m_data[provider] = data;\n        }\n\n        void set(T &&data, const prv::Provider *provider = ImHexApi::Provider::get()) {\n            if (provider == nullptr) [[unlikely]]\n                throw std::invalid_argument(\"PerProvider::set called with nullptr\");\n\n            m_data[provider] = std::move(data);\n        }\n\n        T& operator*() {\n            return this->get();\n        }\n\n        const T& operator*() const {\n            return this->get();\n        }\n\n        PerProvider& operator=(const T &data) {\n            this->set(data);\n            return *this;\n        }\n\n        PerProvider& operator=(T &&data) {\n            this->set(std::move(data));\n            return *this;\n        }\n\n        operator T&() {\n            return this->get();\n        }\n\n        auto all() {\n            return m_data | std::views::values;\n        }\n\n        void setOnCreateCallback(std::function<void(prv::Provider *, T&)> callback) {\n            m_onCreateCallback = std::move(callback);\n        }\n\n        void setOnDestroyCallback(std::function<void(prv::Provider *, T&)> callback) {\n            m_onDestroyCallback = std::move(callback);\n        }\n\n    private:\n        void onCreate() {\n            EventProviderOpened::subscribe(this, [this](prv::Provider *provider) {\n                auto [it, inserted] = m_data.emplace(provider, T());\n                auto &[key, value] = *it;\n                if (m_onCreateCallback)\n                    m_onCreateCallback(provider, value);\n            });\n\n            EventProviderDeleted::subscribe(this, [this](prv::Provider *provider){\n                if (auto it = m_data.find(provider); it != m_data.end()) {\n                    if (m_onDestroyCallback)\n                        m_onDestroyCallback(provider, m_data.at(provider));\n\n                    m_data.erase(it);\n                }\n            });\n\n            EventImHexClosing::subscribe(this, [this] {\n                m_data.clear();\n            });\n\n            // Moves the data of this PerProvider instance from one provider to another\n            MovePerProviderData::subscribe(this, [this](prv::Provider *from, prv::Provider *to) {\n                // Get the value from the old provider, (removes it from the map)\n                auto node = m_data.extract(from);\n\n                // Ensure the value existed\n                if (node.empty()) return;\n\n                // Delete the value from the new provider, that we want to replace\n                m_data.erase(to);\n\n                // Re-insert it with the key of the new provider\n                node.key() = to;\n                m_data.insert(std::move(node));\n            });\n        }\n\n        void onDestroy() {\n\n            EventProviderOpened::unsubscribe(this);\n            EventProviderDeleted::unsubscribe(this);\n            EventImHexClosing::unsubscribe(this);\n            MovePerProviderData::unsubscribe(this);\n        }\n\n    private:\n        std::map<const prv::Provider *, T> m_data;\n        std::function<void(prv::Provider *, T&)> m_onCreateCallback, m_onDestroyCallback;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/providers/undo_redo/operations/operation.hpp",
    "content": "#pragma once\n\n#include <string>\n#include <vector>\n\n#include <hex/helpers/concepts.hpp>\n\nnamespace hex::prv {\n    class Provider;\n}\n\nnamespace hex::prv::undo {\n\n    class Operation : public ICloneable<Operation> {\n    public:\n        ~Operation() override = default;\n\n        virtual void undo(Provider *provider) = 0;\n        virtual void redo(Provider *provider) = 0;\n\n        [[nodiscard]] virtual Region getRegion() const = 0;\n\n        [[nodiscard]] virtual std::string format() const = 0;\n        [[nodiscard]] virtual std::vector<std::string> formatContent() const {\n            return { };\n        }\n\n        [[nodiscard]] virtual bool shouldHighlight() const { return true; }\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/providers/undo_redo/operations/operation_group.hpp",
    "content": "#pragma once\n\n#include <hex/providers/undo_redo/operations/operation.hpp>\n\n#include <hex/api/localization_manager.hpp>\n#include <hex/helpers/fmt.hpp>\n#include <hex/helpers/utils.hpp>\n\nnamespace hex::prv::undo {\n\n    class OperationGroup : public Operation {\n    public:\n        explicit OperationGroup(UnlocalizedString unlocalizedName) : m_unlocalizedName(std::move(unlocalizedName)) {}\n\n        OperationGroup(const OperationGroup &other) {\n            for (const auto &operation : other.m_operations)\n                m_operations.emplace_back(operation->clone());\n        }\n\n        void undo(Provider *provider) override {\n            for (auto &operation : m_operations)\n                operation->undo(provider);\n        }\n\n        void redo(Provider *provider) override {\n            for (auto &operation : m_operations)\n                operation->redo(provider);\n        }\n\n        void addOperation(std::unique_ptr<Operation> &&newOperation) {\n            auto newRegion = newOperation->getRegion();\n            if (newRegion.getStartAddress() < m_startAddress)\n                m_startAddress = newRegion.getStartAddress();\n            if (newRegion.getEndAddress() > m_endAddress)\n                m_endAddress = newRegion.getEndAddress();\n\n            if (m_formattedContent.size() <= 10)\n                m_formattedContent.emplace_back(newOperation->format());\n            else\n                m_formattedContent.back() = fmt::format(\"[{}x] ...\", (m_operations.size() - 10) + 1);\n\n            m_operations.emplace_back(std::move(newOperation));\n        }\n\n        [[nodiscard]] std::string format() const override {\n            return fmt::format(\"{}\", Lang(m_unlocalizedName));\n        }\n\n        [[nodiscard]] Region getRegion() const override {\n            return Region { m_startAddress, (m_endAddress - m_startAddress) + 1 };\n        }\n\n        std::unique_ptr<Operation> clone() const override {\n            return std::make_unique<OperationGroup>(*this);\n        }\n\n        std::vector<std::string> formatContent() const override {\n            return m_formattedContent;\n        }\n\n    private:\n        UnlocalizedString m_unlocalizedName;\n        std::vector<std::unique_ptr<Operation>> m_operations;\n\n        u64 m_startAddress = std::numeric_limits<u64>::max();\n        u64 m_endAddress   = std::numeric_limits<u64>::min();\n        std::vector<std::string> m_formattedContent;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/providers/undo_redo/stack.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <hex/api/localization_manager.hpp>\n\n#include <hex/providers/undo_redo/operations/operation.hpp>\n\n#include <map>\n#include <memory>\n#include <mutex>\n#include <vector>\n\nnamespace hex::prv {\n    class Provider;\n}\n\nnamespace hex::prv::undo {\n\n    using Patches = std::map<u64, u8>;\n\n    class Stack {\n    public:\n        explicit Stack(Provider *provider);\n\n        void undo(u32 count = 1);\n        void redo(u32 count = 1);\n\n        void groupOperations(u32 count, const UnlocalizedString &unlocalizedName);\n        void apply(const Stack &otherStack);\n        void reapply();\n\n        [[nodiscard]] bool canUndo() const;\n        [[nodiscard]] bool canRedo() const;\n\n        template<std::derived_from<Operation> T>\n        bool add(auto && ... args) {\n            auto result = this->add(std::make_unique<T>(std::forward<decltype(args)>(args)...));\n\n            return result;\n        }\n\n        bool add(std::unique_ptr<Operation> &&operation);\n\n        static std::recursive_mutex& getMutex();\n\n        const std::vector<std::unique_ptr<Operation>> &getAppliedOperations() const {\n            return m_undoStack;\n        }\n\n        const std::vector<std::unique_ptr<Operation>> &getUndoneOperations() const {\n            return m_redoStack;\n        }\n\n        void reset() {\n            m_undoStack.clear();\n            m_redoStack.clear();\n        }\n\n    private:\n        [[nodiscard]] Operation* getLastOperation() const {\n            return m_undoStack.back().get();\n        }\n\n    private:\n        std::vector<std::unique_ptr<Operation>> m_undoStack, m_redoStack;\n        Provider *m_provider;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/subcommands/subcommands.hpp",
    "content": "#pragma once\n\n#include <vector>\n#include <string>\n#include <functional>\n\nnamespace hex::subcommands {\n\n    /**\n     * @brief Internal method - takes all the arguments ImHex received from the command line,\n     * and determine which subcommands to run, with which arguments.\n     * In some cases, the subcommand or this function directly might exit the program\n     * (e.g. --help, or when forwarding providers to open to another instance)\n     * and so this function might not return\n     */\n    void processArguments(const std::vector<std::string> &args);\n\n    \n    /**\n     * @brief Forward the given command to the main instance (might be this instance)\n     * The callback will be executed after EventImHexStartupFinished\n     */\n    void forwardSubCommand(const std::string &cmdName, const std::vector<std::string> &args);\n\n    using ForwardCommandHandler = std::function<void(const std::vector<std::string> &)>;\n    \n    /**\n     * @brief Register the handler for this specific command name\n     */\n    void registerSubCommand(const std::string &cmdName, const ForwardCommandHandler &handler);\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/test/test_provider.hpp",
    "content": "#pragma once\n\n#include <hex/providers/provider.hpp>\n#include <nlohmann/json.hpp>\n\nnamespace hex::test {\n    using namespace hex::prv;\n\n    class TestProvider : public prv::Provider {\n    public:\n        explicit TestProvider(std::vector<u8> *data) {\n            this->setData(data);\n        }\n        ~TestProvider() override = default;\n\n        [[nodiscard]] bool isAvailable() const override { return true; }\n        [[nodiscard]] bool isReadable() const override { return true; }\n        [[nodiscard]] bool isWritable() const override { return false; }\n        [[nodiscard]] bool isResizable() const override { return false; }\n        [[nodiscard]] bool isSavable() const override { return false; }\n\n        void setData(std::vector<u8> *data) {\n            m_data = data;\n        }\n\n        [[nodiscard]] std::string getName() const override {\n            return \"\";\n        }\n\n        [[nodiscard]] const char* getIcon() const override {\n            return \"\";\n        }\n\n        void readRaw(u64 offset, void *buffer, size_t size) override {\n            if (offset + size > m_data->size()) return;\n\n            std::memcpy(buffer, m_data->data() + offset, size);\n        }\n\n        void writeRaw(u64 offset, const void *buffer, size_t size) override {\n            if (offset + size > m_data->size()) return;\n\n            std::memcpy(m_data->data() + offset, buffer, size);\n        }\n\n        [[nodiscard]] u64 getActualSize() const override {\n            return m_data->size();\n        }\n\n        [[nodiscard]] UnlocalizedString getTypeName() const override { return \"hex.test.provider.test\"; }\n\n        OpenResult open() override { return {}; }\n        void close() override { }\n\n        nlohmann::json storeSettings(nlohmann::json) const override { return {}; }\n        void loadSettings(const nlohmann::json &) override {};\n\n    private:\n        std::vector<u8> *m_data = nullptr;\n    };\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/test/tests.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <utility>\n#include <hex/helpers/utils.hpp>\n#include <hex/helpers/fmt.hpp>\n#include <hex/helpers/logger.hpp>\n#include <hex/api/plugin_manager.hpp>\n\n#if defined(IMGUI_TEST_ENGINE)\n    #include <imgui_te_context.h>\n    #include <imgui_te_engine.h>\n    #include <source_location>\n    #include <hex/api/events/events_lifecycle.hpp>\n#endif\n\n\n#include <wolv/utils/preproc.hpp>\n\n#include <string>\n#include <map>\n#include <functional>\n\n#define TEST_SEQUENCE(...) static auto WOLV_ANONYMOUS_VARIABLE(TEST_SEQUENCE) = ::hex::test::TestSequenceExecutor(__VA_ARGS__) + []() -> int\n#define TEST_FAIL()        return EXIT_FAILURE\n#define TEST_SUCCESS()     return EXIT_SUCCESS\n#define FAILING            true\n#define TEST_ASSERT(x, ...)                                            \\\n    do {                                                               \\\n        auto ret = (x);                                                \\\n        if (!ret) {                                                    \\\n            hex::log::error(\"Test assert '{}' failed {} at {}:{}\",     \\\n                #x,                                                    \\\n                fmt::format(\"\" __VA_ARGS__),                           \\\n                __FILE__,                                              \\\n                __LINE__);                                             \\\n            return EXIT_FAILURE;                                       \\\n        }                                                              \\\n    } while (0)\n\n#define INIT_PLUGIN(name) \\\n    if (!hex::test::initPluginImpl(name)) TEST_FAIL();\n\n#if defined(IMGUI_TEST_ENGINE)\n    #define IMGUI_TEST_SEQUENCE(category, name, ctx)                                                  \\\n        static auto WOLV_ANONYMOUS_VARIABLE(TEST_SEQUENCE) =                                          \\\n        ::hex::test::ImGuiTestSequenceExecutor(category, name, std::source_location::current()) +     \\\n        [](ImGuiTestContext *ctx) -> void\n#else\n    #define IMGUI_TEST_SEQUENCE(...) static auto WOLV_ANONYMOUS_VARIABLE(TEST_SEQUENCE) = []() -> int\n#endif\n\nnamespace hex::test {\n\n    using Function = int(*)();\n    struct Test {\n        Function function;\n        bool shouldFail;\n    };\n\n    class Tests {\n    public:\n        static int addTest(const std::string &name, Function func, bool shouldFail) noexcept;\n\n        static std::map<std::string, Test> &get() noexcept;\n    };\n\n    template<class F>\n    class TestSequence {\n    public:\n        TestSequence(const std::string &name, F func, bool shouldFail) noexcept {\n            Tests::addTest(name, func, shouldFail);\n        }\n\n        TestSequence &operator=(TestSequence &&) = delete;\n    };\n\n    struct TestSequenceExecutor {\n        explicit TestSequenceExecutor(std::string name, bool shouldFail = false) noexcept : m_name(std::move(name)), m_shouldFail(shouldFail) {\n        }\n\n        [[nodiscard]] const auto &getName() const noexcept {\n            return m_name;\n        }\n\n        [[nodiscard]] bool shouldFail() const noexcept {\n            return m_shouldFail;\n        }\n\n    private:\n        std::string m_name;\n        bool m_shouldFail;\n    };\n\n\n    template<typename F>\n    TestSequence<F> operator+(const TestSequenceExecutor &executor, F &&f) noexcept {\n        return TestSequence<F>(executor.getName(), std::forward<F>(f), executor.shouldFail());\n    }\n\n\n    #if defined(IMGUI_TEST_ENGINE)\n        template<class F>\n        class ImGuiTestSequence {\n        public:\n            ImGuiTestSequence(const std::string &category, const std::string &name, std::source_location sourceLocation, F func) noexcept {\n                log::info(\"Registering ImGui Test\");\n                EventRegisterImGuiTests::subscribe([=](ImGuiTestEngine *engine) {\n                    auto test = ImGuiTestEngine_RegisterTest(engine, category.c_str(), name.c_str(), sourceLocation.file_name(), sourceLocation.line());\n                    test->TestFunc = func;\n                });\n            }\n\n            ImGuiTestSequence &operator=(ImGuiTestSequence &&) = delete;\n        };\n\n        struct ImGuiTestSequenceExecutor {\n            explicit ImGuiTestSequenceExecutor(std::string category, std::string name, std::source_location sourceLocation) noexcept\n                : m_category(std::move(category)), m_name(std::move(name)), m_sourceLocation(sourceLocation) {\n            }\n\n            [[nodiscard]] const auto &getCategory() const noexcept {\n                return m_category;\n            }\n\n            [[nodiscard]] const auto &getName() const noexcept {\n                return m_name;\n            }\n\n            [[nodiscard]] const auto &getSourceLocation() const noexcept {\n                return m_sourceLocation;\n            }\n\n        private:\n            std::string m_category, m_name;\n            std::source_location m_sourceLocation;\n        };\n\n\n        template<typename F>\n        ImGuiTestSequence<F> operator+(const ImGuiTestSequenceExecutor &executor, F &&f) noexcept {\n            return ImGuiTestSequence<F>(executor.getCategory(), executor.getName(), executor.getSourceLocation(), std::forward<F>(f));\n        }\n    #endif\n\n\n    bool initPluginImpl(std::string name);\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/ui/banner.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <imgui.h>\n#include <imgui_internal.h>\n#include <hex/helpers/scaling.hpp>\n\n#include <list>\n#include <memory>\n#include <mutex>\n\nnamespace hex {\n\n    namespace impl {\n\n        class BannerBase {\n        public:\n            BannerBase(ImColor color) : m_color(color) {}\n            virtual ~BannerBase() = default;\n\n            virtual void draw() { drawContent(); }\n            virtual void drawContent() = 0;\n\n            [[nodiscard]] static std::list<std::unique_ptr<BannerBase>> &getOpenBanners();\n\n            [[nodiscard]] const ImColor& getColor() const {\n                return m_color;\n            }\n\n            void close() { m_shouldClose = true; }\n            [[nodiscard]] bool shouldClose() const { return m_shouldClose; }\n\n        protected:\n            static std::mutex& getMutex();\n\n            bool m_shouldClose = false;\n            ImColor m_color;\n        };\n\n    }\n\n    template<typename T>\n    class Banner : public impl::BannerBase {\n    public:\n        using impl::BannerBase::BannerBase;\n\n        template<typename ...Args>\n        static void open(Args && ... args) {\n            std::lock_guard lock(getMutex());\n\n            auto toast = std::make_unique<T>(std::forward<Args>(args)...);\n            getOpenBanners().emplace_back(std::move(toast));\n        }\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/ui/imgui_imhex_extensions.h",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <cstddef>\n#include <string>\n#include <span>\n\n#include <imgui.h>\n\n#include <hex/helpers/fmt.hpp>\n#include <hex/helpers/concepts.hpp>\n#include <hex/helpers/fs.hpp>\n#include <hex/helpers/scaling.hpp>\n\n#include <wolv/utils/string.hpp>\n\nenum ImGuiCustomCol : int {\n    ImGuiCustomCol_DescButton,\n    ImGuiCustomCol_DescButtonHovered,\n    ImGuiCustomCol_DescButtonActive,\n\n    ImGuiCustomCol_ToolbarGray,\n    ImGuiCustomCol_ToolbarRed,\n    ImGuiCustomCol_ToolbarYellow,\n    ImGuiCustomCol_ToolbarGreen,\n    ImGuiCustomCol_ToolbarBlue,\n    ImGuiCustomCol_ToolbarPurple,\n    ImGuiCustomCol_ToolbarBrown,\n\n    ImGuiCustomCol_LoggerDebug,\n    ImGuiCustomCol_LoggerInfo,\n    ImGuiCustomCol_LoggerWarning,\n    ImGuiCustomCol_LoggerError,\n    ImGuiCustomCol_LoggerFatal,\n\n    ImGuiCustomCol_AchievementUnlocked,\n\n    ImGuiCustomCol_FindHighlight,\n\n    ImGuiCustomCol_DiffAdded,\n    ImGuiCustomCol_DiffRemoved,\n    ImGuiCustomCol_DiffChanged,\n\n    ImGuiCustomCol_AdvancedEncodingASCII,\n    ImGuiCustomCol_AdvancedEncodingSingleChar,\n    ImGuiCustomCol_AdvancedEncodingMultiChar,\n    ImGuiCustomCol_AdvancedEncodingUnknown,\n\n    ImGuiCustomCol_Highlight,\n\n    ImGuiCustomCol_Patches,\n    ImGuiCustomCol_PatternSelected,\n\n    ImGuiCustomCol_IEEEToolSign,\n    ImGuiCustomCol_IEEEToolExp,\n    ImGuiCustomCol_IEEEToolMantissa,\n\n    ImGuiCustomCol_BlurBackground,\n\n    ImGuiCustomCol_COUNT\n};\n\nenum ImGuiCustomStyle {\n    ImGuiCustomStyle_WindowBlur,\n\n    ImGuiCustomStyle_COUNT\n};\n\nnamespace ImGuiExt {\n\n    class Texture {\n    public:\n        enum class Filter : int {\n            Linear,\n            Nearest\n        };\n\n        Texture() = default;\n        Texture(const Texture&) = delete;\n        Texture(Texture&& other) noexcept;\n\n        [[nodiscard]] static Texture fromImage(const ImU8 *buffer, int size, Filter filter = Filter::Nearest);\n        [[nodiscard]] static Texture fromImage(std::span<const std::byte> buffer, Filter filter = Filter::Nearest);\n        [[nodiscard]] static Texture fromImage(const char *path, Filter filter = Filter::Nearest);\n        [[nodiscard]] static Texture fromImage(const std::fs::path &path, Filter filter = Filter::Nearest);\n        [[nodiscard]] static Texture fromGLTexture(unsigned int texture, int width, int height);\n        [[nodiscard]] static Texture fromBitmap(const ImU8 *buffer, int size, int width, int height, Filter filter = Filter::Nearest);\n        [[nodiscard]] static Texture fromBitmap(std::span<const std::byte> buffer, int width, int height, Filter filter = Filter::Nearest);\n        [[nodiscard]] static Texture fromSVG(const char *path, int width = 0, int height = 0, Filter filter = Filter::Nearest);\n        [[nodiscard]] static Texture fromSVG(const std::fs::path &path, int width = 0, int height = 0, Filter filter = Filter::Nearest);\n        [[nodiscard]] static Texture fromSVG(std::span<const std::byte> buffer, int width = 0, int height = 0, Filter filter = Filter::Nearest);\n\n        ~Texture();\n\n        Texture& operator=(const Texture&) = delete;\n        Texture& operator=(Texture&& other) noexcept;\n\n        [[nodiscard]] constexpr bool isValid() const noexcept {\n            return m_textureId != 0;\n        }\n\n        [[nodiscard]] operator ImTextureRef() const noexcept {\n            return m_textureId;\n        }\n\n        [[nodiscard]] operator ImTextureID() const noexcept {\n            return m_textureId;\n        }\n\n        [[nodiscard]] ImVec2 getSize() const noexcept {\n            return ImVec2(m_width, m_height);\n        }\n\n        [[nodiscard]] constexpr float getAspectRatio() const noexcept {\n            if (m_height == 0) return 1.0F;\n\n            return float(m_width) / float(m_height);\n        }\n\n        [[nodiscard]] std::vector<u8> toBytes() const noexcept;\n\n        void reset();\n\n    private:\n        ImTextureID m_textureId = 0;\n        int m_width = 0, m_height = 0;\n    };\n\n    float GetTextWrapPos();\n\n    int UpdateStringSizeCallback(ImGuiInputTextCallbackData *data);\n\n    bool IconHyperlink(const char *icon, const char *label, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);\n    bool Hyperlink(const char *label, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);\n    bool BulletHyperlink(const char *label, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);\n    bool DescriptionButton(const char *label, const char *description, const char *icon, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);\n    bool DescriptionButtonProgress(const char *label, const char *description, const char *icon, float fraction, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);\n\n    void HelpHover(const char *text, const char *icon = \"(?)\", ImU32 iconColor = ImGui::GetColorU32(ImGuiCol_ButtonActive));\n\n    void UnderlinedText(const char *label, ImColor color = ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg = ImVec2(0, 0));\n\n    void UnderwavedText(const char *label, ImColor textColor = ImGui::GetStyleColorVec4(ImGuiCol_Text), ImColor lineColor = ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg = ImVec2(0, 0));\n\n    void TextSpinner(const char *label);\n\n    void Header(const char *label, bool firstEntry = false);\n    void HeaderColored(const char *label, ImColor color, bool firstEntry);\n\n    bool InfoTooltip(const char *text = \"\",bool = false);\n\n    bool TitleBarButton(const char *label, ImVec2 size_arg);\n    bool ToolBarButton(const char *symbol, ImVec4 color);\n    bool IconButton(const char *symbol, ImVec4 color, ImVec2 size_arg = ImVec2(0, 0), ImVec2 iconOffset = ImVec2(0, 0));\n\n    bool InputPrefix(const char* label, const char *prefix, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);\n    bool InputIntegerPrefix(const char* label, const char *prefix, void *value, ImGuiDataType type, const char *format, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);\n    bool InputHexadecimal(const char* label, u32 *value, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);\n    bool InputHexadecimal(const char* label, u64 *value, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);\n\n    bool SliderBytes(const char *label, u64 *value, u64 min, u64 max, u64 stepSize = 1, ImGuiSliderFlags flags = ImGuiSliderFlags_None);\n\n    inline bool HasSecondPassed() {\n        return static_cast<ImU32>(ImGui::GetTime() * 100) % 100 <= static_cast<ImU32>(ImGui::GetIO().DeltaTime * 100);\n    }\n\n    void OpenPopupInWindow(const char *window_name, const char *popup_name);\n\n    void DisableWindowResize(ImGuiDir dir);\n\n    struct ImHexCustomData {\n        ImVec4 Colors[ImGuiCustomCol_COUNT];\n\n        struct Styles {\n            float WindowBlur = 0.0F;\n            float PopupWindowAlpha = 0.0F; // Alpha used by Popup tool windows when the user is not hovering over them\n        } styles;\n    };\n\n    ImU32 GetCustomColorU32(ImGuiCustomCol idx, float alpha_mul = 1.0F);\n    ImVec4 GetCustomColorVec4(ImGuiCustomCol idx, float alpha_mul = 1.0F);\n\n    inline ImHexCustomData::Styles& GetCustomStyle() {\n        auto &customData = *static_cast<ImHexCustomData *>(ImGui::GetIO().UserData);\n\n        return customData.styles;\n    }\n\n    float GetCustomStyleFloat(ImGuiCustomStyle idx);\n    ImVec2 GetCustomStyleVec2(ImGuiCustomStyle idx);\n\n    void StyleCustomColorsDark();\n    void StyleCustomColorsLight();\n    void StyleCustomColorsClassic();\n\n    void ProgressBar(float fraction, ImVec2 size_value = ImVec2(0, 0), float yOffset = 0.0F);\n\n    [[nodiscard]] bool IsDarkBackground(const ImColor& bgColor);\n\n    void TextFormatted(std::string_view fmt, auto &&...args) {\n        if constexpr (sizeof...(args) == 0) {\n            ImGui::TextUnformatted(fmt.data(), fmt.data() + fmt.size());\n        } else {\n            const auto string = fmt::format(fmt::runtime(fmt), std::forward<decltype(args)>(args)...);\n            ImGui::TextUnformatted(string.c_str(), string.c_str() + string.size());\n        }\n    }\n\n    void TextFormattedSelectable(std::string_view fmt, auto &&...args) {\n        auto text = fmt::format(fmt::runtime(fmt), std::forward<decltype(args)>(args)...);\n\n        ImGui::PushID(text.c_str());\n\n        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());\n        ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F);\n        ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4());\n\n        ImGui::PushItemWidth(ImGui::CalcTextSize(text.c_str()).x + ImGui::GetStyle().FramePadding.x * 2);\n        ImGui::InputText(\"##\", const_cast<char *>(text.c_str()), text.size() + 1, ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoHorizontalScroll);\n        ImGui::PopItemWidth();\n\n        ImGui::PopStyleColor();\n        ImGui::PopStyleVar(2);\n\n        ImGui::PopID();\n    }\n\n    void TextFormattedColored(ImColor color, std::string_view fmt, auto &&...args) {\n        ImGui::PushStyleColor(ImGuiCol_Text, color.Value);\n        ImGuiExt::TextFormatted(fmt, std::forward<decltype(args)>(args)...);\n        ImGui::PopStyleColor();\n    }\n\n    void TextFormattedReadableColor(ImColor backgroundColor, std::string_view fmt, auto &&...args) {\n        ImGui::PushStyleColor(ImGuiCol_Text, IsDarkBackground(backgroundColor) ? 0xFFFFFFFF : 0xFF000000);\n        ImGuiExt::TextFormatted(fmt, std::forward<decltype(args)>(args)...);\n        ImGui::PopStyleColor();\n    }\n\n    void TextFormattedDisabled(std::string_view fmt, auto &&...args) {\n        ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);\n        ImGuiExt::TextFormatted(fmt, std::forward<decltype(args)>(args)...);\n        ImGui::PopStyleColor();\n    }\n\n    void TextFormattedWrapped(std::string_view fmt, auto &&...args) {\n        const bool need_backup = ImGuiExt::GetTextWrapPos() < 0.0F;  // Keep existing wrap position if one is already set\n        if (need_backup)\n            ImGui::PushTextWrapPos(0.0F);\n        ImGuiExt::TextFormatted(fmt, std::forward<decltype(args)>(args)...);\n        if (need_backup)\n            ImGui::PopTextWrapPos();\n    }\n\n    void TextFormattedWrappedSelectable(std::string_view fmt, auto &&...args) {\n        using namespace hex;\n        // Manually wrap text, using the letter M (generally the widest character in non-monospaced fonts) to calculate the character width to use.\n        auto text = wolv::util::trim(wolv::util::wrapMonospacedString(\n                fmt::format(fmt::runtime(fmt), std::forward<decltype(args)>(args)...),\n                ImGui::CalcTextSize(\"M\").x,\n                std::max(100_scaled, ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ScrollbarSize - ImGui::GetStyle().FrameBorderSize)\n        ));\n\n        auto textSize = ImGui::CalcTextSize(text.c_str());\n\n        ImGui::PushID(text.c_str());\n\n        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());\n        ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F);\n        ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4());\n\n        ImGui::PushItemWidth(textSize.x + ImGui::GetStyle().FramePadding.x * 2);\n        ImGui::InputTextMultiline(\n                \"##\",\n                const_cast<char *>(text.c_str()),\n                text.size() + 1,\n                ImVec2(0, textSize.y),\n                ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoHorizontalScroll\n        );\n        ImGui::PopItemWidth();\n\n        ImGui::PopStyleColor();\n        ImGui::PopStyleVar(2);\n\n        ImGui::PopID();\n    }\n\n    void TextUnformattedCentered(const char *text);\n    void TextFormattedCentered(std::string_view fmt, auto &&...args) {\n        auto text = fmt::format(fmt::runtime(fmt), std::forward<decltype(args)>(args)...);\n        TextUnformattedCentered(text.c_str());\n    }\n\n\n    void TextFormattedCenteredHorizontal(std::string_view fmt, auto &&...args) {\n        auto text = fmt::format(fmt::runtime(fmt), std::forward<decltype(args)>(args)...);\n        auto availableSpace = ImGui::GetContentRegionAvail();\n        auto textSize = ImGui::CalcTextSize(text.c_str(), nullptr, false, availableSpace.x * 0.75F);\n\n        ImGui::SetCursorPosX(((availableSpace - textSize) / 2.0F).x);\n\n        ImGui::PushTextWrapPos(availableSpace.x * 0.75F);\n        ImGuiExt::TextFormattedWrapped(\"{}\", text);\n        ImGui::PopTextWrapPos();\n    }\n\n    bool InputTextIcon(const char* label, const char *icon, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);\n    bool InputTextIconHint(const char* label, const char *icon, const char *hint, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);\n\n    bool InputScalarCallback(const char* label, ImGuiDataType data_type, void* p_data, const char* format, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data);\n\n    void HideTooltip();\n\n    bool BitCheckbox(const char* label, bool* v);\n\n    bool DimmedButton(const char* label, ImVec2 size = ImVec2(0, 0), ImGuiButtonFlags flags = ImGuiButtonFlags_None);\n    bool DimmedIconButton(const char *symbol, ImVec4 color, ImVec2 size = ImVec2(0, 0), ImVec2 iconOffset = ImVec2(0, 0));\n    bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size = ImVec2(0, 0), ImVec2 iconOffset = ImVec2(0, 0));\n    bool DimmedIconToggle(const char *icon, bool *v);\n    bool DimmedIconToggle(const char *iconOn, const char *iconOff, bool *v);\n    bool DimmedArrowButton(const char *id, ImGuiDir dir, ImVec2 size = ImVec2(ImGui::GetFrameHeight(), ImGui::GetFrameHeight()));\n\n    void TextOverlay(const char *text, ImVec2 pos, float maxWidth = -1);\n\n    bool BeginBox();\n    void EndBox();\n\n    bool BeginSubWindow(const char *label, bool *collapsed = nullptr, ImVec2 size = ImVec2(0, 0), ImGuiChildFlags flags = ImGuiChildFlags_None);\n    void EndSubWindow();\n\n    void ConfirmButtons(const char *textLeft, const char *textRight, const auto &leftButtonCallback, const auto &rightButtonCallback) {\n        auto width = ImGui::GetWindowWidth();\n        ImGui::SetCursorPosX(width / 9);\n        if (ImGui::Button(textLeft, ImVec2(width / 3, 0)))\n            leftButtonCallback();\n        ImGui::SameLine();\n        ImGui::SetCursorPosX(width / 9 * 5);\n        if (ImGui::Button(textRight, ImVec2(width / 3, 0)))\n            rightButtonCallback();\n    }\n\n    bool VSliderAngle(const char* label, ImVec2& size, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags);\n\n    bool InputFilePicker(const char *label, std::fs::path &path, const std::vector<hex::fs::ItemFilter> &validExtensions);\n\n    bool ToggleSwitch(const char *label, bool *v);\n    bool ToggleSwitch(const char *label, bool v);\n\n    bool PopupTitleBarButton(const char* label, bool p_enabled);\n    void PopupTitleBarText(const char* text);\n\n    template<typename T>\n    constexpr ImGuiDataType getImGuiDataType() {\n        if constexpr      (std::same_as<T, u8>)     return ImGuiDataType_U8;\n        else if constexpr (std::same_as<T, u16>)    return ImGuiDataType_U16;\n        else if constexpr (std::same_as<T, u32>)    return ImGuiDataType_U32;\n        else if constexpr (std::same_as<T, u64>)    return ImGuiDataType_U64;\n        else if constexpr (std::same_as<T, i8>)     return ImGuiDataType_S8;\n        else if constexpr (std::same_as<T, i16>)    return ImGuiDataType_S16;\n        else if constexpr (std::same_as<T, i32>)    return ImGuiDataType_S32;\n        else if constexpr (std::same_as<T, i64>)    return ImGuiDataType_S64;\n        else if constexpr (std::same_as<T, float>)  return ImGuiDataType_Float;\n        else if constexpr (std::same_as<T, double>) return ImGuiDataType_Double;\n        else static_assert(hex::always_false<T>::value, \"Invalid data type!\");\n    }\n\n    template<typename T>\n    constexpr const char *getFormatLengthSpecifier() {\n        if constexpr      (std::same_as<T, u8>)     return \"hh\";\n        else if constexpr (std::same_as<T, u16>)    return \"h\";\n        else if constexpr (std::same_as<T, u32>)    return \"l\";\n        else if constexpr (std::same_as<T, u64>)    return \"ll\";\n        else if constexpr (std::same_as<T, i8>)     return \"hh\";\n        else if constexpr (std::same_as<T, i16>)    return \"h\";\n        else if constexpr (std::same_as<T, i32>)    return \"l\";\n        else if constexpr (std::same_as<T, i64>)    return \"ll\";\n        else static_assert(hex::always_false<T>::value, \"Invalid data type!\");\n    }\n\n    struct ImGuiTestEngine {\n        ImGuiTestEngine() = delete;\n\n        static void setEnabled(bool enabled);\n        [[nodiscard]] static bool isEnabled();\n    };\n}\n\n// these functions are exception because they just allow conversion from string to char*, they do not really add anything\nnamespace ImGui {\n    bool InputText(const char* label, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);\n    bool InputText(const char *label, std::u8string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);\n    bool InputTextMultiline(const char* label, std::string &buffer, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);\n    bool InputTextWithHint(const char *label, const char *hint, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/ui/popup.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <hex/api/localization_manager.hpp>\n\n#include <memory>\n#include <string>\n\n#include <imgui.h>\n#include <hex/ui/imgui_imhex_extensions.h>\n\n#include <hex/api/task_manager.hpp>\n#include <hex/helpers/scaling.hpp>\n\nnamespace hex {\n\n    namespace impl {\n\n        class PopupBase {\n        public:\n            explicit PopupBase(UnlocalizedString unlocalizedName, bool closeButton, bool modal)\n                : m_unlocalizedName(std::move(unlocalizedName)), m_closeButton(closeButton), m_modal(modal) { }\n\n            virtual ~PopupBase() = default;\n\n            virtual void drawContent() = 0;\n            [[nodiscard]] virtual ImGuiWindowFlags getFlags() const { return ImGuiWindowFlags_None; }\n\n            [[nodiscard]] virtual ImVec2 getMinSize() const {\n                return { 0, 0 };\n            }\n\n            [[nodiscard]] virtual ImVec2 getMaxSize() const {\n                return { 0, 0 };\n            }\n\n            [[nodiscard]] static std::vector<std::unique_ptr<PopupBase>> &getOpenPopups();\n\n            [[nodiscard]] const UnlocalizedString &getUnlocalizedName() const {\n                return m_unlocalizedName;\n            }\n\n            [[nodiscard]] bool hasCloseButton() const {\n                return m_closeButton;\n            }\n\n            [[nodiscard]] bool isModal() const {\n                return m_modal;\n            }\n\n            void close() {\n                m_close = true;\n            }\n\n            [[nodiscard]] bool shouldClose() const {\n                return m_close;\n            }\n\n        protected:\n            static std::mutex& getMutex();\n        private:\n            UnlocalizedString m_unlocalizedName;\n            bool m_closeButton, m_modal;\n            std::atomic<bool> m_close = false;\n        };\n\n    }\n\n\n    template<typename T>\n    class Popup : public impl::PopupBase {\n    protected:\n        explicit Popup(UnlocalizedString unlocalizedName, bool closeButton = true, bool modal = true) : PopupBase(std::move(unlocalizedName), closeButton, modal) { }\n\n    public:\n        template<typename ...Args>\n        static void open(Args && ... args) {\n            std::lock_guard lock(getMutex());\n\n            auto popup = std::make_unique<T>(std::forward<Args>(args)...);\n\n            getOpenPopups().emplace_back(std::move(popup));\n        }\n\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex/ui/toast.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n#include <imgui.h>\n\n#include <list>\n#include <memory>\n#include <mutex>\n#include <hex/api/task_manager.hpp>\n\nnamespace hex {\n\n    namespace impl {\n\n        class ToastBase {\n        public:\n            ToastBase(ImColor color) : m_color(color) {}\n            virtual ~ToastBase() = default;\n\n            virtual void draw() { drawContent(); }\n            virtual void drawContent() = 0;\n\n            [[nodiscard]] static std::list<std::unique_ptr<ToastBase>> &getQueuedToasts();\n\n            [[nodiscard]] const ImColor& getColor() const {\n                return m_color;\n            }\n\n            void setAppearTime(double appearTime) {\n                m_appearTime = appearTime;\n            }\n\n            [[nodiscard]] double getAppearTime() const {\n                return m_appearTime;\n            }\n\n            constexpr static double VisibilityTime = 4.0;\n\n        protected:\n            static std::mutex& getMutex();\n\n            double m_appearTime = 0;\n            ImColor m_color;\n        };\n\n    }\n\n    template<typename T>\n    class Toast : public impl::ToastBase {\n    public:\n        using impl::ToastBase::ToastBase;\n\n        template<typename ...Args>\n        static void open(Args && ... args) {\n            TaskManager::doLater([=] {\n                auto toast = std::make_unique<T>(args...);\n                getQueuedToasts().emplace_back(std::move(toast));\n            });\n        }\n    };\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/ui/view.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <imgui.h>\n#include <hex/ui/imgui_imhex_extensions.h>\n\n#include <hex/api/shortcut_manager.hpp>\n#include <hex/api/localization_manager.hpp>\n\n#include <hex/providers/provider_data.hpp>\n\n#include <hex/helpers/scaling.hpp>\n\n#include <map>\n#include <string>\n\nnamespace hex {\n\n    class View {\n        explicit View(UnlocalizedString unlocalizedName, const char *icon);\n    public:\n        virtual ~View() = default;\n\n        /**\n         * @brief Draws the view\n         * @note Do not override this method. Override drawContent() instead\n         */\n        virtual void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) = 0;\n\n        /**\n         * @brief Draws the content of the view\n         */\n        virtual void drawContent() = 0;\n\n        /**\n         * @brief Draws content that should always be visible, even if the view is not open\n         */\n        virtual void drawAlwaysVisibleContent() { }\n\n        /**\n         * @brief Whether or not the view window should be drawn\n         * @return True if the view window should be drawn, false otherwise\n         */\n        [[nodiscard]] virtual bool shouldDraw() const;\n\n        /**\n         * @brief Whether or not the entire view should be processed\n         * If this returns false, the view will not be drawn and no shortcuts will be handled. This includes things\n         * drawn in the drawAlwaysVisibleContent() function.\n         * @return True if the view should be processed, false otherwise\n         */\n        [[nodiscard]] virtual bool shouldProcess() const;\n\n        /**\n         * @brief Whether or not the view should have an entry in the view menu\n         * @return True if the view should have an entry in the view menu, false otherwise\n         */\n        [[nodiscard]] virtual bool hasViewMenuItemEntry() const;\n\n        /**\n         * @brief Gets the minimum size of the view window\n         * @return The minimum size of the view window\n         */\n        [[nodiscard]] virtual ImVec2 getMinSize() const;\n\n        /**\n         * @brief Gets the maximum size of the view window\n         * @return The maximum size of the view window\n         */\n        [[nodiscard]] virtual ImVec2 getMaxSize() const;\n\n        /**\n         * @brief Gets additional window flags for the view window\n         * @return Additional window flags for the view window\n         */\n        [[nodiscard]] virtual ImGuiWindowFlags getWindowFlags() const;\n\n        /**\n         * @brief Returns a view whose menu items should be additionally visible when this view is focused\n         * @return\n         */\n        [[nodiscard]] virtual View* getMenuItemInheritView() const { return nullptr; }\n\n\n        [[nodiscard]] const char *getIcon() const { return m_icon; }\n        [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const;\n        [[nodiscard]] std::string getName() const;\n\n        [[nodiscard]] virtual bool shouldDefaultFocus() const { return false; }\n        [[nodiscard]] virtual bool shouldStoreWindowState() const { return true; }\n\n        [[nodiscard]] bool &getWindowOpenState();\n        [[nodiscard]] const bool &getWindowOpenState() const;\n\n        [[nodiscard]] bool isFocused() const { return m_focused; }\n\n        [[nodiscard]] static std::string toWindowName(const UnlocalizedString &unlocalizedName);\n        [[nodiscard]] static const View* getLastFocusedView();\n        static void discardNavigationRequests();\n\n        void bringToFront();\n\n        [[nodiscard]] bool didWindowJustOpen();\n        void setWindowJustOpened(bool state);\n\n        [[nodiscard]] bool didWindowJustClose();\n        void setWindowJustClosed(bool state);\n\n        void trackViewState();\n        void setFocused(bool focused);\n\n    protected:\n        /**\n         * @brief Called when this view is opened (i.e. made visible).\n         */\n        virtual void onOpen() {}\n\n        /**\n         * @brief Called when this view is closed (i.e. made invisible).\n         */\n        virtual void onClose() {}\n\n    public:\n        class Window;\n        class Special;\n        class Floating;\n        class Scrolling;\n        class Modal;\n        class FullScreen;\n\n    private:\n        UnlocalizedString m_unlocalizedViewName;\n        bool m_windowOpen = false, m_prevWindowOpen = false;\n        std::map<Shortcut, ShortcutManager::ShortcutEntry> m_shortcuts;\n        bool m_windowJustOpened = false, m_windowJustClosed = false;\n        const char *m_icon;\n        bool m_focused = false;\n\n        friend class ShortcutManager;\n    };\n\n\n    /**\n     * @brief A view that draws a regular window. This should be the default for most views\n     */\n    class View::Window : public View {\n    public:\n        explicit Window(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {}\n        [[nodiscard]] ImGuiWindow *getFocusedSubWindow() const { return m_focusedSubWindow; }\n\n        /**\n         * @brief Draws help text for the view\n         */\n        virtual void drawHelpText() = 0;\n\n        void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) override;\n\n        [[nodiscard]] virtual bool allowScroll() const {\n            return false;\n        }\n\n    private:\n        void handleFocusRestoration();\n\n    private:\n        ImGuiWindow *m_focusedSubWindow{nullptr};\n    };\n\n    /**\n     * @brief A view that doesn't handle any window creation and just draws its content.\n     * This should be used if you intend to draw your own special window\n     */\n    class View::Special : public View {\n    public:\n        explicit Special(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName), \"\") {}\n\n        void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;\n    };\n\n    /**\n     * @brief A view that draws a floating window. These are the same as regular windows but cannot be docked\n     */\n    class View::Floating : public View::Window {\n    public:\n        explicit Floating(UnlocalizedString unlocalizedName, const char *icon) : Window(std::move(unlocalizedName), icon) {}\n\n        void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;\n\n        [[nodiscard]] bool shouldStoreWindowState() const override { return false; }\n    };\n\n    /**\n     * @brief A view that draws all its content at once without any scrolling being done by the window itself\n     */\n    class View::Scrolling : public View::Window {\n    public:\n        explicit Scrolling(UnlocalizedString unlocalizedName, const char *icon) : Window(std::move(unlocalizedName), icon) {}\n\n        void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;\n\n        [[nodiscard]] bool allowScroll() const final {\n            return true;\n        }\n    };\n\n    /**\n     * @brief A view that draws a modal window. The window will always be drawn on top and will block input to other windows\n     */\n    class View::Modal : public View {\n    public:\n        explicit Modal(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {}\n\n        void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;\n\n        [[nodiscard]] virtual bool hasCloseButton() const { return true; }\n        [[nodiscard]] bool shouldStoreWindowState() const override { return false; }\n    };\n\n    class View::FullScreen : public View {\n    public:\n        explicit FullScreen() : View(\"FullScreen\", \"\") {}\n\n        void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;\n    };\n\n}\n"
  },
  {
    "path": "lib/libimhex/include/hex/ui/widgets.hpp",
    "content": "#pragma once\n\n#include <hex.hpp>\n\n#include <vector>\n#include <string>\n#include <atomic>\n\n#include <hex/api/task_manager.hpp>\n\n#include <imgui.h>\n#include <hex/ui/imgui_imhex_extensions.h>\n\nnamespace hex::ui {\n\n    template<typename T>\n    class SearchableWidget {\n    public:\n        SearchableWidget(const std::function<bool(const std::string&, const T&)> &comparator) : m_comparator(comparator) {\n\n        }\n\n        const std::vector<const T*> &draw(const auto &entries) {\n            if (m_filteredEntries.empty() && m_searchBuffer.empty()) {\n                for (auto &entry : entries)\n                    m_filteredEntries.push_back(&entry);\n            }\n\n            if (ImGui::InputText(\"##search\", m_searchBuffer)) {\n                m_pendingUpdate = true;\n            }\n\n            if (m_pendingUpdate && !m_updateTask.isRunning()) {\n                m_pendingUpdate = false;\n                m_filteredEntries.clear();\n                m_filteredEntries.reserve(entries.size());\n\n                m_updateTask = TaskManager::createBackgroundTask(\"Searching\", [this, &entries, searchBuffer = m_searchBuffer](Task&) {\n                    for (auto &entry : entries) {\n                        if (searchBuffer.empty() || m_comparator(searchBuffer, entry))\n                            m_filteredEntries.push_back(&entry);\n                    }\n                });\n\n            }\n\n            return m_filteredEntries;\n        }\n\n        void reset() {\n            m_filteredEntries.clear();\n        }\n    private:\n        std::atomic<bool> m_pendingUpdate = false;\n        TaskHolder m_updateTask;\n\n        std::string m_searchBuffer;\n        std::vector<const T*> m_filteredEntries;\n        std::function<bool(const std::string&, const T&)> m_comparator;\n    };\n\n}"
  },
  {
    "path": "lib/libimhex/include/hex.cppm",
    "content": "module;\n\n#include <cmath>\n#include <map>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <exception>\n#include <algorithm>\n#include <locale>\n#include <array>\n#include <filesystem>\n#include <functional>\n#include <memory>\n#include <list>\n#include <atomic>\n#include <ranges>\n#include <fstream>\n#include <thread>\n#include <future>\n\n#include <fmt/format.h>\n#include <nlohmann/json.hpp>\n#include <wolv/io/file.hpp>\n\n#include <imgui.h>\n#include <imgui_internal.h>\n#include <hex/ui/imgui_imhex_extensions.h>\n\n#include <hex/helpers/auto_reset.hpp>\n#include <hex/api/event_manager.hpp>\n#include <hex/providers/provider.hpp>\n#include <hex/providers/provider_data.hpp>\n#include <hex/data_processor/node.hpp>\n#include <hex/data_processor/link.hpp>\n#include <hex/data_processor/attribute.hpp>\n#include <pl/pattern_language.hpp>\n\nexport module hex;\n\n#define HEX_MODULE_EXPORT\n\n#include <hex/api/imhex_api/bookmarks.hpp>\n#include <hex/api/imhex_api/hex_editor.hpp>\n#include <hex/api/imhex_api/fonts.hpp>\n#include <hex/api/imhex_api/messaging.hpp>\n#include <hex/api/imhex_api/provider.hpp>\n#include <hex/api/imhex_api/system.hpp>\n\n#include <hex/api/task_manager.hpp>\n#include <hex/api/achievement_manager.hpp>\n#include <hex/api/layout_manager.hpp>\n#include <hex/api/localization_manager.hpp>\n#include <hex/api/plugin_manager.hpp>\n#include <hex/api/shortcut_manager.hpp>\n#include <hex/api/theme_manager.hpp>\n#include <hex/api/tutorial_manager.hpp>\n#include <hex/api/workspace_manager.hpp>\n"
  },
  {
    "path": "lib/libimhex/include/hex.hpp",
    "content": "#pragma once\n\n#include <hex/helpers/types.hpp>\n\n#if defined(HEX_MODULE_EXPORT)\n    #define EXPORT_MODULE export\n#else\n    #define EXPORT_MODULE\n#endif"
  },
  {
    "path": "lib/libimhex/source/api/achievement_manager.cpp",
    "content": "#include <hex/api/achievement_manager.hpp>\n#include <hex/api/events/events_interaction.hpp>\n\n#include <hex/helpers/auto_reset.hpp>\n#include <hex/helpers/default_paths.hpp>\n\n#include <nlohmann/json.hpp>\n\n#if defined(OS_WEB)\n    #include <emscripten.h>\n#endif\n\nnamespace hex {\n\n    static AutoReset<std::unordered_map<UnlocalizedString, std::unordered_map<UnlocalizedString, std::unique_ptr<Achievement>>>> s_achievements;\n    const std::unordered_map<UnlocalizedString, std::unordered_map<UnlocalizedString, std::unique_ptr<Achievement>>> &AchievementManager::getAchievements() {\n        return *s_achievements;\n    }\n\n    static AutoReset<std::unordered_map<UnlocalizedString, std::list<AchievementManager::AchievementNode>>> s_nodeCategoryStorage;\n    std::unordered_map<UnlocalizedString, std::list<AchievementManager::AchievementNode>>& getAchievementNodesMutable(bool rebuild) {\n        if (!s_nodeCategoryStorage->empty() || !rebuild)\n            return s_nodeCategoryStorage;\n\n        s_nodeCategoryStorage->clear();\n\n        // Add all achievements to the node storage\n        for (auto &[categoryName, achievements] : AchievementManager::getAchievements()) {\n            auto &nodes = (*s_nodeCategoryStorage)[categoryName];\n\n            for (auto &[achievementName, achievement] : achievements) {\n                nodes.emplace_back(achievement.get());\n            }\n        }\n\n        return s_nodeCategoryStorage;\n    }\n\n    const std::unordered_map<UnlocalizedString, std::list<AchievementManager::AchievementNode>>& AchievementManager::getAchievementNodes(bool rebuild) {\n        return getAchievementNodesMutable(rebuild);\n    }\n\n    static AutoReset<std::unordered_map<UnlocalizedString, std::vector<AchievementManager::AchievementNode*>>> s_startNodes;\n    const std::unordered_map<UnlocalizedString, std::vector<AchievementManager::AchievementNode*>>& AchievementManager::getAchievementStartNodes(bool rebuild) {\n\n        if (!s_startNodes->empty() || !rebuild)\n            return s_startNodes;\n\n        auto &nodeCategoryStorage = getAchievementNodesMutable(rebuild);\n\n        s_startNodes->clear();\n\n        // Add all parents and children to the nodes\n        for (auto &[categoryName, achievements] : nodeCategoryStorage) {\n            for (auto &achievementNode : achievements) {\n                for (auto &requirement : achievementNode.achievement->getRequirements()) {\n                    for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) {\n                        auto iter = std::ranges::find_if(requirementAchievements, [&requirement](auto &node) {\n                            return node.achievement->getUnlocalizedName() == requirement;\n                        });\n\n                        if (iter != requirementAchievements.end()) {\n                            achievementNode.parents.emplace_back(&*iter);\n                            iter->children.emplace_back(&achievementNode);\n                        }\n                    }\n                }\n\n                for (auto &requirement : achievementNode.achievement->getVisibilityRequirements()) {\n                    for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) {\n                        auto iter = std::ranges::find_if(requirementAchievements, [&requirement](auto &node) {\n                            return node.achievement->getUnlocalizedName() == requirement;\n                        });\n\n                        if (iter != requirementAchievements.end()) {\n                            achievementNode.visibilityParents.emplace_back(&*iter);\n                        }\n                    }\n                }\n            }\n        }\n\n        for (auto &[categoryName, achievements] : nodeCategoryStorage) {\n            for (auto &achievementNode : achievements) {\n                if (!achievementNode.hasParents()) {\n                    (*s_startNodes)[categoryName].emplace_back(&achievementNode);\n                }\n\n                for (const auto &parent : achievementNode.parents) {\n                    if (parent->achievement->getUnlocalizedCategory() != achievementNode.achievement->getUnlocalizedCategory())\n                        (*s_startNodes)[categoryName].emplace_back(&achievementNode);\n                }\n            }\n        }\n\n        return s_startNodes;\n    }\n\n    void AchievementManager::unlockAchievement(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName)  {\n        auto &categories = getAchievements();\n\n        auto categoryIter = categories.find(unlocalizedCategory);\n        if (categoryIter == categories.end()) {\n            return;\n        }\n\n        auto &[categoryName, achievements] = *categoryIter;\n\n        const auto achievementIter = achievements.find(unlocalizedName);\n        if (achievementIter == achievements.end()) {\n            return;\n        }\n\n        const auto &nodes = getAchievementNodes();\n        if (!nodes.contains(categoryName))\n            return;\n\n        for (const auto &node : nodes.at(categoryName)) {\n            auto &achievement = node.achievement;\n\n            if (achievement->getUnlocalizedCategory() != unlocalizedCategory) {\n                continue;\n            }\n            if (achievement->getUnlocalizedName() != unlocalizedName) {\n                continue;\n            }\n\n            if (node.achievement->isUnlocked()) {\n                return;\n            }\n\n            for (const auto &requirement : node.parents) {\n                if (!requirement->achievement->isUnlocked()) {\n                    return;\n                }\n            }\n\n            achievement->setUnlocked(true);\n\n            if (achievement->isUnlocked())\n                EventAchievementUnlocked::post(*achievement);\n\n            return;\n        }\n    }\n\n    void AchievementManager::clearTemporary() {\n        auto &categories = *s_achievements;\n        for (auto &[categoryName, achievements] : categories) {\n            std::erase_if(achievements, [](auto &data) {\n                auto &[achievementName, achievement] = data;\n                return achievement->isTemporary();\n            });\n        }\n\n        std::erase_if(categories, [](auto &data) {\n            auto &[categoryName, achievements] = data;\n            return achievements.empty();\n        });\n\n        s_startNodes->clear();\n        s_nodeCategoryStorage->clear();\n    }\n\n    std::pair<u32, u32> AchievementManager::getProgress() {\n        u32 unlocked = 0;\n        u32 total = 0;\n\n        for (auto &[categoryName, achievements] : getAchievements()) {\n            for (auto &[achievementName, achievement] : achievements) {\n                total += 1;\n                if (achievement->isUnlocked()) {\n                    unlocked += 1;\n                }\n            }\n        }\n\n        return { unlocked, total };\n    }\n\n    void AchievementManager::achievementAdded() {\n        s_startNodes->clear();\n        s_nodeCategoryStorage->clear();\n    }\n\n    Achievement &AchievementManager::addAchievementImpl(std::unique_ptr<Achievement> &&newAchievement) {\n        const auto &category = newAchievement->getUnlocalizedCategory();\n        const auto &name = newAchievement->getUnlocalizedName();\n\n        auto [categoryIter, categoryInserted] = s_achievements->insert({ category, std::unordered_map<UnlocalizedString, std::unique_ptr<Achievement>>{} });\n        auto &[categoryKey, achievements] = *categoryIter;\n\n        auto [achievementIter, achievementInserted] = achievements.emplace(name, std::move(newAchievement));\n        auto &[achievementKey, achievement] = *achievementIter;\n\n        achievementAdded();\n\n        return *achievement;\n    }\n\n\n    constexpr static auto AchievementsFile = \"achievements.json\";\n    bool AchievementManager::s_initialized = false;\n\n    void AchievementManager::loadProgress() {\n        if (s_initialized)\n            return;\n        for (const auto &directory : paths::Config.read()) {\n            auto path = directory / AchievementsFile;\n\n            if (!wolv::io::fs::exists(path)) {\n                continue;\n            }\n\n            wolv::io::File file(path, wolv::io::File::Mode::Read);\n\n            if (!file.isValid()) {\n                continue;\n            }\n\n            try {\n                #if defined(OS_WEB)\n                    auto data = (char *) MAIN_THREAD_EM_ASM_INT({\n                        let data = localStorage.getItem(\"achievements\");\n                        return data ? stringToNewUTF8(data) : null;\n                    });\n                #else\n                    auto data = file.readString();\n                #endif\n\n                auto json = nlohmann::json::parse(data);\n\n                for (const auto &[categoryName, achievements] : getAchievements()) {\n                    for (const auto &[achievementName, achievement] : achievements) {\n                        try {\n                            const auto &progress = json[categoryName][achievementName];\n                            if (progress.is_null())\n                                continue;\n\n                            achievement->setProgress(progress);\n                        } catch (const std::exception &e) {\n                            log::warn(\"Failed to load achievement progress for '{}::{}': {}\", categoryName.get(), achievementName.get(), e.what());\n                        }\n                    }\n                }\n                \n                s_initialized = true;\n            } catch (const std::exception &e) {\n                log::error(\"Failed to load achievements: {}\", e.what());\n            }\n\n        }\n    }\n\n    void AchievementManager::storeProgress() {\n        if (!s_initialized)\n            loadProgress();\n        nlohmann::json json;\n        for (const auto &[categoryName, achievements] : getAchievements()) {\n            json[categoryName] = nlohmann::json::object();\n\n            for (const auto &[achievementName, achievement] : achievements) {\n                json[categoryName][achievementName] = achievement->getProgress();\n            }\n        }\n\n        if (json.empty()) return;\n\n        #if defined(OS_WEB)\n            auto data = json.dump();\n            MAIN_THREAD_EM_ASM({\n                localStorage.setItem(\"achievements\", UTF8ToString($0));\n            }, data.c_str());\n        #else\n            for (const auto &directory : paths::Config.write()) {\n                auto path = directory / AchievementsFile;\n\n                wolv::io::File file(path, wolv::io::File::Mode::Create);\n                if (!file.isValid())\n                    continue;\n\n                file.writeString(json.dump(4));\n                break;\n            }\n        #endif\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/api/content_registry.cpp",
    "content": "#include <hex/api/content_registry/background_services.hpp>\n#include <hex/api/content_registry/command_palette.hpp>\n#include <hex/api/content_registry/communication_interface.hpp>\n#include <hex/api/content_registry/data_formatter.hpp>\n#include <hex/api/content_registry/data_information.hpp>\n#include <hex/api/content_registry/data_inspector.hpp>\n#include <hex/api/content_registry/data_processor.hpp>\n#include <hex/api/content_registry/diffing.hpp>\n#include <hex/api/content_registry/disassemblers.hpp>\n#include <hex/api/content_registry/experiments.hpp>\n#include <hex/api/content_registry/file_type_handler.hpp>\n#include <hex/api/content_registry/hashes.hpp>\n#include <hex/api/content_registry/hex_editor.hpp>\n#include <hex/api/content_registry/user_interface.hpp>\n#include <hex/api/content_registry/pattern_language.hpp>\n#include <hex/api/content_registry/provider.hpp>\n#include <hex/api/content_registry/reports.hpp>\n#include <hex/api/content_registry/settings.hpp>\n#include <hex/api/content_registry/tools.hpp>\n#include <hex/api/content_registry/views.hpp>\n\n#include <hex/api/shortcut_manager.hpp>\n#include <hex/api/events/requests_provider.hpp>\n\n#include <hex/helpers/fs.hpp>\n#include <hex/helpers/logger.hpp>\n#include <hex/helpers/auto_reset.hpp>\n#include <hex/helpers/default_paths.hpp>\n#include <hex/helpers/utils.hpp>\n\n#include <hex/ui/view.hpp>\n#include <hex/data_processor/node.hpp>\n\n#include <hex/providers/provider.hpp>\n\n#include <algorithm>\n#include <filesystem>\n#include <jthread.hpp>\n#include <hex/api/events/requests_interaction.hpp>\n\n#if defined(OS_WEB)\n#include <emscripten.h>\n#endif\n\n#include <hex/api/task_manager.hpp>\n#include <nlohmann/json.hpp>\n\n#include <wolv/io/file.hpp>\n#include <wolv/utils/string.hpp>\n\nnamespace hex {\n\n    namespace ContentRegistry::Settings {\n\n        [[maybe_unused]] constexpr auto SettingsFile = \"settings.json\";\n\n        namespace impl {\n\n            struct OnChange {\n                u64 id;\n                OnChangeCallback callback;\n            };\n\n            struct OnSave {\n                u64 id;\n                OnSaveCallback callback;\n            };\n\n            static AutoReset<std::map<std::string, std::map<std::string, std::vector<OnChange>>>> s_onChangeCallbacks;\n            static AutoReset<std::vector<OnSave>> s_onSaveCallbacks;\n\n            static void runAllOnChangeCallbacks() {\n                for (const auto &[category, rest] : *impl::s_onChangeCallbacks) {\n                    for (const auto &[name, callbacks] : rest) {\n                        for (const auto &[id, callback] : callbacks) {\n                            try {\n                                callback(getSetting(category, name, {}));\n                            } catch (const std::exception &e) {\n                                log::error(\"Failed to load setting [{} / {}]: {}\", category, name, e.what());\n                            }\n                        }\n                    }\n                }\n            }\n\n            void runOnChangeHandlers(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &value) {\n                if (auto categoryIt = s_onChangeCallbacks->find(unlocalizedCategory); categoryIt != s_onChangeCallbacks->end()) {\n                    if (auto nameIt = categoryIt->second.find(unlocalizedName); nameIt != categoryIt->second.end()) {\n                        for (const auto &[id, callback] : nameIt->second) {\n                            try {\n                                callback(value);\n                            } catch (const nlohmann::json::exception &e) {\n                                log::error(\"Failed to run onChange handler for setting {}/{}: {}\", unlocalizedCategory.get(), unlocalizedName.get(), e.what());\n                            }\n                        }\n                    }\n                }\n            }\n\n            static AutoReset<nlohmann::json> s_settings;\n            const nlohmann::json& getSettingsData() {\n                return s_settings;\n            }\n\n            nlohmann::json& getSetting(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue) {\n                auto &settings = *s_settings;\n\n                if (!settings.contains(unlocalizedCategory))\n                    settings[unlocalizedCategory] = {};\n\n                if (!settings[unlocalizedCategory].contains(unlocalizedName))\n                    settings[unlocalizedCategory][unlocalizedName] = defaultValue;\n\n                if (settings[unlocalizedCategory][unlocalizedName].is_null())\n                    settings[unlocalizedCategory][unlocalizedName] = defaultValue;\n\n                return settings[unlocalizedCategory][unlocalizedName];\n            }\n\n            #if defined(OS_WEB)\n\n                void load() {\n                    char *data = (char *) MAIN_THREAD_EM_ASM_INT({\n                        let data = localStorage.getItem(\"config\");\n                        return data ? stringToNewUTF8(data) : null;\n                    });\n\n                    if (data == nullptr) {\n                        store();\n                    } else {\n                        s_settings = nlohmann::json::parse(data);\n                    }\n\n                    runAllOnChangeCallbacks();\n                }\n\n                void store() {\n                    if (!s_settings.isValid())\n                        return;\n\n                    const auto &settingsData = *s_settings;\n\n                    // During a crash settings can be empty, causing them to be overwritten.\n                    if (settingsData.empty()) {\n                        return;\n                    }\n\n                    const auto result = settingsData.dump(4);\n                    if (result.empty()) {\n                        return;\n                    }\n\n                    MAIN_THREAD_EM_ASM({\n                        localStorage.setItem(\"config\", UTF8ToString($0));\n                    }, result.c_str());\n                }\n\n                void clear() {\n                    MAIN_THREAD_EM_ASM({\n                        localStorage.removeItem(\"config\");\n                    });\n                }\n\n            #else\n\n                void load() {\n                    bool loaded = false;\n                    for (const auto &dir : paths::Config.read()) {\n                        wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Read);\n\n                        if (file.isValid()) {\n                            s_settings = nlohmann::json::parse(file.readString());\n                            loaded = true;\n                            break;\n                        }\n                    }\n\n                    if (!loaded)\n                        store();\n\n                    runAllOnChangeCallbacks();\n                }\n\n                void store() {\n                    thread_local bool isRunningCallbacks = false;\n\n                    if (isRunningCallbacks)\n                        return;\n                    isRunningCallbacks = true;\n                    for (const auto &[id, callback] : *s_onSaveCallbacks) {\n                        try {\n                            callback();\n                        } catch (const std::exception &e) {\n                            log::error(\"Failed to run onSave handler for setting: {}\", e.what());\n                        }\n                    }\n                    isRunningCallbacks = false;\n\n                    if (!s_settings.isValid())\n                        return;\n\n                    const auto &settingsData = *s_settings;\n\n                    // During a crash settings can be empty, causing them to be overwritten.\n                    if (s_settings->empty()) {\n                        return;\n                    }\n\n                    const auto result = settingsData.dump(4);\n                    if (result.empty()) {\n                        return;\n                    }\n                    for (const auto &dir : paths::Config.write()) {\n                        wolv::io::File file(dir / SettingsFile, wolv::io::File::Mode::Create);\n\n                        if (file.isValid()) {\n                            file.writeString(result);\n                            break;\n                        }\n                    }\n                }\n\n                void clear() {\n                    for (const auto &dir : paths::Config.write()) {\n                        wolv::io::fs::remove(dir / SettingsFile);\n                    }\n                }\n            #endif\n\n            template<typename T>\n            static T* insertOrGetEntry(std::vector<T> &vector, const UnlocalizedString &unlocalizedName) {\n                T *foundEntry = nullptr;\n                for (auto &entry : vector) {\n                    if (entry.unlocalizedName == unlocalizedName) {\n                        foundEntry = &entry;\n                        break;\n                    }\n                }\n\n                if (foundEntry == nullptr) {\n                    if (unlocalizedName.empty())\n                        foundEntry = &*vector.emplace(vector.begin(), unlocalizedName);\n                    else\n                        foundEntry = &vector.emplace_back(unlocalizedName);\n                }\n\n                return foundEntry;\n            }\n\n            static AutoReset<std::vector<Category>> s_categories;\n            const std::vector<Category>& getSettings() {\n                return *s_categories;\n            }\n\n            Widgets::Widget* add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedSubCategory, const UnlocalizedString &unlocalizedName, std::unique_ptr<Widgets::Widget> &&widget) {\n                const auto category    = insertOrGetEntry(*s_categories,           unlocalizedCategory);\n                const auto subCategory = insertOrGetEntry(category->subCategories, unlocalizedSubCategory);\n                const auto entry       = insertOrGetEntry(subCategory->entries,    unlocalizedName);\n\n                entry->widget = std::move(widget);\n                if (entry->widget != nullptr) {\n                    onChange(unlocalizedCategory, unlocalizedName, [widget = entry->widget.get(), unlocalizedCategory, unlocalizedName](const SettingsValue &) {\n                        auto defaultValue = widget->store();\n                        try {\n                            widget->load(ContentRegistry::Settings::impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue));\n                            widget->onChanged();\n                        } catch (const std::exception &e) {\n                            log::error(\"Failed to load setting [{} / {}]: {}\", unlocalizedCategory.get(), unlocalizedName.get(), e.what());\n                            ContentRegistry::Settings::impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue) = defaultValue;\n                        }\n                    });\n\n                    runOnChangeHandlers(unlocalizedCategory, unlocalizedName, getSetting(unlocalizedCategory, unlocalizedName, entry->widget->store()));\n                }\n\n                return entry->widget.get();\n            }\n\n            void printSettingReadError(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json::exception& e) {\n                hex::log::error(\"Failed to read setting {}/{}: {}\", unlocalizedCategory.get(), unlocalizedName.get(), e.what());\n            }\n\n        }\n\n        void setCategoryDescription(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedDescription) {\n            const auto category = insertOrGetEntry(*impl::s_categories, unlocalizedCategory);\n\n            category->unlocalizedDescription = unlocalizedDescription;\n        }\n\n        u64 onChange(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const OnChangeCallback &callback) {\n            static u64 id = 1;\n            (*impl::s_onChangeCallbacks)[unlocalizedCategory][unlocalizedName].emplace_back(id, callback);\n\n            auto result = id;\n            id += 1;\n\n            return result;\n        }\n\n        void removeOnChangeHandler(u64 id) {\n            bool done = false;\n            auto categoryIt = impl::s_onChangeCallbacks->begin();\n            for (; categoryIt != impl::s_onChangeCallbacks->end(); ++categoryIt) {\n                auto nameIt = categoryIt->second.begin();\n                for (; nameIt != categoryIt->second.end(); ++nameIt) {\n                    done = std::erase_if(nameIt->second, [id](const impl::OnChange &entry) {\n                        return entry.id == id;\n                    }) > 0;\n\n                    if (done) break;\n                }\n\n                if (done) {\n                    if (nameIt->second.empty())\n                        categoryIt->second.erase(nameIt);\n\n                    break;\n                }\n            }\n\n            if (done) {\n                if (categoryIt->second.empty())\n                    impl::s_onChangeCallbacks->erase(categoryIt);\n            }\n        }\n\n        u64 onSave(const OnSaveCallback &callback) {\n            static u64 id = 1;\n            impl::s_onSaveCallbacks->emplace_back(id, callback);\n\n            auto result = id;\n            id += 1;\n\n            return result;\n        }\n\n        namespace Widgets {\n\n            bool Checkbox::draw(const std::string &name) {\n                return ImGui::Checkbox(name.c_str(), &m_value);\n            }\n\n            void Checkbox::load(const nlohmann::json &data) {\n                if (data.is_number()) {\n                    m_value = data.get<int>() != 0;\n                } else if (data.is_boolean()) {\n                    m_value = data.get<bool>();\n                } else {\n                    log::warn(\"Invalid data type loaded from settings for checkbox!\");\n                }\n            }\n\n            nlohmann::json Checkbox::store() {\n                return m_value;\n            }\n\n\n            bool SliderInteger::draw(const std::string &name) {\n                return ImGui::SliderInt(name.c_str(), &m_value, m_min, m_max);\n            }\n\n            void SliderInteger::load(const nlohmann::json &data) {\n                if (data.is_number_integer()) {\n                    m_value = data.get<int>();\n                } else {\n                    log::warn(\"Invalid data type loaded from settings for slider!\");\n                }\n            }\n\n            nlohmann::json SliderInteger::store() {\n                return m_value;\n            }\n\n\n            bool SliderFloat::draw(const std::string &name) {\n                return ImGui::SliderFloat(name.c_str(), &m_value, m_min, m_max);\n            }\n\n            void SliderFloat::load(const nlohmann::json &data) {\n                if (data.is_number()) {\n                    m_value = data.get<float>();\n                } else {\n                    log::warn(\"Invalid data type loaded from settings for slider!\");\n                }\n            }\n\n            nlohmann::json SliderFloat::store() {\n                return m_value;\n            }\n\n\n            bool SliderDataSize::draw(const std::string &name) {\n                return ImGuiExt::SliderBytes(name.c_str(), &m_value, m_min, m_max, m_stepSize);\n            }\n\n            void SliderDataSize::load(const nlohmann::json &data) {\n                if (data.is_number_integer()) {\n                    m_value = data.get<u64>();\n                } else {\n                    log::warn(\"Invalid data type loaded from settings for slider!\");\n                }\n            }\n\n            nlohmann::json SliderDataSize::store() {\n                return m_value;\n            }\n\n\n            ColorPicker::ColorPicker(ImColor defaultColor, ImGuiColorEditFlags flags) {\n                m_defaultValue = m_value = {\n                    defaultColor.Value.x,\n                    defaultColor.Value.y,\n                    defaultColor.Value.z,\n                    defaultColor.Value.w\n                };\n                m_flags = flags;\n            }\n\n            static bool areColorsEquals(const std::array<float, 4> &a, const std::array<float, 4> &b) {\n                return std::abs(a[0] - b[0]) < 0.005f &&\n                       std::abs(a[1] - b[1]) < 0.005f &&\n                       std::abs(a[2] - b[2]) < 0.005f &&\n                       std::abs(a[3] - b[3]) < 0.005f;\n            }\n\n            bool ColorPicker::draw(const std::string &name) {\n                ImGui::PushID(name.c_str());\n                auto result = ImGui::ColorEdit4(\"##color_picker\", m_value.data(), ImGuiColorEditFlags_NoInputs | m_flags);\n\n                ImGui::SameLine();\n\n                ImGui::BeginDisabled(areColorsEquals(m_value, m_defaultValue));\n                if (ImGuiExt::DimmedButton(\"\\xee\\xab\\xa2\", ImGui::GetStyle().FramePadding * 2 + ImVec2(ImGui::GetTextLineHeight(), ImGui::GetTextLineHeight()))) {\n                    m_value = m_defaultValue;\n                    result = true;\n                }\n                ImGui::EndDisabled();\n\n                ImGui::SameLine();\n\n                ImGui::TextUnformatted(name.c_str());\n\n                ImGui::PopID();\n\n                return result;\n            }\n\n            void ColorPicker::load(const nlohmann::json &data) {\n                if (data.is_number()) {\n                    const ImColor color(data.get<u32>());\n                    m_value = { color.Value.x, color.Value.y, color.Value.z, color.Value.w };\n                } else {\n                    log::warn(\"Invalid data type loaded from settings for color picker!\");\n                }\n            }\n\n            nlohmann::json ColorPicker::store() {\n                const ImColor color(m_value[0], m_value[1], m_value[2], m_value[3]);\n\n                return static_cast<ImU32>(color);\n            }\n\n            ImColor ColorPicker::getColor() const {\n                return { m_value[0], m_value[1], m_value[2], m_value[3] };\n            }\n\n\n            bool DropDown::draw(const std::string &name) {\n                auto preview = \"\";\n                if (static_cast<size_t>(m_value) < m_items.size())\n                    preview = m_items[m_value].get().c_str();\n\n                bool changed = false;\n                if (ImGui::BeginCombo(name.c_str(), Lang(preview))) {\n\n                    int index = 0;\n                    for (const auto &item : m_items) {\n                        const bool selected = index == m_value;\n\n                        if (ImGui::Selectable(Lang(item), selected)) {\n                            m_value = index;\n                            changed = true;\n                        }\n\n                        if (selected)\n                            ImGui::SetItemDefaultFocus();\n\n                        index += 1;\n                    }\n\n                    ImGui::EndCombo();\n                }\n\n                return changed;\n            }\n\n            void DropDown::load(const nlohmann::json &data) {\n                m_value = 0;\n\n                int defaultItemIndex = 0;\n\n                int index = 0;\n                for (const auto &item : m_settingsValues) {\n                    if (item == m_defaultItem)\n                        defaultItemIndex = index;\n\n                    if (item == data) {\n                        m_value = index;\n                        return;\n                    }\n\n                    index += 1;\n                }\n\n                m_value = defaultItemIndex;\n            }\n\n            nlohmann::json DropDown::store() {\n                if (m_value == -1)\n                    return m_defaultItem;\n                if (static_cast<size_t>(m_value) >= m_items.size())\n                    return m_defaultItem;\n\n                return m_settingsValues[m_value];\n            }\n\n            const nlohmann::json& DropDown::getValue() const {\n                return m_settingsValues[m_value];\n            }\n\n\n            bool TextBox::draw(const std::string &name) {\n                return ImGui::InputText(name.c_str(), m_value);\n            }\n\n            void TextBox::load(const nlohmann::json &data) {\n                if (data.is_string()) {\n                    m_value = data.get<std::string>();\n                } else {\n                    log::warn(\"Invalid data type loaded from settings for text box!\");\n                }\n            }\n\n            nlohmann::json TextBox::store() {\n                return m_value;\n            }\n\n\n            bool FilePicker::draw(const std::string &name) {\n                bool changed = false;\n\n                auto pathString = wolv::util::toUTF8String(m_path);\n                if (ImGui::InputText(\"##font_path\", pathString)) {\n                    changed = true;\n                }\n\n                ImGui::SameLine();\n\n                if (ImGuiExt::IconButton(\"...\", ImGui::GetStyleColorVec4(ImGuiCol_Text))) {\n                    changed = fs::openFileBrowser(fs::DialogMode::Open, { { \"TTF Font\", \"ttf\" }, { \"OTF Font\", \"otf\" } },\n                                               [&](const std::fs::path &path) {\n                                                   pathString = wolv::util::toUTF8String(path);\n                                               });\n                }\n\n                ImGui::SameLine();\n\n                ImGuiExt::TextFormatted(\"{}\", name);\n\n                if (changed) {\n                    m_path = pathString;\n                }\n\n                return changed;\n            }\n\n            void FilePicker::load(const nlohmann::json &data) {\n                if (data.is_string()) {\n                    m_path = data.get<std::fs::path>();\n                } else {\n                    log::warn(\"Invalid data type loaded from settings for file picker!\");\n                }\n            }\n\n            nlohmann::json FilePicker::store() {\n                return m_path;\n            }\n\n            bool Label::draw(const std::string& name) {\n                ImGui::NewLine();\n                ImGui::TextUnformatted(name.c_str());\n\n                return false;\n            }\n\n            bool Spacer::draw(const std::string& name) {\n                std::ignore = name;\n                ImGui::NewLine();\n\n                return false;\n            }\n\n        }\n\n    }\n\n\n    namespace ContentRegistry::CommandPalette {\n\n        namespace impl {\n\n            static AutoReset<std::vector<Entry>> s_entries;\n            const std::vector<Entry>& getEntries() {\n                return *s_entries;\n            }\n\n            static AutoReset<std::vector<Handler>> s_handlers;\n            const std::vector<Handler>& getHandlers() {\n                return *s_handlers;\n            }\n\n            static AutoReset<std::optional<ContentDisplay>> s_displayedContent;\n            std::optional<ContentDisplay>& getDisplayedContent() {\n                return *s_displayedContent;\n            }\n\n        }\n\n        void add(Type type, const std::string &command, const UnlocalizedString &unlocalizedDescription, const impl::DisplayCallback &displayCallback, const impl::ExecuteCallback &executeCallback) {\n            log::debug(\"Registered new command palette command: {}\", command);\n\n            impl::s_entries->push_back(impl::Entry { .type=type, .command=command, .unlocalizedDescription=unlocalizedDescription, .displayCallback=displayCallback, .executeCallback=executeCallback });\n        }\n\n        void addHandler(Type type, const std::string &command, const impl::QueryCallback &queryCallback, const impl::DisplayCallback &displayCallback) {\n            log::debug(\"Registered new command palette command handler: {}\", command);\n\n            impl::s_handlers->push_back(impl::Handler { .type=type, .command=command, .queryCallback=queryCallback, .displayCallback=displayCallback });\n        }\n\n        void setDisplayedContent(const impl::ContentDisplayCallback &displayCallback) {\n            impl::s_displayedContent = impl::ContentDisplay { .showSearchBox=true, .callback=displayCallback };\n        }\n\n        void openWithContent(const impl::ContentDisplayCallback &displayCallback) {\n            RequestOpenCommandPalette::post();\n            impl::s_displayedContent = impl::ContentDisplay { .showSearchBox=false, .callback=displayCallback };\n        }\n\n    }\n\n\n    namespace ContentRegistry::PatternLanguage {\n\n        namespace impl {\n\n            static AutoReset<std::map<std::string, Visualizer>> s_visualizers;\n            const std::map<std::string, Visualizer>& getVisualizers() {\n                return *s_visualizers;\n            }\n\n            static AutoReset<std::map<std::string, Visualizer>> s_inlineVisualizers;\n            const std::map<std::string, Visualizer>& getInlineVisualizers() {\n                return *s_inlineVisualizers;\n            }\n\n            static AutoReset<std::map<std::string, pl::api::PragmaHandler>> s_pragmas;\n            const std::map<std::string, pl::api::PragmaHandler>& getPragmas() {\n                return *s_pragmas;\n            }\n\n            static AutoReset<std::vector<FunctionDefinition>> s_functions;\n            const std::vector<FunctionDefinition>& getFunctions() {\n                return *s_functions;\n            }\n\n            static AutoReset<std::vector<TypeDefinition>> s_types;\n            const std::vector<TypeDefinition>& getTypes() {\n                return *s_types;\n            }\n\n        }\n\n        static std::string getFunctionName(const pl::api::Namespace &ns, const std::string &name) {\n            std::string functionName;\n            for (auto &scope : ns)\n                functionName += scope + \"::\";\n\n            functionName += name;\n\n            return functionName;\n        }\n\n        pl::PatternLanguage& getRuntime() {\n            static PerProvider<pl::PatternLanguage> runtime;\n            AT_FIRST_TIME {\n                runtime.setOnCreateCallback([](prv::Provider *provider, pl::PatternLanguage &runtime) {\n                    configureRuntime(runtime, provider);\n                });\n            };\n\n            return *runtime;\n        }\n\n        std::mutex& getRuntimeLock() {\n            static std::mutex runtimeLock;\n\n            return runtimeLock;\n        }\n\n        void configureRuntime(pl::PatternLanguage &runtime, prv::Provider *provider) {\n            runtime.reset();\n\n            if (provider != nullptr) {\n                runtime.setDataSource(provider->getBaseAddress(), provider->getActualSize(),\n                                      [provider](u64 offset, u8 *buffer, size_t size) {\n                                          provider->read(offset, buffer, size);\n                                      },\n                                      [provider](u64 offset, const u8 *buffer, size_t size) {\n                                          if (provider->isWritable())\n                                              provider->write(offset, buffer, size);\n                                      }\n                );\n            }\n\n            runtime.setIncludePaths(paths::PatternsInclude.read() | paths::Patterns.read());\n\n            for (const auto &[ns, name, paramCount, callback, dangerous] : impl::getFunctions()) {\n                if (dangerous)\n                    runtime.addDangerousFunction(ns, name, paramCount, callback);\n                else\n                    runtime.addFunction(ns, name, paramCount, callback);\n            }\n\n            for (const auto &[ns, name, paramCount, callback] : impl::getTypes()) {\n                runtime.addType(ns, name, paramCount, callback);\n            }\n\n            for (const auto &[name, callback] : impl::getPragmas()) {\n                runtime.addPragma(name, callback);\n            }\n\n            runtime.addDefine(\"__IMHEX__\");\n            runtime.addDefine(\"__IMHEX_VERSION__\", ImHexApi::System::getImHexVersion().get());\n        }\n\n        void addPragma(const std::string &name, const pl::api::PragmaHandler &handler) {\n            log::debug(\"Registered new pattern language pragma: {}\", name);\n\n            (*impl::s_pragmas)[name] = handler;\n        }\n\n        void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func) {\n            log::debug(\"Registered new pattern language function: {}\", getFunctionName(ns, name));\n\n            impl::s_functions->push_back({\n                ns, name,\n                parameterCount, func,\n                false\n            });\n        }\n\n        void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func) {\n            log::debug(\"Registered new dangerous pattern language function: {}\", getFunctionName(ns, name));\n\n            impl::s_functions->push_back({\n                ns, name,\n                parameterCount, func,\n                true\n            });\n        }\n\n        void addType(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::TypeCallback &func) {\n            log::debug(\"Registered new pattern language type: {}\", getFunctionName(ns, name));\n\n            impl::s_types->push_back({\n                ns, name,\n                parameterCount, func\n            });\n        }\n\n\n        void addVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount) {\n            log::debug(\"Registered new pattern visualizer function: {}\", name);\n            (*impl::s_visualizers)[name] = impl::Visualizer { .parameterCount=parameterCount, .callback=function };\n        }\n\n        void addInlineVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount) {\n            log::debug(\"Registered new inline pattern visualizer function: {}\", name);\n            (*impl::s_inlineVisualizers)[name] = impl::Visualizer { .parameterCount=parameterCount, .callback=function };\n        }\n\n    }\n\n\n    namespace ContentRegistry::Views {\n\n        namespace impl {\n\n            static AutoReset<std::map<UnlocalizedString, std::unique_ptr<View>>> s_views;\n            const std::map<UnlocalizedString, std::unique_ptr<View>>& getEntries() {\n                return *s_views;\n            }\n\n            void add(std::unique_ptr<View> &&view) {\n                log::debug(\"Registered new view: {}\", view->getUnlocalizedName().get());\n\n                s_views->emplace(view->getUnlocalizedName(), std::move(view));\n            }\n\n            static AutoReset<std::unique_ptr<View>> s_fullscreenView;\n            const std::unique_ptr<View>& getFullScreenView() {\n                return *s_fullscreenView;\n            }\n\n            void setFullScreenView(std::unique_ptr<View> &&view) {\n                s_fullscreenView = std::move(view);\n            }\n\n        }\n\n        View* getViewByName(const UnlocalizedString &unlocalizedName) {\n            auto &views = *impl::s_views;\n\n            if (views.contains(unlocalizedName))\n                return views[unlocalizedName].get();\n            else\n                return nullptr;\n        }\n\n        View* getFocusedView() {\n            for (const auto &[unlocalizedName, view] : *impl::s_views) {\n                if (view->isFocused())\n                    return view.get();\n            }\n\n            return nullptr;\n        }\n\n    }\n\n    namespace ContentRegistry::Tools {\n\n        namespace impl {\n\n            static AutoReset<std::vector<Entry>> s_tools;\n            const std::vector<Entry>& getEntries() {\n                return *s_tools;\n            }\n\n        }\n\n        void add(const UnlocalizedString &unlocalizedName, const char *icon, const impl::Callback &function) {\n            log::debug(\"Registered new tool: {}\", unlocalizedName.get());\n\n            impl::s_tools->emplace_back(impl::Entry { .unlocalizedName=unlocalizedName, .icon=icon, .function=function });\n        }\n\n    }\n\n    namespace ContentRegistry::DataInspector {\n\n        namespace impl {\n\n            static AutoReset<std::vector<Entry>> s_entries;\n            const std::vector<Entry>& getEntries() {\n                return *s_entries;\n            }\n\n        }\n\n        namespace EditWidget {\n            std::optional<std::vector<u8>> TextInput::draw(std::string &value, std::endian endian) {\n                if (ImGui::InputText(\"##InspectorLineEditing\", value,\n                                 ImGuiInputTextFlags_EnterReturnsTrue |\n                                 ImGuiInputTextFlags_AutoSelectAll)) {\n                    return getBytes(value, endian);\n                }\n\n                return std::nullopt;\n            }\n        }\n\n        void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction) {\n            log::debug(\"Registered new data inspector format: {}\", unlocalizedName.get());\n\n            impl::s_entries->push_back({ unlocalizedName, requiredSize, requiredSize, std::move(displayGeneratorFunction), std::move(editingFunction) });\n        }\n\n        void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, size_t maxSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction) {\n            log::debug(\"Registered new data inspector format: {}\", unlocalizedName.get());\n\n            impl::s_entries->push_back({ unlocalizedName, requiredSize, maxSize, std::move(displayGeneratorFunction), std::move(editingFunction) });\n        }\n\n        void drawMenuItems(const std::function<void()> &function) {\n            if (ImGui::BeginPopup(\"##DataInspectorRowContextMenu\")) {\n                function();\n                ImGui::Separator();\n                ImGui::EndPopup();\n            }\n        }\n\n    }\n\n    namespace ContentRegistry::DataProcessor {\n\n        namespace impl {\n\n            static AutoReset<std::vector<Entry>> s_nodes;\n            const std::vector<Entry>& getEntries() {\n                return *s_nodes;\n            }\n\n            void add(const Entry &entry) {\n                log::debug(\"Registered new data processor node type: [{}]: {}\", entry.unlocalizedCategory.get(), entry.unlocalizedName.get());\n\n                s_nodes->push_back(entry);\n            }\n\n        }\n\n        void addSeparator() {\n            impl::s_nodes->push_back({ \"\", \"\", [] { return nullptr; } });\n        }\n\n    }\n\n    namespace ContentRegistry::UserInterface {\n\n        namespace impl {\n\n            static AutoReset<std::multimap<u32, MainMenuItem>> s_mainMenuItems;\n            const std::multimap<u32, MainMenuItem>& getMainMenuItems() {\n                return *s_mainMenuItems;\n            }\n\n            static AutoReset<std::multimap<u32, MenuItem>> s_menuItems;\n            const std::multimap<u32, MenuItem>& getMenuItems() {\n                return *s_menuItems;\n            }\n\n            static AutoReset<std::vector<MenuItem*>> s_toolbarMenuItems;\n            const std::vector<MenuItem*>& getToolbarMenuItems() {\n                return s_toolbarMenuItems;\n            }\n\n\n            std::multimap<u32, MenuItem>& getMenuItemsMutable() {\n                return *s_menuItems;\n            }\n\n            static AutoReset<std::vector<DrawCallback>> s_welcomeScreenEntries;\n            const std::vector<DrawCallback>& getWelcomeScreenEntries() {\n                return *s_welcomeScreenEntries;\n            }\n\n            static AutoReset<std::vector<DrawCallback>> s_footerItems;\n            const std::vector<DrawCallback>& getFooterItems() {\n                return *s_footerItems;\n            }\n\n            static AutoReset<std::vector<DrawCallback>> s_toolbarItems;\n            const std::vector<DrawCallback>& getToolbarItems() {\n                return *s_toolbarItems;\n            }\n\n            static AutoReset<std::vector<SidebarItem>> s_sidebarItems;\n            const std::vector<SidebarItem>& getSidebarItems() {\n                return *s_sidebarItems;\n            }\n\n            static AutoReset<std::vector<TitleBarButton>> s_titlebarButtons;\n            const std::vector<TitleBarButton>& getTitlebarButtons() {\n                return *s_titlebarButtons;\n            }\n\n            static AutoReset<std::vector<WelcomeScreenQuickSettingsToggle>> s_welcomeScreenQuickSettingsToggles;\n            const std::vector<WelcomeScreenQuickSettingsToggle>& getWelcomeScreenQuickSettingsToggles() {\n                return *s_welcomeScreenQuickSettingsToggles;\n            }\n\n        }\n\n        void registerMainMenuItem(const UnlocalizedString &unlocalizedName, u32 priority) {\n            log::debug(\"Registered new main menu item: {}\", unlocalizedName.get());\n\n            impl::s_mainMenuItems->insert({ priority, { unlocalizedName } });\n        }\n\n        void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, const impl::SelectedCallback &selectedCallback, View *view) {\n            addMenuItem(unlocalizedMainMenuNames, \"\", priority, shortcut, function, enabledCallback, selectedCallback, view);\n        }\n\n        void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, const Icon &icon, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) {\n            addMenuItem(unlocalizedMainMenuNames, icon, priority, shortcut, function, enabledCallback, []{ return false; }, view);\n        }\n\n        void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) {\n            addMenuItem(unlocalizedMainMenuNames, \"\", priority, shortcut, function, enabledCallback, []{ return false; }, view);\n        }\n\n        void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, const Icon &icon, u32 priority, Shortcut shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, const impl::SelectedCallback &selectedCallback, View *view) {\n            log::debug(\"Added new menu item to menu {} with priority {}\", unlocalizedMainMenuNames[0].get(), priority);\n\n            Icon coloredIcon = icon;\n            if (coloredIcon.color == 0x00)\n                coloredIcon.color = ImGuiCustomCol_ToolbarGray;\n\n            impl::s_menuItems->insert({\n                priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon=coloredIcon, .shortcut=shortcut, .view=view, .callback=function, .enabledCallback=enabledCallback, .selectedCallback=selectedCallback, .toolbarIndex=-1 }\n            });\n\n            if (shortcut != Shortcut::None) {\n                if (view != nullptr && !shortcut.isLocal()) {\n                    shortcut += CurrentView;\n                }\n\n                if (shortcut.isLocal() && view != nullptr)\n                    ShortcutManager::addShortcut(view, shortcut, unlocalizedMainMenuNames, function, enabledCallback);\n                else\n                    ShortcutManager::addGlobalShortcut(shortcut, unlocalizedMainMenuNames, function, enabledCallback);\n            }\n        }\n\n        void addMenuItemSubMenu(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view, bool showOnWelcomeScreen) {\n            addMenuItemSubMenu(std::move(unlocalizedMainMenuNames), \"\", priority, function, enabledCallback, view, showOnWelcomeScreen);\n        }\n\n        void addMenuItemSubMenu(std::vector<UnlocalizedString> unlocalizedMainMenuNames, const char *icon, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view, bool showOnWelcomeScreen) {\n            log::debug(\"Added new menu item sub menu to menu {} with priority {}\", unlocalizedMainMenuNames[0].get(), priority);\n\n            unlocalizedMainMenuNames.emplace_back(impl::SubMenuValue);\n            impl::s_menuItems->insert({\n                priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon=icon, .shortcut=showOnWelcomeScreen ? Shortcut({ ShowOnWelcomeScreen }) : Shortcut::None, .view=view, .callback=function, .enabledCallback=enabledCallback, .selectedCallback=[]{ return false; }, .toolbarIndex=-1 }\n            });\n        }\n\n        void addMenuItemSeparator(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, View *view) {\n            unlocalizedMainMenuNames.emplace_back(impl::SeparatorValue);\n            impl::s_menuItems->insert({\n                priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon=\"\", .shortcut=Shortcut::None, .view=view, .callback=[]{}, .enabledCallback=[]{ return true; }, .selectedCallback=[]{ return false; }, .toolbarIndex=-1 }\n            });\n        }\n\n        void addTaskBarMenuItem(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback) {\n            log::debug(\"Added new taskbar menu item to menu {} \", unlocalizedMainMenuNames[0].get());\n\n            unlocalizedMainMenuNames.insert(unlocalizedMainMenuNames.begin(), impl::TaskBarMenuValue);\n            impl::s_menuItems->insert({\n                priority, impl::MenuItem { .unlocalizedNames=unlocalizedMainMenuNames, .icon=\"\", .shortcut=Shortcut::None, .view=nullptr, .callback=function, .enabledCallback=enabledCallback, .selectedCallback=[]{ return false; }, .toolbarIndex=-1 }\n            });\n        }\n\n        void addWelcomeScreenEntry(const impl::DrawCallback &function) {\n            impl::s_welcomeScreenEntries->push_back(function);\n        }\n\n        void addFooterItem(const impl::DrawCallback &function) {\n            impl::s_footerItems->push_back(function);\n        }\n\n        void addToolbarItem(const impl::DrawCallback &function) {\n            impl::s_toolbarItems->push_back(function);\n        }\n\n        void addMenuItemToToolbar(const std::vector<UnlocalizedString>& unlocalizedNames, ImGuiCustomCol color) {\n            const auto maxIndex = std::ranges::max_element(impl::getMenuItems(), [](const auto &a, const auto &b) {\n                return a.second.toolbarIndex < b.second.toolbarIndex;\n            })->second.toolbarIndex;\n\n            for (auto &[priority, menuItem] : *impl::s_menuItems) {\n                if (menuItem.unlocalizedNames == unlocalizedNames) {\n                    menuItem.toolbarIndex = maxIndex + 1;\n                    menuItem.icon.color = color;\n                    updateToolbarItems();\n\n                    break;\n                }\n            }\n        }\n\n        struct MenuItemSorter {\n            bool operator()(const auto *a, const auto *b) const {\n                return a->toolbarIndex < b->toolbarIndex;\n            }\n        };\n\n        void updateToolbarItems() {\n            std::set<impl::MenuItem*, MenuItemSorter> menuItems;\n\n            for (auto &[priority, menuItem] : impl::getMenuItemsMutable()) {\n                if (menuItem.toolbarIndex != -1) {\n                    menuItems.insert(&menuItem);\n                }\n            }\n\n            impl::s_toolbarMenuItems->clear();\n            for (auto menuItem : menuItems) {\n                impl::s_toolbarMenuItems->push_back(menuItem);\n            }\n        }\n\n\n\n        void addSidebarItem(const std::string &icon, const impl::DrawCallback &function, const impl::EnabledCallback &enabledCallback) {\n            impl::s_sidebarItems->push_back({ icon, function, enabledCallback });\n        }\n\n        void addTitleBarButton(const std::string &icon, ImGuiCustomCol color, const UnlocalizedString &unlocalizedTooltip, const impl::ClickCallback &function) {\n            impl::s_titlebarButtons->push_back({ icon, color, unlocalizedTooltip, function });\n        }\n\n        void addWelcomeScreenQuickSettingsToggle(const std::string &icon, const UnlocalizedString &unlocalizedTooltip, bool defaultState, const impl::ToggleCallback &function) {\n            impl::s_welcomeScreenQuickSettingsToggles->push_back({ icon, icon, unlocalizedTooltip, function, defaultState });\n        }\n\n        void addWelcomeScreenQuickSettingsToggle(const std::string &onIcon, const std::string &offIcon, const UnlocalizedString &unlocalizedTooltip, bool defaultState, const impl::ToggleCallback &function) {\n            impl::s_welcomeScreenQuickSettingsToggles->push_back({ onIcon, offIcon, unlocalizedTooltip, function, defaultState });\n\n        }\n\n    }\n\n    \n\n        namespace ContentRegistry::Provider::impl {\n\n            void add(const std::string &typeName, ProviderCreationFunction creationFunction) {\n                (void)RequestCreateProvider::subscribe([expectedName = typeName, creationFunction](const std::string &name, bool skipLoadInterface, bool selectProvider, std::shared_ptr<prv::Provider> *provider) {\n                    if (name != expectedName) return;\n\n                    auto newProvider = creationFunction();\n\n                    if (provider != nullptr) {\n                        *provider = newProvider;\n                        ImHexApi::Provider::add(std::move(newProvider), skipLoadInterface, selectProvider);\n                    }\n                });\n            }\n\n            static AutoReset<std::vector<Entry>> s_providerNames;\n            const std::vector<Entry>& getEntries() {\n                return *s_providerNames;\n            }\n\n            void addProviderName(const UnlocalizedString &unlocalizedName, const char *icon) {\n                log::debug(\"Registered new provider: {}\", unlocalizedName.get());\n\n                s_providerNames->emplace_back(unlocalizedName, icon);\n            }\n\n        }\n\n\n    \n\n    namespace ContentRegistry::DataFormatter {\n\n        namespace impl {\n\n            static AutoReset<std::vector<ExportMenuEntry>> s_exportMenuEntries;\n            const std::vector<ExportMenuEntry>& getExportMenuEntries() {\n                return *s_exportMenuEntries;\n            }\n\n            static AutoReset<std::vector<FindExporterEntry>> s_findExportEntries;\n            const std::vector<FindExporterEntry>& getFindExporterEntries() {\n                return *s_findExportEntries;\n            }\n\n        }\n\n        void addExportMenuEntry(const UnlocalizedString &unlocalizedName, const impl::Callback &callback) {\n            log::debug(\"Registered new data formatter: {}\", unlocalizedName.get());\n\n            impl::s_exportMenuEntries->push_back({ unlocalizedName, callback });\n        }\n\n        void addFindExportFormatter(const UnlocalizedString &unlocalizedName, const std::string &fileExtension, const impl::FindExporterCallback &callback) {\n            log::debug(\"Registered new export formatter: {}\", unlocalizedName.get());\n\n            impl::s_findExportEntries->push_back({ unlocalizedName, fileExtension, callback });\n        }\n\n    }\n\n    namespace ContentRegistry::FileTypeHandler {\n\n        namespace impl {\n\n            static AutoReset<std::vector<Entry>> s_entries;\n            const std::vector<Entry>& getEntries() {\n                return *s_entries;\n            }\n\n        }\n\n        void add(const std::vector<std::string> &extensions, const impl::Callback &callback) {\n            for (const auto &extension : extensions)\n                log::debug(\"Registered new data handler for extensions: {}\", extension);\n\n            impl::s_entries->push_back({ extensions, callback });\n        }\n\n    }\n\n    namespace ContentRegistry::HexEditor {\n\n        int DataVisualizer::DefaultTextInputFlags() {\n            return ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_AlwaysOverwrite;\n        }\n\n        bool DataVisualizer::drawDefaultScalarEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const {\n            struct UserData {\n                u8 *data;\n                i32 maxChars;\n\n                bool editingDone;\n            };\n\n            UserData userData = {\n                .data = data,\n                .maxChars = this->getMaxCharsPerCell(),\n\n                .editingDone = false\n            };\n\n            ImGui::PushID(reinterpret_cast<void*>(address));\n            ImGuiExt::InputScalarCallback(\"##editing_input\", dataType, data, format, flags | DefaultTextInputFlags() | ImGuiInputTextFlags_CallbackEdit, [](ImGuiInputTextCallbackData *data) -> int {\n                auto &userData = *static_cast<UserData*>(data->UserData);\n\n                if (data->CursorPos >= userData.maxChars)\n                    userData.editingDone = true;\n\n                data->Buf[userData.maxChars] = 0x00;\n\n                return 0;\n            }, &userData);\n            ImGui::PopID();\n\n            return userData.editingDone || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Escape);\n        }\n\n        bool DataVisualizer::drawDefaultTextEditingTextBox(u64 address, std::string &data, ImGuiInputTextFlags flags) const {\n            struct UserData {\n                std::string *data;\n                i32 maxChars;\n\n                bool editingDone;\n            };\n\n            UserData userData = {\n                .data = &data,\n                .maxChars = this->getMaxCharsPerCell(),\n\n                .editingDone = false\n            };\n\n            ImGui::PushID(reinterpret_cast<void*>(address));\n            ImGui::InputText(\"##editing_input\", data.data(), data.size() + 1, flags | DefaultTextInputFlags() | ImGuiInputTextFlags_CallbackEdit, [](ImGuiInputTextCallbackData *data) -> int {\n                auto &userData = *static_cast<UserData*>(data->UserData);\n\n                userData.data->resize(data->BufSize);\n\n                if (data->BufTextLen >= userData.maxChars)\n                    userData.editingDone = true;\n\n                data->Buf[userData.maxChars] = 0x00;\n\n                return 0;\n            }, &userData);\n            ImGui::PopID();\n\n            return userData.editingDone || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Escape);\n        }\n\n        namespace impl {\n\n            static AutoReset<std::vector<std::shared_ptr<DataVisualizer>>> s_visualizers;\n            const std::vector<std::shared_ptr<DataVisualizer>>& getVisualizers() {\n                return *s_visualizers;\n            }\n\n            static AutoReset<std::vector<std::shared_ptr<MiniMapVisualizer>>> s_miniMapVisualizers;\n            const std::vector<std::shared_ptr<MiniMapVisualizer>>& getMiniMapVisualizers() {\n                return *s_miniMapVisualizers;\n            }\n\n            void addDataVisualizer(std::shared_ptr<DataVisualizer> &&visualizer) {\n                s_visualizers->emplace_back(std::move(visualizer));\n\n            }\n\n        }\n\n        std::shared_ptr<DataVisualizer> getVisualizerByName(const UnlocalizedString &unlocalizedName) {\n            for (const auto &visualizer : impl::getVisualizers()) {\n                if (visualizer->getUnlocalizedName() == unlocalizedName)\n                    return visualizer;\n            }\n\n            return nullptr;\n        }\n\n        void addMiniMapVisualizer(UnlocalizedString unlocalizedName, MiniMapVisualizer::Callback callback) {\n            impl::s_miniMapVisualizers->emplace_back(std::make_shared<MiniMapVisualizer>(std::move(unlocalizedName), std::move(callback)));\n        }\n\n    }\n\n\n    namespace ContentRegistry::Diffing::impl {\n\n        static AutoReset<std::vector<std::unique_ptr<Algorithm>>> s_algorithms;\n        const std::vector<std::unique_ptr<Algorithm>>& getAlgorithms() {\n            return *s_algorithms;\n        }\n\n        void addAlgorithm(std::unique_ptr<Algorithm> &&hash) {\n            s_algorithms->emplace_back(std::move(hash));\n        }\n\n    }\n\n\n    namespace ContentRegistry::Hashes::impl {\n\n        static AutoReset<std::vector<std::unique_ptr<Hash>>> s_hashes;\n        const std::vector<std::unique_ptr<Hash>>& getHashes() {\n            return *s_hashes;\n        }\n\n        void add(std::unique_ptr<Hash> &&hash) {\n            s_hashes->emplace_back(std::move(hash));\n        }\n\n    }\n\n\n    namespace ContentRegistry::BackgroundServices {\n\n        namespace impl {\n\n            class Service {\n            public:\n                Service(UnlocalizedString unlocalizedName, std::jthread thread) : m_unlocalizedName(std::move(unlocalizedName)), m_thread(std::move(thread)) { }\n                Service(const Service&) = delete;\n                Service(Service &&) = default;\n                ~Service() {\n                    m_thread.request_stop();\n                    if (m_thread.joinable())\n                        m_thread.join();\n                }\n\n                Service& operator=(const Service&) = delete;\n                Service& operator=(Service &&) = default;\n\n                [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const {\n                    return m_unlocalizedName;\n                }\n\n                [[nodiscard]] const std::jthread& getThread() const {\n                    return m_thread;\n                }\n\n            private:\n                UnlocalizedString m_unlocalizedName;\n                std::jthread m_thread;\n            };\n\n            static AutoReset<std::vector<Service>> s_services;\n            const std::vector<Service>& getServices() {\n                return *s_services;\n            }\n\n            void stopServices() {\n                s_services->clear();\n            }\n\n        }\n\n        void registerService(const UnlocalizedString &unlocalizedName, const impl::Callback &callback) {\n            log::debug(\"Registered new background service: {}\", unlocalizedName.get());\n\n            impl::s_services->emplace_back(\n                unlocalizedName,\n                std::jthread([=](const std::stop_token &stopToken){\n                    TaskManager::setCurrentThreadName(Lang(unlocalizedName));\n                    while (!stopToken.stop_requested()) {\n                        callback();\n                        std::this_thread::sleep_for(std::chrono::milliseconds(50));\n                    }\n                })\n            );\n        }\n\n    }\n\n    namespace ContentRegistry::CommunicationInterface {\n\n        namespace impl {\n\n            static AutoReset<std::map<std::string, NetworkCallback>> s_endpoints;\n            const std::map<std::string, NetworkCallback>& getNetworkEndpoints() {\n                return *s_endpoints;\n            }\n\n        }\n\n        void registerNetworkEndpoint(const std::string &endpoint, const impl::NetworkCallback &callback) {\n            log::debug(\"Registered new network endpoint: {}\", endpoint);\n\n            impl::s_endpoints->insert({ endpoint, callback });\n        }\n\n    }\n\n    namespace ContentRegistry::MCP {\n\n        namespace impl {\n\n            std::unique_ptr<mcp::Server>& getMcpServerInstance() {\n                static std::unique_ptr<mcp::Server> server;\n\n                if (server == nullptr)\n                    server = std::make_unique<mcp::Server>();\n\n                return server;\n            }\n\n            static bool s_mcpEnabled = false;\n            void setEnabled(bool enabled) {\n                s_mcpEnabled = enabled;\n            }\n\n        }\n\n        bool isEnabled() {\n            return impl::s_mcpEnabled;\n        }\n\n        bool isConnected() {\n            return impl::getMcpServerInstance()->isConnected();\n        }\n\n        void registerTool(std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json &params)> function) {\n            impl::getMcpServerInstance()->addPrimitive(\"tools\", capabilities, function);\n        }\n\n    }\n\n    namespace ContentRegistry::Experiments {\n\n        namespace impl {\n\n            static AutoReset<std::map<std::string, Experiment>> s_experiments;\n            const std::map<std::string, Experiment>& getExperiments() {\n                return *s_experiments;\n            }\n\n        }\n\n        void addExperiment(const std::string &experimentName, const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) {\n            auto &experiments = *impl::s_experiments;\n\n            if (experiments.contains(experimentName)) {\n                log::error(\"Experiment with name '{}' already exists!\", experimentName);\n                return;\n            }\n\n            experiments[experimentName] = impl::Experiment {\n                .unlocalizedName = unlocalizedName,\n                .unlocalizedDescription = unlocalizedDescription,\n                .enabled = false\n            };\n        }\n\n        void enableExperiement(const std::string &experimentName, bool enabled) {\n            auto &experiments = *impl::s_experiments;\n\n            if (!experiments.contains(experimentName)) {\n                log::error(\"Experiment with name '{}' does not exist!\", experimentName);\n                return;\n            }\n\n            experiments[experimentName].enabled = enabled;\n        }\n\n        [[nodiscard]] bool isExperimentEnabled(const std::string &experimentName) {\n            auto &experiments = *impl::s_experiments;\n\n            if (!experiments.contains(experimentName)) {\n                log::error(\"Experiment with name '{}' does not exist!\", experimentName);\n                return false;\n            }\n\n            return experiments[experimentName].enabled;\n        }\n\n    }\n\n    namespace ContentRegistry::Reports {\n\n        namespace impl {\n\n            static AutoReset<std::vector<ReportGenerator>> s_generators;\n            const std::vector<ReportGenerator>& getGenerators() {\n                return *s_generators;\n            }\n\n        }\n\n        void addReportProvider(impl::Callback callback) {\n            impl::s_generators->push_back(impl::ReportGenerator { std::move(callback ) });\n        }\n\n    }\n\n    namespace ContentRegistry::DataInformation {\n\n        void InformationSection::load(const nlohmann::json &data) {\n            m_enabled = data.value<bool>(\"enabled\", true);\n        }\n        [[nodiscard]] nlohmann::json InformationSection::store() {\n            nlohmann::json data;\n            data[\"enabled\"] = m_enabled.load();\n\n            return data;\n        }\n\n        namespace impl {\n\n            static AutoReset<std::vector<CreateCallback>> s_informationSectionConstructors;\n            const std::vector<CreateCallback>& getInformationSectionConstructors() {\n                return *s_informationSectionConstructors;\n            }\n\n            void addInformationSectionCreator(const CreateCallback &callback) {\n                s_informationSectionConstructors->emplace_back(callback);\n            }\n\n        }\n\n    }\n    \n\n    namespace ContentRegistry::Disassemblers::impl {\n\n        static AutoReset<std::map<std::string, impl::CreatorFunction>> s_architectures;\n\n        void addArchitectureCreator(impl::CreatorFunction function) {\n            const auto arch = function();\n            (*s_architectures)[arch->getName()] = std::move(function);\n        }\n\n        const std::map<std::string, impl::CreatorFunction>& getArchitectures() {\n            return *s_architectures;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/api/event_manager.cpp",
    "content": "#include <algorithm>\n#include <hex/api/event_manager.hpp>\n\nnamespace hex {\n\n    std::multimap<void *, EventManager::EventList::iterator>& EventManager::getTokenStore() {\n        static std::multimap<void *, EventManager::EventList::iterator> tokenStore;\n\n        return tokenStore;\n    }\n\n    EventManager::EventList& EventManager::getEvents() {\n        static EventManager::EventList events;\n\n        return events;\n    }\n\n    std::recursive_mutex& EventManager::getEventMutex() {\n        static std::recursive_mutex mutex;\n\n        return mutex;\n    }\n\n\n    bool EventManager::isAlreadyRegistered(void *token, impl::EventId id) {\n        if (getTokenStore().contains(token)) {\n            auto&& [begin, end] = getTokenStore().equal_range(token);\n\n            return std::any_of(begin, end, [&](auto &item) {\n                return item.second->first == id;\n            });\n        }\n\n        return false;\n    }\n\n    void EventManager::unsubscribe(void *token, impl::EventId id) {\n        auto &tokenStore = getTokenStore();\n        auto iter = std::ranges::find_if(tokenStore, [&](auto &item) {\n            return item.first == token && item.second->first == id;\n        });\n\n        if (iter != tokenStore.end()) {\n            getEvents().erase(iter->second);\n            tokenStore.erase(iter);\n        }\n    }\n\n\n\n}"
  },
  {
    "path": "lib/libimhex/source/api/imhex_api.cpp",
    "content": "#include <hex/api/imhex_api/bookmarks.hpp>\n#include <hex/api/imhex_api/hex_editor.hpp>\n#include <hex/api/imhex_api/fonts.hpp>\n#include <hex/api/imhex_api/messaging.hpp>\n#include <hex/api/imhex_api/provider.hpp>\n#include <hex/api/imhex_api/system.hpp>\n\n#include <hex/api/events/events_provider.hpp>\n#include <hex/api/events/events_lifecycle.hpp>\n#include <hex/api/events/events_gui.hpp>\n#include <hex/api/events/requests_interaction.hpp>\n#include <hex/api/events/requests_lifecycle.hpp>\n#include <hex/api/events/requests_provider.hpp>\n#include <hex/api/events/requests_gui.hpp>\n#include <hex/api/events/events_interaction.hpp>\n\n#include <hex/api/task_manager.hpp>\n#include <hex/helpers/fmt.hpp>\n#include <hex/helpers/utils.hpp>\n#include <hex/helpers/auto_reset.hpp>\n#include <hex/providers/provider_data.hpp>\n#include <hex/providers/provider.hpp>\n\n#include <wolv/utils/string.hpp>\n\n#include <utility>\n#include <numeric>\n\n#include <imgui.h>\n#include <imgui_internal.h>\n#include <set>\n#include <algorithm>\n#include <GLFW/glfw3.h>\n#include <hex/api_urls.hpp>\n#include <hex/helpers/http_requests.hpp>\n\n#include <hex/helpers/utils_macos.hpp>\n#include <nlohmann/json.hpp>\n\n#if defined(OS_WINDOWS)\n    #include <windows.h>\n    #include <DSRole.h>\n#else\n    #include <sys/utsname.h>\n    #include <unistd.h>\n#endif\n\n#if defined(OS_WEB)\n    #include <emscripten.h>\n#elif defined(OS_MACOS)\n    extern \"C\" {\n        void macosRegisterFont(const unsigned char *data, size_t size);\n    }\n#endif\n\nnamespace hex {\n\n\n    namespace ImHexApi::HexEditor {\n\n        Highlighting::Highlighting(Region region, color_t color)\n            : m_region(region), m_color(color) {\n        }\n\n        Tooltip::Tooltip(Region region, std::string value, color_t color) : m_region(region), m_value(std::move(value)), m_color(color) {\n\n        }\n\n        namespace impl {\n\n            static AutoReset<std::map<u32, Highlighting>> s_backgroundHighlights;\n            const std::map<u32, Highlighting>& getBackgroundHighlights() {\n                return *s_backgroundHighlights;\n            }\n\n            static AutoReset<std::map<u32, HighlightingFunction>> s_backgroundHighlightingFunctions;\n            const std::map<u32, HighlightingFunction>& getBackgroundHighlightingFunctions() {\n                return *s_backgroundHighlightingFunctions;\n            }\n\n            static AutoReset<std::map<u32, Highlighting>> s_foregroundHighlights;\n            const std::map<u32, Highlighting>& getForegroundHighlights() {\n                return *s_foregroundHighlights;\n            }\n\n            static AutoReset<std::map<u32, HighlightingFunction>> s_foregroundHighlightingFunctions;\n            const std::map<u32, HighlightingFunction>& getForegroundHighlightingFunctions() {\n                return *s_foregroundHighlightingFunctions;\n            }\n\n            static AutoReset<std::map<u32, Tooltip>> s_tooltips;\n            const std::map<u32, Tooltip>& getTooltips() {\n                return *s_tooltips;\n            }\n\n            static AutoReset<std::map<u32, TooltipFunction>> s_tooltipFunctions;\n            const std::map<u32, TooltipFunction>& getTooltipFunctions() {\n                return *s_tooltipFunctions;\n            }\n\n            static AutoReset<std::map<u32, HoveringFunction>> s_hoveringFunctions;\n            const std::map<u32, HoveringFunction>& getHoveringFunctions() {\n                return *s_hoveringFunctions;\n            }\n\n            static AutoReset<std::optional<ProviderRegion>> s_currentSelection;\n            void setCurrentSelection(const std::optional<ProviderRegion> &region) {\n                if (region == Region::Invalid()) {\n                    clearSelection();\n                } else {\n                    *s_currentSelection = region;\n                }\n            }\n\n            static PerProvider<std::optional<Region>> s_hoveredRegion;\n            void setHoveredRegion(const prv::Provider *provider, const Region &region) {\n                if (provider == nullptr)\n                    return;\n                \n                if (region == Region::Invalid())\n                    s_hoveredRegion.get(provider).reset();\n                else\n                    s_hoveredRegion.get(provider) = region;\n            }\n\n        }\n\n        u32 addBackgroundHighlight(const Region &region, color_t color) {\n            static u32 id = 0;\n\n            id++;\n\n            impl::s_backgroundHighlights->insert({\n                id, Highlighting { region, color }\n            });\n\n            TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });\n\n            return id;\n        }\n\n        void removeBackgroundHighlight(u32 id) {\n            impl::s_backgroundHighlights->erase(id);\n\n            TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });\n        }\n\n        u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function) {\n            static u32 id = 0;\n\n            id++;\n\n            impl::s_backgroundHighlightingFunctions->insert({ id, function });\n\n            TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });\n\n            return id;\n        }\n\n        void removeBackgroundHighlightingProvider(u32 id) {\n            impl::s_backgroundHighlightingFunctions->erase(id);\n\n            TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });\n        }\n\n        u32 addForegroundHighlight(const Region &region, color_t color) {\n            static u32 id = 0;\n\n            id++;\n\n            impl::s_foregroundHighlights->insert({\n                id, Highlighting { region, color }\n            });\n\n            TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });\n\n            return id;\n        }\n\n        void removeForegroundHighlight(u32 id) {\n            impl::s_foregroundHighlights->erase(id);\n\n            TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });\n        }\n\n        u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function) {\n            static u32 id = 0;\n\n            id++;\n\n            impl::s_foregroundHighlightingFunctions->insert({ id, function });\n\n            TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });\n\n            return id;\n        }\n\n        void removeForegroundHighlightingProvider(u32 id) {\n            impl::s_foregroundHighlightingFunctions->erase(id);\n\n            TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });\n        }\n\n        u32 addHoverHighlightProvider(const impl::HoveringFunction &function) {\n            static u32 id = 0;\n\n            id++;\n\n            impl::s_hoveringFunctions->insert({ id, function });\n\n            return id;\n        }\n\n        void removeHoverHighlightProvider(u32 id) {\n            impl::s_hoveringFunctions->erase(id);\n        }\n\n        static u32 s_tooltipId = 0;\n        u32 addTooltip(Region region, std::string value, color_t color) {\n            s_tooltipId++;\n            impl::s_tooltips->insert({ s_tooltipId, { region, std::move(value), color } });\n\n            return s_tooltipId;\n        }\n\n        void removeTooltip(u32 id) {\n            impl::s_tooltips->erase(id);\n        }\n\n        static u32 s_tooltipFunctionId;\n        u32 addTooltipProvider(TooltipFunction function) {\n            s_tooltipFunctionId++;\n            impl::s_tooltipFunctions->insert({ s_tooltipFunctionId, std::move(function) });\n\n            return s_tooltipFunctionId;\n        }\n\n        void removeTooltipProvider(u32 id) {\n            impl::s_tooltipFunctions->erase(id);\n        }\n\n        bool isSelectionValid() {\n            auto selection = getSelection();\n            return selection.has_value() && selection->provider != nullptr;\n        }\n\n        std::optional<ProviderRegion> getSelection() {\n            return impl::s_currentSelection;\n        }\n\n        void clearSelection() {\n            impl::s_currentSelection->reset();\n        }\n\n        void setSelection(const Region &region, prv::Provider *provider) {\n            setSelection(ProviderRegion { region, provider == nullptr ? Provider::get() : provider });\n        }\n\n        void setSelection(const ProviderRegion &region) {\n            RequestHexEditorSelectionChange::post(region);\n        }\n\n        void setSelection(u64 address, size_t size, prv::Provider *provider) {\n            setSelection({ { .address=address, .size=size }, provider == nullptr ? Provider::get() : provider });\n        }\n\n        void addVirtualFile(const std::string &path, std::vector<u8> data, Region region) {\n            RequestAddVirtualFile::post(path, std::move(data), region);\n        }\n\n        const std::optional<Region>& getHoveredRegion(const prv::Provider *provider) {\n            return impl::s_hoveredRegion.get(provider);\n        }\n\n    }\n\n\n    namespace ImHexApi::Bookmarks {\n\n        u64 add(Region region, const std::string &name, const std::string &comment, u32 color) {\n            u64 id = 0;\n            RequestAddBookmark::post(region, name, comment, color, &id);\n\n            return id;\n        }\n\n        u64 add(u64 address, size_t size, const std::string &name, const std::string &comment, u32 color) {\n            return add(Region { .address=address, .size=size }, name, comment, color);\n        }\n\n        void remove(u64 id) {\n            RequestRemoveBookmark::post(id);\n        }\n\n    }\n\n\n    namespace ImHexApi::Provider {\n\n        static i64 s_currentProvider = -1;\n        static AutoReset<std::vector<std::shared_ptr<prv::Provider>>> s_providers;\n        static AutoReset<std::map<prv::Provider*, std::shared_ptr<prv::Provider>>> s_providersToRemove;\n\n        namespace impl {\n\n            static std::set<prv::Provider*> s_closingProviders;\n            void resetClosingProvider() {\n                s_closingProviders.clear();\n            }\n\n            std::set<prv::Provider*> getClosingProviders() {\n                return s_closingProviders;\n            }\n\n            static std::recursive_mutex s_providerMutex;\n        }\n\n        prv::Provider *get() {\n            if (!ImHexApi::Provider::isValid())\n                return nullptr;\n\n            return (*s_providers)[s_currentProvider].get();\n        }\n\n        std::vector<prv::Provider*> getProviders() {\n            std::vector<prv::Provider*> result;\n            result.reserve(s_providers->size());\n            for (const auto &provider : *s_providers)\n                result.push_back(provider.get());\n\n            return result;\n        }\n\n        void setCurrentProvider(i64 index) {\n            std::scoped_lock lock(impl::s_providerMutex);\n\n            if (TaskManager::getRunningTaskCount() > 0)\n                return;\n\n            if (std::cmp_less(index, s_providers->size()) && s_currentProvider != index) {\n                auto oldProvider  = get();\n                s_currentProvider = index;\n                EventProviderChanged::post(oldProvider, get());\n            }\n\n            RequestUpdateWindowTitle::post();\n        }\n\n        void setCurrentProvider(NonNull<prv::Provider*> provider) {\n            std::scoped_lock lock(impl::s_providerMutex);\n\n            if (TaskManager::getRunningTaskCount() > 0)\n                return;\n\n            const auto providers = getProviders();\n            auto it = std::ranges::find(providers, provider.get());\n\n            auto index = std::distance(providers.begin(), it);\n            setCurrentProvider(index);\n        }\n\n        i64 getCurrentProviderIndex() {\n            return s_currentProvider;\n        }\n\n        bool isValid() {\n            return !s_providers->empty() && s_currentProvider >= 0 && s_currentProvider < i64(s_providers->size());\n        }\n\n        void markDirty() {\n            const auto provider = get();\n            if (!provider->isDirty()) {\n                provider->markDirty();\n            }\n            EventProviderDirtied::post(provider);\n        }\n\n        void resetDirty() {\n            for (const auto &provider : *s_providers)\n                provider->markDirty(false);\n        }\n\n        bool isDirty() {\n            return std::ranges::any_of(*s_providers, [](const auto &provider) {\n                return provider->isDirty();\n            });\n        }\n\n        void add(std::shared_ptr<prv::Provider> &&provider, bool skipLoadInterface, bool select) {\n            std::scoped_lock lock(impl::s_providerMutex);\n\n            if (TaskManager::getRunningTaskCount() > 0)\n                return;\n\n            if (skipLoadInterface)\n                provider->skipLoadInterface();\n\n            EventProviderCreated::post(provider);\n            s_providers->emplace_back(std::move(provider));\n\n            if (select || s_providers->size() == 1)\n                setCurrentProvider(s_providers->size() - 1);\n        }\n\n        void remove(prv::Provider *provider, bool noQuestions) {\n            std::scoped_lock lock(impl::s_providerMutex);\n\n            if (provider == nullptr)\n                 return;\n\n            if (TaskManager::getRunningTaskCount() > 0)\n                return;\n\n            if (!noQuestions) {\n                impl::s_closingProviders.insert(provider);\n\n                bool shouldClose = true;\n                EventProviderClosing::post(provider, &shouldClose);\n                if (!shouldClose)\n                    return;\n            }\n\n            const auto it = std::ranges::find_if(*s_providers, [provider](const auto &p) {\n                return p.get() == provider;\n            });\n\n            if (it == s_providers->end())\n                return;\n\n            if (!s_providers->empty()) {\n                if (it == s_providers->begin()) {\n                    // If the first provider is being closed, select the one that's the first one now\n                    setCurrentProvider(0);\n\n                    if (s_providers->size() > 1)\n                        EventProviderChanged::post(s_providers->at(0).get(), s_providers->at(1).get());\n                }\n                else if (std::distance(s_providers->begin(), it) == s_currentProvider) {\n                    // If the current provider is being closed, select the one that's before it\n                    setCurrentProvider(s_currentProvider - 1);\n                }\n                else {\n                    // If any other provider is being closed, find the current provider in the list again and select it again\n                    const auto currentProvider = get();\n                    const auto currentIt = std::ranges::find_if(*s_providers, [currentProvider](const auto &p) {\n                        return p.get() == currentProvider;\n                    });\n\n                    if (currentIt != s_providers->end()) {\n                        auto newIndex = std::distance(s_providers->begin(), currentIt);\n\n                        if (s_currentProvider == newIndex && newIndex != 0)\n                            newIndex -= 1;\n\n                        setCurrentProvider(newIndex);\n                    } else {\n                        // If the current provider is not in the list anymore, select the first one\n                        setCurrentProvider(0);\n                    }\n                }\n            }\n\n            static std::mutex eraseMutex;\n\n            // Move provider over to a list of providers to delete\n            eraseMutex.lock();\n            auto providerToRemove = it->get();\n            (*s_providersToRemove)[providerToRemove] = std::move(*it);\n            eraseMutex.unlock();\n\n            // Remove left over references from the main provider list\n            s_providers->erase(it);\n            impl::s_closingProviders.erase(provider);\n\n            if (s_currentProvider >= i64(s_providers->size()) && !s_providers->empty())\n                setCurrentProvider(s_providers->size() - 1);\n\n            if (s_providers->empty())\n                EventProviderChanged::post(provider, nullptr);\n\n            EventProviderClosed::post(providerToRemove);\n            RequestUpdateWindowTitle::post();\n\n            // Do the destruction of the provider in the background once all tasks have finished\n            TaskManager::runWhenTasksFinished([providerToRemove] {\n                EventProviderDeleted::post(providerToRemove);\n                TaskManager::createBackgroundTask(\"Closing Provider\", [providerToRemove](Task &) {\n                    eraseMutex.lock();\n                    auto provider = std::move((*s_providersToRemove)[providerToRemove]);\n                    s_providersToRemove->erase(providerToRemove);\n                    eraseMutex.unlock();\n\n                    provider->close();\n                });\n            });\n        }\n\n        std::shared_ptr<prv::Provider> createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface, bool select) {\n            std::shared_ptr<prv::Provider> result = nullptr;\n            RequestCreateProvider::post(unlocalizedName, skipLoadInterface, select, &result);\n\n            return result;\n        }\n\n        void openProvider(std::shared_ptr<prv::Provider> provider) {\n            RequestOpenProvider::post(provider);\n        }\n\n    }\n\n    namespace ImHexApi::System {\n\n        namespace impl {\n\n            // Default to true means we forward to ourselves by default\n            static bool s_isMainInstance = true;\n            void setMainInstanceStatus(bool status) {\n                s_isMainInstance = status;\n            }\n\n            static ImVec2 s_mainWindowPos;\n            static ImVec2 s_mainWindowSize;\n            void setMainWindowPosition(i32 x, i32 y) {\n                s_mainWindowPos = ImVec2(float(x), float(y));\n            }\n\n            void setMainWindowSize(u32 width, u32 height) {\n                s_mainWindowSize = ImVec2(float(width), float(height));\n            }\n\n            static ImGuiID s_mainDockSpaceId;\n            void setMainDockSpaceId(ImGuiID id) {\n                s_mainDockSpaceId = id;\n            }\n\n            static GLFWwindow *s_mainWindowHandle;\n            void setMainWindowHandle(GLFWwindow *window) {\n                s_mainWindowHandle = window;\n            }\n\n            static bool s_mainWindowFocused = false;\n            void setMainWindowFocusState(bool focused) {\n                s_mainWindowFocused = focused;\n            }\n\n\n            static float s_globalScale = 1.0;\n            void setGlobalScale(float scale) {\n                s_globalScale = scale;\n            }\n\n            static float s_nativeScale = 1.0;\n            void setNativeScale(float scale) {\n                s_nativeScale = scale;\n            }\n\n\n            static bool s_borderlessWindowMode;\n            void setBorderlessWindowMode(bool enabled) {\n                s_borderlessWindowMode = enabled;\n            }\n\n            static bool s_multiWindowMode = false;\n            void setMultiWindowMode(bool enabled) {\n                s_multiWindowMode = enabled;\n            }\n\n            static std::optional<InitialWindowProperties> s_initialWindowProperties;\n            void setInitialWindowProperties(InitialWindowProperties properties) {\n                s_initialWindowProperties = properties;\n            }\n\n\n            static AutoReset<std::string> s_gpuVendor;\n            void setGPUVendor(const std::string &vendor) {\n                s_gpuVendor = vendor;\n            }\n\n            static AutoReset<std::string> s_glRenderer;\n            void setGLRenderer(const std::string &renderer) {\n                s_glRenderer = renderer;\n            }\n\n            static SemanticVersion s_openGLVersion;\n            void setGLVersion(SemanticVersion version) {\n                s_openGLVersion = version;\n            }\n\n            static AutoReset<std::map<std::string, std::string>> s_initArguments;\n            void addInitArgument(const std::string &key, const std::string &value) {\n                static std::mutex initArgumentsMutex;\n                std::scoped_lock lock(initArgumentsMutex);\n\n                (*s_initArguments)[key] = value;\n            }\n\n            static double s_lastFrameTime;\n            void setLastFrameTime(double time) {\n                s_lastFrameTime = time;\n            }\n\n            static bool s_windowResizable = true;\n            bool isWindowResizable() {\n                return s_windowResizable;\n            }\n\n            static auto& getAutoResetObjects() {\n                static std::set<hex::impl::AutoResetBase*> autoResetObjects;\n\n                return autoResetObjects;\n            }\n\n            void addAutoResetObject(hex::impl::AutoResetBase *object) {\n                getAutoResetObjects().insert(object);\n            }\n\n            void removeAutoResetObject(hex::impl::AutoResetBase *object) {\n                getAutoResetObjects().erase(object);\n            }\n\n            void cleanup() {\n                for (const auto &object : getAutoResetObjects())\n                    object->reset();\n            }\n\n            static bool s_frameRateUnlockRequested = false;\n            bool frameRateUnlockRequested() {\n                return s_frameRateUnlockRequested;\n            }\n\n            void resetFrameRateUnlockRequested() {\n                s_frameRateUnlockRequested = false;\n            }\n\n        }\n\n        bool isMainInstance() {\n            return impl::s_isMainInstance;\n        }\n\n        void closeImHex(bool noQuestions) {\n            RequestCloseImHex::post(noQuestions);\n        }\n\n        void restartImHex() {\n            RequestRestartImHex::post();\n            RequestCloseImHex::post(false);\n        }\n\n        void setTaskBarProgress(TaskProgressState state, TaskProgressType type, u32 progress) {\n            EventSetTaskBarIconState::post(u32(state), u32(type), progress);\n        }\n\n\n        static float s_targetFPS = 14.0F;\n\n        float getTargetFPS() {\n            return s_targetFPS;\n        }\n\n        void setTargetFPS(float fps) {\n            s_targetFPS = fps;\n        }\n\n        float getGlobalScale() {\n            return impl::s_globalScale;\n        }\n\n        float getNativeScale() {\n            return impl::s_nativeScale;\n        }\n\n        float getBackingScaleFactor() {\n            #if defined(OS_WINDOWS)\n                return 1.0F;\n            #elif defined(OS_MACOS)\n                return ::getBackingScaleFactor();\n            #elif defined(OS_LINUX)\n                const auto sessionType = hex::getEnvironmentVariable(\"XDG_SESSION_TYPE\");\n                if (!sessionType.has_value() || sessionType == \"x11\")\n                    return 1.0F;\n                else {\n                    int windowW, windowH;\n                    int displayW, displayH;\n                    glfwGetWindowSize(getMainWindowHandle(), &windowW, &windowH);\n                    glfwGetFramebufferSize(getMainWindowHandle(), &displayW, &displayH);\n\n                    return (windowW > 0) ? float(displayW) / windowW : 1.0f;\n                }\n            #elif defined(OS_WEB)\n                return emscripten_get_device_pixel_ratio();\n            #else\n                return 1.0F;\n            #endif\n        }\n\n\n        ImVec2 getMainWindowPosition() {\n            if ((ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) != ImGuiConfigFlags_None)\n                return impl::s_mainWindowPos;\n            else\n                return { 0, 0 };\n        }\n\n        ImVec2 getMainWindowSize() {\n            return impl::s_mainWindowSize;\n        }\n\n\n        ImGuiID getMainDockSpaceId() {\n            return impl::s_mainDockSpaceId;\n        }\n\n        GLFWwindow* getMainWindowHandle() {\n            return impl::s_mainWindowHandle;\n        }\n\n        bool isMainWindowFocused() {\n            return impl::s_mainWindowFocused;\n        }\n\n        bool isBorderlessWindowModeEnabled() {\n            return impl::s_borderlessWindowMode;\n        }\n\n        bool isMultiWindowModeEnabled() {\n            return impl::s_multiWindowMode;\n        }\n\n        std::optional<InitialWindowProperties> getInitialWindowProperties() {\n            return impl::s_initialWindowProperties;\n        }\n\n        void* getLibImHexModuleHandle() {\n            return hex::getContainingModule(reinterpret_cast<void*>(&getLibImHexModuleHandle));\n        }\n\n        void addMigrationRoutine(SemanticVersion migrationVersion, std::function<void()> function) {\n            EventImHexUpdated::subscribe([migrationVersion, function](const SemanticVersion &oldVersion, const SemanticVersion &newVersion) {\n                if (oldVersion < migrationVersion && newVersion >= migrationVersion) {\n                    function();\n                }\n            });\n        }\n\n\n        const std::map<std::string, std::string>& getInitArguments() {\n            return *impl::s_initArguments;\n        }\n\n        std::string getInitArgument(const std::string &key) {\n            if (impl::s_initArguments->contains(key))\n                return impl::s_initArguments->at(key);\n            else\n                return \"\";\n        }\n\n\n\n        static bool s_systemThemeDetection;\n        void enableSystemThemeDetection(bool enabled) {\n            s_systemThemeDetection = enabled;\n\n            EventOSThemeChanged::post();\n        }\n\n        bool usesSystemThemeDetection() {\n            return s_systemThemeDetection;\n        }\n\n\n        static AutoReset<std::vector<std::fs::path>> s_additionalFolderPaths;\n        const std::vector<std::fs::path>& getAdditionalFolderPaths() {\n            return *s_additionalFolderPaths;\n        }\n\n        void setAdditionalFolderPaths(const std::vector<std::fs::path> &paths) {\n            s_additionalFolderPaths = paths;\n        }\n\n\n        const std::string &getGPUVendor() {\n            return impl::s_gpuVendor;\n        }\n\n        const std::string &getGLRenderer() {\n            return impl::s_glRenderer;\n        }\n\n        const SemanticVersion& getGLVersion() {\n            return impl::s_openGLVersion;\n        }\n\n        bool isCorporateEnvironment() {\n            #if defined(OS_WINDOWS)\n                {\n                    DSROLE_PRIMARY_DOMAIN_INFO_BASIC * info;\n                    if ((DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *)&info) == ERROR_SUCCESS) && (info != nullptr))\n                    {\n                        bool result = std::wstring(info->DomainNameFlat) != L\"WORKGROUP\";\n                        DsRoleFreeMemory(info);\n\n                        return result;\n                    } else {\n                        DWORD size = 128;\n                        char buffer[128];\n                        ::GetComputerNameExA(ComputerNameDnsDomain, buffer, &size);\n                        return size > 0;\n                    }\n                }\n            #else\n                return false;\n            #endif\n        }\n\n        bool isPortableVersion() {\n            static std::optional<bool> portable;\n            if (portable.has_value())\n                return portable.value();\n\n            if (const auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) {\n                const auto flagFile = executablePath->parent_path() / \"PORTABLE\";\n\n                portable = wolv::io::fs::exists(flagFile) && wolv::io::fs::isRegularFile(flagFile);\n            } else {\n                portable = false;\n            }\n\n            return portable.value();\n        }\n\n        std::string getOSName() {\n            #if defined(OS_WINDOWS)\n                return \"Windows\";\n            #elif defined(OS_LINUX)\n                #if defined(OS_FREEBSD)\n                    return \"FreeBSD\";\n                #else\n                    return \"Linux\";\n                #endif\n            #elif defined(OS_MACOS)\n                return \"macOS\";\n            #elif defined(OS_WEB)\n                return \"Web\";\n            #else\n                return \"Unknown\";\n            #endif\n        }\n\n        std::string getOSVersion() {\n            #if defined(OS_WINDOWS)\n                OSVERSIONINFOA info;\n                info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);\n                ::GetVersionExA(&info);\n\n                return fmt::format(\"{}.{}.{}\", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber);\n            #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB)\n                struct utsname details = { };\n\n                if (uname(&details) != 0) {\n                    return \"Unknown\";\n                }\n\n                return std::string(details.release) + \" \" + std::string(details.version);\n            #else\n                return \"Unknown\";\n            #endif\n        }\n\n        std::string getArchitecture() {\n            #if defined(OS_WINDOWS)\n                SYSTEM_INFO info;\n                ::GetNativeSystemInfo(&info);\n\n                switch (info.wProcessorArchitecture) {\n                    case PROCESSOR_ARCHITECTURE_AMD64:\n                        return \"x86_64\";\n                    case PROCESSOR_ARCHITECTURE_ARM:\n                        return \"ARM\";\n                    case PROCESSOR_ARCHITECTURE_ARM64:\n                        return \"ARM64\";\n                    case PROCESSOR_ARCHITECTURE_IA64:\n                        return \"IA64\";\n                    case PROCESSOR_ARCHITECTURE_INTEL:\n                        return \"x86\";\n                    default:\n                        return \"Unknown\";\n                }\n            #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB)\n                struct utsname details = { };\n\n                if (uname(&details) != 0) {\n                    return \"Unknown\";\n                }\n\n                return { details.machine };\n            #else\n                return \"Unknown\";\n            #endif\n        }\n\n        std::optional<LinuxDistro> getLinuxDistro() {\n            wolv::io::File file(\"/etc/os-release\", wolv::io::File::Mode::Read);\n            std::string name;\n            std::string version;\n\n            auto fileContent = file.readString();\n            for (const auto &line : wolv::util::splitString(fileContent, \"\\n\")) {\n                if (line.find(\"PRETTY_NAME=\") != std::string::npos) {\n                    name = line.substr(line.find(\"=\") + 1);\n                    std::erase(name, '\\\"');\n                } else if (line.find(\"VERSION_ID=\") != std::string::npos) {\n                    version = line.substr(line.find(\"=\") + 1);\n                    std::erase(version, '\\\"');\n                }\n            }\n\n            return { { .name=name, .version=version } };\n        }\n\n        const SemanticVersion& getImHexVersion() {\n            #if defined(IMHEX_VERSION)\n                static auto version = SemanticVersion(IMHEX_VERSION);\n                return version;\n            #else\n                static auto version = SemanticVersion();\n                return version;\n            #endif\n        }\n\n        std::string getCommitHash(bool longHash) {\n            #if defined(GIT_COMMIT_HASH_LONG)\n                if (longHash) {\n                    return GIT_COMMIT_HASH_LONG;\n                } else {\n                    return std::string(GIT_COMMIT_HASH_LONG).substr(0, 7);\n                }\n            #else\n                std::ignore = longHash;\n                return \"Unknown\";\n            #endif\n        }\n\n        std::string getCommitBranch() {\n            #if defined(GIT_BRANCH)\n                return GIT_BRANCH;\n            #else\n                return \"Unknown\";\n            #endif\n        }\n\n        std::optional<std::chrono::system_clock::time_point> getBuildTime() {\n            #if defined(IMHEX_BUILD_DATE)\n                return hex::parseTime(\"%Y-%m-%dT%H:%M:%SZ\", IMHEX_BUILD_DATE);\n            #else\n                return std::nullopt;\n            #endif\n        }\n\n        bool isDebugBuild() {\n            #if defined DEBUG\n                return true;\n            #else\n                return false;\n            #endif\n        }\n\n        bool isNightlyBuild() {\n            const static bool isNightly = getImHexVersion().nightly();\n\n            return isNightly;\n        }\n\n        std::optional<std::string> checkForUpdate() {\n            #if defined(OS_WEB)\n                return std::nullopt;\n            #else\n                if (ImHexApi::System::isNightlyBuild()) {\n                    HttpRequest request(\"GET\", GitHubApiURL + std::string(\"/releases/tags/nightly\"));\n                    request.setTimeout(10000);\n\n                    // Query the GitHub API for the latest release version\n                    auto response = request.execute().get();\n                    if (response.getStatusCode() != 200)\n                        return std::nullopt;\n\n                    nlohmann::json releases;\n                    try {\n                        releases = nlohmann::json::parse(response.getData());\n                    } catch (const std::exception &) {\n                        return std::nullopt;\n                    }\n\n                    // Check if the response is valid\n                    if (!releases.contains(\"assets\") || !releases[\"assets\"].is_array())\n                        return std::nullopt;\n\n                    const auto firstAsset = releases[\"assets\"].front();\n                    if (!firstAsset.is_object() || !firstAsset.contains(\"updated_at\"))\n                        return std::nullopt;\n\n                    const auto nightlyUpdateTime = hex::parseTime(\"%Y-%m-%dT%H:%M:%SZ\", firstAsset[\"updated_at\"].get<std::string>());\n                    const auto imhexBuildTime = ImHexApi::System::getBuildTime();\n\n                    // Give a bit of time leniency for the update time check\n                    // We're comparing here the binary build time to the release upload time. If we were to strictly compare\n                    // upload time to be greater than current build time, the check would always be true since the CI\n                    // takes a few minutes after the build to actually upload the artifact.\n                    // TODO: Is there maybe a better way to handle this without downloading the artifact just to check the build time?\n                    if (nightlyUpdateTime.has_value() && imhexBuildTime.has_value() && *nightlyUpdateTime > *imhexBuildTime + std::chrono::hours(1)) {\n                        return \"Nightly\";\n                    }\n                } else {\n                    HttpRequest request(\"GET\", GitHubApiURL + std::string(\"/releases/latest\"));\n\n                    // Query the GitHub API for the latest release version\n                    auto response = request.execute().get();\n                    if (response.getStatusCode() != 200)\n                        return std::nullopt;\n\n                    nlohmann::json releases;\n                    try {\n                        releases = nlohmann::json::parse(response.getData());\n                    } catch (const std::exception &) {\n                        return std::nullopt;\n                    }\n\n                    // Check if the response is valid\n                    if (!releases.contains(\"tag_name\") || !releases[\"tag_name\"].is_string())\n                        return std::nullopt;\n\n                    // Convert the current version string to a format that can be compared to the latest release\n                    auto currVersion   = \"v\" + ImHexApi::System::getImHexVersion().get(false);\n\n                    // Get the latest release version string\n                    auto latestVersion = releases[\"tag_name\"].get<std::string>();\n\n                    // Check if the latest release is different from the current version\n                    if (latestVersion != currVersion)\n                        return latestVersion;\n                }\n\n                return std::nullopt;\n            #endif\n        }\n\n        bool updateImHex(UpdateType updateType) {\n            #if defined(OS_WEB)\n                switch (updateType) {\n                    case UpdateType::Stable:\n                        EM_ASM({ window.location.href = window.location.origin; });\n                        break;\n                    case UpdateType::Nightly:\n                        EM_ASM({ window.location.href = window.location.origin + \"/nightly\"; });\n                        break;\n                }\n\n                return true;\n            #else\n                // Get the path of the updater executable\n                std::fs::path executablePath;\n\n                for (const auto &entry : std::fs::directory_iterator(wolv::io::fs::getExecutablePath()->parent_path())) {\n                    if (entry.path().filename().string().starts_with(\"imhex-updater\")) {\n                        executablePath = entry.path();\n                        break;\n                    }\n                }\n\n                if (executablePath.empty() || !wolv::io::fs::exists(executablePath))\n                    return false;\n\n                std::string updateTypeString;\n                switch (updateType) {\n                    case UpdateType::Stable:\n                        updateTypeString = \"stable\";\n                        break;\n                    case UpdateType::Nightly:\n                        updateTypeString = \"nightly\";\n                        break;\n                }\n\n                EventImHexClosing::subscribe([executablePath, updateTypeString] {\n                    hex::startProgram({ wolv::util::toUTF8String(executablePath), updateTypeString });\n                });\n\n                ImHexApi::System::closeImHex();\n\n                return true;\n            #endif\n        }\n\n        void addStartupTask(const std::string &name, bool async, const std::function<bool()> &function) {\n            RequestAddInitTask::post(name, async, function);\n        }\n\n        double getLastFrameTime() {\n            return impl::s_lastFrameTime;\n        }\n\n        void setWindowResizable(bool resizable) {\n            glfwSetWindowAttrib(impl::s_mainWindowHandle, GLFW_RESIZABLE, int(resizable));\n            impl::s_windowResizable = resizable;\n        }\n\n        void unlockFrameRate() {\n            impl::s_frameRateUnlockRequested = true;\n        }\n\n        void setPostProcessingShader(const std::string &vertexShader, const std::string &fragmentShader) {\n            RequestSetPostProcessingShader::post(vertexShader, fragmentShader);\n        }\n\n\n    }\n\n    namespace ImHexApi::Messaging {\n\n        namespace impl {\n\n            static AutoReset<std::map<std::string, MessagingHandler>> s_handlers;\n            const std::map<std::string, MessagingHandler>& getHandlers() {\n                return *s_handlers;\n            }\n\n            void runHandler(const std::string &eventName, const std::vector<u8> &args) {\n                const auto& handlers = getHandlers();\n                const auto matchHandler = handlers.find(eventName);\n                \n                if (matchHandler == handlers.end()) {\n                    log::error(\"Forward event handler {} not found\", eventName);\n                } else {\n                    matchHandler->second(args);\n                }\n\n            }\n\n        }\n\n        void registerHandler(const std::string &eventName, const impl::MessagingHandler &handler) {\n            log::debug(\"Registered new forward event handler: {}\", eventName);\n\n            impl::s_handlers->insert({ eventName, handler });\n        }\n\n    }\n\n    namespace ImHexApi::Fonts {\n\n        namespace impl {\n\n            static AutoReset<std::vector<MergeFont>> s_fonts;\n            const std::vector<MergeFont>& getMergeFonts() {\n                return *s_fonts;\n            }\n\n            static AutoReset<std::map<UnlocalizedString, FontDefinition>> s_fontDefinitions;\n            std::map<UnlocalizedString, FontDefinition>& getFontDefinitions() {\n                return *s_fontDefinitions;\n            }\n\n            static AutoReset<const Font*> s_defaultFont;\n\n        }\n\n        Font::Font(UnlocalizedString fontName) : m_fontName(std::move(fontName)) {\n            if (impl::s_defaultFont == nullptr)\n                impl::s_defaultFont = this;\n        }\n\n        void Font::push(float size) const {\n            push(size, getFont(m_fontName).regular);\n        }\n\n        void Font::pushBold(float size) const {\n            push(size, getFont(m_fontName).bold);\n        }\n\n        void Font::pushItalic(float size) const {\n            push(size, getFont(m_fontName).italic);\n        }\n\n        void Font::push(float size, ImFont* font) const {\n            if (font != nullptr) {\n                if (size <= 0.0F) {\n                    size = font->LegacySize;\n\n                    if (font->Sources[0]->PixelSnapH)\n                        size *= System::getGlobalScale();\n                    else\n                        size *= std::floor(System::getGlobalScale());\n                } else {\n                    size *= ImGui::GetCurrentContext()->FontSizeBase;\n                }\n            }\n\n            // If no font has been loaded, revert back to the default font to\n            // prevent an assertion failure in ImGui\n            const auto *ctx = ImGui::GetCurrentContext();\n            if (font == nullptr && ctx->Font == nullptr) [[unlikely]] {\n                font = ImGui::GetDefaultFont();\n            }\n\n            ImGui::PushFont(font, size);\n        }\n\n\n        void Font::pop() const {\n            ImGui::PopFont();\n        }\n\n        Font::operator ImFont*() const {\n            return getFont(m_fontName).regular;\n        }\n\n        void registerMergeFont(const std::string &name, const std::span<const u8> &data, Offset offset, std::optional<float> fontSizeMultiplier) {\n            impl::s_fonts->emplace_back(\n                name,\n                data,\n                offset,\n                fontSizeMultiplier\n            );\n\n            #if defined(OS_MACOS)\n                macosRegisterFont(data.data(), data.size_bytes());\n            #endif\n        }\n\n        void registerFont(const Font& font) {\n            (*impl::s_fontDefinitions)[font.getUnlocalizedName()] = {};\n        }\n\n        FontDefinition getFont(const UnlocalizedString &fontName) {\n            auto it = impl::s_fontDefinitions->find(fontName);\n            \n            if (it == impl::s_fontDefinitions->end()) {\n                const auto defaultFont = ImGui::GetDefaultFont();\n                return { .regular=defaultFont, .bold=defaultFont, .italic=defaultFont };\n            } else\n                return it->second;\n        }\n\n        void setDefaultFont(const Font& font) {\n            impl::s_defaultFont = &font;\n        }\n\n        const Font& getDefaultFont() {\n            if (*impl::s_defaultFont == nullptr) {\n                static Font emptyFont(\"\");\n                return emptyFont;\n            }\n            return **impl::s_defaultFont;\n        }\n\n        float getDpi() {\n            auto dpi = ImHexApi::System::getNativeScale() * 96.0F;\n            return dpi ? dpi : 96.0F;\n        }\n\n        float pixelsToPoints(float pixels) {\n            return pixels * (72.0 / getDpi());\n        }\n\n        float pointsToPixels(float points) {\n            return points / (72.0 / getDpi());\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/api/layout_manager.cpp",
    "content": "#include <hex/api/layout_manager.hpp>\n\n#include <hex/api/content_registry/views.hpp>\n#include <hex/ui/view.hpp>\n#include <hex/helpers/logger.hpp>\n#include <hex/helpers/auto_reset.hpp>\n#include <hex/helpers/default_paths.hpp>\n\n#include <wolv/utils/string.hpp>\n\n#include <imgui.h>\n\nnamespace hex {\n\n    namespace {\n\n        AutoReset<std::optional<std::fs::path>> s_layoutPathToLoad;\n        AutoReset<std::optional<std::string>> s_layoutStringToLoad;\n        AutoReset<std::vector<LayoutManager::Layout>> s_layouts;\n\n        AutoReset<std::vector<LayoutManager::LoadCallback>>  s_loadCallbacks;\n        AutoReset<std::vector<LayoutManager::StoreCallback>> s_storeCallbacks;\n\n        bool s_layoutLocked = false;\n\n    }\n\n\n    void LayoutManager::load(const std::fs::path &path) {\n        s_layoutPathToLoad = path;\n    }\n\n    void LayoutManager::loadFromString(const std::string &content) {\n        s_layoutStringToLoad = content;\n    }\n\n    void LayoutManager::save(const std::string &name) {\n        auto fileName = name;\n        fileName = wolv::util::replaceStrings(fileName, \" \", \"_\");\n        std::ranges::transform(fileName, fileName.begin(), tolower);\n        fileName += \".hexlyt\";\n\n        for (const auto &path : paths::Layouts.write()) {\n            size_t outSize = 0;\n            const char* iniData = ImGui::SaveIniSettingsToMemory(&outSize);\n\n            std::fs::path layoutPath = path / fileName;\n            wolv::io::File file = wolv::io::File(layoutPath, wolv::io::File::Mode::Write);\n            if (!file.isValid()) {\n                log::warn(\"Failed to save layout '{}'. Could not open file '{}', continuing with next path\", name, wolv::util::toUTF8String(layoutPath));\n                continue;\n            }\n\n            const size_t written = file.writeBuffer(reinterpret_cast<const u8*>(iniData), outSize);\n            if (written != outSize) {\n                log::warn(\"Failed to save layout '{}'. Could not write file '{}', continuing with next path\", name, wolv::util::toUTF8String(layoutPath));\n                continue;\n            }\n            log::info(\"Layout '{}' saved to '{}'\", name, wolv::util::toUTF8String(layoutPath));\n            LayoutManager::reload();\n            return;\n        }\n        log::error(\"Failed to save layout '{}'. No writable path found\", name);\n    }\n\n    std::string LayoutManager::saveToString() {\n        return ImGui::SaveIniSettingsToMemory();\n    }\n\n\n    const std::vector<LayoutManager::Layout>& LayoutManager::getLayouts() {\n        return s_layouts;\n    }\n\n    void LayoutManager::removeLayout(const std::string& name) {\n        for (const auto &layout : *s_layouts) {\n            if (layout.name == name) {\n                if (wolv::io::fs::remove(layout.path)) {\n                    log::info(\"Removed layout '{}'\", name);\n                } else {\n                    log::error(\"Failed to remove layout '{}'\", name);\n                }\n            }\n        }\n\n        LayoutManager::reload();\n    }\n\n\n    void LayoutManager::closeAllViews() {\n        for (const auto &[name, view] : ContentRegistry::Views::impl::getEntries())\n            view->getWindowOpenState() = false;\n    }\n\n    void LayoutManager::process() {\n        if (s_layoutPathToLoad->has_value()) {\n            LayoutManager::closeAllViews();\n\n            wolv::io::File file(**s_layoutPathToLoad, wolv::io::File::Mode::Read);\n            s_layoutStringToLoad = file.readString();\n            s_layoutPathToLoad->reset();\n        }\n\n        if (s_layoutStringToLoad->has_value()) {\n            LayoutManager::closeAllViews();\n            ImGui::LoadIniSettingsFromMemory((*s_layoutStringToLoad)->c_str());\n\n            s_layoutStringToLoad->reset();\n            log::info(\"Loaded new Layout\");\n        }\n    }\n\n    void LayoutManager::reload() {\n        s_layouts->clear();\n\n        for (const auto &directory : paths::Layouts.read()) {\n            for (const auto &entry : std::fs::directory_iterator(directory)) {\n                const auto &path = entry.path();\n\n                if (path.extension() != \".hexlyt\")\n                    continue;\n\n                auto name = path.stem().string();\n                name = wolv::util::replaceStrings(name, \"_\", \" \");\n                for (size_t i = 0; i < name.size(); i++) {\n                    if (i == 0 || name[i - 1] == '_')\n                        name[i] = char(std::toupper(name[i]));\n                }\n\n                s_layouts->push_back({\n                    name,\n                    path\n                });\n            }\n        }\n    }\n\n    void LayoutManager::reset() {\n        s_layoutPathToLoad->reset();\n        s_layoutStringToLoad->reset();\n        s_layouts->clear();\n    }\n\n    bool LayoutManager::isLayoutLocked() {\n        return s_layoutLocked;\n    }\n\n    void LayoutManager::lockLayout(bool locked) {\n        log::debug(\"Layout {}\", locked ? \"locked\" : \"unlocked\");\n        s_layoutLocked = locked;\n    }\n\n    void LayoutManager::registerLoadCallback(const LoadCallback &callback) {\n        s_loadCallbacks->push_back(callback);\n    }\n\n    void LayoutManager::registerStoreCallback(const StoreCallback &callback) {\n        s_storeCallbacks->push_back(callback);\n    }\n\n    void LayoutManager::onLoad(std::string_view line) {\n        for (const auto &callback : *s_loadCallbacks)\n            callback(line);\n    }\n\n    void LayoutManager::onStore(ImGuiTextBuffer *buffer) {\n        for (const auto &callback : *s_storeCallbacks)\n            callback(buffer);\n    }\n\n\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/api/localization_manager.cpp",
    "content": "#include <hex/api/localization_manager.hpp>\n\n#include <hex/helpers/auto_reset.hpp>\n#include <hex/helpers/logger.hpp>\n#include <hex/helpers/utils.hpp>\n\n#include <nlohmann/json.hpp>\n\n#include <mutex>\n#include <hex/helpers/debugging.hpp>\n\nnamespace hex {\n\n    namespace LocalizationManager {\n\n        constexpr static auto FallbackLanguageId = \"en-US\";\n\n        namespace {\n\n            AutoReset<std::map<LanguageId, LanguageDefinition>> s_languageDefinitions;\n            AutoReset<std::unordered_map<std::size_t, std::string>> s_localizations;\n            AutoReset<LanguageId> s_selectedLanguageId;\n\n        }\n\n        void addLanguages(const std::string_view &languageList, std::function<std::string_view(const std::string &path)> callback) {\n            const auto json = nlohmann::json::parse(languageList);\n\n            for (const auto &item : json) {\n                if (!item.contains(\"code\") || !item.contains(\"path\")) {\n                    log::error(\"Invalid language definition: {}\", item.dump(4));\n                    continue;\n                }\n\n                auto &definition = (*s_languageDefinitions)[item[\"code\"].get<std::string>()];\n\n                if (definition.id.empty()) {\n                    definition.id = item[\"code\"].get<std::string>();\n                }\n\n                if (definition.name.empty() && item.contains(\"name\")) {\n                    definition.name = item[\"name\"].get<std::string>();\n                }\n\n                if (definition.nativeName.empty() && item.contains(\"native_name\")) {\n                    definition.nativeName = item[\"native_name\"].get<std::string>();\n                }\n\n                if (definition.fallbackLanguageId.empty() && item.contains(\"fallback\")) {\n                    definition.fallbackLanguageId = item[\"fallback\"].get<std::string>();\n                }\n\n                if (item.contains(\"hidden\") && item[\"hidden\"].get<bool>()) {\n                    definition.hidden = true;\n                }\n\n                const auto path = item[\"path\"].get<std::string>();\n\n                definition.languageFilePaths.emplace_back(PathEntry{ .path=path, .callback=callback });\n            }\n        }\n\n        static LanguageId findBestLanguageMatch(LanguageId languageId) {\n            if (s_languageDefinitions->contains(languageId))\n                return languageId;\n\n            if (const auto pos = languageId.find('_'); pos != std::string::npos) {\n                // Turn language Ids like \"en_US\" into \"en-US\"\n                languageId[pos] = '-';\n            }\n\n            if (const auto pos = languageId.find('-'); pos != std::string::npos) {\n                // Try to find a match with the language code without region\n                languageId = languageId.substr(0, pos);\n\n                for (const auto &definition : *s_languageDefinitions) {\n                    if (definition.first.starts_with(languageId) || definition.first.starts_with(toLower(languageId))) {\n                        return definition.first;\n                    }\n                }\n            }\n\n            // Fall back to English if no better match was found\n            return \"en-US\";\n        }\n\n        static void populateLocalization(LanguageId languageId, std::unordered_map<std::size_t, std::string> &localizations) {\n            if (languageId.empty())\n                return;\n\n            languageId = findBestLanguageMatch(languageId);\n\n            if (const auto it = s_languageDefinitions->find(languageId); it == s_languageDefinitions->end()) {\n                log::error(\"No language definition found for language: {}\", languageId);\n\n                if (languageId != FallbackLanguageId)\n                    populateLocalization(FallbackLanguageId, localizations);\n            } else {\n                const auto &definition = it->second;\n                for (const auto &path : definition.languageFilePaths) {\n                    try {\n                        const auto translation = path.callback(path.path);\n                        const auto json = nlohmann::json::parse(translation);\n\n                        for (const auto &entry : json.items()) {\n                            auto value = entry.value().get<std::string>();\n\n                            // Skip empty values\n                            if (value.empty())\n                                continue;\n\n                            // Handle references to files\n                            if (value.starts_with(\"#@\")) {\n                                try {\n                                    value = path.callback(value.substr(2));\n                                } catch (std::exception &e) {\n                                    log::error(\"Failed to load localization file reference '{}': {}\", entry.key(), e.what());\n                                    continue;\n                                }\n                            }\n\n                            localizations.try_emplace(LangConst::hash(entry.key()), std::move(value));\n                        }\n                    } catch (std::exception &e) {\n                        log::error(\"Failed to load localization file '{}': {}\", path.path, e.what());\n                    }\n                }\n\n                populateLocalization(definition.fallbackLanguageId, localizations);\n            }\n        }\n\n        void setLanguage(const LanguageId &languageId) {\n            if (languageId == \"native\") {\n                setLanguage(hex::getOSLanguage().value_or(FallbackLanguageId));\n                s_selectedLanguageId = languageId;\n                return;\n            }\n\n            if (*s_selectedLanguageId == languageId)\n                return;\n\n            s_localizations->clear();\n            s_selectedLanguageId = languageId;\n\n            populateLocalization(languageId, s_localizations);\n        }\n\n        [[nodiscard]] const std::string& getSelectedLanguageId() {\n            return *s_selectedLanguageId;\n        }\n\n        [[nodiscard]] const std::string& get(const LanguageId &languageId, const UnlocalizedString &unlocalizedString) {\n            static AutoReset<LanguageId> currentLanguageId;\n            static AutoReset<std::unordered_map<std::size_t, std::string>> loadedLocalization;\n            static std::mutex mutex;\n\n            std::scoped_lock lock(mutex);\n            if (*currentLanguageId != languageId) {\n                currentLanguageId = languageId;\n                loadedLocalization->clear();\n                populateLocalization(languageId, *loadedLocalization);\n            }\n\n            return (*loadedLocalization)[LangConst::hash(unlocalizedString.get())];\n        }\n\n        const std::map<std::string, LanguageDefinition>& getLanguageDefinitions() {\n            return *s_languageDefinitions;\n        }\n\n        const LanguageDefinition& getLanguageDefinition(const LanguageId &languageId) {\n            const auto bestMatch = findBestLanguageMatch(languageId);\n            const auto &result = (*s_languageDefinitions)[bestMatch];\n\n            if (!dbg::debugModeEnabled()) {\n                if (result.hidden)\n                    return getLanguageDefinition(result.fallbackLanguageId);\n            }\n\n            return result;\n        }\n\n    }\n\n    static AutoReset<std::map<std::size_t, std::string>> s_unlocalizedNames;\n\n    Lang::Lang(std::string_view unlocalizedString) : m_entryHash(LangConst::hash(unlocalizedString)) {\n        if (!s_unlocalizedNames->contains(m_entryHash)) [[unlikely]] {\n            s_unlocalizedNames->emplace(m_entryHash, unlocalizedString);\n        }\n    }\n    Lang::Lang(const char *unlocalizedString) : Lang(std::string_view(unlocalizedString)) { }\n    Lang::Lang(const std::string &unlocalizedString) : Lang(std::string_view(unlocalizedString)) { }\n    Lang::Lang(const LangConst &localizedString) : m_entryHash(localizedString.m_entryHash) {\n        if (!s_unlocalizedNames->contains(m_entryHash)) [[unlikely]] {\n            s_unlocalizedNames->emplace(m_entryHash, localizedString.m_unlocalizedString);\n        }\n    }\n    Lang::Lang(const UnlocalizedString &unlocalizedString) : Lang(unlocalizedString.get()) { }\n\n    Lang::operator std::string() const {\n        return get();\n    }\n\n    Lang::operator std::string_view() const {\n        return get();\n    }\n\n    Lang::operator const char *() const {\n        return get();\n    }\n\n    const char *Lang::get() const {\n        const auto &lang = *LocalizationManager::s_localizations;\n\n        const auto it = lang.find(m_entryHash);\n        if (it == lang.end()) {\n            if (auto unlocalizedIt = s_unlocalizedNames->find(m_entryHash); unlocalizedIt != s_unlocalizedNames->end()) {\n                return unlocalizedIt->second.c_str();\n            } else {\n                return \"<unlocalized>\";\n            }\n        } else {\n            return it->second.c_str();\n        }\n    }\n\n    LangConst::operator std::string() const {\n        return get();\n    }\n\n    LangConst::operator std::string_view() const {\n        return get();\n    }\n\n    LangConst::operator const char *() const {\n        return get();\n    }\n\n    const char *LangConst::get() const {\n        const auto &lang = *LocalizationManager::s_localizations;\n\n        const auto it = lang.find(m_entryHash);\n        if (it == lang.end()) {\n            return m_unlocalizedString;\n        } else {\n            return it->second.c_str();\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/api/plugin_manager.cpp",
    "content": "#include <hex/api/plugin_manager.hpp>\n#include <hex/api/imhex_api/system.hpp>\n\n#include <hex/helpers/logger.hpp>\n#include <hex/helpers/fmt.hpp>\n#include <hex/helpers/auto_reset.hpp>\n#include <hex/helpers/utils.hpp>\n#include <hex/helpers/default_paths.hpp>\n\n#include <wolv/utils/string.hpp>\n\n#include <filesystem>\n\n#if defined(OS_WINDOWS)\n    #include <windows.h>\n#else\n    #include <dlfcn.h>\n#endif\n\nnamespace hex {\n\n    static uintptr_t loadLibrary(const std::fs::path &path) {\n        #if defined(OS_WINDOWS)\n            auto handle = uintptr_t(LoadLibraryW(path.c_str()));\n\n            if (handle == uintptr_t(INVALID_HANDLE_VALUE) || handle == 0) {\n                log::error(\"Loading library '{}' failed: {} {}!\", wolv::util::toUTF8String(path.filename()), ::GetLastError(), hex::formatSystemError(::GetLastError()));\n                return 0;\n            }\n\n            return handle;\n        #else\n            const auto pathString = wolv::util::toUTF8String(path);\n\n            auto handle = uintptr_t(dlopen(pathString.c_str(), RTLD_NOLOAD));\n            if (handle == 0)\n                handle = uintptr_t(dlopen(pathString.c_str(), RTLD_NOW | RTLD_GLOBAL));\n\n            if (handle == 0) {\n                log::error(\"Loading library '{}' failed: {}!\", wolv::util::toUTF8String(path.filename()), dlerror());\n                return 0;\n            }\n\n            return handle;\n        #endif\n    }\n\n    static void unloadLibrary(uintptr_t handle, const std::fs::path &path) {\n        #if defined(OS_WINDOWS)\n            if (handle != 0) {\n                if (FreeLibrary(HMODULE(handle)) == FALSE) {\n                    log::error(\"Error when unloading library '{}': {}!\", wolv::util::toUTF8String(path.filename()), hex::formatSystemError(::GetLastError()));\n                }\n            }\n        #else\n            if (handle != 0) {\n                if (dlclose(reinterpret_cast<void*>(handle)) != 0) {\n                    log::error(\"Error when unloading library '{}': {}!\", path.filename().string(), dlerror());\n                }\n            }\n        #endif\n    }\n\n    Plugin::Plugin(const std::fs::path &path) : m_path(path) {\n        log::info(\"Loading plugin '{}'\", wolv::util::toUTF8String(path.filename()));\n\n        m_handle = loadLibrary(path);\n        if (m_handle == 0)\n            return;\n\n        const auto fileName = path.stem().string();\n\n        m_functions.initializePluginFunction        = getPluginFunction<PluginFunctions::InitializePluginFunc>(\"initializePlugin\");\n        m_functions.initializeLibraryFunction       = getPluginFunction<PluginFunctions::InitializePluginFunc>(fmt::format(\"initializeLibrary_{}\", fileName));\n        m_functions.getPluginNameFunction           = getPluginFunction<PluginFunctions::GetPluginNameFunc>(\"getPluginName\");\n        m_functions.getLibraryNameFunction          = getPluginFunction<PluginFunctions::GetLibraryNameFunc>(fmt::format(\"getLibraryName_{}\", fileName));\n        m_functions.getPluginAuthorFunction         = getPluginFunction<PluginFunctions::GetPluginAuthorFunc>(\"getPluginAuthor\");\n        m_functions.getPluginDescriptionFunction    = getPluginFunction<PluginFunctions::GetPluginDescriptionFunc>(\"getPluginDescription\");\n        m_functions.getCompatibleVersionFunction    = getPluginFunction<PluginFunctions::GetCompatibleVersionFunc>(\"getCompatibleVersion\");\n        m_functions.setImGuiContextFunction         = getPluginFunction<PluginFunctions::SetImGuiContextFunc>(\"setImGuiContext\");\n        m_functions.setImGuiContextLibraryFunction  = getPluginFunction<PluginFunctions::SetImGuiContextFunc>(fmt::format(\"setImGuiContext_{}\", fileName));\n        m_functions.getSubCommandsFunction          = getPluginFunction<PluginFunctions::GetSubCommandsFunc>(\"getSubCommands\");\n        m_functions.getFeaturesFunction             = getPluginFunction<PluginFunctions::GetSubCommandsFunc>(\"getFeatures\");\n        m_functions.isBuiltinPluginFunction         = getPluginFunction<PluginFunctions::IsBuiltinPluginFunc>(\"isBuiltinPlugin\");\n    }\n\n    Plugin::Plugin(const std::string &name, const hex::PluginFunctions &functions) :\n         m_path(name), m_addedManually(true), m_functions(functions) { }\n\n\n    Plugin::Plugin(Plugin &&other) noexcept {\n        m_handle = other.m_handle;\n        other.m_handle = 0;\n\n        m_path = std::move(other.m_path);\n        m_addedManually = other.m_addedManually;\n\n        m_functions = other.m_functions;\n        other.m_functions = {};\n\n        m_enabled = other.m_enabled;\n        m_initialized = other.m_initialized;\n    }\n\n    Plugin& Plugin::operator=(Plugin &&other) noexcept {\n        m_handle = other.m_handle;\n        other.m_handle = 0;\n\n        m_path = std::move(other.m_path);\n        m_addedManually = other.m_addedManually;\n\n        m_functions = other.m_functions;\n        other.m_functions = {};\n\n        m_enabled = other.m_enabled;\n        m_initialized = other.m_initialized;\n\n        return *this;\n    }\n\n    Plugin::~Plugin() {\n        unloadLibrary(m_handle, m_path);\n    }\n\n    bool Plugin::initializePlugin() const {\n        const auto pluginName = wolv::util::toUTF8String(m_path.filename());\n\n        if (this->isLibraryPlugin()) {\n            m_functions.initializeLibraryFunction();\n\n            log::info(\"Library '{}' initialized successfully\", pluginName);\n\n            m_initialized = true;\n            return true;\n        }\n\n        if (!m_enabled)\n            return true;\n\n        const auto requestedVersion = getCompatibleVersion();\n        const auto imhexVersion = ImHexApi::System::getImHexVersion().get();\n        if (!imhexVersion.starts_with(requestedVersion)) {\n            if (requestedVersion.empty()) {\n                log::warn(\"Plugin '{}' did not specify a compatible version, assuming it is compatible with the current version of ImHex.\", wolv::util::toUTF8String(m_path.filename()));\n            } else {\n                log::error(\"Refused to load plugin '{}' which was built for a different version of ImHex: '{}'\", wolv::util::toUTF8String(m_path.filename()), requestedVersion);\n                return false;\n            }\n        }\n\n        if (m_functions.initializePluginFunction != nullptr) {\n            try {\n                m_functions.initializePluginFunction();\n            } catch (const std::exception &e) {\n                log::error(\"Plugin '{}' threw an exception on init: {}\", pluginName, e.what());\n                return false;\n            } catch (...) {\n                log::error(\"Plugin '{}' threw an exception on init\", pluginName);\n                return false;\n            }\n        } else {\n            log::error(\"Plugin '{}' does not have a proper entrypoint\", pluginName);\n            return false;\n        }\n\n        log::info(\"Plugin '{}' initialized successfully\", pluginName);\n\n        m_initialized = true;\n        return true;\n    }\n\n    std::string Plugin::getPluginName() const {\n        if (m_functions.getPluginNameFunction != nullptr) {\n            return m_functions.getPluginNameFunction();\n        } else {\n            if (this->isLibraryPlugin())\n                return m_functions.getLibraryNameFunction();\n            else\n                return fmt::format(\"Unknown Plugin @ 0x{0:016X}\", m_handle);\n        }\n    }\n\n    std::string Plugin::getPluginAuthor() const {\n        if (m_functions.getPluginAuthorFunction != nullptr)\n            return m_functions.getPluginAuthorFunction();\n        else\n            return \"Unknown\";\n    }\n\n    std::string Plugin::getPluginDescription() const {\n        if (m_functions.getPluginDescriptionFunction != nullptr)\n            return m_functions.getPluginDescriptionFunction();\n        else\n            return \"\";\n    }\n\n    std::string Plugin::getCompatibleVersion() const {\n        if (m_functions.getCompatibleVersionFunction != nullptr)\n            return m_functions.getCompatibleVersionFunction();\n        else\n            return \"\";\n    }\n\n\n    void Plugin::setImGuiContext(ImGuiContext *ctx) const {\n        if (m_functions.setImGuiContextFunction != nullptr)\n            m_functions.setImGuiContextFunction(ctx);\n    }\n\n    const std::fs::path &Plugin::getPath() const {\n        return m_path;\n    }\n\n    bool Plugin::isLoaded() const {\n        return m_handle != 0;\n    }\n\n\n    bool Plugin::isValid() const {\n        return isLoaded() || m_functions.initializeLibraryFunction != nullptr || m_functions.initializePluginFunction != nullptr;\n    }\n\n    bool Plugin::isInitialized() const {\n        return m_initialized;\n    }\n\n    bool Plugin::isBuiltinPlugin() const {\n        return m_functions.isBuiltinPluginFunction != nullptr && m_functions.isBuiltinPluginFunction();\n    }\n\n\n    std::span<SubCommand> Plugin::getSubCommands() const {\n        if (m_functions.getSubCommandsFunction != nullptr) {\n            const auto result = m_functions.getSubCommandsFunction();\n            if (result == nullptr)\n                return { };\n\n            return *static_cast<std::vector<SubCommand>*>(result);\n        } else {\n            return { };\n        }\n    }\n\n    std::span<Feature> Plugin::getFeatures() const {\n        if (m_functions.getFeaturesFunction != nullptr) {\n            const auto result = m_functions.getFeaturesFunction();\n            if (result == nullptr)\n                return { };\n\n            return *static_cast<std::vector<Feature>*>(result);\n        } else {\n            return { };\n        }\n    }\n\n    bool Plugin::isLibraryPlugin() const {\n        return m_functions.initializeLibraryFunction != nullptr &&\n               m_functions.initializePluginFunction  == nullptr;\n    }\n\n    bool Plugin::wasAddedManually() const {\n        return m_addedManually;\n    }\n\n    void* Plugin::getPluginFunction(const std::string &symbol) const {\n        #if defined(OS_WINDOWS)\n            return reinterpret_cast<void *>(GetProcAddress(HMODULE(m_handle), symbol.c_str()));\n        #else\n            return dlsym(reinterpret_cast<void*>(m_handle), symbol.c_str());\n        #endif\n    }\n\n    void Plugin::setEnabled(bool enabled) {\n        m_enabled = enabled;\n    }\n\n\n\n    AutoReset<std::vector<std::fs::path>> PluginManager::s_pluginPaths, PluginManager::s_pluginLoadPaths;\n\n    void PluginManager::addLoadPath(const std::fs::path& path) {\n        s_pluginLoadPaths->emplace_back(path);\n    }\n\n\n    bool PluginManager::load() {\n        bool success = true;\n        for (const auto &loadPath : getPluginLoadPaths())\n            success = PluginManager::load(loadPath) && success;\n\n        return success;\n    }\n\n\n    bool PluginManager::load(const std::fs::path &pluginFolder) {\n        if (!wolv::io::fs::exists(pluginFolder))\n            return false;\n\n        s_pluginPaths->push_back(pluginFolder);\n\n        // Load library plugins first\n        for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) {\n            if (pluginPath.is_regular_file() && pluginPath.path().extension() == \".hexpluglib\") {\n                if (!isPluginLoaded(pluginPath.path())) {\n                    getPluginsMutable().emplace_back(pluginPath.path());\n                }\n            }\n        }\n\n        // Load regular plugins afterward\n        for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) {\n            if (pluginPath.is_regular_file() && pluginPath.path().extension() == \".hexplug\") {\n                if (!isPluginLoaded(pluginPath.path())) {\n                    getPluginsMutable().emplace_back(pluginPath.path());\n                }\n            }\n        }\n\n        std::erase_if(getPluginsMutable(), [](const Plugin &plugin) {\n            return !plugin.isValid();\n        });\n\n        // Move the built-in plugins to the front of the list so they get initialized first\n        getPluginsMutable().sort([](const Plugin &a, const Plugin &b) {\n            if (a.isBuiltinPlugin() && !b.isBuiltinPlugin()) {\n                return true;\n            } else if (!a.isBuiltinPlugin() && b.isBuiltinPlugin()) {\n                return false;\n            } else {\n                return a.getPluginName() < b.getPluginName();\n            }\n        });\n\n        return true;\n    }\n\n    AutoReset<std::vector<uintptr_t>> PluginManager::s_loadedLibraries;\n\n    bool PluginManager::loadLibraries() {\n        bool success = true;\n        for (const auto &loadPath : paths::Libraries.read())\n            success = PluginManager::loadLibraries(loadPath) && success;\n\n        return success;\n    }\n\n    bool PluginManager::loadLibraries(const std::fs::path& libraryFolder) {\n        bool success = true;\n        for (const auto &entry : std::fs::directory_iterator(libraryFolder)) {\n            if (!(entry.path().extension() == \".dll\" || entry.path().extension() == \".so\" || entry.path().extension() == \".dylib\"))\n                continue;\n\n            auto handle = loadLibrary(entry);\n            if (handle == 0) {\n                success = false;\n            }\n\n            PluginManager::s_loadedLibraries->push_back(handle);\n        }\n\n        return success;\n    }\n\n\n\n    void PluginManager::initializeNewPlugins() {\n        for (const auto &plugin : getPlugins()) {\n            if (!plugin.isInitialized())\n                std::ignore = plugin.initializePlugin();\n        }\n    }\n\n    void PluginManager::unload() {\n        s_pluginPaths->clear();\n\n        // Unload plugins in reverse order\n        auto &plugins = getPluginsMutable();\n\n        std::list<Plugin> savedPlugins;\n        while (!plugins.empty()) {\n            if (plugins.back().wasAddedManually())\n                savedPlugins.emplace_front(std::move(plugins.back()));\n            plugins.pop_back();\n        }\n\n        while (!s_loadedLibraries->empty()) {\n            unloadLibrary(s_loadedLibraries->back(), \"\");\n            s_loadedLibraries->pop_back();\n        }\n\n        getPluginsMutable() = std::move(savedPlugins);\n    }\n\n    void PluginManager::addPlugin(const std::string &name, hex::PluginFunctions functions) {\n        getPluginsMutable().emplace_back(name, functions);\n    }\n\n    const std::list<Plugin>& PluginManager::getPlugins() {\n        return getPluginsMutable();\n    }\n\n\n    std::list<Plugin>& PluginManager::getPluginsMutable() {\n        static std::list<Plugin> plugins;\n        return plugins;\n    }\n\n    Plugin* PluginManager::getPlugin(const std::string &name) {\n        for (auto &plugin : getPluginsMutable()) {\n            if (plugin.getPluginName() == name)\n                return &plugin;\n        }\n\n        return nullptr;\n    }\n\n    const std::vector<std::fs::path>& PluginManager::getPluginPaths() {\n        return s_pluginPaths;\n    }\n\n    const std::vector<std::fs::path>& PluginManager::getPluginLoadPaths() {\n        return s_pluginLoadPaths;\n    }\n\n    bool PluginManager::isPluginLoaded(const std::fs::path &path) {\n        return std::ranges::any_of(getPlugins(), [&path](const Plugin &plugin) {\n            return plugin.getPath().filename() == path.filename();\n        });\n    }\n\n    void PluginManager::setPluginEnabled(const Plugin &plugin, bool enabled) {\n        auto &plugins = getPluginsMutable();\n        auto it = std::ranges::find_if(plugins, [&plugin](const Plugin &p) {\n            return &plugin == &p;\n        });\n\n        if (it != plugins.end()) {\n            it->setEnabled(enabled);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/api/project_file_manager.cpp",
    "content": "#include <hex/api/project_file_manager.hpp>\n\n#include <hex/helpers/auto_reset.hpp>\n\n#include <wolv/io/fs.hpp>\n\nnamespace hex {\n\n    namespace {\n\n        AutoReset<std::vector<ProjectFile::Handler>> s_handlers;\n        AutoReset<std::vector<ProjectFile::ProviderHandler>> s_providerHandlers;\n\n        AutoReset<std::fs::path> s_currProjectPath;\n\n        AutoReset<std::function<bool(const std::fs::path&)>> s_loadProjectFunction;\n        AutoReset<std::function<bool(std::optional<std::fs::path>, bool)>> s_storeProjectFunction;\n\n    }\n\n\n    void ProjectFile::setProjectFunctions(\n            const std::function<bool(const std::fs::path&)> &loadFun,\n            const std::function<bool(std::optional<std::fs::path>, bool)> &storeFun\n    ) {\n        s_loadProjectFunction = loadFun;\n        s_storeProjectFunction = storeFun;\n    }\n\n    bool ProjectFile::load(const std::fs::path &filePath) {\n      return (*s_loadProjectFunction)(filePath);\n    }\n\n    bool ProjectFile::store(std::optional<std::fs::path> filePath, bool updateLocation) {\n       return (*s_storeProjectFunction)(std::move(filePath), updateLocation);\n    }\n\n    bool ProjectFile::hasPath() {\n        return !s_currProjectPath->empty();\n    }\n\n    void ProjectFile::clearPath() {\n        s_currProjectPath->clear();\n    }\n\n    std::fs::path ProjectFile::getPath() {\n        return *s_currProjectPath;\n    }\n\n    void ProjectFile::setPath(const std::fs::path &path) {\n        s_currProjectPath = path;\n    }\n\n    void ProjectFile::registerHandler(const Handler &handler) {\n        s_handlers->push_back(handler);\n    }\n\n    void ProjectFile::registerPerProviderHandler(const ProviderHandler &handler) {\n        s_providerHandlers->push_back(handler);\n    }\n\n    const std::vector<ProjectFile::Handler>& ProjectFile::getHandlers() {\n        return s_handlers;\n    }\n\n    const std::vector<ProjectFile::ProviderHandler>& ProjectFile::getProviderHandlers() {\n        return s_providerHandlers;\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/api/shortcut_manager.cpp",
    "content": "#include <hex/api/shortcut_manager.hpp>\n#include <imgui.h>\n#include <hex/api/content_registry/user_interface.hpp>\n#include <hex/api/task_manager.hpp>\n\n#include <hex/helpers/auto_reset.hpp>\n#include <hex/helpers/utils.hpp>\n\n#include <hex/ui/view.hpp>\n\n#include <imgui_internal.h>\n\nnamespace hex {\n\n    namespace {\n\n        AutoReset<std::map<Shortcut, ShortcutManager::ShortcutEntry>> s_globalShortcuts;\n        std::atomic<bool> s_paused;\n        std::optional<Shortcut> s_prevShortcut;\n        bool s_macOSMode = false;\n        AutoReset<std::optional<UnlocalizedString>> s_lastShortcutMainMenu;\n\n    }\n\n    Shortcut operator+(const Key &lhs, const Key &rhs) {\n        Shortcut result;\n        result.m_keys = { lhs, rhs };\n\n        return result;\n    }\n\n    Shortcut::Shortcut(Keys key) : m_keys({ key }) {\n\n    }\n    Shortcut::Shortcut(std::set<Key> keys) : m_keys(std::move(keys)) {\n\n    }\n\n    Shortcut Shortcut::operator+(const Key &other) const {\n        Shortcut result = *this;\n        result.m_keys.insert(other);\n\n        return result;\n    }\n\n    Shortcut& Shortcut::operator+=(const Key &other) {\n        m_keys.insert(other);\n\n        return *this;\n    }\n\n    bool Shortcut::operator<(const Shortcut &other) const {\n        return m_keys < other.m_keys;\n    }\n\n    bool Shortcut::operator==(const Shortcut &other) const {\n        return m_keys == other.m_keys;\n    }\n\n    bool Shortcut::isLocal() const {\n        return m_keys.contains(CurrentView);\n    }\n\n    const std::set<Key>& Shortcut::getKeys() const {\n        return m_keys;\n    }\n\n    bool Shortcut::has(Key key) const {\n        return m_keys.contains(key);\n    }\n\n    bool Shortcut::matches(const Shortcut& other) const {\n        auto left = this->m_keys;\n        auto right = other.m_keys;\n\n        left.erase(CurrentView);\n        left.erase(AllowWhileTyping);\n        right.erase(CurrentView);\n        right.erase(AllowWhileTyping);\n\n        return left == right;\n    }\n\n\n    std::string Shortcut::toString() const {\n        std::string result;\n\n        const auto CTRL_NAME     = s_macOSMode ? \"⌃\" : \"CTRL\";\n        const auto ALT_NAME      = s_macOSMode ? \"⌥\" : \"ALT\";\n        const auto SHIFT_NAME    = s_macOSMode ? \"⇧\" : \"SHIFT\";\n        const auto SUPER_NAME    = s_macOSMode ? \"⌘\" : \"SUPER\";\n        const auto Concatination = s_macOSMode ? \" \" : \" + \";\n\n        auto keys = m_keys;\n        if (keys.erase(CTRL) > 0 || (!s_macOSMode && keys.erase(CTRLCMD) > 0)) {\n            result += CTRL_NAME;\n            result += Concatination;\n        }\n        if (keys.erase(ALT) > 0) {\n            result += ALT_NAME;\n            result += Concatination;\n        }\n        if (keys.erase(SHIFT) > 0) {\n            result += SHIFT_NAME;\n            result += Concatination;\n        }\n        if (keys.erase(SUPER) > 0 || (s_macOSMode && keys.erase(CTRLCMD) > 0)) {\n            result += SUPER_NAME;\n            result += Concatination;\n        }\n        keys.erase(CurrentView);\n\n        for (const auto &key : keys) {\n            switch (Keys(key.getKeyCode())) {\n                case Keys::Space:           result += \"⎵\";              break;\n                case Keys::Apostrophe:      result += \"'\";              break;\n                case Keys::Comma:           result += \",\";              break;\n                case Keys::Minus:           result += \"-\";              break;\n                case Keys::Period:          result += \".\";              break;\n                case Keys::Slash:           result += \"/\";              break;\n                case Keys::Num0:            result += \"0\";              break;\n                case Keys::Num1:            result += \"1\";              break;\n                case Keys::Num2:            result += \"2\";              break;\n                case Keys::Num3:            result += \"3\";              break;\n                case Keys::Num4:            result += \"4\";              break;\n                case Keys::Num5:            result += \"5\";              break;\n                case Keys::Num6:            result += \"6\";              break;\n                case Keys::Num7:            result += \"7\";              break;\n                case Keys::Num8:            result += \"8\";              break;\n                case Keys::Num9:            result += \"9\";              break;\n                case Keys::Semicolon:       result += \";\";              break;\n                case Keys::Equals:          result += \"=\";              break;\n                case Keys::A:               result += \"A\";              break;\n                case Keys::B:               result += \"B\";              break;\n                case Keys::C:               result += \"C\";              break;\n                case Keys::D:               result += \"D\";              break;\n                case Keys::E:               result += \"E\";              break;\n                case Keys::F:               result += \"F\";              break;\n                case Keys::G:               result += \"G\";              break;\n                case Keys::H:               result += \"H\";              break;\n                case Keys::I:               result += \"I\";              break;\n                case Keys::J:               result += \"J\";              break;\n                case Keys::K:               result += \"K\";              break;\n                case Keys::L:               result += \"L\";              break;\n                case Keys::M:               result += \"M\";              break;\n                case Keys::N:               result += \"N\";              break;\n                case Keys::O:               result += \"O\";              break;\n                case Keys::P:               result += \"P\";              break;\n                case Keys::Q:               result += \"Q\";              break;\n                case Keys::R:               result += \"R\";              break;\n                case Keys::S:               result += \"S\";              break;\n                case Keys::T:               result += \"T\";              break;\n                case Keys::U:               result += \"U\";              break;\n                case Keys::V:               result += \"V\";              break;\n                case Keys::W:               result += \"W\";              break;\n                case Keys::X:               result += \"X\";              break;\n                case Keys::Y:               result += \"Y\";              break;\n                case Keys::Z:               result += \"Z\";              break;\n                case Keys::LeftBracket:     result += \"[\";              break;\n                case Keys::Backslash:       result += \"\\\\\";             break;\n                case Keys::RightBracket:    result += \"]\";              break;\n                case Keys::GraveAccent:     result += \"`\";              break;\n                case Keys::World1:          result += \"WORLD1\";         break;\n                case Keys::World2:          result += \"WORLD2\";         break;\n                case Keys::Escape:          result += \"ESC\";            break;\n                case Keys::Enter:           result += \"⏎\";              break;\n                case Keys::Tab:             result += \"⇥\";              break;\n                case Keys::Backspace:       result += \"⌫\";              break;\n                case Keys::Insert:          result += \"INSERT\";         break;\n                case Keys::Delete:          result += \"DELETE\";         break;\n                case Keys::Right:           result += \"RIGHT\";          break;\n                case Keys::Left:            result += \"LEFT\";           break;\n                case Keys::Down:            result += \"DOWN\";           break;\n                case Keys::Up:              result += \"UP\";             break;\n                case Keys::PageUp:          result += \"PAGEUP\";         break;\n                case Keys::PageDown:        result += \"PAGEDOWN\";       break;\n                case Keys::Home:            result += \"HOME\";           break;\n                case Keys::End:             result += \"END\";            break;\n                case Keys::CapsLock:        result += \"⇪\";              break;\n                case Keys::ScrollLock:      result += \"SCROLLLOCK\";     break;\n                case Keys::NumLock:         result += \"NUMLOCK\";        break;\n                case Keys::PrintScreen:     result += \"PRINTSCREEN\";    break;\n                case Keys::Pause:           result += \"PAUSE\";          break;\n                case Keys::F1:              result += \"F1\";             break;\n                case Keys::F2:              result += \"F2\";             break;\n                case Keys::F3:              result += \"F3\";             break;\n                case Keys::F4:              result += \"F4\";             break;\n                case Keys::F5:              result += \"F5\";             break;\n                case Keys::F6:              result += \"F6\";             break;\n                case Keys::F7:              result += \"F7\";             break;\n                case Keys::F8:              result += \"F8\";             break;\n                case Keys::F9:              result += \"F9\";             break;\n                case Keys::F10:             result += \"F10\";            break;\n                case Keys::F11:             result += \"F11\";            break;\n                case Keys::F12:             result += \"F12\";            break;\n                case Keys::F13:             result += \"F13\";            break;\n                case Keys::F14:             result += \"F14\";            break;\n                case Keys::F15:             result += \"F15\";            break;\n                case Keys::F16:             result += \"F16\";            break;\n                case Keys::F17:             result += \"F17\";            break;\n                case Keys::F18:             result += \"F18\";            break;\n                case Keys::F19:             result += \"F19\";            break;\n                case Keys::F20:             result += \"F20\";            break;\n                case Keys::F21:             result += \"F21\";            break;\n                case Keys::F22:             result += \"F22\";            break;\n                case Keys::F23:             result += \"F23\";            break;\n                case Keys::F24:             result += \"F24\";            break;\n                case Keys::F25:             result += \"F25\";            break;\n                case Keys::KeyPad0:         result += \"KP0\";            break;\n                case Keys::KeyPad1:         result += \"KP1\";            break;\n                case Keys::KeyPad2:         result += \"KP2\";            break;\n                case Keys::KeyPad3:         result += \"KP3\";            break;\n                case Keys::KeyPad4:         result += \"KP4\";            break;\n                case Keys::KeyPad5:         result += \"KP5\";            break;\n                case Keys::KeyPad6:         result += \"KP6\";            break;\n                case Keys::KeyPad7:         result += \"KP7\";            break;\n                case Keys::KeyPad8:         result += \"KP8\";            break;\n                case Keys::KeyPad9:         result += \"KP9\";            break;\n                case Keys::KeyPadDecimal:   result += \"KPDECIMAL\";      break;\n                case Keys::KeyPadDivide:    result += \"KPDIVIDE\";       break;\n                case Keys::KeyPadMultiply:  result += \"KPMULTIPLY\";     break;\n                case Keys::KeyPadSubtract:  result += \"KPSUBTRACT\";     break;\n                case Keys::KeyPadAdd:       result += \"KPADD\";          break;\n                case Keys::KeyPadEnter:     result += \"KPENTER\";        break;\n                case Keys::KeyPadEqual:     result += \"KPEQUAL\";        break;\n                case Keys::Menu:            result += \"MENU\";           break;\n                default:\n                    continue;\n            }\n\n            result += Concatination;\n        }\n\n        if (result.ends_with(Concatination))\n            result = result.substr(0, result.size() - strlen(Concatination));\n\n        return result;\n    }\n\n    KeyEquivalent Shortcut::toKeyEquivalent() const {\n        #if defined(OS_MACOS)\n            if (*this == None)\n                return { };\n\n            KeyEquivalent result = {};\n            result.valid = true;\n\n            for (const auto &key : m_keys) {\n                switch (key.getKeyCode()) {\n                    case CTRL.getKeyCode():\n                        result.ctrl = true;\n                        break;\n                    case SHIFT.getKeyCode():\n                        result.shift = true;\n                        break;\n                    case ALT.getKeyCode():\n                        result.opt = true;\n                        break;\n                    case SUPER.getKeyCode():\n                    case CTRLCMD.getKeyCode():\n                        result.cmd = true;\n                        break;\n                    case CurrentView.getKeyCode():\n                    case AllowWhileTyping.getKeyCode():\n                    case ShowOnWelcomeScreen.getKeyCode():\n                        // Ignore flags that are only used internally by the ShortcutManager\n                        break;\n                    default:\n                        macosGetKey(Keys(key.getKeyCode()), &result.key);\n                        break;\n                }\n            }\n\n            return result;\n        #else\n            return { };\n        #endif\n    }\n\n\n\n    void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const std::vector<UnlocalizedString> &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {\n        log::debug(\"Adding global shortcut {} for {}\", shortcut.toString(), unlocalizedName.back().get());\n        auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { .shortcut=shortcut, .unlocalizedName=unlocalizedName, .callback=callback, .enabledCallback=enabledCallback } });\n        if (!inserted) log::error(\"Failed to add shortcut!\");\n    }\n\n    void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {\n        log::debug(\"Adding global shortcut {} for {}\", shortcut.toString(), unlocalizedName.get());\n        auto [it, inserted] = s_globalShortcuts->insert({ shortcut, { .shortcut=shortcut, .unlocalizedName={ unlocalizedName }, .callback=callback, .enabledCallback=enabledCallback } });\n        if (!inserted) log::error(\"Failed to add shortcut!\");\n    }\n\n    void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const std::vector<UnlocalizedString> &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {\n        log::debug(\"Adding shortcut {} for {}\", shortcut.toString(), unlocalizedName.back().get());\n        auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { .shortcut=shortcut, .unlocalizedName=unlocalizedName, .callback=callback, .enabledCallback=enabledCallback } });\n        if (!inserted) log::error(\"Failed to add shortcut!\");\n    }\n\n    void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback, const EnabledCallback &enabledCallback) {\n        log::debug(\"Adding shortcut {} for {}\", shortcut.toString(), unlocalizedName.get());\n        auto [it, inserted] = view->m_shortcuts.insert({ shortcut + CurrentView, { .shortcut=shortcut, .unlocalizedName={ unlocalizedName }, .callback=callback, .enabledCallback=enabledCallback } });\n        if (!inserted) log::error(\"Failed to add shortcut!\");\n    }\n\n    static Shortcut getShortcut(bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) {\n        Shortcut pressedShortcut;\n\n        if (ctrl)\n            pressedShortcut += s_macOSMode ? CTRL : CTRLCMD;\n        if (alt)\n            pressedShortcut += ALT;\n        if (shift)\n            pressedShortcut += SHIFT;\n        if (super)\n            pressedShortcut += s_macOSMode ? CTRLCMD : SUPER;\n        if (focused)\n            pressedShortcut += CurrentView;\n\n        pressedShortcut += scanCodeToKey(keyCode);\n\n        return pressedShortcut;\n    }\n\n    static bool processShortcut(Shortcut shortcut, const std::map<Shortcut, ShortcutManager::ShortcutEntry> &shortcuts) {\n        if (s_paused)\n            return true;\n\n        if (ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopupId))\n            return true;\n\n        auto it = shortcuts.end();\n        if (ImGui::GetIO().WantTextInput) {\n            it = shortcuts.find(shortcut + AllowWhileTyping);\n        } else {\n            it = shortcuts.find(shortcut);\n            if (it == shortcuts.end())\n                it = shortcuts.find(shortcut + AllowWhileTyping);\n        }\n\n        if (it != shortcuts.end()) {\n            const auto &[foundShortcut, entry] = *it;\n\n            if (entry.enabledCallback()) {\n                entry.callback();\n\n                if (!entry.unlocalizedName.empty()) {\n                    s_lastShortcutMainMenu = entry.unlocalizedName.front();\n                }\n\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    bool ShortcutManager::runShortcut(const Shortcut &shortcut, const View *view) {\n        if (view == nullptr)\n            return processShortcut(shortcut, s_globalShortcuts);\n        else\n            return processShortcut(shortcut, view->m_shortcuts);\n    }\n\n    void ShortcutManager::process(const View *currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) {\n        const Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode);\n        if (keyCode != 0)\n            s_prevShortcut = Shortcut(pressedShortcut.getKeys());\n\n        std::set<const View*> processedViews;\n        while (true) {\n            if (runShortcut(pressedShortcut, currentView)) {\n                break;\n            }\n\n            processedViews.insert(currentView);\n            currentView = currentView->getMenuItemInheritView();\n            if (currentView == nullptr || processedViews.contains(currentView))\n                break;\n        }\n    }\n\n    void ShortcutManager::processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode) {\n        const Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, false, keyCode);\n        if (keyCode != 0)\n            s_prevShortcut = Shortcut(pressedShortcut.getKeys());\n\n        runShortcut(pressedShortcut);\n    }\n\n    std::optional<UnlocalizedString> ShortcutManager::getLastActivatedMenu() {\n        return *s_lastShortcutMainMenu;\n    }\n\n    void ShortcutManager::resetLastActivatedMenu() {\n        s_lastShortcutMainMenu->reset();\n    }\n\n\n    void ShortcutManager::clearShortcuts() {\n        s_globalShortcuts->clear();\n    }\n\n    Shortcut ShortcutManager::getShortcutByName(const std::vector<UnlocalizedString> &unlocalizedName, const View *view) {\n        if (view != nullptr) {\n            for (const auto &[shortcut, entry] : view->m_shortcuts) {\n                if (entry.unlocalizedName == unlocalizedName) {\n                    return entry.shortcut;\n                }\n            }\n        } else {\n            for (const auto &[shortcut, entry] : *s_globalShortcuts) {\n                if (entry.unlocalizedName == unlocalizedName) {\n                    return entry.shortcut;\n                }\n            }\n        }\n\n        return Shortcut::None;\n    }\n\n    void ShortcutManager::resumeShortcuts() {\n        s_paused = false;\n    }\n\n    void ShortcutManager::pauseShortcuts() {\n        s_paused = true;\n        s_prevShortcut.reset();\n    }\n\n    std::optional<Shortcut> ShortcutManager::getPreviousShortcut() {\n        return s_prevShortcut;\n    }\n\n    std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getGlobalShortcuts() {\n        std::vector<ShortcutEntry> result;\n\n        for (auto &[shortcut, entry] : *s_globalShortcuts)\n            result.push_back(entry);\n\n        return result;\n    }\n\n    std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getViewShortcuts(const View *view) {\n        std::vector<ShortcutEntry> result;\n\n        result.reserve(view->m_shortcuts.size());\n        for (auto &[shortcut, entry] : view->m_shortcuts)\n            result.push_back(entry);\n\n        return result;\n    }\n\n    static bool updateShortcutImpl(const Shortcut &oldShortcut, const Shortcut &newShortcut, std::map<Shortcut, ShortcutManager::ShortcutEntry> &shortcuts) {\n        if (shortcuts.contains(oldShortcut)) {\n            if (shortcuts.contains(newShortcut))\n                return false;\n\n            shortcuts[newShortcut] = shortcuts[oldShortcut];\n            shortcuts[newShortcut].shortcut = newShortcut;\n            shortcuts.erase(oldShortcut);\n        }\n\n        return true;\n    }\n\n    bool ShortcutManager::updateShortcut(const Shortcut &oldShortcut, Shortcut newShortcut, View *view) {\n        if (oldShortcut.matches(newShortcut))\n            return true;\n\n        if (oldShortcut.has(AllowWhileTyping))\n            newShortcut += AllowWhileTyping;\n\n        bool result;\n        if (view != nullptr) {\n            result = updateShortcutImpl(oldShortcut + CurrentView, newShortcut + CurrentView , view->m_shortcuts);\n        } else {\n            result = updateShortcutImpl(oldShortcut, newShortcut, s_globalShortcuts);\n        }\n\n        if (result) {\n            for (auto &[priority, menuItem] : ContentRegistry::UserInterface::impl::getMenuItemsMutable()) {\n                if (menuItem.view == view && menuItem.shortcut == oldShortcut) {\n                    menuItem.shortcut = newShortcut;\n                    break;\n                }\n            }\n        }\n\n        return result;\n    }\n\n    void ShortcutManager::enableMacOSMode() {\n        s_macOSMode = true;\n    }\n\n\n}"
  },
  {
    "path": "lib/libimhex/source/api/task_manager.cpp",
    "content": "#include <hex/api/task_manager.hpp>\n\n#include <hex/api/localization_manager.hpp>\n#include <hex/helpers/logger.hpp>\n\n#include <algorithm>\n#include <ranges>\n\n#include <jthread.hpp>\n#include <hex/helpers/debugging.hpp>\n#include <hex/trace/exceptions.hpp>\n#include <utility>\n\n#if defined(OS_WINDOWS)\n    #include <windows.h>\n    #include <processthreadsapi.h>\n    #include <hex/helpers/utils.hpp>\n#else\n    #include <pthread.h>\n#endif\n\nnamespace {\n\n    struct SourceLocationWrapper {\n        std::source_location location;\n\n        bool operator==(const SourceLocationWrapper &other) const {\n            return location.file_name() == other.location.file_name() &&\n                   location.function_name() == other.location.function_name() &&\n                   location.column() == other.location.column() &&\n                   location.line() == other.location.line();\n        }\n    };\n\n}\n\ntemplate<>\nstruct std::hash<SourceLocationWrapper> {\n    std::size_t operator()(const SourceLocationWrapper& s) const noexcept {\n        auto h1 = std::hash<std::string>{}(s.location.file_name());\n        auto h2 = std::hash<std::string>{}(s.location.function_name());\n        auto h3 = std::hash<u32>{}(s.location.column());\n        auto h4 = std::hash<u32>{}(s.location.line());\n        return (h1 << 0) ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3);\n    }\n};\n\nnamespace hex {\n\n    namespace {\n\n        std::recursive_mutex s_deferredCallsMutex, s_tasksFinishedMutex;\n\n        std::list<std::shared_ptr<Task>> s_tasks, s_taskQueue;\n        std::list<std::function<void()>> s_deferredCalls;\n        std::unordered_map<SourceLocationWrapper, std::function<void()>> s_onceDeferredCalls;\n        std::list<std::function<void()>> s_tasksFinishedCallbacks;\n        std::list<std::function<void(Task&)>> s_taskCompletionCallbacks;\n\n        std::mutex s_queueMutex;\n        std::condition_variable s_jobCondVar;\n        std::vector<std::jthread> s_workers;\n\n        thread_local std::array<char, 256> s_currentThreadName;\n        thread_local Task* s_currentTask = nullptr;\n        std::thread::id s_mainThreadId;\n\n    }\n\n\n    Task::Task(UnlocalizedString unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function)\n    : m_unlocalizedName(std::move(unlocalizedName)),\n      m_maxValue(maxValue),\n      m_function(std::move(function)),\n      m_background(background), m_blocking(blocking) { }\n\n    Task::Task(hex::Task &&other) noexcept {\n        {\n            std::scoped_lock thisLock(m_mutex);\n            std::scoped_lock otherLock(other.m_mutex);\n\n            m_function = std::move(other.m_function);\n            m_unlocalizedName = std::move(other.m_unlocalizedName);\n        }\n\n        m_maxValue    = u64(other.m_maxValue);\n        m_currValue   = u64(other.m_currValue);\n\n        if (other.m_finished.test())\n            m_finished.test_and_set();\n        if (other.m_hadException.test())\n            m_hadException.test_and_set();\n        if (other.m_interrupted.test())\n            m_interrupted.test_and_set();\n\n        m_finished.notify_all();\n        m_hadException.notify_all();\n        m_interrupted.notify_all();\n\n        m_shouldInterrupt = bool(other.m_shouldInterrupt);\n    }\n\n    Task::~Task() {\n        if (!this->isFinished())\n            this->interrupt();\n    }\n\n    void Task::update(u64 value) {\n        // Update the current progress value of the task\n        m_currValue.store(value, std::memory_order_relaxed);\n\n        // Check if the task has been interrupted by the main thread and if yes,\n        // throw an exception that is generally not caught by the task\n        if (m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]]\n            throw TaskInterruptor();\n    }\n\n    void Task::update() const {\n        if (m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]]\n            throw TaskInterruptor();\n    }\n\n    void Task::increment() {\n        m_currValue.fetch_add(1, std::memory_order_relaxed);\n\n        if (m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]]\n            throw TaskInterruptor();\n    }\n\n\n    void Task::setMaxValue(u64 value) {\n        m_maxValue = value;\n    }\n\n\n    void Task::interrupt() {\n        m_shouldInterrupt = true;\n\n        // Call the interrupt callback on the current thread if one is set\n        if (m_interruptCallback)\n            m_interruptCallback();\n    }\n\n    void Task::setInterruptCallback(std::function<void()> callback) {\n        m_interruptCallback = std::move(callback);\n    }\n\n    bool Task::isBackgroundTask() const {\n        return m_background;\n    }\n\n    bool Task::isBlocking() const {\n        return m_blocking;\n    }\n\n\n    bool Task::isFinished() const {\n        return m_finished.test();\n    }\n\n    bool Task::hadException() const {\n        return m_hadException.test();\n    }\n\n    bool Task::shouldInterrupt() const {\n        return m_shouldInterrupt;\n    }\n\n    bool Task::wasInterrupted() const {\n        return m_interrupted.test();\n    }\n\n    void Task::clearException() {\n        m_hadException.clear();\n    }\n\n    std::string Task::getExceptionMessage() const {\n        std::scoped_lock lock(m_mutex);\n\n        return m_exceptionMessage;\n    }\n\n    const UnlocalizedString &Task::getUnlocalizedName() {\n        return m_unlocalizedName;\n    }\n\n    u64 Task::getValue() const {\n        return m_currValue;\n    }\n\n    u64 Task::getMaxValue() const {\n        return m_maxValue;\n    }\n\n    void Task::wait() const {\n        m_finished.wait(false);\n    }\n\n    void Task::finish() {\n        m_finished.test_and_set();\n        m_finished.notify_all();\n    }\n\n    void Task::interruption() {\n        m_interrupted.test_and_set();\n        m_interrupted.notify_all();\n    }\n\n    void Task::exception(const char *message) {\n        std::scoped_lock lock(m_mutex);\n\n        // Store information about the caught exception\n        m_exceptionMessage = message;\n        m_hadException.test_and_set();\n        m_hadException.notify_all();\n\n        // Call the interrupt callback on the current thread if one is set\n        if (m_interruptCallback)\n            m_interruptCallback();\n    }\n\n\n    bool TaskHolder::isRunning() const {\n        const auto &task = m_task.lock();\n        if (!task)\n            return false;\n\n        return !task->isFinished();\n    }\n\n    bool TaskHolder::hadException() const {\n        const auto &task = m_task.lock();\n        if (!task)\n            return false;\n\n        return task->hadException();\n    }\n\n    bool TaskHolder::shouldInterrupt() const {\n        const auto &task = m_task.lock();\n        if (!task)\n            return false;\n\n        return task->shouldInterrupt();\n    }\n\n    bool TaskHolder::wasInterrupted() const {\n        const auto &task = m_task.lock();\n        if (!task)\n            return false;\n\n        return task->wasInterrupted();\n    }\n\n    void TaskHolder::interrupt() const {\n        const auto &task = m_task.lock();\n        if (!task)\n            return;\n\n        task->interrupt();\n    }\n\n    void TaskHolder::wait() const {\n        const auto &task = m_task.lock();\n        if (!task)\n            return;\n\n        task->wait();\n    }\n\n    u32 TaskHolder::getProgress() const {\n        const auto &task = m_task.lock();\n        if (!task)\n            return 0;\n\n        // If the max value is 0, the task has no progress\n        if (task->getMaxValue() == 0)\n            return 0;\n\n        // Calculate the progress of the task from 0 to 100\n        return u32((task->getValue() * 100) / task->getMaxValue());\n    }\n\n    void TaskManager::init() {\n        const auto threadCount = std::thread::hardware_concurrency();\n\n        log::debug(\"Initializing task manager thread pool with {} workers.\", threadCount);\n\n        // Create worker threads\n        for (u32 i = 0; i < threadCount; i++) {\n            s_workers.emplace_back([](const std::stop_token &stopToken) {\n                while (true) {\n                    std::shared_ptr<Task> task;\n\n                    // Set the thread name to \"Idle Task\" while waiting for a task\n                    TaskManager::setCurrentThreadName(\"Idle Task\");\n\n                    {\n                        // Wait for a task to be added to the queue\n                        std::unique_lock lock(s_queueMutex);\n                        s_jobCondVar.wait(lock, [&] {\n                            return !s_taskQueue.empty() || stopToken.stop_requested();\n                        });\n\n                        // Check if the thread should exit\n                        if (stopToken.stop_requested())\n                            break;\n\n                        // Grab the next task from the queue\n                        task = std::move(s_taskQueue.front());\n                        s_taskQueue.pop_front();\n\n                        s_currentTask = task.get();\n                    }\n\n                    try {\n                        trace::enableExceptionCaptureForCurrentThread();\n\n                        // Set the thread name to the name of the task\n                        TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName));\n\n                        // Execute the task\n                        task->m_function(*task);\n\n                        log::debug(\"Task '{}' finished\", task->m_unlocalizedName.get());\n\n                        {\n                            std::scoped_lock lock(s_tasksFinishedMutex);\n\n                            for (const auto &callback : s_taskCompletionCallbacks)\n                                callback(*task);\n                        }\n                    } catch (const Task::TaskInterruptor &) {\n                        // Handle the task being interrupted by user request\n                        task->interruption();\n                    } catch (const std::exception &e) {\n                        log::error(\"Exception in task '{}': {}\", task->m_unlocalizedName.get(), e.what());\n\n                        dbg::printStackTrace(trace::getStackTrace());\n\n                        // Handle the task throwing an uncaught exception\n                        task->exception(e.what());\n                    } catch (...) {\n                        log::error(\"Exception in task '{}'\", task->m_unlocalizedName.get());\n\n                        dbg::printStackTrace(trace::getStackTrace());\n\n                        // Handle the task throwing an uncaught exception of unknown type\n                        task->exception(\"Unknown Exception\");\n                    }\n\n                    trace::disableExceptionCaptureForCurrentThread();\n\n                    s_currentTask = nullptr;\n                    task->finish();\n                }\n            });\n        }\n    }\n\n    void TaskManager::exit() {\n        // Interrupt all tasks\n        for (const auto &task : s_tasks) {\n            task->interrupt();\n        }\n\n        // Ask worker threads to exit after finishing their task\n        for (auto &thread : s_workers)\n            thread.request_stop();\n\n        // Wake up all the idle worker threads so they can exit\n        {\n            std::unique_lock lock(s_queueMutex);\n            s_jobCondVar.notify_all();\n        }\n\n        // Wait for all worker threads to exit\n        s_workers.clear();\n\n        s_tasks.clear();\n        s_taskQueue.clear();\n\n        s_deferredCalls.clear();\n        s_onceDeferredCalls.clear();\n        s_tasksFinishedCallbacks.clear();\n        s_taskCompletionCallbacks.clear();\n    }\n\n    TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task&)> function) {\n        std::scoped_lock lock(s_queueMutex);\n\n        // Construct new task\n        auto task = std::make_shared<Task>(unlocalizedName, maxValue, background, blocking, std::move(function));\n\n        s_tasks.emplace_back(task);\n\n        // Add task to the queue for the worker to pick up\n        s_taskQueue.emplace_back(std::move(task));\n\n        s_jobCondVar.notify_one();\n\n        return TaskHolder(s_tasks.back());\n    }\n\n\n    TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function<void(Task &)> function) {\n        log::debug(\"Creating task {}\", unlocalizedName.get());\n        return createTask(unlocalizedName, maxValue, false, false, std::move(function));\n    }\n\n    TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function<void()> function) {\n        log::debug(\"Creating task {}\", unlocalizedName.get());\n        return createTask(unlocalizedName, maxValue, false, false,\n            [function = std::move(function)](Task&) {\n                function();\n            }\n        );\n    }\n\n    TaskHolder TaskManager::createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function<void(Task &)> function) {\n        log::debug(\"Creating background task {}\", unlocalizedName.get());\n        return createTask(unlocalizedName, 0, true, false, std::move(function));\n    }\n\n    TaskHolder TaskManager::createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function<void()> function) {\n        log::debug(\"Creating background task {}\", unlocalizedName.get());\n        return createTask(unlocalizedName, 0, true, false,\n            [function = std::move(function)](Task&) {\n                function();\n            }\n        );\n    }\n\n    TaskHolder TaskManager::createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function<void(Task &)> function) {\n        log::debug(\"Creating blocking task {}\", unlocalizedName.get());\n        return createTask(unlocalizedName, maxValue, true, true, std::move(function));\n    }\n\n    TaskHolder TaskManager::createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function<void()> function) {\n        log::debug(\"Creating blocking task {}\", unlocalizedName.get());\n        return createTask(unlocalizedName, maxValue, true, true,\n            [function = std::move(function)](Task&) {\n                function();\n            }\n        );\n    }\n\n    void TaskManager::collectGarbage() {\n        {\n            std::scoped_lock lock(s_queueMutex);\n            std::erase_if(s_tasks, [](const auto &task) {\n                return task->isFinished() && !task->hadException();\n            });\n        }\n\n        if (s_tasks.empty()) {\n            std::scoped_lock lock(s_deferredCallsMutex);\n            for (const auto &call : s_tasksFinishedCallbacks)\n                call();\n            s_tasksFinishedCallbacks.clear();\n        }\n\n    }\n\n    Task& TaskManager::getCurrentTask() {\n        return *s_currentTask;\n    }\n\n\n    const std::list<std::shared_ptr<Task>>& TaskManager::getRunningTasks() {\n        return s_tasks;\n    }\n\n    size_t TaskManager::getRunningTaskCount() {\n        std::scoped_lock lock(s_queueMutex);\n\n        return std::ranges::count_if(s_tasks, [](const auto &task){\n            return !task->isBackgroundTask();\n        });\n    }\n\n    size_t TaskManager::getRunningBackgroundTaskCount() {\n        std::scoped_lock lock(s_queueMutex);\n\n        return std::ranges::count_if(s_tasks, [](const auto &task){\n            return task->isBackgroundTask();\n        });\n    }\n\n    size_t TaskManager::getRunningBlockingTaskCount() {\n        std::scoped_lock lock(s_queueMutex);\n\n        return std::ranges::count_if(s_tasks, [](const auto &task){\n            return task->isBlocking();\n        });\n    }\n\n\n    void TaskManager::doLater(const std::function<void()> &function) {\n        std::scoped_lock lock(s_deferredCallsMutex);\n\n        s_deferredCalls.push_back(function);\n    }\n\n    void TaskManager::doLaterOnce(const std::function<void()> &function, std::source_location location) {\n        std::scoped_lock lock(s_deferredCallsMutex);\n\n        s_onceDeferredCalls[SourceLocationWrapper{ location }] = function;\n    }\n\n    void TaskManager::runDeferredCalls() {\n        std::scoped_lock lock(s_deferredCallsMutex);\n\n        while (!s_deferredCalls.empty()) {\n            auto callback = s_deferredCalls.front();\n            s_deferredCalls.pop_front();\n            callback();\n        }\n        while (!s_onceDeferredCalls.empty()) {\n            auto node = s_onceDeferredCalls.extract(s_onceDeferredCalls.begin());\n            node.mapped()();\n        }\n    }\n\n    void TaskManager::runWhenTasksFinished(const std::function<void()> &function) {\n        std::scoped_lock lock(s_tasksFinishedMutex);\n\n        for (const auto &task : s_tasks) {\n            task->interrupt();\n        }\n\n        s_tasksFinishedCallbacks.push_back(function);\n    }\n\n    void TaskManager::setCurrentThreadName(const std::string &name) {\n        std::ranges::fill(s_currentThreadName, '\\0');\n        std::ranges::copy(name | std::views::take(255), s_currentThreadName.begin());\n\n        #if defined(OS_WINDOWS)\n            using SetThreadDescriptionFunc =  HRESULT(WINAPI*)(HANDLE hThread, PCWSTR lpThreadDescription);\n\n            static auto setThreadDescription = reinterpret_cast<SetThreadDescriptionFunc>(\n                reinterpret_cast<uintptr_t>(\n                    ::GetProcAddress(\n                        ::GetModuleHandleW(L\"Kernel32.dll\"),\n                        \"SetThreadDescription\"\n                    )\n                )\n            );\n\n            if (setThreadDescription != nullptr) {\n                const auto longName = hex::utf8ToUtf16(name);\n                setThreadDescription(::GetCurrentThread(), longName.c_str());\n            } else {\n                struct THREADNAME_INFO {\n                    DWORD dwType;\n                    LPCSTR szName;\n                    DWORD dwThreadID;\n                    DWORD dwFlags;\n                };\n\n                THREADNAME_INFO info = { };\n                info.dwType = 0x1000;\n                info.szName = name.c_str();\n                info.dwThreadID = ::GetCurrentThreadId();\n                info.dwFlags = 0;\n\n                constexpr static DWORD MS_VC_EXCEPTION = 0x406D1388;\n                RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info));\n            }\n        #elif defined(OS_LINUX)\n                pthread_setname_np(pthread_self(), name.c_str());\n        #elif defined(OS_WEB)\n                std::ignore = name;\n        #elif defined(OS_MACOS)\n                pthread_setname_np(name.c_str());\n        #endif\n    }\n\n    std::string_view TaskManager::getCurrentThreadName() {\n        if (TaskManager::isMainThread())\n            return \"Main\";\n        else\n            return s_currentThreadName.data();\n    }\n\n    void TaskManager::setMainThreadId(std::thread::id threadId) {\n        s_mainThreadId = threadId;\n    }\n\n    bool TaskManager::isMainThread() {\n        return s_mainThreadId == std::this_thread::get_id();\n    }\n\n    void TaskManager::addTaskCompletionCallback(const std::function<void(Task &)> &function) {\n        std::scoped_lock lock(s_tasksFinishedMutex);\n\n        for (const auto &task : s_tasks) {\n            task->interrupt();\n        }\n\n        s_taskCompletionCallbacks.push_back(function);\n    }\n}\n"
  },
  {
    "path": "lib/libimhex/source/api/theme_manager.cpp",
    "content": "#include <hex/api/theme_manager.hpp>\n#include <hex/api/events/events_interaction.hpp>\n\n#include <hex/helpers/logger.hpp>\n#include <hex/helpers/utils.hpp>\n#include <hex/helpers/scaling.hpp>\n#include <hex/helpers/auto_reset.hpp>\n\n#include <nlohmann/json.hpp>\n\nnamespace hex {\n\n    namespace {\n\n        AutoReset<std::map<std::string, nlohmann::json>> s_themes;\n        AutoReset<std::map<std::string, ThemeManager::ThemeHandler>> s_themeHandlers;\n        AutoReset<std::map<std::string, ThemeManager::StyleHandler>> s_styleHandlers;\n        AutoReset<std::string> s_imageTheme;\n        AutoReset<std::string> s_currTheme;\n        AutoReset<std::optional<float>> s_accentColor;\n\n        std::recursive_mutex s_themeMutex;\n    }\n\n\n    void ThemeManager::reapplyCurrentTheme() {\n        ThemeManager::changeTheme(s_currTheme);\n    }\n\n\n    void ThemeManager::addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function<ImColor(u32)> &getFunction, const std::function<void(u32, ImColor)> &setFunction) {\n        std::unique_lock lock(s_themeMutex);\n\n        (*s_themeHandlers)[name] = { .colorMap=colorMap, .getFunction=getFunction, .setFunction=setFunction };\n    }\n\n    void ThemeManager::addStyleHandler(const std::string &name, const StyleMap &styleMap) {\n        std::unique_lock lock(s_themeMutex);\n\n        (*s_styleHandlers)[name] = { styleMap };\n    }\n\n    void ThemeManager::addTheme(const std::string &content) {\n        std::unique_lock lock(s_themeMutex);\n\n        try {\n            auto theme = nlohmann::json::parse(content);\n\n            if (theme.contains(\"name\") && theme.contains(\"colors\")) {\n                (*s_themes)[theme[\"name\"].get<std::string>()] = theme;\n            } else {\n                hex::log::error(\"Invalid theme file\");\n            }\n        } catch (const nlohmann::json::parse_error &e) {\n            hex::log::error(\"Invalid theme file: {}\", e.what());\n        }\n    }\n\n    std::optional<ImColor> ThemeManager::parseColorString(const std::string &colorString) {\n        if (colorString == \"auto\")\n            return ImVec4(0, 0, 0, -1);\n\n        if (colorString.length() != 9 || colorString[0] != '#')\n            return std::nullopt;\n\n        u32 color = 0;\n\n        for (u32 i = 1; i < 9; i++) {\n            color <<= 4;\n            if (colorString[i] >= '0' && colorString[i] <= '9')\n                color |= colorString[i] - '0';\n            else if (colorString[i] >= 'A' && colorString[i] <= 'F')\n                color |= colorString[i] - 'A' + 10;\n            else if (colorString[i] >= 'a' && colorString[i] <= 'f')\n                color |= colorString[i] - 'a' + 10;\n            else\n                return std::nullopt;\n        }\n\n        if (color == 0x00000000)\n            return ImVec4(0, 0, 0, -1);\n\n        return ImColor(hex::changeEndianness(color, std::endian::big));\n    }\n\n    nlohmann::json ThemeManager::exportCurrentTheme(const std::string &name) {\n        nlohmann::json theme = {\n                { \"name\", name },\n                { \"image_theme\", s_imageTheme },\n                { \"colors\", {} },\n                { \"styles\", {} },\n                { \"base\", s_currTheme }\n        };\n\n        for (const auto &[type, handler] : *s_themeHandlers) {\n            theme[\"colors\"][type] = {};\n\n            for (const auto &[key, value] : handler.colorMap) {\n                auto color = handler.getFunction(value);\n                theme[\"colors\"][type][key] = fmt::format(\"#{:08X}\", hex::changeEndianness(u32(color), std::endian::big));\n            }\n        }\n\n        for (const auto &[type, handler] : *s_styleHandlers) {\n            theme[\"styles\"][type] = {};\n\n            for (const auto &[key, style] : handler.styleMap) {\n                if (std::holds_alternative<float*>(style.value)) {\n                    theme[\"styles\"][type][key] = *std::get<float*>(style.value);\n                } else if (std::holds_alternative<ImVec2*>(style.value)) {\n                    theme[\"styles\"][type][key] = {\n                            std::get<ImVec2*>(style.value)->x,\n                            std::get<ImVec2*>(style.value)->y\n                    };\n                }\n            }\n        }\n\n        return theme;\n    }\n\n    void ThemeManager::changeTheme(std::string name) {\n        std::unique_lock lock(s_themeMutex);\n\n        if (!s_themes->contains(name)) {\n            if (s_themes->empty()) {\n                return;\n            } else {\n                const std::string &defaultTheme = s_themes->begin()->first;\n                hex::log::error(\"Theme '{}' does not exist, using default theme '{}' instead!\", name, defaultTheme);\n                name = defaultTheme;\n            }\n        }\n\n        const auto &theme = (*s_themes)[name];\n\n        if (theme.contains(\"base\")) {\n            if (theme[\"base\"].is_string()) {\n                if (theme[\"base\"] != name)\n                    changeTheme(theme[\"base\"].get<std::string>());\n            } else {\n                hex::log::error(\"Theme '{}' has invalid base theme!\", name);\n            }\n        }\n\n        if (theme.contains(\"colors\") && !s_themeHandlers->empty()) {\n            for (const auto&[type, content] : theme[\"colors\"].items()) {\n                if (!s_themeHandlers->contains(type)) {\n                    log::warn(\"No theme handler found for '{}'\", type);\n                    continue;\n                }\n\n                const auto &handler = (*s_themeHandlers)[type];\n                for (const auto &[key, value] : content.items()) {\n                    if (!handler.colorMap.contains(key)) {\n                        log::warn(\"No color found for '{}.{}'\", type, key);\n                        continue;\n                    }\n\n                    auto colorString = value.get<std::string>();\n                    bool accentableColor = false;\n                    if (colorString.starts_with(\"*\")) {\n                        colorString = colorString.substr(1);\n                        accentableColor = true;\n                    }\n                    auto color = parseColorString(colorString);\n\n                    if (!color.has_value()) {\n                        log::warn(\"Invalid color '{}' for '{}.{}'\", colorString, type, key);\n                        continue;\n                    }\n\n                    if (accentableColor && s_accentColor->has_value()) {\n                        float h, s, v;\n                        ImGui::ColorConvertRGBtoHSV(color->Value.x, color->Value.y, color->Value.z, h, s, v);\n\n                        h = s_accentColor->value();\n\n                        ImGui::ColorConvertHSVtoRGB(h, s, v, color->Value.x, color->Value.y, color->Value.z);\n                    }\n\n                    (*s_themeHandlers)[type].setFunction((*s_themeHandlers)[type].colorMap.at(key), color.value());\n                }\n            }\n        }\n\n        if (theme.contains(\"styles\") && !s_styleHandlers->empty()) {\n            for (const auto&[type, content] : theme[\"styles\"].items()) {\n                if (!s_styleHandlers->contains(type)) {\n                    log::warn(\"No style handler found for '{}'\", type);\n                    continue;\n                }\n\n                auto &handler = (*s_styleHandlers)[type];\n                for (const auto &[key, value] : content.items()) {\n                    if (!handler.styleMap.contains(key))\n                        continue;\n\n                    auto &style = handler.styleMap.at(key);\n                    const float scale = style.needsScaling ? 1_scaled : 1.0F;\n\n                    if (value.is_number_float()) {\n                        if (const auto newValue = std::get_if<float*>(&style.value); newValue != nullptr && *newValue != nullptr)\n                            **newValue = value.get<float>() * scale;\n                        else\n                            log::warn(\"Style variable '{}' was of type ImVec2 but a float was expected.\", name);\n                    } else if (value.is_array() && value.size() == 2 && value[0].is_number_float() && value[1].is_number_float()) {\n                        if (const auto newValue = std::get_if<ImVec2*>(&style.value); newValue != nullptr && *newValue != nullptr)\n                            **newValue = ImVec2(value[0].get<float>() * scale, value[1].get<float>() * scale);\n                        else\n                            log::warn(\"Style variable '{}' was of type float but a ImVec2 was expected.\", name);\n                    } else {\n                        hex::log::error(\"Theme '{}' has invalid style value for '{}.{}'!\", name, type, key);\n                    }\n                }\n            }\n        }\n\n        if (theme.contains(\"image_theme\")) {\n            if (theme[\"image_theme\"].is_string()) {\n                s_imageTheme = theme[\"image_theme\"].get<std::string>();\n            } else {\n                hex::log::error(\"Theme '{}' has invalid image theme!\", name);\n                s_imageTheme = \"dark\";\n            }\n        } else {\n            s_imageTheme = \"dark\";\n        }\n\n        s_currTheme = name;\n\n        EventThemeChanged::post();\n    }\n\n    const std::string &ThemeManager::getImageTheme() {\n        return s_imageTheme;\n    }\n\n    std::vector<std::string> ThemeManager::getThemeNames() {\n        std::vector<std::string> themeNames;\n        for (const auto &[name, theme] : *s_themes)\n            themeNames.push_back(name);\n\n        return themeNames;\n    }\n\n    void ThemeManager::reset() {\n        std::unique_lock lock(s_themeMutex);\n\n        s_themes->clear();\n        s_styleHandlers->clear();\n        s_themeHandlers->clear();\n        s_imageTheme->clear();\n        s_currTheme->clear();\n    }\n\n    void ThemeManager::setAccentColor(const ImColor &color) {\n        float h, s, v;\n        ImGui::ColorConvertRGBtoHSV(color.Value.x, color.Value.y, color.Value.z, h, s, v);\n\n        s_accentColor = h;\n        reapplyCurrentTheme();\n    }\n\n\n    const std::map<std::string, ThemeManager::ThemeHandler> &ThemeManager::getThemeHandlers() {\n        return s_themeHandlers;\n    }\n\n    const std::map<std::string, ThemeManager::StyleHandler> &ThemeManager::getStyleHandlers() {\n        return s_styleHandlers;\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/api/tutorial_manager.cpp",
    "content": "#include <hex/api/tutorial_manager.hpp>\n#include <hex/api/imhex_api/system.hpp>\n#include <hex/api/localization_manager.hpp>\n#include <hex/api/task_manager.hpp>\n#include <hex/api/events/events_gui.hpp>\n\n#include <hex/helpers/auto_reset.hpp>\n\n#include <imgui_internal.h>\n#include <hex/helpers/utils.hpp>\n#include <hex/helpers/scaling.hpp>\n#include <wolv/utils/core.hpp>\n\n#include <map>\n\n#include <imgui.h>\n\nnamespace hex {\n\n    namespace {\n\n        AutoReset<std::map<std::string, TutorialManager::Tutorial>> s_tutorials;\n        auto s_currentTutorial = s_tutorials->end();\n\n        AutoReset<std::map<ImGuiID, std::string>> s_highlights;\n        AutoReset<std::vector<std::pair<ImRect, std::string>>> s_highlightDisplays;\n        AutoReset<std::map<ImGuiID, ImRect>> s_interactiveHelpDisplays;\n\n        AutoReset<std::map<ImGuiID, std::function<void()>>> s_interactiveHelpItems;\n        ImRect s_hoveredRect;\n        ImGuiID s_hoveredId;\n        ImGuiID s_activeHelpId;\n        bool s_helpHoverActive = false;\n\n        AutoReset<std::function<std::function<void()>(const std::string &)>> s_renderer;\n\n\n        class IDStack {\n        public:\n            IDStack() {\n                idStack.push_back(0);\n            }\n\n            void add(const char *string) {\n                const ImGuiID seed = idStack.back();\n                const ImGuiID id = ImHashStr(string, 0, seed);\n\n                idStack.push_back(id);\n            }\n\n            void add(const std::string &string) {\n                const ImGuiID seed = idStack.back();\n                const ImGuiID id = ImHashStr(string.c_str(), string.length(), seed);\n\n                idStack.push_back(id);\n            }\n\n            void add(const void *pointer) {\n                const ImGuiID seed = idStack.back();\n                const ImGuiID id = ImHashData((const void*) &pointer, sizeof(pointer), seed);\n\n                idStack.push_back(id);\n            }\n\n            void add(int value) {\n                const ImGuiID seed = idStack.back();\n                const ImGuiID id = ImHashData(&value, sizeof(value), seed);\n\n                idStack.push_back(id);\n            }\n\n            ImGuiID get() {\n                return idStack.back();\n            }\n        private:\n            ImVector<ImGuiID> idStack;\n        };\n\n        ImGuiID calculateId(const auto &ids) {\n            IDStack idStack;\n\n            for (const auto &id : ids) {\n                std::visit(wolv::util::overloaded {\n                        [&idStack](const Lang &id) {\n                            idStack.add(id.get());\n                        },\n                        [&idStack](const auto &id) {\n                            idStack.add(id);\n                        }\n                }, id);\n            }\n\n            return idStack.get();\n        }\n\n    }\n\n    void TutorialManager::init() {\n        if (*s_renderer == nullptr) {\n            *s_renderer = [](const std::string &message) {\n                return [message] {\n                    ImGui::PushTextWrapPos(300_scaled);\n                    ImGui::TextUnformatted(message.c_str());\n                    ImGui::PopTextWrapPos();\n                    ImGui::NewLine();\n                };\n            };\n        }\n    }\n\n    void TutorialManager::postElementRendered(ImGuiID id, const ImRect &boundingBox) {\n        if (!ImGui::IsRectVisible(boundingBox.Min, boundingBox.Max))\n            return;\n\n        {\n            const auto element = hex::s_highlights->find(id);\n            if (element != hex::s_highlights->end()) {\n                hex::s_highlightDisplays->emplace_back(boundingBox, element->second);\n\n                const auto window = ImGui::GetCurrentWindow();\n                if (window != nullptr && window->DockNode != nullptr && window->DockNode->TabBar != nullptr)\n                    window->DockNode->TabBar->NextSelectedTabId = window->TabId;\n            }\n        }\n\n        {\n            const auto element = s_interactiveHelpItems->find(id);\n            if (element != s_interactiveHelpItems->end()) {\n                (*s_interactiveHelpDisplays)[id] = boundingBox;\n            }\n\n        }\n\n        if (id != 0 && boundingBox.Contains(ImGui::GetMousePos())) {\n            if ((s_hoveredRect.GetArea() == 0 || boundingBox.GetArea() < s_hoveredRect.GetArea()) && s_interactiveHelpItems->contains(id)) {\n                s_hoveredRect = boundingBox;\n                s_hoveredId = id;\n            }\n        }\n    }\n\n    const std::map<std::string, TutorialManager::Tutorial>& TutorialManager::getTutorials() {\n        return s_tutorials;\n    }\n\n    std::map<std::string, TutorialManager::Tutorial>::iterator TutorialManager::getCurrentTutorial() {\n        return s_currentTutorial;\n    }\n\n\n    TutorialManager::Tutorial& TutorialManager::createTutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) {\n        return s_tutorials->try_emplace(unlocalizedName, Tutorial(unlocalizedName, unlocalizedDescription)).first->second;\n    }\n\n    void TutorialManager::startHelpHover() {\n        TaskManager::doLater([]{\n            s_helpHoverActive = true;\n        });\n    }\n\n    void TutorialManager::addInteractiveHelpText(std::initializer_list<std::variant<Lang, std::string, int>> &&ids, UnlocalizedString unlocalizedString) {\n        auto id = calculateId(ids);\n\n        s_interactiveHelpItems->emplace(id, [text = std::move(unlocalizedString)]{\n            log::info(\"{}\", Lang(text).get());\n        });\n    }\n\n    void TutorialManager::addInteractiveHelpLink(std::initializer_list<std::variant<Lang, std::string, int>> &&ids, std::string link) {\n        auto id = calculateId(ids);\n\n        s_interactiveHelpItems->emplace(id, [link = std::move(link)]{\n            hex::openWebpage(link);\n        });\n    }\n\n    void TutorialManager::setLastItemInteractiveHelpPopup(std::function<void()> callback) {\n        auto id = ImGui::GetItemID();\n\n        if (!s_interactiveHelpItems->contains(id)) {\n            s_interactiveHelpItems->emplace(id, [id]{\n                s_activeHelpId = id;\n            });\n        }\n\n        if (id == s_activeHelpId) {\n            ImGui::SetNextWindowSize(scaled({ 400, 0 }));\n            if (ImGui::BeginTooltip()) {\n                callback();\n                ImGui::EndTooltip();\n            }\n\n            if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) || ImGui::IsKeyPressed(ImGuiKey_Escape))\n                s_activeHelpId = 0;\n        }\n    }\n\n    void TutorialManager::setLastItemInteractiveHelpLink(std::string link) {\n        auto id = ImGui::GetItemID();\n\n        if (s_interactiveHelpItems->contains(id))\n            return;\n\n        s_interactiveHelpItems->emplace(id, [link = std::move(link)]{\n            hex::openWebpage(link);\n        });\n    }\n\n\n    void TutorialManager::startTutorial(const UnlocalizedString &unlocalizedName) {\n        s_currentTutorial = s_tutorials->find(unlocalizedName);\n        if (s_currentTutorial == s_tutorials->end())\n            return;\n\n        s_currentTutorial->second.start();\n    }\n\n    void TutorialManager::stopCurrentTutorial() {\n        s_currentTutorial = s_tutorials->end();\n    }\n\n    void TutorialManager::drawHighlights() {\n        if (s_helpHoverActive) {\n            const auto &drawList = ImGui::GetForegroundDrawList(ImGui::GetMainViewport());\n            drawList->AddText(ImGui::GetMousePos() + scaled({ 10, -5, }), ImGui::GetColorU32(ImGuiCol_Text), \"?\");\n\n            for (const auto &[id, boundingBox] : *s_interactiveHelpDisplays) {\n                drawList->AddRect(\n                    boundingBox.Min - ImVec2(5, 5),\n                    boundingBox.Max + ImVec2(5, 5),\n                    ImGui::GetColorU32(ImGuiCol_PlotHistogram),\n                    5.0F,\n                    ImDrawFlags_None,\n                    2.0F\n                );\n            }\n\n            s_interactiveHelpDisplays->clear();\n\n            const bool mouseClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left);\n            if (s_hoveredId != 0) {\n                drawList->AddRectFilled(s_hoveredRect.Min, s_hoveredRect.Max, 0x30FFFFFF);\n\n                if (mouseClicked) {\n                    auto it = s_interactiveHelpItems->find(s_hoveredId);\n                    if (it != s_interactiveHelpItems->end()) {\n                        it->second();\n                    }\n                }\n\n                s_hoveredId = 0;\n                s_hoveredRect = {};\n            }\n\n            if (mouseClicked || ImGui::IsKeyPressed(ImGuiKey_Escape)) {\n                s_helpHoverActive = false;\n            }\n\n            // Discard mouse click so it doesn't activate clicked item\n            ImGui::GetIO().MouseDown[ImGuiMouseButton_Left]     = false;\n            ImGui::GetIO().MouseReleased[ImGuiMouseButton_Left] = false;\n            ImGui::GetIO().MouseClicked[ImGuiMouseButton_Left]  = false;\n        }\n\n        for (const auto &[rect, unlocalizedText] : *s_highlightDisplays) {\n            const auto drawList = ImGui::GetForegroundDrawList();\n\n            drawList->PushClipRectFullScreen();\n            {\n                auto highlightColor = ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_Highlight);\n                highlightColor.w *= ImSin(ImGui::GetTime() * 6.0F) / 4.0F + 0.75F;\n                ImHexApi::System::unlockFrameRate();\n\n                drawList->AddRect(rect.Min - ImVec2(5, 5), rect.Max + ImVec2(5, 5), ImColor(highlightColor), 5.0F, ImDrawFlags_None, 2.0F);\n            }\n\n            {\n                if (!unlocalizedText.empty()) {\n                    const auto mainWindowPos = ImHexApi::System::getMainWindowPosition();\n                    const auto mainWindowSize = ImHexApi::System::getMainWindowSize();\n\n                    const auto margin = ImGui::GetStyle().WindowPadding;\n\n                    ImVec2 windowPos  = { rect.Min.x + 20_scaled, rect.Max.y + 10_scaled };\n                    ImVec2 windowSize = { std::max<float>(rect.Max.x - rect.Min.x - 40_scaled, 300_scaled), 0 };\n\n                    const char* text = Lang(unlocalizedText);\n                    const auto textSize = ImGui::CalcTextSize(text, nullptr, false, windowSize.x - margin.x * 2);\n                    windowSize.y = textSize.y + margin.y * 2;\n\n                    if (windowPos.y + windowSize.y > mainWindowPos.y + mainWindowSize.y)\n                        windowPos.y = rect.Min.y - windowSize.y - 15_scaled;\n                    if (windowPos.y < mainWindowPos.y)\n                        windowPos.y = rect.Min.y + 10_scaled;\n\n                    ImGui::SetNextWindowPos(windowPos);\n                    ImGui::SetNextWindowSize(windowSize);\n                    ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);\n                    if (ImGui::Begin(unlocalizedText.c_str(), nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize)) {\n                        ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead());\n                        ImGuiExt::TextFormattedWrapped(\"{}\", text);\n                    }\n                    ImGui::End();\n                }\n            }\n            drawList->PopClipRect();\n\n        }\n        s_highlightDisplays->clear();\n    }\n\n    void TutorialManager::drawMessageBox(std::optional<Tutorial::Step::Message> message) {\n        const auto windowStart = ImHexApi::System::getMainWindowPosition() + scaled({ 10, 10 });\n        const auto windowEnd = ImHexApi::System::getMainWindowPosition() + ImHexApi::System::getMainWindowSize() - scaled({ 10, 10 });\n\n        ImVec2 position = ImHexApi::System::getMainWindowPosition() + ImHexApi::System::getMainWindowSize() / 2.0F;\n        ImVec2 pivot    = { 0.5F, 0.5F };\n\n        if (!message.has_value()) {\n            message = Tutorial::Step::Message {\n                 .position=Position::None,\n                .unlocalizedTitle=\"\",\n                .unlocalizedMessage=\"\",\n                .allowSkip=false\n            };\n        }\n\n        if (message->position == Position::None) {\n            message->position = Position::Bottom | Position::Right;\n        }\n\n        if ((message->position & Position::Top) == Position::Top) {\n            position.y  = windowStart.y;\n            pivot.y     = 0.0F;\n        }\n        if ((message->position & Position::Bottom) == Position::Bottom) {\n            position.y  = windowEnd.y;\n            pivot.y     = 1.0F;\n        }\n        if ((message->position & Position::Left) == Position::Left) {\n            position.x  = windowStart.x;\n            pivot.x     = 0.0F;\n        }\n        if ((message->position & Position::Right) == Position::Right) {\n            position.x  = windowEnd.x;\n            pivot.x     = 1.0F;\n        }\n\n        ImGui::SetNextWindowPos(position, ImGuiCond_Always, pivot);\n        ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);\n        ImGui::SetNextWindowSize(ImVec2(300_scaled, 0));\n\n        bool open = true;\n        if (ImGui::Begin(message->unlocalizedTitle.empty() ? \"##TutorialMessage\" : Lang(message->unlocalizedTitle), &open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoFocusOnAppearing)) {\n            ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead());\n\n            auto &step = s_currentTutorial->second.m_currentStep;\n            if (!message->unlocalizedMessage.empty()) {\n                step->m_drawFunction();\n                ImGui::NewLine();\n                ImGui::NewLine();\n            }\n\n            ImGui::BeginDisabled(step == s_currentTutorial->second.m_steps.begin());\n            if (ImGuiExt::DimmedArrowButton(\"Backwards\", ImGuiDir_Left)) {\n                s_currentTutorial->second.m_currentStep->advance(-1);\n            }\n            ImGui::EndDisabled();\n\n            ImGui::SameLine();\n\n            ImGui::SetCursorPosX(ImGui::GetWindowWidth() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.x);\n            ImGui::BeginDisabled(!message->allowSkip && step == s_currentTutorial->second.m_latestStep);\n            if (ImGuiExt::DimmedArrowButton(\"Forwards\", ImGuiDir_Right)) {\n                step->advance(1);\n            }\n            ImGui::EndDisabled();\n        }\n        ImGui::End();\n\n        if (!open) {\n            stopCurrentTutorial();\n        }\n    }\n\n    void TutorialManager::drawTutorial() {\n        drawHighlights();\n\n        if (s_currentTutorial == s_tutorials->end())\n            return;\n\n        const auto &currentStep = s_currentTutorial->second.m_currentStep;\n        if (currentStep == s_currentTutorial->second.m_steps.end())\n            return;\n\n        const auto &message = currentStep->m_message;\n        drawMessageBox(message);\n    }\n\n\n\n    void TutorialManager::reset() {\n        s_tutorials->clear();\n        s_currentTutorial = s_tutorials->end();\n\n        s_highlights->clear();\n        s_highlightDisplays->clear();\n    }\n\n    void TutorialManager::setRenderer(std::function<DrawFunction(const std::string &)> renderer) {\n        s_renderer = std::move(renderer);\n    }\n\n    TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() {\n        auto &newStep = m_steps.emplace_back(this);\n        m_currentStep = m_steps.end();\n        m_latestStep  = m_currentStep;\n\n        return newStep;\n    }\n\n    void TutorialManager::Tutorial::start() {\n        m_currentStep = m_steps.begin();\n        m_latestStep  = m_currentStep;\n        if (m_currentStep == m_steps.end())\n            return;\n\n        m_currentStep->addHighlights();\n\n        if (m_currentStep->m_message.has_value())\n            m_currentStep->m_drawFunction = (*s_renderer)(Lang(m_currentStep->m_message->unlocalizedMessage));\n    }\n\n    void TutorialManager::Tutorial::Step::addHighlights() const {\n        if (m_onAppear)\n            m_onAppear();\n\n        for (const auto &[text, ids] : m_highlights) {\n            s_highlights->emplace(calculateId(ids), text);\n        }\n    }\n\n    void TutorialManager::Tutorial::Step::removeHighlights() const {\n        for (const auto &[text, ids] : m_highlights) {\n            s_highlights->erase(calculateId(ids));\n        }\n    }\n\n    void TutorialManager::Tutorial::Step::advance(i32 steps) const {\n        m_parent->m_currentStep->removeHighlights();\n\n        if (m_parent->m_currentStep == m_parent->m_latestStep && steps > 0)\n            std::advance(m_parent->m_latestStep, steps);\n        std::advance(m_parent->m_currentStep, steps);\n\n        if (m_parent->m_currentStep != m_parent->m_steps.end()) {\n            m_parent->m_currentStep->addHighlights();\n\n            if (m_message.has_value())\n                m_parent->m_currentStep->m_drawFunction = (*s_renderer)(Lang(m_parent->m_currentStep->m_message->unlocalizedMessage));\n        }\n        else\n            s_currentTutorial = s_tutorials->end();\n    }\n\n\n    TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::addHighlight(const UnlocalizedString &unlocalizedText, std::initializer_list<std::variant<Lang, std::string, int>>&& ids) {\n        m_highlights.emplace_back(\n            unlocalizedText,\n            ids\n        );\n\n        return *this;\n    }\n\n    TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::addHighlight(std::initializer_list<std::variant<Lang, std::string, int>>&& ids) {\n        return this->addHighlight(\"\", std::forward<decltype(ids)>(ids));\n    }\n\n\n\n    TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::setMessage(const UnlocalizedString &unlocalizedTitle, const UnlocalizedString &unlocalizedMessage, Position position) {\n        m_message = Message {\n            .position=position,\n            .unlocalizedTitle=unlocalizedTitle,\n            .unlocalizedMessage=unlocalizedMessage,\n            .allowSkip=false\n        };\n\n        return *this;\n    }\n\n    TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::allowSkip() {\n        if (m_message.has_value()) {\n            m_message->allowSkip = true;\n        } else {\n            m_message = Message {\n                .position=Position::Bottom | Position::Right,\n                .unlocalizedTitle=\"\",\n                .unlocalizedMessage=\"\",\n                .allowSkip=true\n            };\n        }\n\n        return *this;\n    }\n\n    TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::onAppear(std::function<void()> callback) {\n        m_onAppear = std::move(callback);\n\n        return *this;\n    }\n\n    TutorialManager::Tutorial::Step& TutorialManager::Tutorial::Step::onComplete(std::function<void()> callback) {\n        m_onComplete = std::move(callback);\n\n        return *this;\n    }\n\n\n\n\n    bool TutorialManager::Tutorial::Step::isCurrent() const {\n        const auto &currentStep = m_parent->m_currentStep;\n\n        if (currentStep == m_parent->m_steps.end())\n            return false;\n\n        return &*currentStep == this;\n    }\n\n    void TutorialManager::Tutorial::Step::complete() const {\n        if (this->isCurrent()) {\n            this->advance();\n\n            if (m_onComplete) {\n                TaskManager::doLater([this] {\n                    m_onComplete();\n                });\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/api/workspace_manager.cpp",
    "content": "#include <hex/api/workspace_manager.hpp>\n#include <hex/api/layout_manager.hpp>\n\n#include <hex/helpers/logger.hpp>\n#include <hex/helpers/auto_reset.hpp>\n#include <hex/helpers/default_paths.hpp>\n\n#include <wolv/io/file.hpp>\n\n#include <nlohmann/json.hpp>\n\n#include <imgui.h>\n#include <wolv/utils/string.hpp>\n\nnamespace hex {\n\n    static AutoReset<std::map<std::string, WorkspaceManager::Workspace>> s_workspaces;\n    static auto s_currentWorkspace  = s_workspaces->end();\n    static auto s_previousWorkspace = s_workspaces->end();\n    static auto s_workspaceToRemove = s_workspaces->end();\n\n    void WorkspaceManager::createWorkspace(const std::string& name, const std::string &layout) {\n        s_currentWorkspace = s_workspaces->insert_or_assign(name, Workspace {\n            .layout  = layout.empty() ? LayoutManager::saveToString() : layout,\n            .path    = {},\n            .builtin = false\n        }).first;\n\n        for (const auto &workspaceFolder : paths::Workspaces.write()) {\n            const auto workspacePath = workspaceFolder / (name + \".hexws\");\n            if (exportToFile(workspacePath)) {\n                s_currentWorkspace->second.path = workspacePath;\n                break;\n            }\n        }\n    }\n\n    void WorkspaceManager::switchWorkspace(const std::string& name) {\n        const auto newWorkspace = s_workspaces->find(name);\n        if (newWorkspace != s_workspaces->end()) {\n            s_currentWorkspace = newWorkspace;\n            log::info(\"Switching to workspace '{}'\", name);\n        }\n    }\n\n    void WorkspaceManager::importFromFile(const std::fs::path& path) {\n        if (std::ranges::any_of(*s_workspaces, [path](const auto &pair) { return pair.second.path == path; })) {\n            return;\n        }\n\n        wolv::io::File file(path, wolv::io::File::Mode::Read);\n        if (!file.isValid()) {\n            log::error(\"Failed to load workspace from file '{}'\", path.string());\n            file.remove();\n            return;\n        }\n\n        auto content = file.readString();\n        try {\n            auto json = nlohmann::json::parse(content.begin(), content.end());\n\n            const std::string name = json[\"name\"];\n            std::string layout = json[\"layout\"];\n            const bool builtin = json.value(\"builtin\", false);\n\n            (*s_workspaces)[name] = Workspace {\n                .layout = std::move(layout),\n                .path = path,\n                .builtin = builtin\n            };\n        } catch (nlohmann::json::exception &e) {\n            log::error(\"Failed to load workspace from file '{}': {}\", path.string(), e.what());\n            file.remove();\n        }\n    }\n\n    bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName, bool builtin) {\n        if (path.empty()) {\n            if (s_currentWorkspace == s_workspaces->end()) {\n                return false;\n            }\n\n            path = s_currentWorkspace->second.path;\n        }\n\n        if (workspaceName.empty()) {\n            workspaceName = s_currentWorkspace->first;\n        }\n\n        wolv::io::File file(path, wolv::io::File::Mode::Create);\n\n        if (!file.isValid()) {\n            return false;\n        }\n\n        const auto layoutString = LayoutManager::saveToString();\n        if (auto it = s_workspaces->find(workspaceName); it != s_workspaces->end()) {\n            it->second.layout = layoutString;\n        }\n\n        nlohmann::json json;\n        json[\"name\"]    = workspaceName;\n        json[\"layout\"]  = layoutString;\n        json[\"builtin\"] = builtin;\n\n        file.writeString(json.dump(4));\n\n        return true;\n    }\n\n    void WorkspaceManager::removeWorkspace(const std::string& name) {\n        bool deletedCurrentWorkspace = false;\n        for (const auto &[workspaceName, workspace] : *s_workspaces) {\n            if (workspaceName == name) {\n                log::info(\"Removing workspace file '{}'\", wolv::util::toUTF8String(workspace.path));\n                if (wolv::io::fs::remove(workspace.path)) {\n                    log::info(\"Removed workspace '{}'\", name);\n\n                    if (workspaceName == s_currentWorkspace->first) {\n                        deletedCurrentWorkspace = true;\n                    }\n                } else {\n                    log::error(\"Failed to remove workspace '{}'\", name);\n                }\n            }\n        }\n\n        WorkspaceManager::reload();\n\n        if (deletedCurrentWorkspace && !s_workspaces->empty()) {\n            s_currentWorkspace = s_workspaces->begin();\n        }\n    }\n\n\n    void WorkspaceManager::process() {\n        if (s_previousWorkspace != s_currentWorkspace) {\n            log::debug(\"Updating workspace\");\n            if (s_previousWorkspace != s_workspaces->end()) {\n                auto newWorkspace = s_currentWorkspace;\n                s_currentWorkspace = s_previousWorkspace;\n                exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first, s_previousWorkspace->second.builtin);\n                s_currentWorkspace = newWorkspace;\n            }\n\n            LayoutManager::closeAllViews();\n            ImGui::LoadIniSettingsFromMemory(s_currentWorkspace->second.layout.c_str());\n\n            s_previousWorkspace = s_currentWorkspace;\n\n            if (s_workspaceToRemove != s_workspaces->end()) {\n                s_workspaces->erase(s_workspaceToRemove);\n                s_workspaceToRemove = s_workspaces->end();\n            }\n        }\n    }\n\n\n    void WorkspaceManager::reset() {\n        s_workspaces->clear();\n        s_currentWorkspace  = s_workspaces->end();\n        s_previousWorkspace = s_workspaces->end();\n    }\n\n    void WorkspaceManager::reload() {\n        WorkspaceManager::reset();\n\n        // Explicitly only search paths that are writable so the workspaces can be modified\n        for (const auto &defaultPath : paths::Workspaces.write()) {\n            for (const auto &entry : std::fs::directory_iterator(defaultPath)) {\n                if (!entry.is_regular_file()) {\n                    continue;\n                }\n\n                const auto &path = entry.path();\n                if (path.extension() != \".hexws\") {\n                    continue;\n                }\n\n                WorkspaceManager::importFromFile(path);\n            }\n        }\n    }\n\n    const std::map<std::string, WorkspaceManager::Workspace>& WorkspaceManager::getWorkspaces() {\n        return *s_workspaces;\n    }\n\n    const std::map<std::string, WorkspaceManager::Workspace>::iterator& WorkspaceManager::getCurrentWorkspace() {\n        return s_currentWorkspace;\n    }\n\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/data_processor/attribute.cpp",
    "content": "#include <hex/data_processor/attribute.hpp>\n\n\nnamespace hex::dp {\n\n    int Attribute::s_idCounter = 1;\n\n\n    Attribute::Attribute(IOType ioType, Type type, UnlocalizedString unlocalizedName) : m_id(s_idCounter++), m_ioType(ioType), m_type(type), m_unlocalizedName(std::move(unlocalizedName)) {\n    }\n\n    Attribute::~Attribute() {\n        for (auto &[linkId, attr] : this->getConnectedAttributes())\n            attr->removeConnectedAttribute(linkId);\n    }\n\n    void Attribute::setIdCounter(int id) {\n        if (id > s_idCounter)\n            s_idCounter = id;\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/data_processor/link.cpp",
    "content": "#include <hex/data_processor/link.hpp>\n\n\nnamespace hex::dp {\n\n    int Link::s_idCounter = 1;\n\n    Link::Link(int from, int to) : m_id(s_idCounter++), m_from(from), m_to(to) { }\n\n    void Link::setIdCounter(int id) {\n        if (id > s_idCounter)\n            s_idCounter = id;\n    }\n}"
  },
  {
    "path": "lib/libimhex/source/data_processor/node.cpp",
    "content": "#include <hex/data_processor/node.hpp>\n\n#include <hex/helpers/fmt.hpp>\n\n#include <hex/api/localization_manager.hpp>\n#include <hex/providers/provider.hpp>\n\nnamespace hex::dp {\n\n    int Node::s_idCounter = 1;\n    static std::atomic_bool s_interrupted;\n\n    Node::Node(UnlocalizedString unlocalizedTitle, std::vector<Attribute> attributes) : m_id(s_idCounter++), m_unlocalizedTitle(std::move(unlocalizedTitle)), m_attributes(std::move(attributes)) {\n        for (auto &attr : m_attributes)\n            attr.setParentNode(this);\n    }\n\n    void Node::draw() {\n        this->drawNode();\n    }\n\n\n    const std::vector<u8>& Node::getBufferOnInput(u32 index) {\n        auto attribute = this->getConnectedInputAttribute(index);\n\n        if (attribute == nullptr)\n            throwNodeError(fmt::format(\"Nothing connected to input '{0}'\", Lang(m_attributes[index].getUnlocalizedName())));\n\n        if (attribute->getType() != Attribute::Type::Buffer)\n            throwNodeError(\"Tried to read buffer from non-buffer attribute\");\n\n        markInputProcessed(index);\n        attribute->getParentNode()->process();\n        unmarkInputProcessed(index);\n\n        auto &outputData = attribute->getOutputData();\n\n        return outputData;\n    }\n\n    const i128& Node::getIntegerOnInput(u32 index) {\n        auto attribute = this->getConnectedInputAttribute(index);\n\n        auto &outputData = [&]() -> std::vector<u8>&{\n            if (attribute != nullptr) {\n                if (attribute->getType() != Attribute::Type::Integer)\n                    throwNodeError(\"Tried to read integer from non-integer attribute\");\n\n                markInputProcessed(index);\n                attribute->getParentNode()->process();\n                unmarkInputProcessed(index);\n\n                return attribute->getOutputData();\n            } else {\n                return this->getAttribute(index).getOutputData();\n            }\n        }();\n\n        if (outputData.empty())\n            throwNodeError(\"No data available at connected attribute\");\n\n        if (outputData.size() < sizeof(i128))\n            throwNodeError(\"Not enough data provided for integer\");\n\n        return *reinterpret_cast<i128 *>(outputData.data());\n    }\n\n    const double& Node::getFloatOnInput(u32 index) {\n        auto attribute = this->getConnectedInputAttribute(index);\n\n        auto &outputData = [&]() -> std::vector<u8>&{\n            if (attribute != nullptr) {\n                if (attribute->getType() != Attribute::Type::Float)\n                    throwNodeError(\"Tried to read integer from non-float attribute\");\n\n                markInputProcessed(index);\n                attribute->getParentNode()->process();\n                unmarkInputProcessed(index);\n\n                return attribute->getOutputData();\n            } else {\n                return this->getAttribute(index).getOutputData();\n            }\n        }();\n\n        if (outputData.empty())\n            throwNodeError(\"No data available at connected attribute\");\n\n        if (outputData.size() < sizeof(double))\n            throwNodeError(\"Not enough data provided for float\");\n\n        return *reinterpret_cast<double *>(outputData.data());\n    }\n\n    void Node::setBufferOnOutput(u32 index, std::span<const u8> data) {\n        if (index >= this->getAttributes().size())\n            throwNodeError(\"Attribute index out of bounds!\");\n\n        auto &attribute = this->getAttributes()[index];\n\n        if (attribute.getIOType() != Attribute::IOType::Out)\n            throwNodeError(\"Tried to set output data of an input attribute!\");\n\n        if (attribute.getType() != Attribute::Type::Buffer)\n            throwNodeError(\"Tried to set buffer on non-buffer attribute!\");\n\n        attribute.getOutputData() = { data.begin(), data.end() };\n    }\n\n    void Node::setIntegerOnOutput(u32 index, i128 integer) {\n        if (index >= this->getAttributes().size())\n            throwNodeError(\"Attribute index out of bounds!\");\n\n        auto &attribute = this->getAttributes()[index];\n\n        if (attribute.getIOType() != Attribute::IOType::Out)\n            throwNodeError(\"Tried to set output data of an input attribute!\");\n\n        if (attribute.getType() != Attribute::Type::Integer)\n            throwNodeError(\"Tried to set integer on non-integer attribute!\");\n\n        std::vector<u8> buffer(sizeof(integer), 0);\n        std::memcpy(buffer.data(), &integer, sizeof(integer));\n\n        attribute.getOutputData() = buffer;\n    }\n\n    void Node::setFloatOnOutput(u32 index, double floatingPoint) {\n        if (index >= this->getAttributes().size())\n            throwNodeError(\"Attribute index out of bounds!\");\n\n        auto &attribute = this->getAttributes()[index];\n\n        if (attribute.getIOType() != Attribute::IOType::Out)\n            throwNodeError(\"Tried to set output data of an input attribute!\");\n\n        if (attribute.getType() != Attribute::Type::Float)\n            throwNodeError(\"Tried to set float on non-float attribute!\");\n\n        std::vector<u8> buffer(sizeof(floatingPoint), 0);\n        std::memcpy(buffer.data(), &floatingPoint, sizeof(floatingPoint));\n\n        attribute.getOutputData() = buffer;\n    }\n\n    void Node::setOverlayData(u64 address, const std::vector<u8> &data) {\n        if (m_overlay == nullptr)\n            throwNodeError(\"Tried setting overlay data on a node that's not the end of a chain!\");\n\n        m_overlay->setAddress(address);\n        m_overlay->getData() = data;\n    }\n\n    [[noreturn]] void Node::throwNodeError(const std::string &msg) {\n        throw NodeError(this, msg);\n    }\n\n    void Node::setAttributes(std::vector<Attribute> attributes) {\n        m_attributes = std::move(attributes);\n\n        for (auto &attr : m_attributes)\n            attr.setParentNode(this);\n    }\n\n    void Node::setIdCounter(int id) {\n        if (id > s_idCounter)\n            s_idCounter = id;\n    }\n\n    Attribute& Node::getAttribute(u32 index) {\n        if (index >= this->getAttributes().size())\n            throw std::runtime_error(\"Attribute index out of bounds!\");\n\n        return this->getAttributes()[index];\n    }\n\n    Attribute *Node::getConnectedInputAttribute(u32 index) {\n        const auto &connectedAttribute = this->getAttribute(index).getConnectedAttributes();\n\n        if (connectedAttribute.empty())\n            return nullptr;\n\n        return connectedAttribute.begin()->second;\n    }\n\n    void Node::markInputProcessed(u32 index) {\n        const auto &[iter, inserted] = m_processedInputs.insert(index);\n        if (!inserted)\n            throwNodeError(\"Recursion detected!\");\n\n        if (s_interrupted) {\n            s_interrupted = false;\n            throwNodeError(\"Execution interrupted!\");\n        }\n    }\n\n    void Node::unmarkInputProcessed(u32 index) {\n        m_processedInputs.erase(index);\n    }\n\n    void Node::interrupt() {\n        s_interrupted = true;\n    }\n\n\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/binary_pattern.cpp",
    "content": "#include <hex/helpers/binary_pattern.hpp>\n\n#include <algorithm>\n\nnamespace hex {\n\n    namespace {\n\n        void skipWhitespace(std::string_view &string) {\n            while (!string.empty()) {\n                if (!std::isspace(string.front()))\n                    break;\n                string = string.substr(1);\n            }\n        }\n\n        std::vector<BinaryPattern::Pattern> parseValueExpression(std::string_view &string) {\n            string = string.substr(1);\n\n            // Parse bit size number\n            u64 bitSize = 0;\n            std::endian endian = std::endian::little;\n            while (!string.empty() && std::isdigit(string.front())) {\n                bitSize *= 10;\n                bitSize += string.front() - '0';\n\n                string = string.substr(1);\n                skipWhitespace(string);\n            }\n\n            if (string.starts_with(\"le\")) {\n                endian = std::endian::little;\n                string = string.substr(2);\n            } else if (string.starts_with(\"be\")) {\n                endian = std::endian::big;\n                string = string.substr(2);\n            }\n\n            if (bitSize > 64 || bitSize % 8 != 0)\n                return { };\n\n            if (string.empty() || string.front() != '(')\n                return { };\n\n            string = string.substr(1);\n\n            i128 value = 0x00;\n            bool negative = false;\n            for (u32 i = 0; !string.empty(); i++) {\n                const char c = string.front();\n\n                if (c == ')') break;\n                if (i == 0 && c == '-')\n                    negative = true;\n                else if (i == 0 && c == '+')\n                    continue;\n                else if (std::isdigit(c))\n                    value = value * 10 + (c - '0');\n                else\n                    return {};\n\n                string = string.substr(1);\n            }\n\n            if (negative)\n                value = -value;\n\n            if (string.empty() || string.front() != ')')\n                return { };\n\n            string = string.substr(1);\n\n            u128 resultValue = changeEndianness(value, bitSize / 8, endian);\n            std::vector<BinaryPattern::Pattern> result;\n            for (u32 bit = 0; bit < bitSize; bit += 8) {\n                result.emplace_back(\n                    0xFF,\n                    u8((resultValue >> bit) & hex::bitmask(8))\n                );\n            }\n\n            return result;\n        }\n\n        std::vector<BinaryPattern::Pattern> parseBinaryPatternString(std::string_view string) {\n            std::vector<BinaryPattern::Pattern> result;\n\n            if (string.length() < 2)\n                return { };\n\n            bool inString = false;\n            while (!string.empty()) {\n                BinaryPattern::Pattern pattern = { .mask=0, .value=0 };\n\n                 if (string.starts_with(\"\\\"\")) {\n                    inString = !inString;\n                    string = string.substr(1);\n                    continue;\n                } else if (inString) {\n                    pattern = { .mask=0xFF, .value=u8(string.front()) };\n                    string = string.substr(1);\n                } else if (string.starts_with(\"u\") || string.starts_with(\"s\")) {\n                    auto newPatterns = parseValueExpression(string);\n                    if (newPatterns.empty())\n                        return {};\n                    std::ranges::move(newPatterns, std::back_inserter(result));\n                    continue;\n                } else if (string.starts_with(\"??\")) {\n                    pattern = { .mask=0x00, .value=0x00 };\n                    string = string.substr(2);\n                } else if ((std::isxdigit(string.front()) || string.front() == '?') && string.length() >= 2) {\n                    const auto hex = string.substr(0, 2);\n\n                    for (const auto &c : hex) {\n                        pattern.mask  <<= 4;\n                        pattern.value <<= 4;\n\n                        if (std::isxdigit(c)) {\n                            pattern.mask |= 0x0F;\n\n                            if (auto hexValue = hex::hexCharToValue(c); hexValue.has_value())\n                                pattern.value |= hexValue.value();\n                            else\n                                return { };\n                        } else if (c != '?') {\n                            return { };\n                        }\n                    }\n\n                    string = string.substr(2);\n                } else if (std::isspace(string.front())) {\n                    string = string.substr(1);\n                    continue;\n                } else {\n                    return { };\n                }\n\n                result.push_back(pattern);\n            }\n\n            if (inString)\n                return { };\n\n            return result;\n        }\n\n    }\n\n    BinaryPattern::BinaryPattern(const std::string &pattern) : m_patterns(parseBinaryPatternString(pattern)) { }\n\n    bool BinaryPattern::isValid() const { return !m_patterns.empty(); }\n\n    bool BinaryPattern::matches(const std::vector<u8> &bytes) const {\n        if (bytes.size() < m_patterns.size())\n            return false;\n\n        for (u32 i = 0; i < m_patterns.size(); i++) {\n            if (!this->matchesByte(bytes[i], i))\n                return false;\n        }\n\n        return true;\n    }\n\n    bool BinaryPattern::matchesByte(u8 byte, u32 offset) const {\n        const auto &pattern = m_patterns[offset];\n\n        return (byte & pattern.mask) == pattern.value;\n    }\n\n    u64 BinaryPattern::getSize() const {\n        return m_patterns.size();\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/crypto.cpp",
    "content": "#include <algorithm>\n#include <hex/helpers/crypto.hpp>\n\n#include <hex/providers/provider.hpp>\n\n#include <wolv/utils/guards.hpp>\n#include <wolv/utils/expected.hpp>\n\n#include <mbedtls/version.h>\n#include <mbedtls/base64.h>\n\n#if MBEDTLS_VERSION_MAJOR >= 4\n    // TODO: We'll need to migrate to the new <psa/crypto.h> eventually. For now, just include the old stuff again\n    #define MBEDTLS_DECLARE_PRIVATE_IDENTIFIERS\n    #include <mbedtls/private/bignum.h>\n    #include <mbedtls/private/md5.h>\n    #include <mbedtls/private/sha1.h>\n    #include <mbedtls/private/sha256.h>\n    #include <mbedtls/private/sha512.h>\n    #include <mbedtls/private/cipher.h>\n#else\n    #include <mbedtls/bignum.h>\n    #include <mbedtls/md5.h>\n    #include <mbedtls/sha1.h>\n    #include <mbedtls/sha256.h>\n    #include <mbedtls/sha512.h>\n    #include <mbedtls/cipher.h>\n#endif\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <bit>\n#include <span>\n\nnamespace hex::crypt {\n    using namespace std::placeholders;\n\n    template<std::invocable<unsigned char *, size_t> Func>\n    void processDataByChunks(prv::Provider *data, u64 offset, size_t size, Func func) {\n        std::array<u8, 512> buffer = { 0 };\n        for (size_t bufferOffset = 0; bufferOffset < size; bufferOffset += buffer.size()) {\n            const auto readSize = std::min(buffer.size(), size - bufferOffset);\n            data->read(offset + bufferOffset, buffer.data(), readSize);\n            func(buffer.data(), readSize);\n        }\n    }\n\n    template<typename T>\n    T reflect(T in, std::size_t bits) {\n        T out {};\n\n        for (std::size_t i = 0; i < bits; i++) {\n            out <<= 1;\n            if (in & 0b1)\n                out |= 1;\n            in >>= 1;\n        }\n        return out;\n    }\n\n    template<typename T>\n    T reflect(T in) {\n        if constexpr (sizeof(T) == 1) {\n            T out { in };\n\n            out = ((out & 0xf0u) >> 4) | ((out & 0x0fu) << 4);\n            out = ((out & 0xccu) >> 2) | ((out & 0x33u) << 2);\n            out = ((out & 0xaau) >> 1) | ((out & 0x55u) << 1);\n\n            return out;\n        } else {\n            return reflect(in, sizeof(T) * 8);\n        }\n    }\n\n    template<size_t NumBits> requires (std::has_single_bit(NumBits))\n    class Crc {\n        // Use reflected algorithm, so we reflect only if refin / refout is FALSE\n        // mask values, 0b1 << 64 is UB, so use 0b10 << 63\n\n    public:\n        constexpr Crc(u64 polynomial, u64 init, u64 xorOut, bool reflectInput, bool reflectOutput)\n            :  m_init(init & ((0b10ull << (NumBits - 1)) - 1)), m_xorOut(xorOut & ((0b10ull << (NumBits - 1)) - 1)),\n              m_reflectInput(reflectInput), m_reflectOutput(reflectOutput),\n              m_table([polynomial] {\n                auto reflectedPoly = reflect(polynomial & ((0b10ull << (NumBits - 1)) - 1), NumBits);\n                std::array<uint64_t, 256> table = { 0 };\n\n                for (uint32_t i = 0; i < 256; i++) {\n                    uint64_t c = i;\n                    for (std::size_t j = 0; j < 8; j++) {\n                        if (c & 0b1)\n                            c = reflectedPoly ^ (c >> 1);\n                        else\n                            c >>= 1;\n                    }\n                    table[i] = c;\n                }\n\n                return table;\n         }()) {\n            reset();\n        }\n\n        constexpr void reset() {\n            m_value = reflect(m_init, NumBits);\n        }\n\n        constexpr void processBytes(const unsigned char *data, std::size_t size) {\n            for (std::size_t i = 0; i < size; i++) {\n                u8 byte;\n                if (m_reflectInput)\n                    byte = data[i];\n                else\n                    byte = reflect(data[i]);\n\n                m_value = m_table[(m_value ^ byte) & 0xFFL] ^ (m_value >> 8);\n            }\n        }\n\n        [[nodiscard]]\n        constexpr u64 checksum() const {\n            if (m_reflectOutput)\n                return m_value ^ m_xorOut;\n            else\n                return reflect(m_value, NumBits) ^ m_xorOut;\n        }\n\n    private:\n        u64 m_value = 0x00;\n\n        u64 m_init;\n        u64 m_xorOut;\n        bool m_reflectInput;\n        bool m_reflectOutput;\n\n        std::array<uint64_t, 256> m_table;\n    };\n\n    template<size_t NumBits>\n    auto calcCrc(prv::Provider *data, u64 offset, std::size_t size, u32 polynomial, u32 init, u32 xorout, bool reflectIn, bool reflectOut) {\n        using Crc = Crc<NumBits>;\n        Crc crc(polynomial, init, xorout, reflectIn, reflectOut);\n\n        processDataByChunks(data, offset, size, [&crc](auto && data, auto && size) { crc.processBytes(data, size); });\n\n        return crc.checksum();\n    }\n\n    u8 crc8(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut) {\n        return calcCrc<8>(data, offset, size, polynomial, init, xorOut, reflectIn, reflectOut);\n    }\n\n    u16 crc16(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut) {\n        return calcCrc<16>(data, offset, size, polynomial, init, xorOut, reflectIn, reflectOut);\n    }\n\n    u32 crc32(prv::Provider *&data, u64 offset, size_t size, u32 polynomial, u32 init, u32 xorOut, bool reflectIn, bool reflectOut) {\n        return calcCrc<32>(data, offset, size, polynomial, init, xorOut, reflectIn, reflectOut);\n    }\n\n\n    std::array<u8, 16> md5(prv::Provider *&data, u64 offset, size_t size) {\n        std::array<u8, 16> result = { 0 };\n\n        mbedtls_md5_context ctx;\n        mbedtls_md5_init(&ctx);\n\n        mbedtls_md5_starts(&ctx);\n\n        processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_md5_update(&ctx, data, size); });\n\n        mbedtls_md5_finish(&ctx, result.data());\n\n        mbedtls_md5_free(&ctx);\n\n        return result;\n    }\n\n    std::array<u8, 16> md5(const std::vector<u8> &data) {\n        std::array<u8, 16> result = { 0 };\n\n        mbedtls_md5_context ctx;\n        mbedtls_md5_init(&ctx);\n\n        mbedtls_md5_starts(&ctx);\n        mbedtls_md5_update(&ctx, data.data(), data.size());\n        mbedtls_md5_finish(&ctx, result.data());\n\n        mbedtls_md5_free(&ctx);\n\n        return result;\n    }\n\n    std::array<u8, 20> sha1(prv::Provider *&data, u64 offset, size_t size) {\n        std::array<u8, 20> result = { 0 };\n\n        mbedtls_sha1_context ctx;\n        mbedtls_sha1_init(&ctx);\n\n        mbedtls_sha1_starts(&ctx);\n\n        processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha1_update(&ctx, data, size); });\n\n        mbedtls_sha1_finish(&ctx, result.data());\n\n        mbedtls_sha1_free(&ctx);\n\n        return result;\n    }\n\n    std::array<u8, 20> sha1(const std::vector<u8> &data) {\n        std::array<u8, 20> result = { 0 };\n\n        mbedtls_sha1_context ctx;\n        mbedtls_sha1_init(&ctx);\n\n        mbedtls_sha1_starts(&ctx);\n        mbedtls_sha1_update(&ctx, data.data(), data.size());\n        mbedtls_sha1_finish(&ctx, result.data());\n\n        mbedtls_sha1_free(&ctx);\n\n        return result;\n    }\n\n    std::array<u8, 28> sha224(prv::Provider *&data, u64 offset, size_t size) {\n        std::array<u8, 28> result = { 0 };\n\n        mbedtls_sha256_context ctx;\n        mbedtls_sha256_init(&ctx);\n\n        mbedtls_sha256_starts(&ctx, true);\n\n        processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha256_update(&ctx, data, size); });\n\n        mbedtls_sha256_finish(&ctx, result.data());\n\n        mbedtls_sha256_free(&ctx);\n\n        return result;\n    }\n\n    std::array<u8, 28> sha224(const std::vector<u8> &data) {\n        std::array<u8, 28> result = { 0 };\n\n        mbedtls_sha256_context ctx;\n        mbedtls_sha256_init(&ctx);\n\n        mbedtls_sha256_starts(&ctx, true);\n        mbedtls_sha256_update(&ctx, data.data(), data.size());\n        mbedtls_sha256_finish(&ctx, result.data());\n\n        mbedtls_sha256_free(&ctx);\n\n        return result;\n    }\n\n    std::array<u8, 32> sha256(prv::Provider *&data, u64 offset, size_t size) {\n        std::array<u8, 32> result = { 0 };\n\n        mbedtls_sha256_context ctx;\n        mbedtls_sha256_init(&ctx);\n\n        mbedtls_sha256_starts(&ctx, false);\n\n        processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha256_update(&ctx, data, size); });\n\n        mbedtls_sha256_finish(&ctx, result.data());\n\n        mbedtls_sha256_free(&ctx);\n\n        return result;\n    }\n\n    std::array<u8, 32> sha256(const std::vector<u8> &data) {\n        std::array<u8, 32> result = { 0 };\n\n        mbedtls_sha256_context ctx;\n        mbedtls_sha256_init(&ctx);\n\n        mbedtls_sha256_starts(&ctx, false);\n        mbedtls_sha256_update(&ctx, data.data(), data.size());\n        mbedtls_sha256_finish(&ctx, result.data());\n\n        mbedtls_sha256_free(&ctx);\n\n        return result;\n    }\n\n    std::array<u8, 48> sha384(prv::Provider *&data, u64 offset, size_t size) {\n        std::array<u8, 48> result = { 0 };\n\n        mbedtls_sha512_context ctx;\n        mbedtls_sha512_init(&ctx);\n\n        mbedtls_sha512_starts(&ctx, true);\n\n        processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha512_update(&ctx, data, size); });\n\n        mbedtls_sha512_finish(&ctx, result.data());\n\n        mbedtls_sha512_free(&ctx);\n\n        return result;\n    }\n\n    std::array<u8, 48> sha384(const std::vector<u8> &data) {\n        std::array<u8, 48> result = { 0 };\n\n        mbedtls_sha512_context ctx;\n        mbedtls_sha512_init(&ctx);\n\n        mbedtls_sha512_starts(&ctx, true);\n        mbedtls_sha512_update(&ctx, data.data(), data.size());\n        mbedtls_sha512_finish(&ctx, result.data());\n\n        mbedtls_sha512_free(&ctx);\n\n        return result;\n    }\n\n    std::array<u8, 64> sha512(prv::Provider *&data, u64 offset, size_t size) {\n        std::array<u8, 64> result = { 0 };\n\n        mbedtls_sha512_context ctx;\n        mbedtls_sha512_init(&ctx);\n\n        mbedtls_sha512_starts(&ctx, false);\n\n        processDataByChunks(data, offset, size, [&ctx](auto && data, auto && size) { return mbedtls_sha512_update(&ctx, data, size); });\n\n        mbedtls_sha512_finish(&ctx, result.data());\n\n        mbedtls_sha512_free(&ctx);\n\n        return result;\n    }\n\n    std::array<u8, 64> sha512(const std::vector<u8> &data) {\n        std::array<u8, 64> result = { 0 };\n\n        mbedtls_sha512_context ctx;\n        mbedtls_sha512_init(&ctx);\n\n        mbedtls_sha512_starts(&ctx, false);\n        mbedtls_sha512_update(&ctx, data.data(), data.size());\n        mbedtls_sha512_finish(&ctx, result.data());\n\n        mbedtls_sha512_free(&ctx);\n\n        return result;\n    }\n\n\n    std::vector<u8> decode64(const std::vector<u8> &input) {\n\n        size_t written = 0;\n        mbedtls_base64_decode(nullptr, 0, &written, input.data(), input.size());\n        std::vector<u8> output(written, 0x00);\n        if (mbedtls_base64_decode(output.data(), output.size(), &written, input.data(), input.size()))\n            return {};\n\n        output.resize(written);\n\n        return output;\n    }\n\n    std::vector<u8> encode64(const std::vector<u8> &input) {\n\n        size_t written = 0;\n        mbedtls_base64_encode(nullptr, 0, &written, input.data(), input.size());\n\n        std::vector<u8> output(written, 0x00);\n        if (mbedtls_base64_encode(output.data(), output.size(), &written, input.data(), input.size()))\n            return {};\n\n        output.resize(written);\n\n        return output;\n    }\n\n    std::vector<u8> decode16(const std::string &input) {\n        std::vector<u8> output(input.length() / 2, 0x00);\n\n\n        mbedtls_mpi ctx;\n        mbedtls_mpi_init(&ctx);\n\n        ON_SCOPE_EXIT { mbedtls_mpi_free(&ctx); };\n\n        // Read buffered\n        constexpr static auto BufferSize = 0x100;\n        for (size_t offset = 0; offset < input.size(); offset += BufferSize) {\n            std::string inputPart = input.substr(offset, std::min<size_t>(BufferSize, input.size() - offset));\n            if (mbedtls_mpi_read_string(&ctx, 16, inputPart.c_str()))\n                return {};\n\n            if (mbedtls_mpi_write_binary(&ctx, output.data() + offset / 2, inputPart.size() / 2))\n                return {};\n        }\n\n        return output;\n    }\n\n    std::string encode16(const std::vector<u8> &input) {\n\n        if (input.empty())\n            return {};\n\n        std::string output(input.size() * 2, '\\0');\n\n        for (size_t i = 0; i < input.size(); i++) {\n            output[2 * i + 0] = \"0123456789ABCDEF\"[input[i] / 16];\n            output[2 * i + 1] = \"0123456789ABCDEF\"[input[i] % 16];\n        }\n\n        return output;\n    }\n\n    template<typename T>\n    static T safeLeftShift(T t, u32 shift) {\n        if (shift >= sizeof(t) * 8) {\n            return 0;\n        } else {\n            return t << shift;\n        }\n    }\n\n    template<typename T>\n    static T decodeLeb128(const std::vector<u8> &bytes) {\n        T value = 0;\n        u32 shift = 0;\n        u8 b = 0;\n        for (u8 byte : bytes) {\n            b = byte;\n            value |= safeLeftShift(static_cast<T>(byte & 0x7F), shift);\n            shift += 7;\n            if ((byte & 0x80) == 0) {\n                break;\n            }\n        }\n        if constexpr(std::signed_integral<T>) {\n            if ((b & 0x40) != 0) {\n                value |= safeLeftShift(~static_cast<T>(0), shift);\n            }\n        }\n        return value;\n    }\n\n    u128 decodeUleb128(const std::vector<u8> &bytes) {\n        return decodeLeb128<u128>(bytes);\n    }\n\n    i128 decodeSleb128(const std::vector<u8> &bytes) {\n        return decodeLeb128<i128>(bytes);\n    }\n\n    template<typename T>\n    static std::vector<u8> encodeLeb128(T value) {\n        std::vector<u8> bytes;\n        u8 byte;\n        while (true) {\n            byte = u8(value & 0x7F);\n            value >>= 7;\n            if constexpr(std::signed_integral<T>) {\n                if (value == 0 && (byte & 0x40) == 0) {\n                    break;\n                }\n                if (value == -1 && (byte & 0x40) != 0) {\n                    break;\n                }\n            } else {\n                if (value == 0) {\n                    break;\n                }\n            }\n            bytes.push_back(byte | 0x80);\n        }\n        bytes.push_back(byte);\n        return bytes;\n    }\n\n    std::vector<u8> encodeUleb128(u128 value) {\n        return encodeLeb128<u128>(value);\n    }\n\n    std::vector<u8> encodeSleb128(i128 value) {\n        return encodeLeb128<i128>(value);\n    }\n\n    static wolv::util::Expected<std::vector<u8>, int> aes(mbedtls_cipher_type_t type, mbedtls_operation_t operation, const std::vector<u8> &key,\n                   std::array<u8, 8> nonce, std::array<u8, 8> iv, const std::span<const u8> &input) {\n\n        if (input.empty())\n            return {};\n        if (key.size() > 256)\n            return {};\n\n        mbedtls_cipher_context_t ctx;\n        auto cipherInfo = mbedtls_cipher_info_from_type(type);\n\n        if (cipherInfo == nullptr)\n            return {};\n\n        int setupResult = mbedtls_cipher_setup(&ctx, cipherInfo);\n        if (setupResult != 0)\n            return wolv::util::Unexpected(setupResult);\n\n        int setKeyResult = mbedtls_cipher_setkey(&ctx, key.data(), key.size() * 8, operation);\n        if (setKeyResult != 0)\n            return wolv::util::Unexpected(setKeyResult);\n\n        std::array<u8, 16> nonceCounter = { 0 };\n\n        auto mode = mbedtls_cipher_get_cipher_mode(&ctx);\n\n        // if we are in ECB mode, we don't need to set the nonce\n        if (mode != MBEDTLS_MODE_ECB) {\n            std::ranges::copy(nonce, nonceCounter.begin());\n            std::ranges::copy(iv, nonceCounter.begin() + 8);\n        }\n\n        size_t outputSize = input.size() + mbedtls_cipher_get_block_size(&ctx);\n        std::vector<u8> output(outputSize, 0x00);\n\n        int cryptResult = 0;\n        if (mode == MBEDTLS_MODE_ECB) {\n            cryptResult = mbedtls_cipher_crypt(&ctx, nullptr, 0, input.data(), input.size(), output.data(), &outputSize);\n        } else {\n            cryptResult = mbedtls_cipher_crypt(&ctx, nonceCounter.data(), nonceCounter.size(), input.data(), input.size(), output.data(), &outputSize);\n        }\n\n        // free regardless of the result\n        mbedtls_cipher_free(&ctx);\n\n        if (cryptResult != 0) {\n            return wolv::util::Unexpected(cryptResult);\n        }\n\n        output.resize(input.size());\n\n        return output;\n    }\n\n    wolv::util::Expected<std::vector<u8>, int> aesDecrypt(AESMode mode, KeyLength keyLength, const std::vector<u8> &key, std::array<u8, 8> nonce, std::array<u8, 8> iv, const std::vector<u8> &input) {\n        switch (keyLength) {\n            case KeyLength::Key128Bits:\n                if (key.size() != 128 / 8)\n                    return wolv::util::Unexpected(CRYPTO_ERROR_INVALID_KEY_LENGTH);\n                break;\n            case KeyLength::Key192Bits:\n                if (key.size() != 192 / 8)\n                    return wolv::util::Unexpected(CRYPTO_ERROR_INVALID_KEY_LENGTH);\n                break;\n            case KeyLength::Key256Bits:\n                if (key.size() != 256 / 8)\n                    return wolv::util::Unexpected(CRYPTO_ERROR_INVALID_KEY_LENGTH);\n                break;\n            default:\n                return wolv::util::Unexpected(CRYPTO_ERROR_INVALID_KEY_LENGTH);\n        }\n\n        mbedtls_cipher_type_t type;\n        switch (mode) {\n            case AESMode::ECB:\n                type = MBEDTLS_CIPHER_AES_128_ECB;\n                break;\n            case AESMode::CBC:\n                type = MBEDTLS_CIPHER_AES_128_CBC;\n                break;\n            case AESMode::CFB128:\n                type = MBEDTLS_CIPHER_AES_128_CFB128;\n                break;\n            case AESMode::CTR:\n                type = MBEDTLS_CIPHER_AES_128_CTR;\n                break;\n            case AESMode::GCM:\n                type = MBEDTLS_CIPHER_AES_128_GCM;\n                break;\n            case AESMode::CCM:\n                type = MBEDTLS_CIPHER_AES_128_CCM;\n                break;\n            case AESMode::OFB:\n                type = MBEDTLS_CIPHER_AES_128_OFB;\n                break;\n            case AESMode::XTS:\n                type = MBEDTLS_CIPHER_AES_128_XTS;\n                break;\n            default:\n                return wolv::util::Unexpected(CRYPTO_ERROR_INVALID_MODE);\n        }\n\n        type = mbedtls_cipher_type_t(type + u8(keyLength));\n\n        return aes(type, MBEDTLS_DECRYPT, key, nonce, iv, input);\n    }\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/helpers/debugging.cpp",
    "content": "#include <hex/helpers/debugging.hpp>\n#include <hex/helpers/logger.hpp>\n#include <hex/trace/stacktrace.hpp>\n\nnamespace hex::dbg {\n\n    namespace impl {\n\n        bool &getDebugWindowState() {\n            static bool state = false;\n\n            return state;\n        }\n\n    }\n\n    static bool s_debugMode = false;\n    bool debugModeEnabled() {\n        return s_debugMode;\n    }\n\n    void setDebugModeEnabled(bool enabled) {\n        s_debugMode = enabled;\n    }\n\n    [[noreturn]] void assertionHandler(const char* file, int line, const char *function, const char* exprString) {\n        log::error(\"Assertion failed: {} at {}:{} => {}\", exprString, file, line, function);\n\n        const auto stackTrace = trace::getStackTrace();\n        dbg::printStackTrace(stackTrace);\n\n        std::abort();\n    }\n\n    void printStackTrace(const trace::StackTraceResult &stackTrace) {\n        log::fatal(\"Printing stacktrace using implementation '{}'\", stackTrace.implementationName);\n        for (const auto &stackFrame : stackTrace.stackFrames) {\n            if (stackFrame.line == 0)\n                log::fatal(\"  ({}) | {}\", stackFrame.file, stackFrame.function);\n            else\n                log::fatal(\"  ({}:{}) | {}\",  stackFrame.file, stackFrame.line, stackFrame.function);\n        }\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/default_paths.cpp",
    "content": "#include <algorithm>\n#include <hex/helpers/default_paths.hpp>\n\n#include <hex/api/imhex_api/system.hpp>\n#include <hex/api/project_file_manager.hpp>\n\n#include <ranges>\n#include <set>\n\n#if defined(OS_WINDOWS)\n    #include <windows.h>\n    #include <shlobj.h>\n#elif defined(OS_LINUX) || defined(OS_WEB)\n    #include <xdg.hpp>\n# endif\n\nnamespace hex::paths {\n\n    std::vector<std::fs::path> getDataPaths(bool includeSystemFolders) {\n        std::vector<std::fs::path> paths;\n        std::set<std::fs::path> uniquePaths;\n\n        const auto emplaceUniquePath = [&](const std::fs::path &path) {\n            auto duplicate = uniquePaths.insert(path);\n            if (duplicate.second)\n                paths.emplace_back(path);\n        };\n\n        #if defined(OS_WINDOWS)\n\n            // In the portable Windows version, we just use the executable directory\n            // Prevent the use of the AppData folder here\n            if (!ImHexApi::System::isPortableVersion()) {\n                PWSTR wAppDataPath = nullptr;\n                if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &wAppDataPath))) {\n                    emplaceUniquePath(wAppDataPath);\n                    CoTaskMemFree(wAppDataPath);\n                }\n            }\n\n        #elif defined(OS_MACOS)\n\n            emplaceUniquePath(wolv::io::fs::getApplicationSupportDirectoryPath() / \"imhex\");\n\n        #elif defined(OS_LINUX) || defined(OS_WEB)\n\n            emplaceUniquePath(xdg::DataHomeDir());\n\n            auto dataDirs = xdg::DataDirs();\n            for (const auto &path : dataDirs)\n                emplaceUniquePath(path);\n\n        #endif\n\n        #if defined(OS_MACOS)\n\n            if (includeSystemFolders) {\n                if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) {\n                    emplaceUniquePath(executablePath->parent_path());\n                }\n            }\n\n        #else\n\n            uniquePaths.clear();\n            for (auto &path : paths) {\n                path = path / \"imhex\";\n                uniquePaths.insert(path);\n            }\n\n            if (ImHexApi::System::isPortableVersion() || includeSystemFolders) {\n                if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value())\n                    emplaceUniquePath(executablePath->parent_path());\n            }\n\n        #endif\n\n\n        // Add additional data directories to the path\n        auto additionalDirs = ImHexApi::System::getAdditionalFolderPaths();\n        for (const auto &path : additionalDirs)\n            emplaceUniquePath(path);\n\n        // Add the project file directory to the path, if one is loaded\n        if (ProjectFile::hasPath()) {\n            emplaceUniquePath(ProjectFile::getPath().parent_path());\n        }\n\n        return paths;\n    }\n\n    std::vector<std::fs::path> getConfigPaths(bool includeSystemFolders) {\n        #if defined(OS_WINDOWS)\n            return getDataPaths(includeSystemFolders);\n        #elif defined(OS_MACOS)\n            return getDataPaths(includeSystemFolders);\n        #elif defined(OS_LINUX) || defined(OS_WEB)\n            std::ignore = includeSystemFolders;\n            return {xdg::ConfigHomeDir() / \"imhex\"};\n        #endif\n    }\n\n    static std::vector<std::fs::path> appendPath(std::vector<std::fs::path> paths, std::fs::path folder) {\n        folder.make_preferred();\n        \n        for (auto &path : paths)\n            path = path / folder;\n\n        return paths;\n    }\n\n    static std::vector<std::fs::path> getPluginPaths() {\n        // If running from an AppImage, only allow loaded plugins from inside it\n        #if defined(OS_LINUX)\n        if(const char* appdir = std::getenv(\"APPDIR\")) { // check for AppImage environment\n            return {std::string(appdir) + \"/usr/lib/imhex\"};\n        }\n        #endif\n\n        std::vector<std::fs::path> paths = getDataPaths(true);\n\n        // Add the system plugin directory to the path if one was provided at compile time\n        #if defined(OS_LINUX) && defined(SYSTEM_PLUGINS_LOCATION)\n            paths.emplace_back(SYSTEM_PLUGINS_LOCATION);\n        #endif\n\n        return paths;\n    }\n\n    namespace impl {\n\n        std::vector<std::fs::path> DefaultPath::read() const {\n            auto result = this->all();\n\n            std::erase_if(result, [](const auto &entryPath) {\n                return !wolv::io::fs::isDirectory(entryPath);\n            });\n\n            return result;\n        }\n\n        std::vector<std::fs::path> DefaultPath::write() const {\n            auto result = this->read();\n\n            std::erase_if(result, [](const auto &entryPath) {\n                return !hex::fs::isPathWritable(entryPath);\n            });\n\n            return result;\n        }\n\n        std::vector<std::fs::path> ConfigPath::all() const {\n            return appendPath(getConfigPaths(false), m_postfix);\n        }\n\n        std::vector<std::fs::path> DataPath::all() const {\n            return appendPath(getDataPaths(true), m_postfix);\n        }\n\n        std::vector<std::fs::path> DataPath::write() const {\n            auto result = appendPath(getDataPaths(false), m_postfix);\n\n            std::erase_if(result, [](const auto &entryPath) {\n                return !hex::fs::isPathWritable(entryPath);\n            });\n\n            return result;\n        }\n\n        std::vector<std::fs::path> PluginPath::all() const {\n            return appendPath(getPluginPaths(), m_postfix);\n        }\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/encoding_file.cpp",
    "content": "#include <hex/helpers/encoding_file.hpp>\n\n#include <hex/helpers/utils.hpp>\n\n#include <ranges>\n#include <wolv/io/file.hpp>\n#include <wolv/utils/string.hpp>\n\nnamespace hex {\n\n    EncodingFile::EncodingFile() : m_mapping(std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>()) {\n\n    }\n\n    EncodingFile::EncodingFile(const hex::EncodingFile &other) {\n        m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping);\n        m_tableContent = other.m_tableContent;\n        m_longestSequence = other.m_longestSequence;\n        m_shortestSequence = other.m_shortestSequence;\n        m_valid = other.m_valid;\n        m_name = other.m_name;\n    }\n\n    EncodingFile::EncodingFile(EncodingFile &&other) noexcept {\n        m_mapping = std::move(other.m_mapping);\n        m_tableContent = std::move(other.m_tableContent);\n        m_longestSequence = other.m_longestSequence;\n        m_shortestSequence = other.m_shortestSequence;\n        m_valid = other.m_valid;\n        m_name = std::move(other.m_name);\n    }\n\n    EncodingFile::EncodingFile(Type type, const std::fs::path &path) : EncodingFile() {\n        auto file = wolv::io::File(path, wolv::io::File::Mode::Read);\n        switch (type) {\n            case Type::Thingy:\n                parse(file.readString());\n                break;\n            default:\n                return;\n        }\n\n        {\n            m_name = path.stem().string();\n            m_name = wolv::util::replaceStrings(m_name, \"_\", \" \");\n\n            if (!m_name.empty())\n                m_name[0] = std::toupper(m_name[0]);\n        }\n\n        m_valid = true;\n    }\n\n    EncodingFile::EncodingFile(Type type, const std::string &content) : EncodingFile() {\n        switch (type) {\n            case Type::Thingy:\n                parse(content);\n                break;\n            default:\n                return;\n        }\n\n        m_name = \"Unknown\";\n        m_valid = true;\n    }\n\n\n    EncodingFile &EncodingFile::operator=(const hex::EncodingFile &other) {\n        if(this == &other) {\n            return *this;\n        }\n        m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping);\n        m_tableContent = other.m_tableContent;\n        m_longestSequence = other.m_longestSequence;\n        m_shortestSequence = other.m_shortestSequence;\n        m_valid = other.m_valid;\n        m_name = other.m_name;\n\n        return *this;\n    }\n\n    EncodingFile &EncodingFile::operator=(EncodingFile &&other) noexcept {\n        m_mapping = std::move(other.m_mapping);\n        m_tableContent = std::move(other.m_tableContent);\n        m_longestSequence = other.m_longestSequence;\n        m_shortestSequence = other.m_shortestSequence;\n        m_valid = other.m_valid;\n        m_name = std::move(other.m_name);\n\n        return *this;\n    }\n\n\n\n    std::pair<std::string_view, size_t> EncodingFile::getEncodingFor(std::span<const u8> buffer) const {\n        for (const auto &[size, mapping] : std::ranges::reverse_view(*m_mapping)) {\n            if (size > buffer.size()) continue;\n\n            std::vector key(buffer.begin(), buffer.begin() + size);\n            if (mapping.contains(key))\n                return { mapping.at(key), size };\n        }\n\n        return { \".\", 1 };\n    }\n\n    u64 EncodingFile::getEncodingLengthFor(std::span<u8> buffer) const {\n        for (const auto& [size, mapping] : std::ranges::reverse_view(*m_mapping)) {\n            if (size > buffer.size()) continue;\n\n            std::vector key(buffer.begin(), buffer.begin() + size);\n            if (mapping.contains(key))\n                return size;\n        }\n\n        return 1;\n    }\n\n    std::string EncodingFile::decodeAll(std::span<const u8> buffer) const {\n        std::string result;\n\n        while (!buffer.empty()) {\n            const auto [character, size] = getEncodingFor(buffer);\n            result += character;\n            buffer = buffer.subspan(size);\n        }\n\n        return result;\n    }\n\n\n    void EncodingFile::parse(const std::string &content) {\n        m_tableContent = content;\n        for (const auto &line : wolv::util::splitString(m_tableContent, \"\\n\")) {\n\n            std::string from, to;\n            {\n                auto delimiterPos = line.find('=');\n\n                if (delimiterPos >= line.length())\n                    continue;\n\n                from = line.substr(0, delimiterPos);\n                to   = line.substr(delimiterPos + 1);\n\n                if (from.empty()) continue;\n            }\n\n            auto fromBytes = hex::parseByteString(from);\n            if (fromBytes.empty()) continue;\n\n            if (to.length() > 1)\n                to = wolv::util::trim(to);\n            if (to.empty())\n                to = \" \";\n\n            if (!m_mapping->contains(fromBytes.size()))\n                m_mapping->insert({ fromBytes.size(), {} });\n\n            u64 keySize = fromBytes.size();\n            (*m_mapping)[keySize].insert({ std::move(fromBytes), to });\n\n            m_longestSequence = std::max(m_longestSequence, keySize);\n            m_shortestSequence = std::min(m_shortestSequence, keySize);\n        }\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/fs.cpp",
    "content": "#include <hex/helpers/fs.hpp>\n\n#include <hex/helpers/logger.hpp>\n#include <hex/helpers/fmt.hpp>\n#include <hex/helpers/utils_linux.hpp>\n#include <hex/helpers/auto_reset.hpp>\n\n#if defined(OS_WINDOWS)\n    #include <windows.h>\n    #include <shlobj.h>\n    #include <shellapi.h>\n#elif defined(OS_LINUX) || defined(OS_WEB)\n    #include <xdg.hpp>\n# if defined(OS_FREEBSD)\n    #include <sys/syslimits.h>\n# endif\n#endif\n\n#if defined(OS_WEB)\n    #include <emscripten.h>\n#else\n    #include <GLFW/glfw3.h>\n    #include <nfd.hpp>\n#endif\n\n#include <filesystem>\n\n#include <wolv/io/file.hpp>\n#include <wolv/io/fs.hpp>\n#include <wolv/utils/string.hpp>\n\n#include <fmt/format.h>\n#include <fmt/xchar.h>\n\nnamespace hex::fs {\n\n    static AutoReset<std::function<void(const std::string&)>> s_fileBrowserErrorCallback;\n    void setFileBrowserErrorCallback(const std::function<void(const std::string&)> &callback) {\n        s_fileBrowserErrorCallback = callback;\n    }\n\n    // With help from https://github.com/owncloud/client/blob/cba22aa34b3677406e0499aadd126ce1d94637a2/src/gui/openfilemanager.cpp\n    void openFileExternal(std::fs::path filePath) {\n        filePath.make_preferred();\n\n        // Make sure the file exists before trying to open it\n        if (!wolv::io::fs::exists(filePath)) {\n            return;\n        }\n\n        #if defined(OS_WINDOWS)\n            std::ignore = ShellExecuteW(nullptr, L\"open\", filePath.c_str(), nullptr, nullptr, SW_SHOWNORMAL);\n        #elif defined(OS_MACOS)\n            std::ignore = system(\n                fmt::format(\"open {}\", wolv::util::toUTF8String(filePath)).c_str()\n            );\n        #elif defined(OS_LINUX)\n            executeCmd({\"xdg-open\", wolv::util::toUTF8String(filePath)});\n        #endif\n    }\n\n    void openFolderExternal(std::fs::path dirPath) {\n        dirPath.make_preferred();\n\n        // Make sure the folder exists before trying to open it\n        if (!wolv::io::fs::exists(dirPath)) {\n            return;\n        }\n\n        #if defined(OS_WINDOWS)\n            auto args = fmt::format(L\"\\\"{}\\\"\", dirPath.c_str());\n            ShellExecuteW(nullptr, L\"open\", L\"explorer.exe\", args.c_str(), nullptr, SW_SHOWNORMAL);\n        #elif defined(OS_MACOS)\n            std::ignore = system(\n                fmt::format(\"open {}\", wolv::util::toUTF8String(dirPath)).c_str()\n            );\n        #elif defined(OS_LINUX)\n            executeCmd({\"xdg-open\", wolv::util::toUTF8String(dirPath)});\n        #endif\n    }\n\n    void openFolderWithSelectionExternal(std::fs::path selectedFilePath) {\n        selectedFilePath.make_preferred();\n\n        // Make sure the file exists before trying to open it\n        if (!wolv::io::fs::exists(selectedFilePath)) {\n            return;\n        }\n\n        #if defined(OS_WINDOWS)\n            auto args = fmt::format(L\"/select,\\\"{}\\\"\", selectedFilePath.c_str());\n            ShellExecuteW(nullptr, L\"open\", L\"explorer.exe\", args.c_str(), nullptr, SW_SHOWNORMAL);\n        #elif defined(OS_MACOS)\n            std::ignore = system(\n                fmt::format(\n                    R\"(osascript -e 'tell application \"Finder\" to reveal POSIX file \"{}\"')\",\n                    wolv::util::toUTF8String(selectedFilePath)\n                ).c_str()\n            );\n            system(R\"(osascript -e 'tell application \"Finder\" to activate')\");\n        #elif defined(OS_LINUX)\n            // Fallback to only opening the folder for now\n            // TODO actually select the file\n            executeCmd({\"xdg-open\", wolv::util::toUTF8String(selectedFilePath.parent_path())});\n        #endif\n    }\n\n    #if defined(OS_WEB)\n\n        std::function<void(std::fs::path)> currentCallback;\n\n        EMSCRIPTEN_KEEPALIVE\n        extern \"C\" void fileBrowserCallback(char* path) {\n            currentCallback(path);\n        }\n\n        EM_JS(int, callJs_saveFile, (const char *rawFilename), {\n            let filename = UTF8ToString(rawFilename) || \"file.bin\";\n            FS.createPath(\"/\", \"savedFiles\");\n\n            if (FS.analyzePath(filename).exists) {\n                FS.unlink(filename);\n            }\n\n            // Call callback that will write the file\n            Module._fileBrowserCallback(stringToNewUTF8(\"/savedFiles/\" + filename));\n\n            let data;\n            try {\n                data = FS.readFile(\"/savedFiles/\" + filename);\n            } catch (e) {\n                console.log(e);\n                return;\n            }\n\n            const reader = Object.assign(new FileReader(), {\n                onload: () => {\n\n                    // Show popup to user to download\n                    let saver = document.createElement('a');\n                    saver.href = reader.result;\n                    saver.download = filename;\n                    saver.style = \"display: none\";\n\n                    saver.click();\n\n                },\n                onerror: () => {\n                    throw new Error(reader.error);\n                },\n            });\n            reader.readAsDataURL(new File([data], \"\", { type: \"application/octet-stream\" }));\n\n        });\n\n        EM_JS(int, callJs_openFile, (bool multiple), {\n            let selector = document.createElement(\"input\");\n            selector.type = \"file\";\n            selector.style = \"display: none\";\n            if (multiple) {\n                selector.multiple = true;\n            }\n            selector.onchange = () => {\n                if (selector.files.length == 0) return;\n\n                FS.createPath(\"/\", \"openedFiles\");\n                for (let file of selector.files) {\n                    const fr = new FileReader();\n                    fr.onload = () => {\n                        let folder = \"/openedFiles/\"+Math.random().toString(36).substring(2)+\"/\";\n                        FS.createPath(\"/\", folder);\n                        if (FS.analyzePath(folder+file.name).exists) {\n                            console.log(`Error: ${folder+file.name} already exist`);\n                        } else {\n                            FS.createDataFile(folder, file.name, fr.result, true, true);\n                            Module._fileBrowserCallback(stringToNewUTF8(folder+file.name));\n                        }\n                    };\n\n                    fr.readAsBinaryString(file);\n                }\n            };\n            selector.click();\n        });\n\n        bool openFileBrowser(DialogMode mode, const std::vector<ItemFilter> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath, bool multiple) {\n            switch (mode) {\n                case DialogMode::Open: {\n                    currentCallback = callback;\n                    callJs_openFile(multiple);\n                    break;\n                }\n                case DialogMode::Save: {\n                    currentCallback = callback;\n                    std::fs::path path;\n\n                    if (!defaultPath.empty())\n                        path = std::fs::path(defaultPath).filename();\n                    else if (!validExtensions.empty())\n                        path = \"file.\" + validExtensions[0].spec;\n\n                    std::fs::create_directory(\"/savedFiles\");\n                    callJs_saveFile(path.filename().string().c_str());\n                    break;\n                }\n                case DialogMode::Folder: {\n                    throw std::logic_error(\"Selecting a folder is not implemented\");\n                    return false;\n                }\n                default:\n                    std::unreachable();\n            }\n            return true;\n        }\n\n    #else\n\n        bool openFileBrowser(DialogMode mode, const std::vector<ItemFilter> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath, bool multiple) {\n            // Turn the content of the ItemFilter objects into something NFD understands\n            std::vector<nfdfilteritem_t> validExtensionsNfd;\n            validExtensionsNfd.reserve(validExtensions.size());\n            for (const auto &extension : validExtensions) {\n                validExtensionsNfd.emplace_back(nfdfilteritem_t{ extension.name.c_str(), extension.spec.c_str() });\n            }\n\n            // Clear errors from previous runs\n            NFD::ClearError();\n\n            // Try to initialize NFD\n            if (NFD::Init() != NFD_OKAY) {\n                // Handle errors if initialization failed\n                log::error(\"NFD init returned an error: {}\", NFD::GetError());\n                if (*s_fileBrowserErrorCallback != nullptr) {\n                    const auto error = NFD::GetError();\n                    (*s_fileBrowserErrorCallback)(error != nullptr ? error : \"No details\");\n                }\n\n                return false;\n            }\n\n            NFD::UniquePathU8 outPath;\n            NFD::UniquePathSet outPaths;\n            nfdresult_t result = NFD_ERROR;\n\n            // Open the correct file dialog based on the mode\n            switch (mode) {\n                case DialogMode::Open:\n                    if (multiple)\n                        result = NFD::OpenDialogMultiple(outPaths, validExtensionsNfd.data(), validExtensionsNfd.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());\n                    else\n                        result = NFD::OpenDialog(outPath, validExtensionsNfd.data(), validExtensionsNfd.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());\n                    break;\n                case DialogMode::Save:\n                    result = NFD::SaveDialog(outPath, validExtensionsNfd.data(), validExtensionsNfd.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());\n                    break;\n                case DialogMode::Folder:\n                    result = NFD::PickFolder(outPath, defaultPath.empty() ? nullptr : defaultPath.c_str());\n                    break;\n            }\n\n            if (result == NFD_OKAY){\n                // Handle the path if the dialog was opened in single mode\n                if (outPath != nullptr) {\n                    // Call the provided callback with the path\n                    callback(outPath.get());\n                }\n\n                // Handle multiple paths if the dialog was opened in multiple mode\n                if (outPaths != nullptr) {\n                    nfdpathsetsize_t numPaths = 0;\n                    if (NFD::PathSet::Count(outPaths, numPaths) == NFD_OKAY) {\n                        // Loop over all returned paths and call the callback with each of them\n                        for (size_t i = 0; i < numPaths; i++) {\n                            NFD::UniquePathSetPath path;\n                            if (NFD::PathSet::GetPath(outPaths, i, path) == NFD_OKAY)\n                                callback(path.get());\n                        }\n                    }\n                }\n            } else if (result == NFD_ERROR) {\n                // Handle errors that occurred during the file dialog call\n\n                log::error(\"Requested file dialog returned an error: {}\", NFD::GetError());\n\n                if (*s_fileBrowserErrorCallback != nullptr) {\n                    const auto error = NFD::GetError();\n                    (*s_fileBrowserErrorCallback)(error != nullptr ? error : \"No details\");\n                }\n            }\n\n            NFD::Quit();\n\n            return result == NFD_OKAY;\n        }\n\n    #endif\n\n    bool isPathWritable(const std::fs::path &path) {\n        constexpr static auto TestFileName = \"__imhex__tmp__\";\n\n        // Try to open the __imhex__tmp__ file in the given path\n        // If one does exist already, try to delete it\n        {\n            wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Read);\n            if (file.isValid()) {\n                if (!file.remove())\n                    return false;\n            }\n        }\n\n        // Try to create a new file in the given path\n        // If that fails, or the file cannot be deleted anymore afterward; the path is not writable\n        wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Create);\n        const bool result = file.isValid();\n        if (!file.remove())\n            return false;\n\n        return result;\n    }\n\n    std::fs::path toShortPath(const std::fs::path &path) {\n        #if defined(OS_WINDOWS)\n            // Get the size of the short path\n            size_t size = GetShortPathNameW(path.c_str(), nullptr, 0);\n            if (size == 0)\n                return path;\n\n            // Get the short path\n            std::wstring newPath(size, 0x00);\n            GetShortPathNameW(path.c_str(), newPath.data(), newPath.size());\n            newPath.pop_back();\n\n            return newPath;\n        #else\n            // Other supported platforms don't have short paths\n            return path;\n        #endif\n    }\n\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/helpers/http_requests.cpp",
    "content": "#include <hex/helpers/http_requests.hpp>\n\n#include <hex/helpers/crypto.hpp>\n#include <hex/helpers/fmt.hpp>\n\nnamespace hex {\n\n    size_t HttpRequest::writeToVector(void *contents, size_t size, size_t nmemb, void *userdata) {\n        auto &response = *static_cast<std::vector<u8>*>(userdata);\n        auto startSize = response.size();\n\n        response.resize(startSize + size * nmemb);\n        std::memcpy(response.data() + startSize, contents, size * nmemb);\n\n        return size * nmemb;\n    }\n\n    size_t HttpRequest::writeToFile(void *contents, size_t size, size_t nmemb, void *userdata) {\n        auto &file = *static_cast<wolv::io::File*>(userdata);\n\n        file.writeBuffer(static_cast<const u8*>(contents), size * nmemb);\n\n        return size * nmemb;\n    }\n\n    std::string HttpRequest::urlEncode(const std::string &input) {\n        std::string result;\n        for (char c : input){\n            if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')\n                result += c;\n            else\n                result += fmt::format(\"%02X\", c);\n        }\n        return result;\n    }\n\n    std::string HttpRequest::urlDecode(const std::string &input) {\n        std::string result;\n\n        for (u32 i = 0; i < input.size(); i++){\n            if (input[i] != '%'){\n                if (input[i] == '+')\n                    result += ' ';\n                else\n                    result += input[i];\n            } else {\n                const auto hex = crypt::decode16(input.substr(i + 1, 2));\n                if (hex.empty())\n                    return \"\";\n\n                result += char(hex[0]);\n                i += 2;\n            }\n        }\n        return result;\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/http_requests_emscripten.cpp",
    "content": "#if defined(OS_WEB)\n\n#include <hex/helpers/http_requests.hpp>\n\nnamespace hex {\n\n    HttpRequest::HttpRequest(std::string method, std::string url) : m_method(std::move(method)), m_url(std::move(url)) {\n        emscripten_fetch_attr_init(&m_attr);\n    }\n\n    HttpRequest::HttpRequest(HttpRequest &&other) noexcept {\n        m_attr = other.m_attr;\n\n        m_method = std::move(other.m_method);\n        m_url = std::move(other.m_url);\n        m_headers = std::move(other.m_headers);\n        m_body = std::move(other.m_body);\n    }\n\n    HttpRequest& HttpRequest::operator=(HttpRequest &&other) noexcept {\n        m_attr = other.m_attr;\n\n        m_method = std::move(other.m_method);\n        m_url = std::move(other.m_url);\n        m_headers = std::move(other.m_headers);\n        m_body = std::move(other.m_body);\n\n        return *this;\n    }\n\n    HttpRequest::~HttpRequest() { }\n\n    void HttpRequest::setDefaultConfig() { }\n\n    std::future<HttpRequest::Result<std::vector<u8>>> HttpRequest::downloadFile() {\n        return std::async(std::launch::async, [this] {\n            std::vector<u8> response;\n\n            return this->executeImpl<std::vector<u8>>(response);\n        });\n    }\n\n    void HttpRequest::setProxyUrl(std::string proxy) {\n        std::ignore = proxy;\n    }\n\n    void HttpRequest::setProxyState(bool state) {\n        std::ignore = state;\n    }\n\n    void HttpRequest::checkProxyErrors() { }\n}\n\n#endif"
  },
  {
    "path": "lib/libimhex/source/helpers/http_requests_native.cpp",
    "content": "#if !defined(OS_WEB)\n\n#include <hex/helpers/http_requests.hpp>\n#include <curl/curl.h>\n\nnamespace hex {\n\n    namespace {\n\n        std::string s_proxyUrl;\n        bool s_proxyState;\n\n    }\n\n    int progressCallback(void *contents, curl_off_t dlTotal, curl_off_t dlNow, curl_off_t ulTotal, curl_off_t ulNow) {\n        auto &request = *static_cast<HttpRequest *>(contents);\n\n        if (dlTotal > 0)\n            request.setProgress(float(dlNow) / dlTotal);\n        else if (ulTotal > 0)\n            request.setProgress(float(ulNow) / ulTotal);\n        else\n            request.setProgress(0.0F);\n\n        return request.isCanceled() ? CURLE_ABORTED_BY_CALLBACK : CURLE_OK;\n    }\n\n    HttpRequest::HttpRequest(std::string method, std::string url) : m_method(std::move(method)), m_url(std::move(url)) {\n        AT_FIRST_TIME {\n            curl_global_init(CURL_GLOBAL_ALL);\n        };\n\n        AT_FINAL_CLEANUP {\n            curl_global_cleanup();\n        };\n\n        m_curl = curl_easy_init();\n    }\n\n    HttpRequest::~HttpRequest() {\n        curl_easy_cleanup(m_curl);\n    }\n\n    HttpRequest::HttpRequest(HttpRequest &&other) noexcept {\n        m_curl = other.m_curl;\n        other.m_curl = nullptr;\n\n        m_method = std::move(other.m_method);\n        m_url = std::move(other.m_url);\n        m_headers = std::move(other.m_headers);\n        m_body = std::move(other.m_body);\n    }\n\n    HttpRequest& HttpRequest::operator=(HttpRequest &&other) noexcept {\n        m_curl = other.m_curl;\n        other.m_curl = nullptr;\n\n        m_method = std::move(other.m_method);\n        m_url = std::move(other.m_url);\n        m_headers = std::move(other.m_headers);\n        m_body = std::move(other.m_body);\n        m_timeout = other.m_timeout;\n\n        return *this;\n    }\n\n    void HttpRequest::setDefaultConfig() {\n        curl_easy_setopt(m_curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);\n        curl_easy_setopt(m_curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);\n        curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L);\n        curl_easy_setopt(m_curl, CURLOPT_USERAGENT, \"ImHex/1.0\");\n        curl_easy_setopt(m_curl, CURLOPT_DEFAULT_PROTOCOL, \"https\");\n        curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 1L);\n        curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYHOST, 2L);\n        curl_easy_setopt(m_curl, CURLOPT_TIMEOUT_MS, 0L);\n        curl_easy_setopt(m_curl, CURLOPT_CONNECTTIMEOUT_MS, m_timeout);\n        curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1L);\n        curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 0L);\n        curl_easy_setopt(m_curl, CURLOPT_XFERINFODATA, this);\n        curl_easy_setopt(m_curl, CURLOPT_XFERINFOFUNCTION, progressCallback);\n\n        if (s_proxyState)\n            curl_easy_setopt(m_curl, CURLOPT_PROXY, s_proxyUrl.c_str());\n    }\n\n    std::future<HttpRequest::Result<std::vector<u8>>> HttpRequest::downloadFile() {\n        return std::async(std::launch::async, [this] {\n            std::vector<u8> response;\n\n            curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);\n            curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response);\n\n            return this->executeImpl<std::vector<u8>>(response);\n        });\n    }\n\n\n\n    void HttpRequest::setProxyUrl(std::string proxy) {\n        s_proxyUrl = std::move(proxy);\n    }\n\n    void HttpRequest::setProxyState(bool enabled) {\n        s_proxyState = enabled;\n    }\n\n    void HttpRequest::checkProxyErrors() {\n        if (s_proxyState && !s_proxyUrl.empty()){\n            log::info(\"A custom proxy '{0}' is in use. Is it working correctly?\", s_proxyUrl);\n        }\n    }\n\n    namespace impl {\n\n        void setWriteFunctions(CURL *curl, wolv::io::File &file) {\n            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HttpRequest::writeToFile);\n            curl_easy_setopt(curl, CURLOPT_WRITEDATA, &file);\n        }\n\n        void setWriteFunctions(CURL *curl, std::vector<u8> &data) {\n            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HttpRequest::writeToVector);\n            curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);\n        }\n\n        void setupFileUpload(CURL *curl, const wolv::io::File &file, const std::string &fileName, const std::string &mimeName) {\n            curl_mime *mime     = curl_mime_init(curl);\n            curl_mimepart *part = curl_mime_addpart(mime);\n\n\n            curl_mime_data_cb(part, file.getSize(),\n                [](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {\n                    auto handle = static_cast<FILE*>(arg);\n\n                    return fread(buffer, size, nitems, handle);\n                },\n                [](void *arg, curl_off_t offset, int origin) -> int {\n                    auto handle = static_cast<FILE*>(arg);\n\n                    if (fseek(handle, offset, origin) != 0)\n                        return CURL_SEEKFUNC_CANTSEEK;\n                    else\n                        return CURL_SEEKFUNC_OK;\n                },\n                [](void *arg) {\n                    auto handle = static_cast<FILE*>(arg);\n\n                    fclose(handle);\n                },\n                file.getHandle());\n            curl_mime_filename(part, fileName.c_str());\n            curl_mime_name(part, mimeName.c_str());\n\n            curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);\n        }\n\n        void setupFileUpload(CURL *curl, const std::vector<u8> &data, const std::fs::path &fileName, const std::string &mimeName) {\n            curl_mime *mime     = curl_mime_init(curl);\n            curl_mimepart *part = curl_mime_addpart(mime);\n\n            curl_mime_data(part, reinterpret_cast<const char *>(data.data()), data.size());\n            auto fileNameStr = wolv::util::toUTF8String(fileName.filename());\n            curl_mime_filename(part, fileNameStr.c_str());\n            curl_mime_name(part, mimeName.c_str());\n\n            curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);\n        }\n\n        int executeCurl(CURL *curl, const std::string &url, const std::string &method, const std::string &body, std::map<std::string, std::string> &headers) {\n            curl_easy_setopt(curl, CURLOPT_URL, url.c_str());\n            curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str());\n\n            if (!body.empty()) {\n                curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());\n            }\n\n            curl_slist *headersList = nullptr;\n            headersList = curl_slist_append(headersList, \"Cache-Control: no-cache\");\n            ON_SCOPE_EXIT { curl_slist_free_all(headersList); };\n\n            for (auto &[key, value] : headers) {\n                std::string header = fmt::format(\"{}: {}\", key, value);\n                headersList = curl_slist_append(headersList, header.c_str());\n            }\n            curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headersList);\n\n            auto result = curl_easy_perform(curl);\n            if (result != CURLE_OK){\n                return result;\n            }\n\n            return 0;\n        }\n\n        long getStatusCode(CURL *curl) {\n            long statusCode = 0;\n            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);\n\n            return statusCode;\n        }\n\n        std::string getStatusText(int result) {\n            return curl_easy_strerror(CURLcode(result));\n        }\n\n    }\n\n\n}\n\n\n#endif"
  },
  {
    "path": "lib/libimhex/source/helpers/imgui_hooks.cpp",
    "content": "#include <hex/api/events/events_gui.hpp>\n\n#include <imgui.h>\n#include <imgui_internal.h>\n\n#include <hex/api/tutorial_manager.hpp>\n\n#if !defined(IMGUI_TEST_ENGINE)\n\n    void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) {\n        hex::TutorialManager::postElementRendered(id, bb);\n    }\n\n    void ImGuiTestEngineHook_ItemInfo(ImGuiContext*, ImGuiID, const char*, ImGuiItemStatusFlags) {}\n    void ImGuiTestEngineHook_Log(ImGuiContext*, const char*, ...) {}\n    const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext*, ImGuiID) { return nullptr; }\n\n#endif"
  },
  {
    "path": "lib/libimhex/source/helpers/keys.cpp",
    "content": "#include <hex/helpers/keys.hpp>\n\n#include <GLFW/glfw3.h>\n\nenum Keys scanCodeToKey(int scanCode) {\n    switch (scanCode) {\n        case GLFW_KEY_SPACE:            return Keys::Space;\n        case GLFW_KEY_APOSTROPHE:       return Keys::Apostrophe;\n        case GLFW_KEY_COMMA:            return Keys::Comma;\n        case GLFW_KEY_MINUS:            return Keys::Minus;\n        case GLFW_KEY_PERIOD:           return Keys::Period;\n        case GLFW_KEY_SLASH:            return Keys::Slash;\n        case GLFW_KEY_0:                return Keys::Num0;\n        case GLFW_KEY_1:                return Keys::Num1;\n        case GLFW_KEY_2:                return Keys::Num2;\n        case GLFW_KEY_3:                return Keys::Num3;\n        case GLFW_KEY_4:                return Keys::Num4;\n        case GLFW_KEY_5:                return Keys::Num5;\n        case GLFW_KEY_6:                return Keys::Num6;\n        case GLFW_KEY_7:                return Keys::Num7;\n        case GLFW_KEY_8:                return Keys::Num8;\n        case GLFW_KEY_9:                return Keys::Num9;\n        case GLFW_KEY_SEMICOLON:        return Keys::Semicolon;\n        case GLFW_KEY_EQUAL:            return Keys::Equals;\n        case GLFW_KEY_A:                return Keys::A;\n        case GLFW_KEY_B:                return Keys::B;\n        case GLFW_KEY_C:                return Keys::C;\n        case GLFW_KEY_D:                return Keys::D;\n        case GLFW_KEY_E:                return Keys::E;\n        case GLFW_KEY_F:                return Keys::F;\n        case GLFW_KEY_G:                return Keys::G;\n        case GLFW_KEY_H:                return Keys::H;\n        case GLFW_KEY_I:                return Keys::I;\n        case GLFW_KEY_J:                return Keys::J;\n        case GLFW_KEY_K:                return Keys::K;\n        case GLFW_KEY_L:                return Keys::L;\n        case GLFW_KEY_M:                return Keys::M;\n        case GLFW_KEY_N:                return Keys::N;\n        case GLFW_KEY_O:                return Keys::O;\n        case GLFW_KEY_P:                return Keys::P;\n        case GLFW_KEY_Q:                return Keys::Q;\n        case GLFW_KEY_R:                return Keys::R;\n        case GLFW_KEY_S:                return Keys::S;\n        case GLFW_KEY_T:                return Keys::T;\n        case GLFW_KEY_U:                return Keys::U;\n        case GLFW_KEY_V:                return Keys::V;\n        case GLFW_KEY_W:                return Keys::W;\n        case GLFW_KEY_X:                return Keys::X;\n        case GLFW_KEY_Y:                return Keys::Y;\n        case GLFW_KEY_Z:                return Keys::Z;\n        case GLFW_KEY_LEFT_BRACKET:     return Keys::LeftBracket;\n        case GLFW_KEY_BACKSLASH:        return Keys::Backslash;\n        case GLFW_KEY_RIGHT_BRACKET:    return Keys::RightBracket;\n        case GLFW_KEY_GRAVE_ACCENT:     return Keys::GraveAccent;\n        case GLFW_KEY_WORLD_1:          return Keys::World1;\n        case GLFW_KEY_WORLD_2:          return Keys::World2;\n        case GLFW_KEY_ESCAPE:           return Keys::Escape;\n        case GLFW_KEY_ENTER:            return Keys::Enter;\n        case GLFW_KEY_TAB:              return Keys::Tab;\n        case GLFW_KEY_BACKSPACE:        return Keys::Backspace;\n        case GLFW_KEY_INSERT:           return Keys::Insert;\n        case GLFW_KEY_DELETE:           return Keys::Delete;\n        case GLFW_KEY_RIGHT:            return Keys::Right;\n        case GLFW_KEY_LEFT:             return Keys::Left;\n        case GLFW_KEY_DOWN:             return Keys::Down;\n        case GLFW_KEY_UP:               return Keys::Up;\n        case GLFW_KEY_PAGE_UP:          return Keys::PageUp;\n        case GLFW_KEY_PAGE_DOWN:        return Keys::PageDown;\n        case GLFW_KEY_HOME:             return Keys::Home;\n        case GLFW_KEY_END:              return Keys::End;\n        case GLFW_KEY_CAPS_LOCK:        return Keys::CapsLock;\n        case GLFW_KEY_SCROLL_LOCK:      return Keys::ScrollLock;\n        case GLFW_KEY_NUM_LOCK:         return Keys::NumLock;\n        case GLFW_KEY_PRINT_SCREEN:     return Keys::PrintScreen;\n        case GLFW_KEY_PAUSE:            return Keys::Pause;\n        case GLFW_KEY_F1:               return Keys::F1;\n        case GLFW_KEY_F2:               return Keys::F2;\n        case GLFW_KEY_F3:               return Keys::F3;\n        case GLFW_KEY_F4:               return Keys::F4;\n        case GLFW_KEY_F5:               return Keys::F5;\n        case GLFW_KEY_F6:               return Keys::F6;\n        case GLFW_KEY_F7:               return Keys::F7;\n        case GLFW_KEY_F8:               return Keys::F8;\n        case GLFW_KEY_F9:               return Keys::F9;\n        case GLFW_KEY_F10:              return Keys::F10;\n        case GLFW_KEY_F11:              return Keys::F11;\n        case GLFW_KEY_F12:              return Keys::F12;\n        case GLFW_KEY_F13:              return Keys::F13;\n        case GLFW_KEY_F14:              return Keys::F14;\n        case GLFW_KEY_F15:              return Keys::F15;\n        case GLFW_KEY_F16:              return Keys::F16;\n        case GLFW_KEY_F17:              return Keys::F17;\n        case GLFW_KEY_F18:              return Keys::F18;\n        case GLFW_KEY_F19:              return Keys::F19;\n        case GLFW_KEY_F20:              return Keys::F20;\n        case GLFW_KEY_F21:              return Keys::F21;\n        case GLFW_KEY_F22:              return Keys::F22;\n        case GLFW_KEY_F23:              return Keys::F23;\n        case GLFW_KEY_F24:              return Keys::F24;\n        case GLFW_KEY_F25:              return Keys::F25;\n        case GLFW_KEY_KP_0:             return Keys::KeyPad0;\n        case GLFW_KEY_KP_1:             return Keys::KeyPad1;\n        case GLFW_KEY_KP_2:             return Keys::KeyPad2;\n        case GLFW_KEY_KP_3:             return Keys::KeyPad3;\n        case GLFW_KEY_KP_4:             return Keys::KeyPad4;\n        case GLFW_KEY_KP_5:             return Keys::KeyPad5;\n        case GLFW_KEY_KP_6:             return Keys::KeyPad6;\n        case GLFW_KEY_KP_7:             return Keys::KeyPad7;\n        case GLFW_KEY_KP_8:             return Keys::KeyPad8;\n        case GLFW_KEY_KP_9:             return Keys::KeyPad9;\n        case GLFW_KEY_KP_DECIMAL:       return Keys::KeyPadDecimal;\n        case GLFW_KEY_KP_DIVIDE:        return Keys::KeyPadDivide;\n        case GLFW_KEY_KP_MULTIPLY:      return Keys::KeyPadMultiply;\n        case GLFW_KEY_KP_SUBTRACT:      return Keys::KeyPadSubtract;\n        case GLFW_KEY_KP_ADD:           return Keys::KeyPadAdd;\n        case GLFW_KEY_KP_ENTER:         return Keys::KeyPadEnter;\n        case GLFW_KEY_KP_EQUAL:         return Keys::KeyPadEqual;\n        case GLFW_KEY_MENU:             return Keys::Menu;\n        default:                        return Keys(scanCode);\n    }\n}\n\nint keyToScanCode(enum Keys key) {\n    switch (key) {\n        case Keys::Space:               return GLFW_KEY_SPACE;\n        case Keys::Apostrophe:          return GLFW_KEY_APOSTROPHE;\n        case Keys::Comma:               return GLFW_KEY_COMMA;\n        case Keys::Minus:               return GLFW_KEY_MINUS;\n        case Keys::Period:              return GLFW_KEY_PERIOD;\n        case Keys::Slash:               return GLFW_KEY_SLASH;\n        case Keys::Num0:                return GLFW_KEY_0;\n        case Keys::Num1:                return GLFW_KEY_1;\n        case Keys::Num2:                return GLFW_KEY_2;\n        case Keys::Num3:                return GLFW_KEY_3;\n        case Keys::Num4:                return GLFW_KEY_4;\n        case Keys::Num5:                return GLFW_KEY_5;\n        case Keys::Num6:                return GLFW_KEY_6;\n        case Keys::Num7:                return GLFW_KEY_7;\n        case Keys::Num8:                return GLFW_KEY_8;\n        case Keys::Num9:                return GLFW_KEY_9;\n        case Keys::Semicolon:           return GLFW_KEY_SEMICOLON;\n        case Keys::Equals:              return GLFW_KEY_EQUAL;\n        case Keys::A:                   return GLFW_KEY_A;\n        case Keys::B:                   return GLFW_KEY_B;\n        case Keys::C:                   return GLFW_KEY_C;\n        case Keys::D:                   return GLFW_KEY_D;\n        case Keys::E:                   return GLFW_KEY_E;\n        case Keys::F:                   return GLFW_KEY_F;\n        case Keys::G:                   return GLFW_KEY_G;\n        case Keys::H:                   return GLFW_KEY_H;\n        case Keys::I:                   return GLFW_KEY_I;\n        case Keys::J:                   return GLFW_KEY_J;\n        case Keys::K:                   return GLFW_KEY_K;\n        case Keys::L:                   return GLFW_KEY_L;\n        case Keys::M:                   return GLFW_KEY_M;\n        case Keys::N:                   return GLFW_KEY_N;\n        case Keys::O:                   return GLFW_KEY_O;\n        case Keys::P:                   return GLFW_KEY_P;\n        case Keys::Q:                   return GLFW_KEY_Q;\n        case Keys::R:                   return GLFW_KEY_R;\n        case Keys::S:                   return GLFW_KEY_S;\n        case Keys::T:                   return GLFW_KEY_T;\n        case Keys::U:                   return GLFW_KEY_U;\n        case Keys::V:                   return GLFW_KEY_V;\n        case Keys::W:                   return GLFW_KEY_W;\n        case Keys::X:                   return GLFW_KEY_X;\n        case Keys::Y:                   return GLFW_KEY_Y;\n        case Keys::Z:                   return GLFW_KEY_Z;\n        case Keys::LeftBracket:         return GLFW_KEY_LEFT_BRACKET;\n        case Keys::Backslash:           return GLFW_KEY_BACKSLASH;\n        case Keys::RightBracket:        return GLFW_KEY_RIGHT_BRACKET;\n        case Keys::GraveAccent:         return GLFW_KEY_GRAVE_ACCENT;\n        case Keys::World1:              return GLFW_KEY_WORLD_1;\n        case Keys::World2:              return GLFW_KEY_WORLD_2;\n        case Keys::Escape:              return GLFW_KEY_ESCAPE;\n        case Keys::Enter:               return GLFW_KEY_ENTER;\n        case Keys::Tab:                 return GLFW_KEY_TAB;\n        case Keys::Backspace:           return GLFW_KEY_BACKSPACE;\n        case Keys::Insert:              return GLFW_KEY_INSERT;\n        case Keys::Delete:              return GLFW_KEY_DELETE;\n        case Keys::Right:               return GLFW_KEY_RIGHT;\n        case Keys::Left:                return GLFW_KEY_LEFT;\n        case Keys::Down:                return GLFW_KEY_DOWN;\n        case Keys::Up:                  return GLFW_KEY_UP;\n        case Keys::PageUp:              return GLFW_KEY_PAGE_UP;\n        case Keys::PageDown:            return GLFW_KEY_PAGE_DOWN;\n        case Keys::Home:                return GLFW_KEY_HOME;\n        case Keys::End:                 return GLFW_KEY_END;\n        case Keys::CapsLock:            return GLFW_KEY_CAPS_LOCK;\n        case Keys::ScrollLock:          return GLFW_KEY_SCROLL_LOCK;\n        case Keys::NumLock:             return GLFW_KEY_NUM_LOCK;\n        case Keys::PrintScreen:         return GLFW_KEY_PRINT_SCREEN;\n        case Keys::Pause:               return GLFW_KEY_PAUSE;\n        case Keys::F1:                  return GLFW_KEY_F1;\n        case Keys::F2:                  return GLFW_KEY_F2;\n        case Keys::F3:                  return GLFW_KEY_F3;\n        case Keys::F4:                  return GLFW_KEY_F4;\n        case Keys::F5:                  return GLFW_KEY_F5;\n        case Keys::F6:                  return GLFW_KEY_F6;\n        case Keys::F7:                  return GLFW_KEY_F7;\n        case Keys::F8:                  return GLFW_KEY_F8;\n        case Keys::F9:                  return GLFW_KEY_F9;\n        case Keys::F10:                 return GLFW_KEY_F10;\n        case Keys::F11:                 return GLFW_KEY_F11;\n        case Keys::F12:                 return GLFW_KEY_F12;\n        case Keys::F13:                 return GLFW_KEY_F13;\n        case Keys::F14:                 return GLFW_KEY_F14;\n        case Keys::F15:                 return GLFW_KEY_F15;\n        case Keys::F16:                 return GLFW_KEY_F16;\n        case Keys::F17:                 return GLFW_KEY_F17;\n        case Keys::F18:                 return GLFW_KEY_F18;\n        case Keys::F19:                 return GLFW_KEY_F19;\n        case Keys::F20:                 return GLFW_KEY_F20;\n        case Keys::F21:                 return GLFW_KEY_F21;\n        case Keys::F22:                 return GLFW_KEY_F22;\n        case Keys::F23:                 return GLFW_KEY_F23;\n        case Keys::F24:                 return GLFW_KEY_F24;\n        case Keys::F25:                 return GLFW_KEY_F25;\n        case Keys::KeyPad0:             return GLFW_KEY_KP_0;\n        case Keys::KeyPad1:             return GLFW_KEY_KP_1;\n        case Keys::KeyPad2:             return GLFW_KEY_KP_2;\n        case Keys::KeyPad3:             return GLFW_KEY_KP_3;\n        case Keys::KeyPad4:             return GLFW_KEY_KP_4;\n        case Keys::KeyPad5:             return GLFW_KEY_KP_5;\n        case Keys::KeyPad6:             return GLFW_KEY_KP_6;\n        case Keys::KeyPad7:             return GLFW_KEY_KP_7;\n        case Keys::KeyPad8:             return GLFW_KEY_KP_8;\n        case Keys::KeyPad9:             return GLFW_KEY_KP_9;\n        case Keys::KeyPadDecimal:       return GLFW_KEY_KP_DECIMAL;\n        case Keys::KeyPadDivide:        return GLFW_KEY_KP_DIVIDE;\n        case Keys::KeyPadMultiply:      return GLFW_KEY_KP_MULTIPLY;\n        case Keys::KeyPadSubtract:      return GLFW_KEY_KP_SUBTRACT;\n        case Keys::KeyPadAdd:           return GLFW_KEY_KP_ADD;\n        case Keys::KeyPadEnter:         return GLFW_KEY_KP_ENTER;\n        case Keys::KeyPadEqual:         return GLFW_KEY_KP_EQUAL;\n        case Keys::Menu:                return GLFW_KEY_MENU;\n        default:                        return int(key);\n    }\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/logger.cpp",
    "content": "#include <hex/helpers/logger.hpp>\n\n#include <hex/api/task_manager.hpp>\n\n#include <hex/helpers/fs.hpp>\n#include <hex/helpers/fmt.hpp>\n#include <hex/helpers/default_paths.hpp>\n#include <hex/helpers/auto_reset.hpp>\n\n#include <wolv/io/file.hpp>\n\n#include <mutex>\n#include <chrono>\n#include <fmt/chrono.h>\n#include <hex/helpers/debugging.hpp>\n\n#if defined(OS_WINDOWS)\n    #include <Windows.h>\n#endif\n\nnamespace hex::log {\n\n    namespace {\n\n        wolv::io::File s_loggerFile;\n        bool s_colorOutputEnabled = false;\n        std::recursive_mutex s_loggerMutex;\n        bool s_loggingSuspended = false;\n        bool s_debugLoggingEnabled = false;\n\n    }\n\n    void suspendLogging() {\n        s_loggingSuspended = true;\n    }\n\n    void resumeLogging() {\n        s_loggingSuspended = false;\n    }\n\n    void enableDebugLogging() {\n        s_debugLoggingEnabled = true;\n    }\n\n    namespace impl {\n\n        void lockLoggerMutex() {\n            s_loggerMutex.lock();\n        }\n\n        void unlockLoggerMutex() {\n            s_loggerMutex.unlock();\n        }\n\n\n        bool isLoggingSuspended() {\n            return s_loggingSuspended;\n        }\n\n        bool isDebugLoggingEnabled() {\n            #if defined(DEBUG)\n                return true;\n            #else\n                return s_debugLoggingEnabled;\n            #endif\n        }\n\n        FILE *getDestination() {\n            if (s_loggerFile.isValid())\n                return s_loggerFile.getHandle();\n            else\n                return stdout;\n        }\n\n        wolv::io::File& getFile() {\n            return s_loggerFile;\n        }\n\n        bool isRedirected() {\n            return s_loggerFile.isValid();\n        }\n\n        void redirectToFile() {\n            if (s_loggerFile.isValid()) return;\n\n            for (const auto &path : paths::Logs.all()) {\n                wolv::io::fs::createDirectories(path);\n                time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());\n                s_loggerFile = wolv::io::File(path / fmt::format(\"{0:%Y%m%d_%H%M%S}.log\", *std::localtime(&time)), wolv::io::File::Mode::Create);\n                s_loggerFile.disableBuffering();\n\n                if (s_loggerFile.isValid()) {\n                    s_colorOutputEnabled = false;\n                    break;\n                }\n            }\n        }\n\n        void enableColorPrinting() {\n            s_colorOutputEnabled = true;\n\n            #if defined(OS_WINDOWS)\n                auto hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);\n                if (hConsole != INVALID_HANDLE_VALUE) {\n                    DWORD mode = 0;\n                    if (::GetConsoleMode(hConsole, &mode) == TRUE) {\n                        mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;\n                        ::SetConsoleMode(hConsole, mode);\n                    }\n                }\n            #endif\n        }\n\n        static AutoReset<std::vector<LogEntry>> s_logEntries;\n        const std::vector<LogEntry>& getLogEntries() {\n            return s_logEntries;\n        }\n\n        void addLogEntry(std::string_view project, std::string_view level, std::string message) {\n            s_logEntries->emplace_back(\n                project,\n                level,\n                std::move(message)\n            );\n        }\n\n\n        void printPrefix(FILE *dest, fmt::text_style ts, std::string_view level, std::string_view projectName) {\n            const auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());\n            const auto now = *std::localtime(&time);\n\n            auto threadName = TaskManager::getCurrentThreadName();\n            if (threadName.empty()) [[unlikely]] {\n                threadName = \"???\";\n            }\n\n            constexpr static auto MaxTagLength = 25;\n            const auto totalLength = std::min(static_cast<size_t>(MaxTagLength),\n                                              projectName.length() + (threadName.empty() ? 0 : 3 + threadName.length()));\n\n            const auto remainingSpace = MaxTagLength - projectName.length() - 3;\n\n            fmt::print(dest, \"[{0:%H:%M:%S}] {1} [{2} | {3}] {4: <{5}} \",\n                now,\n                s_colorOutputEnabled ? fmt::format(ts, \"{}\", level) : level,\n                projectName.substr(0, std::min(projectName.length(), static_cast<size_t>(MaxTagLength))),\n                threadName.substr(0, remainingSpace),\n                \"\",\n                MaxTagLength - totalLength\n            );\n        }\n\n        namespace color {\n\n            fmt::color debug() { return fmt::color::medium_sea_green; }\n            fmt::color info()  { return fmt::color::steel_blue; }\n            fmt::color warn()  { return fmt::color::orange; }\n            fmt::color error() { return fmt::color::indian_red; }\n            fmt::color fatal() { return fmt::color::medium_purple; }\n\n        }\n\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/macos_menu.m",
    "content": "#import <Foundation/Foundation.h>\n#import <Cocoa/Cocoa.h>\n#import <objc/runtime.h>\n\nstruct KeyEquivalent {\n    bool valid;\n    bool ctrl, opt, cmd, shift;\n    int key;\n};\n\nconst static int MenuBegin = 1;\nstatic NSInteger s_currTag = MenuBegin;\nstatic NSInteger s_selectedTag = -1;\n\n@interface MenuItemHandler : NSObject\n-(void) OnClick: (id) sender;\n@end\n\n@implementation MenuItemHandler\n-(void) OnClick: (id) sender {\n    NSMenuItem* menu_item = sender;\n    s_selectedTag = menu_item.tag;\n}\n@end\n\n\nstatic NSMenu* s_menuStack[1024];\nstatic int s_menuStackSize = 0;\n\nstatic MenuItemHandler* s_menuItemHandler;\n\nstatic bool s_constructingMenu = false;\nstatic bool s_resetNeeded = true;\n\nvoid macosMenuBarInit(void) {\n    s_menuStackSize = 0;\n    s_menuStack[0] = NSApp.mainMenu;\n    s_menuStackSize += 1;\n\n    s_menuItemHandler = [[MenuItemHandler alloc] init];\n}\n\nvoid macosClearMenu(void) {\n    // Remove all items except the Application menu\n    while (s_menuStack[0].itemArray.count > 2) {\n        [s_menuStack[0] removeItemAtIndex:1];\n    }\n\n    s_currTag = MenuBegin;\n}\n\nbool macosBeginMainMenuBar(void) {\n    if (s_resetNeeded) {\n        macosClearMenu();\n        s_resetNeeded = false;\n    }\n\n    return true;\n}\n\nvoid macosEndMainMenuBar(void) {\n    s_constructingMenu = false;\n}\n\n\nstatic NSMutableArray* g_RegisteredIconFontDescriptors = nil;\nvoid macosRegisterFont(const unsigned char* fontBytes, size_t fontLength) {\n    if (!fontBytes || fontLength == 0 || fontLength > 100 * 1024 * 1024) { // Max 100MB sanity check\n        NSLog(@\"Invalid font data: bytes=%p, length=%zu\", fontBytes, fontLength);\n        return;\n    }\n\n    // Initialize array on first use\n    if (!g_RegisteredIconFontDescriptors) {\n        g_RegisteredIconFontDescriptors = [[NSMutableArray alloc] init];\n    }\n\n    // Create NSData - this will copy the bytes\n    NSData *fontData = [NSData dataWithBytes:fontBytes length:fontLength];\n    if (!fontData) {\n        NSLog(@\"Failed to create NSData from font bytes\");\n        return;\n    }\n\n    CFErrorRef error = NULL;\n    CFArrayRef descriptors = CTFontManagerCreateFontDescriptorsFromData((__bridge CFDataRef)fontData);\n\n    if (descriptors && CFArrayGetCount(descriptors) > 0) {\n        // Register all descriptors from this font file\n        CTFontManagerRegisterFontDescriptors(descriptors, kCTFontManagerScopeProcess, true, NULL);\n        if (true) {\n            // Store each descriptor for later use\n            for (CFIndex i = 0; i < CFArrayGetCount(descriptors); i++) {\n                CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descriptors, i);\n                [g_RegisteredIconFontDescriptors addObject:(__bridge id)descriptor];\n\n                // Log the font name for debugging\n                CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, 16.0, NULL);\n                if (font) {\n                    CFRelease(font);\n                }\n            }\n        } else {\n            NSLog(@\"Failed to register font descriptors\");\n        }\n\n        CFRelease(descriptors);\n    } else {\n        NSLog(@\"Failed to create font descriptors from data (length: %zu)\", fontLength);\n        if (error) {\n            NSLog(@\"Error: %@\", (__bridge NSError *)error);\n            CFRelease(error);\n        }\n    }\n}\n\nstatic NSImage* imageFromIconFont(NSString* character,\n                           CGFloat size,\n                           NSColor* color) {\n\n    if (!character || [character length] == 0) {\n        return nil;\n    }\n\n    if (!g_RegisteredIconFontDescriptors || [g_RegisteredIconFontDescriptors count] == 0) {\n        NSLog(@\"No icon fonts registered\");\n        return nil;\n    }\n\n    NSFont *font = nil;\n    unichar unicode = [character characterAtIndex:0];\n\n    for (id descriptorObj in g_RegisteredIconFontDescriptors) {\n        CTFontDescriptorRef descriptor = (__bridge CTFontDescriptorRef)descriptorObj;\n        CTFontRef ctFont = CTFontCreateWithFontDescriptor(descriptor, size, NULL);\n\n        if (ctFont) {\n            CGGlyph glyph;\n            UniChar unichar = unicode;\n            if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {\n                font = (__bridge NSFont *)ctFont;\n                break;\n            }\n            CFRelease(ctFont);\n        }\n    }\n\n    if (!font) {\n        NSLog(@\"No font found with glyph for character U+%04X (string: '%@', length: %lu)\",\n              unicode, character, (unsigned long)[character length]);\n        return nil;\n    }\n\n    NSDictionary *attributes = @{\n        NSFontAttributeName: font,\n        NSForegroundColorAttributeName: color\n    };\n    NSAttributedString *attrString = [[NSAttributedString alloc]\n                                      initWithString:character\n                                      attributes:attributes];\n\n    NSSize stringSize = [attrString size];\n\n    NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(size, size)];\n\n    [image lockFocus];\n\n    NSPoint drawPoint = NSMakePoint((size - stringSize.width) / 2.0,\n                                    (size - stringSize.height) / 2.0);\n    [attrString drawAtPoint:drawPoint];\n\n    [image unlockFocus];\n    [image setTemplate:YES];\n\n    return image;\n}\n\nbool macosBeginMenu(const char* label, const char *icon, bool enabled) {\n    NSString* title = [NSString stringWithUTF8String:label];\n\n    // Search for menu item with the given name\n    NSInteger menuIndex = [s_menuStack[s_menuStackSize - 1] indexOfItemWithTitle:title];\n    if (menuIndex == -1) {\n        // Construct the content of the menu if it doesn't exist yet\n\n        s_constructingMenu = true;\n\n        NSMenu* newMenu = [[NSMenu alloc] init];\n        newMenu.autoenablesItems = false;\n        newMenu.title = title;\n\n        NSMenuItem* menuItem = [[NSMenuItem alloc] init];\n        menuItem.title = title;\n        [menuItem setSubmenu:newMenu];\n\n        // Hide menus starting with '$' (used for special menus)\n        if (label[0] == '$') {\n            [menuItem setHidden:YES];\n        }\n\n        if (icon != NULL) {\n            NSString *iconString = [NSString stringWithUTF8String:icon];\n            NSImage* iconImage = imageFromIconFont(iconString, 16.0, [NSColor blackColor]);\n            [menuItem setImage:iconImage];\n        }\n\n        // Add the new menu to the end of the list\n        menuIndex = [s_menuStack[s_menuStackSize - 1] numberOfItems];\n\n        if (s_menuStackSize == 1)\n            menuIndex -= 1;\n\n        [s_menuStack[s_menuStackSize - 1] insertItem:menuItem atIndex:menuIndex];\n    }\n\n    NSMenuItem* menuItem = [s_menuStack[s_menuStackSize - 1] itemAtIndex:menuIndex];\n    if (menuItem != NULL) {\n        menuItem.enabled = enabled;\n\n        s_menuStack[s_menuStackSize] = menuItem.submenu;\n        s_menuStackSize += 1;\n    }\n\n    return true;\n}\n\nvoid macosEndMenu(void) {\n    s_menuStack[s_menuStackSize - 1] = NULL;\n    s_menuStackSize -= 1;\n}\n\nbool macosMenuItem(const char* label, const char *icon, struct KeyEquivalent keyEquivalent, bool selected, bool enabled) {\n    NSString* title = [NSString stringWithUTF8String:label];\n\n    if (s_constructingMenu) {\n        NSMenuItem* menuItem = [[NSMenuItem alloc] init];\n        menuItem.title = title;\n        menuItem.action = @selector(OnClick:);\n        menuItem.target = s_menuItemHandler;\n\n        if (icon != NULL) {\n            NSString *iconString = [NSString stringWithUTF8String:icon];\n            NSImage* iconImage = imageFromIconFont(iconString, 16.0, [NSColor blackColor]);\n            [menuItem setImage:iconImage];\n        }\n\n        [menuItem setTag:s_currTag];\n        s_currTag += 1;\n\n        // Setup the key equivalent, aka the shortcut\n        if (keyEquivalent.valid) {\n            NSInteger flags = 0x00;\n\n            if (keyEquivalent.ctrl)\n                flags |= NSEventModifierFlagControl;\n            if (keyEquivalent.shift)\n                flags |= NSEventModifierFlagShift;\n            if (keyEquivalent.cmd)\n                flags |= NSEventModifierFlagCommand;\n            if (keyEquivalent.opt)\n                flags |= NSEventModifierFlagOption;\n\n            [menuItem setKeyEquivalentModifierMask:flags];\n            menuItem.keyEquivalent = [[NSString alloc] initWithCharacters:(const unichar *)&keyEquivalent.key length:1];\n        }\n\n        [s_menuStack[s_menuStackSize - 1] addItem:menuItem];\n    }\n\n    NSInteger menuIndex = [s_menuStack[s_menuStackSize - 1] indexOfItemWithTitle:title];\n    NSMenuItem* menuItem = NULL;\n    if (menuIndex >= 0 && menuIndex < [s_menuStack[s_menuStackSize - 1] numberOfItems]) {\n        menuItem = [s_menuStack[s_menuStackSize - 1] itemAtIndex:menuIndex];\n        if (menuItem != NULL) {\n            if (s_constructingMenu == false) {\n                if (![title isEqualToString:menuItem.title]) {\n                    s_resetNeeded = true;\n                }\n            }\n\n            menuItem.enabled = enabled;\n            menuItem.state = selected ? NSControlStateValueOn : NSControlStateValueOff;\n        }\n\n        if (enabled && menuItem != NULL) {\n            if ([menuItem tag] == s_selectedTag) {\n                s_selectedTag = -1;\n                return true;\n            }\n        }\n    } else {\n        s_resetNeeded = true;\n    }\n\n    return false;\n}\n\nbool macosMenuItemSelect(const char* label, const char *icon, struct KeyEquivalent keyEquivalent, bool* selected, bool enabled) {\n    if (macosMenuItem(label, icon, keyEquivalent, selected != NULL ? *selected : false, enabled)) {\n        if (selected != NULL)\n            *selected = !(*selected);\n\n        return true;\n    }\n    return false;\n}\n\nvoid macosSeparator(void) {\n    if (s_constructingMenu) {\n        NSMenuItem* separator = [NSMenuItem separatorItem];\n        [s_menuStack[s_menuStackSize - 1] addItem:separator];\n    }\n}\n\n@interface NSObject (DockMenuAddition)\n- (NSMenu *)imhexApplicationDockMenu:(NSApplication *)sender;\n@end\n\n@implementation NSObject (DockMenuAddition)\n\n- (NSMenu *)cloneMenu:(NSMenu *)originalMenu {\n    NSMenu *clonedMenu = [[NSMenu alloc] initWithTitle:[originalMenu title]];\n\n    for (NSMenuItem *item in [originalMenu itemArray]) {\n        NSMenuItem *clonedItem = [self cloneMenuItem:item];\n        [clonedMenu addItem:clonedItem];\n    }\n\n    return clonedMenu;\n}\n\n- (NSMenuItem *)cloneMenuItem:(NSMenuItem *)original {\n    if ([original isSeparatorItem]) {\n        return [NSMenuItem separatorItem];\n    }\n\n    // Create new item with same properties\n    NSMenuItem *clone = [[NSMenuItem alloc] initWithTitle:[original title]\n                                                   action:[original action]\n                                            keyEquivalent:[original keyEquivalent]];\n\n    // Copy other properties\n    [clone setTarget:[original target]];\n    [clone setEnabled:[original isEnabled]];\n    [clone setImage:[original image]];\n    [clone setTag:[original tag]];\n    [clone setRepresentedObject:[original representedObject]];\n    [clone setToolTip:[original toolTip]];\n    [clone setState:[original state]];\n\n    // Handle submenus recursively\n    if ([original hasSubmenu]) {\n        NSMenu *clonedSubmenu = [self cloneMenu:[original submenu]];\n        [clone setSubmenu:clonedSubmenu];\n    }\n\n    return clone;\n}\n\n- (NSMenu *)imhexApplicationDockMenu:(NSApplication *)sender {\n    NSMenu *dockMenu = [[NSMenu alloc] init];\n\n    NSInteger menuIndex = [s_menuStack[s_menuStackSize - 1] indexOfItemWithTitle:@\"$TASKBAR$\"];\n    if (menuIndex == -1) {\n        return dockMenu;\n    }\n    NSMenuItem *fileMenuItem = [s_menuStack[s_menuStackSize - 1] itemAtIndex:menuIndex];\n\n    // Get the File submenu\n    NSMenu *fileSubmenu = [fileMenuItem submenu];\n\n    if (fileSubmenu) {\n        // Clone each item from the File submenu directly into the dock menu\n        for (NSMenuItem *item in [fileSubmenu itemArray]) {\n            NSMenuItem *clonedItem = [self cloneMenuItem:item];\n            [dockMenu addItem:clonedItem];\n        }\n    }\n\n    return dockMenu;\n}\n\n@end\n\nvoid macosSetupDockMenu(void) {\n    @autoreleasepool {\n        // Get GLFW's delegate class\n        Class delegateClass = objc_getClass(\"GLFWApplicationDelegate\");\n\n        if (delegateClass != nil) {\n            // Get our custom implementation\n            Method customMethod = class_getInstanceMethod([NSObject class],\n                                                         @selector(imhexApplicationDockMenu:));\n\n            // Add the method to GLFW's delegate class\n            class_addMethod(delegateClass,\n                          @selector(applicationDockMenu:),\n                          method_getImplementation(customMethod),\n                          method_getTypeEncoding(customMethod));\n        } else {\n            NSLog(@\"ERROR: Could not find GLFWApplicationDelegate class\");\n        }\n    }\n}\n"
  },
  {
    "path": "lib/libimhex/source/helpers/magic.cpp",
    "content": "#include <hex/helpers/magic.hpp>\n\n#include <hex/helpers/utils.hpp>\n#include <hex/helpers/fs.hpp>\n#include <hex/helpers/logger.hpp>\n#include <hex/helpers/default_paths.hpp>\n\n#include <wolv/utils/guards.hpp>\n#include <wolv/utils/string.hpp>\n\n#include <hex/providers/provider.hpp>\n\n#include <filesystem>\n#include <optional>\n#include <string>\n\n#include <magic.h>\n#include <hex/api/task_manager.hpp>\n#include <hex/api/content_registry/pattern_language.hpp>\n#include <hex/helpers/binary_pattern.hpp>\n\n#if defined(_MSC_VER)\n    #include <direct.h>\n#else\n    #include <unistd.h>\n#endif\n\n#if defined(OS_WINDOWS)\n    #define MAGIC_PATH_SEPARATOR \";\"\n#else\n    #define MAGIC_PATH_SEPARATOR \":\"\n#endif\n\n\nnamespace hex::magic {\n\n    static std::optional<std::string> getMagicFiles(bool sourceFiles = false) {\n        std::string magicFiles;\n\n        std::error_code error;\n        for (const auto &dir : paths::Magic.read()) {\n            for (const auto &entry : std::fs::directory_iterator(dir, error)) {\n                auto path = std::fs::absolute(entry.path());\n\n                if (sourceFiles) {\n                    if (path.extension().empty() || entry.is_directory())\n                        magicFiles += wolv::util::toUTF8String(path) + MAGIC_PATH_SEPARATOR;\n                } else {\n                    if (path.extension() == \".mgc\")\n                        magicFiles += wolv::util::toUTF8String(path) + MAGIC_PATH_SEPARATOR;\n                }\n            }\n        }\n\n        if (!magicFiles.empty())\n            magicFiles.pop_back();\n\n        if (error)\n            return std::nullopt;\n        else\n            return magicFiles;\n    }\n\n    bool compile() {\n        magic_t ctx = magic_open(MAGIC_CHECK);\n        ON_SCOPE_EXIT { magic_close(ctx); };\n\n        auto magicFiles = getMagicFiles(true);\n\n        if (!magicFiles.has_value())\n            return false;\n\n        if (magicFiles->empty())\n            return true;\n\n        std::array<char, 1024> cwd = { };\n        if (getcwd(cwd.data(), cwd.size()) == nullptr)\n            return false;\n\n        std::optional<std::fs::path> magicFolder;\n        for (const auto &dir : paths::Magic.write()) {\n            if (std::fs::exists(dir) && fs::isPathWritable(dir)) {\n                magicFolder = dir;\n                break;\n            }\n        }\n\n        if (!magicFolder.has_value()) {\n            log::error(\"Could not find a writable magic folder\");\n            return false;\n        }\n\n        if (chdir(wolv::util::toUTF8String(*magicFolder).c_str()) != 0)\n            return false;\n\n        auto result = magic_compile(ctx, magicFiles->c_str()) == 0;\n        if (!result) {\n            log::error(\"Failed to compile magic files \\\"{}\\\": {}\", *magicFiles, magic_error(ctx));\n        }\n\n        if (chdir(cwd.data()) != 0)\n            return false;\n\n        return result;\n    }\n\n    std::string getDescription(const std::vector<u8> &data, bool firstEntryOnly) {\n        if (data.empty()) return \"\";\n\n        auto magicFiles = getMagicFiles();\n\n        if (magicFiles.has_value()) {\n            magic_t ctx = magic_open(firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE);\n            ON_SCOPE_EXIT { magic_close(ctx); };\n\n            if (magic_load(ctx, magicFiles->c_str()) == 0) {\n                if (auto description = magic_buffer(ctx, data.data(), data.size()); description != nullptr) {\n                    auto result = wolv::util::replaceStrings(description, \"\\\\012-\", \"\\n-\");\n                    if (result.ends_with(\"- data\"))\n                        result = result.substr(0, result.size() - 6);\n\n                    return result;\n                }\n            }\n        }\n\n        return \"\";\n    }\n\n    std::string getDescription(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {\n        std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);\n        provider->read(address, buffer.data(), buffer.size());\n\n        return getDescription(buffer, firstEntryOnly);\n    }\n\n    std::string getMIMEType(const std::vector<u8> &data, bool firstEntryOnly) {\n        if (data.empty()) return \"\";\n\n        auto magicFiles = getMagicFiles();\n\n        if (magicFiles.has_value()) {\n            magic_t ctx = magic_open(MAGIC_MIME_TYPE | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE));\n            ON_SCOPE_EXIT { magic_close(ctx); };\n\n            if (magic_load(ctx, magicFiles->c_str()) == 0) {\n                if (auto mimeType = magic_buffer(ctx, data.data(), data.size()); mimeType != nullptr) {\n                    auto result = wolv::util::replaceStrings(mimeType, \"\\\\012-\", \"\\n-\");\n                    if (result.ends_with(\"- application/octet-stream\"))\n                        result = result.substr(0, result.size() - 26);\n\n                    return result;\n                }\n            }\n        }\n\n        return \"\";\n    }\n\n    std::string getMIMEType(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {\n        std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);\n        provider->read(address, buffer.data(), buffer.size());\n\n        return getMIMEType(buffer, firstEntryOnly);\n    }\n\n    std::string getExtensions(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {\n        std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);\n        provider->read(address, buffer.data(), buffer.size());\n\n        return getExtensions(buffer, firstEntryOnly);\n    }\n\n    std::string getExtensions(const std::vector<u8> &data, bool firstEntryOnly) {\n        if (data.empty()) return \"\";\n\n        auto magicFiles = getMagicFiles();\n\n        if (magicFiles.has_value()) {\n            magic_t ctx = magic_open(MAGIC_EXTENSION | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE));\n            ON_SCOPE_EXIT { magic_close(ctx); };\n\n            if (magic_load(ctx, magicFiles->c_str()) == 0) {\n                if (auto extension = magic_buffer(ctx, data.data(), data.size()); extension != nullptr) {\n                    auto result = wolv::util::replaceStrings(extension, \"\\\\012-\", \"\\n-\");\n                    if (result.ends_with(\"- ???\"))\n                        result = result.substr(0, result.size() - 5);\n\n                    return result;\n                }\n            }\n        }\n\n        return \"\";\n    }\n\n    std::string getAppleCreatorType(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {\n        std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);\n        provider->read(address, buffer.data(), buffer.size());\n\n        return getAppleCreatorType(buffer, firstEntryOnly);\n    }\n\n    std::string getAppleCreatorType(const std::vector<u8> &data, bool firstEntryOnly) {\n        if (data.empty()) return \"\";\n\n        auto magicFiles = getMagicFiles();\n\n        if (magicFiles.has_value()) {\n            magic_t ctx = magic_open(MAGIC_APPLE | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE));\n            ON_SCOPE_EXIT { magic_close(ctx); };\n\n            if (magic_load(ctx, magicFiles->c_str()) == 0) {\n                if (auto result = magic_buffer(ctx, data.data(), data.size()); result != nullptr)\n                    return wolv::util::replaceStrings(result, \"\\\\012-\", \"\\n-\");\n            }\n        }\n\n        return {};\n    }\n\n    bool isValidMIMEType(const std::string &mimeType) {\n        // MIME types always contain a slash\n        if (!mimeType.contains(\"/\"))\n            return false;\n\n        // The MIME type \"application/octet-stream\" is a fallback type for arbitrary binary data.\n        // Specifying this in a pattern would make it get suggested for every single unknown binary that's being loaded.\n        // We don't want that, so we ignore it here\n        if (mimeType == \"application/octet-stream\")\n            return false;\n\n        return true;\n    }\n\n\n    std::vector<FoundPattern> findViablePatterns(prv::Provider *provider, Task* task) {\n        std::vector<FoundPattern> result;\n\n        pl::PatternLanguage runtime;\n        ContentRegistry::PatternLanguage::configureRuntime(runtime, provider);\n\n        bool foundCorrectType = false;\n\n        auto mimeType = getMIMEType(provider, 0, 4_KiB, true);\n\n        std::error_code errorCode;\n        for (const auto &dir : paths::Patterns.read()) {\n            for (auto &entry : std::fs::recursive_directory_iterator(dir, errorCode)) {\n                if (task != nullptr)\n                    task->update();\n\n                foundCorrectType = false;\n                if (!entry.is_regular_file())\n                    continue;\n\n                wolv::io::File file(entry.path(), wolv::io::File::Mode::Read);\n                if (!file.isValid())\n                    continue;\n\n                std::string author, description;\n                bool matchedMimeType = false;\n                std::optional<u64> magicOffset;\n\n\n                const auto pragmaValues = runtime.getPragmaValues(file.readString());\n                if (auto it = pragmaValues.find(\"author\"); it != pragmaValues.end())\n                    author = it->second;\n                if (auto it = pragmaValues.find(\"description\"); it != pragmaValues.end())\n                    description = it->second;\n\n                // Format: #pragma MIME type/subtype\n                for (auto [it, itEnd] = pragmaValues.equal_range(\"MIME\"); it != itEnd; ++it) {\n                    if (isValidMIMEType(it->second) && it->second == mimeType) {\n                        foundCorrectType = true;\n                        matchedMimeType = true;\n                    }\n                }\n                // Format: #pragma magic [ AA BB CC DD ] @ 0x12345678\n                for (auto [it, itEnd] = pragmaValues.equal_range(\"magic\"); it != itEnd; ++it) {\n                    const auto pattern = [value = it->second]() mutable -> std::optional<BinaryPattern> {\n                        value = wolv::util::trim(value);\n\n                        if (value.empty())\n                            return std::nullopt;\n\n                        if (!value.starts_with('['))\n                            return std::nullopt;\n\n                        value = value.substr(1);\n\n                        const auto end = value.find(']');\n                        if (end == std::string::npos)\n                            return std::nullopt;\n                        value.resize(end);\n\n                        value = wolv::util::trim(value);\n\n                        return BinaryPattern(value);\n                    }();\n\n                    const auto address = [provider, value = it->second]() mutable -> std::optional<u64> {\n                        value = wolv::util::trim(value);\n\n                        if (value.empty())\n                            return std::nullopt;\n\n                        const auto start = value.find('@');\n                        if (start == std::string::npos)\n                            return std::nullopt;\n\n                        value = value.substr(start + 1);\n                        value = wolv::util::trim(value);\n\n                        size_t end = 0;\n                        auto result = std::stoll(value, &end, 0);\n                        if (end != value.length())\n                            return std::nullopt;\n\n                        if (result < 0) {\n                            const auto size = provider->getActualSize();\n                            if (u64(-result) > size) {\n                                return std::nullopt;\n                            }\n\n                            return size + result;\n                        } else {\n                            return result;\n                        }\n                    }();\n\n                    if (address && pattern) {\n                        std::vector<u8> bytes(pattern->getSize());\n                        if (!bytes.empty()) {\n                            provider->read(*address, bytes.data(), bytes.size());\n\n                            if (pattern->matches(bytes)) {\n                                foundCorrectType = true;\n                                magicOffset = address;\n                            }\n                        }\n                    }\n                }\n\n                if (foundCorrectType) {\n                    result.emplace_back(\n                        entry.path(),\n                        std::move(author),\n                        std::move(description),\n                        matchedMimeType ? std::make_optional(mimeType) : std::nullopt,\n                        magicOffset\n                    );\n                }\n\n                runtime.reset();\n            }\n        }\n\n        return result;\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/opengl.cpp",
    "content": "#include <cstddef>\n#include \"wolv/types.hpp\"\n#include <cmath>\n#include <hex/helpers/opengl.hpp>\n#include <opengl_support.h>\n\n#include <hex/helpers/logger.hpp>\n\n#include <string_view>\n#include <vector>\n#include <span>\n#include <wolv/utils/guards.hpp>\n\n#include <numbers>\n\nnamespace hex::gl {\n\n    Matrix<float,4,4> GetOrthographicMatrix( float viewWidth, float viewHeight, float nearVal,float farVal, bool actionType)\n    {\n        int sign =1;\n        if (actionType)\n            sign=-1;\n        //float left = leftRight.x;\n        //float right = leftRight.y;\n        //float down = upDown.x;\n        //float up = upDown.y;\n        Matrix<float,4,4> result(0);\n        //result.updateElement(0,0,sign /(right-left))\n        result.updateElement(0,0,sign / viewWidth);\n        //result.updateElement(0,3,sign * (right + left)/(right - left));\n        //result.updateElement(1,1, sign /(up-down));\n        result.updateElement(1,1, sign / viewHeight);\n        //result.updateElement(1,3, sign * (up + down)/(up - down));\n\n        result.updateElement(2,2,-sign * 2/(farVal - nearVal));\n        result.updateElement(3,2,-sign * (farVal + nearVal)/(farVal - nearVal));\n\n        result.updateElement(3,3,sign);\n        return result;\n    }\n\n\n    Matrix<float,4,4> GetPerspectiveMatrix(\tfloat viewWidth, float viewHeight, float nearVal,float farVal, bool actionType)\n    {\n        int sign =1;\n        if (actionType)\n            sign=-1;\n        //float left = leftRight.x;\n        //float right = leftRight.y;\n        //float down = upDown.x;\n        //float up = upDown.y;\n        //T aspect=(right-left)/(top-bottom);\n        //T f = nearVal/top;\n        Matrix<float,4,4> result(0);\n        //   T f = 1.0 / tan(fovy / 2.0); tan(fovy / 2.0) = top / near; fovy = 2 * atan2(top,near)\n\n        //result.updateElement(0,0,sign * nearVal/(right-left));\n        //result.updateElement(1,1, sign * nearVal/(up-down));\n        result.updateElement(0,0,sign * nearVal/viewWidth);\n        result.updateElement(1,1, sign * nearVal/viewHeight);\n        result.updateElement(2,2,-sign * (farVal + nearVal)/(farVal - nearVal));\n        result.updateElement(3,2,-sign * 2*farVal*nearVal/(farVal - nearVal));\n        result.updateElement(2,3,-sign);\n\n        return result;\n    }\n\n    Shader::Shader(std::string_view vertexSource, std::string_view fragmentSource) {\n        if (vertexSource.empty() || fragmentSource.empty()) {\n            return;\n        }\n\n        auto vertexShader = glCreateShader(GL_VERTEX_SHADER);\n        this->compile(vertexShader, vertexSource);\n\n        auto fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);\n        this->compile(fragmentShader, fragmentSource);\n\n        ON_SCOPE_EXIT { glDeleteShader(vertexShader); glDeleteShader(fragmentShader); };\n\n        m_program = glCreateProgram();\n\n        glAttachShader(m_program, vertexShader);\n        glAttachShader(m_program, fragmentShader);\n        glLinkProgram(m_program);\n\n        int result = false;\n        glGetProgramiv(m_program, GL_LINK_STATUS, &result);\n        if (!result) {\n            std::vector<char> log(512);\n            glGetProgramInfoLog(m_program, log.size(), nullptr, log.data());\n            log::error(\"Failed to link shader: {}\", log.data());\n\n            glDeleteProgram(m_program);\n            m_program = 0;\n        }\n    }\n\n    Shader::~Shader() {\n        if (m_program != 0)\n            glDeleteProgram(m_program);\n    }\n\n    Shader::Shader(Shader &&other) noexcept {\n        m_program = other.m_program;\n        other.m_program = 0;\n    }\n\n    Shader& Shader::operator=(Shader &&other) noexcept {\n        if (m_program != 0)\n            glDeleteProgram(m_program);\n\n        m_program = other.m_program;\n        other.m_program = 0;\n        return *this;\n    }\n\n    void Shader::bind() const {\n        glUseProgram(m_program);\n    }\n\n    void Shader::unbind() const {\n        glUseProgram(0);\n    }\n\n    void Shader::setUniform(std::string_view name, const int &value) {\n        glUniform1i(getUniformLocation(name), value);\n    }\n\n    void Shader::setUniform(std::string_view name, const float &value) {\n        glUniform1f(getUniformLocation(name), value);\n    }\n\n    bool Shader::hasUniform(std::string_view name) {\n        return getUniformLocation(name) != -1;\n    }\n\n\n\n    GLint Shader::getUniformLocation(std::string_view name) {\n        auto nameStr = std::string(name);\n        auto uniform = m_uniforms.find(nameStr);\n        if (uniform == m_uniforms.end()) {\n            auto location = glGetUniformLocation(m_program, nameStr.data());\n            if (location == -1) {\n                log::warn(\"Uniform '{}' not found in shader\", name);\n                m_uniforms[nameStr] = -1;\n                return -1;\n            }\n\n            m_uniforms[nameStr] = location;\n            uniform = m_uniforms.find(nameStr);\n        }\n\n        return uniform->second;\n    }\n\n    void Shader::compile(GLuint shader, std::string_view source) const {\n        auto sourcePtr = source.data();\n\n        glShaderSource(shader, 1, &sourcePtr, nullptr);\n        glCompileShader(shader);\n\n        int result = false;\n        glGetShaderiv(shader, GL_COMPILE_STATUS, &result);\n        if (!result) {\n            std::vector<char> log(512);\n            glGetShaderInfoLog(shader, log.size(), nullptr, log.data());\n            log::error(\"Failed to compile shader: {}\", log.data());\n        }\n    }\n\n\n    template<typename T>\n    Buffer<T>::Buffer(BufferType type, std::span<const T> data) : m_size(data.size()), m_type(GLuint(type)) {\n        glGenBuffers(1, &m_buffer);\n        glBindBuffer(m_type, m_buffer);\n        glBufferData(m_type, data.size_bytes(), data.data(), GL_STATIC_DRAW);\n        glBindBuffer(m_type, 0);\n    }\n\n    template<typename T>\n    Buffer<T>::~Buffer() {\n        glDeleteBuffers(1, &m_buffer);\n    }\n\n    template<typename T>\n    Buffer<T>::Buffer(Buffer &&other) noexcept {\n        m_buffer = other.m_buffer;\n        m_size = other.m_size;\n        m_type = other.m_type;\n        other.m_buffer = 0;\n    }\n\n    template<typename T>\n    Buffer<T>& Buffer<T>::operator=(Buffer &&other) noexcept {\n        m_buffer = other.m_buffer;\n        m_size = other.m_size;\n        m_type = other.m_type;\n        other.m_buffer = 0;\n        return *this;\n    }\n\n    template<typename T>\n    void Buffer<T>::bind() const {\n        glBindBuffer(m_type, m_buffer);\n    }\n\n    template<typename T>\n    void Buffer<T>::unbind() const {\n        glBindBuffer(m_type, 0);\n    }\n\n    template<typename T>\n    size_t Buffer<T>::getSize() const {\n        return m_size;\n    }\n\n    template<typename T>\n    void Buffer<T>::draw(unsigned primitive) const {\n        switch (m_type) {\n            case GL_ARRAY_BUFFER:\n                glDrawArrays(primitive, 0, m_size);\n                break;\n            case GL_ELEMENT_ARRAY_BUFFER:\n                glDrawElements(primitive, m_size, impl::getType<T>(), nullptr);\n                 break;\n        }\n    }\n\n    template<typename T>\n    void Buffer<T>::update(std::span<const T> data) {\n        glBindBuffer(m_type, m_buffer);\n        glBufferSubData(m_type, 0, data.size_bytes(), data.data());\n        glBindBuffer(m_type, 0);\n    }\n\n    template class Buffer<float>;\n    template class Buffer<u32>;\n    template class Buffer<u16>;\n    template class Buffer<u8>;\n\n    VertexArray::VertexArray() {\n        glGenVertexArrays(1, &m_array);\n    }\n\n    VertexArray::~VertexArray() {\n        glDeleteVertexArrays(1, &m_array);\n    }\n\n    VertexArray::VertexArray(VertexArray &&other) noexcept {\n        m_array = other.m_array;\n        other.m_array = 0;\n    }\n\n    VertexArray& VertexArray::operator=(VertexArray &&other) noexcept {\n        m_array = other.m_array;\n        other.m_array = 0;\n        return *this;\n    }\n\n    void VertexArray::bind() const {\n        glBindVertexArray(m_array);\n    }\n\n    void VertexArray::unbind() const {\n        glBindVertexArray(0);\n    }\n\n\n    Texture::Texture(u32 width, u32 height) : m_texture(0), m_width(width), m_height(height) {\n        glGenTextures(1, &m_texture);\n        glBindTexture(GL_TEXTURE_2D, m_texture);\n\n        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);\n\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\n        glBindTexture(GL_TEXTURE_2D, 0);\n    }\n\n    Texture::~Texture() {\n        if (m_texture != 0)\n            glDeleteTextures(1, &m_texture);\n    }\n\n    Texture::Texture(Texture &&other) noexcept {\n        m_texture = other.m_texture;\n        other.m_texture = 0;\n\n        m_width = other.m_width;\n        m_height = other.m_height;\n    }\n\n    Texture& Texture::operator=(Texture &&other) noexcept {\n        m_texture = other.m_texture;\n        other.m_texture = 0;\n\n        m_width = other.m_width;\n        m_height = other.m_height;\n\n        return *this;\n    }\n\n    void Texture::bind() const {\n        glBindTexture(GL_TEXTURE_2D, m_texture);\n    }\n\n    void Texture::unbind() const {\n        glBindTexture(GL_TEXTURE_2D, 0);\n    }\n\n    GLuint Texture::getTexture() const {\n        return m_texture;\n    }\n\n    u32 Texture::getWidth() const {\n        return m_width;\n    }\n\n    u32 Texture::getHeight() const {\n        return m_height;\n    }\n\n    GLuint Texture::release() {\n        auto copy = m_texture;\n        m_texture = 0;\n\n        return copy;\n    }\n\n    FrameBuffer::FrameBuffer(u32 width, u32 height) {\n        glGenFramebuffers(1, &m_frameBuffer);\n        glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer);\n\n        glGenRenderbuffers(1, &m_renderBuffer);\n        glBindRenderbuffer(GL_RENDERBUFFER, m_renderBuffer);\n        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);\n        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_renderBuffer);\n\n        glBindRenderbuffer(GL_RENDERBUFFER, 0);\n        glBindFramebuffer(GL_FRAMEBUFFER, 0);\n    }\n\n    FrameBuffer::~FrameBuffer() {\n        glDeleteFramebuffers(1, &m_frameBuffer);\n        glDeleteRenderbuffers(1, &m_renderBuffer);\n    }\n\n    FrameBuffer::FrameBuffer(FrameBuffer &&other) noexcept {\n        m_frameBuffer = other.m_frameBuffer;\n        other.m_frameBuffer = 0;\n        m_renderBuffer = other.m_renderBuffer;\n        other.m_renderBuffer = 0;\n    }\n\n    FrameBuffer& FrameBuffer::operator=(FrameBuffer &&other) noexcept {\n        m_frameBuffer = other.m_frameBuffer;\n        other.m_frameBuffer = 0;\n        m_renderBuffer = other.m_renderBuffer;\n        other.m_renderBuffer = 0;\n        return *this;\n    }\n\n    void FrameBuffer::bind() const {\n        glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer);\n    }\n\n    void FrameBuffer::unbind() const {\n        glBindFramebuffer(GL_FRAMEBUFFER, 0);\n    }\n\n    void FrameBuffer::attachTexture(const Texture &texture) const {\n        glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer);\n        texture.bind();\n\n        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.getTexture(), 0);\n\n        glBindFramebuffer(GL_FRAMEBUFFER, 0);\n    }\n\n    AxesVectors::AxesVectors() {\n        m_vertices.resize(36);\n        m_colors.resize(48);\n        m_indices.resize(18);\n\n        // Vertices are x,y,z. Colors are RGBA. Indices are for the ends of each segment\n        // Entries with value zero are unneeded but kept to help keep track of location\n        // x-axis\n        //vertices[0]=0.0F;  vertices[1]= 0.0F   vertices[2] = 0.0F;  // shaft base\n        m_vertices[3] = 1.0F;//vertices[4]= 0.0F   vertices[5] = 0.0F;  // shaft tip\n        m_vertices[6] = 0.9F;                      m_vertices[8] = 0.05F; // arrow base\n        m_vertices[9] = 0.9F;                      m_vertices[11]=-0.05F; // arrow base\n        // y-axis\n        //vertices[12]=0.0F;  vertices[13] = 0.0F;  vertices[14]=0.0F;// shaft base\n                              m_vertices[16] = 1.0F;//vertices[17]=0.0F;// shaft tip\n        m_vertices[18] = 0.05F; m_vertices[19] = 0.9F;//vertices[20]=0.0F;// arrow base\n        m_vertices[21] =-0.05F; m_vertices[22] = 0.9F;//vertices[23]=0.0F;// arrow base\n        // z-axis\n        //vertices[24]=0.0F;   vertices[25]=0.0F  vertices[26] = 0.0F; // shaft base\n                                                  m_vertices[29] = 1.0F; // shaft tip\n        m_vertices[30] = 0.05F;                     m_vertices[32] = 0.9F; // arrow base\n        m_vertices[33] =-0.05F;                     m_vertices[35] = 0.9F; // arrow base\n        // x-axis colors\n        m_colors[0]  = 0.7F; m_colors[3]  = 1.0F;\n        m_colors[4]  = 0.7F; m_colors[7]  = 1.0F;\n        m_colors[8]  = 0.7F; m_colors[11] = 1.0F;\n        m_colors[12] = 0.7F; m_colors[15] = 1.0F;\n        // y-axis colors\n        m_colors[17] = 0.7F; m_colors[19] = 1.0F;\n        m_colors[21] = 0.7F; m_colors[23] = 1.0F;\n        m_colors[25] = 0.7F; m_colors[27] = 1.0F;\n        m_colors[29] = 0.7F; m_colors[31] = 1.0F;\n        // z-axis colors\n        m_colors[34] = 0.7F; m_colors[35] = 1.0F;\n        m_colors[38] = 0.7F; m_colors[39] = 1.0F;\n        m_colors[42] = 0.7F; m_colors[43] = 1.0F;\n        m_colors[46] = 0.7F; m_colors[47] = 1.0F;\n        // indices  for x\n        m_indices[0]  = 0;  m_indices[1]  = 1;\n        m_indices[2]  = 2;  m_indices[3]  = 1;\n        m_indices[4]  = 3;  m_indices[5]  = 1;\n        // indices for y\n        m_indices[6]  = 4;  m_indices[7]  = 5;\n        m_indices[8]  = 6;  m_indices[9]  = 5;\n        m_indices[10] = 7;  m_indices[11] = 5;\n        // indices for z\n        m_indices[12] =  8; m_indices[13] = 9;\n        m_indices[14] = 10; m_indices[15] = 9;\n        m_indices[16] = 11; m_indices[17] = 9;\n    }\n\n    AxesBuffers::AxesBuffers(const VertexArray& axesVertexArray, const AxesVectors &axesVectors) {\n        m_vertices = {};\n        m_colors = {};\n        m_indices = {};\n\n        axesVertexArray.bind();\n\n        m_vertices = gl::Buffer<float>(gl::BufferType::Vertex, axesVectors.getVertices());\n        m_colors = gl::Buffer<float>(gl::BufferType::Vertex, axesVectors.getColors());\n        m_indices = gl::Buffer<u8>(gl::BufferType::Index, axesVectors.getIndices());\n\n        axesVertexArray.addBuffer(0, m_vertices);\n        axesVertexArray.addBuffer(1, m_colors, 4);\n\n        m_vertices.unbind();\n        m_colors.unbind();\n        m_indices.unbind();\n        axesVertexArray.unbind();\n    }\n\n\n    GridVectors::GridVectors(int sliceCount) {\n        m_slices = sliceCount;\n        m_vertices.resize((m_slices + 1) * (m_slices + 1) * 3);\n        m_colors.resize((m_slices + 1) * (m_slices + 1) * 4);\n        m_indices.resize(m_slices  * m_slices  * 6 + m_slices * 2);\n        int k = 0;\n        int l = 0;\n        for (u32 j = 0; j <= m_slices; ++j) {\n            float z = 2.0F * float(j) / float(m_slices) - 1.0F;\n            for (u32 i = 0; i <= m_slices; ++i) {\n                m_vertices[k   ] = 2.0F * float(i) / float(m_slices) - 1.0F;\n                m_vertices[k + 1] = 0.0F;\n                m_vertices[k + 2] = z;\n                k += 3;\n                m_colors[l    ] = 0.5F;\n                m_colors[l + 1] = 0.5F;\n                m_colors[l + 2] = 0.5F;\n                m_colors[l + 3] = 0.3F;\n                l += 4;\n            }\n        }\n        k = 0;\n        for (u32 j = 0; j < m_slices; ++j) {\n            int row1 = j * (m_slices + 1);\n            int row2 = (j + 1) * (m_slices + 1);\n\n            for (u32 i = 0; i < m_slices; ++i) {\n                m_indices[k    ] = row1 + i;\n                m_indices[k + 1] = row1 + i + 1;\n                m_indices[k + 2] = row1 + i + 1;\n                m_indices[k + 3] = row2 + i + 1;\n                m_indices[k + 4] = row2 + i + 1;\n                m_indices[k + 5] = row2 + i;\n                k += 6;\n\n                if (i == 0) {\n                    m_indices[k    ] = row2 + i;\n                    m_indices[k + 1] = row1 + i;\n                    k += 2;\n                }\n            }\n        }\n    }\n\n    GridBuffers::GridBuffers(const VertexArray& gridVertexArray, const GridVectors &gridVectors) {\n        m_vertices = {};\n        m_colors = {};\n        m_indices = {};\n\n        gridVertexArray.bind();\n\n        m_vertices = gl::Buffer<float>(gl::BufferType::Vertex, gridVectors.getVertices());\n        m_indices  = gl::Buffer<u8>(gl::BufferType::Index, gridVectors.getIndices());\n        m_colors   = gl::Buffer<float>(gl::BufferType::Vertex, gridVectors.getColors());\n\n        gridVertexArray.addBuffer(0, m_vertices);\n        gridVertexArray.addBuffer(1, m_colors,4);\n\n        m_vertices.unbind();\n        m_colors.unbind();\n        m_indices.unbind();\n        gridVertexArray.unbind();\n    }\n\n    hex::gl::LightSourceVectors::LightSourceVectors(int res) {\n        m_resolution = res;\n        auto res_sq = m_resolution * m_resolution;\n        m_radius = 0.05f;\n        m_vertices.resize((res_sq  + 2) * 3);\n        m_normals.resize((res_sq + 2) * 3);\n        m_colors.resize((res_sq + 2) * 4);\n        m_indices.resize(res_sq * 6);\n\n\n        constexpr auto TwoPi = std::numbers::pi_v<float> * 2.0F;\n        constexpr auto HalfPi = std::numbers::pi_v<float> / 2.0F;\n        const auto dv = TwoPi / m_resolution;\n        const auto du = std::numbers::pi_v<float> / (m_resolution + 1);\n\n        m_normals[0] = 0;\n        m_normals[1] = 0;\n        m_normals[2] = 1;\n\n        m_vertices[0] = 0;\n        m_vertices[1] = 0;\n        m_vertices[2] = m_radius;\n\n        m_colors[0] = 1.0;\n        m_colors[1] = 1.0;\n        m_colors[2] = 1.0;\n        m_colors[3] = 1.0;\n\n        // Vertical: pi/2 to  -pi/2\n        for (int i = 0; i < m_resolution; i += 1) {\n            float u = HalfPi - (i + 1) * du;\n            float z  = std::sin(u);\n            float xy = std::cos(u);\n\n            // Horizontal: 0  to  2pi\n            for (int j = 0; j < m_resolution; j += 1) {\n                float v = j * dv;\n                float x = xy * std::cos(v);\n                float y = xy * std::sin(v);\n\n                i32 n = (i * m_resolution + j + 1) * 3;\n                m_normals[n] = x;\n                m_normals[n + 1] = y;\n                m_normals[n + 2] = z;\n\n                m_vertices[n] = m_radius * x;\n                m_vertices[n + 1] = m_radius * y;\n                m_vertices[n + 2] = m_radius * z;\n\n                n = (i * m_resolution + j + 1) * 4;\n                m_colors[n] = 1.0F;\n                m_colors[n + 1] = 1.0F;\n                m_colors[n + 2] = 1.0F;\n                m_colors[n + 3] = 1.0F;\n            }\n        }\n\n        i32 n = ((res_sq + 1) * 3);\n        m_normals[n    ] = 0;\n        m_normals[n + 1] = 0;\n        m_normals[n + 2] = -1;\n\n        m_vertices[n    ] = 0;\n        m_vertices[n + 1] = 0;\n        m_vertices[n + 2] = -m_radius;\n\n        n = ((res_sq + 1) * 4);\n        m_colors[n    ] = 1.0;\n        m_colors[n + 1] = 1.0;\n        m_colors[n + 2] = 1.0;\n        m_colors[n + 3] = 1.0;\n\n        // that was the easy part, indices are a bit more complicated\n        // and may need some explaining. The RxR grid slices the globe\n        // into longitudes which are the vertical slices and latitudes\n        // which are the horizontal slices. The latitudes are all full\n        // circles except for the poles, so we don't count them as part\n        // of the grid. That means that there are R+2 latitudes and R\n        // longitudes.Between consecutive latitudes we have 2*R triangles.\n        // Since we have R true latitudes there are R-1 spaces between them so\n        // between the top and the bottom we have 2*R*(R-1) triangles.\n        // the top and bottom have R triangles each, so we have a total of\n        // 2*R*(R-1) + 2*R = 2*R*R triangles. Each triangle has 3 vertices,\n        // so we have 6*R*R indices.\n\n        // The North Pole is index 0 and the South Pole is index 6*res*res -1\n        // The first row of vertices is 1 to res, the second row is res+1 to 2*res etc.\n\n        // First, the North Pole\n        for (int i = 0; i < m_resolution; i += 1) {\n            m_indices[i * 3] = 0;\n            m_indices[i * 3 + 1] = i + 1;\n            if (i == m_resolution - 1)\n                m_indices[i * 3 + 2] = 1;\n            else\n                m_indices[i * 3 + 2] = (i + 2);\n        }\n        // Now the spaces between true latitudes\n        for (int i = 0; i < m_resolution - 1; i += 1) {\n            // k is the index of the first vertex of the i-th latitude\n            i32 k = i * m_resolution + 1;\n            // When we go a full circle we need to connect the last vertex to the first, so\n            // we do R-1 first because their indices can be computed easily\n            for (int j = 0; j < m_resolution - 1; j += 1) {\n                // We store the indices of the array where the vertices were store\n                // in the triplets that make the triangles. These triplets are stored in\n                // an array that has indices itself which can be confusing.\n                // l keeps track of the indices of the array that stores the triplets\n                // each i brings 6R and each j 6. 3R from the North Pole.\n                i32 l = (i * m_resolution + j) * 6 + 3 * m_resolution;\n\n                m_indices[l    ] = k + j;\n                m_indices[l + 1] = k + j + m_resolution + 1;\n                m_indices[l + 2] = k + j + 1;\n\n                m_indices[l + 3] = k + j;\n                m_indices[l + 4] = k + j + m_resolution;\n                m_indices[l + 5] = k + j + m_resolution + 1;\n            }\n            // Now the last vertex of the i-th latitude is connected to the first\n            i32 l = (( i + 1) * m_resolution - 1) * 6 + 3 * m_resolution;\n\n            m_indices[l    ] = k + m_resolution  - 1;\n            m_indices[l + 1] = k + m_resolution;\n            m_indices[l + 2] = k;\n\n            m_indices[l + 3] = k + m_resolution - 1;\n            m_indices[l + 4] = k + 2 * m_resolution - 1;\n            m_indices[l + 5] = k + m_resolution;\n\n        }\n        // Now the South Pole\n        i32 k = (m_resolution-1) * m_resolution + 1;\n        i32 l = 3 * m_resolution * ( 2 * m_resolution - 1);\n        for (int i = 0; i < m_resolution; i += 1) {\n            if (i == m_resolution -1)\n                m_indices[l + i * 3] = k;\n            else\n                m_indices[l + i * 3] = k + i + 1;\n\n            m_indices[l + i * 3 + 1] = k + i;\n            m_indices[l + i * 3 + 2] = k + m_resolution;\n        }\n    }\n\n    void LightSourceVectors::moveTo(const Vector<float, 3> &position) {\n        auto vertexCount = m_vertices.size();\n\n        for (unsigned k = 0; k < vertexCount; k += 3) {\n            m_vertices[k    ] = m_radius * m_normals[k    ] + position[0];\n            m_vertices[k + 1] = m_radius * m_normals[k + 1] + position[1];\n            m_vertices[k + 2] = m_radius * m_normals[k + 2] + position[2];\n        }\n    }\n\n    LightSourceBuffers::LightSourceBuffers(const VertexArray &sourceVertexArray, const LightSourceVectors &sourceVectors) {\n        sourceVertexArray.bind();\n\n        m_vertices = gl::Buffer<float>(gl::BufferType::Vertex, sourceVectors.getVertices());\n        m_indices  = gl::Buffer<u16>(gl::BufferType::Index, sourceVectors.getIndices());\n        m_normals  = gl::Buffer<float>(gl::BufferType::Vertex, sourceVectors.getNormals());\n        m_colors   = gl::Buffer<float>(gl::BufferType::Vertex, sourceVectors.getColors());\n\n        sourceVertexArray.addBuffer(0, m_vertices);\n        sourceVertexArray.addBuffer(1, m_normals);\n        sourceVertexArray.addBuffer(2, m_colors, 4);\n\n        m_vertices.unbind();\n        m_normals.unbind();\n        m_colors.unbind();\n        m_indices.unbind();\n        sourceVertexArray.unbind();\n    }\n\n    void LightSourceBuffers::moveVertices(const VertexArray& sourceVertexArray, const LightSourceVectors& sourceVectors) {\n        sourceVertexArray.bind();\n\n        m_vertices.update(sourceVectors.getVertices());\n        sourceVertexArray.addBuffer(0, m_vertices);\n\n        sourceVertexArray.unbind();\n    }\n\n    void LightSourceBuffers::updateColors(const VertexArray& sourceVertexArray, const LightSourceVectors& sourceVectors) {\n        sourceVertexArray.bind();\n\n        m_colors.update(sourceVectors.getColors());\n        sourceVertexArray.addBuffer(2, m_colors, 4);\n\n        sourceVertexArray.unbind();\n    }\n\n}\n\n\n\n"
  },
  {
    "path": "lib/libimhex/source/helpers/patches.cpp",
    "content": "#include <algorithm>\n#include <hex/helpers/patches.hpp>\n\n#include <hex/helpers/utils.hpp>\n\n#include <hex/providers/provider.hpp>\n\n#include <cstring>\n\n\nnamespace hex {\n\n    namespace {\n\n        class PatchesGenerator : public hex::prv::Provider {\n        public:\n            explicit PatchesGenerator() = default;\n            ~PatchesGenerator() override = default;\n\n            [[nodiscard]] bool isAvailable() const override { return true; }\n            [[nodiscard]] bool isReadable()  const override { return true; }\n            [[nodiscard]] bool isWritable()  const override { return true; }\n            [[nodiscard]] bool isResizable() const override { return true; }\n            [[nodiscard]] bool isSavable()   const override { return false; }\n            [[nodiscard]] bool isSavableAsRecent() const override { return false; }\n\n            [[nodiscard]] OpenResult open() override { return {}; }\n            void close() override { }\n\n            void readRaw(u64 offset, void *buffer, size_t size) override {\n                std::ignore = offset;\n                std::ignore = buffer;\n                std::ignore = size;\n            }\n\n            void writeRaw(u64 offset, const void *buffer, size_t size) override {\n                for (u64 i = 0; i < size; i += 1)\n                    m_patches[offset] = static_cast<const u8*>(buffer)[i];\n            }\n\n            [[nodiscard]] u64 getActualSize() const override {\n                if (m_patches.empty())\n                    return 0;\n                else\n                    return m_patches.rbegin()->first;\n            }\n\n            void insertRaw(u64 offset, u64 size) override {\n                std::vector<std::pair<u64, u8>> patchesToMove;\n\n                for (auto &[address, value] : m_patches) {\n                    if (address > offset)\n                        patchesToMove.emplace_back(address, value);\n                }\n\n                for (const auto &[address, value] : patchesToMove)\n                    m_patches.erase(address);\n                for (const auto &[address, value] : patchesToMove)\n                    m_patches.insert({ address + size, value });\n            }\n\n            void removeRaw(u64 offset, u64 size) override {\n                std::vector<std::pair<u64, u8>> patchesToMove;\n\n                for (auto &[address, value] : m_patches) {\n                    if (address > offset)\n                        patchesToMove.emplace_back(address, value);\n                }\n\n                for (const auto &[address, value] : patchesToMove)\n                    m_patches.erase(address);\n                for (const auto &[address, value] : patchesToMove)\n                    m_patches.insert({ address - size, value });\n            }\n\n            [[nodiscard]] std::string getName() const override {\n                return \"\";\n            }\n\n            [[nodiscard]] const char* getIcon() const override {\n                return \"\";\n            }\n\n            [[nodiscard]] UnlocalizedString getTypeName() const override { return \"\"; }\n\n            [[nodiscard]] const std::map<u64, u8>& getPatches() const {\n                return m_patches;\n            }\n        private:\n            std::map<u64, u8> m_patches;\n        };\n\n\n        void pushStringBack(std::vector<u8> &buffer, const std::string &string) {\n            std::ranges::copy(string, std::back_inserter(buffer));\n        }\n\n        template<typename T>\n        void pushBytesBack(std::vector<u8> &buffer, T bytes) {\n            buffer.resize(buffer.size() + sizeof(T));\n            std::memcpy((&buffer.back() - sizeof(T)) + 1, &bytes, sizeof(T));\n        }\n\n    }\n\n\n\n    wolv::util::Expected<std::vector<u8>, IPSError> Patches::toIPSPatch() const {\n        std::vector<u8> result;\n\n        pushStringBack(result, \"PATCH\");\n\n        std::vector<u64> addresses;\n        std::vector<u8> values;\n\n        for (const auto &[address, value] : m_patches) {\n            addresses.push_back(address);\n            values.push_back(value);\n        }\n\n        std::optional<u64> startAddress;\n        std::vector<u8> bytes;\n        for (u32 i = 0; i < addresses.size(); i++) {\n            if (!startAddress.has_value())\n                startAddress = addresses[i];\n\n            if (i != addresses.size() - 1 && addresses[i] == (addresses[i + 1] - 1)) {\n                bytes.push_back(values[i]);\n            } else {\n                bytes.push_back(values[i]);\n\n                if (bytes.size() > 0xFFFF)\n                    return wolv::util::Unexpected(IPSError::PatchTooLarge);\n                if (startAddress > 0xFFFF'FFFF)\n                    return wolv::util::Unexpected(IPSError::AddressOutOfRange);\n\n                u32 address       = startAddress.value();\n                auto addressBytes = reinterpret_cast<u8 *>(&address);\n\n                result.push_back(addressBytes[2]);\n                result.push_back(addressBytes[1]);\n                result.push_back(addressBytes[0]);\n                pushBytesBack<u16>(result, changeEndianness<u16>(bytes.size(), std::endian::big));\n\n                for (auto byte : bytes)\n                    result.push_back(byte);\n\n                bytes.clear();\n                startAddress = {};\n            }\n        }\n\n        pushStringBack(result, \"EOF\");\n\n        return result;\n    }\n\n    wolv::util::Expected<std::vector<u8>, IPSError> Patches::toIPS32Patch() const {\n        std::vector<u8> result;\n\n        pushStringBack(result, \"IPS32\");\n\n        std::vector<u64> addresses;\n        std::vector<u8> values;\n\n        for (const auto &[address, value] : m_patches) {\n            addresses.push_back(address);\n            values.push_back(value);\n        }\n\n        std::optional<u64> startAddress;\n        std::vector<u8> bytes;\n        for (u32 i = 0; i < addresses.size(); i++) {\n            if (!startAddress.has_value())\n                startAddress = addresses[i];\n\n            if (i != addresses.size() - 1 && addresses[i] == (addresses[i + 1] - 1)) {\n                bytes.push_back(values[i]);\n            } else {\n                bytes.push_back(values[i]);\n\n                if (bytes.size() > 0xFFFF)\n                    return wolv::util::Unexpected(IPSError::PatchTooLarge);\n                if (startAddress > 0xFFFF'FFFF)\n                    return wolv::util::Unexpected(IPSError::AddressOutOfRange);\n\n                u32 address       = startAddress.value();\n                auto addressBytes = reinterpret_cast<u8 *>(&address);\n\n                result.push_back(addressBytes[3]);\n                result.push_back(addressBytes[2]);\n                result.push_back(addressBytes[1]);\n                result.push_back(addressBytes[0]);\n                pushBytesBack<u16>(result, changeEndianness<u16>(bytes.size(), std::endian::big));\n\n                for (auto byte : bytes)\n                    result.push_back(byte);\n\n                bytes.clear();\n                startAddress = {};\n            }\n        }\n\n        pushStringBack(result, \"EEOF\");\n\n        return result;\n    }\n\n    wolv::util::Expected<Patches, IPSError> Patches::fromProvider(hex::prv::Provider* provider) {\n        PatchesGenerator generator;\n\n        generator.getUndoStack().apply(provider->getUndoStack());\n\n        if (generator.getActualSize() > 0xFFFF'FFFF)\n            return wolv::util::Unexpected(IPSError::PatchTooLarge);\n\n        auto patches = generator.getPatches();\n\n        return Patches(std::move(patches));\n    }\n\n\n    wolv::util::Expected<Patches, IPSError> Patches::fromIPSPatch(const std::vector<u8> &ipsPatch) {\n        if (ipsPatch.size() < (5 + 3))\n            return wolv::util::Unexpected(IPSError::InvalidPatchHeader);\n\n        const char *header = \"PATCH\";\n        if (std::memcmp(ipsPatch.data(), header, 5) != 0)\n            return wolv::util::Unexpected(IPSError::InvalidPatchHeader);\n\n        Patches result;\n        bool foundEOF = false;\n\n        u32 ipsOffset = 5;\n        while (ipsOffset < ipsPatch.size() - (5 + 3)) {\n            u32 offset = ipsPatch[ipsOffset + 2] | (ipsPatch[ipsOffset + 1] << 8) | (ipsPatch[ipsOffset + 0] << 16);\n            u16 size   = ipsPatch[ipsOffset + 4] | (ipsPatch[ipsOffset + 3] << 8);\n\n            ipsOffset += 5;\n\n            // Handle normal record\n            if (size > 0x0000) {\n                if (ipsOffset + size > ipsPatch.size() - 3)\n                    return wolv::util::Unexpected(IPSError::InvalidPatchFormat);\n\n                for (u16 i = 0; i < size; i++)\n                    result.get()[offset + i] = ipsPatch[ipsOffset + i];\n                ipsOffset += size;\n            }\n            // Handle RLE record\n            else {\n                if (ipsOffset + 3 > ipsPatch.size() - 3)\n                    return wolv::util::Unexpected(IPSError::InvalidPatchFormat);\n\n                u16 rleSize = ipsPatch[ipsOffset + 0] | (ipsPatch[ipsOffset + 1] << 8);\n\n                ipsOffset += 2;\n\n                for (u16 i = 0; i < rleSize; i++)\n                    result.get()[offset + i] = ipsPatch[ipsOffset + 0];\n\n                ipsOffset += 1;\n            }\n\n            const char *footer = \"EOF\";\n            if (std::memcmp(ipsPatch.data() + ipsOffset, footer, 3) == 0)\n                foundEOF = true;\n        }\n\n        if (foundEOF)\n            return result;\n        else\n            return wolv::util::Unexpected(IPSError::MissingEOF);\n    }\n\n    wolv::util::Expected<Patches, IPSError> Patches::fromIPS32Patch(const std::vector<u8> &ipsPatch) {\n        if (ipsPatch.size() < (5 + 4))\n            return wolv::util::Unexpected(IPSError::InvalidPatchHeader);\n\n        const char *header = \"IPS32\";\n        if (std::memcmp(ipsPatch.data(), header, 5) != 0)\n            return wolv::util::Unexpected(IPSError::InvalidPatchHeader);\n\n        Patches result;\n        bool foundEEOF = false;\n\n        u32 ipsOffset = 5;\n        while (ipsOffset < ipsPatch.size() - (5 + 4)) {\n            u32 offset = ipsPatch[ipsOffset + 3] | (ipsPatch[ipsOffset + 2] << 8) | (ipsPatch[ipsOffset + 1] << 16) | (ipsPatch[ipsOffset + 0] << 24);\n            u16 size   = ipsPatch[ipsOffset + 5] | (ipsPatch[ipsOffset + 4] << 8);\n\n            ipsOffset += 6;\n\n            // Handle normal record\n            if (size > 0x0000) {\n                if (ipsOffset + size > ipsPatch.size() - 3)\n                    return wolv::util::Unexpected(IPSError::InvalidPatchFormat);\n\n                for (u16 i = 0; i < size; i++)\n                    result.get()[offset + i] = ipsPatch[ipsOffset + i];\n                ipsOffset += size;\n            }\n            // Handle RLE record\n            else {\n                if (ipsOffset + 3 > ipsPatch.size() - 3)\n                    return wolv::util::Unexpected(IPSError::InvalidPatchFormat);\n\n                u16 rleSize = ipsPatch[ipsOffset + 0] | (ipsPatch[ipsOffset + 1] << 8);\n\n                ipsOffset += 2;\n\n                for (u16 i = 0; i < rleSize; i++)\n                    result.get()[offset + i] = ipsPatch[ipsOffset + 0];\n\n                ipsOffset += 1;\n            }\n\n            const char *footer = \"EEOF\";\n            if (std::memcmp(ipsPatch.data() + ipsOffset, footer, 4) == 0)\n                foundEEOF = true;\n        }\n\n        if (foundEEOF)\n            return result;\n        else\n            return wolv::util::Unexpected(IPSError::MissingEOF);\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/scaling.cpp",
    "content": "#include <hex/helpers/scaling.hpp>\n\n#include <hex/api/imhex_api/system.hpp>\n\nnamespace hex {\n\n    float operator\"\"_scaled(long double value) {\n        return value * ImHexApi::System::getGlobalScale();\n    }\n\n    float operator\"\"_scaled(unsigned long long value) {\n        return value * ImHexApi::System::getGlobalScale();\n    }\n\n    ImVec2 scaled(const ImVec2 &vector) {\n        return vector * ImHexApi::System::getGlobalScale();\n    }\n\n    ImVec2 scaled(float x, float y) {\n        return ImVec2(x, y) * ImHexApi::System::getGlobalScale();\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/semantic_version.cpp",
    "content": "#include <hex/helpers/fmt.hpp>\n#include <hex/helpers/semantic_version.hpp>\n#include <wolv/utils/string.hpp>\n\nnamespace hex {\n\n    SemanticVersion::SemanticVersion(u32 major, u32 minor, u32 patch) : SemanticVersion(fmt::format(\"{}.{}.{}\", major, minor, patch)) {\n\n    }\n\n    SemanticVersion::SemanticVersion(const char *version) : SemanticVersion(std::string(version)) {\n\n    }\n\n    SemanticVersion::SemanticVersion(std::string_view version) : SemanticVersion(std::string(version.begin(), version.end())) {\n\n    }\n\n    SemanticVersion::SemanticVersion(std::string version) {\n        if (version.empty())\n            return;\n\n        if (version.starts_with(\"v\"))\n            version = version.substr(1);\n\n        m_parts = wolv::util::splitString(version, \".\");\n\n        if (m_parts.size() != 3 && m_parts.size() != 4) {\n            m_parts.clear();\n            return;\n        }\n\n        if (m_parts.back().contains(\"-\")) {\n            auto buildTypeParts = wolv::util::splitString(m_parts.back(), \"-\");\n            if (buildTypeParts.size() != 2) {\n                m_parts.clear();\n                return;\n            }\n\n            m_parts.back() = buildTypeParts[0];\n            m_buildType = buildTypeParts[1];\n        }\n    }\n\n    u32 SemanticVersion::major() const {\n        if (!isValid()) return 0;\n\n        try {\n            return std::stoul(m_parts[0]);\n        } catch (...) {\n            return 0;\n        }\n    }\n\n    u32 SemanticVersion::minor() const {\n        if (!isValid()) return 0;\n\n        try {\n            return std::stoul(m_parts[1]);\n        } catch (...) {\n            return 0;\n        }\n    }\n\n    u32 SemanticVersion::patch() const {\n        if (!isValid()) return 0;\n\n        try {\n            return std::stoul(m_parts[2]);\n        } catch (...) {\n            return 0;\n        }\n    }\n\n    bool SemanticVersion::nightly() const {\n        if (!isValid()) return false;\n\n        return m_parts.size() == 4 && m_parts[3] == \"WIP\";\n    }\n\n    const std::string& SemanticVersion::buildType() const {\n        return m_buildType;\n    }\n\n\n    bool SemanticVersion::isValid() const {\n        return !m_parts.empty();\n    }\n\n    bool SemanticVersion::operator==(const SemanticVersion& other) const {\n        return this->m_parts == other.m_parts;\n    }\n\n    std::strong_ordering SemanticVersion::operator<=>(const SemanticVersion &other) const {\n        if (const auto result = major() <=> other.major(); result != std::strong_ordering::equal)\n            return result;\n        if (auto result = minor() <=> other.minor(); result != std::strong_ordering::equal)\n            return result;\n        if (auto result = patch() <=> other.patch(); result != std::strong_ordering::equal)\n            return result;\n\n        // nightly builds are considered \"greater\" than release builds\n        if (nightly() != other.nightly())\n            return nightly() ? std::strong_ordering::greater\n                             : std::strong_ordering::less;\n\n        return std::strong_ordering::equal;\n    }\n\n    std::string SemanticVersion::get(bool withBuildType) const {\n        if (!isValid()) return \"\";\n\n        auto result = wolv::util::combineStrings(m_parts, \".\");\n\n        if (withBuildType && !m_buildType.empty())\n            result += fmt::format(\"-{}\", m_buildType);\n\n        return result;\n    }\n\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/helpers/tar.cpp",
    "content": "#include <hex/helpers/tar.hpp>\n#include <hex/helpers/literals.hpp>\n#include <hex/helpers/logger.hpp>\n#include <hex/helpers/fmt.hpp>\n\n#include <wolv/io/file.hpp>\n\n#include <microtar.h>\n\n#include \"wolv/utils/string.hpp\"\n\nnamespace hex {\n\n    using namespace hex::literals;\n\n    Tar::Tar(const std::fs::path &path, Mode mode) {\n        int tarError = MTAR_ESUCCESS;\n\n        // Explicitly create file so a short path gets generated\n        if (mode == Mode::Create) {\n            wolv::io::File file(path, wolv::io::File::Mode::Create);\n            file.flush();\n        }\n\n        m_ctx = std::make_unique<mtar_t>();\n\n        auto shortPath = wolv::io::fs::toNormalizedPathString(wolv::io::fs::toShortPath(path));\n        if (mode == Tar::Mode::Read)\n            tarError = mtar_open(m_ctx.get(), shortPath.c_str(), \"r\");\n        else if (mode == Tar::Mode::Write)\n            tarError = mtar_open(m_ctx.get(), shortPath.c_str(), \"a\");\n        else if (mode == Tar::Mode::Create)\n            tarError = mtar_open(m_ctx.get(), shortPath.c_str(), \"w\");\n        else\n            tarError = MTAR_EFAILURE;\n\n        m_path = path;\n        m_valid = (tarError == MTAR_ESUCCESS);\n\n        if (!m_valid) {\n            m_tarOpenErrno = tarError;\n            \n            // Hopefully this errno corresponds to the file open call in mtar_open\n            m_fileOpenErrno = errno;\n        }\n    }\n\n    Tar::~Tar() {\n        this->close();\n    }\n\n    Tar::Tar(hex::Tar &&other) noexcept {\n        m_ctx = std::move(other.m_ctx);\n        m_path = other.m_path;\n        m_valid = other.m_valid;\n        m_tarOpenErrno = other.m_tarOpenErrno;\n        m_fileOpenErrno = other.m_fileOpenErrno;\n\n        other.m_ctx = { };\n        other.m_valid = false;\n    }\n\n    Tar &Tar::operator=(Tar &&other) noexcept {\n        m_ctx  = std::move(other.m_ctx);\n        m_path = std::move(other.m_path);\n\n        m_valid = other.m_valid;\n        other.m_valid = false;\n\n        m_tarOpenErrno = other.m_tarOpenErrno;\n        m_fileOpenErrno = other.m_fileOpenErrno;\n        return *this;\n    }\n\n    std::vector<std::fs::path> Tar::listEntries(const std::fs::path &basePath) const {\n        std::vector<std::fs::path> result;\n\n        const std::string PaxHeaderName = \"@PaxHeader\";\n        mtar_header_t header;\n        while (mtar_read_header(m_ctx.get(), &header) != MTAR_ENULLRECORD) {\n            std::fs::path path = header.name;\n            if (header.name != PaxHeaderName && wolv::io::fs::isSubPath(basePath, path)) {\n                result.emplace_back(header.name);\n            }\n\n            mtar_next(m_ctx.get());\n        }\n\n        return result;\n    }\n\n    bool Tar::contains(const std::fs::path &path) const {\n        mtar_header_t header;\n\n        const auto fixedPath = wolv::io::fs::toNormalizedPathString(path);\n        return mtar_find(m_ctx.get(), fixedPath.c_str(), &header) == MTAR_ESUCCESS;\n    }\n\n    std::string Tar::getOpenErrorString() const {\n        return fmt::format(\"{}: {}\", mtar_strerror(m_tarOpenErrno), std::strerror(m_fileOpenErrno));\n    }\n\n    void Tar::close() {\n        if (m_valid) {\n            mtar_finalize(m_ctx.get());\n            mtar_close(m_ctx.get());\n        }\n\n        m_ctx.reset();\n        m_valid = false;\n    }\n\n    std::vector<u8> Tar::readVector(const std::fs::path &path) const {\n        mtar_header_t header;\n\n        const auto fixedPath = wolv::io::fs::toNormalizedPathString(path);\n        int ret = mtar_find(m_ctx.get(), fixedPath.c_str(), &header);\n        if (ret != MTAR_ESUCCESS){\n            log::debug(\"Failed to read vector from path {} in tarred file {}: {}\",\n                path.string(), m_path.string(), mtar_strerror(ret));\n            return {};\n        }\n        \n        std::vector<u8> result(header.size, 0x00);\n        mtar_read_data(m_ctx.get(), result.data(), result.size());\n\n        return result;\n    }\n\n    std::string Tar::readString(const std::fs::path &path) const {\n        auto result = this->readVector(path);\n        return { result.begin(), result.end() };\n    }\n\n    void Tar::writeVector(const std::fs::path &path, const std::vector<u8> &data) const {\n        if (path.has_parent_path()) {\n            std::fs::path pathPart;\n            for (const auto &part : path.parent_path()) {\n                pathPart /= part;\n\n                auto fixedPath = wolv::io::fs::toNormalizedPathString(pathPart);\n                mtar_write_dir_header(m_ctx.get(), fixedPath.c_str());\n            }\n        }\n\n        const auto fixedPath = wolv::io::fs::toNormalizedPathString(path);\n        mtar_write_file_header(m_ctx.get(), fixedPath.c_str(), data.size());\n        mtar_write_data(m_ctx.get(), data.data(), data.size());\n    }\n\n    void Tar::writeString(const std::fs::path &path, const std::string &data) const {\n        this->writeVector(path, { data.begin(), data.end() });\n    }\n\n    static void writeFile(mtar_t *ctx, const mtar_header_t *header, const std::fs::path &path) {\n        constexpr static u64 BufferSize = 1_MiB;\n\n        wolv::io::File outputFile(path, wolv::io::File::Mode::Create);\n\n        std::vector<u8> buffer;\n        for (u64 offset = 0; offset < header->size; offset += BufferSize) {\n            buffer.resize(std::min<u64>(BufferSize, header->size - offset));\n\n            mtar_read_data(ctx, buffer.data(), buffer.size());\n            outputFile.writeVector(buffer);\n        }\n    }\n\n    void Tar::extract(const std::fs::path &path, const std::fs::path &outputPath) const {\n        mtar_header_t header;\n        mtar_find(m_ctx.get(), path.string().c_str(), &header);\n\n        writeFile(m_ctx.get(), &header, outputPath);\n    }\n\n    void Tar::extractAll(const std::fs::path &outputPath) const {\n        mtar_header_t header;\n        while (mtar_read_header(m_ctx.get(), &header) != MTAR_ENULLRECORD) {\n            const auto filePath = std::fs::absolute(outputPath / std::fs::path(header.name));\n\n            if (filePath.filename() != \"@PaxHeader\") {\n\n                std::fs::create_directories(filePath.parent_path());\n\n                writeFile(m_ctx.get(), &header, filePath);\n            }\n\n            mtar_next(m_ctx.get());\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/helpers/udp_server.cpp",
    "content": "#include <hex/helpers/udp_server.hpp>\n\n#if defined(OS_WINDOWS)\n    #include <winsock2.h>\n    #include <ws2tcpip.h>\n    using socklen_t = int;\n#else\n    #include <arpa/inet.h>\n    #include <unistd.h>\n    #include <sys/socket.h>\n    #include <netinet/in.h>\n#endif\n\nnamespace hex {\n\n    UDPServer::UDPServer(u16 port, Callback callback)\n        : m_port(port), m_callback(std::move(callback)), m_running(false) {\n    }\n\n    UDPServer::~UDPServer() {\n        stop();\n    }\n\n    void UDPServer::start() {\n        m_running = true;\n        m_thread = std::thread(&UDPServer::run, this);\n    }\n\n    void UDPServer::stop() {\n        m_running = false;\n\n        if (m_socketFd >= 0) {\n            #if defined(OS_WINDOWS)\n                ::closesocket(m_socketFd);\n            #else\n                ::close(m_socketFd);\n            #endif\n        }\n\n        if (m_thread.joinable()) m_thread.join();\n    }\n\n    void UDPServer::run() {\n        m_socketFd = ::socket(AF_INET, SOCK_DGRAM, 0);\n        if (m_socketFd < 0) {\n            return;\n        }\n\n        sockaddr_in addr = { };\n        addr.sin_family = AF_INET;\n        addr.sin_addr.s_addr = INADDR_ANY;\n        addr.sin_port = htons(m_port);\n\n        if (bind(m_socketFd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {\n            return;\n        }\n\n        std::vector<u8> buffer(64 * 1024, 0x00);\n        while (m_running) {\n            sockaddr_in client{};\n            socklen_t len = sizeof(client);\n            const auto bytes = ::recvfrom(m_socketFd, reinterpret_cast<char*>(buffer.data()), buffer.size(), 0, reinterpret_cast<sockaddr*>(&client), &len);\n\n            if (bytes > 0) {\n                buffer[bytes] = '\\0';\n                m_callback({ buffer.data(), buffer.data() + bytes });\n            }\n        }\n    }\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/utils.cpp",
    "content": "#include <algorithm>\n#include <cwchar>\n#include <hex/helpers/utils.hpp>\n\n#include <hex/api/imhex_api/system.hpp>\n\n#include <hex/helpers/fmt.hpp>\n#include <hex/helpers/crypto.hpp>\n\n#include <hex/providers/buffered_reader.hpp>\n\n#include <imgui.h>\n#include <imgui_internal.h>\n\n#include <GLFW/glfw3.h>\n#include <hex/api/events/events_lifecycle.hpp>\n\n#include <wolv/utils/string.hpp>\n\n#include <clocale>\n#include <sstream>\n#include <hex/helpers/auto_reset.hpp>\n\n#if defined(OS_WINDOWS)\n    #include <windows.h>\n    #include <shellapi.h>\n    #include <wchar.h>\n\n    #include <wolv/utils/guards.hpp>\n#elif defined(OS_LINUX)\n    #include <unistd.h>\n    #include <dlfcn.h>\n    #include <spawn.h>\n    #include <hex/helpers/utils_linux.hpp>\n#elif defined(OS_MACOS)\n    #include <unistd.h>\n    #include <dlfcn.h>\n    #include <spawn.h>\n    #include <hex/helpers/utils_macos.hpp>\n    #include <CoreFoundation/CoreFoundation.h>\n#elif defined(OS_WEB)\n    #include \"emscripten.h\"\n#endif\n\nnamespace hex {\n\n    std::string to_string(u128 value) {\n        char data[45] = { 0 };\n\n        u8 index = sizeof(data) - 2;\n        while (value != 0 && index != 0) {\n            data[index] = static_cast<char>('0' + (value % 10));\n            value /= 10;\n            index--;\n        }\n\n        return { data + index + 1 };\n    }\n\n    std::string to_string(i128 value) {\n        char data[45] = { 0 };\n\n        u128 unsignedValue = value < 0 ? -value : value;\n\n        u8 index = sizeof(data) - 2;\n        while (unsignedValue != 0 && index != 0) {\n            data[index] = static_cast<char>('0' + (unsignedValue % 10));\n            unsignedValue /= 10;\n            index--;\n        }\n\n        if (value < 0) {\n            data[index] = '-';\n            return { data + index };\n        } else {\n            return { data + index + 1 };\n        }\n    }\n\n    std::string toLower(std::string string) {\n        for (char &c : string)\n            c = std::tolower(c);\n\n        return string;\n    }\n\n    std::string toUpper(std::string string) {\n        for (char &c : string)\n            c = std::toupper(c);\n\n        return string;\n    }\n\n    std::vector<u8> parseHexString(std::string string) {\n        if (string.empty())\n            return { };\n\n        // Remove common hex prefixes and commas\n        string = wolv::util::replaceStrings(string, \"0x\", \"\");\n        string = wolv::util::replaceStrings(string, \"0X\", \"\");\n        string = wolv::util::replaceStrings(string, \",\", \"\");\n\n        // Check for non-hex characters\n        bool isValidHexString = std::ranges::find_if(string, [](char c) {\n            return !std::isxdigit(c) && !std::isspace(c);\n        }) == string.end();\n\n        if (!isValidHexString)\n            return { };\n\n        // Remove all whitespace\n        std::erase_if(string, [](char c) { return std::isspace(c); });\n\n        // Only parse whole bytes\n        if (string.length() % 2 != 0)\n            return { };\n\n        // Convert hex string to bytes\n        return crypt::decode16(string);\n    }\n\n    std::optional<u8> parseBinaryString(const std::string &string) {\n        if (string.empty())\n            return std::nullopt;\n\n        u8 byte = 0x00;\n        for (char c : string) {\n            byte <<= 1;\n\n            if (c == '1')\n                byte |= 0b01;\n            else if (c == '0')\n                byte |= 0b00;\n            else\n                return std::nullopt;\n        }\n\n        return byte;\n    }\n\n    std::string toByteString(u64 bytes) {\n        double value = bytes;\n        u8 unitIndex = 0;\n\n        while (value > 1024) {\n            value /= 1024;\n            unitIndex++;\n\n            if (unitIndex == 6)\n                break;\n        }\n\n        std::string result;\n\n        if (unitIndex == 0)\n            result = fmt::format(\"{0:}\", value);\n        else\n            result = fmt::format(\"{0:.2f}\", value);\n\n        switch (unitIndex) {\n            case 0:\n                result += ((value == 1) ? \" Byte\" : \" Bytes\");\n            break;\n            case 1:\n                result += \" kiB\";\n            break;\n            case 2:\n                result += \" MiB\";\n            break;\n            case 3:\n                result += \" GiB\";\n            break;\n            case 4:\n                result += \" TiB\";\n            break;\n            case 5:\n                result += \" PiB\";\n            break;\n            case 6:\n                result += \" EiB\";\n            break;\n            default:\n                result = \"A lot!\";\n        }\n\n        return result;\n    }\n\n    std::string makeStringPrintable(const std::string &string) {\n        std::string result;\n        for (char c : string) {\n            if (std::isprint(c))\n                result += c;\n            else\n                result += fmt::format(\"\\\\x{0:02X}\", u8(c));\n        }\n\n        return result;\n    }\n\n    std::string makePrintable(u8 c) {\n        switch (c) {\n            case 0:\n                return \"NUL\";\n            case 1:\n                return \"SOH\";\n            case 2:\n                return \"STX\";\n            case 3:\n                return \"ETX\";\n            case 4:\n                return \"EOT\";\n            case 5:\n                return \"ENQ\";\n            case 6:\n                return \"ACK\";\n            case 7:\n                return \"BEL\";\n            case 8:\n                return \"BS\";\n            case 9:\n                return \"TAB\";\n            case 10:\n                return \"LF\";\n            case 11:\n                return \"VT\";\n            case 12:\n                return \"FF\";\n            case 13:\n                return \"CR\";\n            case 14:\n                return \"SO\";\n            case 15:\n                return \"SI\";\n            case 16:\n                return \"DLE\";\n            case 17:\n                return \"DC1\";\n            case 18:\n                return \"DC2\";\n            case 19:\n                return \"DC3\";\n            case 20:\n                return \"DC4\";\n            case 21:\n                return \"NAK\";\n            case 22:\n                return \"SYN\";\n            case 23:\n                return \"ETB\";\n            case 24:\n                return \"CAN\";\n            case 25:\n                return \"EM\";\n            case 26:\n                return \"SUB\";\n            case 27:\n                return \"ESC\";\n            case 28:\n                return \"FS\";\n            case 29:\n                return \"GS\";\n            case 30:\n                return \"RS\";\n            case 31:\n                return \"US\";\n            case 32:\n                return \"Space\";\n            case 127:\n                return \"DEL\";\n            default:\n                if (c >= 128)\n                    return \" \";\n                else\n                    return std::string() + static_cast<char>(c);\n        }\n    }\n\n    std::string toEngineeringString(double value) {\n        constexpr static std::array Suffixes = { \"a\", \"f\", \"p\", \"n\", \"u\", \"m\", \"\", \"k\", \"M\", \"G\", \"T\", \"P\", \"E\" };\n\n        int8_t suffixIndex = 6;\n\n        while (suffixIndex != 0 && suffixIndex != 12 && (value >= 1000 || value < 1) && value != 0) {\n            if (value >= 1000) {\n                value /= 1000;\n                suffixIndex++;\n            } else if (value < 1) {\n                value *= 1000;\n                suffixIndex--;\n            }\n        }\n\n        return std::to_string(value).substr(0, 5) + Suffixes[suffixIndex];\n    }\n\n    void startProgram(const std::vector<std::string> &command) {\n        #if defined(OS_WINDOWS)\n            std::ignore = system(fmt::format(\"start \\\"\\\" {0:?}\", fmt::join(command, \" \")).c_str());\n        #elif defined(OS_MACOS)\n            std::ignore = system(fmt::format(\"{0:?}\", fmt::join(command, \" \")).c_str());\n        #elif defined(OS_LINUX)\n            std::vector<std::string> xdgCommand = { \"xdg-open\" };\n            xdgCommand.insert(xdgCommand.end(), command.begin(), command.end());\n            executeCmd(xdgCommand);\n        #elif defined(OS_WEB)\n            std::ignore = command;\n        #endif\n    }\n\n    int executeCommand(const std::string &command) {\n        return ::system(command.c_str());\n    }\n\n    std::optional<std::string> executeCommandWithOutput(const std::string &command) {\n        std::array<char, 256> buffer = {};\n        std::string result;\n\n        #if defined(OS_WINDOWS)\n            FILE* pipe = _popen(command.c_str(), \"r\");\n        #else\n            FILE* pipe = popen(command.c_str(), \"r\");\n        #endif\n\n        if (!pipe) {\n            hex::log::error(\"Failed to open pipe for command: {}\", command);\n            return std::nullopt;\n        }\n\n        try {\n            while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {\n                result += buffer.data();\n            }\n        } catch (const std::exception &e) {\n            hex::log::error(\"Exception while reading command output: {}\", e.what());\n\n            #if defined(OS_WINDOWS)\n                _pclose(pipe);\n            #else\n                pclose(pipe);\n            #endif\n\n            return std::nullopt;\n        }\n\n        #if defined(OS_WINDOWS)\n            int exitCode = _pclose(pipe);\n        #else\n            int exitCode = pclose(pipe);\n        #endif\n\n        if (exitCode != 0) {\n            hex::log::debug(\"Command exited with code {}: {}\", exitCode, command);\n        }\n\n        return result;\n    }\n\n    void executeCommandDetach(const std::string &command) {\n        #if defined(OS_WINDOWS)\n            STARTUPINFOA si = { };\n            PROCESS_INFORMATION pi = { };\n            si.cb = sizeof(si);\n\n            DWORD flags = CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW;\n            std::string cmdCopy = command;\n\n            BOOL result = ::CreateProcessA(\n                nullptr,\n                cmdCopy.data(),\n                nullptr,\n                nullptr,\n                false,\n                flags,\n                nullptr,\n                nullptr,\n                &si,\n                &pi\n            );\n\n            if (result) {\n                ::CloseHandle(pi.hProcess);\n                ::CloseHandle(pi.hThread);\n            }\n        #elif defined(OS_MACOS) || defined(OS_LINUX)\n            pid_t pid;\n            const char* argv[] = { \"sh\", \"-c\", command.c_str(), nullptr };\n\n            ::posix_spawnp(&pid, \"sh\", nullptr, nullptr, const_cast<char* const*>(argv), nullptr);\n        #elif defined(OS_WEB)\n            std::ignore = command;\n        #endif\n    }\n\n    void openWebpage(std::string url) {\n        if (!url.contains(\"://\"))\n            url = \"https://\" + url;\n\n        #if defined(OS_WINDOWS)\n            ShellExecuteA(nullptr, \"open\", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL);\n        #elif defined(OS_MACOS)\n            openWebpageMacos(url.c_str());\n        #elif defined(OS_LINUX)\n            executeCmd({ \"xdg-open\", url });\n        #elif defined(OS_WEB)\n            EM_ASM({\n                window.open(UTF8ToString($0), '_blank');\n            }, url.c_str());\n        #else\n            #warning \"Unknown OS, can't open webpages\"\n        #endif\n    }\n\n    std::optional<u8> hexCharToValue(char c) {\n        if (std::isdigit(c))\n            return c - '0';\n        else if (std::isxdigit(c))\n            return std::toupper(c) - 'A' + 0x0A;\n        else\n            return { };\n    }\n\n    std::string encodeByteString(const std::vector<u8> &bytes) {\n        std::string result;\n\n        for (u8 byte : bytes) {\n            if (std::isprint(byte) && byte != '\\\\') {\n                result += char(byte);\n            } else {\n                switch (byte) {\n                    case '\\\\':\n                        result += \"\\\\\";\n                    break;\n                    case '\\a':\n                        result += \"\\\\a\";\n                    break;\n                    case '\\b':\n                        result += \"\\\\b\";\n                    break;\n                    case '\\f':\n                        result += \"\\\\f\";\n                    break;\n                    case '\\n':\n                        result += \"\\\\n\";\n                    break;\n                    case '\\r':\n                        result += \"\\\\r\";\n                    break;\n                    case '\\t':\n                        result += \"\\\\t\";\n                    break;\n                    case '\\v':\n                        result += \"\\\\v\";\n                    break;\n                    default:\n                        result += fmt::format(\"\\\\x{:02X}\", byte);\n                    break;\n                }\n            }\n        }\n\n        return result;\n    }\n\n    std::vector<u8> decodeByteString(const std::string &string) {\n        u32 offset = 0;\n        std::vector<u8> result;\n\n        while (offset < string.length()) {\n            auto c = [&] { return string[offset]; };\n\n            if (c() == '\\\\') {\n                if ((offset + 2) > string.length()) return {};\n\n                offset++;\n\n                char escapeChar = c();\n\n                offset++;\n\n                switch (escapeChar) {\n                    case 'a':\n                        result.push_back('\\a');\n                    break;\n                    case 'b':\n                        result.push_back('\\b');\n                    break;\n                    case 'f':\n                        result.push_back('\\f');\n                    break;\n                    case 'n':\n                        result.push_back('\\n');\n                    break;\n                    case 'r':\n                        result.push_back('\\r');\n                    break;\n                    case 't':\n                        result.push_back('\\t');\n                    break;\n                    case 'v':\n                        result.push_back('\\v');\n                    break;\n                    case '\\\\':\n                        result.push_back('\\\\');\n                    break;\n                    case 'x':\n                    {\n                        u8 byte = 0x00;\n                        if ((offset + 1) >= string.length()) return {};\n\n                        for (u8 i = 0; i < 2; i++) {\n                            byte <<= 4;\n                            if (auto hexValue = hexCharToValue(c()); hexValue.has_value())\n                                byte |= hexValue.value();\n                            else\n                                return {};\n\n                            offset++;\n                        }\n\n                        result.push_back(byte);\n                    }\n                    break;\n                    default:\n                        return {};\n                }\n            } else {\n                result.push_back(c());\n                offset++;\n            }\n        }\n\n        return result;\n    }\n\n    std::wstring utf8ToUtf16(const std::string& utf8) {\n        std::vector<u32> unicodes;\n\n        for (size_t byteIndex = 0; byteIndex < utf8.size();) {\n            u32 unicode = 0;\n            size_t unicodeSize = 0;\n\n            u8 ch = utf8[byteIndex];\n            byteIndex += 1;\n\n            if (ch <= 0x7F) {\n                unicode = ch;\n                unicodeSize = 0;\n            } else if (ch <= 0xBF) { //NOLINT(bugprone-branch-clone)\n                return { };\n            } else if (ch <= 0xDF) {\n                unicode = ch&0x1F;\n                unicodeSize = 1;\n            } else if (ch <= 0xEF) {\n                unicode = ch&0x0F;\n                unicodeSize = 2;\n            } else if (ch <= 0xF7) {\n                unicode = ch&0x07;\n                unicodeSize = 3;\n            } else {\n                return { };\n            }\n\n            for (size_t unicodeByteIndex = 0; unicodeByteIndex < unicodeSize; unicodeByteIndex += 1) {\n                if (byteIndex == utf8.size())\n                    return { };\n\n                u8 byte = utf8[byteIndex];\n                if (byte < 0x80 || byte > 0xBF)\n                    return { };\n\n                unicode <<= 6;\n                unicode += byte & 0x3F;\n\n                byteIndex += 1;\n            }\n\n            if (unicode >= 0xD800 && unicode <= 0xDFFF)\n                return { };\n            if (unicode > 0x10FFFF)\n                return { };\n\n            unicodes.push_back(unicode);\n        }\n\n        std::wstring utf16;\n\n        for (auto unicode : unicodes) {\n            if (unicode <= 0xFFFF) {\n                utf16 += static_cast<wchar_t>(unicode);\n            } else {\n                unicode -= 0x10000;\n                utf16 += static_cast<wchar_t>(((unicode >> 10) + 0xD800));\n                utf16 += static_cast<wchar_t>(((unicode & 0x3FF) + 0xDC00));\n            }\n        }\n        return utf16;\n    }\n\n    std::string utf16ToUtf8(const std::wstring& utf16) {\n        std::vector<u32> unicodes;\n\n        for (size_t index = 0; index < utf16.size();) {\n            u32 unicode = 0;\n\n            wchar_t wch = utf16[index];\n            index += 1;\n\n            if (wch < 0xD800 || wch > 0xDFFF) {\n                unicode = static_cast<u32>(wch); // NOLINT(cert-str34-c)\n            } else if (wch >= 0xD800 && wch <= 0xDBFF) {\n                if (index == utf16.size())\n                    return \"\";\n\n                wchar_t nextWch = utf16[index];\n                index += 1;\n\n                if (nextWch < 0xDC00 || nextWch > 0xDFFF)\n                    return \"\";\n\n                unicode = static_cast<u32>(((wch - 0xD800) << 10) + (nextWch - 0xDC00) + 0x10000);\n            } else {\n                return \"\";\n            }\n\n            unicodes.push_back(unicode);\n        }\n\n        std::string utf8;\n\n        for (auto unicode : unicodes) {\n            if (unicode <= 0x7F) {\n                utf8 += static_cast<char>(unicode);\n            } else if (unicode <= 0x7FF) {\n                utf8 += static_cast<char>(0xC0 | ((unicode >> 6) & 0x1F));\n                utf8 += static_cast<char>(0x80 | (unicode & 0x3F));\n            } else if (unicode <= 0xFFFF) {\n                utf8 += static_cast<char>(0xE0 | ((unicode >> 12) & 0x0F));\n                utf8 += static_cast<char>(0x80 | ((unicode >> 6) & 0x3F));\n                utf8 += static_cast<char>(0x80 | (unicode & 0x3F));\n            } else if (unicode <= 0x10FFFF) {\n                utf8 += static_cast<char>(0xF0 | ((unicode >> 18) & 0x07));\n                utf8 += static_cast<char>(0x80 | ((unicode >> 12) & 0x3F));\n                utf8 += static_cast<char>(0x80 | ((unicode >> 6) & 0x3F));\n                utf8 += static_cast<char>(0x80 | (unicode & 0x3F));\n            } else {\n                return \"\";\n            }\n        }\n\n        return utf8;\n    }\n\n    bool isProcessElevated() {\n#if defined(OS_WINDOWS)\n        bool elevated = false;\n        HANDLE token  = INVALID_HANDLE_VALUE;\n\n        if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) {\n            TOKEN_ELEVATION elevation;\n            DWORD elevationSize = sizeof(TOKEN_ELEVATION);\n\n            if (::GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &elevationSize))\n                elevated = elevation.TokenIsElevated;\n        }\n\n        if (token != INVALID_HANDLE_VALUE)\n            ::CloseHandle(token);\n\n        return elevated;\n#elif defined(OS_LINUX) || defined(OS_MACOS)\n        return getuid() == 0 || getuid() != geteuid();\n#else\n        return false;\n#endif\n    }\n\n    std::optional<std::string> getEnvironmentVariable(const std::string &env) {\n        auto value = std::getenv(env.c_str());\n\n        if (value == nullptr)\n            return std::nullopt;\n        else\n            return value;\n    }\n\n    [[nodiscard]] std::string limitStringLength(const std::string &string, size_t maxLength, bool fromBothEnds) {\n        // If the string is shorter than the max length, return it as is\n        if (string.size() < maxLength)\n            return string;\n\n        // If the string is longer than the max length, find the last space before the max length\n        auto it = string.begin() + (fromBothEnds ? maxLength / 2 : maxLength);\n        while (it != string.begin() && !std::isspace(*it)) --it;\n\n        // If there's no space before the max length, just cut the string\n        if (it == string.begin()) {\n            it = string.begin() + maxLength / 2;\n\n            // Try to find a UTF-8 character boundary\n            while (it != string.begin() && (*it & 0xC0) == 0x80) --it;\n        }\n\n        // If we still didn't find a valid boundary, just return the string as is\n        if (it == string.begin())\n            return string;\n\n        auto result = std::string(string.begin(), it) + \"…\";\n\n        if (!fromBothEnds)\n            return result;\n\n        // If the string is longer than the max length, find the last space before the max length\n        it = string.end() - 1 - maxLength / 2;\n        while (it != string.end() && !std::isspace(*it)) ++it;\n\n        // If there's no space before the max length, just cut the string\n        if (it == string.end()) {\n            it = string.end() - 1 - maxLength / 2;\n\n            // Try to find a UTF-8 character boundary\n            while (it != string.end() && (*it & 0xC0) == 0x80) ++it;\n        }\n\n        return result + std::string(it, string.end());\n    }\n\n    static std::optional<std::fs::path> s_fileToOpen;\n    extern \"C\" void openFile(const char *path) {\n        log::info(\"Opening file: {0}\", path);\n        s_fileToOpen = path;\n    }\n\n    std::optional<std::fs::path> getInitialFilePath() {\n        return s_fileToOpen;\n    }\n\n    static AutoReset<std::map<std::fs::path, std::string>> s_fonts;\n    extern \"C\" void registerFont(const char *fontName, const char *fontPath) {\n        s_fonts->emplace(fontPath, fontName);\n    }\n\n    const std::map<std::fs::path, std::string>& getFonts() {\n        return s_fonts;\n    }\n\n    namespace {\n\n        std::string generateHexViewImpl(u64 offset, auto begin, auto end) {\n            constexpr static auto HeaderLine = \"Hex View  00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F\\n\";\n            std::string result;\n\n            const auto size = std::distance(begin, end);\n            result.reserve(std::string(HeaderLine).size() * size / 0x10);\n\n            result += HeaderLine;\n\n            u64 address = offset & ~u64(0x0F);\n            std::string asciiRow;\n            for (auto it = begin; it != end; ++it) {\n                u8 byte = *it;\n\n                if ((address % 0x10) == 0) {\n                    result += fmt::format(\" {}\", asciiRow);\n                    result += fmt::format(\"\\n{0:08X}  \", address);\n\n                    asciiRow.clear();\n\n                    if (address == (offset & ~u64(0x0F))) {\n                        for (u64 i = 0; i < (offset - address); i++) {\n                            result += \"   \";\n                            asciiRow += \" \";\n                        }\n\n                        if (offset - address >= 8)\n                            result += \" \";\n\n                        address = offset;\n                    }\n                }\n\n                result += fmt::format(\"{0:02X} \", byte);\n                asciiRow += std::isprint(byte) ? char(byte) : '.';\n                if ((address % 0x10) == 0x07)\n                    result += \" \";\n\n                address++;\n            }\n\n            if ((address % 0x10) != 0x00)\n                for (u32 i = 0; i < (0x10 - (address % 0x10)); i++)\n                    result += \"   \";\n\n            result += fmt::format(\" {}\", asciiRow);\n\n            return result;\n        }\n\n    }\n\n    std::string generateHexView(u64 offset, u64 size, prv::Provider *provider) {\n        auto reader = prv::ProviderReader(provider);\n        reader.seek(offset);\n        reader.setEndAddress((offset + size) - 1);\n\n        return generateHexViewImpl(offset, reader.begin(), reader.end());\n    }\n\n    std::string generateHexView(u64 offset, const std::vector<u8> &data) {\n        return generateHexViewImpl(offset, data.begin(), data.end());\n    }\n\n    std::string formatSystemError(i32 error) {\n#if defined(OS_WINDOWS)\n        wchar_t *message = nullptr;\n        auto wLength = FormatMessageW(\n            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,\n            nullptr, error,\n            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\n            (wchar_t*)&message, 0,\n            nullptr\n        );\n        ON_SCOPE_EXIT { LocalFree(message); };\n\n        auto length = ::WideCharToMultiByte(CP_UTF8, 0, message, wLength, nullptr, 0, nullptr, nullptr);\n        std::string result(length, '\\x00');\n        ::WideCharToMultiByte(CP_UTF8, 0, message, wLength, result.data(), length, nullptr, nullptr);\n\n        return result;\n#else\n        return std::system_category().message(error);\n#endif\n    }\n\n\n    void* getContainingModule(void* symbol) {\n#if defined(OS_WINDOWS)\n        MEMORY_BASIC_INFORMATION mbi;\n        if (VirtualQuery(symbol, &mbi, sizeof(mbi)))\n            return mbi.AllocationBase;\n\n        return nullptr;\n#elif !defined(OS_WEB)\n        Dl_info info = {};\n        if (dladdr(symbol, &info) == 0)\n            return nullptr;\n\n        return dlopen(info.dli_fname, RTLD_LAZY);\n#else\n        std::ignore = symbol;\n        return nullptr;\n#endif\n    }\n\n    std::optional<ImColor> blendColors(const std::optional<ImColor> &a, const std::optional<ImColor> &b) {\n        if (!a.has_value() && !b.has_value())\n            return std::nullopt;\n        else if (a.has_value() && !b.has_value())\n            return a;\n        else if (!a.has_value() && b.has_value())\n            return b;\n        else\n            return ImAlphaBlendColors(a.value(), b.value());\n    }\n\n    std::optional<std::chrono::system_clock::time_point> parseTime(std::string_view format, const std::string &timeString) {\n        std::istringstream input(timeString);\n        input.imbue(std::locale(std::setlocale(LC_ALL, nullptr)));\n\n        tm time = {};\n        input >> std::get_time(&time, std::string(format).data());\n        if (input.fail()) {\n            return std::nullopt;\n        }\n\n        return std::chrono::system_clock::from_time_t(std::mktime(&time));\n    }\n\n    std::optional<std::string> getOSLanguage() {\n        const static auto osLanguage = [] -> std::optional<std::string> {\n            #if defined(OS_WINDOWS)\n                const auto langId = ::GetUserDefaultUILanguage();\n                std::array<wchar_t, LOCALE_NAME_MAX_LENGTH> localeName;\n                if (::LCIDToLocaleName(MAKELCID(langId, SORT_DEFAULT), localeName.data(), localeName.size(), 0) > 0) {\n                    return utf16ToUtf8(localeName.data());\n                }\n\n                return std::nullopt;\n            #elif defined(OS_MACOS)\n                const auto langs = CFLocaleCopyPreferredLanguages();\n                if (langs == nullptr || CFArrayGetCount(langs) == 0)\n                    return std::nullopt;\n\n                ON_SCOPE_EXIT { CFRelease(langs); };\n\n                const auto lang = (CFStringRef)CFArrayGetValueAtIndex(langs, 0);\n                std::array<char, 256> buffer;\n                if (CFStringGetCString(lang, buffer.data(), buffer.size(), kCFStringEncodingUTF8)) {\n                    return std::string(buffer.data());\n                }\n\n                return std::nullopt;\n            #elif defined(OS_LINUX)\n                auto lang = getEnvironmentVariable(\"LC_ALL\");\n                if (!lang.has_value()) lang = getEnvironmentVariable(\"LC_MESSAGES\");\n                if (!lang.has_value()) lang = getEnvironmentVariable(\"LANG\");\n\n                if (lang.has_value() && !lang->empty() && *lang != \"C\" && *lang != \"C.UTF-8\") {\n                    auto parts = wolv::util::splitString(*lang, \".\");\n                    if (!parts.empty())\n                        return parts[0];\n                    else\n                        return lang;\n                }\n\n                return std::nullopt;\n            #elif defined(OS_WEB)\n                char *resultRaw = (char*)EM_ASM_PTR({\n                    return stringToNewUTF8(navigator.language.length > 0 ? navigator.language : navigator.languages[0]);\n                });\n\n                std::string result(resultRaw);\n                std::free(resultRaw);\n\n                return result;\n            #else\n                return std::nullopt;\n            #endif\n        }();\n\n        return osLanguage;\n    }\n\n    extern \"C\" void macOSCloseButtonPressed() {\n        EventCloseButtonPressed::post();\n    }\n\n    extern \"C\" void macosEventDataReceived(const u8 *data, size_t length) {\n        EventNativeMessageReceived::post(std::vector<u8>(data, data + length));\n    }\n\n    void showErrorMessageBox(const std::string &message) {\n        log::fatal(\"{}\", message);\n        #if defined(OS_WINDOWS)\n            MessageBoxA(nullptr, message.c_str(), \"Error\", MB_ICONERROR | MB_OK);\n        #elif defined (OS_MACOS)\n            errorMessageMacos(message.c_str());\n        #elif defined(OS_LINUX)\n            if (std::system(fmt::format(R\"(zenity --error --text=\"{}\")\", message).c_str()) != 0) {\n                std::ignore = std::system(fmt::format(R\"(notify-send -i \"net.werwolv.ImHex\" \"Error\" \"{}\")\", message).c_str());\n            }\n        #elif defined(OS_WEB)\n            EM_ASM({\n                alert(UTF8ToString($0));\n            }, message.c_str());\n        #endif\n    }\n\n    void showToastMessage(const std::string &title, const std::string &message) {\n        #if defined(OS_WINDOWS)\n            const auto wideTitle = wolv::util::utf8ToWstring(title).value_or(L\"???\");\n            const auto wideMessage = wolv::util::utf8ToWstring(message).value_or(L\"???\");\n\n            WNDCLASS wc = { };\n            wc.lpfnWndProc = DefWindowProc;\n            wc.hInstance = GetModuleHandle(nullptr);\n            wc.lpszClassName = L\"ImHex Toast\";\n            RegisterClass(&wc);\n\n            HWND hwnd = CreateWindow(\n                wc.lpszClassName, L\"\", 0,\n                0, 0, 0, 0,\n                nullptr, nullptr, wc.hInstance, nullptr);\n\n            NOTIFYICONDATA nid = { };\n            nid.cbSize = sizeof(nid);\n            nid.hWnd = hwnd;\n            nid.uID = 1;\n            nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_INFO;\n            nid.uCallbackMessage = WM_USER + 1;\n            nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;\n            nid.hIcon = LoadIcon(nullptr, IDI_INFORMATION);\n            nid.uTimeout = 5000;\n            wcsncpy(nid.szTip, L\"ImHex\", ARRAYSIZE(nid.szTip));\n            wcsncpy(nid.szInfoTitle, wideTitle.c_str(), ARRAYSIZE(nid.szInfoTitle));\n            wcsncpy(nid.szInfo, wideMessage.c_str(), ARRAYSIZE(nid.szInfo));\n\n            nid.dwInfoFlags = NIIF_INFO;\n\n            Shell_NotifyIcon(NIM_ADD, &nid);\n\n            Sleep(100);\n\n            Shell_NotifyIcon(NIM_DELETE, &nid);\n            CloseWindow(hwnd);\n            DestroyWindow(hwnd);\n        #elif defined(OS_MACOS)\n            toastMessageMacos(title.c_str(), message.c_str());\n        #elif defined(OS_LINUX)\n            if (std::system(fmt::format(R\"(notify-send -i \"net.werwolv.ImHex\" \"{}\" \"{}\")\", title, message).c_str()) != 0) {\n                std::ignore = std::system(fmt::format(R\"(zenity --info --title=\"{}\" --text=\"{}\")\", title, message).c_str());\n            }\n        #elif defined(OS_WEB)\n            EM_ASM({\n                try {\n                    const t = UTF8ToString($0);\n                    const m = UTF8ToString($1);\n\n                    if (Notification.permission === \"granted\") {\n                        new Notification(t, { body: m });\n                    } else if (Notification.permission !== \"denied\") {\n                        Notification.requestPermission().then(function(p) {\n                            if (p === \"granted\") {\n                                new Notification(t, { body: m });\n                            }\n                        });\n                    }\n                } catch (e) {\n                    console.error(e);\n                }\n            }, title.c_str(), message.c_str());\n        #endif\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/helpers/utils_linux.cpp",
    "content": "#if defined(OS_LINUX)\n\n#include <hex/helpers/logger.hpp>\n\n#include <vector>\n#include <string>\n#include <unistd.h>\n\nnamespace hex {\n\n    void executeCmd(const std::vector<std::string> &argsVector) {\n        std::vector<char*> cArgsVector;\n        cArgsVector.reserve(argsVector.size());\nfor (const auto &str : argsVector) {\n            cArgsVector.push_back(const_cast<char*>(str.c_str()));\n        }\n        cArgsVector.push_back(nullptr);\n        \n        if (fork() == 0) {\n            execvp(cArgsVector[0], cArgsVector.data());\n            log::error(\"execvp() failed: {}\", strerror(errno));\n            exit(EXIT_FAILURE);\n        }\n    }\n\n}\n\n#endif\n"
  },
  {
    "path": "lib/libimhex/source/helpers/utils_macos.m",
    "content": "#if defined(OS_MACOS)\n\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n\n    #include <CoreFoundation/CFBundle.h>\n    #include <ApplicationServices/ApplicationServices.h>\n    #include <Foundation/NSUserDefaults.h>\n    #include <AppKit/NSScreen.h>\n    #include <CoreFoundation/CoreFoundation.h>\n    #include <CoreText/CoreText.h>\n\n    #include <string.h>\n    #include <stdlib.h>\n    #include <stdint.h>\n\n    #define GLFW_EXPOSE_NATIVE_COCOA\n    #include <GLFW/glfw3.h>\n    #include <GLFW/glfw3native.h>\n\n    #import <Cocoa/Cocoa.h>\n    #import <Foundation/Foundation.h>\n    #import <AppleScriptObjC/AppleScriptObjC.h>\n    #import <UserNotifications/UserNotifications.h>\n\n    #include <hex/helpers/keys.hpp>\n\n    void errorMessageMacos(const char *cMessage) {\n        CFStringRef strMessage = CFStringCreateWithCString(NULL, cMessage, kCFStringEncodingUTF8);\n        CFUserNotificationDisplayAlert(0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, strMessage, NULL, NULL, NULL, NULL, NULL);\n    }\n\n    void openFile(const char *path);\n    void registerFont(const char *fontName, const char *fontPath);\n\n    void openWebpageMacos(const char *url) {\n        CFURLRef urlRef = CFURLCreateWithBytes(NULL, (uint8_t*)(url), strlen(url), kCFStringEncodingASCII, NULL);\n        LSOpenCFURLRef(urlRef, NULL);\n        CFRelease(urlRef);\n    }\n\n    bool isMacosSystemDarkModeEnabled(void) {\n        NSString * appleInterfaceStyle = [[NSUserDefaults standardUserDefaults] stringForKey:@\"AppleInterfaceStyle\"];\n\n        if (appleInterfaceStyle && [appleInterfaceStyle length] > 0) {\n            return [[appleInterfaceStyle lowercaseString] containsString:@\"dark\"];\n        } else {\n            return false;\n        }\n    }\n\n    float getBackingScaleFactor(void) {\n        return [[NSScreen mainScreen] backingScaleFactor];\n    }\n\n    void macOSCloseButtonPressed(void);\n\n    @interface CloseButtonHandler : NSObject\n    @end\n\n    @implementation CloseButtonHandler\n    - (void)pressed:(id)sender {\n        macOSCloseButtonPressed();\n    }\n    @end\n\n    void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode) {\n        NSWindow* cocoaWindow = glfwGetCocoaWindow(window);\n\n        cocoaWindow.titleVisibility = NSWindowTitleHidden;\n\n        if (borderlessWindowMode) {\n            cocoaWindow.titlebarAppearsTransparent = YES;\n            cocoaWindow.styleMask |= NSWindowStyleMaskFullSizeContentView;\n\n            // Setup liquid glass background effect\n            {\n                NSView* glfwContentView = [cocoaWindow contentView];\n\n                NSOpenGLContext* context = [NSOpenGLContext currentContext];\n                if (!context) {\n                    glfwMakeContextCurrent(window);\n                    context = [NSOpenGLContext currentContext];\n                }\n\n                GLint opaque = 0;\n                [context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity];\n                [context update];\n\n                NSView* containerView = [[NSView alloc] initWithFrame:[glfwContentView frame]];\n                containerView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;\n                [containerView setWantsLayer:YES];\n\n                Class glassEffectClass = NSClassFromString(@\"NSGlassEffectView\");\n                NSView* effectView = nil;\n                if (glassEffectClass) {\n                    // Use the new liquid glass effect\n                    effectView = [[glassEffectClass alloc] initWithFrame:[containerView bounds]];\n                } else {\n                    // Fall back to NSVisualEffectView for older systems\n                    NSVisualEffectView* visualEffectView = [[NSVisualEffectView alloc] initWithFrame:[containerView bounds]];\n                    visualEffectView.material = NSVisualEffectMaterialHUDWindow;\n                    visualEffectView.blendingMode = NSVisualEffectBlendingModeBehindWindow;\n                    visualEffectView.state = NSVisualEffectStateActive;\n                    effectView = visualEffectView;\n                }\n\n                effectView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;\n\n                [containerView addSubview:effectView];\n\n                [glfwContentView removeFromSuperview];\n                glfwContentView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;\n                [containerView addSubview:glfwContentView];\n\n                [cocoaWindow setContentView:containerView];\n            }\n\n            [cocoaWindow setOpaque:NO];\n            [cocoaWindow setHasShadow:YES];\n            [cocoaWindow setBackgroundColor:[NSColor colorWithWhite: 0 alpha: 0.001f]];\n        }\n\n        NSButton *closeButton = [cocoaWindow standardWindowButton:NSWindowCloseButton];\n        [closeButton setAction:@selector(pressed:)];\n        [closeButton setTarget:[CloseButtonHandler alloc]];\n    }\n\n    bool isMacosFullScreenModeEnabled(GLFWwindow *window) {\n        NSWindow* cocoaWindow = glfwGetCocoaWindow(window);\n        return (cocoaWindow.styleMask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;\n    }\n\n    void enumerateFontsMacos(void) {\n        CFArrayRef fontDescriptors = CTFontManagerCopyAvailableFontFamilyNames();\n        CFIndex count = CFArrayGetCount(fontDescriptors);\n\n        for (CFIndex i = 0; i < count; i++) {\n            CFStringRef fontName = (CFStringRef)CFArrayGetValueAtIndex(fontDescriptors, i);\n\n            // Get font path - skip fonts without valid URLs\n            CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ (__bridge NSString *)kCTFontFamilyNameAttribute : (__bridge NSString *)fontName };\n            CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(attributes);\n            CFURLRef fontURL = CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);\n\n            if (fontURL != NULL) {\n                CFStringRef fontPath = CFURLCopyFileSystemPath(fontURL, kCFURLPOSIXPathStyle);\n\n                if (fontPath != NULL) {\n                    registerFont([(__bridge NSString *)fontName UTF8String], [(__bridge NSString *)fontPath UTF8String]);\n                    CFRelease(fontPath);\n                }\n\n                CFRelease(fontURL);\n            }\n\n            CFRelease(descriptor);\n        }\n\n        CFRelease(fontDescriptors);\n    }\n\n    void macosHandleTitlebarDoubleClickGesture(GLFWwindow *window) {\n        NSWindow* cocoaWindow = glfwGetCocoaWindow(window);\n\n        // Consult user preferences: \"System Settings -> Desktop & Dock -> Double-click a window's title bar to\"\n        NSString* action = [[NSUserDefaults standardUserDefaults] stringForKey:@\"AppleActionOnDoubleClick\"];\n        \n        if (action == nil || [action isEqualToString:@\"None\"]) {\n            // Nothing to do\n        } else if ([action isEqualToString:@\"Minimize\"]) {\n            if ([cocoaWindow isMiniaturizable]) {\n                [cocoaWindow miniaturize:nil];\n            }\n        } else if ([action isEqualToString:@\"Maximize\"]) {\n            // `[NSWindow zoom:_ sender]` takes over pumping the main runloop for the duration of the resize,\n            // and would interfere with our renderer's frame logic. Schedule it for the next frame\n            \n            CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{\n                if ([cocoaWindow isZoomable]) {\n                    [cocoaWindow zoom:nil];\n                }\n            });\n        }\n    }\n\n    void macosSetWindowMovable(GLFWwindow *window, bool movable) {\n        NSWindow* cocoaWindow = glfwGetCocoaWindow(window);\n\n        [cocoaWindow setMovable:movable];\n    }\n\n    bool macosIsWindowBeingResizedByUser(GLFWwindow *window) {\n        NSWindow* cocoaWindow = glfwGetCocoaWindow(window);\n        \n        return cocoaWindow.inLiveResize;\n    }\n\n    void macosMarkContentEdited(GLFWwindow *window, bool edited) {\n        NSWindow* cocoaWindow = glfwGetCocoaWindow(window);\n\n        [cocoaWindow setDocumentEdited:edited];\n    }\n\n    static NSArray* getRunningInstances(NSString *bundleIdentifier) {\n        return [NSRunningApplication runningApplicationsWithBundleIdentifier: bundleIdentifier];\n    }\n\n    bool macosIsMainInstance(void) {\n        NSArray *applications = getRunningInstances(@\"net.WerWolv.ImHex\");\n        return applications.count == 0;\n    }\n\n    extern void macosEventDataReceived(const unsigned char *data, size_t length);\n    static OSErr handleAppleEvent(const AppleEvent *event, AppleEvent *reply, void *refcon) {\n        (void)reply;\n        (void)refcon;\n\n        // Extract the raw binary data from the event's parameter\n        AEDesc paramDesc;\n        OSErr err = AEGetParamDesc(event, keyDirectObject, typeWildCard, &paramDesc);\n        if (err != noErr) {\n            NSLog(@\"Failed to get parameter: %d\", err);\n            return err;\n        }\n\n        // Convert the AEDesc to NSData\n        NSAppleEventDescriptor *descriptor = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&paramDesc];\n        NSData *binaryData = descriptor.data;\n\n        // Process the binary data\n        if (binaryData) {\n            macosEventDataReceived(binaryData.bytes, binaryData.length);\n        }\n\n        return noErr;\n    }\n\n    void macosInstallEventListener(void) {\n        AEInstallEventHandler('misc', 'imhx', NewAEEventHandlerUPP(handleAppleEvent), 0, false);\n    }\n\n    void macosSendMessageToMainInstance(const unsigned char *data, size_t size) {\n        NSString *bundleIdentifier = @\"net.WerWolv.ImHex\";\n\n        NSData *binaryData = [NSData dataWithBytes:data length:size];\n        // Find the target application by its bundle identifier\n        NSAppleEventDescriptor *targetApp = [NSAppleEventDescriptor descriptorWithBundleIdentifier:bundleIdentifier];\n        if (!targetApp) {\n            NSLog(@\"Application with bundle identifier %@ not found.\", bundleIdentifier);\n            return;\n        }\n\n        // Create the Apple event\n        NSAppleEventDescriptor *event = [[NSAppleEventDescriptor alloc] initWithEventClass:'misc'\n                                                                                   eventID:'imhx'\n                                                                          targetDescriptor:targetApp\n                                                                                  returnID:kAutoGenerateReturnID\n                                                                             transactionID:kAnyTransactionID];\n\n        // Add a parameter with raw binary data\n        NSAppleEventDescriptor *binaryDescriptor = [NSAppleEventDescriptor descriptorWithDescriptorType:typeData\n                                                                                                   data:binaryData];\n        [event setParamDescriptor:binaryDescriptor forKeyword:keyDirectObject];\n\n        // Send the event\n        OSStatus status = AESendMessage([event aeDesc], NULL, kAENoReply, kAEDefaultTimeout);\n        if (status != noErr) {\n            NSLog(@\"Failed to send Apple event: %d\", status);\n        }\n    }\n\n    @interface HexDocument : NSDocument\n\n    @end\n\n    @implementation HexDocument\n\n    - (BOOL) readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError {\n        NSString* urlString = [url absoluteString];\n        const char* utf8String = [urlString UTF8String];\n\n        const char *prefix = \"file://\";\n        if (strncmp(utf8String, prefix, strlen(prefix)) == 0)\n            utf8String += strlen(prefix);\n\n        openFile(utf8String);\n\n        return YES;\n    }\n\n    @end\n\n    void macosGetKey(enum Keys key, int *output) {\n        *output = 0x00;\n        switch (key) {\n            case Space:             *output = ' ';  break;\n            case Apostrophe:        *output = '\\''; break;\n            case Comma:             *output = ',';  break;\n            case Minus:             *output = '-';  break;\n            case Period:            *output = '.';  break;\n            case Slash:             *output = '/';  break;\n            case Num0:              *output = '0';  break;\n            case Num1:              *output = '1';  break;\n            case Num2:              *output = '2';  break;\n            case Num3:              *output = '3';  break;\n            case Num4:              *output = '4';  break;\n            case Num5:              *output = '5';  break;\n            case Num6:              *output = '6';  break;\n            case Num7:              *output = '7';  break;\n            case Num8:              *output = '8';  break;\n            case Num9:              *output = '9';  break;\n            case Semicolon:         *output = ';';  break;\n            case Equals:            *output = '=';  break;\n            case A:                 *output = 'a';  break;\n            case B:                 *output = 'b';  break;\n            case C:                 *output = 'c';  break;\n            case D:                 *output = 'd';  break;\n            case E:                 *output = 'e';  break;\n            case F:                 *output = 'f';  break;\n            case G:                 *output = 'g';  break;\n            case H:                 *output = 'h';  break;\n            case I:                 *output = 'i';  break;\n            case J:                 *output = 'j';  break;\n            case K:                 *output = 'k';  break;\n            case L:                 *output = 'l';  break;\n            case M:                 *output = 'm';  break;\n            case N:                 *output = 'n';  break;\n            case O:                 *output = 'o';  break;\n            case P:                 *output = 'p';  break;\n            case Q:                 *output = 'q';  break;\n            case R:                 *output = 'r';  break;\n            case S:                 *output = 's';  break;\n            case T:                 *output = 't';  break;\n            case U:                 *output = 'u';  break;\n            case V:                 *output = 'v';  break;\n            case W:                 *output = 'w';  break;\n            case X:                 *output = 'x';  break;\n            case Y:                 *output = 'y';  break;\n            case Z:                 *output = 'z';  break;\n            case LeftBracket:       *output = '/';  break;\n            case Backslash:         *output = '\\\\'; break;\n            case RightBracket:      *output = ']';  break;\n            case GraveAccent:       *output = '`';  break;\n            case World1:            break;\n            case World2:            break;\n            case Escape:            break;\n            case Enter:             *output = NSEnterCharacter; break;\n            case Tab:               *output = NSTabCharacter; break;\n            case Backspace:         *output = NSBackspaceCharacter; break;\n            case Insert:            *output = NSInsertFunctionKey; break;\n            case Delete:            *output = NSDeleteCharacter; break;\n            case Right:             *output = NSRightArrowFunctionKey; break;\n            case Left:              *output = NSLeftArrowFunctionKey; break;\n            case Down:              *output = NSDownArrowFunctionKey; break;\n            case Up:                *output = NSUpArrowFunctionKey; break;\n            case PageUp:            *output = NSPageUpFunctionKey; break;\n            case PageDown:          *output = NSPageDownFunctionKey; break;\n            case Home:              *output = NSHomeFunctionKey; break;\n            case End:               *output = NSEndFunctionKey; break;\n            case CapsLock:          break;\n            case ScrollLock:        *output = NSScrollLockFunctionKey; break;\n            case NumLock:           break;\n            case PrintScreen:       *output = NSPrintScreenFunctionKey; break;\n            case Pause:             *output = NSPauseFunctionKey; break;\n            case F1:                *output = NSF1FunctionKey;  break;\n            case F2:                *output = NSF2FunctionKey;  break;\n            case F3:                *output = NSF3FunctionKey;  break;\n            case F4:                *output = NSF4FunctionKey;  break;\n            case F5:                *output = NSF5FunctionKey;  break;\n            case F6:                *output = NSF6FunctionKey;  break;\n            case F7:                *output = NSF7FunctionKey;  break;\n            case F8:                *output = NSF8FunctionKey;  break;\n            case F9:                *output = NSF9FunctionKey;  break;\n            case F10:               *output = NSF10FunctionKey; break;\n            case F11:               *output = NSF11FunctionKey; break;\n            case F12:               *output = NSF12FunctionKey; break;\n            case F13:               *output = NSF13FunctionKey; break;\n            case F14:               *output = NSF14FunctionKey; break;\n            case F15:               *output = NSF15FunctionKey; break;\n            case F16:               *output = NSF16FunctionKey; break;\n            case F17:               *output = NSF17FunctionKey; break;\n            case F18:               *output = NSF18FunctionKey; break;\n            case F19:               *output = NSF19FunctionKey; break;\n            case F20:               *output = NSF20FunctionKey; break;\n            case F21:               *output = NSF21FunctionKey; break;\n            case F22:               *output = NSF22FunctionKey; break;\n            case F23:               *output = NSF23FunctionKey; break;\n            case F24:               *output = NSF24FunctionKey; break;\n            case F25:               *output = NSF25FunctionKey; break;\n            case KeyPad0:           *output = '0'; break;\n            case KeyPad1:           *output = '1'; break;\n            case KeyPad2:           *output = '2'; break;\n            case KeyPad3:           *output = '3'; break;\n            case KeyPad4:           *output = '4'; break;\n            case KeyPad5:           *output = '5'; break;\n            case KeyPad6:           *output = '6'; break;\n            case KeyPad7:           *output = '7'; break;\n            case KeyPad8:           *output = '8'; break;\n            case KeyPad9:           *output = '9'; break;\n            case KeyPadDecimal:     *output = '.'; break;\n            case KeyPadDivide:      *output = '/'; break;\n            case KeyPadMultiply:    *output = '*'; break;\n            case KeyPadSubtract:    *output = '-'; break;\n            case KeyPadAdd:         *output = '+'; break;\n            case KeyPadEnter:       *output = NSEnterCharacter; break;\n            case KeyPadEqual:       *output = '='; break;\n            case Menu:              *output = NSMenuFunctionKey; break;\n            default:                break;\n        }\n    }\n\n\n    static bool isRunningInAppBundle(void) {\n        NSString *bundlePath = [[NSBundle mainBundle] bundlePath];\n        return [bundlePath.pathExtension.lowercaseString isEqualToString:@\"app\"];\n    }\n\n    @interface NotificationDelegate : NSObject <UNUserNotificationCenterDelegate>\n    @end\n\n    @implementation NotificationDelegate\n\n    - (void)userNotificationCenter:(UNUserNotificationCenter *)center\n           willPresentNotification:(UNNotification *)notification\n             withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {\n        if (@available(macOS 11.0, *)) {\n            completionHandler(UNNotificationPresentationOptionBanner |\n                              UNNotificationPresentationOptionSound |\n                              UNNotificationPresentationOptionList);\n        } else {\n            // For macOS 10.15 and earlier\n            completionHandler(UNNotificationPresentationOptionAlert |\n                              UNNotificationPresentationOptionSound);\n        }\n    }\n\n    @end\n\n    void toastMessageMacos(const char *title, const char *message) {\n        @autoreleasepool {\n            // Only show notification if we're inside a bundle\n            if (!isRunningInAppBundle()) {\n                return;\n            }\n\n            NSString *nsTitle = [NSString stringWithUTF8String:title];\n            NSString *nsMessage = [NSString stringWithUTF8String:message];\n\n            UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];\n\n            // Request permission if needed\n            [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound)\n                                  completionHandler:^(BOOL granted, NSError * _Nullable error) {\n\n                (void)error;\n                if (!granted) return;\n\n                UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];\n                content.title = nsTitle;\n                content.body = nsMessage;\n                content.sound = [UNNotificationSound defaultSound];\n\n                // Show notification immediately\n                UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.1 repeats:NO];\n\n                NSString *identifier = [[NSUUID UUID] UUIDString];\n                UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier\n                                                                                      content:content\n                                                                                      trigger:trigger];\n\n                [center addNotificationRequest:request withCompletionHandler:nil];\n            }];\n        }\n    }\n\n    #pragma clang diagnostic pop\n\n#endif\n"
  },
  {
    "path": "lib/libimhex/source/mcp/client.cpp",
    "content": "#include <hex/mcp/client.hpp>\n#include <hex/mcp/server.hpp>\n\n#include <hex.hpp>\n\n#include <string>\n#include <cstdlib>\n\n#include <fmt/format.h>\n#include <hex/api/imhex_api/system.hpp>\n#include <hex/helpers/logger.hpp>\n#include <nlohmann/json.hpp>\n#include <wolv/literals.hpp>\n#include <wolv/net/socket_client.hpp>\n\nnamespace hex::mcp {\n\n    using namespace wolv::literals;\n\n    int Client::run(std::istream &input, std::ostream &output) {\n        wolv::net::SocketClient client(wolv::net::SocketClient::Type::TCP, true);\n        client.connect(\"127.0.0.1\", Server::McpInternalPort);\n\n        while (true) {\n            std::string request;\n            std::getline(input, request);\n\n            if (ImHexApi::System::isMainInstance()) {\n                JsonRpc response(request);\n                response.setError(JsonRpc::ErrorCode::InternalError, \"No other instance of ImHex is running. Make sure that you have ImHex open already.\");\n                output << response.execute([](auto, auto){ return nlohmann::json::object(); }).value_or(\"\") << '\\n';\n                continue;\n            }\n\n            client.writeString(request);\n            auto response = client.readBytesUntil(0x00);\n            if (!response.empty() && response.front() != 0x00)\n                output << std::string(response.begin(), response.end()) << std::endl;\n\n            if (!client.isConnected())\n                break;\n\n            std::this_thread::sleep_for(std::chrono::milliseconds(100));\n        }\n\n        return EXIT_SUCCESS;\n    }\n}\n"
  },
  {
    "path": "lib/libimhex/source/mcp/server.cpp",
    "content": "#include <hex/mcp/server.hpp>\n\n#include <string>\n\n#include <fmt/format.h>\n#include <hex/helpers/logger.hpp>\n#include <nlohmann/json.hpp>\n#include <utility>\n#include <hex/api/task_manager.hpp>\n#include <wolv/net/socket_client.hpp>\n#include <hex/api/imhex_api/system.hpp>\n\n\nnamespace hex::mcp {\n\n    std::optional<std::string> JsonRpc::execute(const Callback &callback) {\n        try {\n            auto requestJson = nlohmann::json::parse(m_request);\n\n            if (requestJson.is_array()) {\n                return handleBatchedMessages(requestJson, callback).transform([](const auto &response) { return response.dump(); });\n            } else {\n                return handleMessage(requestJson, callback).transform([](const auto &response) { return response.dump(); });\n            }\n        } catch (const nlohmann::json::exception &) {\n            return createErrorMessage(ErrorCode::ParseError, \"Parse error\").dump();\n        }\n    }\n\n    void JsonRpc::setError(ErrorCode code, std::string message) {\n        m_error = Error{ code, std::move(message) };\n    }\n\n    std::optional<nlohmann::json> JsonRpc::handleMessage(const nlohmann::json &request, const Callback &callback) {\n        try {\n            // Validate JSON-RPC request\n            if (!request.contains(\"jsonrpc\") || request[\"jsonrpc\"] != \"2.0\" ||\n                !request.contains(\"method\") || !request[\"method\"].is_string()) {\n                m_id = request.contains(\"id\") ? std::optional(request[\"id\"].get<int>()) : std::nullopt;\n\n                return createErrorMessage(ErrorCode::InvalidRequest, \"Invalid Request\");\n            }\n\n            m_id = request.contains(\"id\") ? std::optional(request[\"id\"].get<int>()) : std::nullopt;\n\n            // Return a user-specified error if set\n            if (m_error.has_value()) {\n                return createErrorMessage(m_error->code, m_error->message);\n            }\n\n            // Execute the method\n            auto result = callback(request[\"method\"].get<std::string>(), request.value(\"params\", nlohmann::json::object()));\n\n            if (!m_id.has_value())\n                return std::nullopt;\n\n            return createResponseMessage(result.is_null() ? nlohmann::json::object() : result);\n        } catch (const MethodNotFoundException &) {\n            return createErrorMessage(ErrorCode::MethodNotFound, \"Method not found\");\n        } catch (const InvalidParametersException &) {\n            return createErrorMessage(ErrorCode::InvalidParams, \"Invalid params\");\n        } catch (const std::exception &e) {\n            return createErrorMessage(ErrorCode::InternalError, e.what());\n        }\n    }\n\n    std::optional<nlohmann::json> JsonRpc::handleBatchedMessages(const nlohmann::json &request, const Callback &callback) {\n        if (!request.is_array()) {\n            return createErrorMessage(ErrorCode::InvalidRequest, \"Invalid Request\");\n        }\n\n        nlohmann::json responses = nlohmann::json::array();\n        for (const auto &message : request) {\n            auto response = handleMessage(message, callback);\n            if (response.has_value())\n                responses.push_back(*response);\n        }\n\n        if (responses.empty())\n            return std::nullopt;\n\n        return responses;\n    }\n\n    nlohmann::json JsonRpc::createDefaultMessage() {\n        nlohmann::json message;\n        message[\"jsonrpc\"] = \"2.0\";\n        if (m_id.has_value())\n            message[\"id\"] = m_id.value();\n        else\n            message[\"id\"] = nullptr;\n\n        return message;\n    }\n\n    nlohmann::json JsonRpc::createErrorMessage(ErrorCode code, const std::string &message) {\n        auto json = createDefaultMessage();\n        json[\"error\"] = {\n            { \"code\",    int(code) },\n            { \"message\", message   }\n        };\n        return json;\n    }\n\n    nlohmann::json JsonRpc::createResponseMessage(const nlohmann::json &result) {\n        auto json = createDefaultMessage();\n        json[\"result\"] = result;\n        return json;\n    }\n\n    Server::Server() : m_server(McpInternalPort, 1024, 1, true) {\n\n    }\n\n    Server::~Server() {\n        this->shutdown();\n    }\n\n    void Server::listen() {\n        m_server.accept([this](auto, const std::vector<u8> &data) -> std::vector<u8> {\n            std::string request(data.begin(), data.end());\n\n            TaskManager::setCurrentThreadName(\"MCP Server\");\n            log::debug(\"MCP ----> {}\", request);\n\n            JsonRpc rpc(request);\n            auto response = rpc.execute([this](const std::string &method, const nlohmann::json &params) -> nlohmann::json {\n                if (method == \"initialize\") {\n                    return handleInitialize(params);\n                } else if (method.starts_with(\"notifications/\")) {\n                    handleNotifications(method.substr(14), params);\n                    return {};\n                } else if (method.ends_with(\"/list\")) {\n                    auto primitiveName = method.substr(0, method.size() - 5);\n                    if (m_primitives.contains(primitiveName)) {\n                        nlohmann::json capabilitiesList = nlohmann::json::array();\n                        for (const auto &[name, primitive] : m_primitives[primitiveName]) {\n                            capabilitiesList.push_back(primitive.capabilities);\n                        }\n\n                        nlohmann::json result;\n                        result[primitiveName] = capabilitiesList;\n                        return result;\n                    }\n                } else if (method.ends_with(\"/call\")) {\n                    auto primitive = method.substr(0, method.size() - 5);\n                    if (auto primitiveIt = m_primitives.find(primitive); primitiveIt != m_primitives.end()) {\n                        auto name = params.value(\"name\", \"\");\n                        if (auto functionIt = primitiveIt->second.find(name); functionIt != primitiveIt->second.end()) {\n                            auto result = functionIt->second.function(params.value(\"arguments\", nlohmann::json::object()));\n\n                            return result.is_null() ? nlohmann::json::object() : result;\n                        }\n                    }\n                }\n\n                throw JsonRpc::MethodNotFoundException();\n            });\n\n            log::debug(\"MCP <---- {}\", response.value_or(\"<Nothing>\"));\n\n            if (response.has_value()) {\n                response->push_back(0x00);\n                return { response->begin(), response->end() };\n            }\n            else\n                return std::vector<u8>{ 0x00 };\n        }, [this](auto) {\n            log::info(\"MCP client disconnected\");\n            m_connected = false;\n            m_clientInfo = {};\n        }, true);\n    }\n\n    void Server::shutdown() {\n        m_server.shutdown();\n    }\n\n    void Server::disconnect() {\n        m_server.disconnectClients();\n    }\n\n    void Server::addPrimitive(std::string type, std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json &params)> function) {\n        auto json = nlohmann::json::parse(capabilities);\n        auto name = json[\"name\"].get<std::string>();\n\n        m_primitives[type][name] = {\n            .capabilities=json,\n            .function=function\n        };\n    }\n\n\n    nlohmann::json Server::handleInitialize(const nlohmann::json &params) {\n        constexpr static auto ServerName = \"ImHex\";\n        constexpr static auto ProtocolVersion = \"2025-06-18\";\n\n        m_clientInfo = {};\n\n        if (params.contains(\"protocolVersion\")) {\n            auto clientProtocolVersion = params[\"protocolVersion\"].get<std::string>();\n            m_clientInfo.protocolVersion = clientProtocolVersion;\n        } else {\n            throw JsonRpc::InvalidParametersException();\n        }\n\n        if (params.contains(\"clientInfo\")) {\n            const auto &clientInfo = params[\"clientInfo\"];\n            m_clientInfo.name = clientInfo.value(\"name\", \"???\");\n            m_clientInfo.version = clientInfo.value(\"version\", \"???\");\n\n            log::info(\"MCP client connected: {} v{} (protocol {})\", m_clientInfo.name, m_clientInfo.version, m_clientInfo.protocolVersion);\n        } else {\n            log::info(\"MCP client connected: Unknown client info\");\n        }\n\n        return {\n            { \"protocolVersion\", ProtocolVersion },\n            {\n                \"capabilities\",\n                {\n                    { \"tools\",     nlohmann::json::object() },\n                },\n            },\n            {\n                \"serverInfo\", {\n                   { \"name\",    ServerName    },\n                   { \"version\", ImHexApi::System::getImHexVersion().get() }\n                }\n            }\n        };\n    }\n\n    void Server::handleNotifications(const std::string &method, [[maybe_unused]] const nlohmann::json &params) {\n        if (method == \"initialized\") {\n            m_connected = true;\n        }\n    }\n\n    bool Server::isConnected() {\n        return m_connected;\n    }\n}\n"
  },
  {
    "path": "lib/libimhex/source/providers/cached_provider.cpp",
    "content": "#include \"hex/providers/cached_provider.hpp\"\n#include <algorithm>\n#include <optional>\n\nnamespace hex::prv {\n\n    CachedProvider::CachedProvider(size_t cacheBlockSize, size_t maxBlocks)\n        : m_cacheBlockSize(cacheBlockSize), m_maxBlocks(maxBlocks), m_cache(maxBlocks) {}\n\n    CachedProvider::~CachedProvider() {\n        clearCache();\n    }\n\n    Provider::OpenResult CachedProvider::open() {\n        clearCache();\n        return {};\n    }\n\n    void CachedProvider::close() {\n        clearCache();\n    }\n\n    void CachedProvider::readRaw(u64 offset, void* buffer, size_t size) {\n        if (!isAvailable() || !isReadable())\n            return;\n\n        auto out = static_cast<u8 *>(buffer);\n        while (size > 0) {\n            const auto blockIndex = calcBlockIndex(offset);\n            const auto blockOffset = calcBlockOffset(offset);\n            const auto toRead = std::min(m_cacheBlockSize - blockOffset, size);\n            const auto cacheSlot = blockIndex % m_maxBlocks;\n\n            {\n                std::shared_lock lock(m_cacheMutex);\n                const auto &slot = m_cache[cacheSlot];\n                if (slot && slot->index == blockIndex) {\n                    std::copy_n(slot->data.begin() + blockOffset, toRead, out);\n\n                    out += toRead;\n                    offset += toRead;\n                    size -= toRead;\n                    continue;\n                }\n            }\n\n            std::vector<uint8_t> blockData(m_cacheBlockSize);\n            readFromSource(blockIndex * m_cacheBlockSize, blockData.data(), m_cacheBlockSize);\n\n            {\n                std::unique_lock lock(m_cacheMutex);\n                m_cache[cacheSlot] = Block{.index=blockIndex, .data=std::move(blockData), .dirty=false};\n                std::copy_n(m_cache[cacheSlot]->data.begin() + blockOffset, toRead, out);\n            }\n\n            out += toRead;\n            offset += toRead;\n            size -= toRead;\n        }\n    }\n\n    void CachedProvider::writeRaw(u64 offset, const void* buffer, size_t size) {\n        if (!isAvailable() || !isWritable())\n            return;\n\n        auto in = static_cast<const u8 *>(buffer);\n        while (size > 0) {\n            const auto blockIndex = calcBlockIndex(offset);\n            const auto blockOffset = calcBlockOffset(offset);\n            const auto toWrite = std::min(m_cacheBlockSize - blockOffset, size);\n            const auto cacheSlot = blockIndex % m_maxBlocks;\n\n            {\n                std::unique_lock lock(m_cacheMutex);\n                auto& slot = m_cache[cacheSlot];\n                if (!slot || slot->index != blockIndex) {\n                    std::vector<uint8_t> blockData(m_cacheBlockSize);\n                    readFromSource(blockIndex * m_cacheBlockSize, blockData.data(), m_cacheBlockSize);\n                    slot = Block { .index=blockIndex, .data=std::move(blockData), .dirty=false };\n                }\n\n                std::copy_n(in, toWrite, slot->data.begin() + blockOffset);\n                slot->dirty = true;\n            }\n\n            writeToSource(offset, in, toWrite);\n\n            in += toWrite;\n            offset += toWrite;\n            size -= toWrite;\n        }\n    }\n\n    void CachedProvider::resizeRaw(u64 newSize) {\n        clearCache();\n\n        resizeSource(newSize);\n    }\n\n\n    u64 CachedProvider::getActualSize() const {\n        if (!isAvailable())\n            return 0;\n\n        if (m_cachedSize == 0) {\n            std::unique_lock lock(m_cacheMutex);\n            m_cachedSize = getSourceSize();\n        }\n\n        return m_cachedSize;\n    }\n\n\n    void CachedProvider::clearCache() {\n        std::unique_lock lock(m_cacheMutex);\n\n        for (auto& slot : m_cache)\n            slot.reset();\n\n        m_cachedSize = 0;\n    }\n\n    void CachedProvider::evictIfNeeded() {\n        if (m_cache.size() < m_maxBlocks)\n            return;\n\n        m_cache.erase(m_cache.begin());\n    }\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/providers/memory_provider.cpp",
    "content": "#include <hex/providers/memory_provider.hpp>\n\n#include <cstring>\n\nnamespace hex::prv {\n\n    Provider::OpenResult MemoryProvider::open() {\n        if (m_data.empty()) {\n            m_data.resize(1);\n        }\n\n        return {};\n    }\n\n    void MemoryProvider::readRaw(u64 offset, void *buffer, size_t size) {\n        auto actualSize = this->getActualSize();\n        if (actualSize == 0 || (offset + size) > actualSize || buffer == nullptr || size == 0)\n            return;\n\n        std::memcpy(buffer, &m_data.front() + offset, size);\n    }\n\n    void MemoryProvider::writeRaw(u64 offset, const void *buffer, size_t size) {\n        if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0)\n            return;\n\n        std::memcpy(&m_data.front() + offset, buffer, size);\n    }\n\n    void MemoryProvider::resizeRaw(u64 newSize) {\n        m_data.resize(newSize);\n    }\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/providers/provider.cpp",
    "content": "#include <hex/providers/provider.hpp>\n\n#include <hex.hpp>\n#include <hex/api/events/events_provider.hpp>\n#include <hex/api/events/events_interaction.hpp>\n\n#include <cmath>\n#include <cstring>\n#include <optional>\n#include <hex/api/content_registry/settings.hpp>\n\n#include <hex/helpers/magic.hpp>\n#include <wolv/io/file.hpp>\n#include <wolv/literals.hpp>\n\n#include <nlohmann/json.hpp>\n#include <wolv/utils/string.hpp>\n\nnamespace hex::prv {\n\n    using namespace wolv::literals;\n\n    namespace {\n\n        u32 s_idCounter = 0;\n\n    }\n\n    IProviderDataBackupable::IProviderDataBackupable(Provider* provider) : m_provider(provider) {\n        m_shouldCreateBackups = ContentRegistry::Settings::read<bool>(\"hex.builtin.setting.general\", \"hex.builtin.setting.general.backups.file_backup.enable\", true);\n        m_maxSize = ContentRegistry::Settings::read<u32>(\"hex.builtin.setting.general\", \"hex.builtin.setting.general.backups.file_backup.max_size\", 1_MiB);\n        m_backupExtension = ContentRegistry::Settings::read<std::string>(\"hex.builtin.setting.general\", \"hex.builtin.setting.general.backups.file_backup.extension\", \".bak\");\n    }\n\n    void IProviderDataBackupable::createBackupIfNeeded(const std::fs::path &inputFilePath) {\n        if (!m_shouldCreateBackups || m_backupCreated)\n            return;\n\n        if (m_provider->getActualSize() > m_maxSize)\n            return;\n\n        const std::fs::path backupFilePath = wolv::util::toUTF8String(inputFilePath) + m_backupExtension;\n        if (wolv::io::fs::copyFile(inputFilePath, backupFilePath, std::fs::copy_options::overwrite_existing)) {\n            if (wolv::io::fs::exists(backupFilePath)) {\n                m_backupCreated = true;\n                log::info(\"Created backup of provider data at '{}'\", backupFilePath.string());\n            }\n        }\n    }\n\n\n    Provider::Provider() : m_undoRedoStack(this), m_id(s_idCounter++) {\n\n    }\n\n    Provider::~Provider() {\n        m_overlays.clear();\n\n        if (auto selection = ImHexApi::HexEditor::getSelection(); selection.has_value() && selection->provider == this)\n            EventRegionSelected::post(ImHexApi::HexEditor::ProviderRegion { { .address=0x00, .size=0x00 }, nullptr });\n    }\n\n    void Provider::read(u64 offset, void *buffer, size_t size, bool overlays) {\n        this->readRaw(offset - this->getBaseAddress(), buffer, size);\n\n        if (overlays)\n            this->applyOverlays(offset, buffer, size);\n    }\n\n    void Provider::write(u64 offset, const void *buffer, size_t size) {\n        if (!this->isWritable())\n            return;\n\n        EventProviderDataModified::post(this, offset, size, static_cast<const u8*>(buffer));\n        this->markDirty();\n    }\n\n    void Provider::save() {\n        if (!this->isWritable())\n            return;\n        \n        EventProviderSaved::post(this);\n    }\n    void Provider::saveAs(const std::fs::path &path) {\n        wolv::io::File file(path, wolv::io::File::Mode::Create);\n\n        if (file.isValid()) {\n            std::vector<u8> buffer(std::min<size_t>(2_MiB, this->getActualSize()), 0x00);\n            size_t bufferSize = 0;\n\n            for (u64 offset = 0; offset < this->getActualSize(); offset += bufferSize) {\n                bufferSize = std::min<size_t>(buffer.size(), this->getActualSize() - offset);\n\n                this->read(this->getBaseAddress() + offset, buffer.data(), bufferSize, true);\n                file.writeBuffer(buffer.data(), bufferSize);\n            }\n\n            EventProviderSaved::post(this);\n        }\n    }\n\n    bool Provider::resize(u64 newSize) {\n        if (newSize >> 63) {\n            log::error(\"new provider size is very large ({}). Is it a negative number ?\", newSize);\n            return false;\n        }\n        i64 difference = newSize - this->getActualSize();\n\n        if (difference > 0)\n            EventProviderDataInserted::post(this, this->getActualSize(), difference);\n        else if (difference < 0)\n            EventProviderDataRemoved::post(this, this->getActualSize() + difference, -difference);\n\n        this->markDirty();\n        return true;\n    }\n\n    void Provider::insert(u64 offset, u64 size) {\n        EventProviderDataInserted::post(this, offset, size);\n\n        this->markDirty();\n    }\n\n    void Provider::remove(u64 offset, u64 size) {\n        EventProviderDataRemoved::post(this, offset, size);\n\n        this->markDirty();\n    }\n\n    void Provider::insertRaw(u64 offset, u64 size) {\n        auto oldSize = this->getActualSize();\n        auto newSize = oldSize + size;\n        this->resizeRaw(newSize);\n\n        std::vector<u8> buffer(0x1000, 0x00);\n\n        auto position = oldSize;\n        while (position > offset) {\n            const auto readSize = std::min<size_t>(position - offset, buffer.size());\n\n            position -= readSize;\n\n            this->readRaw(position, buffer.data(), readSize);\n            this->writeRaw(position + size, buffer.data(), readSize);\n        }\n\n        constexpr static std::array<u8, 0x1000> ZeroBuffer = {};\n        auto zeroPosition = offset;\n        auto remainingZeros = size;\n        while (remainingZeros > 0) {\n            const auto writeSize = std::min<size_t>(remainingZeros, ZeroBuffer.size());\n            this->writeRaw(zeroPosition, ZeroBuffer.data(), writeSize);\n            zeroPosition += writeSize;\n            remainingZeros -= writeSize;\n        }\n    }\n\n    void Provider::removeRaw(u64 offset, u64 size) {\n        if (offset > this->getActualSize() || size == 0)\n            return;\n\n        if ((offset + size) > this->getActualSize())\n            size = this->getActualSize() - offset;\n\n        auto oldSize = this->getActualSize();\n\n        std::vector<u8> buffer(0x1000);\n\n        const auto newSize = oldSize - size;\n        auto position = offset;\n        while (position < newSize) {\n            const auto readSize = std::min<size_t>(newSize - position, buffer.size());\n\n            this->readRaw(position + size, buffer.data(), readSize);\n            this->writeRaw(position, buffer.data(), readSize);\n\n            position += readSize;\n        }\n\n        this->resizeRaw(newSize);\n    }\n\n\n\n    void Provider::applyOverlays(u64 offset, void *buffer, size_t size) const {\n        for (auto &overlay : m_overlays) {\n            auto overlayOffset = overlay->getAddress();\n            auto overlaySize   = overlay->getSize();\n\n            u64 overlapMin = std::max(offset, overlayOffset);\n            u64 overlapMax = std::min(offset + size, overlayOffset + overlaySize);\n            if (overlapMax > overlapMin)\n                std::memcpy(static_cast<u8 *>(buffer) + std::max<u64>(0, overlapMin - offset), overlay->getData().data() + std::max<u64>(0, overlapMin - overlayOffset), overlapMax - overlapMin);\n        }\n    }\n\n    Overlay *Provider::newOverlay() {\n        return m_overlays.emplace_back(std::make_unique<Overlay>()).get();\n    }\n\n    void Provider::deleteOverlay(Overlay *overlay) {\n        m_overlays.remove_if([overlay](const auto &item) {\n            return item.get() == overlay;\n        });\n    }\n\n    const std::list<std::unique_ptr<Overlay>> &Provider::getOverlays() const {\n        return m_overlays;\n    }\n\n\n    u64 Provider::getPageSize() const {\n        return m_pageSize;\n    }\n\n    void Provider::setPageSize(u64 pageSize) {\n        if (pageSize > MaxPageSize)\n            pageSize = MaxPageSize;\n        if (pageSize == 0)\n            return;\n\n        m_pageSize = pageSize;\n    }\n\n    u32 Provider::getPageCount() const {\n        return (this->getActualSize() / this->getPageSize()) + (this->getActualSize() % this->getPageSize() != 0 ? 1 : 0);\n    }\n\n    u32 Provider::getCurrentPage() const {\n        return m_currPage;\n    }\n\n    void Provider::setCurrentPage(u32 page) {\n        if (page < getPageCount())\n            m_currPage = page;\n    }\n\n\n    void Provider::setBaseAddress(u64 address) {\n        m_baseAddress = address;\n        this->markDirty();\n    }\n\n    u64 Provider::getBaseAddress() const {\n        return m_baseAddress;\n    }\n\n    u64 Provider::getCurrentPageAddress() const {\n        return this->getPageSize() * this->getCurrentPage();\n    }\n\n    u64 Provider::getSize() const {\n        return std::min<u64>(this->getActualSize() - this->getPageSize() * m_currPage, this->getPageSize());\n    }\n\n    std::optional<u32> Provider::getPageOfAddress(u64 address) const {\n        u32 page = std::floor((address - this->getBaseAddress()) / double(this->getPageSize()));\n\n        if (page >= this->getPageCount())\n            return std::nullopt;\n\n        return page;\n    }\n\n    void Provider::undo() {\n        m_undoRedoStack.undo();\n    }\n\n    void Provider::redo() {\n        m_undoRedoStack.redo();\n    }\n\n    bool Provider::canUndo() const {\n        return m_undoRedoStack.canUndo();\n    }\n\n    bool Provider::canRedo() const {\n        return m_undoRedoStack.canRedo();\n    }\n\n    nlohmann::json Provider::storeSettings(nlohmann::json settings) const {\n        settings[\"displayName\"] = this->getName();\n        settings[\"type\"]        = this->getTypeName();\n\n        settings[\"baseAddress\"] = m_baseAddress;\n        settings[\"currPage\"]    = m_currPage;\n\n        return settings;\n    }\n\n    void Provider::loadSettings(const nlohmann::json &settings) {\n        m_baseAddress = settings[\"baseAddress\"];\n        m_currPage    = settings[\"currPage\"];\n    }\n\n    std::pair<Region, bool> Provider::getRegionValidity(u64 address) const {\n        u64 absoluteAddress = address - this->getBaseAddress();\n\n        if (absoluteAddress < this->getActualSize())\n            return { Region { .address=this->getBaseAddress() + absoluteAddress, .size=this->getActualSize() - absoluteAddress }, true };\n\n\n        bool insideValidRegion = false;\n\n        std::optional<u64> nextRegionAddress;\n        for (const auto &overlay : m_overlays) {\n            Region overlayRegion = { .address=overlay->getAddress(), .size=overlay->getSize() };\n            if (!nextRegionAddress.has_value() || overlay->getAddress() < nextRegionAddress) {\n                nextRegionAddress = overlayRegion.getStartAddress();\n            }\n\n            if (Region { .address=address, .size=1 }.overlaps(overlayRegion)) {\n                insideValidRegion = true;\n            }\n        }\n\n        if (!nextRegionAddress.has_value())\n            return { Region::Invalid(), false };\n        else\n            return { Region { .address=address, .size=*nextRegionAddress - address }, insideValidRegion };\n    }\n\n\n    u32 Provider::getID() const {\n        return m_id;\n    }\n\n    void Provider::setID(u32 id) {\n        m_id = id;\n        if (id > s_idCounter)\n            s_idCounter = id + 1;\n    }\n\n\n    [[nodiscard]] std::variant<std::string, i128> Provider::queryInformation(const std::string &category, const std::string &) {\n        if (category == \"mime\")\n            return magic::getMIMEType(this);\n        else if (category == \"description\")\n            return magic::getDescription(this);\n        else if (category == \"provider_type\")\n            return this->getTypeName().get();\n        else\n            return 0;\n    }\n\n    [[nodiscard]] bool Provider::isDumpable() const {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/providers/undo/stack.cpp",
    "content": "#include <hex/providers/undo_redo/stack.hpp>\n#include <hex/providers/undo_redo/operations/operation_group.hpp>\n#include <hex/api/events/events_interaction.hpp>\n\n#include <hex/providers/provider.hpp>\n\n#include <wolv/utils/guards.hpp>\n\n#include <atomic>\n\nnamespace hex::prv::undo {\n\n    namespace {\n\n        std::recursive_mutex s_mutex;\n\n    }\n\n    Stack::Stack(Provider *provider) : m_provider(provider) {\n\n    }\n\n    std::recursive_mutex& Stack::getMutex() {\n        return s_mutex;\n    }\n\n\n    void Stack::undo(u32 count) {\n        std::lock_guard lock(s_mutex);\n\n        // If there are no operations, we can't undo anything.\n        if (m_undoStack.empty())\n            return;\n\n        for (u32 i = 0; i < count; i += 1) {\n            // If we reached the start of the list, we can't undo anymore.\n            if (!this->canUndo()) {\n                return;\n            }\n\n            // Move last element from the undo stack to the redo stack\n            m_redoStack.emplace_back(std::move(m_undoStack.back()));\n            m_redoStack.back()->undo(m_provider);\n            m_undoStack.pop_back();\n            EventDataChanged::post(m_provider);\n        }\n    }\n\n    void Stack::redo(u32 count) {\n        std::lock_guard lock(s_mutex);\n\n        // If there are no operations, we can't redo anything.\n        if (m_redoStack.empty())\n            return;\n\n        for (u32 i = 0; i < count; i += 1) {\n            // If we reached the end of the list, we can't redo anymore.\n            if (!this->canRedo()) {\n                return;\n            }\n\n            // Move last element from the undo stack to the redo stack\n            m_undoStack.emplace_back(std::move(m_redoStack.back()));\n            m_undoStack.back()->redo(m_provider);\n            m_redoStack.pop_back();\n            EventDataChanged::post(m_provider);\n        }\n    }\n\n    void Stack::groupOperations(u32 count, const UnlocalizedString &unlocalizedName) {\n        std::lock_guard lock(s_mutex);\n\n        if (count <= 1)\n            return;\n\n        auto operation = std::make_unique<OperationGroup>(unlocalizedName);\n\n        i64 startIndex = std::max<i64>(0, m_undoStack.size() - count);\n\n        // Move operations from our stack to the group in the same order they were added\n        for (u32 i = 0; i < count; i += 1) {\n            i64 index = startIndex + i;\n\n            if (index < 0 || u64(index) >= m_undoStack.size()) {\n                break;\n            }\n\n            m_undoStack[index]->undo(m_provider);\n            operation->addOperation(std::move(m_undoStack[index]));\n        }\n\n        // Remove the empty operations from the stack\n        m_undoStack.resize(startIndex);\n        this->add(std::move(operation));\n    }\n\n    void Stack::apply(const Stack &otherStack) {\n        std::lock_guard lock(s_mutex);\n\n        for (const auto &operation : otherStack.m_undoStack) {\n            this->add(operation->clone());\n        }\n    }\n\n    void Stack::reapply() {\n        std::lock_guard lock(s_mutex);\n\n        for (const auto &operation : m_undoStack) {\n            operation->redo(m_provider);\n            EventDataChanged::post(m_provider);\n        }\n    }\n\n\n\n\n    bool Stack::add(std::unique_ptr<Operation> &&operation) {\n        std::lock_guard lock(s_mutex);\n\n        // Clear the redo stack\n        m_redoStack.clear();\n\n        // Insert the new operation at the end of the list\n        m_undoStack.emplace_back(std::move(operation));\n\n        // Do the operation\n        this->getLastOperation()->redo(m_provider);\n\n        EventDataChanged::post(m_provider);\n\n        return true;\n    }\n\n    bool Stack::canUndo() const {\n        std::lock_guard lock(s_mutex);\n\n        return !m_undoStack.empty();\n    }\n\n    bool Stack::canRedo() const {\n        std::lock_guard lock(s_mutex);\n\n        return !m_redoStack.empty();\n    }\n\n\n\n\n\n}"
  },
  {
    "path": "lib/libimhex/source/subcommands/subcommands.cpp",
    "content": "#include <numeric>\n#include <string_view>\n#include <ranges>\n\n#include \"hex/subcommands/subcommands.hpp\"\n\n#include <hex/api/events/requests_lifecycle.hpp>\n#include <hex/api/plugin_manager.hpp>\n#include <hex/api/imhex_api/messaging.hpp>\n#include <hex/helpers/logger.hpp>\n#include <hex/helpers/fmt.hpp>\n\nnamespace hex::subcommands {\n\n    std::optional<SubCommand> findSubCommand(const std::string &arg) {\n        if (arg == \"-\" || arg == \"--\")\n            return std::nullopt;\n\n        for (auto &plugin : PluginManager::getPlugins()) {\n            for (auto &subCommand : plugin.getSubCommands()) {\n                if (fmt::format(\"--{}\", subCommand.commandLong) == arg || fmt::format(\"-{}\", subCommand.commandShort) == arg) {\n                    return subCommand;\n                }\n            }\n        }\n\n        return std::nullopt;\n    }\n\n    void processArguments(const std::vector<std::string> &args) {\n        // If no arguments, do not even try to process arguments\n        // (important because this function will exit ImHex if an instance is already opened,\n        // and we don't want that if no arguments were provided)\n        if (args.empty())\n            return;\n\n        std::vector<std::pair<SubCommand, std::vector<std::string>>> subCommands;\n\n        auto argsIter = args.begin();\n\n        std::optional<SubCommand> currentSubCommand;\n        std::vector<std::string> currentSubCommandArgs;\n\n        if (*argsIter == \"--\") {\n            // Treat the rest of the args as files\n            ++argsIter;\n            std::vector<std::string> remainingArgs(argsIter, args.end());\n            subCommands.emplace_back(*findSubCommand(\"--open\"), remainingArgs);\n            argsIter = args.end(); // Skip while loop\n        } else if (!findSubCommand(*argsIter)) {\n            // First argument not a subcommand, treat all args as files\n            subCommands.emplace_back(*findSubCommand(\"--open\"), args);\n            argsIter = args.end(); // Skip while loop\n        }\n\n        while (argsIter != args.end()) {\n            // Get subcommand associated with the argument\n            // Guaranteed to find a match on the first argument, as the other case has been handled above\n            const auto newSubCommand = findSubCommand(*argsIter);\n            if (*argsIter == \"--\") {\n                // Treat the rest of the args as files\n                subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);\n\n                ++argsIter;\n                std::vector<std::string> remainingArgs(argsIter, args.end());\n\n                subCommands.emplace_back(*findSubCommand(\"--open\"), remainingArgs);\n\n                currentSubCommand = std::nullopt;\n                currentSubCommandArgs.clear();\n                break;\n            }\n\n            // Will always take this `if` statement on the first time through the loop\n            if (newSubCommand.has_value()) {\n                if (currentSubCommand.has_value()) {\n                    subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);\n                }\n\n                if (newSubCommand->type == SubCommand::Type::SubCommand) {\n                    ++argsIter;\n                    std::vector<std::string> remainingArgs(argsIter, args.end());\n                    subCommands.emplace_back(*newSubCommand, remainingArgs);\n\n                    currentSubCommand = std::nullopt;\n                    currentSubCommandArgs.clear();\n                    break;\n                }\n\n                currentSubCommand = newSubCommand;\n                currentSubCommandArgs.clear();\n            } else {\n                currentSubCommandArgs.push_back(*argsIter);\n            }\n\n            ++argsIter;\n        }\n\n        // Save last command to run\n        if (currentSubCommand.has_value()) {\n            subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);\n        }\n\n        // Run the subcommands\n        for (const auto &[subcommand, subCommandArgs] : subCommands) {\n            subcommand.callback(subCommandArgs);\n        }\n\n        // Exit the process if it's not the main instance (the commands have been forwarded to another instance)\n        if (!ImHexApi::System::isMainInstance()) {\n            std::exit(0);\n        }\n    }\n\n    void forwardSubCommand(const std::string &cmdName, const std::vector<std::string> &args) {\n        log::debug(\"Forwarding subcommand {} (maybe to us)\", cmdName);\n\n        std::vector<u8> data;\n        if (!args.empty()) {\n            for (const auto &arg: args) {\n                data.insert(data.end(), arg.begin(), arg.end());\n                data.push_back('\\0');\n            }\n\n            data.pop_back();\n        }\n\n        SendMessageToMainInstance::post(fmt::format(\"command/{}\", cmdName), data);\n    }\n\n    void registerSubCommand(const std::string &cmdName, const ForwardCommandHandler &handler) {\n        log::debug(\"Registered new forward command handler: {}\", cmdName);\n\n        ImHexApi::Messaging::registerHandler(fmt::format(\"command/{}\", cmdName), [handler](const std::vector<u8> &eventData){\n            std::string string(reinterpret_cast<const char *>(eventData.data()), eventData.size());\n\n            std::vector<std::string> args;\n\n            for (const auto &argument : std::views::split(string, char(0x00))) {\n                std::string arg(argument.data(), argument.size());\n                args.push_back(arg);\n            }\n\n            handler(args);\n        });\n    }\n}\n"
  },
  {
    "path": "lib/libimhex/source/test/tests.cpp",
    "content": "#include <hex/test/tests.hpp>\n\nnamespace hex::test {\n\n    static std::map<std::string, Test> s_tests;\n    int Tests::addTest(const std::string &name, Function func, bool shouldFail) noexcept {\n        s_tests.insert({\n            name, { .function=func, .shouldFail=shouldFail }\n        });\n\n        return 0;\n    }\n\n    std::map<std::string, Test> &Tests::get() noexcept {\n        return s_tests;\n    }\n\n    bool initPluginImpl(std::string name) {\n        if (name != \"Built-in\") {\n            if(!initPluginImpl(\"Built-in\")) return false;\n        }\n\n        const auto *plugin = hex::PluginManager::getPlugin(name);\n        if (plugin == nullptr) {\n            hex::log::fatal(\"Plugin '{}' was not found !\", name);\n            return false;\n        }else if (!plugin->initializePlugin()) {\n            hex::log::fatal(\"Failed to initialize plugin '{}' !\", name);\n            return false;\n        }\n\n        hex::log::info(\"Initialized plugin '{}' successfully\", name);\n        return true;\n    }\n}"
  },
  {
    "path": "lib/libimhex/source/ui/banner.cpp",
    "content": "#include <hex/ui/banner.hpp>\n#include <hex/helpers/auto_reset.hpp>\n\nnamespace hex::impl {\n\n    [[nodiscard]] std::list<std::unique_ptr<BannerBase>> &BannerBase::getOpenBanners() {\n        static AutoReset<std::list<std::unique_ptr<BannerBase>>> openBanners;\n\n        return openBanners;\n    }\n\n    std::mutex& BannerBase::getMutex() {\n        static std::mutex mutex;\n\n        return mutex;\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/ui/imgui_imhex_extensions.cpp",
    "content": "#include <hex/ui/imgui_imhex_extensions.h>\n\n#include <imgui.h>\n#include <imgui_internal.h>\n#include <implot.h>\n#include <implot_internal.h>\n#include <cimgui.h>\n#include <opengl_support.h>\n\n#undef IMGUI_DEFINE_MATH_OPERATORS\n\n#define STB_IMAGE_IMPLEMENTATION\n#include <stb_image.h>\n\n#include <lunasvg.h>\n\n#include <set>\n#include <string>\n#include <algorithm>\n#include <numbers>\n\n#include <hex/api/imhex_api/system.hpp>\n\n#include <hex/api/task_manager.hpp>\n#include <hex/api/theme_manager.hpp>\n#include <hex/helpers/logger.hpp>\n\nnamespace ImGuiExt {\n\n    using namespace ImGui;\n\n    namespace {\n\n        bool isOpenGLExtensionSupported(const char *name) {\n            static std::set<std::string> extensions;\n\n            if (extensions.empty()) {\n                GLint extensionCount = 0;\n                glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount);\n\n                for (GLint i = 0; i < extensionCount; i++) {\n                    std::string extension = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i));\n                    extensions.emplace(std::move(extension));\n                }\n            }\n\n            return extensions.contains(name);\n        }\n\n        bool isOpenGLVersionAtLeast(u8 major, u8 minor) {\n            static GLint actualMajor = 0, actualMinor = 0;\n            if (actualMajor == 0 || actualMinor == 0) {\n                glGetIntegerv(GL_MAJOR_VERSION, &actualMajor);\n                glGetIntegerv(GL_MINOR_VERSION, &actualMinor);\n            }\n\n            return actualMajor > major || (actualMajor == major && actualMinor >= minor);\n        }\n\n        constexpr auto getGLFilter(Texture::Filter filter) {\n            switch (filter) {\n                using enum Texture::Filter;\n                case Nearest:\n                    return GL_NEAREST;\n                case Linear:\n                    return GL_LINEAR;\n            }\n\n            return GL_NEAREST;\n        }\n\n        [[maybe_unused]] GLint getMaxSamples(GLenum target, GLenum format) {\n            GLint maxSamples;\n\n            glGetInternalformativ(target, format, GL_SAMPLES, 1, &maxSamples);\n            return maxSamples;\n        }\n\n        GLuint createTextureFromRGBA8Array(const ImU8 *buffer, int width, int height, Texture::Filter filter) {\n            GLuint texture;\n\n            // Generate texture\n            glGenTextures(1, &texture);\n            glBindTexture(GL_TEXTURE_2D, texture);\n\n            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, getGLFilter(filter));\n            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, getGLFilter(filter));\n\n            #if defined(GL_UNPACK_ROW_LENGTH)\n                glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);\n            #endif\n\n            // Allocate storage for the texture\n            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);\n\n            return texture;\n        }\n\n        GLuint createMultisampleTextureFromRGBA8Array(const ImU8 *buffer, int width, int height, Texture::Filter filter) {\n            // Create a regular texture from the RGBA8 array\n            GLuint texture = createTextureFromRGBA8Array(buffer, width, height, filter);\n\n            if (filter == Texture::Filter::Nearest) {\n                return texture;\n            }\n\n            if (!isOpenGLVersionAtLeast(3,2)) {\n                return texture;\n            }\n\n            if (!isOpenGLExtensionSupported(\"GL_ARB_texture_multisample\")) {\n                return texture;\n            }\n\n            #if defined(GL_TEXTURE_2D_MULTISAMPLE)\n                static const auto sampleCount = std::min(static_cast<GLint>(8), getMaxSamples(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8));\n\n                // Generate renderbuffer\n                GLuint renderbuffer;\n                glGenRenderbuffers(1, &renderbuffer);\n                glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);\n                glRenderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_DEPTH24_STENCIL8, width, height);\n\n                // Generate framebuffer\n                GLuint framebuffer;\n                glGenFramebuffers(1, &framebuffer);\n                glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);\n\n                // Unbind framebuffer on exit\n                ON_SCOPE_EXIT {\n                    glBindFramebuffer(GL_FRAMEBUFFER, 0);\n                };\n\n                // Attach texture to color attachment 0\n                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, texture, 0);\n\n                // Attach renderbuffer to depth-stencil attachment\n                glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);\n\n                // Check framebuffer status\n                if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {\n                    hex::log::error(\"Driver claims to support texture multisampling but it's not working\");\n                    return texture;\n                }\n\n            #endif\n\n            return texture;\n        }\n\n    }\n    \n    Texture Texture::fromImage(const ImU8 *buffer, int size, Filter filter) {\n        if (size == 0)\n            return {};\n\n        unsigned char *imageData = nullptr;\n\n        Texture result;\n        imageData = stbi_load_from_memory(buffer, size, &result.m_width, &result.m_height, nullptr, 4);\n        if (imageData == nullptr)\n            return {};\n\n        GLuint texture = createMultisampleTextureFromRGBA8Array(imageData, result.m_width, result.m_height, filter);\n\n        STBI_FREE(imageData);\n\n        result.m_textureId = texture;\n\n        return result;\n    }\n\n    Texture Texture::fromImage(std::span<const std::byte> buffer, Filter filter) {\n        return Texture::fromImage(reinterpret_cast<const ImU8*>(buffer.data()), buffer.size(), filter);\n    }\n\n\n    Texture Texture::fromImage(const std::fs::path &path, Filter filter) {\n        return Texture::fromImage(wolv::util::toUTF8String(path).c_str(), filter);\n    }\n\n    Texture Texture::fromImage(const char *path, Filter filter) {\n        Texture result;\n        unsigned char *imageData = stbi_load(path, &result.m_width, &result.m_height, nullptr, 4);\n        if (imageData == nullptr)\n            return {};\n\n        GLuint texture = createMultisampleTextureFromRGBA8Array(imageData, result.m_width, result.m_height, filter);\n\n        STBI_FREE(imageData);\n\n        result.m_textureId = texture;\n\n        return result;\n    }\n\n    Texture Texture::fromGLTexture(unsigned int glTexture, int width, int height) {\n        Texture texture;\n        texture.m_textureId = glTexture;\n        texture.m_width = width;\n        texture.m_height = height;\n\n        return texture;\n    }\n\n    Texture Texture::fromBitmap(std::span<const std::byte> buffer, int width, int height, Filter filter) {\n        return Texture::fromBitmap(reinterpret_cast<const ImU8*>(buffer.data()), buffer.size(), width, height, filter);\n    }\n\n    Texture Texture::fromBitmap(const ImU8 *buffer, int size, int width, int height, Filter filter) {\n        if (width * height * 4 > size)\n            return {};\n\n        GLuint texture = createMultisampleTextureFromRGBA8Array(buffer, width, height, filter);\n\n        Texture result;\n        result.m_width = width;\n        result.m_height = height;\n        result.m_textureId = texture;\n\n        return result;\n    }\n\n    Texture Texture::fromSVG(const char *path, int width, int height, Filter filter) {\n        const auto scaleFactor = hex::ImHexApi::System::getBackingScaleFactor();\n\n        auto document = lunasvg::Document::loadFromFile(path);\n        auto bitmap = document->renderToBitmap(width * scaleFactor, height * scaleFactor);\n\n        auto texture = createMultisampleTextureFromRGBA8Array(bitmap.data(), bitmap.width(), bitmap.height(), filter);\n\n        Texture result;\n        result.m_width = bitmap.width() / scaleFactor;\n        result.m_height = bitmap.height() / scaleFactor;\n        result.m_textureId = texture;\n\n        return result;\n    }\n\n    Texture Texture::fromSVG(const std::fs::path &path, int width, int height, Filter filter) {\n        return Texture::fromSVG(wolv::util::toUTF8String(path).c_str(), width, height, filter);\n    }\n\n    Texture Texture::fromSVG(std::span<const std::byte> buffer, int width, int height, Filter filter) {\n        const auto scaleFactor = hex::ImHexApi::System::getBackingScaleFactor();\n\n        auto document = lunasvg::Document::loadFromData(reinterpret_cast<const char*>(buffer.data()), buffer.size());\n        auto bitmap = document->renderToBitmap(width * scaleFactor, height * scaleFactor);\n        bitmap.convertToRGBA();\n\n        auto texture = createMultisampleTextureFromRGBA8Array(bitmap.data(), bitmap.width(), bitmap.height(), filter);\n\n        Texture result;\n        result.m_width = bitmap.width() / scaleFactor;\n        result.m_height = bitmap.height() / scaleFactor;\n        result.m_textureId = texture;\n\n        return result;\n    }\n\n    Texture::Texture(Texture&& other) noexcept {\n        m_textureId = other.m_textureId;\n        m_width = other.m_width;\n        m_height = other.m_height;\n\n        other.m_textureId = 0;\n    }\n\n    Texture& Texture::operator=(Texture&& other) noexcept {\n        if (this == &other)\n            return *this;\n\n        this->reset();\n\n        m_textureId = other.m_textureId;\n        m_width = other.m_width;\n        m_height = other.m_height;\n\n        other.m_textureId = 0;\n        \n        return *this;\n    }\n\n    Texture::~Texture() {\n        this->reset();\n    }\n\n    void Texture::reset() {\n        if (m_textureId != 0) {\n            #if !defined(OS_WEB)\n                if (glDeleteTextures != nullptr)\n                    glDeleteTextures(1, reinterpret_cast<GLuint*>(&m_textureId));\n            #endif\n            m_textureId = 0;\n        }\n    }\n\n    std::vector<u8> Texture::toBytes() const noexcept {\n        std::vector<u8> result(m_width * m_height * 4);\n\n        #if defined(OS_WEB)\n            GLuint fbo;\n            glGenFramebuffers(1, &fbo);\n            glBindFramebuffer(GL_FRAMEBUFFER, fbo);\n            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_textureId, 0);\n\n            glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, result.data());\n\n            glBindFramebuffer(GL_FRAMEBUFFER, 0);\n            glDeleteFramebuffers(1, &fbo);\n        #else\n            glBindTexture(GL_TEXTURE_2D, m_textureId);\n            glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, result.data());\n            glBindTexture(GL_TEXTURE_2D, 0);\n        #endif\n\n        return result;\n    }\n\n    float GetTextWrapPos() {\n        return GImGui->CurrentWindow->DC.TextWrapPos;\n    }\n\n    int UpdateStringSizeCallback(ImGuiInputTextCallbackData *data) {\n        if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {\n            auto &string = *static_cast<std::string *>(data->UserData);\n\n            string.resize(data->BufTextLen);\n            data->Buf = string.data();\n        }\n\n        return 0;\n    }\n\n    bool IconHyperlink(const char *icon, const char *label, const ImVec2 &size_arg, ImGuiButtonFlags flags) {\n        ImGuiWindow *window = GetCurrentWindow();\n        if (window->SkipItems)\n            return false;\n\n        ImGuiContext &g         = *GImGui;\n        const ImGuiID id        = window->GetID(label);\n        ImVec2 label_size       = CalcTextSize(icon, nullptr, false);\n        label_size.x += CalcTextSize(\" \", nullptr, false).x + CalcTextSize(label, nullptr, false).x;\n\n        ImVec2 pos  = window->DC.CursorPos;\n        ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y);\n\n        const ImRect bb(pos, pos + size);\n        ItemAdd(bb, id);\n\n        bool hovered, held;\n        bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n        // Render\n        BeginGroup();\n        TextDisabled(\"%s\", icon);\n        SameLine();\n\n        const ImU32 col = hovered ? GetColorU32(ImGuiCol_ButtonHovered) : GetColorU32(ImGuiCol_ButtonActive);\n        PushStyleColor(ImGuiCol_Text, ImU32(col));\n        TextUnformatted(label);\n\n        if (hovered)\n            GetWindowDrawList()->AddLine(ImVec2(pos.x, pos.y + size.y), pos + size, ImU32(col));\n\n        PopStyleColor();\n\n        EndGroup();\n\n        IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n        return pressed;\n    }\n\n    bool Hyperlink(const char *label, const ImVec2 &size_arg, ImGuiButtonFlags flags) {\n        ImGuiWindow *window = GetCurrentWindow();\n\n        ImGuiContext &g         = *GImGui;\n        const ImGuiID id        = window->GetID(label);\n        const ImVec2 label_size = CalcTextSize(label, nullptr, true);\n\n        ImVec2 pos  = window->DC.CursorPos;\n        ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y);\n\n        const ImRect bb(pos, pos + size);\n        ItemAdd(bb, id);\n\n        bool hovered, held;\n        bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n        // Render\n        const ImU32 col = hovered ? GetColorU32(ImGuiCol_ButtonHovered) : GetColorU32(ImGuiCol_ButtonActive);\n        PushStyleColor(ImGuiCol_Text, ImU32(col));\n        TextEx(label, nullptr, ImGuiTextFlags_NoWidthForLargeClippedText);    // Skip formatting\n\n        if (hovered)\n            GetWindowDrawList()->AddLine(ImVec2(pos.x, pos.y + size.y), pos + size, ImU32(col));\n\n        PopStyleColor();\n\n        IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n        return pressed;\n    }\n\n    bool BulletHyperlink(const char *label, const ImVec2 &size_arg, ImGuiButtonFlags flags) {\n        ImGuiWindow *window = GetCurrentWindow();\n        if (window->SkipItems)\n            return false;\n\n        ImGuiContext &g         = *GImGui;\n        const ImGuiStyle &style = g.Style;\n        const ImGuiID id        = window->GetID(label);\n        const ImVec2 label_size = CalcTextSize(label, nullptr, true);\n\n        ImVec2 pos  = window->DC.CursorPos;\n        ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y) + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0F);\n\n        const ImRect bb(pos, pos + size);\n        ItemSize(size, 0);\n        if (!ItemAdd(bb, id))\n            return false;\n\n        bool hovered, held;\n        bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n        // Render\n        const ImU32 col = hovered ? GetColorU32(ImGuiCol_ButtonHovered) : GetColorU32(ImGuiCol_ButtonActive);\n        PushStyleColor(ImGuiCol_Text, ImU32(col));\n        RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x, g.FontSize * 0.5F), col);\n        RenderText(bb.Min + ImVec2(g.FontSize * 0.5 + style.FramePadding.x, 0.0F), label, nullptr, false);\n        GetWindowDrawList()->AddLine(bb.Min + ImVec2(g.FontSize * 0.5 + style.FramePadding.x, size.y), pos + size - ImVec2(g.FontSize * 0.5 + style.FramePadding.x, 0), ImU32(col));\n        PopStyleColor();\n\n        IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n        return pressed;\n    }\n\n    bool DescriptionButton(const char *label, const char *description, const char *icon, const ImVec2 &size_arg, ImGuiButtonFlags flags) {\n        ImGuiWindow *window = GetCurrentWindow();\n        if (window->SkipItems)\n            return false;\n\n        ImGuiContext &g         = *GImGui;\n        const ImGuiStyle &style = g.Style;\n        const ImGuiID id        = window->GetID(label);\n        const ImVec2 label_size = CalcTextSize(label, nullptr, true);\n        const ImVec2 text_size  = label_size + CalcTextSize(description, nullptr, true);\n\n        ImVec2 pos = window->DC.CursorPos;\n        if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset)    // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)\n            pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y;\n        ImVec2 size = CalcItemSize(size_arg, text_size.x + style.FramePadding.x * 4.0F, text_size.y + style.FramePadding.y * 4.0F);\n\n        const ImRect bb(pos, pos + size);\n        ItemSize(size, style.FramePadding.y);\n        if (!ItemAdd(bb, id))\n            return false;\n\n        bool hovered, held;\n        bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n        PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0, 0.5));\n        PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);\n\n        // Render\n        const ImU32 col = GetCustomColorU32((held && hovered) ? ImGuiCustomCol_DescButtonActive : hovered ? ImGuiCustomCol_DescButtonHovered\n                                                                                                          : ImGuiCustomCol_DescButton);\n        float icon_padding = style.FramePadding.x * 2;\n        float label_padding = icon_padding + style.FramePadding.x * 5;\n        float description_padding = label_padding + style.FramePadding.x * 2;\n\n        RenderNavCursor(bb, id);\n        RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);\n        PushFont(GImGui->Font, GImGui->FontSizeBase * 1.25F);\n        const ImVec2 icon_size  = CalcTextSize(icon, nullptr, true);\n        RenderTextClipped(bb.Min + ImVec2(icon_padding, (size.y - icon_size.y) / 2), bb.Max - style.FramePadding, icon, nullptr, nullptr);\n        PopFont();\n        PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive));\n        RenderTextClipped(bb.Min + style.FramePadding * 2 + ImVec2(label_padding, 0), bb.Max - style.FramePadding, label, nullptr, nullptr);\n        PopStyleColor();\n        PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text));\n        auto clipBb = bb;\n        clipBb.Max.x -= style.FramePadding.x;\n        RenderTextClipped(bb.Min + style.FramePadding * 2 + ImVec2(description_padding, label_size.y), bb.Max - style.FramePadding, description, nullptr, &text_size, style.ButtonTextAlign, &clipBb);\n        PopStyleColor();\n\n        PopStyleVar(2);\n\n        // Automatically close popups\n        // if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))\n        //    CloseCurrentPopup();\n\n        IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n        return pressed;\n    }\n\n    bool DescriptionButtonProgress(const char *label, const char *description, const char *icon, float fraction, const ImVec2 &size_arg, ImGuiButtonFlags flags) {\n        ImGuiWindow *window = GetCurrentWindow();\n        if (window->SkipItems)\n            return false;\n\n        ImGuiContext &g         = *GImGui;\n        const ImGuiStyle &style = g.Style;\n        const ImGuiID id        = window->GetID(label);\n        const ImVec2 text_size  = CalcTextSize(fmt::format(\"{}\\n{}\", label, description).c_str(), nullptr, true);\n        const ImVec2 label_size = CalcTextSize(label, nullptr, true);\n\n        ImVec2 pos = window->DC.CursorPos;\n        if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset)    // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)\n            pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y;\n        ImVec2 size = CalcItemSize(size_arg, text_size.x + style.FramePadding.x * 4.0F, text_size.y + style.FramePadding.y * 6.0F);\n\n        const ImRect bb(pos, pos + size);\n        ItemSize(size, style.FramePadding.y);\n        if (!ItemAdd(bb, id))\n            return false;\n\n        bool hovered, held;\n        bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n        PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0, 0.5));\n        PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);\n\n        // Render\n        const ImU32 col = GetCustomColorU32((held && hovered) ? ImGuiCustomCol_DescButtonActive : hovered ? ImGuiCustomCol_DescButtonHovered\n                                                                                                          : ImGuiCustomCol_DescButton);\n        float icon_padding = style.FramePadding.x * 2;\n        float label_padding = icon_padding + style.FramePadding.x * 5;\n        float description_padding = label_padding + style.FramePadding.x * 2;\n\n        RenderNavCursor(bb, id);\n        RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);\n        PushFont(GImGui->Font, GImGui->FontSizeBase * 1.25F);\n        const ImVec2 icon_size  = CalcTextSize(icon, nullptr, true);\n        RenderTextClipped(bb.Min + ImVec2(icon_padding, (size.y - icon_size.y) / 2), bb.Max - style.FramePadding, icon, nullptr, nullptr);\n        PopFont();\n        PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive));\n        RenderTextClipped(bb.Min + style.FramePadding * 2 + ImVec2(label_padding, 0), bb.Max - style.FramePadding, label, nullptr, nullptr);\n        PopStyleColor();\n        PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text));\n        auto clipBb = bb;\n        clipBb.Max.x -= style.FramePadding.x;\n        RenderTextClipped(bb.Min + style.FramePadding * 2 + ImVec2(description_padding, label_size.y), bb.Max - style.FramePadding, description, nullptr, &text_size, style.ButtonTextAlign, &clipBb);\n        PopStyleColor();\n\n        RenderFrame(ImVec2(bb.Min.x, bb.Max.y - 5 * hex::ImHexApi::System::getGlobalScale()), bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), false, style.FrameRounding);\n        RenderFrame(ImVec2(bb.Min.x, bb.Max.y - 5 * hex::ImHexApi::System::getGlobalScale()), ImVec2(bb.Min.x + fraction * bb.GetSize().x, bb.Max.y), GetColorU32(ImGuiCol_Button), false, style.FrameRounding);\n        RenderFrame(bb.Min, bb.Max, 0x00, true, style.FrameRounding);\n\n        PopStyleVar(2);\n\n        // Automatically close popups\n        // if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))\n        //    CloseCurrentPopup();\n\n        IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n        return pressed;\n    }\n\n    void HelpHover(const char *text, const char *icon, ImU32 iconColor) {\n        PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));\n        PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));\n        PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0));\n        PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));\n        PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F);\n\n        PushStyleColor(ImGuiCol_Text, iconColor);\n        ImGui::PushID(text);\n        Button(icon, ImVec2(0, ImGui::GetTextLineHeightWithSpacing()));\n        ImGui::PopID();\n        PopStyleColor();\n\n        if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {\n            SetNextWindowSizeConstraints(ImVec2(GetTextLineHeight() * 25, 0), ImVec2(GetTextLineHeight() * 35, FLT_MAX));\n            BeginTooltip();\n            TextFormattedWrapped(\"{}\", text);\n            EndTooltip();\n        }\n\n        PopStyleVar(2);\n        PopStyleColor(3);\n    }\n\n    void UnderlinedText(const char *label, ImColor color, const ImVec2 &size_arg) {\n        ImGuiWindow *window = GetCurrentWindow();\n\n        const ImVec2 label_size = CalcTextSize(label, nullptr, true);\n\n        ImVec2 pos  = window->DC.CursorPos;\n        ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y);\n\n        PushStyleColor(ImGuiCol_Text, ImU32(color));\n        TextEx(label, nullptr, ImGuiTextFlags_NoWidthForLargeClippedText);    // Skip formatting\n        GetWindowDrawList()->AddLine(ImVec2(pos.x, pos.y + size.y), pos + size, ImU32(color));\n        PopStyleColor();\n    }\n\n    void UnderwavedText(const char *label, ImColor textColor, ImColor lineColor, const ImVec2 &size_arg) {\n        ImGuiWindow *window = GetCurrentWindow();\n        std::string labelStr(label);\n        for (char letter : labelStr) {\n            std::string letterStr(1, letter);\n            const ImVec2 label_size = CalcTextSize(letterStr.c_str(), nullptr, true);\n            ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y);\n            ImVec2 pos = window->DC.CursorPos;\n            float lineWidth = size.x / 3.0f;\n            float halfLineW = lineWidth / 2.0f;\n            float lineY = pos.y + size.y;\n            ImVec2 initial = ImVec2(pos.x, lineY);\n            ImVec2 pos1 = ImVec2(pos.x + lineWidth, lineY - 2.0f);\n            ImVec2 pos2 = ImVec2(pos.x + lineWidth + halfLineW, lineY);\n            ImVec2 pos3 = ImVec2(pos.x + lineWidth * 2 + halfLineW, lineY - 2.0f);\n            ImVec2 pos4 = ImVec2(pos.x + lineWidth * 3, lineY - 1.0f);\n\n            PushStyleColor(ImGuiCol_Text, ImU32(textColor));\n            TextEx(letterStr.c_str(), nullptr, ImGuiTextFlags_NoWidthForLargeClippedText);    // Skip formatting\n            GetWindowDrawList()->AddLine(initial, pos1, ImU32(lineColor),0.4f);\n            GetWindowDrawList()->AddLine(pos1, pos2, ImU32(lineColor),0.3f);\n            GetWindowDrawList()->AddLine(pos2, pos3, ImU32(lineColor),0.4f);\n            GetWindowDrawList()->AddLine(pos3, pos4, ImU32(lineColor),0.3f);\n            PopStyleColor();\n            window->DC.CursorPos = ImVec2(pos.x + size.x, pos.y);\n        }\n    }\n\n    void TextSpinner(const char *label) {\n        Text(\"[%c] %s\", \"|/-\\\\\"[ImU32(GetTime() * 20) % 4], label);\n    }\n\n    void Header(const char *label, bool firstEntry) {\n        if (!firstEntry)\n            NewLine();\n        SeparatorText(label);\n    }\n\n    void HeaderColored(const char *label, ImColor color, bool firstEntry) {\n        if (!firstEntry)\n            NewLine();\n        TextFormattedColored(color, \"{}\", label);\n        Separator();\n    }\n\n    bool InfoTooltip(const char *text, bool isSeparator) {\n        static double lastMoveTime;\n        static ImGuiID lastHoveredID;\n\n        double currTime   = GetTime();\n        ImGuiID hoveredID = GetHoveredID();\n\n        bool result = false;\n        if (IsItemHovered(ImGuiHoveredFlags_DelayNormal) && (currTime - lastMoveTime) >= 0.5 && hoveredID == lastHoveredID) {\n            if (!std::string_view(text).empty()) {\n                const auto textWidth = CalcTextSize(text).x;\n\n                const auto maxWidth = 300 * hex::ImHexApi::System::getGlobalScale();\n                const bool wrapping = textWidth > maxWidth;\n\n                if (wrapping)\n                    ImGui::SetNextWindowSizeConstraints(ImVec2(maxWidth, 0), ImVec2(maxWidth, FLT_MAX));\n                else\n                    ImGui::SetNextWindowSize(ImVec2(textWidth + GetStyle().WindowPadding.x * 2, 0));\n\n                if (BeginTooltip()) {\n                    if (isSeparator)\n                        SeparatorText(text);\n                    else {\n                        if (wrapping)\n                            TextFormattedWrapped(\"{}\", text);\n                        else\n                            TextFormatted(\"{}\", text);\n                    }\n\n                    EndTooltip();\n                }\n            }\n\n            result = true;\n        }\n\n        if (hoveredID != lastHoveredID) {\n            lastMoveTime = currTime;\n        }\n        lastHoveredID = hoveredID;\n\n        return result;\n    }\n\n    ImU32 GetCustomColorU32(ImGuiCustomCol idx, float alpha_mul) {\n        auto &customData = *static_cast<ImHexCustomData *>(GImGui->IO.UserData);\n        ImVec4 c         = customData.Colors[idx];\n        c.w *= GImGui->Style.Alpha * alpha_mul;\n        return ColorConvertFloat4ToU32(c);\n    }\n\n    ImVec4 GetCustomColorVec4(ImGuiCustomCol idx, float alpha_mul) {\n        auto &customData = *static_cast<ImHexCustomData *>(GImGui->IO.UserData);\n        ImVec4 c         = customData.Colors[idx];\n        c.w *= GImGui->Style.Alpha * alpha_mul;\n        return c;\n    }\n\n    float GetCustomStyleFloat(ImGuiCustomStyle idx) {\n        auto &customData = *static_cast<ImHexCustomData *>(GImGui->IO.UserData);\n\n        switch (idx) {\n            case ImGuiCustomStyle_WindowBlur:\n                return customData.styles.WindowBlur;\n            default:\n                return 0.0F;\n        }\n    }\n\n    ImVec2 GetCustomStyleVec2(ImGuiCustomStyle idx) {\n        std::ignore = idx;\n        return {};\n    }\n\n    void StyleCustomColorsDark() {\n        auto &colors = static_cast<ImHexCustomData *>(GImGui->IO.UserData)->Colors;\n\n        colors[ImGuiCustomCol_DescButton]        = ImColor(20, 20, 20);\n        colors[ImGuiCustomCol_DescButtonHovered] = ImColor(40, 40, 40);\n        colors[ImGuiCustomCol_DescButtonActive]  = ImColor(60, 60, 60);\n\n        colors[ImGuiCustomCol_ToolbarGray]   = ImColor(230, 230, 230);\n        colors[ImGuiCustomCol_ToolbarRed]    = ImColor(231, 76, 60);\n        colors[ImGuiCustomCol_ToolbarYellow] = ImColor(241, 196, 15);\n        colors[ImGuiCustomCol_ToolbarGreen]  = ImColor(56, 139, 66);\n        colors[ImGuiCustomCol_ToolbarBlue]   = ImColor(6, 83, 155);\n        colors[ImGuiCustomCol_ToolbarPurple] = ImColor(103, 42, 120);\n        colors[ImGuiCustomCol_ToolbarBrown]  = ImColor(219, 179, 119);\n\n        colors[ImGuiCustomCol_Highlight] = ImColor(77, 198, 155);\n\n        colors[ImGuiCustomCol_IEEEToolSign] = ImColor(93, 93, 127);\n        colors[ImGuiCustomCol_IEEEToolExp]  = ImColor(93, 127,  93);\n        colors[ImGuiCustomCol_IEEEToolMantissa]  = ImColor(127, 93,  93);\n    }\n\n    void StyleCustomColorsLight() {\n        auto &colors = static_cast<ImHexCustomData *>(GImGui->IO.UserData)->Colors;\n\n        colors[ImGuiCustomCol_DescButton]        = ImColor(230, 230, 230);\n        colors[ImGuiCustomCol_DescButtonHovered] = ImColor(210, 210, 210);\n        colors[ImGuiCustomCol_DescButtonActive]  = ImColor(190, 190, 190);\n\n        colors[ImGuiCustomCol_ToolbarGray]   = ImColor(25, 25, 25);\n        colors[ImGuiCustomCol_ToolbarRed]    = ImColor(231, 76, 60);\n        colors[ImGuiCustomCol_ToolbarYellow] = ImColor(241, 196, 15);\n        colors[ImGuiCustomCol_ToolbarGreen]  = ImColor(56, 139, 66);\n        colors[ImGuiCustomCol_ToolbarBlue]   = ImColor(6, 83, 155);\n        colors[ImGuiCustomCol_ToolbarPurple] = ImColor(103, 42, 120);\n        colors[ImGuiCustomCol_ToolbarBrown]  = ImColor(219, 179, 119);\n\n        colors[ImGuiCustomCol_Highlight] = ImColor(41, 151, 112);\n\n        colors[ImGuiCustomCol_IEEEToolSign] = ImColor(187, 187, 255);\n        colors[ImGuiCustomCol_IEEEToolExp]  = ImColor(187, 255,  187);\n        colors[ImGuiCustomCol_IEEEToolMantissa]  = ImColor(255, 187,187);\n    }\n\n    void StyleCustomColorsClassic() {\n        auto &colors = static_cast<ImHexCustomData *>(GImGui->IO.UserData)->Colors;\n\n        colors[ImGuiCustomCol_DescButton]        = ImColor(40, 40, 80);\n        colors[ImGuiCustomCol_DescButtonHovered] = ImColor(60, 60, 100);\n        colors[ImGuiCustomCol_DescButtonActive]  = ImColor(80, 80, 120);\n\n        colors[ImGuiCustomCol_ToolbarGray]   = ImColor(230, 230, 230);\n        colors[ImGuiCustomCol_ToolbarRed]    = ImColor(231, 76, 60);\n        colors[ImGuiCustomCol_ToolbarYellow] = ImColor(241, 196, 15);\n        colors[ImGuiCustomCol_ToolbarGreen]  = ImColor(56, 139, 66);\n        colors[ImGuiCustomCol_ToolbarBlue]   = ImColor(6, 83, 155);\n        colors[ImGuiCustomCol_ToolbarPurple] = ImColor(103, 42, 120);\n        colors[ImGuiCustomCol_ToolbarBrown]  = ImColor(219, 179, 119);\n\n        colors[ImGuiCustomCol_Highlight] = ImColor(77, 198, 155);\n        colors[ImGuiCustomCol_IEEEToolSign] = ImColor(93, 93, 127);\n        colors[ImGuiCustomCol_IEEEToolExp]  = ImColor(93, 127,  93);\n        colors[ImGuiCustomCol_IEEEToolMantissa]  = ImColor(127, 93,  93);\n    }\n\n    void OpenPopupInWindow(const char *window_name, const char *popup_name) {\n        if (Begin(window_name)) {\n            OpenPopup(popup_name);\n        }\n        End();\n    }\n\n    void DisableWindowResize(ImGuiDir dir) {\n        const auto window = GetCurrentWindow();\n        const auto borderId = GetWindowResizeBorderID(window, dir);\n        if (borderId == GetHoveredID()) {\n            GImGui->ActiveIdMouseButton = 0;\n            SetHoveredID(0);\n        }\n        if (borderId == GetActiveID())\n            SetActiveID(0, window);\n    }\n\n\n    bool TitleBarButton(const char *label, ImVec2 size_arg) {\n        ImGuiWindow *window = GetCurrentWindow();\n        if (window->SkipItems)\n            return false;\n\n        ImGuiContext &g         = *GImGui;\n        const ImGuiStyle &style = g.Style;\n        const ImGuiID id        = window->GetID(label);\n        const ImVec2 label_size = CalcTextSize(label, nullptr, true);\n\n        ImVec2 pos = window->DC.CursorPos;\n\n        ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0F, label_size.y + style.FramePadding.y * 2.0F);\n\n        const ImRect bb(pos, pos + size);\n        ItemSize(size, style.FramePadding.y);\n        if (!ItemAdd(bb, id))\n            return false;\n\n        bool hovered, held;\n        bool pressed = ButtonBehavior(bb, id, &hovered, &held);\n\n        // Render\n        const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered\n                                                                                          : ImGuiCol_Button);\n        RenderNavCursor(bb, id);\n        RenderFrame(bb.Min, bb.Max, col, false, style.FrameRounding);\n        RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, nullptr, &label_size, style.ButtonTextAlign, &bb);\n\n        // Automatically close popups\n        // if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))\n        //    CloseCurrentPopup();\n\n        IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n        return pressed;\n    }\n\n    bool ToolBarButton(const char *symbol, ImVec4 color) {\n        ImGuiWindow *window = GetCurrentWindow();\n        if (window->SkipItems)\n            return false;\n\n        color.w = 1.0F;\n\n        ImGuiContext &g         = *GImGui;\n        const ImGuiStyle &style = g.Style;\n        const ImGuiID id        = window->GetID(symbol);\n        const ImVec2 label_size = CalcTextSize(symbol, nullptr, true);\n\n        ImVec2 pos = window->DC.CursorPos;\n\n        ImVec2 size = CalcItemSize(ImVec2(1, 1) * GetCurrentWindow()->MenuBarHeight, label_size.x + style.FramePadding.x * 2.0F, label_size.y + style.FramePadding.y * 2.0F);\n\n        ImVec2 padding = (size - label_size) / 2;\n\n        const ImRect bb(pos, pos + size);\n        ItemSize(size, style.FramePadding.y);\n        if (!ItemAdd(bb, id))\n            return false;\n\n        bool hovered, held;\n        bool pressed = ButtonBehavior(bb, id, &hovered, &held);\n\n        PushStyleColor(ImGuiCol_Text, color);\n\n        // Render\n        ImU32 col = 0x00;\n        if (held)\n            col = GetColorU32(ImGuiCol_ScrollbarGrabActive);\n        else if (hovered)\n            col = GetColorU32(ImGuiCol_ScrollbarGrabHovered);\n        RenderNavCursor(bb, id);\n        RenderFrame(bb.Min, bb.Max, col, false, style.FrameRounding);\n        RenderTextClipped(bb.Min + padding, bb.Max - padding, symbol, nullptr, &size, style.ButtonTextAlign, &bb);\n\n        PopStyleColor();\n\n        // Automatically close popups\n        // if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))\n        //    CloseCurrentPopup();\n\n        IMGUI_TEST_ENGINE_ITEM_INFO(id, symbol, g.LastItemData.StatusFlags);\n        return pressed;\n    }\n\n    bool IconButton(const char *symbol, ImVec4 color, ImVec2 size_arg, ImVec2 iconOffset) {\n        ImGuiWindow *window = GetCurrentWindow();\n        if (window->SkipItems)\n            return false;\n\n        color.w = 1.0F;\n\n        ImGuiContext &g         = *GImGui;\n        const ImGuiStyle &style = g.Style;\n        const ImGuiID id        = window->GetID(symbol);\n        const ImVec2 label_size = CalcTextSize(symbol, nullptr, true);\n\n        ImVec2 pos = window->DC.CursorPos;\n\n        ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0F, label_size.y + style.FramePadding.y * 2.0F);\n\n        const ImRect bb(pos, pos + size);\n        ItemSize(size, style.FramePadding.y);\n        if (!ItemAdd(bb, id))\n            return false;\n\n        bool hovered, held;\n        bool pressed = ButtonBehavior(bb, id, &hovered, &held);\n\n        PushStyleColor(ImGuiCol_Text, color);\n\n        // Render\n        const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered\n                                                                                          : ImGuiCol_Button);\n        RenderNavCursor(bb, id);\n        RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);\n        RenderTextClipped(bb.Min + style.FramePadding + iconOffset, bb.Max - style.FramePadding, symbol, nullptr, &label_size, style.ButtonTextAlign, &bb);\n\n        PopStyleColor();\n\n        // Automatically close popups\n        // if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))\n        //    CloseCurrentPopup();\n\n        IMGUI_TEST_ENGINE_ITEM_INFO(id, symbol, g.LastItemData.StatusFlags);\n        return pressed;\n    }\n\n    bool InputPrefix(const char* label, const char *prefix, std::string &buffer, ImGuiInputTextFlags flags) {\n        auto window             = GetCurrentWindow();\n        const ImGuiStyle &style = GImGui->Style;\n\n\n        const ImVec2 label_size = CalcTextSize(label, nullptr, true);\n        const ImVec2 frame_size = CalcItemSize(ImVec2(0, 0), CalcTextSize(prefix).x, label_size.y + style.FramePadding.y * 2.0F);\n        const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(CalcItemWidth(), frame_size.y));\n\n        SetCursorPosX(GetCursorPosX() + frame_size.x);\n\n        RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);\n\n        PushStyleVar(ImGuiStyleVar_Alpha, 0.6F);\n        RenderText(ImVec2(frame_bb.Min.x + style.FramePadding.x, frame_bb.Min.y + style.FramePadding.y), prefix);\n        PopStyleVar();\n\n        bool value_changed = false;\n        PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0);\n        PushStyleColor(ImGuiCol_FrameBg, 0x00000000);\n        PushStyleColor(ImGuiCol_FrameBgHovered, 0x00000000);\n        PushStyleColor(ImGuiCol_FrameBgActive, 0x00000000);\n        value_changed = ImGui::InputText(label, buffer, flags);\n        PopStyleColor(3);\n        PopStyleVar();\n\n        if (value_changed)\n            MarkItemEdited(GImGui->LastItemData.ID);\n\n        return value_changed;\n    }\n\n    bool InputIntegerPrefix(const char *label, const char *prefix, void *value, ImGuiDataType type, const char *format, ImGuiInputTextFlags flags) {\n        auto window             = GetCurrentWindow();\n        const ImGuiStyle &style = GImGui->Style;\n\n\n        const ImVec2 label_size = CalcTextSize(label, nullptr, true);\n        const ImVec2 frame_size = CalcItemSize(ImVec2(0, 0), CalcTextSize(prefix).x, label_size.y + style.FramePadding.y * 2.0F);\n        const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(CalcItemWidth(), frame_size.y));\n\n        SetCursorPosX(GetCursorPosX() + frame_size.x);\n\n        char buf[64];\n        DataTypeFormatString(buf, IM_ARRAYSIZE(buf), type, value, format);\n\n        RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);\n\n        PushStyleVar(ImGuiStyleVar_Alpha, 0.6F);\n        RenderText(ImVec2(frame_bb.Min.x + style.FramePadding.x, frame_bb.Min.y + style.FramePadding.y), prefix);\n        PopStyleVar();\n\n        bool value_changed = false;\n        PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0);\n        PushStyleColor(ImGuiCol_FrameBg, 0x00000000);\n        PushStyleColor(ImGuiCol_FrameBgHovered, 0x00000000);\n        PushStyleColor(ImGuiCol_FrameBgActive, 0x00000000);\n        if (InputTextEx(label, nullptr, buf, IM_ARRAYSIZE(buf), ImVec2(CalcItemWidth() - frame_size.x, label_size.y + style.FramePadding.y * 2.0F), flags))\n            value_changed = DataTypeApplyFromText(buf, type, value, format);\n        PopStyleColor(3);\n        PopStyleVar();\n\n        if (value_changed)\n            MarkItemEdited(GImGui->LastItemData.ID);\n\n        return value_changed;\n    }\n\n    bool InputHexadecimal(const char *label, u32 *value, ImGuiInputTextFlags flags) {\n        return InputIntegerPrefix(label, \"0x\", value, ImGuiDataType_U32, \"%X\", flags | ImGuiInputTextFlags_CharsHexadecimal);\n    }\n\n    bool InputHexadecimal(const char *label, u64 *value, ImGuiInputTextFlags flags) {\n        return InputIntegerPrefix(label, \"0x\", value, ImGuiDataType_U64, \"%llX\", flags | ImGuiInputTextFlags_CharsHexadecimal);\n    }\n\n    bool SliderBytes(const char *label, u64 *value, u64 min, u64 max, u64 stepSize, ImGuiSliderFlags flags) {\n        std::string format;\n\n        if (*value < 1024) {\n            format = fmt::format(\"{} Bytes\", *value);\n        } else if (*value < 1024 * 1024) {\n            format = fmt::format(\"{:.2f} KB\", *value / 1024.0);\n        } else if (*value < 1024 * 1024 * 1024) {\n            format = fmt::format(\"{:.2f} MB\", *value / (1024.0 * 1024.0));\n        } else {\n            format = fmt::format(\"{:.2f} GB\", *value / (1024.0 * 1024.0 * 1024.0));\n        }\n\n        *value /= stepSize;\n        min /= stepSize;\n        max /= stepSize;\n\n        auto result = ImGui::SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format.c_str(), flags | ImGuiSliderFlags_Logarithmic);\n\n        *value *= stepSize;\n\n        return result;\n    }\n\n    void ProgressBar(float fraction, ImVec2 size_value, float yOffset) {\n        ImGuiWindow *window = GetCurrentWindow();\n        if (window->SkipItems)\n            return;\n\n        ImGuiContext &g         = *GImGui;\n        const ImGuiStyle &style = g.Style;\n\n        ImVec2 pos  = window->DC.CursorPos + ImVec2(0, yOffset);\n        ImVec2 size = CalcItemSize(size_value, ImGui::GetContentRegionAvail().x, g.FontSize + style.FramePadding.y * 2.0F);\n        ImRect bb(pos, pos + size);\n        ItemSize(size, 0);\n        if (!ItemAdd(bb, 0))\n            return;\n\n        // Render\n        bool no_progress = fraction < 0;\n        fraction = ImSaturate(fraction);\n        RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);\n        bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));\n\n        if (no_progress) {\n            auto time = (fmod(ImGui::GetTime() * 2, 1.8) - 0.4);\n            RenderRectFilledInRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), ImSaturate(time), ImSaturate(time + 0.2), style.FrameRounding);\n        } else {\n            RenderRectFilledInRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0F, fraction, style.FrameRounding);\n        }\n    }\n\n    void TextUnformattedCentered(const char *text) {\n        auto availableSpace = ImGui::GetContentRegionAvail();\n\n        std::string drawString;\n        auto textEnd = text + strlen(text);\n        for (auto wrapPos = text; wrapPos != textEnd;) {\n            wrapPos = ImGui::GetFont()->CalcWordWrapPosition(GetFontSize(), wrapPos, textEnd, availableSpace.x * 0.8F);\n            if (text == wrapPos)\n                break;\n\n            drawString += std::string(text, wrapPos) + \"\\n\";\n            text = wrapPos;\n        }\n\n        drawString.pop_back();\n\n        auto textSize = ImGui::CalcTextSize(drawString.c_str());\n\n        ImPlot::AddTextCentered(ImGui::GetWindowDrawList(), ImGui::GetCursorScreenPos() + availableSpace / 2 - ImVec2(0, textSize.y / 2), ImGui::GetColorU32(ImGuiCol_Text), drawString.c_str());\n    }\n    \n    bool InputTextIcon(const char *label, const char *icon, std::string &buffer, ImGuiInputTextFlags flags) {\n        return InputTextIconHint(label, icon, nullptr, buffer, flags);\n    }\n\n    bool InputTextIconHint(const char* label, const char *icon, const char *hint, std::string &buffer, ImGuiInputTextFlags flags) {\n        auto window             = GetCurrentWindow();\n        const ImGuiID id        = window->GetID(label);\n        const ImGuiStyle &style = GImGui->Style;\n\n        const ImVec2 label_size = CalcTextSize(label, nullptr, true);\n        const ImVec2 icon_frame_size = CalcTextSize(icon) + style.FramePadding * 2.0F;\n        const ImVec2 frame_size = CalcItemSize(ImVec2(0, 0), icon_frame_size.x, label_size.y + style.FramePadding.y * 2.0F);\n        const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);\n\n        SetCursorPosX(GetCursorPosX() + frame_size.x);\n\n        float width_adjustment = window->DC.ItemWidth < 0 ? 0 : icon_frame_size.x;\n\n        bool value_changed = InputTextEx(label, hint, buffer.data(), buffer.size() + 1, ImVec2(CalcItemWidth() - width_adjustment, label_size.y + style.FramePadding.y * 2.0F), ImGuiInputTextFlags_CallbackResize | flags, UpdateStringSizeCallback, &buffer);\n\n        if (value_changed)\n            MarkItemEdited(GImGui->LastItemData.ID);\n\n        RenderNavCursor(frame_bb, id);\n        RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);\n\n        RenderFrame(frame_bb.Min, frame_bb.Min + icon_frame_size, GetColorU32(ImGuiCol_TableBorderStrong), true, style.FrameRounding);\n        RenderText(ImVec2(frame_bb.Min.x + style.FramePadding.x, frame_bb.Min.y + style.FramePadding.y), icon);\n\n        return value_changed;\n    }\n\n    bool InputScalarCallback(const char* label, ImGuiDataType data_type, void* p_data, const char* format, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) {\n        ImGuiWindow* window = GetCurrentWindow();\n        if (window->SkipItems)\n            return false;\n\n        ImGuiContext& g = *GImGui;\n\n        if (format == nullptr)\n            format = DataTypeGetInfo(data_type)->PrintFmt;\n\n        char buf[64];\n        DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);\n\n        bool value_changed = false;\n        if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)\n            flags |= ImGuiInputTextFlags_CharsDecimal;\n        flags |= ImGuiInputTextFlags_AutoSelectAll;\n\n        if (ImGui::InputText(label, buf, IM_ARRAYSIZE(buf), flags, callback, user_data))\n            value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);\n\n        if (value_changed)\n            MarkItemEdited(g.LastItemData.ID);\n\n        return value_changed;\n    }\n\n    void HideTooltip() {\n        char window_name[16];\n        ImFormatString(window_name, IM_ARRAYSIZE(window_name), \"##Tooltip_%02d\", GImGui->TooltipOverrideCount);\n        if (ImGuiWindow* window = FindWindowByName(window_name); window != nullptr) {\n            if (window->Active)\n                window->Hidden = true;\n        }\n    }\n\n\n    bool BitCheckbox(const char* label, bool* v) {\n        ImGuiWindow* window = GetCurrentWindow();\n        if (window->SkipItems)\n            return false;\n\n        ImGuiContext& g = *GImGui;\n        const ImGuiStyle& style = g.Style;\n        const ImGuiID id = window->GetID(label);\n        const ImVec2 label_size = CalcTextSize(label, nullptr, true);\n\n        const ImVec2 size = ImVec2(CalcTextSize(\"0\").x + style.FramePadding.x * 2, GetFrameHeight());\n        const ImVec2 pos = window->DC.CursorPos;\n        const ImRect total_bb(pos, pos + size);\n        ItemSize(total_bb, style.FramePadding.y);\n        if (!ItemAdd(total_bb, id))\n        {\n            IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));\n            return false;\n        }\n\n        bool hovered, held;\n        bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);\n        if (pressed)\n        {\n            *v = !(*v);\n            MarkItemEdited(id);\n        }\n\n        const ImRect check_bb(pos, pos + size);\n        RenderNavCursor(total_bb, id);\n        RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);\n\n        RenderText(check_bb.Min + style.FramePadding, *v ? \"1\" : \"0\");\n\n        ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);\n        if (label_size.x > 0.0F)\n            RenderText(label_pos, label);\n\n        IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));\n        return pressed;\n    }\n\n    bool DimmedButton(const char* label, ImVec2 size, ImGuiButtonFlags flags){\n        PushStyleColor(ImGuiCol_ButtonHovered, GetCustomColorU32(ImGuiCustomCol_DescButtonHovered));\n        PushStyleColor(ImGuiCol_Button, GetCustomColorU32(ImGuiCustomCol_DescButton));\n        PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive));\n        PushStyleColor(ImGuiCol_ButtonActive, GetCustomColorU32(ImGuiCustomCol_DescButtonActive));\n        PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);\n\n        bool res = ButtonEx(label, size, flags);\n\n        PopStyleColor(4);\n        PopStyleVar(1);\n\n        return res;\n    }\n\n    bool DimmedIconButton(const char *symbol, ImVec4 color, ImVec2 size, ImVec2 iconOffset) {\n        PushStyleColor(ImGuiCol_ButtonHovered, GetCustomColorU32(ImGuiCustomCol_DescButtonHovered));\n        PushStyleColor(ImGuiCol_Button, GetCustomColorU32(ImGuiCustomCol_DescButton));\n        PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive));\n        PushStyleColor(ImGuiCol_ButtonActive, GetCustomColorU32(ImGuiCustomCol_DescButtonActive));\n        PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5 * hex::ImHexApi::System::getGlobalScale());\n\n        bool res = IconButton(symbol, color, size, iconOffset);\n\n        PopStyleColor(4);\n        PopStyleVar(1);\n\n        return res;\n    }\n\n    bool DimmedArrowButton(const char *id, ImGuiDir dir, ImVec2 size) {\n        PushStyleColor(ImGuiCol_ButtonHovered, GetCustomColorU32(ImGuiCustomCol_DescButtonHovered));\n        PushStyleColor(ImGuiCol_Button, GetCustomColorU32(ImGuiCustomCol_DescButton));\n        PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive));\n        PushStyleColor(ImGuiCol_ButtonActive, GetCustomColorU32(ImGuiCustomCol_DescButtonActive));\n        PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5 * hex::ImHexApi::System::getGlobalScale());\n\n        bool res = ArrowButtonEx(id, dir, size);\n\n        PopStyleColor(4);\n        PopStyleVar(1);\n\n        return res;\n    }\n\n    bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size, ImVec2 iconOffset) {\n        bool pushed = false;\n        bool toggled = false;\n\n        if (*v) {\n            PushStyleColor(ImGuiCol_Border, GetStyleColorVec4(ImGuiCol_ButtonActive));\n            pushed = true;\n        }\n\n        if (DimmedIconButton(icon, GetStyleColorVec4(ImGuiCol_Text), size, iconOffset)) {\n            *v = !*v;\n            toggled = true;\n        }\n\n        if (pushed)\n            PopStyleColor();\n\n        return toggled;\n    }\n\n    bool DimmedIconToggle(const char *icon, bool *v) {\n        bool pushed = false;\n        bool toggled = false;\n\n        if (*v) {\n            PushStyleColor(ImGuiCol_Border, GetStyleColorVec4(ImGuiCol_ButtonActive));\n            pushed = true;\n        }\n\n        if (DimmedIconButton(icon, GetStyleColorVec4(ImGuiCol_Text))) {\n            *v = !*v;\n            toggled = true;\n        }\n\n        if (pushed)\n            PopStyleColor();\n\n        return toggled;\n    }\n\n    bool DimmedIconToggle(const char *iconOn, const char *iconOff, bool *v) {\n        bool pushed = false;\n        bool toggled = false;\n\n        if (*v) {\n            PushStyleColor(ImGuiCol_Border, GetStyleColorVec4(ImGuiCol_ButtonActive));\n            pushed = true;\n        }\n\n        if (DimmedIconButton(*v ? iconOn : iconOff, GetStyleColorVec4(ImGuiCol_Text))) {\n            *v = !*v;\n            toggled = true;\n        }\n\n        if (pushed)\n            PopStyleColor();\n\n        return toggled;\n    }\n\n    void TextOverlay(const char *text, ImVec2 pos, float maxWidth) {\n        const auto textSize = CalcTextSize(text, nullptr, false, maxWidth);\n        const auto textPos  = pos - textSize / 2;\n        const auto margin   = GetStyle().FramePadding * 2;\n        const auto textRect = ImRect(textPos - margin, textPos + textSize + margin);\n\n        auto drawList = GetWindowDrawList();\n\n        drawList->AddDrawCmd();\n        drawList->AddRectFilled(textRect.Min, textRect.Max, GetColorU32(ImGuiCol_WindowBg) | 0xFF000000);\n        drawList->AddRect(textRect.Min, textRect.Max, GetColorU32(ImGuiCol_Border));\n        drawList->AddDrawCmd();\n        drawList->AddText(nullptr, 0.0F, textPos, GetColorU32(ImGuiCol_Text), text, nullptr, maxWidth);\n    }\n\n    bool BeginBox() {\n        PushStyleVar(ImGuiStyleVar_CellPadding, hex::scaled(5, 5));\n        if (BeginTable(\"##box\", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_SizingStretchSame)) {\n            TableNextRow();\n            TableNextColumn();\n\n            return true;\n        }\n\n        return false;\n    }\n\n    void EndBox() {\n        EndTable();\n        PopStyleVar();\n    }\n\n    bool BeginSubWindow(const char *label, bool *collapsed, ImVec2 size, ImGuiChildFlags flags) {\n        const bool hasMenuBar = !std::string_view(label).empty();\n\n        bool result = false;\n        ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0F);\n        ImGui::PushID(\"SubWindow\");\n        if (ImGui::BeginChild(label, size, ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY | flags, hasMenuBar ? ImGuiWindowFlags_MenuBar : ImGuiWindowFlags_None)) {\n            result = true;\n\n            if (hasMenuBar && ImGui::BeginMenuBar()) {\n                if (collapsed == nullptr)\n                    ImGui::TextUnformatted(label);\n                else {\n                    const auto &style = ImGui::GetStyle();\n                    const auto framePadding = style.FramePadding.x;\n                    ImGui::PushStyleVarX(ImGuiStyleVar_FramePadding, 0);\n                    ImGui::SetCursorPosX(ImGui::GetCursorPosX() - style.WindowPadding.x + framePadding);\n                    ImGui::TreeNodeSetOpen(ImGui::GetID(\"##CollapseHeader\"), !*collapsed);\n                    *collapsed = !ImGui::TreeNodeEx(\"##CollapseHeader\", ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanLabelWidth);\n                    ImGui::SameLine(0, framePadding);\n                    ImGui::TextUnformatted(label);\n                    if (!*collapsed) ImGui::TreePop();\n\n                    ImGui::PopStyleVar();\n                }\n                ImGui::EndMenuBar();\n            }\n\n            if (collapsed != nullptr && *collapsed) {\n                result = false;\n            }\n        }\n        ImGui::PopStyleVar();\n\n        return result;\n    }\n\n    void EndSubWindow() {\n        ImGui::EndChild();\n        ImGui::PopID();\n    }\n\n    bool VSliderAngle(const char* label, const ImVec2& size, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags) {\n        if (format == nullptr)\n            format = \"%.0f deg\";\n        float v_deg = (*v_rad) * 360.0F / (2 * std::numbers::pi_v<float>);\n        bool value_changed = ImGui::VSliderFloat(label, size, &v_deg, v_degrees_min, v_degrees_max, format, flags);\n        *v_rad = v_deg * (2 * std::numbers::pi_v<float>) / 360.0F;\n        return value_changed;\n    }\n\n    bool InputFilePicker(const char *label, std::fs::path &path, const std::vector<hex::fs::ItemFilter> &validExtensions) {\n        bool picked = false;\n\n        ImGui::PushID(label);\n\n        const auto framePadding = ImGui::GetStyle().FramePadding.x;\n        const auto buttonSize = ImVec2(ImGui::CalcTextSize(\"...\").x + framePadding * 2, ImGui::GetFrameHeight());\n        ImGui::PushItemWidth(ImGui::CalcItemWidth() - buttonSize.x - framePadding);\n        std::string string = wolv::util::toUTF8String(path);\n        if (ImGui::InputText(\"##pathInput\", string, ImGuiInputTextFlags_AutoSelectAll)) {\n            path = std::u8string(string.begin(), string.end());\n            picked = true;\n        }\n        ImGui::PopItemWidth();\n\n        ImGui::SameLine(0, framePadding);\n\n        if (ImGui::Button(\"...\", buttonSize)) {\n            hex::fs::openFileBrowser(hex::fs::DialogMode::Open, validExtensions, [&](const std::fs::path &pickedPath) {\n                path = pickedPath;\n                picked = true;\n            });\n        }\n\n        ImGui::SameLine();\n\n        ImGui::TextUnformatted(label);\n\n        ImGui::PopID();\n\n        return picked;\n    }\n\n    bool ToggleSwitch(const char *label, bool *v) {\n        ImGuiWindow* window = GetCurrentWindow();\n        if (window->SkipItems)\n            return false;\n\n        ImGuiContext& g = *GImGui;\n        const ImGuiStyle& style = g.Style;\n        const ImGuiID id = window->GetID(label);\n        const ImVec2 label_size = CalcTextSize(label, nullptr, true);\n\n        const ImVec2 size = ImVec2(GetFrameHeight() * 2.0F, GetFrameHeight());\n        const ImVec2 pos = window->DC.CursorPos;\n        const ImRect total_bb(pos, pos + ImVec2(size.x + (label_size.x > 0.0F ? style.ItemInnerSpacing.x + label_size.x : 0.0F), label_size.y + style.FramePadding.y * 2.0F));\n        ItemSize(total_bb, style.FramePadding.y);\n        if (!ItemAdd(total_bb, id))\n        {\n            IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));\n            return false;\n        }\n\n        bool hovered, held;\n        bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);\n        if (pressed)\n        {\n            *v = !(*v);\n            MarkItemEdited(id);\n        }\n\n        const ImRect knob_bb(pos, pos + size);\n\n        window->DrawList->AddRectFilled(knob_bb.Min, knob_bb.Max, GetColorU32(held ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : *v ? ImGuiCol_ButtonActive : ImGuiCol_Button), size.y / 2);\n\n        if (*v)\n            window->DrawList->AddCircleFilled(knob_bb.Max - ImVec2(size.y / 2, size.y / 2), (size.y - style.ItemInnerSpacing.y) / 2, GetColorU32(ImGuiCol_ScrollbarGrabActive), 16);\n        else\n            window->DrawList->AddCircleFilled(knob_bb.Min + ImVec2(size.y / 2, size.y / 2), (size.y - style.ItemInnerSpacing.y) / 2, GetColorU32(ImGuiCol_ScrollbarGrabActive), 16);\n\n        ImVec2 label_pos = ImVec2(knob_bb.Max.x + style.ItemInnerSpacing.x, knob_bb.Min.y + style.FramePadding.y);\n        if (g.LogEnabled)\n            LogRenderedText(&label_pos, *v ? \"((*)  )\" : \"(  (*))\");\n        if (label_size.x > 0.0F)\n            RenderText(label_pos, label);\n\n        IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));\n        return pressed;\n    }\n\n    bool ToggleSwitch(const char *label, bool v) {\n        return ToggleSwitch(label, &v);\n    }\n\n    bool PopupTitleBarButton(const char* label, bool p_enabled)\n    {\n        ImGuiContext& g = *GImGui;\n        ImGuiWindow* window = g.CurrentWindow;\n        const ImGuiID id = window->GetID(label);\n        const ImRect title_rect = window->TitleBarRect();\n        const ImVec2 size(g.FontSize, g.FontSize); // Button size matches font size for aesthetic consistency.\n        const ImVec2 pos = window->DC.CursorPos;\n        const ImVec2 max_pos = pos + size;\n        const ImRect bb(pos.x, title_rect.Min.y, max_pos.x, title_rect.Max.y);\n\n        ImGui::PushClipRect(title_rect.Min, title_rect.Max, false);\n\n        // Check for item addition (similar to how clipping is handled in the original button functions).\n        bool is_clipped = !ItemAdd(bb, id);\n        bool hovered, held;\n        bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);\n        if (is_clipped)\n        {\n            ImGui::PopClipRect();\n            return pressed;\n        }\n\n        // const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n        // window->DrawList->AddCircleFilled(bb.GetCenter(), ImMax(2.0f, g.FontSize * 0.5f + 1.0f), bg_col);\n\n        // Draw the label in the center\n        ImU32 text_col = GetColorU32(p_enabled || hovered ? ImGuiCol_Text : ImGuiCol_TextDisabled);\n        window->DrawList->AddText(bb.GetCenter() - ImVec2(g.FontSize * 0.45F, g.FontSize * 0.5F), text_col, label);\n\n        // Return the button press state\n        ImGui::PopClipRect();\n        return pressed;\n    }\n\n    void PopupTitleBarText(const char* text) {\n        ImGuiContext& g = *GImGui;\n        ImGuiWindow* window = g.CurrentWindow;\n        const ImRect title_rect = window->TitleBarRect();\n        const ImVec2 size(g.FontSize, g.FontSize); // Button size matches font size for aesthetic consistency.\n        const ImVec2 pos = window->DC.CursorPos;\n        const ImVec2 max_pos = pos + size;\n        const ImRect bb(pos.x, title_rect.Min.y, max_pos.x, title_rect.Max.y);\n\n        ImGui::PushClipRect(title_rect.Min, title_rect.Max, false);\n\n        // Draw the label in the center\n        ImU32 text_col = GetColorU32(ImGuiCol_Text);\n        window->DrawList->AddText(bb.GetCenter() - ImVec2(g.FontSize * 0.45F, g.FontSize * 0.5F), text_col, text);\n\n        // Return the button press state\n        ImGui::PopClipRect();\n    }\n\n    bool IsDarkBackground(const ImColor& bgColor) {\n        // Extract RGB components in 0–255 range\n        int r = static_cast<int>(bgColor.Value.x * 255.0F);\n        int g = static_cast<int>(bgColor.Value.y * 255.0F);\n        int b = static_cast<int>(bgColor.Value.z * 255.0F);\n\n        // Compute brightness using perceived luminance\n        int brightness = (r * 299 + g * 587 + b * 114) / 1000;\n\n        // If brightness is below threshold, use white text\n        return brightness < 128;\n    }\n\n\n\n    static bool s_imguiTestEngineEnabled = false;\n\n    void ImGuiTestEngine::setEnabled(bool enabled) {\n        s_imguiTestEngineEnabled = enabled;\n    }\n\n    bool ImGuiTestEngine::isEnabled() {\n        return s_imguiTestEngineEnabled;\n    }\n}\n\nnamespace ImGui {\n\n    bool InputText(const char *label, std::u8string &buffer, ImGuiInputTextFlags flags) {\n        return ImGui::InputText(label, reinterpret_cast<char *>(buffer.data()), buffer.size() + 1, ImGuiInputTextFlags_CallbackResize | flags, ImGuiExt::UpdateStringSizeCallback, &buffer);\n    }\n\n    bool InputText(const char *label, std::string &buffer, ImGuiInputTextFlags flags) {\n        return ImGui::InputText(label, buffer.data(), buffer.size() + 1, ImGuiInputTextFlags_CallbackResize | flags, ImGuiExt::UpdateStringSizeCallback, &buffer);\n    }\n\n    bool InputTextMultiline(const char *label, std::string &buffer, const ImVec2 &size, ImGuiInputTextFlags flags) {\n        return ImGui::InputTextMultiline(label, buffer.data(), buffer.size() + 1, size, ImGuiInputTextFlags_CallbackResize | flags, ImGuiExt::UpdateStringSizeCallback, &buffer);\n    }\n\n    bool InputTextWithHint(const char *label, const char *hint, std::string &buffer, ImGuiInputTextFlags flags) {\n        return ImGui::InputTextWithHint(label, hint, buffer.data(), buffer.size() + 1, ImGuiInputTextFlags_CallbackResize | flags, ImGuiExt::UpdateStringSizeCallback, &buffer);\n    }\n\n}\n"
  },
  {
    "path": "lib/libimhex/source/ui/popup.cpp",
    "content": "#include <hex/ui/popup.hpp>\n#include <hex/helpers/auto_reset.hpp>\n\nnamespace hex::impl {\n\n\n    [[nodiscard]] std::vector<std::unique_ptr<PopupBase>> &PopupBase::getOpenPopups() {\n        static AutoReset<std::vector<std::unique_ptr<PopupBase>>> openPopups;\n\n        return openPopups;\n    }\n\n    std::mutex& PopupBase::getMutex() {\n        static std::mutex mutex;\n\n        return mutex;\n    }\n\n\n\n}"
  },
  {
    "path": "lib/libimhex/source/ui/toast.cpp",
    "content": "#include <hex/ui/toast.hpp>\n#include <hex/helpers/auto_reset.hpp>\n\nnamespace hex::impl {\n\n    [[nodiscard]] std::list<std::unique_ptr<ToastBase>> &ToastBase::getQueuedToasts() {\n        static AutoReset<std::list<std::unique_ptr<ToastBase>>> queuedToasts;\n\n        return queuedToasts;\n    }\n\n    std::mutex& ToastBase::getMutex() {\n        static std::mutex mutex;\n\n        return mutex;\n    }\n\n}"
  },
  {
    "path": "lib/libimhex/source/ui/view.cpp",
    "content": "#include <hex/ui/view.hpp>\n#include <hex/helpers/auto_reset.hpp>\n\n#include <hex/api/imhex_api/provider.hpp>\n#include <hex/api/task_manager.hpp>\n#include <hex/api/tutorial_manager.hpp>\n\n#include <imgui_internal.h>\n\n#include <imgui.h>\n\n#include <string>\n#include <GLFW/glfw3.h>\n\nnamespace hex {\n\n    static AutoReset<View*> s_lastFocusedView = nullptr;\n\n    View::View(UnlocalizedString unlocalizedName, const char *icon) : m_unlocalizedViewName(std::move(unlocalizedName)), m_icon(icon) { }\n\n    bool View::shouldDraw() const {\n        return ImHexApi::Provider::isValid() && ImHexApi::Provider::get()->isAvailable();\n    }\n\n    bool View::shouldProcess() const {\n        return this->shouldDraw() && this->getWindowOpenState();\n    }\n\n    bool View::hasViewMenuItemEntry() const {\n        return true;\n    }\n\n    ImVec2 View::getMinSize() const {\n        return scaled({ 300, 400 });\n    }\n\n    ImVec2 View::getMaxSize() const {\n        return { FLT_MAX, FLT_MAX };\n    }\n\n    ImGuiWindowFlags View::getWindowFlags() const {\n        return ImGuiWindowFlags_None;\n    }\n\n\n\n    bool &View::getWindowOpenState() {\n        return m_windowOpen;\n    }\n\n    const bool &View::getWindowOpenState() const {\n        return m_windowOpen;\n    }\n\n    const UnlocalizedString &View::getUnlocalizedName() const {\n        return m_unlocalizedViewName;\n    }\n\n    std::string View::getName() const {\n        return View::toWindowName(m_unlocalizedViewName);\n    }\n\n    bool View::didWindowJustOpen() {\n        return std::exchange(m_windowJustOpened, false);\n    }\n\n    void View::setWindowJustOpened(const bool state) {\n        m_windowJustOpened = state;\n    }\n\n    bool View::didWindowJustClose() {\n        return std::exchange(m_windowJustClosed, false);\n    }\n\n    void View::setWindowJustClosed(const bool state) {\n        m_windowJustClosed = state;\n    }\n\n    void View::trackViewState() {\n        if (m_windowOpen && !m_prevWindowOpen)\n        {\n            this->setWindowJustOpened(true);\n            this->onOpen();\n        } else if (!m_windowOpen && m_prevWindowOpen) {\n            this->setWindowJustClosed(true);\n            this->onClose();\n        }\n        m_prevWindowOpen = m_windowOpen;\n    }\n\n\n    void View::discardNavigationRequests() {\n        if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows))\n            ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NavEnableKeyboard;\n    }\n\n    void View::bringToFront() {\n        getWindowOpenState() = true;\n        TaskManager::doLater([this]{ ImGui::SetWindowFocus(toWindowName(getUnlocalizedName()).c_str()); });\n    }\n\n\n    std::string View::toWindowName(const UnlocalizedString &unlocalizedName) {\n        return fmt::format(\"{}###{}\", Lang(unlocalizedName), unlocalizedName.get());\n    }\n\n    void View::setFocused(bool focused) {\n        m_focused = focused;\n        if (focused)\n            s_lastFocusedView = this;\n    }\n\n\n    const View* View::getLastFocusedView() {\n        if (!ImHexApi::Provider::isValid())\n            return nullptr;\n\n        return s_lastFocusedView;\n    }\n\n    void View::Window::handleFocusRestoration() {\n        const auto title = fmt::format(\"{} {}\", this->getIcon(), View::toWindowName(this->getUnlocalizedName()));\n\n        const ImGuiContext& g = *ImGui::GetCurrentContext();\n        bool foundTopFocused = false;\n        ImGuiWindow *imguiFocusedWindow = nullptr;\n        ImGuiWindow *focusedSubWindow = nullptr;\n\n        if (g.NavWindow != nullptr) {\n            imguiFocusedWindow = g.NavWindow;\n            foundTopFocused = true;\n        }\n        for (auto focusedWindow: g.WindowsFocusOrder | std::views::reverse) {\n            if (focusedWindow == nullptr || !focusedWindow->WasActive)\n                continue;\n            std::string focusedWindowName = focusedWindow->Name;\n            if (!foundTopFocused) {\n                imguiFocusedWindow = focusedWindow;\n                foundTopFocused = true;\n            }\n            if (imguiFocusedWindow == nullptr || !focusedWindowName.contains(\"###hex.builtin.view.\"))\n                continue;\n            if (auto focusedChild = focusedWindow->NavLastChildNavWindow; focusedChild != nullptr)\n                focusedSubWindow = focusedChild;\n            else if (focusedWindow == focusedWindow->RootWindow)\n                focusedSubWindow = focusedWindow;\n\n            break;\n        }\n\n        std::string imguiFocusedWindowName = \"NULL\";\n        if (imguiFocusedWindow != nullptr)\n            imguiFocusedWindowName.assign(imguiFocusedWindow->Name);\n\n        std::string focusedSubWindowName;\n        if (focusedSubWindow != nullptr || m_focusedSubWindow != nullptr) {\n            if (glfwGetWindowAttrib(ImHexApi::System::getMainWindowHandle(), GLFW_FOCUSED)) {\n                focusedSubWindowName = focusedSubWindow != nullptr ? focusedSubWindow->Name : m_focusedSubWindow->Name;\n                if (focusedSubWindow != nullptr && m_focusedSubWindow != nullptr) {\n                    std::string_view windowName = m_focusedSubWindow->Name;\n                    auto stringsVector = wolv::util::splitString(focusedSubWindowName, \"/\");\n                    if (stringsVector.back().contains(\"resize\") || (focusedSubWindow == focusedSubWindow->RootWindow && windowName.starts_with(focusedSubWindowName)))\n                        focusedSubWindowName = windowName;\n                    else\n                        m_focusedSubWindow = focusedSubWindow;\n                } else if (focusedSubWindow != nullptr)\n                    m_focusedSubWindow = focusedSubWindow;\n\n                bool windowAlreadyFocused = focusedSubWindowName == imguiFocusedWindowName;\n                bool titleFocused = focusedSubWindowName.starts_with(title);\n\n                if (titleFocused && !windowAlreadyFocused) {\n\n                    bool windowMayNeedFocus = focusedSubWindowName.starts_with(imguiFocusedWindowName);\n                    std::string activeName = g.ActiveIdWindow ? g.ActiveIdWindow->Name : \"NULL\";\n\n                    if ((activeName == \"NULL\" || windowMayNeedFocus) && (imguiFocusedWindowName == \"##MainMenuBar\" || imguiFocusedWindowName.starts_with(\"ImHexDockSpace\") || imguiFocusedWindowName.contains(\"###hex.builtin.view.\"))) {\n                        if (m_focusedSubWindow == m_focusedSubWindow->RootWindow)\n                            ImGui::FocusWindow(m_focusedSubWindow, ImGuiFocusRequestFlags_RestoreFocusedChild);\n                        else\n                            ImGui::FocusWindow(m_focusedSubWindow, ImGuiFocusRequestFlags_None);\n                    }\n                }\n            }\n        }\n    }\n\n\n    void View::Window::draw(ImGuiWindowFlags extraFlags) {\n        if (this->shouldDraw()) {\n            const auto title = fmt::format(\"{} {}\", this->getIcon(), View::toWindowName(this->getUnlocalizedName()));\n\n            handleFocusRestoration();\n\n            if (!allowScroll())\n                extraFlags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;\n\n            ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());\n            if (ImGui::Begin(title.c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | extraFlags | this->getWindowFlags())) {\n                TutorialManager::setLastItemInteractiveHelpPopup([this]{ this->drawHelpText(); });\n                this->drawContent();\n            }\n            ImGui::End();\n        }\n    }\n\n    void View::Special::draw(ImGuiWindowFlags extraFlags) {\n        std::ignore = extraFlags;\n\n        if (this->shouldDraw()) {\n            ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());\n            this->drawContent();\n        }\n    }\n\n    void View::Floating::draw(ImGuiWindowFlags extraFlags) {\n        Window::draw(extraFlags | ImGuiWindowFlags_NoDocking);\n    }\n\n    void View::Scrolling::draw(ImGuiWindowFlags extraFlags) {\n        Window::draw(extraFlags);\n    }\n\n    void View::Modal::draw(ImGuiWindowFlags extraFlags) {\n        if (this->shouldDraw()) {\n            if (this->getWindowOpenState())\n                ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str());\n\n            ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));\n            ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());\n            const auto title = fmt::format(\"{} {}\", this->getIcon(), View::toWindowName(this->getUnlocalizedName()));\n            if (ImGui::BeginPopupModal(title.c_str(), this->hasCloseButton() ? &this->getWindowOpenState() : nullptr, ImGuiWindowFlags_NoCollapse | extraFlags | this->getWindowFlags())) {\n                this->drawContent();\n\n                ImGui::EndPopup();\n            }\n\n            if (ImGui::IsKeyPressed(ImGuiKey_Escape))\n                this->getWindowOpenState() = false;\n        }\n    }\n\n    void View::FullScreen::draw(ImGuiWindowFlags extraFlags) {\n        std::ignore = extraFlags;\n\n        this->drawContent();\n        this->drawAlwaysVisibleContent();\n    }\n\n\n}\n"
  },
  {
    "path": "lib/third_party/.clang-tidy",
    "content": "# Disable all checks\nChecks: '-*'\n"
  },
  {
    "path": "lib/third_party/boost/CMakeLists.txt",
    "content": "project(boost)\n\nadd_subdirectory(regex)"
  },
  {
    "path": "lib/third_party/boost/regex/CMakeLists.txt",
    "content": "project(boost-regex)\n\nadd_library(boost-regex INTERFACE)\n\ntarget_include_directories(boost-regex INTERFACE\n    include\n)\ntarget_compile_definitions(boost-regex INTERFACE BOOST_REGEX_STANDALONE)\n\nadd_library(boost::regex ALIAS boost-regex)"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/cregex.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org/libs/regex for most recent version.\n  *   FILE         cregex.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares POSIX API functions\n  *                + boost::RegEx high level wrapper.\n  */\n\n#ifndef BOOST_RE_CREGEX_HPP\n#define BOOST_RE_CREGEX_HPP\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n\n#ifdef BOOST_REGEX_CXX03\n#include <boost/regex/v4/cregex.hpp>\n#else\n#include <boost/regex/v5/cregex.hpp>\n#endif\n\n#endif /* include guard */\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/concepts.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         concepts.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression concepts.\n  */\n\n#ifndef BOOST_REGEX_CONCEPTS_HPP_INCLUDED\n#define BOOST_REGEX_CONCEPTS_HPP_INCLUDED\n\n#include <boost/concept_archetype.hpp>\n#include <boost/concept_check.hpp>\n#include <boost/type_traits/is_enum.hpp>\n#include <boost/type_traits/is_base_and_derived.hpp>\n#include <boost/static_assert.hpp>\n#ifndef BOOST_TEST_TR1_REGEX\n#include <boost/regex.hpp>\n#endif\n#include <bitset>\n#include <vector>\n#include <ostream>\n\n#ifdef BOOST_REGEX_CXX03\n#define RW_NS boost\n#else\n#define RW_NS std\n#endif\n\nnamespace boost{\n\n//\n// bitmask_archetype:\n// this can be either an integer type, an enum, or a std::bitset,\n// we use the latter as the architype as it offers the \"strictest\"\n// of the possible interfaces:\n//\ntypedef std::bitset<512> bitmask_archetype;\n//\n// char_architype:\n// A strict model for the character type interface.\n//\nstruct char_architype\n{\n   // default constructable:\n   char_architype();\n   // copy constructable / assignable:\n   char_architype(const char_architype&);\n   char_architype& operator=(const char_architype&);\n   // constructable from an integral value:\n   char_architype(unsigned long val);\n   // comparable:\n   bool operator==(const char_architype&)const;\n   bool operator!=(const char_architype&)const;\n   bool operator<(const char_architype&)const;\n   bool operator<=(const char_architype&)const;\n   bool operator>=(const char_architype&)const;\n   bool operator>(const char_architype&)const;\n   // conversion to integral type:\n   operator long()const;\n};\ninline long hash_value(char_architype val)\n{  return val;  }\n//\n// char_architype can not be used with basic_string:\n//\n} // namespace boost\nnamespace std{\n   template<> struct char_traits<boost::char_architype>\n   {\n      // The intent is that this template is not instantiated,\n      // but this typedef gives us a chance of compilation in\n      // case it is:\n      typedef boost::char_architype char_type;\n   };\n}\n//\n// Allocator architype:\n//\ntemplate <class T>\nclass allocator_architype\n{\npublic:\n   typedef T* pointer;\n   typedef const T* const_pointer;\n   typedef T& reference;\n   typedef const T& const_reference;\n   typedef T value_type;\n   typedef unsigned size_type;\n   typedef int difference_type;\n\n   template <class U>\n   struct rebind\n   {\n      typedef allocator_architype<U> other;\n   };\n\n   pointer address(reference r){ return &r; }\n   const_pointer address(const_reference r) { return &r; }\n   pointer allocate(size_type n) { return static_cast<pointer>(std::malloc(n)); }\n   pointer allocate(size_type n, pointer) { return static_cast<pointer>(std::malloc(n)); }\n   void deallocate(pointer p, size_type) { std::free(p); }\n   size_type max_size()const { return UINT_MAX; }\n\n   allocator_architype(){}\n   allocator_architype(const allocator_architype&){}\n\n   template <class Other>\n   allocator_architype(const allocator_architype<Other>&){}\n\n   void construct(pointer p, const_reference r) { new (p)T(r); }\n   void destroy(pointer p) { p->~T(); }\n};\n\ntemplate <class T>\nbool operator == (const allocator_architype<T>&, const allocator_architype<T>&) {return true; }\ntemplate <class T>\nbool operator != (const allocator_architype<T>&, const allocator_architype<T>&) { return false; }\n\nnamespace boost{\n//\n// regex_traits_architype:\n// A strict interpretation of the regular expression traits class requirements.\n//\ntemplate <class charT>\nstruct regex_traits_architype\n{\npublic:\n   regex_traits_architype(){}\n   typedef charT char_type;\n   // typedef std::size_t size_type;\n   typedef std::vector<char_type> string_type;\n   typedef copy_constructible_archetype<assignable_archetype<> > locale_type;\n   typedef bitmask_archetype char_class_type;\n\n   static std::size_t length(const char_type* ) { return 0; }\n\n   charT translate(charT ) const { return charT(); }\n   charT translate_nocase(charT ) const { return static_object<charT>::get(); }\n\n   template <class ForwardIterator>\n   string_type transform(ForwardIterator , ForwardIterator ) const\n   { return static_object<string_type>::get(); }\n   template <class ForwardIterator>\n   string_type transform_primary(ForwardIterator , ForwardIterator ) const\n   { return static_object<string_type>::get(); }\n\n   template <class ForwardIterator>\n   char_class_type lookup_classname(ForwardIterator , ForwardIterator ) const\n   { return static_object<char_class_type>::get(); }\n   template <class ForwardIterator>\n   string_type lookup_collatename(ForwardIterator , ForwardIterator ) const\n   { return static_object<string_type>::get(); }\n\n   bool isctype(charT, char_class_type) const\n   { return false; }\n   int value(charT, int) const\n   { return 0; }\n\n   locale_type imbue(locale_type l)\n   { return l; }\n   locale_type getloc()const\n   { return static_object<locale_type>::get(); }\n\nprivate:\n   // this type is not copyable:\n   regex_traits_architype(const regex_traits_architype&){}\n   regex_traits_architype& operator=(const regex_traits_architype&){ return *this; }\n};\n\n//\n// alter this to std::tr1, to test a std implementation:\n//\n#ifndef BOOST_TEST_TR1_REGEX\nnamespace global_regex_namespace = ::boost;\n#else\nnamespace global_regex_namespace = ::std::tr1;\n#endif\n\ntemplate <class Bitmask>\nstruct BitmaskConcept\n{\n   void constraints() \n   {\n      function_requires<CopyConstructibleConcept<Bitmask> >();\n      function_requires<AssignableConcept<Bitmask> >();\n\n      m_mask1 = m_mask2 | m_mask3;\n      m_mask1 = m_mask2 & m_mask3;\n      m_mask1 = m_mask2 ^ m_mask3;\n\n      m_mask1 = ~m_mask2;\n\n      m_mask1 |= m_mask2;\n      m_mask1 &= m_mask2;\n      m_mask1 ^= m_mask2;\n   }\n   Bitmask m_mask1, m_mask2, m_mask3;\n};\n\ntemplate <class traits>\nstruct RegexTraitsConcept\n{\n   RegexTraitsConcept();\n   // required typedefs:\n   typedef typename traits::char_type char_type;\n   // typedef typename traits::size_type size_type;\n   typedef typename traits::string_type string_type;\n   typedef typename traits::locale_type locale_type;\n   typedef typename traits::char_class_type char_class_type;\n\n   void constraints() \n   {\n      //function_requires<UnsignedIntegerConcept<size_type> >();\n      function_requires<RandomAccessContainerConcept<string_type> >();\n      function_requires<DefaultConstructibleConcept<locale_type> >();\n      function_requires<CopyConstructibleConcept<locale_type> >();\n      function_requires<AssignableConcept<locale_type> >();\n      function_requires<BitmaskConcept<char_class_type> >();\n\n      std::size_t n = traits::length(m_pointer);\n      ignore_unused_variable_warning(n);\n\n      char_type c = m_ctraits.translate(m_char);\n      ignore_unused_variable_warning(c);\n      c = m_ctraits.translate_nocase(m_char);\n      \n      //string_type::foobar bar;\n      string_type s1 = m_ctraits.transform(m_pointer, m_pointer);\n      ignore_unused_variable_warning(s1);\n\n      string_type s2 = m_ctraits.transform_primary(m_pointer, m_pointer);\n      ignore_unused_variable_warning(s2);\n\n      char_class_type cc = m_ctraits.lookup_classname(m_pointer, m_pointer);\n      ignore_unused_variable_warning(cc);\n\n      string_type s3 = m_ctraits.lookup_collatename(m_pointer, m_pointer);\n      ignore_unused_variable_warning(s3);\n\n      bool b = m_ctraits.isctype(m_char, cc);\n      ignore_unused_variable_warning(b);\n\n      int v = m_ctraits.value(m_char, 16);\n      ignore_unused_variable_warning(v);\n\n      locale_type l(m_ctraits.getloc());\n      m_traits.imbue(l);\n      ignore_unused_variable_warning(l);\n   }\n   traits m_traits;\n   const traits m_ctraits;\n   const char_type* m_pointer;\n   char_type m_char;\nprivate:\n   RegexTraitsConcept& operator=(RegexTraitsConcept&);\n};\n\n//\n// helper class to compute what traits class a regular expression type is using:\n//\ntemplate <class Regex>\nstruct regex_traits_computer;\n\ntemplate <class charT, class traits>\nstruct regex_traits_computer< global_regex_namespace::basic_regex<charT, traits> >\n{\n   typedef traits type;\n};\n\n//\n// BaseRegexConcept does not test anything dependent on basic_string,\n// in case our charT does not have an associated char_traits:\n//\ntemplate <class Regex>\nstruct BaseRegexConcept\n{\n   typedef typename Regex::value_type value_type;\n   //typedef typename Regex::size_type size_type;\n   typedef typename Regex::flag_type flag_type;\n   typedef typename Regex::locale_type locale_type;\n   typedef input_iterator_archetype<value_type> input_iterator_type;\n\n   // derived test types:\n   typedef const value_type* pointer_type;\n   typedef bidirectional_iterator_archetype<value_type> BidiIterator;\n   typedef global_regex_namespace::sub_match<BidiIterator> sub_match_type;\n   typedef global_regex_namespace::match_results<BidiIterator, allocator_architype<sub_match_type> > match_results_type;\n   typedef global_regex_namespace::match_results<BidiIterator> match_results_default_type;\n   typedef output_iterator_archetype<value_type> OutIterator;\n   typedef typename regex_traits_computer<Regex>::type traits_type;\n   typedef global_regex_namespace::regex_iterator<BidiIterator, value_type, traits_type> regex_iterator_type;\n   typedef global_regex_namespace::regex_token_iterator<BidiIterator, value_type, traits_type> regex_token_iterator_type;\n\n   void global_constraints()\n   {\n      //\n      // test non-template components:\n      //\n      function_requires<BitmaskConcept<global_regex_namespace::regex_constants::syntax_option_type> >();\n      global_regex_namespace::regex_constants::syntax_option_type opts\n         = global_regex_namespace::regex_constants::icase\n         | global_regex_namespace::regex_constants::nosubs\n         | global_regex_namespace::regex_constants::optimize\n         | global_regex_namespace::regex_constants::collate\n         | global_regex_namespace::regex_constants::ECMAScript\n         | global_regex_namespace::regex_constants::basic\n         | global_regex_namespace::regex_constants::extended\n         | global_regex_namespace::regex_constants::awk\n         | global_regex_namespace::regex_constants::grep\n         | global_regex_namespace::regex_constants::egrep;\n      ignore_unused_variable_warning(opts);\n\n      function_requires<BitmaskConcept<global_regex_namespace::regex_constants::match_flag_type> >();\n      global_regex_namespace::regex_constants::match_flag_type mopts\n         = global_regex_namespace::regex_constants::match_default\n         | global_regex_namespace::regex_constants::match_not_bol\n         | global_regex_namespace::regex_constants::match_not_eol\n         | global_regex_namespace::regex_constants::match_not_bow\n         | global_regex_namespace::regex_constants::match_not_eow\n         | global_regex_namespace::regex_constants::match_any\n         | global_regex_namespace::regex_constants::match_not_null\n         | global_regex_namespace::regex_constants::match_continuous\n         | global_regex_namespace::regex_constants::match_prev_avail\n         | global_regex_namespace::regex_constants::format_default\n         | global_regex_namespace::regex_constants::format_sed\n         | global_regex_namespace::regex_constants::format_no_copy\n         | global_regex_namespace::regex_constants::format_first_only;\n      ignore_unused_variable_warning(mopts);\n\n      BOOST_STATIC_ASSERT((::boost::is_enum<global_regex_namespace::regex_constants::error_type>::value));\n      global_regex_namespace::regex_constants::error_type e1 = global_regex_namespace::regex_constants::error_collate;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_ctype;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_escape;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_backref;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_brack;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_paren;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_brace;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_badbrace;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_range;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_space;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_badrepeat;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_complexity;\n      ignore_unused_variable_warning(e1);\n      e1 = global_regex_namespace::regex_constants::error_stack;\n      ignore_unused_variable_warning(e1);\n\n      BOOST_STATIC_ASSERT((::boost::is_base_and_derived<std::runtime_error, global_regex_namespace::regex_error>::value  ));\n      const global_regex_namespace::regex_error except(e1);\n      e1 = except.code();\n\n      typedef typename Regex::value_type regex_value_type;\n      function_requires< RegexTraitsConcept<global_regex_namespace::regex_traits<char> > >();\n      function_requires< BaseRegexConcept<global_regex_namespace::basic_regex<char> > >();\n   }\n   void constraints() \n   {\n      global_constraints();\n\n      BOOST_STATIC_ASSERT((::boost::is_same< flag_type, global_regex_namespace::regex_constants::syntax_option_type>::value));\n      flag_type opts\n         = Regex::icase\n         | Regex::nosubs\n         | Regex::optimize\n         | Regex::collate\n         | Regex::ECMAScript\n         | Regex::basic\n         | Regex::extended\n         | Regex::awk\n         | Regex::grep\n         | Regex::egrep;\n      ignore_unused_variable_warning(opts);\n\n      function_requires<DefaultConstructibleConcept<Regex> >();\n      function_requires<CopyConstructibleConcept<Regex> >();\n\n      // Regex constructors:\n      Regex e1(m_pointer);\n      ignore_unused_variable_warning(e1);\n      Regex e2(m_pointer, m_flags);\n      ignore_unused_variable_warning(e2);\n      Regex e3(m_pointer, m_size, m_flags);\n      ignore_unused_variable_warning(e3);\n      Regex e4(in1, in2);\n      ignore_unused_variable_warning(e4);\n      Regex e5(in1, in2, m_flags);\n      ignore_unused_variable_warning(e5);\n\n      // assign etc:\n      Regex e;\n      e = m_pointer;\n      e = e1;\n      e.assign(e1);\n      e.assign(m_pointer);\n      e.assign(m_pointer, m_flags);\n      e.assign(m_pointer, m_size, m_flags);\n      e.assign(in1, in2);\n      e.assign(in1, in2, m_flags);\n\n      // access:\n      const Regex ce;\n      typename Regex::size_type i = ce.mark_count();\n      ignore_unused_variable_warning(i);\n      m_flags = ce.flags();\n      e.imbue(ce.getloc());\n      e.swap(e1);\n      \n      global_regex_namespace::swap(e, e1);\n\n      // sub_match:\n      BOOST_STATIC_ASSERT((::boost::is_base_and_derived<std::pair<BidiIterator, BidiIterator>, sub_match_type>::value));\n      typedef typename sub_match_type::value_type sub_value_type;\n      typedef typename sub_match_type::difference_type sub_diff_type;\n      typedef typename sub_match_type::iterator sub_iter_type;\n      BOOST_STATIC_ASSERT((::boost::is_same<sub_value_type, value_type>::value));\n      BOOST_STATIC_ASSERT((::boost::is_same<sub_iter_type, BidiIterator>::value));\n      bool b = m_sub.matched;\n      ignore_unused_variable_warning(b);\n      BidiIterator bi = m_sub.first;\n      ignore_unused_variable_warning(bi);\n      bi = m_sub.second;\n      ignore_unused_variable_warning(bi);\n      sub_diff_type diff = m_sub.length();\n      ignore_unused_variable_warning(diff);\n      // match_results tests - some typedefs are not used, however these\n      // guarante that they exist (some compilers may warn on non-usage)\n      typedef typename match_results_type::value_type mr_value_type;\n      typedef typename match_results_type::const_reference mr_const_reference;\n      typedef typename match_results_type::reference mr_reference;\n      typedef typename match_results_type::const_iterator mr_const_iterator;\n      typedef typename match_results_type::iterator mr_iterator;\n      typedef typename match_results_type::difference_type mr_difference_type;\n      typedef typename match_results_type::size_type mr_size_type;\n      typedef typename match_results_type::allocator_type mr_allocator_type;\n      typedef typename match_results_type::char_type mr_char_type;\n      typedef typename match_results_type::string_type mr_string_type;\n\n      match_results_type m1;\n      mr_allocator_type at;\n      match_results_type m2(at);\n      match_results_type m3(m1);\n      m1 = m2;\n\n      int ival = 0;\n\n      mr_size_type mrs = m_cresults.size();\n      ignore_unused_variable_warning(mrs);\n      mrs = m_cresults.max_size();\n      ignore_unused_variable_warning(mrs);\n      b = m_cresults.empty();\n      ignore_unused_variable_warning(b);\n      mr_difference_type mrd = m_cresults.length();\n      ignore_unused_variable_warning(mrd);\n      mrd = m_cresults.length(ival);\n      ignore_unused_variable_warning(mrd);\n      mrd = m_cresults.position();\n      ignore_unused_variable_warning(mrd);\n      mrd = m_cresults.position(mrs);\n      ignore_unused_variable_warning(mrd);\n\n      mr_const_reference mrcr = m_cresults[ival];\n      ignore_unused_variable_warning(mrcr);\n      mr_const_reference mrcr2 = m_cresults.prefix();\n      ignore_unused_variable_warning(mrcr2);\n      mr_const_reference mrcr3 = m_cresults.suffix();\n      ignore_unused_variable_warning(mrcr3);\n      mr_const_iterator mrci = m_cresults.begin();\n      ignore_unused_variable_warning(mrci);\n      mrci = m_cresults.end();\n      ignore_unused_variable_warning(mrci);\n\n      (void) m_cresults.get_allocator();\n      m_results.swap(m_results);\n      global_regex_namespace::swap(m_results, m_results);\n\n      // regex_match:\n      b = global_regex_namespace::regex_match(m_in, m_in, m_results, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_match(m_in, m_in, m_results, e, m_mft);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_match(m_in, m_in, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_match(m_in, m_in, e, m_mft);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_match(m_pointer, m_pmatch, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_match(m_pointer, m_pmatch, e, m_mft);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_match(m_pointer, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_match(m_pointer, e, m_mft);\n      ignore_unused_variable_warning(b);\n      // regex_search:\n      b = global_regex_namespace::regex_search(m_in, m_in, m_results, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_search(m_in, m_in, m_results, e, m_mft);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_search(m_in, m_in, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_search(m_in, m_in, e, m_mft);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_search(m_pointer, m_pmatch, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_search(m_pointer, m_pmatch, e, m_mft);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_search(m_pointer, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_search(m_pointer, e, m_mft);\n      ignore_unused_variable_warning(b);\n\n      // regex_iterator:\n      typedef typename regex_iterator_type::regex_type rit_regex_type;\n      typedef typename regex_iterator_type::value_type rit_value_type;\n      typedef typename regex_iterator_type::difference_type rit_difference_type;\n      typedef typename regex_iterator_type::pointer rit_pointer;\n      typedef typename regex_iterator_type::reference rit_reference;\n      typedef typename regex_iterator_type::iterator_category rit_iterator_category;\n      BOOST_STATIC_ASSERT((::boost::is_same<rit_regex_type, Regex>::value));\n      BOOST_STATIC_ASSERT((::boost::is_same<rit_value_type, match_results_default_type>::value));\n      BOOST_STATIC_ASSERT((::boost::is_same<rit_difference_type, std::ptrdiff_t>::value));\n      BOOST_STATIC_ASSERT((::boost::is_same<rit_pointer, const match_results_default_type*>::value));\n      BOOST_STATIC_ASSERT((::boost::is_same<rit_reference, const match_results_default_type&>::value));\n      BOOST_STATIC_ASSERT((::boost::is_convertible<rit_iterator_category*, std::forward_iterator_tag*>::value));\n      // this takes care of most of the checks needed:\n      function_requires<ForwardIteratorConcept<regex_iterator_type> >();\n      regex_iterator_type iter1(m_in, m_in, e);\n      ignore_unused_variable_warning(iter1);\n      regex_iterator_type iter2(m_in, m_in, e, m_mft);\n      ignore_unused_variable_warning(iter2);\n\n      // regex_token_iterator:\n      typedef typename regex_token_iterator_type::regex_type rtit_regex_type;\n      typedef typename regex_token_iterator_type::value_type rtit_value_type;\n      typedef typename regex_token_iterator_type::difference_type rtit_difference_type;\n      typedef typename regex_token_iterator_type::pointer rtit_pointer;\n      typedef typename regex_token_iterator_type::reference rtit_reference;\n      typedef typename regex_token_iterator_type::iterator_category rtit_iterator_category;\n      BOOST_STATIC_ASSERT((::boost::is_same<rtit_regex_type, Regex>::value));\n      BOOST_STATIC_ASSERT((::boost::is_same<rtit_value_type, sub_match_type>::value));\n      BOOST_STATIC_ASSERT((::boost::is_same<rtit_difference_type, std::ptrdiff_t>::value));\n      BOOST_STATIC_ASSERT((::boost::is_same<rtit_pointer, const sub_match_type*>::value));\n      BOOST_STATIC_ASSERT((::boost::is_same<rtit_reference, const sub_match_type&>::value));\n      BOOST_STATIC_ASSERT((::boost::is_convertible<rtit_iterator_category*, std::forward_iterator_tag*>::value));\n      // this takes care of most of the checks needed:\n      function_requires<ForwardIteratorConcept<regex_token_iterator_type> >();\n      regex_token_iterator_type ti1(m_in, m_in, e);\n      ignore_unused_variable_warning(ti1);\n      regex_token_iterator_type ti2(m_in, m_in, e, 0);\n      ignore_unused_variable_warning(ti2);\n      regex_token_iterator_type ti3(m_in, m_in, e, 0, m_mft);\n      ignore_unused_variable_warning(ti3);\n      std::vector<int> subs;\n      regex_token_iterator_type ti4(m_in, m_in, e, subs);\n      ignore_unused_variable_warning(ti4);\n      regex_token_iterator_type ti5(m_in, m_in, e, subs, m_mft);\n      ignore_unused_variable_warning(ti5);\n      static const int i_array[3] = { 1, 2, 3, };\n      regex_token_iterator_type ti6(m_in, m_in, e, i_array);\n      ignore_unused_variable_warning(ti6);\n      regex_token_iterator_type ti7(m_in, m_in, e, i_array, m_mft);\n      ignore_unused_variable_warning(ti7);\n   }\n\n   pointer_type m_pointer;\n   flag_type m_flags;\n   std::size_t m_size;\n   input_iterator_type in1, in2;\n   const sub_match_type m_sub;\n   const value_type m_char;\n   match_results_type m_results;\n   const match_results_type m_cresults;\n   OutIterator m_out;\n   BidiIterator m_in;\n   global_regex_namespace::regex_constants::match_flag_type m_mft;\n   global_regex_namespace::match_results<\n      pointer_type, \n      allocator_architype<global_regex_namespace::sub_match<pointer_type> > > \n      m_pmatch;\n\n   BaseRegexConcept();\n   BaseRegexConcept(const BaseRegexConcept&);\n   BaseRegexConcept& operator=(const BaseRegexConcept&);\n};\n\n//\n// RegexConcept:\n// Test every interface in the std:\n//\ntemplate <class Regex>\nstruct RegexConcept\n{\n   typedef typename Regex::value_type value_type;\n   //typedef typename Regex::size_type size_type;\n   typedef typename Regex::flag_type flag_type;\n   typedef typename Regex::locale_type locale_type;\n\n   // derived test types:\n   typedef const value_type* pointer_type;\n   typedef std::basic_string<value_type> string_type;\n   typedef boost::bidirectional_iterator_archetype<value_type> BidiIterator;\n   typedef global_regex_namespace::sub_match<BidiIterator> sub_match_type;\n   typedef global_regex_namespace::match_results<BidiIterator, allocator_architype<sub_match_type> > match_results_type;\n   typedef output_iterator_archetype<value_type> OutIterator;\n\n\n   void constraints() \n   {\n      function_requires<BaseRegexConcept<Regex> >();\n      // string based construct:\n      Regex e1(m_string);\n      ignore_unused_variable_warning(e1);\n      Regex e2(m_string, m_flags);\n      ignore_unused_variable_warning(e2);\n\n      // assign etc:\n      Regex e;\n      e = m_string;\n      e.assign(m_string);\n      e.assign(m_string, m_flags);\n\n      // sub_match:\n      string_type s(m_sub);\n      ignore_unused_variable_warning(s);\n      s = m_sub.str();\n      ignore_unused_variable_warning(s);\n      int i = m_sub.compare(m_string);\n      ignore_unused_variable_warning(i);\n\n      int i2 = m_sub.compare(m_sub);\n      ignore_unused_variable_warning(i2);\n      i2 = m_sub.compare(m_pointer);\n      ignore_unused_variable_warning(i2);\n\n      bool b = m_sub == m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_sub != m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_sub <= m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_sub <= m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_sub > m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_sub >= m_sub;\n      ignore_unused_variable_warning(b);\n\n      b = m_sub == m_pointer;\n      ignore_unused_variable_warning(b);\n      b = m_sub != m_pointer;\n      ignore_unused_variable_warning(b);\n      b = m_sub <= m_pointer;\n      ignore_unused_variable_warning(b);\n      b = m_sub <= m_pointer;\n      ignore_unused_variable_warning(b);\n      b = m_sub > m_pointer;\n      ignore_unused_variable_warning(b);\n      b = m_sub >= m_pointer;\n      ignore_unused_variable_warning(b);\n\n      b = m_pointer == m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_pointer != m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_pointer <= m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_pointer <= m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_pointer > m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_pointer >= m_sub;\n      ignore_unused_variable_warning(b);\n\n      b = m_sub == m_char;\n      ignore_unused_variable_warning(b);\n      b = m_sub != m_char;\n      ignore_unused_variable_warning(b);\n      b = m_sub <= m_char;\n      ignore_unused_variable_warning(b);\n      b = m_sub <= m_char;\n      ignore_unused_variable_warning(b);\n      b = m_sub > m_char;\n      ignore_unused_variable_warning(b);\n      b = m_sub >= m_char;\n      ignore_unused_variable_warning(b);\n\n      b = m_char == m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_char != m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_char <= m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_char <= m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_char > m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_char >= m_sub;\n      ignore_unused_variable_warning(b);\n\n      b = m_sub == m_string;\n      ignore_unused_variable_warning(b);\n      b = m_sub != m_string;\n      ignore_unused_variable_warning(b);\n      b = m_sub <= m_string;\n      ignore_unused_variable_warning(b);\n      b = m_sub <= m_string;\n      ignore_unused_variable_warning(b);\n      b = m_sub > m_string;\n      ignore_unused_variable_warning(b);\n      b = m_sub >= m_string;\n      ignore_unused_variable_warning(b);\n\n      b = m_string == m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_string != m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_string <= m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_string <= m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_string > m_sub;\n      ignore_unused_variable_warning(b);\n      b = m_string >= m_sub;\n      ignore_unused_variable_warning(b);\n\n      // match results:\n      m_string = m_results.str();\n      ignore_unused_variable_warning(m_string);\n      m_string = m_results.str(0);\n      ignore_unused_variable_warning(m_string);\n      m_out = m_cresults.format(m_out, m_string);\n      m_out = m_cresults.format(m_out, m_string, m_mft);\n      m_string = m_cresults.format(m_string);\n      ignore_unused_variable_warning(m_string);\n      m_string = m_cresults.format(m_string, m_mft);\n      ignore_unused_variable_warning(m_string);\n\n      // regex_match:\n      b = global_regex_namespace::regex_match(m_string, m_smatch, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_match(m_string, m_smatch, e, m_mft);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_match(m_string, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_match(m_string, e, m_mft);\n      ignore_unused_variable_warning(b);\n\n      // regex_search:\n      b = global_regex_namespace::regex_search(m_string, m_smatch, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_search(m_string, m_smatch, e, m_mft);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_search(m_string, e);\n      ignore_unused_variable_warning(b);\n      b = global_regex_namespace::regex_search(m_string, e, m_mft);\n      ignore_unused_variable_warning(b);\n\n      // regex_replace:\n      m_out = global_regex_namespace::regex_replace(m_out, m_in, m_in, e, m_string, m_mft);\n      m_out = global_regex_namespace::regex_replace(m_out, m_in, m_in, e, m_string);\n      m_string = global_regex_namespace::regex_replace(m_string, e, m_string, m_mft);\n      ignore_unused_variable_warning(m_string);\n      m_string = global_regex_namespace::regex_replace(m_string, e, m_string);\n      ignore_unused_variable_warning(m_string);\n\n   }\n\n   flag_type m_flags;\n   string_type m_string;\n   const sub_match_type m_sub;\n   match_results_type m_results;\n   pointer_type m_pointer;\n   value_type m_char;\n   const match_results_type m_cresults;\n   OutIterator m_out;\n   BidiIterator m_in;\n   global_regex_namespace::regex_constants::match_flag_type m_mft;\n   global_regex_namespace::match_results<typename string_type::const_iterator, allocator_architype<global_regex_namespace::sub_match<typename string_type::const_iterator> > > m_smatch;\n\n   RegexConcept();\n   RegexConcept(const RegexConcept&);\n   RegexConcept& operator=(const RegexConcept&);\n};\n\n#ifndef BOOST_REGEX_TEST_STD\n\ntemplate <class M>\nstruct functor1\n{\n   typedef typename M::char_type char_type;\n   const char_type* operator()(const M&)const\n   {\n      static const char_type c = static_cast<char_type>(0);\n      return &c;\n   }\n};\ntemplate <class M>\nstruct functor1b\n{\n   typedef typename M::char_type char_type;\n   std::vector<char_type> operator()(const M&)const\n   {\n      static const std::vector<char_type> c;\n      return c;\n   }\n};\ntemplate <class M>\nstruct functor2\n{\n   template <class O>\n   O operator()(const M& /*m*/, O i)const\n   {\n      return i;\n   }\n};\ntemplate <class M>\nstruct functor3\n{\n   template <class O>\n   O operator()(const M& /*m*/, O i, regex_constants::match_flag_type)const\n   {\n      return i;\n   }\n};\n\n//\n// BoostRegexConcept:\n// Test every interface in the Boost implementation:\n//\ntemplate <class Regex>\nstruct BoostRegexConcept\n{\n   typedef typename Regex::value_type value_type;\n   typedef typename Regex::size_type size_type;\n   typedef typename Regex::flag_type flag_type;\n   typedef typename Regex::locale_type locale_type;\n\n   // derived test types:\n   typedef const value_type* pointer_type;\n   typedef std::basic_string<value_type> string_type;\n   typedef typename Regex::const_iterator const_iterator;\n   typedef bidirectional_iterator_archetype<value_type> BidiIterator;\n   typedef output_iterator_archetype<value_type> OutputIterator;\n   typedef global_regex_namespace::sub_match<BidiIterator> sub_match_type;\n   typedef global_regex_namespace::match_results<BidiIterator, allocator_architype<sub_match_type> > match_results_type;\n   typedef global_regex_namespace::match_results<BidiIterator> match_results_default_type;\n\n   void constraints() \n   {\n      global_regex_namespace::regex_constants::match_flag_type mopts\n         = global_regex_namespace::regex_constants::match_default\n         | global_regex_namespace::regex_constants::match_not_bol\n         | global_regex_namespace::regex_constants::match_not_eol\n         | global_regex_namespace::regex_constants::match_not_bow\n         | global_regex_namespace::regex_constants::match_not_eow\n         | global_regex_namespace::regex_constants::match_any\n         | global_regex_namespace::regex_constants::match_not_null\n         | global_regex_namespace::regex_constants::match_continuous\n         | global_regex_namespace::regex_constants::match_partial\n         | global_regex_namespace::regex_constants::match_prev_avail\n         | global_regex_namespace::regex_constants::format_default\n         | global_regex_namespace::regex_constants::format_sed\n         | global_regex_namespace::regex_constants::format_perl\n         | global_regex_namespace::regex_constants::format_no_copy\n         | global_regex_namespace::regex_constants::format_first_only;\n\n      (void)mopts;\n\n      function_requires<RegexConcept<Regex> >();\n      const global_regex_namespace::regex_error except(global_regex_namespace::regex_constants::error_collate);\n      std::ptrdiff_t pt = except.position();\n      ignore_unused_variable_warning(pt);\n      const Regex ce, ce2;\n#ifndef BOOST_NO_STD_LOCALE\n      m_stream << ce;\n#endif\n      unsigned i = ce.error_code();\n      ignore_unused_variable_warning(i);\n      pointer_type p = ce.expression();\n      ignore_unused_variable_warning(p);\n      int i2 = ce.compare(ce2);\n      ignore_unused_variable_warning(i2);\n      bool b = ce == ce2;\n      ignore_unused_variable_warning(b);\n      b = ce.empty();\n      ignore_unused_variable_warning(b);\n      b = ce != ce2;\n      ignore_unused_variable_warning(b);\n      b = ce < ce2;\n      ignore_unused_variable_warning(b);\n      b = ce > ce2;\n      ignore_unused_variable_warning(b);\n      b = ce <= ce2;\n      ignore_unused_variable_warning(b);\n      b = ce >= ce2;\n      ignore_unused_variable_warning(b);\n      i = ce.status();\n      ignore_unused_variable_warning(i);\n      size_type s = ce.max_size();\n      ignore_unused_variable_warning(s);\n      s = ce.size();\n      ignore_unused_variable_warning(s);\n      const_iterator pi = ce.begin();\n      ignore_unused_variable_warning(pi);\n      pi = ce.end();\n      ignore_unused_variable_warning(pi);\n      string_type s2 = ce.str();\n      ignore_unused_variable_warning(s2);\n\n      m_string = m_sub + m_sub;\n      ignore_unused_variable_warning(m_string);\n      m_string = m_sub + m_pointer;\n      ignore_unused_variable_warning(m_string);\n      m_string = m_pointer + m_sub;\n      ignore_unused_variable_warning(m_string);\n      m_string = m_sub + m_string;\n      ignore_unused_variable_warning(m_string);\n      m_string = m_string + m_sub;\n      ignore_unused_variable_warning(m_string);\n      m_string = m_sub + m_char;\n      ignore_unused_variable_warning(m_string);\n      m_string = m_char + m_sub;\n      ignore_unused_variable_warning(m_string);\n\n      // Named sub-expressions:\n      m_sub = m_cresults[&m_char];\n      ignore_unused_variable_warning(m_sub);\n      m_sub = m_cresults[m_string];\n      ignore_unused_variable_warning(m_sub);\n      m_sub = m_cresults[\"\"];\n      ignore_unused_variable_warning(m_sub);\n      m_sub = m_cresults[std::string(\"\")];\n      ignore_unused_variable_warning(m_sub);\n      m_string = m_cresults.str(&m_char);\n      ignore_unused_variable_warning(m_string);\n      m_string = m_cresults.str(m_string);\n      ignore_unused_variable_warning(m_string);\n      m_string = m_cresults.str(\"\");\n      ignore_unused_variable_warning(m_string);\n      m_string = m_cresults.str(std::string(\"\"));\n      ignore_unused_variable_warning(m_string);\n\n      typename match_results_type::difference_type diff;\n      diff = m_cresults.length(&m_char);\n      ignore_unused_variable_warning(diff);\n      diff = m_cresults.length(m_string);\n      ignore_unused_variable_warning(diff);\n      diff = m_cresults.length(\"\");\n      ignore_unused_variable_warning(diff);\n      diff = m_cresults.length(std::string(\"\"));\n      ignore_unused_variable_warning(diff);\n      diff = m_cresults.position(&m_char);\n      ignore_unused_variable_warning(diff);\n      diff = m_cresults.position(m_string);\n      ignore_unused_variable_warning(diff);\n      diff = m_cresults.position(\"\");\n      ignore_unused_variable_warning(diff);\n      diff = m_cresults.position(std::string(\"\"));\n      ignore_unused_variable_warning(diff);\n\n#ifndef BOOST_NO_STD_LOCALE\n      m_stream << m_sub;\n      m_stream << m_cresults;\n#endif\n      //\n      // Extended formatting with a functor:\n      //\n      regex_constants::match_flag_type f = regex_constants::match_default;\n      OutputIterator out = static_object<OutputIterator>::get();\n      \n      functor3<match_results_default_type> func3;\n      functor2<match_results_default_type> func2;\n      functor1<match_results_default_type> func1;\n      \n      functor3<match_results_type> func3b;\n      functor2<match_results_type> func2b;\n      functor1<match_results_type> func1b;\n\n      out = regex_format(out, m_cresults, func3b, f);\n      out = regex_format(out, m_cresults, func3b);\n      out = regex_format(out, m_cresults, func2b, f);\n      out = regex_format(out, m_cresults, func2b);\n      out = regex_format(out, m_cresults, func1b, f);\n      out = regex_format(out, m_cresults, func1b);\n      out = regex_format(out, m_cresults, RW_NS::ref(func3b), f);\n      out = regex_format(out, m_cresults, RW_NS::ref(func3b));\n      out = regex_format(out, m_cresults, RW_NS::ref(func2b), f);\n      out = regex_format(out, m_cresults, RW_NS::ref(func2b));\n      out = regex_format(out, m_cresults, RW_NS::ref(func1b), f);\n      out = regex_format(out, m_cresults, RW_NS::ref(func1b));\n      out = regex_format(out, m_cresults, RW_NS::cref(func3b), f);\n      out = regex_format(out, m_cresults, RW_NS::cref(func3b));\n      out = regex_format(out, m_cresults, RW_NS::cref(func2b), f);\n      out = regex_format(out, m_cresults, RW_NS::cref(func2b));\n      out = regex_format(out, m_cresults, RW_NS::cref(func1b), f);\n      out = regex_format(out, m_cresults, RW_NS::cref(func1b));\n      m_string += regex_format(m_cresults, func3b, f);\n      m_string += regex_format(m_cresults, func3b);\n      m_string += regex_format(m_cresults, func2b, f);\n      m_string += regex_format(m_cresults, func2b);\n      m_string += regex_format(m_cresults, func1b, f);\n      m_string += regex_format(m_cresults, func1b);\n      m_string += regex_format(m_cresults, RW_NS::ref(func3b), f);\n      m_string += regex_format(m_cresults, RW_NS::ref(func3b));\n      m_string += regex_format(m_cresults, RW_NS::ref(func2b), f);\n      m_string += regex_format(m_cresults, RW_NS::ref(func2b));\n      m_string += regex_format(m_cresults, RW_NS::ref(func1b), f);\n      m_string += regex_format(m_cresults, RW_NS::ref(func1b));\n      m_string += regex_format(m_cresults, RW_NS::cref(func3b), f);\n      m_string += regex_format(m_cresults, RW_NS::cref(func3b));\n      m_string += regex_format(m_cresults, RW_NS::cref(func2b), f);\n      m_string += regex_format(m_cresults, RW_NS::cref(func2b));\n      m_string += regex_format(m_cresults, RW_NS::cref(func1b), f);\n      m_string += regex_format(m_cresults, RW_NS::cref(func1b));\n\n      out = m_cresults.format(out, func3b, f);\n      out = m_cresults.format(out, func3b);\n      out = m_cresults.format(out, func2b, f);\n      out = m_cresults.format(out, func2b);\n      out = m_cresults.format(out, func1b, f);\n      out = m_cresults.format(out, func1b);\n      out = m_cresults.format(out, RW_NS::ref(func3b), f);\n      out = m_cresults.format(out, RW_NS::ref(func3b));\n      out = m_cresults.format(out, RW_NS::ref(func2b), f);\n      out = m_cresults.format(out, RW_NS::ref(func2b));\n      out = m_cresults.format(out, RW_NS::ref(func1b), f);\n      out = m_cresults.format(out, RW_NS::ref(func1b));\n      out = m_cresults.format(out, RW_NS::cref(func3b), f);\n      out = m_cresults.format(out, RW_NS::cref(func3b));\n      out = m_cresults.format(out, RW_NS::cref(func2b), f);\n      out = m_cresults.format(out, RW_NS::cref(func2b));\n      out = m_cresults.format(out, RW_NS::cref(func1b), f);\n      out = m_cresults.format(out, RW_NS::cref(func1b));\n\n      m_string += m_cresults.format(func3b, f);\n      m_string += m_cresults.format(func3b);\n      m_string += m_cresults.format(func2b, f);\n      m_string += m_cresults.format(func2b);\n      m_string += m_cresults.format(func1b, f);\n      m_string += m_cresults.format(func1b);\n      m_string += m_cresults.format(RW_NS::ref(func3b), f);\n      m_string += m_cresults.format(RW_NS::ref(func3b));\n      m_string += m_cresults.format(RW_NS::ref(func2b), f);\n      m_string += m_cresults.format(RW_NS::ref(func2b));\n      m_string += m_cresults.format(RW_NS::ref(func1b), f);\n      m_string += m_cresults.format(RW_NS::ref(func1b));\n      m_string += m_cresults.format(RW_NS::cref(func3b), f);\n      m_string += m_cresults.format(RW_NS::cref(func3b));\n      m_string += m_cresults.format(RW_NS::cref(func2b), f);\n      m_string += m_cresults.format(RW_NS::cref(func2b));\n      m_string += m_cresults.format(RW_NS::cref(func1b), f);\n      m_string += m_cresults.format(RW_NS::cref(func1b));\n\n      out = regex_replace(out, m_in, m_in, ce, func3, f);\n      out = regex_replace(out, m_in, m_in, ce, func3);\n      out = regex_replace(out, m_in, m_in, ce, func2, f);\n      out = regex_replace(out, m_in, m_in, ce, func2);\n      out = regex_replace(out, m_in, m_in, ce, func1, f);\n      out = regex_replace(out, m_in, m_in, ce, func1);\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func3), f);\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func3));\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func2), f);\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func2));\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func1), f);\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::ref(func1));\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func3), f);\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func3));\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func2), f);\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func2));\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func1), f);\n      out = regex_replace(out, m_in, m_in, ce, RW_NS::cref(func1));\n\n      functor3<match_results<typename string_type::const_iterator> > func3s;\n      functor2<match_results<typename string_type::const_iterator> > func2s;\n      functor1<match_results<typename string_type::const_iterator> > func1s;\n      m_string += regex_replace(m_string, ce, func3s, f);\n      m_string += regex_replace(m_string, ce, func3s);\n      m_string += regex_replace(m_string, ce, func2s, f);\n      m_string += regex_replace(m_string, ce, func2s);\n      m_string += regex_replace(m_string, ce, func1s, f);\n      m_string += regex_replace(m_string, ce, func1s);\n      m_string += regex_replace(m_string, ce, RW_NS::ref(func3s), f);\n      m_string += regex_replace(m_string, ce, RW_NS::ref(func3s));\n      m_string += regex_replace(m_string, ce, RW_NS::ref(func2s), f);\n      m_string += regex_replace(m_string, ce, RW_NS::ref(func2s));\n      m_string += regex_replace(m_string, ce, RW_NS::ref(func1s), f);\n      m_string += regex_replace(m_string, ce, RW_NS::ref(func1s));\n      m_string += regex_replace(m_string, ce, RW_NS::cref(func3s), f);\n      m_string += regex_replace(m_string, ce, RW_NS::cref(func3s));\n      m_string += regex_replace(m_string, ce, RW_NS::cref(func2s), f);\n      m_string += regex_replace(m_string, ce, RW_NS::cref(func2s));\n      m_string += regex_replace(m_string, ce, RW_NS::cref(func1s), f);\n      m_string += regex_replace(m_string, ce, RW_NS::cref(func1s));\n   }\n\n   std::basic_ostream<value_type> m_stream;\n   sub_match_type m_sub;\n   pointer_type m_pointer;\n   string_type m_string;\n   const value_type m_char;\n   match_results_type m_results;\n   const match_results_type m_cresults;\n   BidiIterator m_in;\n\n   BoostRegexConcept();\n   BoostRegexConcept(const BoostRegexConcept&);\n   BoostRegexConcept& operator=(const BoostRegexConcept&);\n};\n\n#endif // BOOST_REGEX_TEST_STD\n\n}\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/config/borland.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         boost/regex/config/borland.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: regex borland-specific config setup.\n  */\n\n\n#if defined(__BORLANDC__) && !defined(__clang__)\n#  if (__BORLANDC__ == 0x550) || (__BORLANDC__ == 0x551)\n      // problems with std::basic_string and dll RTL:\n#     if defined(_RTLDLL) && defined(_RWSTD_COMPILE_INSTANTIATE)\n#        ifdef BOOST_REGEX_BUILD_DLL\n#           error _RWSTD_COMPILE_INSTANTIATE must not be defined when building regex++ as a DLL\n#        else\n#           pragma message(\"Defining _RWSTD_COMPILE_INSTANTIATE when linking to the DLL version of the RTL may produce memory corruption problems in std::basic_string, as a result of separate versions of basic_string's static data in the RTL and you're exe/dll: be warned!!\")\n#        endif\n#     endif\n#     ifndef _RTLDLL\n         // this is harmless for a staic link:\n#        define _RWSTD_COMPILE_INSTANTIATE\n#     endif\n      // external templates cause problems for some reason:\n#     define BOOST_REGEX_NO_EXTERNAL_TEMPLATES\n#  endif\n#  if (__BORLANDC__ <= 0x540) && !defined(BOOST_REGEX_NO_LIB) && !defined(_NO_VCL)\n      // C++ Builder 4 and earlier, we can't tell whether we should be using\n      // the VCL runtime or not, do a static link instead:\n#     define BOOST_REGEX_STATIC_LINK\n#  endif\n   //\n   // VCL support:\n   // if we're building a console app then there can't be any VCL (can there?)\n#  if !defined(__CONSOLE__) && !defined(_NO_VCL)\n#     define BOOST_REGEX_USE_VCL\n#  endif\n   //\n   // if this isn't Win32 then don't automatically select link\n   // libraries:\n   //\n#  ifndef _Windows\n#     ifndef BOOST_REGEX_NO_LIB\n#        define BOOST_REGEX_NO_LIB\n#     endif\n#     ifndef BOOST_REGEX_STATIC_LINK\n#        define BOOST_REGEX_STATIC_LINK\n#     endif\n#  endif\n\n#if __BORLANDC__ < 0x600\n//\n// string workarounds:\n//\n#include <cstring>\n#undef strcmp\n#undef strcpy\n#endif\n\n#endif\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/config/cwchar.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         boost/regex/config/cwchar.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: regex wide character string fixes.\n  */\n\n#ifndef BOOST_REGEX_CONFIG_CWCHAR_HPP\n#define BOOST_REGEX_CONFIG_CWCHAR_HPP\n\n#include <cwchar>\n#include <cwctype>\n#include <boost/config.hpp>\n\n#if defined(__STD_RWCOMPILER_H__) || defined(_RWSTD_VER)\n// apparently this is required for the RW STL on Linux:\n#undef iswalnum\n#undef iswalpha\n#undef iswblank\n#undef iswcntrl\n#undef iswdigit\n#undef iswgraph\n#undef iswlower\n#undef iswprint\n#undef iswprint\n#undef iswpunct\n#undef iswspace\n#undef iswupper\n#undef iswxdigit\n#undef iswctype\n#undef towlower\n#undef towupper\n#undef towctrans\n#undef wctrans\n#undef wctype\n#endif\n\nnamespace std{\n\n#ifndef BOOST_NO_STDC_NAMESPACE\nextern \"C\"{\n#endif\n\n#ifdef iswalnum\ninline int (iswalnum)(wint_t i)\n{ return iswalnum(i); }\n#undef iswalnum\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::iswalnum;\n#endif\n\n#ifdef iswalpha\ninline int (iswalpha)(wint_t i)\n{ return iswalpha(i); }\n#undef iswalpha\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::iswalpha;\n#endif\n\n#ifdef iswcntrl\ninline int (iswcntrl)(wint_t i)\n{ return iswcntrl(i); }\n#undef iswcntrl\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::iswcntrl;\n#endif\n\n#ifdef iswdigit\ninline int (iswdigit)(wint_t i)\n{ return iswdigit(i); }\n#undef iswdigit\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::iswdigit;\n#endif\n\n#ifdef iswgraph\ninline int (iswgraph)(wint_t i)\n{ return iswgraph(i); }\n#undef iswgraph\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::iswgraph;\n#endif\n\n#ifdef iswlower\ninline int (iswlower)(wint_t i)\n{ return iswlower(i); }\n#undef iswlower\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::iswlower;\n#endif\n\n#ifdef iswprint\ninline int (iswprint)(wint_t i)\n{ return iswprint(i); }\n#undef iswprint\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::iswprint;\n#endif\n\n#ifdef iswpunct\ninline int (iswpunct)(wint_t i)\n{ return iswpunct(i); }\n#undef iswpunct\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::iswpunct;\n#endif\n\n#ifdef iswspace\ninline int (iswspace)(wint_t i)\n{ return iswspace(i); }\n#undef iswspace\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::iswspace;\n#endif\n\n#ifdef iswupper\ninline int (iswupper)(wint_t i)\n{ return iswupper(i); }\n#undef iswupper\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::iswupper;\n#endif\n\n#ifdef iswxdigit\ninline int (iswxdigit)(wint_t i)\n{ return iswxdigit(i); }\n#undef iswxdigit\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::iswxdigit;\n#endif\n\n#ifdef towlower\ninline wint_t (towlower)(wint_t i)\n{ return towlower(i); }\n#undef towlower\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::towlower;\n#endif\n\n#ifdef towupper\ninline wint_t (towupper)(wint_t i)\n{ return towupper(i); }\n#undef towupper\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing :: towupper;\n#endif\n\n#ifdef wcscmp\ninline int (wcscmp)(const wchar_t *p1, const wchar_t *p2)\n{ return wcscmp(p1,p2); }\n#undef wcscmp\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::wcscmp;\n#endif\n\n#ifdef wcscoll\ninline int (wcscoll)(const wchar_t *p1, const wchar_t *p2)\n{ return wcscoll(p1,p2); }\n#undef wcscoll\n#elif defined(BOOST_NO_STDC_NAMESPACE) && !defined(UNDER_CE)\nusing ::wcscoll;\n#endif\n\n#ifdef wcscpy\ninline wchar_t *(wcscpy)(wchar_t *p1, const wchar_t *p2)\n{ return wcscpy(p1,p2); }\n#undef wcscpy\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::wcscpy;\n#endif\n\n#ifdef wcslen\ninline size_t (wcslen)(const wchar_t *p)\n{ return wcslen(p); }\n#undef wcslen\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::wcslen;\n#endif\n\n#ifdef wcsxfrm\nsize_t wcsxfrm(wchar_t *p1, const wchar_t *p2, size_t s)\n{ return wcsxfrm(p1,p2,s); }\n#undef wcsxfrm\n#elif defined(BOOST_NO_STDC_NAMESPACE)\nusing ::wcsxfrm;\n#endif\n\n\n#ifndef BOOST_NO_STDC_NAMESPACE\n} // extern \"C\"\n#endif\n\n} // namespace std\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/config.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the\n * Boost Software License, Version 1.0. (See accompanying file\n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         config.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: regex extended config setup.\n  */\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#define BOOST_REGEX_CONFIG_HPP\n\n#if !((__cplusplus >= 201103L) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) || defined(BOOST_REGEX_CXX03))\n#  define BOOST_REGEX_CXX03\n#endif\n\n#if defined(BOOST_REGEX_RECURSIVE) && !defined(BOOST_REGEX_CXX03)\n#  define BOOST_REGEX_CXX03\n#endif\n\n#if defined(__has_include)\n#if !defined(BOOST_REGEX_STANDALONE) && !__has_include(<boost/version.hpp>)\n#define BOOST_REGEX_STANDALONE\n#endif\n#endif\n\n/*\n * Borland C++ Fix/error check\n * this has to go *before* we include any std lib headers:\n */\n#if defined(__BORLANDC__) && !defined(__clang__)\n#  include <boost/regex/config/borland.hpp>\n#endif\n#ifndef BOOST_REGEX_STANDALONE\n#include <boost/version.hpp>\n#endif\n\n/*************************************************************************\n*\n* Asserts:\n*\n*************************************************************************/\n\n#ifdef BOOST_REGEX_STANDALONE\n#include <cassert>\n#  define BOOST_REGEX_ASSERT(x) assert(x)\n#else\n#include <boost/assert.hpp>\n#  define BOOST_REGEX_ASSERT(x) BOOST_ASSERT(x)\n#endif\n\n/*****************************************************************************\n *\n *  Include all the headers we need here:\n *\n ****************************************************************************/\n\n#ifdef __cplusplus\n\n#  ifndef BOOST_REGEX_USER_CONFIG\n#     define BOOST_REGEX_USER_CONFIG <boost/regex/user.hpp>\n#  endif\n\n#  include BOOST_REGEX_USER_CONFIG\n\n#ifndef BOOST_REGEX_STANDALONE\n#  include <boost/config.hpp>\n#  include <boost/predef.h>\n#endif\n\n#else\n   /*\n    * C build,\n    * don't include <boost/config.hpp> because that may\n    * do C++ specific things in future...\n    */\n#  include <stdlib.h>\n#  include <stddef.h>\n#  ifdef _MSC_VER\n#     define BOOST_MSVC _MSC_VER\n#  endif\n#endif\n\n\n/****************************************************************************\n*\n* Legacy support:\n*\n*******************************************************************************/\n\n#if defined(BOOST_NO_STD_LOCALE) || defined(BOOST_NO_CXX11_HDR_MUTEX) || defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) \\\n   || defined(BOOST_NO_CXX11_HDR_ATOMIC) || defined(BOOST_NO_CXX11_ALLOCATOR) || defined(BOOST_NO_CXX11_SMART_PTR) \\\n   || defined(BOOST_NO_CXX11_STATIC_ASSERT) || defined(BOOST_NO_NOEXCEPT)\n#ifndef BOOST_REGEX_CXX03\n#  define BOOST_REGEX_CXX03\n#endif\n#endif\n\n/*****************************************************************************\n *\n *  Boilerplate regex config options:\n *\n ****************************************************************************/\n\n/* Obsolete macro, use BOOST_VERSION instead: */\n#define BOOST_RE_VERSION 500\n\n/* fix: */\n#if defined(_UNICODE) && !defined(UNICODE)\n#define UNICODE\n#endif\n\n#define BOOST_REGEX_JOIN(X, Y) BOOST_REGEX_DO_JOIN(X, Y)\n#define BOOST_REGEX_DO_JOIN(X, Y) BOOST_REGEX_DO_JOIN2(X,Y)\n#define BOOST_REGEX_DO_JOIN2(X, Y) X##Y\n\n#ifdef BOOST_FALLTHROUGH\n#  define BOOST_REGEX_FALLTHROUGH BOOST_FALLTHROUGH\n#else\n\n#if defined(__clang__) && (__cplusplus >= 201103L) && defined(__has_warning)\n#  if __has_feature(cxx_attributes) && __has_warning(\"-Wimplicit-fallthrough\")\n#    define BOOST_REGEX_FALLTHROUGH [[clang::fallthrough]]\n#  endif\n#endif\n#if !defined(BOOST_REGEX_FALLTHROUGH) && defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1800) && (__cplusplus >= 201703)\n#  define BOOST_REGEX_FALLTHROUGH [[fallthrough]]\n#endif\n#if !defined(BOOST_REGEX_FALLTHROUGH) && defined(__GNUC__) && (__GNUC__ >= 7)\n#  define BOOST_REGEX_FALLTHROUGH __attribute__((fallthrough))\n#endif\n\n#if !defined(BOOST_REGEX_FALLTHROUGH)\n#  define BOOST_REGEX_FALLTHROUGH\n#endif\n#endif\n\n#ifdef BOOST_NORETURN\n#  define BOOST_REGEX_NORETURN BOOST_NORETURN\n#else\n#  define BOOST_REGEX_NORETURN\n#endif\n\n\n/*\n* Define a macro for the namespace that details are placed in, this includes the Boost\n* version number to avoid mismatched header and library versions:\n*/\n#define BOOST_REGEX_DETAIL_NS BOOST_REGEX_JOIN(re_detail_, BOOST_RE_VERSION)\n\n/*\n * Fix for gcc prior to 3.4: std::ctype<wchar_t> doesn't allow\n * masks to be combined, for example:\n * std::use_facet<std::ctype<wchar_t> >.is(std::ctype_base::lower|std::ctype_base::upper, L'a');\n * returns *false*.\n */\n#if defined(__GLIBCPP__) && defined(BOOST_REGEX_CXX03)\n#  define BOOST_REGEX_BUGGY_CTYPE_FACET\n#endif\n\n/*\n * If there isn't good enough wide character support then there will\n * be no wide character regular expressions:\n */\n#if (defined(BOOST_NO_CWCHAR) || defined(BOOST_NO_CWCTYPE) || defined(BOOST_NO_STD_WSTRING))\n#  if !defined(BOOST_NO_WREGEX)\n#     define BOOST_NO_WREGEX\n#  endif\n#else\n#  if defined(__sgi) && (defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION))\n      /* STLPort on IRIX is misconfigured: <cwctype> does not compile\n       * as a temporary fix include <wctype.h> instead and prevent inclusion\n       * of STLPort version of <cwctype> */\n#     include <wctype.h>\n#     define __STLPORT_CWCTYPE\n#     define _STLP_CWCTYPE\n#  endif\n\n#if defined(__cplusplus) && defined(BOOST_REGEX_CXX03)\n#  include <boost/regex/config/cwchar.hpp>\n#endif\n\n#endif\n\n/*\n * If Win32 support has been disabled for boost in general, then\n * it is for regex in particular:\n */\n#if defined(BOOST_DISABLE_WIN32) && !defined(BOOST_REGEX_NO_W32)\n#  define BOOST_REGEX_NO_W32\n#endif\n\n/* disable our own file-iterators and mapfiles if we can't\n * support them: */\n#if defined(_WIN32)\n#  if defined(BOOST_REGEX_NO_W32) || BOOST_PLAT_WINDOWS_RUNTIME\n#    define BOOST_REGEX_NO_FILEITER\n#  endif\n#else /* defined(_WIN32) */\n#  if !defined(BOOST_HAS_DIRENT_H)\n#    define BOOST_REGEX_NO_FILEITER\n#  endif\n#endif\n\n/* backwards compatibitity: */\n#if defined(BOOST_RE_NO_LIB)\n#  define BOOST_REGEX_NO_LIB\n#endif\n\n#if defined(__GNUC__) && !defined(_MSC_VER) && (defined(_WIN32) || defined(__CYGWIN__))\n/* gcc on win32 has problems if you include <windows.h>\n   (sporadically generates bad code). */\n#  define BOOST_REGEX_NO_W32\n#endif\n#if defined(__COMO__) && !defined(BOOST_REGEX_NO_W32) && !defined(_MSC_EXTENSIONS)\n#  define BOOST_REGEX_NO_W32\n#endif\n\n#ifdef BOOST_REGEX_STANDALONE\n#  if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__)\n#     define BOOST_REGEX_MSVC _MSC_VER\n#endif\n#elif defined(BOOST_MSVC)\n#  define BOOST_REGEX_MSVC BOOST_MSVC\n#endif\n\n\n/*****************************************************************************\n *\n *  Set up dll import/export options:\n *\n ****************************************************************************/\n\n#if (defined(BOOST_REGEX_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)) && !defined(BOOST_REGEX_STATIC_LINK) && defined(BOOST_SYMBOL_IMPORT)\n#  if defined(BOOST_REGEX_SOURCE)\n#     define BOOST_REGEX_BUILD_DLL\n#     define BOOST_REGEX_DECL BOOST_SYMBOL_EXPORT\n#  else\n#     define BOOST_REGEX_DECL BOOST_SYMBOL_IMPORT\n#  endif\n#else\n#  define BOOST_REGEX_DECL\n#endif\n\n#ifdef BOOST_REGEX_CXX03\n#if !defined(BOOST_REGEX_NO_LIB) && !defined(BOOST_REGEX_SOURCE) && !defined(BOOST_ALL_NO_LIB) && defined(__cplusplus)\n#  define BOOST_LIB_NAME boost_regex\n#  if defined(BOOST_REGEX_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)\n#     define BOOST_DYN_LINK\n#  endif\n#  ifdef BOOST_REGEX_DIAG\n#     define BOOST_LIB_DIAGNOSTIC\n#  endif\n#  include <boost/config/auto_link.hpp>\n#endif\n#endif\n\n/*****************************************************************************\n *\n *  Set up function call type:\n *\n ****************************************************************************/\n\n#if defined(_MSC_VER) && defined(_MSC_EXTENSIONS)\n#if defined(_DEBUG) || defined(__MSVC_RUNTIME_CHECKS) || defined(_MANAGED) || defined(BOOST_REGEX_NO_FASTCALL)\n#  define BOOST_REGEX_CALL __cdecl\n#else\n#  define BOOST_REGEX_CALL __fastcall\n#endif\n#  define BOOST_REGEX_CCALL __cdecl\n#endif\n\n#if defined(__BORLANDC__) && !defined(BOOST_DISABLE_WIN32)\n#if defined(__clang__)\n#  define BOOST_REGEX_CALL __cdecl\n#  define BOOST_REGEX_CCALL __cdecl\n#else\n#  define BOOST_REGEX_CALL __fastcall\n#  define BOOST_REGEX_CCALL __stdcall\n#endif\n#endif\n\n#ifndef BOOST_REGEX_CALL\n#  define BOOST_REGEX_CALL\n#endif\n#ifndef BOOST_REGEX_CCALL\n#define BOOST_REGEX_CCALL\n#endif\n\n/*****************************************************************************\n *\n *  Set up localisation model:\n *\n ****************************************************************************/\n\n/* backwards compatibility: */\n#ifdef BOOST_RE_LOCALE_C\n#  define BOOST_REGEX_USE_C_LOCALE\n#endif\n\n#ifdef BOOST_RE_LOCALE_CPP\n#  define BOOST_REGEX_USE_CPP_LOCALE\n#endif\n\n#if defined(__CYGWIN__)\n#  define BOOST_REGEX_USE_C_LOCALE\n#endif\n\n/* use C++ locale when targeting windows store */\n#if BOOST_PLAT_WINDOWS_RUNTIME\n#  define BOOST_REGEX_USE_CPP_LOCALE\n#  define BOOST_REGEX_NO_WIN32_LOCALE\n#endif\n\n/* Win32 defaults to native Win32 locale: */\n#if defined(_WIN32) && \\\n    !defined(BOOST_REGEX_USE_WIN32_LOCALE) && \\\n    !defined(BOOST_REGEX_USE_C_LOCALE) && \\\n    !defined(BOOST_REGEX_USE_CPP_LOCALE) && \\\n    !defined(BOOST_REGEX_NO_W32) && \\\n    !defined(BOOST_REGEX_NO_WIN32_LOCALE)\n#  define BOOST_REGEX_USE_WIN32_LOCALE\n#endif\n/* otherwise use C++ locale if supported: */\n#if !defined(BOOST_REGEX_USE_WIN32_LOCALE) && !defined(BOOST_REGEX_USE_C_LOCALE) && !defined(BOOST_REGEX_USE_CPP_LOCALE) && !defined(BOOST_NO_STD_LOCALE)\n#  define BOOST_REGEX_USE_CPP_LOCALE\n#endif\n/* otherwise use C locale: */\n#if !defined(BOOST_REGEX_USE_WIN32_LOCALE) && !defined(BOOST_REGEX_USE_C_LOCALE) && !defined(BOOST_REGEX_USE_CPP_LOCALE)\n#  define BOOST_REGEX_USE_C_LOCALE\n#endif\n\n#ifndef BOOST_REGEX_MAX_STATE_COUNT\n#  define BOOST_REGEX_MAX_STATE_COUNT 100000000\n#endif\n\n\n/*****************************************************************************\n *\n *  Error Handling for exception free compilers:\n *\n ****************************************************************************/\n\n#ifdef BOOST_NO_EXCEPTIONS\n/*\n * If there are no exceptions then we must report critical-errors\n * the only way we know how; by terminating.\n */\n#include <stdexcept>\n#include <string>\n#include <boost/throw_exception.hpp>\n\n#  define BOOST_REGEX_NOEH_ASSERT(x)\\\nif(0 == (x))\\\n{\\\n   std::string s(\"Error: critical regex++ failure in: \");\\\n   s.append(#x);\\\n   std::runtime_error e(s);\\\n   boost::throw_exception(e);\\\n}\n#else\n/*\n * With exceptions then error handling is taken care of and\n * there is no need for these checks:\n */\n#  define BOOST_REGEX_NOEH_ASSERT(x)\n#endif\n\n\n/*****************************************************************************\n *\n *  Stack protection under MS Windows:\n *\n ****************************************************************************/\n\n#if !defined(BOOST_REGEX_NO_W32) && !defined(BOOST_REGEX_V3)\n#  if(defined(_WIN32) || defined(_WIN64) || defined(_WINCE)) \\\n        && !(defined(__GNUC__) || defined(__BORLANDC__) && defined(__clang__)) \\\n        && !(defined(__BORLANDC__) && (__BORLANDC__ >= 0x600)) \\\n        && !(defined(__MWERKS__) && (__MWERKS__ <= 0x3003))\n#     define BOOST_REGEX_HAS_MS_STACK_GUARD\n#  endif\n#elif defined(BOOST_REGEX_HAS_MS_STACK_GUARD)\n#  undef BOOST_REGEX_HAS_MS_STACK_GUARD\n#endif\n\n#if defined(__cplusplus) && defined(BOOST_REGEX_HAS_MS_STACK_GUARD)\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\nBOOST_REGEX_DECL void BOOST_REGEX_CALL reset_stack_guard_page();\n\n}\n}\n\n#endif\n\n\n/*****************************************************************************\n *\n *  Algorithm selection and configuration.\n *  These options are now obsolete for C++11 and later (regex v5).\n *\n ****************************************************************************/\n\n#if !defined(BOOST_REGEX_RECURSIVE) && !defined(BOOST_REGEX_NON_RECURSIVE)\n#  if defined(BOOST_REGEX_HAS_MS_STACK_GUARD) && !defined(_STLP_DEBUG) && !defined(__STL_DEBUG) && !(defined(_MSC_VER) && (_MSC_VER >= 1400)) && defined(BOOST_REGEX_CXX03)\n#     define BOOST_REGEX_RECURSIVE\n#  else\n#     define BOOST_REGEX_NON_RECURSIVE\n#  endif\n#endif\n\n#ifdef BOOST_REGEX_NON_RECURSIVE\n#  ifdef BOOST_REGEX_RECURSIVE\n#     error \"Can't set both BOOST_REGEX_RECURSIVE and BOOST_REGEX_NON_RECURSIVE\"\n#  endif\n#  ifndef BOOST_REGEX_BLOCKSIZE\n#     define BOOST_REGEX_BLOCKSIZE 4096\n#  endif\n#  if BOOST_REGEX_BLOCKSIZE < 512\n#     error \"BOOST_REGEX_BLOCKSIZE must be at least 512\"\n#  endif\n#  ifndef BOOST_REGEX_MAX_BLOCKS\n#     define BOOST_REGEX_MAX_BLOCKS 1024\n#  endif\n#  ifdef BOOST_REGEX_HAS_MS_STACK_GUARD\n#     undef BOOST_REGEX_HAS_MS_STACK_GUARD\n#  endif\n#  ifndef BOOST_REGEX_MAX_CACHE_BLOCKS\n#     define BOOST_REGEX_MAX_CACHE_BLOCKS 16\n#  endif\n#endif\n\n\n/*****************************************************************************\n *\n *  Diagnostics:\n *\n ****************************************************************************/\n\n#ifdef BOOST_REGEX_CONFIG_INFO\nBOOST_REGEX_DECL void BOOST_REGEX_CALL print_regex_library_info();\n#endif\n\n#if defined(BOOST_REGEX_DIAG)\n#  pragma message (\"BOOST_REGEX_DECL\" BOOST_STRINGIZE(=BOOST_REGEX_DECL))\n#  pragma message (\"BOOST_REGEX_CALL\" BOOST_STRINGIZE(=BOOST_REGEX_CALL))\n#  pragma message (\"BOOST_REGEX_CCALL\" BOOST_STRINGIZE(=BOOST_REGEX_CCALL))\n#ifdef BOOST_REGEX_USE_C_LOCALE\n#  pragma message (\"Using C locale in regex traits class\")\n#elif BOOST_REGEX_USE_CPP_LOCALE\n#  pragma message (\"Using C++ locale in regex traits class\")\n#else\n#  pragma message (\"Using Win32 locale in regex traits class\")\n#endif\n#if defined(BOOST_REGEX_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)\n#  pragma message (\"Dynamic linking enabled\")\n#endif\n#if defined(BOOST_REGEX_NO_LIB) || defined(BOOST_ALL_NO_LIB)\n#  pragma message (\"Auto-linking disabled\")\n#endif\n#ifdef BOOST_REGEX_NO_EXTERNAL_TEMPLATES\n#  pragma message (\"Extern templates disabled\")\n#endif\n\n#endif\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/icu.hpp",
    "content": "/*\n *\n * Copyright (c) 2020\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         icu.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Unicode regular expressions on top of the ICU Library.\n  */\n\n#ifndef BOOST_REGEX_ICU_HPP\n#define BOOST_REGEX_ICU_HPP\n\n#include <boost/regex/config.hpp>\n\n#ifdef BOOST_REGEX_CXX03\n#include <boost/regex/v4/icu.hpp>\n#else\n#include <boost/regex/v5/icu.hpp>\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/mfc.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         mfc.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Overloads and helpers for using MFC/ATL string types with Boost.Regex.\n  */\n\n#ifndef BOOST_REGEX_MFC_HPP\n#define BOOST_REGEX_MFC_HPP\n\n#include <atlsimpstr.h>\n#include <boost/regex.hpp>\n\nnamespace boost{\n\n//\n// define the types used for TCHAR's:\ntypedef basic_regex<TCHAR> tregex;\ntypedef match_results<TCHAR const*> tmatch;\ntypedef regex_iterator<TCHAR const*> tregex_iterator;\ntypedef regex_token_iterator<TCHAR const*> tregex_token_iterator;\n\n// Obsolete. Remove\n#define SIMPLE_STRING_PARAM class B, bool b\n#define SIMPLE_STRING_ARG_LIST B, b\n\n//\n// define regex creation functions:\n//\ntemplate <class B, bool b>\ninline basic_regex<B> \nmake_regex(const ATL::CSimpleStringT<B, b>& s, ::boost::regex_constants::syntax_option_type f = boost::regex_constants::normal)\n{\n   basic_regex<B> result(s.GetString(), s.GetString() + s.GetLength(), f);\n   return result;\n}\n//\n// regex_match overloads:\n//\ntemplate <class B, bool b, class A, class T>\ninline bool regex_match(const ATL::CSimpleStringT<B, b>& s,\n                 match_results<const B*, A>& what,\n                 const basic_regex<B, T>& e,\n                 boost::regex_constants::match_flag_type f = boost::regex_constants::match_default)\n{\n   return ::boost::regex_match(s.GetString(),\n                               s.GetString() + s.GetLength(),\n                               what,\n                               e,\n                               f);\n}\n\ntemplate <class B, bool b, class T>\ninline bool regex_match(const ATL::CSimpleStringT<B, b>& s,\n                 const basic_regex<B, T>& e,\n                 boost::regex_constants::match_flag_type f = boost::regex_constants::match_default)\n{\n   return ::boost::regex_match(s.GetString(),\n                               s.GetString() + s.GetLength(),\n                               e,\n                               f);\n}\n//\n// regex_search overloads:\n//\ntemplate <class B, bool b, class A, class T>\ninline bool regex_search(const ATL::CSimpleStringT<B, b>& s,\n                 match_results<const B*, A>& what,\n                 const basic_regex<B, T>& e,\n                 boost::regex_constants::match_flag_type f = boost::regex_constants::match_default)\n{\n   return ::boost::regex_search(s.GetString(),\n                               s.GetString() + s.GetLength(),\n                               what,\n                               e,\n                               f);\n}\n\ntemplate <class B, bool b, class T>\ninline bool regex_search(const ATL::CSimpleStringT<B, b>& s,\n                 const basic_regex<B, T>& e,\n                 boost::regex_constants::match_flag_type f = boost::regex_constants::match_default)\n{\n   return ::boost::regex_search(s.GetString(),\n                               s.GetString() + s.GetLength(),\n                               e,\n                               f);\n}\n//\n// regex_iterator creation:\n//\ntemplate <class B, bool b>\ninline regex_iterator<B const*> \nmake_regex_iterator(const ATL::CSimpleStringT<B, b>& s, const basic_regex<B>& e, ::boost::regex_constants::match_flag_type f = boost::regex_constants::match_default)\n{\n   regex_iterator<B const*> result(s.GetString(), s.GetString() + s.GetLength(), e, f);\n   return result;\n}\n\ntemplate <class B, bool b>\ninline regex_token_iterator<B const*> \n   make_regex_token_iterator(const ATL::CSimpleStringT<B, b>& s, const basic_regex<B>& e, int sub = 0, ::boost::regex_constants::match_flag_type f = boost::regex_constants::match_default)\n{\n   regex_token_iterator<B const*> result(s.GetString(), s.GetString() + s.GetLength(), e, sub, f);\n   return result;\n}\n\ntemplate <class B, bool b>\ninline regex_token_iterator<B const*> \nmake_regex_token_iterator(const ATL::CSimpleStringT<B, b>& s, const basic_regex<B>& e, const std::vector<int>& subs, ::boost::regex_constants::match_flag_type f = boost::regex_constants::match_default)\n{\n   regex_token_iterator<B const*> result(s.GetString(), s.GetString() + s.GetLength(), e, subs, f);\n   return result;\n}\n\ntemplate <class B, bool b, std::size_t N>\ninline regex_token_iterator<B const*> \nmake_regex_token_iterator(const ATL::CSimpleStringT<B, b>& s, const basic_regex<B>& e, const int (& subs)[N], ::boost::regex_constants::match_flag_type f = boost::regex_constants::match_default)\n{\n   regex_token_iterator<B const*> result(s.GetString(), s.GetString() + s.GetLength(), e, subs, f);\n   return result;\n}\n\ntemplate <class OutputIterator, class BidirectionalIterator, class traits,\n          class B, bool b>\nOutputIterator regex_replace(OutputIterator out,\n                           BidirectionalIterator first,\n                           BidirectionalIterator last,\n                           const basic_regex<B, traits>& e,\n                           const ATL::CSimpleStringT<B, b>& fmt,\n                           match_flag_type flags = match_default)\n{\n   return ::boost::regex_replace(out, first, last, e, fmt.GetString(), flags);\n}\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class B, bool b>\nclass mfc_string_out_iterator\n{\n   ATL::CSimpleStringT<B, b>* out;\npublic:\n   mfc_string_out_iterator(ATL::CSimpleStringT<B, b>& s) : out(&s) {}\n   mfc_string_out_iterator& operator++() { return *this; }\n   mfc_string_out_iterator& operator++(int) { return *this; }\n   mfc_string_out_iterator& operator*() { return *this; }\n   mfc_string_out_iterator& operator=(B v) \n   { \n      out->AppendChar(v); \n      return *this; \n   }\n   typedef std::ptrdiff_t difference_type;\n   typedef B value_type;\n   typedef value_type* pointer;\n   typedef value_type& reference;\n   typedef std::output_iterator_tag iterator_category;\n};\n\n}\n\ntemplate <class traits, class B, bool b>\nATL::CSimpleStringT<B, b> regex_replace(const ATL::CSimpleStringT<B, b>& s,\n                            const basic_regex<B, traits>& e,\n                            const ATL::CSimpleStringT<B, b>& fmt,\n                            match_flag_type flags = match_default)\n{\n   ATL::CSimpleStringT<B, b> result(s.GetManager());\n   BOOST_REGEX_DETAIL_NS::mfc_string_out_iterator<B, b> i(result);\n   regex_replace(i, s.GetString(), s.GetString() + s.GetLength(), e, fmt.GetString(), flags);\n   return result;\n}\n\n} // namespace boost.\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/pattern_except.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         pattern_except.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares pattern-matching exception classes.\n  */\n\n#ifndef BOOST_RE_PAT_EXCEPT_HPP\n#define BOOST_RE_PAT_EXCEPT_HPP\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n\n#ifdef BOOST_REGEX_CXX03\n#include <boost/regex/v4/pattern_except.hpp>\n#else\n#include <boost/regex/v5/pattern_except.hpp>\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/pending/object_cache.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         object_cache.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Implements a generic object cache.\n  */\n\n#ifndef BOOST_REGEX_OBJECT_CACHE_HPP\n#define BOOST_REGEX_OBJECT_CACHE_HPP\n\n#include <boost/regex/config.hpp>\n#ifdef BOOST_REGEX_CXX03\n#include <boost/regex/v4/object_cache.hpp>\n#else\n#include <boost/regex/v5/object_cache.hpp>\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/pending/static_mutex.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         static_mutex.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares static_mutex lock type, there are three different\n  *                implementations: POSIX pthreads, WIN32 threads, and portable,\n  *                these are described in more detail below.\n  */\n\n#ifndef BOOST_REGEX_STATIC_MUTEX_HPP\n#define BOOST_REGEX_STATIC_MUTEX_HPP\n\n#include <boost/config.hpp>\n#include <boost/regex/config.hpp> // dll import/export options.\n\n#ifdef BOOST_HAS_PTHREADS\n#include <pthread.h>\n#endif\n\n#if defined(BOOST_HAS_PTHREADS) && defined(PTHREAD_MUTEX_INITIALIZER)\n//\n// pthreads version:\n// simple wrap around a pthread_mutex_t initialized with\n// PTHREAD_MUTEX_INITIALIZER.\n//\nnamespace boost{\n\nclass static_mutex;\n\n#define BOOST_STATIC_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER, }\n\nclass BOOST_REGEX_DECL scoped_static_mutex_lock\n{\npublic:\n   scoped_static_mutex_lock(static_mutex& mut, bool lk = true);\n   ~scoped_static_mutex_lock();\n   inline bool locked()const\n   {\n      return m_have_lock;\n   }\n   inline operator void const*()const\n   {\n      return locked() ? this : 0;\n   }\n   void lock();\n   void unlock();\nprivate:\n   static_mutex& m_mutex;\n   bool m_have_lock;\n};\n\nclass static_mutex\n{\npublic:\n   typedef scoped_static_mutex_lock scoped_lock;\n   pthread_mutex_t m_mutex;\n};\n\n} // namespace boost\n#elif defined(BOOST_HAS_WINTHREADS)\n//\n// Win32 version:\n// Use a 32-bit int as a lock, along with a test-and-set\n// implementation using InterlockedCompareExchange.\n//\n\n#include <boost/cstdint.hpp>\n\nnamespace boost{\n\nclass BOOST_REGEX_DECL scoped_static_mutex_lock;\n\nclass static_mutex\n{\npublic:\n   typedef scoped_static_mutex_lock scoped_lock;\n   boost::int32_t m_mutex;\n};\n\n#define BOOST_STATIC_MUTEX_INIT { 0, }\n\nclass BOOST_REGEX_DECL scoped_static_mutex_lock\n{\npublic:\n   scoped_static_mutex_lock(static_mutex& mut, bool lk = true);\n   ~scoped_static_mutex_lock();\n   operator void const*()const\n   {\n      return locked() ? this : 0;\n   }\n   bool locked()const\n   {\n      return m_have_lock;\n   }\n   void lock();\n   void unlock();\nprivate:\n   static_mutex& m_mutex;\n   bool m_have_lock;\n   scoped_static_mutex_lock(const scoped_static_mutex_lock&);\n   scoped_static_mutex_lock& operator=(const scoped_static_mutex_lock&);\n};\n\n} // namespace\n\n#else\n//\n// Portable version of a static mutex based on Boost.Thread library:\n// This has to use a single mutex shared by all instances of static_mutex\n// because boost::call_once doesn't alow us to pass instance information\n// down to the initialisation proceedure.  In fact the initialisation routine\n// may need to be called more than once - but only once per instance.\n//\n// Since this preprocessor path is almost never taken, we hide these header\n// dependencies so that build tools don't find them.\n//\n#define BOOST_REGEX_H1 <boost/thread/once.hpp>\n#define BOOST_REGEX_H2 <boost/thread/recursive_mutex.hpp>\n#define BOOST_REGEX_H3 <boost/thread/lock_types.hpp>\n#include BOOST_REGEX_H1\n#include BOOST_REGEX_H2\n#include BOOST_REGEX_H3\n#undef BOOST_REGEX_H1\n#undef BOOST_REGEX_H2\n#undef BOOST_REGEX_H3\n\nnamespace boost{\n\nclass BOOST_REGEX_DECL scoped_static_mutex_lock;\nextern \"C\" BOOST_REGEX_DECL void boost_regex_free_static_mutex();\n\nclass BOOST_REGEX_DECL static_mutex\n{\npublic:\n   typedef scoped_static_mutex_lock scoped_lock;\n   static void init();\n   static boost::recursive_mutex* m_pmutex;\n   static boost::once_flag m_once;\n};\n\n#define BOOST_STATIC_MUTEX_INIT {  }\n\nclass BOOST_REGEX_DECL scoped_static_mutex_lock\n{\npublic:\n   scoped_static_mutex_lock(static_mutex& mut, bool lk = true);\n   ~scoped_static_mutex_lock();\n   operator void const*()const;\n   bool locked()const;\n   void lock();\n   void unlock();\nprivate:\n   boost::unique_lock<boost::recursive_mutex>* m_plock;\n   bool m_have_lock;\n};\n\ninline scoped_static_mutex_lock::operator void const*()const\n{\n   return locked() ? this : 0;\n}\n\ninline bool scoped_static_mutex_lock::locked()const\n{\n   return m_have_lock;\n}\n\n} // namespace\n\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/pending/unicode_iterator.hpp",
    "content": "/*\n *\n * Copyright (c) 2020\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         unicode_iterator.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Iterator adapters for converting between different Unicode encodings.\n  */\n\n#ifndef BOOST_REGEX_PENDING_UNICODE_ITERATOR_HPP\n#define BOOST_REGEX_PENDING_UNICODE_ITERATOR_HPP\n\n#include <boost/regex/config.hpp>\n\n#if defined(BOOST_REGEX_CXX03)\n#include <boost/regex/v4/unicode_iterator.hpp>\n#else\n#include <boost/regex/v5/unicode_iterator.hpp>\n#endif\n\n\n#endif // BOOST_REGEX_PENDING_UNICODE_ITERATOR_HPP\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/regex_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_traits.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression traits classes.\n  */\n\n#ifndef BOOST_REGEX_TRAITS_HPP\n#define BOOST_REGEX_TRAITS_HPP\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#  include <boost/regex/config.hpp>\n#endif\n\n#  ifndef BOOST_REGEX_TRAITS_HPP_INCLUDED\n#ifdef BOOST_REGEX_CXX03\n#     include <boost/regex/v4/regex_traits.hpp>\n#else\n#     include <boost/regex/v5/regex_traits.hpp>\n#endif\n#  endif\n\n#endif // include\n\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/user.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         user.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: User settable options.\n  */\n\n// define if you want the regex library to use the C locale\n// even on Win32:\n// #define BOOST_REGEX_USE_C_LOCALE\n\n// define this is you want the regex library to use the C++\n// locale:\n// #define BOOST_REGEX_USE_CPP_LOCALE\n\n// define this if the runtime library is a dll, and you\n// want BOOST_REGEX_DYN_LINK to set up dll exports/imports\n// with __declspec(dllexport)/__declspec(dllimport.)\n// #define BOOST_REGEX_HAS_DLL_RUNTIME\n\n// define this if you want to dynamically link to regex,\n// if the runtime library is also a dll (Probably Win32 specific,\n// and has no effect unless BOOST_REGEX_HAS_DLL_RUNTIME is set):\n// #define BOOST_REGEX_DYN_LINK\n\n// define this if you don't want the lib to automatically\n// select its link libraries:\n// #define BOOST_REGEX_NO_LIB\n\n// define this if templates with switch statements cause problems:\n// #define BOOST_REGEX_NO_TEMPLATE_SWITCH_MERGE\n \n// define this to disable Win32 support when available:\n// #define BOOST_REGEX_NO_W32\n\n// define this if bool is not a real type:\n// #define BOOST_REGEX_NO_BOOL\n\n// define this if no template instances are to be placed in\n// the library rather than users object files:\n// #define BOOST_REGEX_NO_EXTERNAL_TEMPLATES\n\n// define this if the forward declarations in regex_fwd.hpp\n// cause more problems than they are worth:\n// #define BOOST_REGEX_NO_FWD\n\n// define this if your compiler supports MS Windows structured\n// exception handling.\n// #define BOOST_REGEX_HAS_MS_STACK_GUARD\n\n// define this if you want to use the recursive algorithm\n// even if BOOST_REGEX_HAS_MS_STACK_GUARD is not defined.\n// NOTE: OBSOLETE!!\n// #define BOOST_REGEX_RECURSIVE\n\n// define this if you want to use the non-recursive\n// algorithm, even if the recursive version would be the default.\n// NOTE: OBSOLETE!!\n// #define BOOST_REGEX_NON_RECURSIVE\n\n// define this if you want to set the size of the memory blocks\n// used by the non-recursive algorithm.\n// #define BOOST_REGEX_BLOCKSIZE 4096\n\n// define this if you want to set the maximum number of memory blocks\n// used by the non-recursive algorithm.\n// #define BOOST_REGEX_MAX_BLOCKS 1024\n\n// define this if you want to set the maximum number of memory blocks\n// cached by the non-recursive algorithm: Normally this is 16, but can be \n// higher if you have multiple threads all using boost.regex, or lower \n// if you don't want boost.regex to cache memory.\n// #define BOOST_REGEX_MAX_CACHE_BLOCKS 16\n\n// define this if you want to be able to access extended capture\n// information in your sub_match's (caution this will slow things\n// down quite a bit).\n// #define BOOST_REGEX_MATCH_EXTRA\n\n// define this if you want to enable support for Unicode via ICU.\n// #define BOOST_HAS_ICU\n\n// define this if you want regex to use __cdecl calling convensions, even when __fastcall is available:\n// #define BOOST_REGEX_NO_FASTCALL\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/basic_regex.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2004 John Maddock\n * Copyright 2011 Garmin Ltd. or its subsidiaries\n *\n * Distributed under the Boost Software License, Version 1.0.\n * (See accompanying file LICENSE_1_0.txt or copy at\n * http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org/ for most recent version.\n  *   FILE         basic_regex.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class basic_regex.\n  */\n\n#ifndef BOOST_REGEX_V4_BASIC_REGEX_HPP\n#define BOOST_REGEX_V4_BASIC_REGEX_HPP\n\n#include <boost/type_traits/is_same.hpp>\n#include <boost/container_hash/hash.hpp>\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable : 4251)\n#if BOOST_MSVC < 1700\n#     pragma warning(disable : 4231)\n#endif\n#if BOOST_MSVC < 1600\n#pragma warning(disable : 4660)\n#endif\n#if BOOST_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\n//\n// forward declaration, we will need this one later:\n//\ntemplate <class charT, class traits>\nclass basic_regex_parser;\n\ntemplate <class I>\nvoid bubble_down_one(I first, I last)\n{\n   if(first != last)\n   {\n      I next = last - 1;\n      while((next != first) && (*next < *(next-1)))\n      {\n         (next-1)->swap(*next);\n         --next;\n      }\n   }\n}\n\nstatic const int hash_value_mask = 1 << (std::numeric_limits<int>::digits - 1);\n\ntemplate <class Iterator>\ninline int hash_value_from_capture_name(Iterator i, Iterator j)\n{\n   std::size_t r = boost::hash_range(i, j);\n   r %= ((std::numeric_limits<int>::max)());\n   return static_cast<int>(r) | hash_value_mask;\n}\n\nclass named_subexpressions\n{\npublic:\n   struct name\n   {\n      template <class charT>\n      name(const charT* i, const charT* j, int idx)\n         : index(idx) \n      { \n         hash = hash_value_from_capture_name(i, j); \n      }\n      name(int h, int idx)\n         : index(idx), hash(h)\n      { \n      }\n      int index;\n      int hash;\n      bool operator < (const name& other)const\n      {\n         return hash < other.hash;\n      }\n      bool operator == (const name& other)const\n      {\n         return hash == other.hash; \n      }\n      void swap(name& other)\n      {\n         std::swap(index, other.index);\n         std::swap(hash, other.hash);\n      }\n   };\n\n   typedef std::vector<name>::const_iterator const_iterator;\n   typedef std::pair<const_iterator, const_iterator> range_type;\n\n   named_subexpressions(){}\n\n   template <class charT>\n   void set_name(const charT* i, const charT* j, int index)\n   {\n      m_sub_names.push_back(name(i, j, index));\n      bubble_down_one(m_sub_names.begin(), m_sub_names.end());\n   }\n   template <class charT>\n   int get_id(const charT* i, const charT* j)const\n   {\n      name t(i, j, 0);\n      typename std::vector<name>::const_iterator pos = std::lower_bound(m_sub_names.begin(), m_sub_names.end(), t);\n      if((pos != m_sub_names.end()) && (*pos == t))\n      {\n         return pos->index;\n      }\n      return -1;\n   }\n   template <class charT>\n   range_type equal_range(const charT* i, const charT* j)const\n   {\n      name t(i, j, 0);\n      return std::equal_range(m_sub_names.begin(), m_sub_names.end(), t);\n   }\n   int get_id(int h)const\n   {\n      name t(h, 0);\n      std::vector<name>::const_iterator pos = std::lower_bound(m_sub_names.begin(), m_sub_names.end(), t);\n      if((pos != m_sub_names.end()) && (*pos == t))\n      {\n         return pos->index;\n      }\n      return -1;\n   }\n   range_type equal_range(int h)const\n   {\n      name t(h, 0);\n      return std::equal_range(m_sub_names.begin(), m_sub_names.end(), t);\n   }\nprivate:\n   std::vector<name> m_sub_names;\n};\n\n//\n// class regex_data:\n// represents the data we wish to expose to the matching algorithms.\n//\ntemplate <class charT, class traits>\nstruct regex_data : public named_subexpressions\n{\n   typedef regex_constants::syntax_option_type   flag_type;\n   typedef std::size_t                           size_type;  \n\n   regex_data(const ::boost::shared_ptr<\n      ::boost::regex_traits_wrapper<traits> >& t) \n      : m_ptraits(t), m_flags(0), m_status(0), m_expression(0), m_expression_len(0),\n         m_mark_count(0), m_first_state(0), m_restart_type(0),\n#if !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX) && !(defined(BOOST_MSVC) && (BOOST_MSVC < 1900))\n         m_startmap{ 0 },\n#endif\n         m_can_be_null(0), m_word_mask(0), m_has_recursions(false), m_disable_match_any(false) {}\n   regex_data() \n      : m_ptraits(new ::boost::regex_traits_wrapper<traits>()), m_flags(0), m_status(0), m_expression(0), m_expression_len(0), \n         m_mark_count(0), m_first_state(0), m_restart_type(0), \n#if !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX) && !(defined(BOOST_MSVC) && (BOOST_MSVC < 1900))\n      m_startmap{ 0 },\n#endif\n         m_can_be_null(0), m_word_mask(0), m_has_recursions(false), m_disable_match_any(false) {}\n\n   ::boost::shared_ptr<\n      ::boost::regex_traits_wrapper<traits>\n      >                        m_ptraits;                 // traits class instance\n   flag_type                   m_flags;                   // flags with which we were compiled\n   int                         m_status;                  // error code (0 implies OK).\n   const charT*                m_expression;              // the original expression\n   std::ptrdiff_t              m_expression_len;          // the length of the original expression\n   size_type                   m_mark_count;              // the number of marked sub-expressions\n   BOOST_REGEX_DETAIL_NS::re_syntax_base*  m_first_state;             // the first state of the machine\n   unsigned                    m_restart_type;            // search optimisation type\n   unsigned char               m_startmap[1 << CHAR_BIT]; // which characters can start a match\n   unsigned int                m_can_be_null;             // whether we can match a null string\n   BOOST_REGEX_DETAIL_NS::raw_storage      m_data;                    // the buffer in which our states are constructed\n   typename traits::char_class_type    m_word_mask;       // mask used to determine if a character is a word character\n   std::vector<\n      std::pair<\n      std::size_t, std::size_t> > m_subs;                 // Position of sub-expressions within the *string*.\n   bool                        m_has_recursions;          // whether we have recursive expressions;\n   bool                        m_disable_match_any;       // when set we need to disable the match_any flag as it causes different/buggy behaviour.\n};\n//\n// class basic_regex_implementation\n// pimpl implementation class for basic_regex.\n//\ntemplate <class charT, class traits>\nclass basic_regex_implementation\n   : public regex_data<charT, traits>\n{\npublic:\n   typedef regex_constants::syntax_option_type   flag_type;\n   typedef std::ptrdiff_t                        difference_type;\n   typedef std::size_t                           size_type; \n   typedef typename traits::locale_type          locale_type;\n   typedef const charT*                          const_iterator;\n\n   basic_regex_implementation(){}\n   basic_regex_implementation(const ::boost::shared_ptr<\n      ::boost::regex_traits_wrapper<traits> >& t)\n      : regex_data<charT, traits>(t) {}\n   void assign(const charT* arg_first,\n                          const charT* arg_last,\n                          flag_type f)\n   {\n      regex_data<charT, traits>* pdat = this;\n      basic_regex_parser<charT, traits> parser(pdat);\n      parser.parse(arg_first, arg_last, f);\n   }\n\n   locale_type BOOST_REGEX_CALL imbue(locale_type l)\n   { \n      return this->m_ptraits->imbue(l); \n   }\n   locale_type BOOST_REGEX_CALL getloc()const\n   { \n      return this->m_ptraits->getloc(); \n   }\n   std::basic_string<charT> BOOST_REGEX_CALL str()const\n   {\n      std::basic_string<charT> result;\n      if(this->m_status == 0)\n         result = std::basic_string<charT>(this->m_expression, this->m_expression_len);\n      return result;\n   }\n   const_iterator BOOST_REGEX_CALL expression()const\n   {\n      return this->m_expression;\n   }\n   std::pair<const_iterator, const_iterator> BOOST_REGEX_CALL subexpression(std::size_t n)const\n   {\n      const std::pair<std::size_t, std::size_t>& pi = this->m_subs.at(n);\n      std::pair<const_iterator, const_iterator> p(expression() + pi.first, expression() + pi.second);\n      return p;\n   }\n   //\n   // begin, end:\n   const_iterator BOOST_REGEX_CALL begin()const\n   { \n      return (this->m_status ? 0 : this->m_expression); \n   }\n   const_iterator BOOST_REGEX_CALL end()const\n   { \n      return (this->m_status ? 0 : this->m_expression + this->m_expression_len); \n   }\n   flag_type BOOST_REGEX_CALL flags()const\n   {\n      return this->m_flags;\n   }\n   size_type BOOST_REGEX_CALL size()const\n   {\n      return this->m_expression_len;\n   }\n   int BOOST_REGEX_CALL status()const\n   {\n      return this->m_status;\n   }\n   size_type BOOST_REGEX_CALL mark_count()const\n   {\n      return this->m_mark_count - 1;\n   }\n   const BOOST_REGEX_DETAIL_NS::re_syntax_base* get_first_state()const\n   {\n      return this->m_first_state;\n   }\n   unsigned get_restart_type()const\n   {\n      return this->m_restart_type;\n   }\n   const unsigned char* get_map()const\n   {\n      return this->m_startmap;\n   }\n   const ::boost::regex_traits_wrapper<traits>& get_traits()const\n   {\n      return *(this->m_ptraits);\n   }\n   bool can_be_null()const\n   {\n      return this->m_can_be_null;\n   }\n   const regex_data<charT, traits>& get_data()const\n   {\n      basic_regex_implementation<charT, traits> const* p = this;\n      return *static_cast<const regex_data<charT, traits>*>(p);\n   }\n};\n\n} // namespace BOOST_REGEX_DETAIL_NS\n//\n// class basic_regex:\n// represents the compiled\n// regular expression:\n//\n\n#ifdef BOOST_REGEX_NO_FWD\ntemplate <class charT, class traits = regex_traits<charT> >\n#else\ntemplate <class charT, class traits >\n#endif\nclass basic_regex : public regbase\n{\npublic:\n   // typedefs:\n   typedef std::size_t                           traits_size_type;\n   typedef typename traits::string_type          traits_string_type;\n   typedef charT                                 char_type;\n   typedef traits                                traits_type;\n\n   typedef charT                                 value_type;\n   typedef charT&                                reference;\n   typedef const charT&                          const_reference;\n   typedef const charT*                          const_iterator;\n   typedef const_iterator                        iterator;\n   typedef std::ptrdiff_t                        difference_type;\n   typedef std::size_t                           size_type;   \n   typedef regex_constants::syntax_option_type   flag_type;\n   // locale_type\n   // placeholder for actual locale type used by the\n   // traits class to localise *this.\n   typedef typename traits::locale_type          locale_type;\n   \npublic:\n   explicit basic_regex(){}\n   explicit basic_regex(const charT* p, flag_type f = regex_constants::normal)\n   {\n      assign(p, f);\n   }\n   basic_regex(const charT* p1, const charT* p2, flag_type f = regex_constants::normal)\n   {\n      assign(p1, p2, f);\n   }\n   basic_regex(const charT* p, size_type len, flag_type f)\n   {\n      assign(p, len, f);\n   }\n   basic_regex(const basic_regex& that)\n      : m_pimpl(that.m_pimpl) {}\n   ~basic_regex(){}\n   basic_regex& BOOST_REGEX_CALL operator=(const basic_regex& that)\n   {\n      return assign(that);\n   }\n   basic_regex& BOOST_REGEX_CALL operator=(const charT* ptr)\n   {\n      return assign(ptr);\n   }\n\n   //\n   // assign:\n   basic_regex& assign(const basic_regex& that)\n   { \n      m_pimpl = that.m_pimpl;\n      return *this; \n   }\n   basic_regex& assign(const charT* p, flag_type f = regex_constants::normal)\n   {\n      return assign(p, p + traits::length(p), f);\n   }\n   basic_regex& assign(const charT* p, size_type len, flag_type f)\n   {\n      return assign(p, p + len, f);\n   }\nprivate:\n   basic_regex& do_assign(const charT* p1,\n                          const charT* p2,\n                          flag_type f);\npublic:\n   basic_regex& assign(const charT* p1,\n                          const charT* p2,\n                          flag_type f = regex_constants::normal)\n   {\n      return do_assign(p1, p2, f);\n   }\n#if !defined(BOOST_NO_MEMBER_TEMPLATES)\n\n   template <class ST, class SA>\n   unsigned int BOOST_REGEX_CALL set_expression(const std::basic_string<charT, ST, SA>& p, flag_type f = regex_constants::normal)\n   { \n      return set_expression(p.data(), p.data() + p.size(), f); \n   }\n\n   template <class ST, class SA>\n   explicit basic_regex(const std::basic_string<charT, ST, SA>& p, flag_type f = regex_constants::normal)\n   { \n      assign(p, f); \n   }\n\n   template <class InputIterator>\n   basic_regex(InputIterator arg_first, InputIterator arg_last, flag_type f = regex_constants::normal)\n   {\n      typedef typename traits::string_type seq_type;\n      seq_type a(arg_first, arg_last);\n      if(!a.empty())\n         assign(static_cast<const charT*>(&*a.begin()), static_cast<const charT*>(&*a.begin() + a.size()), f);\n      else\n         assign(static_cast<const charT*>(0), static_cast<const charT*>(0), f);\n   }\n\n   template <class ST, class SA>\n   basic_regex& BOOST_REGEX_CALL operator=(const std::basic_string<charT, ST, SA>& p)\n   {\n      return assign(p.data(), p.data() + p.size(), regex_constants::normal);\n   }\n\n   template <class string_traits, class A>\n   basic_regex& BOOST_REGEX_CALL assign(\n       const std::basic_string<charT, string_traits, A>& s,\n       flag_type f = regex_constants::normal)\n   {\n      return assign(s.data(), s.data() + s.size(), f);\n   }\n\n   template <class InputIterator>\n   basic_regex& BOOST_REGEX_CALL assign(InputIterator arg_first,\n                          InputIterator arg_last,\n                          flag_type f = regex_constants::normal)\n   {\n      typedef typename traits::string_type seq_type;\n      seq_type a(arg_first, arg_last);\n      if(a.size())\n      {\n         const charT* p1 = &*a.begin();\n         const charT* p2 = &*a.begin() + a.size();\n         return assign(p1, p2, f);\n      }\n      return assign(static_cast<const charT*>(0), static_cast<const charT*>(0), f);\n   }\n#else\n   unsigned int BOOST_REGEX_CALL set_expression(const std::basic_string<charT>& p, flag_type f = regex_constants::normal)\n   { \n      return set_expression(p.data(), p.data() + p.size(), f); \n   }\n\n   basic_regex(const std::basic_string<charT>& p, flag_type f = regex_constants::normal)\n   { \n      assign(p, f); \n   }\n\n   basic_regex& BOOST_REGEX_CALL operator=(const std::basic_string<charT>& p)\n   {\n      return assign(p.data(), p.data() + p.size(), regex_constants::normal);\n   }\n\n   basic_regex& BOOST_REGEX_CALL assign(\n       const std::basic_string<charT>& s,\n       flag_type f = regex_constants::normal)\n   {\n      return assign(s.data(), s.data() + s.size(), f);\n   }\n\n#endif\n\n   //\n   // locale:\n   locale_type BOOST_REGEX_CALL imbue(locale_type l);\n   locale_type BOOST_REGEX_CALL getloc()const\n   { \n      return m_pimpl.get() ? m_pimpl->getloc() : locale_type(); \n   }\n   //\n   // getflags:\n   // retained for backwards compatibility only, \"flags\"\n   // is now the preferred name:\n   flag_type BOOST_REGEX_CALL getflags()const\n   { \n      return flags();\n   }\n   flag_type BOOST_REGEX_CALL flags()const\n   { \n      return m_pimpl.get() ? m_pimpl->flags() : 0;\n   }\n   //\n   // str:\n   std::basic_string<charT> BOOST_REGEX_CALL str()const\n   {\n      return m_pimpl.get() ? m_pimpl->str() : std::basic_string<charT>();\n   }\n   //\n   // begin, end, subexpression:\n   std::pair<const_iterator, const_iterator> BOOST_REGEX_CALL subexpression(std::size_t n)const\n   {\n      if(!m_pimpl.get())\n         boost::throw_exception(std::logic_error(\"Can't access subexpressions in an invalid regex.\"));\n      return m_pimpl->subexpression(n);\n   }\n   const_iterator BOOST_REGEX_CALL begin()const\n   { \n      return (m_pimpl.get() ? m_pimpl->begin() : 0); \n   }\n   const_iterator BOOST_REGEX_CALL end()const\n   { \n      return (m_pimpl.get() ? m_pimpl->end() : 0); \n   }\n   //\n   // swap:\n   void BOOST_REGEX_CALL swap(basic_regex& that)throw()\n   {\n      m_pimpl.swap(that.m_pimpl);\n   }\n   //\n   // size:\n   size_type BOOST_REGEX_CALL size()const\n   { \n      return (m_pimpl.get() ? m_pimpl->size() : 0); \n   }\n   //\n   // max_size:\n   size_type BOOST_REGEX_CALL max_size()const\n   { \n      return UINT_MAX; \n   }\n   //\n   // empty:\n   bool BOOST_REGEX_CALL empty()const\n   { \n      return (m_pimpl.get() ? 0 != m_pimpl->status() : true); \n   }\n\n   size_type BOOST_REGEX_CALL mark_count()const \n   { \n      return (m_pimpl.get() ? m_pimpl->mark_count() : 0); \n   }\n\n   int status()const\n   {\n      return (m_pimpl.get() ? m_pimpl->status() : regex_constants::error_empty);\n   }\n\n   int BOOST_REGEX_CALL compare(const basic_regex& that) const\n   {\n      if(m_pimpl.get() == that.m_pimpl.get())\n         return 0;\n      if(!m_pimpl.get())\n         return -1;\n      if(!that.m_pimpl.get())\n         return 1;\n      if(status() != that.status())\n         return status() - that.status();\n      if(flags() != that.flags())\n         return flags() - that.flags();\n      return str().compare(that.str());\n   }\n   bool BOOST_REGEX_CALL operator==(const basic_regex& e)const\n   { \n      return compare(e) == 0; \n   }\n   bool BOOST_REGEX_CALL operator != (const basic_regex& e)const\n   { \n      return compare(e) != 0; \n   }\n   bool BOOST_REGEX_CALL operator<(const basic_regex& e)const\n   { \n      return compare(e) < 0; \n   }\n   bool BOOST_REGEX_CALL operator>(const basic_regex& e)const\n   { \n      return compare(e) > 0; \n   }\n   bool BOOST_REGEX_CALL operator<=(const basic_regex& e)const\n   { \n      return compare(e) <= 0; \n   }\n   bool BOOST_REGEX_CALL operator>=(const basic_regex& e)const\n   { \n      return compare(e) >= 0; \n   }\n\n   //\n   // The following are deprecated as public interfaces\n   // but are available for compatibility with earlier versions.\n   const charT* BOOST_REGEX_CALL expression()const \n   { \n      return (m_pimpl.get() && !m_pimpl->status() ? m_pimpl->expression() : 0); \n   }\n   unsigned int BOOST_REGEX_CALL set_expression(const charT* p1, const charT* p2, flag_type f = regex_constants::normal)\n   {\n      assign(p1, p2, f | regex_constants::no_except);\n      return status();\n   }\n   unsigned int BOOST_REGEX_CALL set_expression(const charT* p, flag_type f = regex_constants::normal) \n   { \n      assign(p, f | regex_constants::no_except); \n      return status();\n   }\n   unsigned int BOOST_REGEX_CALL error_code()const\n   {\n      return status();\n   }\n   //\n   // private access methods:\n   //\n   const BOOST_REGEX_DETAIL_NS::re_syntax_base* get_first_state()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->get_first_state();\n   }\n   unsigned get_restart_type()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->get_restart_type();\n   }\n   const unsigned char* get_map()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->get_map();\n   }\n   const ::boost::regex_traits_wrapper<traits>& get_traits()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->get_traits();\n   }\n   bool can_be_null()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->can_be_null();\n   }\n   const BOOST_REGEX_DETAIL_NS::regex_data<charT, traits>& get_data()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->get_data();\n   }\n   boost::shared_ptr<BOOST_REGEX_DETAIL_NS::named_subexpressions > get_named_subs()const\n   {\n      return m_pimpl;\n   }\n\nprivate:\n   shared_ptr<BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits> > m_pimpl;\n};\n\n//\n// out of line members;\n// these are the only members that mutate the basic_regex object,\n// and are designed to provide the strong exception guarantee\n// (in the event of a throw, the state of the object remains unchanged).\n//\ntemplate <class charT, class traits>\nbasic_regex<charT, traits>& basic_regex<charT, traits>::do_assign(const charT* p1,\n                        const charT* p2,\n                        flag_type f)\n{\n   shared_ptr<BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits> > temp;\n   if(!m_pimpl.get())\n   {\n      temp = shared_ptr<BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits> >(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits>());\n   }\n   else\n   {\n      temp = shared_ptr<BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits> >(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits>(m_pimpl->m_ptraits));\n   }\n   temp->assign(p1, p2, f);\n   temp.swap(m_pimpl);\n   return *this;\n}\n\ntemplate <class charT, class traits>\ntypename basic_regex<charT, traits>::locale_type BOOST_REGEX_CALL basic_regex<charT, traits>::imbue(locale_type l)\n{ \n   shared_ptr<BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits> > temp(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits>());\n   locale_type result = temp->imbue(l);\n   temp.swap(m_pimpl);\n   return result;\n}\n\n//\n// non-members:\n//\ntemplate <class charT, class traits>\nvoid swap(basic_regex<charT, traits>& e1, basic_regex<charT, traits>& e2)\n{\n   e1.swap(e2);\n}\n\n#ifndef BOOST_NO_STD_LOCALE\ntemplate <class charT, class traits, class traits2>\nstd::basic_ostream<charT, traits>& \n   operator << (std::basic_ostream<charT, traits>& os, \n                const basic_regex<charT, traits2>& e)\n{\n   return (os << e.str());\n}\n#else\ntemplate <class traits>\nstd::ostream& operator << (std::ostream& os, const basic_regex<char, traits>& e)\n{\n   return (os << e.str());\n}\n#endif\n\n//\n// class reg_expression:\n// this is provided for backwards compatibility only,\n// it is deprecated, no not use!\n//\n#ifdef BOOST_REGEX_NO_FWD\ntemplate <class charT, class traits = regex_traits<charT> >\n#else\ntemplate <class charT, class traits >\n#endif\nclass reg_expression : public basic_regex<charT, traits>\n{\npublic:\n   typedef typename basic_regex<charT, traits>::flag_type flag_type;\n   typedef typename basic_regex<charT, traits>::size_type size_type;\n   explicit reg_expression(){}\n   explicit reg_expression(const charT* p, flag_type f = regex_constants::normal)\n      : basic_regex<charT, traits>(p, f){}\n   reg_expression(const charT* p1, const charT* p2, flag_type f = regex_constants::normal)\n      : basic_regex<charT, traits>(p1, p2, f){}\n   reg_expression(const charT* p, size_type len, flag_type f)\n      : basic_regex<charT, traits>(p, len, f){}\n   reg_expression(const reg_expression& that)\n      : basic_regex<charT, traits>(that) {}\n   ~reg_expression(){}\n   reg_expression& BOOST_REGEX_CALL operator=(const reg_expression& that)\n   {\n      return this->assign(that);\n   }\n\n#if !defined(BOOST_NO_MEMBER_TEMPLATES)\n   template <class ST, class SA>\n   explicit reg_expression(const std::basic_string<charT, ST, SA>& p, flag_type f = regex_constants::normal)\n   : basic_regex<charT, traits>(p, f)\n   { \n   }\n\n   template <class InputIterator>\n   reg_expression(InputIterator arg_first, InputIterator arg_last, flag_type f = regex_constants::normal)\n   : basic_regex<charT, traits>(arg_first, arg_last, f)\n   {\n   }\n\n   template <class ST, class SA>\n   reg_expression& BOOST_REGEX_CALL operator=(const std::basic_string<charT, ST, SA>& p)\n   {\n      this->assign(p);\n      return *this;\n   }\n#else\n   explicit reg_expression(const std::basic_string<charT>& p, flag_type f = regex_constants::normal)\n   : basic_regex<charT, traits>(p, f)\n   { \n   }\n\n   reg_expression& BOOST_REGEX_CALL operator=(const std::basic_string<charT>& p)\n   {\n      this->assign(p);\n      return *this;\n   }\n#endif\n\n};\n\n#ifdef BOOST_MSVC\n#pragma warning (pop)\n#endif\n\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/basic_regex_creator.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         basic_regex_creator.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class basic_regex_creator which fills in\n  *                the data members of a regex_data object.\n  */\n\n#ifndef BOOST_REGEX_V4_BASIC_REGEX_CREATOR_HPP\n#define BOOST_REGEX_V4_BASIC_REGEX_CREATOR_HPP\n\n#include <boost/regex/v4/indexed_bit_flag.hpp>\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#if BOOST_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace boost{\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class charT>\nstruct digraph : public std::pair<charT, charT>\n{\n   digraph() : std::pair<charT, charT>(charT(0), charT(0)){}\n   digraph(charT c1) : std::pair<charT, charT>(c1, charT(0)){}\n   digraph(charT c1, charT c2) : std::pair<charT, charT>(c1, c2)\n   {}\n   digraph(const digraph<charT>& d) : std::pair<charT, charT>(d.first, d.second){}\n#ifndef BOOST_NO_CXX11_DEFAULTED_FUNCTIONS\n   digraph<charT>& operator=(const digraph<charT>&) = default;\n#endif\n   template <class Seq>\n   digraph(const Seq& s) : std::pair<charT, charT>()\n   {\n      BOOST_REGEX_ASSERT(s.size() <= 2);\n      BOOST_REGEX_ASSERT(s.size());\n      this->first = s[0];\n      this->second = (s.size() > 1) ? s[1] : 0;\n   }\n};\n\ntemplate <class charT, class traits>\nclass basic_char_set\n{\npublic:\n   typedef digraph<charT>                   digraph_type;\n   typedef typename traits::string_type     string_type;\n   typedef typename traits::char_class_type m_type;\n\n   basic_char_set()\n   {\n      m_negate = false;\n      m_has_digraphs = false;\n      m_classes = 0;\n      m_negated_classes = 0;\n      m_empty = true;\n   }\n\n   void add_single(const digraph_type& s)\n   {\n      m_singles.insert(s);\n      if(s.second)\n         m_has_digraphs = true;\n      m_empty = false;\n   }\n   void add_range(const digraph_type& first, const digraph_type& end)\n   {\n      m_ranges.push_back(first);\n      m_ranges.push_back(end);\n      if(first.second)\n      {\n         m_has_digraphs = true;\n         add_single(first);\n      }\n      if(end.second)\n      {\n         m_has_digraphs = true;\n         add_single(end);\n      }\n      m_empty = false;\n   }\n   void add_class(m_type m)\n   {\n      m_classes |= m;\n      m_empty = false;\n   }\n   void add_negated_class(m_type m)\n   {\n      m_negated_classes |= m;\n      m_empty = false;\n   }\n   void add_equivalent(const digraph_type& s)\n   {\n      m_equivalents.insert(s);\n      if(s.second)\n      {\n         m_has_digraphs = true;\n         add_single(s);\n      }\n      m_empty = false;\n   }\n   void negate()\n   { \n      m_negate = true;\n      //m_empty = false;\n   }\n\n   //\n   // accessor functions:\n   //\n   bool has_digraphs()const\n   {\n      return m_has_digraphs;\n   }\n   bool is_negated()const\n   {\n      return m_negate;\n   }\n   typedef typename std::vector<digraph_type>::const_iterator  list_iterator;\n   typedef typename std::set<digraph_type>::const_iterator     set_iterator;\n   set_iterator singles_begin()const\n   {\n      return m_singles.begin();\n   }\n   set_iterator singles_end()const\n   {\n      return m_singles.end();\n   }\n   list_iterator ranges_begin()const\n   {\n      return m_ranges.begin();\n   }\n   list_iterator ranges_end()const\n   {\n      return m_ranges.end();\n   }\n   set_iterator equivalents_begin()const\n   {\n      return m_equivalents.begin();\n   }\n   set_iterator equivalents_end()const\n   {\n      return m_equivalents.end();\n   }\n   m_type classes()const\n   {\n      return m_classes;\n   }\n   m_type negated_classes()const\n   {\n      return m_negated_classes;\n   }\n   bool empty()const\n   {\n      return m_empty;\n   }\nprivate:\n   std::set<digraph_type>    m_singles;         // a list of single characters to match\n   std::vector<digraph_type> m_ranges;          // a list of end points of our ranges\n   bool                      m_negate;          // true if the set is to be negated\n   bool                      m_has_digraphs;    // true if we have digraphs present\n   m_type                    m_classes;         // character classes to match\n   m_type                    m_negated_classes; // negated character classes to match\n   bool                      m_empty;           // whether we've added anything yet\n   std::set<digraph_type>    m_equivalents;     // a list of equivalence classes\n};\n   \ntemplate <class charT, class traits>\nclass basic_regex_creator\n{\npublic:\n   basic_regex_creator(regex_data<charT, traits>* data);\n   std::ptrdiff_t getoffset(void* addr)\n   {\n      return getoffset(addr, m_pdata->m_data.data());\n   }\n   std::ptrdiff_t getoffset(const void* addr, const void* base)\n   {\n      return static_cast<const char*>(addr) - static_cast<const char*>(base);\n   }\n   re_syntax_base* getaddress(std::ptrdiff_t off)\n   {\n      return getaddress(off, m_pdata->m_data.data());\n   }\n   re_syntax_base* getaddress(std::ptrdiff_t off, void* base)\n   {\n      return static_cast<re_syntax_base*>(static_cast<void*>(static_cast<char*>(base) + off));\n   }\n   void init(unsigned l_flags)\n   {\n      m_pdata->m_flags = l_flags;\n      m_icase = l_flags & regex_constants::icase;\n   }\n   regbase::flag_type flags()\n   {\n      return m_pdata->m_flags;\n   }\n   void flags(regbase::flag_type f)\n   {\n      m_pdata->m_flags = f;\n      if(m_icase != static_cast<bool>(f & regbase::icase))\n      {\n         m_icase = static_cast<bool>(f & regbase::icase);\n      }\n   }\n   re_syntax_base* append_state(syntax_element_type t, std::size_t s = sizeof(re_syntax_base));\n   re_syntax_base* insert_state(std::ptrdiff_t pos, syntax_element_type t, std::size_t s = sizeof(re_syntax_base));\n   re_literal* append_literal(charT c);\n   re_syntax_base* append_set(const basic_char_set<charT, traits>& char_set);\n   re_syntax_base* append_set(const basic_char_set<charT, traits>& char_set, mpl::false_*);\n   re_syntax_base* append_set(const basic_char_set<charT, traits>& char_set, mpl::true_*);\n   void finalize(const charT* p1, const charT* p2);\nprotected:\n   regex_data<charT, traits>*    m_pdata;              // pointer to the basic_regex_data struct we are filling in\n   const ::boost::regex_traits_wrapper<traits>&  \n                                 m_traits;             // convenience reference to traits class\n   re_syntax_base*               m_last_state;         // the last state we added\n   bool                          m_icase;              // true for case insensitive matches\n   unsigned                      m_repeater_id;        // the state_id of the next repeater\n   bool                          m_has_backrefs;       // true if there are actually any backrefs\n   indexed_bit_flag              m_backrefs;           // bitmask of permitted backrefs\n   boost::uintmax_t              m_bad_repeats;        // bitmask of repeats we can't deduce a startmap for;\n   bool                          m_has_recursions;     // set when we have recursive expressions to fixup\n   std::vector<unsigned char>    m_recursion_checks;   // notes which recursions we've followed while analysing this expression\n   typename traits::char_class_type m_word_mask;       // mask used to determine if a character is a word character\n   typename traits::char_class_type m_mask_space;      // mask used to determine if a character is a word character\n   typename traits::char_class_type m_lower_mask;       // mask used to determine if a character is a lowercase character\n   typename traits::char_class_type m_upper_mask;      // mask used to determine if a character is an uppercase character\n   typename traits::char_class_type m_alpha_mask;      // mask used to determine if a character is an alphabetic character\nprivate:\n   basic_regex_creator& operator=(const basic_regex_creator&);\n   basic_regex_creator(const basic_regex_creator&);\n\n   void fixup_pointers(re_syntax_base* state);\n   void fixup_recursions(re_syntax_base* state);\n   void create_startmaps(re_syntax_base* state);\n   int calculate_backstep(re_syntax_base* state);\n   void create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask);\n   unsigned get_restart_type(re_syntax_base* state);\n   void set_all_masks(unsigned char* bits, unsigned char);\n   bool is_bad_repeat(re_syntax_base* pt);\n   void set_bad_repeat(re_syntax_base* pt);\n   syntax_element_type get_repeat_type(re_syntax_base* state);\n   void probe_leading_repeat(re_syntax_base* state);\n};\n\ntemplate <class charT, class traits>\nbasic_regex_creator<charT, traits>::basic_regex_creator(regex_data<charT, traits>* data)\n   : m_pdata(data), m_traits(*(data->m_ptraits)), m_last_state(0), m_icase(false), m_repeater_id(0), \n   m_has_backrefs(false), m_bad_repeats(0), m_has_recursions(false), m_word_mask(0), m_mask_space(0), m_lower_mask(0), m_upper_mask(0), m_alpha_mask(0)\n{\n   m_pdata->m_data.clear();\n   m_pdata->m_status = ::boost::regex_constants::error_ok;\n   static const charT w = 'w';\n   static const charT s = 's';\n   static const charT l[5] = { 'l', 'o', 'w', 'e', 'r', };\n   static const charT u[5] = { 'u', 'p', 'p', 'e', 'r', };\n   static const charT a[5] = { 'a', 'l', 'p', 'h', 'a', };\n   m_word_mask = m_traits.lookup_classname(&w, &w +1);\n   m_mask_space = m_traits.lookup_classname(&s, &s +1);\n   m_lower_mask = m_traits.lookup_classname(l, l + 5);\n   m_upper_mask = m_traits.lookup_classname(u, u + 5);\n   m_alpha_mask = m_traits.lookup_classname(a, a + 5);\n   m_pdata->m_word_mask = m_word_mask;\n   BOOST_REGEX_ASSERT(m_word_mask != 0); \n   BOOST_REGEX_ASSERT(m_mask_space != 0); \n   BOOST_REGEX_ASSERT(m_lower_mask != 0); \n   BOOST_REGEX_ASSERT(m_upper_mask != 0); \n   BOOST_REGEX_ASSERT(m_alpha_mask != 0); \n}\n\ntemplate <class charT, class traits>\nre_syntax_base* basic_regex_creator<charT, traits>::append_state(syntax_element_type t, std::size_t s)\n{\n   // if the state is a backref then make a note of it:\n   if(t == syntax_element_backref)\n      this->m_has_backrefs = true;\n   // append a new state, start by aligning our last one:\n   m_pdata->m_data.align();\n   // set the offset to the next state in our last one:\n   if(m_last_state)\n      m_last_state->next.i = m_pdata->m_data.size() - getoffset(m_last_state);\n   // now actually extend our data:\n   m_last_state = static_cast<re_syntax_base*>(m_pdata->m_data.extend(s));\n   // fill in boilerplate options in the new state:\n   m_last_state->next.i = 0;\n   m_last_state->type = t;\n   return m_last_state;\n}\n\ntemplate <class charT, class traits>\nre_syntax_base* basic_regex_creator<charT, traits>::insert_state(std::ptrdiff_t pos, syntax_element_type t, std::size_t s)\n{\n   // append a new state, start by aligning our last one:\n   m_pdata->m_data.align();\n   // set the offset to the next state in our last one:\n   if(m_last_state)\n      m_last_state->next.i = m_pdata->m_data.size() - getoffset(m_last_state);\n   // remember the last state position:\n   std::ptrdiff_t off = getoffset(m_last_state) + s;\n   // now actually insert our data:\n   re_syntax_base* new_state = static_cast<re_syntax_base*>(m_pdata->m_data.insert(pos, s));\n   // fill in boilerplate options in the new state:\n   new_state->next.i = s;\n   new_state->type = t;\n   m_last_state = getaddress(off);\n   return new_state;\n}\n\ntemplate <class charT, class traits>\nre_literal* basic_regex_creator<charT, traits>::append_literal(charT c)\n{\n   re_literal* result;\n   // start by seeing if we have an existing re_literal we can extend:\n   if((0 == m_last_state) || (m_last_state->type != syntax_element_literal))\n   {\n      // no existing re_literal, create a new one:\n      result = static_cast<re_literal*>(append_state(syntax_element_literal, sizeof(re_literal) + sizeof(charT)));\n      result->length = 1;\n      *static_cast<charT*>(static_cast<void*>(result+1)) = m_traits.translate(c, m_icase);\n   }\n   else\n   {\n      // we have an existing re_literal, extend it:\n      std::ptrdiff_t off = getoffset(m_last_state);\n      m_pdata->m_data.extend(sizeof(charT));\n      m_last_state = result = static_cast<re_literal*>(getaddress(off));\n      charT* characters = static_cast<charT*>(static_cast<void*>(result+1));\n      characters[result->length] = m_traits.translate(c, m_icase);\n      result->length += 1;\n   }\n   return result;\n}\n\ntemplate <class charT, class traits>\ninline re_syntax_base* basic_regex_creator<charT, traits>::append_set(\n   const basic_char_set<charT, traits>& char_set)\n{\n   typedef mpl::bool_< (sizeof(charT) == 1) > truth_type;\n   return char_set.has_digraphs() \n      ? append_set(char_set, static_cast<mpl::false_*>(0))\n      : append_set(char_set, static_cast<truth_type*>(0));\n}\n\ntemplate <class charT, class traits>\nre_syntax_base* basic_regex_creator<charT, traits>::append_set(\n   const basic_char_set<charT, traits>& char_set, mpl::false_*)\n{\n   typedef typename traits::string_type string_type;\n   typedef typename basic_char_set<charT, traits>::list_iterator item_iterator;\n   typedef typename basic_char_set<charT, traits>::set_iterator  set_iterator;\n   typedef typename traits::char_class_type m_type;\n   \n   re_set_long<m_type>* result = static_cast<re_set_long<m_type>*>(append_state(syntax_element_long_set, sizeof(re_set_long<m_type>)));\n   //\n   // fill in the basics:\n   //\n   result->csingles = static_cast<unsigned int>(::boost::BOOST_REGEX_DETAIL_NS::distance(char_set.singles_begin(), char_set.singles_end()));\n   result->cranges = static_cast<unsigned int>(::boost::BOOST_REGEX_DETAIL_NS::distance(char_set.ranges_begin(), char_set.ranges_end())) / 2;\n   result->cequivalents = static_cast<unsigned int>(::boost::BOOST_REGEX_DETAIL_NS::distance(char_set.equivalents_begin(), char_set.equivalents_end()));\n   result->cclasses = char_set.classes();\n   result->cnclasses = char_set.negated_classes();\n   if(flags() & regbase::icase)\n   {\n      // adjust classes as needed:\n      if(((result->cclasses & m_lower_mask) == m_lower_mask) || ((result->cclasses & m_upper_mask) == m_upper_mask))\n         result->cclasses |= m_alpha_mask;\n      if(((result->cnclasses & m_lower_mask) == m_lower_mask) || ((result->cnclasses & m_upper_mask) == m_upper_mask))\n         result->cnclasses |= m_alpha_mask;\n   }\n\n   result->isnot = char_set.is_negated();\n   result->singleton = !char_set.has_digraphs();\n   //\n   // remember where the state is for later:\n   //\n   std::ptrdiff_t offset = getoffset(result);\n   //\n   // now extend with all the singles:\n   //\n   item_iterator first, last;\n   set_iterator sfirst, slast;\n   sfirst = char_set.singles_begin();\n   slast = char_set.singles_end();\n   while(sfirst != slast)\n   {\n      charT* p = static_cast<charT*>(this->m_pdata->m_data.extend(sizeof(charT) * (sfirst->first == static_cast<charT>(0) ? 1 : sfirst->second ? 3 : 2)));\n      p[0] = m_traits.translate(sfirst->first, m_icase);\n      if(sfirst->first == static_cast<charT>(0))\n      {\n         p[0] = 0;\n      }\n      else if(sfirst->second)\n      {\n         p[1] = m_traits.translate(sfirst->second, m_icase);\n         p[2] = 0;\n      }\n      else\n         p[1] = 0;\n      ++sfirst;\n   }\n   //\n   // now extend with all the ranges:\n   //\n   first = char_set.ranges_begin();\n   last = char_set.ranges_end();\n   while(first != last)\n   {\n      // first grab the endpoints of the range:\n      digraph<charT> c1 = *first;\n      c1.first = this->m_traits.translate(c1.first, this->m_icase);\n      c1.second = this->m_traits.translate(c1.second, this->m_icase);\n      ++first;\n      digraph<charT> c2 = *first;\n      c2.first = this->m_traits.translate(c2.first, this->m_icase);\n      c2.second = this->m_traits.translate(c2.second, this->m_icase);\n      ++first;\n      string_type s1, s2;\n      // different actions now depending upon whether collation is turned on:\n      if(flags() & regex_constants::collate)\n      {\n         // we need to transform our range into sort keys:\n         charT a1[3] = { c1.first, c1.second, charT(0), };\n         charT a2[3] = { c2.first, c2.second, charT(0), };\n         s1 = this->m_traits.transform(a1, (a1[1] ? a1+2 : a1+1));\n         s2 = this->m_traits.transform(a2, (a2[1] ? a2+2 : a2+1));\n         if(s1.empty())\n            s1 = string_type(1, charT(0));\n         if(s2.empty())\n            s2 = string_type(1, charT(0));\n      }\n      else\n      {\n         if(c1.second)\n         {\n            s1.insert(s1.end(), c1.first);\n            s1.insert(s1.end(), c1.second);\n         }\n         else\n            s1 = string_type(1, c1.first);\n         if(c2.second)\n         {\n            s2.insert(s2.end(), c2.first);\n            s2.insert(s2.end(), c2.second);\n         }\n         else\n            s2.insert(s2.end(), c2.first);\n      }\n      if(s1 > s2)\n      {\n         // Oops error:\n         return 0;\n      }\n      charT* p = static_cast<charT*>(this->m_pdata->m_data.extend(sizeof(charT) * (s1.size() + s2.size() + 2) ) );\n      BOOST_REGEX_DETAIL_NS::copy(s1.begin(), s1.end(), p);\n      p[s1.size()] = charT(0);\n      p += s1.size() + 1;\n      BOOST_REGEX_DETAIL_NS::copy(s2.begin(), s2.end(), p);\n      p[s2.size()] = charT(0);\n   }\n   //\n   // now process the equivalence classes:\n   //\n   sfirst = char_set.equivalents_begin();\n   slast = char_set.equivalents_end();\n   while(sfirst != slast)\n   {\n      string_type s;\n      if(sfirst->second)\n      {\n         charT cs[3] = { sfirst->first, sfirst->second, charT(0), };\n         s = m_traits.transform_primary(cs, cs+2);\n      }\n      else\n         s = m_traits.transform_primary(&sfirst->first, &sfirst->first+1);\n      if(s.empty())\n         return 0;  // invalid or unsupported equivalence class\n      charT* p = static_cast<charT*>(this->m_pdata->m_data.extend(sizeof(charT) * (s.size()+1) ) );\n      BOOST_REGEX_DETAIL_NS::copy(s.begin(), s.end(), p);\n      p[s.size()] = charT(0);\n      ++sfirst;\n   }\n   //\n   // finally reset the address of our last state:\n   //\n   m_last_state = result = static_cast<re_set_long<m_type>*>(getaddress(offset));\n   return result;\n}\n\ntemplate<class T>\ninline bool char_less(T t1, T t2)\n{\n   return t1 < t2;\n}\ninline bool char_less(char t1, char t2)\n{\n   return static_cast<unsigned char>(t1) < static_cast<unsigned char>(t2);\n}\ninline bool char_less(signed char t1, signed char t2)\n{\n   return static_cast<unsigned char>(t1) < static_cast<unsigned char>(t2);\n}\n\ntemplate <class charT, class traits>\nre_syntax_base* basic_regex_creator<charT, traits>::append_set(\n   const basic_char_set<charT, traits>& char_set, mpl::true_*)\n{\n   typedef typename traits::string_type string_type;\n   typedef typename basic_char_set<charT, traits>::list_iterator item_iterator;\n   typedef typename basic_char_set<charT, traits>::set_iterator set_iterator;\n\n   re_set* result = static_cast<re_set*>(append_state(syntax_element_set, sizeof(re_set)));\n   bool negate = char_set.is_negated();\n   std::memset(result->_map, 0, sizeof(result->_map));\n   //\n   // handle singles first:\n   //\n   item_iterator first, last;\n   set_iterator sfirst, slast;\n   sfirst = char_set.singles_begin();\n   slast = char_set.singles_end();\n   while(sfirst != slast)\n   {\n      for(unsigned int i = 0; i < (1 << CHAR_BIT); ++i)\n      {\n         if(this->m_traits.translate(static_cast<charT>(i), this->m_icase)\n            == this->m_traits.translate(sfirst->first, this->m_icase))\n            result->_map[i] = true;\n      }\n      ++sfirst;\n   }\n   //\n   // OK now handle ranges:\n   //\n   first = char_set.ranges_begin();\n   last = char_set.ranges_end();\n   while(first != last)\n   {\n      // first grab the endpoints of the range:\n      charT c1 = this->m_traits.translate(first->first, this->m_icase);\n      ++first;\n      charT c2 = this->m_traits.translate(first->first, this->m_icase);\n      ++first;\n      // different actions now depending upon whether collation is turned on:\n      if(flags() & regex_constants::collate)\n      {\n         // we need to transform our range into sort keys:\n         charT c3[2] = { c1, charT(0), };\n         string_type s1 = this->m_traits.transform(c3, c3+1);\n         c3[0] = c2;\n         string_type s2 = this->m_traits.transform(c3, c3+1);\n         if(s1 > s2)\n         {\n            // Oops error:\n            return 0;\n         }\n         BOOST_REGEX_ASSERT(c3[1] == charT(0));\n         for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n         {\n            c3[0] = static_cast<charT>(i);\n            string_type s3 = this->m_traits.transform(c3, c3 +1);\n            if((s1 <= s3) && (s3 <= s2))\n               result->_map[i] = true;\n         }\n      }\n      else\n      {\n         if(char_less(c2, c1))\n         {\n            // Oops error:\n            return 0;\n         }\n         // everything in range matches:\n         std::memset(result->_map + static_cast<unsigned char>(c1), true, static_cast<unsigned char>(1u) + static_cast<unsigned char>(static_cast<unsigned char>(c2) - static_cast<unsigned char>(c1)));\n      }\n   }\n   //\n   // and now the classes:\n   //\n   typedef typename traits::char_class_type m_type;\n   m_type m = char_set.classes();\n   if(flags() & regbase::icase)\n   {\n      // adjust m as needed:\n      if(((m & m_lower_mask) == m_lower_mask) || ((m & m_upper_mask) == m_upper_mask))\n         m |= m_alpha_mask;\n   }\n   if(m != 0)\n   {\n      for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n      {\n         if(this->m_traits.isctype(static_cast<charT>(i), m))\n            result->_map[i] = true;\n      }\n   }\n   //\n   // and now the negated classes:\n   //\n   m = char_set.negated_classes();\n   if(flags() & regbase::icase)\n   {\n      // adjust m as needed:\n      if(((m & m_lower_mask) == m_lower_mask) || ((m & m_upper_mask) == m_upper_mask))\n         m |= m_alpha_mask;\n   }\n   if(m != 0)\n   {\n      for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n      {\n         if(0 == this->m_traits.isctype(static_cast<charT>(i), m))\n            result->_map[i] = true;\n      }\n   }\n   //\n   // now process the equivalence classes:\n   //\n   sfirst = char_set.equivalents_begin();\n   slast = char_set.equivalents_end();\n   while(sfirst != slast)\n   {\n      string_type s;\n      BOOST_REGEX_ASSERT(static_cast<charT>(0) == sfirst->second);\n      s = m_traits.transform_primary(&sfirst->first, &sfirst->first+1);\n      if(s.empty())\n         return 0;  // invalid or unsupported equivalence class\n      for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n      {\n         charT c[2] = { (static_cast<charT>(i)), charT(0), };\n         string_type s2 = this->m_traits.transform_primary(c, c+1);\n         if(s == s2)\n            result->_map[i] = true;\n      }\n      ++sfirst;\n   }\n   if(negate)\n   {\n      for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n      {\n         result->_map[i] = !(result->_map[i]);\n      }\n   }\n   return result;\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::finalize(const charT* p1, const charT* p2)\n{\n   if(this->m_pdata->m_status)\n      return;\n   // we've added all the states we need, now finish things off.\n   // start by adding a terminating state:\n   append_state(syntax_element_match);\n   // extend storage to store original expression:\n   std::ptrdiff_t len = p2 - p1;\n   m_pdata->m_expression_len = len;\n   charT* ps = static_cast<charT*>(m_pdata->m_data.extend(sizeof(charT) * (1 + (p2 - p1))));\n   m_pdata->m_expression = ps;\n   BOOST_REGEX_DETAIL_NS::copy(p1, p2, ps);\n   ps[p2 - p1] = 0;\n   // fill in our other data...\n   // successful parsing implies a zero status:\n   m_pdata->m_status = 0;\n   // get the first state of the machine:\n   m_pdata->m_first_state = static_cast<re_syntax_base*>(m_pdata->m_data.data());\n   // fixup pointers in the machine:\n   fixup_pointers(m_pdata->m_first_state);\n   if(m_has_recursions)\n   {\n      m_pdata->m_has_recursions = true;\n      fixup_recursions(m_pdata->m_first_state);\n      if(this->m_pdata->m_status)\n         return;\n   }\n   else\n      m_pdata->m_has_recursions = false;\n   // create nested startmaps:\n   create_startmaps(m_pdata->m_first_state);\n   // create main startmap:\n   std::memset(m_pdata->m_startmap, 0, sizeof(m_pdata->m_startmap));\n   m_pdata->m_can_be_null = 0;\n\n   m_bad_repeats = 0;\n   if(m_has_recursions)\n      m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u);\n   create_startmap(m_pdata->m_first_state, m_pdata->m_startmap, &(m_pdata->m_can_be_null), mask_all);\n   // get the restart type:\n   m_pdata->m_restart_type = get_restart_type(m_pdata->m_first_state);\n   // optimise a leading repeat if there is one:\n   probe_leading_repeat(m_pdata->m_first_state);\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::fixup_pointers(re_syntax_base* state)\n{\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_recurse:\n         m_has_recursions = true;\n         if(state->next.i)\n            state->next.p = getaddress(state->next.i, state);\n         else\n            state->next.p = 0;\n         break;\n      case syntax_element_rep:\n      case syntax_element_dot_rep:\n      case syntax_element_char_rep:\n      case syntax_element_short_set_rep:\n      case syntax_element_long_set_rep:\n         // set the state_id of this repeat:\n         static_cast<re_repeat*>(state)->state_id = m_repeater_id++;\n         BOOST_FALLTHROUGH;\n      case syntax_element_alt:\n         std::memset(static_cast<re_alt*>(state)->_map, 0, sizeof(static_cast<re_alt*>(state)->_map));\n         static_cast<re_alt*>(state)->can_be_null = 0;\n         BOOST_FALLTHROUGH;\n      case syntax_element_jump:\n         static_cast<re_jump*>(state)->alt.p = getaddress(static_cast<re_jump*>(state)->alt.i, state);\n         BOOST_FALLTHROUGH;\n      default:\n         if(state->next.i)\n            state->next.p = getaddress(state->next.i, state);\n         else\n            state->next.p = 0;\n      }\n      state = state->next.p;\n   }\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::fixup_recursions(re_syntax_base* state)\n{\n   re_syntax_base* base = state;\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_assert_backref:\n         {\n            // just check that the index is valid:\n            int idx = static_cast<const re_brace*>(state)->index;\n            if(idx < 0)\n            {\n               idx = -idx-1;\n               if(idx >= hash_value_mask)\n               {\n                  idx = m_pdata->get_id(idx);\n                  if(idx <= 0)\n                  {\n                     // check of sub-expression that doesn't exist:\n                     if(0 == this->m_pdata->m_status) // update the error code if not already set\n                        this->m_pdata->m_status = boost::regex_constants::error_bad_pattern;\n                     //\n                     // clear the expression, we should be empty:\n                     //\n                     this->m_pdata->m_expression = 0;\n                     this->m_pdata->m_expression_len = 0;\n                     //\n                     // and throw if required:\n                     //\n                     if(0 == (this->flags() & regex_constants::no_except))\n                     {\n                        std::string message = \"Encountered a forward reference to a marked sub-expression that does not exist.\";\n                        boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0);\n                        e.raise();\n                     }\n                  }\n               }\n            }\n         }\n         break;\n      case syntax_element_recurse:\n         {\n            bool ok = false;\n            re_syntax_base* p = base;\n            std::ptrdiff_t idx = static_cast<re_jump*>(state)->alt.i;\n            if(idx >= hash_value_mask)\n            {\n               //\n               // There may be more than one capture group with this hash, just do what Perl\n               // does and recurse to the leftmost:\n               //\n               idx = m_pdata->get_id(static_cast<int>(idx));\n            }\n            if(idx < 0)\n            {\n               ok = false;\n            }\n            else\n            {\n               while(p)\n               {\n                  if((p->type == syntax_element_startmark) && (static_cast<re_brace*>(p)->index == idx))\n                  {\n                     //\n                     // We've found the target of the recursion, set the jump target:\n                     //\n                     static_cast<re_jump*>(state)->alt.p = p;\n                     ok = true;\n                     // \n                     // Now scan the target for nested repeats:\n                     //\n                     p = p->next.p;\n                     int next_rep_id = 0;\n                     while(p)\n                     {\n                        switch(p->type)\n                        {\n                        case syntax_element_rep:\n                        case syntax_element_dot_rep:\n                        case syntax_element_char_rep:\n                        case syntax_element_short_set_rep:\n                        case syntax_element_long_set_rep:\n                           next_rep_id = static_cast<re_repeat*>(p)->state_id;\n                           break;\n                        case syntax_element_endmark:\n                           if(static_cast<const re_brace*>(p)->index == idx)\n                              next_rep_id = -1;\n                           break;\n                        default:\n                           break;\n                        }\n                        if(next_rep_id)\n                           break;\n                        p = p->next.p;\n                     }\n                     if(next_rep_id > 0)\n                     {\n                        static_cast<re_recurse*>(state)->state_id = next_rep_id - 1;\n                     }\n\n                     break;\n                  }\n                  p = p->next.p;\n               }\n            }\n            if(!ok)\n            {\n               // recursion to sub-expression that doesn't exist:\n               if(0 == this->m_pdata->m_status) // update the error code if not already set\n                  this->m_pdata->m_status = boost::regex_constants::error_bad_pattern;\n               //\n               // clear the expression, we should be empty:\n               //\n               this->m_pdata->m_expression = 0;\n               this->m_pdata->m_expression_len = 0;\n               //\n               // and throw if required:\n               //\n               if(0 == (this->flags() & regex_constants::no_except))\n               {\n                  std::string message = \"Encountered a forward reference to a recursive sub-expression that does not exist.\";\n                  boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0);\n                  e.raise();\n               }\n            }\n         }\n         break;\n      default:\n         break;\n      }\n      state = state->next.p;\n   }\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::create_startmaps(re_syntax_base* state)\n{\n   // non-recursive implementation:\n   // create the last map in the machine first, so that earlier maps\n   // can make use of the result...\n   //\n   // This was originally a recursive implementation, but that caused stack\n   // overflows with complex expressions on small stacks (think COM+).\n\n   // start by saving the case setting:\n   bool l_icase = m_icase;\n   std::vector<std::pair<bool, re_syntax_base*> > v;\n\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_toggle_case:\n         // we need to track case changes here:\n         m_icase = static_cast<re_case*>(state)->icase;\n         state = state->next.p;\n         continue;\n      case syntax_element_alt:\n      case syntax_element_rep:\n      case syntax_element_dot_rep:\n      case syntax_element_char_rep:\n      case syntax_element_short_set_rep:\n      case syntax_element_long_set_rep:\n         // just push the state onto our stack for now:\n         v.push_back(std::pair<bool, re_syntax_base*>(m_icase, state));\n         state = state->next.p;\n         break;\n      case syntax_element_backstep:\n         // we need to calculate how big the backstep is:\n         static_cast<re_brace*>(state)->index\n            = this->calculate_backstep(state->next.p);\n         if(static_cast<re_brace*>(state)->index < 0)\n         {\n            // Oops error:\n            if(0 == this->m_pdata->m_status) // update the error code if not already set\n               this->m_pdata->m_status = boost::regex_constants::error_bad_pattern;\n            //\n            // clear the expression, we should be empty:\n            //\n            this->m_pdata->m_expression = 0;\n            this->m_pdata->m_expression_len = 0;\n            //\n            // and throw if required:\n            //\n            if(0 == (this->flags() & regex_constants::no_except))\n            {\n               std::string message = \"Invalid lookbehind assertion encountered in the regular expression.\";\n               boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0);\n               e.raise();\n            }\n         }\n         BOOST_FALLTHROUGH;\n      default:\n         state = state->next.p;\n      }\n   }\n\n   // now work through our list, building all the maps as we go:\n   while(!v.empty())\n   {\n      // Initialize m_recursion_checks if we need it:\n      if(m_has_recursions)\n         m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u);\n\n      const std::pair<bool, re_syntax_base*>& p = v.back();\n      m_icase = p.first;\n      state = p.second;\n      v.pop_back();\n\n      // Build maps:\n      m_bad_repeats = 0;\n      create_startmap(state->next.p, static_cast<re_alt*>(state)->_map, &static_cast<re_alt*>(state)->can_be_null, mask_take);\n      m_bad_repeats = 0;\n\n      if(m_has_recursions)\n         m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u);\n      create_startmap(static_cast<re_alt*>(state)->alt.p, static_cast<re_alt*>(state)->_map, &static_cast<re_alt*>(state)->can_be_null, mask_skip);\n      // adjust the type of the state to allow for faster matching:\n      state->type = this->get_repeat_type(state);\n   }\n   // restore case sensitivity:\n   m_icase = l_icase;\n}\n\ntemplate <class charT, class traits>\nint basic_regex_creator<charT, traits>::calculate_backstep(re_syntax_base* state)\n{\n   typedef typename traits::char_class_type m_type;\n   int result = 0;\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_startmark:\n         if((static_cast<re_brace*>(state)->index == -1)\n            || (static_cast<re_brace*>(state)->index == -2))\n         {\n            state = static_cast<re_jump*>(state->next.p)->alt.p->next.p;\n            continue;\n         }\n         else if(static_cast<re_brace*>(state)->index == -3)\n         {\n            state = state->next.p->next.p;\n            continue;\n         }\n         break;\n      case syntax_element_endmark:\n         if((static_cast<re_brace*>(state)->index == -1)\n            || (static_cast<re_brace*>(state)->index == -2))\n            return result;\n         break;\n      case syntax_element_literal:\n         result += static_cast<re_literal*>(state)->length;\n         break;\n      case syntax_element_wild:\n      case syntax_element_set:\n         result += 1;\n         break;\n      case syntax_element_dot_rep:\n      case syntax_element_char_rep:\n      case syntax_element_short_set_rep:\n      case syntax_element_backref:\n      case syntax_element_rep:\n      case syntax_element_combining:\n      case syntax_element_long_set_rep:\n      case syntax_element_backstep:\n         {\n            re_repeat* rep = static_cast<re_repeat *>(state);\n            // adjust the type of the state to allow for faster matching:\n            state->type = this->get_repeat_type(state);\n            if((state->type == syntax_element_dot_rep) \n               || (state->type == syntax_element_char_rep)\n               || (state->type == syntax_element_short_set_rep))\n            {\n               if(rep->max != rep->min)\n                  return -1;\n               result += static_cast<int>(rep->min);\n               state = rep->alt.p;\n               continue;\n            }\n            else if(state->type == syntax_element_long_set_rep)\n            {\n               BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_long_set);\n               if(static_cast<re_set_long<m_type>*>(rep->next.p)->singleton == 0)\n                  return -1;\n               if(rep->max != rep->min)\n                  return -1;\n               result += static_cast<int>(rep->min);\n               state = rep->alt.p;\n               continue;\n            }\n         }\n         return -1;\n      case syntax_element_long_set:\n         if(static_cast<re_set_long<m_type>*>(state)->singleton == 0)\n            return -1;\n         result += 1;\n         break;\n      case syntax_element_jump:\n         state = static_cast<re_jump*>(state)->alt.p;\n         continue;\n      case syntax_element_alt:\n         {\n            int r1 = calculate_backstep(state->next.p);\n            int r2 = calculate_backstep(static_cast<re_alt*>(state)->alt.p);\n            if((r1 < 0) || (r1 != r2))\n               return -1;\n            return result + r1;\n         }\n      default:\n         break;\n      }\n      state = state->next.p;\n   }\n   return -1;\n}\n\nstruct recursion_saver\n{\n   std::vector<unsigned char> saved_state;\n   std::vector<unsigned char>* state;\n   recursion_saver(std::vector<unsigned char>* p) : saved_state(*p), state(p) {}\n   ~recursion_saver()\n   {\n      state->swap(saved_state);\n   }\n};\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask)\n{\n   recursion_saver saved_recursions(&m_recursion_checks);\n   int not_last_jump = 1;\n   re_syntax_base* recursion_start = 0;\n   int recursion_sub = 0;\n   re_syntax_base* recursion_restart = 0;\n\n   // track case sensitivity:\n   bool l_icase = m_icase;\n\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_toggle_case:\n         l_icase = static_cast<re_case*>(state)->icase;\n         state = state->next.p;\n         break;\n      case syntax_element_literal:\n      {\n         // don't set anything in *pnull, set each element in l_map\n         // that could match the first character in the literal:\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            charT first_char = *static_cast<charT*>(static_cast<void*>(static_cast<re_literal*>(state) + 1));\n            for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i)\n            {\n               if(m_traits.translate(static_cast<charT>(i), l_icase) == first_char)\n                  l_map[i] |= mask;\n            }\n         }\n         return;\n      }\n      case syntax_element_end_line:\n      {\n         // next character must be a line separator (if there is one):\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            l_map[static_cast<unsigned>('\\n')] |= mask;\n            l_map[static_cast<unsigned>('\\r')] |= mask;\n            l_map[static_cast<unsigned>('\\f')] |= mask;\n            l_map[0x85] |= mask;\n         }\n         // now figure out if we can match a NULL string at this point:\n         if(pnull)\n            create_startmap(state->next.p, 0, pnull, mask);\n         return;\n      }\n      case syntax_element_recurse:\n         {\n            BOOST_REGEX_ASSERT(static_cast<const re_jump*>(state)->alt.p->type == syntax_element_startmark);\n            recursion_sub = static_cast<re_brace*>(static_cast<const re_jump*>(state)->alt.p)->index;\n            if(m_recursion_checks[recursion_sub] & 1u)\n            {\n               // Infinite recursion!!\n               if(0 == this->m_pdata->m_status) // update the error code if not already set\n                  this->m_pdata->m_status = boost::regex_constants::error_bad_pattern;\n               //\n               // clear the expression, we should be empty:\n               //\n               this->m_pdata->m_expression = 0;\n               this->m_pdata->m_expression_len = 0;\n               //\n               // and throw if required:\n               //\n               if(0 == (this->flags() & regex_constants::no_except))\n               {\n                  std::string message = \"Encountered an infinite recursion.\";\n                  boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0);\n                  e.raise();\n               }\n            }\n            else if(recursion_start == 0)\n            {\n               recursion_start = state;\n               recursion_restart = state->next.p;\n               state = static_cast<re_jump*>(state)->alt.p;\n               m_recursion_checks[recursion_sub] |= 1u;\n               break;\n            }\n            m_recursion_checks[recursion_sub] |= 1u;\n            // can't handle nested recursion here...\n            BOOST_FALLTHROUGH;\n         }\n      case syntax_element_backref:\n         // can be null, and any character can match:\n         if(pnull)\n            *pnull |= mask;\n         BOOST_FALLTHROUGH;\n      case syntax_element_wild:\n      {\n         // can't be null, any character can match:\n         set_all_masks(l_map, mask);\n         return;\n      }\n      case syntax_element_accept:\n      case syntax_element_match:\n      {\n         // must be null, any character can match:\n         set_all_masks(l_map, mask);\n         if(pnull)\n            *pnull |= mask;\n         return;\n      }\n      case syntax_element_word_start:\n      {\n         // recurse, then AND with all the word characters:\n         create_startmap(state->next.p, l_map, pnull, mask);\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i)\n            {\n               if(!m_traits.isctype(static_cast<charT>(i), m_word_mask))\n                  l_map[i] &= static_cast<unsigned char>(~mask);\n            }\n         }\n         return;\n      }\n      case syntax_element_word_end:\n      {\n         // recurse, then AND with all the word characters:\n         create_startmap(state->next.p, l_map, pnull, mask);\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i)\n            {\n               if(m_traits.isctype(static_cast<charT>(i), m_word_mask))\n                  l_map[i] &= static_cast<unsigned char>(~mask);\n            }\n         }\n         return;\n      }\n      case syntax_element_buffer_end:\n      {\n         // we *must be null* :\n         if(pnull)\n            *pnull |= mask;\n         return;\n      }\n      case syntax_element_long_set:\n         if(l_map)\n         {\n            typedef typename traits::char_class_type m_type;\n            if(static_cast<re_set_long<m_type>*>(state)->singleton)\n            {\n               l_map[0] |= mask_init;\n               for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i)\n               {\n                  charT c = static_cast<charT>(i);\n                  if(&c != re_is_set_member(&c, &c + 1, static_cast<re_set_long<m_type>*>(state), *m_pdata, l_icase))\n                     l_map[i] |= mask;\n               }\n            }\n            else\n               set_all_masks(l_map, mask);\n         }\n         return;\n      case syntax_element_set:\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i)\n            {\n               if(static_cast<re_set*>(state)->_map[\n                  static_cast<unsigned char>(m_traits.translate(static_cast<charT>(i), l_icase))])\n                  l_map[i] |= mask;\n            }\n         }\n         return;\n      case syntax_element_jump:\n         // take the jump:\n         state = static_cast<re_alt*>(state)->alt.p;\n         not_last_jump = -1;\n         break;\n      case syntax_element_alt:\n      case syntax_element_rep:\n      case syntax_element_dot_rep:\n      case syntax_element_char_rep:\n      case syntax_element_short_set_rep:\n      case syntax_element_long_set_rep:\n         {\n            re_alt* rep = static_cast<re_alt*>(state);\n            if(rep->_map[0] & mask_init)\n            {\n               if(l_map)\n               {\n                  // copy previous results:\n                  l_map[0] |= mask_init;\n                  for(unsigned int i = 0; i <= UCHAR_MAX; ++i)\n                  {\n                     if(rep->_map[i] & mask_any)\n                        l_map[i] |= mask;\n                  }\n               }\n               if(pnull)\n               {\n                  if(rep->can_be_null & mask_any)\n                     *pnull |= mask;\n               }\n            }\n            else\n            {\n               // we haven't created a startmap for this alternative yet\n               // so take the union of the two options:\n               if(is_bad_repeat(state))\n               {\n                  set_all_masks(l_map, mask);\n                  if(pnull)\n                     *pnull |= mask;\n                  return;\n               }\n               set_bad_repeat(state);\n               create_startmap(state->next.p, l_map, pnull, mask);\n               if((state->type == syntax_element_alt)\n                  || (static_cast<re_repeat*>(state)->min == 0)\n                  || (not_last_jump == 0))\n                  create_startmap(rep->alt.p, l_map, pnull, mask);\n            }\n         }\n         return;\n      case syntax_element_soft_buffer_end:\n         // match newline or null:\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            l_map[static_cast<unsigned>('\\n')] |= mask;\n            l_map[static_cast<unsigned>('\\r')] |= mask;\n         }\n         if(pnull)\n            *pnull |= mask;\n         return;\n      case syntax_element_endmark:\n         // need to handle independent subs as a special case:\n         if(static_cast<re_brace*>(state)->index < 0)\n         {\n            // can be null, any character can match:\n            set_all_masks(l_map, mask);\n            if(pnull)\n               *pnull |= mask;\n            return;\n         }\n         else if(recursion_start && (recursion_sub != 0) && (recursion_sub == static_cast<re_brace*>(state)->index))\n         {\n            // recursion termination:\n            recursion_start = 0;\n            state = recursion_restart;\n            break;\n         }\n\n         //\n         // Normally we just go to the next state... but if this sub-expression is\n         // the target of a recursion, then we might be ending a recursion, in which\n         // case we should check whatever follows that recursion, as well as whatever\n         // follows this state:\n         //\n         if(m_pdata->m_has_recursions && static_cast<re_brace*>(state)->index)\n         {\n            bool ok = false;\n            re_syntax_base* p = m_pdata->m_first_state;\n            while(p)\n            {\n               if(p->type == syntax_element_recurse)\n               {\n                  re_brace* p2 = static_cast<re_brace*>(static_cast<re_jump*>(p)->alt.p);\n                  if((p2->type == syntax_element_startmark) && (p2->index == static_cast<re_brace*>(state)->index))\n                  {\n                     ok = true;\n                     break;\n                  }\n               }\n               p = p->next.p;\n            }\n            if(ok && ((m_recursion_checks[static_cast<re_brace*>(state)->index] & 2u) == 0))\n            {\n               m_recursion_checks[static_cast<re_brace*>(state)->index] |= 2u;\n               create_startmap(p->next.p, l_map, pnull, mask);\n            }\n         }\n         state = state->next.p;\n         break;\n\n      case syntax_element_commit:\n         set_all_masks(l_map, mask);\n         // Continue scanning so we can figure out whether we can be null:\n         state = state->next.p;\n         break;\n      case syntax_element_startmark:\n         // need to handle independent subs as a special case:\n         if(static_cast<re_brace*>(state)->index == -3)\n         {\n            state = state->next.p->next.p;\n            break;\n         }\n         BOOST_FALLTHROUGH;\n      default:\n         state = state->next.p;\n      }\n      ++not_last_jump;\n   }\n}\n\ntemplate <class charT, class traits>\nunsigned basic_regex_creator<charT, traits>::get_restart_type(re_syntax_base* state)\n{\n   //\n   // find out how the machine starts, so we can optimise the search:\n   //\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_startmark:\n      case syntax_element_endmark:\n         state = state->next.p;\n         continue;\n      case syntax_element_start_line:\n         return regbase::restart_line;\n      case syntax_element_word_start:\n         return regbase::restart_word;\n      case syntax_element_buffer_start:\n         return regbase::restart_buf;\n      case syntax_element_restart_continue:\n         return regbase::restart_continue;\n      default:\n         state = 0;\n         continue;\n      }\n   }\n   return regbase::restart_any;\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::set_all_masks(unsigned char* bits, unsigned char mask)\n{\n   //\n   // set mask in all of bits elements, \n   // if bits[0] has mask_init not set then we can \n   // optimise this to a call to memset:\n   //\n   if(bits)\n   {\n      if(bits[0] == 0)\n         (std::memset)(bits, mask, 1u << CHAR_BIT);\n      else\n      {\n         for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n            bits[i] |= mask;\n      }\n      bits[0] |= mask_init;\n   }\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_creator<charT, traits>::is_bad_repeat(re_syntax_base* pt)\n{\n   switch(pt->type)\n   {\n   case syntax_element_rep:\n   case syntax_element_dot_rep:\n   case syntax_element_char_rep:\n   case syntax_element_short_set_rep:\n   case syntax_element_long_set_rep:\n      {\n         unsigned state_id = static_cast<re_repeat*>(pt)->state_id;\n         if(state_id >= sizeof(m_bad_repeats) * CHAR_BIT)\n            return true;  // run out of bits, assume we can't traverse this one.\n         static const boost::uintmax_t one = 1uL;\n         return m_bad_repeats & (one << state_id);\n      }\n   default:\n      return false;\n   }\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::set_bad_repeat(re_syntax_base* pt)\n{\n   switch(pt->type)\n   {\n   case syntax_element_rep:\n   case syntax_element_dot_rep:\n   case syntax_element_char_rep:\n   case syntax_element_short_set_rep:\n   case syntax_element_long_set_rep:\n      {\n         unsigned state_id = static_cast<re_repeat*>(pt)->state_id;\n         static const boost::uintmax_t one = 1uL;\n         if(state_id <= sizeof(m_bad_repeats) * CHAR_BIT)\n            m_bad_repeats |= (one << state_id);\n      }\n      break;\n   default:\n      break;\n   }\n}\n\ntemplate <class charT, class traits>\nsyntax_element_type basic_regex_creator<charT, traits>::get_repeat_type(re_syntax_base* state)\n{\n   typedef typename traits::char_class_type m_type;\n   if(state->type == syntax_element_rep)\n   {\n      // check to see if we are repeating a single state:\n      if(state->next.p->next.p->next.p == static_cast<re_alt*>(state)->alt.p)\n      {\n         switch(state->next.p->type)\n         {\n         case BOOST_REGEX_DETAIL_NS::syntax_element_wild:\n            return BOOST_REGEX_DETAIL_NS::syntax_element_dot_rep;\n         case BOOST_REGEX_DETAIL_NS::syntax_element_literal:\n            return BOOST_REGEX_DETAIL_NS::syntax_element_char_rep;\n         case BOOST_REGEX_DETAIL_NS::syntax_element_set:\n            return BOOST_REGEX_DETAIL_NS::syntax_element_short_set_rep;\n         case BOOST_REGEX_DETAIL_NS::syntax_element_long_set:\n            if(static_cast<BOOST_REGEX_DETAIL_NS::re_set_long<m_type>*>(state->next.p)->singleton)\n               return BOOST_REGEX_DETAIL_NS::syntax_element_long_set_rep;\n            break;\n         default:\n            break;\n         }\n      }\n   }\n   return state->type;\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::probe_leading_repeat(re_syntax_base* state)\n{\n   // enumerate our states, and see if we have a leading repeat \n   // for which failed search restarts can be optimized;\n   do\n   {\n      switch(state->type)\n      {\n      case syntax_element_startmark:\n         if(static_cast<re_brace*>(state)->index >= 0)\n         {\n            state = state->next.p;\n            continue;\n         }\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#pragma warning(disable:6011)\n#endif\n         if((static_cast<re_brace*>(state)->index == -1)\n            || (static_cast<re_brace*>(state)->index == -2))\n         {\n            // skip past the zero width assertion:\n            state = static_cast<const re_jump*>(state->next.p)->alt.p->next.p;\n            continue;\n         }\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n         if(static_cast<re_brace*>(state)->index == -3)\n         {\n            // Have to skip the leading jump state:\n            state = state->next.p->next.p;\n            continue;\n         }\n         return;\n      case syntax_element_endmark:\n      case syntax_element_start_line:\n      case syntax_element_end_line:\n      case syntax_element_word_boundary:\n      case syntax_element_within_word:\n      case syntax_element_word_start:\n      case syntax_element_word_end:\n      case syntax_element_buffer_start:\n      case syntax_element_buffer_end:\n      case syntax_element_restart_continue:\n         state = state->next.p;\n         break;\n      case syntax_element_dot_rep:\n      case syntax_element_char_rep:\n      case syntax_element_short_set_rep:\n      case syntax_element_long_set_rep:\n         if(this->m_has_backrefs == 0)\n            static_cast<re_repeat*>(state)->leading = true;\n         BOOST_FALLTHROUGH;\n      default:\n         return;\n      }\n   }while(state);\n}\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/basic_regex_parser.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         basic_regex_parser.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class basic_regex_parser.\n  */\n\n#ifndef BOOST_REGEX_V4_BASIC_REGEX_PARSER_HPP\n#define BOOST_REGEX_V4_BASIC_REGEX_PARSER_HPP\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#if BOOST_MSVC >= 1800\n#pragma warning(disable: 26812)\n#endif\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4244)\n#if BOOST_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\ninline boost::intmax_t umax(mpl::false_ const&)\n{\n   // Get out clause here, just in case numeric_limits is unspecialized:\n   return std::numeric_limits<boost::intmax_t>::is_specialized ? (std::numeric_limits<boost::intmax_t>::max)() : INT_MAX;\n}\ninline boost::intmax_t umax(mpl::true_ const&)\n{\n   return (std::numeric_limits<std::size_t>::max)();\n}\n\ninline boost::intmax_t umax()\n{\n   return umax(mpl::bool_<std::numeric_limits<boost::intmax_t>::digits >= std::numeric_limits<std::size_t>::digits>());\n}\n\ntemplate <class charT, class traits>\nclass basic_regex_parser : public basic_regex_creator<charT, traits>\n{\npublic:\n   basic_regex_parser(regex_data<charT, traits>* data);\n   void parse(const charT* p1, const charT* p2, unsigned flags);\n   void fail(regex_constants::error_type error_code, std::ptrdiff_t position);\n   void fail(regex_constants::error_type error_code, std::ptrdiff_t position, std::string message, std::ptrdiff_t start_pos);\n   void fail(regex_constants::error_type error_code, std::ptrdiff_t position, const std::string& message)\n   {\n      fail(error_code, position, message, position);\n   }\n\n   bool parse_all();\n   bool parse_basic();\n   bool parse_extended();\n   bool parse_literal();\n   bool parse_open_paren();\n   bool parse_basic_escape();\n   bool parse_extended_escape();\n   bool parse_match_any();\n   bool parse_repeat(std::size_t low = 0, std::size_t high = (std::numeric_limits<std::size_t>::max)());\n   bool parse_repeat_range(bool isbasic);\n   bool parse_alt();\n   bool parse_set();\n   bool parse_backref();\n   void parse_set_literal(basic_char_set<charT, traits>& char_set);\n   bool parse_inner_set(basic_char_set<charT, traits>& char_set);\n   bool parse_QE();\n   bool parse_perl_extension();\n   bool parse_perl_verb();\n   bool match_verb(const char*);\n   bool add_emacs_code(bool negate);\n   bool unwind_alts(std::ptrdiff_t last_paren_start);\n   digraph<charT> get_next_set_literal(basic_char_set<charT, traits>& char_set);\n   charT unescape_character();\n   regex_constants::syntax_option_type parse_options();\n\nprivate:\n   typedef bool (basic_regex_parser::*parser_proc_type)();\n   typedef typename traits::string_type string_type;\n   typedef typename traits::char_class_type char_class_type;\n   parser_proc_type           m_parser_proc;    // the main parser to use\n   const charT*               m_base;           // the start of the string being parsed\n   const charT*               m_end;            // the end of the string being parsed\n   const charT*               m_position;       // our current parser position\n   unsigned                   m_mark_count;     // how many sub-expressions we have\n   int                        m_mark_reset;     // used to indicate that we're inside a (?|...) block.\n   unsigned                   m_max_mark;       // largest mark count seen inside a (?|...) block.\n   std::ptrdiff_t             m_paren_start;    // where the last seen ')' began (where repeats are inserted).\n   std::ptrdiff_t             m_alt_insert_point; // where to insert the next alternative\n   bool                       m_has_case_change; // true if somewhere in the current block the case has changed\n   unsigned                   m_recursion_count; // How many times we've called parse_all.\n#if defined(BOOST_MSVC) && defined(_M_IX86)\n   // This is an ugly warning suppression workaround (for warnings *inside* std::vector\n   // that can not otherwise be suppressed)...\n   BOOST_STATIC_ASSERT(sizeof(long) >= sizeof(void*));\n   std::vector<long>           m_alt_jumps;      // list of alternative in the current scope.\n#else\n   std::vector<std::ptrdiff_t> m_alt_jumps;      // list of alternative in the current scope.\n#endif\n\n   basic_regex_parser& operator=(const basic_regex_parser&);\n   basic_regex_parser(const basic_regex_parser&);\n};\n\ntemplate <class charT, class traits>\nbasic_regex_parser<charT, traits>::basic_regex_parser(regex_data<charT, traits>* data)\n   : basic_regex_creator<charT, traits>(data), m_parser_proc(), m_base(0), m_end(0), m_position(0), \n   m_mark_count(0), m_mark_reset(-1), m_max_mark(0), m_paren_start(0), m_alt_insert_point(0), m_has_case_change(false), m_recursion_count(0)\n{\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_parser<charT, traits>::parse(const charT* p1, const charT* p2, unsigned l_flags)\n{\n   // pass l_flags on to base class:\n   this->init(l_flags);\n   // set up pointers:\n   m_position = m_base = p1;\n   m_end = p2;\n   // empty strings are errors:\n   if((p1 == p2) && \n      (\n         ((l_flags & regbase::main_option_type) != regbase::perl_syntax_group)\n         || (l_flags & regbase::no_empty_expressions)\n      )\n     )\n   {\n      fail(regex_constants::error_empty, 0);\n      return;\n   }\n   // select which parser to use:\n   switch(l_flags & regbase::main_option_type)\n   {\n   case regbase::perl_syntax_group:\n      {\n         m_parser_proc = &basic_regex_parser<charT, traits>::parse_extended;\n         //\n         // Add a leading paren with index zero to give recursions a target:\n         //\n         re_brace* br = static_cast<re_brace*>(this->append_state(syntax_element_startmark, sizeof(re_brace)));\n         br->index = 0;\n         br->icase = this->flags() & regbase::icase;\n         break;\n      }\n   case regbase::basic_syntax_group:\n      m_parser_proc = &basic_regex_parser<charT, traits>::parse_basic;\n      break;\n   case regbase::literal:\n      m_parser_proc = &basic_regex_parser<charT, traits>::parse_literal;\n      break;\n   default:\n      // Oops, someone has managed to set more than one of the main option flags, \n      // so this must be an error:\n      fail(regex_constants::error_unknown, 0, \"An invalid combination of regular expression syntax flags was used.\");\n      return;\n   }\n\n   // parse all our characters:\n   bool result = parse_all();\n   //\n   // Unwind our alternatives:\n   //\n   unwind_alts(-1);\n   // reset l_flags as a global scope (?imsx) may have altered them:\n   this->flags(l_flags);\n   // if we haven't gobbled up all the characters then we must\n   // have had an unexpected ')' :\n   if(!result)\n   {\n      fail(regex_constants::error_paren, ::boost::BOOST_REGEX_DETAIL_NS::distance(m_base, m_position), \"Found a closing ) with no corresponding opening parenthesis.\");\n      return;\n   }\n   // if an error has been set then give up now:\n   if(this->m_pdata->m_status)\n      return;\n   // fill in our sub-expression count:\n   this->m_pdata->m_mark_count = 1u + (std::size_t)m_mark_count;\n   this->finalize(p1, p2);\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_parser<charT, traits>::fail(regex_constants::error_type error_code, std::ptrdiff_t position)\n{\n   // get the error message:\n   std::string message = this->m_pdata->m_ptraits->error_string(error_code);\n   fail(error_code, position, message);\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_parser<charT, traits>::fail(regex_constants::error_type error_code, std::ptrdiff_t position, std::string message, std::ptrdiff_t start_pos)\n{\n   if(0 == this->m_pdata->m_status) // update the error code if not already set\n      this->m_pdata->m_status = error_code;\n   m_position = m_end; // don't bother parsing anything else\n\n#ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS\n   //\n   // Augment error message with the regular expression text:\n   //\n   if(start_pos == position)\n      start_pos = (std::max)(static_cast<std::ptrdiff_t>(0), position - static_cast<std::ptrdiff_t>(10));\n   std::ptrdiff_t end_pos = (std::min)(position + static_cast<std::ptrdiff_t>(10), static_cast<std::ptrdiff_t>(m_end - m_base));\n   if(error_code != regex_constants::error_empty)\n   {\n      if((start_pos != 0) || (end_pos != (m_end - m_base)))\n         message += \"  The error occurred while parsing the regular expression fragment: '\";\n      else\n         message += \"  The error occurred while parsing the regular expression: '\";\n      if(start_pos != end_pos)\n      {\n         message += std::string(m_base + start_pos, m_base + position);\n         message += \">>>HERE>>>\";\n         message += std::string(m_base + position, m_base + end_pos);\n      }\n      message += \"'.\";\n   }\n#endif\n\n#ifndef BOOST_NO_EXCEPTIONS\n   if(0 == (this->flags() & regex_constants::no_except))\n   {\n      boost::regex_error e(message, error_code, position);\n      e.raise();\n   }\n#else\n   (void)position; // suppress warnings.\n#endif\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_all()\n{\n   if (++m_recursion_count > 400)\n   {\n      // exceeded internal limits\n      fail(boost::regex_constants::error_complexity, m_position - m_base, \"Exceeded nested brace limit.\");\n   }\n   bool result = true;\n   while(result && (m_position != m_end))\n   {\n      result = (this->*m_parser_proc)();\n   }\n   --m_recursion_count;\n   return result;\n}\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4702)\n#endif\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_basic()\n{\n   switch(this->m_traits.syntax_type(*m_position))\n   {\n   case regex_constants::syntax_escape:\n      return parse_basic_escape();\n   case regex_constants::syntax_dot:\n      return parse_match_any();\n   case regex_constants::syntax_caret:\n      ++m_position;\n      this->append_state(syntax_element_start_line);\n      break;\n   case regex_constants::syntax_dollar:\n      ++m_position;\n      this->append_state(syntax_element_end_line);\n      break;\n   case regex_constants::syntax_star:\n      if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line))\n         return parse_literal();\n      else\n      {\n         ++m_position;\n         return parse_repeat();\n      }\n   case regex_constants::syntax_plus:\n      if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line) || !(this->flags() & regbase::emacs_ex))\n         return parse_literal();\n      else\n      {\n         ++m_position;\n         return parse_repeat(1);\n      }\n   case regex_constants::syntax_question:\n      if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line) || !(this->flags() & regbase::emacs_ex))\n         return parse_literal();\n      else\n      {\n         ++m_position;\n         return parse_repeat(0, 1);\n      }\n   case regex_constants::syntax_open_set:\n      return parse_set();\n   case regex_constants::syntax_newline:\n      if(this->flags() & regbase::newline_alt)\n         return parse_alt();\n      else\n         return parse_literal();\n   default:\n      return parse_literal();\n   }\n   return true;\n}\n\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#if BOOST_MSVC >= 1800\n#pragma warning(disable:26812)\n#endif\n#endif\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_extended()\n{\n   bool result = true;\n   switch(this->m_traits.syntax_type(*m_position))\n   {\n   case regex_constants::syntax_open_mark:\n      return parse_open_paren();\n   case regex_constants::syntax_close_mark:\n      return false;\n   case regex_constants::syntax_escape:\n      return parse_extended_escape();\n   case regex_constants::syntax_dot:\n      return parse_match_any();\n   case regex_constants::syntax_caret:\n      ++m_position;\n      this->append_state(\n         (this->flags() & regex_constants::no_mod_m ? syntax_element_buffer_start : syntax_element_start_line));\n      break;\n   case regex_constants::syntax_dollar:\n      ++m_position;\n      this->append_state(\n         (this->flags() & regex_constants::no_mod_m ? syntax_element_buffer_end : syntax_element_end_line));\n      break;\n   case regex_constants::syntax_star:\n      if(m_position == this->m_base)\n      {\n         fail(regex_constants::error_badrepeat, 0, \"The repeat operator \\\"*\\\" cannot start a regular expression.\");\n         return false;\n      }\n      ++m_position;\n      return parse_repeat();\n   case regex_constants::syntax_question:\n      if(m_position == this->m_base)\n      {\n         fail(regex_constants::error_badrepeat, 0, \"The repeat operator \\\"?\\\" cannot start a regular expression.\");\n         return false;\n      }\n      ++m_position;\n      return parse_repeat(0,1);\n   case regex_constants::syntax_plus:\n      if(m_position == this->m_base)\n      {\n         fail(regex_constants::error_badrepeat, 0, \"The repeat operator \\\"+\\\" cannot start a regular expression.\");\n         return false;\n      }\n      ++m_position;\n      return parse_repeat(1);\n   case regex_constants::syntax_open_brace:\n      ++m_position;\n      return parse_repeat_range(false);\n   case regex_constants::syntax_close_brace:\n      if((this->flags() & regbase::no_perl_ex) == regbase::no_perl_ex)\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, \"Found a closing repetition operator } with no corresponding {.\");\n         return false;\n      }\n      result = parse_literal();\n      break;\n   case regex_constants::syntax_or:\n      return parse_alt();\n   case regex_constants::syntax_open_set:\n      return parse_set();\n   case regex_constants::syntax_newline:\n      if(this->flags() & regbase::newline_alt)\n         return parse_alt();\n      else\n         return parse_literal();\n   case regex_constants::syntax_hash:\n      //\n      // If we have a mod_x flag set, then skip until\n      // we get to a newline character:\n      //\n      if((this->flags() \n         & (regbase::no_perl_ex|regbase::mod_x))\n         == regbase::mod_x)\n      {\n         while((m_position != m_end) && !is_separator(*m_position++)){}\n         return true;\n      }\n      BOOST_FALLTHROUGH;\n   default:\n      result = parse_literal();\n      break;\n   }\n   return result;\n}\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_literal()\n{\n   // append this as a literal provided it's not a space character\n   // or the perl option regbase::mod_x is not set:\n   if(\n      ((this->flags() \n         & (regbase::main_option_type|regbase::mod_x|regbase::no_perl_ex)) \n            != regbase::mod_x)\n      || !this->m_traits.isctype(*m_position, this->m_mask_space))\n         this->append_literal(*m_position);\n   ++m_position;\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_open_paren()\n{\n   //\n   // skip the '(' and error check:\n   //\n   if(++m_position == m_end)\n   {\n      fail(regex_constants::error_paren, m_position - m_base);\n      return false;\n   }\n   //\n   // begin by checking for a perl-style (?...) extension:\n   //\n   if(\n         ((this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) == 0)\n         || ((this->flags() & (regbase::main_option_type | regbase::emacs_ex)) == (regbase::basic_syntax_group|regbase::emacs_ex))\n     )\n   {\n      if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_question)\n         return parse_perl_extension();\n      if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_star)\n         return parse_perl_verb();\n   }\n   //\n   // update our mark count, and append the required state:\n   //\n   unsigned markid = 0;\n   if(0 == (this->flags() & regbase::nosubs))\n   {\n      markid = ++m_mark_count;\n#ifndef BOOST_NO_STD_DISTANCE\n      if(this->flags() & regbase::save_subexpression_location)\n         this->m_pdata->m_subs.push_back(std::pair<std::size_t, std::size_t>(std::distance(m_base, m_position) - 1, 0));\n#else\n      if(this->flags() & regbase::save_subexpression_location)\n         this->m_pdata->m_subs.push_back(std::pair<std::size_t, std::size_t>((m_position - m_base) - 1, 0));\n#endif\n   }\n   re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_startmark, sizeof(re_brace)));\n   pb->index = markid;\n   pb->icase = this->flags() & regbase::icase;\n   std::ptrdiff_t last_paren_start = this->getoffset(pb);\n   // back up insertion point for alternations, and set new point:\n   std::ptrdiff_t last_alt_point = m_alt_insert_point;\n   this->m_pdata->m_data.align();\n   m_alt_insert_point = this->m_pdata->m_data.size();\n   //\n   // back up the current flags in case we have a nested (?imsx) group:\n   //\n   regex_constants::syntax_option_type opts = this->flags();\n   bool old_case_change = m_has_case_change;\n   m_has_case_change = false; // no changes to this scope as yet...\n   //\n   // Back up branch reset data in case we have a nested (?|...)\n   //\n   int mark_reset = m_mark_reset;\n   m_mark_reset = -1;\n   //\n   // now recursively add more states, this will terminate when we get to a\n   // matching ')' :\n   //\n   parse_all();\n   //\n   // Unwind pushed alternatives:\n   //\n   if(0 == unwind_alts(last_paren_start))\n      return false;\n   //\n   // restore flags:\n   //\n   if(m_has_case_change)\n   {\n      // the case has changed in one or more of the alternatives\n      // within the scoped (...) block: we have to add a state\n      // to reset the case sensitivity:\n      static_cast<re_case*>(\n         this->append_state(syntax_element_toggle_case, sizeof(re_case))\n         )->icase = opts & regbase::icase;\n   }\n   this->flags(opts);\n   m_has_case_change = old_case_change;\n   //\n   // restore branch reset:\n   //\n   m_mark_reset = mark_reset;\n   //\n   // we either have a ')' or we have run out of characters prematurely:\n   //\n   if(m_position == m_end)\n   {\n      this->fail(regex_constants::error_paren, ::boost::BOOST_REGEX_DETAIL_NS::distance(m_base, m_end));\n      return false;\n   }\n   if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n      return false;\n#ifndef BOOST_NO_STD_DISTANCE\n   if(markid && (this->flags() & regbase::save_subexpression_location))\n      this->m_pdata->m_subs.at(markid - 1).second = std::distance(m_base, m_position);\n#else\n   if(markid && (this->flags() & regbase::save_subexpression_location))\n      this->m_pdata->m_subs.at(markid - 1).second = (m_position - m_base);\n#endif\n   ++m_position;\n   //\n   // append closing parenthesis state:\n   //\n   pb = static_cast<re_brace*>(this->append_state(syntax_element_endmark, sizeof(re_brace)));\n   pb->index = markid;\n   pb->icase = this->flags() & regbase::icase;\n   this->m_paren_start = last_paren_start;\n   //\n   // restore the alternate insertion point:\n   //\n   this->m_alt_insert_point = last_alt_point;\n   //\n   // allow backrefs to this mark:\n   //\n   if(markid > 0)\n      this->m_backrefs.set(markid);\n\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_basic_escape()\n{\n   if(++m_position == m_end)\n   {\n      fail(regex_constants::error_paren, m_position - m_base);\n      return false;\n   }\n   bool result = true;\n   switch(this->m_traits.escape_syntax_type(*m_position))\n   {\n   case regex_constants::syntax_open_mark:\n      return parse_open_paren();\n   case regex_constants::syntax_close_mark:\n      return false;\n   case regex_constants::syntax_plus:\n      if(this->flags() & regex_constants::bk_plus_qm)\n      {\n         ++m_position;\n         return parse_repeat(1);\n      }\n      else\n         return parse_literal();\n   case regex_constants::syntax_question:\n      if(this->flags() & regex_constants::bk_plus_qm)\n      {\n         ++m_position;\n         return parse_repeat(0, 1);\n      }\n      else\n         return parse_literal();\n   case regex_constants::syntax_open_brace:\n      if(this->flags() & regbase::no_intervals)\n         return parse_literal();\n      ++m_position;\n      return parse_repeat_range(true);\n   case regex_constants::syntax_close_brace:\n      if(this->flags() & regbase::no_intervals)\n         return parse_literal();\n      fail(regex_constants::error_brace, this->m_position - this->m_base, \"Found a closing repetition operator } with no corresponding {.\");\n      return false;\n   case regex_constants::syntax_or:\n      if(this->flags() & regbase::bk_vbar)\n         return parse_alt();\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::syntax_digit:\n      return parse_backref();\n   case regex_constants::escape_type_start_buffer:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_buffer_start);\n      }\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::escape_type_end_buffer:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_buffer_end);\n      }\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::escape_type_word_assert:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_word_boundary);\n      }\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::escape_type_not_word_assert:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_within_word);\n      }\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::escape_type_left_word:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_word_start);\n      }\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::escape_type_right_word:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_word_end);\n      }\n      else\n         result = parse_literal();\n      break;\n   default:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         bool negate = true;\n         switch(*m_position)\n         {\n         case 'w':\n            negate = false;\n            BOOST_FALLTHROUGH;\n         case 'W':\n            {\n            basic_char_set<charT, traits> char_set;\n            if(negate)\n               char_set.negate();\n            char_set.add_class(this->m_word_mask);\n            if(0 == this->append_set(char_set))\n            {\n               fail(regex_constants::error_ctype, m_position - m_base);\n               return false;\n            }\n            ++m_position;\n            return true;\n            }\n         case 's':\n            negate = false;\n            BOOST_FALLTHROUGH;\n         case 'S':\n            return add_emacs_code(negate);\n         case 'c':\n         case 'C':\n            // not supported yet:\n            fail(regex_constants::error_escape, m_position - m_base, \"The \\\\c and \\\\C escape sequences are not supported by POSIX basic regular expressions: try the Perl syntax instead.\");\n            return false;\n         default:\n            break;\n         }\n      }\n      result = parse_literal();\n      break;\n   }\n   return result;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_extended_escape()\n{\n   ++m_position;\n   if(m_position == m_end)\n   {\n      fail(regex_constants::error_escape, m_position - m_base, \"Incomplete escape sequence found.\");\n      return false;\n   }\n   bool negate = false; // in case this is a character class escape: \\w \\d etc\n   switch(this->m_traits.escape_syntax_type(*m_position))\n   {\n   case regex_constants::escape_type_not_class:\n      negate = true;\n      BOOST_FALLTHROUGH;\n   case regex_constants::escape_type_class:\n      {\nescape_type_class_jump:\n         typedef typename traits::char_class_type m_type;\n         m_type m = this->m_traits.lookup_classname(m_position, m_position+1);\n         if(m != 0)\n         {\n            basic_char_set<charT, traits> char_set;\n            if(negate)\n               char_set.negate();\n            char_set.add_class(m);\n            if(0 == this->append_set(char_set))\n            {\n               fail(regex_constants::error_ctype, m_position - m_base);\n               return false;\n            }\n            ++m_position;\n            return true;\n         }\n         //\n         // not a class, just a regular unknown escape:\n         //\n         this->append_literal(unescape_character());\n         break;\n      }\n   case regex_constants::syntax_digit:\n      return parse_backref();\n   case regex_constants::escape_type_left_word:\n      ++m_position;\n      this->append_state(syntax_element_word_start);\n      break;\n   case regex_constants::escape_type_right_word:\n      ++m_position;\n      this->append_state(syntax_element_word_end);\n      break;\n   case regex_constants::escape_type_start_buffer:\n      ++m_position;\n      this->append_state(syntax_element_buffer_start);\n      break;\n   case regex_constants::escape_type_end_buffer:\n      ++m_position;\n      this->append_state(syntax_element_buffer_end);\n      break;\n   case regex_constants::escape_type_word_assert:\n      ++m_position;\n      this->append_state(syntax_element_word_boundary);\n      break;\n   case regex_constants::escape_type_not_word_assert:\n      ++m_position;\n      this->append_state(syntax_element_within_word);\n      break;\n   case regex_constants::escape_type_Z:\n      ++m_position;\n      this->append_state(syntax_element_soft_buffer_end);\n      break;\n   case regex_constants::escape_type_Q:\n      return parse_QE();\n   case regex_constants::escape_type_C:\n      return parse_match_any();\n   case regex_constants::escape_type_X:\n      ++m_position;\n      this->append_state(syntax_element_combining);\n      break;\n   case regex_constants::escape_type_G:\n      ++m_position;\n      this->append_state(syntax_element_restart_continue);\n      break;\n   case regex_constants::escape_type_not_property:\n      negate = true;\n      BOOST_FALLTHROUGH;\n   case regex_constants::escape_type_property:\n      {\n         ++m_position;\n         char_class_type m;\n         if(m_position == m_end)\n         {\n            fail(regex_constants::error_escape, m_position - m_base, \"Incomplete property escape found.\");\n            return false;\n         }\n         // maybe have \\p{ddd}\n         if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace)\n         {\n            const charT* base = m_position;\n            // skip forward until we find enclosing brace:\n            while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace))\n               ++m_position;\n            if(m_position == m_end)\n            {\n               fail(regex_constants::error_escape, m_position - m_base, \"Closing } missing from property escape sequence.\");\n               return false;\n            }\n            m = this->m_traits.lookup_classname(++base, m_position++);\n         }\n         else\n         {\n            m = this->m_traits.lookup_classname(m_position, m_position+1);\n            ++m_position;\n         }\n         if(m != 0)\n         {\n            basic_char_set<charT, traits> char_set;\n            if(negate)\n               char_set.negate();\n            char_set.add_class(m);\n            if(0 == this->append_set(char_set))\n            {\n               fail(regex_constants::error_ctype, m_position - m_base);\n               return false;\n            }\n            return true;\n         }\n         fail(regex_constants::error_ctype, m_position - m_base, \"Escape sequence was neither a valid property nor a valid character class name.\");\n         return false;\n      }\n   case regex_constants::escape_type_reset_start_mark:\n      if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex)))\n      {\n         re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_startmark, sizeof(re_brace)));\n         pb->index = -5;\n         pb->icase = this->flags() & regbase::icase;\n         this->m_pdata->m_data.align();\n         ++m_position;\n         return true;\n      }\n      goto escape_type_class_jump;\n   case regex_constants::escape_type_line_ending:\n      if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex)))\n      {\n         const charT* e = get_escape_R_string<charT>();\n         const charT* old_position = m_position;\n         const charT* old_end = m_end;\n         const charT* old_base = m_base;\n         m_position = e;\n         m_base = e;\n         m_end = e + traits::length(e);\n         bool r = parse_all();\n         m_position = ++old_position;\n         m_end = old_end;\n         m_base = old_base;\n         return r;\n      }\n      goto escape_type_class_jump;\n   case regex_constants::escape_type_extended_backref:\n      if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex)))\n      {\n         bool have_brace = false;\n         bool negative = false;\n         static const char incomplete_message[] = \"Incomplete \\\\g escape found.\";\n         if(++m_position == m_end)\n         {\n            fail(regex_constants::error_escape, m_position - m_base, incomplete_message);\n            return false;\n         }\n         // maybe have \\g{ddd}\n         regex_constants::syntax_type syn = this->m_traits.syntax_type(*m_position);\n         regex_constants::syntax_type syn_end = 0;\n         if((syn == regex_constants::syntax_open_brace) \n            || (syn == regex_constants::escape_type_left_word)\n            || (syn == regex_constants::escape_type_end_buffer))\n         {\n            if(++m_position == m_end)\n            {\n               fail(regex_constants::error_escape, m_position - m_base, incomplete_message);\n               return false;\n            }\n            have_brace = true;\n            switch(syn)\n            {\n            case regex_constants::syntax_open_brace:\n               syn_end = regex_constants::syntax_close_brace;\n               break;\n            case regex_constants::escape_type_left_word:\n               syn_end = regex_constants::escape_type_right_word;\n               break;\n            default:\n               syn_end = regex_constants::escape_type_end_buffer;\n               break;\n            }\n         }\n         negative = (*m_position == static_cast<charT>('-'));\n         if((negative) && (++m_position == m_end))\n         {\n            fail(regex_constants::error_escape, m_position - m_base, incomplete_message);\n            return false;\n         }\n         const charT* pc = m_position;\n         boost::intmax_t i = this->m_traits.toi(pc, m_end, 10);\n         if((i < 0) && syn_end)\n         {\n            // Check for a named capture, get the leftmost one if there is more than one:\n            const charT* base = m_position;\n            while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != syn_end))\n            {\n               ++m_position;\n            }\n            i = hash_value_from_capture_name(base, m_position);\n            pc = m_position;\n         }\n         if(negative)\n            i = 1 + (static_cast<boost::intmax_t>(m_mark_count) - i);\n         if(((i < hash_value_mask) && (i > 0) && (this->m_backrefs.test(i))) || ((i >= hash_value_mask) && (this->m_pdata->get_id(i) > 0) && (this->m_backrefs.test(this->m_pdata->get_id(i)))))\n         {\n            m_position = pc;\n            re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_backref, sizeof(re_brace)));\n            pb->index = i;\n            pb->icase = this->flags() & regbase::icase;\n         }\n         else\n         {\n            fail(regex_constants::error_backref, m_position - m_base);\n            return false;\n         }\n         m_position = pc;\n         if(have_brace)\n         {\n            if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != syn_end))\n            {\n               fail(regex_constants::error_escape, m_position - m_base, incomplete_message);\n               return false;\n            }\n            ++m_position;\n         }\n         return true;\n      }\n      goto escape_type_class_jump;\n   case regex_constants::escape_type_control_v:\n      if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex)))\n         goto escape_type_class_jump;\n      BOOST_FALLTHROUGH;\n   default:\n      this->append_literal(unescape_character());\n      break;\n   }\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_match_any()\n{\n   //\n   // we have a '.' that can match any character:\n   //\n   ++m_position;\n   static_cast<re_dot*>(\n      this->append_state(syntax_element_wild, sizeof(re_dot))\n      )->mask = static_cast<unsigned char>(this->flags() & regbase::no_mod_s \n      ? BOOST_REGEX_DETAIL_NS::force_not_newline \n         : this->flags() & regbase::mod_s ?\n            BOOST_REGEX_DETAIL_NS::force_newline : BOOST_REGEX_DETAIL_NS::dont_care);\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_repeat(std::size_t low, std::size_t high)\n{\n   bool greedy = true;\n   bool possessive = false;\n   std::size_t insert_point;\n   // \n   // when we get to here we may have a non-greedy ? mark still to come:\n   //\n   if((m_position != m_end) \n      && (\n            (0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex)))\n            || ((regbase::basic_syntax_group|regbase::emacs_ex) == (this->flags() & (regbase::main_option_type | regbase::emacs_ex)))\n         )\n      )\n   {\n      // OK we have a perl or emacs regex, check for a '?':\n      if ((this->flags() & (regbase::main_option_type | regbase::mod_x | regbase::no_perl_ex)) == regbase::mod_x)\n      {\n         // whitespace skip:\n         while ((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n            ++m_position;\n      }\n      if((m_position != m_end) && (this->m_traits.syntax_type(*m_position) == regex_constants::syntax_question))\n      {\n         greedy = false;\n         ++m_position;\n      }\n      // for perl regexes only check for possessive ++ repeats.\n      if((m_position != m_end)\n         && (0 == (this->flags() & regbase::main_option_type)) \n         && (this->m_traits.syntax_type(*m_position) == regex_constants::syntax_plus))\n      {\n         possessive = true;\n         ++m_position;\n      }\n   }\n   if(0 == this->m_last_state)\n   {\n      fail(regex_constants::error_badrepeat, ::boost::BOOST_REGEX_DETAIL_NS::distance(m_base, m_position), \"Nothing to repeat.\");\n      return false;\n   }\n   if(this->m_last_state->type == syntax_element_endmark)\n   {\n      // insert a repeat before the '(' matching the last ')':\n      insert_point = this->m_paren_start;\n   }\n   else if((this->m_last_state->type == syntax_element_literal) && (static_cast<re_literal*>(this->m_last_state)->length > 1))\n   {\n      // the last state was a literal with more than one character, split it in two:\n      re_literal* lit = static_cast<re_literal*>(this->m_last_state);\n      charT c = (static_cast<charT*>(static_cast<void*>(lit+1)))[lit->length - 1];\n      lit->length -= 1;\n      // now append new state:\n      lit = static_cast<re_literal*>(this->append_state(syntax_element_literal, sizeof(re_literal) + sizeof(charT)));\n      lit->length = 1;\n      (static_cast<charT*>(static_cast<void*>(lit+1)))[0] = c;\n      insert_point = this->getoffset(this->m_last_state);\n   }\n   else\n   {\n      // repeat the last state whatever it was, need to add some error checking here:\n      switch(this->m_last_state->type)\n      {\n      case syntax_element_start_line:\n      case syntax_element_end_line:\n      case syntax_element_word_boundary:\n      case syntax_element_within_word:\n      case syntax_element_word_start:\n      case syntax_element_word_end:\n      case syntax_element_buffer_start:\n      case syntax_element_buffer_end:\n      case syntax_element_alt:\n      case syntax_element_soft_buffer_end:\n      case syntax_element_restart_continue:\n      case syntax_element_jump:\n      case syntax_element_startmark:\n      case syntax_element_backstep:\n      case syntax_element_toggle_case:\n         // can't legally repeat any of the above:\n         fail(regex_constants::error_badrepeat, m_position - m_base);\n         return false;\n      default:\n         // do nothing...\n         break;\n      }\n      insert_point = this->getoffset(this->m_last_state);\n   }\n   //\n   // OK we now know what to repeat, so insert the repeat around it:\n   //\n   re_repeat* rep = static_cast<re_repeat*>(this->insert_state(insert_point, syntax_element_rep, re_repeater_size));\n   rep->min = low;\n   rep->max = high;\n   rep->greedy = greedy;\n   rep->leading = false;\n   // store our repeater position for later:\n   std::ptrdiff_t rep_off = this->getoffset(rep);\n   // and append a back jump to the repeat:\n   re_jump* jmp = static_cast<re_jump*>(this->append_state(syntax_element_jump, sizeof(re_jump)));\n   jmp->alt.i = rep_off - this->getoffset(jmp);\n   this->m_pdata->m_data.align();\n   // now fill in the alt jump for the repeat:\n   rep = static_cast<re_repeat*>(this->getaddress(rep_off));\n   rep->alt.i = this->m_pdata->m_data.size() - rep_off;\n   //\n   // If the repeat is possessive then bracket the repeat with a (?>...)\n   // independent sub-expression construct:\n   //\n   if(possessive)\n   {\n      if(m_position != m_end)\n      {\n         //\n         // Check for illegal following quantifier, we have to do this here, because\n         // the extra states we insert below circumvents our usual error checking :-(\n         //\n         bool contin = false;\n         do\n         {\n            if ((this->flags() & (regbase::main_option_type | regbase::mod_x | regbase::no_perl_ex)) == regbase::mod_x)\n            {\n               // whitespace skip:\n               while ((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n                  ++m_position;\n            }\n            if (m_position != m_end)\n            {\n               switch (this->m_traits.syntax_type(*m_position))\n               {\n               case regex_constants::syntax_star:\n               case regex_constants::syntax_plus:\n               case regex_constants::syntax_question:\n               case regex_constants::syntax_open_brace:\n                  fail(regex_constants::error_badrepeat, m_position - m_base);\n                  return false;\n               case regex_constants::syntax_open_mark:\n                  // Do we have a comment?  If so we need to skip it here...\n                  if ((m_position + 2 < m_end) && this->m_traits.syntax_type(*(m_position + 1)) == regex_constants::syntax_question\n                     && this->m_traits.syntax_type(*(m_position + 2)) == regex_constants::syntax_hash)\n                  {\n                     while ((m_position != m_end)\n                        && (this->m_traits.syntax_type(*m_position++) != regex_constants::syntax_close_mark)) {\n                     }\n                     contin = true;\n                  }\n                  else\n                     contin = false;\n                  break;\n               default:\n                  contin = false;\n               }\n            }\n            else\n               contin = false;\n         } while (contin);\n      }\n      re_brace* pb = static_cast<re_brace*>(this->insert_state(insert_point, syntax_element_startmark, sizeof(re_brace)));\n      pb->index = -3;\n      pb->icase = this->flags() & regbase::icase;\n      jmp = static_cast<re_jump*>(this->insert_state(insert_point + sizeof(re_brace), syntax_element_jump, sizeof(re_jump)));\n      this->m_pdata->m_data.align();\n      jmp->alt.i = this->m_pdata->m_data.size() - this->getoffset(jmp);\n      pb = static_cast<re_brace*>(this->append_state(syntax_element_endmark, sizeof(re_brace)));\n      pb->index = -3;\n      pb->icase = this->flags() & regbase::icase;\n   }\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_repeat_range(bool isbasic)\n{\n   static const char incomplete_message[] = \"Missing } in quantified repetition.\";\n   //\n   // parse a repeat-range:\n   //\n   std::size_t min, max;\n   boost::intmax_t v;\n   // skip whitespace:\n   while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n      ++m_position;\n   if(this->m_position == this->m_end)\n   {\n      if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex))\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n         return false;\n      }\n      // Treat the opening '{' as a literal character, rewind to start of error:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n      return parse_literal();\n   }\n   // get min:\n   v = this->m_traits.toi(m_position, m_end, 10);\n   // skip whitespace:\n   if((v < 0) || (v > umax()))\n   {\n      if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex))\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n         return false;\n      }\n      // Treat the opening '{' as a literal character, rewind to start of error:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n      return parse_literal();\n   }\n   while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n      ++m_position;\n   if(this->m_position == this->m_end)\n   {\n      if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex))\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n         return false;\n      }\n      // Treat the opening '{' as a literal character, rewind to start of error:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n      return parse_literal();\n   }\n   min = static_cast<std::size_t>(v);\n   // see if we have a comma:\n   if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_comma)\n   {\n      // move on and error check:\n      ++m_position;\n      // skip whitespace:\n      while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n         ++m_position;\n      if(this->m_position == this->m_end)\n      {\n         if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex))\n         {\n            fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n            return false;\n         }\n         // Treat the opening '{' as a literal character, rewind to start of error:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n         return parse_literal();\n      }\n      // get the value if any:\n      v = this->m_traits.toi(m_position, m_end, 10);\n      max = ((v >= 0) && (v < umax())) ? (std::size_t)v : (std::numeric_limits<std::size_t>::max)();\n   }\n   else\n   {\n      // no comma, max = min:\n      max = min;\n   }\n   // skip whitespace:\n   while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n      ++m_position;\n   // OK now check trailing }:\n   if(this->m_position == this->m_end)\n   {\n      if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex))\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n         return false;\n      }\n      // Treat the opening '{' as a literal character, rewind to start of error:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n      return parse_literal();\n   }\n   if(isbasic)\n   {\n      if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_escape)\n      {\n         ++m_position;\n         if(this->m_position == this->m_end)\n         {\n            fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n            return false;\n         }\n      }\n      else\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n         return false;\n      }\n   }\n   if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_brace)\n      ++m_position;\n   else\n   {\n      // Treat the opening '{' as a literal character, rewind to start of error:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n      return parse_literal();\n   }\n   //\n   // finally go and add the repeat, unless error:\n   //\n   if(min > max)\n   {\n      // Backtrack to error location:\n      m_position -= 2;\n      while(this->m_traits.isctype(*m_position, this->m_word_mask)) --m_position;\n         ++m_position;\n      fail(regex_constants::error_badbrace, m_position - m_base);\n      return false;\n   }\n   return parse_repeat(min, max);\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_alt()\n{\n   //\n   // error check: if there have been no previous states,\n   // or if the last state was a '(' then error:\n   //\n   if(\n      ((this->m_last_state == 0) || (this->m_last_state->type == syntax_element_startmark))\n      &&\n      !(\n         ((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group)\n           &&\n         ((this->flags() & regbase::no_empty_expressions) == 0)\n        )\n      )\n   {\n      fail(regex_constants::error_empty, this->m_position - this->m_base, \"A regular expression cannot start with the alternation operator |.\");\n      return false;\n   }\n   //\n   // Reset mark count if required:\n   //\n   if(m_max_mark < m_mark_count)\n      m_max_mark = m_mark_count;\n   if(m_mark_reset >= 0)\n      m_mark_count = m_mark_reset;\n\n   ++m_position;\n   //\n   // we need to append a trailing jump: \n   //\n   re_syntax_base* pj = this->append_state(BOOST_REGEX_DETAIL_NS::syntax_element_jump, sizeof(re_jump));\n   std::ptrdiff_t jump_offset = this->getoffset(pj);\n   //\n   // now insert the alternative:\n   //\n   re_alt* palt = static_cast<re_alt*>(this->insert_state(this->m_alt_insert_point, syntax_element_alt, re_alt_size));\n   jump_offset += re_alt_size;\n   this->m_pdata->m_data.align();\n   palt->alt.i = this->m_pdata->m_data.size() - this->getoffset(palt);\n   //\n   // update m_alt_insert_point so that the next alternate gets\n   // inserted at the start of the second of the two we've just created:\n   //\n   this->m_alt_insert_point = this->m_pdata->m_data.size();\n   //\n   // the start of this alternative must have a case changes state\n   // if the current block has messed around with case changes:\n   //\n   if(m_has_case_change)\n   {\n      static_cast<re_case*>(\n         this->append_state(syntax_element_toggle_case, sizeof(re_case))\n         )->icase = this->m_icase;\n   }\n   //\n   // push the alternative onto our stack, a recursive\n   // implementation here is easier to understand (and faster\n   // as it happens), but causes all kinds of stack overflow problems\n   // on programs with small stacks (COM+).\n   //\n   m_alt_jumps.push_back(jump_offset);\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_set()\n{\n   static const char incomplete_message[] = \"Character set declaration starting with [ terminated prematurely - either no ] was found or the set had no content.\";\n   ++m_position;\n   if(m_position == m_end)\n   {\n      fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n      return false;\n   }\n   basic_char_set<charT, traits> char_set;\n\n   const charT* base = m_position;  // where the '[' was\n   const charT* item_base = m_position;  // where the '[' or '^' was\n\n   while(m_position != m_end)\n   {\n      switch(this->m_traits.syntax_type(*m_position))\n      {\n      case regex_constants::syntax_caret:\n         if(m_position == base)\n         {\n            char_set.negate();\n            ++m_position;\n            item_base = m_position;\n         }\n         else\n            parse_set_literal(char_set);\n         break;\n      case regex_constants::syntax_close_set:\n         if(m_position == item_base)\n         {\n            parse_set_literal(char_set);\n            break;\n         }\n         else\n         {\n            ++m_position;\n            if(0 == this->append_set(char_set))\n            {\n               fail(regex_constants::error_ctype, m_position - m_base);\n               return false;\n            }\n         }\n         return true;\n      case regex_constants::syntax_open_set:\n         if(parse_inner_set(char_set))\n            break;\n         return true;\n      case regex_constants::syntax_escape:\n         {\n            // \n            // look ahead and see if this is a character class shortcut\n            // \\d \\w \\s etc...\n            //\n            ++m_position;\n            if(this->m_traits.escape_syntax_type(*m_position)\n               == regex_constants::escape_type_class)\n            {\n               char_class_type m = this->m_traits.lookup_classname(m_position, m_position+1);\n               if(m != 0)\n               {\n                  char_set.add_class(m);\n                  ++m_position;\n                  break;\n               }\n            }\n            else if(this->m_traits.escape_syntax_type(*m_position)\n               == regex_constants::escape_type_not_class)\n            {\n               // negated character class:\n               char_class_type m = this->m_traits.lookup_classname(m_position, m_position+1);\n               if(m != 0)\n               {\n                  char_set.add_negated_class(m);\n                  ++m_position;\n                  break;\n               }\n            }\n            // not a character class, just a regular escape:\n            --m_position;\n            parse_set_literal(char_set);\n            break;\n         }\n      default:\n         parse_set_literal(char_set);\n         break;\n      }\n   }\n   return m_position != m_end;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_inner_set(basic_char_set<charT, traits>& char_set)\n{\n   static const char incomplete_message[] = \"Character class declaration starting with [ terminated prematurely - either no ] was found or the set had no content.\";\n   //\n   // we have either a character class [:name:]\n   // a collating element [.name.]\n   // or an equivalence class [=name=]\n   //\n   if(m_end == ++m_position)\n   {\n      fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n      return false;\n   }\n   switch(this->m_traits.syntax_type(*m_position))\n   {\n   case regex_constants::syntax_dot:\n      //\n      // a collating element is treated as a literal:\n      //\n      --m_position;\n      parse_set_literal(char_set);\n      return true;\n   case regex_constants::syntax_colon:\n      {\n      // check that character classes are actually enabled:\n      if((this->flags() & (regbase::main_option_type | regbase::no_char_classes)) \n         == (regbase::basic_syntax_group  | regbase::no_char_classes))\n      {\n         --m_position;\n         parse_set_literal(char_set);\n         return true;\n      }\n      // skip the ':'\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      const charT* name_first = m_position;\n      // skip at least one character, then find the matching ':]'\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      while((m_position != m_end) \n         && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_colon)) \n         ++m_position;\n      const charT* name_last = m_position;\n      if(m_end == m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      if((m_end == ++m_position) \n         || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set))\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      //\n      // check for negated class:\n      //\n      bool negated = false;\n      if(this->m_traits.syntax_type(*name_first) == regex_constants::syntax_caret)\n      {\n         ++name_first;\n         negated = true;\n      }\n      typedef typename traits::char_class_type m_type;\n      m_type m = this->m_traits.lookup_classname(name_first, name_last);\n      if(m == 0)\n      {\n         if(char_set.empty() && (name_last - name_first == 1))\n         {\n            // maybe a special case:\n            ++m_position;\n            if( (m_position != m_end) \n               && (this->m_traits.syntax_type(*m_position) \n                  == regex_constants::syntax_close_set))\n            {\n               if(this->m_traits.escape_syntax_type(*name_first) \n                  == regex_constants::escape_type_left_word)\n               {\n                  ++m_position;\n                  this->append_state(syntax_element_word_start);\n                  return false;\n               }\n               if(this->m_traits.escape_syntax_type(*name_first) \n                  == regex_constants::escape_type_right_word)\n               {\n                  ++m_position;\n                  this->append_state(syntax_element_word_end);\n                  return false;\n               }\n            }\n         }\n         fail(regex_constants::error_ctype, name_first - m_base);\n         return false;\n      }\n      if(!negated)\n         char_set.add_class(m);\n      else\n         char_set.add_negated_class(m);\n      ++m_position;\n      break;\n   }\n   case regex_constants::syntax_equal:\n      {\n      // skip the '='\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      const charT* name_first = m_position;\n      // skip at least one character, then find the matching '=]'\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      while((m_position != m_end) \n         && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal)) \n         ++m_position;\n      const charT* name_last = m_position;\n      if(m_end == m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      if((m_end == ++m_position) \n         || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set))\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      string_type m = this->m_traits.lookup_collatename(name_first, name_last);\n      if(m.empty() || (m.size() > 2))\n      {\n         fail(regex_constants::error_collate, name_first - m_base);\n         return false;\n      }\n      digraph<charT> d;\n      d.first = m[0];\n      if(m.size() > 1)\n         d.second = m[1];\n      else\n         d.second = 0;\n      char_set.add_equivalent(d);\n      ++m_position;\n      break;\n   }\n   default:\n      --m_position;\n      parse_set_literal(char_set);\n      break;\n   }\n   return true;\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_parser<charT, traits>::parse_set_literal(basic_char_set<charT, traits>& char_set)\n{\n   digraph<charT> start_range(get_next_set_literal(char_set));\n   if(m_end == m_position)\n   {\n      fail(regex_constants::error_brack, m_position - m_base);\n      return;\n   }\n   if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_dash)\n   {\n      // we have a range:\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base);\n         return;\n      }\n      if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set)\n      {\n         digraph<charT> end_range = get_next_set_literal(char_set);\n         char_set.add_range(start_range, end_range);\n         if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_dash)\n         {\n            if(m_end == ++m_position)\n            {\n               fail(regex_constants::error_brack, m_position - m_base);\n               return;\n            }\n            if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_set)\n            {\n               // trailing - :\n               --m_position;\n               return;\n            }\n            fail(regex_constants::error_range, m_position - m_base);\n            return;\n         }\n         return;\n      }\n      --m_position;\n   }\n   char_set.add_single(start_range);\n}\n\ntemplate <class charT, class traits>\ndigraph<charT> basic_regex_parser<charT, traits>::get_next_set_literal(basic_char_set<charT, traits>& char_set)\n{\n   digraph<charT> result;\n   switch(this->m_traits.syntax_type(*m_position))\n   {\n   case regex_constants::syntax_dash:\n      if(!char_set.empty())\n      {\n         // see if we are at the end of the set:\n         if((++m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set))\n         {\n            fail(regex_constants::error_range, m_position - m_base);\n            return result;\n         }\n         --m_position;\n      }\n      result.first = *m_position++;\n      return result;\n   case regex_constants::syntax_escape:\n      // check to see if escapes are supported first:\n      if(this->flags() & regex_constants::no_escape_in_lists)\n      {\n         result = *m_position++;\n         break;\n      }\n      ++m_position;\n      result = unescape_character();\n      break;\n   case regex_constants::syntax_open_set:\n   {\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_collate, m_position - m_base);\n         return result;\n      }\n      if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_dot)\n      {\n         --m_position;\n         result.first = *m_position;\n         ++m_position;\n         return result;\n      }\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_collate, m_position - m_base);\n         return result;\n      }\n      const charT* name_first = m_position;\n      // skip at least one character, then find the matching ':]'\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_collate, name_first - m_base);\n         return result;\n      }\n      while((m_position != m_end) \n         && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_dot)) \n         ++m_position;\n      const charT* name_last = m_position;\n      if(m_end == m_position)\n      {\n         fail(regex_constants::error_collate, name_first - m_base);\n         return result;\n      }\n      if((m_end == ++m_position) \n         || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set))\n      {\n         fail(regex_constants::error_collate, name_first - m_base);\n         return result;\n      }\n      ++m_position;\n      string_type s = this->m_traits.lookup_collatename(name_first, name_last);\n      if(s.empty() || (s.size() > 2))\n      {\n         fail(regex_constants::error_collate, name_first - m_base);\n         return result;\n      }\n      result.first = s[0];\n      if(s.size() > 1)\n         result.second = s[1];\n      else\n         result.second = 0;\n      return result;\n   }\n   default:\n      result = *m_position++;\n   }\n   return result;\n}\n\n//\n// does a value fit in the specified charT type?\n//\ntemplate <class charT>\nbool valid_value(charT, boost::intmax_t v, const mpl::true_&)\n{\n   return (v >> (sizeof(charT) * CHAR_BIT)) == 0;\n}\ntemplate <class charT>\nbool valid_value(charT, boost::intmax_t, const mpl::false_&)\n{\n   return true; // v will alsways fit in a charT\n}\ntemplate <class charT>\nbool valid_value(charT c, boost::intmax_t v)\n{\n   return valid_value(c, v, mpl::bool_<(sizeof(charT) < sizeof(boost::intmax_t))>());\n}\n\ntemplate <class charT, class traits>\ncharT basic_regex_parser<charT, traits>::unescape_character()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   charT result(0);\n   if(m_position == m_end)\n   {\n      fail(regex_constants::error_escape, m_position - m_base, \"Escape sequence terminated prematurely.\");\n      return false;\n   }\n   switch(this->m_traits.escape_syntax_type(*m_position))\n   {\n   case regex_constants::escape_type_control_a:\n      result = charT('\\a');\n      break;\n   case regex_constants::escape_type_e:\n      result = charT(27);\n      break;\n   case regex_constants::escape_type_control_f:\n      result = charT('\\f');\n      break;\n   case regex_constants::escape_type_control_n:\n      result = charT('\\n');\n      break;\n   case regex_constants::escape_type_control_r:\n      result = charT('\\r');\n      break;\n   case regex_constants::escape_type_control_t:\n      result = charT('\\t');\n      break;\n   case regex_constants::escape_type_control_v:\n      result = charT('\\v');\n      break;\n   case regex_constants::escape_type_word_assert:\n      result = charT('\\b');\n      break;\n   case regex_constants::escape_type_ascii_control:\n      ++m_position;\n      if(m_position == m_end)\n      {\n         // Rewind to start of escape:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n         fail(regex_constants::error_escape, m_position - m_base, \"ASCII escape sequence terminated prematurely.\");\n         return result;\n      }\n      result = static_cast<charT>(*m_position % 32);\n      break;\n   case regex_constants::escape_type_hex:\n      ++m_position;\n      if(m_position == m_end)\n      {\n         // Rewind to start of escape:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n         fail(regex_constants::error_escape, m_position - m_base, \"Hexadecimal escape sequence terminated prematurely.\");\n         return result;\n      }\n      // maybe have \\x{ddd}\n      if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace)\n      {\n         ++m_position;\n         if(m_position == m_end)\n         {\n            // Rewind to start of escape:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n            fail(regex_constants::error_escape, m_position - m_base, \"Missing } in hexadecimal escape sequence.\");\n            return result;\n         }\n         boost::intmax_t i = this->m_traits.toi(m_position, m_end, 16);\n         if((m_position == m_end)\n            || (i < 0)\n            || ((std::numeric_limits<charT>::is_specialized) && (i > (boost::intmax_t)(std::numeric_limits<charT>::max)()))\n            || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace))\n         {\n            // Rewind to start of escape:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n            fail(regex_constants::error_badbrace, m_position - m_base, \"Hexadecimal escape sequence was invalid.\");\n            return result;\n         }\n         ++m_position;\n         result = charT(i);\n      }\n      else\n      {\n         std::ptrdiff_t len = (std::min)(static_cast<std::ptrdiff_t>(2), static_cast<std::ptrdiff_t>(m_end - m_position));\n         boost::intmax_t i = this->m_traits.toi(m_position, m_position + len, 16);\n         if((i < 0)\n            || !valid_value(charT(0), i))\n         {\n            // Rewind to start of escape:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n            fail(regex_constants::error_escape, m_position - m_base, \"Escape sequence did not encode a valid character.\");\n            return result;\n         }\n         result = charT(i);\n      }\n      return result;\n   case regex_constants::syntax_digit:\n      {\n      // an octal escape sequence, the first character must be a zero\n      // followed by up to 3 octal digits:\n      std::ptrdiff_t len = (std::min)(::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end), static_cast<std::ptrdiff_t>(4));\n      const charT* bp = m_position;\n      boost::intmax_t val = this->m_traits.toi(bp, bp + 1, 8);\n      if(val != 0)\n      {\n         // Rewind to start of escape:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n         // Oops not an octal escape after all:\n         fail(regex_constants::error_escape, m_position - m_base, \"Invalid octal escape sequence.\");\n         return result;\n      }\n      val = this->m_traits.toi(m_position, m_position + len, 8);\n      if((val < 0) || (val > (boost::intmax_t)(std::numeric_limits<charT>::max)()))\n      {\n         // Rewind to start of escape:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n         fail(regex_constants::error_escape, m_position - m_base, \"Octal escape sequence is invalid.\");\n         return result;\n      }\n      return static_cast<charT>(val);\n      }\n   case regex_constants::escape_type_named_char:\n      {\n         ++m_position;\n         if(m_position == m_end)\n         {\n            // Rewind to start of escape:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n            fail(regex_constants::error_escape, m_position - m_base);\n            return false;\n         }\n         // maybe have \\N{name}\n         if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace)\n         {\n            const charT* base = m_position;\n            // skip forward until we find enclosing brace:\n            while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace))\n               ++m_position;\n            if(m_position == m_end)\n            {\n               // Rewind to start of escape:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n               fail(regex_constants::error_escape, m_position - m_base);\n               return false;\n            }\n            string_type s = this->m_traits.lookup_collatename(++base, m_position++);\n            if(s.empty())\n            {\n               // Rewind to start of escape:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n               fail(regex_constants::error_collate, m_position - m_base);\n               return false;\n            }\n            if(s.size() == 1)\n            {\n               return s[0];\n            }\n         }\n         // fall through is a failure:\n         // Rewind to start of escape:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n         fail(regex_constants::error_escape, m_position - m_base);\n         return false;\n      }\n   default:\n      result = *m_position;\n      break;\n   }\n   ++m_position;\n   return result;\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_backref()\n{\n   BOOST_REGEX_ASSERT(m_position != m_end);\n   const charT* pc = m_position;\n   boost::intmax_t i = this->m_traits.toi(pc, pc + 1, 10);\n   if((i == 0) || (((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group) && (this->flags() & regbase::no_bk_refs)))\n   {\n      // not a backref at all but an octal escape sequence:\n      charT c = unescape_character();\n      this->append_literal(c);\n   }\n   else if((i > 0) && (this->m_backrefs.test(i)))\n   {\n      m_position = pc;\n      re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_backref, sizeof(re_brace)));\n      pb->index = i;\n      pb->icase = this->flags() & regbase::icase;\n   }\n   else\n   {\n      // Rewind to start of escape:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n      fail(regex_constants::error_backref, m_position - m_base);\n      return false;\n   }\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_QE()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   //\n   // parse a \\Q...\\E sequence:\n   //\n   ++m_position; // skip the Q\n   const charT* start = m_position;\n   const charT* end;\n   do\n   {\n      while((m_position != m_end) \n         && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape))\n         ++m_position;\n      if(m_position == m_end)\n      {\n         //  a \\Q...\\E sequence may terminate with the end of the expression:\n         end = m_position;\n         break;  \n      }\n      if(++m_position == m_end) // skip the escape\n      {\n         fail(regex_constants::error_escape, m_position - m_base, \"Unterminated \\\\Q...\\\\E sequence.\");\n         return false;\n      }\n      // check to see if it's a \\E:\n      if(this->m_traits.escape_syntax_type(*m_position) == regex_constants::escape_type_E)\n      {\n         ++m_position;\n         end = m_position - 2;\n         break;\n      }\n      // otherwise go round again:\n   }while(true);\n   //\n   // now add all the character between the two escapes as literals:\n   //\n   while(start != end)\n   {\n      this->append_literal(*start);\n      ++start;\n   }\n   return true;\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_perl_extension()\n{\n   if(++m_position == m_end)\n   {\n      // Rewind to start of (? sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n      fail(regex_constants::error_perl_extension, m_position - m_base);\n      return false;\n   }\n   //\n   // treat comments as a special case, as these\n   // are the only ones that don't start with a leading\n   // startmark state:\n   //\n   if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_hash)\n   {\n      while((m_position != m_end) \n         && (this->m_traits.syntax_type(*m_position++) != regex_constants::syntax_close_mark))\n      {}\n      return true;\n   }\n   //\n   // backup some state, and prepare the way:\n   //\n   int markid = 0;\n   std::ptrdiff_t jump_offset = 0;\n   re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_startmark, sizeof(re_brace)));\n   pb->icase = this->flags() & regbase::icase;\n   std::ptrdiff_t last_paren_start = this->getoffset(pb);\n   // back up insertion point for alternations, and set new point:\n   std::ptrdiff_t last_alt_point = m_alt_insert_point;\n   this->m_pdata->m_data.align();\n   m_alt_insert_point = this->m_pdata->m_data.size();\n   std::ptrdiff_t expected_alt_point = m_alt_insert_point;\n   bool restore_flags = true;\n   regex_constants::syntax_option_type old_flags = this->flags();\n   bool old_case_change = m_has_case_change;\n   m_has_case_change = false;\n   charT name_delim;\n   int mark_reset = m_mark_reset;\n   int max_mark = m_max_mark;\n   m_mark_reset = -1;\n   m_max_mark = m_mark_count;\n   boost::intmax_t v;\n   //\n   // select the actual extension used:\n   //\n   switch(this->m_traits.syntax_type(*m_position))\n   {\n   case regex_constants::syntax_or:\n      m_mark_reset = m_mark_count;\n      BOOST_FALLTHROUGH;\n   case regex_constants::syntax_colon:\n      //\n      // a non-capturing mark:\n      //\n      pb->index = markid = 0;\n      ++m_position;\n      break;\n   case regex_constants::syntax_digit:\n      {\n      //\n      // a recursive subexpression:\n      //\n      v = this->m_traits.toi(m_position, m_end, 10);\n      if((v < 0) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base, \"The recursive sub-expression refers to an invalid marking group, or is unterminated.\");\n         return false;\n      }\ninsert_recursion:\n      pb->index = markid = 0;\n      re_recurse* pr = static_cast<re_recurse*>(this->append_state(syntax_element_recurse, sizeof(re_recurse)));\n      pr->alt.i = v;\n      pr->state_id = 0;\n      static_cast<re_case*>(\n            this->append_state(syntax_element_toggle_case, sizeof(re_case))\n            )->icase = this->flags() & regbase::icase;\n      break;\n      }\n   case regex_constants::syntax_plus:\n      //\n      // A forward-relative recursive subexpression:\n      //\n      ++m_position;\n      v = this->m_traits.toi(m_position, m_end, 10);\n      if((v <= 0) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base, \"An invalid or unterminated recursive sub-expression.\");\n         return false;\n      }\n      if ((std::numeric_limits<boost::intmax_t>::max)() - m_mark_count < v)\n      {\n         fail(regex_constants::error_perl_extension, m_position - m_base, \"An invalid or unterminated recursive sub-expression.\");\n         return false;\n      }\n      v += m_mark_count;\n      goto insert_recursion;\n   case regex_constants::syntax_dash:\n      //\n      // Possibly a backward-relative recursive subexpression:\n      //\n      ++m_position;\n      v = this->m_traits.toi(m_position, m_end, 10);\n      if(v <= 0)\n      {\n         --m_position;\n         // Oops not a relative recursion at all, but a (?-imsx) group:\n         goto option_group_jump;\n      }\n      v = static_cast<boost::intmax_t>(m_mark_count) + 1 - v;\n      if(v <= 0)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base, \"An invalid or unterminated recursive sub-expression.\");\n         return false;\n      }\n      goto insert_recursion;\n   case regex_constants::syntax_equal:\n      pb->index = markid = -1;\n      ++m_position;\n      jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump)));\n      this->m_pdata->m_data.align();\n      m_alt_insert_point = this->m_pdata->m_data.size();\n      break;\n   case regex_constants::syntax_not:\n      pb->index = markid = -2;\n      ++m_position;\n      jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump)));\n      this->m_pdata->m_data.align();\n      m_alt_insert_point = this->m_pdata->m_data.size();\n      break;\n   case regex_constants::escape_type_left_word:\n      {\n         // a lookbehind assertion:\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         regex_constants::syntax_type t = this->m_traits.syntax_type(*m_position);\n         if(t == regex_constants::syntax_not)\n            pb->index = markid = -2;\n         else if(t == regex_constants::syntax_equal)\n            pb->index = markid = -1;\n         else\n         {\n            // Probably a named capture which also starts (?< :\n            name_delim = '>';\n            --m_position;\n            goto named_capture_jump;\n         }\n         ++m_position;\n         jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump)));\n         this->append_state(syntax_element_backstep, sizeof(re_brace));\n         this->m_pdata->m_data.align();\n         m_alt_insert_point = this->m_pdata->m_data.size();\n         break;\n      }\n   case regex_constants::escape_type_right_word:\n      //\n      // an independent sub-expression:\n      //\n      pb->index = markid = -3;\n      ++m_position;\n      jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump)));\n      this->m_pdata->m_data.align();\n      m_alt_insert_point = this->m_pdata->m_data.size();\n      break;\n   case regex_constants::syntax_open_mark:\n      {\n      // a conditional expression:\n      pb->index = markid = -4;\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      v = this->m_traits.toi(m_position, m_end, 10);\n      if(m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(*m_position == charT('R'))\n      {\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(*m_position == charT('&'))\n         {\n            const charT* base = ++m_position;\n            while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n               ++m_position;\n            if(m_position == m_end)\n            {\n               // Rewind to start of (? sequence:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n               fail(regex_constants::error_perl_extension, m_position - m_base);\n               return false;\n            }\n            v = -static_cast<int>(hash_value_from_capture_name(base, m_position));\n         }\n         else\n         {\n            v = -this->m_traits.toi(m_position, m_end, 10);\n         }\n         re_brace* br = static_cast<re_brace*>(this->append_state(syntax_element_assert_backref, sizeof(re_brace)));\n         br->index = v < 0 ? (v - 1) : 0;\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n      }\n      else if((*m_position == charT('\\'')) || (*m_position == charT('<')))\n      {\n         const charT* base = ++m_position;\n         while((m_position != m_end) && (*m_position != charT('>')) && (*m_position != charT('\\'')))\n            ++m_position;\n         if(m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         v = static_cast<int>(hash_value_from_capture_name(base, m_position));\n         re_brace* br = static_cast<re_brace*>(this->append_state(syntax_element_assert_backref, sizeof(re_brace)));\n         br->index = v;\n         if(((*m_position != charT('>')) && (*m_position != charT('\\''))) || (++m_position == m_end))\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base, \"Unterminated named capture.\");\n            return false;\n         }\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n      }\n      else if(*m_position == charT('D'))\n      {\n         const char* def = \"DEFINE\";\n         while(*def && (m_position != m_end) && (*m_position == charT(*def)))\n            ++m_position, ++def;\n         if((m_position == m_end) || *def)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         re_brace* br = static_cast<re_brace*>(this->append_state(syntax_element_assert_backref, sizeof(re_brace)));\n         br->index = 9999; // special magic value!\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n      }\n      else if(v > 0)\n      {\n         re_brace* br = static_cast<re_brace*>(this->append_state(syntax_element_assert_backref, sizeof(re_brace)));\n         br->index = v;\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n      }\n      else\n      {\n         // verify that we have a lookahead or lookbehind assert:\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_question)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(this->m_traits.syntax_type(*m_position) == regex_constants::escape_type_left_word)\n         {\n            if(++m_position == m_end)\n            {\n               // Rewind to start of (? sequence:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n               fail(regex_constants::error_perl_extension, m_position - m_base);\n               return false;\n            }\n            if((this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal)\n               && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_not))\n            {\n               // Rewind to start of (? sequence:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n               fail(regex_constants::error_perl_extension, m_position - m_base);\n               return false;\n            }\n            m_position -= 3;\n         }\n         else\n         {\n            if((this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal)\n               && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_not))\n            {\n               // Rewind to start of (? sequence:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n               fail(regex_constants::error_perl_extension, m_position - m_base);\n               return false;\n            }\n            m_position -= 2;\n         }\n      }\n      break;\n      }\n   case regex_constants::syntax_close_mark:\n      // Rewind to start of (? sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n      fail(regex_constants::error_perl_extension, m_position - m_base);\n      return false;\n   case regex_constants::escape_type_end_buffer:\n      {\n      name_delim = *m_position;\nnamed_capture_jump:\n      markid = 0;\n      if(0 == (this->flags() & regbase::nosubs))\n      {\n         markid = ++m_mark_count;\n   #ifndef BOOST_NO_STD_DISTANCE\n         if(this->flags() & regbase::save_subexpression_location)\n            this->m_pdata->m_subs.push_back(std::pair<std::size_t, std::size_t>(std::distance(m_base, m_position) - 2, 0));\n   #else\n         if(this->flags() & regbase::save_subexpression_location)\n            this->m_pdata->m_subs.push_back(std::pair<std::size_t, std::size_t>((m_position - m_base) - 2, 0));\n   #endif\n      }\n      pb->index = markid;\n      const charT* base = ++m_position;\n      if(m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      while((m_position != m_end) && (*m_position != name_delim))\n         ++m_position;\n      if(m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      this->m_pdata->set_name(base, m_position, markid);\n      ++m_position;\n      break;\n      }\n   default:\n      if(*m_position == charT('R'))\n      {\n         ++m_position;\n         v = 0;\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         goto insert_recursion;\n      }\n      if(*m_position == charT('&'))\n      {\n         ++m_position;\n         const charT* base = m_position;\n         while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n            ++m_position;\n         if(m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         v = static_cast<int>(hash_value_from_capture_name(base, m_position));\n         goto insert_recursion;\n      }\n      if(*m_position == charT('P'))\n      {\n         ++m_position;\n         if(m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(*m_position == charT('>'))\n         {\n            ++m_position;\n            const charT* base = m_position;\n            while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n               ++m_position;\n            if(m_position == m_end)\n            {\n               // Rewind to start of (? sequence:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n               fail(regex_constants::error_perl_extension, m_position - m_base);\n               return false;\n            }\n            v = static_cast<int>(hash_value_from_capture_name(base, m_position));\n            goto insert_recursion;\n         }\n      }\n      //\n      // lets assume that we have a (?imsx) group and try and parse it:\n      //\noption_group_jump:\n      regex_constants::syntax_option_type opts = parse_options();\n      if(m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      // make a note of whether we have a case change:\n      m_has_case_change = ((opts & regbase::icase) != (this->flags() & regbase::icase));\n      pb->index = markid = 0;\n      if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark)\n      {\n         // update flags and carry on as normal:\n         this->flags(opts);\n         restore_flags = false;\n         old_case_change |= m_has_case_change; // defer end of scope by one ')'\n      }\n      else if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_colon)\n      {\n         // update flags and carry on until the matching ')' is found:\n         this->flags(opts);\n         ++m_position;\n      }\n      else\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n\n      // finally append a case change state if we need it:\n      if(m_has_case_change)\n      {\n         static_cast<re_case*>(\n            this->append_state(syntax_element_toggle_case, sizeof(re_case))\n            )->icase = opts & regbase::icase;\n      }\n\n   }\n   //\n   // now recursively add more states, this will terminate when we get to a\n   // matching ')' :\n   //\n   parse_all();\n   //\n   // Unwind alternatives:\n   //\n   if(0 == unwind_alts(last_paren_start))\n   {\n      // Rewind to start of (? sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n      fail(regex_constants::error_perl_extension, m_position - m_base, \"Invalid alternation operators within (?...) block.\");\n      return false;\n   }\n   //\n   // we either have a ')' or we have run out of characters prematurely:\n   //\n   if(m_position == m_end)\n   {\n      // Rewind to start of (? sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n      this->fail(regex_constants::error_paren, ::boost::BOOST_REGEX_DETAIL_NS::distance(m_base, m_end));\n      return false;\n   }\n   BOOST_REGEX_ASSERT(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark);\n   ++m_position;\n   //\n   // restore the flags:\n   //\n   if(restore_flags)\n   {\n      // append a case change state if we need it:\n      if(m_has_case_change)\n      {\n         static_cast<re_case*>(\n            this->append_state(syntax_element_toggle_case, sizeof(re_case))\n            )->icase = old_flags & regbase::icase;\n      }\n      this->flags(old_flags);\n   }\n   //\n   // set up the jump pointer if we have one:\n   //\n   if(jump_offset)\n   {\n      this->m_pdata->m_data.align();\n      re_jump* jmp = static_cast<re_jump*>(this->getaddress(jump_offset));\n      jmp->alt.i = this->m_pdata->m_data.size() - this->getoffset(jmp);\n      if((this->m_last_state == jmp) && (markid != -2))\n      {\n         // Oops... we didn't have anything inside the assertion.\n         // Note we don't get here for negated forward lookahead as (?!)\n         // does have some uses.\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base, \"Invalid or empty zero width assertion.\");\n         return false;\n      }\n   }\n   //\n   // verify that if this is conditional expression, that we do have\n   // an alternative, if not add one:\n   //\n   if(markid == -4)\n   {\n      re_syntax_base* b = this->getaddress(expected_alt_point);\n      // Make sure we have exactly one alternative following this state:\n      if(b->type != syntax_element_alt)\n      {\n         re_alt* alt = static_cast<re_alt*>(this->insert_state(expected_alt_point, syntax_element_alt, sizeof(re_alt)));\n         alt->alt.i = this->m_pdata->m_data.size() - this->getoffset(alt);\n      }\n      else if(((std::ptrdiff_t)this->m_pdata->m_data.size() > (static_cast<re_alt*>(b)->alt.i + this->getoffset(b))) && (static_cast<re_alt*>(b)->alt.i > 0) && this->getaddress(static_cast<re_alt*>(b)->alt.i, b)->type == syntax_element_alt)\n      {\n         // Can't have seen more than one alternative:\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_bad_pattern, m_position - m_base, \"More than one alternation operator | was encountered inside a conditional expression.\");\n         return false;\n      }\n      else\n      {\n         // We must *not* have seen an alternative inside a (DEFINE) block:\n         b = this->getaddress(b->next.i, b);\n         if((b->type == syntax_element_assert_backref) && (static_cast<re_brace*>(b)->index == 9999))\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_bad_pattern, m_position - m_base, \"Alternation operators are not allowed inside a DEFINE block.\");\n            return false;\n         }\n      }\n      // check for invalid repetition of next state:\n      b = this->getaddress(expected_alt_point);\n      b = this->getaddress(static_cast<re_alt*>(b)->next.i, b);\n      if((b->type != syntax_element_assert_backref)\n         && (b->type != syntax_element_startmark))\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_badrepeat, m_position - m_base, \"A repetition operator cannot be applied to a zero-width assertion.\");\n         return false;\n      }\n   }\n   //\n   // append closing parenthesis state:\n   //\n   pb = static_cast<re_brace*>(this->append_state(syntax_element_endmark, sizeof(re_brace)));\n   pb->index = markid;\n   pb->icase = this->flags() & regbase::icase;\n   this->m_paren_start = last_paren_start;\n   //\n   // restore the alternate insertion point:\n   //\n   this->m_alt_insert_point = last_alt_point;\n   //\n   // and the case change data:\n   //\n   m_has_case_change = old_case_change;\n   //\n   // And the mark_reset data:\n   //\n   if(m_max_mark > m_mark_count)\n   {\n      m_mark_count = m_max_mark;\n   }\n   m_mark_reset = mark_reset;\n   m_max_mark = max_mark;\n\n\n   if(markid > 0)\n   {\n#ifndef BOOST_NO_STD_DISTANCE\n      if(this->flags() & regbase::save_subexpression_location)\n         this->m_pdata->m_subs.at((std::size_t)markid - 1).second = std::distance(m_base, m_position) - 1;\n#else\n      if(this->flags() & regbase::save_subexpression_location)\n         this->m_pdata->m_subs.at(markid - 1).second = (m_position - m_base) - 1;\n#endif\n      //\n      // allow backrefs to this mark:\n      //\n      this->m_backrefs.set(markid);\n   }\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::match_verb(const char* verb)\n{\n   while(*verb)\n   {\n      if(static_cast<charT>(*verb) != *m_position)\n      {\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(++m_position == m_end)\n      {\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      ++verb;\n   }\n   return true;\n}\n\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#if BOOST_MSVC >= 1800\n#pragma warning(disable:26812)\n#endif\n#endif\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_perl_verb()\n{\n   if(++m_position == m_end)\n   {\n      // Rewind to start of (* sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n      fail(regex_constants::error_perl_extension, m_position - m_base);\n      return false;\n   }\n   switch(*m_position)\n   {\n   case 'F':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if((this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark) || match_verb(\"AIL\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         this->append_state(syntax_element_fail);\n         return true;\n      }\n      break;\n   case 'A':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(match_verb(\"CCEPT\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         this->append_state(syntax_element_accept);\n         return true;\n      }\n      break;\n   case 'C':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(match_verb(\"OMMIT\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         static_cast<re_commit*>(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_commit;\n         this->m_pdata->m_disable_match_any = true;\n         return true;\n      }\n      break;\n   case 'P':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(match_verb(\"RUNE\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         static_cast<re_commit*>(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_prune;\n         this->m_pdata->m_disable_match_any = true;\n         return true;\n      }\n      break;\n   case 'S':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(match_verb(\"KIP\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         static_cast<re_commit*>(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_skip;\n         this->m_pdata->m_disable_match_any = true;\n         return true;\n      }\n      break;\n   case 'T':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(match_verb(\"HEN\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         this->append_state(syntax_element_then);\n         this->m_pdata->m_disable_match_any = true;\n         return true;\n      }\n      break;\n   }\n   // Rewind to start of (* sequence:\n   --m_position;\n   while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n   fail(regex_constants::error_perl_extension, m_position - m_base);\n   return false;\n}\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::add_emacs_code(bool negate)\n{\n   //\n   // parses an emacs style \\sx or \\Sx construct.\n   //\n   if(++m_position == m_end)\n   {\n      // Rewind to start of sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n      fail(regex_constants::error_escape, m_position - m_base);\n      return false;\n   }\n   basic_char_set<charT, traits> char_set;\n   if(negate)\n      char_set.negate();\n\n   static const charT s_punct[5] = { 'p', 'u', 'n', 'c', 't', };\n\n   switch(*m_position)\n   {\n   case 's':\n   case ' ':\n      char_set.add_class(this->m_mask_space);\n      break;\n   case 'w':\n      char_set.add_class(this->m_word_mask);\n      break;\n   case '_':\n      char_set.add_single(digraph<charT>(charT('$'))); \n      char_set.add_single(digraph<charT>(charT('&'))); \n      char_set.add_single(digraph<charT>(charT('*'))); \n      char_set.add_single(digraph<charT>(charT('+'))); \n      char_set.add_single(digraph<charT>(charT('-'))); \n      char_set.add_single(digraph<charT>(charT('_'))); \n      char_set.add_single(digraph<charT>(charT('<'))); \n      char_set.add_single(digraph<charT>(charT('>'))); \n      break;\n   case '.':\n      char_set.add_class(this->m_traits.lookup_classname(s_punct, s_punct+5));\n      break;\n   case '(':\n      char_set.add_single(digraph<charT>(charT('('))); \n      char_set.add_single(digraph<charT>(charT('['))); \n      char_set.add_single(digraph<charT>(charT('{'))); \n      break;\n   case ')':\n      char_set.add_single(digraph<charT>(charT(')'))); \n      char_set.add_single(digraph<charT>(charT(']'))); \n      char_set.add_single(digraph<charT>(charT('}'))); \n      break;\n   case '\"':\n      char_set.add_single(digraph<charT>(charT('\"'))); \n      char_set.add_single(digraph<charT>(charT('\\''))); \n      char_set.add_single(digraph<charT>(charT('`'))); \n      break;\n   case '\\'':\n      char_set.add_single(digraph<charT>(charT('\\''))); \n      char_set.add_single(digraph<charT>(charT(','))); \n      char_set.add_single(digraph<charT>(charT('#'))); \n      break;\n   case '<':\n      char_set.add_single(digraph<charT>(charT(';'))); \n      break;\n   case '>':\n      char_set.add_single(digraph<charT>(charT('\\n'))); \n      char_set.add_single(digraph<charT>(charT('\\f'))); \n      break;\n   default:\n      fail(regex_constants::error_ctype, m_position - m_base);\n      return false;\n   }\n   if(0 == this->append_set(char_set))\n   {\n      fail(regex_constants::error_ctype, m_position - m_base);\n      return false;\n   }\n   ++m_position;\n   return true;\n}\n\ntemplate <class charT, class traits>\nregex_constants::syntax_option_type basic_regex_parser<charT, traits>::parse_options()\n{\n   // we have a (?imsx-imsx) group, convert it into a set of flags:\n   regex_constants::syntax_option_type f = this->flags();\n   bool breakout = false;\n   do\n   {\n      switch(*m_position)\n      {\n      case 's':\n         f |= regex_constants::mod_s;\n         f &= ~regex_constants::no_mod_s;\n         break;\n      case 'm':\n         f &= ~regex_constants::no_mod_m;\n         break;\n      case 'i':\n         f |= regex_constants::icase;\n         break;\n      case 'x':\n         f |= regex_constants::mod_x;\n         break;\n      default:\n         breakout = true;\n         continue;\n      }\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_paren, m_position - m_base);\n         return false;\n      }\n   }\n   while(!breakout);\n   \n   breakout = false;\n\n   if(*m_position == static_cast<charT>('-'))\n   {\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_paren, m_position - m_base);\n         return false;\n      }\n      do\n      {\n         switch(*m_position)\n         {\n         case 's':\n            f &= ~regex_constants::mod_s;\n            f |= regex_constants::no_mod_s;\n            break;\n         case 'm':\n            f |= regex_constants::no_mod_m;\n            break;\n         case 'i':\n            f &= ~regex_constants::icase;\n            break;\n         case 'x':\n            f &= ~regex_constants::mod_x;\n            break;\n         default:\n            breakout = true;\n            continue;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_paren, m_position - m_base);\n            return false;\n         }\n      }\n      while(!breakout);\n   }\n   return f;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::unwind_alts(std::ptrdiff_t last_paren_start)\n{\n   //\n   // If we didn't actually add any states after the last \n   // alternative then that's an error:\n   //\n   if((this->m_alt_insert_point == static_cast<std::ptrdiff_t>(this->m_pdata->m_data.size()))\n      && (!m_alt_jumps.empty()) && (m_alt_jumps.back() > last_paren_start)\n      &&\n      !(\n         ((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group)\n           &&\n         ((this->flags() & regbase::no_empty_expressions) == 0)\n        )\n      )\n   {\n      fail(regex_constants::error_empty, this->m_position - this->m_base, \"Can't terminate a sub-expression with an alternation operator |.\");\n      return false;\n   }\n   // \n   // Fix up our alternatives:\n   //\n   while((!m_alt_jumps.empty()) && (m_alt_jumps.back() > last_paren_start))\n   {\n      //\n      // fix up the jump to point to the end of the states\n      // that we've just added:\n      //\n      std::ptrdiff_t jump_offset = m_alt_jumps.back();\n      m_alt_jumps.pop_back();\n      this->m_pdata->m_data.align();\n      re_jump* jmp = static_cast<re_jump*>(this->getaddress(jump_offset));\n      if (jmp->type != syntax_element_jump)\n      {\n         // Something really bad happened, this used to be an assert, \n         // but we'll make it an error just in case we should ever get here.\n         fail(regex_constants::error_unknown, this->m_position - this->m_base, \"Internal logic failed while compiling the expression, probably you added a repeat to something non-repeatable!\");\n         return false;\n      }\n      jmp->alt.i = this->m_pdata->m_data.size() - jump_offset;\n   }\n   return true;\n}\n\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/c_regex_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         c_regex_traits.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression traits class that wraps the global C locale.\n  */\n\n#ifndef BOOST_C_REGEX_TRAITS_HPP_INCLUDED\n#define BOOST_C_REGEX_TRAITS_HPP_INCLUDED\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n#ifndef BOOST_REGEX_WORKAROUND_HPP\n#include <boost/regex/v4/regex_workaround.hpp>\n#endif\n\n#include <cctype>\n\n#ifdef BOOST_NO_STDC_NAMESPACE\nnamespace std{\n   using ::strlen; using ::tolower;\n}\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103 4244)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\n\n   namespace BOOST_REGEX_DETAIL_NS {\n\n      enum\n      {\n         char_class_space = 1 << 0,\n         char_class_print = 1 << 1,\n         char_class_cntrl = 1 << 2,\n         char_class_upper = 1 << 3,\n         char_class_lower = 1 << 4,\n         char_class_alpha = 1 << 5,\n         char_class_digit = 1 << 6,\n         char_class_punct = 1 << 7,\n         char_class_xdigit = 1 << 8,\n         char_class_alnum = char_class_alpha | char_class_digit,\n         char_class_graph = char_class_alnum | char_class_punct,\n         char_class_blank = 1 << 9,\n         char_class_word = 1 << 10,\n         char_class_unicode = 1 << 11,\n         char_class_horizontal = 1 << 12,\n         char_class_vertical = 1 << 13\n      };\n\n   }\n\ntemplate <class charT>\nstruct c_regex_traits;\n\ntemplate<>\nstruct c_regex_traits<char>\n{\n   c_regex_traits(){}\n   typedef char char_type;\n   typedef std::size_t size_type;\n   typedef std::string string_type;\n   struct locale_type{};\n   typedef boost::uint32_t char_class_type;\n\n   static size_type length(const char_type* p) \n   { \n      return (std::strlen)(p); \n   }\n\n   char translate(char c) const \n   { \n      return c; \n   }\n   char translate_nocase(char c) const \n   { \n      return static_cast<char>((std::tolower)(static_cast<unsigned char>(c))); \n   }\n\n   static string_type BOOST_REGEX_CALL transform(const char* p1, const char* p2);\n   static string_type BOOST_REGEX_CALL transform_primary(const char* p1, const char* p2);\n\n   static char_class_type BOOST_REGEX_CALL lookup_classname(const char* p1, const char* p2);\n   static string_type BOOST_REGEX_CALL lookup_collatename(const char* p1, const char* p2);\n\n   static bool BOOST_REGEX_CALL isctype(char, char_class_type);\n   static int BOOST_REGEX_CALL value(char, int);\n\n   locale_type imbue(locale_type l)\n   { return l; }\n   locale_type getloc()const\n   { return locale_type(); }\n\nprivate:\n   // this type is not copyable:\n   c_regex_traits(const c_regex_traits&);\n   c_regex_traits& operator=(const c_regex_traits&);\n};\n\n#ifndef BOOST_NO_WREGEX\ntemplate<>\nstruct c_regex_traits<wchar_t>\n{\n   c_regex_traits(){}\n   typedef wchar_t char_type;\n   typedef std::size_t size_type;\n   typedef std::wstring string_type;\n   struct locale_type{};\n   typedef boost::uint32_t char_class_type;\n\n   static size_type length(const char_type* p) \n   { \n      return (std::wcslen)(p); \n   }\n\n   wchar_t translate(wchar_t c) const \n   { \n      return c; \n   }\n   wchar_t translate_nocase(wchar_t c) const \n   { \n      return (std::towlower)(c); \n   }\n\n   static string_type BOOST_REGEX_CALL transform(const wchar_t* p1, const wchar_t* p2);\n   static string_type BOOST_REGEX_CALL transform_primary(const wchar_t* p1, const wchar_t* p2);\n\n   static char_class_type BOOST_REGEX_CALL lookup_classname(const wchar_t* p1, const wchar_t* p2);\n   static string_type BOOST_REGEX_CALL lookup_collatename(const wchar_t* p1, const wchar_t* p2);\n\n   static bool BOOST_REGEX_CALL isctype(wchar_t, char_class_type);\n   static int BOOST_REGEX_CALL value(wchar_t, int);\n\n   locale_type imbue(locale_type l)\n   { return l; }\n   locale_type getloc()const\n   { return locale_type(); }\n\nprivate:\n   // this type is not copyable:\n   c_regex_traits(const c_regex_traits&);\n   c_regex_traits& operator=(const c_regex_traits&);\n};\n\n#endif // BOOST_NO_WREGEX\n\ninline c_regex_traits<char>::string_type BOOST_REGEX_CALL c_regex_traits<char>::transform(const char* p1, const char* p2)\n{\n   std::string result(10, ' ');\n   std::size_t s = result.size();\n   std::size_t r;\n   std::string src(p1, p2);\n   while (s < (r = std::strxfrm(&*result.begin(), src.c_str(), s)))\n   {\n#if defined(_CPPLIB_VER)\n      //\n      // A bug in VC11 and 12 causes the program to hang if we pass a null-string\n      // to std::strxfrm, but only for certain locales :-(\n      // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware).\n      //\n      if (r == INT_MAX)\n      {\n         result.erase();\n         result.insert(result.begin(), static_cast<char>(0));\n         return result;\n      }\n#endif\n      result.append(r - s + 3, ' ');\n      s = result.size();\n   }\n   result.erase(r);\n   return result;\n}\n\ninline c_regex_traits<char>::string_type BOOST_REGEX_CALL c_regex_traits<char>::transform_primary(const char* p1, const char* p2)\n{\n   static char s_delim;\n   static const int s_collate_type = ::boost::BOOST_REGEX_DETAIL_NS::find_sort_syntax(static_cast<c_regex_traits<char>*>(0), &s_delim);\n   std::string result;\n   //\n   // What we do here depends upon the format of the sort key returned by\n   // sort key returned by this->transform:\n   //\n   switch (s_collate_type)\n   {\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_C:\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_unknown:\n      // the best we can do is translate to lower case, then get a regular sort key:\n   {\n      result.assign(p1, p2);\n      for (std::string::size_type i = 0; i < result.size(); ++i)\n         result[i] = static_cast<char>((std::tolower)(static_cast<unsigned char>(result[i])));\n      result = transform(&*result.begin(), &*result.begin() + result.size());\n      break;\n   }\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_fixed:\n   {\n      // get a regular sort key, and then truncate it:\n      result = transform(p1, p2);\n      result.erase(s_delim);\n      break;\n   }\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_delim:\n      // get a regular sort key, and then truncate everything after the delim:\n      result = transform(p1, p2);\n      if ((!result.empty()) && (result[0] == s_delim))\n         break;\n      std::size_t i;\n      for (i = 0; i < result.size(); ++i)\n      {\n         if (result[i] == s_delim)\n            break;\n      }\n      result.erase(i);\n      break;\n   }\n   if (result.empty())\n      result = std::string(1, char(0));\n   return result;\n}\n\ninline c_regex_traits<char>::char_class_type BOOST_REGEX_CALL c_regex_traits<char>::lookup_classname(const char* p1, const char* p2)\n{\n   using namespace BOOST_REGEX_DETAIL_NS;\n   static const char_class_type masks[] =\n   {\n      0,\n      char_class_alnum,\n      char_class_alpha,\n      char_class_blank,\n      char_class_cntrl,\n      char_class_digit,\n      char_class_digit,\n      char_class_graph,\n      char_class_horizontal,\n      char_class_lower,\n      char_class_lower,\n      char_class_print,\n      char_class_punct,\n      char_class_space,\n      char_class_space,\n      char_class_upper,\n      char_class_unicode,\n      char_class_upper,\n      char_class_vertical,\n      char_class_alnum | char_class_word,\n      char_class_alnum | char_class_word,\n      char_class_xdigit,\n   };\n\n   int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2);\n   if (idx < 0)\n   {\n      std::string s(p1, p2);\n      for (std::string::size_type i = 0; i < s.size(); ++i)\n         s[i] = static_cast<char>((std::tolower)(static_cast<unsigned char>(s[i])));\n      idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size());\n   }\n   BOOST_REGEX_ASSERT(std::size_t(idx) + 1u < sizeof(masks) / sizeof(masks[0]));\n   return masks[idx + 1];\n}\n\ninline bool BOOST_REGEX_CALL c_regex_traits<char>::isctype(char c, char_class_type mask)\n{\n   using namespace BOOST_REGEX_DETAIL_NS;\n   return\n      ((mask & char_class_space) && (std::isspace)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_print) && (std::isprint)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_cntrl) && (std::iscntrl)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_upper) && (std::isupper)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_lower) && (std::islower)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_alpha) && (std::isalpha)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_digit) && (std::isdigit)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_punct) && (std::ispunct)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_xdigit) && (std::isxdigit)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_blank) && (std::isspace)(static_cast<unsigned char>(c)) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c))\n      || ((mask & char_class_word) && (c == '_'))\n      || ((mask & char_class_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\\v')))\n      || ((mask & char_class_horizontal) && (std::isspace)(static_cast<unsigned char>(c)) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && (c != '\\v'));\n}\n\ninline c_regex_traits<char>::string_type BOOST_REGEX_CALL c_regex_traits<char>::lookup_collatename(const char* p1, const char* p2)\n{\n   std::string s(p1, p2);\n   s = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(s);\n   if (s.empty() && (p2 - p1 == 1))\n      s.append(1, *p1);\n   return s;\n}\n\ninline int BOOST_REGEX_CALL c_regex_traits<char>::value(char c, int radix)\n{\n   char b[2] = { c, '\\0', };\n   char* ep;\n   int result = std::strtol(b, &ep, radix);\n   if (ep == b)\n      return -1;\n   return result;\n}\n\n#ifndef BOOST_NO_WREGEX\n\ninline c_regex_traits<wchar_t>::string_type BOOST_REGEX_CALL c_regex_traits<wchar_t>::transform(const wchar_t* p1, const wchar_t* p2)\n{\n   std::size_t r;\n   std::size_t s = 10;\n   std::wstring src(p1, p2);\n   std::wstring result(s, L' ');\n   while (s < (r = std::wcsxfrm(&*result.begin(), src.c_str(), s)))\n   {\n#if defined(_CPPLIB_VER)\n      //\n      // A bug in VC11 and 12 causes the program to hang if we pass a null-string\n      // to std::strxfrm, but only for certain locales :-(\n      // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware).\n      //\n      if (r == INT_MAX)\n      {\n         result.erase();\n         result.insert(result.begin(), static_cast<wchar_t>(0));\n         return result;\n      }\n#endif\n      result.append(r - s + 3, L' ');\n      s = result.size();\n   }\n   result.erase(r);\n   return result;\n}\n\ninline c_regex_traits<wchar_t>::string_type BOOST_REGEX_CALL c_regex_traits<wchar_t>::transform_primary(const wchar_t* p1, const wchar_t* p2)\n{\n   static wchar_t s_delim;\n   static const int s_collate_type = ::boost::BOOST_REGEX_DETAIL_NS::find_sort_syntax(static_cast<const c_regex_traits<wchar_t>*>(0), &s_delim);\n   std::wstring result;\n   //\n   // What we do here depends upon the format of the sort key returned by\n   // sort key returned by this->transform:\n   //\n   switch (s_collate_type)\n   {\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_C:\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_unknown:\n      // the best we can do is translate to lower case, then get a regular sort key:\n   {\n      result.assign(p1, p2);\n      for (std::wstring::size_type i = 0; i < result.size(); ++i)\n         result[i] = (std::towlower)(result[i]);\n      result = c_regex_traits<wchar_t>::transform(&*result.begin(), &*result.begin() + result.size());\n      break;\n   }\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_fixed:\n   {\n      // get a regular sort key, and then truncate it:\n      result = c_regex_traits<wchar_t>::transform(&*result.begin(), &*result.begin() + result.size());\n      result.erase(s_delim);\n      break;\n   }\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_delim:\n      // get a regular sort key, and then truncate everything after the delim:\n      result = c_regex_traits<wchar_t>::transform(&*result.begin(), &*result.begin() + result.size());\n      if ((!result.empty()) && (result[0] == s_delim))\n         break;\n      std::size_t i;\n      for (i = 0; i < result.size(); ++i)\n      {\n         if (result[i] == s_delim)\n            break;\n      }\n      result.erase(i);\n      break;\n   }\n   if (result.empty())\n      result = std::wstring(1, char(0));\n   return result;\n}\n\ninline c_regex_traits<wchar_t>::char_class_type BOOST_REGEX_CALL c_regex_traits<wchar_t>::lookup_classname(const wchar_t* p1, const wchar_t* p2)\n{\n   using namespace BOOST_REGEX_DETAIL_NS;\n   static const char_class_type masks[] =\n   {\n      0,\n      char_class_alnum,\n      char_class_alpha,\n      char_class_blank,\n      char_class_cntrl,\n      char_class_digit,\n      char_class_digit,\n      char_class_graph,\n      char_class_horizontal,\n      char_class_lower,\n      char_class_lower,\n      char_class_print,\n      char_class_punct,\n      char_class_space,\n      char_class_space,\n      char_class_upper,\n      char_class_unicode,\n      char_class_upper,\n      char_class_vertical,\n      char_class_alnum | char_class_word,\n      char_class_alnum | char_class_word,\n      char_class_xdigit,\n   };\n\n   int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2);\n   if (idx < 0)\n   {\n      std::wstring s(p1, p2);\n      for (std::wstring::size_type i = 0; i < s.size(); ++i)\n         s[i] = (std::towlower)(s[i]);\n      idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size());\n   }\n   BOOST_REGEX_ASSERT(idx + 1 < static_cast<int>(sizeof(masks) / sizeof(masks[0])));\n   return masks[idx + 1];\n}\n\ninline bool BOOST_REGEX_CALL c_regex_traits<wchar_t>::isctype(wchar_t c, char_class_type mask)\n{\n   using namespace BOOST_REGEX_DETAIL_NS;\n   return\n      ((mask & char_class_space) && (std::iswspace)(c))\n      || ((mask & char_class_print) && (std::iswprint)(c))\n      || ((mask & char_class_cntrl) && (std::iswcntrl)(c))\n      || ((mask & char_class_upper) && (std::iswupper)(c))\n      || ((mask & char_class_lower) && (std::iswlower)(c))\n      || ((mask & char_class_alpha) && (std::iswalpha)(c))\n      || ((mask & char_class_digit) && (std::iswdigit)(c))\n      || ((mask & char_class_punct) && (std::iswpunct)(c))\n      || ((mask & char_class_xdigit) && (std::iswxdigit)(c))\n      || ((mask & char_class_blank) && (std::iswspace)(c) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c))\n      || ((mask & char_class_word) && (c == '_'))\n      || ((mask & char_class_unicode) && (c & ~static_cast<wchar_t>(0xff)))\n      || ((mask & char_class_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == L'\\v')))\n      || ((mask & char_class_horizontal) && (std::iswspace)(c) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && (c != L'\\v'));\n}\n\ninline c_regex_traits<wchar_t>::string_type BOOST_REGEX_CALL c_regex_traits<wchar_t>::lookup_collatename(const wchar_t* p1, const wchar_t* p2)\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4244)\n#endif\n   std::string name(p1, p2);\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n   name = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(name);\n   if (!name.empty())\n      return string_type(name.begin(), name.end());\n   if (p2 - p1 == 1)\n      return string_type(1, *p1);\n   return string_type();\n}\n\ninline int BOOST_REGEX_CALL c_regex_traits<wchar_t>::value(wchar_t c, int radix)\n{\n#ifdef BOOST_BORLANDC\n   // workaround for broken wcstol:\n   if ((std::iswxdigit)(c) == 0)\n      return -1;\n#endif\n   wchar_t b[2] = { c, '\\0', };\n   wchar_t* ep;\n   int result = std::wcstol(b, &ep, radix);\n   if (ep == b)\n      return -1;\n   return result;\n}\n\n#endif\n\n}\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/char_regex_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the\n * Boost Software License, Version 1.0. (See accompanying file\n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         char_regex_traits.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares deprecated traits classes char_regex_traits<>.\n  */\n\n\n#ifndef BOOST_REGEX_V4_CHAR_REGEX_TRAITS_HPP\n#define BOOST_REGEX_V4_CHAR_REGEX_TRAITS_HPP\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\n\nnamespace deprecated{\n//\n// class char_regex_traits_i\n// provides case insensitive traits classes (deprecated):\ntemplate <class charT>\nclass char_regex_traits_i : public regex_traits<charT> {};\n\ntemplate<>\nclass char_regex_traits_i<char> : public regex_traits<char>\n{\npublic:\n   typedef char char_type;\n   typedef unsigned char uchar_type;\n   typedef unsigned int size_type;\n   typedef regex_traits<char> base_type;\n\n};\n\n#ifndef BOOST_NO_WREGEX\ntemplate<>\nclass char_regex_traits_i<wchar_t> : public regex_traits<wchar_t>\n{\npublic:\n   typedef wchar_t char_type;\n   typedef unsigned short uchar_type;\n   typedef unsigned int size_type;\n   typedef regex_traits<wchar_t> base_type;\n\n};\n#endif\n} // namespace deprecated\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif // include\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/cpp_regex_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 2004 John Maddock\n * Copyright 2011 Garmin Ltd. or its subsidiaries\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         cpp_regex_traits.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression traits class cpp_regex_traits.\n  */\n\n#ifndef BOOST_CPP_REGEX_TRAITS_HPP_INCLUDED\n#define BOOST_CPP_REGEX_TRAITS_HPP_INCLUDED\n\n#include <boost/config.hpp>\n#include <boost/integer.hpp>\n#include <boost/type_traits/make_unsigned.hpp>\n\n#ifndef BOOST_NO_STD_LOCALE\n\n#ifndef BOOST_RE_PAT_EXCEPT_HPP\n#include <boost/regex/pattern_except.hpp>\n#endif\n#ifndef BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED\n#include <boost/regex/v4/regex_traits_defaults.hpp>\n#endif\n#ifdef BOOST_HAS_THREADS\n#include <boost/regex/pending/static_mutex.hpp>\n#endif\n#ifndef BOOST_REGEX_PRIMARY_TRANSFORM\n#include <boost/regex/v4/primary_transform.hpp>\n#endif\n#ifndef BOOST_REGEX_OBJECT_CACHE_HPP\n#include <boost/regex/v4/object_cache.hpp>\n#endif\n\n#include <climits>\n#include <ios>\n#include <istream>\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4786 4251)\n#endif\n\nnamespace boost{ \n\n//\n// forward declaration is needed by some compilers:\n//\ntemplate <class charT>\nclass cpp_regex_traits;\n   \nnamespace BOOST_REGEX_DETAIL_NS{\n\n//\n// class parser_buf:\n// acts as a stream buffer which wraps around a pair of pointers:\n//\ntemplate <class charT,\n          class traits = ::std::char_traits<charT> >\nclass parser_buf : public ::std::basic_streambuf<charT, traits>\n{\n   typedef ::std::basic_streambuf<charT, traits> base_type;\n   typedef typename base_type::int_type int_type;\n   typedef typename base_type::char_type char_type;\n   typedef typename base_type::pos_type pos_type;\n   typedef ::std::streamsize streamsize;\n   typedef typename base_type::off_type off_type;\npublic:\n   parser_buf() : base_type() { setbuf(0, 0); }\n   const charT* getnext() { return this->gptr(); }\nprotected:\n   std::basic_streambuf<charT, traits>* setbuf(char_type* s, streamsize n) BOOST_OVERRIDE;\n   typename parser_buf<charT, traits>::pos_type seekpos(pos_type sp, ::std::ios_base::openmode which) BOOST_OVERRIDE;\n   typename parser_buf<charT, traits>::pos_type seekoff(off_type off, ::std::ios_base::seekdir way, ::std::ios_base::openmode which) BOOST_OVERRIDE;\nprivate:\n   parser_buf& operator=(const parser_buf&);\n   parser_buf(const parser_buf&);\n};\n\ntemplate<class charT, class traits>\nstd::basic_streambuf<charT, traits>*\nparser_buf<charT, traits>::setbuf(char_type* s, streamsize n)\n{\n   this->setg(s, s, s + n);\n   return this;\n}\n\ntemplate<class charT, class traits>\ntypename parser_buf<charT, traits>::pos_type\nparser_buf<charT, traits>::seekoff(off_type off, ::std::ios_base::seekdir way, ::std::ios_base::openmode which)\n{\n   typedef typename boost::int_t<sizeof(way) * CHAR_BIT>::least cast_type;\n\n   if(which & ::std::ios_base::out)\n      return pos_type(off_type(-1));\n   std::ptrdiff_t size = this->egptr() - this->eback();\n   std::ptrdiff_t pos = this->gptr() - this->eback();\n   charT* g = this->eback();\n   switch(static_cast<cast_type>(way))\n   {\n   case ::std::ios_base::beg:\n      if((off < 0) || (off > size))\n         return pos_type(off_type(-1));\n      else\n         this->setg(g, g + off, g + size);\n      break;\n   case ::std::ios_base::end:\n      if((off < 0) || (off > size))\n         return pos_type(off_type(-1));\n      else\n         this->setg(g, g + size - off, g + size);\n      break;\n   case ::std::ios_base::cur:\n   {\n      std::ptrdiff_t newpos = static_cast<std::ptrdiff_t>(pos + off);\n      if((newpos < 0) || (newpos > size))\n         return pos_type(off_type(-1));\n      else\n         this->setg(g, g + newpos, g + size);\n      break;\n   }\n   default: ;\n   }\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4244)\n#endif\n   return static_cast<pos_type>(this->gptr() - this->eback());\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate<class charT, class traits>\ntypename parser_buf<charT, traits>::pos_type\nparser_buf<charT, traits>::seekpos(pos_type sp, ::std::ios_base::openmode which)\n{\n   if(which & ::std::ios_base::out)\n      return pos_type(off_type(-1));\n   off_type size = static_cast<off_type>(this->egptr() - this->eback());\n   charT* g = this->eback();\n   if(off_type(sp) <= size)\n   {\n      this->setg(g, g + off_type(sp), g + size);\n   }\n   return pos_type(off_type(-1));\n}\n\n//\n// class cpp_regex_traits_base:\n// acts as a container for locale and the facets we are using.\n//\ntemplate <class charT>\nstruct cpp_regex_traits_base\n{\n   cpp_regex_traits_base(const std::locale& l)\n   { (void)imbue(l); }\n   std::locale imbue(const std::locale& l);\n\n   std::locale m_locale;\n   std::ctype<charT> const* m_pctype;\n#ifndef BOOST_NO_STD_MESSAGES\n   std::messages<charT> const* m_pmessages;\n#endif\n   std::collate<charT> const* m_pcollate;\n\n   bool operator<(const cpp_regex_traits_base& b)const\n   {\n      if(m_pctype == b.m_pctype)\n      {\n#ifndef BOOST_NO_STD_MESSAGES\n         if(m_pmessages == b.m_pmessages)\n         {\n            return m_pcollate < b.m_pcollate;\n         }\n         return m_pmessages < b.m_pmessages;\n#else\n         return m_pcollate < b.m_pcollate;\n#endif\n      }\n      return m_pctype < b.m_pctype;\n   }\n   bool operator==(const cpp_regex_traits_base& b)const\n   {\n      return (m_pctype == b.m_pctype) \n#ifndef BOOST_NO_STD_MESSAGES\n         && (m_pmessages == b.m_pmessages) \n#endif\n         && (m_pcollate == b.m_pcollate);\n   }\n};\n\ntemplate <class charT>\nstd::locale cpp_regex_traits_base<charT>::imbue(const std::locale& l)\n{\n   std::locale result(m_locale);\n   m_locale = l;\n   m_pctype = &BOOST_USE_FACET(std::ctype<charT>, l);\n#ifndef BOOST_NO_STD_MESSAGES\n   m_pmessages = BOOST_HAS_FACET(std::messages<charT>, l) ? &BOOST_USE_FACET(std::messages<charT>, l) : 0;\n#endif\n   m_pcollate = &BOOST_USE_FACET(std::collate<charT>, l);\n   return result;\n}\n\n//\n// class cpp_regex_traits_char_layer:\n// implements methods that require specialization for narrow characters:\n//\ntemplate <class charT>\nclass cpp_regex_traits_char_layer : public cpp_regex_traits_base<charT>\n{\n   typedef std::basic_string<charT> string_type;\n   typedef std::map<charT, regex_constants::syntax_type> map_type;\n   typedef typename map_type::const_iterator map_iterator_type;\npublic:\n   cpp_regex_traits_char_layer(const std::locale& l)\n      : cpp_regex_traits_base<charT>(l)\n   {\n      init();\n   }\n   cpp_regex_traits_char_layer(const cpp_regex_traits_base<charT>& b)\n      : cpp_regex_traits_base<charT>(b)\n   {\n      init();\n   }\n   void init();\n\n   regex_constants::syntax_type syntax_type(charT c)const\n   {\n      map_iterator_type i = m_char_map.find(c);\n      return ((i == m_char_map.end()) ? 0 : i->second);\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(charT c) const\n   {\n      map_iterator_type i = m_char_map.find(c);\n      if(i == m_char_map.end())\n      {\n         if(this->m_pctype->is(std::ctype_base::lower, c)) return regex_constants::escape_type_class;\n         if(this->m_pctype->is(std::ctype_base::upper, c)) return regex_constants::escape_type_not_class;\n         return 0;\n      }\n      return i->second;\n   }\n\nprivate:\n   string_type get_default_message(regex_constants::syntax_type);\n   // TODO: use a hash table when available!\n   map_type m_char_map;\n};\n\ntemplate <class charT>\nvoid cpp_regex_traits_char_layer<charT>::init()\n{\n   // we need to start by initialising our syntax map so we know which\n   // character is used for which purpose:\n#ifndef BOOST_NO_STD_MESSAGES\n#ifndef __IBMCPP__\n   typename std::messages<charT>::catalog cat = static_cast<std::messages<char>::catalog>(-1);\n#else\n   typename std::messages<charT>::catalog cat = reinterpret_cast<std::messages<char>::catalog>(-1);\n#endif\n   std::string cat_name(cpp_regex_traits<charT>::get_catalog_name());\n   if((!cat_name.empty()) && (this->m_pmessages != 0))\n   {\n      cat = this->m_pmessages->open(\n         cat_name, \n         this->m_locale);\n      if((int)cat < 0)\n      {\n         std::string m(\"Unable to open message catalog: \");\n         std::runtime_error err(m + cat_name);\n         boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n      }\n   }\n   //\n   // if we have a valid catalog then load our messages:\n   //\n   if((int)cat >= 0)\n   {\n#ifndef BOOST_NO_EXCEPTIONS\n      try{\n#endif\n         for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n         {\n            string_type mss = this->m_pmessages->get(cat, 0, i, get_default_message(i));\n            for(typename string_type::size_type j = 0; j < mss.size(); ++j)\n            {\n               m_char_map[mss[j]] = i;\n            }\n         }\n         this->m_pmessages->close(cat);\n#ifndef BOOST_NO_EXCEPTIONS\n      }\n      catch(...)\n      {\n         if(this->m_pmessages)\n            this->m_pmessages->close(cat);\n         throw;\n      }\n#endif\n   }\n   else\n   {\n#endif\n      for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n      {\n         const char* ptr = get_default_syntax(i);\n         while(ptr && *ptr)\n         {\n            m_char_map[this->m_pctype->widen(*ptr)] = i;\n            ++ptr;\n         }\n      }\n#ifndef BOOST_NO_STD_MESSAGES\n   }\n#endif\n}\n\ntemplate <class charT>\ntypename cpp_regex_traits_char_layer<charT>::string_type \n   cpp_regex_traits_char_layer<charT>::get_default_message(regex_constants::syntax_type i)\n{\n   const char* ptr = get_default_syntax(i);\n   string_type result;\n   while(ptr && *ptr)\n   {\n      result.append(1, this->m_pctype->widen(*ptr));\n      ++ptr;\n   }\n   return result;\n}\n\n//\n// specialized version for narrow characters:\n//\ntemplate <>\nclass cpp_regex_traits_char_layer<char> : public cpp_regex_traits_base<char>\n{\n   typedef std::string string_type;\npublic:\n   cpp_regex_traits_char_layer(const std::locale& l)\n   : cpp_regex_traits_base<char>(l)\n   {\n      init();\n   }\n   cpp_regex_traits_char_layer(const cpp_regex_traits_base<char>& l)\n   : cpp_regex_traits_base<char>(l)\n   {\n      init();\n   }\n\n   regex_constants::syntax_type syntax_type(char c)const\n   {\n      return m_char_map[static_cast<unsigned char>(c)];\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(char c) const\n   {\n      return m_char_map[static_cast<unsigned char>(c)];\n   }\n\nprivate:\n   regex_constants::syntax_type m_char_map[1u << CHAR_BIT];\n   void init();\n};\n\n#ifdef BOOST_REGEX_BUGGY_CTYPE_FACET\nenum\n{\n   char_class_space=1<<0, \n   char_class_print=1<<1, \n   char_class_cntrl=1<<2, \n   char_class_upper=1<<3, \n   char_class_lower=1<<4,\n   char_class_alpha=1<<5, \n   char_class_digit=1<<6, \n   char_class_punct=1<<7, \n   char_class_xdigit=1<<8,\n   char_class_alnum=char_class_alpha|char_class_digit, \n   char_class_graph=char_class_alnum|char_class_punct,\n   char_class_blank=1<<9,\n   char_class_word=1<<10,\n   char_class_unicode=1<<11,\n   char_class_horizontal_space=1<<12,\n   char_class_vertical_space=1<<13\n};\n\n#endif\n\n//\n// class cpp_regex_traits_implementation:\n// provides pimpl implementation for cpp_regex_traits.\n//\ntemplate <class charT>\nclass cpp_regex_traits_implementation : public cpp_regex_traits_char_layer<charT>\n{\npublic:\n   typedef typename cpp_regex_traits<charT>::char_class_type      char_class_type;\n   typedef typename std::ctype<charT>::mask                       native_mask_type;\n   typedef typename boost::make_unsigned<native_mask_type>::type  unsigned_native_mask_type;\n#ifndef BOOST_REGEX_BUGGY_CTYPE_FACET\n   BOOST_STATIC_CONSTANT(char_class_type, mask_blank = 1u << 24);\n   BOOST_STATIC_CONSTANT(char_class_type, mask_word = 1u << 25);\n   BOOST_STATIC_CONSTANT(char_class_type, mask_unicode = 1u << 26);\n   BOOST_STATIC_CONSTANT(char_class_type, mask_horizontal = 1u << 27);\n   BOOST_STATIC_CONSTANT(char_class_type, mask_vertical = 1u << 28);\n#endif\n\n   typedef std::basic_string<charT> string_type;\n   typedef charT char_type;\n   //cpp_regex_traits_implementation();\n   cpp_regex_traits_implementation(const std::locale& l)\n      : cpp_regex_traits_char_layer<charT>(l)\n   {\n      init();\n   }\n   cpp_regex_traits_implementation(const cpp_regex_traits_base<charT>& l)\n      : cpp_regex_traits_char_layer<charT>(l)\n   {\n      init();\n   }\n   std::string error_string(regex_constants::error_type n) const\n   {\n      if(!m_error_strings.empty())\n      {\n         std::map<int, std::string>::const_iterator p = m_error_strings.find(n);\n         return (p == m_error_strings.end()) ? std::string(get_default_error_string(n)) : p->second;\n      }\n      return get_default_error_string(n);\n   }\n   char_class_type lookup_classname(const charT* p1, const charT* p2) const\n   {\n      char_class_type result = lookup_classname_imp(p1, p2);\n      if(result == 0)\n      {\n         string_type temp(p1, p2);\n         this->m_pctype->tolower(&*temp.begin(), &*temp.begin() + temp.size());\n         result = lookup_classname_imp(&*temp.begin(), &*temp.begin() + temp.size());\n      }\n      return result;\n   }\n   string_type lookup_collatename(const charT* p1, const charT* p2) const;\n   string_type transform_primary(const charT* p1, const charT* p2) const;\n   string_type transform(const charT* p1, const charT* p2) const;\nprivate:\n   std::map<int, std::string>     m_error_strings;   // error messages indexed by numberic ID\n   std::map<string_type, char_class_type>  m_custom_class_names; // character class names\n   std::map<string_type, string_type>      m_custom_collate_names; // collating element names\n   unsigned                       m_collate_type;    // the form of the collation string\n   charT                          m_collate_delim;   // the collation group delimiter\n   //\n   // helpers:\n   //\n   char_class_type lookup_classname_imp(const charT* p1, const charT* p2) const;\n   void init();\n#ifdef BOOST_REGEX_BUGGY_CTYPE_FACET\npublic:\n   bool isctype(charT c, char_class_type m)const;\n#endif\n};\n\n#ifndef BOOST_REGEX_BUGGY_CTYPE_FACET\n#if !defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION)\n\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type const cpp_regex_traits_implementation<charT>::mask_blank;\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type const cpp_regex_traits_implementation<charT>::mask_word;\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type const cpp_regex_traits_implementation<charT>::mask_unicode;\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type const cpp_regex_traits_implementation<charT>::mask_vertical;\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type const cpp_regex_traits_implementation<charT>::mask_horizontal;\n\n#endif\n#endif\n\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::string_type \n   cpp_regex_traits_implementation<charT>::transform_primary(const charT* p1, const charT* p2) const\n{\n   //\n   // PRECONDITIONS:\n   //\n   // A bug in gcc 3.2 (and maybe other versions as well) treats\n   // p1 as a null terminated string, for efficiency reasons \n   // we work around this elsewhere, but just assert here that\n   // we adhere to gcc's (buggy) preconditions...\n   //\n   BOOST_REGEX_ASSERT(*p2 == 0);\n   string_type result;\n#if defined(_CPPLIB_VER)\n   //\n   // A bug in VC11 and 12 causes the program to hang if we pass a null-string\n   // to std::collate::transform, but only for certain locales :-(\n   // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware).\n   //\n   if(*p1 == 0)\n   {\n      return string_type(1, charT(0));\n   }\n#endif\n   //\n   // swallowing all exceptions here is a bad idea\n   // however at least one std lib will always throw\n   // std::bad_alloc for certain arguments...\n   //\n#ifndef BOOST_NO_EXCEPTIONS\n   try{\n#endif\n      //\n      // What we do here depends upon the format of the sort key returned by\n      // sort key returned by this->transform:\n      //\n      switch(m_collate_type)\n      {\n      case sort_C:\n      case sort_unknown:\n         // the best we can do is translate to lower case, then get a regular sort key:\n         {\n            result.assign(p1, p2);\n            this->m_pctype->tolower(&*result.begin(), &*result.begin() + result.size());\n            result = this->m_pcollate->transform(&*result.begin(), &*result.begin() + result.size());\n            break;\n         }\n      case sort_fixed:\n         {\n            // get a regular sort key, and then truncate it:\n            result.assign(this->m_pcollate->transform(p1, p2));\n            result.erase(this->m_collate_delim);\n            break;\n         }\n      case sort_delim:\n            // get a regular sort key, and then truncate everything after the delim:\n            result.assign(this->m_pcollate->transform(p1, p2));\n            std::size_t i;\n            for(i = 0; i < result.size(); ++i)\n            {\n               if(result[i] == m_collate_delim)\n                  break;\n            }\n            result.erase(i);\n            break;\n      }\n#ifndef BOOST_NO_EXCEPTIONS\n   }catch(...){}\n#endif\n   while((!result.empty()) && (charT(0) == *result.rbegin()))\n      result.erase(result.size() - 1);\n   if(result.empty())\n   {\n      // character is ignorable at the primary level:\n      result = string_type(1, charT(0));\n   }\n   return result;\n}\n\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::string_type \n   cpp_regex_traits_implementation<charT>::transform(const charT* p1, const charT* p2) const\n{\n   //\n   // PRECONDITIONS:\n   //\n   // A bug in gcc 3.2 (and maybe other versions as well) treats\n   // p1 as a null terminated string, for efficiency reasons \n   // we work around this elsewhere, but just assert here that\n   // we adhere to gcc's (buggy) preconditions...\n   //\n   BOOST_REGEX_ASSERT(*p2 == 0);\n   //\n   // swallowing all exceptions here is a bad idea\n   // however at least one std lib will always throw\n   // std::bad_alloc for certain arguments...\n   //\n   string_type result, result2;\n#if defined(_CPPLIB_VER)\n   //\n   // A bug in VC11 and 12 causes the program to hang if we pass a null-string\n   // to std::collate::transform, but only for certain locales :-(\n   // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware).\n   //\n   if(*p1 == 0)\n   {\n      return result;\n   }\n#endif\n#ifndef BOOST_NO_EXCEPTIONS\n   try{\n#endif\n      result = this->m_pcollate->transform(p1, p2);\n      //\n      // Borland's STLPort version returns a NULL-terminated\n      // string that has garbage at the end - each call to\n      // std::collate<wchar_t>::transform returns a different string!\n      // So as a workaround, we'll truncate the string at the first NULL\n      // which _seems_ to work....\n#if BOOST_WORKAROUND(BOOST_BORLANDC, < 0x580)\n      result.erase(result.find(charT(0)));\n#else\n      //\n      // some implementations (Dinkumware) append unnecessary trailing \\0's:\n      while((!result.empty()) && (charT(0) == *result.rbegin()))\n         result.erase(result.size() - 1);\n#endif\n      //\n      // We may have NULL's used as separators between sections of the collate string,\n      // an example would be Boost.Locale.  We have no way to detect this case via\n      // #defines since this can be used with any compiler/platform combination.\n      // Unfortunately our state machine (which was devised when all implementations\n      // used underlying C language API's) can't cope with that case.  One workaround\n      // is to replace each character with 2, fortunately this code isn't used that\n      // much as this is now slower than before :-(\n      //\n      typedef typename make_unsigned<charT>::type uchar_type;\n      result2.reserve(result.size() * 2 + 2);\n      for(unsigned i = 0; i < result.size(); ++i)\n      {\n         if(static_cast<uchar_type>(result[i]) == (std::numeric_limits<uchar_type>::max)())\n         {\n            result2.append(1, charT((std::numeric_limits<uchar_type>::max)())).append(1, charT('b'));\n         }\n         else\n         {\n            result2.append(1, static_cast<charT>(1 + static_cast<uchar_type>(result[i]))).append(1, charT('b') - 1);\n         }\n      }\n      BOOST_REGEX_ASSERT(std::find(result2.begin(), result2.end(), charT(0)) == result2.end());\n#ifndef BOOST_NO_EXCEPTIONS\n   }\n   catch(...)\n   {\n   }\n#endif\n   return result2;\n}\n\n\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::string_type \n   cpp_regex_traits_implementation<charT>::lookup_collatename(const charT* p1, const charT* p2) const\n{\n   typedef typename std::map<string_type, string_type>::const_iterator iter_type;\n   if(!m_custom_collate_names.empty())\n   {\n      iter_type pos = m_custom_collate_names.find(string_type(p1, p2));\n      if(pos != m_custom_collate_names.end())\n         return pos->second;\n   }\n#if !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)\\\n               && !BOOST_WORKAROUND(BOOST_BORLANDC, <= 0x0551)\n   std::string name(p1, p2);\n#else\n   std::string name;\n   const charT* p0 = p1;\n   while(p0 != p2)\n      name.append(1, char(*p0++));\n#endif\n   name = lookup_default_collate_name(name);\n#if !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)\\\n               && !BOOST_WORKAROUND(BOOST_BORLANDC, <= 0x0551)\n   if(!name.empty())\n      return string_type(name.begin(), name.end());\n#else\n   if(!name.empty())\n   {\n      string_type result;\n      typedef std::string::const_iterator iter;\n      iter b = name.begin();\n      iter e = name.end();\n      while(b != e)\n         result.append(1, charT(*b++));\n      return result;\n   }\n#endif\n   if(p2 - p1 == 1)\n      return string_type(1, *p1);\n   return string_type();\n}\n\ntemplate <class charT>\nvoid cpp_regex_traits_implementation<charT>::init()\n{\n#ifndef BOOST_NO_STD_MESSAGES\n#ifndef __IBMCPP__\n   typename std::messages<charT>::catalog cat = static_cast<std::messages<char>::catalog>(-1);\n#else\n   typename std::messages<charT>::catalog cat = reinterpret_cast<std::messages<char>::catalog>(-1);\n#endif\n   std::string cat_name(cpp_regex_traits<charT>::get_catalog_name());\n   if((!cat_name.empty()) && (this->m_pmessages != 0))\n   {\n      cat = this->m_pmessages->open(\n         cat_name, \n         this->m_locale);\n      if((int)cat < 0)\n      {\n         std::string m(\"Unable to open message catalog: \");\n         std::runtime_error err(m + cat_name);\n         boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n      }\n   }\n   //\n   // if we have a valid catalog then load our messages:\n   //\n   if((int)cat >= 0)\n   {\n      //\n      // Error messages:\n      //\n      for(boost::regex_constants::error_type i = static_cast<boost::regex_constants::error_type>(0); \n         i <= boost::regex_constants::error_unknown; \n         i = static_cast<boost::regex_constants::error_type>(i + 1))\n      {\n         const char* p = get_default_error_string(i);\n         string_type default_message;\n         while(*p)\n         {\n            default_message.append(1, this->m_pctype->widen(*p));\n            ++p;\n         }\n         string_type s = this->m_pmessages->get(cat, 0, i+200, default_message);\n         std::string result;\n         for(std::string::size_type j = 0; j < s.size(); ++j)\n         {\n            result.append(1, this->m_pctype->narrow(s[j], 0));\n         }\n         m_error_strings[i] = result;\n      }\n      //\n      // Custom class names:\n      //\n#ifndef BOOST_REGEX_BUGGY_CTYPE_FACET\n      static const char_class_type masks[16] = \n      {\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::alnum),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::alpha),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::cntrl),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::digit),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::graph),\n         cpp_regex_traits_implementation<charT>::mask_horizontal,\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::lower),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::print),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::punct),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::space),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::upper),\n         cpp_regex_traits_implementation<charT>::mask_vertical,\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::xdigit),\n         cpp_regex_traits_implementation<charT>::mask_blank,\n         cpp_regex_traits_implementation<charT>::mask_word,\n         cpp_regex_traits_implementation<charT>::mask_unicode,\n      };\n#else\n      static const char_class_type masks[16] = \n      {\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_alnum,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_alpha,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_cntrl,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_digit,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_graph,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_horizontal_space,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_lower,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_print,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_punct,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_space,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_upper,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_vertical_space,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_xdigit,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_blank,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_word,\n         ::boost::BOOST_REGEX_DETAIL_NS::char_class_unicode,\n      };\n#endif\n      static const string_type null_string;\n      for(unsigned int j = 0; j <= 13; ++j)\n      {\n         string_type s(this->m_pmessages->get(cat, 0, j+300, null_string));\n         if(!s.empty())\n            this->m_custom_class_names[s] = masks[j];\n      }\n   }\n#endif\n   //\n   // get the collation format used by m_pcollate:\n   //\n   m_collate_type = BOOST_REGEX_DETAIL_NS::find_sort_syntax(this, &m_collate_delim);\n}\n\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type \n   cpp_regex_traits_implementation<charT>::lookup_classname_imp(const charT* p1, const charT* p2) const\n{\n#ifndef BOOST_REGEX_BUGGY_CTYPE_FACET\n   static const char_class_type masks[22] = \n   {\n      0,\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::alnum),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::alpha),\n      cpp_regex_traits_implementation<charT>::mask_blank,\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::cntrl),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::digit),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::digit),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::graph),\n      cpp_regex_traits_implementation<charT>::mask_horizontal,\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::lower),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::lower),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::print),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::punct),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::space),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::space),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::upper),\n      cpp_regex_traits_implementation<charT>::mask_unicode,\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::upper),\n      cpp_regex_traits_implementation<charT>::mask_vertical,\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::alnum) | cpp_regex_traits_implementation<charT>::mask_word, \n      static_cast<unsigned_native_mask_type>(std::ctype<char>::alnum) | cpp_regex_traits_implementation<charT>::mask_word, \n      static_cast<unsigned_native_mask_type>(std::ctype<char>::xdigit),\n   };\n#else\n   static const char_class_type masks[22] = \n   {\n      0,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_alnum, \n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_alpha,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_blank,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_cntrl,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_digit,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_digit,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_graph,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_horizontal_space,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_lower,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_lower,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_print,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_punct,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_space,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_space,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_upper,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_unicode,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_upper,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_vertical_space,\n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_alnum | ::boost::BOOST_REGEX_DETAIL_NS::char_class_word, \n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_alnum | ::boost::BOOST_REGEX_DETAIL_NS::char_class_word, \n      ::boost::BOOST_REGEX_DETAIL_NS::char_class_xdigit,\n   };\n#endif\n   if(!m_custom_class_names.empty())\n   {\n      typedef typename std::map<std::basic_string<charT>, char_class_type>::const_iterator map_iter;\n      map_iter pos = m_custom_class_names.find(string_type(p1, p2));\n      if(pos != m_custom_class_names.end())\n         return pos->second;\n   }\n   std::size_t state_id = 1 + BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2);\n   BOOST_REGEX_ASSERT(state_id < sizeof(masks) / sizeof(masks[0]));\n   return masks[state_id];\n}\n\n#ifdef BOOST_REGEX_BUGGY_CTYPE_FACET\ntemplate <class charT>\nbool cpp_regex_traits_implementation<charT>::isctype(const charT c, char_class_type mask) const\n{\n   return\n      ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_space) && (this->m_pctype->is(std::ctype<charT>::space, c)))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_print) && (this->m_pctype->is(std::ctype<charT>::print, c)))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_cntrl) && (this->m_pctype->is(std::ctype<charT>::cntrl, c)))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_upper) && (this->m_pctype->is(std::ctype<charT>::upper, c)))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_lower) && (this->m_pctype->is(std::ctype<charT>::lower, c)))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_alpha) && (this->m_pctype->is(std::ctype<charT>::alpha, c)))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_digit) && (this->m_pctype->is(std::ctype<charT>::digit, c)))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_punct) && (this->m_pctype->is(std::ctype<charT>::punct, c)))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_xdigit) && (this->m_pctype->is(std::ctype<charT>::xdigit, c)))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_blank) && (this->m_pctype->is(std::ctype<charT>::space, c)) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_word) && (c == '_'))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_unicode) && ::boost::BOOST_REGEX_DETAIL_NS::is_extended(c))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_vertical_space) && (is_separator(c) || (c == '\\v')))\n      || ((mask & ::boost::BOOST_REGEX_DETAIL_NS::char_class_horizontal_space) && this->m_pctype->is(std::ctype<charT>::space, c) && !(is_separator(c) || (c == '\\v')));\n}\n#endif\n\n\ntemplate <class charT>\ninline boost::shared_ptr<const cpp_regex_traits_implementation<charT> > create_cpp_regex_traits(const std::locale& l)\n{\n   cpp_regex_traits_base<charT> key(l);\n   return ::boost::object_cache<cpp_regex_traits_base<charT>, cpp_regex_traits_implementation<charT> >::get(key, 5);\n}\n\n} // BOOST_REGEX_DETAIL_NS\n\ntemplate <class charT>\nclass cpp_regex_traits\n{\nprivate:\n   typedef std::ctype<charT>            ctype_type;\npublic:\n   typedef charT                        char_type;\n   typedef std::size_t                  size_type;\n   typedef std::basic_string<char_type> string_type;\n   typedef std::locale                  locale_type;\n   typedef boost::uint_least32_t        char_class_type;\n\n   struct boost_extensions_tag{};\n\n   cpp_regex_traits()\n      : m_pimpl(BOOST_REGEX_DETAIL_NS::create_cpp_regex_traits<charT>(std::locale()))\n   { }\n   static size_type length(const char_type* p)\n   {\n      return std::char_traits<charT>::length(p);\n   }\n   regex_constants::syntax_type syntax_type(charT c)const\n   {\n      return m_pimpl->syntax_type(c);\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(charT c) const\n   {\n      return m_pimpl->escape_syntax_type(c);\n   }\n   charT translate(charT c) const\n   {\n      return c;\n   }\n   charT translate_nocase(charT c) const\n   {\n      return m_pimpl->m_pctype->tolower(c);\n   }\n   charT translate(charT c, bool icase) const\n   {\n      return icase ? m_pimpl->m_pctype->tolower(c) : c;\n   }\n   charT tolower(charT c) const\n   {\n      return m_pimpl->m_pctype->tolower(c);\n   }\n   charT toupper(charT c) const\n   {\n      return m_pimpl->m_pctype->toupper(c);\n   }\n   string_type transform(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->transform(p1, p2);\n   }\n   string_type transform_primary(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->transform_primary(p1, p2);\n   }\n   char_class_type lookup_classname(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->lookup_classname(p1, p2);\n   }\n   string_type lookup_collatename(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->lookup_collatename(p1, p2);\n   }\n   bool isctype(charT c, char_class_type f) const\n   {\n#ifndef BOOST_REGEX_BUGGY_CTYPE_FACET\n      typedef typename std::ctype<charT>::mask ctype_mask;\n\n      static const ctype_mask mask_base = \n         static_cast<ctype_mask>(\n            std::ctype<charT>::alnum \n            | std::ctype<charT>::alpha\n            | std::ctype<charT>::cntrl\n            | std::ctype<charT>::digit\n            | std::ctype<charT>::graph\n            | std::ctype<charT>::lower\n            | std::ctype<charT>::print\n            | std::ctype<charT>::punct\n            | std::ctype<charT>::space\n            | std::ctype<charT>::upper\n            | std::ctype<charT>::xdigit);\n\n      if((f & mask_base) \n         && (m_pimpl->m_pctype->is(\n            static_cast<ctype_mask>(f & mask_base), c)))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_unicode) && BOOST_REGEX_DETAIL_NS::is_extended(c))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_word) && (c == '_'))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_blank) \n         && m_pimpl->m_pctype->is(std::ctype<charT>::space, c)\n         && !BOOST_REGEX_DETAIL_NS::is_separator(c))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_vertical) \n         && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\\v')))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_horizontal) \n         && this->isctype(c, std::ctype<charT>::space) && !this->isctype(c, BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_vertical))\n         return true;\n#ifdef __CYGWIN__\n      //\n      // Cygwin has a buggy ctype facet, see https://www.cygwin.com/ml/cygwin/2012-08/msg00178.html:\n      //\n      else if((f & std::ctype<charT>::xdigit) == std::ctype<charT>::xdigit)\n      {\n         if((c >= 'a') && (c <= 'f'))\n            return true;\n         if((c >= 'A') && (c <= 'F'))\n            return true;\n      }\n#endif\n      return false;\n#else\n      return m_pimpl->isctype(c, f);\n#endif\n   }\n   boost::intmax_t toi(const charT*& p1, const charT* p2, int radix)const;\n   int value(charT c, int radix)const\n   {\n      const charT* pc = &c;\n      return (int)toi(pc, pc + 1, radix);\n   }\n   locale_type imbue(locale_type l)\n   {\n      std::locale result(getloc());\n      m_pimpl = BOOST_REGEX_DETAIL_NS::create_cpp_regex_traits<charT>(l);\n      return result;\n   }\n   locale_type getloc()const\n   {\n      return m_pimpl->m_locale;\n   }\n   std::string error_string(regex_constants::error_type n) const\n   {\n      return m_pimpl->error_string(n);\n   }\n\n   //\n   // extension:\n   // set the name of the message catalog in use (defaults to \"boost_regex\").\n   //\n   static std::string catalog_name(const std::string& name);\n   static std::string get_catalog_name();\n\nprivate:\n   boost::shared_ptr<const BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT> > m_pimpl;\n   //\n   // catalog name handler:\n   //\n   static std::string& get_catalog_name_inst();\n\n#ifdef BOOST_HAS_THREADS\n   static static_mutex& get_mutex_inst();\n#endif\n};\n\n\ntemplate <class charT>\nboost::intmax_t cpp_regex_traits<charT>::toi(const charT*& first, const charT* last, int radix)const\n{\n   BOOST_REGEX_DETAIL_NS::parser_buf<charT>   sbuf;            // buffer for parsing numbers.\n   std::basic_istream<charT>      is(&sbuf);       // stream for parsing numbers.\n\n   // we do NOT want to parse any thousands separators inside the stream:\n   last = std::find(first, last, BOOST_USE_FACET(std::numpunct<charT>, is.getloc()).thousands_sep());\n\n   sbuf.pubsetbuf(const_cast<charT*>(static_cast<const charT*>(first)), static_cast<std::streamsize>(last-first));\n   is.clear();\n   if(std::abs(radix) == 16) is >> std::hex;\n   else if(std::abs(radix) == 8) is >> std::oct;\n   else is >> std::dec;\n   boost::intmax_t val;\n   if(is >> val)\n   {\n      first = first + ((last - first) - sbuf.in_avail());\n      return val;\n   }\n   else\n      return -1;\n}\n\ntemplate <class charT>\nstd::string cpp_regex_traits<charT>::catalog_name(const std::string& name)\n{\n#ifdef BOOST_HAS_THREADS\n   static_mutex::scoped_lock lk(get_mutex_inst());\n#endif\n   std::string result(get_catalog_name_inst());\n   get_catalog_name_inst() = name;\n   return result;\n}\n\ntemplate <class charT>\nstd::string& cpp_regex_traits<charT>::get_catalog_name_inst()\n{\n   static std::string s_name;\n   return s_name;\n}\n\ntemplate <class charT>\nstd::string cpp_regex_traits<charT>::get_catalog_name()\n{\n#ifdef BOOST_HAS_THREADS\n   static_mutex::scoped_lock lk(get_mutex_inst());\n#endif\n   std::string result(get_catalog_name_inst());\n   return result;\n}\n\n#ifdef BOOST_HAS_THREADS\ntemplate <class charT>\nstatic_mutex& cpp_regex_traits<charT>::get_mutex_inst()\n{\n   static static_mutex s_mutex = BOOST_STATIC_MUTEX_INIT;\n   return s_mutex;\n}\n#endif\n\nnamespace BOOST_REGEX_DETAIL_NS {\n\n   inline void cpp_regex_traits_char_layer<char>::init()\n   {\n      // we need to start by initialising our syntax map so we know which\n      // character is used for which purpose:\n      std::memset(m_char_map, 0, sizeof(m_char_map));\n#ifndef BOOST_NO_STD_MESSAGES\n#ifndef __IBMCPP__\n      std::messages<char>::catalog cat = static_cast<std::messages<char>::catalog>(-1);\n#else\n      std::messages<char>::catalog cat = reinterpret_cast<std::messages<char>::catalog>(-1);\n#endif\n      std::string cat_name(cpp_regex_traits<char>::get_catalog_name());\n      if ((!cat_name.empty()) && (m_pmessages != 0))\n      {\n         cat = this->m_pmessages->open(\n            cat_name,\n            this->m_locale);\n         if ((int)cat < 0)\n         {\n            std::string m(\"Unable to open message catalog: \");\n            std::runtime_error err(m + cat_name);\n            boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n         }\n      }\n      //\n      // if we have a valid catalog then load our messages:\n      //\n      if ((int)cat >= 0)\n      {\n#ifndef BOOST_NO_EXCEPTIONS\n         try {\n#endif\n            for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n            {\n               string_type mss = this->m_pmessages->get(cat, 0, i, get_default_syntax(i));\n               for (string_type::size_type j = 0; j < mss.size(); ++j)\n               {\n                  m_char_map[static_cast<unsigned char>(mss[j])] = i;\n               }\n            }\n            this->m_pmessages->close(cat);\n#ifndef BOOST_NO_EXCEPTIONS\n         }\n         catch (...)\n         {\n            this->m_pmessages->close(cat);\n            throw;\n         }\n#endif\n      }\n      else\n      {\n#endif\n         for (regex_constants::syntax_type j = 1; j < regex_constants::syntax_max; ++j)\n         {\n            const char* ptr = get_default_syntax(j);\n            while (ptr && *ptr)\n            {\n               m_char_map[static_cast<unsigned char>(*ptr)] = j;\n               ++ptr;\n            }\n         }\n#ifndef BOOST_NO_STD_MESSAGES\n      }\n#endif\n      //\n      // finish off by calculating our escape types:\n      //\n      unsigned char i = 'A';\n      do\n      {\n         if (m_char_map[i] == 0)\n         {\n            if (this->m_pctype->is(std::ctype_base::lower, i))\n               m_char_map[i] = regex_constants::escape_type_class;\n            else if (this->m_pctype->is(std::ctype_base::upper, i))\n               m_char_map[i] = regex_constants::escape_type_not_class;\n         }\n      } while (0xFF != i++);\n   }\n\n} // namespace detail\n\n\n} // boost\n\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/cregex.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the\n * Boost Software License, Version 1.0. (See accompanying file\n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         cregex.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares POSIX API functions\n  *                + boost::RegEx high level wrapper.\n  */\n\n#ifndef BOOST_RE_CREGEX_HPP_INCLUDED\n#define BOOST_RE_CREGEX_HPP_INCLUDED\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n#include <boost/regex/v4/match_flags.hpp>\n#include <boost/regex/v4/error_type.hpp>\n\n#ifdef __cplusplus\n#include <cstddef>\n#else\n#include <stddef.h>\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n/* include these defs only for POSIX compatablity */\n#ifdef __cplusplus\nnamespace boost{\nextern \"C\" {\n#endif\n\n#if defined(__cplusplus) && !defined(BOOST_NO_STDC_NAMESPACE)\ntypedef std::ptrdiff_t regoff_t;\ntypedef std::size_t regsize_t;\n#else\ntypedef ptrdiff_t regoff_t;\ntypedef size_t regsize_t;\n#endif\n\ntypedef struct\n{\n   unsigned int re_magic;\n#ifdef __cplusplus\n   std::size_t  re_nsub;      /* number of parenthesized subexpressions */\n#else\n   size_t re_nsub; \n#endif\n   const char*  re_endp;       /* end pointer for REG_PEND */\n   void* guts;                /* none of your business :-) */\n   match_flag_type eflags;        /* none of your business :-) */\n} regex_tA;\n\n#ifndef BOOST_NO_WREGEX\ntypedef struct\n{\n   unsigned int re_magic;\n#ifdef __cplusplus\n   std::size_t  re_nsub;         /* number of parenthesized subexpressions */\n#else\n   size_t re_nsub;\n#endif\n   const wchar_t* re_endp;       /* end pointer for REG_PEND */\n   void* guts;                   /* none of your business :-) */\n   match_flag_type eflags;           /* none of your business :-) */\n} regex_tW;\n#endif\n\ntypedef struct\n{\n   regoff_t rm_so;      /* start of match */\n   regoff_t rm_eo;      /* end of match */\n} regmatch_t;\n\n/* regcomp() flags */\ntypedef enum{\n   REG_BASIC = 0000,\n   REG_EXTENDED = 0001,\n   REG_ICASE = 0002,\n   REG_NOSUB = 0004,\n   REG_NEWLINE = 0010,\n   REG_NOSPEC = 0020,\n   REG_PEND = 0040,\n   REG_DUMP = 0200,\n   REG_NOCOLLATE = 0400,\n   REG_ESCAPE_IN_LISTS = 01000,\n   REG_NEWLINE_ALT = 02000,\n   REG_PERLEX = 04000,\n\n   REG_PERL = REG_EXTENDED | REG_NOCOLLATE | REG_ESCAPE_IN_LISTS | REG_PERLEX,\n   REG_AWK = REG_EXTENDED | REG_ESCAPE_IN_LISTS,\n   REG_GREP = REG_BASIC | REG_NEWLINE_ALT,\n   REG_EGREP = REG_EXTENDED | REG_NEWLINE_ALT,\n\n   REG_ASSERT = 15,\n   REG_INVARG = 16,\n   REG_ATOI = 255,   /* convert name to number (!) */\n   REG_ITOA = 0400   /* convert number to name (!) */\n} reg_comp_flags;\n\n/* regexec() flags */\ntypedef enum{\n   REG_NOTBOL =    00001,\n   REG_NOTEOL =    00002,\n   REG_STARTEND =  00004\n} reg_exec_flags;\n\n/*\n * POSIX error codes:\n */\ntypedef unsigned reg_error_t;\ntypedef reg_error_t reg_errcode_t;  /* backwards compatibility */\n\nstatic const reg_error_t REG_NOERROR = 0;   /* Success.  */\nstatic const reg_error_t REG_NOMATCH = 1;   /* Didn't find a match (for regexec).  */\n\n  /* POSIX regcomp return error codes.  (In the order listed in the\n     standard.)  */\nstatic const reg_error_t REG_BADPAT = 2;    /* Invalid pattern.  */\nstatic const reg_error_t REG_ECOLLATE = 3;  /* Undefined collating element.  */\nstatic const reg_error_t REG_ECTYPE = 4;    /* Invalid character class name.  */\nstatic const reg_error_t REG_EESCAPE = 5;   /* Trailing backslash.  */\nstatic const reg_error_t REG_ESUBREG = 6;   /* Invalid back reference.  */\nstatic const reg_error_t REG_EBRACK = 7;    /* Unmatched left bracket.  */\nstatic const reg_error_t REG_EPAREN = 8;    /* Parenthesis imbalance.  */\nstatic const reg_error_t REG_EBRACE = 9;    /* Unmatched \\{.  */\nstatic const reg_error_t REG_BADBR = 10;    /* Invalid contents of \\{\\}.  */\nstatic const reg_error_t REG_ERANGE = 11;   /* Invalid range end.  */\nstatic const reg_error_t REG_ESPACE = 12;   /* Ran out of memory.  */\nstatic const reg_error_t REG_BADRPT = 13;   /* No preceding re for repetition op.  */\nstatic const reg_error_t REG_EEND = 14;     /* unexpected end of expression */\nstatic const reg_error_t REG_ESIZE = 15;    /* expression too big */\nstatic const reg_error_t REG_ERPAREN = 8;   /* = REG_EPAREN : unmatched right parenthesis */\nstatic const reg_error_t REG_EMPTY = 17;    /* empty expression */\nstatic const reg_error_t REG_E_MEMORY = 15; /* = REG_ESIZE : out of memory */\nstatic const reg_error_t REG_ECOMPLEXITY = 18; /* complexity too high */\nstatic const reg_error_t REG_ESTACK = 19;   /* out of stack space */\nstatic const reg_error_t REG_E_PERL = 20;   /* Perl (?...) error */\nstatic const reg_error_t REG_E_UNKNOWN = 21; /* unknown error */\nstatic const reg_error_t REG_ENOSYS = 21;   /* = REG_E_UNKNOWN : Reserved. */\n\nBOOST_REGEX_DECL int BOOST_REGEX_CCALL regcompA(regex_tA*, const char*, int);\nBOOST_REGEX_DECL regsize_t BOOST_REGEX_CCALL regerrorA(int, const regex_tA*, char*, regsize_t);\nBOOST_REGEX_DECL int BOOST_REGEX_CCALL regexecA(const regex_tA*, const char*, regsize_t, regmatch_t*, int);\nBOOST_REGEX_DECL void BOOST_REGEX_CCALL regfreeA(regex_tA*);\n\n#ifndef BOOST_NO_WREGEX\nBOOST_REGEX_DECL int BOOST_REGEX_CCALL regcompW(regex_tW*, const wchar_t*, int);\nBOOST_REGEX_DECL regsize_t BOOST_REGEX_CCALL regerrorW(int, const regex_tW*, wchar_t*, regsize_t);\nBOOST_REGEX_DECL int BOOST_REGEX_CCALL regexecW(const regex_tW*, const wchar_t*, regsize_t, regmatch_t*, int);\nBOOST_REGEX_DECL void BOOST_REGEX_CCALL regfreeW(regex_tW*);\n#endif\n\n#ifdef UNICODE\n#define regcomp regcompW\n#define regerror regerrorW\n#define regexec regexecW\n#define regfree regfreeW\n#define regex_t regex_tW\n#else\n#define regcomp regcompA\n#define regerror regerrorA\n#define regexec regexecA\n#define regfree regfreeA\n#define regex_t regex_tA\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef __cplusplus\n} /* extern \"C\" */\n} /* namespace */\n#endif\n\n#endif /* include guard */\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/error_type.hpp",
    "content": "/*\n *\n * Copyright (c) 2003-2005\n * John Maddock\n *\n * Use, modification and distribution are subject to the\n * Boost Software License, Version 1.0. (See accompanying file\n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         error_type.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression error type enumerator.\n  */\n\n#ifndef BOOST_REGEX_ERROR_TYPE_HPP\n#define BOOST_REGEX_ERROR_TYPE_HPP\n\n#ifdef __cplusplus\nnamespace boost{\n#endif\n\n#ifdef __cplusplus\nnamespace regex_constants{\n\nenum error_type{\n\n   error_ok = 0,         /* not used */\n   error_no_match = 1,   /* not used */\n   error_bad_pattern = 2,\n   error_collate = 3,\n   error_ctype = 4,\n   error_escape = 5,\n   error_backref = 6,\n   error_brack = 7,\n   error_paren = 8,\n   error_brace = 9,\n   error_badbrace = 10,\n   error_range = 11,\n   error_space = 12,\n   error_badrepeat = 13,\n   error_end = 14,    /* not used */\n   error_size = 15,\n   error_right_paren = 16,  /* not used */\n   error_empty = 17,\n   error_complexity = 18,\n   error_stack = 19,\n   error_perl_extension = 20,\n   error_unknown = 21\n};\n\n}\n}\n#endif /* __cplusplus */\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/icu.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the\n * Boost Software License, Version 1.0. (See accompanying file\n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         icu.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Unicode regular expressions on top of the ICU Library.\n  */\n\n#ifndef BOOST_REGEX_ICU_V4_HPP\n#define BOOST_REGEX_ICU_V4_HPP\n\n#include <boost/config.hpp>\n#include <unicode/utypes.h>\n#include <unicode/uchar.h>\n#include <unicode/coll.h>\n#include <boost/regex.hpp>\n#include <boost/regex/v4/unicode_iterator.hpp>\n#include <boost/mpl/int_fwd.hpp>\n#include <boost/static_assert.hpp>\n#include <bitset>\n\n#ifdef BOOST_MSVC\n#pragma warning (push)\n#pragma warning (disable: 4251)\n#endif\n\nnamespace boost {\n\n   namespace BOOST_REGEX_DETAIL_NS {\n\n      // \n      // Implementation details:\n      //\n      class icu_regex_traits_implementation\n      {\n         typedef UChar32                      char_type;\n         typedef std::size_t                  size_type;\n         typedef std::vector<char_type>       string_type;\n         typedef U_NAMESPACE_QUALIFIER Locale locale_type;\n         typedef boost::uint_least32_t        char_class_type;\n      public:\n         icu_regex_traits_implementation(const U_NAMESPACE_QUALIFIER Locale& l)\n            : m_locale(l)\n         {\n            UErrorCode success = U_ZERO_ERROR;\n            m_collator.reset(U_NAMESPACE_QUALIFIER Collator::createInstance(l, success));\n            if (U_SUCCESS(success) == 0)\n               init_error();\n            m_collator->setStrength(U_NAMESPACE_QUALIFIER Collator::IDENTICAL);\n            success = U_ZERO_ERROR;\n            m_primary_collator.reset(U_NAMESPACE_QUALIFIER Collator::createInstance(l, success));\n            if (U_SUCCESS(success) == 0)\n               init_error();\n            m_primary_collator->setStrength(U_NAMESPACE_QUALIFIER Collator::PRIMARY);\n         }\n         U_NAMESPACE_QUALIFIER Locale getloc()const\n         {\n            return m_locale;\n         }\n         string_type do_transform(const char_type* p1, const char_type* p2, const U_NAMESPACE_QUALIFIER Collator* pcoll) const\n         {\n            // TODO make thread safe!!!! :\n            typedef u32_to_u16_iterator<const char_type*, ::UChar> itt;\n            itt i(p1), j(p2);\n#ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS\n            std::vector< ::UChar> t(i, j);\n#else\n            std::vector< ::UChar> t;\n            while (i != j)\n               t.push_back(*i++);\n#endif\n            ::uint8_t result[100];\n            ::int32_t len;\n            if (!t.empty())\n               len = pcoll->getSortKey(&*t.begin(), static_cast< ::int32_t>(t.size()), result, sizeof(result));\n            else\n               len = pcoll->getSortKey(static_cast<UChar const*>(0), static_cast< ::int32_t>(0), result, sizeof(result));\n            if (std::size_t(len) > sizeof(result))\n            {\n               scoped_array< ::uint8_t> presult(new ::uint8_t[len + 1]);\n               if (!t.empty())\n                  len = pcoll->getSortKey(&*t.begin(), static_cast< ::int32_t>(t.size()), presult.get(), len + 1);\n               else\n                  len = pcoll->getSortKey(static_cast<UChar const*>(0), static_cast< ::int32_t>(0), presult.get(), len + 1);\n               if ((0 == presult[len - 1]) && (len > 1))\n                  --len;\n#ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS\n               return string_type(presult.get(), presult.get() + len);\n#else\n               string_type sresult;\n               ::uint8_t const* ia = presult.get();\n               ::uint8_t const* ib = presult.get() + len;\n               while (ia != ib)\n                  sresult.push_back(*ia++);\n               return sresult;\n#endif\n            }\n            if ((0 == result[len - 1]) && (len > 1))\n               --len;\n#ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS\n            return string_type(result, result + len);\n#else\n            string_type sresult;\n            ::uint8_t const* ia = result;\n            ::uint8_t const* ib = result + len;\n            while (ia != ib)\n               sresult.push_back(*ia++);\n            return sresult;\n#endif\n         }\n         string_type transform(const char_type* p1, const char_type* p2) const\n         {\n            return do_transform(p1, p2, m_collator.get());\n         }\n         string_type transform_primary(const char_type* p1, const char_type* p2) const\n         {\n            return do_transform(p1, p2, m_primary_collator.get());\n         }\n      private:\n         void init_error()\n         {\n            std::runtime_error e(\"Could not initialize ICU resources\");\n            boost::throw_exception(e);\n         }\n         U_NAMESPACE_QUALIFIER Locale m_locale;                                  // The ICU locale that we're using\n         boost::scoped_ptr< U_NAMESPACE_QUALIFIER Collator> m_collator;          // The full collation object\n         boost::scoped_ptr< U_NAMESPACE_QUALIFIER Collator> m_primary_collator;  // The primary collation object\n      };\n\n      inline boost::shared_ptr<icu_regex_traits_implementation> get_icu_regex_traits_implementation(const U_NAMESPACE_QUALIFIER Locale& loc)\n      {\n         return boost::shared_ptr<icu_regex_traits_implementation>(new icu_regex_traits_implementation(loc));\n      }\n\n   }\n\n   class icu_regex_traits\n   {\n   public:\n      typedef UChar32                      char_type;\n      typedef std::size_t                  size_type;\n      typedef std::vector<char_type>       string_type;\n      typedef U_NAMESPACE_QUALIFIER Locale locale_type;\n#ifdef BOOST_NO_INT64_T\n      typedef std::bitset<64>              char_class_type;\n#else\n      typedef boost::uint64_t              char_class_type;\n#endif\n\n      struct boost_extensions_tag {};\n\n      icu_regex_traits()\n         : m_pimpl(BOOST_REGEX_DETAIL_NS::get_icu_regex_traits_implementation(U_NAMESPACE_QUALIFIER Locale()))\n      {\n      }\n      static size_type length(const char_type* p)\n      {\n         size_type result = 0;\n         while (*p)\n         {\n            ++p;\n            ++result;\n         }\n         return result;\n      }\n      ::boost::regex_constants::syntax_type syntax_type(char_type c)const\n      {\n         return ((c < 0x7f) && (c > 0)) ? BOOST_REGEX_DETAIL_NS::get_default_syntax_type(static_cast<char>(c)) : regex_constants::syntax_char;\n      }\n      ::boost::regex_constants::escape_syntax_type escape_syntax_type(char_type c) const\n      {\n         return ((c < 0x7f) && (c > 0)) ? BOOST_REGEX_DETAIL_NS::get_default_escape_syntax_type(static_cast<char>(c)) : regex_constants::syntax_char;\n      }\n      char_type translate(char_type c) const\n      {\n         return c;\n      }\n      char_type translate_nocase(char_type c) const\n      {\n         return ::u_foldCase(c, U_FOLD_CASE_DEFAULT);\n      }\n      char_type translate(char_type c, bool icase) const\n      {\n         return icase ? translate_nocase(c) : translate(c);\n      }\n      char_type tolower(char_type c) const\n      {\n         return ::u_tolower(c);\n      }\n      char_type toupper(char_type c) const\n      {\n         return ::u_toupper(c);\n      }\n      string_type transform(const char_type* p1, const char_type* p2) const\n      {\n         return m_pimpl->transform(p1, p2);\n      }\n      string_type transform_primary(const char_type* p1, const char_type* p2) const\n      {\n         return m_pimpl->transform_primary(p1, p2);\n      }\n      char_class_type lookup_classname(const char_type* p1, const char_type* p2) const\n      {\n         static const char_class_type mask_blank = char_class_type(1) << offset_blank;\n         static const char_class_type mask_space = char_class_type(1) << offset_space;\n         static const char_class_type mask_xdigit = char_class_type(1) << offset_xdigit;\n         static const char_class_type mask_underscore = char_class_type(1) << offset_underscore;\n         static const char_class_type mask_unicode = char_class_type(1) << offset_unicode;\n         static const char_class_type mask_any = char_class_type(1) << offset_any;\n         static const char_class_type mask_ascii = char_class_type(1) << offset_ascii;\n         static const char_class_type mask_horizontal = char_class_type(1) << offset_horizontal;\n         static const char_class_type mask_vertical = char_class_type(1) << offset_vertical;\n\n         static const char_class_type masks[] =\n         {\n            0,\n            U_GC_L_MASK | U_GC_ND_MASK,\n            U_GC_L_MASK,\n            mask_blank,\n            U_GC_CC_MASK | U_GC_CF_MASK | U_GC_ZL_MASK | U_GC_ZP_MASK,\n            U_GC_ND_MASK,\n            U_GC_ND_MASK,\n            (0x3FFFFFFFu) & ~(U_GC_CC_MASK | U_GC_CF_MASK | U_GC_CS_MASK | U_GC_CN_MASK | U_GC_Z_MASK),\n            mask_horizontal,\n            U_GC_LL_MASK,\n            U_GC_LL_MASK,\n            ~(U_GC_C_MASK),\n            U_GC_P_MASK,\n            char_class_type(U_GC_Z_MASK) | mask_space,\n            char_class_type(U_GC_Z_MASK) | mask_space,\n            U_GC_LU_MASK,\n            mask_unicode,\n            U_GC_LU_MASK,\n            mask_vertical,\n            char_class_type(U_GC_L_MASK | U_GC_ND_MASK | U_GC_MN_MASK) | mask_underscore,\n            char_class_type(U_GC_L_MASK | U_GC_ND_MASK | U_GC_MN_MASK) | mask_underscore,\n            char_class_type(U_GC_ND_MASK) | mask_xdigit,\n         };\n\n         int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2);\n         if (idx >= 0)\n            return masks[idx + 1];\n         char_class_type result = lookup_icu_mask(p1, p2);\n         if (result != 0)\n            return result;\n\n         if (idx < 0)\n         {\n            string_type s(p1, p2);\n            string_type::size_type i = 0;\n            while (i < s.size())\n            {\n               s[i] = static_cast<char>((::u_tolower)(s[i]));\n               if (::u_isspace(s[i]) || (s[i] == '-') || (s[i] == '_'))\n                  s.erase(s.begin() + i, s.begin() + i + 1);\n               else\n               {\n                  s[i] = static_cast<char>((::u_tolower)(s[i]));\n                  ++i;\n               }\n            }\n            if (!s.empty())\n               idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size());\n            if (idx >= 0)\n               return masks[idx + 1];\n            if (!s.empty())\n               result = lookup_icu_mask(&*s.begin(), &*s.begin() + s.size());\n            if (result != 0)\n               return result;\n         }\n         BOOST_ASSERT(std::size_t(idx + 1) < sizeof(masks) / sizeof(masks[0]));\n         return masks[idx + 1];\n      }\n      string_type lookup_collatename(const char_type* p1, const char_type* p2) const\n      {\n         string_type result;\n#ifdef BOOST_NO_CXX98_BINDERS\n         if (std::find_if(p1, p2, std::bind(std::greater< ::UChar32>(), std::placeholders::_1, 0x7f)) == p2)\n#else\n         if (std::find_if(p1, p2, std::bind2nd(std::greater< ::UChar32>(), 0x7f)) == p2)\n#endif\n         {\n#ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS\n            std::string s(p1, p2);\n#else\n            std::string s;\n            const char_type* p3 = p1;\n            while (p3 != p2)\n               s.append(1, *p3++);\n#endif\n            // Try Unicode name:\n            UErrorCode err = U_ZERO_ERROR;\n            UChar32 c = ::u_charFromName(U_UNICODE_CHAR_NAME, s.c_str(), &err);\n            if (U_SUCCESS(err))\n            {\n               result.push_back(c);\n               return result;\n            }\n            // Try Unicode-extended name:\n            err = U_ZERO_ERROR;\n            c = ::u_charFromName(U_EXTENDED_CHAR_NAME, s.c_str(), &err);\n            if (U_SUCCESS(err))\n            {\n               result.push_back(c);\n               return result;\n            }\n            // try POSIX name:\n            s = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(s);\n#ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS\n            result.assign(s.begin(), s.end());\n#else\n            result.clear();\n            std::string::const_iterator si, sj;\n            si = s.begin();\n            sj = s.end();\n            while (si != sj)\n               result.push_back(*si++);\n#endif\n         }\n         if (result.empty() && (p2 - p1 == 1))\n            result.push_back(*p1);\n         return result;\n      }\n      bool isctype(char_type c, char_class_type f) const\n      {\n         static const char_class_type mask_blank = char_class_type(1) << offset_blank;\n         static const char_class_type mask_space = char_class_type(1) << offset_space;\n         static const char_class_type mask_xdigit = char_class_type(1) << offset_xdigit;\n         static const char_class_type mask_underscore = char_class_type(1) << offset_underscore;\n         static const char_class_type mask_unicode = char_class_type(1) << offset_unicode;\n         static const char_class_type mask_any = char_class_type(1) << offset_any;\n         static const char_class_type mask_ascii = char_class_type(1) << offset_ascii;\n         static const char_class_type mask_horizontal = char_class_type(1) << offset_horizontal;\n         static const char_class_type mask_vertical = char_class_type(1) << offset_vertical;\n\n         // check for standard catagories first:\n         char_class_type m = char_class_type(static_cast<char_class_type>(1) << u_charType(c));\n         if ((m & f) != 0)\n            return true;\n         // now check for special cases:\n         if (((f & mask_blank) != 0) && u_isblank(c))\n            return true;\n         if (((f & mask_space) != 0) && u_isspace(c))\n            return true;\n         if (((f & mask_xdigit) != 0) && (u_digit(c, 16) >= 0))\n            return true;\n         if (((f & mask_unicode) != 0) && (c >= 0x100))\n            return true;\n         if (((f & mask_underscore) != 0) && (c == '_'))\n            return true;\n         if (((f & mask_any) != 0) && (c <= 0x10FFFF))\n            return true;\n         if (((f & mask_ascii) != 0) && (c <= 0x7F))\n            return true;\n         if (((f & mask_vertical) != 0) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == static_cast<char_type>('\\v')) || (m == U_GC_ZL_MASK) || (m == U_GC_ZP_MASK)))\n            return true;\n         if (((f & mask_horizontal) != 0) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && u_isspace(c) && (c != static_cast<char_type>('\\v')))\n            return true;\n         return false;\n      }\n      boost::intmax_t toi(const char_type*& p1, const char_type* p2, int radix)const\n      {\n         return BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this);\n      }\n      int value(char_type c, int radix)const\n      {\n         return u_digit(c, static_cast< ::int8_t>(radix));\n      }\n      locale_type imbue(locale_type l)\n      {\n         locale_type result(m_pimpl->getloc());\n         m_pimpl = BOOST_REGEX_DETAIL_NS::get_icu_regex_traits_implementation(l);\n         return result;\n      }\n      locale_type getloc()const\n      {\n         return locale_type();\n      }\n      std::string error_string(::boost::regex_constants::error_type n) const\n      {\n         return BOOST_REGEX_DETAIL_NS::get_default_error_string(n);\n      }\n   private:\n      icu_regex_traits(const icu_regex_traits&);\n      icu_regex_traits& operator=(const icu_regex_traits&);\n\n      //\n      // define the bitmasks offsets we need for additional character properties:\n      //\n      enum {\n         offset_blank = U_CHAR_CATEGORY_COUNT,\n         offset_space = U_CHAR_CATEGORY_COUNT + 1,\n         offset_xdigit = U_CHAR_CATEGORY_COUNT + 2,\n         offset_underscore = U_CHAR_CATEGORY_COUNT + 3,\n         offset_unicode = U_CHAR_CATEGORY_COUNT + 4,\n         offset_any = U_CHAR_CATEGORY_COUNT + 5,\n         offset_ascii = U_CHAR_CATEGORY_COUNT + 6,\n         offset_horizontal = U_CHAR_CATEGORY_COUNT + 7,\n         offset_vertical = U_CHAR_CATEGORY_COUNT + 8\n      };\n\n      static char_class_type lookup_icu_mask(const ::UChar32* p1, const ::UChar32* p2)\n      {\n         static const char_class_type mask_blank = char_class_type(1) << offset_blank;\n         static const char_class_type mask_space = char_class_type(1) << offset_space;\n         static const char_class_type mask_xdigit = char_class_type(1) << offset_xdigit;\n         static const char_class_type mask_underscore = char_class_type(1) << offset_underscore;\n         static const char_class_type mask_unicode = char_class_type(1) << offset_unicode;\n         static const char_class_type mask_any = char_class_type(1) << offset_any;\n         static const char_class_type mask_ascii = char_class_type(1) << offset_ascii;\n         static const char_class_type mask_horizontal = char_class_type(1) << offset_horizontal;\n         static const char_class_type mask_vertical = char_class_type(1) << offset_vertical;\n\n         static const ::UChar32 prop_name_table[] = {\n            /* any */  'a', 'n', 'y',\n            /* ascii */  'a', 's', 'c', 'i', 'i',\n            /* assigned */  'a', 's', 's', 'i', 'g', 'n', 'e', 'd',\n            /* c* */  'c', '*',\n            /* cc */  'c', 'c',\n            /* cf */  'c', 'f',\n            /* closepunctuation */  'c', 'l', 'o', 's', 'e', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n            /* cn */  'c', 'n',\n            /* co */  'c', 'o',\n            /* connectorpunctuation */  'c', 'o', 'n', 'n', 'e', 'c', 't', 'o', 'r', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n            /* control */  'c', 'o', 'n', 't', 'r', 'o', 'l',\n            /* cs */  'c', 's',\n            /* currencysymbol */  'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 's', 'y', 'm', 'b', 'o', 'l',\n            /* dashpunctuation */  'd', 'a', 's', 'h', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n            /* decimaldigitnumber */  'd', 'e', 'c', 'i', 'm', 'a', 'l', 'd', 'i', 'g', 'i', 't', 'n', 'u', 'm', 'b', 'e', 'r',\n            /* enclosingmark */  'e', 'n', 'c', 'l', 'o', 's', 'i', 'n', 'g', 'm', 'a', 'r', 'k',\n            /* finalpunctuation */  'f', 'i', 'n', 'a', 'l', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n            /* format */  'f', 'o', 'r', 'm', 'a', 't',\n            /* initialpunctuation */  'i', 'n', 'i', 't', 'i', 'a', 'l', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n            /* l* */  'l', '*',\n            /* letter */  'l', 'e', 't', 't', 'e', 'r',\n            /* letternumber */  'l', 'e', 't', 't', 'e', 'r', 'n', 'u', 'm', 'b', 'e', 'r',\n            /* lineseparator */  'l', 'i', 'n', 'e', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r',\n            /* ll */  'l', 'l',\n            /* lm */  'l', 'm',\n            /* lo */  'l', 'o',\n            /* lowercaseletter */  'l', 'o', 'w', 'e', 'r', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r',\n            /* lt */  'l', 't',\n            /* lu */  'l', 'u',\n            /* m* */  'm', '*',\n            /* mark */  'm', 'a', 'r', 'k',\n            /* mathsymbol */  'm', 'a', 't', 'h', 's', 'y', 'm', 'b', 'o', 'l',\n            /* mc */  'm', 'c',\n            /* me */  'm', 'e',\n            /* mn */  'm', 'n',\n            /* modifierletter */  'm', 'o', 'd', 'i', 'f', 'i', 'e', 'r', 'l', 'e', 't', 't', 'e', 'r',\n            /* modifiersymbol */  'm', 'o', 'd', 'i', 'f', 'i', 'e', 'r', 's', 'y', 'm', 'b', 'o', 'l',\n            /* n* */  'n', '*',\n            /* nd */  'n', 'd',\n            /* nl */  'n', 'l',\n            /* no */  'n', 'o',\n            /* nonspacingmark */  'n', 'o', 'n', 's', 'p', 'a', 'c', 'i', 'n', 'g', 'm', 'a', 'r', 'k',\n            /* notassigned */  'n', 'o', 't', 'a', 's', 's', 'i', 'g', 'n', 'e', 'd',\n            /* number */  'n', 'u', 'm', 'b', 'e', 'r',\n            /* openpunctuation */  'o', 'p', 'e', 'n', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n            /* other */  'o', 't', 'h', 'e', 'r',\n            /* otherletter */  'o', 't', 'h', 'e', 'r', 'l', 'e', 't', 't', 'e', 'r',\n            /* othernumber */  'o', 't', 'h', 'e', 'r', 'n', 'u', 'm', 'b', 'e', 'r',\n            /* otherpunctuation */  'o', 't', 'h', 'e', 'r', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n            /* othersymbol */  'o', 't', 'h', 'e', 'r', 's', 'y', 'm', 'b', 'o', 'l',\n            /* p* */  'p', '*',\n            /* paragraphseparator */  'p', 'a', 'r', 'a', 'g', 'r', 'a', 'p', 'h', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r',\n            /* pc */  'p', 'c',\n            /* pd */  'p', 'd',\n            /* pe */  'p', 'e',\n            /* pf */  'p', 'f',\n            /* pi */  'p', 'i',\n            /* po */  'p', 'o',\n            /* privateuse */  'p', 'r', 'i', 'v', 'a', 't', 'e', 'u', 's', 'e',\n            /* ps */  'p', 's',\n            /* punctuation */  'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n            /* s* */  's', '*',\n            /* sc */  's', 'c',\n            /* separator */  's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r',\n            /* sk */  's', 'k',\n            /* sm */  's', 'm',\n            /* so */  's', 'o',\n            /* spaceseparator */  's', 'p', 'a', 'c', 'e', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r',\n            /* spacingcombiningmark */  's', 'p', 'a', 'c', 'i', 'n', 'g', 'c', 'o', 'm', 'b', 'i', 'n', 'i', 'n', 'g', 'm', 'a', 'r', 'k',\n            /* surrogate */  's', 'u', 'r', 'r', 'o', 'g', 'a', 't', 'e',\n            /* symbol */  's', 'y', 'm', 'b', 'o', 'l',\n            /* titlecase */  't', 'i', 't', 'l', 'e', 'c', 'a', 's', 'e',\n            /* titlecaseletter */  't', 'i', 't', 'l', 'e', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r',\n            /* uppercaseletter */  'u', 'p', 'p', 'e', 'r', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r',\n            /* z* */  'z', '*',\n            /* zl */  'z', 'l',\n            /* zp */  'z', 'p',\n            /* zs */  'z', 's',\n         };\n\n         static const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32> range_data[] = {\n            { prop_name_table + 0, prop_name_table + 3, }, // any\n            { prop_name_table + 3, prop_name_table + 8, }, // ascii\n            { prop_name_table + 8, prop_name_table + 16, }, // assigned\n            { prop_name_table + 16, prop_name_table + 18, }, // c*\n            { prop_name_table + 18, prop_name_table + 20, }, // cc\n            { prop_name_table + 20, prop_name_table + 22, }, // cf\n            { prop_name_table + 22, prop_name_table + 38, }, // closepunctuation\n            { prop_name_table + 38, prop_name_table + 40, }, // cn\n            { prop_name_table + 40, prop_name_table + 42, }, // co\n            { prop_name_table + 42, prop_name_table + 62, }, // connectorpunctuation\n            { prop_name_table + 62, prop_name_table + 69, }, // control\n            { prop_name_table + 69, prop_name_table + 71, }, // cs\n            { prop_name_table + 71, prop_name_table + 85, }, // currencysymbol\n            { prop_name_table + 85, prop_name_table + 100, }, // dashpunctuation\n            { prop_name_table + 100, prop_name_table + 118, }, // decimaldigitnumber\n            { prop_name_table + 118, prop_name_table + 131, }, // enclosingmark\n            { prop_name_table + 131, prop_name_table + 147, }, // finalpunctuation\n            { prop_name_table + 147, prop_name_table + 153, }, // format\n            { prop_name_table + 153, prop_name_table + 171, }, // initialpunctuation\n            { prop_name_table + 171, prop_name_table + 173, }, // l*\n            { prop_name_table + 173, prop_name_table + 179, }, // letter\n            { prop_name_table + 179, prop_name_table + 191, }, // letternumber\n            { prop_name_table + 191, prop_name_table + 204, }, // lineseparator\n            { prop_name_table + 204, prop_name_table + 206, }, // ll\n            { prop_name_table + 206, prop_name_table + 208, }, // lm\n            { prop_name_table + 208, prop_name_table + 210, }, // lo\n            { prop_name_table + 210, prop_name_table + 225, }, // lowercaseletter\n            { prop_name_table + 225, prop_name_table + 227, }, // lt\n            { prop_name_table + 227, prop_name_table + 229, }, // lu\n            { prop_name_table + 229, prop_name_table + 231, }, // m*\n            { prop_name_table + 231, prop_name_table + 235, }, // mark\n            { prop_name_table + 235, prop_name_table + 245, }, // mathsymbol\n            { prop_name_table + 245, prop_name_table + 247, }, // mc\n            { prop_name_table + 247, prop_name_table + 249, }, // me\n            { prop_name_table + 249, prop_name_table + 251, }, // mn\n            { prop_name_table + 251, prop_name_table + 265, }, // modifierletter\n            { prop_name_table + 265, prop_name_table + 279, }, // modifiersymbol\n            { prop_name_table + 279, prop_name_table + 281, }, // n*\n            { prop_name_table + 281, prop_name_table + 283, }, // nd\n            { prop_name_table + 283, prop_name_table + 285, }, // nl\n            { prop_name_table + 285, prop_name_table + 287, }, // no\n            { prop_name_table + 287, prop_name_table + 301, }, // nonspacingmark\n            { prop_name_table + 301, prop_name_table + 312, }, // notassigned\n            { prop_name_table + 312, prop_name_table + 318, }, // number\n            { prop_name_table + 318, prop_name_table + 333, }, // openpunctuation\n            { prop_name_table + 333, prop_name_table + 338, }, // other\n            { prop_name_table + 338, prop_name_table + 349, }, // otherletter\n            { prop_name_table + 349, prop_name_table + 360, }, // othernumber\n            { prop_name_table + 360, prop_name_table + 376, }, // otherpunctuation\n            { prop_name_table + 376, prop_name_table + 387, }, // othersymbol\n            { prop_name_table + 387, prop_name_table + 389, }, // p*\n            { prop_name_table + 389, prop_name_table + 407, }, // paragraphseparator\n            { prop_name_table + 407, prop_name_table + 409, }, // pc\n            { prop_name_table + 409, prop_name_table + 411, }, // pd\n            { prop_name_table + 411, prop_name_table + 413, }, // pe\n            { prop_name_table + 413, prop_name_table + 415, }, // pf\n            { prop_name_table + 415, prop_name_table + 417, }, // pi\n            { prop_name_table + 417, prop_name_table + 419, }, // po\n            { prop_name_table + 419, prop_name_table + 429, }, // privateuse\n            { prop_name_table + 429, prop_name_table + 431, }, // ps\n            { prop_name_table + 431, prop_name_table + 442, }, // punctuation\n            { prop_name_table + 442, prop_name_table + 444, }, // s*\n            { prop_name_table + 444, prop_name_table + 446, }, // sc\n            { prop_name_table + 446, prop_name_table + 455, }, // separator\n            { prop_name_table + 455, prop_name_table + 457, }, // sk\n            { prop_name_table + 457, prop_name_table + 459, }, // sm\n            { prop_name_table + 459, prop_name_table + 461, }, // so\n            { prop_name_table + 461, prop_name_table + 475, }, // spaceseparator\n            { prop_name_table + 475, prop_name_table + 495, }, // spacingcombiningmark\n            { prop_name_table + 495, prop_name_table + 504, }, // surrogate\n            { prop_name_table + 504, prop_name_table + 510, }, // symbol\n            { prop_name_table + 510, prop_name_table + 519, }, // titlecase\n            { prop_name_table + 519, prop_name_table + 534, }, // titlecaseletter\n            { prop_name_table + 534, prop_name_table + 549, }, // uppercaseletter\n            { prop_name_table + 549, prop_name_table + 551, }, // z*\n            { prop_name_table + 551, prop_name_table + 553, }, // zl\n            { prop_name_table + 553, prop_name_table + 555, }, // zp\n            { prop_name_table + 555, prop_name_table + 557, }, // zs\n         };\n\n         static const icu_regex_traits::char_class_type icu_class_map[] = {\n            mask_any, // any\n            mask_ascii, // ascii\n            (0x3FFFFFFFu) & ~(U_GC_CN_MASK), // assigned\n            U_GC_C_MASK, // c*\n            U_GC_CC_MASK, // cc\n            U_GC_CF_MASK, // cf\n            U_GC_PE_MASK, // closepunctuation\n            U_GC_CN_MASK, // cn\n            U_GC_CO_MASK, // co\n            U_GC_PC_MASK, // connectorpunctuation\n            U_GC_CC_MASK, // control\n            U_GC_CS_MASK, // cs\n            U_GC_SC_MASK, // currencysymbol\n            U_GC_PD_MASK, // dashpunctuation\n            U_GC_ND_MASK, // decimaldigitnumber\n            U_GC_ME_MASK, // enclosingmark\n            U_GC_PF_MASK, // finalpunctuation\n            U_GC_CF_MASK, // format\n            U_GC_PI_MASK, // initialpunctuation\n            U_GC_L_MASK, // l*\n            U_GC_L_MASK, // letter\n            U_GC_NL_MASK, // letternumber\n            U_GC_ZL_MASK, // lineseparator\n            U_GC_LL_MASK, // ll\n            U_GC_LM_MASK, // lm\n            U_GC_LO_MASK, // lo\n            U_GC_LL_MASK, // lowercaseletter\n            U_GC_LT_MASK, // lt\n            U_GC_LU_MASK, // lu\n            U_GC_M_MASK, // m*\n            U_GC_M_MASK, // mark\n            U_GC_SM_MASK, // mathsymbol\n            U_GC_MC_MASK, // mc\n            U_GC_ME_MASK, // me\n            U_GC_MN_MASK, // mn\n            U_GC_LM_MASK, // modifierletter\n            U_GC_SK_MASK, // modifiersymbol\n            U_GC_N_MASK, // n*\n            U_GC_ND_MASK, // nd\n            U_GC_NL_MASK, // nl\n            U_GC_NO_MASK, // no\n            U_GC_MN_MASK, // nonspacingmark\n            U_GC_CN_MASK, // notassigned\n            U_GC_N_MASK, // number\n            U_GC_PS_MASK, // openpunctuation\n            U_GC_C_MASK, // other\n            U_GC_LO_MASK, // otherletter\n            U_GC_NO_MASK, // othernumber\n            U_GC_PO_MASK, // otherpunctuation\n            U_GC_SO_MASK, // othersymbol\n            U_GC_P_MASK, // p*\n            U_GC_ZP_MASK, // paragraphseparator\n            U_GC_PC_MASK, // pc\n            U_GC_PD_MASK, // pd\n            U_GC_PE_MASK, // pe\n            U_GC_PF_MASK, // pf\n            U_GC_PI_MASK, // pi\n            U_GC_PO_MASK, // po\n            U_GC_CO_MASK, // privateuse\n            U_GC_PS_MASK, // ps\n            U_GC_P_MASK, // punctuation\n            U_GC_S_MASK, // s*\n            U_GC_SC_MASK, // sc\n            U_GC_Z_MASK, // separator\n            U_GC_SK_MASK, // sk\n            U_GC_SM_MASK, // sm\n            U_GC_SO_MASK, // so\n            U_GC_ZS_MASK, // spaceseparator\n            U_GC_MC_MASK, // spacingcombiningmark\n            U_GC_CS_MASK, // surrogate\n            U_GC_S_MASK, // symbol\n            U_GC_LT_MASK, // titlecase\n            U_GC_LT_MASK, // titlecaseletter\n            U_GC_LU_MASK, // uppercaseletter\n            U_GC_Z_MASK, // z*\n            U_GC_ZL_MASK, // zl\n            U_GC_ZP_MASK, // zp\n            U_GC_ZS_MASK, // zs\n         };\n\n\n         const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* ranges_begin = range_data;\n         const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* ranges_end = range_data + (sizeof(range_data) / sizeof(range_data[0]));\n\n         BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32> t = { p1, p2, };\n         const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* p = std::lower_bound(ranges_begin, ranges_end, t);\n         if ((p != ranges_end) && (t == *p))\n            return icu_class_map[p - ranges_begin];\n         return 0;\n      }\n\n      boost::shared_ptr< ::boost::BOOST_REGEX_DETAIL_NS::icu_regex_traits_implementation> m_pimpl;\n   };\n\n} // namespace boost\n\nnamespace boost {\n\n   // types:\n   typedef basic_regex< ::UChar32, icu_regex_traits> u32regex;\n   typedef match_results<const ::UChar32*> u32match;\n   typedef match_results<const ::UChar*> u16match;\n\n   //\n   // Construction of 32-bit regex types from UTF-8 and UTF-16 primitives:\n   //\n   namespace BOOST_REGEX_DETAIL_NS {\n\n#if !defined(BOOST_NO_MEMBER_TEMPLATES) && !defined(__IBMCPP__)\n      template <class InputIterator>\n      inline u32regex do_make_u32regex(InputIterator i,\n         InputIterator j,\n         boost::regex_constants::syntax_option_type opt,\n         const boost::mpl::int_<1>*)\n      {\n         typedef boost::u8_to_u32_iterator<InputIterator, UChar32> conv_type;\n         return u32regex(conv_type(i, i, j), conv_type(j, i, j), opt);\n      }\n\n      template <class InputIterator>\n      inline u32regex do_make_u32regex(InputIterator i,\n         InputIterator j,\n         boost::regex_constants::syntax_option_type opt,\n         const boost::mpl::int_<2>*)\n      {\n         typedef boost::u16_to_u32_iterator<InputIterator, UChar32> conv_type;\n         return u32regex(conv_type(i, i, j), conv_type(j, i, j), opt);\n      }\n\n      template <class InputIterator>\n      inline u32regex do_make_u32regex(InputIterator i,\n         InputIterator j,\n         boost::regex_constants::syntax_option_type opt,\n         const boost::mpl::int_<4>*)\n      {\n         return u32regex(i, j, opt);\n      }\n#else\n      template <class InputIterator>\n      inline u32regex do_make_u32regex(InputIterator i,\n         InputIterator j,\n         boost::regex_constants::syntax_option_type opt,\n         const boost::mpl::int_<1>*)\n      {\n         typedef boost::u8_to_u32_iterator<InputIterator, UChar32> conv_type;\n         typedef std::vector<UChar32> vector_type;\n         vector_type v;\n         conv_type a(i, i, j), b(j, i, j);\n         while (a != b)\n         {\n            v.push_back(*a);\n            ++a;\n         }\n         if (v.size())\n            return u32regex(&*v.begin(), v.size(), opt);\n         return u32regex(static_cast<UChar32 const*>(0), static_cast<u32regex::size_type>(0), opt);\n      }\n\n      template <class InputIterator>\n      inline u32regex do_make_u32regex(InputIterator i,\n         InputIterator j,\n         boost::regex_constants::syntax_option_type opt,\n         const boost::mpl::int_<2>*)\n      {\n         typedef boost::u16_to_u32_iterator<InputIterator, UChar32> conv_type;\n         typedef std::vector<UChar32> vector_type;\n         vector_type v;\n         conv_type a(i, i, j), b(j, i, j);\n         while (a != b)\n         {\n            v.push_back(*a);\n            ++a;\n         }\n         if (v.size())\n            return u32regex(&*v.begin(), v.size(), opt);\n         return u32regex(static_cast<UChar32 const*>(0), static_cast<u32regex::size_type>(0), opt);\n      }\n\n      template <class InputIterator>\n      inline u32regex do_make_u32regex(InputIterator i,\n         InputIterator j,\n         boost::regex_constants::syntax_option_type opt,\n         const boost::mpl::int_<4>*)\n      {\n         typedef std::vector<UChar32> vector_type;\n         vector_type v;\n         while (i != j)\n         {\n            v.push_back((UChar32)(*i));\n            ++i;\n         }\n         if (v.size())\n            return u32regex(&*v.begin(), v.size(), opt);\n         return u32regex(static_cast<UChar32 const*>(0), static_cast<u32regex::size_type>(0), opt);\n      }\n#endif\n   }\n\n   // BOOST_REGEX_UCHAR_IS_WCHAR_T\n   //\n   // Source inspection of unicode/umachine.h in ICU version 59 indicates that:\n   //\n   // On version 59, UChar is always char16_t in C++ mode (and uint16_t in C mode)\n   //\n   // On earlier versions, the logic is\n   //\n   // #if U_SIZEOF_WCHAR_T==2\n   //   typedef wchar_t OldUChar;\n   // #elif defined(__CHAR16_TYPE__)\n   //   typedef __CHAR16_TYPE__ OldUChar;\n   // #else\n   //   typedef uint16_t OldUChar;\n   // #endif\n   //\n   // That is, UChar is wchar_t only on versions below 59, when U_SIZEOF_WCHAR_T==2\n   //\n   // Hence,\n\n#define BOOST_REGEX_UCHAR_IS_WCHAR_T (U_ICU_VERSION_MAJOR_NUM < 59 && U_SIZEOF_WCHAR_T == 2)\n\n#if BOOST_REGEX_UCHAR_IS_WCHAR_T\n   BOOST_STATIC_ASSERT((boost::is_same<UChar, wchar_t>::value));\n#else\n   BOOST_STATIC_ASSERT(!(boost::is_same<UChar, wchar_t>::value));\n#endif\n\n   //\n   // Construction from an iterator pair:\n   //\n   template <class InputIterator>\n   inline u32regex make_u32regex(InputIterator i,\n      InputIterator j,\n      boost::regex_constants::syntax_option_type opt)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_make_u32regex(i, j, opt, static_cast<boost::mpl::int_<sizeof(*i)> const*>(0));\n   }\n   //\n   // construction from UTF-8 nul-terminated strings:\n   //\n   inline u32regex make_u32regex(const char* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::strlen(p), opt, static_cast<boost::mpl::int_<1> const*>(0));\n   }\n   inline u32regex make_u32regex(const unsigned char* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::strlen(reinterpret_cast<const char*>(p)), opt, static_cast<boost::mpl::int_<1> const*>(0));\n   }\n   //\n   // construction from UTF-16 nul-terminated strings:\n   //\n#ifndef BOOST_NO_WREGEX\n   inline u32regex make_u32regex(const wchar_t* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::wcslen(p), opt, static_cast<boost::mpl::int_<sizeof(wchar_t)> const*>(0));\n   }\n#endif\n#if !BOOST_REGEX_UCHAR_IS_WCHAR_T\n   inline u32regex make_u32regex(const UChar* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + u_strlen(p), opt, static_cast<boost::mpl::int_<2> const*>(0));\n   }\n#endif\n   //\n   // construction from basic_string class-template:\n   //\n   template<class C, class T, class A>\n   inline u32regex make_u32regex(const std::basic_string<C, T, A>& s, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_make_u32regex(s.begin(), s.end(), opt, static_cast<boost::mpl::int_<sizeof(C)> const*>(0));\n   }\n   //\n   // Construction from ICU string type:\n   //\n   inline u32regex make_u32regex(const U_NAMESPACE_QUALIFIER UnicodeString& s, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_make_u32regex(s.getBuffer(), s.getBuffer() + s.length(), opt, static_cast<boost::mpl::int_<2> const*>(0));\n   }\n\n   //\n   // regex_match overloads that widen the character type as appropriate:\n   //\n   namespace BOOST_REGEX_DETAIL_NS {\n      template<class MR1, class MR2, class NSubs>\n      void copy_results(MR1& out, MR2 const& in, NSubs named_subs)\n      {\n         // copy results from an adapted MR2 match_results:\n         out.set_size(in.size(), in.prefix().first.base(), in.suffix().second.base());\n         out.set_base(in.base().base());\n         out.set_named_subs(named_subs);\n         for (int i = 0; i < (int)in.size(); ++i)\n         {\n            if (in[i].matched || !i)\n            {\n               out.set_first(in[i].first.base(), i);\n               out.set_second(in[i].second.base(), i, in[i].matched);\n            }\n         }\n#ifdef BOOST_REGEX_MATCH_EXTRA\n         // Copy full capture info as well:\n         for (int i = 0; i < (int)in.size(); ++i)\n         {\n            if (in[i].captures().size())\n            {\n               out[i].get_captures().assign(in[i].captures().size(), typename MR1::value_type());\n               for (int j = 0; j < (int)out[i].captures().size(); ++j)\n               {\n                  out[i].get_captures()[j].first = in[i].captures()[j].first.base();\n                  out[i].get_captures()[j].second = in[i].captures()[j].second.base();\n                  out[i].get_captures()[j].matched = in[i].captures()[j].matched;\n               }\n            }\n         }\n#endif\n      }\n\n      template <class BidiIterator, class Allocator>\n      inline bool do_regex_match(BidiIterator first, BidiIterator last,\n         match_results<BidiIterator, Allocator>& m,\n         const u32regex& e,\n         match_flag_type flags,\n         boost::mpl::int_<4> const*)\n      {\n         return ::boost::regex_match(first, last, m, e, flags);\n      }\n      template <class BidiIterator, class Allocator>\n      bool do_regex_match(BidiIterator first, BidiIterator last,\n         match_results<BidiIterator, Allocator>& m,\n         const u32regex& e,\n         match_flag_type flags,\n         boost::mpl::int_<2> const*)\n      {\n         typedef u16_to_u32_iterator<BidiIterator, UChar32> conv_type;\n         typedef match_results<conv_type>                   match_type;\n         //typedef typename match_type::allocator_type        alloc_type;\n         match_type what;\n         bool result = ::boost::regex_match(conv_type(first, first, last), conv_type(last, first, last), what, e, flags);\n         // copy results across to m:\n         if (result) copy_results(m, what, e.get_named_subs());\n         return result;\n      }\n      template <class BidiIterator, class Allocator>\n      bool do_regex_match(BidiIterator first, BidiIterator last,\n         match_results<BidiIterator, Allocator>& m,\n         const u32regex& e,\n         match_flag_type flags,\n         boost::mpl::int_<1> const*)\n      {\n         typedef u8_to_u32_iterator<BidiIterator, UChar32>  conv_type;\n         typedef match_results<conv_type>                   match_type;\n         //typedef typename match_type::allocator_type        alloc_type;\n         match_type what;\n         bool result = ::boost::regex_match(conv_type(first, first, last), conv_type(last, first, last), what, e, flags);\n         // copy results across to m:\n         if (result) copy_results(m, what, e.get_named_subs());\n         return result;\n      }\n   } // namespace BOOST_REGEX_DETAIL_NS\n\n   template <class BidiIterator, class Allocator>\n   inline bool u32regex_match(BidiIterator first, BidiIterator last,\n      match_results<BidiIterator, Allocator>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(first, last, m, e, flags, static_cast<mpl::int_<sizeof(*first)> const*>(0));\n   }\n   inline bool u32regex_match(const UChar* p,\n      match_results<const UChar*>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + u_strlen(p), m, e, flags, static_cast<mpl::int_<2> const*>(0));\n   }\n#if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX)\n   inline bool u32regex_match(const wchar_t* p,\n      match_results<const wchar_t*>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::wcslen(p), m, e, flags, static_cast<mpl::int_<sizeof(wchar_t)> const*>(0));\n   }\n#endif\n   inline bool u32regex_match(const char* p,\n      match_results<const char*>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::strlen(p), m, e, flags, static_cast<mpl::int_<1> const*>(0));\n   }\n   inline bool u32regex_match(const unsigned char* p,\n      match_results<const unsigned char*>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::strlen((const char*)p), m, e, flags, static_cast<mpl::int_<1> const*>(0));\n   }\n   inline bool u32regex_match(const std::string& s,\n      match_results<std::string::const_iterator>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast<mpl::int_<1> const*>(0));\n   }\n#ifndef BOOST_NO_STD_WSTRING\n   inline bool u32regex_match(const std::wstring& s,\n      match_results<std::wstring::const_iterator>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast<mpl::int_<sizeof(wchar_t)> const*>(0));\n   }\n#endif\n   inline bool u32regex_match(const U_NAMESPACE_QUALIFIER UnicodeString& s,\n      match_results<const UChar*>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, static_cast<mpl::int_<2> const*>(0));\n   }\n   //\n   // regex_match overloads that do not return what matched:\n   //\n   template <class BidiIterator>\n   inline bool u32regex_match(BidiIterator first, BidiIterator last,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<BidiIterator> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(first, last, m, e, flags, static_cast<mpl::int_<sizeof(*first)> const*>(0));\n   }\n   inline bool u32regex_match(const UChar* p,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<const UChar*> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + u_strlen(p), m, e, flags, static_cast<mpl::int_<2> const*>(0));\n   }\n#if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX)\n   inline bool u32regex_match(const wchar_t* p,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<const wchar_t*> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::wcslen(p), m, e, flags, static_cast<mpl::int_<sizeof(wchar_t)> const*>(0));\n   }\n#endif\n   inline bool u32regex_match(const char* p,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<const char*> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::strlen(p), m, e, flags, static_cast<mpl::int_<1> const*>(0));\n   }\n   inline bool u32regex_match(const unsigned char* p,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<const unsigned char*> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p + std::strlen((const char*)p), m, e, flags, static_cast<mpl::int_<1> const*>(0));\n   }\n   inline bool u32regex_match(const std::string& s,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<std::string::const_iterator> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast<mpl::int_<1> const*>(0));\n   }\n#ifndef BOOST_NO_STD_WSTRING\n   inline bool u32regex_match(const std::wstring& s,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<std::wstring::const_iterator> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast<mpl::int_<sizeof(wchar_t)> const*>(0));\n   }\n#endif\n   inline bool u32regex_match(const U_NAMESPACE_QUALIFIER UnicodeString& s,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<const UChar*> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_match(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, static_cast<mpl::int_<2> const*>(0));\n   }\n\n   //\n   // regex_search overloads that widen the character type as appropriate:\n   //\n   namespace BOOST_REGEX_DETAIL_NS {\n      template <class BidiIterator, class Allocator>\n      inline bool do_regex_search(BidiIterator first, BidiIterator last,\n         match_results<BidiIterator, Allocator>& m,\n         const u32regex& e,\n         match_flag_type flags,\n         BidiIterator base,\n         boost::mpl::int_<4> const*)\n      {\n         return ::boost::regex_search(first, last, m, e, flags, base);\n      }\n      template <class BidiIterator, class Allocator>\n      bool do_regex_search(BidiIterator first, BidiIterator last,\n         match_results<BidiIterator, Allocator>& m,\n         const u32regex& e,\n         match_flag_type flags,\n         BidiIterator base,\n         boost::mpl::int_<2> const*)\n      {\n         typedef u16_to_u32_iterator<BidiIterator, UChar32> conv_type;\n         typedef match_results<conv_type>                   match_type;\n         //typedef typename match_type::allocator_type        alloc_type;\n         match_type what;\n         bool result = ::boost::regex_search(conv_type(first, first, last), conv_type(last, first, last), what, e, flags, conv_type(base));\n         // copy results across to m:\n         if (result) copy_results(m, what, e.get_named_subs());\n         return result;\n      }\n      template <class BidiIterator, class Allocator>\n      bool do_regex_search(BidiIterator first, BidiIterator last,\n         match_results<BidiIterator, Allocator>& m,\n         const u32regex& e,\n         match_flag_type flags,\n         BidiIterator base,\n         boost::mpl::int_<1> const*)\n      {\n         typedef u8_to_u32_iterator<BidiIterator, UChar32>  conv_type;\n         typedef match_results<conv_type>                   match_type;\n         //typedef typename match_type::allocator_type        alloc_type;\n         match_type what;\n         bool result = ::boost::regex_search(conv_type(first, first, last), conv_type(last, first, last), what, e, flags, conv_type(base));\n         // copy results across to m:\n         if (result) copy_results(m, what, e.get_named_subs());\n         return result;\n      }\n   }\n\n   template <class BidiIterator, class Allocator>\n   inline bool u32regex_search(BidiIterator first, BidiIterator last,\n      match_results<BidiIterator, Allocator>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, first, static_cast<mpl::int_<sizeof(*first)> const*>(0));\n   }\n   template <class BidiIterator, class Allocator>\n   inline bool u32regex_search(BidiIterator first, BidiIterator last,\n      match_results<BidiIterator, Allocator>& m,\n      const u32regex& e,\n      match_flag_type flags,\n      BidiIterator base)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, base, static_cast<mpl::int_<sizeof(*first)> const*>(0));\n   }\n   inline bool u32regex_search(const UChar* p,\n      match_results<const UChar*>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + u_strlen(p), m, e, flags, p, static_cast<mpl::int_<2> const*>(0));\n   }\n#if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX)\n   inline bool u32regex_search(const wchar_t* p,\n      match_results<const wchar_t*>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::wcslen(p), m, e, flags, p, static_cast<mpl::int_<sizeof(wchar_t)> const*>(0));\n   }\n#endif\n   inline bool u32regex_search(const char* p,\n      match_results<const char*>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::strlen(p), m, e, flags, p, static_cast<mpl::int_<1> const*>(0));\n   }\n   inline bool u32regex_search(const unsigned char* p,\n      match_results<const unsigned char*>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::strlen((const char*)p), m, e, flags, p, static_cast<mpl::int_<1> const*>(0));\n   }\n   inline bool u32regex_search(const std::string& s,\n      match_results<std::string::const_iterator>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast<mpl::int_<1> const*>(0));\n   }\n#ifndef BOOST_NO_STD_WSTRING\n   inline bool u32regex_search(const std::wstring& s,\n      match_results<std::wstring::const_iterator>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast<mpl::int_<sizeof(wchar_t)> const*>(0));\n   }\n#endif\n   inline bool u32regex_search(const U_NAMESPACE_QUALIFIER UnicodeString& s,\n      match_results<const UChar*>& m,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, s.getBuffer(), static_cast<mpl::int_<2> const*>(0));\n   }\n   template <class BidiIterator>\n   inline bool u32regex_search(BidiIterator first, BidiIterator last,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<BidiIterator> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, first, static_cast<mpl::int_<sizeof(*first)> const*>(0));\n   }\n   inline bool u32regex_search(const UChar* p,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<const UChar*> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + u_strlen(p), m, e, flags, p, static_cast<mpl::int_<2> const*>(0));\n   }\n#if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX)\n   inline bool u32regex_search(const wchar_t* p,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<const wchar_t*> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::wcslen(p), m, e, flags, p, static_cast<mpl::int_<sizeof(wchar_t)> const*>(0));\n   }\n#endif\n   inline bool u32regex_search(const char* p,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<const char*> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::strlen(p), m, e, flags, p, static_cast<mpl::int_<1> const*>(0));\n   }\n   inline bool u32regex_search(const unsigned char* p,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<const unsigned char*> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p + std::strlen((const char*)p), m, e, flags, p, static_cast<mpl::int_<1> const*>(0));\n   }\n   inline bool u32regex_search(const std::string& s,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<std::string::const_iterator> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast<mpl::int_<1> const*>(0));\n   }\n#ifndef BOOST_NO_STD_WSTRING\n   inline bool u32regex_search(const std::wstring& s,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<std::wstring::const_iterator> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast<mpl::int_<sizeof(wchar_t)> const*>(0));\n   }\n#endif\n   inline bool u32regex_search(const U_NAMESPACE_QUALIFIER UnicodeString& s,\n      const u32regex& e,\n      match_flag_type flags = match_default)\n   {\n      match_results<const UChar*> m;\n      return BOOST_REGEX_DETAIL_NS::do_regex_search(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, s.getBuffer(), static_cast<mpl::int_<2> const*>(0));\n   }\n\n   //\n   // overloads for regex_replace with utf-8 and utf-16 data types:\n   //\n   namespace BOOST_REGEX_DETAIL_NS {\n      template <class I>\n      inline std::pair< boost::u8_to_u32_iterator<I>, boost::u8_to_u32_iterator<I> >\n         make_utf32_seq(I i, I j, mpl::int_<1> const*)\n      {\n         return std::pair< boost::u8_to_u32_iterator<I>, boost::u8_to_u32_iterator<I> >(boost::u8_to_u32_iterator<I>(i, i, j), boost::u8_to_u32_iterator<I>(j, i, j));\n      }\n      template <class I>\n      inline std::pair< boost::u16_to_u32_iterator<I>, boost::u16_to_u32_iterator<I> >\n         make_utf32_seq(I i, I j, mpl::int_<2> const*)\n      {\n         return std::pair< boost::u16_to_u32_iterator<I>, boost::u16_to_u32_iterator<I> >(boost::u16_to_u32_iterator<I>(i, i, j), boost::u16_to_u32_iterator<I>(j, i, j));\n      }\n      template <class I>\n      inline std::pair< I, I >\n         make_utf32_seq(I i, I j, mpl::int_<4> const*)\n      {\n         return std::pair< I, I >(i, j);\n      }\n      template <class charT>\n      inline std::pair< boost::u8_to_u32_iterator<const charT*>, boost::u8_to_u32_iterator<const charT*> >\n         make_utf32_seq(const charT* p, mpl::int_<1> const*)\n      {\n         std::size_t len = std::strlen((const char*)p);\n         return std::pair< boost::u8_to_u32_iterator<const charT*>, boost::u8_to_u32_iterator<const charT*> >(boost::u8_to_u32_iterator<const charT*>(p, p, p + len), boost::u8_to_u32_iterator<const charT*>(p + len, p, p + len));\n      }\n      template <class charT>\n      inline std::pair< boost::u16_to_u32_iterator<const charT*>, boost::u16_to_u32_iterator<const charT*> >\n         make_utf32_seq(const charT* p, mpl::int_<2> const*)\n      {\n         std::size_t len = u_strlen((const UChar*)p);\n         return std::pair< boost::u16_to_u32_iterator<const charT*>, boost::u16_to_u32_iterator<const charT*> >(boost::u16_to_u32_iterator<const charT*>(p, p, p + len), boost::u16_to_u32_iterator<const charT*>(p + len, p, p + len));\n      }\n      template <class charT>\n      inline std::pair< const charT*, const charT* >\n         make_utf32_seq(const charT* p, mpl::int_<4> const*)\n      {\n         return std::pair< const charT*, const charT* >(p, p + icu_regex_traits::length((UChar32 const*)p));\n      }\n      template <class OutputIterator>\n      inline OutputIterator make_utf32_out(OutputIterator o, mpl::int_<4> const*)\n      {\n         return o;\n      }\n      template <class OutputIterator>\n      inline utf16_output_iterator<OutputIterator> make_utf32_out(OutputIterator o, mpl::int_<2> const*)\n      {\n         return o;\n      }\n      template <class OutputIterator>\n      inline utf8_output_iterator<OutputIterator> make_utf32_out(OutputIterator o, mpl::int_<1> const*)\n      {\n         return o;\n      }\n\n      template <class OutputIterator, class I1, class I2>\n      OutputIterator do_regex_replace(OutputIterator out,\n         std::pair<I1, I1> const& in,\n         const u32regex& e,\n         const std::pair<I2, I2>& fmt,\n         match_flag_type flags\n      )\n      {\n         // unfortunately we have to copy the format string in order to pass in onward:\n         std::vector<UChar32> f;\n#ifndef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS\n         f.assign(fmt.first, fmt.second);\n#else\n         f.clear();\n         I2 pos = fmt.first;\n         while (pos != fmt.second)\n            f.push_back(*pos++);\n#endif\n\n         regex_iterator<I1, UChar32, icu_regex_traits> i(in.first, in.second, e, flags);\n         regex_iterator<I1, UChar32, icu_regex_traits> j;\n         if (i == j)\n         {\n            if (!(flags & regex_constants::format_no_copy))\n               out = BOOST_REGEX_DETAIL_NS::copy(in.first, in.second, out);\n         }\n         else\n         {\n            I1 last_m = in.first;\n            while (i != j)\n            {\n               if (!(flags & regex_constants::format_no_copy))\n                  out = BOOST_REGEX_DETAIL_NS::copy(i->prefix().first, i->prefix().second, out);\n               if (!f.empty())\n                  out = ::boost::BOOST_REGEX_DETAIL_NS::regex_format_imp(out, *i, &*f.begin(), &*f.begin() + f.size(), flags, e.get_traits());\n               else\n                  out = ::boost::BOOST_REGEX_DETAIL_NS::regex_format_imp(out, *i, static_cast<UChar32 const*>(0), static_cast<UChar32 const*>(0), flags, e.get_traits());\n               last_m = (*i)[0].second;\n               if (flags & regex_constants::format_first_only)\n                  break;\n               ++i;\n            }\n            if (!(flags & regex_constants::format_no_copy))\n               out = BOOST_REGEX_DETAIL_NS::copy(last_m, in.second, out);\n         }\n         return out;\n      }\n      template <class BaseIterator>\n      inline const BaseIterator& extract_output_base(const BaseIterator& b)\n      {\n         return b;\n      }\n      template <class BaseIterator>\n      inline BaseIterator extract_output_base(const utf8_output_iterator<BaseIterator>& b)\n      {\n         return b.base();\n      }\n      template <class BaseIterator>\n      inline BaseIterator extract_output_base(const utf16_output_iterator<BaseIterator>& b)\n      {\n         return b.base();\n      }\n   }  // BOOST_REGEX_DETAIL_NS\n\n   template <class OutputIterator, class BidirectionalIterator, class charT>\n   inline OutputIterator u32regex_replace(OutputIterator out,\n      BidirectionalIterator first,\n      BidirectionalIterator last,\n      const u32regex& e,\n      const charT* fmt,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::extract_output_base\n      (\n         BOOST_REGEX_DETAIL_NS::do_regex_replace(\n            BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast<mpl::int_<sizeof(*first)> const*>(0)),\n            BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast<mpl::int_<sizeof(*first)> const*>(0)),\n            e,\n            BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt, static_cast<mpl::int_<sizeof(*fmt)> const*>(0)),\n            flags)\n      );\n   }\n\n   template <class OutputIterator, class Iterator, class charT>\n   inline OutputIterator u32regex_replace(OutputIterator out,\n      Iterator first,\n      Iterator last,\n      const u32regex& e,\n      const std::basic_string<charT>& fmt,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::extract_output_base\n      (\n         BOOST_REGEX_DETAIL_NS::do_regex_replace(\n            BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast<mpl::int_<sizeof(*first)> const*>(0)),\n            BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast<mpl::int_<sizeof(*first)> const*>(0)),\n            e,\n            BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.begin(), fmt.end(), static_cast<mpl::int_<sizeof(charT)> const*>(0)),\n            flags)\n      );\n   }\n\n   template <class OutputIterator, class Iterator>\n   inline OutputIterator u32regex_replace(OutputIterator out,\n      Iterator first,\n      Iterator last,\n      const u32regex& e,\n      const U_NAMESPACE_QUALIFIER UnicodeString& fmt,\n      match_flag_type flags = match_default)\n   {\n      return BOOST_REGEX_DETAIL_NS::extract_output_base\n      (\n         BOOST_REGEX_DETAIL_NS::do_regex_replace(\n            BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast<mpl::int_<sizeof(*first)> const*>(0)),\n            BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast<mpl::int_<sizeof(*first)> const*>(0)),\n            e,\n            BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.getBuffer(), fmt.getBuffer() + fmt.length(), static_cast<mpl::int_<2> const*>(0)),\n            flags)\n      );\n   }\n\n   template <class charT>\n   std::basic_string<charT> u32regex_replace(const std::basic_string<charT>& s,\n      const u32regex& e,\n      const charT* fmt,\n      match_flag_type flags = match_default)\n   {\n      std::basic_string<charT> result;\n      BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<charT> > i(result);\n      u32regex_replace(i, s.begin(), s.end(), e, fmt, flags);\n      return result;\n   }\n\n   template <class charT>\n   std::basic_string<charT> u32regex_replace(const std::basic_string<charT>& s,\n      const u32regex& e,\n      const std::basic_string<charT>& fmt,\n      match_flag_type flags = match_default)\n   {\n      std::basic_string<charT> result;\n      BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<charT> > i(result);\n      u32regex_replace(i, s.begin(), s.end(), e, fmt.c_str(), flags);\n      return result;\n   }\n\n   namespace BOOST_REGEX_DETAIL_NS {\n\n      class unicode_string_out_iterator\n      {\n         U_NAMESPACE_QUALIFIER UnicodeString* out;\n      public:\n         unicode_string_out_iterator(U_NAMESPACE_QUALIFIER UnicodeString& s) : out(&s) {}\n         unicode_string_out_iterator& operator++() { return *this; }\n         unicode_string_out_iterator& operator++(int) { return *this; }\n         unicode_string_out_iterator& operator*() { return *this; }\n         unicode_string_out_iterator& operator=(UChar v)\n         {\n            *out += v;\n            return *this;\n         }\n         typedef std::ptrdiff_t difference_type;\n         typedef UChar value_type;\n         typedef value_type* pointer;\n         typedef value_type& reference;\n         typedef std::output_iterator_tag iterator_category;\n      };\n\n   }\n\n   inline U_NAMESPACE_QUALIFIER UnicodeString u32regex_replace(const U_NAMESPACE_QUALIFIER UnicodeString& s,\n      const u32regex& e,\n      const UChar* fmt,\n      match_flag_type flags = match_default)\n   {\n      U_NAMESPACE_QUALIFIER UnicodeString result;\n      BOOST_REGEX_DETAIL_NS::unicode_string_out_iterator i(result);\n      u32regex_replace(i, s.getBuffer(), s.getBuffer() + s.length(), e, fmt, flags);\n      return result;\n   }\n\n   inline U_NAMESPACE_QUALIFIER UnicodeString u32regex_replace(const U_NAMESPACE_QUALIFIER UnicodeString& s,\n      const u32regex& e,\n      const U_NAMESPACE_QUALIFIER UnicodeString& fmt,\n      match_flag_type flags = match_default)\n   {\n      U_NAMESPACE_QUALIFIER UnicodeString result;\n      BOOST_REGEX_DETAIL_NS::unicode_string_out_iterator i(result);\n      BOOST_REGEX_DETAIL_NS::do_regex_replace(\n         BOOST_REGEX_DETAIL_NS::make_utf32_out(i, static_cast<mpl::int_<2> const*>(0)),\n         BOOST_REGEX_DETAIL_NS::make_utf32_seq(s.getBuffer(), s.getBuffer() + s.length(), static_cast<mpl::int_<2> const*>(0)),\n         e,\n         BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.getBuffer(), fmt.getBuffer() + fmt.length(), static_cast<mpl::int_<2> const*>(0)),\n         flags);\n      return result;\n   }\n\n} // namespace boost.\n\n#ifdef BOOST_MSVC\n#pragma warning (pop)\n#endif\n\n#include <boost/regex/v4/u32regex_iterator.hpp>\n#include <boost/regex/v4/u32regex_token_iterator.hpp>\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/indexed_bit_flag.hpp",
    "content": "/*\n *\n * Copyright (c) 2020\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         basic_regex_parser.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class basic_regex_parser.\n  */\n\n#include <boost/regex/config.hpp>\n#include <set>\n\n#ifndef BOOST_REGEX_V4_INDEXED_BIT_FLAG_HPP\n#define BOOST_REGEX_V4_INDEXED_BIT_FLAG_HPP\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\nclass indexed_bit_flag\n{\n   boost::uint64_t low_mask;\n   std::set<std::size_t> mask_set;\npublic:\n   indexed_bit_flag() : low_mask(0) {}\n   void set(std::size_t i)\n   {\n      if (i < std::numeric_limits<boost::uint64_t>::digits - 1)\n         low_mask |= static_cast<boost::uint64_t>(1u) << i;\n      else\n         mask_set.insert(i);\n   }\n   bool test(std::size_t i)\n   {\n      if (i < std::numeric_limits<boost::uint64_t>::digits - 1)\n         return low_mask & static_cast<boost::uint64_t>(1u) << i ? true : false;\n      else\n         return mask_set.find(i) != mask_set.end();\n   }\n};\n\n} // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/iterator_category.hpp",
    "content": "/*\n *\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_match.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Iterator traits for selecting an iterator type as\n  *                an integral constant expression.\n  */\n\n\n#ifndef BOOST_REGEX_ITERATOR_CATEGORY_HPP\n#define BOOST_REGEX_ITERATOR_CATEGORY_HPP\n\n#include <iterator>\n#include <boost/type_traits/is_convertible.hpp>\n#include <boost/type_traits/is_pointer.hpp>\n\nnamespace boost{\nnamespace detail{\n\ntemplate <class I>\nstruct is_random_imp\n{\n#ifndef BOOST_NO_STD_ITERATOR_TRAITS\nprivate:\n   typedef typename std::iterator_traits<I>::iterator_category cat;\npublic:\n   BOOST_STATIC_CONSTANT(bool, value = (::boost::is_convertible<cat*, std::random_access_iterator_tag*>::value));\n#else\n   BOOST_STATIC_CONSTANT(bool, value = false);\n#endif\n};\n\ntemplate <class I>\nstruct is_random_pointer_imp\n{\n   BOOST_STATIC_CONSTANT(bool, value = true);\n};\n\ntemplate <bool is_pointer_type>\nstruct is_random_imp_selector\n{\n   template <class I>\n   struct rebind\n   {\n      typedef is_random_imp<I> type;\n   };\n};\n\ntemplate <>\nstruct is_random_imp_selector<true>\n{\n   template <class I>\n   struct rebind\n   {\n      typedef is_random_pointer_imp<I> type;\n   };\n};\n\n}\n\ntemplate <class I>\nstruct is_random_access_iterator\n{\nprivate:\n   typedef detail::is_random_imp_selector< ::boost::is_pointer<I>::value> selector;\n   typedef typename selector::template rebind<I> bound_type;\n   typedef typename bound_type::type answer;\npublic:\n   BOOST_STATIC_CONSTANT(bool, value = answer::value);\n};\n\n#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION\ntemplate <class I>\nconst bool is_random_access_iterator<I>::value;\n#endif\n\n}\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/iterator_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         iterator_traits.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares iterator traits workarounds.\n  */\n\n#ifndef BOOST_REGEX_V4_ITERATOR_TRAITS_HPP\n#define BOOST_REGEX_V4_ITERATOR_TRAITS_HPP\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\n#if defined(BOOST_NO_STD_ITERATOR_TRAITS)\n\ntemplate <class T>\nstruct regex_iterator_traits \n{\n  typedef typename T::iterator_category iterator_category;\n  typedef typename T::value_type        value_type;\n#if !defined(BOOST_NO_STD_ITERATOR)\n  typedef typename T::difference_type   difference_type;\n  typedef typename T::pointer           pointer;\n  typedef typename T::reference         reference;\n#else\n  typedef std::ptrdiff_t                difference_type;\n  typedef value_type*                   pointer;\n  typedef value_type&                   reference;\n#endif\n};\n\ntemplate <class T>\nstruct pointer_iterator_traits\n{\n   typedef std::ptrdiff_t difference_type;\n   typedef T value_type;\n   typedef T* pointer;\n   typedef T& reference;\n   typedef std::random_access_iterator_tag iterator_category;\n};\ntemplate <class T>\nstruct const_pointer_iterator_traits\n{\n   typedef std::ptrdiff_t difference_type;\n   typedef T value_type;\n   typedef const T* pointer;\n   typedef const T& reference;\n   typedef std::random_access_iterator_tag iterator_category;\n};\n\ntemplate<>\nstruct regex_iterator_traits<char*> : pointer_iterator_traits<char>{};\ntemplate<>\nstruct regex_iterator_traits<const char*> : const_pointer_iterator_traits<char>{};\ntemplate<>\nstruct regex_iterator_traits<wchar_t*> : pointer_iterator_traits<wchar_t>{};\ntemplate<>\nstruct regex_iterator_traits<const wchar_t*> : const_pointer_iterator_traits<wchar_t>{};\n//\n// the follwoing are needed for ICU support:\n//\ntemplate<>\nstruct regex_iterator_traits<unsigned char*> : pointer_iterator_traits<char>{};\ntemplate<>\nstruct regex_iterator_traits<const unsigned char*> : const_pointer_iterator_traits<char>{};\ntemplate<>\nstruct regex_iterator_traits<int*> : pointer_iterator_traits<int>{};\ntemplate<>\nstruct regex_iterator_traits<const int*> : const_pointer_iterator_traits<int>{};\n\n#ifdef BOOST_REGEX_HAS_OTHER_WCHAR_T\ntemplate<>\nstruct regex_iterator_traits<unsigned short*> : pointer_iterator_traits<unsigned short>{};\ntemplate<>\nstruct regex_iterator_traits<const unsigned short*> : const_pointer_iterator_traits<unsigned short>{};\n#endif\n\n#if defined(__SGI_STL_PORT) && defined(__STL_DEBUG)\ntemplate<>\nstruct regex_iterator_traits<std::string::iterator> : pointer_iterator_traits<char>{};\ntemplate<>\nstruct regex_iterator_traits<std::string::const_iterator> : const_pointer_iterator_traits<char>{};\n#ifndef BOOST_NO_STD_WSTRING\ntemplate<>\nstruct regex_iterator_traits<std::wstring::iterator> : pointer_iterator_traits<wchar_t>{};\ntemplate<>\nstruct regex_iterator_traits<std::wstring::const_iterator> : const_pointer_iterator_traits<wchar_t>{};\n#endif // BOOST_NO_WSTRING\n#endif // stport\n\n#else\n\ntemplate <class T>\nstruct regex_iterator_traits : public std::iterator_traits<T> {};\n\n#endif\n\n} // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/match_flags.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         match_flags.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares match_flags type.\n  */\n\n#ifndef BOOST_REGEX_V4_MATCH_FLAGS\n#define BOOST_REGEX_V4_MATCH_FLAGS\n\n#ifdef __cplusplus\n#  include <boost/cstdint.hpp>\n#endif\n\n#ifdef __cplusplus\nnamespace boost{\n   namespace regex_constants{\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#if BOOST_MSVC >= 1800\n#pragma warning(disable : 26812)\n#endif\n#endif\n\ntypedef enum _match_flags\n{\n   match_default = 0,\n   match_not_bol = 1,                                /* first is not start of line */\n   match_not_eol = match_not_bol << 1,               /* last is not end of line */\n   match_not_bob = match_not_eol << 1,               /* first is not start of buffer */\n   match_not_eob = match_not_bob << 1,               /* last is not end of buffer */\n   match_not_bow = match_not_eob << 1,               /* first is not start of word */\n   match_not_eow = match_not_bow << 1,               /* last is not end of word */\n   match_not_dot_newline = match_not_eow << 1,       /* \\n is not matched by '.' */\n   match_not_dot_null = match_not_dot_newline << 1,  /* '\\0' is not matched by '.' */\n   match_prev_avail = match_not_dot_null << 1,       /* *--first is a valid expression */\n   match_init = match_prev_avail << 1,               /* internal use */\n   match_any = match_init << 1,                      /* don't care what we match */\n   match_not_null = match_any << 1,                  /* string can't be null */\n   match_continuous = match_not_null << 1,           /* each grep match must continue from */\n                                                     /* uninterrupted from the previous one */\n   match_partial = match_continuous << 1,            /* find partial matches */\n   \n   match_stop = match_partial << 1,                  /* stop after first match (grep) V3 only */\n   match_not_initial_null = match_stop,              /* don't match initial null, V4 only */\n   match_all = match_stop << 1,                      /* must find the whole of input even if match_any is set */\n   match_perl = match_all << 1,                      /* Use perl matching rules */\n   match_posix = match_perl << 1,                    /* Use POSIX matching rules */\n   match_nosubs = match_posix << 1,                  /* don't trap marked subs */\n   match_extra = match_nosubs << 1,                  /* include full capture information for repeated captures */\n   match_single_line = match_extra << 1,             /* treat text as single line and ignore any \\n's when matching ^ and $. */\n   match_unused1 = match_single_line << 1,           /* unused */\n   match_unused2 = match_unused1 << 1,               /* unused */\n   match_unused3 = match_unused2 << 1,               /* unused */\n   match_max = match_unused3,\n\n   format_perl = 0,                                  /* perl style replacement */\n   format_default = 0,                               /* ditto. */\n   format_sed = match_max << 1,                      /* sed style replacement. */\n   format_all = format_sed << 1,                     /* enable all extensions to syntax. */\n   format_no_copy = format_all << 1,                 /* don't copy non-matching segments. */\n   format_first_only = format_no_copy << 1,          /* Only replace first occurrence. */\n   format_is_if = format_first_only << 1,            /* internal use only. */\n   format_literal = format_is_if << 1,               /* treat string as a literal */\n\n   match_not_any = match_not_bol | match_not_eol | match_not_bob \n      | match_not_eob | match_not_bow | match_not_eow | match_not_dot_newline \n      | match_not_dot_null | match_prev_avail | match_init | match_not_null\n      | match_continuous | match_partial | match_stop | match_not_initial_null \n      | match_stop | match_all | match_perl | match_posix | match_nosubs\n      | match_extra | match_single_line | match_unused1 | match_unused2 \n      | match_unused3 | match_max | format_perl | format_default | format_sed\n      | format_all | format_no_copy | format_first_only | format_is_if\n      | format_literal\n\n\n} match_flags;\n\n#if defined(BOOST_BORLANDC) || (defined(_MSC_VER) && (_MSC_VER <= 1310))\ntypedef unsigned long match_flag_type;\n#else\ntypedef match_flags match_flag_type;\n\n\n#ifdef __cplusplus\ninline match_flags operator&(match_flags m1, match_flags m2)\n{ return static_cast<match_flags>(static_cast<boost::int32_t>(m1) & static_cast<boost::int32_t>(m2)); }\ninline match_flags operator|(match_flags m1, match_flags m2)\n{ return static_cast<match_flags>(static_cast<boost::int32_t>(m1) | static_cast<boost::int32_t>(m2)); }\ninline match_flags operator^(match_flags m1, match_flags m2)\n{ return static_cast<match_flags>(static_cast<boost::int32_t>(m1) ^ static_cast<boost::int32_t>(m2)); }\ninline match_flags operator~(match_flags m1)\n{ return static_cast<match_flags>(~static_cast<boost::int32_t>(m1)); }\ninline match_flags& operator&=(match_flags& m1, match_flags m2)\n{ m1 = m1&m2; return m1; }\ninline match_flags& operator|=(match_flags& m1, match_flags m2)\n{ m1 = m1|m2; return m1; }\ninline match_flags& operator^=(match_flags& m1, match_flags m2)\n{ m1 = m1^m2; return m1; }\n#endif\n#endif\n\n#ifdef __cplusplus\n} /* namespace regex_constants */\n/*\n * import names into boost for backwards compatibility:\n */\nusing regex_constants::match_flag_type;\nusing regex_constants::match_default;\nusing regex_constants::match_not_bol;\nusing regex_constants::match_not_eol;\nusing regex_constants::match_not_bob;\nusing regex_constants::match_not_eob;\nusing regex_constants::match_not_bow;\nusing regex_constants::match_not_eow;\nusing regex_constants::match_not_dot_newline;\nusing regex_constants::match_not_dot_null;\nusing regex_constants::match_prev_avail;\n/* using regex_constants::match_init; */\nusing regex_constants::match_any;\nusing regex_constants::match_not_null;\nusing regex_constants::match_continuous;\nusing regex_constants::match_partial;\n/*using regex_constants::match_stop; */\nusing regex_constants::match_all;\nusing regex_constants::match_perl;\nusing regex_constants::match_posix;\nusing regex_constants::match_nosubs;\nusing regex_constants::match_extra;\nusing regex_constants::match_single_line;\n/*using regex_constants::match_max; */\nusing regex_constants::format_all;\nusing regex_constants::format_sed;\nusing regex_constants::format_perl;\nusing regex_constants::format_default;\nusing regex_constants::format_no_copy;\nusing regex_constants::format_first_only;\n/*using regex_constants::format_is_if;*/\n\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n\n} /* namespace boost */\n#endif /* __cplusplus */\n#endif /* include guard */\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/match_results.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2009\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         match_results.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class match_results.\n  */\n\n#ifndef BOOST_REGEX_V4_MATCH_RESULTS_HPP\n#define BOOST_REGEX_V4_MATCH_RESULTS_HPP\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable : 4251)\n#if BOOST_MSVC < 1700\n#     pragma warning(disable : 4231)\n#endif\n#  if BOOST_MSVC < 1600\n#     pragma warning(disable : 4660)\n#  endif\n#endif\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\nclass named_subexpressions;\n\n}\n\ntemplate <class BidiIterator, class Allocator>\nclass match_results\n{ \nprivate:\n#ifndef BOOST_NO_STD_ALLOCATOR\n   typedef          std::vector<sub_match<BidiIterator>, Allocator> vector_type;\n#else\n   typedef          std::vector<sub_match<BidiIterator> >           vector_type;\n#endif\npublic: \n   typedef          sub_match<BidiIterator>                         value_type;\n#ifndef BOOST_NO_CXX11_ALLOCATOR\n   typedef typename std::allocator_traits<Allocator>::value_type const &    const_reference;\n#elif  !defined(BOOST_NO_STD_ALLOCATOR) && !(defined(BOOST_MSVC) && defined(_STLPORT_VERSION))\n   typedef typename Allocator::const_reference                              const_reference;\n#else\n   typedef          const value_type&                                       const_reference;\n#endif\n   typedef          const_reference                                         reference;\n   typedef typename vector_type::const_iterator                             const_iterator;\n   typedef          const_iterator                                          iterator;\n   typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<\n                                    BidiIterator>::difference_type          difference_type;\n#ifdef BOOST_NO_CXX11_ALLOCATOR\n   typedef typename Allocator::size_type                                    size_type;\n#else\n   typedef typename std::allocator_traits<Allocator>::size_type             size_type;\n#endif\n   typedef          Allocator                                               allocator_type;\n   typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<\n                                    BidiIterator>::value_type               char_type;\n   typedef          std::basic_string<char_type>                            string_type;\n   typedef          BOOST_REGEX_DETAIL_NS::named_subexpressions                         named_sub_type;\n\n   // construct/copy/destroy:\n   explicit match_results(const Allocator& a = Allocator())\n#ifndef BOOST_NO_STD_ALLOCATOR\n      : m_subs(a), m_base(), m_null(), m_last_closed_paren(0), m_is_singular(true) {}\n#else\n      : m_subs(), m_base(), m_null(), m_last_closed_paren(0), m_is_singular(true) { (void)a; }\n#endif\n   //\n   // IMPORTANT: in the code below, the crazy looking checks around m_is_singular are\n   // all required because it is illegal to copy a singular iterator.\n   // See https://svn.boost.org/trac/boost/ticket/3632.\n   //\n   match_results(const match_results& m)\n      : m_subs(m.m_subs), m_base(), m_null(), m_named_subs(m.m_named_subs), m_last_closed_paren(m.m_last_closed_paren), m_is_singular(m.m_is_singular)\n   {\n      if(!m_is_singular)\n      {\n         m_base = m.m_base;\n         m_null = m.m_null;\n      }\n   }\n   match_results& operator=(const match_results& m)\n   {\n      m_subs = m.m_subs;\n      m_named_subs = m.m_named_subs;\n      m_last_closed_paren = m.m_last_closed_paren;\n      m_is_singular = m.m_is_singular;\n      if(!m_is_singular)\n      {\n         m_base = m.m_base;\n         m_null = m.m_null;\n      }\n      return *this;\n   }\n   ~match_results(){}\n\n   // size:\n   size_type size() const\n   { return empty() ? 0 : m_subs.size() - 2; }\n   size_type max_size() const\n   { return m_subs.max_size(); }\n   bool empty() const\n   { return m_subs.size() < 2; }\n   // element access:\n   difference_type length(int sub = 0) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      sub += 2;\n      if((sub < (int)m_subs.size()) && (sub > 0))\n         return m_subs[sub].length();\n      return 0;\n   }\n   difference_type length(const char_type* sub) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      const char_type* sub_end = sub;\n      while(*sub_end) ++sub_end;\n      return length(named_subexpression_index(sub, sub_end));\n   }\n   template <class charT>\n   difference_type length(const charT* sub) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      const charT* sub_end = sub;\n      while(*sub_end) ++sub_end;\n      return length(named_subexpression_index(sub, sub_end));\n   }\n   template <class charT, class Traits, class A>\n   difference_type length(const std::basic_string<charT, Traits, A>& sub) const\n   {\n      return length(sub.c_str());\n   }\n   difference_type position(size_type sub = 0) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      sub += 2;\n      if(sub < m_subs.size())\n      {\n         const sub_match<BidiIterator>& s = m_subs[sub];\n         if(s.matched || (sub == 2))\n         {\n            return ::boost::BOOST_REGEX_DETAIL_NS::distance((BidiIterator)(m_base), (BidiIterator)(s.first));\n         }\n      }\n      return ~static_cast<difference_type>(0);\n   }\n   difference_type position(const char_type* sub) const\n   {\n      const char_type* sub_end = sub;\n      while(*sub_end) ++sub_end;\n      return position(named_subexpression_index(sub, sub_end));\n   }\n   template <class charT>\n   difference_type position(const charT* sub) const\n   {\n      const charT* sub_end = sub;\n      while(*sub_end) ++sub_end;\n      return position(named_subexpression_index(sub, sub_end));\n   }\n   template <class charT, class Traits, class A>\n   difference_type position(const std::basic_string<charT, Traits, A>& sub) const\n   {\n      return position(sub.c_str());\n   }\n   string_type str(int sub = 0) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      sub += 2;\n      string_type result;\n      if(sub < (int)m_subs.size() && (sub > 0))\n      {\n         const sub_match<BidiIterator>& s = m_subs[sub];\n         if(s.matched)\n         {\n            result = s.str();\n         }\n      }\n      return result;\n   }\n   string_type str(const char_type* sub) const\n   {\n      return (*this)[sub].str();\n   }\n   template <class Traits, class A>\n   string_type str(const std::basic_string<char_type, Traits, A>& sub) const\n   {\n      return (*this)[sub].str();\n   }\n   template <class charT>\n   string_type str(const charT* sub) const\n   {\n      return (*this)[sub].str();\n   }\n   template <class charT, class Traits, class A>\n   string_type str(const std::basic_string<charT, Traits, A>& sub) const\n   {\n      return (*this)[sub].str();\n   }\n   const_reference operator[](int sub) const\n   {\n      if(m_is_singular && m_subs.empty())\n         raise_logic_error();\n      sub += 2;\n      if(sub < (int)m_subs.size() && (sub >= 0))\n      {\n         return m_subs[sub];\n      }\n      return m_null;\n   }\n   //\n   // Named sub-expressions:\n   //\n   const_reference named_subexpression(const char_type* i, const char_type* j) const\n   {\n      //\n      // Scan for the leftmost *matched* subexpression with the specified named:\n      //\n      if(m_is_singular)\n         raise_logic_error();\n      BOOST_REGEX_DETAIL_NS::named_subexpressions::range_type r = m_named_subs->equal_range(i, j);\n      while((r.first != r.second) && ((*this)[r.first->index].matched == false))\n         ++r.first;\n      return r.first != r.second ? (*this)[r.first->index] : m_null;\n   }\n   template <class charT>\n   const_reference named_subexpression(const charT* i, const charT* j) const\n   {\n      BOOST_STATIC_ASSERT(sizeof(charT) <= sizeof(char_type));\n      if(i == j)\n         return m_null;\n      std::vector<char_type> s;\n      while(i != j)\n         s.insert(s.end(), *i++);\n      return named_subexpression(&*s.begin(), &*s.begin() + s.size());\n   }\n   int named_subexpression_index(const char_type* i, const char_type* j) const\n   {\n      //\n      // Scan for the leftmost *matched* subexpression with the specified named.\n      // If none found then return the leftmost expression with that name,\n      // otherwise an invalid index:\n      //\n      if(m_is_singular)\n         raise_logic_error();\n      BOOST_REGEX_DETAIL_NS::named_subexpressions::range_type s, r;\n      s = r = m_named_subs->equal_range(i, j);\n      while((r.first != r.second) && ((*this)[r.first->index].matched == false))\n         ++r.first;\n      if(r.first == r.second)\n         r = s;\n      return r.first != r.second ? r.first->index : -20;\n   }\n   template <class charT>\n   int named_subexpression_index(const charT* i, const charT* j) const\n   {\n      BOOST_STATIC_ASSERT(sizeof(charT) <= sizeof(char_type));\n      if(i == j)\n         return -20;\n      std::vector<char_type> s;\n      while(i != j)\n         s.insert(s.end(), *i++);\n      return named_subexpression_index(&*s.begin(), &*s.begin() + s.size());\n   }\n   template <class Traits, class A>\n   const_reference operator[](const std::basic_string<char_type, Traits, A>& s) const\n   {\n      return named_subexpression(s.c_str(), s.c_str() + s.size());\n   }\n   const_reference operator[](const char_type* p) const\n   {\n      const char_type* e = p;\n      while(*e) ++e;\n      return named_subexpression(p, e);\n   }\n\n   template <class charT>\n   const_reference operator[](const charT* p) const\n   {\n      BOOST_STATIC_ASSERT(sizeof(charT) <= sizeof(char_type));\n      if(*p == 0)\n         return m_null;\n      std::vector<char_type> s;\n      while(*p)\n         s.insert(s.end(), *p++);\n      return named_subexpression(&*s.begin(), &*s.begin() + s.size());\n   }\n   template <class charT, class Traits, class A>\n   const_reference operator[](const std::basic_string<charT, Traits, A>& ns) const\n   {\n      BOOST_STATIC_ASSERT(sizeof(charT) <= sizeof(char_type));\n      if(ns.empty())\n         return m_null;\n      std::vector<char_type> s;\n      for(unsigned i = 0; i < ns.size(); ++i)\n         s.insert(s.end(), ns[i]);\n      return named_subexpression(&*s.begin(), &*s.begin() + s.size());\n   }\n\n   const_reference prefix() const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      return (*this)[-1];\n   }\n\n   const_reference suffix() const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      return (*this)[-2];\n   }\n   const_iterator begin() const\n   {\n      return (m_subs.size() > 2) ? (m_subs.begin() + 2) : m_subs.end();\n   }\n   const_iterator end() const\n   {\n      return m_subs.end();\n   }\n   // format:\n   template <class OutputIterator, class Functor>\n   OutputIterator format(OutputIterator out,\n                         Functor fmt,\n                         match_flag_type flags = format_default) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, OutputIterator>::type F;\n      F func(fmt);\n      return func(*this, out, flags);\n   }\n   template <class Functor>\n   string_type format(Functor fmt, match_flag_type flags = format_default) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      std::basic_string<char_type> result;\n      BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> > i(result);\n\n      typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> > >::type F;\n      F func(fmt);\n\n      func(*this, i, flags);\n      return result;\n   }\n   // format with locale:\n   template <class OutputIterator, class Functor, class RegexT>\n   OutputIterator format(OutputIterator out,\n                         Functor fmt,\n                         match_flag_type flags,\n                         const RegexT& re) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      typedef ::boost::regex_traits_wrapper<typename RegexT::traits_type> traits_type;\n      typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, OutputIterator, traits_type>::type F;\n      F func(fmt);\n      return func(*this, out, flags, re.get_traits());\n   }\n   template <class RegexT, class Functor>\n   string_type format(Functor fmt,\n                      match_flag_type flags,\n                      const RegexT& re) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      typedef ::boost::regex_traits_wrapper<typename RegexT::traits_type> traits_type;\n      std::basic_string<char_type> result;\n      BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> > i(result);\n\n      typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> >, traits_type >::type F;\n      F func(fmt);\n\n      func(*this, i, flags, re.get_traits());\n      return result;\n   }\n\n   const_reference get_last_closed_paren()const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      return m_last_closed_paren == 0 ? m_null : (*this)[m_last_closed_paren];\n   }\n\n   allocator_type get_allocator() const\n   {\n#ifndef BOOST_NO_STD_ALLOCATOR\n      return m_subs.get_allocator();\n#else\n     return allocator_type();\n#endif\n   }\n   void swap(match_results& that)\n   {\n      std::swap(m_subs, that.m_subs);\n      std::swap(m_named_subs, that.m_named_subs);\n      std::swap(m_last_closed_paren, that.m_last_closed_paren);\n      if(m_is_singular)\n      {\n         if(!that.m_is_singular)\n         {\n            m_base = that.m_base;\n            m_null = that.m_null;\n         }\n      }\n      else if(that.m_is_singular)\n      {\n         that.m_base = m_base;\n         that.m_null = m_null;\n      }\n      else\n      {\n         std::swap(m_base, that.m_base);\n         std::swap(m_null, that.m_null);\n      }\n      std::swap(m_is_singular, that.m_is_singular);\n   }\n   bool operator==(const match_results& that)const\n   {\n      if(m_is_singular)\n      {\n         return that.m_is_singular;\n      }\n      else if(that.m_is_singular)\n      {\n         return false;\n      }\n      return (m_subs == that.m_subs) && (m_base == that.m_base) && (m_last_closed_paren == that.m_last_closed_paren);\n   }\n   bool operator!=(const match_results& that)const\n   { return !(*this == that); }\n\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   typedef typename sub_match<BidiIterator>::capture_sequence_type capture_sequence_type;\n\n   const capture_sequence_type& captures(int i)const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      return (*this)[i].captures();\n   }\n#endif\n\n   //\n   // private access functions:\n   void BOOST_REGEX_CALL set_second(BidiIterator i)\n   {\n      BOOST_REGEX_ASSERT(m_subs.size() > 2);\n      m_subs[2].second = i;\n      m_subs[2].matched = true;\n      m_subs[0].first = i;\n      m_subs[0].matched = (m_subs[0].first != m_subs[0].second);\n      m_null.first = i;\n      m_null.second = i;\n      m_null.matched = false;\n      m_is_singular = false;\n   }\n\n   void BOOST_REGEX_CALL set_second(BidiIterator i, size_type pos, bool m = true, bool escape_k = false)\n   {\n      if(pos)\n         m_last_closed_paren = static_cast<int>(pos);\n      pos += 2;\n      BOOST_REGEX_ASSERT(m_subs.size() > pos);\n      m_subs[pos].second = i;\n      m_subs[pos].matched = m;\n      if((pos == 2) && !escape_k)\n      {\n         m_subs[0].first = i;\n         m_subs[0].matched = (m_subs[0].first != m_subs[0].second);\n         m_null.first = i;\n         m_null.second = i;\n         m_null.matched = false;\n         m_is_singular = false;\n      }\n   }\n   void BOOST_REGEX_CALL set_size(size_type n, BidiIterator i, BidiIterator j)\n   {\n      value_type v(j);\n      size_type len = m_subs.size();\n      if(len > n + 2)\n      {\n         m_subs.erase(m_subs.begin()+n+2, m_subs.end());\n         std::fill(m_subs.begin(), m_subs.end(), v);\n      }\n      else\n      {\n         std::fill(m_subs.begin(), m_subs.end(), v);\n         if(n+2 != len)\n            m_subs.insert(m_subs.end(), n+2-len, v);\n      }\n      m_subs[1].first = i;\n      m_last_closed_paren = 0;\n   }\n   void BOOST_REGEX_CALL set_base(BidiIterator pos)\n   {\n      m_base = pos;\n   }\n   BidiIterator base()const\n   {\n      return m_base;\n   }\n   void BOOST_REGEX_CALL set_first(BidiIterator i)\n   {\n      BOOST_REGEX_ASSERT(m_subs.size() > 2);\n      // set up prefix:\n      m_subs[1].second = i;\n      m_subs[1].matched = (m_subs[1].first != i);\n      // set up $0:\n      m_subs[2].first = i;\n      // zero out everything else:\n      for(size_type n = 3; n < m_subs.size(); ++n)\n      {\n         m_subs[n].first = m_subs[n].second = m_subs[0].second;\n         m_subs[n].matched = false;\n      }\n   }\n   void BOOST_REGEX_CALL set_first(BidiIterator i, size_type pos, bool escape_k = false)\n   {\n      BOOST_REGEX_ASSERT(pos+2 < m_subs.size());\n      if(pos || escape_k)\n      {\n         m_subs[pos+2].first = i;\n         if(escape_k)\n         {\n            m_subs[1].second = i;\n            m_subs[1].matched = (m_subs[1].first != m_subs[1].second);\n         }\n      }\n      else\n         set_first(i);\n   }\n   void BOOST_REGEX_CALL maybe_assign(const match_results<BidiIterator, Allocator>& m);\n\n   void BOOST_REGEX_CALL set_named_subs(boost::shared_ptr<named_sub_type> subs)\n   {\n      m_named_subs = subs;\n   }\n\nprivate:\n   //\n   // Error handler called when an uninitialized match_results is accessed:\n   //\n   static void raise_logic_error()\n   {\n      std::logic_error e(\"Attempt to access an uninitialized boost::match_results<> class.\");\n      boost::throw_exception(e);\n   }\n\n\n   vector_type            m_subs;                      // subexpressions\n   BidiIterator   m_base;                              // where the search started from\n   sub_match<BidiIterator> m_null;                     // a null match\n   boost::shared_ptr<named_sub_type> m_named_subs;     // Shared copy of named subs in the regex object\n   int m_last_closed_paren;                            // Last ) to be seen - used for formatting\n   bool m_is_singular;                                 // True if our stored iterators are singular\n};\n\ntemplate <class BidiIterator, class Allocator>\nvoid BOOST_REGEX_CALL match_results<BidiIterator, Allocator>::maybe_assign(const match_results<BidiIterator, Allocator>& m)\n{\n   if(m_is_singular)\n   {\n      *this = m;\n      return;\n   }\n   const_iterator p1, p2;\n   p1 = begin();\n   p2 = m.begin();\n   //\n   // Distances are measured from the start of *this* match, unless this isn't\n   // a valid match in which case we use the start of the whole sequence.  Note that\n   // no subsequent match-candidate can ever be to the left of the first match found.\n   // This ensures that when we are using bidirectional iterators, that distances \n   // measured are as short as possible, and therefore as efficient as possible\n   // to compute.  Finally note that we don't use the \"matched\" data member to test\n   // whether a sub-expression is a valid match, because partial matches set this\n   // to false for sub-expression 0.\n   //\n   BidiIterator l_end = this->suffix().second;\n   BidiIterator l_base = (p1->first == l_end) ? this->prefix().first : (*this)[0].first;\n   difference_type len1 = 0;\n   difference_type len2 = 0;\n   difference_type base1 = 0;\n   difference_type base2 = 0;\n   std::size_t i;\n   for(i = 0; i < size(); ++i, ++p1, ++p2)\n   {\n      //\n      // Leftmost takes priority over longest; handle special cases\n      // where distances need not be computed first (an optimisation\n      // for bidirectional iterators: ensure that we don't accidently\n      // compute the length of the whole sequence, as this can be really\n      // expensive).\n      //\n      if(p1->first == l_end)\n      {\n         if(p2->first != l_end)\n         {\n            // p2 must be better than p1, and no need to calculate\n            // actual distances:\n            base1 = 1;\n            base2 = 0;\n            break;\n         }\n         else\n         {\n            // *p1 and *p2 are either unmatched or match end-of sequence,\n            // either way no need to calculate distances:\n            if((p1->matched == false) && (p2->matched == true))\n               break;\n            if((p1->matched == true) && (p2->matched == false))\n               return;\n            continue;\n         }\n      }\n      else if(p2->first == l_end)\n      {\n         // p1 better than p2, and no need to calculate distances:\n         return;\n      }\n      base1 = ::boost::BOOST_REGEX_DETAIL_NS::distance(l_base, p1->first);\n      base2 = ::boost::BOOST_REGEX_DETAIL_NS::distance(l_base, p2->first);\n      BOOST_REGEX_ASSERT(base1 >= 0);\n      BOOST_REGEX_ASSERT(base2 >= 0);\n      if(base1 < base2) return;\n      if(base2 < base1) break;\n\n      len1 = ::boost::BOOST_REGEX_DETAIL_NS::distance((BidiIterator)p1->first, (BidiIterator)p1->second);\n      len2 = ::boost::BOOST_REGEX_DETAIL_NS::distance((BidiIterator)p2->first, (BidiIterator)p2->second);\n      BOOST_REGEX_ASSERT(len1 >= 0);\n      BOOST_REGEX_ASSERT(len2 >= 0);\n      if((len1 != len2) || ((p1->matched == false) && (p2->matched == true)))\n         break;\n      if((p1->matched == true) && (p2->matched == false))\n         return;\n   }\n   if(i == size())\n      return;\n   if(base2 < base1)\n      *this = m;\n   else if((len2 > len1) || ((p1->matched == false) && (p2->matched == true)) )\n      *this = m;\n}\n\ntemplate <class BidiIterator, class Allocator>\nvoid swap(match_results<BidiIterator, Allocator>& a, match_results<BidiIterator, Allocator>& b)\n{\n   a.swap(b);\n}\n\n#ifndef BOOST_NO_STD_LOCALE\ntemplate <class charT, class traits, class BidiIterator, class Allocator>\nstd::basic_ostream<charT, traits>&\n   operator << (std::basic_ostream<charT, traits>& os,\n                const match_results<BidiIterator, Allocator>& s)\n{\n   return (os << s.str());\n}\n#else\ntemplate <class BidiIterator, class Allocator>\nstd::ostream& operator << (std::ostream& os,\n                           const match_results<BidiIterator, Allocator>& s)\n{\n   return (os << s.str());\n}\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/mem_block_cache.hpp",
    "content": " /*\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         mem_block_cache.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: memory block cache used by the non-recursive matcher.\n  */\n\n#ifndef BOOST_REGEX_V4_MEM_BLOCK_CACHE_HPP\n#define BOOST_REGEX_V4_MEM_BLOCK_CACHE_HPP\n\n#include <new>\n#ifdef BOOST_HAS_THREADS\n#include <boost/regex/pending/static_mutex.hpp>\n#endif\n\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n\n#ifndef BOOST_NO_CXX11_HDR_ATOMIC\n  #include <atomic>\n  #if ATOMIC_POINTER_LOCK_FREE == 2\n    #define BOOST_REGEX_MEM_BLOCK_CACHE_LOCK_FREE\n    #define BOOST_REGEX_ATOMIC_POINTER std::atomic\n  #endif\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\n#ifdef BOOST_REGEX_MEM_BLOCK_CACHE_LOCK_FREE /* lock free implementation */\nstruct mem_block_cache\n{\n  std::atomic<void*> cache[BOOST_REGEX_MAX_CACHE_BLOCKS];\n\n   ~mem_block_cache()\n   {\n     for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) {\n       if (cache[i].load()) ::operator delete(cache[i].load());\n     }\n   }\n   void* get()\n   {\n     for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) {\n       void* p = cache[i].load();\n       if (p != NULL) {\n         if (cache[i].compare_exchange_strong(p, NULL)) return p;\n       }\n     }\n     return ::operator new(BOOST_REGEX_BLOCKSIZE);\n   }\n   void put(void* ptr)\n   {\n     for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) {\n       void* p = cache[i].load();\n       if (p == NULL) {\n         if (cache[i].compare_exchange_strong(p, ptr)) return;\n       }\n     }\n     ::operator delete(ptr);\n   }\n\n   static mem_block_cache& instance()\n   {\n      static mem_block_cache block_cache = { { {nullptr} } };\n      return block_cache;\n   }\n};\n\n\n#else /* lock-based implementation */\n\n\nstruct mem_block_node\n{\n   mem_block_node* next;\n};\n\nstruct mem_block_cache\n{\n   // this member has to be statically initialsed:\n   mem_block_node* next;\n   unsigned cached_blocks;\n#ifdef BOOST_HAS_THREADS\n   boost::static_mutex mut;\n#endif\n\n   ~mem_block_cache()\n   {\n      while(next)\n      {\n         mem_block_node* old = next;\n         next = next->next;\n         ::operator delete(old);\n      }\n   }\n   void* get()\n   {\n#ifdef BOOST_HAS_THREADS\n      boost::static_mutex::scoped_lock g(mut);\n#endif\n     if(next)\n      {\n         mem_block_node* result = next;\n         next = next->next;\n         --cached_blocks;\n         return result;\n      }\n      return ::operator new(BOOST_REGEX_BLOCKSIZE);\n   }\n   void put(void* p)\n   {\n#ifdef BOOST_HAS_THREADS\n      boost::static_mutex::scoped_lock g(mut);\n#endif\n      if(cached_blocks >= BOOST_REGEX_MAX_CACHE_BLOCKS)\n      {\n         ::operator delete(p);\n      }\n      else\n      {\n         mem_block_node* old = static_cast<mem_block_node*>(p);\n         old->next = next;\n         next = old;\n         ++cached_blocks;\n      }\n   }\n   static mem_block_cache& instance()\n   {\n#ifdef BOOST_HAS_THREADS\n      static mem_block_cache block_cache = { 0, 0, BOOST_STATIC_MUTEX_INIT, };\n#else\n      static mem_block_cache block_cache = { 0, 0, };\n#endif\n      return block_cache;\n   }\n};\n#endif\n\n#if BOOST_REGEX_MAX_CACHE_BLOCKS == 0\n\ninline void* BOOST_REGEX_CALL get_mem_block()\n{\n   return ::operator new(BOOST_REGEX_BLOCKSIZE);\n}\n\ninline void BOOST_REGEX_CALL put_mem_block(void* p)\n{\n   ::operator delete(p);\n}\n\n#else\n\ninline void* BOOST_REGEX_CALL get_mem_block()\n{\n   return mem_block_cache::instance().get();\n}\n\ninline void BOOST_REGEX_CALL put_mem_block(void* p)\n{\n   mem_block_cache::instance().put(p);\n}\n\n#endif\n}\n} // namespace boost\n\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/object_cache.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         object_cache.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Implements a generic object cache.\n  */\n\n#ifndef BOOST_REGEX_OBJECT_CACHE_HPP\n#define BOOST_REGEX_OBJECT_CACHE_HPP\n\n#include <boost/regex/config.hpp>\n#include <boost/shared_ptr.hpp>\n#include <map>\n#include <list>\n#include <stdexcept>\n#include <string>\n#ifdef BOOST_HAS_THREADS\n#include <boost/regex/pending/static_mutex.hpp>\n#endif\n\nnamespace boost{\n\ntemplate <class Key, class Object>\nclass object_cache\n{\npublic:\n   typedef std::pair< ::boost::shared_ptr<Object const>, Key const*> value_type;\n   typedef std::list<value_type> list_type;\n   typedef typename list_type::iterator list_iterator;\n   typedef std::map<Key, list_iterator> map_type;\n   typedef typename map_type::iterator map_iterator;\n   typedef typename list_type::size_type size_type;\n   static boost::shared_ptr<Object const> get(const Key& k, size_type l_max_cache_size);\n\nprivate:\n   static boost::shared_ptr<Object const> do_get(const Key& k, size_type l_max_cache_size);\n\n   struct data\n   {\n      list_type   cont;\n      map_type    index;\n   };\n\n   // Needed by compilers not implementing the resolution to DR45. For reference,\n   // see http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#45.\n   friend struct data;\n};\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4702)\n#endif\ntemplate <class Key, class Object>\nboost::shared_ptr<Object const> object_cache<Key, Object>::get(const Key& k, size_type l_max_cache_size)\n{\n#ifdef BOOST_HAS_THREADS\n   static boost::static_mutex mut = BOOST_STATIC_MUTEX_INIT;\n   boost::static_mutex::scoped_lock l(mut);\n   if (l)\n   {\n      return do_get(k, l_max_cache_size);\n   }\n   //\n   // what do we do if the lock fails?\n   // for now just throw, but we should never really get here...\n   //\n   ::boost::throw_exception(std::runtime_error(\"Error in thread safety code: could not acquire a lock\"));\n#if defined(BOOST_NO_UNREACHABLE_RETURN_DETECTION) || defined(BOOST_NO_EXCEPTIONS)\n   return boost::shared_ptr<Object>();\n#endif\n#else\n   return do_get(k, l_max_cache_size);\n#endif\n}\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\ntemplate <class Key, class Object>\nboost::shared_ptr<Object const> object_cache<Key, Object>::do_get(const Key& k, size_type l_max_cache_size)\n{\n   typedef typename object_cache<Key, Object>::data object_data;\n   typedef typename map_type::size_type map_size_type;\n   static object_data s_data;\n\n   //\n   // see if the object is already in the cache:\n   //\n   map_iterator mpos = s_data.index.find(k);\n   if(mpos != s_data.index.end())\n   {\n      //\n      // Eureka! \n      // We have a cached item, bump it up the list and return it:\n      //\n      if(--(s_data.cont.end()) != mpos->second)\n      {\n         // splice out the item we want to move:\n         list_type temp;\n         temp.splice(temp.end(), s_data.cont, mpos->second);\n         // and now place it at the end of the list:\n         s_data.cont.splice(s_data.cont.end(), temp, temp.begin());\n         BOOST_REGEX_ASSERT(*(s_data.cont.back().second) == k);\n         // update index with new position:\n         mpos->second = --(s_data.cont.end());\n         BOOST_REGEX_ASSERT(&(mpos->first) == mpos->second->second);\n         BOOST_REGEX_ASSERT(&(mpos->first) == s_data.cont.back().second);\n      }\n      return s_data.cont.back().first;\n   }\n   //\n   // if we get here then the item is not in the cache,\n   // so create it:\n   //\n   boost::shared_ptr<Object const> result(new Object(k));\n   //\n   // Add it to the list, and index it:\n   //\n   s_data.cont.push_back(value_type(result, static_cast<Key const*>(0)));\n   s_data.index.insert(std::make_pair(k, --(s_data.cont.end())));\n   s_data.cont.back().second = &(s_data.index.find(k)->first);\n   map_size_type s = s_data.index.size();\n   BOOST_REGEX_ASSERT(s_data.index[k]->first.get() == result.get());\n   BOOST_REGEX_ASSERT(&(s_data.index.find(k)->first) == s_data.cont.back().second);\n   BOOST_REGEX_ASSERT(s_data.index.find(k)->first == k);\n   if(s > l_max_cache_size)\n   {\n      //\n      // We have too many items in the list, so we need to start\n      // popping them off the back of the list, but only if they're\n      // being held uniquely by us:\n      //\n      list_iterator pos = s_data.cont.begin();\n      list_iterator last = s_data.cont.end();\n      while((pos != last) && (s > l_max_cache_size))\n      {\n         if(pos->first.unique())\n         {\n            list_iterator condemmed(pos);\n            ++pos;\n            // now remove the items from our containers, \n            // then order has to be as follows:\n            BOOST_REGEX_ASSERT(s_data.index.find(*(condemmed->second)) != s_data.index.end());\n            s_data.index.erase(*(condemmed->second));\n            s_data.cont.erase(condemmed); \n            --s;\n         }\n         else\n            ++pos;\n      }\n      BOOST_REGEX_ASSERT(s_data.index[k]->first.get() == result.get());\n      BOOST_REGEX_ASSERT(&(s_data.index.find(k)->first) == s_data.cont.back().second);\n      BOOST_REGEX_ASSERT(s_data.index.find(k)->first == k);\n   }\n   return result;\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/pattern_except.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         pattern_except.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares pattern-matching exception classes.\n  */\n\n#ifndef BOOST_RE_V4_PAT_EXCEPT_HPP\n#define BOOST_RE_V4_PAT_EXCEPT_HPP\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n\n#include <cstddef>\n#include <stdexcept>\n#include <boost/regex/v4/error_type.hpp>\n#include <boost/regex/v4/regex_traits_defaults.hpp>\n\nnamespace boost{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable : 4275)\n#if BOOST_MSVC >= 1800\n#pragma warning(disable : 26812)\n#endif\n#endif\nclass regex_error : public std::runtime_error\n{\npublic:\n   explicit regex_error(const std::string& s, regex_constants::error_type err = regex_constants::error_unknown, std::ptrdiff_t pos = 0)\n      : std::runtime_error(s)\n      , m_error_code(err)\n      , m_position(pos)\n   {\n   }\n   explicit regex_error(regex_constants::error_type err)\n      : std::runtime_error(::boost::BOOST_REGEX_DETAIL_NS::get_default_error_string(err))\n      , m_error_code(err)\n      , m_position(0)\n   {\n   }\n   ~regex_error() BOOST_NOEXCEPT_OR_NOTHROW BOOST_OVERRIDE {}\n   regex_constants::error_type code()const\n   { return m_error_code; }\n   std::ptrdiff_t position()const\n   { return m_position; }\n   void raise()const \n   {\n#ifndef BOOST_NO_EXCEPTIONS\n#ifndef BOOST_REGEX_STANDALONE\n      ::boost::throw_exception(*this);\n#else\n      throw* this;\n#endif\n#endif\n   }\nprivate:\n   regex_constants::error_type m_error_code;\n   std::ptrdiff_t m_position;\n};\n\ntypedef regex_error bad_pattern;\ntypedef regex_error bad_expression;\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class E>\ninline void raise_runtime_error(const E& ex)\n{\n#ifndef BOOST_REGEX_STANDALONE\n   ::boost::throw_exception(ex);\n#else\n   throw ex;\n#endif\n}\n\ntemplate <class traits>\nvoid raise_error(const traits& t, regex_constants::error_type code)\n{\n   (void)t;  // warning suppression\n   regex_error e(t.error_string(code), code, 0);\n   ::boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(e);\n}\n\n}\n\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/perl_matcher.hpp",
    "content": "/*\n *\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n#ifndef BOOST_REGEX_MATCHER_HPP\n#define BOOST_REGEX_MATCHER_HPP\n\n#include <boost/regex/v4/iterator_category.hpp>\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#pragma warning(disable : 4251)\n#if BOOST_MSVC < 1700\n#     pragma warning(disable : 4231)\n#endif\n#  if BOOST_MSVC < 1600\n#     pragma warning(disable : 4660)\n#  endif\n#if BOOST_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\n//\n// error checking API:\n//\ninline void BOOST_REGEX_CALL verify_options(boost::regex_constants::syntax_option_type, match_flag_type mf)\n{\n   //\n   // can't mix match_extra with POSIX matching rules:\n   //\n   if ((mf & match_extra) && (mf & match_posix))\n   {\n      std::logic_error msg(\"Usage Error: Can't mix regular expression captures with POSIX matching rules\");\n      throw_exception(msg);\n   }\n}\n//\n// function can_start:\n//\ntemplate <class charT>\ninline bool can_start(charT c, const unsigned char* map, unsigned char mask)\n{\n   return ((c < static_cast<charT>(0)) ? true : ((c >= static_cast<charT>(1 << CHAR_BIT)) ? true : map[c] & mask));\n}\ninline bool can_start(char c, const unsigned char* map, unsigned char mask)\n{\n   return map[(unsigned char)c] & mask;\n}\ninline bool can_start(signed char c, const unsigned char* map, unsigned char mask)\n{\n   return map[(unsigned char)c] & mask;\n}\ninline bool can_start(unsigned char c, const unsigned char* map, unsigned char mask)\n{\n   return map[c] & mask;\n}\ninline bool can_start(unsigned short c, const unsigned char* map, unsigned char mask)\n{\n   return ((c >= (1 << CHAR_BIT)) ? true : map[c] & mask);\n}\n#if !defined(__hpux) && !defined(__WINSCW__)// WCHAR_MIN not usable in pp-directives.\n#if defined(WCHAR_MIN) && (WCHAR_MIN == 0) && !defined(BOOST_NO_INTRINSIC_WCHAR_T)\ninline bool can_start(wchar_t c, const unsigned char* map, unsigned char mask)\n{\n   return ((c >= static_cast<wchar_t>(1u << CHAR_BIT)) ? true : map[c] & mask);\n}\n#endif\n#endif\n#if !defined(BOOST_NO_INTRINSIC_WCHAR_T)\ninline bool can_start(unsigned int c, const unsigned char* map, unsigned char mask)\n{\n   return (((c >= static_cast<unsigned int>(1u << CHAR_BIT)) ? true : map[c] & mask));\n}\n#endif\n\n\n//\n// Unfortunately Rogue Waves standard library appears to have a bug\n// in std::basic_string::compare that results in erroneous answers\n// in some cases (tested with Borland C++ 5.1, Rogue Wave lib version\n// 0x020101) the test case was:\n// {39135,0} < {0xff,0}\n// which succeeds when it should not.\n//\n#ifndef _RWSTD_VER\ntemplate <class C, class T, class A>\ninline int string_compare(const std::basic_string<C,T,A>& s, const C* p)\n{ \n   if(0 == *p)\n   {\n      if(s.empty() || ((s.size() == 1) && (s[0] == 0)))\n         return 0;\n   }\n   return s.compare(p); \n}\n#else\ntemplate <class C, class T, class A>\ninline int string_compare(const std::basic_string<C,T,A>& s, const C* p)\n{ \n   if(0 == *p)\n   {\n      if(s.empty() || ((s.size() == 1) && (s[0] == 0)))\n         return 0;\n   }\n   return s.compare(p); \n}\ninline int string_compare(const std::string& s, const char* p)\n{ return std::strcmp(s.c_str(), p); }\n# ifndef BOOST_NO_WREGEX\ninline int string_compare(const std::wstring& s, const wchar_t* p)\n{ return std::wcscmp(s.c_str(), p); }\n#endif\n#endif\ntemplate <class Seq, class C>\ninline int string_compare(const Seq& s, const C* p)\n{\n   std::size_t i = 0;\n   while((i < s.size()) && (p[i] == s[i]))\n   {\n      ++i;\n   }\n   return (i == s.size()) ? -(int)p[i] : (int)s[i] - (int)p[i];\n}\n# define STR_COMP(s,p) string_compare(s,p)\n\ntemplate<class charT>\ninline const charT* re_skip_past_null(const charT* p)\n{\n  while (*p != static_cast<charT>(0)) ++p;\n  return ++p;\n}\n\ntemplate <class iterator, class charT, class traits_type, class char_classT>\niterator BOOST_REGEX_CALL re_is_set_member(iterator next, \n                          iterator last, \n                          const re_set_long<char_classT>* set_, \n                          const regex_data<charT, traits_type>& e, bool icase)\n{   \n   const charT* p = reinterpret_cast<const charT*>(set_+1);\n   iterator ptr;\n   unsigned int i;\n   //bool icase = e.m_flags & regex_constants::icase;\n\n   if(next == last) return next;\n\n   typedef typename traits_type::string_type traits_string_type;\n   const ::boost::regex_traits_wrapper<traits_type>& traits_inst = *(e.m_ptraits);\n   \n   // dwa 9/13/00 suppress incorrect MSVC warning - it claims this is never\n   // referenced\n   (void)traits_inst;\n\n   // try and match a single character, could be a multi-character\n   // collating element...\n   for(i = 0; i < set_->csingles; ++i)\n   {\n      ptr = next;\n      if(*p == static_cast<charT>(0))\n      {\n         // treat null string as special case:\n         if(traits_inst.translate(*ptr, icase))\n         {\n            ++p;\n            continue;\n         }\n         return set_->isnot ? next : (ptr == next) ? ++next : ptr;\n      }\n      else\n      {\n         while(*p && (ptr != last))\n         {\n            if(traits_inst.translate(*ptr, icase) != *p)\n               break;\n            ++p;\n            ++ptr;\n         }\n\n         if(*p == static_cast<charT>(0)) // if null we've matched\n            return set_->isnot ? next : (ptr == next) ? ++next : ptr;\n\n         p = re_skip_past_null(p);     // skip null\n      }\n   }\n\n   charT col = traits_inst.translate(*next, icase);\n\n\n   if(set_->cranges || set_->cequivalents)\n   {\n      traits_string_type s1;\n      //\n      // try and match a range, NB only a single character can match\n      if(set_->cranges)\n      {\n         if((e.m_flags & regex_constants::collate) == 0)\n            s1.assign(1, col);\n         else\n         {\n            charT a[2] = { col, charT(0), };\n            s1 = traits_inst.transform(a, a + 1);\n         }\n         for(i = 0; i < set_->cranges; ++i)\n         {\n            if(STR_COMP(s1, p) >= 0)\n            {\n               do{ ++p; }while(*p);\n               ++p;\n               if(STR_COMP(s1, p) <= 0)\n                  return set_->isnot ? next : ++next;\n            }\n            else\n            {\n               // skip first string\n               do{ ++p; }while(*p);\n               ++p;\n            }\n            // skip second string\n            do{ ++p; }while(*p);\n            ++p;\n         }\n      }\n      //\n      // try and match an equivalence class, NB only a single character can match\n      if(set_->cequivalents)\n      {\n         charT a[2] = { col, charT(0), };\n         s1 = traits_inst.transform_primary(a, a +1);\n         for(i = 0; i < set_->cequivalents; ++i)\n         {\n            if(STR_COMP(s1, p) == 0)\n               return set_->isnot ? next : ++next;\n            // skip string\n            do{ ++p; }while(*p);\n            ++p;\n         }\n      }\n   }\n   if(traits_inst.isctype(col, set_->cclasses) == true)\n      return set_->isnot ? next : ++next;\n   if((set_->cnclasses != 0) && (traits_inst.isctype(col, set_->cnclasses) == false))\n      return set_->isnot ? next : ++next;\n   return set_->isnot ? ++next : next;\n}\n\ntemplate <class BidiIterator>\nclass repeater_count\n{\n   repeater_count** stack;\n   repeater_count* next;\n   int state_id;\n   std::size_t count;        // the number of iterations so far\n   BidiIterator start_pos;   // where the last repeat started\n\n   repeater_count* unwind_until(int n, repeater_count* p, int current_recursion_id)\n   { \n      while(p && (p->state_id != n))\n      {\n         if(-2 - current_recursion_id == p->state_id)\n            return 0;\n         p = p->next;\n         if(p && (p->state_id < 0))\n         {\n            p = unwind_until(p->state_id, p, current_recursion_id);\n            if(!p)\n               return p;\n            p = p->next;\n         }\n      }\n      return p;\n   }\npublic:\n   repeater_count(repeater_count** s) : stack(s), next(0), state_id(-1), count(0), start_pos() {}\n   \n   repeater_count(int i, repeater_count** s, BidiIterator start, int current_recursion_id)\n      : start_pos(start)\n   {\n      state_id = i;\n      stack = s;\n      next = *stack;\n      *stack = this;\n      if((state_id > next->state_id) && (next->state_id >= 0))\n         count = 0;\n      else\n      {\n         repeater_count* p = next;\n         p = unwind_until(state_id, p, current_recursion_id);\n         if(p)\n         {\n            count = p->count;\n            start_pos = p->start_pos;\n         }\n         else\n            count = 0;\n      }\n   }\n   ~repeater_count()\n   {\n      if(next)\n         *stack = next;\n   }\n   std::size_t get_count() { return count; }\n   int get_id() { return state_id; }\n   std::size_t operator++() { return ++count; }\n   bool check_null_repeat(const BidiIterator& pos, std::size_t max)\n   {\n      // this is called when we are about to start a new repeat,\n      // if the last one was NULL move our count to max,\n      // otherwise save the current position.\n      bool result = (count == 0) ? false : (pos == start_pos);\n      if(result)\n         count = max;\n      else\n         start_pos = pos;\n      return result;\n   }\n};\n\nstruct saved_state;\n\nenum saved_state_type\n{\n   saved_type_end = 0,\n   saved_type_paren = 1,\n   saved_type_recurse = 2,\n   saved_type_assertion = 3,\n   saved_state_alt = 4,\n   saved_state_repeater_count = 5,\n   saved_state_extra_block = 6,\n   saved_state_greedy_single_repeat = 7,\n   saved_state_rep_slow_dot = 8,\n   saved_state_rep_fast_dot = 9,\n   saved_state_rep_char = 10,\n   saved_state_rep_short_set = 11,\n   saved_state_rep_long_set = 12,\n   saved_state_non_greedy_long_repeat = 13, \n   saved_state_count = 14\n};\n\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#if BOOST_MSVC >= 1800\n#pragma warning(disable:26495)\n#endif\n#endif\ntemplate <class Results>\nstruct recursion_info\n{\n   typedef typename Results::value_type value_type;\n   typedef typename value_type::iterator iterator;\n   int idx;\n   const re_syntax_base* preturn_address;\n   Results results;\n   repeater_count<iterator>* repeater_stack;\n   iterator location_of_start;\n};\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n\ntemplate <class BidiIterator, class Allocator, class traits>\nclass perl_matcher\n{\npublic:\n   typedef typename traits::char_type char_type;\n   typedef perl_matcher<BidiIterator, Allocator, traits> self_type;\n   typedef bool (self_type::*matcher_proc_type)();\n   typedef std::size_t traits_size_type;\n   typedef typename is_byte<char_type>::width_type width_type;\n   typedef typename regex_iterator_traits<BidiIterator>::difference_type difference_type;\n   typedef match_results<BidiIterator, Allocator> results_type;\n\n   perl_matcher(BidiIterator first, BidiIterator end, \n      match_results<BidiIterator, Allocator>& what, \n      const basic_regex<char_type, traits>& e,\n      match_flag_type f,\n      BidiIterator l_base)\n      :  m_result(what), base(first), last(end), \n         position(first), backstop(l_base), re(e), traits_inst(e.get_traits()), \n         m_independent(false), next_count(&rep_obj), rep_obj(&next_count)\n#ifdef BOOST_REGEX_NON_RECURSIVE\n      , m_recursions(0)\n#endif\n   {\n      construct_init(e, f);\n   }\n\n   bool match();\n   bool find();\n\n   void setf(match_flag_type f)\n   { m_match_flags |= f; }\n   void unsetf(match_flag_type f)\n   { m_match_flags &= ~f; }\n\nprivate:\n   void construct_init(const basic_regex<char_type, traits>& e, match_flag_type f);\n\n   bool find_imp();\n   bool match_imp();\n#ifdef BOOST_REGEX_HAS_MS_STACK_GUARD\n   typedef bool (perl_matcher::*protected_proc_type)();\n   bool protected_call(protected_proc_type);\n#endif\n   void estimate_max_state_count(std::random_access_iterator_tag*);\n   void estimate_max_state_count(void*);\n   bool match_prefix();\n   bool match_all_states();\n\n   // match procs, stored in s_match_vtable:\n   bool match_startmark();\n   bool match_endmark();\n   bool match_literal();\n   bool match_start_line();\n   bool match_end_line();\n   bool match_wild();\n   bool match_match();\n   bool match_word_boundary();\n   bool match_within_word();\n   bool match_word_start();\n   bool match_word_end();\n   bool match_buffer_start();\n   bool match_buffer_end();\n   bool match_backref();\n   bool match_long_set();\n   bool match_set();\n   bool match_jump();\n   bool match_alt();\n   bool match_rep();\n   bool match_combining();\n   bool match_soft_buffer_end();\n   bool match_restart_continue();\n   bool match_long_set_repeat();\n   bool match_set_repeat();\n   bool match_char_repeat();\n   bool match_dot_repeat_fast();\n   bool match_dot_repeat_slow();\n   bool match_dot_repeat_dispatch()\n   {\n      return ::boost::is_random_access_iterator<BidiIterator>::value ? match_dot_repeat_fast() : match_dot_repeat_slow();\n   }\n   bool match_backstep();\n   bool match_assert_backref();\n   bool match_toggle_case();\n#ifdef BOOST_REGEX_RECURSIVE\n   bool backtrack_till_match(std::size_t count);\n#endif\n   bool match_recursion();\n   bool match_fail();\n   bool match_accept();\n   bool match_commit();\n   bool match_then();\n   bool skip_until_paren(int index, bool match = true);\n\n   // find procs stored in s_find_vtable:\n   bool find_restart_any();\n   bool find_restart_word();\n   bool find_restart_line();\n   bool find_restart_buf();\n   bool find_restart_lit();\n\nprivate:\n   // final result structure to be filled in:\n   match_results<BidiIterator, Allocator>& m_result;\n   // temporary result for POSIX matches:\n   scoped_ptr<match_results<BidiIterator, Allocator> > m_temp_match;\n   // pointer to actual result structure to fill in:\n   match_results<BidiIterator, Allocator>* m_presult;\n   // start of sequence being searched:\n   BidiIterator base;\n   // end of sequence being searched:\n   BidiIterator last; \n   // current character being examined:\n   BidiIterator position;\n   // where to restart next search after failed match attempt:\n   BidiIterator restart;\n   // where the current search started from, acts as base for $` during grep:\n   BidiIterator search_base;\n   // how far we can go back when matching lookbehind:\n   BidiIterator backstop;\n   // the expression being examined:\n   const basic_regex<char_type, traits>& re;\n   // the expression's traits class:\n   const ::boost::regex_traits_wrapper<traits>& traits_inst;\n   // the next state in the machine being matched:\n   const re_syntax_base* pstate;\n   // matching flags in use:\n   match_flag_type m_match_flags;\n   // how many states we have examined so far:\n   std::ptrdiff_t state_count;\n   // max number of states to examine before giving up:\n   std::ptrdiff_t max_state_count;\n   // whether we should ignore case or not:\n   bool icase;\n   // set to true when (position == last), indicates that we may have a partial match:\n   bool m_has_partial_match;\n   // set to true whenever we get a match:\n   bool m_has_found_match;\n   // set to true whenever we're inside an independent sub-expression:\n   bool m_independent;\n   // the current repeat being examined:\n   repeater_count<BidiIterator>* next_count;\n   // the first repeat being examined (top of linked list):\n   repeater_count<BidiIterator> rep_obj;\n   // the mask to pass when matching word boundaries:\n   typename traits::char_class_type m_word_mask;\n   // the bitmask to use when determining whether a match_any matches a newline or not:\n   unsigned char match_any_mask;\n   // recursion information:\n   std::vector<recursion_info<results_type> > recursion_stack;\n#ifdef BOOST_REGEX_RECURSIVE\n   // Set to false by a (*COMMIT):\n   bool m_can_backtrack;\n   bool m_have_accept;\n   bool m_have_then;\n#endif\n#ifdef BOOST_REGEX_NON_RECURSIVE\n   //\n   // additional members for non-recursive version:\n   //\n   typedef bool (self_type::*unwind_proc_type)(bool);\n\n   void extend_stack();\n   bool unwind(bool);\n   bool unwind_end(bool);\n   bool unwind_paren(bool);\n   bool unwind_recursion_stopper(bool);\n   bool unwind_assertion(bool);\n   bool unwind_alt(bool);\n   bool unwind_repeater_counter(bool);\n   bool unwind_extra_block(bool);\n   bool unwind_greedy_single_repeat(bool);\n   bool unwind_slow_dot_repeat(bool);\n   bool unwind_fast_dot_repeat(bool);\n   bool unwind_char_repeat(bool);\n   bool unwind_short_set_repeat(bool);\n   bool unwind_long_set_repeat(bool);\n   bool unwind_non_greedy_repeat(bool);\n   bool unwind_recursion(bool);\n   bool unwind_recursion_pop(bool);\n   bool unwind_commit(bool);\n   bool unwind_then(bool);\n   bool unwind_case(bool);\n   void destroy_single_repeat();\n   void push_matched_paren(int index, const sub_match<BidiIterator>& sub);\n   void push_recursion_stopper();\n   void push_assertion(const re_syntax_base* ps, bool positive);\n   void push_alt(const re_syntax_base* ps);\n   void push_repeater_count(int i, repeater_count<BidiIterator>** s);\n   void push_single_repeat(std::size_t c, const re_repeat* r, BidiIterator last_position, int state_id);\n   void push_non_greedy_repeat(const re_syntax_base* ps);\n   void push_recursion(int idx, const re_syntax_base* p, results_type* presults, results_type* presults2);\n   void push_recursion_pop();\n   void push_case_change(bool);\n\n   // pointer to base of stack:\n   saved_state* m_stack_base;\n   // pointer to current stack position:\n   saved_state* m_backup_state;\n   // how many memory blocks have we used up?:\n   unsigned used_block_count;\n   // determines what value to return when unwinding from recursion,\n   // allows for mixed recursive/non-recursive algorithm:\n   bool m_recursive_result;\n   // We have unwound to a lookahead/lookbehind, used by COMMIT/PRUNE/SKIP:\n   bool m_unwound_lookahead;\n   // We have unwound to an alternative, used by THEN:\n   bool m_unwound_alt;\n   // We are unwinding a commit - used by independent subs to determine whether to stop there or carry on unwinding:\n   //bool m_unwind_commit;\n   // Recursion limit:\n   unsigned m_recursions;\n#endif\n\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#if BOOST_MSVC >= 1800\n#pragma warning(disable:26495)\n#endif\n#endif\n   // these operations aren't allowed, so are declared private,\n   // bodies are provided to keep explicit-instantiation requests happy:\n   perl_matcher& operator=(const perl_matcher&)\n   {\n      return *this;\n   }\n   perl_matcher(const perl_matcher& that)\n      : m_result(that.m_result), re(that.re), traits_inst(that.traits_inst), rep_obj(0) {}\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n};\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n//\n// include the implementation of perl_matcher:\n//\n#ifdef BOOST_REGEX_RECURSIVE\n#include <boost/regex/v4/perl_matcher_recursive.hpp>\n#else\n#include <boost/regex/v4/perl_matcher_non_recursive.hpp>\n#endif\n// this one has to be last:\n#include <boost/regex/v4/perl_matcher_common.hpp>\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/perl_matcher_common.hpp",
    "content": "/*\n *\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         perl_matcher_common.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Definitions of perl_matcher member functions that are \n  *                common to both the recursive and non-recursive versions.\n  */\n\n#ifndef BOOST_REGEX_V4_PERL_MATCHER_COMMON_HPP\n#define BOOST_REGEX_V4_PERL_MATCHER_COMMON_HPP\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#if BOOST_MSVC >= 1800\n#pragma warning(disable: 26812)\n#endif\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_BORLANDC\n#  pragma option push -w-8008 -w-8066\n#endif\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#if BOOST_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#pragma warning(disable:26812)\n#endif\n   template <class BidiIterator, class Allocator, class traits>\nvoid perl_matcher<BidiIterator, Allocator, traits>::construct_init(const basic_regex<char_type, traits>& e, match_flag_type f)\n{ \n   typedef typename regex_iterator_traits<BidiIterator>::iterator_category category;\n   typedef typename basic_regex<char_type, traits>::flag_type expression_flag_type;\n   \n   if(e.empty())\n   {\n      // precondition failure: e is not a valid regex.\n      std::invalid_argument ex(\"Invalid regular expression object\");\n      boost::throw_exception(ex);\n   }\n   pstate = 0;\n   m_match_flags = f;\n   estimate_max_state_count(static_cast<category*>(0));\n   expression_flag_type re_f = re.flags();\n   icase = re_f & regex_constants::icase;\n   if(!(m_match_flags & (match_perl|match_posix)))\n   {\n      if((re_f & (regbase::main_option_type|regbase::no_perl_ex)) == 0)\n         m_match_flags |= match_perl;\n      else if((re_f & (regbase::main_option_type|regbase::emacs_ex)) == (regbase::basic_syntax_group|regbase::emacs_ex))\n         m_match_flags |= match_perl;\n      else if((re_f & (regbase::main_option_type|regbase::literal)) == (regbase::literal))\n         m_match_flags |= match_perl;\n      else\n         m_match_flags |= match_posix;\n   }\n   if(m_match_flags & match_posix)\n   {\n      m_temp_match.reset(new match_results<BidiIterator, Allocator>());\n      m_presult = m_temp_match.get();\n   }\n   else\n      m_presult = &m_result;\n#ifdef BOOST_REGEX_NON_RECURSIVE\n   m_stack_base = 0;\n   m_backup_state = 0;\n#elif defined(BOOST_REGEX_RECURSIVE)\n   m_can_backtrack = true;\n   m_have_accept = false;\n#endif\n   // find the value to use for matching word boundaries:\n   m_word_mask = re.get_data().m_word_mask; \n   // find bitmask to use for matching '.':\n   match_any_mask = static_cast<unsigned char>((f & match_not_dot_newline) ? BOOST_REGEX_DETAIL_NS::test_not_newline : BOOST_REGEX_DETAIL_NS::test_newline);\n   // Disable match_any if requested in the state machine:\n   if(e.get_data().m_disable_match_any)\n      m_match_flags &= regex_constants::match_not_any;\n}\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n\ntemplate <class BidiIterator, class Allocator, class traits>\nvoid perl_matcher<BidiIterator, Allocator, traits>::estimate_max_state_count(std::random_access_iterator_tag*)\n{\n   //\n   // How many states should we allow our machine to visit before giving up?\n   // This is a heuristic: it takes the greater of O(N^2) and O(NS^2)\n   // where N is the length of the string, and S is the number of states\n   // in the machine.  It's tempting to up this to O(N^2S) or even O(N^2S^2)\n   // but these take unreasonably amounts of time to bale out in pathological\n   // cases.\n   //\n   // Calculate NS^2 first:\n   //\n   static const std::ptrdiff_t k = 100000;\n   std::ptrdiff_t dist = boost::BOOST_REGEX_DETAIL_NS::distance(base, last);\n   if(dist == 0)\n      dist = 1;\n   std::ptrdiff_t states = re.size();\n   if(states == 0)\n      states = 1;\n   if ((std::numeric_limits<std::ptrdiff_t>::max)() / states < states)\n   {\n      max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);\n      return;\n   }\n   states *= states;\n   if((std::numeric_limits<std::ptrdiff_t>::max)() / dist < states)\n   {\n      max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);\n      return;\n   }\n   states *= dist;\n   if((std::numeric_limits<std::ptrdiff_t>::max)() - k < states)\n   {\n      max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);\n      return;\n   }\n   states += k;\n\n   max_state_count = states;\n\n   //\n   // Now calculate N^2:\n   //\n   states = dist;\n   if((std::numeric_limits<std::ptrdiff_t>::max)() / dist < states)\n   {\n      max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);\n      return;\n   }\n   states *= dist;\n   if((std::numeric_limits<std::ptrdiff_t>::max)() - k < states)\n   {\n      max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);\n      return;\n   }\n   states += k;\n   //\n   // N^2 can be a very large number indeed, to prevent things getting out\n   // of control, cap the max states:\n   //\n   if(states > BOOST_REGEX_MAX_STATE_COUNT)\n      states = BOOST_REGEX_MAX_STATE_COUNT;\n   //\n   // If (the possibly capped) N^2 is larger than our first estimate,\n   // use this instead:\n   //\n   if(states > max_state_count)\n      max_state_count = states;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::estimate_max_state_count(void*)\n{\n   // we don't know how long the sequence is:\n   max_state_count = BOOST_REGEX_MAX_STATE_COUNT;\n}\n\n#ifdef BOOST_REGEX_HAS_MS_STACK_GUARD\ntemplate <class BidiIterator, class Allocator, class traits>\ninline bool perl_matcher<BidiIterator, Allocator, traits>::protected_call(\n   protected_proc_type proc)\n{\n   ::boost::BOOST_REGEX_DETAIL_NS::concrete_protected_call\n      <perl_matcher<BidiIterator, Allocator, traits> >\n      obj(this, proc);\n   return obj.execute();\n\n}\n#endif\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline bool perl_matcher<BidiIterator, Allocator, traits>::match()\n{\n#ifdef BOOST_REGEX_HAS_MS_STACK_GUARD\n   return protected_call(&perl_matcher<BidiIterator, Allocator, traits>::match_imp);\n#else\n   return match_imp();\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_imp()\n{\n   // initialise our stack if we are non-recursive:\n#ifdef BOOST_REGEX_NON_RECURSIVE\n   save_state_init init(&m_stack_base, &m_backup_state);\n   used_block_count = BOOST_REGEX_MAX_BLOCKS;\n#if !defined(BOOST_NO_EXCEPTIONS)\n   try{\n#endif\n#endif\n\n   // reset our state machine:\n   position = base;\n   search_base = base;\n   state_count = 0;\n   m_match_flags |= regex_constants::match_all;\n   m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast<typename results_type::size_type>(1u + re.mark_count()), search_base, last);\n   m_presult->set_base(base);\n   m_presult->set_named_subs(this->re.get_named_subs());\n   if(m_match_flags & match_posix)\n      m_result = *m_presult;\n   verify_options(re.flags(), m_match_flags);\n   if(0 == match_prefix())\n      return false;\n   return (m_result[0].second == last) && (m_result[0].first == base);\n\n#if defined(BOOST_REGEX_NON_RECURSIVE) && !defined(BOOST_NO_EXCEPTIONS)\n   }\n   catch(...)\n   {\n      // unwind all pushed states, apart from anything else this\n      // ensures that all the states are correctly destructed\n      // not just the memory freed.\n      while(unwind(true)){}\n      throw;\n   }\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline bool perl_matcher<BidiIterator, Allocator, traits>::find()\n{\n#ifdef BOOST_REGEX_HAS_MS_STACK_GUARD\n   return protected_call(&perl_matcher<BidiIterator, Allocator, traits>::find_imp);\n#else\n   return find_imp();\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_imp()\n{\n   static matcher_proc_type const s_find_vtable[7] = \n   {\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_any,\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_word,\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_line,\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_buf,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_prefix,\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_lit,\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_lit,\n   };\n\n   // initialise our stack if we are non-recursive:\n#ifdef BOOST_REGEX_NON_RECURSIVE\n   save_state_init init(&m_stack_base, &m_backup_state);\n   used_block_count = BOOST_REGEX_MAX_BLOCKS;\n#if !defined(BOOST_NO_EXCEPTIONS)\n   try{\n#endif\n#endif\n\n   state_count = 0;\n   if((m_match_flags & regex_constants::match_init) == 0)\n   {\n      // reset our state machine:\n      search_base = position = base;\n      pstate = re.get_first_state();\n      m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast<typename results_type::size_type>(1u + re.mark_count()), base, last);\n      m_presult->set_base(base);\n      m_presult->set_named_subs(this->re.get_named_subs());\n      m_match_flags |= regex_constants::match_init;\n   }\n   else\n   {\n      // start again:\n      search_base = position = m_result[0].second;\n      // If last match was null and match_not_null was not set then increment\n      // our start position, otherwise we go into an infinite loop:\n      if(((m_match_flags & match_not_null) == 0) && (m_result.length() == 0))\n      {\n         if(position == last)\n            return false;\n         else \n            ++position;\n      }\n      // reset $` start:\n      m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast<typename results_type::size_type>(1u + re.mark_count()), search_base, last);\n      //if((base != search_base) && (base == backstop))\n      //   m_match_flags |= match_prev_avail;\n   }\n   if(m_match_flags & match_posix)\n   {\n      m_result.set_size(static_cast<typename results_type::size_type>(1u + re.mark_count()), base, last);\n      m_result.set_base(base);\n   }\n\n   verify_options(re.flags(), m_match_flags);\n   // find out what kind of expression we have:\n   unsigned type = (m_match_flags & match_continuous) ? \n      static_cast<unsigned int>(regbase::restart_continue) \n         : static_cast<unsigned int>(re.get_restart_type());\n\n   // call the appropriate search routine:\n   matcher_proc_type proc = s_find_vtable[type];\n   return (this->*proc)();\n\n#if defined(BOOST_REGEX_NON_RECURSIVE) && !defined(BOOST_NO_EXCEPTIONS)\n   }\n   catch(...)\n   {\n      // unwind all pushed states, apart from anything else this\n      // ensures that all the states are correctly destructed\n      // not just the memory freed.\n      while(unwind(true)){}\n      throw;\n   }\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_prefix()\n{\n   m_has_partial_match = false;\n   m_has_found_match = false;\n   pstate = re.get_first_state();\n   m_presult->set_first(position);\n   restart = position;\n   match_all_states();\n   if(!m_has_found_match && m_has_partial_match && (m_match_flags & match_partial))\n   {\n      m_has_found_match = true;\n      m_presult->set_second(last, 0, false);\n      position = last;\n      if((m_match_flags & match_posix) == match_posix)\n      {\n         m_result.maybe_assign(*m_presult);\n      }\n   }\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   if(m_has_found_match && (match_extra & m_match_flags))\n   {\n      //\n      // we have a match, reverse the capture information:\n      //\n      for(unsigned i = 0; i < m_presult->size(); ++i)\n      {\n         typename sub_match<BidiIterator>::capture_sequence_type & seq = ((*m_presult)[i]).get_captures();\n         std::reverse(seq.begin(), seq.end());\n      }\n   }\n#endif\n   if(!m_has_found_match)\n      position = restart; // reset search postion\n#ifdef BOOST_REGEX_RECURSIVE\n   m_can_backtrack = true; // reset for further searches\n#endif\n   return m_has_found_match;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_literal()\n{\n   unsigned int len = static_cast<const re_literal*>(pstate)->length;\n   const char_type* what = reinterpret_cast<const char_type*>(static_cast<const re_literal*>(pstate) + 1);\n   //\n   // compare string with what we stored in\n   // our records:\n   for(unsigned int i = 0; i < len; ++i, ++position)\n   {\n      if((position == last) || (traits_inst.translate(*position, icase) != what[i]))\n         return false;\n   }\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_start_line()\n{\n   if(position == backstop)\n   {\n      if((m_match_flags & match_prev_avail) == 0)\n      {\n         if((m_match_flags & match_not_bol) == 0)\n         {\n            pstate = pstate->next.p;\n            return true;\n         }\n         return false;\n      }\n   }\n   else if(m_match_flags & match_single_line)\n      return false;\n\n   // check the previous value character:\n   BidiIterator t(position);\n   --t;\n   if(position != last)\n   {\n      if(is_separator(*t) && !((*t == static_cast<char_type>('\\r')) && (*position == static_cast<char_type>('\\n'))) )\n      {\n         pstate = pstate->next.p;\n         return true;\n      }\n   }\n   else if(is_separator(*t))\n   {\n      pstate = pstate->next.p;\n      return true;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_end_line()\n{\n   if(position != last)\n   {\n      if(m_match_flags & match_single_line)\n         return false;\n      // we're not yet at the end so *first is always valid:\n      if(is_separator(*position))\n      {\n         if((position != backstop) || (m_match_flags & match_prev_avail))\n         {\n            // check that we're not in the middle of \\r\\n sequence\n            BidiIterator t(position);\n            --t;\n            if((*t == static_cast<char_type>('\\r')) && (*position == static_cast<char_type>('\\n')))\n            {\n               return false;\n            }\n         }\n         pstate = pstate->next.p;\n         return true;\n      }\n   }\n   else if((m_match_flags & match_not_eol) == 0)\n   {\n      pstate = pstate->next.p;\n      return true;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_wild()\n{\n   if(position == last) \n      return false;\n   if(is_separator(*position) && ((match_any_mask & static_cast<const re_dot*>(pstate)->mask) == 0))\n      return false;\n   if((*position == char_type(0)) && (m_match_flags & match_not_dot_null))\n      return false;\n   pstate = pstate->next.p;\n   ++position;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_word_boundary()\n{\n   bool b; // indcates whether next character is a word character\n   if(position != last)\n   {\n      // prev and this character must be opposites:\n      b = traits_inst.isctype(*position, m_word_mask);\n   }\n   else\n   {\n      if (m_match_flags & match_not_eow)\n         return false;\n      b = false;\n   }\n   if((position == backstop) && ((m_match_flags & match_prev_avail) == 0))\n   {\n      if(m_match_flags & match_not_bow)\n         return false;\n      else\n         b ^= false;\n   }\n   else\n   {\n      --position;\n      b ^= traits_inst.isctype(*position, m_word_mask);\n      ++position;\n   }\n   if(b)\n   {\n      pstate = pstate->next.p;\n      return true;\n   }\n   return false; // no match if we get to here...\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_within_word()\n{\n   if(position == last)\n      return false;\n   // both prev and this character must be m_word_mask:\n   bool prev = traits_inst.isctype(*position, m_word_mask);\n   {\n      bool b;\n      if((position == backstop) && ((m_match_flags & match_prev_avail) == 0)) \n         return false;\n      else\n      {\n         --position;\n         b = traits_inst.isctype(*position, m_word_mask);\n         ++position;\n      }\n      if(b == prev)\n      {\n         pstate = pstate->next.p;\n         return true;\n      }\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_word_start()\n{\n   if(position == last)\n      return false; // can't be starting a word if we're already at the end of input\n   if(!traits_inst.isctype(*position, m_word_mask))\n      return false; // next character isn't a word character\n   if((position == backstop) && ((m_match_flags & match_prev_avail) == 0))\n   {\n      if(m_match_flags & match_not_bow)\n         return false; // no previous input\n   }\n   else\n   {\n      // otherwise inside buffer:\n      BidiIterator t(position);\n      --t;\n      if(traits_inst.isctype(*t, m_word_mask))\n         return false; // previous character not non-word\n   }\n   // OK we have a match:\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_word_end()\n{\n   if((position == backstop) && ((m_match_flags & match_prev_avail) == 0))\n      return false;  // start of buffer can't be end of word\n   BidiIterator t(position);\n   --t;\n   if(traits_inst.isctype(*t, m_word_mask) == false)\n      return false;  // previous character wasn't a word character\n\n   if(position == last)\n   {\n      if(m_match_flags & match_not_eow)\n         return false; // end of buffer but not end of word\n   }\n   else\n   {\n      // otherwise inside buffer:\n      if(traits_inst.isctype(*position, m_word_mask))\n         return false; // next character is a word character\n   }\n   pstate = pstate->next.p;\n   return true;      // if we fall through to here then we've succeeded\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_buffer_start()\n{\n   if((position != backstop) || (m_match_flags & match_not_bob))\n      return false;\n   // OK match:\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_buffer_end()\n{\n   if((position != last) || (m_match_flags & match_not_eob))\n      return false;\n   // OK match:\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_backref()\n{\n   //\n   // Compare with what we previously matched.\n   // Note that this succeeds if the backref did not partisipate\n   // in the match, this is in line with ECMAScript, but not Perl\n   // or PCRE.\n   //\n   int index = static_cast<const re_brace*>(pstate)->index;\n   if(index >= hash_value_mask)\n   {\n      named_subexpressions::range_type r = re.get_data().equal_range(index);\n      BOOST_REGEX_ASSERT(r.first != r.second);\n      do\n      {\n         index = r.first->index;\n         ++r.first;\n      }while((r.first != r.second) && ((*m_presult)[index].matched != true));\n   }\n\n   if((m_match_flags & match_perl) && !(*m_presult)[index].matched)\n      return false;\n\n   BidiIterator i = (*m_presult)[index].first;\n   BidiIterator j = (*m_presult)[index].second;\n   while(i != j)\n   {\n      if((position == last) || (traits_inst.translate(*position, icase) != traits_inst.translate(*i, icase)))\n         return false;\n      ++i;\n      ++position;\n   }\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_long_set()\n{\n   typedef typename traits::char_class_type char_class_type;\n   // let the traits class do the work:\n   if(position == last)\n      return false;\n   BidiIterator t = re_is_set_member(position, last, static_cast<const re_set_long<char_class_type>*>(pstate), re.get_data(), icase);\n   if(t != position)\n   {\n      pstate = pstate->next.p;\n      position = t;\n      return true;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_set()\n{\n   if(position == last)\n      return false;\n   if(static_cast<const re_set*>(pstate)->_map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n   {\n      pstate = pstate->next.p;\n      ++position;\n      return true;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_jump()\n{\n   pstate = static_cast<const re_jump*>(pstate)->alt.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_combining()\n{\n   if(position == last)\n      return false;\n   if(is_combining(traits_inst.translate(*position, icase)))\n      return false;\n   ++position;\n   while((position != last) && is_combining(traits_inst.translate(*position, icase)))\n      ++position;\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_soft_buffer_end()\n{\n   if(m_match_flags & match_not_eob)\n      return false;\n   BidiIterator p(position);\n   while((p != last) && is_separator(traits_inst.translate(*p, icase)))++p;\n   if(p != last)\n      return false;\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_restart_continue()\n{\n   if(position == search_base)\n   {\n      pstate = pstate->next.p;\n      return true;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_backstep()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   if( ::boost::is_random_access_iterator<BidiIterator>::value)\n   {\n      std::ptrdiff_t maxlen = ::boost::BOOST_REGEX_DETAIL_NS::distance(backstop, position);\n      if(maxlen < static_cast<const re_brace*>(pstate)->index)\n         return false;\n      std::advance(position, -static_cast<const re_brace*>(pstate)->index);\n   }\n   else\n   {\n      int c = static_cast<const re_brace*>(pstate)->index;\n      while(c--)\n      {\n         if(position == backstop)\n            return false;\n         --position;\n      }\n   }\n   pstate = pstate->next.p;\n   return true;\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline bool perl_matcher<BidiIterator, Allocator, traits>::match_assert_backref()\n{\n   // return true if marked sub-expression N has been matched:\n   int index = static_cast<const re_brace*>(pstate)->index;\n   bool result = false;\n   if(index == 9999)\n   {\n      // Magic value for a (DEFINE) block:\n      return false;\n   }\n   else if(index > 0)\n   {\n      // Have we matched subexpression \"index\"?\n      // Check if index is a hash value:\n      if(index >= hash_value_mask)\n      {\n         named_subexpressions::range_type r = re.get_data().equal_range(index);\n         while(r.first != r.second)\n         {\n            if((*m_presult)[r.first->index].matched)\n            {\n               result = true;\n               break;\n            }\n            ++r.first;\n         }\n      }\n      else\n      {\n         result = (*m_presult)[index].matched;\n      }\n      pstate = pstate->next.p;\n   }\n   else\n   {\n      // Have we recursed into subexpression \"index\"?\n      // If index == 0 then check for any recursion at all, otherwise for recursion to -index-1.\n      int idx = -(index+1);\n      if(idx >= hash_value_mask)\n      {\n         named_subexpressions::range_type r = re.get_data().equal_range(idx);\n         int stack_index = recursion_stack.empty() ? -1 : recursion_stack.back().idx;\n         while(r.first != r.second)\n         {\n            result |= (stack_index == r.first->index);\n            if(result)break;\n            ++r.first;\n         }\n      }\n      else\n      {\n         result = !recursion_stack.empty() && ((recursion_stack.back().idx == idx) || (index == 0));\n      }\n      pstate = pstate->next.p;\n   }\n   return result;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_fail()\n{\n   // Just force a backtrack:\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_accept()\n{\n   if(!recursion_stack.empty())\n   {\n      return skip_until_paren(recursion_stack.back().idx);\n   }\n   else\n   {\n      return skip_until_paren(INT_MAX);\n   }\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_restart_any()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   const unsigned char* _map = re.get_map();\n   while(true)\n   {\n      // skip everything we can't match:\n      while((position != last) && !can_start(*position, _map, (unsigned char)mask_any) )\n         ++position;\n      if(position == last)\n      {\n         // run out of characters, try a null match if possible:\n         if(re.can_be_null())\n            return match_prefix();\n         break;\n      }\n      // now try and obtain a match:\n      if(match_prefix())\n         return true;\n      if(position == last)\n         return false;\n      ++position;\n   }\n   return false;\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_restart_word()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   // do search optimised for word starts:\n   const unsigned char* _map = re.get_map();\n   if((m_match_flags & match_prev_avail) || (position != base))\n      --position;\n   else if(match_prefix())\n      return true;\n   do\n   {\n      while((position != last) && traits_inst.isctype(*position, m_word_mask))\n         ++position;\n      while((position != last) && !traits_inst.isctype(*position, m_word_mask))\n         ++position;\n      if(position == last)\n         break;\n\n      if(can_start(*position, _map, (unsigned char)mask_any) )\n      {\n         if(match_prefix())\n            return true;\n      }\n      if(position == last)\n         break;\n   } while(true);\n   return false;\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_restart_line()\n{\n   // do search optimised for line starts:\n   const unsigned char* _map = re.get_map();\n   if(match_prefix())\n      return true;\n   while(position != last)\n   {\n      while((position != last) && !is_separator(*position))\n         ++position;\n      if(position == last)\n         return false;\n      ++position;\n      if(position == last)\n      {\n         if(re.can_be_null() && match_prefix())\n            return true;\n         return false;\n      }\n\n      if( can_start(*position, _map, (unsigned char)mask_any) )\n      {\n         if(match_prefix())\n            return true;\n      }\n      if(position == last)\n         return false;\n      //++position;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_restart_buf()\n{\n   if((position == base) && ((m_match_flags & match_not_bob) == 0))\n      return match_prefix();\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_restart_lit()\n{\n#if 0\n   if(position == last)\n      return false; // can't possibly match if we're at the end already\n\n   unsigned type = (m_match_flags & match_continuous) ? \n      static_cast<unsigned int>(regbase::restart_continue) \n         : static_cast<unsigned int>(re.get_restart_type());\n\n   const kmp_info<char_type>* info = access::get_kmp(re);\n   int len = info->len;\n   const char_type* x = info->pstr;\n   int j = 0; \n   while (position != last) \n   {\n      while((j > -1) && (x[j] != traits_inst.translate(*position, icase))) \n         j = info->kmp_next[j];\n      ++position;\n      ++j;\n      if(j >= len) \n      {\n         if(type == regbase::restart_fixed_lit)\n         {\n            std::advance(position, -j);\n            restart = position;\n            std::advance(restart, len);\n            m_result.set_first(position);\n            m_result.set_second(restart);\n            position = restart;\n            return true;\n         }\n         else\n         {\n            restart = position;\n            std::advance(position, -j);\n            if(match_prefix())\n               return true;\n            else\n            {\n               for(int k = 0; (restart != position) && (k < j); ++k, --restart)\n                     {} // dwa 10/20/2000 - warning suppression for MWCW\n               if(restart != last)\n                  ++restart;\n               position = restart;\n               j = 0;  //we could do better than this...\n            }\n         }\n      }\n   }\n   if((m_match_flags & match_partial) && (position == last) && j)\n   {\n      // we need to check for a partial match:\n      restart = position;\n      std::advance(position, -j);\n      return match_prefix();\n   }\n#endif\n   return false;\n}\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n\n#ifdef BOOST_BORLANDC\n#  pragma option pop\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/perl_matcher_non_recursive.hpp",
    "content": "/*\n *\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         perl_matcher_common.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Definitions of perl_matcher member functions that are \n  *                specific to the non-recursive implementation.\n  */\n\n#ifndef BOOST_REGEX_V4_PERL_MATCHER_NON_RECURSIVE_HPP\n#define BOOST_REGEX_V4_PERL_MATCHER_NON_RECURSIVE_HPP\n\n#include <boost/regex/v4/mem_block_cache.hpp>\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#  pragma warning(disable: 4706)\n#if BOOST_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class T>\ninline void inplace_destroy(T* p)\n{\n   (void)p;  // warning suppression\n   p->~T();\n}\n\nstruct saved_state\n{\n   union{\n      unsigned int state_id;\n      // this padding ensures correct alignment on 64-bit platforms:\n      std::size_t padding1;\n      std::ptrdiff_t padding2;\n      void* padding3;\n   };\n   saved_state(unsigned i) : state_id(i) {}\n};\n\ntemplate <class BidiIterator>\nstruct saved_matched_paren : public saved_state\n{\n   int index;\n   sub_match<BidiIterator> sub;\n   saved_matched_paren(int i, const sub_match<BidiIterator>& s) : saved_state(1), index(i), sub(s){}\n};\n\ntemplate <class BidiIterator>\nstruct saved_position : public saved_state\n{\n   const re_syntax_base* pstate;\n   BidiIterator position;\n   saved_position(const re_syntax_base* ps, BidiIterator pos, int i) : saved_state(i), pstate(ps), position(pos){}\n};\n\ntemplate <class BidiIterator>\nstruct saved_assertion : public saved_position<BidiIterator>\n{\n   bool positive;\n   saved_assertion(bool p, const re_syntax_base* ps, BidiIterator pos) \n      : saved_position<BidiIterator>(ps, pos, saved_type_assertion), positive(p){}\n};\n\ntemplate <class BidiIterator>\nstruct saved_repeater : public saved_state\n{\n   repeater_count<BidiIterator> count;\n   saved_repeater(int i, repeater_count<BidiIterator>** s, BidiIterator start, int current_recursion_id)\n      : saved_state(saved_state_repeater_count), count(i, s, start, current_recursion_id){}\n};\n\nstruct saved_extra_block : public saved_state\n{\n   saved_state *base, *end;\n   saved_extra_block(saved_state* b, saved_state* e) \n      : saved_state(saved_state_extra_block), base(b), end(e) {}\n};\n\nstruct save_state_init\n{\n   saved_state** stack;\n   save_state_init(saved_state** base, saved_state** end)\n      : stack(base)\n   {\n      *base = static_cast<saved_state*>(get_mem_block());\n      *end = reinterpret_cast<saved_state*>(reinterpret_cast<char*>(*base)+BOOST_REGEX_BLOCKSIZE);\n      --(*end);\n      (void) new (*end)saved_state(0);\n      BOOST_REGEX_ASSERT(*end > *base);\n   }\n   ~save_state_init()\n   {\n      put_mem_block(*stack);\n      *stack = 0;\n   }\n};\n\ntemplate <class BidiIterator>\nstruct saved_single_repeat : public saved_state\n{\n   std::size_t count;\n   const re_repeat* rep;\n   BidiIterator last_position;\n   saved_single_repeat(std::size_t c, const re_repeat* r, BidiIterator lp, int arg_id) \n      : saved_state(arg_id), count(c), rep(r), last_position(lp){}\n};\n\ntemplate <class Results>\nstruct saved_recursion : public saved_state\n{\n   saved_recursion(int idx, const re_syntax_base* p, Results* pr, Results* pr2) \n      : saved_state(14), recursion_id(idx), preturn_address(p), internal_results(*pr), prior_results(*pr2) {}\n   int recursion_id;\n   const re_syntax_base* preturn_address;\n   Results internal_results, prior_results;\n};\n\nstruct saved_change_case : public saved_state\n{\n   bool icase;\n   saved_change_case(bool c) : saved_state(18), icase(c) {}\n};\n\nstruct incrementer\n{\n   incrementer(unsigned* pu) : m_pu(pu) { ++*m_pu; }\n   ~incrementer() { --*m_pu; }\n   bool operator > (unsigned i) { return *m_pu > i; }\nprivate:\n   unsigned* m_pu;\n};\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_all_states()\n{\n   static matcher_proc_type const s_match_vtable[34] = \n   {\n      (&perl_matcher<BidiIterator, Allocator, traits>::match_startmark),\n      &perl_matcher<BidiIterator, Allocator, traits>::match_endmark,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_literal,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_start_line,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_end_line,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_wild,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_match,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_word_boundary,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_within_word,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_word_start,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_word_end,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_buffer_start,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_buffer_end,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_backref,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_long_set,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_set,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_jump,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_alt,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_rep,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_combining,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_soft_buffer_end,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_restart_continue,\n      // Although this next line *should* be evaluated at compile time, in practice\n      // some compilers (VC++) emit run-time initialisation which breaks thread\n      // safety, so use a dispatch function instead:\n      //(::boost::is_random_access_iterator<BidiIterator>::value ? &perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_fast : &perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_slow),\n      &perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_dispatch,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_char_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_set_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_long_set_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_backstep,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_assert_backref,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_toggle_case,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_recursion,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_fail,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_accept,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_commit,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_then,\n   };\n   incrementer inc(&m_recursions);\n   if(inc > 80)\n      raise_error(traits_inst, regex_constants::error_complexity);\n   push_recursion_stopper();\n   do{\n      while(pstate)\n      {\n         matcher_proc_type proc = s_match_vtable[pstate->type];\n         ++state_count;\n         if(!(this->*proc)())\n         {\n            if(state_count > max_state_count)\n               raise_error(traits_inst, regex_constants::error_complexity);\n            if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n               m_has_partial_match = true;\n            bool successful_unwind = unwind(false);\n            if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n               m_has_partial_match = true;\n            if(!successful_unwind)\n               return m_recursive_result;\n         }\n      }\n   }while(unwind(true));\n   return m_recursive_result;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nvoid perl_matcher<BidiIterator, Allocator, traits>::extend_stack()\n{\n   if(used_block_count)\n   {\n      --used_block_count;\n      saved_state* stack_base;\n      saved_state* backup_state;\n      stack_base = static_cast<saved_state*>(get_mem_block());\n      backup_state = reinterpret_cast<saved_state*>(reinterpret_cast<char*>(stack_base)+BOOST_REGEX_BLOCKSIZE);\n      saved_extra_block* block = static_cast<saved_extra_block*>(backup_state);\n      --block;\n      (void) new (block) saved_extra_block(m_stack_base, m_backup_state);\n      m_stack_base = stack_base;\n      m_backup_state = block;\n   }\n   else\n      raise_error(traits_inst, regex_constants::error_stack);\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_matched_paren(int index, const sub_match<BidiIterator>& sub)\n{\n   //BOOST_REGEX_ASSERT(index);\n   saved_matched_paren<BidiIterator>* pmp = static_cast<saved_matched_paren<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_matched_paren<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_matched_paren<BidiIterator>(index, sub);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_case_change(bool c)\n{\n   //BOOST_REGEX_ASSERT(index);\n   saved_change_case* pmp = static_cast<saved_change_case*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_change_case*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_change_case(c);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_recursion_stopper()\n{\n   saved_state* pmp = m_backup_state;\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = m_backup_state;\n      --pmp;\n   }\n   (void) new (pmp)saved_state(saved_type_recurse);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_assertion(const re_syntax_base* ps, bool positive)\n{\n   saved_assertion<BidiIterator>* pmp = static_cast<saved_assertion<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_assertion<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_assertion<BidiIterator>(positive, ps, position);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_alt(const re_syntax_base* ps)\n{\n   saved_position<BidiIterator>* pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_position<BidiIterator>(ps, position, saved_state_alt);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_non_greedy_repeat(const re_syntax_base* ps)\n{\n   saved_position<BidiIterator>* pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_position<BidiIterator>(ps, position, saved_state_non_greedy_long_repeat);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_repeater_count(int i, repeater_count<BidiIterator>** s)\n{\n   saved_repeater<BidiIterator>* pmp = static_cast<saved_repeater<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_repeater<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_repeater<BidiIterator>(i, s, position, this->recursion_stack.empty() ? (INT_MIN + 3) : this->recursion_stack.back().idx);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_single_repeat(std::size_t c, const re_repeat* r, BidiIterator last_position, int state_id)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_single_repeat<BidiIterator>(c, r, last_position, state_id);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_recursion(int idx, const re_syntax_base* p, results_type* presults, results_type* presults2)\n{\n   saved_recursion<results_type>* pmp = static_cast<saved_recursion<results_type>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_recursion<results_type>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_recursion<results_type>(idx, p, presults, presults2);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_toggle_case()\n{\n   // change our case sensitivity:\n   push_case_change(this->icase);\n   this->icase = static_cast<const re_case*>(pstate)->icase;\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_startmark()\n{\n   int index = static_cast<const re_brace*>(pstate)->index;\n   icase = static_cast<const re_brace*>(pstate)->icase;\n   switch(index)\n   {\n   case 0:\n      pstate = pstate->next.p;\n      break;\n   case -1:\n   case -2:\n      {\n         // forward lookahead assert:\n         const re_syntax_base* next_pstate = static_cast<const re_jump*>(pstate->next.p)->alt.p->next.p;\n         pstate = pstate->next.p->next.p;\n         push_assertion(next_pstate, index == -1);\n         break;\n      }\n   case -3:\n      {\n         // independent sub-expression, currently this is always recursive:\n         bool old_independent = m_independent;\n         m_independent = true;\n         const re_syntax_base* next_pstate = static_cast<const re_jump*>(pstate->next.p)->alt.p->next.p;\n         pstate = pstate->next.p->next.p;\n         bool r = false;\n#if !defined(BOOST_NO_EXCEPTIONS)\n      try{\n#endif\n         r = match_all_states();\n         if(!r && !m_independent)\n         {\n            // Must be unwinding from a COMMIT/SKIP/PRUNE and the independent \n            // sub failed, need to unwind everything else:\n            while(unwind(false));\n            return false;\n         }\n#if !defined(BOOST_NO_EXCEPTIONS)\n      }\n      catch(...)\n      {\n         pstate = next_pstate;\n         // unwind all pushed states, apart from anything else this\n         // ensures that all the states are correctly destructed\n         // not just the memory freed.\n         while(unwind(true)) {}\n         throw;\n      }\n#endif\n      pstate = next_pstate;\n      m_independent = old_independent;\n#ifdef BOOST_REGEX_MATCH_EXTRA\n         if(r && (m_match_flags & match_extra))\n         {\n            //\n            // our captures have been stored in *m_presult\n            // we need to unpack them, and insert them\n            // back in the right order when we unwind the stack:\n            //\n            match_results<BidiIterator, Allocator> temp_match(*m_presult);\n            unsigned i;\n            for(i = 0; i < temp_match.size(); ++i)\n               (*m_presult)[i].get_captures().clear();\n            // match everything else:\n#if !defined(BOOST_NO_EXCEPTIONS)\n            try{\n#endif\n               r = match_all_states();\n#if !defined(BOOST_NO_EXCEPTIONS)\n            }\n            catch(...)\n            {\n               pstate = next_pstate;\n               // unwind all pushed states, apart from anything else this\n               // ensures that all the states are correctly destructed\n               // not just the memory freed.\n               while(unwind(true)) {}\n               throw;\n            }\n#endif\n         // now place the stored captures back:\n            for(i = 0; i < temp_match.size(); ++i)\n            {\n               typedef typename sub_match<BidiIterator>::capture_sequence_type seq;\n               seq& s1 = (*m_presult)[i].get_captures();\n               const seq& s2 = temp_match[i].captures();\n               s1.insert(\n                  s1.end(), \n                  s2.begin(), \n                  s2.end());\n            }\n         }\n#endif\n         return r;\n      }\n   case -4:\n      {\n      // conditional expression:\n      const re_alt* alt = static_cast<const re_alt*>(pstate->next.p);\n      BOOST_REGEX_ASSERT(alt->type == syntax_element_alt);\n      pstate = alt->next.p;\n      if(pstate->type == syntax_element_assert_backref)\n      {\n         if(!match_assert_backref())\n            pstate = alt->alt.p;\n         break;\n      }\n      else\n      {\n         // zero width assertion, have to match this recursively:\n         BOOST_REGEX_ASSERT(pstate->type == syntax_element_startmark);\n         bool negated = static_cast<const re_brace*>(pstate)->index == -2;\n         BidiIterator saved_position = position;\n         const re_syntax_base* next_pstate = static_cast<const re_jump*>(pstate->next.p)->alt.p->next.p;\n         pstate = pstate->next.p->next.p;\n#if !defined(BOOST_NO_EXCEPTIONS)\n         try{\n#endif\n            bool r = match_all_states();\n            position = saved_position;\n            if(negated)\n               r = !r;\n            if(r)\n               pstate = next_pstate;\n            else\n               pstate = alt->alt.p;\n#if !defined(BOOST_NO_EXCEPTIONS)\n         }\n         catch(...)\n         {\n            pstate = next_pstate;\n            // unwind all pushed states, apart from anything else this\n            // ensures that all the states are correctly destructed\n            // not just the memory freed.\n            while(unwind(true)){}\n            throw;\n         }\n#endif\n         break;\n      }\n      }\n   case -5:\n      {\n         push_matched_paren(0, (*m_presult)[0]);\n         m_presult->set_first(position, 0, true);\n         pstate = pstate->next.p;\n         break;\n      }\n   default:\n   {\n      BOOST_REGEX_ASSERT(index > 0);\n      if((m_match_flags & match_nosubs) == 0)\n      {\n         push_matched_paren(index, (*m_presult)[index]);\n         m_presult->set_first(position, index);\n      }\n      pstate = pstate->next.p;\n      break;\n   }\n   }\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_alt()\n{\n   bool take_first, take_second;\n   const re_alt* jmp = static_cast<const re_alt*>(pstate);\n\n   // find out which of these two alternatives we need to take:\n   if(position == last)\n   {\n      take_first = jmp->can_be_null & mask_take;\n      take_second = jmp->can_be_null & mask_skip;\n   }\n   else\n   {\n      take_first = can_start(*position, jmp->_map, (unsigned char)mask_take);\n      take_second = can_start(*position, jmp->_map, (unsigned char)mask_skip);\n  }\n\n   if(take_first)\n   {\n      // we can take the first alternative,\n      // see if we need to push next alternative:\n      if(take_second)\n      {\n         push_alt(jmp->alt.p);\n      }\n      pstate = pstate->next.p;\n      return true;\n   }\n   if(take_second)\n   {\n      pstate = jmp->alt.p;\n      return true;\n   }\n   return false;  // neither option is possible\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_rep()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127 4244)\n#endif\n#ifdef BOOST_BORLANDC\n#pragma option push -w-8008 -w-8066 -w-8004\n#endif\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n\n   // find out which of these two alternatives we need to take:\n   bool take_first, take_second;\n   if(position == last)\n   {\n      take_first = rep->can_be_null & mask_take;\n      take_second = rep->can_be_null & mask_skip;\n   }\n   else\n   {\n      take_first = can_start(*position, rep->_map, (unsigned char)mask_take);\n      take_second = can_start(*position, rep->_map, (unsigned char)mask_skip);\n   }\n\n   if((m_backup_state->state_id != saved_state_repeater_count) \n      || (static_cast<saved_repeater<BidiIterator>*>(m_backup_state)->count.get_id() != rep->state_id)\n      || (next_count->get_id() != rep->state_id))\n   {\n      // we're moving to a different repeat from the last\n      // one, so set up a counter object:\n      push_repeater_count(rep->state_id, &next_count);\n   }\n   //\n   // If we've had at least one repeat already, and the last one \n   // matched the NULL string then set the repeat count to\n   // maximum:\n   //\n   next_count->check_null_repeat(position, rep->max);\n\n   if(next_count->get_count() < rep->min)\n   {\n      // we must take the repeat:\n      if(take_first)\n      {\n         // increase the counter:\n         ++(*next_count);\n         pstate = rep->next.p;\n         return true;\n      }\n      return false;\n   }\n\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   if(greedy)\n   {\n      // try and take the repeat if we can:\n      if((next_count->get_count() < rep->max) && take_first)\n      {\n         if(take_second)\n         {\n            // store position in case we fail:\n            push_alt(rep->alt.p);\n         }\n         // increase the counter:\n         ++(*next_count);\n         pstate = rep->next.p;\n         return true;\n      }\n      else if(take_second)\n      {\n         pstate = rep->alt.p;\n         return true;\n      }\n      return false; // can't take anything, fail...\n   }\n   else // non-greedy\n   {\n      // try and skip the repeat if we can:\n      if(take_second)\n      {\n         if((next_count->get_count() < rep->max) && take_first)\n         {\n            // store position in case we fail:\n            push_non_greedy_repeat(rep->next.p);\n         }\n         pstate = rep->alt.p;\n         return true;\n      }\n      if((next_count->get_count() < rep->max) && take_first)\n      {\n         // increase the counter:\n         ++(*next_count);\n         pstate = rep->next.p;\n         return true;\n      }\n   }\n   return false;\n#ifdef BOOST_BORLANDC\n#pragma option pop\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_slow()\n{\n   std::size_t count = 0;\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   re_syntax_base* psingle = rep->next.p;\n   // match compulsory repeats first:\n   while(count < rep->min)\n   {\n      pstate = psingle;\n      if(!match_wild())\n         return false;\n      ++count;\n   }\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   if(greedy)\n   {\n      // repeat for as long as we can:\n      while(count < rep->max)\n      {\n         pstate = psingle;\n         if(!match_wild())\n            break;\n         ++count;\n      }\n      // remember where we got to if this is a leading repeat:\n      if((rep->leading) && (count < rep->max))\n         restart = position;\n      // push backtrack info if available:\n      if(count - rep->min)\n         push_single_repeat(count, rep, position, saved_state_greedy_single_repeat);\n      // jump to next state:\n      pstate = rep->alt.p;\n      return true;\n   }\n   else\n   {\n      // non-greedy, push state and return true if we can skip:\n      if(count < rep->max)\n         push_single_repeat(count, rep, position, saved_state_rep_slow_dot);\n      pstate = rep->alt.p;\n      return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip);\n   }\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_fast()\n{\n   if(m_match_flags & match_not_dot_null)\n      return match_dot_repeat_slow();\n   if((static_cast<const re_dot*>(pstate->next.p)->mask & match_any_mask) == 0)\n      return match_dot_repeat_slow();\n\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t count = static_cast<std::size_t>((std::min)(static_cast<std::size_t>(::boost::BOOST_REGEX_DETAIL_NS::distance(position, last)), greedy ? rep->max : rep->min));\n   if(rep->min > count)\n   {\n      position = last;\n      return false;  // not enough text left to match\n   }\n   std::advance(position, count);\n\n   if(greedy)\n   {\n      if((rep->leading) && (count < rep->max))\n         restart = position;\n      // push backtrack info if available:\n      if(count - rep->min)\n         push_single_repeat(count, rep, position, saved_state_greedy_single_repeat);\n      // jump to next state:\n      pstate = rep->alt.p;\n      return true;\n   }\n   else\n   {\n      // non-greedy, push state and return true if we can skip:\n      if(count < rep->max)\n         push_single_repeat(count, rep, position, saved_state_rep_fast_dot);\n      pstate = rep->alt.p;\n      return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip);\n   }\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_char_repeat()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n#ifdef BOOST_BORLANDC\n#pragma option push -w-8008 -w-8066 -w-8004\n#endif\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   BOOST_REGEX_ASSERT(1 == static_cast<const re_literal*>(rep->next.p)->length);\n   const char_type what = *reinterpret_cast<const char_type*>(static_cast<const re_literal*>(rep->next.p) + 1);\n   std::size_t count = 0;\n   //\n   // start by working out how much we can skip:\n   //\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t desired = greedy ? rep->max : rep->min;\n   if(::boost::is_random_access_iterator<BidiIterator>::value)\n   {\n      BidiIterator end = position;\n      // Move end forward by \"desired\", preferably without using distance or advance if we can\n      // as these can be slow for some iterator types.\n      std::size_t len = (desired == (std::numeric_limits<std::size_t>::max)()) ? 0u : ::boost::BOOST_REGEX_DETAIL_NS::distance(position, last);\n      if(desired >= len)\n         end = last;\n      else\n         std::advance(end, desired);\n      BidiIterator origin(position);\n      while((position != end) && (traits_inst.translate(*position, icase) == what))\n      {\n         ++position;\n      }\n      count = (unsigned)::boost::BOOST_REGEX_DETAIL_NS::distance(origin, position);\n   }\n   else\n   {\n      while((count < desired) && (position != last) && (traits_inst.translate(*position, icase) == what))\n      {\n         ++position;\n         ++count;\n      }\n   }\n\n   if(count < rep->min)\n      return false;\n\n   if(greedy)\n   {\n      if((rep->leading) && (count < rep->max))\n         restart = position;\n      // push backtrack info if available:\n      if(count - rep->min)\n         push_single_repeat(count, rep, position, saved_state_greedy_single_repeat);\n      // jump to next state:\n      pstate = rep->alt.p;\n      return true;\n   }\n   else\n   {\n      // non-greedy, push state and return true if we can skip:\n      if(count < rep->max)\n         push_single_repeat(count, rep, position, saved_state_rep_char);\n      pstate = rep->alt.p;\n      return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip);\n   }\n#ifdef BOOST_BORLANDC\n#pragma option pop\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_set_repeat()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n#ifdef BOOST_BORLANDC\n#pragma option push -w-8008 -w-8066 -w-8004\n#endif\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   const unsigned char* map = static_cast<const re_set*>(rep->next.p)->_map;\n   std::size_t count = 0;\n   //\n   // start by working out how much we can skip:\n   //\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t desired = greedy ? rep->max : rep->min;\n   if(::boost::is_random_access_iterator<BidiIterator>::value)\n   {\n      BidiIterator end = position;\n      // Move end forward by \"desired\", preferably without using distance or advance if we can\n      // as these can be slow for some iterator types.\n      std::size_t len = (desired == (std::numeric_limits<std::size_t>::max)()) ? 0u : ::boost::BOOST_REGEX_DETAIL_NS::distance(position, last);\n      if(desired >= len)\n         end = last;\n      else\n         std::advance(end, desired);\n      BidiIterator origin(position);\n      while((position != end) && map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n      {\n         ++position;\n      }\n      count = (unsigned)::boost::BOOST_REGEX_DETAIL_NS::distance(origin, position);\n   }\n   else\n   {\n      while((count < desired) && (position != last) && map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n      {\n         ++position;\n         ++count;\n      }\n   }\n\n   if(count < rep->min)\n      return false;\n\n   if(greedy)\n   {\n      if((rep->leading) && (count < rep->max))\n         restart = position;\n      // push backtrack info if available:\n      if(count - rep->min)\n         push_single_repeat(count, rep, position, saved_state_greedy_single_repeat);\n      // jump to next state:\n      pstate = rep->alt.p;\n      return true;\n   }\n   else\n   {\n      // non-greedy, push state and return true if we can skip:\n      if(count < rep->max)\n         push_single_repeat(count, rep, position, saved_state_rep_short_set);\n      pstate = rep->alt.p;\n      return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip);\n   }\n#ifdef BOOST_BORLANDC\n#pragma option pop\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_long_set_repeat()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n#ifdef BOOST_BORLANDC\n#pragma option push -w-8008 -w-8066 -w-8004\n#endif\n   typedef typename traits::char_class_type m_type;\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   const re_set_long<m_type>* set = static_cast<const re_set_long<m_type>*>(pstate->next.p);\n   std::size_t count = 0;\n   //\n   // start by working out how much we can skip:\n   //\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t desired = greedy ? rep->max : rep->min;\n   if(::boost::is_random_access_iterator<BidiIterator>::value)\n   {\n      BidiIterator end = position;\n      // Move end forward by \"desired\", preferably without using distance or advance if we can\n      // as these can be slow for some iterator types.\n      std::size_t len = (desired == (std::numeric_limits<std::size_t>::max)()) ? 0u : ::boost::BOOST_REGEX_DETAIL_NS::distance(position, last);\n      if(desired >= len)\n         end = last;\n      else\n         std::advance(end, desired);\n      BidiIterator origin(position);\n      while((position != end) && (position != re_is_set_member(position, last, set, re.get_data(), icase)))\n      {\n         ++position;\n      }\n      count = (unsigned)::boost::BOOST_REGEX_DETAIL_NS::distance(origin, position);\n   }\n   else\n   {\n      while((count < desired) && (position != last) && (position != re_is_set_member(position, last, set, re.get_data(), icase)))\n      {\n         ++position;\n         ++count;\n      }\n   }\n\n   if(count < rep->min)\n      return false;\n\n   if(greedy)\n   {\n      if((rep->leading) && (count < rep->max))\n         restart = position;\n      // push backtrack info if available:\n      if(count - rep->min)\n         push_single_repeat(count, rep, position, saved_state_greedy_single_repeat);\n      // jump to next state:\n      pstate = rep->alt.p;\n      return true;\n   }\n   else\n   {\n      // non-greedy, push state and return true if we can skip:\n      if(count < rep->max)\n         push_single_repeat(count, rep, position, saved_state_rep_long_set);\n      pstate = rep->alt.p;\n      return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip);\n   }\n#ifdef BOOST_BORLANDC\n#pragma option pop\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_recursion()\n{\n   BOOST_REGEX_ASSERT(pstate->type == syntax_element_recurse);\n   //\n   // See if we've seen this recursion before at this location, if we have then\n   // we need to prevent infinite recursion:\n   //\n   for(typename std::vector<recursion_info<results_type> >::reverse_iterator i = recursion_stack.rbegin(); i != recursion_stack.rend(); ++i)\n   {\n      if(i->idx == static_cast<const re_brace*>(static_cast<const re_jump*>(pstate)->alt.p)->index)\n      {\n         if(i->location_of_start == position)\n            return false;\n         break;\n      }\n   }\n   //\n   // Backup call stack:\n   //\n   push_recursion_pop();\n   //\n   // Set new call stack:\n   //\n   if(recursion_stack.capacity() == 0)\n   {\n      recursion_stack.reserve(50);\n   }\n   recursion_stack.push_back(recursion_info<results_type>());\n   recursion_stack.back().preturn_address = pstate->next.p;\n   recursion_stack.back().results = *m_presult;\n   pstate = static_cast<const re_jump*>(pstate)->alt.p;\n   recursion_stack.back().idx = static_cast<const re_brace*>(pstate)->index;\n   recursion_stack.back().location_of_start = position;\n   //if(static_cast<const re_recurse*>(pstate)->state_id > 0)\n   {\n      push_repeater_count(-(2 + static_cast<const re_brace*>(pstate)->index), &next_count);\n   }\n\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_endmark()\n{\n   int index = static_cast<const re_brace*>(pstate)->index;\n   icase = static_cast<const re_brace*>(pstate)->icase;\n   if(index > 0)\n   {\n      if((m_match_flags & match_nosubs) == 0)\n      {\n         m_presult->set_second(position, index);\n      }\n      if(!recursion_stack.empty())\n      {\n         if(index == recursion_stack.back().idx)\n         {\n            pstate = recursion_stack.back().preturn_address;\n            *m_presult = recursion_stack.back().results;\n            push_recursion(recursion_stack.back().idx, recursion_stack.back().preturn_address, m_presult, &recursion_stack.back().results);\n            recursion_stack.pop_back();\n            push_repeater_count(-(2 + index), &next_count);\n         }\n      }\n   }\n   else if((index < 0) && (index != -4))\n   {\n      // matched forward lookahead:\n      pstate = 0;\n      return true;\n   }\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_match()\n{\n   if(!recursion_stack.empty())\n   {\n      BOOST_REGEX_ASSERT(0 == recursion_stack.back().idx);\n      pstate = recursion_stack.back().preturn_address;\n      push_recursion(recursion_stack.back().idx, recursion_stack.back().preturn_address, m_presult, &recursion_stack.back().results);\n      *m_presult = recursion_stack.back().results;\n      recursion_stack.pop_back();\n      return true;\n   }\n   if((m_match_flags & match_not_null) && (position == (*m_presult)[0].first))\n      return false;\n   if((m_match_flags & match_all) && (position != last))\n      return false;\n   if((m_match_flags & regex_constants::match_not_initial_null) && (position == search_base))\n      return false;\n   m_presult->set_second(position);\n   pstate = 0;\n   m_has_found_match = true;\n   if((m_match_flags & match_posix) == match_posix)\n   {\n      m_result.maybe_assign(*m_presult);\n      if((m_match_flags & match_any) == 0)\n         return false;\n   }\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   if(match_extra & m_match_flags)\n   {\n      for(unsigned i = 0; i < m_presult->size(); ++i)\n         if((*m_presult)[i].matched)\n            ((*m_presult)[i]).get_captures().push_back((*m_presult)[i]);\n   }\n#endif\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_commit()\n{\n   // Ideally we would just junk all the states that are on the stack,\n   // however we might not unwind correctly in that case, so for now,\n   // just mark that we don't backtrack into whatever is left (or rather\n   // we'll unwind it unconditionally without pausing to try other matches).\n\n   switch(static_cast<const re_commit*>(pstate)->action)\n   {\n   case commit_commit:\n      restart = last;\n      break;\n   case commit_skip:\n      if(base != position)\n      {\n         restart = position;\n         // Have to decrement restart since it will get incremented again later:\n         --restart;\n      }\n      break;\n   case commit_prune:\n      break;\n   }\n\n   saved_state* pmp = m_backup_state;\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = m_backup_state;\n      --pmp;\n   }\n   (void) new (pmp)saved_state(16);\n   m_backup_state = pmp;\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_then()\n{\n   // Just leave a mark that we need to skip to next alternative:\n   saved_state* pmp = m_backup_state;\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = m_backup_state;\n      --pmp;\n   }\n   (void) new (pmp)saved_state(17);\n   m_backup_state = pmp;\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::skip_until_paren(int index, bool have_match)\n{\n   while(pstate)\n   {\n      if(pstate->type == syntax_element_endmark)\n      {\n         if(static_cast<const re_brace*>(pstate)->index == index)\n         {\n            if(have_match)\n               return this->match_endmark();\n            pstate = pstate->next.p;\n            return true;\n         }\n         else\n         {\n            // Unenclosed closing ), occurs when (*ACCEPT) is inside some other \n            // parenthesis which may or may not have other side effects associated with it.\n            const re_syntax_base* sp = pstate;\n            match_endmark();\n            if(!pstate)\n            {\n               unwind(true);\n               // unwind may leave pstate NULL if we've unwound a forward lookahead, in which\n               // case just move to the next state and keep looking...\n               if (!pstate)\n                  pstate = sp->next.p;\n            }\n         }\n         continue;\n      }\n      else if(pstate->type == syntax_element_match)\n         return true;\n      else if(pstate->type == syntax_element_startmark)\n      {\n         int idx = static_cast<const re_brace*>(pstate)->index;\n         pstate = pstate->next.p;\n         skip_until_paren(idx, false);\n         continue;\n      }\n      pstate = pstate->next.p;\n   }\n   return true;\n}\n\n/****************************************************************************\n\nUnwind and associated procedures follow, these perform what normal stack\nunwinding does in the recursive implementation.\n\n****************************************************************************/\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind(bool have_match)\n{\n   static unwind_proc_type const s_unwind_table[19] = \n   {\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_end,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_paren,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion_stopper,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_assertion,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_alt,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_repeater_counter,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_extra_block,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_greedy_single_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_slow_dot_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_fast_dot_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_char_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_short_set_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_long_set_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_non_greedy_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion_pop,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_commit,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_then,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_case,\n   };\n\n   m_recursive_result = have_match;\n   m_unwound_lookahead = false;\n   m_unwound_alt = false;\n   unwind_proc_type unwinder;\n   bool cont;\n   //\n   // keep unwinding our stack until we have something to do:\n   //\n   do\n   {\n      unwinder = s_unwind_table[m_backup_state->state_id];\n      cont = (this->*unwinder)(m_recursive_result);\n   }while(cont);\n   //\n   // return true if we have more states to try:\n   //\n   return pstate ? true : false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_end(bool)\n{\n   pstate = 0;   // nothing left to search\n   return false; // end of stack nothing more to search\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_case(bool)\n{\n   saved_change_case* pmp = static_cast<saved_change_case*>(m_backup_state);\n   icase = pmp->icase;\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_paren(bool have_match)\n{\n   saved_matched_paren<BidiIterator>* pmp = static_cast<saved_matched_paren<BidiIterator>*>(m_backup_state);\n   // restore previous values if no match was found:\n   if(!have_match)\n   {\n      m_presult->set_first(pmp->sub.first, pmp->index, pmp->index == 0);\n      m_presult->set_second(pmp->sub.second, pmp->index, pmp->sub.matched, pmp->index == 0);\n   }\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   //\n   // we have a match, push the capture information onto the stack:\n   //\n   else if(pmp->sub.matched && (match_extra & m_match_flags))\n      ((*m_presult)[pmp->index]).get_captures().push_back(pmp->sub);\n#endif\n   // unwind stack:\n   m_backup_state = pmp+1;\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp);\n   return true; // keep looking\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion_stopper(bool)\n{\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++);\n   pstate = 0;   // nothing left to search\n   return false; // end of stack nothing more to search\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_assertion(bool r)\n{\n   saved_assertion<BidiIterator>* pmp = static_cast<saved_assertion<BidiIterator>*>(m_backup_state);\n   pstate = pmp->pstate;\n   position = pmp->position;\n   bool result = (r == pmp->positive);\n   m_recursive_result = pmp->positive ? r : !r;\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   m_unwound_lookahead = true;\n   return !result; // return false if the assertion was matched to stop search.\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_alt(bool r)\n{\n   saved_position<BidiIterator>* pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n   if(!r)\n   {\n      pstate = pmp->pstate;\n      position = pmp->position;\n   }\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   m_unwound_alt = !r;\n   return r; \n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_repeater_counter(bool)\n{\n   ++used_block_count;\n   saved_repeater<BidiIterator>* pmp = static_cast<saved_repeater<BidiIterator>*>(m_backup_state);\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return true; // keep looking\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_extra_block(bool)\n{\n   saved_extra_block* pmp = static_cast<saved_extra_block*>(m_backup_state);\n   void* condemmed = m_stack_base;\n   m_stack_base = pmp->base;\n   m_backup_state = pmp->end;\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp);\n   put_mem_block(condemmed);\n   return true; // keep looking\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::destroy_single_repeat()\n{\n   saved_single_repeat<BidiIterator>* p = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(p++);\n   m_backup_state = p;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_greedy_single_repeat(bool r)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r) \n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n   BOOST_REGEX_ASSERT(rep->next.p != 0);\n   BOOST_REGEX_ASSERT(rep->alt.p != 0);\n\n   count -= rep->min;\n   \n   if((m_match_flags & match_partial) && (position == last))\n      m_has_partial_match = true;\n\n   BOOST_REGEX_ASSERT(count);\n   position = pmp->last_position;\n\n   // backtrack till we can skip out:\n   do\n   {\n      --position;\n      --count;\n      ++state_count;\n   }while(count && !can_start(*position, rep->_map, mask_skip));\n\n   // if we've hit base, destroy this state:\n   if(count == 0)\n   {\n         destroy_single_repeat();\n         if(!can_start(*position, rep->_map, mask_skip))\n            return true;\n   }\n   else\n   {\n      pmp->count = count + rep->min;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_slow_dot_repeat(bool r)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r) \n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n   BOOST_REGEX_ASSERT(rep->type == syntax_element_dot_rep);\n   BOOST_REGEX_ASSERT(rep->next.p != 0);\n   BOOST_REGEX_ASSERT(rep->alt.p != 0);\n   BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_wild);\n\n   BOOST_REGEX_ASSERT(count < rep->max);\n   pstate = rep->next.p;\n   position = pmp->last_position;\n\n   if(position != last)\n   {\n      // wind forward until we can skip out of the repeat:\n      do\n      {\n         if(!match_wild())\n         {\n            // failed repeat match, discard this state and look for another:\n            destroy_single_repeat();\n            return true;\n         }\n         ++count;\n         ++state_count;\n         pstate = rep->next.p;\n      }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip));\n   }   \n   if(position == last)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n         m_has_partial_match = true;\n      if(0 == (rep->can_be_null & mask_skip))\n         return true;\n   }\n   else if(count == rep->max)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if(!can_start(*position, rep->_map, mask_skip))\n         return true;\n   }\n   else\n   {\n      pmp->count = count;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_fast_dot_repeat(bool r)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r) \n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n\n   BOOST_REGEX_ASSERT(count < rep->max);\n   position = pmp->last_position;\n   if(position != last)\n   {\n\n      // wind forward until we can skip out of the repeat:\n      do\n      {\n         ++position;\n         ++count;\n         ++state_count;\n      }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip));\n   }\n\n   // remember where we got to if this is a leading repeat:\n   if((rep->leading) && (count < rep->max))\n      restart = position;\n   if(position == last)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n         m_has_partial_match = true;\n      if(0 == (rep->can_be_null & mask_skip))\n         return true;\n   }\n   else if(count == rep->max)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if(!can_start(*position, rep->_map, mask_skip))\n         return true;\n   }\n   else\n   {\n      pmp->count = count;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_char_repeat(bool r)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r) \n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n   pstate = rep->next.p;\n   const char_type what = *reinterpret_cast<const char_type*>(static_cast<const re_literal*>(pstate) + 1);\n   position = pmp->last_position;\n\n   BOOST_REGEX_ASSERT(rep->type == syntax_element_char_rep);\n   BOOST_REGEX_ASSERT(rep->next.p != 0);\n   BOOST_REGEX_ASSERT(rep->alt.p != 0);\n   BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_literal);\n   BOOST_REGEX_ASSERT(count < rep->max);\n\n   if(position != last)\n   {\n      // wind forward until we can skip out of the repeat:\n      do\n      {\n         if(traits_inst.translate(*position, icase) != what)\n         {\n            // failed repeat match, discard this state and look for another:\n            destroy_single_repeat();\n            return true;\n         }\n         ++count;\n         ++ position;\n         ++state_count;\n         pstate = rep->next.p;\n      }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip));\n   }   \n   // remember where we got to if this is a leading repeat:\n   if((rep->leading) && (count < rep->max))\n      restart = position;\n   if(position == last)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n         m_has_partial_match = true;\n      if(0 == (rep->can_be_null & mask_skip))\n         return true;\n   }\n   else if(count == rep->max)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if(!can_start(*position, rep->_map, mask_skip))\n         return true;\n   }\n   else\n   {\n      pmp->count = count;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_short_set_repeat(bool r)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r) \n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n   pstate = rep->next.p;\n   const unsigned char* map = static_cast<const re_set*>(rep->next.p)->_map;\n   position = pmp->last_position;\n\n   BOOST_REGEX_ASSERT(rep->type == syntax_element_short_set_rep);\n   BOOST_REGEX_ASSERT(rep->next.p != 0);\n   BOOST_REGEX_ASSERT(rep->alt.p != 0);\n   BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_set);\n   BOOST_REGEX_ASSERT(count < rep->max);\n   \n   if(position != last)\n   {\n      // wind forward until we can skip out of the repeat:\n      do\n      {\n         if(!map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n         {\n            // failed repeat match, discard this state and look for another:\n            destroy_single_repeat();\n            return true;\n         }\n         ++count;\n         ++ position;\n         ++state_count;\n         pstate = rep->next.p;\n      }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip));\n   }   \n   // remember where we got to if this is a leading repeat:\n   if((rep->leading) && (count < rep->max))\n      restart = position;\n   if(position == last)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n         m_has_partial_match = true;\n      if(0 == (rep->can_be_null & mask_skip))\n         return true;\n   }\n   else if(count == rep->max)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if(!can_start(*position, rep->_map, mask_skip))\n         return true;\n   }\n   else\n   {\n      pmp->count = count;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_long_set_repeat(bool r)\n{\n   typedef typename traits::char_class_type m_type;\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r)\n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n   pstate = rep->next.p;\n   const re_set_long<m_type>* set = static_cast<const re_set_long<m_type>*>(pstate);\n   position = pmp->last_position;\n\n   BOOST_REGEX_ASSERT(rep->type == syntax_element_long_set_rep);\n   BOOST_REGEX_ASSERT(rep->next.p != 0);\n   BOOST_REGEX_ASSERT(rep->alt.p != 0);\n   BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_long_set);\n   BOOST_REGEX_ASSERT(count < rep->max);\n\n   if(position != last)\n   {\n      // wind forward until we can skip out of the repeat:\n      do\n      {\n         if(position == re_is_set_member(position, last, set, re.get_data(), icase))\n         {\n            // failed repeat match, discard this state and look for another:\n            destroy_single_repeat();\n            return true;\n         }\n         ++position;\n         ++count;\n         ++state_count;\n         pstate = rep->next.p;\n      }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip));\n   }   \n   // remember where we got to if this is a leading repeat:\n   if((rep->leading) && (count < rep->max))\n      restart = position;\n   if(position == last)\n   {\n      // can't repeat any more, remove the pushed state:\n      destroy_single_repeat();\n      if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n         m_has_partial_match = true;\n      if(0 == (rep->can_be_null & mask_skip))\n         return true;\n   }\n   else if(count == rep->max)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if(!can_start(*position, rep->_map, mask_skip))\n         return true;\n   }\n   else\n   {\n      pmp->count = count;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_non_greedy_repeat(bool r)\n{\n   saved_position<BidiIterator>* pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n   if(!r)\n   {\n      position = pmp->position;\n      pstate = pmp->pstate;\n      ++(*next_count);\n   }\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return r;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion(bool r)\n{\n   // We are backtracking back inside a recursion, need to push the info\n   // back onto the recursion stack, and do so unconditionally, otherwise\n   // we can get mismatched pushes and pops...\n   saved_recursion<results_type>* pmp = static_cast<saved_recursion<results_type>*>(m_backup_state);\n   if (!r)\n   {\n      recursion_stack.push_back(recursion_info<results_type>());\n      recursion_stack.back().idx = pmp->recursion_id;\n      recursion_stack.back().preturn_address = pmp->preturn_address;\n      recursion_stack.back().results = pmp->prior_results;\n      recursion_stack.back().location_of_start = position;\n      *m_presult = pmp->internal_results;\n   }\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion_pop(bool r)\n{\n   // Backtracking out of a recursion, we must pop state off the recursion\n   // stack unconditionally to ensure matched pushes and pops:\n   saved_state* pmp = static_cast<saved_state*>(m_backup_state);\n   if (!r && !recursion_stack.empty())\n   {\n      *m_presult = recursion_stack.back().results;\n      position = recursion_stack.back().location_of_start;\n      recursion_stack.pop_back();\n   }\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nvoid perl_matcher<BidiIterator, Allocator, traits>::push_recursion_pop()\n{\n   saved_state* pmp = static_cast<saved_state*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_state*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_state(15);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_commit(bool b)\n{\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++);\n   while(unwind(b) && !m_unwound_lookahead){}\n   if(m_unwound_lookahead && pstate)\n   {\n      //\n      // If we stop because we just unwound an assertion, put the\n      // commit state back on the stack again:\n      //\n      m_unwound_lookahead = false;\n      saved_state* pmp = m_backup_state;\n      --pmp;\n      if(pmp < m_stack_base)\n      {\n         extend_stack();\n         pmp = m_backup_state;\n         --pmp;\n      }\n      (void) new (pmp)saved_state(16);\n      m_backup_state = pmp;\n   }\n   // This prevents us from stopping when we exit from an independent sub-expression:\n   m_independent = false;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_then(bool b)\n{\n   // Unwind everything till we hit an alternative:\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++);\n   bool result = false;\n   while((result = unwind(b)) && !m_unwound_alt){}\n   // We're now pointing at the next alternative, need one more backtrack \n   // since *all* the other alternatives must fail once we've reached a THEN clause:\n   if(result && m_unwound_alt)\n      unwind(b);\n   return false;\n}\n\n/*\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_parenthesis_pop(bool r)\n{\n   saved_state* pmp = static_cast<saved_state*>(m_backup_state);\n   if(!r)\n   {\n      --parenthesis_stack_position;\n   }\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nvoid perl_matcher<BidiIterator, Allocator, traits>::push_parenthesis_pop()\n{\n   saved_state* pmp = static_cast<saved_state*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_state*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_state(16);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_parenthesis_push(bool r)\n{\n   saved_position<BidiIterator>* pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n   if(!r)\n   {\n      parenthesis_stack[parenthesis_stack_position++] = pmp->position;\n   }\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_parenthesis_push(BidiIterator p)\n{\n   saved_position<BidiIterator>* pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_position<BidiIterator>(0, p, 17);\n   m_backup_state = pmp;\n}\n*/\n} // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/perl_matcher_recursive.hpp",
    "content": "/*\n *\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         perl_matcher_common.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Definitions of perl_matcher member functions that are \n  *                specific to the recursive implementation.\n  */\n\n#ifndef BOOST_REGEX_V4_PERL_MATCHER_RECURSIVE_HPP\n#define BOOST_REGEX_V4_PERL_MATCHER_RECURSIVE_HPP\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4800)\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class BidiIterator>\nclass backup_subex\n{\n   int index;\n   sub_match<BidiIterator> sub;\npublic:\n   template <class A>\n   backup_subex(const match_results<BidiIterator, A>& w, int i)\n      : index(i), sub(w[i], false) {}\n   template <class A>\n   void restore(match_results<BidiIterator, A>& w)\n   {\n      w.set_first(sub.first, index, index == 0);\n      w.set_second(sub.second, index, sub.matched, index == 0);\n   }\n   const sub_match<BidiIterator>& get() { return sub; }\n};\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_all_states()\n{\n   static matcher_proc_type const s_match_vtable[34] = \n   {\n      (&perl_matcher<BidiIterator, Allocator, traits>::match_startmark),\n      &perl_matcher<BidiIterator, Allocator, traits>::match_endmark,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_literal,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_start_line,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_end_line,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_wild,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_match,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_word_boundary,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_within_word,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_word_start,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_word_end,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_buffer_start,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_buffer_end,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_backref,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_long_set,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_set,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_jump,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_alt,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_rep,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_combining,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_soft_buffer_end,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_restart_continue,\n      // Although this next line *should* be evaluated at compile time, in practice\n      // some compilers (VC++) emit run-time initialisation which breaks thread\n      // safety, so use a dispatch function instead:\n      //(::boost::is_random_access_iterator<BidiIterator>::value ? &perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_fast : &perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_slow),\n      &perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_dispatch,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_char_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_set_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_long_set_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_backstep,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_assert_backref,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_toggle_case,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_recursion,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_fail,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_accept,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_commit,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_then,\n   };\n\n   if(state_count > max_state_count)\n      raise_error(traits_inst, regex_constants::error_complexity);\n   while(pstate)\n   {\n      matcher_proc_type proc = s_match_vtable[pstate->type];\n      ++state_count;\n      if(!(this->*proc)())\n      {\n         if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n            m_has_partial_match = true;\n         return 0;\n      }\n   }\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_startmark()\n{\n   int index = static_cast<const re_brace*>(pstate)->index;\n   icase = static_cast<const re_brace*>(pstate)->icase;\n   bool r = true;\n   switch(index)\n   {\n   case 0:\n      pstate = pstate->next.p;\n      break;\n   case -1:\n   case -2:\n      {\n         // forward lookahead assert:\n         BidiIterator old_position(position);\n         const re_syntax_base* next_pstate = static_cast<const re_jump*>(pstate->next.p)->alt.p->next.p;\n         pstate = pstate->next.p->next.p;\n         r = match_all_states();\n         pstate = next_pstate;\n         position = old_position;\n         if((r && (index != -1)) || (!r && (index != -2)))\n            r = false;\n         else\n            r = true;\n         if(r && m_have_accept)\n            r = skip_until_paren(INT_MAX);\n         break;\n      }\n   case -3:\n      {\n         // independent sub-expression:\n         bool old_independent = m_independent;\n         m_independent = true;\n         const re_syntax_base* next_pstate = static_cast<const re_jump*>(pstate->next.p)->alt.p->next.p;\n         pstate = pstate->next.p->next.p;\n         bool can_backtrack = m_can_backtrack;\n         r = match_all_states();\n         if(r)\n            m_can_backtrack = can_backtrack;\n         pstate = next_pstate;\n         m_independent = old_independent;\n#ifdef BOOST_REGEX_MATCH_EXTRA\n         if(r && (m_match_flags & match_extra))\n         {\n            //\n            // our captures have been stored in *m_presult\n            // we need to unpack them, and insert them\n            // back in the right order when we unwind the stack:\n            //\n            unsigned i;\n            match_results<BidiIterator, Allocator> tm(*m_presult);\n            for(i = 0; i < tm.size(); ++i)\n               (*m_presult)[i].get_captures().clear();\n            // match everything else:\n            r = match_all_states();\n            // now place the stored captures back:\n            for(i = 0; i < tm.size(); ++i)\n            {\n               typedef typename sub_match<BidiIterator>::capture_sequence_type seq;\n               seq& s1 = (*m_presult)[i].get_captures();\n               const seq& s2 = tm[i].captures();\n               s1.insert(\n                  s1.end(), \n                  s2.begin(), \n                  s2.end());\n            }\n         }\n#endif\n         if(r && m_have_accept)\n            r = skip_until_paren(INT_MAX);\n         break;\n      }\n   case -4:\n      {\n      // conditional expression:\n      const re_alt* alt = static_cast<const re_alt*>(pstate->next.p);\n      BOOST_REGEX_ASSERT(alt->type == syntax_element_alt);\n      pstate = alt->next.p;\n      if(pstate->type == syntax_element_assert_backref)\n      {\n         if(!match_assert_backref())\n            pstate = alt->alt.p;\n         break;\n      }\n      else\n      {\n         // zero width assertion, have to match this recursively:\n         BOOST_REGEX_ASSERT(pstate->type == syntax_element_startmark);\n         bool negated = static_cast<const re_brace*>(pstate)->index == -2;\n         BidiIterator saved_position = position;\n         const re_syntax_base* next_pstate = static_cast<const re_jump*>(pstate->next.p)->alt.p->next.p;\n         pstate = pstate->next.p->next.p;\n         bool res = match_all_states();\n         position = saved_position;\n         if(negated)\n            res = !res;\n         if(res)\n            pstate = next_pstate;\n         else\n            pstate = alt->alt.p;\n         break;\n      }\n      }\n   case -5:\n      {\n         // Reset start of $0, since we have a \\K escape\n         backup_subex<BidiIterator> sub(*m_presult, 0);\n         m_presult->set_first(position, 0, true);\n         pstate = pstate->next.p;\n         r = match_all_states();\n         if(r == false)\n            sub.restore(*m_presult);\n         break;\n      }\n   default:\n   {\n      BOOST_REGEX_ASSERT(index > 0);\n      if((m_match_flags & match_nosubs) == 0)\n      {\n         backup_subex<BidiIterator> sub(*m_presult, index);\n         m_presult->set_first(position, index);\n         pstate = pstate->next.p;\n         r = match_all_states();\n         if(r == false)\n            sub.restore(*m_presult);\n#ifdef BOOST_REGEX_MATCH_EXTRA\n         //\n         // we have a match, push the capture information onto the stack:\n         //\n         else if(sub.get().matched && (match_extra & m_match_flags))\n            ((*m_presult)[index]).get_captures().push_back(sub.get());\n#endif\n      }\n      else\n      {\n         pstate = pstate->next.p;\n      }\n      break;\n   }\n   }\n   return r;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_alt()\n{\n   bool take_first, take_second;\n   const re_alt* jmp = static_cast<const re_alt*>(pstate);\n\n   // find out which of these two alternatives we need to take:\n   if(position == last)\n   {\n      take_first = jmp->can_be_null & mask_take;\n      take_second = jmp->can_be_null & mask_skip;\n   }\n   else\n   {\n      take_first = can_start(*position, jmp->_map, (unsigned char)mask_take);\n      take_second = can_start(*position, jmp->_map, (unsigned char)mask_skip);\n  }\n\n   if(take_first)\n   {\n      // we can take the first alternative,\n      // see if we need to push next alternative:\n      if(take_second)\n      {\n         BidiIterator oldposition(position);\n         const re_syntax_base* old_pstate = jmp->alt.p;\n         pstate = pstate->next.p;\n         bool oldcase = icase;\n         m_have_then = false;\n         if(!match_all_states())\n         {\n            pstate = old_pstate;\n            position = oldposition;\n            icase = oldcase;\n            if(m_have_then)\n            {\n               m_can_backtrack = true;\n               m_have_then = false;\n               return false;\n            }\n         }\n         m_have_then = false;\n         return m_can_backtrack;\n      }\n      pstate = pstate->next.p;\n      return true;\n   }\n   if(take_second)\n   {\n      pstate = jmp->alt.p;\n      return true;\n   }\n   return false;  // neither option is possible\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_rep()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127 4244)\n#endif\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   //\n   // Always copy the repeat count, so that the state is restored\n   // when we exit this scope:\n   //\n   repeater_count<BidiIterator> r(rep->state_id, &next_count, position, this->recursion_stack.size() ? this->recursion_stack.back().idx : INT_MIN + 3);\n   //\n   // If we've had at least one repeat already, and the last one \n   // matched the NULL string then set the repeat count to\n   // maximum:\n   //\n   next_count->check_null_repeat(position, rep->max);\n\n   // find out which of these two alternatives we need to take:\n   bool take_first, take_second;\n   if(position == last)\n   {\n      take_first = rep->can_be_null & mask_take;\n      take_second = rep->can_be_null & mask_skip;\n   }\n   else\n   {\n      take_first = can_start(*position, rep->_map, (unsigned char)mask_take);\n      take_second = can_start(*position, rep->_map, (unsigned char)mask_skip);\n   }\n\n   if(next_count->get_count() < rep->min)\n   {\n      // we must take the repeat:\n      if(take_first)\n      {\n         // increase the counter:\n         ++(*next_count);\n         pstate = rep->next.p;\n         return match_all_states();\n      }\n      return false;\n   }\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   if(greedy)\n   {\n      // try and take the repeat if we can:\n      if((next_count->get_count() < rep->max) && take_first)\n      {\n         // store position in case we fail:\n         BidiIterator pos = position;\n         // increase the counter:\n         ++(*next_count);\n         pstate = rep->next.p;\n         if(match_all_states())\n            return true;\n         if(!m_can_backtrack)\n            return false;\n         // failed repeat, reset posistion and fall through for alternative:\n         position = pos;\n      }\n      if(take_second)\n      {\n         pstate = rep->alt.p;\n         return true;\n      }\n      return false; // can't take anything, fail...\n   }\n   else // non-greedy\n   {\n      // try and skip the repeat if we can:\n      if(take_second)\n      {\n         // store position in case we fail:\n         BidiIterator pos = position;\n         pstate = rep->alt.p;\n         if(match_all_states())\n            return true;\n         if(!m_can_backtrack)\n            return false;\n         // failed alternative, reset posistion and fall through for repeat:\n         position = pos;\n      }\n      if((next_count->get_count() < rep->max) && take_first)\n      {\n         // increase the counter:\n         ++(*next_count);\n         pstate = rep->next.p;\n         return match_all_states();\n      }\n   }\n   return false;\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_slow()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   std::size_t count = 0;\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   re_syntax_base* psingle = rep->next.p;\n   // match compulsary repeats first:\n   while(count < rep->min)\n   {\n      pstate = psingle;\n      if(!match_wild())\n         return false;\n      ++count;\n   }\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   if(greedy)\n   {\n      // normal repeat:\n      while(count < rep->max)\n      {\n         pstate = psingle;\n         if(!match_wild())\n            break;\n         ++count;\n      }\n      if((rep->leading) && (count < rep->max))\n         restart = position;\n      pstate = rep;\n      return backtrack_till_match(count - rep->min);\n   }\n   else\n   {\n      // non-greedy, keep trying till we get a match:\n      BidiIterator save_pos;\n      do\n      {\n         if((rep->leading) && (rep->max == UINT_MAX))\n            restart = position;\n         pstate = rep->alt.p;\n         save_pos = position;\n         ++state_count;\n         if(match_all_states())\n            return true;\n         if((count >= rep->max) || !m_can_backtrack)\n            return false;\n         ++count;\n         pstate = psingle;\n         position = save_pos;\n         if(!match_wild())\n            return false;\n      }while(true);\n   }\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_fast()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   if(m_match_flags & match_not_dot_null)\n      return match_dot_repeat_slow();\n   if((static_cast<const re_dot*>(pstate->next.p)->mask & match_any_mask) == 0)\n      return match_dot_repeat_slow();\n   //\n   // start by working out how much we can skip:\n   //\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4267)\n#endif\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t count = (std::min)(static_cast<std::size_t>(::boost::BOOST_REGEX_DETAIL_NS::distance(position, last)), greedy ? rep->max : rep->min);\n   if(rep->min > count)\n   {\n      position = last;\n      return false;  // not enough text left to match\n   }\n   std::advance(position, count);\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n   if((rep->leading) && (count < rep->max) && greedy)\n      restart = position;\n   if(greedy)\n      return backtrack_till_match(count - rep->min);\n\n   // non-greedy, keep trying till we get a match:\n   BidiIterator save_pos;\n   do\n   {\n      while((position != last) && (count < rep->max) && !can_start(*position, rep->_map, mask_skip))\n      {\n         ++position;\n         ++count;\n      }\n      if((rep->leading) && (rep->max == UINT_MAX))\n         restart = position;\n      pstate = rep->alt.p;\n      save_pos = position;\n      ++state_count;\n      if(match_all_states())\n         return true;\n      if((count >= rep->max) || !m_can_backtrack)\n         return false;\n      if(save_pos == last)\n         return false;\n      position = ++save_pos;\n      ++count;\n   }while(true);\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_char_repeat()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#pragma warning(disable:4267)\n#endif\n#ifdef BOOST_BORLANDC\n#pragma option push -w-8008 -w-8066 -w-8004\n#endif\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   BOOST_REGEX_ASSERT(1 == static_cast<const re_literal*>(rep->next.p)->length);\n   const char_type what = *reinterpret_cast<const char_type*>(static_cast<const re_literal*>(rep->next.p) + 1);\n   //\n   // start by working out how much we can skip:\n   //\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t count, desired;\n   if(::boost::is_random_access_iterator<BidiIterator>::value)\n   {\n      desired = \n         (std::min)(\n            (std::size_t)(greedy ? rep->max : rep->min),\n            (std::size_t)::boost::BOOST_REGEX_DETAIL_NS::distance(position, last));\n      count = desired;\n      ++desired;\n      if(icase)\n      {\n         while(--desired && (traits_inst.translate_nocase(*position) == what))\n         {\n            ++position;\n         }\n      }\n      else\n      {\n         while(--desired && (traits_inst.translate(*position) == what))\n         {\n            ++position;\n         }\n      }\n      count = count - desired;\n   }\n   else\n   {\n      count = 0;\n      desired = greedy ? rep->max : rep->min;\n      while((count < desired) && (position != last) && (traits_inst.translate(*position, icase) == what))\n      {\n         ++position;\n         ++count;\n      }\n   }\n   if((rep->leading) && (count < rep->max) && greedy)\n      restart = position;\n   if(count < rep->min)\n      return false;\n\n   if(greedy)\n      return backtrack_till_match(count - rep->min);\n\n   // non-greedy, keep trying till we get a match:\n   BidiIterator save_pos;\n   do\n   {\n      while((position != last) && (count < rep->max) && !can_start(*position, rep->_map, mask_skip))\n      {\n         if((traits_inst.translate(*position, icase) == what))\n         {\n            ++position;\n            ++count;\n         }\n         else\n            return false;  // counldn't repeat even though it was the only option\n      }\n      if((rep->leading) && (rep->max == UINT_MAX))\n         restart = position;\n      pstate = rep->alt.p;\n      save_pos = position;\n      ++state_count;\n      if(match_all_states())\n         return true;\n      if((count >= rep->max) || !m_can_backtrack)\n         return false;\n      position = save_pos;\n      if(position == last)\n         return false;\n      if(traits_inst.translate(*position, icase) == what)\n      {\n         ++position;\n         ++count;\n      }\n      else\n      {\n         return false;\n      }\n   }while(true);\n#ifdef BOOST_BORLANDC\n#pragma option pop\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_set_repeat()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n#ifdef BOOST_BORLANDC\n#pragma option push -w-8008 -w-8066 -w-8004\n#endif\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   const unsigned char* map = static_cast<const re_set*>(rep->next.p)->_map;\n   std::size_t count = 0;\n   //\n   // start by working out how much we can skip:\n   //\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t desired = greedy ? rep->max : rep->min;\n   if(::boost::is_random_access_iterator<BidiIterator>::value)\n   {\n      BidiIterator end = position;\n      // Move end forward by \"desired\", preferably without using distance or advance if we can\n      // as these can be slow for some iterator types.\n      std::size_t len = (desired == (std::numeric_limits<std::size_t>::max)()) ? 0u : ::boost::BOOST_REGEX_DETAIL_NS::distance(position, last);\n      if(desired >= len)\n         end = last;\n      else\n         std::advance(end, desired);\n      BidiIterator origin(position);\n      while((position != end) && map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n      {\n         ++position;\n      }\n      count = (unsigned)::boost::BOOST_REGEX_DETAIL_NS::distance(origin, position);\n   }\n   else\n   {\n      while((count < desired) && (position != last) && map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n      {\n         ++position;\n         ++count;\n      }\n   }\n   if((rep->leading) && (count < rep->max) && greedy)\n      restart = position;\n   if(count < rep->min)\n      return false;\n\n   if(greedy)\n      return backtrack_till_match(count - rep->min);\n\n   // non-greedy, keep trying till we get a match:\n   BidiIterator save_pos;\n   do\n   {\n      while((position != last) && (count < rep->max) && !can_start(*position, rep->_map, mask_skip))\n      {\n         if(map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n         {\n            ++position;\n            ++count;\n         }\n         else\n            return false;  // counldn't repeat even though it was the only option\n      }\n      if((rep->leading) && (rep->max == UINT_MAX))\n         restart = position;\n      pstate = rep->alt.p;\n      save_pos = position;\n      ++state_count;\n      if(match_all_states())\n         return true;\n      if((count >= rep->max) || !m_can_backtrack)\n         return false;\n      position = save_pos;\n      if(position == last)\n         return false;\n      if(map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n      {\n         ++position;\n         ++count;\n      }\n      else\n      {\n         return false;\n      }\n   }while(true);\n#ifdef BOOST_BORLANDC\n#pragma option pop\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_long_set_repeat()\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n#ifdef BOOST_BORLANDC\n#pragma option push -w-8008 -w-8066 -w-8004\n#endif\n   typedef typename traits::char_class_type char_class_type;\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   const re_set_long<char_class_type>* set = static_cast<const re_set_long<char_class_type>*>(pstate->next.p);\n   std::size_t count = 0;\n   //\n   // start by working out how much we can skip:\n   //\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t desired = greedy ? rep->max : rep->min;\n   if(::boost::is_random_access_iterator<BidiIterator>::value)\n   {\n      BidiIterator end = position;\n      // Move end forward by \"desired\", preferably without using distance or advance if we can\n      // as these can be slow for some iterator types.\n      std::size_t len = (desired == (std::numeric_limits<std::size_t>::max)()) ? 0u : ::boost::BOOST_REGEX_DETAIL_NS::distance(position, last);\n      if(desired >= len)\n         end = last;\n      else\n         std::advance(end, desired);\n      BidiIterator origin(position);\n      while((position != end) && (position != re_is_set_member(position, last, set, re.get_data(), icase)))\n      {\n         ++position;\n      }\n      count = (unsigned)::boost::BOOST_REGEX_DETAIL_NS::distance(origin, position);\n   }\n   else\n   {\n      while((count < desired) && (position != last) && (position != re_is_set_member(position, last, set, re.get_data(), icase)))\n      {\n         ++position;\n         ++count;\n      }\n   }\n   if((rep->leading) && (count < rep->max) && greedy)\n      restart = position;\n   if(count < rep->min)\n      return false;\n\n   if(greedy)\n      return backtrack_till_match(count - rep->min);\n\n   // non-greedy, keep trying till we get a match:\n   BidiIterator save_pos;\n   do\n   {\n      while((position != last) && (count < rep->max) && !can_start(*position, rep->_map, mask_skip))\n      {\n         if(position != re_is_set_member(position, last, set, re.get_data(), icase))\n         {\n            ++position;\n            ++count;\n         }\n         else\n            return false;  // counldn't repeat even though it was the only option\n      }\n      if((rep->leading) && (rep->max == UINT_MAX))\n         restart = position;\n      pstate = rep->alt.p;\n      save_pos = position;\n      ++state_count;\n      if(match_all_states())\n         return true;\n      if((count >= rep->max) || !m_can_backtrack)\n         return false;\n      position = save_pos;\n      if(position == last)\n         return false;\n      if(position != re_is_set_member(position, last, set, re.get_data(), icase))\n      {\n         ++position;\n         ++count;\n      }\n      else\n      {\n         return false;\n      }\n   }while(true);\n#ifdef BOOST_BORLANDC\n#pragma option pop\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::backtrack_till_match(std::size_t count)\n{\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   if(!m_can_backtrack)\n      return false;\n   if((m_match_flags & match_partial) && (position == last))\n      m_has_partial_match = true;\n\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   BidiIterator backtrack = position;\n   if(position == last)\n   {\n      if(rep->can_be_null & mask_skip) \n      {\n         pstate = rep->alt.p;\n         if(match_all_states())\n            return true;\n      }\n      if(count)\n      {\n         position = --backtrack;\n         --count;\n      }\n      else\n         return false;\n   }\n   do\n   {\n      while(count && !can_start(*position, rep->_map, mask_skip))\n      {\n         --position;\n         --count;\n         ++state_count;\n      }\n      pstate = rep->alt.p;\n      backtrack = position;\n      if(match_all_states())\n         return true;\n      if(count == 0)\n         return false;\n      position = --backtrack;\n      ++state_count;\n      --count;\n   }while(true);\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_recursion()\n{\n   BOOST_REGEX_ASSERT(pstate->type == syntax_element_recurse);\n   //\n   // Set new call stack:\n   //\n   if(recursion_stack.capacity() == 0)\n   {\n      recursion_stack.reserve(50);\n   }\n   //\n   // See if we've seen this recursion before at this location, if we have then\n   // we need to prevent infinite recursion:\n   //\n   for(typename std::vector<recursion_info<results_type> >::reverse_iterator i = recursion_stack.rbegin(); i != recursion_stack.rend(); ++i)\n   {\n      if(i->idx == static_cast<const re_brace*>(static_cast<const re_jump*>(pstate)->alt.p)->index)\n      {\n         if(i->location_of_start == position)\n            return false;\n         break;\n      }\n   }\n   //\n   // Now get on with it:\n   //\n   recursion_stack.push_back(recursion_info<results_type>());\n   recursion_stack.back().preturn_address = pstate->next.p;\n   recursion_stack.back().results = *m_presult;\n   recursion_stack.back().repeater_stack = next_count;\n   recursion_stack.back().location_of_start = position;\n   pstate = static_cast<const re_jump*>(pstate)->alt.p;\n   recursion_stack.back().idx = static_cast<const re_brace*>(pstate)->index;\n\n   repeater_count<BidiIterator>* saved = next_count;\n   repeater_count<BidiIterator> r(&next_count); // resets all repeat counts since we're recursing and starting fresh on those\n   next_count = &r;\n   bool can_backtrack = m_can_backtrack;\n   bool result = match_all_states();\n   m_can_backtrack = can_backtrack;\n   next_count = saved;\n\n   if(!result)\n   {\n      next_count = recursion_stack.back().repeater_stack;\n      *m_presult = recursion_stack.back().results;\n      recursion_stack.pop_back();\n      return false;\n   }\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_endmark()\n{\n   int index = static_cast<const re_brace*>(pstate)->index;\n   icase = static_cast<const re_brace*>(pstate)->icase;\n   if(index > 0)\n   {\n      if((m_match_flags & match_nosubs) == 0)\n      {\n         m_presult->set_second(position, index);\n      }\n      if(!recursion_stack.empty())\n      {\n         if(index == recursion_stack.back().idx)\n         {\n            recursion_info<results_type> saved = recursion_stack.back();\n            recursion_stack.pop_back();\n            pstate = saved.preturn_address;\n            repeater_count<BidiIterator>* saved_count = next_count;\n            next_count = saved.repeater_stack;\n            *m_presult = saved.results;\n            if(!match_all_states())\n            {\n               recursion_stack.push_back(saved);\n               next_count = saved_count;\n               return false;\n            }\n         }\n      }\n   }\n   else if((index < 0) && (index != -4))\n   {\n      // matched forward lookahead:\n      pstate = 0;\n      return true;\n   }\n   pstate = pstate ? pstate->next.p : 0;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_match()\n{\n   if(!recursion_stack.empty())\n   {\n      BOOST_REGEX_ASSERT(0 == recursion_stack.back().idx);\n      const re_syntax_base* saved_state = pstate = recursion_stack.back().preturn_address;\n      *m_presult = recursion_stack.back().results;\n      recursion_stack.pop_back();\n      if(!match_all_states())\n      {\n         recursion_stack.push_back(recursion_info<results_type>());\n         recursion_stack.back().preturn_address = saved_state;\n         recursion_stack.back().results = *m_presult;\n         recursion_stack.back().location_of_start = position;\n         return false;\n      }\n      return true;\n   }\n   if((m_match_flags & match_not_null) && (position == (*m_presult)[0].first))\n      return false;\n   if((m_match_flags & match_all) && (position != last))\n      return false;\n   if((m_match_flags & regex_constants::match_not_initial_null) && (position == search_base))\n      return false;\n   m_presult->set_second(position);\n   pstate = 0;\n   m_has_found_match = true;\n   if((m_match_flags & match_posix) == match_posix)\n   {\n      m_result.maybe_assign(*m_presult);\n      if((m_match_flags & match_any) == 0)\n         return false;\n   }\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   if(match_extra & m_match_flags)\n   {\n      for(unsigned i = 0; i < m_presult->size(); ++i)\n         if((*m_presult)[i].matched)\n            ((*m_presult)[i]).get_captures().push_back((*m_presult)[i]);\n   }\n#endif\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_commit()\n{\n   m_can_backtrack = false;\n   int action = static_cast<const re_commit*>(pstate)->action;\n   switch(action)\n   {\n   case commit_commit:\n      restart = last;\n      break;\n   case commit_skip:\n      restart = position;\n      break;\n   }\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_then()\n{\n   pstate = pstate->next.p;\n   if(match_all_states())\n      return true;\n   m_can_backtrack = false;\n   m_have_then = true;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_toggle_case()\n{\n   // change our case sensitivity:\n   bool oldcase = this->icase;\n   this->icase = static_cast<const re_case*>(pstate)->icase;\n   pstate = pstate->next.p;\n   bool result = match_all_states();\n   this->icase = oldcase;\n   return result;\n}\n\n\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::skip_until_paren(int index, bool have_match)\n{\n   while(pstate)\n   {\n      if(pstate->type == syntax_element_endmark)\n      {\n         if(static_cast<const re_brace*>(pstate)->index == index)\n         {\n            if(have_match)\n               return this->match_endmark();\n            pstate = pstate->next.p;\n            return true;\n         }\n         else\n         {\n            // Unenclosed closing ), occurs when (*ACCEPT) is inside some other \n            // parenthesis which may or may not have other side effects associated with it.\n            bool r = match_endmark();\n            m_have_accept = true;\n            if(!pstate)\n               return r;\n         }\n         continue;\n      }\n      else if(pstate->type == syntax_element_match)\n         return true;\n      else if(pstate->type == syntax_element_startmark)\n      {\n         int idx = static_cast<const re_brace*>(pstate)->index;\n         pstate = pstate->next.p;\n         skip_until_paren(idx, false);\n         continue;\n      }\n      pstate = pstate->next.p;\n   }\n   return true;\n}\n\n\n} // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/primary_transform.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE:        primary_transform.hpp\n  *   VERSION:     see <boost/version.hpp>\n  *   DESCRIPTION: Heuristically determines the sort string format in use\n  *                by the current locale.\n  */\n\n#ifndef BOOST_REGEX_PRIMARY_TRANSFORM\n#define BOOST_REGEX_PRIMARY_TRANSFORM\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\n   namespace BOOST_REGEX_DETAIL_NS{\n\n\nenum{\n   sort_C,\n   sort_fixed,\n   sort_delim,\n   sort_unknown\n};\n\ntemplate <class S, class charT>\nunsigned count_chars(const S& s, charT c)\n{\n   //\n   // Count how many occurrences of character c occur\n   // in string s: if c is a delimeter between collation\n   // fields, then this should be the same value for all\n   // sort keys:\n   //\n   unsigned int count = 0;\n   for(unsigned pos = 0; pos < s.size(); ++pos)\n   {\n      if(s[pos] == c) ++count;\n   }\n   return count;\n}\n\n\ntemplate <class traits, class charT>\nunsigned find_sort_syntax(const traits* pt, charT* delim)\n{\n   //\n   // compare 'a' with 'A' to see how similar they are,\n   // should really use a-accute but we can't portably do that,\n   //\n   typedef typename traits::string_type string_type;\n   typedef typename traits::char_type char_type;\n\n   // Suppress incorrect warning for MSVC\n   (void)pt;\n\n   char_type a[2] = {'a', '\\0', };\n   string_type sa(pt->transform(a, a+1));\n   if(sa == a)\n   {\n      *delim = 0;\n      return sort_C;\n   }\n   char_type A[2] = { 'A', '\\0', };\n   string_type sA(pt->transform(A, A+1));\n   char_type c[2] = { ';', '\\0', };\n   string_type sc(pt->transform(c, c+1));\n\n   int pos = 0;\n   while((pos <= static_cast<int>(sa.size())) && (pos <= static_cast<int>(sA.size())) && (sa[pos] == sA[pos])) ++pos;\n   --pos;\n   if(pos < 0)\n   {\n      *delim = 0;\n      return sort_unknown;\n   }\n   //\n   // at this point sa[pos] is either the end of a fixed width field\n   // or the character that acts as a delimiter:\n   //\n   charT maybe_delim = sa[pos];\n   if((pos != 0) && (count_chars(sa, maybe_delim) == count_chars(sA, maybe_delim)) && (count_chars(sa, maybe_delim) == count_chars(sc, maybe_delim)))\n   {\n      *delim = maybe_delim;\n      return sort_delim;\n   }\n   //\n   // OK doen't look like a delimiter, try for fixed width field:\n   //\n   if((sa.size() == sA.size()) && (sa.size() == sc.size()))\n   {\n      // note assumes that the fixed width field is less than\n      // (numeric_limits<charT>::max)(), should be true for all types\n      // I can't imagine 127 character fields...\n      *delim = static_cast<charT>(++pos);\n      return sort_fixed;\n   }\n   //\n   // don't know what it is:\n   //\n   *delim = 0;\n   return sort_unknown;\n}\n\n\n   } // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n\n\n\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/protected_call.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         basic_regex_creator.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class basic_regex_creator which fills in\n  *                the data members of a regex_data object.\n  */\n\n#ifndef BOOST_REGEX_V4_PROTECTED_CALL_HPP\n#define BOOST_REGEX_V4_PROTECTED_CALL_HPP\n\n#include <boost/config.hpp>\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\nclass BOOST_REGEX_DECL abstract_protected_call\n{\npublic:\n   bool BOOST_REGEX_CALL execute()const;\n   // this stops gcc-4 from complaining:\n   virtual ~abstract_protected_call(){}\nprivate:\n   virtual bool call()const = 0;\n};\n\ntemplate <class T>\nclass concrete_protected_call\n   : public abstract_protected_call\n{\npublic:\n   typedef bool (T::*proc_type)();\n   concrete_protected_call(T* o, proc_type p)\n      : obj(o), proc(p) {}\nprivate:\n   bool call()const BOOST_OVERRIDE;\n   T* obj;\n   proc_type proc;\n};\n\ntemplate <class T>\nbool concrete_protected_call<T>::call()const\n{\n   return (obj->*proc)();\n}\n\n}\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regbase.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regbase.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares class regbase.\n  */\n\n#ifndef BOOST_REGEX_V4_REGBASE_HPP\n#define BOOST_REGEX_V4_REGBASE_HPP\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\n//\n// class regbase\n// handles error codes and flags\n//\nclass BOOST_REGEX_DECL regbase\n{\npublic:\n   enum flag_type_\n   {\n      //\n      // Divide the flags up into logical groups:\n      // bits 0-7 indicate main synatx type.\n      // bits 8-15 indicate syntax subtype.\n      // bits 16-31 indicate options that are common to all\n      // regex syntaxes.\n      // In all cases the default is 0.\n      //\n      // Main synatx group:\n      //\n      perl_syntax_group = 0,                      // default\n      basic_syntax_group = 1,                     // POSIX basic\n      literal = 2,                                // all characters are literals\n      main_option_type = literal | basic_syntax_group | perl_syntax_group, // everything!\n      //\n      // options specific to perl group:\n      //\n      no_bk_refs = 1 << 8,                        // \\d not allowed\n      no_perl_ex = 1 << 9,                        // disable perl extensions\n      no_mod_m = 1 << 10,                         // disable Perl m modifier\n      mod_x = 1 << 11,                            // Perl x modifier\n      mod_s = 1 << 12,                            // force s modifier on (overrides match_not_dot_newline)\n      no_mod_s = 1 << 13,                         // force s modifier off (overrides match_not_dot_newline)\n\n      //\n      // options specific to basic group:\n      //\n      no_char_classes = 1 << 8,                   // [[:CLASS:]] not allowed\n      no_intervals = 1 << 9,                      // {x,y} not allowed\n      bk_plus_qm = 1 << 10,                       // uses \\+ and \\?\n      bk_vbar = 1 << 11,                          // use \\| for alternatives\n      emacs_ex = 1 << 12,                         // enables emacs extensions\n\n      //\n      // options common to all groups:\n      //\n      no_escape_in_lists = 1 << 16,                     // '\\' not special inside [...]\n      newline_alt = 1 << 17,                            // \\n is the same as |\n      no_except = 1 << 18,                              // no exception on error\n      failbit = 1 << 19,                                // error flag\n      icase = 1 << 20,                                  // characters are matched regardless of case\n      nocollate = 0,                                    // don't use locale specific collation (deprecated)\n      collate = 1 << 21,                                // use locale specific collation\n      nosubs = 1 << 22,                                 // don't mark sub-expressions\n      save_subexpression_location = 1 << 23,            // save subexpression locations\n      no_empty_expressions = 1 << 24,                   // no empty expressions allowed\n      optimize = 0,                                     // not really supported\n      \n\n\n      basic = basic_syntax_group | collate | no_escape_in_lists,\n      extended = no_bk_refs | collate | no_perl_ex | no_escape_in_lists,\n      normal = 0,\n      emacs = basic_syntax_group | collate | emacs_ex | bk_vbar,\n      awk = no_bk_refs | collate | no_perl_ex,\n      grep = basic | newline_alt,\n      egrep = extended | newline_alt,\n      sed = basic,\n      perl = normal,\n      ECMAScript = normal,\n      JavaScript = normal,\n      JScript = normal\n   };\n   typedef unsigned int flag_type;\n\n   enum restart_info\n   {\n      restart_any = 0,\n      restart_word = 1,\n      restart_line = 2,\n      restart_buf = 3,\n      restart_continue = 4,\n      restart_lit = 5,\n      restart_fixed_lit = 6, \n      restart_count = 7\n   };\n};\n\n//\n// provide std lib proposal compatible constants:\n//\nnamespace regex_constants{\n\n   enum flag_type_\n   {\n\n      no_except = ::boost::regbase::no_except,\n      failbit = ::boost::regbase::failbit,\n      literal = ::boost::regbase::literal,\n      icase = ::boost::regbase::icase,\n      nocollate = ::boost::regbase::nocollate,\n      collate = ::boost::regbase::collate,\n      nosubs = ::boost::regbase::nosubs,\n      optimize = ::boost::regbase::optimize,\n      bk_plus_qm = ::boost::regbase::bk_plus_qm,\n      bk_vbar = ::boost::regbase::bk_vbar,\n      no_intervals = ::boost::regbase::no_intervals,\n      no_char_classes = ::boost::regbase::no_char_classes,\n      no_escape_in_lists = ::boost::regbase::no_escape_in_lists,\n      no_mod_m = ::boost::regbase::no_mod_m,\n      mod_x = ::boost::regbase::mod_x,\n      mod_s = ::boost::regbase::mod_s,\n      no_mod_s = ::boost::regbase::no_mod_s,\n      save_subexpression_location = ::boost::regbase::save_subexpression_location,\n      no_empty_expressions = ::boost::regbase::no_empty_expressions,\n\n      basic = ::boost::regbase::basic,\n      extended = ::boost::regbase::extended,\n      normal = ::boost::regbase::normal,\n      emacs = ::boost::regbase::emacs,\n      awk = ::boost::regbase::awk,\n      grep = ::boost::regbase::grep,\n      egrep = ::boost::regbase::egrep,\n      sed = basic,\n      perl = normal,\n      ECMAScript = normal,\n      JavaScript = normal,\n      JScript = normal\n   };\n   typedef ::boost::regbase::flag_type syntax_option_type;\n\n} // namespace regex_constants\n\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares boost::basic_regex<> and associated\n  *                functions and classes. This header is the main\n  *                entry point for the template regex code.\n  */\n\n#ifndef BOOST_RE_REGEX_HPP_INCLUDED\n#define BOOST_RE_REGEX_HPP_INCLUDED\n\n#ifdef __cplusplus\n\n// what follows is all C++ don't include in C builds!!\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n#ifndef BOOST_REGEX_WORKAROUND_HPP\n#include <boost/regex/v4/regex_workaround.hpp>\n#endif\n\n#ifndef BOOST_REGEX_FWD_HPP\n#include <boost/regex_fwd.hpp>\n#endif\n#ifndef BOOST_REGEX_TRAITS_HPP\n#include <boost/regex/regex_traits.hpp>\n#endif\n#ifndef BOOST_REGEX_RAW_BUFFER_HPP\n#include <boost/regex/v4/error_type.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_MATCH_FLAGS\n#include <boost/regex/v4/match_flags.hpp>\n#endif\n#ifndef BOOST_REGEX_RAW_BUFFER_HPP\n#include <boost/regex/v4/regex_raw_buffer.hpp>\n#endif\n#ifndef BOOST_RE_PAT_EXCEPT_HPP\n#include <boost/regex/pattern_except.hpp>\n#endif\n\n#ifndef BOOST_REGEX_V4_CHAR_REGEX_TRAITS_HPP\n#include <boost/regex/v4/char_regex_traits.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_STATES_HPP\n#include <boost/regex/v4/states.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_REGBASE_HPP\n#include <boost/regex/v4/regbase.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_ITERATOR_TRAITS_HPP\n#include <boost/regex/v4/iterator_traits.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_BASIC_REGEX_HPP\n#include <boost/regex/v4/basic_regex.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_BASIC_REGEX_CREATOR_HPP\n#include <boost/regex/v4/basic_regex_creator.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_BASIC_REGEX_PARSER_HPP\n#include <boost/regex/v4/basic_regex_parser.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_SUB_MATCH_HPP\n#include <boost/regex/v4/sub_match.hpp>\n#endif\n#ifndef BOOST_REGEX_FORMAT_HPP\n#include <boost/regex/v4/regex_format.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_MATCH_RESULTS_HPP\n#include <boost/regex/v4/match_results.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_PROTECTED_CALL_HPP\n#include <boost/regex/v4/protected_call.hpp>\n#endif\n#ifndef BOOST_REGEX_MATCHER_HPP\n#include <boost/regex/v4/perl_matcher.hpp>\n#endif\n\n\nnamespace boost{\n#ifdef BOOST_REGEX_NO_FWD\ntypedef basic_regex<char, regex_traits<char> > regex;\n#ifndef BOOST_NO_WREGEX\ntypedef basic_regex<wchar_t, regex_traits<wchar_t> > wregex;\n#endif\n#endif\n\ntypedef match_results<const char*> cmatch;\ntypedef match_results<std::string::const_iterator> smatch;\n#ifndef BOOST_NO_WREGEX\ntypedef match_results<const wchar_t*> wcmatch;\ntypedef match_results<std::wstring::const_iterator> wsmatch;\n#endif\n\n} // namespace boost\n#ifndef BOOST_REGEX_MATCH_HPP\n#include <boost/regex/v4/regex_match.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_REGEX_SEARCH_HPP\n#include <boost/regex/v4/regex_search.hpp>\n#endif\n#ifndef BOOST_REGEX_ITERATOR_HPP\n#include <boost/regex/v4/regex_iterator.hpp>\n#endif\n#ifndef BOOST_REGEX_TOKEN_ITERATOR_HPP\n#include <boost/regex/v4/regex_token_iterator.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_REGEX_GREP_HPP\n#include <boost/regex/v4/regex_grep.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_REGEX_REPLACE_HPP\n#include <boost/regex/v4/regex_replace.hpp>\n#endif\n#ifndef BOOST_REGEX_V4_REGEX_MERGE_HPP\n#include <boost/regex/v4/regex_merge.hpp>\n#endif\n#ifndef BOOST_REGEX_SPLIT_HPP\n#include <boost/regex/v4/regex_split.hpp>\n#endif\n\n#endif  // __cplusplus\n\n#endif  // include\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_format.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2009 John Maddock\n * Copyright 2008 Eric Niebler. \n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_format.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides formatting output routines for search and replace\n  *                operations.  Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n#ifndef BOOST_REGEX_FORMAT_HPP\n#define BOOST_REGEX_FORMAT_HPP\n\n#include <boost/type_traits/is_pointer.hpp>\n#include <boost/type_traits/is_function.hpp>\n#include <boost/type_traits/is_class.hpp>\n#include <boost/type_traits/is_same.hpp>\n#include <boost/type_traits/is_convertible.hpp>\n#include <boost/type_traits/remove_pointer.hpp>\n#include <boost/type_traits/remove_cv.hpp>\n#include <boost/mpl/if.hpp>\n#include <boost/mpl/and.hpp>\n#include <boost/mpl/not.hpp>\n#ifndef BOOST_NO_SFINAE\n#include <boost/mpl/has_xxx.hpp>\n#endif\n#include <boost/ref.hpp>\n\nnamespace boost{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n//\n// Forward declaration:\n//\n   template <class BidiIterator, class Allocator = BOOST_DEDUCED_TYPENAME std::vector<sub_match<BidiIterator> >::allocator_type >\nclass match_results;\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\n//\n// struct trivial_format_traits:\n// defines minimum localisation support for formatting\n// in the case that the actual regex traits is unavailable.\n//\ntemplate <class charT>\nstruct trivial_format_traits\n{\n   typedef charT char_type;\n\n   static std::ptrdiff_t length(const charT* p)\n   {\n      return global_length(p);\n   }\n   static charT tolower(charT c)\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_lower(c);\n   }\n   static charT toupper(charT c)\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_upper(c);\n   }\n   static int value(const charT c, int radix)\n   {\n      int result = global_value(c);\n      return result >= radix ? -1 : result;\n   }\n   int toi(const charT*& p1, const charT* p2, int radix)const\n   {\n      return (int)global_toi(p1, p2, radix, *this);\n   }\n};\n\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#pragma warning(disable:26812)\n#endif\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nclass basic_regex_formatter\n{\npublic:\n   typedef typename traits::char_type char_type;\n   basic_regex_formatter(OutputIterator o, const Results& r, const traits& t)\n      : m_traits(t), m_results(r), m_out(o), m_position(), m_end(), m_flags(), m_state(output_copy), m_restore_state(output_copy), m_have_conditional(false) {}\n   OutputIterator format(ForwardIter p1, ForwardIter p2, match_flag_type f);\n   OutputIterator format(ForwardIter p1, match_flag_type f)\n   {\n      return format(p1, p1 + m_traits.length(p1), f);\n   }\nprivate:\n   typedef typename Results::value_type sub_match_type;\n   enum output_state\n   {\n      output_copy,\n      output_next_lower,\n      output_next_upper,\n      output_lower,\n      output_upper,\n      output_none\n   };\n\n   void put(char_type c);\n   void put(const sub_match_type& sub);\n   void format_all();\n   void format_perl();\n   void format_escape();\n   void format_conditional();\n   void format_until_scope_end();\n   bool handle_perl_verb(bool have_brace);\n\n   inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j, const mpl::false_&)\n   {\n      std::vector<char_type> v(i, j);\n      return (i != j) ? this->m_results.named_subexpression(&v[0], &v[0] + v.size())\n         : this->m_results.named_subexpression(static_cast<const char_type*>(0), static_cast<const char_type*>(0));\n   }\n   inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j, const mpl::true_&)\n   {\n      return this->m_results.named_subexpression(i, j);\n   }\n   inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j)\n   {\n      typedef typename boost::is_convertible<ForwardIter, const char_type*>::type tag_type;\n      return get_named_sub(i, j, tag_type());\n   }\n   inline int get_named_sub_index(ForwardIter i, ForwardIter j, const mpl::false_&)\n   {\n      std::vector<char_type> v(i, j);\n      return (i != j) ? this->m_results.named_subexpression_index(&v[0], &v[0] + v.size())\n         : this->m_results.named_subexpression_index(static_cast<const char_type*>(0), static_cast<const char_type*>(0));\n   }\n   inline int get_named_sub_index(ForwardIter i, ForwardIter j, const mpl::true_&)\n   {\n      return this->m_results.named_subexpression_index(i, j);\n   }\n   inline int get_named_sub_index(ForwardIter i, ForwardIter j)\n   {\n      typedef typename boost::is_convertible<ForwardIter, const char_type*>::type tag_type;\n      return get_named_sub_index(i, j, tag_type());\n   }\n#ifdef BOOST_MSVC\n   // msvc-8.0 issues a spurious warning on the call to std::advance here:\n#pragma warning(push)\n#pragma warning(disable:4244)\n#endif\n   inline int toi(ForwardIter& i, ForwardIter j, int base, const boost::mpl::false_&)\n   {\n      if(i != j)\n      {\n         std::vector<char_type> v(i, j);\n         const char_type* start = &v[0];\n         const char_type* pos = start;\n         int r = (int)m_traits.toi(pos, &v[0] + v.size(), base);\n         std::advance(i, pos - start);\n         return r;\n      }\n      return -1;\n   }\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n   inline int toi(ForwardIter& i, ForwardIter j, int base, const boost::mpl::true_&)\n   {\n      return m_traits.toi(i, j, base);\n   }\n   inline int toi(ForwardIter& i, ForwardIter j, int base)\n   {\n#if defined(_MSC_VER) && defined(__INTEL_COMPILER) && ((__INTEL_COMPILER == 9999) || (__INTEL_COMPILER == 1210))\n      // Workaround for Intel support issue #656654.\n      // See also https://svn.boost.org/trac/boost/ticket/6359\n      return toi(i, j, base, mpl::false_());\n#else\n      typedef typename boost::is_convertible<ForwardIter, const char_type*&>::type tag_type;\n      return toi(i, j, base, tag_type());\n#endif\n   }\n\n   const traits&    m_traits;       // the traits class for localised formatting operations\n   const Results&   m_results;     // the match_results being used.\n   OutputIterator   m_out;         // where to send output.\n   ForwardIter      m_position;  // format string, current position\n   ForwardIter      m_end;       // format string end\n   match_flag_type  m_flags;      // format flags to use\n   output_state     m_state;      // what to do with the next character\n   output_state     m_restore_state;  // what state to restore to.\n   bool             m_have_conditional; // we are parsing a conditional\nprivate:\n   basic_regex_formatter(const basic_regex_formatter&);\n   basic_regex_formatter& operator=(const basic_regex_formatter&);\n};\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nOutputIterator basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format(ForwardIter p1, ForwardIter p2, match_flag_type f)\n{\n   m_position = p1;\n   m_end = p2;\n   m_flags = f;\n   format_all();\n   return m_out;\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_all()\n{\n   // over and over:\n   while(m_position != m_end)\n   {\n      switch(*m_position)\n      {\n      case '&':\n         if(m_flags & ::boost::regex_constants::format_sed)\n         {\n            ++m_position;\n            put(m_results[0]);\n            break;\n         }\n         put(*m_position++);\n         break;\n      case '\\\\':\n         format_escape();\n         break;\n      case '(':\n         if(m_flags & boost::regex_constants::format_all)\n         {\n            ++m_position;\n            bool have_conditional = m_have_conditional;\n            m_have_conditional = false;\n            format_until_scope_end();\n            m_have_conditional = have_conditional;\n            if(m_position == m_end)\n               return;\n            BOOST_REGEX_ASSERT(*m_position == static_cast<char_type>(')'));\n            ++m_position;  // skip the closing ')'\n            break;\n         }\n         put(*m_position);\n         ++m_position;\n         break;\n      case ')':\n         if(m_flags & boost::regex_constants::format_all)\n         {\n            return;\n         }\n         put(*m_position);\n         ++m_position;\n         break;\n      case ':':\n         if((m_flags & boost::regex_constants::format_all) && m_have_conditional)\n         {\n            return;\n         }\n         put(*m_position);\n         ++m_position;\n         break;\n      case '?':\n         if(m_flags & boost::regex_constants::format_all)\n         {\n            ++m_position;\n            format_conditional();\n            break;\n         }\n         put(*m_position);\n         ++m_position;\n         break;\n      case '$':\n         if((m_flags & format_sed) == 0)\n         {\n            format_perl();\n            break;\n         }\n         // not a special character:\n         BOOST_FALLTHROUGH;\n      default:\n         put(*m_position);\n         ++m_position;\n         break;\n      }\n   }\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_perl()\n{\n   //\n   // On entry *m_position points to a '$' character\n   // output the information that goes with it:\n   //\n   BOOST_REGEX_ASSERT(*m_position == '$');\n   //\n   // see if this is a trailing '$':\n   //\n   if(++m_position == m_end)\n   {\n      --m_position;\n      put(*m_position);\n      ++m_position;\n      return;\n   }\n   //\n   // OK find out what kind it is:\n   //\n   bool have_brace = false;\n   ForwardIter save_position = m_position;\n   switch(*m_position)\n   {\n   case '&':\n      ++m_position;\n      put(this->m_results[0]);\n      break;\n   case '`':\n      ++m_position;\n      put(this->m_results.prefix());\n      break;\n   case '\\'':\n      ++m_position;\n      put(this->m_results.suffix());\n      break;\n   case '$':\n      put(*m_position++);\n      break;\n   case '+':\n      if((++m_position != m_end) && (*m_position == '{'))\n      {\n         ForwardIter base = ++m_position;\n         while((m_position != m_end) && (*m_position != '}')) ++m_position;\n         if(m_position != m_end)\n         {\n            // Named sub-expression:\n            put(get_named_sub(base, m_position));\n            ++m_position;\n            break;\n         }\n         else\n         {\n            m_position = --base;\n         }\n      }\n      put((this->m_results)[this->m_results.size() > 1 ? static_cast<int>(this->m_results.size() - 1) : 1]);\n      break;\n   case '{':\n      have_brace = true;\n      ++m_position;\n      BOOST_FALLTHROUGH;\n   default:\n      // see if we have a number:\n      {\n         std::ptrdiff_t len = ::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end);\n         //len = (std::min)(static_cast<std::ptrdiff_t>(2), len);\n         int v = this->toi(m_position, m_position + len, 10);\n         if((v < 0) || (have_brace && ((m_position == m_end) || (*m_position != '}'))))\n         {\n            // Look for a Perl-5.10 verb:\n            if(!handle_perl_verb(have_brace))\n            {\n               // leave the $ as is, and carry on:\n               m_position = --save_position;\n               put(*m_position);\n               ++m_position;\n            }\n            break;\n         }\n         // otherwise output sub v:\n         put(this->m_results[v]);\n         if(have_brace)\n            ++m_position;\n      }\n   }\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nbool basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::handle_perl_verb(bool have_brace)\n{\n   // \n   // We may have a capitalised string containing a Perl action:\n   //\n   static const char_type MATCH[] = { 'M', 'A', 'T', 'C', 'H' };\n   static const char_type PREMATCH[] = { 'P', 'R', 'E', 'M', 'A', 'T', 'C', 'H' };\n   static const char_type POSTMATCH[] = { 'P', 'O', 'S', 'T', 'M', 'A', 'T', 'C', 'H' };\n   static const char_type LAST_PAREN_MATCH[] = { 'L', 'A', 'S', 'T', '_', 'P', 'A', 'R', 'E', 'N', '_', 'M', 'A', 'T', 'C', 'H' };\n   static const char_type LAST_SUBMATCH_RESULT[] = { 'L', 'A', 'S', 'T', '_', 'S', 'U', 'B', 'M', 'A', 'T', 'C', 'H', '_', 'R', 'E', 'S', 'U', 'L', 'T' };\n   static const char_type LAST_SUBMATCH_RESULT_ALT[] = { '^', 'N' };\n\n   if(m_position == m_end)\n      return false;\n   if(have_brace && (*m_position == '^'))\n      ++m_position;\n\n   std::ptrdiff_t max_len = m_end - m_position;\n\n   if((max_len >= 5) && std::equal(m_position, m_position + 5, MATCH))\n   {\n      m_position += 5;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 5;\n            return false;\n         }\n      }\n      put(this->m_results[0]);\n      return true;\n   }\n   if((max_len >= 8) && std::equal(m_position, m_position + 8, PREMATCH))\n   {\n      m_position += 8;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 8;\n            return false;\n         }\n      }\n      put(this->m_results.prefix());\n      return true;\n   }\n   if((max_len >= 9) && std::equal(m_position, m_position + 9, POSTMATCH))\n   {\n      m_position += 9;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 9;\n            return false;\n         }\n      }\n      put(this->m_results.suffix());\n      return true;\n   }\n   if((max_len >= 16) && std::equal(m_position, m_position + 16, LAST_PAREN_MATCH))\n   {\n      m_position += 16;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 16;\n            return false;\n         }\n      }\n      put((this->m_results)[this->m_results.size() > 1 ? static_cast<int>(this->m_results.size() - 1) : 1]);\n      return true;\n   }\n   if((max_len >= 20) && std::equal(m_position, m_position + 20, LAST_SUBMATCH_RESULT))\n   {\n      m_position += 20;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 20;\n            return false;\n         }\n      }\n      put(this->m_results.get_last_closed_paren());\n      return true;\n   }\n   if((max_len >= 2) && std::equal(m_position, m_position + 2, LAST_SUBMATCH_RESULT_ALT))\n   {\n      m_position += 2;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 2;\n            return false;\n         }\n      }\n      put(this->m_results.get_last_closed_paren());\n      return true;\n   }\n   return false;\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_escape()\n{\n   // skip the escape and check for trailing escape:\n   if(++m_position == m_end)\n   {\n      put(static_cast<char_type>('\\\\'));\n      return;\n   }\n   // now switch on the escape type:\n   switch(*m_position)\n   {\n   case 'a':\n      put(static_cast<char_type>('\\a'));\n      ++m_position;\n      break;\n   case 'f':\n      put(static_cast<char_type>('\\f'));\n      ++m_position;\n      break;\n   case 'n':\n      put(static_cast<char_type>('\\n'));\n      ++m_position;\n      break;\n   case 'r':\n      put(static_cast<char_type>('\\r'));\n      ++m_position;\n      break;\n   case 't':\n      put(static_cast<char_type>('\\t'));\n      ++m_position;\n      break;\n   case 'v':\n      put(static_cast<char_type>('\\v'));\n      ++m_position;\n      break;\n   case 'x':\n      if(++m_position == m_end)\n      {\n         put(static_cast<char_type>('x'));\n         return;\n      }\n      // maybe have \\x{ddd}\n      if(*m_position == static_cast<char_type>('{'))\n      {\n         ++m_position;\n         int val = this->toi(m_position, m_end, 16);\n         if(val < 0)\n         {\n            // invalid value treat everything as literals:\n            put(static_cast<char_type>('x'));\n            put(static_cast<char_type>('{'));\n            return;\n         }\n         if((m_position == m_end) || (*m_position != static_cast<char_type>('}')))\n         {\n            --m_position;\n            while(*m_position != static_cast<char_type>('\\\\'))\n               --m_position;\n            ++m_position;\n            put(*m_position++);\n            return;\n         }\n         ++m_position;\n         put(static_cast<char_type>(val));\n         return;\n      }\n      else\n      {\n         std::ptrdiff_t len = ::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end);\n         len = (std::min)(static_cast<std::ptrdiff_t>(2), len);\n         int val = this->toi(m_position, m_position + len, 16);\n         if(val < 0)\n         {\n            --m_position;\n            put(*m_position++);\n            return;\n         }\n         put(static_cast<char_type>(val));\n      }\n      break;\n   case 'c':\n      if(++m_position == m_end)\n      {\n         --m_position;\n         put(*m_position++);\n         return;\n      }\n      put(static_cast<char_type>(*m_position++ % 32));\n      break;\n   case 'e':\n      put(static_cast<char_type>(27));\n      ++m_position;\n      break;\n   default:\n      // see if we have a perl specific escape:\n      if((m_flags & boost::regex_constants::format_sed) == 0)\n      {\n         bool breakout = false;\n         switch(*m_position)\n         {\n         case 'l':\n            ++m_position;\n            m_restore_state = m_state;\n            m_state = output_next_lower;\n            breakout = true;\n            break;\n         case 'L':\n            ++m_position;\n            m_state = output_lower;\n            breakout = true;\n            break;\n         case 'u':\n            ++m_position;\n            m_restore_state = m_state;\n            m_state = output_next_upper;\n            breakout = true;\n            break;\n         case 'U':\n            ++m_position;\n            m_state = output_upper;\n            breakout = true;\n            break;\n         case 'E':\n            ++m_position;\n            m_state = output_copy;\n            breakout = true;\n            break;\n         }\n         if(breakout)\n            break;\n      }\n      // see if we have a \\n sed style backreference:\n      std::ptrdiff_t len = ::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end);\n      len = (std::min)(static_cast<std::ptrdiff_t>(1), len);\n      int v = this->toi(m_position, m_position+len, 10);\n      if((v > 0) || ((v == 0) && (m_flags & ::boost::regex_constants::format_sed)))\n      {\n         put(m_results[v]);\n         break;\n      }\n      else if(v == 0)\n      {\n         // octal ecape sequence:\n         --m_position;\n         len = ::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end);\n         len = (std::min)(static_cast<std::ptrdiff_t>(4), len);\n         v = this->toi(m_position, m_position + len, 8);\n         BOOST_REGEX_ASSERT(v >= 0);\n         put(static_cast<char_type>(v));\n         break;\n      }\n      // Otherwise output the character \"as is\":\n      put(*m_position++);\n      break;\n   }\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_conditional()\n{\n   if(m_position == m_end)\n   {\n      // oops trailing '?':\n      put(static_cast<char_type>('?'));\n      return;\n   }\n   int v;\n   if(*m_position == '{')\n   {\n      ForwardIter base = m_position;\n      ++m_position;\n      v = this->toi(m_position, m_end, 10);\n      if(v < 0)\n      {\n         // Try a named subexpression:\n         while((m_position != m_end) && (*m_position != '}'))\n            ++m_position;\n         v = this->get_named_sub_index(base + 1, m_position);\n      }\n      if((v < 0) || (*m_position != '}'))\n      {\n         m_position = base;\n         // oops trailing '?':\n         put(static_cast<char_type>('?'));\n         return;\n      }\n      // Skip trailing '}':\n      ++m_position;\n   }\n   else\n   {\n      std::ptrdiff_t len = ::boost::BOOST_REGEX_DETAIL_NS::distance(m_position, m_end);\n      len = (std::min)(static_cast<std::ptrdiff_t>(2), len);\n      v = this->toi(m_position, m_position + len, 10);\n   }\n   if(v < 0)\n   {\n      // oops not a number:\n      put(static_cast<char_type>('?'));\n      return;\n   }\n\n   // output varies depending upon whether sub-expression v matched or not:\n   if(m_results[v].matched)\n   {\n      m_have_conditional = true;\n      format_all();\n      m_have_conditional = false;\n      if((m_position != m_end) && (*m_position == static_cast<char_type>(':')))\n      {\n         // skip the ':':\n         ++m_position;\n         // save output state, then turn it off:\n         output_state saved_state = m_state;\n         m_state = output_none;\n         // format the rest of this scope:\n         format_until_scope_end();\n         // restore output state:\n         m_state = saved_state;\n      }\n   }\n   else\n   {\n      // save output state, then turn it off:\n      output_state saved_state = m_state;\n      m_state = output_none;\n      // format until ':' or ')':\n      m_have_conditional = true;\n      format_all();\n      m_have_conditional = false;\n      // restore state:\n      m_state = saved_state;\n      if((m_position != m_end) && (*m_position == static_cast<char_type>(':')))\n      {\n         // skip the ':':\n         ++m_position;\n         // format the rest of this scope:\n         format_until_scope_end();\n      }\n   }\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_until_scope_end()\n{\n   do\n   {\n      format_all();\n      if((m_position == m_end) || (*m_position == static_cast<char_type>(')')))\n         return;\n      put(*m_position++);\n   }while(m_position != m_end);\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::put(char_type c)\n{\n   // write a single character to output\n   // according to which case translation mode we are in:\n   switch(this->m_state)\n   {\n   case output_none:\n      return;\n   case output_next_lower:\n      c = m_traits.tolower(c);\n      this->m_state = m_restore_state;\n      break;\n   case output_next_upper:\n      c = m_traits.toupper(c);\n      this->m_state = m_restore_state;\n      break;\n   case output_lower:\n      c = m_traits.tolower(c);\n      break;\n   case output_upper:\n      c = m_traits.toupper(c);\n      break;\n   default:\n      break;\n   }\n   *m_out = c;\n   ++m_out;\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::put(const sub_match_type& sub)\n{\n   typedef typename sub_match_type::iterator iterator_type;\n   iterator_type i = sub.first;\n   while(i != sub.second)\n   {\n      put(*i);\n      ++i;\n   }\n}\n\ntemplate <class S>\nclass string_out_iterator\n{\n   S* out;\npublic:\n   string_out_iterator(S& s) : out(&s) {}\n   string_out_iterator& operator++() { return *this; }\n   string_out_iterator& operator++(int) { return *this; }\n   string_out_iterator& operator*() { return *this; }\n   string_out_iterator& operator=(typename S::value_type v) \n   { \n      out->append(1, v); \n      return *this; \n   }\n\n   typedef std::ptrdiff_t difference_type;\n   typedef typename S::value_type value_type;\n   typedef value_type* pointer;\n   typedef value_type& reference;\n   typedef std::output_iterator_tag iterator_category;\n};\n\ntemplate <class OutputIterator, class Iterator, class Alloc, class ForwardIter, class traits>\nOutputIterator regex_format_imp(OutputIterator out,\n                          const match_results<Iterator, Alloc>& m,\n                          ForwardIter p1, ForwardIter p2,\n                          match_flag_type flags,\n                          const traits& t\n                         )\n{\n   if(flags & regex_constants::format_literal)\n   {\n      return BOOST_REGEX_DETAIL_NS::copy(p1, p2, out);\n   }\n\n   BOOST_REGEX_DETAIL_NS::basic_regex_formatter<\n      OutputIterator, \n      match_results<Iterator, Alloc>, \n      traits, ForwardIter> f(out, m, t);\n   return f.format(p1, p2, flags);\n}\n\n#ifndef BOOST_NO_SFINAE\n\nBOOST_MPL_HAS_XXX_TRAIT_DEF(const_iterator)\n\nstruct any_type \n{\n   template <class T>\n   any_type(const T&); \n   template <class T, class U>\n   any_type(const T&, const U&); \n   template <class T, class U, class V>\n   any_type(const T&, const U&, const V&); \n};\ntypedef char no_type;\ntypedef char (&unary_type)[2];\ntypedef char (&binary_type)[3];\ntypedef char (&ternary_type)[4];\n\nno_type check_is_formatter(unary_type, binary_type, ternary_type);\ntemplate<typename T>\nunary_type check_is_formatter(T const &, binary_type, ternary_type);\ntemplate<typename T>\nbinary_type check_is_formatter(unary_type, T const &, ternary_type);\ntemplate<typename T, typename U>\nbinary_type check_is_formatter(T const &, U const &, ternary_type);\ntemplate<typename T>\nternary_type check_is_formatter(unary_type, binary_type, T const &);\ntemplate<typename T, typename U>\nternary_type check_is_formatter(T const &, binary_type, U const &);\ntemplate<typename T, typename U>\nternary_type check_is_formatter(unary_type, T const &, U const &);\ntemplate<typename T, typename U, typename V>\nternary_type check_is_formatter(T const &, U const &, V const &);\n\nstruct unary_binary_ternary\n{\n    typedef unary_type (*unary_fun)(any_type);\n    typedef binary_type (*binary_fun)(any_type, any_type);\n    typedef ternary_type (*ternary_fun)(any_type, any_type, any_type);\n    operator unary_fun();\n    operator binary_fun();\n    operator ternary_fun();\n};\n\ntemplate<typename Formatter, bool IsFunction = boost::is_function<Formatter>::value>\nstruct formatter_wrapper\n  : Formatter\n  , unary_binary_ternary\n{\n   formatter_wrapper(){}\n};\n\ntemplate<typename Formatter>\nstruct formatter_wrapper<Formatter, true>\n  : unary_binary_ternary\n{\n    operator Formatter *();\n};\n\ntemplate<typename Formatter>\nstruct formatter_wrapper<Formatter *, false>\n  : unary_binary_ternary\n{\n    operator Formatter *();\n};\n\ntemplate <class F, class M, class O>\nstruct format_traits_imp\n{\nprivate:\n   //\n   // F must be a pointer, a function, or a class with a function call operator:\n   //\n   BOOST_STATIC_ASSERT((::boost::is_pointer<F>::value || ::boost::is_function<F>::value || ::boost::is_class<F>::value));\n   static formatter_wrapper<typename unwrap_reference<F>::type> f;\n   static M m;\n   static O out;\n   static boost::regex_constants::match_flag_type flags;\npublic:\n   BOOST_STATIC_CONSTANT(int, value = sizeof(check_is_formatter(f(m), f(m, out), f(m, out, flags))));\n};\n\ntemplate <class F, class M, class O>\nstruct format_traits\n{\npublic:\n   // \n   // Type is mpl::int_<N> where N is one of:\n   //\n   // 0 : F is a pointer to a presumably null-terminated string.\n   // 1 : F is a character-container such as a std::string.\n   // 2 : F is a Unary Functor.\n   // 3 : F is a Binary Functor.\n   // 4 : F is a Ternary Functor.\n   //\n   typedef typename boost::mpl::if_<\n      boost::mpl::and_<boost::is_pointer<F>, boost::mpl::not_<boost::is_function<typename boost::remove_pointer<F>::type> > >,\n      boost::mpl::int_<0>,\n      typename boost::mpl::if_<\n         has_const_iterator<F>,\n         boost::mpl::int_<1>,\n         boost::mpl::int_<format_traits_imp<F, M, O>::value>\n      >::type\n   >::type type;\n   //\n   // This static assertion will fail if the functor passed does not accept\n   // the same type of arguments passed.\n   //\n   BOOST_STATIC_ASSERT( boost::is_class<F>::value && !has_const_iterator<F>::value ? (type::value > 1) : true);\n};\n\n#else // BOOST_NO_SFINAE\n\ntemplate <class F, class M, class O>\nstruct format_traits\n{\npublic:\n   // \n   // Type is mpl::int_<N> where N is one of:\n   //\n   // 0 : F is a pointer to a presumably null-terminated string.\n   // 1 : F is a character-container such as a std::string.\n   //\n   // Other options such as F being a Functor are not supported without\n   // SFINAE support.\n   //\n   typedef typename boost::mpl::if_<\n      boost::is_pointer<F>,\n      boost::mpl::int_<0>,\n      boost::mpl::int_<1>\n   >::type type;\n};\n\n#endif // BOOST_NO_SFINAE\n\ntemplate <class Base, class Match>\nstruct format_functor3\n{\n   format_functor3(Base b) : func(b) {}\n   template <class OutputIter>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f)\n   {\n      return boost::unwrap_ref(func)(m, i, f);\n   }\n   template <class OutputIter, class Traits>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&)\n   {\n      return (*this)(m, i, f);\n   }\nprivate:\n   Base func;\n   format_functor3(const format_functor3&);\n   format_functor3& operator=(const format_functor3&);\n};\n\ntemplate <class Base, class Match>\nstruct format_functor2\n{\n   format_functor2(Base b) : func(b) {}\n   template <class OutputIter>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type /*f*/)\n   {\n      return boost::unwrap_ref(func)(m, i);\n   }\n   template <class OutputIter, class Traits>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&)\n   {\n      return (*this)(m, i, f);\n   }\nprivate:\n   Base func;\n   format_functor2(const format_functor2&);\n   format_functor2& operator=(const format_functor2&);\n};\n\ntemplate <class Base, class Match>\nstruct format_functor1\n{\n   format_functor1(Base b) : func(b) {}\n\n   template <class S, class OutputIter>\n   OutputIter do_format_string(const S& s, OutputIter i)\n   {\n      return BOOST_REGEX_DETAIL_NS::copy(s.begin(), s.end(), i);\n   }\n   template <class S, class OutputIter>\n   inline OutputIter do_format_string(const S* s, OutputIter i)\n   {\n      while(s && *s)\n      {\n         *i = *s;\n         ++i;\n         ++s;\n      }\n      return i;\n   }\n   template <class OutputIter>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type /*f*/)\n   {\n      return do_format_string(boost::unwrap_ref(func)(m), i);\n   }\n   template <class OutputIter, class Traits>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&)\n   {\n      return (*this)(m, i, f);\n   }\nprivate:\n   Base func;\n   format_functor1(const format_functor1&);\n   format_functor1& operator=(const format_functor1&);\n};\n\ntemplate <class charT, class Match, class Traits>\nstruct format_functor_c_string\n{\n   format_functor_c_string(const charT* ps) : func(ps) {}\n\n   template <class OutputIter>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits& t = Traits())\n   {\n      //typedef typename Match::char_type char_type;\n      const charT* end = func;\n      while(*end) ++end;\n      return regex_format_imp(i, m, func, end, f, t);\n   }\nprivate:\n   const charT* func;\n   format_functor_c_string(const format_functor_c_string&);\n   format_functor_c_string& operator=(const format_functor_c_string&);\n};\n\ntemplate <class Container, class Match, class Traits>\nstruct format_functor_container\n{\n   format_functor_container(const Container& c) : func(c) {}\n\n   template <class OutputIter>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits& t = Traits())\n   {\n      //typedef typename Match::char_type char_type;\n      return BOOST_REGEX_DETAIL_NS::regex_format_imp(i, m, func.begin(), func.end(), f, t);\n   }\nprivate:\n   const Container& func;\n   format_functor_container(const format_functor_container&);\n   format_functor_container& operator=(const format_functor_container&);\n};\n\ntemplate <class Func, class Match, class OutputIterator, class Traits = BOOST_REGEX_DETAIL_NS::trivial_format_traits<typename Match::char_type> >\nstruct compute_functor_type\n{\n   typedef typename format_traits<Func, Match, OutputIterator>::type tag;\n   typedef typename boost::remove_cv< typename boost::remove_pointer<Func>::type>::type maybe_char_type;\n\n   typedef typename mpl::if_<\n      ::boost::is_same<tag, mpl::int_<0> >, format_functor_c_string<maybe_char_type, Match, Traits>,\n      typename mpl::if_<\n         ::boost::is_same<tag, mpl::int_<1> >, format_functor_container<Func, Match, Traits>,\n         typename mpl::if_<\n            ::boost::is_same<tag, mpl::int_<2> >, format_functor1<Func, Match>,\n            typename mpl::if_<\n               ::boost::is_same<tag, mpl::int_<3> >, format_functor2<Func, Match>, \n               format_functor3<Func, Match>\n            >::type\n         >::type\n      >::type\n   >::type type;\n};\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\ntemplate <class OutputIterator, class Iterator, class Allocator, class Functor>\ninline OutputIterator regex_format(OutputIterator out,\n                          const match_results<Iterator, Allocator>& m,\n                          Functor fmt,\n                          match_flag_type flags = format_all\n                         )\n{\n   return m.format(out, fmt, flags);\n}\n\ntemplate <class Iterator, class Allocator, class Functor>\ninline std::basic_string<typename match_results<Iterator, Allocator>::char_type> regex_format(const match_results<Iterator, Allocator>& m, \n                                      Functor fmt, \n                                      match_flag_type flags = format_all)\n{\n   return m.format(fmt, flags);\n}\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_FORMAT_HPP\n\n\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_fwd.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_fwd.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Forward declares boost::basic_regex<> and\n  *                associated typedefs.\n  */\n\n#ifndef BOOST_REGEX_FWD_HPP_INCLUDED\n#define BOOST_REGEX_FWD_HPP_INCLUDED\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n\n//\n// define BOOST_REGEX_NO_FWD if this\n// header doesn't work!\n//\n#ifdef BOOST_REGEX_NO_FWD\n#  ifndef BOOST_RE_REGEX_HPP\n#     include <boost/regex.hpp>\n#  endif\n#else\n\nnamespace boost{\n\ntemplate <class charT>\nclass cpp_regex_traits;\ntemplate <class charT>\nstruct c_regex_traits;\ntemplate <class charT>\nclass w32_regex_traits;\n\n#ifdef BOOST_REGEX_USE_WIN32_LOCALE\ntemplate <class charT, class implementationT = w32_regex_traits<charT> >\nstruct regex_traits;\n#elif defined(BOOST_REGEX_USE_CPP_LOCALE)\ntemplate <class charT, class implementationT = cpp_regex_traits<charT> >\nstruct regex_traits;\n#else\ntemplate <class charT, class implementationT = c_regex_traits<charT> >\nstruct regex_traits;\n#endif\n\ntemplate <class charT, class traits = regex_traits<charT> >\nclass basic_regex;\n\ntypedef basic_regex<char, regex_traits<char> > regex;\n#ifndef BOOST_NO_WREGEX\ntypedef basic_regex<wchar_t, regex_traits<wchar_t> > wregex;\n#endif\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_NO_FWD\n\n#endif\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_grep.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_grep.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides regex_grep implementation.\n  */\n\n#ifndef BOOST_REGEX_V4_REGEX_GREP_HPP\n#define BOOST_REGEX_V4_REGEX_GREP_HPP\n\n\nnamespace boost{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n//\n// regex_grep:\n// find all non-overlapping matches within the sequence first last:\n//\ntemplate <class Predicate, class BidiIterator, class charT, class traits>\ninline unsigned int regex_grep(Predicate foo, \n                               BidiIterator first, \n                               BidiIterator last, \n                               const basic_regex<charT, traits>& e, \n                               match_flag_type flags = match_default)\n{\n   if(e.flags() & regex_constants::failbit)\n      return false;\n\n   typedef typename match_results<BidiIterator>::allocator_type match_allocator_type;\n\n   match_results<BidiIterator> m;\n   BOOST_REGEX_DETAIL_NS::perl_matcher<BidiIterator, match_allocator_type, traits> matcher(first, last, m, e, flags, first);\n   unsigned int count = 0;\n   while(matcher.find())\n   {\n      ++count;\n      if(0 == foo(m))\n         return count; // caller doesn't want to go on\n      if(m[0].second == last)\n         return count; // we've reached the end, don't try and find an extra null match.\n      if(m.length() == 0)\n      {\n         if(m[0].second == last)\n            return count;\n         // we found a NULL-match, now try to find\n         // a non-NULL one at the same position:\n         match_results<BidiIterator, match_allocator_type> m2(m);\n         matcher.setf(match_not_null | match_continuous);\n         if(matcher.find())\n         {\n            ++count;\n            if(0 == foo(m))\n               return count;\n         }\n         else\n         {\n            // reset match back to where it was:\n            m = m2;\n         }\n         matcher.unsetf((match_not_null | match_continuous) & ~flags);\n      }\n   }\n   return count;\n}\n\n//\n// regex_grep convenience interfaces:\n#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING\n//\n// this isn't really a partial specialisation, but template function\n// overloading - if the compiler doesn't support partial specialisation\n// then it really won't support this either:\ntemplate <class Predicate, class charT, class traits>\ninline unsigned int regex_grep(Predicate foo, const charT* str, \n                        const basic_regex<charT, traits>& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_grep(foo, str, str + traits::length(str), e, flags);\n}\n\ntemplate <class Predicate, class ST, class SA, class charT, class traits>\ninline unsigned int regex_grep(Predicate foo, const std::basic_string<charT, ST, SA>& s, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   return regex_grep(foo, s.begin(), s.end(), e, flags);\n}\n#else  // partial specialisation\ninline unsigned int regex_grep(bool (*foo)(const cmatch&), const char* str, \n                        const regex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_grep(foo, str, str + regex::traits_type::length(str), e, flags);\n}\n#ifndef BOOST_NO_WREGEX\ninline unsigned int regex_grep(bool (*foo)(const wcmatch&), const wchar_t* str, \n                        const wregex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_grep(foo, str, str + wregex::traits_type::length(str), e, flags);\n}\n#endif\ninline unsigned int regex_grep(bool (*foo)(const match_results<std::string::const_iterator>&), const std::string& s,\n                        const regex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_grep(foo, s.begin(), s.end(), e, flags);\n}\n#if !defined(BOOST_NO_WREGEX)\ninline unsigned int regex_grep(bool (*foo)(const match_results<std::basic_string<wchar_t>::const_iterator>&), \n                     const std::basic_string<wchar_t>& s, \n                        const wregex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_grep(foo, s.begin(), s.end(), e, flags);\n}\n#endif\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_V4_REGEX_GREP_HPP\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_iterator.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_iterator.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides regex_iterator implementation.\n  */\n\n#ifndef BOOST_REGEX_V4_REGEX_ITERATOR_HPP\n#define BOOST_REGEX_V4_REGEX_ITERATOR_HPP\n\n#include <boost/shared_ptr.hpp>\n\nnamespace boost{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\ntemplate <class BidirectionalIterator, \n          class charT,\n          class traits>\nclass regex_iterator_implementation \n{\n   typedef basic_regex<charT, traits> regex_type;\n\n   match_results<BidirectionalIterator> what;  // current match\n   BidirectionalIterator                base;  // start of sequence\n   BidirectionalIterator                end;   // end of sequence\n   const regex_type                     re;   // the expression\n   match_flag_type                      flags; // flags for matching\n\npublic:\n   regex_iterator_implementation(const regex_type* p, BidirectionalIterator last, match_flag_type f)\n      : base(), end(last), re(*p), flags(f){}\n   regex_iterator_implementation(const regex_iterator_implementation& other)\n      :what(other.what), base(other.base), end(other.end), re(other.re), flags(other.flags){}\n   bool init(BidirectionalIterator first)\n   {\n      base = first;\n      return regex_search(first, end, what, re, flags);\n   }\n   bool compare(const regex_iterator_implementation& that)\n   {\n      if(this == &that) return true;\n      return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second);\n   }\n   const match_results<BidirectionalIterator>& get()\n   { return what; }\n   bool next()\n   {\n      //if(what.prefix().first != what[0].second)\n      //   flags |= match_prev_avail;\n      BidirectionalIterator next_start = what[0].second;\n      match_flag_type f(flags);\n      if(!what.length() || (f & regex_constants::match_posix))\n         f |= regex_constants::match_not_initial_null;\n      //if(base != next_start)\n      //   f |= regex_constants::match_not_bob;\n      bool result = regex_search(next_start, end, what, re, f, base);\n      if(result)\n         what.set_base(base);\n      return result;\n   }\nprivate:\n   regex_iterator_implementation& operator=(const regex_iterator_implementation&);\n};\n\ntemplate <class BidirectionalIterator, \n          class charT = BOOST_DEDUCED_TYPENAME BOOST_REGEX_DETAIL_NS::regex_iterator_traits<BidirectionalIterator>::value_type,\n          class traits = regex_traits<charT> >\nclass regex_iterator \n{\nprivate:\n   typedef regex_iterator_implementation<BidirectionalIterator, charT, traits> impl;\n   typedef shared_ptr<impl> pimpl;\npublic:\n   typedef          basic_regex<charT, traits>                   regex_type;\n   typedef          match_results<BidirectionalIterator>                    value_type;\n   typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<BidirectionalIterator>::difference_type \n                                                                            difference_type;\n   typedef          const value_type*                                       pointer;\n   typedef          const value_type&                                       reference; \n   typedef          std::forward_iterator_tag                               iterator_category;\n   \n   regex_iterator(){}\n   regex_iterator(BidirectionalIterator a, BidirectionalIterator b, \n                  const regex_type& re, \n                  match_flag_type m = match_default)\n                  : pdata(new impl(&re, b, m))\n   {\n      if(!pdata->init(a))\n      {\n         pdata.reset();\n      }\n   }\n   regex_iterator(const regex_iterator& that)\n      : pdata(that.pdata) {}\n   regex_iterator& operator=(const regex_iterator& that)\n   {\n      pdata = that.pdata;\n      return *this;\n   }\n   bool operator==(const regex_iterator& that)const\n   { \n      if((pdata.get() == 0) || (that.pdata.get() == 0))\n         return pdata.get() == that.pdata.get();\n      return pdata->compare(*(that.pdata.get())); \n   }\n   bool operator!=(const regex_iterator& that)const\n   { return !(*this == that); }\n   const value_type& operator*()const\n   { return pdata->get(); }\n   const value_type* operator->()const\n   { return &(pdata->get()); }\n   regex_iterator& operator++()\n   {\n      cow();\n      if(0 == pdata->next())\n      {\n         pdata.reset();\n      }\n      return *this;\n   }\n   regex_iterator operator++(int)\n   {\n      regex_iterator result(*this);\n      ++(*this);\n      return result;\n   }\nprivate:\n\n   pimpl pdata;\n\n   void cow()\n   {\n      // copy-on-write\n      if(pdata.get() && !pdata.unique())\n      {\n         pdata.reset(new impl(*(pdata.get())));\n      }\n   }\n};\n\ntypedef regex_iterator<const char*> cregex_iterator;\ntypedef regex_iterator<std::string::const_iterator> sregex_iterator;\n#ifndef BOOST_NO_WREGEX\ntypedef regex_iterator<const wchar_t*> wcregex_iterator;\ntypedef regex_iterator<std::wstring::const_iterator> wsregex_iterator;\n#endif\n\n// make_regex_iterator:\ntemplate <class charT, class traits>\ninline regex_iterator<const charT*, charT, traits> make_regex_iterator(const charT* p, const basic_regex<charT, traits>& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_iterator<const charT*, charT, traits>(p, p+traits::length(p), e, m);\n}\ntemplate <class charT, class traits, class ST, class SA>\ninline regex_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits> make_regex_iterator(const std::basic_string<charT, ST, SA>& p, const basic_regex<charT, traits>& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits>(p.begin(), p.end(), e, m);\n}\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif // BOOST_REGEX_V4_REGEX_ITERATOR_HPP\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_match.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_match.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Regular expression matching algorithms.\n  *                Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n\n#ifndef BOOST_REGEX_MATCH_HPP\n#define BOOST_REGEX_MATCH_HPP\n\nnamespace boost{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n//\n// proc regex_match\n// returns true if the specified regular expression matches\n// the whole of the input.  Fills in what matched in m.\n//\ntemplate <class BidiIterator, class Allocator, class charT, class traits>\nbool regex_match(BidiIterator first, BidiIterator last, \n                 match_results<BidiIterator, Allocator>& m, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   BOOST_REGEX_DETAIL_NS::perl_matcher<BidiIterator, Allocator, traits> matcher(first, last, m, e, flags, first);\n   return matcher.match();\n}\ntemplate <class iterator, class charT, class traits>\nbool regex_match(iterator first, iterator last, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<iterator> m;\n   return regex_match(first, last, m, e, flags | regex_constants::match_any);\n}\n//\n// query_match convenience interfaces:\n#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING\n//\n// this isn't really a partial specialisation, but template function\n// overloading - if the compiler doesn't support partial specialisation\n// then it really won't support this either:\ntemplate <class charT, class Allocator, class traits>\ninline bool regex_match(const charT* str, \n                        match_results<const charT*, Allocator>& m, \n                        const basic_regex<charT, traits>& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(str, str + traits::length(str), m, e, flags);\n}\n\ntemplate <class ST, class SA, class Allocator, class charT, class traits>\ninline bool regex_match(const std::basic_string<charT, ST, SA>& s, \n                 match_results<typename std::basic_string<charT, ST, SA>::const_iterator, Allocator>& m, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   return regex_match(s.begin(), s.end(), m, e, flags);\n}\ntemplate <class charT, class traits>\ninline bool regex_match(const charT* str, \n                        const basic_regex<charT, traits>& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const charT*> m;\n   return regex_match(str, str + traits::length(str), m, e, flags | regex_constants::match_any);\n}\n\ntemplate <class ST, class SA, class charT, class traits>\ninline bool regex_match(const std::basic_string<charT, ST, SA>& s, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   typedef typename std::basic_string<charT, ST, SA>::const_iterator iterator;\n   match_results<iterator> m;\n   return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n#else  // partial ordering\ninline bool regex_match(const char* str, \n                        cmatch& m, \n                        const regex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(str, str + regex::traits_type::length(str), m, e, flags);\n}\ninline bool regex_match(const char* str, \n                        const regex& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const char*> m;\n   return regex_match(str, str + regex::traits_type::length(str), m, e, flags | regex_constants::match_any);\n}\n#ifndef BOOST_NO_STD_LOCALE\ninline bool regex_match(const char* str, \n                        cmatch& m, \n                        const basic_regex<char, cpp_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(str, str + regex::traits_type::length(str), m, e, flags);\n}\ninline bool regex_match(const char* str, \n                        const basic_regex<char, cpp_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const char*> m;\n   return regex_match(str, str + regex::traits_type::length(str), m, e, flags | regex_constants::match_any);\n}\n#endif\ninline bool regex_match(const char* str, \n                        cmatch& m, \n                        const basic_regex<char, c_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(str, str + regex::traits_type::length(str), m, e, flags);\n}\ninline bool regex_match(const char* str, \n                        const basic_regex<char, c_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const char*> m;\n   return regex_match(str, str + regex::traits_type::length(str), m, e, flags | regex_constants::match_any);\n}\n#if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32)\ninline bool regex_match(const char* str, \n                        cmatch& m, \n                        const basic_regex<char, w32_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(str, str + regex::traits_type::length(str), m, e, flags);\n}\ninline bool regex_match(const char* str, \n                        const basic_regex<char, w32_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const char*> m;\n   return regex_match(str, str + regex::traits_type::length(str), m, e, flags | regex_constants::match_any);\n}\n#endif\n#ifndef BOOST_NO_WREGEX\ninline bool regex_match(const wchar_t* str, \n                        wcmatch& m, \n                        const wregex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(str, str + wregex::traits_type::length(str), m, e, flags);\n}\ninline bool regex_match(const wchar_t* str, \n                        const wregex& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const wchar_t*> m;\n   return regex_match(str, str + wregex::traits_type::length(str), m, e, flags | regex_constants::match_any);\n}\n#ifndef BOOST_NO_STD_LOCALE\ninline bool regex_match(const wchar_t* str, \n                        wcmatch& m, \n                        const basic_regex<wchar_t, cpp_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(str, str + wregex::traits_type::length(str), m, e, flags);\n}\ninline bool regex_match(const wchar_t* str, \n                        const basic_regex<wchar_t, cpp_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const wchar_t*> m;\n   return regex_match(str, str + wregex::traits_type::length(str), m, e, flags | regex_constants::match_any);\n}\n#endif\ninline bool regex_match(const wchar_t* str, \n                        wcmatch& m, \n                        const basic_regex<wchar_t, c_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(str, str + wregex::traits_type::length(str), m, e, flags);\n}\ninline bool regex_match(const wchar_t* str, \n                        const basic_regex<wchar_t, c_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const wchar_t*> m;\n   return regex_match(str, str + wregex::traits_type::length(str), m, e, flags | regex_constants::match_any);\n}\n#if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32)\ninline bool regex_match(const wchar_t* str, \n                        wcmatch& m, \n                        const basic_regex<wchar_t, w32_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(str, str + wregex::traits_type::length(str), m, e, flags);\n}\ninline bool regex_match(const wchar_t* str, \n                        const basic_regex<wchar_t, w32_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const wchar_t*> m;\n   return regex_match(str, str + wregex::traits_type::length(str), m, e, flags | regex_constants::match_any);\n}\n#endif\n#endif\ninline bool regex_match(const std::string& s, \n                        smatch& m,\n                        const regex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(s.begin(), s.end(), m, e, flags);\n}\ninline bool regex_match(const std::string& s, \n                        const regex& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::string::const_iterator> m;\n   return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n#ifndef BOOST_NO_STD_LOCALE\ninline bool regex_match(const std::string& s, \n                        smatch& m,\n                        const basic_regex<char, cpp_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(s.begin(), s.end(), m, e, flags);\n}\ninline bool regex_match(const std::string& s, \n                        const basic_regex<char, cpp_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::string::const_iterator> m;\n   return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n#endif\ninline bool regex_match(const std::string& s, \n                        smatch& m,\n                        const basic_regex<char, c_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(s.begin(), s.end(), m, e, flags);\n}\ninline bool regex_match(const std::string& s, \n                        const basic_regex<char, c_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::string::const_iterator> m;\n   return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n#if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32)\ninline bool regex_match(const std::string& s, \n                        smatch& m,\n                        const basic_regex<char, w32_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(s.begin(), s.end(), m, e, flags);\n}\ninline bool regex_match(const std::string& s, \n                        const basic_regex<char, w32_regex_traits<char> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::string::const_iterator> m;\n   return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n#endif\n#if !defined(BOOST_NO_WREGEX)\ninline bool regex_match(const std::basic_string<wchar_t>& s, \n                        match_results<std::basic_string<wchar_t>::const_iterator>& m,\n                        const wregex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(s.begin(), s.end(), m, e, flags);\n}\ninline bool regex_match(const std::basic_string<wchar_t>& s, \n                        const wregex& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::basic_string<wchar_t>::const_iterator> m;\n   return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n#ifndef BOOST_NO_STD_LOCALE\ninline bool regex_match(const std::basic_string<wchar_t>& s, \n                        match_results<std::basic_string<wchar_t>::const_iterator>& m,\n                        const basic_regex<wchar_t, cpp_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(s.begin(), s.end(), m, e, flags);\n}\ninline bool regex_match(const std::basic_string<wchar_t>& s, \n                        const basic_regex<wchar_t, cpp_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::basic_string<wchar_t>::const_iterator> m;\n   return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n#endif\ninline bool regex_match(const std::basic_string<wchar_t>& s, \n                        match_results<std::basic_string<wchar_t>::const_iterator>& m,\n                        const basic_regex<wchar_t, c_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(s.begin(), s.end(), m, e, flags);\n}\ninline bool regex_match(const std::basic_string<wchar_t>& s, \n                        const basic_regex<wchar_t, c_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::basic_string<wchar_t>::const_iterator> m;\n   return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n#if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32)\ninline bool regex_match(const std::basic_string<wchar_t>& s, \n                        match_results<std::basic_string<wchar_t>::const_iterator>& m,\n                        const basic_regex<wchar_t, w32_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(s.begin(), s.end(), m, e, flags);\n}\ninline bool regex_match(const std::basic_string<wchar_t>& s, \n                        const basic_regex<wchar_t, w32_regex_traits<wchar_t> >& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::basic_string<wchar_t>::const_iterator> m;\n   return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n#endif\n#endif\n\n#endif\n\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif   // BOOST_REGEX_MATCH_HPP\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_merge.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_format.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides formatting output routines for search and replace\n  *                operations.  Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n#ifndef BOOST_REGEX_V4_REGEX_MERGE_HPP\n#define BOOST_REGEX_V4_REGEX_MERGE_HPP\n\n\nnamespace boost{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\ntemplate <class OutputIterator, class Iterator, class traits, class charT>\ninline OutputIterator regex_merge(OutputIterator out,\n                         Iterator first,\n                         Iterator last,\n                         const basic_regex<charT, traits>& e, \n                         const charT* fmt, \n                         match_flag_type flags = match_default)\n{\n   return regex_replace(out, first, last, e, fmt, flags);\n}\n\ntemplate <class OutputIterator, class Iterator, class traits, class charT>\ninline OutputIterator regex_merge(OutputIterator out,\n                         Iterator first,\n                         Iterator last,\n                         const basic_regex<charT, traits>& e, \n                         const std::basic_string<charT>& fmt,\n                         match_flag_type flags = match_default)\n{\n   return regex_merge(out, first, last, e, fmt.c_str(), flags);\n}\n\ntemplate <class traits, class charT>\ninline std::basic_string<charT> regex_merge(const std::basic_string<charT>& s,\n                         const basic_regex<charT, traits>& e, \n                         const charT* fmt,\n                         match_flag_type flags = match_default)\n{\n   return regex_replace(s, e, fmt, flags);\n}\n\ntemplate <class traits, class charT>\ninline std::basic_string<charT> regex_merge(const std::basic_string<charT>& s,\n                         const basic_regex<charT, traits>& e, \n                         const std::basic_string<charT>& fmt,\n                         match_flag_type flags = match_default)\n{\n   return regex_replace(s, e, fmt, flags);\n}\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_V4_REGEX_MERGE_HPP\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_raw_buffer.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_raw_buffer.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Raw character buffer for regex code.\n  *                Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n#ifndef BOOST_REGEX_RAW_BUFFER_HPP\n#define BOOST_REGEX_RAW_BUFFER_HPP\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n\n#include <algorithm>\n#include <cstddef>\n\nnamespace boost{\n   namespace BOOST_REGEX_DETAIL_NS{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nstruct empty_padding{};\n\nunion padding\n{\n   void* p;\n   unsigned int i;\n};\n\ntemplate <int N>\nstruct padding3\n{\n   enum{\n      padding_size = 8,\n      padding_mask = 7\n   };\n};\n\ntemplate<>\nstruct padding3<2>\n{\n   enum{\n      padding_size = 2,\n      padding_mask = 1\n   };\n};\n\ntemplate<>\nstruct padding3<4>\n{\n   enum{\n      padding_size = 4,\n      padding_mask = 3\n   };\n};\n\ntemplate<>\nstruct padding3<8>\n{\n   enum{\n      padding_size = 8,\n      padding_mask = 7\n   };\n};\n\ntemplate<>\nstruct padding3<16>\n{\n   enum{\n      padding_size = 16,\n      padding_mask = 15\n   };\n};\n\nenum{\n   padding_size = padding3<sizeof(padding)>::padding_size,\n   padding_mask = padding3<sizeof(padding)>::padding_mask\n};\n\n//\n// class raw_storage\n// basically this is a simplified vector<unsigned char>\n// this is used by basic_regex for expression storage\n//\n\nclass raw_storage\n{\npublic:\n   typedef std::size_t           size_type;\n   typedef unsigned char*        pointer;\nprivate:\n   pointer last, start, end;\npublic:\n\n   raw_storage();\n   raw_storage(size_type n);\n\n   ~raw_storage()\n   {\n      ::operator delete(start);\n   }\n\n   void BOOST_REGEX_CALL resize(size_type n)\n   {\n      size_type newsize = start ? last - start : 1024;\n      while (newsize < n)\n         newsize *= 2;\n      size_type datasize = end - start;\n      // extend newsize to WORD/DWORD boundary:\n      newsize = (newsize + padding_mask) & ~(padding_mask);\n\n      // allocate and copy data:\n      pointer ptr = static_cast<pointer>(::operator new(newsize));\n      BOOST_REGEX_NOEH_ASSERT(ptr)\n         if (start)\n            std::memcpy(ptr, start, datasize);\n\n      // get rid of old buffer:\n      ::operator delete(start);\n\n      // and set up pointers:\n      start = ptr;\n      end = ptr + datasize;\n      last = ptr + newsize;\n   }\n\n   void* BOOST_REGEX_CALL extend(size_type n)\n   {\n      if(size_type(last - end) < n)\n         resize(n + (end - start));\n      pointer result = end;\n      end += n;\n      return result;\n   }\n\n   void* BOOST_REGEX_CALL insert(size_type pos, size_type n)\n   {\n      BOOST_REGEX_ASSERT(pos <= size_type(end - start));\n      if (size_type(last - end) < n)\n         resize(n + (end - start));\n      void* result = start + pos;\n      std::memmove(start + pos + n, start + pos, (end - start) - pos);\n      end += n;\n      return result;\n   }\n\n   size_type BOOST_REGEX_CALL size()\n   {\n      return size_type(end - start);\n   }\n\n   size_type BOOST_REGEX_CALL capacity()\n   {\n      return size_type(last - start);\n   }\n\n   void* BOOST_REGEX_CALL data()const\n   {\n      return start;\n   }\n\n   size_type BOOST_REGEX_CALL index(void* ptr)\n   {\n      return size_type(static_cast<pointer>(ptr) - static_cast<pointer>(data()));\n   }\n\n   void BOOST_REGEX_CALL clear()\n   {\n      end = start;\n   }\n\n   void BOOST_REGEX_CALL align()\n   {\n      // move end up to a boundary:\n      end = start + (((end - start) + padding_mask) & ~padding_mask);\n   }\n   void swap(raw_storage& that)\n   {\n      std::swap(start, that.start);\n      std::swap(end, that.end);\n      std::swap(last, that.last);\n  }\n};\n\ninline raw_storage::raw_storage()\n{\n   last = start = end = 0;\n}\n\ninline raw_storage::raw_storage(size_type n)\n{\n   start = end = static_cast<pointer>(::operator new(n));\n   BOOST_REGEX_NOEH_ASSERT(start)\n   last = start + n;\n}\n\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n\n#endif\n\n\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_replace.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2009\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_format.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides formatting output routines for search and replace\n  *                operations.  Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n#ifndef BOOST_REGEX_V4_REGEX_REPLACE_HPP\n#define BOOST_REGEX_V4_REGEX_REPLACE_HPP\n\n\nnamespace boost{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\ntemplate <class OutputIterator, class BidirectionalIterator, class traits, class charT, class Formatter>\nOutputIterator regex_replace(OutputIterator out,\n                         BidirectionalIterator first,\n                         BidirectionalIterator last,\n                         const basic_regex<charT, traits>& e, \n                         Formatter fmt, \n                         match_flag_type flags = match_default)\n{\n   regex_iterator<BidirectionalIterator, charT, traits> i(first, last, e, flags);\n   regex_iterator<BidirectionalIterator, charT, traits> j;\n   if(i == j)\n   {\n      if(!(flags & regex_constants::format_no_copy))\n         out = BOOST_REGEX_DETAIL_NS::copy(first, last, out);\n   }\n   else\n   {\n      BidirectionalIterator last_m(first);\n      while(i != j)\n      {\n         if(!(flags & regex_constants::format_no_copy))\n            out = BOOST_REGEX_DETAIL_NS::copy(i->prefix().first, i->prefix().second, out); \n         out = i->format(out, fmt, flags, e);\n         last_m = (*i)[0].second;\n         if(flags & regex_constants::format_first_only)\n            break;\n         ++i;\n      }\n      if(!(flags & regex_constants::format_no_copy))\n         out = BOOST_REGEX_DETAIL_NS::copy(last_m, last, out);\n   }\n   return out;\n}\n\ntemplate <class traits, class charT, class Formatter>\nstd::basic_string<charT> regex_replace(const std::basic_string<charT>& s,\n                         const basic_regex<charT, traits>& e, \n                         Formatter fmt,\n                         match_flag_type flags = match_default)\n{\n   std::basic_string<charT> result;\n   BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<charT> > i(result);\n   regex_replace(i, s.begin(), s.end(), e, fmt, flags);\n   return result;\n}\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_V4_REGEX_REPLACE_HPP\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_search.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_search.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides regex_search implementation.\n  */\n\n#ifndef BOOST_REGEX_V4_REGEX_SEARCH_HPP\n#define BOOST_REGEX_V4_REGEX_SEARCH_HPP\n\n\nnamespace boost{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\ntemplate <class BidiIterator, class Allocator, class charT, class traits>\nbool regex_search(BidiIterator first, BidiIterator last, \n                  match_results<BidiIterator, Allocator>& m, \n                  const basic_regex<charT, traits>& e, \n                  match_flag_type flags = match_default)\n{\n   return regex_search(first, last, m, e, flags, first);\n}\n\ntemplate <class BidiIterator, class Allocator, class charT, class traits>\nbool regex_search(BidiIterator first, BidiIterator last, \n                  match_results<BidiIterator, Allocator>& m, \n                  const basic_regex<charT, traits>& e, \n                  match_flag_type flags,\n                  BidiIterator base)\n{\n   if(e.flags() & regex_constants::failbit)\n      return false;\n\n   BOOST_REGEX_DETAIL_NS::perl_matcher<BidiIterator, Allocator, traits> matcher(first, last, m, e, flags, base);\n   return matcher.find();\n}\n\n//\n// regex_search convenience interfaces:\n#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING\n//\n// this isn't really a partial specialisation, but template function\n// overloading - if the compiler doesn't support partial specialisation\n// then it really won't support this either:\ntemplate <class charT, class Allocator, class traits>\ninline bool regex_search(const charT* str, \n                        match_results<const charT*, Allocator>& m, \n                        const basic_regex<charT, traits>& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_search(str, str + traits::length(str), m, e, flags);\n}\n\ntemplate <class ST, class SA, class Allocator, class charT, class traits>\ninline bool regex_search(const std::basic_string<charT, ST, SA>& s, \n                 match_results<typename std::basic_string<charT, ST, SA>::const_iterator, Allocator>& m, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   return regex_search(s.begin(), s.end(), m, e, flags);\n}\n#else  // partial overloads:\ninline bool regex_search(const char* str, \n                        cmatch& m, \n                        const regex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_search(str, str + regex::traits_type::length(str), m, e, flags);\n}\ninline bool regex_search(const char* first, const char* last, \n                  const regex& e, \n                  match_flag_type flags = match_default)\n{\n   cmatch m;\n   return regex_search(first, last, m, e, flags | regex_constants::match_any);\n}\n\n#ifndef BOOST_NO_WREGEX\ninline bool regex_search(const wchar_t* str, \n                        wcmatch& m, \n                        const wregex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_search(str, str + wregex::traits_type::length(str), m, e, flags);\n}\ninline bool regex_search(const wchar_t* first, const wchar_t* last, \n                  const wregex& e, \n                  match_flag_type flags = match_default)\n{\n   wcmatch m;\n   return regex_search(first, last, m, e, flags | regex_constants::match_any);\n}\n#endif\ninline bool regex_search(const std::string& s, \n                        smatch& m,\n                        const regex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_search(s.begin(), s.end(), m, e, flags);\n}\n#if !defined(BOOST_NO_WREGEX)\ninline bool regex_search(const std::basic_string<wchar_t>& s, \n                        wsmatch& m,\n                        const wregex& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_search(s.begin(), s.end(), m, e, flags);\n}\n#endif\n\n#endif\n\ntemplate <class BidiIterator, class charT, class traits>\nbool regex_search(BidiIterator first, BidiIterator last, \n                  const basic_regex<charT, traits>& e, \n                  match_flag_type flags = match_default)\n{\n   if(e.flags() & regex_constants::failbit)\n      return false;\n\n   match_results<BidiIterator> m;\n   typedef typename match_results<BidiIterator>::allocator_type match_alloc_type;\n   BOOST_REGEX_DETAIL_NS::perl_matcher<BidiIterator, match_alloc_type, traits> matcher(first, last, m, e, flags | regex_constants::match_any, first);\n   return matcher.find();\n}\n\n#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING\n\ntemplate <class charT, class traits>\ninline bool regex_search(const charT* str, \n                        const basic_regex<charT, traits>& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_search(str, str + traits::length(str), e, flags);\n}\n\ntemplate <class ST, class SA, class charT, class traits>\ninline bool regex_search(const std::basic_string<charT, ST, SA>& s, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   return regex_search(s.begin(), s.end(), e, flags);\n}\n#else  // non-template function overloads\ninline bool regex_search(const char* str, \n                        const regex& e, \n                        match_flag_type flags = match_default)\n{\n   cmatch m;\n   return regex_search(str, str + regex::traits_type::length(str), m, e, flags | regex_constants::match_any);\n}\n#ifndef BOOST_NO_WREGEX\ninline bool regex_search(const wchar_t* str, \n                        const wregex& e, \n                        match_flag_type flags = match_default)\n{\n   wcmatch m;\n   return regex_search(str, str + wregex::traits_type::length(str), m, e, flags | regex_constants::match_any);\n}\n#endif\ninline bool regex_search(const std::string& s, \n                        const regex& e, \n                        match_flag_type flags = match_default)\n{\n   smatch m;\n   return regex_search(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n#if !defined(BOOST_NO_WREGEX)\ninline bool regex_search(const std::basic_string<wchar_t>& s, \n                        const wregex& e, \n                        match_flag_type flags = match_default)\n{\n   wsmatch m;\n   return regex_search(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n\n#endif // BOOST_NO_WREGEX\n\n#endif // partial overload\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_V4_REGEX_SEARCH_HPP\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_split.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_split.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Implements regex_split and associated functions.\n  *                Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n#ifndef BOOST_REGEX_SPLIT_HPP\n#define BOOST_REGEX_SPLIT_HPP\n\nnamespace boost{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#if BOOST_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class charT>\nconst basic_regex<charT>& get_default_expression(charT)\n{\n   static const charT expression_text[4] = { '\\\\', 's', '+', '\\00', };\n   static const basic_regex<charT> e(expression_text);\n   return e;\n}\n\ntemplate <class OutputIterator, class charT, class Traits1, class Alloc1>\nclass split_pred\n{\n   typedef std::basic_string<charT, Traits1, Alloc1> string_type;\n   typedef typename string_type::const_iterator iterator_type;\n   iterator_type* p_last;\n   OutputIterator* p_out;\n   std::size_t* p_max;\n   std::size_t initial_max;\npublic:\n   split_pred(iterator_type* a, OutputIterator* b, std::size_t* c)\n      : p_last(a), p_out(b), p_max(c), initial_max(*c) {}\n\n   bool operator()(const match_results<iterator_type>& what);\n};\n\ntemplate <class OutputIterator, class charT, class Traits1, class Alloc1>\nbool split_pred<OutputIterator, charT, Traits1, Alloc1>::operator()\n   (const match_results<iterator_type>& what)\n{\n   *p_last = what[0].second;\n   if(what.size() > 1)\n   {\n      // output sub-expressions only:\n      for(unsigned i = 1; i < what.size(); ++i)\n      {\n         *(*p_out) = what.str(i);\n         ++(*p_out);\n         if(0 == --*p_max) return false;\n      }\n      return *p_max != 0;\n   }\n   else\n   {\n      // output $` only if it's not-null or not at the start of the input:\n      const sub_match<iterator_type>& sub = what[-1];\n      if((sub.first != sub.second) || (*p_max != initial_max))\n      {\n         *(*p_out) = sub.str();\n         ++(*p_out);\n         return --*p_max;\n      }\n   }\n   //\n   // initial null, do nothing:\n   return true;\n}\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\ntemplate <class OutputIterator, class charT, class Traits1, class Alloc1, class Traits2>\nstd::size_t regex_split(OutputIterator out,\n                   std::basic_string<charT, Traits1, Alloc1>& s, \n                   const basic_regex<charT, Traits2>& e,\n                   match_flag_type flags,\n                   std::size_t max_split)\n{\n   typedef typename std::basic_string<charT, Traits1, Alloc1>::const_iterator  ci_t;\n   //typedef typename match_results<ci_t>::allocator_type                        match_allocator;\n   ci_t last = s.begin();\n   std::size_t init_size = max_split;\n   BOOST_REGEX_DETAIL_NS::split_pred<OutputIterator, charT, Traits1, Alloc1> pred(&last, &out, &max_split);\n   ci_t i, j;\n   i = s.begin();\n   j = s.end();\n   regex_grep(pred, i, j, e, flags);\n   //\n   // if there is still input left, do a final push as long as max_split\n   // is not exhausted, and we're not splitting sub-expressions rather \n   // than whitespace:\n   if(max_split && (last != s.end()) && (e.mark_count() == 0))\n   {\n      *out = std::basic_string<charT, Traits1, Alloc1>((ci_t)last, (ci_t)s.end());\n      ++out;\n      last = s.end();\n      --max_split;\n   }\n   //\n   // delete from the string everything that has been processed so far:\n   s.erase(0, last - s.begin());\n   //\n   // return the number of new records pushed:\n   return init_size - max_split;\n}\n\ntemplate <class OutputIterator, class charT, class Traits1, class Alloc1, class Traits2>\ninline std::size_t regex_split(OutputIterator out,\n                   std::basic_string<charT, Traits1, Alloc1>& s, \n                   const basic_regex<charT, Traits2>& e,\n                   match_flag_type flags = match_default)\n{\n   return regex_split(out, s, e, flags, UINT_MAX);\n}\n\ntemplate <class OutputIterator, class charT, class Traits1, class Alloc1>\ninline std::size_t regex_split(OutputIterator out,\n                   std::basic_string<charT, Traits1, Alloc1>& s)\n{\n   return regex_split(out, s, BOOST_REGEX_DETAIL_NS::get_default_expression(charT(0)), match_default, UINT_MAX);\n}\n\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_token_iterator.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_token_iterator.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides regex_token_iterator implementation.\n  */\n\n#ifndef BOOST_REGEX_V4_REGEX_TOKEN_ITERATOR_HPP\n#define BOOST_REGEX_V4_REGEX_TOKEN_ITERATOR_HPP\n\n#include <boost/shared_ptr.hpp>\n#include <boost/detail/workaround.hpp>\n#if (BOOST_WORKAROUND(BOOST_BORLANDC, >= 0x560) && BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x570)))\\\n      || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003))\n//\n// Borland C++ Builder 6, and Visual C++ 6,\n// can't cope with the array template constructor\n// so we have a template member that will accept any type as \n// argument, and then assert that is really is an array:\n//\n#include <boost/static_assert.hpp>\n#include <boost/type_traits/is_array.hpp>\n#endif\n\nnamespace boost{\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#pragma warning(push)\n#pragma warning(disable:4700)\n#endif\n\ntemplate <class BidirectionalIterator,\n          class charT,\n          class traits>\nclass regex_token_iterator_implementation \n{\n   typedef basic_regex<charT, traits> regex_type;\n   typedef sub_match<BidirectionalIterator>      value_type;\n\n   match_results<BidirectionalIterator> what;   // current match\n   BidirectionalIterator                base;    // start of search area\n   BidirectionalIterator                end;    // end of search area\n   const regex_type                     re;    // the expression\n   match_flag_type                      flags;  // match flags\n   value_type                           result; // the current string result\n   int                                  N;      // the current sub-expression being enumerated\n   std::vector<int>                     subs;   // the sub-expressions to enumerate\n\npublic:\n   regex_token_iterator_implementation(const regex_token_iterator_implementation& other)\n      : what(other.what), base(other.base), end(other.end), re(other.re), flags(other.flags), result(other.result), N(other.N), subs(other.subs) {}\n   regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, int sub, match_flag_type f)\n      : end(last), re(*p), flags(f), N(0){ subs.push_back(sub); }\n   regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const std::vector<int>& v, match_flag_type f)\n      : end(last), re(*p), flags(f), N(0), subs(v){}\n#if !BOOST_WORKAROUND(__HP_aCC, < 60700)\n#if (BOOST_WORKAROUND(BOOST_BORLANDC, >= 0x560) && BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x570)))\\\n      || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) \\\n      || BOOST_WORKAROUND(__HP_aCC, < 60700)\n   template <class T>\n   regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const T& submatches, match_flag_type f)\n      : end(last), re(*p), flags(f), N(0)\n   {\n      // assert that T really is an array:\n      BOOST_STATIC_ASSERT(::boost::is_array<T>::value);\n      const std::size_t array_size = sizeof(T) / sizeof(submatches[0]);\n      for(std::size_t i = 0; i < array_size; ++i)\n      {\n         subs.push_back(submatches[i]);\n      }\n   }\n#else\n   template <std::size_t CN>\n   regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const int (&submatches)[CN], match_flag_type f)\n      : end(last), re(*p), flags(f), N(0)\n   {\n      for(std::size_t i = 0; i < CN; ++i)\n      {\n         subs.push_back(submatches[i]);\n      }\n   }\n#endif\n#endif\n   bool init(BidirectionalIterator first)\n   {\n      N = 0;\n      base = first;\n      if(regex_search(first, end, what, re, flags, base) == true)\n      {\n         N = 0;\n         result = ((subs[N] == -1) ? what.prefix() : what[(int)subs[N]]);\n         return true;\n      }\n      else if((subs[N] == -1) && (first != end))\n      {\n         result.first = first;\n         result.second = end;\n         result.matched = (first != end);\n         N = -1;\n         return true;\n      }\n      return false;\n   }\n   bool compare(const regex_token_iterator_implementation& that)\n   {\n      if(this == &that) return true;\n      return (&re.get_data() == &that.re.get_data()) \n         && (end == that.end) \n         && (flags == that.flags) \n         && (N == that.N) \n         && (what[0].first == that.what[0].first) \n         && (what[0].second == that.what[0].second);\n   }\n   const value_type& get()\n   { return result; }\n   bool next()\n   {\n      if(N == -1)\n         return false;\n      if(N+1 < (int)subs.size())\n      {\n         ++N;\n         result =((subs[N] == -1) ? what.prefix() : what[subs[N]]);\n         return true;\n      }\n      //if(what.prefix().first != what[0].second)\n      //   flags |= /*match_prev_avail |*/ regex_constants::match_not_bob;\n      BidirectionalIterator last_end(what[0].second);\n      if(regex_search(last_end, end, what, re, ((what[0].first == what[0].second) ? flags | regex_constants::match_not_initial_null : flags), base))\n      {\n         N =0;\n         result =((subs[N] == -1) ? what.prefix() : what[subs[N]]);\n         return true;\n      }\n      else if((last_end != end) && (subs[0] == -1))\n      {\n         N =-1;\n         result.first = last_end;\n         result.second = end;\n         result.matched = (last_end != end);\n         return true;\n      }\n      return false;\n   }\nprivate:\n   regex_token_iterator_implementation& operator=(const regex_token_iterator_implementation&);\n};\n\ntemplate <class BidirectionalIterator, \n          class charT = BOOST_DEDUCED_TYPENAME BOOST_REGEX_DETAIL_NS::regex_iterator_traits<BidirectionalIterator>::value_type,\n          class traits = regex_traits<charT> >\nclass regex_token_iterator \n{\nprivate:\n   typedef regex_token_iterator_implementation<BidirectionalIterator, charT, traits> impl;\n   typedef shared_ptr<impl> pimpl;\npublic:\n   typedef          basic_regex<charT, traits>                   regex_type;\n   typedef          sub_match<BidirectionalIterator>                        value_type;\n   typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<BidirectionalIterator>::difference_type \n                                                                            difference_type;\n   typedef          const value_type*                                       pointer;\n   typedef          const value_type&                                       reference; \n   typedef          std::forward_iterator_tag                               iterator_category;\n   \n   regex_token_iterator(){}\n   regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, \n                        int submatch = 0, match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatch, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n   regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, \n                        const std::vector<int>& submatches, match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatches, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n#if !BOOST_WORKAROUND(__HP_aCC, < 60700)\n#if (BOOST_WORKAROUND(BOOST_BORLANDC, >= 0x560) && BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x570)))\\\n      || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) \\\n      || BOOST_WORKAROUND(__HP_aCC, < 60700)\n   template <class T>\n   regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re,\n                        const T& submatches, match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatches, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n#else\n   template <std::size_t N>\n   regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re,\n                        const int (&submatches)[N], match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatches, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n#endif\n#endif\n   regex_token_iterator(const regex_token_iterator& that)\n      : pdata(that.pdata) {}\n   regex_token_iterator& operator=(const regex_token_iterator& that)\n   {\n      pdata = that.pdata;\n      return *this;\n   }\n   bool operator==(const regex_token_iterator& that)const\n   { \n      if((pdata.get() == 0) || (that.pdata.get() == 0))\n         return pdata.get() == that.pdata.get();\n      return pdata->compare(*(that.pdata.get())); \n   }\n   bool operator!=(const regex_token_iterator& that)const\n   { return !(*this == that); }\n   const value_type& operator*()const\n   { return pdata->get(); }\n   const value_type* operator->()const\n   { return &(pdata->get()); }\n   regex_token_iterator& operator++()\n   {\n      cow();\n      if(0 == pdata->next())\n      {\n         pdata.reset();\n      }\n      return *this;\n   }\n   regex_token_iterator operator++(int)\n   {\n      regex_token_iterator result(*this);\n      ++(*this);\n      return result;\n   }\nprivate:\n\n   pimpl pdata;\n\n   void cow()\n   {\n      // copy-on-write\n      if(pdata.get() && !pdata.unique())\n      {\n         pdata.reset(new impl(*(pdata.get())));\n      }\n   }\n};\n\ntypedef regex_token_iterator<const char*> cregex_token_iterator;\ntypedef regex_token_iterator<std::string::const_iterator> sregex_token_iterator;\n#ifndef BOOST_NO_WREGEX\ntypedef regex_token_iterator<const wchar_t*> wcregex_token_iterator;\ntypedef regex_token_iterator<std::wstring::const_iterator> wsregex_token_iterator;\n#endif\n\ntemplate <class charT, class traits>\ninline regex_token_iterator<const charT*, charT, traits> make_regex_token_iterator(const charT* p, const basic_regex<charT, traits>& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<const charT*, charT, traits>(p, p+traits::length(p), e, submatch, m);\n}\ntemplate <class charT, class traits, class ST, class SA>\ninline regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string<charT, ST, SA>& p, const basic_regex<charT, traits>& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m);\n}\ntemplate <class charT, class traits, std::size_t N>\ninline regex_token_iterator<const charT*, charT, traits> make_regex_token_iterator(const charT* p, const basic_regex<charT, traits>& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<const charT*, charT, traits>(p, p+traits::length(p), e, submatch, m);\n}\ntemplate <class charT, class traits, class ST, class SA, std::size_t N>\ninline regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string<charT, ST, SA>& p, const basic_regex<charT, traits>& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m);\n}\ntemplate <class charT, class traits>\ninline regex_token_iterator<const charT*, charT, traits> make_regex_token_iterator(const charT* p, const basic_regex<charT, traits>& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<const charT*, charT, traits>(p, p+traits::length(p), e, submatch, m);\n}\ntemplate <class charT, class traits, class ST, class SA>\ninline regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string<charT, ST, SA>& p, const basic_regex<charT, traits>& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m);\n}\n\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif // BOOST_REGEX_V4_REGEX_TOKEN_ITERATOR_HPP\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_traits.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression traits classes.\n  */\n\n#ifndef BOOST_REGEX_TRAITS_HPP_INCLUDED\n#define BOOST_REGEX_TRAITS_HPP_INCLUDED\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n#ifndef BOOST_REGEX_WORKAROUND_HPP\n#include <boost/regex/v4/regex_workaround.hpp>\n#endif\n#ifndef BOOST_REGEX_SYNTAX_TYPE_HPP\n#include <boost/regex/v4/syntax_type.hpp>\n#endif\n#ifndef BOOST_REGEX_ERROR_TYPE_HPP\n#include <boost/regex/v4/error_type.hpp>\n#endif\n#ifndef BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED\n#include <boost/regex/v4/regex_traits_defaults.hpp>\n#endif\n#ifndef BOOST_NO_STD_LOCALE\n#  ifndef BOOST_CPP_REGEX_TRAITS_HPP_INCLUDED\n#     include <boost/regex/v4/cpp_regex_traits.hpp>\n#  endif\n#endif\n#if !BOOST_WORKAROUND(BOOST_BORLANDC, < 0x560)\n#  ifndef BOOST_C_REGEX_TRAITS_HPP_INCLUDED\n#     include <boost/regex/v4/c_regex_traits.hpp>\n#  endif\n#endif\n#if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32)\n#  ifndef BOOST_W32_REGEX_TRAITS_HPP_INCLUDED\n#     include <boost/regex/v4/w32_regex_traits.hpp>\n#  endif\n#endif\n#ifndef BOOST_REGEX_FWD_HPP_INCLUDED\n#include <boost/regex_fwd.hpp>\n#endif\n\n#include \"boost/mpl/has_xxx.hpp\"\n#include <boost/static_assert.hpp>\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\n\ntemplate <class charT, class implementationT >\nstruct regex_traits : public implementationT\n{\n   regex_traits() : implementationT() {}\n};\n\n//\n// class regex_traits_wrapper.\n// this is what our implementation will actually store;\n// it provides default implementations of the \"optional\"\n// interfaces that we support, in addition to the\n// required \"standard\" ones:\n//\nnamespace BOOST_REGEX_DETAIL_NS{\n#if !BOOST_WORKAROUND(__HP_aCC, < 60000)\nBOOST_MPL_HAS_XXX_TRAIT_DEF(boost_extensions_tag)\n#else\ntemplate<class T>\nstruct has_boost_extensions_tag\n{\n   BOOST_STATIC_CONSTANT(bool, value = false);\n};\n#endif\n\ntemplate <class BaseT>\nstruct default_wrapper : public BaseT\n{\n   typedef typename BaseT::char_type char_type;\n   std::string error_string(::boost::regex_constants::error_type e)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::get_default_error_string(e);\n   }\n   ::boost::regex_constants::syntax_type syntax_type(char_type c)const\n   {\n      return ((c & 0x7f) == c) ? get_default_syntax_type(static_cast<char>(c)) : ::boost::regex_constants::syntax_char;\n   }\n   ::boost::regex_constants::escape_syntax_type escape_syntax_type(char_type c)const\n   {\n      return ((c & 0x7f) == c) ? get_default_escape_syntax_type(static_cast<char>(c)) : ::boost::regex_constants::escape_type_identity;\n   }\n   boost::intmax_t toi(const char_type*& p1, const char_type* p2, int radix)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this);\n   }\n   char_type translate(char_type c, bool icase)const\n   {\n      return (icase ? this->translate_nocase(c) : this->translate(c));\n   }\n   char_type translate(char_type c)const\n   {\n      return BaseT::translate(c);\n   }\n   char_type tolower(char_type c)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_lower(c);\n   }\n   char_type toupper(char_type c)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_upper(c);\n   }\n};\n\ntemplate <class BaseT, bool has_extensions>\nstruct compute_wrapper_base\n{\n   typedef BaseT type;\n};\n#if !BOOST_WORKAROUND(__HP_aCC, < 60000)\ntemplate <class BaseT>\nstruct compute_wrapper_base<BaseT, false>\n{\n   typedef default_wrapper<BaseT> type;\n};\n#else\ntemplate <>\nstruct compute_wrapper_base<c_regex_traits<char>, false>\n{\n   typedef default_wrapper<c_regex_traits<char> > type;\n};\n#ifndef BOOST_NO_WREGEX\ntemplate <>\nstruct compute_wrapper_base<c_regex_traits<wchar_t>, false>\n{\n   typedef default_wrapper<c_regex_traits<wchar_t> > type;\n};\n#endif\n#endif\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\ntemplate <class BaseT>\nstruct regex_traits_wrapper \n   : public ::boost::BOOST_REGEX_DETAIL_NS::compute_wrapper_base<\n               BaseT, \n               ::boost::BOOST_REGEX_DETAIL_NS::has_boost_extensions_tag<BaseT>::value\n            >::type\n{\n   regex_traits_wrapper(){}\nprivate:\n   regex_traits_wrapper(const regex_traits_wrapper&);\n   regex_traits_wrapper& operator=(const regex_traits_wrapper&);\n};\n\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif // include\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_traits_defaults.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the\n * Boost Software License, Version 1.0. (See accompanying file\n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_traits_defaults.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares API's for access to regex_traits default properties.\n  */\n\n#ifndef BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED\n#define BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#include <boost/regex/config.hpp>\n#include <boost/cstdint.hpp>\n\n#include <cctype>\n#include <cwctype>\n#include <locale>\n\n#ifndef BOOST_REGEX_SYNTAX_TYPE_HPP\n#include <boost/regex/v4/syntax_type.hpp>\n#endif\n#ifndef BOOST_REGEX_ERROR_TYPE_HPP\n#include <boost/regex/v4/error_type.hpp>\n#endif\n#include <boost/regex/v4/regex_workaround.hpp>\n#include <boost/type_traits/make_unsigned.hpp>\n#include <boost/utility/enable_if.hpp>\n\n#ifdef BOOST_NO_STDC_NAMESPACE\nnamespace std{\n   using ::strlen;\n}\n#endif\n\nnamespace boost{ namespace BOOST_REGEX_DETAIL_NS{\n\n\n//\n// helpers to suppress warnings:\n//\ntemplate <class charT>\ninline bool is_extended(charT c)\n{\n   typedef typename make_unsigned<charT>::type unsigned_type; \n   return (sizeof(charT) > 1) && (static_cast<unsigned_type>(c) >= 256u); \n}\ninline bool is_extended(char)\n{ return false; }\n\ninline const char* BOOST_REGEX_CALL get_default_syntax(regex_constants::syntax_type n)\n{\n   // if the user hasn't supplied a message catalog, then this supplies\n   // default \"messages\" for us to load in the range 1-100.\n   const char* messages[] = {\n         \"\",\n         \"(\",\n         \")\",\n         \"$\",\n         \"^\",\n         \".\",\n         \"*\",\n         \"+\",\n         \"?\",\n         \"[\",\n         \"]\",\n         \"|\",\n         \"\\\\\",\n         \"#\",\n         \"-\",\n         \"{\",\n         \"}\",\n         \"0123456789\",\n         \"b\",\n         \"B\",\n         \"<\",\n         \">\",\n         \"\",\n         \"\",\n         \"A`\",\n         \"z'\",\n         \"\\n\",\n         \",\",\n         \"a\",\n         \"f\",\n         \"n\",\n         \"r\",\n         \"t\",\n         \"v\",\n         \"x\",\n         \"c\",\n         \":\",\n         \"=\",\n         \"e\",\n         \"\",\n         \"\",\n         \"\",\n         \"\",\n         \"\",\n         \"\",\n         \"\",\n         \"\",\n         \"E\",\n         \"Q\",\n         \"X\",\n         \"C\",\n         \"Z\",\n         \"G\",\n         \"!\",\n         \"p\",\n         \"P\",\n         \"N\",\n         \"gk\",\n         \"K\",\n         \"R\",\n   };\n\n   return ((n >= (sizeof(messages) / sizeof(messages[1]))) ? \"\" : messages[n]);\n}\n\ninline const char* BOOST_REGEX_CALL get_default_error_string(regex_constants::error_type n)\n{\n   static const char* const s_default_error_messages[] = {\n      \"Success\",                                                            /* REG_NOERROR 0 error_ok */\n      \"No match\",                                                           /* REG_NOMATCH 1 error_no_match */\n      \"Invalid regular expression.\",                                        /* REG_BADPAT 2 error_bad_pattern */\n      \"Invalid collation character.\",                                       /* REG_ECOLLATE 3 error_collate */\n      \"Invalid character class name, collating name, or character range.\",  /* REG_ECTYPE 4 error_ctype */\n      \"Invalid or unterminated escape sequence.\",                           /* REG_EESCAPE 5 error_escape */\n      \"Invalid back reference: specified capturing group does not exist.\",  /* REG_ESUBREG 6 error_backref */\n      \"Unmatched [ or [^ in character class declaration.\",                  /* REG_EBRACK 7 error_brack */\n      \"Unmatched marking parenthesis ( or \\\\(.\",                            /* REG_EPAREN 8 error_paren */\n      \"Unmatched quantified repeat operator { or \\\\{.\",                     /* REG_EBRACE 9 error_brace */\n      \"Invalid content of repeat range.\",                                   /* REG_BADBR 10 error_badbrace */\n      \"Invalid range end in character class\",                               /* REG_ERANGE 11 error_range */\n      \"Out of memory.\",                                                     /* REG_ESPACE 12 error_space NOT USED */\n      \"Invalid preceding regular expression prior to repetition operator.\", /* REG_BADRPT 13 error_badrepeat */\n      \"Premature end of regular expression\",                                /* REG_EEND 14 error_end NOT USED */\n      \"Regular expression is too large.\",                                   /* REG_ESIZE 15 error_size NOT USED */\n      \"Unmatched ) or \\\\)\",                                                 /* REG_ERPAREN 16 error_right_paren NOT USED */\n      \"Empty regular expression.\",                                          /* REG_EMPTY 17 error_empty */\n      \"The complexity of matching the regular expression exceeded predefined bounds.  \"\n      \"Try refactoring the regular expression to make each choice made by the state machine unambiguous.  \"\n      \"This exception is thrown to prevent \\\"eternal\\\" matches that take an \"\n      \"indefinite period time to locate.\",                                  /* REG_ECOMPLEXITY 18 error_complexity */\n      \"Ran out of stack space trying to match the regular expression.\",     /* REG_ESTACK 19 error_stack */\n      \"Invalid or unterminated Perl (?...) sequence.\",                      /* REG_E_PERL 20 error_perl */\n      \"Unknown error.\",                                                     /* REG_E_UNKNOWN 21 error_unknown */\n   };\n\n   return (n > ::boost::regex_constants::error_unknown) ? s_default_error_messages[::boost::regex_constants::error_unknown] : s_default_error_messages[n];\n}\n\ninline regex_constants::syntax_type BOOST_REGEX_CALL get_default_syntax_type(char c)\n{\n   //\n   // char_syntax determines how the compiler treats a given character\n   // in a regular expression.\n   //\n   static regex_constants::syntax_type char_syntax[] = {\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_newline,     /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /* */    // 32\n      regex_constants::syntax_not,        /*!*/\n      regex_constants::syntax_char,        /*\"*/\n      regex_constants::syntax_hash,        /*#*/\n      regex_constants::syntax_dollar,        /*$*/\n      regex_constants::syntax_char,        /*%*/\n      regex_constants::syntax_char,        /*&*/\n      regex_constants::escape_type_end_buffer,  /*'*/\n      regex_constants::syntax_open_mark,        /*(*/\n      regex_constants::syntax_close_mark,        /*)*/\n      regex_constants::syntax_star,        /***/\n      regex_constants::syntax_plus,        /*+*/\n      regex_constants::syntax_comma,        /*,*/\n      regex_constants::syntax_dash,        /*-*/\n      regex_constants::syntax_dot,        /*.*/\n      regex_constants::syntax_char,        /*/*/\n      regex_constants::syntax_digit,        /*0*/\n      regex_constants::syntax_digit,        /*1*/\n      regex_constants::syntax_digit,        /*2*/\n      regex_constants::syntax_digit,        /*3*/\n      regex_constants::syntax_digit,        /*4*/\n      regex_constants::syntax_digit,        /*5*/\n      regex_constants::syntax_digit,        /*6*/\n      regex_constants::syntax_digit,        /*7*/\n      regex_constants::syntax_digit,        /*8*/\n      regex_constants::syntax_digit,        /*9*/\n      regex_constants::syntax_colon,        /*:*/\n      regex_constants::syntax_char,        /*;*/\n      regex_constants::escape_type_left_word, /*<*/\n      regex_constants::syntax_equal,        /*=*/\n      regex_constants::escape_type_right_word, /*>*/\n      regex_constants::syntax_question,        /*?*/\n      regex_constants::syntax_char,        /*@*/\n      regex_constants::syntax_char,        /*A*/\n      regex_constants::syntax_char,        /*B*/\n      regex_constants::syntax_char,        /*C*/\n      regex_constants::syntax_char,        /*D*/\n      regex_constants::syntax_char,        /*E*/\n      regex_constants::syntax_char,        /*F*/\n      regex_constants::syntax_char,        /*G*/\n      regex_constants::syntax_char,        /*H*/\n      regex_constants::syntax_char,        /*I*/\n      regex_constants::syntax_char,        /*J*/\n      regex_constants::syntax_char,        /*K*/\n      regex_constants::syntax_char,        /*L*/\n      regex_constants::syntax_char,        /*M*/\n      regex_constants::syntax_char,        /*N*/\n      regex_constants::syntax_char,        /*O*/\n      regex_constants::syntax_char,        /*P*/\n      regex_constants::syntax_char,        /*Q*/\n      regex_constants::syntax_char,        /*R*/\n      regex_constants::syntax_char,        /*S*/\n      regex_constants::syntax_char,        /*T*/\n      regex_constants::syntax_char,        /*U*/\n      regex_constants::syntax_char,        /*V*/\n      regex_constants::syntax_char,        /*W*/\n      regex_constants::syntax_char,        /*X*/\n      regex_constants::syntax_char,        /*Y*/\n      regex_constants::syntax_char,        /*Z*/\n      regex_constants::syntax_open_set,        /*[*/\n      regex_constants::syntax_escape,        /*\\*/\n      regex_constants::syntax_close_set,        /*]*/\n      regex_constants::syntax_caret,        /*^*/\n      regex_constants::syntax_char,        /*_*/\n      regex_constants::syntax_char,        /*`*/\n      regex_constants::syntax_char,        /*a*/\n      regex_constants::syntax_char,        /*b*/\n      regex_constants::syntax_char,        /*c*/\n      regex_constants::syntax_char,        /*d*/\n      regex_constants::syntax_char,        /*e*/\n      regex_constants::syntax_char,        /*f*/\n      regex_constants::syntax_char,        /*g*/\n      regex_constants::syntax_char,        /*h*/\n      regex_constants::syntax_char,        /*i*/\n      regex_constants::syntax_char,        /*j*/\n      regex_constants::syntax_char,        /*k*/\n      regex_constants::syntax_char,        /*l*/\n      regex_constants::syntax_char,        /*m*/\n      regex_constants::syntax_char,        /*n*/\n      regex_constants::syntax_char,        /*o*/\n      regex_constants::syntax_char,        /*p*/\n      regex_constants::syntax_char,        /*q*/\n      regex_constants::syntax_char,        /*r*/\n      regex_constants::syntax_char,        /*s*/\n      regex_constants::syntax_char,        /*t*/\n      regex_constants::syntax_char,        /*u*/\n      regex_constants::syntax_char,        /*v*/\n      regex_constants::syntax_char,        /*w*/\n      regex_constants::syntax_char,        /*x*/\n      regex_constants::syntax_char,        /*y*/\n      regex_constants::syntax_char,        /*z*/\n      regex_constants::syntax_open_brace,        /*{*/\n      regex_constants::syntax_or,        /*|*/\n      regex_constants::syntax_close_brace,        /*}*/\n      regex_constants::syntax_char,        /*~*/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n   };\n\n   return char_syntax[(unsigned char)c];\n}\n\ninline regex_constants::escape_syntax_type BOOST_REGEX_CALL get_default_escape_syntax_type(char c)\n{\n   //\n   // char_syntax determines how the compiler treats a given character\n   // in a regular expression.\n   //\n   static regex_constants::escape_syntax_type char_syntax[] = {\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,     /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /* */    // 32\n      regex_constants::escape_type_identity,        /*!*/\n      regex_constants::escape_type_identity,        /*\"*/\n      regex_constants::escape_type_identity,        /*#*/\n      regex_constants::escape_type_identity,        /*$*/\n      regex_constants::escape_type_identity,        /*%*/\n      regex_constants::escape_type_identity,        /*&*/\n      regex_constants::escape_type_end_buffer,        /*'*/\n      regex_constants::syntax_open_mark,        /*(*/\n      regex_constants::syntax_close_mark,        /*)*/\n      regex_constants::escape_type_identity,        /***/\n      regex_constants::syntax_plus,                 /*+*/\n      regex_constants::escape_type_identity,        /*,*/\n      regex_constants::escape_type_identity,        /*-*/\n      regex_constants::escape_type_identity,        /*.*/\n      regex_constants::escape_type_identity,        /*/*/\n      regex_constants::escape_type_decimal,        /*0*/\n      regex_constants::escape_type_backref,        /*1*/\n      regex_constants::escape_type_backref,        /*2*/\n      regex_constants::escape_type_backref,        /*3*/\n      regex_constants::escape_type_backref,        /*4*/\n      regex_constants::escape_type_backref,        /*5*/\n      regex_constants::escape_type_backref,        /*6*/\n      regex_constants::escape_type_backref,        /*7*/\n      regex_constants::escape_type_backref,        /*8*/\n      regex_constants::escape_type_backref,        /*9*/\n      regex_constants::escape_type_identity,        /*:*/\n      regex_constants::escape_type_identity,        /*;*/\n      regex_constants::escape_type_left_word,        /*<*/\n      regex_constants::escape_type_identity,        /*=*/\n      regex_constants::escape_type_right_word,        /*>*/\n      regex_constants::syntax_question,              /*?*/\n      regex_constants::escape_type_identity,         /*@*/\n      regex_constants::escape_type_start_buffer,     /*A*/\n      regex_constants::escape_type_not_word_assert,  /*B*/\n      regex_constants::escape_type_C,                /*C*/\n      regex_constants::escape_type_not_class,        /*D*/\n      regex_constants::escape_type_E,                /*E*/\n      regex_constants::escape_type_not_class,        /*F*/\n      regex_constants::escape_type_G,                /*G*/\n      regex_constants::escape_type_not_class,        /*H*/\n      regex_constants::escape_type_not_class,        /*I*/\n      regex_constants::escape_type_not_class,        /*J*/\n      regex_constants::escape_type_reset_start_mark, /*K*/\n      regex_constants::escape_type_not_class,        /*L*/\n      regex_constants::escape_type_not_class,        /*M*/\n      regex_constants::escape_type_named_char,       /*N*/\n      regex_constants::escape_type_not_class,        /*O*/\n      regex_constants::escape_type_not_property,     /*P*/\n      regex_constants::escape_type_Q,                /*Q*/\n      regex_constants::escape_type_line_ending,      /*R*/\n      regex_constants::escape_type_not_class,        /*S*/\n      regex_constants::escape_type_not_class,        /*T*/\n      regex_constants::escape_type_not_class,        /*U*/\n      regex_constants::escape_type_not_class,        /*V*/\n      regex_constants::escape_type_not_class,        /*W*/\n      regex_constants::escape_type_X,                /*X*/\n      regex_constants::escape_type_not_class,        /*Y*/\n      regex_constants::escape_type_Z,                /*Z*/\n      regex_constants::escape_type_identity,        /*[*/\n      regex_constants::escape_type_identity,        /*\\*/\n      regex_constants::escape_type_identity,        /*]*/\n      regex_constants::escape_type_identity,        /*^*/\n      regex_constants::escape_type_identity,        /*_*/\n      regex_constants::escape_type_start_buffer,        /*`*/\n      regex_constants::escape_type_control_a,        /*a*/\n      regex_constants::escape_type_word_assert,        /*b*/\n      regex_constants::escape_type_ascii_control,        /*c*/\n      regex_constants::escape_type_class,        /*d*/\n      regex_constants::escape_type_e,        /*e*/\n      regex_constants::escape_type_control_f,       /*f*/\n      regex_constants::escape_type_extended_backref,  /*g*/\n      regex_constants::escape_type_class,        /*h*/\n      regex_constants::escape_type_class,        /*i*/\n      regex_constants::escape_type_class,        /*j*/\n      regex_constants::escape_type_extended_backref, /*k*/\n      regex_constants::escape_type_class,        /*l*/\n      regex_constants::escape_type_class,        /*m*/\n      regex_constants::escape_type_control_n,       /*n*/\n      regex_constants::escape_type_class,           /*o*/\n      regex_constants::escape_type_property,        /*p*/\n      regex_constants::escape_type_class,           /*q*/\n      regex_constants::escape_type_control_r,       /*r*/\n      regex_constants::escape_type_class,           /*s*/\n      regex_constants::escape_type_control_t,       /*t*/\n      regex_constants::escape_type_class,         /*u*/\n      regex_constants::escape_type_control_v,       /*v*/\n      regex_constants::escape_type_class,           /*w*/\n      regex_constants::escape_type_hex,             /*x*/\n      regex_constants::escape_type_class,           /*y*/\n      regex_constants::escape_type_end_buffer,      /*z*/\n      regex_constants::syntax_open_brace,           /*{*/\n      regex_constants::syntax_or,                   /*|*/\n      regex_constants::syntax_close_brace,          /*}*/\n      regex_constants::escape_type_identity,        /*~*/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n   };\n\n   return char_syntax[(unsigned char)c];\n}\n\n// is charT c a combining character?\ninline bool BOOST_REGEX_CALL is_combining_implementation(boost::uint_least16_t c)\n{\n   const boost::uint_least16_t combining_ranges[] = { 0x0300, 0x0361,\n                           0x0483, 0x0486,\n                           0x0903, 0x0903,\n                           0x093E, 0x0940,\n                           0x0949, 0x094C,\n                           0x0982, 0x0983,\n                           0x09BE, 0x09C0,\n                           0x09C7, 0x09CC,\n                           0x09D7, 0x09D7,\n                           0x0A3E, 0x0A40,\n                           0x0A83, 0x0A83,\n                           0x0ABE, 0x0AC0,\n                           0x0AC9, 0x0ACC,\n                           0x0B02, 0x0B03,\n                           0x0B3E, 0x0B3E,\n                           0x0B40, 0x0B40,\n                           0x0B47, 0x0B4C,\n                           0x0B57, 0x0B57,\n                           0x0B83, 0x0B83,\n                           0x0BBE, 0x0BBF,\n                           0x0BC1, 0x0BCC,\n                           0x0BD7, 0x0BD7,\n                           0x0C01, 0x0C03,\n                           0x0C41, 0x0C44,\n                           0x0C82, 0x0C83,\n                           0x0CBE, 0x0CBE,\n                           0x0CC0, 0x0CC4,\n                           0x0CC7, 0x0CCB,\n                           0x0CD5, 0x0CD6,\n                           0x0D02, 0x0D03,\n                           0x0D3E, 0x0D40,\n                           0x0D46, 0x0D4C,\n                           0x0D57, 0x0D57,\n                           0x0F7F, 0x0F7F,\n                           0x20D0, 0x20E1,\n                           0x3099, 0x309A,\n                           0xFE20, 0xFE23,\n                           0xffff, 0xffff, };\n\n   const boost::uint_least16_t* p = combining_ranges + 1;\n   while (*p < c) p += 2;\n   --p;\n   if ((c >= *p) && (c <= *(p + 1)))\n      return true;\n   return false;\n}\n\ntemplate <class charT>\ninline bool is_combining(charT c)\n{\n   return (c <= static_cast<charT>(0)) ? false : ((c >= static_cast<charT>((std::numeric_limits<uint_least16_t>::max)())) ? false : is_combining_implementation(static_cast<unsigned short>(c)));\n}\ntemplate <>\ninline bool is_combining<char>(char)\n{\n   return false;\n}\ntemplate <>\ninline bool is_combining<signed char>(signed char)\n{\n   return false;\n}\ntemplate <>\ninline bool is_combining<unsigned char>(unsigned char)\n{\n   return false;\n}\n#if !defined(__hpux) && !defined(__WINSCW__) // can't use WCHAR_MAX/MIN in pp-directives\n#ifdef _MSC_VER\ntemplate<>\ninline bool is_combining<wchar_t>(wchar_t c)\n{\n   return is_combining_implementation(static_cast<unsigned short>(c));\n}\n#elif !defined(__DECCXX) && !defined(__osf__) && !defined(__OSF__) && defined(WCHAR_MIN) && (WCHAR_MIN == 0) && !defined(BOOST_NO_INTRINSIC_WCHAR_T)\n#if defined(WCHAR_MAX) && (WCHAR_MAX <= USHRT_MAX)\ntemplate<>\ninline bool is_combining<wchar_t>(wchar_t c)\n{\n   return is_combining_implementation(static_cast<unsigned short>(c));\n}\n#else\ntemplate<>\ninline bool is_combining<wchar_t>(wchar_t c)\n{\n   return (c >= (std::numeric_limits<uint_least16_t>::max)()) ? false : is_combining_implementation(static_cast<unsigned short>(c));\n}\n#endif\n#endif\n#endif\n\n//\n// is a charT c a line separator?\n//\ntemplate <class charT>\ninline bool is_separator(charT c)\n{\n   return BOOST_REGEX_MAKE_BOOL(\n      (c == static_cast<charT>('\\n'))\n      || (c == static_cast<charT>('\\r'))\n      || (c == static_cast<charT>('\\f'))\n      || (static_cast<boost::uint16_t>(c) == 0x2028u)\n      || (static_cast<boost::uint16_t>(c) == 0x2029u)\n      || (static_cast<boost::uint16_t>(c) == 0x85u));\n}\ntemplate <>\ninline bool is_separator<char>(char c)\n{\n   return BOOST_REGEX_MAKE_BOOL((c == '\\n') || (c == '\\r') || (c == '\\f'));\n}\n\n//\n// get a default collating element:\n//\ninline std::string BOOST_REGEX_CALL lookup_default_collate_name(const std::string& name)\n{\n   //\n   // these are the POSIX collating names:\n   //\n   static const char* def_coll_names[] = {\n   \"NUL\", \"SOH\", \"STX\", \"ETX\", \"EOT\", \"ENQ\", \"ACK\", \"alert\", \"backspace\", \"tab\", \"newline\",\n   \"vertical-tab\", \"form-feed\", \"carriage-return\", \"SO\", \"SI\", \"DLE\", \"DC1\", \"DC2\", \"DC3\", \"DC4\", \"NAK\",\n   \"SYN\", \"ETB\", \"CAN\", \"EM\", \"SUB\", \"ESC\", \"IS4\", \"IS3\", \"IS2\", \"IS1\", \"space\", \"exclamation-mark\",\n   \"quotation-mark\", \"number-sign\", \"dollar-sign\", \"percent-sign\", \"ampersand\", \"apostrophe\",\n   \"left-parenthesis\", \"right-parenthesis\", \"asterisk\", \"plus-sign\", \"comma\", \"hyphen\",\n   \"period\", \"slash\", \"zero\", \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\",\n   \"colon\", \"semicolon\", \"less-than-sign\", \"equals-sign\", \"greater-than-sign\",\n   \"question-mark\", \"commercial-at\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\",\n   \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\", \"left-square-bracket\", \"backslash\",\n   \"right-square-bracket\", \"circumflex\", \"underscore\", \"grave-accent\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\",\n   \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"left-curly-bracket\",\n   \"vertical-line\", \"right-curly-bracket\", \"tilde\", \"DEL\", \"\",\n   };\n\n   // these multi-character collating elements\n   // should keep most Western-European locales\n   // happy - we should really localise these a\n   // little more - but this will have to do for\n   // now:\n\n   static const char* def_multi_coll[] = {\n      \"ae\",\n      \"Ae\",\n      \"AE\",\n      \"ch\",\n      \"Ch\",\n      \"CH\",\n      \"ll\",\n      \"Ll\",\n      \"LL\",\n      \"ss\",\n      \"Ss\",\n      \"SS\",\n      \"nj\",\n      \"Nj\",\n      \"NJ\",\n      \"dz\",\n      \"Dz\",\n      \"DZ\",\n      \"lj\",\n      \"Lj\",\n      \"LJ\",\n      \"\",\n   };\n\n   unsigned int i = 0;\n   while (*def_coll_names[i])\n   {\n      if (def_coll_names[i] == name)\n      {\n         return std::string(1, char(i));\n      }\n      ++i;\n   }\n   i = 0;\n   while (*def_multi_coll[i])\n   {\n      if (def_multi_coll[i] == name)\n      {\n         return def_multi_coll[i];\n      }\n      ++i;\n   }\n   return std::string();\n}\n\n//\n// get the state_id of a character classification, the individual\n// traits classes then transform that state_id into a bitmask:\n//\ntemplate <class charT>\nstruct character_pointer_range\n{\n   const charT* p1;\n   const charT* p2;\n\n   bool operator < (const character_pointer_range& r)const\n   {\n      return std::lexicographical_compare(p1, p2, r.p1, r.p2);\n   }\n   bool operator == (const character_pointer_range& r)const\n   {\n      // Not only do we check that the ranges are of equal size before\n      // calling std::equal, but there is no other algorithm available:\n      // not even a non-standard MS one.  So forward to unchecked_equal\n      // in the MS case.\n      return ((p2 - p1) == (r.p2 - r.p1)) && BOOST_REGEX_DETAIL_NS::equal(p1, p2, r.p1);\n   }\n};\ntemplate <class charT>\nint get_default_class_id(const charT* p1, const charT* p2)\n{\n   static const charT data[73] = {\n      'a', 'l', 'n', 'u', 'm',\n      'a', 'l', 'p', 'h', 'a',\n      'b', 'l', 'a', 'n', 'k',\n      'c', 'n', 't', 'r', 'l',\n      'd', 'i', 'g', 'i', 't',\n      'g', 'r', 'a', 'p', 'h',\n      'l', 'o', 'w', 'e', 'r',\n      'p', 'r', 'i', 'n', 't',\n      'p', 'u', 'n', 'c', 't',\n      's', 'p', 'a', 'c', 'e',\n      'u', 'n', 'i', 'c', 'o', 'd', 'e',\n      'u', 'p', 'p', 'e', 'r',\n      'v',\n      'w', 'o', 'r', 'd',\n      'x', 'd', 'i', 'g', 'i', 't',\n   };\n\n   static const character_pointer_range<charT> ranges[21] =\n   {\n      {data+0, data+5,}, // alnum\n      {data+5, data+10,}, // alpha\n      {data+10, data+15,}, // blank\n      {data+15, data+20,}, // cntrl\n      {data+20, data+21,}, // d\n      {data+20, data+25,}, // digit\n      {data+25, data+30,}, // graph\n      {data+29, data+30,}, // h\n      {data+30, data+31,}, // l\n      {data+30, data+35,}, // lower\n      {data+35, data+40,}, // print\n      {data+40, data+45,}, // punct\n      {data+45, data+46,}, // s\n      {data+45, data+50,}, // space\n      {data+57, data+58,}, // u\n      {data+50, data+57,}, // unicode\n      {data+57, data+62,}, // upper\n      {data+62, data+63,}, // v\n      {data+63, data+64,}, // w\n      {data+63, data+67,}, // word\n      {data+67, data+73,}, // xdigit\n   };\n   const character_pointer_range<charT>* ranges_begin = ranges;\n   const character_pointer_range<charT>* ranges_end = ranges + (sizeof(ranges)/sizeof(ranges[0]));\n\n   character_pointer_range<charT> t = { p1, p2, };\n   const character_pointer_range<charT>* p = std::lower_bound(ranges_begin, ranges_end, t);\n   if((p != ranges_end) && (t == *p))\n      return static_cast<int>(p - ranges);\n   return -1;\n}\n\n//\n// helper functions:\n//\ntemplate <class charT>\nstd::ptrdiff_t global_length(const charT* p)\n{\n   std::ptrdiff_t n = 0;\n   while(*p)\n   {\n      ++p;\n      ++n;\n   }\n   return n;\n}\ntemplate<>\ninline std::ptrdiff_t global_length<char>(const char* p)\n{\n   return (std::strlen)(p);\n}\n#ifndef BOOST_NO_WREGEX\ntemplate<>\ninline std::ptrdiff_t global_length<wchar_t>(const wchar_t* p)\n{\n   return (std::ptrdiff_t)(std::wcslen)(p);\n}\n#endif\ntemplate <class charT>\ninline charT BOOST_REGEX_CALL global_lower(charT c)\n{\n   return c;\n}\ntemplate <class charT>\ninline charT BOOST_REGEX_CALL global_upper(charT c)\n{\n   return c;\n}\n\ninline char BOOST_REGEX_CALL do_global_lower(char c)\n{\n   return static_cast<char>((std::tolower)((unsigned char)c));\n}\n\ninline char BOOST_REGEX_CALL do_global_upper(char c)\n{\n   return static_cast<char>((std::toupper)((unsigned char)c));\n}\n#ifndef BOOST_NO_WREGEX\ninline wchar_t BOOST_REGEX_CALL do_global_lower(wchar_t c)\n{\n   return (std::towlower)(c);\n}\n\ninline wchar_t BOOST_REGEX_CALL do_global_upper(wchar_t c)\n{\n   return (std::towupper)(c);\n}\n#endif\n//\n// This sucks: declare template specialisations of global_lower/global_upper\n// that just forward to the non-template implementation functions.  We do\n// this because there is one compiler (Compaq Tru64 C++) that doesn't seem\n// to differentiate between templates and non-template overloads....\n// what's more, the primary template, plus all overloads have to be\n// defined in the same translation unit (if one is inline they all must be)\n// otherwise the \"local template instantiation\" compiler option can pick\n// the wrong instantiation when linking:\n//\ntemplate<> inline char BOOST_REGEX_CALL global_lower<char>(char c) { return do_global_lower(c); }\ntemplate<> inline char BOOST_REGEX_CALL global_upper<char>(char c) { return do_global_upper(c); }\n#ifndef BOOST_NO_WREGEX\ntemplate<> inline wchar_t BOOST_REGEX_CALL global_lower<wchar_t>(wchar_t c) { return do_global_lower(c); }\ntemplate<> inline wchar_t BOOST_REGEX_CALL global_upper<wchar_t>(wchar_t c) { return do_global_upper(c); }\n#endif\n\ntemplate <class charT>\nint global_value(charT c)\n{\n   static const charT zero = '0';\n   static const charT nine = '9';\n   static const charT a = 'a';\n   static const charT f = 'f';\n   static const charT A = 'A';\n   static const charT F = 'F';\n\n   if(c > f) return -1;\n   if(c >= a) return 10 + (c - a);\n   if(c > F) return -1;\n   if(c >= A) return 10 + (c - A);\n   if(c > nine) return -1;\n   if(c >= zero) return c - zero;\n   return -1;\n}\ntemplate <class charT, class traits>\nboost::intmax_t global_toi(const charT*& p1, const charT* p2, int radix, const traits& t)\n{\n   (void)t; // warning suppression\n   boost::intmax_t limit = (std::numeric_limits<boost::intmax_t>::max)() / radix;\n   boost::intmax_t next_value = t.value(*p1, radix);\n   if((p1 == p2) || (next_value < 0) || (next_value >= radix))\n      return -1;\n   boost::intmax_t result = 0;\n   while(p1 != p2)\n   {\n      next_value = t.value(*p1, radix);\n      if((next_value < 0) || (next_value >= radix))\n         break;\n      result *= radix;\n      result += next_value;\n      ++p1;\n      if (result > limit)\n         return -1;\n   }\n   return result;\n}\n\ntemplate <class charT>\ninline typename boost::enable_if_c<(sizeof(charT) > 1), const charT*>::type get_escape_R_string()\n{\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#  pragma warning(disable:4309 4245)\n#endif\n   static const charT e1[] = { '(', '?', '-', 'x', ':', '(', '?', '>', '\\x0D', '\\x0A', '?',\n      '|', '[', '\\x0A', '\\x0B', '\\x0C', static_cast<charT>(0x85), static_cast<charT>(0x2028),\n      static_cast<charT>(0x2029), ']', ')', ')', '\\0' };\n   static const charT e2[] = { '(', '?', '-', 'x', ':', '(', '?', '>', '\\x0D', '\\x0A', '?',\n      '|', '[', '\\x0A', '\\x0B', '\\x0C', static_cast<charT>(0x85), ']', ')', ')', '\\0' };\n\n   charT c = static_cast<charT>(0x2029u);\n   bool b = (static_cast<unsigned>(c) == 0x2029u);\n\n   return (b ? e1 : e2);\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n}\n\ntemplate <class charT>\ninline typename boost::disable_if_c<(sizeof(charT) > 1), const charT*>::type get_escape_R_string()\n{\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#  pragma warning(disable:4309)\n#endif\n   static const charT e2[] = { '(', '?', '-', 'x', ':', '(', '?', '>', '\\x0D', '\\x0A', '?',\n      '|', '[', '\\x0A', '\\x0B', '\\x0C', '\\x85', ']', ')', ')', '\\0' };\n   return e2;\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n}\n\n} // BOOST_REGEX_DETAIL_NS\n} // boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/regex_workaround.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2005\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_workarounds.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares Misc workarounds.\n  */\n\n#ifndef BOOST_REGEX_WORKAROUND_HPP\n#define BOOST_REGEX_WORKAROUND_HPP\n\n#include <boost/config.hpp>\n#include <new>\n#include <cstring>\n#include <cstdlib>\n#include <cstddef>\n#include <cassert>\n#include <cstdio>\n#include <climits>\n#include <string>\n#include <stdexcept>\n#include <iterator>\n#include <algorithm>\n#include <iosfwd>\n#include <vector>\n#include <set>\n#include <map>\n#include <boost/limits.hpp>\n#include <boost/assert.hpp>\n#include <boost/cstdint.hpp>\n#include <boost/throw_exception.hpp>\n#include <boost/scoped_ptr.hpp>\n#include <boost/scoped_array.hpp>\n#include <boost/shared_ptr.hpp>\n#include <boost/mpl/bool_fwd.hpp>\n#include <boost/regex/config.hpp>\n#ifndef BOOST_NO_STD_LOCALE\n#   include <locale>\n#endif\n\n#if defined(BOOST_NO_STDC_NAMESPACE)\nnamespace std{\n   using ::sprintf; using ::strcpy; using ::strcat; using ::strlen;\n}\n#endif\n\nnamespace boost{ namespace BOOST_REGEX_DETAIL_NS{\n#ifdef BOOST_NO_STD_DISTANCE\ntemplate <class T>\nstd::ptrdiff_t distance(const T& x, const T& y)\n{ return y - x; }\n#else\nusing std::distance;\n#endif\n}}\n\n\n#ifdef BOOST_REGEX_NO_BOOL\n#  define BOOST_REGEX_MAKE_BOOL(x) static_cast<bool>((x) ? true : false)\n#else\n#  define BOOST_REGEX_MAKE_BOOL(x) static_cast<bool>(x)\n#endif\n\n/*****************************************************************************\n *\n *  Fix broken namespace support:\n *\n ****************************************************************************/\n\n#if defined(BOOST_NO_STDC_NAMESPACE) && defined(__cplusplus)\n\nnamespace std{\n   using ::ptrdiff_t;\n   using ::size_t;\n   using ::abs;\n   using ::memset;\n   using ::memcpy;\n}\n\n#endif\n\n/*****************************************************************************\n *\n *  helper functions pointer_construct/pointer_destroy:\n *\n ****************************************************************************/\n\n#ifdef __cplusplus\nnamespace boost{ namespace BOOST_REGEX_DETAIL_NS{\n\n#ifdef BOOST_MSVC\n#pragma warning (push)\n#pragma warning (disable : 4100)\n#endif\n\ntemplate <class T>\ninline void pointer_destroy(T* p)\n{ p->~T(); (void)p; }\n\n#ifdef BOOST_MSVC\n#pragma warning (pop)\n#endif\n\ntemplate <class T>\ninline void pointer_construct(T* p, const T& t)\n{ new (p) T(t); }\n\n}} // namespaces\n#endif\n\n/*****************************************************************************\n *\n *  helper function copy:\n *\n ****************************************************************************/\n\n#ifdef __cplusplus\nnamespace boost{ namespace BOOST_REGEX_DETAIL_NS{\n#if BOOST_WORKAROUND(BOOST_MSVC,>=1400) && BOOST_WORKAROUND(BOOST_MSVC, <1600) && defined(_CPPLIB_VER) && defined(BOOST_DINKUMWARE_STDLIB) && !(defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION))\n   //\n   // MSVC 8 will either emit warnings or else refuse to compile\n   // code that makes perfectly legitimate use of std::copy, when\n   // the OutputIterator type is a user-defined class (apparently all user \n   // defined iterators are \"unsafe\").  This code works around that:\n   //\n   template<class InputIterator, class OutputIterator>\n   inline OutputIterator copy(\n      InputIterator first, \n      InputIterator last, \n      OutputIterator dest\n   )\n   {\n      return stdext::unchecked_copy(first, last, dest);\n   }\n   template<class InputIterator1, class InputIterator2>\n   inline bool equal(\n      InputIterator1 first, \n      InputIterator1 last, \n      InputIterator2 with\n   )\n   {\n      return stdext::unchecked_equal(first, last, with);\n   }\n#elif BOOST_WORKAROUND(BOOST_MSVC, > 1500)\n   //\n   // MSVC 10 will either emit warnings or else refuse to compile\n   // code that makes perfectly legitimate use of std::copy, when\n   // the OutputIterator type is a user-defined class (apparently all user \n   // defined iterators are \"unsafe\").  What's more Microsoft have removed their\n   // non-standard \"unchecked\" versions, even though their still in the MS\n   // documentation!! Work around this as best we can: \n   //\n   template<class InputIterator, class OutputIterator>\n   inline OutputIterator copy(\n      InputIterator first, \n      InputIterator last, \n      OutputIterator dest\n   )\n   {\n      while(first != last)\n         *dest++ = *first++;\n      return dest;\n   }\n   template<class InputIterator1, class InputIterator2>\n   inline bool equal(\n      InputIterator1 first, \n      InputIterator1 last, \n      InputIterator2 with\n   )\n   {\n      while(first != last)\n         if(*first++ != *with++) return false;\n      return true;\n   }\n#else \n   using std::copy; \n   using std::equal; \n#endif \n#if BOOST_WORKAROUND(BOOST_MSVC,>=1400) && defined(__STDC_WANT_SECURE_LIB__) && __STDC_WANT_SECURE_LIB__ \n\n   // use safe versions of strcpy etc:\n   using ::strcpy_s;\n   using ::strcat_s;\n#else\n   inline std::size_t strcpy_s(\n      char *strDestination,\n      std::size_t sizeInBytes,\n      const char *strSource \n   )\n   {\n     std::size_t lenSourceWithNull = std::strlen(strSource) + 1;\n     if (lenSourceWithNull > sizeInBytes)\n         return 1;\n     std::memcpy(strDestination, strSource, lenSourceWithNull);\n      return 0;\n   }\n   inline std::size_t strcat_s(\n      char *strDestination,\n      std::size_t sizeInBytes,\n      const char *strSource \n   )\n   {\n     std::size_t lenSourceWithNull = std::strlen(strSource) + 1;\n     std::size_t lenDestination = std::strlen(strDestination);\n     if (lenSourceWithNull + lenDestination > sizeInBytes)\n         return 1;\n     std::memcpy(strDestination + lenDestination, strSource, lenSourceWithNull);\n      return 0;\n   }\n\n#endif\n\n   inline void overflow_error_if_not_zero(std::size_t i)\n   {\n      if(i)\n      {\n         std::overflow_error e(\"String buffer too small\");\n         boost::throw_exception(e);\n      }\n   }\n\n}} // namespaces\n\n#endif // __cplusplus\n\n#endif // include guard\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/states.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         states.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares internal state machine structures.\n  */\n\n#ifndef BOOST_REGEX_V4_STATES_HPP\n#define BOOST_REGEX_V4_STATES_HPP\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\n/*** mask_type *******************************************************\nWhenever we have a choice of two alternatives, we use an array of bytes\nto indicate which of the two alternatives it is possible to take for any\ngiven input character.  If mask_take is set, then we can take the next \nstate, and if mask_skip is set then we can take the alternative.\n***********************************************************************/\nenum mask_type\n{\n   mask_take = 1,\n   mask_skip = 2,\n   mask_init = 4,\n   mask_any = mask_skip | mask_take,\n   mask_all = mask_any\n};\n\n/*** helpers **********************************************************\nThese helpers let us use function overload resolution to detect whether\nwe have narrow or wide character strings:\n***********************************************************************/\nstruct _narrow_type{};\nstruct _wide_type{};\ntemplate <class charT> struct is_byte;\ntemplate<>             struct is_byte<char>         { typedef _narrow_type width_type; };\ntemplate<>             struct is_byte<unsigned char>{ typedef _narrow_type width_type; };\ntemplate<>             struct is_byte<signed char>  { typedef _narrow_type width_type; };\ntemplate <class charT> struct is_byte               { typedef _wide_type width_type; };\n\n/*** enum syntax_element_type ******************************************\nEvery record in the state machine falls into one of the following types:\n***********************************************************************/\nenum syntax_element_type\n{\n   // start of a marked sub-expression, or perl-style (?...) extension\n   syntax_element_startmark = 0,\n   // end of a marked sub-expression, or perl-style (?...) extension\n   syntax_element_endmark = syntax_element_startmark + 1,\n   // any sequence of literal characters\n   syntax_element_literal = syntax_element_endmark + 1,\n   // start of line assertion: ^\n   syntax_element_start_line = syntax_element_literal + 1,\n   // end of line assertion $\n   syntax_element_end_line = syntax_element_start_line + 1,\n   // match any character: .\n   syntax_element_wild = syntax_element_end_line + 1,\n   // end of expression: we have a match when we get here\n   syntax_element_match = syntax_element_wild + 1,\n   // perl style word boundary: \\b\n   syntax_element_word_boundary = syntax_element_match + 1,\n   // perl style within word boundary: \\B\n   syntax_element_within_word = syntax_element_word_boundary + 1,\n   // start of word assertion: \\<\n   syntax_element_word_start = syntax_element_within_word + 1,\n   // end of word assertion: \\>\n   syntax_element_word_end = syntax_element_word_start + 1,\n   // start of buffer assertion: \\`\n   syntax_element_buffer_start = syntax_element_word_end + 1,\n   // end of buffer assertion: \\'\n   syntax_element_buffer_end = syntax_element_buffer_start + 1,\n   // backreference to previously matched sub-expression\n   syntax_element_backref = syntax_element_buffer_end + 1,\n   // either a wide character set [..] or one with multicharacter collating elements:\n   syntax_element_long_set = syntax_element_backref + 1,\n   // narrow character set: [...]\n   syntax_element_set = syntax_element_long_set + 1,\n   // jump to a new state in the machine:\n   syntax_element_jump = syntax_element_set + 1,\n   // choose between two production states:\n   syntax_element_alt = syntax_element_jump + 1,\n   // a repeat\n   syntax_element_rep = syntax_element_alt + 1,\n   // match a combining character sequence\n   syntax_element_combining = syntax_element_rep + 1,\n   // perl style soft buffer end: \\z\n   syntax_element_soft_buffer_end = syntax_element_combining + 1,\n   // perl style continuation: \\G\n   syntax_element_restart_continue = syntax_element_soft_buffer_end + 1,\n   // single character repeats:\n   syntax_element_dot_rep = syntax_element_restart_continue + 1,\n   syntax_element_char_rep = syntax_element_dot_rep + 1,\n   syntax_element_short_set_rep = syntax_element_char_rep + 1,\n   syntax_element_long_set_rep = syntax_element_short_set_rep + 1,\n   // a backstep for lookbehind repeats:\n   syntax_element_backstep = syntax_element_long_set_rep + 1,\n   // an assertion that a mark was matched:\n   syntax_element_assert_backref = syntax_element_backstep + 1,\n   syntax_element_toggle_case = syntax_element_assert_backref + 1,\n   // a recursive expression:\n   syntax_element_recurse = syntax_element_toggle_case + 1,\n   // Verbs:\n   syntax_element_fail = syntax_element_recurse + 1,\n   syntax_element_accept = syntax_element_fail + 1,\n   syntax_element_commit = syntax_element_accept + 1,\n   syntax_element_then = syntax_element_commit + 1\n};\n\n#ifdef BOOST_REGEX_DEBUG\n// dwa 09/26/00 - This is needed to suppress warnings about an ambiguous conversion\nstd::ostream& operator<<(std::ostream&, syntax_element_type);\n#endif\n\nstruct re_syntax_base;\n\n/*** union offset_type ************************************************\nPoints to another state in the machine.  During machine construction\nwe use integral offsets, but these are converted to pointers before\nexecution of the machine.\n***********************************************************************/\nunion offset_type\n{\n   re_syntax_base*   p;\n   std::ptrdiff_t    i;\n};\n\n/*** struct re_syntax_base ********************************************\nBase class for all states in the machine.\n***********************************************************************/\nstruct re_syntax_base\n{\n   syntax_element_type   type;         // what kind of state this is\n   offset_type           next;         // next state in the machine\n};\n\n/*** struct re_brace **************************************************\nA marked parenthesis.\n***********************************************************************/\nstruct re_brace : public re_syntax_base\n{\n   // The index to match, can be zero (don't mark the sub-expression)\n   // or negative (for perl style (?...) extensions):\n   int index;\n   bool icase;\n};\n\n/*** struct re_dot **************************************************\nMatch anything.\n***********************************************************************/\nenum\n{\n   dont_care = 1,\n   force_not_newline = 0,\n   force_newline = 2,\n\n   test_not_newline = 2,\n   test_newline = 3\n};\nstruct re_dot : public re_syntax_base\n{\n   unsigned char mask;\n};\n\n/*** struct re_literal ************************************************\nA string of literals, following this structure will be an \narray of characters: charT[length]\n***********************************************************************/\nstruct re_literal : public re_syntax_base\n{\n   unsigned int length;\n};\n\n/*** struct re_case ************************************************\nIndicates whether we are moving to a case insensive block or not\n***********************************************************************/\nstruct re_case : public re_syntax_base\n{\n   bool icase;\n};\n\n/*** struct re_set_long ***********************************************\nA wide character set of characters, following this structure will be\nan array of type charT:\nFirst csingles null-terminated strings\nThen 2 * cranges NULL terminated strings\nThen cequivalents NULL terminated strings\n***********************************************************************/\ntemplate <class mask_type>\nstruct re_set_long : public re_syntax_base\n{\n   unsigned int            csingles, cranges, cequivalents;\n   mask_type               cclasses;\n   mask_type               cnclasses;\n   bool                    isnot;\n   bool                    singleton;\n};\n\n/*** struct re_set ****************************************************\nA set of narrow-characters, matches any of _map which is none-zero\n***********************************************************************/\nstruct re_set : public re_syntax_base\n{\n   unsigned char _map[1 << CHAR_BIT];\n};\n\n/*** struct re_jump ***************************************************\nJump to a new location in the machine (not next).\n***********************************************************************/\nstruct re_jump : public re_syntax_base\n{\n   offset_type     alt;                 // location to jump to\n};\n\n/*** struct re_alt ***************************************************\nJump to a new location in the machine (possibly next).\n***********************************************************************/\nstruct re_alt : public re_jump\n{\n   unsigned char   _map[1 << CHAR_BIT]; // which characters can take the jump\n   unsigned int    can_be_null;         // true if we match a NULL string\n};\n\n/*** struct re_repeat *************************************************\nRepeat a section of the machine\n***********************************************************************/\nstruct re_repeat : public re_alt\n{\n   std::size_t   min, max;  // min and max allowable repeats\n   int           state_id;        // Unique identifier for this repeat\n   bool          leading;   // True if this repeat is at the start of the machine (lets us optimize some searches)\n   bool          greedy;    // True if this is a greedy repeat\n};\n\n/*** struct re_recurse ************************************************\nRecurse to a particular subexpression.\n**********************************************************************/\nstruct re_recurse : public re_jump\n{\n   int state_id;             // identifier of first nested repeat within the recursion.\n};\n\n/*** struct re_commit *************************************************\nUsed for the PRUNE, SKIP and COMMIT verbs which basically differ only in what happens\nif no match is found and we start searching forward.\n**********************************************************************/\nenum commit_type\n{\n   commit_prune,\n   commit_skip,\n   commit_commit\n};\nstruct re_commit : public re_syntax_base\n{\n   commit_type action;\n};\n\n/*** enum re_jump_size_type *******************************************\nProvides compiled size of re_jump structure (allowing for trailing alignment).\nWe provide this so we know how manybytes to insert when constructing the machine\n(The value of padding_mask is defined in regex_raw_buffer.hpp).\n***********************************************************************/\nenum re_jump_size_type\n{\n   re_jump_size = (sizeof(re_jump) + padding_mask) & ~(padding_mask),\n   re_repeater_size = (sizeof(re_repeat) + padding_mask) & ~(padding_mask),\n   re_alt_size = (sizeof(re_alt) + padding_mask) & ~(padding_mask)\n};\n\n/*** proc re_is_set_member *********************************************\nForward declaration: we'll need this one later...\n***********************************************************************/\n\ntemplate<class charT, class traits>\nstruct regex_data;\n\ntemplate <class iterator, class charT, class traits_type, class char_classT>\niterator BOOST_REGEX_CALL re_is_set_member(iterator next, \n                          iterator last, \n                          const re_set_long<char_classT>* set_, \n                          const regex_data<charT, traits_type>& e, bool icase);\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/sub_match.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         sub_match.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class sub_match.\n  */\n\n#ifndef BOOST_REGEX_V4_SUB_MATCH_HPP\n#define BOOST_REGEX_V4_SUB_MATCH_HPP\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace boost{\n\ntemplate <class BidiIterator>\nstruct sub_match : public std::pair<BidiIterator, BidiIterator>\n{\n   typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<BidiIterator>::value_type       value_type;\n#if defined(BOOST_NO_STD_ITERATOR_TRAITS)\n   typedef          std::ptrdiff_t                                                   difference_type;\n#else\n   typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<BidiIterator>::difference_type  difference_type;\n#endif\n   typedef          BidiIterator                                                     iterator_type;\n   typedef          BidiIterator                                                     iterator;\n   typedef          BidiIterator                                                     const_iterator;\n\n   bool matched;\n\n   sub_match() : std::pair<BidiIterator, BidiIterator>(), matched(false) {}\n   sub_match(BidiIterator i) : std::pair<BidiIterator, BidiIterator>(i, i), matched(false) {}\n#if !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)\\\n               && !BOOST_WORKAROUND(BOOST_BORLANDC, <= 0x0551)\\\n               && !BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042))\n   template <class T, class A>\n   operator std::basic_string<value_type, T, A> ()const\n   {\n      return matched ? std::basic_string<value_type, T, A>(this->first, this->second) : std::basic_string<value_type, T, A>();\n   }\n#else\n   operator std::basic_string<value_type> ()const\n   {\n      return str();\n   }\n#endif\n   difference_type BOOST_REGEX_CALL length()const\n   {\n      difference_type n = matched ? ::boost::BOOST_REGEX_DETAIL_NS::distance((BidiIterator)this->first, (BidiIterator)this->second) : 0;\n      return n;\n   }\n   std::basic_string<value_type> str()const\n   {\n      std::basic_string<value_type> result;\n      if(matched)\n      {\n         std::size_t len = ::boost::BOOST_REGEX_DETAIL_NS::distance((BidiIterator)this->first, (BidiIterator)this->second);\n         result.reserve(len);\n         BidiIterator i = this->first;\n         while(i != this->second)\n         {\n            result.append(1, *i);\n            ++i;\n         }\n      }\n      return result;\n   }\n   int compare(const sub_match& s)const\n   {\n      if(matched != s.matched)\n         return static_cast<int>(matched) - static_cast<int>(s.matched);\n      return str().compare(s.str());\n   }\n   int compare(const std::basic_string<value_type>& s)const\n   {\n      return str().compare(s);\n   }\n   int compare(const value_type* p)const\n   {\n      return str().compare(p);\n   }\n\n   bool operator==(const sub_match& that)const\n   { return compare(that) == 0; }\n   bool BOOST_REGEX_CALL operator !=(const sub_match& that)const\n   { return compare(that) != 0; }\n   bool operator<(const sub_match& that)const\n   { return compare(that) < 0; }\n   bool operator>(const sub_match& that)const\n   { return compare(that) > 0; }\n   bool operator<=(const sub_match& that)const\n   { return compare(that) <= 0; }\n   bool operator>=(const sub_match& that)const\n   { return compare(that) >= 0; }\n\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   typedef std::vector<sub_match<BidiIterator> > capture_sequence_type;\n\n   const capture_sequence_type& captures()const\n   {\n      if(!m_captures) \n         m_captures.reset(new capture_sequence_type());\n      return *m_captures;\n   }\n   //\n   // Private implementation API: DO NOT USE!\n   //\n   capture_sequence_type& get_captures()const\n   {\n      if(!m_captures) \n         m_captures.reset(new capture_sequence_type());\n      return *m_captures;\n   }\n\nprivate:\n   mutable boost::scoped_ptr<capture_sequence_type> m_captures;\npublic:\n\n#endif\n   sub_match(const sub_match& that, bool \n#ifdef BOOST_REGEX_MATCH_EXTRA\n      deep_copy\n#endif\n      = true\n      ) \n      : std::pair<BidiIterator, BidiIterator>(that), \n        matched(that.matched) \n   {\n#ifdef BOOST_REGEX_MATCH_EXTRA\n      if(that.m_captures)\n         if(deep_copy)\n            m_captures.reset(new capture_sequence_type(*(that.m_captures)));\n#endif\n   }\n   sub_match& operator=(const sub_match& that)\n   {\n      this->first = that.first;\n      this->second = that.second;\n      matched = that.matched;\n#ifdef BOOST_REGEX_MATCH_EXTRA\n      if(that.m_captures)\n         get_captures() = *(that.m_captures);\n#endif\n      return *this;\n   }\n   //\n   // Make this type a range, for both Boost.Range, and C++11:\n   //\n   BidiIterator begin()const { return this->first; }\n   BidiIterator end()const { return this->second; }\n\n\n#ifdef BOOST_OLD_REGEX_H\n   //\n   // the following are deprecated, do not use!!\n   //\n   operator int()const;\n   operator unsigned int()const;\n   operator short()const\n   {\n      return (short)(int)(*this);\n   }\n   operator unsigned short()const\n   {\n      return (unsigned short)(unsigned int)(*this);\n   }\n#endif\n};\n\ntypedef sub_match<const char*> csub_match;\ntypedef sub_match<std::string::const_iterator> ssub_match;\n#ifndef BOOST_NO_WREGEX\ntypedef sub_match<const wchar_t*> wcsub_match;\ntypedef sub_match<std::wstring::const_iterator> wssub_match;\n#endif\n\n// comparison to std::basic_string<> part 1:\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator == (const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) == 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator != (const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) != 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator < (const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                 const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) < 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator <= (const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) <= 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator >= (const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) >= 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator > (const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                 const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) > 0; }\n// comparison to std::basic_string<> part 2:\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator == (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) == 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator != (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) != 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator < (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) < 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator > (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) > 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator <= (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) <= 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator >= (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) >= 0; }\n// comparison to const charT* part 1:\ntemplate <class RandomAccessIterator>\ninline bool operator == (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) == 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator != (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) != 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator > (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) > 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator < (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) < 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator >= (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) >= 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator <= (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) <= 0; }\n// comparison to const charT* part 2:\ntemplate <class RandomAccessIterator>\ninline bool operator == (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) == 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator != (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) != 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator < (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) > 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator > (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) < 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator <= (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) >= 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator >= (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) <= 0; }\n\n// comparison to const charT& part 1:\ntemplate <class RandomAccessIterator>\ninline bool operator == (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) == 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator != (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) != 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator > (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) > 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator < (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) < 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator >= (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) >= 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator <= (const sub_match<RandomAccessIterator>& m,\n                  typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) <= 0; }\n// comparison to const charT* part 2:\ntemplate <class RandomAccessIterator>\ninline bool operator == (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) == 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator != (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) != 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator < (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) > 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator > (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) < 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator <= (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) >= 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator >= (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) <= 0; }\n\n// addition operators:\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator> \noperator + (const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                  const sub_match<RandomAccessIterator>& m)\n{\n   std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator> result;\n   result.reserve(s.size() + m.length() + 1);\n   return result.append(s).append(m.first, m.second);\n}\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator> \noperator + (const sub_match<RandomAccessIterator>& m,\n            const std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{\n   std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type, traits, Allocator> result;\n   result.reserve(s.size() + m.length() + 1);\n   return result.append(m.first, m.second).append(s);\n}\n#if !(defined(__GNUC__) && defined(BOOST_NO_STD_LOCALE))\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> \noperator + (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{\n   std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> result;\n   result.reserve(std::char_traits<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type>::length(s) + m.length() + 1);\n   return result.append(s).append(m.first, m.second);\n}\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> \noperator + (const sub_match<RandomAccessIterator>& m,\n            typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const * s)\n{\n   std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> result;\n   result.reserve(std::char_traits<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type>::length(s) + m.length() + 1);\n   return result.append(m.first, m.second).append(s);\n}\n#else\n// worwaround versions:\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> \noperator + (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{\n   return s + m.str();\n}\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> \noperator + (const sub_match<RandomAccessIterator>& m,\n            typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const * s)\n{\n   return m.str() + s;\n}\n#endif\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> \noperator + (typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{\n   std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> result;\n   result.reserve(m.length() + 2);\n   return result.append(1, s).append(m.first, m.second);\n}\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> \noperator + (const sub_match<RandomAccessIterator>& m,\n            typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type const& s)\n{\n   std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> result;\n   result.reserve(m.length() + 2);\n   return result.append(m.first, m.second).append(1, s);\n}\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> \noperator + (const sub_match<RandomAccessIterator>& m1,\n            const sub_match<RandomAccessIterator>& m2)\n{\n   std::basic_string<typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<RandomAccessIterator>::value_type> result;\n   result.reserve(m1.length() + m2.length() + 1);\n   return result.append(m1.first, m1.second).append(m2.first, m2.second);\n}\n#ifndef BOOST_NO_STD_LOCALE\ntemplate <class charT, class traits, class RandomAccessIterator>\nstd::basic_ostream<charT, traits>&\n   operator << (std::basic_ostream<charT, traits>& os,\n                const sub_match<RandomAccessIterator>& s)\n{\n   return (os << s.str());\n}\n#else\ntemplate <class RandomAccessIterator>\nstd::ostream& operator << (std::ostream& os,\n                           const sub_match<RandomAccessIterator>& s)\n{\n   return (os << s.str());\n}\n#endif\n\n#ifdef BOOST_OLD_REGEX_H\nnamespace BOOST_REGEX_DETAIL_NS{\ntemplate <class BidiIterator, class charT>\nint do_toi(BidiIterator i, BidiIterator j, char c, int radix)\n{\n   std::string s(i, j);\n   char* p;\n   int result = std::strtol(s.c_str(), &p, radix);\n   if(*p)raise_regex_exception(\"Bad sub-expression\");\n   return result;\n}\n\n//\n// helper:\ntemplate <class I, class charT>\nint do_toi(I& i, I j, charT c)\n{\n   int result = 0;\n   while((i != j) && (isdigit(*i)))\n   {\n      result = result*10 + (*i - '0');\n      ++i;\n   }\n   return result;\n}\n}\n\n\ntemplate <class BidiIterator>\nsub_match<BidiIterator>::operator int()const\n{\n   BidiIterator i = first;\n   BidiIterator j = second;\n   if(i == j)raise_regex_exception(\"Bad sub-expression\");\n   int neg = 1;\n   if((i != j) && (*i == '-'))\n   {\n      neg = -1;\n      ++i;\n   }\n   neg *= BOOST_REGEX_DETAIL_NS::do_toi(i, j, *i);\n   if(i != j)raise_regex_exception(\"Bad sub-expression\");\n   return neg;\n}\ntemplate <class BidiIterator>\nsub_match<BidiIterator>::operator unsigned int()const\n{\n   BidiIterator i = first;\n   BidiIterator j = second;\n   if(i == j)\n      raise_regex_exception(\"Bad sub-expression\");\n   return BOOST_REGEX_DETAIL_NS::do_toi(i, j, *first);\n}\n#endif\n\n} // namespace boost\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/syntax_type.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         syntax_type.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression synatx type enumerator.\n  */\n\n#ifndef BOOST_REGEX_SYNTAX_TYPE_HPP\n#define BOOST_REGEX_SYNTAX_TYPE_HPP\n\nnamespace boost{\nnamespace regex_constants{\n\ntypedef unsigned char syntax_type;\n\n//\n// values chosen are binary compatible with previous version:\n//\nstatic const syntax_type syntax_char = 0;\nstatic const syntax_type syntax_open_mark = 1;\nstatic const syntax_type syntax_close_mark = 2;\nstatic const syntax_type syntax_dollar = 3;\nstatic const syntax_type syntax_caret = 4;\nstatic const syntax_type syntax_dot = 5;\nstatic const syntax_type syntax_star = 6;\nstatic const syntax_type syntax_plus = 7;\nstatic const syntax_type syntax_question = 8;\nstatic const syntax_type syntax_open_set = 9;\nstatic const syntax_type syntax_close_set = 10;\nstatic const syntax_type syntax_or = 11;\nstatic const syntax_type syntax_escape = 12;\nstatic const syntax_type syntax_dash = 14;\nstatic const syntax_type syntax_open_brace = 15;\nstatic const syntax_type syntax_close_brace = 16;\nstatic const syntax_type syntax_digit = 17;\nstatic const syntax_type syntax_comma = 27;\nstatic const syntax_type syntax_equal = 37;\nstatic const syntax_type syntax_colon = 36;\nstatic const syntax_type syntax_not = 53;\n\n// extensions:\n\nstatic const syntax_type syntax_hash = 13;\nstatic const syntax_type syntax_newline = 26;\n\n// escapes:\n\ntypedef syntax_type escape_syntax_type;\n\nstatic const escape_syntax_type escape_type_word_assert = 18;\nstatic const escape_syntax_type escape_type_not_word_assert = 19;\nstatic const escape_syntax_type escape_type_control_f = 29;\nstatic const escape_syntax_type escape_type_control_n = 30;\nstatic const escape_syntax_type escape_type_control_r = 31;\nstatic const escape_syntax_type escape_type_control_t = 32;\nstatic const escape_syntax_type escape_type_control_v = 33;\nstatic const escape_syntax_type escape_type_ascii_control = 35;\nstatic const escape_syntax_type escape_type_hex = 34;\nstatic const escape_syntax_type escape_type_unicode = 0; // not used\nstatic const escape_syntax_type escape_type_identity = 0; // not used\nstatic const escape_syntax_type escape_type_backref = syntax_digit;\nstatic const escape_syntax_type escape_type_decimal = syntax_digit; // not used\nstatic const escape_syntax_type escape_type_class = 22; \nstatic const escape_syntax_type escape_type_not_class = 23; \n\n// extensions:\n\nstatic const escape_syntax_type escape_type_left_word = 20;\nstatic const escape_syntax_type escape_type_right_word = 21;\nstatic const escape_syntax_type escape_type_start_buffer = 24;                 // for \\`\nstatic const escape_syntax_type escape_type_end_buffer = 25;                   // for \\'\nstatic const escape_syntax_type escape_type_control_a = 28;                    // for \\a\nstatic const escape_syntax_type escape_type_e = 38;                            // for \\e\nstatic const escape_syntax_type escape_type_E = 47;                            // for \\Q\\E\nstatic const escape_syntax_type escape_type_Q = 48;                            // for \\Q\\E\nstatic const escape_syntax_type escape_type_X = 49;                            // for \\X\nstatic const escape_syntax_type escape_type_C = 50;                            // for \\C\nstatic const escape_syntax_type escape_type_Z = 51;                            // for \\Z\nstatic const escape_syntax_type escape_type_G = 52;                            // for \\G\n\nstatic const escape_syntax_type escape_type_property = 54;                     // for \\p\nstatic const escape_syntax_type escape_type_not_property = 55;                 // for \\P\nstatic const escape_syntax_type escape_type_named_char = 56;                   // for \\N\nstatic const escape_syntax_type escape_type_extended_backref = 57;             // for \\g\nstatic const escape_syntax_type escape_type_reset_start_mark = 58;             // for \\K\nstatic const escape_syntax_type escape_type_line_ending = 59;                  // for \\R\n\nstatic const escape_syntax_type syntax_max = 60;\n\n}\n}\n\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/u32regex_iterator.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         u32regex_iterator.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides u32regex_iterator implementation.\n  */\n\n#ifndef BOOST_REGEX_V4_U32REGEX_ITERATOR_HPP\n#define BOOST_REGEX_V4_U32REGEX_ITERATOR_HPP\n\nnamespace boost{\n\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n\ntemplate <class BidirectionalIterator>\nclass u32regex_iterator_implementation \n{\n   typedef u32regex regex_type;\n\n   match_results<BidirectionalIterator> what;  // current match\n   BidirectionalIterator                base;  // start of sequence\n   BidirectionalIterator                end;   // end of sequence\n   const regex_type                     re;   // the expression\n   match_flag_type                      flags; // flags for matching\n\npublic:\n   u32regex_iterator_implementation(const regex_type* p, BidirectionalIterator last, match_flag_type f)\n      : base(), end(last), re(*p), flags(f){}\n   bool init(BidirectionalIterator first)\n   {\n      base = first;\n      return u32regex_search(first, end, what, re, flags, base);\n   }\n   bool compare(const u32regex_iterator_implementation& that)\n   {\n      if(this == &that) return true;\n      return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second);\n   }\n   const match_results<BidirectionalIterator>& get()\n   { return what; }\n   bool next()\n   {\n      //if(what.prefix().first != what[0].second)\n      //   flags |= match_prev_avail;\n      BidirectionalIterator next_start = what[0].second;\n      match_flag_type f(flags);\n      if(!what.length())\n         f |= regex_constants::match_not_initial_null;\n      //if(base != next_start)\n      //   f |= regex_constants::match_not_bob;\n      bool result = u32regex_search(next_start, end, what, re, f, base);\n      if(result)\n         what.set_base(base);\n      return result;\n   }\nprivate:\n   u32regex_iterator_implementation& operator=(const u32regex_iterator_implementation&);\n};\n\ntemplate <class BidirectionalIterator>\nclass u32regex_iterator \n{\nprivate:\n   typedef u32regex_iterator_implementation<BidirectionalIterator> impl;\n   typedef shared_ptr<impl> pimpl;\npublic:\n   typedef          u32regex                                                regex_type;\n   typedef          match_results<BidirectionalIterator>                    value_type;\n   typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<BidirectionalIterator>::difference_type \n                                                                            difference_type;\n   typedef          const value_type*                                       pointer;\n   typedef          const value_type&                                       reference; \n   typedef          std::forward_iterator_tag                               iterator_category;\n   \n   u32regex_iterator(){}\n   u32regex_iterator(BidirectionalIterator a, BidirectionalIterator b, \n                  const regex_type& re, \n                  match_flag_type m = match_default)\n                  : pdata(new impl(&re, b, m))\n   {\n      if(!pdata->init(a))\n      {\n         pdata.reset();\n      }\n   }\n   u32regex_iterator(const u32regex_iterator& that)\n      : pdata(that.pdata) {}\n   u32regex_iterator& operator=(const u32regex_iterator& that)\n   {\n      pdata = that.pdata;\n      return *this;\n   }\n   bool operator==(const u32regex_iterator& that)const\n   { \n      if((pdata.get() == 0) || (that.pdata.get() == 0))\n         return pdata.get() == that.pdata.get();\n      return pdata->compare(*(that.pdata.get())); \n   }\n   bool operator!=(const u32regex_iterator& that)const\n   { return !(*this == that); }\n   const value_type& operator*()const\n   { return pdata->get(); }\n   const value_type* operator->()const\n   { return &(pdata->get()); }\n   u32regex_iterator& operator++()\n   {\n      cow();\n      if(0 == pdata->next())\n      {\n         pdata.reset();\n      }\n      return *this;\n   }\n   u32regex_iterator operator++(int)\n   {\n      u32regex_iterator result(*this);\n      ++(*this);\n      return result;\n   }\nprivate:\n\n   pimpl pdata;\n\n   void cow()\n   {\n      // copy-on-write\n      if(pdata.get() && !pdata.unique())\n      {\n         pdata.reset(new impl(*(pdata.get())));\n      }\n   }\n};\n\ntypedef u32regex_iterator<const char*> utf8regex_iterator;\ntypedef u32regex_iterator<const UChar*> utf16regex_iterator;\ntypedef u32regex_iterator<const UChar32*> utf32regex_iterator;\n\ninline u32regex_iterator<const char*> make_u32regex_iterator(const char* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_iterator<const char*>(p, p+std::strlen(p), e, m);\n}\n#ifndef BOOST_NO_WREGEX\ninline u32regex_iterator<const wchar_t*> make_u32regex_iterator(const wchar_t* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_iterator<const wchar_t*>(p, p+std::wcslen(p), e, m);\n}\n#endif\n#if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T)\ninline u32regex_iterator<const UChar*> make_u32regex_iterator(const UChar* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_iterator<const UChar*>(p, p+u_strlen(p), e, m);\n}\n#endif\ntemplate <class charT, class Traits, class Alloc>\ninline u32regex_iterator<typename std::basic_string<charT, Traits, Alloc>::const_iterator> make_u32regex_iterator(const std::basic_string<charT, Traits, Alloc>& p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   typedef typename std::basic_string<charT, Traits, Alloc>::const_iterator iter_type;\n   return u32regex_iterator<iter_type>(p.begin(), p.end(), e, m);\n}\ninline u32regex_iterator<const UChar*> make_u32regex_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_iterator<const UChar*>(s.getBuffer(), s.getBuffer() + s.length(), e, m);\n}\n\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n\n} // namespace boost\n\n#endif // BOOST_REGEX_V4_REGEX_ITERATOR_HPP\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/u32regex_token_iterator.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         u32regex_token_iterator.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides u32regex_token_iterator implementation.\n  */\n\n#ifndef BOOST_REGEX_V4_U32REGEX_TOKEN_ITERATOR_HPP\n#define BOOST_REGEX_V4_U32REGEX_TOKEN_ITERATOR_HPP\n\n#if (BOOST_WORKAROUND(BOOST_BORLANDC, >= 0x560) && BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x570)))\\\n      || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003))\n//\n// Borland C++ Builder 6, and Visual C++ 6,\n// can't cope with the array template constructor\n// so we have a template member that will accept any type as \n// argument, and then assert that is really is an array:\n//\n#include <boost/static_assert.hpp>\n#include <boost/type_traits/is_array.hpp>\n#endif\n\nnamespace boost{\n\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#  pragma warning(push)\n#  pragma warning(disable:4700)\n#endif\n\ntemplate <class BidirectionalIterator>\nclass u32regex_token_iterator_implementation \n{\n   typedef u32regex                              regex_type;\n   typedef sub_match<BidirectionalIterator>      value_type;\n\n   match_results<BidirectionalIterator> what;   // current match\n   BidirectionalIterator                end;    // end of search area\n   BidirectionalIterator                base;   // start of search area\n   const regex_type                     re;     // the expression\n   match_flag_type                      flags;  // match flags\n   value_type                           result; // the current string result\n   int                                  N;      // the current sub-expression being enumerated\n   std::vector<int>                     subs;   // the sub-expressions to enumerate\n\npublic:\n   u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, int sub, match_flag_type f)\n      : end(last), re(*p), flags(f){ subs.push_back(sub); }\n   u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const std::vector<int>& v, match_flag_type f)\n      : end(last), re(*p), flags(f), subs(v){}\n#if (BOOST_WORKAROUND(__BORLANDC__, >= 0x560) && BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x570)))\\\n      || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) \\\n      || BOOST_WORKAROUND(__HP_aCC, < 60700)\n   template <class T>\n   u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const T& submatches, match_flag_type f)\n      : end(last), re(*p), flags(f)\n   {\n      // assert that T really is an array:\n      BOOST_STATIC_ASSERT(::boost::is_array<T>::value);\n      const std::size_t array_size = sizeof(T) / sizeof(submatches[0]);\n      for(std::size_t i = 0; i < array_size; ++i)\n      {\n         subs.push_back(submatches[i]);\n      }\n   }\n#else\n   template <std::size_t CN>\n   u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const int (&submatches)[CN], match_flag_type f)\n      : end(last), re(*p), flags(f)\n   {\n      for(std::size_t i = 0; i < CN; ++i)\n      {\n         subs.push_back(submatches[i]);\n      }\n   }\n#endif\n\n   bool init(BidirectionalIterator first)\n   {\n      base = first;\n      N = 0;\n      if(u32regex_search(first, end, what, re, flags, base) == true)\n      {\n         N = 0;\n         result = ((subs[N] == -1) ? what.prefix() : what[(int)subs[N]]);\n         return true;\n      }\n      else if((subs[N] == -1) && (first != end))\n      {\n         result.first = first;\n         result.second = end;\n         result.matched = (first != end);\n         N = -1;\n         return true;\n      }\n      return false;\n   }\n   bool compare(const u32regex_token_iterator_implementation& that)\n   {\n      if(this == &that) return true;\n      return (&re.get_data() == &that.re.get_data()) \n         && (end == that.end) \n         && (flags == that.flags) \n         && (N == that.N) \n         && (what[0].first == that.what[0].first) \n         && (what[0].second == that.what[0].second);\n   }\n   const value_type& get()\n   { return result; }\n   bool next()\n   {\n      if(N == -1)\n         return false;\n      if(N+1 < (int)subs.size())\n      {\n         ++N;\n         result =((subs[N] == -1) ? what.prefix() : what[subs[N]]);\n         return true;\n      }\n      //if(what.prefix().first != what[0].second)\n      //   flags |= match_prev_avail | regex_constants::match_not_bob;\n      BidirectionalIterator last_end(what[0].second);\n      if(u32regex_search(last_end, end, what, re, ((what[0].first == what[0].second) ? flags | regex_constants::match_not_initial_null : flags), base))\n      {\n         N =0;\n         result =((subs[N] == -1) ? what.prefix() : what[subs[N]]);\n         return true;\n      }\n      else if((last_end != end) && (subs[0] == -1))\n      {\n         N =-1;\n         result.first = last_end;\n         result.second = end;\n         result.matched = (last_end != end);\n         return true;\n      }\n      return false;\n   }\nprivate:\n   u32regex_token_iterator_implementation& operator=(const u32regex_token_iterator_implementation&);\n};\n\ntemplate <class BidirectionalIterator>\nclass u32regex_token_iterator \n{\nprivate:\n   typedef u32regex_token_iterator_implementation<BidirectionalIterator> impl;\n   typedef shared_ptr<impl> pimpl;\npublic:\n   typedef          u32regex                                                regex_type;\n   typedef          sub_match<BidirectionalIterator>                        value_type;\n   typedef typename BOOST_REGEX_DETAIL_NS::regex_iterator_traits<BidirectionalIterator>::difference_type \n                                                                            difference_type;\n   typedef          const value_type*                                       pointer;\n   typedef          const value_type&                                       reference; \n   typedef          std::forward_iterator_tag                               iterator_category;\n   \n   u32regex_token_iterator(){}\n   u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, \n                        int submatch = 0, match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatch, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n   u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, \n                        const std::vector<int>& submatches, match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatches, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n#if (BOOST_WORKAROUND(__BORLANDC__, >= 0x560) && BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x570)))\\\n      || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) \\\n      || BOOST_WORKAROUND(__HP_aCC, < 60700)\n   template <class T>\n   u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re,\n                        const T& submatches, match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatches, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n#else\n   template <std::size_t N>\n   u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re,\n                        const int (&submatches)[N], match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatches, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n#endif\n   u32regex_token_iterator(const u32regex_token_iterator& that)\n      : pdata(that.pdata) {}\n   u32regex_token_iterator& operator=(const u32regex_token_iterator& that)\n   {\n      pdata = that.pdata;\n      return *this;\n   }\n   bool operator==(const u32regex_token_iterator& that)const\n   { \n      if((pdata.get() == 0) || (that.pdata.get() == 0))\n         return pdata.get() == that.pdata.get();\n      return pdata->compare(*(that.pdata.get())); \n   }\n   bool operator!=(const u32regex_token_iterator& that)const\n   { return !(*this == that); }\n   const value_type& operator*()const\n   { return pdata->get(); }\n   const value_type* operator->()const\n   { return &(pdata->get()); }\n   u32regex_token_iterator& operator++()\n   {\n      cow();\n      if(0 == pdata->next())\n      {\n         pdata.reset();\n      }\n      return *this;\n   }\n   u32regex_token_iterator operator++(int)\n   {\n      u32regex_token_iterator result(*this);\n      ++(*this);\n      return result;\n   }\nprivate:\n\n   pimpl pdata;\n\n   void cow()\n   {\n      // copy-on-write\n      if(pdata.get() && !pdata.unique())\n      {\n         pdata.reset(new impl(*(pdata.get())));\n      }\n   }\n};\n\ntypedef u32regex_token_iterator<const char*> utf8regex_token_iterator;\ntypedef u32regex_token_iterator<const UChar*> utf16regex_token_iterator;\ntypedef u32regex_token_iterator<const UChar32*> utf32regex_token_iterator;\n\n// construction from an integral sub_match state_id:\ninline u32regex_token_iterator<const char*> make_u32regex_token_iterator(const char* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const char*>(p, p+std::strlen(p), e, submatch, m);\n}\n#ifndef BOOST_NO_WREGEX\ninline u32regex_token_iterator<const wchar_t*> make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const wchar_t*>(p, p+std::wcslen(p), e, submatch, m);\n}\n#endif\n#if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T)\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const UChar* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(p, p+u_strlen(p), e, submatch, m);\n}\n#endif\ntemplate <class charT, class Traits, class Alloc>\ninline u32regex_token_iterator<typename std::basic_string<charT, Traits, Alloc>::const_iterator> make_u32regex_token_iterator(const std::basic_string<charT, Traits, Alloc>& p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   typedef typename std::basic_string<charT, Traits, Alloc>::const_iterator iter_type;\n   return u32regex_token_iterator<iter_type>(p.begin(), p.end(), e, submatch, m);\n}\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m);\n}\n\n// construction from a reference to an array:\ntemplate <std::size_t N>\ninline u32regex_token_iterator<const char*> make_u32regex_token_iterator(const char* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const char*>(p, p+std::strlen(p), e, submatch, m);\n}\n#ifndef BOOST_NO_WREGEX\ntemplate <std::size_t N>\ninline u32regex_token_iterator<const wchar_t*> make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const wchar_t*>(p, p+std::wcslen(p), e, submatch, m);\n}\n#endif\n#if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T)\ntemplate <std::size_t N>\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const UChar* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(p, p+u_strlen(p), e, submatch, m);\n}\n#endif\ntemplate <class charT, class Traits, class Alloc, std::size_t N>\ninline u32regex_token_iterator<typename std::basic_string<charT, Traits, Alloc>::const_iterator> make_u32regex_token_iterator(const std::basic_string<charT, Traits, Alloc>& p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   typedef typename std::basic_string<charT, Traits, Alloc>::const_iterator iter_type;\n   return u32regex_token_iterator<iter_type>(p.begin(), p.end(), e, submatch, m);\n}\ntemplate <std::size_t N>\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m);\n}\n\n// construction from a vector of sub_match state_id's:\ninline u32regex_token_iterator<const char*> make_u32regex_token_iterator(const char* p, const u32regex& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const char*>(p, p+std::strlen(p), e, submatch, m);\n}\n#ifndef BOOST_NO_WREGEX\ninline u32regex_token_iterator<const wchar_t*> make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const wchar_t*>(p, p+std::wcslen(p), e, submatch, m);\n}\n#endif\n#if !defined(U_WCHAR_IS_UTF16) && (U_SIZEOF_WCHAR_T != 2)\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const UChar* p, const u32regex& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(p, p+u_strlen(p), e, submatch, m);\n}\n#endif\ntemplate <class charT, class Traits, class Alloc>\ninline u32regex_token_iterator<typename std::basic_string<charT, Traits, Alloc>::const_iterator> make_u32regex_token_iterator(const std::basic_string<charT, Traits, Alloc>& p, const u32regex& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   typedef typename std::basic_string<charT, Traits, Alloc>::const_iterator iter_type;\n   return u32regex_token_iterator<iter_type>(p.begin(), p.end(), e, submatch, m);\n}\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m);\n}\n\n#ifdef BOOST_MSVC\n#  pragma warning(pop)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n\n} // namespace boost\n\n#endif // BOOST_REGEX_V4_REGEX_TOKEN_ITERATOR_HPP\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/unicode_iterator.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         unicode_iterator.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Iterator adapters for converting between different Unicode encodings.\n  */\n\n/****************************************************************************\n\nContents:\n~~~~~~~~~\n\n1) Read Only, Input Adapters:\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\ntemplate <class BaseIterator, class U8Type = ::boost::uint8_t>\nclass u32_to_u8_iterator;\n\nAdapts sequence of UTF-32 code points to \"look like\" a sequence of UTF-8.\n\ntemplate <class BaseIterator, class U32Type = ::boost::uint32_t>\nclass u8_to_u32_iterator;\n\nAdapts sequence of UTF-8 code points to \"look like\" a sequence of UTF-32.\n\ntemplate <class BaseIterator, class U16Type = ::boost::uint16_t>\nclass u32_to_u16_iterator;\n\nAdapts sequence of UTF-32 code points to \"look like\" a sequence of UTF-16.\n\ntemplate <class BaseIterator, class U32Type = ::boost::uint32_t>\nclass u16_to_u32_iterator;\n\nAdapts sequence of UTF-16 code points to \"look like\" a sequence of UTF-32.\n\n2) Single pass output iterator adapters:\n\ntemplate <class BaseIterator>\nclass utf8_output_iterator;\n\nAccepts UTF-32 code points and forwards them on as UTF-8 code points.\n\ntemplate <class BaseIterator>\nclass utf16_output_iterator;\n\nAccepts UTF-32 code points and forwards them on as UTF-16 code points.\n\n****************************************************************************/\n\n#ifndef BOOST_REGEX_V4_UNICODE_ITERATOR_HPP\n#define BOOST_REGEX_V4_UNICODE_ITERATOR_HPP\n#include <boost/cstdint.hpp>\n#include <boost/regex/config.hpp>\n#include <boost/static_assert.hpp>\n#include <boost/throw_exception.hpp>\n#include <stdexcept>\n#ifndef BOOST_NO_STD_LOCALE\n#include <sstream>\n#include <ios>\n#endif\n#include <limits.h> // CHAR_BIT\n\n#ifdef BOOST_REGEX_CXX03\n\n#else\n#endif\n\nnamespace boost{\n\nnamespace detail{\n\nstatic const ::boost::uint16_t high_surrogate_base = 0xD7C0u;\nstatic const ::boost::uint16_t low_surrogate_base = 0xDC00u;\nstatic const ::boost::uint32_t ten_bit_mask = 0x3FFu;\n\ninline bool is_high_surrogate(::boost::uint16_t v)\n{\n   return (v & 0xFFFFFC00u) == 0xd800u;\n}\ninline bool is_low_surrogate(::boost::uint16_t v)\n{\n   return (v & 0xFFFFFC00u) == 0xdc00u;\n}\ntemplate <class T>\ninline bool is_surrogate(T v)\n{\n   return (v & 0xFFFFF800u) == 0xd800;\n}\n\ninline unsigned utf8_byte_count(boost::uint8_t c)\n{\n   // if the most significant bit with a zero in it is in position\n   // 8-N then there are N bytes in this UTF-8 sequence:\n   boost::uint8_t mask = 0x80u;\n   unsigned result = 0;\n   while(c & mask)\n   {\n      ++result;\n      mask >>= 1;\n   }\n   return (result == 0) ? 1 : ((result > 4) ? 4 : result);\n}\n\ninline unsigned utf8_trailing_byte_count(boost::uint8_t c)\n{\n   return utf8_byte_count(c) - 1;\n}\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4100)\n#endif\n#ifndef BOOST_NO_EXCEPTIONS\nBOOST_NORETURN\n#endif\ninline void invalid_utf32_code_point(::boost::uint32_t val)\n{\n#ifndef BOOST_NO_STD_LOCALE\n   std::stringstream ss;\n   ss << \"Invalid UTF-32 code point U+\" << std::showbase << std::hex << val << \" encountered while trying to encode UTF-16 sequence\";\n   std::out_of_range e(ss.str());\n#else\n   std::out_of_range e(\"Invalid UTF-32 code point encountered while trying to encode UTF-16 sequence\");\n#endif\n   boost::throw_exception(e);\n}\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n\n} // namespace detail\n\ntemplate <class BaseIterator, class U16Type = ::boost::uint16_t>\nclass u32_to_u16_iterator\n{\n#if !defined(BOOST_NO_STD_ITERATOR_TRAITS)\n   typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;\n\n   BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 32);\n   BOOST_STATIC_ASSERT(sizeof(U16Type)*CHAR_BIT == 16);\n#endif\n\npublic:\n   typedef std::ptrdiff_t     difference_type;\n   typedef U16Type            value_type;\n   typedef value_type const*  pointer;\n   typedef value_type const   reference;\n   typedef std::bidirectional_iterator_tag iterator_category;\n\n   reference operator*()const\n   {\n      if(m_current == 2)\n         extract_current();\n      return m_values[m_current];\n   }\n   bool operator==(const u32_to_u16_iterator& that)const\n   {\n      if(m_position == that.m_position)\n      {\n         // Both m_currents must be equal, or both even\n         // this is the same as saying their sum must be even:\n         return (m_current + that.m_current) & 1u ? false : true;\n      }\n      return false;\n   }\n   bool operator!=(const u32_to_u16_iterator& that)const\n   {\n      return !(*this == that);\n   }\n   u32_to_u16_iterator& operator++()\n   {\n      // if we have a pending read then read now, so that we know whether\n      // to skip a position, or move to a low-surrogate:\n      if(m_current == 2)\n      {\n         // pending read:\n         extract_current();\n      }\n      // move to the next surrogate position:\n      ++m_current;\n      // if we've reached the end skip a position:\n      if(m_values[m_current] == 0)\n      {\n         m_current = 2;\n         ++m_position;\n      }\n      return *this;\n   }\n   u32_to_u16_iterator operator++(int)\n   {\n      u32_to_u16_iterator r(*this);\n      ++(*this);\n      return r;\n   }\n   u32_to_u16_iterator& operator--()\n   {\n      if(m_current != 1)\n      {\n         // decrementing an iterator always leads to a valid position:\n         --m_position;\n         extract_current();\n         m_current = m_values[1] ? 1 : 0;\n      }\n      else\n      {\n         m_current = 0;\n      }\n      return *this;\n   }\n   u32_to_u16_iterator operator--(int)\n   {\n      u32_to_u16_iterator r(*this);\n      --(*this);\n      return r;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\n   // construct:\n   u32_to_u16_iterator() : m_position(), m_current(0)\n   {\n      m_values[0] = 0;\n      m_values[1] = 0;\n      m_values[2] = 0;\n   }\n   u32_to_u16_iterator(BaseIterator b) : m_position(b), m_current(2)\n   {\n      m_values[0] = 0;\n      m_values[1] = 0;\n      m_values[2] = 0;\n   }\nprivate:\n\n   void extract_current()const\n   {\n      // begin by checking for a code point out of range:\n      ::boost::uint32_t v = *m_position;\n      if(v >= 0x10000u)\n      {\n         if(v > 0x10FFFFu)\n            detail::invalid_utf32_code_point(*m_position);\n         // split into two surrogates:\n         m_values[0] = static_cast<U16Type>(v >> 10) + detail::high_surrogate_base;\n         m_values[1] = static_cast<U16Type>(v & detail::ten_bit_mask) + detail::low_surrogate_base;\n         m_current = 0;\n         BOOST_REGEX_ASSERT(detail::is_high_surrogate(m_values[0]));\n         BOOST_REGEX_ASSERT(detail::is_low_surrogate(m_values[1]));\n      }\n      else\n      {\n         // 16-bit code point:\n         m_values[0] = static_cast<U16Type>(*m_position);\n         m_values[1] = 0;\n         m_current = 0;\n         // value must not be a surrogate:\n         if(detail::is_surrogate(m_values[0]))\n            detail::invalid_utf32_code_point(*m_position);\n      }\n   }\n   BaseIterator m_position;\n   mutable U16Type m_values[3];\n   mutable unsigned m_current;\n};\n\ntemplate <class BaseIterator, class U32Type = ::boost::uint32_t>\nclass u16_to_u32_iterator\n{\n   // special values for pending iterator reads:\n   BOOST_STATIC_CONSTANT(U32Type, pending_read = 0xffffffffu);\n\n#if !defined(BOOST_NO_STD_ITERATOR_TRAITS)\n   typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;\n\n   BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 16);\n   BOOST_STATIC_ASSERT(sizeof(U32Type)*CHAR_BIT == 32);\n#endif\n\npublic:\n   typedef std::ptrdiff_t     difference_type;\n   typedef U32Type            value_type;\n   typedef value_type const*  pointer;\n   typedef value_type const   reference;\n   typedef std::bidirectional_iterator_tag iterator_category;\n\n   reference operator*()const\n   {\n      if(m_value == pending_read)\n         extract_current();\n      return m_value;\n   }\n   bool operator==(const u16_to_u32_iterator& that)const\n   {\n      return m_position == that.m_position;\n   }\n   bool operator!=(const u16_to_u32_iterator& that)const\n   {\n      return !(*this == that);\n   }\n   u16_to_u32_iterator& operator++()\n   {\n      // skip high surrogate first if there is one:\n      if(detail::is_high_surrogate(*m_position)) ++m_position;\n      ++m_position;\n      m_value = pending_read;\n      return *this;\n   }\n   u16_to_u32_iterator operator++(int)\n   {\n      u16_to_u32_iterator r(*this);\n      ++(*this);\n      return r;\n   }\n   u16_to_u32_iterator& operator--()\n   {\n      --m_position;\n      // if we have a low surrogate then go back one more:\n      if(detail::is_low_surrogate(*m_position)) \n         --m_position;\n      m_value = pending_read;\n      return *this;\n   }\n   u16_to_u32_iterator operator--(int)\n   {\n      u16_to_u32_iterator r(*this);\n      --(*this);\n      return r;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\n   // construct:\n   u16_to_u32_iterator() : m_position()\n   {\n      m_value = pending_read;\n   }\n   u16_to_u32_iterator(BaseIterator b) : m_position(b)\n   {\n      m_value = pending_read;\n   }\n   //\n   // Range checked version:\n   //\n   u16_to_u32_iterator(BaseIterator b, BaseIterator start, BaseIterator end) : m_position(b)\n   {\n      m_value = pending_read;\n      //\n      // The range must not start with a low surrogate, or end in a high surrogate,\n      // otherwise we run the risk of running outside the underlying input range.\n      // Likewise b must not be located at a low surrogate.\n      //\n      boost::uint16_t val;\n      if(start != end)\n      {\n         if((b != start) && (b != end))\n         {\n            val = *b;\n            if(detail::is_surrogate(val) && ((val & 0xFC00u) == 0xDC00u))\n               invalid_code_point(val);\n         }\n         val = *start;\n         if(detail::is_surrogate(val) && ((val & 0xFC00u) == 0xDC00u))\n            invalid_code_point(val);\n         val = *--end;\n         if(detail::is_high_surrogate(val))\n            invalid_code_point(val);\n      }\n   }\nprivate:\n   static void invalid_code_point(::boost::uint16_t val)\n   {\n#ifndef BOOST_NO_STD_LOCALE\n      std::stringstream ss;\n      ss << \"Misplaced UTF-16 surrogate U+\" << std::showbase << std::hex << val << \" encountered while trying to encode UTF-32 sequence\";\n      std::out_of_range e(ss.str());\n#else\n      std::out_of_range e(\"Misplaced UTF-16 surrogate encountered while trying to encode UTF-32 sequence\");\n#endif\n      boost::throw_exception(e);\n   }\n   void extract_current()const\n   {\n      m_value = static_cast<U32Type>(static_cast< ::boost::uint16_t>(*m_position));\n      // if the last value is a high surrogate then adjust m_position and m_value as needed:\n      if(detail::is_high_surrogate(*m_position))\n      {\n         // precondition; next value must have be a low-surrogate:\n         BaseIterator next(m_position);\n         ::boost::uint16_t t = *++next;\n         if((t & 0xFC00u) != 0xDC00u)\n            invalid_code_point(t);\n         m_value = (m_value - detail::high_surrogate_base) << 10;\n         m_value |= (static_cast<U32Type>(static_cast< ::boost::uint16_t>(t)) & detail::ten_bit_mask);\n      }\n      // postcondition; result must not be a surrogate:\n      if(detail::is_surrogate(m_value))\n         invalid_code_point(static_cast< ::boost::uint16_t>(m_value));\n   }\n   BaseIterator m_position;\n   mutable U32Type m_value;\n};\n\ntemplate <class BaseIterator, class U8Type = ::boost::uint8_t>\nclass u32_to_u8_iterator\n{\n#if !defined(BOOST_NO_STD_ITERATOR_TRAITS)\n   typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;\n\n   BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 32);\n   BOOST_STATIC_ASSERT(sizeof(U8Type)*CHAR_BIT == 8);\n#endif\n\npublic:\n   typedef std::ptrdiff_t     difference_type;\n   typedef U8Type             value_type;\n   typedef value_type const*  pointer;\n   typedef value_type const   reference;\n   typedef std::bidirectional_iterator_tag iterator_category;\n\n   reference operator*()const\n   {\n      if(m_current == 4)\n         extract_current();\n      return m_values[m_current];\n   }\n   bool operator==(const u32_to_u8_iterator& that)const\n   {\n      if(m_position == that.m_position)\n      {\n         // either the m_current's must be equal, or one must be 0 and \n         // the other 4: which means neither must have bits 1 or 2 set:\n         return (m_current == that.m_current)\n            || (((m_current | that.m_current) & 3) == 0);\n      }\n      return false;\n   }\n   bool operator!=(const u32_to_u8_iterator& that)const\n   {\n      return !(*this == that);\n   }\n   u32_to_u8_iterator& operator++()\n   {\n      // if we have a pending read then read now, so that we know whether\n      // to skip a position, or move to a low-surrogate:\n      if(m_current == 4)\n      {\n         // pending read:\n         extract_current();\n      }\n      // move to the next surrogate position:\n      ++m_current;\n      // if we've reached the end skip a position:\n      if(m_values[m_current] == 0)\n      {\n         m_current = 4;\n         ++m_position;\n      }\n      return *this;\n   }\n   u32_to_u8_iterator operator++(int)\n   {\n      u32_to_u8_iterator r(*this);\n      ++(*this);\n      return r;\n   }\n   u32_to_u8_iterator& operator--()\n   {\n      if((m_current & 3) == 0)\n      {\n         --m_position;\n         extract_current();\n         m_current = 3;\n         while(m_current && (m_values[m_current] == 0))\n            --m_current;\n      }\n      else\n         --m_current;\n      return *this;\n   }\n   u32_to_u8_iterator operator--(int)\n   {\n      u32_to_u8_iterator r(*this);\n      --(*this);\n      return r;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\n   // construct:\n   u32_to_u8_iterator() : m_position(), m_current(0)\n   {\n      m_values[0] = 0;\n      m_values[1] = 0;\n      m_values[2] = 0;\n      m_values[3] = 0;\n      m_values[4] = 0;\n   }\n   u32_to_u8_iterator(BaseIterator b) : m_position(b), m_current(4)\n   {\n      m_values[0] = 0;\n      m_values[1] = 0;\n      m_values[2] = 0;\n      m_values[3] = 0;\n      m_values[4] = 0;\n   }\nprivate:\n\n   void extract_current()const\n   {\n      boost::uint32_t c = *m_position;\n      if(c > 0x10FFFFu)\n         detail::invalid_utf32_code_point(c);\n      if(c < 0x80u)\n      {\n         m_values[0] = static_cast<unsigned char>(c);\n         m_values[1] = static_cast<unsigned char>(0u);\n         m_values[2] = static_cast<unsigned char>(0u);\n         m_values[3] = static_cast<unsigned char>(0u);\n      }\n      else if(c < 0x800u)\n      {\n         m_values[0] = static_cast<unsigned char>(0xC0u + (c >> 6));\n         m_values[1] = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n         m_values[2] = static_cast<unsigned char>(0u);\n         m_values[3] = static_cast<unsigned char>(0u);\n      }\n      else if(c < 0x10000u)\n      {\n         m_values[0] = static_cast<unsigned char>(0xE0u + (c >> 12));\n         m_values[1] = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));\n         m_values[2] = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n         m_values[3] = static_cast<unsigned char>(0u);\n      }\n      else\n      {\n         m_values[0] = static_cast<unsigned char>(0xF0u + (c >> 18));\n         m_values[1] = static_cast<unsigned char>(0x80u + ((c >> 12) & 0x3Fu));\n         m_values[2] = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));\n         m_values[3] = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n      }\n      m_current= 0;\n   }\n   BaseIterator m_position;\n   mutable U8Type m_values[5];\n   mutable unsigned m_current;\n};\n\ntemplate <class BaseIterator, class U32Type = ::boost::uint32_t>\nclass u8_to_u32_iterator\n{\n   // special values for pending iterator reads:\n   BOOST_STATIC_CONSTANT(U32Type, pending_read = 0xffffffffu);\n\n#if !defined(BOOST_NO_STD_ITERATOR_TRAITS)\n   typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;\n\n   BOOST_STATIC_ASSERT(sizeof(base_value_type)*CHAR_BIT == 8);\n   BOOST_STATIC_ASSERT(sizeof(U32Type)*CHAR_BIT == 32);\n#endif\n\npublic:\n   typedef std::ptrdiff_t     difference_type;\n   typedef U32Type            value_type;\n   typedef value_type const*  pointer;\n   typedef value_type const   reference;\n   typedef std::bidirectional_iterator_tag iterator_category;\n\n   reference operator*()const\n   {\n      if(m_value == pending_read)\n         extract_current();\n      return m_value;\n   }\n   bool operator==(const u8_to_u32_iterator& that)const\n   {\n      return m_position == that.m_position;\n   }\n   bool operator!=(const u8_to_u32_iterator& that)const\n   {\n      return !(*this == that);\n   }\n   u8_to_u32_iterator& operator++()\n   {\n      // We must not start with a continuation character:\n      if((static_cast<boost::uint8_t>(*m_position) & 0xC0) == 0x80)\n         invalid_sequence();\n      // skip high surrogate first if there is one:\n      unsigned c = detail::utf8_byte_count(*m_position);\n      if(m_value == pending_read)\n      {\n         // Since we haven't read in a value, we need to validate the code points:\n         for(unsigned i = 0; i < c; ++i)\n         {\n            ++m_position;\n            // We must have a continuation byte:\n            if((i != c - 1) && ((static_cast<boost::uint8_t>(*m_position) & 0xC0) != 0x80))\n               invalid_sequence();\n         }\n      }\n      else\n      {\n         std::advance(m_position, c);\n      }\n      m_value = pending_read;\n      return *this;\n   }\n   u8_to_u32_iterator operator++(int)\n   {\n      u8_to_u32_iterator r(*this);\n      ++(*this);\n      return r;\n   }\n   u8_to_u32_iterator& operator--()\n   {\n      // Keep backtracking until we don't have a trailing character:\n      unsigned count = 0;\n      while((*--m_position & 0xC0u) == 0x80u) ++count;\n      // now check that the sequence was valid:\n      if(count != detail::utf8_trailing_byte_count(*m_position))\n         invalid_sequence();\n      m_value = pending_read;\n      return *this;\n   }\n   u8_to_u32_iterator operator--(int)\n   {\n      u8_to_u32_iterator r(*this);\n      --(*this);\n      return r;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\n   // construct:\n   u8_to_u32_iterator() : m_position()\n   {\n      m_value = pending_read;\n   }\n   u8_to_u32_iterator(BaseIterator b) : m_position(b)\n   {\n      m_value = pending_read;\n   }\n   //\n   // Checked constructor:\n   //\n   u8_to_u32_iterator(BaseIterator b, BaseIterator start, BaseIterator end) : m_position(b)\n   {\n      m_value = pending_read;\n      //\n      // We must not start with a continuation character, or end with a \n      // truncated UTF-8 sequence otherwise we run the risk of going past\n      // the start/end of the underlying sequence:\n      //\n      if(start != end)\n      {\n         unsigned char v = *start;\n         if((v & 0xC0u) == 0x80u)\n            invalid_sequence();\n         if((b != start) && (b != end) && ((*b & 0xC0u) == 0x80u))\n            invalid_sequence();\n         BaseIterator pos = end;\n         do\n         {\n            v = *--pos;\n         }\n         while((start != pos) && ((v & 0xC0u) == 0x80u));\n         std::ptrdiff_t extra = detail::utf8_byte_count(v);\n         if(std::distance(pos, end) < extra)\n            invalid_sequence();\n      }\n   }\nprivate:\n   static void invalid_sequence()\n   {\n      std::out_of_range e(\"Invalid UTF-8 sequence encountered while trying to encode UTF-32 character\");\n      boost::throw_exception(e);\n   }\n   void extract_current()const\n   {\n      m_value = static_cast<U32Type>(static_cast< ::boost::uint8_t>(*m_position));\n      // we must not have a continuation character:\n      if((m_value & 0xC0u) == 0x80u)\n         invalid_sequence();\n      // see how many extra bytes we have:\n      unsigned extra = detail::utf8_trailing_byte_count(*m_position);\n      // extract the extra bits, 6 from each extra byte:\n      BaseIterator next(m_position);\n      for(unsigned c = 0; c < extra; ++c)\n      {\n         ++next;\n         m_value <<= 6;\n         // We must have a continuation byte:\n         if((static_cast<boost::uint8_t>(*next) & 0xC0) != 0x80)\n            invalid_sequence();\n         m_value += static_cast<boost::uint8_t>(*next) & 0x3Fu;\n      }\n      // we now need to remove a few of the leftmost bits, but how many depends\n      // upon how many extra bytes we've extracted:\n      static const boost::uint32_t masks[4] = \n      {\n         0x7Fu,\n         0x7FFu,\n         0xFFFFu,\n         0x1FFFFFu,\n      };\n      m_value &= masks[extra];\n      // check the result is in range:\n      if(m_value > static_cast<U32Type>(0x10FFFFu))\n         invalid_sequence();\n      // The result must not be a surrogate:\n      if((m_value >= static_cast<U32Type>(0xD800)) && (m_value <= static_cast<U32Type>(0xDFFF)))\n         invalid_sequence();\n      // We should not have had an invalidly encoded UTF8 sequence:\n      if((extra > 0) && (m_value <= static_cast<U32Type>(masks[extra - 1])))\n         invalid_sequence();\n   }\n   BaseIterator m_position;\n   mutable U32Type m_value;\n};\n\ntemplate <class BaseIterator>\nclass utf16_output_iterator\n{\npublic:\n   typedef void                                   difference_type;\n   typedef void                                   value_type;\n   typedef boost::uint32_t*                       pointer;\n   typedef boost::uint32_t&                       reference;\n   typedef std::output_iterator_tag               iterator_category;\n\n   utf16_output_iterator(const BaseIterator& b)\n      : m_position(b){}\n   utf16_output_iterator(const utf16_output_iterator& that)\n      : m_position(that.m_position){}\n   utf16_output_iterator& operator=(const utf16_output_iterator& that)\n   {\n      m_position = that.m_position;\n      return *this;\n   }\n   const utf16_output_iterator& operator*()const\n   {\n      return *this;\n   }\n   void operator=(boost::uint32_t val)const\n   {\n      push(val);\n   }\n   utf16_output_iterator& operator++()\n   {\n      return *this;\n   }\n   utf16_output_iterator& operator++(int)\n   {\n      return *this;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\nprivate:\n   void push(boost::uint32_t v)const\n   {\n      if(v >= 0x10000u)\n      {\n         // begin by checking for a code point out of range:\n         if(v > 0x10FFFFu)\n            detail::invalid_utf32_code_point(v);\n         // split into two surrogates:\n         *m_position++ = static_cast<boost::uint16_t>(v >> 10) + detail::high_surrogate_base;\n         *m_position++ = static_cast<boost::uint16_t>(v & detail::ten_bit_mask) + detail::low_surrogate_base;\n      }\n      else\n      {\n         // 16-bit code point:\n         // value must not be a surrogate:\n         if(detail::is_surrogate(v))\n            detail::invalid_utf32_code_point(v);\n         *m_position++ = static_cast<boost::uint16_t>(v);\n      }\n   }\n   mutable BaseIterator m_position;\n};\n\ntemplate <class BaseIterator>\nclass utf8_output_iterator\n{\npublic:\n   typedef void                                   difference_type;\n   typedef void                                   value_type;\n   typedef boost::uint32_t*                       pointer;\n   typedef boost::uint32_t&                       reference;\n   typedef std::output_iterator_tag               iterator_category;\n\n   utf8_output_iterator(const BaseIterator& b)\n      : m_position(b){}\n   utf8_output_iterator(const utf8_output_iterator& that)\n      : m_position(that.m_position){}\n   utf8_output_iterator& operator=(const utf8_output_iterator& that)\n   {\n      m_position = that.m_position;\n      return *this;\n   }\n   const utf8_output_iterator& operator*()const\n   {\n      return *this;\n   }\n   void operator=(boost::uint32_t val)const\n   {\n      push(val);\n   }\n   utf8_output_iterator& operator++()\n   {\n      return *this;\n   }\n   utf8_output_iterator& operator++(int)\n   {\n      return *this;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\nprivate:\n   void push(boost::uint32_t c)const\n   {\n      if(c > 0x10FFFFu)\n         detail::invalid_utf32_code_point(c);\n      if(c < 0x80u)\n      {\n         *m_position++ = static_cast<unsigned char>(c);\n      }\n      else if(c < 0x800u)\n      {\n         *m_position++ = static_cast<unsigned char>(0xC0u + (c >> 6));\n         *m_position++ = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n      }\n      else if(c < 0x10000u)\n      {\n         *m_position++ = static_cast<unsigned char>(0xE0u + (c >> 12));\n         *m_position++ = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));\n         *m_position++ = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n      }\n      else\n      {\n         *m_position++ = static_cast<unsigned char>(0xF0u + (c >> 18));\n         *m_position++ = static_cast<unsigned char>(0x80u + ((c >> 12) & 0x3Fu));\n         *m_position++ = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));\n         *m_position++ = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n      }\n   }\n   mutable BaseIterator m_position;\n};\n\n} // namespace boost\n\n#endif // BOOST_REGEX_UNICODE_ITERATOR_HPP\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v4/w32_regex_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         w32_regex_traits.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression traits class w32_regex_traits.\n  */\n\n#ifndef BOOST_W32_REGEX_TRAITS_HPP_INCLUDED\n#define BOOST_W32_REGEX_TRAITS_HPP_INCLUDED\n\n#ifndef BOOST_REGEX_NO_WIN32_LOCALE\n\n#ifndef BOOST_RE_PAT_EXCEPT_HPP\n#include <boost/regex/pattern_except.hpp>\n#endif\n#ifndef BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED\n#include <boost/regex/v4/regex_traits_defaults.hpp>\n#endif\n#ifdef BOOST_HAS_THREADS\n#include <boost/regex/pending/static_mutex.hpp>\n#endif\n#ifndef BOOST_REGEX_PRIMARY_TRANSFORM\n#include <boost/regex/v4/primary_transform.hpp>\n#endif\n#ifndef BOOST_REGEX_OBJECT_CACHE_HPP\n#include <boost/regex/v4/object_cache.hpp>\n#endif\n\n#define VC_EXTRALEAN\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n\n#if defined(_MSC_VER) && !defined(_WIN32_WCE) && !defined(UNDER_CE)\n#pragma comment(lib, \"user32.lib\")\n#endif\n\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_PREFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable:4786)\n#if BOOST_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace boost{ \n\n//\n// forward declaration is needed by some compilers:\n//\ntemplate <class charT>\nclass w32_regex_traits;\n   \nnamespace BOOST_REGEX_DETAIL_NS{\n\n//\n// start by typedeffing the types we'll need:\n//\ntypedef ::boost::uint32_t lcid_type;   // placeholder for LCID.\ntypedef ::boost::shared_ptr<void> cat_type; // placeholder for dll HANDLE.\n\n//\n// then add wrappers around the actual Win32 API's (ie implementation hiding):\n//\nlcid_type BOOST_REGEX_CALL w32_get_default_locale();\nbool BOOST_REGEX_CALL w32_is_lower(char, lcid_type);\n#ifndef BOOST_NO_WREGEX\nbool BOOST_REGEX_CALL w32_is_lower(wchar_t, lcid_type);\n#endif\nbool BOOST_REGEX_CALL w32_is_upper(char, lcid_type);\n#ifndef BOOST_NO_WREGEX\nbool BOOST_REGEX_CALL w32_is_upper(wchar_t, lcid_type);\n#endif\ncat_type BOOST_REGEX_CALL w32_cat_open(const std::string& name);\nstd::string BOOST_REGEX_CALL w32_cat_get(const cat_type& cat, lcid_type state_id, int i, const std::string& def);\n#ifndef BOOST_NO_WREGEX\nstd::wstring BOOST_REGEX_CALL w32_cat_get(const cat_type& cat, lcid_type state_id, int i, const std::wstring& def);\n#endif\nstd::string BOOST_REGEX_CALL w32_transform(lcid_type state_id, const char* p1, const char* p2);\n#ifndef BOOST_NO_WREGEX\nstd::wstring BOOST_REGEX_CALL w32_transform(lcid_type state_id, const wchar_t* p1, const wchar_t* p2);\n#endif\nchar BOOST_REGEX_CALL w32_tolower(char c, lcid_type);\n#ifndef BOOST_NO_WREGEX\nwchar_t BOOST_REGEX_CALL w32_tolower(wchar_t c, lcid_type);\n#endif\nchar BOOST_REGEX_CALL w32_toupper(char c, lcid_type);\n#ifndef BOOST_NO_WREGEX\nwchar_t BOOST_REGEX_CALL w32_toupper(wchar_t c, lcid_type);\n#endif\nbool BOOST_REGEX_CALL w32_is(lcid_type, boost::uint32_t mask, char c);\n#ifndef BOOST_NO_WREGEX\nbool BOOST_REGEX_CALL w32_is(lcid_type, boost::uint32_t mask, wchar_t c);\n#endif\n//\n// class w32_regex_traits_base:\n// acts as a container for locale and the facets we are using.\n//\ntemplate <class charT>\nstruct w32_regex_traits_base\n{\n   w32_regex_traits_base(lcid_type l)\n   { imbue(l); }\n   lcid_type imbue(lcid_type l);\n\n   lcid_type m_locale;\n};\n\ntemplate <class charT>\ninline lcid_type w32_regex_traits_base<charT>::imbue(lcid_type l)\n{\n   lcid_type result(m_locale);\n   m_locale = l;\n   return result;\n}\n\n//\n// class w32_regex_traits_char_layer:\n// implements methods that require specialisation for narrow characters:\n//\ntemplate <class charT>\nclass w32_regex_traits_char_layer : public w32_regex_traits_base<charT>\n{\n   typedef std::basic_string<charT> string_type;\n   typedef std::map<charT, regex_constants::syntax_type> map_type;\n   typedef typename map_type::const_iterator map_iterator_type;\npublic:\n   w32_regex_traits_char_layer(const lcid_type l);\n\n   regex_constants::syntax_type syntax_type(charT c)const\n   {\n      map_iterator_type i = m_char_map.find(c);\n      return ((i == m_char_map.end()) ? 0 : i->second);\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(charT c) const\n   {\n      map_iterator_type i = m_char_map.find(c);\n      if(i == m_char_map.end())\n      {\n         if(::boost::BOOST_REGEX_DETAIL_NS::w32_is_lower(c, this->m_locale)) return regex_constants::escape_type_class;\n         if(::boost::BOOST_REGEX_DETAIL_NS::w32_is_upper(c, this->m_locale)) return regex_constants::escape_type_not_class;\n         return 0;\n      }\n      return i->second;\n   }\n   charT tolower(charT c)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::w32_tolower(c, this->m_locale);\n   }\n   bool isctype(boost::uint32_t mask, charT c)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, mask, c);\n   }\n\nprivate:\n   string_type get_default_message(regex_constants::syntax_type);\n   // TODO: use a hash table when available!\n   map_type m_char_map;\n};\n\ntemplate <class charT>\nw32_regex_traits_char_layer<charT>::w32_regex_traits_char_layer(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l) \n   : w32_regex_traits_base<charT>(l)\n{\n   // we need to start by initialising our syntax map so we know which\n   // character is used for which purpose:\n   cat_type cat;\n   std::string cat_name(w32_regex_traits<charT>::get_catalog_name());\n   if(cat_name.size())\n   {\n      cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name);\n      if(!cat)\n      {\n         std::string m(\"Unable to open message catalog: \");\n         std::runtime_error err(m + cat_name);\n         boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n      }\n   }\n   //\n   // if we have a valid catalog then load our messages:\n   //\n   if(cat)\n   {\n      for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n      {\n         string_type mss = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i, get_default_message(i));\n         for(typename string_type::size_type j = 0; j < mss.size(); ++j)\n         {\n            this->m_char_map[mss[j]] = i;\n         }\n      }\n   }\n   else\n   {\n      for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n      {\n         const char* ptr = get_default_syntax(i);\n         while(ptr && *ptr)\n         {\n            this->m_char_map[static_cast<charT>(*ptr)] = i;\n            ++ptr;\n         }\n      }\n   }\n}\n\ntemplate <class charT>\ntypename w32_regex_traits_char_layer<charT>::string_type \n   w32_regex_traits_char_layer<charT>::get_default_message(regex_constants::syntax_type i)\n{\n   const char* ptr = get_default_syntax(i);\n   string_type result;\n   while(ptr && *ptr)\n   {\n      result.append(1, static_cast<charT>(*ptr));\n      ++ptr;\n   }\n   return result;\n}\n\n//\n// specialised version for narrow characters:\n//\ntemplate <>\nclass w32_regex_traits_char_layer<char> : public w32_regex_traits_base<char>\n{\n   typedef std::string string_type;\npublic:\n   w32_regex_traits_char_layer(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l)\n   : w32_regex_traits_base<char>(l)\n   {\n      init<char>();\n   }\n\n   regex_constants::syntax_type syntax_type(char c)const\n   {\n      return m_char_map[static_cast<unsigned char>(c)];\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(char c) const\n   {\n      return m_char_map[static_cast<unsigned char>(c)];\n   }\n   char tolower(char c)const\n   {\n      return m_lower_map[static_cast<unsigned char>(c)];\n   }\n   bool isctype(boost::uint32_t mask, char c)const\n   {\n      return m_type_map[static_cast<unsigned char>(c)] & mask;\n   }\n\nprivate:\n   regex_constants::syntax_type m_char_map[1u << CHAR_BIT];\n   char m_lower_map[1u << CHAR_BIT];\n   boost::uint16_t m_type_map[1u << CHAR_BIT];\n   template <class U>\n   void init();\n};\n\n//\n// class w32_regex_traits_implementation:\n// provides pimpl implementation for w32_regex_traits.\n//\ntemplate <class charT>\nclass w32_regex_traits_implementation : public w32_regex_traits_char_layer<charT>\n{\npublic:\n   typedef typename w32_regex_traits<charT>::char_class_type char_class_type;\n   BOOST_STATIC_CONSTANT(char_class_type, mask_word = 0x0400); // must be C1_DEFINED << 1\n   BOOST_STATIC_CONSTANT(char_class_type, mask_unicode = 0x0800); // must be C1_DEFINED << 2\n   BOOST_STATIC_CONSTANT(char_class_type, mask_horizontal = 0x1000); // must be C1_DEFINED << 3\n   BOOST_STATIC_CONSTANT(char_class_type, mask_vertical = 0x2000); // must be C1_DEFINED << 4\n   BOOST_STATIC_CONSTANT(char_class_type, mask_base = 0x3ff);  // all the masks used by the CT_CTYPE1 group\n\n   typedef std::basic_string<charT> string_type;\n   typedef charT char_type;\n   w32_regex_traits_implementation(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l);\n   std::string error_string(regex_constants::error_type n) const\n   {\n      if(!m_error_strings.empty())\n      {\n         std::map<int, std::string>::const_iterator p = m_error_strings.find(n);\n         return (p == m_error_strings.end()) ? std::string(get_default_error_string(n)) : p->second;\n      }\n      return get_default_error_string(n);\n   }\n   char_class_type lookup_classname(const charT* p1, const charT* p2) const\n   {\n      char_class_type result = lookup_classname_imp(p1, p2);\n      if(result == 0)\n      {\n         typedef typename string_type::size_type size_type;\n         string_type temp(p1, p2);\n         for(size_type i = 0; i < temp.size(); ++i)\n            temp[i] = this->tolower(temp[i]);\n         result = lookup_classname_imp(&*temp.begin(), &*temp.begin() + temp.size());\n      }\n      return result;\n   }\n   string_type lookup_collatename(const charT* p1, const charT* p2) const;\n   string_type transform_primary(const charT* p1, const charT* p2) const;\n   string_type transform(const charT* p1, const charT* p2) const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::w32_transform(this->m_locale, p1, p2);\n   }\nprivate:\n   std::map<int, std::string>     m_error_strings;   // error messages indexed by numberic ID\n   std::map<string_type, char_class_type>  m_custom_class_names; // character class names\n   std::map<string_type, string_type>      m_custom_collate_names; // collating element names\n   unsigned                       m_collate_type;    // the form of the collation string\n   charT                          m_collate_delim;   // the collation group delimiter\n   //\n   // helpers:\n   //\n   char_class_type lookup_classname_imp(const charT* p1, const charT* p2) const;\n};\n\ntemplate <class charT>\ntypename w32_regex_traits_implementation<charT>::string_type \n   w32_regex_traits_implementation<charT>::transform_primary(const charT* p1, const charT* p2) const\n{\n   string_type result;\n   //\n   // What we do here depends upon the format of the sort key returned by\n   // sort key returned by this->transform:\n   //\n   switch(m_collate_type)\n   {\n   case sort_C:\n   case sort_unknown:\n      // the best we can do is translate to lower case, then get a regular sort key:\n      {\n         result.assign(p1, p2);\n         typedef typename string_type::size_type size_type;\n         for(size_type i = 0; i < result.size(); ++i)\n            result[i] = this->tolower(result[i]);\n         result = this->transform(&*result.begin(), &*result.begin() + result.size());\n         break;\n      }\n   case sort_fixed:\n      {\n         // get a regular sort key, and then truncate it:\n         result.assign(this->transform(p1, p2));\n         result.erase(this->m_collate_delim);\n         break;\n      }\n   case sort_delim:\n         // get a regular sort key, and then truncate everything after the delim:\n         result.assign(this->transform(p1, p2));\n         std::size_t i;\n         for(i = 0; i < result.size(); ++i)\n         {\n            if(result[i] == m_collate_delim)\n               break;\n         }\n         result.erase(i);\n         break;\n   }\n   if(result.empty())\n      result = string_type(1, charT(0));\n   return result;\n}\n\ntemplate <class charT>\ntypename w32_regex_traits_implementation<charT>::string_type \n   w32_regex_traits_implementation<charT>::lookup_collatename(const charT* p1, const charT* p2) const\n{\n   typedef typename std::map<string_type, string_type>::const_iterator iter_type;\n   if(m_custom_collate_names.size())\n   {\n      iter_type pos = m_custom_collate_names.find(string_type(p1, p2));\n      if(pos != m_custom_collate_names.end())\n         return pos->second;\n   }\n#if !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)\\\n               && !BOOST_WORKAROUND(BOOST_BORLANDC, <= 0x0551)\n   std::string name(p1, p2);\n#else\n   std::string name;\n   const charT* p0 = p1;\n   while(p0 != p2)\n      name.append(1, char(*p0++));\n#endif\n   name = lookup_default_collate_name(name);\n#if !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)\\\n               && !BOOST_WORKAROUND(BOOST_BORLANDC, <= 0x0551)\n   if(name.size())\n      return string_type(name.begin(), name.end());\n#else\n   if(name.size())\n   {\n      string_type result;\n      typedef std::string::const_iterator iter;\n      iter b = name.begin();\n      iter e = name.end();\n      while(b != e)\n         result.append(1, charT(*b++));\n      return result;\n   }\n#endif\n   if(p2 - p1 == 1)\n      return string_type(1, *p1);\n   return string_type();\n}\n\ntemplate <class charT>\nw32_regex_traits_implementation<charT>::w32_regex_traits_implementation(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l)\n: w32_regex_traits_char_layer<charT>(l)\n{\n   cat_type cat;\n   std::string cat_name(w32_regex_traits<charT>::get_catalog_name());\n   if(cat_name.size())\n   {\n      cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name);\n      if(!cat)\n      {\n         std::string m(\"Unable to open message catalog: \");\n         std::runtime_error err(m + cat_name);\n         boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n      }\n   }\n   //\n   // if we have a valid catalog then load our messages:\n   //\n   if(cat)\n   {\n      //\n      // Error messages:\n      //\n      for(boost::regex_constants::error_type i = static_cast<boost::regex_constants::error_type>(0); \n         i <= boost::regex_constants::error_unknown; \n         i = static_cast<boost::regex_constants::error_type>(i + 1))\n      {\n         const char* p = get_default_error_string(i);\n         string_type default_message;\n         while(*p)\n         {\n            default_message.append(1, static_cast<charT>(*p));\n            ++p;\n         }\n         string_type s = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i+200, default_message);\n         std::string result;\n         for(std::string::size_type j = 0; j < s.size(); ++j)\n         {\n            result.append(1, static_cast<char>(s[j]));\n         }\n         m_error_strings[i] = result;\n      }\n      //\n      // Custom class names:\n      //\n      static const char_class_type masks[14] = \n      {\n         0x0104u, // C1_ALPHA | C1_DIGIT\n         0x0100u, // C1_ALPHA\n         0x0020u, // C1_CNTRL\n         0x0004u, // C1_DIGIT\n         (~(0x0020u|0x0008u) & 0x01ffu) | 0x0400u, // not C1_CNTRL or C1_SPACE\n         0x0002u, // C1_LOWER\n         (~0x0020u & 0x01ffu) | 0x0400, // not C1_CNTRL\n         0x0010u, // C1_PUNCT\n         0x0008u, // C1_SPACE\n         0x0001u, // C1_UPPER\n         0x0080u, // C1_XDIGIT\n         0x0040u, // C1_BLANK\n         w32_regex_traits_implementation<charT>::mask_word,\n         w32_regex_traits_implementation<charT>::mask_unicode,\n      };\n      static const string_type null_string;\n      for(unsigned int j = 0; j <= 13; ++j)\n      {\n         string_type s(::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, j+300, null_string));\n         if(s.size())\n            this->m_custom_class_names[s] = masks[j];\n      }\n   }\n   //\n   // get the collation format used by m_pcollate:\n   //\n   m_collate_type = BOOST_REGEX_DETAIL_NS::find_sort_syntax(this, &m_collate_delim);\n}\n\ntemplate <class charT>\ntypename w32_regex_traits_implementation<charT>::char_class_type \n   w32_regex_traits_implementation<charT>::lookup_classname_imp(const charT* p1, const charT* p2) const\n{\n   static const char_class_type masks[22] = \n   {\n      0,\n      0x0104u, // C1_ALPHA | C1_DIGIT\n      0x0100u, // C1_ALPHA\n      0x0040u, // C1_BLANK\n      0x0020u, // C1_CNTRL\n      0x0004u, // C1_DIGIT\n      0x0004u, // C1_DIGIT\n      (~(0x0020u|0x0008u|0x0040) & 0x01ffu) | 0x0400u, // not C1_CNTRL or C1_SPACE or C1_BLANK\n      w32_regex_traits_implementation<charT>::mask_horizontal, \n      0x0002u, // C1_LOWER\n      0x0002u, // C1_LOWER\n      (~0x0020u & 0x01ffu) | 0x0400, // not C1_CNTRL\n      0x0010u, // C1_PUNCT\n      0x0008u, // C1_SPACE\n      0x0008u, // C1_SPACE\n      0x0001u, // C1_UPPER\n      w32_regex_traits_implementation<charT>::mask_unicode,\n      0x0001u, // C1_UPPER\n      w32_regex_traits_implementation<charT>::mask_vertical, \n      0x0104u | w32_regex_traits_implementation<charT>::mask_word, \n      0x0104u | w32_regex_traits_implementation<charT>::mask_word, \n      0x0080u, // C1_XDIGIT\n   };\n   if(m_custom_class_names.size())\n   {\n      typedef typename std::map<std::basic_string<charT>, char_class_type>::const_iterator map_iter;\n      map_iter pos = m_custom_class_names.find(string_type(p1, p2));\n      if(pos != m_custom_class_names.end())\n         return pos->second;\n   }\n   std::size_t state_id = 1u + (std::size_t)BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2);\n   if(state_id < sizeof(masks) / sizeof(masks[0]))\n      return masks[state_id];\n   return masks[0];\n}\n\n\ntemplate <class charT>\nboost::shared_ptr<const w32_regex_traits_implementation<charT> > create_w32_regex_traits(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l)\n{\n   // TODO: create a cache for previously constructed objects.\n   return boost::object_cache< ::boost::BOOST_REGEX_DETAIL_NS::lcid_type, w32_regex_traits_implementation<charT> >::get(l, 5);\n}\n\n} // BOOST_REGEX_DETAIL_NS\n\ntemplate <class charT>\nclass w32_regex_traits\n{\npublic:\n   typedef charT                         char_type;\n   typedef std::size_t                   size_type;\n   typedef std::basic_string<char_type>  string_type;\n   typedef ::boost::BOOST_REGEX_DETAIL_NS::lcid_type locale_type;\n   typedef boost::uint_least32_t         char_class_type;\n\n   struct boost_extensions_tag{};\n\n   w32_regex_traits()\n      : m_pimpl(BOOST_REGEX_DETAIL_NS::create_w32_regex_traits<charT>(::boost::BOOST_REGEX_DETAIL_NS::w32_get_default_locale()))\n   { }\n   static size_type length(const char_type* p)\n   {\n      return std::char_traits<charT>::length(p);\n   }\n   regex_constants::syntax_type syntax_type(charT c)const\n   {\n      return m_pimpl->syntax_type(c);\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(charT c) const\n   {\n      return m_pimpl->escape_syntax_type(c);\n   }\n   charT translate(charT c) const\n   {\n      return c;\n   }\n   charT translate_nocase(charT c) const\n   {\n      return this->m_pimpl->tolower(c);\n   }\n   charT translate(charT c, bool icase) const\n   {\n      return icase ? this->m_pimpl->tolower(c) : c;\n   }\n   charT tolower(charT c) const\n   {\n      return this->m_pimpl->tolower(c);\n   }\n   charT toupper(charT c) const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::w32_toupper(c, this->m_pimpl->m_locale);\n   }\n   string_type transform(const charT* p1, const charT* p2) const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::w32_transform(this->m_pimpl->m_locale, p1, p2);\n   }\n   string_type transform_primary(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->transform_primary(p1, p2);\n   }\n   char_class_type lookup_classname(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->lookup_classname(p1, p2);\n   }\n   string_type lookup_collatename(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->lookup_collatename(p1, p2);\n   }\n   bool isctype(charT c, char_class_type f) const\n   {\n      if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_base) \n         && (this->m_pimpl->isctype(f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_base, c)))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_unicode) && BOOST_REGEX_DETAIL_NS::is_extended(c))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_word) && (c == '_'))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_vertical)\n         && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\\v')))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_horizontal) \n         && this->isctype(c, 0x0008u) && !this->isctype(c, BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_vertical))\n         return true;\n      return false;\n   }\n   boost::intmax_t toi(const charT*& p1, const charT* p2, int radix)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this);\n   }\n   int value(charT c, int radix)const\n   {\n      int result = (int)::boost::BOOST_REGEX_DETAIL_NS::global_value(c);\n      return result < radix ? result : -1;\n   }\n   locale_type imbue(locale_type l)\n   {\n      ::boost::BOOST_REGEX_DETAIL_NS::lcid_type result(getloc());\n      m_pimpl = BOOST_REGEX_DETAIL_NS::create_w32_regex_traits<charT>(l);\n      return result;\n   }\n   locale_type getloc()const\n   {\n      return m_pimpl->m_locale;\n   }\n   std::string error_string(regex_constants::error_type n) const\n   {\n      return m_pimpl->error_string(n);\n   }\n\n   //\n   // extension:\n   // set the name of the message catalog in use (defaults to \"boost_regex\").\n   //\n   static std::string catalog_name(const std::string& name);\n   static std::string get_catalog_name();\n\nprivate:\n   boost::shared_ptr<const BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT> > m_pimpl;\n   //\n   // catalog name handler:\n   //\n   static std::string& get_catalog_name_inst();\n\n#ifdef BOOST_HAS_THREADS\n   static static_mutex& get_mutex_inst();\n#endif\n};\n\ntemplate <class charT>\nstd::string w32_regex_traits<charT>::catalog_name(const std::string& name)\n{\n#ifdef BOOST_HAS_THREADS\n   static_mutex::scoped_lock lk(get_mutex_inst());\n#endif\n   std::string result(get_catalog_name_inst());\n   get_catalog_name_inst() = name;\n   return result;\n}\n\ntemplate <class charT>\nstd::string& w32_regex_traits<charT>::get_catalog_name_inst()\n{\n   static std::string s_name;\n   return s_name;\n}\n\ntemplate <class charT>\nstd::string w32_regex_traits<charT>::get_catalog_name()\n{\n#ifdef BOOST_HAS_THREADS\n   static_mutex::scoped_lock lk(get_mutex_inst());\n#endif\n   std::string result(get_catalog_name_inst());\n   return result;\n}\n\n#ifdef BOOST_HAS_THREADS\ntemplate <class charT>\nstatic_mutex& w32_regex_traits<charT>::get_mutex_inst()\n{\n   static static_mutex s_mutex = BOOST_STATIC_MUTEX_INIT;\n   return s_mutex;\n}\n#endif\n\nnamespace BOOST_REGEX_DETAIL_NS {\n\n#ifdef BOOST_NO_ANSI_APIS\n   inline UINT get_code_page_for_locale_id(lcid_type idx)\n   {\n      WCHAR code_page_string[7];\n      if (::GetLocaleInfoW(idx, LOCALE_IDEFAULTANSICODEPAGE, code_page_string, 7) == 0)\n         return 0;\n\n      return static_cast<UINT>(_wtol(code_page_string));\n}\n#endif\n\n   template <class U>\n   inline void w32_regex_traits_char_layer<char>::init()\n   {\n      // we need to start by initialising our syntax map so we know which\n      // character is used for which purpose:\n      std::memset(m_char_map, 0, sizeof(m_char_map));\n      cat_type cat;\n      std::string cat_name(w32_regex_traits<char>::get_catalog_name());\n      if (cat_name.size())\n      {\n         cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name);\n         if (!cat)\n         {\n            std::string m(\"Unable to open message catalog: \");\n            std::runtime_error err(m + cat_name);\n            ::boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n         }\n      }\n      //\n      // if we have a valid catalog then load our messages:\n      //\n      if (cat)\n      {\n         for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n         {\n            string_type mss = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i, get_default_syntax(i));\n            for (string_type::size_type j = 0; j < mss.size(); ++j)\n            {\n               m_char_map[static_cast<unsigned char>(mss[j])] = i;\n            }\n         }\n      }\n      else\n      {\n         for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n         {\n            const char* ptr = get_default_syntax(i);\n            while (ptr && *ptr)\n            {\n               m_char_map[static_cast<unsigned char>(*ptr)] = i;\n               ++ptr;\n            }\n         }\n      }\n      //\n      // finish off by calculating our escape types:\n      //\n      unsigned char i = 'A';\n      do\n      {\n         if (m_char_map[i] == 0)\n         {\n            if (::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, 0x0002u, (char)i))\n               m_char_map[i] = regex_constants::escape_type_class;\n            else if (::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, 0x0001u, (char)i))\n               m_char_map[i] = regex_constants::escape_type_not_class;\n         }\n      } while (0xFF != i++);\n\n      //\n      // fill in lower case map:\n      //\n      char char_map[1 << CHAR_BIT];\n      for (int ii = 0; ii < (1 << CHAR_BIT); ++ii)\n         char_map[ii] = static_cast<char>(ii);\n#ifndef BOOST_NO_ANSI_APIS\n      int r = ::LCMapStringA(this->m_locale, LCMAP_LOWERCASE, char_map, 1 << CHAR_BIT, this->m_lower_map, 1 << CHAR_BIT);\n      BOOST_REGEX_ASSERT(r != 0);\n#else\n      UINT code_page = get_code_page_for_locale_id(this->m_locale);\n      BOOST_REGEX_ASSERT(code_page != 0);\n\n      WCHAR wide_char_map[1 << CHAR_BIT];\n      int conv_r = ::MultiByteToWideChar(code_page, 0, char_map, 1 << CHAR_BIT, wide_char_map, 1 << CHAR_BIT);\n      BOOST_REGEX_ASSERT(conv_r != 0);\n\n      WCHAR wide_lower_map[1 << CHAR_BIT];\n      int r = ::LCMapStringW(this->m_locale, LCMAP_LOWERCASE, wide_char_map, 1 << CHAR_BIT, wide_lower_map, 1 << CHAR_BIT);\n      BOOST_REGEX_ASSERT(r != 0);\n\n      conv_r = ::WideCharToMultiByte(code_page, 0, wide_lower_map, r, this->m_lower_map, 1 << CHAR_BIT, NULL, NULL);\n      BOOST_REGEX_ASSERT(conv_r != 0);\n#endif\n      if (r < (1 << CHAR_BIT))\n      {\n         // if we have multibyte characters then not all may have been given\n         // a lower case mapping:\n         for (int jj = r; jj < (1 << CHAR_BIT); ++jj)\n            this->m_lower_map[jj] = static_cast<char>(jj);\n      }\n\n#ifndef BOOST_NO_ANSI_APIS\n      r = ::GetStringTypeExA(this->m_locale, CT_CTYPE1, char_map, 1 << CHAR_BIT, this->m_type_map);\n#else\n      r = ::GetStringTypeExW(this->m_locale, CT_CTYPE1, wide_char_map, 1 << CHAR_BIT, this->m_type_map);\n#endif\n      BOOST_REGEX_ASSERT(0 != r);\n   }\n\n   inline lcid_type BOOST_REGEX_CALL w32_get_default_locale()\n   {\n      return ::GetUserDefaultLCID();\n   }\n\n   inline bool BOOST_REGEX_CALL w32_is_lower(char c, lcid_type idx)\n   {\n#ifndef BOOST_NO_ANSI_APIS\n      WORD mask;\n      if (::GetStringTypeExA(idx, CT_CTYPE1, &c, 1, &mask) && (mask & C1_LOWER))\n         return true;\n      return false;\n#else\n      UINT code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return false;\n\n      WCHAR wide_c;\n      if (::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0)\n         return false;\n\n      WORD mask;\n      if (::GetStringTypeExW(idx, CT_CTYPE1, &wide_c, 1, &mask) && (mask & C1_LOWER))\n         return true;\n      return false;\n#endif\n   }\n\n   inline bool BOOST_REGEX_CALL w32_is_lower(wchar_t c, lcid_type idx)\n   {\n      WORD mask;\n      if (::GetStringTypeExW(idx, CT_CTYPE1, &c, 1, &mask) && (mask & C1_LOWER))\n         return true;\n      return false;\n   }\n\n   inline bool BOOST_REGEX_CALL w32_is_upper(char c, lcid_type idx)\n   {\n#ifndef BOOST_NO_ANSI_APIS\n      WORD mask;\n      if (::GetStringTypeExA(idx, CT_CTYPE1, &c, 1, &mask) && (mask & C1_UPPER))\n         return true;\n      return false;\n#else\n      UINT code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return false;\n\n      WCHAR wide_c;\n      if (::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0)\n         return false;\n\n      WORD mask;\n      if (::GetStringTypeExW(idx, CT_CTYPE1, &wide_c, 1, &mask) && (mask & C1_UPPER))\n         return true;\n      return false;\n#endif\n   }\n\n   inline bool BOOST_REGEX_CALL w32_is_upper(wchar_t c, lcid_type idx)\n   {\n      WORD mask;\n      if (::GetStringTypeExW(idx, CT_CTYPE1, &c, 1, &mask) && (mask & C1_UPPER))\n         return true;\n      return false;\n   }\n\n   inline void free_module(void* mod)\n   {\n      ::FreeLibrary(static_cast<HMODULE>(mod));\n   }\n\n   inline cat_type BOOST_REGEX_CALL w32_cat_open(const std::string& name)\n   {\n#ifndef BOOST_NO_ANSI_APIS\n      cat_type result(::LoadLibraryA(name.c_str()), &free_module);\n      return result;\n#else\n      LPWSTR wide_name = (LPWSTR)_alloca((name.size() + 1) * sizeof(WCHAR));\n      if (::MultiByteToWideChar(CP_ACP, 0, name.c_str(), name.size(), wide_name, name.size() + 1) == 0)\n         return cat_type();\n\n      cat_type result(::LoadLibraryW(wide_name), &free_module);\n      return result;\n#endif\n   }\n\n   inline std::string BOOST_REGEX_CALL w32_cat_get(const cat_type& cat, lcid_type, int i, const std::string& def)\n   {\n#ifndef BOOST_NO_ANSI_APIS\n      char buf[256];\n      if (0 == ::LoadStringA(\n         static_cast<HMODULE>(cat.get()),\n         i,\n         buf,\n         256\n      ))\n      {\n         return def;\n      }\n#else\n      WCHAR wbuf[256];\n      int r = ::LoadStringW(\n         static_cast<HMODULE>(cat.get()),\n         i,\n         wbuf,\n         256\n      );\n      if (r == 0)\n         return def;\n\n\n      int buf_size = 1 + ::WideCharToMultiByte(CP_ACP, 0, wbuf, r, NULL, 0, NULL, NULL);\n      LPSTR buf = (LPSTR)_alloca(buf_size);\n      if (::WideCharToMultiByte(CP_ACP, 0, wbuf, r, buf, buf_size, NULL, NULL) == 0)\n         return def; // failed conversion.\n#endif\n      return std::string(buf);\n   }\n\n#ifndef BOOST_NO_WREGEX\n   inline std::wstring BOOST_REGEX_CALL w32_cat_get(const cat_type& cat, lcid_type, int i, const std::wstring& def)\n   {\n      wchar_t buf[256];\n      if (0 == ::LoadStringW(\n         static_cast<HMODULE>(cat.get()),\n         i,\n         buf,\n         256\n      ))\n      {\n         return def;\n      }\n      return std::wstring(buf);\n   }\n#endif\n   inline std::string BOOST_REGEX_CALL w32_transform(lcid_type idx, const char* p1, const char* p2)\n   {\n#ifndef BOOST_NO_ANSI_APIS\n      int bytes = ::LCMapStringA(\n         idx,       // locale identifier\n         LCMAP_SORTKEY,  // mapping transformation type\n         p1,  // source string\n         static_cast<int>(p2 - p1),        // number of characters in source string\n         0,  // destination buffer\n         0        // size of destination buffer\n      );\n      if (!bytes)\n         return std::string(p1, p2);\n      std::string result(++bytes, '\\0');\n      bytes = ::LCMapStringA(\n         idx,       // locale identifier\n         LCMAP_SORTKEY,  // mapping transformation type\n         p1,  // source string\n         static_cast<int>(p2 - p1),        // number of characters in source string\n         &*result.begin(),  // destination buffer\n         bytes        // size of destination buffer\n      );\n#else\n      UINT code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return std::string(p1, p2);\n\n      int src_len = static_cast<int>(p2 - p1);\n      LPWSTR wide_p1 = (LPWSTR)_alloca((src_len + 1) * 2);\n      if (::MultiByteToWideChar(code_page, 0, p1, src_len, wide_p1, src_len + 1) == 0)\n         return std::string(p1, p2);\n\n      int bytes = ::LCMapStringW(\n         idx,       // locale identifier\n         LCMAP_SORTKEY,  // mapping transformation type\n         wide_p1,  // source string\n         src_len,        // number of characters in source string\n         0,  // destination buffer\n         0        // size of destination buffer\n      );\n      if (!bytes)\n         return std::string(p1, p2);\n      std::string result(++bytes, '\\0');\n      bytes = ::LCMapStringW(\n         idx,       // locale identifier\n         LCMAP_SORTKEY,  // mapping transformation type\n         wide_p1,  // source string\n         src_len,        // number of characters in source string\n         (LPWSTR) & *result.begin(),  // destination buffer\n         bytes        // size of destination buffer\n      );\n#endif\n      if (bytes > static_cast<int>(result.size()))\n         return std::string(p1, p2);\n      while (result.size() && result[result.size() - 1] == '\\0')\n      {\n         result.erase(result.size() - 1);\n      }\n      return result;\n   }\n\n#ifndef BOOST_NO_WREGEX\n   inline std::wstring BOOST_REGEX_CALL w32_transform(lcid_type idx, const wchar_t* p1, const wchar_t* p2)\n   {\n      int bytes = ::LCMapStringW(\n         idx,       // locale identifier\n         LCMAP_SORTKEY,  // mapping transformation type\n         p1,  // source string\n         static_cast<int>(p2 - p1),        // number of characters in source string\n         0,  // destination buffer\n         0        // size of destination buffer\n      );\n      if (!bytes)\n         return std::wstring(p1, p2);\n      std::string result(++bytes, '\\0');\n      bytes = ::LCMapStringW(\n         idx,       // locale identifier\n         LCMAP_SORTKEY,  // mapping transformation type\n         p1,  // source string\n         static_cast<int>(p2 - p1),        // number of characters in source string\n         reinterpret_cast<wchar_t*>(&*result.begin()),  // destination buffer *of bytes*\n         bytes        // size of destination buffer\n      );\n      if (bytes > static_cast<int>(result.size()))\n         return std::wstring(p1, p2);\n      while (result.size() && result[result.size() - 1] == L'\\0')\n      {\n         result.erase(result.size() - 1);\n      }\n      std::wstring r2;\n      for (std::string::size_type i = 0; i < result.size(); ++i)\n         r2.append(1, static_cast<wchar_t>(static_cast<unsigned char>(result[i])));\n      return r2;\n   }\n#endif\n   inline char BOOST_REGEX_CALL w32_tolower(char c, lcid_type idx)\n   {\n      char result[2];\n#ifndef BOOST_NO_ANSI_APIS\n      int b = ::LCMapStringA(\n         idx,       // locale identifier\n         LCMAP_LOWERCASE,  // mapping transformation type\n         &c,  // source string\n         1,        // number of characters in source string\n         result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n#else\n      UINT code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return c;\n\n      WCHAR wide_c;\n      if (::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0)\n         return c;\n\n      WCHAR wide_result;\n      int b = ::LCMapStringW(\n         idx,       // locale identifier\n         LCMAP_LOWERCASE,  // mapping transformation type\n         &wide_c,  // source string\n         1,        // number of characters in source string\n         &wide_result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n\n      if (::WideCharToMultiByte(code_page, 0, &wide_result, 1, result, 2, NULL, NULL) == 0)\n         return c;  // No single byte lower case equivalent available\n#endif\n      return result[0];\n   }\n\n#ifndef BOOST_NO_WREGEX\n   inline wchar_t BOOST_REGEX_CALL w32_tolower(wchar_t c, lcid_type idx)\n   {\n      wchar_t result[2];\n      int b = ::LCMapStringW(\n         idx,       // locale identifier\n         LCMAP_LOWERCASE,  // mapping transformation type\n         &c,  // source string\n         1,        // number of characters in source string\n         result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n      return result[0];\n   }\n#endif\n   inline char BOOST_REGEX_CALL w32_toupper(char c, lcid_type idx)\n   {\n      char result[2];\n#ifndef BOOST_NO_ANSI_APIS\n      int b = ::LCMapStringA(\n         idx,       // locale identifier\n         LCMAP_UPPERCASE,  // mapping transformation type\n         &c,  // source string\n         1,        // number of characters in source string\n         result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n#else\n      UINT code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return c;\n\n      WCHAR wide_c;\n      if (::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0)\n         return c;\n\n      WCHAR wide_result;\n      int b = ::LCMapStringW(\n         idx,       // locale identifier\n         LCMAP_UPPERCASE,  // mapping transformation type\n         &wide_c,  // source string\n         1,        // number of characters in source string\n         &wide_result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n\n      if (::WideCharToMultiByte(code_page, 0, &wide_result, 1, result, 2, NULL, NULL) == 0)\n         return c;  // No single byte upper case equivalent available.\n#endif\n      return result[0];\n   }\n\n#ifndef BOOST_NO_WREGEX\n   inline wchar_t BOOST_REGEX_CALL w32_toupper(wchar_t c, lcid_type idx)\n   {\n      wchar_t result[2];\n      int b = ::LCMapStringW(\n         idx,       // locale identifier\n         LCMAP_UPPERCASE,  // mapping transformation type\n         &c,  // source string\n         1,        // number of characters in source string\n         result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n      return result[0];\n   }\n#endif\n   inline bool BOOST_REGEX_CALL w32_is(lcid_type idx, boost::uint32_t m, char c)\n   {\n      WORD mask;\n#ifndef BOOST_NO_ANSI_APIS\n      if (::GetStringTypeExA(idx, CT_CTYPE1, &c, 1, &mask) && (mask & m & w32_regex_traits_implementation<char>::mask_base))\n         return true;\n#else\n      UINT code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return false;\n\n      WCHAR wide_c;\n      if (::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0)\n         return false;\n\n      if (::GetStringTypeExW(idx, CT_CTYPE1, &wide_c, 1, &mask) && (mask & m & w32_regex_traits_implementation<char>::mask_base))\n         return true;\n#endif\n      if ((m & w32_regex_traits_implementation<char>::mask_word) && (c == '_'))\n         return true;\n      return false;\n   }\n\n#ifndef BOOST_NO_WREGEX\n   inline bool BOOST_REGEX_CALL w32_is(lcid_type idx, boost::uint32_t m, wchar_t c)\n   {\n      WORD mask;\n      if (::GetStringTypeExW(idx, CT_CTYPE1, &c, 1, &mask) && (mask & m & w32_regex_traits_implementation<wchar_t>::mask_base))\n         return true;\n      if ((m & w32_regex_traits_implementation<wchar_t>::mask_word) && (c == '_'))\n         return true;\n      if ((m & w32_regex_traits_implementation<wchar_t>::mask_unicode) && (c > 0xff))\n         return true;\n      return false;\n   }\n#endif\n\n} // BOOST_REGEX_DETAIL_NS\n\n\n} // boost\n\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#ifdef BOOST_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4103)\n#endif\n#ifdef BOOST_HAS_ABI_HEADERS\n#  include BOOST_ABI_SUFFIX\n#endif\n#ifdef BOOST_MSVC\n#pragma warning(pop)\n#endif\n\n#endif // BOOST_REGEX_NO_WIN32_LOCALE\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/basic_regex.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2004 John Maddock\n * Copyright 2011 Garmin Ltd. or its subsidiaries\n *\n * Distributed under the Boost Software License, Version 1.0.\n * (See accompanying file LICENSE_1_0.txt or copy at\n * http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org/ for most recent version.\n  *   FILE         basic_regex.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class basic_regex.\n  */\n\n#ifndef BOOST_REGEX_V5_BASIC_REGEX_HPP\n#define BOOST_REGEX_V5_BASIC_REGEX_HPP\n\n#include <vector>\n\nnamespace boost{\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable : 4251)\n#if BOOST_REGEX_MSVC < 1700\n#     pragma warning(disable : 4231)\n#endif\n#if BOOST_REGEX_MSVC < 1600\n#pragma warning(disable : 4660)\n#endif\n#if BOOST_REGEX_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\n//\n// forward declaration, we will need this one later:\n//\ntemplate <class charT, class traits>\nclass basic_regex_parser;\n\ntemplate <class I>\nvoid bubble_down_one(I first, I last)\n{\n   if(first != last)\n   {\n      I next = last - 1;\n      while((next != first) && (*next < *(next-1)))\n      {\n         (next-1)->swap(*next);\n         --next;\n      }\n   }\n}\n\nstatic const int hash_value_mask = 1 << (std::numeric_limits<int>::digits - 1);\n\ntemplate <class Iterator>\ninline int hash_value_from_capture_name(Iterator i, Iterator j)\n{\n   std::size_t r = 0;\n   while (i != j)\n   {\n      r ^= *i + 0x9e3779b9 + (r << 6) + (r >> 2);\n      ++i;\n   }\n   r %= ((std::numeric_limits<int>::max)());\n   return static_cast<int>(r) | hash_value_mask;\n}\n\nclass named_subexpressions\n{\npublic:\n   struct name\n   {\n      template <class charT>\n      name(const charT* i, const charT* j, int idx)\n         : index(idx) \n      { \n         hash = hash_value_from_capture_name(i, j); \n      }\n      name(int h, int idx)\n         : index(idx), hash(h)\n      { \n      }\n      int index;\n      int hash;\n      bool operator < (const name& other)const\n      {\n         return hash < other.hash;\n      }\n      bool operator == (const name& other)const\n      {\n         return hash == other.hash; \n      }\n      void swap(name& other)\n      {\n         std::swap(index, other.index);\n         std::swap(hash, other.hash);\n      }\n   };\n\n   typedef std::vector<name>::const_iterator const_iterator;\n   typedef std::pair<const_iterator, const_iterator> range_type;\n\n   named_subexpressions(){}\n\n   template <class charT>\n   void set_name(const charT* i, const charT* j, int index)\n   {\n      m_sub_names.push_back(name(i, j, index));\n      bubble_down_one(m_sub_names.begin(), m_sub_names.end());\n   }\n   template <class charT>\n   int get_id(const charT* i, const charT* j)const\n   {\n      name t(i, j, 0);\n      typename std::vector<name>::const_iterator pos = std::lower_bound(m_sub_names.begin(), m_sub_names.end(), t);\n      if((pos != m_sub_names.end()) && (*pos == t))\n      {\n         return pos->index;\n      }\n      return -1;\n   }\n   template <class charT>\n   range_type equal_range(const charT* i, const charT* j)const\n   {\n      name t(i, j, 0);\n      return std::equal_range(m_sub_names.begin(), m_sub_names.end(), t);\n   }\n   int get_id(int h)const\n   {\n      name t(h, 0);\n      std::vector<name>::const_iterator pos = std::lower_bound(m_sub_names.begin(), m_sub_names.end(), t);\n      if((pos != m_sub_names.end()) && (*pos == t))\n      {\n         return pos->index;\n      }\n      return -1;\n   }\n   range_type equal_range(int h)const\n   {\n      name t(h, 0);\n      return std::equal_range(m_sub_names.begin(), m_sub_names.end(), t);\n   }\nprivate:\n   std::vector<name> m_sub_names;\n};\n\n//\n// class regex_data:\n// represents the data we wish to expose to the matching algorithms.\n//\ntemplate <class charT, class traits>\nstruct regex_data : public named_subexpressions\n{\n   typedef regex_constants::syntax_option_type   flag_type;\n   typedef std::size_t                           size_type;  \n\n   regex_data(const ::std::shared_ptr<\n      ::boost::regex_traits_wrapper<traits> >& t) \n      : m_ptraits(t), m_flags(0), m_status(0), m_expression(0), m_expression_len(0),\n         m_mark_count(0), m_first_state(0), m_restart_type(0),\n         m_startmap{ 0 },\n         m_can_be_null(0), m_word_mask(0), m_has_recursions(false), m_disable_match_any(false) {}\n   regex_data() \n      : m_ptraits(new ::boost::regex_traits_wrapper<traits>()), m_flags(0), m_status(0), m_expression(0), m_expression_len(0), \n         m_mark_count(0), m_first_state(0), m_restart_type(0), \n      m_startmap{ 0 },\n         m_can_be_null(0), m_word_mask(0), m_has_recursions(false), m_disable_match_any(false) {}\n\n   ::std::shared_ptr<\n      ::boost::regex_traits_wrapper<traits>\n      >                        m_ptraits;                 // traits class instance\n   flag_type                   m_flags;                   // flags with which we were compiled\n   int                         m_status;                  // error code (0 implies OK).\n   const charT*                m_expression;              // the original expression\n   std::ptrdiff_t              m_expression_len;          // the length of the original expression\n   size_type                   m_mark_count;              // the number of marked sub-expressions\n   BOOST_REGEX_DETAIL_NS::re_syntax_base*  m_first_state;             // the first state of the machine\n   unsigned                    m_restart_type;            // search optimisation type\n   unsigned char               m_startmap[1 << CHAR_BIT]; // which characters can start a match\n   unsigned int                m_can_be_null;             // whether we can match a null string\n   BOOST_REGEX_DETAIL_NS::raw_storage      m_data;                    // the buffer in which our states are constructed\n   typename traits::char_class_type    m_word_mask;       // mask used to determine if a character is a word character\n   std::vector<\n      std::pair<\n      std::size_t, std::size_t> > m_subs;                 // Position of sub-expressions within the *string*.\n   bool                        m_has_recursions;          // whether we have recursive expressions;\n   bool                        m_disable_match_any;       // when set we need to disable the match_any flag as it causes different/buggy behaviour.\n};\n//\n// class basic_regex_implementation\n// pimpl implementation class for basic_regex.\n//\ntemplate <class charT, class traits>\nclass basic_regex_implementation\n   : public regex_data<charT, traits>\n{\npublic:\n   typedef regex_constants::syntax_option_type   flag_type;\n   typedef std::ptrdiff_t                        difference_type;\n   typedef std::size_t                           size_type; \n   typedef typename traits::locale_type          locale_type;\n   typedef const charT*                          const_iterator;\n\n   basic_regex_implementation(){}\n   basic_regex_implementation(const ::std::shared_ptr<\n      ::boost::regex_traits_wrapper<traits> >& t)\n      : regex_data<charT, traits>(t) {}\n   void assign(const charT* arg_first,\n                          const charT* arg_last,\n                          flag_type f)\n   {\n      regex_data<charT, traits>* pdat = this;\n      basic_regex_parser<charT, traits> parser(pdat);\n      parser.parse(arg_first, arg_last, f);\n   }\n\n   locale_type  imbue(locale_type l)\n   { \n      return this->m_ptraits->imbue(l); \n   }\n   locale_type  getloc()const\n   { \n      return this->m_ptraits->getloc(); \n   }\n   std::basic_string<charT>  str()const\n   {\n      std::basic_string<charT> result;\n      if(this->m_status == 0)\n         result = std::basic_string<charT>(this->m_expression, this->m_expression_len);\n      return result;\n   }\n   const_iterator  expression()const\n   {\n      return this->m_expression;\n   }\n   std::pair<const_iterator, const_iterator>  subexpression(std::size_t n)const\n   {\n      const std::pair<std::size_t, std::size_t>& pi = this->m_subs.at(n);\n      std::pair<const_iterator, const_iterator> p(expression() + pi.first, expression() + pi.second);\n      return p;\n   }\n   //\n   // begin, end:\n   const_iterator  begin()const\n   { \n      return (this->m_status ? 0 : this->m_expression); \n   }\n   const_iterator  end()const\n   { \n      return (this->m_status ? 0 : this->m_expression + this->m_expression_len); \n   }\n   flag_type  flags()const\n   {\n      return this->m_flags;\n   }\n   size_type  size()const\n   {\n      return this->m_expression_len;\n   }\n   int  status()const\n   {\n      return this->m_status;\n   }\n   size_type  mark_count()const\n   {\n      return this->m_mark_count - 1;\n   }\n   const BOOST_REGEX_DETAIL_NS::re_syntax_base* get_first_state()const\n   {\n      return this->m_first_state;\n   }\n   unsigned get_restart_type()const\n   {\n      return this->m_restart_type;\n   }\n   const unsigned char* get_map()const\n   {\n      return this->m_startmap;\n   }\n   const ::boost::regex_traits_wrapper<traits>& get_traits()const\n   {\n      return *(this->m_ptraits);\n   }\n   bool can_be_null()const\n   {\n      return this->m_can_be_null;\n   }\n   const regex_data<charT, traits>& get_data()const\n   {\n      basic_regex_implementation<charT, traits> const* p = this;\n      return *static_cast<const regex_data<charT, traits>*>(p);\n   }\n};\n\n} // namespace BOOST_REGEX_DETAIL_NS\n//\n// class basic_regex:\n// represents the compiled\n// regular expression:\n//\n\n#ifdef BOOST_REGEX_NO_FWD\ntemplate <class charT, class traits = regex_traits<charT> >\n#else\ntemplate <class charT, class traits >\n#endif\nclass basic_regex : public regbase\n{\npublic:\n   // typedefs:\n   typedef std::size_t                           traits_size_type;\n   typedef typename traits::string_type          traits_string_type;\n   typedef charT                                 char_type;\n   typedef traits                                traits_type;\n\n   typedef charT                                 value_type;\n   typedef charT&                                reference;\n   typedef const charT&                          const_reference;\n   typedef const charT*                          const_iterator;\n   typedef const_iterator                        iterator;\n   typedef std::ptrdiff_t                        difference_type;\n   typedef std::size_t                           size_type;   \n   typedef regex_constants::syntax_option_type   flag_type;\n   // locale_type\n   // placeholder for actual locale type used by the\n   // traits class to localise *this.\n   typedef typename traits::locale_type          locale_type;\n   \npublic:\n   explicit basic_regex(){}\n   explicit basic_regex(const charT* p, flag_type f = regex_constants::normal)\n   {\n      assign(p, f);\n   }\n   basic_regex(const charT* p1, const charT* p2, flag_type f = regex_constants::normal)\n   {\n      assign(p1, p2, f);\n   }\n   basic_regex(const charT* p, size_type len, flag_type f)\n   {\n      assign(p, len, f);\n   }\n   basic_regex(const basic_regex& that)\n      : m_pimpl(that.m_pimpl) {}\n   ~basic_regex(){}\n   basic_regex&  operator=(const basic_regex& that)\n   {\n      return assign(that);\n   }\n   basic_regex&  operator=(const charT* ptr)\n   {\n      return assign(ptr);\n   }\n\n   //\n   // assign:\n   basic_regex& assign(const basic_regex& that)\n   { \n      m_pimpl = that.m_pimpl;\n      return *this; \n   }\n   basic_regex& assign(const charT* p, flag_type f = regex_constants::normal)\n   {\n      return assign(p, p + traits::length(p), f);\n   }\n   basic_regex& assign(const charT* p, size_type len, flag_type f)\n   {\n      return assign(p, p + len, f);\n   }\nprivate:\n   basic_regex& do_assign(const charT* p1,\n                          const charT* p2,\n                          flag_type f);\npublic:\n   basic_regex& assign(const charT* p1,\n                          const charT* p2,\n                          flag_type f = regex_constants::normal)\n   {\n      return do_assign(p1, p2, f);\n   }\n\n   template <class ST, class SA>\n   unsigned int  set_expression(const std::basic_string<charT, ST, SA>& p, flag_type f = regex_constants::normal)\n   { \n      return set_expression(p.data(), p.data() + p.size(), f); \n   }\n\n   template <class ST, class SA>\n   explicit basic_regex(const std::basic_string<charT, ST, SA>& p, flag_type f = regex_constants::normal)\n   { \n      assign(p, f); \n   }\n\n   template <class InputIterator>\n   basic_regex(InputIterator arg_first, InputIterator arg_last, flag_type f = regex_constants::normal)\n   {\n      typedef typename traits::string_type seq_type;\n      seq_type a(arg_first, arg_last);\n      if(!a.empty())\n         assign(static_cast<const charT*>(&*a.begin()), static_cast<const charT*>(&*a.begin() + a.size()), f);\n      else\n         assign(static_cast<const charT*>(0), static_cast<const charT*>(0), f);\n   }\n\n   template <class ST, class SA>\n   basic_regex&  operator=(const std::basic_string<charT, ST, SA>& p)\n   {\n      return assign(p.data(), p.data() + p.size(), regex_constants::normal);\n   }\n\n   template <class string_traits, class A>\n   basic_regex&  assign(\n       const std::basic_string<charT, string_traits, A>& s,\n       flag_type f = regex_constants::normal)\n   {\n      return assign(s.data(), s.data() + s.size(), f);\n   }\n\n   template <class InputIterator>\n   basic_regex&  assign(InputIterator arg_first,\n                          InputIterator arg_last,\n                          flag_type f = regex_constants::normal)\n   {\n      typedef typename traits::string_type seq_type;\n      seq_type a(arg_first, arg_last);\n      if(a.size())\n      {\n         const charT* p1 = &*a.begin();\n         const charT* p2 = &*a.begin() + a.size();\n         return assign(p1, p2, f);\n      }\n      return assign(static_cast<const charT*>(0), static_cast<const charT*>(0), f);\n   }\n\n   //\n   // locale:\n   locale_type  imbue(locale_type l);\n   locale_type  getloc()const\n   { \n      return m_pimpl.get() ? m_pimpl->getloc() : locale_type(); \n   }\n   //\n   // getflags:\n   // retained for backwards compatibility only, \"flags\"\n   // is now the preferred name:\n   flag_type  getflags()const\n   { \n      return flags();\n   }\n   flag_type  flags()const\n   { \n      return m_pimpl.get() ? m_pimpl->flags() : 0;\n   }\n   //\n   // str:\n   std::basic_string<charT>  str()const\n   {\n      return m_pimpl.get() ? m_pimpl->str() : std::basic_string<charT>();\n   }\n   //\n   // begin, end, subexpression:\n   std::pair<const_iterator, const_iterator>  subexpression(std::size_t n)const\n   {\n#ifdef BOOST_REGEX_STANDALONE\n      if (!m_pimpl.get())\n         throw std::logic_error(\"Can't access subexpressions in an invalid regex.\");\n#else\n      if(!m_pimpl.get())\n         boost::throw_exception(std::logic_error(\"Can't access subexpressions in an invalid regex.\"));\n#endif\n      return m_pimpl->subexpression(n);\n   }\n   const_iterator  begin()const\n   { \n      return (m_pimpl.get() ? m_pimpl->begin() : 0); \n   }\n   const_iterator  end()const\n   { \n      return (m_pimpl.get() ? m_pimpl->end() : 0); \n   }\n   //\n   // swap:\n   void  swap(basic_regex& that)throw()\n   {\n      m_pimpl.swap(that.m_pimpl);\n   }\n   //\n   // size:\n   size_type  size()const\n   { \n      return (m_pimpl.get() ? m_pimpl->size() : 0); \n   }\n   //\n   // max_size:\n   size_type  max_size()const\n   { \n      return UINT_MAX; \n   }\n   //\n   // empty:\n   bool  empty()const\n   { \n      return (m_pimpl.get() ? 0 != m_pimpl->status() : true); \n   }\n\n   size_type  mark_count()const \n   { \n      return (m_pimpl.get() ? m_pimpl->mark_count() : 0); \n   }\n\n   int status()const\n   {\n      return (m_pimpl.get() ? m_pimpl->status() : regex_constants::error_empty);\n   }\n\n   int  compare(const basic_regex& that) const\n   {\n      if(m_pimpl.get() == that.m_pimpl.get())\n         return 0;\n      if(!m_pimpl.get())\n         return -1;\n      if(!that.m_pimpl.get())\n         return 1;\n      if(status() != that.status())\n         return status() - that.status();\n      if(flags() != that.flags())\n         return flags() - that.flags();\n      return str().compare(that.str());\n   }\n   bool  operator==(const basic_regex& e)const\n   { \n      return compare(e) == 0; \n   }\n   bool  operator != (const basic_regex& e)const\n   { \n      return compare(e) != 0; \n   }\n   bool  operator<(const basic_regex& e)const\n   { \n      return compare(e) < 0; \n   }\n   bool  operator>(const basic_regex& e)const\n   { \n      return compare(e) > 0; \n   }\n   bool  operator<=(const basic_regex& e)const\n   { \n      return compare(e) <= 0; \n   }\n   bool  operator>=(const basic_regex& e)const\n   { \n      return compare(e) >= 0; \n   }\n\n   //\n   // The following are deprecated as public interfaces\n   // but are available for compatibility with earlier versions.\n   const charT*  expression()const \n   { \n      return (m_pimpl.get() && !m_pimpl->status() ? m_pimpl->expression() : 0); \n   }\n   unsigned int  set_expression(const charT* p1, const charT* p2, flag_type f = regex_constants::normal)\n   {\n      assign(p1, p2, f | regex_constants::no_except);\n      return status();\n   }\n   unsigned int  set_expression(const charT* p, flag_type f = regex_constants::normal) \n   { \n      assign(p, f | regex_constants::no_except); \n      return status();\n   }\n   unsigned int  error_code()const\n   {\n      return status();\n   }\n   //\n   // private access methods:\n   //\n   const BOOST_REGEX_DETAIL_NS::re_syntax_base* get_first_state()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->get_first_state();\n   }\n   unsigned get_restart_type()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->get_restart_type();\n   }\n   const unsigned char* get_map()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->get_map();\n   }\n   const ::boost::regex_traits_wrapper<traits>& get_traits()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->get_traits();\n   }\n   bool can_be_null()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->can_be_null();\n   }\n   const BOOST_REGEX_DETAIL_NS::regex_data<charT, traits>& get_data()const\n   {\n      BOOST_REGEX_ASSERT(0 != m_pimpl.get());\n      return m_pimpl->get_data();\n   }\n   std::shared_ptr<BOOST_REGEX_DETAIL_NS::named_subexpressions > get_named_subs()const\n   {\n      return m_pimpl;\n   }\n\nprivate:\n   std::shared_ptr<BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits> > m_pimpl;\n};\n\n//\n// out of line members;\n// these are the only members that mutate the basic_regex object,\n// and are designed to provide the strong exception guarantee\n// (in the event of a throw, the state of the object remains unchanged).\n//\ntemplate <class charT, class traits>\nbasic_regex<charT, traits>& basic_regex<charT, traits>::do_assign(const charT* p1,\n                        const charT* p2,\n                        flag_type f)\n{\n   std::shared_ptr<BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits> > temp;\n   if(!m_pimpl.get())\n   {\n      temp = std::shared_ptr<BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits> >(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits>());\n   }\n   else\n   {\n      temp = std::shared_ptr<BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits> >(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits>(m_pimpl->m_ptraits));\n   }\n   temp->assign(p1, p2, f);\n   temp.swap(m_pimpl);\n   return *this;\n}\n\ntemplate <class charT, class traits>\ntypename basic_regex<charT, traits>::locale_type  basic_regex<charT, traits>::imbue(locale_type l)\n{ \n   std::shared_ptr<BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits> > temp(new BOOST_REGEX_DETAIL_NS::basic_regex_implementation<charT, traits>());\n   locale_type result = temp->imbue(l);\n   temp.swap(m_pimpl);\n   return result;\n}\n\n//\n// non-members:\n//\ntemplate <class charT, class traits>\nvoid swap(basic_regex<charT, traits>& e1, basic_regex<charT, traits>& e2)\n{\n   e1.swap(e2);\n}\n\ntemplate <class charT, class traits, class traits2>\nstd::basic_ostream<charT, traits>& \n   operator << (std::basic_ostream<charT, traits>& os, \n                const basic_regex<charT, traits2>& e)\n{\n   return (os << e.str());\n}\n\n//\n// class reg_expression:\n// this is provided for backwards compatibility only,\n// it is deprecated, no not use!\n//\n#ifdef BOOST_REGEX_NO_FWD\ntemplate <class charT, class traits = regex_traits<charT> >\n#else\ntemplate <class charT, class traits >\n#endif\nclass reg_expression : public basic_regex<charT, traits>\n{\npublic:\n   typedef typename basic_regex<charT, traits>::flag_type flag_type;\n   typedef typename basic_regex<charT, traits>::size_type size_type;\n   explicit reg_expression(){}\n   explicit reg_expression(const charT* p, flag_type f = regex_constants::normal)\n      : basic_regex<charT, traits>(p, f){}\n   reg_expression(const charT* p1, const charT* p2, flag_type f = regex_constants::normal)\n      : basic_regex<charT, traits>(p1, p2, f){}\n   reg_expression(const charT* p, size_type len, flag_type f)\n      : basic_regex<charT, traits>(p, len, f){}\n   reg_expression(const reg_expression& that)\n      : basic_regex<charT, traits>(that) {}\n   ~reg_expression(){}\n   reg_expression&  operator=(const reg_expression& that)\n   {\n      return this->assign(that);\n   }\n\n   template <class ST, class SA>\n   explicit reg_expression(const std::basic_string<charT, ST, SA>& p, flag_type f = regex_constants::normal)\n   : basic_regex<charT, traits>(p, f)\n   { \n   }\n\n   template <class InputIterator>\n   reg_expression(InputIterator arg_first, InputIterator arg_last, flag_type f = regex_constants::normal)\n   : basic_regex<charT, traits>(arg_first, arg_last, f)\n   {\n   }\n\n   template <class ST, class SA>\n   reg_expression&  operator=(const std::basic_string<charT, ST, SA>& p)\n   {\n      this->assign(p);\n      return *this;\n   }\n\n};\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning (pop)\n#endif\n\n} // namespace boost\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/basic_regex_creator.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         basic_regex_creator.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class basic_regex_creator which fills in\n  *                the data members of a regex_data object.\n  */\n\n#ifndef BOOST_REGEX_V5_BASIC_REGEX_CREATOR_HPP\n#define BOOST_REGEX_V5_BASIC_REGEX_CREATOR_HPP\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#pragma warning(disable:4459)\n#if BOOST_REGEX_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\n#include <set>\n\nnamespace boost{\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class charT>\nstruct digraph : public std::pair<charT, charT>\n{\n   digraph() : std::pair<charT, charT>(charT(0), charT(0)){}\n   digraph(charT c1) : std::pair<charT, charT>(c1, charT(0)){}\n   digraph(charT c1, charT c2) : std::pair<charT, charT>(c1, c2)\n   {}\n   digraph(const digraph<charT>& d) : std::pair<charT, charT>(d.first, d.second){}\n   digraph<charT>& operator=(const digraph<charT>&) = default;\n   template <class Seq>\n   digraph(const Seq& s) : std::pair<charT, charT>()\n   {\n      BOOST_REGEX_ASSERT(s.size() <= 2);\n      BOOST_REGEX_ASSERT(s.size());\n      this->first = s[0];\n      this->second = (s.size() > 1) ? s[1] : 0;\n   }\n};\n\ntemplate <class charT, class traits>\nclass basic_char_set\n{\npublic:\n   typedef digraph<charT>                   digraph_type;\n   typedef typename traits::string_type     string_type;\n   typedef typename traits::char_class_type m_type;\n\n   basic_char_set()\n   {\n      m_negate = false;\n      m_has_digraphs = false;\n      m_classes = 0;\n      m_negated_classes = 0;\n      m_empty = true;\n   }\n\n   void add_single(const digraph_type& s)\n   {\n      m_singles.insert(s);\n      if(s.second)\n         m_has_digraphs = true;\n      m_empty = false;\n   }\n   void add_range(const digraph_type& first, const digraph_type& end)\n   {\n      m_ranges.push_back(first);\n      m_ranges.push_back(end);\n      if(first.second)\n      {\n         m_has_digraphs = true;\n         add_single(first);\n      }\n      if(end.second)\n      {\n         m_has_digraphs = true;\n         add_single(end);\n      }\n      m_empty = false;\n   }\n   void add_class(m_type m)\n   {\n      m_classes |= m;\n      m_empty = false;\n   }\n   void add_negated_class(m_type m)\n   {\n      m_negated_classes |= m;\n      m_empty = false;\n   }\n   void add_equivalent(const digraph_type& s)\n   {\n      m_equivalents.insert(s);\n      if(s.second)\n      {\n         m_has_digraphs = true;\n         add_single(s);\n      }\n      m_empty = false;\n   }\n   void negate()\n   { \n      m_negate = true;\n      //m_empty = false;\n   }\n\n   //\n   // accessor functions:\n   //\n   bool has_digraphs()const\n   {\n      return m_has_digraphs;\n   }\n   bool is_negated()const\n   {\n      return m_negate;\n   }\n   typedef typename std::vector<digraph_type>::const_iterator  list_iterator;\n   typedef typename std::set<digraph_type>::const_iterator     set_iterator;\n   set_iterator singles_begin()const\n   {\n      return m_singles.begin();\n   }\n   set_iterator singles_end()const\n   {\n      return m_singles.end();\n   }\n   list_iterator ranges_begin()const\n   {\n      return m_ranges.begin();\n   }\n   list_iterator ranges_end()const\n   {\n      return m_ranges.end();\n   }\n   set_iterator equivalents_begin()const\n   {\n      return m_equivalents.begin();\n   }\n   set_iterator equivalents_end()const\n   {\n      return m_equivalents.end();\n   }\n   m_type classes()const\n   {\n      return m_classes;\n   }\n   m_type negated_classes()const\n   {\n      return m_negated_classes;\n   }\n   bool empty()const\n   {\n      return m_empty;\n   }\nprivate:\n   std::set<digraph_type>    m_singles;         // a list of single characters to match\n   std::vector<digraph_type> m_ranges;          // a list of end points of our ranges\n   bool                      m_negate;          // true if the set is to be negated\n   bool                      m_has_digraphs;    // true if we have digraphs present\n   m_type                    m_classes;         // character classes to match\n   m_type                    m_negated_classes; // negated character classes to match\n   bool                      m_empty;           // whether we've added anything yet\n   std::set<digraph_type>    m_equivalents;     // a list of equivalence classes\n};\n   \ntemplate <class charT, class traits>\nclass basic_regex_creator\n{\npublic:\n   basic_regex_creator(regex_data<charT, traits>* data);\n   std::ptrdiff_t getoffset(void* addr)\n   {\n      return getoffset(addr, m_pdata->m_data.data());\n   }\n   std::ptrdiff_t getoffset(const void* addr, const void* base)\n   {\n      return static_cast<const char*>(addr) - static_cast<const char*>(base);\n   }\n   re_syntax_base* getaddress(std::ptrdiff_t off)\n   {\n      return getaddress(off, m_pdata->m_data.data());\n   }\n   re_syntax_base* getaddress(std::ptrdiff_t off, void* base)\n   {\n      return static_cast<re_syntax_base*>(static_cast<void*>(static_cast<char*>(base) + off));\n   }\n   void init(unsigned l_flags)\n   {\n      m_pdata->m_flags = l_flags;\n      m_icase = l_flags & regex_constants::icase;\n   }\n   regbase::flag_type flags()\n   {\n      return m_pdata->m_flags;\n   }\n   void flags(regbase::flag_type f)\n   {\n      m_pdata->m_flags = f;\n      if(m_icase != static_cast<bool>(f & regbase::icase))\n      {\n         m_icase = static_cast<bool>(f & regbase::icase);\n      }\n   }\n   re_syntax_base* append_state(syntax_element_type t, std::size_t s = sizeof(re_syntax_base));\n   re_syntax_base* insert_state(std::ptrdiff_t pos, syntax_element_type t, std::size_t s = sizeof(re_syntax_base));\n   re_literal* append_literal(charT c);\n   re_syntax_base* append_set(const basic_char_set<charT, traits>& char_set);\n   re_syntax_base* append_set(const basic_char_set<charT, traits>& char_set, std::integral_constant<bool, false>*);\n   re_syntax_base* append_set(const basic_char_set<charT, traits>& char_set, std::integral_constant<bool, true>*);\n   void finalize(const charT* p1, const charT* p2);\nprotected:\n   regex_data<charT, traits>*    m_pdata;              // pointer to the basic_regex_data struct we are filling in\n   const ::boost::regex_traits_wrapper<traits>&  \n                                 m_traits;             // convenience reference to traits class\n   re_syntax_base*               m_last_state;         // the last state we added\n   bool                          m_icase;              // true for case insensitive matches\n   unsigned                      m_repeater_id;        // the state_id of the next repeater\n   bool                          m_has_backrefs;       // true if there are actually any backrefs\n   std::uintmax_t                m_bad_repeats;        // bitmask of repeats we can't deduce a startmap for;\n   bool                          m_has_recursions;     // set when we have recursive expressions to fixup\n   std::vector<unsigned char>    m_recursion_checks;   // notes which recursions we've followed while analysing this expression\n   typename traits::char_class_type m_word_mask;       // mask used to determine if a character is a word character\n   typename traits::char_class_type m_mask_space;      // mask used to determine if a character is a word character\n   typename traits::char_class_type m_lower_mask;       // mask used to determine if a character is a lowercase character\n   typename traits::char_class_type m_upper_mask;      // mask used to determine if a character is an uppercase character\n   typename traits::char_class_type m_alpha_mask;      // mask used to determine if a character is an alphabetic character\nprivate:\n   basic_regex_creator& operator=(const basic_regex_creator&);\n   basic_regex_creator(const basic_regex_creator&);\n\n   void fixup_pointers(re_syntax_base* state);\n   void fixup_recursions(re_syntax_base* state);\n   void create_startmaps(re_syntax_base* state);\n   int calculate_backstep(re_syntax_base* state);\n   void create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask);\n   unsigned get_restart_type(re_syntax_base* state);\n   void set_all_masks(unsigned char* bits, unsigned char);\n   bool is_bad_repeat(re_syntax_base* pt);\n   void set_bad_repeat(re_syntax_base* pt);\n   syntax_element_type get_repeat_type(re_syntax_base* state);\n   void probe_leading_repeat(re_syntax_base* state);\n};\n\ntemplate <class charT, class traits>\nbasic_regex_creator<charT, traits>::basic_regex_creator(regex_data<charT, traits>* data)\n   : m_pdata(data), m_traits(*(data->m_ptraits)), m_last_state(0), m_icase(false), m_repeater_id(0), \n   m_has_backrefs(false), m_bad_repeats(0), m_has_recursions(false), m_word_mask(0), m_mask_space(0), m_lower_mask(0), m_upper_mask(0), m_alpha_mask(0)\n{\n   m_pdata->m_data.clear();\n   m_pdata->m_status = ::boost::regex_constants::error_ok;\n   static const charT w = 'w';\n   static const charT s = 's';\n   static const charT l[5] = { 'l', 'o', 'w', 'e', 'r', };\n   static const charT u[5] = { 'u', 'p', 'p', 'e', 'r', };\n   static const charT a[5] = { 'a', 'l', 'p', 'h', 'a', };\n   m_word_mask = m_traits.lookup_classname(&w, &w +1);\n   m_mask_space = m_traits.lookup_classname(&s, &s +1);\n   m_lower_mask = m_traits.lookup_classname(l, l + 5);\n   m_upper_mask = m_traits.lookup_classname(u, u + 5);\n   m_alpha_mask = m_traits.lookup_classname(a, a + 5);\n   m_pdata->m_word_mask = m_word_mask;\n   BOOST_REGEX_ASSERT(m_word_mask != 0); \n   BOOST_REGEX_ASSERT(m_mask_space != 0); \n   BOOST_REGEX_ASSERT(m_lower_mask != 0); \n   BOOST_REGEX_ASSERT(m_upper_mask != 0); \n   BOOST_REGEX_ASSERT(m_alpha_mask != 0); \n}\n\ntemplate <class charT, class traits>\nre_syntax_base* basic_regex_creator<charT, traits>::append_state(syntax_element_type t, std::size_t s)\n{\n   // if the state is a backref then make a note of it:\n   if(t == syntax_element_backref)\n      this->m_has_backrefs = true;\n   // append a new state, start by aligning our last one:\n   m_pdata->m_data.align();\n   // set the offset to the next state in our last one:\n   if(m_last_state)\n      m_last_state->next.i = m_pdata->m_data.size() - getoffset(m_last_state);\n   // now actually extend our data:\n   m_last_state = static_cast<re_syntax_base*>(m_pdata->m_data.extend(s));\n   // fill in boilerplate options in the new state:\n   m_last_state->next.i = 0;\n   m_last_state->type = t;\n   return m_last_state;\n}\n\ntemplate <class charT, class traits>\nre_syntax_base* basic_regex_creator<charT, traits>::insert_state(std::ptrdiff_t pos, syntax_element_type t, std::size_t s)\n{\n   // append a new state, start by aligning our last one:\n   m_pdata->m_data.align();\n   // set the offset to the next state in our last one:\n   if(m_last_state)\n      m_last_state->next.i = m_pdata->m_data.size() - getoffset(m_last_state);\n   // remember the last state position:\n   std::ptrdiff_t off = getoffset(m_last_state) + s;\n   // now actually insert our data:\n   re_syntax_base* new_state = static_cast<re_syntax_base*>(m_pdata->m_data.insert(pos, s));\n   // fill in boilerplate options in the new state:\n   new_state->next.i = s;\n   new_state->type = t;\n   m_last_state = getaddress(off);\n   return new_state;\n}\n\ntemplate <class charT, class traits>\nre_literal* basic_regex_creator<charT, traits>::append_literal(charT c)\n{\n   re_literal* result;\n   // start by seeing if we have an existing re_literal we can extend:\n   if((0 == m_last_state) || (m_last_state->type != syntax_element_literal))\n   {\n      // no existing re_literal, create a new one:\n      result = static_cast<re_literal*>(append_state(syntax_element_literal, sizeof(re_literal) + sizeof(charT)));\n      result->length = 1;\n      *static_cast<charT*>(static_cast<void*>(result+1)) = m_traits.translate(c, m_icase);\n   }\n   else\n   {\n      // we have an existing re_literal, extend it:\n      std::ptrdiff_t off = getoffset(m_last_state);\n      m_pdata->m_data.extend(sizeof(charT));\n      m_last_state = result = static_cast<re_literal*>(getaddress(off));\n      charT* characters = static_cast<charT*>(static_cast<void*>(result+1));\n      characters[result->length] = m_traits.translate(c, m_icase);\n      result->length += 1;\n   }\n   return result;\n}\n\ntemplate <class charT, class traits>\ninline re_syntax_base* basic_regex_creator<charT, traits>::append_set(\n   const basic_char_set<charT, traits>& char_set)\n{\n   typedef std::integral_constant<bool, (sizeof(charT) == 1) > truth_type;\n   return char_set.has_digraphs() \n      ? append_set(char_set, static_cast<std::integral_constant<bool, false>*>(0))\n      : append_set(char_set, static_cast<truth_type*>(0));\n}\n\ntemplate <class charT, class traits>\nre_syntax_base* basic_regex_creator<charT, traits>::append_set(\n   const basic_char_set<charT, traits>& char_set, std::integral_constant<bool, false>*)\n{\n   typedef typename traits::string_type string_type;\n   typedef typename basic_char_set<charT, traits>::list_iterator item_iterator;\n   typedef typename basic_char_set<charT, traits>::set_iterator  set_iterator;\n   typedef typename traits::char_class_type m_type;\n   \n   re_set_long<m_type>* result = static_cast<re_set_long<m_type>*>(append_state(syntax_element_long_set, sizeof(re_set_long<m_type>)));\n   //\n   // fill in the basics:\n   //\n   result->csingles = static_cast<unsigned int>(std::distance(char_set.singles_begin(), char_set.singles_end()));\n   result->cranges = static_cast<unsigned int>(std::distance(char_set.ranges_begin(), char_set.ranges_end())) / 2;\n   result->cequivalents = static_cast<unsigned int>(std::distance(char_set.equivalents_begin(), char_set.equivalents_end()));\n   result->cclasses = char_set.classes();\n   result->cnclasses = char_set.negated_classes();\n   if(flags() & regbase::icase)\n   {\n      // adjust classes as needed:\n      if(((result->cclasses & m_lower_mask) == m_lower_mask) || ((result->cclasses & m_upper_mask) == m_upper_mask))\n         result->cclasses |= m_alpha_mask;\n      if(((result->cnclasses & m_lower_mask) == m_lower_mask) || ((result->cnclasses & m_upper_mask) == m_upper_mask))\n         result->cnclasses |= m_alpha_mask;\n   }\n\n   result->isnot = char_set.is_negated();\n   result->singleton = !char_set.has_digraphs();\n   //\n   // remember where the state is for later:\n   //\n   std::ptrdiff_t offset = getoffset(result);\n   //\n   // now extend with all the singles:\n   //\n   item_iterator first, last;\n   set_iterator sfirst, slast;\n   sfirst = char_set.singles_begin();\n   slast = char_set.singles_end();\n   while(sfirst != slast)\n   {\n      charT* p = static_cast<charT*>(this->m_pdata->m_data.extend(sizeof(charT) * (sfirst->first == static_cast<charT>(0) ? 1 : sfirst->second ? 3 : 2)));\n      p[0] = m_traits.translate(sfirst->first, m_icase);\n      if(sfirst->first == static_cast<charT>(0))\n      {\n         p[0] = 0;\n      }\n      else if(sfirst->second)\n      {\n         p[1] = m_traits.translate(sfirst->second, m_icase);\n         p[2] = 0;\n      }\n      else\n         p[1] = 0;\n      ++sfirst;\n   }\n   //\n   // now extend with all the ranges:\n   //\n   first = char_set.ranges_begin();\n   last = char_set.ranges_end();\n   while(first != last)\n   {\n      // first grab the endpoints of the range:\n      digraph<charT> c1 = *first;\n      c1.first = this->m_traits.translate(c1.first, this->m_icase);\n      c1.second = this->m_traits.translate(c1.second, this->m_icase);\n      ++first;\n      digraph<charT> c2 = *first;\n      c2.first = this->m_traits.translate(c2.first, this->m_icase);\n      c2.second = this->m_traits.translate(c2.second, this->m_icase);\n      ++first;\n      string_type s1, s2;\n      // different actions now depending upon whether collation is turned on:\n      if(flags() & regex_constants::collate)\n      {\n         // we need to transform our range into sort keys:\n         charT a1[3] = { c1.first, c1.second, charT(0), };\n         charT a2[3] = { c2.first, c2.second, charT(0), };\n         s1 = this->m_traits.transform(a1, (a1[1] ? a1+2 : a1+1));\n         s2 = this->m_traits.transform(a2, (a2[1] ? a2+2 : a2+1));\n         if(s1.empty())\n            s1 = string_type(1, charT(0));\n         if(s2.empty())\n            s2 = string_type(1, charT(0));\n      }\n      else\n      {\n         if(c1.second)\n         {\n            s1.insert(s1.end(), c1.first);\n            s1.insert(s1.end(), c1.second);\n         }\n         else\n            s1 = string_type(1, c1.first);\n         if(c2.second)\n         {\n            s2.insert(s2.end(), c2.first);\n            s2.insert(s2.end(), c2.second);\n         }\n         else\n            s2.insert(s2.end(), c2.first);\n      }\n      if(s1 > s2)\n      {\n         // Oops error:\n         return 0;\n      }\n      charT* p = static_cast<charT*>(this->m_pdata->m_data.extend(sizeof(charT) * (s1.size() + s2.size() + 2) ) );\n      BOOST_REGEX_DETAIL_NS::copy(s1.begin(), s1.end(), p);\n      p[s1.size()] = charT(0);\n      p += s1.size() + 1;\n      BOOST_REGEX_DETAIL_NS::copy(s2.begin(), s2.end(), p);\n      p[s2.size()] = charT(0);\n   }\n   //\n   // now process the equivalence classes:\n   //\n   sfirst = char_set.equivalents_begin();\n   slast = char_set.equivalents_end();\n   while(sfirst != slast)\n   {\n      string_type s;\n      if(sfirst->second)\n      {\n         charT cs[3] = { sfirst->first, sfirst->second, charT(0), };\n         s = m_traits.transform_primary(cs, cs+2);\n      }\n      else\n         s = m_traits.transform_primary(&sfirst->first, &sfirst->first+1);\n      if(s.empty())\n         return 0;  // invalid or unsupported equivalence class\n      charT* p = static_cast<charT*>(this->m_pdata->m_data.extend(sizeof(charT) * (s.size()+1) ) );\n      BOOST_REGEX_DETAIL_NS::copy(s.begin(), s.end(), p);\n      p[s.size()] = charT(0);\n      ++sfirst;\n   }\n   //\n   // finally reset the address of our last state:\n   //\n   m_last_state = result = static_cast<re_set_long<m_type>*>(getaddress(offset));\n   return result;\n}\n\ntemplate<class T>\ninline bool char_less(T t1, T t2)\n{\n   return t1 < t2;\n}\ninline bool char_less(char t1, char t2)\n{\n   return static_cast<unsigned char>(t1) < static_cast<unsigned char>(t2);\n}\ninline bool char_less(signed char t1, signed char t2)\n{\n   return static_cast<unsigned char>(t1) < static_cast<unsigned char>(t2);\n}\n\ntemplate <class charT, class traits>\nre_syntax_base* basic_regex_creator<charT, traits>::append_set(\n   const basic_char_set<charT, traits>& char_set, std::integral_constant<bool, true>*)\n{\n   typedef typename traits::string_type string_type;\n   typedef typename basic_char_set<charT, traits>::list_iterator item_iterator;\n   typedef typename basic_char_set<charT, traits>::set_iterator set_iterator;\n\n   re_set* result = static_cast<re_set*>(append_state(syntax_element_set, sizeof(re_set)));\n   bool negate = char_set.is_negated();\n   std::memset(result->_map, 0, sizeof(result->_map));\n   //\n   // handle singles first:\n   //\n   item_iterator first, last;\n   set_iterator sfirst, slast;\n   sfirst = char_set.singles_begin();\n   slast = char_set.singles_end();\n   while(sfirst != slast)\n   {\n      for(unsigned int i = 0; i < (1 << CHAR_BIT); ++i)\n      {\n         if(this->m_traits.translate(static_cast<charT>(i), this->m_icase)\n            == this->m_traits.translate(sfirst->first, this->m_icase))\n            result->_map[i] = true;\n      }\n      ++sfirst;\n   }\n   //\n   // OK now handle ranges:\n   //\n   first = char_set.ranges_begin();\n   last = char_set.ranges_end();\n   while(first != last)\n   {\n      // first grab the endpoints of the range:\n      charT c1 = this->m_traits.translate(first->first, this->m_icase);\n      ++first;\n      charT c2 = this->m_traits.translate(first->first, this->m_icase);\n      ++first;\n      // different actions now depending upon whether collation is turned on:\n      if(flags() & regex_constants::collate)\n      {\n         // we need to transform our range into sort keys:\n         charT c3[2] = { c1, charT(0), };\n         string_type s1 = this->m_traits.transform(c3, c3+1);\n         c3[0] = c2;\n         string_type s2 = this->m_traits.transform(c3, c3+1);\n         if(s1 > s2)\n         {\n            // Oops error:\n            return 0;\n         }\n         BOOST_REGEX_ASSERT(c3[1] == charT(0));\n         for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n         {\n            c3[0] = static_cast<charT>(i);\n            string_type s3 = this->m_traits.transform(c3, c3 +1);\n            if((s1 <= s3) && (s3 <= s2))\n               result->_map[i] = true;\n         }\n      }\n      else\n      {\n         if(char_less(c2, c1))\n         {\n            // Oops error:\n            return 0;\n         }\n         // everything in range matches:\n         std::memset(result->_map + static_cast<unsigned char>(c1), true, static_cast<unsigned char>(1u) + static_cast<unsigned char>(static_cast<unsigned char>(c2) - static_cast<unsigned char>(c1)));\n      }\n   }\n   //\n   // and now the classes:\n   //\n   typedef typename traits::char_class_type m_type;\n   m_type m = char_set.classes();\n   if(flags() & regbase::icase)\n   {\n      // adjust m as needed:\n      if(((m & m_lower_mask) == m_lower_mask) || ((m & m_upper_mask) == m_upper_mask))\n         m |= m_alpha_mask;\n   }\n   if(m != 0)\n   {\n      for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n      {\n         if(this->m_traits.isctype(static_cast<charT>(i), m))\n            result->_map[i] = true;\n      }\n   }\n   //\n   // and now the negated classes:\n   //\n   m = char_set.negated_classes();\n   if(flags() & regbase::icase)\n   {\n      // adjust m as needed:\n      if(((m & m_lower_mask) == m_lower_mask) || ((m & m_upper_mask) == m_upper_mask))\n         m |= m_alpha_mask;\n   }\n   if(m != 0)\n   {\n      for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n      {\n         if(0 == this->m_traits.isctype(static_cast<charT>(i), m))\n            result->_map[i] = true;\n      }\n   }\n   //\n   // now process the equivalence classes:\n   //\n   sfirst = char_set.equivalents_begin();\n   slast = char_set.equivalents_end();\n   while(sfirst != slast)\n   {\n      string_type s;\n      BOOST_REGEX_ASSERT(static_cast<charT>(0) == sfirst->second);\n      s = m_traits.transform_primary(&sfirst->first, &sfirst->first+1);\n      if(s.empty())\n         return 0;  // invalid or unsupported equivalence class\n      for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n      {\n         charT c[2] = { (static_cast<charT>(i)), charT(0), };\n         string_type s2 = this->m_traits.transform_primary(c, c+1);\n         if(s == s2)\n            result->_map[i] = true;\n      }\n      ++sfirst;\n   }\n   if(negate)\n   {\n      for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n      {\n         result->_map[i] = !(result->_map[i]);\n      }\n   }\n   return result;\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::finalize(const charT* p1, const charT* p2)\n{\n   if(this->m_pdata->m_status)\n      return;\n   // we've added all the states we need, now finish things off.\n   // start by adding a terminating state:\n   append_state(syntax_element_match);\n   // extend storage to store original expression:\n   std::ptrdiff_t len = p2 - p1;\n   m_pdata->m_expression_len = len;\n   charT* ps = static_cast<charT*>(m_pdata->m_data.extend(sizeof(charT) * (1 + (p2 - p1))));\n   m_pdata->m_expression = ps;\n   BOOST_REGEX_DETAIL_NS::copy(p1, p2, ps);\n   ps[p2 - p1] = 0;\n   // fill in our other data...\n   // successful parsing implies a zero status:\n   m_pdata->m_status = 0;\n   // get the first state of the machine:\n   m_pdata->m_first_state = static_cast<re_syntax_base*>(m_pdata->m_data.data());\n   // fixup pointers in the machine:\n   fixup_pointers(m_pdata->m_first_state);\n   if(m_has_recursions)\n   {\n      m_pdata->m_has_recursions = true;\n      fixup_recursions(m_pdata->m_first_state);\n      if(this->m_pdata->m_status)\n         return;\n   }\n   else\n      m_pdata->m_has_recursions = false;\n   // create nested startmaps:\n   create_startmaps(m_pdata->m_first_state);\n   // create main startmap:\n   std::memset(m_pdata->m_startmap, 0, sizeof(m_pdata->m_startmap));\n   m_pdata->m_can_be_null = 0;\n\n   m_bad_repeats = 0;\n   if(m_has_recursions)\n      m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u);\n   create_startmap(m_pdata->m_first_state, m_pdata->m_startmap, &(m_pdata->m_can_be_null), mask_all);\n   // get the restart type:\n   m_pdata->m_restart_type = get_restart_type(m_pdata->m_first_state);\n   // optimise a leading repeat if there is one:\n   probe_leading_repeat(m_pdata->m_first_state);\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::fixup_pointers(re_syntax_base* state)\n{\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_recurse:\n         m_has_recursions = true;\n         if(state->next.i)\n            state->next.p = getaddress(state->next.i, state);\n         else\n            state->next.p = 0;\n         break;\n      case syntax_element_rep:\n      case syntax_element_dot_rep:\n      case syntax_element_char_rep:\n      case syntax_element_short_set_rep:\n      case syntax_element_long_set_rep:\n         // set the state_id of this repeat:\n         static_cast<re_repeat*>(state)->state_id = m_repeater_id++;\n         BOOST_REGEX_FALLTHROUGH;\n      case syntax_element_alt:\n         std::memset(static_cast<re_alt*>(state)->_map, 0, sizeof(static_cast<re_alt*>(state)->_map));\n         static_cast<re_alt*>(state)->can_be_null = 0;\n         BOOST_REGEX_FALLTHROUGH;\n      case syntax_element_jump:\n         static_cast<re_jump*>(state)->alt.p = getaddress(static_cast<re_jump*>(state)->alt.i, state);\n         BOOST_REGEX_FALLTHROUGH;\n      default:\n         if(state->next.i)\n            state->next.p = getaddress(state->next.i, state);\n         else\n            state->next.p = 0;\n      }\n      state = state->next.p;\n   }\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::fixup_recursions(re_syntax_base* state)\n{\n   re_syntax_base* base = state;\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_assert_backref:\n         {\n            // just check that the index is valid:\n            int idx = static_cast<const re_brace*>(state)->index;\n            if(idx < 0)\n            {\n               idx = -idx-1;\n               if(idx >= hash_value_mask)\n               {\n                  idx = m_pdata->get_id(idx);\n                  if(idx <= 0)\n                  {\n                     // check of sub-expression that doesn't exist:\n                     if(0 == this->m_pdata->m_status) // update the error code if not already set\n                        this->m_pdata->m_status = boost::regex_constants::error_bad_pattern;\n                     //\n                     // clear the expression, we should be empty:\n                     //\n                     this->m_pdata->m_expression = 0;\n                     this->m_pdata->m_expression_len = 0;\n                     //\n                     // and throw if required:\n                     //\n                     if(0 == (this->flags() & regex_constants::no_except))\n                     {\n                        std::string message = \"Encountered a forward reference to a marked sub-expression that does not exist.\";\n                        boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0);\n                        e.raise();\n                     }\n                  }\n               }\n            }\n         }\n         break;\n      case syntax_element_recurse:\n         {\n            bool ok = false;\n            re_syntax_base* p = base;\n            std::ptrdiff_t idx = static_cast<re_jump*>(state)->alt.i;\n            if(idx >= hash_value_mask)\n            {\n               //\n               // There may be more than one capture group with this hash, just do what Perl\n               // does and recurse to the leftmost:\n               //\n               idx = m_pdata->get_id(static_cast<int>(idx));\n            }\n            if(idx < 0)\n            {\n               ok = false;\n            }\n            else\n            {\n               while(p)\n               {\n                  if((p->type == syntax_element_startmark) && (static_cast<re_brace*>(p)->index == idx))\n                  {\n                     //\n                     // We've found the target of the recursion, set the jump target:\n                     //\n                     static_cast<re_jump*>(state)->alt.p = p;\n                     ok = true;\n                     // \n                     // Now scan the target for nested repeats:\n                     //\n                     p = p->next.p;\n                     int next_rep_id = 0;\n                     while(p)\n                     {\n                        switch(p->type)\n                        {\n                        case syntax_element_rep:\n                        case syntax_element_dot_rep:\n                        case syntax_element_char_rep:\n                        case syntax_element_short_set_rep:\n                        case syntax_element_long_set_rep:\n                           next_rep_id = static_cast<re_repeat*>(p)->state_id;\n                           break;\n                        case syntax_element_endmark:\n                           if(static_cast<const re_brace*>(p)->index == idx)\n                              next_rep_id = -1;\n                           break;\n                        default:\n                           break;\n                        }\n                        if(next_rep_id)\n                           break;\n                        p = p->next.p;\n                     }\n                     if(next_rep_id > 0)\n                     {\n                        static_cast<re_recurse*>(state)->state_id = next_rep_id - 1;\n                     }\n\n                     break;\n                  }\n                  p = p->next.p;\n               }\n            }\n            if(!ok)\n            {\n               // recursion to sub-expression that doesn't exist:\n               if(0 == this->m_pdata->m_status) // update the error code if not already set\n                  this->m_pdata->m_status = boost::regex_constants::error_bad_pattern;\n               //\n               // clear the expression, we should be empty:\n               //\n               this->m_pdata->m_expression = 0;\n               this->m_pdata->m_expression_len = 0;\n               //\n               // and throw if required:\n               //\n               if(0 == (this->flags() & regex_constants::no_except))\n               {\n                  std::string message = \"Encountered a forward reference to a recursive sub-expression that does not exist.\";\n                  boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0);\n                  e.raise();\n               }\n            }\n         }\n         break;\n      default:\n         break;\n      }\n      state = state->next.p;\n   }\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::create_startmaps(re_syntax_base* state)\n{\n   // non-recursive implementation:\n   // create the last map in the machine first, so that earlier maps\n   // can make use of the result...\n   //\n   // This was originally a recursive implementation, but that caused stack\n   // overflows with complex expressions on small stacks (think COM+).\n\n   // start by saving the case setting:\n   bool l_icase = m_icase;\n   std::vector<std::pair<bool, re_syntax_base*> > v;\n\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_toggle_case:\n         // we need to track case changes here:\n         m_icase = static_cast<re_case*>(state)->icase;\n         state = state->next.p;\n         continue;\n      case syntax_element_alt:\n      case syntax_element_rep:\n      case syntax_element_dot_rep:\n      case syntax_element_char_rep:\n      case syntax_element_short_set_rep:\n      case syntax_element_long_set_rep:\n         // just push the state onto our stack for now:\n         v.push_back(std::pair<bool, re_syntax_base*>(m_icase, state));\n         state = state->next.p;\n         break;\n      case syntax_element_backstep:\n         // we need to calculate how big the backstep is:\n         static_cast<re_brace*>(state)->index\n            = this->calculate_backstep(state->next.p);\n         if(static_cast<re_brace*>(state)->index < 0)\n         {\n            // Oops error:\n            if(0 == this->m_pdata->m_status) // update the error code if not already set\n               this->m_pdata->m_status = boost::regex_constants::error_bad_pattern;\n            //\n            // clear the expression, we should be empty:\n            //\n            this->m_pdata->m_expression = 0;\n            this->m_pdata->m_expression_len = 0;\n            //\n            // and throw if required:\n            //\n            if(0 == (this->flags() & regex_constants::no_except))\n            {\n               std::string message = \"Invalid lookbehind assertion encountered in the regular expression.\";\n               boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0);\n               e.raise();\n            }\n         }\n         BOOST_REGEX_FALLTHROUGH;\n      default:\n         state = state->next.p;\n      }\n   }\n\n   // now work through our list, building all the maps as we go:\n   while(!v.empty())\n   {\n      // Initialize m_recursion_checks if we need it:\n      if(m_has_recursions)\n         m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u);\n\n      const std::pair<bool, re_syntax_base*>& p = v.back();\n      m_icase = p.first;\n      state = p.second;\n      v.pop_back();\n\n      // Build maps:\n      m_bad_repeats = 0;\n      create_startmap(state->next.p, static_cast<re_alt*>(state)->_map, &static_cast<re_alt*>(state)->can_be_null, mask_take);\n      m_bad_repeats = 0;\n\n      if(m_has_recursions)\n         m_recursion_checks.assign(1 + m_pdata->m_mark_count, 0u);\n      create_startmap(static_cast<re_alt*>(state)->alt.p, static_cast<re_alt*>(state)->_map, &static_cast<re_alt*>(state)->can_be_null, mask_skip);\n      // adjust the type of the state to allow for faster matching:\n      state->type = this->get_repeat_type(state);\n   }\n   // restore case sensitivity:\n   m_icase = l_icase;\n}\n\ntemplate <class charT, class traits>\nint basic_regex_creator<charT, traits>::calculate_backstep(re_syntax_base* state)\n{\n   typedef typename traits::char_class_type m_type;\n   int result = 0;\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_startmark:\n         if((static_cast<re_brace*>(state)->index == -1)\n            || (static_cast<re_brace*>(state)->index == -2))\n         {\n            state = static_cast<re_jump*>(state->next.p)->alt.p->next.p;\n            continue;\n         }\n         else if(static_cast<re_brace*>(state)->index == -3)\n         {\n            state = state->next.p->next.p;\n            continue;\n         }\n         break;\n      case syntax_element_endmark:\n         if((static_cast<re_brace*>(state)->index == -1)\n            || (static_cast<re_brace*>(state)->index == -2))\n            return result;\n         break;\n      case syntax_element_literal:\n         result += static_cast<re_literal*>(state)->length;\n         break;\n      case syntax_element_wild:\n      case syntax_element_set:\n         result += 1;\n         break;\n      case syntax_element_dot_rep:\n      case syntax_element_char_rep:\n      case syntax_element_short_set_rep:\n      case syntax_element_backref:\n      case syntax_element_rep:\n      case syntax_element_combining:\n      case syntax_element_long_set_rep:\n      case syntax_element_backstep:\n         {\n            re_repeat* rep = static_cast<re_repeat *>(state);\n            // adjust the type of the state to allow for faster matching:\n            state->type = this->get_repeat_type(state);\n            if((state->type == syntax_element_dot_rep) \n               || (state->type == syntax_element_char_rep)\n               || (state->type == syntax_element_short_set_rep))\n            {\n               if(rep->max != rep->min)\n                  return -1;\n               if (static_cast<std::size_t>((std::numeric_limits<int>::max)() - result) < rep->min)\n                  return -1; // protection against overflow, we can't calculate a backstep in this case and the expression is probably ill-formed.\n               result += static_cast<int>(rep->min);\n               state = rep->alt.p;\n               continue;\n            }\n            else if(state->type == syntax_element_long_set_rep)\n            {\n               BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_long_set);\n               if(static_cast<re_set_long<m_type>*>(rep->next.p)->singleton == 0)\n                  return -1;\n               if(rep->max != rep->min)\n                  return -1;\n               result += static_cast<int>(rep->min);\n               state = rep->alt.p;\n               continue;\n            }\n         }\n         return -1;\n      case syntax_element_long_set:\n         if(static_cast<re_set_long<m_type>*>(state)->singleton == 0)\n            return -1;\n         result += 1;\n         break;\n      case syntax_element_jump:\n         state = static_cast<re_jump*>(state)->alt.p;\n         continue;\n      case syntax_element_alt:\n         {\n            int r1 = calculate_backstep(state->next.p);\n            int r2 = calculate_backstep(static_cast<re_alt*>(state)->alt.p);\n            if((r1 < 0) || (r1 != r2))\n               return -1;\n            return result + r1;\n         }\n      default:\n         break;\n      }\n      state = state->next.p;\n   }\n   return -1;\n}\n\nstruct recursion_saver\n{\n   std::vector<unsigned char> saved_state;\n   std::vector<unsigned char>* state;\n   recursion_saver(std::vector<unsigned char>* p) : saved_state(*p), state(p) {}\n   ~recursion_saver()\n   {\n      state->swap(saved_state);\n   }\n};\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::create_startmap(re_syntax_base* state, unsigned char* l_map, unsigned int* pnull, unsigned char mask)\n{\n   recursion_saver saved_recursions(&m_recursion_checks);\n   int not_last_jump = 1;\n   re_syntax_base* recursion_start = 0;\n   int recursion_sub = 0;\n   re_syntax_base* recursion_restart = 0;\n\n   // track case sensitivity:\n   bool l_icase = m_icase;\n\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_toggle_case:\n         l_icase = static_cast<re_case*>(state)->icase;\n         state = state->next.p;\n         break;\n      case syntax_element_literal:\n      {\n         // don't set anything in *pnull, set each element in l_map\n         // that could match the first character in the literal:\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            charT first_char = *static_cast<charT*>(static_cast<void*>(static_cast<re_literal*>(state) + 1));\n            for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i)\n            {\n               if(m_traits.translate(static_cast<charT>(i), l_icase) == first_char)\n                  l_map[i] |= mask;\n            }\n         }\n         return;\n      }\n      case syntax_element_end_line:\n      {\n         // next character must be a line separator (if there is one):\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            l_map[static_cast<unsigned>('\\n')] |= mask;\n            l_map[static_cast<unsigned>('\\r')] |= mask;\n            l_map[static_cast<unsigned>('\\f')] |= mask;\n            l_map[0x85] |= mask;\n         }\n         // now figure out if we can match a NULL string at this point:\n         if(pnull)\n            create_startmap(state->next.p, 0, pnull, mask);\n         return;\n      }\n      case syntax_element_recurse:\n         {\n            BOOST_REGEX_ASSERT(static_cast<const re_jump*>(state)->alt.p->type == syntax_element_startmark);\n            recursion_sub = static_cast<re_brace*>(static_cast<const re_jump*>(state)->alt.p)->index;\n            if(m_recursion_checks[recursion_sub] & 1u)\n            {\n               // Infinite recursion!!\n               if(0 == this->m_pdata->m_status) // update the error code if not already set\n                  this->m_pdata->m_status = boost::regex_constants::error_bad_pattern;\n               //\n               // clear the expression, we should be empty:\n               //\n               this->m_pdata->m_expression = 0;\n               this->m_pdata->m_expression_len = 0;\n               //\n               // and throw if required:\n               //\n               if(0 == (this->flags() & regex_constants::no_except))\n               {\n                  std::string message = \"Encountered an infinite recursion.\";\n                  boost::regex_error e(message, boost::regex_constants::error_bad_pattern, 0);\n                  e.raise();\n               }\n            }\n            else if(recursion_start == 0)\n            {\n               recursion_start = state;\n               recursion_restart = state->next.p;\n               state = static_cast<re_jump*>(state)->alt.p;\n               m_recursion_checks[recursion_sub] |= 1u;\n               break;\n            }\n            m_recursion_checks[recursion_sub] |= 1u;\n            // can't handle nested recursion here...\n            BOOST_REGEX_FALLTHROUGH;\n         }\n      case syntax_element_backref:\n         // can be null, and any character can match:\n         if(pnull)\n            *pnull |= mask;\n         BOOST_REGEX_FALLTHROUGH;\n      case syntax_element_wild:\n      {\n         // can't be null, any character can match:\n         set_all_masks(l_map, mask);\n         return;\n      }\n      case syntax_element_accept:\n      case syntax_element_match:\n      {\n         // must be null, any character can match:\n         set_all_masks(l_map, mask);\n         if(pnull)\n            *pnull |= mask;\n         return;\n      }\n      case syntax_element_word_start:\n      {\n         // recurse, then AND with all the word characters:\n         create_startmap(state->next.p, l_map, pnull, mask);\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i)\n            {\n               if(!m_traits.isctype(static_cast<charT>(i), m_word_mask))\n                  l_map[i] &= static_cast<unsigned char>(~mask);\n            }\n         }\n         return;\n      }\n      case syntax_element_word_end:\n      {\n         // recurse, then AND with all the word characters:\n         create_startmap(state->next.p, l_map, pnull, mask);\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i)\n            {\n               if(m_traits.isctype(static_cast<charT>(i), m_word_mask))\n                  l_map[i] &= static_cast<unsigned char>(~mask);\n            }\n         }\n         return;\n      }\n      case syntax_element_buffer_end:\n      {\n         // we *must be null* :\n         if(pnull)\n            *pnull |= mask;\n         return;\n      }\n      case syntax_element_long_set:\n         if(l_map)\n         {\n            typedef typename traits::char_class_type m_type;\n            if(static_cast<re_set_long<m_type>*>(state)->singleton)\n            {\n               l_map[0] |= mask_init;\n               for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i)\n               {\n                  charT c = static_cast<charT>(i);\n                  if(&c != re_is_set_member(&c, &c + 1, static_cast<re_set_long<m_type>*>(state), *m_pdata, l_icase))\n                     l_map[i] |= mask;\n               }\n            }\n            else\n               set_all_masks(l_map, mask);\n         }\n         return;\n      case syntax_element_set:\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            for(unsigned int i = 0; i < (1u << CHAR_BIT); ++i)\n            {\n               if(static_cast<re_set*>(state)->_map[\n                  static_cast<unsigned char>(m_traits.translate(static_cast<charT>(i), l_icase))])\n                  l_map[i] |= mask;\n            }\n         }\n         return;\n      case syntax_element_jump:\n         // take the jump:\n         state = static_cast<re_alt*>(state)->alt.p;\n         not_last_jump = -1;\n         break;\n      case syntax_element_alt:\n      case syntax_element_rep:\n      case syntax_element_dot_rep:\n      case syntax_element_char_rep:\n      case syntax_element_short_set_rep:\n      case syntax_element_long_set_rep:\n         {\n            re_alt* rep = static_cast<re_alt*>(state);\n            if(rep->_map[0] & mask_init)\n            {\n               if(l_map)\n               {\n                  // copy previous results:\n                  l_map[0] |= mask_init;\n                  for(unsigned int i = 0; i <= UCHAR_MAX; ++i)\n                  {\n                     if(rep->_map[i] & mask_any)\n                        l_map[i] |= mask;\n                  }\n               }\n               if(pnull)\n               {\n                  if(rep->can_be_null & mask_any)\n                     *pnull |= mask;\n               }\n            }\n            else\n            {\n               // we haven't created a startmap for this alternative yet\n               // so take the union of the two options:\n               if(is_bad_repeat(state))\n               {\n                  set_all_masks(l_map, mask);\n                  if(pnull)\n                     *pnull |= mask;\n                  return;\n               }\n               set_bad_repeat(state);\n               create_startmap(state->next.p, l_map, pnull, mask);\n               if((state->type == syntax_element_alt)\n                  || (static_cast<re_repeat*>(state)->min == 0)\n                  || (not_last_jump == 0))\n                  create_startmap(rep->alt.p, l_map, pnull, mask);\n            }\n         }\n         return;\n      case syntax_element_soft_buffer_end:\n         // match newline or null:\n         if(l_map)\n         {\n            l_map[0] |= mask_init;\n            l_map[static_cast<unsigned>('\\n')] |= mask;\n            l_map[static_cast<unsigned>('\\r')] |= mask;\n         }\n         if(pnull)\n            *pnull |= mask;\n         return;\n      case syntax_element_endmark:\n         // need to handle independent subs as a special case:\n         if(static_cast<re_brace*>(state)->index < 0)\n         {\n            // can be null, any character can match:\n            set_all_masks(l_map, mask);\n            if(pnull)\n               *pnull |= mask;\n            return;\n         }\n         else if(recursion_start && (recursion_sub != 0) && (recursion_sub == static_cast<re_brace*>(state)->index))\n         {\n            // recursion termination:\n            recursion_start = 0;\n            state = recursion_restart;\n            break;\n         }\n\n         //\n         // Normally we just go to the next state... but if this sub-expression is\n         // the target of a recursion, then we might be ending a recursion, in which\n         // case we should check whatever follows that recursion, as well as whatever\n         // follows this state:\n         //\n         if(m_pdata->m_has_recursions && static_cast<re_brace*>(state)->index)\n         {\n            bool ok = false;\n            re_syntax_base* p = m_pdata->m_first_state;\n            while(p)\n            {\n               if(p->type == syntax_element_recurse)\n               {\n                  re_brace* p2 = static_cast<re_brace*>(static_cast<re_jump*>(p)->alt.p);\n                  if((p2->type == syntax_element_startmark) && (p2->index == static_cast<re_brace*>(state)->index))\n                  {\n                     ok = true;\n                     break;\n                  }\n               }\n               p = p->next.p;\n            }\n            if(ok && ((m_recursion_checks[static_cast<re_brace*>(state)->index] & 2u) == 0))\n            {\n               m_recursion_checks[static_cast<re_brace*>(state)->index] |= 2u;\n               create_startmap(p->next.p, l_map, pnull, mask);\n            }\n         }\n         state = state->next.p;\n         break;\n\n      case syntax_element_commit:\n         set_all_masks(l_map, mask);\n         // Continue scanning so we can figure out whether we can be null:\n         state = state->next.p;\n         break;\n      case syntax_element_startmark:\n         // need to handle independent subs as a special case:\n         if(static_cast<re_brace*>(state)->index == -3)\n         {\n            state = state->next.p->next.p;\n            break;\n         }\n         BOOST_REGEX_FALLTHROUGH;\n      default:\n         state = state->next.p;\n      }\n      ++not_last_jump;\n   }\n}\n\ntemplate <class charT, class traits>\nunsigned basic_regex_creator<charT, traits>::get_restart_type(re_syntax_base* state)\n{\n   //\n   // find out how the machine starts, so we can optimise the search:\n   //\n   while(state)\n   {\n      switch(state->type)\n      {\n      case syntax_element_startmark:\n      case syntax_element_endmark:\n         state = state->next.p;\n         continue;\n      case syntax_element_start_line:\n         return regbase::restart_line;\n      case syntax_element_word_start:\n         return regbase::restart_word;\n      case syntax_element_buffer_start:\n         return regbase::restart_buf;\n      case syntax_element_restart_continue:\n         return regbase::restart_continue;\n      default:\n         state = 0;\n         continue;\n      }\n   }\n   return regbase::restart_any;\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::set_all_masks(unsigned char* bits, unsigned char mask)\n{\n   //\n   // set mask in all of bits elements, \n   // if bits[0] has mask_init not set then we can \n   // optimise this to a call to memset:\n   //\n   if(bits)\n   {\n      if(bits[0] == 0)\n         (std::memset)(bits, mask, 1u << CHAR_BIT);\n      else\n      {\n         for(unsigned i = 0; i < (1u << CHAR_BIT); ++i)\n            bits[i] |= mask;\n      }\n      bits[0] |= mask_init;\n   }\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_creator<charT, traits>::is_bad_repeat(re_syntax_base* pt)\n{\n   switch(pt->type)\n   {\n   case syntax_element_rep:\n   case syntax_element_dot_rep:\n   case syntax_element_char_rep:\n   case syntax_element_short_set_rep:\n   case syntax_element_long_set_rep:\n      {\n         unsigned state_id = static_cast<re_repeat*>(pt)->state_id;\n         if(state_id >= sizeof(m_bad_repeats) * CHAR_BIT)\n            return true;  // run out of bits, assume we can't traverse this one.\n         static const std::uintmax_t one = 1uL;\n         return m_bad_repeats & (one << state_id);\n      }\n   default:\n      return false;\n   }\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::set_bad_repeat(re_syntax_base* pt)\n{\n   switch(pt->type)\n   {\n   case syntax_element_rep:\n   case syntax_element_dot_rep:\n   case syntax_element_char_rep:\n   case syntax_element_short_set_rep:\n   case syntax_element_long_set_rep:\n      {\n         unsigned state_id = static_cast<re_repeat*>(pt)->state_id;\n         static const std::uintmax_t one = 1uL;\n         if(state_id <= sizeof(m_bad_repeats) * CHAR_BIT)\n            m_bad_repeats |= (one << state_id);\n      }\n      break;\n   default:\n      break;\n   }\n}\n\ntemplate <class charT, class traits>\nsyntax_element_type basic_regex_creator<charT, traits>::get_repeat_type(re_syntax_base* state)\n{\n   typedef typename traits::char_class_type m_type;\n   if(state->type == syntax_element_rep)\n   {\n      // check to see if we are repeating a single state:\n      if(state->next.p->next.p->next.p == static_cast<re_alt*>(state)->alt.p)\n      {\n         switch(state->next.p->type)\n         {\n         case BOOST_REGEX_DETAIL_NS::syntax_element_wild:\n            return BOOST_REGEX_DETAIL_NS::syntax_element_dot_rep;\n         case BOOST_REGEX_DETAIL_NS::syntax_element_literal:\n            return BOOST_REGEX_DETAIL_NS::syntax_element_char_rep;\n         case BOOST_REGEX_DETAIL_NS::syntax_element_set:\n            return BOOST_REGEX_DETAIL_NS::syntax_element_short_set_rep;\n         case BOOST_REGEX_DETAIL_NS::syntax_element_long_set:\n            if(static_cast<BOOST_REGEX_DETAIL_NS::re_set_long<m_type>*>(state->next.p)->singleton)\n               return BOOST_REGEX_DETAIL_NS::syntax_element_long_set_rep;\n            break;\n         default:\n            break;\n         }\n      }\n   }\n   return state->type;\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_creator<charT, traits>::probe_leading_repeat(re_syntax_base* state)\n{\n   // enumerate our states, and see if we have a leading repeat \n   // for which failed search restarts can be optimized;\n   do\n   {\n      switch(state->type)\n      {\n      case syntax_element_startmark:\n         if(static_cast<re_brace*>(state)->index >= 0)\n         {\n            state = state->next.p;\n            continue;\n         }\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#pragma warning(disable:6011)\n#endif\n         if((static_cast<re_brace*>(state)->index == -1)\n            || (static_cast<re_brace*>(state)->index == -2))\n         {\n            // skip past the zero width assertion:\n            state = static_cast<const re_jump*>(state->next.p)->alt.p->next.p;\n            continue;\n         }\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n         if(static_cast<re_brace*>(state)->index == -3)\n         {\n            // Have to skip the leading jump state:\n            state = state->next.p->next.p;\n            continue;\n         }\n         return;\n      case syntax_element_endmark:\n      case syntax_element_start_line:\n      case syntax_element_end_line:\n      case syntax_element_word_boundary:\n      case syntax_element_within_word:\n      case syntax_element_word_start:\n      case syntax_element_word_end:\n      case syntax_element_buffer_start:\n      case syntax_element_buffer_end:\n      case syntax_element_restart_continue:\n         state = state->next.p;\n         break;\n      case syntax_element_dot_rep:\n      case syntax_element_char_rep:\n      case syntax_element_short_set_rep:\n      case syntax_element_long_set_rep:\n         if(this->m_has_backrefs == 0)\n            static_cast<re_repeat*>(state)->leading = true;\n         BOOST_REGEX_FALLTHROUGH;\n      default:\n         return;\n      }\n   }while(state);\n}\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\n} // namespace boost\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/basic_regex_parser.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         basic_regex_parser.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class basic_regex_parser.\n  */\n\n#ifndef BOOST_REGEX_V5_BASIC_REGEX_PARSER_HPP\n#define BOOST_REGEX_V5_BASIC_REGEX_PARSER_HPP\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4244 4459)\n#if BOOST_REGEX_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\ninline std::intmax_t umax(std::integral_constant<bool, false> const&)\n{\n   // Get out clause here, just in case numeric_limits is unspecialized:\n   return std::numeric_limits<std::intmax_t>::is_specialized ? (std::numeric_limits<std::intmax_t>::max)() : INT_MAX;\n}\ninline std::intmax_t umax(std::integral_constant<bool, true> const&)\n{\n   return (std::numeric_limits<std::size_t>::max)();\n}\n\ninline std::intmax_t umax()\n{\n   return umax(std::integral_constant<bool, std::numeric_limits<std::intmax_t>::digits >= std::numeric_limits<std::size_t>::digits>());\n}\n\ntemplate <class charT, class traits>\nclass basic_regex_parser : public basic_regex_creator<charT, traits>\n{\npublic:\n   basic_regex_parser(regex_data<charT, traits>* data);\n   void parse(const charT* p1, const charT* p2, unsigned flags);\n   void fail(regex_constants::error_type error_code, std::ptrdiff_t position);\n   void fail(regex_constants::error_type error_code, std::ptrdiff_t position, std::string message, std::ptrdiff_t start_pos);\n   void fail(regex_constants::error_type error_code, std::ptrdiff_t position, const std::string& message)\n   {\n      fail(error_code, position, message, position);\n   }\n\n   bool parse_all();\n   bool parse_basic();\n   bool parse_extended();\n   bool parse_literal();\n   bool parse_open_paren();\n   bool parse_basic_escape();\n   bool parse_extended_escape();\n   bool parse_match_any();\n   bool parse_repeat(std::size_t low = 0, std::size_t high = (std::numeric_limits<std::size_t>::max)());\n   bool parse_repeat_range(bool isbasic);\n   bool parse_alt();\n   bool parse_set();\n   bool parse_backref();\n   void parse_set_literal(basic_char_set<charT, traits>& char_set);\n   bool parse_inner_set(basic_char_set<charT, traits>& char_set);\n   bool parse_QE();\n   bool parse_perl_extension();\n   bool parse_perl_verb();\n   bool match_verb(const char*);\n   bool add_emacs_code(bool negate);\n   bool unwind_alts(std::ptrdiff_t last_paren_start);\n   digraph<charT> get_next_set_literal(basic_char_set<charT, traits>& char_set);\n   charT unescape_character();\n   regex_constants::syntax_option_type parse_options();\n\nprivate:\n   typedef bool (basic_regex_parser::*parser_proc_type)();\n   typedef typename traits::string_type string_type;\n   typedef typename traits::char_class_type char_class_type;\n   parser_proc_type           m_parser_proc;    // the main parser to use\n   const charT*               m_base;           // the start of the string being parsed\n   const charT*               m_end;            // the end of the string being parsed\n   const charT*               m_position;       // our current parser position\n   unsigned                   m_mark_count;     // how many sub-expressions we have\n   int                        m_mark_reset;     // used to indicate that we're inside a (?|...) block.\n   unsigned                   m_max_mark;       // largest mark count seen inside a (?|...) block.\n   std::ptrdiff_t             m_paren_start;    // where the last seen ')' began (where repeats are inserted).\n   std::ptrdiff_t             m_alt_insert_point; // where to insert the next alternative\n   bool                       m_has_case_change; // true if somewhere in the current block the case has changed\n   unsigned                   m_recursion_count; // How many times we've called parse_all.\n   unsigned                   m_max_backref;     // Largest index of any backref.\n#if defined(BOOST_REGEX_MSVC) && defined(_M_IX86)\n   // This is an ugly warning suppression workaround (for warnings *inside* std::vector\n   // that can not otherwise be suppressed)...\n   static_assert(sizeof(long) >= sizeof(void*), \"Long isn't long enough!\");\n   std::vector<long>           m_alt_jumps;      // list of alternative in the current scope.\n#else\n   std::vector<std::ptrdiff_t> m_alt_jumps;      // list of alternative in the current scope.\n#endif\n\n   basic_regex_parser& operator=(const basic_regex_parser&);\n   basic_regex_parser(const basic_regex_parser&);\n};\n\ntemplate <class charT, class traits>\nbasic_regex_parser<charT, traits>::basic_regex_parser(regex_data<charT, traits>* data)\n   : basic_regex_creator<charT, traits>(data), m_parser_proc(), m_base(0), m_end(0), m_position(0), \n   m_mark_count(0), m_mark_reset(-1), m_max_mark(0), m_paren_start(0), m_alt_insert_point(0), m_has_case_change(false), m_recursion_count(0), m_max_backref(0)\n{\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_parser<charT, traits>::parse(const charT* p1, const charT* p2, unsigned l_flags)\n{\n   // pass l_flags on to base class:\n   this->init(l_flags);\n   // set up pointers:\n   m_position = m_base = p1;\n   m_end = p2;\n   // empty strings are errors:\n   if((p1 == p2) && \n      (\n         ((l_flags & regbase::main_option_type) != regbase::perl_syntax_group)\n         || (l_flags & regbase::no_empty_expressions)\n      )\n     )\n   {\n      fail(regex_constants::error_empty, 0);\n      return;\n   }\n   // select which parser to use:\n   switch(l_flags & regbase::main_option_type)\n   {\n   case regbase::perl_syntax_group:\n      {\n         m_parser_proc = &basic_regex_parser<charT, traits>::parse_extended;\n         //\n         // Add a leading paren with index zero to give recursions a target:\n         //\n         re_brace* br = static_cast<re_brace*>(this->append_state(syntax_element_startmark, sizeof(re_brace)));\n         br->index = 0;\n         br->icase = this->flags() & regbase::icase;\n         break;\n      }\n   case regbase::basic_syntax_group:\n      m_parser_proc = &basic_regex_parser<charT, traits>::parse_basic;\n      break;\n   case regbase::literal:\n      m_parser_proc = &basic_regex_parser<charT, traits>::parse_literal;\n      break;\n   default:\n      // Oops, someone has managed to set more than one of the main option flags, \n      // so this must be an error:\n      fail(regex_constants::error_unknown, 0, \"An invalid combination of regular expression syntax flags was used.\");\n      return;\n   }\n\n   // parse all our characters:\n   bool result = parse_all();\n   //\n   // Unwind our alternatives:\n   //\n   unwind_alts(-1);\n   // reset l_flags as a global scope (?imsx) may have altered them:\n   this->flags(l_flags);\n   // if we haven't gobbled up all the characters then we must\n   // have had an unexpected ')' :\n   if(!result)\n   {\n      fail(regex_constants::error_paren, std::distance(m_base, m_position), \"Found a closing ) with no corresponding opening parenthesis.\");\n      return;\n   }\n   // if an error has been set then give up now:\n   if(this->m_pdata->m_status)\n      return;\n   // fill in our sub-expression count:\n   this->m_pdata->m_mark_count = 1u + (std::size_t)m_mark_count;\n   //\n   // Check we don't have backreferences to sub-expressions which don't exist:\n   //\n   if (m_max_backref > m_mark_count)\n   {\n      fail(regex_constants::error_backref, std::distance(m_base, m_position), \"Found a backreference to a non-existant sub-expression.\");\n   }\n   this->finalize(p1, p2);\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_parser<charT, traits>::fail(regex_constants::error_type error_code, std::ptrdiff_t position)\n{\n   // get the error message:\n   std::string message = this->m_pdata->m_ptraits->error_string(error_code);\n   fail(error_code, position, message);\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_parser<charT, traits>::fail(regex_constants::error_type error_code, std::ptrdiff_t position, std::string message, std::ptrdiff_t start_pos)\n{\n   if(0 == this->m_pdata->m_status) // update the error code if not already set\n      this->m_pdata->m_status = error_code;\n   m_position = m_end; // don't bother parsing anything else\n\n   //\n   // Augment error message with the regular expression text:\n   //\n   if(start_pos == position)\n      start_pos = (std::max)(static_cast<std::ptrdiff_t>(0), position - static_cast<std::ptrdiff_t>(10));\n   std::ptrdiff_t end_pos = (std::min)(position + static_cast<std::ptrdiff_t>(10), static_cast<std::ptrdiff_t>(m_end - m_base));\n   if(error_code != regex_constants::error_empty)\n   {\n      if((start_pos != 0) || (end_pos != (m_end - m_base)))\n         message += \"  The error occurred while parsing the regular expression fragment: '\";\n      else\n         message += \"  The error occurred while parsing the regular expression: '\";\n      if(start_pos != end_pos)\n      {\n         message += std::string(m_base + start_pos, m_base + position);\n         message += \">>>HERE>>>\";\n         message += std::string(m_base + position, m_base + end_pos);\n      }\n      message += \"'.\";\n   }\n\n#ifndef BOOST_NO_EXCEPTIONS\n   if(0 == (this->flags() & regex_constants::no_except))\n   {\n      boost::regex_error e(message, error_code, position);\n      e.raise();\n   }\n#else\n   (void)position; // suppress warnings.\n#endif\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_all()\n{\n   if (++m_recursion_count > 400)\n   {\n      // exceeded internal limits\n      fail(boost::regex_constants::error_complexity, m_position - m_base, \"Exceeded nested brace limit.\");\n   }\n   bool result = true;\n   while(result && (m_position != m_end))\n   {\n      result = (this->*m_parser_proc)();\n   }\n   --m_recursion_count;\n   return result;\n}\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4702)\n#endif\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_basic()\n{\n   switch(this->m_traits.syntax_type(*m_position))\n   {\n   case regex_constants::syntax_escape:\n      return parse_basic_escape();\n   case regex_constants::syntax_dot:\n      return parse_match_any();\n   case regex_constants::syntax_caret:\n      ++m_position;\n      this->append_state(syntax_element_start_line);\n      break;\n   case regex_constants::syntax_dollar:\n      ++m_position;\n      this->append_state(syntax_element_end_line);\n      break;\n   case regex_constants::syntax_star:\n      if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line))\n         return parse_literal();\n      else\n      {\n         ++m_position;\n         return parse_repeat();\n      }\n   case regex_constants::syntax_plus:\n      if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line) || !(this->flags() & regbase::emacs_ex))\n         return parse_literal();\n      else\n      {\n         ++m_position;\n         return parse_repeat(1);\n      }\n   case regex_constants::syntax_question:\n      if(!(this->m_last_state) || (this->m_last_state->type == syntax_element_start_line) || !(this->flags() & regbase::emacs_ex))\n         return parse_literal();\n      else\n      {\n         ++m_position;\n         return parse_repeat(0, 1);\n      }\n   case regex_constants::syntax_open_set:\n      return parse_set();\n   case regex_constants::syntax_newline:\n      if(this->flags() & regbase::newline_alt)\n         return parse_alt();\n      else\n         return parse_literal();\n   default:\n      return parse_literal();\n   }\n   return true;\n}\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#if BOOST_REGEX_MSVC >= 1800\n#pragma warning(disable:26812)\n#endif\n#endif\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_extended()\n{\n   bool result = true;\n   switch(this->m_traits.syntax_type(*m_position))\n   {\n   case regex_constants::syntax_open_mark:\n      return parse_open_paren();\n   case regex_constants::syntax_close_mark:\n      return false;\n   case regex_constants::syntax_escape:\n      return parse_extended_escape();\n   case regex_constants::syntax_dot:\n      return parse_match_any();\n   case regex_constants::syntax_caret:\n      ++m_position;\n      this->append_state(\n         (this->flags() & regex_constants::no_mod_m ? syntax_element_buffer_start : syntax_element_start_line));\n      break;\n   case regex_constants::syntax_dollar:\n      ++m_position;\n      this->append_state(\n         (this->flags() & regex_constants::no_mod_m ? syntax_element_buffer_end : syntax_element_end_line));\n      break;\n   case regex_constants::syntax_star:\n      if(m_position == this->m_base)\n      {\n         fail(regex_constants::error_badrepeat, 0, \"The repeat operator \\\"*\\\" cannot start a regular expression.\");\n         return false;\n      }\n      ++m_position;\n      return parse_repeat();\n   case regex_constants::syntax_question:\n      if(m_position == this->m_base)\n      {\n         fail(regex_constants::error_badrepeat, 0, \"The repeat operator \\\"?\\\" cannot start a regular expression.\");\n         return false;\n      }\n      ++m_position;\n      return parse_repeat(0,1);\n   case regex_constants::syntax_plus:\n      if(m_position == this->m_base)\n      {\n         fail(regex_constants::error_badrepeat, 0, \"The repeat operator \\\"+\\\" cannot start a regular expression.\");\n         return false;\n      }\n      ++m_position;\n      return parse_repeat(1);\n   case regex_constants::syntax_open_brace:\n      ++m_position;\n      return parse_repeat_range(false);\n   case regex_constants::syntax_close_brace:\n      if((this->flags() & regbase::no_perl_ex) == regbase::no_perl_ex)\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, \"Found a closing repetition operator } with no corresponding {.\");\n         return false;\n      }\n      result = parse_literal();\n      break;\n   case regex_constants::syntax_or:\n      return parse_alt();\n   case regex_constants::syntax_open_set:\n      return parse_set();\n   case regex_constants::syntax_newline:\n      if(this->flags() & regbase::newline_alt)\n         return parse_alt();\n      else\n         return parse_literal();\n   case regex_constants::syntax_hash:\n      //\n      // If we have a mod_x flag set, then skip until\n      // we get to a newline character:\n      //\n      if((this->flags() \n         & (regbase::no_perl_ex|regbase::mod_x))\n         == regbase::mod_x)\n      {\n         while((m_position != m_end) && !is_separator(*m_position++)){}\n         return true;\n      }\n      BOOST_REGEX_FALLTHROUGH;\n   default:\n      result = parse_literal();\n      break;\n   }\n   return result;\n}\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_literal()\n{\n   // append this as a literal provided it's not a space character\n   // or the perl option regbase::mod_x is not set:\n   if(\n      ((this->flags() \n         & (regbase::main_option_type|regbase::mod_x|regbase::no_perl_ex)) \n            != regbase::mod_x)\n      || !this->m_traits.isctype(*m_position, this->m_mask_space))\n         this->append_literal(*m_position);\n   ++m_position;\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_open_paren()\n{\n   //\n   // skip the '(' and error check:\n   //\n   if(++m_position == m_end)\n   {\n      fail(regex_constants::error_paren, m_position - m_base);\n      return false;\n   }\n   //\n   // begin by checking for a perl-style (?...) extension:\n   //\n   if(\n         ((this->flags() & (regbase::main_option_type | regbase::no_perl_ex)) == 0)\n         || ((this->flags() & (regbase::main_option_type | regbase::emacs_ex)) == (regbase::basic_syntax_group|regbase::emacs_ex))\n     )\n   {\n      if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_question)\n         return parse_perl_extension();\n      if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_star)\n         return parse_perl_verb();\n   }\n   //\n   // update our mark count, and append the required state:\n   //\n   unsigned markid = 0;\n   if(0 == (this->flags() & regbase::nosubs))\n   {\n      markid = ++m_mark_count;\n      if(this->flags() & regbase::save_subexpression_location)\n         this->m_pdata->m_subs.push_back(std::pair<std::size_t, std::size_t>(std::distance(m_base, m_position) - 1, 0));\n   }\n   re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_startmark, sizeof(re_brace)));\n   pb->index = markid;\n   pb->icase = this->flags() & regbase::icase;\n   std::ptrdiff_t last_paren_start = this->getoffset(pb);\n   // back up insertion point for alternations, and set new point:\n   std::ptrdiff_t last_alt_point = m_alt_insert_point;\n   this->m_pdata->m_data.align();\n   m_alt_insert_point = this->m_pdata->m_data.size();\n   //\n   // back up the current flags in case we have a nested (?imsx) group:\n   //\n   regex_constants::syntax_option_type opts = this->flags();\n   bool old_case_change = m_has_case_change;\n   m_has_case_change = false; // no changes to this scope as yet...\n   //\n   // Back up branch reset data in case we have a nested (?|...)\n   //\n   int mark_reset = m_mark_reset;\n   m_mark_reset = -1;\n   //\n   // now recursively add more states, this will terminate when we get to a\n   // matching ')' :\n   //\n   parse_all();\n   //\n   // Unwind pushed alternatives:\n   //\n   if(0 == unwind_alts(last_paren_start))\n      return false;\n   //\n   // restore flags:\n   //\n   if(m_has_case_change)\n   {\n      // the case has changed in one or more of the alternatives\n      // within the scoped (...) block: we have to add a state\n      // to reset the case sensitivity:\n      static_cast<re_case*>(\n         this->append_state(syntax_element_toggle_case, sizeof(re_case))\n         )->icase = opts & regbase::icase;\n   }\n   this->flags(opts);\n   m_has_case_change = old_case_change;\n   //\n   // restore branch reset:\n   //\n   m_mark_reset = mark_reset;\n   //\n   // we either have a ')' or we have run out of characters prematurely:\n   //\n   if(m_position == m_end)\n   {\n      this->fail(regex_constants::error_paren, std::distance(m_base, m_end));\n      return false;\n   }\n   if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n      return false;\n   if(markid && (this->flags() & regbase::save_subexpression_location))\n      this->m_pdata->m_subs.at(markid - 1).second = std::distance(m_base, m_position);\n   ++m_position;\n   //\n   // append closing parenthesis state:\n   //\n   pb = static_cast<re_brace*>(this->append_state(syntax_element_endmark, sizeof(re_brace)));\n   pb->index = markid;\n   pb->icase = this->flags() & regbase::icase;\n   this->m_paren_start = last_paren_start;\n   //\n   // restore the alternate insertion point:\n   //\n   this->m_alt_insert_point = last_alt_point;\n\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_basic_escape()\n{\n   if(++m_position == m_end)\n   {\n      fail(regex_constants::error_paren, m_position - m_base);\n      return false;\n   }\n   bool result = true;\n   switch(this->m_traits.escape_syntax_type(*m_position))\n   {\n   case regex_constants::syntax_open_mark:\n      return parse_open_paren();\n   case regex_constants::syntax_close_mark:\n      return false;\n   case regex_constants::syntax_plus:\n      if(this->flags() & regex_constants::bk_plus_qm)\n      {\n         ++m_position;\n         return parse_repeat(1);\n      }\n      else\n         return parse_literal();\n   case regex_constants::syntax_question:\n      if(this->flags() & regex_constants::bk_plus_qm)\n      {\n         ++m_position;\n         return parse_repeat(0, 1);\n      }\n      else\n         return parse_literal();\n   case regex_constants::syntax_open_brace:\n      if(this->flags() & regbase::no_intervals)\n         return parse_literal();\n      ++m_position;\n      return parse_repeat_range(true);\n   case regex_constants::syntax_close_brace:\n      if(this->flags() & regbase::no_intervals)\n         return parse_literal();\n      fail(regex_constants::error_brace, this->m_position - this->m_base, \"Found a closing repetition operator } with no corresponding {.\");\n      return false;\n   case regex_constants::syntax_or:\n      if(this->flags() & regbase::bk_vbar)\n         return parse_alt();\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::syntax_digit:\n      return parse_backref();\n   case regex_constants::escape_type_start_buffer:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_buffer_start);\n      }\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::escape_type_end_buffer:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_buffer_end);\n      }\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::escape_type_word_assert:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_word_boundary);\n      }\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::escape_type_not_word_assert:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_within_word);\n      }\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::escape_type_left_word:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_word_start);\n      }\n      else\n         result = parse_literal();\n      break;\n   case regex_constants::escape_type_right_word:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         ++m_position;\n         this->append_state(syntax_element_word_end);\n      }\n      else\n         result = parse_literal();\n      break;\n   default:\n      if(this->flags() & regbase::emacs_ex)\n      {\n         bool negate = true;\n         switch(*m_position)\n         {\n         case 'w':\n            negate = false;\n            BOOST_REGEX_FALLTHROUGH;\n         case 'W':\n            {\n            basic_char_set<charT, traits> char_set;\n            if(negate)\n               char_set.negate();\n            char_set.add_class(this->m_word_mask);\n            if(0 == this->append_set(char_set))\n            {\n               fail(regex_constants::error_ctype, m_position - m_base);\n               return false;\n            }\n            ++m_position;\n            return true;\n            }\n         case 's':\n            negate = false;\n            BOOST_REGEX_FALLTHROUGH;\n         case 'S':\n            return add_emacs_code(negate);\n         case 'c':\n         case 'C':\n            // not supported yet:\n            fail(regex_constants::error_escape, m_position - m_base, \"The \\\\c and \\\\C escape sequences are not supported by POSIX basic regular expressions: try the Perl syntax instead.\");\n            return false;\n         default:\n            break;\n         }\n      }\n      result = parse_literal();\n      break;\n   }\n   return result;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_extended_escape()\n{\n   ++m_position;\n   if(m_position == m_end)\n   {\n      fail(regex_constants::error_escape, m_position - m_base, \"Incomplete escape sequence found.\");\n      return false;\n   }\n   bool negate = false; // in case this is a character class escape: \\w \\d etc\n   switch(this->m_traits.escape_syntax_type(*m_position))\n   {\n   case regex_constants::escape_type_not_class:\n      negate = true;\n      BOOST_REGEX_FALLTHROUGH;\n   case regex_constants::escape_type_class:\n      {\nescape_type_class_jump:\n         typedef typename traits::char_class_type m_type;\n         m_type m = this->m_traits.lookup_classname(m_position, m_position+1);\n         if(m != 0)\n         {\n            basic_char_set<charT, traits> char_set;\n            if(negate)\n               char_set.negate();\n            char_set.add_class(m);\n            if(0 == this->append_set(char_set))\n            {\n               fail(regex_constants::error_ctype, m_position - m_base);\n               return false;\n            }\n            ++m_position;\n            return true;\n         }\n         //\n         // not a class, just a regular unknown escape:\n         //\n         this->append_literal(unescape_character());\n         break;\n      }\n   case regex_constants::syntax_digit:\n      return parse_backref();\n   case regex_constants::escape_type_left_word:\n      ++m_position;\n      this->append_state(syntax_element_word_start);\n      break;\n   case regex_constants::escape_type_right_word:\n      ++m_position;\n      this->append_state(syntax_element_word_end);\n      break;\n   case regex_constants::escape_type_start_buffer:\n      ++m_position;\n      this->append_state(syntax_element_buffer_start);\n      break;\n   case regex_constants::escape_type_end_buffer:\n      ++m_position;\n      this->append_state(syntax_element_buffer_end);\n      break;\n   case regex_constants::escape_type_word_assert:\n      ++m_position;\n      this->append_state(syntax_element_word_boundary);\n      break;\n   case regex_constants::escape_type_not_word_assert:\n      ++m_position;\n      this->append_state(syntax_element_within_word);\n      break;\n   case regex_constants::escape_type_Z:\n      ++m_position;\n      this->append_state(syntax_element_soft_buffer_end);\n      break;\n   case regex_constants::escape_type_Q:\n      return parse_QE();\n   case regex_constants::escape_type_C:\n      return parse_match_any();\n   case regex_constants::escape_type_X:\n      ++m_position;\n      this->append_state(syntax_element_combining);\n      break;\n   case regex_constants::escape_type_G:\n      ++m_position;\n      this->append_state(syntax_element_restart_continue);\n      break;\n   case regex_constants::escape_type_not_property:\n      negate = true;\n      BOOST_REGEX_FALLTHROUGH;\n   case regex_constants::escape_type_property:\n      {\n         ++m_position;\n         char_class_type m;\n         if(m_position == m_end)\n         {\n            fail(regex_constants::error_escape, m_position - m_base, \"Incomplete property escape found.\");\n            return false;\n         }\n         // maybe have \\p{ddd}\n         if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace)\n         {\n            const charT* base = m_position;\n            // skip forward until we find enclosing brace:\n            while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace))\n               ++m_position;\n            if(m_position == m_end)\n            {\n               fail(regex_constants::error_escape, m_position - m_base, \"Closing } missing from property escape sequence.\");\n               return false;\n            }\n            m = this->m_traits.lookup_classname(++base, m_position++);\n         }\n         else\n         {\n            m = this->m_traits.lookup_classname(m_position, m_position+1);\n            ++m_position;\n         }\n         if(m != 0)\n         {\n            basic_char_set<charT, traits> char_set;\n            if(negate)\n               char_set.negate();\n            char_set.add_class(m);\n            if(0 == this->append_set(char_set))\n            {\n               fail(regex_constants::error_ctype, m_position - m_base);\n               return false;\n            }\n            return true;\n         }\n         fail(regex_constants::error_ctype, m_position - m_base, \"Escape sequence was neither a valid property nor a valid character class name.\");\n         return false;\n      }\n   case regex_constants::escape_type_reset_start_mark:\n      if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex)))\n      {\n         re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_startmark, sizeof(re_brace)));\n         pb->index = -5;\n         pb->icase = this->flags() & regbase::icase;\n         this->m_pdata->m_data.align();\n         ++m_position;\n         return true;\n      }\n      goto escape_type_class_jump;\n   case regex_constants::escape_type_line_ending:\n      if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex)))\n      {\n         const charT* e = get_escape_R_string<charT>();\n         const charT* old_position = m_position;\n         const charT* old_end = m_end;\n         const charT* old_base = m_base;\n         m_position = e;\n         m_base = e;\n         m_end = e + traits::length(e);\n         bool r = parse_all();\n         m_position = ++old_position;\n         m_end = old_end;\n         m_base = old_base;\n         return r;\n      }\n      goto escape_type_class_jump;\n   case regex_constants::escape_type_extended_backref:\n      if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex)))\n      {\n         bool have_brace = false;\n         bool negative = false;\n         static const char incomplete_message[] = \"Incomplete \\\\g escape found.\";\n         if(++m_position == m_end)\n         {\n            fail(regex_constants::error_escape, m_position - m_base, incomplete_message);\n            return false;\n         }\n         // maybe have \\g{ddd}\n         regex_constants::syntax_type syn = this->m_traits.syntax_type(*m_position);\n         regex_constants::syntax_type syn_end = 0;\n         if((syn == regex_constants::syntax_open_brace) \n            || (syn == regex_constants::escape_type_left_word)\n            || (syn == regex_constants::escape_type_end_buffer))\n         {\n            if(++m_position == m_end)\n            {\n               fail(regex_constants::error_escape, m_position - m_base, incomplete_message);\n               return false;\n            }\n            have_brace = true;\n            switch(syn)\n            {\n            case regex_constants::syntax_open_brace:\n               syn_end = regex_constants::syntax_close_brace;\n               break;\n            case regex_constants::escape_type_left_word:\n               syn_end = regex_constants::escape_type_right_word;\n               break;\n            default:\n               syn_end = regex_constants::escape_type_end_buffer;\n               break;\n            }\n         }\n         negative = (*m_position == static_cast<charT>('-'));\n         if((negative) && (++m_position == m_end))\n         {\n            fail(regex_constants::error_escape, m_position - m_base, incomplete_message);\n            return false;\n         }\n         const charT* pc = m_position;\n         std::intmax_t i = this->m_traits.toi(pc, m_end, 10);\n         if((i < 0) && syn_end)\n         {\n            // Check for a named capture, get the leftmost one if there is more than one:\n            const charT* base = m_position;\n            while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != syn_end))\n            {\n               ++m_position;\n            }\n            i = hash_value_from_capture_name(base, m_position);\n            pc = m_position;\n         }\n         if(negative)\n            i = 1 + (static_cast<std::intmax_t>(m_mark_count) - i);\n         if(((i < hash_value_mask) && (i > 0)) || ((i >= hash_value_mask) && (this->m_pdata->get_id((int)i) > 0)))\n         {\n            m_position = pc;\n            re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_backref, sizeof(re_brace)));\n            pb->index = (int)i;\n            pb->icase = this->flags() & regbase::icase;\n            if ((i > m_max_backref) && (i < hash_value_mask))\n               m_max_backref = i;\n         }\n         else\n         {\n            fail(regex_constants::error_backref, m_position - m_base);\n            return false;\n         }\n         m_position = pc;\n         if(have_brace)\n         {\n            if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != syn_end))\n            {\n               fail(regex_constants::error_escape, m_position - m_base, incomplete_message);\n               return false;\n            }\n            ++m_position;\n         }\n         return true;\n      }\n      goto escape_type_class_jump;\n   case regex_constants::escape_type_control_v:\n      if(0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex)))\n         goto escape_type_class_jump;\n      BOOST_REGEX_FALLTHROUGH;\n   default:\n      this->append_literal(unescape_character());\n      break;\n   }\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_match_any()\n{\n   //\n   // we have a '.' that can match any character:\n   //\n   ++m_position;\n   static_cast<re_dot*>(\n      this->append_state(syntax_element_wild, sizeof(re_dot))\n      )->mask = static_cast<unsigned char>(this->flags() & regbase::no_mod_s \n      ? BOOST_REGEX_DETAIL_NS::force_not_newline \n         : this->flags() & regbase::mod_s ?\n            BOOST_REGEX_DETAIL_NS::force_newline : BOOST_REGEX_DETAIL_NS::dont_care);\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_repeat(std::size_t low, std::size_t high)\n{\n   bool greedy = true;\n   bool possessive = false;\n   std::size_t insert_point;\n   // \n   // when we get to here we may have a non-greedy ? mark still to come:\n   //\n   if((m_position != m_end) \n      && (\n            (0 == (this->flags() & (regbase::main_option_type | regbase::no_perl_ex)))\n            || ((regbase::basic_syntax_group|regbase::emacs_ex) == (this->flags() & (regbase::main_option_type | regbase::emacs_ex)))\n         )\n      )\n   {\n      // OK we have a perl or emacs regex, check for a '?':\n      if ((this->flags() & (regbase::main_option_type | regbase::mod_x | regbase::no_perl_ex)) == regbase::mod_x)\n      {\n         // whitespace skip:\n         while ((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n            ++m_position;\n      }\n      if((m_position != m_end) && (this->m_traits.syntax_type(*m_position) == regex_constants::syntax_question))\n      {\n         greedy = false;\n         ++m_position;\n      }\n      // for perl regexes only check for possessive ++ repeats.\n      if((m_position != m_end)\n         && (0 == (this->flags() & regbase::main_option_type)) \n         && (this->m_traits.syntax_type(*m_position) == regex_constants::syntax_plus))\n      {\n         possessive = true;\n         ++m_position;\n      }\n   }\n   if(0 == this->m_last_state)\n   {\n      fail(regex_constants::error_badrepeat, std::distance(m_base, m_position), \"Nothing to repeat.\");\n      return false;\n   }\n   if(this->m_last_state->type == syntax_element_endmark)\n   {\n      // insert a repeat before the '(' matching the last ')':\n      insert_point = this->m_paren_start;\n   }\n   else if((this->m_last_state->type == syntax_element_literal) && (static_cast<re_literal*>(this->m_last_state)->length > 1))\n   {\n      // the last state was a literal with more than one character, split it in two:\n      re_literal* lit = static_cast<re_literal*>(this->m_last_state);\n      charT c = (static_cast<charT*>(static_cast<void*>(lit+1)))[lit->length - 1];\n      lit->length -= 1;\n      // now append new state:\n      lit = static_cast<re_literal*>(this->append_state(syntax_element_literal, sizeof(re_literal) + sizeof(charT)));\n      lit->length = 1;\n      (static_cast<charT*>(static_cast<void*>(lit+1)))[0] = c;\n      insert_point = this->getoffset(this->m_last_state);\n   }\n   else\n   {\n      // repeat the last state whatever it was, need to add some error checking here:\n      switch(this->m_last_state->type)\n      {\n      case syntax_element_start_line:\n      case syntax_element_end_line:\n      case syntax_element_word_boundary:\n      case syntax_element_within_word:\n      case syntax_element_word_start:\n      case syntax_element_word_end:\n      case syntax_element_buffer_start:\n      case syntax_element_buffer_end:\n      case syntax_element_alt:\n      case syntax_element_soft_buffer_end:\n      case syntax_element_restart_continue:\n      case syntax_element_jump:\n      case syntax_element_startmark:\n      case syntax_element_backstep:\n      case syntax_element_toggle_case:\n         // can't legally repeat any of the above:\n         fail(regex_constants::error_badrepeat, m_position - m_base);\n         return false;\n      default:\n         // do nothing...\n         break;\n      }\n      insert_point = this->getoffset(this->m_last_state);\n   }\n   //\n   // OK we now know what to repeat, so insert the repeat around it:\n   //\n   re_repeat* rep = static_cast<re_repeat*>(this->insert_state(insert_point, syntax_element_rep, re_repeater_size));\n   rep->min = low;\n   rep->max = high;\n   rep->greedy = greedy;\n   rep->leading = false;\n   // store our repeater position for later:\n   std::ptrdiff_t rep_off = this->getoffset(rep);\n   // and append a back jump to the repeat:\n   re_jump* jmp = static_cast<re_jump*>(this->append_state(syntax_element_jump, sizeof(re_jump)));\n   jmp->alt.i = rep_off - this->getoffset(jmp);\n   this->m_pdata->m_data.align();\n   // now fill in the alt jump for the repeat:\n   rep = static_cast<re_repeat*>(this->getaddress(rep_off));\n   rep->alt.i = this->m_pdata->m_data.size() - rep_off;\n   //\n   // If the repeat is possessive then bracket the repeat with a (?>...)\n   // independent sub-expression construct:\n   //\n   if(possessive)\n   {\n      if(m_position != m_end)\n      {\n         //\n         // Check for illegal following quantifier, we have to do this here, because\n         // the extra states we insert below circumvents our usual error checking :-(\n         //\n         bool contin = false;\n         do\n         {\n            if ((this->flags() & (regbase::main_option_type | regbase::mod_x | regbase::no_perl_ex)) == regbase::mod_x)\n            {\n               // whitespace skip:\n               while ((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n                  ++m_position;\n            }\n            if (m_position != m_end)\n            {\n               switch (this->m_traits.syntax_type(*m_position))\n               {\n               case regex_constants::syntax_star:\n               case regex_constants::syntax_plus:\n               case regex_constants::syntax_question:\n               case regex_constants::syntax_open_brace:\n                  fail(regex_constants::error_badrepeat, m_position - m_base);\n                  return false;\n               case regex_constants::syntax_open_mark:\n                  // Do we have a comment?  If so we need to skip it here...\n                  if ((m_position + 2 < m_end) && this->m_traits.syntax_type(*(m_position + 1)) == regex_constants::syntax_question\n                     && this->m_traits.syntax_type(*(m_position + 2)) == regex_constants::syntax_hash)\n                  {\n                     while ((m_position != m_end)\n                        && (this->m_traits.syntax_type(*m_position++) != regex_constants::syntax_close_mark)) {\n                     }\n                     contin = true;\n                  }\n                  else\n                     contin = false;\n                  break;\n               default:\n                  contin = false;\n               }\n            }\n            else\n               contin = false;\n         } while (contin);\n      }\n      re_brace* pb = static_cast<re_brace*>(this->insert_state(insert_point, syntax_element_startmark, sizeof(re_brace)));\n      pb->index = -3;\n      pb->icase = this->flags() & regbase::icase;\n      jmp = static_cast<re_jump*>(this->insert_state(insert_point + sizeof(re_brace), syntax_element_jump, sizeof(re_jump)));\n      this->m_pdata->m_data.align();\n      jmp->alt.i = this->m_pdata->m_data.size() - this->getoffset(jmp);\n      pb = static_cast<re_brace*>(this->append_state(syntax_element_endmark, sizeof(re_brace)));\n      pb->index = -3;\n      pb->icase = this->flags() & regbase::icase;\n   }\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_repeat_range(bool isbasic)\n{\n   static const char incomplete_message[] = \"Missing } in quantified repetition.\";\n   //\n   // parse a repeat-range:\n   //\n   std::size_t min, max;\n   std::intmax_t v;\n   // skip whitespace:\n   while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n      ++m_position;\n   if(this->m_position == this->m_end)\n   {\n      if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex))\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n         return false;\n      }\n      // Treat the opening '{' as a literal character, rewind to start of error:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n      return parse_literal();\n   }\n   // get min:\n   v = this->m_traits.toi(m_position, m_end, 10);\n   // skip whitespace:\n   if((v < 0) || (v > umax()))\n   {\n      if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex))\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n         return false;\n      }\n      // Treat the opening '{' as a literal character, rewind to start of error:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n      return parse_literal();\n   }\n   while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n      ++m_position;\n   if(this->m_position == this->m_end)\n   {\n      if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex))\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n         return false;\n      }\n      // Treat the opening '{' as a literal character, rewind to start of error:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n      return parse_literal();\n   }\n   min = static_cast<std::size_t>(v);\n   // see if we have a comma:\n   if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_comma)\n   {\n      // move on and error check:\n      ++m_position;\n      // skip whitespace:\n      while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n         ++m_position;\n      if(this->m_position == this->m_end)\n      {\n         if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex))\n         {\n            fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n            return false;\n         }\n         // Treat the opening '{' as a literal character, rewind to start of error:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n         return parse_literal();\n      }\n      // get the value if any:\n      v = this->m_traits.toi(m_position, m_end, 10);\n      max = ((v >= 0) && (v < umax())) ? (std::size_t)v : (std::numeric_limits<std::size_t>::max)();\n   }\n   else\n   {\n      // no comma, max = min:\n      max = min;\n   }\n   // skip whitespace:\n   while((m_position != m_end) && this->m_traits.isctype(*m_position, this->m_mask_space))\n      ++m_position;\n   // OK now check trailing }:\n   if(this->m_position == this->m_end)\n   {\n      if(this->flags() & (regbase::main_option_type | regbase::no_perl_ex))\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n         return false;\n      }\n      // Treat the opening '{' as a literal character, rewind to start of error:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n      return parse_literal();\n   }\n   if(isbasic)\n   {\n      if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_escape)\n      {\n         ++m_position;\n         if(this->m_position == this->m_end)\n         {\n            fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n            return false;\n         }\n      }\n      else\n      {\n         fail(regex_constants::error_brace, this->m_position - this->m_base, incomplete_message);\n         return false;\n      }\n   }\n   if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_brace)\n      ++m_position;\n   else\n   {\n      // Treat the opening '{' as a literal character, rewind to start of error:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_brace) --m_position;\n      return parse_literal();\n   }\n   //\n   // finally go and add the repeat, unless error:\n   //\n   if(min > max)\n   {\n      // Backtrack to error location:\n      m_position -= 2;\n      while(this->m_traits.isctype(*m_position, this->m_word_mask)) --m_position;\n         ++m_position;\n      fail(regex_constants::error_badbrace, m_position - m_base);\n      return false;\n   }\n   return parse_repeat(min, max);\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_alt()\n{\n   //\n   // error check: if there have been no previous states,\n   // or if the last state was a '(' then error:\n   //\n   if(\n      ((this->m_last_state == 0) || (this->m_last_state->type == syntax_element_startmark))\n      &&\n      !(\n         ((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group)\n           &&\n         ((this->flags() & regbase::no_empty_expressions) == 0)\n        )\n      )\n   {\n      fail(regex_constants::error_empty, this->m_position - this->m_base, \"A regular expression cannot start with the alternation operator |.\");\n      return false;\n   }\n   //\n   // Reset mark count if required:\n   //\n   if(m_max_mark < m_mark_count)\n      m_max_mark = m_mark_count;\n   if(m_mark_reset >= 0)\n      m_mark_count = m_mark_reset;\n\n   ++m_position;\n   //\n   // we need to append a trailing jump: \n   //\n   re_syntax_base* pj = this->append_state(BOOST_REGEX_DETAIL_NS::syntax_element_jump, sizeof(re_jump));\n   std::ptrdiff_t jump_offset = this->getoffset(pj);\n   //\n   // now insert the alternative:\n   //\n   re_alt* palt = static_cast<re_alt*>(this->insert_state(this->m_alt_insert_point, syntax_element_alt, re_alt_size));\n   jump_offset += re_alt_size;\n   this->m_pdata->m_data.align();\n   palt->alt.i = this->m_pdata->m_data.size() - this->getoffset(palt);\n   //\n   // update m_alt_insert_point so that the next alternate gets\n   // inserted at the start of the second of the two we've just created:\n   //\n   this->m_alt_insert_point = this->m_pdata->m_data.size();\n   //\n   // the start of this alternative must have a case changes state\n   // if the current block has messed around with case changes:\n   //\n   if(m_has_case_change)\n   {\n      static_cast<re_case*>(\n         this->append_state(syntax_element_toggle_case, sizeof(re_case))\n         )->icase = this->m_icase;\n   }\n   //\n   // push the alternative onto our stack, a recursive\n   // implementation here is easier to understand (and faster\n   // as it happens), but causes all kinds of stack overflow problems\n   // on programs with small stacks (COM+).\n   //\n   m_alt_jumps.push_back(jump_offset);\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_set()\n{\n   static const char incomplete_message[] = \"Character set declaration starting with [ terminated prematurely - either no ] was found or the set had no content.\";\n   ++m_position;\n   if(m_position == m_end)\n   {\n      fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n      return false;\n   }\n   basic_char_set<charT, traits> char_set;\n\n   const charT* base = m_position;  // where the '[' was\n   const charT* item_base = m_position;  // where the '[' or '^' was\n\n   while(m_position != m_end)\n   {\n      switch(this->m_traits.syntax_type(*m_position))\n      {\n      case regex_constants::syntax_caret:\n         if(m_position == base)\n         {\n            char_set.negate();\n            ++m_position;\n            item_base = m_position;\n         }\n         else\n            parse_set_literal(char_set);\n         break;\n      case regex_constants::syntax_close_set:\n         if(m_position == item_base)\n         {\n            parse_set_literal(char_set);\n            break;\n         }\n         else\n         {\n            ++m_position;\n            if(0 == this->append_set(char_set))\n            {\n               fail(regex_constants::error_ctype, m_position - m_base);\n               return false;\n            }\n         }\n         return true;\n      case regex_constants::syntax_open_set:\n         if(parse_inner_set(char_set))\n            break;\n         return true;\n      case regex_constants::syntax_escape:\n         {\n            // \n            // look ahead and see if this is a character class shortcut\n            // \\d \\w \\s etc...\n            //\n            ++m_position;\n            if(this->m_traits.escape_syntax_type(*m_position)\n               == regex_constants::escape_type_class)\n            {\n               char_class_type m = this->m_traits.lookup_classname(m_position, m_position+1);\n               if(m != 0)\n               {\n                  char_set.add_class(m);\n                  ++m_position;\n                  break;\n               }\n            }\n            else if(this->m_traits.escape_syntax_type(*m_position)\n               == regex_constants::escape_type_not_class)\n            {\n               // negated character class:\n               char_class_type m = this->m_traits.lookup_classname(m_position, m_position+1);\n               if(m != 0)\n               {\n                  char_set.add_negated_class(m);\n                  ++m_position;\n                  break;\n               }\n            }\n            // not a character class, just a regular escape:\n            --m_position;\n            parse_set_literal(char_set);\n            break;\n         }\n      default:\n         parse_set_literal(char_set);\n         break;\n      }\n   }\n   return m_position != m_end;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_inner_set(basic_char_set<charT, traits>& char_set)\n{\n   static const char incomplete_message[] = \"Character class declaration starting with [ terminated prematurely - either no ] was found or the set had no content.\";\n   //\n   // we have either a character class [:name:]\n   // a collating element [.name.]\n   // or an equivalence class [=name=]\n   //\n   if(m_end == ++m_position)\n   {\n      fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n      return false;\n   }\n   switch(this->m_traits.syntax_type(*m_position))\n   {\n   case regex_constants::syntax_dot:\n      //\n      // a collating element is treated as a literal:\n      //\n      --m_position;\n      parse_set_literal(char_set);\n      return true;\n   case regex_constants::syntax_colon:\n      {\n      // check that character classes are actually enabled:\n      if((this->flags() & (regbase::main_option_type | regbase::no_char_classes)) \n         == (regbase::basic_syntax_group  | regbase::no_char_classes))\n      {\n         --m_position;\n         parse_set_literal(char_set);\n         return true;\n      }\n      // skip the ':'\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      const charT* name_first = m_position;\n      // skip at least one character, then find the matching ':]'\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      while((m_position != m_end) \n         && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_colon)) \n         ++m_position;\n      const charT* name_last = m_position;\n      if(m_end == m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      if((m_end == ++m_position) \n         || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set))\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      //\n      // check for negated class:\n      //\n      bool negated = false;\n      if(this->m_traits.syntax_type(*name_first) == regex_constants::syntax_caret)\n      {\n         ++name_first;\n         negated = true;\n      }\n      typedef typename traits::char_class_type m_type;\n      m_type m = this->m_traits.lookup_classname(name_first, name_last);\n      if(m == 0)\n      {\n         if(char_set.empty() && (name_last - name_first == 1))\n         {\n            // maybe a special case:\n            ++m_position;\n            if( (m_position != m_end) \n               && (this->m_traits.syntax_type(*m_position) \n                  == regex_constants::syntax_close_set))\n            {\n               if(this->m_traits.escape_syntax_type(*name_first) \n                  == regex_constants::escape_type_left_word)\n               {\n                  ++m_position;\n                  this->append_state(syntax_element_word_start);\n                  return false;\n               }\n               if(this->m_traits.escape_syntax_type(*name_first) \n                  == regex_constants::escape_type_right_word)\n               {\n                  ++m_position;\n                  this->append_state(syntax_element_word_end);\n                  return false;\n               }\n            }\n         }\n         fail(regex_constants::error_ctype, name_first - m_base);\n         return false;\n      }\n      if(!negated)\n         char_set.add_class(m);\n      else\n         char_set.add_negated_class(m);\n      ++m_position;\n      break;\n   }\n   case regex_constants::syntax_equal:\n      {\n      // skip the '='\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      const charT* name_first = m_position;\n      // skip at least one character, then find the matching '=]'\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      while((m_position != m_end) \n         && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal)) \n         ++m_position;\n      const charT* name_last = m_position;\n      if(m_end == m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      if((m_end == ++m_position) \n         || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set))\n      {\n         fail(regex_constants::error_brack, m_position - m_base, incomplete_message);\n         return false;\n      }\n      string_type m = this->m_traits.lookup_collatename(name_first, name_last);\n      if(m.empty() || (m.size() > 2))\n      {\n         fail(regex_constants::error_collate, name_first - m_base);\n         return false;\n      }\n      digraph<charT> d;\n      d.first = m[0];\n      if(m.size() > 1)\n         d.second = m[1];\n      else\n         d.second = 0;\n      char_set.add_equivalent(d);\n      ++m_position;\n      break;\n   }\n   default:\n      --m_position;\n      parse_set_literal(char_set);\n      break;\n   }\n   return true;\n}\n\ntemplate <class charT, class traits>\nvoid basic_regex_parser<charT, traits>::parse_set_literal(basic_char_set<charT, traits>& char_set)\n{\n   digraph<charT> start_range(get_next_set_literal(char_set));\n   if(m_end == m_position)\n   {\n      fail(regex_constants::error_brack, m_position - m_base);\n      return;\n   }\n   if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_dash)\n   {\n      // we have a range:\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_brack, m_position - m_base);\n         return;\n      }\n      if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set)\n      {\n         digraph<charT> end_range = get_next_set_literal(char_set);\n         char_set.add_range(start_range, end_range);\n         if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_dash)\n         {\n            if(m_end == ++m_position)\n            {\n               fail(regex_constants::error_brack, m_position - m_base);\n               return;\n            }\n            if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_set)\n            {\n               // trailing - :\n               --m_position;\n               return;\n            }\n            fail(regex_constants::error_range, m_position - m_base);\n            return;\n         }\n         return;\n      }\n      --m_position;\n   }\n   char_set.add_single(start_range);\n}\n\ntemplate <class charT, class traits>\ndigraph<charT> basic_regex_parser<charT, traits>::get_next_set_literal(basic_char_set<charT, traits>& char_set)\n{\n   digraph<charT> result;\n   switch(this->m_traits.syntax_type(*m_position))\n   {\n   case regex_constants::syntax_dash:\n      if(!char_set.empty())\n      {\n         // see if we are at the end of the set:\n         if((++m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set))\n         {\n            fail(regex_constants::error_range, m_position - m_base);\n            return result;\n         }\n         --m_position;\n      }\n      result.first = *m_position++;\n      return result;\n   case regex_constants::syntax_escape:\n      // check to see if escapes are supported first:\n      if(this->flags() & regex_constants::no_escape_in_lists)\n      {\n         result = *m_position++;\n         break;\n      }\n      ++m_position;\n      result = unescape_character();\n      break;\n   case regex_constants::syntax_open_set:\n   {\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_collate, m_position - m_base);\n         return result;\n      }\n      if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_dot)\n      {\n         --m_position;\n         result.first = *m_position;\n         ++m_position;\n         return result;\n      }\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_collate, m_position - m_base);\n         return result;\n      }\n      const charT* name_first = m_position;\n      // skip at least one character, then find the matching ':]'\n      if(m_end == ++m_position)\n      {\n         fail(regex_constants::error_collate, name_first - m_base);\n         return result;\n      }\n      while((m_position != m_end) \n         && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_dot)) \n         ++m_position;\n      const charT* name_last = m_position;\n      if(m_end == m_position)\n      {\n         fail(regex_constants::error_collate, name_first - m_base);\n         return result;\n      }\n      if((m_end == ++m_position) \n         || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_set))\n      {\n         fail(regex_constants::error_collate, name_first - m_base);\n         return result;\n      }\n      ++m_position;\n      string_type s = this->m_traits.lookup_collatename(name_first, name_last);\n      if(s.empty() || (s.size() > 2))\n      {\n         fail(regex_constants::error_collate, name_first - m_base);\n         return result;\n      }\n      result.first = s[0];\n      if(s.size() > 1)\n         result.second = s[1];\n      else\n         result.second = 0;\n      return result;\n   }\n   default:\n      result = *m_position++;\n   }\n   return result;\n}\n\n//\n// does a value fit in the specified charT type?\n//\ntemplate <class charT>\nbool valid_value(charT, std::intmax_t v, const std::integral_constant<bool, true>&)\n{\n   return (v >> (sizeof(charT) * CHAR_BIT)) == 0;\n}\ntemplate <class charT>\nbool valid_value(charT, std::intmax_t, const std::integral_constant<bool, false>&)\n{\n   return true; // v will alsways fit in a charT\n}\ntemplate <class charT>\nbool valid_value(charT c, std::intmax_t v)\n{\n   return valid_value(c, v, std::integral_constant<bool, (sizeof(charT) < sizeof(std::intmax_t))>());\n}\n\ntemplate <class charT, class traits>\ncharT basic_regex_parser<charT, traits>::unescape_character()\n{\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   charT result(0);\n   if(m_position == m_end)\n   {\n      fail(regex_constants::error_escape, m_position - m_base, \"Escape sequence terminated prematurely.\");\n      return false;\n   }\n   switch(this->m_traits.escape_syntax_type(*m_position))\n   {\n   case regex_constants::escape_type_control_a:\n      result = charT('\\a');\n      break;\n   case regex_constants::escape_type_e:\n      result = charT(27);\n      break;\n   case regex_constants::escape_type_control_f:\n      result = charT('\\f');\n      break;\n   case regex_constants::escape_type_control_n:\n      result = charT('\\n');\n      break;\n   case regex_constants::escape_type_control_r:\n      result = charT('\\r');\n      break;\n   case regex_constants::escape_type_control_t:\n      result = charT('\\t');\n      break;\n   case regex_constants::escape_type_control_v:\n      result = charT('\\v');\n      break;\n   case regex_constants::escape_type_word_assert:\n      result = charT('\\b');\n      break;\n   case regex_constants::escape_type_ascii_control:\n      ++m_position;\n      if(m_position == m_end)\n      {\n         // Rewind to start of escape:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n         fail(regex_constants::error_escape, m_position - m_base, \"ASCII escape sequence terminated prematurely.\");\n         return result;\n      }\n      result = static_cast<charT>(*m_position % 32);\n      break;\n   case regex_constants::escape_type_hex:\n      ++m_position;\n      if(m_position == m_end)\n      {\n         // Rewind to start of escape:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n         fail(regex_constants::error_escape, m_position - m_base, \"Hexadecimal escape sequence terminated prematurely.\");\n         return result;\n      }\n      // maybe have \\x{ddd}\n      if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace)\n      {\n         ++m_position;\n         if(m_position == m_end)\n         {\n            // Rewind to start of escape:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n            fail(regex_constants::error_escape, m_position - m_base, \"Missing } in hexadecimal escape sequence.\");\n            return result;\n         }\n         std::intmax_t i = this->m_traits.toi(m_position, m_end, 16);\n         if((m_position == m_end)\n            || (i < 0)\n            || ((std::numeric_limits<charT>::is_specialized) && (i > (std::intmax_t)(std::numeric_limits<charT>::max)()))\n            || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace))\n         {\n            // Rewind to start of escape:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n            fail(regex_constants::error_badbrace, m_position - m_base, \"Hexadecimal escape sequence was invalid.\");\n            return result;\n         }\n         ++m_position;\n         result = charT(i);\n      }\n      else\n      {\n         std::ptrdiff_t len = (std::min)(static_cast<std::ptrdiff_t>(2), static_cast<std::ptrdiff_t>(m_end - m_position));\n         std::intmax_t i = this->m_traits.toi(m_position, m_position + len, 16);\n         if((i < 0)\n            || !valid_value(charT(0), i))\n         {\n            // Rewind to start of escape:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n            fail(regex_constants::error_escape, m_position - m_base, \"Escape sequence did not encode a valid character.\");\n            return result;\n         }\n         result = charT(i);\n      }\n      return result;\n   case regex_constants::syntax_digit:\n      {\n      // an octal escape sequence, the first character must be a zero\n      // followed by up to 3 octal digits:\n      std::ptrdiff_t len = (std::min)(std::distance(m_position, m_end), static_cast<std::ptrdiff_t>(4));\n      const charT* bp = m_position;\n      std::intmax_t val = this->m_traits.toi(bp, bp + 1, 8);\n      if(val != 0)\n      {\n         // Rewind to start of escape:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n         // Oops not an octal escape after all:\n         fail(regex_constants::error_escape, m_position - m_base, \"Invalid octal escape sequence.\");\n         return result;\n      }\n      val = this->m_traits.toi(m_position, m_position + len, 8);\n      if((val < 0) || (val > (std::intmax_t)(std::numeric_limits<charT>::max)()))\n      {\n         // Rewind to start of escape:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n         fail(regex_constants::error_escape, m_position - m_base, \"Octal escape sequence is invalid.\");\n         return result;\n      }\n      return static_cast<charT>(val);\n      }\n   case regex_constants::escape_type_named_char:\n      {\n         ++m_position;\n         if(m_position == m_end)\n         {\n            // Rewind to start of escape:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n            fail(regex_constants::error_escape, m_position - m_base);\n            return false;\n         }\n         // maybe have \\N{name}\n         if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace)\n         {\n            const charT* base = m_position;\n            // skip forward until we find enclosing brace:\n            while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace))\n               ++m_position;\n            if(m_position == m_end)\n            {\n               // Rewind to start of escape:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n               fail(regex_constants::error_escape, m_position - m_base);\n               return false;\n            }\n            string_type s = this->m_traits.lookup_collatename(++base, m_position++);\n            if(s.empty())\n            {\n               // Rewind to start of escape:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n               fail(regex_constants::error_collate, m_position - m_base);\n               return false;\n            }\n            if(s.size() == 1)\n            {\n               return s[0];\n            }\n         }\n         // fall through is a failure:\n         // Rewind to start of escape:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n         fail(regex_constants::error_escape, m_position - m_base);\n         return false;\n      }\n   default:\n      result = *m_position;\n      break;\n   }\n   ++m_position;\n   return result;\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_backref()\n{\n   BOOST_REGEX_ASSERT(m_position != m_end);\n   const charT* pc = m_position;\n   std::intmax_t i = this->m_traits.toi(pc, pc + 1, 10);\n   if((i == 0) || (((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group) && (this->flags() & regbase::no_bk_refs)))\n   {\n      // not a backref at all but an octal escape sequence:\n      charT c = unescape_character();\n      this->append_literal(c);\n   }\n   else if((i > 0))\n   {\n      m_position = pc;\n      re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_backref, sizeof(re_brace)));\n      pb->index = (int)i;\n      pb->icase = this->flags() & regbase::icase;\n      if(i > m_max_backref)\n         m_max_backref = i;\n   }\n   else\n   {\n      // Rewind to start of escape:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n      fail(regex_constants::error_backref, m_position - m_base);\n      return false;\n   }\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_QE()\n{\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   //\n   // parse a \\Q...\\E sequence:\n   //\n   ++m_position; // skip the Q\n   const charT* start = m_position;\n   const charT* end;\n   do\n   {\n      while((m_position != m_end) \n         && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape))\n         ++m_position;\n      if(m_position == m_end)\n      {\n         //  a \\Q...\\E sequence may terminate with the end of the expression:\n         end = m_position;\n         break;  \n      }\n      if(++m_position == m_end) // skip the escape\n      {\n         fail(regex_constants::error_escape, m_position - m_base, \"Unterminated \\\\Q...\\\\E sequence.\");\n         return false;\n      }\n      // check to see if it's a \\E:\n      if(this->m_traits.escape_syntax_type(*m_position) == regex_constants::escape_type_E)\n      {\n         ++m_position;\n         end = m_position - 2;\n         break;\n      }\n      // otherwise go round again:\n   }while(true);\n   //\n   // now add all the character between the two escapes as literals:\n   //\n   while(start != end)\n   {\n      this->append_literal(*start);\n      ++start;\n   }\n   return true;\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_perl_extension()\n{\n   if(++m_position == m_end)\n   {\n      // Rewind to start of (? sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n      fail(regex_constants::error_perl_extension, m_position - m_base);\n      return false;\n   }\n   //\n   // treat comments as a special case, as these\n   // are the only ones that don't start with a leading\n   // startmark state:\n   //\n   if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_hash)\n   {\n      while((m_position != m_end) \n         && (this->m_traits.syntax_type(*m_position++) != regex_constants::syntax_close_mark))\n      {}\n      return true;\n   }\n   //\n   // backup some state, and prepare the way:\n   //\n   int markid = 0;\n   std::ptrdiff_t jump_offset = 0;\n   re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_startmark, sizeof(re_brace)));\n   pb->icase = this->flags() & regbase::icase;\n   std::ptrdiff_t last_paren_start = this->getoffset(pb);\n   // back up insertion point for alternations, and set new point:\n   std::ptrdiff_t last_alt_point = m_alt_insert_point;\n   this->m_pdata->m_data.align();\n   m_alt_insert_point = this->m_pdata->m_data.size();\n   std::ptrdiff_t expected_alt_point = m_alt_insert_point;\n   bool restore_flags = true;\n   regex_constants::syntax_option_type old_flags = this->flags();\n   bool old_case_change = m_has_case_change;\n   m_has_case_change = false;\n   charT name_delim;\n   int mark_reset = m_mark_reset;\n   int max_mark = m_max_mark;\n   m_mark_reset = -1;\n   m_max_mark = m_mark_count;\n   std::intmax_t v;\n   //\n   // select the actual extension used:\n   //\n   switch(this->m_traits.syntax_type(*m_position))\n   {\n   case regex_constants::syntax_or:\n      m_mark_reset = m_mark_count;\n      BOOST_REGEX_FALLTHROUGH;\n   case regex_constants::syntax_colon:\n      //\n      // a non-capturing mark:\n      //\n      pb->index = markid = 0;\n      ++m_position;\n      break;\n   case regex_constants::syntax_digit:\n      {\n      //\n      // a recursive subexpression:\n      //\n      v = this->m_traits.toi(m_position, m_end, 10);\n      if((v < 0) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base, \"The recursive sub-expression refers to an invalid marking group, or is unterminated.\");\n         return false;\n      }\ninsert_recursion:\n      pb->index = markid = 0;\n      re_recurse* pr = static_cast<re_recurse*>(this->append_state(syntax_element_recurse, sizeof(re_recurse)));\n      pr->alt.i = (std::ptrdiff_t)v;\n      pr->state_id = 0;\n      static_cast<re_case*>(\n            this->append_state(syntax_element_toggle_case, sizeof(re_case))\n            )->icase = this->flags() & regbase::icase;\n      break;\n      }\n   case regex_constants::syntax_plus:\n      //\n      // A forward-relative recursive subexpression:\n      //\n      ++m_position;\n      v = this->m_traits.toi(m_position, m_end, 10);\n      if((v <= 0) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base, \"An invalid or unterminated recursive sub-expression.\");\n         return false;\n      }\n      if ((std::numeric_limits<std::intmax_t>::max)() - m_mark_count < v)\n      {\n         fail(regex_constants::error_perl_extension, m_position - m_base, \"An invalid or unterminated recursive sub-expression.\");\n         return false;\n      }\n      v += m_mark_count;\n      goto insert_recursion;\n   case regex_constants::syntax_dash:\n      //\n      // Possibly a backward-relative recursive subexpression:\n      //\n      ++m_position;\n      v = this->m_traits.toi(m_position, m_end, 10);\n      if(v <= 0)\n      {\n         --m_position;\n         // Oops not a relative recursion at all, but a (?-imsx) group:\n         goto option_group_jump;\n      }\n      v = static_cast<std::intmax_t>(m_mark_count) + 1 - v;\n      if(v <= 0)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base, \"An invalid or unterminated recursive sub-expression.\");\n         return false;\n      }\n      goto insert_recursion;\n   case regex_constants::syntax_equal:\n      pb->index = markid = -1;\n      ++m_position;\n      jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump)));\n      this->m_pdata->m_data.align();\n      m_alt_insert_point = this->m_pdata->m_data.size();\n      break;\n   case regex_constants::syntax_not:\n      pb->index = markid = -2;\n      ++m_position;\n      jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump)));\n      this->m_pdata->m_data.align();\n      m_alt_insert_point = this->m_pdata->m_data.size();\n      break;\n   case regex_constants::escape_type_left_word:\n      {\n         // a lookbehind assertion:\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         regex_constants::syntax_type t = this->m_traits.syntax_type(*m_position);\n         if(t == regex_constants::syntax_not)\n            pb->index = markid = -2;\n         else if(t == regex_constants::syntax_equal)\n            pb->index = markid = -1;\n         else\n         {\n            // Probably a named capture which also starts (?< :\n            name_delim = '>';\n            --m_position;\n            goto named_capture_jump;\n         }\n         ++m_position;\n         jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump)));\n         this->append_state(syntax_element_backstep, sizeof(re_brace));\n         this->m_pdata->m_data.align();\n         m_alt_insert_point = this->m_pdata->m_data.size();\n         break;\n      }\n   case regex_constants::escape_type_right_word:\n      //\n      // an independent sub-expression:\n      //\n      pb->index = markid = -3;\n      ++m_position;\n      jump_offset = this->getoffset(this->append_state(syntax_element_jump, sizeof(re_jump)));\n      this->m_pdata->m_data.align();\n      m_alt_insert_point = this->m_pdata->m_data.size();\n      break;\n   case regex_constants::syntax_open_mark:\n      {\n      // a conditional expression:\n      pb->index = markid = -4;\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      v = this->m_traits.toi(m_position, m_end, 10);\n      if(m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(*m_position == charT('R'))\n      {\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(*m_position == charT('&'))\n         {\n            const charT* base = ++m_position;\n            while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n               ++m_position;\n            if(m_position == m_end)\n            {\n               // Rewind to start of (? sequence:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n               fail(regex_constants::error_perl_extension, m_position - m_base);\n               return false;\n            }\n            v = -static_cast<int>(hash_value_from_capture_name(base, m_position));\n         }\n         else\n         {\n            v = -this->m_traits.toi(m_position, m_end, 10);\n         }\n         re_brace* br = static_cast<re_brace*>(this->append_state(syntax_element_assert_backref, sizeof(re_brace)));\n         br->index = v < 0 ? (int)(v - 1) : 0;\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n      }\n      else if((*m_position == charT('\\'')) || (*m_position == charT('<')))\n      {\n         const charT* base = ++m_position;\n         while((m_position != m_end) && (*m_position != charT('>')) && (*m_position != charT('\\'')))\n            ++m_position;\n         if(m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         v = static_cast<int>(hash_value_from_capture_name(base, m_position));\n         re_brace* br = static_cast<re_brace*>(this->append_state(syntax_element_assert_backref, sizeof(re_brace)));\n         br->index = (int)v;\n         if(((*m_position != charT('>')) && (*m_position != charT('\\''))) || (++m_position == m_end))\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base, \"Unterminated named capture.\");\n            return false;\n         }\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n      }\n      else if(*m_position == charT('D'))\n      {\n         const char* def = \"DEFINE\";\n         while(*def && (m_position != m_end) && (*m_position == charT(*def)))\n            ++m_position, ++def;\n         if((m_position == m_end) || *def)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         re_brace* br = static_cast<re_brace*>(this->append_state(syntax_element_assert_backref, sizeof(re_brace)));\n         br->index = 9999; // special magic value!\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n      }\n      else if(v > 0)\n      {\n         re_brace* br = static_cast<re_brace*>(this->append_state(syntax_element_assert_backref, sizeof(re_brace)));\n         br->index = (int)v;\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n      }\n      else\n      {\n         // verify that we have a lookahead or lookbehind assert:\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_question)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(this->m_traits.syntax_type(*m_position) == regex_constants::escape_type_left_word)\n         {\n            if(++m_position == m_end)\n            {\n               // Rewind to start of (? sequence:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n               fail(regex_constants::error_perl_extension, m_position - m_base);\n               return false;\n            }\n            if((this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal)\n               && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_not))\n            {\n               // Rewind to start of (? sequence:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n               fail(regex_constants::error_perl_extension, m_position - m_base);\n               return false;\n            }\n            m_position -= 3;\n         }\n         else\n         {\n            if((this->m_traits.syntax_type(*m_position) != regex_constants::syntax_equal)\n               && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_not))\n            {\n               // Rewind to start of (? sequence:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n               fail(regex_constants::error_perl_extension, m_position - m_base);\n               return false;\n            }\n            m_position -= 2;\n         }\n      }\n      break;\n      }\n   case regex_constants::syntax_close_mark:\n      // Rewind to start of (? sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n      fail(regex_constants::error_perl_extension, m_position - m_base);\n      return false;\n   case regex_constants::escape_type_end_buffer:\n      {\n      name_delim = *m_position;\nnamed_capture_jump:\n      markid = 0;\n      if(0 == (this->flags() & regbase::nosubs))\n      {\n         markid = ++m_mark_count;\n         if(this->flags() & regbase::save_subexpression_location)\n            this->m_pdata->m_subs.push_back(std::pair<std::size_t, std::size_t>(std::distance(m_base, m_position) - 2, 0));\n      }\n      pb->index = markid;\n      const charT* base = ++m_position;\n      if(m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      while((m_position != m_end) && (*m_position != name_delim))\n         ++m_position;\n      if(m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      this->m_pdata->set_name(base, m_position, markid);\n      ++m_position;\n      break;\n      }\n   default:\n      if(*m_position == charT('R'))\n      {\n         ++m_position;\n         v = 0;\n         if(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         goto insert_recursion;\n      }\n      if(*m_position == charT('&'))\n      {\n         ++m_position;\n         const charT* base = m_position;\n         while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n            ++m_position;\n         if(m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         v = static_cast<int>(hash_value_from_capture_name(base, m_position));\n         goto insert_recursion;\n      }\n      if(*m_position == charT('P'))\n      {\n         ++m_position;\n         if(m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         if(*m_position == charT('>'))\n         {\n            ++m_position;\n            const charT* base = m_position;\n            while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n               ++m_position;\n            if(m_position == m_end)\n            {\n               // Rewind to start of (? sequence:\n               --m_position;\n               while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n               fail(regex_constants::error_perl_extension, m_position - m_base);\n               return false;\n            }\n            v = static_cast<int>(hash_value_from_capture_name(base, m_position));\n            goto insert_recursion;\n         }\n      }\n      //\n      // lets assume that we have a (?imsx) group and try and parse it:\n      //\noption_group_jump:\n      regex_constants::syntax_option_type opts = parse_options();\n      if(m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      // make a note of whether we have a case change:\n      m_has_case_change = ((opts & regbase::icase) != (this->flags() & regbase::icase));\n      pb->index = markid = 0;\n      if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark)\n      {\n         // update flags and carry on as normal:\n         this->flags(opts);\n         restore_flags = false;\n         old_case_change |= m_has_case_change; // defer end of scope by one ')'\n      }\n      else if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_colon)\n      {\n         // update flags and carry on until the matching ')' is found:\n         this->flags(opts);\n         ++m_position;\n      }\n      else\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n\n      // finally append a case change state if we need it:\n      if(m_has_case_change)\n      {\n         static_cast<re_case*>(\n            this->append_state(syntax_element_toggle_case, sizeof(re_case))\n            )->icase = opts & regbase::icase;\n      }\n\n   }\n   //\n   // now recursively add more states, this will terminate when we get to a\n   // matching ')' :\n   //\n   parse_all();\n   //\n   // Unwind alternatives:\n   //\n   if(0 == unwind_alts(last_paren_start))\n   {\n      // Rewind to start of (? sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n      fail(regex_constants::error_perl_extension, m_position - m_base, \"Invalid alternation operators within (?...) block.\");\n      return false;\n   }\n   //\n   // we either have a ')' or we have run out of characters prematurely:\n   //\n   if(m_position == m_end)\n   {\n      // Rewind to start of (? sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n      this->fail(regex_constants::error_paren, std::distance(m_base, m_end));\n      return false;\n   }\n   BOOST_REGEX_ASSERT(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark);\n   ++m_position;\n   //\n   // restore the flags:\n   //\n   if(restore_flags)\n   {\n      // append a case change state if we need it:\n      if(m_has_case_change)\n      {\n         static_cast<re_case*>(\n            this->append_state(syntax_element_toggle_case, sizeof(re_case))\n            )->icase = old_flags & regbase::icase;\n      }\n      this->flags(old_flags);\n   }\n   //\n   // set up the jump pointer if we have one:\n   //\n   if(jump_offset)\n   {\n      this->m_pdata->m_data.align();\n      re_jump* jmp = static_cast<re_jump*>(this->getaddress(jump_offset));\n      jmp->alt.i = this->m_pdata->m_data.size() - this->getoffset(jmp);\n      if((this->m_last_state == jmp) && (markid != -2))\n      {\n         // Oops... we didn't have anything inside the assertion.\n         // Note we don't get here for negated forward lookahead as (?!)\n         // does have some uses.\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base, \"Invalid or empty zero width assertion.\");\n         return false;\n      }\n   }\n   //\n   // verify that if this is conditional expression, that we do have\n   // an alternative, if not add one:\n   //\n   if(markid == -4)\n   {\n      re_syntax_base* b = this->getaddress(expected_alt_point);\n      // Make sure we have exactly one alternative following this state:\n      if(b->type != syntax_element_alt)\n      {\n         re_alt* alt = static_cast<re_alt*>(this->insert_state(expected_alt_point, syntax_element_alt, sizeof(re_alt)));\n         alt->alt.i = this->m_pdata->m_data.size() - this->getoffset(alt);\n      }\n      else if(((std::ptrdiff_t)this->m_pdata->m_data.size() > (static_cast<re_alt*>(b)->alt.i + this->getoffset(b))) && (static_cast<re_alt*>(b)->alt.i > 0) && this->getaddress(static_cast<re_alt*>(b)->alt.i, b)->type == syntax_element_alt)\n      {\n         // Can't have seen more than one alternative:\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_bad_pattern, m_position - m_base, \"More than one alternation operator | was encountered inside a conditional expression.\");\n         return false;\n      }\n      else\n      {\n         // We must *not* have seen an alternative inside a (DEFINE) block:\n         b = this->getaddress(b->next.i, b);\n         if((b->type == syntax_element_assert_backref) && (static_cast<re_brace*>(b)->index == 9999))\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_bad_pattern, m_position - m_base, \"Alternation operators are not allowed inside a DEFINE block.\");\n            return false;\n         }\n      }\n      // check for invalid repetition of next state:\n      b = this->getaddress(expected_alt_point);\n      b = this->getaddress(static_cast<re_alt*>(b)->next.i, b);\n      if((b->type != syntax_element_assert_backref)\n         && (b->type != syntax_element_startmark))\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_badrepeat, m_position - m_base, \"A repetition operator cannot be applied to a zero-width assertion.\");\n         return false;\n      }\n   }\n   //\n   // append closing parenthesis state:\n   //\n   pb = static_cast<re_brace*>(this->append_state(syntax_element_endmark, sizeof(re_brace)));\n   pb->index = markid;\n   pb->icase = this->flags() & regbase::icase;\n   this->m_paren_start = last_paren_start;\n   //\n   // restore the alternate insertion point:\n   //\n   this->m_alt_insert_point = last_alt_point;\n   //\n   // and the case change data:\n   //\n   m_has_case_change = old_case_change;\n   //\n   // And the mark_reset data:\n   //\n   if(m_max_mark > m_mark_count)\n   {\n      m_mark_count = m_max_mark;\n   }\n   m_mark_reset = mark_reset;\n   m_max_mark = max_mark;\n\n\n   if(markid > 0)\n   {\n      if(this->flags() & regbase::save_subexpression_location)\n         this->m_pdata->m_subs.at((std::size_t)markid - 1).second = std::distance(m_base, m_position) - 1;\n   }\n   return true;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::match_verb(const char* verb)\n{\n   while(*verb)\n   {\n      if(static_cast<charT>(*verb) != *m_position)\n      {\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(++m_position == m_end)\n      {\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      ++verb;\n   }\n   return true;\n}\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#if BOOST_REGEX_MSVC >= 1800\n#pragma warning(disable:26812)\n#endif\n#endif\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::parse_perl_verb()\n{\n   if(++m_position == m_end)\n   {\n      // Rewind to start of (* sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n      fail(regex_constants::error_perl_extension, m_position - m_base);\n      return false;\n   }\n   switch(*m_position)\n   {\n   case 'F':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if((this->m_traits.syntax_type(*m_position) == regex_constants::syntax_close_mark) || match_verb(\"AIL\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         this->append_state(syntax_element_fail);\n         return true;\n      }\n      break;\n   case 'A':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(match_verb(\"CCEPT\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         this->append_state(syntax_element_accept);\n         return true;\n      }\n      break;\n   case 'C':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(match_verb(\"OMMIT\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         static_cast<re_commit*>(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_commit;\n         this->m_pdata->m_disable_match_any = true;\n         return true;\n      }\n      break;\n   case 'P':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(match_verb(\"RUNE\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         static_cast<re_commit*>(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_prune;\n         this->m_pdata->m_disable_match_any = true;\n         return true;\n      }\n      break;\n   case 'S':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(match_verb(\"KIP\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         static_cast<re_commit*>(this->append_state(syntax_element_commit, sizeof(re_commit)))->action = commit_skip;\n         this->m_pdata->m_disable_match_any = true;\n         return true;\n      }\n      break;\n   case 'T':\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (* sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_perl_extension, m_position - m_base);\n         return false;\n      }\n      if(match_verb(\"HEN\"))\n      {\n         if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_mark))\n         {\n            // Rewind to start of (* sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_perl_extension, m_position - m_base);\n            return false;\n         }\n         ++m_position;\n         this->append_state(syntax_element_then);\n         this->m_pdata->m_disable_match_any = true;\n         return true;\n      }\n      break;\n   }\n   // Rewind to start of (* sequence:\n   --m_position;\n   while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n   fail(regex_constants::error_perl_extension, m_position - m_base);\n   return false;\n}\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::add_emacs_code(bool negate)\n{\n   //\n   // parses an emacs style \\sx or \\Sx construct.\n   //\n   if(++m_position == m_end)\n   {\n      // Rewind to start of sequence:\n      --m_position;\n      while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_escape) --m_position;\n      fail(regex_constants::error_escape, m_position - m_base);\n      return false;\n   }\n   basic_char_set<charT, traits> char_set;\n   if(negate)\n      char_set.negate();\n\n   static const charT s_punct[5] = { 'p', 'u', 'n', 'c', 't', };\n\n   switch(*m_position)\n   {\n   case 's':\n   case ' ':\n      char_set.add_class(this->m_mask_space);\n      break;\n   case 'w':\n      char_set.add_class(this->m_word_mask);\n      break;\n   case '_':\n      char_set.add_single(digraph<charT>(charT('$'))); \n      char_set.add_single(digraph<charT>(charT('&'))); \n      char_set.add_single(digraph<charT>(charT('*'))); \n      char_set.add_single(digraph<charT>(charT('+'))); \n      char_set.add_single(digraph<charT>(charT('-'))); \n      char_set.add_single(digraph<charT>(charT('_'))); \n      char_set.add_single(digraph<charT>(charT('<'))); \n      char_set.add_single(digraph<charT>(charT('>'))); \n      break;\n   case '.':\n      char_set.add_class(this->m_traits.lookup_classname(s_punct, s_punct+5));\n      break;\n   case '(':\n      char_set.add_single(digraph<charT>(charT('('))); \n      char_set.add_single(digraph<charT>(charT('['))); \n      char_set.add_single(digraph<charT>(charT('{'))); \n      break;\n   case ')':\n      char_set.add_single(digraph<charT>(charT(')'))); \n      char_set.add_single(digraph<charT>(charT(']'))); \n      char_set.add_single(digraph<charT>(charT('}'))); \n      break;\n   case '\"':\n      char_set.add_single(digraph<charT>(charT('\"'))); \n      char_set.add_single(digraph<charT>(charT('\\''))); \n      char_set.add_single(digraph<charT>(charT('`'))); \n      break;\n   case '\\'':\n      char_set.add_single(digraph<charT>(charT('\\''))); \n      char_set.add_single(digraph<charT>(charT(','))); \n      char_set.add_single(digraph<charT>(charT('#'))); \n      break;\n   case '<':\n      char_set.add_single(digraph<charT>(charT(';'))); \n      break;\n   case '>':\n      char_set.add_single(digraph<charT>(charT('\\n'))); \n      char_set.add_single(digraph<charT>(charT('\\f'))); \n      break;\n   default:\n      fail(regex_constants::error_ctype, m_position - m_base);\n      return false;\n   }\n   if(0 == this->append_set(char_set))\n   {\n      fail(regex_constants::error_ctype, m_position - m_base);\n      return false;\n   }\n   ++m_position;\n   return true;\n}\n\ntemplate <class charT, class traits>\nregex_constants::syntax_option_type basic_regex_parser<charT, traits>::parse_options()\n{\n   // we have a (?imsx-imsx) group, convert it into a set of flags:\n   regex_constants::syntax_option_type f = this->flags();\n   bool breakout = false;\n   do\n   {\n      switch(*m_position)\n      {\n      case 's':\n         f |= regex_constants::mod_s;\n         f &= ~regex_constants::no_mod_s;\n         break;\n      case 'm':\n         f &= ~regex_constants::no_mod_m;\n         break;\n      case 'i':\n         f |= regex_constants::icase;\n         break;\n      case 'x':\n         f |= regex_constants::mod_x;\n         break;\n      default:\n         breakout = true;\n         continue;\n      }\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_paren, m_position - m_base);\n         return false;\n      }\n   }\n   while(!breakout);\n   \n   breakout = false;\n\n   if(*m_position == static_cast<charT>('-'))\n   {\n      if(++m_position == m_end)\n      {\n         // Rewind to start of (? sequence:\n         --m_position;\n         while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n         fail(regex_constants::error_paren, m_position - m_base);\n         return false;\n      }\n      do\n      {\n         switch(*m_position)\n         {\n         case 's':\n            f &= ~regex_constants::mod_s;\n            f |= regex_constants::no_mod_s;\n            break;\n         case 'm':\n            f |= regex_constants::no_mod_m;\n            break;\n         case 'i':\n            f &= ~regex_constants::icase;\n            break;\n         case 'x':\n            f &= ~regex_constants::mod_x;\n            break;\n         default:\n            breakout = true;\n            continue;\n         }\n         if(++m_position == m_end)\n         {\n            // Rewind to start of (? sequence:\n            --m_position;\n            while(this->m_traits.syntax_type(*m_position) != regex_constants::syntax_open_mark) --m_position;\n            fail(regex_constants::error_paren, m_position - m_base);\n            return false;\n         }\n      }\n      while(!breakout);\n   }\n   return f;\n}\n\ntemplate <class charT, class traits>\nbool basic_regex_parser<charT, traits>::unwind_alts(std::ptrdiff_t last_paren_start)\n{\n   //\n   // If we didn't actually add any states after the last \n   // alternative then that's an error:\n   //\n   if((this->m_alt_insert_point == static_cast<std::ptrdiff_t>(this->m_pdata->m_data.size()))\n      && (!m_alt_jumps.empty()) && (m_alt_jumps.back() > last_paren_start)\n      &&\n      !(\n         ((this->flags() & regbase::main_option_type) == regbase::perl_syntax_group)\n           &&\n         ((this->flags() & regbase::no_empty_expressions) == 0)\n        )\n      )\n   {\n      fail(regex_constants::error_empty, this->m_position - this->m_base, \"Can't terminate a sub-expression with an alternation operator |.\");\n      return false;\n   }\n   // \n   // Fix up our alternatives:\n   //\n   while((!m_alt_jumps.empty()) && (m_alt_jumps.back() > last_paren_start))\n   {\n      //\n      // fix up the jump to point to the end of the states\n      // that we've just added:\n      //\n      std::ptrdiff_t jump_offset = m_alt_jumps.back();\n      m_alt_jumps.pop_back();\n      this->m_pdata->m_data.align();\n      re_jump* jmp = static_cast<re_jump*>(this->getaddress(jump_offset));\n      if (jmp->type != syntax_element_jump)\n      {\n         // Something really bad happened, this used to be an assert, \n         // but we'll make it an error just in case we should ever get here.\n         fail(regex_constants::error_unknown, this->m_position - this->m_base, \"Internal logic failed while compiling the expression, probably you added a repeat to something non-repeatable!\");\n         return false;\n      }\n      jmp->alt.i = this->m_pdata->m_data.size() - jump_offset;\n   }\n   return true;\n}\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/c_regex_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         c_regex_traits.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression traits class that wraps the global C locale.\n  */\n\n#ifndef BOOST_C_REGEX_TRAITS_HPP_INCLUDED\n#define BOOST_C_REGEX_TRAITS_HPP_INCLUDED\n\n#include <boost/regex/config.hpp>\n#include <boost/regex/v5/regex_workaround.hpp>\n#include <cctype>\n\nnamespace boost{\n\n   namespace BOOST_REGEX_DETAIL_NS {\n\n      enum\n      {\n         char_class_space = 1 << 0,\n         char_class_print = 1 << 1,\n         char_class_cntrl = 1 << 2,\n         char_class_upper = 1 << 3,\n         char_class_lower = 1 << 4,\n         char_class_alpha = 1 << 5,\n         char_class_digit = 1 << 6,\n         char_class_punct = 1 << 7,\n         char_class_xdigit = 1 << 8,\n         char_class_alnum = char_class_alpha | char_class_digit,\n         char_class_graph = char_class_alnum | char_class_punct,\n         char_class_blank = 1 << 9,\n         char_class_word = 1 << 10,\n         char_class_unicode = 1 << 11,\n         char_class_horizontal = 1 << 12,\n         char_class_vertical = 1 << 13\n      };\n\n   }\n\ntemplate <class charT>\nstruct c_regex_traits;\n\ntemplate<>\nstruct c_regex_traits<char>\n{\n   c_regex_traits(){}\n   typedef char char_type;\n   typedef std::size_t size_type;\n   typedef std::string string_type;\n   struct locale_type{};\n   typedef std::uint32_t char_class_type;\n\n   static size_type length(const char_type* p) \n   { \n      return (std::strlen)(p); \n   }\n\n   char translate(char c) const \n   { \n      return c; \n   }\n   char translate_nocase(char c) const \n   { \n      return static_cast<char>((std::tolower)(static_cast<unsigned char>(c))); \n   }\n\n   static string_type  transform(const char* p1, const char* p2);\n   static string_type  transform_primary(const char* p1, const char* p2);\n\n   static char_class_type  lookup_classname(const char* p1, const char* p2);\n   static string_type  lookup_collatename(const char* p1, const char* p2);\n\n   static bool  isctype(char, char_class_type);\n   static int  value(char, int);\n\n   locale_type imbue(locale_type l)\n   { return l; }\n   locale_type getloc()const\n   { return locale_type(); }\n\nprivate:\n   // this type is not copyable:\n   c_regex_traits(const c_regex_traits&);\n   c_regex_traits& operator=(const c_regex_traits&);\n};\n\n#ifndef BOOST_NO_WREGEX\ntemplate<>\nstruct c_regex_traits<wchar_t>\n{\n   c_regex_traits(){}\n   typedef wchar_t char_type;\n   typedef std::size_t size_type;\n   typedef std::wstring string_type;\n   struct locale_type{};\n   typedef std::uint32_t char_class_type;\n\n   static size_type length(const char_type* p) \n   { \n      return (std::wcslen)(p); \n   }\n\n   wchar_t translate(wchar_t c) const \n   { \n      return c; \n   }\n   wchar_t translate_nocase(wchar_t c) const \n   { \n      return (std::towlower)(c); \n   }\n\n   static string_type  transform(const wchar_t* p1, const wchar_t* p2);\n   static string_type  transform_primary(const wchar_t* p1, const wchar_t* p2);\n\n   static char_class_type  lookup_classname(const wchar_t* p1, const wchar_t* p2);\n   static string_type  lookup_collatename(const wchar_t* p1, const wchar_t* p2);\n\n   static bool  isctype(wchar_t, char_class_type);\n   static int  value(wchar_t, int);\n\n   locale_type imbue(locale_type l)\n   { return l; }\n   locale_type getloc()const\n   { return locale_type(); }\n\nprivate:\n   // this type is not copyable:\n   c_regex_traits(const c_regex_traits&);\n   c_regex_traits& operator=(const c_regex_traits&);\n};\n\n#endif // BOOST_NO_WREGEX\n\ninline c_regex_traits<char>::string_type  c_regex_traits<char>::transform(const char* p1, const char* p2)\n{\n   std::string result(10, ' ');\n   std::size_t s = result.size();\n   std::size_t r;\n   std::string src(p1, p2);\n   while (s < (r = std::strxfrm(&*result.begin(), src.c_str(), s)))\n   {\n#if defined(_CPPLIB_VER)\n      //\n      // A bug in VC11 and 12 causes the program to hang if we pass a null-string\n      // to std::strxfrm, but only for certain locales :-(\n      // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware).\n      //\n      if (r == INT_MAX)\n      {\n         result.erase();\n         result.insert(result.begin(), static_cast<char>(0));\n         return result;\n      }\n#endif\n      result.append(r - s + 3, ' ');\n      s = result.size();\n   }\n   result.erase(r);\n   return result;\n}\n\ninline c_regex_traits<char>::string_type  c_regex_traits<char>::transform_primary(const char* p1, const char* p2)\n{\n   static char s_delim;\n   static const int s_collate_type = ::boost::BOOST_REGEX_DETAIL_NS::find_sort_syntax(static_cast<c_regex_traits<char>*>(0), &s_delim);\n   std::string result;\n   //\n   // What we do here depends upon the format of the sort key returned by\n   // sort key returned by this->transform:\n   //\n   switch (s_collate_type)\n   {\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_C:\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_unknown:\n      // the best we can do is translate to lower case, then get a regular sort key:\n   {\n      result.assign(p1, p2);\n      for (std::string::size_type i = 0; i < result.size(); ++i)\n         result[i] = static_cast<char>((std::tolower)(static_cast<unsigned char>(result[i])));\n      result = transform(&*result.begin(), &*result.begin() + result.size());\n      break;\n   }\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_fixed:\n   {\n      // get a regular sort key, and then truncate it:\n      result = transform(p1, p2);\n      result.erase(s_delim);\n      break;\n   }\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_delim:\n      // get a regular sort key, and then truncate everything after the delim:\n      result = transform(p1, p2);\n      if ((!result.empty()) && (result[0] == s_delim))\n         break;\n      std::size_t i;\n      for (i = 0; i < result.size(); ++i)\n      {\n         if (result[i] == s_delim)\n            break;\n      }\n      result.erase(i);\n      break;\n   }\n   if (result.empty())\n      result = std::string(1, char(0));\n   return result;\n}\n\ninline c_regex_traits<char>::char_class_type  c_regex_traits<char>::lookup_classname(const char* p1, const char* p2)\n{\n   using namespace BOOST_REGEX_DETAIL_NS;\n   static const char_class_type masks[] =\n   {\n      0,\n      char_class_alnum,\n      char_class_alpha,\n      char_class_blank,\n      char_class_cntrl,\n      char_class_digit,\n      char_class_digit,\n      char_class_graph,\n      char_class_horizontal,\n      char_class_lower,\n      char_class_lower,\n      char_class_print,\n      char_class_punct,\n      char_class_space,\n      char_class_space,\n      char_class_upper,\n      char_class_unicode,\n      char_class_upper,\n      char_class_vertical,\n      char_class_alnum | char_class_word,\n      char_class_alnum | char_class_word,\n      char_class_xdigit,\n   };\n\n   int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2);\n   if (idx < 0)\n   {\n      std::string s(p1, p2);\n      for (std::string::size_type i = 0; i < s.size(); ++i)\n         s[i] = static_cast<char>((std::tolower)(static_cast<unsigned char>(s[i])));\n      idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size());\n   }\n   BOOST_REGEX_ASSERT(std::size_t(idx) + 1u < sizeof(masks) / sizeof(masks[0]));\n   return masks[idx + 1];\n}\n\ninline bool  c_regex_traits<char>::isctype(char c, char_class_type mask)\n{\n   using namespace BOOST_REGEX_DETAIL_NS;\n   return\n      ((mask & char_class_space) && (std::isspace)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_print) && (std::isprint)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_cntrl) && (std::iscntrl)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_upper) && (std::isupper)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_lower) && (std::islower)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_alpha) && (std::isalpha)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_digit) && (std::isdigit)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_punct) && (std::ispunct)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_xdigit) && (std::isxdigit)(static_cast<unsigned char>(c)))\n      || ((mask & char_class_blank) && (std::isspace)(static_cast<unsigned char>(c)) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c))\n      || ((mask & char_class_word) && (c == '_'))\n      || ((mask & char_class_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\\v')))\n      || ((mask & char_class_horizontal) && (std::isspace)(static_cast<unsigned char>(c)) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && (c != '\\v'));\n}\n\ninline c_regex_traits<char>::string_type  c_regex_traits<char>::lookup_collatename(const char* p1, const char* p2)\n{\n   std::string s(p1, p2);\n   s = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(s);\n   if (s.empty() && (p2 - p1 == 1))\n      s.append(1, *p1);\n   return s;\n}\n\ninline int  c_regex_traits<char>::value(char c, int radix)\n{\n   char b[2] = { c, '\\0', };\n   char* ep;\n   int result = std::strtol(b, &ep, radix);\n   if (ep == b)\n      return -1;\n   return result;\n}\n\n#ifndef BOOST_NO_WREGEX\n\ninline c_regex_traits<wchar_t>::string_type  c_regex_traits<wchar_t>::transform(const wchar_t* p1, const wchar_t* p2)\n{\n   std::size_t r;\n   std::size_t s = 10;\n   std::wstring src(p1, p2);\n   std::wstring result(s, L' ');\n   while (s < (r = std::wcsxfrm(&*result.begin(), src.c_str(), s)))\n   {\n#if defined(_CPPLIB_VER)\n      //\n      // A bug in VC11 and 12 causes the program to hang if we pass a null-string\n      // to std::strxfrm, but only for certain locales :-(\n      // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware).\n      //\n      if (r == INT_MAX)\n      {\n         result.erase();\n         result.insert(result.begin(), static_cast<wchar_t>(0));\n         return result;\n      }\n#endif\n      result.append(r - s + 3, L' ');\n      s = result.size();\n   }\n   result.erase(r);\n   return result;\n}\n\ninline c_regex_traits<wchar_t>::string_type  c_regex_traits<wchar_t>::transform_primary(const wchar_t* p1, const wchar_t* p2)\n{\n   static wchar_t s_delim;\n   static const int s_collate_type = ::boost::BOOST_REGEX_DETAIL_NS::find_sort_syntax(static_cast<const c_regex_traits<wchar_t>*>(0), &s_delim);\n   std::wstring result;\n   //\n   // What we do here depends upon the format of the sort key returned by\n   // sort key returned by this->transform:\n   //\n   switch (s_collate_type)\n   {\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_C:\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_unknown:\n      // the best we can do is translate to lower case, then get a regular sort key:\n   {\n      result.assign(p1, p2);\n      for (std::wstring::size_type i = 0; i < result.size(); ++i)\n         result[i] = (std::towlower)(result[i]);\n      result = c_regex_traits<wchar_t>::transform(&*result.begin(), &*result.begin() + result.size());\n      break;\n   }\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_fixed:\n   {\n      // get a regular sort key, and then truncate it:\n      result = c_regex_traits<wchar_t>::transform(&*result.begin(), &*result.begin() + result.size());\n      result.erase(s_delim);\n      break;\n   }\n   case ::boost::BOOST_REGEX_DETAIL_NS::sort_delim:\n      // get a regular sort key, and then truncate everything after the delim:\n      result = c_regex_traits<wchar_t>::transform(&*result.begin(), &*result.begin() + result.size());\n      if ((!result.empty()) && (result[0] == s_delim))\n         break;\n      std::size_t i;\n      for (i = 0; i < result.size(); ++i)\n      {\n         if (result[i] == s_delim)\n            break;\n      }\n      result.erase(i);\n      break;\n   }\n   if (result.empty())\n      result = std::wstring(1, char(0));\n   return result;\n}\n\ninline c_regex_traits<wchar_t>::char_class_type  c_regex_traits<wchar_t>::lookup_classname(const wchar_t* p1, const wchar_t* p2)\n{\n   using namespace BOOST_REGEX_DETAIL_NS;\n   static const char_class_type masks[] =\n   {\n      0,\n      char_class_alnum,\n      char_class_alpha,\n      char_class_blank,\n      char_class_cntrl,\n      char_class_digit,\n      char_class_digit,\n      char_class_graph,\n      char_class_horizontal,\n      char_class_lower,\n      char_class_lower,\n      char_class_print,\n      char_class_punct,\n      char_class_space,\n      char_class_space,\n      char_class_upper,\n      char_class_unicode,\n      char_class_upper,\n      char_class_vertical,\n      char_class_alnum | char_class_word,\n      char_class_alnum | char_class_word,\n      char_class_xdigit,\n   };\n\n   int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2);\n   if (idx < 0)\n   {\n      std::wstring s(p1, p2);\n      for (std::wstring::size_type i = 0; i < s.size(); ++i)\n         s[i] = (std::towlower)(s[i]);\n      idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size());\n   }\n   BOOST_REGEX_ASSERT(idx + 1 < static_cast<int>(sizeof(masks) / sizeof(masks[0])));\n   return masks[idx + 1];\n}\n\ninline bool  c_regex_traits<wchar_t>::isctype(wchar_t c, char_class_type mask)\n{\n   using namespace BOOST_REGEX_DETAIL_NS;\n   return\n      ((mask & char_class_space) && (std::iswspace)(c))\n      || ((mask & char_class_print) && (std::iswprint)(c))\n      || ((mask & char_class_cntrl) && (std::iswcntrl)(c))\n      || ((mask & char_class_upper) && (std::iswupper)(c))\n      || ((mask & char_class_lower) && (std::iswlower)(c))\n      || ((mask & char_class_alpha) && (std::iswalpha)(c))\n      || ((mask & char_class_digit) && (std::iswdigit)(c))\n      || ((mask & char_class_punct) && (std::iswpunct)(c))\n      || ((mask & char_class_xdigit) && (std::iswxdigit)(c))\n      || ((mask & char_class_blank) && (std::iswspace)(c) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c))\n      || ((mask & char_class_word) && (c == '_'))\n      || ((mask & char_class_unicode) && (c & ~static_cast<wchar_t>(0xff)))\n      || ((mask & char_class_vertical) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == L'\\v')))\n      || ((mask & char_class_horizontal) && (std::iswspace)(c) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && (c != L'\\v'));\n}\n\ninline c_regex_traits<wchar_t>::string_type  c_regex_traits<wchar_t>::lookup_collatename(const wchar_t* p1, const wchar_t* p2)\n{\n   std::string name;\n   // Usual msvc warning suppression does not work here with std::string template constructor.... use a workaround instead:\n   for (const wchar_t* pos = p1; pos != p2; ++pos)\n      name.push_back((char)*pos);\n   name = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(name);\n   if (!name.empty())\n      return string_type(name.begin(), name.end());\n   if (p2 - p1 == 1)\n      return string_type(1, *p1);\n   return string_type();\n}\n\ninline int  c_regex_traits<wchar_t>::value(wchar_t c, int radix)\n{\n#ifdef BOOST_BORLANDC\n   // workaround for broken wcstol:\n   if ((std::iswxdigit)(c) == 0)\n      return -1;\n#endif\n   wchar_t b[2] = { c, '\\0', };\n   wchar_t* ep;\n   int result = std::wcstol(b, &ep, radix);\n   if (ep == b)\n      return -1;\n   return result;\n}\n\n#endif\n\n}\n\n#endif\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/char_regex_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the\n * Boost Software License, Version 1.0. (See accompanying file\n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         char_regex_traits.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares deprecated traits classes char_regex_traits<>.\n  */\n\n\n#ifndef BOOST_REGEX_V5_CHAR_REGEX_TRAITS_HPP\n#define BOOST_REGEX_V5_CHAR_REGEX_TRAITS_HPP\n\nnamespace boost{\n\nnamespace deprecated{\n//\n// class char_regex_traits_i\n// provides case insensitive traits classes (deprecated):\ntemplate <class charT>\nclass char_regex_traits_i : public regex_traits<charT> {};\n\ntemplate<>\nclass char_regex_traits_i<char> : public regex_traits<char>\n{\npublic:\n   typedef char char_type;\n   typedef unsigned char uchar_type;\n   typedef unsigned int size_type;\n   typedef regex_traits<char> base_type;\n\n};\n\n#ifndef BOOST_NO_WREGEX\ntemplate<>\nclass char_regex_traits_i<wchar_t> : public regex_traits<wchar_t>\n{\npublic:\n   typedef wchar_t char_type;\n   typedef unsigned short uchar_type;\n   typedef unsigned int size_type;\n   typedef regex_traits<wchar_t> base_type;\n\n};\n#endif\n} // namespace deprecated\n} // namespace boost\n\n#endif // include\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/cpp_regex_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 2004 John Maddock\n * Copyright 2011 Garmin Ltd. or its subsidiaries\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         cpp_regex_traits.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression traits class cpp_regex_traits.\n  */\n\n#ifndef BOOST_CPP_REGEX_TRAITS_HPP_INCLUDED\n#define BOOST_CPP_REGEX_TRAITS_HPP_INCLUDED\n\n#include <boost/regex/config.hpp>\n#include <cstdint>\n#include <locale>\n#include <type_traits>\n\n#include <boost/regex/pattern_except.hpp>\n#include <boost/regex/v5/regex_traits_defaults.hpp>\n\n#ifdef BOOST_HAS_THREADS\n#include <mutex>\n#endif\n#include <boost/regex/v5/primary_transform.hpp>\n#include <boost/regex/v5/object_cache.hpp>\n\n#include <climits>\n#include <ios>\n#include <istream>\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4786 4251)\n#endif\n\nnamespace boost{ \n\n//\n// forward declaration is needed by some compilers:\n//\ntemplate <class charT>\nclass cpp_regex_traits;\n   \nnamespace BOOST_REGEX_DETAIL_NS{\n\n//\n// class parser_buf:\n// acts as a stream buffer which wraps around a pair of pointers:\n//\ntemplate <class charT,\n          class traits = ::std::char_traits<charT> >\nclass parser_buf : public ::std::basic_streambuf<charT, traits>\n{\n   typedef ::std::basic_streambuf<charT, traits> base_type;\n   typedef typename base_type::int_type int_type;\n   typedef typename base_type::char_type char_type;\n   typedef typename base_type::pos_type pos_type;\n   typedef ::std::streamsize streamsize;\n   typedef typename base_type::off_type off_type;\npublic:\n   parser_buf() : base_type() { setbuf(0, 0); }\n   const charT* getnext() { return this->gptr(); }\nprotected:\n   std::basic_streambuf<charT, traits>* setbuf(char_type* s, streamsize n) override;\n   typename parser_buf<charT, traits>::pos_type seekpos(pos_type sp, ::std::ios_base::openmode which) override;\n   typename parser_buf<charT, traits>::pos_type seekoff(off_type off, ::std::ios_base::seekdir way, ::std::ios_base::openmode which) override;\nprivate:\n   parser_buf& operator=(const parser_buf&);\n   parser_buf(const parser_buf&);\n};\n\ntemplate<class charT, class traits>\nstd::basic_streambuf<charT, traits>*\nparser_buf<charT, traits>::setbuf(char_type* s, streamsize n)\n{\n   this->setg(s, s, s + n);\n   return this;\n}\n\ntemplate<class charT, class traits>\ntypename parser_buf<charT, traits>::pos_type\nparser_buf<charT, traits>::seekoff(off_type off, ::std::ios_base::seekdir way, ::std::ios_base::openmode which)\n{\n   if(which & ::std::ios_base::out)\n      return pos_type(off_type(-1));\n   std::ptrdiff_t size = this->egptr() - this->eback();\n   std::ptrdiff_t pos = this->gptr() - this->eback();\n   charT* g = this->eback();\n   switch(static_cast<std::intmax_t>(way))\n   {\n   case ::std::ios_base::beg:\n      if((off < 0) || (off > size))\n         return pos_type(off_type(-1));\n      else\n         this->setg(g, g + off, g + size);\n      break;\n   case ::std::ios_base::end:\n      if((off < 0) || (off > size))\n         return pos_type(off_type(-1));\n      else\n         this->setg(g, g + size - off, g + size);\n      break;\n   case ::std::ios_base::cur:\n   {\n      std::ptrdiff_t newpos = static_cast<std::ptrdiff_t>(pos + off);\n      if((newpos < 0) || (newpos > size))\n         return pos_type(off_type(-1));\n      else\n         this->setg(g, g + newpos, g + size);\n      break;\n   }\n   default: ;\n   }\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4244)\n#endif\n   return static_cast<pos_type>(this->gptr() - this->eback());\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate<class charT, class traits>\ntypename parser_buf<charT, traits>::pos_type\nparser_buf<charT, traits>::seekpos(pos_type sp, ::std::ios_base::openmode which)\n{\n   if(which & ::std::ios_base::out)\n      return pos_type(off_type(-1));\n   off_type size = static_cast<off_type>(this->egptr() - this->eback());\n   charT* g = this->eback();\n   if(off_type(sp) <= size)\n   {\n      this->setg(g, g + off_type(sp), g + size);\n   }\n   return pos_type(off_type(-1));\n}\n\n//\n// class cpp_regex_traits_base:\n// acts as a container for locale and the facets we are using.\n//\ntemplate <class charT>\nstruct cpp_regex_traits_base\n{\n   cpp_regex_traits_base(const std::locale& l)\n   { (void)imbue(l); }\n   std::locale imbue(const std::locale& l);\n\n   std::locale m_locale;\n   std::ctype<charT> const* m_pctype;\n   std::messages<charT> const* m_pmessages;\n   std::collate<charT> const* m_pcollate;\n\n   bool operator<(const cpp_regex_traits_base& b)const\n   {\n      if(m_pctype == b.m_pctype)\n      {\n         if(m_pmessages == b.m_pmessages)\n         {\n            return m_pcollate < b.m_pcollate;\n         }\n         return m_pmessages < b.m_pmessages;\n      }\n      return m_pctype < b.m_pctype;\n   }\n   bool operator==(const cpp_regex_traits_base& b)const\n   {\n      return (m_pctype == b.m_pctype) \n         && (m_pmessages == b.m_pmessages) \n         && (m_pcollate == b.m_pcollate);\n   }\n};\n\ntemplate <class charT>\nstd::locale cpp_regex_traits_base<charT>::imbue(const std::locale& l)\n{\n   std::locale result(m_locale);\n   m_locale = l;\n   m_pctype = &std::use_facet<std::ctype<charT>>(l);\n   m_pmessages = std::has_facet<std::messages<charT> >(l) ? &std::use_facet<std::messages<charT> >(l) : 0;\n   m_pcollate = &std::use_facet<std::collate<charT> >(l);\n   return result;\n}\n\n//\n// class cpp_regex_traits_char_layer:\n// implements methods that require specialization for narrow characters:\n//\ntemplate <class charT>\nclass cpp_regex_traits_char_layer : public cpp_regex_traits_base<charT>\n{\n   typedef std::basic_string<charT> string_type;\n   typedef std::map<charT, regex_constants::syntax_type> map_type;\n   typedef typename map_type::const_iterator map_iterator_type;\npublic:\n   cpp_regex_traits_char_layer(const std::locale& l)\n      : cpp_regex_traits_base<charT>(l)\n   {\n      init();\n   }\n   cpp_regex_traits_char_layer(const cpp_regex_traits_base<charT>& b)\n      : cpp_regex_traits_base<charT>(b)\n   {\n      init();\n   }\n   void init();\n\n   regex_constants::syntax_type syntax_type(charT c)const\n   {\n      map_iterator_type i = m_char_map.find(c);\n      return ((i == m_char_map.end()) ? 0 : i->second);\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(charT c) const\n   {\n      map_iterator_type i = m_char_map.find(c);\n      if(i == m_char_map.end())\n      {\n         if(this->m_pctype->is(std::ctype_base::lower, c)) return regex_constants::escape_type_class;\n         if(this->m_pctype->is(std::ctype_base::upper, c)) return regex_constants::escape_type_not_class;\n         return 0;\n      }\n      return i->second;\n   }\n\nprivate:\n   string_type get_default_message(regex_constants::syntax_type);\n   // TODO: use a hash table when available!\n   map_type m_char_map;\n};\n\ntemplate <class charT>\nvoid cpp_regex_traits_char_layer<charT>::init()\n{\n   // we need to start by initialising our syntax map so we know which\n   // character is used for which purpose:\n#ifndef __IBMCPP__\n   typename std::messages<charT>::catalog cat = static_cast<std::messages<char>::catalog>(-1);\n#else\n   typename std::messages<charT>::catalog cat = reinterpret_cast<std::messages<char>::catalog>(-1);\n#endif\n   std::string cat_name(cpp_regex_traits<charT>::get_catalog_name());\n   if((!cat_name.empty()) && (this->m_pmessages != 0))\n   {\n      cat = this->m_pmessages->open(\n         cat_name, \n         this->m_locale);\n      if((int)cat < 0)\n      {\n         std::string m(\"Unable to open message catalog: \");\n         std::runtime_error err(m + cat_name);\n         boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n      }\n   }\n   //\n   // if we have a valid catalog then load our messages:\n   //\n   if((int)cat >= 0)\n   {\n#ifndef BOOST_NO_EXCEPTIONS\n      try{\n#endif\n         for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n         {\n            string_type mss = this->m_pmessages->get(cat, 0, i, get_default_message(i));\n            for(typename string_type::size_type j = 0; j < mss.size(); ++j)\n            {\n               m_char_map[mss[j]] = i;\n            }\n         }\n         this->m_pmessages->close(cat);\n#ifndef BOOST_NO_EXCEPTIONS\n      }\n      catch(...)\n      {\n         if(this->m_pmessages)\n            this->m_pmessages->close(cat);\n         throw;\n      }\n#endif\n   }\n   else\n   {\n      for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n      {\n         const char* ptr = get_default_syntax(i);\n         while(ptr && *ptr)\n         {\n            m_char_map[this->m_pctype->widen(*ptr)] = i;\n            ++ptr;\n         }\n      }\n   }\n}\n\ntemplate <class charT>\ntypename cpp_regex_traits_char_layer<charT>::string_type \n   cpp_regex_traits_char_layer<charT>::get_default_message(regex_constants::syntax_type i)\n{\n   const char* ptr = get_default_syntax(i);\n   string_type result;\n   while(ptr && *ptr)\n   {\n      result.append(1, this->m_pctype->widen(*ptr));\n      ++ptr;\n   }\n   return result;\n}\n\n//\n// specialized version for narrow characters:\n//\ntemplate <>\nclass cpp_regex_traits_char_layer<char> : public cpp_regex_traits_base<char>\n{\n   typedef std::string string_type;\npublic:\n   cpp_regex_traits_char_layer(const std::locale& l)\n   : cpp_regex_traits_base<char>(l)\n   {\n      init();\n   }\n   cpp_regex_traits_char_layer(const cpp_regex_traits_base<char>& l)\n   : cpp_regex_traits_base<char>(l)\n   {\n      init();\n   }\n\n   regex_constants::syntax_type syntax_type(char c)const\n   {\n      return m_char_map[static_cast<unsigned char>(c)];\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(char c) const\n   {\n      return m_char_map[static_cast<unsigned char>(c)];\n   }\n\nprivate:\n   regex_constants::syntax_type m_char_map[1u << CHAR_BIT];\n   void init();\n};\n\n//\n// class cpp_regex_traits_implementation:\n// provides pimpl implementation for cpp_regex_traits.\n//\ntemplate <class charT>\nclass cpp_regex_traits_implementation : public cpp_regex_traits_char_layer<charT>\n{\npublic:\n   typedef typename cpp_regex_traits<charT>::char_class_type      char_class_type;\n   typedef typename std::ctype<charT>::mask                       native_mask_type;\n   typedef typename std::make_unsigned<native_mask_type>::type    unsigned_native_mask_type;\n   static const char_class_type mask_blank = 1u << 24;\n   static const char_class_type mask_word = 1u << 25;\n   static const char_class_type mask_unicode = 1u << 26;\n   static const char_class_type mask_horizontal = 1u << 27;\n   static const char_class_type mask_vertical = 1u << 28;\n\n   typedef std::basic_string<charT> string_type;\n   typedef charT char_type;\n   //cpp_regex_traits_implementation();\n   cpp_regex_traits_implementation(const std::locale& l)\n      : cpp_regex_traits_char_layer<charT>(l)\n   {\n      init();\n   }\n   cpp_regex_traits_implementation(const cpp_regex_traits_base<charT>& l)\n      : cpp_regex_traits_char_layer<charT>(l)\n   {\n      init();\n   }\n   std::string error_string(regex_constants::error_type n) const\n   {\n      if(!m_error_strings.empty())\n      {\n         std::map<int, std::string>::const_iterator p = m_error_strings.find(n);\n         return (p == m_error_strings.end()) ? std::string(get_default_error_string(n)) : p->second;\n      }\n      return get_default_error_string(n);\n   }\n   char_class_type lookup_classname(const charT* p1, const charT* p2) const\n   {\n      char_class_type result = lookup_classname_imp(p1, p2);\n      if(result == 0)\n      {\n         string_type temp(p1, p2);\n         this->m_pctype->tolower(&*temp.begin(), &*temp.begin() + temp.size());\n         result = lookup_classname_imp(&*temp.begin(), &*temp.begin() + temp.size());\n      }\n      return result;\n   }\n   string_type lookup_collatename(const charT* p1, const charT* p2) const;\n   string_type transform_primary(const charT* p1, const charT* p2) const;\n   string_type transform(const charT* p1, const charT* p2) const;\nprivate:\n   std::map<int, std::string>     m_error_strings;   // error messages indexed by numberic ID\n   std::map<string_type, char_class_type>  m_custom_class_names; // character class names\n   std::map<string_type, string_type>      m_custom_collate_names; // collating element names\n   unsigned                       m_collate_type;    // the form of the collation string\n   charT                          m_collate_delim;   // the collation group delimiter\n   //\n   // helpers:\n   //\n   char_class_type lookup_classname_imp(const charT* p1, const charT* p2) const;\n   void init();\n};\n\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type const cpp_regex_traits_implementation<charT>::mask_blank;\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type const cpp_regex_traits_implementation<charT>::mask_word;\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type const cpp_regex_traits_implementation<charT>::mask_unicode;\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type const cpp_regex_traits_implementation<charT>::mask_vertical;\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type const cpp_regex_traits_implementation<charT>::mask_horizontal;\n\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::string_type \n   cpp_regex_traits_implementation<charT>::transform_primary(const charT* p1, const charT* p2) const\n{\n   //\n   // PRECONDITIONS:\n   //\n   // A bug in gcc 3.2 (and maybe other versions as well) treats\n   // p1 as a null terminated string, for efficiency reasons \n   // we work around this elsewhere, but just assert here that\n   // we adhere to gcc's (buggy) preconditions...\n   //\n   BOOST_REGEX_ASSERT(*p2 == 0);\n   string_type result;\n#if defined(_CPPLIB_VER)\n   //\n   // A bug in VC11 and 12 causes the program to hang if we pass a null-string\n   // to std::collate::transform, but only for certain locales :-(\n   // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware).\n   //\n   if(*p1 == 0)\n   {\n      return string_type(1, charT(0));\n   }\n#endif\n   //\n   // swallowing all exceptions here is a bad idea\n   // however at least one std lib will always throw\n   // std::bad_alloc for certain arguments...\n   //\n#ifndef BOOST_NO_EXCEPTIONS\n   try{\n#endif\n      //\n      // What we do here depends upon the format of the sort key returned by\n      // sort key returned by this->transform:\n      //\n      switch(m_collate_type)\n      {\n      case sort_C:\n      case sort_unknown:\n         // the best we can do is translate to lower case, then get a regular sort key:\n         {\n            result.assign(p1, p2);\n            this->m_pctype->tolower(&*result.begin(), &*result.begin() + result.size());\n            result = this->m_pcollate->transform(&*result.begin(), &*result.begin() + result.size());\n            break;\n         }\n      case sort_fixed:\n         {\n            // get a regular sort key, and then truncate it:\n            result.assign(this->m_pcollate->transform(p1, p2));\n            result.erase(this->m_collate_delim);\n            break;\n         }\n      case sort_delim:\n            // get a regular sort key, and then truncate everything after the delim:\n            result.assign(this->m_pcollate->transform(p1, p2));\n            std::size_t i;\n            for(i = 0; i < result.size(); ++i)\n            {\n               if(result[i] == m_collate_delim)\n                  break;\n            }\n            result.erase(i);\n            break;\n      }\n#ifndef BOOST_NO_EXCEPTIONS\n   }catch(...){}\n#endif\n   while((!result.empty()) && (charT(0) == *result.rbegin()))\n      result.erase(result.size() - 1);\n   if(result.empty())\n   {\n      // character is ignorable at the primary level:\n      result = string_type(1, charT(0));\n   }\n   return result;\n}\n\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::string_type \n   cpp_regex_traits_implementation<charT>::transform(const charT* p1, const charT* p2) const\n{\n   //\n   // PRECONDITIONS:\n   //\n   // A bug in gcc 3.2 (and maybe other versions as well) treats\n   // p1 as a null terminated string, for efficiency reasons \n   // we work around this elsewhere, but just assert here that\n   // we adhere to gcc's (buggy) preconditions...\n   //\n   BOOST_REGEX_ASSERT(*p2 == 0);\n   //\n   // swallowing all exceptions here is a bad idea\n   // however at least one std lib will always throw\n   // std::bad_alloc for certain arguments...\n   //\n   string_type result, result2;\n#if defined(_CPPLIB_VER)\n   //\n   // A bug in VC11 and 12 causes the program to hang if we pass a null-string\n   // to std::collate::transform, but only for certain locales :-(\n   // Probably effects Intel and Clang or any compiler using the VC std library (Dinkumware).\n   //\n   if(*p1 == 0)\n   {\n      return result;\n   }\n#endif\n#ifndef BOOST_NO_EXCEPTIONS\n   try{\n#endif\n      result = this->m_pcollate->transform(p1, p2);\n      //\n      // some implementations (Dinkumware) append unnecessary trailing \\0's:\n      while((!result.empty()) && (charT(0) == *result.rbegin()))\n         result.erase(result.size() - 1);\n      //\n      // We may have NULL's used as separators between sections of the collate string,\n      // an example would be Boost.Locale.  We have no way to detect this case via\n      // #defines since this can be used with any compiler/platform combination.\n      // Unfortunately our state machine (which was devised when all implementations\n      // used underlying C language API's) can't cope with that case.  One workaround\n      // is to replace each character with 2, fortunately this code isn't used that\n      // much as this is now slower than before :-(\n      //\n      typedef typename std::make_unsigned<charT>::type uchar_type;\n      result2.reserve(result.size() * 2 + 2);\n      for(unsigned i = 0; i < result.size(); ++i)\n      {\n         if(static_cast<uchar_type>(result[i]) == (std::numeric_limits<uchar_type>::max)())\n         {\n            result2.append(1, charT((std::numeric_limits<uchar_type>::max)())).append(1, charT('b'));\n         }\n         else\n         {\n            result2.append(1, static_cast<charT>(1 + static_cast<uchar_type>(result[i]))).append(1, charT('b') - 1);\n         }\n      }\n      BOOST_REGEX_ASSERT(std::find(result2.begin(), result2.end(), charT(0)) == result2.end());\n#ifndef BOOST_NO_EXCEPTIONS\n   }\n   catch(...)\n   {\n   }\n#endif\n   return result2;\n}\n\n\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::string_type \n   cpp_regex_traits_implementation<charT>::lookup_collatename(const charT* p1, const charT* p2) const\n{\n   typedef typename std::map<string_type, string_type>::const_iterator iter_type;\n   if(!m_custom_collate_names.empty())\n   {\n      iter_type pos = m_custom_collate_names.find(string_type(p1, p2));\n      if(pos != m_custom_collate_names.end())\n         return pos->second;\n   }\n   std::string name(p1, p2);\n   name = lookup_default_collate_name(name);\n   if(!name.empty())\n      return string_type(name.begin(), name.end());\n   if(p2 - p1 == 1)\n      return string_type(1, *p1);\n   return string_type();\n}\n\ntemplate <class charT>\nvoid cpp_regex_traits_implementation<charT>::init()\n{\n#ifndef __IBMCPP__\n   typename std::messages<charT>::catalog cat = static_cast<std::messages<char>::catalog>(-1);\n#else\n   typename std::messages<charT>::catalog cat = reinterpret_cast<std::messages<char>::catalog>(-1);\n#endif\n   std::string cat_name(cpp_regex_traits<charT>::get_catalog_name());\n   if((!cat_name.empty()) && (this->m_pmessages != 0))\n   {\n      cat = this->m_pmessages->open(\n         cat_name, \n         this->m_locale);\n      if((int)cat < 0)\n      {\n         std::string m(\"Unable to open message catalog: \");\n         std::runtime_error err(m + cat_name);\n         boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n      }\n   }\n   //\n   // if we have a valid catalog then load our messages:\n   //\n   if((int)cat >= 0)\n   {\n      //\n      // Error messages:\n      //\n      for(boost::regex_constants::error_type i = static_cast<boost::regex_constants::error_type>(0); \n         i <= boost::regex_constants::error_unknown; \n         i = static_cast<boost::regex_constants::error_type>(i + 1))\n      {\n         const char* p = get_default_error_string(i);\n         string_type default_message;\n         while(*p)\n         {\n            default_message.append(1, this->m_pctype->widen(*p));\n            ++p;\n         }\n         string_type s = this->m_pmessages->get(cat, 0, i+200, default_message);\n         std::string result;\n         for(std::string::size_type j = 0; j < s.size(); ++j)\n         {\n            result.append(1, this->m_pctype->narrow(s[j], 0));\n         }\n         m_error_strings[i] = result;\n      }\n      //\n      // Custom class names:\n      //\n      static const char_class_type masks[16] = \n      {\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::alnum),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::alpha),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::cntrl),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::digit),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::graph),\n         cpp_regex_traits_implementation<charT>::mask_horizontal,\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::lower),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::print),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::punct),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::space),\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::upper),\n         cpp_regex_traits_implementation<charT>::mask_vertical,\n         static_cast<unsigned_native_mask_type>(std::ctype<charT>::xdigit),\n         cpp_regex_traits_implementation<charT>::mask_blank,\n         cpp_regex_traits_implementation<charT>::mask_word,\n         cpp_regex_traits_implementation<charT>::mask_unicode,\n      };\n      static const string_type null_string;\n      for(unsigned int j = 0; j <= 13; ++j)\n      {\n         string_type s(this->m_pmessages->get(cat, 0, j+300, null_string));\n         if(!s.empty())\n            this->m_custom_class_names[s] = masks[j];\n      }\n   }\n   //\n   // get the collation format used by m_pcollate:\n   //\n   m_collate_type = BOOST_REGEX_DETAIL_NS::find_sort_syntax(this, &m_collate_delim);\n}\n\ntemplate <class charT>\ntypename cpp_regex_traits_implementation<charT>::char_class_type \n   cpp_regex_traits_implementation<charT>::lookup_classname_imp(const charT* p1, const charT* p2) const\n{\n   static const char_class_type masks[22] = \n   {\n      0,\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::alnum),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::alpha),\n      cpp_regex_traits_implementation<charT>::mask_blank,\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::cntrl),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::digit),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::digit),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::graph),\n      cpp_regex_traits_implementation<charT>::mask_horizontal,\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::lower),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::lower),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::print),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::punct),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::space),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::space),\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::upper),\n      cpp_regex_traits_implementation<charT>::mask_unicode,\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::upper),\n      cpp_regex_traits_implementation<charT>::mask_vertical,\n      static_cast<unsigned_native_mask_type>(std::ctype<char>::alnum) | cpp_regex_traits_implementation<charT>::mask_word, \n      static_cast<unsigned_native_mask_type>(std::ctype<char>::alnum) | cpp_regex_traits_implementation<charT>::mask_word, \n      static_cast<unsigned_native_mask_type>(std::ctype<char>::xdigit),\n   };\n   if(!m_custom_class_names.empty())\n   {\n      typedef typename std::map<std::basic_string<charT>, char_class_type>::const_iterator map_iter;\n      map_iter pos = m_custom_class_names.find(string_type(p1, p2));\n      if(pos != m_custom_class_names.end())\n         return pos->second;\n   }\n   std::size_t state_id = 1 + BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2);\n   BOOST_REGEX_ASSERT(state_id < sizeof(masks) / sizeof(masks[0]));\n   return masks[state_id];\n}\n\ntemplate <class charT>\ninline std::shared_ptr<const cpp_regex_traits_implementation<charT> > create_cpp_regex_traits(const std::locale& l)\n{\n   cpp_regex_traits_base<charT> key(l);\n   return ::boost::object_cache<cpp_regex_traits_base<charT>, cpp_regex_traits_implementation<charT> >::get(key, 5);\n}\n\n} // BOOST_REGEX_DETAIL_NS\n\ntemplate <class charT>\nclass cpp_regex_traits\n{\nprivate:\n   typedef std::ctype<charT>            ctype_type;\npublic:\n   typedef charT                        char_type;\n   typedef std::size_t                  size_type;\n   typedef std::basic_string<char_type> string_type;\n   typedef std::locale                  locale_type;\n   typedef std::uint_least32_t          char_class_type;\n\n   struct boost_extensions_tag{};\n\n   cpp_regex_traits()\n      : m_pimpl(BOOST_REGEX_DETAIL_NS::create_cpp_regex_traits<charT>(std::locale()))\n   { }\n   static size_type length(const char_type* p)\n   {\n      return std::char_traits<charT>::length(p);\n   }\n   regex_constants::syntax_type syntax_type(charT c)const\n   {\n      return m_pimpl->syntax_type(c);\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(charT c) const\n   {\n      return m_pimpl->escape_syntax_type(c);\n   }\n   charT translate(charT c) const\n   {\n      return c;\n   }\n   charT translate_nocase(charT c) const\n   {\n      return m_pimpl->m_pctype->tolower(c);\n   }\n   charT translate(charT c, bool icase) const\n   {\n      return icase ? m_pimpl->m_pctype->tolower(c) : c;\n   }\n   charT tolower(charT c) const\n   {\n      return m_pimpl->m_pctype->tolower(c);\n   }\n   charT toupper(charT c) const\n   {\n      return m_pimpl->m_pctype->toupper(c);\n   }\n   string_type transform(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->transform(p1, p2);\n   }\n   string_type transform_primary(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->transform_primary(p1, p2);\n   }\n   char_class_type lookup_classname(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->lookup_classname(p1, p2);\n   }\n   string_type lookup_collatename(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->lookup_collatename(p1, p2);\n   }\n   bool isctype(charT c, char_class_type f) const\n   {\n      typedef typename std::ctype<charT>::mask ctype_mask;\n\n      static const ctype_mask mask_base = \n         static_cast<ctype_mask>(\n            std::ctype<charT>::alnum \n            | std::ctype<charT>::alpha\n            | std::ctype<charT>::cntrl\n            | std::ctype<charT>::digit\n            | std::ctype<charT>::graph\n            | std::ctype<charT>::lower\n            | std::ctype<charT>::print\n            | std::ctype<charT>::punct\n            | std::ctype<charT>::space\n            | std::ctype<charT>::upper\n            | std::ctype<charT>::xdigit);\n\n      if((f & mask_base) \n         && (m_pimpl->m_pctype->is(\n            static_cast<ctype_mask>(f & mask_base), c)))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_unicode) && BOOST_REGEX_DETAIL_NS::is_extended(c))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_word) && (c == '_'))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_blank) \n         && m_pimpl->m_pctype->is(std::ctype<charT>::space, c)\n         && !BOOST_REGEX_DETAIL_NS::is_separator(c))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_vertical) \n         && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\\v')))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_horizontal) \n         && this->isctype(c, std::ctype<charT>::space) && !this->isctype(c, BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT>::mask_vertical))\n         return true;\n#ifdef __CYGWIN__\n      //\n      // Cygwin has a buggy ctype facet, see https://www.cygwin.com/ml/cygwin/2012-08/msg00178.html:\n      //\n      else if((f & std::ctype<charT>::xdigit) == std::ctype<charT>::xdigit)\n      {\n         if((c >= 'a') && (c <= 'f'))\n            return true;\n         if((c >= 'A') && (c <= 'F'))\n            return true;\n      }\n#endif\n      return false;\n   }\n   std::intmax_t toi(const charT*& p1, const charT* p2, int radix)const;\n   int value(charT c, int radix)const\n   {\n      const charT* pc = &c;\n      return (int)toi(pc, pc + 1, radix);\n   }\n   locale_type imbue(locale_type l)\n   {\n      std::locale result(getloc());\n      m_pimpl = BOOST_REGEX_DETAIL_NS::create_cpp_regex_traits<charT>(l);\n      return result;\n   }\n   locale_type getloc()const\n   {\n      return m_pimpl->m_locale;\n   }\n   std::string error_string(regex_constants::error_type n) const\n   {\n      return m_pimpl->error_string(n);\n   }\n\n   //\n   // extension:\n   // set the name of the message catalog in use (defaults to \"boost_regex\").\n   //\n   static std::string catalog_name(const std::string& name);\n   static std::string get_catalog_name();\n\nprivate:\n   std::shared_ptr<const BOOST_REGEX_DETAIL_NS::cpp_regex_traits_implementation<charT> > m_pimpl;\n   //\n   // catalog name handler:\n   //\n   static std::string& get_catalog_name_inst();\n\n#ifdef BOOST_HAS_THREADS\n   static std::mutex& get_mutex_inst();\n#endif\n};\n\n\ntemplate <class charT>\nstd::intmax_t cpp_regex_traits<charT>::toi(const charT*& first, const charT* last, int radix)const\n{\n   BOOST_REGEX_DETAIL_NS::parser_buf<charT>   sbuf;            // buffer for parsing numbers.\n   std::basic_istream<charT>      is(&sbuf);       // stream for parsing numbers.\n\n   // we do NOT want to parse any thousands separators inside the stream:\n   last = std::find(first, last, std::use_facet<std::numpunct<charT>>(is.getloc()).thousands_sep());\n\n   sbuf.pubsetbuf(const_cast<charT*>(static_cast<const charT*>(first)), static_cast<std::streamsize>(last-first));\n   is.clear();\n   if(std::abs(radix) == 16) is >> std::hex;\n   else if(std::abs(radix) == 8) is >> std::oct;\n   else is >> std::dec;\n   std::intmax_t val;\n   if(is >> val)\n   {\n      first = first + ((last - first) - sbuf.in_avail());\n      return val;\n   }\n   else\n      return -1;\n}\n\ntemplate <class charT>\nstd::string cpp_regex_traits<charT>::catalog_name(const std::string& name)\n{\n#ifdef BOOST_HAS_THREADS\n   std::lock_guard<std::mutex> lk(get_mutex_inst());\n#endif\n   std::string result(get_catalog_name_inst());\n   get_catalog_name_inst() = name;\n   return result;\n}\n\ntemplate <class charT>\nstd::string& cpp_regex_traits<charT>::get_catalog_name_inst()\n{\n   static std::string s_name;\n   return s_name;\n}\n\ntemplate <class charT>\nstd::string cpp_regex_traits<charT>::get_catalog_name()\n{\n#ifdef BOOST_HAS_THREADS\n   std::lock_guard<std::mutex> lk(get_mutex_inst());\n#endif\n   std::string result(get_catalog_name_inst());\n   return result;\n}\n\n#ifdef BOOST_HAS_THREADS\ntemplate <class charT>\nstd::mutex& cpp_regex_traits<charT>::get_mutex_inst()\n{\n   static std::mutex s_mutex;\n   return s_mutex;\n}\n#endif\n\nnamespace BOOST_REGEX_DETAIL_NS {\n\n   inline void cpp_regex_traits_char_layer<char>::init()\n   {\n      // we need to start by initialising our syntax map so we know which\n      // character is used for which purpose:\n      std::memset(m_char_map, 0, sizeof(m_char_map));\n#ifndef __IBMCPP__\n      std::messages<char>::catalog cat = static_cast<std::messages<char>::catalog>(-1);\n#else\n      std::messages<char>::catalog cat = reinterpret_cast<std::messages<char>::catalog>(-1);\n#endif\n      std::string cat_name(cpp_regex_traits<char>::get_catalog_name());\n      if ((!cat_name.empty()) && (m_pmessages != 0))\n      {\n         cat = this->m_pmessages->open(\n            cat_name,\n            this->m_locale);\n         if ((int)cat < 0)\n         {\n            std::string m(\"Unable to open message catalog: \");\n            std::runtime_error err(m + cat_name);\n            boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n         }\n      }\n      //\n      // if we have a valid catalog then load our messages:\n      //\n      if ((int)cat >= 0)\n      {\n#ifndef BOOST_NO_EXCEPTIONS\n         try {\n#endif\n            for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n            {\n               string_type mss = this->m_pmessages->get(cat, 0, i, get_default_syntax(i));\n               for (string_type::size_type j = 0; j < mss.size(); ++j)\n               {\n                  m_char_map[static_cast<unsigned char>(mss[j])] = i;\n               }\n            }\n            this->m_pmessages->close(cat);\n#ifndef BOOST_NO_EXCEPTIONS\n         }\n         catch (...)\n         {\n            this->m_pmessages->close(cat);\n            throw;\n         }\n#endif\n      }\n      else\n      {\n         for (regex_constants::syntax_type j = 1; j < regex_constants::syntax_max; ++j)\n         {\n            const char* ptr = get_default_syntax(j);\n            while (ptr && *ptr)\n            {\n               m_char_map[static_cast<unsigned char>(*ptr)] = j;\n               ++ptr;\n            }\n         }\n      }\n      //\n      // finish off by calculating our escape types:\n      //\n      unsigned char i = 'A';\n      do\n      {\n         if (m_char_map[i] == 0)\n         {\n            if (this->m_pctype->is(std::ctype_base::lower, i))\n               m_char_map[i] = regex_constants::escape_type_class;\n            else if (this->m_pctype->is(std::ctype_base::upper, i))\n               m_char_map[i] = regex_constants::escape_type_not_class;\n         }\n      } while (0xFF != i++);\n   }\n\n} // namespace detail\n\n\n} // boost\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/cregex.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the\n * Boost Software License, Version 1.0. (See accompanying file\n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         cregex.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares POSIX API functions\n  *                + boost::RegEx high level wrapper.\n  */\n\n#ifndef BOOST_RE_CREGEX_HPP_INCLUDED\n#define BOOST_RE_CREGEX_HPP_INCLUDED\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n#include <boost/regex/v5/match_flags.hpp>\n#include <boost/regex/v5/error_type.hpp>\n\n#ifndef BOOST_REGEX_STANDALONE\n#if !defined(BOOST_REGEX_NO_LIB) && !defined(BOOST_REGEX_SOURCE) && !defined(BOOST_ALL_NO_LIB) && defined(__cplusplus)\n#  define BOOST_LIB_NAME boost_regex\n#  if defined(BOOST_REGEX_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)\n#     define BOOST_DYN_LINK\n#  endif\n#  ifdef BOOST_REGEX_DIAG\n#     define BOOST_LIB_DIAGNOSTIC\n#  endif\n#  include <boost/config/auto_link.hpp>\n#endif\n#endif\n\n#ifdef __cplusplus\n#include <cstddef>\n#else\n#include <stddef.h>\n#endif\n\n/* include these defs only for POSIX compatablity */\n#ifdef __cplusplus\nnamespace boost{\nextern \"C\" {\n#endif\n\n#if defined(__cplusplus)\ntypedef std::ptrdiff_t regoff_t;\ntypedef std::size_t regsize_t;\n#else\ntypedef ptrdiff_t regoff_t;\ntypedef size_t regsize_t;\n#endif\n\ntypedef struct\n{\n   unsigned int re_magic;\n#ifdef __cplusplus\n   std::size_t  re_nsub;      /* number of parenthesized subexpressions */\n#else\n   size_t re_nsub; \n#endif\n   const char*  re_endp;       /* end pointer for REG_PEND */\n   void* guts;                /* none of your business :-) */\n   match_flag_type eflags;        /* none of your business :-) */\n} regex_tA;\n\n#ifndef BOOST_NO_WREGEX\ntypedef struct\n{\n   unsigned int re_magic;\n#ifdef __cplusplus\n   std::size_t  re_nsub;         /* number of parenthesized subexpressions */\n#else\n   size_t re_nsub;\n#endif\n   const wchar_t* re_endp;       /* end pointer for REG_PEND */\n   void* guts;                   /* none of your business :-) */\n   match_flag_type eflags;           /* none of your business :-) */\n} regex_tW;\n#endif\n\ntypedef struct\n{\n   regoff_t rm_so;      /* start of match */\n   regoff_t rm_eo;      /* end of match */\n} regmatch_t;\n\n/* regcomp() flags */\ntypedef enum{\n   REG_BASIC = 0000,\n   REG_EXTENDED = 0001,\n   REG_ICASE = 0002,\n   REG_NOSUB = 0004,\n   REG_NEWLINE = 0010,\n   REG_NOSPEC = 0020,\n   REG_PEND = 0040,\n   REG_DUMP = 0200,\n   REG_NOCOLLATE = 0400,\n   REG_ESCAPE_IN_LISTS = 01000,\n   REG_NEWLINE_ALT = 02000,\n   REG_PERLEX = 04000,\n\n   REG_PERL = REG_EXTENDED | REG_NOCOLLATE | REG_ESCAPE_IN_LISTS | REG_PERLEX,\n   REG_AWK = REG_EXTENDED | REG_ESCAPE_IN_LISTS,\n   REG_GREP = REG_BASIC | REG_NEWLINE_ALT,\n   REG_EGREP = REG_EXTENDED | REG_NEWLINE_ALT,\n\n   REG_ASSERT = 15,\n   REG_INVARG = 16,\n   REG_ATOI = 255,   /* convert name to number (!) */\n   REG_ITOA = 0400   /* convert number to name (!) */\n} reg_comp_flags;\n\n/* regexec() flags */\ntypedef enum{\n   REG_NOTBOL =    00001,\n   REG_NOTEOL =    00002,\n   REG_STARTEND =  00004\n} reg_exec_flags;\n\n/*\n * POSIX error codes:\n */\ntypedef unsigned reg_error_t;\ntypedef reg_error_t reg_errcode_t;  /* backwards compatibility */\n\nstatic const reg_error_t REG_NOERROR = 0;   /* Success.  */\nstatic const reg_error_t REG_NOMATCH = 1;   /* Didn't find a match (for regexec).  */\n\n  /* POSIX regcomp return error codes.  (In the order listed in the\n     standard.)  */\nstatic const reg_error_t REG_BADPAT = 2;    /* Invalid pattern.  */\nstatic const reg_error_t REG_ECOLLATE = 3;  /* Undefined collating element.  */\nstatic const reg_error_t REG_ECTYPE = 4;    /* Invalid character class name.  */\nstatic const reg_error_t REG_EESCAPE = 5;   /* Trailing backslash.  */\nstatic const reg_error_t REG_ESUBREG = 6;   /* Invalid back reference.  */\nstatic const reg_error_t REG_EBRACK = 7;    /* Unmatched left bracket.  */\nstatic const reg_error_t REG_EPAREN = 8;    /* Parenthesis imbalance.  */\nstatic const reg_error_t REG_EBRACE = 9;    /* Unmatched \\{.  */\nstatic const reg_error_t REG_BADBR = 10;    /* Invalid contents of \\{\\}.  */\nstatic const reg_error_t REG_ERANGE = 11;   /* Invalid range end.  */\nstatic const reg_error_t REG_ESPACE = 12;   /* Ran out of memory.  */\nstatic const reg_error_t REG_BADRPT = 13;   /* No preceding re for repetition op.  */\nstatic const reg_error_t REG_EEND = 14;     /* unexpected end of expression */\nstatic const reg_error_t REG_ESIZE = 15;    /* expression too big */\nstatic const reg_error_t REG_ERPAREN = 8;   /* = REG_EPAREN : unmatched right parenthesis */\nstatic const reg_error_t REG_EMPTY = 17;    /* empty expression */\nstatic const reg_error_t REG_E_MEMORY = 15; /* = REG_ESIZE : out of memory */\nstatic const reg_error_t REG_ECOMPLEXITY = 18; /* complexity too high */\nstatic const reg_error_t REG_ESTACK = 19;   /* out of stack space */\nstatic const reg_error_t REG_E_PERL = 20;   /* Perl (?...) error */\nstatic const reg_error_t REG_E_UNKNOWN = 21; /* unknown error */\nstatic const reg_error_t REG_ENOSYS = 21;   /* = REG_E_UNKNOWN : Reserved. */\n\nBOOST_REGEX_DECL int BOOST_REGEX_CCALL regcompA(regex_tA*, const char*, int);\nBOOST_REGEX_DECL regsize_t BOOST_REGEX_CCALL regerrorA(int, const regex_tA*, char*, regsize_t);\nBOOST_REGEX_DECL int BOOST_REGEX_CCALL regexecA(const regex_tA*, const char*, regsize_t, regmatch_t*, int);\nBOOST_REGEX_DECL void BOOST_REGEX_CCALL regfreeA(regex_tA*);\n\n#ifndef BOOST_NO_WREGEX\nBOOST_REGEX_DECL int BOOST_REGEX_CCALL regcompW(regex_tW*, const wchar_t*, int);\nBOOST_REGEX_DECL regsize_t BOOST_REGEX_CCALL regerrorW(int, const regex_tW*, wchar_t*, regsize_t);\nBOOST_REGEX_DECL int BOOST_REGEX_CCALL regexecW(const regex_tW*, const wchar_t*, regsize_t, regmatch_t*, int);\nBOOST_REGEX_DECL void BOOST_REGEX_CCALL regfreeW(regex_tW*);\n#endif\n\n#ifdef UNICODE\n#define regcomp regcompW\n#define regerror regerrorW\n#define regexec regexecW\n#define regfree regfreeW\n#define regex_t regex_tW\n#else\n#define regcomp regcompA\n#define regerror regerrorA\n#define regexec regexecA\n#define regfree regfreeA\n#define regex_t regex_tA\n#endif\n\n#ifdef __cplusplus\n} /* extern \"C\" */\n} /* namespace */\n#endif\n\n#endif /* include guard */\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/error_type.hpp",
    "content": "/*\n *\n * Copyright (c) 2003-2005\n * John Maddock\n *\n * Use, modification and distribution are subject to the\n * Boost Software License, Version 1.0. (See accompanying file\n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         error_type.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression error type enumerator.\n  */\n\n#ifndef BOOST_REGEX_ERROR_TYPE_HPP\n#define BOOST_REGEX_ERROR_TYPE_HPP\n\n#ifdef __cplusplus\nnamespace boost{\n#endif\n\n#ifdef __cplusplus\nnamespace regex_constants{\n\nenum error_type{\n\n   error_ok = 0,         /* not used */\n   error_no_match = 1,   /* not used */\n   error_bad_pattern = 2,\n   error_collate = 3,\n   error_ctype = 4,\n   error_escape = 5,\n   error_backref = 6,\n   error_brack = 7,\n   error_paren = 8,\n   error_brace = 9,\n   error_badbrace = 10,\n   error_range = 11,\n   error_space = 12,\n   error_badrepeat = 13,\n   error_end = 14,    /* not used */\n   error_size = 15,\n   error_right_paren = 16,  /* not used */\n   error_empty = 17,\n   error_complexity = 18,\n   error_stack = 19,\n   error_perl_extension = 20,\n   error_unknown = 21\n};\n\n}\n}\n#endif /* __cplusplus */\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/icu.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         icu.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Unicode regular expressions on top of the ICU Library.\n  */\n\n#ifndef BOOST_REGEX_ICU_V5_HPP\n#define BOOST_REGEX_ICU_V5_HPP\n\n#include <unicode/utypes.h>\n#include <unicode/uchar.h>\n#include <unicode/coll.h>\n#include <type_traits>\n#include <functional>\n#include <boost/regex.hpp>\n#include <boost/regex/v5/unicode_iterator.hpp>\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning (push)\n#pragma warning (disable: 4251)\n#endif\n\nnamespace boost{\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\n// \n// Implementation details:\n//\nclass icu_regex_traits_implementation\n{\n   typedef UChar32                      char_type;\n   typedef std::size_t                  size_type;\n   typedef std::vector<char_type>       string_type;\n   typedef U_NAMESPACE_QUALIFIER Locale locale_type;\n   typedef std::uint_least32_t          char_class_type;\npublic:\n   icu_regex_traits_implementation(const U_NAMESPACE_QUALIFIER Locale& l)\n      : m_locale(l)\n   {\n      UErrorCode success = U_ZERO_ERROR;\n      m_collator.reset(U_NAMESPACE_QUALIFIER Collator::createInstance(l, success));\n      if(U_SUCCESS(success) == 0)\n         init_error();\n      m_collator->setStrength(U_NAMESPACE_QUALIFIER Collator::IDENTICAL);\n      success = U_ZERO_ERROR;\n      m_primary_collator.reset(U_NAMESPACE_QUALIFIER Collator::createInstance(l, success));\n      if(U_SUCCESS(success) == 0)\n         init_error();\n      m_primary_collator->setStrength(U_NAMESPACE_QUALIFIER Collator::PRIMARY);\n   }\n   U_NAMESPACE_QUALIFIER Locale getloc()const\n   {\n      return m_locale;\n   }\n   string_type do_transform(const char_type* p1, const char_type* p2, const U_NAMESPACE_QUALIFIER Collator* pcoll) const\n   {\n      // TODO make thread safe!!!! :\n      typedef u32_to_u16_iterator<const char_type*, ::UChar> itt;\n      itt i(p1), j(p2);\n      std::vector< ::UChar> t(i, j);\n      std::uint8_t result[100];\n      std::int32_t len;\n      if (!t.empty())\n         len = pcoll->getSortKey(&*t.begin(), static_cast<std::int32_t>(t.size()), result, sizeof(result));\n      else\n         len = pcoll->getSortKey(static_cast<UChar const*>(0), static_cast<std::int32_t>(0), result, sizeof(result));\n      if (std::size_t(len) > sizeof(result))\n      {\n         std::unique_ptr< std::uint8_t[]> presult(new ::uint8_t[len + 1]);\n         if (!t.empty())\n            len = pcoll->getSortKey(&*t.begin(), static_cast<std::int32_t>(t.size()), presult.get(), len + 1);\n         else\n            len = pcoll->getSortKey(static_cast<UChar const*>(0), static_cast<std::int32_t>(0), presult.get(), len + 1);\n         if ((0 == presult[len - 1]) && (len > 1))\n            --len;\n         return string_type(presult.get(), presult.get() + len);\n      }\n      if ((0 == result[len - 1]) && (len > 1))\n         --len;\n      return string_type(result, result + len);\n   }\n   string_type transform(const char_type* p1, const char_type* p2) const\n   {\n      return do_transform(p1, p2, m_collator.get());\n   }\n   string_type transform_primary(const char_type* p1, const char_type* p2) const\n   {\n      return do_transform(p1, p2, m_primary_collator.get());\n   }\nprivate:\n   void init_error()\n   {\n      std::runtime_error e(\"Could not initialize ICU resources\");\n#ifndef BOOST_REGEX_STANDALONE\n      boost::throw_exception(e);\n#else\n      throw e;\n#endif\n   }\n   U_NAMESPACE_QUALIFIER Locale m_locale;                                  // The ICU locale that we're using\n   std::unique_ptr< U_NAMESPACE_QUALIFIER Collator> m_collator;          // The full collation object\n   std::unique_ptr< U_NAMESPACE_QUALIFIER Collator> m_primary_collator;  // The primary collation object\n};\ninline std::shared_ptr<icu_regex_traits_implementation> get_icu_regex_traits_implementation(const U_NAMESPACE_QUALIFIER Locale& loc)\n{\n   return std::shared_ptr<icu_regex_traits_implementation>(new icu_regex_traits_implementation(loc));\n}\n\n}\n\nclass icu_regex_traits\n{\npublic:\n   typedef UChar32                      char_type;\n   typedef std::size_t                  size_type;\n   typedef std::vector<char_type>       string_type;\n   typedef U_NAMESPACE_QUALIFIER Locale locale_type;\n   typedef std::uint64_t                char_class_type;\n\n   struct boost_extensions_tag{};\n\n   icu_regex_traits()\n      : m_pimpl(BOOST_REGEX_DETAIL_NS::get_icu_regex_traits_implementation(U_NAMESPACE_QUALIFIER Locale()))\n   {\n   }\n   static size_type length(const char_type* p)\n   {\n      size_type result = 0;\n      while (*p)\n      {\n         ++p;\n         ++result;\n      }\n      return result;\n   }\n\n   ::boost::regex_constants::syntax_type syntax_type(char_type c)const\n   {\n      return ((c < 0x7f) && (c > 0)) ? BOOST_REGEX_DETAIL_NS::get_default_syntax_type(static_cast<char>(c)) : regex_constants::syntax_char;\n   }\n   ::boost::regex_constants::escape_syntax_type escape_syntax_type(char_type c) const\n   {\n      return ((c < 0x7f) && (c > 0)) ? BOOST_REGEX_DETAIL_NS::get_default_escape_syntax_type(static_cast<char>(c)) : regex_constants::syntax_char;\n   }\n   char_type translate(char_type c) const\n   {\n      return c;\n   }\n   char_type translate_nocase(char_type c) const\n   {\n      return ::u_foldCase(c, U_FOLD_CASE_DEFAULT);\n   }\n   char_type translate(char_type c, bool icase) const\n   {\n      return icase ? translate_nocase(c) : translate(c);\n   }\n   char_type tolower(char_type c) const\n   {\n      return ::u_tolower(c);\n   }\n   char_type toupper(char_type c) const\n   {\n      return ::u_toupper(c);\n   }\n   string_type transform(const char_type* p1, const char_type* p2) const\n   {\n      return m_pimpl->transform(p1, p2);\n   }\n   string_type transform_primary(const char_type* p1, const char_type* p2) const\n   {\n      return m_pimpl->transform_primary(p1, p2);\n   }\n   char_class_type lookup_classname(const char_type* p1, const char_type* p2) const\n   {\n      constexpr char_class_type mask_blank = char_class_type(1) << offset_blank;\n      constexpr char_class_type mask_space = char_class_type(1) << offset_space;\n      constexpr char_class_type mask_xdigit = char_class_type(1) << offset_xdigit;\n      constexpr char_class_type mask_underscore = char_class_type(1) << offset_underscore;\n      constexpr char_class_type mask_unicode = char_class_type(1) << offset_unicode;\n      //constexpr char_class_type mask_any = char_class_type(1) << offset_any;\n      //constexpr char_class_type mask_ascii = char_class_type(1) << offset_ascii;\n      constexpr char_class_type mask_horizontal = char_class_type(1) << offset_horizontal;\n      constexpr char_class_type mask_vertical = char_class_type(1) << offset_vertical;\n\n      static const char_class_type masks[] =\n      {\n         0,\n         U_GC_L_MASK | U_GC_ND_MASK,\n         U_GC_L_MASK,\n         mask_blank,\n         U_GC_CC_MASK | U_GC_CF_MASK | U_GC_ZL_MASK | U_GC_ZP_MASK,\n         U_GC_ND_MASK,\n         U_GC_ND_MASK,\n         (0x3FFFFFFFu) & ~(U_GC_CC_MASK | U_GC_CF_MASK | U_GC_CS_MASK | U_GC_CN_MASK | U_GC_Z_MASK),\n         mask_horizontal,\n         U_GC_LL_MASK,\n         U_GC_LL_MASK,\n         ~(U_GC_C_MASK),\n         U_GC_P_MASK,\n         char_class_type(U_GC_Z_MASK) | mask_space,\n         char_class_type(U_GC_Z_MASK) | mask_space,\n         U_GC_LU_MASK,\n         mask_unicode,\n         U_GC_LU_MASK,\n         mask_vertical,\n         char_class_type(U_GC_L_MASK | U_GC_ND_MASK | U_GC_MN_MASK) | mask_underscore,\n         char_class_type(U_GC_L_MASK | U_GC_ND_MASK | U_GC_MN_MASK) | mask_underscore,\n         char_class_type(U_GC_ND_MASK) | mask_xdigit,\n      };\n\n      int idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2);\n      if (idx >= 0)\n         return masks[idx + 1];\n      char_class_type result = lookup_icu_mask(p1, p2);\n      if (result != 0)\n         return result;\n\n      if (idx < 0)\n      {\n         string_type s(p1, p2);\n         string_type::size_type i = 0;\n         while (i < s.size())\n         {\n            s[i] = static_cast<char>((::u_tolower)(s[i]));\n            if (::u_isspace(s[i]) || (s[i] == '-') || (s[i] == '_'))\n               s.erase(s.begin() + i, s.begin() + i + 1);\n            else\n            {\n               s[i] = static_cast<char>((::u_tolower)(s[i]));\n               ++i;\n            }\n         }\n         if (!s.empty())\n            idx = ::boost::BOOST_REGEX_DETAIL_NS::get_default_class_id(&*s.begin(), &*s.begin() + s.size());\n         if (idx >= 0)\n            return masks[idx + 1];\n         if (!s.empty())\n            result = lookup_icu_mask(&*s.begin(), &*s.begin() + s.size());\n         if (result != 0)\n            return result;\n      }\n      BOOST_REGEX_ASSERT(std::size_t(idx + 1) < sizeof(masks) / sizeof(masks[0]));\n      return masks[idx + 1];\n   }\n   string_type lookup_collatename(const char_type* p1, const char_type* p2) const\n   {\n      string_type result;\n      if (std::find_if(p1, p2, std::bind(std::greater< ::UChar32>(), std::placeholders::_1, 0x7f)) == p2)\n      {\n         std::string s(p1, p2);\n         // Try Unicode name:\n         UErrorCode err = U_ZERO_ERROR;\n         UChar32 c = ::u_charFromName(U_UNICODE_CHAR_NAME, s.c_str(), &err);\n         if (U_SUCCESS(err))\n         {\n            result.push_back(c);\n            return result;\n         }\n         // Try Unicode-extended name:\n         err = U_ZERO_ERROR;\n         c = ::u_charFromName(U_EXTENDED_CHAR_NAME, s.c_str(), &err);\n         if (U_SUCCESS(err))\n         {\n            result.push_back(c);\n            return result;\n         }\n         // try POSIX name:\n         s = ::boost::BOOST_REGEX_DETAIL_NS::lookup_default_collate_name(s);\n         result.assign(s.begin(), s.end());\n      }\n      if (result.empty() && (p2 - p1 == 1))\n         result.push_back(*p1);\n      return result;\n   }\n   bool isctype(char_type c, char_class_type f) const\n   {\n      constexpr char_class_type mask_blank = char_class_type(1) << offset_blank;\n      constexpr char_class_type mask_space = char_class_type(1) << offset_space;\n      constexpr char_class_type mask_xdigit = char_class_type(1) << offset_xdigit;\n      constexpr char_class_type mask_underscore = char_class_type(1) << offset_underscore;\n      constexpr char_class_type mask_unicode = char_class_type(1) << offset_unicode;\n      constexpr char_class_type mask_any = char_class_type(1) << offset_any;\n      constexpr char_class_type mask_ascii = char_class_type(1) << offset_ascii;\n      constexpr char_class_type mask_horizontal = char_class_type(1) << offset_horizontal;\n      constexpr char_class_type mask_vertical = char_class_type(1) << offset_vertical;\n\n      // check for standard catagories first:\n      char_class_type m = char_class_type(static_cast<char_class_type>(1) << u_charType(c));\n      if ((m & f) != 0)\n         return true;\n      // now check for special cases:\n      if (((f & mask_blank) != 0) && u_isblank(c))\n         return true;\n      if (((f & mask_space) != 0) && u_isspace(c))\n         return true;\n      if (((f & mask_xdigit) != 0) && (u_digit(c, 16) >= 0))\n         return true;\n      if (((f & mask_unicode) != 0) && (c >= 0x100))\n         return true;\n      if (((f & mask_underscore) != 0) && (c == '_'))\n         return true;\n      if (((f & mask_any) != 0) && (c <= 0x10FFFF))\n         return true;\n      if (((f & mask_ascii) != 0) && (c <= 0x7F))\n         return true;\n      if (((f & mask_vertical) != 0) && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == static_cast<char_type>('\\v')) || (m == U_GC_ZL_MASK) || (m == U_GC_ZP_MASK)))\n         return true;\n      if (((f & mask_horizontal) != 0) && !::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) && u_isspace(c) && (c != static_cast<char_type>('\\v')))\n         return true;\n      return false;\n   }\n   std::intmax_t toi(const char_type*& p1, const char_type* p2, int radix)const\n   {\n      return BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this);\n   }\n   int value(char_type c, int radix)const\n   {\n      return u_digit(c, static_cast< std::int8_t>(radix));\n   }\n   locale_type imbue(locale_type l)\n   {\n      locale_type result(m_pimpl->getloc());\n      m_pimpl = BOOST_REGEX_DETAIL_NS::get_icu_regex_traits_implementation(l);\n      return result;\n   }\n   locale_type getloc()const\n   {\n      return locale_type();\n   }\n   std::string error_string(::boost::regex_constants::error_type n) const\n   {\n      return BOOST_REGEX_DETAIL_NS::get_default_error_string(n);\n   }\nprivate:\n   icu_regex_traits(const icu_regex_traits&);\n   icu_regex_traits& operator=(const icu_regex_traits&);\n\n   //\n   // define the bitmasks offsets we need for additional character properties:\n   //\n   enum{\n      offset_blank = U_CHAR_CATEGORY_COUNT,\n      offset_space = U_CHAR_CATEGORY_COUNT+1,\n      offset_xdigit = U_CHAR_CATEGORY_COUNT+2,\n      offset_underscore = U_CHAR_CATEGORY_COUNT+3,\n      offset_unicode = U_CHAR_CATEGORY_COUNT+4,\n      offset_any = U_CHAR_CATEGORY_COUNT+5,\n      offset_ascii = U_CHAR_CATEGORY_COUNT+6,\n      offset_horizontal = U_CHAR_CATEGORY_COUNT+7,\n      offset_vertical = U_CHAR_CATEGORY_COUNT+8\n   };\n\n   static char_class_type lookup_icu_mask(const ::UChar32* p1, const ::UChar32* p2)\n   {\n      //constexpr char_class_type mask_blank = char_class_type(1) << offset_blank;\n      //constexpr char_class_type mask_space = char_class_type(1) << offset_space;\n      //constexpr char_class_type mask_xdigit = char_class_type(1) << offset_xdigit;\n      //constexpr char_class_type mask_underscore = char_class_type(1) << offset_underscore;\n      //constexpr char_class_type mask_unicode = char_class_type(1) << offset_unicode;\n      constexpr char_class_type mask_any = char_class_type(1) << offset_any;\n      constexpr char_class_type mask_ascii = char_class_type(1) << offset_ascii;\n      //constexpr char_class_type mask_horizontal = char_class_type(1) << offset_horizontal;\n      //constexpr char_class_type mask_vertical = char_class_type(1) << offset_vertical;\n\n      static const ::UChar32 prop_name_table[] = {\n         /* any */  'a', 'n', 'y',\n         /* ascii */  'a', 's', 'c', 'i', 'i',\n         /* assigned */  'a', 's', 's', 'i', 'g', 'n', 'e', 'd',\n         /* c* */  'c', '*',\n         /* cc */  'c', 'c',\n         /* cf */  'c', 'f',\n         /* closepunctuation */  'c', 'l', 'o', 's', 'e', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n         /* cn */  'c', 'n',\n         /* co */  'c', 'o',\n         /* connectorpunctuation */  'c', 'o', 'n', 'n', 'e', 'c', 't', 'o', 'r', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n         /* control */  'c', 'o', 'n', 't', 'r', 'o', 'l',\n         /* cs */  'c', 's',\n         /* currencysymbol */  'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 's', 'y', 'm', 'b', 'o', 'l',\n         /* dashpunctuation */  'd', 'a', 's', 'h', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n         /* decimaldigitnumber */  'd', 'e', 'c', 'i', 'm', 'a', 'l', 'd', 'i', 'g', 'i', 't', 'n', 'u', 'm', 'b', 'e', 'r',\n         /* enclosingmark */  'e', 'n', 'c', 'l', 'o', 's', 'i', 'n', 'g', 'm', 'a', 'r', 'k',\n         /* finalpunctuation */  'f', 'i', 'n', 'a', 'l', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n         /* format */  'f', 'o', 'r', 'm', 'a', 't',\n         /* initialpunctuation */  'i', 'n', 'i', 't', 'i', 'a', 'l', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n         /* l* */  'l', '*',\n         /* letter */  'l', 'e', 't', 't', 'e', 'r',\n         /* letternumber */  'l', 'e', 't', 't', 'e', 'r', 'n', 'u', 'm', 'b', 'e', 'r',\n         /* lineseparator */  'l', 'i', 'n', 'e', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r',\n         /* ll */  'l', 'l',\n         /* lm */  'l', 'm',\n         /* lo */  'l', 'o',\n         /* lowercaseletter */  'l', 'o', 'w', 'e', 'r', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r',\n         /* lt */  'l', 't',\n         /* lu */  'l', 'u',\n         /* m* */  'm', '*',\n         /* mark */  'm', 'a', 'r', 'k',\n         /* mathsymbol */  'm', 'a', 't', 'h', 's', 'y', 'm', 'b', 'o', 'l',\n         /* mc */  'm', 'c',\n         /* me */  'm', 'e',\n         /* mn */  'm', 'n',\n         /* modifierletter */  'm', 'o', 'd', 'i', 'f', 'i', 'e', 'r', 'l', 'e', 't', 't', 'e', 'r',\n         /* modifiersymbol */  'm', 'o', 'd', 'i', 'f', 'i', 'e', 'r', 's', 'y', 'm', 'b', 'o', 'l',\n         /* n* */  'n', '*',\n         /* nd */  'n', 'd',\n         /* nl */  'n', 'l',\n         /* no */  'n', 'o',\n         /* nonspacingmark */  'n', 'o', 'n', 's', 'p', 'a', 'c', 'i', 'n', 'g', 'm', 'a', 'r', 'k',\n         /* notassigned */  'n', 'o', 't', 'a', 's', 's', 'i', 'g', 'n', 'e', 'd',\n         /* number */  'n', 'u', 'm', 'b', 'e', 'r',\n         /* openpunctuation */  'o', 'p', 'e', 'n', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n         /* other */  'o', 't', 'h', 'e', 'r',\n         /* otherletter */  'o', 't', 'h', 'e', 'r', 'l', 'e', 't', 't', 'e', 'r',\n         /* othernumber */  'o', 't', 'h', 'e', 'r', 'n', 'u', 'm', 'b', 'e', 'r',\n         /* otherpunctuation */  'o', 't', 'h', 'e', 'r', 'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n         /* othersymbol */  'o', 't', 'h', 'e', 'r', 's', 'y', 'm', 'b', 'o', 'l',\n         /* p* */  'p', '*',\n         /* paragraphseparator */  'p', 'a', 'r', 'a', 'g', 'r', 'a', 'p', 'h', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r',\n         /* pc */  'p', 'c',\n         /* pd */  'p', 'd',\n         /* pe */  'p', 'e',\n         /* pf */  'p', 'f',\n         /* pi */  'p', 'i',\n         /* po */  'p', 'o',\n         /* privateuse */  'p', 'r', 'i', 'v', 'a', 't', 'e', 'u', 's', 'e',\n         /* ps */  'p', 's',\n         /* punctuation */  'p', 'u', 'n', 'c', 't', 'u', 'a', 't', 'i', 'o', 'n',\n         /* s* */  's', '*',\n         /* sc */  's', 'c',\n         /* separator */  's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r',\n         /* sk */  's', 'k',\n         /* sm */  's', 'm',\n         /* so */  's', 'o',\n         /* spaceseparator */  's', 'p', 'a', 'c', 'e', 's', 'e', 'p', 'a', 'r', 'a', 't', 'o', 'r',\n         /* spacingcombiningmark */  's', 'p', 'a', 'c', 'i', 'n', 'g', 'c', 'o', 'm', 'b', 'i', 'n', 'i', 'n', 'g', 'm', 'a', 'r', 'k',\n         /* surrogate */  's', 'u', 'r', 'r', 'o', 'g', 'a', 't', 'e',\n         /* symbol */  's', 'y', 'm', 'b', 'o', 'l',\n         /* titlecase */  't', 'i', 't', 'l', 'e', 'c', 'a', 's', 'e',\n         /* titlecaseletter */  't', 'i', 't', 'l', 'e', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r',\n         /* uppercaseletter */  'u', 'p', 'p', 'e', 'r', 'c', 'a', 's', 'e', 'l', 'e', 't', 't', 'e', 'r',\n         /* z* */  'z', '*',\n         /* zl */  'z', 'l',\n         /* zp */  'z', 'p',\n         /* zs */  'z', 's',\n      };\n\n      static const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32> range_data[] = {\n         { prop_name_table + 0, prop_name_table + 3, }, // any\n         { prop_name_table + 3, prop_name_table + 8, }, // ascii\n         { prop_name_table + 8, prop_name_table + 16, }, // assigned\n         { prop_name_table + 16, prop_name_table + 18, }, // c*\n         { prop_name_table + 18, prop_name_table + 20, }, // cc\n         { prop_name_table + 20, prop_name_table + 22, }, // cf\n         { prop_name_table + 22, prop_name_table + 38, }, // closepunctuation\n         { prop_name_table + 38, prop_name_table + 40, }, // cn\n         { prop_name_table + 40, prop_name_table + 42, }, // co\n         { prop_name_table + 42, prop_name_table + 62, }, // connectorpunctuation\n         { prop_name_table + 62, prop_name_table + 69, }, // control\n         { prop_name_table + 69, prop_name_table + 71, }, // cs\n         { prop_name_table + 71, prop_name_table + 85, }, // currencysymbol\n         { prop_name_table + 85, prop_name_table + 100, }, // dashpunctuation\n         { prop_name_table + 100, prop_name_table + 118, }, // decimaldigitnumber\n         { prop_name_table + 118, prop_name_table + 131, }, // enclosingmark\n         { prop_name_table + 131, prop_name_table + 147, }, // finalpunctuation\n         { prop_name_table + 147, prop_name_table + 153, }, // format\n         { prop_name_table + 153, prop_name_table + 171, }, // initialpunctuation\n         { prop_name_table + 171, prop_name_table + 173, }, // l*\n         { prop_name_table + 173, prop_name_table + 179, }, // letter\n         { prop_name_table + 179, prop_name_table + 191, }, // letternumber\n         { prop_name_table + 191, prop_name_table + 204, }, // lineseparator\n         { prop_name_table + 204, prop_name_table + 206, }, // ll\n         { prop_name_table + 206, prop_name_table + 208, }, // lm\n         { prop_name_table + 208, prop_name_table + 210, }, // lo\n         { prop_name_table + 210, prop_name_table + 225, }, // lowercaseletter\n         { prop_name_table + 225, prop_name_table + 227, }, // lt\n         { prop_name_table + 227, prop_name_table + 229, }, // lu\n         { prop_name_table + 229, prop_name_table + 231, }, // m*\n         { prop_name_table + 231, prop_name_table + 235, }, // mark\n         { prop_name_table + 235, prop_name_table + 245, }, // mathsymbol\n         { prop_name_table + 245, prop_name_table + 247, }, // mc\n         { prop_name_table + 247, prop_name_table + 249, }, // me\n         { prop_name_table + 249, prop_name_table + 251, }, // mn\n         { prop_name_table + 251, prop_name_table + 265, }, // modifierletter\n         { prop_name_table + 265, prop_name_table + 279, }, // modifiersymbol\n         { prop_name_table + 279, prop_name_table + 281, }, // n*\n         { prop_name_table + 281, prop_name_table + 283, }, // nd\n         { prop_name_table + 283, prop_name_table + 285, }, // nl\n         { prop_name_table + 285, prop_name_table + 287, }, // no\n         { prop_name_table + 287, prop_name_table + 301, }, // nonspacingmark\n         { prop_name_table + 301, prop_name_table + 312, }, // notassigned\n         { prop_name_table + 312, prop_name_table + 318, }, // number\n         { prop_name_table + 318, prop_name_table + 333, }, // openpunctuation\n         { prop_name_table + 333, prop_name_table + 338, }, // other\n         { prop_name_table + 338, prop_name_table + 349, }, // otherletter\n         { prop_name_table + 349, prop_name_table + 360, }, // othernumber\n         { prop_name_table + 360, prop_name_table + 376, }, // otherpunctuation\n         { prop_name_table + 376, prop_name_table + 387, }, // othersymbol\n         { prop_name_table + 387, prop_name_table + 389, }, // p*\n         { prop_name_table + 389, prop_name_table + 407, }, // paragraphseparator\n         { prop_name_table + 407, prop_name_table + 409, }, // pc\n         { prop_name_table + 409, prop_name_table + 411, }, // pd\n         { prop_name_table + 411, prop_name_table + 413, }, // pe\n         { prop_name_table + 413, prop_name_table + 415, }, // pf\n         { prop_name_table + 415, prop_name_table + 417, }, // pi\n         { prop_name_table + 417, prop_name_table + 419, }, // po\n         { prop_name_table + 419, prop_name_table + 429, }, // privateuse\n         { prop_name_table + 429, prop_name_table + 431, }, // ps\n         { prop_name_table + 431, prop_name_table + 442, }, // punctuation\n         { prop_name_table + 442, prop_name_table + 444, }, // s*\n         { prop_name_table + 444, prop_name_table + 446, }, // sc\n         { prop_name_table + 446, prop_name_table + 455, }, // separator\n         { prop_name_table + 455, prop_name_table + 457, }, // sk\n         { prop_name_table + 457, prop_name_table + 459, }, // sm\n         { prop_name_table + 459, prop_name_table + 461, }, // so\n         { prop_name_table + 461, prop_name_table + 475, }, // spaceseparator\n         { prop_name_table + 475, prop_name_table + 495, }, // spacingcombiningmark\n         { prop_name_table + 495, prop_name_table + 504, }, // surrogate\n         { prop_name_table + 504, prop_name_table + 510, }, // symbol\n         { prop_name_table + 510, prop_name_table + 519, }, // titlecase\n         { prop_name_table + 519, prop_name_table + 534, }, // titlecaseletter\n         { prop_name_table + 534, prop_name_table + 549, }, // uppercaseletter\n         { prop_name_table + 549, prop_name_table + 551, }, // z*\n         { prop_name_table + 551, prop_name_table + 553, }, // zl\n         { prop_name_table + 553, prop_name_table + 555, }, // zp\n         { prop_name_table + 555, prop_name_table + 557, }, // zs\n      };\n\n      static const icu_regex_traits::char_class_type icu_class_map[] = {\n         mask_any, // any\n         mask_ascii, // ascii\n         (0x3FFFFFFFu) & ~(U_GC_CN_MASK), // assigned\n         U_GC_C_MASK, // c*\n         U_GC_CC_MASK, // cc\n         U_GC_CF_MASK, // cf\n         U_GC_PE_MASK, // closepunctuation\n         U_GC_CN_MASK, // cn\n         U_GC_CO_MASK, // co\n         U_GC_PC_MASK, // connectorpunctuation\n         U_GC_CC_MASK, // control\n         U_GC_CS_MASK, // cs\n         U_GC_SC_MASK, // currencysymbol\n         U_GC_PD_MASK, // dashpunctuation\n         U_GC_ND_MASK, // decimaldigitnumber\n         U_GC_ME_MASK, // enclosingmark\n         U_GC_PF_MASK, // finalpunctuation\n         U_GC_CF_MASK, // format\n         U_GC_PI_MASK, // initialpunctuation\n         U_GC_L_MASK, // l*\n         U_GC_L_MASK, // letter\n         U_GC_NL_MASK, // letternumber\n         U_GC_ZL_MASK, // lineseparator\n         U_GC_LL_MASK, // ll\n         U_GC_LM_MASK, // lm\n         U_GC_LO_MASK, // lo\n         U_GC_LL_MASK, // lowercaseletter\n         U_GC_LT_MASK, // lt\n         U_GC_LU_MASK, // lu\n         U_GC_M_MASK, // m*\n         U_GC_M_MASK, // mark\n         U_GC_SM_MASK, // mathsymbol\n         U_GC_MC_MASK, // mc\n         U_GC_ME_MASK, // me\n         U_GC_MN_MASK, // mn\n         U_GC_LM_MASK, // modifierletter\n         U_GC_SK_MASK, // modifiersymbol\n         U_GC_N_MASK, // n*\n         U_GC_ND_MASK, // nd\n         U_GC_NL_MASK, // nl\n         U_GC_NO_MASK, // no\n         U_GC_MN_MASK, // nonspacingmark\n         U_GC_CN_MASK, // notassigned\n         U_GC_N_MASK, // number\n         U_GC_PS_MASK, // openpunctuation\n         U_GC_C_MASK, // other\n         U_GC_LO_MASK, // otherletter\n         U_GC_NO_MASK, // othernumber\n         U_GC_PO_MASK, // otherpunctuation\n         U_GC_SO_MASK, // othersymbol\n         U_GC_P_MASK, // p*\n         U_GC_ZP_MASK, // paragraphseparator\n         U_GC_PC_MASK, // pc\n         U_GC_PD_MASK, // pd\n         U_GC_PE_MASK, // pe\n         U_GC_PF_MASK, // pf\n         U_GC_PI_MASK, // pi\n         U_GC_PO_MASK, // po\n         U_GC_CO_MASK, // privateuse\n         U_GC_PS_MASK, // ps\n         U_GC_P_MASK, // punctuation\n         U_GC_S_MASK, // s*\n         U_GC_SC_MASK, // sc\n         U_GC_Z_MASK, // separator\n         U_GC_SK_MASK, // sk\n         U_GC_SM_MASK, // sm\n         U_GC_SO_MASK, // so\n         U_GC_ZS_MASK, // spaceseparator\n         U_GC_MC_MASK, // spacingcombiningmark\n         U_GC_CS_MASK, // surrogate\n         U_GC_S_MASK, // symbol\n         U_GC_LT_MASK, // titlecase\n         U_GC_LT_MASK, // titlecaseletter\n         U_GC_LU_MASK, // uppercaseletter\n         U_GC_Z_MASK, // z*\n         U_GC_ZL_MASK, // zl\n         U_GC_ZP_MASK, // zp\n         U_GC_ZS_MASK, // zs\n      };\n\n\n      const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* ranges_begin = range_data;\n      const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* ranges_end = range_data + (sizeof(range_data) / sizeof(range_data[0]));\n\n      BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32> t = { p1, p2, };\n      const BOOST_REGEX_DETAIL_NS::character_pointer_range< ::UChar32>* p = std::lower_bound(ranges_begin, ranges_end, t);\n      if ((p != ranges_end) && (t == *p))\n         return icu_class_map[p - ranges_begin];\n      return 0;\n   }\n   std::shared_ptr< ::boost::BOOST_REGEX_DETAIL_NS::icu_regex_traits_implementation> m_pimpl;\n};\n\n} // namespace boost\n\nnamespace boost{\n\n// types:\ntypedef basic_regex< ::UChar32, icu_regex_traits> u32regex;\ntypedef match_results<const ::UChar32*> u32match;\ntypedef match_results<const ::UChar*> u16match;\n\n//\n// Construction of 32-bit regex types from UTF-8 and UTF-16 primitives:\n//\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class InputIterator>\ninline u32regex do_make_u32regex(InputIterator i, \n                              InputIterator j, \n                              boost::regex_constants::syntax_option_type opt, \n                              const std::integral_constant<int, 1>*)\n{\n   typedef boost::u8_to_u32_iterator<InputIterator, UChar32> conv_type;\n   return u32regex(conv_type(i, i, j), conv_type(j, i, j), opt);\n}\n\ntemplate <class InputIterator>\ninline u32regex do_make_u32regex(InputIterator i, \n                              InputIterator j, \n                              boost::regex_constants::syntax_option_type opt, \n                              const std::integral_constant<int, 2>*)\n{\n   typedef boost::u16_to_u32_iterator<InputIterator, UChar32> conv_type;\n   return u32regex(conv_type(i, i, j), conv_type(j, i, j), opt);\n}\n\ntemplate <class InputIterator>\ninline u32regex do_make_u32regex(InputIterator i, \n                              InputIterator j, \n                              boost::regex_constants::syntax_option_type opt, \n                              const std::integral_constant<int, 4>*)\n{\n   return u32regex(i, j, opt);\n}\n}\n\n// BOOST_REGEX_UCHAR_IS_WCHAR_T\n//\n// Source inspection of unicode/umachine.h in ICU version 59 indicates that:\n//\n// On version 59, UChar is always char16_t in C++ mode (and uint16_t in C mode)\n//\n// On earlier versions, the logic is\n//\n// #if U_SIZEOF_WCHAR_T==2\n//   typedef wchar_t OldUChar;\n// #elif defined(__CHAR16_TYPE__)\n//   typedef __CHAR16_TYPE__ OldUChar;\n// #else\n//   typedef uint16_t OldUChar;\n// #endif\n//\n// That is, UChar is wchar_t only on versions below 59, when U_SIZEOF_WCHAR_T==2\n//\n// Hence,\n\n#define BOOST_REGEX_UCHAR_IS_WCHAR_T (U_ICU_VERSION_MAJOR_NUM < 59 && U_SIZEOF_WCHAR_T == 2)\n\n#if BOOST_REGEX_UCHAR_IS_WCHAR_T\n  static_assert((std::is_same<UChar, wchar_t>::value), \"Configuration logic has failed!\");\n#else\n  static_assert(!(std::is_same<UChar, wchar_t>::value), \"Configuration logic has failed!\");\n#endif\n\n//\n// Construction from an iterator pair:\n//\ntemplate <class InputIterator>\ninline u32regex make_u32regex(InputIterator i, \n                              InputIterator j, \n                              boost::regex_constants::syntax_option_type opt)\n{\n   return BOOST_REGEX_DETAIL_NS::do_make_u32regex(i, j, opt, static_cast<std::integral_constant<int, sizeof(*i)> const*>(0));\n}\n//\n// construction from UTF-8 nul-terminated strings:\n//\ninline u32regex make_u32regex(const char* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n{\n   return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::strlen(p), opt, static_cast<std::integral_constant<int, 1> const*>(0));\n}\ninline u32regex make_u32regex(const unsigned char* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n{\n   return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::strlen(reinterpret_cast<const char*>(p)), opt, static_cast<std::integral_constant<int, 1> const*>(0));\n}\n//\n// construction from UTF-16 nul-terminated strings:\n//\n#ifndef BOOST_NO_WREGEX\ninline u32regex make_u32regex(const wchar_t* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n{\n   return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + std::wcslen(p), opt, static_cast<std::integral_constant<int, sizeof(wchar_t)> const*>(0));\n}\n#endif\n#if !BOOST_REGEX_UCHAR_IS_WCHAR_T\ninline u32regex make_u32regex(const UChar* p, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n{\n   return BOOST_REGEX_DETAIL_NS::do_make_u32regex(p, p + u_strlen(p), opt, static_cast<std::integral_constant<int, 2> const*>(0));\n}\n#endif\n//\n// construction from basic_string class-template:\n//\ntemplate<class C, class T, class A>\ninline u32regex make_u32regex(const std::basic_string<C, T, A>& s, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n{\n   return BOOST_REGEX_DETAIL_NS::do_make_u32regex(s.begin(), s.end(), opt, static_cast<std::integral_constant<int, sizeof(C)> const*>(0));\n}\n//\n// Construction from ICU string type:\n//\ninline u32regex make_u32regex(const U_NAMESPACE_QUALIFIER UnicodeString& s, boost::regex_constants::syntax_option_type opt = boost::regex_constants::perl)\n{\n   return BOOST_REGEX_DETAIL_NS::do_make_u32regex(s.getBuffer(), s.getBuffer() + s.length(), opt, static_cast<std::integral_constant<int, 2> const*>(0));\n}\n\n//\n// regex_match overloads that widen the character type as appropriate:\n//\nnamespace BOOST_REGEX_DETAIL_NS{\ntemplate<class MR1, class MR2, class NSubs>\nvoid copy_results(MR1& out, MR2 const& in, NSubs named_subs)\n{\n   // copy results from an adapted MR2 match_results:\n   out.set_size(in.size(), in.prefix().first.base(), in.suffix().second.base());\n   out.set_base(in.base().base());\n   out.set_named_subs(named_subs);\n   for(int i = 0; i < (int)in.size(); ++i)\n   {\n      if(in[i].matched || !i)\n      {\n         out.set_first(in[i].first.base(), i);\n         out.set_second(in[i].second.base(), i, in[i].matched);\n      }\n   }\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   // Copy full capture info as well:\n   for(int i = 0; i < (int)in.size(); ++i)\n   {\n      if(in[i].captures().size())\n      {\n         out[i].get_captures().assign(in[i].captures().size(), typename MR1::value_type());\n         for(int j = 0; j < (int)out[i].captures().size(); ++j)\n         {\n            out[i].get_captures()[j].first = in[i].captures()[j].first.base();\n            out[i].get_captures()[j].second = in[i].captures()[j].second.base();\n            out[i].get_captures()[j].matched = in[i].captures()[j].matched;\n         }\n      }\n   }\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator>\ninline bool do_regex_match(BidiIterator first, BidiIterator last, \n                 match_results<BidiIterator, Allocator>& m, \n                 const u32regex& e, \n                 match_flag_type flags,\n                 std::integral_constant<int, 4> const*)\n{\n   return ::boost::regex_match(first, last, m, e, flags);\n}\ntemplate <class BidiIterator, class Allocator>\nbool do_regex_match(BidiIterator first, BidiIterator last, \n                 match_results<BidiIterator, Allocator>& m, \n                 const u32regex& e, \n                 match_flag_type flags,\n                 std::integral_constant<int, 2> const*)\n{\n   typedef u16_to_u32_iterator<BidiIterator, UChar32> conv_type;\n   typedef match_results<conv_type>                   match_type;\n   //typedef typename match_type::allocator_type        alloc_type;\n   match_type what;\n   bool result = ::boost::regex_match(conv_type(first, first, last), conv_type(last, first, last), what, e, flags);\n   // copy results across to m:\n   if(result) copy_results(m, what, e.get_named_subs());\n   return result;\n}\ntemplate <class BidiIterator, class Allocator>\nbool do_regex_match(BidiIterator first, BidiIterator last, \n                 match_results<BidiIterator, Allocator>& m, \n                 const u32regex& e, \n                 match_flag_type flags,\n                 std::integral_constant<int, 1> const*)\n{\n   typedef u8_to_u32_iterator<BidiIterator, UChar32>  conv_type;\n   typedef match_results<conv_type>                   match_type;\n   //typedef typename match_type::allocator_type        alloc_type;\n   match_type what;\n   bool result = ::boost::regex_match(conv_type(first, first, last), conv_type(last, first, last), what, e, flags);\n   // copy results across to m:\n   if(result) copy_results(m, what, e.get_named_subs());\n   return result;\n}\n} // namespace BOOST_REGEX_DETAIL_NS\n\ntemplate <class BidiIterator, class Allocator>\ninline bool u32regex_match(BidiIterator first, BidiIterator last, \n                 match_results<BidiIterator, Allocator>& m, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(first, last, m, e, flags, static_cast<std::integral_constant<int, sizeof(*first)> const*>(0));\n}\ninline bool u32regex_match(const UChar* p, \n                 match_results<const UChar*>& m, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+u_strlen(p), m, e, flags, static_cast<std::integral_constant<int, 2> const*>(0));\n}\n#if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX)\ninline bool u32regex_match(const wchar_t* p, \n                 match_results<const wchar_t*>& m, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::wcslen(p), m, e, flags, static_cast<std::integral_constant<int, sizeof(wchar_t)> const*>(0));\n}\n#endif\ninline bool u32regex_match(const char* p, \n                 match_results<const char*>& m, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::strlen(p), m, e, flags, static_cast<std::integral_constant<int, 1> const*>(0));\n}\ninline bool u32regex_match(const unsigned char* p, \n                 match_results<const unsigned char*>& m, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::strlen((const char*)p), m, e, flags, static_cast<std::integral_constant<int, 1> const*>(0));\n}\ninline bool u32regex_match(const std::string& s, \n                        match_results<std::string::const_iterator>& m, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast<std::integral_constant<int, 1> const*>(0));\n}\n#ifndef BOOST_NO_STD_WSTRING\ninline bool u32regex_match(const std::wstring& s, \n                        match_results<std::wstring::const_iterator>& m, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast<std::integral_constant<int, sizeof(wchar_t)> const*>(0));\n}\n#endif\ninline bool u32regex_match(const U_NAMESPACE_QUALIFIER UnicodeString& s, \n                        match_results<const UChar*>& m, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, static_cast<std::integral_constant<int, 2> const*>(0));\n}\n//\n// regex_match overloads that do not return what matched:\n//\ntemplate <class BidiIterator>\ninline bool u32regex_match(BidiIterator first, BidiIterator last, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<BidiIterator> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(first, last, m, e, flags, static_cast<std::integral_constant<int, sizeof(*first)> const*>(0));\n}\ninline bool u32regex_match(const UChar* p, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<const UChar*> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+u_strlen(p), m, e, flags, static_cast<std::integral_constant<int, 2> const*>(0));\n}\n#if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX)\ninline bool u32regex_match(const wchar_t* p, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<const wchar_t*> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::wcslen(p), m, e, flags, static_cast<std::integral_constant<int, sizeof(wchar_t)> const*>(0));\n}\n#endif\ninline bool u32regex_match(const char* p, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<const char*> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::strlen(p), m, e, flags, static_cast<std::integral_constant<int, 1> const*>(0));\n}\ninline bool u32regex_match(const unsigned char* p, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<const unsigned char*> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(p, p+std::strlen((const char*)p), m, e, flags, static_cast<std::integral_constant<int, 1> const*>(0));\n}\ninline bool u32regex_match(const std::string& s, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::string::const_iterator> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast<std::integral_constant<int, 1> const*>(0));\n}\n#ifndef BOOST_NO_STD_WSTRING\ninline bool u32regex_match(const std::wstring& s, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::wstring::const_iterator> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(s.begin(), s.end(), m, e, flags, static_cast<std::integral_constant<int, sizeof(wchar_t)> const*>(0));\n}\n#endif\ninline bool u32regex_match(const U_NAMESPACE_QUALIFIER UnicodeString& s, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const UChar*> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_match(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, static_cast<std::integral_constant<int, 2> const*>(0));\n}\n\n//\n// regex_search overloads that widen the character type as appropriate:\n//\nnamespace BOOST_REGEX_DETAIL_NS{\ntemplate <class BidiIterator, class Allocator>\ninline bool do_regex_search(BidiIterator first, BidiIterator last, \n                 match_results<BidiIterator, Allocator>& m, \n                 const u32regex& e, \n                 match_flag_type flags,\n                 BidiIterator base,\n                 std::integral_constant<int, 4> const*)\n{\n   return ::boost::regex_search(first, last, m, e, flags, base);\n}\ntemplate <class BidiIterator, class Allocator>\nbool do_regex_search(BidiIterator first, BidiIterator last, \n                 match_results<BidiIterator, Allocator>& m, \n                 const u32regex& e, \n                 match_flag_type flags,\n                 BidiIterator base,\n                 std::integral_constant<int, 2> const*)\n{\n   typedef u16_to_u32_iterator<BidiIterator, UChar32> conv_type;\n   typedef match_results<conv_type>                   match_type;\n   //typedef typename match_type::allocator_type        alloc_type;\n   match_type what;\n   bool result = ::boost::regex_search(conv_type(first, first, last), conv_type(last, first, last), what, e, flags, conv_type(base));\n   // copy results across to m:\n   if(result) copy_results(m, what, e.get_named_subs());\n   return result;\n}\ntemplate <class BidiIterator, class Allocator>\nbool do_regex_search(BidiIterator first, BidiIterator last, \n                 match_results<BidiIterator, Allocator>& m, \n                 const u32regex& e, \n                 match_flag_type flags,\n                 BidiIterator base,\n                 std::integral_constant<int, 1> const*)\n{\n   typedef u8_to_u32_iterator<BidiIterator, UChar32>  conv_type;\n   typedef match_results<conv_type>                   match_type;\n   //typedef typename match_type::allocator_type        alloc_type;\n   match_type what;\n   bool result = ::boost::regex_search(conv_type(first, first, last), conv_type(last, first, last), what, e, flags, conv_type(base));\n   // copy results across to m:\n   if(result) copy_results(m, what, e.get_named_subs());\n   return result;\n}\n}\n\ntemplate <class BidiIterator, class Allocator>\ninline bool u32regex_search(BidiIterator first, BidiIterator last, \n                 match_results<BidiIterator, Allocator>& m, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, first, static_cast<std::integral_constant<int, sizeof(*first)> const*>(0));\n}\ntemplate <class BidiIterator, class Allocator>\ninline bool u32regex_search(BidiIterator first, BidiIterator last, \n                 match_results<BidiIterator, Allocator>& m, \n                 const u32regex& e, \n                 match_flag_type flags,\n                 BidiIterator base)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, base, static_cast<std::integral_constant<int, sizeof(*first)> const*>(0));\n}\ninline bool u32regex_search(const UChar* p, \n                 match_results<const UChar*>& m, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+u_strlen(p), m, e, flags, p, static_cast<std::integral_constant<int, 2> const*>(0));\n}\n#if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX)\ninline bool u32regex_search(const wchar_t* p, \n                 match_results<const wchar_t*>& m, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::wcslen(p), m, e, flags, p, static_cast<std::integral_constant<int, sizeof(wchar_t)> const*>(0));\n}\n#endif\ninline bool u32regex_search(const char* p, \n                 match_results<const char*>& m, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::strlen(p), m, e, flags, p, static_cast<std::integral_constant<int, 1> const*>(0));\n}\ninline bool u32regex_search(const unsigned char* p, \n                 match_results<const unsigned char*>& m, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::strlen((const char*)p), m, e, flags, p, static_cast<std::integral_constant<int, 1> const*>(0));\n}\ninline bool u32regex_search(const std::string& s, \n                        match_results<std::string::const_iterator>& m, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast<std::integral_constant<int, 1> const*>(0));\n}\n#ifndef BOOST_NO_STD_WSTRING\ninline bool u32regex_search(const std::wstring& s, \n                        match_results<std::wstring::const_iterator>& m, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast<std::integral_constant<int, sizeof(wchar_t)> const*>(0));\n}\n#endif\ninline bool u32regex_search(const U_NAMESPACE_QUALIFIER UnicodeString& s, \n                        match_results<const UChar*>& m, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, s.getBuffer(), static_cast<std::integral_constant<int, 2> const*>(0));\n}\ntemplate <class BidiIterator>\ninline bool u32regex_search(BidiIterator first, BidiIterator last, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<BidiIterator> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(first, last, m, e, flags, first, static_cast<std::integral_constant<int, sizeof(*first)> const*>(0));\n}\ninline bool u32regex_search(const UChar* p, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<const UChar*> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+u_strlen(p), m, e, flags, p, static_cast<std::integral_constant<int, 2> const*>(0));\n}\n#if !BOOST_REGEX_UCHAR_IS_WCHAR_T && !defined(BOOST_NO_WREGEX)\ninline bool u32regex_search(const wchar_t* p, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<const wchar_t*> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::wcslen(p), m, e, flags, p, static_cast<std::integral_constant<int, sizeof(wchar_t)> const*>(0));\n}\n#endif\ninline bool u32regex_search(const char* p, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<const char*> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::strlen(p), m, e, flags, p, static_cast<std::integral_constant<int, 1> const*>(0));\n}\ninline bool u32regex_search(const unsigned char* p, \n                 const u32regex& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<const unsigned char*> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(p, p+std::strlen((const char*)p), m, e, flags, p, static_cast<std::integral_constant<int, 1> const*>(0));\n}\ninline bool u32regex_search(const std::string& s, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::string::const_iterator> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast<std::integral_constant<int, 1> const*>(0));\n}\n#ifndef BOOST_NO_STD_WSTRING\ninline bool u32regex_search(const std::wstring& s, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<std::wstring::const_iterator> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(s.begin(), s.end(), m, e, flags, s.begin(), static_cast<std::integral_constant<int, sizeof(wchar_t)> const*>(0));\n}\n#endif\ninline bool u32regex_search(const U_NAMESPACE_QUALIFIER UnicodeString& s, \n                        const u32regex& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const UChar*> m;\n   return BOOST_REGEX_DETAIL_NS::do_regex_search(s.getBuffer(), s.getBuffer() + s.length(), m, e, flags, s.getBuffer(), static_cast<std::integral_constant<int, 2> const*>(0));\n}\n\n//\n// overloads for regex_replace with utf-8 and utf-16 data types:\n//\nnamespace BOOST_REGEX_DETAIL_NS{\ntemplate <class I>\ninline std::pair< boost::u8_to_u32_iterator<I>, boost::u8_to_u32_iterator<I> >\n   make_utf32_seq(I i, I j, std::integral_constant<int, 1> const*)\n{\n   return std::pair< boost::u8_to_u32_iterator<I>, boost::u8_to_u32_iterator<I> >(boost::u8_to_u32_iterator<I>(i, i, j), boost::u8_to_u32_iterator<I>(j, i, j));\n}\ntemplate <class I>\ninline std::pair< boost::u16_to_u32_iterator<I>, boost::u16_to_u32_iterator<I> >\n   make_utf32_seq(I i, I j, std::integral_constant<int, 2> const*)\n{\n   return std::pair< boost::u16_to_u32_iterator<I>, boost::u16_to_u32_iterator<I> >(boost::u16_to_u32_iterator<I>(i, i, j), boost::u16_to_u32_iterator<I>(j, i, j));\n}\ntemplate <class I>\ninline std::pair< I, I >\n   make_utf32_seq(I i, I j, std::integral_constant<int, 4> const*)\n{\n   return std::pair< I, I >(i, j);\n}\ntemplate <class charT>\ninline std::pair< boost::u8_to_u32_iterator<const charT*>, boost::u8_to_u32_iterator<const charT*> >\n   make_utf32_seq(const charT* p, std::integral_constant<int, 1> const*)\n{\n   std::size_t len = std::strlen((const char*)p);\n   return std::pair< boost::u8_to_u32_iterator<const charT*>, boost::u8_to_u32_iterator<const charT*> >(boost::u8_to_u32_iterator<const charT*>(p, p, p+len), boost::u8_to_u32_iterator<const charT*>(p+len, p, p+len));\n}\ntemplate <class charT>\ninline std::pair< boost::u16_to_u32_iterator<const charT*>, boost::u16_to_u32_iterator<const charT*> >\n   make_utf32_seq(const charT* p, std::integral_constant<int, 2> const*)\n{\n   std::size_t len = u_strlen((const UChar*)p);\n   return std::pair< boost::u16_to_u32_iterator<const charT*>, boost::u16_to_u32_iterator<const charT*> >(boost::u16_to_u32_iterator<const charT*>(p, p, p + len), boost::u16_to_u32_iterator<const charT*>(p+len, p, p + len));\n}\ntemplate <class charT>\ninline std::pair< const charT*, const charT* >\n   make_utf32_seq(const charT* p, std::integral_constant<int, 4> const*)\n{\n   return std::pair< const charT*, const charT* >(p, p+icu_regex_traits::length((UChar32 const*)p));\n}\ntemplate <class OutputIterator>\ninline OutputIterator make_utf32_out(OutputIterator o, std::integral_constant<int, 4> const*)\n{\n   return o;\n}\ntemplate <class OutputIterator>\ninline utf16_output_iterator<OutputIterator> make_utf32_out(OutputIterator o, std::integral_constant<int, 2> const*)\n{\n   return o;\n}\ntemplate <class OutputIterator>\ninline utf8_output_iterator<OutputIterator> make_utf32_out(OutputIterator o, std::integral_constant<int, 1> const*)\n{\n   return o;\n}\n\ntemplate <class OutputIterator, class I1, class I2>\nOutputIterator do_regex_replace(OutputIterator out,\n                                 std::pair<I1, I1> const& in,\n                                 const u32regex& e, \n                                 const std::pair<I2, I2>& fmt, \n                                 match_flag_type flags\n                                 )\n{\n   // unfortunately we have to copy the format string in order to pass in onward:\n   std::vector<UChar32> f;\n   f.assign(fmt.first, fmt.second);\n   \n   regex_iterator<I1, UChar32, icu_regex_traits> i(in.first, in.second, e, flags);\n   regex_iterator<I1, UChar32, icu_regex_traits> j;\n   if(i == j)\n   {\n      if(!(flags & regex_constants::format_no_copy))\n         out = std::copy(in.first, in.second, out);\n   }\n   else\n   {\n      I1 last_m = in.first;\n      while(i != j)\n      {\n         if(!(flags & regex_constants::format_no_copy))\n            out = std::copy(i->prefix().first, i->prefix().second, out); \n         if(!f.empty())\n            out = ::boost::BOOST_REGEX_DETAIL_NS::regex_format_imp(out, *i, &*f.begin(), &*f.begin() + f.size(), flags, e.get_traits());\n         else\n            out = ::boost::BOOST_REGEX_DETAIL_NS::regex_format_imp(out, *i, static_cast<UChar32 const*>(0), static_cast<UChar32 const*>(0), flags, e.get_traits());\n         last_m = (*i)[0].second;\n         if(flags & regex_constants::format_first_only)\n            break;\n         ++i;\n      }\n      if(!(flags & regex_constants::format_no_copy))\n         out = std::copy(last_m, in.second, out);\n   }\n   return out;\n}\ntemplate <class BaseIterator>\ninline const BaseIterator& extract_output_base(const BaseIterator& b)\n{\n   return b;\n}\ntemplate <class BaseIterator>\ninline BaseIterator extract_output_base(const utf8_output_iterator<BaseIterator>& b)\n{\n   return b.base();\n}\ntemplate <class BaseIterator>\ninline BaseIterator extract_output_base(const utf16_output_iterator<BaseIterator>& b)\n{\n   return b.base();\n}\n}  // BOOST_REGEX_DETAIL_NS\n\ntemplate <class OutputIterator, class BidirectionalIterator, class charT>\ninline OutputIterator u32regex_replace(OutputIterator out,\n                         BidirectionalIterator first,\n                         BidirectionalIterator last,\n                         const u32regex& e, \n                         const charT* fmt, \n                         match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::extract_output_base\n    (\n      BOOST_REGEX_DETAIL_NS::do_regex_replace(\n         BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast<std::integral_constant<int, sizeof(*first)> const*>(0)),\n         BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast<std::integral_constant<int, sizeof(*first)> const*>(0)),\n         e,\n         BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt, static_cast<std::integral_constant<int, sizeof(*fmt)> const*>(0)),\n         flags)\n      );\n}\n\ntemplate <class OutputIterator, class Iterator, class charT>\ninline OutputIterator u32regex_replace(OutputIterator out,\n                         Iterator first,\n                         Iterator last,\n                         const u32regex& e, \n                         const std::basic_string<charT>& fmt,\n                         match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::extract_output_base\n    (\n      BOOST_REGEX_DETAIL_NS::do_regex_replace(\n         BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast<std::integral_constant<int, sizeof(*first)> const*>(0)),\n         BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast<std::integral_constant<int, sizeof(*first)> const*>(0)),\n         e,\n         BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.begin(), fmt.end(), static_cast<std::integral_constant<int, sizeof(charT)> const*>(0)),\n         flags)\n      );\n}\n\ntemplate <class OutputIterator, class Iterator>\ninline OutputIterator u32regex_replace(OutputIterator out,\n                         Iterator first,\n                         Iterator last,\n                         const u32regex& e, \n                         const U_NAMESPACE_QUALIFIER UnicodeString& fmt,\n                         match_flag_type flags = match_default)\n{\n   return BOOST_REGEX_DETAIL_NS::extract_output_base\n   (\n      BOOST_REGEX_DETAIL_NS::do_regex_replace(\n         BOOST_REGEX_DETAIL_NS::make_utf32_out(out, static_cast<std::integral_constant<int, sizeof(*first)> const*>(0)),\n         BOOST_REGEX_DETAIL_NS::make_utf32_seq(first, last, static_cast<std::integral_constant<int, sizeof(*first)> const*>(0)),\n         e,\n         BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.getBuffer(), fmt.getBuffer() + fmt.length(), static_cast<std::integral_constant<int, 2> const*>(0)),\n         flags)\n      );\n}\n\ntemplate <class charT>\nstd::basic_string<charT> u32regex_replace(const std::basic_string<charT>& s,\n                         const u32regex& e, \n                         const charT* fmt,\n                         match_flag_type flags = match_default)\n{\n   std::basic_string<charT> result;\n   BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<charT> > i(result);\n   u32regex_replace(i, s.begin(), s.end(), e, fmt, flags);\n   return result;\n}\n\ntemplate <class charT>\nstd::basic_string<charT> u32regex_replace(const std::basic_string<charT>& s,\n                         const u32regex& e, \n                         const std::basic_string<charT>& fmt,\n                         match_flag_type flags = match_default)\n{\n   std::basic_string<charT> result;\n   BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<charT> > i(result);\n   u32regex_replace(i, s.begin(), s.end(), e, fmt.c_str(), flags);\n   return result;\n}\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\nclass unicode_string_out_iterator\n{\n   U_NAMESPACE_QUALIFIER UnicodeString* out;\npublic:\n   unicode_string_out_iterator(U_NAMESPACE_QUALIFIER UnicodeString& s) : out(&s) {}\n   unicode_string_out_iterator& operator++() { return *this; }\n   unicode_string_out_iterator& operator++(int) { return *this; }\n   unicode_string_out_iterator& operator*() { return *this; }\n   unicode_string_out_iterator& operator=(UChar v) \n   { \n      *out += v; \n      return *this; \n   }\n   typedef std::ptrdiff_t difference_type;\n   typedef UChar value_type;\n   typedef value_type* pointer;\n   typedef value_type& reference;\n   typedef std::output_iterator_tag iterator_category;\n};\n\n}\n\ninline U_NAMESPACE_QUALIFIER UnicodeString u32regex_replace(const U_NAMESPACE_QUALIFIER UnicodeString& s,\n                         const u32regex& e, \n                         const UChar* fmt,\n                         match_flag_type flags = match_default)\n{\n   U_NAMESPACE_QUALIFIER UnicodeString result;\n   BOOST_REGEX_DETAIL_NS::unicode_string_out_iterator i(result);\n   u32regex_replace(i, s.getBuffer(), s.getBuffer()+s.length(), e, fmt, flags);\n   return result;\n}\n\ninline U_NAMESPACE_QUALIFIER UnicodeString u32regex_replace(const U_NAMESPACE_QUALIFIER UnicodeString& s,\n                         const u32regex& e, \n                         const U_NAMESPACE_QUALIFIER UnicodeString& fmt,\n                         match_flag_type flags = match_default)\n{\n   U_NAMESPACE_QUALIFIER UnicodeString result;\n   BOOST_REGEX_DETAIL_NS::unicode_string_out_iterator i(result);\n   BOOST_REGEX_DETAIL_NS::do_regex_replace(\n         BOOST_REGEX_DETAIL_NS::make_utf32_out(i, static_cast<std::integral_constant<int, 2> const*>(0)),\n         BOOST_REGEX_DETAIL_NS::make_utf32_seq(s.getBuffer(), s.getBuffer()+s.length(), static_cast<std::integral_constant<int, 2> const*>(0)),\n         e,\n         BOOST_REGEX_DETAIL_NS::make_utf32_seq(fmt.getBuffer(), fmt.getBuffer() + fmt.length(), static_cast<std::integral_constant<int, 2> const*>(0)),\n         flags);\n   return result;\n}\n\n} // namespace boost.\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning (pop)\n#endif\n\n#include <boost/regex/v5/u32regex_iterator.hpp>\n#include <boost/regex/v5/u32regex_token_iterator.hpp>\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/iterator_category.hpp",
    "content": "/*\n *\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_match.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Iterator traits for selecting an iterator type as\n  *                an integral constant expression.\n  */\n\n\n#ifndef BOOST_REGEX_ITERATOR_CATEGORY_HPP\n#define BOOST_REGEX_ITERATOR_CATEGORY_HPP\n\n#include <iterator>\n#include <type_traits>\n\nnamespace boost{\nnamespace detail{\n\ntemplate <class I>\nstruct is_random_imp\n{\nprivate:\n   typedef typename std::iterator_traits<I>::iterator_category cat;\npublic:\n   static const bool value = (std::is_convertible<cat*, std::random_access_iterator_tag*>::value);\n};\n\ntemplate <class I>\nstruct is_random_pointer_imp\n{\n   static const bool value = true;\n};\n\ntemplate <bool is_pointer_type>\nstruct is_random_imp_selector\n{\n   template <class I>\n   struct rebind\n   {\n      typedef is_random_imp<I> type;\n   };\n};\n\ntemplate <>\nstruct is_random_imp_selector<true>\n{\n   template <class I>\n   struct rebind\n   {\n      typedef is_random_pointer_imp<I> type;\n   };\n};\n\n}\n\ntemplate <class I>\nstruct is_random_access_iterator\n{\nprivate:\n   typedef detail::is_random_imp_selector< std::is_pointer<I>::value> selector;\n   typedef typename selector::template rebind<I> bound_type;\n   typedef typename bound_type::type answer;\npublic:\n   static const bool value = answer::value;\n};\n\ntemplate <class I>\nconst bool is_random_access_iterator<I>::value;\n\n}\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/iterator_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         iterator_traits.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares iterator traits workarounds.\n  */\n\n#ifndef BOOST_REGEX_V5_ITERATOR_TRAITS_HPP\n#define BOOST_REGEX_V5_ITERATOR_TRAITS_HPP\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class T>\nstruct regex_iterator_traits : public std::iterator_traits<T> {};\n\n} // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/match_flags.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         match_flags.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares match_flags type.\n  */\n\n#ifndef BOOST_REGEX_V5_MATCH_FLAGS\n#define BOOST_REGEX_V5_MATCH_FLAGS\n\n#ifdef __cplusplus\n#  include <cstdint>\n#endif\n\n#ifdef __cplusplus\nnamespace boost{\n   namespace regex_constants{\n#endif\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#if BOOST_REGEX_MSVC >= 1800\n#pragma warning(disable : 26812)\n#endif\n#endif\n\ntypedef enum _match_flags\n{\n   match_default = 0,\n   match_not_bol = 1,                                /* first is not start of line */\n   match_not_eol = match_not_bol << 1,               /* last is not end of line */\n   match_not_bob = match_not_eol << 1,               /* first is not start of buffer */\n   match_not_eob = match_not_bob << 1,               /* last is not end of buffer */\n   match_not_bow = match_not_eob << 1,               /* first is not start of word */\n   match_not_eow = match_not_bow << 1,               /* last is not end of word */\n   match_not_dot_newline = match_not_eow << 1,       /* \\n is not matched by '.' */\n   match_not_dot_null = match_not_dot_newline << 1,  /* '\\0' is not matched by '.' */\n   match_prev_avail = match_not_dot_null << 1,       /* *--first is a valid expression */\n   match_init = match_prev_avail << 1,               /* internal use */\n   match_any = match_init << 1,                      /* don't care what we match */\n   match_not_null = match_any << 1,                  /* string can't be null */\n   match_continuous = match_not_null << 1,           /* each grep match must continue from */\n                                                     /* uninterrupted from the previous one */\n   match_partial = match_continuous << 1,            /* find partial matches */\n   \n   match_stop = match_partial << 1,                  /* stop after first match (grep) V3 only */\n   match_not_initial_null = match_stop,              /* don't match initial null, V4 only */\n   match_all = match_stop << 1,                      /* must find the whole of input even if match_any is set */\n   match_perl = match_all << 1,                      /* Use perl matching rules */\n   match_posix = match_perl << 1,                    /* Use POSIX matching rules */\n   match_nosubs = match_posix << 1,                  /* don't trap marked subs */\n   match_extra = match_nosubs << 1,                  /* include full capture information for repeated captures */\n   match_single_line = match_extra << 1,             /* treat text as single line and ignore any \\n's when matching ^ and $. */\n   match_unused1 = match_single_line << 1,           /* unused */\n   match_unused2 = match_unused1 << 1,               /* unused */\n   match_unused3 = match_unused2 << 1,               /* unused */\n   match_max = match_unused3,\n\n   format_perl = 0,                                  /* perl style replacement */\n   format_default = 0,                               /* ditto. */\n   format_sed = match_max << 1,                      /* sed style replacement. */\n   format_all = format_sed << 1,                     /* enable all extensions to syntax. */\n   format_no_copy = format_all << 1,                 /* don't copy non-matching segments. */\n   format_first_only = format_no_copy << 1,          /* Only replace first occurrence. */\n   format_is_if = format_first_only << 1,            /* internal use only. */\n   format_literal = format_is_if << 1,               /* treat string as a literal */\n\n   match_not_any = match_not_bol | match_not_eol | match_not_bob \n      | match_not_eob | match_not_bow | match_not_eow | match_not_dot_newline \n      | match_not_dot_null | match_prev_avail | match_init | match_not_null\n      | match_continuous | match_partial | match_stop | match_not_initial_null \n      | match_stop | match_all | match_perl | match_posix | match_nosubs\n      | match_extra | match_single_line | match_unused1 | match_unused2 \n      | match_unused3 | match_max | format_perl | format_default | format_sed\n      | format_all | format_no_copy | format_first_only | format_is_if\n      | format_literal\n\n\n} match_flags;\n\ntypedef match_flags match_flag_type;\n\n#ifdef __cplusplus\ninline match_flags operator&(match_flags m1, match_flags m2)\n{ return static_cast<match_flags>(static_cast<std::int32_t>(m1) & static_cast<std::int32_t>(m2)); }\ninline match_flags operator|(match_flags m1, match_flags m2)\n{ return static_cast<match_flags>(static_cast<std::int32_t>(m1) | static_cast<std::int32_t>(m2)); }\ninline match_flags operator^(match_flags m1, match_flags m2)\n{ return static_cast<match_flags>(static_cast<std::int32_t>(m1) ^ static_cast<std::int32_t>(m2)); }\ninline match_flags operator~(match_flags m1)\n{ return static_cast<match_flags>(~static_cast<std::int32_t>(m1)); }\ninline match_flags& operator&=(match_flags& m1, match_flags m2)\n{ m1 = m1&m2; return m1; }\ninline match_flags& operator|=(match_flags& m1, match_flags m2)\n{ m1 = m1|m2; return m1; }\ninline match_flags& operator^=(match_flags& m1, match_flags m2)\n{ m1 = m1^m2; return m1; }\n#endif\n\n#ifdef __cplusplus\n} /* namespace regex_constants */\n/*\n * import names into boost for backwards compatibility:\n */\nusing regex_constants::match_flag_type;\nusing regex_constants::match_default;\nusing regex_constants::match_not_bol;\nusing regex_constants::match_not_eol;\nusing regex_constants::match_not_bob;\nusing regex_constants::match_not_eob;\nusing regex_constants::match_not_bow;\nusing regex_constants::match_not_eow;\nusing regex_constants::match_not_dot_newline;\nusing regex_constants::match_not_dot_null;\nusing regex_constants::match_prev_avail;\n/* using regex_constants::match_init; */\nusing regex_constants::match_any;\nusing regex_constants::match_not_null;\nusing regex_constants::match_continuous;\nusing regex_constants::match_partial;\n/*using regex_constants::match_stop; */\nusing regex_constants::match_all;\nusing regex_constants::match_perl;\nusing regex_constants::match_posix;\nusing regex_constants::match_nosubs;\nusing regex_constants::match_extra;\nusing regex_constants::match_single_line;\n/*using regex_constants::match_max; */\nusing regex_constants::format_all;\nusing regex_constants::format_sed;\nusing regex_constants::format_perl;\nusing regex_constants::format_default;\nusing regex_constants::format_no_copy;\nusing regex_constants::format_first_only;\n/*using regex_constants::format_is_if;*/\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n\n\n} /* namespace boost */\n#endif /* __cplusplus */\n#endif /* include guard */\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/match_results.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2009\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         match_results.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class match_results.\n  */\n\n#ifndef BOOST_REGEX_V5_MATCH_RESULTS_HPP\n#define BOOST_REGEX_V5_MATCH_RESULTS_HPP\n\nnamespace boost{\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable : 4251 4459)\n#if BOOST_REGEX_MSVC < 1700\n#     pragma warning(disable : 4231)\n#endif\n#  if BOOST_REGEX_MSVC < 1600\n#     pragma warning(disable : 4660)\n#  endif\n#endif\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\nclass named_subexpressions;\n\n}\n\ntemplate <class BidiIterator, class Allocator>\nclass match_results\n{ \nprivate:\n   typedef          std::vector<sub_match<BidiIterator>, Allocator> vector_type;\npublic: \n   typedef          sub_match<BidiIterator>                         value_type;\n   typedef typename std::allocator_traits<Allocator>::value_type const &    const_reference;\n   typedef          const_reference                                         reference;\n   typedef typename vector_type::const_iterator                             const_iterator;\n   typedef          const_iterator                                          iterator;\n   typedef typename std::iterator_traits<\n                                    BidiIterator>::difference_type          difference_type;\n   typedef typename std::allocator_traits<Allocator>::size_type             size_type;\n   typedef          Allocator                                               allocator_type;\n   typedef typename std::iterator_traits<\n                                    BidiIterator>::value_type               char_type;\n   typedef          std::basic_string<char_type>                            string_type;\n   typedef          BOOST_REGEX_DETAIL_NS::named_subexpressions             named_sub_type;\n\n   // construct/copy/destroy:\n   explicit match_results(const Allocator& a = Allocator())\n      : m_subs(a), m_base(), m_null(), m_last_closed_paren(0), m_is_singular(true) {}\n   //\n   // IMPORTANT: in the code below, the crazy looking checks around m_is_singular are\n   // all required because it is illegal to copy a singular iterator.\n   // See https://svn.boost.org/trac/boost/ticket/3632.\n   //\n   match_results(const match_results& m)\n      : m_subs(m.m_subs), m_base(), m_null(), m_named_subs(m.m_named_subs), m_last_closed_paren(m.m_last_closed_paren), m_is_singular(m.m_is_singular)\n   {\n      if(!m_is_singular)\n      {\n         m_base = m.m_base;\n         m_null = m.m_null;\n      }\n   }\n   match_results& operator=(const match_results& m)\n   {\n      m_subs = m.m_subs;\n      m_named_subs = m.m_named_subs;\n      m_last_closed_paren = m.m_last_closed_paren;\n      m_is_singular = m.m_is_singular;\n      if(!m_is_singular)\n      {\n         m_base = m.m_base;\n         m_null = m.m_null;\n      }\n      return *this;\n   }\n   ~match_results(){}\n\n   // size:\n   size_type size() const\n   { return empty() ? 0 : m_subs.size() - 2; }\n   size_type max_size() const\n   { return m_subs.max_size(); }\n   bool empty() const\n   { return m_subs.size() < 2; }\n   // element access:\n   difference_type length(int sub = 0) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      sub += 2;\n      if((sub < (int)m_subs.size()) && (sub > 0))\n         return m_subs[sub].length();\n      return 0;\n   }\n   difference_type length(const char_type* sub) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      const char_type* sub_end = sub;\n      while(*sub_end) ++sub_end;\n      return length(named_subexpression_index(sub, sub_end));\n   }\n   template <class charT>\n   difference_type length(const charT* sub) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      const charT* sub_end = sub;\n      while(*sub_end) ++sub_end;\n      return length(named_subexpression_index(sub, sub_end));\n   }\n   template <class charT, class Traits, class A>\n   difference_type length(const std::basic_string<charT, Traits, A>& sub) const\n   {\n      return length(sub.c_str());\n   }\n   difference_type position(size_type sub = 0) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      sub += 2;\n      if(sub < m_subs.size())\n      {\n         const sub_match<BidiIterator>& s = m_subs[sub];\n         if(s.matched || (sub == 2))\n         {\n            return std::distance((BidiIterator)(m_base), (BidiIterator)(s.first));\n         }\n      }\n      return ~static_cast<difference_type>(0);\n   }\n   difference_type position(const char_type* sub) const\n   {\n      const char_type* sub_end = sub;\n      while(*sub_end) ++sub_end;\n      return position(named_subexpression_index(sub, sub_end));\n   }\n   template <class charT>\n   difference_type position(const charT* sub) const\n   {\n      const charT* sub_end = sub;\n      while(*sub_end) ++sub_end;\n      return position(named_subexpression_index(sub, sub_end));\n   }\n   template <class charT, class Traits, class A>\n   difference_type position(const std::basic_string<charT, Traits, A>& sub) const\n   {\n      return position(sub.c_str());\n   }\n   string_type str(int sub = 0) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      sub += 2;\n      string_type result;\n      if(sub < (int)m_subs.size() && (sub > 0))\n      {\n         const sub_match<BidiIterator>& s = m_subs[sub];\n         if(s.matched)\n         {\n            result = s.str();\n         }\n      }\n      return result;\n   }\n   string_type str(const char_type* sub) const\n   {\n      return (*this)[sub].str();\n   }\n   template <class Traits, class A>\n   string_type str(const std::basic_string<char_type, Traits, A>& sub) const\n   {\n      return (*this)[sub].str();\n   }\n   template <class charT>\n   string_type str(const charT* sub) const\n   {\n      return (*this)[sub].str();\n   }\n   template <class charT, class Traits, class A>\n   string_type str(const std::basic_string<charT, Traits, A>& sub) const\n   {\n      return (*this)[sub].str();\n   }\n   const_reference operator[](int sub) const\n   {\n      if(m_is_singular && m_subs.empty())\n         raise_logic_error();\n      sub += 2;\n      if(sub < (int)m_subs.size() && (sub >= 0))\n      {\n         return m_subs[sub];\n      }\n      return m_null;\n   }\n   //\n   // Named sub-expressions:\n   //\n   const_reference named_subexpression(const char_type* i, const char_type* j) const\n   {\n      //\n      // Scan for the leftmost *matched* subexpression with the specified named:\n      //\n      if(m_is_singular)\n         raise_logic_error();\n      BOOST_REGEX_DETAIL_NS::named_subexpressions::range_type r = m_named_subs->equal_range(i, j);\n      while((r.first != r.second) && ((*this)[r.first->index].matched == false))\n         ++r.first;\n      return r.first != r.second ? (*this)[r.first->index] : m_null;\n   }\n   template <class charT>\n   const_reference named_subexpression(const charT* i, const charT* j) const\n   {\n      static_assert(sizeof(charT) <= sizeof(char_type), \"Failed internal logic\");\n      if(i == j)\n         return m_null;\n      std::vector<char_type> s;\n      while(i != j)\n         s.insert(s.end(), *i++);\n      return named_subexpression(&*s.begin(), &*s.begin() + s.size());\n   }\n   int named_subexpression_index(const char_type* i, const char_type* j) const\n   {\n      //\n      // Scan for the leftmost *matched* subexpression with the specified named.\n      // If none found then return the leftmost expression with that name,\n      // otherwise an invalid index:\n      //\n      if(m_is_singular)\n         raise_logic_error();\n      BOOST_REGEX_DETAIL_NS::named_subexpressions::range_type s, r;\n      s = r = m_named_subs->equal_range(i, j);\n      while((r.first != r.second) && ((*this)[r.first->index].matched == false))\n         ++r.first;\n      if(r.first == r.second)\n         r = s;\n      return r.first != r.second ? r.first->index : -20;\n   }\n   template <class charT>\n   int named_subexpression_index(const charT* i, const charT* j) const\n   {\n      static_assert(sizeof(charT) <= sizeof(char_type), \"Failed internal logic\");\n      if(i == j)\n         return -20;\n      std::vector<char_type> s;\n      while(i != j)\n         s.insert(s.end(), *i++);\n      return named_subexpression_index(&*s.begin(), &*s.begin() + s.size());\n   }\n   template <class Traits, class A>\n   const_reference operator[](const std::basic_string<char_type, Traits, A>& s) const\n   {\n      return named_subexpression(s.c_str(), s.c_str() + s.size());\n   }\n   const_reference operator[](const char_type* p) const\n   {\n      const char_type* e = p;\n      while(*e) ++e;\n      return named_subexpression(p, e);\n   }\n\n   template <class charT>\n   const_reference operator[](const charT* p) const\n   {\n      static_assert(sizeof(charT) <= sizeof(char_type), \"Failed internal logic\");\n      if(*p == 0)\n         return m_null;\n      std::vector<char_type> s;\n      while(*p)\n         s.insert(s.end(), *p++);\n      return named_subexpression(&*s.begin(), &*s.begin() + s.size());\n   }\n   template <class charT, class Traits, class A>\n   const_reference operator[](const std::basic_string<charT, Traits, A>& ns) const\n   {\n      static_assert(sizeof(charT) <= sizeof(char_type), \"Failed internal logic\");\n      if(ns.empty())\n         return m_null;\n      std::vector<char_type> s;\n      for(unsigned i = 0; i < ns.size(); ++i)\n         s.insert(s.end(), ns[i]);\n      return named_subexpression(&*s.begin(), &*s.begin() + s.size());\n   }\n\n   const_reference prefix() const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      return (*this)[-1];\n   }\n\n   const_reference suffix() const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      return (*this)[-2];\n   }\n   const_iterator begin() const\n   {\n      return (m_subs.size() > 2) ? (m_subs.begin() + 2) : m_subs.end();\n   }\n   const_iterator end() const\n   {\n      return m_subs.end();\n   }\n   // format:\n   template <class OutputIterator, class Functor>\n   OutputIterator format(OutputIterator out,\n                         Functor fmt,\n                         match_flag_type flags = format_default) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, OutputIterator>::type F;\n      F func(fmt);\n      return func(*this, out, flags);\n   }\n   template <class Functor>\n   string_type format(Functor fmt, match_flag_type flags = format_default) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      std::basic_string<char_type> result;\n      BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> > i(result);\n\n      typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> > >::type F;\n      F func(fmt);\n\n      func(*this, i, flags);\n      return result;\n   }\n   // format with locale:\n   template <class OutputIterator, class Functor, class RegexT>\n   OutputIterator format(OutputIterator out,\n                         Functor fmt,\n                         match_flag_type flags,\n                         const RegexT& re) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      typedef ::boost::regex_traits_wrapper<typename RegexT::traits_type> traits_type;\n      typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, OutputIterator, traits_type>::type F;\n      F func(fmt);\n      return func(*this, out, flags, re.get_traits());\n   }\n   template <class RegexT, class Functor>\n   string_type format(Functor fmt,\n                      match_flag_type flags,\n                      const RegexT& re) const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      typedef ::boost::regex_traits_wrapper<typename RegexT::traits_type> traits_type;\n      std::basic_string<char_type> result;\n      BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> > i(result);\n\n      typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> >, traits_type >::type F;\n      F func(fmt);\n\n      func(*this, i, flags, re.get_traits());\n      return result;\n   }\n\n   const_reference get_last_closed_paren()const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      return m_last_closed_paren == 0 ? m_null : (*this)[m_last_closed_paren];\n   }\n\n   allocator_type get_allocator() const\n   {\n      return m_subs.get_allocator();\n   }\n   void swap(match_results& that)\n   {\n      std::swap(m_subs, that.m_subs);\n      std::swap(m_named_subs, that.m_named_subs);\n      std::swap(m_last_closed_paren, that.m_last_closed_paren);\n      if(m_is_singular)\n      {\n         if(!that.m_is_singular)\n         {\n            m_base = that.m_base;\n            m_null = that.m_null;\n         }\n      }\n      else if(that.m_is_singular)\n      {\n         that.m_base = m_base;\n         that.m_null = m_null;\n      }\n      else\n      {\n         std::swap(m_base, that.m_base);\n         std::swap(m_null, that.m_null);\n      }\n      std::swap(m_is_singular, that.m_is_singular);\n   }\n   bool operator==(const match_results& that)const\n   {\n      if(m_is_singular)\n      {\n         return that.m_is_singular;\n      }\n      else if(that.m_is_singular)\n      {\n         return false;\n      }\n      return (m_subs == that.m_subs) && (m_base == that.m_base) && (m_last_closed_paren == that.m_last_closed_paren);\n   }\n   bool operator!=(const match_results& that)const\n   { return !(*this == that); }\n\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   typedef typename sub_match<BidiIterator>::capture_sequence_type capture_sequence_type;\n\n   const capture_sequence_type& captures(int i)const\n   {\n      if(m_is_singular)\n         raise_logic_error();\n      return (*this)[i].captures();\n   }\n#endif\n\n   //\n   // private access functions:\n   void  set_second(BidiIterator i)\n   {\n      BOOST_REGEX_ASSERT(m_subs.size() > 2);\n      m_subs[2].second = i;\n      m_subs[2].matched = true;\n      m_subs[0].first = i;\n      m_subs[0].matched = (m_subs[0].first != m_subs[0].second);\n      m_null.first = i;\n      m_null.second = i;\n      m_null.matched = false;\n      m_is_singular = false;\n   }\n\n   void  set_second(BidiIterator i, size_type pos, bool m = true, bool escape_k = false)\n   {\n      if(pos)\n         m_last_closed_paren = static_cast<int>(pos);\n      pos += 2;\n      BOOST_REGEX_ASSERT(m_subs.size() > pos);\n      m_subs[pos].second = i;\n      m_subs[pos].matched = m;\n      if((pos == 2) && !escape_k)\n      {\n         m_subs[0].first = i;\n         m_subs[0].matched = (m_subs[0].first != m_subs[0].second);\n         m_null.first = i;\n         m_null.second = i;\n         m_null.matched = false;\n         m_is_singular = false;\n      }\n   }\n   void  set_size(size_type n, BidiIterator i, BidiIterator j)\n   {\n      value_type v(j);\n      size_type len = m_subs.size();\n      if(len > n + 2)\n      {\n         m_subs.erase(m_subs.begin()+n+2, m_subs.end());\n         std::fill(m_subs.begin(), m_subs.end(), v);\n      }\n      else\n      {\n         std::fill(m_subs.begin(), m_subs.end(), v);\n         if(n+2 != len)\n            m_subs.insert(m_subs.end(), n+2-len, v);\n      }\n      m_subs[1].first = i;\n      m_last_closed_paren = 0;\n   }\n   void  set_base(BidiIterator pos)\n   {\n      m_base = pos;\n   }\n   BidiIterator base()const\n   {\n      return m_base;\n   }\n   void  set_first(BidiIterator i)\n   {\n      BOOST_REGEX_ASSERT(m_subs.size() > 2);\n      // set up prefix:\n      m_subs[1].second = i;\n      m_subs[1].matched = (m_subs[1].first != i);\n      // set up $0:\n      m_subs[2].first = i;\n      // zero out everything else:\n      for(size_type n = 3; n < m_subs.size(); ++n)\n      {\n         m_subs[n].first = m_subs[n].second = m_subs[0].second;\n         m_subs[n].matched = false;\n      }\n   }\n   void  set_first(BidiIterator i, size_type pos, bool escape_k = false)\n   {\n      BOOST_REGEX_ASSERT(pos+2 < m_subs.size());\n      if(pos || escape_k)\n      {\n         m_subs[pos+2].first = i;\n         if(escape_k)\n         {\n            m_subs[1].second = i;\n            m_subs[1].matched = (m_subs[1].first != m_subs[1].second);\n         }\n      }\n      else\n         set_first(i);\n   }\n   void  maybe_assign(const match_results<BidiIterator, Allocator>& m);\n\n   void  set_named_subs(std::shared_ptr<named_sub_type> subs)\n   {\n      m_named_subs = subs;\n   }\n\nprivate:\n   //\n   // Error handler called when an uninitialized match_results is accessed:\n   //\n   static void raise_logic_error()\n   {\n      std::logic_error e(\"Attempt to access an uninitialized boost::match_results<> class.\");\n#ifndef BOOST_REGEX_STANDALONE\n      boost::throw_exception(e);\n#else\n      throw e;\n#endif\n   }\n\n\n   vector_type            m_subs;                      // subexpressions\n   BidiIterator   m_base;                              // where the search started from\n   sub_match<BidiIterator> m_null;                     // a null match\n   std::shared_ptr<named_sub_type> m_named_subs;     // Shared copy of named subs in the regex object\n   int m_last_closed_paren;                            // Last ) to be seen - used for formatting\n   bool m_is_singular;                                 // True if our stored iterators are singular\n};\n\ntemplate <class BidiIterator, class Allocator>\nvoid  match_results<BidiIterator, Allocator>::maybe_assign(const match_results<BidiIterator, Allocator>& m)\n{\n   if(m_is_singular)\n   {\n      *this = m;\n      return;\n   }\n   const_iterator p1, p2;\n   p1 = begin();\n   p2 = m.begin();\n   //\n   // Distances are measured from the start of *this* match, unless this isn't\n   // a valid match in which case we use the start of the whole sequence.  Note that\n   // no subsequent match-candidate can ever be to the left of the first match found.\n   // This ensures that when we are using bidirectional iterators, that distances \n   // measured are as short as possible, and therefore as efficient as possible\n   // to compute.  Finally note that we don't use the \"matched\" data member to test\n   // whether a sub-expression is a valid match, because partial matches set this\n   // to false for sub-expression 0.\n   //\n   BidiIterator l_end = this->suffix().second;\n   BidiIterator l_base = (p1->first == l_end) ? this->prefix().first : (*this)[0].first;\n   difference_type len1 = 0;\n   difference_type len2 = 0;\n   difference_type base1 = 0;\n   difference_type base2 = 0;\n   std::size_t i;\n   for(i = 0; i < size(); ++i, ++p1, ++p2)\n   {\n      //\n      // Leftmost takes priority over longest; handle special cases\n      // where distances need not be computed first (an optimisation\n      // for bidirectional iterators: ensure that we don't accidently\n      // compute the length of the whole sequence, as this can be really\n      // expensive).\n      //\n      if(p1->first == l_end)\n      {\n         if(p2->first != l_end)\n         {\n            // p2 must be better than p1, and no need to calculate\n            // actual distances:\n            base1 = 1;\n            base2 = 0;\n            break;\n         }\n         else\n         {\n            // *p1 and *p2 are either unmatched or match end-of sequence,\n            // either way no need to calculate distances:\n            if((p1->matched == false) && (p2->matched == true))\n               break;\n            if((p1->matched == true) && (p2->matched == false))\n               return;\n            continue;\n         }\n      }\n      else if(p2->first == l_end)\n      {\n         // p1 better than p2, and no need to calculate distances:\n         return;\n      }\n      base1 = std::distance(l_base, p1->first);\n      base2 = std::distance(l_base, p2->first);\n      BOOST_REGEX_ASSERT(base1 >= 0);\n      BOOST_REGEX_ASSERT(base2 >= 0);\n      if(base1 < base2) return;\n      if(base2 < base1) break;\n\n      len1 = std::distance((BidiIterator)p1->first, (BidiIterator)p1->second);\n      len2 = std::distance((BidiIterator)p2->first, (BidiIterator)p2->second);\n      BOOST_REGEX_ASSERT(len1 >= 0);\n      BOOST_REGEX_ASSERT(len2 >= 0);\n      if((len1 != len2) || ((p1->matched == false) && (p2->matched == true)))\n         break;\n      if((p1->matched == true) && (p2->matched == false))\n         return;\n   }\n   if(i == size())\n      return;\n   if(base2 < base1)\n      *this = m;\n   else if((len2 > len1) || ((p1->matched == false) && (p2->matched == true)) )\n      *this = m;\n}\n\ntemplate <class BidiIterator, class Allocator>\nvoid swap(match_results<BidiIterator, Allocator>& a, match_results<BidiIterator, Allocator>& b)\n{\n   a.swap(b);\n}\n\ntemplate <class charT, class traits, class BidiIterator, class Allocator>\nstd::basic_ostream<charT, traits>&\n   operator << (std::basic_ostream<charT, traits>& os,\n                const match_results<BidiIterator, Allocator>& s)\n{\n   return (os << s.str());\n}\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n} // namespace boost\n\n#endif\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/mem_block_cache.hpp",
    "content": " /*\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         mem_block_cache.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: memory block cache used by the non-recursive matcher.\n  */\n\n#ifndef BOOST_REGEX_V5_MEM_BLOCK_CACHE_HPP\n#define BOOST_REGEX_V5_MEM_BLOCK_CACHE_HPP\n\n#include <new>\n#ifdef BOOST_HAS_THREADS\n#include <mutex>\n#endif\n\n#ifndef BOOST_NO_CXX11_HDR_ATOMIC\n  #include <atomic>\n  #if ATOMIC_POINTER_LOCK_FREE == 2\n    #define BOOST_REGEX_MEM_BLOCK_CACHE_LOCK_FREE\n    #define BOOST_REGEX_ATOMIC_POINTER std::atomic\n  #endif\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\n#if BOOST_REGEX_MAX_CACHE_BLOCKS != 0\n#ifdef BOOST_REGEX_MEM_BLOCK_CACHE_LOCK_FREE /* lock free implementation */\nstruct mem_block_cache\n{\n  std::atomic<void*> cache[BOOST_REGEX_MAX_CACHE_BLOCKS];\n\n   ~mem_block_cache()\n   {\n     for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) {\n       if (cache[i].load()) ::operator delete(cache[i].load());\n     }\n   }\n   void* get()\n   {\n     for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) {\n       void* p = cache[i].load();\n       if (p != NULL) {\n         if (cache[i].compare_exchange_strong(p, NULL)) return p;\n       }\n     }\n     return ::operator new(BOOST_REGEX_BLOCKSIZE);\n   }\n   void put(void* ptr)\n   {\n     for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) {\n       void* p = cache[i].load();\n       if (p == NULL) {\n         if (cache[i].compare_exchange_strong(p, ptr)) return;\n       }\n     }\n     ::operator delete(ptr);\n   }\n\n   static mem_block_cache& instance()\n   {\n      static mem_block_cache block_cache = { { {nullptr} } };\n      return block_cache;\n   }\n};\n\n\n#else /* lock-based implementation */\n\n\nstruct mem_block_node\n{\n   mem_block_node* next;\n};\n\nstruct mem_block_cache\n{\n   // this member has to be statically initialsed:\n   mem_block_node* next { nullptr };\n   unsigned cached_blocks { 0 };\n#ifdef BOOST_HAS_THREADS\n   std::mutex mut;\n#endif\n\n   ~mem_block_cache()\n   {\n      while(next)\n      {\n         mem_block_node* old = next;\n         next = next->next;\n         ::operator delete(old);\n      }\n   }\n   void* get()\n   {\n#ifdef BOOST_HAS_THREADS\n      std::lock_guard<std::mutex> g(mut);\n#endif\n     if(next)\n      {\n         mem_block_node* result = next;\n         next = next->next;\n         --cached_blocks;\n         return result;\n      }\n      return ::operator new(BOOST_REGEX_BLOCKSIZE);\n   }\n   void put(void* p)\n   {\n#ifdef BOOST_HAS_THREADS\n      std::lock_guard<std::mutex> g(mut);\n#endif\n      if(cached_blocks >= BOOST_REGEX_MAX_CACHE_BLOCKS)\n      {\n         ::operator delete(p);\n      }\n      else\n      {\n         mem_block_node* old = static_cast<mem_block_node*>(p);\n         old->next = next;\n         next = old;\n         ++cached_blocks;\n      }\n   }\n   static mem_block_cache& instance()\n   {\n      static mem_block_cache block_cache;\n      return block_cache;\n   }\n};\n#endif\n#endif\n\n#if BOOST_REGEX_MAX_CACHE_BLOCKS == 0\n\ninline void*  get_mem_block()\n{\n   return ::operator new(BOOST_REGEX_BLOCKSIZE);\n}\n\ninline void  put_mem_block(void* p)\n{\n   ::operator delete(p);\n}\n\n#else\n\ninline void*  get_mem_block()\n{\n   return mem_block_cache::instance().get();\n}\n\ninline void  put_mem_block(void* p)\n{\n   mem_block_cache::instance().put(p);\n}\n\n#endif\n}\n} // namespace boost\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/object_cache.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         object_cache.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Implements a generic object cache.\n  */\n\n#ifndef BOOST_REGEX_OBJECT_CACHE_HPP\n#define BOOST_REGEX_OBJECT_CACHE_HPP\n\n#include <boost/regex/config.hpp>\n#include <memory>\n#include <map>\n#include <list>\n#include <stdexcept>\n#include <string>\n#ifdef BOOST_HAS_THREADS\n#include <mutex>\n#endif\n\nnamespace boost{\n\ntemplate <class Key, class Object>\nclass object_cache\n{\npublic:\n   typedef std::pair< ::std::shared_ptr<Object const>, Key const*> value_type;\n   typedef std::list<value_type> list_type;\n   typedef typename list_type::iterator list_iterator;\n   typedef std::map<Key, list_iterator> map_type;\n   typedef typename map_type::iterator map_iterator;\n   typedef typename list_type::size_type size_type;\n   static std::shared_ptr<Object const> get(const Key& k, size_type l_max_cache_size);\n\nprivate:\n   static std::shared_ptr<Object const> do_get(const Key& k, size_type l_max_cache_size);\n\n   struct data\n   {\n      list_type   cont;\n      map_type    index;\n   };\n\n   // Needed by compilers not implementing the resolution to DR45. For reference,\n   // see http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#45.\n   friend struct data;\n};\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4702)\n#endif\ntemplate <class Key, class Object>\nstd::shared_ptr<Object const> object_cache<Key, Object>::get(const Key& k, size_type l_max_cache_size)\n{\n#ifdef BOOST_HAS_THREADS\n   static std::mutex mut;\n   std::lock_guard<std::mutex> l(mut);\n   return do_get(k, l_max_cache_size);\n#else\n   return do_get(k, l_max_cache_size);\n#endif\n}\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n\ntemplate <class Key, class Object>\nstd::shared_ptr<Object const> object_cache<Key, Object>::do_get(const Key& k, size_type l_max_cache_size)\n{\n   typedef typename object_cache<Key, Object>::data object_data;\n   typedef typename map_type::size_type map_size_type;\n   static object_data s_data;\n\n   //\n   // see if the object is already in the cache:\n   //\n   map_iterator mpos = s_data.index.find(k);\n   if(mpos != s_data.index.end())\n   {\n      //\n      // Eureka! \n      // We have a cached item, bump it up the list and return it:\n      //\n      if(--(s_data.cont.end()) != mpos->second)\n      {\n         // splice out the item we want to move:\n         list_type temp;\n         temp.splice(temp.end(), s_data.cont, mpos->second);\n         // and now place it at the end of the list:\n         s_data.cont.splice(s_data.cont.end(), temp, temp.begin());\n         BOOST_REGEX_ASSERT(*(s_data.cont.back().second) == k);\n         // update index with new position:\n         mpos->second = --(s_data.cont.end());\n         BOOST_REGEX_ASSERT(&(mpos->first) == mpos->second->second);\n         BOOST_REGEX_ASSERT(&(mpos->first) == s_data.cont.back().second);\n      }\n      return s_data.cont.back().first;\n   }\n   //\n   // if we get here then the item is not in the cache,\n   // so create it:\n   //\n   std::shared_ptr<Object const> result(new Object(k));\n   //\n   // Add it to the list, and index it:\n   //\n   s_data.cont.push_back(value_type(result, static_cast<Key const*>(0)));\n   s_data.index.insert(std::make_pair(k, --(s_data.cont.end())));\n   s_data.cont.back().second = &(s_data.index.find(k)->first);\n   map_size_type s = s_data.index.size();\n   BOOST_REGEX_ASSERT(s_data.index[k]->first.get() == result.get());\n   BOOST_REGEX_ASSERT(&(s_data.index.find(k)->first) == s_data.cont.back().second);\n   BOOST_REGEX_ASSERT(s_data.index.find(k)->first == k);\n   if(s > l_max_cache_size)\n   {\n      //\n      // We have too many items in the list, so we need to start\n      // popping them off the back of the list, but only if they're\n      // being held uniquely by us:\n      //\n      list_iterator pos = s_data.cont.begin();\n      list_iterator last = s_data.cont.end();\n      while((pos != last) && (s > l_max_cache_size))\n      {\n         if(pos->first.use_count() == 1)\n         {\n            list_iterator condemmed(pos);\n            ++pos;\n            // now remove the items from our containers, \n            // then order has to be as follows:\n            BOOST_REGEX_ASSERT(s_data.index.find(*(condemmed->second)) != s_data.index.end());\n            s_data.index.erase(*(condemmed->second));\n            s_data.cont.erase(condemmed); \n            --s;\n         }\n         else\n            ++pos;\n      }\n      BOOST_REGEX_ASSERT(s_data.index[k]->first.get() == result.get());\n      BOOST_REGEX_ASSERT(&(s_data.index.find(k)->first) == s_data.cont.back().second);\n      BOOST_REGEX_ASSERT(s_data.index.find(k)->first == k);\n   }\n   return result;\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/pattern_except.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         pattern_except.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares pattern-matching exception classes.\n  */\n\n#ifndef BOOST_RE_V5_PAT_EXCEPT_HPP\n#define BOOST_RE_V5_PAT_EXCEPT_HPP\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n\n#include <cstddef>\n#include <stdexcept>\n#include <boost/regex/v5/error_type.hpp>\n#include <boost/regex/v5/regex_traits_defaults.hpp>\n\nnamespace boost{\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable : 4275)\n#if BOOST_REGEX_MSVC >= 1800\n#pragma warning(disable : 26812 4459)\n#endif\n#endif\nclass regex_error : public std::runtime_error\n{\npublic:\n   explicit regex_error(const std::string& s, regex_constants::error_type err = regex_constants::error_unknown, std::ptrdiff_t pos = 0)\n      : std::runtime_error(s)\n      , m_error_code(err)\n      , m_position(pos)\n   {\n   }\n   explicit regex_error(regex_constants::error_type err)\n      : std::runtime_error(::boost::BOOST_REGEX_DETAIL_NS::get_default_error_string(err))\n      , m_error_code(err)\n      , m_position(0)\n   {\n   }\n   ~regex_error() noexcept override {}\n   regex_constants::error_type code()const\n   { return m_error_code; }\n   std::ptrdiff_t position()const\n   { return m_position; }\n   void raise()const \n   {\n#ifndef BOOST_NO_EXCEPTIONS\n#ifndef BOOST_REGEX_STANDALONE\n      ::boost::throw_exception(*this);\n#else\n      throw* this;\n#endif\n#endif\n   }\nprivate:\n   regex_constants::error_type m_error_code;\n   std::ptrdiff_t m_position;\n};\n\ntypedef regex_error bad_pattern;\ntypedef regex_error bad_expression;\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class E>\ninline void raise_runtime_error(const E& ex)\n{\n#ifndef BOOST_REGEX_STANDALONE\n   ::boost::throw_exception(ex);\n#else\n   throw ex;\n#endif\n}\n\ntemplate <class traits>\nvoid raise_error(const traits& t, regex_constants::error_type code)\n{\n   (void)t;  // warning suppression\n   regex_error e(t.error_string(code), code, 0);\n   ::boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(e);\n}\n\n}\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/perl_matcher.hpp",
    "content": "/*\n *\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n#ifndef BOOST_REGEX_MATCHER_HPP\n#define BOOST_REGEX_MATCHER_HPP\n\n#include <boost/regex/v5/iterator_category.hpp>\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#pragma warning(disable : 4251 4459)\n#if BOOST_REGEX_MSVC < 1700\n#     pragma warning(disable : 4231)\n#endif\n#  if BOOST_REGEX_MSVC < 1600\n#     pragma warning(disable : 4660)\n#  endif\n#if BOOST_REGEX_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\n//\n// error checking API:\n//\ninline void  verify_options(boost::regex_constants::syntax_option_type, match_flag_type mf)\n{\n   //\n   // can't mix match_extra with POSIX matching rules:\n   //\n   if ((mf & match_extra) && (mf & match_posix))\n   {\n      std::logic_error msg(\"Usage Error: Can't mix regular expression captures with POSIX matching rules\");\n#ifndef BOOST_REGEX_STANDALONE\n      throw_exception(msg);\n#else\n      throw msg;\n#endif\n   }\n}\n//\n// function can_start:\n//\ntemplate <class charT>\ninline bool can_start(charT c, const unsigned char* map, unsigned char mask)\n{\n   return ((c < static_cast<charT>(0)) ? true : ((c >= static_cast<charT>(1 << CHAR_BIT)) ? true : map[c] & mask));\n}\ninline bool can_start(char c, const unsigned char* map, unsigned char mask)\n{\n   return map[(unsigned char)c] & mask;\n}\ninline bool can_start(signed char c, const unsigned char* map, unsigned char mask)\n{\n   return map[(unsigned char)c] & mask;\n}\ninline bool can_start(unsigned char c, const unsigned char* map, unsigned char mask)\n{\n   return map[c] & mask;\n}\ninline bool can_start(unsigned short c, const unsigned char* map, unsigned char mask)\n{\n   return ((c >= (1 << CHAR_BIT)) ? true : map[c] & mask);\n}\n#if defined(WCHAR_MIN) && (WCHAR_MIN == 0) && !defined(BOOST_NO_INTRINSIC_WCHAR_T)\ninline bool can_start(wchar_t c, const unsigned char* map, unsigned char mask)\n{\n   return ((c >= static_cast<wchar_t>(1u << CHAR_BIT)) ? true : map[c] & mask);\n}\n#endif\n#if !defined(BOOST_NO_INTRINSIC_WCHAR_T)\ninline bool can_start(unsigned int c, const unsigned char* map, unsigned char mask)\n{\n   return (((c >= static_cast<unsigned int>(1u << CHAR_BIT)) ? true : map[c] & mask));\n}\n#endif\n\ntemplate <class C, class T, class A>\ninline int string_compare(const std::basic_string<C,T,A>& s, const C* p)\n{ \n   if(0 == *p)\n   {\n      if(s.empty() || ((s.size() == 1) && (s[0] == 0)))\n         return 0;\n   }\n   return s.compare(p); \n}\ntemplate <class Seq, class C>\ninline int string_compare(const Seq& s, const C* p)\n{\n   std::size_t i = 0;\n   while((i < s.size()) && (p[i] == s[i]))\n   {\n      ++i;\n   }\n   return (i == s.size()) ? -(int)p[i] : (int)s[i] - (int)p[i];\n}\n# define STR_COMP(s,p) string_compare(s,p)\n\ntemplate<class charT>\ninline const charT* re_skip_past_null(const charT* p)\n{\n  while (*p != static_cast<charT>(0)) ++p;\n  return ++p;\n}\n\ntemplate <class iterator, class charT, class traits_type, class char_classT>\niterator  re_is_set_member(iterator next, \n                          iterator last, \n                          const re_set_long<char_classT>* set_, \n                          const regex_data<charT, traits_type>& e, bool icase)\n{   \n   const charT* p = reinterpret_cast<const charT*>(set_+1);\n   iterator ptr;\n   unsigned int i;\n   //bool icase = e.m_flags & regex_constants::icase;\n\n   if(next == last) return next;\n\n   typedef typename traits_type::string_type traits_string_type;\n   const ::boost::regex_traits_wrapper<traits_type>& traits_inst = *(e.m_ptraits);\n   \n   // dwa 9/13/00 suppress incorrect MSVC warning - it claims this is never\n   // referenced\n   (void)traits_inst;\n\n   // try and match a single character, could be a multi-character\n   // collating element...\n   for(i = 0; i < set_->csingles; ++i)\n   {\n      ptr = next;\n      if(*p == static_cast<charT>(0))\n      {\n         // treat null string as special case:\n         if(traits_inst.translate(*ptr, icase))\n         {\n            ++p;\n            continue;\n         }\n         return set_->isnot ? next : (ptr == next) ? ++next : ptr;\n      }\n      else\n      {\n         while(*p && (ptr != last))\n         {\n            if(traits_inst.translate(*ptr, icase) != *p)\n               break;\n            ++p;\n            ++ptr;\n         }\n\n         if(*p == static_cast<charT>(0)) // if null we've matched\n            return set_->isnot ? next : (ptr == next) ? ++next : ptr;\n\n         p = re_skip_past_null(p);     // skip null\n      }\n   }\n\n   charT col = traits_inst.translate(*next, icase);\n\n\n   if(set_->cranges || set_->cequivalents)\n   {\n      traits_string_type s1;\n      //\n      // try and match a range, NB only a single character can match\n      if(set_->cranges)\n      {\n         if((e.m_flags & regex_constants::collate) == 0)\n            s1.assign(1, col);\n         else\n         {\n            charT a[2] = { col, charT(0), };\n            s1 = traits_inst.transform(a, a + 1);\n         }\n         for(i = 0; i < set_->cranges; ++i)\n         {\n            if(STR_COMP(s1, p) >= 0)\n            {\n               do{ ++p; }while(*p);\n               ++p;\n               if(STR_COMP(s1, p) <= 0)\n                  return set_->isnot ? next : ++next;\n            }\n            else\n            {\n               // skip first string\n               do{ ++p; }while(*p);\n               ++p;\n            }\n            // skip second string\n            do{ ++p; }while(*p);\n            ++p;\n         }\n      }\n      //\n      // try and match an equivalence class, NB only a single character can match\n      if(set_->cequivalents)\n      {\n         charT a[2] = { col, charT(0), };\n         s1 = traits_inst.transform_primary(a, a +1);\n         for(i = 0; i < set_->cequivalents; ++i)\n         {\n            if(STR_COMP(s1, p) == 0)\n               return set_->isnot ? next : ++next;\n            // skip string\n            do{ ++p; }while(*p);\n            ++p;\n         }\n      }\n   }\n   if(traits_inst.isctype(col, set_->cclasses) == true)\n      return set_->isnot ? next : ++next;\n   if((set_->cnclasses != 0) && (traits_inst.isctype(col, set_->cnclasses) == false))\n      return set_->isnot ? next : ++next;\n   return set_->isnot ? ++next : next;\n}\n\ntemplate <class BidiIterator>\nclass repeater_count\n{\n   repeater_count** stack;\n   repeater_count* next;\n   int state_id;\n   std::size_t count;        // the number of iterations so far\n   BidiIterator start_pos;   // where the last repeat started\n\n   repeater_count* unwind_until(int n, repeater_count* p, int current_recursion_id)\n   { \n      while(p && (p->state_id != n))\n      {\n         if(-2 - current_recursion_id == p->state_id)\n            return 0;\n         p = p->next;\n         if(p && (p->state_id < 0))\n         {\n            p = unwind_until(p->state_id, p, current_recursion_id);\n            if(!p)\n               return p;\n            p = p->next;\n         }\n      }\n      return p;\n   }\npublic:\n   repeater_count(repeater_count** s) : stack(s), next(0), state_id(-1), count(0), start_pos() {}\n   \n   repeater_count(int i, repeater_count** s, BidiIterator start, int current_recursion_id)\n      : start_pos(start)\n   {\n      state_id = i;\n      stack = s;\n      next = *stack;\n      *stack = this;\n      if((state_id > next->state_id) && (next->state_id >= 0))\n         count = 0;\n      else\n      {\n         repeater_count* p = next;\n         p = unwind_until(state_id, p, current_recursion_id);\n         if(p)\n         {\n            count = p->count;\n            start_pos = p->start_pos;\n         }\n         else\n            count = 0;\n      }\n   }\n   ~repeater_count()\n   {\n      if(next)\n         *stack = next;\n   }\n   std::size_t get_count() { return count; }\n   int get_id() { return state_id; }\n   std::size_t operator++() { return ++count; }\n   bool check_null_repeat(const BidiIterator& pos, std::size_t max)\n   {\n      // this is called when we are about to start a new repeat,\n      // if the last one was NULL move our count to max,\n      // otherwise save the current position.\n      bool result = (count == 0) ? false : (pos == start_pos);\n      if(result)\n         count = max;\n      else\n         start_pos = pos;\n      return result;\n   }\n};\n\nstruct saved_state;\n\nenum saved_state_type\n{\n   saved_type_end = 0,\n   saved_type_paren = 1,\n   saved_type_recurse = 2,\n   saved_type_assertion = 3,\n   saved_state_alt = 4,\n   saved_state_repeater_count = 5,\n   saved_state_extra_block = 6,\n   saved_state_greedy_single_repeat = 7,\n   saved_state_rep_slow_dot = 8,\n   saved_state_rep_fast_dot = 9,\n   saved_state_rep_char = 10,\n   saved_state_rep_short_set = 11,\n   saved_state_rep_long_set = 12,\n   saved_state_non_greedy_long_repeat = 13, \n   saved_state_count = 14\n};\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#if BOOST_REGEX_MSVC >= 1800\n#pragma warning(disable:26495)\n#endif\n#endif\ntemplate <class Results>\nstruct recursion_info\n{\n   typedef typename Results::value_type value_type;\n   typedef typename value_type::iterator iterator;\n   int idx;\n   const re_syntax_base* preturn_address;\n   Results results;\n   repeater_count<iterator>* repeater_stack;\n   iterator location_of_start;\n};\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n\ntemplate <class BidiIterator, class Allocator, class traits>\nclass perl_matcher\n{\npublic:\n   typedef typename traits::char_type char_type;\n   typedef perl_matcher<BidiIterator, Allocator, traits> self_type;\n   typedef bool (self_type::*matcher_proc_type)();\n   typedef std::size_t traits_size_type;\n   typedef typename is_byte<char_type>::width_type width_type;\n   typedef typename std::iterator_traits<BidiIterator>::difference_type difference_type;\n   typedef match_results<BidiIterator, Allocator> results_type;\n\n   perl_matcher(BidiIterator first, BidiIterator end, \n      match_results<BidiIterator, Allocator>& what, \n      const basic_regex<char_type, traits>& e,\n      match_flag_type f,\n      BidiIterator l_base)\n      :  m_result(what), base(first), last(end), \n         position(first), backstop(l_base), re(e), traits_inst(e.get_traits()), \n         m_independent(false), next_count(&rep_obj), rep_obj(&next_count)\n      , m_recursions(0)\n   {\n      construct_init(e, f);\n   }\n\n   bool match();\n   bool find();\n\n   void setf(match_flag_type f)\n   { m_match_flags |= f; }\n   void unsetf(match_flag_type f)\n   { m_match_flags &= ~f; }\n\nprivate:\n   void construct_init(const basic_regex<char_type, traits>& e, match_flag_type f);\n\n   bool find_imp();\n   bool match_imp();\n   void estimate_max_state_count(std::random_access_iterator_tag*);\n   void estimate_max_state_count(void*);\n   bool match_prefix();\n   bool match_all_states();\n\n   // match procs, stored in s_match_vtable:\n   bool match_startmark();\n   bool match_endmark();\n   bool match_literal();\n   bool match_start_line();\n   bool match_end_line();\n   bool match_wild();\n   bool match_match();\n   bool match_word_boundary();\n   bool match_within_word();\n   bool match_word_start();\n   bool match_word_end();\n   bool match_buffer_start();\n   bool match_buffer_end();\n   bool match_backref();\n   bool match_long_set();\n   bool match_set();\n   bool match_jump();\n   bool match_alt();\n   bool match_rep();\n   bool match_combining();\n   bool match_soft_buffer_end();\n   bool match_restart_continue();\n   bool match_long_set_repeat();\n   bool match_set_repeat();\n   bool match_char_repeat();\n   bool match_dot_repeat_fast();\n   bool match_dot_repeat_slow();\n   bool match_dot_repeat_dispatch()\n   {\n      return ::boost::is_random_access_iterator<BidiIterator>::value ? match_dot_repeat_fast() : match_dot_repeat_slow();\n   }\n   bool match_backstep();\n   bool match_assert_backref();\n   bool match_toggle_case();\n   bool match_recursion();\n   bool match_fail();\n   bool match_accept();\n   bool match_commit();\n   bool match_then();\n   bool skip_until_paren(int index, bool match = true);\n\n   // find procs stored in s_find_vtable:\n   bool find_restart_any();\n   bool find_restart_word();\n   bool find_restart_line();\n   bool find_restart_buf();\n   bool find_restart_lit();\n\nprivate:\n   // final result structure to be filled in:\n   match_results<BidiIterator, Allocator>& m_result;\n   // temporary result for POSIX matches:\n   std::unique_ptr<match_results<BidiIterator, Allocator> > m_temp_match;\n   // pointer to actual result structure to fill in:\n   match_results<BidiIterator, Allocator>* m_presult;\n   // start of sequence being searched:\n   BidiIterator base;\n   // end of sequence being searched:\n   BidiIterator last; \n   // current character being examined:\n   BidiIterator position;\n   // where to restart next search after failed match attempt:\n   BidiIterator restart;\n   // where the current search started from, acts as base for $` during grep:\n   BidiIterator search_base;\n   // how far we can go back when matching lookbehind:\n   BidiIterator backstop;\n   // the expression being examined:\n   const basic_regex<char_type, traits>& re;\n   // the expression's traits class:\n   const ::boost::regex_traits_wrapper<traits>& traits_inst;\n   // the next state in the machine being matched:\n   const re_syntax_base* pstate;\n   // matching flags in use:\n   match_flag_type m_match_flags;\n   // how many states we have examined so far:\n   std::ptrdiff_t state_count;\n   // max number of states to examine before giving up:\n   std::ptrdiff_t max_state_count;\n   // whether we should ignore case or not:\n   bool icase;\n   // set to true when (position == last), indicates that we may have a partial match:\n   bool m_has_partial_match;\n   // set to true whenever we get a match:\n   bool m_has_found_match;\n   // set to true whenever we're inside an independent sub-expression:\n   bool m_independent;\n   // the current repeat being examined:\n   repeater_count<BidiIterator>* next_count;\n   // the first repeat being examined (top of linked list):\n   repeater_count<BidiIterator> rep_obj;\n   // the mask to pass when matching word boundaries:\n   typename traits::char_class_type m_word_mask;\n   // the bitmask to use when determining whether a match_any matches a newline or not:\n   unsigned char match_any_mask;\n   // recursion information:\n   std::vector<recursion_info<results_type> > recursion_stack;\n   //\n   // additional members for non-recursive version:\n   //\n   typedef bool (self_type::*unwind_proc_type)(bool);\n\n   void extend_stack();\n   bool unwind(bool);\n   bool unwind_end(bool);\n   bool unwind_paren(bool);\n   bool unwind_recursion_stopper(bool);\n   bool unwind_assertion(bool);\n   bool unwind_alt(bool);\n   bool unwind_repeater_counter(bool);\n   bool unwind_extra_block(bool);\n   bool unwind_greedy_single_repeat(bool);\n   bool unwind_slow_dot_repeat(bool);\n   bool unwind_fast_dot_repeat(bool);\n   bool unwind_char_repeat(bool);\n   bool unwind_short_set_repeat(bool);\n   bool unwind_long_set_repeat(bool);\n   bool unwind_non_greedy_repeat(bool);\n   bool unwind_recursion(bool);\n   bool unwind_recursion_pop(bool);\n   bool unwind_commit(bool);\n   bool unwind_then(bool);\n   bool unwind_case(bool);\n   void destroy_single_repeat();\n   void push_matched_paren(int index, const sub_match<BidiIterator>& sub);\n   void push_recursion_stopper();\n   void push_assertion(const re_syntax_base* ps, bool positive);\n   void push_alt(const re_syntax_base* ps);\n   void push_repeater_count(int i, repeater_count<BidiIterator>** s);\n   void push_single_repeat(std::size_t c, const re_repeat* r, BidiIterator last_position, int state_id);\n   void push_non_greedy_repeat(const re_syntax_base* ps);\n   void push_recursion(int idx, const re_syntax_base* p, results_type* presults, results_type* presults2);\n   void push_recursion_pop();\n   void push_case_change(bool);\n\n   // pointer to base of stack:\n   saved_state* m_stack_base;\n   // pointer to current stack position:\n   saved_state* m_backup_state;\n   // how many memory blocks have we used up?:\n   unsigned used_block_count;\n   // determines what value to return when unwinding from recursion,\n   // allows for mixed recursive/non-recursive algorithm:\n   bool m_recursive_result;\n   // We have unwound to a lookahead/lookbehind, used by COMMIT/PRUNE/SKIP:\n   bool m_unwound_lookahead;\n   // We have unwound to an alternative, used by THEN:\n   bool m_unwound_alt;\n   // We are unwinding a commit - used by independent subs to determine whether to stop there or carry on unwinding:\n   //bool m_unwind_commit;\n   // Recursion limit:\n   unsigned m_recursions;\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#if BOOST_REGEX_MSVC >= 1800\n#pragma warning(disable:26495)\n#endif\n#endif\n   // these operations aren't allowed, so are declared private,\n   // bodies are provided to keep explicit-instantiation requests happy:\n   perl_matcher& operator=(const perl_matcher&)\n   {\n      return *this;\n   }\n   perl_matcher(const perl_matcher& that)\n      : m_result(that.m_result), re(that.re), traits_inst(that.traits_inst), rep_obj(0) {}\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n};\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n\n} // namespace boost\n\n//\n// include the implementation of perl_matcher:\n//\n#include <boost/regex/v5/perl_matcher_non_recursive.hpp>\n// this one has to be last:\n#include <boost/regex/v5/perl_matcher_common.hpp>\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/perl_matcher_common.hpp",
    "content": "/*\n *\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         perl_matcher_common.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Definitions of perl_matcher member functions that are \n  *                common to both the recursive and non-recursive versions.\n  */\n\n#ifndef BOOST_REGEX_V5_PERL_MATCHER_COMMON_HPP\n#define BOOST_REGEX_V5_PERL_MATCHER_COMMON_HPP\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#pragma warning(disable:4459)\n#if BOOST_REGEX_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#pragma warning(disable:26812)\n#endif\n   template <class BidiIterator, class Allocator, class traits>\nvoid perl_matcher<BidiIterator, Allocator, traits>::construct_init(const basic_regex<char_type, traits>& e, match_flag_type f)\n{ \n   typedef typename std::iterator_traits<BidiIterator>::iterator_category category;\n   typedef typename basic_regex<char_type, traits>::flag_type expression_flag_type;\n   \n   if(e.empty())\n   {\n      // precondition failure: e is not a valid regex.\n      std::invalid_argument ex(\"Invalid regular expression object\");\n#ifndef BOOST_REGEX_STANDALONE\n      boost::throw_exception(ex);\n#else\n      throw e;\n#endif\n   }\n   pstate = 0;\n   m_match_flags = f;\n   estimate_max_state_count(static_cast<category*>(0));\n   expression_flag_type re_f = re.flags();\n   icase = re_f & regex_constants::icase;\n   if(!(m_match_flags & (match_perl|match_posix)))\n   {\n      if((re_f & (regbase::main_option_type|regbase::no_perl_ex)) == 0)\n         m_match_flags |= match_perl;\n      else if((re_f & (regbase::main_option_type|regbase::emacs_ex)) == (regbase::basic_syntax_group|regbase::emacs_ex))\n         m_match_flags |= match_perl;\n      else if((re_f & (regbase::main_option_type|regbase::literal)) == (regbase::literal))\n         m_match_flags |= match_perl;\n      else\n         m_match_flags |= match_posix;\n   }\n   if(m_match_flags & match_posix)\n   {\n      m_temp_match.reset(new match_results<BidiIterator, Allocator>());\n      m_presult = m_temp_match.get();\n   }\n   else\n      m_presult = &m_result;\n   m_stack_base = 0;\n   m_backup_state = 0;\n   // find the value to use for matching word boundaries:\n   m_word_mask = re.get_data().m_word_mask; \n   // find bitmask to use for matching '.':\n   match_any_mask = static_cast<unsigned char>((f & match_not_dot_newline) ? BOOST_REGEX_DETAIL_NS::test_not_newline : BOOST_REGEX_DETAIL_NS::test_newline);\n   // Disable match_any if requested in the state machine:\n   if(e.get_data().m_disable_match_any)\n      m_match_flags &= regex_constants::match_not_any;\n}\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n\ntemplate <class BidiIterator, class Allocator, class traits>\nvoid perl_matcher<BidiIterator, Allocator, traits>::estimate_max_state_count(std::random_access_iterator_tag*)\n{\n   //\n   // How many states should we allow our machine to visit before giving up?\n   // This is a heuristic: it takes the greater of O(N^2) and O(NS^2)\n   // where N is the length of the string, and S is the number of states\n   // in the machine.  It's tempting to up this to O(N^2S) or even O(N^2S^2)\n   // but these take unreasonably amounts of time to bale out in pathological\n   // cases.\n   //\n   // Calculate NS^2 first:\n   //\n   static const std::ptrdiff_t k = 100000;\n   std::ptrdiff_t dist = std::distance(base, last);\n   if(dist == 0)\n      dist = 1;\n   std::ptrdiff_t states = re.size();\n   if(states == 0)\n      states = 1;\n   if ((std::numeric_limits<std::ptrdiff_t>::max)() / states < states)\n   {\n      max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);\n      return;\n   }\n   states *= states;\n   if((std::numeric_limits<std::ptrdiff_t>::max)() / dist < states)\n   {\n      max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);\n      return;\n   }\n   states *= dist;\n   if((std::numeric_limits<std::ptrdiff_t>::max)() - k < states)\n   {\n      max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);\n      return;\n   }\n   states += k;\n\n   max_state_count = states;\n\n   //\n   // Now calculate N^2:\n   //\n   states = dist;\n   if((std::numeric_limits<std::ptrdiff_t>::max)() / dist < states)\n   {\n      max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);\n      return;\n   }\n   states *= dist;\n   if((std::numeric_limits<std::ptrdiff_t>::max)() - k < states)\n   {\n      max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);\n      return;\n   }\n   states += k;\n   //\n   // N^2 can be a very large number indeed, to prevent things getting out\n   // of control, cap the max states:\n   //\n   if(states > BOOST_REGEX_MAX_STATE_COUNT)\n      states = BOOST_REGEX_MAX_STATE_COUNT;\n   //\n   // If (the possibly capped) N^2 is larger than our first estimate,\n   // use this instead:\n   //\n   if(states > max_state_count)\n      max_state_count = states;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::estimate_max_state_count(void*)\n{\n   // we don't know how long the sequence is:\n   max_state_count = BOOST_REGEX_MAX_STATE_COUNT;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline bool perl_matcher<BidiIterator, Allocator, traits>::match()\n{\n   return match_imp();\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_imp()\n{\n   // initialise our stack if we are non-recursive:\n   save_state_init init(&m_stack_base, &m_backup_state);\n   used_block_count = BOOST_REGEX_MAX_BLOCKS;\n#if !defined(BOOST_NO_EXCEPTIONS)\n   try{\n#endif\n\n   // reset our state machine:\n   position = base;\n   search_base = base;\n   state_count = 0;\n   m_match_flags |= regex_constants::match_all;\n   m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast<typename results_type::size_type>(1u + re.mark_count()), search_base, last);\n   m_presult->set_base(base);\n   m_presult->set_named_subs(this->re.get_named_subs());\n   if(m_match_flags & match_posix)\n      m_result = *m_presult;\n   verify_options(re.flags(), m_match_flags);\n   if(0 == match_prefix())\n      return false;\n   return (m_result[0].second == last) && (m_result[0].first == base);\n\n#if !defined(BOOST_NO_EXCEPTIONS)\n   }\n   catch(...)\n   {\n      // unwind all pushed states, apart from anything else this\n      // ensures that all the states are correctly destructed\n      // not just the memory freed.\n      while(unwind(true)){}\n      throw;\n   }\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline bool perl_matcher<BidiIterator, Allocator, traits>::find()\n{\n   return find_imp();\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_imp()\n{\n   static matcher_proc_type const s_find_vtable[7] = \n   {\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_any,\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_word,\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_line,\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_buf,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_prefix,\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_lit,\n      &perl_matcher<BidiIterator, Allocator, traits>::find_restart_lit,\n   };\n\n   // initialise our stack if we are non-recursive:\n   save_state_init init(&m_stack_base, &m_backup_state);\n   used_block_count = BOOST_REGEX_MAX_BLOCKS;\n#if !defined(BOOST_NO_EXCEPTIONS)\n   try{\n#endif\n\n   state_count = 0;\n   if((m_match_flags & regex_constants::match_init) == 0)\n   {\n      // reset our state machine:\n      search_base = position = base;\n      pstate = re.get_first_state();\n      m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast<typename results_type::size_type>(1u + re.mark_count()), base, last);\n      m_presult->set_base(base);\n      m_presult->set_named_subs(this->re.get_named_subs());\n      m_match_flags |= regex_constants::match_init;\n   }\n   else\n   {\n      // start again:\n      search_base = position = m_result[0].second;\n      // If last match was null and match_not_null was not set then increment\n      // our start position, otherwise we go into an infinite loop:\n      if(((m_match_flags & match_not_null) == 0) && (m_result.length() == 0))\n      {\n         if(position == last)\n            return false;\n         else \n            ++position;\n      }\n      // reset $` start:\n      m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast<typename results_type::size_type>(1u + re.mark_count()), search_base, last);\n      //if((base != search_base) && (base == backstop))\n      //   m_match_flags |= match_prev_avail;\n   }\n   if(m_match_flags & match_posix)\n   {\n      m_result.set_size(static_cast<typename results_type::size_type>(1u + re.mark_count()), base, last);\n      m_result.set_base(base);\n   }\n\n   verify_options(re.flags(), m_match_flags);\n   // find out what kind of expression we have:\n   unsigned type = (m_match_flags & match_continuous) ? \n      static_cast<unsigned int>(regbase::restart_continue) \n         : static_cast<unsigned int>(re.get_restart_type());\n\n   // call the appropriate search routine:\n   matcher_proc_type proc = s_find_vtable[type];\n   return (this->*proc)();\n\n#if !defined(BOOST_NO_EXCEPTIONS)\n   }\n   catch(...)\n   {\n      // unwind all pushed states, apart from anything else this\n      // ensures that all the states are correctly destructed\n      // not just the memory freed.\n      while(unwind(true)){}\n      throw;\n   }\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_prefix()\n{\n   m_has_partial_match = false;\n   m_has_found_match = false;\n   pstate = re.get_first_state();\n   m_presult->set_first(position);\n   restart = position;\n   match_all_states();\n   if(!m_has_found_match && m_has_partial_match && (m_match_flags & match_partial))\n   {\n      m_has_found_match = true;\n      m_presult->set_second(last, 0, false);\n      position = last;\n      if((m_match_flags & match_posix) == match_posix)\n      {\n         m_result.maybe_assign(*m_presult);\n      }\n   }\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   if(m_has_found_match && (match_extra & m_match_flags))\n   {\n      //\n      // we have a match, reverse the capture information:\n      //\n      for(unsigned i = 0; i < m_presult->size(); ++i)\n      {\n         typename sub_match<BidiIterator>::capture_sequence_type & seq = ((*m_presult)[i]).get_captures();\n         std::reverse(seq.begin(), seq.end());\n      }\n   }\n#endif\n   if(!m_has_found_match)\n      position = restart; // reset search postion\n   return m_has_found_match;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_literal()\n{\n   unsigned int len = static_cast<const re_literal*>(pstate)->length;\n   const char_type* what = reinterpret_cast<const char_type*>(static_cast<const re_literal*>(pstate) + 1);\n   //\n   // compare string with what we stored in\n   // our records:\n   for(unsigned int i = 0; i < len; ++i, ++position)\n   {\n      if((position == last) || (traits_inst.translate(*position, icase) != what[i]))\n         return false;\n   }\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_start_line()\n{\n   if(position == backstop)\n   {\n      if((m_match_flags & match_prev_avail) == 0)\n      {\n         if((m_match_flags & match_not_bol) == 0)\n         {\n            pstate = pstate->next.p;\n            return true;\n         }\n         return false;\n      }\n   }\n   else if(m_match_flags & match_single_line)\n      return false;\n\n   // check the previous value character:\n   BidiIterator t(position);\n   --t;\n   if(position != last)\n   {\n      if(is_separator(*t) && !((*t == static_cast<char_type>('\\r')) && (*position == static_cast<char_type>('\\n'))) )\n      {\n         pstate = pstate->next.p;\n         return true;\n      }\n   }\n   else if(is_separator(*t))\n   {\n      pstate = pstate->next.p;\n      return true;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_end_line()\n{\n   if(position != last)\n   {\n      if(m_match_flags & match_single_line)\n         return false;\n      // we're not yet at the end so *first is always valid:\n      if(is_separator(*position))\n      {\n         if((position != backstop) || (m_match_flags & match_prev_avail))\n         {\n            // check that we're not in the middle of \\r\\n sequence\n            BidiIterator t(position);\n            --t;\n            if((*t == static_cast<char_type>('\\r')) && (*position == static_cast<char_type>('\\n')))\n            {\n               return false;\n            }\n         }\n         pstate = pstate->next.p;\n         return true;\n      }\n   }\n   else if((m_match_flags & match_not_eol) == 0)\n   {\n      pstate = pstate->next.p;\n      return true;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_wild()\n{\n   if(position == last) \n      return false;\n   if(is_separator(*position) && ((match_any_mask & static_cast<const re_dot*>(pstate)->mask) == 0))\n      return false;\n   if((*position == char_type(0)) && (m_match_flags & match_not_dot_null))\n      return false;\n   pstate = pstate->next.p;\n   ++position;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_word_boundary()\n{\n   bool b; // indcates whether next character is a word character\n   if(position != last)\n   {\n      // prev and this character must be opposites:\n      b = traits_inst.isctype(*position, m_word_mask);\n   }\n   else\n   {\n      if (m_match_flags & match_not_eow)\n         return false;\n      b = false;\n   }\n   if((position == backstop) && ((m_match_flags & match_prev_avail) == 0))\n   {\n      if(m_match_flags & match_not_bow)\n         return false;\n      else\n         b ^= false;\n   }\n   else\n   {\n      --position;\n      b ^= traits_inst.isctype(*position, m_word_mask);\n      ++position;\n   }\n   if(b)\n   {\n      pstate = pstate->next.p;\n      return true;\n   }\n   return false; // no match if we get to here...\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_within_word()\n{\n   bool b = !match_word_boundary();\n   if(b)\n      pstate = pstate->next.p;\n   return b;\n   /*\n   if(position == last)\n      return false;\n   // both prev and this character must be m_word_mask:\n   bool prev = traits_inst.isctype(*position, m_word_mask);\n   {\n      bool b;\n      if((position == backstop) && ((m_match_flags & match_prev_avail) == 0)) \n         return false;\n      else\n      {\n         --position;\n         b = traits_inst.isctype(*position, m_word_mask);\n         ++position;\n      }\n      if(b == prev)\n      {\n         pstate = pstate->next.p;\n         return true;\n      }\n   }\n   return false;\n   */\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_word_start()\n{\n   if(position == last)\n      return false; // can't be starting a word if we're already at the end of input\n   if(!traits_inst.isctype(*position, m_word_mask))\n      return false; // next character isn't a word character\n   if((position == backstop) && ((m_match_flags & match_prev_avail) == 0))\n   {\n      if(m_match_flags & match_not_bow)\n         return false; // no previous input\n   }\n   else\n   {\n      // otherwise inside buffer:\n      BidiIterator t(position);\n      --t;\n      if(traits_inst.isctype(*t, m_word_mask))\n         return false; // previous character not non-word\n   }\n   // OK we have a match:\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_word_end()\n{\n   if((position == backstop) && ((m_match_flags & match_prev_avail) == 0))\n      return false;  // start of buffer can't be end of word\n   BidiIterator t(position);\n   --t;\n   if(traits_inst.isctype(*t, m_word_mask) == false)\n      return false;  // previous character wasn't a word character\n\n   if(position == last)\n   {\n      if(m_match_flags & match_not_eow)\n         return false; // end of buffer but not end of word\n   }\n   else\n   {\n      // otherwise inside buffer:\n      if(traits_inst.isctype(*position, m_word_mask))\n         return false; // next character is a word character\n   }\n   pstate = pstate->next.p;\n   return true;      // if we fall through to here then we've succeeded\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_buffer_start()\n{\n   if((position != backstop) || (m_match_flags & match_not_bob))\n      return false;\n   // OK match:\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_buffer_end()\n{\n   if((position != last) || (m_match_flags & match_not_eob))\n      return false;\n   // OK match:\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_backref()\n{\n   //\n   // Compare with what we previously matched.\n   // Note that this succeeds if the backref did not partisipate\n   // in the match, this is in line with ECMAScript, but not Perl\n   // or PCRE.\n   //\n   int index = static_cast<const re_brace*>(pstate)->index;\n   if(index >= hash_value_mask)\n   {\n      named_subexpressions::range_type r = re.get_data().equal_range(index);\n      BOOST_REGEX_ASSERT(r.first != r.second);\n      do\n      {\n         index = r.first->index;\n         ++r.first;\n      }while((r.first != r.second) && ((*m_presult)[index].matched != true));\n   }\n\n   if((m_match_flags & match_perl) && !(*m_presult)[index].matched)\n      return false;\n\n   BidiIterator i = (*m_presult)[index].first;\n   BidiIterator j = (*m_presult)[index].second;\n   while(i != j)\n   {\n      if((position == last) || (traits_inst.translate(*position, icase) != traits_inst.translate(*i, icase)))\n         return false;\n      ++i;\n      ++position;\n   }\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_long_set()\n{\n   typedef typename traits::char_class_type char_class_type;\n   // let the traits class do the work:\n   if(position == last)\n      return false;\n   BidiIterator t = re_is_set_member(position, last, static_cast<const re_set_long<char_class_type>*>(pstate), re.get_data(), icase);\n   if(t != position)\n   {\n      pstate = pstate->next.p;\n      position = t;\n      return true;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_set()\n{\n   if(position == last)\n      return false;\n   if(static_cast<const re_set*>(pstate)->_map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n   {\n      pstate = pstate->next.p;\n      ++position;\n      return true;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_jump()\n{\n   pstate = static_cast<const re_jump*>(pstate)->alt.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_combining()\n{\n   if(position == last)\n      return false;\n   if(is_combining(traits_inst.translate(*position, icase)))\n      return false;\n   ++position;\n   while((position != last) && is_combining(traits_inst.translate(*position, icase)))\n      ++position;\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_soft_buffer_end()\n{\n   if(m_match_flags & match_not_eob)\n      return false;\n   BidiIterator p(position);\n   while((p != last) && is_separator(traits_inst.translate(*p, icase)))++p;\n   if(p != last)\n      return false;\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_restart_continue()\n{\n   if(position == search_base)\n   {\n      pstate = pstate->next.p;\n      return true;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_backstep()\n{\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   if( ::boost::is_random_access_iterator<BidiIterator>::value)\n   {\n      std::ptrdiff_t maxlen = std::distance(backstop, position);\n      if(maxlen < static_cast<const re_brace*>(pstate)->index)\n         return false;\n      std::advance(position, -static_cast<const re_brace*>(pstate)->index);\n   }\n   else\n   {\n      int c = static_cast<const re_brace*>(pstate)->index;\n      while(c--)\n      {\n         if(position == backstop)\n            return false;\n         --position;\n      }\n   }\n   pstate = pstate->next.p;\n   return true;\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline bool perl_matcher<BidiIterator, Allocator, traits>::match_assert_backref()\n{\n   // return true if marked sub-expression N has been matched:\n   int index = static_cast<const re_brace*>(pstate)->index;\n   bool result = false;\n   if(index == 9999)\n   {\n      // Magic value for a (DEFINE) block:\n      return false;\n   }\n   else if(index > 0)\n   {\n      // Have we matched subexpression \"index\"?\n      // Check if index is a hash value:\n      if(index >= hash_value_mask)\n      {\n         named_subexpressions::range_type r = re.get_data().equal_range(index);\n         while(r.first != r.second)\n         {\n            if((*m_presult)[r.first->index].matched)\n            {\n               result = true;\n               break;\n            }\n            ++r.first;\n         }\n      }\n      else\n      {\n         result = (*m_presult)[index].matched;\n      }\n      pstate = pstate->next.p;\n   }\n   else\n   {\n      // Have we recursed into subexpression \"index\"?\n      // If index == 0 then check for any recursion at all, otherwise for recursion to -index-1.\n      int idx = -(index+1);\n      if(idx >= hash_value_mask)\n      {\n         named_subexpressions::range_type r = re.get_data().equal_range(idx);\n         int stack_index = recursion_stack.empty() ? -1 : recursion_stack.back().idx;\n         while(r.first != r.second)\n         {\n            result |= (stack_index == r.first->index);\n            if(result)break;\n            ++r.first;\n         }\n      }\n      else\n      {\n         result = !recursion_stack.empty() && ((recursion_stack.back().idx == idx) || (index == 0));\n      }\n      pstate = pstate->next.p;\n   }\n   return result;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_fail()\n{\n   // Just force a backtrack:\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_accept()\n{\n   if(!recursion_stack.empty())\n   {\n      return skip_until_paren(recursion_stack.back().idx);\n   }\n   else\n   {\n      return skip_until_paren(INT_MAX);\n   }\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_restart_any()\n{\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   const unsigned char* _map = re.get_map();\n   while(true)\n   {\n      // skip everything we can't match:\n      while((position != last) && !can_start(*position, _map, (unsigned char)mask_any) )\n         ++position;\n      if(position == last)\n      {\n         // run out of characters, try a null match if possible:\n         if(re.can_be_null())\n            return match_prefix();\n         break;\n      }\n      // now try and obtain a match:\n      if(match_prefix())\n         return true;\n      if(position == last)\n         return false;\n      ++position;\n   }\n   return false;\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_restart_word()\n{\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n   // do search optimised for word starts:\n   const unsigned char* _map = re.get_map();\n   if((m_match_flags & match_prev_avail) || (position != base))\n      --position;\n   else if(match_prefix())\n      return true;\n   do\n   {\n      while((position != last) && traits_inst.isctype(*position, m_word_mask))\n         ++position;\n      while((position != last) && !traits_inst.isctype(*position, m_word_mask))\n         ++position;\n      if(position == last)\n         break;\n\n      if(can_start(*position, _map, (unsigned char)mask_any) )\n      {\n         if(match_prefix())\n            return true;\n      }\n      if(position == last)\n         break;\n   } while(true);\n   return false;\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_restart_line()\n{\n   // do search optimised for line starts:\n   const unsigned char* _map = re.get_map();\n   if(match_prefix())\n      return true;\n   while(position != last)\n   {\n      while((position != last) && !is_separator(*position))\n         ++position;\n      if(position == last)\n         return false;\n      ++position;\n      if(position == last)\n      {\n         if(re.can_be_null() && match_prefix())\n            return true;\n         return false;\n      }\n\n      if( can_start(*position, _map, (unsigned char)mask_any) )\n      {\n         if(match_prefix())\n            return true;\n      }\n      if(position == last)\n         return false;\n      //++position;\n   }\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_restart_buf()\n{\n   if((position == base) && ((m_match_flags & match_not_bob) == 0))\n      return match_prefix();\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::find_restart_lit()\n{\n   return false;\n}\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\n} // namespace boost\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/perl_matcher_non_recursive.hpp",
    "content": "/*\n *\n * Copyright (c) 2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         perl_matcher_common.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Definitions of perl_matcher member functions that are \n  *                specific to the non-recursive implementation.\n  */\n\n#ifndef BOOST_REGEX_V5_PERL_MATCHER_NON_RECURSIVE_HPP\n#define BOOST_REGEX_V5_PERL_MATCHER_NON_RECURSIVE_HPP\n\n#include <boost/regex/v5/mem_block_cache.hpp>\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#  pragma warning(disable: 4706 4459)\n#if BOOST_REGEX_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class T>\ninline void inplace_destroy(T* p)\n{\n   (void)p;  // warning suppression\n   p->~T();\n}\n\nstruct saved_state\n{\n   union{\n      unsigned int state_id;\n      // this padding ensures correct alignment on 64-bit platforms:\n      std::size_t padding1;\n      std::ptrdiff_t padding2;\n      void* padding3;\n   };\n   saved_state(unsigned i) : state_id(i) {}\n};\n\ntemplate <class BidiIterator>\nstruct saved_matched_paren : public saved_state\n{\n   int index;\n   sub_match<BidiIterator> sub;\n   saved_matched_paren(int i, const sub_match<BidiIterator>& s) : saved_state(1), index(i), sub(s){}\n};\n\ntemplate <class BidiIterator>\nstruct saved_position : public saved_state\n{\n   const re_syntax_base* pstate;\n   BidiIterator position;\n   saved_position(const re_syntax_base* ps, BidiIterator pos, int i) : saved_state(i), pstate(ps), position(pos){}\n};\n\ntemplate <class BidiIterator>\nstruct saved_assertion : public saved_position<BidiIterator>\n{\n   bool positive;\n   saved_assertion(bool p, const re_syntax_base* ps, BidiIterator pos) \n      : saved_position<BidiIterator>(ps, pos, saved_type_assertion), positive(p){}\n};\n\ntemplate <class BidiIterator>\nstruct saved_repeater : public saved_state\n{\n   repeater_count<BidiIterator> count;\n   saved_repeater(int i, repeater_count<BidiIterator>** s, BidiIterator start, int current_recursion_id)\n      : saved_state(saved_state_repeater_count), count(i, s, start, current_recursion_id){}\n};\n\nstruct saved_extra_block : public saved_state\n{\n   saved_state *base, *end;\n   saved_extra_block(saved_state* b, saved_state* e) \n      : saved_state(saved_state_extra_block), base(b), end(e) {}\n};\n\nstruct save_state_init\n{\n   saved_state** stack;\n   save_state_init(saved_state** base, saved_state** end)\n      : stack(base)\n   {\n      *base = static_cast<saved_state*>(get_mem_block());\n      *end = reinterpret_cast<saved_state*>(reinterpret_cast<char*>(*base)+BOOST_REGEX_BLOCKSIZE);\n      --(*end);\n      (void) new (*end)saved_state(0);\n      BOOST_REGEX_ASSERT(*end > *base);\n   }\n   ~save_state_init()\n   {\n      put_mem_block(*stack);\n      *stack = 0;\n   }\n};\n\ntemplate <class BidiIterator>\nstruct saved_single_repeat : public saved_state\n{\n   std::size_t count;\n   const re_repeat* rep;\n   BidiIterator last_position;\n   saved_single_repeat(std::size_t c, const re_repeat* r, BidiIterator lp, int arg_id) \n      : saved_state(arg_id), count(c), rep(r), last_position(lp){}\n};\n\ntemplate <class Results>\nstruct saved_recursion : public saved_state\n{\n   saved_recursion(int idx, const re_syntax_base* p, Results* pr, Results* pr2) \n      : saved_state(14), recursion_id(idx), preturn_address(p), internal_results(*pr), prior_results(*pr2) {}\n   int recursion_id;\n   const re_syntax_base* preturn_address;\n   Results internal_results, prior_results;\n};\n\nstruct saved_change_case : public saved_state\n{\n   bool icase;\n   saved_change_case(bool c) : saved_state(18), icase(c) {}\n};\n\nstruct incrementer\n{\n   incrementer(unsigned* pu) : m_pu(pu) { ++*m_pu; }\n   ~incrementer() { --*m_pu; }\n   bool operator > (unsigned i) { return *m_pu > i; }\nprivate:\n   unsigned* m_pu;\n};\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_all_states()\n{\n   static matcher_proc_type const s_match_vtable[34] = \n   {\n      (&perl_matcher<BidiIterator, Allocator, traits>::match_startmark),\n      &perl_matcher<BidiIterator, Allocator, traits>::match_endmark,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_literal,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_start_line,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_end_line,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_wild,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_match,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_word_boundary,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_within_word,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_word_start,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_word_end,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_buffer_start,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_buffer_end,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_backref,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_long_set,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_set,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_jump,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_alt,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_rep,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_combining,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_soft_buffer_end,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_restart_continue,\n      // Although this next line *should* be evaluated at compile time, in practice\n      // some compilers (VC++) emit run-time initialisation which breaks thread\n      // safety, so use a dispatch function instead:\n      //(::boost::is_random_access_iterator<BidiIterator>::value ? &perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_fast : &perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_slow),\n      &perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_dispatch,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_char_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_set_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_long_set_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_backstep,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_assert_backref,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_toggle_case,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_recursion,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_fail,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_accept,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_commit,\n      &perl_matcher<BidiIterator, Allocator, traits>::match_then,\n   };\n   incrementer inc(&m_recursions);\n   if(inc > 80)\n      raise_error(traits_inst, regex_constants::error_complexity);\n   push_recursion_stopper();\n   do{\n      while(pstate)\n      {\n         matcher_proc_type proc = s_match_vtable[pstate->type];\n         ++state_count;\n         if(!(this->*proc)())\n         {\n            if(state_count > max_state_count)\n               raise_error(traits_inst, regex_constants::error_complexity);\n            if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n               m_has_partial_match = true;\n            bool successful_unwind = unwind(false);\n            if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n               m_has_partial_match = true;\n            if(!successful_unwind)\n               return m_recursive_result;\n         }\n      }\n   }while(unwind(true));\n   return m_recursive_result;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nvoid perl_matcher<BidiIterator, Allocator, traits>::extend_stack()\n{\n   if(used_block_count)\n   {\n      --used_block_count;\n      saved_state* stack_base;\n      saved_state* backup_state;\n      stack_base = static_cast<saved_state*>(get_mem_block());\n      backup_state = reinterpret_cast<saved_state*>(reinterpret_cast<char*>(stack_base)+BOOST_REGEX_BLOCKSIZE);\n      saved_extra_block* block = static_cast<saved_extra_block*>(backup_state);\n      --block;\n      (void) new (block) saved_extra_block(m_stack_base, m_backup_state);\n      m_stack_base = stack_base;\n      m_backup_state = block;\n   }\n   else\n      raise_error(traits_inst, regex_constants::error_stack);\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_matched_paren(int index, const sub_match<BidiIterator>& sub)\n{\n   //BOOST_REGEX_ASSERT(index);\n   saved_matched_paren<BidiIterator>* pmp = static_cast<saved_matched_paren<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_matched_paren<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_matched_paren<BidiIterator>(index, sub);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_case_change(bool c)\n{\n   //BOOST_REGEX_ASSERT(index);\n   saved_change_case* pmp = static_cast<saved_change_case*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_change_case*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_change_case(c);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_recursion_stopper()\n{\n   saved_state* pmp = m_backup_state;\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = m_backup_state;\n      --pmp;\n   }\n   (void) new (pmp)saved_state(saved_type_recurse);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_assertion(const re_syntax_base* ps, bool positive)\n{\n   saved_assertion<BidiIterator>* pmp = static_cast<saved_assertion<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_assertion<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_assertion<BidiIterator>(positive, ps, position);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_alt(const re_syntax_base* ps)\n{\n   saved_position<BidiIterator>* pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_position<BidiIterator>(ps, position, saved_state_alt);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_non_greedy_repeat(const re_syntax_base* ps)\n{\n   saved_position<BidiIterator>* pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_position<BidiIterator>(ps, position, saved_state_non_greedy_long_repeat);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_repeater_count(int i, repeater_count<BidiIterator>** s)\n{\n   saved_repeater<BidiIterator>* pmp = static_cast<saved_repeater<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_repeater<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_repeater<BidiIterator>(i, s, position, this->recursion_stack.empty() ? (INT_MIN + 3) : this->recursion_stack.back().idx);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_single_repeat(std::size_t c, const re_repeat* r, BidiIterator last_position, int state_id)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_single_repeat<BidiIterator>(c, r, last_position, state_id);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::push_recursion(int idx, const re_syntax_base* p, results_type* presults, results_type* presults2)\n{\n   saved_recursion<results_type>* pmp = static_cast<saved_recursion<results_type>*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_recursion<results_type>*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_recursion<results_type>(idx, p, presults, presults2);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_toggle_case()\n{\n   // change our case sensitivity:\n   push_case_change(this->icase);\n   this->icase = static_cast<const re_case*>(pstate)->icase;\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_startmark()\n{\n   int index = static_cast<const re_brace*>(pstate)->index;\n   icase = static_cast<const re_brace*>(pstate)->icase;\n   switch(index)\n   {\n   case 0:\n      pstate = pstate->next.p;\n      break;\n   case -1:\n   case -2:\n      {\n         // forward lookahead assert:\n         const re_syntax_base* next_pstate = static_cast<const re_jump*>(pstate->next.p)->alt.p->next.p;\n         pstate = pstate->next.p->next.p;\n         push_assertion(next_pstate, index == -1);\n         break;\n      }\n   case -3:\n      {\n         // independent sub-expression, currently this is always recursive:\n         bool old_independent = m_independent;\n         m_independent = true;\n         const re_syntax_base* next_pstate = static_cast<const re_jump*>(pstate->next.p)->alt.p->next.p;\n         pstate = pstate->next.p->next.p;\n         bool r = false;\n#if !defined(BOOST_NO_EXCEPTIONS)\n      try{\n#endif\n         r = match_all_states();\n         if(!r && !m_independent)\n         {\n            // Must be unwinding from a COMMIT/SKIP/PRUNE and the independent \n            // sub failed, need to unwind everything else:\n            while (m_backup_state->state_id)\n               unwind(false);\n            return false;\n         }\n#if !defined(BOOST_NO_EXCEPTIONS)\n      }\n      catch(...)\n      {\n         pstate = next_pstate;\n         // unwind all pushed states, apart from anything else this\n         // ensures that all the states are correctly destructed\n         // not just the memory freed.\n         while(unwind(true)) {}\n         throw;\n      }\n#endif\n      pstate = next_pstate;\n      m_independent = old_independent;\n#ifdef BOOST_REGEX_MATCH_EXTRA\n         if(r && (m_match_flags & match_extra))\n         {\n            //\n            // our captures have been stored in *m_presult\n            // we need to unpack them, and insert them\n            // back in the right order when we unwind the stack:\n            //\n            match_results<BidiIterator, Allocator> temp_match(*m_presult);\n            unsigned i;\n            for(i = 0; i < temp_match.size(); ++i)\n               (*m_presult)[i].get_captures().clear();\n            // match everything else:\n#if !defined(BOOST_NO_EXCEPTIONS)\n            try{\n#endif\n               r = match_all_states();\n#if !defined(BOOST_NO_EXCEPTIONS)\n            }\n            catch(...)\n            {\n               pstate = next_pstate;\n               // unwind all pushed states, apart from anything else this\n               // ensures that all the states are correctly destructed\n               // not just the memory freed.\n               while(unwind(true)) {}\n               throw;\n            }\n#endif\n         // now place the stored captures back:\n            for(i = 0; i < temp_match.size(); ++i)\n            {\n               typedef typename sub_match<BidiIterator>::capture_sequence_type seq;\n               seq& s1 = (*m_presult)[i].get_captures();\n               const seq& s2 = temp_match[i].captures();\n               s1.insert(\n                  s1.end(), \n                  s2.begin(), \n                  s2.end());\n            }\n         }\n#endif\n         return r;\n      }\n   case -4:\n      {\n      // conditional expression:\n      const re_alt* alt = static_cast<const re_alt*>(pstate->next.p);\n      BOOST_REGEX_ASSERT(alt->type == syntax_element_alt);\n      pstate = alt->next.p;\n      if(pstate->type == syntax_element_assert_backref)\n      {\n         if(!match_assert_backref())\n            pstate = alt->alt.p;\n         break;\n      }\n      else\n      {\n         // zero width assertion, have to match this recursively:\n         BOOST_REGEX_ASSERT(pstate->type == syntax_element_startmark);\n         bool negated = static_cast<const re_brace*>(pstate)->index == -2;\n         BidiIterator saved_position = position;\n         const re_syntax_base* next_pstate = static_cast<const re_jump*>(pstate->next.p)->alt.p->next.p;\n         pstate = pstate->next.p->next.p;\n#if !defined(BOOST_NO_EXCEPTIONS)\n         try{\n#endif\n            bool r = match_all_states();\n            position = saved_position;\n            if(negated)\n               r = !r;\n            if(r)\n               pstate = next_pstate;\n            else\n               pstate = alt->alt.p;\n#if !defined(BOOST_NO_EXCEPTIONS)\n         }\n         catch(...)\n         {\n            pstate = next_pstate;\n            // unwind all pushed states, apart from anything else this\n            // ensures that all the states are correctly destructed\n            // not just the memory freed.\n            while(unwind(true)){}\n            throw;\n         }\n#endif\n         break;\n      }\n      }\n   case -5:\n      {\n         push_matched_paren(0, (*m_presult)[0]);\n         m_presult->set_first(position, 0, true);\n         pstate = pstate->next.p;\n         break;\n      }\n   default:\n   {\n      BOOST_REGEX_ASSERT(index > 0);\n      if((m_match_flags & match_nosubs) == 0)\n      {\n         push_matched_paren(index, (*m_presult)[index]);\n         m_presult->set_first(position, index);\n      }\n      pstate = pstate->next.p;\n      break;\n   }\n   }\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_alt()\n{\n   bool take_first, take_second;\n   const re_alt* jmp = static_cast<const re_alt*>(pstate);\n\n   // find out which of these two alternatives we need to take:\n   if(position == last)\n   {\n      take_first = jmp->can_be_null & mask_take;\n      take_second = jmp->can_be_null & mask_skip;\n   }\n   else\n   {\n      take_first = can_start(*position, jmp->_map, (unsigned char)mask_take);\n      take_second = can_start(*position, jmp->_map, (unsigned char)mask_skip);\n  }\n\n   if(take_first)\n   {\n      // we can take the first alternative,\n      // see if we need to push next alternative:\n      if(take_second)\n      {\n         push_alt(jmp->alt.p);\n      }\n      pstate = pstate->next.p;\n      return true;\n   }\n   if(take_second)\n   {\n      pstate = jmp->alt.p;\n      return true;\n   }\n   return false;  // neither option is possible\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_rep()\n{\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127 4244)\n#endif\n#ifdef BOOST_BORLANDC\n#pragma option push -w-8008 -w-8066 -w-8004\n#endif\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n\n   // find out which of these two alternatives we need to take:\n   bool take_first, take_second;\n   if(position == last)\n   {\n      take_first = rep->can_be_null & mask_take;\n      take_second = rep->can_be_null & mask_skip;\n   }\n   else\n   {\n      take_first = can_start(*position, rep->_map, (unsigned char)mask_take);\n      take_second = can_start(*position, rep->_map, (unsigned char)mask_skip);\n   }\n\n   if((m_backup_state->state_id != saved_state_repeater_count) \n      || (static_cast<saved_repeater<BidiIterator>*>(m_backup_state)->count.get_id() != rep->state_id)\n      || (next_count->get_id() != rep->state_id))\n   {\n      // we're moving to a different repeat from the last\n      // one, so set up a counter object:\n      push_repeater_count(rep->state_id, &next_count);\n   }\n   //\n   // If we've had at least one repeat already, and the last one \n   // matched the NULL string then set the repeat count to\n   // maximum:\n   //\n   next_count->check_null_repeat(position, rep->max);\n\n   if(next_count->get_count() < rep->min)\n   {\n      // we must take the repeat:\n      if(take_first)\n      {\n         // increase the counter:\n         ++(*next_count);\n         pstate = rep->next.p;\n         return true;\n      }\n      return false;\n   }\n\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   if(greedy)\n   {\n      // try and take the repeat if we can:\n      if((next_count->get_count() < rep->max) && take_first)\n      {\n         if(take_second)\n         {\n            // store position in case we fail:\n            push_alt(rep->alt.p);\n         }\n         // increase the counter:\n         ++(*next_count);\n         pstate = rep->next.p;\n         return true;\n      }\n      else if(take_second)\n      {\n         pstate = rep->alt.p;\n         return true;\n      }\n      return false; // can't take anything, fail...\n   }\n   else // non-greedy\n   {\n      // try and skip the repeat if we can:\n      if(take_second)\n      {\n         if((next_count->get_count() < rep->max) && take_first)\n         {\n            // store position in case we fail:\n            push_non_greedy_repeat(rep->next.p);\n         }\n         pstate = rep->alt.p;\n         return true;\n      }\n      if((next_count->get_count() < rep->max) && take_first)\n      {\n         // increase the counter:\n         ++(*next_count);\n         pstate = rep->next.p;\n         return true;\n      }\n   }\n   return false;\n#ifdef BOOST_BORLANDC\n#pragma option pop\n#endif\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_slow()\n{\n   std::size_t count = 0;\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   re_syntax_base* psingle = rep->next.p;\n   // match compulsory repeats first:\n   while(count < rep->min)\n   {\n      pstate = psingle;\n      if(!match_wild())\n         return false;\n      ++count;\n   }\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   if(greedy)\n   {\n      // repeat for as long as we can:\n      while(count < rep->max)\n      {\n         pstate = psingle;\n         if(!match_wild())\n            break;\n         ++count;\n      }\n      // remember where we got to if this is a leading repeat:\n      if((rep->leading) && (count < rep->max))\n         restart = position;\n      // push backtrack info if available:\n      if(count - rep->min)\n         push_single_repeat(count, rep, position, saved_state_greedy_single_repeat);\n      // jump to next state:\n      pstate = rep->alt.p;\n      return true;\n   }\n   else\n   {\n      // non-greedy, push state and return true if we can skip:\n      if(count < rep->max)\n         push_single_repeat(count, rep, position, saved_state_rep_slow_dot);\n      pstate = rep->alt.p;\n      return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip);\n   }\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_dot_repeat_fast()\n{\n   if(m_match_flags & match_not_dot_null)\n      return match_dot_repeat_slow();\n   if((static_cast<const re_dot*>(pstate->next.p)->mask & match_any_mask) == 0)\n      return match_dot_repeat_slow();\n\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t count = static_cast<std::size_t>((std::min)(static_cast<std::size_t>(std::distance(position, last)), greedy ? rep->max : rep->min));\n   if(rep->min > count)\n   {\n      position = last;\n      return false;  // not enough text left to match\n   }\n   std::advance(position, count);\n\n   if(greedy)\n   {\n      if((rep->leading) && (count < rep->max))\n         restart = position;\n      // push backtrack info if available:\n      if(count - rep->min)\n         push_single_repeat(count, rep, position, saved_state_greedy_single_repeat);\n      // jump to next state:\n      pstate = rep->alt.p;\n      return true;\n   }\n   else\n   {\n      // non-greedy, push state and return true if we can skip:\n      if(count < rep->max)\n         push_single_repeat(count, rep, position, saved_state_rep_fast_dot);\n      pstate = rep->alt.p;\n      return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip);\n   }\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_char_repeat()\n{\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n#ifdef BOOST_BORLANDC\n#pragma option push -w-8008 -w-8066 -w-8004\n#endif\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   BOOST_REGEX_ASSERT(1 == static_cast<const re_literal*>(rep->next.p)->length);\n   const char_type what = *reinterpret_cast<const char_type*>(static_cast<const re_literal*>(rep->next.p) + 1);\n   std::size_t count = 0;\n   //\n   // start by working out how much we can skip:\n   //\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t desired = greedy ? rep->max : rep->min;\n   if(::boost::is_random_access_iterator<BidiIterator>::value)\n   {\n      BidiIterator end = position;\n      // Move end forward by \"desired\", preferably without using distance or advance if we can\n      // as these can be slow for some iterator types.\n      std::size_t len = (desired == (std::numeric_limits<std::size_t>::max)()) ? 0u : std::distance(position, last);\n      if(desired >= len)\n         end = last;\n      else\n         std::advance(end, desired);\n      BidiIterator origin(position);\n      while((position != end) && (traits_inst.translate(*position, icase) == what))\n      {\n         ++position;\n      }\n      count = (unsigned)std::distance(origin, position);\n   }\n   else\n   {\n      while((count < desired) && (position != last) && (traits_inst.translate(*position, icase) == what))\n      {\n         ++position;\n         ++count;\n      }\n   }\n\n   if(count < rep->min)\n      return false;\n\n   if(greedy)\n   {\n      if((rep->leading) && (count < rep->max))\n         restart = position;\n      // push backtrack info if available:\n      if(count - rep->min)\n         push_single_repeat(count, rep, position, saved_state_greedy_single_repeat);\n      // jump to next state:\n      pstate = rep->alt.p;\n      return true;\n   }\n   else\n   {\n      // non-greedy, push state and return true if we can skip:\n      if(count < rep->max)\n         push_single_repeat(count, rep, position, saved_state_rep_char);\n      pstate = rep->alt.p;\n      return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip);\n   }\n#ifdef BOOST_BORLANDC\n#pragma option pop\n#endif\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_set_repeat()\n{\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n#ifdef BOOST_BORLANDC\n#pragma option push -w-8008 -w-8066 -w-8004\n#endif\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   const unsigned char* map = static_cast<const re_set*>(rep->next.p)->_map;\n   std::size_t count = 0;\n   //\n   // start by working out how much we can skip:\n   //\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t desired = greedy ? rep->max : rep->min;\n   if(::boost::is_random_access_iterator<BidiIterator>::value)\n   {\n      BidiIterator end = position;\n      // Move end forward by \"desired\", preferably without using distance or advance if we can\n      // as these can be slow for some iterator types.\n      std::size_t len = (desired == (std::numeric_limits<std::size_t>::max)()) ? 0u : std::distance(position, last);\n      if(desired >= len)\n         end = last;\n      else\n         std::advance(end, desired);\n      BidiIterator origin(position);\n      while((position != end) && map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n      {\n         ++position;\n      }\n      count = (unsigned)std::distance(origin, position);\n   }\n   else\n   {\n      while((count < desired) && (position != last) && map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n      {\n         ++position;\n         ++count;\n      }\n   }\n\n   if(count < rep->min)\n      return false;\n\n   if(greedy)\n   {\n      if((rep->leading) && (count < rep->max))\n         restart = position;\n      // push backtrack info if available:\n      if(count - rep->min)\n         push_single_repeat(count, rep, position, saved_state_greedy_single_repeat);\n      // jump to next state:\n      pstate = rep->alt.p;\n      return true;\n   }\n   else\n   {\n      // non-greedy, push state and return true if we can skip:\n      if(count < rep->max)\n         push_single_repeat(count, rep, position, saved_state_rep_short_set);\n      pstate = rep->alt.p;\n      return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip);\n   }\n#ifdef BOOST_BORLANDC\n#pragma option pop\n#endif\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_long_set_repeat()\n{\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4127)\n#endif\n#ifdef BOOST_BORLANDC\n#pragma option push -w-8008 -w-8066 -w-8004\n#endif\n   typedef typename traits::char_class_type m_type;\n   const re_repeat* rep = static_cast<const re_repeat*>(pstate);\n   const re_set_long<m_type>* set = static_cast<const re_set_long<m_type>*>(pstate->next.p);\n   std::size_t count = 0;\n   //\n   // start by working out how much we can skip:\n   //\n   bool greedy = (rep->greedy) && (!(m_match_flags & regex_constants::match_any) || m_independent);   \n   std::size_t desired = greedy ? rep->max : rep->min;\n   if(::boost::is_random_access_iterator<BidiIterator>::value)\n   {\n      BidiIterator end = position;\n      // Move end forward by \"desired\", preferably without using distance or advance if we can\n      // as these can be slow for some iterator types.\n      std::size_t len = (desired == (std::numeric_limits<std::size_t>::max)()) ? 0u : std::distance(position, last);\n      if(desired >= len)\n         end = last;\n      else\n         std::advance(end, desired);\n      BidiIterator origin(position);\n      while((position != end) && (position != re_is_set_member(position, last, set, re.get_data(), icase)))\n      {\n         ++position;\n      }\n      count = (unsigned)std::distance(origin, position);\n   }\n   else\n   {\n      while((count < desired) && (position != last) && (position != re_is_set_member(position, last, set, re.get_data(), icase)))\n      {\n         ++position;\n         ++count;\n      }\n   }\n\n   if(count < rep->min)\n      return false;\n\n   if(greedy)\n   {\n      if((rep->leading) && (count < rep->max))\n         restart = position;\n      // push backtrack info if available:\n      if(count - rep->min)\n         push_single_repeat(count, rep, position, saved_state_greedy_single_repeat);\n      // jump to next state:\n      pstate = rep->alt.p;\n      return true;\n   }\n   else\n   {\n      // non-greedy, push state and return true if we can skip:\n      if(count < rep->max)\n         push_single_repeat(count, rep, position, saved_state_rep_long_set);\n      pstate = rep->alt.p;\n      return (position == last) ? (rep->can_be_null & mask_skip) : can_start(*position, rep->_map, mask_skip);\n   }\n#ifdef BOOST_BORLANDC\n#pragma option pop\n#endif\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_recursion()\n{\n   BOOST_REGEX_ASSERT(pstate->type == syntax_element_recurse);\n   //\n   // See if we've seen this recursion before at this location, if we have then\n   // we need to prevent infinite recursion:\n   //\n   for(typename std::vector<recursion_info<results_type> >::reverse_iterator i = recursion_stack.rbegin(); i != recursion_stack.rend(); ++i)\n   {\n      if(i->idx == static_cast<const re_brace*>(static_cast<const re_jump*>(pstate)->alt.p)->index)\n      {\n         if(i->location_of_start == position)\n            return false;\n         break;\n      }\n   }\n   //\n   // Backup call stack:\n   //\n   push_recursion_pop();\n   //\n   // Set new call stack:\n   //\n   if(recursion_stack.capacity() == 0)\n   {\n      recursion_stack.reserve(50);\n   }\n   recursion_stack.push_back(recursion_info<results_type>());\n   recursion_stack.back().preturn_address = pstate->next.p;\n   recursion_stack.back().results = *m_presult;\n   pstate = static_cast<const re_jump*>(pstate)->alt.p;\n   recursion_stack.back().idx = static_cast<const re_brace*>(pstate)->index;\n   recursion_stack.back().location_of_start = position;\n   //if(static_cast<const re_recurse*>(pstate)->state_id > 0)\n   {\n      push_repeater_count(-(2 + static_cast<const re_brace*>(pstate)->index), &next_count);\n   }\n\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_endmark()\n{\n   int index = static_cast<const re_brace*>(pstate)->index;\n   icase = static_cast<const re_brace*>(pstate)->icase;\n   if(index > 0)\n   {\n      if((m_match_flags & match_nosubs) == 0)\n      {\n         m_presult->set_second(position, index);\n      }\n      if(!recursion_stack.empty())\n      {\n         if(index == recursion_stack.back().idx)\n         {\n            pstate = recursion_stack.back().preturn_address;\n            *m_presult = recursion_stack.back().results;\n            push_recursion(recursion_stack.back().idx, recursion_stack.back().preturn_address, m_presult, &recursion_stack.back().results);\n            recursion_stack.pop_back();\n            push_repeater_count(-(2 + index), &next_count);\n         }\n      }\n   }\n   else if((index < 0) && (index != -4))\n   {\n      // matched forward lookahead:\n      pstate = 0;\n      return true;\n   }\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_match()\n{\n   if(!recursion_stack.empty())\n   {\n      BOOST_REGEX_ASSERT(0 == recursion_stack.back().idx);\n      pstate = recursion_stack.back().preturn_address;\n      push_recursion(recursion_stack.back().idx, recursion_stack.back().preturn_address, m_presult, &recursion_stack.back().results);\n      *m_presult = recursion_stack.back().results;\n      recursion_stack.pop_back();\n      return true;\n   }\n   if((m_match_flags & match_not_null) && (position == (*m_presult)[0].first))\n      return false;\n   if((m_match_flags & match_all) && (position != last))\n      return false;\n   if((m_match_flags & regex_constants::match_not_initial_null) && (position == search_base))\n      return false;\n   m_presult->set_second(position);\n   pstate = 0;\n   m_has_found_match = true;\n   if((m_match_flags & match_posix) == match_posix)\n   {\n      m_result.maybe_assign(*m_presult);\n      if((m_match_flags & match_any) == 0)\n         return false;\n   }\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   if(match_extra & m_match_flags)\n   {\n      for(unsigned i = 0; i < m_presult->size(); ++i)\n         if((*m_presult)[i].matched)\n            ((*m_presult)[i]).get_captures().push_back((*m_presult)[i]);\n   }\n#endif\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_commit()\n{\n   // Ideally we would just junk all the states that are on the stack,\n   // however we might not unwind correctly in that case, so for now,\n   // just mark that we don't backtrack into whatever is left (or rather\n   // we'll unwind it unconditionally without pausing to try other matches).\n\n   switch(static_cast<const re_commit*>(pstate)->action)\n   {\n   case commit_commit:\n      restart = last;\n      break;\n   case commit_skip:\n      if(base != position)\n      {\n         restart = position;\n         // Have to decrement restart since it will get incremented again later:\n         --restart;\n      }\n      break;\n   case commit_prune:\n      break;\n   }\n\n   saved_state* pmp = m_backup_state;\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = m_backup_state;\n      --pmp;\n   }\n   (void) new (pmp)saved_state(16);\n   m_backup_state = pmp;\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::match_then()\n{\n   // Just leave a mark that we need to skip to next alternative:\n   saved_state* pmp = m_backup_state;\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = m_backup_state;\n      --pmp;\n   }\n   (void) new (pmp)saved_state(17);\n   m_backup_state = pmp;\n   pstate = pstate->next.p;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::skip_until_paren(int index, bool have_match)\n{\n   while(pstate)\n   {\n      if(pstate->type == syntax_element_endmark)\n      {\n         if(static_cast<const re_brace*>(pstate)->index == index)\n         {\n            if(have_match)\n               return this->match_endmark();\n            pstate = pstate->next.p;\n            return true;\n         }\n         else\n         {\n            // Unenclosed closing ), occurs when (*ACCEPT) is inside some other \n            // parenthesis which may or may not have other side effects associated with it.\n            const re_syntax_base* sp = pstate;\n            match_endmark();\n            if(!pstate)\n            {\n               unwind(true);\n               // unwind may leave pstate NULL if we've unwound a forward lookahead, in which\n               // case just move to the next state and keep looking...\n               if (!pstate)\n                  pstate = sp->next.p;\n            }\n         }\n         continue;\n      }\n      else if(pstate->type == syntax_element_match)\n         return true;\n      else if(pstate->type == syntax_element_startmark)\n      {\n         int idx = static_cast<const re_brace*>(pstate)->index;\n         pstate = pstate->next.p;\n         skip_until_paren(idx, false);\n         continue;\n      }\n      pstate = pstate->next.p;\n   }\n   return true;\n}\n\n/****************************************************************************\n\nUnwind and associated procedures follow, these perform what normal stack\nunwinding does in the recursive implementation.\n\n****************************************************************************/\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind(bool have_match)\n{\n   static unwind_proc_type const s_unwind_table[19] = \n   {\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_end,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_paren,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion_stopper,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_assertion,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_alt,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_repeater_counter,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_extra_block,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_greedy_single_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_slow_dot_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_fast_dot_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_char_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_short_set_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_long_set_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_non_greedy_repeat,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion_pop,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_commit,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_then,\n      &perl_matcher<BidiIterator, Allocator, traits>::unwind_case,\n   };\n\n   m_recursive_result = have_match;\n   m_unwound_lookahead = false;\n   m_unwound_alt = false;\n   unwind_proc_type unwinder;\n   bool cont;\n   //\n   // keep unwinding our stack until we have something to do:\n   //\n   do\n   {\n      unwinder = s_unwind_table[m_backup_state->state_id];\n      cont = (this->*unwinder)(m_recursive_result);\n   }while(cont);\n   //\n   // return true if we have more states to try:\n   //\n   return pstate ? true : false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_end(bool)\n{\n   pstate = 0;   // nothing left to search\n   return false; // end of stack nothing more to search\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_case(bool)\n{\n   saved_change_case* pmp = static_cast<saved_change_case*>(m_backup_state);\n   icase = pmp->icase;\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_paren(bool have_match)\n{\n   saved_matched_paren<BidiIterator>* pmp = static_cast<saved_matched_paren<BidiIterator>*>(m_backup_state);\n   // restore previous values if no match was found:\n   if(!have_match)\n   {\n      m_presult->set_first(pmp->sub.first, pmp->index, pmp->index == 0);\n      m_presult->set_second(pmp->sub.second, pmp->index, pmp->sub.matched, pmp->index == 0);\n   }\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   //\n   // we have a match, push the capture information onto the stack:\n   //\n   else if(pmp->sub.matched && (match_extra & m_match_flags))\n      ((*m_presult)[pmp->index]).get_captures().push_back(pmp->sub);\n#endif\n   // unwind stack:\n   m_backup_state = pmp+1;\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp);\n   return true; // keep looking\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion_stopper(bool)\n{\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++);\n   pstate = 0;   // nothing left to search\n   return false; // end of stack nothing more to search\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_assertion(bool r)\n{\n   saved_assertion<BidiIterator>* pmp = static_cast<saved_assertion<BidiIterator>*>(m_backup_state);\n   pstate = pmp->pstate;\n   position = pmp->position;\n   bool result = (r == pmp->positive);\n   m_recursive_result = pmp->positive ? r : !r;\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   m_unwound_lookahead = true;\n   return !result; // return false if the assertion was matched to stop search.\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_alt(bool r)\n{\n   saved_position<BidiIterator>* pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n   if(!r)\n   {\n      pstate = pmp->pstate;\n      position = pmp->position;\n   }\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   m_unwound_alt = !r;\n   return r; \n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_repeater_counter(bool)\n{\n   saved_repeater<BidiIterator>* pmp = static_cast<saved_repeater<BidiIterator>*>(m_backup_state);\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return true; // keep looking\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_extra_block(bool)\n{\n   ++used_block_count;\n   saved_extra_block* pmp = static_cast<saved_extra_block*>(m_backup_state);\n   void* condemmed = m_stack_base;\n   m_stack_base = pmp->base;\n   m_backup_state = pmp->end;\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp);\n   put_mem_block(condemmed);\n   return true; // keep looking\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\ninline void perl_matcher<BidiIterator, Allocator, traits>::destroy_single_repeat()\n{\n   saved_single_repeat<BidiIterator>* p = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(p++);\n   m_backup_state = p;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_greedy_single_repeat(bool r)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r) \n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n   BOOST_REGEX_ASSERT(rep->next.p != 0);\n   BOOST_REGEX_ASSERT(rep->alt.p != 0);\n\n   count -= rep->min;\n   \n   if((m_match_flags & match_partial) && (position == last))\n      m_has_partial_match = true;\n\n   BOOST_REGEX_ASSERT(count);\n   position = pmp->last_position;\n\n   // backtrack till we can skip out:\n   do\n   {\n      --position;\n      --count;\n      ++state_count;\n   }while(count && !can_start(*position, rep->_map, mask_skip));\n\n   // if we've hit base, destroy this state:\n   if(count == 0)\n   {\n         destroy_single_repeat();\n         if(!can_start(*position, rep->_map, mask_skip))\n            return true;\n   }\n   else\n   {\n      pmp->count = count + rep->min;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_slow_dot_repeat(bool r)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r) \n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n   BOOST_REGEX_ASSERT(rep->type == syntax_element_dot_rep);\n   BOOST_REGEX_ASSERT(rep->next.p != 0);\n   BOOST_REGEX_ASSERT(rep->alt.p != 0);\n   BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_wild);\n\n   BOOST_REGEX_ASSERT(count < rep->max);\n   pstate = rep->next.p;\n   position = pmp->last_position;\n\n   if(position != last)\n   {\n      // wind forward until we can skip out of the repeat:\n      do\n      {\n         if(!match_wild())\n         {\n            // failed repeat match, discard this state and look for another:\n            destroy_single_repeat();\n            return true;\n         }\n         ++count;\n         ++state_count;\n         pstate = rep->next.p;\n      }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip));\n   }   \n   if(position == last)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n         m_has_partial_match = true;\n      if(0 == (rep->can_be_null & mask_skip))\n         return true;\n   }\n   else if(count == rep->max)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if(!can_start(*position, rep->_map, mask_skip))\n         return true;\n   }\n   else\n   {\n      pmp->count = count;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_fast_dot_repeat(bool r)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r) \n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n\n   BOOST_REGEX_ASSERT(count < rep->max);\n   position = pmp->last_position;\n   if(position != last)\n   {\n\n      // wind forward until we can skip out of the repeat:\n      do\n      {\n         ++position;\n         ++count;\n         ++state_count;\n      }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip));\n   }\n\n   // remember where we got to if this is a leading repeat:\n   if((rep->leading) && (count < rep->max))\n      restart = position;\n   if(position == last)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n         m_has_partial_match = true;\n      if(0 == (rep->can_be_null & mask_skip))\n         return true;\n   }\n   else if(count == rep->max)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if(!can_start(*position, rep->_map, mask_skip))\n         return true;\n   }\n   else\n   {\n      pmp->count = count;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_char_repeat(bool r)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r) \n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n   pstate = rep->next.p;\n   const char_type what = *reinterpret_cast<const char_type*>(static_cast<const re_literal*>(pstate) + 1);\n   position = pmp->last_position;\n\n   BOOST_REGEX_ASSERT(rep->type == syntax_element_char_rep);\n   BOOST_REGEX_ASSERT(rep->next.p != 0);\n   BOOST_REGEX_ASSERT(rep->alt.p != 0);\n   BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_literal);\n   BOOST_REGEX_ASSERT(count < rep->max);\n\n   if(position != last)\n   {\n      // wind forward until we can skip out of the repeat:\n      do\n      {\n         if(traits_inst.translate(*position, icase) != what)\n         {\n            // failed repeat match, discard this state and look for another:\n            destroy_single_repeat();\n            return true;\n         }\n         ++count;\n         ++ position;\n         ++state_count;\n         pstate = rep->next.p;\n      }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip));\n   }   \n   // remember where we got to if this is a leading repeat:\n   if((rep->leading) && (count < rep->max))\n      restart = position;\n   if(position == last)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n         m_has_partial_match = true;\n      if(0 == (rep->can_be_null & mask_skip))\n         return true;\n   }\n   else if(count == rep->max)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if(!can_start(*position, rep->_map, mask_skip))\n         return true;\n   }\n   else\n   {\n      pmp->count = count;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_short_set_repeat(bool r)\n{\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r) \n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n   pstate = rep->next.p;\n   const unsigned char* map = static_cast<const re_set*>(rep->next.p)->_map;\n   position = pmp->last_position;\n\n   BOOST_REGEX_ASSERT(rep->type == syntax_element_short_set_rep);\n   BOOST_REGEX_ASSERT(rep->next.p != 0);\n   BOOST_REGEX_ASSERT(rep->alt.p != 0);\n   BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_set);\n   BOOST_REGEX_ASSERT(count < rep->max);\n   \n   if(position != last)\n   {\n      // wind forward until we can skip out of the repeat:\n      do\n      {\n         if(!map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])\n         {\n            // failed repeat match, discard this state and look for another:\n            destroy_single_repeat();\n            return true;\n         }\n         ++count;\n         ++ position;\n         ++state_count;\n         pstate = rep->next.p;\n      }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip));\n   }   \n   // remember where we got to if this is a leading repeat:\n   if((rep->leading) && (count < rep->max))\n      restart = position;\n   if(position == last)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n         m_has_partial_match = true;\n      if(0 == (rep->can_be_null & mask_skip))\n         return true;\n   }\n   else if(count == rep->max)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if(!can_start(*position, rep->_map, mask_skip))\n         return true;\n   }\n   else\n   {\n      pmp->count = count;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_long_set_repeat(bool r)\n{\n   typedef typename traits::char_class_type m_type;\n   saved_single_repeat<BidiIterator>* pmp = static_cast<saved_single_repeat<BidiIterator>*>(m_backup_state);\n\n   // if we have a match, just discard this state:\n   if(r)\n   {\n      destroy_single_repeat();\n      return true;\n   }\n\n   const re_repeat* rep = pmp->rep;\n   std::size_t count = pmp->count;\n   pstate = rep->next.p;\n   const re_set_long<m_type>* set = static_cast<const re_set_long<m_type>*>(pstate);\n   position = pmp->last_position;\n\n   BOOST_REGEX_ASSERT(rep->type == syntax_element_long_set_rep);\n   BOOST_REGEX_ASSERT(rep->next.p != 0);\n   BOOST_REGEX_ASSERT(rep->alt.p != 0);\n   BOOST_REGEX_ASSERT(rep->next.p->type == syntax_element_long_set);\n   BOOST_REGEX_ASSERT(count < rep->max);\n\n   if(position != last)\n   {\n      // wind forward until we can skip out of the repeat:\n      do\n      {\n         if(position == re_is_set_member(position, last, set, re.get_data(), icase))\n         {\n            // failed repeat match, discard this state and look for another:\n            destroy_single_repeat();\n            return true;\n         }\n         ++position;\n         ++count;\n         ++state_count;\n         pstate = rep->next.p;\n      }while((count < rep->max) && (position != last) && !can_start(*position, rep->_map, mask_skip));\n   }   \n   // remember where we got to if this is a leading repeat:\n   if((rep->leading) && (count < rep->max))\n      restart = position;\n   if(position == last)\n   {\n      // can't repeat any more, remove the pushed state:\n      destroy_single_repeat();\n      if((m_match_flags & match_partial) && (position == last) && (position != search_base))\n         m_has_partial_match = true;\n      if(0 == (rep->can_be_null & mask_skip))\n         return true;\n   }\n   else if(count == rep->max)\n   {\n      // can't repeat any more, remove the pushed state: \n      destroy_single_repeat();\n      if(!can_start(*position, rep->_map, mask_skip))\n         return true;\n   }\n   else\n   {\n      pmp->count = count;\n      pmp->last_position = position;\n   }\n   pstate = rep->alt.p;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_non_greedy_repeat(bool r)\n{\n   saved_position<BidiIterator>* pmp = static_cast<saved_position<BidiIterator>*>(m_backup_state);\n   if(!r)\n   {\n      position = pmp->position;\n      pstate = pmp->pstate;\n      ++(*next_count);\n   }\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return r;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion(bool r)\n{\n   // We are backtracking back inside a recursion, need to push the info\n   // back onto the recursion stack, and do so unconditionally, otherwise\n   // we can get mismatched pushes and pops...\n   saved_recursion<results_type>* pmp = static_cast<saved_recursion<results_type>*>(m_backup_state);\n   if (!r)\n   {\n      recursion_stack.push_back(recursion_info<results_type>());\n      recursion_stack.back().idx = pmp->recursion_id;\n      recursion_stack.back().preturn_address = pmp->preturn_address;\n      recursion_stack.back().results = pmp->prior_results;\n      recursion_stack.back().location_of_start = position;\n      *m_presult = pmp->internal_results;\n   }\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_recursion_pop(bool r)\n{\n   // Backtracking out of a recursion, we must pop state off the recursion\n   // stack unconditionally to ensure matched pushes and pops:\n   saved_state* pmp = static_cast<saved_state*>(m_backup_state);\n   if (!r && !recursion_stack.empty())\n   {\n      *m_presult = recursion_stack.back().results;\n      position = recursion_stack.back().location_of_start;\n      recursion_stack.pop_back();\n   }\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(pmp++);\n   m_backup_state = pmp;\n   return true;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nvoid perl_matcher<BidiIterator, Allocator, traits>::push_recursion_pop()\n{\n   saved_state* pmp = static_cast<saved_state*>(m_backup_state);\n   --pmp;\n   if(pmp < m_stack_base)\n   {\n      extend_stack();\n      pmp = static_cast<saved_state*>(m_backup_state);\n      --pmp;\n   }\n   (void) new (pmp)saved_state(15);\n   m_backup_state = pmp;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_commit(bool b)\n{\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++);\n   while(unwind(b) && !m_unwound_lookahead){}\n   if(m_unwound_lookahead && pstate)\n   {\n      //\n      // If we stop because we just unwound an assertion, put the\n      // commit state back on the stack again:\n      //\n      m_unwound_lookahead = false;\n      saved_state* pmp = m_backup_state;\n      --pmp;\n      if(pmp < m_stack_base)\n      {\n         extend_stack();\n         pmp = m_backup_state;\n         --pmp;\n      }\n      (void) new (pmp)saved_state(16);\n      m_backup_state = pmp;\n   }\n   // This prevents us from stopping when we exit from an independent sub-expression:\n   m_independent = false;\n   return false;\n}\n\ntemplate <class BidiIterator, class Allocator, class traits>\nbool perl_matcher<BidiIterator, Allocator, traits>::unwind_then(bool b)\n{\n   // Unwind everything till we hit an alternative:\n   boost::BOOST_REGEX_DETAIL_NS::inplace_destroy(m_backup_state++);\n   bool result = false;\n   result = unwind(b);\n   while(result && !m_unwound_alt)\n   {\n      result = unwind(b);\n   }\n   // We're now pointing at the next alternative, need one more backtrack \n   // since *all* the other alternatives must fail once we've reached a THEN clause:\n   if(result && m_unwound_alt)\n      unwind(b);\n   return false;\n}\n\n} // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/primary_transform.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE:        primary_transform.hpp\n  *   VERSION:     see <boost/version.hpp>\n  *   DESCRIPTION: Heuristically determines the sort string format in use\n  *                by the current locale.\n  */\n\n#ifndef BOOST_REGEX_PRIMARY_TRANSFORM\n#define BOOST_REGEX_PRIMARY_TRANSFORM\n\nnamespace boost{\n   namespace BOOST_REGEX_DETAIL_NS{\n\n\nenum{\n   sort_C,\n   sort_fixed,\n   sort_delim,\n   sort_unknown\n};\n\ntemplate <class S, class charT>\nunsigned count_chars(const S& s, charT c)\n{\n   //\n   // Count how many occurrences of character c occur\n   // in string s: if c is a delimeter between collation\n   // fields, then this should be the same value for all\n   // sort keys:\n   //\n   unsigned int count = 0;\n   for(unsigned pos = 0; pos < s.size(); ++pos)\n   {\n      if(s[pos] == c) ++count;\n   }\n   return count;\n}\n\n\ntemplate <class traits, class charT>\nunsigned find_sort_syntax(const traits* pt, charT* delim)\n{\n   //\n   // compare 'a' with 'A' to see how similar they are,\n   // should really use a-accute but we can't portably do that,\n   //\n   typedef typename traits::string_type string_type;\n   typedef typename traits::char_type char_type;\n\n   // Suppress incorrect warning for MSVC\n   (void)pt;\n\n   char_type a[2] = {'a', '\\0', };\n   string_type sa(pt->transform(a, a+1));\n   if(sa == a)\n   {\n      *delim = 0;\n      return sort_C;\n   }\n   char_type A[2] = { 'A', '\\0', };\n   string_type sA(pt->transform(A, A+1));\n   char_type c[2] = { ';', '\\0', };\n   string_type sc(pt->transform(c, c+1));\n\n   int pos = 0;\n   while((pos <= static_cast<int>(sa.size())) && (pos <= static_cast<int>(sA.size())) && (sa[pos] == sA[pos])) ++pos;\n   --pos;\n   if(pos < 0)\n   {\n      *delim = 0;\n      return sort_unknown;\n   }\n   //\n   // at this point sa[pos] is either the end of a fixed width field\n   // or the character that acts as a delimiter:\n   //\n   charT maybe_delim = sa[pos];\n   if((pos != 0) && (count_chars(sa, maybe_delim) == count_chars(sA, maybe_delim)) && (count_chars(sa, maybe_delim) == count_chars(sc, maybe_delim)))\n   {\n      *delim = maybe_delim;\n      return sort_delim;\n   }\n   //\n   // OK doen't look like a delimiter, try for fixed width field:\n   //\n   if((sa.size() == sA.size()) && (sa.size() == sc.size()))\n   {\n      // note assumes that the fixed width field is less than\n      // (numeric_limits<charT>::max)(), should be true for all types\n      // I can't imagine 127 character fields...\n      *delim = static_cast<charT>(++pos);\n      return sort_fixed;\n   }\n   //\n   // don't know what it is:\n   //\n   *delim = 0;\n   return sort_unknown;\n}\n\n\n   } // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n\n#endif\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regbase.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regbase.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares class regbase.\n  */\n\n#ifndef BOOST_REGEX_V5_REGBASE_HPP\n#define BOOST_REGEX_V5_REGBASE_HPP\n\nnamespace boost{\n//\n// class regbase\n// handles error codes and flags\n//\nclass regbase\n{\npublic:\n   enum flag_type_\n   {\n      //\n      // Divide the flags up into logical groups:\n      // bits 0-7 indicate main synatx type.\n      // bits 8-15 indicate syntax subtype.\n      // bits 16-31 indicate options that are common to all\n      // regex syntaxes.\n      // In all cases the default is 0.\n      //\n      // Main synatx group:\n      //\n      perl_syntax_group = 0,                      // default\n      basic_syntax_group = 1,                     // POSIX basic\n      literal = 2,                                // all characters are literals\n      main_option_type = literal | basic_syntax_group | perl_syntax_group, // everything!\n      //\n      // options specific to perl group:\n      //\n      no_bk_refs = 1 << 8,                        // \\d not allowed\n      no_perl_ex = 1 << 9,                        // disable perl extensions\n      no_mod_m = 1 << 10,                         // disable Perl m modifier\n      mod_x = 1 << 11,                            // Perl x modifier\n      mod_s = 1 << 12,                            // force s modifier on (overrides match_not_dot_newline)\n      no_mod_s = 1 << 13,                         // force s modifier off (overrides match_not_dot_newline)\n\n      //\n      // options specific to basic group:\n      //\n      no_char_classes = 1 << 8,                   // [[:CLASS:]] not allowed\n      no_intervals = 1 << 9,                      // {x,y} not allowed\n      bk_plus_qm = 1 << 10,                       // uses \\+ and \\?\n      bk_vbar = 1 << 11,                          // use \\| for alternatives\n      emacs_ex = 1 << 12,                         // enables emacs extensions\n\n      //\n      // options common to all groups:\n      //\n      no_escape_in_lists = 1 << 16,                     // '\\' not special inside [...]\n      newline_alt = 1 << 17,                            // \\n is the same as |\n      no_except = 1 << 18,                              // no exception on error\n      failbit = 1 << 19,                                // error flag\n      icase = 1 << 20,                                  // characters are matched regardless of case\n      nocollate = 0,                                    // don't use locale specific collation (deprecated)\n      collate = 1 << 21,                                // use locale specific collation\n      nosubs = 1 << 22,                                 // don't mark sub-expressions\n      save_subexpression_location = 1 << 23,            // save subexpression locations\n      no_empty_expressions = 1 << 24,                   // no empty expressions allowed\n      optimize = 0,                                     // not really supported\n      \n\n\n      basic = basic_syntax_group | collate | no_escape_in_lists,\n      extended = no_bk_refs | collate | no_perl_ex | no_escape_in_lists,\n      normal = 0,\n      emacs = basic_syntax_group | collate | emacs_ex | bk_vbar,\n      awk = no_bk_refs | collate | no_perl_ex,\n      grep = basic | newline_alt,\n      egrep = extended | newline_alt,\n      sed = basic,\n      perl = normal,\n      ECMAScript = normal,\n      JavaScript = normal,\n      JScript = normal\n   };\n   typedef unsigned int flag_type;\n\n   enum restart_info\n   {\n      restart_any = 0,\n      restart_word = 1,\n      restart_line = 2,\n      restart_buf = 3,\n      restart_continue = 4,\n      restart_lit = 5,\n      restart_fixed_lit = 6, \n      restart_count = 7\n   };\n};\n\n//\n// provide std lib proposal compatible constants:\n//\nnamespace regex_constants{\n\n   enum flag_type_\n   {\n\n      no_except = ::boost::regbase::no_except,\n      failbit = ::boost::regbase::failbit,\n      literal = ::boost::regbase::literal,\n      icase = ::boost::regbase::icase,\n      nocollate = ::boost::regbase::nocollate,\n      collate = ::boost::regbase::collate,\n      nosubs = ::boost::regbase::nosubs,\n      optimize = ::boost::regbase::optimize,\n      bk_plus_qm = ::boost::regbase::bk_plus_qm,\n      bk_vbar = ::boost::regbase::bk_vbar,\n      no_intervals = ::boost::regbase::no_intervals,\n      no_char_classes = ::boost::regbase::no_char_classes,\n      no_escape_in_lists = ::boost::regbase::no_escape_in_lists,\n      no_mod_m = ::boost::regbase::no_mod_m,\n      mod_x = ::boost::regbase::mod_x,\n      mod_s = ::boost::regbase::mod_s,\n      no_mod_s = ::boost::regbase::no_mod_s,\n      save_subexpression_location = ::boost::regbase::save_subexpression_location,\n      no_empty_expressions = ::boost::regbase::no_empty_expressions,\n\n      basic = ::boost::regbase::basic,\n      extended = ::boost::regbase::extended,\n      normal = ::boost::regbase::normal,\n      emacs = ::boost::regbase::emacs,\n      awk = ::boost::regbase::awk,\n      grep = ::boost::regbase::grep,\n      egrep = ::boost::regbase::egrep,\n      sed = basic,\n      perl = normal,\n      ECMAScript = normal,\n      JavaScript = normal,\n      JScript = normal\n   };\n   typedef ::boost::regbase::flag_type syntax_option_type;\n\n} // namespace regex_constants\n\n} // namespace boost\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares boost::basic_regex<> and associated\n  *                functions and classes. This header is the main\n  *                entry point for the template regex code.\n  */\n\n#ifndef BOOST_RE_REGEX_HPP_INCLUDED\n#define BOOST_RE_REGEX_HPP_INCLUDED\n\n#ifdef __cplusplus\n\n// what follows is all C++ don't include in C builds!!\n\n#include <boost/regex/config.hpp>\n#include <boost/regex/v5/regex_workaround.hpp>\n#include <boost/regex_fwd.hpp>\n#include <boost/regex/regex_traits.hpp>\n#include <boost/regex/v5/error_type.hpp>\n#include <boost/regex/v5/match_flags.hpp>\n#include <boost/regex/v5/regex_raw_buffer.hpp>\n#include <boost/regex/pattern_except.hpp>\n#include <boost/regex/v5/char_regex_traits.hpp>\n#include <boost/regex/v5/states.hpp>\n#include <boost/regex/v5/regbase.hpp>\n#include <boost/regex/v5/basic_regex.hpp>\n#include <boost/regex/v5/basic_regex_creator.hpp>\n#include <boost/regex/v5/basic_regex_parser.hpp>\n#include <boost/regex/v5/sub_match.hpp>\n#include <boost/regex/v5/regex_format.hpp>\n#include <boost/regex/v5/match_results.hpp>\n#include <boost/regex/v5/perl_matcher.hpp>\n\nnamespace boost{\n#ifdef BOOST_REGEX_NO_FWD\ntypedef basic_regex<char, regex_traits<char> > regex;\n#ifndef BOOST_NO_WREGEX\ntypedef basic_regex<wchar_t, regex_traits<wchar_t> > wregex;\n#endif\n#endif\n\ntypedef match_results<const char*> cmatch;\ntypedef match_results<std::string::const_iterator> smatch;\n#ifndef BOOST_NO_WREGEX\ntypedef match_results<const wchar_t*> wcmatch;\ntypedef match_results<std::wstring::const_iterator> wsmatch;\n#endif\n\n} // namespace boost\n\n#include <boost/regex/v5/regex_match.hpp>\n#include <boost/regex/v5/regex_search.hpp>\n#include <boost/regex/v5/regex_iterator.hpp>\n#include <boost/regex/v5/regex_token_iterator.hpp>\n#include <boost/regex/v5/regex_grep.hpp>\n#include <boost/regex/v5/regex_replace.hpp>\n#include <boost/regex/v5/regex_merge.hpp>\n#include <boost/regex/v5/regex_split.hpp>\n\n#endif  // __cplusplus\n\n#endif  // include\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_format.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2009 John Maddock\n * Copyright 2008 Eric Niebler. \n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_format.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides formatting output routines for search and replace\n  *                operations.  Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n#ifndef BOOST_REGEX_FORMAT_HPP\n#define BOOST_REGEX_FORMAT_HPP\n\n#include <type_traits>\n#include <functional>\n\nnamespace boost{\n\n//\n// Forward declaration:\n//\n   template <class BidiIterator, class Allocator = typename std::vector<sub_match<BidiIterator> >::allocator_type >\nclass match_results;\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\n//\n// struct trivial_format_traits:\n// defines minimum localisation support for formatting\n// in the case that the actual regex traits is unavailable.\n//\ntemplate <class charT>\nstruct trivial_format_traits\n{\n   typedef charT char_type;\n\n   static std::ptrdiff_t length(const charT* p)\n   {\n      return global_length(p);\n   }\n   static charT tolower(charT c)\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_lower(c);\n   }\n   static charT toupper(charT c)\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_upper(c);\n   }\n   static int value(const charT c, int radix)\n   {\n      int result = global_value(c);\n      return result >= radix ? -1 : result;\n   }\n   int toi(const charT*& p1, const charT* p2, int radix)const\n   {\n      return (int)global_toi(p1, p2, radix, *this);\n   }\n};\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#pragma warning(disable:26812)\n#endif\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nclass basic_regex_formatter\n{\npublic:\n   typedef typename traits::char_type char_type;\n   basic_regex_formatter(OutputIterator o, const Results& r, const traits& t)\n      : m_traits(t), m_results(r), m_out(o), m_position(), m_end(), m_flags(), m_state(output_copy), m_restore_state(output_copy), m_have_conditional(false) {}\n   OutputIterator format(ForwardIter p1, ForwardIter p2, match_flag_type f);\n   OutputIterator format(ForwardIter p1, match_flag_type f)\n   {\n      return format(p1, p1 + m_traits.length(p1), f);\n   }\nprivate:\n   typedef typename Results::value_type sub_match_type;\n   enum output_state\n   {\n      output_copy,\n      output_next_lower,\n      output_next_upper,\n      output_lower,\n      output_upper,\n      output_none\n   };\n\n   void put(char_type c);\n   void put(const sub_match_type& sub);\n   void format_all();\n   void format_perl();\n   void format_escape();\n   void format_conditional();\n   void format_until_scope_end();\n   bool handle_perl_verb(bool have_brace);\n\n   inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j, const std::integral_constant<bool, false>&)\n   {\n      std::vector<char_type> v(i, j);\n      return (i != j) ? this->m_results.named_subexpression(&v[0], &v[0] + v.size())\n         : this->m_results.named_subexpression(static_cast<const char_type*>(0), static_cast<const char_type*>(0));\n   }\n   inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j, const std::integral_constant<bool, true>&)\n   {\n      return this->m_results.named_subexpression(i, j);\n   }\n   inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j)\n   {\n      typedef typename std::is_convertible<ForwardIter, const char_type*>::type tag_type;\n      return get_named_sub(i, j, tag_type());\n   }\n   inline int get_named_sub_index(ForwardIter i, ForwardIter j, const std::integral_constant<bool, false>&)\n   {\n      std::vector<char_type> v(i, j);\n      return (i != j) ? this->m_results.named_subexpression_index(&v[0], &v[0] + v.size())\n         : this->m_results.named_subexpression_index(static_cast<const char_type*>(0), static_cast<const char_type*>(0));\n   }\n   inline int get_named_sub_index(ForwardIter i, ForwardIter j, const std::integral_constant<bool, true>&)\n   {\n      return this->m_results.named_subexpression_index(i, j);\n   }\n   inline int get_named_sub_index(ForwardIter i, ForwardIter j)\n   {\n      typedef typename std::is_convertible<ForwardIter, const char_type*>::type tag_type;\n      return get_named_sub_index(i, j, tag_type());\n   }\n#ifdef BOOST_REGEX_MSVC\n   // msvc-8.0 issues a spurious warning on the call to std::advance here:\n#pragma warning(push)\n#pragma warning(disable:4244)\n#endif\n   inline int toi(ForwardIter& i, ForwardIter j, int base, const std::integral_constant<bool, false>&)\n   {\n      if(i != j)\n      {\n         std::vector<char_type> v(i, j);\n         const char_type* start = &v[0];\n         const char_type* pos = start;\n         int r = (int)m_traits.toi(pos, &v[0] + v.size(), base);\n         std::advance(i, pos - start);\n         return r;\n      }\n      return -1;\n   }\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n   inline int toi(ForwardIter& i, ForwardIter j, int base, const std::integral_constant<bool, true>&)\n   {\n      return m_traits.toi(i, j, base);\n   }\n   inline int toi(ForwardIter& i, ForwardIter j, int base)\n   {\n#if defined(_MSC_VER) && defined(__INTEL_COMPILER) && ((__INTEL_COMPILER == 9999) || (__INTEL_COMPILER == 1210))\n      // Workaround for Intel support issue #656654.\n      // See also https://svn.boost.org/trac/boost/ticket/6359\n      return toi(i, j, base, std::integral_constant<bool, false>());\n#else\n      typedef typename std::is_convertible<ForwardIter, const char_type*&>::type tag_type;\n      return toi(i, j, base, tag_type());\n#endif\n   }\n\n   const traits&    m_traits;       // the traits class for localised formatting operations\n   const Results&   m_results;     // the match_results being used.\n   OutputIterator   m_out;         // where to send output.\n   ForwardIter      m_position;  // format string, current position\n   ForwardIter      m_end;       // format string end\n   match_flag_type  m_flags;      // format flags to use\n   output_state     m_state;      // what to do with the next character\n   output_state     m_restore_state;  // what state to restore to.\n   bool             m_have_conditional; // we are parsing a conditional\nprivate:\n   basic_regex_formatter(const basic_regex_formatter&);\n   basic_regex_formatter& operator=(const basic_regex_formatter&);\n};\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nOutputIterator basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format(ForwardIter p1, ForwardIter p2, match_flag_type f)\n{\n   m_position = p1;\n   m_end = p2;\n   m_flags = f;\n   format_all();\n   return m_out;\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_all()\n{\n   // over and over:\n   while(m_position != m_end)\n   {\n      switch(*m_position)\n      {\n      case '&':\n         if(m_flags & ::boost::regex_constants::format_sed)\n         {\n            ++m_position;\n            put(m_results[0]);\n            break;\n         }\n         put(*m_position++);\n         break;\n      case '\\\\':\n         format_escape();\n         break;\n      case '(':\n         if(m_flags & boost::regex_constants::format_all)\n         {\n            ++m_position;\n            bool have_conditional = m_have_conditional;\n            m_have_conditional = false;\n            format_until_scope_end();\n            m_have_conditional = have_conditional;\n            if(m_position == m_end)\n               return;\n            BOOST_REGEX_ASSERT(*m_position == static_cast<char_type>(')'));\n            ++m_position;  // skip the closing ')'\n            break;\n         }\n         put(*m_position);\n         ++m_position;\n         break;\n      case ')':\n         if(m_flags & boost::regex_constants::format_all)\n         {\n            return;\n         }\n         put(*m_position);\n         ++m_position;\n         break;\n      case ':':\n         if((m_flags & boost::regex_constants::format_all) && m_have_conditional)\n         {\n            return;\n         }\n         put(*m_position);\n         ++m_position;\n         break;\n      case '?':\n         if(m_flags & boost::regex_constants::format_all)\n         {\n            ++m_position;\n            format_conditional();\n            break;\n         }\n         put(*m_position);\n         ++m_position;\n         break;\n      case '$':\n         if((m_flags & format_sed) == 0)\n         {\n            format_perl();\n            break;\n         }\n         // not a special character:\n         BOOST_REGEX_FALLTHROUGH;\n      default:\n         put(*m_position);\n         ++m_position;\n         break;\n      }\n   }\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_perl()\n{\n   //\n   // On entry *m_position points to a '$' character\n   // output the information that goes with it:\n   //\n   BOOST_REGEX_ASSERT(*m_position == '$');\n   //\n   // see if this is a trailing '$':\n   //\n   if(++m_position == m_end)\n   {\n      --m_position;\n      put(*m_position);\n      ++m_position;\n      return;\n   }\n   //\n   // OK find out what kind it is:\n   //\n   bool have_brace = false;\n   ForwardIter save_position = m_position;\n   switch(*m_position)\n   {\n   case '&':\n      ++m_position;\n      put(this->m_results[0]);\n      break;\n   case '`':\n      ++m_position;\n      put(this->m_results.prefix());\n      break;\n   case '\\'':\n      ++m_position;\n      put(this->m_results.suffix());\n      break;\n   case '$':\n      put(*m_position++);\n      break;\n   case '+':\n      if((++m_position != m_end) && (*m_position == '{'))\n      {\n         ForwardIter base = ++m_position;\n         while((m_position != m_end) && (*m_position != '}')) ++m_position;\n         if(m_position != m_end)\n         {\n            // Named sub-expression:\n            put(get_named_sub(base, m_position));\n            ++m_position;\n            break;\n         }\n         else\n         {\n            m_position = --base;\n         }\n      }\n      put((this->m_results)[this->m_results.size() > 1 ? static_cast<int>(this->m_results.size() - 1) : 1]);\n      break;\n   case '{':\n      have_brace = true;\n      ++m_position;\n      BOOST_REGEX_FALLTHROUGH;\n   default:\n      // see if we have a number:\n      {\n         std::ptrdiff_t len = std::distance(m_position, m_end);\n         //len = (std::min)(static_cast<std::ptrdiff_t>(2), len);\n         int v = this->toi(m_position, m_position + len, 10);\n         if((v < 0) || (have_brace && ((m_position == m_end) || (*m_position != '}'))))\n         {\n            // Look for a Perl-5.10 verb:\n            if(!handle_perl_verb(have_brace))\n            {\n               // leave the $ as is, and carry on:\n               m_position = --save_position;\n               put(*m_position);\n               ++m_position;\n            }\n            break;\n         }\n         // otherwise output sub v:\n         put(this->m_results[v]);\n         if(have_brace)\n            ++m_position;\n      }\n   }\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nbool basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::handle_perl_verb(bool have_brace)\n{\n   // \n   // We may have a capitalised string containing a Perl action:\n   //\n   static const char_type MATCH[] = { 'M', 'A', 'T', 'C', 'H' };\n   static const char_type PREMATCH[] = { 'P', 'R', 'E', 'M', 'A', 'T', 'C', 'H' };\n   static const char_type POSTMATCH[] = { 'P', 'O', 'S', 'T', 'M', 'A', 'T', 'C', 'H' };\n   static const char_type LAST_PAREN_MATCH[] = { 'L', 'A', 'S', 'T', '_', 'P', 'A', 'R', 'E', 'N', '_', 'M', 'A', 'T', 'C', 'H' };\n   static const char_type LAST_SUBMATCH_RESULT[] = { 'L', 'A', 'S', 'T', '_', 'S', 'U', 'B', 'M', 'A', 'T', 'C', 'H', '_', 'R', 'E', 'S', 'U', 'L', 'T' };\n   static const char_type LAST_SUBMATCH_RESULT_ALT[] = { '^', 'N' };\n\n   if(m_position == m_end)\n      return false;\n   if(have_brace && (*m_position == '^'))\n      ++m_position;\n\n   std::ptrdiff_t max_len = m_end - m_position;\n\n   if((max_len >= 5) && std::equal(m_position, m_position + 5, MATCH))\n   {\n      m_position += 5;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 5;\n            return false;\n         }\n      }\n      put(this->m_results[0]);\n      return true;\n   }\n   if((max_len >= 8) && std::equal(m_position, m_position + 8, PREMATCH))\n   {\n      m_position += 8;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 8;\n            return false;\n         }\n      }\n      put(this->m_results.prefix());\n      return true;\n   }\n   if((max_len >= 9) && std::equal(m_position, m_position + 9, POSTMATCH))\n   {\n      m_position += 9;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 9;\n            return false;\n         }\n      }\n      put(this->m_results.suffix());\n      return true;\n   }\n   if((max_len >= 16) && std::equal(m_position, m_position + 16, LAST_PAREN_MATCH))\n   {\n      m_position += 16;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 16;\n            return false;\n         }\n      }\n      put((this->m_results)[this->m_results.size() > 1 ? static_cast<int>(this->m_results.size() - 1) : 1]);\n      return true;\n   }\n   if((max_len >= 20) && std::equal(m_position, m_position + 20, LAST_SUBMATCH_RESULT))\n   {\n      m_position += 20;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 20;\n            return false;\n         }\n      }\n      put(this->m_results.get_last_closed_paren());\n      return true;\n   }\n   if((max_len >= 2) && std::equal(m_position, m_position + 2, LAST_SUBMATCH_RESULT_ALT))\n   {\n      m_position += 2;\n      if(have_brace)\n      {\n         if((m_position != m_end) && (*m_position == '}'))\n            ++m_position;\n         else\n         {\n            m_position -= 2;\n            return false;\n         }\n      }\n      put(this->m_results.get_last_closed_paren());\n      return true;\n   }\n   return false;\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_escape()\n{\n   // skip the escape and check for trailing escape:\n   if(++m_position == m_end)\n   {\n      put(static_cast<char_type>('\\\\'));\n      return;\n   }\n   // now switch on the escape type:\n   switch(*m_position)\n   {\n   case 'a':\n      put(static_cast<char_type>('\\a'));\n      ++m_position;\n      break;\n   case 'f':\n      put(static_cast<char_type>('\\f'));\n      ++m_position;\n      break;\n   case 'n':\n      put(static_cast<char_type>('\\n'));\n      ++m_position;\n      break;\n   case 'r':\n      put(static_cast<char_type>('\\r'));\n      ++m_position;\n      break;\n   case 't':\n      put(static_cast<char_type>('\\t'));\n      ++m_position;\n      break;\n   case 'v':\n      put(static_cast<char_type>('\\v'));\n      ++m_position;\n      break;\n   case 'x':\n      if(++m_position == m_end)\n      {\n         put(static_cast<char_type>('x'));\n         return;\n      }\n      // maybe have \\x{ddd}\n      if(*m_position == static_cast<char_type>('{'))\n      {\n         ++m_position;\n         int val = this->toi(m_position, m_end, 16);\n         if(val < 0)\n         {\n            // invalid value treat everything as literals:\n            put(static_cast<char_type>('x'));\n            put(static_cast<char_type>('{'));\n            return;\n         }\n         if((m_position == m_end) || (*m_position != static_cast<char_type>('}')))\n         {\n            --m_position;\n            while(*m_position != static_cast<char_type>('\\\\'))\n               --m_position;\n            ++m_position;\n            put(*m_position++);\n            return;\n         }\n         ++m_position;\n         put(static_cast<char_type>(val));\n         return;\n      }\n      else\n      {\n         std::ptrdiff_t len = std::distance(m_position, m_end);\n         len = (std::min)(static_cast<std::ptrdiff_t>(2), len);\n         int val = this->toi(m_position, m_position + len, 16);\n         if(val < 0)\n         {\n            --m_position;\n            put(*m_position++);\n            return;\n         }\n         put(static_cast<char_type>(val));\n      }\n      break;\n   case 'c':\n      if(++m_position == m_end)\n      {\n         --m_position;\n         put(*m_position++);\n         return;\n      }\n      put(static_cast<char_type>(*m_position++ % 32));\n      break;\n   case 'e':\n      put(static_cast<char_type>(27));\n      ++m_position;\n      break;\n   default:\n      // see if we have a perl specific escape:\n      if((m_flags & boost::regex_constants::format_sed) == 0)\n      {\n         bool breakout = false;\n         switch(*m_position)\n         {\n         case 'l':\n            ++m_position;\n            m_restore_state = m_state;\n            m_state = output_next_lower;\n            breakout = true;\n            break;\n         case 'L':\n            ++m_position;\n            m_state = output_lower;\n            breakout = true;\n            break;\n         case 'u':\n            ++m_position;\n            m_restore_state = m_state;\n            m_state = output_next_upper;\n            breakout = true;\n            break;\n         case 'U':\n            ++m_position;\n            m_state = output_upper;\n            breakout = true;\n            break;\n         case 'E':\n            ++m_position;\n            m_state = output_copy;\n            breakout = true;\n            break;\n         }\n         if(breakout)\n            break;\n      }\n      // see if we have a \\n sed style backreference:\n      std::ptrdiff_t len = std::distance(m_position, m_end);\n      len = (std::min)(static_cast<std::ptrdiff_t>(1), len);\n      int v = this->toi(m_position, m_position+len, 10);\n      if((v > 0) || ((v == 0) && (m_flags & ::boost::regex_constants::format_sed)))\n      {\n         put(m_results[v]);\n         break;\n      }\n      else if(v == 0)\n      {\n         // octal ecape sequence:\n         --m_position;\n         len = std::distance(m_position, m_end);\n         len = (std::min)(static_cast<std::ptrdiff_t>(4), len);\n         v = this->toi(m_position, m_position + len, 8);\n         BOOST_REGEX_ASSERT(v >= 0);\n         put(static_cast<char_type>(v));\n         break;\n      }\n      // Otherwise output the character \"as is\":\n      put(*m_position++);\n      break;\n   }\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_conditional()\n{\n   if(m_position == m_end)\n   {\n      // oops trailing '?':\n      put(static_cast<char_type>('?'));\n      return;\n   }\n   int v;\n   if(*m_position == '{')\n   {\n      ForwardIter base = m_position;\n      ++m_position;\n      v = this->toi(m_position, m_end, 10);\n      if(v < 0)\n      {\n         // Try a named subexpression:\n         while((m_position != m_end) && (*m_position != '}'))\n            ++m_position;\n         v = this->get_named_sub_index(base + 1, m_position);\n      }\n      if((v < 0) || (*m_position != '}'))\n      {\n         m_position = base;\n         // oops trailing '?':\n         put(static_cast<char_type>('?'));\n         return;\n      }\n      // Skip trailing '}':\n      ++m_position;\n   }\n   else\n   {\n      std::ptrdiff_t len = std::distance(m_position, m_end);\n      len = (std::min)(static_cast<std::ptrdiff_t>(2), len);\n      v = this->toi(m_position, m_position + len, 10);\n   }\n   if(v < 0)\n   {\n      // oops not a number:\n      put(static_cast<char_type>('?'));\n      return;\n   }\n\n   // output varies depending upon whether sub-expression v matched or not:\n   if(m_results[v].matched)\n   {\n      m_have_conditional = true;\n      format_all();\n      m_have_conditional = false;\n      if((m_position != m_end) && (*m_position == static_cast<char_type>(':')))\n      {\n         // skip the ':':\n         ++m_position;\n         // save output state, then turn it off:\n         output_state saved_state = m_state;\n         m_state = output_none;\n         // format the rest of this scope:\n         format_until_scope_end();\n         // restore output state:\n         m_state = saved_state;\n      }\n   }\n   else\n   {\n      // save output state, then turn it off:\n      output_state saved_state = m_state;\n      m_state = output_none;\n      // format until ':' or ')':\n      m_have_conditional = true;\n      format_all();\n      m_have_conditional = false;\n      // restore state:\n      m_state = saved_state;\n      if((m_position != m_end) && (*m_position == static_cast<char_type>(':')))\n      {\n         // skip the ':':\n         ++m_position;\n         // format the rest of this scope:\n         format_until_scope_end();\n      }\n   }\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_until_scope_end()\n{\n   do\n   {\n      format_all();\n      if((m_position == m_end) || (*m_position == static_cast<char_type>(')')))\n         return;\n      put(*m_position++);\n   }while(m_position != m_end);\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::put(char_type c)\n{\n   // write a single character to output\n   // according to which case translation mode we are in:\n   switch(this->m_state)\n   {\n   case output_none:\n      return;\n   case output_next_lower:\n      c = m_traits.tolower(c);\n      this->m_state = m_restore_state;\n      break;\n   case output_next_upper:\n      c = m_traits.toupper(c);\n      this->m_state = m_restore_state;\n      break;\n   case output_lower:\n      c = m_traits.tolower(c);\n      break;\n   case output_upper:\n      c = m_traits.toupper(c);\n      break;\n   default:\n      break;\n   }\n   *m_out = c;\n   ++m_out;\n}\n\ntemplate <class OutputIterator, class Results, class traits, class ForwardIter>\nvoid basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::put(const sub_match_type& sub)\n{\n   typedef typename sub_match_type::iterator iterator_type;\n   iterator_type i = sub.first;\n   while(i != sub.second)\n   {\n      put(*i);\n      ++i;\n   }\n}\n\ntemplate <class S>\nclass string_out_iterator\n{\n   S* out;\npublic:\n   string_out_iterator(S& s) : out(&s) {}\n   string_out_iterator& operator++() { return *this; }\n   string_out_iterator& operator++(int) { return *this; }\n   string_out_iterator& operator*() { return *this; }\n   string_out_iterator& operator=(typename S::value_type v) \n   { \n      out->append(1, v); \n      return *this; \n   }\n\n   typedef std::ptrdiff_t difference_type;\n   typedef typename S::value_type value_type;\n   typedef value_type* pointer;\n   typedef value_type& reference;\n   typedef std::output_iterator_tag iterator_category;\n};\n\ntemplate <class OutputIterator, class Iterator, class Alloc, class ForwardIter, class traits>\nOutputIterator regex_format_imp(OutputIterator out,\n                          const match_results<Iterator, Alloc>& m,\n                          ForwardIter p1, ForwardIter p2,\n                          match_flag_type flags,\n                          const traits& t\n                         )\n{\n   if(flags & regex_constants::format_literal)\n   {\n      return BOOST_REGEX_DETAIL_NS::copy(p1, p2, out);\n   }\n\n   BOOST_REGEX_DETAIL_NS::basic_regex_formatter<\n      OutputIterator, \n      match_results<Iterator, Alloc>, \n      traits, ForwardIter> f(out, m, t);\n   return f.format(p1, p2, flags);\n}\n\ntemplate <class T>\nstruct has_const_iterator\n{\n   template <class U>\n   static typename U::const_iterator tester(U*);\n   static char tester(...);\n\n   static T* get();\n\n   static const bool value = sizeof(tester(get())) != sizeof(char);\n};\n\nstruct any_type \n{\n   template <class T>\n   any_type(const T&); \n   template <class T, class U>\n   any_type(const T&, const U&); \n   template <class T, class U, class V>\n   any_type(const T&, const U&, const V&); \n};\ntypedef char no_type;\ntypedef char (&unary_type)[2];\ntypedef char (&binary_type)[3];\ntypedef char (&ternary_type)[4];\n\nno_type check_is_formatter(unary_type, binary_type, ternary_type);\ntemplate<typename T>\nunary_type check_is_formatter(T const &, binary_type, ternary_type);\ntemplate<typename T>\nbinary_type check_is_formatter(unary_type, T const &, ternary_type);\ntemplate<typename T, typename U>\nbinary_type check_is_formatter(T const &, U const &, ternary_type);\ntemplate<typename T>\nternary_type check_is_formatter(unary_type, binary_type, T const &);\ntemplate<typename T, typename U>\nternary_type check_is_formatter(T const &, binary_type, U const &);\ntemplate<typename T, typename U>\nternary_type check_is_formatter(unary_type, T const &, U const &);\ntemplate<typename T, typename U, typename V>\nternary_type check_is_formatter(T const &, U const &, V const &);\n\nstruct unary_binary_ternary\n{\n    typedef unary_type (*unary_fun)(any_type);\n    typedef binary_type (*binary_fun)(any_type, any_type);\n    typedef ternary_type (*ternary_fun)(any_type, any_type, any_type);\n    operator unary_fun();\n    operator binary_fun();\n    operator ternary_fun();\n};\n\ntemplate<typename Formatter, bool IsFunction = std::is_function<Formatter>::value>\nstruct formatter_wrapper\n  : Formatter\n  , unary_binary_ternary\n{\n   formatter_wrapper(){}\n};\n\ntemplate<typename Formatter>\nstruct formatter_wrapper<Formatter, true>\n  : unary_binary_ternary\n{\n    operator Formatter *();\n};\n\ntemplate<typename Formatter>\nstruct formatter_wrapper<Formatter *, false>\n  : unary_binary_ternary\n{\n    operator Formatter *();\n};\n\ntemplate <class T>\nstruct do_unwrap_reference\n{\n   typedef T type;\n};\ntemplate <class T>\nstruct do_unwrap_reference<std::reference_wrapper<T> >\n{\n   typedef T type;\n};\n\ntemplate <class T>\nT& do_unwrap_ref(T& r) { return r; }\ntemplate <class T>\nT& do_unwrap_ref(std::reference_wrapper<T> const& r) { return r.get(); }\n\ntemplate <class F, class M, class O>\nstruct format_traits_imp\n{\nprivate:\n   //\n   // F must be a pointer, a function, or a class with a function call operator:\n   //\n   static_assert((::std::is_pointer<F>::value || ::std::is_function<F>::value || ::std::is_class<F>::value), \"The functor must be a pointer or a class with a function call operator\");\n   static formatter_wrapper<typename do_unwrap_reference<F>::type> f;\n   static M m;\n   static O out;\n   static boost::regex_constants::match_flag_type flags;\npublic:\n   static const int value = sizeof(check_is_formatter(f(m), f(m, out), f(m, out, flags)));\n};\n\ntemplate <class F, class M, class O>\nstruct format_traits\n{\npublic:\n   // \n   // Type is std::integral_constant<int, N> where N is one of:\n   //\n   // 0 : F is a pointer to a presumably null-terminated string.\n   // 1 : F is a character-container such as a std::string.\n   // 2 : F is a Unary Functor.\n   // 3 : F is a Binary Functor.\n   // 4 : F is a Ternary Functor.\n   //\n   typedef typename std::conditional<\n      std::is_pointer<F>::value && !std::is_function<typename std::remove_pointer<F>::type>::value,\n      std::integral_constant<int, 0>,\n      typename std::conditional<\n         has_const_iterator<F>::value,\n         std::integral_constant<int, 1>,\n         std::integral_constant<int, format_traits_imp<F, M, O>::value>\n      >::type\n   >::type type;\n   //\n   // This static assertion will fail if the functor passed does not accept\n   // the same type of arguments passed.\n   //\n   static_assert( std::is_class<F>::value && !has_const_iterator<F>::value ? (type::value > 1) : true, \"Argument mismatch in Functor type\");\n};\n\ntemplate <class Base, class Match>\nstruct format_functor3\n{\n   format_functor3(Base b) : func(b) {}\n   template <class OutputIter>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f)\n   {\n      return do_unwrap_ref(func)(m, i, f);\n   }\n   template <class OutputIter, class Traits>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&)\n   {\n      return (*this)(m, i, f);\n   }\nprivate:\n   Base func;\n   format_functor3(const format_functor3&);\n   format_functor3& operator=(const format_functor3&);\n};\n\ntemplate <class Base, class Match>\nstruct format_functor2\n{\n   format_functor2(Base b) : func(b) {}\n   template <class OutputIter>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type /*f*/)\n   {\n      return do_unwrap_ref(func)(m, i);\n   }\n   template <class OutputIter, class Traits>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&)\n   {\n      return (*this)(m, i, f);\n   }\nprivate:\n   Base func;\n   format_functor2(const format_functor2&);\n   format_functor2& operator=(const format_functor2&);\n};\n\ntemplate <class Base, class Match>\nstruct format_functor1\n{\n   format_functor1(Base b) : func(b) {}\n\n   template <class S, class OutputIter>\n   OutputIter do_format_string(const S& s, OutputIter i)\n   {\n      return std::copy(s.begin(), s.end(), i);\n   }\n   template <class S, class OutputIter>\n   inline OutputIter do_format_string(const S* s, OutputIter i)\n   {\n      while(s && *s)\n      {\n         *i = *s;\n         ++i;\n         ++s;\n      }\n      return i;\n   }\n   template <class OutputIter>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type /*f*/)\n   {\n      return do_format_string(do_unwrap_ref(func)(m), i);\n   }\n   template <class OutputIter, class Traits>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&)\n   {\n      return (*this)(m, i, f);\n   }\nprivate:\n   Base func;\n   format_functor1(const format_functor1&);\n   format_functor1& operator=(const format_functor1&);\n};\n\ntemplate <class charT, class Match, class Traits>\nstruct format_functor_c_string\n{\n   format_functor_c_string(const charT* ps) : func(ps) {}\n\n   template <class OutputIter>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits& t = Traits())\n   {\n      //typedef typename Match::char_type char_type;\n      const charT* end = func;\n      while(*end) ++end;\n      return regex_format_imp(i, m, func, end, f, t);\n   }\nprivate:\n   const charT* func;\n   format_functor_c_string(const format_functor_c_string&);\n   format_functor_c_string& operator=(const format_functor_c_string&);\n};\n\ntemplate <class Container, class Match, class Traits>\nstruct format_functor_container\n{\n   format_functor_container(const Container& c) : func(c) {}\n\n   template <class OutputIter>\n   OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits& t = Traits())\n   {\n      //typedef typename Match::char_type char_type;\n      return BOOST_REGEX_DETAIL_NS::regex_format_imp(i, m, func.begin(), func.end(), f, t);\n   }\nprivate:\n   const Container& func;\n   format_functor_container(const format_functor_container&);\n   format_functor_container& operator=(const format_functor_container&);\n};\n\ntemplate <class Func, class Match, class OutputIterator, class Traits = BOOST_REGEX_DETAIL_NS::trivial_format_traits<typename Match::char_type> >\nstruct compute_functor_type\n{\n   typedef typename format_traits<Func, Match, OutputIterator>::type tag;\n   typedef typename std::remove_cv< typename std::remove_pointer<Func>::type>::type maybe_char_type;\n\n   typedef typename std::conditional<\n      tag::value == 0, format_functor_c_string<maybe_char_type, Match, Traits>,\n      typename std::conditional<\n         tag::value == 1, format_functor_container<Func, Match, Traits>,\n         typename std::conditional<\n            tag::value == 2, format_functor1<Func, Match>,\n            typename std::conditional<\n               tag::value == 3, format_functor2<Func, Match>,\n               format_functor3<Func, Match>\n            >::type\n         >::type\n      >::type\n   >::type type;\n};\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\ntemplate <class OutputIterator, class Iterator, class Allocator, class Functor>\ninline OutputIterator regex_format(OutputIterator out,\n                          const match_results<Iterator, Allocator>& m,\n                          Functor fmt,\n                          match_flag_type flags = format_all\n                         )\n{\n   return m.format(out, fmt, flags);\n}\n\ntemplate <class Iterator, class Allocator, class Functor>\ninline std::basic_string<typename match_results<Iterator, Allocator>::char_type> regex_format(const match_results<Iterator, Allocator>& m, \n                                      Functor fmt, \n                                      match_flag_type flags = format_all)\n{\n   return m.format(fmt, flags);\n}\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_FORMAT_HPP\n\n\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_fwd.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_fwd.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Forward declares boost::basic_regex<> and\n  *                associated typedefs.\n  */\n\n#ifndef BOOST_REGEX_FWD_HPP_INCLUDED\n#define BOOST_REGEX_FWD_HPP_INCLUDED\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n\n//\n// define BOOST_REGEX_NO_FWD if this\n// header doesn't work!\n//\n#ifdef BOOST_REGEX_NO_FWD\n#  ifndef BOOST_RE_REGEX_HPP\n#     include <boost/regex.hpp>\n#  endif\n#else\n\nnamespace boost{\n\ntemplate <class charT>\nclass cpp_regex_traits;\ntemplate <class charT>\nstruct c_regex_traits;\ntemplate <class charT>\nclass w32_regex_traits;\n\n#ifdef BOOST_REGEX_USE_WIN32_LOCALE\ntemplate <class charT, class implementationT = w32_regex_traits<charT> >\nstruct regex_traits;\n#elif defined(BOOST_REGEX_USE_CPP_LOCALE)\ntemplate <class charT, class implementationT = cpp_regex_traits<charT> >\nstruct regex_traits;\n#else\ntemplate <class charT, class implementationT = c_regex_traits<charT> >\nstruct regex_traits;\n#endif\n\ntemplate <class charT, class traits = regex_traits<charT> >\nclass basic_regex;\n\ntypedef basic_regex<char, regex_traits<char> > regex;\n#ifndef BOOST_NO_WREGEX\ntypedef basic_regex<wchar_t, regex_traits<wchar_t> > wregex;\n#endif\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_NO_FWD\n\n#endif\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_grep.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_grep.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides regex_grep implementation.\n  */\n\n#ifndef BOOST_REGEX_V5_REGEX_GREP_HPP\n#define BOOST_REGEX_V5_REGEX_GREP_HPP\n\n\nnamespace boost{\n\n//\n// regex_grep:\n// find all non-overlapping matches within the sequence first last:\n//\ntemplate <class Predicate, class BidiIterator, class charT, class traits>\ninline unsigned int regex_grep(Predicate foo, \n                               BidiIterator first, \n                               BidiIterator last, \n                               const basic_regex<charT, traits>& e, \n                               match_flag_type flags = match_default)\n{\n   if(e.flags() & regex_constants::failbit)\n      return false;\n\n   typedef typename match_results<BidiIterator>::allocator_type match_allocator_type;\n\n   match_results<BidiIterator> m;\n   BOOST_REGEX_DETAIL_NS::perl_matcher<BidiIterator, match_allocator_type, traits> matcher(first, last, m, e, flags, first);\n   unsigned int count = 0;\n   while(matcher.find())\n   {\n      ++count;\n      if(0 == foo(m))\n         return count; // caller doesn't want to go on\n      if(m[0].second == last)\n         return count; // we've reached the end, don't try and find an extra null match.\n      if(m.length() == 0)\n      {\n         if(m[0].second == last)\n            return count;\n         // we found a NULL-match, now try to find\n         // a non-NULL one at the same position:\n         match_results<BidiIterator, match_allocator_type> m2(m);\n         matcher.setf(match_not_null | match_continuous);\n         if(matcher.find())\n         {\n            ++count;\n            if(0 == foo(m))\n               return count;\n         }\n         else\n         {\n            // reset match back to where it was:\n            m = m2;\n         }\n         matcher.unsetf((match_not_null | match_continuous) & ~flags);\n      }\n   }\n   return count;\n}\n\n//\n// regex_grep convenience interfaces:\n//\ntemplate <class Predicate, class charT, class traits>\ninline unsigned int regex_grep(Predicate foo, const charT* str, \n                        const basic_regex<charT, traits>& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_grep(foo, str, str + traits::length(str), e, flags);\n}\n\ntemplate <class Predicate, class ST, class SA, class charT, class traits>\ninline unsigned int regex_grep(Predicate foo, const std::basic_string<charT, ST, SA>& s, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   return regex_grep(foo, s.begin(), s.end(), e, flags);\n}\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_V5_REGEX_GREP_HPP\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_iterator.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_iterator.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides regex_iterator implementation.\n  */\n\n#ifndef BOOST_REGEX_V5_REGEX_ITERATOR_HPP\n#define BOOST_REGEX_V5_REGEX_ITERATOR_HPP\n\n#include <memory>\n\nnamespace boost{\n\ntemplate <class BidirectionalIterator, \n          class charT,\n          class traits>\nclass regex_iterator_implementation \n{\n   typedef basic_regex<charT, traits> regex_type;\n\n   match_results<BidirectionalIterator> what;  // current match\n   BidirectionalIterator                base;  // start of sequence\n   BidirectionalIterator                end;   // end of sequence\n   const regex_type                     re;   // the expression\n   match_flag_type                      flags; // flags for matching\n\npublic:\n   regex_iterator_implementation(const regex_type* p, BidirectionalIterator last, match_flag_type f)\n      : base(), end(last), re(*p), flags(f){}\n   regex_iterator_implementation(const regex_iterator_implementation& other)\n      :what(other.what), base(other.base), end(other.end), re(other.re), flags(other.flags){}\n   bool init(BidirectionalIterator first)\n   {\n      base = first;\n      return regex_search(first, end, what, re, flags);\n   }\n   bool compare(const regex_iterator_implementation& that)\n   {\n      if(this == &that) return true;\n      return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second);\n   }\n   const match_results<BidirectionalIterator>& get()\n   { return what; }\n   bool next()\n   {\n      //if(what.prefix().first != what[0].second)\n      //   flags |= match_prev_avail;\n      BidirectionalIterator next_start = what[0].second;\n      match_flag_type f(flags);\n      if(!what.length() || (f & regex_constants::match_posix))\n         f |= regex_constants::match_not_initial_null;\n      //if(base != next_start)\n      //   f |= regex_constants::match_not_bob;\n      bool result = regex_search(next_start, end, what, re, f, base);\n      if(result)\n         what.set_base(base);\n      return result;\n   }\nprivate:\n   regex_iterator_implementation& operator=(const regex_iterator_implementation&);\n};\n\ntemplate <class BidirectionalIterator, \n          class charT = typename std::iterator_traits<BidirectionalIterator>::value_type,\n          class traits = regex_traits<charT> >\nclass regex_iterator \n{\nprivate:\n   typedef regex_iterator_implementation<BidirectionalIterator, charT, traits> impl;\n   typedef std::shared_ptr<impl> pimpl;\npublic:\n   typedef          basic_regex<charT, traits>                   regex_type;\n   typedef          match_results<BidirectionalIterator>                    value_type;\n   typedef typename std::iterator_traits<BidirectionalIterator>::difference_type \n                                                                            difference_type;\n   typedef          const value_type*                                       pointer;\n   typedef          const value_type&                                       reference; \n   typedef          std::forward_iterator_tag                               iterator_category;\n   \n   regex_iterator(){}\n   regex_iterator(BidirectionalIterator a, BidirectionalIterator b, \n                  const regex_type& re, \n                  match_flag_type m = match_default)\n                  : pdata(new impl(&re, b, m))\n   {\n      if(!pdata->init(a))\n      {\n         pdata.reset();\n      }\n   }\n   regex_iterator(const regex_iterator& that)\n      : pdata(that.pdata) {}\n   regex_iterator& operator=(const regex_iterator& that)\n   {\n      pdata = that.pdata;\n      return *this;\n   }\n   bool operator==(const regex_iterator& that)const\n   { \n      if((pdata.get() == 0) || (that.pdata.get() == 0))\n         return pdata.get() == that.pdata.get();\n      return pdata->compare(*(that.pdata.get())); \n   }\n   bool operator!=(const regex_iterator& that)const\n   { return !(*this == that); }\n   const value_type& operator*()const\n   { return pdata->get(); }\n   const value_type* operator->()const\n   { return &(pdata->get()); }\n   regex_iterator& operator++()\n   {\n      cow();\n      if(0 == pdata->next())\n      {\n         pdata.reset();\n      }\n      return *this;\n   }\n   regex_iterator operator++(int)\n   {\n      regex_iterator result(*this);\n      ++(*this);\n      return result;\n   }\nprivate:\n\n   pimpl pdata;\n\n   void cow()\n   {\n      // copy-on-write\n      if(pdata.get() && (pdata.use_count() > 1))\n      {\n         pdata.reset(new impl(*(pdata.get())));\n      }\n   }\n};\n\ntypedef regex_iterator<const char*> cregex_iterator;\ntypedef regex_iterator<std::string::const_iterator> sregex_iterator;\n#ifndef BOOST_NO_WREGEX\ntypedef regex_iterator<const wchar_t*> wcregex_iterator;\ntypedef regex_iterator<std::wstring::const_iterator> wsregex_iterator;\n#endif\n\n// make_regex_iterator:\ntemplate <class charT, class traits>\ninline regex_iterator<const charT*, charT, traits> make_regex_iterator(const charT* p, const basic_regex<charT, traits>& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_iterator<const charT*, charT, traits>(p, p+traits::length(p), e, m);\n}\ntemplate <class charT, class traits, class ST, class SA>\ninline regex_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits> make_regex_iterator(const std::basic_string<charT, ST, SA>& p, const basic_regex<charT, traits>& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits>(p.begin(), p.end(), e, m);\n}\n\n} // namespace boost\n\n#endif // BOOST_REGEX_V5_REGEX_ITERATOR_HPP\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_match.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_match.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Regular expression matching algorithms.\n  *                Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n\n#ifndef BOOST_REGEX_MATCH_HPP\n#define BOOST_REGEX_MATCH_HPP\n\nnamespace boost{\n\n//\n// proc regex_match\n// returns true if the specified regular expression matches\n// the whole of the input.  Fills in what matched in m.\n//\ntemplate <class BidiIterator, class Allocator, class charT, class traits>\nbool regex_match(BidiIterator first, BidiIterator last, \n                 match_results<BidiIterator, Allocator>& m, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   BOOST_REGEX_DETAIL_NS::perl_matcher<BidiIterator, Allocator, traits> matcher(first, last, m, e, flags, first);\n   return matcher.match();\n}\ntemplate <class iterator, class charT, class traits>\nbool regex_match(iterator first, iterator last, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   match_results<iterator> m;\n   return regex_match(first, last, m, e, flags | regex_constants::match_any);\n}\n//\n// query_match convenience interfaces:\n//\ntemplate <class charT, class Allocator, class traits>\ninline bool regex_match(const charT* str, \n                        match_results<const charT*, Allocator>& m, \n                        const basic_regex<charT, traits>& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_match(str, str + traits::length(str), m, e, flags);\n}\n\ntemplate <class ST, class SA, class Allocator, class charT, class traits>\ninline bool regex_match(const std::basic_string<charT, ST, SA>& s, \n                 match_results<typename std::basic_string<charT, ST, SA>::const_iterator, Allocator>& m, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   return regex_match(s.begin(), s.end(), m, e, flags);\n}\ntemplate <class charT, class traits>\ninline bool regex_match(const charT* str, \n                        const basic_regex<charT, traits>& e, \n                        match_flag_type flags = match_default)\n{\n   match_results<const charT*> m;\n   return regex_match(str, str + traits::length(str), m, e, flags | regex_constants::match_any);\n}\n\ntemplate <class ST, class SA, class charT, class traits>\ninline bool regex_match(const std::basic_string<charT, ST, SA>& s, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   typedef typename std::basic_string<charT, ST, SA>::const_iterator iterator;\n   match_results<iterator> m;\n   return regex_match(s.begin(), s.end(), m, e, flags | regex_constants::match_any);\n}\n\n\n} // namespace boost\n\n#endif   // BOOST_REGEX_MATCH_HPP\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_merge.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_format.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides formatting output routines for search and replace\n  *                operations.  Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n#ifndef BOOST_REGEX_V5_REGEX_MERGE_HPP\n#define BOOST_REGEX_V5_REGEX_MERGE_HPP\n\n\nnamespace boost{\n\ntemplate <class OutputIterator, class Iterator, class traits, class charT>\ninline OutputIterator regex_merge(OutputIterator out,\n                         Iterator first,\n                         Iterator last,\n                         const basic_regex<charT, traits>& e, \n                         const charT* fmt, \n                         match_flag_type flags = match_default)\n{\n   return regex_replace(out, first, last, e, fmt, flags);\n}\n\ntemplate <class OutputIterator, class Iterator, class traits, class charT>\ninline OutputIterator regex_merge(OutputIterator out,\n                         Iterator first,\n                         Iterator last,\n                         const basic_regex<charT, traits>& e, \n                         const std::basic_string<charT>& fmt,\n                         match_flag_type flags = match_default)\n{\n   return regex_merge(out, first, last, e, fmt.c_str(), flags);\n}\n\ntemplate <class traits, class charT>\ninline std::basic_string<charT> regex_merge(const std::basic_string<charT>& s,\n                         const basic_regex<charT, traits>& e, \n                         const charT* fmt,\n                         match_flag_type flags = match_default)\n{\n   return regex_replace(s, e, fmt, flags);\n}\n\ntemplate <class traits, class charT>\ninline std::basic_string<charT> regex_merge(const std::basic_string<charT>& s,\n                         const basic_regex<charT, traits>& e, \n                         const std::basic_string<charT>& fmt,\n                         match_flag_type flags = match_default)\n{\n   return regex_replace(s, e, fmt, flags);\n}\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_V5_REGEX_MERGE_HPP\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_raw_buffer.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_raw_buffer.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Raw character buffer for regex code.\n  *                Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n#ifndef BOOST_REGEX_RAW_BUFFER_HPP\n#define BOOST_REGEX_RAW_BUFFER_HPP\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n\n#include <algorithm>\n#include <cstddef>\n\nnamespace boost{\n   namespace BOOST_REGEX_DETAIL_NS{\n\nstruct empty_padding{};\n\nunion padding\n{\n   void* p;\n   unsigned int i;\n};\n\ntemplate <int N>\nstruct padding3\n{\n   enum{\n      padding_size = 8,\n      padding_mask = 7\n   };\n};\n\ntemplate<>\nstruct padding3<2>\n{\n   enum{\n      padding_size = 2,\n      padding_mask = 1\n   };\n};\n\ntemplate<>\nstruct padding3<4>\n{\n   enum{\n      padding_size = 4,\n      padding_mask = 3\n   };\n};\n\ntemplate<>\nstruct padding3<8>\n{\n   enum{\n      padding_size = 8,\n      padding_mask = 7\n   };\n};\n\ntemplate<>\nstruct padding3<16>\n{\n   enum{\n      padding_size = 16,\n      padding_mask = 15\n   };\n};\n\nenum{\n   padding_size = padding3<sizeof(padding)>::padding_size,\n   padding_mask = padding3<sizeof(padding)>::padding_mask\n};\n\n//\n// class raw_storage\n// basically this is a simplified vector<unsigned char>\n// this is used by basic_regex for expression storage\n//\n\nclass raw_storage\n{\npublic:\n   typedef std::size_t           size_type;\n   typedef unsigned char*        pointer;\nprivate:\n   pointer last, start, end;\npublic:\n\n   raw_storage();\n   raw_storage(size_type n);\n\n   ~raw_storage()\n   {\n      ::operator delete(start);\n   }\n\n   void  resize(size_type n)\n   {\n      size_type newsize = start ? last - start : 1024;\n      while (newsize < n)\n         newsize *= 2;\n      size_type datasize = end - start;\n      // extend newsize to WORD/DWORD boundary:\n      newsize = (newsize + padding_mask) & ~(padding_mask);\n\n      // allocate and copy data:\n      pointer ptr = static_cast<pointer>(::operator new(newsize));\n      BOOST_REGEX_NOEH_ASSERT(ptr)\n         if (start)\n            std::memcpy(ptr, start, datasize);\n\n      // get rid of old buffer:\n      ::operator delete(start);\n\n      // and set up pointers:\n      start = ptr;\n      end = ptr + datasize;\n      last = ptr + newsize;\n   }\n\n   void*  extend(size_type n)\n   {\n      if(size_type(last - end) < n)\n         resize(n + (end - start));\n      pointer result = end;\n      end += n;\n      return result;\n   }\n\n   void*  insert(size_type pos, size_type n)\n   {\n      BOOST_REGEX_ASSERT(pos <= size_type(end - start));\n      if (size_type(last - end) < n)\n         resize(n + (end - start));\n      void* result = start + pos;\n      std::memmove(start + pos + n, start + pos, (end - start) - pos);\n      end += n;\n      return result;\n   }\n\n   size_type  size()\n   {\n      return size_type(end - start);\n   }\n\n   size_type  capacity()\n   {\n      return size_type(last - start);\n   }\n\n   void*  data()const\n   {\n      return start;\n   }\n\n   size_type  index(void* ptr)\n   {\n      return size_type(static_cast<pointer>(ptr) - static_cast<pointer>(data()));\n   }\n\n   void  clear()\n   {\n      end = start;\n   }\n\n   void  align()\n   {\n      // move end up to a boundary:\n      end = start + (((end - start) + padding_mask) & ~padding_mask);\n   }\n   void swap(raw_storage& that)\n   {\n      std::swap(start, that.start);\n      std::swap(end, that.end);\n      std::swap(last, that.last);\n  }\n};\n\ninline raw_storage::raw_storage()\n{\n   last = start = end = 0;\n}\n\ninline raw_storage::raw_storage(size_type n)\n{\n   start = end = static_cast<pointer>(::operator new(n));\n   BOOST_REGEX_NOEH_ASSERT(start)\n   last = start + n;\n}\n\n} // namespace BOOST_REGEX_DETAIL_NS\n} // namespace boost\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_replace.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2009\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_format.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides formatting output routines for search and replace\n  *                operations.  Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n#ifndef BOOST_REGEX_V5_REGEX_REPLACE_HPP\n#define BOOST_REGEX_V5_REGEX_REPLACE_HPP\n\n\nnamespace boost{\n\ntemplate <class OutputIterator, class BidirectionalIterator, class traits, class charT, class Formatter>\nOutputIterator regex_replace(OutputIterator out,\n                         BidirectionalIterator first,\n                         BidirectionalIterator last,\n                         const basic_regex<charT, traits>& e, \n                         Formatter fmt, \n                         match_flag_type flags = match_default)\n{\n   regex_iterator<BidirectionalIterator, charT, traits> i(first, last, e, flags);\n   regex_iterator<BidirectionalIterator, charT, traits> j;\n   if(i == j)\n   {\n      if(!(flags & regex_constants::format_no_copy))\n         out = BOOST_REGEX_DETAIL_NS::copy(first, last, out);\n   }\n   else\n   {\n      BidirectionalIterator last_m(first);\n      while(i != j)\n      {\n         if(!(flags & regex_constants::format_no_copy))\n            out = BOOST_REGEX_DETAIL_NS::copy(i->prefix().first, i->prefix().second, out);\n         out = i->format(out, fmt, flags, e);\n         last_m = (*i)[0].second;\n         if(flags & regex_constants::format_first_only)\n            break;\n         ++i;\n      }\n      if(!(flags & regex_constants::format_no_copy))\n         out = BOOST_REGEX_DETAIL_NS::copy(last_m, last, out);\n   }\n   return out;\n}\n\ntemplate <class traits, class charT, class Formatter>\nstd::basic_string<charT> regex_replace(const std::basic_string<charT>& s,\n                         const basic_regex<charT, traits>& e, \n                         Formatter fmt,\n                         match_flag_type flags = match_default)\n{\n   std::basic_string<charT> result;\n   BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<charT> > i(result);\n   regex_replace(i, s.begin(), s.end(), e, fmt, flags);\n   return result;\n}\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_V5_REGEX_REPLACE_HPP\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_search.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_search.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides regex_search implementation.\n  */\n\n#ifndef BOOST_REGEX_V5_REGEX_SEARCH_HPP\n#define BOOST_REGEX_V5_REGEX_SEARCH_HPP\n\n\nnamespace boost{\n\ntemplate <class BidiIterator, class Allocator, class charT, class traits>\nbool regex_search(BidiIterator first, BidiIterator last, \n                  match_results<BidiIterator, Allocator>& m, \n                  const basic_regex<charT, traits>& e, \n                  match_flag_type flags = match_default)\n{\n   return regex_search(first, last, m, e, flags, first);\n}\n\ntemplate <class BidiIterator, class Allocator, class charT, class traits>\nbool regex_search(BidiIterator first, BidiIterator last, \n                  match_results<BidiIterator, Allocator>& m, \n                  const basic_regex<charT, traits>& e, \n                  match_flag_type flags,\n                  BidiIterator base)\n{\n   if(e.flags() & regex_constants::failbit)\n      return false;\n\n   BOOST_REGEX_DETAIL_NS::perl_matcher<BidiIterator, Allocator, traits> matcher(first, last, m, e, flags, base);\n   return matcher.find();\n}\n\n//\n// regex_search convenience interfaces:\n//\ntemplate <class charT, class Allocator, class traits>\ninline bool regex_search(const charT* str, \n                        match_results<const charT*, Allocator>& m, \n                        const basic_regex<charT, traits>& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_search(str, str + traits::length(str), m, e, flags);\n}\n\ntemplate <class ST, class SA, class Allocator, class charT, class traits>\ninline bool regex_search(const std::basic_string<charT, ST, SA>& s, \n                 match_results<typename std::basic_string<charT, ST, SA>::const_iterator, Allocator>& m, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   return regex_search(s.begin(), s.end(), m, e, flags);\n}\n\ntemplate <class BidiIterator, class charT, class traits>\nbool regex_search(BidiIterator first, BidiIterator last, \n                  const basic_regex<charT, traits>& e, \n                  match_flag_type flags = match_default)\n{\n   if(e.flags() & regex_constants::failbit)\n      return false;\n\n   match_results<BidiIterator> m;\n   typedef typename match_results<BidiIterator>::allocator_type match_alloc_type;\n   BOOST_REGEX_DETAIL_NS::perl_matcher<BidiIterator, match_alloc_type, traits> matcher(first, last, m, e, flags | regex_constants::match_any, first);\n   return matcher.find();\n}\n\ntemplate <class charT, class traits>\ninline bool regex_search(const charT* str, \n                        const basic_regex<charT, traits>& e, \n                        match_flag_type flags = match_default)\n{\n   return regex_search(str, str + traits::length(str), e, flags);\n}\n\ntemplate <class ST, class SA, class charT, class traits>\ninline bool regex_search(const std::basic_string<charT, ST, SA>& s, \n                 const basic_regex<charT, traits>& e, \n                 match_flag_type flags = match_default)\n{\n   return regex_search(s.begin(), s.end(), e, flags);\n}\n\n} // namespace boost\n\n#endif  // BOOST_REGEX_V5_REGEX_SEARCH_HPP\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_split.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_split.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Implements regex_split and associated functions.\n  *                Note this is an internal header file included\n  *                by regex.hpp, do not include on its own.\n  */\n\n#ifndef BOOST_REGEX_SPLIT_HPP\n#define BOOST_REGEX_SPLIT_HPP\n\nnamespace boost{\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#if BOOST_REGEX_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\nnamespace BOOST_REGEX_DETAIL_NS{\n\ntemplate <class charT>\nconst basic_regex<charT>& get_default_expression(charT)\n{\n   static const charT expression_text[4] = { '\\\\', 's', '+', '\\00', };\n   static const basic_regex<charT> e(expression_text);\n   return e;\n}\n\ntemplate <class OutputIterator, class charT, class Traits1, class Alloc1>\nclass split_pred\n{\n   typedef std::basic_string<charT, Traits1, Alloc1> string_type;\n   typedef typename string_type::const_iterator iterator_type;\n   iterator_type* p_last;\n   OutputIterator* p_out;\n   std::size_t* p_max;\n   std::size_t initial_max;\npublic:\n   split_pred(iterator_type* a, OutputIterator* b, std::size_t* c)\n      : p_last(a), p_out(b), p_max(c), initial_max(*c) {}\n\n   bool operator()(const match_results<iterator_type>& what);\n};\n\ntemplate <class OutputIterator, class charT, class Traits1, class Alloc1>\nbool split_pred<OutputIterator, charT, Traits1, Alloc1>::operator()\n   (const match_results<iterator_type>& what)\n{\n   *p_last = what[0].second;\n   if(what.size() > 1)\n   {\n      // output sub-expressions only:\n      for(unsigned i = 1; i < what.size(); ++i)\n      {\n         *(*p_out) = what.str(i);\n         ++(*p_out);\n         if(0 == --*p_max) return false;\n      }\n      return *p_max != 0;\n   }\n   else\n   {\n      // output $` only if it's not-null or not at the start of the input:\n      const sub_match<iterator_type>& sub = what[-1];\n      if((sub.first != sub.second) || (*p_max != initial_max))\n      {\n         *(*p_out) = sub.str();\n         ++(*p_out);\n         return --*p_max;\n      }\n   }\n   //\n   // initial null, do nothing:\n   return true;\n}\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\ntemplate <class OutputIterator, class charT, class Traits1, class Alloc1, class Traits2>\nstd::size_t regex_split(OutputIterator out,\n                   std::basic_string<charT, Traits1, Alloc1>& s, \n                   const basic_regex<charT, Traits2>& e,\n                   match_flag_type flags,\n                   std::size_t max_split)\n{\n   typedef typename std::basic_string<charT, Traits1, Alloc1>::const_iterator  ci_t;\n   //typedef typename match_results<ci_t>::allocator_type                        match_allocator;\n   ci_t last = s.begin();\n   std::size_t init_size = max_split;\n   BOOST_REGEX_DETAIL_NS::split_pred<OutputIterator, charT, Traits1, Alloc1> pred(&last, &out, &max_split);\n   ci_t i, j;\n   i = s.begin();\n   j = s.end();\n   regex_grep(pred, i, j, e, flags);\n   //\n   // if there is still input left, do a final push as long as max_split\n   // is not exhausted, and we're not splitting sub-expressions rather \n   // than whitespace:\n   if(max_split && (last != s.end()) && (e.mark_count() == 0))\n   {\n      *out = std::basic_string<charT, Traits1, Alloc1>((ci_t)last, (ci_t)s.end());\n      ++out;\n      last = s.end();\n      --max_split;\n   }\n   //\n   // delete from the string everything that has been processed so far:\n   s.erase(0, last - s.begin());\n   //\n   // return the number of new records pushed:\n   return init_size - max_split;\n}\n\ntemplate <class OutputIterator, class charT, class Traits1, class Alloc1, class Traits2>\ninline std::size_t regex_split(OutputIterator out,\n                   std::basic_string<charT, Traits1, Alloc1>& s, \n                   const basic_regex<charT, Traits2>& e,\n                   match_flag_type flags = match_default)\n{\n   return regex_split(out, s, e, flags, UINT_MAX);\n}\n\ntemplate <class OutputIterator, class charT, class Traits1, class Alloc1>\ninline std::size_t regex_split(OutputIterator out,\n                   std::basic_string<charT, Traits1, Alloc1>& s)\n{\n   return regex_split(out, s, BOOST_REGEX_DETAIL_NS::get_default_expression(charT(0)), match_default, UINT_MAX);\n}\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_token_iterator.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_token_iterator.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides regex_token_iterator implementation.\n  */\n\n#ifndef BOOST_REGEX_V5_REGEX_TOKEN_ITERATOR_HPP\n#define BOOST_REGEX_V5_REGEX_TOKEN_ITERATOR_HPP\n\n#include <memory>\n\nnamespace boost{\n\ntemplate <class BidirectionalIterator,\n          class charT,\n          class traits>\nclass regex_token_iterator_implementation \n{\n   typedef basic_regex<charT, traits> regex_type;\n   typedef sub_match<BidirectionalIterator>      value_type;\n\n   match_results<BidirectionalIterator> what;   // current match\n   BidirectionalIterator                base;    // start of search area\n   BidirectionalIterator                end;    // end of search area\n   const regex_type                     re;    // the expression\n   match_flag_type                      flags;  // match flags\n   value_type                           result; // the current string result\n   int                                  N;      // the current sub-expression being enumerated\n   std::vector<int>                     subs;   // the sub-expressions to enumerate\n\npublic:\n   regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, int sub, match_flag_type f)\n      : end(last), re(*p), flags(f), N(0){ subs.push_back(sub); }\n   regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const std::vector<int>& v, match_flag_type f)\n      : end(last), re(*p), flags(f), N(0), subs(v){}\n   template <std::size_t CN>\n   regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const int (&submatches)[CN], match_flag_type f)\n      : end(last), re(*p), flags(f), N(0)\n   {\n      for(std::size_t i = 0; i < CN; ++i)\n      {\n         subs.push_back(submatches[i]);\n      }\n   }\n   regex_token_iterator_implementation(const regex_token_iterator_implementation& other) = default;\n   bool init(BidirectionalIterator first)\n   {\n      N = 0;\n      base = first;\n      if(regex_search(first, end, what, re, flags, base) == true)\n      {\n         N = 0;\n         result = ((subs[N] == -1) ? what.prefix() : what[(int)subs[N]]);\n         return true;\n      }\n      else if((subs[N] == -1) && (first != end))\n      {\n         result.first = first;\n         result.second = end;\n         result.matched = (first != end);\n         N = -1;\n         return true;\n      }\n      return false;\n   }\n   bool compare(const regex_token_iterator_implementation& that)\n   {\n      if(this == &that) return true;\n      return (&re.get_data() == &that.re.get_data()) \n         && (end == that.end) \n         && (flags == that.flags) \n         && (N == that.N) \n         && (what[0].first == that.what[0].first) \n         && (what[0].second == that.what[0].second);\n   }\n   const value_type& get()\n   { return result; }\n   bool next()\n   {\n      if(N == -1)\n         return false;\n      if(N+1 < (int)subs.size())\n      {\n         ++N;\n         result =((subs[N] == -1) ? what.prefix() : what[subs[N]]);\n         return true;\n      }\n      //if(what.prefix().first != what[0].second)\n      //   flags |= /*match_prev_avail |*/ regex_constants::match_not_bob;\n      BidirectionalIterator last_end(what[0].second);\n      if(regex_search(last_end, end, what, re, ((what[0].first == what[0].second) ? flags | regex_constants::match_not_initial_null : flags), base))\n      {\n         N =0;\n         result =((subs[N] == -1) ? what.prefix() : what[subs[N]]);\n         return true;\n      }\n      else if((last_end != end) && (subs[0] == -1))\n      {\n         N =-1;\n         result.first = last_end;\n         result.second = end;\n         result.matched = (last_end != end);\n         return true;\n      }\n      return false;\n   }\nprivate:\n   regex_token_iterator_implementation& operator=(const regex_token_iterator_implementation&);\n};\n\ntemplate <class BidirectionalIterator, \n          class charT = typename std::iterator_traits<BidirectionalIterator>::value_type,\n          class traits = regex_traits<charT> >\nclass regex_token_iterator \n{\nprivate:\n   typedef regex_token_iterator_implementation<BidirectionalIterator, charT, traits> impl;\n   typedef std::shared_ptr<impl> pimpl;\npublic:\n   typedef          basic_regex<charT, traits>                   regex_type;\n   typedef          sub_match<BidirectionalIterator>                        value_type;\n   typedef typename std::iterator_traits<BidirectionalIterator>::difference_type \n                                                                            difference_type;\n   typedef          const value_type*                                       pointer;\n   typedef          const value_type&                                       reference; \n   typedef          std::forward_iterator_tag                               iterator_category;\n   \n   regex_token_iterator(){}\n   regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, \n                        int submatch = 0, match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatch, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n   regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, \n                        const std::vector<int>& submatches, match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatches, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n   template <std::size_t N>\n   regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re,\n                        const int (&submatches)[N], match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatches, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n   regex_token_iterator(const regex_token_iterator& that)\n      : pdata(that.pdata) {}\n   regex_token_iterator& operator=(const regex_token_iterator& that)\n   {\n      pdata = that.pdata;\n      return *this;\n   }\n   bool operator==(const regex_token_iterator& that)const\n   { \n      if((pdata.get() == 0) || (that.pdata.get() == 0))\n         return pdata.get() == that.pdata.get();\n      return pdata->compare(*(that.pdata.get())); \n   }\n   bool operator!=(const regex_token_iterator& that)const\n   { return !(*this == that); }\n   const value_type& operator*()const\n   { return pdata->get(); }\n   const value_type* operator->()const\n   { return &(pdata->get()); }\n   regex_token_iterator& operator++()\n   {\n      cow();\n      if(0 == pdata->next())\n      {\n         pdata.reset();\n      }\n      return *this;\n   }\n   regex_token_iterator operator++(int)\n   {\n      regex_token_iterator result(*this);\n      ++(*this);\n      return result;\n   }\nprivate:\n\n   pimpl pdata;\n\n   void cow()\n   {\n      // copy-on-write\n      if(pdata.get() && (pdata.use_count() > 1))\n      {\n         pdata.reset(new impl(*(pdata.get())));\n      }\n   }\n};\n\ntypedef regex_token_iterator<const char*> cregex_token_iterator;\ntypedef regex_token_iterator<std::string::const_iterator> sregex_token_iterator;\n#ifndef BOOST_NO_WREGEX\ntypedef regex_token_iterator<const wchar_t*> wcregex_token_iterator;\ntypedef regex_token_iterator<std::wstring::const_iterator> wsregex_token_iterator;\n#endif\n\ntemplate <class charT, class traits>\ninline regex_token_iterator<const charT*, charT, traits> make_regex_token_iterator(const charT* p, const basic_regex<charT, traits>& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<const charT*, charT, traits>(p, p+traits::length(p), e, submatch, m);\n}\ntemplate <class charT, class traits, class ST, class SA>\ninline regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string<charT, ST, SA>& p, const basic_regex<charT, traits>& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m);\n}\ntemplate <class charT, class traits, std::size_t N>\ninline regex_token_iterator<const charT*, charT, traits> make_regex_token_iterator(const charT* p, const basic_regex<charT, traits>& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<const charT*, charT, traits>(p, p+traits::length(p), e, submatch, m);\n}\ntemplate <class charT, class traits, class ST, class SA, std::size_t N>\ninline regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string<charT, ST, SA>& p, const basic_regex<charT, traits>& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m);\n}\ntemplate <class charT, class traits>\ninline regex_token_iterator<const charT*, charT, traits> make_regex_token_iterator(const charT* p, const basic_regex<charT, traits>& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<const charT*, charT, traits>(p, p+traits::length(p), e, submatch, m);\n}\ntemplate <class charT, class traits, class ST, class SA>\ninline regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits> make_regex_token_iterator(const std::basic_string<charT, ST, SA>& p, const basic_regex<charT, traits>& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return regex_token_iterator<typename std::basic_string<charT, ST, SA>::const_iterator, charT, traits>(p.begin(), p.end(), e, submatch, m);\n}\n\n} // namespace boost\n\n#endif // BOOST_REGEX_V5_REGEX_TOKEN_ITERATOR_HPP\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_traits.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression traits classes.\n  */\n\n#ifndef BOOST_REGEX_TRAITS_HPP_INCLUDED\n#define BOOST_REGEX_TRAITS_HPP_INCLUDED\n\n#include <boost/regex/config.hpp>\n#include <boost/regex/v5/regex_workaround.hpp>\n#include <boost/regex/v5/syntax_type.hpp>\n#include <boost/regex/v5/error_type.hpp>\n#include <boost/regex/v5/regex_traits_defaults.hpp>\n#include <boost/regex/v5/cpp_regex_traits.hpp>\n#include <boost/regex/v5/c_regex_traits.hpp>\n#if defined(_WIN32) && !defined(BOOST_REGEX_NO_W32)\n#     include <boost/regex/v5/w32_regex_traits.hpp>\n#endif\n#include <boost/regex_fwd.hpp>\n\nnamespace boost{\n\ntemplate <class charT, class implementationT >\nstruct regex_traits : public implementationT\n{\n   regex_traits() : implementationT() {}\n};\n\n//\n// class regex_traits_wrapper.\n// this is what our implementation will actually store;\n// it provides default implementations of the \"optional\"\n// interfaces that we support, in addition to the\n// required \"standard\" ones:\n//\nnamespace BOOST_REGEX_DETAIL_NS{\n\n   template <class T>\n   struct has_boost_extensions_tag\n   {\n      template <class U>\n      static double checker(U*, typename U::boost_extensions_tag* = nullptr);\n      static char   checker(...);\n      static T* get();\n\n      static const bool value = sizeof(checker(get())) > 1;\n   };\n   \n\ntemplate <class BaseT>\nstruct default_wrapper : public BaseT\n{\n   typedef typename BaseT::char_type char_type;\n   std::string error_string(::boost::regex_constants::error_type e)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::get_default_error_string(e);\n   }\n   ::boost::regex_constants::syntax_type syntax_type(char_type c)const\n   {\n      return (char_type(c & 0x7f) == c) ? get_default_syntax_type(static_cast<char>(c)) : ::boost::regex_constants::syntax_char;\n   }\n   ::boost::regex_constants::escape_syntax_type escape_syntax_type(char_type c)const\n   {\n      return (char_type(c & 0x7f) == c) ? get_default_escape_syntax_type(static_cast<char>(c)) : ::boost::regex_constants::escape_type_identity;\n   }\n   std::intmax_t toi(const char_type*& p1, const char_type* p2, int radix)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this);\n   }\n   char_type translate(char_type c, bool icase)const\n   {\n      return (icase ? this->translate_nocase(c) : this->translate(c));\n   }\n   char_type translate(char_type c)const\n   {\n      return BaseT::translate(c);\n   }\n   char_type tolower(char_type c)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_lower(c);\n   }\n   char_type toupper(char_type c)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_upper(c);\n   }\n};\n\ntemplate <class BaseT, bool has_extensions>\nstruct compute_wrapper_base\n{\n   typedef BaseT type;\n};\ntemplate <class BaseT>\nstruct compute_wrapper_base<BaseT, false>\n{\n   typedef default_wrapper<BaseT> type;\n};\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\ntemplate <class BaseT>\nstruct regex_traits_wrapper \n   : public ::boost::BOOST_REGEX_DETAIL_NS::compute_wrapper_base<\n               BaseT, \n               ::boost::BOOST_REGEX_DETAIL_NS::has_boost_extensions_tag<BaseT>::value\n            >::type\n{\n   regex_traits_wrapper(){}\nprivate:\n   regex_traits_wrapper(const regex_traits_wrapper&);\n   regex_traits_wrapper& operator=(const regex_traits_wrapper&);\n};\n\n} // namespace boost\n\n#endif // include\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_traits_defaults.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the\n * Boost Software License, Version 1.0. (See accompanying file\n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_traits_defaults.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares API's for access to regex_traits default properties.\n  */\n\n#ifndef BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED\n#define BOOST_REGEX_TRAITS_DEFAULTS_HPP_INCLUDED\n\n#include <boost/regex/config.hpp>\n\n#include <boost/regex/v5/syntax_type.hpp>\n#include <boost/regex/v5/error_type.hpp>\n#include <boost/regex/v5/regex_workaround.hpp>\n#include <type_traits>\n#include <cstdint>\n#include <cctype>\n#include <locale>\n#include <cwctype>\n#include <limits>\n\nnamespace boost{ namespace BOOST_REGEX_DETAIL_NS{\n\n\n//\n// helpers to suppress warnings:\n//\ntemplate <class charT>\ninline bool is_extended(charT c)\n{\n   typedef typename std::make_unsigned<charT>::type unsigned_type; \n   return (sizeof(charT) > 1) && (static_cast<unsigned_type>(c) >= 256u); \n}\ninline bool is_extended(char)\n{ return false; }\n\ninline const char*  get_default_syntax(regex_constants::syntax_type n)\n{\n   // if the user hasn't supplied a message catalog, then this supplies\n   // default \"messages\" for us to load in the range 1-100.\n   const char* messages[] = {\n         \"\",\n         \"(\",\n         \")\",\n         \"$\",\n         \"^\",\n         \".\",\n         \"*\",\n         \"+\",\n         \"?\",\n         \"[\",\n         \"]\",\n         \"|\",\n         \"\\\\\",\n         \"#\",\n         \"-\",\n         \"{\",\n         \"}\",\n         \"0123456789\",\n         \"b\",\n         \"B\",\n         \"<\",\n         \">\",\n         \"\",\n         \"\",\n         \"A`\",\n         \"z'\",\n         \"\\n\",\n         \",\",\n         \"a\",\n         \"f\",\n         \"n\",\n         \"r\",\n         \"t\",\n         \"v\",\n         \"x\",\n         \"c\",\n         \":\",\n         \"=\",\n         \"e\",\n         \"\",\n         \"\",\n         \"\",\n         \"\",\n         \"\",\n         \"\",\n         \"\",\n         \"\",\n         \"E\",\n         \"Q\",\n         \"X\",\n         \"C\",\n         \"Z\",\n         \"G\",\n         \"!\",\n         \"p\",\n         \"P\",\n         \"N\",\n         \"gk\",\n         \"K\",\n         \"R\",\n   };\n\n   return ((n >= (sizeof(messages) / sizeof(messages[1]))) ? \"\" : messages[n]);\n}\n\ninline const char*  get_default_error_string(regex_constants::error_type n)\n{\n   static const char* const s_default_error_messages[] = {\n      \"Success\",                                                            /* REG_NOERROR 0 error_ok */\n      \"No match\",                                                           /* REG_NOMATCH 1 error_no_match */\n      \"Invalid regular expression.\",                                        /* REG_BADPAT 2 error_bad_pattern */\n      \"Invalid collation character.\",                                       /* REG_ECOLLATE 3 error_collate */\n      \"Invalid character class name, collating name, or character range.\",  /* REG_ECTYPE 4 error_ctype */\n      \"Invalid or unterminated escape sequence.\",                           /* REG_EESCAPE 5 error_escape */\n      \"Invalid back reference: specified capturing group does not exist.\",  /* REG_ESUBREG 6 error_backref */\n      \"Unmatched [ or [^ in character class declaration.\",                  /* REG_EBRACK 7 error_brack */\n      \"Unmatched marking parenthesis ( or \\\\(.\",                            /* REG_EPAREN 8 error_paren */\n      \"Unmatched quantified repeat operator { or \\\\{.\",                     /* REG_EBRACE 9 error_brace */\n      \"Invalid content of repeat range.\",                                   /* REG_BADBR 10 error_badbrace */\n      \"Invalid range end in character class\",                               /* REG_ERANGE 11 error_range */\n      \"Out of memory.\",                                                     /* REG_ESPACE 12 error_space NOT USED */\n      \"Invalid preceding regular expression prior to repetition operator.\", /* REG_BADRPT 13 error_badrepeat */\n      \"Premature end of regular expression\",                                /* REG_EEND 14 error_end NOT USED */\n      \"Regular expression is too large.\",                                   /* REG_ESIZE 15 error_size NOT USED */\n      \"Unmatched ) or \\\\)\",                                                 /* REG_ERPAREN 16 error_right_paren NOT USED */\n      \"Empty regular expression.\",                                          /* REG_EMPTY 17 error_empty */\n      \"The complexity of matching the regular expression exceeded predefined bounds.  \"\n      \"Try refactoring the regular expression to make each choice made by the state machine unambiguous.  \"\n      \"This exception is thrown to prevent \\\"eternal\\\" matches that take an \"\n      \"indefinite period time to locate.\",                                  /* REG_ECOMPLEXITY 18 error_complexity */\n      \"Ran out of stack space trying to match the regular expression.\",     /* REG_ESTACK 19 error_stack */\n      \"Invalid or unterminated Perl (?...) sequence.\",                      /* REG_E_PERL 20 error_perl */\n      \"Unknown error.\",                                                     /* REG_E_UNKNOWN 21 error_unknown */\n   };\n\n   return (n > ::boost::regex_constants::error_unknown) ? s_default_error_messages[::boost::regex_constants::error_unknown] : s_default_error_messages[n];\n}\n\ninline regex_constants::syntax_type  get_default_syntax_type(char c)\n{\n   //\n   // char_syntax determines how the compiler treats a given character\n   // in a regular expression.\n   //\n   static regex_constants::syntax_type char_syntax[] = {\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_newline,     /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /* */    // 32\n      regex_constants::syntax_not,        /*!*/\n      regex_constants::syntax_char,        /*\"*/\n      regex_constants::syntax_hash,        /*#*/\n      regex_constants::syntax_dollar,        /*$*/\n      regex_constants::syntax_char,        /*%*/\n      regex_constants::syntax_char,        /*&*/\n      regex_constants::escape_type_end_buffer,  /*'*/\n      regex_constants::syntax_open_mark,        /*(*/\n      regex_constants::syntax_close_mark,        /*)*/\n      regex_constants::syntax_star,        /***/\n      regex_constants::syntax_plus,        /*+*/\n      regex_constants::syntax_comma,        /*,*/\n      regex_constants::syntax_dash,        /*-*/\n      regex_constants::syntax_dot,        /*.*/\n      regex_constants::syntax_char,        /*/*/\n      regex_constants::syntax_digit,        /*0*/\n      regex_constants::syntax_digit,        /*1*/\n      regex_constants::syntax_digit,        /*2*/\n      regex_constants::syntax_digit,        /*3*/\n      regex_constants::syntax_digit,        /*4*/\n      regex_constants::syntax_digit,        /*5*/\n      regex_constants::syntax_digit,        /*6*/\n      regex_constants::syntax_digit,        /*7*/\n      regex_constants::syntax_digit,        /*8*/\n      regex_constants::syntax_digit,        /*9*/\n      regex_constants::syntax_colon,        /*:*/\n      regex_constants::syntax_char,        /*;*/\n      regex_constants::escape_type_left_word, /*<*/\n      regex_constants::syntax_equal,        /*=*/\n      regex_constants::escape_type_right_word, /*>*/\n      regex_constants::syntax_question,        /*?*/\n      regex_constants::syntax_char,        /*@*/\n      regex_constants::syntax_char,        /*A*/\n      regex_constants::syntax_char,        /*B*/\n      regex_constants::syntax_char,        /*C*/\n      regex_constants::syntax_char,        /*D*/\n      regex_constants::syntax_char,        /*E*/\n      regex_constants::syntax_char,        /*F*/\n      regex_constants::syntax_char,        /*G*/\n      regex_constants::syntax_char,        /*H*/\n      regex_constants::syntax_char,        /*I*/\n      regex_constants::syntax_char,        /*J*/\n      regex_constants::syntax_char,        /*K*/\n      regex_constants::syntax_char,        /*L*/\n      regex_constants::syntax_char,        /*M*/\n      regex_constants::syntax_char,        /*N*/\n      regex_constants::syntax_char,        /*O*/\n      regex_constants::syntax_char,        /*P*/\n      regex_constants::syntax_char,        /*Q*/\n      regex_constants::syntax_char,        /*R*/\n      regex_constants::syntax_char,        /*S*/\n      regex_constants::syntax_char,        /*T*/\n      regex_constants::syntax_char,        /*U*/\n      regex_constants::syntax_char,        /*V*/\n      regex_constants::syntax_char,        /*W*/\n      regex_constants::syntax_char,        /*X*/\n      regex_constants::syntax_char,        /*Y*/\n      regex_constants::syntax_char,        /*Z*/\n      regex_constants::syntax_open_set,        /*[*/\n      regex_constants::syntax_escape,        /*\\*/\n      regex_constants::syntax_close_set,        /*]*/\n      regex_constants::syntax_caret,        /*^*/\n      regex_constants::syntax_char,        /*_*/\n      regex_constants::syntax_char,        /*`*/\n      regex_constants::syntax_char,        /*a*/\n      regex_constants::syntax_char,        /*b*/\n      regex_constants::syntax_char,        /*c*/\n      regex_constants::syntax_char,        /*d*/\n      regex_constants::syntax_char,        /*e*/\n      regex_constants::syntax_char,        /*f*/\n      regex_constants::syntax_char,        /*g*/\n      regex_constants::syntax_char,        /*h*/\n      regex_constants::syntax_char,        /*i*/\n      regex_constants::syntax_char,        /*j*/\n      regex_constants::syntax_char,        /*k*/\n      regex_constants::syntax_char,        /*l*/\n      regex_constants::syntax_char,        /*m*/\n      regex_constants::syntax_char,        /*n*/\n      regex_constants::syntax_char,        /*o*/\n      regex_constants::syntax_char,        /*p*/\n      regex_constants::syntax_char,        /*q*/\n      regex_constants::syntax_char,        /*r*/\n      regex_constants::syntax_char,        /*s*/\n      regex_constants::syntax_char,        /*t*/\n      regex_constants::syntax_char,        /*u*/\n      regex_constants::syntax_char,        /*v*/\n      regex_constants::syntax_char,        /*w*/\n      regex_constants::syntax_char,        /*x*/\n      regex_constants::syntax_char,        /*y*/\n      regex_constants::syntax_char,        /*z*/\n      regex_constants::syntax_open_brace,        /*{*/\n      regex_constants::syntax_or,        /*|*/\n      regex_constants::syntax_close_brace,        /*}*/\n      regex_constants::syntax_char,        /*~*/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n      regex_constants::syntax_char,        /**/\n   };\n\n   return char_syntax[(unsigned char)c];\n}\n\ninline regex_constants::escape_syntax_type  get_default_escape_syntax_type(char c)\n{\n   //\n   // char_syntax determines how the compiler treats a given character\n   // in a regular expression.\n   //\n   static regex_constants::escape_syntax_type char_syntax[] = {\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,     /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /* */    // 32\n      regex_constants::escape_type_identity,        /*!*/\n      regex_constants::escape_type_identity,        /*\"*/\n      regex_constants::escape_type_identity,        /*#*/\n      regex_constants::escape_type_identity,        /*$*/\n      regex_constants::escape_type_identity,        /*%*/\n      regex_constants::escape_type_identity,        /*&*/\n      regex_constants::escape_type_end_buffer,        /*'*/\n      regex_constants::syntax_open_mark,        /*(*/\n      regex_constants::syntax_close_mark,        /*)*/\n      regex_constants::escape_type_identity,        /***/\n      regex_constants::syntax_plus,                 /*+*/\n      regex_constants::escape_type_identity,        /*,*/\n      regex_constants::escape_type_identity,        /*-*/\n      regex_constants::escape_type_identity,        /*.*/\n      regex_constants::escape_type_identity,        /*/*/\n      regex_constants::escape_type_decimal,        /*0*/\n      regex_constants::escape_type_backref,        /*1*/\n      regex_constants::escape_type_backref,        /*2*/\n      regex_constants::escape_type_backref,        /*3*/\n      regex_constants::escape_type_backref,        /*4*/\n      regex_constants::escape_type_backref,        /*5*/\n      regex_constants::escape_type_backref,        /*6*/\n      regex_constants::escape_type_backref,        /*7*/\n      regex_constants::escape_type_backref,        /*8*/\n      regex_constants::escape_type_backref,        /*9*/\n      regex_constants::escape_type_identity,        /*:*/\n      regex_constants::escape_type_identity,        /*;*/\n      regex_constants::escape_type_left_word,        /*<*/\n      regex_constants::escape_type_identity,        /*=*/\n      regex_constants::escape_type_right_word,        /*>*/\n      regex_constants::syntax_question,              /*?*/\n      regex_constants::escape_type_identity,         /*@*/\n      regex_constants::escape_type_start_buffer,     /*A*/\n      regex_constants::escape_type_not_word_assert,  /*B*/\n      regex_constants::escape_type_C,                /*C*/\n      regex_constants::escape_type_not_class,        /*D*/\n      regex_constants::escape_type_E,                /*E*/\n      regex_constants::escape_type_not_class,        /*F*/\n      regex_constants::escape_type_G,                /*G*/\n      regex_constants::escape_type_not_class,        /*H*/\n      regex_constants::escape_type_not_class,        /*I*/\n      regex_constants::escape_type_not_class,        /*J*/\n      regex_constants::escape_type_reset_start_mark, /*K*/\n      regex_constants::escape_type_not_class,        /*L*/\n      regex_constants::escape_type_not_class,        /*M*/\n      regex_constants::escape_type_named_char,       /*N*/\n      regex_constants::escape_type_not_class,        /*O*/\n      regex_constants::escape_type_not_property,     /*P*/\n      regex_constants::escape_type_Q,                /*Q*/\n      regex_constants::escape_type_line_ending,      /*R*/\n      regex_constants::escape_type_not_class,        /*S*/\n      regex_constants::escape_type_not_class,        /*T*/\n      regex_constants::escape_type_not_class,        /*U*/\n      regex_constants::escape_type_not_class,        /*V*/\n      regex_constants::escape_type_not_class,        /*W*/\n      regex_constants::escape_type_X,                /*X*/\n      regex_constants::escape_type_not_class,        /*Y*/\n      regex_constants::escape_type_Z,                /*Z*/\n      regex_constants::escape_type_identity,        /*[*/\n      regex_constants::escape_type_identity,        /*\\*/\n      regex_constants::escape_type_identity,        /*]*/\n      regex_constants::escape_type_identity,        /*^*/\n      regex_constants::escape_type_identity,        /*_*/\n      regex_constants::escape_type_start_buffer,        /*`*/\n      regex_constants::escape_type_control_a,        /*a*/\n      regex_constants::escape_type_word_assert,        /*b*/\n      regex_constants::escape_type_ascii_control,        /*c*/\n      regex_constants::escape_type_class,        /*d*/\n      regex_constants::escape_type_e,        /*e*/\n      regex_constants::escape_type_control_f,       /*f*/\n      regex_constants::escape_type_extended_backref,  /*g*/\n      regex_constants::escape_type_class,        /*h*/\n      regex_constants::escape_type_class,        /*i*/\n      regex_constants::escape_type_class,        /*j*/\n      regex_constants::escape_type_extended_backref, /*k*/\n      regex_constants::escape_type_class,        /*l*/\n      regex_constants::escape_type_class,        /*m*/\n      regex_constants::escape_type_control_n,       /*n*/\n      regex_constants::escape_type_class,           /*o*/\n      regex_constants::escape_type_property,        /*p*/\n      regex_constants::escape_type_class,           /*q*/\n      regex_constants::escape_type_control_r,       /*r*/\n      regex_constants::escape_type_class,           /*s*/\n      regex_constants::escape_type_control_t,       /*t*/\n      regex_constants::escape_type_class,         /*u*/\n      regex_constants::escape_type_control_v,       /*v*/\n      regex_constants::escape_type_class,           /*w*/\n      regex_constants::escape_type_hex,             /*x*/\n      regex_constants::escape_type_class,           /*y*/\n      regex_constants::escape_type_end_buffer,      /*z*/\n      regex_constants::syntax_open_brace,           /*{*/\n      regex_constants::syntax_or,                   /*|*/\n      regex_constants::syntax_close_brace,          /*}*/\n      regex_constants::escape_type_identity,        /*~*/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n      regex_constants::escape_type_identity,        /**/\n   };\n\n   return char_syntax[(unsigned char)c];\n}\n\n// is charT c a combining character?\ninline bool  is_combining_implementation(std::uint_least16_t c)\n{\n   const std::uint_least16_t combining_ranges[] = { 0x0300, 0x0361,\n                           0x0483, 0x0486,\n                           0x0903, 0x0903,\n                           0x093E, 0x0940,\n                           0x0949, 0x094C,\n                           0x0982, 0x0983,\n                           0x09BE, 0x09C0,\n                           0x09C7, 0x09CC,\n                           0x09D7, 0x09D7,\n                           0x0A3E, 0x0A40,\n                           0x0A83, 0x0A83,\n                           0x0ABE, 0x0AC0,\n                           0x0AC9, 0x0ACC,\n                           0x0B02, 0x0B03,\n                           0x0B3E, 0x0B3E,\n                           0x0B40, 0x0B40,\n                           0x0B47, 0x0B4C,\n                           0x0B57, 0x0B57,\n                           0x0B83, 0x0B83,\n                           0x0BBE, 0x0BBF,\n                           0x0BC1, 0x0BCC,\n                           0x0BD7, 0x0BD7,\n                           0x0C01, 0x0C03,\n                           0x0C41, 0x0C44,\n                           0x0C82, 0x0C83,\n                           0x0CBE, 0x0CBE,\n                           0x0CC0, 0x0CC4,\n                           0x0CC7, 0x0CCB,\n                           0x0CD5, 0x0CD6,\n                           0x0D02, 0x0D03,\n                           0x0D3E, 0x0D40,\n                           0x0D46, 0x0D4C,\n                           0x0D57, 0x0D57,\n                           0x0F7F, 0x0F7F,\n                           0x20D0, 0x20E1,\n                           0x3099, 0x309A,\n                           0xFE20, 0xFE23,\n                           0xffff, 0xffff, };\n\n   const std::uint_least16_t* p = combining_ranges + 1;\n   while (*p < c) p += 2;\n   --p;\n   if ((c >= *p) && (c <= *(p + 1)))\n      return true;\n   return false;\n}\n\ntemplate <class charT>\ninline bool is_combining(charT c)\n{\n   return (c <= static_cast<charT>(0)) ? false : ((c >= static_cast<charT>((std::numeric_limits<uint_least16_t>::max)())) ? false : is_combining_implementation(static_cast<unsigned short>(c)));\n}\ntemplate <>\ninline bool is_combining<char>(char)\n{\n   return false;\n}\ntemplate <>\ninline bool is_combining<signed char>(signed char)\n{\n   return false;\n}\ntemplate <>\ninline bool is_combining<unsigned char>(unsigned char)\n{\n   return false;\n}\n#ifdef _MSC_VER\ntemplate<>\ninline bool is_combining<wchar_t>(wchar_t c)\n{\n   return is_combining_implementation(static_cast<unsigned short>(c));\n}\n#elif !defined(__DECCXX) && !defined(__osf__) && !defined(__OSF__) && defined(WCHAR_MIN) && (WCHAR_MIN == 0) && !defined(BOOST_NO_INTRINSIC_WCHAR_T)\n#if defined(WCHAR_MAX) && (WCHAR_MAX <= USHRT_MAX)\ntemplate<>\ninline bool is_combining<wchar_t>(wchar_t c)\n{\n   return is_combining_implementation(static_cast<unsigned short>(c));\n}\n#else\ntemplate<>\ninline bool is_combining<wchar_t>(wchar_t c)\n{\n   return (c >= (std::numeric_limits<uint_least16_t>::max)()) ? false : is_combining_implementation(static_cast<unsigned short>(c));\n}\n#endif\n#endif\n\n//\n// is a charT c a line separator?\n//\ntemplate <class charT>\ninline bool is_separator(charT c)\n{\n   return BOOST_REGEX_MAKE_BOOL(\n      (c == static_cast<charT>('\\n'))\n      || (c == static_cast<charT>('\\r'))\n      || (c == static_cast<charT>('\\f'))\n      || (static_cast<std::uint16_t>(c) == 0x2028u)\n      || (static_cast<std::uint16_t>(c) == 0x2029u)\n      || (static_cast<std::uint16_t>(c) == 0x85u));\n}\ntemplate <>\ninline bool is_separator<char>(char c)\n{\n   return BOOST_REGEX_MAKE_BOOL((c == '\\n') || (c == '\\r') || (c == '\\f'));\n}\n\n//\n// get a default collating element:\n//\ninline std::string  lookup_default_collate_name(const std::string& name)\n{\n   //\n   // these are the POSIX collating names:\n   //\n   static const char* def_coll_names[] = {\n   \"NUL\", \"SOH\", \"STX\", \"ETX\", \"EOT\", \"ENQ\", \"ACK\", \"alert\", \"backspace\", \"tab\", \"newline\",\n   \"vertical-tab\", \"form-feed\", \"carriage-return\", \"SO\", \"SI\", \"DLE\", \"DC1\", \"DC2\", \"DC3\", \"DC4\", \"NAK\",\n   \"SYN\", \"ETB\", \"CAN\", \"EM\", \"SUB\", \"ESC\", \"IS4\", \"IS3\", \"IS2\", \"IS1\", \"space\", \"exclamation-mark\",\n   \"quotation-mark\", \"number-sign\", \"dollar-sign\", \"percent-sign\", \"ampersand\", \"apostrophe\",\n   \"left-parenthesis\", \"right-parenthesis\", \"asterisk\", \"plus-sign\", \"comma\", \"hyphen\",\n   \"period\", \"slash\", \"zero\", \"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\",\n   \"colon\", \"semicolon\", \"less-than-sign\", \"equals-sign\", \"greater-than-sign\",\n   \"question-mark\", \"commercial-at\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\",\n   \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\", \"left-square-bracket\", \"backslash\",\n   \"right-square-bracket\", \"circumflex\", \"underscore\", \"grave-accent\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\",\n   \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"left-curly-bracket\",\n   \"vertical-line\", \"right-curly-bracket\", \"tilde\", \"DEL\", \"\",\n   };\n\n   // these multi-character collating elements\n   // should keep most Western-European locales\n   // happy - we should really localise these a\n   // little more - but this will have to do for\n   // now:\n\n   static const char* def_multi_coll[] = {\n      \"ae\",\n      \"Ae\",\n      \"AE\",\n      \"ch\",\n      \"Ch\",\n      \"CH\",\n      \"ll\",\n      \"Ll\",\n      \"LL\",\n      \"ss\",\n      \"Ss\",\n      \"SS\",\n      \"nj\",\n      \"Nj\",\n      \"NJ\",\n      \"dz\",\n      \"Dz\",\n      \"DZ\",\n      \"lj\",\n      \"Lj\",\n      \"LJ\",\n      \"\",\n   };\n\n   unsigned int i = 0;\n   while (*def_coll_names[i])\n   {\n      if (def_coll_names[i] == name)\n      {\n         return std::string(1, char(i));\n      }\n      ++i;\n   }\n   i = 0;\n   while (*def_multi_coll[i])\n   {\n      if (def_multi_coll[i] == name)\n      {\n         return def_multi_coll[i];\n      }\n      ++i;\n   }\n   return std::string();\n}\n\n//\n// get the state_id of a character classification, the individual\n// traits classes then transform that state_id into a bitmask:\n//\ntemplate <class charT>\nstruct character_pointer_range\n{\n   const charT* p1;\n   const charT* p2;\n\n   bool operator < (const character_pointer_range& r)const\n   {\n      return std::lexicographical_compare(p1, p2, r.p1, r.p2);\n   }\n   bool operator == (const character_pointer_range& r)const\n   {\n      // Not only do we check that the ranges are of equal size before\n      // calling std::equal, but there is no other algorithm available:\n      // not even a non-standard MS one.  So forward to unchecked_equal\n      // in the MS case.\n#ifdef __cpp_lib_robust_nonmodifying_seq_ops\n      return std::equal(p1, p2, r.p1, r.p2);\n#elif defined(BOOST_REGEX_MSVC)\n      if (((p2 - p1) != (r.p2 - r.p1)))\n         return false;\n      const charT* with = r.p1;\n      const charT* pos = p1;\n      while (pos != p2)\n         if (*pos++ != *with++) return false;\n      return true;\n\n#else\n      return ((p2 - p1) == (r.p2 - r.p1)) && std::equal(p1, p2, r.p1);\n#endif\n   }\n};\ntemplate <class charT>\nint get_default_class_id(const charT* p1, const charT* p2)\n{\n   static const charT data[73] = {\n      'a', 'l', 'n', 'u', 'm',\n      'a', 'l', 'p', 'h', 'a',\n      'b', 'l', 'a', 'n', 'k',\n      'c', 'n', 't', 'r', 'l',\n      'd', 'i', 'g', 'i', 't',\n      'g', 'r', 'a', 'p', 'h',\n      'l', 'o', 'w', 'e', 'r',\n      'p', 'r', 'i', 'n', 't',\n      'p', 'u', 'n', 'c', 't',\n      's', 'p', 'a', 'c', 'e',\n      'u', 'n', 'i', 'c', 'o', 'd', 'e',\n      'u', 'p', 'p', 'e', 'r',\n      'v',\n      'w', 'o', 'r', 'd',\n      'x', 'd', 'i', 'g', 'i', 't',\n   };\n\n   static const character_pointer_range<charT> ranges[21] =\n   {\n      {data+0, data+5,}, // alnum\n      {data+5, data+10,}, // alpha\n      {data+10, data+15,}, // blank\n      {data+15, data+20,}, // cntrl\n      {data+20, data+21,}, // d\n      {data+20, data+25,}, // digit\n      {data+25, data+30,}, // graph\n      {data+29, data+30,}, // h\n      {data+30, data+31,}, // l\n      {data+30, data+35,}, // lower\n      {data+35, data+40,}, // print\n      {data+40, data+45,}, // punct\n      {data+45, data+46,}, // s\n      {data+45, data+50,}, // space\n      {data+57, data+58,}, // u\n      {data+50, data+57,}, // unicode\n      {data+57, data+62,}, // upper\n      {data+62, data+63,}, // v\n      {data+63, data+64,}, // w\n      {data+63, data+67,}, // word\n      {data+67, data+73,}, // xdigit\n   };\n   const character_pointer_range<charT>* ranges_begin = ranges;\n   const character_pointer_range<charT>* ranges_end = ranges + (sizeof(ranges)/sizeof(ranges[0]));\n\n   character_pointer_range<charT> t = { p1, p2, };\n   const character_pointer_range<charT>* p = std::lower_bound(ranges_begin, ranges_end, t);\n   if((p != ranges_end) && (t == *p))\n      return static_cast<int>(p - ranges);\n   return -1;\n}\n\n//\n// helper functions:\n//\ntemplate <class charT>\nstd::ptrdiff_t global_length(const charT* p)\n{\n   std::ptrdiff_t n = 0;\n   while(*p)\n   {\n      ++p;\n      ++n;\n   }\n   return n;\n}\ntemplate<>\ninline std::ptrdiff_t global_length<char>(const char* p)\n{\n   return (std::strlen)(p);\n}\n#ifndef BOOST_NO_WREGEX\ntemplate<>\ninline std::ptrdiff_t global_length<wchar_t>(const wchar_t* p)\n{\n   return (std::ptrdiff_t)(std::wcslen)(p);\n}\n#endif\ntemplate <class charT>\ninline charT  global_lower(charT c)\n{\n   return c;\n}\ntemplate <class charT>\ninline charT  global_upper(charT c)\n{\n   return c;\n}\n\ninline char  do_global_lower(char c)\n{\n   return static_cast<char>((std::tolower)((unsigned char)c));\n}\n\ninline char  do_global_upper(char c)\n{\n   return static_cast<char>((std::toupper)((unsigned char)c));\n}\n#ifndef BOOST_NO_WREGEX\ninline wchar_t  do_global_lower(wchar_t c)\n{\n   return (std::towlower)(c);\n}\n\ninline wchar_t  do_global_upper(wchar_t c)\n{\n   return (std::towupper)(c);\n}\n#endif\n//\n// This sucks: declare template specialisations of global_lower/global_upper\n// that just forward to the non-template implementation functions.  We do\n// this because there is one compiler (Compaq Tru64 C++) that doesn't seem\n// to differentiate between templates and non-template overloads....\n// what's more, the primary template, plus all overloads have to be\n// defined in the same translation unit (if one is inline they all must be)\n// otherwise the \"local template instantiation\" compiler option can pick\n// the wrong instantiation when linking:\n//\ntemplate<> inline char  global_lower<char>(char c) { return do_global_lower(c); }\ntemplate<> inline char  global_upper<char>(char c) { return do_global_upper(c); }\n#ifndef BOOST_NO_WREGEX\ntemplate<> inline wchar_t  global_lower<wchar_t>(wchar_t c) { return do_global_lower(c); }\ntemplate<> inline wchar_t  global_upper<wchar_t>(wchar_t c) { return do_global_upper(c); }\n#endif\n\ntemplate <class charT>\nint global_value(charT c)\n{\n   static const charT zero = '0';\n   static const charT nine = '9';\n   static const charT a = 'a';\n   static const charT f = 'f';\n   static const charT A = 'A';\n   static const charT F = 'F';\n\n   if(c > f) return -1;\n   if(c >= a) return 10 + (c - a);\n   if(c > F) return -1;\n   if(c >= A) return 10 + (c - A);\n   if(c > nine) return -1;\n   if(c >= zero) return c - zero;\n   return -1;\n}\ntemplate <class charT, class traits>\nstd::intmax_t global_toi(const charT*& p1, const charT* p2, int radix, const traits& t)\n{\n   (void)t; // warning suppression\n   std::intmax_t limit = (std::numeric_limits<std::intmax_t>::max)() / radix;\n   std::intmax_t next_value = t.value(*p1, radix);\n   if((p1 == p2) || (next_value < 0) || (next_value >= radix))\n      return -1;\n   std::intmax_t result = 0;\n   while(p1 != p2)\n   {\n      next_value = t.value(*p1, radix);\n      if((next_value < 0) || (next_value >= radix))\n         break;\n      result *= radix;\n      result += next_value;\n      ++p1;\n      if (result > limit)\n         return -1;\n   }\n   return result;\n}\n\ntemplate <class charT>\ninline typename std::enable_if<(sizeof(charT) > 1), const charT*>::type get_escape_R_string()\n{\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#  pragma warning(disable:4309 4245)\n#endif\n   static const charT e1[] = { '(', '?', '-', 'x', ':', '(', '?', '>', '\\x0D', '\\x0A', '?',\n      '|', '[', '\\x0A', '\\x0B', '\\x0C', static_cast<charT>(0x85), static_cast<charT>(0x2028),\n      static_cast<charT>(0x2029), ']', ')', ')', '\\0' };\n   static const charT e2[] = { '(', '?', '-', 'x', ':', '(', '?', '>', '\\x0D', '\\x0A', '?',\n      '|', '[', '\\x0A', '\\x0B', '\\x0C', static_cast<charT>(0x85), ']', ')', ')', '\\0' };\n\n   charT c = static_cast<charT>(0x2029u);\n   bool b = (static_cast<unsigned>(c) == 0x2029u);\n\n   return (b ? e1 : e2);\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n}\n\ntemplate <class charT>\ninline typename std::enable_if<(sizeof(charT) == 1), const charT*>::type get_escape_R_string()\n{\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#  pragma warning(disable:4309 4245)\n#endif\n   static const charT e2[] = { \n      static_cast<charT>('('), \n      static_cast<charT>('?'), \n      static_cast<charT>('-'), \n      static_cast<charT>('x'), \n      static_cast<charT>(':'), \n      static_cast<charT>('('), \n      static_cast<charT>('?'), \n      static_cast<charT>('>'), \n      static_cast<charT>('\\x0D'), \n      static_cast<charT>('\\x0A'), \n      static_cast<charT>('?'),\n      static_cast<charT>('|'), \n      static_cast<charT>('['), \n      static_cast<charT>('\\x0A'), \n      static_cast<charT>('\\x0B'), \n      static_cast<charT>('\\x0C'), \n      static_cast<charT>('\\x85'), \n      static_cast<charT>(']'), \n      static_cast<charT>(')'), \n      static_cast<charT>(')'), \n      static_cast<charT>('\\0') \n   };\n   return e2;\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n}\n\n} // BOOST_REGEX_DETAIL_NS\n} // boost\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/regex_workaround.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2005\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         regex_workarounds.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares Misc workarounds.\n  */\n\n#ifndef BOOST_REGEX_WORKAROUND_HPP\n#define BOOST_REGEX_WORKAROUND_HPP\n\n#include <boost/regex/config.hpp>\n#include <algorithm>\n#include <stdexcept>\n#include <cstring>\n\n#ifndef BOOST_REGEX_STANDALONE\n#include <boost/detail/workaround.hpp>\n#include <boost/throw_exception.hpp>\n#endif\n\n#ifdef BOOST_REGEX_NO_BOOL\n#  define BOOST_REGEX_MAKE_BOOL(x) static_cast<bool>((x) ? true : false)\n#else\n#  define BOOST_REGEX_MAKE_BOOL(x) static_cast<bool>(x)\n#endif\n\n/*****************************************************************************\n *\n *  helper functions pointer_construct/pointer_destroy:\n *\n ****************************************************************************/\n\n#ifdef __cplusplus\nnamespace boost{ namespace BOOST_REGEX_DETAIL_NS{\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning (push)\n#pragma warning (disable : 4100)\n#endif\n\ntemplate <class T>\ninline void pointer_destroy(T* p)\n{ p->~T(); (void)p; }\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning (pop)\n#endif\n\ntemplate <class T>\ninline void pointer_construct(T* p, const T& t)\n{ new (p) T(t); }\n\n}} // namespaces\n#endif\n\n/*****************************************************************************\n *\n *  helper function copy:\n *\n ****************************************************************************/\n\n#if defined(BOOST_WORKAROUND)\n#if BOOST_WORKAROUND(BOOST_REGEX_MSVC, >= 1400) && defined(__STDC_WANT_SECURE_LIB__) && __STDC_WANT_SECURE_LIB__\n#define BOOST_REGEX_HAS_STRCPY_S\n#endif\n#endif\n\n#ifdef __cplusplus\nnamespace boost{ namespace BOOST_REGEX_DETAIL_NS{\n\n#if defined(BOOST_REGEX_MSVC) && (BOOST_REGEX_MSVC < 1910)\n   //\n   // MSVC 10 will either emit warnings or else refuse to compile\n   // code that makes perfectly legitimate use of std::copy, when\n   // the OutputIterator type is a user-defined class (apparently all user \n   // defined iterators are \"unsafe\").  What's more Microsoft have removed their\n   // non-standard \"unchecked\" versions, even though they are still in the MS\n   // documentation!! Work around this as best we can: \n   //\n   template<class InputIterator, class OutputIterator>\n   inline OutputIterator copy(\n      InputIterator first,\n      InputIterator last,\n      OutputIterator dest\n   )\n   {\n      while (first != last)\n         *dest++ = *first++;\n      return dest;\n   }\n#else \n   using std::copy;\n#endif \n\n\n#if defined(BOOST_REGEX_HAS_STRCPY_S)\n\n   // use safe versions of strcpy etc:\n   using ::strcpy_s;\n   using ::strcat_s;\n#else\n   inline std::size_t strcpy_s(\n      char *strDestination,\n      std::size_t sizeInBytes,\n      const char *strSource \n   )\n   {\n     std::size_t lenSourceWithNull = std::strlen(strSource) + 1;\n     if (lenSourceWithNull > sizeInBytes)\n         return 1;\n     std::memcpy(strDestination, strSource, lenSourceWithNull);\n      return 0;\n   }\n   inline std::size_t strcat_s(\n      char *strDestination,\n      std::size_t sizeInBytes,\n      const char *strSource \n   )\n   {\n     std::size_t lenSourceWithNull = std::strlen(strSource) + 1;\n     std::size_t lenDestination = std::strlen(strDestination);\n     if (lenSourceWithNull + lenDestination > sizeInBytes)\n         return 1;\n     std::memcpy(strDestination + lenDestination, strSource, lenSourceWithNull);\n      return 0;\n   }\n\n#endif\n\n   inline void overflow_error_if_not_zero(std::size_t i)\n   {\n      if(i)\n      {\n         std::overflow_error e(\"String buffer too small\");\n#ifndef BOOST_REGEX_STANDALONE\n         boost::throw_exception(e);\n#else\n         throw e;\n#endif\n      }\n   }\n\n}} // namespaces\n\n#endif // __cplusplus\n\n#endif // include guard\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/states.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         states.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares internal state machine structures.\n  */\n\n#ifndef BOOST_REGEX_V5_STATES_HPP\n#define BOOST_REGEX_V5_STATES_HPP\n\nnamespace boost{\nnamespace BOOST_REGEX_DETAIL_NS{\n\n/*** mask_type *******************************************************\nWhenever we have a choice of two alternatives, we use an array of bytes\nto indicate which of the two alternatives it is possible to take for any\ngiven input character.  If mask_take is set, then we can take the next \nstate, and if mask_skip is set then we can take the alternative.\n***********************************************************************/\nenum mask_type\n{\n   mask_take = 1,\n   mask_skip = 2,\n   mask_init = 4,\n   mask_any = mask_skip | mask_take,\n   mask_all = mask_any\n};\n\n/*** helpers **********************************************************\nThese helpers let us use function overload resolution to detect whether\nwe have narrow or wide character strings:\n***********************************************************************/\nstruct _narrow_type{};\nstruct _wide_type{};\ntemplate <class charT> struct is_byte;\ntemplate<>             struct is_byte<char>         { typedef _narrow_type width_type; };\ntemplate<>             struct is_byte<unsigned char>{ typedef _narrow_type width_type; };\ntemplate<>             struct is_byte<signed char>  { typedef _narrow_type width_type; };\ntemplate <class charT> struct is_byte               { typedef _wide_type width_type; };\n\n/*** enum syntax_element_type ******************************************\nEvery record in the state machine falls into one of the following types:\n***********************************************************************/\nenum syntax_element_type\n{\n   // start of a marked sub-expression, or perl-style (?...) extension\n   syntax_element_startmark = 0,\n   // end of a marked sub-expression, or perl-style (?...) extension\n   syntax_element_endmark = syntax_element_startmark + 1,\n   // any sequence of literal characters\n   syntax_element_literal = syntax_element_endmark + 1,\n   // start of line assertion: ^\n   syntax_element_start_line = syntax_element_literal + 1,\n   // end of line assertion $\n   syntax_element_end_line = syntax_element_start_line + 1,\n   // match any character: .\n   syntax_element_wild = syntax_element_end_line + 1,\n   // end of expression: we have a match when we get here\n   syntax_element_match = syntax_element_wild + 1,\n   // perl style word boundary: \\b\n   syntax_element_word_boundary = syntax_element_match + 1,\n   // perl style within word boundary: \\B\n   syntax_element_within_word = syntax_element_word_boundary + 1,\n   // start of word assertion: \\<\n   syntax_element_word_start = syntax_element_within_word + 1,\n   // end of word assertion: \\>\n   syntax_element_word_end = syntax_element_word_start + 1,\n   // start of buffer assertion: \\`\n   syntax_element_buffer_start = syntax_element_word_end + 1,\n   // end of buffer assertion: \\'\n   syntax_element_buffer_end = syntax_element_buffer_start + 1,\n   // backreference to previously matched sub-expression\n   syntax_element_backref = syntax_element_buffer_end + 1,\n   // either a wide character set [..] or one with multicharacter collating elements:\n   syntax_element_long_set = syntax_element_backref + 1,\n   // narrow character set: [...]\n   syntax_element_set = syntax_element_long_set + 1,\n   // jump to a new state in the machine:\n   syntax_element_jump = syntax_element_set + 1,\n   // choose between two production states:\n   syntax_element_alt = syntax_element_jump + 1,\n   // a repeat\n   syntax_element_rep = syntax_element_alt + 1,\n   // match a combining character sequence\n   syntax_element_combining = syntax_element_rep + 1,\n   // perl style soft buffer end: \\z\n   syntax_element_soft_buffer_end = syntax_element_combining + 1,\n   // perl style continuation: \\G\n   syntax_element_restart_continue = syntax_element_soft_buffer_end + 1,\n   // single character repeats:\n   syntax_element_dot_rep = syntax_element_restart_continue + 1,\n   syntax_element_char_rep = syntax_element_dot_rep + 1,\n   syntax_element_short_set_rep = syntax_element_char_rep + 1,\n   syntax_element_long_set_rep = syntax_element_short_set_rep + 1,\n   // a backstep for lookbehind repeats:\n   syntax_element_backstep = syntax_element_long_set_rep + 1,\n   // an assertion that a mark was matched:\n   syntax_element_assert_backref = syntax_element_backstep + 1,\n   syntax_element_toggle_case = syntax_element_assert_backref + 1,\n   // a recursive expression:\n   syntax_element_recurse = syntax_element_toggle_case + 1,\n   // Verbs:\n   syntax_element_fail = syntax_element_recurse + 1,\n   syntax_element_accept = syntax_element_fail + 1,\n   syntax_element_commit = syntax_element_accept + 1,\n   syntax_element_then = syntax_element_commit + 1\n};\n\n#ifdef BOOST_REGEX_DEBUG\n// dwa 09/26/00 - This is needed to suppress warnings about an ambiguous conversion\nstd::ostream& operator<<(std::ostream&, syntax_element_type);\n#endif\n\nstruct re_syntax_base;\n\n/*** union offset_type ************************************************\nPoints to another state in the machine.  During machine construction\nwe use integral offsets, but these are converted to pointers before\nexecution of the machine.\n***********************************************************************/\nunion offset_type\n{\n   re_syntax_base*   p;\n   std::ptrdiff_t    i;\n};\n\n/*** struct re_syntax_base ********************************************\nBase class for all states in the machine.\n***********************************************************************/\nstruct re_syntax_base\n{\n   syntax_element_type   type;         // what kind of state this is\n   offset_type           next;         // next state in the machine\n};\n\n/*** struct re_brace **************************************************\nA marked parenthesis.\n***********************************************************************/\nstruct re_brace : public re_syntax_base\n{\n   // The index to match, can be zero (don't mark the sub-expression)\n   // or negative (for perl style (?...) extensions):\n   int index;\n   bool icase;\n};\n\n/*** struct re_dot **************************************************\nMatch anything.\n***********************************************************************/\nenum\n{\n   dont_care = 1,\n   force_not_newline = 0,\n   force_newline = 2,\n\n   test_not_newline = 2,\n   test_newline = 3\n};\nstruct re_dot : public re_syntax_base\n{\n   unsigned char mask;\n};\n\n/*** struct re_literal ************************************************\nA string of literals, following this structure will be an \narray of characters: charT[length]\n***********************************************************************/\nstruct re_literal : public re_syntax_base\n{\n   unsigned int length;\n};\n\n/*** struct re_case ************************************************\nIndicates whether we are moving to a case insensive block or not\n***********************************************************************/\nstruct re_case : public re_syntax_base\n{\n   bool icase;\n};\n\n/*** struct re_set_long ***********************************************\nA wide character set of characters, following this structure will be\nan array of type charT:\nFirst csingles null-terminated strings\nThen 2 * cranges NULL terminated strings\nThen cequivalents NULL terminated strings\n***********************************************************************/\ntemplate <class mask_type>\nstruct re_set_long : public re_syntax_base\n{\n   unsigned int            csingles, cranges, cequivalents;\n   mask_type               cclasses;\n   mask_type               cnclasses;\n   bool                    isnot;\n   bool                    singleton;\n};\n\n/*** struct re_set ****************************************************\nA set of narrow-characters, matches any of _map which is none-zero\n***********************************************************************/\nstruct re_set : public re_syntax_base\n{\n   unsigned char _map[1 << CHAR_BIT];\n};\n\n/*** struct re_jump ***************************************************\nJump to a new location in the machine (not next).\n***********************************************************************/\nstruct re_jump : public re_syntax_base\n{\n   offset_type     alt;                 // location to jump to\n};\n\n/*** struct re_alt ***************************************************\nJump to a new location in the machine (possibly next).\n***********************************************************************/\nstruct re_alt : public re_jump\n{\n   unsigned char   _map[1 << CHAR_BIT]; // which characters can take the jump\n   unsigned int    can_be_null;         // true if we match a NULL string\n};\n\n/*** struct re_repeat *************************************************\nRepeat a section of the machine\n***********************************************************************/\nstruct re_repeat : public re_alt\n{\n   std::size_t   min, max;  // min and max allowable repeats\n   int           state_id;        // Unique identifier for this repeat\n   bool          leading;   // True if this repeat is at the start of the machine (lets us optimize some searches)\n   bool          greedy;    // True if this is a greedy repeat\n};\n\n/*** struct re_recurse ************************************************\nRecurse to a particular subexpression.\n**********************************************************************/\nstruct re_recurse : public re_jump\n{\n   int state_id;             // identifier of first nested repeat within the recursion.\n};\n\n/*** struct re_commit *************************************************\nUsed for the PRUNE, SKIP and COMMIT verbs which basically differ only in what happens\nif no match is found and we start searching forward.\n**********************************************************************/\nenum commit_type\n{\n   commit_prune,\n   commit_skip,\n   commit_commit\n};\nstruct re_commit : public re_syntax_base\n{\n   commit_type action;\n};\n\n/*** enum re_jump_size_type *******************************************\nProvides compiled size of re_jump structure (allowing for trailing alignment).\nWe provide this so we know how manybytes to insert when constructing the machine\n(The value of padding_mask is defined in regex_raw_buffer.hpp).\n***********************************************************************/\nenum re_jump_size_type\n{\n   re_jump_size = (sizeof(re_jump) + padding_mask) & ~(padding_mask),\n   re_repeater_size = (sizeof(re_repeat) + padding_mask) & ~(padding_mask),\n   re_alt_size = (sizeof(re_alt) + padding_mask) & ~(padding_mask)\n};\n\n/*** proc re_is_set_member *********************************************\nForward declaration: we'll need this one later...\n***********************************************************************/\n\ntemplate<class charT, class traits>\nstruct regex_data;\n\ntemplate <class iterator, class charT, class traits_type, class char_classT>\niterator  re_is_set_member(iterator next, \n                          iterator last, \n                          const re_set_long<char_classT>* set_, \n                          const regex_data<charT, traits_type>& e, bool icase);\n\n} // namespace BOOST_REGEX_DETAIL_NS\n\n} // namespace boost\n\n#endif\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/sub_match.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         sub_match.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares template class sub_match.\n  */\n\n#ifndef BOOST_REGEX_V5_SUB_MATCH_HPP\n#define BOOST_REGEX_V5_SUB_MATCH_HPP\n\nnamespace boost{\n\ntemplate <class BidiIterator>\nstruct sub_match : public std::pair<BidiIterator, BidiIterator>\n{\n   typedef typename std::iterator_traits<BidiIterator>::value_type       value_type;\n   typedef typename std::iterator_traits<BidiIterator>::difference_type  difference_type;\n   typedef          BidiIterator                                                     iterator_type;\n   typedef          BidiIterator                                                     iterator;\n   typedef          BidiIterator                                                     const_iterator;\n\n   bool matched;\n\n   sub_match() : std::pair<BidiIterator, BidiIterator>(), matched(false) {}\n   sub_match(BidiIterator i) : std::pair<BidiIterator, BidiIterator>(i, i), matched(false) {}\n   template <class T, class A>\n   operator std::basic_string<value_type, T, A> ()const\n   {\n      return matched ? std::basic_string<value_type, T, A>(this->first, this->second) : std::basic_string<value_type, T, A>();\n   }\n   difference_type  length()const\n   {\n      difference_type n = matched ? std::distance((BidiIterator)this->first, (BidiIterator)this->second) : 0;\n      return n;\n   }\n   std::basic_string<value_type> str()const\n   {\n      std::basic_string<value_type> result;\n      if(matched)\n      {\n         std::size_t len = std::distance((BidiIterator)this->first, (BidiIterator)this->second);\n         result.reserve(len);\n         BidiIterator i = this->first;\n         while(i != this->second)\n         {\n            result.append(1, *i);\n            ++i;\n         }\n      }\n      return result;\n   }\n   int compare(const sub_match& s)const\n   {\n      if(matched != s.matched)\n         return static_cast<int>(matched) - static_cast<int>(s.matched);\n      return str().compare(s.str());\n   }\n   int compare(const std::basic_string<value_type>& s)const\n   {\n      return str().compare(s);\n   }\n   int compare(const value_type* p)const\n   {\n      return str().compare(p);\n   }\n\n   bool operator==(const sub_match& that)const\n   { return compare(that) == 0; }\n   bool  operator !=(const sub_match& that)const\n   { return compare(that) != 0; }\n   bool operator<(const sub_match& that)const\n   { return compare(that) < 0; }\n   bool operator>(const sub_match& that)const\n   { return compare(that) > 0; }\n   bool operator<=(const sub_match& that)const\n   { return compare(that) <= 0; }\n   bool operator>=(const sub_match& that)const\n   { return compare(that) >= 0; }\n\n#ifdef BOOST_REGEX_MATCH_EXTRA\n   typedef std::vector<sub_match<BidiIterator> > capture_sequence_type;\n\n   const capture_sequence_type& captures()const\n   {\n      if(!m_captures) \n         m_captures.reset(new capture_sequence_type());\n      return *m_captures;\n   }\n   //\n   // Private implementation API: DO NOT USE!\n   //\n   capture_sequence_type& get_captures()const\n   {\n      if(!m_captures) \n         m_captures.reset(new capture_sequence_type());\n      return *m_captures;\n   }\n\nprivate:\n   mutable std::unique_ptr<capture_sequence_type> m_captures;\npublic:\n\n#endif\n   sub_match(const sub_match& that, bool \n#ifdef BOOST_REGEX_MATCH_EXTRA\n      deep_copy\n#endif\n      = true\n      ) \n      : std::pair<BidiIterator, BidiIterator>(that), \n        matched(that.matched) \n   {\n#ifdef BOOST_REGEX_MATCH_EXTRA\n      if(that.m_captures)\n         if(deep_copy)\n            m_captures.reset(new capture_sequence_type(*(that.m_captures)));\n#endif\n   }\n   sub_match& operator=(const sub_match& that)\n   {\n      this->first = that.first;\n      this->second = that.second;\n      matched = that.matched;\n#ifdef BOOST_REGEX_MATCH_EXTRA\n      if(that.m_captures)\n         get_captures() = *(that.m_captures);\n#endif\n      return *this;\n   }\n   //\n   // Make this type a range, for both Boost.Range, and C++11:\n   //\n   BidiIterator begin()const { return this->first; }\n   BidiIterator end()const { return this->second; }\n};\n\ntypedef sub_match<const char*> csub_match;\ntypedef sub_match<std::string::const_iterator> ssub_match;\n#ifndef BOOST_NO_WREGEX\ntypedef sub_match<const wchar_t*> wcsub_match;\ntypedef sub_match<std::wstring::const_iterator> wssub_match;\n#endif\n\n// comparison to std::basic_string<> part 1:\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator == (const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) == 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator != (const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) != 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator < (const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                 const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) < 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator <= (const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) <= 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator >= (const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) >= 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator > (const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                 const sub_match<RandomAccessIterator>& m)\n{ return s.compare(m.str()) > 0; }\n// comparison to std::basic_string<> part 2:\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator == (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) == 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator != (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) != 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator < (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) < 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator > (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) > 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator <= (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) <= 0; }\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline bool operator >= (const sub_match<RandomAccessIterator>& m,\n                  const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{ return m.str().compare(s) >= 0; }\n// comparison to const charT* part 1:\ntemplate <class RandomAccessIterator>\ninline bool operator == (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) == 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator != (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) != 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator > (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) > 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator < (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) < 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator >= (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) >= 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator <= (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const* s)\n{ return m.str().compare(s) <= 0; }\n// comparison to const charT* part 2:\ntemplate <class RandomAccessIterator>\ninline bool operator == (typename std::iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) == 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator != (typename std::iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) != 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator < (typename std::iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) > 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator > (typename std::iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) < 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator <= (typename std::iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) >= 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator >= (typename std::iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(s) <= 0; }\n\n// comparison to const charT& part 1:\ntemplate <class RandomAccessIterator>\ninline bool operator == (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) == 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator != (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) != 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator > (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) > 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator < (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) < 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator >= (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) >= 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator <= (const sub_match<RandomAccessIterator>& m,\n                  typename std::iterator_traits<RandomAccessIterator>::value_type const& s)\n{ return m.str().compare(0, m.length(), &s, 1) <= 0; }\n// comparison to const charT* part 2:\ntemplate <class RandomAccessIterator>\ninline bool operator == (typename std::iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) == 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator != (typename std::iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) != 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator < (typename std::iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) > 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator > (typename std::iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) < 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator <= (typename std::iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) >= 0; }\ntemplate <class RandomAccessIterator>\ninline bool operator >= (typename std::iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{ return m.str().compare(0, m.length(), &s, 1) <= 0; }\n\n// addition operators:\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator> \noperator + (const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s,\n                  const sub_match<RandomAccessIterator>& m)\n{\n   std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator> result;\n   result.reserve(s.size() + m.length() + 1);\n   return result.append(s).append(m.first, m.second);\n}\ntemplate <class RandomAccessIterator, class traits, class Allocator>\ninline std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator> \noperator + (const sub_match<RandomAccessIterator>& m,\n            const std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator>& s)\n{\n   std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type, traits, Allocator> result;\n   result.reserve(s.size() + m.length() + 1);\n   return result.append(m.first, m.second).append(s);\n}\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type> \noperator + (typename std::iterator_traits<RandomAccessIterator>::value_type const* s,\n                  const sub_match<RandomAccessIterator>& m)\n{\n   std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type> result;\n   result.reserve(std::char_traits<typename std::iterator_traits<RandomAccessIterator>::value_type>::length(s) + m.length() + 1);\n   return result.append(s).append(m.first, m.second);\n}\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type> \noperator + (const sub_match<RandomAccessIterator>& m,\n            typename std::iterator_traits<RandomAccessIterator>::value_type const * s)\n{\n   std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type> result;\n   result.reserve(std::char_traits<typename std::iterator_traits<RandomAccessIterator>::value_type>::length(s) + m.length() + 1);\n   return result.append(m.first, m.second).append(s);\n}\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type> \noperator + (typename std::iterator_traits<RandomAccessIterator>::value_type const& s,\n                  const sub_match<RandomAccessIterator>& m)\n{\n   std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type> result;\n   result.reserve(m.length() + 2);\n   return result.append(1, s).append(m.first, m.second);\n}\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type> \noperator + (const sub_match<RandomAccessIterator>& m,\n            typename std::iterator_traits<RandomAccessIterator>::value_type const& s)\n{\n   std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type> result;\n   result.reserve(m.length() + 2);\n   return result.append(m.first, m.second).append(1, s);\n}\ntemplate <class RandomAccessIterator>\ninline std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type> \noperator + (const sub_match<RandomAccessIterator>& m1,\n            const sub_match<RandomAccessIterator>& m2)\n{\n   std::basic_string<typename std::iterator_traits<RandomAccessIterator>::value_type> result;\n   result.reserve(m1.length() + m2.length() + 1);\n   return result.append(m1.first, m1.second).append(m2.first, m2.second);\n}\ntemplate <class charT, class traits, class RandomAccessIterator>\nstd::basic_ostream<charT, traits>&\n   operator << (std::basic_ostream<charT, traits>& os,\n                const sub_match<RandomAccessIterator>& s)\n{\n   return (os << s.str());\n}\n\n} // namespace boost\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/syntax_type.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         syntax_type.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression synatx type enumerator.\n  */\n\n#ifndef BOOST_REGEX_SYNTAX_TYPE_HPP\n#define BOOST_REGEX_SYNTAX_TYPE_HPP\n\nnamespace boost{\nnamespace regex_constants{\n\ntypedef unsigned char syntax_type;\n\n//\n// values chosen are binary compatible with previous version:\n//\nstatic const syntax_type syntax_char = 0;\nstatic const syntax_type syntax_open_mark = 1;\nstatic const syntax_type syntax_close_mark = 2;\nstatic const syntax_type syntax_dollar = 3;\nstatic const syntax_type syntax_caret = 4;\nstatic const syntax_type syntax_dot = 5;\nstatic const syntax_type syntax_star = 6;\nstatic const syntax_type syntax_plus = 7;\nstatic const syntax_type syntax_question = 8;\nstatic const syntax_type syntax_open_set = 9;\nstatic const syntax_type syntax_close_set = 10;\nstatic const syntax_type syntax_or = 11;\nstatic const syntax_type syntax_escape = 12;\nstatic const syntax_type syntax_dash = 14;\nstatic const syntax_type syntax_open_brace = 15;\nstatic const syntax_type syntax_close_brace = 16;\nstatic const syntax_type syntax_digit = 17;\nstatic const syntax_type syntax_comma = 27;\nstatic const syntax_type syntax_equal = 37;\nstatic const syntax_type syntax_colon = 36;\nstatic const syntax_type syntax_not = 53;\n\n// extensions:\n\nstatic const syntax_type syntax_hash = 13;\nstatic const syntax_type syntax_newline = 26;\n\n// escapes:\n\ntypedef syntax_type escape_syntax_type;\n\nstatic const escape_syntax_type escape_type_word_assert = 18;\nstatic const escape_syntax_type escape_type_not_word_assert = 19;\nstatic const escape_syntax_type escape_type_control_f = 29;\nstatic const escape_syntax_type escape_type_control_n = 30;\nstatic const escape_syntax_type escape_type_control_r = 31;\nstatic const escape_syntax_type escape_type_control_t = 32;\nstatic const escape_syntax_type escape_type_control_v = 33;\nstatic const escape_syntax_type escape_type_ascii_control = 35;\nstatic const escape_syntax_type escape_type_hex = 34;\nstatic const escape_syntax_type escape_type_unicode = 0; // not used\nstatic const escape_syntax_type escape_type_identity = 0; // not used\nstatic const escape_syntax_type escape_type_backref = syntax_digit;\nstatic const escape_syntax_type escape_type_decimal = syntax_digit; // not used\nstatic const escape_syntax_type escape_type_class = 22; \nstatic const escape_syntax_type escape_type_not_class = 23; \n\n// extensions:\n\nstatic const escape_syntax_type escape_type_left_word = 20;\nstatic const escape_syntax_type escape_type_right_word = 21;\nstatic const escape_syntax_type escape_type_start_buffer = 24;                 // for \\`\nstatic const escape_syntax_type escape_type_end_buffer = 25;                   // for \\'\nstatic const escape_syntax_type escape_type_control_a = 28;                    // for \\a\nstatic const escape_syntax_type escape_type_e = 38;                            // for \\e\nstatic const escape_syntax_type escape_type_E = 47;                            // for \\Q\\E\nstatic const escape_syntax_type escape_type_Q = 48;                            // for \\Q\\E\nstatic const escape_syntax_type escape_type_X = 49;                            // for \\X\nstatic const escape_syntax_type escape_type_C = 50;                            // for \\C\nstatic const escape_syntax_type escape_type_Z = 51;                            // for \\Z\nstatic const escape_syntax_type escape_type_G = 52;                            // for \\G\n\nstatic const escape_syntax_type escape_type_property = 54;                     // for \\p\nstatic const escape_syntax_type escape_type_not_property = 55;                 // for \\P\nstatic const escape_syntax_type escape_type_named_char = 56;                   // for \\N\nstatic const escape_syntax_type escape_type_extended_backref = 57;             // for \\g\nstatic const escape_syntax_type escape_type_reset_start_mark = 58;             // for \\K\nstatic const escape_syntax_type escape_type_line_ending = 59;                  // for \\R\n\nstatic const escape_syntax_type syntax_max = 60;\n\n}\n}\n\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/u32regex_iterator.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         u32regex_iterator.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides u32regex_iterator implementation.\n  */\n\n#ifndef BOOST_REGEX_V5_U32REGEX_ITERATOR_HPP\n#define BOOST_REGEX_V5_U32REGEX_ITERATOR_HPP\n\nnamespace boost{\n\ntemplate <class BidirectionalIterator>\nclass u32regex_iterator_implementation \n{\n   typedef u32regex regex_type;\n\n   match_results<BidirectionalIterator> what;  // current match\n   BidirectionalIterator                base;  // start of sequence\n   BidirectionalIterator                end;   // end of sequence\n   const regex_type                     re;   // the expression\n   match_flag_type                      flags; // flags for matching\n\npublic:\n   u32regex_iterator_implementation(const regex_type* p, BidirectionalIterator last, match_flag_type f)\n      : base(), end(last), re(*p), flags(f){}\n   bool init(BidirectionalIterator first)\n   {\n      base = first;\n      return u32regex_search(first, end, what, re, flags, base);\n   }\n   bool compare(const u32regex_iterator_implementation& that)\n   {\n      if(this == &that) return true;\n      return (&re.get_data() == &that.re.get_data()) && (end == that.end) && (flags == that.flags) && (what[0].first == that.what[0].first) && (what[0].second == that.what[0].second);\n   }\n   const match_results<BidirectionalIterator>& get()\n   { return what; }\n   bool next()\n   {\n      //if(what.prefix().first != what[0].second)\n      //   flags |= match_prev_avail;\n      BidirectionalIterator next_start = what[0].second;\n      match_flag_type f(flags);\n      if(!what.length())\n         f |= regex_constants::match_not_initial_null;\n      //if(base != next_start)\n      //   f |= regex_constants::match_not_bob;\n      bool result = u32regex_search(next_start, end, what, re, f, base);\n      if(result)\n         what.set_base(base);\n      return result;\n   }\nprivate:\n   u32regex_iterator_implementation& operator=(const u32regex_iterator_implementation&);\n};\n\ntemplate <class BidirectionalIterator>\nclass u32regex_iterator \n{\nprivate:\n   typedef u32regex_iterator_implementation<BidirectionalIterator> impl;\n   typedef std::shared_ptr<impl> pimpl;\npublic:\n   typedef          u32regex                                                regex_type;\n   typedef          match_results<BidirectionalIterator>                    value_type;\n   typedef typename std::iterator_traits<BidirectionalIterator>::difference_type \n                                                                            difference_type;\n   typedef          const value_type*                                       pointer;\n   typedef          const value_type&                                       reference; \n   typedef          std::forward_iterator_tag                               iterator_category;\n   \n   u32regex_iterator(){}\n   u32regex_iterator(BidirectionalIterator a, BidirectionalIterator b, \n                  const regex_type& re, \n                  match_flag_type m = match_default)\n                  : pdata(new impl(&re, b, m))\n   {\n      if(!pdata->init(a))\n      {\n         pdata.reset();\n      }\n   }\n   u32regex_iterator(const u32regex_iterator& that)\n      : pdata(that.pdata) {}\n   u32regex_iterator& operator=(const u32regex_iterator& that)\n   {\n      pdata = that.pdata;\n      return *this;\n   }\n   bool operator==(const u32regex_iterator& that)const\n   { \n      if((pdata.get() == 0) || (that.pdata.get() == 0))\n         return pdata.get() == that.pdata.get();\n      return pdata->compare(*(that.pdata.get())); \n   }\n   bool operator!=(const u32regex_iterator& that)const\n   { return !(*this == that); }\n   const value_type& operator*()const\n   { return pdata->get(); }\n   const value_type* operator->()const\n   { return &(pdata->get()); }\n   u32regex_iterator& operator++()\n   {\n      cow();\n      if(0 == pdata->next())\n      {\n         pdata.reset();\n      }\n      return *this;\n   }\n   u32regex_iterator operator++(int)\n   {\n      u32regex_iterator result(*this);\n      ++(*this);\n      return result;\n   }\nprivate:\n\n   pimpl pdata;\n\n   void cow()\n   {\n      // copy-on-write\n      if(pdata.get() && (pdata.use_count() > 1))\n      {\n         pdata.reset(new impl(*(pdata.get())));\n      }\n   }\n};\n\ntypedef u32regex_iterator<const char*> utf8regex_iterator;\ntypedef u32regex_iterator<const UChar*> utf16regex_iterator;\ntypedef u32regex_iterator<const UChar32*> utf32regex_iterator;\n\ninline u32regex_iterator<const char*> make_u32regex_iterator(const char* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_iterator<const char*>(p, p+std::strlen(p), e, m);\n}\n#ifndef BOOST_NO_WREGEX\ninline u32regex_iterator<const wchar_t*> make_u32regex_iterator(const wchar_t* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_iterator<const wchar_t*>(p, p+std::wcslen(p), e, m);\n}\n#endif\n#if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T)\ninline u32regex_iterator<const UChar*> make_u32regex_iterator(const UChar* p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_iterator<const UChar*>(p, p+u_strlen(p), e, m);\n}\n#endif\ntemplate <class charT, class Traits, class Alloc>\ninline u32regex_iterator<typename std::basic_string<charT, Traits, Alloc>::const_iterator> make_u32regex_iterator(const std::basic_string<charT, Traits, Alloc>& p, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   typedef typename std::basic_string<charT, Traits, Alloc>::const_iterator iter_type;\n   return u32regex_iterator<iter_type>(p.begin(), p.end(), e, m);\n}\ninline u32regex_iterator<const UChar*> make_u32regex_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_iterator<const UChar*>(s.getBuffer(), s.getBuffer() + s.length(), e, m);\n}\n\n} // namespace boost\n\n#endif // BOOST_REGEX_V5_REGEX_ITERATOR_HPP\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/u32regex_token_iterator.hpp",
    "content": "/*\n *\n * Copyright (c) 2003\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         u32regex_token_iterator.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Provides u32regex_token_iterator implementation.\n  */\n\n#ifndef BOOST_REGEX_V5_U32REGEX_TOKEN_ITERATOR_HPP\n#define BOOST_REGEX_V5_U32REGEX_TOKEN_ITERATOR_HPP\n\nnamespace boost{\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(push)\n#  pragma warning(disable:4700)\n#endif\n\ntemplate <class BidirectionalIterator>\nclass u32regex_token_iterator_implementation \n{\n   typedef u32regex                              regex_type;\n   typedef sub_match<BidirectionalIterator>      value_type;\n\n   match_results<BidirectionalIterator> what;   // current match\n   BidirectionalIterator                end;    // end of search area\n   BidirectionalIterator                base;   // start of search area\n   const regex_type                     re;     // the expression\n   match_flag_type                      flags;  // match flags\n   value_type                           result; // the current string result\n   int                                  N;      // the current sub-expression being enumerated\n   std::vector<int>                     subs;   // the sub-expressions to enumerate\n\npublic:\n   u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, int sub, match_flag_type f)\n      : end(last), re(*p), flags(f){ subs.push_back(sub); }\n   u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const std::vector<int>& v, match_flag_type f)\n      : end(last), re(*p), flags(f), subs(v){}\n   template <std::size_t CN>\n   u32regex_token_iterator_implementation(const regex_type* p, BidirectionalIterator last, const int (&submatches)[CN], match_flag_type f)\n      : end(last), re(*p), flags(f)\n   {\n      for(std::size_t i = 0; i < CN; ++i)\n      {\n         subs.push_back(submatches[i]);\n      }\n   }\n\n   bool init(BidirectionalIterator first)\n   {\n      base = first;\n      N = 0;\n      if(u32regex_search(first, end, what, re, flags, base) == true)\n      {\n         N = 0;\n         result = ((subs[N] == -1) ? what.prefix() : what[(int)subs[N]]);\n         return true;\n      }\n      else if((subs[N] == -1) && (first != end))\n      {\n         result.first = first;\n         result.second = end;\n         result.matched = (first != end);\n         N = -1;\n         return true;\n      }\n      return false;\n   }\n   bool compare(const u32regex_token_iterator_implementation& that)\n   {\n      if(this == &that) return true;\n      return (&re.get_data() == &that.re.get_data()) \n         && (end == that.end) \n         && (flags == that.flags) \n         && (N == that.N) \n         && (what[0].first == that.what[0].first) \n         && (what[0].second == that.what[0].second);\n   }\n   const value_type& get()\n   { return result; }\n   bool next()\n   {\n      if(N == -1)\n         return false;\n      if(N+1 < (int)subs.size())\n      {\n         ++N;\n         result =((subs[N] == -1) ? what.prefix() : what[subs[N]]);\n         return true;\n      }\n      //if(what.prefix().first != what[0].second)\n      //   flags |= match_prev_avail | regex_constants::match_not_bob;\n      BidirectionalIterator last_end(what[0].second);\n      if(u32regex_search(last_end, end, what, re, ((what[0].first == what[0].second) ? flags | regex_constants::match_not_initial_null : flags), base))\n      {\n         N =0;\n         result =((subs[N] == -1) ? what.prefix() : what[subs[N]]);\n         return true;\n      }\n      else if((last_end != end) && (subs[0] == -1))\n      {\n         N =-1;\n         result.first = last_end;\n         result.second = end;\n         result.matched = (last_end != end);\n         return true;\n      }\n      return false;\n   }\nprivate:\n   u32regex_token_iterator_implementation& operator=(const u32regex_token_iterator_implementation&);\n};\n\ntemplate <class BidirectionalIterator>\nclass u32regex_token_iterator \n{\nprivate:\n   typedef u32regex_token_iterator_implementation<BidirectionalIterator> impl;\n   typedef std::shared_ptr<impl> pimpl;\npublic:\n   typedef          u32regex                                                regex_type;\n   typedef          sub_match<BidirectionalIterator>                        value_type;\n   typedef typename std::iterator_traits<BidirectionalIterator>::difference_type \n                                                                            difference_type;\n   typedef          const value_type*                                       pointer;\n   typedef          const value_type&                                       reference; \n   typedef          std::forward_iterator_tag                               iterator_category;\n   \n   u32regex_token_iterator(){}\n   u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, \n                        int submatch = 0, match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatch, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n   u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, \n                        const std::vector<int>& submatches, match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatches, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n   template <std::size_t N>\n   u32regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re,\n                        const int (&submatches)[N], match_flag_type m = match_default)\n                        : pdata(new impl(&re, b, submatches, m))\n   {\n      if(!pdata->init(a))\n         pdata.reset();\n   }\n   u32regex_token_iterator(const u32regex_token_iterator& that)\n      : pdata(that.pdata) {}\n   u32regex_token_iterator& operator=(const u32regex_token_iterator& that)\n   {\n      pdata = that.pdata;\n      return *this;\n   }\n   bool operator==(const u32regex_token_iterator& that)const\n   { \n      if((pdata.get() == 0) || (that.pdata.get() == 0))\n         return pdata.get() == that.pdata.get();\n      return pdata->compare(*(that.pdata.get())); \n   }\n   bool operator!=(const u32regex_token_iterator& that)const\n   { return !(*this == that); }\n   const value_type& operator*()const\n   { return pdata->get(); }\n   const value_type* operator->()const\n   { return &(pdata->get()); }\n   u32regex_token_iterator& operator++()\n   {\n      cow();\n      if(0 == pdata->next())\n      {\n         pdata.reset();\n      }\n      return *this;\n   }\n   u32regex_token_iterator operator++(int)\n   {\n      u32regex_token_iterator result(*this);\n      ++(*this);\n      return result;\n   }\nprivate:\n\n   pimpl pdata;\n\n   void cow()\n   {\n      // copy-on-write\n      if(pdata.get() && (pdata.use_count() > 1))\n      {\n         pdata.reset(new impl(*(pdata.get())));\n      }\n   }\n};\n\ntypedef u32regex_token_iterator<const char*> utf8regex_token_iterator;\ntypedef u32regex_token_iterator<const UChar*> utf16regex_token_iterator;\ntypedef u32regex_token_iterator<const UChar32*> utf32regex_token_iterator;\n\n// construction from an integral sub_match state_id:\ninline u32regex_token_iterator<const char*> make_u32regex_token_iterator(const char* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const char*>(p, p+std::strlen(p), e, submatch, m);\n}\n#ifndef BOOST_NO_WREGEX\ninline u32regex_token_iterator<const wchar_t*> make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const wchar_t*>(p, p+std::wcslen(p), e, submatch, m);\n}\n#endif\n#if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T)\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const UChar* p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(p, p+u_strlen(p), e, submatch, m);\n}\n#endif\ntemplate <class charT, class Traits, class Alloc>\ninline u32regex_token_iterator<typename std::basic_string<charT, Traits, Alloc>::const_iterator> make_u32regex_token_iterator(const std::basic_string<charT, Traits, Alloc>& p, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   typedef typename std::basic_string<charT, Traits, Alloc>::const_iterator iter_type;\n   return u32regex_token_iterator<iter_type>(p.begin(), p.end(), e, submatch, m);\n}\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m);\n}\n\n// construction from a reference to an array:\ntemplate <std::size_t N>\ninline u32regex_token_iterator<const char*> make_u32regex_token_iterator(const char* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const char*>(p, p+std::strlen(p), e, submatch, m);\n}\n#ifndef BOOST_NO_WREGEX\ntemplate <std::size_t N>\ninline u32regex_token_iterator<const wchar_t*> make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const wchar_t*>(p, p+std::wcslen(p), e, submatch, m);\n}\n#endif\n#if !defined(BOOST_REGEX_UCHAR_IS_WCHAR_T)\ntemplate <std::size_t N>\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const UChar* p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(p, p+u_strlen(p), e, submatch, m);\n}\n#endif\ntemplate <class charT, class Traits, class Alloc, std::size_t N>\ninline u32regex_token_iterator<typename std::basic_string<charT, Traits, Alloc>::const_iterator> make_u32regex_token_iterator(const std::basic_string<charT, Traits, Alloc>& p, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   typedef typename std::basic_string<charT, Traits, Alloc>::const_iterator iter_type;\n   return u32regex_token_iterator<iter_type>(p.begin(), p.end(), e, submatch, m);\n}\ntemplate <std::size_t N>\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const int (&submatch)[N], regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m);\n}\n\n// construction from a vector of sub_match state_id's:\ninline u32regex_token_iterator<const char*> make_u32regex_token_iterator(const char* p, const u32regex& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const char*>(p, p+std::strlen(p), e, submatch, m);\n}\n#ifndef BOOST_NO_WREGEX\ninline u32regex_token_iterator<const wchar_t*> make_u32regex_token_iterator(const wchar_t* p, const u32regex& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const wchar_t*>(p, p+std::wcslen(p), e, submatch, m);\n}\n#endif\n#if !defined(U_WCHAR_IS_UTF16) && (U_SIZEOF_WCHAR_T != 2)\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const UChar* p, const u32regex& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(p, p+u_strlen(p), e, submatch, m);\n}\n#endif\ntemplate <class charT, class Traits, class Alloc>\ninline u32regex_token_iterator<typename std::basic_string<charT, Traits, Alloc>::const_iterator> make_u32regex_token_iterator(const std::basic_string<charT, Traits, Alloc>& p, const u32regex& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   typedef typename std::basic_string<charT, Traits, Alloc>::const_iterator iter_type;\n   return u32regex_token_iterator<iter_type>(p.begin(), p.end(), e, submatch, m);\n}\ninline u32regex_token_iterator<const UChar*> make_u32regex_token_iterator(const U_NAMESPACE_QUALIFIER UnicodeString& s, const u32regex& e, const std::vector<int>& submatch, regex_constants::match_flag_type m = regex_constants::match_default)\n{\n   return u32regex_token_iterator<const UChar*>(s.getBuffer(), s.getBuffer() + s.length(), e, submatch, m);\n}\n\n#ifdef BOOST_REGEX_MSVC\n#  pragma warning(pop)\n#endif\n\n} // namespace boost\n\n#endif // BOOST_REGEX_V5_REGEX_TOKEN_ITERATOR_HPP\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/unicode_iterator.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         unicode_iterator.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Iterator adapters for converting between different Unicode encodings.\n  */\n\n/****************************************************************************\n\nContents:\n~~~~~~~~~\n\n1) Read Only, Input Adapters:\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\ntemplate <class BaseIterator, class U8Type = std::uint8_t>\nclass u32_to_u8_iterator;\n\nAdapts sequence of UTF-32 code points to \"look like\" a sequence of UTF-8.\n\ntemplate <class BaseIterator, class U32Type = std::uint32_t>\nclass u8_to_u32_iterator;\n\nAdapts sequence of UTF-8 code points to \"look like\" a sequence of UTF-32.\n\ntemplate <class BaseIterator, class U16Type = std::uint16_t>\nclass u32_to_u16_iterator;\n\nAdapts sequence of UTF-32 code points to \"look like\" a sequence of UTF-16.\n\ntemplate <class BaseIterator, class U32Type = std::uint32_t>\nclass u16_to_u32_iterator;\n\nAdapts sequence of UTF-16 code points to \"look like\" a sequence of UTF-32.\n\n2) Single pass output iterator adapters:\n\ntemplate <class BaseIterator>\nclass utf8_output_iterator;\n\nAccepts UTF-32 code points and forwards them on as UTF-8 code points.\n\ntemplate <class BaseIterator>\nclass utf16_output_iterator;\n\nAccepts UTF-32 code points and forwards them on as UTF-16 code points.\n\n****************************************************************************/\n\n#ifndef BOOST_REGEX_UNICODE_ITERATOR_HPP\n#define BOOST_REGEX_UNICODE_ITERATOR_HPP\n#include <cstdint>\n#include <boost/regex/config.hpp>\n#include <stdexcept>\n#include <sstream>\n#include <ios>\n#include <limits.h> // CHAR_BIT\n\n#ifndef BOOST_REGEX_STANDALONE\n#include <boost/throw_exception.hpp>\n#endif\n\nnamespace boost{\n\nnamespace detail{\n\nstatic const std::uint16_t high_surrogate_base = 0xD7C0u;\nstatic const std::uint16_t low_surrogate_base = 0xDC00u;\nstatic const std::uint32_t ten_bit_mask = 0x3FFu;\n\ninline bool is_high_surrogate(std::uint16_t v)\n{\n   return (v & 0xFFFFFC00u) == 0xd800u;\n}\ninline bool is_low_surrogate(std::uint16_t v)\n{\n   return (v & 0xFFFFFC00u) == 0xdc00u;\n}\ntemplate <class T>\ninline bool is_surrogate(T v)\n{\n   return (v & 0xFFFFF800u) == 0xd800;\n}\n\ninline unsigned utf8_byte_count(std::uint8_t c)\n{\n   // if the most significant bit with a zero in it is in position\n   // 8-N then there are N bytes in this UTF-8 sequence:\n   std::uint8_t mask = 0x80u;\n   unsigned result = 0;\n   while(c & mask)\n   {\n      ++result;\n      mask >>= 1;\n   }\n   return (result == 0) ? 1 : ((result > 4) ? 4 : result);\n}\n\ninline unsigned utf8_trailing_byte_count(std::uint8_t c)\n{\n   return utf8_byte_count(c) - 1;\n}\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4100)\n#endif\n#ifndef BOOST_NO_EXCEPTIONS\nBOOST_REGEX_NORETURN\n#endif\ninline void invalid_utf32_code_point(std::uint32_t val)\n{\n   std::stringstream ss;\n   ss << \"Invalid UTF-32 code point U+\" << std::showbase << std::hex << val << \" encountered while trying to encode UTF-16 sequence\";\n   std::out_of_range e(ss.str());\n#ifndef BOOST_REGEX_STANDALONE\n   boost::throw_exception(e);\n#else\n   throw e;\n#endif\n}\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n\n\n} // namespace detail\n\ntemplate <class BaseIterator, class U16Type = std::uint16_t>\nclass u32_to_u16_iterator\n{\n   typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;\n\n   static_assert(sizeof(base_value_type)*CHAR_BIT == 32, \"Incorrectly sized template argument\");\n   static_assert(sizeof(U16Type)*CHAR_BIT == 16, \"Incorrectly sized template argument\");\n\npublic:\n   typedef std::ptrdiff_t     difference_type;\n   typedef U16Type            value_type;\n   typedef value_type const*  pointer;\n   typedef value_type const   reference;\n   typedef std::bidirectional_iterator_tag iterator_category;\n\n   reference operator*()const\n   {\n      if(m_current == 2)\n         extract_current();\n      return m_values[m_current];\n   }\n   bool operator==(const u32_to_u16_iterator& that)const\n   {\n      if(m_position == that.m_position)\n      {\n         // Both m_currents must be equal, or both even\n         // this is the same as saying their sum must be even:\n         return (m_current + that.m_current) & 1u ? false : true;\n      }\n      return false;\n   }\n   bool operator!=(const u32_to_u16_iterator& that)const\n   {\n      return !(*this == that);\n   }\n   u32_to_u16_iterator& operator++()\n   {\n      // if we have a pending read then read now, so that we know whether\n      // to skip a position, or move to a low-surrogate:\n      if(m_current == 2)\n      {\n         // pending read:\n         extract_current();\n      }\n      // move to the next surrogate position:\n      ++m_current;\n      // if we've reached the end skip a position:\n      if(m_values[m_current] == 0)\n      {\n         m_current = 2;\n         ++m_position;\n      }\n      return *this;\n   }\n   u32_to_u16_iterator operator++(int)\n   {\n      u32_to_u16_iterator r(*this);\n      ++(*this);\n      return r;\n   }\n   u32_to_u16_iterator& operator--()\n   {\n      if(m_current != 1)\n      {\n         // decrementing an iterator always leads to a valid position:\n         --m_position;\n         extract_current();\n         m_current = m_values[1] ? 1 : 0;\n      }\n      else\n      {\n         m_current = 0;\n      }\n      return *this;\n   }\n   u32_to_u16_iterator operator--(int)\n   {\n      u32_to_u16_iterator r(*this);\n      --(*this);\n      return r;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\n   // construct:\n   u32_to_u16_iterator() : m_position(), m_current(0)\n   {\n      m_values[0] = 0;\n      m_values[1] = 0;\n      m_values[2] = 0;\n   }\n   u32_to_u16_iterator(BaseIterator b) : m_position(b), m_current(2)\n   {\n      m_values[0] = 0;\n      m_values[1] = 0;\n      m_values[2] = 0;\n   }\nprivate:\n\n   void extract_current()const\n   {\n      // begin by checking for a code point out of range:\n      std::uint32_t v = *m_position;\n      if(v >= 0x10000u)\n      {\n         if(v > 0x10FFFFu)\n            detail::invalid_utf32_code_point(*m_position);\n         // split into two surrogates:\n         m_values[0] = static_cast<U16Type>(v >> 10) + detail::high_surrogate_base;\n         m_values[1] = static_cast<U16Type>(v & detail::ten_bit_mask) + detail::low_surrogate_base;\n         m_current = 0;\n         BOOST_REGEX_ASSERT(detail::is_high_surrogate(m_values[0]));\n         BOOST_REGEX_ASSERT(detail::is_low_surrogate(m_values[1]));\n      }\n      else\n      {\n         // 16-bit code point:\n         m_values[0] = static_cast<U16Type>(*m_position);\n         m_values[1] = 0;\n         m_current = 0;\n         // value must not be a surrogate:\n         if(detail::is_surrogate(m_values[0]))\n            detail::invalid_utf32_code_point(*m_position);\n      }\n   }\n   BaseIterator m_position;\n   mutable U16Type m_values[3];\n   mutable unsigned m_current;\n};\n\ntemplate <class BaseIterator, class U32Type = std::uint32_t>\nclass u16_to_u32_iterator\n{\n   // special values for pending iterator reads:\n   static const U32Type pending_read = 0xffffffffu;\n\n   typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;\n\n   static_assert(sizeof(base_value_type)*CHAR_BIT == 16, \"Incorrectly sized template argument\");\n   static_assert(sizeof(U32Type)*CHAR_BIT == 32, \"Incorrectly sized template argument\");\n\npublic:\n   typedef std::ptrdiff_t     difference_type;\n   typedef U32Type            value_type;\n   typedef value_type const*  pointer;\n   typedef value_type const   reference;\n   typedef std::bidirectional_iterator_tag iterator_category;\n\n   reference operator*()const\n   {\n      if(m_value == pending_read)\n         extract_current();\n      return m_value;\n   }\n   bool operator==(const u16_to_u32_iterator& that)const\n   {\n      return m_position == that.m_position;\n   }\n   bool operator!=(const u16_to_u32_iterator& that)const\n   {\n      return !(*this == that);\n   }\n   u16_to_u32_iterator& operator++()\n   {\n      // skip high surrogate first if there is one:\n      if(detail::is_high_surrogate(*m_position)) ++m_position;\n      ++m_position;\n      m_value = pending_read;\n      return *this;\n   }\n   u16_to_u32_iterator operator++(int)\n   {\n      u16_to_u32_iterator r(*this);\n      ++(*this);\n      return r;\n   }\n   u16_to_u32_iterator& operator--()\n   {\n      --m_position;\n      // if we have a low surrogate then go back one more:\n      if(detail::is_low_surrogate(*m_position)) \n         --m_position;\n      m_value = pending_read;\n      return *this;\n   }\n   u16_to_u32_iterator operator--(int)\n   {\n      u16_to_u32_iterator r(*this);\n      --(*this);\n      return r;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\n   // construct:\n   u16_to_u32_iterator() : m_position()\n   {\n      m_value = pending_read;\n   }\n   u16_to_u32_iterator(BaseIterator b) : m_position(b)\n   {\n      m_value = pending_read;\n   }\n   //\n   // Range checked version:\n   //\n   u16_to_u32_iterator(BaseIterator b, BaseIterator start, BaseIterator end) : m_position(b)\n   {\n      m_value = pending_read;\n      //\n      // The range must not start with a low surrogate, or end in a high surrogate,\n      // otherwise we run the risk of running outside the underlying input range.\n      // Likewise b must not be located at a low surrogate.\n      //\n      std::uint16_t val;\n      if(start != end)\n      {\n         if((b != start) && (b != end))\n         {\n            val = *b;\n            if(detail::is_surrogate(val) && ((val & 0xFC00u) == 0xDC00u))\n               invalid_code_point(val);\n         }\n         val = *start;\n         if(detail::is_surrogate(val) && ((val & 0xFC00u) == 0xDC00u))\n            invalid_code_point(val);\n         val = *--end;\n         if(detail::is_high_surrogate(val))\n            invalid_code_point(val);\n      }\n   }\nprivate:\n   static void invalid_code_point(std::uint16_t val)\n   {\n      std::stringstream ss;\n      ss << \"Misplaced UTF-16 surrogate U+\" << std::showbase << std::hex << val << \" encountered while trying to encode UTF-32 sequence\";\n      std::out_of_range e(ss.str());\n#ifndef BOOST_REGEX_STANDALONE\n      boost::throw_exception(e);\n#else\n      throw e;\n#endif\n   }\n   void extract_current()const\n   {\n      m_value = static_cast<U32Type>(static_cast< std::uint16_t>(*m_position));\n      // if the last value is a high surrogate then adjust m_position and m_value as needed:\n      if(detail::is_high_surrogate(*m_position))\n      {\n         // precondition; next value must have be a low-surrogate:\n         BaseIterator next(m_position);\n         std::uint16_t t = *++next;\n         if((t & 0xFC00u) != 0xDC00u)\n            invalid_code_point(t);\n         m_value = (m_value - detail::high_surrogate_base) << 10;\n         m_value |= (static_cast<U32Type>(static_cast< std::uint16_t>(t)) & detail::ten_bit_mask);\n      }\n      // postcondition; result must not be a surrogate:\n      if(detail::is_surrogate(m_value))\n         invalid_code_point(static_cast< std::uint16_t>(m_value));\n   }\n   BaseIterator m_position;\n   mutable U32Type m_value;\n};\n\ntemplate <class BaseIterator, class U8Type = std::uint8_t>\nclass u32_to_u8_iterator\n{\n   typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;\n\n   static_assert(sizeof(base_value_type)*CHAR_BIT == 32, \"Incorrectly sized template argument\");\n   static_assert(sizeof(U8Type)*CHAR_BIT == 8, \"Incorrectly sized template argument\");\n\npublic:\n   typedef std::ptrdiff_t     difference_type;\n   typedef U8Type             value_type;\n   typedef value_type const*  pointer;\n   typedef value_type const   reference;\n   typedef std::bidirectional_iterator_tag iterator_category;\n\n   reference operator*()const\n   {\n      if(m_current == 4)\n         extract_current();\n      return m_values[m_current];\n   }\n   bool operator==(const u32_to_u8_iterator& that)const\n   {\n      if(m_position == that.m_position)\n      {\n         // either the m_current's must be equal, or one must be 0 and \n         // the other 4: which means neither must have bits 1 or 2 set:\n         return (m_current == that.m_current)\n            || (((m_current | that.m_current) & 3) == 0);\n      }\n      return false;\n   }\n   bool operator!=(const u32_to_u8_iterator& that)const\n   {\n      return !(*this == that);\n   }\n   u32_to_u8_iterator& operator++()\n   {\n      // if we have a pending read then read now, so that we know whether\n      // to skip a position, or move to a low-surrogate:\n      if(m_current == 4)\n      {\n         // pending read:\n         extract_current();\n      }\n      // move to the next surrogate position:\n      ++m_current;\n      // if we've reached the end skip a position:\n      if(m_values[m_current] == 0)\n      {\n         m_current = 4;\n         ++m_position;\n      }\n      return *this;\n   }\n   u32_to_u8_iterator operator++(int)\n   {\n      u32_to_u8_iterator r(*this);\n      ++(*this);\n      return r;\n   }\n   u32_to_u8_iterator& operator--()\n   {\n      if((m_current & 3) == 0)\n      {\n         --m_position;\n         extract_current();\n         m_current = 3;\n         while(m_current && (m_values[m_current] == 0))\n            --m_current;\n      }\n      else\n         --m_current;\n      return *this;\n   }\n   u32_to_u8_iterator operator--(int)\n   {\n      u32_to_u8_iterator r(*this);\n      --(*this);\n      return r;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\n   // construct:\n   u32_to_u8_iterator() : m_position(), m_current(0)\n   {\n      m_values[0] = 0;\n      m_values[1] = 0;\n      m_values[2] = 0;\n      m_values[3] = 0;\n      m_values[4] = 0;\n   }\n   u32_to_u8_iterator(BaseIterator b) : m_position(b), m_current(4)\n   {\n      m_values[0] = 0;\n      m_values[1] = 0;\n      m_values[2] = 0;\n      m_values[3] = 0;\n      m_values[4] = 0;\n   }\nprivate:\n\n   void extract_current()const\n   {\n      std::uint32_t c = *m_position;\n      if(c > 0x10FFFFu)\n         detail::invalid_utf32_code_point(c);\n      if(c < 0x80u)\n      {\n         m_values[0] = static_cast<unsigned char>(c);\n         m_values[1] = static_cast<unsigned char>(0u);\n         m_values[2] = static_cast<unsigned char>(0u);\n         m_values[3] = static_cast<unsigned char>(0u);\n      }\n      else if(c < 0x800u)\n      {\n         m_values[0] = static_cast<unsigned char>(0xC0u + (c >> 6));\n         m_values[1] = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n         m_values[2] = static_cast<unsigned char>(0u);\n         m_values[3] = static_cast<unsigned char>(0u);\n      }\n      else if(c < 0x10000u)\n      {\n         m_values[0] = static_cast<unsigned char>(0xE0u + (c >> 12));\n         m_values[1] = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));\n         m_values[2] = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n         m_values[3] = static_cast<unsigned char>(0u);\n      }\n      else\n      {\n         m_values[0] = static_cast<unsigned char>(0xF0u + (c >> 18));\n         m_values[1] = static_cast<unsigned char>(0x80u + ((c >> 12) & 0x3Fu));\n         m_values[2] = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));\n         m_values[3] = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n      }\n      m_current= 0;\n   }\n   BaseIterator m_position;\n   mutable U8Type m_values[5];\n   mutable unsigned m_current;\n};\n\ntemplate <class BaseIterator, class U32Type = std::uint32_t>\nclass u8_to_u32_iterator\n{\n   // special values for pending iterator reads:\n   static const U32Type pending_read = 0xffffffffu;\n\n   typedef typename std::iterator_traits<BaseIterator>::value_type base_value_type;\n\n   static_assert(sizeof(base_value_type)*CHAR_BIT == 8, \"Incorrectly sized template argument\");\n   static_assert(sizeof(U32Type)*CHAR_BIT == 32, \"Incorrectly sized template argument\");\n\npublic:\n   typedef std::ptrdiff_t     difference_type;\n   typedef U32Type            value_type;\n   typedef value_type const*  pointer;\n   typedef value_type const   reference;\n   typedef std::bidirectional_iterator_tag iterator_category;\n\n   reference operator*()const\n   {\n      if(m_value == pending_read)\n         extract_current();\n      return m_value;\n   }\n   bool operator==(const u8_to_u32_iterator& that)const\n   {\n      return m_position == that.m_position;\n   }\n   bool operator!=(const u8_to_u32_iterator& that)const\n   {\n      return !(*this == that);\n   }\n   u8_to_u32_iterator& operator++()\n   {\n      // We must not start with a continuation character:\n      if((static_cast<std::uint8_t>(*m_position) & 0xC0) == 0x80)\n         invalid_sequence();\n      // skip high surrogate first if there is one:\n      unsigned c = detail::utf8_byte_count(*m_position);\n      if(m_value == pending_read)\n      {\n         // Since we haven't read in a value, we need to validate the code points:\n         for(unsigned i = 0; i < c; ++i)\n         {\n            ++m_position;\n            // We must have a continuation byte:\n            if((i != c - 1) && ((static_cast<std::uint8_t>(*m_position) & 0xC0) != 0x80))\n               invalid_sequence();\n         }\n      }\n      else\n      {\n         std::advance(m_position, c);\n      }\n      m_value = pending_read;\n      return *this;\n   }\n   u8_to_u32_iterator operator++(int)\n   {\n      u8_to_u32_iterator r(*this);\n      ++(*this);\n      return r;\n   }\n   u8_to_u32_iterator& operator--()\n   {\n      // Keep backtracking until we don't have a trailing character:\n      unsigned count = 0;\n      while((*--m_position & 0xC0u) == 0x80u) ++count;\n      // now check that the sequence was valid:\n      if(count != detail::utf8_trailing_byte_count(*m_position))\n         invalid_sequence();\n      m_value = pending_read;\n      return *this;\n   }\n   u8_to_u32_iterator operator--(int)\n   {\n      u8_to_u32_iterator r(*this);\n      --(*this);\n      return r;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\n   // construct:\n   u8_to_u32_iterator() : m_position()\n   {\n      m_value = pending_read;\n   }\n   u8_to_u32_iterator(BaseIterator b) : m_position(b)\n   {\n      m_value = pending_read;\n   }\n   //\n   // Checked constructor:\n   //\n   u8_to_u32_iterator(BaseIterator b, BaseIterator start, BaseIterator end) : m_position(b)\n   {\n      m_value = pending_read;\n      //\n      // We must not start with a continuation character, or end with a \n      // truncated UTF-8 sequence otherwise we run the risk of going past\n      // the start/end of the underlying sequence:\n      //\n      if(start != end)\n      {\n         unsigned char v = *start;\n         if((v & 0xC0u) == 0x80u)\n            invalid_sequence();\n         if((b != start) && (b != end) && ((*b & 0xC0u) == 0x80u))\n            invalid_sequence();\n         BaseIterator pos = end;\n         do\n         {\n            v = *--pos;\n         }\n         while((start != pos) && ((v & 0xC0u) == 0x80u));\n         std::ptrdiff_t extra = detail::utf8_byte_count(v);\n         if(std::distance(pos, end) < extra)\n            invalid_sequence();\n      }\n   }\nprivate:\n   static void invalid_sequence()\n   {\n      std::out_of_range e(\"Invalid UTF-8 sequence encountered while trying to encode UTF-32 character\");\n#ifndef BOOST_REGEX_STANDALONE\n      boost::throw_exception(e);\n#else\n      throw e;\n#endif\n   }\n   void extract_current()const\n   {\n      m_value = static_cast<U32Type>(static_cast< std::uint8_t>(*m_position));\n      // we must not have a continuation character:\n      if((m_value & 0xC0u) == 0x80u)\n         invalid_sequence();\n      // see how many extra bytes we have:\n      unsigned extra = detail::utf8_trailing_byte_count(*m_position);\n      // extract the extra bits, 6 from each extra byte:\n      BaseIterator next(m_position);\n      for(unsigned c = 0; c < extra; ++c)\n      {\n         ++next;\n         m_value <<= 6;\n         // We must have a continuation byte:\n         if((static_cast<std::uint8_t>(*next) & 0xC0) != 0x80)\n            invalid_sequence();\n         m_value += static_cast<std::uint8_t>(*next) & 0x3Fu;\n      }\n      // we now need to remove a few of the leftmost bits, but how many depends\n      // upon how many extra bytes we've extracted:\n      static const std::uint32_t masks[4] = \n      {\n         0x7Fu,\n         0x7FFu,\n         0xFFFFu,\n         0x1FFFFFu,\n      };\n      m_value &= masks[extra];\n      // check the result is in range:\n      if(m_value > static_cast<U32Type>(0x10FFFFu))\n         invalid_sequence();\n      // The result must not be a surrogate:\n      if((m_value >= static_cast<U32Type>(0xD800)) && (m_value <= static_cast<U32Type>(0xDFFF)))\n         invalid_sequence();\n      // We should not have had an invalidly encoded UTF8 sequence:\n      if((extra > 0) && (m_value <= static_cast<U32Type>(masks[extra - 1])))\n         invalid_sequence();\n   }\n   BaseIterator m_position;\n   mutable U32Type m_value;\n};\n\ntemplate <class BaseIterator>\nclass utf16_output_iterator\n{\npublic:\n   typedef void                                   difference_type;\n   typedef void                                   value_type;\n   typedef std::uint32_t*                         pointer;\n   typedef std::uint32_t&                         reference;\n   typedef std::output_iterator_tag               iterator_category;\n\n   utf16_output_iterator(const BaseIterator& b)\n      : m_position(b){}\n   utf16_output_iterator(const utf16_output_iterator& that)\n      : m_position(that.m_position){}\n   utf16_output_iterator& operator=(const utf16_output_iterator& that)\n   {\n      m_position = that.m_position;\n      return *this;\n   }\n   const utf16_output_iterator& operator*()const\n   {\n      return *this;\n   }\n   void operator=(std::uint32_t val)const\n   {\n      push(val);\n   }\n   utf16_output_iterator& operator++()\n   {\n      return *this;\n   }\n   utf16_output_iterator& operator++(int)\n   {\n      return *this;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\nprivate:\n   void push(std::uint32_t v)const\n   {\n      if(v >= 0x10000u)\n      {\n         // begin by checking for a code point out of range:\n         if(v > 0x10FFFFu)\n            detail::invalid_utf32_code_point(v);\n         // split into two surrogates:\n         *m_position++ = static_cast<std::uint16_t>(v >> 10) + detail::high_surrogate_base;\n         *m_position++ = static_cast<std::uint16_t>(v & detail::ten_bit_mask) + detail::low_surrogate_base;\n      }\n      else\n      {\n         // 16-bit code point:\n         // value must not be a surrogate:\n         if(detail::is_surrogate(v))\n            detail::invalid_utf32_code_point(v);\n         *m_position++ = static_cast<std::uint16_t>(v);\n      }\n   }\n   mutable BaseIterator m_position;\n};\n\ntemplate <class BaseIterator>\nclass utf8_output_iterator\n{\npublic:\n   typedef void                                   difference_type;\n   typedef void                                   value_type;\n   typedef std::uint32_t*                       pointer;\n   typedef std::uint32_t&                       reference;\n   typedef std::output_iterator_tag               iterator_category;\n\n   utf8_output_iterator(const BaseIterator& b)\n      : m_position(b){}\n   utf8_output_iterator(const utf8_output_iterator& that)\n      : m_position(that.m_position){}\n   utf8_output_iterator& operator=(const utf8_output_iterator& that)\n   {\n      m_position = that.m_position;\n      return *this;\n   }\n   const utf8_output_iterator& operator*()const\n   {\n      return *this;\n   }\n   void operator=(std::uint32_t val)const\n   {\n      push(val);\n   }\n   utf8_output_iterator& operator++()\n   {\n      return *this;\n   }\n   utf8_output_iterator& operator++(int)\n   {\n      return *this;\n   }\n   BaseIterator base()const\n   {\n      return m_position;\n   }\nprivate:\n   void push(std::uint32_t c)const\n   {\n      if(c > 0x10FFFFu)\n         detail::invalid_utf32_code_point(c);\n      if(c < 0x80u)\n      {\n         *m_position++ = static_cast<unsigned char>(c);\n      }\n      else if(c < 0x800u)\n      {\n         *m_position++ = static_cast<unsigned char>(0xC0u + (c >> 6));\n         *m_position++ = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n      }\n      else if(c < 0x10000u)\n      {\n         *m_position++ = static_cast<unsigned char>(0xE0u + (c >> 12));\n         *m_position++ = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));\n         *m_position++ = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n      }\n      else\n      {\n         *m_position++ = static_cast<unsigned char>(0xF0u + (c >> 18));\n         *m_position++ = static_cast<unsigned char>(0x80u + ((c >> 12) & 0x3Fu));\n         *m_position++ = static_cast<unsigned char>(0x80u + ((c >> 6) & 0x3Fu));\n         *m_position++ = static_cast<unsigned char>(0x80u + (c & 0x3Fu));\n      }\n   }\n   mutable BaseIterator m_position;\n};\n\n} // namespace boost\n\n#endif // BOOST_REGEX_UNICODE_ITERATOR_HPP\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex/v5/w32_regex_traits.hpp",
    "content": "/*\n *\n * Copyright (c) 2004\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org for most recent version.\n  *   FILE         w32_regex_traits.hpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares regular expression traits class w32_regex_traits.\n  */\n\n#ifndef BOOST_W32_REGEX_TRAITS_HPP_INCLUDED\n#define BOOST_W32_REGEX_TRAITS_HPP_INCLUDED\n\n#ifndef BOOST_REGEX_NO_WIN32_LOCALE\n\n#include <boost/regex/pattern_except.hpp>\n#include <boost/regex/v5/regex_traits_defaults.hpp>\n#ifdef BOOST_HAS_THREADS\n#include <mutex>\n#endif\n#include <boost/regex/v5/primary_transform.hpp>\n#include <boost/regex/v5/object_cache.hpp>\n\n#if defined(_MSC_VER) && !defined(_WIN32_WCE) && !defined(UNDER_CE)\n#pragma comment(lib, \"user32.lib\")\n#endif\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(push)\n#pragma warning(disable:4786)\n#if BOOST_REGEX_MSVC < 1910\n#pragma warning(disable:4800)\n#endif\n#endif\n\n#ifndef BASETYPES\n//\n// windows.h not included, so lets forward declare what we need:\n//\n#ifndef NO_STRICT\n#ifndef STRICT\n#define STRICT 1\n#endif\n#endif\n\n#if defined(STRICT)\n#define BOOST_RE_DETAIL_DECLARE_HANDLE(x) struct x##__; typedef struct x##__ *x\n#else\n#define BOOST_RE_DETAIL_DECLARE_HANDLE(x) typedef void* x\n#endif\n//\n// This must be in the global namespace:\n//\nextern \"C\" {\n\n   BOOST_RE_DETAIL_DECLARE_HANDLE(HINSTANCE);\n   typedef HINSTANCE HMODULE;\n}\n#endif\n\nnamespace boost{ \n\n//\n// forward declaration is needed by some compilers:\n//\ntemplate <class charT>\nclass w32_regex_traits;\n   \nnamespace BOOST_REGEX_DETAIL_NS{\n\n//\n// start by typedeffing the types we'll need:\n//\ntypedef unsigned long lcid_type;        // placeholder for LCID.\ntypedef std::shared_ptr<void> cat_type; // placeholder for dll HANDLE.\n\n//\n// then add wrappers around the actual Win32 API's (ie implementation hiding):\n//\nlcid_type  w32_get_default_locale();\nbool  w32_is_lower(char, lcid_type);\n#ifndef BOOST_NO_WREGEX\nbool  w32_is_lower(wchar_t, lcid_type);\n#endif\nbool  w32_is_upper(char, lcid_type);\n#ifndef BOOST_NO_WREGEX\nbool  w32_is_upper(wchar_t, lcid_type);\n#endif\ncat_type  w32_cat_open(const std::string& name);\nstd::string  w32_cat_get(const cat_type& cat, lcid_type state_id, int i, const std::string& def);\n#ifndef BOOST_NO_WREGEX\nstd::wstring  w32_cat_get(const cat_type& cat, lcid_type state_id, int i, const std::wstring& def);\n#endif\nstd::string  w32_transform(lcid_type state_id, const char* p1, const char* p2);\n#ifndef BOOST_NO_WREGEX\nstd::wstring  w32_transform(lcid_type state_id, const wchar_t* p1, const wchar_t* p2);\n#endif\nchar  w32_tolower(char c, lcid_type);\n#ifndef BOOST_NO_WREGEX\nwchar_t  w32_tolower(wchar_t c, lcid_type);\n#endif\nchar  w32_toupper(char c, lcid_type);\n#ifndef BOOST_NO_WREGEX\nwchar_t  w32_toupper(wchar_t c, lcid_type);\n#endif\nbool  w32_is(lcid_type, std::uint32_t mask, char c);\n#ifndef BOOST_NO_WREGEX\nbool  w32_is(lcid_type, std::uint32_t mask, wchar_t c);\n#endif\n\n#ifndef BASETYPES\n//\n// Forward declarations of the small number of windows types and API's we use:\n//\n\n#if !defined(__LP64__)\nusing dword = unsigned long;\n#else\nusing DWORD = unsigned int;\n#endif\nusing word = unsigned short;\nusing lctype = dword;\n\nstatic constexpr dword ct_ctype1 = 0x00000001;\nstatic constexpr dword c1_upper = 0x0001;      // upper case\nstatic constexpr dword c1_lower = 0x0002;      // lower case\nstatic constexpr dword c1_digit = 0x0004;      // decimal digits\nstatic constexpr dword c1_space = 0x0008;      // spacing characters\nstatic constexpr dword c1_punct = 0x0010;      // punctuation characters\nstatic constexpr dword c1_cntrl = 0x0020;      // control characters\nstatic constexpr dword c1_blank = 0x0040;      // blank characters\nstatic constexpr dword c1_xdigit = 0x0080;      // other digits\nstatic constexpr dword c1_alpha = 0x0100;      // any linguistic character\nstatic constexpr dword c1_defined = 0x0200;      // defined character\nstatic constexpr unsigned int cp_acp = 0;\nstatic constexpr dword lcmap_lowercase = 0x00000100;\nstatic constexpr dword lcmap_uppercase = 0x00000200;\nstatic constexpr dword lcmap_sortkey = 0x00000400;  // WC sort key (normalize)\nstatic constexpr lctype locale_idefaultansicodepage = 0x00001004;\n\n# ifdef UNDER_CE\n#  ifndef WINAPI\n#   ifndef _WIN32_WCE_EMULATION\n#    define BOOST_RE_STDCALL __cdecl     // Note this doesn't match the desktop definition\n#   else\n#    define BOOST_RE_STDCALL __stdcall\n#   endif\n#  endif\n# else\n#  if defined(_M_IX86) || defined(__i386__)\n#   define BOOST_RE_STDCALL __stdcall\n#  else\n    // On architectures other than 32-bit x86 __stdcall is ignored. Clang also issues a warning.\n#   define BOOST_RE_STDCALL\n#  endif\n# endif\n\n#if defined (WIN32_PLATFORM_PSPC)\n#define BOOST_RE_IMPORT __declspec( dllimport )\n#elif defined (_WIN32_WCE)\n#define BOOST_RE_IMPORT\n#else\n#define BOOST_RE_IMPORT __declspec( dllimport )\n#endif\n\nextern \"C\" {\n\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL FreeLibrary(HMODULE hLibModule);\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL LCMapStringA(lcid_type Locale, dword dwMapFlags, const char* lpSrcStr, int cchSrc, char* lpDestStr, int cchDest);\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL LCMapStringW(lcid_type Locale, dword dwMapFlags, const wchar_t* lpSrcStr, int cchSrc, wchar_t* lpDestStr, int cchDest);\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL MultiByteToWideChar(unsigned int CodePage, dword dwFlags, const char* lpMultiByteStr, int cbMultiByte, wchar_t* lpWideCharStr, int cchWideChar);\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL LCMapStringW(lcid_type Locale, dword dwMapFlags, const wchar_t* lpSrcStr, int cchSrc, wchar_t* lpDestStr, int cchDest);\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL WideCharToMultiByte(unsigned int CodePage, dword dwFlags, const wchar_t* lpWideCharStr, int cchWideChar, char* lpMultiByteStr, int cbMultiByte, const char* lpDefaultChar, int* lpUsedDefaultChar);\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL GetStringTypeExA(lcid_type Locale, dword dwInfoType, const char* lpSrcStr, int cchSrc, word* lpCharType);\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL GetStringTypeExW(lcid_type Locale, dword dwInfoType, const wchar_t* lpSrcStr, int cchSrc, word* lpCharType);\n   BOOST_RE_IMPORT lcid_type BOOST_RE_STDCALL GetUserDefaultLCID();\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL GetStringTypeExA(lcid_type Locale, dword dwInfoType, const char* lpSrcStr, int cchSrc, word* lpCharType);\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL GetStringTypeExW(lcid_type Locale, dword dwInfoType, const wchar_t* lpSrcStr, int cchSrc, word* lpCharType);\n   BOOST_RE_IMPORT HMODULE BOOST_RE_STDCALL LoadLibraryA(const char* lpLibFileName);\n   BOOST_RE_IMPORT HMODULE BOOST_RE_STDCALL LoadLibraryW(const wchar_t* lpLibFileName);\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL LoadStringW(HINSTANCE hInstance, unsigned int uID, wchar_t* lpBuffer, int cchBufferMax);\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL LoadStringA(HINSTANCE hInstance, unsigned int uID, char* lpBuffer, int cchBufferMax);\n   BOOST_RE_IMPORT int BOOST_RE_STDCALL GetLocaleInfoW(lcid_type Locale, lctype LCType, wchar_t* lpLCData, int cchData);\n}\n\n#else\n//\n// We have windows.h already included:\n//\nusing dword = DWORD;\nusing word = WORD;\nusing lctype = LCTYPE;\n\nstatic constexpr dword ct_ctype1 = 0x00000001;\nstatic constexpr dword c1_upper = 0x0001;      // upper case\nstatic constexpr dword c1_lower = 0x0002;      // lower case\nstatic constexpr dword c1_digit = 0x0004;      // decimal digits\nstatic constexpr dword c1_space = 0x0008;      // spacing characters\nstatic constexpr dword c1_punct = 0x0010;      // punctuation characters\nstatic constexpr dword c1_cntrl = 0x0020;      // control characters\nstatic constexpr dword c1_blank = 0x0040;      // blank characters\nstatic constexpr dword c1_xdigit = 0x0080;      // other digits\nstatic constexpr dword c1_alpha = 0x0100;      // any linguistic character\nstatic constexpr dword c1_defined = 0x0200;      // defined character\nstatic constexpr unsigned int cp_acp = 0;\nstatic constexpr dword lcmap_lowercase = 0x00000100;\nstatic constexpr dword lcmap_uppercase = 0x00000200;\nstatic constexpr dword lcmap_sortkey = 0x00000400;  // WC sort key (normalize)\nstatic constexpr lctype locale_idefaultansicodepage = 0x00001004;\n\nusing ::FreeLibrary;\nusing ::LCMapStringA;\nusing ::LCMapStringW;\nusing ::MultiByteToWideChar;\nusing ::LCMapStringW;\nusing ::WideCharToMultiByte;\nusing ::GetStringTypeExA;\nusing ::GetStringTypeExW;\nusing ::GetUserDefaultLCID;\nusing ::GetStringTypeExA;\nusing ::GetStringTypeExW;\nusing ::LoadLibraryA;\nusing ::LoadLibraryW;\nusing ::LoadStringW;\nusing ::LoadStringA;\nusing ::GetLocaleInfoW;\n\n#endif\n//\n// class w32_regex_traits_base:\n// acts as a container for locale and the facets we are using.\n//\ntemplate <class charT>\nstruct w32_regex_traits_base\n{\n   w32_regex_traits_base(lcid_type l)\n   { imbue(l); }\n   lcid_type imbue(lcid_type l);\n\n   lcid_type m_locale;\n};\n\ntemplate <class charT>\ninline lcid_type w32_regex_traits_base<charT>::imbue(lcid_type l)\n{\n   lcid_type result(m_locale);\n   m_locale = l;\n   return result;\n}\n\n//\n// class w32_regex_traits_char_layer:\n// implements methods that require specialisation for narrow characters:\n//\ntemplate <class charT>\nclass w32_regex_traits_char_layer : public w32_regex_traits_base<charT>\n{\n   typedef std::basic_string<charT> string_type;\n   typedef std::map<charT, regex_constants::syntax_type> map_type;\n   typedef typename map_type::const_iterator map_iterator_type;\npublic:\n   w32_regex_traits_char_layer(const lcid_type l);\n\n   regex_constants::syntax_type syntax_type(charT c)const\n   {\n      map_iterator_type i = m_char_map.find(c);\n      return ((i == m_char_map.end()) ? 0 : i->second);\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(charT c) const\n   {\n      map_iterator_type i = m_char_map.find(c);\n      if(i == m_char_map.end())\n      {\n         if(::boost::BOOST_REGEX_DETAIL_NS::w32_is_lower(c, this->m_locale)) return regex_constants::escape_type_class;\n         if(::boost::BOOST_REGEX_DETAIL_NS::w32_is_upper(c, this->m_locale)) return regex_constants::escape_type_not_class;\n         return 0;\n      }\n      return i->second;\n   }\n   charT tolower(charT c)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::w32_tolower(c, this->m_locale);\n   }\n   bool isctype(std::uint32_t mask, charT c)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, mask, c);\n   }\n\nprivate:\n   string_type get_default_message(regex_constants::syntax_type);\n   // TODO: use a hash table when available!\n   map_type m_char_map;\n};\n\ntemplate <class charT>\nw32_regex_traits_char_layer<charT>::w32_regex_traits_char_layer(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l) \n   : w32_regex_traits_base<charT>(l)\n{\n   // we need to start by initialising our syntax map so we know which\n   // character is used for which purpose:\n   cat_type cat;\n   std::string cat_name(w32_regex_traits<charT>::get_catalog_name());\n   if(cat_name.size())\n   {\n      cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name);\n      if(!cat)\n      {\n         std::string m(\"Unable to open message catalog: \");\n         std::runtime_error err(m + cat_name);\n         boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n      }\n   }\n   //\n   // if we have a valid catalog then load our messages:\n   //\n   if(cat)\n   {\n      for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n      {\n         string_type mss = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i, get_default_message(i));\n         for(typename string_type::size_type j = 0; j < mss.size(); ++j)\n         {\n            this->m_char_map[mss[j]] = i;\n         }\n      }\n   }\n   else\n   {\n      for(regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n      {\n         const char* ptr = get_default_syntax(i);\n         while(ptr && *ptr)\n         {\n            this->m_char_map[static_cast<charT>(*ptr)] = i;\n            ++ptr;\n         }\n      }\n   }\n}\n\ntemplate <class charT>\ntypename w32_regex_traits_char_layer<charT>::string_type \n   w32_regex_traits_char_layer<charT>::get_default_message(regex_constants::syntax_type i)\n{\n   const char* ptr = get_default_syntax(i);\n   string_type result;\n   while(ptr && *ptr)\n   {\n      result.append(1, static_cast<charT>(*ptr));\n      ++ptr;\n   }\n   return result;\n}\n\n//\n// specialised version for narrow characters:\n//\ntemplate <>\nclass w32_regex_traits_char_layer<char> : public w32_regex_traits_base<char>\n{\n   typedef std::string string_type;\npublic:\n   w32_regex_traits_char_layer(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l)\n   : w32_regex_traits_base<char>(l)\n   {\n      init<char>();\n   }\n\n   regex_constants::syntax_type syntax_type(char c)const\n   {\n      return m_char_map[static_cast<unsigned char>(c)];\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(char c) const\n   {\n      return m_char_map[static_cast<unsigned char>(c)];\n   }\n   char tolower(char c)const\n   {\n      return m_lower_map[static_cast<unsigned char>(c)];\n   }\n   bool isctype(std::uint32_t mask, char c)const\n   {\n      return m_type_map[static_cast<unsigned char>(c)] & mask;\n   }\n\nprivate:\n   regex_constants::syntax_type m_char_map[1u << CHAR_BIT];\n   char m_lower_map[1u << CHAR_BIT];\n   std::uint16_t m_type_map[1u << CHAR_BIT];\n   template <class U>\n   void init();\n};\n\n//\n// class w32_regex_traits_implementation:\n// provides pimpl implementation for w32_regex_traits.\n//\ntemplate <class charT>\nclass w32_regex_traits_implementation : public w32_regex_traits_char_layer<charT>\n{\npublic:\n   typedef typename w32_regex_traits<charT>::char_class_type char_class_type;\n   static const char_class_type mask_word = 0x0400; // must be C1_DEFINED << 1\n   static const char_class_type mask_unicode = 0x0800; // must be C1_DEFINED << 2\n   static const char_class_type mask_horizontal = 0x1000; // must be C1_DEFINED << 3\n   static const char_class_type mask_vertical = 0x2000; // must be C1_DEFINED << 4\n   static const char_class_type mask_base = 0x3ff;  // all the masks used by the CT_CTYPE1 group\n\n   typedef std::basic_string<charT> string_type;\n   typedef charT char_type;\n   w32_regex_traits_implementation(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l);\n   std::string error_string(regex_constants::error_type n) const\n   {\n      if(!m_error_strings.empty())\n      {\n         std::map<int, std::string>::const_iterator p = m_error_strings.find(n);\n         return (p == m_error_strings.end()) ? std::string(get_default_error_string(n)) : p->second;\n      }\n      return get_default_error_string(n);\n   }\n   char_class_type lookup_classname(const charT* p1, const charT* p2) const\n   {\n      char_class_type result = lookup_classname_imp(p1, p2);\n      if(result == 0)\n      {\n         typedef typename string_type::size_type size_type;\n         string_type temp(p1, p2);\n         for(size_type i = 0; i < temp.size(); ++i)\n            temp[i] = this->tolower(temp[i]);\n         result = lookup_classname_imp(&*temp.begin(), &*temp.begin() + temp.size());\n      }\n      return result;\n   }\n   string_type lookup_collatename(const charT* p1, const charT* p2) const;\n   string_type transform_primary(const charT* p1, const charT* p2) const;\n   string_type transform(const charT* p1, const charT* p2) const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::w32_transform(this->m_locale, p1, p2);\n   }\nprivate:\n   std::map<int, std::string>     m_error_strings;   // error messages indexed by numberic ID\n   std::map<string_type, char_class_type>  m_custom_class_names; // character class names\n   std::map<string_type, string_type>      m_custom_collate_names; // collating element names\n   unsigned                       m_collate_type;    // the form of the collation string\n   charT                          m_collate_delim;   // the collation group delimiter\n   //\n   // helpers:\n   //\n   char_class_type lookup_classname_imp(const charT* p1, const charT* p2) const;\n};\n\ntemplate <class charT>\ntypename w32_regex_traits_implementation<charT>::string_type \n   w32_regex_traits_implementation<charT>::transform_primary(const charT* p1, const charT* p2) const\n{\n   string_type result;\n   //\n   // What we do here depends upon the format of the sort key returned by\n   // sort key returned by this->transform:\n   //\n   switch(m_collate_type)\n   {\n   case sort_C:\n   case sort_unknown:\n      // the best we can do is translate to lower case, then get a regular sort key:\n      {\n         result.assign(p1, p2);\n         typedef typename string_type::size_type size_type;\n         for(size_type i = 0; i < result.size(); ++i)\n            result[i] = this->tolower(result[i]);\n         result = this->transform(&*result.begin(), &*result.begin() + result.size());\n         break;\n      }\n   case sort_fixed:\n      {\n         // get a regular sort key, and then truncate it:\n         result.assign(this->transform(p1, p2));\n         result.erase(this->m_collate_delim);\n         break;\n      }\n   case sort_delim:\n         // get a regular sort key, and then truncate everything after the delim:\n         result.assign(this->transform(p1, p2));\n         std::size_t i;\n         for(i = 0; i < result.size(); ++i)\n         {\n            if(result[i] == m_collate_delim)\n               break;\n         }\n         result.erase(i);\n         break;\n   }\n   if(result.empty())\n      result = string_type(1, charT(0));\n   return result;\n}\n\ntemplate <class charT>\ntypename w32_regex_traits_implementation<charT>::string_type \n   w32_regex_traits_implementation<charT>::lookup_collatename(const charT* p1, const charT* p2) const\n{\n   typedef typename std::map<string_type, string_type>::const_iterator iter_type;\n   if(m_custom_collate_names.size())\n   {\n      iter_type pos = m_custom_collate_names.find(string_type(p1, p2));\n      if(pos != m_custom_collate_names.end())\n         return pos->second;\n   }\n   std::string name(p1, p2);\n   name = lookup_default_collate_name(name);\n   if(name.size())\n      return string_type(name.begin(), name.end());\n   if(p2 - p1 == 1)\n      return string_type(1, *p1);\n   return string_type();\n}\n\ntemplate <class charT>\nw32_regex_traits_implementation<charT>::w32_regex_traits_implementation(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l)\n: w32_regex_traits_char_layer<charT>(l)\n{\n   cat_type cat;\n   std::string cat_name(w32_regex_traits<charT>::get_catalog_name());\n   if(cat_name.size())\n   {\n      cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name);\n      if(!cat)\n      {\n         std::string m(\"Unable to open message catalog: \");\n         std::runtime_error err(m + cat_name);\n         boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n      }\n   }\n   //\n   // if we have a valid catalog then load our messages:\n   //\n   if(cat)\n   {\n      //\n      // Error messages:\n      //\n      for(boost::regex_constants::error_type i = static_cast<boost::regex_constants::error_type>(0); \n         i <= boost::regex_constants::error_unknown; \n         i = static_cast<boost::regex_constants::error_type>(i + 1))\n      {\n         const char* p = get_default_error_string(i);\n         string_type default_message;\n         while(*p)\n         {\n            default_message.append(1, static_cast<charT>(*p));\n            ++p;\n         }\n         string_type s = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i+200, default_message);\n         std::string result;\n         for(std::string::size_type j = 0; j < s.size(); ++j)\n         {\n            result.append(1, static_cast<char>(s[j]));\n         }\n         m_error_strings[i] = result;\n      }\n      //\n      // Custom class names:\n      //\n      static const char_class_type masks[14] = \n      {\n         0x0104u, // C1_ALPHA | C1_DIGIT\n         0x0100u, // C1_ALPHA\n         0x0020u, // C1_CNTRL\n         0x0004u, // C1_DIGIT\n         (~(0x0020u|0x0008u) & 0x01ffu) | 0x0400u, // not C1_CNTRL or C1_SPACE\n         0x0002u, // C1_LOWER\n         (~0x0020u & 0x01ffu) | 0x0400, // not C1_CNTRL\n         0x0010u, // C1_PUNCT\n         0x0008u, // C1_SPACE\n         0x0001u, // C1_UPPER\n         0x0080u, // C1_XDIGIT\n         0x0040u, // C1_BLANK\n         w32_regex_traits_implementation<charT>::mask_word,\n         w32_regex_traits_implementation<charT>::mask_unicode,\n      };\n      static const string_type null_string;\n      for(unsigned int j = 0; j <= 13; ++j)\n      {\n         string_type s(::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, j+300, null_string));\n         if(s.size())\n            this->m_custom_class_names[s] = masks[j];\n      }\n   }\n   //\n   // get the collation format used by m_pcollate:\n   //\n   m_collate_type = BOOST_REGEX_DETAIL_NS::find_sort_syntax(this, &m_collate_delim);\n}\n\ntemplate <class charT>\ntypename w32_regex_traits_implementation<charT>::char_class_type \n   w32_regex_traits_implementation<charT>::lookup_classname_imp(const charT* p1, const charT* p2) const\n{\n   static const char_class_type masks[22] = \n   {\n      0,\n      0x0104u, // C1_ALPHA | C1_DIGIT\n      0x0100u, // C1_ALPHA\n      0x0040u, // C1_BLANK\n      0x0020u, // C1_CNTRL\n      0x0004u, // C1_DIGIT\n      0x0004u, // C1_DIGIT\n      (~(0x0020u|0x0008u|0x0040) & 0x01ffu) | 0x0400u, // not C1_CNTRL or C1_SPACE or C1_BLANK\n      w32_regex_traits_implementation<charT>::mask_horizontal, \n      0x0002u, // C1_LOWER\n      0x0002u, // C1_LOWER\n      (~0x0020u & 0x01ffu) | 0x0400, // not C1_CNTRL\n      0x0010u, // C1_PUNCT\n      0x0008u, // C1_SPACE\n      0x0008u, // C1_SPACE\n      0x0001u, // C1_UPPER\n      w32_regex_traits_implementation<charT>::mask_unicode,\n      0x0001u, // C1_UPPER\n      w32_regex_traits_implementation<charT>::mask_vertical, \n      0x0104u | w32_regex_traits_implementation<charT>::mask_word, \n      0x0104u | w32_regex_traits_implementation<charT>::mask_word, \n      0x0080u, // C1_XDIGIT\n   };\n   if(m_custom_class_names.size())\n   {\n      typedef typename std::map<std::basic_string<charT>, char_class_type>::const_iterator map_iter;\n      map_iter pos = m_custom_class_names.find(string_type(p1, p2));\n      if(pos != m_custom_class_names.end())\n         return pos->second;\n   }\n   std::size_t state_id = 1u + (std::size_t)BOOST_REGEX_DETAIL_NS::get_default_class_id(p1, p2);\n   if(state_id < sizeof(masks) / sizeof(masks[0]))\n      return masks[state_id];\n   return masks[0];\n}\n\n\ntemplate <class charT>\nstd::shared_ptr<const w32_regex_traits_implementation<charT> > create_w32_regex_traits(::boost::BOOST_REGEX_DETAIL_NS::lcid_type l)\n{\n   // TODO: create a cache for previously constructed objects.\n   return boost::object_cache< ::boost::BOOST_REGEX_DETAIL_NS::lcid_type, w32_regex_traits_implementation<charT> >::get(l, 5);\n}\n\n} // BOOST_REGEX_DETAIL_NS\n\ntemplate <class charT>\nclass w32_regex_traits\n{\npublic:\n   typedef charT                         char_type;\n   typedef std::size_t                   size_type;\n   typedef std::basic_string<char_type>  string_type;\n   typedef ::boost::BOOST_REGEX_DETAIL_NS::lcid_type locale_type;\n   typedef std::uint_least32_t         char_class_type;\n\n   struct boost_extensions_tag{};\n\n   w32_regex_traits()\n      : m_pimpl(BOOST_REGEX_DETAIL_NS::create_w32_regex_traits<charT>(::boost::BOOST_REGEX_DETAIL_NS::w32_get_default_locale()))\n   { }\n   static size_type length(const char_type* p)\n   {\n      return std::char_traits<charT>::length(p);\n   }\n   regex_constants::syntax_type syntax_type(charT c)const\n   {\n      return m_pimpl->syntax_type(c);\n   }\n   regex_constants::escape_syntax_type escape_syntax_type(charT c) const\n   {\n      return m_pimpl->escape_syntax_type(c);\n   }\n   charT translate(charT c) const\n   {\n      return c;\n   }\n   charT translate_nocase(charT c) const\n   {\n      return this->m_pimpl->tolower(c);\n   }\n   charT translate(charT c, bool icase) const\n   {\n      return icase ? this->m_pimpl->tolower(c) : c;\n   }\n   charT tolower(charT c) const\n   {\n      return this->m_pimpl->tolower(c);\n   }\n   charT toupper(charT c) const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::w32_toupper(c, this->m_pimpl->m_locale);\n   }\n   string_type transform(const charT* p1, const charT* p2) const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::w32_transform(this->m_pimpl->m_locale, p1, p2);\n   }\n   string_type transform_primary(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->transform_primary(p1, p2);\n   }\n   char_class_type lookup_classname(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->lookup_classname(p1, p2);\n   }\n   string_type lookup_collatename(const charT* p1, const charT* p2) const\n   {\n      return m_pimpl->lookup_collatename(p1, p2);\n   }\n   bool isctype(charT c, char_class_type f) const\n   {\n      if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_base) \n         && (this->m_pimpl->isctype(f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_base, c)))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_unicode) && BOOST_REGEX_DETAIL_NS::is_extended(c))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_word) && (c == '_'))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_vertical)\n         && (::boost::BOOST_REGEX_DETAIL_NS::is_separator(c) || (c == '\\v')))\n         return true;\n      else if((f & BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_horizontal) \n         && this->isctype(c, 0x0008u) && !this->isctype(c, BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT>::mask_vertical))\n         return true;\n      return false;\n   }\n   std::intmax_t toi(const charT*& p1, const charT* p2, int radix)const\n   {\n      return ::boost::BOOST_REGEX_DETAIL_NS::global_toi(p1, p2, radix, *this);\n   }\n   int value(charT c, int radix)const\n   {\n      int result = (int)::boost::BOOST_REGEX_DETAIL_NS::global_value(c);\n      return result < radix ? result : -1;\n   }\n   locale_type imbue(locale_type l)\n   {\n      ::boost::BOOST_REGEX_DETAIL_NS::lcid_type result(getloc());\n      m_pimpl = BOOST_REGEX_DETAIL_NS::create_w32_regex_traits<charT>(l);\n      return result;\n   }\n   locale_type getloc()const\n   {\n      return m_pimpl->m_locale;\n   }\n   std::string error_string(regex_constants::error_type n) const\n   {\n      return m_pimpl->error_string(n);\n   }\n\n   //\n   // extension:\n   // set the name of the message catalog in use (defaults to \"boost_regex\").\n   //\n   static std::string catalog_name(const std::string& name);\n   static std::string get_catalog_name();\n\nprivate:\n   std::shared_ptr<const BOOST_REGEX_DETAIL_NS::w32_regex_traits_implementation<charT> > m_pimpl;\n   //\n   // catalog name handler:\n   //\n   static std::string& get_catalog_name_inst();\n\n#ifdef BOOST_HAS_THREADS\n   static std::mutex& get_mutex_inst();\n#endif\n};\n\ntemplate <class charT>\nstd::string w32_regex_traits<charT>::catalog_name(const std::string& name)\n{\n#ifdef BOOST_HAS_THREADS\n   std::lock_guard<std::mutex> lk(get_mutex_inst());\n#endif\n   std::string result(get_catalog_name_inst());\n   get_catalog_name_inst() = name;\n   return result;\n}\n\ntemplate <class charT>\nstd::string& w32_regex_traits<charT>::get_catalog_name_inst()\n{\n   static std::string s_name;\n   return s_name;\n}\n\ntemplate <class charT>\nstd::string w32_regex_traits<charT>::get_catalog_name()\n{\n#ifdef BOOST_HAS_THREADS\n   std::lock_guard<std::mutex> lk(get_mutex_inst());\n#endif\n   std::string result(get_catalog_name_inst());\n   return result;\n}\n\n#ifdef BOOST_HAS_THREADS\ntemplate <class charT>\nstd::mutex& w32_regex_traits<charT>::get_mutex_inst()\n{\n   static std::mutex s_mutex;\n   return s_mutex;\n}\n#endif\n\nnamespace BOOST_REGEX_DETAIL_NS {\n\n#ifdef BOOST_NO_ANSI_APIS\n   inline unsigned int get_code_page_for_locale_id(lcid_type idx)\n   {\n      wchar_t code_page_string[7];\n      if (boost::BOOST_REGEX_DETAIL_NS::GetLocaleInfoW(idx, locale_idefaultansicodepage, code_page_string, 7) == 0)\n         return 0;\n\n      return static_cast<unsigned int>(_wtol(code_page_string));\n}\n#endif\n\n   template <class U>\n   inline void w32_regex_traits_char_layer<char>::init()\n   {\n      // we need to start by initialising our syntax map so we know which\n      // character is used for which purpose:\n      std::memset(m_char_map, 0, sizeof(m_char_map));\n      cat_type cat;\n      std::string cat_name(w32_regex_traits<char>::get_catalog_name());\n      if (cat_name.size())\n      {\n         cat = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_open(cat_name);\n         if (!cat)\n         {\n            std::string m(\"Unable to open message catalog: \");\n            std::runtime_error err(m + cat_name);\n            ::boost::BOOST_REGEX_DETAIL_NS::raise_runtime_error(err);\n         }\n      }\n      //\n      // if we have a valid catalog then load our messages:\n      //\n      if (cat)\n      {\n         for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n         {\n            string_type mss = ::boost::BOOST_REGEX_DETAIL_NS::w32_cat_get(cat, this->m_locale, i, get_default_syntax(i));\n            for (string_type::size_type j = 0; j < mss.size(); ++j)\n            {\n               m_char_map[static_cast<unsigned char>(mss[j])] = i;\n            }\n         }\n      }\n      else\n      {\n         for (regex_constants::syntax_type i = 1; i < regex_constants::syntax_max; ++i)\n         {\n            const char* ptr = get_default_syntax(i);\n            while (ptr && *ptr)\n            {\n               m_char_map[static_cast<unsigned char>(*ptr)] = i;\n               ++ptr;\n            }\n         }\n      }\n      //\n      // finish off by calculating our escape types:\n      //\n      unsigned char i = 'A';\n      do\n      {\n         if (m_char_map[i] == 0)\n         {\n            if (::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, 0x0002u, (char)i))\n               m_char_map[i] = regex_constants::escape_type_class;\n            else if (::boost::BOOST_REGEX_DETAIL_NS::w32_is(this->m_locale, 0x0001u, (char)i))\n               m_char_map[i] = regex_constants::escape_type_not_class;\n         }\n      } while (0xFF != i++);\n\n      //\n      // fill in lower case map:\n      //\n      char char_map[1 << CHAR_BIT];\n      for (int ii = 0; ii < (1 << CHAR_BIT); ++ii)\n         char_map[ii] = static_cast<char>(ii);\n#ifndef BOOST_NO_ANSI_APIS\n      int r = boost::BOOST_REGEX_DETAIL_NS::LCMapStringA(this->m_locale, lcmap_lowercase, char_map, 1 << CHAR_BIT, this->m_lower_map, 1 << CHAR_BIT);\n      BOOST_REGEX_ASSERT(r != 0);\n#else\n      unsigned int code_page = get_code_page_for_locale_id(this->m_locale);\n      BOOST_REGEX_ASSERT(code_page != 0);\n\n      wchar_t wide_char_map[1 << CHAR_BIT];\n      int conv_r = boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, char_map, 1 << CHAR_BIT, wide_char_map, 1 << CHAR_BIT);\n      BOOST_REGEX_ASSERT(conv_r != 0);\n\n      wchar_t wide_lower_map[1 << CHAR_BIT];\n      int r = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW(this->m_locale, lcmap_lowercase, wide_char_map, 1 << CHAR_BIT, wide_lower_map, 1 << CHAR_BIT);\n      BOOST_REGEX_ASSERT(r != 0);\n\n      conv_r = boost::BOOST_REGEX_DETAIL_NS::WideCharToMultiByte(code_page, 0, wide_lower_map, r, this->m_lower_map, 1 << CHAR_BIT, NULL, NULL);\n      BOOST_REGEX_ASSERT(conv_r != 0);\n#endif\n      if (r < (1 << CHAR_BIT))\n      {\n         // if we have multibyte characters then not all may have been given\n         // a lower case mapping:\n         for (int jj = r; jj < (1 << CHAR_BIT); ++jj)\n            this->m_lower_map[jj] = static_cast<char>(jj);\n      }\n\n#ifndef BOOST_NO_ANSI_APIS\n      r = boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExA(this->m_locale, ct_ctype1, char_map, 1 << CHAR_BIT, this->m_type_map);\n#else\n      r = boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(this->m_locale, ct_ctype1, wide_char_map, 1 << CHAR_BIT, this->m_type_map);\n#endif\n      BOOST_REGEX_ASSERT(0 != r);\n   }\n\n   inline lcid_type  w32_get_default_locale()\n   {\n      return boost::BOOST_REGEX_DETAIL_NS::GetUserDefaultLCID();\n   }\n\n   inline bool  w32_is_lower(char c, lcid_type idx)\n   {\n#ifndef BOOST_NO_ANSI_APIS\n      word mask;\n      if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExA(idx, ct_ctype1, &c, 1, &mask) && (mask & c1_lower))\n         return true;\n      return false;\n#else\n      unsigned int code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return false;\n\n      wchar_t wide_c;\n      if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0)\n         return false;\n\n      word mask;\n      if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &wide_c, 1, &mask) && (mask & c1_lower))\n         return true;\n      return false;\n#endif\n   }\n\n   inline bool  w32_is_lower(wchar_t c, lcid_type idx)\n   {\n      word mask;\n      if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &c, 1, &mask) && (mask & c1_lower))\n         return true;\n      return false;\n   }\n\n   inline bool  w32_is_upper(char c, lcid_type idx)\n   {\n#ifndef BOOST_NO_ANSI_APIS\n      word mask;\n      if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExA(idx, ct_ctype1, &c, 1, &mask) && (mask & c1_upper))\n         return true;\n      return false;\n#else\n      unsigned int code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return false;\n\n      wchar_t wide_c;\n      if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0)\n         return false;\n\n      word mask;\n      if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &wide_c, 1, &mask) && (mask & c1_upper))\n         return true;\n      return false;\n#endif\n   }\n\n   inline bool  w32_is_upper(wchar_t c, lcid_type idx)\n   {\n      word mask;\n      if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &c, 1, &mask) && (mask & c1_upper))\n         return true;\n      return false;\n   }\n\n   inline void free_module(void* mod)\n   {\n      boost::BOOST_REGEX_DETAIL_NS::FreeLibrary(static_cast<HMODULE>(mod));\n   }\n\n   inline cat_type  w32_cat_open(const std::string& name)\n   {\n#ifndef BOOST_NO_ANSI_APIS\n      cat_type result(boost::BOOST_REGEX_DETAIL_NS::LoadLibraryA(name.c_str()), &free_module);\n      return result;\n#else\n      wchar_t* wide_name = (wchar_t*)_alloca((name.size() + 1) * sizeof(wchar_t));\n      if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(cp_acp, 0, name.c_str(), (int)name.size(), wide_name, (int)(name.size() + 1)) == 0)\n         return cat_type();\n\n      cat_type result(boost::BOOST_REGEX_DETAIL_NS::LoadLibraryW(wide_name), &free_module);\n      return result;\n#endif\n   }\n\n   inline std::string  w32_cat_get(const cat_type& cat, lcid_type, int i, const std::string& def)\n   {\n#ifndef BOOST_NO_ANSI_APIS\n      char buf[256];\n      if (0 == boost::BOOST_REGEX_DETAIL_NS::LoadStringA(\n         static_cast<HMODULE>(cat.get()),\n         i,\n         buf,\n         256\n      ))\n      {\n         return def;\n      }\n#else\n      wchar_t wbuf[256];\n      int r = boost::BOOST_REGEX_DETAIL_NS::LoadStringW(\n         static_cast<HMODULE>(cat.get()),\n         i,\n         wbuf,\n         256\n      );\n      if (r == 0)\n         return def;\n\n\n      int buf_size = 1 + boost::BOOST_REGEX_DETAIL_NS::WideCharToMultiByte(cp_acp, 0, wbuf, r, NULL, 0, NULL, NULL);\n      char* buf = (char*)_alloca(buf_size);\n      if (boost::BOOST_REGEX_DETAIL_NS::WideCharToMultiByte(cp_acp, 0, wbuf, r, buf, buf_size, NULL, NULL) == 0)\n         return def; // failed conversion.\n#endif\n      return std::string(buf);\n   }\n\n#ifndef BOOST_NO_WREGEX\n   inline std::wstring  w32_cat_get(const cat_type& cat, lcid_type, int i, const std::wstring& def)\n   {\n      wchar_t buf[256];\n      if (0 == boost::BOOST_REGEX_DETAIL_NS::LoadStringW(static_cast<HMODULE>(cat.get()), i, buf, 256))\n      {\n         return def;\n      }\n      return std::wstring(buf);\n   }\n#endif\n   inline std::string  w32_transform(lcid_type idx, const char* p1, const char* p2)\n   {\n#ifndef BOOST_NO_ANSI_APIS\n      int bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringA(\n         idx,       // locale identifier\n         lcmap_sortkey,  // mapping transformation type\n         p1,  // source string\n         static_cast<int>(p2 - p1),        // number of characters in source string\n         0,  // destination buffer\n         0        // size of destination buffer\n      );\n      if (!bytes)\n         return std::string(p1, p2);\n      std::string result(++bytes, '\\0');\n      bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringA(\n         idx,       // locale identifier\n         lcmap_sortkey,  // mapping transformation type\n         p1,  // source string\n         static_cast<int>(p2 - p1),        // number of characters in source string\n         &*result.begin(),  // destination buffer\n         bytes        // size of destination buffer\n      );\n#else\n      unsigned int code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return std::string(p1, p2);\n\n      int src_len = static_cast<int>(p2 - p1);\n      wchar_t* wide_p1 = (wchar_t*)_alloca((src_len + 1) * 2);\n      if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, p1, src_len, wide_p1, src_len + 1) == 0)\n         return std::string(p1, p2);\n\n      int bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW(\n         idx,       // locale identifier\n         lcmap_sortkey,  // mapping transformation type\n         wide_p1,  // source string\n         src_len,        // number of characters in source string\n         0,  // destination buffer\n         0        // size of destination buffer\n      );\n      if (!bytes)\n         return std::string(p1, p2);\n      std::string result(++bytes, '\\0');\n      bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW(\n         idx,       // locale identifier\n         lcmap_sortkey,  // mapping transformation type\n         wide_p1,  // source string\n         src_len,        // number of characters in source string\n         (wchar_t*) & *result.begin(),  // destination buffer\n         bytes        // size of destination buffer\n      );\n#endif\n      if (bytes > static_cast<int>(result.size()))\n         return std::string(p1, p2);\n      while (result.size() && result[result.size() - 1] == '\\0')\n      {\n         result.erase(result.size() - 1);\n      }\n      return result;\n   }\n\n#ifndef BOOST_NO_WREGEX\n   inline std::wstring  w32_transform(lcid_type idx, const wchar_t* p1, const wchar_t* p2)\n   {\n      int bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW(\n         idx,       // locale identifier\n         lcmap_sortkey,  // mapping transformation type\n         p1,  // source string\n         static_cast<int>(p2 - p1),        // number of characters in source string\n         0,  // destination buffer\n         0        // size of destination buffer\n      );\n      if (!bytes)\n         return std::wstring(p1, p2);\n      std::string result(++bytes, '\\0');\n      bytes = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW(\n         idx,       // locale identifier\n         lcmap_sortkey,  // mapping transformation type\n         p1,  // source string\n         static_cast<int>(p2 - p1),        // number of characters in source string\n         reinterpret_cast<wchar_t*>(&*result.begin()),  // destination buffer *of bytes*\n         bytes        // size of destination buffer\n      );\n      if (bytes > static_cast<int>(result.size()))\n         return std::wstring(p1, p2);\n      while (result.size() && result[result.size() - 1] == L'\\0')\n      {\n         result.erase(result.size() - 1);\n      }\n      std::wstring r2;\n      for (std::string::size_type i = 0; i < result.size(); ++i)\n         r2.append(1, static_cast<wchar_t>(static_cast<unsigned char>(result[i])));\n      return r2;\n   }\n#endif\n   inline char  w32_tolower(char c, lcid_type idx)\n   {\n      char result[2];\n#ifndef BOOST_NO_ANSI_APIS\n      int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringA(\n         idx,       // locale identifier\n         lcmap_lowercase,  // mapping transformation type\n         &c,  // source string\n         1,        // number of characters in source string\n         result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n#else\n      unsigned int code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return c;\n\n      wchar_t wide_c;\n      if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0)\n         return c;\n\n      wchar_t  wide_result;\n      int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW(\n         idx,       // locale identifier\n         lcmap_lowercase,  // mapping transformation type\n         &wide_c,  // source string\n         1,        // number of characters in source string\n         &wide_result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n\n      if (boost::BOOST_REGEX_DETAIL_NS::WideCharToMultiByte(code_page, 0, &wide_result, 1, result, 2, NULL, NULL) == 0)\n         return c;  // No single byte lower case equivalent available\n#endif\n      return result[0];\n   }\n\n#ifndef BOOST_NO_WREGEX\n   inline wchar_t  w32_tolower(wchar_t c, lcid_type idx)\n   {\n      wchar_t result[2];\n      int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW(\n         idx,       // locale identifier\n         lcmap_lowercase,  // mapping transformation type\n         &c,  // source string\n         1,        // number of characters in source string\n         result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n      return result[0];\n   }\n#endif\n   inline char  w32_toupper(char c, lcid_type idx)\n   {\n      char result[2];\n#ifndef BOOST_NO_ANSI_APIS\n      int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringA(\n         idx,       // locale identifier\n         lcmap_uppercase,  // mapping transformation type\n         &c,  // source string\n         1,        // number of characters in source string\n         result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n#else\n      unsigned int code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return c;\n\n      wchar_t wide_c;\n      if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0)\n         return c;\n\n      wchar_t wide_result;\n      int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW(\n         idx,       // locale identifier\n         lcmap_uppercase,  // mapping transformation type\n         &wide_c,  // source string\n         1,        // number of characters in source string\n         &wide_result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n\n      if (boost::BOOST_REGEX_DETAIL_NS::WideCharToMultiByte(code_page, 0, &wide_result, 1, result, 2, NULL, NULL) == 0)\n         return c;  // No single byte upper case equivalent available.\n#endif\n      return result[0];\n   }\n\n#ifndef BOOST_NO_WREGEX\n   inline wchar_t  w32_toupper(wchar_t c, lcid_type idx)\n   {\n      wchar_t result[2];\n      int b = boost::BOOST_REGEX_DETAIL_NS::LCMapStringW(\n         idx,       // locale identifier\n         lcmap_uppercase,  // mapping transformation type\n         &c,  // source string\n         1,        // number of characters in source string\n         result,  // destination buffer\n         1);        // size of destination buffer\n      if (b == 0)\n         return c;\n      return result[0];\n   }\n#endif\n   inline bool  w32_is(lcid_type idx, std::uint32_t m, char c)\n   {\n      word mask;\n#ifndef BOOST_NO_ANSI_APIS\n      if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExA(idx, ct_ctype1, &c, 1, &mask) && (mask & m & w32_regex_traits_implementation<char>::mask_base))\n         return true;\n#else\n      unsigned int code_page = get_code_page_for_locale_id(idx);\n      if (code_page == 0)\n         return false;\n\n      wchar_t wide_c;\n      if (boost::BOOST_REGEX_DETAIL_NS::MultiByteToWideChar(code_page, 0, &c, 1, &wide_c, 1) == 0)\n         return false;\n\n      if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &wide_c, 1, &mask) && (mask & m & w32_regex_traits_implementation<char>::mask_base))\n         return true;\n#endif\n      if ((m & w32_regex_traits_implementation<char>::mask_word) && (c == '_'))\n         return true;\n      return false;\n   }\n\n#ifndef BOOST_NO_WREGEX\n   inline bool  w32_is(lcid_type idx, std::uint32_t m, wchar_t c)\n   {\n      word mask;\n      if (boost::BOOST_REGEX_DETAIL_NS::GetStringTypeExW(idx, ct_ctype1, &c, 1, &mask) && (mask & m & w32_regex_traits_implementation<wchar_t>::mask_base))\n         return true;\n      if ((m & w32_regex_traits_implementation<wchar_t>::mask_word) && (c == '_'))\n         return true;\n      if ((m & w32_regex_traits_implementation<wchar_t>::mask_unicode) && (c > 0xff))\n         return true;\n      return false;\n   }\n#endif\n\n} // BOOST_REGEX_DETAIL_NS\n\n\n} // boost\n\n#ifdef BOOST_REGEX_MSVC\n#pragma warning(pop)\n#endif\n\n#endif // BOOST_REGEX_NO_WIN32_LOCALE\n\n#endif\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex.h",
    "content": "/*\n *\n * Copyright (c) 1998-2000\n * Dr John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n \n /*\n  *   LOCATION:    see http://www.boost.org/libs/regex for documentation.\n  *   FILE         regex.h\n  *   VERSION      3.12\n  *   DESCRIPTION: Declares POSIX API functions\n  */\n\n#ifndef BOOST_RE_REGEX_H\n#define BOOST_RE_REGEX_H\n\n#include <boost/cregex.hpp>\n\n/*\n*  add using declarations to bring POSIX API functions into\n* global scope, only if this is C++ (and not C).\n*/\n#ifdef __cplusplus\n\nusing boost::regoff_t;\nusing boost::regex_tA;\nusing boost::regmatch_t;\nusing boost::REG_BASIC;\nusing boost::REG_EXTENDED;\nusing boost::REG_ICASE;\nusing boost::REG_NOSUB;\nusing boost::REG_NEWLINE;\nusing boost::REG_NOSPEC;\nusing boost::REG_PEND;\nusing boost::REG_DUMP;\nusing boost::REG_NOCOLLATE;\nusing boost::REG_ESCAPE_IN_LISTS;\nusing boost::REG_NEWLINE_ALT;\nusing boost::REG_PERL;\nusing boost::REG_AWK;\nusing boost::REG_GREP;\nusing boost::REG_EGREP;\nusing boost::REG_ASSERT;\nusing boost::REG_INVARG;\nusing boost::REG_ATOI;\nusing boost::REG_ITOA;\n\nusing boost::REG_NOTBOL;\nusing boost::REG_NOTEOL;\nusing boost::REG_STARTEND;\n\nusing boost::reg_comp_flags;\nusing boost::reg_exec_flags;\nusing boost::regcompA;\nusing boost::regerrorA;\nusing boost::regexecA;\nusing boost::regfreeA;\n\n#ifndef BOOST_NO_WREGEX\nusing boost::regcompW;\nusing boost::regerrorW;\nusing boost::regexecW;\nusing boost::regfreeW;\nusing boost::regex_tW;\n#endif\n\nusing boost::REG_NOERROR;\nusing boost::REG_NOMATCH;\nusing boost::REG_BADPAT;\nusing boost::REG_ECOLLATE;\nusing boost::REG_ECTYPE;\nusing boost::REG_EESCAPE;\nusing boost::REG_ESUBREG;\nusing boost::REG_EBRACK;\nusing boost::REG_EPAREN;\nusing boost::REG_EBRACE;\nusing boost::REG_BADBR;\nusing boost::REG_ERANGE;\nusing boost::REG_ESPACE;\nusing boost::REG_BADRPT;\nusing boost::REG_EEND;\nusing boost::REG_ESIZE;\nusing boost::REG_ERPAREN;\nusing boost::REG_EMPTY;\nusing boost::REG_E_MEMORY;\nusing boost::REG_E_UNKNOWN;\nusing boost::reg_errcode_t;\n\n#endif /* __cplusplus */\n\n#endif /* BOOST_RE_REGEX_H */\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org/libs/regex for documentation.\n  *   FILE         regex.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Declares boost::basic_regex<> and associated\n  *                functions and classes. This header is the main\n  *                entry point for the template regex code.\n  */\n\n\n/* start with C compatibility API */\n\n#ifndef BOOST_RE_REGEX_HPP\n#define BOOST_RE_REGEX_HPP\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n\n#ifdef BOOST_REGEX_CXX03\n#include <boost/regex/v4/regex.hpp>\n#else\n#include <boost/regex/v5/regex.hpp>\n#endif\n\n#endif  // include\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/boost/regex/include/boost/regex_fwd.hpp",
    "content": "/*\n *\n * Copyright (c) 1998-2002\n * John Maddock\n *\n * Use, modification and distribution are subject to the \n * Boost Software License, Version 1.0. (See accompanying file \n * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n *\n */\n\n /*\n  *   LOCATION:    see http://www.boost.org/libs/regex for documentation.\n  *   FILE         regex_fwd.cpp\n  *   VERSION      see <boost/version.hpp>\n  *   DESCRIPTION: Forward declares boost::basic_regex<> and\n  *                associated typedefs.\n  */\n\n#ifndef BOOST_REGEX_FWD_HPP\n#define BOOST_REGEX_FWD_HPP\n\n#ifndef BOOST_REGEX_CONFIG_HPP\n#include <boost/regex/config.hpp>\n#endif\n\n#ifdef BOOST_REGEX_CXX03\n#include <boost/regex/v4/regex_fwd.hpp>\n#else\n#include <boost/regex/v5/regex_fwd.hpp>\n#endif\n\n#endif\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/imgui/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\nproject(imgui)\n\nset(CMAKE_CXX_STANDARD 23)\n\nadd_library(imgui_all_includes INTERFACE)\n\nadd_subdirectory(imgui)\nadd_subdirectory(cimgui)\nadd_subdirectory(implot)\nadd_subdirectory(implot3d)\nadd_subdirectory(imnodes)\nadd_subdirectory(backend)\nadd_subdirectory(imgui_test_engine)\n\nset(IMGUI_LIBRARIES imgui_imgui imgui_cimgui imgui_implot imgui_implot3d imgui_imnodes imgui_backend)\nset(IMGUI_LIBRARIES ${IMGUI_LIBRARIES} PARENT_SCOPE)\n\nif (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)\n    foreach (LIBRARY IN LISTS IMGUI_LIBRARIES)\n        target_compile_definitions(${LIBRARY} PRIVATE EXPORT_SYMBOLS=1)\n    endforeach ()\nendif()"
  },
  {
    "path": "lib/third_party/imgui/backend/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\n# https://github.com/ocornut/imgui backends with custom modifications made to the OpenGL 3 and GLFW backends\nproject(imgui_backend)\n\nset(CMAKE_CXX_STANDARD 23)\n\nif (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)\n    add_library(imgui_backend OBJECT\n        source/imgui_impl_opengl3.cpp\n        source/imgui_impl_glfw.cpp\n    )\n\n    target_include_directories(imgui_backend PUBLIC\n        include\n    )\n\n    target_link_libraries(imgui_backend PRIVATE imgui_includes)\n\n    find_package(OpenGL REQUIRED)\n    find_package(Freetype REQUIRED)\n\n    if (UNIX AND NOT APPLE AND NOT EMSCRIPTEN)\n        find_package(X11)\n        target_include_directories(imgui_backend PUBLIC ${X11_INCLUDE_DIR})\n        target_link_libraries(imgui_backend PUBLIC ${X11_LIBRARIES})\n    endif()\n\n    if (EMSCRIPTEN)\n        target_compile_options(imgui_backend PRIVATE --use-port=contrib.glfw3)\n    endif()\n\n    find_package(GLFW QUIET)\n    if (NOT GLFW_FOUND OR \"${GLFW_LIBRARIES}\" STREQUAL \"\")\n        find_package(glfw3 QUIET)\n        set(GLFW_INCLUDE_DIRS ${glfw3_INCLUDE_DIRS})\n        set(GLFW_LIBRARIES ${glfw3_LIBRARIES})\n\n        if (NOT glfw3_FOUND AND \"${GLFW_LIBRARIES}\" STREQUAL \"\")\n            find_package(PkgConfig REQUIRED)\n            pkg_search_module(GLFW REQUIRED glfw3)\n\n            if (\"${GLFW_LIBRARIES}\" MATCHES \".+dll\")\n                set(GLFW_LIBRARIES \"glfw3\")\n            endif ()\n        else()\n            set(GLFW_LIBRARIES GLFW::GLFW)\n        endif ()\n    endif()\n\n    target_include_directories(imgui_backend PUBLIC ${FREETYPE_INCLUDE_DIRS} ${OpenGL_INCLUDE_DIRS})\n    target_link_directories(imgui_backend PUBLIC ${FREETYPE_LIBRARY_DIRS} ${OpenGL_LIBRARY_DIRS})\n    target_link_libraries(imgui_backend PUBLIC ${GLFW_LIBRARIES} ${OPENGL_LIBRARIES})\nendif()\n\ntarget_include_directories(imgui_all_includes INTERFACE include)"
  },
  {
    "path": "lib/third_party/imgui/backend/include/emscripten_browser_clipboard.h",
    "content": "#ifndef EMSCRIPTEN_BROWSER_CLIPBOARD_H_INCLUDED\n#define EMSCRIPTEN_BROWSER_CLIPBOARD_H_INCLUDED\n\n#include <string>\n#include <emscripten.h>\n\n#define _EM_JS_INLINE(ret, c_name, js_name, params, code)                          \\\n  extern \"C\" {                                                                     \\\n  ret c_name params EM_IMPORT(js_name);                                            \\\n  EMSCRIPTEN_KEEPALIVE                                                             \\\n  __attribute__((section(\"em_js\"), aligned(1))) inline char __em_js__##js_name[] = \\\n    #params \"<::>\" code;                                                           \\\n  }\n\n#define EM_JS_INLINE(ret, name, params, ...) _EM_JS_INLINE(ret, name, name, params, #__VA_ARGS__)\n\nnamespace emscripten_browser_clipboard {\n\n/////////////////////////////////// Interface //////////////////////////////////\n\nusing paste_handler = void(*)(std::string const&, void*);\nusing copy_handler = char const*(*)(void*);\n\ninline void paste(paste_handler callback, void *callback_data = nullptr);\ninline void copy(copy_handler callback, void *callback_data = nullptr);\ninline void copy(std::string const &content);\n\n///////////////////////////////// Implementation ///////////////////////////////\n\nnamespace {\n\nEM_JS_INLINE(void, paste_js, (paste_handler callback, void *callback_data), {\n  /// Register the given callback to handle paste events. Callback data pointer is passed through to the callback.\n  /// Paste handler callback signature is:\n  ///   void my_handler(std::string const &paste_data, void *callback_data = nullptr);\n  document.addEventListener('paste', (event) => {\n    Module[\"ccall\"]('paste_return', 'number', ['string', 'number', 'number'], [event.clipboardData.getData('text/plain'), callback, callback_data]);\n  });\n});\n\nEM_JS_INLINE(void, copy_js, (copy_handler callback, void *callback_data), {\n  /// Register the given callback to handle copy events. Callback data pointer is passed through to the callback.\n  /// Copy handler callback signature is:\n  ///   char const *my_handler(void *callback_data = nullptr);\n  document.addEventListener('copy', (event) => {\n    const content_ptr = Module[\"ccall\"]('copy_return', 'number', ['number', 'number'], [callback, callback_data]);\n    event.clipboardData.setData('text/plain', UTF8ToString(content_ptr));\n    event.preventDefault();\n  });\n});\n\nEM_JS_INLINE(void, copy_async_js, (char const *content_ptr), {\n  /// Attempt to copy the provided text asynchronously\n  navigator.clipboard.writeText(UTF8ToString(content_ptr));\n});\n\n}\n\ninline void paste(paste_handler callback, void *callback_data) {\n  /// C++ wrapper for javascript paste call\n  paste_js(callback, callback_data);\n}\n\ninline void copy(copy_handler callback, void *callback_data) {\n  /// C++ wrapper for javascript copy call\n  copy_js(callback, callback_data);\n}\n\ninline void copy(std::string const &content) {\n  /// C++ wrapper for javascript copy call\n  copy_async_js(content.c_str());\n}\n\nnamespace {\n\nextern \"C\" {\n\nEMSCRIPTEN_KEEPALIVE inline int paste_return(char const *paste_data, paste_handler callback, void *callback_data);\n\nEMSCRIPTEN_KEEPALIVE inline int paste_return(char const *paste_data, paste_handler callback, void *callback_data) {\n  /// Call paste callback - this function is called from javascript when the paste event occurs\n  callback(paste_data, callback_data);\n  return 1;\n}\n\nEMSCRIPTEN_KEEPALIVE inline char const *copy_return(copy_handler callback, void *callback_data);\n\nEMSCRIPTEN_KEEPALIVE inline char const *copy_return(copy_handler callback, void *callback_data) {\n  /// Call paste callback - this function is called from javascript when the paste event occurs\n  return callback(callback_data);\n}\n\n}\n\n}\n\n}\n\n#endif // EMSCRIPTEN_BROWSER_CLIPBOARD_H_INCLUDED"
  },
  {
    "path": "lib/third_party/imgui/backend/include/imgui_impl_glfw.h",
    "content": "// dear imgui: Platform Backend for GLFW\n// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)\n// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)\n// (Requires: GLFW 3.0+. Prefer GLFW 3.3+/3.4+ for full feature support.)\n\n// Implemented features:\n//  [X] Platform: Clipboard support.\n//  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only).\n//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]\n//  [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.\n//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors) with GLFW 3.1+. Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.\n//  [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.\n//  [X] Multiple Dear ImGui contexts support.\n// Missing features or Issues:\n//  [ ] Platform: Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround.\n//  [ ] Platform: Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors.\n//  [ ] Platform: Multi-viewport: Missing ImGuiBackendFlags_HasParentViewport support. The viewport->ParentViewportID field is ignored, and therefore io.ConfigViewportsNoDefaultParent has no effect either.\n\n// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.\n// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.\n// Learn about Dear ImGui:\n// - FAQ                  https://dearimgui.com/faq\n// - Getting Started      https://dearimgui.com/getting-started\n// - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).\n// - Introduction, links and more at the top of imgui.cpp\n\n#pragma once\n#include \"imgui.h\"      // IMGUI_IMPL_API\n#ifndef IMGUI_DISABLE\n\nstruct GLFWwindow;\nstruct GLFWmonitor;\n\n// Follow \"Getting Started\" link and check examples/ folder to learn about using backends!\nIMGUI_IMPL_API bool     ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);\nIMGUI_IMPL_API bool     ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks);\nIMGUI_IMPL_API bool     ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks);\nIMGUI_IMPL_API void     ImGui_ImplGlfw_Shutdown();\nIMGUI_IMPL_API void     ImGui_ImplGlfw_NewFrame();\n\n// Emscripten related initialization phase methods (call after ImGui_ImplGlfw_InitForOpenGL)\n#ifdef __EMSCRIPTEN__\nIMGUI_IMPL_API void     ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector);\n//static inline void    ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) { ImGui_ImplGlfw_InstallEmscriptenCallbacks(nullptr, canvas_selector); } } // Renamed in 1.91.0\n#endif\n\n// GLFW callbacks install\n// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any.\n// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks.\nIMGUI_IMPL_API void     ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window);\nIMGUI_IMPL_API void     ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window);\n\n// GLFW callbacks options:\n// - Set 'chain_for_all_windows=true' to enable chaining callbacks for all windows (including secondary viewports created by backends or by user)\nIMGUI_IMPL_API void     ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows);\n\n// GLFW callbacks (individual callbacks to call yourself if you didn't install callbacks)\nIMGUI_IMPL_API void     ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused);        // Since 1.84\nIMGUI_IMPL_API void     ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered);        // Since 1.84\nIMGUI_IMPL_API void     ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y);   // Since 1.87\nIMGUI_IMPL_API void     ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);\nIMGUI_IMPL_API void     ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);\nIMGUI_IMPL_API void     ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);\nIMGUI_IMPL_API void     ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);\nIMGUI_IMPL_API void     ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event);\n\n// GLFW helpers\nIMGUI_IMPL_API void     ImGui_ImplGlfw_Sleep(int milliseconds);\nIMGUI_IMPL_API float    ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window);\nIMGUI_IMPL_API float    ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor);\n\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/backend/include/imgui_impl_opengl3.h",
    "content": "// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline\n// - Desktop GL: 2.x 3.x 4.x\n// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)\n// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)\n\n// Implemented features:\n//  [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!\n//  [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!]\n//  [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).\n//  [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.\n\n// About WebGL/ES:\n// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES.\n// - This is done automatically on iOS, Android and Emscripten targets.\n// - For other targets, the define needs to be visible from the imgui_impl_opengl3.cpp compilation unit. If unsure, define globally or in imconfig.h.\n\n// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.\n// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.\n// Learn about Dear ImGui:\n// - FAQ                  https://dearimgui.com/faq\n// - Getting Started      https://dearimgui.com/getting-started\n// - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).\n// - Introduction, links and more at the top of imgui.cpp\n\n// About GLSL version:\n//  The 'glsl_version' initialization parameter should be nullptr (default) or a \"#version XXX\" string.\n//  On computer platform the GLSL version default to \"#version 130\". On OpenGL ES 3 platform it defaults to \"#version 300 es\"\n//  Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.\n\n#pragma once\n#include \"imgui.h\"      // IMGUI_IMPL_API\n#ifndef IMGUI_DISABLE\n\n// Follow \"Getting Started\" link and check examples/ folder to learn about using backends!\nIMGUI_IMPL_API bool     ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);\nIMGUI_IMPL_API void     ImGui_ImplOpenGL3_Shutdown();\nIMGUI_IMPL_API void     ImGui_ImplOpenGL3_NewFrame();\nIMGUI_IMPL_API void     ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);\n\n// (Optional) Called by Init/NewFrame/Shutdown\nIMGUI_IMPL_API bool     ImGui_ImplOpenGL3_CreateDeviceObjects();\nIMGUI_IMPL_API void     ImGui_ImplOpenGL3_DestroyDeviceObjects();\n\n// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = nullptr to handle this manually.\nIMGUI_IMPL_API void     ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex);\n\n// Configuration flags to add in your imconfig file:\n//#define IMGUI_IMPL_OPENGL_ES2     // Enable ES 2 (Auto-detected on Emscripten)\n//#define IMGUI_IMPL_OPENGL_ES3     // Enable ES 3 (Auto-detected on iOS/Android)\n\n// You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.\n#if !defined(IMGUI_IMPL_OPENGL_ES2) \\\n && !defined(IMGUI_IMPL_OPENGL_ES3)\n\n// Try to detect GLES on matching platforms\n#if defined(__APPLE__)\n#include <TargetConditionals.h>\n#endif\n#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))\n#define IMGUI_IMPL_OPENGL_ES3               // iOS, Android  -> GL ES 3, \"#version 300 es\"\n#elif defined(__EMSCRIPTEN__) || defined(__amigaos4__)\n#define IMGUI_IMPL_OPENGL_ES2               // Emscripten    -> GL ES 2, \"#version 100\"\n#else\n// Otherwise imgui_impl_opengl3_loader.h will be used.\n#endif\n\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/backend/include/imgui_impl_opengl3_loader.h",
    "content": "/*\n * This file was generated with gl3w_gen.py, part of imgl3w\n * (hosted at https://github.com/dearimgui/gl3w_stripped)\n *\n * This is free and unencumbered software released into the public domain.\n *\n * Anyone is free to copy, modify, publish, use, compile, sell, or\n * distribute this software, either in source code form or as a compiled\n * binary, for any purpose, commercial or non-commercial, and by any\n * means.\n *\n * In jurisdictions that recognize copyright laws, the author or authors\n * of this software dedicate any and all copyright interest in the\n * software to the public domain. We make this dedication for the benefit\n * of the public at large and to the detriment of our heirs and\n * successors. We intend this dedication to be an overt act of\n * relinquishment in perpetuity of all present and future rights to this\n * software under copyright law.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n */\n// We embed our own OpenGL loader to not require user to provide their own or to have to use ours, which proved to be endless problems for users.\n// Our loader is custom-generated, based on gl3w but automatically filtered to only include enums/functions that we use in this source file.\n// Regenerate with: python gl3w_gen.py --imgui-dir /path/to/imgui/\n// see https://github.com/dearimgui/gl3w_stripped for more info.\n#ifndef __gl3w_h_\n#define __gl3w_h_\n\n#if defined(__APPLE__)\n#include <OpenGL/gl.h>\n#elif !defined(__EMSCRIPTEN__)\n#include <GL/gl.h>\n#endif\n\n#if __has_include(<KHR/khrplatform.h>)\n    #include <KHR/khrplatform.h>\n#else\n    // Adapted from KHR/khrplatform.h to avoid including entire file.\n    typedef          float         khronos_float_t;\n    typedef signed   char          khronos_int8_t;\n    typedef unsigned char          khronos_uint8_t;\n    typedef signed   short int     khronos_int16_t;\n    typedef unsigned short int     khronos_uint16_t;\n    #ifdef _WIN64\n    typedef signed   long long int khronos_intptr_t;\n    typedef signed   long long int khronos_ssize_t;\n    #else\n    typedef signed   long  int     khronos_intptr_t;\n    typedef signed   long  int     khronos_ssize_t;\n    #endif\n    \n    #if defined(_MSC_VER) && !defined(__clang__)\n    typedef signed   __int64       khronos_int64_t;\n    typedef unsigned __int64       khronos_uint64_t;\n    #elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100)\n    #include <stdint.h>\n    typedef          int64_t       khronos_int64_t;\n    typedef          uint64_t      khronos_uint64_t;\n    #else\n    typedef signed   long long     khronos_int64_t;\n    typedef unsigned long long     khronos_uint64_t;\n    #endif\n\n#endif\n\n#ifndef __gl_glcorearb_h_\n#define __gl_glcorearb_h_ 1\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n/*\n** Copyright 2013-2020 The Khronos Group Inc.\n** SPDX-License-Identifier: MIT\n**\n** This header is generated from the Khronos OpenGL / OpenGL ES XML\n** API Registry. The current version of the Registry, generator scripts\n** used to make the header, and the header can be found at\n**   https://github.com/KhronosGroup/OpenGL-Registry\n*/\n#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN 1\n#endif\n#include <windows.h>\n#endif\n#ifndef APIENTRY\n#define APIENTRY\n#endif\n#ifndef APIENTRYP\n#define APIENTRYP APIENTRY *\n#endif\n#ifndef GLAPI\n#define GLAPI extern\n#endif\n/* glcorearb.h is for use with OpenGL core profile implementations.\n** It should should be placed in the same directory as gl.h and\n** included as <GL/glcorearb.h>.\n**\n** glcorearb.h includes only APIs in the latest OpenGL core profile\n** implementation together with APIs in newer ARB extensions which \n** can be supported by the core profile. It does not, and never will\n** include functionality removed from the core profile, such as\n** fixed-function vertex and fragment processing.\n**\n** Do not #include both <GL/glcorearb.h> and either of <GL/gl.h> or\n** <GL/glext.h> in the same source file.\n*/\n/* Generated C header for:\n * API: gl\n * Profile: core\n * Versions considered: .*\n * Versions emitted: .*\n * Default extensions included: glcore\n * Additional extensions included: _nomatch_^\n * Extensions removed: _nomatch_^\n */\n#ifndef GL3W_GL_VERSION_1_0\n#define GL3W_GL_VERSION_1_0 1\ntypedef void GLvoid;\ntypedef unsigned int GLenum;\n\ntypedef khronos_float_t GLfloat;\ntypedef int GLint;\ntypedef int GLsizei;\ntypedef unsigned int GLbitfield;\ntypedef double GLdouble;\ntypedef unsigned int GLuint;\ntypedef unsigned char GLboolean;\ntypedef khronos_uint8_t GLubyte;\n#define GL_DEPTH_BUFFER_BIT               0x00000100\n#define GL_STENCIL_BUFFER_BIT             0x00000400\n#define GL_COLOR_BUFFER_BIT               0x00004000\n#define GL_FALSE                          0\n#define GL_TRUE                           1\n#define GL_POINTS                         0x0000\n#define GL_LINES                          0x0001\n#define GL_LINE_LOOP                      0x0002\n#define GL_LINE_STRIP                     0x0003\n#define GL_TRIANGLES                      0x0004\n#define GL_TRIANGLE_STRIP                 0x0005\n#define GL_TRIANGLE_FAN                   0x0006\n#define GL_QUADS                          0x0007\n#define GL_NEVER                          0x0200\n#define GL_LESS                           0x0201\n#define GL_EQUAL                          0x0202\n#define GL_LEQUAL                         0x0203\n#define GL_GREATER                        0x0204\n#define GL_NOTEQUAL                       0x0205\n#define GL_GEQUAL                         0x0206\n#define GL_ALWAYS                         0x0207\n#define GL_ZERO                           0\n#define GL_ONE                            1\n#define GL_SRC_COLOR                      0x0300\n#define GL_ONE_MINUS_SRC_COLOR            0x0301\n#define GL_SRC_ALPHA                      0x0302\n#define GL_ONE_MINUS_SRC_ALPHA            0x0303\n#define GL_DST_ALPHA                      0x0304\n#define GL_ONE_MINUS_DST_ALPHA            0x0305\n#define GL_DST_COLOR                      0x0306\n#define GL_ONE_MINUS_DST_COLOR            0x0307\n#define GL_SRC_ALPHA_SATURATE             0x0308\n#define GL_NONE                           0\n#define GL_FRONT_LEFT                     0x0400\n#define GL_FRONT_RIGHT                    0x0401\n#define GL_BACK_LEFT                      0x0402\n#define GL_BACK_RIGHT                     0x0403\n#define GL_FRONT                          0x0404\n#define GL_BACK                           0x0405\n#define GL_LEFT                           0x0406\n#define GL_RIGHT                          0x0407\n#define GL_FRONT_AND_BACK                 0x0408\n#define GL_NO_ERROR                       0\n#define GL_INVALID_ENUM                   0x0500\n#define GL_INVALID_VALUE                  0x0501\n#define GL_INVALID_OPERATION              0x0502\n#define GL_OUT_OF_MEMORY                  0x0505\n#define GL_CW                             0x0900\n#define GL_CCW                            0x0901\n#define GL_POINT_SIZE                     0x0B11\n#define GL_POINT_SIZE_RANGE               0x0B12\n#define GL_POINT_SIZE_GRANULARITY         0x0B13\n#define GL_LINE_SMOOTH                    0x0B20\n#define GL_LINE_WIDTH                     0x0B21\n#define GL_LINE_WIDTH_RANGE               0x0B22\n#define GL_LINE_WIDTH_GRANULARITY         0x0B23\n#define GL_POLYGON_MODE                   0x0B40\n#define GL_POLYGON_SMOOTH                 0x0B41\n#define GL_CULL_FACE                      0x0B44\n#define GL_CULL_FACE_MODE                 0x0B45\n#define GL_FRONT_FACE                     0x0B46\n#define GL_DEPTH_RANGE                    0x0B70\n#define GL_DEPTH_TEST                     0x0B71\n#define GL_DEPTH_WRITEMASK                0x0B72\n#define GL_DEPTH_CLEAR_VALUE              0x0B73\n#define GL_DEPTH_FUNC                     0x0B74\n#define GL_STENCIL_TEST                   0x0B90\n#define GL_STENCIL_CLEAR_VALUE            0x0B91\n#define GL_STENCIL_FUNC                   0x0B92\n#define GL_STENCIL_VALUE_MASK             0x0B93\n#define GL_STENCIL_FAIL                   0x0B94\n#define GL_STENCIL_PASS_DEPTH_FAIL        0x0B95\n#define GL_STENCIL_PASS_DEPTH_PASS        0x0B96\n#define GL_STENCIL_REF                    0x0B97\n#define GL_STENCIL_WRITEMASK              0x0B98\n#define GL_VIEWPORT                       0x0BA2\n#define GL_DITHER                         0x0BD0\n#define GL_BLEND_DST                      0x0BE0\n#define GL_BLEND_SRC                      0x0BE1\n#define GL_BLEND                          0x0BE2\n#define GL_LOGIC_OP_MODE                  0x0BF0\n#define GL_DRAW_BUFFER                    0x0C01\n#define GL_READ_BUFFER                    0x0C02\n#define GL_SCISSOR_BOX                    0x0C10\n#define GL_SCISSOR_TEST                   0x0C11\n#define GL_COLOR_CLEAR_VALUE              0x0C22\n#define GL_COLOR_WRITEMASK                0x0C23\n#define GL_DOUBLEBUFFER                   0x0C32\n#define GL_STEREO                         0x0C33\n#define GL_LINE_SMOOTH_HINT               0x0C52\n#define GL_POLYGON_SMOOTH_HINT            0x0C53\n#define GL_UNPACK_SWAP_BYTES              0x0CF0\n#define GL_UNPACK_LSB_FIRST               0x0CF1\n#define GL_UNPACK_ROW_LENGTH              0x0CF2\n#define GL_UNPACK_SKIP_ROWS               0x0CF3\n#define GL_UNPACK_SKIP_PIXELS             0x0CF4\n#define GL_UNPACK_ALIGNMENT               0x0CF5\n#define GL_PACK_SWAP_BYTES                0x0D00\n#define GL_PACK_LSB_FIRST                 0x0D01\n#define GL_PACK_ROW_LENGTH                0x0D02\n#define GL_PACK_SKIP_ROWS                 0x0D03\n#define GL_PACK_SKIP_PIXELS               0x0D04\n#define GL_PACK_ALIGNMENT                 0x0D05\n#define GL_MAX_TEXTURE_SIZE               0x0D33\n#define GL_MAX_VIEWPORT_DIMS              0x0D3A\n#define GL_SUBPIXEL_BITS                  0x0D50\n#define GL_TEXTURE_1D                     0x0DE0\n#define GL_TEXTURE_2D                     0x0DE1\n#define GL_TEXTURE_WIDTH                  0x1000\n#define GL_TEXTURE_HEIGHT                 0x1001\n#define GL_TEXTURE_BORDER_COLOR           0x1004\n#define GL_DONT_CARE                      0x1100\n#define GL_FASTEST                        0x1101\n#define GL_NICEST                         0x1102\n#define GL_BYTE                           0x1400\n#define GL_UNSIGNED_BYTE                  0x1401\n#define GL_SHORT                          0x1402\n#define GL_UNSIGNED_SHORT                 0x1403\n#define GL_INT                            0x1404\n#define GL_UNSIGNED_INT                   0x1405\n#define GL_FLOAT                          0x1406\n#define GL_STACK_OVERFLOW                 0x0503\n#define GL_STACK_UNDERFLOW                0x0504\n#define GL_CLEAR                          0x1500\n#define GL_AND                            0x1501\n#define GL_AND_REVERSE                    0x1502\n#define GL_COPY                           0x1503\n#define GL_AND_INVERTED                   0x1504\n#define GL_NOOP                           0x1505\n#define GL_XOR                            0x1506\n#define GL_OR                             0x1507\n#define GL_NOR                            0x1508\n#define GL_EQUIV                          0x1509\n#define GL_INVERT                         0x150A\n#define GL_OR_REVERSE                     0x150B\n#define GL_COPY_INVERTED                  0x150C\n#define GL_OR_INVERTED                    0x150D\n#define GL_NAND                           0x150E\n#define GL_SET                            0x150F\n#define GL_TEXTURE                        0x1702\n#define GL_COLOR                          0x1800\n#define GL_DEPTH                          0x1801\n#define GL_STENCIL                        0x1802\n#define GL_STENCIL_INDEX                  0x1901\n#define GL_DEPTH_COMPONENT                0x1902\n#define GL_RED                            0x1903\n#define GL_GREEN                          0x1904\n#define GL_BLUE                           0x1905\n#define GL_ALPHA                          0x1906\n#define GL_RGB                            0x1907\n#define GL_RGBA                           0x1908\n#define GL_POINT                          0x1B00\n#define GL_LINE                           0x1B01\n#define GL_FILL                           0x1B02\n#define GL_KEEP                           0x1E00\n#define GL_REPLACE                        0x1E01\n#define GL_INCR                           0x1E02\n#define GL_DECR                           0x1E03\n#define GL_VENDOR                         0x1F00\n#define GL_RENDERER                       0x1F01\n#define GL_VERSION                        0x1F02\n#define GL_EXTENSIONS                     0x1F03\n#define GL_NEAREST                        0x2600\n#define GL_LINEAR                         0x2601\n#define GL_NEAREST_MIPMAP_NEAREST         0x2700\n#define GL_LINEAR_MIPMAP_NEAREST          0x2701\n#define GL_NEAREST_MIPMAP_LINEAR          0x2702\n#define GL_LINEAR_MIPMAP_LINEAR           0x2703\n#define GL_TEXTURE_MAG_FILTER             0x2800\n#define GL_TEXTURE_MIN_FILTER             0x2801\n#define GL_TEXTURE_WRAP_S                 0x2802\n#define GL_TEXTURE_WRAP_T                 0x2803\n#define GL_REPEAT                         0x2901\ntypedef void (APIENTRYP PFNGLCULLFACEPROC) (GLenum mode);\ntypedef void (APIENTRYP PFNGLFRONTFACEPROC) (GLenum mode);\ntypedef void (APIENTRYP PFNGLHINTPROC) (GLenum target, GLenum mode);\ntypedef void (APIENTRYP PFNGLLINEWIDTHPROC) (GLfloat width);\ntypedef void (APIENTRYP PFNGLPOINTSIZEPROC) (GLfloat size);\ntypedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode);\ntypedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLTEXPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat param);\ntypedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params);\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params);\ntypedef void (APIENTRYP PFNGLTEXIMAGE1DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLDRAWBUFFERPROC) (GLenum buf);\ntypedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask);\ntypedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\ntypedef void (APIENTRYP PFNGLCLEARSTENCILPROC) (GLint s);\ntypedef void (APIENTRYP PFNGLCLEARDEPTHPROC) (GLdouble depth);\ntypedef void (APIENTRYP PFNGLSTENCILMASKPROC) (GLuint mask);\ntypedef void (APIENTRYP PFNGLCOLORMASKPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);\ntypedef void (APIENTRYP PFNGLDEPTHMASKPROC) (GLboolean flag);\ntypedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap);\ntypedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap);\ntypedef void (APIENTRYP PFNGLFINISHPROC) (void);\ntypedef void (APIENTRYP PFNGLFLUSHPROC) (void);\ntypedef void (APIENTRYP PFNGLBLENDFUNCPROC) (GLenum sfactor, GLenum dfactor);\ntypedef void (APIENTRYP PFNGLLOGICOPPROC) (GLenum opcode);\ntypedef void (APIENTRYP PFNGLSTENCILFUNCPROC) (GLenum func, GLint ref, GLuint mask);\ntypedef void (APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum zpass);\ntypedef void (APIENTRYP PFNGLDEPTHFUNCPROC) (GLenum func);\ntypedef void (APIENTRYP PFNGLPIXELSTOREFPROC) (GLenum pname, GLfloat param);\ntypedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLREADBUFFERPROC) (GLenum src);\ntypedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);\ntypedef void (APIENTRYP PFNGLGETBOOLEANVPROC) (GLenum pname, GLboolean *data);\ntypedef void (APIENTRYP PFNGLGETDOUBLEVPROC) (GLenum pname, GLdouble *data);\ntypedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void);\ntypedef void (APIENTRYP PFNGLGETFLOATVPROC) (GLenum pname, GLfloat *data);\ntypedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data);\ntypedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name);\ntypedef void (APIENTRYP PFNGLGETTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, void *pixels);\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC) (GLenum target, GLint level, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC) (GLenum target, GLint level, GLenum pname, GLint *params);\ntypedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap);\ntypedef void (APIENTRYP PFNGLDEPTHRANGEPROC) (GLdouble n, GLdouble f);\ntypedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glCullFace (GLenum mode);\nGLAPI void APIENTRY glFrontFace (GLenum mode);\nGLAPI void APIENTRY glHint (GLenum target, GLenum mode);\nGLAPI void APIENTRY glLineWidth (GLfloat width);\nGLAPI void APIENTRY glPointSize (GLfloat size);\nGLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode);\nGLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param);\nGLAPI void APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params);\nGLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param);\nGLAPI void APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params);\nGLAPI void APIENTRY glTexImage1D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels);\nGLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);\nGLAPI void APIENTRY glDrawBuffer (GLenum buf);\nGLAPI void APIENTRY glClear (GLbitfield mask);\nGLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\nGLAPI void APIENTRY glClearStencil (GLint s);\nGLAPI void APIENTRY glClearDepth (GLdouble depth);\nGLAPI void APIENTRY glStencilMask (GLuint mask);\nGLAPI void APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);\nGLAPI void APIENTRY glDepthMask (GLboolean flag);\nGLAPI void APIENTRY glDisable (GLenum cap);\nGLAPI void APIENTRY glEnable (GLenum cap);\nGLAPI void APIENTRY glFinish (void);\nGLAPI void APIENTRY glFlush (void);\nGLAPI void APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor);\nGLAPI void APIENTRY glLogicOp (GLenum opcode);\nGLAPI void APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask);\nGLAPI void APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass);\nGLAPI void APIENTRY glDepthFunc (GLenum func);\nGLAPI void APIENTRY glPixelStoref (GLenum pname, GLfloat param);\nGLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param);\nGLAPI void APIENTRY glReadBuffer (GLenum src);\nGLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);\nGLAPI void APIENTRY glGetBooleanv (GLenum pname, GLboolean *data);\nGLAPI void APIENTRY glGetDoublev (GLenum pname, GLdouble *data);\nGLAPI GLenum APIENTRY glGetError (void);\nGLAPI void APIENTRY glGetFloatv (GLenum pname, GLfloat *data);\nGLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data);\nGLAPI const GLubyte *APIENTRY glGetString (GLenum name);\nGLAPI void APIENTRY glGetTexImage (GLenum target, GLint level, GLenum format, GLenum type, void *pixels);\nGLAPI void APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params);\nGLAPI void APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetTexLevelParameterfv (GLenum target, GLint level, GLenum pname, GLfloat *params);\nGLAPI void APIENTRY glGetTexLevelParameteriv (GLenum target, GLint level, GLenum pname, GLint *params);\nGLAPI GLboolean APIENTRY glIsEnabled (GLenum cap);\nGLAPI void APIENTRY glDepthRange (GLdouble n, GLdouble f);\nGLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);\n#endif\n#endif /* GL3W_GL_VERSION_1_0 */\n#ifndef GL3W_GL_VERSION_1_1\n#define GL3W_GL_VERSION_1_1 1\ntypedef khronos_float_t GLclampf;\ntypedef double GLclampd;\n#define GL_COLOR_LOGIC_OP                 0x0BF2\n#define GL_POLYGON_OFFSET_UNITS           0x2A00\n#define GL_POLYGON_OFFSET_POINT           0x2A01\n#define GL_POLYGON_OFFSET_LINE            0x2A02\n#define GL_POLYGON_OFFSET_FILL            0x8037\n#define GL_POLYGON_OFFSET_FACTOR          0x8038\n#define GL_TEXTURE_BINDING_1D             0x8068\n#define GL_TEXTURE_BINDING_2D             0x8069\n#define GL_TEXTURE_INTERNAL_FORMAT        0x1003\n#define GL_TEXTURE_RED_SIZE               0x805C\n#define GL_TEXTURE_GREEN_SIZE             0x805D\n#define GL_TEXTURE_BLUE_SIZE              0x805E\n#define GL_TEXTURE_ALPHA_SIZE             0x805F\n#define GL_DOUBLE                         0x140A\n#define GL_PROXY_TEXTURE_1D               0x8063\n#define GL_PROXY_TEXTURE_2D               0x8064\n#define GL_R3_G3_B2                       0x2A10\n#define GL_RGB4                           0x804F\n#define GL_RGB5                           0x8050\n#define GL_RGB8                           0x8051\n#define GL_RGB10                          0x8052\n#define GL_RGB12                          0x8053\n#define GL_RGB16                          0x8054\n#define GL_RGBA2                          0x8055\n#define GL_RGBA4                          0x8056\n#define GL_RGB5_A1                        0x8057\n#define GL_RGBA8                          0x8058\n#define GL_RGB10_A2                       0x8059\n#define GL_RGBA12                         0x805A\n#define GL_RGBA16                         0x805B\n#define GL_VERTEX_ARRAY                   0x8074\ntypedef void (APIENTRYP PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count);\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices);\ntypedef void (APIENTRYP PFNGLGETPOINTERVPROC) (GLenum pname, void **params);\ntypedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC) (GLfloat factor, GLfloat units);\ntypedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border);\ntypedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture);\ntypedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures);\ntypedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures);\ntypedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC) (GLuint texture);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);\nGLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);\nGLAPI void APIENTRY glGetPointerv (GLenum pname, void **params);\nGLAPI void APIENTRY glPolygonOffset (GLfloat factor, GLfloat units);\nGLAPI void APIENTRY glCopyTexImage1D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border);\nGLAPI void APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);\nGLAPI void APIENTRY glCopyTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);\nGLAPI void APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels);\nGLAPI void APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);\nGLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture);\nGLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures);\nGLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures);\nGLAPI GLboolean APIENTRY glIsTexture (GLuint texture);\n#endif\n#endif /* GL3W_GL_VERSION_1_1 */\n#ifndef GL3W_GL_VERSION_1_2\n#define GL3W_GL_VERSION_1_2 1\n#define GL_UNSIGNED_BYTE_3_3_2            0x8032\n#define GL_UNSIGNED_SHORT_4_4_4_4         0x8033\n#define GL_UNSIGNED_SHORT_5_5_5_1         0x8034\n#define GL_UNSIGNED_INT_8_8_8_8           0x8035\n#define GL_UNSIGNED_INT_10_10_10_2        0x8036\n#define GL_TEXTURE_BINDING_3D             0x806A\n#define GL_PACK_SKIP_IMAGES               0x806B\n#define GL_PACK_IMAGE_HEIGHT              0x806C\n#define GL_UNPACK_SKIP_IMAGES             0x806D\n#define GL_UNPACK_IMAGE_HEIGHT            0x806E\n#define GL_TEXTURE_3D                     0x806F\n#define GL_PROXY_TEXTURE_3D               0x8070\n#define GL_TEXTURE_DEPTH                  0x8071\n#define GL_TEXTURE_WRAP_R                 0x8072\n#define GL_MAX_3D_TEXTURE_SIZE            0x8073\n#define GL_UNSIGNED_BYTE_2_3_3_REV        0x8362\n#define GL_UNSIGNED_SHORT_5_6_5           0x8363\n#define GL_UNSIGNED_SHORT_5_6_5_REV       0x8364\n#define GL_UNSIGNED_SHORT_4_4_4_4_REV     0x8365\n#define GL_UNSIGNED_SHORT_1_5_5_5_REV     0x8366\n#define GL_UNSIGNED_INT_8_8_8_8_REV       0x8367\n#define GL_UNSIGNED_INT_2_10_10_10_REV    0x8368\n#define GL_BGR                            0x80E0\n#define GL_BGRA                           0x80E1\n#define GL_MAX_ELEMENTS_VERTICES          0x80E8\n#define GL_MAX_ELEMENTS_INDICES           0x80E9\n#define GL_CLAMP_TO_EDGE                  0x812F\n#define GL_TEXTURE_MIN_LOD                0x813A\n#define GL_TEXTURE_MAX_LOD                0x813B\n#define GL_TEXTURE_BASE_LEVEL             0x813C\n#define GL_TEXTURE_MAX_LEVEL              0x813D\n#define GL_SMOOTH_POINT_SIZE_RANGE        0x0B12\n#define GL_SMOOTH_POINT_SIZE_GRANULARITY  0x0B13\n#define GL_SMOOTH_LINE_WIDTH_RANGE        0x0B22\n#define GL_SMOOTH_LINE_WIDTH_GRANULARITY  0x0B23\n#define GL_ALIASED_LINE_WIDTH_RANGE       0x846E\ntypedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices);\ntypedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices);\nGLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels);\nGLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);\nGLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);\n#endif\n#endif /* GL3W_GL_VERSION_1_2 */\n#ifndef GL3W_GL_VERSION_1_3\n#define GL3W_GL_VERSION_1_3 1\n#define GL_TEXTURE0                       0x84C0\n#define GL_TEXTURE1                       0x84C1\n#define GL_TEXTURE2                       0x84C2\n#define GL_TEXTURE3                       0x84C3\n#define GL_TEXTURE4                       0x84C4\n#define GL_TEXTURE5                       0x84C5\n#define GL_TEXTURE6                       0x84C6\n#define GL_TEXTURE7                       0x84C7\n#define GL_TEXTURE8                       0x84C8\n#define GL_TEXTURE9                       0x84C9\n#define GL_TEXTURE10                      0x84CA\n#define GL_TEXTURE11                      0x84CB\n#define GL_TEXTURE12                      0x84CC\n#define GL_TEXTURE13                      0x84CD\n#define GL_TEXTURE14                      0x84CE\n#define GL_TEXTURE15                      0x84CF\n#define GL_TEXTURE16                      0x84D0\n#define GL_TEXTURE17                      0x84D1\n#define GL_TEXTURE18                      0x84D2\n#define GL_TEXTURE19                      0x84D3\n#define GL_TEXTURE20                      0x84D4\n#define GL_TEXTURE21                      0x84D5\n#define GL_TEXTURE22                      0x84D6\n#define GL_TEXTURE23                      0x84D7\n#define GL_TEXTURE24                      0x84D8\n#define GL_TEXTURE25                      0x84D9\n#define GL_TEXTURE26                      0x84DA\n#define GL_TEXTURE27                      0x84DB\n#define GL_TEXTURE28                      0x84DC\n#define GL_TEXTURE29                      0x84DD\n#define GL_TEXTURE30                      0x84DE\n#define GL_TEXTURE31                      0x84DF\n#define GL_ACTIVE_TEXTURE                 0x84E0\n#define GL_MULTISAMPLE                    0x809D\n#define GL_SAMPLE_ALPHA_TO_COVERAGE       0x809E\n#define GL_SAMPLE_ALPHA_TO_ONE            0x809F\n#define GL_SAMPLE_COVERAGE                0x80A0\n#define GL_SAMPLE_BUFFERS                 0x80A8\n#define GL_SAMPLES                        0x80A9\n#define GL_SAMPLE_COVERAGE_VALUE          0x80AA\n#define GL_SAMPLE_COVERAGE_INVERT         0x80AB\n#define GL_TEXTURE_CUBE_MAP               0x8513\n#define GL_TEXTURE_BINDING_CUBE_MAP       0x8514\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_X    0x8515\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X    0x8516\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y    0x8517\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y    0x8518\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z    0x8519\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z    0x851A\n#define GL_PROXY_TEXTURE_CUBE_MAP         0x851B\n#define GL_MAX_CUBE_MAP_TEXTURE_SIZE      0x851C\n#define GL_COMPRESSED_RGB                 0x84ED\n#define GL_COMPRESSED_RGBA                0x84EE\n#define GL_TEXTURE_COMPRESSION_HINT       0x84EF\n#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE  0x86A0\n#define GL_TEXTURE_COMPRESSED             0x86A1\n#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2\n#define GL_COMPRESSED_TEXTURE_FORMATS     0x86A3\n#define GL_CLAMP_TO_BORDER                0x812D\ntypedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);\ntypedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data);\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, void *img);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glActiveTexture (GLenum texture);\nGLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert);\nGLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data);\nGLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data);\nGLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data);\nGLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data);\nGLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);\nGLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data);\nGLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, void *img);\n#endif\n#endif /* GL3W_GL_VERSION_1_3 */\n#ifndef GL3W_GL_VERSION_1_4\n#define GL3W_GL_VERSION_1_4 1\n#define GL_BLEND_DST_RGB                  0x80C8\n#define GL_BLEND_SRC_RGB                  0x80C9\n#define GL_BLEND_DST_ALPHA                0x80CA\n#define GL_BLEND_SRC_ALPHA                0x80CB\n#define GL_POINT_FADE_THRESHOLD_SIZE      0x8128\n#define GL_DEPTH_COMPONENT16              0x81A5\n#define GL_DEPTH_COMPONENT24              0x81A6\n#define GL_DEPTH_COMPONENT32              0x81A7\n#define GL_MIRRORED_REPEAT                0x8370\n#define GL_MAX_TEXTURE_LOD_BIAS           0x84FD\n#define GL_TEXTURE_LOD_BIAS               0x8501\n#define GL_INCR_WRAP                      0x8507\n#define GL_DECR_WRAP                      0x8508\n#define GL_TEXTURE_DEPTH_SIZE             0x884A\n#define GL_TEXTURE_COMPARE_MODE           0x884C\n#define GL_TEXTURE_COMPARE_FUNC           0x884D\n#define GL_BLEND_COLOR                    0x8005\n#define GL_BLEND_EQUATION                 0x8009\n#define GL_CONSTANT_COLOR                 0x8001\n#define GL_ONE_MINUS_CONSTANT_COLOR       0x8002\n#define GL_CONSTANT_ALPHA                 0x8003\n#define GL_ONE_MINUS_CONSTANT_ALPHA       0x8004\n#define GL_FUNC_ADD                       0x8006\n#define GL_FUNC_REVERSE_SUBTRACT          0x800B\n#define GL_FUNC_SUBTRACT                  0x800A\n#define GL_MIN                            0x8007\n#define GL_MAX                            0x8008\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount);\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount);\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param);\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params);\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params);\ntypedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);\nGLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount);\nGLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount);\nGLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param);\nGLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params);\nGLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param);\nGLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params);\nGLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\nGLAPI void APIENTRY glBlendEquation (GLenum mode);\n#endif\n#endif /* GL3W_GL_VERSION_1_4 */\n#ifndef GL3W_GL_VERSION_1_5\n#define GL3W_GL_VERSION_1_5 1\ntypedef khronos_ssize_t GLsizeiptr;\ntypedef khronos_intptr_t GLintptr;\n#define GL_BUFFER_SIZE                    0x8764\n#define GL_BUFFER_USAGE                   0x8765\n#define GL_QUERY_COUNTER_BITS             0x8864\n#define GL_CURRENT_QUERY                  0x8865\n#define GL_QUERY_RESULT                   0x8866\n#define GL_QUERY_RESULT_AVAILABLE         0x8867\n#define GL_ARRAY_BUFFER                   0x8892\n#define GL_ELEMENT_ARRAY_BUFFER           0x8893\n#define GL_ARRAY_BUFFER_BINDING           0x8894\n#define GL_ELEMENT_ARRAY_BUFFER_BINDING   0x8895\n#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F\n#define GL_READ_ONLY                      0x88B8\n#define GL_WRITE_ONLY                     0x88B9\n#define GL_READ_WRITE                     0x88BA\n#define GL_BUFFER_ACCESS                  0x88BB\n#define GL_BUFFER_MAPPED                  0x88BC\n#define GL_BUFFER_MAP_POINTER             0x88BD\n#define GL_STREAM_DRAW                    0x88E0\n#define GL_STREAM_READ                    0x88E1\n#define GL_STREAM_COPY                    0x88E2\n#define GL_STATIC_DRAW                    0x88E4\n#define GL_STATIC_READ                    0x88E5\n#define GL_STATIC_COPY                    0x88E6\n#define GL_DYNAMIC_DRAW                   0x88E8\n#define GL_DYNAMIC_READ                   0x88E9\n#define GL_DYNAMIC_COPY                   0x88EA\n#define GL_SAMPLES_PASSED                 0x8914\n#define GL_SRC1_ALPHA                     0x8589\ntypedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids);\ntypedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids);\ntypedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id);\ntypedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id);\ntypedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target);\ntypedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params);\ntypedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);\ntypedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);\ntypedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);\ntypedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer);\ntypedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage);\ntypedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);\ntypedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data);\ntypedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access);\ntypedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target);\ntypedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, void **params);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids);\nGLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids);\nGLAPI GLboolean APIENTRY glIsQuery (GLuint id);\nGLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id);\nGLAPI void APIENTRY glEndQuery (GLenum target);\nGLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params);\nGLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer);\nGLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers);\nGLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers);\nGLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer);\nGLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage);\nGLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);\nGLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, void *data);\nGLAPI void *APIENTRY glMapBuffer (GLenum target, GLenum access);\nGLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target);\nGLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, void **params);\n#endif\n#endif /* GL3W_GL_VERSION_1_5 */\n#ifndef GL3W_GL_VERSION_2_0\n#define GL3W_GL_VERSION_2_0 1\ntypedef char GLchar;\ntypedef khronos_int16_t GLshort;\ntypedef khronos_int8_t GLbyte;\ntypedef khronos_uint16_t GLushort;\n#define GL_BLEND_EQUATION_RGB             0x8009\n#define GL_VERTEX_ATTRIB_ARRAY_ENABLED    0x8622\n#define GL_VERTEX_ATTRIB_ARRAY_SIZE       0x8623\n#define GL_VERTEX_ATTRIB_ARRAY_STRIDE     0x8624\n#define GL_VERTEX_ATTRIB_ARRAY_TYPE       0x8625\n#define GL_CURRENT_VERTEX_ATTRIB          0x8626\n#define GL_VERTEX_PROGRAM_POINT_SIZE      0x8642\n#define GL_VERTEX_ATTRIB_ARRAY_POINTER    0x8645\n#define GL_STENCIL_BACK_FUNC              0x8800\n#define GL_STENCIL_BACK_FAIL              0x8801\n#define GL_STENCIL_BACK_PASS_DEPTH_FAIL   0x8802\n#define GL_STENCIL_BACK_PASS_DEPTH_PASS   0x8803\n#define GL_MAX_DRAW_BUFFERS               0x8824\n#define GL_DRAW_BUFFER0                   0x8825\n#define GL_DRAW_BUFFER1                   0x8826\n#define GL_DRAW_BUFFER2                   0x8827\n#define GL_DRAW_BUFFER3                   0x8828\n#define GL_DRAW_BUFFER4                   0x8829\n#define GL_DRAW_BUFFER5                   0x882A\n#define GL_DRAW_BUFFER6                   0x882B\n#define GL_DRAW_BUFFER7                   0x882C\n#define GL_DRAW_BUFFER8                   0x882D\n#define GL_DRAW_BUFFER9                   0x882E\n#define GL_DRAW_BUFFER10                  0x882F\n#define GL_DRAW_BUFFER11                  0x8830\n#define GL_DRAW_BUFFER12                  0x8831\n#define GL_DRAW_BUFFER13                  0x8832\n#define GL_DRAW_BUFFER14                  0x8833\n#define GL_DRAW_BUFFER15                  0x8834\n#define GL_BLEND_EQUATION_ALPHA           0x883D\n#define GL_MAX_VERTEX_ATTRIBS             0x8869\n#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A\n#define GL_MAX_TEXTURE_IMAGE_UNITS        0x8872\n#define GL_FRAGMENT_SHADER                0x8B30\n#define GL_VERTEX_SHADER                  0x8B31\n#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49\n#define GL_MAX_VERTEX_UNIFORM_COMPONENTS  0x8B4A\n#define GL_MAX_VARYING_FLOATS             0x8B4B\n#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C\n#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D\n#define GL_SHADER_TYPE                    0x8B4F\n#define GL_FLOAT_VEC2                     0x8B50\n#define GL_FLOAT_VEC3                     0x8B51\n#define GL_FLOAT_VEC4                     0x8B52\n#define GL_INT_VEC2                       0x8B53\n#define GL_INT_VEC3                       0x8B54\n#define GL_INT_VEC4                       0x8B55\n#define GL_BOOL                           0x8B56\n#define GL_BOOL_VEC2                      0x8B57\n#define GL_BOOL_VEC3                      0x8B58\n#define GL_BOOL_VEC4                      0x8B59\n#define GL_FLOAT_MAT2                     0x8B5A\n#define GL_FLOAT_MAT3                     0x8B5B\n#define GL_FLOAT_MAT4                     0x8B5C\n#define GL_SAMPLER_1D                     0x8B5D\n#define GL_SAMPLER_2D                     0x8B5E\n#define GL_SAMPLER_3D                     0x8B5F\n#define GL_SAMPLER_CUBE                   0x8B60\n#define GL_SAMPLER_1D_SHADOW              0x8B61\n#define GL_SAMPLER_2D_SHADOW              0x8B62\n#define GL_DELETE_STATUS                  0x8B80\n#define GL_COMPILE_STATUS                 0x8B81\n#define GL_LINK_STATUS                    0x8B82\n#define GL_VALIDATE_STATUS                0x8B83\n#define GL_INFO_LOG_LENGTH                0x8B84\n#define GL_ATTACHED_SHADERS               0x8B85\n#define GL_ACTIVE_UNIFORMS                0x8B86\n#define GL_ACTIVE_UNIFORM_MAX_LENGTH      0x8B87\n#define GL_SHADER_SOURCE_LENGTH           0x8B88\n#define GL_ACTIVE_ATTRIBUTES              0x8B89\n#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH    0x8B8A\n#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B\n#define GL_SHADING_LANGUAGE_VERSION       0x8B8C\n#define GL_CURRENT_PROGRAM                0x8B8D\n#define GL_POINT_SPRITE_COORD_ORIGIN      0x8CA0\n#define GL_LOWER_LEFT                     0x8CA1\n#define GL_UPPER_LEFT                     0x8CA2\n#define GL_STENCIL_BACK_REF               0x8CA3\n#define GL_STENCIL_BACK_VALUE_MASK        0x8CA4\n#define GL_STENCIL_BACK_WRITEMASK         0x8CA5\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha);\ntypedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs);\ntypedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);\ntypedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask);\ntypedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask);\ntypedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);\ntypedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name);\ntypedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader);\ntypedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void);\ntypedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type);\ntypedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program);\ntypedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader);\ntypedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader);\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index);\ntypedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);\ntypedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);\ntypedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders);\ntypedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);\ntypedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);\ntypedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);\ntypedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source);\ntypedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name);\ntypedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer);\ntypedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program);\ntypedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader);\ntypedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program);\ntypedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);\ntypedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program);\ntypedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0);\ntypedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1);\ntypedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2);\ntypedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);\ntypedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0);\ntypedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1);\ntypedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2);\ntypedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3);\ntypedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha);\nGLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs);\nGLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);\nGLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask);\nGLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask);\nGLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader);\nGLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name);\nGLAPI void APIENTRY glCompileShader (GLuint shader);\nGLAPI GLuint APIENTRY glCreateProgram (void);\nGLAPI GLuint APIENTRY glCreateShader (GLenum type);\nGLAPI void APIENTRY glDeleteProgram (GLuint program);\nGLAPI void APIENTRY glDeleteShader (GLuint shader);\nGLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader);\nGLAPI void APIENTRY glDisableVertexAttribArray (GLuint index);\nGLAPI void APIENTRY glEnableVertexAttribArray (GLuint index);\nGLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);\nGLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);\nGLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders);\nGLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name);\nGLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);\nGLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);\nGLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source);\nGLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name);\nGLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params);\nGLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params);\nGLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params);\nGLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params);\nGLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer);\nGLAPI GLboolean APIENTRY glIsProgram (GLuint program);\nGLAPI GLboolean APIENTRY glIsShader (GLuint shader);\nGLAPI void APIENTRY glLinkProgram (GLuint program);\nGLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);\nGLAPI void APIENTRY glUseProgram (GLuint program);\nGLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0);\nGLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1);\nGLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2);\nGLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);\nGLAPI void APIENTRY glUniform1i (GLint location, GLint v0);\nGLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1);\nGLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2);\nGLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3);\nGLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value);\nGLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value);\nGLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value);\nGLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value);\nGLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value);\nGLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value);\nGLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value);\nGLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value);\nGLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glValidateProgram (GLuint program);\nGLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x);\nGLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v);\nGLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x);\nGLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v);\nGLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x);\nGLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v);\nGLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y);\nGLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v);\nGLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y);\nGLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v);\nGLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y);\nGLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v);\nGLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z);\nGLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v);\nGLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z);\nGLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v);\nGLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z);\nGLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v);\nGLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v);\nGLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v);\nGLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v);\nGLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w);\nGLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v);\nGLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v);\nGLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v);\nGLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v);\nGLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\nGLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v);\nGLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);\nGLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v);\nGLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v);\nGLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w);\nGLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v);\nGLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v);\nGLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v);\nGLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v);\nGLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);\n#endif\n#endif /* GL3W_GL_VERSION_2_0 */\n#ifndef GL3W_GL_VERSION_2_1\n#define GL3W_GL_VERSION_2_1 1\n#define GL_PIXEL_PACK_BUFFER              0x88EB\n#define GL_PIXEL_UNPACK_BUFFER            0x88EC\n#define GL_PIXEL_PACK_BUFFER_BINDING      0x88ED\n#define GL_PIXEL_UNPACK_BUFFER_BINDING    0x88EF\n#define GL_FLOAT_MAT2x3                   0x8B65\n#define GL_FLOAT_MAT2x4                   0x8B66\n#define GL_FLOAT_MAT3x2                   0x8B67\n#define GL_FLOAT_MAT3x4                   0x8B68\n#define GL_FLOAT_MAT4x2                   0x8B69\n#define GL_FLOAT_MAT4x3                   0x8B6A\n#define GL_SRGB                           0x8C40\n#define GL_SRGB8                          0x8C41\n#define GL_SRGB_ALPHA                     0x8C42\n#define GL_SRGB8_ALPHA8                   0x8C43\n#define GL_COMPRESSED_SRGB                0x8C48\n#define GL_COMPRESSED_SRGB_ALPHA          0x8C49\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\n#endif\n#endif /* GL3W_GL_VERSION_2_1 */\n#ifndef GL3W_GL_VERSION_3_0\n#define GL3W_GL_VERSION_3_0 1\ntypedef khronos_uint16_t GLhalf;\n#define GL_COMPARE_REF_TO_TEXTURE         0x884E\n#define GL_CLIP_DISTANCE0                 0x3000\n#define GL_CLIP_DISTANCE1                 0x3001\n#define GL_CLIP_DISTANCE2                 0x3002\n#define GL_CLIP_DISTANCE3                 0x3003\n#define GL_CLIP_DISTANCE4                 0x3004\n#define GL_CLIP_DISTANCE5                 0x3005\n#define GL_CLIP_DISTANCE6                 0x3006\n#define GL_CLIP_DISTANCE7                 0x3007\n#define GL_MAX_CLIP_DISTANCES             0x0D32\n#define GL_MAJOR_VERSION                  0x821B\n#define GL_MINOR_VERSION                  0x821C\n#define GL_NUM_EXTENSIONS                 0x821D\n#define GL_CONTEXT_FLAGS                  0x821E\n#define GL_COMPRESSED_RED                 0x8225\n#define GL_COMPRESSED_RG                  0x8226\n#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001\n#define GL_RGBA32F                        0x8814\n#define GL_RGB32F                         0x8815\n#define GL_RGBA16F                        0x881A\n#define GL_RGB16F                         0x881B\n#define GL_VERTEX_ATTRIB_ARRAY_INTEGER    0x88FD\n#define GL_MAX_ARRAY_TEXTURE_LAYERS       0x88FF\n#define GL_MIN_PROGRAM_TEXEL_OFFSET       0x8904\n#define GL_MAX_PROGRAM_TEXEL_OFFSET       0x8905\n#define GL_CLAMP_READ_COLOR               0x891C\n#define GL_FIXED_ONLY                     0x891D\n#define GL_MAX_VARYING_COMPONENTS         0x8B4B\n#define GL_TEXTURE_1D_ARRAY               0x8C18\n#define GL_PROXY_TEXTURE_1D_ARRAY         0x8C19\n#define GL_TEXTURE_2D_ARRAY               0x8C1A\n#define GL_PROXY_TEXTURE_2D_ARRAY         0x8C1B\n#define GL_TEXTURE_BINDING_1D_ARRAY       0x8C1C\n#define GL_TEXTURE_BINDING_2D_ARRAY       0x8C1D\n#define GL_R11F_G11F_B10F                 0x8C3A\n#define GL_UNSIGNED_INT_10F_11F_11F_REV   0x8C3B\n#define GL_RGB9_E5                        0x8C3D\n#define GL_UNSIGNED_INT_5_9_9_9_REV       0x8C3E\n#define GL_TEXTURE_SHARED_SIZE            0x8C3F\n#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76\n#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80\n#define GL_TRANSFORM_FEEDBACK_VARYINGS    0x8C83\n#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84\n#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85\n#define GL_PRIMITIVES_GENERATED           0x8C87\n#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88\n#define GL_RASTERIZER_DISCARD             0x8C89\n#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B\n#define GL_INTERLEAVED_ATTRIBS            0x8C8C\n#define GL_SEPARATE_ATTRIBS               0x8C8D\n#define GL_TRANSFORM_FEEDBACK_BUFFER      0x8C8E\n#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F\n#define GL_RGBA32UI                       0x8D70\n#define GL_RGB32UI                        0x8D71\n#define GL_RGBA16UI                       0x8D76\n#define GL_RGB16UI                        0x8D77\n#define GL_RGBA8UI                        0x8D7C\n#define GL_RGB8UI                         0x8D7D\n#define GL_RGBA32I                        0x8D82\n#define GL_RGB32I                         0x8D83\n#define GL_RGBA16I                        0x8D88\n#define GL_RGB16I                         0x8D89\n#define GL_RGBA8I                         0x8D8E\n#define GL_RGB8I                          0x8D8F\n#define GL_RED_INTEGER                    0x8D94\n#define GL_GREEN_INTEGER                  0x8D95\n#define GL_BLUE_INTEGER                   0x8D96\n#define GL_RGB_INTEGER                    0x8D98\n#define GL_RGBA_INTEGER                   0x8D99\n#define GL_BGR_INTEGER                    0x8D9A\n#define GL_BGRA_INTEGER                   0x8D9B\n#define GL_SAMPLER_1D_ARRAY               0x8DC0\n#define GL_SAMPLER_2D_ARRAY               0x8DC1\n#define GL_SAMPLER_1D_ARRAY_SHADOW        0x8DC3\n#define GL_SAMPLER_2D_ARRAY_SHADOW        0x8DC4\n#define GL_SAMPLER_CUBE_SHADOW            0x8DC5\n#define GL_UNSIGNED_INT_VEC2              0x8DC6\n#define GL_UNSIGNED_INT_VEC3              0x8DC7\n#define GL_UNSIGNED_INT_VEC4              0x8DC8\n#define GL_INT_SAMPLER_1D                 0x8DC9\n#define GL_INT_SAMPLER_2D                 0x8DCA\n#define GL_INT_SAMPLER_3D                 0x8DCB\n#define GL_INT_SAMPLER_CUBE               0x8DCC\n#define GL_INT_SAMPLER_1D_ARRAY           0x8DCE\n#define GL_INT_SAMPLER_2D_ARRAY           0x8DCF\n#define GL_UNSIGNED_INT_SAMPLER_1D        0x8DD1\n#define GL_UNSIGNED_INT_SAMPLER_2D        0x8DD2\n#define GL_UNSIGNED_INT_SAMPLER_3D        0x8DD3\n#define GL_UNSIGNED_INT_SAMPLER_CUBE      0x8DD4\n#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY  0x8DD6\n#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY  0x8DD7\n#define GL_QUERY_WAIT                     0x8E13\n#define GL_QUERY_NO_WAIT                  0x8E14\n#define GL_QUERY_BY_REGION_WAIT           0x8E15\n#define GL_QUERY_BY_REGION_NO_WAIT        0x8E16\n#define GL_BUFFER_ACCESS_FLAGS            0x911F\n#define GL_BUFFER_MAP_LENGTH              0x9120\n#define GL_BUFFER_MAP_OFFSET              0x9121\n#define GL_DEPTH_COMPONENT32F             0x8CAC\n#define GL_DEPTH32F_STENCIL8              0x8CAD\n#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD\n#define GL_INVALID_FRAMEBUFFER_OPERATION  0x0506\n#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210\n#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211\n#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212\n#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213\n#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214\n#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215\n#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216\n#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217\n#define GL_FRAMEBUFFER_DEFAULT            0x8218\n#define GL_FRAMEBUFFER_UNDEFINED          0x8219\n#define GL_DEPTH_STENCIL_ATTACHMENT       0x821A\n#define GL_MAX_RENDERBUFFER_SIZE          0x84E8\n#define GL_DEPTH_STENCIL                  0x84F9\n#define GL_UNSIGNED_INT_24_8              0x84FA\n#define GL_DEPTH24_STENCIL8               0x88F0\n#define GL_TEXTURE_STENCIL_SIZE           0x88F1\n#define GL_TEXTURE_RED_TYPE               0x8C10\n#define GL_TEXTURE_GREEN_TYPE             0x8C11\n#define GL_TEXTURE_BLUE_TYPE              0x8C12\n#define GL_TEXTURE_ALPHA_TYPE             0x8C13\n#define GL_TEXTURE_DEPTH_TYPE             0x8C16\n#define GL_UNSIGNED_NORMALIZED            0x8C17\n#define GL_FRAMEBUFFER_BINDING            0x8CA6\n#ifndef __APPLE__\n#define GL_DRAW_FRAMEBUFFER_BINDING       0x8CA6\n#endif\n#define GL_RENDERBUFFER_BINDING           0x8CA7\n#define GL_READ_FRAMEBUFFER               0x8CA8\n#define GL_DRAW_FRAMEBUFFER               0x8CA9\n#define GL_READ_FRAMEBUFFER_BINDING       0x8CAA\n#define GL_RENDERBUFFER_SAMPLES           0x8CAB\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4\n#define GL_FRAMEBUFFER_COMPLETE           0x8CD5\n#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6\n#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7\n#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB\n#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC\n#define GL_FRAMEBUFFER_UNSUPPORTED        0x8CDD\n#define GL_MAX_COLOR_ATTACHMENTS          0x8CDF\n#define GL_COLOR_ATTACHMENT0              0x8CE0\n#define GL_COLOR_ATTACHMENT1              0x8CE1\n#define GL_COLOR_ATTACHMENT2              0x8CE2\n#define GL_COLOR_ATTACHMENT3              0x8CE3\n#define GL_COLOR_ATTACHMENT4              0x8CE4\n#define GL_COLOR_ATTACHMENT5              0x8CE5\n#define GL_COLOR_ATTACHMENT6              0x8CE6\n#define GL_COLOR_ATTACHMENT7              0x8CE7\n#define GL_COLOR_ATTACHMENT8              0x8CE8\n#define GL_COLOR_ATTACHMENT9              0x8CE9\n#define GL_COLOR_ATTACHMENT10             0x8CEA\n#define GL_COLOR_ATTACHMENT11             0x8CEB\n#define GL_COLOR_ATTACHMENT12             0x8CEC\n#define GL_COLOR_ATTACHMENT13             0x8CED\n#define GL_COLOR_ATTACHMENT14             0x8CEE\n#define GL_COLOR_ATTACHMENT15             0x8CEF\n#define GL_COLOR_ATTACHMENT16             0x8CF0\n#define GL_COLOR_ATTACHMENT17             0x8CF1\n#define GL_COLOR_ATTACHMENT18             0x8CF2\n#define GL_COLOR_ATTACHMENT19             0x8CF3\n#define GL_COLOR_ATTACHMENT20             0x8CF4\n#define GL_COLOR_ATTACHMENT21             0x8CF5\n#define GL_COLOR_ATTACHMENT22             0x8CF6\n#define GL_COLOR_ATTACHMENT23             0x8CF7\n#define GL_COLOR_ATTACHMENT24             0x8CF8\n#define GL_COLOR_ATTACHMENT25             0x8CF9\n#define GL_COLOR_ATTACHMENT26             0x8CFA\n#define GL_COLOR_ATTACHMENT27             0x8CFB\n#define GL_COLOR_ATTACHMENT28             0x8CFC\n#define GL_COLOR_ATTACHMENT29             0x8CFD\n#define GL_COLOR_ATTACHMENT30             0x8CFE\n#define GL_COLOR_ATTACHMENT31             0x8CFF\n#define GL_DEPTH_ATTACHMENT               0x8D00\n#define GL_STENCIL_ATTACHMENT             0x8D20\n#define GL_FRAMEBUFFER                    0x8D40\n#define GL_RENDERBUFFER                   0x8D41\n#define GL_RENDERBUFFER_WIDTH             0x8D42\n#define GL_RENDERBUFFER_HEIGHT            0x8D43\n#define GL_RENDERBUFFER_INTERNAL_FORMAT   0x8D44\n#define GL_STENCIL_INDEX1                 0x8D46\n#define GL_STENCIL_INDEX4                 0x8D47\n#define GL_STENCIL_INDEX8                 0x8D48\n#define GL_STENCIL_INDEX16                0x8D49\n#define GL_RENDERBUFFER_RED_SIZE          0x8D50\n#define GL_RENDERBUFFER_GREEN_SIZE        0x8D51\n#define GL_RENDERBUFFER_BLUE_SIZE         0x8D52\n#define GL_RENDERBUFFER_ALPHA_SIZE        0x8D53\n#define GL_RENDERBUFFER_DEPTH_SIZE        0x8D54\n#define GL_RENDERBUFFER_STENCIL_SIZE      0x8D55\n#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56\n#define GL_MAX_SAMPLES                    0x8D57\n#define GL_FRAMEBUFFER_SRGB               0x8DB9\n#define GL_HALF_FLOAT                     0x140B\n#define GL_MAP_READ_BIT                   0x0001\n#define GL_MAP_WRITE_BIT                  0x0002\n#define GL_MAP_INVALIDATE_RANGE_BIT       0x0004\n#define GL_MAP_INVALIDATE_BUFFER_BIT      0x0008\n#define GL_MAP_FLUSH_EXPLICIT_BIT         0x0010\n#define GL_MAP_UNSYNCHRONIZED_BIT         0x0020\n#define GL_COMPRESSED_RED_RGTC1           0x8DBB\n#define GL_COMPRESSED_SIGNED_RED_RGTC1    0x8DBC\n#define GL_COMPRESSED_RG_RGTC2            0x8DBD\n#define GL_COMPRESSED_SIGNED_RG_RGTC2     0x8DBE\n#define GL_RG                             0x8227\n#define GL_RG_INTEGER                     0x8228\n#define GL_R8                             0x8229\n#define GL_R16                            0x822A\n#define GL_RG8                            0x822B\n#define GL_RG16                           0x822C\n#define GL_R16F                           0x822D\n#define GL_R32F                           0x822E\n#define GL_RG16F                          0x822F\n#define GL_RG32F                          0x8230\n#define GL_R8I                            0x8231\n#define GL_R8UI                           0x8232\n#define GL_R16I                           0x8233\n#define GL_R16UI                          0x8234\n#define GL_R32I                           0x8235\n#define GL_R32UI                          0x8236\n#define GL_RG8I                           0x8237\n#define GL_RG8UI                          0x8238\n#define GL_RG16I                          0x8239\n#define GL_RG16UI                         0x823A\n#define GL_RG32I                          0x823B\n#define GL_RG32UI                         0x823C\n#define GL_VERTEX_ARRAY_BINDING           0x85B5\ntypedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a);\ntypedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data);\ntypedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data);\ntypedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index);\ntypedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index);\ntypedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index);\ntypedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode);\ntypedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void);\ntypedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);\ntypedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer);\ntypedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode);\ntypedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name);\ntypedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp);\ntypedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode);\ntypedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v);\ntypedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params);\ntypedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name);\ntypedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name);\ntypedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0);\ntypedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1);\ntypedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2);\ntypedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);\ntypedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params);\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params);\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params);\ntypedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value);\ntypedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value);\ntypedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);\ntypedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index);\ntypedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer);\ntypedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer);\ntypedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers);\ntypedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers);\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params);\ntypedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer);\ntypedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer);\ntypedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers);\ntypedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers);\ntypedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);\ntypedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target);\ntypedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);\ntypedef void *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);\ntypedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length);\ntypedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array);\ntypedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays);\ntypedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);\ntypedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a);\nGLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data);\nGLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data);\nGLAPI void APIENTRY glEnablei (GLenum target, GLuint index);\nGLAPI void APIENTRY glDisablei (GLenum target, GLuint index);\nGLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index);\nGLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode);\nGLAPI void APIENTRY glEndTransformFeedback (void);\nGLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);\nGLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer);\nGLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode);\nGLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name);\nGLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp);\nGLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode);\nGLAPI void APIENTRY glEndConditionalRender (void);\nGLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);\nGLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params);\nGLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x);\nGLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y);\nGLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z);\nGLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w);\nGLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x);\nGLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y);\nGLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z);\nGLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);\nGLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v);\nGLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v);\nGLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v);\nGLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v);\nGLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v);\nGLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v);\nGLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v);\nGLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v);\nGLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v);\nGLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v);\nGLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v);\nGLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v);\nGLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params);\nGLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name);\nGLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name);\nGLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0);\nGLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1);\nGLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2);\nGLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);\nGLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value);\nGLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value);\nGLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value);\nGLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value);\nGLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params);\nGLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params);\nGLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params);\nGLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value);\nGLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value);\nGLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value);\nGLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);\nGLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index);\nGLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer);\nGLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer);\nGLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers);\nGLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers);\nGLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params);\nGLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer);\nGLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer);\nGLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers);\nGLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers);\nGLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target);\nGLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);\nGLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);\nGLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);\nGLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);\nGLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGenerateMipmap (GLenum target);\nGLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);\nGLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);\nGLAPI void *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);\nGLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length);\nGLAPI void APIENTRY glBindVertexArray (GLuint array);\nGLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays);\nGLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays);\nGLAPI GLboolean APIENTRY glIsVertexArray (GLuint array);\n#endif\n#endif /* GL3W_GL_VERSION_3_0 */\n#ifndef GL3W_GL_VERSION_3_1\n#define GL3W_GL_VERSION_3_1 1\n#define GL_SAMPLER_2D_RECT                0x8B63\n#define GL_SAMPLER_2D_RECT_SHADOW         0x8B64\n#define GL_SAMPLER_BUFFER                 0x8DC2\n#define GL_INT_SAMPLER_2D_RECT            0x8DCD\n#define GL_INT_SAMPLER_BUFFER             0x8DD0\n#define GL_UNSIGNED_INT_SAMPLER_2D_RECT   0x8DD5\n#define GL_UNSIGNED_INT_SAMPLER_BUFFER    0x8DD8\n#define GL_TEXTURE_BUFFER                 0x8C2A\n#define GL_MAX_TEXTURE_BUFFER_SIZE        0x8C2B\n#define GL_TEXTURE_BINDING_BUFFER         0x8C2C\n#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D\n#define GL_TEXTURE_RECTANGLE              0x84F5\n#define GL_TEXTURE_BINDING_RECTANGLE      0x84F6\n#define GL_PROXY_TEXTURE_RECTANGLE        0x84F7\n#define GL_MAX_RECTANGLE_TEXTURE_SIZE     0x84F8\n#define GL_R8_SNORM                       0x8F94\n#define GL_RG8_SNORM                      0x8F95\n#define GL_RGB8_SNORM                     0x8F96\n#define GL_RGBA8_SNORM                    0x8F97\n#define GL_R16_SNORM                      0x8F98\n#define GL_RG16_SNORM                     0x8F99\n#define GL_RGB16_SNORM                    0x8F9A\n#define GL_RGBA16_SNORM                   0x8F9B\n#define GL_SIGNED_NORMALIZED              0x8F9C\n#define GL_PRIMITIVE_RESTART              0x8F9D\n#define GL_PRIMITIVE_RESTART_INDEX        0x8F9E\n#define GL_COPY_READ_BUFFER               0x8F36\n#define GL_COPY_WRITE_BUFFER              0x8F37\n#define GL_UNIFORM_BUFFER                 0x8A11\n#define GL_UNIFORM_BUFFER_BINDING         0x8A28\n#define GL_UNIFORM_BUFFER_START           0x8A29\n#define GL_UNIFORM_BUFFER_SIZE            0x8A2A\n#define GL_MAX_VERTEX_UNIFORM_BLOCKS      0x8A2B\n#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS    0x8A2C\n#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS    0x8A2D\n#define GL_MAX_COMBINED_UNIFORM_BLOCKS    0x8A2E\n#define GL_MAX_UNIFORM_BUFFER_BINDINGS    0x8A2F\n#define GL_MAX_UNIFORM_BLOCK_SIZE         0x8A30\n#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31\n#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32\n#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33\n#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34\n#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35\n#define GL_ACTIVE_UNIFORM_BLOCKS          0x8A36\n#define GL_UNIFORM_TYPE                   0x8A37\n#define GL_UNIFORM_SIZE                   0x8A38\n#define GL_UNIFORM_NAME_LENGTH            0x8A39\n#define GL_UNIFORM_BLOCK_INDEX            0x8A3A\n#define GL_UNIFORM_OFFSET                 0x8A3B\n#define GL_UNIFORM_ARRAY_STRIDE           0x8A3C\n#define GL_UNIFORM_MATRIX_STRIDE          0x8A3D\n#define GL_UNIFORM_IS_ROW_MAJOR           0x8A3E\n#define GL_UNIFORM_BLOCK_BINDING          0x8A3F\n#define GL_UNIFORM_BLOCK_DATA_SIZE        0x8A40\n#define GL_UNIFORM_BLOCK_NAME_LENGTH      0x8A41\n#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS  0x8A42\n#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46\n#define GL_INVALID_INDEX                  0xFFFFFFFFu\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount);\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount);\ntypedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer);\ntypedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index);\ntypedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);\ntypedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices);\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName);\ntypedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName);\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName);\ntypedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount);\nGLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount);\nGLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer);\nGLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index);\nGLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);\nGLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices);\nGLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName);\nGLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName);\nGLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName);\nGLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);\n#endif\n#endif /* GL3W_GL_VERSION_3_1 */\n#ifndef GL3W_GL_VERSION_3_2\n#define GL3W_GL_VERSION_3_2 1\ntypedef struct __GLsync *GLsync;\ntypedef khronos_uint64_t GLuint64;\ntypedef khronos_int64_t GLint64;\n#define GL_CONTEXT_CORE_PROFILE_BIT       0x00000001\n#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002\n#define GL_LINES_ADJACENCY                0x000A\n#define GL_LINE_STRIP_ADJACENCY           0x000B\n#define GL_TRIANGLES_ADJACENCY            0x000C\n#define GL_TRIANGLE_STRIP_ADJACENCY       0x000D\n#define GL_PROGRAM_POINT_SIZE             0x8642\n#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29\n#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7\n#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8\n#define GL_GEOMETRY_SHADER                0x8DD9\n#define GL_GEOMETRY_VERTICES_OUT          0x8916\n#define GL_GEOMETRY_INPUT_TYPE            0x8917\n#define GL_GEOMETRY_OUTPUT_TYPE           0x8918\n#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF\n#define GL_MAX_GEOMETRY_OUTPUT_VERTICES   0x8DE0\n#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1\n#define GL_MAX_VERTEX_OUTPUT_COMPONENTS   0x9122\n#define GL_MAX_GEOMETRY_INPUT_COMPONENTS  0x9123\n#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124\n#define GL_MAX_FRAGMENT_INPUT_COMPONENTS  0x9125\n#define GL_CONTEXT_PROFILE_MASK           0x9126\n#define GL_DEPTH_CLAMP                    0x864F\n#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C\n#define GL_FIRST_VERTEX_CONVENTION        0x8E4D\n#define GL_LAST_VERTEX_CONVENTION         0x8E4E\n#define GL_PROVOKING_VERTEX               0x8E4F\n#define GL_TEXTURE_CUBE_MAP_SEAMLESS      0x884F\n#define GL_MAX_SERVER_WAIT_TIMEOUT        0x9111\n#define GL_OBJECT_TYPE                    0x9112\n#define GL_SYNC_CONDITION                 0x9113\n#define GL_SYNC_STATUS                    0x9114\n#define GL_SYNC_FLAGS                     0x9115\n#define GL_SYNC_FENCE                     0x9116\n#define GL_SYNC_GPU_COMMANDS_COMPLETE     0x9117\n#define GL_UNSIGNALED                     0x9118\n#define GL_SIGNALED                       0x9119\n#define GL_ALREADY_SIGNALED               0x911A\n#define GL_TIMEOUT_EXPIRED                0x911B\n#define GL_CONDITION_SATISFIED            0x911C\n#define GL_WAIT_FAILED                    0x911D\n#define GL_TIMEOUT_IGNORED                0xFFFFFFFFFFFFFFFFull\n#define GL_SYNC_FLUSH_COMMANDS_BIT        0x00000001\n#define GL_SAMPLE_POSITION                0x8E50\n#define GL_SAMPLE_MASK                    0x8E51\n#define GL_SAMPLE_MASK_VALUE              0x8E52\n#define GL_MAX_SAMPLE_MASK_WORDS          0x8E59\n#define GL_TEXTURE_2D_MULTISAMPLE         0x9100\n#define GL_PROXY_TEXTURE_2D_MULTISAMPLE   0x9101\n#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY   0x9102\n#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103\n#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104\n#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105\n#define GL_TEXTURE_SAMPLES                0x9106\n#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107\n#define GL_SAMPLER_2D_MULTISAMPLE         0x9108\n#define GL_INT_SAMPLER_2D_MULTISAMPLE     0x9109\n#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A\n#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY   0x910B\n#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C\n#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D\n#define GL_MAX_COLOR_TEXTURE_SAMPLES      0x910E\n#define GL_MAX_DEPTH_TEXTURE_SAMPLES      0x910F\n#define GL_MAX_INTEGER_SAMPLES            0x9110\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);\ntypedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex);\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex);\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex);\ntypedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode);\ntypedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags);\ntypedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync);\ntypedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync);\ntypedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout);\ntypedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout);\ntypedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data);\ntypedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values);\ntypedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data);\ntypedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level);\ntypedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);\ntypedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);\ntypedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val);\ntypedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);\nGLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex);\nGLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex);\nGLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex);\nGLAPI void APIENTRY glProvokingVertex (GLenum mode);\nGLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags);\nGLAPI GLboolean APIENTRY glIsSync (GLsync sync);\nGLAPI void APIENTRY glDeleteSync (GLsync sync);\nGLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout);\nGLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout);\nGLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *data);\nGLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values);\nGLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data);\nGLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params);\nGLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level);\nGLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);\nGLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);\nGLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val);\nGLAPI void APIENTRY glSampleMaski (GLuint maskNumber, GLbitfield mask);\n#endif\n#endif /* GL3W_GL_VERSION_3_2 */\n#ifndef GL3W_GL_VERSION_3_3\n#define GL3W_GL_VERSION_3_3 1\n#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR    0x88FE\n#define GL_SRC1_COLOR                     0x88F9\n#define GL_ONE_MINUS_SRC1_COLOR           0x88FA\n#define GL_ONE_MINUS_SRC1_ALPHA           0x88FB\n#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS   0x88FC\n#define GL_ANY_SAMPLES_PASSED             0x8C2F\n#define GL_SAMPLER_BINDING                0x8919\n#define GL_RGB10_A2UI                     0x906F\n#define GL_TEXTURE_SWIZZLE_R              0x8E42\n#define GL_TEXTURE_SWIZZLE_G              0x8E43\n#define GL_TEXTURE_SWIZZLE_B              0x8E44\n#define GL_TEXTURE_SWIZZLE_A              0x8E45\n#define GL_TEXTURE_SWIZZLE_RGBA           0x8E46\n#define GL_TIME_ELAPSED                   0x88BF\n#define GL_TIMESTAMP                      0x8E28\n#define GL_INT_2_10_10_10_REV             0x8D9F\ntypedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name);\ntypedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name);\ntypedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers);\ntypedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers);\ntypedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler);\ntypedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler);\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param);\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param);\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param);\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param);\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param);\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params);\ntypedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target);\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params);\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name);\nGLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name);\nGLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers);\nGLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers);\nGLAPI GLboolean APIENTRY glIsSampler (GLuint sampler);\nGLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler);\nGLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param);\nGLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param);\nGLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param);\nGLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param);\nGLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param);\nGLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param);\nGLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params);\nGLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params);\nGLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target);\nGLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params);\nGLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params);\nGLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor);\nGLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value);\nGLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\nGLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value);\nGLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\nGLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value);\nGLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\nGLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value);\nGLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value);\n#endif\n#endif /* GL3W_GL_VERSION_3_3 */\n#ifndef GL3W_GL_VERSION_4_0\n#define GL3W_GL_VERSION_4_0 1\n#define GL_SAMPLE_SHADING                 0x8C36\n#define GL_MIN_SAMPLE_SHADING_VALUE       0x8C37\n#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E\n#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F\n#define GL_TEXTURE_CUBE_MAP_ARRAY         0x9009\n#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A\n#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY   0x900B\n#define GL_SAMPLER_CUBE_MAP_ARRAY         0x900C\n#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW  0x900D\n#define GL_INT_SAMPLER_CUBE_MAP_ARRAY     0x900E\n#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F\n#define GL_DRAW_INDIRECT_BUFFER           0x8F3F\n#define GL_DRAW_INDIRECT_BUFFER_BINDING   0x8F43\n#define GL_GEOMETRY_SHADER_INVOCATIONS    0x887F\n#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A\n#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B\n#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C\n#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D\n#define GL_MAX_VERTEX_STREAMS             0x8E71\n#define GL_DOUBLE_VEC2                    0x8FFC\n#define GL_DOUBLE_VEC3                    0x8FFD\n#define GL_DOUBLE_VEC4                    0x8FFE\n#define GL_DOUBLE_MAT2                    0x8F46\n#define GL_DOUBLE_MAT3                    0x8F47\n#define GL_DOUBLE_MAT4                    0x8F48\n#define GL_DOUBLE_MAT2x3                  0x8F49\n#define GL_DOUBLE_MAT2x4                  0x8F4A\n#define GL_DOUBLE_MAT3x2                  0x8F4B\n#define GL_DOUBLE_MAT3x4                  0x8F4C\n#define GL_DOUBLE_MAT4x2                  0x8F4D\n#define GL_DOUBLE_MAT4x3                  0x8F4E\n#define GL_ACTIVE_SUBROUTINES             0x8DE5\n#define GL_ACTIVE_SUBROUTINE_UNIFORMS     0x8DE6\n#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47\n#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH   0x8E48\n#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49\n#define GL_MAX_SUBROUTINES                0x8DE7\n#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8\n#define GL_NUM_COMPATIBLE_SUBROUTINES     0x8E4A\n#define GL_COMPATIBLE_SUBROUTINES         0x8E4B\n#define GL_PATCHES                        0x000E\n#define GL_PATCH_VERTICES                 0x8E72\n#define GL_PATCH_DEFAULT_INNER_LEVEL      0x8E73\n#define GL_PATCH_DEFAULT_OUTER_LEVEL      0x8E74\n#define GL_TESS_CONTROL_OUTPUT_VERTICES   0x8E75\n#define GL_TESS_GEN_MODE                  0x8E76\n#define GL_TESS_GEN_SPACING               0x8E77\n#define GL_TESS_GEN_VERTEX_ORDER          0x8E78\n#define GL_TESS_GEN_POINT_MODE            0x8E79\n#define GL_ISOLINES                       0x8E7A\n#define GL_FRACTIONAL_ODD                 0x8E7B\n#define GL_FRACTIONAL_EVEN                0x8E7C\n#define GL_MAX_PATCH_VERTICES             0x8E7D\n#define GL_MAX_TESS_GEN_LEVEL             0x8E7E\n#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F\n#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80\n#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81\n#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82\n#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83\n#define GL_MAX_TESS_PATCH_COMPONENTS      0x8E84\n#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85\n#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86\n#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89\n#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A\n#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C\n#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D\n#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E\n#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1\n#define GL_TESS_EVALUATION_SHADER         0x8E87\n#define GL_TESS_CONTROL_SHADER            0x8E88\n#define GL_TRANSFORM_FEEDBACK             0x8E22\n#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23\n#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24\n#define GL_TRANSFORM_FEEDBACK_BINDING     0x8E25\n#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70\ntypedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value);\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode);\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha);\ntypedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst);\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect);\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect);\ntypedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x);\ntypedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y);\ntypedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z);\ntypedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\ntypedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params);\ntypedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name);\ntypedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name);\ntypedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values);\ntypedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name);\ntypedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name);\ntypedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices);\ntypedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params);\ntypedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values);\ntypedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value);\ntypedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values);\ntypedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id);\ntypedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids);\ntypedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids);\ntypedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id);\ntypedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void);\ntypedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void);\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id);\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream);\ntypedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id);\ntypedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index);\ntypedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glMinSampleShading (GLfloat value);\nGLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode);\nGLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha);\nGLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst);\nGLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);\nGLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const void *indirect);\nGLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect);\nGLAPI void APIENTRY glUniform1d (GLint location, GLdouble x);\nGLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y);\nGLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z);\nGLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\nGLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value);\nGLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value);\nGLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value);\nGLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value);\nGLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params);\nGLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name);\nGLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name);\nGLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values);\nGLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name);\nGLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name);\nGLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices);\nGLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params);\nGLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values);\nGLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value);\nGLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values);\nGLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id);\nGLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids);\nGLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids);\nGLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id);\nGLAPI void APIENTRY glPauseTransformFeedback (void);\nGLAPI void APIENTRY glResumeTransformFeedback (void);\nGLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id);\nGLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream);\nGLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id);\nGLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index);\nGLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params);\n#endif\n#endif /* GL3W_GL_VERSION_4_0 */\n#ifndef GL3W_GL_VERSION_4_1\n#define GL3W_GL_VERSION_4_1 1\n#define GL_FIXED                          0x140C\n#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A\n#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B\n#define GL_LOW_FLOAT                      0x8DF0\n#define GL_MEDIUM_FLOAT                   0x8DF1\n#define GL_HIGH_FLOAT                     0x8DF2\n#define GL_LOW_INT                        0x8DF3\n#define GL_MEDIUM_INT                     0x8DF4\n#define GL_HIGH_INT                       0x8DF5\n#define GL_SHADER_COMPILER                0x8DFA\n#define GL_SHADER_BINARY_FORMATS          0x8DF8\n#define GL_NUM_SHADER_BINARY_FORMATS      0x8DF9\n#define GL_MAX_VERTEX_UNIFORM_VECTORS     0x8DFB\n#define GL_MAX_VARYING_VECTORS            0x8DFC\n#define GL_MAX_FRAGMENT_UNIFORM_VECTORS   0x8DFD\n#define GL_RGB565                         0x8D62\n#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257\n#define GL_PROGRAM_BINARY_LENGTH          0x8741\n#define GL_NUM_PROGRAM_BINARY_FORMATS     0x87FE\n#define GL_PROGRAM_BINARY_FORMATS         0x87FF\n#define GL_VERTEX_SHADER_BIT              0x00000001\n#define GL_FRAGMENT_SHADER_BIT            0x00000002\n#define GL_GEOMETRY_SHADER_BIT            0x00000004\n#define GL_TESS_CONTROL_SHADER_BIT        0x00000008\n#define GL_TESS_EVALUATION_SHADER_BIT     0x00000010\n#define GL_ALL_SHADER_BITS                0xFFFFFFFF\n#define GL_PROGRAM_SEPARABLE              0x8258\n#define GL_ACTIVE_PROGRAM                 0x8259\n#define GL_PROGRAM_PIPELINE_BINDING       0x825A\n#define GL_MAX_VIEWPORTS                  0x825B\n#define GL_VIEWPORT_SUBPIXEL_BITS         0x825C\n#define GL_VIEWPORT_BOUNDS_RANGE          0x825D\n#define GL_LAYER_PROVOKING_VERTEX         0x825E\n#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F\n#define GL_UNDEFINED_VERTEX               0x8260\ntypedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void);\ntypedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length);\ntypedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision);\ntypedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f);\ntypedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d);\ntypedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary);\ntypedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length);\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value);\ntypedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program);\ntypedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program);\ntypedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar *const*strings);\ntypedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline);\ntypedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines);\ntypedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines);\ntypedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline);\ntypedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline);\ntypedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params);\ntypedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h);\ntypedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v);\ntypedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v);\ntypedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f);\ntypedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data);\ntypedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glReleaseShaderCompiler (void);\nGLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length);\nGLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision);\nGLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f);\nGLAPI void APIENTRY glClearDepthf (GLfloat d);\nGLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary);\nGLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length);\nGLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value);\nGLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program);\nGLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program);\nGLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar *const*strings);\nGLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline);\nGLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines);\nGLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines);\nGLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline);\nGLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params);\nGLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0);\nGLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value);\nGLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0);\nGLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0);\nGLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0);\nGLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value);\nGLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1);\nGLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value);\nGLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1);\nGLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1);\nGLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1);\nGLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value);\nGLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2);\nGLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value);\nGLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2);\nGLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2);\nGLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2);\nGLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value);\nGLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3);\nGLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value);\nGLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);\nGLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3);\nGLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);\nGLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value);\nGLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\nGLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\nGLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline);\nGLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog);\nGLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x);\nGLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y);\nGLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z);\nGLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\nGLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v);\nGLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v);\nGLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v);\nGLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v);\nGLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);\nGLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params);\nGLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v);\nGLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h);\nGLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v);\nGLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v);\nGLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v);\nGLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v);\nGLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f);\nGLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data);\nGLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data);\n#endif\n#endif /* GL3W_GL_VERSION_4_1 */\n#ifndef GL3W_GL_VERSION_4_2\n#define GL3W_GL_VERSION_4_2 1\n#define GL_COPY_READ_BUFFER_BINDING       0x8F36\n#define GL_COPY_WRITE_BUFFER_BINDING      0x8F37\n#define GL_TRANSFORM_FEEDBACK_ACTIVE      0x8E24\n#define GL_TRANSFORM_FEEDBACK_PAUSED      0x8E23\n#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH  0x9127\n#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128\n#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH  0x9129\n#define GL_UNPACK_COMPRESSED_BLOCK_SIZE   0x912A\n#define GL_PACK_COMPRESSED_BLOCK_WIDTH    0x912B\n#define GL_PACK_COMPRESSED_BLOCK_HEIGHT   0x912C\n#define GL_PACK_COMPRESSED_BLOCK_DEPTH    0x912D\n#define GL_PACK_COMPRESSED_BLOCK_SIZE     0x912E\n#define GL_NUM_SAMPLE_COUNTS              0x9380\n#define GL_MIN_MAP_BUFFER_ALIGNMENT       0x90BC\n#define GL_ATOMIC_COUNTER_BUFFER          0x92C0\n#define GL_ATOMIC_COUNTER_BUFFER_BINDING  0x92C1\n#define GL_ATOMIC_COUNTER_BUFFER_START    0x92C2\n#define GL_ATOMIC_COUNTER_BUFFER_SIZE     0x92C3\n#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4\n#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5\n#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB\n#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC\n#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD\n#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE\n#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF\n#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0\n#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1\n#define GL_MAX_VERTEX_ATOMIC_COUNTERS     0x92D2\n#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3\n#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4\n#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS   0x92D5\n#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS   0x92D6\n#define GL_MAX_COMBINED_ATOMIC_COUNTERS   0x92D7\n#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8\n#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC\n#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS  0x92D9\n#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA\n#define GL_UNSIGNED_INT_ATOMIC_COUNTER    0x92DB\n#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001\n#define GL_ELEMENT_ARRAY_BARRIER_BIT      0x00000002\n#define GL_UNIFORM_BARRIER_BIT            0x00000004\n#define GL_TEXTURE_FETCH_BARRIER_BIT      0x00000008\n#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020\n#define GL_COMMAND_BARRIER_BIT            0x00000040\n#define GL_PIXEL_BUFFER_BARRIER_BIT       0x00000080\n#define GL_TEXTURE_UPDATE_BARRIER_BIT     0x00000100\n#define GL_BUFFER_UPDATE_BARRIER_BIT      0x00000200\n#define GL_FRAMEBUFFER_BARRIER_BIT        0x00000400\n#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800\n#define GL_ATOMIC_COUNTER_BARRIER_BIT     0x00001000\n#define GL_ALL_BARRIER_BITS               0xFFFFFFFF\n#define GL_MAX_IMAGE_UNITS                0x8F38\n#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39\n#define GL_IMAGE_BINDING_NAME             0x8F3A\n#define GL_IMAGE_BINDING_LEVEL            0x8F3B\n#define GL_IMAGE_BINDING_LAYERED          0x8F3C\n#define GL_IMAGE_BINDING_LAYER            0x8F3D\n#define GL_IMAGE_BINDING_ACCESS           0x8F3E\n#define GL_IMAGE_1D                       0x904C\n#define GL_IMAGE_2D                       0x904D\n#define GL_IMAGE_3D                       0x904E\n#define GL_IMAGE_2D_RECT                  0x904F\n#define GL_IMAGE_CUBE                     0x9050\n#define GL_IMAGE_BUFFER                   0x9051\n#define GL_IMAGE_1D_ARRAY                 0x9052\n#define GL_IMAGE_2D_ARRAY                 0x9053\n#define GL_IMAGE_CUBE_MAP_ARRAY           0x9054\n#define GL_IMAGE_2D_MULTISAMPLE           0x9055\n#define GL_IMAGE_2D_MULTISAMPLE_ARRAY     0x9056\n#define GL_INT_IMAGE_1D                   0x9057\n#define GL_INT_IMAGE_2D                   0x9058\n#define GL_INT_IMAGE_3D                   0x9059\n#define GL_INT_IMAGE_2D_RECT              0x905A\n#define GL_INT_IMAGE_CUBE                 0x905B\n#define GL_INT_IMAGE_BUFFER               0x905C\n#define GL_INT_IMAGE_1D_ARRAY             0x905D\n#define GL_INT_IMAGE_2D_ARRAY             0x905E\n#define GL_INT_IMAGE_CUBE_MAP_ARRAY       0x905F\n#define GL_INT_IMAGE_2D_MULTISAMPLE       0x9060\n#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061\n#define GL_UNSIGNED_INT_IMAGE_1D          0x9062\n#define GL_UNSIGNED_INT_IMAGE_2D          0x9063\n#define GL_UNSIGNED_INT_IMAGE_3D          0x9064\n#define GL_UNSIGNED_INT_IMAGE_2D_RECT     0x9065\n#define GL_UNSIGNED_INT_IMAGE_CUBE        0x9066\n#define GL_UNSIGNED_INT_IMAGE_BUFFER      0x9067\n#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY    0x9068\n#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY    0x9069\n#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A\n#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B\n#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C\n#define GL_MAX_IMAGE_SAMPLES              0x906D\n#define GL_IMAGE_BINDING_FORMAT           0x906E\n#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7\n#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8\n#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9\n#define GL_MAX_VERTEX_IMAGE_UNIFORMS      0x90CA\n#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB\n#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC\n#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS    0x90CD\n#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS    0x90CE\n#define GL_MAX_COMBINED_IMAGE_UNIFORMS    0x90CF\n#define GL_COMPRESSED_RGBA_BPTC_UNORM     0x8E8C\n#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D\n#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E\n#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F\n#define GL_TEXTURE_IMMUTABLE_FORMAT       0x912F\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance);\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance);\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance);\ntypedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params);\ntypedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format);\ntypedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers);\ntypedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width);\ntypedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount);\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance);\nGLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance);\nGLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance);\nGLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params);\nGLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params);\nGLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format);\nGLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers);\nGLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width);\nGLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);\nGLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount);\nGLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount);\n#endif\n#endif /* GL3W_GL_VERSION_4_2 */\n#ifndef GL3W_GL_VERSION_4_3\n#define GL3W_GL_VERSION_4_3 1\ntypedef void (APIENTRY  *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\n#define GL_NUM_SHADING_LANGUAGE_VERSIONS  0x82E9\n#define GL_VERTEX_ATTRIB_ARRAY_LONG       0x874E\n#define GL_COMPRESSED_RGB8_ETC2           0x9274\n#define GL_COMPRESSED_SRGB8_ETC2          0x9275\n#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276\n#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277\n#define GL_COMPRESSED_RGBA8_ETC2_EAC      0x9278\n#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279\n#define GL_COMPRESSED_R11_EAC             0x9270\n#define GL_COMPRESSED_SIGNED_R11_EAC      0x9271\n#define GL_COMPRESSED_RG11_EAC            0x9272\n#define GL_COMPRESSED_SIGNED_RG11_EAC     0x9273\n#define GL_PRIMITIVE_RESTART_FIXED_INDEX  0x8D69\n#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A\n#define GL_MAX_ELEMENT_INDEX              0x8D6B\n#define GL_COMPUTE_SHADER                 0x91B9\n#define GL_MAX_COMPUTE_UNIFORM_BLOCKS     0x91BB\n#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC\n#define GL_MAX_COMPUTE_IMAGE_UNIFORMS     0x91BD\n#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262\n#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263\n#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264\n#define GL_MAX_COMPUTE_ATOMIC_COUNTERS    0x8265\n#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266\n#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB\n#define GL_MAX_COMPUTE_WORK_GROUP_COUNT   0x91BE\n#define GL_MAX_COMPUTE_WORK_GROUP_SIZE    0x91BF\n#define GL_COMPUTE_WORK_GROUP_SIZE        0x8267\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED\n#define GL_DISPATCH_INDIRECT_BUFFER       0x90EE\n#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF\n#define GL_COMPUTE_SHADER_BIT             0x00000020\n#define GL_DEBUG_OUTPUT_SYNCHRONOUS       0x8242\n#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243\n#define GL_DEBUG_CALLBACK_FUNCTION        0x8244\n#define GL_DEBUG_CALLBACK_USER_PARAM      0x8245\n#define GL_DEBUG_SOURCE_API               0x8246\n#define GL_DEBUG_SOURCE_WINDOW_SYSTEM     0x8247\n#define GL_DEBUG_SOURCE_SHADER_COMPILER   0x8248\n#define GL_DEBUG_SOURCE_THIRD_PARTY       0x8249\n#define GL_DEBUG_SOURCE_APPLICATION       0x824A\n#define GL_DEBUG_SOURCE_OTHER             0x824B\n#define GL_DEBUG_TYPE_ERROR               0x824C\n#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D\n#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR  0x824E\n#define GL_DEBUG_TYPE_PORTABILITY         0x824F\n#define GL_DEBUG_TYPE_PERFORMANCE         0x8250\n#define GL_DEBUG_TYPE_OTHER               0x8251\n#define GL_MAX_DEBUG_MESSAGE_LENGTH       0x9143\n#define GL_MAX_DEBUG_LOGGED_MESSAGES      0x9144\n#define GL_DEBUG_LOGGED_MESSAGES          0x9145\n#define GL_DEBUG_SEVERITY_HIGH            0x9146\n#define GL_DEBUG_SEVERITY_MEDIUM          0x9147\n#define GL_DEBUG_SEVERITY_LOW             0x9148\n#define GL_DEBUG_TYPE_MARKER              0x8268\n#define GL_DEBUG_TYPE_PUSH_GROUP          0x8269\n#define GL_DEBUG_TYPE_POP_GROUP           0x826A\n#define GL_DEBUG_SEVERITY_NOTIFICATION    0x826B\n#define GL_MAX_DEBUG_GROUP_STACK_DEPTH    0x826C\n#define GL_DEBUG_GROUP_STACK_DEPTH        0x826D\n#define GL_BUFFER                         0x82E0\n#define GL_SHADER                         0x82E1\n#define GL_PROGRAM                        0x82E2\n#define GL_QUERY                          0x82E3\n#define GL_PROGRAM_PIPELINE               0x82E4\n#define GL_SAMPLER                        0x82E6\n#define GL_MAX_LABEL_LENGTH               0x82E8\n#define GL_DEBUG_OUTPUT                   0x92E0\n#define GL_CONTEXT_FLAG_DEBUG_BIT         0x00000002\n#define GL_MAX_UNIFORM_LOCATIONS          0x826E\n#define GL_FRAMEBUFFER_DEFAULT_WIDTH      0x9310\n#define GL_FRAMEBUFFER_DEFAULT_HEIGHT     0x9311\n#define GL_FRAMEBUFFER_DEFAULT_LAYERS     0x9312\n#define GL_FRAMEBUFFER_DEFAULT_SAMPLES    0x9313\n#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314\n#define GL_MAX_FRAMEBUFFER_WIDTH          0x9315\n#define GL_MAX_FRAMEBUFFER_HEIGHT         0x9316\n#define GL_MAX_FRAMEBUFFER_LAYERS         0x9317\n#define GL_MAX_FRAMEBUFFER_SAMPLES        0x9318\n#define GL_INTERNALFORMAT_SUPPORTED       0x826F\n#define GL_INTERNALFORMAT_PREFERRED       0x8270\n#define GL_INTERNALFORMAT_RED_SIZE        0x8271\n#define GL_INTERNALFORMAT_GREEN_SIZE      0x8272\n#define GL_INTERNALFORMAT_BLUE_SIZE       0x8273\n#define GL_INTERNALFORMAT_ALPHA_SIZE      0x8274\n#define GL_INTERNALFORMAT_DEPTH_SIZE      0x8275\n#define GL_INTERNALFORMAT_STENCIL_SIZE    0x8276\n#define GL_INTERNALFORMAT_SHARED_SIZE     0x8277\n#define GL_INTERNALFORMAT_RED_TYPE        0x8278\n#define GL_INTERNALFORMAT_GREEN_TYPE      0x8279\n#define GL_INTERNALFORMAT_BLUE_TYPE       0x827A\n#define GL_INTERNALFORMAT_ALPHA_TYPE      0x827B\n#define GL_INTERNALFORMAT_DEPTH_TYPE      0x827C\n#define GL_INTERNALFORMAT_STENCIL_TYPE    0x827D\n#define GL_MAX_WIDTH                      0x827E\n#define GL_MAX_HEIGHT                     0x827F\n#define GL_MAX_DEPTH                      0x8280\n#define GL_MAX_LAYERS                     0x8281\n#define GL_MAX_COMBINED_DIMENSIONS        0x8282\n#define GL_COLOR_COMPONENTS               0x8283\n#define GL_DEPTH_COMPONENTS               0x8284\n#define GL_STENCIL_COMPONENTS             0x8285\n#define GL_COLOR_RENDERABLE               0x8286\n#define GL_DEPTH_RENDERABLE               0x8287\n#define GL_STENCIL_RENDERABLE             0x8288\n#define GL_FRAMEBUFFER_RENDERABLE         0x8289\n#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A\n#define GL_FRAMEBUFFER_BLEND              0x828B\n#define GL_READ_PIXELS                    0x828C\n#define GL_READ_PIXELS_FORMAT             0x828D\n#define GL_READ_PIXELS_TYPE               0x828E\n#define GL_TEXTURE_IMAGE_FORMAT           0x828F\n#define GL_TEXTURE_IMAGE_TYPE             0x8290\n#define GL_GET_TEXTURE_IMAGE_FORMAT       0x8291\n#define GL_GET_TEXTURE_IMAGE_TYPE         0x8292\n#define GL_MIPMAP                         0x8293\n#define GL_MANUAL_GENERATE_MIPMAP         0x8294\n#define GL_AUTO_GENERATE_MIPMAP           0x8295\n#define GL_COLOR_ENCODING                 0x8296\n#define GL_SRGB_READ                      0x8297\n#define GL_SRGB_WRITE                     0x8298\n#define GL_FILTER                         0x829A\n#define GL_VERTEX_TEXTURE                 0x829B\n#define GL_TESS_CONTROL_TEXTURE           0x829C\n#define GL_TESS_EVALUATION_TEXTURE        0x829D\n#define GL_GEOMETRY_TEXTURE               0x829E\n#define GL_FRAGMENT_TEXTURE               0x829F\n#define GL_COMPUTE_TEXTURE                0x82A0\n#define GL_TEXTURE_SHADOW                 0x82A1\n#define GL_TEXTURE_GATHER                 0x82A2\n#define GL_TEXTURE_GATHER_SHADOW          0x82A3\n#define GL_SHADER_IMAGE_LOAD              0x82A4\n#define GL_SHADER_IMAGE_STORE             0x82A5\n#define GL_SHADER_IMAGE_ATOMIC            0x82A6\n#define GL_IMAGE_TEXEL_SIZE               0x82A7\n#define GL_IMAGE_COMPATIBILITY_CLASS      0x82A8\n#define GL_IMAGE_PIXEL_FORMAT             0x82A9\n#define GL_IMAGE_PIXEL_TYPE               0x82AA\n#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC\n#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD\n#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE\n#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF\n#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1\n#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2\n#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE  0x82B3\n#define GL_CLEAR_BUFFER                   0x82B4\n#define GL_TEXTURE_VIEW                   0x82B5\n#define GL_VIEW_COMPATIBILITY_CLASS       0x82B6\n#define GL_FULL_SUPPORT                   0x82B7\n#define GL_CAVEAT_SUPPORT                 0x82B8\n#define GL_IMAGE_CLASS_4_X_32             0x82B9\n#define GL_IMAGE_CLASS_2_X_32             0x82BA\n#define GL_IMAGE_CLASS_1_X_32             0x82BB\n#define GL_IMAGE_CLASS_4_X_16             0x82BC\n#define GL_IMAGE_CLASS_2_X_16             0x82BD\n#define GL_IMAGE_CLASS_1_X_16             0x82BE\n#define GL_IMAGE_CLASS_4_X_8              0x82BF\n#define GL_IMAGE_CLASS_2_X_8              0x82C0\n#define GL_IMAGE_CLASS_1_X_8              0x82C1\n#define GL_IMAGE_CLASS_11_11_10           0x82C2\n#define GL_IMAGE_CLASS_10_10_10_2         0x82C3\n#define GL_VIEW_CLASS_128_BITS            0x82C4\n#define GL_VIEW_CLASS_96_BITS             0x82C5\n#define GL_VIEW_CLASS_64_BITS             0x82C6\n#define GL_VIEW_CLASS_48_BITS             0x82C7\n#define GL_VIEW_CLASS_32_BITS             0x82C8\n#define GL_VIEW_CLASS_24_BITS             0x82C9\n#define GL_VIEW_CLASS_16_BITS             0x82CA\n#define GL_VIEW_CLASS_8_BITS              0x82CB\n#define GL_VIEW_CLASS_S3TC_DXT1_RGB       0x82CC\n#define GL_VIEW_CLASS_S3TC_DXT1_RGBA      0x82CD\n#define GL_VIEW_CLASS_S3TC_DXT3_RGBA      0x82CE\n#define GL_VIEW_CLASS_S3TC_DXT5_RGBA      0x82CF\n#define GL_VIEW_CLASS_RGTC1_RED           0x82D0\n#define GL_VIEW_CLASS_RGTC2_RG            0x82D1\n#define GL_VIEW_CLASS_BPTC_UNORM          0x82D2\n#define GL_VIEW_CLASS_BPTC_FLOAT          0x82D3\n#define GL_UNIFORM                        0x92E1\n#define GL_UNIFORM_BLOCK                  0x92E2\n#define GL_PROGRAM_INPUT                  0x92E3\n#define GL_PROGRAM_OUTPUT                 0x92E4\n#define GL_BUFFER_VARIABLE                0x92E5\n#define GL_SHADER_STORAGE_BLOCK           0x92E6\n#define GL_VERTEX_SUBROUTINE              0x92E8\n#define GL_TESS_CONTROL_SUBROUTINE        0x92E9\n#define GL_TESS_EVALUATION_SUBROUTINE     0x92EA\n#define GL_GEOMETRY_SUBROUTINE            0x92EB\n#define GL_FRAGMENT_SUBROUTINE            0x92EC\n#define GL_COMPUTE_SUBROUTINE             0x92ED\n#define GL_VERTEX_SUBROUTINE_UNIFORM      0x92EE\n#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF\n#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0\n#define GL_GEOMETRY_SUBROUTINE_UNIFORM    0x92F1\n#define GL_FRAGMENT_SUBROUTINE_UNIFORM    0x92F2\n#define GL_COMPUTE_SUBROUTINE_UNIFORM     0x92F3\n#define GL_TRANSFORM_FEEDBACK_VARYING     0x92F4\n#define GL_ACTIVE_RESOURCES               0x92F5\n#define GL_MAX_NAME_LENGTH                0x92F6\n#define GL_MAX_NUM_ACTIVE_VARIABLES       0x92F7\n#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8\n#define GL_NAME_LENGTH                    0x92F9\n#define GL_TYPE                           0x92FA\n#define GL_ARRAY_SIZE                     0x92FB\n#define GL_OFFSET                         0x92FC\n#define GL_BLOCK_INDEX                    0x92FD\n#define GL_ARRAY_STRIDE                   0x92FE\n#define GL_MATRIX_STRIDE                  0x92FF\n#define GL_IS_ROW_MAJOR                   0x9300\n#define GL_ATOMIC_COUNTER_BUFFER_INDEX    0x9301\n#define GL_BUFFER_BINDING                 0x9302\n#define GL_BUFFER_DATA_SIZE               0x9303\n#define GL_NUM_ACTIVE_VARIABLES           0x9304\n#define GL_ACTIVE_VARIABLES               0x9305\n#define GL_REFERENCED_BY_VERTEX_SHADER    0x9306\n#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307\n#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308\n#define GL_REFERENCED_BY_GEOMETRY_SHADER  0x9309\n#define GL_REFERENCED_BY_FRAGMENT_SHADER  0x930A\n#define GL_REFERENCED_BY_COMPUTE_SHADER   0x930B\n#define GL_TOP_LEVEL_ARRAY_SIZE           0x930C\n#define GL_TOP_LEVEL_ARRAY_STRIDE         0x930D\n#define GL_LOCATION                       0x930E\n#define GL_LOCATION_INDEX                 0x930F\n#define GL_IS_PER_PATCH                   0x92E7\n#define GL_SHADER_STORAGE_BUFFER          0x90D2\n#define GL_SHADER_STORAGE_BUFFER_BINDING  0x90D3\n#define GL_SHADER_STORAGE_BUFFER_START    0x90D4\n#define GL_SHADER_STORAGE_BUFFER_SIZE     0x90D5\n#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6\n#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7\n#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8\n#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9\n#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA\n#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB\n#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC\n#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD\n#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE  0x90DE\n#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF\n#define GL_SHADER_STORAGE_BARRIER_BIT     0x00002000\n#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39\n#define GL_DEPTH_STENCIL_TEXTURE_MODE     0x90EA\n#define GL_TEXTURE_BUFFER_OFFSET          0x919D\n#define GL_TEXTURE_BUFFER_SIZE            0x919E\n#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F\n#define GL_TEXTURE_VIEW_MIN_LEVEL         0x82DB\n#define GL_TEXTURE_VIEW_NUM_LEVELS        0x82DC\n#define GL_TEXTURE_VIEW_MIN_LAYER         0x82DD\n#define GL_TEXTURE_VIEW_NUM_LAYERS        0x82DE\n#define GL_TEXTURE_IMMUTABLE_LEVELS       0x82DF\n#define GL_VERTEX_ATTRIB_BINDING          0x82D4\n#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET  0x82D5\n#define GL_VERTEX_BINDING_DIVISOR         0x82D6\n#define GL_VERTEX_BINDING_OFFSET          0x82D7\n#define GL_VERTEX_BINDING_STRIDE          0x82D8\n#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9\n#define GL_MAX_VERTEX_ATTRIB_BINDINGS     0x82DA\n#define GL_VERTEX_BINDING_BUFFER          0x8F4F\ntypedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data);\ntypedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data);\ntypedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);\ntypedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect);\ntypedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params);\ntypedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth);\ntypedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level);\ntypedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length);\ntypedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer);\ntypedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments);\ntypedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride);\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride);\ntypedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params);\ntypedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name);\ntypedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name);\ntypedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params);\ntypedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name);\ntypedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name);\ntypedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding);\ntypedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);\ntypedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);\ntypedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);\ntypedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers);\ntypedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex);\ntypedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor);\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf);\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam);\ntypedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog);\ntypedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message);\ntypedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void);\ntypedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label);\ntypedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label);\ntypedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label);\ntypedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data);\nGLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data);\nGLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);\nGLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect);\nGLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);\nGLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param);\nGLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params);\nGLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth);\nGLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level);\nGLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length);\nGLAPI void APIENTRY glInvalidateBufferData (GLuint buffer);\nGLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments);\nGLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride);\nGLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride);\nGLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params);\nGLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name);\nGLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name);\nGLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params);\nGLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name);\nGLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name);\nGLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding);\nGLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);\nGLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);\nGLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);\nGLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers);\nGLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);\nGLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);\nGLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\nGLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\nGLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex);\nGLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor);\nGLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);\nGLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf);\nGLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam);\nGLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog);\nGLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message);\nGLAPI void APIENTRY glPopDebugGroup (void);\nGLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label);\nGLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label);\nGLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label);\nGLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label);\n#endif\n#endif /* GL3W_GL_VERSION_4_3 */\n#ifndef GL3W_GL_VERSION_4_4\n#define GL3W_GL_VERSION_4_4 1\n#define GL_MAX_VERTEX_ATTRIB_STRIDE       0x82E5\n#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221\n#define GL_TEXTURE_BUFFER_BINDING         0x8C2A\n#define GL_MAP_PERSISTENT_BIT             0x0040\n#define GL_MAP_COHERENT_BIT               0x0080\n#define GL_DYNAMIC_STORAGE_BIT            0x0100\n#define GL_CLIENT_STORAGE_BIT             0x0200\n#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000\n#define GL_BUFFER_IMMUTABLE_STORAGE       0x821F\n#define GL_BUFFER_STORAGE_FLAGS           0x8220\n#define GL_CLEAR_TEXTURE                  0x9365\n#define GL_LOCATION_COMPONENT             0x934A\n#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B\n#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C\n#define GL_QUERY_BUFFER                   0x9192\n#define GL_QUERY_BUFFER_BARRIER_BIT       0x00008000\n#define GL_QUERY_BUFFER_BINDING           0x9193\n#define GL_QUERY_RESULT_NO_WAIT           0x9194\n#define GL_MIRROR_CLAMP_TO_EDGE           0x8743\ntypedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags);\ntypedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data);\ntypedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data);\ntypedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers);\ntypedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes);\ntypedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures);\ntypedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers);\ntypedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures);\ntypedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC) (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glBufferStorage (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags);\nGLAPI void APIENTRY glClearTexImage (GLuint texture, GLint level, GLenum format, GLenum type, const void *data);\nGLAPI void APIENTRY glClearTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data);\nGLAPI void APIENTRY glBindBuffersBase (GLenum target, GLuint first, GLsizei count, const GLuint *buffers);\nGLAPI void APIENTRY glBindBuffersRange (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes);\nGLAPI void APIENTRY glBindTextures (GLuint first, GLsizei count, const GLuint *textures);\nGLAPI void APIENTRY glBindSamplers (GLuint first, GLsizei count, const GLuint *samplers);\nGLAPI void APIENTRY glBindImageTextures (GLuint first, GLsizei count, const GLuint *textures);\nGLAPI void APIENTRY glBindVertexBuffers (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides);\n#endif\n#endif /* GL3W_GL_VERSION_4_4 */\n#ifndef GL3W_GL_VERSION_4_5\n#define GL3W_GL_VERSION_4_5 1\n#define GL_CONTEXT_LOST                   0x0507\n#define GL_NEGATIVE_ONE_TO_ONE            0x935E\n#define GL_ZERO_TO_ONE                    0x935F\n#define GL_CLIP_ORIGIN                    0x935C\n#define GL_CLIP_DEPTH_MODE                0x935D\n#define GL_QUERY_WAIT_INVERTED            0x8E17\n#define GL_QUERY_NO_WAIT_INVERTED         0x8E18\n#define GL_QUERY_BY_REGION_WAIT_INVERTED  0x8E19\n#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A\n#define GL_MAX_CULL_DISTANCES             0x82F9\n#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA\n#define GL_TEXTURE_TARGET                 0x1006\n#define GL_QUERY_TARGET                   0x82EA\n#define GL_GUILTY_CONTEXT_RESET           0x8253\n#define GL_INNOCENT_CONTEXT_RESET         0x8254\n#define GL_UNKNOWN_CONTEXT_RESET          0x8255\n#define GL_RESET_NOTIFICATION_STRATEGY    0x8256\n#define GL_LOSE_CONTEXT_ON_RESET          0x8252\n#define GL_NO_RESET_NOTIFICATION          0x8261\n#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004\n#define GL_CONTEXT_RELEASE_BEHAVIOR       0x82FB\n#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC\ntypedef void (APIENTRYP PFNGLCLIPCONTROLPROC) (GLenum origin, GLenum depth);\ntypedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids);\ntypedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC) (GLuint xfb, GLuint index, GLuint buffer);\ntypedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC) (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);\ntypedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKIVPROC) (GLuint xfb, GLenum pname, GLint *param);\ntypedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param);\ntypedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param);\ntypedef void (APIENTRYP PFNGLCREATEBUFFERSPROC) (GLsizei n, GLuint *buffers);\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags);\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERDATAPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage);\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data);\ntypedef void (APIENTRYP PFNGLCOPYNAMEDBUFFERSUBDATAPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);\ntypedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data);\ntypedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data);\ntypedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERPROC) (GLuint buffer, GLenum access);\ntypedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access);\ntypedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFERPROC) (GLuint buffer);\ntypedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length);\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVPROC) (GLuint buffer, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERI64VPROC) (GLuint buffer, GLenum pname, GLint64 *params);\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVPROC) (GLuint buffer, GLenum pname, void **params);\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data);\ntypedef void (APIENTRYP PFNGLCREATEFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC) (GLuint framebuffer, GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC) (GLuint framebuffer, GLenum buf);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC) (GLuint framebuffer, GLenum src);\ntypedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments);\ntypedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value);\ntypedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value);\ntypedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFIPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);\ntypedef void (APIENTRYP PFNGLBLITNAMEDFRAMEBUFFERPROC) (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);\ntypedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) (GLuint framebuffer, GLenum target);\ntypedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC) (GLuint framebuffer, GLenum pname, GLint *param);\ntypedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLCREATERENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers);\ntypedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC) (GLuint renderbuffer, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLCREATETEXTURESPROC) (GLenum target, GLsizei n, GLuint *textures);\ntypedef void (APIENTRYP PFNGLTEXTUREBUFFERPROC) (GLuint texture, GLenum internalformat, GLuint buffer);\ntypedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEPROC) (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE1DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width);\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE2DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE3DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data);\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERFPROC) (GLuint texture, GLenum pname, GLfloat param);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, const GLfloat *param);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIPROC) (GLuint texture, GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, const GLint *params);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, const GLuint *params);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, const GLint *param);\ntypedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPPROC) (GLuint texture);\ntypedef void (APIENTRYP PFNGLBINDTEXTUREUNITPROC) (GLuint unit, GLuint texture);\ntypedef void (APIENTRYP PFNGLGETTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels);\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLsizei bufSize, void *pixels);\ntypedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVPROC) (GLuint texture, GLint level, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVPROC) (GLuint texture, GLint level, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, GLuint *params);\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLCREATEVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index);\ntypedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYELEMENTBUFFERPROC) (GLuint vaobj, GLuint buffer);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERSPROC) (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBBINDINGPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBIFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBLFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYBINDINGDIVISORPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor);\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYIVPROC) (GLuint vaobj, GLenum pname, GLint *param);\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXEDIVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param);\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXED64IVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param);\ntypedef void (APIENTRYP PFNGLCREATESAMPLERSPROC) (GLsizei n, GLuint *samplers);\ntypedef void (APIENTRYP PFNGLCREATEPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines);\ntypedef void (APIENTRYP PFNGLCREATEQUERIESPROC) (GLenum target, GLsizei n, GLuint *ids);\ntypedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\ntypedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\ntypedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\ntypedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\ntypedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC) (GLbitfield barriers);\ntypedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels);\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels);\ntypedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC) (void);\ntypedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint lod, GLsizei bufSize, void *pixels);\ntypedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels);\ntypedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params);\ntypedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params);\ntypedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params);\ntypedef void (APIENTRYP PFNGLREADNPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data);\ntypedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC) (void);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glClipControl (GLenum origin, GLenum depth);\nGLAPI void APIENTRY glCreateTransformFeedbacks (GLsizei n, GLuint *ids);\nGLAPI void APIENTRY glTransformFeedbackBufferBase (GLuint xfb, GLuint index, GLuint buffer);\nGLAPI void APIENTRY glTransformFeedbackBufferRange (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);\nGLAPI void APIENTRY glGetTransformFeedbackiv (GLuint xfb, GLenum pname, GLint *param);\nGLAPI void APIENTRY glGetTransformFeedbacki_v (GLuint xfb, GLenum pname, GLuint index, GLint *param);\nGLAPI void APIENTRY glGetTransformFeedbacki64_v (GLuint xfb, GLenum pname, GLuint index, GLint64 *param);\nGLAPI void APIENTRY glCreateBuffers (GLsizei n, GLuint *buffers);\nGLAPI void APIENTRY glNamedBufferStorage (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags);\nGLAPI void APIENTRY glNamedBufferData (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage);\nGLAPI void APIENTRY glNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data);\nGLAPI void APIENTRY glCopyNamedBufferSubData (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);\nGLAPI void APIENTRY glClearNamedBufferData (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data);\nGLAPI void APIENTRY glClearNamedBufferSubData (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data);\nGLAPI void *APIENTRY glMapNamedBuffer (GLuint buffer, GLenum access);\nGLAPI void *APIENTRY glMapNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access);\nGLAPI GLboolean APIENTRY glUnmapNamedBuffer (GLuint buffer);\nGLAPI void APIENTRY glFlushMappedNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length);\nGLAPI void APIENTRY glGetNamedBufferParameteriv (GLuint buffer, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetNamedBufferParameteri64v (GLuint buffer, GLenum pname, GLint64 *params);\nGLAPI void APIENTRY glGetNamedBufferPointerv (GLuint buffer, GLenum pname, void **params);\nGLAPI void APIENTRY glGetNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data);\nGLAPI void APIENTRY glCreateFramebuffers (GLsizei n, GLuint *framebuffers);\nGLAPI void APIENTRY glNamedFramebufferRenderbuffer (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);\nGLAPI void APIENTRY glNamedFramebufferParameteri (GLuint framebuffer, GLenum pname, GLint param);\nGLAPI void APIENTRY glNamedFramebufferTexture (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level);\nGLAPI void APIENTRY glNamedFramebufferTextureLayer (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer);\nGLAPI void APIENTRY glNamedFramebufferDrawBuffer (GLuint framebuffer, GLenum buf);\nGLAPI void APIENTRY glNamedFramebufferDrawBuffers (GLuint framebuffer, GLsizei n, const GLenum *bufs);\nGLAPI void APIENTRY glNamedFramebufferReadBuffer (GLuint framebuffer, GLenum src);\nGLAPI void APIENTRY glInvalidateNamedFramebufferData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments);\nGLAPI void APIENTRY glInvalidateNamedFramebufferSubData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glClearNamedFramebufferiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value);\nGLAPI void APIENTRY glClearNamedFramebufferuiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value);\nGLAPI void APIENTRY glClearNamedFramebufferfv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value);\nGLAPI void APIENTRY glClearNamedFramebufferfi (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);\nGLAPI void APIENTRY glBlitNamedFramebuffer (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);\nGLAPI GLenum APIENTRY glCheckNamedFramebufferStatus (GLuint framebuffer, GLenum target);\nGLAPI void APIENTRY glGetNamedFramebufferParameteriv (GLuint framebuffer, GLenum pname, GLint *param);\nGLAPI void APIENTRY glGetNamedFramebufferAttachmentParameteriv (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params);\nGLAPI void APIENTRY glCreateRenderbuffers (GLsizei n, GLuint *renderbuffers);\nGLAPI void APIENTRY glNamedRenderbufferStorage (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glNamedRenderbufferStorageMultisample (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glGetNamedRenderbufferParameteriv (GLuint renderbuffer, GLenum pname, GLint *params);\nGLAPI void APIENTRY glCreateTextures (GLenum target, GLsizei n, GLuint *textures);\nGLAPI void APIENTRY glTextureBuffer (GLuint texture, GLenum internalformat, GLuint buffer);\nGLAPI void APIENTRY glTextureBufferRange (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);\nGLAPI void APIENTRY glTextureStorage1D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width);\nGLAPI void APIENTRY glTextureStorage2D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glTextureStorage3D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);\nGLAPI void APIENTRY glTextureStorage2DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);\nGLAPI void APIENTRY glTextureStorage3DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);\nGLAPI void APIENTRY glTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels);\nGLAPI void APIENTRY glTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);\nGLAPI void APIENTRY glTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);\nGLAPI void APIENTRY glCompressedTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data);\nGLAPI void APIENTRY glCompressedTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);\nGLAPI void APIENTRY glCompressedTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data);\nGLAPI void APIENTRY glCopyTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);\nGLAPI void APIENTRY glCopyTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glCopyTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);\nGLAPI void APIENTRY glTextureParameterf (GLuint texture, GLenum pname, GLfloat param);\nGLAPI void APIENTRY glTextureParameterfv (GLuint texture, GLenum pname, const GLfloat *param);\nGLAPI void APIENTRY glTextureParameteri (GLuint texture, GLenum pname, GLint param);\nGLAPI void APIENTRY glTextureParameterIiv (GLuint texture, GLenum pname, const GLint *params);\nGLAPI void APIENTRY glTextureParameterIuiv (GLuint texture, GLenum pname, const GLuint *params);\nGLAPI void APIENTRY glTextureParameteriv (GLuint texture, GLenum pname, const GLint *param);\nGLAPI void APIENTRY glGenerateTextureMipmap (GLuint texture);\nGLAPI void APIENTRY glBindTextureUnit (GLuint unit, GLuint texture);\nGLAPI void APIENTRY glGetTextureImage (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels);\nGLAPI void APIENTRY glGetCompressedTextureImage (GLuint texture, GLint level, GLsizei bufSize, void *pixels);\nGLAPI void APIENTRY glGetTextureLevelParameterfv (GLuint texture, GLint level, GLenum pname, GLfloat *params);\nGLAPI void APIENTRY glGetTextureLevelParameteriv (GLuint texture, GLint level, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetTextureParameterfv (GLuint texture, GLenum pname, GLfloat *params);\nGLAPI void APIENTRY glGetTextureParameterIiv (GLuint texture, GLenum pname, GLint *params);\nGLAPI void APIENTRY glGetTextureParameterIuiv (GLuint texture, GLenum pname, GLuint *params);\nGLAPI void APIENTRY glGetTextureParameteriv (GLuint texture, GLenum pname, GLint *params);\nGLAPI void APIENTRY glCreateVertexArrays (GLsizei n, GLuint *arrays);\nGLAPI void APIENTRY glDisableVertexArrayAttrib (GLuint vaobj, GLuint index);\nGLAPI void APIENTRY glEnableVertexArrayAttrib (GLuint vaobj, GLuint index);\nGLAPI void APIENTRY glVertexArrayElementBuffer (GLuint vaobj, GLuint buffer);\nGLAPI void APIENTRY glVertexArrayVertexBuffer (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);\nGLAPI void APIENTRY glVertexArrayVertexBuffers (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides);\nGLAPI void APIENTRY glVertexArrayAttribBinding (GLuint vaobj, GLuint attribindex, GLuint bindingindex);\nGLAPI void APIENTRY glVertexArrayAttribFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);\nGLAPI void APIENTRY glVertexArrayAttribIFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\nGLAPI void APIENTRY glVertexArrayAttribLFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\nGLAPI void APIENTRY glVertexArrayBindingDivisor (GLuint vaobj, GLuint bindingindex, GLuint divisor);\nGLAPI void APIENTRY glGetVertexArrayiv (GLuint vaobj, GLenum pname, GLint *param);\nGLAPI void APIENTRY glGetVertexArrayIndexediv (GLuint vaobj, GLuint index, GLenum pname, GLint *param);\nGLAPI void APIENTRY glGetVertexArrayIndexed64iv (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param);\nGLAPI void APIENTRY glCreateSamplers (GLsizei n, GLuint *samplers);\nGLAPI void APIENTRY glCreateProgramPipelines (GLsizei n, GLuint *pipelines);\nGLAPI void APIENTRY glCreateQueries (GLenum target, GLsizei n, GLuint *ids);\nGLAPI void APIENTRY glGetQueryBufferObjecti64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\nGLAPI void APIENTRY glGetQueryBufferObjectiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\nGLAPI void APIENTRY glGetQueryBufferObjectui64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\nGLAPI void APIENTRY glGetQueryBufferObjectuiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset);\nGLAPI void APIENTRY glMemoryBarrierByRegion (GLbitfield barriers);\nGLAPI void APIENTRY glGetTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels);\nGLAPI void APIENTRY glGetCompressedTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels);\nGLAPI GLenum APIENTRY glGetGraphicsResetStatus (void);\nGLAPI void APIENTRY glGetnCompressedTexImage (GLenum target, GLint lod, GLsizei bufSize, void *pixels);\nGLAPI void APIENTRY glGetnTexImage (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels);\nGLAPI void APIENTRY glGetnUniformdv (GLuint program, GLint location, GLsizei bufSize, GLdouble *params);\nGLAPI void APIENTRY glGetnUniformfv (GLuint program, GLint location, GLsizei bufSize, GLfloat *params);\nGLAPI void APIENTRY glGetnUniformiv (GLuint program, GLint location, GLsizei bufSize, GLint *params);\nGLAPI void APIENTRY glGetnUniformuiv (GLuint program, GLint location, GLsizei bufSize, GLuint *params);\nGLAPI void APIENTRY glReadnPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data);\nGLAPI void APIENTRY glTextureBarrier (void);\n#endif\n#endif /* GL3W_GL_VERSION_4_5 */\n#ifndef GL3W_GL_VERSION_4_6\n#define GL3W_GL_VERSION_4_6 1\n#define GL_SHADER_BINARY_FORMAT_SPIR_V    0x9551\n#define GL_SPIR_V_BINARY                  0x9552\n#define GL_PARAMETER_BUFFER               0x80EE\n#define GL_PARAMETER_BUFFER_BINDING       0x80EF\n#define GL_CONTEXT_FLAG_NO_ERROR_BIT      0x00000008\n#define GL_VERTICES_SUBMITTED             0x82EE\n#define GL_PRIMITIVES_SUBMITTED           0x82EF\n#define GL_VERTEX_SHADER_INVOCATIONS      0x82F0\n#define GL_TESS_CONTROL_SHADER_PATCHES    0x82F1\n#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2\n#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3\n#define GL_FRAGMENT_SHADER_INVOCATIONS    0x82F4\n#define GL_COMPUTE_SHADER_INVOCATIONS     0x82F5\n#define GL_CLIPPING_INPUT_PRIMITIVES      0x82F6\n#define GL_CLIPPING_OUTPUT_PRIMITIVES     0x82F7\n#define GL_POLYGON_OFFSET_CLAMP           0x8E1B\n#define GL_SPIR_V_EXTENSIONS              0x9553\n#define GL_NUM_SPIR_V_EXTENSIONS          0x9554\n#define GL_TEXTURE_MAX_ANISOTROPY         0x84FE\n#define GL_MAX_TEXTURE_MAX_ANISOTROPY     0x84FF\n#define GL_TRANSFORM_FEEDBACK_OVERFLOW    0x82EC\n#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED\ntypedef void (APIENTRYP PFNGLSPECIALIZESHADERPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue);\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);\ntypedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPPROC) (GLfloat factor, GLfloat units, GLfloat clamp);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glSpecializeShader (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue);\nGLAPI void APIENTRY glMultiDrawArraysIndirectCount (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);\nGLAPI void APIENTRY glMultiDrawElementsIndirectCount (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);\nGLAPI void APIENTRY glPolygonOffsetClamp (GLfloat factor, GLfloat units, GLfloat clamp);\n#endif\n#endif /* GL3W_GL_VERSION_4_6 */\n#ifndef GL_ARB_ES2_compatibility\n#define GL_ARB_ES2_compatibility 1\n#endif /* GL_ARB_ES2_compatibility */\n#ifndef GL_ARB_ES3_1_compatibility\n#define GL_ARB_ES3_1_compatibility 1\n#endif /* GL_ARB_ES3_1_compatibility */\n#ifndef GL_ARB_ES3_2_compatibility\n#define GL_ARB_ES3_2_compatibility 1\n#define GL_PRIMITIVE_BOUNDING_BOX_ARB     0x92BE\n#define GL_MULTISAMPLE_LINE_WIDTH_RANGE_ARB 0x9381\n#define GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY_ARB 0x9382\ntypedef void (APIENTRYP PFNGLPRIMITIVEBOUNDINGBOXARBPROC) (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW);\n#endif /* GL_ARB_ES3_2_compatibility */\n#ifndef GL_ARB_ES3_compatibility\n#define GL_ARB_ES3_compatibility 1\n#endif /* GL_ARB_ES3_compatibility */\n#ifndef GL_ARB_arrays_of_arrays\n#define GL_ARB_arrays_of_arrays 1\n#endif /* GL_ARB_arrays_of_arrays */\n#ifndef GL_ARB_base_instance\n#define GL_ARB_base_instance 1\n#endif /* GL_ARB_base_instance */\n#ifndef GL_ARB_bindless_texture\n#define GL_ARB_bindless_texture 1\ntypedef khronos_uint64_t GLuint64EXT;\n#define GL_UNSIGNED_INT64_ARB             0x140F\ntypedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC) (GLuint texture);\ntypedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC) (GLuint texture, GLuint sampler);\ntypedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle);\ntypedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) (GLuint64 handle);\ntypedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format);\ntypedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle, GLenum access);\ntypedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) (GLuint64 handle);\ntypedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC) (GLint location, GLuint64 value);\ntypedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) (GLuint program, GLint location, GLuint64 value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values);\ntypedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle);\ntypedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC) (GLuint index, GLuint64EXT x);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC) (GLuint index, const GLuint64EXT *v);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC) (GLuint index, GLenum pname, GLuint64EXT *params);\n#endif /* GL_ARB_bindless_texture */\n#ifndef GL_ARB_blend_func_extended\n#define GL_ARB_blend_func_extended 1\n#endif /* GL_ARB_blend_func_extended */\n#ifndef GL_ARB_buffer_storage\n#define GL_ARB_buffer_storage 1\n#endif /* GL_ARB_buffer_storage */\n#ifndef GL_ARB_cl_event\n#define GL_ARB_cl_event 1\nstruct _cl_context;\nstruct _cl_event;\n#define GL_SYNC_CL_EVENT_ARB              0x8240\n#define GL_SYNC_CL_EVENT_COMPLETE_ARB     0x8241\ntypedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context *context, struct _cl_event *event, GLbitfield flags);\n#endif /* GL_ARB_cl_event */\n#ifndef GL_ARB_clear_buffer_object\n#define GL_ARB_clear_buffer_object 1\n#endif /* GL_ARB_clear_buffer_object */\n#ifndef GL_ARB_clear_texture\n#define GL_ARB_clear_texture 1\n#endif /* GL_ARB_clear_texture */\n#ifndef GL_ARB_clip_control\n#define GL_ARB_clip_control 1\n#endif /* GL_ARB_clip_control */\n#ifndef GL_ARB_compressed_texture_pixel_storage\n#define GL_ARB_compressed_texture_pixel_storage 1\n#endif /* GL_ARB_compressed_texture_pixel_storage */\n#ifndef GL_ARB_compute_shader\n#define GL_ARB_compute_shader 1\n#endif /* GL_ARB_compute_shader */\n#ifndef GL_ARB_compute_variable_group_size\n#define GL_ARB_compute_variable_group_size 1\n#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344\n#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB\n#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345\n#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF\ntypedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z);\n#endif /* GL_ARB_compute_variable_group_size */\n#ifndef GL_ARB_conditional_render_inverted\n#define GL_ARB_conditional_render_inverted 1\n#endif /* GL_ARB_conditional_render_inverted */\n#ifndef GL_ARB_conservative_depth\n#define GL_ARB_conservative_depth 1\n#endif /* GL_ARB_conservative_depth */\n#ifndef GL_ARB_copy_buffer\n#define GL_ARB_copy_buffer 1\n#endif /* GL_ARB_copy_buffer */\n#ifndef GL_ARB_copy_image\n#define GL_ARB_copy_image 1\n#endif /* GL_ARB_copy_image */\n#ifndef GL_ARB_cull_distance\n#define GL_ARB_cull_distance 1\n#endif /* GL_ARB_cull_distance */\n#ifndef GL_ARB_debug_output\n#define GL_ARB_debug_output 1\ntypedef void (APIENTRY  *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\n#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB   0x8242\n#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243\n#define GL_DEBUG_CALLBACK_FUNCTION_ARB    0x8244\n#define GL_DEBUG_CALLBACK_USER_PARAM_ARB  0x8245\n#define GL_DEBUG_SOURCE_API_ARB           0x8246\n#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247\n#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248\n#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB   0x8249\n#define GL_DEBUG_SOURCE_APPLICATION_ARB   0x824A\n#define GL_DEBUG_SOURCE_OTHER_ARB         0x824B\n#define GL_DEBUG_TYPE_ERROR_ARB           0x824C\n#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D\n#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E\n#define GL_DEBUG_TYPE_PORTABILITY_ARB     0x824F\n#define GL_DEBUG_TYPE_PERFORMANCE_ARB     0x8250\n#define GL_DEBUG_TYPE_OTHER_ARB           0x8251\n#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB   0x9143\n#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB  0x9144\n#define GL_DEBUG_LOGGED_MESSAGES_ARB      0x9145\n#define GL_DEBUG_SEVERITY_HIGH_ARB        0x9146\n#define GL_DEBUG_SEVERITY_MEDIUM_ARB      0x9147\n#define GL_DEBUG_SEVERITY_LOW_ARB         0x9148\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf);\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam);\ntypedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog);\n#endif /* GL_ARB_debug_output */\n#ifndef GL_ARB_depth_buffer_float\n#define GL_ARB_depth_buffer_float 1\n#endif /* GL_ARB_depth_buffer_float */\n#ifndef GL_ARB_depth_clamp\n#define GL_ARB_depth_clamp 1\n#endif /* GL_ARB_depth_clamp */\n#ifndef GL_ARB_derivative_control\n#define GL_ARB_derivative_control 1\n#endif /* GL_ARB_derivative_control */\n#ifndef GL_ARB_direct_state_access\n#define GL_ARB_direct_state_access 1\n#endif /* GL_ARB_direct_state_access */\n#ifndef GL_ARB_draw_buffers_blend\n#define GL_ARB_draw_buffers_blend 1\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode);\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha);\ntypedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst);\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);\n#endif /* GL_ARB_draw_buffers_blend */\n#ifndef GL_ARB_draw_elements_base_vertex\n#define GL_ARB_draw_elements_base_vertex 1\n#endif /* GL_ARB_draw_elements_base_vertex */\n#ifndef GL_ARB_draw_indirect\n#define GL_ARB_draw_indirect 1\n#endif /* GL_ARB_draw_indirect */\n#ifndef GL_ARB_draw_instanced\n#define GL_ARB_draw_instanced 1\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount);\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);\n#endif /* GL_ARB_draw_instanced */\n#ifndef GL_ARB_enhanced_layouts\n#define GL_ARB_enhanced_layouts 1\n#endif /* GL_ARB_enhanced_layouts */\n#ifndef GL_ARB_explicit_attrib_location\n#define GL_ARB_explicit_attrib_location 1\n#endif /* GL_ARB_explicit_attrib_location */\n#ifndef GL_ARB_explicit_uniform_location\n#define GL_ARB_explicit_uniform_location 1\n#endif /* GL_ARB_explicit_uniform_location */\n#ifndef GL_ARB_fragment_coord_conventions\n#define GL_ARB_fragment_coord_conventions 1\n#endif /* GL_ARB_fragment_coord_conventions */\n#ifndef GL_ARB_fragment_layer_viewport\n#define GL_ARB_fragment_layer_viewport 1\n#endif /* GL_ARB_fragment_layer_viewport */\n#ifndef GL_ARB_fragment_shader_interlock\n#define GL_ARB_fragment_shader_interlock 1\n#endif /* GL_ARB_fragment_shader_interlock */\n#ifndef GL_ARB_framebuffer_no_attachments\n#define GL_ARB_framebuffer_no_attachments 1\n#endif /* GL_ARB_framebuffer_no_attachments */\n#ifndef GL_ARB_framebuffer_object\n#define GL_ARB_framebuffer_object 1\n#endif /* GL_ARB_framebuffer_object */\n#ifndef GL_ARB_framebuffer_sRGB\n#define GL_ARB_framebuffer_sRGB 1\n#endif /* GL_ARB_framebuffer_sRGB */\n#ifndef GL_ARB_geometry_shader4\n#define GL_ARB_geometry_shader4 1\n#define GL_LINES_ADJACENCY_ARB            0x000A\n#define GL_LINE_STRIP_ADJACENCY_ARB       0x000B\n#define GL_TRIANGLES_ADJACENCY_ARB        0x000C\n#define GL_TRIANGLE_STRIP_ADJACENCY_ARB   0x000D\n#define GL_PROGRAM_POINT_SIZE_ARB         0x8642\n#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29\n#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7\n#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8\n#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9\n#define GL_GEOMETRY_SHADER_ARB            0x8DD9\n#define GL_GEOMETRY_VERTICES_OUT_ARB      0x8DDA\n#define GL_GEOMETRY_INPUT_TYPE_ARB        0x8DDB\n#define GL_GEOMETRY_OUTPUT_TYPE_ARB       0x8DDC\n#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD\n#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE\n#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF\n#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0\n#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC) (GLuint program, GLenum pname, GLint value);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face);\n#endif /* GL_ARB_geometry_shader4 */\n#ifndef GL_ARB_get_program_binary\n#define GL_ARB_get_program_binary 1\n#endif /* GL_ARB_get_program_binary */\n#ifndef GL_ARB_get_texture_sub_image\n#define GL_ARB_get_texture_sub_image 1\n#endif /* GL_ARB_get_texture_sub_image */\n#ifndef GL_ARB_gl_spirv\n#define GL_ARB_gl_spirv 1\n#define GL_SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551\n#define GL_SPIR_V_BINARY_ARB              0x9552\ntypedef void (APIENTRYP PFNGLSPECIALIZESHADERARBPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue);\n#endif /* GL_ARB_gl_spirv */\n#ifndef GL_ARB_gpu_shader5\n#define GL_ARB_gpu_shader5 1\n#endif /* GL_ARB_gpu_shader5 */\n#ifndef GL_ARB_gpu_shader_fp64\n#define GL_ARB_gpu_shader_fp64 1\n#endif /* GL_ARB_gpu_shader_fp64 */\n#ifndef GL_ARB_gpu_shader_int64\n#define GL_ARB_gpu_shader_int64 1\n#define GL_INT64_ARB                      0x140E\n#define GL_INT64_VEC2_ARB                 0x8FE9\n#define GL_INT64_VEC3_ARB                 0x8FEA\n#define GL_INT64_VEC4_ARB                 0x8FEB\n#define GL_UNSIGNED_INT64_VEC2_ARB        0x8FF5\n#define GL_UNSIGNED_INT64_VEC3_ARB        0x8FF6\n#define GL_UNSIGNED_INT64_VEC4_ARB        0x8FF7\ntypedef void (APIENTRYP PFNGLUNIFORM1I64ARBPROC) (GLint location, GLint64 x);\ntypedef void (APIENTRYP PFNGLUNIFORM2I64ARBPROC) (GLint location, GLint64 x, GLint64 y);\ntypedef void (APIENTRYP PFNGLUNIFORM3I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z);\ntypedef void (APIENTRYP PFNGLUNIFORM4I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w);\ntypedef void (APIENTRYP PFNGLUNIFORM1I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value);\ntypedef void (APIENTRYP PFNGLUNIFORM2I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value);\ntypedef void (APIENTRYP PFNGLUNIFORM3I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value);\ntypedef void (APIENTRYP PFNGLUNIFORM4I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value);\ntypedef void (APIENTRYP PFNGLUNIFORM1UI64ARBPROC) (GLint location, GLuint64 x);\ntypedef void (APIENTRYP PFNGLUNIFORM2UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y);\ntypedef void (APIENTRYP PFNGLUNIFORM3UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z);\ntypedef void (APIENTRYP PFNGLUNIFORM4UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w);\ntypedef void (APIENTRYP PFNGLUNIFORM1UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value);\ntypedef void (APIENTRYP PFNGLUNIFORM2UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value);\ntypedef void (APIENTRYP PFNGLUNIFORM3UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value);\ntypedef void (APIENTRYP PFNGLUNIFORM4UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value);\ntypedef void (APIENTRYP PFNGLGETUNIFORMI64VARBPROC) (GLuint program, GLint location, GLint64 *params);\ntypedef void (APIENTRYP PFNGLGETUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLuint64 *params);\ntypedef void (APIENTRYP PFNGLGETNUNIFORMI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint64 *params);\ntypedef void (APIENTRYP PFNGLGETNUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint64 *params);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64ARBPROC) (GLuint program, GLint location, GLint64 x);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64ARBPROC) (GLuint program, GLint location, GLuint64 x);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value);\n#endif /* GL_ARB_gpu_shader_int64 */\n#ifndef GL_ARB_half_float_vertex\n#define GL_ARB_half_float_vertex 1\n#endif /* GL_ARB_half_float_vertex */\n#ifndef GL_ARB_imaging\n#define GL_ARB_imaging 1\n#endif /* GL_ARB_imaging */\n#ifndef GL_ARB_indirect_parameters\n#define GL_ARB_indirect_parameters 1\n#define GL_PARAMETER_BUFFER_ARB           0x80EE\n#define GL_PARAMETER_BUFFER_BINDING_ARB   0x80EF\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);\n#endif /* GL_ARB_indirect_parameters */\n#ifndef GL_ARB_instanced_arrays\n#define GL_ARB_instanced_arrays 1\n#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint divisor);\n#endif /* GL_ARB_instanced_arrays */\n#ifndef GL_ARB_internalformat_query\n#define GL_ARB_internalformat_query 1\n#endif /* GL_ARB_internalformat_query */\n#ifndef GL_ARB_internalformat_query2\n#define GL_ARB_internalformat_query2 1\n#define GL_SRGB_DECODE_ARB                0x8299\n#define GL_VIEW_CLASS_EAC_R11             0x9383\n#define GL_VIEW_CLASS_EAC_RG11            0x9384\n#define GL_VIEW_CLASS_ETC2_RGB            0x9385\n#define GL_VIEW_CLASS_ETC2_RGBA           0x9386\n#define GL_VIEW_CLASS_ETC2_EAC_RGBA       0x9387\n#define GL_VIEW_CLASS_ASTC_4x4_RGBA       0x9388\n#define GL_VIEW_CLASS_ASTC_5x4_RGBA       0x9389\n#define GL_VIEW_CLASS_ASTC_5x5_RGBA       0x938A\n#define GL_VIEW_CLASS_ASTC_6x5_RGBA       0x938B\n#define GL_VIEW_CLASS_ASTC_6x6_RGBA       0x938C\n#define GL_VIEW_CLASS_ASTC_8x5_RGBA       0x938D\n#define GL_VIEW_CLASS_ASTC_8x6_RGBA       0x938E\n#define GL_VIEW_CLASS_ASTC_8x8_RGBA       0x938F\n#define GL_VIEW_CLASS_ASTC_10x5_RGBA      0x9390\n#define GL_VIEW_CLASS_ASTC_10x6_RGBA      0x9391\n#define GL_VIEW_CLASS_ASTC_10x8_RGBA      0x9392\n#define GL_VIEW_CLASS_ASTC_10x10_RGBA     0x9393\n#define GL_VIEW_CLASS_ASTC_12x10_RGBA     0x9394\n#define GL_VIEW_CLASS_ASTC_12x12_RGBA     0x9395\n#endif /* GL_ARB_internalformat_query2 */\n#ifndef GL_ARB_invalidate_subdata\n#define GL_ARB_invalidate_subdata 1\n#endif /* GL_ARB_invalidate_subdata */\n#ifndef GL_ARB_map_buffer_alignment\n#define GL_ARB_map_buffer_alignment 1\n#endif /* GL_ARB_map_buffer_alignment */\n#ifndef GL_ARB_map_buffer_range\n#define GL_ARB_map_buffer_range 1\n#endif /* GL_ARB_map_buffer_range */\n#ifndef GL_ARB_multi_bind\n#define GL_ARB_multi_bind 1\n#endif /* GL_ARB_multi_bind */\n#ifndef GL_ARB_multi_draw_indirect\n#define GL_ARB_multi_draw_indirect 1\n#endif /* GL_ARB_multi_draw_indirect */\n#ifndef GL_ARB_occlusion_query2\n#define GL_ARB_occlusion_query2 1\n#endif /* GL_ARB_occlusion_query2 */\n#ifndef GL_ARB_parallel_shader_compile\n#define GL_ARB_parallel_shader_compile 1\n#define GL_MAX_SHADER_COMPILER_THREADS_ARB 0x91B0\n#define GL_COMPLETION_STATUS_ARB          0x91B1\ntypedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSARBPROC) (GLuint count);\n#endif /* GL_ARB_parallel_shader_compile */\n#ifndef GL_ARB_pipeline_statistics_query\n#define GL_ARB_pipeline_statistics_query 1\n#define GL_VERTICES_SUBMITTED_ARB         0x82EE\n#define GL_PRIMITIVES_SUBMITTED_ARB       0x82EF\n#define GL_VERTEX_SHADER_INVOCATIONS_ARB  0x82F0\n#define GL_TESS_CONTROL_SHADER_PATCHES_ARB 0x82F1\n#define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB 0x82F2\n#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3\n#define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4\n#define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5\n#define GL_CLIPPING_INPUT_PRIMITIVES_ARB  0x82F6\n#define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7\n#endif /* GL_ARB_pipeline_statistics_query */\n#ifndef GL_ARB_pixel_buffer_object\n#define GL_ARB_pixel_buffer_object 1\n#define GL_PIXEL_PACK_BUFFER_ARB          0x88EB\n#define GL_PIXEL_UNPACK_BUFFER_ARB        0x88EC\n#define GL_PIXEL_PACK_BUFFER_BINDING_ARB  0x88ED\n#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF\n#endif /* GL_ARB_pixel_buffer_object */\n#ifndef GL_ARB_polygon_offset_clamp\n#define GL_ARB_polygon_offset_clamp 1\n#endif /* GL_ARB_polygon_offset_clamp */\n#ifndef GL_ARB_post_depth_coverage\n#define GL_ARB_post_depth_coverage 1\n#endif /* GL_ARB_post_depth_coverage */\n#ifndef GL_ARB_program_interface_query\n#define GL_ARB_program_interface_query 1\n#endif /* GL_ARB_program_interface_query */\n#ifndef GL_ARB_provoking_vertex\n#define GL_ARB_provoking_vertex 1\n#endif /* GL_ARB_provoking_vertex */\n#ifndef GL_ARB_query_buffer_object\n#define GL_ARB_query_buffer_object 1\n#endif /* GL_ARB_query_buffer_object */\n#ifndef GL_ARB_robust_buffer_access_behavior\n#define GL_ARB_robust_buffer_access_behavior 1\n#endif /* GL_ARB_robust_buffer_access_behavior */\n#ifndef GL_ARB_robustness\n#define GL_ARB_robustness 1\n#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004\n#define GL_LOSE_CONTEXT_ON_RESET_ARB      0x8252\n#define GL_GUILTY_CONTEXT_RESET_ARB       0x8253\n#define GL_INNOCENT_CONTEXT_RESET_ARB     0x8254\n#define GL_UNKNOWN_CONTEXT_RESET_ARB      0x8255\n#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256\n#define GL_NO_RESET_NOTIFICATION_ARB      0x8261\ntypedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void);\ntypedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img);\ntypedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data);\ntypedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, void *img);\ntypedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params);\ntypedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params);\ntypedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params);\n#endif /* GL_ARB_robustness */\n#ifndef GL_ARB_robustness_isolation\n#define GL_ARB_robustness_isolation 1\n#endif /* GL_ARB_robustness_isolation */\n#ifndef GL_ARB_sample_locations\n#define GL_ARB_sample_locations 1\n#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_ARB 0x933D\n#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_ARB 0x933E\n#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_ARB 0x933F\n#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_ARB 0x9340\n#define GL_SAMPLE_LOCATION_ARB            0x8E50\n#define GL_PROGRAMMABLE_SAMPLE_LOCATION_ARB 0x9341\n#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB 0x9342\n#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB 0x9343\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLEVALUATEDEPTHVALUESARBPROC) (void);\n#endif /* GL_ARB_sample_locations */\n#ifndef GL_ARB_sample_shading\n#define GL_ARB_sample_shading 1\n#define GL_SAMPLE_SHADING_ARB             0x8C36\n#define GL_MIN_SAMPLE_SHADING_VALUE_ARB   0x8C37\ntypedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value);\n#endif /* GL_ARB_sample_shading */\n#ifndef GL_ARB_sampler_objects\n#define GL_ARB_sampler_objects 1\n#endif /* GL_ARB_sampler_objects */\n#ifndef GL_ARB_seamless_cube_map\n#define GL_ARB_seamless_cube_map 1\n#endif /* GL_ARB_seamless_cube_map */\n#ifndef GL_ARB_seamless_cubemap_per_texture\n#define GL_ARB_seamless_cubemap_per_texture 1\n#endif /* GL_ARB_seamless_cubemap_per_texture */\n#ifndef GL_ARB_separate_shader_objects\n#define GL_ARB_separate_shader_objects 1\n#endif /* GL_ARB_separate_shader_objects */\n#ifndef GL_ARB_shader_atomic_counter_ops\n#define GL_ARB_shader_atomic_counter_ops 1\n#endif /* GL_ARB_shader_atomic_counter_ops */\n#ifndef GL_ARB_shader_atomic_counters\n#define GL_ARB_shader_atomic_counters 1\n#endif /* GL_ARB_shader_atomic_counters */\n#ifndef GL_ARB_shader_ballot\n#define GL_ARB_shader_ballot 1\n#endif /* GL_ARB_shader_ballot */\n#ifndef GL_ARB_shader_bit_encoding\n#define GL_ARB_shader_bit_encoding 1\n#endif /* GL_ARB_shader_bit_encoding */\n#ifndef GL_ARB_shader_clock\n#define GL_ARB_shader_clock 1\n#endif /* GL_ARB_shader_clock */\n#ifndef GL_ARB_shader_draw_parameters\n#define GL_ARB_shader_draw_parameters 1\n#endif /* GL_ARB_shader_draw_parameters */\n#ifndef GL_ARB_shader_group_vote\n#define GL_ARB_shader_group_vote 1\n#endif /* GL_ARB_shader_group_vote */\n#ifndef GL_ARB_shader_image_load_store\n#define GL_ARB_shader_image_load_store 1\n#endif /* GL_ARB_shader_image_load_store */\n#ifndef GL_ARB_shader_image_size\n#define GL_ARB_shader_image_size 1\n#endif /* GL_ARB_shader_image_size */\n#ifndef GL_ARB_shader_precision\n#define GL_ARB_shader_precision 1\n#endif /* GL_ARB_shader_precision */\n#ifndef GL_ARB_shader_stencil_export\n#define GL_ARB_shader_stencil_export 1\n#endif /* GL_ARB_shader_stencil_export */\n#ifndef GL_ARB_shader_storage_buffer_object\n#define GL_ARB_shader_storage_buffer_object 1\n#endif /* GL_ARB_shader_storage_buffer_object */\n#ifndef GL_ARB_shader_subroutine\n#define GL_ARB_shader_subroutine 1\n#endif /* GL_ARB_shader_subroutine */\n#ifndef GL_ARB_shader_texture_image_samples\n#define GL_ARB_shader_texture_image_samples 1\n#endif /* GL_ARB_shader_texture_image_samples */\n#ifndef GL_ARB_shader_viewport_layer_array\n#define GL_ARB_shader_viewport_layer_array 1\n#endif /* GL_ARB_shader_viewport_layer_array */\n#ifndef GL_ARB_shading_language_420pack\n#define GL_ARB_shading_language_420pack 1\n#endif /* GL_ARB_shading_language_420pack */\n#ifndef GL_ARB_shading_language_include\n#define GL_ARB_shading_language_include 1\n#define GL_SHADER_INCLUDE_ARB             0x8DAE\n#define GL_NAMED_STRING_LENGTH_ARB        0x8DE9\n#define GL_NAMED_STRING_TYPE_ARB          0x8DEA\ntypedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string);\ntypedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name);\ntypedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length);\ntypedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name);\ntypedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string);\ntypedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params);\n#endif /* GL_ARB_shading_language_include */\n#ifndef GL_ARB_shading_language_packing\n#define GL_ARB_shading_language_packing 1\n#endif /* GL_ARB_shading_language_packing */\n#ifndef GL_ARB_sparse_buffer\n#define GL_ARB_sparse_buffer 1\n#define GL_SPARSE_STORAGE_BIT_ARB         0x0400\n#define GL_SPARSE_BUFFER_PAGE_SIZE_ARB    0x82F8\ntypedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTARBPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit);\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit);\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit);\n#endif /* GL_ARB_sparse_buffer */\n#ifndef GL_ARB_sparse_texture\n#define GL_ARB_sparse_texture 1\n#define GL_TEXTURE_SPARSE_ARB             0x91A6\n#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB    0x91A7\n#define GL_NUM_SPARSE_LEVELS_ARB          0x91AA\n#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB     0x91A8\n#define GL_VIRTUAL_PAGE_SIZE_X_ARB        0x9195\n#define GL_VIRTUAL_PAGE_SIZE_Y_ARB        0x9196\n#define GL_VIRTUAL_PAGE_SIZE_Z_ARB        0x9197\n#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB    0x9198\n#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199\n#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A\n#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9\ntypedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit);\n#endif /* GL_ARB_sparse_texture */\n#ifndef GL_ARB_sparse_texture2\n#define GL_ARB_sparse_texture2 1\n#endif /* GL_ARB_sparse_texture2 */\n#ifndef GL_ARB_sparse_texture_clamp\n#define GL_ARB_sparse_texture_clamp 1\n#endif /* GL_ARB_sparse_texture_clamp */\n#ifndef GL_ARB_spirv_extensions\n#define GL_ARB_spirv_extensions 1\n#endif /* GL_ARB_spirv_extensions */\n#ifndef GL_ARB_stencil_texturing\n#define GL_ARB_stencil_texturing 1\n#endif /* GL_ARB_stencil_texturing */\n#ifndef GL_ARB_sync\n#define GL_ARB_sync 1\n#endif /* GL_ARB_sync */\n#ifndef GL_ARB_tessellation_shader\n#define GL_ARB_tessellation_shader 1\n#endif /* GL_ARB_tessellation_shader */\n#ifndef GL_ARB_texture_barrier\n#define GL_ARB_texture_barrier 1\n#endif /* GL_ARB_texture_barrier */\n#ifndef GL_ARB_texture_border_clamp\n#define GL_ARB_texture_border_clamp 1\n#define GL_CLAMP_TO_BORDER_ARB            0x812D\n#endif /* GL_ARB_texture_border_clamp */\n#ifndef GL_ARB_texture_buffer_object\n#define GL_ARB_texture_buffer_object 1\n#define GL_TEXTURE_BUFFER_ARB             0x8C2A\n#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB    0x8C2B\n#define GL_TEXTURE_BINDING_BUFFER_ARB     0x8C2C\n#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D\n#define GL_TEXTURE_BUFFER_FORMAT_ARB      0x8C2E\ntypedef void (APIENTRYP PFNGLTEXBUFFERARBPROC) (GLenum target, GLenum internalformat, GLuint buffer);\n#endif /* GL_ARB_texture_buffer_object */\n#ifndef GL_ARB_texture_buffer_object_rgb32\n#define GL_ARB_texture_buffer_object_rgb32 1\n#endif /* GL_ARB_texture_buffer_object_rgb32 */\n#ifndef GL_ARB_texture_buffer_range\n#define GL_ARB_texture_buffer_range 1\n#endif /* GL_ARB_texture_buffer_range */\n#ifndef GL_ARB_texture_compression_bptc\n#define GL_ARB_texture_compression_bptc 1\n#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C\n#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D\n#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E\n#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F\n#endif /* GL_ARB_texture_compression_bptc */\n#ifndef GL_ARB_texture_compression_rgtc\n#define GL_ARB_texture_compression_rgtc 1\n#endif /* GL_ARB_texture_compression_rgtc */\n#ifndef GL_ARB_texture_cube_map_array\n#define GL_ARB_texture_cube_map_array 1\n#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB     0x9009\n#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A\n#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B\n#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB     0x900C\n#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D\n#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E\n#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F\n#endif /* GL_ARB_texture_cube_map_array */\n#ifndef GL_ARB_texture_filter_anisotropic\n#define GL_ARB_texture_filter_anisotropic 1\n#endif /* GL_ARB_texture_filter_anisotropic */\n#ifndef GL_ARB_texture_filter_minmax\n#define GL_ARB_texture_filter_minmax 1\n#define GL_TEXTURE_REDUCTION_MODE_ARB     0x9366\n#define GL_WEIGHTED_AVERAGE_ARB           0x9367\n#endif /* GL_ARB_texture_filter_minmax */\n#ifndef GL_ARB_texture_gather\n#define GL_ARB_texture_gather 1\n#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E\n#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F\n#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F\n#endif /* GL_ARB_texture_gather */\n#ifndef GL_ARB_texture_mirror_clamp_to_edge\n#define GL_ARB_texture_mirror_clamp_to_edge 1\n#endif /* GL_ARB_texture_mirror_clamp_to_edge */\n#ifndef GL_ARB_texture_mirrored_repeat\n#define GL_ARB_texture_mirrored_repeat 1\n#define GL_MIRRORED_REPEAT_ARB            0x8370\n#endif /* GL_ARB_texture_mirrored_repeat */\n#ifndef GL_ARB_texture_multisample\n#define GL_ARB_texture_multisample 1\n#endif /* GL_ARB_texture_multisample */\n#ifndef GL_ARB_texture_non_power_of_two\n#define GL_ARB_texture_non_power_of_two 1\n#endif /* GL_ARB_texture_non_power_of_two */\n#ifndef GL_ARB_texture_query_levels\n#define GL_ARB_texture_query_levels 1\n#endif /* GL_ARB_texture_query_levels */\n#ifndef GL_ARB_texture_query_lod\n#define GL_ARB_texture_query_lod 1\n#endif /* GL_ARB_texture_query_lod */\n#ifndef GL_ARB_texture_rg\n#define GL_ARB_texture_rg 1\n#endif /* GL_ARB_texture_rg */\n#ifndef GL_ARB_texture_rgb10_a2ui\n#define GL_ARB_texture_rgb10_a2ui 1\n#endif /* GL_ARB_texture_rgb10_a2ui */\n#ifndef GL_ARB_texture_stencil8\n#define GL_ARB_texture_stencil8 1\n#endif /* GL_ARB_texture_stencil8 */\n#ifndef GL_ARB_texture_storage\n#define GL_ARB_texture_storage 1\n#endif /* GL_ARB_texture_storage */\n#ifndef GL_ARB_texture_storage_multisample\n#define GL_ARB_texture_storage_multisample 1\n#endif /* GL_ARB_texture_storage_multisample */\n#ifndef GL_ARB_texture_swizzle\n#define GL_ARB_texture_swizzle 1\n#endif /* GL_ARB_texture_swizzle */\n#ifndef GL_ARB_texture_view\n#define GL_ARB_texture_view 1\n#endif /* GL_ARB_texture_view */\n#ifndef GL_ARB_timer_query\n#define GL_ARB_timer_query 1\n#endif /* GL_ARB_timer_query */\n#ifndef GL_ARB_transform_feedback2\n#define GL_ARB_transform_feedback2 1\n#endif /* GL_ARB_transform_feedback2 */\n#ifndef GL_ARB_transform_feedback3\n#define GL_ARB_transform_feedback3 1\n#endif /* GL_ARB_transform_feedback3 */\n#ifndef GL_ARB_transform_feedback_instanced\n#define GL_ARB_transform_feedback_instanced 1\n#endif /* GL_ARB_transform_feedback_instanced */\n#ifndef GL_ARB_transform_feedback_overflow_query\n#define GL_ARB_transform_feedback_overflow_query 1\n#define GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB 0x82EC\n#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB 0x82ED\n#endif /* GL_ARB_transform_feedback_overflow_query */\n#ifndef GL_ARB_uniform_buffer_object\n#define GL_ARB_uniform_buffer_object 1\n#endif /* GL_ARB_uniform_buffer_object */\n#ifndef GL_ARB_vertex_array_bgra\n#define GL_ARB_vertex_array_bgra 1\n#endif /* GL_ARB_vertex_array_bgra */\n#ifndef GL_ARB_vertex_array_object\n#define GL_ARB_vertex_array_object 1\n#endif /* GL_ARB_vertex_array_object */\n#ifndef GL_ARB_vertex_attrib_64bit\n#define GL_ARB_vertex_attrib_64bit 1\n#endif /* GL_ARB_vertex_attrib_64bit */\n#ifndef GL_ARB_vertex_attrib_binding\n#define GL_ARB_vertex_attrib_binding 1\n#endif /* GL_ARB_vertex_attrib_binding */\n#ifndef GL_ARB_vertex_type_10f_11f_11f_rev\n#define GL_ARB_vertex_type_10f_11f_11f_rev 1\n#endif /* GL_ARB_vertex_type_10f_11f_11f_rev */\n#ifndef GL_ARB_vertex_type_2_10_10_10_rev\n#define GL_ARB_vertex_type_2_10_10_10_rev 1\n#endif /* GL_ARB_vertex_type_2_10_10_10_rev */\n#ifndef GL_ARB_viewport_array\n#define GL_ARB_viewport_array 1\ntypedef void (APIENTRYP PFNGLDEPTHRANGEARRAYDVNVPROC) (GLuint first, GLsizei count, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDDNVPROC) (GLuint index, GLdouble n, GLdouble f);\n#endif /* GL_ARB_viewport_array */\n#ifndef GL_KHR_blend_equation_advanced\n#define GL_KHR_blend_equation_advanced 1\n#define GL_MULTIPLY_KHR                   0x9294\n#define GL_SCREEN_KHR                     0x9295\n#define GL_OVERLAY_KHR                    0x9296\n#define GL_DARKEN_KHR                     0x9297\n#define GL_LIGHTEN_KHR                    0x9298\n#define GL_COLORDODGE_KHR                 0x9299\n#define GL_COLORBURN_KHR                  0x929A\n#define GL_HARDLIGHT_KHR                  0x929B\n#define GL_SOFTLIGHT_KHR                  0x929C\n#define GL_DIFFERENCE_KHR                 0x929E\n#define GL_EXCLUSION_KHR                  0x92A0\n#define GL_HSL_HUE_KHR                    0x92AD\n#define GL_HSL_SATURATION_KHR             0x92AE\n#define GL_HSL_COLOR_KHR                  0x92AF\n#define GL_HSL_LUMINOSITY_KHR             0x92B0\ntypedef void (APIENTRYP PFNGLBLENDBARRIERKHRPROC) (void);\n#endif /* GL_KHR_blend_equation_advanced */\n#ifndef GL_KHR_blend_equation_advanced_coherent\n#define GL_KHR_blend_equation_advanced_coherent 1\n#define GL_BLEND_ADVANCED_COHERENT_KHR    0x9285\n#endif /* GL_KHR_blend_equation_advanced_coherent */\n#ifndef GL_KHR_context_flush_control\n#define GL_KHR_context_flush_control 1\n#endif /* GL_KHR_context_flush_control */\n#ifndef GL_KHR_debug\n#define GL_KHR_debug 1\n#endif /* GL_KHR_debug */\n#ifndef GL_KHR_no_error\n#define GL_KHR_no_error 1\n#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR  0x00000008\n#endif /* GL_KHR_no_error */\n#ifndef GL_KHR_parallel_shader_compile\n#define GL_KHR_parallel_shader_compile 1\n#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0\n#define GL_COMPLETION_STATUS_KHR          0x91B1\ntypedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) (GLuint count);\n#endif /* GL_KHR_parallel_shader_compile */\n#ifndef GL_KHR_robust_buffer_access_behavior\n#define GL_KHR_robust_buffer_access_behavior 1\n#endif /* GL_KHR_robust_buffer_access_behavior */\n#ifndef GL_KHR_robustness\n#define GL_KHR_robustness 1\n#define GL_CONTEXT_ROBUST_ACCESS          0x90F3\n#endif /* GL_KHR_robustness */\n#ifndef GL_KHR_shader_subgroup\n#define GL_KHR_shader_subgroup 1\n#define GL_SUBGROUP_SIZE_KHR              0x9532\n#define GL_SUBGROUP_SUPPORTED_STAGES_KHR  0x9533\n#define GL_SUBGROUP_SUPPORTED_FEATURES_KHR 0x9534\n#define GL_SUBGROUP_QUAD_ALL_STAGES_KHR   0x9535\n#define GL_SUBGROUP_FEATURE_BASIC_BIT_KHR 0x00000001\n#define GL_SUBGROUP_FEATURE_VOTE_BIT_KHR  0x00000002\n#define GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR 0x00000004\n#define GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR 0x00000008\n#define GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR 0x00000010\n#define GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR 0x00000020\n#define GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR 0x00000040\n#define GL_SUBGROUP_FEATURE_QUAD_BIT_KHR  0x00000080\n#endif /* GL_KHR_shader_subgroup */\n#ifndef GL_KHR_texture_compression_astc_hdr\n#define GL_KHR_texture_compression_astc_hdr 1\n#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR   0x93B0\n#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR   0x93B1\n#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR   0x93B2\n#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR   0x93B3\n#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR   0x93B4\n#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR   0x93B5\n#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR   0x93B6\n#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR   0x93B7\n#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR  0x93B8\n#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR  0x93B9\n#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR  0x93BA\n#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB\n#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC\n#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD\n#endif /* GL_KHR_texture_compression_astc_hdr */\n#ifndef GL_KHR_texture_compression_astc_ldr\n#define GL_KHR_texture_compression_astc_ldr 1\n#endif /* GL_KHR_texture_compression_astc_ldr */\n#ifndef GL_KHR_texture_compression_astc_sliced_3d\n#define GL_KHR_texture_compression_astc_sliced_3d 1\n#endif /* GL_KHR_texture_compression_astc_sliced_3d */\n#ifndef GL_AMD_framebuffer_multisample_advanced\n#define GL_AMD_framebuffer_multisample_advanced 1\n#define GL_RENDERBUFFER_STORAGE_SAMPLES_AMD 0x91B2\n#define GL_MAX_COLOR_FRAMEBUFFER_SAMPLES_AMD 0x91B3\n#define GL_MAX_COLOR_FRAMEBUFFER_STORAGE_SAMPLES_AMD 0x91B4\n#define GL_MAX_DEPTH_STENCIL_FRAMEBUFFER_SAMPLES_AMD 0x91B5\n#define GL_NUM_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B6\n#define GL_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B7\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height);\n#endif /* GL_AMD_framebuffer_multisample_advanced */\n#ifndef GL_AMD_performance_monitor\n#define GL_AMD_performance_monitor 1\n#define GL_COUNTER_TYPE_AMD               0x8BC0\n#define GL_COUNTER_RANGE_AMD              0x8BC1\n#define GL_UNSIGNED_INT64_AMD             0x8BC2\n#define GL_PERCENTAGE_AMD                 0x8BC3\n#define GL_PERFMON_RESULT_AVAILABLE_AMD   0x8BC4\n#define GL_PERFMON_RESULT_SIZE_AMD        0x8BC5\n#define GL_PERFMON_RESULT_AMD             0x8BC6\ntypedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups);\ntypedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters);\ntypedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString);\ntypedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString);\ntypedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data);\ntypedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors);\ntypedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors);\ntypedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList);\ntypedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor);\ntypedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor);\ntypedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten);\n#endif /* GL_AMD_performance_monitor */\n#ifndef GL_APPLE_rgb_422\n#define GL_APPLE_rgb_422 1\n#define GL_RGB_422_APPLE                  0x8A1F\n#define GL_UNSIGNED_SHORT_8_8_APPLE       0x85BA\n#define GL_UNSIGNED_SHORT_8_8_REV_APPLE   0x85BB\n#define GL_RGB_RAW_422_APPLE              0x8A51\n#endif /* GL_APPLE_rgb_422 */\n#ifndef GL_EXT_EGL_image_storage\n#define GL_EXT_EGL_image_storage 1\ntypedef void *GLeglImageOES;\ntypedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC) (GLenum target, GLeglImageOES image, const GLint* attrib_list);\ntypedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC) (GLuint texture, GLeglImageOES image, const GLint* attrib_list);\n#endif /* GL_EXT_EGL_image_storage */\n#ifndef GL_EXT_EGL_sync\n#define GL_EXT_EGL_sync 1\n#endif /* GL_EXT_EGL_sync */\n#ifndef GL_EXT_debug_label\n#define GL_EXT_debug_label 1\n#define GL_PROGRAM_PIPELINE_OBJECT_EXT    0x8A4F\n#define GL_PROGRAM_OBJECT_EXT             0x8B40\n#define GL_SHADER_OBJECT_EXT              0x8B48\n#define GL_BUFFER_OBJECT_EXT              0x9151\n#define GL_QUERY_OBJECT_EXT               0x9153\n#define GL_VERTEX_ARRAY_OBJECT_EXT        0x9154\ntypedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC) (GLenum type, GLuint object, GLsizei length, const GLchar *label);\ntypedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label);\n#endif /* GL_EXT_debug_label */\n#ifndef GL_EXT_debug_marker\n#define GL_EXT_debug_marker 1\ntypedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC) (GLsizei length, const GLchar *marker);\ntypedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC) (GLsizei length, const GLchar *marker);\ntypedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC) (void);\n#endif /* GL_EXT_debug_marker */\n#ifndef GL_EXT_direct_state_access\n#define GL_EXT_direct_state_access 1\n#define GL_PROGRAM_MATRIX_EXT             0x8E2D\n#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT   0x8E2E\n#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F\ntypedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m);\ntypedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m);\ntypedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m);\ntypedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m);\ntypedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode);\ntypedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z);\ntypedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z);\ntypedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z);\ntypedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z);\ntypedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z);\ntypedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z);\ntypedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);\ntypedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);\ntypedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode);\ntypedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode);\ntypedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask);\ntypedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat param);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLfloat *params);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params);\ntypedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border);\ntypedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels);\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC) (GLenum texunit, GLenum target, GLuint texture);\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC) (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer);\ntypedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param);\ntypedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params);\ntypedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params);\ntypedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble param);\ntypedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params);\ntypedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat param);\ntypedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params);\ntypedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLint *params);\ntypedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params);\ntypedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params);\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param);\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params);\ntypedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border);\ntypedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);\ntypedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);\ntypedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels);\ntypedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);\ntypedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index);\ntypedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index);\ntypedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data);\ntypedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data);\ntypedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, void **data);\ntypedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index);\ntypedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index);\ntypedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index);\ntypedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data);\ntypedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint lod, void *img);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits);\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint lod, void *img);\ntypedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m);\ntypedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m);\ntypedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m);\ntypedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m);\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage);\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data);\ntypedef void *(APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access);\ntypedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer);\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, void **params);\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat v0);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint v0);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer);\ntypedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params);\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLuint *params);\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLuint *params);\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params);\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLuint *params);\ntypedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLuint *params);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC) (GLuint program, GLint location, GLuint v0);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC) (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLint *params);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLuint *params);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params);\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLint *params);\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint *params);\ntypedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index);\ntypedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index);\ntypedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params);\ntypedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLdouble *params);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble *params);\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC) (GLuint program, GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum pname, void *string);\ntypedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC) (GLuint renderbuffer, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC) (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC) (GLuint framebuffer, GLenum target);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);\ntypedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC) (GLuint texture, GLenum target);\ntypedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC) (GLenum texunit, GLenum target);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC) (GLuint framebuffer, GLenum mode);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs);\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC) (GLuint framebuffer, GLenum mode);\ntypedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face);\ntypedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC) (GLuint texture, GLenum target, GLuint renderbuffer);\ntypedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC) (GLenum texunit, GLenum target, GLuint renderbuffer);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array);\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array);\ntypedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index);\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index);\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC) (GLuint vaobj, GLenum pname, GLint *param);\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC) (GLuint vaobj, GLenum pname, void **param);\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param);\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param);\ntypedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access);\ntypedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length);\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags);\ntypedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data);\ntypedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC) (GLuint program, GLint location, GLdouble x);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value);\ntypedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width);\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset);\ntypedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit);\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC) (GLuint vaobj, GLuint index, GLuint divisor);\n#endif /* GL_EXT_direct_state_access */\n#ifndef GL_EXT_draw_instanced\n#define GL_EXT_draw_instanced 1\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount);\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);\n#endif /* GL_EXT_draw_instanced */\n#ifndef GL_EXT_multiview_tessellation_geometry_shader\n#define GL_EXT_multiview_tessellation_geometry_shader 1\n#endif /* GL_EXT_multiview_tessellation_geometry_shader */\n#ifndef GL_EXT_multiview_texture_multisample\n#define GL_EXT_multiview_texture_multisample 1\n#endif /* GL_EXT_multiview_texture_multisample */\n#ifndef GL_EXT_multiview_timer_query\n#define GL_EXT_multiview_timer_query 1\n#endif /* GL_EXT_multiview_timer_query */\n#ifndef GL_EXT_polygon_offset_clamp\n#define GL_EXT_polygon_offset_clamp 1\n#define GL_POLYGON_OFFSET_CLAMP_EXT       0x8E1B\ntypedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPEXTPROC) (GLfloat factor, GLfloat units, GLfloat clamp);\n#endif /* GL_EXT_polygon_offset_clamp */\n#ifndef GL_EXT_post_depth_coverage\n#define GL_EXT_post_depth_coverage 1\n#endif /* GL_EXT_post_depth_coverage */\n#ifndef GL_EXT_raster_multisample\n#define GL_EXT_raster_multisample 1\n#define GL_RASTER_MULTISAMPLE_EXT         0x9327\n#define GL_RASTER_SAMPLES_EXT             0x9328\n#define GL_MAX_RASTER_SAMPLES_EXT         0x9329\n#define GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT 0x932A\n#define GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT 0x932B\n#define GL_EFFECTIVE_RASTER_SAMPLES_EXT   0x932C\ntypedef void (APIENTRYP PFNGLRASTERSAMPLESEXTPROC) (GLuint samples, GLboolean fixedsamplelocations);\n#endif /* GL_EXT_raster_multisample */\n#ifndef GL_EXT_separate_shader_objects\n#define GL_EXT_separate_shader_objects 1\n#define GL_ACTIVE_PROGRAM_EXT             0x8B8D\ntypedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC) (GLenum type, GLuint program);\ntypedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC) (GLuint program);\ntypedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC) (GLenum type, const GLchar *string);\n#endif /* GL_EXT_separate_shader_objects */\n#ifndef GL_EXT_shader_framebuffer_fetch\n#define GL_EXT_shader_framebuffer_fetch 1\n#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52\n#endif /* GL_EXT_shader_framebuffer_fetch */\n#ifndef GL_EXT_shader_framebuffer_fetch_non_coherent\n#define GL_EXT_shader_framebuffer_fetch_non_coherent 1\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC) (void);\n#endif /* GL_EXT_shader_framebuffer_fetch_non_coherent */\n#ifndef GL_EXT_shader_integer_mix\n#define GL_EXT_shader_integer_mix 1\n#endif /* GL_EXT_shader_integer_mix */\n#ifndef GL_EXT_texture_compression_s3tc\n#define GL_EXT_texture_compression_s3tc 1\n#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT   0x83F0\n#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT  0x83F1\n#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT  0x83F2\n#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT  0x83F3\n#endif /* GL_EXT_texture_compression_s3tc */\n#ifndef GL_EXT_texture_filter_minmax\n#define GL_EXT_texture_filter_minmax 1\n#define GL_TEXTURE_REDUCTION_MODE_EXT     0x9366\n#define GL_WEIGHTED_AVERAGE_EXT           0x9367\n#endif /* GL_EXT_texture_filter_minmax */\n#ifndef GL_EXT_texture_sRGB_R8\n#define GL_EXT_texture_sRGB_R8 1\n#define GL_SR8_EXT                        0x8FBD\n#endif /* GL_EXT_texture_sRGB_R8 */\n#ifndef GL_EXT_texture_sRGB_RG8\n#define GL_EXT_texture_sRGB_RG8 1\n#define GL_SRG8_EXT                       0x8FBE\n#endif /* GL_EXT_texture_sRGB_RG8 */\n#ifndef GL_EXT_texture_sRGB_decode\n#define GL_EXT_texture_sRGB_decode 1\n#define GL_TEXTURE_SRGB_DECODE_EXT        0x8A48\n#define GL_DECODE_EXT                     0x8A49\n#define GL_SKIP_DECODE_EXT                0x8A4A\n#endif /* GL_EXT_texture_sRGB_decode */\n#ifndef GL_EXT_texture_shadow_lod\n#define GL_EXT_texture_shadow_lod 1\n#endif /* GL_EXT_texture_shadow_lod */\n#ifndef GL_EXT_window_rectangles\n#define GL_EXT_window_rectangles 1\n#define GL_INCLUSIVE_EXT                  0x8F10\n#define GL_EXCLUSIVE_EXT                  0x8F11\n#define GL_WINDOW_RECTANGLE_EXT           0x8F12\n#define GL_WINDOW_RECTANGLE_MODE_EXT      0x8F13\n#define GL_MAX_WINDOW_RECTANGLES_EXT      0x8F14\n#define GL_NUM_WINDOW_RECTANGLES_EXT      0x8F15\ntypedef void (APIENTRYP PFNGLWINDOWRECTANGLESEXTPROC) (GLenum mode, GLsizei count, const GLint *box);\n#endif /* GL_EXT_window_rectangles */\n#ifndef GL_INTEL_blackhole_render\n#define GL_INTEL_blackhole_render 1\n#define GL_BLACKHOLE_RENDER_INTEL         0x83FC\n#endif /* GL_INTEL_blackhole_render */\n#ifndef GL_INTEL_conservative_rasterization\n#define GL_INTEL_conservative_rasterization 1\n#define GL_CONSERVATIVE_RASTERIZATION_INTEL 0x83FE\n#endif /* GL_INTEL_conservative_rasterization */\n#ifndef GL_INTEL_framebuffer_CMAA\n#define GL_INTEL_framebuffer_CMAA 1\ntypedef void (APIENTRYP PFNGLAPPLYFRAMEBUFFERATTACHMENTCMAAINTELPROC) (void);\n#endif /* GL_INTEL_framebuffer_CMAA */\n#ifndef GL_INTEL_performance_query\n#define GL_INTEL_performance_query 1\n#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000\n#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001\n#define GL_PERFQUERY_WAIT_INTEL           0x83FB\n#define GL_PERFQUERY_FLUSH_INTEL          0x83FA\n#define GL_PERFQUERY_DONOT_FLUSH_INTEL    0x83F9\n#define GL_PERFQUERY_COUNTER_EVENT_INTEL  0x94F0\n#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1\n#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2\n#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3\n#define GL_PERFQUERY_COUNTER_RAW_INTEL    0x94F4\n#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5\n#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8\n#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9\n#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA\n#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB\n#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC\n#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD\n#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE\n#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF\n#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500\ntypedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC) (GLuint queryHandle);\ntypedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC) (GLuint queryId, GLuint *queryHandle);\ntypedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC) (GLuint queryHandle);\ntypedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle);\ntypedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId);\ntypedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId);\ntypedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue);\ntypedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten);\ntypedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId);\ntypedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask);\n#endif /* GL_INTEL_performance_query */\n#ifndef GL_MESA_framebuffer_flip_x\n#define GL_MESA_framebuffer_flip_x 1\n#define GL_FRAMEBUFFER_FLIP_X_MESA        0x8BBC\n#endif /* GL_MESA_framebuffer_flip_x */\n#ifndef GL_MESA_framebuffer_flip_y\n#define GL_MESA_framebuffer_flip_y 1\n#define GL_FRAMEBUFFER_FLIP_Y_MESA        0x8BBB\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIMESAPROC) (GLenum target, GLenum pname, GLint param);\ntypedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC) (GLenum target, GLenum pname, GLint *params);\n#ifdef GL_GLEXT_PROTOTYPES\nGLAPI void APIENTRY glFramebufferParameteriMESA (GLenum target, GLenum pname, GLint param);\nGLAPI void APIENTRY glGetFramebufferParameterivMESA (GLenum target, GLenum pname, GLint *params);\n#endif\n#endif /* GL_MESA_framebuffer_flip_y */\n#ifndef GL_MESA_framebuffer_swap_xy\n#define GL_MESA_framebuffer_swap_xy 1\n#define GL_FRAMEBUFFER_SWAP_XY_MESA       0x8BBD\n#endif /* GL_MESA_framebuffer_swap_xy */\n#ifndef GL_NV_bindless_multi_draw_indirect\n#define GL_NV_bindless_multi_draw_indirect 1\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount);\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount);\n#endif /* GL_NV_bindless_multi_draw_indirect */\n#ifndef GL_NV_bindless_multi_draw_indirect_count\n#define GL_NV_bindless_multi_draw_indirect_count 1\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount);\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount);\n#endif /* GL_NV_bindless_multi_draw_indirect_count */\n#ifndef GL_NV_bindless_texture\n#define GL_NV_bindless_texture 1\ntypedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC) (GLuint texture);\ntypedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC) (GLuint texture, GLuint sampler);\ntypedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle);\ntypedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC) (GLuint64 handle);\ntypedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format);\ntypedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle, GLenum access);\ntypedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC) (GLuint64 handle);\ntypedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC) (GLint location, GLuint64 value);\ntypedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC) (GLint location, GLsizei count, const GLuint64 *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC) (GLuint program, GLint location, GLuint64 value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values);\ntypedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle);\ntypedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle);\n#endif /* GL_NV_bindless_texture */\n#ifndef GL_NV_blend_equation_advanced\n#define GL_NV_blend_equation_advanced 1\n#define GL_BLEND_OVERLAP_NV               0x9281\n#define GL_BLEND_PREMULTIPLIED_SRC_NV     0x9280\n#define GL_BLUE_NV                        0x1905\n#define GL_COLORBURN_NV                   0x929A\n#define GL_COLORDODGE_NV                  0x9299\n#define GL_CONJOINT_NV                    0x9284\n#define GL_CONTRAST_NV                    0x92A1\n#define GL_DARKEN_NV                      0x9297\n#define GL_DIFFERENCE_NV                  0x929E\n#define GL_DISJOINT_NV                    0x9283\n#define GL_DST_ATOP_NV                    0x928F\n#define GL_DST_IN_NV                      0x928B\n#define GL_DST_NV                         0x9287\n#define GL_DST_OUT_NV                     0x928D\n#define GL_DST_OVER_NV                    0x9289\n#define GL_EXCLUSION_NV                   0x92A0\n#define GL_GREEN_NV                       0x1904\n#define GL_HARDLIGHT_NV                   0x929B\n#define GL_HARDMIX_NV                     0x92A9\n#define GL_HSL_COLOR_NV                   0x92AF\n#define GL_HSL_HUE_NV                     0x92AD\n#define GL_HSL_LUMINOSITY_NV              0x92B0\n#define GL_HSL_SATURATION_NV              0x92AE\n#define GL_INVERT_OVG_NV                  0x92B4\n#define GL_INVERT_RGB_NV                  0x92A3\n#define GL_LIGHTEN_NV                     0x9298\n#define GL_LINEARBURN_NV                  0x92A5\n#define GL_LINEARDODGE_NV                 0x92A4\n#define GL_LINEARLIGHT_NV                 0x92A7\n#define GL_MINUS_CLAMPED_NV               0x92B3\n#define GL_MINUS_NV                       0x929F\n#define GL_MULTIPLY_NV                    0x9294\n#define GL_OVERLAY_NV                     0x9296\n#define GL_PINLIGHT_NV                    0x92A8\n#define GL_PLUS_CLAMPED_ALPHA_NV          0x92B2\n#define GL_PLUS_CLAMPED_NV                0x92B1\n#define GL_PLUS_DARKER_NV                 0x9292\n#define GL_PLUS_NV                        0x9291\n#define GL_RED_NV                         0x1903\n#define GL_SCREEN_NV                      0x9295\n#define GL_SOFTLIGHT_NV                   0x929C\n#define GL_SRC_ATOP_NV                    0x928E\n#define GL_SRC_IN_NV                      0x928A\n#define GL_SRC_NV                         0x9286\n#define GL_SRC_OUT_NV                     0x928C\n#define GL_SRC_OVER_NV                    0x9288\n#define GL_UNCORRELATED_NV                0x9282\n#define GL_VIVIDLIGHT_NV                  0x92A6\n#define GL_XOR_NV                         0x1506\ntypedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC) (GLenum pname, GLint value);\ntypedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC) (void);\n#endif /* GL_NV_blend_equation_advanced */\n#ifndef GL_NV_blend_equation_advanced_coherent\n#define GL_NV_blend_equation_advanced_coherent 1\n#define GL_BLEND_ADVANCED_COHERENT_NV     0x9285\n#endif /* GL_NV_blend_equation_advanced_coherent */\n#ifndef GL_NV_blend_minmax_factor\n#define GL_NV_blend_minmax_factor 1\n#define GL_FACTOR_MIN_AMD                 0x901C\n#define GL_FACTOR_MAX_AMD                 0x901D\n#endif /* GL_NV_blend_minmax_factor */\n#ifndef GL_NV_clip_space_w_scaling\n#define GL_NV_clip_space_w_scaling 1\n#define GL_VIEWPORT_POSITION_W_SCALE_NV   0x937C\n#define GL_VIEWPORT_POSITION_W_SCALE_X_COEFF_NV 0x937D\n#define GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV 0x937E\ntypedef void (APIENTRYP PFNGLVIEWPORTPOSITIONWSCALENVPROC) (GLuint index, GLfloat xcoeff, GLfloat ycoeff);\n#endif /* GL_NV_clip_space_w_scaling */\n#ifndef GL_NV_command_list\n#define GL_NV_command_list 1\n#define GL_TERMINATE_SEQUENCE_COMMAND_NV  0x0000\n#define GL_NOP_COMMAND_NV                 0x0001\n#define GL_DRAW_ELEMENTS_COMMAND_NV       0x0002\n#define GL_DRAW_ARRAYS_COMMAND_NV         0x0003\n#define GL_DRAW_ELEMENTS_STRIP_COMMAND_NV 0x0004\n#define GL_DRAW_ARRAYS_STRIP_COMMAND_NV   0x0005\n#define GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV 0x0006\n#define GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV 0x0007\n#define GL_ELEMENT_ADDRESS_COMMAND_NV     0x0008\n#define GL_ATTRIBUTE_ADDRESS_COMMAND_NV   0x0009\n#define GL_UNIFORM_ADDRESS_COMMAND_NV     0x000A\n#define GL_BLEND_COLOR_COMMAND_NV         0x000B\n#define GL_STENCIL_REF_COMMAND_NV         0x000C\n#define GL_LINE_WIDTH_COMMAND_NV          0x000D\n#define GL_POLYGON_OFFSET_COMMAND_NV      0x000E\n#define GL_ALPHA_REF_COMMAND_NV           0x000F\n#define GL_VIEWPORT_COMMAND_NV            0x0010\n#define GL_SCISSOR_COMMAND_NV             0x0011\n#define GL_FRONT_FACE_COMMAND_NV          0x0012\ntypedef void (APIENTRYP PFNGLCREATESTATESNVPROC) (GLsizei n, GLuint *states);\ntypedef void (APIENTRYP PFNGLDELETESTATESNVPROC) (GLsizei n, const GLuint *states);\ntypedef GLboolean (APIENTRYP PFNGLISSTATENVPROC) (GLuint state);\ntypedef void (APIENTRYP PFNGLSTATECAPTURENVPROC) (GLuint state, GLenum mode);\ntypedef GLuint (APIENTRYP PFNGLGETCOMMANDHEADERNVPROC) (GLenum tokenID, GLuint size);\ntypedef GLushort (APIENTRYP PFNGLGETSTAGEINDEXNVPROC) (GLenum shadertype);\ntypedef void (APIENTRYP PFNGLDRAWCOMMANDSNVPROC) (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count);\ntypedef void (APIENTRYP PFNGLDRAWCOMMANDSADDRESSNVPROC) (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count);\ntypedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESNVPROC) (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count);\ntypedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESADDRESSNVPROC) (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count);\ntypedef void (APIENTRYP PFNGLCREATECOMMANDLISTSNVPROC) (GLsizei n, GLuint *lists);\ntypedef void (APIENTRYP PFNGLDELETECOMMANDLISTSNVPROC) (GLsizei n, const GLuint *lists);\ntypedef GLboolean (APIENTRYP PFNGLISCOMMANDLISTNVPROC) (GLuint list);\ntypedef void (APIENTRYP PFNGLLISTDRAWCOMMANDSSTATESCLIENTNVPROC) (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count);\ntypedef void (APIENTRYP PFNGLCOMMANDLISTSEGMENTSNVPROC) (GLuint list, GLuint segments);\ntypedef void (APIENTRYP PFNGLCOMPILECOMMANDLISTNVPROC) (GLuint list);\ntypedef void (APIENTRYP PFNGLCALLCOMMANDLISTNVPROC) (GLuint list);\n#endif /* GL_NV_command_list */\n#ifndef GL_NV_compute_shader_derivatives\n#define GL_NV_compute_shader_derivatives 1\n#endif /* GL_NV_compute_shader_derivatives */\n#ifndef GL_NV_conditional_render\n#define GL_NV_conditional_render 1\n#define GL_QUERY_WAIT_NV                  0x8E13\n#define GL_QUERY_NO_WAIT_NV               0x8E14\n#define GL_QUERY_BY_REGION_WAIT_NV        0x8E15\n#define GL_QUERY_BY_REGION_NO_WAIT_NV     0x8E16\ntypedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC) (GLuint id, GLenum mode);\ntypedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC) (void);\n#endif /* GL_NV_conditional_render */\n#ifndef GL_NV_conservative_raster\n#define GL_NV_conservative_raster 1\n#define GL_CONSERVATIVE_RASTERIZATION_NV  0x9346\n#define GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV 0x9347\n#define GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV 0x9348\n#define GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV 0x9349\ntypedef void (APIENTRYP PFNGLSUBPIXELPRECISIONBIASNVPROC) (GLuint xbits, GLuint ybits);\n#endif /* GL_NV_conservative_raster */\n#ifndef GL_NV_conservative_raster_dilate\n#define GL_NV_conservative_raster_dilate 1\n#define GL_CONSERVATIVE_RASTER_DILATE_NV  0x9379\n#define GL_CONSERVATIVE_RASTER_DILATE_RANGE_NV 0x937A\n#define GL_CONSERVATIVE_RASTER_DILATE_GRANULARITY_NV 0x937B\ntypedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERFNVPROC) (GLenum pname, GLfloat value);\n#endif /* GL_NV_conservative_raster_dilate */\n#ifndef GL_NV_conservative_raster_pre_snap\n#define GL_NV_conservative_raster_pre_snap 1\n#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV 0x9550\n#endif /* GL_NV_conservative_raster_pre_snap */\n#ifndef GL_NV_conservative_raster_pre_snap_triangles\n#define GL_NV_conservative_raster_pre_snap_triangles 1\n#define GL_CONSERVATIVE_RASTER_MODE_NV    0x954D\n#define GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV 0x954E\n#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV 0x954F\ntypedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERINVPROC) (GLenum pname, GLint param);\n#endif /* GL_NV_conservative_raster_pre_snap_triangles */\n#ifndef GL_NV_conservative_raster_underestimation\n#define GL_NV_conservative_raster_underestimation 1\n#endif /* GL_NV_conservative_raster_underestimation */\n#ifndef GL_NV_depth_buffer_float\n#define GL_NV_depth_buffer_float 1\n#define GL_DEPTH_COMPONENT32F_NV          0x8DAB\n#define GL_DEPTH32F_STENCIL8_NV           0x8DAC\n#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD\n#define GL_DEPTH_BUFFER_FLOAT_MODE_NV     0x8DAF\ntypedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar);\ntypedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth);\ntypedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax);\n#endif /* GL_NV_depth_buffer_float */\n#ifndef GL_NV_draw_vulkan_image\n#define GL_NV_draw_vulkan_image 1\ntypedef void (APIENTRY  *GLVULKANPROCNV)(void);\ntypedef void (APIENTRYP PFNGLDRAWVKIMAGENVPROC) (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1);\ntypedef GLVULKANPROCNV (APIENTRYP PFNGLGETVKPROCADDRNVPROC) (const GLchar *name);\ntypedef void (APIENTRYP PFNGLWAITVKSEMAPHORENVPROC) (GLuint64 vkSemaphore);\ntypedef void (APIENTRYP PFNGLSIGNALVKSEMAPHORENVPROC) (GLuint64 vkSemaphore);\ntypedef void (APIENTRYP PFNGLSIGNALVKFENCENVPROC) (GLuint64 vkFence);\n#endif /* GL_NV_draw_vulkan_image */\n#ifndef GL_NV_fill_rectangle\n#define GL_NV_fill_rectangle 1\n#define GL_FILL_RECTANGLE_NV              0x933C\n#endif /* GL_NV_fill_rectangle */\n#ifndef GL_NV_fragment_coverage_to_color\n#define GL_NV_fragment_coverage_to_color 1\n#define GL_FRAGMENT_COVERAGE_TO_COLOR_NV  0x92DD\n#define GL_FRAGMENT_COVERAGE_COLOR_NV     0x92DE\ntypedef void (APIENTRYP PFNGLFRAGMENTCOVERAGECOLORNVPROC) (GLuint color);\n#endif /* GL_NV_fragment_coverage_to_color */\n#ifndef GL_NV_fragment_shader_barycentric\n#define GL_NV_fragment_shader_barycentric 1\n#endif /* GL_NV_fragment_shader_barycentric */\n#ifndef GL_NV_fragment_shader_interlock\n#define GL_NV_fragment_shader_interlock 1\n#endif /* GL_NV_fragment_shader_interlock */\n#ifndef GL_NV_framebuffer_mixed_samples\n#define GL_NV_framebuffer_mixed_samples 1\n#define GL_COVERAGE_MODULATION_TABLE_NV   0x9331\n#define GL_COLOR_SAMPLES_NV               0x8E20\n#define GL_DEPTH_SAMPLES_NV               0x932D\n#define GL_STENCIL_SAMPLES_NV             0x932E\n#define GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV 0x932F\n#define GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV 0x9330\n#define GL_COVERAGE_MODULATION_NV         0x9332\n#define GL_COVERAGE_MODULATION_TABLE_SIZE_NV 0x9333\ntypedef void (APIENTRYP PFNGLCOVERAGEMODULATIONTABLENVPROC) (GLsizei n, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLGETCOVERAGEMODULATIONTABLENVPROC) (GLsizei bufSize, GLfloat *v);\ntypedef void (APIENTRYP PFNGLCOVERAGEMODULATIONNVPROC) (GLenum components);\n#endif /* GL_NV_framebuffer_mixed_samples */\n#ifndef GL_NV_framebuffer_multisample_coverage\n#define GL_NV_framebuffer_multisample_coverage 1\n#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB\n#define GL_RENDERBUFFER_COLOR_SAMPLES_NV  0x8E10\n#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11\n#define GL_MULTISAMPLE_COVERAGE_MODES_NV  0x8E12\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height);\n#endif /* GL_NV_framebuffer_multisample_coverage */\n#ifndef GL_NV_geometry_shader_passthrough\n#define GL_NV_geometry_shader_passthrough 1\n#endif /* GL_NV_geometry_shader_passthrough */\n#ifndef GL_NV_gpu_shader5\n#define GL_NV_gpu_shader5 1\ntypedef khronos_int64_t GLint64EXT;\n#define GL_INT64_NV                       0x140E\n#define GL_UNSIGNED_INT64_NV              0x140F\n#define GL_INT8_NV                        0x8FE0\n#define GL_INT8_VEC2_NV                   0x8FE1\n#define GL_INT8_VEC3_NV                   0x8FE2\n#define GL_INT8_VEC4_NV                   0x8FE3\n#define GL_INT16_NV                       0x8FE4\n#define GL_INT16_VEC2_NV                  0x8FE5\n#define GL_INT16_VEC3_NV                  0x8FE6\n#define GL_INT16_VEC4_NV                  0x8FE7\n#define GL_INT64_VEC2_NV                  0x8FE9\n#define GL_INT64_VEC3_NV                  0x8FEA\n#define GL_INT64_VEC4_NV                  0x8FEB\n#define GL_UNSIGNED_INT8_NV               0x8FEC\n#define GL_UNSIGNED_INT8_VEC2_NV          0x8FED\n#define GL_UNSIGNED_INT8_VEC3_NV          0x8FEE\n#define GL_UNSIGNED_INT8_VEC4_NV          0x8FEF\n#define GL_UNSIGNED_INT16_NV              0x8FF0\n#define GL_UNSIGNED_INT16_VEC2_NV         0x8FF1\n#define GL_UNSIGNED_INT16_VEC3_NV         0x8FF2\n#define GL_UNSIGNED_INT16_VEC4_NV         0x8FF3\n#define GL_UNSIGNED_INT64_VEC2_NV         0x8FF5\n#define GL_UNSIGNED_INT64_VEC3_NV         0x8FF6\n#define GL_UNSIGNED_INT64_VEC4_NV         0x8FF7\n#define GL_FLOAT16_NV                     0x8FF8\n#define GL_FLOAT16_VEC2_NV                0x8FF9\n#define GL_FLOAT16_VEC3_NV                0x8FFA\n#define GL_FLOAT16_VEC4_NV                0x8FFB\ntypedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC) (GLint location, GLint64EXT x);\ntypedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y);\ntypedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z);\ntypedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w);\ntypedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value);\ntypedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value);\ntypedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value);\ntypedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value);\ntypedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC) (GLint location, GLuint64EXT x);\ntypedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y);\ntypedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z);\ntypedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w);\ntypedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value);\ntypedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value);\ntypedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value);\ntypedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value);\ntypedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC) (GLuint program, GLint location, GLint64EXT *params);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC) (GLuint program, GLint location, GLint64EXT x);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value);\n#endif /* GL_NV_gpu_shader5 */\n#ifndef GL_NV_internalformat_sample_query\n#define GL_NV_internalformat_sample_query 1\n#define GL_MULTISAMPLES_NV                0x9371\n#define GL_SUPERSAMPLE_SCALE_X_NV         0x9372\n#define GL_SUPERSAMPLE_SCALE_Y_NV         0x9373\n#define GL_CONFORMANT_NV                  0x9374\ntypedef void (APIENTRYP PFNGLGETINTERNALFORMATSAMPLEIVNVPROC) (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint *params);\n#endif /* GL_NV_internalformat_sample_query */\n#ifndef GL_NV_memory_attachment\n#define GL_NV_memory_attachment 1\n#define GL_ATTACHED_MEMORY_OBJECT_NV      0x95A4\n#define GL_ATTACHED_MEMORY_OFFSET_NV      0x95A5\n#define GL_MEMORY_ATTACHABLE_ALIGNMENT_NV 0x95A6\n#define GL_MEMORY_ATTACHABLE_SIZE_NV      0x95A7\n#define GL_MEMORY_ATTACHABLE_NV           0x95A8\n#define GL_DETACHED_MEMORY_INCARNATION_NV 0x95A9\n#define GL_DETACHED_TEXTURES_NV           0x95AA\n#define GL_DETACHED_BUFFERS_NV            0x95AB\n#define GL_MAX_DETACHED_TEXTURES_NV       0x95AC\n#define GL_MAX_DETACHED_BUFFERS_NV        0x95AD\ntypedef void (APIENTRYP PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC) (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params);\ntypedef void (APIENTRYP PFNGLRESETMEMORYOBJECTPARAMETERNVPROC) (GLuint memory, GLenum pname);\ntypedef void (APIENTRYP PFNGLTEXATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset);\ntypedef void (APIENTRYP PFNGLBUFFERATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset);\ntypedef void (APIENTRYP PFNGLTEXTUREATTACHMEMORYNVPROC) (GLuint texture, GLuint memory, GLuint64 offset);\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERATTACHMEMORYNVPROC) (GLuint buffer, GLuint memory, GLuint64 offset);\n#endif /* GL_NV_memory_attachment */\n#ifndef GL_NV_memory_object_sparse\n#define GL_NV_memory_object_sparse 1\ntypedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTMEMNVPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit);\ntypedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTMEMNVPROC) (GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit);\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTMEMNVPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit);\ntypedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTMEMNVPROC) (GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit);\n#endif /* GL_NV_memory_object_sparse */\n#ifndef GL_NV_mesh_shader\n#define GL_NV_mesh_shader 1\n#define GL_MESH_SHADER_NV                 0x9559\n#define GL_TASK_SHADER_NV                 0x955A\n#define GL_MAX_MESH_UNIFORM_BLOCKS_NV     0x8E60\n#define GL_MAX_MESH_TEXTURE_IMAGE_UNITS_NV 0x8E61\n#define GL_MAX_MESH_IMAGE_UNIFORMS_NV     0x8E62\n#define GL_MAX_MESH_UNIFORM_COMPONENTS_NV 0x8E63\n#define GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_NV 0x8E64\n#define GL_MAX_MESH_ATOMIC_COUNTERS_NV    0x8E65\n#define GL_MAX_MESH_SHADER_STORAGE_BLOCKS_NV 0x8E66\n#define GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_NV 0x8E67\n#define GL_MAX_TASK_UNIFORM_BLOCKS_NV     0x8E68\n#define GL_MAX_TASK_TEXTURE_IMAGE_UNITS_NV 0x8E69\n#define GL_MAX_TASK_IMAGE_UNIFORMS_NV     0x8E6A\n#define GL_MAX_TASK_UNIFORM_COMPONENTS_NV 0x8E6B\n#define GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_NV 0x8E6C\n#define GL_MAX_TASK_ATOMIC_COUNTERS_NV    0x8E6D\n#define GL_MAX_TASK_SHADER_STORAGE_BLOCKS_NV 0x8E6E\n#define GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_NV 0x8E6F\n#define GL_MAX_MESH_WORK_GROUP_INVOCATIONS_NV 0x95A2\n#define GL_MAX_TASK_WORK_GROUP_INVOCATIONS_NV 0x95A3\n#define GL_MAX_MESH_TOTAL_MEMORY_SIZE_NV  0x9536\n#define GL_MAX_TASK_TOTAL_MEMORY_SIZE_NV  0x9537\n#define GL_MAX_MESH_OUTPUT_VERTICES_NV    0x9538\n#define GL_MAX_MESH_OUTPUT_PRIMITIVES_NV  0x9539\n#define GL_MAX_TASK_OUTPUT_COUNT_NV       0x953A\n#define GL_MAX_DRAW_MESH_TASKS_COUNT_NV   0x953D\n#define GL_MAX_MESH_VIEWS_NV              0x9557\n#define GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_NV 0x92DF\n#define GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_NV 0x9543\n#define GL_MAX_MESH_WORK_GROUP_SIZE_NV    0x953B\n#define GL_MAX_TASK_WORK_GROUP_SIZE_NV    0x953C\n#define GL_MESH_WORK_GROUP_SIZE_NV        0x953E\n#define GL_TASK_WORK_GROUP_SIZE_NV        0x953F\n#define GL_MESH_VERTICES_OUT_NV           0x9579\n#define GL_MESH_PRIMITIVES_OUT_NV         0x957A\n#define GL_MESH_OUTPUT_TYPE_NV            0x957B\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_NV 0x959C\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_NV 0x959D\n#define GL_REFERENCED_BY_MESH_SHADER_NV   0x95A0\n#define GL_REFERENCED_BY_TASK_SHADER_NV   0x95A1\n#define GL_MESH_SHADER_BIT_NV             0x00000040\n#define GL_TASK_SHADER_BIT_NV             0x00000080\n#define GL_MESH_SUBROUTINE_NV             0x957C\n#define GL_TASK_SUBROUTINE_NV             0x957D\n#define GL_MESH_SUBROUTINE_UNIFORM_NV     0x957E\n#define GL_TASK_SUBROUTINE_UNIFORM_NV     0x957F\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_NV 0x959E\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_NV 0x959F\ntypedef void (APIENTRYP PFNGLDRAWMESHTASKSNVPROC) (GLuint first, GLuint count);\ntypedef void (APIENTRYP PFNGLDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect);\ntypedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect, GLsizei drawcount, GLsizei stride);\ntypedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC) (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);\n#endif /* GL_NV_mesh_shader */\n#ifndef GL_NV_path_rendering\n#define GL_NV_path_rendering 1\n#define GL_PATH_FORMAT_SVG_NV             0x9070\n#define GL_PATH_FORMAT_PS_NV              0x9071\n#define GL_STANDARD_FONT_NAME_NV          0x9072\n#define GL_SYSTEM_FONT_NAME_NV            0x9073\n#define GL_FILE_NAME_NV                   0x9074\n#define GL_PATH_STROKE_WIDTH_NV           0x9075\n#define GL_PATH_END_CAPS_NV               0x9076\n#define GL_PATH_INITIAL_END_CAP_NV        0x9077\n#define GL_PATH_TERMINAL_END_CAP_NV       0x9078\n#define GL_PATH_JOIN_STYLE_NV             0x9079\n#define GL_PATH_MITER_LIMIT_NV            0x907A\n#define GL_PATH_DASH_CAPS_NV              0x907B\n#define GL_PATH_INITIAL_DASH_CAP_NV       0x907C\n#define GL_PATH_TERMINAL_DASH_CAP_NV      0x907D\n#define GL_PATH_DASH_OFFSET_NV            0x907E\n#define GL_PATH_CLIENT_LENGTH_NV          0x907F\n#define GL_PATH_FILL_MODE_NV              0x9080\n#define GL_PATH_FILL_MASK_NV              0x9081\n#define GL_PATH_FILL_COVER_MODE_NV        0x9082\n#define GL_PATH_STROKE_COVER_MODE_NV      0x9083\n#define GL_PATH_STROKE_MASK_NV            0x9084\n#define GL_COUNT_UP_NV                    0x9088\n#define GL_COUNT_DOWN_NV                  0x9089\n#define GL_PATH_OBJECT_BOUNDING_BOX_NV    0x908A\n#define GL_CONVEX_HULL_NV                 0x908B\n#define GL_BOUNDING_BOX_NV                0x908D\n#define GL_TRANSLATE_X_NV                 0x908E\n#define GL_TRANSLATE_Y_NV                 0x908F\n#define GL_TRANSLATE_2D_NV                0x9090\n#define GL_TRANSLATE_3D_NV                0x9091\n#define GL_AFFINE_2D_NV                   0x9092\n#define GL_AFFINE_3D_NV                   0x9094\n#define GL_TRANSPOSE_AFFINE_2D_NV         0x9096\n#define GL_TRANSPOSE_AFFINE_3D_NV         0x9098\n#define GL_UTF8_NV                        0x909A\n#define GL_UTF16_NV                       0x909B\n#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C\n#define GL_PATH_COMMAND_COUNT_NV          0x909D\n#define GL_PATH_COORD_COUNT_NV            0x909E\n#define GL_PATH_DASH_ARRAY_COUNT_NV       0x909F\n#define GL_PATH_COMPUTED_LENGTH_NV        0x90A0\n#define GL_PATH_FILL_BOUNDING_BOX_NV      0x90A1\n#define GL_PATH_STROKE_BOUNDING_BOX_NV    0x90A2\n#define GL_SQUARE_NV                      0x90A3\n#define GL_ROUND_NV                       0x90A4\n#define GL_TRIANGULAR_NV                  0x90A5\n#define GL_BEVEL_NV                       0x90A6\n#define GL_MITER_REVERT_NV                0x90A7\n#define GL_MITER_TRUNCATE_NV              0x90A8\n#define GL_SKIP_MISSING_GLYPH_NV          0x90A9\n#define GL_USE_MISSING_GLYPH_NV           0x90AA\n#define GL_PATH_ERROR_POSITION_NV         0x90AB\n#define GL_ACCUM_ADJACENT_PAIRS_NV        0x90AD\n#define GL_ADJACENT_PAIRS_NV              0x90AE\n#define GL_FIRST_TO_REST_NV               0x90AF\n#define GL_PATH_GEN_MODE_NV               0x90B0\n#define GL_PATH_GEN_COEFF_NV              0x90B1\n#define GL_PATH_GEN_COMPONENTS_NV         0x90B3\n#define GL_PATH_STENCIL_FUNC_NV           0x90B7\n#define GL_PATH_STENCIL_REF_NV            0x90B8\n#define GL_PATH_STENCIL_VALUE_MASK_NV     0x90B9\n#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD\n#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE\n#define GL_PATH_COVER_DEPTH_FUNC_NV       0x90BF\n#define GL_PATH_DASH_OFFSET_RESET_NV      0x90B4\n#define GL_MOVE_TO_RESETS_NV              0x90B5\n#define GL_MOVE_TO_CONTINUES_NV           0x90B6\n#define GL_CLOSE_PATH_NV                  0x00\n#define GL_MOVE_TO_NV                     0x02\n#define GL_RELATIVE_MOVE_TO_NV            0x03\n#define GL_LINE_TO_NV                     0x04\n#define GL_RELATIVE_LINE_TO_NV            0x05\n#define GL_HORIZONTAL_LINE_TO_NV          0x06\n#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07\n#define GL_VERTICAL_LINE_TO_NV            0x08\n#define GL_RELATIVE_VERTICAL_LINE_TO_NV   0x09\n#define GL_QUADRATIC_CURVE_TO_NV          0x0A\n#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B\n#define GL_CUBIC_CURVE_TO_NV              0x0C\n#define GL_RELATIVE_CUBIC_CURVE_TO_NV     0x0D\n#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV   0x0E\n#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F\n#define GL_SMOOTH_CUBIC_CURVE_TO_NV       0x10\n#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11\n#define GL_SMALL_CCW_ARC_TO_NV            0x12\n#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV   0x13\n#define GL_SMALL_CW_ARC_TO_NV             0x14\n#define GL_RELATIVE_SMALL_CW_ARC_TO_NV    0x15\n#define GL_LARGE_CCW_ARC_TO_NV            0x16\n#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV   0x17\n#define GL_LARGE_CW_ARC_TO_NV             0x18\n#define GL_RELATIVE_LARGE_CW_ARC_TO_NV    0x19\n#define GL_RESTART_PATH_NV                0xF0\n#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV    0xF2\n#define GL_DUP_LAST_CUBIC_CURVE_TO_NV     0xF4\n#define GL_RECT_NV                        0xF6\n#define GL_CIRCULAR_CCW_ARC_TO_NV         0xF8\n#define GL_CIRCULAR_CW_ARC_TO_NV          0xFA\n#define GL_CIRCULAR_TANGENT_ARC_TO_NV     0xFC\n#define GL_ARC_TO_NV                      0xFE\n#define GL_RELATIVE_ARC_TO_NV             0xFF\n#define GL_BOLD_BIT_NV                    0x01\n#define GL_ITALIC_BIT_NV                  0x02\n#define GL_GLYPH_WIDTH_BIT_NV             0x01\n#define GL_GLYPH_HEIGHT_BIT_NV            0x02\n#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04\n#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08\n#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10\n#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20\n#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40\n#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80\n#define GL_GLYPH_HAS_KERNING_BIT_NV       0x100\n#define GL_FONT_X_MIN_BOUNDS_BIT_NV       0x00010000\n#define GL_FONT_Y_MIN_BOUNDS_BIT_NV       0x00020000\n#define GL_FONT_X_MAX_BOUNDS_BIT_NV       0x00040000\n#define GL_FONT_Y_MAX_BOUNDS_BIT_NV       0x00080000\n#define GL_FONT_UNITS_PER_EM_BIT_NV       0x00100000\n#define GL_FONT_ASCENDER_BIT_NV           0x00200000\n#define GL_FONT_DESCENDER_BIT_NV          0x00400000\n#define GL_FONT_HEIGHT_BIT_NV             0x00800000\n#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV  0x01000000\n#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000\n#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000\n#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000\n#define GL_FONT_HAS_KERNING_BIT_NV        0x10000000\n#define GL_ROUNDED_RECT_NV                0xE8\n#define GL_RELATIVE_ROUNDED_RECT_NV       0xE9\n#define GL_ROUNDED_RECT2_NV               0xEA\n#define GL_RELATIVE_ROUNDED_RECT2_NV      0xEB\n#define GL_ROUNDED_RECT4_NV               0xEC\n#define GL_RELATIVE_ROUNDED_RECT4_NV      0xED\n#define GL_ROUNDED_RECT8_NV               0xEE\n#define GL_RELATIVE_ROUNDED_RECT8_NV      0xEF\n#define GL_RELATIVE_RECT_NV               0xF7\n#define GL_FONT_GLYPHS_AVAILABLE_NV       0x9368\n#define GL_FONT_TARGET_UNAVAILABLE_NV     0x9369\n#define GL_FONT_UNAVAILABLE_NV            0x936A\n#define GL_FONT_UNINTELLIGIBLE_NV         0x936B\n#define GL_CONIC_CURVE_TO_NV              0x1A\n#define GL_RELATIVE_CONIC_CURVE_TO_NV     0x1B\n#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV  0x20000000\n#define GL_STANDARD_FONT_FORMAT_NV        0x936C\n#define GL_PATH_PROJECTION_NV             0x1701\n#define GL_PATH_MODELVIEW_NV              0x1700\n#define GL_PATH_MODELVIEW_STACK_DEPTH_NV  0x0BA3\n#define GL_PATH_MODELVIEW_MATRIX_NV       0x0BA6\n#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36\n#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3\n#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4\n#define GL_PATH_PROJECTION_MATRIX_NV      0x0BA7\n#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38\n#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4\n#define GL_FRAGMENT_INPUT_NV              0x936D\ntypedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range);\ntypedef void (APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range);\ntypedef GLboolean (APIENTRYP PFNGLISPATHNVPROC) (GLuint path);\ntypedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords);\ntypedef void (APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords);\ntypedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords);\ntypedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords);\ntypedef void (APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString);\ntypedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale);\ntypedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale);\ntypedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights);\ntypedef void (APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath);\ntypedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight);\ntypedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues);\ntypedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value);\ntypedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value);\ntypedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value);\ntypedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value);\ntypedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray);\ntypedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask);\ntypedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units);\ntypedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask);\ntypedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask);\ntypedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues);\ntypedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues);\ntypedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func);\ntypedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode);\ntypedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode);\ntypedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);\ntypedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);\ntypedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value);\ntypedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value);\ntypedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands);\ntypedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords);\ntypedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray);\ntypedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics);\ntypedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics);\ntypedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing);\ntypedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y);\ntypedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y);\ntypedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments);\ntypedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY);\ntypedef void (APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m);\ntypedef void (APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m);\ntypedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m);\ntypedef void (APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m);\ntypedef void (APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m);\ntypedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m);\ntypedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode);\ntypedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode);\ntypedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);\ntypedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);\ntypedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount);\ntypedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale);\ntypedef GLenum (APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale);\ntypedef void (APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs);\ntypedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLfloat *params);\n#endif /* GL_NV_path_rendering */\n#ifndef GL_NV_path_rendering_shared_edge\n#define GL_NV_path_rendering_shared_edge 1\n#define GL_SHARED_EDGE_NV                 0xC0\n#endif /* GL_NV_path_rendering_shared_edge */\n#ifndef GL_NV_primitive_shading_rate\n#define GL_NV_primitive_shading_rate 1\n#define GL_SHADING_RATE_IMAGE_PER_PRIMITIVE_NV 0x95B1\n#define GL_SHADING_RATE_IMAGE_PALETTE_COUNT_NV 0x95B2\n#endif /* GL_NV_primitive_shading_rate */\n#ifndef GL_NV_representative_fragment_test\n#define GL_NV_representative_fragment_test 1\n#define GL_REPRESENTATIVE_FRAGMENT_TEST_NV 0x937F\n#endif /* GL_NV_representative_fragment_test */\n#ifndef GL_NV_sample_locations\n#define GL_NV_sample_locations 1\n#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV 0x933D\n#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV 0x933E\n#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV 0x933F\n#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV 0x9340\n#define GL_SAMPLE_LOCATION_NV             0x8E50\n#define GL_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9341\n#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV 0x9342\n#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV 0x9343\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLRESOLVEDEPTHVALUESNVPROC) (void);\n#endif /* GL_NV_sample_locations */\n#ifndef GL_NV_sample_mask_override_coverage\n#define GL_NV_sample_mask_override_coverage 1\n#endif /* GL_NV_sample_mask_override_coverage */\n#ifndef GL_NV_scissor_exclusive\n#define GL_NV_scissor_exclusive 1\n#define GL_SCISSOR_TEST_EXCLUSIVE_NV      0x9555\n#define GL_SCISSOR_BOX_EXCLUSIVE_NV       0x9556\ntypedef void (APIENTRYP PFNGLSCISSOREXCLUSIVENVPROC) (GLint x, GLint y, GLsizei width, GLsizei height);\ntypedef void (APIENTRYP PFNGLSCISSOREXCLUSIVEARRAYVNVPROC) (GLuint first, GLsizei count, const GLint *v);\n#endif /* GL_NV_scissor_exclusive */\n#ifndef GL_NV_shader_atomic_counters\n#define GL_NV_shader_atomic_counters 1\n#endif /* GL_NV_shader_atomic_counters */\n#ifndef GL_NV_shader_atomic_float\n#define GL_NV_shader_atomic_float 1\n#endif /* GL_NV_shader_atomic_float */\n#ifndef GL_NV_shader_atomic_float64\n#define GL_NV_shader_atomic_float64 1\n#endif /* GL_NV_shader_atomic_float64 */\n#ifndef GL_NV_shader_atomic_fp16_vector\n#define GL_NV_shader_atomic_fp16_vector 1\n#endif /* GL_NV_shader_atomic_fp16_vector */\n#ifndef GL_NV_shader_atomic_int64\n#define GL_NV_shader_atomic_int64 1\n#endif /* GL_NV_shader_atomic_int64 */\n#ifndef GL_NV_shader_buffer_load\n#define GL_NV_shader_buffer_load 1\n#define GL_BUFFER_GPU_ADDRESS_NV          0x8F1D\n#define GL_GPU_ADDRESS_NV                 0x8F34\n#define GL_MAX_SHADER_BUFFER_ADDRESS_NV   0x8F35\ntypedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC) (GLenum target, GLenum access);\ntypedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC) (GLenum target);\ntypedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC) (GLenum target);\ntypedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC) (GLuint buffer, GLenum access);\ntypedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC) (GLuint buffer);\ntypedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC) (GLuint buffer);\ntypedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC) (GLenum target, GLenum pname, GLuint64EXT *params);\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC) (GLuint buffer, GLenum pname, GLuint64EXT *params);\ntypedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC) (GLenum value, GLuint64EXT *result);\ntypedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC) (GLint location, GLuint64EXT value);\ntypedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value);\ntypedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLuint64EXT *params);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC) (GLuint program, GLint location, GLuint64EXT value);\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value);\n#endif /* GL_NV_shader_buffer_load */\n#ifndef GL_NV_shader_buffer_store\n#define GL_NV_shader_buffer_store 1\n#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010\n#endif /* GL_NV_shader_buffer_store */\n#ifndef GL_NV_shader_subgroup_partitioned\n#define GL_NV_shader_subgroup_partitioned 1\n#define GL_SUBGROUP_FEATURE_PARTITIONED_BIT_NV 0x00000100\n#endif /* GL_NV_shader_subgroup_partitioned */\n#ifndef GL_NV_shader_texture_footprint\n#define GL_NV_shader_texture_footprint 1\n#endif /* GL_NV_shader_texture_footprint */\n#ifndef GL_NV_shader_thread_group\n#define GL_NV_shader_thread_group 1\n#define GL_WARP_SIZE_NV                   0x9339\n#define GL_WARPS_PER_SM_NV                0x933A\n#define GL_SM_COUNT_NV                    0x933B\n#endif /* GL_NV_shader_thread_group */\n#ifndef GL_NV_shader_thread_shuffle\n#define GL_NV_shader_thread_shuffle 1\n#endif /* GL_NV_shader_thread_shuffle */\n#ifndef GL_NV_shading_rate_image\n#define GL_NV_shading_rate_image 1\n#define GL_SHADING_RATE_IMAGE_NV          0x9563\n#define GL_SHADING_RATE_NO_INVOCATIONS_NV 0x9564\n#define GL_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV 0x9565\n#define GL_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV 0x9566\n#define GL_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV 0x9567\n#define GL_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV 0x9568\n#define GL_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV 0x9569\n#define GL_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV 0x956A\n#define GL_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV 0x956B\n#define GL_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV 0x956C\n#define GL_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV 0x956D\n#define GL_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV 0x956E\n#define GL_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV 0x956F\n#define GL_SHADING_RATE_IMAGE_BINDING_NV  0x955B\n#define GL_SHADING_RATE_IMAGE_TEXEL_WIDTH_NV 0x955C\n#define GL_SHADING_RATE_IMAGE_TEXEL_HEIGHT_NV 0x955D\n#define GL_SHADING_RATE_IMAGE_PALETTE_SIZE_NV 0x955E\n#define GL_MAX_COARSE_FRAGMENT_SAMPLES_NV 0x955F\n#define GL_SHADING_RATE_SAMPLE_ORDER_DEFAULT_NV 0x95AE\n#define GL_SHADING_RATE_SAMPLE_ORDER_PIXEL_MAJOR_NV 0x95AF\n#define GL_SHADING_RATE_SAMPLE_ORDER_SAMPLE_MAJOR_NV 0x95B0\ntypedef void (APIENTRYP PFNGLBINDSHADINGRATEIMAGENVPROC) (GLuint texture);\ntypedef void (APIENTRYP PFNGLGETSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint entry, GLenum *rate);\ntypedef void (APIENTRYP PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC) (GLenum rate, GLuint samples, GLuint index, GLint *location);\ntypedef void (APIENTRYP PFNGLSHADINGRATEIMAGEBARRIERNVPROC) (GLboolean synchronize);\ntypedef void (APIENTRYP PFNGLSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates);\ntypedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERNVPROC) (GLenum order);\ntypedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC) (GLenum rate, GLuint samples, const GLint *locations);\n#endif /* GL_NV_shading_rate_image */\n#ifndef GL_NV_stereo_view_rendering\n#define GL_NV_stereo_view_rendering 1\n#endif /* GL_NV_stereo_view_rendering */\n#ifndef GL_NV_texture_barrier\n#define GL_NV_texture_barrier 1\ntypedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC) (void);\n#endif /* GL_NV_texture_barrier */\n#ifndef GL_NV_texture_rectangle_compressed\n#define GL_NV_texture_rectangle_compressed 1\n#endif /* GL_NV_texture_rectangle_compressed */\n#ifndef GL_NV_uniform_buffer_unified_memory\n#define GL_NV_uniform_buffer_unified_memory 1\n#define GL_UNIFORM_BUFFER_UNIFIED_NV      0x936E\n#define GL_UNIFORM_BUFFER_ADDRESS_NV      0x936F\n#define GL_UNIFORM_BUFFER_LENGTH_NV       0x9370\n#endif /* GL_NV_uniform_buffer_unified_memory */\n#ifndef GL_NV_vertex_attrib_integer_64bit\n#define GL_NV_vertex_attrib_integer_64bit 1\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC) (GLuint index, GLint64EXT x);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC) (GLuint index, const GLint64EXT *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC) (GLuint index, const GLint64EXT *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC) (GLuint index, const GLint64EXT *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC) (GLuint index, const GLint64EXT *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC) (GLuint index, GLuint64EXT x);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC) (GLuint index, const GLuint64EXT *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC) (GLuint index, const GLuint64EXT *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC) (GLuint index, const GLuint64EXT *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC) (GLuint index, const GLuint64EXT *v);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC) (GLuint index, GLenum pname, GLint64EXT *params);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC) (GLuint index, GLenum pname, GLuint64EXT *params);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride);\n#endif /* GL_NV_vertex_attrib_integer_64bit */\n#ifndef GL_NV_vertex_buffer_unified_memory\n#define GL_NV_vertex_buffer_unified_memory 1\n#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E\n#define GL_ELEMENT_ARRAY_UNIFIED_NV       0x8F1F\n#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20\n#define GL_VERTEX_ARRAY_ADDRESS_NV        0x8F21\n#define GL_NORMAL_ARRAY_ADDRESS_NV        0x8F22\n#define GL_COLOR_ARRAY_ADDRESS_NV         0x8F23\n#define GL_INDEX_ARRAY_ADDRESS_NV         0x8F24\n#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25\n#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV     0x8F26\n#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27\n#define GL_FOG_COORD_ARRAY_ADDRESS_NV     0x8F28\n#define GL_ELEMENT_ARRAY_ADDRESS_NV       0x8F29\n#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV  0x8F2A\n#define GL_VERTEX_ARRAY_LENGTH_NV         0x8F2B\n#define GL_NORMAL_ARRAY_LENGTH_NV         0x8F2C\n#define GL_COLOR_ARRAY_LENGTH_NV          0x8F2D\n#define GL_INDEX_ARRAY_LENGTH_NV          0x8F2E\n#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV  0x8F2F\n#define GL_EDGE_FLAG_ARRAY_LENGTH_NV      0x8F30\n#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31\n#define GL_FOG_COORD_ARRAY_LENGTH_NV      0x8F32\n#define GL_ELEMENT_ARRAY_LENGTH_NV        0x8F33\n#define GL_DRAW_INDIRECT_UNIFIED_NV       0x8F40\n#define GL_DRAW_INDIRECT_ADDRESS_NV       0x8F41\n#define GL_DRAW_INDIRECT_LENGTH_NV        0x8F42\ntypedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC) (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length);\ntypedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC) (GLint size, GLenum type, GLsizei stride);\ntypedef void (APIENTRYP PFNGLNORMALFORMATNVPROC) (GLenum type, GLsizei stride);\ntypedef void (APIENTRYP PFNGLCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride);\ntypedef void (APIENTRYP PFNGLINDEXFORMATNVPROC) (GLenum type, GLsizei stride);\ntypedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC) (GLint size, GLenum type, GLsizei stride);\ntypedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC) (GLsizei stride);\ntypedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride);\ntypedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC) (GLenum type, GLsizei stride);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride);\ntypedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result);\n#endif /* GL_NV_vertex_buffer_unified_memory */\n#ifndef GL_NV_viewport_array2\n#define GL_NV_viewport_array2 1\n#endif /* GL_NV_viewport_array2 */\n#ifndef GL_NV_viewport_swizzle\n#define GL_NV_viewport_swizzle 1\n#define GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV 0x9350\n#define GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV 0x9351\n#define GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV 0x9352\n#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV 0x9353\n#define GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV 0x9354\n#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV 0x9355\n#define GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV 0x9356\n#define GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV 0x9357\n#define GL_VIEWPORT_SWIZZLE_X_NV          0x9358\n#define GL_VIEWPORT_SWIZZLE_Y_NV          0x9359\n#define GL_VIEWPORT_SWIZZLE_Z_NV          0x935A\n#define GL_VIEWPORT_SWIZZLE_W_NV          0x935B\ntypedef void (APIENTRYP PFNGLVIEWPORTSWIZZLENVPROC) (GLuint index, GLenum swizzlex, GLenum swizzley, GLenum swizzlez, GLenum swizzlew);\n#endif /* GL_NV_viewport_swizzle */\n#ifndef GL_OVR_multiview\n#define GL_OVR_multiview 1\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632\n#define GL_MAX_VIEWS_OVR                  0x9631\n#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews);\n#endif /* GL_OVR_multiview */\n#ifndef GL_OVR_multiview2\n#define GL_OVR_multiview2 1\n#endif /* GL_OVR_multiview2 */\n#ifdef __cplusplus\n}\n#endif\n#endif\n\n#ifndef GL3W_API\n#ifdef _MSC_VER\n#if EXPORT_SYMBOLS == 1\n#define GL3W_API __declspec(dllexport)\n#else\n#define GL3W_API __declspec(dllimport)\n#endif\n#else\n#define GL3W_API\n#endif\n#endif\n\n#ifndef __gl_h_\n#define __gl_h_\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define GL3W_OK 0\n#define GL3W_ERROR_INIT -1\n#define GL3W_ERROR_LIBRARY_OPEN -2\n#define GL3W_ERROR_OPENGL_VERSION -3\n\ntypedef void (*GL3WglProc)(void);\ntypedef GL3WglProc (*GL3WGetProcAddressProc)(const char *proc);\n\n/* gl3w api */\nGL3W_API int imgl3wInit(void);\nGL3W_API int imgl3wInit2(GL3WGetProcAddressProc proc);\nGL3W_API void imgl3wShutdown(void);\nGL3W_API int imgl3wIsSupported(int major, int minor);\nGL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc);\n\n/* gl3w internal state */\nunion GL3WProcs {\n    GL3WglProc ptr[659];\n    struct {\n        PFNGLACTIVESHADERPROGRAMPROC                         ActiveShaderProgram;\n        PFNGLACTIVETEXTUREPROC                               ActiveTexture;\n        PFNGLATTACHSHADERPROC                                AttachShader;\n        PFNGLBEGINCONDITIONALRENDERPROC                      BeginConditionalRender;\n        PFNGLBEGINQUERYPROC                                  BeginQuery;\n        PFNGLBEGINQUERYINDEXEDPROC                           BeginQueryIndexed;\n        PFNGLBEGINTRANSFORMFEEDBACKPROC                      BeginTransformFeedback;\n        PFNGLBINDATTRIBLOCATIONPROC                          BindAttribLocation;\n        PFNGLBINDBUFFERPROC                                  BindBuffer;\n        PFNGLBINDBUFFERBASEPROC                              BindBufferBase;\n        PFNGLBINDBUFFERRANGEPROC                             BindBufferRange;\n        PFNGLBINDBUFFERSBASEPROC                             BindBuffersBase;\n        PFNGLBINDBUFFERSRANGEPROC                            BindBuffersRange;\n        PFNGLBINDFRAGDATALOCATIONPROC                        BindFragDataLocation;\n        PFNGLBINDFRAGDATALOCATIONINDEXEDPROC                 BindFragDataLocationIndexed;\n        PFNGLBINDFRAMEBUFFERPROC                             BindFramebuffer;\n        PFNGLBINDIMAGETEXTUREPROC                            BindImageTexture;\n        PFNGLBINDIMAGETEXTURESPROC                           BindImageTextures;\n        PFNGLBINDPROGRAMPIPELINEPROC                         BindProgramPipeline;\n        PFNGLBINDRENDERBUFFERPROC                            BindRenderbuffer;\n        PFNGLBINDSAMPLERPROC                                 BindSampler;\n        PFNGLBINDSAMPLERSPROC                                BindSamplers;\n        PFNGLBINDTEXTUREPROC                                 BindTexture;\n        PFNGLBINDTEXTUREUNITPROC                             BindTextureUnit;\n        PFNGLBINDTEXTURESPROC                                BindTextures;\n        PFNGLBINDTRANSFORMFEEDBACKPROC                       BindTransformFeedback;\n        PFNGLBINDVERTEXARRAYPROC                             BindVertexArray;\n        PFNGLBINDVERTEXBUFFERPROC                            BindVertexBuffer;\n        PFNGLBINDVERTEXBUFFERSPROC                           BindVertexBuffers;\n        PFNGLBLENDCOLORPROC                                  BlendColor;\n        PFNGLBLENDEQUATIONPROC                               BlendEquation;\n        PFNGLBLENDEQUATIONSEPARATEPROC                       BlendEquationSeparate;\n        PFNGLBLENDEQUATIONSEPARATEIPROC                      BlendEquationSeparatei;\n        PFNGLBLENDEQUATIONIPROC                              BlendEquationi;\n        PFNGLBLENDFUNCPROC                                   BlendFunc;\n        PFNGLBLENDFUNCSEPARATEPROC                           BlendFuncSeparate;\n        PFNGLBLENDFUNCSEPARATEIPROC                          BlendFuncSeparatei;\n        PFNGLBLENDFUNCIPROC                                  BlendFunci;\n        PFNGLBLITFRAMEBUFFERPROC                             BlitFramebuffer;\n        PFNGLBLITNAMEDFRAMEBUFFERPROC                        BlitNamedFramebuffer;\n        PFNGLBUFFERDATAPROC                                  BufferData;\n        PFNGLBUFFERSTORAGEPROC                               BufferStorage;\n        PFNGLBUFFERSUBDATAPROC                               BufferSubData;\n        PFNGLCHECKFRAMEBUFFERSTATUSPROC                      CheckFramebufferStatus;\n        PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC                 CheckNamedFramebufferStatus;\n        PFNGLCLAMPCOLORPROC                                  ClampColor;\n        PFNGLCLEARPROC                                       Clear;\n        PFNGLCLEARBUFFERDATAPROC                             ClearBufferData;\n        PFNGLCLEARBUFFERSUBDATAPROC                          ClearBufferSubData;\n        PFNGLCLEARBUFFERFIPROC                               ClearBufferfi;\n        PFNGLCLEARBUFFERFVPROC                               ClearBufferfv;\n        PFNGLCLEARBUFFERIVPROC                               ClearBufferiv;\n        PFNGLCLEARBUFFERUIVPROC                              ClearBufferuiv;\n        PFNGLCLEARCOLORPROC                                  ClearColor;\n        PFNGLCLEARDEPTHPROC                                  ClearDepth;\n        PFNGLCLEARDEPTHFPROC                                 ClearDepthf;\n        PFNGLCLEARNAMEDBUFFERDATAPROC                        ClearNamedBufferData;\n        PFNGLCLEARNAMEDBUFFERSUBDATAPROC                     ClearNamedBufferSubData;\n        PFNGLCLEARNAMEDFRAMEBUFFERFIPROC                     ClearNamedFramebufferfi;\n        PFNGLCLEARNAMEDFRAMEBUFFERFVPROC                     ClearNamedFramebufferfv;\n        PFNGLCLEARNAMEDFRAMEBUFFERIVPROC                     ClearNamedFramebufferiv;\n        PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC                    ClearNamedFramebufferuiv;\n        PFNGLCLEARSTENCILPROC                                ClearStencil;\n        PFNGLCLEARTEXIMAGEPROC                               ClearTexImage;\n        PFNGLCLEARTEXSUBIMAGEPROC                            ClearTexSubImage;\n        PFNGLCLIENTWAITSYNCPROC                              ClientWaitSync;\n        PFNGLCLIPCONTROLPROC                                 ClipControl;\n        PFNGLCOLORMASKPROC                                   ColorMask;\n        PFNGLCOLORMASKIPROC                                  ColorMaski;\n        PFNGLCOMPILESHADERPROC                               CompileShader;\n        PFNGLCOMPRESSEDTEXIMAGE1DPROC                        CompressedTexImage1D;\n        PFNGLCOMPRESSEDTEXIMAGE2DPROC                        CompressedTexImage2D;\n        PFNGLCOMPRESSEDTEXIMAGE3DPROC                        CompressedTexImage3D;\n        PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC                     CompressedTexSubImage1D;\n        PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC                     CompressedTexSubImage2D;\n        PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC                     CompressedTexSubImage3D;\n        PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC                 CompressedTextureSubImage1D;\n        PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC                 CompressedTextureSubImage2D;\n        PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC                 CompressedTextureSubImage3D;\n        PFNGLCOPYBUFFERSUBDATAPROC                           CopyBufferSubData;\n        PFNGLCOPYIMAGESUBDATAPROC                            CopyImageSubData;\n        PFNGLCOPYNAMEDBUFFERSUBDATAPROC                      CopyNamedBufferSubData;\n        PFNGLCOPYTEXIMAGE1DPROC                              CopyTexImage1D;\n        PFNGLCOPYTEXIMAGE2DPROC                              CopyTexImage2D;\n        PFNGLCOPYTEXSUBIMAGE1DPROC                           CopyTexSubImage1D;\n        PFNGLCOPYTEXSUBIMAGE2DPROC                           CopyTexSubImage2D;\n        PFNGLCOPYTEXSUBIMAGE3DPROC                           CopyTexSubImage3D;\n        PFNGLCOPYTEXTURESUBIMAGE1DPROC                       CopyTextureSubImage1D;\n        PFNGLCOPYTEXTURESUBIMAGE2DPROC                       CopyTextureSubImage2D;\n        PFNGLCOPYTEXTURESUBIMAGE3DPROC                       CopyTextureSubImage3D;\n        PFNGLCREATEBUFFERSPROC                               CreateBuffers;\n        PFNGLCREATEFRAMEBUFFERSPROC                          CreateFramebuffers;\n        PFNGLCREATEPROGRAMPROC                               CreateProgram;\n        PFNGLCREATEPROGRAMPIPELINESPROC                      CreateProgramPipelines;\n        PFNGLCREATEQUERIESPROC                               CreateQueries;\n        PFNGLCREATERENDERBUFFERSPROC                         CreateRenderbuffers;\n        PFNGLCREATESAMPLERSPROC                              CreateSamplers;\n        PFNGLCREATESHADERPROC                                CreateShader;\n        PFNGLCREATESHADERPROGRAMVPROC                        CreateShaderProgramv;\n        PFNGLCREATETEXTURESPROC                              CreateTextures;\n        PFNGLCREATETRANSFORMFEEDBACKSPROC                    CreateTransformFeedbacks;\n        PFNGLCREATEVERTEXARRAYSPROC                          CreateVertexArrays;\n        PFNGLCULLFACEPROC                                    CullFace;\n        PFNGLDEBUGMESSAGECALLBACKPROC                        DebugMessageCallback;\n        PFNGLDEBUGMESSAGECONTROLPROC                         DebugMessageControl;\n        PFNGLDEBUGMESSAGEINSERTPROC                          DebugMessageInsert;\n        PFNGLDELETEBUFFERSPROC                               DeleteBuffers;\n        PFNGLDELETEFRAMEBUFFERSPROC                          DeleteFramebuffers;\n        PFNGLDELETEPROGRAMPROC                               DeleteProgram;\n        PFNGLDELETEPROGRAMPIPELINESPROC                      DeleteProgramPipelines;\n        PFNGLDELETEQUERIESPROC                               DeleteQueries;\n        PFNGLDELETERENDERBUFFERSPROC                         DeleteRenderbuffers;\n        PFNGLDELETESAMPLERSPROC                              DeleteSamplers;\n        PFNGLDELETESHADERPROC                                DeleteShader;\n        PFNGLDELETESYNCPROC                                  DeleteSync;\n        PFNGLDELETETEXTURESPROC                              DeleteTextures;\n        PFNGLDELETETRANSFORMFEEDBACKSPROC                    DeleteTransformFeedbacks;\n        PFNGLDELETEVERTEXARRAYSPROC                          DeleteVertexArrays;\n        PFNGLDEPTHFUNCPROC                                   DepthFunc;\n        PFNGLDEPTHMASKPROC                                   DepthMask;\n        PFNGLDEPTHRANGEPROC                                  DepthRange;\n        PFNGLDEPTHRANGEARRAYVPROC                            DepthRangeArrayv;\n        PFNGLDEPTHRANGEINDEXEDPROC                           DepthRangeIndexed;\n        PFNGLDEPTHRANGEFPROC                                 DepthRangef;\n        PFNGLDETACHSHADERPROC                                DetachShader;\n        PFNGLDISABLEPROC                                     Disable;\n        PFNGLDISABLEVERTEXARRAYATTRIBPROC                    DisableVertexArrayAttrib;\n        PFNGLDISABLEVERTEXATTRIBARRAYPROC                    DisableVertexAttribArray;\n        PFNGLDISABLEIPROC                                    Disablei;\n        PFNGLDISPATCHCOMPUTEPROC                             DispatchCompute;\n        PFNGLDISPATCHCOMPUTEINDIRECTPROC                     DispatchComputeIndirect;\n        PFNGLDRAWARRAYSPROC                                  DrawArrays;\n        PFNGLDRAWARRAYSINDIRECTPROC                          DrawArraysIndirect;\n        PFNGLDRAWARRAYSINSTANCEDPROC                         DrawArraysInstanced;\n        PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC             DrawArraysInstancedBaseInstance;\n        PFNGLDRAWBUFFERPROC                                  DrawBuffer;\n        PFNGLDRAWBUFFERSPROC                                 DrawBuffers;\n        PFNGLDRAWELEMENTSPROC                                DrawElements;\n        PFNGLDRAWELEMENTSBASEVERTEXPROC                      DrawElementsBaseVertex;\n        PFNGLDRAWELEMENTSINDIRECTPROC                        DrawElementsIndirect;\n        PFNGLDRAWELEMENTSINSTANCEDPROC                       DrawElementsInstanced;\n        PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC           DrawElementsInstancedBaseInstance;\n        PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC             DrawElementsInstancedBaseVertex;\n        PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC DrawElementsInstancedBaseVertexBaseInstance;\n        PFNGLDRAWRANGEELEMENTSPROC                           DrawRangeElements;\n        PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC                 DrawRangeElementsBaseVertex;\n        PFNGLDRAWTRANSFORMFEEDBACKPROC                       DrawTransformFeedback;\n        PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC              DrawTransformFeedbackInstanced;\n        PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC                 DrawTransformFeedbackStream;\n        PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC        DrawTransformFeedbackStreamInstanced;\n        PFNGLENABLEPROC                                      Enable;\n        PFNGLENABLEVERTEXARRAYATTRIBPROC                     EnableVertexArrayAttrib;\n        PFNGLENABLEVERTEXATTRIBARRAYPROC                     EnableVertexAttribArray;\n        PFNGLENABLEIPROC                                     Enablei;\n        PFNGLENDCONDITIONALRENDERPROC                        EndConditionalRender;\n        PFNGLENDQUERYPROC                                    EndQuery;\n        PFNGLENDQUERYINDEXEDPROC                             EndQueryIndexed;\n        PFNGLENDTRANSFORMFEEDBACKPROC                        EndTransformFeedback;\n        PFNGLFENCESYNCPROC                                   FenceSync;\n        PFNGLFINISHPROC                                      Finish;\n        PFNGLFLUSHPROC                                       Flush;\n        PFNGLFLUSHMAPPEDBUFFERRANGEPROC                      FlushMappedBufferRange;\n        PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC                 FlushMappedNamedBufferRange;\n        PFNGLFRAMEBUFFERPARAMETERIPROC                       FramebufferParameteri;\n        PFNGLFRAMEBUFFERPARAMETERIMESAPROC                   FramebufferParameteriMESA;\n        PFNGLFRAMEBUFFERRENDERBUFFERPROC                     FramebufferRenderbuffer;\n        PFNGLFRAMEBUFFERTEXTUREPROC                          FramebufferTexture;\n        PFNGLFRAMEBUFFERTEXTURE1DPROC                        FramebufferTexture1D;\n        PFNGLFRAMEBUFFERTEXTURE2DPROC                        FramebufferTexture2D;\n        PFNGLFRAMEBUFFERTEXTURE3DPROC                        FramebufferTexture3D;\n        PFNGLFRAMEBUFFERTEXTURELAYERPROC                     FramebufferTextureLayer;\n        PFNGLFRONTFACEPROC                                   FrontFace;\n        PFNGLGENBUFFERSPROC                                  GenBuffers;\n        PFNGLGENFRAMEBUFFERSPROC                             GenFramebuffers;\n        PFNGLGENPROGRAMPIPELINESPROC                         GenProgramPipelines;\n        PFNGLGENQUERIESPROC                                  GenQueries;\n        PFNGLGENRENDERBUFFERSPROC                            GenRenderbuffers;\n        PFNGLGENSAMPLERSPROC                                 GenSamplers;\n        PFNGLGENTEXTURESPROC                                 GenTextures;\n        PFNGLGENTRANSFORMFEEDBACKSPROC                       GenTransformFeedbacks;\n        PFNGLGENVERTEXARRAYSPROC                             GenVertexArrays;\n        PFNGLGENERATEMIPMAPPROC                              GenerateMipmap;\n        PFNGLGENERATETEXTUREMIPMAPPROC                       GenerateTextureMipmap;\n        PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC              GetActiveAtomicCounterBufferiv;\n        PFNGLGETACTIVEATTRIBPROC                             GetActiveAttrib;\n        PFNGLGETACTIVESUBROUTINENAMEPROC                     GetActiveSubroutineName;\n        PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC              GetActiveSubroutineUniformName;\n        PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC                GetActiveSubroutineUniformiv;\n        PFNGLGETACTIVEUNIFORMPROC                            GetActiveUniform;\n        PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC                   GetActiveUniformBlockName;\n        PFNGLGETACTIVEUNIFORMBLOCKIVPROC                     GetActiveUniformBlockiv;\n        PFNGLGETACTIVEUNIFORMNAMEPROC                        GetActiveUniformName;\n        PFNGLGETACTIVEUNIFORMSIVPROC                         GetActiveUniformsiv;\n        PFNGLGETATTACHEDSHADERSPROC                          GetAttachedShaders;\n        PFNGLGETATTRIBLOCATIONPROC                           GetAttribLocation;\n        PFNGLGETBOOLEANI_VPROC                               GetBooleani_v;\n        PFNGLGETBOOLEANVPROC                                 GetBooleanv;\n        PFNGLGETBUFFERPARAMETERI64VPROC                      GetBufferParameteri64v;\n        PFNGLGETBUFFERPARAMETERIVPROC                        GetBufferParameteriv;\n        PFNGLGETBUFFERPOINTERVPROC                           GetBufferPointerv;\n        PFNGLGETBUFFERSUBDATAPROC                            GetBufferSubData;\n        PFNGLGETCOMPRESSEDTEXIMAGEPROC                       GetCompressedTexImage;\n        PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC                   GetCompressedTextureImage;\n        PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC                GetCompressedTextureSubImage;\n        PFNGLGETDEBUGMESSAGELOGPROC                          GetDebugMessageLog;\n        PFNGLGETDOUBLEI_VPROC                                GetDoublei_v;\n        PFNGLGETDOUBLEVPROC                                  GetDoublev;\n        PFNGLGETERRORPROC                                    GetError;\n        PFNGLGETFLOATI_VPROC                                 GetFloati_v;\n        PFNGLGETFLOATVPROC                                   GetFloatv;\n        PFNGLGETFRAGDATAINDEXPROC                            GetFragDataIndex;\n        PFNGLGETFRAGDATALOCATIONPROC                         GetFragDataLocation;\n        PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC         GetFramebufferAttachmentParameteriv;\n        PFNGLGETFRAMEBUFFERPARAMETERIVPROC                   GetFramebufferParameteriv;\n        PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC               GetFramebufferParameterivMESA;\n        PFNGLGETGRAPHICSRESETSTATUSPROC                      GetGraphicsResetStatus;\n        PFNGLGETINTEGER64I_VPROC                             GetInteger64i_v;\n        PFNGLGETINTEGER64VPROC                               GetInteger64v;\n        PFNGLGETINTEGERI_VPROC                               GetIntegeri_v;\n        PFNGLGETINTEGERVPROC                                 GetIntegerv;\n        PFNGLGETINTERNALFORMATI64VPROC                       GetInternalformati64v;\n        PFNGLGETINTERNALFORMATIVPROC                         GetInternalformativ;\n        PFNGLGETMULTISAMPLEFVPROC                            GetMultisamplefv;\n        PFNGLGETNAMEDBUFFERPARAMETERI64VPROC                 GetNamedBufferParameteri64v;\n        PFNGLGETNAMEDBUFFERPARAMETERIVPROC                   GetNamedBufferParameteriv;\n        PFNGLGETNAMEDBUFFERPOINTERVPROC                      GetNamedBufferPointerv;\n        PFNGLGETNAMEDBUFFERSUBDATAPROC                       GetNamedBufferSubData;\n        PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC    GetNamedFramebufferAttachmentParameteriv;\n        PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC              GetNamedFramebufferParameteriv;\n        PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC             GetNamedRenderbufferParameteriv;\n        PFNGLGETOBJECTLABELPROC                              GetObjectLabel;\n        PFNGLGETOBJECTPTRLABELPROC                           GetObjectPtrLabel;\n        PFNGLGETPOINTERVPROC                                 GetPointerv;\n        PFNGLGETPROGRAMBINARYPROC                            GetProgramBinary;\n        PFNGLGETPROGRAMINFOLOGPROC                           GetProgramInfoLog;\n        PFNGLGETPROGRAMINTERFACEIVPROC                       GetProgramInterfaceiv;\n        PFNGLGETPROGRAMPIPELINEINFOLOGPROC                   GetProgramPipelineInfoLog;\n        PFNGLGETPROGRAMPIPELINEIVPROC                        GetProgramPipelineiv;\n        PFNGLGETPROGRAMRESOURCEINDEXPROC                     GetProgramResourceIndex;\n        PFNGLGETPROGRAMRESOURCELOCATIONPROC                  GetProgramResourceLocation;\n        PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC             GetProgramResourceLocationIndex;\n        PFNGLGETPROGRAMRESOURCENAMEPROC                      GetProgramResourceName;\n        PFNGLGETPROGRAMRESOURCEIVPROC                        GetProgramResourceiv;\n        PFNGLGETPROGRAMSTAGEIVPROC                           GetProgramStageiv;\n        PFNGLGETPROGRAMIVPROC                                GetProgramiv;\n        PFNGLGETQUERYBUFFEROBJECTI64VPROC                    GetQueryBufferObjecti64v;\n        PFNGLGETQUERYBUFFEROBJECTIVPROC                      GetQueryBufferObjectiv;\n        PFNGLGETQUERYBUFFEROBJECTUI64VPROC                   GetQueryBufferObjectui64v;\n        PFNGLGETQUERYBUFFEROBJECTUIVPROC                     GetQueryBufferObjectuiv;\n        PFNGLGETQUERYINDEXEDIVPROC                           GetQueryIndexediv;\n        PFNGLGETQUERYOBJECTI64VPROC                          GetQueryObjecti64v;\n        PFNGLGETQUERYOBJECTIVPROC                            GetQueryObjectiv;\n        PFNGLGETQUERYOBJECTUI64VPROC                         GetQueryObjectui64v;\n        PFNGLGETQUERYOBJECTUIVPROC                           GetQueryObjectuiv;\n        PFNGLGETQUERYIVPROC                                  GetQueryiv;\n        PFNGLGETRENDERBUFFERPARAMETERIVPROC                  GetRenderbufferParameteriv;\n        PFNGLGETSAMPLERPARAMETERIIVPROC                      GetSamplerParameterIiv;\n        PFNGLGETSAMPLERPARAMETERIUIVPROC                     GetSamplerParameterIuiv;\n        PFNGLGETSAMPLERPARAMETERFVPROC                       GetSamplerParameterfv;\n        PFNGLGETSAMPLERPARAMETERIVPROC                       GetSamplerParameteriv;\n        PFNGLGETSHADERINFOLOGPROC                            GetShaderInfoLog;\n        PFNGLGETSHADERPRECISIONFORMATPROC                    GetShaderPrecisionFormat;\n        PFNGLGETSHADERSOURCEPROC                             GetShaderSource;\n        PFNGLGETSHADERIVPROC                                 GetShaderiv;\n        PFNGLGETSTRINGPROC                                   GetString;\n        PFNGLGETSTRINGIPROC                                  GetStringi;\n        PFNGLGETSUBROUTINEINDEXPROC                          GetSubroutineIndex;\n        PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC                GetSubroutineUniformLocation;\n        PFNGLGETSYNCIVPROC                                   GetSynciv;\n        PFNGLGETTEXIMAGEPROC                                 GetTexImage;\n        PFNGLGETTEXLEVELPARAMETERFVPROC                      GetTexLevelParameterfv;\n        PFNGLGETTEXLEVELPARAMETERIVPROC                      GetTexLevelParameteriv;\n        PFNGLGETTEXPARAMETERIIVPROC                          GetTexParameterIiv;\n        PFNGLGETTEXPARAMETERIUIVPROC                         GetTexParameterIuiv;\n        PFNGLGETTEXPARAMETERFVPROC                           GetTexParameterfv;\n        PFNGLGETTEXPARAMETERIVPROC                           GetTexParameteriv;\n        PFNGLGETTEXTUREIMAGEPROC                             GetTextureImage;\n        PFNGLGETTEXTURELEVELPARAMETERFVPROC                  GetTextureLevelParameterfv;\n        PFNGLGETTEXTURELEVELPARAMETERIVPROC                  GetTextureLevelParameteriv;\n        PFNGLGETTEXTUREPARAMETERIIVPROC                      GetTextureParameterIiv;\n        PFNGLGETTEXTUREPARAMETERIUIVPROC                     GetTextureParameterIuiv;\n        PFNGLGETTEXTUREPARAMETERFVPROC                       GetTextureParameterfv;\n        PFNGLGETTEXTUREPARAMETERIVPROC                       GetTextureParameteriv;\n        PFNGLGETTEXTURESUBIMAGEPROC                          GetTextureSubImage;\n        PFNGLGETTRANSFORMFEEDBACKVARYINGPROC                 GetTransformFeedbackVarying;\n        PFNGLGETTRANSFORMFEEDBACKI64_VPROC                   GetTransformFeedbacki64_v;\n        PFNGLGETTRANSFORMFEEDBACKI_VPROC                     GetTransformFeedbacki_v;\n        PFNGLGETTRANSFORMFEEDBACKIVPROC                      GetTransformFeedbackiv;\n        PFNGLGETUNIFORMBLOCKINDEXPROC                        GetUniformBlockIndex;\n        PFNGLGETUNIFORMINDICESPROC                           GetUniformIndices;\n        PFNGLGETUNIFORMLOCATIONPROC                          GetUniformLocation;\n        PFNGLGETUNIFORMSUBROUTINEUIVPROC                     GetUniformSubroutineuiv;\n        PFNGLGETUNIFORMDVPROC                                GetUniformdv;\n        PFNGLGETUNIFORMFVPROC                                GetUniformfv;\n        PFNGLGETUNIFORMIVPROC                                GetUniformiv;\n        PFNGLGETUNIFORMUIVPROC                               GetUniformuiv;\n        PFNGLGETVERTEXARRAYINDEXED64IVPROC                   GetVertexArrayIndexed64iv;\n        PFNGLGETVERTEXARRAYINDEXEDIVPROC                     GetVertexArrayIndexediv;\n        PFNGLGETVERTEXARRAYIVPROC                            GetVertexArrayiv;\n        PFNGLGETVERTEXATTRIBIIVPROC                          GetVertexAttribIiv;\n        PFNGLGETVERTEXATTRIBIUIVPROC                         GetVertexAttribIuiv;\n        PFNGLGETVERTEXATTRIBLDVPROC                          GetVertexAttribLdv;\n        PFNGLGETVERTEXATTRIBPOINTERVPROC                     GetVertexAttribPointerv;\n        PFNGLGETVERTEXATTRIBDVPROC                           GetVertexAttribdv;\n        PFNGLGETVERTEXATTRIBFVPROC                           GetVertexAttribfv;\n        PFNGLGETVERTEXATTRIBIVPROC                           GetVertexAttribiv;\n        PFNGLGETNCOMPRESSEDTEXIMAGEPROC                      GetnCompressedTexImage;\n        PFNGLGETNTEXIMAGEPROC                                GetnTexImage;\n        PFNGLGETNUNIFORMDVPROC                               GetnUniformdv;\n        PFNGLGETNUNIFORMFVPROC                               GetnUniformfv;\n        PFNGLGETNUNIFORMIVPROC                               GetnUniformiv;\n        PFNGLGETNUNIFORMUIVPROC                              GetnUniformuiv;\n        PFNGLHINTPROC                                        Hint;\n        PFNGLINVALIDATEBUFFERDATAPROC                        InvalidateBufferData;\n        PFNGLINVALIDATEBUFFERSUBDATAPROC                     InvalidateBufferSubData;\n        PFNGLINVALIDATEFRAMEBUFFERPROC                       InvalidateFramebuffer;\n        PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC              InvalidateNamedFramebufferData;\n        PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC           InvalidateNamedFramebufferSubData;\n        PFNGLINVALIDATESUBFRAMEBUFFERPROC                    InvalidateSubFramebuffer;\n        PFNGLINVALIDATETEXIMAGEPROC                          InvalidateTexImage;\n        PFNGLINVALIDATETEXSUBIMAGEPROC                       InvalidateTexSubImage;\n        PFNGLISBUFFERPROC                                    IsBuffer;\n        PFNGLISENABLEDPROC                                   IsEnabled;\n        PFNGLISENABLEDIPROC                                  IsEnabledi;\n        PFNGLISFRAMEBUFFERPROC                               IsFramebuffer;\n        PFNGLISPROGRAMPROC                                   IsProgram;\n        PFNGLISPROGRAMPIPELINEPROC                           IsProgramPipeline;\n        PFNGLISQUERYPROC                                     IsQuery;\n        PFNGLISRENDERBUFFERPROC                              IsRenderbuffer;\n        PFNGLISSAMPLERPROC                                   IsSampler;\n        PFNGLISSHADERPROC                                    IsShader;\n        PFNGLISSYNCPROC                                      IsSync;\n        PFNGLISTEXTUREPROC                                   IsTexture;\n        PFNGLISTRANSFORMFEEDBACKPROC                         IsTransformFeedback;\n        PFNGLISVERTEXARRAYPROC                               IsVertexArray;\n        PFNGLLINEWIDTHPROC                                   LineWidth;\n        PFNGLLINKPROGRAMPROC                                 LinkProgram;\n        PFNGLLOGICOPPROC                                     LogicOp;\n        PFNGLMAPBUFFERPROC                                   MapBuffer;\n        PFNGLMAPBUFFERRANGEPROC                              MapBufferRange;\n        PFNGLMAPNAMEDBUFFERPROC                              MapNamedBuffer;\n        PFNGLMAPNAMEDBUFFERRANGEPROC                         MapNamedBufferRange;\n        PFNGLMEMORYBARRIERPROC                               MemoryBarrier;\n        PFNGLMEMORYBARRIERBYREGIONPROC                       MemoryBarrierByRegion;\n        PFNGLMINSAMPLESHADINGPROC                            MinSampleShading;\n        PFNGLMULTIDRAWARRAYSPROC                             MultiDrawArrays;\n        PFNGLMULTIDRAWARRAYSINDIRECTPROC                     MultiDrawArraysIndirect;\n        PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC                MultiDrawArraysIndirectCount;\n        PFNGLMULTIDRAWELEMENTSPROC                           MultiDrawElements;\n        PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC                 MultiDrawElementsBaseVertex;\n        PFNGLMULTIDRAWELEMENTSINDIRECTPROC                   MultiDrawElementsIndirect;\n        PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC              MultiDrawElementsIndirectCount;\n        PFNGLNAMEDBUFFERDATAPROC                             NamedBufferData;\n        PFNGLNAMEDBUFFERSTORAGEPROC                          NamedBufferStorage;\n        PFNGLNAMEDBUFFERSUBDATAPROC                          NamedBufferSubData;\n        PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC                  NamedFramebufferDrawBuffer;\n        PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC                 NamedFramebufferDrawBuffers;\n        PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC                  NamedFramebufferParameteri;\n        PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC                  NamedFramebufferReadBuffer;\n        PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC                NamedFramebufferRenderbuffer;\n        PFNGLNAMEDFRAMEBUFFERTEXTUREPROC                     NamedFramebufferTexture;\n        PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC                NamedFramebufferTextureLayer;\n        PFNGLNAMEDRENDERBUFFERSTORAGEPROC                    NamedRenderbufferStorage;\n        PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC         NamedRenderbufferStorageMultisample;\n        PFNGLOBJECTLABELPROC                                 ObjectLabel;\n        PFNGLOBJECTPTRLABELPROC                              ObjectPtrLabel;\n        PFNGLPATCHPARAMETERFVPROC                            PatchParameterfv;\n        PFNGLPATCHPARAMETERIPROC                             PatchParameteri;\n        PFNGLPAUSETRANSFORMFEEDBACKPROC                      PauseTransformFeedback;\n        PFNGLPIXELSTOREFPROC                                 PixelStoref;\n        PFNGLPIXELSTOREIPROC                                 PixelStorei;\n        PFNGLPOINTPARAMETERFPROC                             PointParameterf;\n        PFNGLPOINTPARAMETERFVPROC                            PointParameterfv;\n        PFNGLPOINTPARAMETERIPROC                             PointParameteri;\n        PFNGLPOINTPARAMETERIVPROC                            PointParameteriv;\n        PFNGLPOINTSIZEPROC                                   PointSize;\n        PFNGLPOLYGONMODEPROC                                 PolygonMode;\n        PFNGLPOLYGONOFFSETPROC                               PolygonOffset;\n        PFNGLPOLYGONOFFSETCLAMPPROC                          PolygonOffsetClamp;\n        PFNGLPOPDEBUGGROUPPROC                               PopDebugGroup;\n        PFNGLPRIMITIVERESTARTINDEXPROC                       PrimitiveRestartIndex;\n        PFNGLPROGRAMBINARYPROC                               ProgramBinary;\n        PFNGLPROGRAMPARAMETERIPROC                           ProgramParameteri;\n        PFNGLPROGRAMUNIFORM1DPROC                            ProgramUniform1d;\n        PFNGLPROGRAMUNIFORM1DVPROC                           ProgramUniform1dv;\n        PFNGLPROGRAMUNIFORM1FPROC                            ProgramUniform1f;\n        PFNGLPROGRAMUNIFORM1FVPROC                           ProgramUniform1fv;\n        PFNGLPROGRAMUNIFORM1IPROC                            ProgramUniform1i;\n        PFNGLPROGRAMUNIFORM1IVPROC                           ProgramUniform1iv;\n        PFNGLPROGRAMUNIFORM1UIPROC                           ProgramUniform1ui;\n        PFNGLPROGRAMUNIFORM1UIVPROC                          ProgramUniform1uiv;\n        PFNGLPROGRAMUNIFORM2DPROC                            ProgramUniform2d;\n        PFNGLPROGRAMUNIFORM2DVPROC                           ProgramUniform2dv;\n        PFNGLPROGRAMUNIFORM2FPROC                            ProgramUniform2f;\n        PFNGLPROGRAMUNIFORM2FVPROC                           ProgramUniform2fv;\n        PFNGLPROGRAMUNIFORM2IPROC                            ProgramUniform2i;\n        PFNGLPROGRAMUNIFORM2IVPROC                           ProgramUniform2iv;\n        PFNGLPROGRAMUNIFORM2UIPROC                           ProgramUniform2ui;\n        PFNGLPROGRAMUNIFORM2UIVPROC                          ProgramUniform2uiv;\n        PFNGLPROGRAMUNIFORM3DPROC                            ProgramUniform3d;\n        PFNGLPROGRAMUNIFORM3DVPROC                           ProgramUniform3dv;\n        PFNGLPROGRAMUNIFORM3FPROC                            ProgramUniform3f;\n        PFNGLPROGRAMUNIFORM3FVPROC                           ProgramUniform3fv;\n        PFNGLPROGRAMUNIFORM3IPROC                            ProgramUniform3i;\n        PFNGLPROGRAMUNIFORM3IVPROC                           ProgramUniform3iv;\n        PFNGLPROGRAMUNIFORM3UIPROC                           ProgramUniform3ui;\n        PFNGLPROGRAMUNIFORM3UIVPROC                          ProgramUniform3uiv;\n        PFNGLPROGRAMUNIFORM4DPROC                            ProgramUniform4d;\n        PFNGLPROGRAMUNIFORM4DVPROC                           ProgramUniform4dv;\n        PFNGLPROGRAMUNIFORM4FPROC                            ProgramUniform4f;\n        PFNGLPROGRAMUNIFORM4FVPROC                           ProgramUniform4fv;\n        PFNGLPROGRAMUNIFORM4IPROC                            ProgramUniform4i;\n        PFNGLPROGRAMUNIFORM4IVPROC                           ProgramUniform4iv;\n        PFNGLPROGRAMUNIFORM4UIPROC                           ProgramUniform4ui;\n        PFNGLPROGRAMUNIFORM4UIVPROC                          ProgramUniform4uiv;\n        PFNGLPROGRAMUNIFORMMATRIX2DVPROC                     ProgramUniformMatrix2dv;\n        PFNGLPROGRAMUNIFORMMATRIX2FVPROC                     ProgramUniformMatrix2fv;\n        PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC                   ProgramUniformMatrix2x3dv;\n        PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC                   ProgramUniformMatrix2x3fv;\n        PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC                   ProgramUniformMatrix2x4dv;\n        PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC                   ProgramUniformMatrix2x4fv;\n        PFNGLPROGRAMUNIFORMMATRIX3DVPROC                     ProgramUniformMatrix3dv;\n        PFNGLPROGRAMUNIFORMMATRIX3FVPROC                     ProgramUniformMatrix3fv;\n        PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC                   ProgramUniformMatrix3x2dv;\n        PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC                   ProgramUniformMatrix3x2fv;\n        PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC                   ProgramUniformMatrix3x4dv;\n        PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC                   ProgramUniformMatrix3x4fv;\n        PFNGLPROGRAMUNIFORMMATRIX4DVPROC                     ProgramUniformMatrix4dv;\n        PFNGLPROGRAMUNIFORMMATRIX4FVPROC                     ProgramUniformMatrix4fv;\n        PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC                   ProgramUniformMatrix4x2dv;\n        PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC                   ProgramUniformMatrix4x2fv;\n        PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC                   ProgramUniformMatrix4x3dv;\n        PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC                   ProgramUniformMatrix4x3fv;\n        PFNGLPROVOKINGVERTEXPROC                             ProvokingVertex;\n        PFNGLPUSHDEBUGGROUPPROC                              PushDebugGroup;\n        PFNGLQUERYCOUNTERPROC                                QueryCounter;\n        PFNGLREADBUFFERPROC                                  ReadBuffer;\n        PFNGLREADPIXELSPROC                                  ReadPixels;\n        PFNGLREADNPIXELSPROC                                 ReadnPixels;\n        PFNGLRELEASESHADERCOMPILERPROC                       ReleaseShaderCompiler;\n        PFNGLRENDERBUFFERSTORAGEPROC                         RenderbufferStorage;\n        PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC              RenderbufferStorageMultisample;\n        PFNGLRESUMETRANSFORMFEEDBACKPROC                     ResumeTransformFeedback;\n        PFNGLSAMPLECOVERAGEPROC                              SampleCoverage;\n        PFNGLSAMPLEMASKIPROC                                 SampleMaski;\n        PFNGLSAMPLERPARAMETERIIVPROC                         SamplerParameterIiv;\n        PFNGLSAMPLERPARAMETERIUIVPROC                        SamplerParameterIuiv;\n        PFNGLSAMPLERPARAMETERFPROC                           SamplerParameterf;\n        PFNGLSAMPLERPARAMETERFVPROC                          SamplerParameterfv;\n        PFNGLSAMPLERPARAMETERIPROC                           SamplerParameteri;\n        PFNGLSAMPLERPARAMETERIVPROC                          SamplerParameteriv;\n        PFNGLSCISSORPROC                                     Scissor;\n        PFNGLSCISSORARRAYVPROC                               ScissorArrayv;\n        PFNGLSCISSORINDEXEDPROC                              ScissorIndexed;\n        PFNGLSCISSORINDEXEDVPROC                             ScissorIndexedv;\n        PFNGLSHADERBINARYPROC                                ShaderBinary;\n        PFNGLSHADERSOURCEPROC                                ShaderSource;\n        PFNGLSHADERSTORAGEBLOCKBINDINGPROC                   ShaderStorageBlockBinding;\n        PFNGLSPECIALIZESHADERPROC                            SpecializeShader;\n        PFNGLSTENCILFUNCPROC                                 StencilFunc;\n        PFNGLSTENCILFUNCSEPARATEPROC                         StencilFuncSeparate;\n        PFNGLSTENCILMASKPROC                                 StencilMask;\n        PFNGLSTENCILMASKSEPARATEPROC                         StencilMaskSeparate;\n        PFNGLSTENCILOPPROC                                   StencilOp;\n        PFNGLSTENCILOPSEPARATEPROC                           StencilOpSeparate;\n        PFNGLTEXBUFFERPROC                                   TexBuffer;\n        PFNGLTEXBUFFERRANGEPROC                              TexBufferRange;\n        PFNGLTEXIMAGE1DPROC                                  TexImage1D;\n        PFNGLTEXIMAGE2DPROC                                  TexImage2D;\n        PFNGLTEXIMAGE2DMULTISAMPLEPROC                       TexImage2DMultisample;\n        PFNGLTEXIMAGE3DPROC                                  TexImage3D;\n        PFNGLTEXIMAGE3DMULTISAMPLEPROC                       TexImage3DMultisample;\n        PFNGLTEXPARAMETERIIVPROC                             TexParameterIiv;\n        PFNGLTEXPARAMETERIUIVPROC                            TexParameterIuiv;\n        PFNGLTEXPARAMETERFPROC                               TexParameterf;\n        PFNGLTEXPARAMETERFVPROC                              TexParameterfv;\n        PFNGLTEXPARAMETERIPROC                               TexParameteri;\n        PFNGLTEXPARAMETERIVPROC                              TexParameteriv;\n        PFNGLTEXSTORAGE1DPROC                                TexStorage1D;\n        PFNGLTEXSTORAGE2DPROC                                TexStorage2D;\n        PFNGLTEXSTORAGE2DMULTISAMPLEPROC                     TexStorage2DMultisample;\n        PFNGLTEXSTORAGE3DPROC                                TexStorage3D;\n        PFNGLTEXSTORAGE3DMULTISAMPLEPROC                     TexStorage3DMultisample;\n        PFNGLTEXSUBIMAGE1DPROC                               TexSubImage1D;\n        PFNGLTEXSUBIMAGE2DPROC                               TexSubImage2D;\n        PFNGLTEXSUBIMAGE3DPROC                               TexSubImage3D;\n        PFNGLTEXTUREBARRIERPROC                              TextureBarrier;\n        PFNGLTEXTUREBUFFERPROC                               TextureBuffer;\n        PFNGLTEXTUREBUFFERRANGEPROC                          TextureBufferRange;\n        PFNGLTEXTUREPARAMETERIIVPROC                         TextureParameterIiv;\n        PFNGLTEXTUREPARAMETERIUIVPROC                        TextureParameterIuiv;\n        PFNGLTEXTUREPARAMETERFPROC                           TextureParameterf;\n        PFNGLTEXTUREPARAMETERFVPROC                          TextureParameterfv;\n        PFNGLTEXTUREPARAMETERIPROC                           TextureParameteri;\n        PFNGLTEXTUREPARAMETERIVPROC                          TextureParameteriv;\n        PFNGLTEXTURESTORAGE1DPROC                            TextureStorage1D;\n        PFNGLTEXTURESTORAGE2DPROC                            TextureStorage2D;\n        PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC                 TextureStorage2DMultisample;\n        PFNGLTEXTURESTORAGE3DPROC                            TextureStorage3D;\n        PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC                 TextureStorage3DMultisample;\n        PFNGLTEXTURESUBIMAGE1DPROC                           TextureSubImage1D;\n        PFNGLTEXTURESUBIMAGE2DPROC                           TextureSubImage2D;\n        PFNGLTEXTURESUBIMAGE3DPROC                           TextureSubImage3D;\n        PFNGLTEXTUREVIEWPROC                                 TextureView;\n        PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC                 TransformFeedbackBufferBase;\n        PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC                TransformFeedbackBufferRange;\n        PFNGLTRANSFORMFEEDBACKVARYINGSPROC                   TransformFeedbackVaryings;\n        PFNGLUNIFORM1DPROC                                   Uniform1d;\n        PFNGLUNIFORM1DVPROC                                  Uniform1dv;\n        PFNGLUNIFORM1FPROC                                   Uniform1f;\n        PFNGLUNIFORM1FVPROC                                  Uniform1fv;\n        PFNGLUNIFORM1IPROC                                   Uniform1i;\n        PFNGLUNIFORM1IVPROC                                  Uniform1iv;\n        PFNGLUNIFORM1UIPROC                                  Uniform1ui;\n        PFNGLUNIFORM1UIVPROC                                 Uniform1uiv;\n        PFNGLUNIFORM2DPROC                                   Uniform2d;\n        PFNGLUNIFORM2DVPROC                                  Uniform2dv;\n        PFNGLUNIFORM2FPROC                                   Uniform2f;\n        PFNGLUNIFORM2FVPROC                                  Uniform2fv;\n        PFNGLUNIFORM2IPROC                                   Uniform2i;\n        PFNGLUNIFORM2IVPROC                                  Uniform2iv;\n        PFNGLUNIFORM2UIPROC                                  Uniform2ui;\n        PFNGLUNIFORM2UIVPROC                                 Uniform2uiv;\n        PFNGLUNIFORM3DPROC                                   Uniform3d;\n        PFNGLUNIFORM3DVPROC                                  Uniform3dv;\n        PFNGLUNIFORM3FPROC                                   Uniform3f;\n        PFNGLUNIFORM3FVPROC                                  Uniform3fv;\n        PFNGLUNIFORM3IPROC                                   Uniform3i;\n        PFNGLUNIFORM3IVPROC                                  Uniform3iv;\n        PFNGLUNIFORM3UIPROC                                  Uniform3ui;\n        PFNGLUNIFORM3UIVPROC                                 Uniform3uiv;\n        PFNGLUNIFORM4DPROC                                   Uniform4d;\n        PFNGLUNIFORM4DVPROC                                  Uniform4dv;\n        PFNGLUNIFORM4FPROC                                   Uniform4f;\n        PFNGLUNIFORM4FVPROC                                  Uniform4fv;\n        PFNGLUNIFORM4IPROC                                   Uniform4i;\n        PFNGLUNIFORM4IVPROC                                  Uniform4iv;\n        PFNGLUNIFORM4UIPROC                                  Uniform4ui;\n        PFNGLUNIFORM4UIVPROC                                 Uniform4uiv;\n        PFNGLUNIFORMBLOCKBINDINGPROC                         UniformBlockBinding;\n        PFNGLUNIFORMMATRIX2DVPROC                            UniformMatrix2dv;\n        PFNGLUNIFORMMATRIX2FVPROC                            UniformMatrix2fv;\n        PFNGLUNIFORMMATRIX2X3DVPROC                          UniformMatrix2x3dv;\n        PFNGLUNIFORMMATRIX2X3FVPROC                          UniformMatrix2x3fv;\n        PFNGLUNIFORMMATRIX2X4DVPROC                          UniformMatrix2x4dv;\n        PFNGLUNIFORMMATRIX2X4FVPROC                          UniformMatrix2x4fv;\n        PFNGLUNIFORMMATRIX3DVPROC                            UniformMatrix3dv;\n        PFNGLUNIFORMMATRIX3FVPROC                            UniformMatrix3fv;\n        PFNGLUNIFORMMATRIX3X2DVPROC                          UniformMatrix3x2dv;\n        PFNGLUNIFORMMATRIX3X2FVPROC                          UniformMatrix3x2fv;\n        PFNGLUNIFORMMATRIX3X4DVPROC                          UniformMatrix3x4dv;\n        PFNGLUNIFORMMATRIX3X4FVPROC                          UniformMatrix3x4fv;\n        PFNGLUNIFORMMATRIX4DVPROC                            UniformMatrix4dv;\n        PFNGLUNIFORMMATRIX4FVPROC                            UniformMatrix4fv;\n        PFNGLUNIFORMMATRIX4X2DVPROC                          UniformMatrix4x2dv;\n        PFNGLUNIFORMMATRIX4X2FVPROC                          UniformMatrix4x2fv;\n        PFNGLUNIFORMMATRIX4X3DVPROC                          UniformMatrix4x3dv;\n        PFNGLUNIFORMMATRIX4X3FVPROC                          UniformMatrix4x3fv;\n        PFNGLUNIFORMSUBROUTINESUIVPROC                       UniformSubroutinesuiv;\n        PFNGLUNMAPBUFFERPROC                                 UnmapBuffer;\n        PFNGLUNMAPNAMEDBUFFERPROC                            UnmapNamedBuffer;\n        PFNGLUSEPROGRAMPROC                                  UseProgram;\n        PFNGLUSEPROGRAMSTAGESPROC                            UseProgramStages;\n        PFNGLVALIDATEPROGRAMPROC                             ValidateProgram;\n        PFNGLVALIDATEPROGRAMPIPELINEPROC                     ValidateProgramPipeline;\n        PFNGLVERTEXARRAYATTRIBBINDINGPROC                    VertexArrayAttribBinding;\n        PFNGLVERTEXARRAYATTRIBFORMATPROC                     VertexArrayAttribFormat;\n        PFNGLVERTEXARRAYATTRIBIFORMATPROC                    VertexArrayAttribIFormat;\n        PFNGLVERTEXARRAYATTRIBLFORMATPROC                    VertexArrayAttribLFormat;\n        PFNGLVERTEXARRAYBINDINGDIVISORPROC                   VertexArrayBindingDivisor;\n        PFNGLVERTEXARRAYELEMENTBUFFERPROC                    VertexArrayElementBuffer;\n        PFNGLVERTEXARRAYVERTEXBUFFERPROC                     VertexArrayVertexBuffer;\n        PFNGLVERTEXARRAYVERTEXBUFFERSPROC                    VertexArrayVertexBuffers;\n        PFNGLVERTEXATTRIB1DPROC                              VertexAttrib1d;\n        PFNGLVERTEXATTRIB1DVPROC                             VertexAttrib1dv;\n        PFNGLVERTEXATTRIB1FPROC                              VertexAttrib1f;\n        PFNGLVERTEXATTRIB1FVPROC                             VertexAttrib1fv;\n        PFNGLVERTEXATTRIB1SPROC                              VertexAttrib1s;\n        PFNGLVERTEXATTRIB1SVPROC                             VertexAttrib1sv;\n        PFNGLVERTEXATTRIB2DPROC                              VertexAttrib2d;\n        PFNGLVERTEXATTRIB2DVPROC                             VertexAttrib2dv;\n        PFNGLVERTEXATTRIB2FPROC                              VertexAttrib2f;\n        PFNGLVERTEXATTRIB2FVPROC                             VertexAttrib2fv;\n        PFNGLVERTEXATTRIB2SPROC                              VertexAttrib2s;\n        PFNGLVERTEXATTRIB2SVPROC                             VertexAttrib2sv;\n        PFNGLVERTEXATTRIB3DPROC                              VertexAttrib3d;\n        PFNGLVERTEXATTRIB3DVPROC                             VertexAttrib3dv;\n        PFNGLVERTEXATTRIB3FPROC                              VertexAttrib3f;\n        PFNGLVERTEXATTRIB3FVPROC                             VertexAttrib3fv;\n        PFNGLVERTEXATTRIB3SPROC                              VertexAttrib3s;\n        PFNGLVERTEXATTRIB3SVPROC                             VertexAttrib3sv;\n        PFNGLVERTEXATTRIB4NBVPROC                            VertexAttrib4Nbv;\n        PFNGLVERTEXATTRIB4NIVPROC                            VertexAttrib4Niv;\n        PFNGLVERTEXATTRIB4NSVPROC                            VertexAttrib4Nsv;\n        PFNGLVERTEXATTRIB4NUBPROC                            VertexAttrib4Nub;\n        PFNGLVERTEXATTRIB4NUBVPROC                           VertexAttrib4Nubv;\n        PFNGLVERTEXATTRIB4NUIVPROC                           VertexAttrib4Nuiv;\n        PFNGLVERTEXATTRIB4NUSVPROC                           VertexAttrib4Nusv;\n        PFNGLVERTEXATTRIB4BVPROC                             VertexAttrib4bv;\n        PFNGLVERTEXATTRIB4DPROC                              VertexAttrib4d;\n        PFNGLVERTEXATTRIB4DVPROC                             VertexAttrib4dv;\n        PFNGLVERTEXATTRIB4FPROC                              VertexAttrib4f;\n        PFNGLVERTEXATTRIB4FVPROC                             VertexAttrib4fv;\n        PFNGLVERTEXATTRIB4IVPROC                             VertexAttrib4iv;\n        PFNGLVERTEXATTRIB4SPROC                              VertexAttrib4s;\n        PFNGLVERTEXATTRIB4SVPROC                             VertexAttrib4sv;\n        PFNGLVERTEXATTRIB4UBVPROC                            VertexAttrib4ubv;\n        PFNGLVERTEXATTRIB4UIVPROC                            VertexAttrib4uiv;\n        PFNGLVERTEXATTRIB4USVPROC                            VertexAttrib4usv;\n        PFNGLVERTEXATTRIBBINDINGPROC                         VertexAttribBinding;\n        PFNGLVERTEXATTRIBDIVISORPROC                         VertexAttribDivisor;\n        PFNGLVERTEXATTRIBFORMATPROC                          VertexAttribFormat;\n        PFNGLVERTEXATTRIBI1IPROC                             VertexAttribI1i;\n        PFNGLVERTEXATTRIBI1IVPROC                            VertexAttribI1iv;\n        PFNGLVERTEXATTRIBI1UIPROC                            VertexAttribI1ui;\n        PFNGLVERTEXATTRIBI1UIVPROC                           VertexAttribI1uiv;\n        PFNGLVERTEXATTRIBI2IPROC                             VertexAttribI2i;\n        PFNGLVERTEXATTRIBI2IVPROC                            VertexAttribI2iv;\n        PFNGLVERTEXATTRIBI2UIPROC                            VertexAttribI2ui;\n        PFNGLVERTEXATTRIBI2UIVPROC                           VertexAttribI2uiv;\n        PFNGLVERTEXATTRIBI3IPROC                             VertexAttribI3i;\n        PFNGLVERTEXATTRIBI3IVPROC                            VertexAttribI3iv;\n        PFNGLVERTEXATTRIBI3UIPROC                            VertexAttribI3ui;\n        PFNGLVERTEXATTRIBI3UIVPROC                           VertexAttribI3uiv;\n        PFNGLVERTEXATTRIBI4BVPROC                            VertexAttribI4bv;\n        PFNGLVERTEXATTRIBI4IPROC                             VertexAttribI4i;\n        PFNGLVERTEXATTRIBI4IVPROC                            VertexAttribI4iv;\n        PFNGLVERTEXATTRIBI4SVPROC                            VertexAttribI4sv;\n        PFNGLVERTEXATTRIBI4UBVPROC                           VertexAttribI4ubv;\n        PFNGLVERTEXATTRIBI4UIPROC                            VertexAttribI4ui;\n        PFNGLVERTEXATTRIBI4UIVPROC                           VertexAttribI4uiv;\n        PFNGLVERTEXATTRIBI4USVPROC                           VertexAttribI4usv;\n        PFNGLVERTEXATTRIBIFORMATPROC                         VertexAttribIFormat;\n        PFNGLVERTEXATTRIBIPOINTERPROC                        VertexAttribIPointer;\n        PFNGLVERTEXATTRIBL1DPROC                             VertexAttribL1d;\n        PFNGLVERTEXATTRIBL1DVPROC                            VertexAttribL1dv;\n        PFNGLVERTEXATTRIBL2DPROC                             VertexAttribL2d;\n        PFNGLVERTEXATTRIBL2DVPROC                            VertexAttribL2dv;\n        PFNGLVERTEXATTRIBL3DPROC                             VertexAttribL3d;\n        PFNGLVERTEXATTRIBL3DVPROC                            VertexAttribL3dv;\n        PFNGLVERTEXATTRIBL4DPROC                             VertexAttribL4d;\n        PFNGLVERTEXATTRIBL4DVPROC                            VertexAttribL4dv;\n        PFNGLVERTEXATTRIBLFORMATPROC                         VertexAttribLFormat;\n        PFNGLVERTEXATTRIBLPOINTERPROC                        VertexAttribLPointer;\n        PFNGLVERTEXATTRIBP1UIPROC                            VertexAttribP1ui;\n        PFNGLVERTEXATTRIBP1UIVPROC                           VertexAttribP1uiv;\n        PFNGLVERTEXATTRIBP2UIPROC                            VertexAttribP2ui;\n        PFNGLVERTEXATTRIBP2UIVPROC                           VertexAttribP2uiv;\n        PFNGLVERTEXATTRIBP3UIPROC                            VertexAttribP3ui;\n        PFNGLVERTEXATTRIBP3UIVPROC                           VertexAttribP3uiv;\n        PFNGLVERTEXATTRIBP4UIPROC                            VertexAttribP4ui;\n        PFNGLVERTEXATTRIBP4UIVPROC                           VertexAttribP4uiv;\n        PFNGLVERTEXATTRIBPOINTERPROC                         VertexAttribPointer;\n        PFNGLVERTEXBINDINGDIVISORPROC                        VertexBindingDivisor;\n        PFNGLVIEWPORTPROC                                    Viewport;\n        PFNGLVIEWPORTARRAYVPROC                              ViewportArrayv;\n        PFNGLVIEWPORTINDEXEDFPROC                            ViewportIndexedf;\n        PFNGLVIEWPORTINDEXEDFVPROC                           ViewportIndexedfv;\n        PFNGLWAITSYNCPROC                                    WaitSync;\n    } gl;\n};\n\nGL3W_API extern union GL3WProcs gl3wProcs;\n\n/* OpenGL functions */\n#define glActiveShaderProgram                                gl3wProcs.gl.ActiveShaderProgram\n#define glActiveTexture                                      gl3wProcs.gl.ActiveTexture\n#define glAttachShader                                       gl3wProcs.gl.AttachShader\n#define glBeginConditionalRender                             gl3wProcs.gl.BeginConditionalRender\n#define glBeginQuery                                         gl3wProcs.gl.BeginQuery\n#define glBeginQueryIndexed                                  gl3wProcs.gl.BeginQueryIndexed\n#define glBeginTransformFeedback                             gl3wProcs.gl.BeginTransformFeedback\n#define glBindAttribLocation                                 gl3wProcs.gl.BindAttribLocation\n#define glBindBuffer                                         gl3wProcs.gl.BindBuffer\n#define glBindBufferBase                                     gl3wProcs.gl.BindBufferBase\n#define glBindBufferRange                                    gl3wProcs.gl.BindBufferRange\n#define glBindBuffersBase                                    gl3wProcs.gl.BindBuffersBase\n#define glBindBuffersRange                                   gl3wProcs.gl.BindBuffersRange\n#define glBindFragDataLocation                               gl3wProcs.gl.BindFragDataLocation\n#define glBindFragDataLocationIndexed                        gl3wProcs.gl.BindFragDataLocationIndexed\n#define glBindFramebuffer                                    gl3wProcs.gl.BindFramebuffer\n#define glBindImageTexture                                   gl3wProcs.gl.BindImageTexture\n#define glBindImageTextures                                  gl3wProcs.gl.BindImageTextures\n#define glBindProgramPipeline                                gl3wProcs.gl.BindProgramPipeline\n#define glBindRenderbuffer                                   gl3wProcs.gl.BindRenderbuffer\n#define glBindSampler                                        gl3wProcs.gl.BindSampler\n#define glBindSamplers                                       gl3wProcs.gl.BindSamplers\n#define glBindTexture                                        gl3wProcs.gl.BindTexture\n#define glBindTextureUnit                                    gl3wProcs.gl.BindTextureUnit\n#define glBindTextures                                       gl3wProcs.gl.BindTextures\n#define glBindTransformFeedback                              gl3wProcs.gl.BindTransformFeedback\n#define glBindVertexArray                                    gl3wProcs.gl.BindVertexArray\n#define glBindVertexBuffer                                   gl3wProcs.gl.BindVertexBuffer\n#define glBindVertexBuffers                                  gl3wProcs.gl.BindVertexBuffers\n#define glBlendColor                                         gl3wProcs.gl.BlendColor\n#define glBlendEquation                                      gl3wProcs.gl.BlendEquation\n#define glBlendEquationSeparate                              gl3wProcs.gl.BlendEquationSeparate\n#define glBlendEquationSeparatei                             gl3wProcs.gl.BlendEquationSeparatei\n#define glBlendEquationi                                     gl3wProcs.gl.BlendEquationi\n#define glBlendFunc                                          gl3wProcs.gl.BlendFunc\n#define glBlendFuncSeparate                                  gl3wProcs.gl.BlendFuncSeparate\n#define glBlendFuncSeparatei                                 gl3wProcs.gl.BlendFuncSeparatei\n#define glBlendFunci                                         gl3wProcs.gl.BlendFunci\n#define glBlitFramebuffer                                    gl3wProcs.gl.BlitFramebuffer\n#define glBlitNamedFramebuffer                               gl3wProcs.gl.BlitNamedFramebuffer\n#define glBufferData                                         gl3wProcs.gl.BufferData\n#define glBufferStorage                                      gl3wProcs.gl.BufferStorage\n#define glBufferSubData                                      gl3wProcs.gl.BufferSubData\n#define glCheckFramebufferStatus                             gl3wProcs.gl.CheckFramebufferStatus\n#define glCheckNamedFramebufferStatus                        gl3wProcs.gl.CheckNamedFramebufferStatus\n#define glClampColor                                         gl3wProcs.gl.ClampColor\n#define glClear                                              gl3wProcs.gl.Clear\n#define glClearBufferData                                    gl3wProcs.gl.ClearBufferData\n#define glClearBufferSubData                                 gl3wProcs.gl.ClearBufferSubData\n#define glClearBufferfi                                      gl3wProcs.gl.ClearBufferfi\n#define glClearBufferfv                                      gl3wProcs.gl.ClearBufferfv\n#define glClearBufferiv                                      gl3wProcs.gl.ClearBufferiv\n#define glClearBufferuiv                                     gl3wProcs.gl.ClearBufferuiv\n#define glClearColor                                         gl3wProcs.gl.ClearColor\n#define glClearDepth                                         gl3wProcs.gl.ClearDepth\n#define glClearDepthf                                        gl3wProcs.gl.ClearDepthf\n#define glClearNamedBufferData                               gl3wProcs.gl.ClearNamedBufferData\n#define glClearNamedBufferSubData                            gl3wProcs.gl.ClearNamedBufferSubData\n#define glClearNamedFramebufferfi                            gl3wProcs.gl.ClearNamedFramebufferfi\n#define glClearNamedFramebufferfv                            gl3wProcs.gl.ClearNamedFramebufferfv\n#define glClearNamedFramebufferiv                            gl3wProcs.gl.ClearNamedFramebufferiv\n#define glClearNamedFramebufferuiv                           gl3wProcs.gl.ClearNamedFramebufferuiv\n#define glClearStencil                                       gl3wProcs.gl.ClearStencil\n#define glClearTexImage                                      gl3wProcs.gl.ClearTexImage\n#define glClearTexSubImage                                   gl3wProcs.gl.ClearTexSubImage\n#define glClientWaitSync                                     gl3wProcs.gl.ClientWaitSync\n#define glClipControl                                        gl3wProcs.gl.ClipControl\n#define glColorMask                                          gl3wProcs.gl.ColorMask\n#define glColorMaski                                         gl3wProcs.gl.ColorMaski\n#define glCompileShader                                      gl3wProcs.gl.CompileShader\n#define glCompressedTexImage1D                               gl3wProcs.gl.CompressedTexImage1D\n#define glCompressedTexImage2D                               gl3wProcs.gl.CompressedTexImage2D\n#define glCompressedTexImage3D                               gl3wProcs.gl.CompressedTexImage3D\n#define glCompressedTexSubImage1D                            gl3wProcs.gl.CompressedTexSubImage1D\n#define glCompressedTexSubImage2D                            gl3wProcs.gl.CompressedTexSubImage2D\n#define glCompressedTexSubImage3D                            gl3wProcs.gl.CompressedTexSubImage3D\n#define glCompressedTextureSubImage1D                        gl3wProcs.gl.CompressedTextureSubImage1D\n#define glCompressedTextureSubImage2D                        gl3wProcs.gl.CompressedTextureSubImage2D\n#define glCompressedTextureSubImage3D                        gl3wProcs.gl.CompressedTextureSubImage3D\n#define glCopyBufferSubData                                  gl3wProcs.gl.CopyBufferSubData\n#define glCopyImageSubData                                   gl3wProcs.gl.CopyImageSubData\n#define glCopyNamedBufferSubData                             gl3wProcs.gl.CopyNamedBufferSubData\n#define glCopyTexImage1D                                     gl3wProcs.gl.CopyTexImage1D\n#define glCopyTexImage2D                                     gl3wProcs.gl.CopyTexImage2D\n#define glCopyTexSubImage1D                                  gl3wProcs.gl.CopyTexSubImage1D\n#define glCopyTexSubImage2D                                  gl3wProcs.gl.CopyTexSubImage2D\n#define glCopyTexSubImage3D                                  gl3wProcs.gl.CopyTexSubImage3D\n#define glCopyTextureSubImage1D                              gl3wProcs.gl.CopyTextureSubImage1D\n#define glCopyTextureSubImage2D                              gl3wProcs.gl.CopyTextureSubImage2D\n#define glCopyTextureSubImage3D                              gl3wProcs.gl.CopyTextureSubImage3D\n#define glCreateBuffers                                      gl3wProcs.gl.CreateBuffers\n#define glCreateFramebuffers                                 gl3wProcs.gl.CreateFramebuffers\n#define glCreateProgram                                      gl3wProcs.gl.CreateProgram\n#define glCreateProgramPipelines                             gl3wProcs.gl.CreateProgramPipelines\n#define glCreateQueries                                      gl3wProcs.gl.CreateQueries\n#define glCreateRenderbuffers                                gl3wProcs.gl.CreateRenderbuffers\n#define glCreateSamplers                                     gl3wProcs.gl.CreateSamplers\n#define glCreateShader                                       gl3wProcs.gl.CreateShader\n#define glCreateShaderProgramv                               gl3wProcs.gl.CreateShaderProgramv\n#define glCreateTextures                                     gl3wProcs.gl.CreateTextures\n#define glCreateTransformFeedbacks                           gl3wProcs.gl.CreateTransformFeedbacks\n#define glCreateVertexArrays                                 gl3wProcs.gl.CreateVertexArrays\n#define glCullFace                                           gl3wProcs.gl.CullFace\n#define glDebugMessageCallback                               gl3wProcs.gl.DebugMessageCallback\n#define glDebugMessageControl                                gl3wProcs.gl.DebugMessageControl\n#define glDebugMessageInsert                                 gl3wProcs.gl.DebugMessageInsert\n#define glDeleteBuffers                                      gl3wProcs.gl.DeleteBuffers\n#define glDeleteFramebuffers                                 gl3wProcs.gl.DeleteFramebuffers\n#define glDeleteProgram                                      gl3wProcs.gl.DeleteProgram\n#define glDeleteProgramPipelines                             gl3wProcs.gl.DeleteProgramPipelines\n#define glDeleteQueries                                      gl3wProcs.gl.DeleteQueries\n#define glDeleteRenderbuffers                                gl3wProcs.gl.DeleteRenderbuffers\n#define glDeleteSamplers                                     gl3wProcs.gl.DeleteSamplers\n#define glDeleteShader                                       gl3wProcs.gl.DeleteShader\n#define glDeleteSync                                         gl3wProcs.gl.DeleteSync\n#define glDeleteTextures                                     gl3wProcs.gl.DeleteTextures\n#define glDeleteTransformFeedbacks                           gl3wProcs.gl.DeleteTransformFeedbacks\n#define glDeleteVertexArrays                                 gl3wProcs.gl.DeleteVertexArrays\n#define glDepthFunc                                          gl3wProcs.gl.DepthFunc\n#define glDepthMask                                          gl3wProcs.gl.DepthMask\n#define glDepthRange                                         gl3wProcs.gl.DepthRange\n#define glDepthRangeArrayv                                   gl3wProcs.gl.DepthRangeArrayv\n#define glDepthRangeIndexed                                  gl3wProcs.gl.DepthRangeIndexed\n#define glDepthRangef                                        gl3wProcs.gl.DepthRangef\n#define glDetachShader                                       gl3wProcs.gl.DetachShader\n#define glDisable                                            gl3wProcs.gl.Disable\n#define glDisableVertexArrayAttrib                           gl3wProcs.gl.DisableVertexArrayAttrib\n#define glDisableVertexAttribArray                           gl3wProcs.gl.DisableVertexAttribArray\n#define glDisablei                                           gl3wProcs.gl.Disablei\n#define glDispatchCompute                                    gl3wProcs.gl.DispatchCompute\n#define glDispatchComputeIndirect                            gl3wProcs.gl.DispatchComputeIndirect\n#define glDrawArrays                                         gl3wProcs.gl.DrawArrays\n#define glDrawArraysIndirect                                 gl3wProcs.gl.DrawArraysIndirect\n#define glDrawArraysInstanced                                gl3wProcs.gl.DrawArraysInstanced\n#define glDrawArraysInstancedBaseInstance                    gl3wProcs.gl.DrawArraysInstancedBaseInstance\n#define glDrawBuffer                                         gl3wProcs.gl.DrawBuffer\n#define glDrawBuffers                                        gl3wProcs.gl.DrawBuffers\n#define glDrawElements                                       gl3wProcs.gl.DrawElements\n#define glDrawElementsBaseVertex                             gl3wProcs.gl.DrawElementsBaseVertex\n#define glDrawElementsIndirect                               gl3wProcs.gl.DrawElementsIndirect\n#define glDrawElementsInstanced                              gl3wProcs.gl.DrawElementsInstanced\n#define glDrawElementsInstancedBaseInstance                  gl3wProcs.gl.DrawElementsInstancedBaseInstance\n#define glDrawElementsInstancedBaseVertex                    gl3wProcs.gl.DrawElementsInstancedBaseVertex\n#define glDrawElementsInstancedBaseVertexBaseInstance        gl3wProcs.gl.DrawElementsInstancedBaseVertexBaseInstance\n#define glDrawRangeElements                                  gl3wProcs.gl.DrawRangeElements\n#define glDrawRangeElementsBaseVertex                        gl3wProcs.gl.DrawRangeElementsBaseVertex\n#define glDrawTransformFeedback                              gl3wProcs.gl.DrawTransformFeedback\n#define glDrawTransformFeedbackInstanced                     gl3wProcs.gl.DrawTransformFeedbackInstanced\n#define glDrawTransformFeedbackStream                        gl3wProcs.gl.DrawTransformFeedbackStream\n#define glDrawTransformFeedbackStreamInstanced               gl3wProcs.gl.DrawTransformFeedbackStreamInstanced\n#define glEnable                                             gl3wProcs.gl.Enable\n#define glEnableVertexArrayAttrib                            gl3wProcs.gl.EnableVertexArrayAttrib\n#define glEnableVertexAttribArray                            gl3wProcs.gl.EnableVertexAttribArray\n#define glEnablei                                            gl3wProcs.gl.Enablei\n#define glEndConditionalRender                               gl3wProcs.gl.EndConditionalRender\n#define glEndQuery                                           gl3wProcs.gl.EndQuery\n#define glEndQueryIndexed                                    gl3wProcs.gl.EndQueryIndexed\n#define glEndTransformFeedback                               gl3wProcs.gl.EndTransformFeedback\n#define glFenceSync                                          gl3wProcs.gl.FenceSync\n#define glFinish                                             gl3wProcs.gl.Finish\n#define glFlush                                              gl3wProcs.gl.Flush\n#define glFlushMappedBufferRange                             gl3wProcs.gl.FlushMappedBufferRange\n#define glFlushMappedNamedBufferRange                        gl3wProcs.gl.FlushMappedNamedBufferRange\n#define glFramebufferParameteri                              gl3wProcs.gl.FramebufferParameteri\n#define glFramebufferParameteriMESA                          gl3wProcs.gl.FramebufferParameteriMESA\n#define glFramebufferRenderbuffer                            gl3wProcs.gl.FramebufferRenderbuffer\n#define glFramebufferTexture                                 gl3wProcs.gl.FramebufferTexture\n#define glFramebufferTexture1D                               gl3wProcs.gl.FramebufferTexture1D\n#define glFramebufferTexture2D                               gl3wProcs.gl.FramebufferTexture2D\n#define glFramebufferTexture3D                               gl3wProcs.gl.FramebufferTexture3D\n#define glFramebufferTextureLayer                            gl3wProcs.gl.FramebufferTextureLayer\n#define glFrontFace                                          gl3wProcs.gl.FrontFace\n#define glGenBuffers                                         gl3wProcs.gl.GenBuffers\n#define glGenFramebuffers                                    gl3wProcs.gl.GenFramebuffers\n#define glGenProgramPipelines                                gl3wProcs.gl.GenProgramPipelines\n#define glGenQueries                                         gl3wProcs.gl.GenQueries\n#define glGenRenderbuffers                                   gl3wProcs.gl.GenRenderbuffers\n#define glGenSamplers                                        gl3wProcs.gl.GenSamplers\n#define glGenTextures                                        gl3wProcs.gl.GenTextures\n#define glGenTransformFeedbacks                              gl3wProcs.gl.GenTransformFeedbacks\n#define glGenVertexArrays                                    gl3wProcs.gl.GenVertexArrays\n#define glGenerateMipmap                                     gl3wProcs.gl.GenerateMipmap\n#define glGenerateTextureMipmap                              gl3wProcs.gl.GenerateTextureMipmap\n#define glGetActiveAtomicCounterBufferiv                     gl3wProcs.gl.GetActiveAtomicCounterBufferiv\n#define glGetActiveAttrib                                    gl3wProcs.gl.GetActiveAttrib\n#define glGetActiveSubroutineName                            gl3wProcs.gl.GetActiveSubroutineName\n#define glGetActiveSubroutineUniformName                     gl3wProcs.gl.GetActiveSubroutineUniformName\n#define glGetActiveSubroutineUniformiv                       gl3wProcs.gl.GetActiveSubroutineUniformiv\n#define glGetActiveUniform                                   gl3wProcs.gl.GetActiveUniform\n#define glGetActiveUniformBlockName                          gl3wProcs.gl.GetActiveUniformBlockName\n#define glGetActiveUniformBlockiv                            gl3wProcs.gl.GetActiveUniformBlockiv\n#define glGetActiveUniformName                               gl3wProcs.gl.GetActiveUniformName\n#define glGetActiveUniformsiv                                gl3wProcs.gl.GetActiveUniformsiv\n#define glGetAttachedShaders                                 gl3wProcs.gl.GetAttachedShaders\n#define glGetAttribLocation                                  gl3wProcs.gl.GetAttribLocation\n#define glGetBooleani_v                                      gl3wProcs.gl.GetBooleani_v\n#define glGetBooleanv                                        gl3wProcs.gl.GetBooleanv\n#define glGetBufferParameteri64v                             gl3wProcs.gl.GetBufferParameteri64v\n#define glGetBufferParameteriv                               gl3wProcs.gl.GetBufferParameteriv\n#define glGetBufferPointerv                                  gl3wProcs.gl.GetBufferPointerv\n#define glGetBufferSubData                                   gl3wProcs.gl.GetBufferSubData\n#define glGetCompressedTexImage                              gl3wProcs.gl.GetCompressedTexImage\n#define glGetCompressedTextureImage                          gl3wProcs.gl.GetCompressedTextureImage\n#define glGetCompressedTextureSubImage                       gl3wProcs.gl.GetCompressedTextureSubImage\n#define glGetDebugMessageLog                                 gl3wProcs.gl.GetDebugMessageLog\n#define glGetDoublei_v                                       gl3wProcs.gl.GetDoublei_v\n#define glGetDoublev                                         gl3wProcs.gl.GetDoublev\n#define glGetError                                           gl3wProcs.gl.GetError\n#define glGetFloati_v                                        gl3wProcs.gl.GetFloati_v\n#define glGetFloatv                                          gl3wProcs.gl.GetFloatv\n#define glGetFragDataIndex                                   gl3wProcs.gl.GetFragDataIndex\n#define glGetFragDataLocation                                gl3wProcs.gl.GetFragDataLocation\n#define glGetFramebufferAttachmentParameteriv                gl3wProcs.gl.GetFramebufferAttachmentParameteriv\n#define glGetFramebufferParameteriv                          gl3wProcs.gl.GetFramebufferParameteriv\n#define glGetFramebufferParameterivMESA                      gl3wProcs.gl.GetFramebufferParameterivMESA\n#define glGetGraphicsResetStatus                             gl3wProcs.gl.GetGraphicsResetStatus\n#define glGetInteger64i_v                                    gl3wProcs.gl.GetInteger64i_v\n#define glGetInteger64v                                      gl3wProcs.gl.GetInteger64v\n#define glGetIntegeri_v                                      gl3wProcs.gl.GetIntegeri_v\n#define glGetIntegerv                                        gl3wProcs.gl.GetIntegerv\n#define glGetInternalformati64v                              gl3wProcs.gl.GetInternalformati64v\n#define glGetInternalformativ                                gl3wProcs.gl.GetInternalformativ\n#define glGetMultisamplefv                                   gl3wProcs.gl.GetMultisamplefv\n#define glGetNamedBufferParameteri64v                        gl3wProcs.gl.GetNamedBufferParameteri64v\n#define glGetNamedBufferParameteriv                          gl3wProcs.gl.GetNamedBufferParameteriv\n#define glGetNamedBufferPointerv                             gl3wProcs.gl.GetNamedBufferPointerv\n#define glGetNamedBufferSubData                              gl3wProcs.gl.GetNamedBufferSubData\n#define glGetNamedFramebufferAttachmentParameteriv           gl3wProcs.gl.GetNamedFramebufferAttachmentParameteriv\n#define glGetNamedFramebufferParameteriv                     gl3wProcs.gl.GetNamedFramebufferParameteriv\n#define glGetNamedRenderbufferParameteriv                    gl3wProcs.gl.GetNamedRenderbufferParameteriv\n#define glGetObjectLabel                                     gl3wProcs.gl.GetObjectLabel\n#define glGetObjectPtrLabel                                  gl3wProcs.gl.GetObjectPtrLabel\n#define glGetPointerv                                        gl3wProcs.gl.GetPointerv\n#define glGetProgramBinary                                   gl3wProcs.gl.GetProgramBinary\n#define glGetProgramInfoLog                                  gl3wProcs.gl.GetProgramInfoLog\n#define glGetProgramInterfaceiv                              gl3wProcs.gl.GetProgramInterfaceiv\n#define glGetProgramPipelineInfoLog                          gl3wProcs.gl.GetProgramPipelineInfoLog\n#define glGetProgramPipelineiv                               gl3wProcs.gl.GetProgramPipelineiv\n#define glGetProgramResourceIndex                            gl3wProcs.gl.GetProgramResourceIndex\n#define glGetProgramResourceLocation                         gl3wProcs.gl.GetProgramResourceLocation\n#define glGetProgramResourceLocationIndex                    gl3wProcs.gl.GetProgramResourceLocationIndex\n#define glGetProgramResourceName                             gl3wProcs.gl.GetProgramResourceName\n#define glGetProgramResourceiv                               gl3wProcs.gl.GetProgramResourceiv\n#define glGetProgramStageiv                                  gl3wProcs.gl.GetProgramStageiv\n#define glGetProgramiv                                       gl3wProcs.gl.GetProgramiv\n#define glGetQueryBufferObjecti64v                           gl3wProcs.gl.GetQueryBufferObjecti64v\n#define glGetQueryBufferObjectiv                             gl3wProcs.gl.GetQueryBufferObjectiv\n#define glGetQueryBufferObjectui64v                          gl3wProcs.gl.GetQueryBufferObjectui64v\n#define glGetQueryBufferObjectuiv                            gl3wProcs.gl.GetQueryBufferObjectuiv\n#define glGetQueryIndexediv                                  gl3wProcs.gl.GetQueryIndexediv\n#define glGetQueryObjecti64v                                 gl3wProcs.gl.GetQueryObjecti64v\n#define glGetQueryObjectiv                                   gl3wProcs.gl.GetQueryObjectiv\n#define glGetQueryObjectui64v                                gl3wProcs.gl.GetQueryObjectui64v\n#define glGetQueryObjectuiv                                  gl3wProcs.gl.GetQueryObjectuiv\n#define glGetQueryiv                                         gl3wProcs.gl.GetQueryiv\n#define glGetRenderbufferParameteriv                         gl3wProcs.gl.GetRenderbufferParameteriv\n#define glGetSamplerParameterIiv                             gl3wProcs.gl.GetSamplerParameterIiv\n#define glGetSamplerParameterIuiv                            gl3wProcs.gl.GetSamplerParameterIuiv\n#define glGetSamplerParameterfv                              gl3wProcs.gl.GetSamplerParameterfv\n#define glGetSamplerParameteriv                              gl3wProcs.gl.GetSamplerParameteriv\n#define glGetShaderInfoLog                                   gl3wProcs.gl.GetShaderInfoLog\n#define glGetShaderPrecisionFormat                           gl3wProcs.gl.GetShaderPrecisionFormat\n#define glGetShaderSource                                    gl3wProcs.gl.GetShaderSource\n#define glGetShaderiv                                        gl3wProcs.gl.GetShaderiv\n#define glGetString                                          gl3wProcs.gl.GetString\n#define glGetStringi                                         gl3wProcs.gl.GetStringi\n#define glGetSubroutineIndex                                 gl3wProcs.gl.GetSubroutineIndex\n#define glGetSubroutineUniformLocation                       gl3wProcs.gl.GetSubroutineUniformLocation\n#define glGetSynciv                                          gl3wProcs.gl.GetSynciv\n#define glGetTexImage                                        gl3wProcs.gl.GetTexImage\n#define glGetTexLevelParameterfv                             gl3wProcs.gl.GetTexLevelParameterfv\n#define glGetTexLevelParameteriv                             gl3wProcs.gl.GetTexLevelParameteriv\n#define glGetTexParameterIiv                                 gl3wProcs.gl.GetTexParameterIiv\n#define glGetTexParameterIuiv                                gl3wProcs.gl.GetTexParameterIuiv\n#define glGetTexParameterfv                                  gl3wProcs.gl.GetTexParameterfv\n#define glGetTexParameteriv                                  gl3wProcs.gl.GetTexParameteriv\n#define glGetTextureImage                                    gl3wProcs.gl.GetTextureImage\n#define glGetTextureLevelParameterfv                         gl3wProcs.gl.GetTextureLevelParameterfv\n#define glGetTextureLevelParameteriv                         gl3wProcs.gl.GetTextureLevelParameteriv\n#define glGetTextureParameterIiv                             gl3wProcs.gl.GetTextureParameterIiv\n#define glGetTextureParameterIuiv                            gl3wProcs.gl.GetTextureParameterIuiv\n#define glGetTextureParameterfv                              gl3wProcs.gl.GetTextureParameterfv\n#define glGetTextureParameteriv                              gl3wProcs.gl.GetTextureParameteriv\n#define glGetTextureSubImage                                 gl3wProcs.gl.GetTextureSubImage\n#define glGetTransformFeedbackVarying                        gl3wProcs.gl.GetTransformFeedbackVarying\n#define glGetTransformFeedbacki64_v                          gl3wProcs.gl.GetTransformFeedbacki64_v\n#define glGetTransformFeedbacki_v                            gl3wProcs.gl.GetTransformFeedbacki_v\n#define glGetTransformFeedbackiv                             gl3wProcs.gl.GetTransformFeedbackiv\n#define glGetUniformBlockIndex                               gl3wProcs.gl.GetUniformBlockIndex\n#define glGetUniformIndices                                  gl3wProcs.gl.GetUniformIndices\n#define glGetUniformLocation                                 gl3wProcs.gl.GetUniformLocation\n#define glGetUniformSubroutineuiv                            gl3wProcs.gl.GetUniformSubroutineuiv\n#define glGetUniformdv                                       gl3wProcs.gl.GetUniformdv\n#define glGetUniformfv                                       gl3wProcs.gl.GetUniformfv\n#define glGetUniformiv                                       gl3wProcs.gl.GetUniformiv\n#define glGetUniformuiv                                      gl3wProcs.gl.GetUniformuiv\n#define glGetVertexArrayIndexed64iv                          gl3wProcs.gl.GetVertexArrayIndexed64iv\n#define glGetVertexArrayIndexediv                            gl3wProcs.gl.GetVertexArrayIndexediv\n#define glGetVertexArrayiv                                   gl3wProcs.gl.GetVertexArrayiv\n#define glGetVertexAttribIiv                                 gl3wProcs.gl.GetVertexAttribIiv\n#define glGetVertexAttribIuiv                                gl3wProcs.gl.GetVertexAttribIuiv\n#define glGetVertexAttribLdv                                 gl3wProcs.gl.GetVertexAttribLdv\n#define glGetVertexAttribPointerv                            gl3wProcs.gl.GetVertexAttribPointerv\n#define glGetVertexAttribdv                                  gl3wProcs.gl.GetVertexAttribdv\n#define glGetVertexAttribfv                                  gl3wProcs.gl.GetVertexAttribfv\n#define glGetVertexAttribiv                                  gl3wProcs.gl.GetVertexAttribiv\n#define glGetnCompressedTexImage                             gl3wProcs.gl.GetnCompressedTexImage\n#define glGetnTexImage                                       gl3wProcs.gl.GetnTexImage\n#define glGetnUniformdv                                      gl3wProcs.gl.GetnUniformdv\n#define glGetnUniformfv                                      gl3wProcs.gl.GetnUniformfv\n#define glGetnUniformiv                                      gl3wProcs.gl.GetnUniformiv\n#define glGetnUniformuiv                                     gl3wProcs.gl.GetnUniformuiv\n#define glHint                                               gl3wProcs.gl.Hint\n#define glInvalidateBufferData                               gl3wProcs.gl.InvalidateBufferData\n#define glInvalidateBufferSubData                            gl3wProcs.gl.InvalidateBufferSubData\n#define glInvalidateFramebuffer                              gl3wProcs.gl.InvalidateFramebuffer\n#define glInvalidateNamedFramebufferData                     gl3wProcs.gl.InvalidateNamedFramebufferData\n#define glInvalidateNamedFramebufferSubData                  gl3wProcs.gl.InvalidateNamedFramebufferSubData\n#define glInvalidateSubFramebuffer                           gl3wProcs.gl.InvalidateSubFramebuffer\n#define glInvalidateTexImage                                 gl3wProcs.gl.InvalidateTexImage\n#define glInvalidateTexSubImage                              gl3wProcs.gl.InvalidateTexSubImage\n#define glIsBuffer                                           gl3wProcs.gl.IsBuffer\n#define glIsEnabled                                          gl3wProcs.gl.IsEnabled\n#define glIsEnabledi                                         gl3wProcs.gl.IsEnabledi\n#define glIsFramebuffer                                      gl3wProcs.gl.IsFramebuffer\n#define glIsProgram                                          gl3wProcs.gl.IsProgram\n#define glIsProgramPipeline                                  gl3wProcs.gl.IsProgramPipeline\n#define glIsQuery                                            gl3wProcs.gl.IsQuery\n#define glIsRenderbuffer                                     gl3wProcs.gl.IsRenderbuffer\n#define glIsSampler                                          gl3wProcs.gl.IsSampler\n#define glIsShader                                           gl3wProcs.gl.IsShader\n#define glIsSync                                             gl3wProcs.gl.IsSync\n#define glIsTexture                                          gl3wProcs.gl.IsTexture\n#define glIsTransformFeedback                                gl3wProcs.gl.IsTransformFeedback\n#define glIsVertexArray                                      gl3wProcs.gl.IsVertexArray\n#define glLineWidth                                          gl3wProcs.gl.LineWidth\n#define glLinkProgram                                        gl3wProcs.gl.LinkProgram\n#define glLogicOp                                            gl3wProcs.gl.LogicOp\n#define glMapBuffer                                          gl3wProcs.gl.MapBuffer\n#define glMapBufferRange                                     gl3wProcs.gl.MapBufferRange\n#define glMapNamedBuffer                                     gl3wProcs.gl.MapNamedBuffer\n#define glMapNamedBufferRange                                gl3wProcs.gl.MapNamedBufferRange\n#define glMemoryBarrier                                      gl3wProcs.gl.MemoryBarrier\n#define glMemoryBarrierByRegion                              gl3wProcs.gl.MemoryBarrierByRegion\n#define glMinSampleShading                                   gl3wProcs.gl.MinSampleShading\n#define glMultiDrawArrays                                    gl3wProcs.gl.MultiDrawArrays\n#define glMultiDrawArraysIndirect                            gl3wProcs.gl.MultiDrawArraysIndirect\n#define glMultiDrawArraysIndirectCount                       gl3wProcs.gl.MultiDrawArraysIndirectCount\n#define glMultiDrawElements                                  gl3wProcs.gl.MultiDrawElements\n#define glMultiDrawElementsBaseVertex                        gl3wProcs.gl.MultiDrawElementsBaseVertex\n#define glMultiDrawElementsIndirect                          gl3wProcs.gl.MultiDrawElementsIndirect\n#define glMultiDrawElementsIndirectCount                     gl3wProcs.gl.MultiDrawElementsIndirectCount\n#define glNamedBufferData                                    gl3wProcs.gl.NamedBufferData\n#define glNamedBufferStorage                                 gl3wProcs.gl.NamedBufferStorage\n#define glNamedBufferSubData                                 gl3wProcs.gl.NamedBufferSubData\n#define glNamedFramebufferDrawBuffer                         gl3wProcs.gl.NamedFramebufferDrawBuffer\n#define glNamedFramebufferDrawBuffers                        gl3wProcs.gl.NamedFramebufferDrawBuffers\n#define glNamedFramebufferParameteri                         gl3wProcs.gl.NamedFramebufferParameteri\n#define glNamedFramebufferReadBuffer                         gl3wProcs.gl.NamedFramebufferReadBuffer\n#define glNamedFramebufferRenderbuffer                       gl3wProcs.gl.NamedFramebufferRenderbuffer\n#define glNamedFramebufferTexture                            gl3wProcs.gl.NamedFramebufferTexture\n#define glNamedFramebufferTextureLayer                       gl3wProcs.gl.NamedFramebufferTextureLayer\n#define glNamedRenderbufferStorage                           gl3wProcs.gl.NamedRenderbufferStorage\n#define glNamedRenderbufferStorageMultisample                gl3wProcs.gl.NamedRenderbufferStorageMultisample\n#define glObjectLabel                                        gl3wProcs.gl.ObjectLabel\n#define glObjectPtrLabel                                     gl3wProcs.gl.ObjectPtrLabel\n#define glPatchParameterfv                                   gl3wProcs.gl.PatchParameterfv\n#define glPatchParameteri                                    gl3wProcs.gl.PatchParameteri\n#define glPauseTransformFeedback                             gl3wProcs.gl.PauseTransformFeedback\n#define glPixelStoref                                        gl3wProcs.gl.PixelStoref\n#define glPixelStorei                                        gl3wProcs.gl.PixelStorei\n#define glPointParameterf                                    gl3wProcs.gl.PointParameterf\n#define glPointParameterfv                                   gl3wProcs.gl.PointParameterfv\n#define glPointParameteri                                    gl3wProcs.gl.PointParameteri\n#define glPointParameteriv                                   gl3wProcs.gl.PointParameteriv\n#define glPointSize                                          gl3wProcs.gl.PointSize\n#define glPolygonMode                                        gl3wProcs.gl.PolygonMode\n#define glPolygonOffset                                      gl3wProcs.gl.PolygonOffset\n#define glPolygonOffsetClamp                                 gl3wProcs.gl.PolygonOffsetClamp\n#define glPopDebugGroup                                      gl3wProcs.gl.PopDebugGroup\n#define glPrimitiveRestartIndex                              gl3wProcs.gl.PrimitiveRestartIndex\n#define glProgramBinary                                      gl3wProcs.gl.ProgramBinary\n#define glProgramParameteri                                  gl3wProcs.gl.ProgramParameteri\n#define glProgramUniform1d                                   gl3wProcs.gl.ProgramUniform1d\n#define glProgramUniform1dv                                  gl3wProcs.gl.ProgramUniform1dv\n#define glProgramUniform1f                                   gl3wProcs.gl.ProgramUniform1f\n#define glProgramUniform1fv                                  gl3wProcs.gl.ProgramUniform1fv\n#define glProgramUniform1i                                   gl3wProcs.gl.ProgramUniform1i\n#define glProgramUniform1iv                                  gl3wProcs.gl.ProgramUniform1iv\n#define glProgramUniform1ui                                  gl3wProcs.gl.ProgramUniform1ui\n#define glProgramUniform1uiv                                 gl3wProcs.gl.ProgramUniform1uiv\n#define glProgramUniform2d                                   gl3wProcs.gl.ProgramUniform2d\n#define glProgramUniform2dv                                  gl3wProcs.gl.ProgramUniform2dv\n#define glProgramUniform2f                                   gl3wProcs.gl.ProgramUniform2f\n#define glProgramUniform2fv                                  gl3wProcs.gl.ProgramUniform2fv\n#define glProgramUniform2i                                   gl3wProcs.gl.ProgramUniform2i\n#define glProgramUniform2iv                                  gl3wProcs.gl.ProgramUniform2iv\n#define glProgramUniform2ui                                  gl3wProcs.gl.ProgramUniform2ui\n#define glProgramUniform2uiv                                 gl3wProcs.gl.ProgramUniform2uiv\n#define glProgramUniform3d                                   gl3wProcs.gl.ProgramUniform3d\n#define glProgramUniform3dv                                  gl3wProcs.gl.ProgramUniform3dv\n#define glProgramUniform3f                                   gl3wProcs.gl.ProgramUniform3f\n#define glProgramUniform3fv                                  gl3wProcs.gl.ProgramUniform3fv\n#define glProgramUniform3i                                   gl3wProcs.gl.ProgramUniform3i\n#define glProgramUniform3iv                                  gl3wProcs.gl.ProgramUniform3iv\n#define glProgramUniform3ui                                  gl3wProcs.gl.ProgramUniform3ui\n#define glProgramUniform3uiv                                 gl3wProcs.gl.ProgramUniform3uiv\n#define glProgramUniform4d                                   gl3wProcs.gl.ProgramUniform4d\n#define glProgramUniform4dv                                  gl3wProcs.gl.ProgramUniform4dv\n#define glProgramUniform4f                                   gl3wProcs.gl.ProgramUniform4f\n#define glProgramUniform4fv                                  gl3wProcs.gl.ProgramUniform4fv\n#define glProgramUniform4i                                   gl3wProcs.gl.ProgramUniform4i\n#define glProgramUniform4iv                                  gl3wProcs.gl.ProgramUniform4iv\n#define glProgramUniform4ui                                  gl3wProcs.gl.ProgramUniform4ui\n#define glProgramUniform4uiv                                 gl3wProcs.gl.ProgramUniform4uiv\n#define glProgramUniformMatrix2dv                            gl3wProcs.gl.ProgramUniformMatrix2dv\n#define glProgramUniformMatrix2fv                            gl3wProcs.gl.ProgramUniformMatrix2fv\n#define glProgramUniformMatrix2x3dv                          gl3wProcs.gl.ProgramUniformMatrix2x3dv\n#define glProgramUniformMatrix2x3fv                          gl3wProcs.gl.ProgramUniformMatrix2x3fv\n#define glProgramUniformMatrix2x4dv                          gl3wProcs.gl.ProgramUniformMatrix2x4dv\n#define glProgramUniformMatrix2x4fv                          gl3wProcs.gl.ProgramUniformMatrix2x4fv\n#define glProgramUniformMatrix3dv                            gl3wProcs.gl.ProgramUniformMatrix3dv\n#define glProgramUniformMatrix3fv                            gl3wProcs.gl.ProgramUniformMatrix3fv\n#define glProgramUniformMatrix3x2dv                          gl3wProcs.gl.ProgramUniformMatrix3x2dv\n#define glProgramUniformMatrix3x2fv                          gl3wProcs.gl.ProgramUniformMatrix3x2fv\n#define glProgramUniformMatrix3x4dv                          gl3wProcs.gl.ProgramUniformMatrix3x4dv\n#define glProgramUniformMatrix3x4fv                          gl3wProcs.gl.ProgramUniformMatrix3x4fv\n#define glProgramUniformMatrix4dv                            gl3wProcs.gl.ProgramUniformMatrix4dv\n#define glProgramUniformMatrix4fv                            gl3wProcs.gl.ProgramUniformMatrix4fv\n#define glProgramUniformMatrix4x2dv                          gl3wProcs.gl.ProgramUniformMatrix4x2dv\n#define glProgramUniformMatrix4x2fv                          gl3wProcs.gl.ProgramUniformMatrix4x2fv\n#define glProgramUniformMatrix4x3dv                          gl3wProcs.gl.ProgramUniformMatrix4x3dv\n#define glProgramUniformMatrix4x3fv                          gl3wProcs.gl.ProgramUniformMatrix4x3fv\n#define glProvokingVertex                                    gl3wProcs.gl.ProvokingVertex\n#define glPushDebugGroup                                     gl3wProcs.gl.PushDebugGroup\n#define glQueryCounter                                       gl3wProcs.gl.QueryCounter\n#define glReadBuffer                                         gl3wProcs.gl.ReadBuffer\n#define glReadPixels                                         gl3wProcs.gl.ReadPixels\n#define glReadnPixels                                        gl3wProcs.gl.ReadnPixels\n#define glReleaseShaderCompiler                              gl3wProcs.gl.ReleaseShaderCompiler\n#define glRenderbufferStorage                                gl3wProcs.gl.RenderbufferStorage\n#define glRenderbufferStorageMultisample                     gl3wProcs.gl.RenderbufferStorageMultisample\n#define glResumeTransformFeedback                            gl3wProcs.gl.ResumeTransformFeedback\n#define glSampleCoverage                                     gl3wProcs.gl.SampleCoverage\n#define glSampleMaski                                        gl3wProcs.gl.SampleMaski\n#define glSamplerParameterIiv                                gl3wProcs.gl.SamplerParameterIiv\n#define glSamplerParameterIuiv                               gl3wProcs.gl.SamplerParameterIuiv\n#define glSamplerParameterf                                  gl3wProcs.gl.SamplerParameterf\n#define glSamplerParameterfv                                 gl3wProcs.gl.SamplerParameterfv\n#define glSamplerParameteri                                  gl3wProcs.gl.SamplerParameteri\n#define glSamplerParameteriv                                 gl3wProcs.gl.SamplerParameteriv\n#define glScissor                                            gl3wProcs.gl.Scissor\n#define glScissorArrayv                                      gl3wProcs.gl.ScissorArrayv\n#define glScissorIndexed                                     gl3wProcs.gl.ScissorIndexed\n#define glScissorIndexedv                                    gl3wProcs.gl.ScissorIndexedv\n#define glShaderBinary                                       gl3wProcs.gl.ShaderBinary\n#define glShaderSource                                       gl3wProcs.gl.ShaderSource\n#define glShaderStorageBlockBinding                          gl3wProcs.gl.ShaderStorageBlockBinding\n#define glSpecializeShader                                   gl3wProcs.gl.SpecializeShader\n#define glStencilFunc                                        gl3wProcs.gl.StencilFunc\n#define glStencilFuncSeparate                                gl3wProcs.gl.StencilFuncSeparate\n#define glStencilMask                                        gl3wProcs.gl.StencilMask\n#define glStencilMaskSeparate                                gl3wProcs.gl.StencilMaskSeparate\n#define glStencilOp                                          gl3wProcs.gl.StencilOp\n#define glStencilOpSeparate                                  gl3wProcs.gl.StencilOpSeparate\n#define glTexBuffer                                          gl3wProcs.gl.TexBuffer\n#define glTexBufferRange                                     gl3wProcs.gl.TexBufferRange\n#define glTexImage1D                                         gl3wProcs.gl.TexImage1D\n#define glTexImage2D                                         gl3wProcs.gl.TexImage2D\n#define glTexImage2DMultisample                              gl3wProcs.gl.TexImage2DMultisample\n#define glTexImage3D                                         gl3wProcs.gl.TexImage3D\n#define glTexImage3DMultisample                              gl3wProcs.gl.TexImage3DMultisample\n#define glTexParameterIiv                                    gl3wProcs.gl.TexParameterIiv\n#define glTexParameterIuiv                                   gl3wProcs.gl.TexParameterIuiv\n#define glTexParameterf                                      gl3wProcs.gl.TexParameterf\n#define glTexParameterfv                                     gl3wProcs.gl.TexParameterfv\n#define glTexParameteri                                      gl3wProcs.gl.TexParameteri\n#define glTexParameteriv                                     gl3wProcs.gl.TexParameteriv\n#define glTexStorage1D                                       gl3wProcs.gl.TexStorage1D\n#define glTexStorage2D                                       gl3wProcs.gl.TexStorage2D\n#define glTexStorage2DMultisample                            gl3wProcs.gl.TexStorage2DMultisample\n#define glTexStorage3D                                       gl3wProcs.gl.TexStorage3D\n#define glTexStorage3DMultisample                            gl3wProcs.gl.TexStorage3DMultisample\n#define glTexSubImage1D                                      gl3wProcs.gl.TexSubImage1D\n#define glTexSubImage2D                                      gl3wProcs.gl.TexSubImage2D\n#define glTexSubImage3D                                      gl3wProcs.gl.TexSubImage3D\n#define glTextureBarrier                                     gl3wProcs.gl.TextureBarrier\n#define glTextureBuffer                                      gl3wProcs.gl.TextureBuffer\n#define glTextureBufferRange                                 gl3wProcs.gl.TextureBufferRange\n#define glTextureParameterIiv                                gl3wProcs.gl.TextureParameterIiv\n#define glTextureParameterIuiv                               gl3wProcs.gl.TextureParameterIuiv\n#define glTextureParameterf                                  gl3wProcs.gl.TextureParameterf\n#define glTextureParameterfv                                 gl3wProcs.gl.TextureParameterfv\n#define glTextureParameteri                                  gl3wProcs.gl.TextureParameteri\n#define glTextureParameteriv                                 gl3wProcs.gl.TextureParameteriv\n#define glTextureStorage1D                                   gl3wProcs.gl.TextureStorage1D\n#define glTextureStorage2D                                   gl3wProcs.gl.TextureStorage2D\n#define glTextureStorage2DMultisample                        gl3wProcs.gl.TextureStorage2DMultisample\n#define glTextureStorage3D                                   gl3wProcs.gl.TextureStorage3D\n#define glTextureStorage3DMultisample                        gl3wProcs.gl.TextureStorage3DMultisample\n#define glTextureSubImage1D                                  gl3wProcs.gl.TextureSubImage1D\n#define glTextureSubImage2D                                  gl3wProcs.gl.TextureSubImage2D\n#define glTextureSubImage3D                                  gl3wProcs.gl.TextureSubImage3D\n#define glTextureView                                        gl3wProcs.gl.TextureView\n#define glTransformFeedbackBufferBase                        gl3wProcs.gl.TransformFeedbackBufferBase\n#define glTransformFeedbackBufferRange                       gl3wProcs.gl.TransformFeedbackBufferRange\n#define glTransformFeedbackVaryings                          gl3wProcs.gl.TransformFeedbackVaryings\n#define glUniform1d                                          gl3wProcs.gl.Uniform1d\n#define glUniform1dv                                         gl3wProcs.gl.Uniform1dv\n#define glUniform1f                                          gl3wProcs.gl.Uniform1f\n#define glUniform1fv                                         gl3wProcs.gl.Uniform1fv\n#define glUniform1i                                          gl3wProcs.gl.Uniform1i\n#define glUniform1iv                                         gl3wProcs.gl.Uniform1iv\n#define glUniform1ui                                         gl3wProcs.gl.Uniform1ui\n#define glUniform1uiv                                        gl3wProcs.gl.Uniform1uiv\n#define glUniform2d                                          gl3wProcs.gl.Uniform2d\n#define glUniform2dv                                         gl3wProcs.gl.Uniform2dv\n#define glUniform2f                                          gl3wProcs.gl.Uniform2f\n#define glUniform2fv                                         gl3wProcs.gl.Uniform2fv\n#define glUniform2i                                          gl3wProcs.gl.Uniform2i\n#define glUniform2iv                                         gl3wProcs.gl.Uniform2iv\n#define glUniform2ui                                         gl3wProcs.gl.Uniform2ui\n#define glUniform2uiv                                        gl3wProcs.gl.Uniform2uiv\n#define glUniform3d                                          gl3wProcs.gl.Uniform3d\n#define glUniform3dv                                         gl3wProcs.gl.Uniform3dv\n#define glUniform3f                                          gl3wProcs.gl.Uniform3f\n#define glUniform3fv                                         gl3wProcs.gl.Uniform3fv\n#define glUniform3i                                          gl3wProcs.gl.Uniform3i\n#define glUniform3iv                                         gl3wProcs.gl.Uniform3iv\n#define glUniform3ui                                         gl3wProcs.gl.Uniform3ui\n#define glUniform3uiv                                        gl3wProcs.gl.Uniform3uiv\n#define glUniform4d                                          gl3wProcs.gl.Uniform4d\n#define glUniform4dv                                         gl3wProcs.gl.Uniform4dv\n#define glUniform4f                                          gl3wProcs.gl.Uniform4f\n#define glUniform4fv                                         gl3wProcs.gl.Uniform4fv\n#define glUniform4i                                          gl3wProcs.gl.Uniform4i\n#define glUniform4iv                                         gl3wProcs.gl.Uniform4iv\n#define glUniform4ui                                         gl3wProcs.gl.Uniform4ui\n#define glUniform4uiv                                        gl3wProcs.gl.Uniform4uiv\n#define glUniformBlockBinding                                gl3wProcs.gl.UniformBlockBinding\n#define glUniformMatrix2dv                                   gl3wProcs.gl.UniformMatrix2dv\n#define glUniformMatrix2fv                                   gl3wProcs.gl.UniformMatrix2fv\n#define glUniformMatrix2x3dv                                 gl3wProcs.gl.UniformMatrix2x3dv\n#define glUniformMatrix2x3fv                                 gl3wProcs.gl.UniformMatrix2x3fv\n#define glUniformMatrix2x4dv                                 gl3wProcs.gl.UniformMatrix2x4dv\n#define glUniformMatrix2x4fv                                 gl3wProcs.gl.UniformMatrix2x4fv\n#define glUniformMatrix3dv                                   gl3wProcs.gl.UniformMatrix3dv\n#define glUniformMatrix3fv                                   gl3wProcs.gl.UniformMatrix3fv\n#define glUniformMatrix3x2dv                                 gl3wProcs.gl.UniformMatrix3x2dv\n#define glUniformMatrix3x2fv                                 gl3wProcs.gl.UniformMatrix3x2fv\n#define glUniformMatrix3x4dv                                 gl3wProcs.gl.UniformMatrix3x4dv\n#define glUniformMatrix3x4fv                                 gl3wProcs.gl.UniformMatrix3x4fv\n#define glUniformMatrix4dv                                   gl3wProcs.gl.UniformMatrix4dv\n#define glUniformMatrix4fv                                   gl3wProcs.gl.UniformMatrix4fv\n#define glUniformMatrix4x2dv                                 gl3wProcs.gl.UniformMatrix4x2dv\n#define glUniformMatrix4x2fv                                 gl3wProcs.gl.UniformMatrix4x2fv\n#define glUniformMatrix4x3dv                                 gl3wProcs.gl.UniformMatrix4x3dv\n#define glUniformMatrix4x3fv                                 gl3wProcs.gl.UniformMatrix4x3fv\n#define glUniformSubroutinesuiv                              gl3wProcs.gl.UniformSubroutinesuiv\n#define glUnmapBuffer                                        gl3wProcs.gl.UnmapBuffer\n#define glUnmapNamedBuffer                                   gl3wProcs.gl.UnmapNamedBuffer\n#define glUseProgram                                         gl3wProcs.gl.UseProgram\n#define glUseProgramStages                                   gl3wProcs.gl.UseProgramStages\n#define glValidateProgram                                    gl3wProcs.gl.ValidateProgram\n#define glValidateProgramPipeline                            gl3wProcs.gl.ValidateProgramPipeline\n#define glVertexArrayAttribBinding                           gl3wProcs.gl.VertexArrayAttribBinding\n#define glVertexArrayAttribFormat                            gl3wProcs.gl.VertexArrayAttribFormat\n#define glVertexArrayAttribIFormat                           gl3wProcs.gl.VertexArrayAttribIFormat\n#define glVertexArrayAttribLFormat                           gl3wProcs.gl.VertexArrayAttribLFormat\n#define glVertexArrayBindingDivisor                          gl3wProcs.gl.VertexArrayBindingDivisor\n#define glVertexArrayElementBuffer                           gl3wProcs.gl.VertexArrayElementBuffer\n#define glVertexArrayVertexBuffer                            gl3wProcs.gl.VertexArrayVertexBuffer\n#define glVertexArrayVertexBuffers                           gl3wProcs.gl.VertexArrayVertexBuffers\n#define glVertexAttrib1d                                     gl3wProcs.gl.VertexAttrib1d\n#define glVertexAttrib1dv                                    gl3wProcs.gl.VertexAttrib1dv\n#define glVertexAttrib1f                                     gl3wProcs.gl.VertexAttrib1f\n#define glVertexAttrib1fv                                    gl3wProcs.gl.VertexAttrib1fv\n#define glVertexAttrib1s                                     gl3wProcs.gl.VertexAttrib1s\n#define glVertexAttrib1sv                                    gl3wProcs.gl.VertexAttrib1sv\n#define glVertexAttrib2d                                     gl3wProcs.gl.VertexAttrib2d\n#define glVertexAttrib2dv                                    gl3wProcs.gl.VertexAttrib2dv\n#define glVertexAttrib2f                                     gl3wProcs.gl.VertexAttrib2f\n#define glVertexAttrib2fv                                    gl3wProcs.gl.VertexAttrib2fv\n#define glVertexAttrib2s                                     gl3wProcs.gl.VertexAttrib2s\n#define glVertexAttrib2sv                                    gl3wProcs.gl.VertexAttrib2sv\n#define glVertexAttrib3d                                     gl3wProcs.gl.VertexAttrib3d\n#define glVertexAttrib3dv                                    gl3wProcs.gl.VertexAttrib3dv\n#define glVertexAttrib3f                                     gl3wProcs.gl.VertexAttrib3f\n#define glVertexAttrib3fv                                    gl3wProcs.gl.VertexAttrib3fv\n#define glVertexAttrib3s                                     gl3wProcs.gl.VertexAttrib3s\n#define glVertexAttrib3sv                                    gl3wProcs.gl.VertexAttrib3sv\n#define glVertexAttrib4Nbv                                   gl3wProcs.gl.VertexAttrib4Nbv\n#define glVertexAttrib4Niv                                   gl3wProcs.gl.VertexAttrib4Niv\n#define glVertexAttrib4Nsv                                   gl3wProcs.gl.VertexAttrib4Nsv\n#define glVertexAttrib4Nub                                   gl3wProcs.gl.VertexAttrib4Nub\n#define glVertexAttrib4Nubv                                  gl3wProcs.gl.VertexAttrib4Nubv\n#define glVertexAttrib4Nuiv                                  gl3wProcs.gl.VertexAttrib4Nuiv\n#define glVertexAttrib4Nusv                                  gl3wProcs.gl.VertexAttrib4Nusv\n#define glVertexAttrib4bv                                    gl3wProcs.gl.VertexAttrib4bv\n#define glVertexAttrib4d                                     gl3wProcs.gl.VertexAttrib4d\n#define glVertexAttrib4dv                                    gl3wProcs.gl.VertexAttrib4dv\n#define glVertexAttrib4f                                     gl3wProcs.gl.VertexAttrib4f\n#define glVertexAttrib4fv                                    gl3wProcs.gl.VertexAttrib4fv\n#define glVertexAttrib4iv                                    gl3wProcs.gl.VertexAttrib4iv\n#define glVertexAttrib4s                                     gl3wProcs.gl.VertexAttrib4s\n#define glVertexAttrib4sv                                    gl3wProcs.gl.VertexAttrib4sv\n#define glVertexAttrib4ubv                                   gl3wProcs.gl.VertexAttrib4ubv\n#define glVertexAttrib4uiv                                   gl3wProcs.gl.VertexAttrib4uiv\n#define glVertexAttrib4usv                                   gl3wProcs.gl.VertexAttrib4usv\n#define glVertexAttribBinding                                gl3wProcs.gl.VertexAttribBinding\n#define glVertexAttribDivisor                                gl3wProcs.gl.VertexAttribDivisor\n#define glVertexAttribFormat                                 gl3wProcs.gl.VertexAttribFormat\n#define glVertexAttribI1i                                    gl3wProcs.gl.VertexAttribI1i\n#define glVertexAttribI1iv                                   gl3wProcs.gl.VertexAttribI1iv\n#define glVertexAttribI1ui                                   gl3wProcs.gl.VertexAttribI1ui\n#define glVertexAttribI1uiv                                  gl3wProcs.gl.VertexAttribI1uiv\n#define glVertexAttribI2i                                    gl3wProcs.gl.VertexAttribI2i\n#define glVertexAttribI2iv                                   gl3wProcs.gl.VertexAttribI2iv\n#define glVertexAttribI2ui                                   gl3wProcs.gl.VertexAttribI2ui\n#define glVertexAttribI2uiv                                  gl3wProcs.gl.VertexAttribI2uiv\n#define glVertexAttribI3i                                    gl3wProcs.gl.VertexAttribI3i\n#define glVertexAttribI3iv                                   gl3wProcs.gl.VertexAttribI3iv\n#define glVertexAttribI3ui                                   gl3wProcs.gl.VertexAttribI3ui\n#define glVertexAttribI3uiv                                  gl3wProcs.gl.VertexAttribI3uiv\n#define glVertexAttribI4bv                                   gl3wProcs.gl.VertexAttribI4bv\n#define glVertexAttribI4i                                    gl3wProcs.gl.VertexAttribI4i\n#define glVertexAttribI4iv                                   gl3wProcs.gl.VertexAttribI4iv\n#define glVertexAttribI4sv                                   gl3wProcs.gl.VertexAttribI4sv\n#define glVertexAttribI4ubv                                  gl3wProcs.gl.VertexAttribI4ubv\n#define glVertexAttribI4ui                                   gl3wProcs.gl.VertexAttribI4ui\n#define glVertexAttribI4uiv                                  gl3wProcs.gl.VertexAttribI4uiv\n#define glVertexAttribI4usv                                  gl3wProcs.gl.VertexAttribI4usv\n#define glVertexAttribIFormat                                gl3wProcs.gl.VertexAttribIFormat\n#define glVertexAttribIPointer                               gl3wProcs.gl.VertexAttribIPointer\n#define glVertexAttribL1d                                    gl3wProcs.gl.VertexAttribL1d\n#define glVertexAttribL1dv                                   gl3wProcs.gl.VertexAttribL1dv\n#define glVertexAttribL2d                                    gl3wProcs.gl.VertexAttribL2d\n#define glVertexAttribL2dv                                   gl3wProcs.gl.VertexAttribL2dv\n#define glVertexAttribL3d                                    gl3wProcs.gl.VertexAttribL3d\n#define glVertexAttribL3dv                                   gl3wProcs.gl.VertexAttribL3dv\n#define glVertexAttribL4d                                    gl3wProcs.gl.VertexAttribL4d\n#define glVertexAttribL4dv                                   gl3wProcs.gl.VertexAttribL4dv\n#define glVertexAttribLFormat                                gl3wProcs.gl.VertexAttribLFormat\n#define glVertexAttribLPointer                               gl3wProcs.gl.VertexAttribLPointer\n#define glVertexAttribP1ui                                   gl3wProcs.gl.VertexAttribP1ui\n#define glVertexAttribP1uiv                                  gl3wProcs.gl.VertexAttribP1uiv\n#define glVertexAttribP2ui                                   gl3wProcs.gl.VertexAttribP2ui\n#define glVertexAttribP2uiv                                  gl3wProcs.gl.VertexAttribP2uiv\n#define glVertexAttribP3ui                                   gl3wProcs.gl.VertexAttribP3ui\n#define glVertexAttribP3uiv                                  gl3wProcs.gl.VertexAttribP3uiv\n#define glVertexAttribP4ui                                   gl3wProcs.gl.VertexAttribP4ui\n#define glVertexAttribP4uiv                                  gl3wProcs.gl.VertexAttribP4uiv\n#define glVertexAttribPointer                                gl3wProcs.gl.VertexAttribPointer\n#define glVertexBindingDivisor                               gl3wProcs.gl.VertexBindingDivisor\n#define glViewport                                           gl3wProcs.gl.Viewport\n#define glViewportArrayv                                     gl3wProcs.gl.ViewportArrayv\n#define glViewportIndexedf                                   gl3wProcs.gl.ViewportIndexedf\n#define glViewportIndexedfv                                  gl3wProcs.gl.ViewportIndexedfv\n#define glWaitSync                                           gl3wProcs.gl.WaitSync\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n#ifdef IMGL3W_IMPL\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdlib.h>\n\n#define ARRAY_SIZE(x)  (sizeof(x) / sizeof((x)[0]))\n\n#if defined(_WIN32)\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN 1\n#endif\n#include <windows.h>\n\nstatic HMODULE libgl;\ntypedef PROC(__stdcall* GL3WglGetProcAddr)(LPCSTR);\nstatic GL3WglGetProcAddr wgl_get_proc_address;\n\nstatic int open_libgl(void)\n{\n    libgl = LoadLibraryA(\"opengl32.dll\");\n    if (!libgl)\n        return GL3W_ERROR_LIBRARY_OPEN;\n    wgl_get_proc_address = (GL3WglGetProcAddr)GetProcAddress(libgl, \"wglGetProcAddress\");\n    return GL3W_OK;\n}\n\nstatic void close_libgl(void) { FreeLibrary(libgl); }\nstatic GL3WglProc get_proc(const char *proc)\n{\n    GL3WglProc res;\n    res = (GL3WglProc)wgl_get_proc_address(proc);\n    if (!res)\n        res = (GL3WglProc)GetProcAddress(libgl, proc);\n    return res;\n}\n#elif defined(__APPLE__)\n#include <dlfcn.h>\n\nstatic void *libgl;\nstatic int open_libgl(void)\n{\n    libgl = dlopen(\"/System/Library/Frameworks/OpenGL.framework/OpenGL\", RTLD_LAZY | RTLD_LOCAL);\n    if (!libgl)\n        return GL3W_ERROR_LIBRARY_OPEN;\n    return GL3W_OK;\n}\n\nstatic void close_libgl(void) { dlclose(libgl); }\n\nstatic GL3WglProc get_proc(const char *proc)\n{\n    GL3WglProc res;\n    *(void **)(&res) = dlsym(libgl, proc);\n    return res;\n}\n#else\n#include <dlfcn.h>\n\nstatic void *libgl;\nstatic GL3WglProc (*glx_get_proc_address)(const GLubyte *);\n\nstatic int open_libgl(void)\n{\n    libgl = dlopen(\"libGL.so.1\", RTLD_LAZY | RTLD_LOCAL);\n    if (!libgl)\n        return GL3W_ERROR_LIBRARY_OPEN;\n    *(void **)(&glx_get_proc_address) = dlsym(libgl, \"glXGetProcAddressARB\");\n    return GL3W_OK;\n}\n\nstatic void close_libgl(void) {\n    if (!libgl)\n        return;\n\n    dlclose(libgl);\n    libgl = NULL;\n}\n\nstatic GL3WglProc get_proc(const char *proc)\n{\n    GL3WglProc res;\n    res = glx_get_proc_address((const GLubyte *)proc);\n    if (!res)\n        *(void **)(&res) = dlsym(libgl, proc);\n    return res;\n}\n#endif\n\nstatic struct { int major, minor; } version;\n\nstatic int parse_version(void)\n{\n    if (!glGetIntegerv)\n        return GL3W_ERROR_INIT;\n    glGetIntegerv(GL_MAJOR_VERSION, &version.major);\n    glGetIntegerv(GL_MINOR_VERSION, &version.minor);\n    if (version.major < 3)\n        return GL3W_ERROR_OPENGL_VERSION;\n    return GL3W_OK;\n}\n\nstatic void load_procs(GL3WGetProcAddressProc proc);\n\nint imgl3wInit(void)\n{\n    int res = open_libgl();\n    if (res)\n        return res;\n    atexit(close_libgl);\n    return imgl3wInit2(get_proc);\n}\n\nint imgl3wInit2(GL3WGetProcAddressProc proc)\n{\n    load_procs(proc);\n    return parse_version();\n}\n\nvoid imgl3wShutdown(void)\n{\n    close_libgl();\n    gl3wProcs = {};\n}\n\nint imgl3wIsSupported(int major, int minor)\n{\n    if (major < 3)\n        return 0;\n    if (version.major == major)\n        return version.minor >= minor;\n    return version.major >= major;\n}\n\nGL3WglProc imgl3wGetProcAddress(const char *proc) { return get_proc(proc); }\n\nstatic const char *proc_names[] = {\n    \"glActiveShaderProgram\",\n    \"glActiveTexture\",\n    \"glAttachShader\",\n    \"glBeginConditionalRender\",\n    \"glBeginQuery\",\n    \"glBeginQueryIndexed\",\n    \"glBeginTransformFeedback\",\n    \"glBindAttribLocation\",\n    \"glBindBuffer\",\n    \"glBindBufferBase\",\n    \"glBindBufferRange\",\n    \"glBindBuffersBase\",\n    \"glBindBuffersRange\",\n    \"glBindFragDataLocation\",\n    \"glBindFragDataLocationIndexed\",\n    \"glBindFramebuffer\",\n    \"glBindImageTexture\",\n    \"glBindImageTextures\",\n    \"glBindProgramPipeline\",\n    \"glBindRenderbuffer\",\n    \"glBindSampler\",\n    \"glBindSamplers\",\n    \"glBindTexture\",\n    \"glBindTextureUnit\",\n    \"glBindTextures\",\n    \"glBindTransformFeedback\",\n    \"glBindVertexArray\",\n    \"glBindVertexBuffer\",\n    \"glBindVertexBuffers\",\n    \"glBlendColor\",\n    \"glBlendEquation\",\n    \"glBlendEquationSeparate\",\n    \"glBlendEquationSeparatei\",\n    \"glBlendEquationi\",\n    \"glBlendFunc\",\n    \"glBlendFuncSeparate\",\n    \"glBlendFuncSeparatei\",\n    \"glBlendFunci\",\n    \"glBlitFramebuffer\",\n    \"glBlitNamedFramebuffer\",\n    \"glBufferData\",\n    \"glBufferStorage\",\n    \"glBufferSubData\",\n    \"glCheckFramebufferStatus\",\n    \"glCheckNamedFramebufferStatus\",\n    \"glClampColor\",\n    \"glClear\",\n    \"glClearBufferData\",\n    \"glClearBufferSubData\",\n    \"glClearBufferfi\",\n    \"glClearBufferfv\",\n    \"glClearBufferiv\",\n    \"glClearBufferuiv\",\n    \"glClearColor\",\n    \"glClearDepth\",\n    \"glClearDepthf\",\n    \"glClearNamedBufferData\",\n    \"glClearNamedBufferSubData\",\n    \"glClearNamedFramebufferfi\",\n    \"glClearNamedFramebufferfv\",\n    \"glClearNamedFramebufferiv\",\n    \"glClearNamedFramebufferuiv\",\n    \"glClearStencil\",\n    \"glClearTexImage\",\n    \"glClearTexSubImage\",\n    \"glClientWaitSync\",\n    \"glClipControl\",\n    \"glColorMask\",\n    \"glColorMaski\",\n    \"glCompileShader\",\n    \"glCompressedTexImage1D\",\n    \"glCompressedTexImage2D\",\n    \"glCompressedTexImage3D\",\n    \"glCompressedTexSubImage1D\",\n    \"glCompressedTexSubImage2D\",\n    \"glCompressedTexSubImage3D\",\n    \"glCompressedTextureSubImage1D\",\n    \"glCompressedTextureSubImage2D\",\n    \"glCompressedTextureSubImage3D\",\n    \"glCopyBufferSubData\",\n    \"glCopyImageSubData\",\n    \"glCopyNamedBufferSubData\",\n    \"glCopyTexImage1D\",\n    \"glCopyTexImage2D\",\n    \"glCopyTexSubImage1D\",\n    \"glCopyTexSubImage2D\",\n    \"glCopyTexSubImage3D\",\n    \"glCopyTextureSubImage1D\",\n    \"glCopyTextureSubImage2D\",\n    \"glCopyTextureSubImage3D\",\n    \"glCreateBuffers\",\n    \"glCreateFramebuffers\",\n    \"glCreateProgram\",\n    \"glCreateProgramPipelines\",\n    \"glCreateQueries\",\n    \"glCreateRenderbuffers\",\n    \"glCreateSamplers\",\n    \"glCreateShader\",\n    \"glCreateShaderProgramv\",\n    \"glCreateTextures\",\n    \"glCreateTransformFeedbacks\",\n    \"glCreateVertexArrays\",\n    \"glCullFace\",\n    \"glDebugMessageCallback\",\n    \"glDebugMessageControl\",\n    \"glDebugMessageInsert\",\n    \"glDeleteBuffers\",\n    \"glDeleteFramebuffers\",\n    \"glDeleteProgram\",\n    \"glDeleteProgramPipelines\",\n    \"glDeleteQueries\",\n    \"glDeleteRenderbuffers\",\n    \"glDeleteSamplers\",\n    \"glDeleteShader\",\n    \"glDeleteSync\",\n    \"glDeleteTextures\",\n    \"glDeleteTransformFeedbacks\",\n    \"glDeleteVertexArrays\",\n    \"glDepthFunc\",\n    \"glDepthMask\",\n    \"glDepthRange\",\n    \"glDepthRangeArrayv\",\n    \"glDepthRangeIndexed\",\n    \"glDepthRangef\",\n    \"glDetachShader\",\n    \"glDisable\",\n    \"glDisableVertexArrayAttrib\",\n    \"glDisableVertexAttribArray\",\n    \"glDisablei\",\n    \"glDispatchCompute\",\n    \"glDispatchComputeIndirect\",\n    \"glDrawArrays\",\n    \"glDrawArraysIndirect\",\n    \"glDrawArraysInstanced\",\n    \"glDrawArraysInstancedBaseInstance\",\n    \"glDrawBuffer\",\n    \"glDrawBuffers\",\n    \"glDrawElements\",\n    \"glDrawElementsBaseVertex\",\n    \"glDrawElementsIndirect\",\n    \"glDrawElementsInstanced\",\n    \"glDrawElementsInstancedBaseInstance\",\n    \"glDrawElementsInstancedBaseVertex\",\n    \"glDrawElementsInstancedBaseVertexBaseInstance\",\n    \"glDrawRangeElements\",\n    \"glDrawRangeElementsBaseVertex\",\n    \"glDrawTransformFeedback\",\n    \"glDrawTransformFeedbackInstanced\",\n    \"glDrawTransformFeedbackStream\",\n    \"glDrawTransformFeedbackStreamInstanced\",\n    \"glEnable\",\n    \"glEnableVertexArrayAttrib\",\n    \"glEnableVertexAttribArray\",\n    \"glEnablei\",\n    \"glEndConditionalRender\",\n    \"glEndQuery\",\n    \"glEndQueryIndexed\",\n    \"glEndTransformFeedback\",\n    \"glFenceSync\",\n    \"glFinish\",\n    \"glFlush\",\n    \"glFlushMappedBufferRange\",\n    \"glFlushMappedNamedBufferRange\",\n    \"glFramebufferParameteri\",\n    \"glFramebufferParameteriMESA\",\n    \"glFramebufferRenderbuffer\",\n    \"glFramebufferTexture\",\n    \"glFramebufferTexture1D\",\n    \"glFramebufferTexture2D\",\n    \"glFramebufferTexture3D\",\n    \"glFramebufferTextureLayer\",\n    \"glFrontFace\",\n    \"glGenBuffers\",\n    \"glGenFramebuffers\",\n    \"glGenProgramPipelines\",\n    \"glGenQueries\",\n    \"glGenRenderbuffers\",\n    \"glGenSamplers\",\n    \"glGenTextures\",\n    \"glGenTransformFeedbacks\",\n    \"glGenVertexArrays\",\n    \"glGenerateMipmap\",\n    \"glGenerateTextureMipmap\",\n    \"glGetActiveAtomicCounterBufferiv\",\n    \"glGetActiveAttrib\",\n    \"glGetActiveSubroutineName\",\n    \"glGetActiveSubroutineUniformName\",\n    \"glGetActiveSubroutineUniformiv\",\n    \"glGetActiveUniform\",\n    \"glGetActiveUniformBlockName\",\n    \"glGetActiveUniformBlockiv\",\n    \"glGetActiveUniformName\",\n    \"glGetActiveUniformsiv\",\n    \"glGetAttachedShaders\",\n    \"glGetAttribLocation\",\n    \"glGetBooleani_v\",\n    \"glGetBooleanv\",\n    \"glGetBufferParameteri64v\",\n    \"glGetBufferParameteriv\",\n    \"glGetBufferPointerv\",\n    \"glGetBufferSubData\",\n    \"glGetCompressedTexImage\",\n    \"glGetCompressedTextureImage\",\n    \"glGetCompressedTextureSubImage\",\n    \"glGetDebugMessageLog\",\n    \"glGetDoublei_v\",\n    \"glGetDoublev\",\n    \"glGetError\",\n    \"glGetFloati_v\",\n    \"glGetFloatv\",\n    \"glGetFragDataIndex\",\n    \"glGetFragDataLocation\",\n    \"glGetFramebufferAttachmentParameteriv\",\n    \"glGetFramebufferParameteriv\",\n    \"glGetFramebufferParameterivMESA\",\n    \"glGetGraphicsResetStatus\",\n    \"glGetInteger64i_v\",\n    \"glGetInteger64v\",\n    \"glGetIntegeri_v\",\n    \"glGetIntegerv\",\n    \"glGetInternalformati64v\",\n    \"glGetInternalformativ\",\n    \"glGetMultisamplefv\",\n    \"glGetNamedBufferParameteri64v\",\n    \"glGetNamedBufferParameteriv\",\n    \"glGetNamedBufferPointerv\",\n    \"glGetNamedBufferSubData\",\n    \"glGetNamedFramebufferAttachmentParameteriv\",\n    \"glGetNamedFramebufferParameteriv\",\n    \"glGetNamedRenderbufferParameteriv\",\n    \"glGetObjectLabel\",\n    \"glGetObjectPtrLabel\",\n    \"glGetPointerv\",\n    \"glGetProgramBinary\",\n    \"glGetProgramInfoLog\",\n    \"glGetProgramInterfaceiv\",\n    \"glGetProgramPipelineInfoLog\",\n    \"glGetProgramPipelineiv\",\n    \"glGetProgramResourceIndex\",\n    \"glGetProgramResourceLocation\",\n    \"glGetProgramResourceLocationIndex\",\n    \"glGetProgramResourceName\",\n    \"glGetProgramResourceiv\",\n    \"glGetProgramStageiv\",\n    \"glGetProgramiv\",\n    \"glGetQueryBufferObjecti64v\",\n    \"glGetQueryBufferObjectiv\",\n    \"glGetQueryBufferObjectui64v\",\n    \"glGetQueryBufferObjectuiv\",\n    \"glGetQueryIndexediv\",\n    \"glGetQueryObjecti64v\",\n    \"glGetQueryObjectiv\",\n    \"glGetQueryObjectui64v\",\n    \"glGetQueryObjectuiv\",\n    \"glGetQueryiv\",\n    \"glGetRenderbufferParameteriv\",\n    \"glGetSamplerParameterIiv\",\n    \"glGetSamplerParameterIuiv\",\n    \"glGetSamplerParameterfv\",\n    \"glGetSamplerParameteriv\",\n    \"glGetShaderInfoLog\",\n    \"glGetShaderPrecisionFormat\",\n    \"glGetShaderSource\",\n    \"glGetShaderiv\",\n    \"glGetString\",\n    \"glGetStringi\",\n    \"glGetSubroutineIndex\",\n    \"glGetSubroutineUniformLocation\",\n    \"glGetSynciv\",\n    \"glGetTexImage\",\n    \"glGetTexLevelParameterfv\",\n    \"glGetTexLevelParameteriv\",\n    \"glGetTexParameterIiv\",\n    \"glGetTexParameterIuiv\",\n    \"glGetTexParameterfv\",\n    \"glGetTexParameteriv\",\n    \"glGetTextureImage\",\n    \"glGetTextureLevelParameterfv\",\n    \"glGetTextureLevelParameteriv\",\n    \"glGetTextureParameterIiv\",\n    \"glGetTextureParameterIuiv\",\n    \"glGetTextureParameterfv\",\n    \"glGetTextureParameteriv\",\n    \"glGetTextureSubImage\",\n    \"glGetTransformFeedbackVarying\",\n    \"glGetTransformFeedbacki64_v\",\n    \"glGetTransformFeedbacki_v\",\n    \"glGetTransformFeedbackiv\",\n    \"glGetUniformBlockIndex\",\n    \"glGetUniformIndices\",\n    \"glGetUniformLocation\",\n    \"glGetUniformSubroutineuiv\",\n    \"glGetUniformdv\",\n    \"glGetUniformfv\",\n    \"glGetUniformiv\",\n    \"glGetUniformuiv\",\n    \"glGetVertexArrayIndexed64iv\",\n    \"glGetVertexArrayIndexediv\",\n    \"glGetVertexArrayiv\",\n    \"glGetVertexAttribIiv\",\n    \"glGetVertexAttribIuiv\",\n    \"glGetVertexAttribLdv\",\n    \"glGetVertexAttribPointerv\",\n    \"glGetVertexAttribdv\",\n    \"glGetVertexAttribfv\",\n    \"glGetVertexAttribiv\",\n    \"glGetnCompressedTexImage\",\n    \"glGetnTexImage\",\n    \"glGetnUniformdv\",\n    \"glGetnUniformfv\",\n    \"glGetnUniformiv\",\n    \"glGetnUniformuiv\",\n    \"glHint\",\n    \"glInvalidateBufferData\",\n    \"glInvalidateBufferSubData\",\n    \"glInvalidateFramebuffer\",\n    \"glInvalidateNamedFramebufferData\",\n    \"glInvalidateNamedFramebufferSubData\",\n    \"glInvalidateSubFramebuffer\",\n    \"glInvalidateTexImage\",\n    \"glInvalidateTexSubImage\",\n    \"glIsBuffer\",\n    \"glIsEnabled\",\n    \"glIsEnabledi\",\n    \"glIsFramebuffer\",\n    \"glIsProgram\",\n    \"glIsProgramPipeline\",\n    \"glIsQuery\",\n    \"glIsRenderbuffer\",\n    \"glIsSampler\",\n    \"glIsShader\",\n    \"glIsSync\",\n    \"glIsTexture\",\n    \"glIsTransformFeedback\",\n    \"glIsVertexArray\",\n    \"glLineWidth\",\n    \"glLinkProgram\",\n    \"glLogicOp\",\n    \"glMapBuffer\",\n    \"glMapBufferRange\",\n    \"glMapNamedBuffer\",\n    \"glMapNamedBufferRange\",\n    \"glMemoryBarrier\",\n    \"glMemoryBarrierByRegion\",\n    \"glMinSampleShading\",\n    \"glMultiDrawArrays\",\n    \"glMultiDrawArraysIndirect\",\n    \"glMultiDrawArraysIndirectCount\",\n    \"glMultiDrawElements\",\n    \"glMultiDrawElementsBaseVertex\",\n    \"glMultiDrawElementsIndirect\",\n    \"glMultiDrawElementsIndirectCount\",\n    \"glNamedBufferData\",\n    \"glNamedBufferStorage\",\n    \"glNamedBufferSubData\",\n    \"glNamedFramebufferDrawBuffer\",\n    \"glNamedFramebufferDrawBuffers\",\n    \"glNamedFramebufferParameteri\",\n    \"glNamedFramebufferReadBuffer\",\n    \"glNamedFramebufferRenderbuffer\",\n    \"glNamedFramebufferTexture\",\n    \"glNamedFramebufferTextureLayer\",\n    \"glNamedRenderbufferStorage\",\n    \"glNamedRenderbufferStorageMultisample\",\n    \"glObjectLabel\",\n    \"glObjectPtrLabel\",\n    \"glPatchParameterfv\",\n    \"glPatchParameteri\",\n    \"glPauseTransformFeedback\",\n    \"glPixelStoref\",\n    \"glPixelStorei\",\n    \"glPointParameterf\",\n    \"glPointParameterfv\",\n    \"glPointParameteri\",\n    \"glPointParameteriv\",\n    \"glPointSize\",\n    \"glPolygonMode\",\n    \"glPolygonOffset\",\n    \"glPolygonOffsetClamp\",\n    \"glPopDebugGroup\",\n    \"glPrimitiveRestartIndex\",\n    \"glProgramBinary\",\n    \"glProgramParameteri\",\n    \"glProgramUniform1d\",\n    \"glProgramUniform1dv\",\n    \"glProgramUniform1f\",\n    \"glProgramUniform1fv\",\n    \"glProgramUniform1i\",\n    \"glProgramUniform1iv\",\n    \"glProgramUniform1ui\",\n    \"glProgramUniform1uiv\",\n    \"glProgramUniform2d\",\n    \"glProgramUniform2dv\",\n    \"glProgramUniform2f\",\n    \"glProgramUniform2fv\",\n    \"glProgramUniform2i\",\n    \"glProgramUniform2iv\",\n    \"glProgramUniform2ui\",\n    \"glProgramUniform2uiv\",\n    \"glProgramUniform3d\",\n    \"glProgramUniform3dv\",\n    \"glProgramUniform3f\",\n    \"glProgramUniform3fv\",\n    \"glProgramUniform3i\",\n    \"glProgramUniform3iv\",\n    \"glProgramUniform3ui\",\n    \"glProgramUniform3uiv\",\n    \"glProgramUniform4d\",\n    \"glProgramUniform4dv\",\n    \"glProgramUniform4f\",\n    \"glProgramUniform4fv\",\n    \"glProgramUniform4i\",\n    \"glProgramUniform4iv\",\n    \"glProgramUniform4ui\",\n    \"glProgramUniform4uiv\",\n    \"glProgramUniformMatrix2dv\",\n    \"glProgramUniformMatrix2fv\",\n    \"glProgramUniformMatrix2x3dv\",\n    \"glProgramUniformMatrix2x3fv\",\n    \"glProgramUniformMatrix2x4dv\",\n    \"glProgramUniformMatrix2x4fv\",\n    \"glProgramUniformMatrix3dv\",\n    \"glProgramUniformMatrix3fv\",\n    \"glProgramUniformMatrix3x2dv\",\n    \"glProgramUniformMatrix3x2fv\",\n    \"glProgramUniformMatrix3x4dv\",\n    \"glProgramUniformMatrix3x4fv\",\n    \"glProgramUniformMatrix4dv\",\n    \"glProgramUniformMatrix4fv\",\n    \"glProgramUniformMatrix4x2dv\",\n    \"glProgramUniformMatrix4x2fv\",\n    \"glProgramUniformMatrix4x3dv\",\n    \"glProgramUniformMatrix4x3fv\",\n    \"glProvokingVertex\",\n    \"glPushDebugGroup\",\n    \"glQueryCounter\",\n    \"glReadBuffer\",\n    \"glReadPixels\",\n    \"glReadnPixels\",\n    \"glReleaseShaderCompiler\",\n    \"glRenderbufferStorage\",\n    \"glRenderbufferStorageMultisample\",\n    \"glResumeTransformFeedback\",\n    \"glSampleCoverage\",\n    \"glSampleMaski\",\n    \"glSamplerParameterIiv\",\n    \"glSamplerParameterIuiv\",\n    \"glSamplerParameterf\",\n    \"glSamplerParameterfv\",\n    \"glSamplerParameteri\",\n    \"glSamplerParameteriv\",\n    \"glScissor\",\n    \"glScissorArrayv\",\n    \"glScissorIndexed\",\n    \"glScissorIndexedv\",\n    \"glShaderBinary\",\n    \"glShaderSource\",\n    \"glShaderStorageBlockBinding\",\n    \"glSpecializeShader\",\n    \"glStencilFunc\",\n    \"glStencilFuncSeparate\",\n    \"glStencilMask\",\n    \"glStencilMaskSeparate\",\n    \"glStencilOp\",\n    \"glStencilOpSeparate\",\n    \"glTexBuffer\",\n    \"glTexBufferRange\",\n    \"glTexImage1D\",\n    \"glTexImage2D\",\n    \"glTexImage2DMultisample\",\n    \"glTexImage3D\",\n    \"glTexImage3DMultisample\",\n    \"glTexParameterIiv\",\n    \"glTexParameterIuiv\",\n    \"glTexParameterf\",\n    \"glTexParameterfv\",\n    \"glTexParameteri\",\n    \"glTexParameteriv\",\n    \"glTexStorage1D\",\n    \"glTexStorage2D\",\n    \"glTexStorage2DMultisample\",\n    \"glTexStorage3D\",\n    \"glTexStorage3DMultisample\",\n    \"glTexSubImage1D\",\n    \"glTexSubImage2D\",\n    \"glTexSubImage3D\",\n    \"glTextureBarrier\",\n    \"glTextureBuffer\",\n    \"glTextureBufferRange\",\n    \"glTextureParameterIiv\",\n    \"glTextureParameterIuiv\",\n    \"glTextureParameterf\",\n    \"glTextureParameterfv\",\n    \"glTextureParameteri\",\n    \"glTextureParameteriv\",\n    \"glTextureStorage1D\",\n    \"glTextureStorage2D\",\n    \"glTextureStorage2DMultisample\",\n    \"glTextureStorage3D\",\n    \"glTextureStorage3DMultisample\",\n    \"glTextureSubImage1D\",\n    \"glTextureSubImage2D\",\n    \"glTextureSubImage3D\",\n    \"glTextureView\",\n    \"glTransformFeedbackBufferBase\",\n    \"glTransformFeedbackBufferRange\",\n    \"glTransformFeedbackVaryings\",\n    \"glUniform1d\",\n    \"glUniform1dv\",\n    \"glUniform1f\",\n    \"glUniform1fv\",\n    \"glUniform1i\",\n    \"glUniform1iv\",\n    \"glUniform1ui\",\n    \"glUniform1uiv\",\n    \"glUniform2d\",\n    \"glUniform2dv\",\n    \"glUniform2f\",\n    \"glUniform2fv\",\n    \"glUniform2i\",\n    \"glUniform2iv\",\n    \"glUniform2ui\",\n    \"glUniform2uiv\",\n    \"glUniform3d\",\n    \"glUniform3dv\",\n    \"glUniform3f\",\n    \"glUniform3fv\",\n    \"glUniform3i\",\n    \"glUniform3iv\",\n    \"glUniform3ui\",\n    \"glUniform3uiv\",\n    \"glUniform4d\",\n    \"glUniform4dv\",\n    \"glUniform4f\",\n    \"glUniform4fv\",\n    \"glUniform4i\",\n    \"glUniform4iv\",\n    \"glUniform4ui\",\n    \"glUniform4uiv\",\n    \"glUniformBlockBinding\",\n    \"glUniformMatrix2dv\",\n    \"glUniformMatrix2fv\",\n    \"glUniformMatrix2x3dv\",\n    \"glUniformMatrix2x3fv\",\n    \"glUniformMatrix2x4dv\",\n    \"glUniformMatrix2x4fv\",\n    \"glUniformMatrix3dv\",\n    \"glUniformMatrix3fv\",\n    \"glUniformMatrix3x2dv\",\n    \"glUniformMatrix3x2fv\",\n    \"glUniformMatrix3x4dv\",\n    \"glUniformMatrix3x4fv\",\n    \"glUniformMatrix4dv\",\n    \"glUniformMatrix4fv\",\n    \"glUniformMatrix4x2dv\",\n    \"glUniformMatrix4x2fv\",\n    \"glUniformMatrix4x3dv\",\n    \"glUniformMatrix4x3fv\",\n    \"glUniformSubroutinesuiv\",\n    \"glUnmapBuffer\",\n    \"glUnmapNamedBuffer\",\n    \"glUseProgram\",\n    \"glUseProgramStages\",\n    \"glValidateProgram\",\n    \"glValidateProgramPipeline\",\n    \"glVertexArrayAttribBinding\",\n    \"glVertexArrayAttribFormat\",\n    \"glVertexArrayAttribIFormat\",\n    \"glVertexArrayAttribLFormat\",\n    \"glVertexArrayBindingDivisor\",\n    \"glVertexArrayElementBuffer\",\n    \"glVertexArrayVertexBuffer\",\n    \"glVertexArrayVertexBuffers\",\n    \"glVertexAttrib1d\",\n    \"glVertexAttrib1dv\",\n    \"glVertexAttrib1f\",\n    \"glVertexAttrib1fv\",\n    \"glVertexAttrib1s\",\n    \"glVertexAttrib1sv\",\n    \"glVertexAttrib2d\",\n    \"glVertexAttrib2dv\",\n    \"glVertexAttrib2f\",\n    \"glVertexAttrib2fv\",\n    \"glVertexAttrib2s\",\n    \"glVertexAttrib2sv\",\n    \"glVertexAttrib3d\",\n    \"glVertexAttrib3dv\",\n    \"glVertexAttrib3f\",\n    \"glVertexAttrib3fv\",\n    \"glVertexAttrib3s\",\n    \"glVertexAttrib3sv\",\n    \"glVertexAttrib4Nbv\",\n    \"glVertexAttrib4Niv\",\n    \"glVertexAttrib4Nsv\",\n    \"glVertexAttrib4Nub\",\n    \"glVertexAttrib4Nubv\",\n    \"glVertexAttrib4Nuiv\",\n    \"glVertexAttrib4Nusv\",\n    \"glVertexAttrib4bv\",\n    \"glVertexAttrib4d\",\n    \"glVertexAttrib4dv\",\n    \"glVertexAttrib4f\",\n    \"glVertexAttrib4fv\",\n    \"glVertexAttrib4iv\",\n    \"glVertexAttrib4s\",\n    \"glVertexAttrib4sv\",\n    \"glVertexAttrib4ubv\",\n    \"glVertexAttrib4uiv\",\n    \"glVertexAttrib4usv\",\n    \"glVertexAttribBinding\",\n    \"glVertexAttribDivisor\",\n    \"glVertexAttribFormat\",\n    \"glVertexAttribI1i\",\n    \"glVertexAttribI1iv\",\n    \"glVertexAttribI1ui\",\n    \"glVertexAttribI1uiv\",\n    \"glVertexAttribI2i\",\n    \"glVertexAttribI2iv\",\n    \"glVertexAttribI2ui\",\n    \"glVertexAttribI2uiv\",\n    \"glVertexAttribI3i\",\n    \"glVertexAttribI3iv\",\n    \"glVertexAttribI3ui\",\n    \"glVertexAttribI3uiv\",\n    \"glVertexAttribI4bv\",\n    \"glVertexAttribI4i\",\n    \"glVertexAttribI4iv\",\n    \"glVertexAttribI4sv\",\n    \"glVertexAttribI4ubv\",\n    \"glVertexAttribI4ui\",\n    \"glVertexAttribI4uiv\",\n    \"glVertexAttribI4usv\",\n    \"glVertexAttribIFormat\",\n    \"glVertexAttribIPointer\",\n    \"glVertexAttribL1d\",\n    \"glVertexAttribL1dv\",\n    \"glVertexAttribL2d\",\n    \"glVertexAttribL2dv\",\n    \"glVertexAttribL3d\",\n    \"glVertexAttribL3dv\",\n    \"glVertexAttribL4d\",\n    \"glVertexAttribL4dv\",\n    \"glVertexAttribLFormat\",\n    \"glVertexAttribLPointer\",\n    \"glVertexAttribP1ui\",\n    \"glVertexAttribP1uiv\",\n    \"glVertexAttribP2ui\",\n    \"glVertexAttribP2uiv\",\n    \"glVertexAttribP3ui\",\n    \"glVertexAttribP3uiv\",\n    \"glVertexAttribP4ui\",\n    \"glVertexAttribP4uiv\",\n    \"glVertexAttribPointer\",\n    \"glVertexBindingDivisor\",\n    \"glViewport\",\n    \"glViewportArrayv\",\n    \"glViewportIndexedf\",\n    \"glViewportIndexedfv\",\n    \"glWaitSync\",\n};\n\nGL3W_API union GL3WProcs gl3wProcs;\n\nstatic void load_procs(GL3WGetProcAddressProc proc)\n{\n    size_t i;\n    for (i = 0; i < ARRAY_SIZE(proc_names); i++)\n        gl3wProcs.ptr[i] = proc(proc_names[i]);\n}\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "lib/third_party/imgui/backend/include/opengl_support.h",
    "content": "#pragma once\n\n#if defined(OS_WINDOWS)\n    #include <Windows.h>\n    #include <GL/GL.h>\n#endif\n\n#if defined(OS_WEB)\n    #define GLFW_INCLUDE_ES3\n    #include <GLES3/gl3.h>\n#else\n    #include <imgui_impl_opengl3_loader.h>\n#endif"
  },
  {
    "path": "lib/third_party/imgui/backend/include/stb_image.h",
    "content": "/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb\n                                  no warranty implied; use at your own risk\n\n   Do this:\n      #define STB_IMAGE_IMPLEMENTATION\n   before you include this file in *one* C or C++ file to create the implementation.\n\n   // i.e. it should look like this:\n   #include ...\n   #include ...\n   #include ...\n   #define STB_IMAGE_IMPLEMENTATION\n   #include \"stb_image.h\"\n\n   You can #define STBI_ASSERT(x) before the #include to avoid using assert.h.\n   And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free\n\n\n   QUICK NOTES:\n      Primarily of interest to game developers and other people who can\n          avoid problematic images and only need the trivial interface\n\n      JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib)\n      PNG 1/2/4/8/16-bit-per-channel\n\n      TGA (not sure what subset, if a subset)\n      BMP non-1bpp, non-RLE\n      PSD (composited view only, no extra channels, 8/16 bit-per-channel)\n\n      GIF (*comp always reports as 4-channel)\n      HDR (radiance rgbE format)\n      PIC (Softimage PIC)\n      PNM (PPM and PGM binary only)\n\n      Animated GIF still needs a proper API, but here's one way to do it:\n          http://gist.github.com/urraka/685d9a6340b26b830d49\n\n      - decode from memory or through FILE (define STBI_NO_STDIO to remove code)\n      - decode from arbitrary I/O callbacks\n      - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)\n\n   Full documentation under \"DOCUMENTATION\" below.\n\n\nLICENSE\n\n  See end of file for license information.\n\nRECENT REVISION HISTORY:\n\n      2.28  (2023-01-29) many error fixes, security errors, just tons of stuff\n      2.27  (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes\n      2.26  (2020-07-13) many minor fixes\n      2.25  (2020-02-02) fix warnings\n      2.24  (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically\n      2.23  (2019-08-11) fix clang static analysis warning\n      2.22  (2019-03-04) gif fixes, fix warnings\n      2.21  (2019-02-25) fix typo in comment\n      2.20  (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs\n      2.19  (2018-02-11) fix warning\n      2.18  (2018-01-30) fix warnings\n      2.17  (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings\n      2.16  (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes\n      2.15  (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC\n      2.14  (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs\n      2.13  (2016-12-04) experimental 16-bit API, only for PNG so far; fixes\n      2.12  (2016-04-02) fix typo in 2.11 PSD fix that caused crashes\n      2.11  (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64\n                         RGB-format JPEG; remove white matting in PSD;\n                         allocate large structures on the stack;\n                         correct channel count for PNG & BMP\n      2.10  (2016-01-22) avoid warning introduced in 2.09\n      2.09  (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED\n\n   See end of file for full revision history.\n\n\n ============================    Contributors    =========================\n\n Image formats                          Extensions, features\n    Sean Barrett (jpeg, png, bmp)          Jetro Lauha (stbi_info)\n    Nicolas Schulz (hdr, psd)              Martin \"SpartanJ\" Golini (stbi_info)\n    Jonathan Dummer (tga)                  James \"moose2000\" Brown (iPhone PNG)\n    Jean-Marc Lienher (gif)                Ben \"Disch\" Wenger (io callbacks)\n    Tom Seddon (pic)                       Omar Cornut (1/2/4-bit PNG)\n    Thatcher Ulrich (psd)                  Nicolas Guillemot (vertical flip)\n    Ken Miller (pgm, ppm)                  Richard Mitton (16-bit PSD)\n    github:urraka (animated gif)           Junggon Kim (PNM comments)\n    Christopher Forseth (animated gif)     Daniel Gibson (16-bit TGA)\n                                           socks-the-fox (16-bit PNG)\n                                           Jeremy Sawicki (handle all ImageNet JPGs)\n Optimizations & bugfixes                  Mikhail Morozov (1-bit BMP)\n    Fabian \"ryg\" Giesen                    Anael Seghezzi (is-16-bit query)\n    Arseny Kapoulkine                      Simon Breuss (16-bit PNM)\n    John-Mark Allen\n    Carmelo J Fdez-Aguera\n\n Bug & warning fixes\n    Marc LeBlanc            David Woo          Guillaume George     Martins Mozeiko\n    Christpher Lloyd        Jerry Jansson      Joseph Thomson       Blazej Dariusz Roszkowski\n    Phil Jordan                                Dave Moore           Roy Eltham\n    Hayaki Saito            Nathan Reed        Won Chun\n    Luke Graham             Johan Duparc       Nick Verigakis       the Horde3D community\n    Thomas Ruf              Ronny Chevalier                         github:rlyeh\n    Janez Zemva             John Bartholomew   Michal Cichon        github:romigrou\n    Jonathan Blow           Ken Hamada         Tero Hanninen        github:svdijk\n    Eugene Golushkov        Laurent Gomila     Cort Stratton        github:snagar\n    Aruelien Pocheville     Sergio Gonzalez    Thibault Reuille     github:Zelex\n    Cass Everitt            Ryamond Barbiero                        github:grim210\n    Paul Du Bois            Engin Manap        Aldo Culquicondor    github:sammyhw\n    Philipp Wiesemann       Dale Weiler        Oriol Ferrer Mesia   github:phprus\n    Josh Tobin              Neil Bickford      Matthew Gregan       github:poppolopoppo\n    Julian Raschke          Gregory Mullen     Christian Floisand   github:darealshinji\n    Baldur Karlsson         Kevin Schmidt      JR Smith             github:Michaelangel007\n                            Brad Weinberger    Matvey Cherevko      github:mosra\n    Luca Sas                Alexander Veselov  Zack Middleton       [reserved]\n    Ryan C. Gordon          [reserved]                              [reserved]\n                     DO NOT ADD YOUR NAME HERE\n\n                     Jacko Dirks\n\n  To add your name to the credits, pick a random blank space in the middle and fill it.\n  80% of merge conflicts on stb PRs are due to people adding their name at the end\n  of the credits.\n*/\n\n#ifndef STBI_INCLUDE_STB_IMAGE_H\n#define STBI_INCLUDE_STB_IMAGE_H\n\n// DOCUMENTATION\n//\n// Limitations:\n//    - no 12-bit-per-channel JPEG\n//    - no JPEGs with arithmetic coding\n//    - GIF always returns *comp=4\n//\n// Basic usage (see HDR discussion below for HDR usage):\n//    int x,y,n;\n//    unsigned char *data = stbi_load(filename, &x, &y, &n, 0);\n//    // ... process data if not NULL ...\n//    // ... x = width, y = height, n = # 8-bit components per pixel ...\n//    // ... replace '0' with '1'..'4' to force that many components per pixel\n//    // ... but 'n' will always be the number that it would have been if you said 0\n//    stbi_image_free(data);\n//\n// Standard parameters:\n//    int *x                 -- outputs image width in pixels\n//    int *y                 -- outputs image height in pixels\n//    int *channels_in_file  -- outputs # of image components in image file\n//    int desired_channels   -- if non-zero, # of image components requested in result\n//\n// The return value from an image loader is an 'unsigned char *' which points\n// to the pixel data, or NULL on an allocation failure or if the image is\n// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels,\n// with each pixel consisting of N interleaved 8-bit components; the first\n// pixel pointed to is top-left-most in the image. There is no padding between\n// image scanlines or between pixels, regardless of format. The number of\n// components N is 'desired_channels' if desired_channels is non-zero, or\n// *channels_in_file otherwise. If desired_channels is non-zero,\n// *channels_in_file has the number of components that _would_ have been\n// output otherwise. E.g. if you set desired_channels to 4, you will always\n// get RGBA output, but you can check *channels_in_file to see if it's trivially\n// opaque because e.g. there were only 3 channels in the source image.\n//\n// An output image with N components has the following components interleaved\n// in this order in each pixel:\n//\n//     N=#comp     components\n//       1           grey\n//       2           grey, alpha\n//       3           red, green, blue\n//       4           red, green, blue, alpha\n//\n// If image loading fails for any reason, the return value will be NULL,\n// and *x, *y, *channels_in_file will be unchanged. The function\n// stbi_failure_reason() can be queried for an extremely brief, end-user\n// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS\n// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly\n// more user-friendly ones.\n//\n// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.\n//\n// To query the width, height and component count of an image without having to\n// decode the full file, you can use the stbi_info family of functions:\n//\n//   int x,y,n,ok;\n//   ok = stbi_info(filename, &x, &y, &n);\n//   // returns ok=1 and sets x, y, n if image is a supported format,\n//   // 0 otherwise.\n//\n// Note that stb_image pervasively uses ints in its public API for sizes,\n// including sizes of memory buffers. This is now part of the API and thus\n// hard to change without causing breakage. As a result, the various image\n// loaders all have certain limits on image size; these differ somewhat\n// by format but generally boil down to either just under 2GB or just under\n// 1GB. When the decoded image would be larger than this, stb_image decoding\n// will fail.\n//\n// Additionally, stb_image will reject image files that have any of their\n// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS,\n// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit,\n// the only way to have an image with such dimensions load correctly\n// is for it to have a rather extreme aspect ratio. Either way, the\n// assumption here is that such larger images are likely to be malformed\n// or malicious. If you do need to load an image with individual dimensions\n// larger than that, and it still fits in the overall size limit, you can\n// #define STBI_MAX_DIMENSIONS on your own to be something larger.\n//\n// ===========================================================================\n//\n// UNICODE:\n//\n//   If compiling for Windows and you wish to use Unicode filenames, compile\n//   with\n//       #define STBI_WINDOWS_UTF8\n//   and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert\n//   Windows wchar_t filenames to utf8.\n//\n// ===========================================================================\n//\n// Philosophy\n//\n// stb libraries are designed with the following priorities:\n//\n//    1. easy to use\n//    2. easy to maintain\n//    3. good performance\n//\n// Sometimes I let \"good performance\" creep up in priority over \"easy to maintain\",\n// and for best performance I may provide less-easy-to-use APIs that give higher\n// performance, in addition to the easy-to-use ones. Nevertheless, it's important\n// to keep in mind that from the standpoint of you, a client of this library,\n// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all.\n//\n// Some secondary priorities arise directly from the first two, some of which\n// provide more explicit reasons why performance can't be emphasized.\n//\n//    - Portable (\"ease of use\")\n//    - Small source code footprint (\"easy to maintain\")\n//    - No dependencies (\"ease of use\")\n//\n// ===========================================================================\n//\n// I/O callbacks\n//\n// I/O callbacks allow you to read from arbitrary sources, like packaged\n// files or some other source. Data read from callbacks are processed\n// through a small internal buffer (currently 128 bytes) to try to reduce\n// overhead.\n//\n// The three functions you must define are \"read\" (reads some bytes of data),\n// \"skip\" (skips some bytes of data), \"eof\" (reports if the stream is at the end).\n//\n// ===========================================================================\n//\n// SIMD support\n//\n// The JPEG decoder will try to automatically use SIMD kernels on x86 when\n// supported by the compiler. For ARM Neon support, you must explicitly\n// request it.\n//\n// (The old do-it-yourself SIMD API is no longer supported in the current\n// code.)\n//\n// On x86, SSE2 will automatically be used when available based on a run-time\n// test; if not, the generic C versions are used as a fall-back. On ARM targets,\n// the typical path is to have separate builds for NEON and non-NEON devices\n// (at least this is true for iOS and Android). Therefore, the NEON support is\n// toggled by a build flag: define STBI_NEON to get NEON loops.\n//\n// If for some reason you do not want to use any of SIMD code, or if\n// you have issues compiling it, you can disable it entirely by\n// defining STBI_NO_SIMD.\n//\n// ===========================================================================\n//\n// HDR image support   (disable by defining STBI_NO_HDR)\n//\n// stb_image supports loading HDR images in general, and currently the Radiance\n// .HDR file format specifically. You can still load any file through the existing\n// interface; if you attempt to load an HDR file, it will be automatically remapped\n// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;\n// both of these constants can be reconfigured through this interface:\n//\n//     stbi_hdr_to_ldr_gamma(2.2f);\n//     stbi_hdr_to_ldr_scale(1.0f);\n//\n// (note, do not use _inverse_ constants; stbi_image will invert them\n// appropriately).\n//\n// Additionally, there is a new, parallel interface for loading files as\n// (linear) floats to preserve the full dynamic range:\n//\n//    float *data = stbi_loadf(filename, &x, &y, &n, 0);\n//\n// If you load LDR images through this interface, those images will\n// be promoted to floating point values, run through the inverse of\n// constants corresponding to the above:\n//\n//     stbi_ldr_to_hdr_scale(1.0f);\n//     stbi_ldr_to_hdr_gamma(2.2f);\n//\n// Finally, given a filename (or an open file or memory block--see header\n// file for details) containing image data, you can query for the \"most\n// appropriate\" interface to use (that is, whether the image is HDR or\n// not), using:\n//\n//     stbi_is_hdr(char *filename);\n//\n// ===========================================================================\n//\n// iPhone PNG support:\n//\n// We optionally support converting iPhone-formatted PNGs (which store\n// premultiplied BGRA) back to RGB, even though they're internally encoded\n// differently. To enable this conversion, call\n// stbi_convert_iphone_png_to_rgb(1).\n//\n// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per\n// pixel to remove any premultiplied alpha *only* if the image file explicitly\n// says there's premultiplied data (currently only happens in iPhone images,\n// and only if iPhone convert-to-rgb processing is on).\n//\n// ===========================================================================\n//\n// ADDITIONAL CONFIGURATION\n//\n//  - You can suppress implementation of any of the decoders to reduce\n//    your code footprint by #defining one or more of the following\n//    symbols before creating the implementation.\n//\n//        STBI_NO_JPEG\n//        STBI_NO_PNG\n//        STBI_NO_BMP\n//        STBI_NO_PSD\n//        STBI_NO_TGA\n//        STBI_NO_GIF\n//        STBI_NO_HDR\n//        STBI_NO_PIC\n//        STBI_NO_PNM   (.ppm and .pgm)\n//\n//  - You can request *only* certain decoders and suppress all other ones\n//    (this will be more forward-compatible, as addition of new decoders\n//    doesn't require you to disable them explicitly):\n//\n//        STBI_ONLY_JPEG\n//        STBI_ONLY_PNG\n//        STBI_ONLY_BMP\n//        STBI_ONLY_PSD\n//        STBI_ONLY_TGA\n//        STBI_ONLY_GIF\n//        STBI_ONLY_HDR\n//        STBI_ONLY_PIC\n//        STBI_ONLY_PNM   (.ppm and .pgm)\n//\n//   - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still\n//     want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB\n//\n//  - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater\n//    than that size (in either width or height) without further processing.\n//    This is to let programs in the wild set an upper bound to prevent\n//    denial-of-service attacks on untrusted data, as one could generate a\n//    valid image of gigantic dimensions and force stb_image to allocate a\n//    huge block of memory and spend disproportionate time decoding it. By\n//    default this is set to (1 << 24), which is 16777216, but that's still\n//    very big.\n\n#ifndef STBI_NO_STDIO\n#include <stdio.h>\n#endif // STBI_NO_STDIO\n\n#define STBI_VERSION 1\n\nenum\n{\n   STBI_default = 0, // only used for desired_channels\n\n   STBI_grey       = 1,\n   STBI_grey_alpha = 2,\n   STBI_rgb        = 3,\n   STBI_rgb_alpha  = 4\n};\n\n#include <stdlib.h>\ntypedef unsigned char stbi_uc;\ntypedef unsigned short stbi_us;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifndef STBIDEF\n#ifdef STB_IMAGE_STATIC\n#define STBIDEF static\n#else\n#define STBIDEF extern\n#endif\n#endif\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// PRIMARY API - works on images of any type\n//\n\n//\n// load image by filename, open file, or memory buffer\n//\n\ntypedef struct\n{\n   int      (*read)  (void *user,char *data,int size);   // fill 'data' with 'size' bytes.  return number of bytes actually read\n   void     (*skip)  (void *user,int n);                 // skip the next 'n' bytes, or 'unget' the last -n bytes if negative\n   int      (*eof)   (void *user);                       // returns nonzero if we are at end of file/data\n} stbi_io_callbacks;\n\n////////////////////////////////////\n//\n// 8-bits-per-channel interface\n//\n\nSTBIDEF stbi_uc *stbi_load_from_memory   (stbi_uc           const *buffer, int len   , int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk  , void *user, int *x, int *y, int *channels_in_file, int desired_channels);\n\n#ifndef STBI_NO_STDIO\nSTBIDEF stbi_uc *stbi_load            (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF stbi_uc *stbi_load_from_file  (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);\n// for stbi_load_from_file, file pointer is left pointing immediately after image\n#endif\n\n#ifndef STBI_NO_GIF\nSTBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp);\n#endif\n\n#ifdef STBI_WINDOWS_UTF8\nSTBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);\n#endif\n\n////////////////////////////////////\n//\n// 16-bits-per-channel interface\n//\n\nSTBIDEF stbi_us *stbi_load_16_from_memory   (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);\n\n#ifndef STBI_NO_STDIO\nSTBIDEF stbi_us *stbi_load_16          (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);\n#endif\n\n////////////////////////////////////\n//\n// float-per-channel interface\n//\n#ifndef STBI_NO_LINEAR\n   STBIDEF float *stbi_loadf_from_memory     (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);\n   STBIDEF float *stbi_loadf_from_callbacks  (stbi_io_callbacks const *clbk, void *user, int *x, int *y,  int *channels_in_file, int desired_channels);\n\n   #ifndef STBI_NO_STDIO\n   STBIDEF float *stbi_loadf            (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);\n   STBIDEF float *stbi_loadf_from_file  (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);\n   #endif\n#endif\n\n#ifndef STBI_NO_HDR\n   STBIDEF void   stbi_hdr_to_ldr_gamma(float gamma);\n   STBIDEF void   stbi_hdr_to_ldr_scale(float scale);\n#endif // STBI_NO_HDR\n\n#ifndef STBI_NO_LINEAR\n   STBIDEF void   stbi_ldr_to_hdr_gamma(float gamma);\n   STBIDEF void   stbi_ldr_to_hdr_scale(float scale);\n#endif // STBI_NO_LINEAR\n\n// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR\nSTBIDEF int    stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);\nSTBIDEF int    stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);\n#ifndef STBI_NO_STDIO\nSTBIDEF int      stbi_is_hdr          (char const *filename);\nSTBIDEF int      stbi_is_hdr_from_file(FILE *f);\n#endif // STBI_NO_STDIO\n\n\n// get a VERY brief reason for failure\n// on most compilers (and ALL modern mainstream compilers) this is threadsafe\nSTBIDEF const char *stbi_failure_reason  (void);\n\n// free the loaded image -- this is just free()\nSTBIDEF void     stbi_image_free      (void *retval_from_stbi_load);\n\n// get image dimensions & components without fully decoding\nSTBIDEF int      stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);\nSTBIDEF int      stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);\nSTBIDEF int      stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len);\nSTBIDEF int      stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user);\n\n#ifndef STBI_NO_STDIO\nSTBIDEF int      stbi_info               (char const *filename,     int *x, int *y, int *comp);\nSTBIDEF int      stbi_info_from_file     (FILE *f,                  int *x, int *y, int *comp);\nSTBIDEF int      stbi_is_16_bit          (char const *filename);\nSTBIDEF int      stbi_is_16_bit_from_file(FILE *f);\n#endif\n\n\n\n// for image formats that explicitly notate that they have premultiplied alpha,\n// we just return the colors as stored in the file. set this flag to force\n// unpremultiplication. results are undefined if the unpremultiply overflow.\nSTBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);\n\n// indicate whether we should process iphone images back to canonical format,\n// or just pass them through \"as-is\"\nSTBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);\n\n// flip the image vertically, so the first pixel in the output array is the bottom left\nSTBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);\n\n// as above, but only applies to images loaded on the thread that calls the function\n// this function is only available if your compiler supports thread-local variables;\n// calling it will fail to link if your compiler doesn't\nSTBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);\nSTBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);\nSTBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);\n\n// ZLIB client - used by PNG, available for other purposes\n\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);\nSTBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);\nSTBIDEF int   stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);\n\nSTBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);\nSTBIDEF int   stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);\n\n\n#ifdef __cplusplus\n}\n#endif\n\n//\n//\n////   end header file   /////////////////////////////////////////////////////\n#endif // STBI_INCLUDE_STB_IMAGE_H\n\n#ifdef STB_IMAGE_IMPLEMENTATION\n\n#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \\\n  || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \\\n  || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \\\n  || defined(STBI_ONLY_ZLIB)\n   #ifndef STBI_ONLY_JPEG\n   #define STBI_NO_JPEG\n   #endif\n   #ifndef STBI_ONLY_PNG\n   #define STBI_NO_PNG\n   #endif\n   #ifndef STBI_ONLY_BMP\n   #define STBI_NO_BMP\n   #endif\n   #ifndef STBI_ONLY_PSD\n   #define STBI_NO_PSD\n   #endif\n   #ifndef STBI_ONLY_TGA\n   #define STBI_NO_TGA\n   #endif\n   #ifndef STBI_ONLY_GIF\n   #define STBI_NO_GIF\n   #endif\n   #ifndef STBI_ONLY_HDR\n   #define STBI_NO_HDR\n   #endif\n   #ifndef STBI_ONLY_PIC\n   #define STBI_NO_PIC\n   #endif\n   #ifndef STBI_ONLY_PNM\n   #define STBI_NO_PNM\n   #endif\n#endif\n\n#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)\n#define STBI_NO_ZLIB\n#endif\n\n\n#include <stdarg.h>\n#include <stddef.h> // ptrdiff_t on osx\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n\n#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)\n#include <math.h>  // ldexp, pow\n#endif\n\n#ifndef STBI_NO_STDIO\n#include <stdio.h>\n#endif\n\n#ifndef STBI_ASSERT\n#include <assert.h>\n#define STBI_ASSERT(x) assert(x)\n#endif\n\n#ifdef __cplusplus\n#define STBI_EXTERN extern \"C\"\n#else\n#define STBI_EXTERN extern\n#endif\n\n\n#ifndef _MSC_VER\n   #ifdef __cplusplus\n   #define stbi_inline inline\n   #else\n   #define stbi_inline\n   #endif\n#else\n   #define stbi_inline __forceinline\n#endif\n\n#ifndef STBI_NO_THREAD_LOCALS\n   #if defined(__cplusplus) &&  __cplusplus >= 201103L\n      #define STBI_THREAD_LOCAL       thread_local\n   #elif defined(__GNUC__) && __GNUC__ < 5\n      #define STBI_THREAD_LOCAL       __thread\n   #elif defined(_MSC_VER)\n      #define STBI_THREAD_LOCAL       __declspec(thread)\n   #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)\n      #define STBI_THREAD_LOCAL       _Thread_local\n   #endif\n\n   #ifndef STBI_THREAD_LOCAL\n      #if defined(__GNUC__)\n        #define STBI_THREAD_LOCAL       __thread\n      #endif\n   #endif\n#endif\n\n#if defined(_MSC_VER) || defined(__SYMBIAN32__)\ntypedef unsigned short stbi__uint16;\ntypedef   signed short stbi__int16;\ntypedef unsigned int   stbi__uint32;\ntypedef   signed int   stbi__int32;\n#else\n#include <stdint.h>\ntypedef uint16_t stbi__uint16;\ntypedef int16_t  stbi__int16;\ntypedef uint32_t stbi__uint32;\ntypedef int32_t  stbi__int32;\n#endif\n\n// should produce compiler error if size is wrong\ntypedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];\n\n#ifdef _MSC_VER\n#define STBI_NOTUSED(v)  (void)(v)\n#else\n#define STBI_NOTUSED(v)  (void)sizeof(v)\n#endif\n\n#ifdef _MSC_VER\n#define STBI_HAS_LROTL\n#endif\n\n#ifdef STBI_HAS_LROTL\n   #define stbi_lrot(x,y)  _lrotl(x,y)\n#else\n   #define stbi_lrot(x,y)  (((x) << (y)) | ((x) >> (-(y) & 31)))\n#endif\n\n#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))\n// ok\n#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED)\n// ok\n#else\n#error \"Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED).\"\n#endif\n\n#ifndef STBI_MALLOC\n#define STBI_MALLOC(sz)           malloc(sz)\n#define STBI_REALLOC(p,newsz)     realloc(p,newsz)\n#define STBI_FREE(p)              free(p)\n#endif\n\n#ifndef STBI_REALLOC_SIZED\n#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz)\n#endif\n\n// x86/x64 detection\n#if defined(__x86_64__) || defined(_M_X64)\n#define STBI__X64_TARGET\n#elif defined(__i386) || defined(_M_IX86)\n#define STBI__X86_TARGET\n#endif\n\n#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)\n// gcc doesn't support sse2 intrinsics unless you compile with -msse2,\n// which in turn means it gets to use SSE2 everywhere. This is unfortunate,\n// but previous attempts to provide the SSE2 functions with runtime\n// detection caused numerous issues. The way architecture extensions are\n// exposed in GCC/Clang is, sadly, not really suited for one-file libs.\n// New behavior: if compiled with -msse2, we use SSE2 without any\n// detection; if not, we don't use it at all.\n#define STBI_NO_SIMD\n#endif\n\n#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD)\n// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET\n//\n// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the\n// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant.\n// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not\n// simultaneously enabling \"-mstackrealign\".\n//\n// See https://github.com/nothings/stb/issues/81 for more information.\n//\n// So default to no SSE2 on 32-bit MinGW. If you've read this far and added\n// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2.\n#define STBI_NO_SIMD\n#endif\n\n#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET))\n#define STBI_SSE2\n#include <emmintrin.h>\n\n#ifdef _MSC_VER\n\n#if _MSC_VER >= 1400  // not VC6\n#include <intrin.h> // __cpuid\nstatic int stbi__cpuid3(void)\n{\n   int info[4];\n   __cpuid(info,1);\n   return info[3];\n}\n#else\nstatic int stbi__cpuid3(void)\n{\n   int res;\n   __asm {\n      mov  eax,1\n      cpuid\n      mov  res,edx\n   }\n   return res;\n}\n#endif\n\n#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name\n\n#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)\nstatic int stbi__sse2_available(void)\n{\n   int info3 = stbi__cpuid3();\n   return ((info3 >> 26) & 1) != 0;\n}\n#endif\n\n#else // assume GCC-style if not VC++\n#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))\n\n#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)\nstatic int stbi__sse2_available(void)\n{\n   // If we're even attempting to compile this on GCC/Clang, that means\n   // -msse2 is on, which means the compiler is allowed to use SSE2\n   // instructions at will, and so are we.\n   return 1;\n}\n#endif\n\n#endif\n#endif\n\n// ARM NEON\n#if defined(STBI_NO_SIMD) && defined(STBI_NEON)\n#undef STBI_NEON\n#endif\n\n#ifdef STBI_NEON\n#include <arm_neon.h>\n#ifdef _MSC_VER\n#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name\n#else\n#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))\n#endif\n#endif\n\n#ifndef STBI_SIMD_ALIGN\n#define STBI_SIMD_ALIGN(type, name) type name\n#endif\n\n#ifndef STBI_MAX_DIMENSIONS\n#define STBI_MAX_DIMENSIONS (1 << 24)\n#endif\n\n///////////////////////////////////////////////\n//\n//  stbi__context struct and start_xxx functions\n\n// stbi__context structure is our basic context used by all images, so it\n// contains all the IO context, plus some basic image information\ntypedef struct\n{\n   stbi__uint32 img_x, img_y;\n   int img_n, img_out_n;\n\n   stbi_io_callbacks io;\n   void *io_user_data;\n\n   int read_from_callbacks;\n   int buflen;\n   stbi_uc buffer_start[128];\n   int callback_already_read;\n\n   stbi_uc *img_buffer, *img_buffer_end;\n   stbi_uc *img_buffer_original, *img_buffer_original_end;\n} stbi__context;\n\n\nstatic void stbi__refill_buffer(stbi__context *s);\n\n// initialize a memory-decode context\nstatic void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len)\n{\n   s->io.read = NULL;\n   s->read_from_callbacks = 0;\n   s->callback_already_read = 0;\n   s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;\n   s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len;\n}\n\n// initialize a callback-based context\nstatic void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user)\n{\n   s->io = *c;\n   s->io_user_data = user;\n   s->buflen = sizeof(s->buffer_start);\n   s->read_from_callbacks = 1;\n   s->callback_already_read = 0;\n   s->img_buffer = s->img_buffer_original = s->buffer_start;\n   stbi__refill_buffer(s);\n   s->img_buffer_original_end = s->img_buffer_end;\n}\n\n#ifndef STBI_NO_STDIO\n\nstatic int stbi__stdio_read(void *user, char *data, int size)\n{\n   return (int) fread(data,1,size,(FILE*) user);\n}\n\nstatic void stbi__stdio_skip(void *user, int n)\n{\n   int ch;\n   fseek((FILE*) user, n, SEEK_CUR);\n   ch = fgetc((FILE*) user);  /* have to read a byte to reset feof()'s flag */\n   if (ch != EOF) {\n      ungetc(ch, (FILE *) user);  /* push byte back onto stream if valid. */\n   }\n}\n\nstatic int stbi__stdio_eof(void *user)\n{\n   return feof((FILE*) user) || ferror((FILE *) user);\n}\n\nstatic stbi_io_callbacks stbi__stdio_callbacks =\n{\n   stbi__stdio_read,\n   stbi__stdio_skip,\n   stbi__stdio_eof,\n};\n\nstatic void stbi__start_file(stbi__context *s, FILE *f)\n{\n   stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f);\n}\n\n//static void stop_file(stbi__context *s) { }\n\n#endif // !STBI_NO_STDIO\n\nstatic void stbi__rewind(stbi__context *s)\n{\n   // conceptually rewind SHOULD rewind to the beginning of the stream,\n   // but we just rewind to the beginning of the initial buffer, because\n   // we only use it after doing 'test', which only ever looks at at most 92 bytes\n   s->img_buffer = s->img_buffer_original;\n   s->img_buffer_end = s->img_buffer_original_end;\n}\n\nenum\n{\n   STBI_ORDER_RGB,\n   STBI_ORDER_BGR\n};\n\ntypedef struct\n{\n   int bits_per_channel;\n   int num_channels;\n   int channel_order;\n} stbi__result_info;\n\n#ifndef STBI_NO_JPEG\nstatic int      stbi__jpeg_test(stbi__context *s);\nstatic void    *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_PNG\nstatic int      stbi__png_test(stbi__context *s);\nstatic void    *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__png_info(stbi__context *s, int *x, int *y, int *comp);\nstatic int      stbi__png_is16(stbi__context *s);\n#endif\n\n#ifndef STBI_NO_BMP\nstatic int      stbi__bmp_test(stbi__context *s);\nstatic void    *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_TGA\nstatic int      stbi__tga_test(stbi__context *s);\nstatic void    *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__tga_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_PSD\nstatic int      stbi__psd_test(stbi__context *s);\nstatic void    *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc);\nstatic int      stbi__psd_info(stbi__context *s, int *x, int *y, int *comp);\nstatic int      stbi__psd_is16(stbi__context *s);\n#endif\n\n#ifndef STBI_NO_HDR\nstatic int      stbi__hdr_test(stbi__context *s);\nstatic float   *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_PIC\nstatic int      stbi__pic_test(stbi__context *s);\nstatic void    *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__pic_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_GIF\nstatic int      stbi__gif_test(stbi__context *s);\nstatic void    *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic void    *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp);\nstatic int      stbi__gif_info(stbi__context *s, int *x, int *y, int *comp);\n#endif\n\n#ifndef STBI_NO_PNM\nstatic int      stbi__pnm_test(stbi__context *s);\nstatic void    *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);\nstatic int      stbi__pnm_is16(stbi__context *s);\n#endif\n\nstatic\n#ifdef STBI_THREAD_LOCAL\nSTBI_THREAD_LOCAL\n#endif\nconst char *stbi__g_failure_reason;\n\nSTBIDEF const char *stbi_failure_reason(void)\n{\n   return stbi__g_failure_reason;\n}\n\n#ifndef STBI_NO_FAILURE_STRINGS\nstatic int stbi__err(const char *str)\n{\n   stbi__g_failure_reason = str;\n   return 0;\n}\n#endif\n\nstatic void *stbi__malloc(size_t size)\n{\n    return STBI_MALLOC(size);\n}\n\n// stb_image uses ints pervasively, including for offset calculations.\n// therefore the largest decoded image size we can support with the\n// current code, even on 64-bit targets, is INT_MAX. this is not a\n// significant limitation for the intended use case.\n//\n// we do, however, need to make sure our size calculations don't\n// overflow. hence a few helper functions for size calculations that\n// multiply integers together, making sure that they're non-negative\n// and no overflow occurs.\n\n// return 1 if the sum is valid, 0 on overflow.\n// negative terms are considered invalid.\nstatic int stbi__addsizes_valid(int a, int b)\n{\n   if (b < 0) return 0;\n   // now 0 <= b <= INT_MAX, hence also\n   // 0 <= INT_MAX - b <= INTMAX.\n   // And \"a + b <= INT_MAX\" (which might overflow) is the\n   // same as a <= INT_MAX - b (no overflow)\n   return a <= INT_MAX - b;\n}\n\n// returns 1 if the product is valid, 0 on overflow.\n// negative factors are considered invalid.\nstatic int stbi__mul2sizes_valid(int a, int b)\n{\n   if (a < 0 || b < 0) return 0;\n   if (b == 0) return 1; // mul-by-0 is always safe\n   // portable way to check for no overflows in a*b\n   return a <= INT_MAX/b;\n}\n\n#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)\n// returns 1 if \"a*b + add\" has no negative terms/factors and doesn't overflow\nstatic int stbi__mad2sizes_valid(int a, int b, int add)\n{\n   return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add);\n}\n#endif\n\n// returns 1 if \"a*b*c + add\" has no negative terms/factors and doesn't overflow\nstatic int stbi__mad3sizes_valid(int a, int b, int c, int add)\n{\n   return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&\n      stbi__addsizes_valid(a*b*c, add);\n}\n\n// returns 1 if \"a*b*c*d + add\" has no negative terms/factors and doesn't overflow\n#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)\nstatic int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)\n{\n   return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&\n      stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add);\n}\n#endif\n\n#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)\n// mallocs with size overflow checking\nstatic void *stbi__malloc_mad2(int a, int b, int add)\n{\n   if (!stbi__mad2sizes_valid(a, b, add)) return NULL;\n   return stbi__malloc(a*b + add);\n}\n#endif\n\nstatic void *stbi__malloc_mad3(int a, int b, int c, int add)\n{\n   if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL;\n   return stbi__malloc(a*b*c + add);\n}\n\n#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)\nstatic void *stbi__malloc_mad4(int a, int b, int c, int d, int add)\n{\n   if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;\n   return stbi__malloc(a*b*c*d + add);\n}\n#endif\n\n// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow.\nstatic int stbi__addints_valid(int a, int b)\n{\n   if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow\n   if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0.\n   return a <= INT_MAX - b;\n}\n\n// returns 1 if the product of two signed shorts is valid, 0 on overflow.\nstatic int stbi__mul2shorts_valid(short a, short b)\n{\n   if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow\n   if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid\n   if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN\n   return a >= SHRT_MIN / b;\n}\n\n// stbi__err - error\n// stbi__errpf - error returning pointer to float\n// stbi__errpuc - error returning pointer to unsigned char\n\n#ifdef STBI_NO_FAILURE_STRINGS\n   #define stbi__err(x,y)  0\n#elif defined(STBI_FAILURE_USERMSG)\n   #define stbi__err(x,y)  stbi__err(y)\n#else\n   #define stbi__err(x,y)  stbi__err(x)\n#endif\n\n#define stbi__errpf(x,y)   ((float *)(size_t) (stbi__err(x,y)?NULL:NULL))\n#define stbi__errpuc(x,y)  ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))\n\nSTBIDEF void stbi_image_free(void *retval_from_stbi_load)\n{\n   STBI_FREE(retval_from_stbi_load);\n}\n\n#ifndef STBI_NO_LINEAR\nstatic float   *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp);\n#endif\n\n#ifndef STBI_NO_HDR\nstatic stbi_uc *stbi__hdr_to_ldr(float   *data, int x, int y, int comp);\n#endif\n\nstatic int stbi__vertically_flip_on_load_global = 0;\n\nSTBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)\n{\n   stbi__vertically_flip_on_load_global = flag_true_if_should_flip;\n}\n\n#ifndef STBI_THREAD_LOCAL\n#define stbi__vertically_flip_on_load  stbi__vertically_flip_on_load_global\n#else\nstatic STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set;\n\nSTBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip)\n{\n   stbi__vertically_flip_on_load_local = flag_true_if_should_flip;\n   stbi__vertically_flip_on_load_set = 1;\n}\n\n#define stbi__vertically_flip_on_load  (stbi__vertically_flip_on_load_set       \\\n                                         ? stbi__vertically_flip_on_load_local  \\\n                                         : stbi__vertically_flip_on_load_global)\n#endif // STBI_THREAD_LOCAL\n\nstatic void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)\n{\n   memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields\n   ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed\n   ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order\n   ri->num_channels = 0;\n\n   // test the formats with a very explicit header first (at least a FOURCC\n   // or distinctive magic number first)\n   #ifndef STBI_NO_PNG\n   if (stbi__png_test(s))  return stbi__png_load(s,x,y,comp,req_comp, ri);\n   #endif\n   #ifndef STBI_NO_BMP\n   if (stbi__bmp_test(s))  return stbi__bmp_load(s,x,y,comp,req_comp, ri);\n   #endif\n   #ifndef STBI_NO_GIF\n   if (stbi__gif_test(s))  return stbi__gif_load(s,x,y,comp,req_comp, ri);\n   #endif\n   #ifndef STBI_NO_PSD\n   if (stbi__psd_test(s))  return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc);\n   #else\n   STBI_NOTUSED(bpc);\n   #endif\n   #ifndef STBI_NO_PIC\n   if (stbi__pic_test(s))  return stbi__pic_load(s,x,y,comp,req_comp, ri);\n   #endif\n\n   // then the formats that can end up attempting to load with just 1 or 2\n   // bytes matching expectations; these are prone to false positives, so\n   // try them later\n   #ifndef STBI_NO_JPEG\n   if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);\n   #endif\n   #ifndef STBI_NO_PNM\n   if (stbi__pnm_test(s))  return stbi__pnm_load(s,x,y,comp,req_comp, ri);\n   #endif\n\n   #ifndef STBI_NO_HDR\n   if (stbi__hdr_test(s)) {\n      float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri);\n      return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);\n   }\n   #endif\n\n   #ifndef STBI_NO_TGA\n   // test tga last because it's a crappy test!\n   if (stbi__tga_test(s))\n      return stbi__tga_load(s,x,y,comp,req_comp, ri);\n   #endif\n\n   return stbi__errpuc(\"unknown image type\", \"Image not of any known type, or corrupt\");\n}\n\nstatic stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels)\n{\n   int i;\n   int img_len = w * h * channels;\n   stbi_uc *reduced;\n\n   reduced = (stbi_uc *) stbi__malloc(img_len);\n   if (reduced == NULL) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n\n   for (i = 0; i < img_len; ++i)\n      reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling\n\n   STBI_FREE(orig);\n   return reduced;\n}\n\nstatic stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels)\n{\n   int i;\n   int img_len = w * h * channels;\n   stbi__uint16 *enlarged;\n\n   enlarged = (stbi__uint16 *) stbi__malloc(img_len*2);\n   if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc(\"outofmem\", \"Out of memory\");\n\n   for (i = 0; i < img_len; ++i)\n      enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff\n\n   STBI_FREE(orig);\n   return enlarged;\n}\n\nstatic void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel)\n{\n   int row;\n   size_t bytes_per_row = (size_t)w * bytes_per_pixel;\n   stbi_uc temp[2048];\n   stbi_uc *bytes = (stbi_uc *)image;\n\n   for (row = 0; row < (h>>1); row++) {\n      stbi_uc *row0 = bytes + row*bytes_per_row;\n      stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row;\n      // swap row0 with row1\n      size_t bytes_left = bytes_per_row;\n      while (bytes_left) {\n         size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);\n         memcpy(temp, row0, bytes_copy);\n         memcpy(row0, row1, bytes_copy);\n         memcpy(row1, temp, bytes_copy);\n         row0 += bytes_copy;\n         row1 += bytes_copy;\n         bytes_left -= bytes_copy;\n      }\n   }\n}\n\n#ifndef STBI_NO_GIF\nstatic void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel)\n{\n   int slice;\n   int slice_size = w * h * bytes_per_pixel;\n\n   stbi_uc *bytes = (stbi_uc *)image;\n   for (slice = 0; slice < z; ++slice) {\n      stbi__vertical_flip(bytes, w, h, bytes_per_pixel);\n      bytes += slice_size;\n   }\n}\n#endif\n\nstatic unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)\n{\n   stbi__result_info ri;\n   void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);\n\n   if (result == NULL)\n      return NULL;\n\n   // it is the responsibility of the loaders to make sure we get either 8 or 16 bit.\n   STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);\n\n   if (ri.bits_per_channel != 8) {\n      result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp);\n      ri.bits_per_channel = 8;\n   }\n\n   // @TODO: move stbi__convert_format to here\n\n   if (stbi__vertically_flip_on_load) {\n      int channels = req_comp ? req_comp : *comp;\n      stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc));\n   }\n\n   return (unsigned char *) result;\n}\n\nstatic stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)\n{\n   stbi__result_info ri;\n   void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16);\n\n   if (result == NULL)\n      return NULL;\n\n   // it is the responsibility of the loaders to make sure we get either 8 or 16 bit.\n   STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);\n\n   if (ri.bits_per_channel != 16) {\n      result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp);\n      ri.bits_per_channel = 16;\n   }\n\n   // @TODO: move stbi__convert_format16 to here\n   // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision\n\n   if (stbi__vertically_flip_on_load) {\n      int channels = req_comp ? req_comp : *comp;\n      stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16));\n   }\n\n   return (stbi__uint16 *) result;\n}\n\n#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR)\nstatic void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp)\n{\n   if (stbi__vertically_flip_on_load && result != NULL) {\n      int channels = req_comp ? req_comp : *comp;\n      stbi__vertical_flip(result, *x, *y, channels * sizeof(float));\n   }\n}\n#endif\n\n#ifndef STBI_NO_STDIO\n\n#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)\nSTBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);\nSTBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);\n#endif\n\n#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)\nSTBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)\n{\n\treturn WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);\n}\n#endif\n\nstatic FILE *stbi__fopen(char const *filename, char const *mode)\n{\n   FILE *f;\n#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)\n   wchar_t wMode[64];\n   wchar_t wFilename[1024];\n\tif (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))\n      return 0;\n\n\tif (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))\n      return 0;\n\n#if defined(_MSC_VER) && _MSC_VER >= 1400\n\tif (0 != _wfopen_s(&f, wFilename, wMode))\n\t\tf = 0;\n#else\n   f = _wfopen(wFilename, wMode);\n#endif\n\n#elif defined(_MSC_VER) && _MSC_VER >= 1400\n   if (0 != fopen_s(&f, filename, mode))\n      f=0;\n#else\n   f = fopen(filename, mode);\n#endif\n   return f;\n}\n\n\nSTBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)\n{\n   FILE *f = stbi__fopen(filename, \"rb\");\n   unsigned char *result;\n   if (!f) return stbi__errpuc(\"can't fopen\", \"Unable to open file\");\n   result = stbi_load_from_file(f,x,y,comp,req_comp);\n   fclose(f);\n   return result;\n}\n\nSTBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)\n{\n   unsigned char *result;\n   stbi__context s;\n   stbi__start_file(&s,f);\n   result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);\n   if (result) {\n      // need to 'unget' all the characters in the IO buffer\n      fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);\n   }\n   return result;\n}\n\nSTBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp)\n{\n   stbi__uint16 *result;\n   stbi__context s;\n   stbi__start_file(&s,f);\n   result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp);\n   if (result) {\n      // need to 'unget' all the characters in the IO buffer\n      fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);\n   }\n   return result;\n}\n\nSTBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp)\n{\n   FILE *f = stbi__fopen(filename, \"rb\");\n   stbi__uint16 *result;\n   if (!f) return (stbi_us *) stbi__errpuc(\"can't fopen\", \"Unable to open file\");\n   result = stbi_load_from_file_16(f,x,y,comp,req_comp);\n   fclose(f);\n   return result;\n}\n\n\n#endif //!STBI_NO_STDIO\n\nSTBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels)\n{\n   stbi__context s;\n   stbi__start_mem(&s,buffer,len);\n   return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);\n}\n\nSTBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels)\n{\n   stbi__context s;\n   stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user);\n   return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);\n}\n\nSTBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)\n{\n   stbi__context s;\n   stbi__start_mem(&s,buffer,len);\n   return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);\n}\n\nSTBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)\n{\n   stbi__context s;\n   stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);\n   return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);\n}\n\n#ifndef STBI_NO_GIF\nSTBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp)\n{\n   unsigned char *result;\n   stbi__context s;\n   stbi__start_mem(&s,buffer,len);\n\n   result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp);\n   if (stbi__vertically_flip_on_load) {\n      stbi__vertical_flip_slices( result, *x, *y, *z, *comp );\n   }\n\n   return result;\n}\n#endif\n\n#ifndef STBI_NO_LINEAR\nstatic float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)\n{\n   unsigned char *data;\n   #ifndef STBI_NO_HDR\n   if (stbi__hdr_test(s)) {\n      stbi__result_info ri;\n      float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri);\n      if (hdr_data)\n         stbi__float_postprocess(hdr_data,x,y,comp,req_comp);\n      return hdr_data;\n   }\n   #endif\n   data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp);\n   if (data)\n      return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);\n   return stbi__errpf(\"unknown image type\", \"Image not of any known type, or corrupt\");\n}\n\nSTBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)\n{\n   stbi__context s;\n   stbi__start_mem(&s,buffer,len);\n   return stbi__loadf_main(&s,x,y,comp,req_comp);\n}\n\nSTBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)\n{\n   stbi__context s;\n   stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);\n   return stbi__loadf_main(&s,x,y,comp,req_comp);\n}\n\n#ifndef STBI_NO_STDIO\nSTBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)\n{\n   float *result;\n   FILE *f = stbi__fopen(filename, \"rb\");\n   if (!f) return stbi__errpf(\"can't fopen\", \"Unable to open file\");\n   result = stbi_loadf_from_file(f,x,y,comp,req_comp);\n   fclose(f);\n   return result;\n}\n\nSTBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)\n{\n   stbi__context s;\n   stbi__start_file(&s,f);\n   return stbi__loadf_main(&s,x,y,comp,req_comp);\n}\n#endif // !STBI_NO_STDIO\n\n#endif // !STBI_NO_LINEAR\n\n// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is\n// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always\n// reports false!\n\nSTBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)\n{\n   #ifndef STBI_NO_HDR\n   stbi__context s;\n   stbi__start_mem(&s,buffer,len);\n   return stbi__hdr_test(&s);\n   #else\n   STBI_NOTUSED(buffer);\n   STBI_NOTUSED(len);\n   return 0;\n   #endif\n}\n\n#ifndef STBI_NO_STDIO\nSTBIDEF int      stbi_is_hdr          (char const *filename)\n{\n   FILE *f = stbi__fopen(filename, \"rb\");\n   int result=0;\n   if (f) {\n      result = stbi_is_hdr_from_file(f);\n      fclose(f);\n   }\n   return result;\n}\n\nSTBIDEF int stbi_is_hdr_from_file(FILE *f)\n{\n   #ifndef STBI_NO_HDR\n   long pos = ftell(f);\n   int res;\n   stbi__context s;\n   stbi__start_file(&s,f);\n   res = stbi__hdr_test(&s);\n   fseek(f, pos, SEEK_SET);\n   return res;\n   #else\n   STBI_NOTUSED(f);\n   return 0;\n   #endif\n}\n#endif // !STBI_NO_STDIO\n\nSTBIDEF int      stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user)\n{\n   #ifndef STBI_NO_HDR\n   stbi__context s;\n   stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);\n   return stbi__hdr_test(&s);\n   #else\n   STBI_NOTUSED(clbk);\n   STBI_NOTUSED(user);\n   return 0;\n   #endif\n}\n\n#ifndef STBI_NO_LINEAR\nstatic float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f;\n\nSTBIDEF void   stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; }\nSTBIDEF void   stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }\n#endif\n\nstatic float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;\n\nSTBIDEF void   stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; }\nSTBIDEF void   stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; }\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Common code used by all image loaders\n//\n\nenum\n{\n   STBI__SCAN_load=0,\n   STBI__SCAN_type,\n   STBI__SCAN_header\n};\n\nstatic void stbi__refill_buffer(stbi__context *s)\n{\n   int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);\n   s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original);\n   if (n == 0) {\n      // at end of file, treat same as if from memory, but need to handle case\n      // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file\n      s->read_from_callbacks = 0;\n      s->img_buffer = s->buffer_start;\n      s->img_buffer_end = s->buffer_start+1;\n      *s->img_buffer = 0;\n   } else {\n      s->img_buffer = s->buffer_start;\n      s->img_buffer_end = s->buffer_start + n;\n   }\n}\n\nstbi_inline static stbi_uc stbi__get8(stbi__context *s)\n{\n   if (s->img_buffer < s->img_buffer_end)\n      return *s->img_buffer++;\n   if (s->read_from_callbacks) {\n      stbi__refill_buffer(s);\n      return *s->img_buffer++;\n   }\n   return 0;\n}\n\n#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)\n// nothing\n#else\nstbi_inline static int stbi__at_eof(stbi__context *s)\n{\n   if (s->io.read) {\n      if (!(s->io.eof)(s->io_user_data)) return 0;\n      // if feof() is true, check if buffer = end\n      // special case: we've only got the special 0 character at the end\n      if (s->read_from_callbacks == 0) return 1;\n   }\n\n   return s->img_buffer >= s->img_buffer_end;\n}\n#endif\n\n#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC)\n// nothing\n#else\nstatic void stbi__skip(stbi__context *s, int n)\n{\n   if (n == 0) return;  // already there!\n   if (n < 0) {\n      s->img_buffer = s->img_buffer_end;\n      return;\n   }\n   if (s->io.read) {\n      int blen = (int) (s->img_buffer_end - s->img_buffer);\n      if (blen < n) {\n         s->img_buffer = s->img_buffer_end;\n         (s->io.skip)(s->io_user_data, n - blen);\n         return;\n      }\n   }\n   s->img_buffer += n;\n}\n#endif\n\n#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM)\n// nothing\n#else\nstatic int stbi__getn(stbi__context *s, stbi_uc *buffer, int n)\n{\n   if (s->io.read) {\n      int blen = (int) (s->img_buffer_end - s->img_buffer);\n      if (blen < n) {\n         int res, count;\n\n         memcpy(buffer, s->img_buffer, blen);\n\n         count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen);\n         res = (count == (n-blen));\n         s->img_buffer = s->img_buffer_end;\n         return res;\n      }\n   }\n\n   if (s->img_buffer+n <= s->img_buffer_end) {\n      memcpy(buffer, s->img_buffer, n);\n      s->img_buffer += n;\n      return 1;\n   } else\n      return 0;\n}\n#endif\n\n#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)\n// nothing\n#else\nstatic int stbi__get16be(stbi__context *s)\n{\n   int z = stbi__get8(s);\n   return (z << 8) + stbi__get8(s);\n}\n#endif\n\n#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)\n// nothing\n#else\nstatic stbi__uint32 stbi__get32be(stbi__context *s)\n{\n   stbi__uint32 z = stbi__get16be(s);\n   return (z << 16) + stbi__get16be(s);\n}\n#endif\n\n#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF)\n// nothing\n#else\nstatic int stbi__get16le(stbi__context *s)\n{\n   int z = stbi__get8(s);\n   return z + (stbi__get8(s) << 8);\n}\n#endif\n\n#ifndef STBI_NO_BMP\nstatic stbi__uint32 stbi__get32le(stbi__context *s)\n{\n   stbi__uint32 z = stbi__get16le(s);\n   z += (stbi__uint32)stbi__get16le(s) << 16;\n   return z;\n}\n#endif\n\n#define STBI__BYTECAST(x)  ((stbi_uc) ((x) & 255))  // truncate int to byte without warnings\n\n#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)\n// nothing\n#else\n//////////////////////////////////////////////////////////////////////////////\n//\n//  generic converter from built-in img_n to req_comp\n//    individual types do this automatically as much as possible (e.g. jpeg\n//    does all cases internally since it needs to colorspace convert anyway,\n//    and it never has alpha, so very few cases ). png can automatically\n//    interleave an alpha=255 channel, but falls back to this for other cases\n//\n//  assume data buffer is malloced, so malloc a new one and free that one\n//  only failure mode is malloc failing\n\nstatic stbi_uc stbi__compute_y(int r, int g, int b)\n{\n   return (stbi_uc) (((r*77) + (g*150) +  (29*b)) >> 8);\n}\n#endif\n\n#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)\n// nothing\n#else\nstatic unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y)\n{\n   int i,j;\n   unsigned char *good;\n\n   if (req_comp == img_n) return data;\n   STBI_ASSERT(req_comp >= 1 && req_comp <= 4);\n\n   good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0);\n   if (good == NULL) {\n      STBI_FREE(data);\n      return stbi__errpuc(\"outofmem\", \"Out of memory\");\n   }\n\n   for (j=0; j < (int) y; ++j) {\n      unsigned char *src  = data + j * x * img_n   ;\n      unsigned char *dest = good + j * x * req_comp;\n\n      #define STBI__COMBO(a,b)  ((a)*8+(b))\n      #define STBI__CASE(a,b)   case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)\n      // convert source image with img_n components to one with req_comp components;\n      // avoid switch per pixel, so use switch per scanline and massive macros\n      switch (STBI__COMBO(img_n, req_comp)) {\n         STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255;                                     } break;\n         STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0];                                  } break;\n         STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255;                     } break;\n         STBI__CASE(2,1) { dest[0]=src[0];                                                  } break;\n         STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0];                                  } break;\n         STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1];                  } break;\n         STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255;        } break;\n         STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]);                   } break;\n         STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255;    } break;\n         STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]);                   } break;\n         STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break;\n         STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];                    } break;\n         default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc(\"unsupported\", \"Unsupported format conversion\");\n      }\n      #undef STBI__CASE\n   }\n\n   STBI_FREE(data);\n   return good;\n}\n#endif\n\n#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)\n// nothing\n#else\nstatic stbi__uint16 stbi__compute_y_16(int r, int g, int b)\n{\n   return (stbi__uint16) (((r*77) + (g*150) +  (29*b)) >> 8);\n}\n#endif\n\n#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)\n// nothing\n#else\nstatic stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y)\n{\n   int i,j;\n   stbi__uint16 *good;\n\n   if (req_comp == img_n) return data;\n   STBI_ASSERT(req_comp >= 1 && req_comp <= 4);\n\n   good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2);\n   if (good == NULL) {\n      STBI_FREE(data);\n      return (stbi__uint16 *) stbi__errpuc(\"outofmem\", \"Out of memory\");\n   }\n\n   for (j=0; j < (int) y; ++j) {\n      stbi__uint16 *src  = data + j * x * img_n   ;\n      stbi__uint16 *dest = good + j * x * req_comp;\n\n      #define STBI__COMBO(a,b)  ((a)*8+(b))\n      #define STBI__CASE(a,b)   case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)\n      // convert source image with img_n components to one with req_comp components;\n      // avoid switch per pixel, so use switch per scanline and massive macros\n      switch (STBI__COMBO(img_n, req_comp)) {\n         STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff;                                     } break;\n         STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0];                                     } break;\n         STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff;                     } break;\n         STBI__CASE(2,1) { dest[0]=src[0];                                                     } break;\n         STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0];                                     } break;\n         STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1];                     } break;\n         STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff;        } break;\n         STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]);                   } break;\n         STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break;\n         STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]);                   } break;\n         STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break;\n         STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];                       } break;\n         default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc(\"unsupported\", \"Unsupported format conversion\");\n      }\n      #undef STBI__CASE\n   }\n\n   STBI_FREE(data);\n   return good;\n}\n#endif\n\n#ifndef STBI_NO_LINEAR\nstatic float   *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp)\n{\n   int i,k,n;\n   float *output;\n   if (!data) return NULL;\n   output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0);\n   if (output == NULL) { STBI_FREE(data); return stbi__errpf(\"outofmem\", \"Out of memory\"); }\n   // compute number of non-alpha components\n   if (comp & 1) n = comp; else n = comp-1;\n   for (i=0; i < x*y; ++i) {\n      for (k=0; k < n; ++k) {\n         output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale);\n      }\n   }\n   if (n < comp) {\n      for (i=0; i < x*y; ++i) {\n         output[i*comp + n] = data[i*comp + n]/255.0f;\n      }\n   }\n   STBI_FREE(data);\n   return output;\n}\n#endif\n\n#ifndef STBI_NO_HDR\n#define stbi__float2int(x)   ((int) (x))\nstatic stbi_uc *stbi__hdr_to_ldr(float   *data, int x, int y, int comp)\n{\n   int i,k,n;\n   stbi_uc *output;\n   if (!data) return NULL;\n   output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0);\n   if (output == NULL) { STBI_FREE(data); return stbi__errpuc(\"outofmem\", \"Out of memory\"); }\n   // compute number of non-alpha components\n   if (comp & 1) n = comp; else n = comp-1;\n   for (i=0; i < x*y; ++i) {\n      for (k=0; k < n; ++k) {\n         float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f;\n         if (z < 0) z = 0;\n         if (z > 255) z = 255;\n         output[i*comp + k] = (stbi_uc) stbi__float2int(z);\n      }\n      if (k < comp) {\n         float z = data[i*comp+k] * 255 + 0.5f;\n         if (z < 0) z = 0;\n         if (z > 255) z = 255;\n         output[i*comp + k] = (stbi_uc) stbi__float2int(z);\n      }\n   }\n   STBI_FREE(data);\n   return output;\n}\n#endif\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//  \"baseline\" JPEG/JFIF decoder\n//\n//    simple implementation\n//      - doesn't support delayed output of y-dimension\n//      - simple interface (only one output format: 8-bit interleaved RGB)\n//      - doesn't try to recover corrupt jpegs\n//      - doesn't allow partial loading, loading multiple at once\n//      - still fast on x86 (copying globals into locals doesn't help x86)\n//      - allocates lots of intermediate memory (full size of all components)\n//        - non-interleaved case requires this anyway\n//        - allows good upsampling (see next)\n//    high-quality\n//      - upsampled channels are bilinearly interpolated, even across blocks\n//      - quality integer IDCT derived from IJG's 'slow'\n//    performance\n//      - fast huffman; reasonable integer IDCT\n//      - some SIMD kernels for common paths on targets with SSE2/NEON\n//      - uses a lot of intermediate memory, could cache poorly\n\n#ifndef STBI_NO_JPEG\n\n// huffman decoding acceleration\n#define FAST_BITS   9  // larger handles more cases; smaller stomps less cache\n\ntypedef struct\n{\n   stbi_uc  fast[1 << FAST_BITS];\n   // weirdly, repacking this into AoS is a 10% speed loss, instead of a win\n   stbi__uint16 code[256];\n   stbi_uc  values[256];\n   stbi_uc  size[257];\n   unsigned int maxcode[18];\n   int    delta[17];   // old 'firstsymbol' - old 'firstcode'\n} stbi__huffman;\n\ntypedef struct\n{\n   stbi__context *s;\n   stbi__huffman huff_dc[4];\n   stbi__huffman huff_ac[4];\n   stbi__uint16 dequant[4][64];\n   stbi__int16 fast_ac[4][1 << FAST_BITS];\n\n// sizes for components, interleaved MCUs\n   int img_h_max, img_v_max;\n   int img_mcu_x, img_mcu_y;\n   int img_mcu_w, img_mcu_h;\n\n// definition of jpeg image component\n   struct\n   {\n      int id;\n      int h,v;\n      int tq;\n      int hd,ha;\n      int dc_pred;\n\n      int x,y,w2,h2;\n      stbi_uc *data;\n      void *raw_data, *raw_coeff;\n      stbi_uc *linebuf;\n      short   *coeff;   // progressive only\n      int      coeff_w, coeff_h; // number of 8x8 coefficient blocks\n   } img_comp[4];\n\n   stbi__uint32   code_buffer; // jpeg entropy-coded buffer\n   int            code_bits;   // number of valid bits\n   unsigned char  marker;      // marker seen while filling entropy buffer\n   int            nomore;      // flag if we saw a marker so must stop\n\n   int            progressive;\n   int            spec_start;\n   int            spec_end;\n   int            succ_high;\n   int            succ_low;\n   int            eob_run;\n   int            jfif;\n   int            app14_color_transform; // Adobe APP14 tag\n   int            rgb;\n\n   int scan_n, order[4];\n   int restart_interval, todo;\n\n// kernels\n   void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]);\n   void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step);\n   stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs);\n} stbi__jpeg;\n\nstatic int stbi__build_huffman(stbi__huffman *h, int *count)\n{\n   int i,j,k=0;\n   unsigned int code;\n   // build size list for each symbol (from JPEG spec)\n   for (i=0; i < 16; ++i) {\n      for (j=0; j < count[i]; ++j) {\n         h->size[k++] = (stbi_uc) (i+1);\n         if(k >= 257) return stbi__err(\"bad size list\",\"Corrupt JPEG\");\n      }\n   }\n   h->size[k] = 0;\n\n   // compute actual symbols (from jpeg spec)\n   code = 0;\n   k = 0;\n   for(j=1; j <= 16; ++j) {\n      // compute delta to add to code to compute symbol id\n      h->delta[j] = k - code;\n      if (h->size[k] == j) {\n         while (h->size[k] == j)\n            h->code[k++] = (stbi__uint16) (code++);\n         if (code-1 >= (1u << j)) return stbi__err(\"bad code lengths\",\"Corrupt JPEG\");\n      }\n      // compute largest code + 1 for this size, preshifted as needed later\n      h->maxcode[j] = code << (16-j);\n      code <<= 1;\n   }\n   h->maxcode[j] = 0xffffffff;\n\n   // build non-spec acceleration table; 255 is flag for not-accelerated\n   memset(h->fast, 255, 1 << FAST_BITS);\n   for (i=0; i < k; ++i) {\n      int s = h->size[i];\n      if (s <= FAST_BITS) {\n         int c = h->code[i] << (FAST_BITS-s);\n         int m = 1 << (FAST_BITS-s);\n         for (j=0; j < m; ++j) {\n            h->fast[c+j] = (stbi_uc) i;\n         }\n      }\n   }\n   return 1;\n}\n\n// build a table that decodes both magnitude and value of small ACs in\n// one go.\nstatic void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h)\n{\n   int i;\n   for (i=0; i < (1 << FAST_BITS); ++i) {\n      stbi_uc fast = h->fast[i];\n      fast_ac[i] = 0;\n      if (fast < 255) {\n         int rs = h->values[fast];\n         int run = (rs >> 4) & 15;\n         int magbits = rs & 15;\n         int len = h->size[fast];\n\n         if (magbits && len + magbits <= FAST_BITS) {\n            // magnitude code followed by receive_extend code\n            int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits);\n            int m = 1 << (magbits - 1);\n            if (k < m) k += (~0U << magbits) + 1;\n            // if the result is small enough, we can fit it in fast_ac table\n            if (k >= -128 && k <= 127)\n               fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits));\n         }\n      }\n   }\n}\n\nstatic void stbi__grow_buffer_unsafe(stbi__jpeg *j)\n{\n   do {\n      unsigned int b = j->nomore ? 0 : stbi__get8(j->s);\n      if (b == 0xff) {\n         int c = stbi__get8(j->s);\n         while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes\n         if (c != 0) {\n            j->marker = (unsigned char) c;\n            j->nomore = 1;\n            return;\n         }\n      }\n      j->code_buffer |= b << (24 - j->code_bits);\n      j->code_bits += 8;\n   } while (j->code_bits <= 24);\n}\n\n// (1 << n) - 1\nstatic const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};\n\n// decode a jpeg huffman value from the bitstream\nstbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)\n{\n   unsigned int temp;\n   int c,k;\n\n   if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n\n   // look at the top FAST_BITS and determine what symbol ID it is,\n   // if the code is <= FAST_BITS\n   c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);\n   k = h->fast[c];\n   if (k < 255) {\n      int s = h->size[k];\n      if (s > j->code_bits)\n         return -1;\n      j->code_buffer <<= s;\n      j->code_bits -= s;\n      return h->values[k];\n   }\n\n   // naive test is to shift the code_buffer down so k bits are\n   // valid, then test against maxcode. To speed this up, we've\n   // preshifted maxcode left so that it has (16-k) 0s at the\n   // end; in other words, regardless of the number of bits, it\n   // wants to be compared against something shifted to have 16;\n   // that way we don't need to shift inside the loop.\n   temp = j->code_buffer >> 16;\n   for (k=FAST_BITS+1 ; ; ++k)\n      if (temp < h->maxcode[k])\n         break;\n   if (k == 17) {\n      // error! code not found\n      j->code_bits -= 16;\n      return -1;\n   }\n\n   if (k > j->code_bits)\n      return -1;\n\n   // convert the huffman code to the symbol id\n   c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];\n   if(c < 0 || c >= 256) // symbol id out of bounds!\n       return -1;\n   STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);\n\n   // convert the id to a symbol\n   j->code_bits -= k;\n   j->code_buffer <<= k;\n   return h->values[c];\n}\n\n// bias[n] = (-1<<n) + 1\nstatic const int stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767};\n\n// combined JPEG 'receive' and JPEG 'extend', since baseline\n// always extends everything it receives.\nstbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)\n{\n   unsigned int k;\n   int sgn;\n   if (j->code_bits < n) stbi__grow_buffer_unsafe(j);\n   if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing\n\n   sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)\n   k = stbi_lrot(j->code_buffer, n);\n   j->code_buffer = k & ~stbi__bmask[n];\n   k &= stbi__bmask[n];\n   j->code_bits -= n;\n   return k + (stbi__jbias[n] & (sgn - 1));\n}\n\n// get some unsigned bits\nstbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)\n{\n   unsigned int k;\n   if (j->code_bits < n) stbi__grow_buffer_unsafe(j);\n   if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing\n   k = stbi_lrot(j->code_buffer, n);\n   j->code_buffer = k & ~stbi__bmask[n];\n   k &= stbi__bmask[n];\n   j->code_bits -= n;\n   return k;\n}\n\nstbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)\n{\n   unsigned int k;\n   if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);\n   if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing\n   k = j->code_buffer;\n   j->code_buffer <<= 1;\n   --j->code_bits;\n   return k & 0x80000000;\n}\n\n// given a value that's at position X in the zigzag stream,\n// where does it appear in the 8x8 matrix coded as row-major?\nstatic const stbi_uc stbi__jpeg_dezigzag[64+15] =\n{\n    0,  1,  8, 16,  9,  2,  3, 10,\n   17, 24, 32, 25, 18, 11,  4,  5,\n   12, 19, 26, 33, 40, 48, 41, 34,\n   27, 20, 13,  6,  7, 14, 21, 28,\n   35, 42, 49, 56, 57, 50, 43, 36,\n   29, 22, 15, 23, 30, 37, 44, 51,\n   58, 59, 52, 45, 38, 31, 39, 46,\n   53, 60, 61, 54, 47, 55, 62, 63,\n   // let corrupt input sample past end\n   63, 63, 63, 63, 63, 63, 63, 63,\n   63, 63, 63, 63, 63, 63, 63\n};\n\n// decode one 64-entry block--\nstatic int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant)\n{\n   int diff,dc,k;\n   int t;\n\n   if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n   t = stbi__jpeg_huff_decode(j, hdc);\n   if (t < 0 || t > 15) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n\n   // 0 all the ac values now so we can do it 32-bits at a time\n   memset(data,0,64*sizeof(data[0]));\n\n   diff = t ? stbi__extend_receive(j, t) : 0;\n   if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err(\"bad delta\",\"Corrupt JPEG\");\n   dc = j->img_comp[b].dc_pred + diff;\n   j->img_comp[b].dc_pred = dc;\n   if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err(\"can't merge dc and ac\", \"Corrupt JPEG\");\n   data[0] = (short) (dc * dequant[0]);\n\n   // decode AC components, see JPEG spec\n   k = 1;\n   do {\n      unsigned int zig;\n      int c,r,s;\n      if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n      c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);\n      r = fac[c];\n      if (r) { // fast-AC path\n         k += (r >> 4) & 15; // run\n         s = r & 15; // combined length\n         if (s > j->code_bits) return stbi__err(\"bad huffman code\", \"Combined length longer than code bits available\");\n         j->code_buffer <<= s;\n         j->code_bits -= s;\n         // decode into unzigzag'd location\n         zig = stbi__jpeg_dezigzag[k++];\n         data[zig] = (short) ((r >> 8) * dequant[zig]);\n      } else {\n         int rs = stbi__jpeg_huff_decode(j, hac);\n         if (rs < 0) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n         s = rs & 15;\n         r = rs >> 4;\n         if (s == 0) {\n            if (rs != 0xf0) break; // end block\n            k += 16;\n         } else {\n            k += r;\n            // decode into unzigzag'd location\n            zig = stbi__jpeg_dezigzag[k++];\n            data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]);\n         }\n      }\n   } while (k < 64);\n   return 1;\n}\n\nstatic int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b)\n{\n   int diff,dc;\n   int t;\n   if (j->spec_end != 0) return stbi__err(\"can't merge dc and ac\", \"Corrupt JPEG\");\n\n   if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n\n   if (j->succ_high == 0) {\n      // first scan for DC coefficient, must be first\n      memset(data,0,64*sizeof(data[0])); // 0 all the ac values now\n      t = stbi__jpeg_huff_decode(j, hdc);\n      if (t < 0 || t > 15) return stbi__err(\"can't merge dc and ac\", \"Corrupt JPEG\");\n      diff = t ? stbi__extend_receive(j, t) : 0;\n\n      if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err(\"bad delta\", \"Corrupt JPEG\");\n      dc = j->img_comp[b].dc_pred + diff;\n      j->img_comp[b].dc_pred = dc;\n      if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err(\"can't merge dc and ac\", \"Corrupt JPEG\");\n      data[0] = (short) (dc * (1 << j->succ_low));\n   } else {\n      // refinement scan for DC coefficient\n      if (stbi__jpeg_get_bit(j))\n         data[0] += (short) (1 << j->succ_low);\n   }\n   return 1;\n}\n\n// @OPTIMIZE: store non-zigzagged during the decode passes,\n// and only de-zigzag when dequantizing\nstatic int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac)\n{\n   int k;\n   if (j->spec_start == 0) return stbi__err(\"can't merge dc and ac\", \"Corrupt JPEG\");\n\n   if (j->succ_high == 0) {\n      int shift = j->succ_low;\n\n      if (j->eob_run) {\n         --j->eob_run;\n         return 1;\n      }\n\n      k = j->spec_start;\n      do {\n         unsigned int zig;\n         int c,r,s;\n         if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n         c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);\n         r = fac[c];\n         if (r) { // fast-AC path\n            k += (r >> 4) & 15; // run\n            s = r & 15; // combined length\n            if (s > j->code_bits) return stbi__err(\"bad huffman code\", \"Combined length longer than code bits available\");\n            j->code_buffer <<= s;\n            j->code_bits -= s;\n            zig = stbi__jpeg_dezigzag[k++];\n            data[zig] = (short) ((r >> 8) * (1 << shift));\n         } else {\n            int rs = stbi__jpeg_huff_decode(j, hac);\n            if (rs < 0) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n            s = rs & 15;\n            r = rs >> 4;\n            if (s == 0) {\n               if (r < 15) {\n                  j->eob_run = (1 << r);\n                  if (r)\n                     j->eob_run += stbi__jpeg_get_bits(j, r);\n                  --j->eob_run;\n                  break;\n               }\n               k += 16;\n            } else {\n               k += r;\n               zig = stbi__jpeg_dezigzag[k++];\n               data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift));\n            }\n         }\n      } while (k <= j->spec_end);\n   } else {\n      // refinement scan for these AC coefficients\n\n      short bit = (short) (1 << j->succ_low);\n\n      if (j->eob_run) {\n         --j->eob_run;\n         for (k = j->spec_start; k <= j->spec_end; ++k) {\n            short *p = &data[stbi__jpeg_dezigzag[k]];\n            if (*p != 0)\n               if (stbi__jpeg_get_bit(j))\n                  if ((*p & bit)==0) {\n                     if (*p > 0)\n                        *p += bit;\n                     else\n                        *p -= bit;\n                  }\n         }\n      } else {\n         k = j->spec_start;\n         do {\n            int r,s;\n            int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh\n            if (rs < 0) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n            s = rs & 15;\n            r = rs >> 4;\n            if (s == 0) {\n               if (r < 15) {\n                  j->eob_run = (1 << r) - 1;\n                  if (r)\n                     j->eob_run += stbi__jpeg_get_bits(j, r);\n                  r = 64; // force end of block\n               } else {\n                  // r=15 s=0 should write 16 0s, so we just do\n                  // a run of 15 0s and then write s (which is 0),\n                  // so we don't have to do anything special here\n               }\n            } else {\n               if (s != 1) return stbi__err(\"bad huffman code\", \"Corrupt JPEG\");\n               // sign bit\n               if (stbi__jpeg_get_bit(j))\n                  s = bit;\n               else\n                  s = -bit;\n            }\n\n            // advance by r\n            while (k <= j->spec_end) {\n               short *p = &data[stbi__jpeg_dezigzag[k++]];\n               if (*p != 0) {\n                  if (stbi__jpeg_get_bit(j))\n                     if ((*p & bit)==0) {\n                        if (*p > 0)\n                           *p += bit;\n                        else\n                           *p -= bit;\n                     }\n               } else {\n                  if (r == 0) {\n                     *p = (short) s;\n                     break;\n                  }\n                  --r;\n               }\n            }\n         } while (k <= j->spec_end);\n      }\n   }\n   return 1;\n}\n\n// take a -128..127 value and stbi__clamp it and convert to 0..255\nstbi_inline static stbi_uc stbi__clamp(int x)\n{\n   // trick to use a single test to catch both cases\n   if ((unsigned int) x > 255) {\n      if (x < 0) return 0;\n      if (x > 255) return 255;\n   }\n   return (stbi_uc) x;\n}\n\n#define stbi__f2f(x)  ((int) (((x) * 4096 + 0.5)))\n#define stbi__fsh(x)  ((x) * 4096)\n\n// derived from jidctint -- DCT_ISLOW\n#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \\\n   int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \\\n   p2 = s2;                                    \\\n   p3 = s6;                                    \\\n   p1 = (p2+p3) * stbi__f2f(0.5411961f);       \\\n   t2 = p1 + p3*stbi__f2f(-1.847759065f);      \\\n   t3 = p1 + p2*stbi__f2f( 0.765366865f);      \\\n   p2 = s0;                                    \\\n   p3 = s4;                                    \\\n   t0 = stbi__fsh(p2+p3);                      \\\n   t1 = stbi__fsh(p2-p3);                      \\\n   x0 = t0+t3;                                 \\\n   x3 = t0-t3;                                 \\\n   x1 = t1+t2;                                 \\\n   x2 = t1-t2;                                 \\\n   t0 = s7;                                    \\\n   t1 = s5;                                    \\\n   t2 = s3;                                    \\\n   t3 = s1;                                    \\\n   p3 = t0+t2;                                 \\\n   p4 = t1+t3;                                 \\\n   p1 = t0+t3;                                 \\\n   p2 = t1+t2;                                 \\\n   p5 = (p3+p4)*stbi__f2f( 1.175875602f);      \\\n   t0 = t0*stbi__f2f( 0.298631336f);           \\\n   t1 = t1*stbi__f2f( 2.053119869f);           \\\n   t2 = t2*stbi__f2f( 3.072711026f);           \\\n   t3 = t3*stbi__f2f( 1.501321110f);           \\\n   p1 = p5 + p1*stbi__f2f(-0.899976223f);      \\\n   p2 = p5 + p2*stbi__f2f(-2.562915447f);      \\\n   p3 = p3*stbi__f2f(-1.961570560f);           \\\n   p4 = p4*stbi__f2f(-0.390180644f);           \\\n   t3 += p1+p4;                                \\\n   t2 += p2+p3;                                \\\n   t1 += p2+p4;                                \\\n   t0 += p1+p3;\n\nstatic void stbi__idct_block(stbi_uc *out, int out_stride, short data[64])\n{\n   int i,val[64],*v=val;\n   stbi_uc *o;\n   short *d = data;\n\n   // columns\n   for (i=0; i < 8; ++i,++d, ++v) {\n      // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing\n      if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0\n           && d[40]==0 && d[48]==0 && d[56]==0) {\n         //    no shortcut                 0     seconds\n         //    (1|2|3|4|5|6|7)==0          0     seconds\n         //    all separate               -0.047 seconds\n         //    1 && 2|3 && 4|5 && 6|7:    -0.047 seconds\n         int dcterm = d[0]*4;\n         v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;\n      } else {\n         STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56])\n         // constants scaled things up by 1<<12; let's bring them back\n         // down, but keep 2 extra bits of precision\n         x0 += 512; x1 += 512; x2 += 512; x3 += 512;\n         v[ 0] = (x0+t3) >> 10;\n         v[56] = (x0-t3) >> 10;\n         v[ 8] = (x1+t2) >> 10;\n         v[48] = (x1-t2) >> 10;\n         v[16] = (x2+t1) >> 10;\n         v[40] = (x2-t1) >> 10;\n         v[24] = (x3+t0) >> 10;\n         v[32] = (x3-t0) >> 10;\n      }\n   }\n\n   for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {\n      // no fast case since the first 1D IDCT spread components out\n      STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])\n      // constants scaled things up by 1<<12, plus we had 1<<2 from first\n      // loop, plus horizontal and vertical each scale by sqrt(8) so together\n      // we've got an extra 1<<3, so 1<<17 total we need to remove.\n      // so we want to round that, which means adding 0.5 * 1<<17,\n      // aka 65536. Also, we'll end up with -128 to 127 that we want\n      // to encode as 0..255 by adding 128, so we'll add that before the shift\n      x0 += 65536 + (128<<17);\n      x1 += 65536 + (128<<17);\n      x2 += 65536 + (128<<17);\n      x3 += 65536 + (128<<17);\n      // tried computing the shifts into temps, or'ing the temps to see\n      // if any were out of range, but that was slower\n      o[0] = stbi__clamp((x0+t3) >> 17);\n      o[7] = stbi__clamp((x0-t3) >> 17);\n      o[1] = stbi__clamp((x1+t2) >> 17);\n      o[6] = stbi__clamp((x1-t2) >> 17);\n      o[2] = stbi__clamp((x2+t1) >> 17);\n      o[5] = stbi__clamp((x2-t1) >> 17);\n      o[3] = stbi__clamp((x3+t0) >> 17);\n      o[4] = stbi__clamp((x3-t0) >> 17);\n   }\n}\n\n#ifdef STBI_SSE2\n// sse2 integer IDCT. not the fastest possible implementation but it\n// produces bit-identical results to the generic C version so it's\n// fully \"transparent\".\nstatic void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64])\n{\n   // This is constructed to match our regular (generic) integer IDCT exactly.\n   __m128i row0, row1, row2, row3, row4, row5, row6, row7;\n   __m128i tmp;\n\n   // dot product constant: even elems=x, odd elems=y\n   #define dct_const(x,y)  _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y))\n\n   // out(0) = c0[even]*x + c0[odd]*y   (c0, x, y 16-bit, out 32-bit)\n   // out(1) = c1[even]*x + c1[odd]*y\n   #define dct_rot(out0,out1, x,y,c0,c1) \\\n      __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \\\n      __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \\\n      __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \\\n      __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \\\n      __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \\\n      __m128i out1##_h = _mm_madd_epi16(c0##hi, c1)\n\n   // out = in << 12  (in 16-bit, out 32-bit)\n   #define dct_widen(out, in) \\\n      __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \\\n      __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4)\n\n   // wide add\n   #define dct_wadd(out, a, b) \\\n      __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \\\n      __m128i out##_h = _mm_add_epi32(a##_h, b##_h)\n\n   // wide sub\n   #define dct_wsub(out, a, b) \\\n      __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \\\n      __m128i out##_h = _mm_sub_epi32(a##_h, b##_h)\n\n   // butterfly a/b, add bias, then shift by \"s\" and pack\n   #define dct_bfly32o(out0, out1, a,b,bias,s) \\\n      { \\\n         __m128i abiased_l = _mm_add_epi32(a##_l, bias); \\\n         __m128i abiased_h = _mm_add_epi32(a##_h, bias); \\\n         dct_wadd(sum, abiased, b); \\\n         dct_wsub(dif, abiased, b); \\\n         out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \\\n         out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \\\n      }\n\n   // 8-bit interleave step (for transposes)\n   #define dct_interleave8(a, b) \\\n      tmp = a; \\\n      a = _mm_unpacklo_epi8(a, b); \\\n      b = _mm_unpackhi_epi8(tmp, b)\n\n   // 16-bit interleave step (for transposes)\n   #define dct_interleave16(a, b) \\\n      tmp = a; \\\n      a = _mm_unpacklo_epi16(a, b); \\\n      b = _mm_unpackhi_epi16(tmp, b)\n\n   #define dct_pass(bias,shift) \\\n      { \\\n         /* even part */ \\\n         dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \\\n         __m128i sum04 = _mm_add_epi16(row0, row4); \\\n         __m128i dif04 = _mm_sub_epi16(row0, row4); \\\n         dct_widen(t0e, sum04); \\\n         dct_widen(t1e, dif04); \\\n         dct_wadd(x0, t0e, t3e); \\\n         dct_wsub(x3, t0e, t3e); \\\n         dct_wadd(x1, t1e, t2e); \\\n         dct_wsub(x2, t1e, t2e); \\\n         /* odd part */ \\\n         dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \\\n         dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \\\n         __m128i sum17 = _mm_add_epi16(row1, row7); \\\n         __m128i sum35 = _mm_add_epi16(row3, row5); \\\n         dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \\\n         dct_wadd(x4, y0o, y4o); \\\n         dct_wadd(x5, y1o, y5o); \\\n         dct_wadd(x6, y2o, y5o); \\\n         dct_wadd(x7, y3o, y4o); \\\n         dct_bfly32o(row0,row7, x0,x7,bias,shift); \\\n         dct_bfly32o(row1,row6, x1,x6,bias,shift); \\\n         dct_bfly32o(row2,row5, x2,x5,bias,shift); \\\n         dct_bfly32o(row3,row4, x3,x4,bias,shift); \\\n      }\n\n   __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f));\n   __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f));\n   __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f));\n   __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f));\n   __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f));\n   __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f));\n   __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f));\n   __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f));\n\n   // rounding biases in column/row passes, see stbi__idct_block for explanation.\n   __m128i bias_0 = _mm_set1_epi32(512);\n   __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17));\n\n   // load\n   row0 = _mm_load_si128((const __m128i *) (data + 0*8));\n   row1 = _mm_load_si128((const __m128i *) (data + 1*8));\n   row2 = _mm_load_si128((const __m128i *) (data + 2*8));\n   row3 = _mm_load_si128((const __m128i *) (data + 3*8));\n   row4 = _mm_load_si128((const __m128i *) (data + 4*8));\n   row5 = _mm_load_si128((const __m128i *) (data + 5*8));\n   row6 = _mm_load_si128((const __m128i *) (data + 6*8));\n   row7 = _mm_load_si128((const __m128i *) (data + 7*8));\n\n   // column pass\n   dct_pass(bias_0, 10);\n\n   {\n      // 16bit 8x8 transpose pass 1\n      dct_interleave16(row0, row4);\n      dct_interleave16(row1, row5);\n      dct_interleave16(row2, row6);\n      dct_interleave16(row3, row7);\n\n      // transpose pass 2\n      dct_interleave16(row0, row2);\n      dct_interleave16(row1, row3);\n      dct_interleave16(row4, row6);\n      dct_interleave16(row5, row7);\n\n      // transpose pass 3\n      dct_interleave16(row0, row1);\n      dct_interleave16(row2, row3);\n      dct_interleave16(row4, row5);\n      dct_interleave16(row6, row7);\n   }\n\n   // row pass\n   dct_pass(bias_1, 17);\n\n   {\n      // pack\n      __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7\n      __m128i p1 = _mm_packus_epi16(row2, row3);\n      __m128i p2 = _mm_packus_epi16(row4, row5);\n      __m128i p3 = _mm_packus_epi16(row6, row7);\n\n      // 8bit 8x8 transpose pass 1\n      dct_interleave8(p0, p2); // a0e0a1e1...\n      dct_interleave8(p1, p3); // c0g0c1g1...\n\n      // transpose pass 2\n      dct_interleave8(p0, p1); // a0c0e0g0...\n      dct_interleave8(p2, p3); // b0d0f0h0...\n\n      // transpose pass 3\n      dct_interleave8(p0, p2); // a0b0c0d0...\n      dct_interleave8(p1, p3); // a4b4c4d4...\n\n      // store\n      _mm_storel_epi64((__m128i *) out, p0); out += out_stride;\n      _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride;\n      _mm_storel_epi64((__m128i *) out, p2); out += out_stride;\n      _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride;\n      _mm_storel_epi64((__m128i *) out, p1); out += out_stride;\n      _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride;\n      _mm_storel_epi64((__m128i *) out, p3); out += out_stride;\n      _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e));\n   }\n\n#undef dct_const\n#undef dct_rot\n#undef dct_widen\n#undef dct_wadd\n#undef dct_wsub\n#undef dct_bfly32o\n#undef dct_interleave8\n#undef dct_interleave16\n#undef dct_pass\n}\n\n#endif // STBI_SSE2\n\n#ifdef STBI_NEON\n\n// NEON integer IDCT. should produce bit-identical\n// results to the generic C version.\nstatic void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64])\n{\n   int16x8_t row0, row1, row2, row3, row4, row5, row6, row7;\n\n   int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f));\n   int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f));\n   int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f));\n   int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f));\n   int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f));\n   int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f));\n   int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f));\n   int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f));\n   int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f));\n   int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f));\n   int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f));\n   int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f));\n\n#define dct_long_mul(out, inq, coeff) \\\n   int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \\\n   int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff)\n\n#define dct_long_mac(out, acc, inq, coeff) \\\n   int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \\\n   int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff)\n\n#define dct_widen(out, inq) \\\n   int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \\\n   int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12)\n\n// wide add\n#define dct_wadd(out, a, b) \\\n   int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \\\n   int32x4_t out##_h = vaddq_s32(a##_h, b##_h)\n\n// wide sub\n#define dct_wsub(out, a, b) \\\n   int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \\\n   int32x4_t out##_h = vsubq_s32(a##_h, b##_h)\n\n// butterfly a/b, then shift using \"shiftop\" by \"s\" and pack\n#define dct_bfly32o(out0,out1, a,b,shiftop,s) \\\n   { \\\n      dct_wadd(sum, a, b); \\\n      dct_wsub(dif, a, b); \\\n      out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \\\n      out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \\\n   }\n\n#define dct_pass(shiftop, shift) \\\n   { \\\n      /* even part */ \\\n      int16x8_t sum26 = vaddq_s16(row2, row6); \\\n      dct_long_mul(p1e, sum26, rot0_0); \\\n      dct_long_mac(t2e, p1e, row6, rot0_1); \\\n      dct_long_mac(t3e, p1e, row2, rot0_2); \\\n      int16x8_t sum04 = vaddq_s16(row0, row4); \\\n      int16x8_t dif04 = vsubq_s16(row0, row4); \\\n      dct_widen(t0e, sum04); \\\n      dct_widen(t1e, dif04); \\\n      dct_wadd(x0, t0e, t3e); \\\n      dct_wsub(x3, t0e, t3e); \\\n      dct_wadd(x1, t1e, t2e); \\\n      dct_wsub(x2, t1e, t2e); \\\n      /* odd part */ \\\n      int16x8_t sum15 = vaddq_s16(row1, row5); \\\n      int16x8_t sum17 = vaddq_s16(row1, row7); \\\n      int16x8_t sum35 = vaddq_s16(row3, row5); \\\n      int16x8_t sum37 = vaddq_s16(row3, row7); \\\n      int16x8_t sumodd = vaddq_s16(sum17, sum35); \\\n      dct_long_mul(p5o, sumodd, rot1_0); \\\n      dct_long_mac(p1o, p5o, sum17, rot1_1); \\\n      dct_long_mac(p2o, p5o, sum35, rot1_2); \\\n      dct_long_mul(p3o, sum37, rot2_0); \\\n      dct_long_mul(p4o, sum15, rot2_1); \\\n      dct_wadd(sump13o, p1o, p3o); \\\n      dct_wadd(sump24o, p2o, p4o); \\\n      dct_wadd(sump23o, p2o, p3o); \\\n      dct_wadd(sump14o, p1o, p4o); \\\n      dct_long_mac(x4, sump13o, row7, rot3_0); \\\n      dct_long_mac(x5, sump24o, row5, rot3_1); \\\n      dct_long_mac(x6, sump23o, row3, rot3_2); \\\n      dct_long_mac(x7, sump14o, row1, rot3_3); \\\n      dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \\\n      dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \\\n      dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \\\n      dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \\\n   }\n\n   // load\n   row0 = vld1q_s16(data + 0*8);\n   row1 = vld1q_s16(data + 1*8);\n   row2 = vld1q_s16(data + 2*8);\n   row3 = vld1q_s16(data + 3*8);\n   row4 = vld1q_s16(data + 4*8);\n   row5 = vld1q_s16(data + 5*8);\n   row6 = vld1q_s16(data + 6*8);\n   row7 = vld1q_s16(data + 7*8);\n\n   // add DC bias\n   row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0));\n\n   // column pass\n   dct_pass(vrshrn_n_s32, 10);\n\n   // 16bit 8x8 transpose\n   {\n// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively.\n// whether compilers actually get this is another story, sadly.\n#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; }\n#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); }\n#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); }\n\n      // pass 1\n      dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6\n      dct_trn16(row2, row3);\n      dct_trn16(row4, row5);\n      dct_trn16(row6, row7);\n\n      // pass 2\n      dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4\n      dct_trn32(row1, row3);\n      dct_trn32(row4, row6);\n      dct_trn32(row5, row7);\n\n      // pass 3\n      dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0\n      dct_trn64(row1, row5);\n      dct_trn64(row2, row6);\n      dct_trn64(row3, row7);\n\n#undef dct_trn16\n#undef dct_trn32\n#undef dct_trn64\n   }\n\n   // row pass\n   // vrshrn_n_s32 only supports shifts up to 16, we need\n   // 17. so do a non-rounding shift of 16 first then follow\n   // up with a rounding shift by 1.\n   dct_pass(vshrn_n_s32, 16);\n\n   {\n      // pack and round\n      uint8x8_t p0 = vqrshrun_n_s16(row0, 1);\n      uint8x8_t p1 = vqrshrun_n_s16(row1, 1);\n      uint8x8_t p2 = vqrshrun_n_s16(row2, 1);\n      uint8x8_t p3 = vqrshrun_n_s16(row3, 1);\n      uint8x8_t p4 = vqrshrun_n_s16(row4, 1);\n      uint8x8_t p5 = vqrshrun_n_s16(row5, 1);\n      uint8x8_t p6 = vqrshrun_n_s16(row6, 1);\n      uint8x8_t p7 = vqrshrun_n_s16(row7, 1);\n\n      // again, these can translate into one instruction, but often don't.\n#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; }\n#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); }\n#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); }\n\n      // sadly can't use interleaved stores here since we only write\n      // 8 bytes to each scan line!\n\n      // 8x8 8-bit transpose pass 1\n      dct_trn8_8(p0, p1);\n      dct_trn8_8(p2, p3);\n      dct_trn8_8(p4, p5);\n      dct_trn8_8(p6, p7);\n\n      // pass 2\n      dct_trn8_16(p0, p2);\n      dct_trn8_16(p1, p3);\n      dct_trn8_16(p4, p6);\n      dct_trn8_16(p5, p7);\n\n      // pass 3\n      dct_trn8_32(p0, p4);\n      dct_trn8_32(p1, p5);\n      dct_trn8_32(p2, p6);\n      dct_trn8_32(p3, p7);\n\n      // store\n      vst1_u8(out, p0); out += out_stride;\n      vst1_u8(out, p1); out += out_stride;\n      vst1_u8(out, p2); out += out_stride;\n      vst1_u8(out, p3); out += out_stride;\n      vst1_u8(out, p4); out += out_stride;\n      vst1_u8(out, p5); out += out_stride;\n      vst1_u8(out, p6); out += out_stride;\n      vst1_u8(out, p7);\n\n#undef dct_trn8_8\n#undef dct_trn8_16\n#undef dct_trn8_32\n   }\n\n#undef dct_long_mul\n#undef dct_long_mac\n#undef dct_widen\n#undef dct_wadd\n#undef dct_wsub\n#undef dct_bfly32o\n#undef dct_pass\n}\n\n#endif // STBI_NEON\n\n#define STBI__MARKER_none  0xff\n// if there's a pending marker from the entropy stream, return that\n// otherwise, fetch from the stream and get a marker. if there's no\n// marker, return 0xff, which is never a valid marker value\nstatic stbi_uc stbi__get_marker(stbi__jpeg *j)\n{\n   stbi_uc x;\n   if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; }\n   x = stbi__get8(j->s);\n   if (x != 0xff) return STBI__MARKER_none;\n   while (x == 0xff)\n      x = stbi__get8(j->s); // consume repeated 0xff fill bytes\n   return x;\n}\n\n// in each scan, we'll have scan_n components, and the order\n// of the components is specified by order[]\n#define STBI__RESTART(x)     ((x) >= 0xd0 && (x) <= 0xd7)\n\n// after a restart interval, stbi__jpeg_reset the entropy decoder and\n// the dc prediction\nstatic void stbi__jpeg_reset(stbi__jpeg *j)\n{\n   j->code_bits = 0;\n   j->code_buffer = 0;\n   j->nomore = 0;\n   j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0;\n   j->marker = STBI__MARKER_none;\n   j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff;\n   j->eob_run = 0;\n   // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,\n   // since we don't even allow 1<<30 pixels\n}\n\nstatic int stbi__parse_entropy_coded_data(stbi__jpeg *z)\n{\n   stbi__jpeg_reset(z);\n   if (!z->progressive) {\n      if (z->scan_n == 1) {\n         int i,j;\n         STBI_SIMD_ALIGN(short, data[64]);\n         int n = z->order[0];\n         // non-interleaved data, we just need to process one block at a time,\n         // in trivial scanline order\n         // number of blocks to do just depends on how many actual \"pixels\" this\n         // component has, independent of interleaved MCU blocking and such\n         int w = (z->img_comp[n].x+7) >> 3;\n         int h = (z->img_comp[n].y+7) >> 3;\n         for (j=0; j < h; ++j) {\n            for (i=0; i < w; ++i) {\n               int ha = z->img_comp[n].ha;\n               if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;\n               z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);\n               // every data block is an MCU, so countdown the restart interval\n               if (--z->todo <= 0) {\n                  if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                  // if it's NOT a restart, then just bail, so we get corrupt data\n                  // rather than no data\n                  if (!STBI__RESTART(z->marker)) return 1;\n                  stbi__jpeg_reset(z);\n               }\n            }\n         }\n         return 1;\n      } else { // interleaved\n         int i,j,k,x,y;\n         STBI_SIMD_ALIGN(short, data[64]);\n         for (j=0; j < z->img_mcu_y; ++j) {\n            for (i=0; i < z->img_mcu_x; ++i) {\n               // scan an interleaved mcu... process scan_n components in order\n               for (k=0; k < z->scan_n; ++k) {\n                  int n = z->order[k];\n                  // scan out an mcu's worth of this component; that's just determined\n                  // by the basic H and V specified for the component\n                  for (y=0; y < z->img_comp[n].v; ++y) {\n                     for (x=0; x < z->img_comp[n].h; ++x) {\n                        int x2 = (i*z->img_comp[n].h + x)*8;\n                        int y2 = (j*z->img_comp[n].v + y)*8;\n                        int ha = z->img_comp[n].ha;\n                        if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;\n                        z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data);\n                     }\n                  }\n               }\n               // after all interleaved components, that's an interleaved MCU,\n               // so now count down the restart interval\n               if (--z->todo <= 0) {\n                  if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                  if (!STBI__RESTART(z->marker)) return 1;\n                  stbi__jpeg_reset(z);\n               }\n            }\n         }\n         return 1;\n      }\n   } else {\n      if (z->scan_n == 1) {\n         int i,j;\n         int n = z->order[0];\n         // non-interleaved data, we just need to process one block at a time,\n         // in trivial scanline order\n         // number of blocks to do just depends on how many actual \"pixels\" this\n         // component has, independent of interleaved MCU blocking and such\n         int w = (z->img_comp[n].x+7) >> 3;\n         int h = (z->img_comp[n].y+7) >> 3;\n         for (j=0; j < h; ++j) {\n            for (i=0; i < w; ++i) {\n               short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);\n               if (z->spec_start == 0) {\n                  if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))\n                     return 0;\n               } else {\n                  int ha = z->img_comp[n].ha;\n                  if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha]))\n                     return 0;\n               }\n               // every data block is an MCU, so countdown the restart interval\n               if (--z->todo <= 0) {\n                  if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                  if (!STBI__RESTART(z->marker)) return 1;\n                  stbi__jpeg_reset(z);\n               }\n            }\n         }\n         return 1;\n      } else { // interleaved\n         int i,j,k,x,y;\n         for (j=0; j < z->img_mcu_y; ++j) {\n            for (i=0; i < z->img_mcu_x; ++i) {\n               // scan an interleaved mcu... process scan_n components in order\n               for (k=0; k < z->scan_n; ++k) {\n                  int n = z->order[k];\n                  // scan out an mcu's worth of this component; that's just determined\n                  // by the basic H and V specified for the component\n                  for (y=0; y < z->img_comp[n].v; ++y) {\n                     for (x=0; x < z->img_comp[n].h; ++x) {\n                        int x2 = (i*z->img_comp[n].h + x);\n                        int y2 = (j*z->img_comp[n].v + y);\n                        short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w);\n                        if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))\n                           return 0;\n                     }\n                  }\n               }\n               // after all interleaved components, that's an interleaved MCU,\n               // so now count down the restart interval\n               if (--z->todo <= 0) {\n                  if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                  if (!STBI__RESTART(z->marker)) return 1;\n                  stbi__jpeg_reset(z);\n               }\n            }\n         }\n         return 1;\n      }\n   }\n}\n\nstatic void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant)\n{\n   int i;\n   for (i=0; i < 64; ++i)\n      data[i] *= dequant[i];\n}\n\nstatic void stbi__jpeg_finish(stbi__jpeg *z)\n{\n   if (z->progressive) {\n      // dequantize and idct the data\n      int i,j,n;\n      for (n=0; n < z->s->img_n; ++n) {\n         int w = (z->img_comp[n].x+7) >> 3;\n         int h = (z->img_comp[n].y+7) >> 3;\n         for (j=0; j < h; ++j) {\n            for (i=0; i < w; ++i) {\n               short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);\n               stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]);\n               z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);\n            }\n         }\n      }\n   }\n}\n\nstatic int stbi__process_marker(stbi__jpeg *z, int m)\n{\n   int L;\n   switch (m) {\n      case STBI__MARKER_none: // no marker found\n         return stbi__err(\"expected marker\",\"Corrupt JPEG\");\n\n      case 0xDD: // DRI - specify restart interval\n         if (stbi__get16be(z->s) != 4) return stbi__err(\"bad DRI len\",\"Corrupt JPEG\");\n         z->restart_interval = stbi__get16be(z->s);\n         return 1;\n\n      case 0xDB: // DQT - define quantization table\n         L = stbi__get16be(z->s)-2;\n         while (L > 0) {\n            int q = stbi__get8(z->s);\n            int p = q >> 4, sixteen = (p != 0);\n            int t = q & 15,i;\n            if (p != 0 && p != 1) return stbi__err(\"bad DQT type\",\"Corrupt JPEG\");\n            if (t > 3) return stbi__err(\"bad DQT table\",\"Corrupt JPEG\");\n\n            for (i=0; i < 64; ++i)\n               z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s));\n            L -= (sixteen ? 129 : 65);\n         }\n         return L==0;\n\n      case 0xC4: // DHT - define huffman table\n         L = stbi__get16be(z->s)-2;\n         while (L > 0) {\n            stbi_uc *v;\n            int sizes[16],i,n=0;\n            int q = stbi__get8(z->s);\n            int tc = q >> 4;\n            int th = q & 15;\n            if (tc > 1 || th > 3) return stbi__err(\"bad DHT header\",\"Corrupt JPEG\");\n            for (i=0; i < 16; ++i) {\n               sizes[i] = stbi__get8(z->s);\n               n += sizes[i];\n            }\n            if(n > 256) return stbi__err(\"bad DHT header\",\"Corrupt JPEG\"); // Loop over i < n would write past end of values!\n            L -= 17;\n            if (tc == 0) {\n               if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;\n               v = z->huff_dc[th].values;\n            } else {\n               if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0;\n               v = z->huff_ac[th].values;\n            }\n            for (i=0; i < n; ++i)\n               v[i] = stbi__get8(z->s);\n            if (tc != 0)\n               stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th);\n            L -= n;\n         }\n         return L==0;\n   }\n\n   // check for comment block or APP blocks\n   if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {\n      L = stbi__get16be(z->s);\n      if (L < 2) {\n         if (m == 0xFE)\n            return stbi__err(\"bad COM len\",\"Corrupt JPEG\");\n         else\n            return stbi__err(\"bad APP len\",\"Corrupt JPEG\");\n      }\n      L -= 2;\n\n      if (m == 0xE0 && L >= 5) { // JFIF APP0 segment\n         static const unsigned char tag[5] = {'J','F','I','F','\\0'};\n         int ok = 1;\n         int i;\n         for (i=0; i < 5; ++i)\n            if (stbi__get8(z->s) != tag[i])\n               ok = 0;\n         L -= 5;\n         if (ok)\n            z->jfif = 1;\n      } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment\n         static const unsigned char tag[6] = {'A','d','o','b','e','\\0'};\n         int ok = 1;\n         int i;\n         for (i=0; i < 6; ++i)\n            if (stbi__get8(z->s) != tag[i])\n               ok = 0;\n         L -= 6;\n         if (ok) {\n            stbi__get8(z->s); // version\n            stbi__get16be(z->s); // flags0\n            stbi__get16be(z->s); // flags1\n            z->app14_color_transform = stbi__get8(z->s); // color transform\n            L -= 6;\n         }\n      }\n\n      stbi__skip(z->s, L);\n      return 1;\n   }\n\n   return stbi__err(\"unknown marker\",\"Corrupt JPEG\");\n}\n\n// after we see SOS\nstatic int stbi__process_scan_header(stbi__jpeg *z)\n{\n   int i;\n   int Ls = stbi__get16be(z->s);\n   z->scan_n = stbi__get8(z->s);\n   if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err(\"bad SOS component count\",\"Corrupt JPEG\");\n   if (Ls != 6+2*z->scan_n) return stbi__err(\"bad SOS len\",\"Corrupt JPEG\");\n   for (i=0; i < z->scan_n; ++i) {\n      int id = stbi__get8(z->s), which;\n      int q = stbi__get8(z->s);\n      for (which = 0; which < z->s->img_n; ++which)\n         if (z->img_comp[which].id == id)\n            break;\n      if (which == z->s->img_n) return 0; // no match\n      z->img_comp[which].hd = q >> 4;   if (z->img_comp[which].hd > 3) return stbi__err(\"bad DC huff\",\"Corrupt JPEG\");\n      z->img_comp[which].ha = q & 15;   if (z->img_comp[which].ha > 3) return stbi__err(\"bad AC huff\",\"Corrupt JPEG\");\n      z->order[i] = which;\n   }\n\n   {\n      int aa;\n      z->spec_start = stbi__get8(z->s);\n      z->spec_end   = stbi__get8(z->s); // should be 63, but might be 0\n      aa = stbi__get8(z->s);\n      z->succ_high = (aa >> 4);\n      z->succ_low  = (aa & 15);\n      if (z->progressive) {\n         if (z->spec_start > 63 || z->spec_end > 63  || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13)\n            return stbi__err(\"bad SOS\", \"Corrupt JPEG\");\n      } else {\n         if (z->spec_start != 0) return stbi__err(\"bad SOS\",\"Corrupt JPEG\");\n         if (z->succ_high != 0 || z->succ_low != 0) return stbi__err(\"bad SOS\",\"Corrupt JPEG\");\n         z->spec_end = 63;\n      }\n   }\n\n   return 1;\n}\n\nstatic int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why)\n{\n   int i;\n   for (i=0; i < ncomp; ++i) {\n      if (z->img_comp[i].raw_data) {\n         STBI_FREE(z->img_comp[i].raw_data);\n         z->img_comp[i].raw_data = NULL;\n         z->img_comp[i].data = NULL;\n      }\n      if (z->img_comp[i].raw_coeff) {\n         STBI_FREE(z->img_comp[i].raw_coeff);\n         z->img_comp[i].raw_coeff = 0;\n         z->img_comp[i].coeff = 0;\n      }\n      if (z->img_comp[i].linebuf) {\n         STBI_FREE(z->img_comp[i].linebuf);\n         z->img_comp[i].linebuf = NULL;\n      }\n   }\n   return why;\n}\n\nstatic int stbi__process_frame_header(stbi__jpeg *z, int scan)\n{\n   stbi__context *s = z->s;\n   int Lf,p,i,q, h_max=1,v_max=1,c;\n   Lf = stbi__get16be(s);         if (Lf < 11) return stbi__err(\"bad SOF len\",\"Corrupt JPEG\"); // JPEG\n   p  = stbi__get8(s);            if (p != 8) return stbi__err(\"only 8-bit\",\"JPEG format not supported: 8-bit only\"); // JPEG baseline\n   s->img_y = stbi__get16be(s);   if (s->img_y == 0) return stbi__err(\"no header height\", \"JPEG format not supported: delayed height\"); // Legal, but we don't handle it--but neither does IJG\n   s->img_x = stbi__get16be(s);   if (s->img_x == 0) return stbi__err(\"0 width\",\"Corrupt JPEG\"); // JPEG requires\n   if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err(\"too large\",\"Very large image (corrupt?)\");\n   if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err(\"too large\",\"Very large image (corrupt?)\");\n   c = stbi__get8(s);\n   if (c != 3 && c != 1 && c != 4) return stbi__err(\"bad component count\",\"Corrupt JPEG\");\n   s->img_n = c;\n   for (i=0; i < c; ++i) {\n      z->img_comp[i].data = NULL;\n      z->img_comp[i].linebuf = NULL;\n   }\n\n   if (Lf != 8+3*s->img_n) return stbi__err(\"bad SOF len\",\"Corrupt JPEG\");\n\n   z->rgb = 0;\n   for (i=0; i < s->img_n; ++i) {\n      static const unsigned char rgb[3] = { 'R', 'G', 'B' };\n      z->img_comp[i].id = stbi__get8(s);\n      if (s->img_n == 3 && z->img_comp[i].id == rgb[i])\n         ++z->rgb;\n      q = stbi__get8(s);\n      z->img_comp[i].h = (q >> 4);  if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err(\"bad H\",\"Corrupt JPEG\");\n      z->img_comp[i].v = q & 15;    if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err(\"bad V\",\"Corrupt JPEG\");\n      z->img_comp[i].tq = stbi__get8(s);  if (z->img_comp[i].tq > 3) return stbi__err(\"bad TQ\",\"Corrupt JPEG\");\n   }\n\n   if (scan != STBI__SCAN_load) return 1;\n\n   if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err(\"too large\", \"Image too large to decode\");\n\n   for (i=0; i < s->img_n; ++i) {\n      if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h;\n      if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;\n   }\n\n   // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios\n   // and I've never seen a non-corrupted JPEG file actually use them\n   for (i=0; i < s->img_n; ++i) {\n      if (h_max % z->img_comp[i].h != 0) return stbi__err(\"bad H\",\"Corrupt JPEG\");\n      if (v_max % z->img_comp[i].v != 0) return stbi__err(\"bad V\",\"Corrupt JPEG\");\n   }\n\n   // compute interleaved mcu info\n   z->img_h_max = h_max;\n   z->img_v_max = v_max;\n   z->img_mcu_w = h_max * 8;\n   z->img_mcu_h = v_max * 8;\n   // these sizes can't be more than 17 bits\n   z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w;\n   z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h;\n\n   for (i=0; i < s->img_n; ++i) {\n      // number of effective pixels (e.g. for non-interleaved MCU)\n      z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max;\n      z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max;\n      // to simplify generation, we'll allocate enough memory to decode\n      // the bogus oversized data from using interleaved MCUs and their\n      // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't\n      // discard the extra data until colorspace conversion\n      //\n      // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier)\n      // so these muls can't overflow with 32-bit ints (which we require)\n      z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8;\n      z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8;\n      z->img_comp[i].coeff = 0;\n      z->img_comp[i].raw_coeff = 0;\n      z->img_comp[i].linebuf = NULL;\n      z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15);\n      if (z->img_comp[i].raw_data == NULL)\n         return stbi__free_jpeg_components(z, i+1, stbi__err(\"outofmem\", \"Out of memory\"));\n      // align blocks for idct using mmx/sse\n      z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15);\n      if (z->progressive) {\n         // w2, h2 are multiples of 8 (see above)\n         z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8;\n         z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8;\n         z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15);\n         if (z->img_comp[i].raw_coeff == NULL)\n            return stbi__free_jpeg_components(z, i+1, stbi__err(\"outofmem\", \"Out of memory\"));\n         z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15);\n      }\n   }\n\n   return 1;\n}\n\n// use comparisons since in some cases we handle more than one case (e.g. SOF)\n#define stbi__DNL(x)         ((x) == 0xdc)\n#define stbi__SOI(x)         ((x) == 0xd8)\n#define stbi__EOI(x)         ((x) == 0xd9)\n#define stbi__SOF(x)         ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2)\n#define stbi__SOS(x)         ((x) == 0xda)\n\n#define stbi__SOF_progressive(x)   ((x) == 0xc2)\n\nstatic int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)\n{\n   int m;\n   z->jfif = 0;\n   z->app14_color_transform = -1; // valid values are 0,1,2\n   z->marker = STBI__MARKER_none; // initialize cached marker to empty\n   m = stbi__get_marker(z);\n   if (!stbi__SOI(m)) return stbi__err(\"no SOI\",\"Corrupt JPEG\");\n   if (scan == STBI__SCAN_type) return 1;\n   m = stbi__get_marker(z);\n   while (!stbi__SOF(m)) {\n      if (!stbi__process_marker(z,m)) return 0;\n      m = stbi__get_marker(z);\n      while (m == STBI__MARKER_none) {\n         // some files have extra padding after their blocks, so ok, we'll scan\n         if (stbi__at_eof(z->s)) return stbi__err(\"no SOF\", \"Corrupt JPEG\");\n         m = stbi__get_marker(z);\n      }\n   }\n   z->progressive = stbi__SOF_progressive(m);\n   if (!stbi__process_frame_header(z, scan)) return 0;\n   return 1;\n}\n\nstatic int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)\n{\n   // some JPEGs have junk at end, skip over it but if we find what looks\n   // like a valid marker, resume there\n   while (!stbi__at_eof(j->s)) {\n      int x = stbi__get8(j->s);\n      while (x == 255) { // might be a marker\n         if (stbi__at_eof(j->s)) return STBI__MARKER_none;\n         x = stbi__get8(j->s);\n         if (x != 0x00 && x != 0xff) {\n            // not a stuffed zero or lead-in to another marker, looks\n            // like an actual marker, return it\n            return x;\n         }\n         // stuffed zero has x=0 now which ends the loop, meaning we go\n         // back to regular scan loop.\n         // repeated 0xff keeps trying to read the next byte of the marker.\n      }\n   }\n   return STBI__MARKER_none;\n}\n\n// decode image to YCbCr format\nstatic int stbi__decode_jpeg_image(stbi__jpeg *j)\n{\n   int m;\n   for (m = 0; m < 4; m++) {\n      j->img_comp[m].raw_data = NULL;\n      j->img_comp[m].raw_coeff = NULL;\n   }\n   j->restart_interval = 0;\n   if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0;\n   m = stbi__get_marker(j);\n   while (!stbi__EOI(m)) {\n      if (stbi__SOS(m)) {\n         if (!stbi__process_scan_header(j)) return 0;\n         if (!stbi__parse_entropy_coded_data(j)) return 0;\n         if (j->marker == STBI__MARKER_none ) {\n         j->marker = stbi__skip_jpeg_junk_at_end(j);\n            // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0\n         }\n         m = stbi__get_marker(j);\n         if (STBI__RESTART(m))\n            m = stbi__get_marker(j);\n      } else if (stbi__DNL(m)) {\n         int Ld = stbi__get16be(j->s);\n         stbi__uint32 NL = stbi__get16be(j->s);\n         if (Ld != 4) return stbi__err(\"bad DNL len\", \"Corrupt JPEG\");\n         if (NL != j->s->img_y) return stbi__err(\"bad DNL height\", \"Corrupt JPEG\");\n         m = stbi__get_marker(j);\n      } else {\n         if (!stbi__process_marker(j, m)) return 1;\n         m = stbi__get_marker(j);\n      }\n   }\n   if (j->progressive)\n      stbi__jpeg_finish(j);\n   return 1;\n}\n\n// static jfif-centered resampling (across block boundaries)\n\ntypedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1,\n                                    int w, int hs);\n\n#define stbi__div4(x) ((stbi_uc) ((x) >> 2))\n\nstatic stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)\n{\n   STBI_NOTUSED(out);\n   STBI_NOTUSED(in_far);\n   STBI_NOTUSED(w);\n   STBI_NOTUSED(hs);\n   return in_near;\n}\n\nstatic stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)\n{\n   // need to generate two samples vertically for every one in input\n   int i;\n   STBI_NOTUSED(hs);\n   for (i=0; i < w; ++i)\n      out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2);\n   return out;\n}\n\nstatic stbi_uc*  stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)\n{\n   // need to generate two samples horizontally for every one in input\n   int i;\n   stbi_uc *input = in_near;\n\n   if (w == 1) {\n      // if only one sample, can't do any interpolation\n      out[0] = out[1] = input[0];\n      return out;\n   }\n\n   out[0] = input[0];\n   out[1] = stbi__div4(input[0]*3 + input[1] + 2);\n   for (i=1; i < w-1; ++i) {\n      int n = 3*input[i]+2;\n      out[i*2+0] = stbi__div4(n+input[i-1]);\n      out[i*2+1] = stbi__div4(n+input[i+1]);\n   }\n   out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2);\n   out[i*2+1] = input[w-1];\n\n   STBI_NOTUSED(in_far);\n   STBI_NOTUSED(hs);\n\n   return out;\n}\n\n#define stbi__div16(x) ((stbi_uc) ((x) >> 4))\n\nstatic stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)\n{\n   // need to generate 2x2 samples for every one in input\n   int i,t0,t1;\n   if (w == 1) {\n      out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);\n      return out;\n   }\n\n   t1 = 3*in_near[0] + in_far[0];\n   out[0] = stbi__div4(t1+2);\n   for (i=1; i < w; ++i) {\n      t0 = t1;\n      t1 = 3*in_near[i]+in_far[i];\n      out[i*2-1] = stbi__div16(3*t0 + t1 + 8);\n      out[i*2  ] = stbi__div16(3*t1 + t0 + 8);\n   }\n   out[w*2-1] = stbi__div4(t1+2);\n\n   STBI_NOTUSED(hs);\n\n   return out;\n}\n\n#if defined(STBI_SSE2) || defined(STBI_NEON)\nstatic stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)\n{\n   // need to generate 2x2 samples for every one in input\n   int i=0,t0,t1;\n\n   if (w == 1) {\n      out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);\n      return out;\n   }\n\n   t1 = 3*in_near[0] + in_far[0];\n   // process groups of 8 pixels for as long as we can.\n   // note we can't handle the last pixel in a row in this loop\n   // because we need to handle the filter boundary conditions.\n   for (; i < ((w-1) & ~7); i += 8) {\n#if defined(STBI_SSE2)\n      // load and perform the vertical filtering pass\n      // this uses 3*x + y = 4*x + (y - x)\n      __m128i zero  = _mm_setzero_si128();\n      __m128i farb  = _mm_loadl_epi64((__m128i *) (in_far + i));\n      __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i));\n      __m128i farw  = _mm_unpacklo_epi8(farb, zero);\n      __m128i nearw = _mm_unpacklo_epi8(nearb, zero);\n      __m128i diff  = _mm_sub_epi16(farw, nearw);\n      __m128i nears = _mm_slli_epi16(nearw, 2);\n      __m128i curr  = _mm_add_epi16(nears, diff); // current row\n\n      // horizontal filter works the same based on shifted vers of current\n      // row. \"prev\" is current row shifted right by 1 pixel; we need to\n      // insert the previous pixel value (from t1).\n      // \"next\" is current row shifted left by 1 pixel, with first pixel\n      // of next block of 8 pixels added in.\n      __m128i prv0 = _mm_slli_si128(curr, 2);\n      __m128i nxt0 = _mm_srli_si128(curr, 2);\n      __m128i prev = _mm_insert_epi16(prv0, t1, 0);\n      __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7);\n\n      // horizontal filter, polyphase implementation since it's convenient:\n      // even pixels = 3*cur + prev = cur*4 + (prev - cur)\n      // odd  pixels = 3*cur + next = cur*4 + (next - cur)\n      // note the shared term.\n      __m128i bias  = _mm_set1_epi16(8);\n      __m128i curs = _mm_slli_epi16(curr, 2);\n      __m128i prvd = _mm_sub_epi16(prev, curr);\n      __m128i nxtd = _mm_sub_epi16(next, curr);\n      __m128i curb = _mm_add_epi16(curs, bias);\n      __m128i even = _mm_add_epi16(prvd, curb);\n      __m128i odd  = _mm_add_epi16(nxtd, curb);\n\n      // interleave even and odd pixels, then undo scaling.\n      __m128i int0 = _mm_unpacklo_epi16(even, odd);\n      __m128i int1 = _mm_unpackhi_epi16(even, odd);\n      __m128i de0  = _mm_srli_epi16(int0, 4);\n      __m128i de1  = _mm_srli_epi16(int1, 4);\n\n      // pack and write output\n      __m128i outv = _mm_packus_epi16(de0, de1);\n      _mm_storeu_si128((__m128i *) (out + i*2), outv);\n#elif defined(STBI_NEON)\n      // load and perform the vertical filtering pass\n      // this uses 3*x + y = 4*x + (y - x)\n      uint8x8_t farb  = vld1_u8(in_far + i);\n      uint8x8_t nearb = vld1_u8(in_near + i);\n      int16x8_t diff  = vreinterpretq_s16_u16(vsubl_u8(farb, nearb));\n      int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2));\n      int16x8_t curr  = vaddq_s16(nears, diff); // current row\n\n      // horizontal filter works the same based on shifted vers of current\n      // row. \"prev\" is current row shifted right by 1 pixel; we need to\n      // insert the previous pixel value (from t1).\n      // \"next\" is current row shifted left by 1 pixel, with first pixel\n      // of next block of 8 pixels added in.\n      int16x8_t prv0 = vextq_s16(curr, curr, 7);\n      int16x8_t nxt0 = vextq_s16(curr, curr, 1);\n      int16x8_t prev = vsetq_lane_s16(t1, prv0, 0);\n      int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7);\n\n      // horizontal filter, polyphase implementation since it's convenient:\n      // even pixels = 3*cur + prev = cur*4 + (prev - cur)\n      // odd  pixels = 3*cur + next = cur*4 + (next - cur)\n      // note the shared term.\n      int16x8_t curs = vshlq_n_s16(curr, 2);\n      int16x8_t prvd = vsubq_s16(prev, curr);\n      int16x8_t nxtd = vsubq_s16(next, curr);\n      int16x8_t even = vaddq_s16(curs, prvd);\n      int16x8_t odd  = vaddq_s16(curs, nxtd);\n\n      // undo scaling and round, then store with even/odd phases interleaved\n      uint8x8x2_t o;\n      o.val[0] = vqrshrun_n_s16(even, 4);\n      o.val[1] = vqrshrun_n_s16(odd,  4);\n      vst2_u8(out + i*2, o);\n#endif\n\n      // \"previous\" value for next iter\n      t1 = 3*in_near[i+7] + in_far[i+7];\n   }\n\n   t0 = t1;\n   t1 = 3*in_near[i] + in_far[i];\n   out[i*2] = stbi__div16(3*t1 + t0 + 8);\n\n   for (++i; i < w; ++i) {\n      t0 = t1;\n      t1 = 3*in_near[i]+in_far[i];\n      out[i*2-1] = stbi__div16(3*t0 + t1 + 8);\n      out[i*2  ] = stbi__div16(3*t1 + t0 + 8);\n   }\n   out[w*2-1] = stbi__div4(t1+2);\n\n   STBI_NOTUSED(hs);\n\n   return out;\n}\n#endif\n\nstatic stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)\n{\n   // resample with nearest-neighbor\n   int i,j;\n   STBI_NOTUSED(in_far);\n   for (i=0; i < w; ++i)\n      for (j=0; j < hs; ++j)\n         out[i*hs+j] = in_near[i];\n   return out;\n}\n\n// this is a reduced-precision calculation of YCbCr-to-RGB introduced\n// to make sure the code produces the same results in both SIMD and scalar\n#define stbi__float2fixed(x)  (((int) ((x) * 4096.0f + 0.5f)) << 8)\nstatic void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step)\n{\n   int i;\n   for (i=0; i < count; ++i) {\n      int y_fixed = (y[i] << 20) + (1<<19); // rounding\n      int r,g,b;\n      int cr = pcr[i] - 128;\n      int cb = pcb[i] - 128;\n      r = y_fixed +  cr* stbi__float2fixed(1.40200f);\n      g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);\n      b = y_fixed                                     +   cb* stbi__float2fixed(1.77200f);\n      r >>= 20;\n      g >>= 20;\n      b >>= 20;\n      if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }\n      if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }\n      if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }\n      out[0] = (stbi_uc)r;\n      out[1] = (stbi_uc)g;\n      out[2] = (stbi_uc)b;\n      out[3] = 255;\n      out += step;\n   }\n}\n\n#if defined(STBI_SSE2) || defined(STBI_NEON)\nstatic void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step)\n{\n   int i = 0;\n\n#ifdef STBI_SSE2\n   // step == 3 is pretty ugly on the final interleave, and i'm not convinced\n   // it's useful in practice (you wouldn't use it for textures, for example).\n   // so just accelerate step == 4 case.\n   if (step == 4) {\n      // this is a fairly straightforward implementation and not super-optimized.\n      __m128i signflip  = _mm_set1_epi8(-0x80);\n      __m128i cr_const0 = _mm_set1_epi16(   (short) ( 1.40200f*4096.0f+0.5f));\n      __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f));\n      __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f));\n      __m128i cb_const1 = _mm_set1_epi16(   (short) ( 1.77200f*4096.0f+0.5f));\n      __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128);\n      __m128i xw = _mm_set1_epi16(255); // alpha channel\n\n      for (; i+7 < count; i += 8) {\n         // load\n         __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i));\n         __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i));\n         __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i));\n         __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128\n         __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128\n\n         // unpack to short (and left-shift cr, cb by 8)\n         __m128i yw  = _mm_unpacklo_epi8(y_bias, y_bytes);\n         __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased);\n         __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased);\n\n         // color transform\n         __m128i yws = _mm_srli_epi16(yw, 4);\n         __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw);\n         __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw);\n         __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1);\n         __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1);\n         __m128i rws = _mm_add_epi16(cr0, yws);\n         __m128i gwt = _mm_add_epi16(cb0, yws);\n         __m128i bws = _mm_add_epi16(yws, cb1);\n         __m128i gws = _mm_add_epi16(gwt, cr1);\n\n         // descale\n         __m128i rw = _mm_srai_epi16(rws, 4);\n         __m128i bw = _mm_srai_epi16(bws, 4);\n         __m128i gw = _mm_srai_epi16(gws, 4);\n\n         // back to byte, set up for transpose\n         __m128i brb = _mm_packus_epi16(rw, bw);\n         __m128i gxb = _mm_packus_epi16(gw, xw);\n\n         // transpose to interleave channels\n         __m128i t0 = _mm_unpacklo_epi8(brb, gxb);\n         __m128i t1 = _mm_unpackhi_epi8(brb, gxb);\n         __m128i o0 = _mm_unpacklo_epi16(t0, t1);\n         __m128i o1 = _mm_unpackhi_epi16(t0, t1);\n\n         // store\n         _mm_storeu_si128((__m128i *) (out + 0), o0);\n         _mm_storeu_si128((__m128i *) (out + 16), o1);\n         out += 32;\n      }\n   }\n#endif\n\n#ifdef STBI_NEON\n   // in this version, step=3 support would be easy to add. but is there demand?\n   if (step == 4) {\n      // this is a fairly straightforward implementation and not super-optimized.\n      uint8x8_t signflip = vdup_n_u8(0x80);\n      int16x8_t cr_const0 = vdupq_n_s16(   (short) ( 1.40200f*4096.0f+0.5f));\n      int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f));\n      int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f));\n      int16x8_t cb_const1 = vdupq_n_s16(   (short) ( 1.77200f*4096.0f+0.5f));\n\n      for (; i+7 < count; i += 8) {\n         // load\n         uint8x8_t y_bytes  = vld1_u8(y + i);\n         uint8x8_t cr_bytes = vld1_u8(pcr + i);\n         uint8x8_t cb_bytes = vld1_u8(pcb + i);\n         int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip));\n         int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip));\n\n         // expand to s16\n         int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4));\n         int16x8_t crw = vshll_n_s8(cr_biased, 7);\n         int16x8_t cbw = vshll_n_s8(cb_biased, 7);\n\n         // color transform\n         int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0);\n         int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0);\n         int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1);\n         int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1);\n         int16x8_t rws = vaddq_s16(yws, cr0);\n         int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1);\n         int16x8_t bws = vaddq_s16(yws, cb1);\n\n         // undo scaling, round, convert to byte\n         uint8x8x4_t o;\n         o.val[0] = vqrshrun_n_s16(rws, 4);\n         o.val[1] = vqrshrun_n_s16(gws, 4);\n         o.val[2] = vqrshrun_n_s16(bws, 4);\n         o.val[3] = vdup_n_u8(255);\n\n         // store, interleaving r/g/b/a\n         vst4_u8(out, o);\n         out += 8*4;\n      }\n   }\n#endif\n\n   for (; i < count; ++i) {\n      int y_fixed = (y[i] << 20) + (1<<19); // rounding\n      int r,g,b;\n      int cr = pcr[i] - 128;\n      int cb = pcb[i] - 128;\n      r = y_fixed + cr* stbi__float2fixed(1.40200f);\n      g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);\n      b = y_fixed                                   +   cb* stbi__float2fixed(1.77200f);\n      r >>= 20;\n      g >>= 20;\n      b >>= 20;\n      if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }\n      if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }\n      if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }\n      out[0] = (stbi_uc)r;\n      out[1] = (stbi_uc)g;\n      out[2] = (stbi_uc)b;\n      out[3] = 255;\n      out += step;\n   }\n}\n#endif\n\n// set up the kernels\nstatic void stbi__setup_jpeg(stbi__jpeg *j)\n{\n   j->idct_block_kernel = stbi__idct_block;\n   j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row;\n   j->resample_row_hv_2_kernel = stbi__resample_row_hv_2;\n\n#ifdef STBI_SSE2\n   if (stbi__sse2_available()) {\n      j->idct_block_kernel = stbi__idct_simd;\n      j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;\n      j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;\n   }\n#endif\n\n#ifdef STBI_NEON\n   j->idct_block_kernel = stbi__idct_simd;\n   j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;\n   j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;\n#endif\n}\n\n// clean up the temporary component buffers\nstatic void stbi__cleanup_jpeg(stbi__jpeg *j)\n{\n   stbi__free_jpeg_components(j, j->s->img_n, 0);\n}\n\ntypedef struct\n{\n   resample_row_func resample;\n   stbi_uc *line0,*line1;\n   int hs,vs;   // expansion factor in each axis\n   int w_lores; // horizontal pixels pre-expansion\n   int ystep;   // how far through vertical expansion we are\n   int ypos;    // which pre-expansion row we're on\n} stbi__resample;\n\n// fast 0..255 * 0..255 => 0..255 rounded multiplication\nstatic stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y)\n{\n   unsigned int t = x*y + 128;\n   return (stbi_uc) ((t + (t >>8)) >> 8);\n}\n\nstatic stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp)\n{\n   int n, decode_n, is_rgb;\n   z->s->img_n = 0; // make stbi__cleanup_jpeg safe\n\n   // validate req_comp\n   if (req_comp < 0 || req_comp > 4) return stbi__errpuc(\"bad req_comp\", \"Internal error\");\n\n   // load a jpeg image from whichever source, but leave in YCbCr format\n   if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; }\n\n   // determine actual number of components to generate\n   n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1;\n\n   is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif));\n\n   if (z->s->img_n == 3 && n < 3 && !is_rgb)\n      decode_n = 1;\n   else\n      decode_n = z->s->img_n;\n\n   // nothing to do if no components requested; check this now to avoid\n   // accessing uninitialized coutput[0] later\n   if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; }\n\n   // resample and color-convert\n   {\n      int k;\n      unsigned int i,j;\n      stbi_uc *output;\n      stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL };\n\n      stbi__resample res_comp[4];\n\n      for (k=0; k < decode_n; ++k) {\n         stbi__resample *r = &res_comp[k];\n\n         // allocate line buffer big enough for upsampling off the edges\n         // with upsample factor of 4\n         z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3);\n         if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc(\"outofmem\", \"Out of memory\"); }\n\n         r->hs      = z->img_h_max / z->img_comp[k].h;\n         r->vs      = z->img_v_max / z->img_comp[k].v;\n         r->ystep   = r->vs >> 1;\n         r->w_lores = (z->s->img_x + r->hs-1) / r->hs;\n         r->ypos    = 0;\n         r->line0   = r->line1 = z->img_comp[k].data;\n\n         if      (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;\n         else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2;\n         else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2;\n         else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel;\n         else                               r->resample = stbi__resample_row_generic;\n      }\n\n      // can't error after this so, this is safe\n      output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1);\n      if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc(\"outofmem\", \"Out of memory\"); }\n\n      // now go ahead and resample\n      for (j=0; j < z->s->img_y; ++j) {\n         stbi_uc *out = output + n * z->s->img_x * j;\n         for (k=0; k < decode_n; ++k) {\n            stbi__resample *r = &res_comp[k];\n            int y_bot = r->ystep >= (r->vs >> 1);\n            coutput[k] = r->resample(z->img_comp[k].linebuf,\n                                     y_bot ? r->line1 : r->line0,\n                                     y_bot ? r->line0 : r->line1,\n                                     r->w_lores, r->hs);\n            if (++r->ystep >= r->vs) {\n               r->ystep = 0;\n               r->line0 = r->line1;\n               if (++r->ypos < z->img_comp[k].y)\n                  r->line1 += z->img_comp[k].w2;\n            }\n         }\n         if (n >= 3) {\n            stbi_uc *y = coutput[0];\n            if (z->s->img_n == 3) {\n               if (is_rgb) {\n                  for (i=0; i < z->s->img_x; ++i) {\n                     out[0] = y[i];\n                     out[1] = coutput[1][i];\n                     out[2] = coutput[2][i];\n                     out[3] = 255;\n                     out += n;\n                  }\n               } else {\n                  z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);\n               }\n            } else if (z->s->img_n == 4) {\n               if (z->app14_color_transform == 0) { // CMYK\n                  for (i=0; i < z->s->img_x; ++i) {\n                     stbi_uc m = coutput[3][i];\n                     out[0] = stbi__blinn_8x8(coutput[0][i], m);\n                     out[1] = stbi__blinn_8x8(coutput[1][i], m);\n                     out[2] = stbi__blinn_8x8(coutput[2][i], m);\n                     out[3] = 255;\n                     out += n;\n                  }\n               } else if (z->app14_color_transform == 2) { // YCCK\n                  z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);\n                  for (i=0; i < z->s->img_x; ++i) {\n                     stbi_uc m = coutput[3][i];\n                     out[0] = stbi__blinn_8x8(255 - out[0], m);\n                     out[1] = stbi__blinn_8x8(255 - out[1], m);\n                     out[2] = stbi__blinn_8x8(255 - out[2], m);\n                     out += n;\n                  }\n               } else { // YCbCr + alpha?  Ignore the fourth channel for now\n                  z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);\n               }\n            } else\n               for (i=0; i < z->s->img_x; ++i) {\n                  out[0] = out[1] = out[2] = y[i];\n                  out[3] = 255; // not used if n==3\n                  out += n;\n               }\n         } else {\n            if (is_rgb) {\n               if (n == 1)\n                  for (i=0; i < z->s->img_x; ++i)\n                     *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);\n               else {\n                  for (i=0; i < z->s->img_x; ++i, out += 2) {\n                     out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);\n                     out[1] = 255;\n                  }\n               }\n            } else if (z->s->img_n == 4 && z->app14_color_transform == 0) {\n               for (i=0; i < z->s->img_x; ++i) {\n                  stbi_uc m = coutput[3][i];\n                  stbi_uc r = stbi__blinn_8x8(coutput[0][i], m);\n                  stbi_uc g = stbi__blinn_8x8(coutput[1][i], m);\n                  stbi_uc b = stbi__blinn_8x8(coutput[2][i], m);\n                  out[0] = stbi__compute_y(r, g, b);\n                  out[1] = 255;\n                  out += n;\n               }\n            } else if (z->s->img_n == 4 && z->app14_color_transform == 2) {\n               for (i=0; i < z->s->img_x; ++i) {\n                  out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]);\n                  out[1] = 255;\n                  out += n;\n               }\n            } else {\n               stbi_uc *y = coutput[0];\n               if (n == 1)\n                  for (i=0; i < z->s->img_x; ++i) out[i] = y[i];\n               else\n                  for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; }\n            }\n         }\n      }\n      stbi__cleanup_jpeg(z);\n      *out_x = z->s->img_x;\n      *out_y = z->s->img_y;\n      if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output\n      return output;\n   }\n}\n\nstatic void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)\n{\n   unsigned char* result;\n   stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));\n   if (!j) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n   memset(j, 0, sizeof(stbi__jpeg));\n   STBI_NOTUSED(ri);\n   j->s = s;\n   stbi__setup_jpeg(j);\n   result = load_jpeg_image(j, x,y,comp,req_comp);\n   STBI_FREE(j);\n   return result;\n}\n\nstatic int stbi__jpeg_test(stbi__context *s)\n{\n   int r;\n   stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));\n   if (!j) return stbi__err(\"outofmem\", \"Out of memory\");\n   memset(j, 0, sizeof(stbi__jpeg));\n   j->s = s;\n   stbi__setup_jpeg(j);\n   r = stbi__decode_jpeg_header(j, STBI__SCAN_type);\n   stbi__rewind(s);\n   STBI_FREE(j);\n   return r;\n}\n\nstatic int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp)\n{\n   if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {\n      stbi__rewind( j->s );\n      return 0;\n   }\n   if (x) *x = j->s->img_x;\n   if (y) *y = j->s->img_y;\n   if (comp) *comp = j->s->img_n >= 3 ? 3 : 1;\n   return 1;\n}\n\nstatic int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)\n{\n   int result;\n   stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));\n   if (!j) return stbi__err(\"outofmem\", \"Out of memory\");\n   memset(j, 0, sizeof(stbi__jpeg));\n   j->s = s;\n   result = stbi__jpeg_info_raw(j, x, y, comp);\n   STBI_FREE(j);\n   return result;\n}\n#endif\n\n// public domain zlib decode    v0.2  Sean Barrett 2006-11-18\n//    simple implementation\n//      - all input must be provided in an upfront buffer\n//      - all output is written to a single output buffer (can malloc/realloc)\n//    performance\n//      - fast huffman\n\n#ifndef STBI_NO_ZLIB\n\n// fast-way is faster to check than jpeg huffman, but slow way is slower\n#define STBI__ZFAST_BITS  9 // accelerate all cases in default tables\n#define STBI__ZFAST_MASK  ((1 << STBI__ZFAST_BITS) - 1)\n#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet\n\n// zlib-style huffman encoding\n// (jpegs packs from left, zlib from right, so can't share code)\ntypedef struct\n{\n   stbi__uint16 fast[1 << STBI__ZFAST_BITS];\n   stbi__uint16 firstcode[16];\n   int maxcode[17];\n   stbi__uint16 firstsymbol[16];\n   stbi_uc  size[STBI__ZNSYMS];\n   stbi__uint16 value[STBI__ZNSYMS];\n} stbi__zhuffman;\n\nstbi_inline static int stbi__bitreverse16(int n)\n{\n  n = ((n & 0xAAAA) >>  1) | ((n & 0x5555) << 1);\n  n = ((n & 0xCCCC) >>  2) | ((n & 0x3333) << 2);\n  n = ((n & 0xF0F0) >>  4) | ((n & 0x0F0F) << 4);\n  n = ((n & 0xFF00) >>  8) | ((n & 0x00FF) << 8);\n  return n;\n}\n\nstbi_inline static int stbi__bit_reverse(int v, int bits)\n{\n   STBI_ASSERT(bits <= 16);\n   // to bit reverse n bits, reverse 16 and shift\n   // e.g. 11 bits, bit reverse and shift away 5\n   return stbi__bitreverse16(v) >> (16-bits);\n}\n\nstatic int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num)\n{\n   int i,k=0;\n   int code, next_code[16], sizes[17];\n\n   // DEFLATE spec for generating codes\n   memset(sizes, 0, sizeof(sizes));\n   memset(z->fast, 0, sizeof(z->fast));\n   for (i=0; i < num; ++i)\n      ++sizes[sizelist[i]];\n   sizes[0] = 0;\n   for (i=1; i < 16; ++i)\n      if (sizes[i] > (1 << i))\n         return stbi__err(\"bad sizes\", \"Corrupt PNG\");\n   code = 0;\n   for (i=1; i < 16; ++i) {\n      next_code[i] = code;\n      z->firstcode[i] = (stbi__uint16) code;\n      z->firstsymbol[i] = (stbi__uint16) k;\n      code = (code + sizes[i]);\n      if (sizes[i])\n         if (code-1 >= (1 << i)) return stbi__err(\"bad codelengths\",\"Corrupt PNG\");\n      z->maxcode[i] = code << (16-i); // preshift for inner loop\n      code <<= 1;\n      k += sizes[i];\n   }\n   z->maxcode[16] = 0x10000; // sentinel\n   for (i=0; i < num; ++i) {\n      int s = sizelist[i];\n      if (s) {\n         int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];\n         stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i);\n         z->size [c] = (stbi_uc     ) s;\n         z->value[c] = (stbi__uint16) i;\n         if (s <= STBI__ZFAST_BITS) {\n            int j = stbi__bit_reverse(next_code[s],s);\n            while (j < (1 << STBI__ZFAST_BITS)) {\n               z->fast[j] = fastv;\n               j += (1 << s);\n            }\n         }\n         ++next_code[s];\n      }\n   }\n   return 1;\n}\n\n// zlib-from-memory implementation for PNG reading\n//    because PNG allows splitting the zlib stream arbitrarily,\n//    and it's annoying structurally to have PNG call ZLIB call PNG,\n//    we require PNG read all the IDATs and combine them into a single\n//    memory buffer\n\ntypedef struct\n{\n   stbi_uc *zbuffer, *zbuffer_end;\n   int num_bits;\n   stbi__uint32 code_buffer;\n\n   char *zout;\n   char *zout_start;\n   char *zout_end;\n   int   z_expandable;\n\n   stbi__zhuffman z_length, z_distance;\n} stbi__zbuf;\n\nstbi_inline static int stbi__zeof(stbi__zbuf *z)\n{\n   return (z->zbuffer >= z->zbuffer_end);\n}\n\nstbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z)\n{\n   return stbi__zeof(z) ? 0 : *z->zbuffer++;\n}\n\nstatic void stbi__fill_bits(stbi__zbuf *z)\n{\n   do {\n      if (z->code_buffer >= (1U << z->num_bits)) {\n        z->zbuffer = z->zbuffer_end;  /* treat this as EOF so we fail. */\n        return;\n      }\n      z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits;\n      z->num_bits += 8;\n   } while (z->num_bits <= 24);\n}\n\nstbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n)\n{\n   unsigned int k;\n   if (z->num_bits < n) stbi__fill_bits(z);\n   k = z->code_buffer & ((1 << n) - 1);\n   z->code_buffer >>= n;\n   z->num_bits -= n;\n   return k;\n}\n\nstatic int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)\n{\n   int b,s,k;\n   // not resolved by fast table, so compute it the slow way\n   // use jpeg approach, which requires MSbits at top\n   k = stbi__bit_reverse(a->code_buffer, 16);\n   for (s=STBI__ZFAST_BITS+1; ; ++s)\n      if (k < z->maxcode[s])\n         break;\n   if (s >= 16) return -1; // invalid code!\n   // code size is s, so:\n   b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];\n   if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere!\n   if (z->size[b] != s) return -1;  // was originally an assert, but report failure instead.\n   a->code_buffer >>= s;\n   a->num_bits -= s;\n   return z->value[b];\n}\n\nstbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)\n{\n   int b,s;\n   if (a->num_bits < 16) {\n      if (stbi__zeof(a)) {\n         return -1;   /* report error for unexpected end of data. */\n      }\n      stbi__fill_bits(a);\n   }\n   b = z->fast[a->code_buffer & STBI__ZFAST_MASK];\n   if (b) {\n      s = b >> 9;\n      a->code_buffer >>= s;\n      a->num_bits -= s;\n      return b & 511;\n   }\n   return stbi__zhuffman_decode_slowpath(a, z);\n}\n\nstatic int stbi__zexpand(stbi__zbuf *z, char *zout, int n)  // need to make room for n bytes\n{\n   char *q;\n   unsigned int cur, limit, old_limit;\n   z->zout = zout;\n   if (!z->z_expandable) return stbi__err(\"output buffer limit\",\"Corrupt PNG\");\n   cur   = (unsigned int) (z->zout - z->zout_start);\n   limit = old_limit = (unsigned) (z->zout_end - z->zout_start);\n   if (UINT_MAX - cur < (unsigned) n) return stbi__err(\"outofmem\", \"Out of memory\");\n   while (cur + n > limit) {\n      if(limit > UINT_MAX / 2) return stbi__err(\"outofmem\", \"Out of memory\");\n      limit *= 2;\n   }\n   q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);\n   STBI_NOTUSED(old_limit);\n   if (q == NULL) return stbi__err(\"outofmem\", \"Out of memory\");\n   z->zout_start = q;\n   z->zout       = q + cur;\n   z->zout_end   = q + limit;\n   return 1;\n}\n\nstatic const int stbi__zlength_base[31] = {\n   3,4,5,6,7,8,9,10,11,13,\n   15,17,19,23,27,31,35,43,51,59,\n   67,83,99,115,131,163,195,227,258,0,0 };\n\nstatic const int stbi__zlength_extra[31]=\n{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };\n\nstatic const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,\n257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};\n\nstatic const int stbi__zdist_extra[32] =\n{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};\n\nstatic int stbi__parse_huffman_block(stbi__zbuf *a)\n{\n   char *zout = a->zout;\n   for(;;) {\n      int z = stbi__zhuffman_decode(a, &a->z_length);\n      if (z < 256) {\n         if (z < 0) return stbi__err(\"bad huffman code\",\"Corrupt PNG\"); // error in huffman codes\n         if (zout >= a->zout_end) {\n            if (!stbi__zexpand(a, zout, 1)) return 0;\n            zout = a->zout;\n         }\n         *zout++ = (char) z;\n      } else {\n         stbi_uc *p;\n         int len,dist;\n         if (z == 256) {\n            a->zout = zout;\n            return 1;\n         }\n         if (z >= 286) return stbi__err(\"bad huffman code\",\"Corrupt PNG\"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data\n         z -= 257;\n         len = stbi__zlength_base[z];\n         if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);\n         z = stbi__zhuffman_decode(a, &a->z_distance);\n         if (z < 0 || z >= 30) return stbi__err(\"bad huffman code\",\"Corrupt PNG\"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data\n         dist = stbi__zdist_base[z];\n         if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);\n         if (zout - a->zout_start < dist) return stbi__err(\"bad dist\",\"Corrupt PNG\");\n         if (zout + len > a->zout_end) {\n            if (!stbi__zexpand(a, zout, len)) return 0;\n            zout = a->zout;\n         }\n         p = (stbi_uc *) (zout - dist);\n         if (dist == 1) { // run of one byte; common in images.\n            stbi_uc v = *p;\n            if (len) { do *zout++ = v; while (--len); }\n         } else {\n            if (len) { do *zout++ = *p++; while (--len); }\n         }\n      }\n   }\n}\n\nstatic int stbi__compute_huffman_codes(stbi__zbuf *a)\n{\n   static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };\n   stbi__zhuffman z_codelength;\n   stbi_uc lencodes[286+32+137];//padding for maximum single op\n   stbi_uc codelength_sizes[19];\n   int i,n;\n\n   int hlit  = stbi__zreceive(a,5) + 257;\n   int hdist = stbi__zreceive(a,5) + 1;\n   int hclen = stbi__zreceive(a,4) + 4;\n   int ntot  = hlit + hdist;\n\n   memset(codelength_sizes, 0, sizeof(codelength_sizes));\n   for (i=0; i < hclen; ++i) {\n      int s = stbi__zreceive(a,3);\n      codelength_sizes[length_dezigzag[i]] = (stbi_uc) s;\n   }\n   if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;\n\n   n = 0;\n   while (n < ntot) {\n      int c = stbi__zhuffman_decode(a, &z_codelength);\n      if (c < 0 || c >= 19) return stbi__err(\"bad codelengths\", \"Corrupt PNG\");\n      if (c < 16)\n         lencodes[n++] = (stbi_uc) c;\n      else {\n         stbi_uc fill = 0;\n         if (c == 16) {\n            c = stbi__zreceive(a,2)+3;\n            if (n == 0) return stbi__err(\"bad codelengths\", \"Corrupt PNG\");\n            fill = lencodes[n-1];\n         } else if (c == 17) {\n            c = stbi__zreceive(a,3)+3;\n         } else if (c == 18) {\n            c = stbi__zreceive(a,7)+11;\n         } else {\n            return stbi__err(\"bad codelengths\", \"Corrupt PNG\");\n         }\n         if (ntot - n < c) return stbi__err(\"bad codelengths\", \"Corrupt PNG\");\n         memset(lencodes+n, fill, c);\n         n += c;\n      }\n   }\n   if (n != ntot) return stbi__err(\"bad codelengths\",\"Corrupt PNG\");\n   if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;\n   if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;\n   return 1;\n}\n\nstatic int stbi__parse_uncompressed_block(stbi__zbuf *a)\n{\n   stbi_uc header[4];\n   int len,nlen,k;\n   if (a->num_bits & 7)\n      stbi__zreceive(a, a->num_bits & 7); // discard\n   // drain the bit-packed data into header\n   k = 0;\n   while (a->num_bits > 0) {\n      header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check\n      a->code_buffer >>= 8;\n      a->num_bits -= 8;\n   }\n   if (a->num_bits < 0) return stbi__err(\"zlib corrupt\",\"Corrupt PNG\");\n   // now fill header the normal way\n   while (k < 4)\n      header[k++] = stbi__zget8(a);\n   len  = header[1] * 256 + header[0];\n   nlen = header[3] * 256 + header[2];\n   if (nlen != (len ^ 0xffff)) return stbi__err(\"zlib corrupt\",\"Corrupt PNG\");\n   if (a->zbuffer + len > a->zbuffer_end) return stbi__err(\"read past buffer\",\"Corrupt PNG\");\n   if (a->zout + len > a->zout_end)\n      if (!stbi__zexpand(a, a->zout, len)) return 0;\n   memcpy(a->zout, a->zbuffer, len);\n   a->zbuffer += len;\n   a->zout += len;\n   return 1;\n}\n\nstatic int stbi__parse_zlib_header(stbi__zbuf *a)\n{\n   int cmf   = stbi__zget8(a);\n   int cm    = cmf & 15;\n   /* int cinfo = cmf >> 4; */\n   int flg   = stbi__zget8(a);\n   if (stbi__zeof(a)) return stbi__err(\"bad zlib header\",\"Corrupt PNG\"); // zlib spec\n   if ((cmf*256+flg) % 31 != 0) return stbi__err(\"bad zlib header\",\"Corrupt PNG\"); // zlib spec\n   if (flg & 32) return stbi__err(\"no preset dict\",\"Corrupt PNG\"); // preset dictionary not allowed in png\n   if (cm != 8) return stbi__err(\"bad compression\",\"Corrupt PNG\"); // DEFLATE required for png\n   // window = 1 << (8 + cinfo)... but who cares, we fully buffer output\n   return 1;\n}\n\nstatic const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] =\n{\n   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8\n};\nstatic const stbi_uc stbi__zdefault_distance[32] =\n{\n   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5\n};\n/*\nInit algorithm:\n{\n   int i;   // use <= to match clearly with spec\n   for (i=0; i <= 143; ++i)     stbi__zdefault_length[i]   = 8;\n   for (   ; i <= 255; ++i)     stbi__zdefault_length[i]   = 9;\n   for (   ; i <= 279; ++i)     stbi__zdefault_length[i]   = 7;\n   for (   ; i <= 287; ++i)     stbi__zdefault_length[i]   = 8;\n\n   for (i=0; i <=  31; ++i)     stbi__zdefault_distance[i] = 5;\n}\n*/\n\nstatic int stbi__parse_zlib(stbi__zbuf *a, int parse_header)\n{\n   int final, type;\n   if (parse_header)\n      if (!stbi__parse_zlib_header(a)) return 0;\n   a->num_bits = 0;\n   a->code_buffer = 0;\n   do {\n      final = stbi__zreceive(a,1);\n      type = stbi__zreceive(a,2);\n      if (type == 0) {\n         if (!stbi__parse_uncompressed_block(a)) return 0;\n      } else if (type == 3) {\n         return 0;\n      } else {\n         if (type == 1) {\n            // use fixed code lengths\n            if (!stbi__zbuild_huffman(&a->z_length  , stbi__zdefault_length  , STBI__ZNSYMS)) return 0;\n            if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance,  32)) return 0;\n         } else {\n            if (!stbi__compute_huffman_codes(a)) return 0;\n         }\n         if (!stbi__parse_huffman_block(a)) return 0;\n      }\n   } while (!final);\n   return 1;\n}\n\nstatic int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header)\n{\n   a->zout_start = obuf;\n   a->zout       = obuf;\n   a->zout_end   = obuf + olen;\n   a->z_expandable = exp;\n\n   return stbi__parse_zlib(a, parse_header);\n}\n\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen)\n{\n   stbi__zbuf a;\n   char *p = (char *) stbi__malloc(initial_size);\n   if (p == NULL) return NULL;\n   a.zbuffer = (stbi_uc *) buffer;\n   a.zbuffer_end = (stbi_uc *) buffer + len;\n   if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {\n      if (outlen) *outlen = (int) (a.zout - a.zout_start);\n      return a.zout_start;\n   } else {\n      STBI_FREE(a.zout_start);\n      return NULL;\n   }\n}\n\nSTBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)\n{\n   return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);\n}\n\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header)\n{\n   stbi__zbuf a;\n   char *p = (char *) stbi__malloc(initial_size);\n   if (p == NULL) return NULL;\n   a.zbuffer = (stbi_uc *) buffer;\n   a.zbuffer_end = (stbi_uc *) buffer + len;\n   if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {\n      if (outlen) *outlen = (int) (a.zout - a.zout_start);\n      return a.zout_start;\n   } else {\n      STBI_FREE(a.zout_start);\n      return NULL;\n   }\n}\n\nSTBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)\n{\n   stbi__zbuf a;\n   a.zbuffer = (stbi_uc *) ibuffer;\n   a.zbuffer_end = (stbi_uc *) ibuffer + ilen;\n   if (stbi__do_zlib(&a, obuffer, olen, 0, 1))\n      return (int) (a.zout - a.zout_start);\n   else\n      return -1;\n}\n\nSTBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)\n{\n   stbi__zbuf a;\n   char *p = (char *) stbi__malloc(16384);\n   if (p == NULL) return NULL;\n   a.zbuffer = (stbi_uc *) buffer;\n   a.zbuffer_end = (stbi_uc *) buffer+len;\n   if (stbi__do_zlib(&a, p, 16384, 1, 0)) {\n      if (outlen) *outlen = (int) (a.zout - a.zout_start);\n      return a.zout_start;\n   } else {\n      STBI_FREE(a.zout_start);\n      return NULL;\n   }\n}\n\nSTBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)\n{\n   stbi__zbuf a;\n   a.zbuffer = (stbi_uc *) ibuffer;\n   a.zbuffer_end = (stbi_uc *) ibuffer + ilen;\n   if (stbi__do_zlib(&a, obuffer, olen, 0, 0))\n      return (int) (a.zout - a.zout_start);\n   else\n      return -1;\n}\n#endif\n\n// public domain \"baseline\" PNG decoder   v0.10  Sean Barrett 2006-11-18\n//    simple implementation\n//      - only 8-bit samples\n//      - no CRC checking\n//      - allocates lots of intermediate memory\n//        - avoids problem of streaming data between subsystems\n//        - avoids explicit window management\n//    performance\n//      - uses stb_zlib, a PD zlib implementation with fast huffman decoding\n\n#ifndef STBI_NO_PNG\ntypedef struct\n{\n   stbi__uint32 length;\n   stbi__uint32 type;\n} stbi__pngchunk;\n\nstatic stbi__pngchunk stbi__get_chunk_header(stbi__context *s)\n{\n   stbi__pngchunk c;\n   c.length = stbi__get32be(s);\n   c.type   = stbi__get32be(s);\n   return c;\n}\n\nstatic int stbi__check_png_header(stbi__context *s)\n{\n   static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 };\n   int i;\n   for (i=0; i < 8; ++i)\n      if (stbi__get8(s) != png_sig[i]) return stbi__err(\"bad png sig\",\"Not a PNG\");\n   return 1;\n}\n\ntypedef struct\n{\n   stbi__context *s;\n   stbi_uc *idata, *expanded, *out;\n   int depth;\n} stbi__png;\n\n\nenum {\n   STBI__F_none=0,\n   STBI__F_sub=1,\n   STBI__F_up=2,\n   STBI__F_avg=3,\n   STBI__F_paeth=4,\n   // synthetic filters used for first scanline to avoid needing a dummy row of 0s\n   STBI__F_avg_first,\n   STBI__F_paeth_first\n};\n\nstatic stbi_uc first_row_filter[5] =\n{\n   STBI__F_none,\n   STBI__F_sub,\n   STBI__F_none,\n   STBI__F_avg_first,\n   STBI__F_paeth_first\n};\n\nstatic int stbi__paeth(int a, int b, int c)\n{\n   int p = a + b - c;\n   int pa = abs(p-a);\n   int pb = abs(p-b);\n   int pc = abs(p-c);\n   if (pa <= pb && pa <= pc) return a;\n   if (pb <= pc) return b;\n   return c;\n}\n\nstatic const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };\n\n// create the png data from post-deflated data\nstatic int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)\n{\n   int bytes = (depth == 16? 2 : 1);\n   stbi__context *s = a->s;\n   stbi__uint32 i,j,stride = x*out_n*bytes;\n   stbi__uint32 img_len, img_width_bytes;\n   int k;\n   int img_n = s->img_n; // copy it into a local for later\n\n   int output_bytes = out_n*bytes;\n   int filter_bytes = img_n*bytes;\n   int width = x;\n\n   STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);\n   a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into\n   if (!a->out) return stbi__err(\"outofmem\", \"Out of memory\");\n\n   if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err(\"too large\", \"Corrupt PNG\");\n   img_width_bytes = (((img_n * x * depth) + 7) >> 3);\n   img_len = (img_width_bytes + 1) * y;\n\n   // we used to check for exact match between raw_len and img_len on non-interlaced PNGs,\n   // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros),\n   // so just check for raw_len < img_len always.\n   if (raw_len < img_len) return stbi__err(\"not enough pixels\",\"Corrupt PNG\");\n\n   for (j=0; j < y; ++j) {\n      stbi_uc *cur = a->out + stride*j;\n      stbi_uc *prior;\n      int filter = *raw++;\n\n      if (filter > 4)\n         return stbi__err(\"invalid filter\",\"Corrupt PNG\");\n\n      if (depth < 8) {\n         if (img_width_bytes > x) return stbi__err(\"invalid width\",\"Corrupt PNG\");\n         cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place\n         filter_bytes = 1;\n         width = img_width_bytes;\n      }\n      prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above\n\n      // if first row, use special filter that doesn't sample previous row\n      if (j == 0) filter = first_row_filter[filter];\n\n      // handle first byte explicitly\n      for (k=0; k < filter_bytes; ++k) {\n         switch (filter) {\n            case STBI__F_none       : cur[k] = raw[k]; break;\n            case STBI__F_sub        : cur[k] = raw[k]; break;\n            case STBI__F_up         : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;\n            case STBI__F_avg        : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;\n            case STBI__F_paeth      : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;\n            case STBI__F_avg_first  : cur[k] = raw[k]; break;\n            case STBI__F_paeth_first: cur[k] = raw[k]; break;\n         }\n      }\n\n      if (depth == 8) {\n         if (img_n != out_n)\n            cur[img_n] = 255; // first pixel\n         raw += img_n;\n         cur += out_n;\n         prior += out_n;\n      } else if (depth == 16) {\n         if (img_n != out_n) {\n            cur[filter_bytes]   = 255; // first pixel top byte\n            cur[filter_bytes+1] = 255; // first pixel bottom byte\n         }\n         raw += filter_bytes;\n         cur += output_bytes;\n         prior += output_bytes;\n      } else {\n         raw += 1;\n         cur += 1;\n         prior += 1;\n      }\n\n      // this is a little gross, so that we don't switch per-pixel or per-component\n      if (depth < 8 || img_n == out_n) {\n         int nk = (width - 1)*filter_bytes;\n         #define STBI__CASE(f) \\\n             case f:     \\\n                for (k=0; k < nk; ++k)\n         switch (filter) {\n            // \"none\" filter turns into a memcpy here; make that explicit.\n            case STBI__F_none:         memcpy(cur, raw, nk); break;\n            STBI__CASE(STBI__F_sub)          { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;\n            STBI__CASE(STBI__F_up)           { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;\n            STBI__CASE(STBI__F_avg)          { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;\n            STBI__CASE(STBI__F_paeth)        { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;\n            STBI__CASE(STBI__F_avg_first)    { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;\n            STBI__CASE(STBI__F_paeth_first)  { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;\n         }\n         #undef STBI__CASE\n         raw += nk;\n      } else {\n         STBI_ASSERT(img_n+1 == out_n);\n         #define STBI__CASE(f) \\\n             case f:     \\\n                for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \\\n                   for (k=0; k < filter_bytes; ++k)\n         switch (filter) {\n            STBI__CASE(STBI__F_none)         { cur[k] = raw[k]; } break;\n            STBI__CASE(STBI__F_sub)          { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;\n            STBI__CASE(STBI__F_up)           { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;\n            STBI__CASE(STBI__F_avg)          { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;\n            STBI__CASE(STBI__F_paeth)        { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;\n            STBI__CASE(STBI__F_avg_first)    { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;\n            STBI__CASE(STBI__F_paeth_first)  { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;\n         }\n         #undef STBI__CASE\n\n         // the loop above sets the high byte of the pixels' alpha, but for\n         // 16 bit png files we also need the low byte set. we'll do that here.\n         if (depth == 16) {\n            cur = a->out + stride*j; // start at the beginning of the row again\n            for (i=0; i < x; ++i,cur+=output_bytes) {\n               cur[filter_bytes+1] = 255;\n            }\n         }\n      }\n   }\n\n   // we make a separate pass to expand bits to pixels; for performance,\n   // this could run two scanlines behind the above code, so it won't\n   // intefere with filtering but will still be in the cache.\n   if (depth < 8) {\n      for (j=0; j < y; ++j) {\n         stbi_uc *cur = a->out + stride*j;\n         stbi_uc *in  = a->out + stride*j + x*out_n - img_width_bytes;\n         // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit\n         // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop\n         stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range\n\n         // note that the final byte might overshoot and write more data than desired.\n         // we can allocate enough data that this never writes out of memory, but it\n         // could also overwrite the next scanline. can it overwrite non-empty data\n         // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.\n         // so we need to explicitly clamp the final ones\n\n         if (depth == 4) {\n            for (k=x*img_n; k >= 2; k-=2, ++in) {\n               *cur++ = scale * ((*in >> 4)       );\n               *cur++ = scale * ((*in     ) & 0x0f);\n            }\n            if (k > 0) *cur++ = scale * ((*in >> 4)       );\n         } else if (depth == 2) {\n            for (k=x*img_n; k >= 4; k-=4, ++in) {\n               *cur++ = scale * ((*in >> 6)       );\n               *cur++ = scale * ((*in >> 4) & 0x03);\n               *cur++ = scale * ((*in >> 2) & 0x03);\n               *cur++ = scale * ((*in     ) & 0x03);\n            }\n            if (k > 0) *cur++ = scale * ((*in >> 6)       );\n            if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);\n            if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);\n         } else if (depth == 1) {\n            for (k=x*img_n; k >= 8; k-=8, ++in) {\n               *cur++ = scale * ((*in >> 7)       );\n               *cur++ = scale * ((*in >> 6) & 0x01);\n               *cur++ = scale * ((*in >> 5) & 0x01);\n               *cur++ = scale * ((*in >> 4) & 0x01);\n               *cur++ = scale * ((*in >> 3) & 0x01);\n               *cur++ = scale * ((*in >> 2) & 0x01);\n               *cur++ = scale * ((*in >> 1) & 0x01);\n               *cur++ = scale * ((*in     ) & 0x01);\n            }\n            if (k > 0) *cur++ = scale * ((*in >> 7)       );\n            if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);\n            if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);\n            if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);\n            if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);\n            if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);\n            if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);\n         }\n         if (img_n != out_n) {\n            int q;\n            // insert alpha = 255\n            cur = a->out + stride*j;\n            if (img_n == 1) {\n               for (q=x-1; q >= 0; --q) {\n                  cur[q*2+1] = 255;\n                  cur[q*2+0] = cur[q];\n               }\n            } else {\n               STBI_ASSERT(img_n == 3);\n               for (q=x-1; q >= 0; --q) {\n                  cur[q*4+3] = 255;\n                  cur[q*4+2] = cur[q*3+2];\n                  cur[q*4+1] = cur[q*3+1];\n                  cur[q*4+0] = cur[q*3+0];\n               }\n            }\n         }\n      }\n   } else if (depth == 16) {\n      // force the image data from big-endian to platform-native.\n      // this is done in a separate pass due to the decoding relying\n      // on the data being untouched, but could probably be done\n      // per-line during decode if care is taken.\n      stbi_uc *cur = a->out;\n      stbi__uint16 *cur16 = (stbi__uint16*)cur;\n\n      for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {\n         *cur16 = (cur[0] << 8) | cur[1];\n      }\n   }\n\n   return 1;\n}\n\nstatic int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)\n{\n   int bytes = (depth == 16 ? 2 : 1);\n   int out_bytes = out_n * bytes;\n   stbi_uc *final;\n   int p;\n   if (!interlaced)\n      return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);\n\n   // de-interlacing\n   final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);\n   if (!final) return stbi__err(\"outofmem\", \"Out of memory\");\n   for (p=0; p < 7; ++p) {\n      int xorig[] = { 0,4,0,2,0,1,0 };\n      int yorig[] = { 0,0,4,0,2,0,1 };\n      int xspc[]  = { 8,8,4,4,2,2,1 };\n      int yspc[]  = { 8,8,8,4,4,2,2 };\n      int i,j,x,y;\n      // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1\n      x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];\n      y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];\n      if (x && y) {\n         stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;\n         if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {\n            STBI_FREE(final);\n            return 0;\n         }\n         for (j=0; j < y; ++j) {\n            for (i=0; i < x; ++i) {\n               int out_y = j*yspc[p]+yorig[p];\n               int out_x = i*xspc[p]+xorig[p];\n               memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes,\n                      a->out + (j*x+i)*out_bytes, out_bytes);\n            }\n         }\n         STBI_FREE(a->out);\n         image_data += img_len;\n         image_data_len -= img_len;\n      }\n   }\n   a->out = final;\n\n   return 1;\n}\n\nstatic int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n)\n{\n   stbi__context *s = z->s;\n   stbi__uint32 i, pixel_count = s->img_x * s->img_y;\n   stbi_uc *p = z->out;\n\n   // compute color-based transparency, assuming we've\n   // already got 255 as the alpha value in the output\n   STBI_ASSERT(out_n == 2 || out_n == 4);\n\n   if (out_n == 2) {\n      for (i=0; i < pixel_count; ++i) {\n         p[1] = (p[0] == tc[0] ? 0 : 255);\n         p += 2;\n      }\n   } else {\n      for (i=0; i < pixel_count; ++i) {\n         if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])\n            p[3] = 0;\n         p += 4;\n      }\n   }\n   return 1;\n}\n\nstatic int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n)\n{\n   stbi__context *s = z->s;\n   stbi__uint32 i, pixel_count = s->img_x * s->img_y;\n   stbi__uint16 *p = (stbi__uint16*) z->out;\n\n   // compute color-based transparency, assuming we've\n   // already got 65535 as the alpha value in the output\n   STBI_ASSERT(out_n == 2 || out_n == 4);\n\n   if (out_n == 2) {\n      for (i = 0; i < pixel_count; ++i) {\n         p[1] = (p[0] == tc[0] ? 0 : 65535);\n         p += 2;\n      }\n   } else {\n      for (i = 0; i < pixel_count; ++i) {\n         if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])\n            p[3] = 0;\n         p += 4;\n      }\n   }\n   return 1;\n}\n\nstatic int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n)\n{\n   stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;\n   stbi_uc *p, *temp_out, *orig = a->out;\n\n   p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0);\n   if (p == NULL) return stbi__err(\"outofmem\", \"Out of memory\");\n\n   // between here and free(out) below, exitting would leak\n   temp_out = p;\n\n   if (pal_img_n == 3) {\n      for (i=0; i < pixel_count; ++i) {\n         int n = orig[i]*4;\n         p[0] = palette[n  ];\n         p[1] = palette[n+1];\n         p[2] = palette[n+2];\n         p += 3;\n      }\n   } else {\n      for (i=0; i < pixel_count; ++i) {\n         int n = orig[i]*4;\n         p[0] = palette[n  ];\n         p[1] = palette[n+1];\n         p[2] = palette[n+2];\n         p[3] = palette[n+3];\n         p += 4;\n      }\n   }\n   STBI_FREE(a->out);\n   a->out = temp_out;\n\n   STBI_NOTUSED(len);\n\n   return 1;\n}\n\nstatic int stbi__unpremultiply_on_load_global = 0;\nstatic int stbi__de_iphone_flag_global = 0;\n\nSTBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)\n{\n   stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply;\n}\n\nSTBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)\n{\n   stbi__de_iphone_flag_global = flag_true_if_should_convert;\n}\n\n#ifndef STBI_THREAD_LOCAL\n#define stbi__unpremultiply_on_load  stbi__unpremultiply_on_load_global\n#define stbi__de_iphone_flag  stbi__de_iphone_flag_global\n#else\nstatic STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;\nstatic STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;\n\nSTBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)\n{\n   stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;\n   stbi__unpremultiply_on_load_set = 1;\n}\n\nSTBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert)\n{\n   stbi__de_iphone_flag_local = flag_true_if_should_convert;\n   stbi__de_iphone_flag_set = 1;\n}\n\n#define stbi__unpremultiply_on_load  (stbi__unpremultiply_on_load_set           \\\n                                       ? stbi__unpremultiply_on_load_local      \\\n                                       : stbi__unpremultiply_on_load_global)\n#define stbi__de_iphone_flag  (stbi__de_iphone_flag_set                         \\\n                                ? stbi__de_iphone_flag_local                    \\\n                                : stbi__de_iphone_flag_global)\n#endif // STBI_THREAD_LOCAL\n\nstatic void stbi__de_iphone(stbi__png *z)\n{\n   stbi__context *s = z->s;\n   stbi__uint32 i, pixel_count = s->img_x * s->img_y;\n   stbi_uc *p = z->out;\n\n   if (s->img_out_n == 3) {  // convert bgr to rgb\n      for (i=0; i < pixel_count; ++i) {\n         stbi_uc t = p[0];\n         p[0] = p[2];\n         p[2] = t;\n         p += 3;\n      }\n   } else {\n      STBI_ASSERT(s->img_out_n == 4);\n      if (stbi__unpremultiply_on_load) {\n         // convert bgr to rgb and unpremultiply\n         for (i=0; i < pixel_count; ++i) {\n            stbi_uc a = p[3];\n            stbi_uc t = p[0];\n            if (a) {\n               stbi_uc half = a / 2;\n               p[0] = (p[2] * 255 + half) / a;\n               p[1] = (p[1] * 255 + half) / a;\n               p[2] = ( t   * 255 + half) / a;\n            } else {\n               p[0] = p[2];\n               p[2] = t;\n            }\n            p += 4;\n         }\n      } else {\n         // convert bgr to rgb\n         for (i=0; i < pixel_count; ++i) {\n            stbi_uc t = p[0];\n            p[0] = p[2];\n            p[2] = t;\n            p += 4;\n         }\n      }\n   }\n}\n\n#define STBI__PNG_TYPE(a,b,c,d)  (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d))\n\nstatic int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)\n{\n   stbi_uc palette[1024], pal_img_n=0;\n   stbi_uc has_trans=0, tc[3]={0};\n   stbi__uint16 tc16[3];\n   stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;\n   int first=1,k,interlace=0, color=0, is_iphone=0;\n   stbi__context *s = z->s;\n\n   z->expanded = NULL;\n   z->idata = NULL;\n   z->out = NULL;\n\n   if (!stbi__check_png_header(s)) return 0;\n\n   if (scan == STBI__SCAN_type) return 1;\n\n   for (;;) {\n      stbi__pngchunk c = stbi__get_chunk_header(s);\n      switch (c.type) {\n         case STBI__PNG_TYPE('C','g','B','I'):\n            is_iphone = 1;\n            stbi__skip(s, c.length);\n            break;\n         case STBI__PNG_TYPE('I','H','D','R'): {\n            int comp,filter;\n            if (!first) return stbi__err(\"multiple IHDR\",\"Corrupt PNG\");\n            first = 0;\n            if (c.length != 13) return stbi__err(\"bad IHDR len\",\"Corrupt PNG\");\n            s->img_x = stbi__get32be(s);\n            s->img_y = stbi__get32be(s);\n            if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err(\"too large\",\"Very large image (corrupt?)\");\n            if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err(\"too large\",\"Very large image (corrupt?)\");\n            z->depth = stbi__get8(s);  if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16)  return stbi__err(\"1/2/4/8/16-bit only\",\"PNG not supported: 1/2/4/8/16-bit only\");\n            color = stbi__get8(s);  if (color > 6)         return stbi__err(\"bad ctype\",\"Corrupt PNG\");\n            if (color == 3 && z->depth == 16)                  return stbi__err(\"bad ctype\",\"Corrupt PNG\");\n            if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err(\"bad ctype\",\"Corrupt PNG\");\n            comp  = stbi__get8(s);  if (comp) return stbi__err(\"bad comp method\",\"Corrupt PNG\");\n            filter= stbi__get8(s);  if (filter) return stbi__err(\"bad filter method\",\"Corrupt PNG\");\n            interlace = stbi__get8(s); if (interlace>1) return stbi__err(\"bad interlace method\",\"Corrupt PNG\");\n            if (!s->img_x || !s->img_y) return stbi__err(\"0-pixel image\",\"Corrupt PNG\");\n            if (!pal_img_n) {\n               s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);\n               if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err(\"too large\", \"Image too large to decode\");\n            } else {\n               // if paletted, then pal_n is our final components, and\n               // img_n is # components to decompress/filter.\n               s->img_n = 1;\n               if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err(\"too large\",\"Corrupt PNG\");\n            }\n            // even with SCAN_header, have to scan to see if we have a tRNS\n            break;\n         }\n\n         case STBI__PNG_TYPE('P','L','T','E'):  {\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (c.length > 256*3) return stbi__err(\"invalid PLTE\",\"Corrupt PNG\");\n            pal_len = c.length / 3;\n            if (pal_len * 3 != c.length) return stbi__err(\"invalid PLTE\",\"Corrupt PNG\");\n            for (i=0; i < pal_len; ++i) {\n               palette[i*4+0] = stbi__get8(s);\n               palette[i*4+1] = stbi__get8(s);\n               palette[i*4+2] = stbi__get8(s);\n               palette[i*4+3] = 255;\n            }\n            break;\n         }\n\n         case STBI__PNG_TYPE('t','R','N','S'): {\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (z->idata) return stbi__err(\"tRNS after IDAT\",\"Corrupt PNG\");\n            if (pal_img_n) {\n               if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; }\n               if (pal_len == 0) return stbi__err(\"tRNS before PLTE\",\"Corrupt PNG\");\n               if (c.length > pal_len) return stbi__err(\"bad tRNS len\",\"Corrupt PNG\");\n               pal_img_n = 4;\n               for (i=0; i < c.length; ++i)\n                  palette[i*4+3] = stbi__get8(s);\n            } else {\n               if (!(s->img_n & 1)) return stbi__err(\"tRNS with alpha\",\"Corrupt PNG\");\n               if (c.length != (stbi__uint32) s->img_n*2) return stbi__err(\"bad tRNS len\",\"Corrupt PNG\");\n               has_trans = 1;\n               // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.\n               if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }\n               if (z->depth == 16) {\n                  for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is\n               } else {\n                  for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger\n               }\n            }\n            break;\n         }\n\n         case STBI__PNG_TYPE('I','D','A','T'): {\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (pal_img_n && !pal_len) return stbi__err(\"no PLTE\",\"Corrupt PNG\");\n            if (scan == STBI__SCAN_header) {\n               // header scan definitely stops at first IDAT\n               if (pal_img_n)\n                  s->img_n = pal_img_n;\n               return 1;\n            }\n            if (c.length > (1u << 30)) return stbi__err(\"IDAT size limit\", \"IDAT section larger than 2^30 bytes\");\n            if ((int)(ioff + c.length) < (int)ioff) return 0;\n            if (ioff + c.length > idata_limit) {\n               stbi__uint32 idata_limit_old = idata_limit;\n               stbi_uc *p;\n               if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;\n               while (ioff + c.length > idata_limit)\n                  idata_limit *= 2;\n               STBI_NOTUSED(idata_limit_old);\n               p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err(\"outofmem\", \"Out of memory\");\n               z->idata = p;\n            }\n            if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err(\"outofdata\",\"Corrupt PNG\");\n            ioff += c.length;\n            break;\n         }\n\n         case STBI__PNG_TYPE('I','E','N','D'): {\n            stbi__uint32 raw_len, bpl;\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (scan != STBI__SCAN_load) return 1;\n            if (z->idata == NULL) return stbi__err(\"no IDAT\",\"Corrupt PNG\");\n            // initial guess for decoded data size to avoid unnecessary reallocs\n            bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component\n            raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;\n            z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);\n            if (z->expanded == NULL) return 0; // zlib should set error\n            STBI_FREE(z->idata); z->idata = NULL;\n            if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)\n               s->img_out_n = s->img_n+1;\n            else\n               s->img_out_n = s->img_n;\n            if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0;\n            if (has_trans) {\n               if (z->depth == 16) {\n                  if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0;\n               } else {\n                  if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;\n               }\n            }\n            if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)\n               stbi__de_iphone(z);\n            if (pal_img_n) {\n               // pal_img_n == 3 or 4\n               s->img_n = pal_img_n; // record the actual colors we had\n               s->img_out_n = pal_img_n;\n               if (req_comp >= 3) s->img_out_n = req_comp;\n               if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))\n                  return 0;\n            } else if (has_trans) {\n               // non-paletted image with tRNS -> source image has (constant) alpha\n               ++s->img_n;\n            }\n            STBI_FREE(z->expanded); z->expanded = NULL;\n            // end of PNG chunk, read and skip CRC\n            stbi__get32be(s);\n            return 1;\n         }\n\n         default:\n            // if critical, fail\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if ((c.type & (1 << 29)) == 0) {\n               #ifndef STBI_NO_FAILURE_STRINGS\n               // not threadsafe\n               static char invalid_chunk[] = \"XXXX PNG chunk not known\";\n               invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);\n               invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);\n               invalid_chunk[2] = STBI__BYTECAST(c.type >>  8);\n               invalid_chunk[3] = STBI__BYTECAST(c.type >>  0);\n               #endif\n               return stbi__err(invalid_chunk, \"PNG not supported: unknown PNG chunk type\");\n            }\n            stbi__skip(s, c.length);\n            break;\n      }\n      // end of PNG chunk, read and skip CRC\n      stbi__get32be(s);\n   }\n}\n\nstatic void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri)\n{\n   void *result=NULL;\n   if (req_comp < 0 || req_comp > 4) return stbi__errpuc(\"bad req_comp\", \"Internal error\");\n   if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {\n      if (p->depth <= 8)\n         ri->bits_per_channel = 8;\n      else if (p->depth == 16)\n         ri->bits_per_channel = 16;\n      else\n         return stbi__errpuc(\"bad bits_per_channel\", \"PNG not supported: unsupported color depth\");\n      result = p->out;\n      p->out = NULL;\n      if (req_comp && req_comp != p->s->img_out_n) {\n         if (ri->bits_per_channel == 8)\n            result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);\n         else\n            result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);\n         p->s->img_out_n = req_comp;\n         if (result == NULL) return result;\n      }\n      *x = p->s->img_x;\n      *y = p->s->img_y;\n      if (n) *n = p->s->img_n;\n   }\n   STBI_FREE(p->out);      p->out      = NULL;\n   STBI_FREE(p->expanded); p->expanded = NULL;\n   STBI_FREE(p->idata);    p->idata    = NULL;\n\n   return result;\n}\n\nstatic void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)\n{\n   stbi__png p;\n   p.s = s;\n   return stbi__do_png(&p, x,y,comp,req_comp, ri);\n}\n\nstatic int stbi__png_test(stbi__context *s)\n{\n   int r;\n   r = stbi__check_png_header(s);\n   stbi__rewind(s);\n   return r;\n}\n\nstatic int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp)\n{\n   if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {\n      stbi__rewind( p->s );\n      return 0;\n   }\n   if (x) *x = p->s->img_x;\n   if (y) *y = p->s->img_y;\n   if (comp) *comp = p->s->img_n;\n   return 1;\n}\n\nstatic int stbi__png_info(stbi__context *s, int *x, int *y, int *comp)\n{\n   stbi__png p;\n   p.s = s;\n   return stbi__png_info_raw(&p, x, y, comp);\n}\n\nstatic int stbi__png_is16(stbi__context *s)\n{\n   stbi__png p;\n   p.s = s;\n   if (!stbi__png_info_raw(&p, NULL, NULL, NULL))\n\t   return 0;\n   if (p.depth != 16) {\n      stbi__rewind(p.s);\n      return 0;\n   }\n   return 1;\n}\n#endif\n\n// Microsoft/Windows BMP image\n\n#ifndef STBI_NO_BMP\nstatic int stbi__bmp_test_raw(stbi__context *s)\n{\n   int r;\n   int sz;\n   if (stbi__get8(s) != 'B') return 0;\n   if (stbi__get8(s) != 'M') return 0;\n   stbi__get32le(s); // discard filesize\n   stbi__get16le(s); // discard reserved\n   stbi__get16le(s); // discard reserved\n   stbi__get32le(s); // discard data offset\n   sz = stbi__get32le(s);\n   r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124);\n   return r;\n}\n\nstatic int stbi__bmp_test(stbi__context *s)\n{\n   int r = stbi__bmp_test_raw(s);\n   stbi__rewind(s);\n   return r;\n}\n\n\n// returns 0..31 for the highest set bit\nstatic int stbi__high_bit(unsigned int z)\n{\n   int n=0;\n   if (z == 0) return -1;\n   if (z >= 0x10000) { n += 16; z >>= 16; }\n   if (z >= 0x00100) { n +=  8; z >>=  8; }\n   if (z >= 0x00010) { n +=  4; z >>=  4; }\n   if (z >= 0x00004) { n +=  2; z >>=  2; }\n   if (z >= 0x00002) { n +=  1;/* >>=  1;*/ }\n   return n;\n}\n\nstatic int stbi__bitcount(unsigned int a)\n{\n   a = (a & 0x55555555) + ((a >>  1) & 0x55555555); // max 2\n   a = (a & 0x33333333) + ((a >>  2) & 0x33333333); // max 4\n   a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits\n   a = (a + (a >> 8)); // max 16 per 8 bits\n   a = (a + (a >> 16)); // max 32 per 8 bits\n   return a & 0xff;\n}\n\n// extract an arbitrarily-aligned N-bit value (N=bits)\n// from v, and then make it 8-bits long and fractionally\n// extend it to full full range.\nstatic int stbi__shiftsigned(unsigned int v, int shift, int bits)\n{\n   static unsigned int mul_table[9] = {\n      0,\n      0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/,\n      0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/,\n   };\n   static unsigned int shift_table[9] = {\n      0, 0,0,1,0,2,4,6,0,\n   };\n   if (shift < 0)\n      v <<= -shift;\n   else\n      v >>= shift;\n   STBI_ASSERT(v < 256);\n   v >>= (8-bits);\n   STBI_ASSERT(bits >= 0 && bits <= 8);\n   return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits];\n}\n\ntypedef struct\n{\n   int bpp, offset, hsz;\n   unsigned int mr,mg,mb,ma, all_a;\n   int extra_read;\n} stbi__bmp_data;\n\nstatic int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress)\n{\n   // BI_BITFIELDS specifies masks explicitly, don't override\n   if (compress == 3)\n      return 1;\n\n   if (compress == 0) {\n      if (info->bpp == 16) {\n         info->mr = 31u << 10;\n         info->mg = 31u <<  5;\n         info->mb = 31u <<  0;\n      } else if (info->bpp == 32) {\n         info->mr = 0xffu << 16;\n         info->mg = 0xffu <<  8;\n         info->mb = 0xffu <<  0;\n         info->ma = 0xffu << 24;\n         info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0\n      } else {\n         // otherwise, use defaults, which is all-0\n         info->mr = info->mg = info->mb = info->ma = 0;\n      }\n      return 1;\n   }\n   return 0; // error\n}\n\nstatic void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)\n{\n   int hsz;\n   if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc(\"not BMP\", \"Corrupt BMP\");\n   stbi__get32le(s); // discard filesize\n   stbi__get16le(s); // discard reserved\n   stbi__get16le(s); // discard reserved\n   info->offset = stbi__get32le(s);\n   info->hsz = hsz = stbi__get32le(s);\n   info->mr = info->mg = info->mb = info->ma = 0;\n   info->extra_read = 14;\n\n   if (info->offset < 0) return stbi__errpuc(\"bad BMP\", \"bad BMP\");\n\n   if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc(\"unknown BMP\", \"BMP type not supported: unknown\");\n   if (hsz == 12) {\n      s->img_x = stbi__get16le(s);\n      s->img_y = stbi__get16le(s);\n   } else {\n      s->img_x = stbi__get32le(s);\n      s->img_y = stbi__get32le(s);\n   }\n   if (stbi__get16le(s) != 1) return stbi__errpuc(\"bad BMP\", \"bad BMP\");\n   info->bpp = stbi__get16le(s);\n   if (hsz != 12) {\n      int compress = stbi__get32le(s);\n      if (compress == 1 || compress == 2) return stbi__errpuc(\"BMP RLE\", \"BMP type not supported: RLE\");\n      if (compress >= 4) return stbi__errpuc(\"BMP JPEG/PNG\", \"BMP type not supported: unsupported compression\"); // this includes PNG/JPEG modes\n      if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc(\"bad BMP\", \"bad BMP\"); // bitfields requires 16 or 32 bits/pixel\n      stbi__get32le(s); // discard sizeof\n      stbi__get32le(s); // discard hres\n      stbi__get32le(s); // discard vres\n      stbi__get32le(s); // discard colorsused\n      stbi__get32le(s); // discard max important\n      if (hsz == 40 || hsz == 56) {\n         if (hsz == 56) {\n            stbi__get32le(s);\n            stbi__get32le(s);\n            stbi__get32le(s);\n            stbi__get32le(s);\n         }\n         if (info->bpp == 16 || info->bpp == 32) {\n            if (compress == 0) {\n               stbi__bmp_set_mask_defaults(info, compress);\n            } else if (compress == 3) {\n               info->mr = stbi__get32le(s);\n               info->mg = stbi__get32le(s);\n               info->mb = stbi__get32le(s);\n               info->extra_read += 12;\n               // not documented, but generated by photoshop and handled by mspaint\n               if (info->mr == info->mg && info->mg == info->mb) {\n                  // ?!?!?\n                  return stbi__errpuc(\"bad BMP\", \"bad BMP\");\n               }\n            } else\n               return stbi__errpuc(\"bad BMP\", \"bad BMP\");\n         }\n      } else {\n         // V4/V5 header\n         int i;\n         if (hsz != 108 && hsz != 124)\n            return stbi__errpuc(\"bad BMP\", \"bad BMP\");\n         info->mr = stbi__get32le(s);\n         info->mg = stbi__get32le(s);\n         info->mb = stbi__get32le(s);\n         info->ma = stbi__get32le(s);\n         if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs\n            stbi__bmp_set_mask_defaults(info, compress);\n         stbi__get32le(s); // discard color space\n         for (i=0; i < 12; ++i)\n            stbi__get32le(s); // discard color space parameters\n         if (hsz == 124) {\n            stbi__get32le(s); // discard rendering intent\n            stbi__get32le(s); // discard offset of profile data\n            stbi__get32le(s); // discard size of profile data\n            stbi__get32le(s); // discard reserved\n         }\n      }\n   }\n   return (void *) 1;\n}\n\n\nstatic void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)\n{\n   stbi_uc *out;\n   unsigned int mr=0,mg=0,mb=0,ma=0, all_a;\n   stbi_uc pal[256][4];\n   int psize=0,i,j,width;\n   int flip_vertically, pad, target;\n   stbi__bmp_data info;\n   STBI_NOTUSED(ri);\n\n   info.all_a = 255;\n   if (stbi__bmp_parse_header(s, &info) == NULL)\n      return NULL; // error code already set\n\n   flip_vertically = ((int) s->img_y) > 0;\n   s->img_y = abs((int) s->img_y);\n\n   if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc(\"too large\",\"Very large image (corrupt?)\");\n   if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc(\"too large\",\"Very large image (corrupt?)\");\n\n   mr = info.mr;\n   mg = info.mg;\n   mb = info.mb;\n   ma = info.ma;\n   all_a = info.all_a;\n\n   if (info.hsz == 12) {\n      if (info.bpp < 24)\n         psize = (info.offset - info.extra_read - 24) / 3;\n   } else {\n      if (info.bpp < 16)\n         psize = (info.offset - info.extra_read - info.hsz) >> 2;\n   }\n   if (psize == 0) {\n      // accept some number of extra bytes after the header, but if the offset points either to before\n      // the header ends or implies a large amount of extra data, reject the file as malformed\n      int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original);\n      int header_limit = 1024; // max we actually read is below 256 bytes currently.\n      int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size.\n      if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) {\n         return stbi__errpuc(\"bad header\", \"Corrupt BMP\");\n      }\n      // we established that bytes_read_so_far is positive and sensible.\n      // the first half of this test rejects offsets that are either too small positives, or\n      // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn\n      // ensures the number computed in the second half of the test can't overflow.\n      if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) {\n         return stbi__errpuc(\"bad offset\", \"Corrupt BMP\");\n      } else {\n         stbi__skip(s, info.offset - bytes_read_so_far);\n      }\n   }\n\n   if (info.bpp == 24 && ma == 0xff000000)\n      s->img_n = 3;\n   else\n      s->img_n = ma ? 4 : 3;\n   if (req_comp && req_comp >= 3) // we can directly decode 3 or 4\n      target = req_comp;\n   else\n      target = s->img_n; // if they want monochrome, we'll post-convert\n\n   // sanity-check size\n   if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0))\n      return stbi__errpuc(\"too large\", \"Corrupt BMP\");\n\n   out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0);\n   if (!out) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n   if (info.bpp < 16) {\n      int z=0;\n      if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc(\"invalid\", \"Corrupt BMP\"); }\n      for (i=0; i < psize; ++i) {\n         pal[i][2] = stbi__get8(s);\n         pal[i][1] = stbi__get8(s);\n         pal[i][0] = stbi__get8(s);\n         if (info.hsz != 12) stbi__get8(s);\n         pal[i][3] = 255;\n      }\n      stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4));\n      if (info.bpp == 1) width = (s->img_x + 7) >> 3;\n      else if (info.bpp == 4) width = (s->img_x + 1) >> 1;\n      else if (info.bpp == 8) width = s->img_x;\n      else { STBI_FREE(out); return stbi__errpuc(\"bad bpp\", \"Corrupt BMP\"); }\n      pad = (-width)&3;\n      if (info.bpp == 1) {\n         for (j=0; j < (int) s->img_y; ++j) {\n            int bit_offset = 7, v = stbi__get8(s);\n            for (i=0; i < (int) s->img_x; ++i) {\n               int color = (v>>bit_offset)&0x1;\n               out[z++] = pal[color][0];\n               out[z++] = pal[color][1];\n               out[z++] = pal[color][2];\n               if (target == 4) out[z++] = 255;\n               if (i+1 == (int) s->img_x) break;\n               if((--bit_offset) < 0) {\n                  bit_offset = 7;\n                  v = stbi__get8(s);\n               }\n            }\n            stbi__skip(s, pad);\n         }\n      } else {\n         for (j=0; j < (int) s->img_y; ++j) {\n            for (i=0; i < (int) s->img_x; i += 2) {\n               int v=stbi__get8(s),v2=0;\n               if (info.bpp == 4) {\n                  v2 = v & 15;\n                  v >>= 4;\n               }\n               out[z++] = pal[v][0];\n               out[z++] = pal[v][1];\n               out[z++] = pal[v][2];\n               if (target == 4) out[z++] = 255;\n               if (i+1 == (int) s->img_x) break;\n               v = (info.bpp == 8) ? stbi__get8(s) : v2;\n               out[z++] = pal[v][0];\n               out[z++] = pal[v][1];\n               out[z++] = pal[v][2];\n               if (target == 4) out[z++] = 255;\n            }\n            stbi__skip(s, pad);\n         }\n      }\n   } else {\n      int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;\n      int z = 0;\n      int easy=0;\n      stbi__skip(s, info.offset - info.extra_read - info.hsz);\n      if (info.bpp == 24) width = 3 * s->img_x;\n      else if (info.bpp == 16) width = 2*s->img_x;\n      else /* bpp = 32 and pad = 0 */ width=0;\n      pad = (-width) & 3;\n      if (info.bpp == 24) {\n         easy = 1;\n      } else if (info.bpp == 32) {\n         if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)\n            easy = 2;\n      }\n      if (!easy) {\n         if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc(\"bad masks\", \"Corrupt BMP\"); }\n         // right shift amt to put high bit in position #7\n         rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr);\n         gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg);\n         bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb);\n         ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma);\n         if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc(\"bad masks\", \"Corrupt BMP\"); }\n      }\n      for (j=0; j < (int) s->img_y; ++j) {\n         if (easy) {\n            for (i=0; i < (int) s->img_x; ++i) {\n               unsigned char a;\n               out[z+2] = stbi__get8(s);\n               out[z+1] = stbi__get8(s);\n               out[z+0] = stbi__get8(s);\n               z += 3;\n               a = (easy == 2 ? stbi__get8(s) : 255);\n               all_a |= a;\n               if (target == 4) out[z++] = a;\n            }\n         } else {\n            int bpp = info.bpp;\n            for (i=0; i < (int) s->img_x; ++i) {\n               stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s));\n               unsigned int a;\n               out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount));\n               out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));\n               out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));\n               a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);\n               all_a |= a;\n               if (target == 4) out[z++] = STBI__BYTECAST(a);\n            }\n         }\n         stbi__skip(s, pad);\n      }\n   }\n\n   // if alpha channel is all 0s, replace with all 255s\n   if (target == 4 && all_a == 0)\n      for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4)\n         out[i] = 255;\n\n   if (flip_vertically) {\n      stbi_uc t;\n      for (j=0; j < (int) s->img_y>>1; ++j) {\n         stbi_uc *p1 = out +      j     *s->img_x*target;\n         stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target;\n         for (i=0; i < (int) s->img_x*target; ++i) {\n            t = p1[i]; p1[i] = p2[i]; p2[i] = t;\n         }\n      }\n   }\n\n   if (req_comp && req_comp != target) {\n      out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y);\n      if (out == NULL) return out; // stbi__convert_format frees input on failure\n   }\n\n   *x = s->img_x;\n   *y = s->img_y;\n   if (comp) *comp = s->img_n;\n   return out;\n}\n#endif\n\n// Targa Truevision - TGA\n// by Jonathan Dummer\n#ifndef STBI_NO_TGA\n// returns STBI_rgb or whatever, 0 on error\nstatic int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16)\n{\n   // only RGB or RGBA (incl. 16bit) or grey allowed\n   if (is_rgb16) *is_rgb16 = 0;\n   switch(bits_per_pixel) {\n      case 8:  return STBI_grey;\n      case 16: if(is_grey) return STBI_grey_alpha;\n               // fallthrough\n      case 15: if(is_rgb16) *is_rgb16 = 1;\n               return STBI_rgb;\n      case 24: // fallthrough\n      case 32: return bits_per_pixel/8;\n      default: return 0;\n   }\n}\n\nstatic int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp)\n{\n    int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp;\n    int sz, tga_colormap_type;\n    stbi__get8(s);                   // discard Offset\n    tga_colormap_type = stbi__get8(s); // colormap type\n    if( tga_colormap_type > 1 ) {\n        stbi__rewind(s);\n        return 0;      // only RGB or indexed allowed\n    }\n    tga_image_type = stbi__get8(s); // image type\n    if ( tga_colormap_type == 1 ) { // colormapped (paletted) image\n        if (tga_image_type != 1 && tga_image_type != 9) {\n            stbi__rewind(s);\n            return 0;\n        }\n        stbi__skip(s,4);       // skip index of first colormap entry and number of entries\n        sz = stbi__get8(s);    //   check bits per palette color entry\n        if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) {\n            stbi__rewind(s);\n            return 0;\n        }\n        stbi__skip(s,4);       // skip image x and y origin\n        tga_colormap_bpp = sz;\n    } else { // \"normal\" image w/o colormap - only RGB or grey allowed, +/- RLE\n        if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) {\n            stbi__rewind(s);\n            return 0; // only RGB or grey allowed, +/- RLE\n        }\n        stbi__skip(s,9); // skip colormap specification and image x/y origin\n        tga_colormap_bpp = 0;\n    }\n    tga_w = stbi__get16le(s);\n    if( tga_w < 1 ) {\n        stbi__rewind(s);\n        return 0;   // test width\n    }\n    tga_h = stbi__get16le(s);\n    if( tga_h < 1 ) {\n        stbi__rewind(s);\n        return 0;   // test height\n    }\n    tga_bits_per_pixel = stbi__get8(s); // bits per pixel\n    stbi__get8(s); // ignore alpha bits\n    if (tga_colormap_bpp != 0) {\n        if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) {\n            // when using a colormap, tga_bits_per_pixel is the size of the indexes\n            // I don't think anything but 8 or 16bit indexes makes sense\n            stbi__rewind(s);\n            return 0;\n        }\n        tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL);\n    } else {\n        tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL);\n    }\n    if(!tga_comp) {\n      stbi__rewind(s);\n      return 0;\n    }\n    if (x) *x = tga_w;\n    if (y) *y = tga_h;\n    if (comp) *comp = tga_comp;\n    return 1;                   // seems to have passed everything\n}\n\nstatic int stbi__tga_test(stbi__context *s)\n{\n   int res = 0;\n   int sz, tga_color_type;\n   stbi__get8(s);      //   discard Offset\n   tga_color_type = stbi__get8(s);   //   color type\n   if ( tga_color_type > 1 ) goto errorEnd;   //   only RGB or indexed allowed\n   sz = stbi__get8(s);   //   image type\n   if ( tga_color_type == 1 ) { // colormapped (paletted) image\n      if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9\n      stbi__skip(s,4);       // skip index of first colormap entry and number of entries\n      sz = stbi__get8(s);    //   check bits per palette color entry\n      if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;\n      stbi__skip(s,4);       // skip image x and y origin\n   } else { // \"normal\" image w/o colormap\n      if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE\n      stbi__skip(s,9); // skip colormap specification and image x/y origin\n   }\n   if ( stbi__get16le(s) < 1 ) goto errorEnd;      //   test width\n   if ( stbi__get16le(s) < 1 ) goto errorEnd;      //   test height\n   sz = stbi__get8(s);   //   bits per pixel\n   if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index\n   if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;\n\n   res = 1; // if we got this far, everything's good and we can return 1 instead of 0\n\nerrorEnd:\n   stbi__rewind(s);\n   return res;\n}\n\n// read 16bit value and convert to 24bit RGB\nstatic void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out)\n{\n   stbi__uint16 px = (stbi__uint16)stbi__get16le(s);\n   stbi__uint16 fiveBitMask = 31;\n   // we have 3 channels with 5bits each\n   int r = (px >> 10) & fiveBitMask;\n   int g = (px >> 5) & fiveBitMask;\n   int b = px & fiveBitMask;\n   // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later\n   out[0] = (stbi_uc)((r * 255)/31);\n   out[1] = (stbi_uc)((g * 255)/31);\n   out[2] = (stbi_uc)((b * 255)/31);\n\n   // some people claim that the most significant bit might be used for alpha\n   // (possibly if an alpha-bit is set in the \"image descriptor byte\")\n   // but that only made 16bit test images completely translucent..\n   // so let's treat all 15 and 16bit TGAs as RGB with no alpha.\n}\n\nstatic void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)\n{\n   //   read in the TGA header stuff\n   int tga_offset = stbi__get8(s);\n   int tga_indexed = stbi__get8(s);\n   int tga_image_type = stbi__get8(s);\n   int tga_is_RLE = 0;\n   int tga_palette_start = stbi__get16le(s);\n   int tga_palette_len = stbi__get16le(s);\n   int tga_palette_bits = stbi__get8(s);\n   int tga_x_origin = stbi__get16le(s);\n   int tga_y_origin = stbi__get16le(s);\n   int tga_width = stbi__get16le(s);\n   int tga_height = stbi__get16le(s);\n   int tga_bits_per_pixel = stbi__get8(s);\n   int tga_comp, tga_rgb16=0;\n   int tga_inverted = stbi__get8(s);\n   // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?)\n   //   image data\n   unsigned char *tga_data;\n   unsigned char *tga_palette = NULL;\n   int i, j;\n   unsigned char raw_data[4] = {0};\n   int RLE_count = 0;\n   int RLE_repeating = 0;\n   int read_next_pixel = 1;\n   STBI_NOTUSED(ri);\n   STBI_NOTUSED(tga_x_origin); // @TODO\n   STBI_NOTUSED(tga_y_origin); // @TODO\n\n   if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc(\"too large\",\"Very large image (corrupt?)\");\n   if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc(\"too large\",\"Very large image (corrupt?)\");\n\n   //   do a tiny bit of precessing\n   if ( tga_image_type >= 8 )\n   {\n      tga_image_type -= 8;\n      tga_is_RLE = 1;\n   }\n   tga_inverted = 1 - ((tga_inverted >> 5) & 1);\n\n   //   If I'm paletted, then I'll use the number of bits from the palette\n   if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16);\n   else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16);\n\n   if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency\n      return stbi__errpuc(\"bad format\", \"Can't find out TGA pixelformat\");\n\n   //   tga info\n   *x = tga_width;\n   *y = tga_height;\n   if (comp) *comp = tga_comp;\n\n   if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0))\n      return stbi__errpuc(\"too large\", \"Corrupt TGA\");\n\n   tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0);\n   if (!tga_data) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n\n   // skip to the data's starting position (offset usually = 0)\n   stbi__skip(s, tga_offset );\n\n   if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) {\n      for (i=0; i < tga_height; ++i) {\n         int row = tga_inverted ? tga_height -i - 1 : i;\n         stbi_uc *tga_row = tga_data + row*tga_width*tga_comp;\n         stbi__getn(s, tga_row, tga_width * tga_comp);\n      }\n   } else  {\n      //   do I need to load a palette?\n      if ( tga_indexed)\n      {\n         if (tga_palette_len == 0) {  /* you have to have at least one entry! */\n            STBI_FREE(tga_data);\n            return stbi__errpuc(\"bad palette\", \"Corrupt TGA\");\n         }\n\n         //   any data to skip? (offset usually = 0)\n         stbi__skip(s, tga_palette_start );\n         //   load the palette\n         tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0);\n         if (!tga_palette) {\n            STBI_FREE(tga_data);\n            return stbi__errpuc(\"outofmem\", \"Out of memory\");\n         }\n         if (tga_rgb16) {\n            stbi_uc *pal_entry = tga_palette;\n            STBI_ASSERT(tga_comp == STBI_rgb);\n            for (i=0; i < tga_palette_len; ++i) {\n               stbi__tga_read_rgb16(s, pal_entry);\n               pal_entry += tga_comp;\n            }\n         } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) {\n               STBI_FREE(tga_data);\n               STBI_FREE(tga_palette);\n               return stbi__errpuc(\"bad palette\", \"Corrupt TGA\");\n         }\n      }\n      //   load the data\n      for (i=0; i < tga_width * tga_height; ++i)\n      {\n         //   if I'm in RLE mode, do I need to get a RLE stbi__pngchunk?\n         if ( tga_is_RLE )\n         {\n            if ( RLE_count == 0 )\n            {\n               //   yep, get the next byte as a RLE command\n               int RLE_cmd = stbi__get8(s);\n               RLE_count = 1 + (RLE_cmd & 127);\n               RLE_repeating = RLE_cmd >> 7;\n               read_next_pixel = 1;\n            } else if ( !RLE_repeating )\n            {\n               read_next_pixel = 1;\n            }\n         } else\n         {\n            read_next_pixel = 1;\n         }\n         //   OK, if I need to read a pixel, do it now\n         if ( read_next_pixel )\n         {\n            //   load however much data we did have\n            if ( tga_indexed )\n            {\n               // read in index, then perform the lookup\n               int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s);\n               if ( pal_idx >= tga_palette_len ) {\n                  // invalid index\n                  pal_idx = 0;\n               }\n               pal_idx *= tga_comp;\n               for (j = 0; j < tga_comp; ++j) {\n                  raw_data[j] = tga_palette[pal_idx+j];\n               }\n            } else if(tga_rgb16) {\n               STBI_ASSERT(tga_comp == STBI_rgb);\n               stbi__tga_read_rgb16(s, raw_data);\n            } else {\n               //   read in the data raw\n               for (j = 0; j < tga_comp; ++j) {\n                  raw_data[j] = stbi__get8(s);\n               }\n            }\n            //   clear the reading flag for the next pixel\n            read_next_pixel = 0;\n         } // end of reading a pixel\n\n         // copy data\n         for (j = 0; j < tga_comp; ++j)\n           tga_data[i*tga_comp+j] = raw_data[j];\n\n         //   in case we're in RLE mode, keep counting down\n         --RLE_count;\n      }\n      //   do I need to invert the image?\n      if ( tga_inverted )\n      {\n         for (j = 0; j*2 < tga_height; ++j)\n         {\n            int index1 = j * tga_width * tga_comp;\n            int index2 = (tga_height - 1 - j) * tga_width * tga_comp;\n            for (i = tga_width * tga_comp; i > 0; --i)\n            {\n               unsigned char temp = tga_data[index1];\n               tga_data[index1] = tga_data[index2];\n               tga_data[index2] = temp;\n               ++index1;\n               ++index2;\n            }\n         }\n      }\n      //   clear my palette, if I had one\n      if ( tga_palette != NULL )\n      {\n         STBI_FREE( tga_palette );\n      }\n   }\n\n   // swap RGB - if the source data was RGB16, it already is in the right order\n   if (tga_comp >= 3 && !tga_rgb16)\n   {\n      unsigned char* tga_pixel = tga_data;\n      for (i=0; i < tga_width * tga_height; ++i)\n      {\n         unsigned char temp = tga_pixel[0];\n         tga_pixel[0] = tga_pixel[2];\n         tga_pixel[2] = temp;\n         tga_pixel += tga_comp;\n      }\n   }\n\n   // convert to target component count\n   if (req_comp && req_comp != tga_comp)\n      tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);\n\n   //   the things I do to get rid of an error message, and yet keep\n   //   Microsoft's C compilers happy... [8^(\n   tga_palette_start = tga_palette_len = tga_palette_bits =\n         tga_x_origin = tga_y_origin = 0;\n   STBI_NOTUSED(tga_palette_start);\n   //   OK, done\n   return tga_data;\n}\n#endif\n\n// *************************************************************************************************\n// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB\n\n#ifndef STBI_NO_PSD\nstatic int stbi__psd_test(stbi__context *s)\n{\n   int r = (stbi__get32be(s) == 0x38425053);\n   stbi__rewind(s);\n   return r;\n}\n\nstatic int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount)\n{\n   int count, nleft, len;\n\n   count = 0;\n   while ((nleft = pixelCount - count) > 0) {\n      len = stbi__get8(s);\n      if (len == 128) {\n         // No-op.\n      } else if (len < 128) {\n         // Copy next len+1 bytes literally.\n         len++;\n         if (len > nleft) return 0; // corrupt data\n         count += len;\n         while (len) {\n            *p = stbi__get8(s);\n            p += 4;\n            len--;\n         }\n      } else if (len > 128) {\n         stbi_uc   val;\n         // Next -len+1 bytes in the dest are replicated from next source byte.\n         // (Interpret len as a negative 8-bit int.)\n         len = 257 - len;\n         if (len > nleft) return 0; // corrupt data\n         val = stbi__get8(s);\n         count += len;\n         while (len) {\n            *p = val;\n            p += 4;\n            len--;\n         }\n      }\n   }\n\n   return 1;\n}\n\nstatic void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)\n{\n   int pixelCount;\n   int channelCount, compression;\n   int channel, i;\n   int bitdepth;\n   int w,h;\n   stbi_uc *out;\n   STBI_NOTUSED(ri);\n\n   // Check identifier\n   if (stbi__get32be(s) != 0x38425053)   // \"8BPS\"\n      return stbi__errpuc(\"not PSD\", \"Corrupt PSD image\");\n\n   // Check file type version.\n   if (stbi__get16be(s) != 1)\n      return stbi__errpuc(\"wrong version\", \"Unsupported version of PSD image\");\n\n   // Skip 6 reserved bytes.\n   stbi__skip(s, 6 );\n\n   // Read the number of channels (R, G, B, A, etc).\n   channelCount = stbi__get16be(s);\n   if (channelCount < 0 || channelCount > 16)\n      return stbi__errpuc(\"wrong channel count\", \"Unsupported number of channels in PSD image\");\n\n   // Read the rows and columns of the image.\n   h = stbi__get32be(s);\n   w = stbi__get32be(s);\n\n   if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc(\"too large\",\"Very large image (corrupt?)\");\n   if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc(\"too large\",\"Very large image (corrupt?)\");\n\n   // Make sure the depth is 8 bits.\n   bitdepth = stbi__get16be(s);\n   if (bitdepth != 8 && bitdepth != 16)\n      return stbi__errpuc(\"unsupported bit depth\", \"PSD bit depth is not 8 or 16 bit\");\n\n   // Make sure the color mode is RGB.\n   // Valid options are:\n   //   0: Bitmap\n   //   1: Grayscale\n   //   2: Indexed color\n   //   3: RGB color\n   //   4: CMYK color\n   //   7: Multichannel\n   //   8: Duotone\n   //   9: Lab color\n   if (stbi__get16be(s) != 3)\n      return stbi__errpuc(\"wrong color format\", \"PSD is not in RGB color format\");\n\n   // Skip the Mode Data.  (It's the palette for indexed color; other info for other modes.)\n   stbi__skip(s,stbi__get32be(s) );\n\n   // Skip the image resources.  (resolution, pen tool paths, etc)\n   stbi__skip(s, stbi__get32be(s) );\n\n   // Skip the reserved data.\n   stbi__skip(s, stbi__get32be(s) );\n\n   // Find out if the data is compressed.\n   // Known values:\n   //   0: no compression\n   //   1: RLE compressed\n   compression = stbi__get16be(s);\n   if (compression > 1)\n      return stbi__errpuc(\"bad compression\", \"PSD has an unknown compression format\");\n\n   // Check size\n   if (!stbi__mad3sizes_valid(4, w, h, 0))\n      return stbi__errpuc(\"too large\", \"Corrupt PSD\");\n\n   // Create the destination image.\n\n   if (!compression && bitdepth == 16 && bpc == 16) {\n      out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0);\n      ri->bits_per_channel = 16;\n   } else\n      out = (stbi_uc *) stbi__malloc(4 * w*h);\n\n   if (!out) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n   pixelCount = w*h;\n\n   // Initialize the data to zero.\n   //memset( out, 0, pixelCount * 4 );\n\n   // Finally, the image data.\n   if (compression) {\n      // RLE as used by .PSD and .TIFF\n      // Loop until you get the number of unpacked bytes you are expecting:\n      //     Read the next source byte into n.\n      //     If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.\n      //     Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.\n      //     Else if n is 128, noop.\n      // Endloop\n\n      // The RLE-compressed data is preceded by a 2-byte data count for each row in the data,\n      // which we're going to just skip.\n      stbi__skip(s, h * channelCount * 2 );\n\n      // Read the RLE data by channel.\n      for (channel = 0; channel < 4; channel++) {\n         stbi_uc *p;\n\n         p = out+channel;\n         if (channel >= channelCount) {\n            // Fill this channel with default data.\n            for (i = 0; i < pixelCount; i++, p += 4)\n               *p = (channel == 3 ? 255 : 0);\n         } else {\n            // Read the RLE data.\n            if (!stbi__psd_decode_rle(s, p, pixelCount)) {\n               STBI_FREE(out);\n               return stbi__errpuc(\"corrupt\", \"bad RLE data\");\n            }\n         }\n      }\n\n   } else {\n      // We're at the raw image data.  It's each channel in order (Red, Green, Blue, Alpha, ...)\n      // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image.\n\n      // Read the data by channel.\n      for (channel = 0; channel < 4; channel++) {\n         if (channel >= channelCount) {\n            // Fill this channel with default data.\n            if (bitdepth == 16 && bpc == 16) {\n               stbi__uint16 *q = ((stbi__uint16 *) out) + channel;\n               stbi__uint16 val = channel == 3 ? 65535 : 0;\n               for (i = 0; i < pixelCount; i++, q += 4)\n                  *q = val;\n            } else {\n               stbi_uc *p = out+channel;\n               stbi_uc val = channel == 3 ? 255 : 0;\n               for (i = 0; i < pixelCount; i++, p += 4)\n                  *p = val;\n            }\n         } else {\n            if (ri->bits_per_channel == 16) {    // output bpc\n               stbi__uint16 *q = ((stbi__uint16 *) out) + channel;\n               for (i = 0; i < pixelCount; i++, q += 4)\n                  *q = (stbi__uint16) stbi__get16be(s);\n            } else {\n               stbi_uc *p = out+channel;\n               if (bitdepth == 16) {  // input bpc\n                  for (i = 0; i < pixelCount; i++, p += 4)\n                     *p = (stbi_uc) (stbi__get16be(s) >> 8);\n               } else {\n                  for (i = 0; i < pixelCount; i++, p += 4)\n                     *p = stbi__get8(s);\n               }\n            }\n         }\n      }\n   }\n\n   // remove weird white matte from PSD\n   if (channelCount >= 4) {\n      if (ri->bits_per_channel == 16) {\n         for (i=0; i < w*h; ++i) {\n            stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i;\n            if (pixel[3] != 0 && pixel[3] != 65535) {\n               float a = pixel[3] / 65535.0f;\n               float ra = 1.0f / a;\n               float inv_a = 65535.0f * (1 - ra);\n               pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a);\n               pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a);\n               pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a);\n            }\n         }\n      } else {\n         for (i=0; i < w*h; ++i) {\n            unsigned char *pixel = out + 4*i;\n            if (pixel[3] != 0 && pixel[3] != 255) {\n               float a = pixel[3] / 255.0f;\n               float ra = 1.0f / a;\n               float inv_a = 255.0f * (1 - ra);\n               pixel[0] = (unsigned char) (pixel[0]*ra + inv_a);\n               pixel[1] = (unsigned char) (pixel[1]*ra + inv_a);\n               pixel[2] = (unsigned char) (pixel[2]*ra + inv_a);\n            }\n         }\n      }\n   }\n\n   // convert to desired output format\n   if (req_comp && req_comp != 4) {\n      if (ri->bits_per_channel == 16)\n         out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h);\n      else\n         out = stbi__convert_format(out, 4, req_comp, w, h);\n      if (out == NULL) return out; // stbi__convert_format frees input on failure\n   }\n\n   if (comp) *comp = 4;\n   *y = h;\n   *x = w;\n\n   return out;\n}\n#endif\n\n// *************************************************************************************************\n// Softimage PIC loader\n// by Tom Seddon\n//\n// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format\n// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/\n\n#ifndef STBI_NO_PIC\nstatic int stbi__pic_is4(stbi__context *s,const char *str)\n{\n   int i;\n   for (i=0; i<4; ++i)\n      if (stbi__get8(s) != (stbi_uc)str[i])\n         return 0;\n\n   return 1;\n}\n\nstatic int stbi__pic_test_core(stbi__context *s)\n{\n   int i;\n\n   if (!stbi__pic_is4(s,\"\\x53\\x80\\xF6\\x34\"))\n      return 0;\n\n   for(i=0;i<84;++i)\n      stbi__get8(s);\n\n   if (!stbi__pic_is4(s,\"PICT\"))\n      return 0;\n\n   return 1;\n}\n\ntypedef struct\n{\n   stbi_uc size,type,channel;\n} stbi__pic_packet;\n\nstatic stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest)\n{\n   int mask=0x80, i;\n\n   for (i=0; i<4; ++i, mask>>=1) {\n      if (channel & mask) {\n         if (stbi__at_eof(s)) return stbi__errpuc(\"bad file\",\"PIC file too short\");\n         dest[i]=stbi__get8(s);\n      }\n   }\n\n   return dest;\n}\n\nstatic void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src)\n{\n   int mask=0x80,i;\n\n   for (i=0;i<4; ++i, mask>>=1)\n      if (channel&mask)\n         dest[i]=src[i];\n}\n\nstatic stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result)\n{\n   int act_comp=0,num_packets=0,y,chained;\n   stbi__pic_packet packets[10];\n\n   // this will (should...) cater for even some bizarre stuff like having data\n    // for the same channel in multiple packets.\n   do {\n      stbi__pic_packet *packet;\n\n      if (num_packets==sizeof(packets)/sizeof(packets[0]))\n         return stbi__errpuc(\"bad format\",\"too many packets\");\n\n      packet = &packets[num_packets++];\n\n      chained = stbi__get8(s);\n      packet->size    = stbi__get8(s);\n      packet->type    = stbi__get8(s);\n      packet->channel = stbi__get8(s);\n\n      act_comp |= packet->channel;\n\n      if (stbi__at_eof(s))          return stbi__errpuc(\"bad file\",\"file too short (reading packets)\");\n      if (packet->size != 8)  return stbi__errpuc(\"bad format\",\"packet isn't 8bpp\");\n   } while (chained);\n\n   *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel?\n\n   for(y=0; y<height; ++y) {\n      int packet_idx;\n\n      for(packet_idx=0; packet_idx < num_packets; ++packet_idx) {\n         stbi__pic_packet *packet = &packets[packet_idx];\n         stbi_uc *dest = result+y*width*4;\n\n         switch (packet->type) {\n            default:\n               return stbi__errpuc(\"bad format\",\"packet has bad compression type\");\n\n            case 0: {//uncompressed\n               int x;\n\n               for(x=0;x<width;++x, dest+=4)\n                  if (!stbi__readval(s,packet->channel,dest))\n                     return 0;\n               break;\n            }\n\n            case 1://Pure RLE\n               {\n                  int left=width, i;\n\n                  while (left>0) {\n                     stbi_uc count,value[4];\n\n                     count=stbi__get8(s);\n                     if (stbi__at_eof(s))   return stbi__errpuc(\"bad file\",\"file too short (pure read count)\");\n\n                     if (count > left)\n                        count = (stbi_uc) left;\n\n                     if (!stbi__readval(s,packet->channel,value))  return 0;\n\n                     for(i=0; i<count; ++i,dest+=4)\n                        stbi__copyval(packet->channel,dest,value);\n                     left -= count;\n                  }\n               }\n               break;\n\n            case 2: {//Mixed RLE\n               int left=width;\n               while (left>0) {\n                  int count = stbi__get8(s), i;\n                  if (stbi__at_eof(s))  return stbi__errpuc(\"bad file\",\"file too short (mixed read count)\");\n\n                  if (count >= 128) { // Repeated\n                     stbi_uc value[4];\n\n                     if (count==128)\n                        count = stbi__get16be(s);\n                     else\n                        count -= 127;\n                     if (count > left)\n                        return stbi__errpuc(\"bad file\",\"scanline overrun\");\n\n                     if (!stbi__readval(s,packet->channel,value))\n                        return 0;\n\n                     for(i=0;i<count;++i, dest += 4)\n                        stbi__copyval(packet->channel,dest,value);\n                  } else { // Raw\n                     ++count;\n                     if (count>left) return stbi__errpuc(\"bad file\",\"scanline overrun\");\n\n                     for(i=0;i<count;++i, dest+=4)\n                        if (!stbi__readval(s,packet->channel,dest))\n                           return 0;\n                  }\n                  left-=count;\n               }\n               break;\n            }\n         }\n      }\n   }\n\n   return result;\n}\n\nstatic void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri)\n{\n   stbi_uc *result;\n   int i, x,y, internal_comp;\n   STBI_NOTUSED(ri);\n\n   if (!comp) comp = &internal_comp;\n\n   for (i=0; i<92; ++i)\n      stbi__get8(s);\n\n   x = stbi__get16be(s);\n   y = stbi__get16be(s);\n\n   if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc(\"too large\",\"Very large image (corrupt?)\");\n   if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc(\"too large\",\"Very large image (corrupt?)\");\n\n   if (stbi__at_eof(s))  return stbi__errpuc(\"bad file\",\"file too short (pic header)\");\n   if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc(\"too large\", \"PIC image too large to decode\");\n\n   stbi__get32be(s); //skip `ratio'\n   stbi__get16be(s); //skip `fields'\n   stbi__get16be(s); //skip `pad'\n\n   // intermediate buffer is RGBA\n   result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0);\n   if (!result) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n   memset(result, 0xff, x*y*4);\n\n   if (!stbi__pic_load_core(s,x,y,comp, result)) {\n      STBI_FREE(result);\n      result=0;\n   }\n   *px = x;\n   *py = y;\n   if (req_comp == 0) req_comp = *comp;\n   result=stbi__convert_format(result,4,req_comp,x,y);\n\n   return result;\n}\n\nstatic int stbi__pic_test(stbi__context *s)\n{\n   int r = stbi__pic_test_core(s);\n   stbi__rewind(s);\n   return r;\n}\n#endif\n\n// *************************************************************************************************\n// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb\n\n#ifndef STBI_NO_GIF\ntypedef struct\n{\n   stbi__int16 prefix;\n   stbi_uc first;\n   stbi_uc suffix;\n} stbi__gif_lzw;\n\ntypedef struct\n{\n   int w,h;\n   stbi_uc *out;                 // output buffer (always 4 components)\n   stbi_uc *background;          // The current \"background\" as far as a gif is concerned\n   stbi_uc *history;\n   int flags, bgindex, ratio, transparent, eflags;\n   stbi_uc  pal[256][4];\n   stbi_uc lpal[256][4];\n   stbi__gif_lzw codes[8192];\n   stbi_uc *color_table;\n   int parse, step;\n   int lflags;\n   int start_x, start_y;\n   int max_x, max_y;\n   int cur_x, cur_y;\n   int line_size;\n   int delay;\n} stbi__gif;\n\nstatic int stbi__gif_test_raw(stbi__context *s)\n{\n   int sz;\n   if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0;\n   sz = stbi__get8(s);\n   if (sz != '9' && sz != '7') return 0;\n   if (stbi__get8(s) != 'a') return 0;\n   return 1;\n}\n\nstatic int stbi__gif_test(stbi__context *s)\n{\n   int r = stbi__gif_test_raw(s);\n   stbi__rewind(s);\n   return r;\n}\n\nstatic void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp)\n{\n   int i;\n   for (i=0; i < num_entries; ++i) {\n      pal[i][2] = stbi__get8(s);\n      pal[i][1] = stbi__get8(s);\n      pal[i][0] = stbi__get8(s);\n      pal[i][3] = transp == i ? 0 : 255;\n   }\n}\n\nstatic int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info)\n{\n   stbi_uc version;\n   if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8')\n      return stbi__err(\"not GIF\", \"Corrupt GIF\");\n\n   version = stbi__get8(s);\n   if (version != '7' && version != '9')    return stbi__err(\"not GIF\", \"Corrupt GIF\");\n   if (stbi__get8(s) != 'a')                return stbi__err(\"not GIF\", \"Corrupt GIF\");\n\n   stbi__g_failure_reason = \"\";\n   g->w = stbi__get16le(s);\n   g->h = stbi__get16le(s);\n   g->flags = stbi__get8(s);\n   g->bgindex = stbi__get8(s);\n   g->ratio = stbi__get8(s);\n   g->transparent = -1;\n\n   if (g->w > STBI_MAX_DIMENSIONS) return stbi__err(\"too large\",\"Very large image (corrupt?)\");\n   if (g->h > STBI_MAX_DIMENSIONS) return stbi__err(\"too large\",\"Very large image (corrupt?)\");\n\n   if (comp != 0) *comp = 4;  // can't actually tell whether it's 3 or 4 until we parse the comments\n\n   if (is_info) return 1;\n\n   if (g->flags & 0x80)\n      stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1);\n\n   return 1;\n}\n\nstatic int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)\n{\n   stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));\n   if (!g) return stbi__err(\"outofmem\", \"Out of memory\");\n   if (!stbi__gif_header(s, g, comp, 1)) {\n      STBI_FREE(g);\n      stbi__rewind( s );\n      return 0;\n   }\n   if (x) *x = g->w;\n   if (y) *y = g->h;\n   STBI_FREE(g);\n   return 1;\n}\n\nstatic void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)\n{\n   stbi_uc *p, *c;\n   int idx;\n\n   // recurse to decode the prefixes, since the linked-list is backwards,\n   // and working backwards through an interleaved image would be nasty\n   if (g->codes[code].prefix >= 0)\n      stbi__out_gif_code(g, g->codes[code].prefix);\n\n   if (g->cur_y >= g->max_y) return;\n\n   idx = g->cur_x + g->cur_y;\n   p = &g->out[idx];\n   g->history[idx / 4] = 1;\n\n   c = &g->color_table[g->codes[code].suffix * 4];\n   if (c[3] > 128) { // don't render transparent pixels;\n      p[0] = c[2];\n      p[1] = c[1];\n      p[2] = c[0];\n      p[3] = c[3];\n   }\n   g->cur_x += 4;\n\n   if (g->cur_x >= g->max_x) {\n      g->cur_x = g->start_x;\n      g->cur_y += g->step;\n\n      while (g->cur_y >= g->max_y && g->parse > 0) {\n         g->step = (1 << g->parse) * g->line_size;\n         g->cur_y = g->start_y + (g->step >> 1);\n         --g->parse;\n      }\n   }\n}\n\nstatic stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)\n{\n   stbi_uc lzw_cs;\n   stbi__int32 len, init_code;\n   stbi__uint32 first;\n   stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;\n   stbi__gif_lzw *p;\n\n   lzw_cs = stbi__get8(s);\n   if (lzw_cs > 12) return NULL;\n   clear = 1 << lzw_cs;\n   first = 1;\n   codesize = lzw_cs + 1;\n   codemask = (1 << codesize) - 1;\n   bits = 0;\n   valid_bits = 0;\n   for (init_code = 0; init_code < clear; init_code++) {\n      g->codes[init_code].prefix = -1;\n      g->codes[init_code].first = (stbi_uc) init_code;\n      g->codes[init_code].suffix = (stbi_uc) init_code;\n   }\n\n   // support no starting clear code\n   avail = clear+2;\n   oldcode = -1;\n\n   len = 0;\n   for(;;) {\n      if (valid_bits < codesize) {\n         if (len == 0) {\n            len = stbi__get8(s); // start new block\n            if (len == 0)\n               return g->out;\n         }\n         --len;\n         bits |= (stbi__int32) stbi__get8(s) << valid_bits;\n         valid_bits += 8;\n      } else {\n         stbi__int32 code = bits & codemask;\n         bits >>= codesize;\n         valid_bits -= codesize;\n         // @OPTIMIZE: is there some way we can accelerate the non-clear path?\n         if (code == clear) {  // clear code\n            codesize = lzw_cs + 1;\n            codemask = (1 << codesize) - 1;\n            avail = clear + 2;\n            oldcode = -1;\n            first = 0;\n         } else if (code == clear + 1) { // end of stream code\n            stbi__skip(s, len);\n            while ((len = stbi__get8(s)) > 0)\n               stbi__skip(s,len);\n            return g->out;\n         } else if (code <= avail) {\n            if (first) {\n               return stbi__errpuc(\"no clear code\", \"Corrupt GIF\");\n            }\n\n            if (oldcode >= 0) {\n               p = &g->codes[avail++];\n               if (avail > 8192) {\n                  return stbi__errpuc(\"too many codes\", \"Corrupt GIF\");\n               }\n\n               p->prefix = (stbi__int16) oldcode;\n               p->first = g->codes[oldcode].first;\n               p->suffix = (code == avail) ? p->first : g->codes[code].first;\n            } else if (code == avail)\n               return stbi__errpuc(\"illegal code in raster\", \"Corrupt GIF\");\n\n            stbi__out_gif_code(g, (stbi__uint16) code);\n\n            if ((avail & codemask) == 0 && avail <= 0x0FFF) {\n               codesize++;\n               codemask = (1 << codesize) - 1;\n            }\n\n            oldcode = code;\n         } else {\n            return stbi__errpuc(\"illegal code in raster\", \"Corrupt GIF\");\n         }\n      }\n   }\n}\n\n// this function is designed to support animated gifs, although stb_image doesn't support it\n// two back is the image from two frames ago, used for a very specific disposal format\nstatic stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back)\n{\n   int dispose;\n   int first_frame;\n   int pi;\n   int pcount;\n   STBI_NOTUSED(req_comp);\n\n   // on first frame, any non-written pixels get the background colour (non-transparent)\n   first_frame = 0;\n   if (g->out == 0) {\n      if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header\n      if (!stbi__mad3sizes_valid(4, g->w, g->h, 0))\n         return stbi__errpuc(\"too large\", \"GIF image is too large\");\n      pcount = g->w * g->h;\n      g->out = (stbi_uc *) stbi__malloc(4 * pcount);\n      g->background = (stbi_uc *) stbi__malloc(4 * pcount);\n      g->history = (stbi_uc *) stbi__malloc(pcount);\n      if (!g->out || !g->background || !g->history)\n         return stbi__errpuc(\"outofmem\", \"Out of memory\");\n\n      // image is treated as \"transparent\" at the start - ie, nothing overwrites the current background;\n      // background colour is only used for pixels that are not rendered first frame, after that \"background\"\n      // color refers to the color that was there the previous frame.\n      memset(g->out, 0x00, 4 * pcount);\n      memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent)\n      memset(g->history, 0x00, pcount);        // pixels that were affected previous frame\n      first_frame = 1;\n   } else {\n      // second frame - how do we dispose of the previous one?\n      dispose = (g->eflags & 0x1C) >> 2;\n      pcount = g->w * g->h;\n\n      if ((dispose == 3) && (two_back == 0)) {\n         dispose = 2; // if I don't have an image to revert back to, default to the old background\n      }\n\n      if (dispose == 3) { // use previous graphic\n         for (pi = 0; pi < pcount; ++pi) {\n            if (g->history[pi]) {\n               memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 );\n            }\n         }\n      } else if (dispose == 2) {\n         // restore what was changed last frame to background before that frame;\n         for (pi = 0; pi < pcount; ++pi) {\n            if (g->history[pi]) {\n               memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 );\n            }\n         }\n      } else {\n         // This is a non-disposal case eithe way, so just\n         // leave the pixels as is, and they will become the new background\n         // 1: do not dispose\n         // 0:  not specified.\n      }\n\n      // background is what out is after the undoing of the previou frame;\n      memcpy( g->background, g->out, 4 * g->w * g->h );\n   }\n\n   // clear my history;\n   memset( g->history, 0x00, g->w * g->h );        // pixels that were affected previous frame\n\n   for (;;) {\n      int tag = stbi__get8(s);\n      switch (tag) {\n         case 0x2C: /* Image Descriptor */\n         {\n            stbi__int32 x, y, w, h;\n            stbi_uc *o;\n\n            x = stbi__get16le(s);\n            y = stbi__get16le(s);\n            w = stbi__get16le(s);\n            h = stbi__get16le(s);\n            if (((x + w) > (g->w)) || ((y + h) > (g->h)))\n               return stbi__errpuc(\"bad Image Descriptor\", \"Corrupt GIF\");\n\n            g->line_size = g->w * 4;\n            g->start_x = x * 4;\n            g->start_y = y * g->line_size;\n            g->max_x   = g->start_x + w * 4;\n            g->max_y   = g->start_y + h * g->line_size;\n            g->cur_x   = g->start_x;\n            g->cur_y   = g->start_y;\n\n            // if the width of the specified rectangle is 0, that means\n            // we may not see *any* pixels or the image is malformed;\n            // to make sure this is caught, move the current y down to\n            // max_y (which is what out_gif_code checks).\n            if (w == 0)\n               g->cur_y = g->max_y;\n\n            g->lflags = stbi__get8(s);\n\n            if (g->lflags & 0x40) {\n               g->step = 8 * g->line_size; // first interlaced spacing\n               g->parse = 3;\n            } else {\n               g->step = g->line_size;\n               g->parse = 0;\n            }\n\n            if (g->lflags & 0x80) {\n               stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);\n               g->color_table = (stbi_uc *) g->lpal;\n            } else if (g->flags & 0x80) {\n               g->color_table = (stbi_uc *) g->pal;\n            } else\n               return stbi__errpuc(\"missing color table\", \"Corrupt GIF\");\n\n            o = stbi__process_gif_raster(s, g);\n            if (!o) return NULL;\n\n            // if this was the first frame,\n            pcount = g->w * g->h;\n            if (first_frame && (g->bgindex > 0)) {\n               // if first frame, any pixel not drawn to gets the background color\n               for (pi = 0; pi < pcount; ++pi) {\n                  if (g->history[pi] == 0) {\n                     g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be;\n                     memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 );\n                  }\n               }\n            }\n\n            return o;\n         }\n\n         case 0x21: // Comment Extension.\n         {\n            int len;\n            int ext = stbi__get8(s);\n            if (ext == 0xF9) { // Graphic Control Extension.\n               len = stbi__get8(s);\n               if (len == 4) {\n                  g->eflags = stbi__get8(s);\n                  g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths.\n\n                  // unset old transparent\n                  if (g->transparent >= 0) {\n                     g->pal[g->transparent][3] = 255;\n                  }\n                  if (g->eflags & 0x01) {\n                     g->transparent = stbi__get8(s);\n                     if (g->transparent >= 0) {\n                        g->pal[g->transparent][3] = 0;\n                     }\n                  } else {\n                     // don't need transparent\n                     stbi__skip(s, 1);\n                     g->transparent = -1;\n                  }\n               } else {\n                  stbi__skip(s, len);\n                  break;\n               }\n            }\n            while ((len = stbi__get8(s)) != 0) {\n               stbi__skip(s, len);\n            }\n            break;\n         }\n\n         case 0x3B: // gif stream termination code\n            return (stbi_uc *) s; // using '1' causes warning on some compilers\n\n         default:\n            return stbi__errpuc(\"unknown code\", \"Corrupt GIF\");\n      }\n   }\n}\n\nstatic void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays)\n{\n   STBI_FREE(g->out);\n   STBI_FREE(g->history);\n   STBI_FREE(g->background);\n\n   if (out) STBI_FREE(out);\n   if (delays && *delays) STBI_FREE(*delays);\n   return stbi__errpuc(\"outofmem\", \"Out of memory\");\n}\n\nstatic void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)\n{\n   if (stbi__gif_test(s)) {\n      int layers = 0;\n      stbi_uc *u = 0;\n      stbi_uc *out = 0;\n      stbi_uc *two_back = 0;\n      stbi__gif g;\n      int stride;\n      int out_size = 0;\n      int delays_size = 0;\n\n      STBI_NOTUSED(out_size);\n      STBI_NOTUSED(delays_size);\n\n      memset(&g, 0, sizeof(g));\n      if (delays) {\n         *delays = 0;\n      }\n\n      do {\n         u = stbi__gif_load_next(s, &g, comp, req_comp, two_back);\n         if (u == (stbi_uc *) s) u = 0;  // end of animated gif marker\n\n         if (u) {\n            *x = g.w;\n            *y = g.h;\n            ++layers;\n            stride = g.w * g.h * 4;\n\n            if (out) {\n               void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride );\n               if (!tmp)\n                  return stbi__load_gif_main_outofmem(&g, out, delays);\n               else {\n                   out = (stbi_uc*) tmp;\n                   out_size = layers * stride;\n               }\n\n               if (delays) {\n                  int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );\n                  if (!new_delays)\n                     return stbi__load_gif_main_outofmem(&g, out, delays);\n                  *delays = new_delays;\n                  delays_size = layers * sizeof(int);\n               }\n            } else {\n               out = (stbi_uc*)stbi__malloc( layers * stride );\n               if (!out)\n                  return stbi__load_gif_main_outofmem(&g, out, delays);\n               out_size = layers * stride;\n               if (delays) {\n                  *delays = (int*) stbi__malloc( layers * sizeof(int) );\n                  if (!*delays)\n                     return stbi__load_gif_main_outofmem(&g, out, delays);\n                  delays_size = layers * sizeof(int);\n               }\n            }\n            memcpy( out + ((layers - 1) * stride), u, stride );\n            if (layers >= 2) {\n               two_back = out - 2 * stride;\n            }\n\n            if (delays) {\n               (*delays)[layers - 1U] = g.delay;\n            }\n         }\n      } while (u != 0);\n\n      // free temp buffer;\n      STBI_FREE(g.out);\n      STBI_FREE(g.history);\n      STBI_FREE(g.background);\n\n      // do the final conversion after loading everything;\n      if (req_comp && req_comp != 4)\n         out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h);\n\n      *z = layers;\n      return out;\n   } else {\n      return stbi__errpuc(\"not GIF\", \"Image was not as a gif type.\");\n   }\n}\n\nstatic void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)\n{\n   stbi_uc *u = 0;\n   stbi__gif g;\n   memset(&g, 0, sizeof(g));\n   STBI_NOTUSED(ri);\n\n   u = stbi__gif_load_next(s, &g, comp, req_comp, 0);\n   if (u == (stbi_uc *) s) u = 0;  // end of animated gif marker\n   if (u) {\n      *x = g.w;\n      *y = g.h;\n\n      // moved conversion to after successful load so that the same\n      // can be done for multiple frames.\n      if (req_comp && req_comp != 4)\n         u = stbi__convert_format(u, 4, req_comp, g.w, g.h);\n   } else if (g.out) {\n      // if there was an error and we allocated an image buffer, free it!\n      STBI_FREE(g.out);\n   }\n\n   // free buffers needed for multiple frame loading;\n   STBI_FREE(g.history);\n   STBI_FREE(g.background);\n\n   return u;\n}\n\nstatic int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp)\n{\n   return stbi__gif_info_raw(s,x,y,comp);\n}\n#endif\n\n// *************************************************************************************************\n// Radiance RGBE HDR loader\n// originally by Nicolas Schulz\n#ifndef STBI_NO_HDR\nstatic int stbi__hdr_test_core(stbi__context *s, const char *signature)\n{\n   int i;\n   for (i=0; signature[i]; ++i)\n      if (stbi__get8(s) != signature[i])\n          return 0;\n   stbi__rewind(s);\n   return 1;\n}\n\nstatic int stbi__hdr_test(stbi__context* s)\n{\n   int r = stbi__hdr_test_core(s, \"#?RADIANCE\\n\");\n   stbi__rewind(s);\n   if(!r) {\n       r = stbi__hdr_test_core(s, \"#?RGBE\\n\");\n       stbi__rewind(s);\n   }\n   return r;\n}\n\n#define STBI__HDR_BUFLEN  1024\nstatic char *stbi__hdr_gettoken(stbi__context *z, char *buffer)\n{\n   int len=0;\n   char c = '\\0';\n\n   c = (char) stbi__get8(z);\n\n   while (!stbi__at_eof(z) && c != '\\n') {\n      buffer[len++] = c;\n      if (len == STBI__HDR_BUFLEN-1) {\n         // flush to end of line\n         while (!stbi__at_eof(z) && stbi__get8(z) != '\\n')\n            ;\n         break;\n      }\n      c = (char) stbi__get8(z);\n   }\n\n   buffer[len] = 0;\n   return buffer;\n}\n\nstatic void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp)\n{\n   if ( input[3] != 0 ) {\n      float f1;\n      // Exponent\n      f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));\n      if (req_comp <= 2)\n         output[0] = (input[0] + input[1] + input[2]) * f1 / 3;\n      else {\n         output[0] = input[0] * f1;\n         output[1] = input[1] * f1;\n         output[2] = input[2] * f1;\n      }\n      if (req_comp == 2) output[1] = 1;\n      if (req_comp == 4) output[3] = 1;\n   } else {\n      switch (req_comp) {\n         case 4: output[3] = 1; /* fallthrough */\n         case 3: output[0] = output[1] = output[2] = 0;\n                 break;\n         case 2: output[1] = 1; /* fallthrough */\n         case 1: output[0] = 0;\n                 break;\n      }\n   }\n}\n\nstatic float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)\n{\n   char buffer[STBI__HDR_BUFLEN];\n   char *token;\n   int valid = 0;\n   int width, height;\n   stbi_uc *scanline;\n   float *hdr_data;\n   int len;\n   unsigned char count, value;\n   int i, j, k, c1,c2, z;\n   const char *headerToken;\n   STBI_NOTUSED(ri);\n\n   // Check identifier\n   headerToken = stbi__hdr_gettoken(s,buffer);\n   if (strcmp(headerToken, \"#?RADIANCE\") != 0 && strcmp(headerToken, \"#?RGBE\") != 0)\n      return stbi__errpf(\"not HDR\", \"Corrupt HDR image\");\n\n   // Parse header\n   for(;;) {\n      token = stbi__hdr_gettoken(s,buffer);\n      if (token[0] == 0) break;\n      if (strcmp(token, \"FORMAT=32-bit_rle_rgbe\") == 0) valid = 1;\n   }\n\n   if (!valid)    return stbi__errpf(\"unsupported format\", \"Unsupported HDR format\");\n\n   // Parse width and height\n   // can't use sscanf() if we're not using stdio!\n   token = stbi__hdr_gettoken(s,buffer);\n   if (strncmp(token, \"-Y \", 3))  return stbi__errpf(\"unsupported data layout\", \"Unsupported HDR format\");\n   token += 3;\n   height = (int) strtol(token, &token, 10);\n   while (*token == ' ') ++token;\n   if (strncmp(token, \"+X \", 3))  return stbi__errpf(\"unsupported data layout\", \"Unsupported HDR format\");\n   token += 3;\n   width = (int) strtol(token, NULL, 10);\n\n   if (height > STBI_MAX_DIMENSIONS) return stbi__errpf(\"too large\",\"Very large image (corrupt?)\");\n   if (width > STBI_MAX_DIMENSIONS) return stbi__errpf(\"too large\",\"Very large image (corrupt?)\");\n\n   *x = width;\n   *y = height;\n\n   if (comp) *comp = 3;\n   if (req_comp == 0) req_comp = 3;\n\n   if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0))\n      return stbi__errpf(\"too large\", \"HDR image is too large\");\n\n   // Read data\n   hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0);\n   if (!hdr_data)\n      return stbi__errpf(\"outofmem\", \"Out of memory\");\n\n   // Load image data\n   // image data is stored as some number of sca\n   if ( width < 8 || width >= 32768) {\n      // Read flat data\n      for (j=0; j < height; ++j) {\n         for (i=0; i < width; ++i) {\n            stbi_uc rgbe[4];\n           main_decode_loop:\n            stbi__getn(s, rgbe, 4);\n            stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);\n         }\n      }\n   } else {\n      // Read RLE-encoded data\n      scanline = NULL;\n\n      for (j = 0; j < height; ++j) {\n         c1 = stbi__get8(s);\n         c2 = stbi__get8(s);\n         len = stbi__get8(s);\n         if (c1 != 2 || c2 != 2 || (len & 0x80)) {\n            // not run-length encoded, so we have to actually use THIS data as a decoded\n            // pixel (note this can't be a valid pixel--one of RGB must be >= 128)\n            stbi_uc rgbe[4];\n            rgbe[0] = (stbi_uc) c1;\n            rgbe[1] = (stbi_uc) c2;\n            rgbe[2] = (stbi_uc) len;\n            rgbe[3] = (stbi_uc) stbi__get8(s);\n            stbi__hdr_convert(hdr_data, rgbe, req_comp);\n            i = 1;\n            j = 0;\n            STBI_FREE(scanline);\n            goto main_decode_loop; // yes, this makes no sense\n         }\n         len <<= 8;\n         len |= stbi__get8(s);\n         if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf(\"invalid decoded scanline length\", \"corrupt HDR\"); }\n         if (scanline == NULL) {\n            scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0);\n            if (!scanline) {\n               STBI_FREE(hdr_data);\n               return stbi__errpf(\"outofmem\", \"Out of memory\");\n            }\n         }\n\n         for (k = 0; k < 4; ++k) {\n            int nleft;\n            i = 0;\n            while ((nleft = width - i) > 0) {\n               count = stbi__get8(s);\n               if (count > 128) {\n                  // Run\n                  value = stbi__get8(s);\n                  count -= 128;\n                  if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf(\"corrupt\", \"bad RLE data in HDR\"); }\n                  for (z = 0; z < count; ++z)\n                     scanline[i++ * 4 + k] = value;\n               } else {\n                  // Dump\n                  if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf(\"corrupt\", \"bad RLE data in HDR\"); }\n                  for (z = 0; z < count; ++z)\n                     scanline[i++ * 4 + k] = stbi__get8(s);\n               }\n            }\n         }\n         for (i=0; i < width; ++i)\n            stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);\n      }\n      if (scanline)\n         STBI_FREE(scanline);\n   }\n\n   return hdr_data;\n}\n\nstatic int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp)\n{\n   char buffer[STBI__HDR_BUFLEN];\n   char *token;\n   int valid = 0;\n   int dummy;\n\n   if (!x) x = &dummy;\n   if (!y) y = &dummy;\n   if (!comp) comp = &dummy;\n\n   if (stbi__hdr_test(s) == 0) {\n       stbi__rewind( s );\n       return 0;\n   }\n\n   for(;;) {\n      token = stbi__hdr_gettoken(s,buffer);\n      if (token[0] == 0) break;\n      if (strcmp(token, \"FORMAT=32-bit_rle_rgbe\") == 0) valid = 1;\n   }\n\n   if (!valid) {\n       stbi__rewind( s );\n       return 0;\n   }\n   token = stbi__hdr_gettoken(s,buffer);\n   if (strncmp(token, \"-Y \", 3)) {\n       stbi__rewind( s );\n       return 0;\n   }\n   token += 3;\n   *y = (int) strtol(token, &token, 10);\n   while (*token == ' ') ++token;\n   if (strncmp(token, \"+X \", 3)) {\n       stbi__rewind( s );\n       return 0;\n   }\n   token += 3;\n   *x = (int) strtol(token, NULL, 10);\n   *comp = 3;\n   return 1;\n}\n#endif // STBI_NO_HDR\n\n#ifndef STBI_NO_BMP\nstatic int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)\n{\n   void *p;\n   stbi__bmp_data info;\n\n   info.all_a = 255;\n   p = stbi__bmp_parse_header(s, &info);\n   if (p == NULL) {\n      stbi__rewind( s );\n      return 0;\n   }\n   if (x) *x = s->img_x;\n   if (y) *y = s->img_y;\n   if (comp) {\n      if (info.bpp == 24 && info.ma == 0xff000000)\n         *comp = 3;\n      else\n         *comp = info.ma ? 4 : 3;\n   }\n   return 1;\n}\n#endif\n\n#ifndef STBI_NO_PSD\nstatic int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp)\n{\n   int channelCount, dummy, depth;\n   if (!x) x = &dummy;\n   if (!y) y = &dummy;\n   if (!comp) comp = &dummy;\n   if (stbi__get32be(s) != 0x38425053) {\n       stbi__rewind( s );\n       return 0;\n   }\n   if (stbi__get16be(s) != 1) {\n       stbi__rewind( s );\n       return 0;\n   }\n   stbi__skip(s, 6);\n   channelCount = stbi__get16be(s);\n   if (channelCount < 0 || channelCount > 16) {\n       stbi__rewind( s );\n       return 0;\n   }\n   *y = stbi__get32be(s);\n   *x = stbi__get32be(s);\n   depth = stbi__get16be(s);\n   if (depth != 8 && depth != 16) {\n       stbi__rewind( s );\n       return 0;\n   }\n   if (stbi__get16be(s) != 3) {\n       stbi__rewind( s );\n       return 0;\n   }\n   *comp = 4;\n   return 1;\n}\n\nstatic int stbi__psd_is16(stbi__context *s)\n{\n   int channelCount, depth;\n   if (stbi__get32be(s) != 0x38425053) {\n       stbi__rewind( s );\n       return 0;\n   }\n   if (stbi__get16be(s) != 1) {\n       stbi__rewind( s );\n       return 0;\n   }\n   stbi__skip(s, 6);\n   channelCount = stbi__get16be(s);\n   if (channelCount < 0 || channelCount > 16) {\n       stbi__rewind( s );\n       return 0;\n   }\n   STBI_NOTUSED(stbi__get32be(s));\n   STBI_NOTUSED(stbi__get32be(s));\n   depth = stbi__get16be(s);\n   if (depth != 16) {\n       stbi__rewind( s );\n       return 0;\n   }\n   return 1;\n}\n#endif\n\n#ifndef STBI_NO_PIC\nstatic int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)\n{\n   int act_comp=0,num_packets=0,chained,dummy;\n   stbi__pic_packet packets[10];\n\n   if (!x) x = &dummy;\n   if (!y) y = &dummy;\n   if (!comp) comp = &dummy;\n\n   if (!stbi__pic_is4(s,\"\\x53\\x80\\xF6\\x34\")) {\n      stbi__rewind(s);\n      return 0;\n   }\n\n   stbi__skip(s, 88);\n\n   *x = stbi__get16be(s);\n   *y = stbi__get16be(s);\n   if (stbi__at_eof(s)) {\n      stbi__rewind( s);\n      return 0;\n   }\n   if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) {\n      stbi__rewind( s );\n      return 0;\n   }\n\n   stbi__skip(s, 8);\n\n   do {\n      stbi__pic_packet *packet;\n\n      if (num_packets==sizeof(packets)/sizeof(packets[0]))\n         return 0;\n\n      packet = &packets[num_packets++];\n      chained = stbi__get8(s);\n      packet->size    = stbi__get8(s);\n      packet->type    = stbi__get8(s);\n      packet->channel = stbi__get8(s);\n      act_comp |= packet->channel;\n\n      if (stbi__at_eof(s)) {\n          stbi__rewind( s );\n          return 0;\n      }\n      if (packet->size != 8) {\n          stbi__rewind( s );\n          return 0;\n      }\n   } while (chained);\n\n   *comp = (act_comp & 0x10 ? 4 : 3);\n\n   return 1;\n}\n#endif\n\n// *************************************************************************************************\n// Portable Gray Map and Portable Pixel Map loader\n// by Ken Miller\n//\n// PGM: http://netpbm.sourceforge.net/doc/pgm.html\n// PPM: http://netpbm.sourceforge.net/doc/ppm.html\n//\n// Known limitations:\n//    Does not support comments in the header section\n//    Does not support ASCII image data (formats P2 and P3)\n\n#ifndef STBI_NO_PNM\n\nstatic int      stbi__pnm_test(stbi__context *s)\n{\n   char p, t;\n   p = (char) stbi__get8(s);\n   t = (char) stbi__get8(s);\n   if (p != 'P' || (t != '5' && t != '6')) {\n       stbi__rewind( s );\n       return 0;\n   }\n   return 1;\n}\n\nstatic void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)\n{\n   stbi_uc *out;\n   STBI_NOTUSED(ri);\n\n   ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n);\n   if (ri->bits_per_channel == 0)\n      return 0;\n\n   if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc(\"too large\",\"Very large image (corrupt?)\");\n   if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc(\"too large\",\"Very large image (corrupt?)\");\n\n   *x = s->img_x;\n   *y = s->img_y;\n   if (comp) *comp = s->img_n;\n\n   if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0))\n      return stbi__errpuc(\"too large\", \"PNM too large\");\n\n   out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);\n   if (!out) return stbi__errpuc(\"outofmem\", \"Out of memory\");\n   if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {\n      STBI_FREE(out);\n      return stbi__errpuc(\"bad PNM\", \"PNM file truncated\");\n   }\n\n   if (req_comp && req_comp != s->img_n) {\n      if (ri->bits_per_channel == 16) {\n         out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y);\n      } else {\n         out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);\n      }\n      if (out == NULL) return out; // stbi__convert_format frees input on failure\n   }\n   return out;\n}\n\nstatic int      stbi__pnm_isspace(char c)\n{\n   return c == ' ' || c == '\\t' || c == '\\n' || c == '\\v' || c == '\\f' || c == '\\r';\n}\n\nstatic void     stbi__pnm_skip_whitespace(stbi__context *s, char *c)\n{\n   for (;;) {\n      while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))\n         *c = (char) stbi__get8(s);\n\n      if (stbi__at_eof(s) || *c != '#')\n         break;\n\n      while (!stbi__at_eof(s) && *c != '\\n' && *c != '\\r' )\n         *c = (char) stbi__get8(s);\n   }\n}\n\nstatic int      stbi__pnm_isdigit(char c)\n{\n   return c >= '0' && c <= '9';\n}\n\nstatic int      stbi__pnm_getinteger(stbi__context *s, char *c)\n{\n   int value = 0;\n\n   while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {\n      value = value*10 + (*c - '0');\n      *c = (char) stbi__get8(s);\n      if((value > 214748364) || (value == 214748364 && *c > '7'))\n          return stbi__err(\"integer parse overflow\", \"Parsing an integer in the PPM header overflowed a 32-bit int\");\n   }\n\n   return value;\n}\n\nstatic int      stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)\n{\n   int maxv, dummy;\n   char c, p, t;\n\n   if (!x) x = &dummy;\n   if (!y) y = &dummy;\n   if (!comp) comp = &dummy;\n\n   stbi__rewind(s);\n\n   // Get identifier\n   p = (char) stbi__get8(s);\n   t = (char) stbi__get8(s);\n   if (p != 'P' || (t != '5' && t != '6')) {\n       stbi__rewind(s);\n       return 0;\n   }\n\n   *comp = (t == '6') ? 3 : 1;  // '5' is 1-component .pgm; '6' is 3-component .ppm\n\n   c = (char) stbi__get8(s);\n   stbi__pnm_skip_whitespace(s, &c);\n\n   *x = stbi__pnm_getinteger(s, &c); // read width\n   if(*x == 0)\n       return stbi__err(\"invalid width\", \"PPM image header had zero or overflowing width\");\n   stbi__pnm_skip_whitespace(s, &c);\n\n   *y = stbi__pnm_getinteger(s, &c); // read height\n   if (*y == 0)\n       return stbi__err(\"invalid width\", \"PPM image header had zero or overflowing width\");\n   stbi__pnm_skip_whitespace(s, &c);\n\n   maxv = stbi__pnm_getinteger(s, &c);  // read max value\n   if (maxv > 65535)\n      return stbi__err(\"max value > 65535\", \"PPM image supports only 8-bit and 16-bit images\");\n   else if (maxv > 255)\n      return 16;\n   else\n      return 8;\n}\n\nstatic int stbi__pnm_is16(stbi__context *s)\n{\n   if (stbi__pnm_info(s, NULL, NULL, NULL) == 16)\n\t   return 1;\n   return 0;\n}\n#endif\n\nstatic int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)\n{\n   #ifndef STBI_NO_JPEG\n   if (stbi__jpeg_info(s, x, y, comp)) return 1;\n   #endif\n\n   #ifndef STBI_NO_PNG\n   if (stbi__png_info(s, x, y, comp))  return 1;\n   #endif\n\n   #ifndef STBI_NO_GIF\n   if (stbi__gif_info(s, x, y, comp))  return 1;\n   #endif\n\n   #ifndef STBI_NO_BMP\n   if (stbi__bmp_info(s, x, y, comp))  return 1;\n   #endif\n\n   #ifndef STBI_NO_PSD\n   if (stbi__psd_info(s, x, y, comp))  return 1;\n   #endif\n\n   #ifndef STBI_NO_PIC\n   if (stbi__pic_info(s, x, y, comp))  return 1;\n   #endif\n\n   #ifndef STBI_NO_PNM\n   if (stbi__pnm_info(s, x, y, comp))  return 1;\n   #endif\n\n   #ifndef STBI_NO_HDR\n   if (stbi__hdr_info(s, x, y, comp))  return 1;\n   #endif\n\n   // test tga last because it's a crappy test!\n   #ifndef STBI_NO_TGA\n   if (stbi__tga_info(s, x, y, comp))\n       return 1;\n   #endif\n   return stbi__err(\"unknown image type\", \"Image not of any known type, or corrupt\");\n}\n\nstatic int stbi__is_16_main(stbi__context *s)\n{\n   #ifndef STBI_NO_PNG\n   if (stbi__png_is16(s))  return 1;\n   #endif\n\n   #ifndef STBI_NO_PSD\n   if (stbi__psd_is16(s))  return 1;\n   #endif\n\n   #ifndef STBI_NO_PNM\n   if (stbi__pnm_is16(s))  return 1;\n   #endif\n   return 0;\n}\n\n#ifndef STBI_NO_STDIO\nSTBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp)\n{\n    FILE *f = stbi__fopen(filename, \"rb\");\n    int result;\n    if (!f) return stbi__err(\"can't fopen\", \"Unable to open file\");\n    result = stbi_info_from_file(f, x, y, comp);\n    fclose(f);\n    return result;\n}\n\nSTBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp)\n{\n   int r;\n   stbi__context s;\n   long pos = ftell(f);\n   stbi__start_file(&s, f);\n   r = stbi__info_main(&s,x,y,comp);\n   fseek(f,pos,SEEK_SET);\n   return r;\n}\n\nSTBIDEF int stbi_is_16_bit(char const *filename)\n{\n    FILE *f = stbi__fopen(filename, \"rb\");\n    int result;\n    if (!f) return stbi__err(\"can't fopen\", \"Unable to open file\");\n    result = stbi_is_16_bit_from_file(f);\n    fclose(f);\n    return result;\n}\n\nSTBIDEF int stbi_is_16_bit_from_file(FILE *f)\n{\n   int r;\n   stbi__context s;\n   long pos = ftell(f);\n   stbi__start_file(&s, f);\n   r = stbi__is_16_main(&s);\n   fseek(f,pos,SEEK_SET);\n   return r;\n}\n#endif // !STBI_NO_STDIO\n\nSTBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp)\n{\n   stbi__context s;\n   stbi__start_mem(&s,buffer,len);\n   return stbi__info_main(&s,x,y,comp);\n}\n\nSTBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp)\n{\n   stbi__context s;\n   stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);\n   return stbi__info_main(&s,x,y,comp);\n}\n\nSTBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len)\n{\n   stbi__context s;\n   stbi__start_mem(&s,buffer,len);\n   return stbi__is_16_main(&s);\n}\n\nSTBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user)\n{\n   stbi__context s;\n   stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);\n   return stbi__is_16_main(&s);\n}\n\n#endif // STB_IMAGE_IMPLEMENTATION\n\n/*\n   revision history:\n      2.20  (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs\n      2.19  (2018-02-11) fix warning\n      2.18  (2018-01-30) fix warnings\n      2.17  (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug\n                         1-bit BMP\n                         *_is_16_bit api\n                         avoid warnings\n      2.16  (2017-07-23) all functions have 16-bit variants;\n                         STBI_NO_STDIO works again;\n                         compilation fixes;\n                         fix rounding in unpremultiply;\n                         optimize vertical flip;\n                         disable raw_len validation;\n                         documentation fixes\n      2.15  (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode;\n                         warning fixes; disable run-time SSE detection on gcc;\n                         uniform handling of optional \"return\" values;\n                         thread-safe initialization of zlib tables\n      2.14  (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs\n      2.13  (2016-11-29) add 16-bit API, only supported for PNG right now\n      2.12  (2016-04-02) fix typo in 2.11 PSD fix that caused crashes\n      2.11  (2016-04-02) allocate large structures on the stack\n                         remove white matting for transparent PSD\n                         fix reported channel count for PNG & BMP\n                         re-enable SSE2 in non-gcc 64-bit\n                         support RGB-formatted JPEG\n                         read 16-bit PNGs (only as 8-bit)\n      2.10  (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED\n      2.09  (2016-01-16) allow comments in PNM files\n                         16-bit-per-pixel TGA (not bit-per-component)\n                         info() for TGA could break due to .hdr handling\n                         info() for BMP to shares code instead of sloppy parse\n                         can use STBI_REALLOC_SIZED if allocator doesn't support realloc\n                         code cleanup\n      2.08  (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA\n      2.07  (2015-09-13) fix compiler warnings\n                         partial animated GIF support\n                         limited 16-bpc PSD support\n                         #ifdef unused functions\n                         bug with < 92 byte PIC,PNM,HDR,TGA\n      2.06  (2015-04-19) fix bug where PSD returns wrong '*comp' value\n      2.05  (2015-04-19) fix bug in progressive JPEG handling, fix warning\n      2.04  (2015-04-15) try to re-enable SIMD on MinGW 64-bit\n      2.03  (2015-04-12) extra corruption checking (mmozeiko)\n                         stbi_set_flip_vertically_on_load (nguillemot)\n                         fix NEON support; fix mingw support\n      2.02  (2015-01-19) fix incorrect assert, fix warning\n      2.01  (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2\n      2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG\n      2.00  (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg)\n                         progressive JPEG (stb)\n                         PGM/PPM support (Ken Miller)\n                         STBI_MALLOC,STBI_REALLOC,STBI_FREE\n                         GIF bugfix -- seemingly never worked\n                         STBI_NO_*, STBI_ONLY_*\n      1.48  (2014-12-14) fix incorrectly-named assert()\n      1.47  (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb)\n                         optimize PNG (ryg)\n                         fix bug in interlaced PNG with user-specified channel count (stb)\n      1.46  (2014-08-26)\n              fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG\n      1.45  (2014-08-16)\n              fix MSVC-ARM internal compiler error by wrapping malloc\n      1.44  (2014-08-07)\n              various warning fixes from Ronny Chevalier\n      1.43  (2014-07-15)\n              fix MSVC-only compiler problem in code changed in 1.42\n      1.42  (2014-07-09)\n              don't define _CRT_SECURE_NO_WARNINGS (affects user code)\n              fixes to stbi__cleanup_jpeg path\n              added STBI_ASSERT to avoid requiring assert.h\n      1.41  (2014-06-25)\n              fix search&replace from 1.36 that messed up comments/error messages\n      1.40  (2014-06-22)\n              fix gcc struct-initialization warning\n      1.39  (2014-06-15)\n              fix to TGA optimization when req_comp != number of components in TGA;\n              fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite)\n              add support for BMP version 5 (more ignored fields)\n      1.38  (2014-06-06)\n              suppress MSVC warnings on integer casts truncating values\n              fix accidental rename of 'skip' field of I/O\n      1.37  (2014-06-04)\n              remove duplicate typedef\n      1.36  (2014-06-03)\n              convert to header file single-file library\n              if de-iphone isn't set, load iphone images color-swapped instead of returning NULL\n      1.35  (2014-05-27)\n              various warnings\n              fix broken STBI_SIMD path\n              fix bug where stbi_load_from_file no longer left file pointer in correct place\n              fix broken non-easy path for 32-bit BMP (possibly never used)\n              TGA optimization by Arseny Kapoulkine\n      1.34  (unknown)\n              use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case\n      1.33  (2011-07-14)\n              make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements\n      1.32  (2011-07-13)\n              support for \"info\" function for all supported filetypes (SpartanJ)\n      1.31  (2011-06-20)\n              a few more leak fixes, bug in PNG handling (SpartanJ)\n      1.30  (2011-06-11)\n              added ability to load files via callbacks to accomidate custom input streams (Ben Wenger)\n              removed deprecated format-specific test/load functions\n              removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway\n              error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha)\n              fix inefficiency in decoding 32-bit BMP (David Woo)\n      1.29  (2010-08-16)\n              various warning fixes from Aurelien Pocheville\n      1.28  (2010-08-01)\n              fix bug in GIF palette transparency (SpartanJ)\n      1.27  (2010-08-01)\n              cast-to-stbi_uc to fix warnings\n      1.26  (2010-07-24)\n              fix bug in file buffering for PNG reported by SpartanJ\n      1.25  (2010-07-17)\n              refix trans_data warning (Won Chun)\n      1.24  (2010-07-12)\n              perf improvements reading from files on platforms with lock-heavy fgetc()\n              minor perf improvements for jpeg\n              deprecated type-specific functions so we'll get feedback if they're needed\n              attempt to fix trans_data warning (Won Chun)\n      1.23    fixed bug in iPhone support\n      1.22  (2010-07-10)\n              removed image *writing* support\n              stbi_info support from Jetro Lauha\n              GIF support from Jean-Marc Lienher\n              iPhone PNG-extensions from James Brown\n              warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva)\n      1.21    fix use of 'stbi_uc' in header (reported by jon blow)\n      1.20    added support for Softimage PIC, by Tom Seddon\n      1.19    bug in interlaced PNG corruption check (found by ryg)\n      1.18  (2008-08-02)\n              fix a threading bug (local mutable static)\n      1.17    support interlaced PNG\n      1.16    major bugfix - stbi__convert_format converted one too many pixels\n      1.15    initialize some fields for thread safety\n      1.14    fix threadsafe conversion bug\n              header-file-only version (#define STBI_HEADER_FILE_ONLY before including)\n      1.13    threadsafe\n      1.12    const qualifiers in the API\n      1.11    Support installable IDCT, colorspace conversion routines\n      1.10    Fixes for 64-bit (don't use \"unsigned long\")\n              optimized upsampling by Fabian \"ryg\" Giesen\n      1.09    Fix format-conversion for PSD code (bad global variables!)\n      1.08    Thatcher Ulrich's PSD code integrated by Nicolas Schulz\n      1.07    attempt to fix C++ warning/errors again\n      1.06    attempt to fix C++ warning/errors again\n      1.05    fix TGA loading to return correct *comp and use good luminance calc\n      1.04    default float alpha is 1, not 255; use 'void *' for stbi_image_free\n      1.03    bugfixes to STBI_NO_STDIO, STBI_NO_HDR\n      1.02    support for (subset of) HDR files, float interface for preferred access to them\n      1.01    fix bug: possible bug in handling right-side up bmps... not sure\n              fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all\n      1.00    interface to zlib that skips zlib header\n      0.99    correct handling of alpha in palette\n      0.98    TGA loader by lonesock; dynamically add loaders (untested)\n      0.97    jpeg errors on too large a file; also catch another malloc failure\n      0.96    fix detection of invalid v value - particleman@mollyrocket forum\n      0.95    during header scan, seek to markers in case of padding\n      0.94    STBI_NO_STDIO to disable stdio usage; rename all #defines the same\n      0.93    handle jpegtran output; verbose errors\n      0.92    read 4,8,16,24,32-bit BMP files of several formats\n      0.91    output 24-bit Windows 3.0 BMP files\n      0.90    fix a few more warnings; bump version number to approach 1.0\n      0.61    bugfixes due to Marc LeBlanc, Christopher Lloyd\n      0.60    fix compiling as c++\n      0.59    fix warnings: merge Dave Moore's -Wall fixes\n      0.58    fix bug: zlib uncompressed mode len/nlen was wrong endian\n      0.57    fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available\n      0.56    fix bug: zlib uncompressed mode len vs. nlen\n      0.55    fix bug: restart_interval not initialized to 0\n      0.54    allow NULL for 'int *comp'\n      0.53    fix bug in png 3->4; speedup png decoding\n      0.52    png handles req_comp=3,4 directly; minor cleanup; jpeg comments\n      0.51    obey req_comp requests, 1-component jpegs return as 1-component,\n              on 'test' only check type, not whether we support this variant\n      0.50  (2006-11-19)\n              first released version\n*/\n\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "lib/third_party/imgui/backend/source/imgui_impl_glfw.cpp",
    "content": "// dear imgui: Platform Backend for GLFW\n// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)\n// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)\n// (Requires: GLFW 3.0+. Prefer GLFW 3.3+/3.4+ for full feature support.)\n\n// Implemented features:\n//  [X] Platform: Clipboard support.\n//  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only).\n//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]\n//  [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.\n//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors) with GLFW 3.1+. Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.\n//  [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.\n//  [X] Multiple Dear ImGui contexts support.\n// Missing features or Issues:\n//  [ ] Platform: Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround.\n//  [ ] Platform: Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors.\n//  [ ] Platform: Multi-viewport: Missing ImGuiBackendFlags_HasParentViewport support. The viewport->ParentViewportID field is ignored, and therefore io.ConfigViewportsNoDefaultParent has no effect either.\n\n// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.\n// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.\n// Learn about Dear ImGui:\n// - FAQ                  https://dearimgui.com/faq\n// - Getting Started      https://dearimgui.com/getting-started\n// - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).\n// - Introduction, links and more at the top of imgui.cpp\n\n// About Emscripten support:\n// - Emscripten provides its own GLFW (3.2.1) implementation (syntax: \"-sUSE_GLFW=3\"), but Joystick is broken and several features are not supported (multiple windows, clipboard, timer, etc.)\n// - A third-party Emscripten GLFW (3.4.0) implementation (syntax: \"--use-port=contrib.glfw3\") fixes the Joystick issue and implements all relevant features for the browser.\n// See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Comparison.md for details.\n\n// CHANGELOG\n// (minor and older changes stripped away, please see git history for details)\n//  2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.\n//  2026-02-10: Try to set IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND automatically if corresponding headers are not accessible. (#9225)\n//  2026-01-25: [Docking] Improve workarounds for cases where GLFW is unable to provide any reliable monitor info. Preserve existing monitor list when none of the new one is valid. (#9195, #7902, #5683)\n//  2026-01-18: [Docking] Dynamically load X11 functions to avoid -lx11 linking requirement introduced on 2025-09-10.\n//  2025-12-12: Added IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND to forcefully disable either.\n//  2025-12-10: Avoid repeated glfwSetCursor()/glfwSetInputMode() calls when unnecessary. Lowers overhead for very high framerates (e.g. 10k+ FPS).\n//  2025-11-06: Lower minimum requirement to GLFW 3.0. Though a recent version e.g GLFW 3.4 is highly recommended.\n//  2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown.\n//  2025-09-15: Content Scales are always reported as 1.0 on Wayland. FramebufferScale are always reported as 1.0 on X11. (#8920, #8921)\n//  2025-09-10: [Docking] Improve multi-viewport behavior in tiling WMs on X11 via the ImGui_ImplGlfw_SetWindowFloating() function. Note: using GLFW backend on Linux/BSD etc. requires linking with -lX11. (#8884, #8474, #8289)\n//  2025-07-08: Made ImGui_ImplGlfw_GetContentScaleForWindow(), ImGui_ImplGlfw_GetContentScaleForMonitor() helpers return 1.0f on Emscripten and Android platforms, matching macOS logic. (#8742, #8733)\n//  2025-06-18: Added support for multiple Dear ImGui contexts. (#8676, #8239, #8069)\n//  2025-06-11: Added ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window) and ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor) helper to facilitate making DPI-aware apps.\n//  2025-05-15: [Docking] Add Platform_GetWindowFramebufferScale() handler, to allow varying Retina display density on multiple monitors.\n//  2025-04-26: [Docking] Disable multi-viewports under Wayland. (#8587)\n//  2025-03-10: Map GLFW_KEY_WORLD_1 and GLFW_KEY_WORLD_2 into ImGuiKey_Oem102.\n//  2025-03-03: Fixed clipboard handler assertion when using GLFW <= 3.2.1 compiled with asserts enabled.\n//  2025-02-21: [Docking] Update monitors and work areas information every frame, as the later may change regardless of monitor changes. (#8415)\n//  2024-11-05: [Docking] Added Linux workaround for spurious mouse up events emitted while dragging and creating new viewport. (#3158, #7733, #7922)\n//  2024-08-22: Moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:\n//               - io.GetClipboardTextFn    -> platform_io.Platform_GetClipboardTextFn\n//               - io.SetClipboardTextFn    -> platform_io.Platform_SetClipboardTextFn\n//               - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn\n//  2024-07-31: Added ImGui_ImplGlfw_Sleep() helper function for usage by our examples app, since GLFW doesn't provide one.\n//  2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter.\n//  2024-07-08: Emscripten: Added support for GLFW3 contrib port (GLFW 3.4.0 features + bug fixes): to enable, replace -sUSE_GLFW=3 with --use-port=contrib.glfw3 (requires emscripten 3.1.59+) (https://github.com/pongasoft/emscripten-glfw)\n//  2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions.\n//  2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window.\n//  2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys.\n//  2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609)\n//  2023-06-12: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser + Emscripten. (#6491)\n//  2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen on Windows ONLY, using a custom WndProc hook. (#2702)\n//  2023-03-16: Inputs: Fixed key modifiers handling on secondary viewports (docking branch). Broken on 2023/01/04. (#6248, #6034)\n//  2023-03-14: Emscripten: Avoid using glfwGetError() and glfwGetGamepadState() which are not correctly implemented in Emscripten emulation. (#6240)\n//  2023-02-03: Emscripten: Registering custom low-level mouse wheel handler to get more accurate scrolling impulses on Emscripten. (#4019, #6096)\n//  2023-01-18: Handle unsupported glfwGetVideoMode() call on e.g. Emscripten.\n//  2023-01-04: Inputs: Fixed mods state on Linux when using Alt-GR text input (e.g. German keyboard layout), could lead to broken text input. Revert a 2022/01/17 change were we resumed using mods provided by GLFW, turns out they were faulty.\n//  2022-11-22: Perform a dummy glfwGetError() read to cancel missing names with glfwGetKeyName(). (#5908)\n//  2022-10-18: Perform a dummy glfwGetError() read to cancel missing mouse cursors errors. Using GLFW_VERSION_COMBINED directly. (#5785)\n//  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.\n//  2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).\n//  2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position *EDIT* Reverted 2023-07-18.\n//  2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX.\n//  2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11.\n//  2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend.\n//  2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.\n//  2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].\n//  2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).\n//  2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates.\n//  2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback().\n//  2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.\n//  2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API.\n//  2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback().\n//  2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback().\n//  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).\n//  2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors.\n//  2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor).\n//  2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown.\n//  2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.\n//  2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter().\n//  2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.\n//  2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.\n//  2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them.\n//  2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.\n//  2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.\n//  2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples.\n//  2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.\n//  2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()).\n//  2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.\n//  2018-02-06: Inputs: Added mapping for ImGuiKey_Space.\n//  2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set.\n//  2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).\n//  2018-01-20: Inputs: Added Horizontal Mouse Wheel support.\n//  2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.\n//  2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).\n//  2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_impl_glfw.h\"\n\n// Clang warnings with -Weverything\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wold-style-cast\"         // warning: use of old-style cast\n#pragma clang diagnostic ignored \"-Wsign-conversion\"        // warning: implicit conversion changes signedness\n#pragma clang diagnostic ignored \"-Wexit-time-destructors\"  // warning: declaration requires an exit-time destructor     // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.\n#pragma clang diagnostic ignored \"-Wglobal-constructors\"    // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.\n#elif defined(__GNUC__)\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"              // warning: comparing floating-point with '==' or '!=' is unsafe\n#endif\n\n#if defined(__has_include)\n#if !__has_include(<X11/Xlib.h>) || !__has_include(<X11/extensions/Xrandr.h>)\n#define IMGUI_IMPL_GLFW_DISABLE_X11\n#endif\n#if !__has_include(<wayland-client.h>)\n#define IMGUI_IMPL_GLFW_DISABLE_WAYLAND\n#endif\n#endif\n\n// GLFW\n#if !defined(IMGUI_IMPL_GLFW_DISABLE_X11) && (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__))\n#define GLFW_HAS_X11        1\n#else\n#define GLFW_HAS_X11        0\n#endif\n#if !defined(IMGUI_IMPL_GLFW_DISABLE_WAYLAND) && (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__))\n#define GLFW_HAS_WAYLAND    1\n#else\n#define GLFW_HAS_WAYLAND    0\n#endif\n#include <GLFW/glfw3.h>\n#ifdef _WIN32\n#undef APIENTRY\n#ifndef GLFW_EXPOSE_NATIVE_WIN32    // for glfwGetWin32Window()\n#define GLFW_EXPOSE_NATIVE_WIN32\n#endif\n#include <GLFW/glfw3native.h>\n#elif defined(__APPLE__)\n#ifndef GLFW_EXPOSE_NATIVE_COCOA    // for glfwGetCocoaWindow()\n#define GLFW_EXPOSE_NATIVE_COCOA\n#endif\n#include <GLFW/glfw3native.h>\n#elif GLFW_HAS_X11\n#ifndef GLFW_EXPOSE_NATIVE_X11      // for glfwGetX11Display(), glfwGetX11Window() on Freedesktop (Linux, BSD, etc.)\n#define GLFW_EXPOSE_NATIVE_X11\n#include <X11/Xatom.h>\n#include <dlfcn.h>              // for dlopen()\n#endif\n#include <GLFW/glfw3native.h>\n#undef Status                   // X11 headers are leaking this.\n#endif\n\n#ifndef _WIN32\n#include <unistd.h>             // for usleep()\n// IMHEX PATCH BEGIN\n#include <string_view>          // for std::string_view\n#include <cstdlib>              // for std::getenv()\n// IMHEX PATCH END\n#endif\n#include <stdio.h>              // for snprintf()\n\n#ifdef __EMSCRIPTEN__\n#include <emscripten.h>\n#include <emscripten/html5.h>\n\n// IMHEX PATCH BEGIN\n#include <emscripten_browser_clipboard.h>\n\nstatic std::string clipboardContent;\n// IMHEX PATCH END\n\n#ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3\n#include <GLFW/emscripten_glfw3.h>\n#else\n#define EMSCRIPTEN_USE_EMBEDDED_GLFW3\n#endif\n#endif\n\n// We gather version tests as define in order to easily see which features are version-dependent.\n#define GLFW_VERSION_COMBINED           (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION)\n#define GLFW_HAS_CREATECURSOR           (GLFW_VERSION_COMBINED >= 3100) // 3.1+ glfwCreateCursor()\n#define GLFW_HAS_WINDOW_TOPMOST         (GLFW_VERSION_COMBINED >= 3200) // 3.2+ GLFW_FLOATING\n#define GLFW_HAS_WINDOW_HOVERED         (GLFW_VERSION_COMBINED >= 3300) // 3.3+ GLFW_HOVERED\n#define GLFW_HAS_WINDOW_ALPHA           (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwSetWindowOpacity\n#define GLFW_HAS_PER_MONITOR_DPI        (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorContentScale\n#if defined(__EMSCRIPTEN__) || defined(__SWITCH__)                      // no Vulkan support in GLFW for Emscripten or homebrew Nintendo Switch\n#define GLFW_HAS_VULKAN                 (0)\n#else\n#define GLFW_HAS_VULKAN                 (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwCreateWindowSurface\n#endif\n#define GLFW_HAS_FOCUS_WINDOW           (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwFocusWindow\n#define GLFW_HAS_FOCUS_ON_SHOW          (GLFW_VERSION_COMBINED >= 3300) // 3.3+ GLFW_FOCUS_ON_SHOW\n#define GLFW_HAS_MONITOR_WORK_AREA      (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorWorkarea\n#define GLFW_HAS_OSX_WINDOW_POS_FIX     (GLFW_VERSION_COMBINED >= 3301) // 3.3.1+ Fixed: Resizing window repositions it on MacOS #1553\n#ifdef GLFW_RESIZE_NESW_CURSOR          // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released?\n#define GLFW_HAS_NEW_CURSORS            (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR\n#else\n#define GLFW_HAS_NEW_CURSORS            (0)\n#endif\n#ifdef GLFW_MOUSE_PASSTHROUGH           // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2020-07-17 (passthrough)\n#define GLFW_HAS_MOUSE_PASSTHROUGH      (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_MOUSE_PASSTHROUGH\n#else\n#define GLFW_HAS_MOUSE_PASSTHROUGH      (0)\n#endif\n#define GLFW_HAS_GAMEPAD_API            (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetGamepadState() new api\n#define GLFW_HAS_GETKEYNAME             (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName()\n#define GLFW_HAS_GETERROR               (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError()\n#define GLFW_HAS_GETPLATFORM            (GLFW_VERSION_COMBINED >= 3400) // 3.4+ glfwGetPlatform()\n\n// Map GLFWWindow* to ImGuiContext*.\n// - Would be simpler if we could use glfwSetWindowUserPointer()/glfwGetWindowUserPointer(), but this is a single and shared resource.\n// - Would be simpler if we could use e.g. std::map<> as well. But we don't.\n// - This is not particularly optimized as we expect size to be small and queries to be rare.\nstruct ImGui_ImplGlfw_WindowToContext { GLFWwindow* Window; ImGuiContext* Context; };\nstatic ImVector<ImGui_ImplGlfw_WindowToContext> g_ContextMap;\nstatic void ImGui_ImplGlfw_ContextMap_Add(GLFWwindow* window, ImGuiContext* ctx) { g_ContextMap.push_back(ImGui_ImplGlfw_WindowToContext{ window, ctx }); }\nstatic void ImGui_ImplGlfw_ContextMap_Remove(GLFWwindow* window)                 { for (ImGui_ImplGlfw_WindowToContext& entry : g_ContextMap) if (entry.Window == window) { g_ContextMap.erase_unsorted(&entry); if (g_ContextMap.empty()) g_ContextMap.clear(); return; } }\nstatic ImGuiContext* ImGui_ImplGlfw_ContextMap_Get(GLFWwindow* window)           { for (ImGui_ImplGlfw_WindowToContext& entry : g_ContextMap) if (entry.Window == window) return entry.Context; return nullptr; }\n\nenum GlfwClientApi\n{\n    GlfwClientApi_OpenGL,\n    GlfwClientApi_Vulkan,\n    GlfwClientApi_Unknown,  // Anything else fits here.\n};\n\n#if GLFW_HAS_X11\ntypedef Atom (*PFN_XInternAtom)(Display*, const char* ,Bool);\ntypedef int  (*PFN_XChangeProperty)(Display*, Window, Atom, Atom, int, int, const unsigned char*, int);\ntypedef int  (*PFN_XChangeWindowAttributes)(Display*, Window, unsigned long, XSetWindowAttributes*);\ntypedef int  (*PFN_XFlush)(Display*);\n#endif\n\n// GLFW data\nstruct ImGui_ImplGlfw_Data\n{\n    ImGuiContext*           Context;\n    GLFWwindow*             Window;\n    GlfwClientApi           ClientApi;\n    double                  Time;\n    GLFWwindow*             MouseWindow;\n#if GLFW_HAS_CREATECURSOR\n    GLFWcursor*             MouseCursors[ImGuiMouseCursor_COUNT];\n    GLFWcursor*             LastMouseCursor;\n#endif\n    bool                    MouseIgnoreButtonUpWaitForFocusLoss;\n    bool                    MouseIgnoreButtonUp;\n    ImVec2                  LastValidMousePos;\n    GLFWwindow*             KeyOwnerWindows[GLFW_KEY_LAST];\n    bool                    IsWayland;\n    bool                    InstalledCallbacks;\n    bool                    CallbacksChainForAllWindows;\n    char                    BackendPlatformName[32];\n#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3\n    const char*             CanvasSelector;\n#endif\n\n    // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.\n    GLFWwindowfocusfun      PrevUserCallbackWindowFocus;\n    GLFWcursorposfun        PrevUserCallbackCursorPos;\n    GLFWcursorenterfun      PrevUserCallbackCursorEnter;\n    GLFWmousebuttonfun      PrevUserCallbackMousebutton;\n    GLFWscrollfun           PrevUserCallbackScroll;\n    GLFWkeyfun              PrevUserCallbackKey;\n    GLFWcharfun             PrevUserCallbackChar;\n    GLFWmonitorfun          PrevUserCallbackMonitor;\n#ifdef _WIN32\n    WNDPROC                 PrevWndProc;\n#endif\n\n#if GLFW_HAS_X11\n    // Module and function pointers loaded at initialization to avoid linking statically with X11.\n    void*                       X11Module;\n    PFN_XInternAtom             XInternAtom;\n    PFN_XChangeProperty         XChangeProperty;\n    PFN_XChangeWindowAttributes XChangeWindowAttributes;\n    PFN_XFlush                  XFlush;\n#endif\n\n    ImGui_ImplGlfw_Data()   { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts\n// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.\n// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.\n// - Because glfwPollEvents() process all windows and some events may be called outside of it, you will need to register your own callbacks\n//   (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks.\n// - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it.\n// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.\nnamespace ImGui { extern ImGuiIO& GetIO(ImGuiContext*); }\nstatic ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData()\n{\n    // Get data for current context\n    return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;\n}\nstatic ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData(GLFWwindow* window)\n{\n    // Get data for a given GLFW window, regardless of current context (since GLFW events are sent together)\n    ImGuiContext* ctx = ImGui_ImplGlfw_ContextMap_Get(window);\n    return (ImGui_ImplGlfw_Data*)ImGui::GetIO(ctx).BackendPlatformUserData;\n}\n\n// Forward Declarations\nstatic void ImGui_ImplGlfw_UpdateMonitors();\nstatic void ImGui_ImplGlfw_InitMultiViewportSupport();\nstatic void ImGui_ImplGlfw_ShutdownMultiViewportSupport();\n\n// Functions\nstatic bool ImGui_ImplGlfw_IsWayland()\n{\n#if !GLFW_HAS_WAYLAND\n    return false;\n#elif GLFW_HAS_GETPLATFORM\n    return glfwGetPlatform() == GLFW_PLATFORM_WAYLAND;\n#else\n    const char* version = glfwGetVersionString();\n    if (strstr(version, \"Wayland\") == nullptr) // e.g. Ubuntu 22.04 ships with GLFW 3.3.6 compiled without Wayland\n        return false;\n#ifdef GLFW_EXPOSE_NATIVE_X11\n    if (glfwGetX11Display() != nullptr)\n        return false;\n#endif\n    return true;\n#endif\n}\n\n// Not static to allow third-party code to use that if they want to (but undocumented)\nImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode);\nImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode)\n{\n    IM_UNUSED(scancode);\n    switch (keycode)\n    {\n        case GLFW_KEY_TAB: return ImGuiKey_Tab;\n        case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow;\n        case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow;\n        case GLFW_KEY_UP: return ImGuiKey_UpArrow;\n        case GLFW_KEY_DOWN: return ImGuiKey_DownArrow;\n        case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp;\n        case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown;\n        case GLFW_KEY_HOME: return ImGuiKey_Home;\n        case GLFW_KEY_END: return ImGuiKey_End;\n        case GLFW_KEY_INSERT: return ImGuiKey_Insert;\n        case GLFW_KEY_DELETE: return ImGuiKey_Delete;\n        case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace;\n        case GLFW_KEY_SPACE: return ImGuiKey_Space;\n        case GLFW_KEY_ENTER: return ImGuiKey_Enter;\n        case GLFW_KEY_ESCAPE: return ImGuiKey_Escape;\n        case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe;\n        case GLFW_KEY_COMMA: return ImGuiKey_Comma;\n        case GLFW_KEY_MINUS: return ImGuiKey_Minus;\n        case GLFW_KEY_PERIOD: return ImGuiKey_Period;\n        case GLFW_KEY_SLASH: return ImGuiKey_Slash;\n        case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon;\n        case GLFW_KEY_EQUAL: return ImGuiKey_Equal;\n        case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket;\n        case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash;\n        case GLFW_KEY_WORLD_1: return ImGuiKey_Oem102;\n        case GLFW_KEY_WORLD_2: return ImGuiKey_Oem102;\n        case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket;\n        case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent;\n        case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock;\n        case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock;\n        case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock;\n        case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen;\n        case GLFW_KEY_PAUSE: return ImGuiKey_Pause;\n        case GLFW_KEY_KP_0: return ImGuiKey_Keypad0;\n        case GLFW_KEY_KP_1: return ImGuiKey_Keypad1;\n        case GLFW_KEY_KP_2: return ImGuiKey_Keypad2;\n        case GLFW_KEY_KP_3: return ImGuiKey_Keypad3;\n        case GLFW_KEY_KP_4: return ImGuiKey_Keypad4;\n        case GLFW_KEY_KP_5: return ImGuiKey_Keypad5;\n        case GLFW_KEY_KP_6: return ImGuiKey_Keypad6;\n        case GLFW_KEY_KP_7: return ImGuiKey_Keypad7;\n        case GLFW_KEY_KP_8: return ImGuiKey_Keypad8;\n        case GLFW_KEY_KP_9: return ImGuiKey_Keypad9;\n        case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal;\n        case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide;\n        case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;\n        case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract;\n        case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd;\n        case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter;\n        case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual;\n        case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift;\n        case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl;\n        case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt;\n        case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper;\n        case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift;\n        case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl;\n        case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt;\n        case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper;\n        case GLFW_KEY_MENU: return ImGuiKey_Menu;\n        case GLFW_KEY_0: return ImGuiKey_0;\n        case GLFW_KEY_1: return ImGuiKey_1;\n        case GLFW_KEY_2: return ImGuiKey_2;\n        case GLFW_KEY_3: return ImGuiKey_3;\n        case GLFW_KEY_4: return ImGuiKey_4;\n        case GLFW_KEY_5: return ImGuiKey_5;\n        case GLFW_KEY_6: return ImGuiKey_6;\n        case GLFW_KEY_7: return ImGuiKey_7;\n        case GLFW_KEY_8: return ImGuiKey_8;\n        case GLFW_KEY_9: return ImGuiKey_9;\n        case GLFW_KEY_A: return ImGuiKey_A;\n        case GLFW_KEY_B: return ImGuiKey_B;\n        case GLFW_KEY_C: return ImGuiKey_C;\n        case GLFW_KEY_D: return ImGuiKey_D;\n        case GLFW_KEY_E: return ImGuiKey_E;\n        case GLFW_KEY_F: return ImGuiKey_F;\n        case GLFW_KEY_G: return ImGuiKey_G;\n        case GLFW_KEY_H: return ImGuiKey_H;\n        case GLFW_KEY_I: return ImGuiKey_I;\n        case GLFW_KEY_J: return ImGuiKey_J;\n        case GLFW_KEY_K: return ImGuiKey_K;\n        case GLFW_KEY_L: return ImGuiKey_L;\n        case GLFW_KEY_M: return ImGuiKey_M;\n        case GLFW_KEY_N: return ImGuiKey_N;\n        case GLFW_KEY_O: return ImGuiKey_O;\n        case GLFW_KEY_P: return ImGuiKey_P;\n        case GLFW_KEY_Q: return ImGuiKey_Q;\n        case GLFW_KEY_R: return ImGuiKey_R;\n        case GLFW_KEY_S: return ImGuiKey_S;\n        case GLFW_KEY_T: return ImGuiKey_T;\n        case GLFW_KEY_U: return ImGuiKey_U;\n        case GLFW_KEY_V: return ImGuiKey_V;\n        case GLFW_KEY_W: return ImGuiKey_W;\n        case GLFW_KEY_X: return ImGuiKey_X;\n        case GLFW_KEY_Y: return ImGuiKey_Y;\n        case GLFW_KEY_Z: return ImGuiKey_Z;\n        case GLFW_KEY_F1: return ImGuiKey_F1;\n        case GLFW_KEY_F2: return ImGuiKey_F2;\n        case GLFW_KEY_F3: return ImGuiKey_F3;\n        case GLFW_KEY_F4: return ImGuiKey_F4;\n        case GLFW_KEY_F5: return ImGuiKey_F5;\n        case GLFW_KEY_F6: return ImGuiKey_F6;\n        case GLFW_KEY_F7: return ImGuiKey_F7;\n        case GLFW_KEY_F8: return ImGuiKey_F8;\n        case GLFW_KEY_F9: return ImGuiKey_F9;\n        case GLFW_KEY_F10: return ImGuiKey_F10;\n        case GLFW_KEY_F11: return ImGuiKey_F11;\n        case GLFW_KEY_F12: return ImGuiKey_F12;\n        case GLFW_KEY_F13: return ImGuiKey_F13;\n        case GLFW_KEY_F14: return ImGuiKey_F14;\n        case GLFW_KEY_F15: return ImGuiKey_F15;\n        case GLFW_KEY_F16: return ImGuiKey_F16;\n        case GLFW_KEY_F17: return ImGuiKey_F17;\n        case GLFW_KEY_F18: return ImGuiKey_F18;\n        case GLFW_KEY_F19: return ImGuiKey_F19;\n        case GLFW_KEY_F20: return ImGuiKey_F20;\n        case GLFW_KEY_F21: return ImGuiKey_F21;\n        case GLFW_KEY_F22: return ImGuiKey_F22;\n        case GLFW_KEY_F23: return ImGuiKey_F23;\n        case GLFW_KEY_F24: return ImGuiKey_F24;\n        default: return ImGuiKey_None;\n    }\n}\n\n// X11 does not include current pressed/released modifier key in 'mods' flags submitted by GLFW\n// See https://github.com/ocornut/imgui/issues/6034 and https://github.com/glfw/glfw/issues/1630\nstatic void ImGui_ImplGlfw_UpdateKeyModifiers(ImGuiIO& io, GLFWwindow* window, int mods)\n{\n    // IMHEX PATCH BEGIN\n#ifdef __linux__\n    static bool isX11 = [] {\n        const auto sessionType = std::getenv(\"XDG_SESSION_TYPE\");\n        if (sessionType == nullptr)\n            return false;\n\n        return std::string_view(sessionType) == \"x11\";\n    }();\n\n    if (isX11) {\n    io.AddKeyEvent(ImGuiMod_Ctrl,  (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS));\n    io.AddKeyEvent(ImGuiMod_Shift, (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT)   == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT)   == GLFW_PRESS));\n    io.AddKeyEvent(ImGuiMod_Alt,   (glfwGetKey(window, GLFW_KEY_LEFT_ALT)     == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_ALT)     == GLFW_PRESS));\n    io.AddKeyEvent(ImGuiMod_Super, (glfwGetKey(window, GLFW_KEY_LEFT_SUPER)   == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SUPER)   == GLFW_PRESS));\n    } else\n#endif\n    {\n        io.AddKeyEvent(ImGuiMod_Ctrl,  (mods & GLFW_MOD_CONTROL) != 0);\n        io.AddKeyEvent(ImGuiMod_Shift, (mods & GLFW_MOD_SHIFT)   != 0);\n        io.AddKeyEvent(ImGuiMod_Alt,   (mods & GLFW_MOD_ALT)     != 0);\n        io.AddKeyEvent(ImGuiMod_Super, (mods & GLFW_MOD_SUPER)   != 0);\n    }\n    // IMHEX PATCH END\n}\n\nstatic bool ImGui_ImplGlfw_ShouldChainCallback(ImGui_ImplGlfw_Data* bd, GLFWwindow* window)\n{\n    return bd->CallbacksChainForAllWindows ? true : (window == bd->Window);\n}\n\nvoid ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);\n    if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))\n        bd->PrevUserCallbackMousebutton(window, button, action, mods);\n\n    // Workaround for Linux: ignore mouse up events which are following an focus loss following a viewport creation\n    if (bd->MouseIgnoreButtonUp && action == GLFW_RELEASE)\n        return;\n\n    ImGuiIO& io = ImGui::GetIO(bd->Context);\n    // IMHEX PATCH BEGIN\n    ImGui_ImplGlfw_UpdateKeyModifiers(io, window, mods);\n    // IMHEX PATCH END\n    if (button >= 0 && button < ImGuiMouseButton_COUNT)\n        io.AddMouseButtonEvent(button, action == GLFW_PRESS);\n}\n\nvoid ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);\n    if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))\n        bd->PrevUserCallbackScroll(window, xoffset, yoffset);\n\n#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3\n    // Ignore GLFW events: will be processed in ImGui_ImplEmscripten_WheelCallback().\n    return;\n#endif\n\n    ImGuiIO& io = ImGui::GetIO(bd->Context);\n    io.AddMouseWheelEvent((float)xoffset, (float)yoffset);\n}\n\n// FIXME: should this be baked into ImGui_ImplGlfw_KeyToImGuiKey()? then what about the values passed to io.SetKeyEventNativeData()?\nstatic int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)\n{\n#if GLFW_HAS_GETKEYNAME && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3)\n    // GLFW 3.1+ attempts to \"untranslate\" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult.\n    // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently)\n    // See https://github.com/glfw/glfw/issues/1502 for details.\n    // Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process).\n    // This won't cover edge cases but this is at least going to cover common cases.\n    if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL)\n        return key;\n    GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr);\n    const char* key_name = glfwGetKeyName(key, scancode);\n    glfwSetErrorCallback(prev_error_callback);\n#if GLFW_HAS_GETERROR && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) // Eat errors (see #5908)\n    (void)glfwGetError(nullptr);\n#endif\n    if (key_name && key_name[0] != 0 && key_name[1] == 0)\n    {\n        const char char_names[] = \"`-=[]\\\\,;\\'./\";\n        const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 };\n        IM_ASSERT(IM_COUNTOF(char_names) == IM_COUNTOF(char_keys));\n        if (key_name[0] >= '0' && key_name[0] <= '9')               { key = GLFW_KEY_0 + (key_name[0] - '0'); }\n        else if (key_name[0] >= 'A' && key_name[0] <= 'Z')          { key = GLFW_KEY_A + (key_name[0] - 'A'); }\n        else if (key_name[0] >= 'a' && key_name[0] <= 'z')          { key = GLFW_KEY_A + (key_name[0] - 'a'); }\n        else if (const char* p = strchr(char_names, key_name[0]))   { key = char_keys[p - char_names]; }\n    }\n    // if (action == GLFW_PRESS) printf(\"key %d scancode %d name '%s'\\n\", key, scancode, key_name);\n#else\n    IM_UNUSED(scancode);\n#endif\n    return key;\n}\n\nvoid ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);\n    if (bd->PrevUserCallbackKey != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))\n        bd->PrevUserCallbackKey(window, keycode, scancode, action, mods);\n\n    if (action != GLFW_PRESS && action != GLFW_RELEASE)\n        return;\n\n    ImGuiIO& io = ImGui::GetIO(bd->Context);\n    // IMHEX PATCH BEGIN\n    ImGui_ImplGlfw_UpdateKeyModifiers(io, window, mods);\n    // IMHEX PATCH END\n\n    if (keycode >= 0 && keycode < IM_COUNTOF(bd->KeyOwnerWindows))\n        bd->KeyOwnerWindows[keycode] = (action == GLFW_PRESS) ? window : nullptr;\n\n    keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode);\n\n    ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode, scancode);\n    io.AddKeyEvent(imgui_key, (action == GLFW_PRESS));\n    io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code)\n}\n\nvoid ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);\n    if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))\n        bd->PrevUserCallbackWindowFocus(window, focused);\n\n    // Workaround for Linux: when losing focus with MouseIgnoreButtonUpWaitForFocusLoss set, we will temporarily ignore subsequent Mouse Up events\n    bd->MouseIgnoreButtonUp = (bd->MouseIgnoreButtonUpWaitForFocusLoss && focused == 0);\n    bd->MouseIgnoreButtonUpWaitForFocusLoss = false;\n\n    ImGuiIO& io = ImGui::GetIO(bd->Context);\n    io.AddFocusEvent(focused != 0);\n}\n\nvoid ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);\n    if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))\n        bd->PrevUserCallbackCursorPos(window, x, y);\n\n    ImGuiIO& io = ImGui::GetIO(bd->Context);\n    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)\n    {\n        int window_x, window_y;\n        glfwGetWindowPos(window, &window_x, &window_y);\n        x += window_x;\n        y += window_y;\n    }\n    io.AddMousePosEvent((float)x, (float)y);\n    bd->LastValidMousePos = ImVec2((float)x, (float)y);\n}\n\n// Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position,\n// so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984)\nvoid ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);\n    if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))\n        bd->PrevUserCallbackCursorEnter(window, entered);\n\n    ImGuiIO& io = ImGui::GetIO(bd->Context);\n    if (entered)\n    {\n        bd->MouseWindow = window;\n        io.AddMousePosEvent(bd->LastValidMousePos.x, bd->LastValidMousePos.y);\n    }\n    else if (!entered && bd->MouseWindow == window)\n    {\n        bd->LastValidMousePos = io.MousePos;\n        bd->MouseWindow = nullptr;\n        io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);\n    }\n}\n\nvoid ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);\n    if (bd->PrevUserCallbackChar != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window))\n        bd->PrevUserCallbackChar(window, c);\n\n    ImGuiIO& io = ImGui::GetIO(bd->Context);\n    io.AddInputCharacter(c);\n}\n\nvoid ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int)\n{\n    // This function is technically part of the API even if we stopped using the callback, so leaving it around.\n}\n\n#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3\nstatic EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void* user_data)\n{\n    // Mimic Emscripten_HandleWheel() in SDL.\n    // Corresponding equivalent in GLFW JS emulation layer has incorrect quantizing preventing small values. See #6096\n    ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data;\n    float multiplier = 0.0f;\n    if (ev->deltaMode == DOM_DELTA_PIXEL)       { multiplier = 1.0f / 100.0f; } // 100 pixels make up a step.\n    else if (ev->deltaMode == DOM_DELTA_LINE)   { multiplier = 1.0f / 3.0f; }   // 3 lines make up a step.\n    else if (ev->deltaMode == DOM_DELTA_PAGE)   { multiplier = 80.0f; }         // A page makes up 80 steps.\n    float wheel_x = ev->deltaX * -multiplier;\n    float wheel_y = ev->deltaY * -multiplier;\n    ImGuiIO& io = ImGui::GetIO(bd->Context);\n    io.AddMouseWheelEvent(wheel_x, wheel_y);\n    //IMGUI_DEBUG_LOG(\"[Emsc] mode %d dx: %.2f, dy: %.2f, dz: %.2f --> feed %.2f %.2f\\n\", (int)ev->deltaMode, ev->deltaX, ev->deltaY, ev->deltaZ, wheel_x, wheel_y);\n    return EM_TRUE;\n}\n#endif\n\n#ifdef _WIN32\nstatic LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);\n#endif\n\nvoid ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);\n    IM_ASSERT(bd->InstalledCallbacks == false && \"Callbacks already installed!\");\n    IM_ASSERT(bd->Window == window);\n\n    bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback);\n    bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback);\n    bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback);\n    bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback);\n    bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback);\n    bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback);\n    bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback);\n    bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback);\n    bd->InstalledCallbacks = true;\n}\n\nvoid ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);\n    IM_ASSERT(bd->InstalledCallbacks == true && \"Callbacks not installed!\");\n    IM_ASSERT(bd->Window == window);\n\n    glfwSetWindowFocusCallback(window, bd->PrevUserCallbackWindowFocus);\n    glfwSetCursorEnterCallback(window, bd->PrevUserCallbackCursorEnter);\n    glfwSetCursorPosCallback(window, bd->PrevUserCallbackCursorPos);\n    glfwSetMouseButtonCallback(window, bd->PrevUserCallbackMousebutton);\n    glfwSetScrollCallback(window, bd->PrevUserCallbackScroll);\n    glfwSetKeyCallback(window, bd->PrevUserCallbackKey);\n    glfwSetCharCallback(window, bd->PrevUserCallbackChar);\n    glfwSetMonitorCallback(bd->PrevUserCallbackMonitor);\n    bd->InstalledCallbacks = false;\n    bd->PrevUserCallbackWindowFocus = nullptr;\n    bd->PrevUserCallbackCursorEnter = nullptr;\n    bd->PrevUserCallbackCursorPos = nullptr;\n    bd->PrevUserCallbackMousebutton = nullptr;\n    bd->PrevUserCallbackScroll = nullptr;\n    bd->PrevUserCallbackKey = nullptr;\n    bd->PrevUserCallbackChar = nullptr;\n    bd->PrevUserCallbackMonitor = nullptr;\n}\n\n// Set to 'true' to enable chaining installed callbacks for all windows (including secondary viewports created by backends or by user).\n// This is 'false' by default meaning we only chain callbacks for the main viewport.\n// We cannot set this to 'true' by default because user callbacks code may be not testing the 'window' parameter of their callback.\n// If you set this to 'true' your user callback code will need to make sure you are testing the 'window' parameter.\nvoid ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    bd->CallbacksChainForAllWindows = chain_for_all_windows;\n}\n\n#ifdef __EMSCRIPTEN__\n#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817\nvoid ImGui_ImplGlfw_EmscriptenOpenURL(const char* url) { if (url) emscripten::glfw3::OpenURL(url); }\n#else\nEM_JS(void, ImGui_ImplGlfw_EmscriptenOpenURL, (const char* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); });\n#endif\n#endif\n\nstatic bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)\n{\n    ImGuiIO& io = ImGui::GetIO();\n    IMGUI_CHECKVERSION();\n    IM_ASSERT(io.BackendPlatformUserData == nullptr && \"Already initialized a platform backend!\");\n    //printf(\"GLFW_VERSION: %d.%d.%d (%d)\", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION, GLFW_VERSION_COMBINED);\n\n    // Setup backend capabilities flags\n    ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)();\n    snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), \"imgui_impl_glfw (%d)\", GLFW_VERSION_COMBINED);\n    io.BackendPlatformUserData = (void*)bd;\n    io.BackendPlatformName = bd->BackendPlatformName;\n#if GLFW_HAS_CREATECURSOR\n    io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;         // We can honor GetMouseCursor() values (optional)\n#endif\n    io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;          // We can honor io.WantSetMousePos requests (optional, rarely used)\n\n    bool has_viewports = false;\n#ifndef __EMSCRIPTEN__\n    has_viewports = true;\n#if GLFW_HAS_GETPLATFORM\n    if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND)\n        has_viewports = false;\n#endif\n    if (has_viewports)\n        io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports;  // We can create multi-viewports on the Platform side (optional)\n#endif\n#if GLFW_HAS_MOUSE_PASSTHROUGH || GLFW_HAS_WINDOW_HOVERED\n    io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional)\n#endif\n\n    bd->Context = ImGui::GetCurrentContext();\n    bd->Window = window;\n    bd->Time = 0.0;\n    bd->IsWayland = ImGui_ImplGlfw_IsWayland();\n    ImGui_ImplGlfw_ContextMap_Add(window, bd->Context);\n\n    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();\n#if GLFW_VERSION_COMBINED < 3300\n    platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(ImGui_ImplGlfw_GetBackendData()->Window, text); };\n    platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(ImGui_ImplGlfw_GetBackendData()->Window); };\n#else\n    platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(nullptr, text); };\n    platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(nullptr); };\n#endif\n\n#ifdef __EMSCRIPTEN__\n    platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; };\n#endif\n\n// IMHEX PATCH BEGIN\n#ifdef __EMSCRIPTEN__\n    // Callback to handle clipboard paste from browser\n    emscripten_browser_clipboard::paste([](std::string const &paste_data, void *callback_data [[maybe_unused]]) {\n        clipboardContent = std::move(paste_data);\n    });\n#endif\n// IMHEX PATCH END\n\n    // Create mouse cursors\n    // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist,\n    // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting.\n    // Missing cursors will return nullptr and our _UpdateMouseCursor() function will use the Arrow cursor instead.)\n#if GLFW_HAS_CREATECURSOR\n    GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr);\n    bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);\n    bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);\n    bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);\n    bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);\n    bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR);\n#if GLFW_HAS_NEW_CURSORS\n    bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR);\n    bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR);\n    bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR);\n    bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR);\n#else\n    bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);\n    bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);\n    bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);\n    bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);\n#endif\n    glfwSetErrorCallback(prev_error_callback);\n#endif\n#if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908)\n    (void)glfwGetError(nullptr);\n#endif\n\n    // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.\n    if (install_callbacks)\n        ImGui_ImplGlfw_InstallCallbacks(window);\n\n    // Update monitor a first time during init\n    // (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784)\n    ImGui_ImplGlfw_UpdateMonitors();\n    glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback);\n\n    // Set platform dependent data in viewport\n    ImGuiViewport* main_viewport = ImGui::GetMainViewport();\n    main_viewport->PlatformHandle = (void*)bd->Window;\n#ifdef _WIN32\n    main_viewport->PlatformHandleRaw = glfwGetWin32Window(bd->Window);\n#elif defined(__APPLE__)\n    main_viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(bd->Window);\n#else\n    IM_UNUSED(main_viewport);\n#endif\n    if (has_viewports)\n        ImGui_ImplGlfw_InitMultiViewportSupport();\n\n    // Windows: register a WndProc hook so we can intercept some messages.\n#ifdef _WIN32\n    HWND hwnd = (HWND)main_viewport->PlatformHandleRaw;\n    ::SetPropA(hwnd, \"IMGUI_BACKEND_DATA\", bd);\n    bd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW(hwnd, GWLP_WNDPROC);\n    IM_ASSERT(bd->PrevWndProc != nullptr);\n    ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc);\n#endif\n\n#if GLFW_HAS_X11\n    if (!bd->IsWayland)\n    {\n        // Load X11 module dynamically. Copied from the way that GLFW does it in x11_init.c\n#if defined(__CYGWIN__)\n        const char* x11_module_path = \"libX11-6.so\";\n#elif defined(__OpenBSD__) || defined(__NetBSD__)\n        const char* x11_module_path = \"libX11.so\";\n#else\n        const char* x11_module_path = \"libX11.so.6\";\n#endif\n        bd->X11Module = dlopen(x11_module_path, RTLD_LAZY | RTLD_LOCAL);\n        bd->XInternAtom = (PFN_XInternAtom)dlsym(bd->X11Module, \"XInternAtom\");\n        bd->XChangeProperty = (PFN_XChangeProperty)dlsym(bd->X11Module, \"XChangeProperty\");\n        bd->XChangeWindowAttributes = (PFN_XChangeWindowAttributes)dlsym(bd->X11Module, \"XChangeWindowAttributes\");\n        bd->XFlush = (PFN_XFlush)dlsym(bd->X11Module, \"XFlush\");\n        IM_ASSERT(bd->XInternAtom != nullptr && bd->XChangeProperty != nullptr && bd->XChangeWindowAttributes != nullptr && bd->XFlush != nullptr);\n    }\n#endif\n\n    // Emscripten: the same application can run on various platforms, so we detect the Apple platform at runtime\n    // to override io.ConfigMacOSXBehaviors from its default (which is always false in Emscripten).\n#ifdef __EMSCRIPTEN__\n#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817\n    if (emscripten::glfw3::IsRuntimePlatformApple())\n    {\n        io.ConfigMacOSXBehaviors = true;\n\n        // Due to how the browser (poorly) handles the Meta Key, this line essentially disables repeats when used.\n        // This means that Meta + V only registers a single key-press, even if the keys are held.\n        // This is a compromise for dealing with this issue in ImGui since ImGui implements key repeat itself.\n        // See https://github.com/pongasoft/emscripten-glfw/blob/v3.4.0.20240817/docs/Usage.md#the-problem-of-the-super-key\n        emscripten::glfw3::SetSuperPlusKeyTimeouts(10, 10);\n    }\n#endif\n#endif\n\n    bd->ClientApi = client_api;\n    return true;\n}\n\nbool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks)\n{\n    return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL);\n}\n\nbool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks)\n{\n    return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan);\n}\n\nbool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks)\n{\n    return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown);\n}\n\nvoid ImGui_ImplGlfw_Shutdown()\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    IM_ASSERT(bd != nullptr && \"No platform backend to shutdown, or already shutdown?\");\n\n    ImGuiIO& io = ImGui::GetIO();\n    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();\n\n    ImGui_ImplGlfw_ShutdownMultiViewportSupport();\n    if (bd->InstalledCallbacks)\n        ImGui_ImplGlfw_RestoreCallbacks(bd->Window);\n#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3\n    if (bd->CanvasSelector)\n        emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, nullptr);\n#endif\n#if GLFW_HAS_CREATECURSOR\n    for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)\n        glfwDestroyCursor(bd->MouseCursors[cursor_n]);\n#endif\n    // Windows: restore our WndProc hook\n#ifdef _WIN32\n    ImGuiViewport* main_viewport = ImGui::GetMainViewport();\n    ::SetPropA((HWND)main_viewport->PlatformHandleRaw, \"IMGUI_BACKEND_DATA\", nullptr);\n    ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->PrevWndProc);\n    bd->PrevWndProc = nullptr;\n#endif\n\n#if GLFW_HAS_X11\n    if (bd->X11Module != nullptr)\n        dlclose(bd->X11Module);\n#endif\n\n    io.BackendPlatformName = nullptr;\n    io.BackendPlatformUserData = nullptr;\n    io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport);\n    platform_io.ClearPlatformHandlers();\n    ImGui_ImplGlfw_ContextMap_Remove(bd->Window);\n    IM_DELETE(bd);\n}\n\nstatic void ImGui_ImplGlfw_UpdateMouseData()\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    ImGuiIO& io = ImGui::GetIO();\n    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();\n\n    ImGuiID mouse_viewport_id = 0;\n    const ImVec2 mouse_pos_prev = io.MousePos;\n    for (int n = 0; n < platform_io.Viewports.Size; n++)\n    {\n        ImGuiViewport* viewport = platform_io.Viewports[n];\n        GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle;\n\n#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3\n        const bool is_window_focused = true;\n#else\n        const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0;\n#endif\n        if (is_window_focused)\n        {\n            // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)\n            // When multi-viewports are enabled, all Dear ImGui positions are same as OS positions.\n            if (io.WantSetMousePos)\n                glfwSetCursorPos(window, (double)(mouse_pos_prev.x - viewport->Pos.x), (double)(mouse_pos_prev.y - viewport->Pos.y));\n\n            // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured)\n            if (bd->MouseWindow == nullptr)\n            {\n                double mouse_x, mouse_y;\n                glfwGetCursorPos(window, &mouse_x, &mouse_y);\n                if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)\n                {\n                    // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)\n                    // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor)\n                    int window_x, window_y;\n                    glfwGetWindowPos(window, &window_x, &window_y);\n                    mouse_x += window_x;\n                    mouse_y += window_y;\n                }\n                bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y);\n                io.AddMousePosEvent((float)mouse_x, (float)mouse_y);\n            }\n        }\n\n        // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.\n        // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.\n        // - [X] GLFW >= 3.3 backend ON WINDOWS ONLY does correctly ignore viewports with the _NoInputs flag (since we implement hit via our WndProc hook)\n        //       On other platforms we rely on the library fallbacking to its own search when reporting a viewport with _NoInputs flag.\n        // - [!] GLFW <= 3.2 backend CANNOT correctly ignore viewports with the _NoInputs flag, and CANNOT reported Hovered Viewport because of mouse capture.\n        //       Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window\n        //       for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported\n        //       by the backend, and use its flawed heuristic to guess the viewport behind.\n        // - [X] GLFW backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).\n        // FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems.\n        // See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature.\n#if GLFW_HAS_MOUSE_PASSTHROUGH\n        const bool window_no_input = (viewport->Flags & ImGuiViewportFlags_NoInputs) != 0;\n        glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, window_no_input);\n#endif\n#if GLFW_HAS_MOUSE_PASSTHROUGH || GLFW_HAS_WINDOW_HOVERED\n        if (glfwGetWindowAttrib(window, GLFW_HOVERED))\n            mouse_viewport_id = viewport->ID;\n#else\n        // We cannot use bd->MouseWindow maintained from CursorEnter/Leave callbacks, because it is locked to the window capturing mouse.\n#endif\n    }\n\n    if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)\n        io.AddMouseViewportEvent(mouse_viewport_id);\n}\n\nstatic void ImGui_ImplGlfw_UpdateMouseCursor()\n{\n    ImGuiIO& io = ImGui::GetIO();\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)\n        return;\n\n    ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();\n    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();\n    for (int n = 0; n < platform_io.Viewports.Size; n++)\n    {\n        GLFWwindow* window = (GLFWwindow*)platform_io.Viewports[n]->PlatformHandle;\n        if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)\n        {\n            if (bd->LastMouseCursor != nullptr)\n            {\n                // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor\n                glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);\n                bd->LastMouseCursor = nullptr;\n            }\n        }\n        else\n        {\n            // Show OS mouse cursor\n            // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here.\n// IMHEX PATCH BEGIN\n#if GLFW_HAS_CREATECURSOR && !defined(_WIN32)\n// IMHEX PATCH END\n            GLFWcursor* cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow];\n            if (bd->LastMouseCursor != cursor)\n            {\n                glfwSetCursor(window, cursor);\n                bd->LastMouseCursor = cursor;\n            }\n#endif\n            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);\n        }\n    }\n}\n\n// Update gamepad inputs\nstatic inline float Saturate(float v) { return v < 0.0f ? 0.0f : v  > 1.0f ? 1.0f : v; }\nstatic void ImGui_ImplGlfw_UpdateGamepads()\n{\n    ImGuiIO& io = ImGui::GetIO();\n    if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs, but see #8075\n        return;\n\n    io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;\n#if GLFW_HAS_GAMEPAD_API && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3)\n    GLFWgamepadstate gamepad;\n    if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad))\n        return;\n    #define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED)          do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0); } while (0)\n    #define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1)    do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0)\n#else\n    int axes_count = 0, buttons_count = 0;\n    const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count);\n    const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count);\n    if (axes_count == 0 || buttons_count == 0)\n        return;\n    #define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO)          do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS)); } while (0)\n    #define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1)    do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0)\n#endif\n    io.BackendFlags |= ImGuiBackendFlags_HasGamepad;\n    MAP_BUTTON(ImGuiKey_GamepadStart,       GLFW_GAMEPAD_BUTTON_START,          7);\n    MAP_BUTTON(ImGuiKey_GamepadBack,        GLFW_GAMEPAD_BUTTON_BACK,           6);\n    MAP_BUTTON(ImGuiKey_GamepadFaceLeft,    GLFW_GAMEPAD_BUTTON_X,              2);     // Xbox X, PS Square\n    MAP_BUTTON(ImGuiKey_GamepadFaceRight,   GLFW_GAMEPAD_BUTTON_B,              1);     // Xbox B, PS Circle\n    MAP_BUTTON(ImGuiKey_GamepadFaceUp,      GLFW_GAMEPAD_BUTTON_Y,              3);     // Xbox Y, PS Triangle\n    MAP_BUTTON(ImGuiKey_GamepadFaceDown,    GLFW_GAMEPAD_BUTTON_A,              0);     // Xbox A, PS Cross\n    MAP_BUTTON(ImGuiKey_GamepadDpadLeft,    GLFW_GAMEPAD_BUTTON_DPAD_LEFT,      13);\n    MAP_BUTTON(ImGuiKey_GamepadDpadRight,   GLFW_GAMEPAD_BUTTON_DPAD_RIGHT,     11);\n    MAP_BUTTON(ImGuiKey_GamepadDpadUp,      GLFW_GAMEPAD_BUTTON_DPAD_UP,        10);\n    MAP_BUTTON(ImGuiKey_GamepadDpadDown,    GLFW_GAMEPAD_BUTTON_DPAD_DOWN,      12);\n    MAP_BUTTON(ImGuiKey_GamepadL1,          GLFW_GAMEPAD_BUTTON_LEFT_BUMPER,    4);\n    MAP_BUTTON(ImGuiKey_GamepadR1,          GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER,   5);\n    MAP_ANALOG(ImGuiKey_GamepadL2,          GLFW_GAMEPAD_AXIS_LEFT_TRIGGER,     4,      -0.75f,  +1.0f);\n    MAP_ANALOG(ImGuiKey_GamepadR2,          GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER,    5,      -0.75f,  +1.0f);\n    MAP_BUTTON(ImGuiKey_GamepadL3,          GLFW_GAMEPAD_BUTTON_LEFT_THUMB,     8);\n    MAP_BUTTON(ImGuiKey_GamepadR3,          GLFW_GAMEPAD_BUTTON_RIGHT_THUMB,    9);\n    MAP_ANALOG(ImGuiKey_GamepadLStickLeft,  GLFW_GAMEPAD_AXIS_LEFT_X,           0,      -0.25f,  -1.0f);\n    MAP_ANALOG(ImGuiKey_GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X,           0,      +0.25f,  +1.0f);\n    MAP_ANALOG(ImGuiKey_GamepadLStickUp,    GLFW_GAMEPAD_AXIS_LEFT_Y,           1,      -0.25f,  -1.0f);\n    MAP_ANALOG(ImGuiKey_GamepadLStickDown,  GLFW_GAMEPAD_AXIS_LEFT_Y,           1,      +0.25f,  +1.0f);\n    MAP_ANALOG(ImGuiKey_GamepadRStickLeft,  GLFW_GAMEPAD_AXIS_RIGHT_X,          2,      -0.25f,  -1.0f);\n    MAP_ANALOG(ImGuiKey_GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X,          2,      +0.25f,  +1.0f);\n    MAP_ANALOG(ImGuiKey_GamepadRStickUp,    GLFW_GAMEPAD_AXIS_RIGHT_Y,          3,      -0.25f,  -1.0f);\n    MAP_ANALOG(ImGuiKey_GamepadRStickDown,  GLFW_GAMEPAD_AXIS_RIGHT_Y,          3,      +0.25f,  +1.0f);\n    #undef MAP_BUTTON\n    #undef MAP_ANALOG\n}\n\nstatic void ImGui_ImplGlfw_UpdateMonitors()\n{\n    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();\n    int monitors_count = 0;\n    GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count);\n\n    bool updated_monitors = false;\n    for (int n = 0; n < monitors_count; n++)\n    {\n        ImGuiPlatformMonitor monitor;\n        int x, y;\n        glfwGetMonitorPos(glfw_monitors[n], &x, &y);\n        const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]);\n        if (vid_mode == nullptr)\n            continue; // Failed to get Video mode (e.g. Emscripten does not support this function)\n        if (vid_mode->width <= 0 || vid_mode->height <= 0)\n            continue; // Failed to query suitable monitor info (#9195)\n        monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y);\n        monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height);\n#if GLFW_HAS_MONITOR_WORK_AREA\n        int w, h;\n        glfwGetMonitorWorkarea(glfw_monitors[n], &x, &y, &w, &h);\n        if (w > 0 && h > 0) // Workaround a small GLFW issue reporting zero on monitor changes: https://github.com/glfw/glfw/pull/1761\n        {\n            monitor.WorkPos = ImVec2((float)x, (float)y);\n            monitor.WorkSize = ImVec2((float)w, (float)h);\n        }\n#endif\n        float scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfw_monitors[n]);\n        if (scale == 0.0f)\n            continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0 (#7902)\n        monitor.DpiScale = scale;\n        monitor.PlatformHandle = (void*)glfw_monitors[n]; // [...] GLFW doc states: \"guaranteed to be valid only until the monitor configuration changes\"\n\n        // Preserve existing monitor list until a valid one is added.\n        // Happens on macOS sleeping (#5683) and seemingly occasionally on Windows (#9195)\n        if (!updated_monitors)\n            platform_io.Monitors.resize(0);\n        updated_monitors = true;\n        platform_io.Monitors.push_back(monitor);\n    }\n}\n\n// - On Windows the process needs to be marked DPI-aware!! SDL2 doesn't do it by default. You can call ::SetProcessDPIAware() or call ImGui_ImplWin32_EnableDpiAwareness() from Win32 backend.\n// - Apple platforms use FramebufferScale so we always return 1.0f.\n// - Some accessibility applications are declaring virtual monitors with a DPI of 0.0f, see #7902. We preserve this value for caller to handle.\nfloat ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window)\n{\n#if GLFW_HAS_WAYLAND\n    if (ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window))\n        if (bd->IsWayland)\n            return 1.0f;\n#endif\n#if GLFW_HAS_PER_MONITOR_DPI && !(defined(__APPLE__) || defined(__EMSCRIPTEN__) || defined(__ANDROID__))\n    float x_scale, y_scale;\n    glfwGetWindowContentScale(window, &x_scale, &y_scale);\n    return x_scale;\n#else\n    IM_UNUSED(window);\n    return 1.0f;\n#endif\n}\n\nfloat ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor)\n{\n#if GLFW_HAS_WAYLAND\n    if (ImGui_ImplGlfw_IsWayland()) // We can't access our bd->IsWayland cache for a monitor.\n        return 1.0f;\n#endif\n#if GLFW_HAS_PER_MONITOR_DPI && !(defined(__APPLE__) || defined(__EMSCRIPTEN__) || defined(__ANDROID__))\n    float x_scale, y_scale;\n    glfwGetMonitorContentScale(monitor, &x_scale, &y_scale);\n    return x_scale;\n#else\n    IM_UNUSED(monitor);\n    return 1.0f;\n#endif\n}\n\nstatic void ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(GLFWwindow* window, ImVec2* out_size, ImVec2* out_framebuffer_scale)\n{\n    int w, h;\n    int display_w, display_h;\n    glfwGetWindowSize(window, &w, &h);\n    glfwGetFramebufferSize(window, &display_w, &display_h);\n    float fb_scale_x = (w > 0) ? (float)display_w / (float)w : 1.0f;\n    float fb_scale_y = (h > 0) ? (float)display_h / (float)h : 1.0f;\n#if GLFW_HAS_WAYLAND\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window);\n    if (!bd->IsWayland)\n        fb_scale_x = fb_scale_y = 1.0f;\n#endif\n    if (out_size != nullptr)\n        *out_size = ImVec2((float)w, (float)h);\n    if (out_framebuffer_scale != nullptr)\n        *out_framebuffer_scale = ImVec2(fb_scale_x, fb_scale_y);\n}\n\nvoid ImGui_ImplGlfw_NewFrame()\n{\n    ImGuiIO& io = ImGui::GetIO();\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    IM_ASSERT(bd != nullptr && \"Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?\");\n\n    // Setup main viewport size (every frame to accommodate for window resizing)\n    ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(bd->Window, &io.DisplaySize, &io.DisplayFramebufferScale);\n    ImGui_ImplGlfw_UpdateMonitors();\n\n    // Setup time step\n    // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644)\n    double current_time = glfwGetTime();\n    if (current_time <= bd->Time)\n        current_time = bd->Time + 0.00001f;\n    io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);\n    bd->Time = current_time;\n\n    bd->MouseIgnoreButtonUp = false;\n    ImGui_ImplGlfw_UpdateMouseData();\n    ImGui_ImplGlfw_UpdateMouseCursor();\n\n    // Update game controllers (if enabled and available)\n    ImGui_ImplGlfw_UpdateGamepads();\n}\n\n// GLFW doesn't provide a portable sleep function\nvoid ImGui_ImplGlfw_Sleep(int milliseconds)\n{\n#ifdef _WIN32\n    ::Sleep(milliseconds);\n#else\n    usleep(milliseconds * 1000);\n#endif\n}\n\n#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3\nstatic EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data)\n{\n    ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data;\n    double canvas_width, canvas_height;\n    emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height);\n    glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height);\n    return true;\n}\n\nstatic EM_BOOL ImGui_ImplEmscripten_FullscreenChangeCallback(int event_type, const EmscriptenFullscreenChangeEvent* event, void* user_data)\n{\n    ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data;\n    double canvas_width, canvas_height;\n    emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height);\n    glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height);\n    return true;\n}\n\n// 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query.\n// STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID.\nvoid ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow*, const char* canvas_selector)\n{\n    IM_ASSERT(canvas_selector != nullptr);\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    IM_ASSERT(bd != nullptr && \"Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?\");\n\n    bd->CanvasSelector = canvas_selector;\n    emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, bd, false, ImGui_ImplGlfw_OnCanvasSizeChange);\n    emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, bd, false, ImGui_ImplEmscripten_FullscreenChangeCallback);\n\n    // Change the size of the GLFW window according to the size of the canvas\n    ImGui_ImplGlfw_OnCanvasSizeChange(EMSCRIPTEN_EVENT_RESIZE, {}, bd);\n\n    // Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096)\n    // We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves.\n    // FIXME: May break chaining in case user registered their own Emscripten callback?\n    emscripten_set_wheel_callback(bd->CanvasSelector, bd, false, ImGui_ImplEmscripten_WheelCallback);\n}\n#elif defined(EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3)\n// When using --use-port=contrib.glfw3 for the GLFW implementation, you can override the behavior of this call\n// by invoking emscripten_glfw_make_canvas_resizable afterward.\n// See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Usage.md#how-to-make-the-canvas-resizable-by-the-user for an explanation\nvoid ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector)\n{\n  GLFWwindow* w = (GLFWwindow*)(EM_ASM_INT({ return Module.glfwGetWindow(UTF8ToString($0)); }, canvas_selector));\n  IM_ASSERT(window == w); // Sanity check\n  IM_UNUSED(w);\n  emscripten_glfw_make_canvas_resizable(window, \"window\", nullptr);\n}\n#endif // #ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3\n\n\n//--------------------------------------------------------------------------------------------------------\n// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT\n// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.\n// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..\n//--------------------------------------------------------------------------------------------------------\n\n// Helper structure we store in the void* PlatformUserData field of each ImGuiViewport to easily retrieve our backend data.\nstruct ImGui_ImplGlfw_ViewportData\n{\n    GLFWwindow* Window;             // Stored in ImGuiViewport::PlatformHandle\n    bool        WindowOwned;\n    int         IgnoreWindowPosEventFrame;\n    int         IgnoreWindowSizeEventFrame;\n#ifdef _WIN32\n    WNDPROC     PrevWndProc;\n#endif\n\n    ImGui_ImplGlfw_ViewportData()  { memset((void*)this, 0, sizeof(*this)); IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; }\n    ~ImGui_ImplGlfw_ViewportData() { IM_ASSERT(Window == nullptr); }\n};\n\nstatic void ImGui_ImplGlfw_WindowCloseCallback(GLFWwindow* window)\n{\n    if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window))\n        viewport->PlatformRequestClose = true;\n}\n\n// GLFW may dispatch window pos/size events after calling glfwSetWindowPos()/glfwSetWindowSize().\n// However: depending on the platform the callback may be invoked at different time:\n// - on Windows it appears to be called within the glfwSetWindowPos()/glfwSetWindowSize() call\n// - on Linux it is queued and invoked during glfwPollEvents()\n// Because the event doesn't always fire on glfwSetWindowXXX() we use a frame counter tag to only\n// ignore recent glfwSetWindowXXX() calls.\nstatic void ImGui_ImplGlfw_WindowPosCallback(GLFWwindow* window, int, int)\n{\n    if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window))\n    {\n        if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData)\n        {\n            bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowPosEventFrame + 1);\n            //data->IgnoreWindowPosEventFrame = -1;\n            if (ignore_event)\n                return;\n        }\n        viewport->PlatformRequestMove = true;\n    }\n}\n\nstatic void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int)\n{\n    if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window))\n    {\n        if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData)\n        {\n            bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowSizeEventFrame + 1);\n            //data->IgnoreWindowSizeEventFrame = -1;\n            if (ignore_event)\n                return;\n        }\n        viewport->PlatformRequestResize = true;\n    }\n}\n\n#if !defined(__APPLE__) && !defined(_WIN32) && !defined(__EMSCRIPTEN__) && GLFW_HAS_GETPLATFORM\n#define IMGUI_GLFW_HAS_SETWINDOWFLOATING\nstatic void ImGui_ImplGlfw_SetWindowFloating(ImGui_ImplGlfw_Data* bd, GLFWwindow* window)\n{\n#ifdef GLFW_EXPOSE_NATIVE_X11\n    if (glfwGetPlatform() == GLFW_PLATFORM_X11)\n    {\n        Display* display = glfwGetX11Display();\n        Window xwindow = glfwGetX11Window(window);\n        Atom wm_type = bd->XInternAtom(display, \"_NET_WM_WINDOW_TYPE\", False);\n        Atom wm_type_dialog = bd->XInternAtom(display, \"_NET_WM_WINDOW_TYPE_DIALOG\", False);\n        bd->XChangeProperty(display, xwindow, wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&wm_type_dialog, 1);\n        XSetWindowAttributes attrs;\n        attrs.override_redirect = False;\n        bd->XChangeWindowAttributes(display, xwindow, CWOverrideRedirect, &attrs);\n        bd->XFlush(display);\n    }\n#endif // GLFW_EXPOSE_NATIVE_X11\n#ifdef GLFW_EXPOSE_NATIVE_WAYLAND\n    // FIXME: Help needed, see #8884, #8474 for discussions about this.\n#endif // GLFW_EXPOSE_NATIVE_X11\n}\n#endif // IMGUI_GLFW_HAS_SETWINDOWFLOATING\n\nstatic void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)();\n    viewport->PlatformUserData = vd;\n\n    // Workaround for Linux: ignore mouse up events corresponding to losing focus of the previously focused window (#7733, #3158, #7922)\n#ifdef __linux__\n    bd->MouseIgnoreButtonUpWaitForFocusLoss = true;\n#endif\n\n    // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED\n    // With GLFW 3.3, the hint GLFW_FOCUS_ON_SHOW fixes this problem\n    glfwWindowHint(GLFW_VISIBLE, false);\n    glfwWindowHint(GLFW_FOCUSED, false);\n#if GLFW_HAS_FOCUS_ON_SHOW\n    glfwWindowHint(GLFW_FOCUS_ON_SHOW, false);\n#endif\n    glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true);\n#if GLFW_HAS_WINDOW_TOPMOST\n    glfwWindowHint(GLFW_FLOATING, (viewport->Flags & ImGuiViewportFlags_TopMost) ? true : false);\n#endif\n    GLFWwindow* share_window = (bd->ClientApi == GlfwClientApi_OpenGL) ? bd->Window : nullptr;\n    vd->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, \"No Title Yet\", nullptr, share_window);\n    vd->WindowOwned = true;\n    ImGui_ImplGlfw_ContextMap_Add(vd->Window, bd->Context);\n    viewport->PlatformHandle = (void*)vd->Window;\n#ifdef IMGUI_GLFW_HAS_SETWINDOWFLOATING\n    ImGui_ImplGlfw_SetWindowFloating(bd, vd->Window);\n#endif\n#ifdef _WIN32\n    viewport->PlatformHandleRaw = glfwGetWin32Window(vd->Window);\n    ::SetPropA((HWND)viewport->PlatformHandleRaw, \"IMGUI_BACKEND_DATA\", bd);\n// IMHEX PATCH BEGIN\n// REASON: The patch with #include <GLFW/glfw3native.h>\n// #elif defined(__APPLE__)\n//     viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(vd->Window);\n// IMHEX PATCH END\n#endif\n    glfwSetWindowPos(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y);\n\n    // Install GLFW callbacks for secondary viewports\n    glfwSetWindowFocusCallback(vd->Window, ImGui_ImplGlfw_WindowFocusCallback);\n    glfwSetCursorEnterCallback(vd->Window, ImGui_ImplGlfw_CursorEnterCallback);\n    glfwSetCursorPosCallback(vd->Window, ImGui_ImplGlfw_CursorPosCallback);\n    glfwSetMouseButtonCallback(vd->Window, ImGui_ImplGlfw_MouseButtonCallback);\n    glfwSetScrollCallback(vd->Window, ImGui_ImplGlfw_ScrollCallback);\n    glfwSetKeyCallback(vd->Window, ImGui_ImplGlfw_KeyCallback);\n    glfwSetCharCallback(vd->Window, ImGui_ImplGlfw_CharCallback);\n    glfwSetWindowCloseCallback(vd->Window, ImGui_ImplGlfw_WindowCloseCallback);\n    glfwSetWindowPosCallback(vd->Window, ImGui_ImplGlfw_WindowPosCallback);\n    glfwSetWindowSizeCallback(vd->Window, ImGui_ImplGlfw_WindowSizeCallback);\n    if (bd->ClientApi == GlfwClientApi_OpenGL)\n    {\n        glfwMakeContextCurrent(vd->Window);\n        glfwSwapInterval(0);\n    }\n}\n\nstatic void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData)\n    {\n        if (vd->WindowOwned)\n        {\n#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)\n            HWND hwnd = (HWND)viewport->PlatformHandleRaw;\n            ::RemovePropA(hwnd, \"IMGUI_VIEWPORT\");\n#endif\n\n            // Release any keys that were pressed in the window being destroyed and are still held down,\n            // because we will not receive any release events after window is destroyed.\n            for (int i = 0; i < IM_COUNTOF(bd->KeyOwnerWindows); i++)\n                if (bd->KeyOwnerWindows[i] == vd->Window)\n                    ImGui_ImplGlfw_KeyCallback(vd->Window, i, 0, GLFW_RELEASE, 0); // Later params are only used for main viewport, on which this function is never called.\n\n            ImGui_ImplGlfw_ContextMap_Remove(vd->Window);\n            glfwDestroyWindow(vd->Window);\n        }\n        vd->Window = nullptr;\n        IM_DELETE(vd);\n    }\n    viewport->PlatformUserData = viewport->PlatformHandle = nullptr;\n}\n\nstatic void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport)\n{\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n\n#if defined(_WIN32)\n    // GLFW hack: Hide icon from task bar\n    HWND hwnd = (HWND)viewport->PlatformHandleRaw;\n    if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon)\n    {\n        LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);\n        ex_style &= ~WS_EX_APPWINDOW;\n        ex_style |= WS_EX_TOOLWINDOW;\n        ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style);\n    }\n\n    // GLFW hack: install WndProc for mouse source event and WM_NCHITTEST message handler.\n    ::SetPropA(hwnd, \"IMGUI_VIEWPORT\", viewport);\n    vd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW(hwnd, GWLP_WNDPROC);\n    ::SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc);\n\n#if !GLFW_HAS_FOCUS_ON_SHOW\n    // GLFW hack: GLFW 3.2 has a bug where glfwShowWindow() also activates/focus the window.\n    // The fix was pushed to GLFW repository on 2018/01/09 and should be included in GLFW 3.3 via a GLFW_FOCUS_ON_SHOW window attribute.\n    // See https://github.com/glfw/glfw/issues/1189\n    // FIXME-VIEWPORT: Implement same work-around for Linux/OSX in the meanwhile.\n    if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)\n    {\n        ::ShowWindow(hwnd, SW_SHOWNA);\n        return;\n    }\n#endif\n#endif\n\n    glfwShowWindow(vd->Window);\n}\n\nstatic ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport)\n{\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    int x = 0, y = 0;\n    glfwGetWindowPos(vd->Window, &x, &y);\n    return ImVec2((float)x, (float)y);\n}\n\nstatic void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)\n{\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    vd->IgnoreWindowPosEventFrame = ImGui::GetFrameCount();\n    glfwSetWindowPos(vd->Window, (int)pos.x, (int)pos.y);\n}\n\nstatic ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport)\n{\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    int w = 0, h = 0;\n    glfwGetWindowSize(vd->Window, &w, &h);\n    return ImVec2((float)w, (float)h);\n}\n\nstatic void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)\n{\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n#if defined(__APPLE__) && !GLFW_HAS_OSX_WINDOW_POS_FIX\n    // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are\n    // positioned from the upper-left corner. GLFW makes an effort to convert macOS style coordinates, however it\n    // doesn't handle it when changing size. We are manually moving the window in order for changes of size to be based\n    // on the upper-left corner.\n    int x, y, width, height;\n    glfwGetWindowPos(vd->Window, &x, &y);\n    glfwGetWindowSize(vd->Window, &width, &height);\n    glfwSetWindowPos(vd->Window, x, y - height + size.y);\n#endif\n    vd->IgnoreWindowSizeEventFrame = ImGui::GetFrameCount();\n    glfwSetWindowSize(vd->Window, (int)size.x, (int)size.y);\n}\n\nstatic ImVec2 ImGui_ImplGlfw_GetWindowFramebufferScale(ImGuiViewport* viewport)\n{\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    ImVec2 framebuffer_scale;\n    ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(vd->Window, nullptr, &framebuffer_scale);\n    return framebuffer_scale;\n}\n\nstatic void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* title)\n{\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    glfwSetWindowTitle(vd->Window, title);\n}\n\nstatic void ImGui_ImplGlfw_SetWindowFocus(ImGuiViewport* viewport)\n{\n#if GLFW_HAS_FOCUS_WINDOW\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    glfwFocusWindow(vd->Window);\n#else\n    // FIXME: What are the effect of not having this function? At the moment imgui doesn't actually call SetWindowFocus - we set that up ahead, will answer that question later.\n    (void)viewport;\n#endif\n}\n\nstatic bool ImGui_ImplGlfw_GetWindowFocus(ImGuiViewport* viewport)\n{\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    return glfwGetWindowAttrib(vd->Window, GLFW_FOCUSED) != 0;\n}\n\nstatic bool ImGui_ImplGlfw_GetWindowMinimized(ImGuiViewport* viewport)\n{\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    return glfwGetWindowAttrib(vd->Window, GLFW_ICONIFIED) != 0;\n}\n\n#if GLFW_HAS_WINDOW_ALPHA\nstatic void ImGui_ImplGlfw_SetWindowAlpha(ImGuiViewport* viewport, float alpha)\n{\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    glfwSetWindowOpacity(vd->Window, alpha);\n}\n#endif\n\nstatic void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport, void*)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    if (bd->ClientApi == GlfwClientApi_OpenGL)\n        glfwMakeContextCurrent(vd->Window);\n}\n\nstatic void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    if (bd->ClientApi == GlfwClientApi_OpenGL)\n    {\n        glfwMakeContextCurrent(vd->Window);\n        glfwSwapBuffers(vd->Window);\n    }\n}\n\n//--------------------------------------------------------------------------------------------------------\n// Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface)\n//--------------------------------------------------------------------------------------------------------\n\n// Avoid including <vulkan.h> so we can build without it\n#if GLFW_HAS_VULKAN\n#ifndef VULKAN_H_\n#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;\n#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)\n#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object;\n#else\n#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;\n#endif\nVK_DEFINE_HANDLE(VkInstance)\nVK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR)\nstruct VkAllocationCallbacks;\nenum VkResult { VK_RESULT_MAX_ENUM = 0x7FFFFFFF };\n#endif // VULKAN_H_\nextern \"C\" { extern GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); }\nstatic int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface)\n{\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData;\n    IM_UNUSED(bd);\n    IM_ASSERT(bd->ClientApi == GlfwClientApi_Vulkan);\n    VkResult err = glfwCreateWindowSurface((VkInstance)vk_instance, vd->Window, (const VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface);\n    return (int)err;\n}\n#endif // GLFW_HAS_VULKAN\n\nstatic void ImGui_ImplGlfw_InitMultiViewportSupport()\n{\n    // Register platform interface (will be coupled with a renderer interface)\n    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();\n    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();\n    platform_io.Platform_CreateWindow = ImGui_ImplGlfw_CreateWindow;\n    platform_io.Platform_DestroyWindow = ImGui_ImplGlfw_DestroyWindow;\n    platform_io.Platform_ShowWindow = ImGui_ImplGlfw_ShowWindow;\n    platform_io.Platform_SetWindowPos = ImGui_ImplGlfw_SetWindowPos;\n    platform_io.Platform_GetWindowPos = ImGui_ImplGlfw_GetWindowPos;\n    platform_io.Platform_SetWindowSize = ImGui_ImplGlfw_SetWindowSize;\n    platform_io.Platform_GetWindowSize = ImGui_ImplGlfw_GetWindowSize;\n    platform_io.Platform_GetWindowFramebufferScale = ImGui_ImplGlfw_GetWindowFramebufferScale;\n    platform_io.Platform_SetWindowFocus = ImGui_ImplGlfw_SetWindowFocus;\n    platform_io.Platform_GetWindowFocus = ImGui_ImplGlfw_GetWindowFocus;\n    platform_io.Platform_GetWindowMinimized = ImGui_ImplGlfw_GetWindowMinimized;\n    platform_io.Platform_SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle;\n    platform_io.Platform_RenderWindow = ImGui_ImplGlfw_RenderWindow;\n    platform_io.Platform_SwapBuffers = ImGui_ImplGlfw_SwapBuffers;\n#if GLFW_HAS_WINDOW_ALPHA\n    platform_io.Platform_SetWindowAlpha = ImGui_ImplGlfw_SetWindowAlpha;\n#endif\n#if GLFW_HAS_VULKAN\n    platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface;\n#endif\n\n    // Register main window handle (which is owned by the main application, not by us)\n    // 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.\n    ImGuiViewport* main_viewport = ImGui::GetMainViewport();\n    ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)();\n    vd->Window = bd->Window;\n    vd->WindowOwned = false;\n    main_viewport->PlatformUserData = vd;\n    main_viewport->PlatformHandle = (void*)bd->Window;\n}\n\nstatic void ImGui_ImplGlfw_ShutdownMultiViewportSupport()\n{\n    ImGui::DestroyPlatformWindows();\n}\n\n//-----------------------------------------------------------------------------\n\n// WndProc hook (declared here because we will need access to ImGui_ImplGlfw_ViewportData)\n#ifdef _WIN32\nstatic ImGuiMouseSource GetMouseSourceFromMessageExtraInfo()\n{\n    LPARAM extra_info = ::GetMessageExtraInfo();\n    if ((extra_info & 0xFFFFFF80) == 0xFF515700)\n        return ImGuiMouseSource_Pen;\n    if ((extra_info & 0xFFFFFF80) == 0xFF515780)\n        return ImGuiMouseSource_TouchScreen;\n    return ImGuiMouseSource_Mouse;\n}\nstatic LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)\n{\n    ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)::GetPropA(hWnd, \"IMGUI_BACKEND_DATA\");\n    ImGuiIO& io = ImGui::GetIO(bd->Context);\n\n    WNDPROC prev_wndproc = bd->PrevWndProc;\n    ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, \"IMGUI_VIEWPORT\");\n    if (viewport != NULL)\n        if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData)\n            prev_wndproc = vd->PrevWndProc;\n\n    switch (msg)\n    {\n        // GLFW doesn't allow to distinguish Mouse vs TouchScreen vs Pen.\n        // Add support for Win32 (based on imgui_impl_win32), because we rely on _TouchScreen info to trickle inputs differently.\n    case WM_MOUSEMOVE: case WM_NCMOUSEMOVE:\n    case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP:\n    case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP:\n    case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONUP:\n    case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP:\n        io.AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo());\n        break;\n\n        // We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support \"transparent inputs\".\n        // In the meanwhile we implement custom per-platform workarounds here (FIXME-VIEWPORT: Implement same work-around for Linux/OSX!)\n#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED\n    case WM_NCHITTEST:\n    {\n        // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() properly (which is OPTIONAL).\n        // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging.\n        // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in\n        // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system.\n        if (viewport && (viewport->Flags & ImGuiViewportFlags_NoInputs))\n            return HTTRANSPARENT;\n        break;\n    }\n#endif\n    default: break;\n    }\n    return ::CallWindowProcW(prev_wndproc, hWnd, msg, wParam, lParam);\n}\n#endif // #ifdef _WIN32\n\n//-----------------------------------------------------------------------------\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/backend/source/imgui_impl_opengl3.cpp",
    "content": "// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline\n// - Desktop GL: 2.x 3.x 4.x\n// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)\n// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)\n\n// Implemented features:\n//  [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!\n//  [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!]\n//  [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).\n//  [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.\n\n// About WebGL/ES:\n// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES.\n// - This is done automatically on iOS, Android and Emscripten targets.\n// - For other targets, the define needs to be visible from the imgui_impl_opengl3.cpp compilation unit. If unsure, define globally or in imconfig.h.\n\n// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.\n// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.\n// Learn about Dear ImGui:\n// - FAQ                  https://dearimgui.com/faq\n// - Getting Started      https://dearimgui.com/getting-started\n// - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).\n// - Introduction, links and more at the top of imgui.cpp\n\n// CHANGELOG\n// (minor and older changes stripped away, please see git history for details)\n//  2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.\n//  2025-12-11: OpenGL: Fixed embedded loader multiple init/shutdown cycles broken on some platforms. (#8792, #9112)\n//  2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.\n//  2025-07-22: OpenGL: Add and call embedded loader shutdown during ImGui_ImplOpenGL3_Shutdown() to facilitate multiple init/shutdown cycles in same process. (#8792)\n//  2025-07-15: OpenGL: Set GL_UNPACK_ALIGNMENT to 1 before updating textures (#8802) + restore non-WebGL/ES update path that doesn't require a CPU-side copy.\n//  2025-06-11: OpenGL: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplOpenGL3_CreateFontsTexture() and ImGui_ImplOpenGL3_DestroyFontsTexture().\n//  2025-06-04: OpenGL: Made GLES 3.20 contexts not access GL_CONTEXT_PROFILE_MASK nor GL_PRIMITIVE_RESTART. (#8664)\n//  2025-02-18: OpenGL: Lazily reinitialize embedded GL loader for when calling backend from e.g. other DLL boundaries. (#8406)\n//  2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap.\n//  2024-06-28: OpenGL: ImGui_ImplOpenGL3_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748)\n//  2024-05-07: OpenGL: Update loader for Linux to support EGL/GLVND. (#7562)\n//  2024-04-16: OpenGL: Detect ES3 contexts on desktop based on version string, to e.g. avoid calling glPolygonMode() on them. (#7447)\n//  2024-01-09: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load \"libGL.so\" and variants, fixing regression on distros missing a symlink.\n//  2023-11-08: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load \"libGL.so\" instead of \"libGL.so.1\", accommodating for NetBSD systems having only \"libGL.so.3\" available. (#6983)\n//  2023-10-05: OpenGL: Rename symbols in our internal loader so that LTO compilation with another copy of gl3w is possible. (#6875, #6668, #4445)\n//  2023-06-20: OpenGL: Fixed erroneous use glGetIntegerv(GL_CONTEXT_PROFILE_MASK) on contexts lower than 3.2. (#6539, #6333)\n//  2023-05-09: OpenGL: Support for glBindSampler() backup/restore on ES3. (#6375)\n//  2023-04-18: OpenGL: Restore front and back polygon mode separately when supported by context. (#6333)\n//  2023-03-23: OpenGL: Properly restoring \"no shader program bound\" if it was the case prior to running the rendering function. (#6267, #6220, #6224)\n//  2023-03-15: OpenGL: Fixed GL loader crash when GL_VERSION returns nullptr. (#6154, #4445, #3530)\n//  2023-03-06: OpenGL: Fixed restoration of a potentially deleted OpenGL program, by calling glIsProgram(). (#6220, #6224)\n//  2022-11-09: OpenGL: Reverted use of glBufferSubData(), too many corruptions issues + old issues seemingly can't be reproed with Intel drivers nowadays (revert 2021-12-15 and 2022-05-23 changes).\n//  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.\n//  2022-09-27: OpenGL: Added ability to '#define IMGUI_IMPL_OPENGL_DEBUG'.\n//  2022-05-23: OpenGL: Reworking 2021-12-15 \"Using buffer orphaning\" so it only happens on Intel GPU, seems to cause problems otherwise. (#4468, #4825, #4832, #5127).\n//  2022-05-13: OpenGL: Fixed state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states.\n//  2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers.\n//  2021-08-23: OpenGL: Fixed ES 3.0 shader (\"#version 300 es\") use normal precision floats to avoid wobbly rendering at HD resolutions.\n//  2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader.\n//  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).\n//  2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state.\n//  2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader.\n//  2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when \"GL_ARB_clip_control\" extension is detected, inside of just OpenGL 4.5 version.\n//  2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)\n//  2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater.\n//  2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer.\n//  2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state.\n//  2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state.\n//  2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x)\n//  2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre-3.3 context which have the defines set by a loader.\n//  2020-07-10: OpenGL: Added support for glad2 OpenGL loader.\n//  2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.\n//  2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.\n//  2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.\n//  2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.\n//  2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.\n//  2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.\n//  2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.\n//  2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.\n//  2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.\n//  2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.\n//  2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.\n//  2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.\n//  2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).\n//  2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.\n//  2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.\n//  2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).\n//  2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.\n//  2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.\n//  2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.\n//  2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to \"#version 300 ES\".\n//  2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.\n//  2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.\n//  2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.\n//  2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.\n//  2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.\n//  2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a nullptr pointer.\n//  2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. \"#version 150\".\n//  2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.\n//  2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.\n//  2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.\n//  2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.\n//  2017-05-01: OpenGL: Fixed save and restore of current blend func state.\n//  2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.\n//  2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.\n//  2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)\n\n//----------------------------------------\n// OpenGL    GLSL      GLSL\n// version   version   string\n//----------------------------------------\n//  2.0       110       \"#version 110\"\n//  2.1       120       \"#version 120\"\n//  3.0       130       \"#version 130\"\n//  3.1       140       \"#version 140\"\n//  3.2       150       \"#version 150\"\n//  3.3       330       \"#version 330 core\"\n//  4.0       400       \"#version 400 core\"\n//  4.1       410       \"#version 410 core\"\n//  4.2       420       \"#version 410 core\"\n//  4.3       430       \"#version 430 core\"\n//  ES 2.0    100       \"#version 100\"      = WebGL 1.0\n//  ES 3.0    300       \"#version 300 es\"   = WebGL 2.0\n//----------------------------------------\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n// IMHEX PATCH BEGIN\n#if defined(WIN32)\n#include <Windows.h>\n#include <GL/gl.h>\n#endif\n// IMHEX PATCH END\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_impl_opengl3.h\"\n#include <stdio.h>\n#include <stdint.h>     // intptr_t\n#if defined(__APPLE__)\n#include <TargetConditionals.h>\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\" // warning: ignore unknown flags\n#pragma clang diagnostic ignored \"-Wold-style-cast\"         // warning: use of old-style cast\n#pragma clang diagnostic ignored \"-Wsign-conversion\"        // warning: implicit conversion changes signedness\n#pragma clang diagnostic ignored \"-Wunused-macros\"          // warning: macro is not used\n#pragma clang diagnostic ignored \"-Wnonportable-system-include-path\"\n#pragma clang diagnostic ignored \"-Wcast-function-type\"     // warning: cast between incompatible function types (for loader)\n#endif\n#if defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpragmas\"                  // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wunknown-warning-option\"   // warning: unknown warning group 'xxx'\n#pragma GCC diagnostic ignored \"-Wcast-function-type\"       // warning: cast between incompatible function types (for loader)\n#pragma GCC diagnostic ignored \"-Wstrict-overflow\"          // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1\n#endif\n\n// GL includes\n#if defined(IMGUI_IMPL_OPENGL_ES2)\n#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))\n#include <OpenGLES/ES2/gl.h>    // Use GL ES 2\n#else\n#include <GLES2/gl2.h>          // Use GL ES 2\n#endif\n#if defined(__EMSCRIPTEN__)\n#ifndef GL_GLEXT_PROTOTYPES\n#define GL_GLEXT_PROTOTYPES\n#endif\n#include <GLES2/gl2ext.h>\n#endif\n#elif defined(IMGUI_IMPL_OPENGL_ES3)\n#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))\n#include <OpenGLES/ES3/gl.h>    // Use GL ES 3\n#else\n#include <GLES3/gl3.h>          // Use GL ES 3\n#endif\n#elif !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)\n// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.\n// Helper libraries are often used for this purpose! Here we are using our own minimal custom loader based on gl3w.\n// In the rest of your app/engine, you can use another loader of your choice (gl3w, glew, glad, glbinding, glext, glLoadGen, etc.).\n// If you happen to be developing a new feature for this backend (imgui_impl_opengl3.cpp):\n// - You may need to regenerate imgui_impl_opengl3_loader.h to add new symbols. See https://github.com/dearimgui/gl3w_stripped\n//   Typically you would run: python3 ./gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt\n// - You can temporarily use an unstripped version. See https://github.com/dearimgui/gl3w_stripped/releases\n// Changes to this backend using new APIs should be accompanied by a regenerated stripped loader version.\n#define IMGL3W_IMPL\n#define IMGUI_IMPL_OPENGL_LOADER_IMGL3W\n#include \"imgui_impl_opengl3_loader.h\"\n#endif\n\n// Vertex arrays are not supported on ES2/WebGL1 unless Emscripten which uses an extension\n#ifndef IMGUI_IMPL_OPENGL_ES2\n#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\n#elif defined(__EMSCRIPTEN__)\n#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\n#define glBindVertexArray       glBindVertexArrayOES\n#define glGenVertexArrays       glGenVertexArraysOES\n#define glDeleteVertexArrays    glDeleteVertexArraysOES\n#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES\n#endif\n\n// Desktop GL 2.0+ has extension and glPolygonMode() which GL ES and WebGL don't have..\n// A desktop ES context can technically compile fine with our loader, so we also perform a runtime checks\n#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)\n#define IMGUI_IMPL_OPENGL_HAS_EXTENSIONS        // has glGetIntegerv(GL_NUM_EXTENSIONS)\n#define IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE // may have glPolygonMode()\n#endif\n\n// Desktop GL 2.1+ and GL ES 3.0+ have glBindBuffer() with GL_PIXEL_UNPACK_BUFFER target.\n#if !defined(IMGUI_IMPL_OPENGL_ES2)\n#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK\n#endif\n\n// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state\n#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)\n#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART\n#endif\n\n// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.\n#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_2)\n#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET\n#endif\n\n// Desktop GL 3.3+ and GL ES 3.0+ have glBindSampler()\n#if !defined(IMGUI_IMPL_OPENGL_ES2) && (defined(IMGUI_IMPL_OPENGL_ES3) || defined(GL_VERSION_3_3))\n#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER\n#endif\n\n// [Debugging]\n//#define IMGUI_IMPL_OPENGL_DEBUG\n#ifdef IMGUI_IMPL_OPENGL_DEBUG\n#include <stdio.h>\n#define GL_CALL(_CALL)      do { _CALL; GLenum gl_err = glGetError(); if (gl_err != 0) fprintf(stderr, \"GL error 0x%x returned from '%s'.\\n\", gl_err, #_CALL); } while (0)  // Call with error check\n#else\n#define GL_CALL(_CALL)      _CALL   // Call without error check\n#endif\n\n// OpenGL Data\nstruct ImGui_ImplOpenGL3_Data\n{\n    GLuint          GlVersion;               // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)\n    char            GlslVersionString[32];   // Specified by user or detected based on compile time GL settings.\n    bool            GlProfileIsES2;\n    bool            GlProfileIsES3;\n    bool            GlProfileIsCompat;\n    GLint           GlProfileMask;\n    GLint           MaxTextureSize;\n    GLuint          ShaderHandle;\n    GLint           AttribLocationTex;       // Uniforms location\n    // IMHEX PATCH BEGIN\n    GLint           AttribLocationGammaCorrected;\n    // IMHEX PATCH END\n    GLint           AttribLocationProjMtx;\n    GLuint          AttribLocationVtxPos;    // Vertex attributes location\n    GLuint          AttribLocationVtxUV;\n    GLuint          AttribLocationVtxColor;\n    unsigned int    VboHandle, ElementsHandle;\n    GLsizeiptr      VertexBufferSize;\n    GLsizeiptr      IndexBufferSize;\n    bool            HasPolygonMode;\n    bool            HasBindSampler;\n    bool            HasClipOrigin;\n    bool            UseBufferSubData;\n    ImVector<char>  TempBuffer;\n\n    ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts\n// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.\nstatic ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()\n{\n    return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;\n}\n\n// Forward Declarations\nstatic void ImGui_ImplOpenGL3_InitMultiViewportSupport();\nstatic void ImGui_ImplOpenGL3_ShutdownMultiViewportSupport();\n\n// OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only)\n#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\nstruct ImGui_ImplOpenGL3_VtxAttribState\n{\n    GLint   Enabled, Size, Type, Normalized, Stride;\n    GLvoid* Ptr;\n\n    void GetState(GLint index)\n    {\n        glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &Enabled);\n        glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_SIZE, &Size);\n        glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_TYPE, &Type);\n        glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &Normalized);\n        glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &Stride);\n        glGetVertexAttribPointerv(index, GL_VERTEX_ATTRIB_ARRAY_POINTER, &Ptr);\n    }\n    void SetState(GLint index)\n    {\n        glVertexAttribPointer(index, Size, Type, (GLboolean)Normalized, Stride, Ptr);\n        if (Enabled) glEnableVertexAttribArray(index); else glDisableVertexAttribArray(index);\n    }\n};\n#endif\n\n// Not static to allow third-party code to use that if they want to (but undocumented)\nbool ImGui_ImplOpenGL3_InitLoader();\nbool ImGui_ImplOpenGL3_InitLoader()\n{\n    // Lazily initialize our loader if not already done\n    // (to facilitate handling multiple DLL boundaries and multiple context shutdowns we call this from all main entry points)\n#ifdef IMGUI_IMPL_OPENGL_LOADER_IMGL3W\n    if (glGetIntegerv == nullptr && imgl3wInit() != 0)\n    {\n        fprintf(stderr, \"Failed to initialize OpenGL loader!\\n\");\n        return false;\n    }\n#endif\n    return true;\n}\n\nstatic void ImGui_ImplOpenGL3_ShutdownLoader()\n{\n#ifdef IMGUI_IMPL_OPENGL_LOADER_IMGL3W\n    imgl3wShutdown();\n#endif\n}\n\n// Functions\nbool    ImGui_ImplOpenGL3_Init(const char* glsl_version)\n{\n    ImGuiIO& io = ImGui::GetIO();\n    IMGUI_CHECKVERSION();\n    IM_ASSERT(io.BackendRendererUserData == nullptr && \"Already initialized a renderer backend!\");\n\n    // Initialize loader\n    if (!ImGui_ImplOpenGL3_InitLoader())\n        return false;\n\n    // Setup backend capabilities flags\n    ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)();\n    io.BackendRendererUserData = (void*)bd;\n    io.BackendRendererName = \"imgui_impl_opengl3\";\n\n    // Query for GL version (e.g. 320 for GL 3.2)\n    const char* gl_version_str = (const char*)glGetString(GL_VERSION);\n#if defined(IMGUI_IMPL_OPENGL_ES2)\n    // GLES 2\n    bd->GlVersion = 200;\n    bd->GlProfileIsES2 = true;\n    IM_UNUSED(gl_version_str);\n#else\n    // Desktop or GLES 3\n    GLint major = 0;\n    GLint minor = 0;\n    glGetIntegerv(GL_MAJOR_VERSION, &major);\n    glGetIntegerv(GL_MINOR_VERSION, &minor);\n    if (major == 0 && minor == 0)\n        sscanf(gl_version_str, \"%d.%d\", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with \"<major>.<minor>\"\n    bd->GlVersion = (GLuint)(major * 100 + minor * 10);\n    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &bd->MaxTextureSize);\n\n#if defined(IMGUI_IMPL_OPENGL_ES3)\n    bd->GlProfileIsES3 = true;\n#else\n    if (strncmp(gl_version_str, \"OpenGL ES 3\", 11) == 0)\n        bd->GlProfileIsES3 = true;\n#endif\n\n#if defined(GL_CONTEXT_PROFILE_MASK)\n    if (!bd->GlProfileIsES3 && bd->GlVersion >= 320)\n        glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask);\n    bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0;\n#endif\n\n    bd->UseBufferSubData = false;\n    /*\n    // Query vendor to enable glBufferSubData kludge\n#ifdef _WIN32\n    if (const char* vendor = (const char*)glGetString(GL_VENDOR))\n        if (strncmp(vendor, \"Intel\", 5) == 0)\n            bd->UseBufferSubData = true;\n#endif\n    */\n#endif\n\n#ifdef IMGUI_IMPL_OPENGL_DEBUG\n    printf(\"GlVersion = %d, \\\"%s\\\"\\nGlProfileIsCompat = %d\\nGlProfileMask = 0x%X\\nGlProfileIsES2/IsEs3 = %d/%d\\nGL_VENDOR = '%s'\\nGL_RENDERER = '%s'\\n\", bd->GlVersion, gl_version_str, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG]\n#endif\n\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET\n    if (bd->GlVersion >= 320)\n        io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.\n#endif\n    io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures;       // We can honor ImGuiPlatformIO::Textures[] requests during render.\n    io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports;      // We can create multi-viewports on the Renderer side (optional)\n\n    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();\n    platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = (int)bd->MaxTextureSize;\n\n    // Store GLSL version string so we can refer to it later in case we recreate shaders.\n    // Note: GLSL version is NOT the same as GL version. Leave this to nullptr if unsure.\n    if (glsl_version == nullptr)\n    {\n#if defined(IMGUI_IMPL_OPENGL_ES2)\n        glsl_version = \"#version 100\";\n#elif defined(IMGUI_IMPL_OPENGL_ES3)\n        glsl_version = \"#version 300 es\";\n#elif defined(__APPLE__)\n        glsl_version = \"#version 150\";\n#else\n        glsl_version = \"#version 130\";\n#endif\n    }\n    IM_ASSERT((int)strlen(glsl_version) + 2 < IM_COUNTOF(bd->GlslVersionString));\n    strcpy(bd->GlslVersionString, glsl_version);\n    strcat(bd->GlslVersionString, \"\\n\");\n\n    // Make an arbitrary GL call (we don't actually need the result)\n    // IF YOU GET A CRASH HERE: it probably means the OpenGL function loader didn't do its job. Let us know!\n    GLint current_texture;\n    glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);\n\n    // Detect extensions we support\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE\n    bd->HasPolygonMode = (!bd->GlProfileIsES2 && !bd->GlProfileIsES3);\n#endif\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER\n    bd->HasBindSampler = (bd->GlVersion >= 330 || bd->GlProfileIsES3);\n#endif\n    bd->HasClipOrigin = (bd->GlVersion >= 450);\n#ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS\n    GLint num_extensions = 0;\n    glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);\n    for (GLint i = 0; i < num_extensions; i++)\n    {\n        const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i);\n        if (extension != nullptr && strcmp(extension, \"GL_ARB_clip_control\") == 0)\n            bd->HasClipOrigin = true;\n    }\n#endif\n\n    ImGui_ImplOpenGL3_InitMultiViewportSupport();\n\n    return true;\n}\n\nvoid    ImGui_ImplOpenGL3_Shutdown()\n{\n    ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();\n    IM_ASSERT(bd != nullptr && \"No renderer backend to shutdown, or already shutdown?\");\n    ImGuiIO& io = ImGui::GetIO();\n    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();\n\n    ImGui_ImplOpenGL3_ShutdownMultiViewportSupport();\n    ImGui_ImplOpenGL3_DestroyDeviceObjects();\n\n    io.BackendRendererName = nullptr;\n    io.BackendRendererUserData = nullptr;\n    io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures | ImGuiBackendFlags_RendererHasViewports);\n    platform_io.ClearRendererHandlers();\n    IM_DELETE(bd);\n\n    ImGui_ImplOpenGL3_ShutdownLoader();\n}\n\nvoid    ImGui_ImplOpenGL3_NewFrame()\n{\n    ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();\n    IM_ASSERT(bd != nullptr && \"Context or backend not initialized! Did you call ImGui_ImplOpenGL3_Init()?\");\n\n    ImGui_ImplOpenGL3_InitLoader();\n    if (!bd->ShaderHandle)\n        if (!ImGui_ImplOpenGL3_CreateDeviceObjects())\n            IM_ASSERT(0 && \"ImGui_ImplOpenGL3_CreateDeviceObjects() failed!\");\n}\n\n// IMHEX PATCH BEGIN\nstatic bool useFontShaders = false;\nvoid ImGui_ImplOpenGL3_TurnFontShadersOn(const ImDrawList *parent_list, const ImDrawCmd *cmd) {\n    useFontShaders = true;\n}\nvoid ImGui_ImplOpenGL3_TurnFontShadersOff(const ImDrawList *parent_list, const ImDrawCmd *cmd) {\n    useFontShaders = false;\n}\n// IMHEX PATCH END\n\nstatic void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)\n{\n    ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();\n\n    // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill\n    glEnable(GL_BLEND);\n    glBlendEquation(GL_FUNC_ADD);\n    // IMHEX PATCH BEGIN\n#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)\n    if (useFontShaders) {\n        glBlendFuncSeparate(GL_SRC1_COLOR, GL_ONE_MINUS_SRC1_COLOR,GL_ONE, GL_ONE_MINUS_SRC_ALPHA);\n    } else {\n    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);\n    }\n#else\n    // IMHEX PATCH END\n    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);\n    // IMHEX PATCH BEGIN\n#endif\n    // IMHEX PATCH END\n    glDisable(GL_CULL_FACE);\n    glDisable(GL_DEPTH_TEST);\n    glDisable(GL_STENCIL_TEST);\n    glEnable(GL_SCISSOR_TEST);\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART\n    if (!bd->GlProfileIsES3 && bd->GlVersion >= 310)\n        glDisable(GL_PRIMITIVE_RESTART);\n#endif\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE\n    if (bd->HasPolygonMode)\n        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\n#endif\n\n    // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)\n#if defined(GL_CLIP_ORIGIN)\n    bool clip_origin_lower_left = true;\n    if (bd->HasClipOrigin)\n    {\n        GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&current_clip_origin);\n        if (current_clip_origin == GL_UPPER_LEFT)\n            clip_origin_lower_left = false;\n    }\n#endif\n\n    // Setup viewport, orthographic projection matrix\n    // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.\n    GL_CALL(glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height));\n    float L = draw_data->DisplayPos.x;\n    float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;\n    float T = draw_data->DisplayPos.y;\n    float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;\n#if defined(GL_CLIP_ORIGIN)\n    if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left\n#endif\n    const float ortho_projection[4][4] =\n    {\n        { 2.0f/(R-L),   0.0f,         0.0f,   0.0f },\n        { 0.0f,         2.0f/(T-B),   0.0f,   0.0f },\n        { 0.0f,         0.0f,        -1.0f,   0.0f },\n        { (R+L)/(L-R),  (T+B)/(B-T),  0.0f,   1.0f },\n    };\n    glUseProgram(bd->ShaderHandle);\n    glUniform1i(bd->AttribLocationTex, 0);\n    glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);\n    // IMHEX PATCH BEGIN\n    glUniform1i(bd->AttribLocationGammaCorrected, useFontShaders);\n    // IMHEX PATCH END\n\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER\n    if (bd->HasBindSampler)\n        glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise.\n#endif\n\n    (void)vertex_array_object;\n#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\n    glBindVertexArray(vertex_array_object);\n#endif\n\n    // Bind vertex/index buffers and setup attributes for ImDrawVert\n    GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle));\n    GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle));\n    GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxPos));\n    GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxUV));\n    GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxColor));\n    GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos,   2, GL_FLOAT,         GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, pos)));\n    GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV,    2, GL_FLOAT,         GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, uv)));\n    GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col)));\n}\n\n// OpenGL3 Render function.\n// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.\n// This is in order to be able to run within an OpenGL engine that doesn't do so.\nvoid    ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)\n{\n    // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)\n    int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);\n    int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);\n    if (fb_width <= 0 || fb_height <= 0)\n        return;\n\n    ImGui_ImplOpenGL3_InitLoader();\n\n    ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();\n\n    // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.\n    // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).\n    if (draw_data->Textures != nullptr)\n        for (ImTextureData* tex : *draw_data->Textures)\n            if (tex->Status != ImTextureStatus_OK)\n                ImGui_ImplOpenGL3_UpdateTexture(tex);\n\n    // Backup GL state\n    GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);\n    glActiveTexture(GL_TEXTURE0);\n    GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program);\n    GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture);\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER\n    GLuint last_sampler; if (bd->HasBindSampler) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; }\n#endif\n    GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer);\n#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\n    // This is part of VAO on OpenGL 3.0+ and OpenGL ES 3.0+.\n    GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer);\n    ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_pos; last_vtx_attrib_state_pos.GetState(bd->AttribLocationVtxPos);\n    ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_uv; last_vtx_attrib_state_uv.GetState(bd->AttribLocationVtxUV);\n    ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_color; last_vtx_attrib_state_color.GetState(bd->AttribLocationVtxColor);\n#endif\n#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\n    GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);\n#endif\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE\n    GLint last_polygon_mode[2]; if (bd->HasPolygonMode) { glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); }\n#endif\n    GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);\n    GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);\n    GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);\n    GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);\n    GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);\n    GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);\n    GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);\n    GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);\n    GLboolean last_enable_blend = glIsEnabled(GL_BLEND);\n    GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);\n    GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);\n    GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST);\n    GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART\n    GLboolean last_enable_primitive_restart = (!bd->GlProfileIsES3 && bd->GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE;\n#endif\n\n    // Setup desired GL state\n    // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)\n    // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.\n    GLuint vertex_array_object = 0;\n#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\n    GL_CALL(glGenVertexArrays(1, &vertex_array_object));\n#endif\n    ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);\n\n    // Will project scissor/clipping rectangles into framebuffer space\n    ImVec2 clip_off = draw_data->DisplayPos;         // (0,0) unless using multi-viewports\n    ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)\n\n    // Render command lists\n    for (const ImDrawList* draw_list : draw_data->CmdLists)\n    {\n        // Upload vertex/index buffers\n        // - OpenGL drivers are in a very sorry state nowadays....\n        //   During 2021 we attempted to switch from glBufferData() to orphaning+glBufferSubData() following reports\n        //   of leaks on Intel GPU when using multi-viewports on Windows.\n        // - After this we kept hearing of various display corruptions issues. We started disabling on non-Intel GPU, but issues still got reported on Intel.\n        // - We are now back to using exclusively glBufferData(). So bd->UseBufferSubData IS ALWAYS FALSE in this code.\n        //   We are keeping the old code path for a while in case people finding new issues may want to test the bd->UseBufferSubData path.\n        // - See https://github.com/ocornut/imgui/issues/4468 and please report any corruption issues.\n        const GLsizeiptr vtx_buffer_size = (GLsizeiptr)draw_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);\n        const GLsizeiptr idx_buffer_size = (GLsizeiptr)draw_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);\n        if (bd->UseBufferSubData)\n        {\n            if (bd->VertexBufferSize < vtx_buffer_size)\n            {\n                bd->VertexBufferSize = vtx_buffer_size;\n                GL_CALL(glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, nullptr, GL_STREAM_DRAW));\n            }\n            if (bd->IndexBufferSize < idx_buffer_size)\n            {\n                bd->IndexBufferSize = idx_buffer_size;\n                GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, nullptr, GL_STREAM_DRAW));\n            }\n            GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)draw_list->VtxBuffer.Data));\n            GL_CALL(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)draw_list->IdxBuffer.Data));\n        }\n        else\n        {\n            GL_CALL(glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)draw_list->VtxBuffer.Data, GL_STREAM_DRAW));\n            GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)draw_list->IdxBuffer.Data, GL_STREAM_DRAW));\n        }\n\n        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)\n        {\n            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];\n            if (pcmd->UserCallback != nullptr)\n            {\n                // User callback, registered via ImDrawList::AddCallback()\n                // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)\n                if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)\n                    ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);\n                else\n                    pcmd->UserCallback(draw_list, pcmd);\n            }\n            else\n            {\n                // Project scissor/clipping rectangles into framebuffer space\n                ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);\n                ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);\n                if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)\n                    continue;\n\n                // Apply scissor/clipping rectangle (Y is inverted in OpenGL)\n                GL_CALL(glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y)));\n\n                // Bind texture, Draw\n                GL_CALL(glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()));\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET\n                if (bd->GlVersion >= 320)\n                    GL_CALL(glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset));\n                else\n#endif\n                GL_CALL(glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))));\n            }\n        }\n    }\n\n    // Destroy the temporary VAO\n#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\n    GL_CALL(glDeleteVertexArrays(1, &vertex_array_object));\n#endif\n\n    // Restore modified GL state\n    // This \"glIsProgram()\" check is required because if the program is \"pending deletion\" at the time of binding backup, it will have been deleted by now and will cause an OpenGL error. See #6220.\n    if (last_program == 0 || glIsProgram(last_program)) glUseProgram(last_program);\n    glBindTexture(GL_TEXTURE_2D, last_texture);\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER\n    if (bd->HasBindSampler)\n        glBindSampler(0, last_sampler);\n#endif\n    glActiveTexture(last_active_texture);\n#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\n    glBindVertexArray(last_vertex_array_object);\n#endif\n    glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);\n#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\n    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer);\n    last_vtx_attrib_state_pos.SetState(bd->AttribLocationVtxPos);\n    last_vtx_attrib_state_uv.SetState(bd->AttribLocationVtxUV);\n    last_vtx_attrib_state_color.SetState(bd->AttribLocationVtxColor);\n#endif\n    glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);\n    glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);\n    if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);\n    if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);\n    if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);\n    if (last_enable_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST);\n    if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART\n    if (!bd->GlProfileIsES3 && bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }\n#endif\n\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE\n    // Desktop OpenGL 3.0 and OpenGL 3.1 had separate polygon draw modes for front-facing and back-facing faces of polygons\n    if (bd->HasPolygonMode) { if (bd->GlVersion <= 310 || bd->GlProfileIsCompat) { glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); } else { glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); } }\n#endif // IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE\n\n    glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);\n    glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);\n    (void)bd; // Not all compilation paths use this\n}\n\nstatic void ImGui_ImplOpenGL3_DestroyTexture(ImTextureData* tex)\n{\n    GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID;\n    glDeleteTextures(1, &gl_tex_id);\n\n    // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)\n    tex->SetTexID(ImTextureID_Invalid);\n    tex->SetStatus(ImTextureStatus_Destroyed);\n}\n\nvoid ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex)\n{\n    // FIXME: Consider backing up and restoring\n    if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates)\n    {\n#ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES\n        GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));\n#endif\n#ifdef GL_UNPACK_ALIGNMENT\n        GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));\n#endif\n    }\n\n    if (tex->Status == ImTextureStatus_WantCreate)\n    {\n        // Create and upload new texture to graphics system\n        //IMGUI_DEBUG_LOG(\"UpdateTexture #%03d: WantCreate %dx%d\\n\", tex->UniqueID, tex->Width, tex->Height);\n        IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);\n        IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);\n        const void* pixels = tex->GetPixels();\n        GLuint gl_texture_id = 0;\n\n        // Upload texture to graphics system\n        // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)\n        GLint last_texture;\n        GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));\n        GL_CALL(glGenTextures(1, &gl_texture_id));\n        GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_texture_id));\n        GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));\n        GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));\n        GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));\n        GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));\n        GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->Width, tex->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));\n\n        // Store identifiers\n        tex->SetTexID((ImTextureID)(intptr_t)gl_texture_id);\n        tex->SetStatus(ImTextureStatus_OK);\n\n        // Restore state\n        GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture));\n    }\n    else if (tex->Status == ImTextureStatus_WantUpdates)\n    {\n        // Update selected blocks. We only ever write to textures regions which have never been used before!\n        // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.\n        GLint last_texture;\n        GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));\n\n        GLuint gl_tex_id = (GLuint)(intptr_t)tex->TexID;\n        GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_tex_id));\n#if GL_UNPACK_ROW_LENGTH // Not on WebGL/ES\n        GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->Width));\n        for (ImTextureRect& r : tex->Updates)\n            GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, tex->GetPixelsAt(r.x, r.y)));\n        GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));\n#else\n        // GL ES doesn't have GL_UNPACK_ROW_LENGTH, so we need to (A) copy to a contiguous buffer or (B) upload line by line.\n        ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();\n        for (ImTextureRect& r : tex->Updates)\n        {\n            const int src_pitch = r.w * tex->BytesPerPixel;\n            bd->TempBuffer.resize(r.h * src_pitch);\n            char* out_p = bd->TempBuffer.Data;\n            for (int y = 0; y < r.h; y++, out_p += src_pitch)\n                memcpy(out_p, tex->GetPixelsAt(r.x, r.y + y), src_pitch);\n            IM_ASSERT(out_p == bd->TempBuffer.end());\n            GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, r.x, r.y, r.w, r.h, GL_RGBA, GL_UNSIGNED_BYTE, bd->TempBuffer.Data));\n        }\n#endif\n        tex->SetStatus(ImTextureStatus_OK);\n        GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); // Restore state\n    }\n    else if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0)\n        ImGui_ImplOpenGL3_DestroyTexture(tex);\n}\n\n// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.\nstatic bool CheckShader(GLuint handle, const char* desc)\n{\n    ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();\n    GLint status = 0, log_length = 0;\n    glGetShaderiv(handle, GL_COMPILE_STATUS, &status);\n    glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);\n    if ((GLboolean)status == GL_FALSE)\n        fprintf(stderr, \"ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s! With GLSL: %s\\n\", desc, bd->GlslVersionString);\n    if (log_length > 1)\n    {\n        ImVector<char> buf;\n        buf.resize((int)(log_length + 1));\n        glGetShaderInfoLog(handle, log_length, nullptr, (GLchar*)buf.begin());\n        fprintf(stderr, \"%s\\n\", buf.begin());\n    }\n    return (GLboolean)status == GL_TRUE;\n}\n\n// If you get an error please report on GitHub. You may try different GL context version or GLSL version.\nstatic bool CheckProgram(GLuint handle, const char* desc)\n{\n    ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();\n    GLint status = 0, log_length = 0;\n    glGetProgramiv(handle, GL_LINK_STATUS, &status);\n    glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);\n    if ((GLboolean)status == GL_FALSE)\n        fprintf(stderr, \"ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! With GLSL %s\\n\", desc, bd->GlslVersionString);\n    if (log_length > 1)\n    {\n        ImVector<char> buf;\n        buf.resize((int)(log_length + 1));\n        glGetProgramInfoLog(handle, log_length, nullptr, (GLchar*)buf.begin());\n        fprintf(stderr, \"%s\\n\", buf.begin());\n    }\n    return (GLboolean)status == GL_TRUE;\n}\n\nbool    ImGui_ImplOpenGL3_CreateDeviceObjects()\n{\n    ImGui_ImplOpenGL3_InitLoader();\n    ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();\n\n    // Backup GL state\n    GLint last_texture, last_array_buffer;\n    glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);\n    glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK\n    GLint last_pixel_unpack_buffer = 0;\n    if (bd->GlVersion >= 210) { glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &last_pixel_unpack_buffer); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); }\n#endif\n#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\n    GLint last_vertex_array;\n    glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);\n#endif\n\n    // Parse GLSL version string\n    int glsl_version = 130;\n    sscanf(bd->GlslVersionString, \"#version %d\", &glsl_version);\n\n    const GLchar* vertex_shader_glsl_120 =\n        \"uniform mat4 ProjMtx;\\n\"\n        \"attribute vec2 Position;\\n\"\n        \"attribute vec2 UV;\\n\"\n        \"attribute vec4 Color;\\n\"\n        \"varying vec2 Frag_UV;\\n\"\n        \"varying vec4 Frag_Color;\\n\"\n        \"void main()\\n\"\n        \"{\\n\"\n        \"    Frag_UV = UV;\\n\"\n        \"    Frag_Color = Color;\\n\"\n        \"    gl_Position = ProjMtx * vec4(Position.xy,0,1);\\n\"\n        \"}\\n\";\n\n    const GLchar* vertex_shader_glsl_130 =\n        \"uniform mat4 ProjMtx;\\n\"\n        \"in vec2 Position;\\n\"\n        \"in vec2 UV;\\n\"\n        \"in vec4 Color;\\n\"\n        \"out vec2 Frag_UV;\\n\"\n        \"out vec4 Frag_Color;\\n\"\n        \"void main()\\n\"\n        \"{\\n\"\n        \"    Frag_UV = UV;\\n\"\n        \"    Frag_Color = Color;\\n\"\n        \"    gl_Position = ProjMtx * vec4(Position.xy,0,1);\\n\"\n        \"}\\n\";\n\n    const GLchar* vertex_shader_glsl_300_es =\n        \"precision highp float;\\n\"\n        \"layout (location = 0) in vec2 Position;\\n\"\n        \"layout (location = 1) in vec2 UV;\\n\"\n        \"layout (location = 2) in vec4 Color;\\n\"\n        \"uniform mat4 ProjMtx;\\n\"\n        \"out vec2 Frag_UV;\\n\"\n        \"out vec4 Frag_Color;\\n\"\n        \"void main()\\n\"\n        \"{\\n\"\n        \"    Frag_UV = UV;\\n\"\n        \"    Frag_Color = Color;\\n\"\n        \"    gl_Position = ProjMtx * vec4(Position.xy,0,1);\\n\"\n        \"}\\n\";\n\n    const GLchar* vertex_shader_glsl_410_core =\n        \"layout (location = 0) in vec2 Position;\\n\"\n        \"layout (location = 1) in vec2 UV;\\n\"\n        \"layout (location = 2) in vec4 Color;\\n\"\n        \"uniform mat4 ProjMtx;\\n\"\n        \"out vec2 Frag_UV;\\n\"\n        \"out vec4 Frag_Color;\\n\"\n        \"void main()\\n\"\n        \"{\\n\"\n        \"    Frag_UV = UV;\\n\"\n        \"    Frag_Color = Color;\\n\"\n        \"    gl_Position = ProjMtx * vec4(Position.xy,0,1);\\n\"\n        \"}\\n\";\n\n    const GLchar* fragment_shader_glsl_120 =\n        \"#ifdef GL_ES\\n\"\n        \"    precision mediump float;\\n\"\n        \"#endif\\n\"\n        \"uniform sampler2D Texture;\\n\"\n        \"varying vec2 Frag_UV;\\n\"\n        \"varying vec4 Frag_Color;\\n\"\n        \"void main()\\n\"\n        \"{\\n\"\n        \"    gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\\n\"\n        \"}\\n\";\n\n    const GLchar* fragment_shader_glsl_130 =\n        \"uniform sampler2D Texture;\\n\"\n        \"in vec2 Frag_UV;\\n\"\n        \"in vec4 Frag_Color;\\n\"\n        \"out vec4 Out_Color;\\n\"\n        \"void main()\\n\"\n        \"{\\n\"\n        \"    Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\\n\"\n        \"}\\n\";\n\n    const GLchar* fragment_shader_glsl_300_es =\n        \"precision mediump float;\\n\"\n        \"uniform sampler2D Texture;\\n\"\n        \"in vec2 Frag_UV;\\n\"\n        \"in vec4 Frag_Color;\\n\"\n        \"layout (location = 0) out vec4 Out_Color;\\n\"\n        \"void main()\\n\"\n        \"{\\n\"\n        \"    Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\\n\"\n        \"}\\n\";\n\n    const GLchar* fragment_shader_glsl_410_core =\n        \"in vec2 Frag_UV;\\n\"\n        \"in vec4 Frag_Color;\\n\"\n        \"uniform sampler2D Texture;\\n\"\n        \"layout (location = 0) out vec4 Out_Color;\\n\"\n        // IMHEX PATCH BEGIN\n        \"layout (location = 0, index = 1) out vec4 SRC1_Color;\\n\"\n        \"uniform bool GammaCorrected;\\n\"\n        // IMHEX PATCH END\n        \"void main()\\n\"\n        \"{\\n\"\n        // IMHEX PATCH BEGIN\n        \"    if (!GammaCorrected)\\n\"\n        \"    Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\\n\"\n        \"    else {\\n\"\n        \"       Out_Color = Frag_Color;\\n\"\n        \"       SRC1_Color = vec4(texture(Texture, Frag_UV.st).rgb * Frag_Color.aaa,1.0);\\n\"\n        \"    }\\n\"\n        // IMHEX PATCH END\n        \"}\\n\";\n\n    // Select shaders matching our GLSL versions\n    const GLchar* vertex_shader = nullptr;\n    const GLchar* fragment_shader = nullptr;\n    if (glsl_version < 130)\n    {\n        vertex_shader = vertex_shader_glsl_120;\n        fragment_shader = fragment_shader_glsl_120;\n    }\n    else if (glsl_version >= 410)\n    {\n        vertex_shader = vertex_shader_glsl_410_core;\n        fragment_shader = fragment_shader_glsl_410_core;\n    }\n    else if (glsl_version == 300)\n    {\n        vertex_shader = vertex_shader_glsl_300_es;\n        fragment_shader = fragment_shader_glsl_300_es;\n    }\n    else\n    {\n        vertex_shader = vertex_shader_glsl_130;\n        fragment_shader = fragment_shader_glsl_130;\n    }\n\n    // Create shaders\n    const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader };\n    GLuint vert_handle;\n    GL_CALL(vert_handle = glCreateShader(GL_VERTEX_SHADER));\n    glShaderSource(vert_handle, 2, vertex_shader_with_version, nullptr);\n    glCompileShader(vert_handle);\n    if (!CheckShader(vert_handle, \"vertex shader\"))\n        return false;\n\n    const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader };\n    GLuint frag_handle;\n    GL_CALL(frag_handle = glCreateShader(GL_FRAGMENT_SHADER));\n    glShaderSource(frag_handle, 2, fragment_shader_with_version, nullptr);\n    glCompileShader(frag_handle);\n    if (!CheckShader(frag_handle, \"fragment shader\"))\n        return false;\n\n    // Link\n    bd->ShaderHandle = glCreateProgram();\n    glAttachShader(bd->ShaderHandle, vert_handle);\n    glAttachShader(bd->ShaderHandle, frag_handle);\n    glLinkProgram(bd->ShaderHandle);\n    if (!CheckProgram(bd->ShaderHandle, \"shader program\"))\n        return false;\n\n    glDetachShader(bd->ShaderHandle, vert_handle);\n    glDetachShader(bd->ShaderHandle, frag_handle);\n    glDeleteShader(vert_handle);\n    glDeleteShader(frag_handle);\n\n    // IMHEX PATCH BEGIN\n    bd->AttribLocationGammaCorrected = (GLuint)glGetUniformLocation(bd->ShaderHandle, \"GammaCorrected\");\n    // IMHEX PATCH END\n    bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, \"Texture\");\n    bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, \"ProjMtx\");\n    bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, \"Position\");\n    bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, \"UV\");\n    bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, \"Color\");\n\n    // Create buffers\n    glGenBuffers(1, &bd->VboHandle);\n    glGenBuffers(1, &bd->ElementsHandle);\n\n    // Restore modified GL state\n    glBindTexture(GL_TEXTURE_2D, last_texture);\n    glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);\n#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK\n    if (bd->GlVersion >= 210) { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, last_pixel_unpack_buffer); }\n#endif\n#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY\n    glBindVertexArray(last_vertex_array);\n#endif\n\n    return true;\n}\n\nvoid    ImGui_ImplOpenGL3_DestroyDeviceObjects()\n{\n    ImGui_ImplOpenGL3_InitLoader();\n    ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();\n    if (bd->VboHandle)      { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; }\n    if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; }\n    if (bd->ShaderHandle)   { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; }\n\n    // Destroy all textures\n    for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)\n        if (tex->RefCount == 1)\n            ImGui_ImplOpenGL3_DestroyTexture(tex);\n}\n\n//--------------------------------------------------------------------------------------------------------\n// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT\n// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.\n// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..\n//--------------------------------------------------------------------------------------------------------\n\nstatic void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*)\n{\n    if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))\n    {\n        ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);\n        glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);\n        glClear(GL_COLOR_BUFFER_BIT);\n    }\n    ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData);\n}\n\nstatic void ImGui_ImplOpenGL3_InitMultiViewportSupport()\n{\n    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();\n    platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow;\n}\n\nstatic void ImGui_ImplOpenGL3_ShutdownMultiViewportSupport()\n{\n    ImGui::DestroyPlatformWindows();\n}\n\n//-----------------------------------------------------------------------------\n\n#if defined(__GNUC__)\n#pragma GCC diagnostic pop\n#endif\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/cimgui/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\n\n# https://github.com/cimgui/cimgui\nproject(imgui_cimgui)\n\nset(CMAKE_CXX_STANDARD 23)\n\nif (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)\n    add_library(imgui_cimgui OBJECT\n        source/cimgui.cpp\n    )\n\n    target_include_directories(imgui_cimgui PUBLIC\n        include\n    )\n\n    target_link_libraries(imgui_cimgui PRIVATE imgui_includes)\n    add_dependencies(imhex_all imgui_cimgui)\nendif()\n\n"
  },
  {
    "path": "lib/third_party/imgui/cimgui/include/cimgui.h",
    "content": "//This file is automatically generated by generator.lua from https://github.com/cimgui/cimgui\n//based on imgui.h file version \"1.92.0\" 19200 from Dear ImGui https://github.com/ocornut/imgui\n//with imgui_internal.h api\n//with imgui_freetype.h api\n//docking branch\n\n#ifndef CIMGUI_INCLUDED\n#define CIMGUI_INCLUDED\n#include <stdio.h>\n#include <stdint.h>\n\n#if defined _WIN32 || defined __CYGWIN__\n    #ifdef CIMGUI_NO_EXPORT\n        #define API\n    #else\n        #define API __declspec(dllexport)\n    #endif\n#else\n    #ifdef __GNUC__\n        #define API  __attribute__((__visibility__(\"default\")))\n    #else\n        #define API\n    #endif\n#endif\n\n#if defined __cplusplus\n    #define EXTERN extern \"C\"\n#else\n    #include <stdarg.h>\n    #include <stdbool.h>\n    #define EXTERN extern\n#endif\n\n#define CIMGUI_API EXTERN API\n#define CONST const\n\n\n#ifdef _MSC_VER\ntypedef unsigned __int64 ImU64;\n#else\n//typedef unsigned long long ImU64;\n#endif\n\n#ifndef CIMGUI_DEFINE_ENUMS_AND_STRUCTS\n#ifdef IMGUI_ENABLE_FREETYPE\n#include \"misc/freetype/imgui_freetype.h\"\n#endif\n#endif\n\n\n#ifdef CIMGUI_DEFINE_ENUMS_AND_STRUCTS\n\ntypedef struct ImDrawChannel ImDrawChannel;\ntypedef struct ImDrawCmd ImDrawCmd;\ntypedef struct ImDrawData ImDrawData;\ntypedef struct ImDrawList ImDrawList;\ntypedef struct ImDrawListSharedData ImDrawListSharedData;\ntypedef struct ImDrawListSplitter ImDrawListSplitter;\ntypedef struct ImDrawVert ImDrawVert;\ntypedef struct ImFont ImFont;\ntypedef struct ImFontAtlas ImFontAtlas;\ntypedef struct ImFontAtlasBuilder ImFontAtlasBuilder;\ntypedef struct ImFontAtlasRect ImFontAtlasRect;\ntypedef struct ImFontBaked ImFontBaked;\ntypedef struct ImFontConfig ImFontConfig;\ntypedef struct ImFontGlyph ImFontGlyph;\ntypedef struct ImFontGlyphRangesBuilder ImFontGlyphRangesBuilder;\ntypedef struct ImFontLoader ImFontLoader;\ntypedef struct ImTextureData ImTextureData;\ntypedef struct ImTextureRect ImTextureRect;\ntypedef struct ImColor ImColor;\ntypedef struct ImGuiContext ImGuiContext;\ntypedef struct ImGuiIO ImGuiIO;\ntypedef struct ImGuiInputTextCallbackData ImGuiInputTextCallbackData;\ntypedef struct ImGuiKeyData ImGuiKeyData;\ntypedef struct ImGuiListClipper ImGuiListClipper;\ntypedef struct ImGuiMultiSelectIO ImGuiMultiSelectIO;\ntypedef struct ImGuiOnceUponAFrame ImGuiOnceUponAFrame;\ntypedef struct ImGuiPayload ImGuiPayload;\ntypedef struct ImGuiPlatformIO ImGuiPlatformIO;\ntypedef struct ImGuiPlatformImeData ImGuiPlatformImeData;\ntypedef struct ImGuiPlatformMonitor ImGuiPlatformMonitor;\ntypedef struct ImGuiSelectionBasicStorage ImGuiSelectionBasicStorage;\ntypedef struct ImGuiSelectionExternalStorage ImGuiSelectionExternalStorage;\ntypedef struct ImGuiSelectionRequest ImGuiSelectionRequest;\ntypedef struct ImGuiSizeCallbackData ImGuiSizeCallbackData;\ntypedef struct ImGuiStorage ImGuiStorage;\ntypedef struct ImGuiStoragePair ImGuiStoragePair;\ntypedef struct ImGuiStyle ImGuiStyle;\ntypedef struct ImGuiTableSortSpecs ImGuiTableSortSpecs;\ntypedef struct ImGuiTableColumnSortSpecs ImGuiTableColumnSortSpecs;\ntypedef struct ImGuiTextBuffer ImGuiTextBuffer;\ntypedef struct ImGuiTextFilter ImGuiTextFilter;\ntypedef struct ImGuiViewport ImGuiViewport;\ntypedef struct ImGuiWindowClass ImGuiWindowClass;\ntypedef struct ImBitVector ImBitVector;\ntypedef struct ImRect ImRect;\ntypedef struct ImGuiTextIndex ImGuiTextIndex;\ntypedef struct ImDrawDataBuilder ImDrawDataBuilder;\ntypedef struct ImFontAtlasPostProcessData ImFontAtlasPostProcessData;\ntypedef struct ImFontAtlasRectEntry ImFontAtlasRectEntry;\ntypedef struct ImGuiBoxSelectState ImGuiBoxSelectState;\ntypedef struct ImGuiColorMod ImGuiColorMod;\ntypedef struct ImGuiContextHook ImGuiContextHook;\ntypedef struct ImGuiDataTypeInfo ImGuiDataTypeInfo;\ntypedef struct ImGuiDeactivatedItemData ImGuiDeactivatedItemData;\ntypedef struct ImGuiDockContext ImGuiDockContext;\ntypedef struct ImGuiDockRequest ImGuiDockRequest;\ntypedef struct ImGuiDockNode ImGuiDockNode;\ntypedef struct ImGuiDockNodeSettings ImGuiDockNodeSettings;\ntypedef struct ImGuiErrorRecoveryState ImGuiErrorRecoveryState;\ntypedef struct ImGuiGroupData ImGuiGroupData;\ntypedef struct ImGuiInputTextState ImGuiInputTextState;\ntypedef struct ImGuiInputTextDeactivateData ImGuiInputTextDeactivateData;\ntypedef struct ImGuiLastItemData ImGuiLastItemData;\ntypedef struct ImGuiLocEntry ImGuiLocEntry;\ntypedef struct ImGuiMenuColumns ImGuiMenuColumns;\ntypedef struct ImGuiMultiSelectState ImGuiMultiSelectState;\ntypedef struct ImGuiMultiSelectTempData ImGuiMultiSelectTempData;\ntypedef struct ImGuiNavItemData ImGuiNavItemData;\ntypedef struct ImGuiMetricsConfig ImGuiMetricsConfig;\ntypedef struct ImGuiNextWindowData ImGuiNextWindowData;\ntypedef struct ImGuiNextItemData ImGuiNextItemData;\ntypedef struct ImGuiOldColumnData ImGuiOldColumnData;\ntypedef struct ImGuiOldColumns ImGuiOldColumns;\ntypedef struct ImGuiPopupData ImGuiPopupData;\ntypedef struct ImGuiSettingsHandler ImGuiSettingsHandler;\ntypedef struct ImGuiStyleMod ImGuiStyleMod;\ntypedef struct ImGuiStyleVarInfo ImGuiStyleVarInfo;\ntypedef struct ImGuiTabBar ImGuiTabBar;\ntypedef struct ImGuiTabItem ImGuiTabItem;\ntypedef struct ImGuiTable ImGuiTable;\ntypedef struct ImGuiTableHeaderData ImGuiTableHeaderData;\ntypedef struct ImGuiTableColumn ImGuiTableColumn;\ntypedef struct ImGuiTableInstanceData ImGuiTableInstanceData;\ntypedef struct ImGuiTableTempData ImGuiTableTempData;\ntypedef struct ImGuiTableSettings ImGuiTableSettings;\ntypedef struct ImGuiTableColumnsSettings ImGuiTableColumnsSettings;\ntypedef struct ImGuiTreeNodeStackData ImGuiTreeNodeStackData;\ntypedef struct ImGuiTypingSelectState ImGuiTypingSelectState;\ntypedef struct ImGuiTypingSelectRequest ImGuiTypingSelectRequest;\ntypedef struct ImGuiWindow ImGuiWindow;\ntypedef struct ImGuiWindowDockStyle ImGuiWindowDockStyle;\ntypedef struct ImGuiWindowTempData ImGuiWindowTempData;\ntypedef struct ImGuiWindowSettings ImGuiWindowSettings;\ntypedef struct STB_TexteditState STB_TexteditState;\ntypedef struct stbrp_node stbrp_node;\ntypedef struct ImVector_const_charPtr {int Size;int Capacity;const char** Data;} ImVector_const_charPtr;\n\ntypedef unsigned int ImGuiID;\ntypedef signed char ImS8;\ntypedef unsigned char ImU8;\ntypedef signed short ImS16;\ntypedef unsigned short ImU16;\ntypedef signed int ImS32;\ntypedef unsigned int ImU32;\ntypedef signed long long ImS64;\ntypedef unsigned long long ImU64;\nstruct ImDrawChannel;\nstruct ImDrawCmd;\nstruct ImDrawData;\nstruct ImDrawList;\nstruct ImDrawListSharedData;\nstruct ImDrawListSplitter;\nstruct ImDrawVert;\nstruct ImFont;\nstruct ImFontAtlas;\nstruct ImFontAtlasBuilder;\nstruct ImFontAtlasRect;\nstruct ImFontBaked;\nstruct ImFontConfig;\nstruct ImFontGlyph;\nstruct ImFontGlyphRangesBuilder;\nstruct ImFontLoader;\nstruct ImTextureData;\nstruct ImTextureRect;\nstruct ImColor;\nstruct ImGuiContext;\nstruct ImGuiIO;\nstruct ImGuiInputTextCallbackData;\nstruct ImGuiKeyData;\nstruct ImGuiListClipper;\nstruct ImGuiMultiSelectIO;\nstruct ImGuiOnceUponAFrame;\nstruct ImGuiPayload;\nstruct ImGuiPlatformIO;\nstruct ImGuiPlatformImeData;\nstruct ImGuiPlatformMonitor;\nstruct ImGuiSelectionBasicStorage;\nstruct ImGuiSelectionExternalStorage;\nstruct ImGuiSelectionRequest;\nstruct ImGuiSizeCallbackData;\nstruct ImGuiStorage;\nstruct ImGuiStoragePair;\nstruct ImGuiStyle;\nstruct ImGuiTableSortSpecs;\nstruct ImGuiTableColumnSortSpecs;\nstruct ImGuiTextBuffer;\nstruct ImGuiTextFilter;\nstruct ImGuiViewport;\nstruct ImGuiWindowClass;\ntypedef int ImGuiCol;\ntypedef int ImGuiCond;\ntypedef int ImGuiDataType;\ntypedef int ImGuiMouseButton;\ntypedef int ImGuiMouseCursor;\ntypedef int ImGuiStyleVar;\ntypedef int ImGuiTableBgTarget;\ntypedef int ImDrawFlags;\ntypedef int ImDrawListFlags;\ntypedef int ImFontFlags;\ntypedef int ImFontAtlasFlags;\ntypedef int ImGuiBackendFlags;\ntypedef int ImGuiButtonFlags;\ntypedef int ImGuiChildFlags;\ntypedef int ImGuiColorEditFlags;\ntypedef int ImGuiConfigFlags;\ntypedef int ImGuiComboFlags;\ntypedef int ImGuiDockNodeFlags;\ntypedef int ImGuiDragDropFlags;\ntypedef int ImGuiFocusedFlags;\ntypedef int ImGuiHoveredFlags;\ntypedef int ImGuiInputFlags;\ntypedef int ImGuiInputTextFlags;\ntypedef int ImGuiItemFlags;\ntypedef int ImGuiKeyChord;\ntypedef int ImGuiPopupFlags;\ntypedef int ImGuiMultiSelectFlags;\ntypedef int ImGuiSelectableFlags;\ntypedef int ImGuiSliderFlags;\ntypedef int ImGuiTabBarFlags;\ntypedef int ImGuiTabItemFlags;\ntypedef int ImGuiTableFlags;\ntypedef int ImGuiTableColumnFlags;\ntypedef int ImGuiTableRowFlags;\ntypedef int ImGuiTreeNodeFlags;\ntypedef int ImGuiViewportFlags;\ntypedef int ImGuiWindowFlags;\ntypedef unsigned int ImWchar32;\ntypedef unsigned short ImWchar16;\n\n#ifdef IMGUI_USE_WCHAR32\ntypedef ImWchar32 ImWchar;\n#else\ntypedef ImWchar16 ImWchar;\n#endif\n#ifdef IMGUI_USE_WCHAR32\n#define IM_UNICODE_CODEPOINT_MAX     0x10FFFF\n#else\n#define IM_UNICODE_CODEPOINT_MAX     0xFFFF\n#endif\n\ntypedef ImS64 ImGuiSelectionUserData;\ntypedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data);\ntypedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data);\ntypedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data);\ntypedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data);\ntypedef struct ImVec2 ImVec2;\nstruct ImVec2\n{\n    float x, y;\n};\ntypedef struct ImVec4 ImVec4;\nstruct ImVec4\n{\n    float x, y, z, w;\n};\ntypedef ImU64 ImTextureID;\ntypedef struct ImTextureRef ImTextureRef;\nstruct ImTextureRef\n{\n    ImTextureData* _TexData;\n    ImTextureID _TexID;\n};\ntypedef enum {\n    ImGuiWindowFlags_None = 0,\n    ImGuiWindowFlags_NoTitleBar = 1 << 0,\n    ImGuiWindowFlags_NoResize = 1 << 1,\n    ImGuiWindowFlags_NoMove = 1 << 2,\n    ImGuiWindowFlags_NoScrollbar = 1 << 3,\n    ImGuiWindowFlags_NoScrollWithMouse = 1 << 4,\n    ImGuiWindowFlags_NoCollapse = 1 << 5,\n    ImGuiWindowFlags_AlwaysAutoResize = 1 << 6,\n    ImGuiWindowFlags_NoBackground = 1 << 7,\n    ImGuiWindowFlags_NoSavedSettings = 1 << 8,\n    ImGuiWindowFlags_NoMouseInputs = 1 << 9,\n    ImGuiWindowFlags_MenuBar = 1 << 10,\n    ImGuiWindowFlags_HorizontalScrollbar = 1 << 11,\n    ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12,\n    ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13,\n    ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14,\n    ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15,\n    ImGuiWindowFlags_NoNavInputs = 1 << 16,\n    ImGuiWindowFlags_NoNavFocus = 1 << 17,\n    ImGuiWindowFlags_UnsavedDocument = 1 << 18,\n    ImGuiWindowFlags_NoDocking = 1 << 19,\n    ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,\n    ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse,\n    ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,\n    ImGuiWindowFlags_DockNodeHost = 1 << 23,\n    ImGuiWindowFlags_ChildWindow = 1 << 24,\n    ImGuiWindowFlags_Tooltip = 1 << 25,\n    ImGuiWindowFlags_Popup = 1 << 26,\n    ImGuiWindowFlags_Modal = 1 << 27,\n    ImGuiWindowFlags_ChildMenu = 1 << 28,\n}ImGuiWindowFlags_;\ntypedef enum {\n    ImGuiChildFlags_None = 0,\n    ImGuiChildFlags_Borders = 1 << 0,\n    ImGuiChildFlags_AlwaysUseWindowPadding = 1 << 1,\n    ImGuiChildFlags_ResizeX = 1 << 2,\n    ImGuiChildFlags_ResizeY = 1 << 3,\n    ImGuiChildFlags_AutoResizeX = 1 << 4,\n    ImGuiChildFlags_AutoResizeY = 1 << 5,\n    ImGuiChildFlags_AlwaysAutoResize = 1 << 6,\n    ImGuiChildFlags_FrameStyle = 1 << 7,\n    ImGuiChildFlags_NavFlattened = 1 << 8,\n}ImGuiChildFlags_;\ntypedef enum {\n    ImGuiItemFlags_None = 0,\n    ImGuiItemFlags_NoTabStop = 1 << 0,\n    ImGuiItemFlags_NoNav = 1 << 1,\n    ImGuiItemFlags_NoNavDefaultFocus = 1 << 2,\n    ImGuiItemFlags_ButtonRepeat = 1 << 3,\n    ImGuiItemFlags_AutoClosePopups = 1 << 4,\n    ImGuiItemFlags_AllowDuplicateId = 1 << 5,\n}ImGuiItemFlags_;\ntypedef enum {\n    ImGuiInputTextFlags_None = 0,\n    ImGuiInputTextFlags_CharsDecimal = 1 << 0,\n    ImGuiInputTextFlags_CharsHexadecimal = 1 << 1,\n    ImGuiInputTextFlags_CharsScientific = 1 << 2,\n    ImGuiInputTextFlags_CharsUppercase = 1 << 3,\n    ImGuiInputTextFlags_CharsNoBlank = 1 << 4,\n    ImGuiInputTextFlags_AllowTabInput = 1 << 5,\n    ImGuiInputTextFlags_EnterReturnsTrue = 1 << 6,\n    ImGuiInputTextFlags_EscapeClearsAll = 1 << 7,\n    ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 8,\n    ImGuiInputTextFlags_ReadOnly = 1 << 9,\n    ImGuiInputTextFlags_Password = 1 << 10,\n    ImGuiInputTextFlags_AlwaysOverwrite = 1 << 11,\n    ImGuiInputTextFlags_AutoSelectAll = 1 << 12,\n    ImGuiInputTextFlags_ParseEmptyRefVal = 1 << 13,\n    ImGuiInputTextFlags_DisplayEmptyRefVal = 1 << 14,\n    ImGuiInputTextFlags_NoHorizontalScroll = 1 << 15,\n    ImGuiInputTextFlags_NoUndoRedo = 1 << 16,\n    ImGuiInputTextFlags_ElideLeft = 1 << 17,\n    ImGuiInputTextFlags_CallbackCompletion = 1 << 18,\n    ImGuiInputTextFlags_CallbackHistory = 1 << 19,\n    ImGuiInputTextFlags_CallbackAlways = 1 << 20,\n    ImGuiInputTextFlags_CallbackCharFilter = 1 << 21,\n    ImGuiInputTextFlags_CallbackResize = 1 << 22,\n    ImGuiInputTextFlags_CallbackEdit = 1 << 23,\n}ImGuiInputTextFlags_;\ntypedef enum {\n    ImGuiTreeNodeFlags_None = 0,\n    ImGuiTreeNodeFlags_Selected = 1 << 0,\n    ImGuiTreeNodeFlags_Framed = 1 << 1,\n    ImGuiTreeNodeFlags_AllowOverlap = 1 << 2,\n    ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3,\n    ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4,\n    ImGuiTreeNodeFlags_DefaultOpen = 1 << 5,\n    ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6,\n    ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7,\n    ImGuiTreeNodeFlags_Leaf = 1 << 8,\n    ImGuiTreeNodeFlags_Bullet = 1 << 9,\n    ImGuiTreeNodeFlags_FramePadding = 1 << 10,\n    ImGuiTreeNodeFlags_SpanAvailWidth = 1 << 11,\n    ImGuiTreeNodeFlags_SpanFullWidth = 1 << 12,\n    ImGuiTreeNodeFlags_SpanLabelWidth = 1 << 13,\n    ImGuiTreeNodeFlags_SpanAllColumns = 1 << 14,\n    ImGuiTreeNodeFlags_LabelSpanAllColumns = 1 << 15,\n    ImGuiTreeNodeFlags_NavLeftJumpsToParent = 1 << 17,\n    ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog,\n    ImGuiTreeNodeFlags_DrawLinesNone = 1 << 18,\n    ImGuiTreeNodeFlags_DrawLinesFull = 1 << 19,\n    ImGuiTreeNodeFlags_DrawLinesToNodes = 1 << 20,\n}ImGuiTreeNodeFlags_;\ntypedef enum {\n    ImGuiPopupFlags_None = 0,\n    ImGuiPopupFlags_MouseButtonLeft = 0,\n    ImGuiPopupFlags_MouseButtonRight = 1,\n    ImGuiPopupFlags_MouseButtonMiddle = 2,\n    ImGuiPopupFlags_MouseButtonMask_ = 0x1F,\n    ImGuiPopupFlags_MouseButtonDefault_ = 1,\n    ImGuiPopupFlags_NoReopen = 1 << 5,\n    ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 7,\n    ImGuiPopupFlags_NoOpenOverItems = 1 << 8,\n    ImGuiPopupFlags_AnyPopupId = 1 << 10,\n    ImGuiPopupFlags_AnyPopupLevel = 1 << 11,\n    ImGuiPopupFlags_AnyPopup = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel,\n}ImGuiPopupFlags_;\ntypedef enum {\n    ImGuiSelectableFlags_None = 0,\n    ImGuiSelectableFlags_NoAutoClosePopups = 1 << 0,\n    ImGuiSelectableFlags_SpanAllColumns = 1 << 1,\n    ImGuiSelectableFlags_AllowDoubleClick = 1 << 2,\n    ImGuiSelectableFlags_Disabled = 1 << 3,\n    ImGuiSelectableFlags_AllowOverlap = 1 << 4,\n    ImGuiSelectableFlags_Highlight = 1 << 5,\n}ImGuiSelectableFlags_;\ntypedef enum {\n    ImGuiComboFlags_None = 0,\n    ImGuiComboFlags_PopupAlignLeft = 1 << 0,\n    ImGuiComboFlags_HeightSmall = 1 << 1,\n    ImGuiComboFlags_HeightRegular = 1 << 2,\n    ImGuiComboFlags_HeightLarge = 1 << 3,\n    ImGuiComboFlags_HeightLargest = 1 << 4,\n    ImGuiComboFlags_NoArrowButton = 1 << 5,\n    ImGuiComboFlags_NoPreview = 1 << 6,\n    ImGuiComboFlags_WidthFitPreview = 1 << 7,\n    ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest,\n}ImGuiComboFlags_;\ntypedef enum {\n    ImGuiTabBarFlags_None = 0,\n    ImGuiTabBarFlags_Reorderable = 1 << 0,\n    ImGuiTabBarFlags_AutoSelectNewTabs = 1 << 1,\n    ImGuiTabBarFlags_TabListPopupButton = 1 << 2,\n    ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 1 << 3,\n    ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4,\n    ImGuiTabBarFlags_NoTooltip = 1 << 5,\n    ImGuiTabBarFlags_DrawSelectedOverline = 1 << 6,\n    ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 7,\n    ImGuiTabBarFlags_FittingPolicyScroll = 1 << 8,\n    ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll,\n    ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyResizeDown,\n}ImGuiTabBarFlags_;\ntypedef enum {\n    ImGuiTabItemFlags_None = 0,\n    ImGuiTabItemFlags_UnsavedDocument = 1 << 0,\n    ImGuiTabItemFlags_SetSelected = 1 << 1,\n    ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2,\n    ImGuiTabItemFlags_NoPushId = 1 << 3,\n    ImGuiTabItemFlags_NoTooltip = 1 << 4,\n    ImGuiTabItemFlags_NoReorder = 1 << 5,\n    ImGuiTabItemFlags_Leading = 1 << 6,\n    ImGuiTabItemFlags_Trailing = 1 << 7,\n    ImGuiTabItemFlags_NoAssumedClosure = 1 << 8,\n}ImGuiTabItemFlags_;\ntypedef enum {\n    ImGuiFocusedFlags_None = 0,\n    ImGuiFocusedFlags_ChildWindows = 1 << 0,\n    ImGuiFocusedFlags_RootWindow = 1 << 1,\n    ImGuiFocusedFlags_AnyWindow = 1 << 2,\n    ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3,\n    ImGuiFocusedFlags_DockHierarchy = 1 << 4,\n    ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows,\n}ImGuiFocusedFlags_;\ntypedef enum {\n    ImGuiHoveredFlags_None = 0,\n    ImGuiHoveredFlags_ChildWindows = 1 << 0,\n    ImGuiHoveredFlags_RootWindow = 1 << 1,\n    ImGuiHoveredFlags_AnyWindow = 1 << 2,\n    ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3,\n    ImGuiHoveredFlags_DockHierarchy = 1 << 4,\n    ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5,\n    ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7,\n    ImGuiHoveredFlags_AllowWhenOverlappedByItem = 1 << 8,\n    ImGuiHoveredFlags_AllowWhenOverlappedByWindow = 1 << 9,\n    ImGuiHoveredFlags_AllowWhenDisabled = 1 << 10,\n    ImGuiHoveredFlags_NoNavOverride = 1 << 11,\n    ImGuiHoveredFlags_AllowWhenOverlapped = ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenOverlappedByWindow,\n    ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped,\n    ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows,\n    ImGuiHoveredFlags_ForTooltip = 1 << 12,\n    ImGuiHoveredFlags_Stationary = 1 << 13,\n    ImGuiHoveredFlags_DelayNone = 1 << 14,\n    ImGuiHoveredFlags_DelayShort = 1 << 15,\n    ImGuiHoveredFlags_DelayNormal = 1 << 16,\n    ImGuiHoveredFlags_NoSharedDelay = 1 << 17,\n}ImGuiHoveredFlags_;\ntypedef enum {\n    ImGuiDockNodeFlags_None = 0,\n    ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0,\n    ImGuiDockNodeFlags_NoDockingOverCentralNode = 1 << 2,\n    ImGuiDockNodeFlags_PassthruCentralNode = 1 << 3,\n    ImGuiDockNodeFlags_NoDockingSplit = 1 << 4,\n    ImGuiDockNodeFlags_NoResize = 1 << 5,\n    ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6,\n    ImGuiDockNodeFlags_NoUndocking = 1 << 7,\n}ImGuiDockNodeFlags_;\ntypedef enum {\n    ImGuiDragDropFlags_None = 0,\n    ImGuiDragDropFlags_SourceNoPreviewTooltip = 1 << 0,\n    ImGuiDragDropFlags_SourceNoDisableHover = 1 << 1,\n    ImGuiDragDropFlags_SourceNoHoldToOpenOthers = 1 << 2,\n    ImGuiDragDropFlags_SourceAllowNullID = 1 << 3,\n    ImGuiDragDropFlags_SourceExtern = 1 << 4,\n    ImGuiDragDropFlags_PayloadAutoExpire = 1 << 5,\n    ImGuiDragDropFlags_PayloadNoCrossContext = 1 << 6,\n    ImGuiDragDropFlags_PayloadNoCrossProcess = 1 << 7,\n    ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10,\n    ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11,\n    ImGuiDragDropFlags_AcceptNoPreviewTooltip = 1 << 12,\n    ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect,\n}ImGuiDragDropFlags_;\ntypedef enum {\n    ImGuiDataType_S8,\n    ImGuiDataType_U8,\n    ImGuiDataType_S16,\n    ImGuiDataType_U16,\n    ImGuiDataType_S32,\n    ImGuiDataType_U32,\n    ImGuiDataType_S64,\n    ImGuiDataType_U64,\n    ImGuiDataType_Float,\n    ImGuiDataType_Double,\n    ImGuiDataType_Bool,\n    ImGuiDataType_String,\n    ImGuiDataType_COUNT\n}ImGuiDataType_;\ntypedef enum {\nImGuiDir_None=-1,\nImGuiDir_Left=0,\nImGuiDir_Right=1,\nImGuiDir_Up=2,\nImGuiDir_Down=3,\nImGuiDir_COUNT=4,\n}ImGuiDir;\ntypedef enum {\nImGuiSortDirection_None=0,\nImGuiSortDirection_Ascending=1,\nImGuiSortDirection_Descending=2,\n}ImGuiSortDirection;\ntypedef enum {\nImGuiKey_None=0,\nImGuiKey_NamedKey_BEGIN=512,\nImGuiKey_Tab=512,\nImGuiKey_LeftArrow=513,\nImGuiKey_RightArrow=514,\nImGuiKey_UpArrow=515,\nImGuiKey_DownArrow=516,\nImGuiKey_PageUp=517,\nImGuiKey_PageDown=518,\nImGuiKey_Home=519,\nImGuiKey_End=520,\nImGuiKey_Insert=521,\nImGuiKey_Delete=522,\nImGuiKey_Backspace=523,\nImGuiKey_Space=524,\nImGuiKey_Enter=525,\nImGuiKey_Escape=526,\nImGuiKey_LeftCtrl=527,\nImGuiKey_LeftShift=528,\nImGuiKey_LeftAlt=529,\nImGuiKey_LeftSuper=530,\nImGuiKey_RightCtrl=531,\nImGuiKey_RightShift=532,\nImGuiKey_RightAlt=533,\nImGuiKey_RightSuper=534,\nImGuiKey_Menu=535,\nImGuiKey_0=536,\nImGuiKey_1=537,\nImGuiKey_2=538,\nImGuiKey_3=539,\nImGuiKey_4=540,\nImGuiKey_5=541,\nImGuiKey_6=542,\nImGuiKey_7=543,\nImGuiKey_8=544,\nImGuiKey_9=545,\nImGuiKey_A=546,\nImGuiKey_B=547,\nImGuiKey_C=548,\nImGuiKey_D=549,\nImGuiKey_E=550,\nImGuiKey_F=551,\nImGuiKey_G=552,\nImGuiKey_H=553,\nImGuiKey_I=554,\nImGuiKey_J=555,\nImGuiKey_K=556,\nImGuiKey_L=557,\nImGuiKey_M=558,\nImGuiKey_N=559,\nImGuiKey_O=560,\nImGuiKey_P=561,\nImGuiKey_Q=562,\nImGuiKey_R=563,\nImGuiKey_S=564,\nImGuiKey_T=565,\nImGuiKey_U=566,\nImGuiKey_V=567,\nImGuiKey_W=568,\nImGuiKey_X=569,\nImGuiKey_Y=570,\nImGuiKey_Z=571,\nImGuiKey_F1=572,\nImGuiKey_F2=573,\nImGuiKey_F3=574,\nImGuiKey_F4=575,\nImGuiKey_F5=576,\nImGuiKey_F6=577,\nImGuiKey_F7=578,\nImGuiKey_F8=579,\nImGuiKey_F9=580,\nImGuiKey_F10=581,\nImGuiKey_F11=582,\nImGuiKey_F12=583,\nImGuiKey_F13=584,\nImGuiKey_F14=585,\nImGuiKey_F15=586,\nImGuiKey_F16=587,\nImGuiKey_F17=588,\nImGuiKey_F18=589,\nImGuiKey_F19=590,\nImGuiKey_F20=591,\nImGuiKey_F21=592,\nImGuiKey_F22=593,\nImGuiKey_F23=594,\nImGuiKey_F24=595,\nImGuiKey_Apostrophe=596,\nImGuiKey_Comma=597,\nImGuiKey_Minus=598,\nImGuiKey_Period=599,\nImGuiKey_Slash=600,\nImGuiKey_Semicolon=601,\nImGuiKey_Equal=602,\nImGuiKey_LeftBracket=603,\nImGuiKey_Backslash=604,\nImGuiKey_RightBracket=605,\nImGuiKey_GraveAccent=606,\nImGuiKey_CapsLock=607,\nImGuiKey_ScrollLock=608,\nImGuiKey_NumLock=609,\nImGuiKey_PrintScreen=610,\nImGuiKey_Pause=611,\nImGuiKey_Keypad0=612,\nImGuiKey_Keypad1=613,\nImGuiKey_Keypad2=614,\nImGuiKey_Keypad3=615,\nImGuiKey_Keypad4=616,\nImGuiKey_Keypad5=617,\nImGuiKey_Keypad6=618,\nImGuiKey_Keypad7=619,\nImGuiKey_Keypad8=620,\nImGuiKey_Keypad9=621,\nImGuiKey_KeypadDecimal=622,\nImGuiKey_KeypadDivide=623,\nImGuiKey_KeypadMultiply=624,\nImGuiKey_KeypadSubtract=625,\nImGuiKey_KeypadAdd=626,\nImGuiKey_KeypadEnter=627,\nImGuiKey_KeypadEqual=628,\nImGuiKey_AppBack=629,\nImGuiKey_AppForward=630,\nImGuiKey_Oem102=631,\nImGuiKey_GamepadStart=632,\nImGuiKey_GamepadBack=633,\nImGuiKey_GamepadFaceLeft=634,\nImGuiKey_GamepadFaceRight=635,\nImGuiKey_GamepadFaceUp=636,\nImGuiKey_GamepadFaceDown=637,\nImGuiKey_GamepadDpadLeft=638,\nImGuiKey_GamepadDpadRight=639,\nImGuiKey_GamepadDpadUp=640,\nImGuiKey_GamepadDpadDown=641,\nImGuiKey_GamepadL1=642,\nImGuiKey_GamepadR1=643,\nImGuiKey_GamepadL2=644,\nImGuiKey_GamepadR2=645,\nImGuiKey_GamepadL3=646,\nImGuiKey_GamepadR3=647,\nImGuiKey_GamepadLStickLeft=648,\nImGuiKey_GamepadLStickRight=649,\nImGuiKey_GamepadLStickUp=650,\nImGuiKey_GamepadLStickDown=651,\nImGuiKey_GamepadRStickLeft=652,\nImGuiKey_GamepadRStickRight=653,\nImGuiKey_GamepadRStickUp=654,\nImGuiKey_GamepadRStickDown=655,\nImGuiKey_MouseLeft=656,\nImGuiKey_MouseRight=657,\nImGuiKey_MouseMiddle=658,\nImGuiKey_MouseX1=659,\nImGuiKey_MouseX2=660,\nImGuiKey_MouseWheelX=661,\nImGuiKey_MouseWheelY=662,\nImGuiKey_ReservedForModCtrl=663,\nImGuiKey_ReservedForModShift=664,\nImGuiKey_ReservedForModAlt=665,\nImGuiKey_ReservedForModSuper=666,\nImGuiKey_NamedKey_END=667,\nImGuiKey_NamedKey_COUNT=ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN,\nImGuiMod_None=0,\nImGuiMod_Ctrl=1 << 12,\nImGuiMod_Shift=1 << 13,\nImGuiMod_Alt=1 << 14,\nImGuiMod_Super=1 << 15,\nImGuiMod_Mask_=0xF000,\n}ImGuiKey;\ntypedef enum {\n    ImGuiInputFlags_None = 0,\n    ImGuiInputFlags_Repeat = 1 << 0,\n    ImGuiInputFlags_RouteActive = 1 << 10,\n    ImGuiInputFlags_RouteFocused = 1 << 11,\n    ImGuiInputFlags_RouteGlobal = 1 << 12,\n    ImGuiInputFlags_RouteAlways = 1 << 13,\n    ImGuiInputFlags_RouteOverFocused = 1 << 14,\n    ImGuiInputFlags_RouteOverActive = 1 << 15,\n    ImGuiInputFlags_RouteUnlessBgFocused = 1 << 16,\n    ImGuiInputFlags_RouteFromRootWindow = 1 << 17,\n    ImGuiInputFlags_Tooltip = 1 << 18,\n}ImGuiInputFlags_;\ntypedef enum {\n    ImGuiConfigFlags_None = 0,\n    ImGuiConfigFlags_NavEnableKeyboard = 1 << 0,\n    ImGuiConfigFlags_NavEnableGamepad = 1 << 1,\n    ImGuiConfigFlags_NoMouse = 1 << 4,\n    ImGuiConfigFlags_NoMouseCursorChange = 1 << 5,\n    ImGuiConfigFlags_NoKeyboard = 1 << 6,\n    ImGuiConfigFlags_DockingEnable = 1 << 7,\n    ImGuiConfigFlags_ViewportsEnable = 1 << 10,\n    ImGuiConfigFlags_IsSRGB = 1 << 20,\n    ImGuiConfigFlags_IsTouchScreen = 1 << 21,\n}ImGuiConfigFlags_;\ntypedef enum {\n    ImGuiBackendFlags_None = 0,\n    ImGuiBackendFlags_HasGamepad = 1 << 0,\n    ImGuiBackendFlags_HasMouseCursors = 1 << 1,\n    ImGuiBackendFlags_HasSetMousePos = 1 << 2,\n    ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3,\n    ImGuiBackendFlags_RendererHasTextures = 1 << 4,\n    ImGuiBackendFlags_PlatformHasViewports = 1 << 10,\n    ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11,\n    ImGuiBackendFlags_RendererHasViewports = 1 << 12,\n}ImGuiBackendFlags_;\ntypedef enum {\n    ImGuiCol_Text,\n    ImGuiCol_TextDisabled,\n    ImGuiCol_WindowBg,\n    ImGuiCol_ChildBg,\n    ImGuiCol_PopupBg,\n    ImGuiCol_Border,\n    ImGuiCol_BorderShadow,\n    ImGuiCol_FrameBg,\n    ImGuiCol_FrameBgHovered,\n    ImGuiCol_FrameBgActive,\n    ImGuiCol_TitleBg,\n    ImGuiCol_TitleBgActive,\n    ImGuiCol_TitleBgCollapsed,\n    ImGuiCol_MenuBarBg,\n    ImGuiCol_ScrollbarBg,\n    ImGuiCol_ScrollbarGrab,\n    ImGuiCol_ScrollbarGrabHovered,\n    ImGuiCol_ScrollbarGrabActive,\n    ImGuiCol_CheckMark,\n    ImGuiCol_SliderGrab,\n    ImGuiCol_SliderGrabActive,\n    ImGuiCol_Button,\n    ImGuiCol_ButtonHovered,\n    ImGuiCol_ButtonActive,\n    ImGuiCol_Header,\n    ImGuiCol_HeaderHovered,\n    ImGuiCol_HeaderActive,\n    ImGuiCol_Separator,\n    ImGuiCol_SeparatorHovered,\n    ImGuiCol_SeparatorActive,\n    ImGuiCol_ResizeGrip,\n    ImGuiCol_ResizeGripHovered,\n    ImGuiCol_ResizeGripActive,\n    ImGuiCol_InputTextCursor,\n    ImGuiCol_TabHovered,\n    ImGuiCol_Tab,\n    ImGuiCol_TabSelected,\n    ImGuiCol_TabSelectedOverline,\n    ImGuiCol_TabDimmed,\n    ImGuiCol_TabDimmedSelected,\n    ImGuiCol_TabDimmedSelectedOverline,\n    ImGuiCol_DockingPreview,\n    ImGuiCol_DockingEmptyBg,\n    ImGuiCol_PlotLines,\n    ImGuiCol_PlotLinesHovered,\n    ImGuiCol_PlotHistogram,\n    ImGuiCol_PlotHistogramHovered,\n    ImGuiCol_TableHeaderBg,\n    ImGuiCol_TableBorderStrong,\n    ImGuiCol_TableBorderLight,\n    ImGuiCol_TableRowBg,\n    ImGuiCol_TableRowBgAlt,\n    ImGuiCol_TextLink,\n    ImGuiCol_TextSelectedBg,\n    ImGuiCol_TreeLines,\n    ImGuiCol_DragDropTarget,\n    ImGuiCol_NavCursor,\n    ImGuiCol_NavWindowingHighlight,\n    ImGuiCol_NavWindowingDimBg,\n    ImGuiCol_ModalWindowDimBg,\n    ImGuiCol_COUNT,\n}ImGuiCol_;\ntypedef enum {\n    ImGuiStyleVar_Alpha,\n    ImGuiStyleVar_DisabledAlpha,\n    ImGuiStyleVar_WindowPadding,\n    ImGuiStyleVar_WindowRounding,\n    ImGuiStyleVar_WindowBorderSize,\n    ImGuiStyleVar_WindowMinSize,\n    ImGuiStyleVar_WindowTitleAlign,\n    ImGuiStyleVar_ChildRounding,\n    ImGuiStyleVar_ChildBorderSize,\n    ImGuiStyleVar_PopupRounding,\n    ImGuiStyleVar_PopupBorderSize,\n    ImGuiStyleVar_FramePadding,\n    ImGuiStyleVar_FrameRounding,\n    ImGuiStyleVar_FrameBorderSize,\n    ImGuiStyleVar_ItemSpacing,\n    ImGuiStyleVar_ItemInnerSpacing,\n    ImGuiStyleVar_IndentSpacing,\n    ImGuiStyleVar_CellPadding,\n    ImGuiStyleVar_ScrollbarSize,\n    ImGuiStyleVar_ScrollbarRounding,\n    ImGuiStyleVar_GrabMinSize,\n    ImGuiStyleVar_GrabRounding,\n    ImGuiStyleVar_ImageBorderSize,\n    ImGuiStyleVar_TabRounding,\n    ImGuiStyleVar_TabBorderSize,\n    ImGuiStyleVar_TabBarBorderSize,\n    ImGuiStyleVar_TabBarOverlineSize,\n    ImGuiStyleVar_TableAngledHeadersAngle,\n    ImGuiStyleVar_TableAngledHeadersTextAlign,\n    ImGuiStyleVar_TreeLinesSize,\n    ImGuiStyleVar_TreeLinesRounding,\n    ImGuiStyleVar_ButtonTextAlign,\n    ImGuiStyleVar_SelectableTextAlign,\n    ImGuiStyleVar_SeparatorTextBorderSize,\n    ImGuiStyleVar_SeparatorTextAlign,\n    ImGuiStyleVar_SeparatorTextPadding,\n    ImGuiStyleVar_DockingSeparatorSize,\n    ImGuiStyleVar_COUNT\n}ImGuiStyleVar_;\ntypedef enum {\n    ImGuiButtonFlags_None = 0,\n    ImGuiButtonFlags_MouseButtonLeft = 1 << 0,\n    ImGuiButtonFlags_MouseButtonRight = 1 << 1,\n    ImGuiButtonFlags_MouseButtonMiddle = 1 << 2,\n    ImGuiButtonFlags_MouseButtonMask_ = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle,\n    ImGuiButtonFlags_EnableNav = 1 << 3,\n}ImGuiButtonFlags_;\ntypedef enum {\n    ImGuiColorEditFlags_None = 0,\n    ImGuiColorEditFlags_NoAlpha = 1 << 1,\n    ImGuiColorEditFlags_NoPicker = 1 << 2,\n    ImGuiColorEditFlags_NoOptions = 1 << 3,\n    ImGuiColorEditFlags_NoSmallPreview = 1 << 4,\n    ImGuiColorEditFlags_NoInputs = 1 << 5,\n    ImGuiColorEditFlags_NoTooltip = 1 << 6,\n    ImGuiColorEditFlags_NoLabel = 1 << 7,\n    ImGuiColorEditFlags_NoSidePreview = 1 << 8,\n    ImGuiColorEditFlags_NoDragDrop = 1 << 9,\n    ImGuiColorEditFlags_NoBorder = 1 << 10,\n    ImGuiColorEditFlags_AlphaOpaque = 1 << 11,\n    ImGuiColorEditFlags_AlphaNoBg = 1 << 12,\n    ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 13,\n    ImGuiColorEditFlags_AlphaBar = 1 << 16,\n    ImGuiColorEditFlags_HDR = 1 << 19,\n    ImGuiColorEditFlags_DisplayRGB = 1 << 20,\n    ImGuiColorEditFlags_DisplayHSV = 1 << 21,\n    ImGuiColorEditFlags_DisplayHex = 1 << 22,\n    ImGuiColorEditFlags_Uint8 = 1 << 23,\n    ImGuiColorEditFlags_Float = 1 << 24,\n    ImGuiColorEditFlags_PickerHueBar = 1 << 25,\n    ImGuiColorEditFlags_PickerHueWheel = 1 << 26,\n    ImGuiColorEditFlags_InputRGB = 1 << 27,\n    ImGuiColorEditFlags_InputHSV = 1 << 28,\n    ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar,\n    ImGuiColorEditFlags_AlphaMask_ = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque | ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf,\n    ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex,\n    ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float,\n    ImGuiColorEditFlags_PickerMask_ = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar,\n    ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV,\n}ImGuiColorEditFlags_;\ntypedef enum {\n    ImGuiSliderFlags_None = 0,\n    ImGuiSliderFlags_Logarithmic = 1 << 5,\n    ImGuiSliderFlags_NoRoundToFormat = 1 << 6,\n    ImGuiSliderFlags_NoInput = 1 << 7,\n    ImGuiSliderFlags_WrapAround = 1 << 8,\n    ImGuiSliderFlags_ClampOnInput = 1 << 9,\n    ImGuiSliderFlags_ClampZeroRange = 1 << 10,\n    ImGuiSliderFlags_NoSpeedTweaks = 1 << 11,\n    ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange,\n    ImGuiSliderFlags_InvalidMask_ = 0x7000000F,\n}ImGuiSliderFlags_;\ntypedef enum {\n    ImGuiMouseButton_Left = 0,\n    ImGuiMouseButton_Right = 1,\n    ImGuiMouseButton_Middle = 2,\n    ImGuiMouseButton_COUNT = 5\n}ImGuiMouseButton_;\ntypedef enum {\n    ImGuiMouseCursor_None = -1,\n    ImGuiMouseCursor_Arrow = 0,\n    ImGuiMouseCursor_TextInput,\n    ImGuiMouseCursor_ResizeAll,\n    ImGuiMouseCursor_ResizeNS,\n    ImGuiMouseCursor_ResizeEW,\n    ImGuiMouseCursor_ResizeNESW,\n    ImGuiMouseCursor_ResizeNWSE,\n    ImGuiMouseCursor_Hand,\n    ImGuiMouseCursor_Wait,\n    ImGuiMouseCursor_Progress,\n    ImGuiMouseCursor_NotAllowed,\n    ImGuiMouseCursor_COUNT\n}ImGuiMouseCursor_;\ntypedef enum {\nImGuiMouseSource_Mouse=0,\nImGuiMouseSource_TouchScreen=1,\nImGuiMouseSource_Pen=2,\nImGuiMouseSource_COUNT=3,\n}ImGuiMouseSource;\ntypedef enum {\n    ImGuiCond_None = 0,\n    ImGuiCond_Always = 1 << 0,\n    ImGuiCond_Once = 1 << 1,\n    ImGuiCond_FirstUseEver = 1 << 2,\n    ImGuiCond_Appearing = 1 << 3,\n}ImGuiCond_;\ntypedef enum {\n    ImGuiTableFlags_None = 0,\n    ImGuiTableFlags_Resizable = 1 << 0,\n    ImGuiTableFlags_Reorderable = 1 << 1,\n    ImGuiTableFlags_Hideable = 1 << 2,\n    ImGuiTableFlags_Sortable = 1 << 3,\n    ImGuiTableFlags_NoSavedSettings = 1 << 4,\n    ImGuiTableFlags_ContextMenuInBody = 1 << 5,\n    ImGuiTableFlags_RowBg = 1 << 6,\n    ImGuiTableFlags_BordersInnerH = 1 << 7,\n    ImGuiTableFlags_BordersOuterH = 1 << 8,\n    ImGuiTableFlags_BordersInnerV = 1 << 9,\n    ImGuiTableFlags_BordersOuterV = 1 << 10,\n    ImGuiTableFlags_BordersH = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH,\n    ImGuiTableFlags_BordersV = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV,\n    ImGuiTableFlags_BordersInner = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH,\n    ImGuiTableFlags_BordersOuter = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH,\n    ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter,\n    ImGuiTableFlags_NoBordersInBody = 1 << 11,\n    ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12,\n    ImGuiTableFlags_SizingFixedFit = 1 << 13,\n    ImGuiTableFlags_SizingFixedSame = 2 << 13,\n    ImGuiTableFlags_SizingStretchProp = 3 << 13,\n    ImGuiTableFlags_SizingStretchSame = 4 << 13,\n    ImGuiTableFlags_NoHostExtendX = 1 << 16,\n    ImGuiTableFlags_NoHostExtendY = 1 << 17,\n    ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18,\n    ImGuiTableFlags_PreciseWidths = 1 << 19,\n    ImGuiTableFlags_NoClip = 1 << 20,\n    ImGuiTableFlags_PadOuterX = 1 << 21,\n    ImGuiTableFlags_NoPadOuterX = 1 << 22,\n    ImGuiTableFlags_NoPadInnerX = 1 << 23,\n    ImGuiTableFlags_ScrollX = 1 << 24,\n    ImGuiTableFlags_ScrollY = 1 << 25,\n    ImGuiTableFlags_SortMulti = 1 << 26,\n    ImGuiTableFlags_SortTristate = 1 << 27,\n    ImGuiTableFlags_HighlightHoveredColumn = 1 << 28,\n    ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame,\n}ImGuiTableFlags_;\ntypedef enum {\n    ImGuiTableColumnFlags_None = 0,\n    ImGuiTableColumnFlags_Disabled = 1 << 0,\n    ImGuiTableColumnFlags_DefaultHide = 1 << 1,\n    ImGuiTableColumnFlags_DefaultSort = 1 << 2,\n    ImGuiTableColumnFlags_WidthStretch = 1 << 3,\n    ImGuiTableColumnFlags_WidthFixed = 1 << 4,\n    ImGuiTableColumnFlags_NoResize = 1 << 5,\n    ImGuiTableColumnFlags_NoReorder = 1 << 6,\n    ImGuiTableColumnFlags_NoHide = 1 << 7,\n    ImGuiTableColumnFlags_NoClip = 1 << 8,\n    ImGuiTableColumnFlags_NoSort = 1 << 9,\n    ImGuiTableColumnFlags_NoSortAscending = 1 << 10,\n    ImGuiTableColumnFlags_NoSortDescending = 1 << 11,\n    ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12,\n    ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13,\n    ImGuiTableColumnFlags_PreferSortAscending = 1 << 14,\n    ImGuiTableColumnFlags_PreferSortDescending = 1 << 15,\n    ImGuiTableColumnFlags_IndentEnable = 1 << 16,\n    ImGuiTableColumnFlags_IndentDisable = 1 << 17,\n    ImGuiTableColumnFlags_AngledHeader = 1 << 18,\n    ImGuiTableColumnFlags_IsEnabled = 1 << 24,\n    ImGuiTableColumnFlags_IsVisible = 1 << 25,\n    ImGuiTableColumnFlags_IsSorted = 1 << 26,\n    ImGuiTableColumnFlags_IsHovered = 1 << 27,\n    ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed,\n    ImGuiTableColumnFlags_IndentMask_ = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable,\n    ImGuiTableColumnFlags_StatusMask_ = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered,\n    ImGuiTableColumnFlags_NoDirectResize_ = 1 << 30,\n}ImGuiTableColumnFlags_;\ntypedef enum {\n    ImGuiTableRowFlags_None = 0,\n    ImGuiTableRowFlags_Headers = 1 << 0,\n}ImGuiTableRowFlags_;\ntypedef enum {\n    ImGuiTableBgTarget_None = 0,\n    ImGuiTableBgTarget_RowBg0 = 1,\n    ImGuiTableBgTarget_RowBg1 = 2,\n    ImGuiTableBgTarget_CellBg = 3,\n}ImGuiTableBgTarget_;\nstruct ImGuiTableSortSpecs\n{\n    const ImGuiTableColumnSortSpecs* Specs;\n    int SpecsCount;\n    bool SpecsDirty;\n};\nstruct ImGuiTableColumnSortSpecs\n{\n    ImGuiID ColumnUserID;\n    ImS16 ColumnIndex;\n    ImS16 SortOrder;\n    ImGuiSortDirection SortDirection;\n};\nstruct ImGuiStyle\n{\n    float FontSizeBase;\n    float FontScaleMain;\n    float FontScaleDpi;\n    float Alpha;\n    float DisabledAlpha;\n    ImVec2 WindowPadding;\n    float WindowRounding;\n    float WindowBorderSize;\n    float WindowBorderHoverPadding;\n    ImVec2 WindowMinSize;\n    ImVec2 WindowTitleAlign;\n    ImGuiDir WindowMenuButtonPosition;\n    float ChildRounding;\n    float ChildBorderSize;\n    float PopupRounding;\n    float PopupBorderSize;\n    ImVec2 FramePadding;\n    float FrameRounding;\n    float FrameBorderSize;\n    ImVec2 ItemSpacing;\n    ImVec2 ItemInnerSpacing;\n    ImVec2 CellPadding;\n    ImVec2 TouchExtraPadding;\n    float IndentSpacing;\n    float ColumnsMinSpacing;\n    float ScrollbarSize;\n    float ScrollbarRounding;\n    float GrabMinSize;\n    float GrabRounding;\n    float LogSliderDeadzone;\n    float ImageBorderSize;\n    float TabRounding;\n    float TabBorderSize;\n    float TabCloseButtonMinWidthSelected;\n    float TabCloseButtonMinWidthUnselected;\n    float TabBarBorderSize;\n    float TabBarOverlineSize;\n    float TableAngledHeadersAngle;\n    ImVec2 TableAngledHeadersTextAlign;\n    ImGuiTreeNodeFlags TreeLinesFlags;\n    float TreeLinesSize;\n    float TreeLinesRounding;\n    ImGuiDir ColorButtonPosition;\n    ImVec2 ButtonTextAlign;\n    ImVec2 SelectableTextAlign;\n    float SeparatorTextBorderSize;\n    ImVec2 SeparatorTextAlign;\n    ImVec2 SeparatorTextPadding;\n    ImVec2 DisplayWindowPadding;\n    ImVec2 DisplaySafeAreaPadding;\n    float DockingSeparatorSize;\n    float MouseCursorScale;\n    bool AntiAliasedLines;\n    bool AntiAliasedLinesUseTex;\n    bool AntiAliasedFill;\n    float CurveTessellationTol;\n    float CircleTessellationMaxError;\n    ImVec4 Colors[ImGuiCol_COUNT];\n    float HoverStationaryDelay;\n    float HoverDelayShort;\n    float HoverDelayNormal;\n    ImGuiHoveredFlags HoverFlagsForTooltipMouse;\n    ImGuiHoveredFlags HoverFlagsForTooltipNav;\n    float _MainScale;\n    float _NextFrameFontSizeBase;\n};\nstruct ImGuiKeyData\n{\n    bool Down;\n    float DownDuration;\n    float DownDurationPrev;\n    float AnalogValue;\n};\ntypedef struct ImVector_ImWchar {int Size;int Capacity;ImWchar* Data;} ImVector_ImWchar;\n\nstruct ImGuiIO\n{\n    ImGuiConfigFlags ConfigFlags;\n    ImGuiBackendFlags BackendFlags;\n    ImVec2 DisplaySize;\n    ImVec2 DisplayFramebufferScale;\n    float DeltaTime;\n    float IniSavingRate;\n    const char* IniFilename;\n    const char* LogFilename;\n    void* UserData;\n    ImFontAtlas*Fonts;\n    ImFont* FontDefault;\n    bool FontAllowUserScaling;\n    bool ConfigNavSwapGamepadButtons;\n    bool ConfigNavMoveSetMousePos;\n    bool ConfigNavCaptureKeyboard;\n    bool ConfigNavEscapeClearFocusItem;\n    bool ConfigNavEscapeClearFocusWindow;\n    bool ConfigNavCursorVisibleAuto;\n    bool ConfigNavCursorVisibleAlways;\n    bool ConfigDockingNoSplit;\n    bool ConfigDockingWithShift;\n    bool ConfigDockingAlwaysTabBar;\n    bool ConfigDockingTransparentPayload;\n    bool ConfigViewportsNoAutoMerge;\n    bool ConfigViewportsNoTaskBarIcon;\n    bool ConfigViewportsNoDecoration;\n    bool ConfigViewportsNoDefaultParent;\n    bool ConfigDpiScaleFonts;\n    bool ConfigDpiScaleViewports;\n    bool MouseDrawCursor;\n    bool ConfigMacOSXBehaviors;\n    bool ConfigInputTrickleEventQueue;\n    bool ConfigInputTextCursorBlink;\n    bool ConfigInputTextEnterKeepActive;\n    bool ConfigDragClickToInputText;\n    bool ConfigWindowsResizeFromEdges;\n    bool ConfigWindowsMoveFromTitleBarOnly;\n    bool ConfigWindowsCopyContentsWithCtrlC;\n    bool ConfigScrollbarScrollByPage;\n    float ConfigMemoryCompactTimer;\n    float MouseDoubleClickTime;\n    float MouseDoubleClickMaxDist;\n    float MouseDragThreshold;\n    float KeyRepeatDelay;\n    float KeyRepeatRate;\n    bool ConfigErrorRecovery;\n    bool ConfigErrorRecoveryEnableAssert;\n    bool ConfigErrorRecoveryEnableDebugLog;\n    bool ConfigErrorRecoveryEnableTooltip;\n    bool ConfigDebugIsDebuggerPresent;\n    bool ConfigDebugHighlightIdConflicts;\n    bool ConfigDebugHighlightIdConflictsShowItemPicker;\n    bool ConfigDebugBeginReturnValueOnce;\n    bool ConfigDebugBeginReturnValueLoop;\n    bool ConfigDebugIgnoreFocusLoss;\n    bool ConfigDebugIniSettings;\n    const char* BackendPlatformName;\n    const char* BackendRendererName;\n    void* BackendPlatformUserData;\n    void* BackendRendererUserData;\n    void* BackendLanguageUserData;\n    bool WantCaptureMouse;\n    bool WantCaptureKeyboard;\n    bool WantTextInput;\n    bool WantSetMousePos;\n    bool WantSaveIniSettings;\n    bool NavActive;\n    bool NavVisible;\n    float Framerate;\n    int MetricsRenderVertices;\n    int MetricsRenderIndices;\n    int MetricsRenderWindows;\n    int MetricsActiveWindows;\n    ImVec2 MouseDelta;\n    ImGuiContext* Ctx;\n    ImVec2 MousePos;\n    bool MouseDown[5];\n    float MouseWheel;\n    float MouseWheelH;\n    ImGuiMouseSource MouseSource;\n    ImGuiID MouseHoveredViewport;\n    bool KeyCtrl;\n    bool KeyShift;\n    bool KeyAlt;\n    bool KeySuper;\n    ImGuiKeyChord KeyMods;\n    ImGuiKeyData KeysData[ImGuiKey_NamedKey_COUNT];\n    bool WantCaptureMouseUnlessPopupClose;\n    ImVec2 MousePosPrev;\n    ImVec2 MouseClickedPos[5];\n    double MouseClickedTime[5];\n    bool MouseClicked[5];\n    bool MouseDoubleClicked[5];\n    ImU16 MouseClickedCount[5];\n    ImU16 MouseClickedLastCount[5];\n    bool MouseReleased[5];\n    double MouseReleasedTime[5];\n    bool MouseDownOwned[5];\n    bool MouseDownOwnedUnlessPopupClose[5];\n    bool MouseWheelRequestAxisSwap;\n    bool MouseCtrlLeftAsRightClick;\n    float MouseDownDuration[5];\n    float MouseDownDurationPrev[5];\n    ImVec2 MouseDragMaxDistanceAbs[5];\n    float MouseDragMaxDistanceSqr[5];\n    float PenPressure;\n    bool AppFocusLost;\n    bool AppAcceptingEvents;\n    ImWchar16 InputQueueSurrogate;\n    ImVector_ImWchar InputQueueCharacters;\n};\nstruct ImGuiInputTextCallbackData\n{\n    ImGuiContext* Ctx;\n    ImGuiInputTextFlags EventFlag;\n    ImGuiInputTextFlags Flags;\n    void* UserData;\n    ImWchar EventChar;\n    ImGuiKey EventKey;\n    char* Buf;\n    int BufTextLen;\n    int BufSize;\n    bool BufDirty;\n    int CursorPos;\n    int SelectionStart;\n    int SelectionEnd;\n};\nstruct ImGuiSizeCallbackData\n{\n    void* UserData;\n    ImVec2 Pos;\n    ImVec2 CurrentSize;\n    ImVec2 DesiredSize;\n};\nstruct ImGuiWindowClass\n{\n    ImGuiID ClassId;\n    ImGuiID ParentViewportId;\n    ImGuiID FocusRouteParentWindowId;\n    ImGuiViewportFlags ViewportFlagsOverrideSet;\n    ImGuiViewportFlags ViewportFlagsOverrideClear;\n    ImGuiTabItemFlags TabItemFlagsOverrideSet;\n    ImGuiDockNodeFlags DockNodeFlagsOverrideSet;\n    bool DockingAlwaysTabBar;\n    bool DockingAllowUnclassed;\n};\nstruct ImGuiPayload\n{\n    void* Data;\n    int DataSize;\n    ImGuiID SourceId;\n    ImGuiID SourceParentId;\n    int DataFrameCount;\n    char DataType[32 + 1];\n    bool Preview;\n    bool Delivery;\n};\nstruct ImGuiOnceUponAFrame\n{\n     int RefFrame;\n};\nstruct ImGuiTextRange\n{\n        const char* b;\n        const char* e;\n};\ntypedef struct ImGuiTextRange ImGuiTextRange;\n\ntypedef struct ImVector_ImGuiTextRange {int Size;int Capacity;ImGuiTextRange* Data;} ImVector_ImGuiTextRange;\n\nstruct ImGuiTextFilter\n{\n    char InputBuf[256];\n    ImVector_ImGuiTextRange Filters;\n    int CountGrep;\n};\ntypedef struct ImGuiTextRange ImGuiTextRange;\ntypedef struct ImVector_char {int Size;int Capacity;char* Data;} ImVector_char;\n\nstruct ImGuiTextBuffer\n{\n    ImVector_char Buf;\n};\nstruct ImGuiStoragePair\n{\n    ImGuiID key;\n    union { int val_i; float val_f; void* val_p; };\n};\ntypedef struct ImVector_ImGuiStoragePair {int Size;int Capacity;ImGuiStoragePair* Data;} ImVector_ImGuiStoragePair;\n\nstruct ImGuiStorage\n{\n    ImVector_ImGuiStoragePair Data;\n};\nstruct ImGuiListClipper\n{\n    ImGuiContext* Ctx;\n    int DisplayStart;\n    int DisplayEnd;\n    int ItemsCount;\n    float ItemsHeight;\n    double StartPosY;\n    double StartSeekOffsetY;\n    void* TempData;\n};\nstruct ImColor\n{\n    ImVec4 Value;\n};\ntypedef enum {\n    ImGuiMultiSelectFlags_None = 0,\n    ImGuiMultiSelectFlags_SingleSelect = 1 << 0,\n    ImGuiMultiSelectFlags_NoSelectAll = 1 << 1,\n    ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2,\n    ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3,\n    ImGuiMultiSelectFlags_NoAutoClear = 1 << 4,\n    ImGuiMultiSelectFlags_NoAutoClearOnReselect = 1 << 5,\n    ImGuiMultiSelectFlags_BoxSelect1d = 1 << 6,\n    ImGuiMultiSelectFlags_BoxSelect2d = 1 << 7,\n    ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 8,\n    ImGuiMultiSelectFlags_ClearOnEscape = 1 << 9,\n    ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 10,\n    ImGuiMultiSelectFlags_ScopeWindow = 1 << 11,\n    ImGuiMultiSelectFlags_ScopeRect = 1 << 12,\n    ImGuiMultiSelectFlags_SelectOnClick = 1 << 13,\n    ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14,\n    ImGuiMultiSelectFlags_NavWrapX = 1 << 16,\n}ImGuiMultiSelectFlags_;\ntypedef struct ImVector_ImGuiSelectionRequest {int Size;int Capacity;ImGuiSelectionRequest* Data;} ImVector_ImGuiSelectionRequest;\n\nstruct ImGuiMultiSelectIO\n{\n    ImVector_ImGuiSelectionRequest Requests;\n    ImGuiSelectionUserData RangeSrcItem;\n    ImGuiSelectionUserData NavIdItem;\n    bool NavIdSelected;\n    bool RangeSrcReset;\n    int ItemsCount;\n};\ntypedef enum {\n    ImGuiSelectionRequestType_None = 0,\n    ImGuiSelectionRequestType_SetAll,\n    ImGuiSelectionRequestType_SetRange,\n}ImGuiSelectionRequestType;\nstruct ImGuiSelectionRequest\n{\n    ImGuiSelectionRequestType Type;\n    bool Selected;\n    ImS8 RangeDirection;\n    ImGuiSelectionUserData RangeFirstItem;\n    ImGuiSelectionUserData RangeLastItem;\n};\nstruct ImGuiSelectionBasicStorage\n{\n    int Size;\n    bool PreserveOrder;\n    void* UserData;\n    ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx);\n    int _SelectionOrder;\n    ImGuiStorage _Storage;\n};\nstruct ImGuiSelectionExternalStorage\n{\n    void* UserData;\n    void (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected);\n};\ntypedef unsigned short ImDrawIdx;\ntypedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd);\nstruct ImDrawCmd\n{\n    ImVec4 ClipRect;\n    ImTextureRef TexRef;\n    unsigned int VtxOffset;\n    unsigned int IdxOffset;\n    unsigned int ElemCount;\n    ImDrawCallback UserCallback;\n    void* UserCallbackData;\n    int UserCallbackDataSize;\n    int UserCallbackDataOffset;\n};\nstruct ImDrawVert\n{\n    ImVec2 pos;\n    ImVec2 uv;\n    ImU32 col;\n};\ntypedef struct ImDrawCmdHeader ImDrawCmdHeader;\nstruct ImDrawCmdHeader\n{\n    ImVec4 ClipRect;\n    ImTextureRef TexRef;\n    unsigned int VtxOffset;\n};\ntypedef struct ImVector_ImDrawCmd {int Size;int Capacity;ImDrawCmd* Data;} ImVector_ImDrawCmd;\n\ntypedef struct ImVector_ImDrawIdx {int Size;int Capacity;ImDrawIdx* Data;} ImVector_ImDrawIdx;\n\nstruct ImDrawChannel\n{\n    ImVector_ImDrawCmd _CmdBuffer;\n    ImVector_ImDrawIdx _IdxBuffer;\n};\ntypedef struct ImVector_ImDrawChannel {int Size;int Capacity;ImDrawChannel* Data;} ImVector_ImDrawChannel;\n\nstruct ImDrawListSplitter\n{\n    int _Current;\n    int _Count;\n    ImVector_ImDrawChannel _Channels;\n};\ntypedef enum {\n    ImDrawFlags_None = 0,\n    ImDrawFlags_Closed = 1 << 0,\n    ImDrawFlags_RoundCornersTopLeft = 1 << 4,\n    ImDrawFlags_RoundCornersTopRight = 1 << 5,\n    ImDrawFlags_RoundCornersBottomLeft = 1 << 6,\n    ImDrawFlags_RoundCornersBottomRight = 1 << 7,\n    ImDrawFlags_RoundCornersNone = 1 << 8,\n    ImDrawFlags_RoundCornersTop = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight,\n    ImDrawFlags_RoundCornersBottom = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,\n    ImDrawFlags_RoundCornersLeft = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft,\n    ImDrawFlags_RoundCornersRight = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight,\n    ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,\n    ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll,\n    ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone,\n}ImDrawFlags_;\ntypedef enum {\n    ImDrawListFlags_None = 0,\n    ImDrawListFlags_AntiAliasedLines = 1 << 0,\n    ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1,\n    ImDrawListFlags_AntiAliasedFill = 1 << 2,\n    ImDrawListFlags_AllowVtxOffset = 1 << 3,\n}ImDrawListFlags_;\ntypedef struct ImVector_ImDrawVert {int Size;int Capacity;ImDrawVert* Data;} ImVector_ImDrawVert;\n\ntypedef struct ImVector_ImVec2 {int Size;int Capacity;ImVec2* Data;} ImVector_ImVec2;\n\ntypedef struct ImVector_ImVec4 {int Size;int Capacity;ImVec4* Data;} ImVector_ImVec4;\n\ntypedef struct ImVector_ImTextureRef {int Size;int Capacity;ImTextureRef* Data;} ImVector_ImTextureRef;\n\ntypedef struct ImVector_ImU8 {int Size;int Capacity;ImU8* Data;} ImVector_ImU8;\n\nstruct ImDrawList\n{\n    ImVector_ImDrawCmd CmdBuffer;\n    ImVector_ImDrawIdx IdxBuffer;\n    ImVector_ImDrawVert VtxBuffer;\n    ImDrawListFlags Flags;\n    unsigned int _VtxCurrentIdx;\n    ImDrawListSharedData* _Data;\n    ImDrawVert* _VtxWritePtr;\n    ImDrawIdx* _IdxWritePtr;\n    ImVector_ImVec2 _Path;\n    ImDrawCmdHeader _CmdHeader;\n    ImDrawListSplitter _Splitter;\n    ImVector_ImVec4 _ClipRectStack;\n    ImVector_ImTextureRef _TextureStack;\n    ImVector_ImU8 _CallbacksDataBuf;\n    float _FringeScale;\n    const char* _OwnerName;\n};\ntypedef struct ImVector_ImDrawListPtr {int Size;int Capacity;ImDrawList** Data;} ImVector_ImDrawListPtr;\n\ntypedef struct ImVector_ImTextureDataPtr {int Size;int Capacity;ImTextureData** Data;} ImVector_ImTextureDataPtr;\n\nstruct ImDrawData\n{\n    bool Valid;\n    int CmdListsCount;\n    int TotalIdxCount;\n    int TotalVtxCount;\n    ImVector_ImDrawListPtr CmdLists;\n    ImVec2 DisplayPos;\n    ImVec2 DisplaySize;\n    ImVec2 FramebufferScale;\n    ImGuiViewport* OwnerViewport;\n    ImVector_ImTextureDataPtr* Textures;\n};\ntypedef enum {\n    ImTextureFormat_RGBA32,\n    ImTextureFormat_Alpha8,\n}ImTextureFormat;\ntypedef enum {\n    ImTextureStatus_OK,\n    ImTextureStatus_Destroyed,\n    ImTextureStatus_WantCreate,\n    ImTextureStatus_WantUpdates,\n    ImTextureStatus_WantDestroy,\n}ImTextureStatus;\nstruct ImTextureRect\n{\n    unsigned short x, y;\n    unsigned short w, h;\n};\ntypedef struct ImVector_ImTextureRect {int Size;int Capacity;ImTextureRect* Data;} ImVector_ImTextureRect;\n\nstruct ImTextureData\n{\n    int UniqueID;\n    ImTextureStatus Status;\n    void* BackendUserData;\n    ImTextureID TexID;\n    ImTextureFormat Format;\n    int Width;\n    int Height;\n    int BytesPerPixel;\n    unsigned char* Pixels;\n    ImTextureRect UsedRect;\n    ImTextureRect UpdateRect;\n    ImVector_ImTextureRect Updates;\n    int UnusedFrames;\n    unsigned short RefCount;\n    bool UseColors;\n    bool WantDestroyNextFrame;\n};\nstruct ImFontConfig\n{\n    char Name[40];\n    void* FontData;\n    int FontDataSize;\n    bool FontDataOwnedByAtlas;\n    bool MergeMode;\n    bool PixelSnapH;\n    bool PixelSnapV;\n    ImS8 FontNo;\n    ImS8 OversampleH;\n    ImS8 OversampleV;\n    float SizePixels;\n    const ImWchar* GlyphRanges;\n    const ImWchar* GlyphExcludeRanges;\n    ImVec2 GlyphOffset;\n    float GlyphMinAdvanceX;\n    float GlyphMaxAdvanceX;\n    float GlyphExtraAdvanceX;\n    unsigned int FontLoaderFlags;\n    float RasterizerMultiply;\n    float RasterizerDensity;\n    ImWchar EllipsisChar;\n    ImFontFlags Flags;\n    ImFont* DstFont;\n    const ImFontLoader* FontLoader;\n    void* FontLoaderData;\n};\nstruct ImFontGlyph\n{\n    unsigned int Colored : 1;\n    unsigned int Visible : 1;\n    unsigned int SourceIdx : 4;\n    unsigned int Codepoint : 26;\n    float AdvanceX;\n    float X0, Y0, X1, Y1;\n    float U0, V0, U1, V1;\n    int PackId;\n};\ntypedef struct ImVector_ImU32 {int Size;int Capacity;ImU32* Data;} ImVector_ImU32;\n\nstruct ImFontGlyphRangesBuilder\n{\n    ImVector_ImU32 UsedChars;\n};\ntypedef int ImFontAtlasRectId;\nstruct ImFontAtlasRect\n{\n    unsigned short x, y;\n    unsigned short w, h;\n    ImVec2 uv0, uv1;\n};\ntypedef enum {\n    ImFontAtlasFlags_None = 0,\n    ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0,\n    ImFontAtlasFlags_NoMouseCursors = 1 << 1,\n    ImFontAtlasFlags_NoBakedLines = 1 << 2,\n}ImFontAtlasFlags_;\ntypedef struct ImVector_ImFontPtr {int Size;int Capacity;ImFont** Data;} ImVector_ImFontPtr;\n\ntypedef struct ImVector_ImFontConfig {int Size;int Capacity;ImFontConfig* Data;} ImVector_ImFontConfig;\n\ntypedef struct ImVector_ImDrawListSharedDataPtr {int Size;int Capacity;ImDrawListSharedData** Data;} ImVector_ImDrawListSharedDataPtr;\n\nstruct ImFontAtlas\n{\n    ImFontAtlasFlags Flags;\n    ImTextureFormat TexDesiredFormat;\n    int TexGlyphPadding;\n    int TexMinWidth;\n    int TexMinHeight;\n    int TexMaxWidth;\n    int TexMaxHeight;\n    void* UserData;\n    ImTextureRef TexRef;\n    ImTextureData* TexData;\n    ImVector_ImTextureDataPtr TexList;\n    bool Locked;\n    bool RendererHasTextures;\n    bool TexIsBuilt;\n    bool TexPixelsUseColors;\n    ImVec2 TexUvScale;\n    ImVec2 TexUvWhitePixel;\n    ImVector_ImFontPtr Fonts;\n    ImVector_ImFontConfig Sources;\n    ImVec4 TexUvLines[(32) + 1];\n    int TexNextUniqueID;\n    int FontNextUniqueID;\n    ImVector_ImDrawListSharedDataPtr DrawListSharedDatas;\n    ImFontAtlasBuilder* Builder;\n    const ImFontLoader* FontLoader;\n    const char* FontLoaderName;\n    void* FontLoaderData;\n    unsigned int FontLoaderFlags;\n    int RefCount;\n    ImGuiContext* OwnerContext;\n};\ntypedef struct ImVector_float {int Size;int Capacity;float* Data;} ImVector_float;\n\ntypedef struct ImVector_ImU16 {int Size;int Capacity;ImU16* Data;} ImVector_ImU16;\n\ntypedef struct ImVector_ImFontGlyph {int Size;int Capacity;ImFontGlyph* Data;} ImVector_ImFontGlyph;\n\nstruct ImFontBaked\n{\n    ImVector_float IndexAdvanceX;\n    float FallbackAdvanceX;\n    float Size;\n    float RasterizerDensity;\n    ImVector_ImU16 IndexLookup;\n    ImVector_ImFontGlyph Glyphs;\n    int FallbackGlyphIndex;\n    float Ascent, Descent;\n    unsigned int MetricsTotalSurface:26;\n    unsigned int WantDestroy:1;\n    unsigned int LockLoadingFallback:1;\n    int LastUsedFrame;\n    ImGuiID BakedId;\n    ImFont* ContainerFont;\n    void* FontLoaderDatas;\n};\ntypedef enum {\n    ImFontFlags_None = 0,\n    ImFontFlags_NoLoadError = 1 << 1,\n    ImFontFlags_NoLoadGlyphs = 1 << 2,\n    ImFontFlags_LockBakedSizes = 1 << 3,\n}ImFontFlags_;\ntypedef struct ImVector_ImFontConfigPtr {int Size;int Capacity;ImFontConfig** Data;} ImVector_ImFontConfigPtr;\n\nstruct ImFont\n{\n    ImFontBaked* LastBaked;\n    ImFontAtlas* ContainerAtlas;\n    ImFontFlags Flags;\n    float CurrentRasterizerDensity;\n    ImGuiID FontId;\n    float LegacySize;\n    ImVector_ImFontConfigPtr Sources;\n    ImWchar EllipsisChar;\n    ImWchar FallbackChar;\n    ImU8 Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX +1)/8192/8];\n    bool EllipsisAutoBake;\n    ImGuiStorage RemapPairs;\n};\ntypedef enum {\n    ImGuiViewportFlags_None = 0,\n    ImGuiViewportFlags_IsPlatformWindow = 1 << 0,\n    ImGuiViewportFlags_IsPlatformMonitor = 1 << 1,\n    ImGuiViewportFlags_OwnedByApp = 1 << 2,\n    ImGuiViewportFlags_NoDecoration = 1 << 3,\n    ImGuiViewportFlags_NoTaskBarIcon = 1 << 4,\n    ImGuiViewportFlags_NoFocusOnAppearing = 1 << 5,\n    ImGuiViewportFlags_NoFocusOnClick = 1 << 6,\n    ImGuiViewportFlags_NoInputs = 1 << 7,\n    ImGuiViewportFlags_NoRendererClear = 1 << 8,\n    ImGuiViewportFlags_NoAutoMerge = 1 << 9,\n    ImGuiViewportFlags_TopMost = 1 << 10,\n    ImGuiViewportFlags_CanHostOtherWindows = 1 << 11,\n    ImGuiViewportFlags_IsMinimized = 1 << 12,\n    ImGuiViewportFlags_IsFocused = 1 << 13,\n}ImGuiViewportFlags_;\nstruct ImGuiViewport\n{\n    ImGuiID ID;\n    ImGuiViewportFlags Flags;\n    ImVec2 Pos;\n    ImVec2 Size;\n    ImVec2 FramebufferScale;\n    ImVec2 WorkPos;\n    ImVec2 WorkSize;\n    float DpiScale;\n    ImGuiID ParentViewportId;\n    ImDrawData* DrawData;\n    void* RendererUserData;\n    void* PlatformUserData;\n    void* PlatformHandle;\n    void* PlatformHandleRaw;\n    bool PlatformWindowCreated;\n    bool PlatformRequestMove;\n    bool PlatformRequestResize;\n    bool PlatformRequestClose;\n};\ntypedef struct ImVector_ImGuiPlatformMonitor {int Size;int Capacity;ImGuiPlatformMonitor* Data;} ImVector_ImGuiPlatformMonitor;\n\ntypedef struct ImVector_ImGuiViewportPtr {int Size;int Capacity;ImGuiViewport** Data;} ImVector_ImGuiViewportPtr;\n\nstruct ImGuiPlatformIO\n{\n    const char* (*Platform_GetClipboardTextFn)(ImGuiContext* ctx);\n    void (*Platform_SetClipboardTextFn)(ImGuiContext* ctx, const char* text);\n    void* Platform_ClipboardUserData;\n    bool (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path);\n    void* Platform_OpenInShellUserData;\n    void (*Platform_SetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);\n    void* Platform_ImeUserData;\n    ImWchar Platform_LocaleDecimalPoint;\n    int Renderer_TextureMaxWidth;\n    int Renderer_TextureMaxHeight;\n    void* Renderer_RenderState;\n    void (*Platform_CreateWindow)(ImGuiViewport* vp);\n    void (*Platform_DestroyWindow)(ImGuiViewport* vp);\n    void (*Platform_ShowWindow)(ImGuiViewport* vp);\n    void (*Platform_SetWindowPos)(ImGuiViewport* vp, ImVec2 pos);\n    ImVec2 (*Platform_GetWindowPos)(ImGuiViewport* vp);\n    void (*Platform_SetWindowSize)(ImGuiViewport* vp, ImVec2 size);\n    ImVec2 (*Platform_GetWindowSize)(ImGuiViewport* vp);\n    ImVec2 (*Platform_GetWindowFramebufferScale)(ImGuiViewport* vp);\n    void (*Platform_SetWindowFocus)(ImGuiViewport* vp);\n    bool (*Platform_GetWindowFocus)(ImGuiViewport* vp);\n    bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp);\n    void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* str);\n    void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha);\n    void (*Platform_UpdateWindow)(ImGuiViewport* vp);\n    void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg);\n    void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg);\n    float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp);\n    void (*Platform_OnChangedViewport)(ImGuiViewport* vp);\n    ImVec4 (*Platform_GetWindowWorkAreaInsets)(ImGuiViewport* vp);\n    int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface);\n    void (*Renderer_CreateWindow)(ImGuiViewport* vp);\n    void (*Renderer_DestroyWindow)(ImGuiViewport* vp);\n    void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size);\n    void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg);\n    void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg);\n    ImVector_ImGuiPlatformMonitor Monitors;\n    ImVector_ImTextureDataPtr Textures;\n    ImVector_ImGuiViewportPtr Viewports;\n};\nstruct ImGuiPlatformMonitor\n{\n    ImVec2 MainPos, MainSize;\n    ImVec2 WorkPos, WorkSize;\n    float DpiScale;\n    void* PlatformHandle;\n};\nstruct ImGuiPlatformImeData\n{\n    bool WantVisible;\n    bool WantTextInput;\n    ImVec2 InputPos;\n    float InputLineHeight;\n    ImGuiID ViewportId;\n};\nstruct ImBitVector;\nstruct ImRect;\nstruct ImGuiTextIndex;\nstruct ImDrawDataBuilder;\nstruct ImDrawListSharedData;\nstruct ImFontAtlasBuilder;\nstruct ImFontAtlasPostProcessData;\nstruct ImFontAtlasRectEntry;\nstruct ImGuiBoxSelectState;\nstruct ImGuiColorMod;\nstruct ImGuiContext;\nstruct ImGuiContextHook;\nstruct ImGuiDataTypeInfo;\nstruct ImGuiDeactivatedItemData;\nstruct ImGuiDockContext;\nstruct ImGuiDockRequest;\nstruct ImGuiDockNode;\nstruct ImGuiDockNodeSettings;\nstruct ImGuiErrorRecoveryState;\nstruct ImGuiGroupData;\nstruct ImGuiInputTextState;\nstruct ImGuiInputTextDeactivateData;\nstruct ImGuiLastItemData;\nstruct ImGuiLocEntry;\nstruct ImGuiMenuColumns;\nstruct ImGuiMultiSelectState;\nstruct ImGuiMultiSelectTempData;\nstruct ImGuiNavItemData;\nstruct ImGuiMetricsConfig;\nstruct ImGuiNextWindowData;\nstruct ImGuiNextItemData;\nstruct ImGuiOldColumnData;\nstruct ImGuiOldColumns;\nstruct ImGuiPopupData;\nstruct ImGuiSettingsHandler;\nstruct ImGuiStyleMod;\nstruct ImGuiStyleVarInfo;\nstruct ImGuiTabBar;\nstruct ImGuiTabItem;\nstruct ImGuiTable;\nstruct ImGuiTableHeaderData;\nstruct ImGuiTableColumn;\nstruct ImGuiTableInstanceData;\nstruct ImGuiTableTempData;\nstruct ImGuiTableSettings;\nstruct ImGuiTableColumnsSettings;\nstruct ImGuiTreeNodeStackData;\nstruct ImGuiTypingSelectState;\nstruct ImGuiTypingSelectRequest;\nstruct ImGuiWindow;\nstruct ImGuiWindowDockStyle;\nstruct ImGuiWindowTempData;\nstruct ImGuiWindowSettings;\ntypedef int ImGuiDataAuthority;\ntypedef int ImGuiLayoutType;\ntypedef int ImGuiActivateFlags;\ntypedef int ImGuiDebugLogFlags;\ntypedef int ImGuiFocusRequestFlags;\ntypedef int ImGuiItemStatusFlags;\ntypedef int ImGuiOldColumnFlags;\ntypedef int ImGuiLogFlags;\ntypedef int ImGuiNavRenderCursorFlags;\ntypedef int ImGuiNavMoveFlags;\ntypedef int ImGuiNextItemDataFlags;\ntypedef int ImGuiNextWindowDataFlags;\ntypedef int ImGuiScrollFlags;\ntypedef int ImGuiSeparatorFlags;\ntypedef int ImGuiTextFlags;\ntypedef int ImGuiTooltipFlags;\ntypedef int ImGuiTypingSelectFlags;\ntypedef int ImGuiWindowRefreshFlags;\ntypedef ImS16 ImGuiTableColumnIdx;\ntypedef ImU16 ImGuiTableDrawChannelIdx;\nextern ImGuiContext* GImGui;\ntypedef FILE* ImFileHandle;\ntypedef struct ImVec1 ImVec1;\nstruct ImVec1\n{\n    float x;\n};\ntypedef struct ImVec2i ImVec2i;\nstruct ImVec2i\n{\n    int x, y;\n};\ntypedef struct ImVec2ih ImVec2ih;\nstruct ImVec2ih\n{\n    short x, y;\n};\nstruct ImRect\n{\n    ImVec2 Min;\n    ImVec2 Max;\n};\ntypedef ImU32* ImBitArrayPtr;\nstruct ImBitVector\n{\n    ImVector_ImU32 Storage;\n};\ntypedef int ImPoolIdx;\ntypedef struct ImVector_int {int Size;int Capacity;int* Data;} ImVector_int;\n\nstruct ImGuiTextIndex\n{\n    ImVector_int LineOffsets;\n    int EndOffset;\n};\nstruct ImDrawListSharedData\n{\n    ImVec2 TexUvWhitePixel;\n    const ImVec4* TexUvLines;\n    ImFontAtlas* FontAtlas;\n    ImFont* Font;\n    float FontSize;\n    float FontScale;\n    float CurveTessellationTol;\n    float CircleSegmentMaxError;\n    float InitialFringeScale;\n    ImDrawListFlags InitialFlags;\n    ImVec4 ClipRectFullscreen;\n    ImVector_ImVec2 TempBuffer;\n    ImVector_ImDrawListPtr DrawLists;\n    ImGuiContext* Context;\n    ImVec2 ArcFastVtx[48];\n    float ArcFastRadiusCutoff;\n    ImU8 CircleSegmentCounts[64];\n};\nstruct ImDrawDataBuilder\n{\n    ImVector_ImDrawListPtr* Layers[2];\n    ImVector_ImDrawListPtr LayerData1;\n};\ntypedef struct ImFontStackData ImFontStackData;\nstruct ImFontStackData\n{\n    ImFont* Font;\n    float FontSizeBeforeScaling;\n    float FontSizeAfterScaling;\n};\nstruct ImGuiStyleVarInfo\n{\n    ImU32 Count : 8;\n    ImGuiDataType DataType : 8;\n    ImU32 Offset : 16;\n};\nstruct ImGuiColorMod\n{\n    ImGuiCol Col;\n    ImVec4 BackupValue;\n};\nstruct ImGuiStyleMod\n{\n    ImGuiStyleVar VarIdx;\n    union { int BackupInt[2]; float BackupFloat[2]; };\n};\ntypedef struct ImGuiDataTypeStorage ImGuiDataTypeStorage;\nstruct ImGuiDataTypeStorage\n{\n    ImU8 Data[8];\n};\nstruct ImGuiDataTypeInfo\n{\n    size_t Size;\n    const char* Name;\n    const char* PrintFmt;\n    const char* ScanFmt;\n};\ntypedef enum {\n    ImGuiDataType_Pointer = ImGuiDataType_COUNT,\n    ImGuiDataType_ID,\n}ImGuiDataTypePrivate_;\ntypedef enum {\n    ImGuiItemFlags_Disabled = 1 << 10,\n    ImGuiItemFlags_ReadOnly = 1 << 11,\n    ImGuiItemFlags_MixedValue = 1 << 12,\n    ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13,\n    ImGuiItemFlags_AllowOverlap = 1 << 14,\n    ImGuiItemFlags_NoNavDisableMouseHover = 1 << 15,\n    ImGuiItemFlags_NoMarkEdited = 1 << 16,\n    ImGuiItemFlags_NoFocus = 1 << 17,\n    ImGuiItemFlags_Inputable = 1 << 20,\n    ImGuiItemFlags_HasSelectionUserData = 1 << 21,\n    ImGuiItemFlags_IsMultiSelect = 1 << 22,\n    ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups,\n}ImGuiItemFlagsPrivate_;\ntypedef enum {\n    ImGuiItemStatusFlags_None = 0,\n    ImGuiItemStatusFlags_HoveredRect = 1 << 0,\n    ImGuiItemStatusFlags_HasDisplayRect = 1 << 1,\n    ImGuiItemStatusFlags_Edited = 1 << 2,\n    ImGuiItemStatusFlags_ToggledSelection = 1 << 3,\n    ImGuiItemStatusFlags_ToggledOpen = 1 << 4,\n    ImGuiItemStatusFlags_HasDeactivated = 1 << 5,\n    ImGuiItemStatusFlags_Deactivated = 1 << 6,\n    ImGuiItemStatusFlags_HoveredWindow = 1 << 7,\n    ImGuiItemStatusFlags_Visible = 1 << 8,\n    ImGuiItemStatusFlags_HasClipRect = 1 << 9,\n    ImGuiItemStatusFlags_HasShortcut = 1 << 10,\n}ImGuiItemStatusFlags_;\ntypedef enum {\n    ImGuiHoveredFlags_DelayMask_ = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay,\n    ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary,\n    ImGuiHoveredFlags_AllowedMaskForIsItemHovered = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_,\n}ImGuiHoveredFlagsPrivate_;\ntypedef enum {\n    ImGuiInputTextFlags_Multiline = 1 << 26,\n    ImGuiInputTextFlags_MergedItem = 1 << 27,\n    ImGuiInputTextFlags_LocalizeDecimalPoint= 1 << 28,\n}ImGuiInputTextFlagsPrivate_;\ntypedef enum {\n    ImGuiButtonFlags_PressedOnClick = 1 << 4,\n    ImGuiButtonFlags_PressedOnClickRelease = 1 << 5,\n    ImGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 6,\n    ImGuiButtonFlags_PressedOnRelease = 1 << 7,\n    ImGuiButtonFlags_PressedOnDoubleClick = 1 << 8,\n    ImGuiButtonFlags_PressedOnDragDropHold = 1 << 9,\n    ImGuiButtonFlags_FlattenChildren = 1 << 11,\n    ImGuiButtonFlags_AllowOverlap = 1 << 12,\n    ImGuiButtonFlags_AlignTextBaseLine = 1 << 15,\n    ImGuiButtonFlags_NoKeyModsAllowed = 1 << 16,\n    ImGuiButtonFlags_NoHoldingActiveId = 1 << 17,\n    ImGuiButtonFlags_NoNavFocus = 1 << 18,\n    ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19,\n    ImGuiButtonFlags_NoSetKeyOwner = 1 << 20,\n    ImGuiButtonFlags_NoTestKeyOwner = 1 << 21,\n    ImGuiButtonFlags_NoFocus = 1 << 22,\n    ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold,\n    ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease,\n}ImGuiButtonFlagsPrivate_;\ntypedef enum {\n    ImGuiComboFlags_CustomPreview = 1 << 20,\n}ImGuiComboFlagsPrivate_;\ntypedef enum {\n    ImGuiSliderFlags_Vertical = 1 << 20,\n    ImGuiSliderFlags_ReadOnly = 1 << 21,\n}ImGuiSliderFlagsPrivate_;\ntypedef enum {\n    ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20,\n    ImGuiSelectableFlags_SelectOnNav = 1 << 21,\n    ImGuiSelectableFlags_SelectOnClick = 1 << 22,\n    ImGuiSelectableFlags_SelectOnRelease = 1 << 23,\n    ImGuiSelectableFlags_SpanAvailWidth = 1 << 24,\n    ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25,\n    ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 26,\n    ImGuiSelectableFlags_NoSetKeyOwner = 1 << 27,\n}ImGuiSelectableFlagsPrivate_;\ntypedef enum {\n    ImGuiTreeNodeFlags_NoNavFocus = 1 << 27,\n    ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,\n    ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29,\n    ImGuiTreeNodeFlags_OpenOnMask_ = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow,\n    ImGuiTreeNodeFlags_DrawLinesMask_ = ImGuiTreeNodeFlags_DrawLinesNone | ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes,\n}ImGuiTreeNodeFlagsPrivate_;\ntypedef enum {\n    ImGuiSeparatorFlags_None = 0,\n    ImGuiSeparatorFlags_Horizontal = 1 << 0,\n    ImGuiSeparatorFlags_Vertical = 1 << 1,\n    ImGuiSeparatorFlags_SpanAllColumns = 1 << 2,\n}ImGuiSeparatorFlags_;\ntypedef enum {\n    ImGuiFocusRequestFlags_None = 0,\n    ImGuiFocusRequestFlags_RestoreFocusedChild = 1 << 0,\n    ImGuiFocusRequestFlags_UnlessBelowModal = 1 << 1,\n}ImGuiFocusRequestFlags_;\ntypedef enum {\n    ImGuiTextFlags_None = 0,\n    ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0,\n}ImGuiTextFlags_;\ntypedef enum {\n    ImGuiTooltipFlags_None = 0,\n    ImGuiTooltipFlags_OverridePrevious = 1 << 1,\n}ImGuiTooltipFlags_;\ntypedef enum {\n    ImGuiLayoutType_Horizontal = 0,\n    ImGuiLayoutType_Vertical = 1\n}ImGuiLayoutType_;\ntypedef enum {\n    ImGuiLogFlags_None = 0,\n    ImGuiLogFlags_OutputTTY = 1 << 0,\n    ImGuiLogFlags_OutputFile = 1 << 1,\n    ImGuiLogFlags_OutputBuffer = 1 << 2,\n    ImGuiLogFlags_OutputClipboard = 1 << 3,\n    ImGuiLogFlags_OutputMask_ = ImGuiLogFlags_OutputTTY | ImGuiLogFlags_OutputFile | ImGuiLogFlags_OutputBuffer | ImGuiLogFlags_OutputClipboard,\n}ImGuiLogFlags_;\ntypedef enum {\n    ImGuiAxis_None = -1,\n    ImGuiAxis_X = 0,\n    ImGuiAxis_Y = 1\n}ImGuiAxis;\ntypedef enum {\n    ImGuiPlotType_Lines,\n    ImGuiPlotType_Histogram,\n}ImGuiPlotType;\ntypedef struct ImGuiComboPreviewData ImGuiComboPreviewData;\nstruct ImGuiComboPreviewData\n{\n    ImRect PreviewRect;\n    ImVec2 BackupCursorPos;\n    ImVec2 BackupCursorMaxPos;\n    ImVec2 BackupCursorPosPrevLine;\n    float BackupPrevLineTextBaseOffset;\n    ImGuiLayoutType BackupLayout;\n};\nstruct ImGuiGroupData\n{\n    ImGuiID WindowID;\n    ImVec2 BackupCursorPos;\n    ImVec2 BackupCursorMaxPos;\n    ImVec2 BackupCursorPosPrevLine;\n    ImVec1 BackupIndent;\n    ImVec1 BackupGroupOffset;\n    ImVec2 BackupCurrLineSize;\n    float BackupCurrLineTextBaseOffset;\n    ImGuiID BackupActiveIdIsAlive;\n    bool BackupDeactivatedIdIsAlive;\n    bool BackupHoveredIdIsAlive;\n    bool BackupIsSameLine;\n    bool EmitItem;\n};\nstruct ImGuiMenuColumns\n{\n    ImU32 TotalWidth;\n    ImU32 NextTotalWidth;\n    ImU16 Spacing;\n    ImU16 OffsetIcon;\n    ImU16 OffsetLabel;\n    ImU16 OffsetShortcut;\n    ImU16 OffsetMark;\n    ImU16 Widths[4];\n};\ntypedef struct ImGuiInputTextDeactivatedState ImGuiInputTextDeactivatedState;\nstruct ImGuiInputTextDeactivatedState\n{\n    ImGuiID ID;\n    ImVector_char TextA;\n}; struct STB_TexteditState;\ntypedef STB_TexteditState ImStbTexteditState;\nstruct ImGuiInputTextState\n{\n    ImGuiContext* Ctx;\n    ImStbTexteditState* Stb;\n    ImGuiInputTextFlags Flags;\n    ImGuiID ID;\n    int TextLen;\n    const char* TextSrc;\n    ImVector_char TextA;\n    ImVector_char TextToRevertTo;\n    ImVector_char CallbackTextBackup;\n    int BufCapacity;\n    ImVec2 Scroll;\n    float CursorAnim;\n    bool CursorFollow;\n    bool SelectedAllMouseLock;\n    bool Edited;\n    bool WantReloadUserBuf;\n    int ReloadSelectionStart;\n    int ReloadSelectionEnd;\n};\ntypedef enum {\n    ImGuiWindowRefreshFlags_None = 0,\n    ImGuiWindowRefreshFlags_TryToAvoidRefresh = 1 << 0,\n    ImGuiWindowRefreshFlags_RefreshOnHover = 1 << 1,\n    ImGuiWindowRefreshFlags_RefreshOnFocus = 1 << 2,\n}ImGuiWindowRefreshFlags_;\ntypedef enum {\n    ImGuiNextWindowDataFlags_None = 0,\n    ImGuiNextWindowDataFlags_HasPos = 1 << 0,\n    ImGuiNextWindowDataFlags_HasSize = 1 << 1,\n    ImGuiNextWindowDataFlags_HasContentSize = 1 << 2,\n    ImGuiNextWindowDataFlags_HasCollapsed = 1 << 3,\n    ImGuiNextWindowDataFlags_HasSizeConstraint = 1 << 4,\n    ImGuiNextWindowDataFlags_HasFocus = 1 << 5,\n    ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6,\n    ImGuiNextWindowDataFlags_HasScroll = 1 << 7,\n    ImGuiNextWindowDataFlags_HasWindowFlags = 1 << 8,\n    ImGuiNextWindowDataFlags_HasChildFlags = 1 << 9,\n    ImGuiNextWindowDataFlags_HasRefreshPolicy = 1 << 10,\n    ImGuiNextWindowDataFlags_HasViewport = 1 << 11,\n    ImGuiNextWindowDataFlags_HasDock = 1 << 12,\n    ImGuiNextWindowDataFlags_HasWindowClass = 1 << 13,\n}ImGuiNextWindowDataFlags_;\nstruct ImGuiNextWindowData\n{\n    ImGuiNextWindowDataFlags HasFlags;\n    ImGuiCond PosCond;\n    ImGuiCond SizeCond;\n    ImGuiCond CollapsedCond;\n    ImGuiCond DockCond;\n    ImVec2 PosVal;\n    ImVec2 PosPivotVal;\n    ImVec2 SizeVal;\n    ImVec2 ContentSizeVal;\n    ImVec2 ScrollVal;\n    ImGuiWindowFlags WindowFlags;\n    ImGuiChildFlags ChildFlags;\n    bool PosUndock;\n    bool CollapsedVal;\n    ImRect SizeConstraintRect;\n    ImGuiSizeCallback SizeCallback;\n    void* SizeCallbackUserData;\n    float BgAlphaVal;\n    ImGuiID ViewportId;\n    ImGuiID DockId;\n    ImGuiWindowClass WindowClass;\n    ImVec2 MenuBarOffsetMinVal;\n    ImGuiWindowRefreshFlags RefreshFlagsVal;\n};\ntypedef enum {\n    ImGuiNextItemDataFlags_None = 0,\n    ImGuiNextItemDataFlags_HasWidth = 1 << 0,\n    ImGuiNextItemDataFlags_HasOpen = 1 << 1,\n    ImGuiNextItemDataFlags_HasShortcut = 1 << 2,\n    ImGuiNextItemDataFlags_HasRefVal = 1 << 3,\n    ImGuiNextItemDataFlags_HasStorageID = 1 << 4,\n}ImGuiNextItemDataFlags_;\nstruct ImGuiNextItemData\n{\n    ImGuiNextItemDataFlags HasFlags;\n    ImGuiItemFlags ItemFlags;\n    ImGuiID FocusScopeId;\n    ImGuiSelectionUserData SelectionUserData;\n    float Width;\n    ImGuiKeyChord Shortcut;\n    ImGuiInputFlags ShortcutFlags;\n    bool OpenVal;\n    ImU8 OpenCond;\n    ImGuiDataTypeStorage RefVal;\n    ImGuiID StorageId;\n};\nstruct ImGuiLastItemData\n{\n    ImGuiID ID;\n    ImGuiItemFlags ItemFlags;\n    ImGuiItemStatusFlags StatusFlags;\n    ImRect Rect;\n    ImRect NavRect;\n    ImRect DisplayRect;\n    ImRect ClipRect;\n    ImGuiKeyChord Shortcut;\n};\nstruct ImGuiTreeNodeStackData\n{\n    ImGuiID ID;\n    ImGuiTreeNodeFlags TreeFlags;\n    ImGuiItemFlags ItemFlags;\n    ImRect NavRect;\n    float DrawLinesX1;\n    float DrawLinesToNodesY2;\n    ImGuiTableColumnIdx DrawLinesTableColumn;\n};\nstruct ImGuiErrorRecoveryState\n{\n    short SizeOfWindowStack;\n    short SizeOfIDStack;\n    short SizeOfTreeStack;\n    short SizeOfColorStack;\n    short SizeOfStyleVarStack;\n    short SizeOfFontStack;\n    short SizeOfFocusScopeStack;\n    short SizeOfGroupStack;\n    short SizeOfItemFlagsStack;\n    short SizeOfBeginPopupStack;\n    short SizeOfDisabledStack;\n};\ntypedef struct ImGuiWindowStackData ImGuiWindowStackData;\nstruct ImGuiWindowStackData\n{\n    ImGuiWindow* Window;\n    ImGuiLastItemData ParentLastItemDataBackup;\n    ImGuiErrorRecoveryState StackSizesInBegin;\n    bool DisabledOverrideReenable;\n    float DisabledOverrideReenableAlphaBackup;\n};\ntypedef struct ImGuiShrinkWidthItem ImGuiShrinkWidthItem;\nstruct ImGuiShrinkWidthItem\n{\n    int Index;\n    float Width;\n    float InitialWidth;\n};\ntypedef struct ImGuiPtrOrIndex ImGuiPtrOrIndex;\nstruct ImGuiPtrOrIndex\n{\n    void* Ptr;\n    int Index;\n};\nstruct ImGuiDeactivatedItemData\n{\n    ImGuiID ID;\n    int ElapseFrame;\n    bool HasBeenEditedBefore;\n    bool IsAlive;\n};\ntypedef enum {\n    ImGuiPopupPositionPolicy_Default,\n    ImGuiPopupPositionPolicy_ComboBox,\n    ImGuiPopupPositionPolicy_Tooltip,\n}ImGuiPopupPositionPolicy;\nstruct ImGuiPopupData\n{\n    ImGuiID PopupId;\n    ImGuiWindow* Window;\n    ImGuiWindow* RestoreNavWindow;\n    int ParentNavLayer;\n    int OpenFrameCount;\n    ImGuiID OpenParentId;\n    ImVec2 OpenPopupPos;\n    ImVec2 OpenMousePos;\n};\ntypedef struct ImBitArray_ImGuiKey_NamedKey_COUNT__lessImGuiKey_NamedKey_BEGIN {ImU32 Storage[(ImGuiKey_NamedKey_COUNT+31)>>5];} ImBitArray_ImGuiKey_NamedKey_COUNT__lessImGuiKey_NamedKey_BEGIN;\n\ntypedef ImBitArray_ImGuiKey_NamedKey_COUNT__lessImGuiKey_NamedKey_BEGIN ImBitArrayForNamedKeys;\ntypedef enum {\n    ImGuiInputEventType_None = 0,\n    ImGuiInputEventType_MousePos,\n    ImGuiInputEventType_MouseWheel,\n    ImGuiInputEventType_MouseButton,\n    ImGuiInputEventType_MouseViewport,\n    ImGuiInputEventType_Key,\n    ImGuiInputEventType_Text,\n    ImGuiInputEventType_Focus,\n    ImGuiInputEventType_COUNT\n}ImGuiInputEventType;\ntypedef enum {\n    ImGuiInputSource_None = 0,\n    ImGuiInputSource_Mouse,\n    ImGuiInputSource_Keyboard,\n    ImGuiInputSource_Gamepad,\n    ImGuiInputSource_COUNT\n}ImGuiInputSource;\ntypedef struct ImGuiInputEventMousePos ImGuiInputEventMousePos;\nstruct ImGuiInputEventMousePos\n{ float PosX, PosY; ImGuiMouseSource MouseSource;\n};\ntypedef struct ImGuiInputEventMouseWheel ImGuiInputEventMouseWheel;\nstruct ImGuiInputEventMouseWheel\n{ float WheelX, WheelY; ImGuiMouseSource MouseSource;\n};\ntypedef struct ImGuiInputEventMouseButton ImGuiInputEventMouseButton;\nstruct ImGuiInputEventMouseButton\n{ int Button; bool Down; ImGuiMouseSource MouseSource;\n};\ntypedef struct ImGuiInputEventMouseViewport ImGuiInputEventMouseViewport;\nstruct ImGuiInputEventMouseViewport\n{ ImGuiID HoveredViewportID;\n};\ntypedef struct ImGuiInputEventKey ImGuiInputEventKey;\nstruct ImGuiInputEventKey\n{ ImGuiKey Key; bool Down; float AnalogValue;\n};\ntypedef struct ImGuiInputEventText ImGuiInputEventText;\nstruct ImGuiInputEventText\n{ unsigned int Char;\n};\ntypedef struct ImGuiInputEventAppFocused ImGuiInputEventAppFocused;\nstruct ImGuiInputEventAppFocused\n{ bool Focused;\n};\ntypedef struct ImGuiInputEvent ImGuiInputEvent;\nstruct ImGuiInputEvent\n{\n    ImGuiInputEventType Type;\n    ImGuiInputSource Source;\n    ImU32 EventId;\n    union\n    {\n        ImGuiInputEventMousePos MousePos;\n        ImGuiInputEventMouseWheel MouseWheel;\n        ImGuiInputEventMouseButton MouseButton;\n        ImGuiInputEventMouseViewport MouseViewport;\n        ImGuiInputEventKey Key;\n        ImGuiInputEventText Text;\n        ImGuiInputEventAppFocused AppFocused;\n    };\n    bool AddedByTestEngine;\n};\ntypedef ImS16 ImGuiKeyRoutingIndex;\ntypedef struct ImGuiKeyRoutingData ImGuiKeyRoutingData;\nstruct ImGuiKeyRoutingData\n{\n    ImGuiKeyRoutingIndex NextEntryIndex;\n    ImU16 Mods;\n    ImU8 RoutingCurrScore;\n    ImU8 RoutingNextScore;\n    ImGuiID RoutingCurr;\n    ImGuiID RoutingNext;\n};\ntypedef struct ImGuiKeyRoutingTable ImGuiKeyRoutingTable;\ntypedef struct ImVector_ImGuiKeyRoutingData {int Size;int Capacity;ImGuiKeyRoutingData* Data;} ImVector_ImGuiKeyRoutingData;\n\nstruct ImGuiKeyRoutingTable\n{\n    ImGuiKeyRoutingIndex Index[ImGuiKey_NamedKey_COUNT];\n    ImVector_ImGuiKeyRoutingData Entries;\n    ImVector_ImGuiKeyRoutingData EntriesNext;\n};\ntypedef struct ImGuiKeyOwnerData ImGuiKeyOwnerData;\nstruct ImGuiKeyOwnerData\n{\n    ImGuiID OwnerCurr;\n    ImGuiID OwnerNext;\n    bool LockThisFrame;\n    bool LockUntilRelease;\n};\ntypedef enum {\n    ImGuiInputFlags_RepeatRateDefault = 1 << 1,\n    ImGuiInputFlags_RepeatRateNavMove = 1 << 2,\n    ImGuiInputFlags_RepeatRateNavTweak = 1 << 3,\n    ImGuiInputFlags_RepeatUntilRelease = 1 << 4,\n    ImGuiInputFlags_RepeatUntilKeyModsChange = 1 << 5,\n    ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone = 1 << 6,\n    ImGuiInputFlags_RepeatUntilOtherKeyPress = 1 << 7,\n    ImGuiInputFlags_LockThisFrame = 1 << 20,\n    ImGuiInputFlags_LockUntilRelease = 1 << 21,\n    ImGuiInputFlags_CondHovered = 1 << 22,\n    ImGuiInputFlags_CondActive = 1 << 23,\n    ImGuiInputFlags_CondDefault_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,\n    ImGuiInputFlags_RepeatRateMask_ = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak,\n    ImGuiInputFlags_RepeatUntilMask_ = ImGuiInputFlags_RepeatUntilRelease | ImGuiInputFlags_RepeatUntilKeyModsChange | ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone | ImGuiInputFlags_RepeatUntilOtherKeyPress,\n    ImGuiInputFlags_RepeatMask_ = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_,\n    ImGuiInputFlags_CondMask_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,\n    ImGuiInputFlags_RouteTypeMask_ = ImGuiInputFlags_RouteActive | ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteAlways,\n    ImGuiInputFlags_RouteOptionsMask_ = ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused | ImGuiInputFlags_RouteFromRootWindow,\n    ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_RepeatMask_,\n    ImGuiInputFlags_SupportedByIsMouseClicked = ImGuiInputFlags_Repeat,\n    ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_,\n    ImGuiInputFlags_SupportedBySetNextItemShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_ | ImGuiInputFlags_Tooltip,\n    ImGuiInputFlags_SupportedBySetKeyOwner = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease,\n    ImGuiInputFlags_SupportedBySetItemKeyOwner = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_,\n}ImGuiInputFlagsPrivate_;\ntypedef struct ImGuiListClipperRange ImGuiListClipperRange;\nstruct ImGuiListClipperRange\n{\n    int Min;\n    int Max;\n    bool PosToIndexConvert;\n    ImS8 PosToIndexOffsetMin;\n    ImS8 PosToIndexOffsetMax;\n};\ntypedef struct ImGuiListClipperData ImGuiListClipperData;\ntypedef struct ImVector_ImGuiListClipperRange {int Size;int Capacity;ImGuiListClipperRange* Data;} ImVector_ImGuiListClipperRange;\n\nstruct ImGuiListClipperData\n{\n    ImGuiListClipper* ListClipper;\n    float LossynessOffset;\n    int StepNo;\n    int ItemsFrozen;\n    ImVector_ImGuiListClipperRange Ranges;\n};\ntypedef enum {\n    ImGuiActivateFlags_None = 0,\n    ImGuiActivateFlags_PreferInput = 1 << 0,\n    ImGuiActivateFlags_PreferTweak = 1 << 1,\n    ImGuiActivateFlags_TryToPreserveState = 1 << 2,\n    ImGuiActivateFlags_FromTabbing = 1 << 3,\n    ImGuiActivateFlags_FromShortcut = 1 << 4,\n}ImGuiActivateFlags_;\ntypedef enum {\n    ImGuiScrollFlags_None = 0,\n    ImGuiScrollFlags_KeepVisibleEdgeX = 1 << 0,\n    ImGuiScrollFlags_KeepVisibleEdgeY = 1 << 1,\n    ImGuiScrollFlags_KeepVisibleCenterX = 1 << 2,\n    ImGuiScrollFlags_KeepVisibleCenterY = 1 << 3,\n    ImGuiScrollFlags_AlwaysCenterX = 1 << 4,\n    ImGuiScrollFlags_AlwaysCenterY = 1 << 5,\n    ImGuiScrollFlags_NoScrollParent = 1 << 6,\n    ImGuiScrollFlags_MaskX_ = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX,\n    ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY,\n}ImGuiScrollFlags_;\ntypedef enum {\n    ImGuiNavRenderCursorFlags_None = 0,\n    ImGuiNavRenderCursorFlags_Compact = 1 << 1,\n    ImGuiNavRenderCursorFlags_AlwaysDraw = 1 << 2,\n    ImGuiNavRenderCursorFlags_NoRounding = 1 << 3,\n}ImGuiNavRenderCursorFlags_;\ntypedef enum {\n    ImGuiNavMoveFlags_None = 0,\n    ImGuiNavMoveFlags_LoopX = 1 << 0,\n    ImGuiNavMoveFlags_LoopY = 1 << 1,\n    ImGuiNavMoveFlags_WrapX = 1 << 2,\n    ImGuiNavMoveFlags_WrapY = 1 << 3,\n    ImGuiNavMoveFlags_WrapMask_ = ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_WrapY,\n    ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4,\n    ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5,\n    ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6,\n    ImGuiNavMoveFlags_Forwarded = 1 << 7,\n    ImGuiNavMoveFlags_DebugNoResult = 1 << 8,\n    ImGuiNavMoveFlags_FocusApi = 1 << 9,\n    ImGuiNavMoveFlags_IsTabbing = 1 << 10,\n    ImGuiNavMoveFlags_IsPageMove = 1 << 11,\n    ImGuiNavMoveFlags_Activate = 1 << 12,\n    ImGuiNavMoveFlags_NoSelect = 1 << 13,\n    ImGuiNavMoveFlags_NoSetNavCursorVisible = 1 << 14,\n    ImGuiNavMoveFlags_NoClearActiveId = 1 << 15,\n}ImGuiNavMoveFlags_;\ntypedef enum {\n    ImGuiNavLayer_Main = 0,\n    ImGuiNavLayer_Menu = 1,\n    ImGuiNavLayer_COUNT\n}ImGuiNavLayer;\nstruct ImGuiNavItemData\n{\n    ImGuiWindow* Window;\n    ImGuiID ID;\n    ImGuiID FocusScopeId;\n    ImRect RectRel;\n    ImGuiItemFlags ItemFlags;\n    float DistBox;\n    float DistCenter;\n    float DistAxial;\n    ImGuiSelectionUserData SelectionUserData;\n};\ntypedef struct ImGuiFocusScopeData ImGuiFocusScopeData;\nstruct ImGuiFocusScopeData\n{\n    ImGuiID ID;\n    ImGuiID WindowID;\n};\ntypedef enum {\n    ImGuiTypingSelectFlags_None = 0,\n    ImGuiTypingSelectFlags_AllowBackspace = 1 << 0,\n    ImGuiTypingSelectFlags_AllowSingleCharMode = 1 << 1,\n}ImGuiTypingSelectFlags_;\nstruct ImGuiTypingSelectRequest\n{\n    ImGuiTypingSelectFlags Flags;\n    int SearchBufferLen;\n    const char* SearchBuffer;\n    bool SelectRequest;\n    bool SingleCharMode;\n    ImS8 SingleCharSize;\n};\nstruct ImGuiTypingSelectState\n{\n    ImGuiTypingSelectRequest Request;\n    char SearchBuffer[64];\n    ImGuiID FocusScope;\n    int LastRequestFrame;\n    float LastRequestTime;\n    bool SingleCharModeLock;\n};\ntypedef enum {\n    ImGuiOldColumnFlags_None = 0,\n    ImGuiOldColumnFlags_NoBorder = 1 << 0,\n    ImGuiOldColumnFlags_NoResize = 1 << 1,\n    ImGuiOldColumnFlags_NoPreserveWidths = 1 << 2,\n    ImGuiOldColumnFlags_NoForceWithinWindow = 1 << 3,\n    ImGuiOldColumnFlags_GrowParentContentsSize = 1 << 4,\n}ImGuiOldColumnFlags_;\nstruct ImGuiOldColumnData\n{\n    float OffsetNorm;\n    float OffsetNormBeforeResize;\n    ImGuiOldColumnFlags Flags;\n    ImRect ClipRect;\n};\ntypedef struct ImVector_ImGuiOldColumnData {int Size;int Capacity;ImGuiOldColumnData* Data;} ImVector_ImGuiOldColumnData;\n\nstruct ImGuiOldColumns\n{\n    ImGuiID ID;\n    ImGuiOldColumnFlags Flags;\n    bool IsFirstFrame;\n    bool IsBeingResized;\n    int Current;\n    int Count;\n    float OffMinX, OffMaxX;\n    float LineMinY, LineMaxY;\n    float HostCursorPosY;\n    float HostCursorMaxPosX;\n    ImRect HostInitialClipRect;\n    ImRect HostBackupClipRect;\n    ImRect HostBackupParentWorkRect;\n    ImVector_ImGuiOldColumnData Columns;\n    ImDrawListSplitter Splitter;\n};\nstruct ImGuiBoxSelectState\n{\n    ImGuiID ID;\n    bool IsActive;\n    bool IsStarting;\n    bool IsStartedFromVoid;\n    bool IsStartedSetNavIdOnce;\n    bool RequestClear;\n    ImGuiKeyChord KeyMods : 16;\n    ImVec2 StartPosRel;\n    ImVec2 EndPosRel;\n    ImVec2 ScrollAccum;\n    ImGuiWindow* Window;\n    bool UnclipMode;\n    ImRect UnclipRect;\n    ImRect BoxSelectRectPrev;\n    ImRect BoxSelectRectCurr;\n};\nstruct ImGuiMultiSelectTempData\n{\n    ImGuiMultiSelectIO IO;\n    ImGuiMultiSelectState* Storage;\n    ImGuiID FocusScopeId;\n    ImGuiMultiSelectFlags Flags;\n    ImVec2 ScopeRectMin;\n    ImVec2 BackupCursorMaxPos;\n    ImGuiSelectionUserData LastSubmittedItem;\n    ImGuiID BoxSelectId;\n    ImGuiKeyChord KeyMods;\n    ImS8 LoopRequestSetAll;\n    bool IsEndIO;\n    bool IsFocused;\n    bool IsKeyboardSetRange;\n    bool NavIdPassedBy;\n    bool RangeSrcPassedBy;\n    bool RangeDstPassedBy;\n};\nstruct ImGuiMultiSelectState\n{\n    ImGuiWindow* Window;\n    ImGuiID ID;\n    int LastFrameActive;\n    int LastSelectionSize;\n    ImS8 RangeSelected;\n    ImS8 NavIdSelected;\n    ImGuiSelectionUserData RangeSrcItem;\n    ImGuiSelectionUserData NavIdItem;\n};\ntypedef enum {\n    ImGuiDockNodeFlags_DockSpace = 1 << 10,\n    ImGuiDockNodeFlags_CentralNode = 1 << 11,\n    ImGuiDockNodeFlags_NoTabBar = 1 << 12,\n    ImGuiDockNodeFlags_HiddenTabBar = 1 << 13,\n    ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14,\n    ImGuiDockNodeFlags_NoCloseButton = 1 << 15,\n    ImGuiDockNodeFlags_NoResizeX = 1 << 16,\n    ImGuiDockNodeFlags_NoResizeY = 1 << 17,\n    ImGuiDockNodeFlags_DockedWindowsInFocusRoute= 1 << 18,\n    ImGuiDockNodeFlags_NoDockingSplitOther = 1 << 19,\n    ImGuiDockNodeFlags_NoDockingOverMe = 1 << 20,\n    ImGuiDockNodeFlags_NoDockingOverOther = 1 << 21,\n    ImGuiDockNodeFlags_NoDockingOverEmpty = 1 << 22,\n    ImGuiDockNodeFlags_NoDocking = ImGuiDockNodeFlags_NoDockingOverMe | ImGuiDockNodeFlags_NoDockingOverOther | ImGuiDockNodeFlags_NoDockingOverEmpty | ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoDockingSplitOther,\n    ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0,\n    ImGuiDockNodeFlags_NoResizeFlagsMask_ = (int)ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY,\n    ImGuiDockNodeFlags_LocalFlagsTransferMask_ = (int)ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | (int)ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton,\n    ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton,\n}ImGuiDockNodeFlagsPrivate_;\ntypedef enum {\n    ImGuiDataAuthority_Auto,\n    ImGuiDataAuthority_DockNode,\n    ImGuiDataAuthority_Window,\n}ImGuiDataAuthority_;\ntypedef enum {\n    ImGuiDockNodeState_Unknown,\n    ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow,\n    ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing,\n    ImGuiDockNodeState_HostWindowVisible,\n}ImGuiDockNodeState;\ntypedef struct ImVector_ImGuiWindowPtr {int Size;int Capacity;ImGuiWindow** Data;} ImVector_ImGuiWindowPtr;\n\nstruct ImGuiDockNode\n{\n    ImGuiID ID;\n    ImGuiDockNodeFlags SharedFlags;\n    ImGuiDockNodeFlags LocalFlags;\n    ImGuiDockNodeFlags LocalFlagsInWindows;\n    ImGuiDockNodeFlags MergedFlags;\n    ImGuiDockNodeState State;\n    ImGuiDockNode* ParentNode;\n    ImGuiDockNode* ChildNodes[2];\n    ImVector_ImGuiWindowPtr Windows;\n    ImGuiTabBar* TabBar;\n    ImVec2 Pos;\n    ImVec2 Size;\n    ImVec2 SizeRef;\n    ImGuiAxis SplitAxis;\n    ImGuiWindowClass WindowClass;\n    ImU32 LastBgColor;\n    ImGuiWindow* HostWindow;\n    ImGuiWindow* VisibleWindow;\n    ImGuiDockNode* CentralNode;\n    ImGuiDockNode* OnlyNodeWithWindows;\n    int CountNodeWithWindows;\n    int LastFrameAlive;\n    int LastFrameActive;\n    int LastFrameFocused;\n    ImGuiID LastFocusedNodeId;\n    ImGuiID SelectedTabId;\n    ImGuiID WantCloseTabId;\n    ImGuiID RefViewportId;\n    ImGuiDataAuthority AuthorityForPos :3;\n    ImGuiDataAuthority AuthorityForSize :3;\n    ImGuiDataAuthority AuthorityForViewport :3;\n    bool IsVisible :1;\n    bool IsFocused :1;\n    bool IsBgDrawnThisFrame :1;\n    bool HasCloseButton :1;\n    bool HasWindowMenuButton :1;\n    bool HasCentralNodeChild :1;\n    bool WantCloseAll :1;\n    bool WantLockSizeOnce :1;\n    bool WantMouseMove :1;\n    bool WantHiddenTabBarUpdate :1;\n    bool WantHiddenTabBarToggle :1;\n};\ntypedef enum {\n    ImGuiWindowDockStyleCol_Text,\n    ImGuiWindowDockStyleCol_TabHovered,\n    ImGuiWindowDockStyleCol_TabFocused,\n    ImGuiWindowDockStyleCol_TabSelected,\n    ImGuiWindowDockStyleCol_TabSelectedOverline,\n    ImGuiWindowDockStyleCol_TabDimmed,\n    ImGuiWindowDockStyleCol_TabDimmedSelected,\n    ImGuiWindowDockStyleCol_TabDimmedSelectedOverline,\n    ImGuiWindowDockStyleCol_COUNT\n}ImGuiWindowDockStyleCol;\nstruct ImGuiWindowDockStyle\n{\n    ImU32 Colors[ImGuiWindowDockStyleCol_COUNT];\n};\ntypedef struct ImVector_ImGuiDockRequest {int Size;int Capacity;ImGuiDockRequest* Data;} ImVector_ImGuiDockRequest;\n\ntypedef struct ImVector_ImGuiDockNodeSettings {int Size;int Capacity;ImGuiDockNodeSettings* Data;} ImVector_ImGuiDockNodeSettings;\n\nstruct ImGuiDockContext\n{\n    ImGuiStorage Nodes;\n    ImVector_ImGuiDockRequest Requests;\n    ImVector_ImGuiDockNodeSettings NodesSettings;\n    bool WantFullRebuild;\n};\ntypedef struct ImGuiViewportP ImGuiViewportP;\nstruct ImGuiViewportP\n{\n    ImGuiViewport _ImGuiViewport;\n    ImGuiWindow* Window;\n    int Idx;\n    int LastFrameActive;\n    int LastFocusedStampCount;\n    ImGuiID LastNameHash;\n    ImVec2 LastPos;\n    ImVec2 LastSize;\n    float Alpha;\n    float LastAlpha;\n    bool LastFocusedHadNavWindow;\n    short PlatformMonitor;\n    int BgFgDrawListsLastFrame[2];\n    ImDrawList* BgFgDrawLists[2];\n    ImDrawData DrawDataP;\n    ImDrawDataBuilder DrawDataBuilder;\n    ImVec2 LastPlatformPos;\n    ImVec2 LastPlatformSize;\n    ImVec2 LastRendererSize;\n    ImVec2 WorkInsetMin;\n    ImVec2 WorkInsetMax;\n    ImVec2 BuildWorkInsetMin;\n    ImVec2 BuildWorkInsetMax;\n};\nstruct ImGuiWindowSettings\n{\n    ImGuiID ID;\n    ImVec2ih Pos;\n    ImVec2ih Size;\n    ImVec2ih ViewportPos;\n    ImGuiID ViewportId;\n    ImGuiID DockId;\n    ImGuiID ClassId;\n    short DockOrder;\n    bool Collapsed;\n    bool IsChild;\n    bool WantApply;\n    bool WantDelete;\n};\nstruct ImGuiSettingsHandler\n{\n    const char* TypeName;\n    ImGuiID TypeHash;\n    void (*ClearAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);\n    void (*ReadInitFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);\n    void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name);\n    void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line);\n    void (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);\n    void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf);\n    void* UserData;\n};\ntypedef enum {\nImGuiLocKey_VersionStr=0,\nImGuiLocKey_TableSizeOne=1,\nImGuiLocKey_TableSizeAllFit=2,\nImGuiLocKey_TableSizeAllDefault=3,\nImGuiLocKey_TableResetOrder=4,\nImGuiLocKey_WindowingMainMenuBar=5,\nImGuiLocKey_WindowingPopup=6,\nImGuiLocKey_WindowingUntitled=7,\nImGuiLocKey_OpenLink_s=8,\nImGuiLocKey_CopyLink=9,\nImGuiLocKey_DockingHideTabBar=10,\nImGuiLocKey_DockingHoldShiftToDock=11,\nImGuiLocKey_DockingDragToUndockOrMoveNode=12,\nImGuiLocKey_COUNT=13,\n}ImGuiLocKey;\nstruct ImGuiLocEntry\n{\n    ImGuiLocKey Key;\n    const char* Text;\n};\ntypedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg);\ntypedef enum {\n    ImGuiDebugLogFlags_None = 0,\n    ImGuiDebugLogFlags_EventError = 1 << 0,\n    ImGuiDebugLogFlags_EventActiveId = 1 << 1,\n    ImGuiDebugLogFlags_EventFocus = 1 << 2,\n    ImGuiDebugLogFlags_EventPopup = 1 << 3,\n    ImGuiDebugLogFlags_EventNav = 1 << 4,\n    ImGuiDebugLogFlags_EventClipper = 1 << 5,\n    ImGuiDebugLogFlags_EventSelection = 1 << 6,\n    ImGuiDebugLogFlags_EventIO = 1 << 7,\n    ImGuiDebugLogFlags_EventFont = 1 << 8,\n    ImGuiDebugLogFlags_EventInputRouting = 1 << 9,\n    ImGuiDebugLogFlags_EventDocking = 1 << 10,\n    ImGuiDebugLogFlags_EventViewport = 1 << 11,\n    ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventFont | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport,\n    ImGuiDebugLogFlags_OutputToTTY = 1 << 20,\n    ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21,\n}ImGuiDebugLogFlags_;\ntypedef struct ImGuiDebugAllocEntry ImGuiDebugAllocEntry;\nstruct ImGuiDebugAllocEntry\n{\n    int FrameCount;\n    ImS16 AllocCount;\n    ImS16 FreeCount;\n};\ntypedef struct ImGuiDebugAllocInfo ImGuiDebugAllocInfo;\nstruct ImGuiDebugAllocInfo\n{\n    int TotalAllocCount;\n    int TotalFreeCount;\n    ImS16 LastEntriesIdx;\n    ImGuiDebugAllocEntry LastEntriesBuf[6];\n};\nstruct ImGuiMetricsConfig\n{\n    bool ShowDebugLog;\n    bool ShowIDStackTool;\n    bool ShowWindowsRects;\n    bool ShowWindowsBeginOrder;\n    bool ShowTablesRects;\n    bool ShowDrawCmdMesh;\n    bool ShowDrawCmdBoundingBoxes;\n    bool ShowTextEncodingViewer;\n    bool ShowTextureUsedRect;\n    bool ShowDockingNodes;\n    int ShowWindowsRectsType;\n    int ShowTablesRectsType;\n    int HighlightMonitorIdx;\n    ImGuiID HighlightViewportID;\n    bool ShowFontPreview;\n};\ntypedef struct ImGuiStackLevelInfo ImGuiStackLevelInfo;\nstruct ImGuiStackLevelInfo\n{\n    ImGuiID ID;\n    ImS8 QueryFrameCount;\n    bool QuerySuccess;\n    ImGuiDataType DataType : 8;\n    char Desc[57];\n};\ntypedef struct ImGuiIDStackTool ImGuiIDStackTool;\ntypedef struct ImVector_ImGuiStackLevelInfo {int Size;int Capacity;ImGuiStackLevelInfo* Data;} ImVector_ImGuiStackLevelInfo;\n\nstruct ImGuiIDStackTool\n{\n    int LastActiveFrame;\n    int StackLevel;\n    ImGuiID QueryId;\n    ImVector_ImGuiStackLevelInfo Results;\n    bool CopyToClipboardOnCtrlC;\n    float CopyToClipboardLastTime;\n    ImGuiTextBuffer ResultPathBuf;\n};\ntypedef void (*ImGuiContextHookCallback)(ImGuiContext* ctx, ImGuiContextHook* hook);\ntypedef enum { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown, ImGuiContextHookType_PendingRemoval_ }ImGuiContextHookType;\nstruct ImGuiContextHook\n{\n    ImGuiID HookId;\n    ImGuiContextHookType Type;\n    ImGuiID Owner;\n    ImGuiContextHookCallback Callback;\n    void* UserData;\n};\ntypedef struct ImVector_ImFontAtlasPtr {int Size;int Capacity;ImFontAtlas** Data;} ImVector_ImFontAtlasPtr;\n\ntypedef struct ImVector_ImGuiInputEvent {int Size;int Capacity;ImGuiInputEvent* Data;} ImVector_ImGuiInputEvent;\n\ntypedef struct ImVector_ImGuiWindowStackData {int Size;int Capacity;ImGuiWindowStackData* Data;} ImVector_ImGuiWindowStackData;\n\ntypedef struct ImVector_ImGuiColorMod {int Size;int Capacity;ImGuiColorMod* Data;} ImVector_ImGuiColorMod;\n\ntypedef struct ImVector_ImGuiStyleMod {int Size;int Capacity;ImGuiStyleMod* Data;} ImVector_ImGuiStyleMod;\n\ntypedef struct ImVector_ImFontStackData {int Size;int Capacity;ImFontStackData* Data;} ImVector_ImFontStackData;\n\ntypedef struct ImVector_ImGuiFocusScopeData {int Size;int Capacity;ImGuiFocusScopeData* Data;} ImVector_ImGuiFocusScopeData;\n\ntypedef struct ImVector_ImGuiItemFlags {int Size;int Capacity;ImGuiItemFlags* Data;} ImVector_ImGuiItemFlags;\n\ntypedef struct ImVector_ImGuiGroupData {int Size;int Capacity;ImGuiGroupData* Data;} ImVector_ImGuiGroupData;\n\ntypedef struct ImVector_ImGuiPopupData {int Size;int Capacity;ImGuiPopupData* Data;} ImVector_ImGuiPopupData;\n\ntypedef struct ImVector_ImGuiTreeNodeStackData {int Size;int Capacity;ImGuiTreeNodeStackData* Data;} ImVector_ImGuiTreeNodeStackData;\n\ntypedef struct ImVector_ImGuiViewportPPtr {int Size;int Capacity;ImGuiViewportP** Data;} ImVector_ImGuiViewportPPtr;\n\ntypedef struct ImVector_unsigned_char {int Size;int Capacity;unsigned char* Data;} ImVector_unsigned_char;\n\ntypedef struct ImVector_ImGuiListClipperData {int Size;int Capacity;ImGuiListClipperData* Data;} ImVector_ImGuiListClipperData;\n\ntypedef struct ImVector_ImGuiTableTempData {int Size;int Capacity;ImGuiTableTempData* Data;} ImVector_ImGuiTableTempData;\n\ntypedef struct ImVector_ImGuiTable {int Size;int Capacity;ImGuiTable* Data;} ImVector_ImGuiTable;\n\ntypedef struct ImPool_ImGuiTable {ImVector_ImGuiTable Buf;ImGuiStorage Map;ImPoolIdx FreeIdx;ImPoolIdx AliveCount;} ImPool_ImGuiTable;\n\ntypedef struct ImVector_ImGuiTabBar {int Size;int Capacity;ImGuiTabBar* Data;} ImVector_ImGuiTabBar;\n\ntypedef struct ImPool_ImGuiTabBar {ImVector_ImGuiTabBar Buf;ImGuiStorage Map;ImPoolIdx FreeIdx;ImPoolIdx AliveCount;} ImPool_ImGuiTabBar;\n\ntypedef struct ImVector_ImGuiPtrOrIndex {int Size;int Capacity;ImGuiPtrOrIndex* Data;} ImVector_ImGuiPtrOrIndex;\n\ntypedef struct ImVector_ImGuiShrinkWidthItem {int Size;int Capacity;ImGuiShrinkWidthItem* Data;} ImVector_ImGuiShrinkWidthItem;\n\ntypedef struct ImVector_ImGuiMultiSelectTempData {int Size;int Capacity;ImGuiMultiSelectTempData* Data;} ImVector_ImGuiMultiSelectTempData;\n\ntypedef struct ImVector_ImGuiMultiSelectState {int Size;int Capacity;ImGuiMultiSelectState* Data;} ImVector_ImGuiMultiSelectState;\n\ntypedef struct ImPool_ImGuiMultiSelectState {ImVector_ImGuiMultiSelectState Buf;ImGuiStorage Map;ImPoolIdx FreeIdx;ImPoolIdx AliveCount;} ImPool_ImGuiMultiSelectState;\n\ntypedef struct ImVector_ImGuiID {int Size;int Capacity;ImGuiID* Data;} ImVector_ImGuiID;\n\ntypedef struct ImVector_ImGuiSettingsHandler {int Size;int Capacity;ImGuiSettingsHandler* Data;} ImVector_ImGuiSettingsHandler;\n\ntypedef struct ImChunkStream_ImGuiWindowSettings {ImVector_char Buf;} ImChunkStream_ImGuiWindowSettings;\n\ntypedef struct ImChunkStream_ImGuiTableSettings {ImVector_char Buf;} ImChunkStream_ImGuiTableSettings;\n\ntypedef struct ImVector_ImGuiContextHook {int Size;int Capacity;ImGuiContextHook* Data;} ImVector_ImGuiContextHook;\n\nstruct ImGuiContext\n{\n    bool Initialized;\n    ImGuiIO IO;\n    ImGuiPlatformIO PlatformIO;\n    ImGuiStyle Style;\n    ImGuiConfigFlags ConfigFlagsCurrFrame;\n    ImGuiConfigFlags ConfigFlagsLastFrame;\n    ImVector_ImFontAtlasPtr FontAtlases;\n    ImFont* Font;\n    ImFontBaked* FontBaked;\n    float FontSize;\n    float FontSizeBase;\n    float FontBakedScale;\n    float FontRasterizerDensity;\n    float CurrentDpiScale;\n    ImDrawListSharedData DrawListSharedData;\n    double Time;\n    int FrameCount;\n    int FrameCountEnded;\n    int FrameCountPlatformEnded;\n    int FrameCountRendered;\n    ImGuiID WithinEndChildID;\n    bool WithinFrameScope;\n    bool WithinFrameScopeWithImplicitWindow;\n    bool GcCompactAll;\n    bool TestEngineHookItems;\n    void* TestEngine;\n    char ContextName[16];\n    ImVector_ImGuiInputEvent InputEventsQueue;\n    ImVector_ImGuiInputEvent InputEventsTrail;\n    ImGuiMouseSource InputEventsNextMouseSource;\n    ImU32 InputEventsNextEventId;\n    ImVector_ImGuiWindowPtr Windows;\n    ImVector_ImGuiWindowPtr WindowsFocusOrder;\n    ImVector_ImGuiWindowPtr WindowsTempSortBuffer;\n    ImVector_ImGuiWindowStackData CurrentWindowStack;\n    ImGuiStorage WindowsById;\n    int WindowsActiveCount;\n    float WindowsBorderHoverPadding;\n    ImGuiID DebugBreakInWindow;\n    ImGuiWindow* CurrentWindow;\n    ImGuiWindow* HoveredWindow;\n    ImGuiWindow* HoveredWindowUnderMovingWindow;\n    ImGuiWindow* HoveredWindowBeforeClear;\n    ImGuiWindow* MovingWindow;\n    ImGuiWindow* WheelingWindow;\n    ImVec2 WheelingWindowRefMousePos;\n    int WheelingWindowStartFrame;\n    int WheelingWindowScrolledFrame;\n    float WheelingWindowReleaseTimer;\n    ImVec2 WheelingWindowWheelRemainder;\n    ImVec2 WheelingAxisAvg;\n    ImGuiID DebugDrawIdConflicts;\n    ImGuiID DebugHookIdInfo;\n    ImGuiID HoveredId;\n    ImGuiID HoveredIdPreviousFrame;\n    int HoveredIdPreviousFrameItemCount;\n    float HoveredIdTimer;\n    float HoveredIdNotActiveTimer;\n    bool HoveredIdAllowOverlap;\n    bool HoveredIdIsDisabled;\n    bool ItemUnclipByLog;\n    ImGuiID ActiveId;\n    ImGuiID ActiveIdIsAlive;\n    float ActiveIdTimer;\n    bool ActiveIdIsJustActivated;\n    bool ActiveIdAllowOverlap;\n    bool ActiveIdNoClearOnFocusLoss;\n    bool ActiveIdHasBeenPressedBefore;\n    bool ActiveIdHasBeenEditedBefore;\n    bool ActiveIdHasBeenEditedThisFrame;\n    bool ActiveIdFromShortcut;\n    int ActiveIdMouseButton : 8;\n    ImVec2 ActiveIdClickOffset;\n    ImGuiWindow* ActiveIdWindow;\n    ImGuiInputSource ActiveIdSource;\n    ImGuiID ActiveIdPreviousFrame;\n    ImGuiDeactivatedItemData DeactivatedItemData;\n    ImGuiDataTypeStorage ActiveIdValueOnActivation;\n    ImGuiID LastActiveId;\n    float LastActiveIdTimer;\n    double LastKeyModsChangeTime;\n    double LastKeyModsChangeFromNoneTime;\n    double LastKeyboardKeyPressTime;\n    ImBitArrayForNamedKeys KeysMayBeCharInput;\n    ImGuiKeyOwnerData KeysOwnerData[ImGuiKey_NamedKey_COUNT];\n    ImGuiKeyRoutingTable KeysRoutingTable;\n    ImU32 ActiveIdUsingNavDirMask;\n    bool ActiveIdUsingAllKeyboardKeys;\n    ImGuiKeyChord DebugBreakInShortcutRouting;\n    ImGuiID CurrentFocusScopeId;\n    ImGuiItemFlags CurrentItemFlags;\n    ImGuiID DebugLocateId;\n    ImGuiNextItemData NextItemData;\n    ImGuiLastItemData LastItemData;\n    ImGuiNextWindowData NextWindowData;\n    bool DebugShowGroupRects;\n    ImGuiCol DebugFlashStyleColorIdx;\n    ImVector_ImGuiColorMod ColorStack;\n    ImVector_ImGuiStyleMod StyleVarStack;\n    ImVector_ImFontStackData FontStack;\n    ImVector_ImGuiFocusScopeData FocusScopeStack;\n    ImVector_ImGuiItemFlags ItemFlagsStack;\n    ImVector_ImGuiGroupData GroupStack;\n    ImVector_ImGuiPopupData OpenPopupStack;\n    ImVector_ImGuiPopupData BeginPopupStack;\n    ImVector_ImGuiTreeNodeStackData TreeNodeStack;\n    ImVector_ImGuiViewportPPtr Viewports;\n    ImGuiViewportP* CurrentViewport;\n    ImGuiViewportP* MouseViewport;\n    ImGuiViewportP* MouseLastHoveredViewport;\n    ImGuiID PlatformLastFocusedViewportId;\n    ImGuiPlatformMonitor FallbackMonitor;\n    ImRect PlatformMonitorsFullWorkRect;\n    int ViewportCreatedCount;\n    int PlatformWindowsCreatedCount;\n    int ViewportFocusedStampCount;\n    bool NavCursorVisible;\n    bool NavHighlightItemUnderNav;\n    bool NavMousePosDirty;\n    bool NavIdIsAlive;\n    ImGuiID NavId;\n    ImGuiWindow* NavWindow;\n    ImGuiID NavFocusScopeId;\n    ImGuiNavLayer NavLayer;\n    ImGuiID NavActivateId;\n    ImGuiID NavActivateDownId;\n    ImGuiID NavActivatePressedId;\n    ImGuiActivateFlags NavActivateFlags;\n    ImVector_ImGuiFocusScopeData NavFocusRoute;\n    ImGuiID NavHighlightActivatedId;\n    float NavHighlightActivatedTimer;\n    ImGuiID NavNextActivateId;\n    ImGuiActivateFlags NavNextActivateFlags;\n    ImGuiInputSource NavInputSource;\n    ImGuiSelectionUserData NavLastValidSelectionUserData;\n    ImS8 NavCursorHideFrames;\n    bool NavAnyRequest;\n    bool NavInitRequest;\n    bool NavInitRequestFromMove;\n    ImGuiNavItemData NavInitResult;\n    bool NavMoveSubmitted;\n    bool NavMoveScoringItems;\n    bool NavMoveForwardToNextFrame;\n    ImGuiNavMoveFlags NavMoveFlags;\n    ImGuiScrollFlags NavMoveScrollFlags;\n    ImGuiKeyChord NavMoveKeyMods;\n    ImGuiDir NavMoveDir;\n    ImGuiDir NavMoveDirForDebug;\n    ImGuiDir NavMoveClipDir;\n    ImRect NavScoringRect;\n    ImRect NavScoringNoClipRect;\n    int NavScoringDebugCount;\n    int NavTabbingDir;\n    int NavTabbingCounter;\n    ImGuiNavItemData NavMoveResultLocal;\n    ImGuiNavItemData NavMoveResultLocalVisible;\n    ImGuiNavItemData NavMoveResultOther;\n    ImGuiNavItemData NavTabbingResultFirst;\n    ImGuiID NavJustMovedFromFocusScopeId;\n    ImGuiID NavJustMovedToId;\n    ImGuiID NavJustMovedToFocusScopeId;\n    ImGuiKeyChord NavJustMovedToKeyMods;\n    bool NavJustMovedToIsTabbing;\n    bool NavJustMovedToHasSelectionData;\n    bool ConfigNavWindowingWithGamepad;\n    ImGuiKeyChord ConfigNavWindowingKeyNext;\n    ImGuiKeyChord ConfigNavWindowingKeyPrev;\n    ImGuiWindow* NavWindowingTarget;\n    ImGuiWindow* NavWindowingTargetAnim;\n    ImGuiWindow* NavWindowingListWindow;\n    float NavWindowingTimer;\n    float NavWindowingHighlightAlpha;\n    ImGuiInputSource NavWindowingInputSource;\n    bool NavWindowingToggleLayer;\n    ImGuiKey NavWindowingToggleKey;\n    ImVec2 NavWindowingAccumDeltaPos;\n    ImVec2 NavWindowingAccumDeltaSize;\n    float DimBgRatio;\n    bool DragDropActive;\n    bool DragDropWithinSource;\n    bool DragDropWithinTarget;\n    ImGuiDragDropFlags DragDropSourceFlags;\n    int DragDropSourceFrameCount;\n    int DragDropMouseButton;\n    ImGuiPayload DragDropPayload;\n    ImRect DragDropTargetRect;\n    ImRect DragDropTargetClipRect;\n    ImGuiID DragDropTargetId;\n    ImGuiDragDropFlags DragDropAcceptFlags;\n    float DragDropAcceptIdCurrRectSurface;\n    ImGuiID DragDropAcceptIdCurr;\n    ImGuiID DragDropAcceptIdPrev;\n    int DragDropAcceptFrameCount;\n    ImGuiID DragDropHoldJustPressedId;\n    ImVector_unsigned_char DragDropPayloadBufHeap;\n    unsigned char DragDropPayloadBufLocal[16];\n    int ClipperTempDataStacked;\n    ImVector_ImGuiListClipperData ClipperTempData;\n    ImGuiTable* CurrentTable;\n    ImGuiID DebugBreakInTable;\n    int TablesTempDataStacked;\n    ImVector_ImGuiTableTempData TablesTempData;\n    ImPool_ImGuiTable Tables;\n    ImVector_float TablesLastTimeActive;\n    ImVector_ImDrawChannel DrawChannelsTempMergeBuffer;\n    ImGuiTabBar* CurrentTabBar;\n    ImPool_ImGuiTabBar TabBars;\n    ImVector_ImGuiPtrOrIndex CurrentTabBarStack;\n    ImVector_ImGuiShrinkWidthItem ShrinkWidthBuffer;\n    ImGuiBoxSelectState BoxSelectState;\n    ImGuiMultiSelectTempData* CurrentMultiSelect;\n    int MultiSelectTempDataStacked;\n    ImVector_ImGuiMultiSelectTempData MultiSelectTempData;\n    ImPool_ImGuiMultiSelectState MultiSelectStorage;\n    ImGuiID HoverItemDelayId;\n    ImGuiID HoverItemDelayIdPreviousFrame;\n    float HoverItemDelayTimer;\n    float HoverItemDelayClearTimer;\n    ImGuiID HoverItemUnlockedStationaryId;\n    ImGuiID HoverWindowUnlockedStationaryId;\n    ImGuiMouseCursor MouseCursor;\n    float MouseStationaryTimer;\n    ImVec2 MouseLastValidPos;\n    ImGuiInputTextState InputTextState;\n    ImGuiInputTextDeactivatedState InputTextDeactivatedState;\n    ImFontBaked InputTextPasswordFontBackupBaked;\n    ImFontFlags InputTextPasswordFontBackupFlags;\n    ImGuiID TempInputId;\n    ImGuiDataTypeStorage DataTypeZeroValue;\n    int BeginMenuDepth;\n    int BeginComboDepth;\n    ImGuiColorEditFlags ColorEditOptions;\n    ImGuiID ColorEditCurrentID;\n    ImGuiID ColorEditSavedID;\n    float ColorEditSavedHue;\n    float ColorEditSavedSat;\n    ImU32 ColorEditSavedColor;\n    ImVec4 ColorPickerRef;\n    ImGuiComboPreviewData ComboPreviewData;\n    ImRect WindowResizeBorderExpectedRect;\n    bool WindowResizeRelativeMode;\n    short ScrollbarSeekMode;\n    float ScrollbarClickDeltaToGrabCenter;\n    float SliderGrabClickOffset;\n    float SliderCurrentAccum;\n    bool SliderCurrentAccumDirty;\n    bool DragCurrentAccumDirty;\n    float DragCurrentAccum;\n    float DragSpeedDefaultRatio;\n    float DisabledAlphaBackup;\n    short DisabledStackSize;\n    short TooltipOverrideCount;\n    ImGuiWindow* TooltipPreviousWindow;\n    ImVector_char ClipboardHandlerData;\n    ImVector_ImGuiID MenusIdSubmittedThisFrame;\n    ImGuiTypingSelectState TypingSelectState;\n    ImGuiPlatformImeData PlatformImeData;\n    ImGuiPlatformImeData PlatformImeDataPrev;\n    ImVector_ImTextureDataPtr UserTextures;\n    ImGuiDockContext DockContext;\n    void (*DockNodeWindowMenuHandler)(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar);\n    bool SettingsLoaded;\n    float SettingsDirtyTimer;\n    ImGuiTextBuffer SettingsIniData;\n    ImVector_ImGuiSettingsHandler SettingsHandlers;\n    ImChunkStream_ImGuiWindowSettings SettingsWindows;\n    ImChunkStream_ImGuiTableSettings SettingsTables;\n    ImVector_ImGuiContextHook Hooks;\n    ImGuiID HookIdNext;\n    const char* LocalizationTable[ImGuiLocKey_COUNT];\n    bool LogEnabled;\n    ImGuiLogFlags LogFlags;\n    ImGuiWindow* LogWindow;\n    ImFileHandle LogFile;\n    ImGuiTextBuffer LogBuffer;\n    const char* LogNextPrefix;\n    const char* LogNextSuffix;\n    float LogLinePosY;\n    bool LogLineFirstItem;\n    int LogDepthRef;\n    int LogDepthToExpand;\n    int LogDepthToExpandDefault;\n    ImGuiErrorCallback ErrorCallback;\n    void* ErrorCallbackUserData;\n    ImVec2 ErrorTooltipLockedPos;\n    bool ErrorFirst;\n    int ErrorCountCurrentFrame;\n    ImGuiErrorRecoveryState StackSizesInNewFrame;\n    ImGuiErrorRecoveryState*StackSizesInBeginForCurrentWindow;\n    int DebugDrawIdConflictsCount;\n    ImGuiDebugLogFlags DebugLogFlags;\n    ImGuiTextBuffer DebugLogBuf;\n    ImGuiTextIndex DebugLogIndex;\n    int DebugLogSkippedErrors;\n    ImGuiDebugLogFlags DebugLogAutoDisableFlags;\n    ImU8 DebugLogAutoDisableFrames;\n    ImU8 DebugLocateFrames;\n    bool DebugBreakInLocateId;\n    ImGuiKeyChord DebugBreakKeyChord;\n    ImS8 DebugBeginReturnValueCullDepth;\n    bool DebugItemPickerActive;\n    ImU8 DebugItemPickerMouseButton;\n    ImGuiID DebugItemPickerBreakId;\n    float DebugFlashStyleColorTime;\n    ImVec4 DebugFlashStyleColorBackup;\n    ImGuiMetricsConfig DebugMetricsConfig;\n    ImGuiIDStackTool DebugIDStackTool;\n    ImGuiDebugAllocInfo DebugAllocInfo;\n    ImGuiDockNode* DebugHoveredDockNode;\n    float FramerateSecPerFrame[60];\n    int FramerateSecPerFrameIdx;\n    int FramerateSecPerFrameCount;\n    float FramerateSecPerFrameAccum;\n    int WantCaptureMouseNextFrame;\n    int WantCaptureKeyboardNextFrame;\n    int WantTextInputNextFrame;\n    ImVector_char TempBuffer;\n    char TempKeychordName[64];\n};\nstruct ImGuiWindowTempData\n{\n    ImVec2 CursorPos;\n    ImVec2 CursorPosPrevLine;\n    ImVec2 CursorStartPos;\n    ImVec2 CursorMaxPos;\n    ImVec2 IdealMaxPos;\n    ImVec2 CurrLineSize;\n    ImVec2 PrevLineSize;\n    float CurrLineTextBaseOffset;\n    float PrevLineTextBaseOffset;\n    bool IsSameLine;\n    bool IsSetPos;\n    ImVec1 Indent;\n    ImVec1 ColumnsOffset;\n    ImVec1 GroupOffset;\n    ImVec2 CursorStartPosLossyness;\n    ImGuiNavLayer NavLayerCurrent;\n    short NavLayersActiveMask;\n    short NavLayersActiveMaskNext;\n    bool NavIsScrollPushableX;\n    bool NavHideHighlightOneFrame;\n    bool NavWindowHasScrollY;\n    bool MenuBarAppending;\n    ImVec2 MenuBarOffset;\n    ImGuiMenuColumns MenuColumns;\n    int TreeDepth;\n    ImU32 TreeHasStackDataDepthMask;\n    ImU32 TreeRecordsClippedNodesY2Mask;\n    ImVector_ImGuiWindowPtr ChildWindows;\n    ImGuiStorage* StateStorage;\n    ImGuiOldColumns* CurrentColumns;\n    int CurrentTableIdx;\n    ImGuiLayoutType LayoutType;\n    ImGuiLayoutType ParentLayoutType;\n    ImU32 ModalDimBgColor;\n    ImGuiItemStatusFlags WindowItemStatusFlags;\n    ImGuiItemStatusFlags ChildItemStatusFlags;\n    ImGuiItemStatusFlags DockTabItemStatusFlags;\n    ImRect DockTabItemRect;\n    float ItemWidth;\n    float TextWrapPos;\n    ImVector_float ItemWidthStack;\n    ImVector_float TextWrapPosStack;\n};\ntypedef struct ImVector_ImGuiOldColumns {int Size;int Capacity;ImGuiOldColumns* Data;} ImVector_ImGuiOldColumns;\n\nstruct ImGuiWindow\n{\n    ImGuiContext* Ctx;\n    char* Name;\n    ImGuiID ID;\n    ImGuiWindowFlags Flags, FlagsPreviousFrame;\n    ImGuiChildFlags ChildFlags;\n    ImGuiWindowClass WindowClass;\n    ImGuiViewportP* Viewport;\n    ImGuiID ViewportId;\n    ImVec2 ViewportPos;\n    int ViewportAllowPlatformMonitorExtend;\n    ImVec2 Pos;\n    ImVec2 Size;\n    ImVec2 SizeFull;\n    ImVec2 ContentSize;\n    ImVec2 ContentSizeIdeal;\n    ImVec2 ContentSizeExplicit;\n    ImVec2 WindowPadding;\n    float WindowRounding;\n    float WindowBorderSize;\n    float TitleBarHeight, MenuBarHeight;\n    float DecoOuterSizeX1, DecoOuterSizeY1;\n    float DecoOuterSizeX2, DecoOuterSizeY2;\n    float DecoInnerSizeX1, DecoInnerSizeY1;\n    int NameBufLen;\n    ImGuiID MoveId;\n    ImGuiID TabId;\n    ImGuiID ChildId;\n    ImGuiID PopupId;\n    ImVec2 Scroll;\n    ImVec2 ScrollMax;\n    ImVec2 ScrollTarget;\n    ImVec2 ScrollTargetCenterRatio;\n    ImVec2 ScrollTargetEdgeSnapDist;\n    ImVec2 ScrollbarSizes;\n    bool ScrollbarX, ScrollbarY;\n    bool ScrollbarXStabilizeEnabled;\n    ImU8 ScrollbarXStabilizeToggledHistory;\n    bool ViewportOwned;\n    bool Active;\n    bool WasActive;\n    bool WriteAccessed;\n    bool Collapsed;\n    bool WantCollapseToggle;\n    bool SkipItems;\n    bool SkipRefresh;\n    bool Appearing;\n    bool Hidden;\n    bool IsFallbackWindow;\n    bool IsExplicitChild;\n    bool HasCloseButton;\n    signed char ResizeBorderHovered;\n    signed char ResizeBorderHeld;\n    short BeginCount;\n    short BeginCountPreviousFrame;\n    short BeginOrderWithinParent;\n    short BeginOrderWithinContext;\n    short FocusOrder;\n    ImS8 AutoFitFramesX, AutoFitFramesY;\n    bool AutoFitOnlyGrows;\n    ImGuiDir AutoPosLastDirection;\n    ImS8 HiddenFramesCanSkipItems;\n    ImS8 HiddenFramesCannotSkipItems;\n    ImS8 HiddenFramesForRenderOnly;\n    ImS8 DisableInputsFrames;\n    ImGuiCond SetWindowPosAllowFlags : 8;\n    ImGuiCond SetWindowSizeAllowFlags : 8;\n    ImGuiCond SetWindowCollapsedAllowFlags : 8;\n    ImGuiCond SetWindowDockAllowFlags : 8;\n    ImVec2 SetWindowPosVal;\n    ImVec2 SetWindowPosPivot;\n    ImVector_ImGuiID IDStack;\n    ImGuiWindowTempData DC;\n    ImRect OuterRectClipped;\n    ImRect InnerRect;\n    ImRect InnerClipRect;\n    ImRect WorkRect;\n    ImRect ParentWorkRect;\n    ImRect ClipRect;\n    ImRect ContentRegionRect;\n    ImVec2ih HitTestHoleSize;\n    ImVec2ih HitTestHoleOffset;\n    int LastFrameActive;\n    int LastFrameJustFocused;\n    float LastTimeActive;\n    float ItemWidthDefault;\n    ImGuiStorage StateStorage;\n    ImVector_ImGuiOldColumns ColumnsStorage;\n    float FontWindowScale;\n    float FontWindowScaleParents;\n    float FontRefSize;\n    int SettingsOffset;\n    ImDrawList* DrawList;\n    ImDrawList DrawListInst;\n    ImGuiWindow* ParentWindow;\n    ImGuiWindow* ParentWindowInBeginStack;\n    ImGuiWindow* RootWindow;\n    ImGuiWindow* RootWindowPopupTree;\n    ImGuiWindow* RootWindowDockTree;\n    ImGuiWindow* RootWindowForTitleBarHighlight;\n    ImGuiWindow* RootWindowForNav;\n    ImGuiWindow* ParentWindowForFocusRoute;\n    ImGuiWindow* NavLastChildNavWindow;\n    ImGuiID NavLastIds[ImGuiNavLayer_COUNT];\n    ImRect NavRectRel[ImGuiNavLayer_COUNT];\n    ImVec2 NavPreferredScoringPosRel[ImGuiNavLayer_COUNT];\n    ImGuiID NavRootFocusScopeId;\n    int MemoryDrawListIdxCapacity;\n    int MemoryDrawListVtxCapacity;\n    bool MemoryCompacted;\n    bool DockIsActive :1;\n    bool DockNodeIsVisible :1;\n    bool DockTabIsVisible :1;\n    bool DockTabWantClose :1;\n    short DockOrder;\n    ImGuiWindowDockStyle DockStyle;\n    ImGuiDockNode* DockNode;\n    ImGuiDockNode* DockNodeAsHost;\n    ImGuiID DockId;\n};\ntypedef enum {\n    ImGuiTabBarFlags_DockNode = 1 << 20,\n    ImGuiTabBarFlags_IsFocused = 1 << 21,\n    ImGuiTabBarFlags_SaveSettings = 1 << 22,\n}ImGuiTabBarFlagsPrivate_;\ntypedef enum {\n    ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing,\n    ImGuiTabItemFlags_NoCloseButton = 1 << 20,\n    ImGuiTabItemFlags_Button = 1 << 21,\n    ImGuiTabItemFlags_Invisible = 1 << 22,\n    ImGuiTabItemFlags_Unsorted = 1 << 23,\n}ImGuiTabItemFlagsPrivate_;\nstruct ImGuiTabItem\n{\n    ImGuiID ID;\n    ImGuiTabItemFlags Flags;\n    ImGuiWindow* Window;\n    int LastFrameVisible;\n    int LastFrameSelected;\n    float Offset;\n    float Width;\n    float ContentWidth;\n    float RequestedWidth;\n    ImS32 NameOffset;\n    ImS16 BeginOrder;\n    ImS16 IndexDuringLayout;\n    bool WantClose;\n};\ntypedef struct ImVector_ImGuiTabItem {int Size;int Capacity;ImGuiTabItem* Data;} ImVector_ImGuiTabItem;\n\nstruct ImGuiTabBar\n{\n    ImGuiWindow* Window;\n    ImVector_ImGuiTabItem Tabs;\n    ImGuiTabBarFlags Flags;\n    ImGuiID ID;\n    ImGuiID SelectedTabId;\n    ImGuiID NextSelectedTabId;\n    ImGuiID VisibleTabId;\n    int CurrFrameVisible;\n    int PrevFrameVisible;\n    ImRect BarRect;\n    float CurrTabsContentsHeight;\n    float PrevTabsContentsHeight;\n    float WidthAllTabs;\n    float WidthAllTabsIdeal;\n    float ScrollingAnim;\n    float ScrollingTarget;\n    float ScrollingTargetDistToVisibility;\n    float ScrollingSpeed;\n    float ScrollingRectMinX;\n    float ScrollingRectMaxX;\n    float SeparatorMinX;\n    float SeparatorMaxX;\n    ImGuiID ReorderRequestTabId;\n    ImS16 ReorderRequestOffset;\n    ImS8 BeginCount;\n    bool WantLayout;\n    bool VisibleTabWasSubmitted;\n    bool TabsAddedNew;\n    ImS16 TabsActiveCount;\n    ImS16 LastTabItemIdx;\n    float ItemSpacingY;\n    ImVec2 FramePadding;\n    ImVec2 BackupCursorPos;\n    ImGuiTextBuffer TabsNames;\n};\nstruct ImGuiTableColumn\n{\n    ImGuiTableColumnFlags Flags;\n    float WidthGiven;\n    float MinX;\n    float MaxX;\n    float WidthRequest;\n    float WidthAuto;\n    float WidthMax;\n    float StretchWeight;\n    float InitStretchWeightOrWidth;\n    ImRect ClipRect;\n    ImGuiID UserID;\n    float WorkMinX;\n    float WorkMaxX;\n    float ItemWidth;\n    float ContentMaxXFrozen;\n    float ContentMaxXUnfrozen;\n    float ContentMaxXHeadersUsed;\n    float ContentMaxXHeadersIdeal;\n    ImS16 NameOffset;\n    ImGuiTableColumnIdx DisplayOrder;\n    ImGuiTableColumnIdx IndexWithinEnabledSet;\n    ImGuiTableColumnIdx PrevEnabledColumn;\n    ImGuiTableColumnIdx NextEnabledColumn;\n    ImGuiTableColumnIdx SortOrder;\n    ImGuiTableDrawChannelIdx DrawChannelCurrent;\n    ImGuiTableDrawChannelIdx DrawChannelFrozen;\n    ImGuiTableDrawChannelIdx DrawChannelUnfrozen;\n    bool IsEnabled;\n    bool IsUserEnabled;\n    bool IsUserEnabledNextFrame;\n    bool IsVisibleX;\n    bool IsVisibleY;\n    bool IsRequestOutput;\n    bool IsSkipItems;\n    bool IsPreserveWidthAuto;\n    ImS8 NavLayerCurrent;\n    ImU8 AutoFitQueue;\n    ImU8 CannotSkipItemsQueue;\n    ImU8 SortDirection : 2;\n    ImU8 SortDirectionsAvailCount : 2;\n    ImU8 SortDirectionsAvailMask : 4;\n    ImU8 SortDirectionsAvailList;\n};\ntypedef struct ImGuiTableCellData ImGuiTableCellData;\nstruct ImGuiTableCellData\n{\n    ImU32 BgColor;\n    ImGuiTableColumnIdx Column;\n};\nstruct ImGuiTableHeaderData\n{\n    ImGuiTableColumnIdx Index;\n    ImU32 TextColor;\n    ImU32 BgColor0;\n    ImU32 BgColor1;\n};\nstruct ImGuiTableInstanceData\n{\n    ImGuiID TableInstanceID;\n    float LastOuterHeight;\n    float LastTopHeadersRowHeight;\n    float LastFrozenHeight;\n    int HoveredRowLast;\n    int HoveredRowNext;\n};\ntypedef struct ImSpan_ImGuiTableColumn {ImGuiTableColumn* Data;ImGuiTableColumn* DataEnd;} ImSpan_ImGuiTableColumn;\n\ntypedef struct ImSpan_ImGuiTableColumnIdx {ImGuiTableColumnIdx* Data;ImGuiTableColumnIdx* DataEnd;} ImSpan_ImGuiTableColumnIdx;\n\ntypedef struct ImSpan_ImGuiTableCellData {ImGuiTableCellData* Data;ImGuiTableCellData* DataEnd;} ImSpan_ImGuiTableCellData;\n\ntypedef struct ImVector_ImGuiTableInstanceData {int Size;int Capacity;ImGuiTableInstanceData* Data;} ImVector_ImGuiTableInstanceData;\n\ntypedef struct ImVector_ImGuiTableColumnSortSpecs {int Size;int Capacity;ImGuiTableColumnSortSpecs* Data;} ImVector_ImGuiTableColumnSortSpecs;\n\nstruct ImGuiTable\n{\n    ImGuiID ID;\n    ImGuiTableFlags Flags;\n    void* RawData;\n    ImGuiTableTempData* TempData;\n    ImSpan_ImGuiTableColumn Columns;\n    ImSpan_ImGuiTableColumnIdx DisplayOrderToIndex;\n    ImSpan_ImGuiTableCellData RowCellData;\n    ImBitArrayPtr EnabledMaskByDisplayOrder;\n    ImBitArrayPtr EnabledMaskByIndex;\n    ImBitArrayPtr VisibleMaskByIndex;\n    ImGuiTableFlags SettingsLoadedFlags;\n    int SettingsOffset;\n    int LastFrameActive;\n    int ColumnsCount;\n    int CurrentRow;\n    int CurrentColumn;\n    ImS16 InstanceCurrent;\n    ImS16 InstanceInteracted;\n    float RowPosY1;\n    float RowPosY2;\n    float RowMinHeight;\n    float RowCellPaddingY;\n    float RowTextBaseline;\n    float RowIndentOffsetX;\n    ImGuiTableRowFlags RowFlags : 16;\n    ImGuiTableRowFlags LastRowFlags : 16;\n    int RowBgColorCounter;\n    ImU32 RowBgColor[2];\n    ImU32 BorderColorStrong;\n    ImU32 BorderColorLight;\n    float BorderX1;\n    float BorderX2;\n    float HostIndentX;\n    float MinColumnWidth;\n    float OuterPaddingX;\n    float CellPaddingX;\n    float CellSpacingX1;\n    float CellSpacingX2;\n    float InnerWidth;\n    float ColumnsGivenWidth;\n    float ColumnsAutoFitWidth;\n    float ColumnsStretchSumWeights;\n    float ResizedColumnNextWidth;\n    float ResizeLockMinContentsX2;\n    float RefScale;\n    float AngledHeadersHeight;\n    float AngledHeadersSlope;\n    ImRect OuterRect;\n    ImRect InnerRect;\n    ImRect WorkRect;\n    ImRect InnerClipRect;\n    ImRect BgClipRect;\n    ImRect Bg0ClipRectForDrawCmd;\n    ImRect Bg2ClipRectForDrawCmd;\n    ImRect HostClipRect;\n    ImRect HostBackupInnerClipRect;\n    ImGuiWindow* OuterWindow;\n    ImGuiWindow* InnerWindow;\n    ImGuiTextBuffer ColumnsNames;\n    ImDrawListSplitter* DrawSplitter;\n    ImGuiTableInstanceData InstanceDataFirst;\n    ImVector_ImGuiTableInstanceData InstanceDataExtra;\n    ImGuiTableColumnSortSpecs SortSpecsSingle;\n    ImVector_ImGuiTableColumnSortSpecs SortSpecsMulti;\n    ImGuiTableSortSpecs SortSpecs;\n    ImGuiTableColumnIdx SortSpecsCount;\n    ImGuiTableColumnIdx ColumnsEnabledCount;\n    ImGuiTableColumnIdx ColumnsEnabledFixedCount;\n    ImGuiTableColumnIdx DeclColumnsCount;\n    ImGuiTableColumnIdx AngledHeadersCount;\n    ImGuiTableColumnIdx HoveredColumnBody;\n    ImGuiTableColumnIdx HoveredColumnBorder;\n    ImGuiTableColumnIdx HighlightColumnHeader;\n    ImGuiTableColumnIdx AutoFitSingleColumn;\n    ImGuiTableColumnIdx ResizedColumn;\n    ImGuiTableColumnIdx LastResizedColumn;\n    ImGuiTableColumnIdx HeldHeaderColumn;\n    ImGuiTableColumnIdx ReorderColumn;\n    ImGuiTableColumnIdx ReorderColumnDir;\n    ImGuiTableColumnIdx LeftMostEnabledColumn;\n    ImGuiTableColumnIdx RightMostEnabledColumn;\n    ImGuiTableColumnIdx LeftMostStretchedColumn;\n    ImGuiTableColumnIdx RightMostStretchedColumn;\n    ImGuiTableColumnIdx ContextPopupColumn;\n    ImGuiTableColumnIdx FreezeRowsRequest;\n    ImGuiTableColumnIdx FreezeRowsCount;\n    ImGuiTableColumnIdx FreezeColumnsRequest;\n    ImGuiTableColumnIdx FreezeColumnsCount;\n    ImGuiTableColumnIdx RowCellDataCurrent;\n    ImGuiTableDrawChannelIdx DummyDrawChannel;\n    ImGuiTableDrawChannelIdx Bg2DrawChannelCurrent;\n    ImGuiTableDrawChannelIdx Bg2DrawChannelUnfrozen;\n    ImS8 NavLayer;\n    bool IsLayoutLocked;\n    bool IsInsideRow;\n    bool IsInitializing;\n    bool IsSortSpecsDirty;\n    bool IsUsingHeaders;\n    bool IsContextPopupOpen;\n    bool DisableDefaultContextMenu;\n    bool IsSettingsRequestLoad;\n    bool IsSettingsDirty;\n    bool IsDefaultDisplayOrder;\n    bool IsResetAllRequest;\n    bool IsResetDisplayOrderRequest;\n    bool IsUnfrozenRows;\n    bool IsDefaultSizingPolicy;\n    bool IsActiveIdAliveBeforeTable;\n    bool IsActiveIdInTable;\n    bool HasScrollbarYCurr;\n    bool HasScrollbarYPrev;\n    bool MemoryCompacted;\n    bool HostSkipItems;\n};\ntypedef struct ImVector_ImGuiTableHeaderData {int Size;int Capacity;ImGuiTableHeaderData* Data;} ImVector_ImGuiTableHeaderData;\n\nstruct ImGuiTableTempData\n{\n    int TableIndex;\n    float LastTimeActive;\n    float AngledHeadersExtraWidth;\n    ImVector_ImGuiTableHeaderData AngledHeadersRequests;\n    ImVec2 UserOuterSize;\n    ImDrawListSplitter DrawSplitter;\n    ImRect HostBackupWorkRect;\n    ImRect HostBackupParentWorkRect;\n    ImVec2 HostBackupPrevLineSize;\n    ImVec2 HostBackupCurrLineSize;\n    ImVec2 HostBackupCursorMaxPos;\n    ImVec1 HostBackupColumnsOffset;\n    float HostBackupItemWidth;\n    int HostBackupItemWidthStackSize;\n};\ntypedef struct ImGuiTableColumnSettings ImGuiTableColumnSettings;\nstruct ImGuiTableColumnSettings\n{\n    float WidthOrWeight;\n    ImGuiID UserID;\n    ImGuiTableColumnIdx Index;\n    ImGuiTableColumnIdx DisplayOrder;\n    ImGuiTableColumnIdx SortOrder;\n    ImU8 SortDirection : 2;\n    ImS8 IsEnabled : 2;\n    ImU8 IsStretch : 1;\n};\nstruct ImGuiTableSettings\n{\n    ImGuiID ID;\n    ImGuiTableFlags SaveFlags;\n    float RefScale;\n    ImGuiTableColumnIdx ColumnsCount;\n    ImGuiTableColumnIdx ColumnsCountMax;\n    bool WantApply;\n};\nstruct ImFontLoader\n{\n    const char* Name;\n    bool (*LoaderInit)(ImFontAtlas* atlas);\n    void (*LoaderShutdown)(ImFontAtlas* atlas);\n    bool (*FontSrcInit)(ImFontAtlas* atlas, ImFontConfig* src);\n    void (*FontSrcDestroy)(ImFontAtlas* atlas, ImFontConfig* src);\n    bool (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint);\n    bool (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);\n    void (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);\n    bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph);\n    size_t FontBakedSrcLoaderDataSize;\n};\nstruct ImFontAtlasRectEntry\n{\n    int TargetIndex : 20;\n    int Generation : 10;\n    unsigned int IsUsed : 1;\n};\nstruct ImFontAtlasPostProcessData\n{\n    ImFontAtlas* FontAtlas;\n    ImFont* Font;\n    ImFontConfig* FontSrc;\n    ImFontBaked* FontBaked;\n    ImFontGlyph* Glyph;\n    void* Pixels;\n    ImTextureFormat Format;\n    int Pitch;\n    int Width;\n    int Height;\n};\nstruct stbrp_node;\ntypedef stbrp_node stbrp_node_im;\ntypedef struct stbrp_context_opaque stbrp_context_opaque;\nstruct stbrp_context_opaque\n{ char data[80];\n};\ntypedef struct ImVector_stbrp_node_im {int Size;int Capacity;stbrp_node_im* Data;} ImVector_stbrp_node_im;\n\ntypedef struct ImVector_ImFontAtlasRectEntry {int Size;int Capacity;ImFontAtlasRectEntry* Data;} ImVector_ImFontAtlasRectEntry;\n\ntypedef struct ImVector_ImFontBakedPtr {int Size;int Capacity;ImFontBaked** Data;} ImVector_ImFontBakedPtr;\n\ntypedef struct ImStableVector_ImFontBaked__32 {int Size;int Capacity;ImVector_ImFontBakedPtr Blocks;} ImStableVector_ImFontBaked__32;\n\nstruct ImFontAtlasBuilder\n{\n    stbrp_context_opaque PackContext;\n    ImVector_stbrp_node_im PackNodes;\n    ImVector_ImTextureRect Rects;\n    ImVector_ImFontAtlasRectEntry RectsIndex;\n    ImVector_unsigned_char TempBuffer;\n    int RectsIndexFreeListStart;\n    int RectsPackedCount;\n    int RectsPackedSurface;\n    int RectsDiscardedCount;\n    int RectsDiscardedSurface;\n    int FrameCount;\n    ImVec2i MaxRectSize;\n    ImVec2i MaxRectBounds;\n    bool LockDisableResize;\n    bool PreloadedAllGlyphsRanges;\n    ImStableVector_ImFontBaked__32 BakedPool;\n    ImGuiStorage BakedMap;\n    int BakedDiscardedCount;\n    ImFontAtlasRectId PackIdMouseCursors;\n    ImFontAtlasRectId PackIdLinesTexData;\n};\n#ifdef IMGUI_ENABLE_FREETYPE\nstruct ImFontAtlas;\nstruct ImFontLoader;\ntypedef unsigned int ImGuiFreeTypeLoaderFlags;\ntypedef enum {\n    ImGuiFreeTypeLoaderFlags_NoHinting = 1 << 0,\n    ImGuiFreeTypeLoaderFlags_NoAutoHint = 1 << 1,\n    ImGuiFreeTypeLoaderFlags_ForceAutoHint = 1 << 2,\n    ImGuiFreeTypeLoaderFlags_LightHinting = 1 << 3,\n    ImGuiFreeTypeLoaderFlags_MonoHinting = 1 << 4,\n    ImGuiFreeTypeLoaderFlags_Bold = 1 << 5,\n    ImGuiFreeTypeLoaderFlags_Oblique = 1 << 6,\n    ImGuiFreeTypeLoaderFlags_Monochrome = 1 << 7,\n    ImGuiFreeTypeLoaderFlags_LoadColor = 1 << 8,\n    ImGuiFreeTypeLoaderFlags_Bitmap = 1 << 9,\n}ImGuiFreeTypeLoaderFlags_;\n#endif\n#define IMGUI_HAS_DOCK       1\n\n#define ImDrawCallback_ResetRenderState       (ImDrawCallback)(-8)\n\n#else\nstruct GLFWwindow;\nstruct SDL_Window;\ntypedef union SDL_Event SDL_Event;\n#endif // CIMGUI_DEFINE_ENUMS_AND_STRUCTS\n\n#ifndef CIMGUI_DEFINE_ENUMS_AND_STRUCTS\ntypedef struct ImGuiTextFilter::ImGuiTextRange ImGuiTextRange;\ntypedef ImStb::STB_TexteditState STB_TexteditState;\ntypedef ImChunkStream<ImGuiTableSettings> ImChunkStream_ImGuiTableSettings;\ntypedef ImChunkStream<ImGuiWindowSettings> ImChunkStream_ImGuiWindowSettings;\ntypedef ImPool<ImGuiMultiSelectState> ImPool_ImGuiMultiSelectState;\ntypedef ImPool<ImGuiTabBar> ImPool_ImGuiTabBar;\ntypedef ImPool<ImGuiTable> ImPool_ImGuiTable;\ntypedef ImSpan<ImGuiTableCellData> ImSpan_ImGuiTableCellData;\ntypedef ImSpan<ImGuiTableColumn> ImSpan_ImGuiTableColumn;\ntypedef ImSpan<ImGuiTableColumnIdx> ImSpan_ImGuiTableColumnIdx;\ntypedef ImStableVector<ImFontBaked,32> ImStableVector_ImFontBaked__32;\ntypedef ImVector<ImDrawChannel> ImVector_ImDrawChannel;\ntypedef ImVector<ImDrawCmd> ImVector_ImDrawCmd;\ntypedef ImVector<ImDrawIdx> ImVector_ImDrawIdx;\ntypedef ImVector<ImDrawList*> ImVector_ImDrawListPtr;\ntypedef ImVector<ImDrawListSharedData*> ImVector_ImDrawListSharedDataPtr;\ntypedef ImVector<ImDrawVert> ImVector_ImDrawVert;\ntypedef ImVector<ImFont*> ImVector_ImFontPtr;\ntypedef ImVector<ImFontAtlas*> ImVector_ImFontAtlasPtr;\ntypedef ImVector<ImFontAtlasRectEntry> ImVector_ImFontAtlasRectEntry;\ntypedef ImVector<ImFontConfig> ImVector_ImFontConfig;\ntypedef ImVector<ImFontConfig*> ImVector_ImFontConfigPtr;\ntypedef ImVector<ImFontGlyph> ImVector_ImFontGlyph;\ntypedef ImVector<ImFontStackData> ImVector_ImFontStackData;\ntypedef ImVector<ImGuiColorMod> ImVector_ImGuiColorMod;\ntypedef ImVector<ImGuiContextHook> ImVector_ImGuiContextHook;\ntypedef ImVector<ImGuiDockNodeSettings> ImVector_ImGuiDockNodeSettings;\ntypedef ImVector<ImGuiDockRequest> ImVector_ImGuiDockRequest;\ntypedef ImVector<ImGuiFocusScopeData> ImVector_ImGuiFocusScopeData;\ntypedef ImVector<ImGuiGroupData> ImVector_ImGuiGroupData;\ntypedef ImVector<ImGuiID> ImVector_ImGuiID;\ntypedef ImVector<ImGuiInputEvent> ImVector_ImGuiInputEvent;\ntypedef ImVector<ImGuiItemFlags> ImVector_ImGuiItemFlags;\ntypedef ImVector<ImGuiKeyRoutingData> ImVector_ImGuiKeyRoutingData;\ntypedef ImVector<ImGuiListClipperData> ImVector_ImGuiListClipperData;\ntypedef ImVector<ImGuiListClipperRange> ImVector_ImGuiListClipperRange;\ntypedef ImVector<ImGuiMultiSelectTempData> ImVector_ImGuiMultiSelectTempData;\ntypedef ImVector<ImGuiOldColumnData> ImVector_ImGuiOldColumnData;\ntypedef ImVector<ImGuiOldColumns> ImVector_ImGuiOldColumns;\ntypedef ImVector<ImGuiPlatformMonitor> ImVector_ImGuiPlatformMonitor;\ntypedef ImVector<ImGuiPopupData> ImVector_ImGuiPopupData;\ntypedef ImVector<ImGuiPtrOrIndex> ImVector_ImGuiPtrOrIndex;\ntypedef ImVector<ImGuiSelectionRequest> ImVector_ImGuiSelectionRequest;\ntypedef ImVector<ImGuiSettingsHandler> ImVector_ImGuiSettingsHandler;\ntypedef ImVector<ImGuiShrinkWidthItem> ImVector_ImGuiShrinkWidthItem;\ntypedef ImVector<ImGuiStackLevelInfo> ImVector_ImGuiStackLevelInfo;\ntypedef ImVector<ImGuiStoragePair> ImVector_ImGuiStoragePair;\ntypedef ImVector<ImGuiStyleMod> ImVector_ImGuiStyleMod;\ntypedef ImVector<ImGuiTabItem> ImVector_ImGuiTabItem;\ntypedef ImVector<ImGuiTableColumnSortSpecs> ImVector_ImGuiTableColumnSortSpecs;\ntypedef ImVector<ImGuiTableHeaderData> ImVector_ImGuiTableHeaderData;\ntypedef ImVector<ImGuiTableInstanceData> ImVector_ImGuiTableInstanceData;\ntypedef ImVector<ImGuiTableTempData> ImVector_ImGuiTableTempData;\ntypedef ImVector<ImGuiTextRange> ImVector_ImGuiTextRange;\ntypedef ImVector<ImGuiTreeNodeStackData> ImVector_ImGuiTreeNodeStackData;\ntypedef ImVector<ImGuiViewport*> ImVector_ImGuiViewportPtr;\ntypedef ImVector<ImGuiViewportP*> ImVector_ImGuiViewportPPtr;\ntypedef ImVector<ImGuiWindow*> ImVector_ImGuiWindowPtr;\ntypedef ImVector<ImGuiWindowStackData> ImVector_ImGuiWindowStackData;\ntypedef ImVector<ImTextureData*> ImVector_ImTextureDataPtr;\ntypedef ImVector<ImTextureRect> ImVector_ImTextureRect;\ntypedef ImVector<ImTextureRef> ImVector_ImTextureRef;\ntypedef ImVector<ImU16> ImVector_ImU16;\ntypedef ImVector<ImU32> ImVector_ImU32;\ntypedef ImVector<ImU8> ImVector_ImU8;\ntypedef ImVector<ImVec2> ImVector_ImVec2;\ntypedef ImVector<ImVec4> ImVector_ImVec4;\ntypedef ImVector<ImWchar> ImVector_ImWchar;\ntypedef ImVector<char> ImVector_char;\ntypedef ImVector<const char*> ImVector_const_charPtr;\ntypedef ImVector<float> ImVector_float;\ntypedef ImVector<int> ImVector_int;\ntypedef ImVector<stbrp_node_im> ImVector_stbrp_node_im;\ntypedef ImVector<unsigned char> ImVector_unsigned_char;\n#endif //CIMGUI_DEFINE_ENUMS_AND_STRUCTS\nCIMGUI_API ImVec2* ImVec2_ImVec2_Nil(void);\nCIMGUI_API void ImVec2_destroy(ImVec2* self);\nCIMGUI_API ImVec2* ImVec2_ImVec2_Float(float _x,float _y);\nCIMGUI_API ImVec4* ImVec4_ImVec4_Nil(void);\nCIMGUI_API void ImVec4_destroy(ImVec4* self);\nCIMGUI_API ImVec4* ImVec4_ImVec4_Float(float _x,float _y,float _z,float _w);\nCIMGUI_API ImTextureRef* ImTextureRef_ImTextureRef_Nil(void);\nCIMGUI_API void ImTextureRef_destroy(ImTextureRef* self);\nCIMGUI_API ImTextureRef* ImTextureRef_ImTextureRef_TextureID(ImTextureID tex_id);\nCIMGUI_API ImTextureID ImTextureRef_GetTexID(ImTextureRef* self);\nCIMGUI_API ImGuiContext* igCreateContext(ImFontAtlas* shared_font_atlas);\nCIMGUI_API void igDestroyContext(ImGuiContext* ctx);\nCIMGUI_API ImGuiContext* igGetCurrentContext(void);\nCIMGUI_API void igSetCurrentContext(ImGuiContext* ctx);\nCIMGUI_API ImGuiIO* igGetIO_Nil(void);\nCIMGUI_API ImGuiPlatformIO* igGetPlatformIO_Nil(void);\nCIMGUI_API ImGuiStyle* igGetStyle(void);\nCIMGUI_API void igNewFrame(void);\nCIMGUI_API void igEndFrame(void);\nCIMGUI_API void igRender(void);\nCIMGUI_API ImDrawData* igGetDrawData(void);\nCIMGUI_API void igShowDemoWindow(bool* p_open);\nCIMGUI_API void igShowMetricsWindow(bool* p_open);\nCIMGUI_API void igShowDebugLogWindow(bool* p_open);\nCIMGUI_API void igShowIDStackToolWindow(bool* p_open);\nCIMGUI_API void igShowAboutWindow(bool* p_open);\nCIMGUI_API void igShowStyleEditor(ImGuiStyle* ref);\nCIMGUI_API bool igShowStyleSelector(const char* label);\nCIMGUI_API void igShowFontSelector(const char* label);\nCIMGUI_API void igShowUserGuide(void);\nCIMGUI_API const char* igGetVersion(void);\nCIMGUI_API void igStyleColorsDark(ImGuiStyle* dst);\nCIMGUI_API void igStyleColorsLight(ImGuiStyle* dst);\nCIMGUI_API void igStyleColorsClassic(ImGuiStyle* dst);\nCIMGUI_API bool igBegin(const char* name,bool* p_open,ImGuiWindowFlags flags);\nCIMGUI_API void igEnd(void);\nCIMGUI_API bool igBeginChild_Str(const char* str_id,const ImVec2 size,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags);\nCIMGUI_API bool igBeginChild_ID(ImGuiID id,const ImVec2 size,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags);\nCIMGUI_API void igEndChild(void);\nCIMGUI_API bool igIsWindowAppearing(void);\nCIMGUI_API bool igIsWindowCollapsed(void);\nCIMGUI_API bool igIsWindowFocused(ImGuiFocusedFlags flags);\nCIMGUI_API bool igIsWindowHovered(ImGuiHoveredFlags flags);\nCIMGUI_API ImDrawList* igGetWindowDrawList(void);\nCIMGUI_API float igGetWindowDpiScale(void);\nCIMGUI_API void igGetWindowPos(ImVec2 *pOut);\nCIMGUI_API void igGetWindowSize(ImVec2 *pOut);\nCIMGUI_API float igGetWindowWidth(void);\nCIMGUI_API float igGetWindowHeight(void);\nCIMGUI_API ImGuiViewport* igGetWindowViewport(void);\nCIMGUI_API void igSetNextWindowPos(const ImVec2 pos,ImGuiCond cond,const ImVec2 pivot);\nCIMGUI_API void igSetNextWindowSize(const ImVec2 size,ImGuiCond cond);\nCIMGUI_API void igSetNextWindowSizeConstraints(const ImVec2 size_min,const ImVec2 size_max,ImGuiSizeCallback custom_callback,void* custom_callback_data);\nCIMGUI_API void igSetNextWindowContentSize(const ImVec2 size);\nCIMGUI_API void igSetNextWindowCollapsed(bool collapsed,ImGuiCond cond);\nCIMGUI_API void igSetNextWindowFocus(void);\nCIMGUI_API void igSetNextWindowScroll(const ImVec2 scroll);\nCIMGUI_API void igSetNextWindowBgAlpha(float alpha);\nCIMGUI_API void igSetNextWindowViewport(ImGuiID viewport_id);\nCIMGUI_API void igSetWindowPos_Vec2(const ImVec2 pos,ImGuiCond cond);\nCIMGUI_API void igSetWindowSize_Vec2(const ImVec2 size,ImGuiCond cond);\nCIMGUI_API void igSetWindowCollapsed_Bool(bool collapsed,ImGuiCond cond);\nCIMGUI_API void igSetWindowFocus_Nil(void);\nCIMGUI_API void igSetWindowPos_Str(const char* name,const ImVec2 pos,ImGuiCond cond);\nCIMGUI_API void igSetWindowSize_Str(const char* name,const ImVec2 size,ImGuiCond cond);\nCIMGUI_API void igSetWindowCollapsed_Str(const char* name,bool collapsed,ImGuiCond cond);\nCIMGUI_API void igSetWindowFocus_Str(const char* name);\nCIMGUI_API float igGetScrollX(void);\nCIMGUI_API float igGetScrollY(void);\nCIMGUI_API void igSetScrollX_Float(float scroll_x);\nCIMGUI_API void igSetScrollY_Float(float scroll_y);\nCIMGUI_API float igGetScrollMaxX(void);\nCIMGUI_API float igGetScrollMaxY(void);\nCIMGUI_API void igSetScrollHereX(float center_x_ratio);\nCIMGUI_API void igSetScrollHereY(float center_y_ratio);\nCIMGUI_API void igSetScrollFromPosX_Float(float local_x,float center_x_ratio);\nCIMGUI_API void igSetScrollFromPosY_Float(float local_y,float center_y_ratio);\nCIMGUI_API void igPushFont(ImFont* font,float font_size_base_unscaled);\nCIMGUI_API void igPopFont(void);\nCIMGUI_API ImFont* igGetFont(void);\nCIMGUI_API float igGetFontSize(void);\nCIMGUI_API ImFontBaked* igGetFontBaked(void);\nCIMGUI_API void igPushStyleColor_U32(ImGuiCol idx,ImU32 col);\nCIMGUI_API void igPushStyleColor_Vec4(ImGuiCol idx,const ImVec4 col);\nCIMGUI_API void igPopStyleColor(int count);\nCIMGUI_API void igPushStyleVar_Float(ImGuiStyleVar idx,float val);\nCIMGUI_API void igPushStyleVar_Vec2(ImGuiStyleVar idx,const ImVec2 val);\nCIMGUI_API void igPushStyleVarX(ImGuiStyleVar idx,float val_x);\nCIMGUI_API void igPushStyleVarY(ImGuiStyleVar idx,float val_y);\nCIMGUI_API void igPopStyleVar(int count);\nCIMGUI_API void igPushItemFlag(ImGuiItemFlags option,bool enabled);\nCIMGUI_API void igPopItemFlag(void);\nCIMGUI_API void igPushItemWidth(float item_width);\nCIMGUI_API void igPopItemWidth(void);\nCIMGUI_API void igSetNextItemWidth(float item_width);\nCIMGUI_API float igCalcItemWidth(void);\nCIMGUI_API void igPushTextWrapPos(float wrap_local_pos_x);\nCIMGUI_API void igPopTextWrapPos(void);\nCIMGUI_API void igGetFontTexUvWhitePixel(ImVec2 *pOut);\nCIMGUI_API ImU32 igGetColorU32_Col(ImGuiCol idx,float alpha_mul);\nCIMGUI_API ImU32 igGetColorU32_Vec4(const ImVec4 col);\nCIMGUI_API ImU32 igGetColorU32_U32(ImU32 col,float alpha_mul);\nCIMGUI_API const ImVec4* igGetStyleColorVec4(ImGuiCol idx);\nCIMGUI_API void igGetCursorScreenPos(ImVec2 *pOut);\nCIMGUI_API void igSetCursorScreenPos(const ImVec2 pos);\nCIMGUI_API void igGetContentRegionAvail(ImVec2 *pOut);\nCIMGUI_API void igGetCursorPos(ImVec2 *pOut);\nCIMGUI_API float igGetCursorPosX(void);\nCIMGUI_API float igGetCursorPosY(void);\nCIMGUI_API void igSetCursorPos(const ImVec2 local_pos);\nCIMGUI_API void igSetCursorPosX(float local_x);\nCIMGUI_API void igSetCursorPosY(float local_y);\nCIMGUI_API void igGetCursorStartPos(ImVec2 *pOut);\nCIMGUI_API void igSeparator(void);\nCIMGUI_API void igSameLine(float offset_from_start_x,float spacing);\nCIMGUI_API void igNewLine(void);\nCIMGUI_API void igSpacing(void);\nCIMGUI_API void igDummy(const ImVec2 size);\nCIMGUI_API void igIndent(float indent_w);\nCIMGUI_API void igUnindent(float indent_w);\nCIMGUI_API void igBeginGroup(void);\nCIMGUI_API void igEndGroup(void);\nCIMGUI_API void igAlignTextToFramePadding(void);\nCIMGUI_API float igGetTextLineHeight(void);\nCIMGUI_API float igGetTextLineHeightWithSpacing(void);\nCIMGUI_API float igGetFrameHeight(void);\nCIMGUI_API float igGetFrameHeightWithSpacing(void);\nCIMGUI_API void igPushID_Str(const char* str_id);\nCIMGUI_API void igPushID_StrStr(const char* str_id_begin,const char* str_id_end);\nCIMGUI_API void igPushID_Ptr(const void* ptr_id);\nCIMGUI_API void igPushID_Int(int int_id);\nCIMGUI_API void igPopID(void);\nCIMGUI_API ImGuiID igGetID_Str(const char* str_id);\nCIMGUI_API ImGuiID igGetID_StrStr(const char* str_id_begin,const char* str_id_end);\nCIMGUI_API ImGuiID igGetID_Ptr(const void* ptr_id);\nCIMGUI_API ImGuiID igGetID_Int(int int_id);\nCIMGUI_API void igTextUnformatted(const char* text,const char* text_end);\nCIMGUI_API void igText(const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igText0(const char* fmt);\n#endif\nCIMGUI_API void igTextV(const char* fmt,va_list args);\nCIMGUI_API void igTextColored(const ImVec4 col,const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igTextColored0(const ImVec4 col,const char* fmt);\n#endif\nCIMGUI_API void igTextColoredV(const ImVec4 col,const char* fmt,va_list args);\nCIMGUI_API void igTextDisabled(const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igTextDisabled0(const char* fmt);\n#endif\nCIMGUI_API void igTextDisabledV(const char* fmt,va_list args);\nCIMGUI_API void igTextWrapped(const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igTextWrapped0(const char* fmt);\n#endif\nCIMGUI_API void igTextWrappedV(const char* fmt,va_list args);\nCIMGUI_API void igLabelText(const char* label,const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igLabelText0(const char* label,const char* fmt);\n#endif\nCIMGUI_API void igLabelTextV(const char* label,const char* fmt,va_list args);\nCIMGUI_API void igBulletText(const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igBulletText0(const char* fmt);\n#endif\nCIMGUI_API void igBulletTextV(const char* fmt,va_list args);\nCIMGUI_API void igSeparatorText(const char* label);\nCIMGUI_API bool igButton(const char* label,const ImVec2 size);\nCIMGUI_API bool igSmallButton(const char* label);\nCIMGUI_API bool igInvisibleButton(const char* str_id,const ImVec2 size,ImGuiButtonFlags flags);\nCIMGUI_API bool igArrowButton(const char* str_id,ImGuiDir dir);\nCIMGUI_API bool igCheckbox(const char* label,bool* v);\nCIMGUI_API bool igCheckboxFlags_IntPtr(const char* label,int* flags,int flags_value);\nCIMGUI_API bool igCheckboxFlags_UintPtr(const char* label,unsigned int* flags,unsigned int flags_value);\nCIMGUI_API bool igRadioButton_Bool(const char* label,bool active);\nCIMGUI_API bool igRadioButton_IntPtr(const char* label,int* v,int v_button);\nCIMGUI_API void igProgressBar(float fraction,const ImVec2 size_arg,const char* overlay);\nCIMGUI_API void igBullet(void);\nCIMGUI_API bool igTextLink(const char* label);\nCIMGUI_API bool igTextLinkOpenURL(const char* label,const char* url);\nCIMGUI_API void igImage(ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1);\nCIMGUI_API void igImageWithBg(ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col);\nCIMGUI_API bool igImageButton(const char* str_id,ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col);\nCIMGUI_API bool igBeginCombo(const char* label,const char* preview_value,ImGuiComboFlags flags);\nCIMGUI_API void igEndCombo(void);\nCIMGUI_API bool igCombo_Str_arr(const char* label,int* current_item,const char* const items[],int items_count,int popup_max_height_in_items);\nCIMGUI_API bool igCombo_Str(const char* label,int* current_item,const char* items_separated_by_zeros,int popup_max_height_in_items);\nCIMGUI_API bool igCombo_FnStrPtr(const char* label,int* current_item,const char*(*getter)(void* user_data,int idx),void* user_data,int items_count,int popup_max_height_in_items);\nCIMGUI_API bool igDragFloat(const char* label,float* v,float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igDragFloat2(const char* label,float v[2],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igDragFloat3(const char* label,float v[3],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igDragFloat4(const char* label,float v[4],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igDragFloatRange2(const char* label,float* v_current_min,float* v_current_max,float v_speed,float v_min,float v_max,const char* format,const char* format_max,ImGuiSliderFlags flags);\nCIMGUI_API bool igDragInt(const char* label,int* v,float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igDragInt2(const char* label,int v[2],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igDragInt3(const char* label,int v[3],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igDragInt4(const char* label,int v[4],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igDragIntRange2(const char* label,int* v_current_min,int* v_current_max,float v_speed,int v_min,int v_max,const char* format,const char* format_max,ImGuiSliderFlags flags);\nCIMGUI_API bool igDragScalar(const char* label,ImGuiDataType data_type,void* p_data,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igDragScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderFloat(const char* label,float* v,float v_min,float v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderFloat2(const char* label,float v[2],float v_min,float v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderFloat3(const char* label,float v[3],float v_min,float v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderFloat4(const char* label,float v[4],float v_min,float v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderAngle(const char* label,float* v_rad,float v_degrees_min,float v_degrees_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderInt(const char* label,int* v,int v_min,int v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderInt2(const char* label,int v[2],int v_min,int v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderInt3(const char* label,int v[3],int v_min,int v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderInt4(const char* label,int v[4],int v_min,int v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderScalar(const char* label,ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igVSliderFloat(const char* label,const ImVec2 size,float* v,float v_min,float v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igVSliderInt(const char* label,const ImVec2 size,int* v,int v_min,int v_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igVSliderScalar(const char* label,const ImVec2 size,ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igInputText(const char* label,char* buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data);\nCIMGUI_API bool igInputTextMultiline(const char* label,char* buf,size_t buf_size,const ImVec2 size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data);\nCIMGUI_API bool igInputTextWithHint(const char* label,const char* hint,char* buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data);\nCIMGUI_API bool igInputFloat(const char* label,float* v,float step,float step_fast,const char* format,ImGuiInputTextFlags flags);\nCIMGUI_API bool igInputFloat2(const char* label,float v[2],const char* format,ImGuiInputTextFlags flags);\nCIMGUI_API bool igInputFloat3(const char* label,float v[3],const char* format,ImGuiInputTextFlags flags);\nCIMGUI_API bool igInputFloat4(const char* label,float v[4],const char* format,ImGuiInputTextFlags flags);\nCIMGUI_API bool igInputInt(const char* label,int* v,int step,int step_fast,ImGuiInputTextFlags flags);\nCIMGUI_API bool igInputInt2(const char* label,int v[2],ImGuiInputTextFlags flags);\nCIMGUI_API bool igInputInt3(const char* label,int v[3],ImGuiInputTextFlags flags);\nCIMGUI_API bool igInputInt4(const char* label,int v[4],ImGuiInputTextFlags flags);\nCIMGUI_API bool igInputDouble(const char* label,double* v,double step,double step_fast,const char* format,ImGuiInputTextFlags flags);\nCIMGUI_API bool igInputScalar(const char* label,ImGuiDataType data_type,void* p_data,const void* p_step,const void* p_step_fast,const char* format,ImGuiInputTextFlags flags);\nCIMGUI_API bool igInputScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,const void* p_step,const void* p_step_fast,const char* format,ImGuiInputTextFlags flags);\nCIMGUI_API bool igColorEdit3(const char* label,float col[3],ImGuiColorEditFlags flags);\nCIMGUI_API bool igColorEdit4(const char* label,float col[4],ImGuiColorEditFlags flags);\nCIMGUI_API bool igColorPicker3(const char* label,float col[3],ImGuiColorEditFlags flags);\nCIMGUI_API bool igColorPicker4(const char* label,float col[4],ImGuiColorEditFlags flags,const float* ref_col);\nCIMGUI_API bool igColorButton(const char* desc_id,const ImVec4 col,ImGuiColorEditFlags flags,const ImVec2 size);\nCIMGUI_API void igSetColorEditOptions(ImGuiColorEditFlags flags);\nCIMGUI_API bool igTreeNode_Str(const char* label);\nCIMGUI_API bool igTreeNode_StrStr(const char* str_id,const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API bool igTreeNode_StrStr0(const char* str_id,const char* fmt);\n#endif\nCIMGUI_API bool igTreeNode_Ptr(const void* ptr_id,const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API bool igTreeNode_Ptr0(const void* ptr_id,const char* fmt);\n#endif\nCIMGUI_API bool igTreeNodeV_Str(const char* str_id,const char* fmt,va_list args);\nCIMGUI_API bool igTreeNodeV_Ptr(const void* ptr_id,const char* fmt,va_list args);\nCIMGUI_API bool igTreeNodeEx_Str(const char* label,ImGuiTreeNodeFlags flags);\nCIMGUI_API bool igTreeNodeEx_StrStr(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API bool igTreeNodeEx_StrStr0(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt);\n#endif\nCIMGUI_API bool igTreeNodeEx_Ptr(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API bool igTreeNodeEx_Ptr0(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt);\n#endif\nCIMGUI_API bool igTreeNodeExV_Str(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt,va_list args);\nCIMGUI_API bool igTreeNodeExV_Ptr(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt,va_list args);\nCIMGUI_API void igTreePush_Str(const char* str_id);\nCIMGUI_API void igTreePush_Ptr(const void* ptr_id);\nCIMGUI_API void igTreePop(void);\nCIMGUI_API float igGetTreeNodeToLabelSpacing(void);\nCIMGUI_API bool igCollapsingHeader_TreeNodeFlags(const char* label,ImGuiTreeNodeFlags flags);\nCIMGUI_API bool igCollapsingHeader_BoolPtr(const char* label,bool* p_visible,ImGuiTreeNodeFlags flags);\nCIMGUI_API void igSetNextItemOpen(bool is_open,ImGuiCond cond);\nCIMGUI_API void igSetNextItemStorageID(ImGuiID storage_id);\nCIMGUI_API bool igSelectable_Bool(const char* label,bool selected,ImGuiSelectableFlags flags,const ImVec2 size);\nCIMGUI_API bool igSelectable_BoolPtr(const char* label,bool* p_selected,ImGuiSelectableFlags flags,const ImVec2 size);\nCIMGUI_API ImGuiMultiSelectIO* igBeginMultiSelect(ImGuiMultiSelectFlags flags,int selection_size,int items_count);\nCIMGUI_API ImGuiMultiSelectIO* igEndMultiSelect(void);\nCIMGUI_API void igSetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data);\nCIMGUI_API bool igIsItemToggledSelection(void);\nCIMGUI_API bool igBeginListBox(const char* label,const ImVec2 size);\nCIMGUI_API void igEndListBox(void);\nCIMGUI_API bool igListBox_Str_arr(const char* label,int* current_item,const char* const items[],int items_count,int height_in_items);\nCIMGUI_API bool igListBox_FnStrPtr(const char* label,int* current_item,const char*(*getter)(void* user_data,int idx),void* user_data,int items_count,int height_in_items);\nCIMGUI_API void igPlotLines_FloatPtr(const char* label,const float* values,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride);\nCIMGUI_API void igPlotLines_FnFloatPtr(const char* label,float(*values_getter)(void* data,int idx),void* data,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size);\nCIMGUI_API void igPlotHistogram_FloatPtr(const char* label,const float* values,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride);\nCIMGUI_API void igPlotHistogram_FnFloatPtr(const char* label,float(*values_getter)(void* data,int idx),void* data,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size);\nCIMGUI_API void igValue_Bool(const char* prefix,bool b);\nCIMGUI_API void igValue_Int(const char* prefix,int v);\nCIMGUI_API void igValue_Uint(const char* prefix,unsigned int v);\nCIMGUI_API void igValue_Float(const char* prefix,float v,const char* float_format);\nCIMGUI_API bool igBeginMenuBar(void);\nCIMGUI_API void igEndMenuBar(void);\nCIMGUI_API bool igBeginMainMenuBar(void);\nCIMGUI_API void igEndMainMenuBar(void);\nCIMGUI_API bool igBeginMenu(const char* label,bool enabled);\nCIMGUI_API void igEndMenu(void);\nCIMGUI_API bool igMenuItem_Bool(const char* label,const char* shortcut,bool selected,bool enabled);\nCIMGUI_API bool igMenuItem_BoolPtr(const char* label,const char* shortcut,bool* p_selected,bool enabled);\nCIMGUI_API bool igBeginTooltip(void);\nCIMGUI_API void igEndTooltip(void);\nCIMGUI_API void igSetTooltip(const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igSetTooltip0(const char* fmt);\n#endif\nCIMGUI_API void igSetTooltipV(const char* fmt,va_list args);\nCIMGUI_API bool igBeginItemTooltip(void);\nCIMGUI_API void igSetItemTooltip(const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igSetItemTooltip0(const char* fmt);\n#endif\nCIMGUI_API void igSetItemTooltipV(const char* fmt,va_list args);\nCIMGUI_API bool igBeginPopup(const char* str_id,ImGuiWindowFlags flags);\nCIMGUI_API bool igBeginPopupModal(const char* name,bool* p_open,ImGuiWindowFlags flags);\nCIMGUI_API void igEndPopup(void);\nCIMGUI_API void igOpenPopup_Str(const char* str_id,ImGuiPopupFlags popup_flags);\nCIMGUI_API void igOpenPopup_ID(ImGuiID id,ImGuiPopupFlags popup_flags);\nCIMGUI_API void igOpenPopupOnItemClick(const char* str_id,ImGuiPopupFlags popup_flags);\nCIMGUI_API void igCloseCurrentPopup(void);\nCIMGUI_API bool igBeginPopupContextItem(const char* str_id,ImGuiPopupFlags popup_flags);\nCIMGUI_API bool igBeginPopupContextWindow(const char* str_id,ImGuiPopupFlags popup_flags);\nCIMGUI_API bool igBeginPopupContextVoid(const char* str_id,ImGuiPopupFlags popup_flags);\nCIMGUI_API bool igIsPopupOpen_Str(const char* str_id,ImGuiPopupFlags flags);\nCIMGUI_API bool igBeginTable(const char* str_id,int columns,ImGuiTableFlags flags,const ImVec2 outer_size,float inner_width);\nCIMGUI_API void igEndTable(void);\nCIMGUI_API void igTableNextRow(ImGuiTableRowFlags row_flags,float min_row_height);\nCIMGUI_API bool igTableNextColumn(void);\nCIMGUI_API bool igTableSetColumnIndex(int column_n);\nCIMGUI_API void igTableSetupColumn(const char* label,ImGuiTableColumnFlags flags,float init_width_or_weight,ImGuiID user_id);\nCIMGUI_API void igTableSetupScrollFreeze(int cols,int rows);\nCIMGUI_API void igTableHeader(const char* label);\nCIMGUI_API void igTableHeadersRow(void);\nCIMGUI_API void igTableAngledHeadersRow(void);\nCIMGUI_API ImGuiTableSortSpecs* igTableGetSortSpecs(void);\nCIMGUI_API int igTableGetColumnCount(void);\nCIMGUI_API int igTableGetColumnIndex(void);\nCIMGUI_API int igTableGetRowIndex(void);\nCIMGUI_API const char* igTableGetColumnName_Int(int column_n);\nCIMGUI_API ImGuiTableColumnFlags igTableGetColumnFlags(int column_n);\nCIMGUI_API void igTableSetColumnEnabled(int column_n,bool v);\nCIMGUI_API int igTableGetHoveredColumn(void);\nCIMGUI_API void igTableSetBgColor(ImGuiTableBgTarget target,ImU32 color,int column_n);\nCIMGUI_API void igColumns(int count,const char* id,bool borders);\nCIMGUI_API void igNextColumn(void);\nCIMGUI_API int igGetColumnIndex(void);\nCIMGUI_API float igGetColumnWidth(int column_index);\nCIMGUI_API void igSetColumnWidth(int column_index,float width);\nCIMGUI_API float igGetColumnOffset(int column_index);\nCIMGUI_API void igSetColumnOffset(int column_index,float offset_x);\nCIMGUI_API int igGetColumnsCount(void);\nCIMGUI_API bool igBeginTabBar(const char* str_id,ImGuiTabBarFlags flags);\nCIMGUI_API void igEndTabBar(void);\nCIMGUI_API bool igBeginTabItem(const char* label,bool* p_open,ImGuiTabItemFlags flags);\nCIMGUI_API void igEndTabItem(void);\nCIMGUI_API bool igTabItemButton(const char* label,ImGuiTabItemFlags flags);\nCIMGUI_API void igSetTabItemClosed(const char* tab_or_docked_window_label);\nCIMGUI_API ImGuiID igDockSpace(ImGuiID dockspace_id,const ImVec2 size,ImGuiDockNodeFlags flags,const ImGuiWindowClass* window_class);\nCIMGUI_API ImGuiID igDockSpaceOverViewport(ImGuiID dockspace_id,const ImGuiViewport* viewport,ImGuiDockNodeFlags flags,const ImGuiWindowClass* window_class);\nCIMGUI_API void igSetNextWindowDockID(ImGuiID dock_id,ImGuiCond cond);\nCIMGUI_API void igSetNextWindowClass(const ImGuiWindowClass* window_class);\nCIMGUI_API ImGuiID igGetWindowDockID(void);\nCIMGUI_API bool igIsWindowDocked(void);\nCIMGUI_API void igLogToTTY(int auto_open_depth);\nCIMGUI_API void igLogToFile(int auto_open_depth,const char* filename);\nCIMGUI_API void igLogToClipboard(int auto_open_depth);\nCIMGUI_API void igLogFinish(void);\nCIMGUI_API void igLogButtons(void);\nCIMGUI_API void igLogText(const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igLogText0(const char* fmt);\n#endif\nCIMGUI_API void igLogTextV(const char* fmt,va_list args);\nCIMGUI_API bool igBeginDragDropSource(ImGuiDragDropFlags flags);\nCIMGUI_API bool igSetDragDropPayload(const char* type,const void* data,size_t sz,ImGuiCond cond);\nCIMGUI_API void igEndDragDropSource(void);\nCIMGUI_API bool igBeginDragDropTarget(void);\nCIMGUI_API const ImGuiPayload* igAcceptDragDropPayload(const char* type,ImGuiDragDropFlags flags);\nCIMGUI_API void igEndDragDropTarget(void);\nCIMGUI_API const ImGuiPayload* igGetDragDropPayload(void);\nCIMGUI_API void igBeginDisabled(bool disabled);\nCIMGUI_API void igEndDisabled(void);\nCIMGUI_API void igPushClipRect(const ImVec2 clip_rect_min,const ImVec2 clip_rect_max,bool intersect_with_current_clip_rect);\nCIMGUI_API void igPopClipRect(void);\nCIMGUI_API void igSetItemDefaultFocus(void);\nCIMGUI_API void igSetKeyboardFocusHere(int offset);\nCIMGUI_API void igSetNavCursorVisible(bool visible);\nCIMGUI_API void igSetNextItemAllowOverlap(void);\nCIMGUI_API bool igIsItemHovered(ImGuiHoveredFlags flags);\nCIMGUI_API bool igIsItemActive(void);\nCIMGUI_API bool igIsItemFocused(void);\nCIMGUI_API bool igIsItemClicked(ImGuiMouseButton mouse_button);\nCIMGUI_API bool igIsItemVisible(void);\nCIMGUI_API bool igIsItemEdited(void);\nCIMGUI_API bool igIsItemActivated(void);\nCIMGUI_API bool igIsItemDeactivated(void);\nCIMGUI_API bool igIsItemDeactivatedAfterEdit(void);\nCIMGUI_API bool igIsItemToggledOpen(void);\nCIMGUI_API bool igIsAnyItemHovered(void);\nCIMGUI_API bool igIsAnyItemActive(void);\nCIMGUI_API bool igIsAnyItemFocused(void);\nCIMGUI_API ImGuiID igGetItemID(void);\nCIMGUI_API void igGetItemRectMin(ImVec2 *pOut);\nCIMGUI_API void igGetItemRectMax(ImVec2 *pOut);\nCIMGUI_API void igGetItemRectSize(ImVec2 *pOut);\nCIMGUI_API ImGuiViewport* igGetMainViewport(void);\nCIMGUI_API ImDrawList* igGetBackgroundDrawList(ImGuiViewport* viewport);\nCIMGUI_API ImDrawList* igGetForegroundDrawList_ViewportPtr(ImGuiViewport* viewport);\nCIMGUI_API bool igIsRectVisible_Nil(const ImVec2 size);\nCIMGUI_API bool igIsRectVisible_Vec2(const ImVec2 rect_min,const ImVec2 rect_max);\nCIMGUI_API double igGetTime(void);\nCIMGUI_API int igGetFrameCount(void);\nCIMGUI_API ImDrawListSharedData* igGetDrawListSharedData(void);\nCIMGUI_API const char* igGetStyleColorName(ImGuiCol idx);\nCIMGUI_API void igSetStateStorage(ImGuiStorage* storage);\nCIMGUI_API ImGuiStorage* igGetStateStorage(void);\nCIMGUI_API void igCalcTextSize(ImVec2 *pOut,const char* text,const char* text_end,bool hide_text_after_double_hash,float wrap_width);\nCIMGUI_API void igColorConvertU32ToFloat4(ImVec4 *pOut,ImU32 in);\nCIMGUI_API ImU32 igColorConvertFloat4ToU32(const ImVec4 in);\nCIMGUI_API void igColorConvertRGBtoHSV(float r,float g,float b,float* out_h,float* out_s,float* out_v);\nCIMGUI_API void igColorConvertHSVtoRGB(float h,float s,float v,float* out_r,float* out_g,float* out_b);\nCIMGUI_API bool igIsKeyDown_Nil(ImGuiKey key);\nCIMGUI_API bool igIsKeyPressed_Bool(ImGuiKey key,bool repeat);\nCIMGUI_API bool igIsKeyReleased_Nil(ImGuiKey key);\nCIMGUI_API bool igIsKeyChordPressed_Nil(ImGuiKeyChord key_chord);\nCIMGUI_API int igGetKeyPressedAmount(ImGuiKey key,float repeat_delay,float rate);\nCIMGUI_API const char* igGetKeyName(ImGuiKey key);\nCIMGUI_API void igSetNextFrameWantCaptureKeyboard(bool want_capture_keyboard);\nCIMGUI_API bool igShortcut_Nil(ImGuiKeyChord key_chord,ImGuiInputFlags flags);\nCIMGUI_API void igSetNextItemShortcut(ImGuiKeyChord key_chord,ImGuiInputFlags flags);\nCIMGUI_API void igSetItemKeyOwner_Nil(ImGuiKey key);\nCIMGUI_API bool igIsMouseDown_Nil(ImGuiMouseButton button);\nCIMGUI_API bool igIsMouseClicked_Bool(ImGuiMouseButton button,bool repeat);\nCIMGUI_API bool igIsMouseReleased_Nil(ImGuiMouseButton button);\nCIMGUI_API bool igIsMouseDoubleClicked_Nil(ImGuiMouseButton button);\nCIMGUI_API bool igIsMouseReleasedWithDelay(ImGuiMouseButton button,float delay);\nCIMGUI_API int igGetMouseClickedCount(ImGuiMouseButton button);\nCIMGUI_API bool igIsMouseHoveringRect(const ImVec2 r_min,const ImVec2 r_max,bool clip);\nCIMGUI_API bool igIsMousePosValid(const ImVec2* mouse_pos);\nCIMGUI_API bool igIsAnyMouseDown(void);\nCIMGUI_API void igGetMousePos(ImVec2 *pOut);\nCIMGUI_API void igGetMousePosOnOpeningCurrentPopup(ImVec2 *pOut);\nCIMGUI_API bool igIsMouseDragging(ImGuiMouseButton button,float lock_threshold);\nCIMGUI_API void igGetMouseDragDelta(ImVec2 *pOut,ImGuiMouseButton button,float lock_threshold);\nCIMGUI_API void igResetMouseDragDelta(ImGuiMouseButton button);\nCIMGUI_API ImGuiMouseCursor igGetMouseCursor(void);\nCIMGUI_API void igSetMouseCursor(ImGuiMouseCursor cursor_type);\nCIMGUI_API void igSetNextFrameWantCaptureMouse(bool want_capture_mouse);\nCIMGUI_API const char* igGetClipboardText(void);\nCIMGUI_API void igSetClipboardText(const char* text);\nCIMGUI_API void igLoadIniSettingsFromDisk(const char* ini_filename);\nCIMGUI_API void igLoadIniSettingsFromMemory(const char* ini_data,size_t ini_size);\nCIMGUI_API void igSaveIniSettingsToDisk(const char* ini_filename);\nCIMGUI_API const char* igSaveIniSettingsToMemory(size_t* out_ini_size);\nCIMGUI_API void igDebugTextEncoding(const char* text);\nCIMGUI_API void igDebugFlashStyleColor(ImGuiCol idx);\nCIMGUI_API void igDebugStartItemPicker(void);\nCIMGUI_API bool igDebugCheckVersionAndDataLayout(const char* version_str,size_t sz_io,size_t sz_style,size_t sz_vec2,size_t sz_vec4,size_t sz_drawvert,size_t sz_drawidx);\nCIMGUI_API void igDebugLog(const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igDebugLog0(const char* fmt);\n#endif\nCIMGUI_API void igDebugLogV(const char* fmt,va_list args);\nCIMGUI_API void igSetAllocatorFunctions(ImGuiMemAllocFunc alloc_func,ImGuiMemFreeFunc free_func,void* user_data);\nCIMGUI_API void igGetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func,ImGuiMemFreeFunc* p_free_func,void** p_user_data);\nCIMGUI_API void* igMemAlloc(size_t size);\nCIMGUI_API void igMemFree(void* ptr);\nCIMGUI_API void igUpdatePlatformWindows(void);\nCIMGUI_API void igRenderPlatformWindowsDefault(void* platform_render_arg,void* renderer_render_arg);\nCIMGUI_API void igDestroyPlatformWindows(void);\nCIMGUI_API ImGuiViewport* igFindViewportByID(ImGuiID id);\nCIMGUI_API ImGuiViewport* igFindViewportByPlatformHandle(void* platform_handle);\nCIMGUI_API ImGuiTableSortSpecs* ImGuiTableSortSpecs_ImGuiTableSortSpecs(void);\nCIMGUI_API void ImGuiTableSortSpecs_destroy(ImGuiTableSortSpecs* self);\nCIMGUI_API ImGuiTableColumnSortSpecs* ImGuiTableColumnSortSpecs_ImGuiTableColumnSortSpecs(void);\nCIMGUI_API void ImGuiTableColumnSortSpecs_destroy(ImGuiTableColumnSortSpecs* self);\nCIMGUI_API ImGuiStyle* ImGuiStyle_ImGuiStyle(void);\nCIMGUI_API void ImGuiStyle_destroy(ImGuiStyle* self);\nCIMGUI_API void ImGuiStyle_ScaleAllSizes(ImGuiStyle* self,float scale_factor);\nCIMGUI_API void ImGuiIO_AddKeyEvent(ImGuiIO* self,ImGuiKey key,bool down);\nCIMGUI_API void ImGuiIO_AddKeyAnalogEvent(ImGuiIO* self,ImGuiKey key,bool down,float v);\nCIMGUI_API void ImGuiIO_AddMousePosEvent(ImGuiIO* self,float x,float y);\nCIMGUI_API void ImGuiIO_AddMouseButtonEvent(ImGuiIO* self,int button,bool down);\nCIMGUI_API void ImGuiIO_AddMouseWheelEvent(ImGuiIO* self,float wheel_x,float wheel_y);\nCIMGUI_API void ImGuiIO_AddMouseSourceEvent(ImGuiIO* self,ImGuiMouseSource source);\nCIMGUI_API void ImGuiIO_AddMouseViewportEvent(ImGuiIO* self,ImGuiID id);\nCIMGUI_API void ImGuiIO_AddFocusEvent(ImGuiIO* self,bool focused);\nCIMGUI_API void ImGuiIO_AddInputCharacter(ImGuiIO* self,unsigned int c);\nCIMGUI_API void ImGuiIO_AddInputCharacterUTF16(ImGuiIO* self,ImWchar16 c);\nCIMGUI_API void ImGuiIO_AddInputCharactersUTF8(ImGuiIO* self,const char* str);\nCIMGUI_API void ImGuiIO_SetKeyEventNativeData(ImGuiIO* self,ImGuiKey key,int native_keycode,int native_scancode,int native_legacy_index);\nCIMGUI_API void ImGuiIO_SetAppAcceptingEvents(ImGuiIO* self,bool accepting_events);\nCIMGUI_API void ImGuiIO_ClearEventsQueue(ImGuiIO* self);\nCIMGUI_API void ImGuiIO_ClearInputKeys(ImGuiIO* self);\nCIMGUI_API void ImGuiIO_ClearInputMouse(ImGuiIO* self);\nCIMGUI_API ImGuiIO* ImGuiIO_ImGuiIO(void);\nCIMGUI_API void ImGuiIO_destroy(ImGuiIO* self);\nCIMGUI_API ImGuiInputTextCallbackData* ImGuiInputTextCallbackData_ImGuiInputTextCallbackData(void);\nCIMGUI_API void ImGuiInputTextCallbackData_destroy(ImGuiInputTextCallbackData* self);\nCIMGUI_API void ImGuiInputTextCallbackData_DeleteChars(ImGuiInputTextCallbackData* self,int pos,int bytes_count);\nCIMGUI_API void ImGuiInputTextCallbackData_InsertChars(ImGuiInputTextCallbackData* self,int pos,const char* text,const char* text_end);\nCIMGUI_API void ImGuiInputTextCallbackData_SelectAll(ImGuiInputTextCallbackData* self);\nCIMGUI_API void ImGuiInputTextCallbackData_ClearSelection(ImGuiInputTextCallbackData* self);\nCIMGUI_API bool ImGuiInputTextCallbackData_HasSelection(ImGuiInputTextCallbackData* self);\nCIMGUI_API ImGuiWindowClass* ImGuiWindowClass_ImGuiWindowClass(void);\nCIMGUI_API void ImGuiWindowClass_destroy(ImGuiWindowClass* self);\nCIMGUI_API ImGuiPayload* ImGuiPayload_ImGuiPayload(void);\nCIMGUI_API void ImGuiPayload_destroy(ImGuiPayload* self);\nCIMGUI_API void ImGuiPayload_Clear(ImGuiPayload* self);\nCIMGUI_API bool ImGuiPayload_IsDataType(ImGuiPayload* self,const char* type);\nCIMGUI_API bool ImGuiPayload_IsPreview(ImGuiPayload* self);\nCIMGUI_API bool ImGuiPayload_IsDelivery(ImGuiPayload* self);\nCIMGUI_API ImGuiOnceUponAFrame* ImGuiOnceUponAFrame_ImGuiOnceUponAFrame(void);\nCIMGUI_API void ImGuiOnceUponAFrame_destroy(ImGuiOnceUponAFrame* self);\nCIMGUI_API ImGuiTextFilter* ImGuiTextFilter_ImGuiTextFilter(const char* default_filter);\nCIMGUI_API void ImGuiTextFilter_destroy(ImGuiTextFilter* self);\nCIMGUI_API bool ImGuiTextFilter_Draw(ImGuiTextFilter* self,const char* label,float width);\nCIMGUI_API bool ImGuiTextFilter_PassFilter(ImGuiTextFilter* self,const char* text,const char* text_end);\nCIMGUI_API void ImGuiTextFilter_Build(ImGuiTextFilter* self);\nCIMGUI_API void ImGuiTextFilter_Clear(ImGuiTextFilter* self);\nCIMGUI_API bool ImGuiTextFilter_IsActive(ImGuiTextFilter* self);\nCIMGUI_API ImGuiTextRange* ImGuiTextRange_ImGuiTextRange_Nil(void);\nCIMGUI_API void ImGuiTextRange_destroy(ImGuiTextRange* self);\nCIMGUI_API ImGuiTextRange* ImGuiTextRange_ImGuiTextRange_Str(const char* _b,const char* _e);\nCIMGUI_API bool ImGuiTextRange_empty(ImGuiTextRange* self);\nCIMGUI_API void ImGuiTextRange_split(ImGuiTextRange* self,char separator,ImVector_ImGuiTextRange* out);\nCIMGUI_API ImGuiTextBuffer* ImGuiTextBuffer_ImGuiTextBuffer(void);\nCIMGUI_API void ImGuiTextBuffer_destroy(ImGuiTextBuffer* self);\nCIMGUI_API const char* ImGuiTextBuffer_begin(ImGuiTextBuffer* self);\nCIMGUI_API const char* ImGuiTextBuffer_end(ImGuiTextBuffer* self);\nCIMGUI_API int ImGuiTextBuffer_size(ImGuiTextBuffer* self);\nCIMGUI_API bool ImGuiTextBuffer_empty(ImGuiTextBuffer* self);\nCIMGUI_API void ImGuiTextBuffer_clear(ImGuiTextBuffer* self);\nCIMGUI_API void ImGuiTextBuffer_resize(ImGuiTextBuffer* self,int size);\nCIMGUI_API void ImGuiTextBuffer_reserve(ImGuiTextBuffer* self,int capacity);\nCIMGUI_API const char* ImGuiTextBuffer_c_str(ImGuiTextBuffer* self);\nCIMGUI_API void ImGuiTextBuffer_append(ImGuiTextBuffer* self,const char* str,const char* str_end);\nCIMGUI_API void ImGuiTextBuffer_appendfv(ImGuiTextBuffer* self,const char* fmt,va_list args);\nCIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Int(ImGuiID _key,int _val);\nCIMGUI_API void ImGuiStoragePair_destroy(ImGuiStoragePair* self);\nCIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Float(ImGuiID _key,float _val);\nCIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Ptr(ImGuiID _key,void* _val);\nCIMGUI_API void ImGuiStorage_Clear(ImGuiStorage* self);\nCIMGUI_API int ImGuiStorage_GetInt(ImGuiStorage* self,ImGuiID key,int default_val);\nCIMGUI_API void ImGuiStorage_SetInt(ImGuiStorage* self,ImGuiID key,int val);\nCIMGUI_API bool ImGuiStorage_GetBool(ImGuiStorage* self,ImGuiID key,bool default_val);\nCIMGUI_API void ImGuiStorage_SetBool(ImGuiStorage* self,ImGuiID key,bool val);\nCIMGUI_API float ImGuiStorage_GetFloat(ImGuiStorage* self,ImGuiID key,float default_val);\nCIMGUI_API void ImGuiStorage_SetFloat(ImGuiStorage* self,ImGuiID key,float val);\nCIMGUI_API void* ImGuiStorage_GetVoidPtr(ImGuiStorage* self,ImGuiID key);\nCIMGUI_API void ImGuiStorage_SetVoidPtr(ImGuiStorage* self,ImGuiID key,void* val);\nCIMGUI_API int* ImGuiStorage_GetIntRef(ImGuiStorage* self,ImGuiID key,int default_val);\nCIMGUI_API bool* ImGuiStorage_GetBoolRef(ImGuiStorage* self,ImGuiID key,bool default_val);\nCIMGUI_API float* ImGuiStorage_GetFloatRef(ImGuiStorage* self,ImGuiID key,float default_val);\nCIMGUI_API void** ImGuiStorage_GetVoidPtrRef(ImGuiStorage* self,ImGuiID key,void* default_val);\nCIMGUI_API void ImGuiStorage_BuildSortByKey(ImGuiStorage* self);\nCIMGUI_API void ImGuiStorage_SetAllInt(ImGuiStorage* self,int val);\nCIMGUI_API ImGuiListClipper* ImGuiListClipper_ImGuiListClipper(void);\nCIMGUI_API void ImGuiListClipper_destroy(ImGuiListClipper* self);\nCIMGUI_API void ImGuiListClipper_Begin(ImGuiListClipper* self,int items_count,float items_height);\nCIMGUI_API void ImGuiListClipper_End(ImGuiListClipper* self);\nCIMGUI_API bool ImGuiListClipper_Step(ImGuiListClipper* self);\nCIMGUI_API void ImGuiListClipper_IncludeItemByIndex(ImGuiListClipper* self,int item_index);\nCIMGUI_API void ImGuiListClipper_IncludeItemsByIndex(ImGuiListClipper* self,int item_begin,int item_end);\nCIMGUI_API void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* self,int item_index);\nCIMGUI_API ImColor* ImColor_ImColor_Nil(void);\nCIMGUI_API void ImColor_destroy(ImColor* self);\nCIMGUI_API ImColor* ImColor_ImColor_Float(float r,float g,float b,float a);\nCIMGUI_API ImColor* ImColor_ImColor_Vec4(const ImVec4 col);\nCIMGUI_API ImColor* ImColor_ImColor_Int(int r,int g,int b,int a);\nCIMGUI_API ImColor* ImColor_ImColor_U32(ImU32 rgba);\nCIMGUI_API void ImColor_SetHSV(ImColor* self,float h,float s,float v,float a);\nCIMGUI_API void ImColor_HSV(ImColor *pOut,float h,float s,float v,float a);\nCIMGUI_API ImGuiSelectionBasicStorage* ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage(void);\nCIMGUI_API void ImGuiSelectionBasicStorage_destroy(ImGuiSelectionBasicStorage* self);\nCIMGUI_API void ImGuiSelectionBasicStorage_ApplyRequests(ImGuiSelectionBasicStorage* self,ImGuiMultiSelectIO* ms_io);\nCIMGUI_API bool ImGuiSelectionBasicStorage_Contains(ImGuiSelectionBasicStorage* self,ImGuiID id);\nCIMGUI_API void ImGuiSelectionBasicStorage_Clear(ImGuiSelectionBasicStorage* self);\nCIMGUI_API void ImGuiSelectionBasicStorage_Swap(ImGuiSelectionBasicStorage* self,ImGuiSelectionBasicStorage* r);\nCIMGUI_API void ImGuiSelectionBasicStorage_SetItemSelected(ImGuiSelectionBasicStorage* self,ImGuiID id,bool selected);\nCIMGUI_API bool ImGuiSelectionBasicStorage_GetNextSelectedItem(ImGuiSelectionBasicStorage* self,void** opaque_it,ImGuiID* out_id);\nCIMGUI_API ImGuiID ImGuiSelectionBasicStorage_GetStorageIdFromIndex(ImGuiSelectionBasicStorage* self,int idx);\nCIMGUI_API ImGuiSelectionExternalStorage* ImGuiSelectionExternalStorage_ImGuiSelectionExternalStorage(void);\nCIMGUI_API void ImGuiSelectionExternalStorage_destroy(ImGuiSelectionExternalStorage* self);\nCIMGUI_API void ImGuiSelectionExternalStorage_ApplyRequests(ImGuiSelectionExternalStorage* self,ImGuiMultiSelectIO* ms_io);\nCIMGUI_API ImDrawCmd* ImDrawCmd_ImDrawCmd(void);\nCIMGUI_API void ImDrawCmd_destroy(ImDrawCmd* self);\nCIMGUI_API ImTextureID ImDrawCmd_GetTexID(ImDrawCmd* self);\nCIMGUI_API ImDrawListSplitter* ImDrawListSplitter_ImDrawListSplitter(void);\nCIMGUI_API void ImDrawListSplitter_destroy(ImDrawListSplitter* self);\nCIMGUI_API void ImDrawListSplitter_Clear(ImDrawListSplitter* self);\nCIMGUI_API void ImDrawListSplitter_ClearFreeMemory(ImDrawListSplitter* self);\nCIMGUI_API void ImDrawListSplitter_Split(ImDrawListSplitter* self,ImDrawList* draw_list,int count);\nCIMGUI_API void ImDrawListSplitter_Merge(ImDrawListSplitter* self,ImDrawList* draw_list);\nCIMGUI_API void ImDrawListSplitter_SetCurrentChannel(ImDrawListSplitter* self,ImDrawList* draw_list,int channel_idx);\nCIMGUI_API ImDrawList* ImDrawList_ImDrawList(ImDrawListSharedData* shared_data);\nCIMGUI_API void ImDrawList_destroy(ImDrawList* self);\nCIMGUI_API void ImDrawList_PushClipRect(ImDrawList* self,const ImVec2 clip_rect_min,const ImVec2 clip_rect_max,bool intersect_with_current_clip_rect);\nCIMGUI_API void ImDrawList_PushClipRectFullScreen(ImDrawList* self);\nCIMGUI_API void ImDrawList_PopClipRect(ImDrawList* self);\nCIMGUI_API void ImDrawList_PushTexture(ImDrawList* self,ImTextureRef tex_ref);\nCIMGUI_API void ImDrawList_PopTexture(ImDrawList* self);\nCIMGUI_API void ImDrawList_GetClipRectMin(ImVec2 *pOut,ImDrawList* self);\nCIMGUI_API void ImDrawList_GetClipRectMax(ImVec2 *pOut,ImDrawList* self);\nCIMGUI_API void ImDrawList_AddLine(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,ImU32 col,float thickness);\nCIMGUI_API void ImDrawList_AddRect(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col,float rounding,ImDrawFlags flags,float thickness);\nCIMGUI_API void ImDrawList_AddRectFilled(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col,float rounding,ImDrawFlags flags);\nCIMGUI_API void ImDrawList_AddRectFilledMultiColor(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col_upr_left,ImU32 col_upr_right,ImU32 col_bot_right,ImU32 col_bot_left);\nCIMGUI_API void ImDrawList_AddQuad(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col,float thickness);\nCIMGUI_API void ImDrawList_AddQuadFilled(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col);\nCIMGUI_API void ImDrawList_AddTriangle(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col,float thickness);\nCIMGUI_API void ImDrawList_AddTriangleFilled(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col);\nCIMGUI_API void ImDrawList_AddCircle(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments,float thickness);\nCIMGUI_API void ImDrawList_AddCircleFilled(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments);\nCIMGUI_API void ImDrawList_AddNgon(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments,float thickness);\nCIMGUI_API void ImDrawList_AddNgonFilled(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments);\nCIMGUI_API void ImDrawList_AddEllipse(ImDrawList* self,const ImVec2 center,const ImVec2 radius,ImU32 col,float rot,int num_segments,float thickness);\nCIMGUI_API void ImDrawList_AddEllipseFilled(ImDrawList* self,const ImVec2 center,const ImVec2 radius,ImU32 col,float rot,int num_segments);\nCIMGUI_API void ImDrawList_AddText_Vec2(ImDrawList* self,const ImVec2 pos,ImU32 col,const char* text_begin,const char* text_end);\nCIMGUI_API void ImDrawList_AddText_FontPtr(ImDrawList* self,ImFont* font,float font_size,const ImVec2 pos,ImU32 col,const char* text_begin,const char* text_end,float wrap_width,const ImVec4* cpu_fine_clip_rect);\nCIMGUI_API void ImDrawList_AddBezierCubic(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col,float thickness,int num_segments);\nCIMGUI_API void ImDrawList_AddBezierQuadratic(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col,float thickness,int num_segments);\nCIMGUI_API void ImDrawList_AddPolyline(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col,ImDrawFlags flags,float thickness);\nCIMGUI_API void ImDrawList_AddConvexPolyFilled(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col);\nCIMGUI_API void ImDrawList_AddConcavePolyFilled(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col);\nCIMGUI_API void ImDrawList_AddImage(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p_min,const ImVec2 p_max,const ImVec2 uv_min,const ImVec2 uv_max,ImU32 col);\nCIMGUI_API void ImDrawList_AddImageQuad(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 uv1,const ImVec2 uv2,const ImVec2 uv3,const ImVec2 uv4,ImU32 col);\nCIMGUI_API void ImDrawList_AddImageRounded(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p_min,const ImVec2 p_max,const ImVec2 uv_min,const ImVec2 uv_max,ImU32 col,float rounding,ImDrawFlags flags);\nCIMGUI_API void ImDrawList_PathClear(ImDrawList* self);\nCIMGUI_API void ImDrawList_PathLineTo(ImDrawList* self,const ImVec2 pos);\nCIMGUI_API void ImDrawList_PathLineToMergeDuplicate(ImDrawList* self,const ImVec2 pos);\nCIMGUI_API void ImDrawList_PathFillConvex(ImDrawList* self,ImU32 col);\nCIMGUI_API void ImDrawList_PathFillConcave(ImDrawList* self,ImU32 col);\nCIMGUI_API void ImDrawList_PathStroke(ImDrawList* self,ImU32 col,ImDrawFlags flags,float thickness);\nCIMGUI_API void ImDrawList_PathArcTo(ImDrawList* self,const ImVec2 center,float radius,float a_min,float a_max,int num_segments);\nCIMGUI_API void ImDrawList_PathArcToFast(ImDrawList* self,const ImVec2 center,float radius,int a_min_of_12,int a_max_of_12);\nCIMGUI_API void ImDrawList_PathEllipticalArcTo(ImDrawList* self,const ImVec2 center,const ImVec2 radius,float rot,float a_min,float a_max,int num_segments);\nCIMGUI_API void ImDrawList_PathBezierCubicCurveTo(ImDrawList* self,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,int num_segments);\nCIMGUI_API void ImDrawList_PathBezierQuadraticCurveTo(ImDrawList* self,const ImVec2 p2,const ImVec2 p3,int num_segments);\nCIMGUI_API void ImDrawList_PathRect(ImDrawList* self,const ImVec2 rect_min,const ImVec2 rect_max,float rounding,ImDrawFlags flags);\nCIMGUI_API void ImDrawList_AddCallback(ImDrawList* self,ImDrawCallback callback,void* userdata,size_t userdata_size);\nCIMGUI_API void ImDrawList_AddDrawCmd(ImDrawList* self);\nCIMGUI_API ImDrawList* ImDrawList_CloneOutput(ImDrawList* self);\nCIMGUI_API void ImDrawList_ChannelsSplit(ImDrawList* self,int count);\nCIMGUI_API void ImDrawList_ChannelsMerge(ImDrawList* self);\nCIMGUI_API void ImDrawList_ChannelsSetCurrent(ImDrawList* self,int n);\nCIMGUI_API void ImDrawList_PrimReserve(ImDrawList* self,int idx_count,int vtx_count);\nCIMGUI_API void ImDrawList_PrimUnreserve(ImDrawList* self,int idx_count,int vtx_count);\nCIMGUI_API void ImDrawList_PrimRect(ImDrawList* self,const ImVec2 a,const ImVec2 b,ImU32 col);\nCIMGUI_API void ImDrawList_PrimRectUV(ImDrawList* self,const ImVec2 a,const ImVec2 b,const ImVec2 uv_a,const ImVec2 uv_b,ImU32 col);\nCIMGUI_API void ImDrawList_PrimQuadUV(ImDrawList* self,const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 d,const ImVec2 uv_a,const ImVec2 uv_b,const ImVec2 uv_c,const ImVec2 uv_d,ImU32 col);\nCIMGUI_API void ImDrawList_PrimWriteVtx(ImDrawList* self,const ImVec2 pos,const ImVec2 uv,ImU32 col);\nCIMGUI_API void ImDrawList_PrimWriteIdx(ImDrawList* self,ImDrawIdx idx);\nCIMGUI_API void ImDrawList_PrimVtx(ImDrawList* self,const ImVec2 pos,const ImVec2 uv,ImU32 col);\nCIMGUI_API void ImDrawList__SetDrawListSharedData(ImDrawList* self,ImDrawListSharedData* data);\nCIMGUI_API void ImDrawList__ResetForNewFrame(ImDrawList* self);\nCIMGUI_API void ImDrawList__ClearFreeMemory(ImDrawList* self);\nCIMGUI_API void ImDrawList__PopUnusedDrawCmd(ImDrawList* self);\nCIMGUI_API void ImDrawList__TryMergeDrawCmds(ImDrawList* self);\nCIMGUI_API void ImDrawList__OnChangedClipRect(ImDrawList* self);\nCIMGUI_API void ImDrawList__OnChangedTexture(ImDrawList* self);\nCIMGUI_API void ImDrawList__OnChangedVtxOffset(ImDrawList* self);\nCIMGUI_API void ImDrawList__SetTexture(ImDrawList* self,ImTextureRef tex_ref);\nCIMGUI_API int ImDrawList__CalcCircleAutoSegmentCount(ImDrawList* self,float radius);\nCIMGUI_API void ImDrawList__PathArcToFastEx(ImDrawList* self,const ImVec2 center,float radius,int a_min_sample,int a_max_sample,int a_step);\nCIMGUI_API void ImDrawList__PathArcToN(ImDrawList* self,const ImVec2 center,float radius,float a_min,float a_max,int num_segments);\nCIMGUI_API ImDrawData* ImDrawData_ImDrawData(void);\nCIMGUI_API void ImDrawData_destroy(ImDrawData* self);\nCIMGUI_API void ImDrawData_Clear(ImDrawData* self);\nCIMGUI_API void ImDrawData_AddDrawList(ImDrawData* self,ImDrawList* draw_list);\nCIMGUI_API void ImDrawData_DeIndexAllBuffers(ImDrawData* self);\nCIMGUI_API void ImDrawData_ScaleClipRects(ImDrawData* self,const ImVec2 fb_scale);\nCIMGUI_API ImTextureData* ImTextureData_ImTextureData(void);\nCIMGUI_API void ImTextureData_destroy(ImTextureData* self);\nCIMGUI_API void ImTextureData_Create(ImTextureData* self,ImTextureFormat format,int w,int h);\nCIMGUI_API void ImTextureData_DestroyPixels(ImTextureData* self);\nCIMGUI_API void* ImTextureData_GetPixels(ImTextureData* self);\nCIMGUI_API void* ImTextureData_GetPixelsAt(ImTextureData* self,int x,int y);\nCIMGUI_API int ImTextureData_GetSizeInBytes(ImTextureData* self);\nCIMGUI_API int ImTextureData_GetPitch(ImTextureData* self);\nCIMGUI_API void ImTextureData_GetTexRef(ImTextureRef *pOut,ImTextureData* self);\nCIMGUI_API ImTextureID ImTextureData_GetTexID(ImTextureData* self);\nCIMGUI_API void ImTextureData_SetTexID(ImTextureData* self,ImTextureID tex_id);\nCIMGUI_API void ImTextureData_SetStatus(ImTextureData* self,ImTextureStatus status);\nCIMGUI_API ImFontConfig* ImFontConfig_ImFontConfig(void);\nCIMGUI_API void ImFontConfig_destroy(ImFontConfig* self);\nCIMGUI_API ImFontGlyph* ImFontGlyph_ImFontGlyph(void);\nCIMGUI_API void ImFontGlyph_destroy(ImFontGlyph* self);\nCIMGUI_API ImFontGlyphRangesBuilder* ImFontGlyphRangesBuilder_ImFontGlyphRangesBuilder(void);\nCIMGUI_API void ImFontGlyphRangesBuilder_destroy(ImFontGlyphRangesBuilder* self);\nCIMGUI_API void ImFontGlyphRangesBuilder_Clear(ImFontGlyphRangesBuilder* self);\nCIMGUI_API bool ImFontGlyphRangesBuilder_GetBit(ImFontGlyphRangesBuilder* self,size_t n);\nCIMGUI_API void ImFontGlyphRangesBuilder_SetBit(ImFontGlyphRangesBuilder* self,size_t n);\nCIMGUI_API void ImFontGlyphRangesBuilder_AddChar(ImFontGlyphRangesBuilder* self,ImWchar c);\nCIMGUI_API void ImFontGlyphRangesBuilder_AddText(ImFontGlyphRangesBuilder* self,const char* text,const char* text_end);\nCIMGUI_API void ImFontGlyphRangesBuilder_AddRanges(ImFontGlyphRangesBuilder* self,const ImWchar* ranges);\nCIMGUI_API void ImFontGlyphRangesBuilder_BuildRanges(ImFontGlyphRangesBuilder* self,ImVector_ImWchar* out_ranges);\nCIMGUI_API ImFontAtlasRect* ImFontAtlasRect_ImFontAtlasRect(void);\nCIMGUI_API void ImFontAtlasRect_destroy(ImFontAtlasRect* self);\nCIMGUI_API ImFontAtlas* ImFontAtlas_ImFontAtlas(void);\nCIMGUI_API void ImFontAtlas_destroy(ImFontAtlas* self);\nCIMGUI_API ImFont* ImFontAtlas_AddFont(ImFontAtlas* self,const ImFontConfig* font_cfg);\nCIMGUI_API ImFont* ImFontAtlas_AddFontDefault(ImFontAtlas* self,const ImFontConfig* font_cfg);\nCIMGUI_API ImFont* ImFontAtlas_AddFontFromFileTTF(ImFontAtlas* self,const char* filename,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges);\nCIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryTTF(ImFontAtlas* self,void* font_data,int font_data_size,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges);\nCIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryCompressedTTF(ImFontAtlas* self,const void* compressed_font_data,int compressed_font_data_size,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges);\nCIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryCompressedBase85TTF(ImFontAtlas* self,const char* compressed_font_data_base85,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges);\nCIMGUI_API void ImFontAtlas_RemoveFont(ImFontAtlas* self,ImFont* font);\nCIMGUI_API void ImFontAtlas_Clear(ImFontAtlas* self);\nCIMGUI_API void ImFontAtlas_CompactCache(ImFontAtlas* self);\nCIMGUI_API void ImFontAtlas_ClearInputData(ImFontAtlas* self);\nCIMGUI_API void ImFontAtlas_ClearFonts(ImFontAtlas* self);\nCIMGUI_API void ImFontAtlas_ClearTexData(ImFontAtlas* self);\nCIMGUI_API const ImWchar* ImFontAtlas_GetGlyphRangesDefault(ImFontAtlas* self);\nCIMGUI_API ImFontAtlasRectId ImFontAtlas_AddCustomRect(ImFontAtlas* self,int width,int height,ImFontAtlasRect* out_r);\nCIMGUI_API void ImFontAtlas_RemoveCustomRect(ImFontAtlas* self,ImFontAtlasRectId id);\nCIMGUI_API bool ImFontAtlas_GetCustomRect(ImFontAtlas* self,ImFontAtlasRectId id,ImFontAtlasRect* out_r);\nCIMGUI_API ImFontBaked* ImFontBaked_ImFontBaked(void);\nCIMGUI_API void ImFontBaked_destroy(ImFontBaked* self);\nCIMGUI_API void ImFontBaked_ClearOutputData(ImFontBaked* self);\nCIMGUI_API ImFontGlyph* ImFontBaked_FindGlyph(ImFontBaked* self,ImWchar c);\nCIMGUI_API ImFontGlyph* ImFontBaked_FindGlyphNoFallback(ImFontBaked* self,ImWchar c);\nCIMGUI_API float ImFontBaked_GetCharAdvance(ImFontBaked* self,ImWchar c);\nCIMGUI_API bool ImFontBaked_IsGlyphLoaded(ImFontBaked* self,ImWchar c);\nCIMGUI_API ImFont* ImFont_ImFont(void);\nCIMGUI_API void ImFont_destroy(ImFont* self);\nCIMGUI_API bool ImFont_IsGlyphInFont(ImFont* self,ImWchar c);\nCIMGUI_API bool ImFont_IsLoaded(ImFont* self);\nCIMGUI_API const char* ImFont_GetDebugName(ImFont* self);\nCIMGUI_API ImFontBaked* ImFont_GetFontBaked(ImFont* self,float font_size,float density);\nCIMGUI_API void ImFont_CalcTextSizeA(ImVec2 *pOut,ImFont* self,float size,float max_width,float wrap_width,const char* text_begin,const char* text_end,const char** remaining);\nCIMGUI_API const char* ImFont_CalcWordWrapPosition(ImFont* self,float size,const char* text,const char* text_end,float wrap_width);\nCIMGUI_API void ImFont_RenderChar(ImFont* self,ImDrawList* draw_list,float size,const ImVec2 pos,ImU32 col,ImWchar c,const ImVec4* cpu_fine_clip);\nCIMGUI_API void ImFont_RenderText(ImFont* self,ImDrawList* draw_list,float size,const ImVec2 pos,ImU32 col,const ImVec4 clip_rect,const char* text_begin,const char* text_end,float wrap_width,bool cpu_fine_clip);\nCIMGUI_API void ImFont_ClearOutputData(ImFont* self);\nCIMGUI_API void ImFont_AddRemapChar(ImFont* self,ImWchar from_codepoint,ImWchar to_codepoint);\nCIMGUI_API bool ImFont_IsGlyphRangeUnused(ImFont* self,unsigned int c_begin,unsigned int c_last);\nCIMGUI_API ImGuiViewport* ImGuiViewport_ImGuiViewport(void);\nCIMGUI_API void ImGuiViewport_destroy(ImGuiViewport* self);\nCIMGUI_API void ImGuiViewport_GetCenter(ImVec2 *pOut,ImGuiViewport* self);\nCIMGUI_API void ImGuiViewport_GetWorkCenter(ImVec2 *pOut,ImGuiViewport* self);\nCIMGUI_API ImGuiPlatformIO* ImGuiPlatformIO_ImGuiPlatformIO(void);\nCIMGUI_API void ImGuiPlatformIO_destroy(ImGuiPlatformIO* self);\nCIMGUI_API ImGuiPlatformMonitor* ImGuiPlatformMonitor_ImGuiPlatformMonitor(void);\nCIMGUI_API void ImGuiPlatformMonitor_destroy(ImGuiPlatformMonitor* self);\nCIMGUI_API ImGuiPlatformImeData* ImGuiPlatformImeData_ImGuiPlatformImeData(void);\nCIMGUI_API void ImGuiPlatformImeData_destroy(ImGuiPlatformImeData* self);\nCIMGUI_API ImGuiID igImHashData(const void* data,size_t data_size,ImGuiID seed);\nCIMGUI_API ImGuiID igImHashStr(const char* data,size_t data_size,ImGuiID seed);\nCIMGUI_API void igImQsort(void* base,size_t count,size_t size_of_element,int(*compare_func)(void const*,void const*));\nCIMGUI_API ImU32 igImAlphaBlendColors(ImU32 col_a,ImU32 col_b);\nCIMGUI_API bool igImIsPowerOfTwo_Int(int v);\nCIMGUI_API bool igImIsPowerOfTwo_U64(ImU64 v);\nCIMGUI_API int igImUpperPowerOfTwo(int v);\nCIMGUI_API unsigned int igImCountSetBits(unsigned int v);\nCIMGUI_API int igImStricmp(const char* str1,const char* str2);\nCIMGUI_API int igImStrnicmp(const char* str1,const char* str2,size_t count);\nCIMGUI_API void igImStrncpy(char* dst,const char* src,size_t count);\nCIMGUI_API char* igImStrdup(const char* str);\nCIMGUI_API void* igImMemdup(const void* src,size_t size);\nCIMGUI_API char* igImStrdupcpy(char* dst,size_t* p_dst_size,const char* str);\nCIMGUI_API const char* igImStrchrRange(const char* str_begin,const char* str_end,char c);\nCIMGUI_API const char* igImStreolRange(const char* str,const char* str_end);\nCIMGUI_API const char* igImStristr(const char* haystack,const char* haystack_end,const char* needle,const char* needle_end);\nCIMGUI_API void igImStrTrimBlanks(char* str);\nCIMGUI_API const char* igImStrSkipBlank(const char* str);\nCIMGUI_API int igImStrlenW(const ImWchar* str);\nCIMGUI_API const char* igImStrbol(const char* buf_mid_line,const char* buf_begin);\nCIMGUI_API char igImToUpper(char c);\nCIMGUI_API bool igImCharIsBlankA(char c);\nCIMGUI_API bool igImCharIsBlankW(unsigned int c);\nCIMGUI_API bool igImCharIsXdigitA(char c);\nCIMGUI_API int igImFormatString(char* buf,size_t buf_size,const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API int igImFormatString0(char* buf,size_t buf_size,const char* fmt);\n#endif\nCIMGUI_API int igImFormatStringV(char* buf,size_t buf_size,const char* fmt,va_list args);\nCIMGUI_API void igImFormatStringToTempBuffer(const char** out_buf,const char** out_buf_end,const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igImFormatStringToTempBuffer0(const char** out_buf,const char** out_buf_end,const char* fmt);\n#endif\nCIMGUI_API void igImFormatStringToTempBufferV(const char** out_buf,const char** out_buf_end,const char* fmt,va_list args);\nCIMGUI_API const char* igImParseFormatFindStart(const char* format);\nCIMGUI_API const char* igImParseFormatFindEnd(const char* format);\nCIMGUI_API const char* igImParseFormatTrimDecorations(const char* format,char* buf,size_t buf_size);\nCIMGUI_API void igImParseFormatSanitizeForPrinting(const char* fmt_in,char* fmt_out,size_t fmt_out_size);\nCIMGUI_API const char* igImParseFormatSanitizeForScanning(const char* fmt_in,char* fmt_out,size_t fmt_out_size);\nCIMGUI_API int igImParseFormatPrecision(const char* format,int default_value);\nCIMGUI_API int igImTextCharToUtf8(char out_buf[5],unsigned int c);\nCIMGUI_API int igImTextStrToUtf8(char* out_buf,int out_buf_size,const ImWchar* in_text,const ImWchar* in_text_end);\nCIMGUI_API int igImTextCharFromUtf8(unsigned int* out_char,const char* in_text,const char* in_text_end);\nCIMGUI_API int igImTextStrFromUtf8(ImWchar* out_buf,int out_buf_size,const char* in_text,const char* in_text_end,const char** in_remaining);\nCIMGUI_API int igImTextCountCharsFromUtf8(const char* in_text,const char* in_text_end);\nCIMGUI_API int igImTextCountUtf8BytesFromChar(const char* in_text,const char* in_text_end);\nCIMGUI_API int igImTextCountUtf8BytesFromStr(const ImWchar* in_text,const ImWchar* in_text_end);\nCIMGUI_API const char* igImTextFindPreviousUtf8Codepoint(const char* in_text_start,const char* in_text_curr);\nCIMGUI_API int igImTextCountLines(const char* in_text,const char* in_text_end);\nCIMGUI_API ImFileHandle igImFileOpen(const char* filename,const char* mode);\nCIMGUI_API bool igImFileClose(ImFileHandle file);\nCIMGUI_API ImU64 igImFileGetSize(ImFileHandle file);\nCIMGUI_API ImU64 igImFileRead(void* data,ImU64 size,ImU64 count,ImFileHandle file);\nCIMGUI_API ImU64 igImFileWrite(const void* data,ImU64 size,ImU64 count,ImFileHandle file);\nCIMGUI_API void* igImFileLoadToMemory(const char* filename,const char* mode,size_t* out_file_size,int padding_bytes);\nCIMGUI_API float igImPow_Float(float x,float y);\nCIMGUI_API double igImPow_double(double x,double y);\nCIMGUI_API float igImLog_Float(float x);\nCIMGUI_API double igImLog_double(double x);\nCIMGUI_API int igImAbs_Int(int x);\nCIMGUI_API float igImAbs_Float(float x);\nCIMGUI_API double igImAbs_double(double x);\nCIMGUI_API float igImSign_Float(float x);\nCIMGUI_API double igImSign_double(double x);\nCIMGUI_API float igImRsqrt_Float(float x);\nCIMGUI_API double igImRsqrt_double(double x);\nCIMGUI_API void igImMin(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs);\nCIMGUI_API void igImMax(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs);\nCIMGUI_API void igImClamp(ImVec2 *pOut,const ImVec2 v,const ImVec2 mn,const ImVec2 mx);\nCIMGUI_API void igImLerp_Vec2Float(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,float t);\nCIMGUI_API void igImLerp_Vec2Vec2(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 t);\nCIMGUI_API void igImLerp_Vec4(ImVec4 *pOut,const ImVec4 a,const ImVec4 b,float t);\nCIMGUI_API float igImSaturate(float f);\nCIMGUI_API float igImLengthSqr_Vec2(const ImVec2 lhs);\nCIMGUI_API float igImLengthSqr_Vec4(const ImVec4 lhs);\nCIMGUI_API float igImInvLength(const ImVec2 lhs,float fail_value);\nCIMGUI_API float igImTrunc_Float(float f);\nCIMGUI_API void igImTrunc_Vec2(ImVec2 *pOut,const ImVec2 v);\nCIMGUI_API float igImFloor_Float(float f);\nCIMGUI_API void igImFloor_Vec2(ImVec2 *pOut,const ImVec2 v);\nCIMGUI_API float igImTrunc64(float f);\nCIMGUI_API float igImRound64(float f);\nCIMGUI_API int igImModPositive(int a,int b);\nCIMGUI_API float igImDot(const ImVec2 a,const ImVec2 b);\nCIMGUI_API void igImRotate(ImVec2 *pOut,const ImVec2 v,float cos_a,float sin_a);\nCIMGUI_API float igImLinearSweep(float current,float target,float speed);\nCIMGUI_API float igImLinearRemapClamp(float s0,float s1,float d0,float d1,float x);\nCIMGUI_API void igImMul(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs);\nCIMGUI_API bool igImIsFloatAboveGuaranteedIntegerPrecision(float f);\nCIMGUI_API float igImExponentialMovingAverage(float avg,float sample,int n);\nCIMGUI_API void igImBezierCubicCalc(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,float t);\nCIMGUI_API void igImBezierCubicClosestPoint(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 p,int num_segments);\nCIMGUI_API void igImBezierCubicClosestPointCasteljau(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 p,float tess_tol);\nCIMGUI_API void igImBezierQuadraticCalc(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,float t);\nCIMGUI_API void igImLineClosestPoint(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 p);\nCIMGUI_API bool igImTriangleContainsPoint(const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p);\nCIMGUI_API void igImTriangleClosestPoint(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p);\nCIMGUI_API void igImTriangleBarycentricCoords(const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p,float* out_u,float* out_v,float* out_w);\nCIMGUI_API float igImTriangleArea(const ImVec2 a,const ImVec2 b,const ImVec2 c);\nCIMGUI_API bool igImTriangleIsClockwise(const ImVec2 a,const ImVec2 b,const ImVec2 c);\nCIMGUI_API ImVec1* ImVec1_ImVec1_Nil(void);\nCIMGUI_API void ImVec1_destroy(ImVec1* self);\nCIMGUI_API ImVec1* ImVec1_ImVec1_Float(float _x);\nCIMGUI_API ImVec2i* ImVec2i_ImVec2i_Nil(void);\nCIMGUI_API void ImVec2i_destroy(ImVec2i* self);\nCIMGUI_API ImVec2i* ImVec2i_ImVec2i_Int(int _x,int _y);\nCIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_Nil(void);\nCIMGUI_API void ImVec2ih_destroy(ImVec2ih* self);\nCIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_short(short _x,short _y);\nCIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_Vec2(const ImVec2 rhs);\nCIMGUI_API ImRect* ImRect_ImRect_Nil(void);\nCIMGUI_API void ImRect_destroy(ImRect* self);\nCIMGUI_API ImRect* ImRect_ImRect_Vec2(const ImVec2 min,const ImVec2 max);\nCIMGUI_API ImRect* ImRect_ImRect_Vec4(const ImVec4 v);\nCIMGUI_API ImRect* ImRect_ImRect_Float(float x1,float y1,float x2,float y2);\nCIMGUI_API void ImRect_GetCenter(ImVec2 *pOut,ImRect* self);\nCIMGUI_API void ImRect_GetSize(ImVec2 *pOut,ImRect* self);\nCIMGUI_API float ImRect_GetWidth(ImRect* self);\nCIMGUI_API float ImRect_GetHeight(ImRect* self);\nCIMGUI_API float ImRect_GetArea(ImRect* self);\nCIMGUI_API void ImRect_GetTL(ImVec2 *pOut,ImRect* self);\nCIMGUI_API void ImRect_GetTR(ImVec2 *pOut,ImRect* self);\nCIMGUI_API void ImRect_GetBL(ImVec2 *pOut,ImRect* self);\nCIMGUI_API void ImRect_GetBR(ImVec2 *pOut,ImRect* self);\nCIMGUI_API bool ImRect_Contains_Vec2(ImRect* self,const ImVec2 p);\nCIMGUI_API bool ImRect_Contains_Rect(ImRect* self,const ImRect r);\nCIMGUI_API bool ImRect_ContainsWithPad(ImRect* self,const ImVec2 p,const ImVec2 pad);\nCIMGUI_API bool ImRect_Overlaps(ImRect* self,const ImRect r);\nCIMGUI_API void ImRect_Add_Vec2(ImRect* self,const ImVec2 p);\nCIMGUI_API void ImRect_Add_Rect(ImRect* self,const ImRect r);\nCIMGUI_API void ImRect_Expand_Float(ImRect* self,const float amount);\nCIMGUI_API void ImRect_Expand_Vec2(ImRect* self,const ImVec2 amount);\nCIMGUI_API void ImRect_Translate(ImRect* self,const ImVec2 d);\nCIMGUI_API void ImRect_TranslateX(ImRect* self,float dx);\nCIMGUI_API void ImRect_TranslateY(ImRect* self,float dy);\nCIMGUI_API void ImRect_ClipWith(ImRect* self,const ImRect r);\nCIMGUI_API void ImRect_ClipWithFull(ImRect* self,const ImRect r);\nCIMGUI_API bool ImRect_IsInverted(ImRect* self);\nCIMGUI_API void ImRect_ToVec4(ImVec4 *pOut,ImRect* self);\nCIMGUI_API size_t igImBitArrayGetStorageSizeInBytes(int bitcount);\nCIMGUI_API void igImBitArrayClearAllBits(ImU32* arr,int bitcount);\nCIMGUI_API bool igImBitArrayTestBit(const ImU32* arr,int n);\nCIMGUI_API void igImBitArrayClearBit(ImU32* arr,int n);\nCIMGUI_API void igImBitArraySetBit(ImU32* arr,int n);\nCIMGUI_API void igImBitArraySetBitRange(ImU32* arr,int n,int n2);\nCIMGUI_API void ImBitVector_Create(ImBitVector* self,int sz);\nCIMGUI_API void ImBitVector_Clear(ImBitVector* self);\nCIMGUI_API bool ImBitVector_TestBit(ImBitVector* self,int n);\nCIMGUI_API void ImBitVector_SetBit(ImBitVector* self,int n);\nCIMGUI_API void ImBitVector_ClearBit(ImBitVector* self,int n);\nCIMGUI_API void ImGuiTextIndex_clear(ImGuiTextIndex* self);\nCIMGUI_API int ImGuiTextIndex_size(ImGuiTextIndex* self);\nCIMGUI_API const char* ImGuiTextIndex_get_line_begin(ImGuiTextIndex* self,const char* base,int n);\nCIMGUI_API const char* ImGuiTextIndex_get_line_end(ImGuiTextIndex* self,const char* base,int n);\nCIMGUI_API void ImGuiTextIndex_append(ImGuiTextIndex* self,const char* base,int old_size,int new_size);\nCIMGUI_API ImGuiStoragePair* igImLowerBound(ImGuiStoragePair* in_begin,ImGuiStoragePair* in_end,ImGuiID key);\nCIMGUI_API ImDrawListSharedData* ImDrawListSharedData_ImDrawListSharedData(void);\nCIMGUI_API void ImDrawListSharedData_destroy(ImDrawListSharedData* self);\nCIMGUI_API void ImDrawListSharedData_SetCircleTessellationMaxError(ImDrawListSharedData* self,float max_error);\nCIMGUI_API ImDrawDataBuilder* ImDrawDataBuilder_ImDrawDataBuilder(void);\nCIMGUI_API void ImDrawDataBuilder_destroy(ImDrawDataBuilder* self);\nCIMGUI_API void* ImGuiStyleVarInfo_GetVarPtr(ImGuiStyleVarInfo* self,void* parent);\nCIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Int(ImGuiStyleVar idx,int v);\nCIMGUI_API void ImGuiStyleMod_destroy(ImGuiStyleMod* self);\nCIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Float(ImGuiStyleVar idx,float v);\nCIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Vec2(ImGuiStyleVar idx,ImVec2 v);\nCIMGUI_API ImGuiComboPreviewData* ImGuiComboPreviewData_ImGuiComboPreviewData(void);\nCIMGUI_API void ImGuiComboPreviewData_destroy(ImGuiComboPreviewData* self);\nCIMGUI_API ImGuiMenuColumns* ImGuiMenuColumns_ImGuiMenuColumns(void);\nCIMGUI_API void ImGuiMenuColumns_destroy(ImGuiMenuColumns* self);\nCIMGUI_API void ImGuiMenuColumns_Update(ImGuiMenuColumns* self,float spacing,bool window_reappearing);\nCIMGUI_API float ImGuiMenuColumns_DeclColumns(ImGuiMenuColumns* self,float w_icon,float w_label,float w_shortcut,float w_mark);\nCIMGUI_API void ImGuiMenuColumns_CalcNextTotalWidth(ImGuiMenuColumns* self,bool update_offsets);\nCIMGUI_API ImGuiInputTextDeactivatedState* ImGuiInputTextDeactivatedState_ImGuiInputTextDeactivatedState(void);\nCIMGUI_API void ImGuiInputTextDeactivatedState_destroy(ImGuiInputTextDeactivatedState* self);\nCIMGUI_API void ImGuiInputTextDeactivatedState_ClearFreeMemory(ImGuiInputTextDeactivatedState* self);\nCIMGUI_API ImGuiInputTextState* ImGuiInputTextState_ImGuiInputTextState(void);\nCIMGUI_API void ImGuiInputTextState_destroy(ImGuiInputTextState* self);\nCIMGUI_API void ImGuiInputTextState_ClearText(ImGuiInputTextState* self);\nCIMGUI_API void ImGuiInputTextState_ClearFreeMemory(ImGuiInputTextState* self);\nCIMGUI_API void ImGuiInputTextState_OnKeyPressed(ImGuiInputTextState* self,int key);\nCIMGUI_API void ImGuiInputTextState_OnCharPressed(ImGuiInputTextState* self,unsigned int c);\nCIMGUI_API void ImGuiInputTextState_CursorAnimReset(ImGuiInputTextState* self);\nCIMGUI_API void ImGuiInputTextState_CursorClamp(ImGuiInputTextState* self);\nCIMGUI_API bool ImGuiInputTextState_HasSelection(ImGuiInputTextState* self);\nCIMGUI_API void ImGuiInputTextState_ClearSelection(ImGuiInputTextState* self);\nCIMGUI_API int ImGuiInputTextState_GetCursorPos(ImGuiInputTextState* self);\nCIMGUI_API int ImGuiInputTextState_GetSelectionStart(ImGuiInputTextState* self);\nCIMGUI_API int ImGuiInputTextState_GetSelectionEnd(ImGuiInputTextState* self);\nCIMGUI_API void ImGuiInputTextState_SelectAll(ImGuiInputTextState* self);\nCIMGUI_API void ImGuiInputTextState_ReloadUserBufAndSelectAll(ImGuiInputTextState* self);\nCIMGUI_API void ImGuiInputTextState_ReloadUserBufAndKeepSelection(ImGuiInputTextState* self);\nCIMGUI_API void ImGuiInputTextState_ReloadUserBufAndMoveToEnd(ImGuiInputTextState* self);\nCIMGUI_API ImGuiNextWindowData* ImGuiNextWindowData_ImGuiNextWindowData(void);\nCIMGUI_API void ImGuiNextWindowData_destroy(ImGuiNextWindowData* self);\nCIMGUI_API void ImGuiNextWindowData_ClearFlags(ImGuiNextWindowData* self);\nCIMGUI_API ImGuiNextItemData* ImGuiNextItemData_ImGuiNextItemData(void);\nCIMGUI_API void ImGuiNextItemData_destroy(ImGuiNextItemData* self);\nCIMGUI_API void ImGuiNextItemData_ClearFlags(ImGuiNextItemData* self);\nCIMGUI_API ImGuiLastItemData* ImGuiLastItemData_ImGuiLastItemData(void);\nCIMGUI_API void ImGuiLastItemData_destroy(ImGuiLastItemData* self);\nCIMGUI_API ImGuiErrorRecoveryState* ImGuiErrorRecoveryState_ImGuiErrorRecoveryState(void);\nCIMGUI_API void ImGuiErrorRecoveryState_destroy(ImGuiErrorRecoveryState* self);\nCIMGUI_API ImGuiPtrOrIndex* ImGuiPtrOrIndex_ImGuiPtrOrIndex_Ptr(void* ptr);\nCIMGUI_API void ImGuiPtrOrIndex_destroy(ImGuiPtrOrIndex* self);\nCIMGUI_API ImGuiPtrOrIndex* ImGuiPtrOrIndex_ImGuiPtrOrIndex_Int(int index);\nCIMGUI_API ImGuiPopupData* ImGuiPopupData_ImGuiPopupData(void);\nCIMGUI_API void ImGuiPopupData_destroy(ImGuiPopupData* self);\nCIMGUI_API ImGuiInputEvent* ImGuiInputEvent_ImGuiInputEvent(void);\nCIMGUI_API void ImGuiInputEvent_destroy(ImGuiInputEvent* self);\nCIMGUI_API ImGuiKeyRoutingData* ImGuiKeyRoutingData_ImGuiKeyRoutingData(void);\nCIMGUI_API void ImGuiKeyRoutingData_destroy(ImGuiKeyRoutingData* self);\nCIMGUI_API ImGuiKeyRoutingTable* ImGuiKeyRoutingTable_ImGuiKeyRoutingTable(void);\nCIMGUI_API void ImGuiKeyRoutingTable_destroy(ImGuiKeyRoutingTable* self);\nCIMGUI_API void ImGuiKeyRoutingTable_Clear(ImGuiKeyRoutingTable* self);\nCIMGUI_API ImGuiKeyOwnerData* ImGuiKeyOwnerData_ImGuiKeyOwnerData(void);\nCIMGUI_API void ImGuiKeyOwnerData_destroy(ImGuiKeyOwnerData* self);\nCIMGUI_API ImGuiListClipperRange ImGuiListClipperRange_FromIndices(int min,int max);\nCIMGUI_API ImGuiListClipperRange ImGuiListClipperRange_FromPositions(float y1,float y2,int off_min,int off_max);\nCIMGUI_API ImGuiListClipperData* ImGuiListClipperData_ImGuiListClipperData(void);\nCIMGUI_API void ImGuiListClipperData_destroy(ImGuiListClipperData* self);\nCIMGUI_API void ImGuiListClipperData_Reset(ImGuiListClipperData* self,ImGuiListClipper* clipper);\nCIMGUI_API ImGuiNavItemData* ImGuiNavItemData_ImGuiNavItemData(void);\nCIMGUI_API void ImGuiNavItemData_destroy(ImGuiNavItemData* self);\nCIMGUI_API void ImGuiNavItemData_Clear(ImGuiNavItemData* self);\nCIMGUI_API ImGuiTypingSelectState* ImGuiTypingSelectState_ImGuiTypingSelectState(void);\nCIMGUI_API void ImGuiTypingSelectState_destroy(ImGuiTypingSelectState* self);\nCIMGUI_API void ImGuiTypingSelectState_Clear(ImGuiTypingSelectState* self);\nCIMGUI_API ImGuiOldColumnData* ImGuiOldColumnData_ImGuiOldColumnData(void);\nCIMGUI_API void ImGuiOldColumnData_destroy(ImGuiOldColumnData* self);\nCIMGUI_API ImGuiOldColumns* ImGuiOldColumns_ImGuiOldColumns(void);\nCIMGUI_API void ImGuiOldColumns_destroy(ImGuiOldColumns* self);\nCIMGUI_API ImGuiBoxSelectState* ImGuiBoxSelectState_ImGuiBoxSelectState(void);\nCIMGUI_API void ImGuiBoxSelectState_destroy(ImGuiBoxSelectState* self);\nCIMGUI_API ImGuiMultiSelectTempData* ImGuiMultiSelectTempData_ImGuiMultiSelectTempData(void);\nCIMGUI_API void ImGuiMultiSelectTempData_destroy(ImGuiMultiSelectTempData* self);\nCIMGUI_API void ImGuiMultiSelectTempData_Clear(ImGuiMultiSelectTempData* self);\nCIMGUI_API void ImGuiMultiSelectTempData_ClearIO(ImGuiMultiSelectTempData* self);\nCIMGUI_API ImGuiMultiSelectState* ImGuiMultiSelectState_ImGuiMultiSelectState(void);\nCIMGUI_API void ImGuiMultiSelectState_destroy(ImGuiMultiSelectState* self);\nCIMGUI_API ImGuiDockNode* ImGuiDockNode_ImGuiDockNode(ImGuiID id);\nCIMGUI_API void ImGuiDockNode_destroy(ImGuiDockNode* self);\nCIMGUI_API bool ImGuiDockNode_IsRootNode(ImGuiDockNode* self);\nCIMGUI_API bool ImGuiDockNode_IsDockSpace(ImGuiDockNode* self);\nCIMGUI_API bool ImGuiDockNode_IsFloatingNode(ImGuiDockNode* self);\nCIMGUI_API bool ImGuiDockNode_IsCentralNode(ImGuiDockNode* self);\nCIMGUI_API bool ImGuiDockNode_IsHiddenTabBar(ImGuiDockNode* self);\nCIMGUI_API bool ImGuiDockNode_IsNoTabBar(ImGuiDockNode* self);\nCIMGUI_API bool ImGuiDockNode_IsSplitNode(ImGuiDockNode* self);\nCIMGUI_API bool ImGuiDockNode_IsLeafNode(ImGuiDockNode* self);\nCIMGUI_API bool ImGuiDockNode_IsEmpty(ImGuiDockNode* self);\nCIMGUI_API void ImGuiDockNode_Rect(ImRect *pOut,ImGuiDockNode* self);\nCIMGUI_API void ImGuiDockNode_SetLocalFlags(ImGuiDockNode* self,ImGuiDockNodeFlags flags);\nCIMGUI_API void ImGuiDockNode_UpdateMergedFlags(ImGuiDockNode* self);\nCIMGUI_API ImGuiDockContext* ImGuiDockContext_ImGuiDockContext(void);\nCIMGUI_API void ImGuiDockContext_destroy(ImGuiDockContext* self);\nCIMGUI_API ImGuiViewportP* ImGuiViewportP_ImGuiViewportP(void);\nCIMGUI_API void ImGuiViewportP_destroy(ImGuiViewportP* self);\nCIMGUI_API void ImGuiViewportP_ClearRequestFlags(ImGuiViewportP* self);\nCIMGUI_API void ImGuiViewportP_CalcWorkRectPos(ImVec2 *pOut,ImGuiViewportP* self,const ImVec2 inset_min);\nCIMGUI_API void ImGuiViewportP_CalcWorkRectSize(ImVec2 *pOut,ImGuiViewportP* self,const ImVec2 inset_min,const ImVec2 inset_max);\nCIMGUI_API void ImGuiViewportP_UpdateWorkRect(ImGuiViewportP* self);\nCIMGUI_API void ImGuiViewportP_GetMainRect(ImRect *pOut,ImGuiViewportP* self);\nCIMGUI_API void ImGuiViewportP_GetWorkRect(ImRect *pOut,ImGuiViewportP* self);\nCIMGUI_API void ImGuiViewportP_GetBuildWorkRect(ImRect *pOut,ImGuiViewportP* self);\nCIMGUI_API ImGuiWindowSettings* ImGuiWindowSettings_ImGuiWindowSettings(void);\nCIMGUI_API void ImGuiWindowSettings_destroy(ImGuiWindowSettings* self);\nCIMGUI_API char* ImGuiWindowSettings_GetName(ImGuiWindowSettings* self);\nCIMGUI_API ImGuiSettingsHandler* ImGuiSettingsHandler_ImGuiSettingsHandler(void);\nCIMGUI_API void ImGuiSettingsHandler_destroy(ImGuiSettingsHandler* self);\nCIMGUI_API ImGuiDebugAllocInfo* ImGuiDebugAllocInfo_ImGuiDebugAllocInfo(void);\nCIMGUI_API void ImGuiDebugAllocInfo_destroy(ImGuiDebugAllocInfo* self);\nCIMGUI_API ImGuiStackLevelInfo* ImGuiStackLevelInfo_ImGuiStackLevelInfo(void);\nCIMGUI_API void ImGuiStackLevelInfo_destroy(ImGuiStackLevelInfo* self);\nCIMGUI_API ImGuiIDStackTool* ImGuiIDStackTool_ImGuiIDStackTool(void);\nCIMGUI_API void ImGuiIDStackTool_destroy(ImGuiIDStackTool* self);\nCIMGUI_API ImGuiContextHook* ImGuiContextHook_ImGuiContextHook(void);\nCIMGUI_API void ImGuiContextHook_destroy(ImGuiContextHook* self);\nCIMGUI_API ImGuiContext* ImGuiContext_ImGuiContext(ImFontAtlas* shared_font_atlas);\nCIMGUI_API void ImGuiContext_destroy(ImGuiContext* self);\nCIMGUI_API ImGuiWindow* ImGuiWindow_ImGuiWindow(ImGuiContext* context,const char* name);\nCIMGUI_API void ImGuiWindow_destroy(ImGuiWindow* self);\nCIMGUI_API ImGuiID ImGuiWindow_GetID_Str(ImGuiWindow* self,const char* str,const char* str_end);\nCIMGUI_API ImGuiID ImGuiWindow_GetID_Ptr(ImGuiWindow* self,const void* ptr);\nCIMGUI_API ImGuiID ImGuiWindow_GetID_Int(ImGuiWindow* self,int n);\nCIMGUI_API ImGuiID ImGuiWindow_GetIDFromPos(ImGuiWindow* self,const ImVec2 p_abs);\nCIMGUI_API ImGuiID ImGuiWindow_GetIDFromRectangle(ImGuiWindow* self,const ImRect r_abs);\nCIMGUI_API void ImGuiWindow_Rect(ImRect *pOut,ImGuiWindow* self);\nCIMGUI_API void ImGuiWindow_TitleBarRect(ImRect *pOut,ImGuiWindow* self);\nCIMGUI_API void ImGuiWindow_MenuBarRect(ImRect *pOut,ImGuiWindow* self);\nCIMGUI_API ImGuiTabItem* ImGuiTabItem_ImGuiTabItem(void);\nCIMGUI_API void ImGuiTabItem_destroy(ImGuiTabItem* self);\nCIMGUI_API ImGuiTabBar* ImGuiTabBar_ImGuiTabBar(void);\nCIMGUI_API void ImGuiTabBar_destroy(ImGuiTabBar* self);\nCIMGUI_API ImGuiTableColumn* ImGuiTableColumn_ImGuiTableColumn(void);\nCIMGUI_API void ImGuiTableColumn_destroy(ImGuiTableColumn* self);\nCIMGUI_API ImGuiTableInstanceData* ImGuiTableInstanceData_ImGuiTableInstanceData(void);\nCIMGUI_API void ImGuiTableInstanceData_destroy(ImGuiTableInstanceData* self);\nCIMGUI_API ImGuiTable* ImGuiTable_ImGuiTable(void);\nCIMGUI_API void ImGuiTable_destroy(ImGuiTable* self);\nCIMGUI_API ImGuiTableTempData* ImGuiTableTempData_ImGuiTableTempData(void);\nCIMGUI_API void ImGuiTableTempData_destroy(ImGuiTableTempData* self);\nCIMGUI_API ImGuiTableColumnSettings* ImGuiTableColumnSettings_ImGuiTableColumnSettings(void);\nCIMGUI_API void ImGuiTableColumnSettings_destroy(ImGuiTableColumnSettings* self);\nCIMGUI_API ImGuiTableSettings* ImGuiTableSettings_ImGuiTableSettings(void);\nCIMGUI_API void ImGuiTableSettings_destroy(ImGuiTableSettings* self);\nCIMGUI_API ImGuiTableColumnSettings* ImGuiTableSettings_GetColumnSettings(ImGuiTableSettings* self);\nCIMGUI_API ImGuiIO* igGetIO_ContextPtr(ImGuiContext* ctx);\nCIMGUI_API ImGuiPlatformIO* igGetPlatformIO_ContextPtr(ImGuiContext* ctx);\nCIMGUI_API ImGuiWindow* igGetCurrentWindowRead(void);\nCIMGUI_API ImGuiWindow* igGetCurrentWindow(void);\nCIMGUI_API ImGuiWindow* igFindWindowByID(ImGuiID id);\nCIMGUI_API ImGuiWindow* igFindWindowByName(const char* name);\nCIMGUI_API void igUpdateWindowParentAndRootLinks(ImGuiWindow* window,ImGuiWindowFlags flags,ImGuiWindow* parent_window);\nCIMGUI_API void igUpdateWindowSkipRefresh(ImGuiWindow* window);\nCIMGUI_API void igCalcWindowNextAutoFitSize(ImVec2 *pOut,ImGuiWindow* window);\nCIMGUI_API bool igIsWindowChildOf(ImGuiWindow* window,ImGuiWindow* potential_parent,bool popup_hierarchy,bool dock_hierarchy);\nCIMGUI_API bool igIsWindowWithinBeginStackOf(ImGuiWindow* window,ImGuiWindow* potential_parent);\nCIMGUI_API bool igIsWindowAbove(ImGuiWindow* potential_above,ImGuiWindow* potential_below);\nCIMGUI_API bool igIsWindowNavFocusable(ImGuiWindow* window);\nCIMGUI_API void igSetWindowPos_WindowPtr(ImGuiWindow* window,const ImVec2 pos,ImGuiCond cond);\nCIMGUI_API void igSetWindowSize_WindowPtr(ImGuiWindow* window,const ImVec2 size,ImGuiCond cond);\nCIMGUI_API void igSetWindowCollapsed_WindowPtr(ImGuiWindow* window,bool collapsed,ImGuiCond cond);\nCIMGUI_API void igSetWindowHitTestHole(ImGuiWindow* window,const ImVec2 pos,const ImVec2 size);\nCIMGUI_API void igSetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window);\nCIMGUI_API void igSetWindowParentWindowForFocusRoute(ImGuiWindow* window,ImGuiWindow* parent_window);\nCIMGUI_API void igWindowRectAbsToRel(ImRect *pOut,ImGuiWindow* window,const ImRect r);\nCIMGUI_API void igWindowRectRelToAbs(ImRect *pOut,ImGuiWindow* window,const ImRect r);\nCIMGUI_API void igWindowPosAbsToRel(ImVec2 *pOut,ImGuiWindow* window,const ImVec2 p);\nCIMGUI_API void igWindowPosRelToAbs(ImVec2 *pOut,ImGuiWindow* window,const ImVec2 p);\nCIMGUI_API void igFocusWindow(ImGuiWindow* window,ImGuiFocusRequestFlags flags);\nCIMGUI_API void igFocusTopMostWindowUnderOne(ImGuiWindow* under_this_window,ImGuiWindow* ignore_window,ImGuiViewport* filter_viewport,ImGuiFocusRequestFlags flags);\nCIMGUI_API void igBringWindowToFocusFront(ImGuiWindow* window);\nCIMGUI_API void igBringWindowToDisplayFront(ImGuiWindow* window);\nCIMGUI_API void igBringWindowToDisplayBack(ImGuiWindow* window);\nCIMGUI_API void igBringWindowToDisplayBehind(ImGuiWindow* window,ImGuiWindow* above_window);\nCIMGUI_API int igFindWindowDisplayIndex(ImGuiWindow* window);\nCIMGUI_API ImGuiWindow* igFindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window);\nCIMGUI_API void igSetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags);\nCIMGUI_API void igRegisterUserTexture(ImTextureData* tex);\nCIMGUI_API void igUnregisterUserTexture(ImTextureData* tex);\nCIMGUI_API void igRegisterFontAtlas(ImFontAtlas* atlas);\nCIMGUI_API void igUnregisterFontAtlas(ImFontAtlas* atlas);\nCIMGUI_API void igSetCurrentFont(ImFont* font,float font_size_before_scaling,float font_size_after_scaling);\nCIMGUI_API void igUpdateCurrentFontSize(float restore_font_size_after_scaling);\nCIMGUI_API void igSetFontRasterizerDensity(float rasterizer_density);\nCIMGUI_API float igGetFontRasterizerDensity(void);\nCIMGUI_API float igGetRoundedFontSize(float size);\nCIMGUI_API ImFont* igGetDefaultFont(void);\nCIMGUI_API void igPushPasswordFont(void);\nCIMGUI_API void igPopPasswordFont(void);\nCIMGUI_API ImDrawList* igGetForegroundDrawList_WindowPtr(ImGuiWindow* window);\nCIMGUI_API void igAddDrawListToDrawDataEx(ImDrawData* draw_data,ImVector_ImDrawListPtr* out_list,ImDrawList* draw_list);\nCIMGUI_API void igInitialize(void);\nCIMGUI_API void igShutdown(void);\nCIMGUI_API void igUpdateInputEvents(bool trickle_fast_inputs);\nCIMGUI_API void igUpdateHoveredWindowAndCaptureFlags(const ImVec2 mouse_pos);\nCIMGUI_API void igFindHoveredWindowEx(const ImVec2 pos,bool find_first_and_in_any_viewport,ImGuiWindow** out_hovered_window,ImGuiWindow** out_hovered_window_under_moving_window);\nCIMGUI_API void igStartMouseMovingWindow(ImGuiWindow* window);\nCIMGUI_API void igStartMouseMovingWindowOrNode(ImGuiWindow* window,ImGuiDockNode* node,bool undock);\nCIMGUI_API void igUpdateMouseMovingWindowNewFrame(void);\nCIMGUI_API void igUpdateMouseMovingWindowEndFrame(void);\nCIMGUI_API ImGuiID igAddContextHook(ImGuiContext* context,const ImGuiContextHook* hook);\nCIMGUI_API void igRemoveContextHook(ImGuiContext* context,ImGuiID hook_to_remove);\nCIMGUI_API void igCallContextHooks(ImGuiContext* context,ImGuiContextHookType type);\nCIMGUI_API void igTranslateWindowsInViewport(ImGuiViewportP* viewport,const ImVec2 old_pos,const ImVec2 new_pos,const ImVec2 old_size,const ImVec2 new_size);\nCIMGUI_API void igScaleWindowsInViewport(ImGuiViewportP* viewport,float scale);\nCIMGUI_API void igDestroyPlatformWindow(ImGuiViewportP* viewport);\nCIMGUI_API void igSetWindowViewport(ImGuiWindow* window,ImGuiViewportP* viewport);\nCIMGUI_API void igSetCurrentViewport(ImGuiWindow* window,ImGuiViewportP* viewport);\nCIMGUI_API const ImGuiPlatformMonitor* igGetViewportPlatformMonitor(ImGuiViewport* viewport);\nCIMGUI_API ImGuiViewportP* igFindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos);\nCIMGUI_API void igMarkIniSettingsDirty_Nil(void);\nCIMGUI_API void igMarkIniSettingsDirty_WindowPtr(ImGuiWindow* window);\nCIMGUI_API void igClearIniSettings(void);\nCIMGUI_API void igAddSettingsHandler(const ImGuiSettingsHandler* handler);\nCIMGUI_API void igRemoveSettingsHandler(const char* type_name);\nCIMGUI_API ImGuiSettingsHandler* igFindSettingsHandler(const char* type_name);\nCIMGUI_API ImGuiWindowSettings* igCreateNewWindowSettings(const char* name);\nCIMGUI_API ImGuiWindowSettings* igFindWindowSettingsByID(ImGuiID id);\nCIMGUI_API ImGuiWindowSettings* igFindWindowSettingsByWindow(ImGuiWindow* window);\nCIMGUI_API void igClearWindowSettings(const char* name);\nCIMGUI_API void igLocalizeRegisterEntries(const ImGuiLocEntry* entries,int count);\nCIMGUI_API const char* igLocalizeGetMsg(ImGuiLocKey key);\nCIMGUI_API void igSetScrollX_WindowPtr(ImGuiWindow* window,float scroll_x);\nCIMGUI_API void igSetScrollY_WindowPtr(ImGuiWindow* window,float scroll_y);\nCIMGUI_API void igSetScrollFromPosX_WindowPtr(ImGuiWindow* window,float local_x,float center_x_ratio);\nCIMGUI_API void igSetScrollFromPosY_WindowPtr(ImGuiWindow* window,float local_y,float center_y_ratio);\nCIMGUI_API void igScrollToItem(ImGuiScrollFlags flags);\nCIMGUI_API void igScrollToRect(ImGuiWindow* window,const ImRect rect,ImGuiScrollFlags flags);\nCIMGUI_API void igScrollToRectEx(ImVec2 *pOut,ImGuiWindow* window,const ImRect rect,ImGuiScrollFlags flags);\nCIMGUI_API void igScrollToBringRectIntoView(ImGuiWindow* window,const ImRect rect);\nCIMGUI_API ImGuiItemStatusFlags igGetItemStatusFlags(void);\nCIMGUI_API ImGuiItemFlags igGetItemFlags(void);\nCIMGUI_API ImGuiID igGetActiveID(void);\nCIMGUI_API ImGuiID igGetFocusID(void);\nCIMGUI_API void igSetActiveID(ImGuiID id,ImGuiWindow* window);\nCIMGUI_API void igSetFocusID(ImGuiID id,ImGuiWindow* window);\nCIMGUI_API void igClearActiveID(void);\nCIMGUI_API ImGuiID igGetHoveredID(void);\nCIMGUI_API void igSetHoveredID(ImGuiID id);\nCIMGUI_API void igKeepAliveID(ImGuiID id);\nCIMGUI_API void igMarkItemEdited(ImGuiID id);\nCIMGUI_API void igPushOverrideID(ImGuiID id);\nCIMGUI_API ImGuiID igGetIDWithSeed_Str(const char* str_id_begin,const char* str_id_end,ImGuiID seed);\nCIMGUI_API ImGuiID igGetIDWithSeed_Int(int n,ImGuiID seed);\nCIMGUI_API void igItemSize_Vec2(const ImVec2 size,float text_baseline_y);\nCIMGUI_API void igItemSize_Rect(const ImRect bb,float text_baseline_y);\nCIMGUI_API bool igItemAdd(const ImRect bb,ImGuiID id,const ImRect* nav_bb,ImGuiItemFlags extra_flags);\nCIMGUI_API bool igItemHoverable(const ImRect bb,ImGuiID id,ImGuiItemFlags item_flags);\nCIMGUI_API bool igIsWindowContentHoverable(ImGuiWindow* window,ImGuiHoveredFlags flags);\nCIMGUI_API bool igIsClippedEx(const ImRect bb,ImGuiID id);\nCIMGUI_API void igSetLastItemData(ImGuiID item_id,ImGuiItemFlags item_flags,ImGuiItemStatusFlags status_flags,const ImRect item_rect);\nCIMGUI_API void igCalcItemSize(ImVec2 *pOut,ImVec2 size,float default_w,float default_h);\nCIMGUI_API float igCalcWrapWidthForPos(const ImVec2 pos,float wrap_pos_x);\nCIMGUI_API void igPushMultiItemsWidths(int components,float width_full);\nCIMGUI_API void igShrinkWidths(ImGuiShrinkWidthItem* items,int count,float width_excess,float width_min);\nCIMGUI_API const ImGuiStyleVarInfo* igGetStyleVarInfo(ImGuiStyleVar idx);\nCIMGUI_API void igBeginDisabledOverrideReenable(void);\nCIMGUI_API void igEndDisabledOverrideReenable(void);\nCIMGUI_API void igLogBegin(ImGuiLogFlags flags,int auto_open_depth);\nCIMGUI_API void igLogToBuffer(int auto_open_depth);\nCIMGUI_API void igLogRenderedText(const ImVec2* ref_pos,const char* text,const char* text_end);\nCIMGUI_API void igLogSetNextTextDecoration(const char* prefix,const char* suffix);\nCIMGUI_API bool igBeginChildEx(const char* name,ImGuiID id,const ImVec2 size_arg,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags);\nCIMGUI_API bool igBeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_window_flags);\nCIMGUI_API bool igBeginPopupMenuEx(ImGuiID id,const char* label,ImGuiWindowFlags extra_window_flags);\nCIMGUI_API void igOpenPopupEx(ImGuiID id,ImGuiPopupFlags popup_flags);\nCIMGUI_API void igClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup);\nCIMGUI_API void igClosePopupsOverWindow(ImGuiWindow* ref_window,bool restore_focus_to_window_under_popup);\nCIMGUI_API void igClosePopupsExceptModals(void);\nCIMGUI_API bool igIsPopupOpen_ID(ImGuiID id,ImGuiPopupFlags popup_flags);\nCIMGUI_API void igGetPopupAllowedExtentRect(ImRect *pOut,ImGuiWindow* window);\nCIMGUI_API ImGuiWindow* igGetTopMostPopupModal(void);\nCIMGUI_API ImGuiWindow* igGetTopMostAndVisiblePopupModal(void);\nCIMGUI_API ImGuiWindow* igFindBlockingModal(ImGuiWindow* window);\nCIMGUI_API void igFindBestWindowPosForPopup(ImVec2 *pOut,ImGuiWindow* window);\nCIMGUI_API void igFindBestWindowPosForPopupEx(ImVec2 *pOut,const ImVec2 ref_pos,const ImVec2 size,ImGuiDir* last_dir,const ImRect r_outer,const ImRect r_avoid,ImGuiPopupPositionPolicy policy);\nCIMGUI_API bool igBeginTooltipEx(ImGuiTooltipFlags tooltip_flags,ImGuiWindowFlags extra_window_flags);\nCIMGUI_API bool igBeginTooltipHidden(void);\nCIMGUI_API bool igBeginViewportSideBar(const char* name,ImGuiViewport* viewport,ImGuiDir dir,float size,ImGuiWindowFlags window_flags);\nCIMGUI_API bool igBeginMenuEx(const char* label,const char* icon,bool enabled);\nCIMGUI_API bool igMenuItemEx(const char* label,const char* icon,const char* shortcut,bool selected,bool enabled);\nCIMGUI_API bool igBeginComboPopup(ImGuiID popup_id,const ImRect bb,ImGuiComboFlags flags);\nCIMGUI_API bool igBeginComboPreview(void);\nCIMGUI_API void igEndComboPreview(void);\nCIMGUI_API void igNavInitWindow(ImGuiWindow* window,bool force_reinit);\nCIMGUI_API void igNavInitRequestApplyResult(void);\nCIMGUI_API bool igNavMoveRequestButNoResultYet(void);\nCIMGUI_API void igNavMoveRequestSubmit(ImGuiDir move_dir,ImGuiDir clip_dir,ImGuiNavMoveFlags move_flags,ImGuiScrollFlags scroll_flags);\nCIMGUI_API void igNavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,ImGuiNavMoveFlags move_flags,ImGuiScrollFlags scroll_flags);\nCIMGUI_API void igNavMoveRequestResolveWithLastItem(ImGuiNavItemData* result);\nCIMGUI_API void igNavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result,const ImGuiTreeNodeStackData* tree_node_data);\nCIMGUI_API void igNavMoveRequestCancel(void);\nCIMGUI_API void igNavMoveRequestApplyResult(void);\nCIMGUI_API void igNavMoveRequestTryWrapping(ImGuiWindow* window,ImGuiNavMoveFlags move_flags);\nCIMGUI_API void igNavHighlightActivated(ImGuiID id);\nCIMGUI_API void igNavClearPreferredPosForAxis(ImGuiAxis axis);\nCIMGUI_API void igSetNavCursorVisibleAfterMove(void);\nCIMGUI_API void igNavUpdateCurrentWindowIsScrollPushableX(void);\nCIMGUI_API void igSetNavWindow(ImGuiWindow* window);\nCIMGUI_API void igSetNavID(ImGuiID id,ImGuiNavLayer nav_layer,ImGuiID focus_scope_id,const ImRect rect_rel);\nCIMGUI_API void igSetNavFocusScope(ImGuiID focus_scope_id);\nCIMGUI_API void igFocusItem(void);\nCIMGUI_API void igActivateItemByID(ImGuiID id);\nCIMGUI_API bool igIsNamedKey(ImGuiKey key);\nCIMGUI_API bool igIsNamedKeyOrMod(ImGuiKey key);\nCIMGUI_API bool igIsLegacyKey(ImGuiKey key);\nCIMGUI_API bool igIsKeyboardKey(ImGuiKey key);\nCIMGUI_API bool igIsGamepadKey(ImGuiKey key);\nCIMGUI_API bool igIsMouseKey(ImGuiKey key);\nCIMGUI_API bool igIsAliasKey(ImGuiKey key);\nCIMGUI_API bool igIsLRModKey(ImGuiKey key);\nCIMGUI_API ImGuiKeyChord igFixupKeyChord(ImGuiKeyChord key_chord);\nCIMGUI_API ImGuiKey igConvertSingleModFlagToKey(ImGuiKey key);\nCIMGUI_API ImGuiKeyData* igGetKeyData_ContextPtr(ImGuiContext* ctx,ImGuiKey key);\nCIMGUI_API ImGuiKeyData* igGetKeyData_Key(ImGuiKey key);\nCIMGUI_API const char* igGetKeyChordName(ImGuiKeyChord key_chord);\nCIMGUI_API ImGuiKey igMouseButtonToKey(ImGuiMouseButton button);\nCIMGUI_API bool igIsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold);\nCIMGUI_API void igGetKeyMagnitude2d(ImVec2 *pOut,ImGuiKey key_left,ImGuiKey key_right,ImGuiKey key_up,ImGuiKey key_down);\nCIMGUI_API float igGetNavTweakPressedAmount(ImGuiAxis axis);\nCIMGUI_API int igCalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate);\nCIMGUI_API void igGetTypematicRepeatRate(ImGuiInputFlags flags,float* repeat_delay,float* repeat_rate);\nCIMGUI_API void igTeleportMousePos(const ImVec2 pos);\nCIMGUI_API void igSetActiveIdUsingAllKeyboardKeys(void);\nCIMGUI_API bool igIsActiveIdUsingNavDir(ImGuiDir dir);\nCIMGUI_API ImGuiID igGetKeyOwner(ImGuiKey key);\nCIMGUI_API void igSetKeyOwner(ImGuiKey key,ImGuiID owner_id,ImGuiInputFlags flags);\nCIMGUI_API void igSetKeyOwnersForKeyChord(ImGuiKeyChord key,ImGuiID owner_id,ImGuiInputFlags flags);\nCIMGUI_API void igSetItemKeyOwner_InputFlags(ImGuiKey key,ImGuiInputFlags flags);\nCIMGUI_API bool igTestKeyOwner(ImGuiKey key,ImGuiID owner_id);\nCIMGUI_API ImGuiKeyOwnerData* igGetKeyOwnerData(ImGuiContext* ctx,ImGuiKey key);\nCIMGUI_API bool igIsKeyDown_ID(ImGuiKey key,ImGuiID owner_id);\nCIMGUI_API bool igIsKeyPressed_InputFlags(ImGuiKey key,ImGuiInputFlags flags,ImGuiID owner_id);\nCIMGUI_API bool igIsKeyReleased_ID(ImGuiKey key,ImGuiID owner_id);\nCIMGUI_API bool igIsKeyChordPressed_InputFlags(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id);\nCIMGUI_API bool igIsMouseDown_ID(ImGuiMouseButton button,ImGuiID owner_id);\nCIMGUI_API bool igIsMouseClicked_InputFlags(ImGuiMouseButton button,ImGuiInputFlags flags,ImGuiID owner_id);\nCIMGUI_API bool igIsMouseReleased_ID(ImGuiMouseButton button,ImGuiID owner_id);\nCIMGUI_API bool igIsMouseDoubleClicked_ID(ImGuiMouseButton button,ImGuiID owner_id);\nCIMGUI_API bool igShortcut_ID(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id);\nCIMGUI_API bool igSetShortcutRouting(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id);\nCIMGUI_API bool igTestShortcutRouting(ImGuiKeyChord key_chord,ImGuiID owner_id);\nCIMGUI_API ImGuiKeyRoutingData* igGetShortcutRoutingData(ImGuiKeyChord key_chord);\nCIMGUI_API void igDockContextInitialize(ImGuiContext* ctx);\nCIMGUI_API void igDockContextShutdown(ImGuiContext* ctx);\nCIMGUI_API void igDockContextClearNodes(ImGuiContext* ctx,ImGuiID root_id,bool clear_settings_refs);\nCIMGUI_API void igDockContextRebuildNodes(ImGuiContext* ctx);\nCIMGUI_API void igDockContextNewFrameUpdateUndocking(ImGuiContext* ctx);\nCIMGUI_API void igDockContextNewFrameUpdateDocking(ImGuiContext* ctx);\nCIMGUI_API void igDockContextEndFrame(ImGuiContext* ctx);\nCIMGUI_API ImGuiID igDockContextGenNodeID(ImGuiContext* ctx);\nCIMGUI_API void igDockContextQueueDock(ImGuiContext* ctx,ImGuiWindow* target,ImGuiDockNode* target_node,ImGuiWindow* payload,ImGuiDir split_dir,float split_ratio,bool split_outer);\nCIMGUI_API void igDockContextQueueUndockWindow(ImGuiContext* ctx,ImGuiWindow* window);\nCIMGUI_API void igDockContextQueueUndockNode(ImGuiContext* ctx,ImGuiDockNode* node);\nCIMGUI_API void igDockContextProcessUndockWindow(ImGuiContext* ctx,ImGuiWindow* window,bool clear_persistent_docking_ref);\nCIMGUI_API void igDockContextProcessUndockNode(ImGuiContext* ctx,ImGuiDockNode* node);\nCIMGUI_API bool igDockContextCalcDropPosForDocking(ImGuiWindow* target,ImGuiDockNode* target_node,ImGuiWindow* payload_window,ImGuiDockNode* payload_node,ImGuiDir split_dir,bool split_outer,ImVec2* out_pos);\nCIMGUI_API ImGuiDockNode* igDockContextFindNodeByID(ImGuiContext* ctx,ImGuiID id);\nCIMGUI_API void igDockNodeWindowMenuHandler_Default(ImGuiContext* ctx,ImGuiDockNode* node,ImGuiTabBar* tab_bar);\nCIMGUI_API bool igDockNodeBeginAmendTabBar(ImGuiDockNode* node);\nCIMGUI_API void igDockNodeEndAmendTabBar(void);\nCIMGUI_API ImGuiDockNode* igDockNodeGetRootNode(ImGuiDockNode* node);\nCIMGUI_API bool igDockNodeIsInHierarchyOf(ImGuiDockNode* node,ImGuiDockNode* parent);\nCIMGUI_API int igDockNodeGetDepth(const ImGuiDockNode* node);\nCIMGUI_API ImGuiID igDockNodeGetWindowMenuButtonId(const ImGuiDockNode* node);\nCIMGUI_API ImGuiDockNode* igGetWindowDockNode(void);\nCIMGUI_API bool igGetWindowAlwaysWantOwnTabBar(ImGuiWindow* window);\nCIMGUI_API void igBeginDocked(ImGuiWindow* window,bool* p_open);\nCIMGUI_API void igBeginDockableDragDropSource(ImGuiWindow* window);\nCIMGUI_API void igBeginDockableDragDropTarget(ImGuiWindow* window);\nCIMGUI_API void igSetWindowDock(ImGuiWindow* window,ImGuiID dock_id,ImGuiCond cond);\nCIMGUI_API void igDockBuilderDockWindow(const char* window_name,ImGuiID node_id);\nCIMGUI_API ImGuiDockNode* igDockBuilderGetNode(ImGuiID node_id);\nCIMGUI_API ImGuiDockNode* igDockBuilderGetCentralNode(ImGuiID node_id);\nCIMGUI_API ImGuiID igDockBuilderAddNode(ImGuiID node_id,ImGuiDockNodeFlags flags);\nCIMGUI_API void igDockBuilderRemoveNode(ImGuiID node_id);\nCIMGUI_API void igDockBuilderRemoveNodeDockedWindows(ImGuiID node_id,bool clear_settings_refs);\nCIMGUI_API void igDockBuilderRemoveNodeChildNodes(ImGuiID node_id);\nCIMGUI_API void igDockBuilderSetNodePos(ImGuiID node_id,ImVec2 pos);\nCIMGUI_API void igDockBuilderSetNodeSize(ImGuiID node_id,ImVec2 size);\nCIMGUI_API ImGuiID igDockBuilderSplitNode(ImGuiID node_id,ImGuiDir split_dir,float size_ratio_for_node_at_dir,ImGuiID* out_id_at_dir,ImGuiID* out_id_at_opposite_dir);\nCIMGUI_API void igDockBuilderCopyDockSpace(ImGuiID src_dockspace_id,ImGuiID dst_dockspace_id,ImVector_const_charPtr* in_window_remap_pairs);\nCIMGUI_API void igDockBuilderCopyNode(ImGuiID src_node_id,ImGuiID dst_node_id,ImVector_ImGuiID* out_node_remap_pairs);\nCIMGUI_API void igDockBuilderCopyWindowSettings(const char* src_name,const char* dst_name);\nCIMGUI_API void igDockBuilderFinish(ImGuiID node_id);\nCIMGUI_API void igPushFocusScope(ImGuiID id);\nCIMGUI_API void igPopFocusScope(void);\nCIMGUI_API ImGuiID igGetCurrentFocusScope(void);\nCIMGUI_API bool igIsDragDropActive(void);\nCIMGUI_API bool igBeginDragDropTargetCustom(const ImRect bb,ImGuiID id);\nCIMGUI_API void igClearDragDrop(void);\nCIMGUI_API bool igIsDragDropPayloadBeingAccepted(void);\nCIMGUI_API void igRenderDragDropTargetRectEx(ImDrawList *draw_list, const ImRect bb);\nCIMGUI_API void igRenderDragDropTargetRectForItem(const ImRect bb);\nCIMGUI_API ImGuiTypingSelectRequest* igGetTypingSelectRequest(ImGuiTypingSelectFlags flags);\nCIMGUI_API int igTypingSelectFindMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data,int nav_item_idx);\nCIMGUI_API int igTypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data,int nav_item_idx);\nCIMGUI_API int igTypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data);\nCIMGUI_API bool igBeginBoxSelect(const ImRect scope_rect,ImGuiWindow* window,ImGuiID box_select_id,ImGuiMultiSelectFlags ms_flags);\nCIMGUI_API void igEndBoxSelect(const ImRect scope_rect,ImGuiMultiSelectFlags ms_flags);\nCIMGUI_API void igMultiSelectItemHeader(ImGuiID id,bool* p_selected,ImGuiButtonFlags* p_button_flags);\nCIMGUI_API void igMultiSelectItemFooter(ImGuiID id,bool* p_selected,bool* p_pressed);\nCIMGUI_API void igMultiSelectAddSetAll(ImGuiMultiSelectTempData* ms,bool selected);\nCIMGUI_API void igMultiSelectAddSetRange(ImGuiMultiSelectTempData* ms,bool selected,int range_dir,ImGuiSelectionUserData first_item,ImGuiSelectionUserData last_item);\nCIMGUI_API ImGuiBoxSelectState* igGetBoxSelectState(ImGuiID id);\nCIMGUI_API ImGuiMultiSelectState* igGetMultiSelectState(ImGuiID id);\nCIMGUI_API void igSetWindowClipRectBeforeSetChannel(ImGuiWindow* window,const ImRect clip_rect);\nCIMGUI_API void igBeginColumns(const char* str_id,int count,ImGuiOldColumnFlags flags);\nCIMGUI_API void igEndColumns(void);\nCIMGUI_API void igPushColumnClipRect(int column_index);\nCIMGUI_API void igPushColumnsBackground(void);\nCIMGUI_API void igPopColumnsBackground(void);\nCIMGUI_API ImGuiID igGetColumnsID(const char* str_id,int count);\nCIMGUI_API ImGuiOldColumns* igFindOrCreateColumns(ImGuiWindow* window,ImGuiID id);\nCIMGUI_API float igGetColumnOffsetFromNorm(const ImGuiOldColumns* columns,float offset_norm);\nCIMGUI_API float igGetColumnNormFromOffset(const ImGuiOldColumns* columns,float offset);\nCIMGUI_API void igTableOpenContextMenu(int column_n);\nCIMGUI_API void igTableSetColumnWidth(int column_n,float width);\nCIMGUI_API void igTableSetColumnSortDirection(int column_n,ImGuiSortDirection sort_direction,bool append_to_sort_specs);\nCIMGUI_API int igTableGetHoveredRow(void);\nCIMGUI_API float igTableGetHeaderRowHeight(void);\nCIMGUI_API float igTableGetHeaderAngledMaxLabelWidth(void);\nCIMGUI_API void igTablePushBackgroundChannel(void);\nCIMGUI_API void igTablePopBackgroundChannel(void);\nCIMGUI_API void igTablePushColumnChannel(int column_n);\nCIMGUI_API void igTablePopColumnChannel(void);\nCIMGUI_API void igTableAngledHeadersRowEx(ImGuiID row_id,float angle,float max_label_width,const ImGuiTableHeaderData* data,int data_count);\nCIMGUI_API ImGuiTable* igGetCurrentTable(void);\nCIMGUI_API ImGuiTable* igTableFindByID(ImGuiID id);\nCIMGUI_API bool igBeginTableEx(const char* name,ImGuiID id,int columns_count,ImGuiTableFlags flags,const ImVec2 outer_size,float inner_width);\nCIMGUI_API void igTableBeginInitMemory(ImGuiTable* table,int columns_count);\nCIMGUI_API void igTableBeginApplyRequests(ImGuiTable* table);\nCIMGUI_API void igTableSetupDrawChannels(ImGuiTable* table);\nCIMGUI_API void igTableUpdateLayout(ImGuiTable* table);\nCIMGUI_API void igTableUpdateBorders(ImGuiTable* table);\nCIMGUI_API void igTableUpdateColumnsWeightFromWidth(ImGuiTable* table);\nCIMGUI_API void igTableDrawBorders(ImGuiTable* table);\nCIMGUI_API void igTableDrawDefaultContextMenu(ImGuiTable* table,ImGuiTableFlags flags_for_section_to_display);\nCIMGUI_API bool igTableBeginContextMenuPopup(ImGuiTable* table);\nCIMGUI_API void igTableMergeDrawChannels(ImGuiTable* table);\nCIMGUI_API ImGuiTableInstanceData* igTableGetInstanceData(ImGuiTable* table,int instance_no);\nCIMGUI_API ImGuiID igTableGetInstanceID(ImGuiTable* table,int instance_no);\nCIMGUI_API void igTableSortSpecsSanitize(ImGuiTable* table);\nCIMGUI_API void igTableSortSpecsBuild(ImGuiTable* table);\nCIMGUI_API ImGuiSortDirection igTableGetColumnNextSortDirection(ImGuiTableColumn* column);\nCIMGUI_API void igTableFixColumnSortDirection(ImGuiTable* table,ImGuiTableColumn* column);\nCIMGUI_API float igTableGetColumnWidthAuto(ImGuiTable* table,ImGuiTableColumn* column);\nCIMGUI_API void igTableBeginRow(ImGuiTable* table);\nCIMGUI_API void igTableEndRow(ImGuiTable* table);\nCIMGUI_API void igTableBeginCell(ImGuiTable* table,int column_n);\nCIMGUI_API void igTableEndCell(ImGuiTable* table);\nCIMGUI_API void igTableGetCellBgRect(ImRect *pOut,const ImGuiTable* table,int column_n);\nCIMGUI_API const char* igTableGetColumnName_TablePtr(const ImGuiTable* table,int column_n);\nCIMGUI_API ImGuiID igTableGetColumnResizeID(ImGuiTable* table,int column_n,int instance_no);\nCIMGUI_API float igTableCalcMaxColumnWidth(const ImGuiTable* table,int column_n);\nCIMGUI_API void igTableSetColumnWidthAutoSingle(ImGuiTable* table,int column_n);\nCIMGUI_API void igTableSetColumnWidthAutoAll(ImGuiTable* table);\nCIMGUI_API void igTableRemove(ImGuiTable* table);\nCIMGUI_API void igTableGcCompactTransientBuffers_TablePtr(ImGuiTable* table);\nCIMGUI_API void igTableGcCompactTransientBuffers_TableTempDataPtr(ImGuiTableTempData* table);\nCIMGUI_API void igTableGcCompactSettings(void);\nCIMGUI_API void igTableLoadSettings(ImGuiTable* table);\nCIMGUI_API void igTableSaveSettings(ImGuiTable* table);\nCIMGUI_API void igTableResetSettings(ImGuiTable* table);\nCIMGUI_API ImGuiTableSettings* igTableGetBoundSettings(ImGuiTable* table);\nCIMGUI_API void igTableSettingsAddSettingsHandler(void);\nCIMGUI_API ImGuiTableSettings* igTableSettingsCreate(ImGuiID id,int columns_count);\nCIMGUI_API ImGuiTableSettings* igTableSettingsFindByID(ImGuiID id);\nCIMGUI_API ImGuiTabBar* igGetCurrentTabBar(void);\nCIMGUI_API bool igBeginTabBarEx(ImGuiTabBar* tab_bar,const ImRect bb,ImGuiTabBarFlags flags);\nCIMGUI_API ImGuiTabItem* igTabBarFindTabByID(ImGuiTabBar* tab_bar,ImGuiID tab_id);\nCIMGUI_API ImGuiTabItem* igTabBarFindTabByOrder(ImGuiTabBar* tab_bar,int order);\nCIMGUI_API ImGuiTabItem* igTabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar);\nCIMGUI_API ImGuiTabItem* igTabBarGetCurrentTab(ImGuiTabBar* tab_bar);\nCIMGUI_API int igTabBarGetTabOrder(ImGuiTabBar* tab_bar,ImGuiTabItem* tab);\nCIMGUI_API const char* igTabBarGetTabName(ImGuiTabBar* tab_bar,ImGuiTabItem* tab);\nCIMGUI_API void igTabBarAddTab(ImGuiTabBar* tab_bar,ImGuiTabItemFlags tab_flags,ImGuiWindow* window);\nCIMGUI_API void igTabBarRemoveTab(ImGuiTabBar* tab_bar,ImGuiID tab_id);\nCIMGUI_API void igTabBarCloseTab(ImGuiTabBar* tab_bar,ImGuiTabItem* tab);\nCIMGUI_API void igTabBarQueueFocus_TabItemPtr(ImGuiTabBar* tab_bar,ImGuiTabItem* tab);\nCIMGUI_API void igTabBarQueueFocus_Str(ImGuiTabBar* tab_bar,const char* tab_name);\nCIMGUI_API void igTabBarQueueReorder(ImGuiTabBar* tab_bar,ImGuiTabItem* tab,int offset);\nCIMGUI_API void igTabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar,ImGuiTabItem* tab,ImVec2 mouse_pos);\nCIMGUI_API bool igTabBarProcessReorder(ImGuiTabBar* tab_bar);\nCIMGUI_API bool igTabItemEx(ImGuiTabBar* tab_bar,const char* label,bool* p_open,ImGuiTabItemFlags flags,ImGuiWindow* docked_window);\nCIMGUI_API void igTabItemSpacing(const char* str_id,ImGuiTabItemFlags flags,float width);\nCIMGUI_API void igTabItemCalcSize_Str(ImVec2 *pOut,const char* label,bool has_close_button_or_unsaved_marker);\nCIMGUI_API void igTabItemCalcSize_WindowPtr(ImVec2 *pOut,ImGuiWindow* window);\nCIMGUI_API void igTabItemBackground(ImDrawList* draw_list,const ImRect bb,ImGuiTabItemFlags flags,ImU32 col);\nCIMGUI_API void igTabItemLabelAndCloseButton(ImDrawList* draw_list,const ImRect bb,ImGuiTabItemFlags flags,ImVec2 frame_padding,const char* label,ImGuiID tab_id,ImGuiID close_button_id,bool is_contents_visible,bool* out_just_closed,bool* out_text_clipped);\nCIMGUI_API void igRenderText(ImVec2 pos,const char* text,const char* text_end,bool hide_text_after_hash);\nCIMGUI_API void igRenderTextWrapped(ImVec2 pos,const char* text,const char* text_end,float wrap_width);\nCIMGUI_API void igRenderTextClipped(const ImVec2 pos_min,const ImVec2 pos_max,const char* text,const char* text_end,const ImVec2* text_size_if_known,const ImVec2 align,const ImRect* clip_rect);\nCIMGUI_API void igRenderTextClippedEx(ImDrawList* draw_list,const ImVec2 pos_min,const ImVec2 pos_max,const char* text,const char* text_end,const ImVec2* text_size_if_known,const ImVec2 align,const ImRect* clip_rect);\nCIMGUI_API void igRenderTextEllipsis(ImDrawList* draw_list,const ImVec2 pos_min,const ImVec2 pos_max,float ellipsis_max_x,const char* text,const char* text_end,const ImVec2* text_size_if_known);\nCIMGUI_API void igRenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool borders,float rounding);\nCIMGUI_API void igRenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding);\nCIMGUI_API void igRenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list,ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,float grid_step,ImVec2 grid_off,float rounding,ImDrawFlags flags);\nCIMGUI_API void igRenderNavCursor(const ImRect bb,ImGuiID id,ImGuiNavRenderCursorFlags flags);\nCIMGUI_API const char* igFindRenderedTextEnd(const char* text,const char* text_end);\nCIMGUI_API void igRenderMouseCursor(ImVec2 pos,float scale,ImGuiMouseCursor mouse_cursor,ImU32 col_fill,ImU32 col_border,ImU32 col_shadow);\nCIMGUI_API void igRenderArrow(ImDrawList* draw_list,ImVec2 pos,ImU32 col,ImGuiDir dir,float scale);\nCIMGUI_API void igRenderBullet(ImDrawList* draw_list,ImVec2 pos,ImU32 col);\nCIMGUI_API void igRenderCheckMark(ImDrawList* draw_list,ImVec2 pos,ImU32 col,float sz);\nCIMGUI_API void igRenderArrowPointingAt(ImDrawList* draw_list,ImVec2 pos,ImVec2 half_sz,ImGuiDir direction,ImU32 col);\nCIMGUI_API void igRenderArrowDockMenu(ImDrawList* draw_list,ImVec2 p_min,float sz,ImU32 col);\nCIMGUI_API void igRenderRectFilledWithHole(ImDrawList* draw_list,const ImRect outer,const ImRect inner,ImU32 col,float rounding);\nCIMGUI_API ImDrawFlags igCalcRoundingFlagsForRectInRect(const ImRect r_in,const ImRect r_outer,float threshold);\nCIMGUI_API void igTextEx(const char* text,const char* text_end,ImGuiTextFlags flags);\nCIMGUI_API void igTextAligned(float align_x,float size_x,const char* fmt,...);\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igTextAligned0(float align_x,float size_x,const char* fmt);\n#endif\nCIMGUI_API void igTextAlignedV(float align_x,float size_x,const char* fmt,va_list args);\nCIMGUI_API bool igButtonEx(const char* label,const ImVec2 size_arg,ImGuiButtonFlags flags);\nCIMGUI_API bool igArrowButtonEx(const char* str_id,ImGuiDir dir,ImVec2 size_arg,ImGuiButtonFlags flags);\nCIMGUI_API bool igImageButtonEx(ImGuiID id,ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col,ImGuiButtonFlags flags);\nCIMGUI_API void igSeparatorEx(ImGuiSeparatorFlags flags,float thickness);\nCIMGUI_API void igSeparatorTextEx(ImGuiID id,const char* label,const char* label_end,float extra_width);\nCIMGUI_API bool igCheckboxFlags_S64Ptr(const char* label,ImS64* flags,ImS64 flags_value);\nCIMGUI_API bool igCheckboxFlags_U64Ptr(const char* label,ImU64* flags,ImU64 flags_value);\nCIMGUI_API bool igCloseButton(ImGuiID id,const ImVec2 pos);\nCIMGUI_API bool igCollapseButton(ImGuiID id,const ImVec2 pos,ImGuiDockNode* dock_node);\nCIMGUI_API void igScrollbar(ImGuiAxis axis);\nCIMGUI_API bool igScrollbarEx(const ImRect bb,ImGuiID id,ImGuiAxis axis,ImS64* p_scroll_v,ImS64 avail_v,ImS64 contents_v,ImDrawFlags draw_rounding_flags);\nCIMGUI_API void igGetWindowScrollbarRect(ImRect *pOut,ImGuiWindow* window,ImGuiAxis axis);\nCIMGUI_API ImGuiID igGetWindowScrollbarID(ImGuiWindow* window,ImGuiAxis axis);\nCIMGUI_API ImGuiID igGetWindowResizeCornerID(ImGuiWindow* window,int n);\nCIMGUI_API ImGuiID igGetWindowResizeBorderID(ImGuiWindow* window,ImGuiDir dir);\nCIMGUI_API bool igButtonBehavior(const ImRect bb,ImGuiID id,bool* out_hovered,bool* out_held,ImGuiButtonFlags flags);\nCIMGUI_API bool igDragBehavior(ImGuiID id,ImGuiDataType data_type,void* p_v,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags);\nCIMGUI_API bool igSliderBehavior(const ImRect bb,ImGuiID id,ImGuiDataType data_type,void* p_v,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags,ImRect* out_grab_bb);\nCIMGUI_API bool igSplitterBehavior(const ImRect bb,ImGuiID id,ImGuiAxis axis,float* size1,float* size2,float min_size1,float min_size2,float hover_extend,float hover_visibility_delay,ImU32 bg_col);\nCIMGUI_API bool igTreeNodeBehavior(ImGuiID id,ImGuiTreeNodeFlags flags,const char* label,const char* label_end);\nCIMGUI_API void igTreeNodeDrawLineToChildNode(const ImVec2 target_pos);\nCIMGUI_API void igTreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data);\nCIMGUI_API void igTreePushOverrideID(ImGuiID id);\nCIMGUI_API bool igTreeNodeGetOpen(ImGuiID storage_id);\nCIMGUI_API void igTreeNodeSetOpen(ImGuiID storage_id,bool open);\nCIMGUI_API bool igTreeNodeUpdateNextOpen(ImGuiID storage_id,ImGuiTreeNodeFlags flags);\nCIMGUI_API const ImGuiDataTypeInfo* igDataTypeGetInfo(ImGuiDataType data_type);\nCIMGUI_API int igDataTypeFormatString(char* buf,int buf_size,ImGuiDataType data_type,const void* p_data,const char* format);\nCIMGUI_API void igDataTypeApplyOp(ImGuiDataType data_type,int op,void* output,const void* arg_1,const void* arg_2);\nCIMGUI_API bool igDataTypeApplyFromText(const char* buf,ImGuiDataType data_type,void* p_data,const char* format,void* p_data_when_empty);\nCIMGUI_API int igDataTypeCompare(ImGuiDataType data_type,const void* arg_1,const void* arg_2);\nCIMGUI_API bool igDataTypeClamp(ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max);\nCIMGUI_API bool igDataTypeIsZero(ImGuiDataType data_type,const void* p_data);\nCIMGUI_API bool igInputTextEx(const char* label,const char* hint,char* buf,int buf_size,const ImVec2 size_arg,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data);\nCIMGUI_API void igInputTextDeactivateHook(ImGuiID id);\nCIMGUI_API bool igTempInputText(const ImRect bb,ImGuiID id,const char* label,char* buf,int buf_size,ImGuiInputTextFlags flags);\nCIMGUI_API bool igTempInputScalar(const ImRect bb,ImGuiID id,const char* label,ImGuiDataType data_type,void* p_data,const char* format,const void* p_clamp_min,const void* p_clamp_max);\nCIMGUI_API bool igTempInputIsActive(ImGuiID id);\nCIMGUI_API ImGuiInputTextState* igGetInputTextState(ImGuiID id);\nCIMGUI_API void igSetNextItemRefVal(ImGuiDataType data_type,void* p_data);\nCIMGUI_API bool igIsItemActiveAsInputText(void);\nCIMGUI_API void igColorTooltip(const char* text,const float* col,ImGuiColorEditFlags flags);\nCIMGUI_API void igColorEditOptionsPopup(const float* col,ImGuiColorEditFlags flags);\nCIMGUI_API void igColorPickerOptionsPopup(const float* ref_col,ImGuiColorEditFlags flags);\nCIMGUI_API int igPlotEx(ImGuiPlotType plot_type,const char* label,float(*values_getter)(void* data,int idx),void* data,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,const ImVec2 size_arg);\nCIMGUI_API void igShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,ImVec2 gradient_p0,ImVec2 gradient_p1,ImU32 col0,ImU32 col1);\nCIMGUI_API void igShadeVertsLinearUV(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,const ImVec2 a,const ImVec2 b,const ImVec2 uv_a,const ImVec2 uv_b,bool clamp);\nCIMGUI_API void igShadeVertsTransformPos(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,const ImVec2 pivot_in,float cos_a,float sin_a,const ImVec2 pivot_out);\nCIMGUI_API void igGcCompactTransientMiscBuffers(void);\nCIMGUI_API void igGcCompactTransientWindowBuffers(ImGuiWindow* window);\nCIMGUI_API void igGcAwakeTransientWindowBuffers(ImGuiWindow* window);\nCIMGUI_API bool igErrorLog(const char* msg);\nCIMGUI_API void igErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out);\nCIMGUI_API void igErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in);\nCIMGUI_API void igErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in);\nCIMGUI_API void igErrorCheckUsingSetCursorPosToExtendParentBoundaries(void);\nCIMGUI_API void igErrorCheckEndFrameFinalizeErrorTooltip(void);\nCIMGUI_API bool igBeginErrorTooltip(void);\nCIMGUI_API void igEndErrorTooltip(void);\nCIMGUI_API void igDebugAllocHook(ImGuiDebugAllocInfo* info,int frame_count,void* ptr,size_t size);\nCIMGUI_API void igDebugDrawCursorPos(ImU32 col);\nCIMGUI_API void igDebugDrawLineExtents(ImU32 col);\nCIMGUI_API void igDebugDrawItemRect(ImU32 col);\nCIMGUI_API void igDebugTextUnformattedWithLocateItem(const char* line_begin,const char* line_end);\nCIMGUI_API void igDebugLocateItem(ImGuiID target_id);\nCIMGUI_API void igDebugLocateItemOnHover(ImGuiID target_id);\nCIMGUI_API void igDebugLocateItemResolveWithLastItem(void);\nCIMGUI_API void igDebugBreakClearData(void);\nCIMGUI_API bool igDebugBreakButton(const char* label,const char* description_of_location);\nCIMGUI_API void igDebugBreakButtonTooltip(bool keyboard_only,const char* description_of_location);\nCIMGUI_API void igShowFontAtlas(ImFontAtlas* atlas);\nCIMGUI_API void igDebugHookIdInfo(ImGuiID id,ImGuiDataType data_type,const void* data_id,const void* data_id_end);\nCIMGUI_API void igDebugNodeColumns(ImGuiOldColumns* columns);\nCIMGUI_API void igDebugNodeDockNode(ImGuiDockNode* node,const char* label);\nCIMGUI_API void igDebugNodeDrawList(ImGuiWindow* window,ImGuiViewportP* viewport,const ImDrawList* draw_list,const char* label);\nCIMGUI_API void igDebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list,const ImDrawList* draw_list,const ImDrawCmd* draw_cmd,bool show_mesh,bool show_aabb);\nCIMGUI_API void igDebugNodeFont(ImFont* font);\nCIMGUI_API void igDebugNodeFontGlyph(ImFont* font,const ImFontGlyph* glyph);\nCIMGUI_API void igDebugNodeTexture(ImTextureData* tex,int int_id,const ImFontAtlasRect* highlight_rect);\nCIMGUI_API void igDebugNodeStorage(ImGuiStorage* storage,const char* label);\nCIMGUI_API void igDebugNodeTabBar(ImGuiTabBar* tab_bar,const char* label);\nCIMGUI_API void igDebugNodeTable(ImGuiTable* table);\nCIMGUI_API void igDebugNodeTableSettings(ImGuiTableSettings* settings);\nCIMGUI_API void igDebugNodeInputTextState(ImGuiInputTextState* state);\nCIMGUI_API void igDebugNodeTypingSelectState(ImGuiTypingSelectState* state);\nCIMGUI_API void igDebugNodeMultiSelectState(ImGuiMultiSelectState* state);\nCIMGUI_API void igDebugNodeWindow(ImGuiWindow* window,const char* label);\nCIMGUI_API void igDebugNodeWindowSettings(ImGuiWindowSettings* settings);\nCIMGUI_API void igDebugNodeWindowsList(ImVector_ImGuiWindowPtr* windows,const char* label);\nCIMGUI_API void igDebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows,int windows_size,ImGuiWindow* parent_in_begin_stack);\nCIMGUI_API void igDebugNodeViewport(ImGuiViewportP* viewport);\nCIMGUI_API void igDebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor,const char* label,int idx);\nCIMGUI_API void igDebugRenderKeyboardPreview(ImDrawList* draw_list);\nCIMGUI_API void igDebugRenderViewportThumbnail(ImDrawList* draw_list,ImGuiViewportP* viewport,const ImRect bb);\nCIMGUI_API ImFontLoader* ImFontLoader_ImFontLoader(void);\nCIMGUI_API void ImFontLoader_destroy(ImFontLoader* self);\nCIMGUI_API const ImFontLoader* igImFontAtlasGetFontLoaderForStbTruetype(void);\nCIMGUI_API int igImFontAtlasRectId_GetIndex(ImFontAtlasRectId id);\nCIMGUI_API int igImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id);\nCIMGUI_API ImFontAtlasRectId igImFontAtlasRectId_Make(int index_idx,int gen_idx);\nCIMGUI_API ImFontAtlasBuilder* ImFontAtlasBuilder_ImFontAtlasBuilder(void);\nCIMGUI_API void ImFontAtlasBuilder_destroy(ImFontAtlasBuilder* self);\nCIMGUI_API void igImFontAtlasBuildInit(ImFontAtlas* atlas);\nCIMGUI_API void igImFontAtlasBuildDestroy(ImFontAtlas* atlas);\nCIMGUI_API void igImFontAtlasBuildMain(ImFontAtlas* atlas);\nCIMGUI_API void igImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas,const ImFontLoader* font_loader);\nCIMGUI_API void igImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas);\nCIMGUI_API void igImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas,int x,int y,int w,int h,const char* in_str,char in_marker_char);\nCIMGUI_API void igImFontAtlasBuildClear(ImFontAtlas* atlas);\nCIMGUI_API ImTextureData* igImFontAtlasTextureAdd(ImFontAtlas* atlas,int w,int h);\nCIMGUI_API void igImFontAtlasTextureMakeSpace(ImFontAtlas* atlas);\nCIMGUI_API void igImFontAtlasTextureRepack(ImFontAtlas* atlas,int w,int h);\nCIMGUI_API void igImFontAtlasTextureGrow(ImFontAtlas* atlas,int old_w,int old_h);\nCIMGUI_API void igImFontAtlasTextureCompact(ImFontAtlas* atlas);\nCIMGUI_API void igImFontAtlasTextureGetSizeEstimate(ImVec2i *pOut,ImFontAtlas* atlas);\nCIMGUI_API void igImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas,ImFont* font,ImFontConfig* src);\nCIMGUI_API void igImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas);\nCIMGUI_API void igImFontAtlasBuildGetOversampleFactors(ImFontConfig* src,ImFontBaked* baked,int* out_oversample_h,int* out_oversample_v);\nCIMGUI_API void igImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas,int unused_frames);\nCIMGUI_API bool igImFontAtlasFontSourceInit(ImFontAtlas* atlas,ImFontConfig* src);\nCIMGUI_API void igImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas,ImFont* font,ImFontConfig* src);\nCIMGUI_API void igImFontAtlasFontDestroySourceData(ImFontAtlas* atlas,ImFontConfig* src);\nCIMGUI_API bool igImFontAtlasFontInitOutput(ImFontAtlas* atlas,ImFont* font);\nCIMGUI_API void igImFontAtlasFontDestroyOutput(ImFontAtlas* atlas,ImFont* font);\nCIMGUI_API void igImFontAtlasFontDiscardBakes(ImFontAtlas* atlas,ImFont* font,int unused_frames);\nCIMGUI_API ImGuiID igImFontAtlasBakedGetId(ImGuiID font_id,float baked_size,float rasterizer_density);\nCIMGUI_API ImFontBaked* igImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density);\nCIMGUI_API ImFontBaked* igImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density);\nCIMGUI_API ImFontBaked* igImFontAtlasBakedAdd(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density,ImGuiID baked_id);\nCIMGUI_API void igImFontAtlasBakedDiscard(ImFontAtlas* atlas,ImFont* font,ImFontBaked* baked);\nCIMGUI_API ImFontGlyph* igImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas,ImFontBaked* baked,ImFontConfig* src,const ImFontGlyph* in_glyph);\nCIMGUI_API void igImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas,ImFont* font,ImFontBaked* baked,ImFontGlyph* glyph);\nCIMGUI_API void igImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas,ImFontBaked* baked,ImFontConfig* src,ImFontGlyph* glyph,ImTextureRect* r,const unsigned char* src_pixels,ImTextureFormat src_fmt,int src_pitch);\nCIMGUI_API void igImFontAtlasPackInit(ImFontAtlas* atlas);\nCIMGUI_API ImFontAtlasRectId igImFontAtlasPackAddRect(ImFontAtlas* atlas,int w,int h,ImFontAtlasRectEntry* overwrite_entry);\nCIMGUI_API ImTextureRect* igImFontAtlasPackGetRect(ImFontAtlas* atlas,ImFontAtlasRectId id);\nCIMGUI_API ImTextureRect* igImFontAtlasPackGetRectSafe(ImFontAtlas* atlas,ImFontAtlasRectId id);\nCIMGUI_API void igImFontAtlasPackDiscardRect(ImFontAtlas* atlas,ImFontAtlasRectId id);\nCIMGUI_API void igImFontAtlasUpdateNewFrame(ImFontAtlas* atlas,int frame_count,bool renderer_has_textures);\nCIMGUI_API void igImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas,ImDrawListSharedData* data);\nCIMGUI_API void igImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas,ImDrawListSharedData* data);\nCIMGUI_API void igImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas,ImTextureRef old_tex,ImTextureRef new_tex);\nCIMGUI_API void igImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas);\nCIMGUI_API void igImFontAtlasTextureBlockConvert(const unsigned char* src_pixels,ImTextureFormat src_fmt,int src_pitch,unsigned char* dst_pixels,ImTextureFormat dst_fmt,int dst_pitch,int w,int h);\nCIMGUI_API void igImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data);\nCIMGUI_API void igImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data,float multiply_factor);\nCIMGUI_API void igImFontAtlasTextureBlockFill(ImTextureData* dst_tex,int dst_x,int dst_y,int w,int h,ImU32 col);\nCIMGUI_API void igImFontAtlasTextureBlockCopy(ImTextureData* src_tex,int src_x,int src_y,ImTextureData* dst_tex,int dst_x,int dst_y,int w,int h);\nCIMGUI_API void igImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas,ImTextureData* tex,int x,int y,int w,int h);\nCIMGUI_API int igImTextureDataGetFormatBytesPerPixel(ImTextureFormat format);\nCIMGUI_API const char* igImTextureDataGetStatusName(ImTextureStatus status);\nCIMGUI_API const char* igImTextureDataGetFormatName(ImTextureFormat format);\nCIMGUI_API void igImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas);\nCIMGUI_API bool igImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas,ImGuiMouseCursor cursor_type,ImVec2* out_offset,ImVec2* out_size,ImVec2 out_uv_border[2],ImVec2 out_uv_fill[2]);\n#ifdef IMGUI_ENABLE_FREETYPE\nCIMGUI_API const ImFontLoader* ImGuiFreeType_GetFontLoader(void);\nCIMGUI_API void ImGuiFreeType_SetAllocatorFunctions(void*(*alloc_func)(size_t sz,void* user_data),void(*free_func)(void* ptr,void* user_data),void* user_data);\nCIMGUI_API bool ImGuiFreeType_DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags);\n#endif\n\n\n/////////////////////////hand written functions\n//no appendfV\nCIMGUI_API void ImGuiTextBuffer_appendf(ImGuiTextBuffer *self, const char *fmt, ...);\n//for getting FLT_MAX in bindings\nCIMGUI_API float igGET_FLT_MAX(void);\n//for getting FLT_MIN in bindings\nCIMGUI_API float igGET_FLT_MIN(void);\n\n\nCIMGUI_API ImVector_ImWchar* ImVector_ImWchar_create(void);\nCIMGUI_API void ImVector_ImWchar_destroy(ImVector_ImWchar* self);\nCIMGUI_API void ImVector_ImWchar_Init(ImVector_ImWchar* p);\nCIMGUI_API void ImVector_ImWchar_UnInit(ImVector_ImWchar* p);\n\n#ifdef IMGUI_HAS_DOCK\nCIMGUI_API void ImGuiPlatformIO_Set_Platform_GetWindowPos(ImGuiPlatformIO* platform_io, void(*user_callback)(ImGuiViewport* vp, ImVec2* out_pos));\nCIMGUI_API void ImGuiPlatformIO_Set_Platform_GetWindowSize(ImGuiPlatformIO* platform_io, void(*user_callback)(ImGuiViewport* vp, ImVec2* out_size));\n#endif\n\n#endif //CIMGUI_INCLUDED\n\n\n\n\n"
  },
  {
    "path": "lib/third_party/imgui/cimgui/source/cimgui.cpp",
    "content": "//This file is automatically generated by generator.lua from https://github.com/cimgui/cimgui\n//based on imgui.h file version \"1.92.0\" 19200 from Dear ImGui https://github.com/ocornut/imgui\n//with imgui_internal.h api\n//with imgui_freetype.h api\n//docking branch\n\n#include <imgui.h>\n#include <imgui_internal.h>\n#include \"cimgui.h\"\n\nCIMGUI_API ImVec2* ImVec2_ImVec2_Nil(void)\n{\n    return IM_NEW(ImVec2)();\n}\nCIMGUI_API void ImVec2_destroy(ImVec2* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImVec2* ImVec2_ImVec2_Float(float _x,float _y)\n{\n    return IM_NEW(ImVec2)(_x,_y);\n}\nCIMGUI_API ImVec4* ImVec4_ImVec4_Nil(void)\n{\n    return IM_NEW(ImVec4)();\n}\nCIMGUI_API void ImVec4_destroy(ImVec4* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImVec4* ImVec4_ImVec4_Float(float _x,float _y,float _z,float _w)\n{\n    return IM_NEW(ImVec4)(_x,_y,_z,_w);\n}\nCIMGUI_API ImTextureRef* ImTextureRef_ImTextureRef_Nil(void)\n{\n    return IM_NEW(ImTextureRef)();\n}\nCIMGUI_API void ImTextureRef_destroy(ImTextureRef* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImTextureRef* ImTextureRef_ImTextureRef_TextureID(ImTextureID tex_id)\n{\n    return IM_NEW(ImTextureRef)(tex_id);\n}\nCIMGUI_API ImTextureID ImTextureRef_GetTexID(ImTextureRef* self)\n{\n    return self->GetTexID();\n}\nCIMGUI_API ImGuiContext* igCreateContext(ImFontAtlas* shared_font_atlas)\n{\n    return ImGui::CreateContext(shared_font_atlas);\n}\nCIMGUI_API void igDestroyContext(ImGuiContext* ctx)\n{\n    return ImGui::DestroyContext(ctx);\n}\nCIMGUI_API ImGuiContext* igGetCurrentContext()\n{\n    return ImGui::GetCurrentContext();\n}\nCIMGUI_API void igSetCurrentContext(ImGuiContext* ctx)\n{\n    return ImGui::SetCurrentContext(ctx);\n}\nCIMGUI_API ImGuiIO* igGetIO_Nil()\n{\n    return &ImGui::GetIO();\n}\nCIMGUI_API ImGuiPlatformIO* igGetPlatformIO_Nil()\n{\n    return &ImGui::GetPlatformIO();\n}\nCIMGUI_API ImGuiStyle* igGetStyle()\n{\n    return &ImGui::GetStyle();\n}\nCIMGUI_API void igNewFrame()\n{\n    return ImGui::NewFrame();\n}\nCIMGUI_API void igEndFrame()\n{\n    return ImGui::EndFrame();\n}\nCIMGUI_API void igRender()\n{\n    return ImGui::Render();\n}\nCIMGUI_API ImDrawData* igGetDrawData()\n{\n    return ImGui::GetDrawData();\n}\nCIMGUI_API void igShowDemoWindow(bool* p_open)\n{\n    return ImGui::ShowDemoWindow(p_open);\n}\nCIMGUI_API void igShowMetricsWindow(bool* p_open)\n{\n    return ImGui::ShowMetricsWindow(p_open);\n}\nCIMGUI_API void igShowDebugLogWindow(bool* p_open)\n{\n    return ImGui::ShowDebugLogWindow(p_open);\n}\nCIMGUI_API void igShowIDStackToolWindow(bool* p_open)\n{\n    return ImGui::ShowIDStackToolWindow(p_open);\n}\nCIMGUI_API void igShowAboutWindow(bool* p_open)\n{\n    return ImGui::ShowAboutWindow(p_open);\n}\nCIMGUI_API void igShowStyleEditor(ImGuiStyle* ref)\n{\n    return ImGui::ShowStyleEditor(ref);\n}\nCIMGUI_API bool igShowStyleSelector(const char* label)\n{\n    return ImGui::ShowStyleSelector(label);\n}\nCIMGUI_API void igShowFontSelector(const char* label)\n{\n    return ImGui::ShowFontSelector(label);\n}\nCIMGUI_API void igShowUserGuide()\n{\n    return ImGui::ShowUserGuide();\n}\nCIMGUI_API const char* igGetVersion()\n{\n    return ImGui::GetVersion();\n}\nCIMGUI_API void igStyleColorsDark(ImGuiStyle* dst)\n{\n    return ImGui::StyleColorsDark(dst);\n}\nCIMGUI_API void igStyleColorsLight(ImGuiStyle* dst)\n{\n    return ImGui::StyleColorsLight(dst);\n}\nCIMGUI_API void igStyleColorsClassic(ImGuiStyle* dst)\n{\n    return ImGui::StyleColorsClassic(dst);\n}\nCIMGUI_API bool igBegin(const char* name,bool* p_open,ImGuiWindowFlags flags)\n{\n    return ImGui::Begin(name,p_open,flags);\n}\nCIMGUI_API void igEnd()\n{\n    return ImGui::End();\n}\nCIMGUI_API bool igBeginChild_Str(const char* str_id,const ImVec2 size,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags)\n{\n    return ImGui::BeginChild(str_id,size,child_flags,window_flags);\n}\nCIMGUI_API bool igBeginChild_ID(ImGuiID id,const ImVec2 size,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags)\n{\n    return ImGui::BeginChild(id,size,child_flags,window_flags);\n}\nCIMGUI_API void igEndChild()\n{\n    return ImGui::EndChild();\n}\nCIMGUI_API bool igIsWindowAppearing()\n{\n    return ImGui::IsWindowAppearing();\n}\nCIMGUI_API bool igIsWindowCollapsed()\n{\n    return ImGui::IsWindowCollapsed();\n}\nCIMGUI_API bool igIsWindowFocused(ImGuiFocusedFlags flags)\n{\n    return ImGui::IsWindowFocused(flags);\n}\nCIMGUI_API bool igIsWindowHovered(ImGuiHoveredFlags flags)\n{\n    return ImGui::IsWindowHovered(flags);\n}\nCIMGUI_API ImDrawList* igGetWindowDrawList()\n{\n    return ImGui::GetWindowDrawList();\n}\nCIMGUI_API float igGetWindowDpiScale()\n{\n    return ImGui::GetWindowDpiScale();\n}\nCIMGUI_API void igGetWindowPos(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetWindowPos();\n}\nCIMGUI_API void igGetWindowSize(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetWindowSize();\n}\nCIMGUI_API float igGetWindowWidth()\n{\n    return ImGui::GetWindowWidth();\n}\nCIMGUI_API float igGetWindowHeight()\n{\n    return ImGui::GetWindowHeight();\n}\nCIMGUI_API ImGuiViewport* igGetWindowViewport()\n{\n    return ImGui::GetWindowViewport();\n}\nCIMGUI_API void igSetNextWindowPos(const ImVec2 pos,ImGuiCond cond,const ImVec2 pivot)\n{\n    return ImGui::SetNextWindowPos(pos,cond,pivot);\n}\nCIMGUI_API void igSetNextWindowSize(const ImVec2 size,ImGuiCond cond)\n{\n    return ImGui::SetNextWindowSize(size,cond);\n}\nCIMGUI_API void igSetNextWindowSizeConstraints(const ImVec2 size_min,const ImVec2 size_max,ImGuiSizeCallback custom_callback,void* custom_callback_data)\n{\n    return ImGui::SetNextWindowSizeConstraints(size_min,size_max,custom_callback,custom_callback_data);\n}\nCIMGUI_API void igSetNextWindowContentSize(const ImVec2 size)\n{\n    return ImGui::SetNextWindowContentSize(size);\n}\nCIMGUI_API void igSetNextWindowCollapsed(bool collapsed,ImGuiCond cond)\n{\n    return ImGui::SetNextWindowCollapsed(collapsed,cond);\n}\nCIMGUI_API void igSetNextWindowFocus()\n{\n    return ImGui::SetNextWindowFocus();\n}\nCIMGUI_API void igSetNextWindowScroll(const ImVec2 scroll)\n{\n    return ImGui::SetNextWindowScroll(scroll);\n}\nCIMGUI_API void igSetNextWindowBgAlpha(float alpha)\n{\n    return ImGui::SetNextWindowBgAlpha(alpha);\n}\nCIMGUI_API void igSetNextWindowViewport(ImGuiID viewport_id)\n{\n    return ImGui::SetNextWindowViewport(viewport_id);\n}\nCIMGUI_API void igSetWindowPos_Vec2(const ImVec2 pos,ImGuiCond cond)\n{\n    return ImGui::SetWindowPos(pos,cond);\n}\nCIMGUI_API void igSetWindowSize_Vec2(const ImVec2 size,ImGuiCond cond)\n{\n    return ImGui::SetWindowSize(size,cond);\n}\nCIMGUI_API void igSetWindowCollapsed_Bool(bool collapsed,ImGuiCond cond)\n{\n    return ImGui::SetWindowCollapsed(collapsed,cond);\n}\nCIMGUI_API void igSetWindowFocus_Nil()\n{\n    return ImGui::SetWindowFocus();\n}\nCIMGUI_API void igSetWindowPos_Str(const char* name,const ImVec2 pos,ImGuiCond cond)\n{\n    return ImGui::SetWindowPos(name,pos,cond);\n}\nCIMGUI_API void igSetWindowSize_Str(const char* name,const ImVec2 size,ImGuiCond cond)\n{\n    return ImGui::SetWindowSize(name,size,cond);\n}\nCIMGUI_API void igSetWindowCollapsed_Str(const char* name,bool collapsed,ImGuiCond cond)\n{\n    return ImGui::SetWindowCollapsed(name,collapsed,cond);\n}\nCIMGUI_API void igSetWindowFocus_Str(const char* name)\n{\n    return ImGui::SetWindowFocus(name);\n}\nCIMGUI_API float igGetScrollX()\n{\n    return ImGui::GetScrollX();\n}\nCIMGUI_API float igGetScrollY()\n{\n    return ImGui::GetScrollY();\n}\nCIMGUI_API void igSetScrollX_Float(float scroll_x)\n{\n    return ImGui::SetScrollX(scroll_x);\n}\nCIMGUI_API void igSetScrollY_Float(float scroll_y)\n{\n    return ImGui::SetScrollY(scroll_y);\n}\nCIMGUI_API float igGetScrollMaxX()\n{\n    return ImGui::GetScrollMaxX();\n}\nCIMGUI_API float igGetScrollMaxY()\n{\n    return ImGui::GetScrollMaxY();\n}\nCIMGUI_API void igSetScrollHereX(float center_x_ratio)\n{\n    return ImGui::SetScrollHereX(center_x_ratio);\n}\nCIMGUI_API void igSetScrollHereY(float center_y_ratio)\n{\n    return ImGui::SetScrollHereY(center_y_ratio);\n}\nCIMGUI_API void igSetScrollFromPosX_Float(float local_x,float center_x_ratio)\n{\n    return ImGui::SetScrollFromPosX(local_x,center_x_ratio);\n}\nCIMGUI_API void igSetScrollFromPosY_Float(float local_y,float center_y_ratio)\n{\n    return ImGui::SetScrollFromPosY(local_y,center_y_ratio);\n}\nCIMGUI_API void igPushFont(ImFont* font,float font_size_base_unscaled)\n{\n    return ImGui::PushFont(font,font_size_base_unscaled);\n}\nCIMGUI_API void igPopFont()\n{\n    return ImGui::PopFont();\n}\nCIMGUI_API ImFont* igGetFont()\n{\n    return ImGui::GetFont();\n}\nCIMGUI_API float igGetFontSize()\n{\n    return ImGui::GetFontSize();\n}\nCIMGUI_API ImFontBaked* igGetFontBaked()\n{\n    return ImGui::GetFontBaked();\n}\nCIMGUI_API void igPushStyleColor_U32(ImGuiCol idx,ImU32 col)\n{\n    return ImGui::PushStyleColor(idx,col);\n}\nCIMGUI_API void igPushStyleColor_Vec4(ImGuiCol idx,const ImVec4 col)\n{\n    return ImGui::PushStyleColor(idx,col);\n}\nCIMGUI_API void igPopStyleColor(int count)\n{\n    return ImGui::PopStyleColor(count);\n}\nCIMGUI_API void igPushStyleVar_Float(ImGuiStyleVar idx,float val)\n{\n    return ImGui::PushStyleVar(idx,val);\n}\nCIMGUI_API void igPushStyleVar_Vec2(ImGuiStyleVar idx,const ImVec2 val)\n{\n    return ImGui::PushStyleVar(idx,val);\n}\nCIMGUI_API void igPushStyleVarX(ImGuiStyleVar idx,float val_x)\n{\n    return ImGui::PushStyleVarX(idx,val_x);\n}\nCIMGUI_API void igPushStyleVarY(ImGuiStyleVar idx,float val_y)\n{\n    return ImGui::PushStyleVarY(idx,val_y);\n}\nCIMGUI_API void igPopStyleVar(int count)\n{\n    return ImGui::PopStyleVar(count);\n}\nCIMGUI_API void igPushItemFlag(ImGuiItemFlags option,bool enabled)\n{\n    return ImGui::PushItemFlag(option,enabled);\n}\nCIMGUI_API void igPopItemFlag()\n{\n    return ImGui::PopItemFlag();\n}\nCIMGUI_API void igPushItemWidth(float item_width)\n{\n    return ImGui::PushItemWidth(item_width);\n}\nCIMGUI_API void igPopItemWidth()\n{\n    return ImGui::PopItemWidth();\n}\nCIMGUI_API void igSetNextItemWidth(float item_width)\n{\n    return ImGui::SetNextItemWidth(item_width);\n}\nCIMGUI_API float igCalcItemWidth()\n{\n    return ImGui::CalcItemWidth();\n}\nCIMGUI_API void igPushTextWrapPos(float wrap_local_pos_x)\n{\n    return ImGui::PushTextWrapPos(wrap_local_pos_x);\n}\nCIMGUI_API void igPopTextWrapPos()\n{\n    return ImGui::PopTextWrapPos();\n}\nCIMGUI_API void igGetFontTexUvWhitePixel(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetFontTexUvWhitePixel();\n}\nCIMGUI_API ImU32 igGetColorU32_Col(ImGuiCol idx,float alpha_mul)\n{\n    return ImGui::GetColorU32(idx,alpha_mul);\n}\nCIMGUI_API ImU32 igGetColorU32_Vec4(const ImVec4 col)\n{\n    return ImGui::GetColorU32(col);\n}\nCIMGUI_API ImU32 igGetColorU32_U32(ImU32 col,float alpha_mul)\n{\n    return ImGui::GetColorU32(col,alpha_mul);\n}\nCIMGUI_API const ImVec4* igGetStyleColorVec4(ImGuiCol idx)\n{\n    return &ImGui::GetStyleColorVec4(idx);\n}\nCIMGUI_API void igGetCursorScreenPos(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetCursorScreenPos();\n}\nCIMGUI_API void igSetCursorScreenPos(const ImVec2 pos)\n{\n    return ImGui::SetCursorScreenPos(pos);\n}\nCIMGUI_API void igGetContentRegionAvail(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetContentRegionAvail();\n}\nCIMGUI_API void igGetCursorPos(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetCursorPos();\n}\nCIMGUI_API float igGetCursorPosX()\n{\n    return ImGui::GetCursorPosX();\n}\nCIMGUI_API float igGetCursorPosY()\n{\n    return ImGui::GetCursorPosY();\n}\nCIMGUI_API void igSetCursorPos(const ImVec2 local_pos)\n{\n    return ImGui::SetCursorPos(local_pos);\n}\nCIMGUI_API void igSetCursorPosX(float local_x)\n{\n    return ImGui::SetCursorPosX(local_x);\n}\nCIMGUI_API void igSetCursorPosY(float local_y)\n{\n    return ImGui::SetCursorPosY(local_y);\n}\nCIMGUI_API void igGetCursorStartPos(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetCursorStartPos();\n}\nCIMGUI_API void igSeparator()\n{\n    return ImGui::Separator();\n}\nCIMGUI_API void igSameLine(float offset_from_start_x,float spacing)\n{\n    return ImGui::SameLine(offset_from_start_x,spacing);\n}\nCIMGUI_API void igNewLine()\n{\n    return ImGui::NewLine();\n}\nCIMGUI_API void igSpacing()\n{\n    return ImGui::Spacing();\n}\nCIMGUI_API void igDummy(const ImVec2 size)\n{\n    return ImGui::Dummy(size);\n}\nCIMGUI_API void igIndent(float indent_w)\n{\n    return ImGui::Indent(indent_w);\n}\nCIMGUI_API void igUnindent(float indent_w)\n{\n    return ImGui::Unindent(indent_w);\n}\nCIMGUI_API void igBeginGroup()\n{\n    return ImGui::BeginGroup();\n}\nCIMGUI_API void igEndGroup()\n{\n    return ImGui::EndGroup();\n}\nCIMGUI_API void igAlignTextToFramePadding()\n{\n    return ImGui::AlignTextToFramePadding();\n}\nCIMGUI_API float igGetTextLineHeight()\n{\n    return ImGui::GetTextLineHeight();\n}\nCIMGUI_API float igGetTextLineHeightWithSpacing()\n{\n    return ImGui::GetTextLineHeightWithSpacing();\n}\nCIMGUI_API float igGetFrameHeight()\n{\n    return ImGui::GetFrameHeight();\n}\nCIMGUI_API float igGetFrameHeightWithSpacing()\n{\n    return ImGui::GetFrameHeightWithSpacing();\n}\nCIMGUI_API void igPushID_Str(const char* str_id)\n{\n    return ImGui::PushID(str_id);\n}\nCIMGUI_API void igPushID_StrStr(const char* str_id_begin,const char* str_id_end)\n{\n    return ImGui::PushID(str_id_begin,str_id_end);\n}\nCIMGUI_API void igPushID_Ptr(const void* ptr_id)\n{\n    return ImGui::PushID(ptr_id);\n}\nCIMGUI_API void igPushID_Int(int int_id)\n{\n    return ImGui::PushID(int_id);\n}\nCIMGUI_API void igPopID()\n{\n    return ImGui::PopID();\n}\nCIMGUI_API ImGuiID igGetID_Str(const char* str_id)\n{\n    return ImGui::GetID(str_id);\n}\nCIMGUI_API ImGuiID igGetID_StrStr(const char* str_id_begin,const char* str_id_end)\n{\n    return ImGui::GetID(str_id_begin,str_id_end);\n}\nCIMGUI_API ImGuiID igGetID_Ptr(const void* ptr_id)\n{\n    return ImGui::GetID(ptr_id);\n}\nCIMGUI_API ImGuiID igGetID_Int(int int_id)\n{\n    return ImGui::GetID(int_id);\n}\nCIMGUI_API void igTextUnformatted(const char* text,const char* text_end)\n{\n    return ImGui::TextUnformatted(text,text_end);\n}\nCIMGUI_API void igText(const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImGui::TextV(fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igText0(const char* fmt)\n{\n    return igText(fmt);\n}\n#endif\nCIMGUI_API void igTextV(const char* fmt,va_list args)\n{\n    return ImGui::TextV(fmt,args);\n}\nCIMGUI_API void igTextColored(const ImVec4 col,const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImGui::TextColoredV(col,fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igTextColored0(const ImVec4 col,const char* fmt)\n{\n    return igTextColored(col,fmt);\n}\n#endif\nCIMGUI_API void igTextColoredV(const ImVec4 col,const char* fmt,va_list args)\n{\n    return ImGui::TextColoredV(col,fmt,args);\n}\nCIMGUI_API void igTextDisabled(const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImGui::TextDisabledV(fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igTextDisabled0(const char* fmt)\n{\n    return igTextDisabled(fmt);\n}\n#endif\nCIMGUI_API void igTextDisabledV(const char* fmt,va_list args)\n{\n    return ImGui::TextDisabledV(fmt,args);\n}\nCIMGUI_API void igTextWrapped(const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImGui::TextWrappedV(fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igTextWrapped0(const char* fmt)\n{\n    return igTextWrapped(fmt);\n}\n#endif\nCIMGUI_API void igTextWrappedV(const char* fmt,va_list args)\n{\n    return ImGui::TextWrappedV(fmt,args);\n}\nCIMGUI_API void igLabelText(const char* label,const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImGui::LabelTextV(label,fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igLabelText0(const char* label,const char* fmt)\n{\n    return igLabelText(label,fmt);\n}\n#endif\nCIMGUI_API void igLabelTextV(const char* label,const char* fmt,va_list args)\n{\n    return ImGui::LabelTextV(label,fmt,args);\n}\nCIMGUI_API void igBulletText(const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImGui::BulletTextV(fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igBulletText0(const char* fmt)\n{\n    return igBulletText(fmt);\n}\n#endif\nCIMGUI_API void igBulletTextV(const char* fmt,va_list args)\n{\n    return ImGui::BulletTextV(fmt,args);\n}\nCIMGUI_API void igSeparatorText(const char* label)\n{\n    return ImGui::SeparatorText(label);\n}\nCIMGUI_API bool igButton(const char* label,const ImVec2 size)\n{\n    return ImGui::Button(label,size);\n}\nCIMGUI_API bool igSmallButton(const char* label)\n{\n    return ImGui::SmallButton(label);\n}\nCIMGUI_API bool igInvisibleButton(const char* str_id,const ImVec2 size,ImGuiButtonFlags flags)\n{\n    return ImGui::InvisibleButton(str_id,size,flags);\n}\nCIMGUI_API bool igArrowButton(const char* str_id,ImGuiDir dir)\n{\n    return ImGui::ArrowButton(str_id,dir);\n}\nCIMGUI_API bool igCheckbox(const char* label,bool* v)\n{\n    return ImGui::Checkbox(label,v);\n}\nCIMGUI_API bool igCheckboxFlags_IntPtr(const char* label,int* flags,int flags_value)\n{\n    return ImGui::CheckboxFlags(label,flags,flags_value);\n}\nCIMGUI_API bool igCheckboxFlags_UintPtr(const char* label,unsigned int* flags,unsigned int flags_value)\n{\n    return ImGui::CheckboxFlags(label,flags,flags_value);\n}\nCIMGUI_API bool igRadioButton_Bool(const char* label,bool active)\n{\n    return ImGui::RadioButton(label,active);\n}\nCIMGUI_API bool igRadioButton_IntPtr(const char* label,int* v,int v_button)\n{\n    return ImGui::RadioButton(label,v,v_button);\n}\nCIMGUI_API void igProgressBar(float fraction,const ImVec2 size_arg,const char* overlay)\n{\n    return ImGui::ProgressBar(fraction,size_arg,overlay);\n}\nCIMGUI_API void igBullet()\n{\n    return ImGui::Bullet();\n}\nCIMGUI_API bool igTextLink(const char* label)\n{\n    return ImGui::TextLink(label);\n}\nCIMGUI_API bool igTextLinkOpenURL(const char* label,const char* url)\n{\n    return ImGui::TextLinkOpenURL(label,url);\n}\nCIMGUI_API void igImage(ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1)\n{\n    return ImGui::Image(tex_ref,image_size,uv0,uv1);\n}\nCIMGUI_API void igImageWithBg(ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col)\n{\n    return ImGui::ImageWithBg(tex_ref,image_size,uv0,uv1,bg_col,tint_col);\n}\nCIMGUI_API bool igImageButton(const char* str_id,ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col)\n{\n    return ImGui::ImageButton(str_id,tex_ref,image_size,uv0,uv1,bg_col,tint_col);\n}\nCIMGUI_API bool igBeginCombo(const char* label,const char* preview_value,ImGuiComboFlags flags)\n{\n    return ImGui::BeginCombo(label,preview_value,flags);\n}\nCIMGUI_API void igEndCombo()\n{\n    return ImGui::EndCombo();\n}\nCIMGUI_API bool igCombo_Str_arr(const char* label,int* current_item,const char* const items[],int items_count,int popup_max_height_in_items)\n{\n    return ImGui::Combo(label,current_item,items,items_count,popup_max_height_in_items);\n}\nCIMGUI_API bool igCombo_Str(const char* label,int* current_item,const char* items_separated_by_zeros,int popup_max_height_in_items)\n{\n    return ImGui::Combo(label,current_item,items_separated_by_zeros,popup_max_height_in_items);\n}\nCIMGUI_API bool igCombo_FnStrPtr(const char* label,int* current_item,const char*(*getter)(void* user_data,int idx),void* user_data,int items_count,int popup_max_height_in_items)\n{\n    return ImGui::Combo(label,current_item,getter,user_data,items_count,popup_max_height_in_items);\n}\nCIMGUI_API bool igDragFloat(const char* label,float* v,float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::DragFloat(label,v,v_speed,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igDragFloat2(const char* label,float v[2],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::DragFloat2(label,v,v_speed,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igDragFloat3(const char* label,float v[3],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::DragFloat3(label,v,v_speed,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igDragFloat4(const char* label,float v[4],float v_speed,float v_min,float v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::DragFloat4(label,v,v_speed,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igDragFloatRange2(const char* label,float* v_current_min,float* v_current_max,float v_speed,float v_min,float v_max,const char* format,const char* format_max,ImGuiSliderFlags flags)\n{\n    return ImGui::DragFloatRange2(label,v_current_min,v_current_max,v_speed,v_min,v_max,format,format_max,flags);\n}\nCIMGUI_API bool igDragInt(const char* label,int* v,float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::DragInt(label,v,v_speed,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igDragInt2(const char* label,int v[2],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::DragInt2(label,v,v_speed,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igDragInt3(const char* label,int v[3],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::DragInt3(label,v,v_speed,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igDragInt4(const char* label,int v[4],float v_speed,int v_min,int v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::DragInt4(label,v,v_speed,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igDragIntRange2(const char* label,int* v_current_min,int* v_current_max,float v_speed,int v_min,int v_max,const char* format,const char* format_max,ImGuiSliderFlags flags)\n{\n    return ImGui::DragIntRange2(label,v_current_min,v_current_max,v_speed,v_min,v_max,format,format_max,flags);\n}\nCIMGUI_API bool igDragScalar(const char* label,ImGuiDataType data_type,void* p_data,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::DragScalar(label,data_type,p_data,v_speed,p_min,p_max,format,flags);\n}\nCIMGUI_API bool igDragScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::DragScalarN(label,data_type,p_data,components,v_speed,p_min,p_max,format,flags);\n}\nCIMGUI_API bool igSliderFloat(const char* label,float* v,float v_min,float v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::SliderFloat(label,v,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igSliderFloat2(const char* label,float v[2],float v_min,float v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::SliderFloat2(label,v,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igSliderFloat3(const char* label,float v[3],float v_min,float v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::SliderFloat3(label,v,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igSliderFloat4(const char* label,float v[4],float v_min,float v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::SliderFloat4(label,v,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igSliderAngle(const char* label,float* v_rad,float v_degrees_min,float v_degrees_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::SliderAngle(label,v_rad,v_degrees_min,v_degrees_max,format,flags);\n}\nCIMGUI_API bool igSliderInt(const char* label,int* v,int v_min,int v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::SliderInt(label,v,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igSliderInt2(const char* label,int v[2],int v_min,int v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::SliderInt2(label,v,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igSliderInt3(const char* label,int v[3],int v_min,int v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::SliderInt3(label,v,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igSliderInt4(const char* label,int v[4],int v_min,int v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::SliderInt4(label,v,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igSliderScalar(const char* label,ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::SliderScalar(label,data_type,p_data,p_min,p_max,format,flags);\n}\nCIMGUI_API bool igSliderScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::SliderScalarN(label,data_type,p_data,components,p_min,p_max,format,flags);\n}\nCIMGUI_API bool igVSliderFloat(const char* label,const ImVec2 size,float* v,float v_min,float v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::VSliderFloat(label,size,v,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igVSliderInt(const char* label,const ImVec2 size,int* v,int v_min,int v_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::VSliderInt(label,size,v,v_min,v_max,format,flags);\n}\nCIMGUI_API bool igVSliderScalar(const char* label,const ImVec2 size,ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::VSliderScalar(label,size,data_type,p_data,p_min,p_max,format,flags);\n}\nCIMGUI_API bool igInputText(const char* label,char* buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data)\n{\n    return ImGui::InputText(label,buf,buf_size,flags,callback,user_data);\n}\nCIMGUI_API bool igInputTextMultiline(const char* label,char* buf,size_t buf_size,const ImVec2 size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data)\n{\n    return ImGui::InputTextMultiline(label,buf,buf_size,size,flags,callback,user_data);\n}\nCIMGUI_API bool igInputTextWithHint(const char* label,const char* hint,char* buf,size_t buf_size,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data)\n{\n    return ImGui::InputTextWithHint(label,hint,buf,buf_size,flags,callback,user_data);\n}\nCIMGUI_API bool igInputFloat(const char* label,float* v,float step,float step_fast,const char* format,ImGuiInputTextFlags flags)\n{\n    return ImGui::InputFloat(label,v,step,step_fast,format,flags);\n}\nCIMGUI_API bool igInputFloat2(const char* label,float v[2],const char* format,ImGuiInputTextFlags flags)\n{\n    return ImGui::InputFloat2(label,v,format,flags);\n}\nCIMGUI_API bool igInputFloat3(const char* label,float v[3],const char* format,ImGuiInputTextFlags flags)\n{\n    return ImGui::InputFloat3(label,v,format,flags);\n}\nCIMGUI_API bool igInputFloat4(const char* label,float v[4],const char* format,ImGuiInputTextFlags flags)\n{\n    return ImGui::InputFloat4(label,v,format,flags);\n}\nCIMGUI_API bool igInputInt(const char* label,int* v,int step,int step_fast,ImGuiInputTextFlags flags)\n{\n    return ImGui::InputInt(label,v,step,step_fast,flags);\n}\nCIMGUI_API bool igInputInt2(const char* label,int v[2],ImGuiInputTextFlags flags)\n{\n    return ImGui::InputInt2(label,v,flags);\n}\nCIMGUI_API bool igInputInt3(const char* label,int v[3],ImGuiInputTextFlags flags)\n{\n    return ImGui::InputInt3(label,v,flags);\n}\nCIMGUI_API bool igInputInt4(const char* label,int v[4],ImGuiInputTextFlags flags)\n{\n    return ImGui::InputInt4(label,v,flags);\n}\nCIMGUI_API bool igInputDouble(const char* label,double* v,double step,double step_fast,const char* format,ImGuiInputTextFlags flags)\n{\n    return ImGui::InputDouble(label,v,step,step_fast,format,flags);\n}\nCIMGUI_API bool igInputScalar(const char* label,ImGuiDataType data_type,void* p_data,const void* p_step,const void* p_step_fast,const char* format,ImGuiInputTextFlags flags)\n{\n    return ImGui::InputScalar(label,data_type,p_data,p_step,p_step_fast,format,flags);\n}\nCIMGUI_API bool igInputScalarN(const char* label,ImGuiDataType data_type,void* p_data,int components,const void* p_step,const void* p_step_fast,const char* format,ImGuiInputTextFlags flags)\n{\n    return ImGui::InputScalarN(label,data_type,p_data,components,p_step,p_step_fast,format,flags);\n}\nCIMGUI_API bool igColorEdit3(const char* label,float col[3],ImGuiColorEditFlags flags)\n{\n    return ImGui::ColorEdit3(label,col,flags);\n}\nCIMGUI_API bool igColorEdit4(const char* label,float col[4],ImGuiColorEditFlags flags)\n{\n    return ImGui::ColorEdit4(label,col,flags);\n}\nCIMGUI_API bool igColorPicker3(const char* label,float col[3],ImGuiColorEditFlags flags)\n{\n    return ImGui::ColorPicker3(label,col,flags);\n}\nCIMGUI_API bool igColorPicker4(const char* label,float col[4],ImGuiColorEditFlags flags,const float* ref_col)\n{\n    return ImGui::ColorPicker4(label,col,flags,ref_col);\n}\nCIMGUI_API bool igColorButton(const char* desc_id,const ImVec4 col,ImGuiColorEditFlags flags,const ImVec2 size)\n{\n    return ImGui::ColorButton(desc_id,col,flags,size);\n}\nCIMGUI_API void igSetColorEditOptions(ImGuiColorEditFlags flags)\n{\n    return ImGui::SetColorEditOptions(flags);\n}\nCIMGUI_API bool igTreeNode_Str(const char* label)\n{\n    return ImGui::TreeNode(label);\n}\nCIMGUI_API bool igTreeNode_StrStr(const char* str_id,const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool ret = ImGui::TreeNodeV(str_id,fmt,args);\n    va_end(args);\n    return ret;\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API bool igTreeNode_StrStr0(const char* str_id,const char* fmt)\n{\n    return igTreeNode_StrStr(str_id,fmt);\n}\n#endif\nCIMGUI_API bool igTreeNode_Ptr(const void* ptr_id,const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool ret = ImGui::TreeNodeV(ptr_id,fmt,args);\n    va_end(args);\n    return ret;\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API bool igTreeNode_Ptr0(const void* ptr_id,const char* fmt)\n{\n    return igTreeNode_Ptr(ptr_id,fmt);\n}\n#endif\nCIMGUI_API bool igTreeNodeV_Str(const char* str_id,const char* fmt,va_list args)\n{\n    return ImGui::TreeNodeV(str_id,fmt,args);\n}\nCIMGUI_API bool igTreeNodeV_Ptr(const void* ptr_id,const char* fmt,va_list args)\n{\n    return ImGui::TreeNodeV(ptr_id,fmt,args);\n}\nCIMGUI_API bool igTreeNodeEx_Str(const char* label,ImGuiTreeNodeFlags flags)\n{\n    return ImGui::TreeNodeEx(label,flags);\n}\nCIMGUI_API bool igTreeNodeEx_StrStr(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool ret = ImGui::TreeNodeExV(str_id,flags,fmt,args);\n    va_end(args);\n    return ret;\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API bool igTreeNodeEx_StrStr0(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt)\n{\n    return igTreeNodeEx_StrStr(str_id,flags,fmt);\n}\n#endif\nCIMGUI_API bool igTreeNodeEx_Ptr(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool ret = ImGui::TreeNodeExV(ptr_id,flags,fmt,args);\n    va_end(args);\n    return ret;\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API bool igTreeNodeEx_Ptr0(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt)\n{\n    return igTreeNodeEx_Ptr(ptr_id,flags,fmt);\n}\n#endif\nCIMGUI_API bool igTreeNodeExV_Str(const char* str_id,ImGuiTreeNodeFlags flags,const char* fmt,va_list args)\n{\n    return ImGui::TreeNodeExV(str_id,flags,fmt,args);\n}\nCIMGUI_API bool igTreeNodeExV_Ptr(const void* ptr_id,ImGuiTreeNodeFlags flags,const char* fmt,va_list args)\n{\n    return ImGui::TreeNodeExV(ptr_id,flags,fmt,args);\n}\nCIMGUI_API void igTreePush_Str(const char* str_id)\n{\n    return ImGui::TreePush(str_id);\n}\nCIMGUI_API void igTreePush_Ptr(const void* ptr_id)\n{\n    return ImGui::TreePush(ptr_id);\n}\nCIMGUI_API void igTreePop()\n{\n    return ImGui::TreePop();\n}\nCIMGUI_API float igGetTreeNodeToLabelSpacing()\n{\n    return ImGui::GetTreeNodeToLabelSpacing();\n}\nCIMGUI_API bool igCollapsingHeader_TreeNodeFlags(const char* label,ImGuiTreeNodeFlags flags)\n{\n    return ImGui::CollapsingHeader(label,flags);\n}\nCIMGUI_API bool igCollapsingHeader_BoolPtr(const char* label,bool* p_visible,ImGuiTreeNodeFlags flags)\n{\n    return ImGui::CollapsingHeader(label,p_visible,flags);\n}\nCIMGUI_API void igSetNextItemOpen(bool is_open,ImGuiCond cond)\n{\n    return ImGui::SetNextItemOpen(is_open,cond);\n}\nCIMGUI_API void igSetNextItemStorageID(ImGuiID storage_id)\n{\n    return ImGui::SetNextItemStorageID(storage_id);\n}\nCIMGUI_API bool igSelectable_Bool(const char* label,bool selected,ImGuiSelectableFlags flags,const ImVec2 size)\n{\n    return ImGui::Selectable(label,selected,flags,size);\n}\nCIMGUI_API bool igSelectable_BoolPtr(const char* label,bool* p_selected,ImGuiSelectableFlags flags,const ImVec2 size)\n{\n    return ImGui::Selectable(label,p_selected,flags,size);\n}\nCIMGUI_API ImGuiMultiSelectIO* igBeginMultiSelect(ImGuiMultiSelectFlags flags,int selection_size,int items_count)\n{\n    return ImGui::BeginMultiSelect(flags,selection_size,items_count);\n}\nCIMGUI_API ImGuiMultiSelectIO* igEndMultiSelect()\n{\n    return ImGui::EndMultiSelect();\n}\nCIMGUI_API void igSetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data)\n{\n    return ImGui::SetNextItemSelectionUserData(selection_user_data);\n}\nCIMGUI_API bool igIsItemToggledSelection()\n{\n    return ImGui::IsItemToggledSelection();\n}\nCIMGUI_API bool igBeginListBox(const char* label,const ImVec2 size)\n{\n    return ImGui::BeginListBox(label,size);\n}\nCIMGUI_API void igEndListBox()\n{\n    return ImGui::EndListBox();\n}\nCIMGUI_API bool igListBox_Str_arr(const char* label,int* current_item,const char* const items[],int items_count,int height_in_items)\n{\n    return ImGui::ListBox(label,current_item,items,items_count,height_in_items);\n}\nCIMGUI_API bool igListBox_FnStrPtr(const char* label,int* current_item,const char*(*getter)(void* user_data,int idx),void* user_data,int items_count,int height_in_items)\n{\n    return ImGui::ListBox(label,current_item,getter,user_data,items_count,height_in_items);\n}\nCIMGUI_API void igPlotLines_FloatPtr(const char* label,const float* values,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride)\n{\n    return ImGui::PlotLines(label,values,values_count,values_offset,overlay_text,scale_min,scale_max,graph_size,stride);\n}\nCIMGUI_API void igPlotLines_FnFloatPtr(const char* label,float(*values_getter)(void* data,int idx),void* data,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size)\n{\n    return ImGui::PlotLines(label,values_getter,data,values_count,values_offset,overlay_text,scale_min,scale_max,graph_size);\n}\nCIMGUI_API void igPlotHistogram_FloatPtr(const char* label,const float* values,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size,int stride)\n{\n    return ImGui::PlotHistogram(label,values,values_count,values_offset,overlay_text,scale_min,scale_max,graph_size,stride);\n}\nCIMGUI_API void igPlotHistogram_FnFloatPtr(const char* label,float(*values_getter)(void* data,int idx),void* data,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,ImVec2 graph_size)\n{\n    return ImGui::PlotHistogram(label,values_getter,data,values_count,values_offset,overlay_text,scale_min,scale_max,graph_size);\n}\nCIMGUI_API void igValue_Bool(const char* prefix,bool b)\n{\n    return ImGui::Value(prefix,b);\n}\nCIMGUI_API void igValue_Int(const char* prefix,int v)\n{\n    return ImGui::Value(prefix,v);\n}\nCIMGUI_API void igValue_Uint(const char* prefix,unsigned int v)\n{\n    return ImGui::Value(prefix,v);\n}\nCIMGUI_API void igValue_Float(const char* prefix,float v,const char* float_format)\n{\n    return ImGui::Value(prefix,v,float_format);\n}\nCIMGUI_API bool igBeginMenuBar()\n{\n    return ImGui::BeginMenuBar();\n}\nCIMGUI_API void igEndMenuBar()\n{\n    return ImGui::EndMenuBar();\n}\nCIMGUI_API bool igBeginMainMenuBar()\n{\n    return ImGui::BeginMainMenuBar();\n}\nCIMGUI_API void igEndMainMenuBar()\n{\n    return ImGui::EndMainMenuBar();\n}\nCIMGUI_API bool igBeginMenu(const char* label,bool enabled)\n{\n    return ImGui::BeginMenu(label,enabled);\n}\nCIMGUI_API void igEndMenu()\n{\n    return ImGui::EndMenu();\n}\nCIMGUI_API bool igMenuItem_Bool(const char* label,const char* shortcut,bool selected,bool enabled)\n{\n    return ImGui::MenuItem(label,shortcut,selected,enabled);\n}\nCIMGUI_API bool igMenuItem_BoolPtr(const char* label,const char* shortcut,bool* p_selected,bool enabled)\n{\n    return ImGui::MenuItem(label,shortcut,p_selected,enabled);\n}\nCIMGUI_API bool igBeginTooltip()\n{\n    return ImGui::BeginTooltip();\n}\nCIMGUI_API void igEndTooltip()\n{\n    return ImGui::EndTooltip();\n}\nCIMGUI_API void igSetTooltip(const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImGui::SetTooltipV(fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igSetTooltip0(const char* fmt)\n{\n    return igSetTooltip(fmt);\n}\n#endif\nCIMGUI_API void igSetTooltipV(const char* fmt,va_list args)\n{\n    return ImGui::SetTooltipV(fmt,args);\n}\nCIMGUI_API bool igBeginItemTooltip()\n{\n    return ImGui::BeginItemTooltip();\n}\nCIMGUI_API void igSetItemTooltip(const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImGui::SetItemTooltipV(fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igSetItemTooltip0(const char* fmt)\n{\n    return igSetItemTooltip(fmt);\n}\n#endif\nCIMGUI_API void igSetItemTooltipV(const char* fmt,va_list args)\n{\n    return ImGui::SetItemTooltipV(fmt,args);\n}\nCIMGUI_API bool igBeginPopup(const char* str_id,ImGuiWindowFlags flags)\n{\n    return ImGui::BeginPopup(str_id,flags);\n}\nCIMGUI_API bool igBeginPopupModal(const char* name,bool* p_open,ImGuiWindowFlags flags)\n{\n    return ImGui::BeginPopupModal(name,p_open,flags);\n}\nCIMGUI_API void igEndPopup()\n{\n    return ImGui::EndPopup();\n}\nCIMGUI_API void igOpenPopup_Str(const char* str_id,ImGuiPopupFlags popup_flags)\n{\n    return ImGui::OpenPopup(str_id,popup_flags);\n}\nCIMGUI_API void igOpenPopup_ID(ImGuiID id,ImGuiPopupFlags popup_flags)\n{\n    return ImGui::OpenPopup(id,popup_flags);\n}\nCIMGUI_API void igOpenPopupOnItemClick(const char* str_id,ImGuiPopupFlags popup_flags)\n{\n    return ImGui::OpenPopupOnItemClick(str_id,popup_flags);\n}\nCIMGUI_API void igCloseCurrentPopup()\n{\n    return ImGui::CloseCurrentPopup();\n}\nCIMGUI_API bool igBeginPopupContextItem(const char* str_id,ImGuiPopupFlags popup_flags)\n{\n    return ImGui::BeginPopupContextItem(str_id,popup_flags);\n}\nCIMGUI_API bool igBeginPopupContextWindow(const char* str_id,ImGuiPopupFlags popup_flags)\n{\n    return ImGui::BeginPopupContextWindow(str_id,popup_flags);\n}\nCIMGUI_API bool igBeginPopupContextVoid(const char* str_id,ImGuiPopupFlags popup_flags)\n{\n    return ImGui::BeginPopupContextVoid(str_id,popup_flags);\n}\nCIMGUI_API bool igIsPopupOpen_Str(const char* str_id,ImGuiPopupFlags flags)\n{\n    return ImGui::IsPopupOpen(str_id,flags);\n}\nCIMGUI_API bool igBeginTable(const char* str_id,int columns,ImGuiTableFlags flags,const ImVec2 outer_size,float inner_width)\n{\n    return ImGui::BeginTable(str_id,columns,flags,outer_size,inner_width);\n}\nCIMGUI_API void igEndTable()\n{\n    return ImGui::EndTable();\n}\nCIMGUI_API void igTableNextRow(ImGuiTableRowFlags row_flags,float min_row_height)\n{\n    return ImGui::TableNextRow(row_flags,min_row_height);\n}\nCIMGUI_API bool igTableNextColumn()\n{\n    return ImGui::TableNextColumn();\n}\nCIMGUI_API bool igTableSetColumnIndex(int column_n)\n{\n    return ImGui::TableSetColumnIndex(column_n);\n}\nCIMGUI_API void igTableSetupColumn(const char* label,ImGuiTableColumnFlags flags,float init_width_or_weight,ImGuiID user_id)\n{\n    return ImGui::TableSetupColumn(label,flags,init_width_or_weight,user_id);\n}\nCIMGUI_API void igTableSetupScrollFreeze(int cols,int rows)\n{\n    return ImGui::TableSetupScrollFreeze(cols,rows);\n}\nCIMGUI_API void igTableHeader(const char* label)\n{\n    return ImGui::TableHeader(label);\n}\nCIMGUI_API void igTableHeadersRow()\n{\n    return ImGui::TableHeadersRow();\n}\nCIMGUI_API void igTableAngledHeadersRow()\n{\n    return ImGui::TableAngledHeadersRow();\n}\nCIMGUI_API ImGuiTableSortSpecs* igTableGetSortSpecs()\n{\n    return ImGui::TableGetSortSpecs();\n}\nCIMGUI_API int igTableGetColumnCount()\n{\n    return ImGui::TableGetColumnCount();\n}\nCIMGUI_API int igTableGetColumnIndex()\n{\n    return ImGui::TableGetColumnIndex();\n}\nCIMGUI_API int igTableGetRowIndex()\n{\n    return ImGui::TableGetRowIndex();\n}\nCIMGUI_API const char* igTableGetColumnName_Int(int column_n)\n{\n    return ImGui::TableGetColumnName(column_n);\n}\nCIMGUI_API ImGuiTableColumnFlags igTableGetColumnFlags(int column_n)\n{\n    return ImGui::TableGetColumnFlags(column_n);\n}\nCIMGUI_API void igTableSetColumnEnabled(int column_n,bool v)\n{\n    return ImGui::TableSetColumnEnabled(column_n,v);\n}\nCIMGUI_API int igTableGetHoveredColumn()\n{\n    return ImGui::TableGetHoveredColumn();\n}\nCIMGUI_API void igTableSetBgColor(ImGuiTableBgTarget target,ImU32 color,int column_n)\n{\n    return ImGui::TableSetBgColor(target,color,column_n);\n}\nCIMGUI_API void igColumns(int count,const char* id,bool borders)\n{\n    return ImGui::Columns(count,id,borders);\n}\nCIMGUI_API void igNextColumn()\n{\n    return ImGui::NextColumn();\n}\nCIMGUI_API int igGetColumnIndex()\n{\n    return ImGui::GetColumnIndex();\n}\nCIMGUI_API float igGetColumnWidth(int column_index)\n{\n    return ImGui::GetColumnWidth(column_index);\n}\nCIMGUI_API void igSetColumnWidth(int column_index,float width)\n{\n    return ImGui::SetColumnWidth(column_index,width);\n}\nCIMGUI_API float igGetColumnOffset(int column_index)\n{\n    return ImGui::GetColumnOffset(column_index);\n}\nCIMGUI_API void igSetColumnOffset(int column_index,float offset_x)\n{\n    return ImGui::SetColumnOffset(column_index,offset_x);\n}\nCIMGUI_API int igGetColumnsCount()\n{\n    return ImGui::GetColumnsCount();\n}\nCIMGUI_API bool igBeginTabBar(const char* str_id,ImGuiTabBarFlags flags)\n{\n    return ImGui::BeginTabBar(str_id,flags);\n}\nCIMGUI_API void igEndTabBar()\n{\n    return ImGui::EndTabBar();\n}\nCIMGUI_API bool igBeginTabItem(const char* label,bool* p_open,ImGuiTabItemFlags flags)\n{\n    return ImGui::BeginTabItem(label,p_open,flags);\n}\nCIMGUI_API void igEndTabItem()\n{\n    return ImGui::EndTabItem();\n}\nCIMGUI_API bool igTabItemButton(const char* label,ImGuiTabItemFlags flags)\n{\n    return ImGui::TabItemButton(label,flags);\n}\nCIMGUI_API void igSetTabItemClosed(const char* tab_or_docked_window_label)\n{\n    return ImGui::SetTabItemClosed(tab_or_docked_window_label);\n}\nCIMGUI_API ImGuiID igDockSpace(ImGuiID dockspace_id,const ImVec2 size,ImGuiDockNodeFlags flags,const ImGuiWindowClass* window_class)\n{\n    return ImGui::DockSpace(dockspace_id,size,flags,window_class);\n}\nCIMGUI_API ImGuiID igDockSpaceOverViewport(ImGuiID dockspace_id,const ImGuiViewport* viewport,ImGuiDockNodeFlags flags,const ImGuiWindowClass* window_class)\n{\n    return ImGui::DockSpaceOverViewport(dockspace_id,viewport,flags,window_class);\n}\nCIMGUI_API void igSetNextWindowDockID(ImGuiID dock_id,ImGuiCond cond)\n{\n    return ImGui::SetNextWindowDockID(dock_id,cond);\n}\nCIMGUI_API void igSetNextWindowClass(const ImGuiWindowClass* window_class)\n{\n    return ImGui::SetNextWindowClass(window_class);\n}\nCIMGUI_API ImGuiID igGetWindowDockID()\n{\n    return ImGui::GetWindowDockID();\n}\nCIMGUI_API bool igIsWindowDocked()\n{\n    return ImGui::IsWindowDocked();\n}\nCIMGUI_API void igLogToTTY(int auto_open_depth)\n{\n    return ImGui::LogToTTY(auto_open_depth);\n}\nCIMGUI_API void igLogToFile(int auto_open_depth,const char* filename)\n{\n    return ImGui::LogToFile(auto_open_depth,filename);\n}\nCIMGUI_API void igLogToClipboard(int auto_open_depth)\n{\n    return ImGui::LogToClipboard(auto_open_depth);\n}\nCIMGUI_API void igLogFinish()\n{\n    return ImGui::LogFinish();\n}\nCIMGUI_API void igLogButtons()\n{\n    return ImGui::LogButtons();\n}\nCIMGUI_API void igLogText(const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImGui::LogTextV(fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igLogText0(const char* fmt)\n{\n    return igLogText(fmt);\n}\n#endif\nCIMGUI_API void igLogTextV(const char* fmt,va_list args)\n{\n    return ImGui::LogTextV(fmt,args);\n}\nCIMGUI_API bool igBeginDragDropSource(ImGuiDragDropFlags flags)\n{\n    return ImGui::BeginDragDropSource(flags);\n}\nCIMGUI_API bool igSetDragDropPayload(const char* type,const void* data,size_t sz,ImGuiCond cond)\n{\n    return ImGui::SetDragDropPayload(type,data,sz,cond);\n}\nCIMGUI_API void igEndDragDropSource()\n{\n    return ImGui::EndDragDropSource();\n}\nCIMGUI_API bool igBeginDragDropTarget()\n{\n    return ImGui::BeginDragDropTarget();\n}\nCIMGUI_API const ImGuiPayload* igAcceptDragDropPayload(const char* type,ImGuiDragDropFlags flags)\n{\n    return ImGui::AcceptDragDropPayload(type,flags);\n}\nCIMGUI_API void igEndDragDropTarget()\n{\n    return ImGui::EndDragDropTarget();\n}\nCIMGUI_API const ImGuiPayload* igGetDragDropPayload()\n{\n    return ImGui::GetDragDropPayload();\n}\nCIMGUI_API void igBeginDisabled(bool disabled)\n{\n    return ImGui::BeginDisabled(disabled);\n}\nCIMGUI_API void igEndDisabled()\n{\n    return ImGui::EndDisabled();\n}\nCIMGUI_API void igPushClipRect(const ImVec2 clip_rect_min,const ImVec2 clip_rect_max,bool intersect_with_current_clip_rect)\n{\n    return ImGui::PushClipRect(clip_rect_min,clip_rect_max,intersect_with_current_clip_rect);\n}\nCIMGUI_API void igPopClipRect()\n{\n    return ImGui::PopClipRect();\n}\nCIMGUI_API void igSetItemDefaultFocus()\n{\n    return ImGui::SetItemDefaultFocus();\n}\nCIMGUI_API void igSetKeyboardFocusHere(int offset)\n{\n    return ImGui::SetKeyboardFocusHere(offset);\n}\nCIMGUI_API void igSetNavCursorVisible(bool visible)\n{\n    return ImGui::SetNavCursorVisible(visible);\n}\nCIMGUI_API void igSetNextItemAllowOverlap()\n{\n    return ImGui::SetNextItemAllowOverlap();\n}\nCIMGUI_API bool igIsItemHovered(ImGuiHoveredFlags flags)\n{\n    return ImGui::IsItemHovered(flags);\n}\nCIMGUI_API bool igIsItemActive()\n{\n    return ImGui::IsItemActive();\n}\nCIMGUI_API bool igIsItemFocused()\n{\n    return ImGui::IsItemFocused();\n}\nCIMGUI_API bool igIsItemClicked(ImGuiMouseButton mouse_button)\n{\n    return ImGui::IsItemClicked(mouse_button);\n}\nCIMGUI_API bool igIsItemVisible()\n{\n    return ImGui::IsItemVisible();\n}\nCIMGUI_API bool igIsItemEdited()\n{\n    return ImGui::IsItemEdited();\n}\nCIMGUI_API bool igIsItemActivated()\n{\n    return ImGui::IsItemActivated();\n}\nCIMGUI_API bool igIsItemDeactivated()\n{\n    return ImGui::IsItemDeactivated();\n}\nCIMGUI_API bool igIsItemDeactivatedAfterEdit()\n{\n    return ImGui::IsItemDeactivatedAfterEdit();\n}\nCIMGUI_API bool igIsItemToggledOpen()\n{\n    return ImGui::IsItemToggledOpen();\n}\nCIMGUI_API bool igIsAnyItemHovered()\n{\n    return ImGui::IsAnyItemHovered();\n}\nCIMGUI_API bool igIsAnyItemActive()\n{\n    return ImGui::IsAnyItemActive();\n}\nCIMGUI_API bool igIsAnyItemFocused()\n{\n    return ImGui::IsAnyItemFocused();\n}\nCIMGUI_API ImGuiID igGetItemID()\n{\n    return ImGui::GetItemID();\n}\nCIMGUI_API void igGetItemRectMin(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetItemRectMin();\n}\nCIMGUI_API void igGetItemRectMax(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetItemRectMax();\n}\nCIMGUI_API void igGetItemRectSize(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetItemRectSize();\n}\nCIMGUI_API ImGuiViewport* igGetMainViewport()\n{\n    return ImGui::GetMainViewport();\n}\nCIMGUI_API ImDrawList* igGetBackgroundDrawList(ImGuiViewport* viewport)\n{\n    return ImGui::GetBackgroundDrawList(viewport);\n}\nCIMGUI_API ImDrawList* igGetForegroundDrawList_ViewportPtr(ImGuiViewport* viewport)\n{\n    return ImGui::GetForegroundDrawList(viewport);\n}\nCIMGUI_API bool igIsRectVisible_Nil(const ImVec2 size)\n{\n    return ImGui::IsRectVisible(size);\n}\nCIMGUI_API bool igIsRectVisible_Vec2(const ImVec2 rect_min,const ImVec2 rect_max)\n{\n    return ImGui::IsRectVisible(rect_min,rect_max);\n}\nCIMGUI_API double igGetTime()\n{\n    return ImGui::GetTime();\n}\nCIMGUI_API int igGetFrameCount()\n{\n    return ImGui::GetFrameCount();\n}\nCIMGUI_API ImDrawListSharedData* igGetDrawListSharedData()\n{\n    return ImGui::GetDrawListSharedData();\n}\nCIMGUI_API const char* igGetStyleColorName(ImGuiCol idx)\n{\n    return ImGui::GetStyleColorName(idx);\n}\nCIMGUI_API void igSetStateStorage(ImGuiStorage* storage)\n{\n    return ImGui::SetStateStorage(storage);\n}\nCIMGUI_API ImGuiStorage* igGetStateStorage()\n{\n    return ImGui::GetStateStorage();\n}\nCIMGUI_API void igCalcTextSize(ImVec2 *pOut,const char* text,const char* text_end,bool hide_text_after_double_hash,float wrap_width)\n{\n    *pOut = ImGui::CalcTextSize(text,text_end,hide_text_after_double_hash,wrap_width);\n}\nCIMGUI_API void igColorConvertU32ToFloat4(ImVec4 *pOut,ImU32 in)\n{\n    *pOut = ImGui::ColorConvertU32ToFloat4(in);\n}\nCIMGUI_API ImU32 igColorConvertFloat4ToU32(const ImVec4 in)\n{\n    return ImGui::ColorConvertFloat4ToU32(in);\n}\nCIMGUI_API void igColorConvertRGBtoHSV(float r,float g,float b,float* out_h,float* out_s,float* out_v)\n{\n    return ImGui::ColorConvertRGBtoHSV(r,g,b,*out_h,*out_s,*out_v);\n}\nCIMGUI_API void igColorConvertHSVtoRGB(float h,float s,float v,float* out_r,float* out_g,float* out_b)\n{\n    return ImGui::ColorConvertHSVtoRGB(h,s,v,*out_r,*out_g,*out_b);\n}\nCIMGUI_API bool igIsKeyDown_Nil(ImGuiKey key)\n{\n    return ImGui::IsKeyDown(key);\n}\nCIMGUI_API bool igIsKeyPressed_Bool(ImGuiKey key,bool repeat)\n{\n    return ImGui::IsKeyPressed(key,repeat);\n}\nCIMGUI_API bool igIsKeyReleased_Nil(ImGuiKey key)\n{\n    return ImGui::IsKeyReleased(key);\n}\nCIMGUI_API bool igIsKeyChordPressed_Nil(ImGuiKeyChord key_chord)\n{\n    return ImGui::IsKeyChordPressed(key_chord);\n}\nCIMGUI_API int igGetKeyPressedAmount(ImGuiKey key,float repeat_delay,float rate)\n{\n    return ImGui::GetKeyPressedAmount(key,repeat_delay,rate);\n}\nCIMGUI_API const char* igGetKeyName(ImGuiKey key)\n{\n    return ImGui::GetKeyName(key);\n}\nCIMGUI_API void igSetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)\n{\n    return ImGui::SetNextFrameWantCaptureKeyboard(want_capture_keyboard);\n}\nCIMGUI_API bool igShortcut_Nil(ImGuiKeyChord key_chord,ImGuiInputFlags flags)\n{\n    return ImGui::Shortcut(key_chord,flags);\n}\nCIMGUI_API void igSetNextItemShortcut(ImGuiKeyChord key_chord,ImGuiInputFlags flags)\n{\n    return ImGui::SetNextItemShortcut(key_chord,flags);\n}\nCIMGUI_API void igSetItemKeyOwner_Nil(ImGuiKey key)\n{\n    return ImGui::SetItemKeyOwner(key);\n}\nCIMGUI_API bool igIsMouseDown_Nil(ImGuiMouseButton button)\n{\n    return ImGui::IsMouseDown(button);\n}\nCIMGUI_API bool igIsMouseClicked_Bool(ImGuiMouseButton button,bool repeat)\n{\n    return ImGui::IsMouseClicked(button,repeat);\n}\nCIMGUI_API bool igIsMouseReleased_Nil(ImGuiMouseButton button)\n{\n    return ImGui::IsMouseReleased(button);\n}\nCIMGUI_API bool igIsMouseDoubleClicked_Nil(ImGuiMouseButton button)\n{\n    return ImGui::IsMouseDoubleClicked(button);\n}\nCIMGUI_API bool igIsMouseReleasedWithDelay(ImGuiMouseButton button,float delay)\n{\n    return ImGui::IsMouseReleasedWithDelay(button,delay);\n}\nCIMGUI_API int igGetMouseClickedCount(ImGuiMouseButton button)\n{\n    return ImGui::GetMouseClickedCount(button);\n}\nCIMGUI_API bool igIsMouseHoveringRect(const ImVec2 r_min,const ImVec2 r_max,bool clip)\n{\n    return ImGui::IsMouseHoveringRect(r_min,r_max,clip);\n}\nCIMGUI_API bool igIsMousePosValid(const ImVec2* mouse_pos)\n{\n    return ImGui::IsMousePosValid(mouse_pos);\n}\nCIMGUI_API bool igIsAnyMouseDown()\n{\n    return ImGui::IsAnyMouseDown();\n}\nCIMGUI_API void igGetMousePos(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetMousePos();\n}\nCIMGUI_API void igGetMousePosOnOpeningCurrentPopup(ImVec2 *pOut)\n{\n    *pOut = ImGui::GetMousePosOnOpeningCurrentPopup();\n}\nCIMGUI_API bool igIsMouseDragging(ImGuiMouseButton button,float lock_threshold)\n{\n    return ImGui::IsMouseDragging(button,lock_threshold);\n}\nCIMGUI_API void igGetMouseDragDelta(ImVec2 *pOut,ImGuiMouseButton button,float lock_threshold)\n{\n    *pOut = ImGui::GetMouseDragDelta(button,lock_threshold);\n}\nCIMGUI_API void igResetMouseDragDelta(ImGuiMouseButton button)\n{\n    return ImGui::ResetMouseDragDelta(button);\n}\nCIMGUI_API ImGuiMouseCursor igGetMouseCursor()\n{\n    return ImGui::GetMouseCursor();\n}\nCIMGUI_API void igSetMouseCursor(ImGuiMouseCursor cursor_type)\n{\n    return ImGui::SetMouseCursor(cursor_type);\n}\nCIMGUI_API void igSetNextFrameWantCaptureMouse(bool want_capture_mouse)\n{\n    return ImGui::SetNextFrameWantCaptureMouse(want_capture_mouse);\n}\nCIMGUI_API const char* igGetClipboardText()\n{\n    return ImGui::GetClipboardText();\n}\nCIMGUI_API void igSetClipboardText(const char* text)\n{\n    return ImGui::SetClipboardText(text);\n}\nCIMGUI_API void igLoadIniSettingsFromDisk(const char* ini_filename)\n{\n    return ImGui::LoadIniSettingsFromDisk(ini_filename);\n}\nCIMGUI_API void igLoadIniSettingsFromMemory(const char* ini_data,size_t ini_size)\n{\n    return ImGui::LoadIniSettingsFromMemory(ini_data,ini_size);\n}\nCIMGUI_API void igSaveIniSettingsToDisk(const char* ini_filename)\n{\n    return ImGui::SaveIniSettingsToDisk(ini_filename);\n}\nCIMGUI_API const char* igSaveIniSettingsToMemory(size_t* out_ini_size)\n{\n    return ImGui::SaveIniSettingsToMemory(out_ini_size);\n}\nCIMGUI_API void igDebugTextEncoding(const char* text)\n{\n    return ImGui::DebugTextEncoding(text);\n}\nCIMGUI_API void igDebugFlashStyleColor(ImGuiCol idx)\n{\n    return ImGui::DebugFlashStyleColor(idx);\n}\nCIMGUI_API void igDebugStartItemPicker()\n{\n    return ImGui::DebugStartItemPicker();\n}\nCIMGUI_API bool igDebugCheckVersionAndDataLayout(const char* version_str,size_t sz_io,size_t sz_style,size_t sz_vec2,size_t sz_vec4,size_t sz_drawvert,size_t sz_drawidx)\n{\n    return ImGui::DebugCheckVersionAndDataLayout(version_str,sz_io,sz_style,sz_vec2,sz_vec4,sz_drawvert,sz_drawidx);\n}\nCIMGUI_API void igDebugLog(const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImGui::DebugLogV(fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igDebugLog0(const char* fmt)\n{\n    return igDebugLog(fmt);\n}\n#endif\nCIMGUI_API void igDebugLogV(const char* fmt,va_list args)\n{\n    return ImGui::DebugLogV(fmt,args);\n}\nCIMGUI_API void igSetAllocatorFunctions(ImGuiMemAllocFunc alloc_func,ImGuiMemFreeFunc free_func,void* user_data)\n{\n    return ImGui::SetAllocatorFunctions(alloc_func,free_func,user_data);\n}\nCIMGUI_API void igGetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func,ImGuiMemFreeFunc* p_free_func,void** p_user_data)\n{\n    return ImGui::GetAllocatorFunctions(p_alloc_func,p_free_func,p_user_data);\n}\nCIMGUI_API void* igMemAlloc(size_t size)\n{\n    return ImGui::MemAlloc(size);\n}\nCIMGUI_API void igMemFree(void* ptr)\n{\n    return ImGui::MemFree(ptr);\n}\nCIMGUI_API void igUpdatePlatformWindows()\n{\n    return ImGui::UpdatePlatformWindows();\n}\nCIMGUI_API void igRenderPlatformWindowsDefault(void* platform_render_arg,void* renderer_render_arg)\n{\n    return ImGui::RenderPlatformWindowsDefault(platform_render_arg,renderer_render_arg);\n}\nCIMGUI_API void igDestroyPlatformWindows()\n{\n    return ImGui::DestroyPlatformWindows();\n}\nCIMGUI_API ImGuiViewport* igFindViewportByID(ImGuiID id)\n{\n    return ImGui::FindViewportByID(id);\n}\nCIMGUI_API ImGuiViewport* igFindViewportByPlatformHandle(void* platform_handle)\n{\n    return ImGui::FindViewportByPlatformHandle(platform_handle);\n}\nCIMGUI_API ImGuiTableSortSpecs* ImGuiTableSortSpecs_ImGuiTableSortSpecs(void)\n{\n    return IM_NEW(ImGuiTableSortSpecs)();\n}\nCIMGUI_API void ImGuiTableSortSpecs_destroy(ImGuiTableSortSpecs* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiTableColumnSortSpecs* ImGuiTableColumnSortSpecs_ImGuiTableColumnSortSpecs(void)\n{\n    return IM_NEW(ImGuiTableColumnSortSpecs)();\n}\nCIMGUI_API void ImGuiTableColumnSortSpecs_destroy(ImGuiTableColumnSortSpecs* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiStyle* ImGuiStyle_ImGuiStyle(void)\n{\n    return IM_NEW(ImGuiStyle)();\n}\nCIMGUI_API void ImGuiStyle_destroy(ImGuiStyle* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiStyle_ScaleAllSizes(ImGuiStyle* self,float scale_factor)\n{\n    return self->ScaleAllSizes(scale_factor);\n}\nCIMGUI_API void ImGuiIO_AddKeyEvent(ImGuiIO* self,ImGuiKey key,bool down)\n{\n    return self->AddKeyEvent(key,down);\n}\nCIMGUI_API void ImGuiIO_AddKeyAnalogEvent(ImGuiIO* self,ImGuiKey key,bool down,float v)\n{\n    return self->AddKeyAnalogEvent(key,down,v);\n}\nCIMGUI_API void ImGuiIO_AddMousePosEvent(ImGuiIO* self,float x,float y)\n{\n    return self->AddMousePosEvent(x,y);\n}\nCIMGUI_API void ImGuiIO_AddMouseButtonEvent(ImGuiIO* self,int button,bool down)\n{\n    return self->AddMouseButtonEvent(button,down);\n}\nCIMGUI_API void ImGuiIO_AddMouseWheelEvent(ImGuiIO* self,float wheel_x,float wheel_y)\n{\n    return self->AddMouseWheelEvent(wheel_x,wheel_y);\n}\nCIMGUI_API void ImGuiIO_AddMouseSourceEvent(ImGuiIO* self,ImGuiMouseSource source)\n{\n    return self->AddMouseSourceEvent(source);\n}\nCIMGUI_API void ImGuiIO_AddMouseViewportEvent(ImGuiIO* self,ImGuiID id)\n{\n    return self->AddMouseViewportEvent(id);\n}\nCIMGUI_API void ImGuiIO_AddFocusEvent(ImGuiIO* self,bool focused)\n{\n    return self->AddFocusEvent(focused);\n}\nCIMGUI_API void ImGuiIO_AddInputCharacter(ImGuiIO* self,unsigned int c)\n{\n    return self->AddInputCharacter(c);\n}\nCIMGUI_API void ImGuiIO_AddInputCharacterUTF16(ImGuiIO* self,ImWchar16 c)\n{\n    return self->AddInputCharacterUTF16(c);\n}\nCIMGUI_API void ImGuiIO_AddInputCharactersUTF8(ImGuiIO* self,const char* str)\n{\n    return self->AddInputCharactersUTF8(str);\n}\nCIMGUI_API void ImGuiIO_SetKeyEventNativeData(ImGuiIO* self,ImGuiKey key,int native_keycode,int native_scancode,int native_legacy_index)\n{\n    return self->SetKeyEventNativeData(key,native_keycode,native_scancode,native_legacy_index);\n}\nCIMGUI_API void ImGuiIO_SetAppAcceptingEvents(ImGuiIO* self,bool accepting_events)\n{\n    return self->SetAppAcceptingEvents(accepting_events);\n}\nCIMGUI_API void ImGuiIO_ClearEventsQueue(ImGuiIO* self)\n{\n    return self->ClearEventsQueue();\n}\nCIMGUI_API void ImGuiIO_ClearInputKeys(ImGuiIO* self)\n{\n    return self->ClearInputKeys();\n}\nCIMGUI_API void ImGuiIO_ClearInputMouse(ImGuiIO* self)\n{\n    return self->ClearInputMouse();\n}\nCIMGUI_API ImGuiIO* ImGuiIO_ImGuiIO(void)\n{\n    return IM_NEW(ImGuiIO)();\n}\nCIMGUI_API void ImGuiIO_destroy(ImGuiIO* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiInputTextCallbackData* ImGuiInputTextCallbackData_ImGuiInputTextCallbackData(void)\n{\n    return IM_NEW(ImGuiInputTextCallbackData)();\n}\nCIMGUI_API void ImGuiInputTextCallbackData_destroy(ImGuiInputTextCallbackData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiInputTextCallbackData_DeleteChars(ImGuiInputTextCallbackData* self,int pos,int bytes_count)\n{\n    return self->DeleteChars(pos,bytes_count);\n}\nCIMGUI_API void ImGuiInputTextCallbackData_InsertChars(ImGuiInputTextCallbackData* self,int pos,const char* text,const char* text_end)\n{\n    return self->InsertChars(pos,text,text_end);\n}\nCIMGUI_API void ImGuiInputTextCallbackData_SelectAll(ImGuiInputTextCallbackData* self)\n{\n    return self->SelectAll();\n}\nCIMGUI_API void ImGuiInputTextCallbackData_ClearSelection(ImGuiInputTextCallbackData* self)\n{\n    return self->ClearSelection();\n}\nCIMGUI_API bool ImGuiInputTextCallbackData_HasSelection(ImGuiInputTextCallbackData* self)\n{\n    return self->HasSelection();\n}\nCIMGUI_API ImGuiWindowClass* ImGuiWindowClass_ImGuiWindowClass(void)\n{\n    return IM_NEW(ImGuiWindowClass)();\n}\nCIMGUI_API void ImGuiWindowClass_destroy(ImGuiWindowClass* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiPayload* ImGuiPayload_ImGuiPayload(void)\n{\n    return IM_NEW(ImGuiPayload)();\n}\nCIMGUI_API void ImGuiPayload_destroy(ImGuiPayload* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiPayload_Clear(ImGuiPayload* self)\n{\n    return self->Clear();\n}\nCIMGUI_API bool ImGuiPayload_IsDataType(ImGuiPayload* self,const char* type)\n{\n    return self->IsDataType(type);\n}\nCIMGUI_API bool ImGuiPayload_IsPreview(ImGuiPayload* self)\n{\n    return self->IsPreview();\n}\nCIMGUI_API bool ImGuiPayload_IsDelivery(ImGuiPayload* self)\n{\n    return self->IsDelivery();\n}\nCIMGUI_API ImGuiOnceUponAFrame* ImGuiOnceUponAFrame_ImGuiOnceUponAFrame(void)\n{\n    return IM_NEW(ImGuiOnceUponAFrame)();\n}\nCIMGUI_API void ImGuiOnceUponAFrame_destroy(ImGuiOnceUponAFrame* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiTextFilter* ImGuiTextFilter_ImGuiTextFilter(const char* default_filter)\n{\n    return IM_NEW(ImGuiTextFilter)(default_filter);\n}\nCIMGUI_API void ImGuiTextFilter_destroy(ImGuiTextFilter* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API bool ImGuiTextFilter_Draw(ImGuiTextFilter* self,const char* label,float width)\n{\n    return self->Draw(label,width);\n}\nCIMGUI_API bool ImGuiTextFilter_PassFilter(ImGuiTextFilter* self,const char* text,const char* text_end)\n{\n    return self->PassFilter(text,text_end);\n}\nCIMGUI_API void ImGuiTextFilter_Build(ImGuiTextFilter* self)\n{\n    return self->Build();\n}\nCIMGUI_API void ImGuiTextFilter_Clear(ImGuiTextFilter* self)\n{\n    return self->Clear();\n}\nCIMGUI_API bool ImGuiTextFilter_IsActive(ImGuiTextFilter* self)\n{\n    return self->IsActive();\n}\nCIMGUI_API ImGuiTextRange* ImGuiTextRange_ImGuiTextRange_Nil(void)\n{\n    return IM_NEW(ImGuiTextRange)();\n}\nCIMGUI_API void ImGuiTextRange_destroy(ImGuiTextRange* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiTextRange* ImGuiTextRange_ImGuiTextRange_Str(const char* _b,const char* _e)\n{\n    return IM_NEW(ImGuiTextRange)(_b,_e);\n}\nCIMGUI_API bool ImGuiTextRange_empty(ImGuiTextRange* self)\n{\n    return self->empty();\n}\nCIMGUI_API void ImGuiTextRange_split(ImGuiTextRange* self,char separator,ImVector_ImGuiTextRange* out)\n{\n    return self->split(separator,out);\n}\nCIMGUI_API ImGuiTextBuffer* ImGuiTextBuffer_ImGuiTextBuffer(void)\n{\n    return IM_NEW(ImGuiTextBuffer)();\n}\nCIMGUI_API void ImGuiTextBuffer_destroy(ImGuiTextBuffer* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API const char* ImGuiTextBuffer_begin(ImGuiTextBuffer* self)\n{\n    return self->begin();\n}\nCIMGUI_API const char* ImGuiTextBuffer_end(ImGuiTextBuffer* self)\n{\n    return self->end();\n}\nCIMGUI_API int ImGuiTextBuffer_size(ImGuiTextBuffer* self)\n{\n    return self->size();\n}\nCIMGUI_API bool ImGuiTextBuffer_empty(ImGuiTextBuffer* self)\n{\n    return self->empty();\n}\nCIMGUI_API void ImGuiTextBuffer_clear(ImGuiTextBuffer* self)\n{\n    return self->clear();\n}\nCIMGUI_API void ImGuiTextBuffer_resize(ImGuiTextBuffer* self,int size)\n{\n    return self->resize(size);\n}\nCIMGUI_API void ImGuiTextBuffer_reserve(ImGuiTextBuffer* self,int capacity)\n{\n    return self->reserve(capacity);\n}\nCIMGUI_API const char* ImGuiTextBuffer_c_str(ImGuiTextBuffer* self)\n{\n    return self->c_str();\n}\nCIMGUI_API void ImGuiTextBuffer_append(ImGuiTextBuffer* self,const char* str,const char* str_end)\n{\n    return self->append(str,str_end);\n}\nCIMGUI_API void ImGuiTextBuffer_appendfv(ImGuiTextBuffer* self,const char* fmt,va_list args)\n{\n    return self->appendfv(fmt,args);\n}\nCIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Int(ImGuiID _key,int _val)\n{\n    return IM_NEW(ImGuiStoragePair)(_key,_val);\n}\nCIMGUI_API void ImGuiStoragePair_destroy(ImGuiStoragePair* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Float(ImGuiID _key,float _val)\n{\n    return IM_NEW(ImGuiStoragePair)(_key,_val);\n}\nCIMGUI_API ImGuiStoragePair* ImGuiStoragePair_ImGuiStoragePair_Ptr(ImGuiID _key,void* _val)\n{\n    return IM_NEW(ImGuiStoragePair)(_key,_val);\n}\nCIMGUI_API void ImGuiStorage_Clear(ImGuiStorage* self)\n{\n    return self->Clear();\n}\nCIMGUI_API int ImGuiStorage_GetInt(ImGuiStorage* self,ImGuiID key,int default_val)\n{\n    return self->GetInt(key,default_val);\n}\nCIMGUI_API void ImGuiStorage_SetInt(ImGuiStorage* self,ImGuiID key,int val)\n{\n    return self->SetInt(key,val);\n}\nCIMGUI_API bool ImGuiStorage_GetBool(ImGuiStorage* self,ImGuiID key,bool default_val)\n{\n    return self->GetBool(key,default_val);\n}\nCIMGUI_API void ImGuiStorage_SetBool(ImGuiStorage* self,ImGuiID key,bool val)\n{\n    return self->SetBool(key,val);\n}\nCIMGUI_API float ImGuiStorage_GetFloat(ImGuiStorage* self,ImGuiID key,float default_val)\n{\n    return self->GetFloat(key,default_val);\n}\nCIMGUI_API void ImGuiStorage_SetFloat(ImGuiStorage* self,ImGuiID key,float val)\n{\n    return self->SetFloat(key,val);\n}\nCIMGUI_API void* ImGuiStorage_GetVoidPtr(ImGuiStorage* self,ImGuiID key)\n{\n    return self->GetVoidPtr(key);\n}\nCIMGUI_API void ImGuiStorage_SetVoidPtr(ImGuiStorage* self,ImGuiID key,void* val)\n{\n    return self->SetVoidPtr(key,val);\n}\nCIMGUI_API int* ImGuiStorage_GetIntRef(ImGuiStorage* self,ImGuiID key,int default_val)\n{\n    return self->GetIntRef(key,default_val);\n}\nCIMGUI_API bool* ImGuiStorage_GetBoolRef(ImGuiStorage* self,ImGuiID key,bool default_val)\n{\n    return self->GetBoolRef(key,default_val);\n}\nCIMGUI_API float* ImGuiStorage_GetFloatRef(ImGuiStorage* self,ImGuiID key,float default_val)\n{\n    return self->GetFloatRef(key,default_val);\n}\nCIMGUI_API void** ImGuiStorage_GetVoidPtrRef(ImGuiStorage* self,ImGuiID key,void* default_val)\n{\n    return self->GetVoidPtrRef(key,default_val);\n}\nCIMGUI_API void ImGuiStorage_BuildSortByKey(ImGuiStorage* self)\n{\n    return self->BuildSortByKey();\n}\nCIMGUI_API void ImGuiStorage_SetAllInt(ImGuiStorage* self,int val)\n{\n    return self->SetAllInt(val);\n}\nCIMGUI_API ImGuiListClipper* ImGuiListClipper_ImGuiListClipper(void)\n{\n    return IM_NEW(ImGuiListClipper)();\n}\nCIMGUI_API void ImGuiListClipper_destroy(ImGuiListClipper* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiListClipper_Begin(ImGuiListClipper* self,int items_count,float items_height)\n{\n    return self->Begin(items_count,items_height);\n}\nCIMGUI_API void ImGuiListClipper_End(ImGuiListClipper* self)\n{\n    return self->End();\n}\nCIMGUI_API bool ImGuiListClipper_Step(ImGuiListClipper* self)\n{\n    return self->Step();\n}\nCIMGUI_API void ImGuiListClipper_IncludeItemByIndex(ImGuiListClipper* self,int item_index)\n{\n    return self->IncludeItemByIndex(item_index);\n}\nCIMGUI_API void ImGuiListClipper_IncludeItemsByIndex(ImGuiListClipper* self,int item_begin,int item_end)\n{\n    return self->IncludeItemsByIndex(item_begin,item_end);\n}\nCIMGUI_API void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* self,int item_index)\n{\n    return self->SeekCursorForItem(item_index);\n}\nCIMGUI_API ImColor* ImColor_ImColor_Nil(void)\n{\n    return IM_NEW(ImColor)();\n}\nCIMGUI_API void ImColor_destroy(ImColor* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImColor* ImColor_ImColor_Float(float r,float g,float b,float a)\n{\n    return IM_NEW(ImColor)(r,g,b,a);\n}\nCIMGUI_API ImColor* ImColor_ImColor_Vec4(const ImVec4 col)\n{\n    return IM_NEW(ImColor)(col);\n}\nCIMGUI_API ImColor* ImColor_ImColor_Int(int r,int g,int b,int a)\n{\n    return IM_NEW(ImColor)(r,g,b,a);\n}\nCIMGUI_API ImColor* ImColor_ImColor_U32(ImU32 rgba)\n{\n    return IM_NEW(ImColor)(rgba);\n}\nCIMGUI_API void ImColor_SetHSV(ImColor* self,float h,float s,float v,float a)\n{\n    return self->SetHSV(h,s,v,a);\n}\nCIMGUI_API void ImColor_HSV(ImColor *pOut,float h,float s,float v,float a)\n{\n    *pOut = ImColor::HSV(h,s,v,a);\n}\nCIMGUI_API ImGuiSelectionBasicStorage* ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage(void)\n{\n    return IM_NEW(ImGuiSelectionBasicStorage)();\n}\nCIMGUI_API void ImGuiSelectionBasicStorage_destroy(ImGuiSelectionBasicStorage* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiSelectionBasicStorage_ApplyRequests(ImGuiSelectionBasicStorage* self,ImGuiMultiSelectIO* ms_io)\n{\n    return self->ApplyRequests(ms_io);\n}\nCIMGUI_API bool ImGuiSelectionBasicStorage_Contains(ImGuiSelectionBasicStorage* self,ImGuiID id)\n{\n    return self->Contains(id);\n}\nCIMGUI_API void ImGuiSelectionBasicStorage_Clear(ImGuiSelectionBasicStorage* self)\n{\n    return self->Clear();\n}\nCIMGUI_API void ImGuiSelectionBasicStorage_Swap(ImGuiSelectionBasicStorage* self,ImGuiSelectionBasicStorage* r)\n{\n    return self->Swap(*r);\n}\nCIMGUI_API void ImGuiSelectionBasicStorage_SetItemSelected(ImGuiSelectionBasicStorage* self,ImGuiID id,bool selected)\n{\n    return self->SetItemSelected(id,selected);\n}\nCIMGUI_API bool ImGuiSelectionBasicStorage_GetNextSelectedItem(ImGuiSelectionBasicStorage* self,void** opaque_it,ImGuiID* out_id)\n{\n    return self->GetNextSelectedItem(opaque_it,out_id);\n}\nCIMGUI_API ImGuiID ImGuiSelectionBasicStorage_GetStorageIdFromIndex(ImGuiSelectionBasicStorage* self,int idx)\n{\n    return self->GetStorageIdFromIndex(idx);\n}\nCIMGUI_API ImGuiSelectionExternalStorage* ImGuiSelectionExternalStorage_ImGuiSelectionExternalStorage(void)\n{\n    return IM_NEW(ImGuiSelectionExternalStorage)();\n}\nCIMGUI_API void ImGuiSelectionExternalStorage_destroy(ImGuiSelectionExternalStorage* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiSelectionExternalStorage_ApplyRequests(ImGuiSelectionExternalStorage* self,ImGuiMultiSelectIO* ms_io)\n{\n    return self->ApplyRequests(ms_io);\n}\nCIMGUI_API ImDrawCmd* ImDrawCmd_ImDrawCmd(void)\n{\n    return IM_NEW(ImDrawCmd)();\n}\nCIMGUI_API void ImDrawCmd_destroy(ImDrawCmd* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImTextureID ImDrawCmd_GetTexID(ImDrawCmd* self)\n{\n    return self->GetTexID();\n}\nCIMGUI_API ImDrawListSplitter* ImDrawListSplitter_ImDrawListSplitter(void)\n{\n    return IM_NEW(ImDrawListSplitter)();\n}\nCIMGUI_API void ImDrawListSplitter_destroy(ImDrawListSplitter* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImDrawListSplitter_Clear(ImDrawListSplitter* self)\n{\n    return self->Clear();\n}\nCIMGUI_API void ImDrawListSplitter_ClearFreeMemory(ImDrawListSplitter* self)\n{\n    return self->ClearFreeMemory();\n}\nCIMGUI_API void ImDrawListSplitter_Split(ImDrawListSplitter* self,ImDrawList* draw_list,int count)\n{\n    return self->Split(draw_list,count);\n}\nCIMGUI_API void ImDrawListSplitter_Merge(ImDrawListSplitter* self,ImDrawList* draw_list)\n{\n    return self->Merge(draw_list);\n}\nCIMGUI_API void ImDrawListSplitter_SetCurrentChannel(ImDrawListSplitter* self,ImDrawList* draw_list,int channel_idx)\n{\n    return self->SetCurrentChannel(draw_list,channel_idx);\n}\nCIMGUI_API ImDrawList* ImDrawList_ImDrawList(ImDrawListSharedData* shared_data)\n{\n    return IM_NEW(ImDrawList)(shared_data);\n}\nCIMGUI_API void ImDrawList_destroy(ImDrawList* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImDrawList_PushClipRect(ImDrawList* self,const ImVec2 clip_rect_min,const ImVec2 clip_rect_max,bool intersect_with_current_clip_rect)\n{\n    return self->PushClipRect(clip_rect_min,clip_rect_max,intersect_with_current_clip_rect);\n}\nCIMGUI_API void ImDrawList_PushClipRectFullScreen(ImDrawList* self)\n{\n    return self->PushClipRectFullScreen();\n}\nCIMGUI_API void ImDrawList_PopClipRect(ImDrawList* self)\n{\n    return self->PopClipRect();\n}\nCIMGUI_API void ImDrawList_PushTexture(ImDrawList* self,ImTextureRef tex_ref)\n{\n    return self->PushTexture(tex_ref);\n}\nCIMGUI_API void ImDrawList_PopTexture(ImDrawList* self)\n{\n    return self->PopTexture();\n}\nCIMGUI_API void ImDrawList_GetClipRectMin(ImVec2 *pOut,ImDrawList* self)\n{\n    *pOut = self->GetClipRectMin();\n}\nCIMGUI_API void ImDrawList_GetClipRectMax(ImVec2 *pOut,ImDrawList* self)\n{\n    *pOut = self->GetClipRectMax();\n}\nCIMGUI_API void ImDrawList_AddLine(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,ImU32 col,float thickness)\n{\n    return self->AddLine(p1,p2,col,thickness);\n}\nCIMGUI_API void ImDrawList_AddRect(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col,float rounding,ImDrawFlags flags,float thickness)\n{\n    return self->AddRect(p_min,p_max,col,rounding,flags,thickness);\n}\nCIMGUI_API void ImDrawList_AddRectFilled(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col,float rounding,ImDrawFlags flags)\n{\n    return self->AddRectFilled(p_min,p_max,col,rounding,flags);\n}\nCIMGUI_API void ImDrawList_AddRectFilledMultiColor(ImDrawList* self,const ImVec2 p_min,const ImVec2 p_max,ImU32 col_upr_left,ImU32 col_upr_right,ImU32 col_bot_right,ImU32 col_bot_left)\n{\n    return self->AddRectFilledMultiColor(p_min,p_max,col_upr_left,col_upr_right,col_bot_right,col_bot_left);\n}\nCIMGUI_API void ImDrawList_AddQuad(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col,float thickness)\n{\n    return self->AddQuad(p1,p2,p3,p4,col,thickness);\n}\nCIMGUI_API void ImDrawList_AddQuadFilled(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col)\n{\n    return self->AddQuadFilled(p1,p2,p3,p4,col);\n}\nCIMGUI_API void ImDrawList_AddTriangle(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col,float thickness)\n{\n    return self->AddTriangle(p1,p2,p3,col,thickness);\n}\nCIMGUI_API void ImDrawList_AddTriangleFilled(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col)\n{\n    return self->AddTriangleFilled(p1,p2,p3,col);\n}\nCIMGUI_API void ImDrawList_AddCircle(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments,float thickness)\n{\n    return self->AddCircle(center,radius,col,num_segments,thickness);\n}\nCIMGUI_API void ImDrawList_AddCircleFilled(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments)\n{\n    return self->AddCircleFilled(center,radius,col,num_segments);\n}\nCIMGUI_API void ImDrawList_AddNgon(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments,float thickness)\n{\n    return self->AddNgon(center,radius,col,num_segments,thickness);\n}\nCIMGUI_API void ImDrawList_AddNgonFilled(ImDrawList* self,const ImVec2 center,float radius,ImU32 col,int num_segments)\n{\n    return self->AddNgonFilled(center,radius,col,num_segments);\n}\nCIMGUI_API void ImDrawList_AddEllipse(ImDrawList* self,const ImVec2 center,const ImVec2 radius,ImU32 col,float rot,int num_segments,float thickness)\n{\n    return self->AddEllipse(center,radius,col,rot,num_segments,thickness);\n}\nCIMGUI_API void ImDrawList_AddEllipseFilled(ImDrawList* self,const ImVec2 center,const ImVec2 radius,ImU32 col,float rot,int num_segments)\n{\n    return self->AddEllipseFilled(center,radius,col,rot,num_segments);\n}\nCIMGUI_API void ImDrawList_AddText_Vec2(ImDrawList* self,const ImVec2 pos,ImU32 col,const char* text_begin,const char* text_end)\n{\n    return self->AddText(pos,col,text_begin,text_end);\n}\nCIMGUI_API void ImDrawList_AddText_FontPtr(ImDrawList* self,ImFont* font,float font_size,const ImVec2 pos,ImU32 col,const char* text_begin,const char* text_end,float wrap_width,const ImVec4* cpu_fine_clip_rect)\n{\n    return self->AddText(font,font_size,pos,col,text_begin,text_end,wrap_width,cpu_fine_clip_rect);\n}\nCIMGUI_API void ImDrawList_AddBezierCubic(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,ImU32 col,float thickness,int num_segments)\n{\n    return self->AddBezierCubic(p1,p2,p3,p4,col,thickness,num_segments);\n}\nCIMGUI_API void ImDrawList_AddBezierQuadratic(ImDrawList* self,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,ImU32 col,float thickness,int num_segments)\n{\n    return self->AddBezierQuadratic(p1,p2,p3,col,thickness,num_segments);\n}\nCIMGUI_API void ImDrawList_AddPolyline(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col,ImDrawFlags flags,float thickness)\n{\n    return self->AddPolyline(points,num_points,col,flags,thickness);\n}\nCIMGUI_API void ImDrawList_AddConvexPolyFilled(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col)\n{\n    return self->AddConvexPolyFilled(points,num_points,col);\n}\nCIMGUI_API void ImDrawList_AddConcavePolyFilled(ImDrawList* self,const ImVec2* points,int num_points,ImU32 col)\n{\n    return self->AddConcavePolyFilled(points,num_points,col);\n}\nCIMGUI_API void ImDrawList_AddImage(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p_min,const ImVec2 p_max,const ImVec2 uv_min,const ImVec2 uv_max,ImU32 col)\n{\n    return self->AddImage(tex_ref,p_min,p_max,uv_min,uv_max,col);\n}\nCIMGUI_API void ImDrawList_AddImageQuad(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 uv1,const ImVec2 uv2,const ImVec2 uv3,const ImVec2 uv4,ImU32 col)\n{\n    return self->AddImageQuad(tex_ref,p1,p2,p3,p4,uv1,uv2,uv3,uv4,col);\n}\nCIMGUI_API void ImDrawList_AddImageRounded(ImDrawList* self,ImTextureRef tex_ref,const ImVec2 p_min,const ImVec2 p_max,const ImVec2 uv_min,const ImVec2 uv_max,ImU32 col,float rounding,ImDrawFlags flags)\n{\n    return self->AddImageRounded(tex_ref,p_min,p_max,uv_min,uv_max,col,rounding,flags);\n}\nCIMGUI_API void ImDrawList_PathClear(ImDrawList* self)\n{\n    return self->PathClear();\n}\nCIMGUI_API void ImDrawList_PathLineTo(ImDrawList* self,const ImVec2 pos)\n{\n    return self->PathLineTo(pos);\n}\nCIMGUI_API void ImDrawList_PathLineToMergeDuplicate(ImDrawList* self,const ImVec2 pos)\n{\n    return self->PathLineToMergeDuplicate(pos);\n}\nCIMGUI_API void ImDrawList_PathFillConvex(ImDrawList* self,ImU32 col)\n{\n    return self->PathFillConvex(col);\n}\nCIMGUI_API void ImDrawList_PathFillConcave(ImDrawList* self,ImU32 col)\n{\n    return self->PathFillConcave(col);\n}\nCIMGUI_API void ImDrawList_PathStroke(ImDrawList* self,ImU32 col,ImDrawFlags flags,float thickness)\n{\n    return self->PathStroke(col,flags,thickness);\n}\nCIMGUI_API void ImDrawList_PathArcTo(ImDrawList* self,const ImVec2 center,float radius,float a_min,float a_max,int num_segments)\n{\n    return self->PathArcTo(center,radius,a_min,a_max,num_segments);\n}\nCIMGUI_API void ImDrawList_PathArcToFast(ImDrawList* self,const ImVec2 center,float radius,int a_min_of_12,int a_max_of_12)\n{\n    return self->PathArcToFast(center,radius,a_min_of_12,a_max_of_12);\n}\nCIMGUI_API void ImDrawList_PathEllipticalArcTo(ImDrawList* self,const ImVec2 center,const ImVec2 radius,float rot,float a_min,float a_max,int num_segments)\n{\n    return self->PathEllipticalArcTo(center,radius,rot,a_min,a_max,num_segments);\n}\nCIMGUI_API void ImDrawList_PathBezierCubicCurveTo(ImDrawList* self,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,int num_segments)\n{\n    return self->PathBezierCubicCurveTo(p2,p3,p4,num_segments);\n}\nCIMGUI_API void ImDrawList_PathBezierQuadraticCurveTo(ImDrawList* self,const ImVec2 p2,const ImVec2 p3,int num_segments)\n{\n    return self->PathBezierQuadraticCurveTo(p2,p3,num_segments);\n}\nCIMGUI_API void ImDrawList_PathRect(ImDrawList* self,const ImVec2 rect_min,const ImVec2 rect_max,float rounding,ImDrawFlags flags)\n{\n    return self->PathRect(rect_min,rect_max,rounding,flags);\n}\nCIMGUI_API void ImDrawList_AddCallback(ImDrawList* self,ImDrawCallback callback,void* userdata,size_t userdata_size)\n{\n    return self->AddCallback(callback,userdata,userdata_size);\n}\nCIMGUI_API void ImDrawList_AddDrawCmd(ImDrawList* self)\n{\n    return self->AddDrawCmd();\n}\nCIMGUI_API ImDrawList* ImDrawList_CloneOutput(ImDrawList* self)\n{\n    return self->CloneOutput();\n}\nCIMGUI_API void ImDrawList_ChannelsSplit(ImDrawList* self,int count)\n{\n    return self->ChannelsSplit(count);\n}\nCIMGUI_API void ImDrawList_ChannelsMerge(ImDrawList* self)\n{\n    return self->ChannelsMerge();\n}\nCIMGUI_API void ImDrawList_ChannelsSetCurrent(ImDrawList* self,int n)\n{\n    return self->ChannelsSetCurrent(n);\n}\nCIMGUI_API void ImDrawList_PrimReserve(ImDrawList* self,int idx_count,int vtx_count)\n{\n    return self->PrimReserve(idx_count,vtx_count);\n}\nCIMGUI_API void ImDrawList_PrimUnreserve(ImDrawList* self,int idx_count,int vtx_count)\n{\n    return self->PrimUnreserve(idx_count,vtx_count);\n}\nCIMGUI_API void ImDrawList_PrimRect(ImDrawList* self,const ImVec2 a,const ImVec2 b,ImU32 col)\n{\n    return self->PrimRect(a,b,col);\n}\nCIMGUI_API void ImDrawList_PrimRectUV(ImDrawList* self,const ImVec2 a,const ImVec2 b,const ImVec2 uv_a,const ImVec2 uv_b,ImU32 col)\n{\n    return self->PrimRectUV(a,b,uv_a,uv_b,col);\n}\nCIMGUI_API void ImDrawList_PrimQuadUV(ImDrawList* self,const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 d,const ImVec2 uv_a,const ImVec2 uv_b,const ImVec2 uv_c,const ImVec2 uv_d,ImU32 col)\n{\n    return self->PrimQuadUV(a,b,c,d,uv_a,uv_b,uv_c,uv_d,col);\n}\nCIMGUI_API void ImDrawList_PrimWriteVtx(ImDrawList* self,const ImVec2 pos,const ImVec2 uv,ImU32 col)\n{\n    return self->PrimWriteVtx(pos,uv,col);\n}\nCIMGUI_API void ImDrawList_PrimWriteIdx(ImDrawList* self,ImDrawIdx idx)\n{\n    return self->PrimWriteIdx(idx);\n}\nCIMGUI_API void ImDrawList_PrimVtx(ImDrawList* self,const ImVec2 pos,const ImVec2 uv,ImU32 col)\n{\n    return self->PrimVtx(pos,uv,col);\n}\nCIMGUI_API void ImDrawList__SetDrawListSharedData(ImDrawList* self,ImDrawListSharedData* data)\n{\n    return self->_SetDrawListSharedData(data);\n}\nCIMGUI_API void ImDrawList__ResetForNewFrame(ImDrawList* self)\n{\n    return self->_ResetForNewFrame();\n}\nCIMGUI_API void ImDrawList__ClearFreeMemory(ImDrawList* self)\n{\n    return self->_ClearFreeMemory();\n}\nCIMGUI_API void ImDrawList__PopUnusedDrawCmd(ImDrawList* self)\n{\n    return self->_PopUnusedDrawCmd();\n}\nCIMGUI_API void ImDrawList__TryMergeDrawCmds(ImDrawList* self)\n{\n    return self->_TryMergeDrawCmds();\n}\nCIMGUI_API void ImDrawList__OnChangedClipRect(ImDrawList* self)\n{\n    return self->_OnChangedClipRect();\n}\nCIMGUI_API void ImDrawList__OnChangedTexture(ImDrawList* self)\n{\n    return self->_OnChangedTexture();\n}\nCIMGUI_API void ImDrawList__OnChangedVtxOffset(ImDrawList* self)\n{\n    return self->_OnChangedVtxOffset();\n}\nCIMGUI_API void ImDrawList__SetTexture(ImDrawList* self,ImTextureRef tex_ref)\n{\n    return self->_SetTexture(tex_ref);\n}\nCIMGUI_API int ImDrawList__CalcCircleAutoSegmentCount(ImDrawList* self,float radius)\n{\n    return self->_CalcCircleAutoSegmentCount(radius);\n}\nCIMGUI_API void ImDrawList__PathArcToFastEx(ImDrawList* self,const ImVec2 center,float radius,int a_min_sample,int a_max_sample,int a_step)\n{\n    return self->_PathArcToFastEx(center,radius,a_min_sample,a_max_sample,a_step);\n}\nCIMGUI_API void ImDrawList__PathArcToN(ImDrawList* self,const ImVec2 center,float radius,float a_min,float a_max,int num_segments)\n{\n    return self->_PathArcToN(center,radius,a_min,a_max,num_segments);\n}\nCIMGUI_API ImDrawData* ImDrawData_ImDrawData(void)\n{\n    return IM_NEW(ImDrawData)();\n}\nCIMGUI_API void ImDrawData_destroy(ImDrawData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImDrawData_Clear(ImDrawData* self)\n{\n    return self->Clear();\n}\nCIMGUI_API void ImDrawData_AddDrawList(ImDrawData* self,ImDrawList* draw_list)\n{\n    return self->AddDrawList(draw_list);\n}\nCIMGUI_API void ImDrawData_DeIndexAllBuffers(ImDrawData* self)\n{\n    return self->DeIndexAllBuffers();\n}\nCIMGUI_API void ImDrawData_ScaleClipRects(ImDrawData* self,const ImVec2 fb_scale)\n{\n    return self->ScaleClipRects(fb_scale);\n}\nCIMGUI_API ImTextureData* ImTextureData_ImTextureData(void)\n{\n    return IM_NEW(ImTextureData)();\n}\nCIMGUI_API void ImTextureData_destroy(ImTextureData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImTextureData_Create(ImTextureData* self,ImTextureFormat format,int w,int h)\n{\n    return self->Create(format,w,h);\n}\nCIMGUI_API void ImTextureData_DestroyPixels(ImTextureData* self)\n{\n    return self->DestroyPixels();\n}\nCIMGUI_API void* ImTextureData_GetPixels(ImTextureData* self)\n{\n    return self->GetPixels();\n}\nCIMGUI_API void* ImTextureData_GetPixelsAt(ImTextureData* self,int x,int y)\n{\n    return self->GetPixelsAt(x,y);\n}\nCIMGUI_API int ImTextureData_GetSizeInBytes(ImTextureData* self)\n{\n    return self->GetSizeInBytes();\n}\nCIMGUI_API int ImTextureData_GetPitch(ImTextureData* self)\n{\n    return self->GetPitch();\n}\nCIMGUI_API void ImTextureData_GetTexRef(ImTextureRef *pOut,ImTextureData* self)\n{\n    *pOut = self->GetTexRef();\n}\nCIMGUI_API ImTextureID ImTextureData_GetTexID(ImTextureData* self)\n{\n    return self->GetTexID();\n}\nCIMGUI_API void ImTextureData_SetTexID(ImTextureData* self,ImTextureID tex_id)\n{\n    return self->SetTexID(tex_id);\n}\nCIMGUI_API void ImTextureData_SetStatus(ImTextureData* self,ImTextureStatus status)\n{\n    return self->SetStatus(status);\n}\nCIMGUI_API ImFontConfig* ImFontConfig_ImFontConfig(void)\n{\n    return IM_NEW(ImFontConfig)();\n}\nCIMGUI_API void ImFontConfig_destroy(ImFontConfig* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImFontGlyph* ImFontGlyph_ImFontGlyph(void)\n{\n    return IM_NEW(ImFontGlyph)();\n}\nCIMGUI_API void ImFontGlyph_destroy(ImFontGlyph* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImFontGlyphRangesBuilder* ImFontGlyphRangesBuilder_ImFontGlyphRangesBuilder(void)\n{\n    return IM_NEW(ImFontGlyphRangesBuilder)();\n}\nCIMGUI_API void ImFontGlyphRangesBuilder_destroy(ImFontGlyphRangesBuilder* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImFontGlyphRangesBuilder_Clear(ImFontGlyphRangesBuilder* self)\n{\n    return self->Clear();\n}\nCIMGUI_API bool ImFontGlyphRangesBuilder_GetBit(ImFontGlyphRangesBuilder* self,size_t n)\n{\n    return self->GetBit(n);\n}\nCIMGUI_API void ImFontGlyphRangesBuilder_SetBit(ImFontGlyphRangesBuilder* self,size_t n)\n{\n    return self->SetBit(n);\n}\nCIMGUI_API void ImFontGlyphRangesBuilder_AddChar(ImFontGlyphRangesBuilder* self,ImWchar c)\n{\n    return self->AddChar(c);\n}\nCIMGUI_API void ImFontGlyphRangesBuilder_AddText(ImFontGlyphRangesBuilder* self,const char* text,const char* text_end)\n{\n    return self->AddText(text,text_end);\n}\nCIMGUI_API void ImFontGlyphRangesBuilder_AddRanges(ImFontGlyphRangesBuilder* self,const ImWchar* ranges)\n{\n    return self->AddRanges(ranges);\n}\nCIMGUI_API void ImFontGlyphRangesBuilder_BuildRanges(ImFontGlyphRangesBuilder* self,ImVector_ImWchar* out_ranges)\n{\n    return self->BuildRanges(out_ranges);\n}\nCIMGUI_API ImFontAtlasRect* ImFontAtlasRect_ImFontAtlasRect(void)\n{\n    return IM_NEW(ImFontAtlasRect)();\n}\nCIMGUI_API void ImFontAtlasRect_destroy(ImFontAtlasRect* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImFontAtlas* ImFontAtlas_ImFontAtlas(void)\n{\n    return IM_NEW(ImFontAtlas)();\n}\nCIMGUI_API void ImFontAtlas_destroy(ImFontAtlas* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImFont* ImFontAtlas_AddFont(ImFontAtlas* self,const ImFontConfig* font_cfg)\n{\n    return self->AddFont(font_cfg);\n}\nCIMGUI_API ImFont* ImFontAtlas_AddFontDefault(ImFontAtlas* self,const ImFontConfig* font_cfg)\n{\n    return self->AddFontDefault(font_cfg);\n}\nCIMGUI_API ImFont* ImFontAtlas_AddFontFromFileTTF(ImFontAtlas* self,const char* filename,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges)\n{\n    return self->AddFontFromFileTTF(filename,size_pixels,font_cfg,glyph_ranges);\n}\nCIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryTTF(ImFontAtlas* self,void* font_data,int font_data_size,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges)\n{\n    return self->AddFontFromMemoryTTF(font_data,font_data_size,size_pixels,font_cfg,glyph_ranges);\n}\nCIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryCompressedTTF(ImFontAtlas* self,const void* compressed_font_data,int compressed_font_data_size,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges)\n{\n    return self->AddFontFromMemoryCompressedTTF(compressed_font_data,compressed_font_data_size,size_pixels,font_cfg,glyph_ranges);\n}\nCIMGUI_API ImFont* ImFontAtlas_AddFontFromMemoryCompressedBase85TTF(ImFontAtlas* self,const char* compressed_font_data_base85,float size_pixels,const ImFontConfig* font_cfg,const ImWchar* glyph_ranges)\n{\n    return self->AddFontFromMemoryCompressedBase85TTF(compressed_font_data_base85,size_pixels,font_cfg,glyph_ranges);\n}\nCIMGUI_API void ImFontAtlas_RemoveFont(ImFontAtlas* self,ImFont* font)\n{\n    return self->RemoveFont(font);\n}\nCIMGUI_API void ImFontAtlas_Clear(ImFontAtlas* self)\n{\n    return self->Clear();\n}\nCIMGUI_API void ImFontAtlas_CompactCache(ImFontAtlas* self)\n{\n    return self->CompactCache();\n}\nCIMGUI_API void ImFontAtlas_ClearInputData(ImFontAtlas* self)\n{\n    return self->ClearInputData();\n}\nCIMGUI_API void ImFontAtlas_ClearFonts(ImFontAtlas* self)\n{\n    return self->ClearFonts();\n}\nCIMGUI_API void ImFontAtlas_ClearTexData(ImFontAtlas* self)\n{\n    return self->ClearTexData();\n}\nCIMGUI_API const ImWchar* ImFontAtlas_GetGlyphRangesDefault(ImFontAtlas* self)\n{\n    return self->GetGlyphRangesDefault();\n}\nCIMGUI_API ImFontAtlasRectId ImFontAtlas_AddCustomRect(ImFontAtlas* self,int width,int height,ImFontAtlasRect* out_r)\n{\n    return self->AddCustomRect(width,height,out_r);\n}\nCIMGUI_API void ImFontAtlas_RemoveCustomRect(ImFontAtlas* self,ImFontAtlasRectId id)\n{\n    return self->RemoveCustomRect(id);\n}\nCIMGUI_API bool ImFontAtlas_GetCustomRect(ImFontAtlas* self,ImFontAtlasRectId id,ImFontAtlasRect* out_r)\n{\n    return self->GetCustomRect(id,out_r);\n}\nCIMGUI_API ImFontBaked* ImFontBaked_ImFontBaked(void)\n{\n    return IM_NEW(ImFontBaked)();\n}\nCIMGUI_API void ImFontBaked_destroy(ImFontBaked* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImFontBaked_ClearOutputData(ImFontBaked* self)\n{\n    return self->ClearOutputData();\n}\nCIMGUI_API ImFontGlyph* ImFontBaked_FindGlyph(ImFontBaked* self,ImWchar c)\n{\n    return self->FindGlyph(c);\n}\nCIMGUI_API ImFontGlyph* ImFontBaked_FindGlyphNoFallback(ImFontBaked* self,ImWchar c)\n{\n    return self->FindGlyphNoFallback(c);\n}\nCIMGUI_API float ImFontBaked_GetCharAdvance(ImFontBaked* self,ImWchar c)\n{\n    return self->GetCharAdvance(c);\n}\nCIMGUI_API bool ImFontBaked_IsGlyphLoaded(ImFontBaked* self,ImWchar c)\n{\n    return self->IsGlyphLoaded(c);\n}\nCIMGUI_API ImFont* ImFont_ImFont(void)\n{\n    return IM_NEW(ImFont)();\n}\nCIMGUI_API void ImFont_destroy(ImFont* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API bool ImFont_IsGlyphInFont(ImFont* self,ImWchar c)\n{\n    return self->IsGlyphInFont(c);\n}\nCIMGUI_API bool ImFont_IsLoaded(ImFont* self)\n{\n    return self->IsLoaded();\n}\nCIMGUI_API const char* ImFont_GetDebugName(ImFont* self)\n{\n    return self->GetDebugName();\n}\nCIMGUI_API ImFontBaked* ImFont_GetFontBaked(ImFont* self,float font_size,float density)\n{\n    return self->GetFontBaked(font_size,density);\n}\nCIMGUI_API void ImFont_CalcTextSizeA(ImVec2 *pOut,ImFont* self,float size,float max_width,float wrap_width,const char* text_begin,const char* text_end,const char** remaining)\n{\n    *pOut = self->CalcTextSizeA(size,max_width,wrap_width,text_begin,text_end,remaining);\n}\nCIMGUI_API const char* ImFont_CalcWordWrapPosition(ImFont* self,float size,const char* text,const char* text_end,float wrap_width)\n{\n    return self->CalcWordWrapPosition(size,text,text_end,wrap_width);\n}\nCIMGUI_API void ImFont_RenderChar(ImFont* self,ImDrawList* draw_list,float size,const ImVec2 pos,ImU32 col,ImWchar c,const ImVec4* cpu_fine_clip)\n{\n    return self->RenderChar(draw_list,size,pos,col,c,cpu_fine_clip);\n}\nCIMGUI_API void ImFont_RenderText(ImFont* self,ImDrawList* draw_list,float size,const ImVec2 pos,ImU32 col,const ImVec4 clip_rect,const char* text_begin,const char* text_end,float wrap_width,bool cpu_fine_clip)\n{\n    return self->RenderText(draw_list,size,pos,col,clip_rect,text_begin,text_end,wrap_width,cpu_fine_clip);\n}\nCIMGUI_API void ImFont_ClearOutputData(ImFont* self)\n{\n    return self->ClearOutputData();\n}\nCIMGUI_API void ImFont_AddRemapChar(ImFont* self,ImWchar from_codepoint,ImWchar to_codepoint)\n{\n    return self->AddRemapChar(from_codepoint,to_codepoint);\n}\nCIMGUI_API bool ImFont_IsGlyphRangeUnused(ImFont* self,unsigned int c_begin,unsigned int c_last)\n{\n    return self->IsGlyphRangeUnused(c_begin,c_last);\n}\nCIMGUI_API ImGuiViewport* ImGuiViewport_ImGuiViewport(void)\n{\n    return IM_NEW(ImGuiViewport)();\n}\nCIMGUI_API void ImGuiViewport_destroy(ImGuiViewport* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiViewport_GetCenter(ImVec2 *pOut,ImGuiViewport* self)\n{\n    *pOut = self->GetCenter();\n}\nCIMGUI_API void ImGuiViewport_GetWorkCenter(ImVec2 *pOut,ImGuiViewport* self)\n{\n    *pOut = self->GetWorkCenter();\n}\nCIMGUI_API ImGuiPlatformIO* ImGuiPlatformIO_ImGuiPlatformIO(void)\n{\n    return IM_NEW(ImGuiPlatformIO)();\n}\nCIMGUI_API void ImGuiPlatformIO_destroy(ImGuiPlatformIO* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiPlatformMonitor* ImGuiPlatformMonitor_ImGuiPlatformMonitor(void)\n{\n    return IM_NEW(ImGuiPlatformMonitor)();\n}\nCIMGUI_API void ImGuiPlatformMonitor_destroy(ImGuiPlatformMonitor* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiPlatformImeData* ImGuiPlatformImeData_ImGuiPlatformImeData(void)\n{\n    return IM_NEW(ImGuiPlatformImeData)();\n}\nCIMGUI_API void ImGuiPlatformImeData_destroy(ImGuiPlatformImeData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiID igImHashData(const void* data,size_t data_size,ImGuiID seed)\n{\n    return ImHashData(data,data_size,seed);\n}\nCIMGUI_API ImGuiID igImHashStr(const char* data,size_t data_size,ImGuiID seed)\n{\n    return ImHashStr(data,data_size,seed);\n}\nCIMGUI_API void igImQsort(void* base,size_t count,size_t size_of_element,int(*compare_func)(void const*,void const*))\n{\n    return ImQsort(base,count,size_of_element,compare_func);\n}\nCIMGUI_API ImU32 igImAlphaBlendColors(ImU32 col_a,ImU32 col_b)\n{\n    return ImAlphaBlendColors(col_a,col_b);\n}\nCIMGUI_API bool igImIsPowerOfTwo_Int(int v)\n{\n    return ImIsPowerOfTwo(v);\n}\nCIMGUI_API bool igImIsPowerOfTwo_U64(ImU64 v)\n{\n    return ImIsPowerOfTwo(v);\n}\nCIMGUI_API int igImUpperPowerOfTwo(int v)\n{\n    return ImUpperPowerOfTwo(v);\n}\nCIMGUI_API unsigned int igImCountSetBits(unsigned int v)\n{\n    return ImCountSetBits(v);\n}\nCIMGUI_API int igImStricmp(const char* str1,const char* str2)\n{\n    return ImStricmp(str1,str2);\n}\nCIMGUI_API int igImStrnicmp(const char* str1,const char* str2,size_t count)\n{\n    return ImStrnicmp(str1,str2,count);\n}\nCIMGUI_API void igImStrncpy(char* dst,const char* src,size_t count)\n{\n    return ImStrncpy(dst,src,count);\n}\nCIMGUI_API char* igImStrdup(const char* str)\n{\n    return ImStrdup(str);\n}\nCIMGUI_API void* igImMemdup(const void* src,size_t size)\n{\n    return ImMemdup(src,size);\n}\nCIMGUI_API char* igImStrdupcpy(char* dst,size_t* p_dst_size,const char* str)\n{\n    return ImStrdupcpy(dst,p_dst_size,str);\n}\nCIMGUI_API const char* igImStrchrRange(const char* str_begin,const char* str_end,char c)\n{\n    return ImStrchrRange(str_begin,str_end,c);\n}\nCIMGUI_API const char* igImStreolRange(const char* str,const char* str_end)\n{\n    return ImStreolRange(str,str_end);\n}\nCIMGUI_API const char* igImStristr(const char* haystack,const char* haystack_end,const char* needle,const char* needle_end)\n{\n    return ImStristr(haystack,haystack_end,needle,needle_end);\n}\nCIMGUI_API void igImStrTrimBlanks(char* str)\n{\n    return ImStrTrimBlanks(str);\n}\nCIMGUI_API const char* igImStrSkipBlank(const char* str)\n{\n    return ImStrSkipBlank(str);\n}\nCIMGUI_API int igImStrlenW(const ImWchar* str)\n{\n    return ImStrlenW(str);\n}\nCIMGUI_API const char* igImStrbol(const char* buf_mid_line,const char* buf_begin)\n{\n    return ImStrbol(buf_mid_line,buf_begin);\n}\nCIMGUI_API char igImToUpper(char c)\n{\n    return ImToUpper(c);\n}\nCIMGUI_API bool igImCharIsBlankA(char c)\n{\n    return ImCharIsBlankA(c);\n}\nCIMGUI_API bool igImCharIsBlankW(unsigned int c)\n{\n    return ImCharIsBlankW(c);\n}\nCIMGUI_API bool igImCharIsXdigitA(char c)\n{\n    return ImCharIsXdigitA(c);\n}\nCIMGUI_API int igImFormatString(char* buf,size_t buf_size,const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    int ret = ImFormatStringV(buf,buf_size,fmt,args);\n    va_end(args);\n    return ret;\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API int igImFormatString0(char* buf,size_t buf_size,const char* fmt)\n{\n    return igImFormatString(buf,buf_size,fmt);\n}\n#endif\nCIMGUI_API int igImFormatStringV(char* buf,size_t buf_size,const char* fmt,va_list args)\n{\n    return ImFormatStringV(buf,buf_size,fmt,args);\n}\nCIMGUI_API void igImFormatStringToTempBuffer(const char** out_buf,const char** out_buf_end,const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImFormatStringToTempBufferV(out_buf,out_buf_end,fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igImFormatStringToTempBuffer0(const char** out_buf,const char** out_buf_end,const char* fmt)\n{\n    return igImFormatStringToTempBuffer(out_buf,out_buf_end,fmt);\n}\n#endif\nCIMGUI_API void igImFormatStringToTempBufferV(const char** out_buf,const char** out_buf_end,const char* fmt,va_list args)\n{\n    return ImFormatStringToTempBufferV(out_buf,out_buf_end,fmt,args);\n}\nCIMGUI_API const char* igImParseFormatFindStart(const char* format)\n{\n    return ImParseFormatFindStart(format);\n}\nCIMGUI_API const char* igImParseFormatFindEnd(const char* format)\n{\n    return ImParseFormatFindEnd(format);\n}\nCIMGUI_API const char* igImParseFormatTrimDecorations(const char* format,char* buf,size_t buf_size)\n{\n    return ImParseFormatTrimDecorations(format,buf,buf_size);\n}\nCIMGUI_API void igImParseFormatSanitizeForPrinting(const char* fmt_in,char* fmt_out,size_t fmt_out_size)\n{\n    return ImParseFormatSanitizeForPrinting(fmt_in,fmt_out,fmt_out_size);\n}\nCIMGUI_API const char* igImParseFormatSanitizeForScanning(const char* fmt_in,char* fmt_out,size_t fmt_out_size)\n{\n    return ImParseFormatSanitizeForScanning(fmt_in,fmt_out,fmt_out_size);\n}\nCIMGUI_API int igImParseFormatPrecision(const char* format,int default_value)\n{\n    return ImParseFormatPrecision(format,default_value);\n}\nCIMGUI_API int igImTextCharToUtf8(char out_buf[5],unsigned int c)\n{\n    return ImTextCharToUtf8(out_buf,c);\n}\nCIMGUI_API int igImTextStrToUtf8(char* out_buf,int out_buf_size,const ImWchar* in_text,const ImWchar* in_text_end)\n{\n    return ImTextStrToUtf8(out_buf,out_buf_size,in_text,in_text_end);\n}\nCIMGUI_API int igImTextCharFromUtf8(unsigned int* out_char,const char* in_text,const char* in_text_end)\n{\n    return ImTextCharFromUtf8(out_char,in_text,in_text_end);\n}\nCIMGUI_API int igImTextStrFromUtf8(ImWchar* out_buf,int out_buf_size,const char* in_text,const char* in_text_end,const char** in_remaining)\n{\n    return ImTextStrFromUtf8(out_buf,out_buf_size,in_text,in_text_end,in_remaining);\n}\nCIMGUI_API int igImTextCountCharsFromUtf8(const char* in_text,const char* in_text_end)\n{\n    return ImTextCountCharsFromUtf8(in_text,in_text_end);\n}\nCIMGUI_API int igImTextCountUtf8BytesFromChar(const char* in_text,const char* in_text_end)\n{\n    return ImTextCountUtf8BytesFromChar(in_text,in_text_end);\n}\nCIMGUI_API int igImTextCountUtf8BytesFromStr(const ImWchar* in_text,const ImWchar* in_text_end)\n{\n    return ImTextCountUtf8BytesFromStr(in_text,in_text_end);\n}\nCIMGUI_API const char* igImTextFindPreviousUtf8Codepoint(const char* in_text_start,const char* in_text_curr)\n{\n    return ImTextFindPreviousUtf8Codepoint(in_text_start,in_text_curr);\n}\nCIMGUI_API int igImTextCountLines(const char* in_text,const char* in_text_end)\n{\n    return ImTextCountLines(in_text,in_text_end);\n}\nCIMGUI_API ImFileHandle igImFileOpen(const char* filename,const char* mode)\n{\n    return ImFileOpen(filename,mode);\n}\nCIMGUI_API bool igImFileClose(ImFileHandle file)\n{\n    return ImFileClose(file);\n}\nCIMGUI_API ImU64 igImFileGetSize(ImFileHandle file)\n{\n    return ImFileGetSize(file);\n}\nCIMGUI_API ImU64 igImFileRead(void* data,ImU64 size,ImU64 count,ImFileHandle file)\n{\n    return ImFileRead(data,size,count,file);\n}\nCIMGUI_API ImU64 igImFileWrite(const void* data,ImU64 size,ImU64 count,ImFileHandle file)\n{\n    return ImFileWrite(data,size,count,file);\n}\nCIMGUI_API void* igImFileLoadToMemory(const char* filename,const char* mode,size_t* out_file_size,int padding_bytes)\n{\n    return ImFileLoadToMemory(filename,mode,out_file_size,padding_bytes);\n}\nCIMGUI_API float igImPow_Float(float x,float y)\n{\n    return ImPow(x,y);\n}\nCIMGUI_API double igImPow_double(double x,double y)\n{\n    return ImPow(x,y);\n}\nCIMGUI_API float igImLog_Float(float x)\n{\n    return ImLog(x);\n}\nCIMGUI_API double igImLog_double(double x)\n{\n    return ImLog(x);\n}\nCIMGUI_API int igImAbs_Int(int x)\n{\n    return ImAbs(x);\n}\nCIMGUI_API float igImAbs_Float(float x)\n{\n    return ImAbs(x);\n}\nCIMGUI_API double igImAbs_double(double x)\n{\n    return ImAbs(x);\n}\nCIMGUI_API float igImSign_Float(float x)\n{\n    return ImSign(x);\n}\nCIMGUI_API double igImSign_double(double x)\n{\n    return ImSign(x);\n}\nCIMGUI_API float igImRsqrt_Float(float x)\n{\n    return ImRsqrt(x);\n}\nCIMGUI_API double igImRsqrt_double(double x)\n{\n    return ImRsqrt(x);\n}\nCIMGUI_API void igImMin(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs)\n{\n    *pOut = ImMin(lhs,rhs);\n}\nCIMGUI_API void igImMax(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs)\n{\n    *pOut = ImMax(lhs,rhs);\n}\nCIMGUI_API void igImClamp(ImVec2 *pOut,const ImVec2 v,const ImVec2 mn,const ImVec2 mx)\n{\n    *pOut = ImClamp(v,mn,mx);\n}\nCIMGUI_API void igImLerp_Vec2Float(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,float t)\n{\n    *pOut = ImLerp(a,b,t);\n}\nCIMGUI_API void igImLerp_Vec2Vec2(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 t)\n{\n    *pOut = ImLerp(a,b,t);\n}\nCIMGUI_API void igImLerp_Vec4(ImVec4 *pOut,const ImVec4 a,const ImVec4 b,float t)\n{\n    *pOut = ImLerp(a,b,t);\n}\nCIMGUI_API float igImSaturate(float f)\n{\n    return ImSaturate(f);\n}\nCIMGUI_API float igImLengthSqr_Vec2(const ImVec2 lhs)\n{\n    return ImLengthSqr(lhs);\n}\nCIMGUI_API float igImLengthSqr_Vec4(const ImVec4 lhs)\n{\n    return ImLengthSqr(lhs);\n}\nCIMGUI_API float igImInvLength(const ImVec2 lhs,float fail_value)\n{\n    return ImInvLength(lhs,fail_value);\n}\nCIMGUI_API float igImTrunc_Float(float f)\n{\n    return ImTrunc(f);\n}\nCIMGUI_API void igImTrunc_Vec2(ImVec2 *pOut,const ImVec2 v)\n{\n    *pOut = ImTrunc(v);\n}\nCIMGUI_API float igImFloor_Float(float f)\n{\n    return ImFloor(f);\n}\nCIMGUI_API void igImFloor_Vec2(ImVec2 *pOut,const ImVec2 v)\n{\n    *pOut = ImFloor(v);\n}\nCIMGUI_API float igImTrunc64(float f)\n{\n    return ImTrunc64(f);\n}\nCIMGUI_API float igImRound64(float f)\n{\n    return ImRound64(f);\n}\nCIMGUI_API int igImModPositive(int a,int b)\n{\n    return ImModPositive(a,b);\n}\nCIMGUI_API float igImDot(const ImVec2 a,const ImVec2 b)\n{\n    return ImDot(a,b);\n}\nCIMGUI_API void igImRotate(ImVec2 *pOut,const ImVec2 v,float cos_a,float sin_a)\n{\n    *pOut = ImRotate(v,cos_a,sin_a);\n}\nCIMGUI_API float igImLinearSweep(float current,float target,float speed)\n{\n    return ImLinearSweep(current,target,speed);\n}\nCIMGUI_API float igImLinearRemapClamp(float s0,float s1,float d0,float d1,float x)\n{\n    return ImLinearRemapClamp(s0,s1,d0,d1,x);\n}\nCIMGUI_API void igImMul(ImVec2 *pOut,const ImVec2 lhs,const ImVec2 rhs)\n{\n    *pOut = ImMul(lhs,rhs);\n}\nCIMGUI_API bool igImIsFloatAboveGuaranteedIntegerPrecision(float f)\n{\n    return ImIsFloatAboveGuaranteedIntegerPrecision(f);\n}\nCIMGUI_API float igImExponentialMovingAverage(float avg,float sample,int n)\n{\n    return ImExponentialMovingAverage(avg,sample,n);\n}\nCIMGUI_API void igImBezierCubicCalc(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,float t)\n{\n    *pOut = ImBezierCubicCalc(p1,p2,p3,p4,t);\n}\nCIMGUI_API void igImBezierCubicClosestPoint(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 p,int num_segments)\n{\n    *pOut = ImBezierCubicClosestPoint(p1,p2,p3,p4,p,num_segments);\n}\nCIMGUI_API void igImBezierCubicClosestPointCasteljau(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,const ImVec2 p4,const ImVec2 p,float tess_tol)\n{\n    *pOut = ImBezierCubicClosestPointCasteljau(p1,p2,p3,p4,p,tess_tol);\n}\nCIMGUI_API void igImBezierQuadraticCalc(ImVec2 *pOut,const ImVec2 p1,const ImVec2 p2,const ImVec2 p3,float t)\n{\n    *pOut = ImBezierQuadraticCalc(p1,p2,p3,t);\n}\nCIMGUI_API void igImLineClosestPoint(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 p)\n{\n    *pOut = ImLineClosestPoint(a,b,p);\n}\nCIMGUI_API bool igImTriangleContainsPoint(const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p)\n{\n    return ImTriangleContainsPoint(a,b,c,p);\n}\nCIMGUI_API void igImTriangleClosestPoint(ImVec2 *pOut,const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p)\n{\n    *pOut = ImTriangleClosestPoint(a,b,c,p);\n}\nCIMGUI_API void igImTriangleBarycentricCoords(const ImVec2 a,const ImVec2 b,const ImVec2 c,const ImVec2 p,float* out_u,float* out_v,float* out_w)\n{\n    return ImTriangleBarycentricCoords(a,b,c,p,*out_u,*out_v,*out_w);\n}\nCIMGUI_API float igImTriangleArea(const ImVec2 a,const ImVec2 b,const ImVec2 c)\n{\n    return ImTriangleArea(a,b,c);\n}\nCIMGUI_API bool igImTriangleIsClockwise(const ImVec2 a,const ImVec2 b,const ImVec2 c)\n{\n    return ImTriangleIsClockwise(a,b,c);\n}\nCIMGUI_API ImVec1* ImVec1_ImVec1_Nil(void)\n{\n    return IM_NEW(ImVec1)();\n}\nCIMGUI_API void ImVec1_destroy(ImVec1* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImVec1* ImVec1_ImVec1_Float(float _x)\n{\n    return IM_NEW(ImVec1)(_x);\n}\nCIMGUI_API ImVec2i* ImVec2i_ImVec2i_Nil(void)\n{\n    return IM_NEW(ImVec2i)();\n}\nCIMGUI_API void ImVec2i_destroy(ImVec2i* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImVec2i* ImVec2i_ImVec2i_Int(int _x,int _y)\n{\n    return IM_NEW(ImVec2i)(_x,_y);\n}\nCIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_Nil(void)\n{\n    return IM_NEW(ImVec2ih)();\n}\nCIMGUI_API void ImVec2ih_destroy(ImVec2ih* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_short(short _x,short _y)\n{\n    return IM_NEW(ImVec2ih)(_x,_y);\n}\nCIMGUI_API ImVec2ih* ImVec2ih_ImVec2ih_Vec2(const ImVec2 rhs)\n{\n    return IM_NEW(ImVec2ih)(rhs);\n}\nCIMGUI_API ImRect* ImRect_ImRect_Nil(void)\n{\n    return IM_NEW(ImRect)();\n}\nCIMGUI_API void ImRect_destroy(ImRect* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImRect* ImRect_ImRect_Vec2(const ImVec2 min,const ImVec2 max)\n{\n    return IM_NEW(ImRect)(min,max);\n}\nCIMGUI_API ImRect* ImRect_ImRect_Vec4(const ImVec4 v)\n{\n    return IM_NEW(ImRect)(v);\n}\nCIMGUI_API ImRect* ImRect_ImRect_Float(float x1,float y1,float x2,float y2)\n{\n    return IM_NEW(ImRect)(x1,y1,x2,y2);\n}\nCIMGUI_API void ImRect_GetCenter(ImVec2 *pOut,ImRect* self)\n{\n    *pOut = self->GetCenter();\n}\nCIMGUI_API void ImRect_GetSize(ImVec2 *pOut,ImRect* self)\n{\n    *pOut = self->GetSize();\n}\nCIMGUI_API float ImRect_GetWidth(ImRect* self)\n{\n    return self->GetWidth();\n}\nCIMGUI_API float ImRect_GetHeight(ImRect* self)\n{\n    return self->GetHeight();\n}\nCIMGUI_API float ImRect_GetArea(ImRect* self)\n{\n    return self->GetArea();\n}\nCIMGUI_API void ImRect_GetTL(ImVec2 *pOut,ImRect* self)\n{\n    *pOut = self->GetTL();\n}\nCIMGUI_API void ImRect_GetTR(ImVec2 *pOut,ImRect* self)\n{\n    *pOut = self->GetTR();\n}\nCIMGUI_API void ImRect_GetBL(ImVec2 *pOut,ImRect* self)\n{\n    *pOut = self->GetBL();\n}\nCIMGUI_API void ImRect_GetBR(ImVec2 *pOut,ImRect* self)\n{\n    *pOut = self->GetBR();\n}\nCIMGUI_API bool ImRect_Contains_Vec2(ImRect* self,const ImVec2 p)\n{\n    return self->Contains(p);\n}\nCIMGUI_API bool ImRect_Contains_Rect(ImRect* self,const ImRect r)\n{\n    return self->Contains(r);\n}\nCIMGUI_API bool ImRect_ContainsWithPad(ImRect* self,const ImVec2 p,const ImVec2 pad)\n{\n    return self->ContainsWithPad(p,pad);\n}\nCIMGUI_API bool ImRect_Overlaps(ImRect* self,const ImRect r)\n{\n    return self->Overlaps(r);\n}\nCIMGUI_API void ImRect_Add_Vec2(ImRect* self,const ImVec2 p)\n{\n    return self->Add(p);\n}\nCIMGUI_API void ImRect_Add_Rect(ImRect* self,const ImRect r)\n{\n    return self->Add(r);\n}\nCIMGUI_API void ImRect_Expand_Float(ImRect* self,const float amount)\n{\n    return self->Expand(amount);\n}\nCIMGUI_API void ImRect_Expand_Vec2(ImRect* self,const ImVec2 amount)\n{\n    return self->Expand(amount);\n}\nCIMGUI_API void ImRect_Translate(ImRect* self,const ImVec2 d)\n{\n    return self->Translate(d);\n}\nCIMGUI_API void ImRect_TranslateX(ImRect* self,float dx)\n{\n    return self->TranslateX(dx);\n}\nCIMGUI_API void ImRect_TranslateY(ImRect* self,float dy)\n{\n    return self->TranslateY(dy);\n}\nCIMGUI_API void ImRect_ClipWith(ImRect* self,const ImRect r)\n{\n    return self->ClipWith(r);\n}\nCIMGUI_API void ImRect_ClipWithFull(ImRect* self,const ImRect r)\n{\n    return self->ClipWithFull(r);\n}\nCIMGUI_API bool ImRect_IsInverted(ImRect* self)\n{\n    return self->IsInverted();\n}\nCIMGUI_API void ImRect_ToVec4(ImVec4 *pOut,ImRect* self)\n{\n    *pOut = self->ToVec4();\n}\nCIMGUI_API size_t igImBitArrayGetStorageSizeInBytes(int bitcount)\n{\n    return ImBitArrayGetStorageSizeInBytes(bitcount);\n}\nCIMGUI_API void igImBitArrayClearAllBits(ImU32* arr,int bitcount)\n{\n    return ImBitArrayClearAllBits(arr,bitcount);\n}\nCIMGUI_API bool igImBitArrayTestBit(const ImU32* arr,int n)\n{\n    return ImBitArrayTestBit(arr,n);\n}\nCIMGUI_API void igImBitArrayClearBit(ImU32* arr,int n)\n{\n    return ImBitArrayClearBit(arr,n);\n}\nCIMGUI_API void igImBitArraySetBit(ImU32* arr,int n)\n{\n    return ImBitArraySetBit(arr,n);\n}\nCIMGUI_API void igImBitArraySetBitRange(ImU32* arr,int n,int n2)\n{\n    return ImBitArraySetBitRange(arr,n,n2);\n}\nCIMGUI_API void ImBitVector_Create(ImBitVector* self,int sz)\n{\n    return self->Create(sz);\n}\nCIMGUI_API void ImBitVector_Clear(ImBitVector* self)\n{\n    return self->Clear();\n}\nCIMGUI_API bool ImBitVector_TestBit(ImBitVector* self,int n)\n{\n    return self->TestBit(n);\n}\nCIMGUI_API void ImBitVector_SetBit(ImBitVector* self,int n)\n{\n    return self->SetBit(n);\n}\nCIMGUI_API void ImBitVector_ClearBit(ImBitVector* self,int n)\n{\n    return self->ClearBit(n);\n}\nCIMGUI_API void ImGuiTextIndex_clear(ImGuiTextIndex* self)\n{\n    return self->clear();\n}\nCIMGUI_API int ImGuiTextIndex_size(ImGuiTextIndex* self)\n{\n    return self->size();\n}\nCIMGUI_API const char* ImGuiTextIndex_get_line_begin(ImGuiTextIndex* self,const char* base,int n)\n{\n    return self->get_line_begin(base,n);\n}\nCIMGUI_API const char* ImGuiTextIndex_get_line_end(ImGuiTextIndex* self,const char* base,int n)\n{\n    return self->get_line_end(base,n);\n}\nCIMGUI_API void ImGuiTextIndex_append(ImGuiTextIndex* self,const char* base,int old_size,int new_size)\n{\n    return self->append(base,old_size,new_size);\n}\nCIMGUI_API ImGuiStoragePair* igImLowerBound(ImGuiStoragePair* in_begin,ImGuiStoragePair* in_end,ImGuiID key)\n{\n    return ImLowerBound(in_begin,in_end,key);\n}\nCIMGUI_API ImDrawListSharedData* ImDrawListSharedData_ImDrawListSharedData(void)\n{\n    return IM_NEW(ImDrawListSharedData)();\n}\nCIMGUI_API void ImDrawListSharedData_destroy(ImDrawListSharedData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImDrawListSharedData_SetCircleTessellationMaxError(ImDrawListSharedData* self,float max_error)\n{\n    return self->SetCircleTessellationMaxError(max_error);\n}\nCIMGUI_API ImDrawDataBuilder* ImDrawDataBuilder_ImDrawDataBuilder(void)\n{\n    return IM_NEW(ImDrawDataBuilder)();\n}\nCIMGUI_API void ImDrawDataBuilder_destroy(ImDrawDataBuilder* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void* ImGuiStyleVarInfo_GetVarPtr(ImGuiStyleVarInfo* self,void* parent)\n{\n    return self->GetVarPtr(parent);\n}\nCIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Int(ImGuiStyleVar idx,int v)\n{\n    return IM_NEW(ImGuiStyleMod)(idx,v);\n}\nCIMGUI_API void ImGuiStyleMod_destroy(ImGuiStyleMod* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Float(ImGuiStyleVar idx,float v)\n{\n    return IM_NEW(ImGuiStyleMod)(idx,v);\n}\nCIMGUI_API ImGuiStyleMod* ImGuiStyleMod_ImGuiStyleMod_Vec2(ImGuiStyleVar idx,ImVec2 v)\n{\n    return IM_NEW(ImGuiStyleMod)(idx,v);\n}\nCIMGUI_API ImGuiComboPreviewData* ImGuiComboPreviewData_ImGuiComboPreviewData(void)\n{\n    return IM_NEW(ImGuiComboPreviewData)();\n}\nCIMGUI_API void ImGuiComboPreviewData_destroy(ImGuiComboPreviewData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiMenuColumns* ImGuiMenuColumns_ImGuiMenuColumns(void)\n{\n    return IM_NEW(ImGuiMenuColumns)();\n}\nCIMGUI_API void ImGuiMenuColumns_destroy(ImGuiMenuColumns* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiMenuColumns_Update(ImGuiMenuColumns* self,float spacing,bool window_reappearing)\n{\n    return self->Update(spacing,window_reappearing);\n}\nCIMGUI_API float ImGuiMenuColumns_DeclColumns(ImGuiMenuColumns* self,float w_icon,float w_label,float w_shortcut,float w_mark)\n{\n    return self->DeclColumns(w_icon,w_label,w_shortcut,w_mark);\n}\nCIMGUI_API void ImGuiMenuColumns_CalcNextTotalWidth(ImGuiMenuColumns* self,bool update_offsets)\n{\n    return self->CalcNextTotalWidth(update_offsets);\n}\nCIMGUI_API ImGuiInputTextDeactivatedState* ImGuiInputTextDeactivatedState_ImGuiInputTextDeactivatedState(void)\n{\n    return IM_NEW(ImGuiInputTextDeactivatedState)();\n}\nCIMGUI_API void ImGuiInputTextDeactivatedState_destroy(ImGuiInputTextDeactivatedState* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiInputTextDeactivatedState_ClearFreeMemory(ImGuiInputTextDeactivatedState* self)\n{\n    return self->ClearFreeMemory();\n}\nCIMGUI_API ImGuiInputTextState* ImGuiInputTextState_ImGuiInputTextState(void)\n{\n    return IM_NEW(ImGuiInputTextState)();\n}\nCIMGUI_API void ImGuiInputTextState_destroy(ImGuiInputTextState* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiInputTextState_ClearText(ImGuiInputTextState* self)\n{\n    return self->ClearText();\n}\nCIMGUI_API void ImGuiInputTextState_ClearFreeMemory(ImGuiInputTextState* self)\n{\n    return self->ClearFreeMemory();\n}\nCIMGUI_API void ImGuiInputTextState_OnKeyPressed(ImGuiInputTextState* self,int key)\n{\n    return self->OnKeyPressed(key);\n}\nCIMGUI_API void ImGuiInputTextState_OnCharPressed(ImGuiInputTextState* self,unsigned int c)\n{\n    return self->OnCharPressed(c);\n}\nCIMGUI_API void ImGuiInputTextState_CursorAnimReset(ImGuiInputTextState* self)\n{\n    return self->CursorAnimReset();\n}\nCIMGUI_API void ImGuiInputTextState_CursorClamp(ImGuiInputTextState* self)\n{\n    return self->CursorClamp();\n}\nCIMGUI_API bool ImGuiInputTextState_HasSelection(ImGuiInputTextState* self)\n{\n    return self->HasSelection();\n}\nCIMGUI_API void ImGuiInputTextState_ClearSelection(ImGuiInputTextState* self)\n{\n    return self->ClearSelection();\n}\nCIMGUI_API int ImGuiInputTextState_GetCursorPos(ImGuiInputTextState* self)\n{\n    return self->GetCursorPos();\n}\nCIMGUI_API int ImGuiInputTextState_GetSelectionStart(ImGuiInputTextState* self)\n{\n    return self->GetSelectionStart();\n}\nCIMGUI_API int ImGuiInputTextState_GetSelectionEnd(ImGuiInputTextState* self)\n{\n    return self->GetSelectionEnd();\n}\nCIMGUI_API void ImGuiInputTextState_SelectAll(ImGuiInputTextState* self)\n{\n    return self->SelectAll();\n}\nCIMGUI_API void ImGuiInputTextState_ReloadUserBufAndSelectAll(ImGuiInputTextState* self)\n{\n    return self->ReloadUserBufAndSelectAll();\n}\nCIMGUI_API void ImGuiInputTextState_ReloadUserBufAndKeepSelection(ImGuiInputTextState* self)\n{\n    return self->ReloadUserBufAndKeepSelection();\n}\nCIMGUI_API void ImGuiInputTextState_ReloadUserBufAndMoveToEnd(ImGuiInputTextState* self)\n{\n    return self->ReloadUserBufAndMoveToEnd();\n}\nCIMGUI_API ImGuiNextWindowData* ImGuiNextWindowData_ImGuiNextWindowData(void)\n{\n    return IM_NEW(ImGuiNextWindowData)();\n}\nCIMGUI_API void ImGuiNextWindowData_destroy(ImGuiNextWindowData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiNextWindowData_ClearFlags(ImGuiNextWindowData* self)\n{\n    return self->ClearFlags();\n}\nCIMGUI_API ImGuiNextItemData* ImGuiNextItemData_ImGuiNextItemData(void)\n{\n    return IM_NEW(ImGuiNextItemData)();\n}\nCIMGUI_API void ImGuiNextItemData_destroy(ImGuiNextItemData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiNextItemData_ClearFlags(ImGuiNextItemData* self)\n{\n    return self->ClearFlags();\n}\nCIMGUI_API ImGuiLastItemData* ImGuiLastItemData_ImGuiLastItemData(void)\n{\n    return IM_NEW(ImGuiLastItemData)();\n}\nCIMGUI_API void ImGuiLastItemData_destroy(ImGuiLastItemData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiErrorRecoveryState* ImGuiErrorRecoveryState_ImGuiErrorRecoveryState(void)\n{\n    return IM_NEW(ImGuiErrorRecoveryState)();\n}\nCIMGUI_API void ImGuiErrorRecoveryState_destroy(ImGuiErrorRecoveryState* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiPtrOrIndex* ImGuiPtrOrIndex_ImGuiPtrOrIndex_Ptr(void* ptr)\n{\n    return IM_NEW(ImGuiPtrOrIndex)(ptr);\n}\nCIMGUI_API void ImGuiPtrOrIndex_destroy(ImGuiPtrOrIndex* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiPtrOrIndex* ImGuiPtrOrIndex_ImGuiPtrOrIndex_Int(int index)\n{\n    return IM_NEW(ImGuiPtrOrIndex)(index);\n}\nCIMGUI_API ImGuiPopupData* ImGuiPopupData_ImGuiPopupData(void)\n{\n    return IM_NEW(ImGuiPopupData)();\n}\nCIMGUI_API void ImGuiPopupData_destroy(ImGuiPopupData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiInputEvent* ImGuiInputEvent_ImGuiInputEvent(void)\n{\n    return IM_NEW(ImGuiInputEvent)();\n}\nCIMGUI_API void ImGuiInputEvent_destroy(ImGuiInputEvent* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiKeyRoutingData* ImGuiKeyRoutingData_ImGuiKeyRoutingData(void)\n{\n    return IM_NEW(ImGuiKeyRoutingData)();\n}\nCIMGUI_API void ImGuiKeyRoutingData_destroy(ImGuiKeyRoutingData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiKeyRoutingTable* ImGuiKeyRoutingTable_ImGuiKeyRoutingTable(void)\n{\n    return IM_NEW(ImGuiKeyRoutingTable)();\n}\nCIMGUI_API void ImGuiKeyRoutingTable_destroy(ImGuiKeyRoutingTable* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiKeyRoutingTable_Clear(ImGuiKeyRoutingTable* self)\n{\n    return self->Clear();\n}\nCIMGUI_API ImGuiKeyOwnerData* ImGuiKeyOwnerData_ImGuiKeyOwnerData(void)\n{\n    return IM_NEW(ImGuiKeyOwnerData)();\n}\nCIMGUI_API void ImGuiKeyOwnerData_destroy(ImGuiKeyOwnerData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiListClipperRange ImGuiListClipperRange_FromIndices(int min,int max)\n{\n    return ImGuiListClipperRange::FromIndices(min,max);\n}\nCIMGUI_API ImGuiListClipperRange ImGuiListClipperRange_FromPositions(float y1,float y2,int off_min,int off_max)\n{\n    return ImGuiListClipperRange::FromPositions(y1,y2,off_min,off_max);\n}\nCIMGUI_API ImGuiListClipperData* ImGuiListClipperData_ImGuiListClipperData(void)\n{\n    return IM_NEW(ImGuiListClipperData)();\n}\nCIMGUI_API void ImGuiListClipperData_destroy(ImGuiListClipperData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiListClipperData_Reset(ImGuiListClipperData* self,ImGuiListClipper* clipper)\n{\n    return self->Reset(clipper);\n}\nCIMGUI_API ImGuiNavItemData* ImGuiNavItemData_ImGuiNavItemData(void)\n{\n    return IM_NEW(ImGuiNavItemData)();\n}\nCIMGUI_API void ImGuiNavItemData_destroy(ImGuiNavItemData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiNavItemData_Clear(ImGuiNavItemData* self)\n{\n    return self->Clear();\n}\nCIMGUI_API ImGuiTypingSelectState* ImGuiTypingSelectState_ImGuiTypingSelectState(void)\n{\n    return IM_NEW(ImGuiTypingSelectState)();\n}\nCIMGUI_API void ImGuiTypingSelectState_destroy(ImGuiTypingSelectState* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiTypingSelectState_Clear(ImGuiTypingSelectState* self)\n{\n    return self->Clear();\n}\nCIMGUI_API ImGuiOldColumnData* ImGuiOldColumnData_ImGuiOldColumnData(void)\n{\n    return IM_NEW(ImGuiOldColumnData)();\n}\nCIMGUI_API void ImGuiOldColumnData_destroy(ImGuiOldColumnData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiOldColumns* ImGuiOldColumns_ImGuiOldColumns(void)\n{\n    return IM_NEW(ImGuiOldColumns)();\n}\nCIMGUI_API void ImGuiOldColumns_destroy(ImGuiOldColumns* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiBoxSelectState* ImGuiBoxSelectState_ImGuiBoxSelectState(void)\n{\n    return IM_NEW(ImGuiBoxSelectState)();\n}\nCIMGUI_API void ImGuiBoxSelectState_destroy(ImGuiBoxSelectState* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiMultiSelectTempData* ImGuiMultiSelectTempData_ImGuiMultiSelectTempData(void)\n{\n    return IM_NEW(ImGuiMultiSelectTempData)();\n}\nCIMGUI_API void ImGuiMultiSelectTempData_destroy(ImGuiMultiSelectTempData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiMultiSelectTempData_Clear(ImGuiMultiSelectTempData* self)\n{\n    return self->Clear();\n}\nCIMGUI_API void ImGuiMultiSelectTempData_ClearIO(ImGuiMultiSelectTempData* self)\n{\n    return self->ClearIO();\n}\nCIMGUI_API ImGuiMultiSelectState* ImGuiMultiSelectState_ImGuiMultiSelectState(void)\n{\n    return IM_NEW(ImGuiMultiSelectState)();\n}\nCIMGUI_API void ImGuiMultiSelectState_destroy(ImGuiMultiSelectState* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiDockNode* ImGuiDockNode_ImGuiDockNode(ImGuiID id)\n{\n    return IM_NEW(ImGuiDockNode)(id);\n}\nCIMGUI_API void ImGuiDockNode_destroy(ImGuiDockNode* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API bool ImGuiDockNode_IsRootNode(ImGuiDockNode* self)\n{\n    return self->IsRootNode();\n}\nCIMGUI_API bool ImGuiDockNode_IsDockSpace(ImGuiDockNode* self)\n{\n    return self->IsDockSpace();\n}\nCIMGUI_API bool ImGuiDockNode_IsFloatingNode(ImGuiDockNode* self)\n{\n    return self->IsFloatingNode();\n}\nCIMGUI_API bool ImGuiDockNode_IsCentralNode(ImGuiDockNode* self)\n{\n    return self->IsCentralNode();\n}\nCIMGUI_API bool ImGuiDockNode_IsHiddenTabBar(ImGuiDockNode* self)\n{\n    return self->IsHiddenTabBar();\n}\nCIMGUI_API bool ImGuiDockNode_IsNoTabBar(ImGuiDockNode* self)\n{\n    return self->IsNoTabBar();\n}\nCIMGUI_API bool ImGuiDockNode_IsSplitNode(ImGuiDockNode* self)\n{\n    return self->IsSplitNode();\n}\nCIMGUI_API bool ImGuiDockNode_IsLeafNode(ImGuiDockNode* self)\n{\n    return self->IsLeafNode();\n}\nCIMGUI_API bool ImGuiDockNode_IsEmpty(ImGuiDockNode* self)\n{\n    return self->IsEmpty();\n}\nCIMGUI_API void ImGuiDockNode_Rect(ImRect *pOut,ImGuiDockNode* self)\n{\n    *pOut = self->Rect();\n}\nCIMGUI_API void ImGuiDockNode_SetLocalFlags(ImGuiDockNode* self,ImGuiDockNodeFlags flags)\n{\n    return self->SetLocalFlags(flags);\n}\nCIMGUI_API void ImGuiDockNode_UpdateMergedFlags(ImGuiDockNode* self)\n{\n    return self->UpdateMergedFlags();\n}\nCIMGUI_API ImGuiDockContext* ImGuiDockContext_ImGuiDockContext(void)\n{\n    return IM_NEW(ImGuiDockContext)();\n}\nCIMGUI_API void ImGuiDockContext_destroy(ImGuiDockContext* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiViewportP* ImGuiViewportP_ImGuiViewportP(void)\n{\n    return IM_NEW(ImGuiViewportP)();\n}\nCIMGUI_API void ImGuiViewportP_destroy(ImGuiViewportP* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void ImGuiViewportP_ClearRequestFlags(ImGuiViewportP* self)\n{\n    return self->ClearRequestFlags();\n}\nCIMGUI_API void ImGuiViewportP_CalcWorkRectPos(ImVec2 *pOut,ImGuiViewportP* self,const ImVec2 inset_min)\n{\n    *pOut = self->CalcWorkRectPos(inset_min);\n}\nCIMGUI_API void ImGuiViewportP_CalcWorkRectSize(ImVec2 *pOut,ImGuiViewportP* self,const ImVec2 inset_min,const ImVec2 inset_max)\n{\n    *pOut = self->CalcWorkRectSize(inset_min,inset_max);\n}\nCIMGUI_API void ImGuiViewportP_UpdateWorkRect(ImGuiViewportP* self)\n{\n    return self->UpdateWorkRect();\n}\nCIMGUI_API void ImGuiViewportP_GetMainRect(ImRect *pOut,ImGuiViewportP* self)\n{\n    *pOut = self->GetMainRect();\n}\nCIMGUI_API void ImGuiViewportP_GetWorkRect(ImRect *pOut,ImGuiViewportP* self)\n{\n    *pOut = self->GetWorkRect();\n}\nCIMGUI_API void ImGuiViewportP_GetBuildWorkRect(ImRect *pOut,ImGuiViewportP* self)\n{\n    *pOut = self->GetBuildWorkRect();\n}\nCIMGUI_API ImGuiWindowSettings* ImGuiWindowSettings_ImGuiWindowSettings(void)\n{\n    return IM_NEW(ImGuiWindowSettings)();\n}\nCIMGUI_API void ImGuiWindowSettings_destroy(ImGuiWindowSettings* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API char* ImGuiWindowSettings_GetName(ImGuiWindowSettings* self)\n{\n    return self->GetName();\n}\nCIMGUI_API ImGuiSettingsHandler* ImGuiSettingsHandler_ImGuiSettingsHandler(void)\n{\n    return IM_NEW(ImGuiSettingsHandler)();\n}\nCIMGUI_API void ImGuiSettingsHandler_destroy(ImGuiSettingsHandler* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiDebugAllocInfo* ImGuiDebugAllocInfo_ImGuiDebugAllocInfo(void)\n{\n    return IM_NEW(ImGuiDebugAllocInfo)();\n}\nCIMGUI_API void ImGuiDebugAllocInfo_destroy(ImGuiDebugAllocInfo* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiStackLevelInfo* ImGuiStackLevelInfo_ImGuiStackLevelInfo(void)\n{\n    return IM_NEW(ImGuiStackLevelInfo)();\n}\nCIMGUI_API void ImGuiStackLevelInfo_destroy(ImGuiStackLevelInfo* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiIDStackTool* ImGuiIDStackTool_ImGuiIDStackTool(void)\n{\n    return IM_NEW(ImGuiIDStackTool)();\n}\nCIMGUI_API void ImGuiIDStackTool_destroy(ImGuiIDStackTool* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiContextHook* ImGuiContextHook_ImGuiContextHook(void)\n{\n    return IM_NEW(ImGuiContextHook)();\n}\nCIMGUI_API void ImGuiContextHook_destroy(ImGuiContextHook* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiContext* ImGuiContext_ImGuiContext(ImFontAtlas* shared_font_atlas)\n{\n    return IM_NEW(ImGuiContext)(shared_font_atlas);\n}\nCIMGUI_API void ImGuiContext_destroy(ImGuiContext* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiWindow* ImGuiWindow_ImGuiWindow(ImGuiContext* context,const char* name)\n{\n    return IM_NEW(ImGuiWindow)(context,name);\n}\nCIMGUI_API void ImGuiWindow_destroy(ImGuiWindow* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiID ImGuiWindow_GetID_Str(ImGuiWindow* self,const char* str,const char* str_end)\n{\n    return self->GetID(str,str_end);\n}\nCIMGUI_API ImGuiID ImGuiWindow_GetID_Ptr(ImGuiWindow* self,const void* ptr)\n{\n    return self->GetID(ptr);\n}\nCIMGUI_API ImGuiID ImGuiWindow_GetID_Int(ImGuiWindow* self,int n)\n{\n    return self->GetID(n);\n}\nCIMGUI_API ImGuiID ImGuiWindow_GetIDFromPos(ImGuiWindow* self,const ImVec2 p_abs)\n{\n    return self->GetIDFromPos(p_abs);\n}\nCIMGUI_API ImGuiID ImGuiWindow_GetIDFromRectangle(ImGuiWindow* self,const ImRect r_abs)\n{\n    return self->GetIDFromRectangle(r_abs);\n}\nCIMGUI_API void ImGuiWindow_Rect(ImRect *pOut,ImGuiWindow* self)\n{\n    *pOut = self->Rect();\n}\nCIMGUI_API void ImGuiWindow_TitleBarRect(ImRect *pOut,ImGuiWindow* self)\n{\n    *pOut = self->TitleBarRect();\n}\nCIMGUI_API void ImGuiWindow_MenuBarRect(ImRect *pOut,ImGuiWindow* self)\n{\n    *pOut = self->MenuBarRect();\n}\nCIMGUI_API ImGuiTabItem* ImGuiTabItem_ImGuiTabItem(void)\n{\n    return IM_NEW(ImGuiTabItem)();\n}\nCIMGUI_API void ImGuiTabItem_destroy(ImGuiTabItem* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiTabBar* ImGuiTabBar_ImGuiTabBar(void)\n{\n    return IM_NEW(ImGuiTabBar)();\n}\nCIMGUI_API void ImGuiTabBar_destroy(ImGuiTabBar* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiTableColumn* ImGuiTableColumn_ImGuiTableColumn(void)\n{\n    return IM_NEW(ImGuiTableColumn)();\n}\nCIMGUI_API void ImGuiTableColumn_destroy(ImGuiTableColumn* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiTableInstanceData* ImGuiTableInstanceData_ImGuiTableInstanceData(void)\n{\n    return IM_NEW(ImGuiTableInstanceData)();\n}\nCIMGUI_API void ImGuiTableInstanceData_destroy(ImGuiTableInstanceData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiTable* ImGuiTable_ImGuiTable(void)\n{\n    return IM_NEW(ImGuiTable)();\n}\nCIMGUI_API void ImGuiTable_destroy(ImGuiTable* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiTableTempData* ImGuiTableTempData_ImGuiTableTempData(void)\n{\n    return IM_NEW(ImGuiTableTempData)();\n}\nCIMGUI_API void ImGuiTableTempData_destroy(ImGuiTableTempData* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiTableColumnSettings* ImGuiTableColumnSettings_ImGuiTableColumnSettings(void)\n{\n    return IM_NEW(ImGuiTableColumnSettings)();\n}\nCIMGUI_API void ImGuiTableColumnSettings_destroy(ImGuiTableColumnSettings* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiTableSettings* ImGuiTableSettings_ImGuiTableSettings(void)\n{\n    return IM_NEW(ImGuiTableSettings)();\n}\nCIMGUI_API void ImGuiTableSettings_destroy(ImGuiTableSettings* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API ImGuiTableColumnSettings* ImGuiTableSettings_GetColumnSettings(ImGuiTableSettings* self)\n{\n    return self->GetColumnSettings();\n}\nCIMGUI_API ImGuiIO* igGetIO_ContextPtr(ImGuiContext* ctx)\n{\n    return &ImGui::GetIO(ctx);\n}\nCIMGUI_API ImGuiPlatformIO* igGetPlatformIO_ContextPtr(ImGuiContext* ctx)\n{\n    return &ImGui::GetPlatformIO(ctx);\n}\nCIMGUI_API ImGuiWindow* igGetCurrentWindowRead()\n{\n    return ImGui::GetCurrentWindowRead();\n}\nCIMGUI_API ImGuiWindow* igGetCurrentWindow()\n{\n    return ImGui::GetCurrentWindow();\n}\nCIMGUI_API ImGuiWindow* igFindWindowByID(ImGuiID id)\n{\n    return ImGui::FindWindowByID(id);\n}\nCIMGUI_API ImGuiWindow* igFindWindowByName(const char* name)\n{\n    return ImGui::FindWindowByName(name);\n}\nCIMGUI_API void igUpdateWindowParentAndRootLinks(ImGuiWindow* window,ImGuiWindowFlags flags,ImGuiWindow* parent_window)\n{\n    return ImGui::UpdateWindowParentAndRootLinks(window,flags,parent_window);\n}\nCIMGUI_API void igUpdateWindowSkipRefresh(ImGuiWindow* window)\n{\n    return ImGui::UpdateWindowSkipRefresh(window);\n}\nCIMGUI_API void igCalcWindowNextAutoFitSize(ImVec2 *pOut,ImGuiWindow* window)\n{\n    *pOut = ImGui::CalcWindowNextAutoFitSize(window);\n}\nCIMGUI_API bool igIsWindowChildOf(ImGuiWindow* window,ImGuiWindow* potential_parent,bool popup_hierarchy,bool dock_hierarchy)\n{\n    return ImGui::IsWindowChildOf(window,potential_parent,popup_hierarchy,dock_hierarchy);\n}\nCIMGUI_API bool igIsWindowWithinBeginStackOf(ImGuiWindow* window,ImGuiWindow* potential_parent)\n{\n    return ImGui::IsWindowWithinBeginStackOf(window,potential_parent);\n}\nCIMGUI_API bool igIsWindowAbove(ImGuiWindow* potential_above,ImGuiWindow* potential_below)\n{\n    return ImGui::IsWindowAbove(potential_above,potential_below);\n}\nCIMGUI_API bool igIsWindowNavFocusable(ImGuiWindow* window)\n{\n    return ImGui::IsWindowNavFocusable(window);\n}\nCIMGUI_API void igSetWindowPos_WindowPtr(ImGuiWindow* window,const ImVec2 pos,ImGuiCond cond)\n{\n    return ImGui::SetWindowPos(window,pos,cond);\n}\nCIMGUI_API void igSetWindowSize_WindowPtr(ImGuiWindow* window,const ImVec2 size,ImGuiCond cond)\n{\n    return ImGui::SetWindowSize(window,size,cond);\n}\nCIMGUI_API void igSetWindowCollapsed_WindowPtr(ImGuiWindow* window,bool collapsed,ImGuiCond cond)\n{\n    return ImGui::SetWindowCollapsed(window,collapsed,cond);\n}\nCIMGUI_API void igSetWindowHitTestHole(ImGuiWindow* window,const ImVec2 pos,const ImVec2 size)\n{\n    return ImGui::SetWindowHitTestHole(window,pos,size);\n}\nCIMGUI_API void igSetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window)\n{\n    return ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(window);\n}\nCIMGUI_API void igSetWindowParentWindowForFocusRoute(ImGuiWindow* window,ImGuiWindow* parent_window)\n{\n    return ImGui::SetWindowParentWindowForFocusRoute(window,parent_window);\n}\nCIMGUI_API void igWindowRectAbsToRel(ImRect *pOut,ImGuiWindow* window,const ImRect r)\n{\n    *pOut = ImGui::WindowRectAbsToRel(window,r);\n}\nCIMGUI_API void igWindowRectRelToAbs(ImRect *pOut,ImGuiWindow* window,const ImRect r)\n{\n    *pOut = ImGui::WindowRectRelToAbs(window,r);\n}\nCIMGUI_API void igWindowPosAbsToRel(ImVec2 *pOut,ImGuiWindow* window,const ImVec2 p)\n{\n    *pOut = ImGui::WindowPosAbsToRel(window,p);\n}\nCIMGUI_API void igWindowPosRelToAbs(ImVec2 *pOut,ImGuiWindow* window,const ImVec2 p)\n{\n    *pOut = ImGui::WindowPosRelToAbs(window,p);\n}\nCIMGUI_API void igFocusWindow(ImGuiWindow* window,ImGuiFocusRequestFlags flags)\n{\n    return ImGui::FocusWindow(window,flags);\n}\nCIMGUI_API void igFocusTopMostWindowUnderOne(ImGuiWindow* under_this_window,ImGuiWindow* ignore_window,ImGuiViewport* filter_viewport,ImGuiFocusRequestFlags flags)\n{\n    return ImGui::FocusTopMostWindowUnderOne(under_this_window,ignore_window,filter_viewport,flags);\n}\nCIMGUI_API void igBringWindowToFocusFront(ImGuiWindow* window)\n{\n    return ImGui::BringWindowToFocusFront(window);\n}\nCIMGUI_API void igBringWindowToDisplayFront(ImGuiWindow* window)\n{\n    return ImGui::BringWindowToDisplayFront(window);\n}\nCIMGUI_API void igBringWindowToDisplayBack(ImGuiWindow* window)\n{\n    return ImGui::BringWindowToDisplayBack(window);\n}\nCIMGUI_API void igBringWindowToDisplayBehind(ImGuiWindow* window,ImGuiWindow* above_window)\n{\n    return ImGui::BringWindowToDisplayBehind(window,above_window);\n}\nCIMGUI_API int igFindWindowDisplayIndex(ImGuiWindow* window)\n{\n    return ImGui::FindWindowDisplayIndex(window);\n}\nCIMGUI_API ImGuiWindow* igFindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window)\n{\n    return ImGui::FindBottomMostVisibleWindowWithinBeginStack(window);\n}\nCIMGUI_API void igSetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags)\n{\n    return ImGui::SetNextWindowRefreshPolicy(flags);\n}\nCIMGUI_API void igRegisterUserTexture(ImTextureData* tex)\n{\n    return ImGui::RegisterUserTexture(tex);\n}\nCIMGUI_API void igUnregisterUserTexture(ImTextureData* tex)\n{\n    return ImGui::UnregisterUserTexture(tex);\n}\nCIMGUI_API void igRegisterFontAtlas(ImFontAtlas* atlas)\n{\n    return ImGui::RegisterFontAtlas(atlas);\n}\nCIMGUI_API void igUnregisterFontAtlas(ImFontAtlas* atlas)\n{\n    return ImGui::UnregisterFontAtlas(atlas);\n}\nCIMGUI_API void igSetCurrentFont(ImFont* font,float font_size_before_scaling,float font_size_after_scaling)\n{\n    return ImGui::SetCurrentFont(font,font_size_before_scaling,font_size_after_scaling);\n}\nCIMGUI_API void igUpdateCurrentFontSize(float restore_font_size_after_scaling)\n{\n    return ImGui::UpdateCurrentFontSize(restore_font_size_after_scaling);\n}\nCIMGUI_API void igSetFontRasterizerDensity(float rasterizer_density)\n{\n    return ImGui::SetFontRasterizerDensity(rasterizer_density);\n}\nCIMGUI_API float igGetFontRasterizerDensity()\n{\n    return ImGui::GetFontRasterizerDensity();\n}\nCIMGUI_API float igGetRoundedFontSize(float size)\n{\n    return ImGui::GetRoundedFontSize(size);\n}\nCIMGUI_API ImFont* igGetDefaultFont()\n{\n    return ImGui::GetDefaultFont();\n}\nCIMGUI_API void igPushPasswordFont()\n{\n    return ImGui::PushPasswordFont();\n}\nCIMGUI_API void igPopPasswordFont()\n{\n    return ImGui::PopPasswordFont();\n}\nCIMGUI_API ImDrawList* igGetForegroundDrawList_WindowPtr(ImGuiWindow* window)\n{\n    return ImGui::GetForegroundDrawList(window);\n}\nCIMGUI_API void igAddDrawListToDrawDataEx(ImDrawData* draw_data,ImVector_ImDrawListPtr* out_list,ImDrawList* draw_list)\n{\n    return ImGui::AddDrawListToDrawDataEx(draw_data,out_list,draw_list);\n}\nCIMGUI_API void igInitialize()\n{\n    return ImGui::Initialize();\n}\nCIMGUI_API void igShutdown()\n{\n    return ImGui::Shutdown();\n}\nCIMGUI_API void igUpdateInputEvents(bool trickle_fast_inputs)\n{\n    return ImGui::UpdateInputEvents(trickle_fast_inputs);\n}\nCIMGUI_API void igUpdateHoveredWindowAndCaptureFlags(const ImVec2 mouse_pos)\n{\n    return ImGui::UpdateHoveredWindowAndCaptureFlags(mouse_pos);\n}\nCIMGUI_API void igFindHoveredWindowEx(const ImVec2 pos,bool find_first_and_in_any_viewport,ImGuiWindow** out_hovered_window,ImGuiWindow** out_hovered_window_under_moving_window)\n{\n    return ImGui::FindHoveredWindowEx(pos,find_first_and_in_any_viewport,out_hovered_window,out_hovered_window_under_moving_window);\n}\nCIMGUI_API void igStartMouseMovingWindow(ImGuiWindow* window)\n{\n    return ImGui::StartMouseMovingWindow(window);\n}\nCIMGUI_API void igStartMouseMovingWindowOrNode(ImGuiWindow* window,ImGuiDockNode* node,bool undock)\n{\n    return ImGui::StartMouseMovingWindowOrNode(window,node,undock);\n}\nCIMGUI_API void igUpdateMouseMovingWindowNewFrame()\n{\n    return ImGui::UpdateMouseMovingWindowNewFrame();\n}\nCIMGUI_API void igUpdateMouseMovingWindowEndFrame()\n{\n    return ImGui::UpdateMouseMovingWindowEndFrame();\n}\nCIMGUI_API ImGuiID igAddContextHook(ImGuiContext* context,const ImGuiContextHook* hook)\n{\n    return ImGui::AddContextHook(context,hook);\n}\nCIMGUI_API void igRemoveContextHook(ImGuiContext* context,ImGuiID hook_to_remove)\n{\n    return ImGui::RemoveContextHook(context,hook_to_remove);\n}\nCIMGUI_API void igCallContextHooks(ImGuiContext* context,ImGuiContextHookType type)\n{\n    return ImGui::CallContextHooks(context,type);\n}\nCIMGUI_API void igTranslateWindowsInViewport(ImGuiViewportP* viewport,const ImVec2 old_pos,const ImVec2 new_pos,const ImVec2 old_size,const ImVec2 new_size)\n{\n    return ImGui::TranslateWindowsInViewport(viewport,old_pos,new_pos,old_size,new_size);\n}\nCIMGUI_API void igScaleWindowsInViewport(ImGuiViewportP* viewport,float scale)\n{\n    return ImGui::ScaleWindowsInViewport(viewport,scale);\n}\nCIMGUI_API void igDestroyPlatformWindow(ImGuiViewportP* viewport)\n{\n    return ImGui::DestroyPlatformWindow(viewport);\n}\nCIMGUI_API void igSetWindowViewport(ImGuiWindow* window,ImGuiViewportP* viewport)\n{\n    return ImGui::SetWindowViewport(window,viewport);\n}\nCIMGUI_API void igSetCurrentViewport(ImGuiWindow* window,ImGuiViewportP* viewport)\n{\n    return ImGui::SetCurrentViewport(window,viewport);\n}\nCIMGUI_API const ImGuiPlatformMonitor* igGetViewportPlatformMonitor(ImGuiViewport* viewport)\n{\n    return ImGui::GetViewportPlatformMonitor(viewport);\n}\nCIMGUI_API ImGuiViewportP* igFindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos)\n{\n    return ImGui::FindHoveredViewportFromPlatformWindowStack(mouse_platform_pos);\n}\nCIMGUI_API void igMarkIniSettingsDirty_Nil()\n{\n    return ImGui::MarkIniSettingsDirty();\n}\nCIMGUI_API void igMarkIniSettingsDirty_WindowPtr(ImGuiWindow* window)\n{\n    return ImGui::MarkIniSettingsDirty(window);\n}\nCIMGUI_API void igClearIniSettings()\n{\n    return ImGui::ClearIniSettings();\n}\nCIMGUI_API void igAddSettingsHandler(const ImGuiSettingsHandler* handler)\n{\n    return ImGui::AddSettingsHandler(handler);\n}\nCIMGUI_API void igRemoveSettingsHandler(const char* type_name)\n{\n    return ImGui::RemoveSettingsHandler(type_name);\n}\nCIMGUI_API ImGuiSettingsHandler* igFindSettingsHandler(const char* type_name)\n{\n    return ImGui::FindSettingsHandler(type_name);\n}\nCIMGUI_API ImGuiWindowSettings* igCreateNewWindowSettings(const char* name)\n{\n    return ImGui::CreateNewWindowSettings(name);\n}\nCIMGUI_API ImGuiWindowSettings* igFindWindowSettingsByID(ImGuiID id)\n{\n    return ImGui::FindWindowSettingsByID(id);\n}\nCIMGUI_API ImGuiWindowSettings* igFindWindowSettingsByWindow(ImGuiWindow* window)\n{\n    return ImGui::FindWindowSettingsByWindow(window);\n}\nCIMGUI_API void igClearWindowSettings(const char* name)\n{\n    return ImGui::ClearWindowSettings(name);\n}\nCIMGUI_API void igLocalizeRegisterEntries(const ImGuiLocEntry* entries,int count)\n{\n    return ImGui::LocalizeRegisterEntries(entries,count);\n}\nCIMGUI_API const char* igLocalizeGetMsg(ImGuiLocKey key)\n{\n    return ImGui::LocalizeGetMsg(key);\n}\nCIMGUI_API void igSetScrollX_WindowPtr(ImGuiWindow* window,float scroll_x)\n{\n    return ImGui::SetScrollX(window,scroll_x);\n}\nCIMGUI_API void igSetScrollY_WindowPtr(ImGuiWindow* window,float scroll_y)\n{\n    return ImGui::SetScrollY(window,scroll_y);\n}\nCIMGUI_API void igSetScrollFromPosX_WindowPtr(ImGuiWindow* window,float local_x,float center_x_ratio)\n{\n    return ImGui::SetScrollFromPosX(window,local_x,center_x_ratio);\n}\nCIMGUI_API void igSetScrollFromPosY_WindowPtr(ImGuiWindow* window,float local_y,float center_y_ratio)\n{\n    return ImGui::SetScrollFromPosY(window,local_y,center_y_ratio);\n}\nCIMGUI_API void igScrollToItem(ImGuiScrollFlags flags)\n{\n    return ImGui::ScrollToItem(flags);\n}\nCIMGUI_API void igScrollToRect(ImGuiWindow* window,const ImRect rect,ImGuiScrollFlags flags)\n{\n    return ImGui::ScrollToRect(window,rect,flags);\n}\nCIMGUI_API void igScrollToRectEx(ImVec2 *pOut,ImGuiWindow* window,const ImRect rect,ImGuiScrollFlags flags)\n{\n    *pOut = ImGui::ScrollToRectEx(window,rect,flags);\n}\nCIMGUI_API void igScrollToBringRectIntoView(ImGuiWindow* window,const ImRect rect)\n{\n    return ImGui::ScrollToBringRectIntoView(window,rect);\n}\nCIMGUI_API ImGuiItemStatusFlags igGetItemStatusFlags()\n{\n    return ImGui::GetItemStatusFlags();\n}\nCIMGUI_API ImGuiItemFlags igGetItemFlags()\n{\n    return ImGui::GetItemFlags();\n}\nCIMGUI_API ImGuiID igGetActiveID()\n{\n    return ImGui::GetActiveID();\n}\nCIMGUI_API ImGuiID igGetFocusID()\n{\n    return ImGui::GetFocusID();\n}\nCIMGUI_API void igSetActiveID(ImGuiID id,ImGuiWindow* window)\n{\n    return ImGui::SetActiveID(id,window);\n}\nCIMGUI_API void igSetFocusID(ImGuiID id,ImGuiWindow* window)\n{\n    return ImGui::SetFocusID(id,window);\n}\nCIMGUI_API void igClearActiveID()\n{\n    return ImGui::ClearActiveID();\n}\nCIMGUI_API ImGuiID igGetHoveredID()\n{\n    return ImGui::GetHoveredID();\n}\nCIMGUI_API void igSetHoveredID(ImGuiID id)\n{\n    return ImGui::SetHoveredID(id);\n}\nCIMGUI_API void igKeepAliveID(ImGuiID id)\n{\n    return ImGui::KeepAliveID(id);\n}\nCIMGUI_API void igMarkItemEdited(ImGuiID id)\n{\n    return ImGui::MarkItemEdited(id);\n}\nCIMGUI_API void igPushOverrideID(ImGuiID id)\n{\n    return ImGui::PushOverrideID(id);\n}\nCIMGUI_API ImGuiID igGetIDWithSeed_Str(const char* str_id_begin,const char* str_id_end,ImGuiID seed)\n{\n    return ImGui::GetIDWithSeed(str_id_begin,str_id_end,seed);\n}\nCIMGUI_API ImGuiID igGetIDWithSeed_Int(int n,ImGuiID seed)\n{\n    return ImGui::GetIDWithSeed(n,seed);\n}\nCIMGUI_API void igItemSize_Vec2(const ImVec2 size,float text_baseline_y)\n{\n    return ImGui::ItemSize(size,text_baseline_y);\n}\nCIMGUI_API void igItemSize_Rect(const ImRect bb,float text_baseline_y)\n{\n    return ImGui::ItemSize(bb,text_baseline_y);\n}\nCIMGUI_API bool igItemAdd(const ImRect bb,ImGuiID id,const ImRect* nav_bb,ImGuiItemFlags extra_flags)\n{\n    return ImGui::ItemAdd(bb,id,nav_bb,extra_flags);\n}\nCIMGUI_API bool igItemHoverable(const ImRect bb,ImGuiID id,ImGuiItemFlags item_flags)\n{\n    return ImGui::ItemHoverable(bb,id,item_flags);\n}\nCIMGUI_API bool igIsWindowContentHoverable(ImGuiWindow* window,ImGuiHoveredFlags flags)\n{\n    return ImGui::IsWindowContentHoverable(window,flags);\n}\nCIMGUI_API bool igIsClippedEx(const ImRect bb,ImGuiID id)\n{\n    return ImGui::IsClippedEx(bb,id);\n}\nCIMGUI_API void igSetLastItemData(ImGuiID item_id,ImGuiItemFlags item_flags,ImGuiItemStatusFlags status_flags,const ImRect item_rect)\n{\n    return ImGui::SetLastItemData(item_id,item_flags,status_flags,item_rect);\n}\nCIMGUI_API void igCalcItemSize(ImVec2 *pOut,ImVec2 size,float default_w,float default_h)\n{\n    *pOut = ImGui::CalcItemSize(size,default_w,default_h);\n}\nCIMGUI_API float igCalcWrapWidthForPos(const ImVec2 pos,float wrap_pos_x)\n{\n    return ImGui::CalcWrapWidthForPos(pos,wrap_pos_x);\n}\nCIMGUI_API void igPushMultiItemsWidths(int components,float width_full)\n{\n    return ImGui::PushMultiItemsWidths(components,width_full);\n}\nCIMGUI_API void igShrinkWidths(ImGuiShrinkWidthItem* items,int count,float width_excess,float width_min)\n{\n    return ImGui::ShrinkWidths(items,count,width_excess,width_min);\n}\nCIMGUI_API const ImGuiStyleVarInfo* igGetStyleVarInfo(ImGuiStyleVar idx)\n{\n    return ImGui::GetStyleVarInfo(idx);\n}\nCIMGUI_API void igBeginDisabledOverrideReenable()\n{\n    return ImGui::BeginDisabledOverrideReenable();\n}\nCIMGUI_API void igEndDisabledOverrideReenable()\n{\n    return ImGui::EndDisabledOverrideReenable();\n}\nCIMGUI_API void igLogBegin(ImGuiLogFlags flags,int auto_open_depth)\n{\n    return ImGui::LogBegin(flags,auto_open_depth);\n}\nCIMGUI_API void igLogToBuffer(int auto_open_depth)\n{\n    return ImGui::LogToBuffer(auto_open_depth);\n}\nCIMGUI_API void igLogRenderedText(const ImVec2* ref_pos,const char* text,const char* text_end)\n{\n    return ImGui::LogRenderedText(ref_pos,text,text_end);\n}\nCIMGUI_API void igLogSetNextTextDecoration(const char* prefix,const char* suffix)\n{\n    return ImGui::LogSetNextTextDecoration(prefix,suffix);\n}\nCIMGUI_API bool igBeginChildEx(const char* name,ImGuiID id,const ImVec2 size_arg,ImGuiChildFlags child_flags,ImGuiWindowFlags window_flags)\n{\n    return ImGui::BeginChildEx(name,id,size_arg,child_flags,window_flags);\n}\nCIMGUI_API bool igBeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_window_flags)\n{\n    return ImGui::BeginPopupEx(id,extra_window_flags);\n}\nCIMGUI_API bool igBeginPopupMenuEx(ImGuiID id,const char* label,ImGuiWindowFlags extra_window_flags)\n{\n    return ImGui::BeginPopupMenuEx(id,label,extra_window_flags);\n}\nCIMGUI_API void igOpenPopupEx(ImGuiID id,ImGuiPopupFlags popup_flags)\n{\n    return ImGui::OpenPopupEx(id,popup_flags);\n}\nCIMGUI_API void igClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)\n{\n    return ImGui::ClosePopupToLevel(remaining,restore_focus_to_window_under_popup);\n}\nCIMGUI_API void igClosePopupsOverWindow(ImGuiWindow* ref_window,bool restore_focus_to_window_under_popup)\n{\n    return ImGui::ClosePopupsOverWindow(ref_window,restore_focus_to_window_under_popup);\n}\nCIMGUI_API void igClosePopupsExceptModals()\n{\n    return ImGui::ClosePopupsExceptModals();\n}\nCIMGUI_API bool igIsPopupOpen_ID(ImGuiID id,ImGuiPopupFlags popup_flags)\n{\n    return ImGui::IsPopupOpen(id,popup_flags);\n}\nCIMGUI_API void igGetPopupAllowedExtentRect(ImRect *pOut,ImGuiWindow* window)\n{\n    *pOut = ImGui::GetPopupAllowedExtentRect(window);\n}\nCIMGUI_API ImGuiWindow* igGetTopMostPopupModal()\n{\n    return ImGui::GetTopMostPopupModal();\n}\nCIMGUI_API ImGuiWindow* igGetTopMostAndVisiblePopupModal()\n{\n    return ImGui::GetTopMostAndVisiblePopupModal();\n}\nCIMGUI_API ImGuiWindow* igFindBlockingModal(ImGuiWindow* window)\n{\n    return ImGui::FindBlockingModal(window);\n}\nCIMGUI_API void igFindBestWindowPosForPopup(ImVec2 *pOut,ImGuiWindow* window)\n{\n    *pOut = ImGui::FindBestWindowPosForPopup(window);\n}\nCIMGUI_API void igFindBestWindowPosForPopupEx(ImVec2 *pOut,const ImVec2 ref_pos,const ImVec2 size,ImGuiDir* last_dir,const ImRect r_outer,const ImRect r_avoid,ImGuiPopupPositionPolicy policy)\n{\n    *pOut = ImGui::FindBestWindowPosForPopupEx(ref_pos,size,last_dir,r_outer,r_avoid,policy);\n}\nCIMGUI_API bool igBeginTooltipEx(ImGuiTooltipFlags tooltip_flags,ImGuiWindowFlags extra_window_flags)\n{\n    return ImGui::BeginTooltipEx(tooltip_flags,extra_window_flags);\n}\nCIMGUI_API bool igBeginTooltipHidden()\n{\n    return ImGui::BeginTooltipHidden();\n}\nCIMGUI_API bool igBeginViewportSideBar(const char* name,ImGuiViewport* viewport,ImGuiDir dir,float size,ImGuiWindowFlags window_flags)\n{\n    return ImGui::BeginViewportSideBar(name,viewport,dir,size,window_flags);\n}\nCIMGUI_API bool igBeginMenuEx(const char* label,const char* icon,bool enabled)\n{\n    return ImGui::BeginMenuEx(label,icon,enabled);\n}\nCIMGUI_API bool igMenuItemEx(const char* label,const char* icon,const char* shortcut,bool selected,bool enabled)\n{\n    return ImGui::MenuItemEx(label,icon,shortcut,selected,enabled);\n}\nCIMGUI_API bool igBeginComboPopup(ImGuiID popup_id,const ImRect bb,ImGuiComboFlags flags)\n{\n    return ImGui::BeginComboPopup(popup_id,bb,flags);\n}\nCIMGUI_API bool igBeginComboPreview()\n{\n    return ImGui::BeginComboPreview();\n}\nCIMGUI_API void igEndComboPreview()\n{\n    return ImGui::EndComboPreview();\n}\nCIMGUI_API void igNavInitWindow(ImGuiWindow* window,bool force_reinit)\n{\n    return ImGui::NavInitWindow(window,force_reinit);\n}\nCIMGUI_API void igNavInitRequestApplyResult()\n{\n    return ImGui::NavInitRequestApplyResult();\n}\nCIMGUI_API bool igNavMoveRequestButNoResultYet()\n{\n    return ImGui::NavMoveRequestButNoResultYet();\n}\nCIMGUI_API void igNavMoveRequestSubmit(ImGuiDir move_dir,ImGuiDir clip_dir,ImGuiNavMoveFlags move_flags,ImGuiScrollFlags scroll_flags)\n{\n    return ImGui::NavMoveRequestSubmit(move_dir,clip_dir,move_flags,scroll_flags);\n}\nCIMGUI_API void igNavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,ImGuiNavMoveFlags move_flags,ImGuiScrollFlags scroll_flags)\n{\n    return ImGui::NavMoveRequestForward(move_dir,clip_dir,move_flags,scroll_flags);\n}\nCIMGUI_API void igNavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)\n{\n    return ImGui::NavMoveRequestResolveWithLastItem(result);\n}\nCIMGUI_API void igNavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result,const ImGuiTreeNodeStackData* tree_node_data)\n{\n    return ImGui::NavMoveRequestResolveWithPastTreeNode(result,tree_node_data);\n}\nCIMGUI_API void igNavMoveRequestCancel()\n{\n    return ImGui::NavMoveRequestCancel();\n}\nCIMGUI_API void igNavMoveRequestApplyResult()\n{\n    return ImGui::NavMoveRequestApplyResult();\n}\nCIMGUI_API void igNavMoveRequestTryWrapping(ImGuiWindow* window,ImGuiNavMoveFlags move_flags)\n{\n    return ImGui::NavMoveRequestTryWrapping(window,move_flags);\n}\nCIMGUI_API void igNavHighlightActivated(ImGuiID id)\n{\n    return ImGui::NavHighlightActivated(id);\n}\nCIMGUI_API void igNavClearPreferredPosForAxis(ImGuiAxis axis)\n{\n    return ImGui::NavClearPreferredPosForAxis(axis);\n}\nCIMGUI_API void igSetNavCursorVisibleAfterMove()\n{\n    return ImGui::SetNavCursorVisibleAfterMove();\n}\nCIMGUI_API void igNavUpdateCurrentWindowIsScrollPushableX()\n{\n    return ImGui::NavUpdateCurrentWindowIsScrollPushableX();\n}\nCIMGUI_API void igSetNavWindow(ImGuiWindow* window)\n{\n    return ImGui::SetNavWindow(window);\n}\nCIMGUI_API void igSetNavID(ImGuiID id,ImGuiNavLayer nav_layer,ImGuiID focus_scope_id,const ImRect rect_rel)\n{\n    return ImGui::SetNavID(id,nav_layer,focus_scope_id,rect_rel);\n}\nCIMGUI_API void igSetNavFocusScope(ImGuiID focus_scope_id)\n{\n    return ImGui::SetNavFocusScope(focus_scope_id);\n}\nCIMGUI_API void igFocusItem()\n{\n    return ImGui::FocusItem();\n}\nCIMGUI_API void igActivateItemByID(ImGuiID id)\n{\n    return ImGui::ActivateItemByID(id);\n}\nCIMGUI_API bool igIsNamedKey(ImGuiKey key)\n{\n    return ImGui::IsNamedKey(key);\n}\nCIMGUI_API bool igIsNamedKeyOrMod(ImGuiKey key)\n{\n    return ImGui::IsNamedKeyOrMod(key);\n}\nCIMGUI_API bool igIsLegacyKey(ImGuiKey key)\n{\n    return ImGui::IsLegacyKey(key);\n}\nCIMGUI_API bool igIsKeyboardKey(ImGuiKey key)\n{\n    return ImGui::IsKeyboardKey(key);\n}\nCIMGUI_API bool igIsGamepadKey(ImGuiKey key)\n{\n    return ImGui::IsGamepadKey(key);\n}\nCIMGUI_API bool igIsMouseKey(ImGuiKey key)\n{\n    return ImGui::IsMouseKey(key);\n}\nCIMGUI_API bool igIsAliasKey(ImGuiKey key)\n{\n    return ImGui::IsAliasKey(key);\n}\nCIMGUI_API bool igIsLRModKey(ImGuiKey key)\n{\n    return ImGui::IsLRModKey(key);\n}\nCIMGUI_API ImGuiKeyChord igFixupKeyChord(ImGuiKeyChord key_chord)\n{\n    return ImGui::FixupKeyChord(key_chord);\n}\nCIMGUI_API ImGuiKey igConvertSingleModFlagToKey(ImGuiKey key)\n{\n    return ImGui::ConvertSingleModFlagToKey(key);\n}\nCIMGUI_API ImGuiKeyData* igGetKeyData_ContextPtr(ImGuiContext* ctx,ImGuiKey key)\n{\n    return ImGui::GetKeyData(ctx,key);\n}\nCIMGUI_API ImGuiKeyData* igGetKeyData_Key(ImGuiKey key)\n{\n    return ImGui::GetKeyData(key);\n}\nCIMGUI_API const char* igGetKeyChordName(ImGuiKeyChord key_chord)\n{\n    return ImGui::GetKeyChordName(key_chord);\n}\nCIMGUI_API ImGuiKey igMouseButtonToKey(ImGuiMouseButton button)\n{\n    return ImGui::MouseButtonToKey(button);\n}\nCIMGUI_API bool igIsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold)\n{\n    return ImGui::IsMouseDragPastThreshold(button,lock_threshold);\n}\nCIMGUI_API void igGetKeyMagnitude2d(ImVec2 *pOut,ImGuiKey key_left,ImGuiKey key_right,ImGuiKey key_up,ImGuiKey key_down)\n{\n    *pOut = ImGui::GetKeyMagnitude2d(key_left,key_right,key_up,key_down);\n}\nCIMGUI_API float igGetNavTweakPressedAmount(ImGuiAxis axis)\n{\n    return ImGui::GetNavTweakPressedAmount(axis);\n}\nCIMGUI_API int igCalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)\n{\n    return ImGui::CalcTypematicRepeatAmount(t0,t1,repeat_delay,repeat_rate);\n}\nCIMGUI_API void igGetTypematicRepeatRate(ImGuiInputFlags flags,float* repeat_delay,float* repeat_rate)\n{\n    return ImGui::GetTypematicRepeatRate(flags,repeat_delay,repeat_rate);\n}\nCIMGUI_API void igTeleportMousePos(const ImVec2 pos)\n{\n    return ImGui::TeleportMousePos(pos);\n}\nCIMGUI_API void igSetActiveIdUsingAllKeyboardKeys()\n{\n    return ImGui::SetActiveIdUsingAllKeyboardKeys();\n}\nCIMGUI_API bool igIsActiveIdUsingNavDir(ImGuiDir dir)\n{\n    return ImGui::IsActiveIdUsingNavDir(dir);\n}\nCIMGUI_API ImGuiID igGetKeyOwner(ImGuiKey key)\n{\n    return ImGui::GetKeyOwner(key);\n}\nCIMGUI_API void igSetKeyOwner(ImGuiKey key,ImGuiID owner_id,ImGuiInputFlags flags)\n{\n    return ImGui::SetKeyOwner(key,owner_id,flags);\n}\nCIMGUI_API void igSetKeyOwnersForKeyChord(ImGuiKeyChord key,ImGuiID owner_id,ImGuiInputFlags flags)\n{\n    return ImGui::SetKeyOwnersForKeyChord(key,owner_id,flags);\n}\nCIMGUI_API void igSetItemKeyOwner_InputFlags(ImGuiKey key,ImGuiInputFlags flags)\n{\n    return ImGui::SetItemKeyOwner(key,flags);\n}\nCIMGUI_API bool igTestKeyOwner(ImGuiKey key,ImGuiID owner_id)\n{\n    return ImGui::TestKeyOwner(key,owner_id);\n}\nCIMGUI_API ImGuiKeyOwnerData* igGetKeyOwnerData(ImGuiContext* ctx,ImGuiKey key)\n{\n    return ImGui::GetKeyOwnerData(ctx,key);\n}\nCIMGUI_API bool igIsKeyDown_ID(ImGuiKey key,ImGuiID owner_id)\n{\n    return ImGui::IsKeyDown(key,owner_id);\n}\nCIMGUI_API bool igIsKeyPressed_InputFlags(ImGuiKey key,ImGuiInputFlags flags,ImGuiID owner_id)\n{\n    return ImGui::IsKeyPressed(key,flags,owner_id);\n}\nCIMGUI_API bool igIsKeyReleased_ID(ImGuiKey key,ImGuiID owner_id)\n{\n    return ImGui::IsKeyReleased(key,owner_id);\n}\nCIMGUI_API bool igIsKeyChordPressed_InputFlags(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id)\n{\n    return ImGui::IsKeyChordPressed(key_chord,flags,owner_id);\n}\nCIMGUI_API bool igIsMouseDown_ID(ImGuiMouseButton button,ImGuiID owner_id)\n{\n    return ImGui::IsMouseDown(button,owner_id);\n}\nCIMGUI_API bool igIsMouseClicked_InputFlags(ImGuiMouseButton button,ImGuiInputFlags flags,ImGuiID owner_id)\n{\n    return ImGui::IsMouseClicked(button,flags,owner_id);\n}\nCIMGUI_API bool igIsMouseReleased_ID(ImGuiMouseButton button,ImGuiID owner_id)\n{\n    return ImGui::IsMouseReleased(button,owner_id);\n}\nCIMGUI_API bool igIsMouseDoubleClicked_ID(ImGuiMouseButton button,ImGuiID owner_id)\n{\n    return ImGui::IsMouseDoubleClicked(button,owner_id);\n}\nCIMGUI_API bool igShortcut_ID(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id)\n{\n    return ImGui::Shortcut(key_chord,flags,owner_id);\n}\nCIMGUI_API bool igSetShortcutRouting(ImGuiKeyChord key_chord,ImGuiInputFlags flags,ImGuiID owner_id)\n{\n    return ImGui::SetShortcutRouting(key_chord,flags,owner_id);\n}\nCIMGUI_API bool igTestShortcutRouting(ImGuiKeyChord key_chord,ImGuiID owner_id)\n{\n    return ImGui::TestShortcutRouting(key_chord,owner_id);\n}\nCIMGUI_API ImGuiKeyRoutingData* igGetShortcutRoutingData(ImGuiKeyChord key_chord)\n{\n    return ImGui::GetShortcutRoutingData(key_chord);\n}\nCIMGUI_API void igDockContextInitialize(ImGuiContext* ctx)\n{\n    return ImGui::DockContextInitialize(ctx);\n}\nCIMGUI_API void igDockContextShutdown(ImGuiContext* ctx)\n{\n    return ImGui::DockContextShutdown(ctx);\n}\nCIMGUI_API void igDockContextClearNodes(ImGuiContext* ctx,ImGuiID root_id,bool clear_settings_refs)\n{\n    return ImGui::DockContextClearNodes(ctx,root_id,clear_settings_refs);\n}\nCIMGUI_API void igDockContextRebuildNodes(ImGuiContext* ctx)\n{\n    return ImGui::DockContextRebuildNodes(ctx);\n}\nCIMGUI_API void igDockContextNewFrameUpdateUndocking(ImGuiContext* ctx)\n{\n    return ImGui::DockContextNewFrameUpdateUndocking(ctx);\n}\nCIMGUI_API void igDockContextNewFrameUpdateDocking(ImGuiContext* ctx)\n{\n    return ImGui::DockContextNewFrameUpdateDocking(ctx);\n}\nCIMGUI_API void igDockContextEndFrame(ImGuiContext* ctx)\n{\n    return ImGui::DockContextEndFrame(ctx);\n}\nCIMGUI_API ImGuiID igDockContextGenNodeID(ImGuiContext* ctx)\n{\n    return ImGui::DockContextGenNodeID(ctx);\n}\nCIMGUI_API void igDockContextQueueDock(ImGuiContext* ctx,ImGuiWindow* target,ImGuiDockNode* target_node,ImGuiWindow* payload,ImGuiDir split_dir,float split_ratio,bool split_outer)\n{\n    return ImGui::DockContextQueueDock(ctx,target,target_node,payload,split_dir,split_ratio,split_outer);\n}\nCIMGUI_API void igDockContextQueueUndockWindow(ImGuiContext* ctx,ImGuiWindow* window)\n{\n    return ImGui::DockContextQueueUndockWindow(ctx,window);\n}\nCIMGUI_API void igDockContextQueueUndockNode(ImGuiContext* ctx,ImGuiDockNode* node)\n{\n    return ImGui::DockContextQueueUndockNode(ctx,node);\n}\nCIMGUI_API void igDockContextProcessUndockWindow(ImGuiContext* ctx,ImGuiWindow* window,bool clear_persistent_docking_ref)\n{\n    return ImGui::DockContextProcessUndockWindow(ctx,window,clear_persistent_docking_ref);\n}\nCIMGUI_API void igDockContextProcessUndockNode(ImGuiContext* ctx,ImGuiDockNode* node)\n{\n    return ImGui::DockContextProcessUndockNode(ctx,node);\n}\nCIMGUI_API bool igDockContextCalcDropPosForDocking(ImGuiWindow* target,ImGuiDockNode* target_node,ImGuiWindow* payload_window,ImGuiDockNode* payload_node,ImGuiDir split_dir,bool split_outer,ImVec2* out_pos)\n{\n    return ImGui::DockContextCalcDropPosForDocking(target,target_node,payload_window,payload_node,split_dir,split_outer,out_pos);\n}\nCIMGUI_API ImGuiDockNode* igDockContextFindNodeByID(ImGuiContext* ctx,ImGuiID id)\n{\n    return ImGui::DockContextFindNodeByID(ctx,id);\n}\nCIMGUI_API void igDockNodeWindowMenuHandler_Default(ImGuiContext* ctx,ImGuiDockNode* node,ImGuiTabBar* tab_bar)\n{\n    return ImGui::DockNodeWindowMenuHandler_Default(ctx,node,tab_bar);\n}\nCIMGUI_API bool igDockNodeBeginAmendTabBar(ImGuiDockNode* node)\n{\n    return ImGui::DockNodeBeginAmendTabBar(node);\n}\nCIMGUI_API void igDockNodeEndAmendTabBar()\n{\n    return ImGui::DockNodeEndAmendTabBar();\n}\nCIMGUI_API ImGuiDockNode* igDockNodeGetRootNode(ImGuiDockNode* node)\n{\n    return ImGui::DockNodeGetRootNode(node);\n}\nCIMGUI_API bool igDockNodeIsInHierarchyOf(ImGuiDockNode* node,ImGuiDockNode* parent)\n{\n    return ImGui::DockNodeIsInHierarchyOf(node,parent);\n}\nCIMGUI_API int igDockNodeGetDepth(const ImGuiDockNode* node)\n{\n    return ImGui::DockNodeGetDepth(node);\n}\nCIMGUI_API ImGuiID igDockNodeGetWindowMenuButtonId(const ImGuiDockNode* node)\n{\n    return ImGui::DockNodeGetWindowMenuButtonId(node);\n}\nCIMGUI_API ImGuiDockNode* igGetWindowDockNode()\n{\n    return ImGui::GetWindowDockNode();\n}\nCIMGUI_API bool igGetWindowAlwaysWantOwnTabBar(ImGuiWindow* window)\n{\n    return ImGui::GetWindowAlwaysWantOwnTabBar(window);\n}\nCIMGUI_API void igBeginDocked(ImGuiWindow* window,bool* p_open)\n{\n    return ImGui::BeginDocked(window,p_open);\n}\nCIMGUI_API void igBeginDockableDragDropSource(ImGuiWindow* window)\n{\n    return ImGui::BeginDockableDragDropSource(window);\n}\nCIMGUI_API void igBeginDockableDragDropTarget(ImGuiWindow* window)\n{\n    return ImGui::BeginDockableDragDropTarget(window);\n}\nCIMGUI_API void igSetWindowDock(ImGuiWindow* window,ImGuiID dock_id,ImGuiCond cond)\n{\n    return ImGui::SetWindowDock(window,dock_id,cond);\n}\nCIMGUI_API void igDockBuilderDockWindow(const char* window_name,ImGuiID node_id)\n{\n    return ImGui::DockBuilderDockWindow(window_name,node_id);\n}\nCIMGUI_API ImGuiDockNode* igDockBuilderGetNode(ImGuiID node_id)\n{\n    return ImGui::DockBuilderGetNode(node_id);\n}\nCIMGUI_API ImGuiDockNode* igDockBuilderGetCentralNode(ImGuiID node_id)\n{\n    return ImGui::DockBuilderGetCentralNode(node_id);\n}\nCIMGUI_API ImGuiID igDockBuilderAddNode(ImGuiID node_id,ImGuiDockNodeFlags flags)\n{\n    return ImGui::DockBuilderAddNode(node_id,flags);\n}\nCIMGUI_API void igDockBuilderRemoveNode(ImGuiID node_id)\n{\n    return ImGui::DockBuilderRemoveNode(node_id);\n}\nCIMGUI_API void igDockBuilderRemoveNodeDockedWindows(ImGuiID node_id,bool clear_settings_refs)\n{\n    return ImGui::DockBuilderRemoveNodeDockedWindows(node_id,clear_settings_refs);\n}\nCIMGUI_API void igDockBuilderRemoveNodeChildNodes(ImGuiID node_id)\n{\n    return ImGui::DockBuilderRemoveNodeChildNodes(node_id);\n}\nCIMGUI_API void igDockBuilderSetNodePos(ImGuiID node_id,ImVec2 pos)\n{\n    return ImGui::DockBuilderSetNodePos(node_id,pos);\n}\nCIMGUI_API void igDockBuilderSetNodeSize(ImGuiID node_id,ImVec2 size)\n{\n    return ImGui::DockBuilderSetNodeSize(node_id,size);\n}\nCIMGUI_API ImGuiID igDockBuilderSplitNode(ImGuiID node_id,ImGuiDir split_dir,float size_ratio_for_node_at_dir,ImGuiID* out_id_at_dir,ImGuiID* out_id_at_opposite_dir)\n{\n    return ImGui::DockBuilderSplitNode(node_id,split_dir,size_ratio_for_node_at_dir,out_id_at_dir,out_id_at_opposite_dir);\n}\nCIMGUI_API void igDockBuilderCopyDockSpace(ImGuiID src_dockspace_id,ImGuiID dst_dockspace_id,ImVector_const_charPtr* in_window_remap_pairs)\n{\n    return ImGui::DockBuilderCopyDockSpace(src_dockspace_id,dst_dockspace_id,in_window_remap_pairs);\n}\nCIMGUI_API void igDockBuilderCopyNode(ImGuiID src_node_id,ImGuiID dst_node_id,ImVector_ImGuiID* out_node_remap_pairs)\n{\n    return ImGui::DockBuilderCopyNode(src_node_id,dst_node_id,out_node_remap_pairs);\n}\nCIMGUI_API void igDockBuilderCopyWindowSettings(const char* src_name,const char* dst_name)\n{\n    return ImGui::DockBuilderCopyWindowSettings(src_name,dst_name);\n}\nCIMGUI_API void igDockBuilderFinish(ImGuiID node_id)\n{\n    return ImGui::DockBuilderFinish(node_id);\n}\nCIMGUI_API void igPushFocusScope(ImGuiID id)\n{\n    return ImGui::PushFocusScope(id);\n}\nCIMGUI_API void igPopFocusScope()\n{\n    return ImGui::PopFocusScope();\n}\nCIMGUI_API ImGuiID igGetCurrentFocusScope()\n{\n    return ImGui::GetCurrentFocusScope();\n}\nCIMGUI_API bool igIsDragDropActive()\n{\n    return ImGui::IsDragDropActive();\n}\nCIMGUI_API bool igBeginDragDropTargetCustom(const ImRect bb,ImGuiID id)\n{\n    return ImGui::BeginDragDropTargetCustom(bb,id);\n}\nCIMGUI_API void igClearDragDrop()\n{\n    return ImGui::ClearDragDrop();\n}\nCIMGUI_API bool igIsDragDropPayloadBeingAccepted()\n{\n    return ImGui::IsDragDropPayloadBeingAccepted();\n}\nCIMGUI_API void igRenderDragDropTargetRectEx(ImDrawList *draw_list, const ImRect bb)\n{\n    return ImGui::RenderDragDropTargetRectEx(draw_list, bb);\n}\nCIMGUI_API void igRenderDragDropTargetRectForItem(const ImRect bb)\n{\n    return ImGui::RenderDragDropTargetRectForItem(bb);\n}\nCIMGUI_API ImGuiTypingSelectRequest* igGetTypingSelectRequest(ImGuiTypingSelectFlags flags)\n{\n    return ImGui::GetTypingSelectRequest(flags);\n}\nCIMGUI_API int igTypingSelectFindMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data,int nav_item_idx)\n{\n    return ImGui::TypingSelectFindMatch(req,items_count,get_item_name_func,user_data,nav_item_idx);\n}\nCIMGUI_API int igTypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data,int nav_item_idx)\n{\n    return ImGui::TypingSelectFindNextSingleCharMatch(req,items_count,get_item_name_func,user_data,nav_item_idx);\n}\nCIMGUI_API int igTypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req,int items_count,const char*(*get_item_name_func)(void*,int),void* user_data)\n{\n    return ImGui::TypingSelectFindBestLeadingMatch(req,items_count,get_item_name_func,user_data);\n}\nCIMGUI_API bool igBeginBoxSelect(const ImRect scope_rect,ImGuiWindow* window,ImGuiID box_select_id,ImGuiMultiSelectFlags ms_flags)\n{\n    return ImGui::BeginBoxSelect(scope_rect,window,box_select_id,ms_flags);\n}\nCIMGUI_API void igEndBoxSelect(const ImRect scope_rect,ImGuiMultiSelectFlags ms_flags)\n{\n    return ImGui::EndBoxSelect(scope_rect,ms_flags);\n}\nCIMGUI_API void igMultiSelectItemHeader(ImGuiID id,bool* p_selected,ImGuiButtonFlags* p_button_flags)\n{\n    return ImGui::MultiSelectItemHeader(id,p_selected,p_button_flags);\n}\nCIMGUI_API void igMultiSelectItemFooter(ImGuiID id,bool* p_selected,bool* p_pressed)\n{\n    return ImGui::MultiSelectItemFooter(id,p_selected,p_pressed);\n}\nCIMGUI_API void igMultiSelectAddSetAll(ImGuiMultiSelectTempData* ms,bool selected)\n{\n    return ImGui::MultiSelectAddSetAll(ms,selected);\n}\nCIMGUI_API void igMultiSelectAddSetRange(ImGuiMultiSelectTempData* ms,bool selected,int range_dir,ImGuiSelectionUserData first_item,ImGuiSelectionUserData last_item)\n{\n    return ImGui::MultiSelectAddSetRange(ms,selected,range_dir,first_item,last_item);\n}\nCIMGUI_API ImGuiBoxSelectState* igGetBoxSelectState(ImGuiID id)\n{\n    return ImGui::GetBoxSelectState(id);\n}\nCIMGUI_API ImGuiMultiSelectState* igGetMultiSelectState(ImGuiID id)\n{\n    return ImGui::GetMultiSelectState(id);\n}\nCIMGUI_API void igSetWindowClipRectBeforeSetChannel(ImGuiWindow* window,const ImRect clip_rect)\n{\n    return ImGui::SetWindowClipRectBeforeSetChannel(window,clip_rect);\n}\nCIMGUI_API void igBeginColumns(const char* str_id,int count,ImGuiOldColumnFlags flags)\n{\n    return ImGui::BeginColumns(str_id,count,flags);\n}\nCIMGUI_API void igEndColumns()\n{\n    return ImGui::EndColumns();\n}\nCIMGUI_API void igPushColumnClipRect(int column_index)\n{\n    return ImGui::PushColumnClipRect(column_index);\n}\nCIMGUI_API void igPushColumnsBackground()\n{\n    return ImGui::PushColumnsBackground();\n}\nCIMGUI_API void igPopColumnsBackground()\n{\n    return ImGui::PopColumnsBackground();\n}\nCIMGUI_API ImGuiID igGetColumnsID(const char* str_id,int count)\n{\n    return ImGui::GetColumnsID(str_id,count);\n}\nCIMGUI_API ImGuiOldColumns* igFindOrCreateColumns(ImGuiWindow* window,ImGuiID id)\n{\n    return ImGui::FindOrCreateColumns(window,id);\n}\nCIMGUI_API float igGetColumnOffsetFromNorm(const ImGuiOldColumns* columns,float offset_norm)\n{\n    return ImGui::GetColumnOffsetFromNorm(columns,offset_norm);\n}\nCIMGUI_API float igGetColumnNormFromOffset(const ImGuiOldColumns* columns,float offset)\n{\n    return ImGui::GetColumnNormFromOffset(columns,offset);\n}\nCIMGUI_API void igTableOpenContextMenu(int column_n)\n{\n    return ImGui::TableOpenContextMenu(column_n);\n}\nCIMGUI_API void igTableSetColumnWidth(int column_n,float width)\n{\n    return ImGui::TableSetColumnWidth(column_n,width);\n}\nCIMGUI_API void igTableSetColumnSortDirection(int column_n,ImGuiSortDirection sort_direction,bool append_to_sort_specs)\n{\n    return ImGui::TableSetColumnSortDirection(column_n,sort_direction,append_to_sort_specs);\n}\nCIMGUI_API int igTableGetHoveredRow()\n{\n    return ImGui::TableGetHoveredRow();\n}\nCIMGUI_API float igTableGetHeaderRowHeight()\n{\n    return ImGui::TableGetHeaderRowHeight();\n}\nCIMGUI_API float igTableGetHeaderAngledMaxLabelWidth()\n{\n    return ImGui::TableGetHeaderAngledMaxLabelWidth();\n}\nCIMGUI_API void igTablePushBackgroundChannel()\n{\n    return ImGui::TablePushBackgroundChannel();\n}\nCIMGUI_API void igTablePopBackgroundChannel()\n{\n    return ImGui::TablePopBackgroundChannel();\n}\nCIMGUI_API void igTablePushColumnChannel(int column_n)\n{\n    return ImGui::TablePushColumnChannel(column_n);\n}\nCIMGUI_API void igTablePopColumnChannel()\n{\n    return ImGui::TablePopColumnChannel();\n}\nCIMGUI_API void igTableAngledHeadersRowEx(ImGuiID row_id,float angle,float max_label_width,const ImGuiTableHeaderData* data,int data_count)\n{\n    return ImGui::TableAngledHeadersRowEx(row_id,angle,max_label_width,data,data_count);\n}\nCIMGUI_API ImGuiTable* igGetCurrentTable()\n{\n    return ImGui::GetCurrentTable();\n}\nCIMGUI_API ImGuiTable* igTableFindByID(ImGuiID id)\n{\n    return ImGui::TableFindByID(id);\n}\nCIMGUI_API bool igBeginTableEx(const char* name,ImGuiID id,int columns_count,ImGuiTableFlags flags,const ImVec2 outer_size,float inner_width)\n{\n    return ImGui::BeginTableEx(name,id,columns_count,flags,outer_size,inner_width);\n}\nCIMGUI_API void igTableBeginInitMemory(ImGuiTable* table,int columns_count)\n{\n    return ImGui::TableBeginInitMemory(table,columns_count);\n}\nCIMGUI_API void igTableBeginApplyRequests(ImGuiTable* table)\n{\n    return ImGui::TableBeginApplyRequests(table);\n}\nCIMGUI_API void igTableSetupDrawChannels(ImGuiTable* table)\n{\n    return ImGui::TableSetupDrawChannels(table);\n}\nCIMGUI_API void igTableUpdateLayout(ImGuiTable* table)\n{\n    return ImGui::TableUpdateLayout(table);\n}\nCIMGUI_API void igTableUpdateBorders(ImGuiTable* table)\n{\n    return ImGui::TableUpdateBorders(table);\n}\nCIMGUI_API void igTableUpdateColumnsWeightFromWidth(ImGuiTable* table)\n{\n    return ImGui::TableUpdateColumnsWeightFromWidth(table);\n}\nCIMGUI_API void igTableDrawBorders(ImGuiTable* table)\n{\n    return ImGui::TableDrawBorders(table);\n}\nCIMGUI_API void igTableDrawDefaultContextMenu(ImGuiTable* table,ImGuiTableFlags flags_for_section_to_display)\n{\n    return ImGui::TableDrawDefaultContextMenu(table,flags_for_section_to_display);\n}\nCIMGUI_API bool igTableBeginContextMenuPopup(ImGuiTable* table)\n{\n    return ImGui::TableBeginContextMenuPopup(table);\n}\nCIMGUI_API void igTableMergeDrawChannels(ImGuiTable* table)\n{\n    return ImGui::TableMergeDrawChannels(table);\n}\nCIMGUI_API ImGuiTableInstanceData* igTableGetInstanceData(ImGuiTable* table,int instance_no)\n{\n    return ImGui::TableGetInstanceData(table,instance_no);\n}\nCIMGUI_API ImGuiID igTableGetInstanceID(ImGuiTable* table,int instance_no)\n{\n    return ImGui::TableGetInstanceID(table,instance_no);\n}\nCIMGUI_API void igTableSortSpecsSanitize(ImGuiTable* table)\n{\n    return ImGui::TableSortSpecsSanitize(table);\n}\nCIMGUI_API void igTableSortSpecsBuild(ImGuiTable* table)\n{\n    return ImGui::TableSortSpecsBuild(table);\n}\nCIMGUI_API ImGuiSortDirection igTableGetColumnNextSortDirection(ImGuiTableColumn* column)\n{\n    return ImGui::TableGetColumnNextSortDirection(column);\n}\nCIMGUI_API void igTableFixColumnSortDirection(ImGuiTable* table,ImGuiTableColumn* column)\n{\n    return ImGui::TableFixColumnSortDirection(table,column);\n}\nCIMGUI_API float igTableGetColumnWidthAuto(ImGuiTable* table,ImGuiTableColumn* column)\n{\n    return ImGui::TableGetColumnWidthAuto(table,column);\n}\nCIMGUI_API void igTableBeginRow(ImGuiTable* table)\n{\n    return ImGui::TableBeginRow(table);\n}\nCIMGUI_API void igTableEndRow(ImGuiTable* table)\n{\n    return ImGui::TableEndRow(table);\n}\nCIMGUI_API void igTableBeginCell(ImGuiTable* table,int column_n)\n{\n    return ImGui::TableBeginCell(table,column_n);\n}\nCIMGUI_API void igTableEndCell(ImGuiTable* table)\n{\n    return ImGui::TableEndCell(table);\n}\nCIMGUI_API void igTableGetCellBgRect(ImRect *pOut,const ImGuiTable* table,int column_n)\n{\n    *pOut = ImGui::TableGetCellBgRect(table,column_n);\n}\nCIMGUI_API const char* igTableGetColumnName_TablePtr(const ImGuiTable* table,int column_n)\n{\n    return ImGui::TableGetColumnName(table,column_n);\n}\nCIMGUI_API ImGuiID igTableGetColumnResizeID(ImGuiTable* table,int column_n,int instance_no)\n{\n    return ImGui::TableGetColumnResizeID(table,column_n,instance_no);\n}\nCIMGUI_API float igTableCalcMaxColumnWidth(const ImGuiTable* table,int column_n)\n{\n    return ImGui::TableCalcMaxColumnWidth(table,column_n);\n}\nCIMGUI_API void igTableSetColumnWidthAutoSingle(ImGuiTable* table,int column_n)\n{\n    return ImGui::TableSetColumnWidthAutoSingle(table,column_n);\n}\nCIMGUI_API void igTableSetColumnWidthAutoAll(ImGuiTable* table)\n{\n    return ImGui::TableSetColumnWidthAutoAll(table);\n}\nCIMGUI_API void igTableRemove(ImGuiTable* table)\n{\n    return ImGui::TableRemove(table);\n}\nCIMGUI_API void igTableGcCompactTransientBuffers_TablePtr(ImGuiTable* table)\n{\n    return ImGui::TableGcCompactTransientBuffers(table);\n}\nCIMGUI_API void igTableGcCompactTransientBuffers_TableTempDataPtr(ImGuiTableTempData* table)\n{\n    return ImGui::TableGcCompactTransientBuffers(table);\n}\nCIMGUI_API void igTableGcCompactSettings()\n{\n    return ImGui::TableGcCompactSettings();\n}\nCIMGUI_API void igTableLoadSettings(ImGuiTable* table)\n{\n    return ImGui::TableLoadSettings(table);\n}\nCIMGUI_API void igTableSaveSettings(ImGuiTable* table)\n{\n    return ImGui::TableSaveSettings(table);\n}\nCIMGUI_API void igTableResetSettings(ImGuiTable* table)\n{\n    return ImGui::TableResetSettings(table);\n}\nCIMGUI_API ImGuiTableSettings* igTableGetBoundSettings(ImGuiTable* table)\n{\n    return ImGui::TableGetBoundSettings(table);\n}\nCIMGUI_API void igTableSettingsAddSettingsHandler()\n{\n    return ImGui::TableSettingsAddSettingsHandler();\n}\nCIMGUI_API ImGuiTableSettings* igTableSettingsCreate(ImGuiID id,int columns_count)\n{\n    return ImGui::TableSettingsCreate(id,columns_count);\n}\nCIMGUI_API ImGuiTableSettings* igTableSettingsFindByID(ImGuiID id)\n{\n    return ImGui::TableSettingsFindByID(id);\n}\nCIMGUI_API ImGuiTabBar* igGetCurrentTabBar()\n{\n    return ImGui::GetCurrentTabBar();\n}\nCIMGUI_API bool igBeginTabBarEx(ImGuiTabBar* tab_bar,const ImRect bb,ImGuiTabBarFlags flags)\n{\n    return ImGui::BeginTabBarEx(tab_bar,bb,flags);\n}\nCIMGUI_API ImGuiTabItem* igTabBarFindTabByID(ImGuiTabBar* tab_bar,ImGuiID tab_id)\n{\n    return ImGui::TabBarFindTabByID(tab_bar,tab_id);\n}\nCIMGUI_API ImGuiTabItem* igTabBarFindTabByOrder(ImGuiTabBar* tab_bar,int order)\n{\n    return ImGui::TabBarFindTabByOrder(tab_bar,order);\n}\nCIMGUI_API ImGuiTabItem* igTabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar)\n{\n    return ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(tab_bar);\n}\nCIMGUI_API ImGuiTabItem* igTabBarGetCurrentTab(ImGuiTabBar* tab_bar)\n{\n    return ImGui::TabBarGetCurrentTab(tab_bar);\n}\nCIMGUI_API int igTabBarGetTabOrder(ImGuiTabBar* tab_bar,ImGuiTabItem* tab)\n{\n    return ImGui::TabBarGetTabOrder(tab_bar,tab);\n}\nCIMGUI_API const char* igTabBarGetTabName(ImGuiTabBar* tab_bar,ImGuiTabItem* tab)\n{\n    return ImGui::TabBarGetTabName(tab_bar,tab);\n}\nCIMGUI_API void igTabBarAddTab(ImGuiTabBar* tab_bar,ImGuiTabItemFlags tab_flags,ImGuiWindow* window)\n{\n    return ImGui::TabBarAddTab(tab_bar,tab_flags,window);\n}\nCIMGUI_API void igTabBarRemoveTab(ImGuiTabBar* tab_bar,ImGuiID tab_id)\n{\n    return ImGui::TabBarRemoveTab(tab_bar,tab_id);\n}\nCIMGUI_API void igTabBarCloseTab(ImGuiTabBar* tab_bar,ImGuiTabItem* tab)\n{\n    return ImGui::TabBarCloseTab(tab_bar,tab);\n}\nCIMGUI_API void igTabBarQueueFocus_TabItemPtr(ImGuiTabBar* tab_bar,ImGuiTabItem* tab)\n{\n    return ImGui::TabBarQueueFocus(tab_bar,tab);\n}\nCIMGUI_API void igTabBarQueueFocus_Str(ImGuiTabBar* tab_bar,const char* tab_name)\n{\n    return ImGui::TabBarQueueFocus(tab_bar,tab_name);\n}\nCIMGUI_API void igTabBarQueueReorder(ImGuiTabBar* tab_bar,ImGuiTabItem* tab,int offset)\n{\n    return ImGui::TabBarQueueReorder(tab_bar,tab,offset);\n}\nCIMGUI_API void igTabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar,ImGuiTabItem* tab,ImVec2 mouse_pos)\n{\n    return ImGui::TabBarQueueReorderFromMousePos(tab_bar,tab,mouse_pos);\n}\nCIMGUI_API bool igTabBarProcessReorder(ImGuiTabBar* tab_bar)\n{\n    return ImGui::TabBarProcessReorder(tab_bar);\n}\nCIMGUI_API bool igTabItemEx(ImGuiTabBar* tab_bar,const char* label,bool* p_open,ImGuiTabItemFlags flags,ImGuiWindow* docked_window)\n{\n    return ImGui::TabItemEx(tab_bar,label,p_open,flags,docked_window);\n}\nCIMGUI_API void igTabItemSpacing(const char* str_id,ImGuiTabItemFlags flags,float width)\n{\n    return ImGui::TabItemSpacing(str_id,flags,width);\n}\nCIMGUI_API void igTabItemCalcSize_Str(ImVec2 *pOut,const char* label,bool has_close_button_or_unsaved_marker)\n{\n    *pOut = ImGui::TabItemCalcSize(label,has_close_button_or_unsaved_marker);\n}\nCIMGUI_API void igTabItemCalcSize_WindowPtr(ImVec2 *pOut,ImGuiWindow* window)\n{\n    *pOut = ImGui::TabItemCalcSize(window);\n}\nCIMGUI_API void igTabItemBackground(ImDrawList* draw_list,const ImRect bb,ImGuiTabItemFlags flags,ImU32 col)\n{\n    return ImGui::TabItemBackground(draw_list,bb,flags,col);\n}\nCIMGUI_API void igTabItemLabelAndCloseButton(ImDrawList* draw_list,const ImRect bb,ImGuiTabItemFlags flags,ImVec2 frame_padding,const char* label,ImGuiID tab_id,ImGuiID close_button_id,bool is_contents_visible,bool* out_just_closed,bool* out_text_clipped)\n{\n    return ImGui::TabItemLabelAndCloseButton(draw_list,bb,flags,frame_padding,label,tab_id,close_button_id,is_contents_visible,out_just_closed,out_text_clipped);\n}\nCIMGUI_API void igRenderText(ImVec2 pos,const char* text,const char* text_end,bool hide_text_after_hash)\n{\n    return ImGui::RenderText(pos,text,text_end,hide_text_after_hash);\n}\nCIMGUI_API void igRenderTextWrapped(ImVec2 pos,const char* text,const char* text_end,float wrap_width)\n{\n    return ImGui::RenderTextWrapped(pos,text,text_end,wrap_width);\n}\nCIMGUI_API void igRenderTextClipped(const ImVec2 pos_min,const ImVec2 pos_max,const char* text,const char* text_end,const ImVec2* text_size_if_known,const ImVec2 align,const ImRect* clip_rect)\n{\n    return ImGui::RenderTextClipped(pos_min,pos_max,text,text_end,text_size_if_known,align,clip_rect);\n}\nCIMGUI_API void igRenderTextClippedEx(ImDrawList* draw_list,const ImVec2 pos_min,const ImVec2 pos_max,const char* text,const char* text_end,const ImVec2* text_size_if_known,const ImVec2 align,const ImRect* clip_rect)\n{\n    return ImGui::RenderTextClippedEx(draw_list,pos_min,pos_max,text,text_end,text_size_if_known,align,clip_rect);\n}\nCIMGUI_API void igRenderTextEllipsis(ImDrawList* draw_list,const ImVec2 pos_min,const ImVec2 pos_max,float ellipsis_max_x,const char* text,const char* text_end,const ImVec2* text_size_if_known)\n{\n    return ImGui::RenderTextEllipsis(draw_list,pos_min,pos_max,ellipsis_max_x,text,text_end,text_size_if_known);\n}\nCIMGUI_API void igRenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool borders,float rounding)\n{\n    return ImGui::RenderFrame(p_min,p_max,fill_col,borders,rounding);\n}\nCIMGUI_API void igRenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)\n{\n    return ImGui::RenderFrameBorder(p_min,p_max,rounding);\n}\nCIMGUI_API void igRenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list,ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,float grid_step,ImVec2 grid_off,float rounding,ImDrawFlags flags)\n{\n    return ImGui::RenderColorRectWithAlphaCheckerboard(draw_list,p_min,p_max,fill_col,grid_step,grid_off,rounding,flags);\n}\nCIMGUI_API void igRenderNavCursor(const ImRect bb,ImGuiID id,ImGuiNavRenderCursorFlags flags)\n{\n    return ImGui::RenderNavCursor(bb,id,flags);\n}\nCIMGUI_API const char* igFindRenderedTextEnd(const char* text,const char* text_end)\n{\n    return ImGui::FindRenderedTextEnd(text,text_end);\n}\nCIMGUI_API void igRenderMouseCursor(ImVec2 pos,float scale,ImGuiMouseCursor mouse_cursor,ImU32 col_fill,ImU32 col_border,ImU32 col_shadow)\n{\n    return ImGui::RenderMouseCursor(pos,scale,mouse_cursor,col_fill,col_border,col_shadow);\n}\nCIMGUI_API void igRenderArrow(ImDrawList* draw_list,ImVec2 pos,ImU32 col,ImGuiDir dir,float scale)\n{\n    return ImGui::RenderArrow(draw_list,pos,col,dir,scale);\n}\nCIMGUI_API void igRenderBullet(ImDrawList* draw_list,ImVec2 pos,ImU32 col)\n{\n    return ImGui::RenderBullet(draw_list,pos,col);\n}\nCIMGUI_API void igRenderCheckMark(ImDrawList* draw_list,ImVec2 pos,ImU32 col,float sz)\n{\n    return ImGui::RenderCheckMark(draw_list,pos,col,sz);\n}\nCIMGUI_API void igRenderArrowPointingAt(ImDrawList* draw_list,ImVec2 pos,ImVec2 half_sz,ImGuiDir direction,ImU32 col)\n{\n    return ImGui::RenderArrowPointingAt(draw_list,pos,half_sz,direction,col);\n}\nCIMGUI_API void igRenderArrowDockMenu(ImDrawList* draw_list,ImVec2 p_min,float sz,ImU32 col)\n{\n    return ImGui::RenderArrowDockMenu(draw_list,p_min,sz,col);\n}\nCIMGUI_API void igRenderRectFilledWithHole(ImDrawList* draw_list,const ImRect outer,const ImRect inner,ImU32 col,float rounding)\n{\n    return ImGui::RenderRectFilledWithHole(draw_list,outer,inner,col,rounding);\n}\nCIMGUI_API ImDrawFlags igCalcRoundingFlagsForRectInRect(const ImRect r_in,const ImRect r_outer,float threshold)\n{\n    return ImGui::CalcRoundingFlagsForRectInRect(r_in,r_outer,threshold);\n}\nCIMGUI_API void igTextEx(const char* text,const char* text_end,ImGuiTextFlags flags)\n{\n    return ImGui::TextEx(text,text_end,flags);\n}\nCIMGUI_API void igTextAligned(float align_x,float size_x,const char* fmt,...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImGui::TextAlignedV(align_x,size_x,fmt,args);\n    va_end(args);\n}\n#ifdef CIMGUI_VARGS0\nCIMGUI_API void igTextAligned0(float align_x,float size_x,const char* fmt)\n{\n    return igTextAligned(align_x,size_x,fmt);\n}\n#endif\nCIMGUI_API void igTextAlignedV(float align_x,float size_x,const char* fmt,va_list args)\n{\n    return ImGui::TextAlignedV(align_x,size_x,fmt,args);\n}\nCIMGUI_API bool igButtonEx(const char* label,const ImVec2 size_arg,ImGuiButtonFlags flags)\n{\n    return ImGui::ButtonEx(label,size_arg,flags);\n}\nCIMGUI_API bool igArrowButtonEx(const char* str_id,ImGuiDir dir,ImVec2 size_arg,ImGuiButtonFlags flags)\n{\n    return ImGui::ArrowButtonEx(str_id,dir,size_arg,flags);\n}\nCIMGUI_API bool igImageButtonEx(ImGuiID id,ImTextureRef tex_ref,const ImVec2 image_size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 bg_col,const ImVec4 tint_col,ImGuiButtonFlags flags)\n{\n    return ImGui::ImageButtonEx(id,tex_ref,image_size,uv0,uv1,bg_col,tint_col,flags);\n}\nCIMGUI_API void igSeparatorEx(ImGuiSeparatorFlags flags,float thickness)\n{\n    return ImGui::SeparatorEx(flags,thickness);\n}\nCIMGUI_API void igSeparatorTextEx(ImGuiID id,const char* label,const char* label_end,float extra_width)\n{\n    return ImGui::SeparatorTextEx(id,label,label_end,extra_width);\n}\nCIMGUI_API bool igCheckboxFlags_S64Ptr(const char* label,ImS64* flags,ImS64 flags_value)\n{\n    return ImGui::CheckboxFlags(label,flags,flags_value);\n}\nCIMGUI_API bool igCheckboxFlags_U64Ptr(const char* label,ImU64* flags,ImU64 flags_value)\n{\n    return ImGui::CheckboxFlags(label,flags,flags_value);\n}\nCIMGUI_API bool igCloseButton(ImGuiID id,const ImVec2 pos)\n{\n    return ImGui::CloseButton(id,pos);\n}\nCIMGUI_API bool igCollapseButton(ImGuiID id,const ImVec2 pos,ImGuiDockNode* dock_node)\n{\n    return ImGui::CollapseButton(id,pos,dock_node);\n}\nCIMGUI_API void igScrollbar(ImGuiAxis axis)\n{\n    return ImGui::Scrollbar(axis);\n}\nCIMGUI_API bool igScrollbarEx(const ImRect bb,ImGuiID id,ImGuiAxis axis,ImS64* p_scroll_v,ImS64 avail_v,ImS64 contents_v,ImDrawFlags draw_rounding_flags)\n{\n    return ImGui::ScrollbarEx(bb,id,axis,p_scroll_v,avail_v,contents_v,draw_rounding_flags);\n}\nCIMGUI_API void igGetWindowScrollbarRect(ImRect *pOut,ImGuiWindow* window,ImGuiAxis axis)\n{\n    *pOut = ImGui::GetWindowScrollbarRect(window,axis);\n}\nCIMGUI_API ImGuiID igGetWindowScrollbarID(ImGuiWindow* window,ImGuiAxis axis)\n{\n    return ImGui::GetWindowScrollbarID(window,axis);\n}\nCIMGUI_API ImGuiID igGetWindowResizeCornerID(ImGuiWindow* window,int n)\n{\n    return ImGui::GetWindowResizeCornerID(window,n);\n}\nCIMGUI_API ImGuiID igGetWindowResizeBorderID(ImGuiWindow* window,ImGuiDir dir)\n{\n    return ImGui::GetWindowResizeBorderID(window,dir);\n}\nCIMGUI_API bool igButtonBehavior(const ImRect bb,ImGuiID id,bool* out_hovered,bool* out_held,ImGuiButtonFlags flags)\n{\n    return ImGui::ButtonBehavior(bb,id,out_hovered,out_held,flags);\n}\nCIMGUI_API bool igDragBehavior(ImGuiID id,ImGuiDataType data_type,void* p_v,float v_speed,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags)\n{\n    return ImGui::DragBehavior(id,data_type,p_v,v_speed,p_min,p_max,format,flags);\n}\nCIMGUI_API bool igSliderBehavior(const ImRect bb,ImGuiID id,ImGuiDataType data_type,void* p_v,const void* p_min,const void* p_max,const char* format,ImGuiSliderFlags flags,ImRect* out_grab_bb)\n{\n    return ImGui::SliderBehavior(bb,id,data_type,p_v,p_min,p_max,format,flags,out_grab_bb);\n}\nCIMGUI_API bool igSplitterBehavior(const ImRect bb,ImGuiID id,ImGuiAxis axis,float* size1,float* size2,float min_size1,float min_size2,float hover_extend,float hover_visibility_delay,ImU32 bg_col)\n{\n    return ImGui::SplitterBehavior(bb,id,axis,size1,size2,min_size1,min_size2,hover_extend,hover_visibility_delay,bg_col);\n}\nCIMGUI_API bool igTreeNodeBehavior(ImGuiID id,ImGuiTreeNodeFlags flags,const char* label,const char* label_end)\n{\n    return ImGui::TreeNodeBehavior(id,flags,label,label_end);\n}\nCIMGUI_API void igTreeNodeDrawLineToChildNode(const ImVec2 target_pos)\n{\n    return ImGui::TreeNodeDrawLineToChildNode(target_pos);\n}\nCIMGUI_API void igTreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data)\n{\n    return ImGui::TreeNodeDrawLineToTreePop(data);\n}\nCIMGUI_API void igTreePushOverrideID(ImGuiID id)\n{\n    return ImGui::TreePushOverrideID(id);\n}\nCIMGUI_API bool igTreeNodeGetOpen(ImGuiID storage_id)\n{\n    return ImGui::TreeNodeGetOpen(storage_id);\n}\nCIMGUI_API void igTreeNodeSetOpen(ImGuiID storage_id,bool open)\n{\n    return ImGui::TreeNodeSetOpen(storage_id,open);\n}\nCIMGUI_API bool igTreeNodeUpdateNextOpen(ImGuiID storage_id,ImGuiTreeNodeFlags flags)\n{\n    return ImGui::TreeNodeUpdateNextOpen(storage_id,flags);\n}\nCIMGUI_API const ImGuiDataTypeInfo* igDataTypeGetInfo(ImGuiDataType data_type)\n{\n    return ImGui::DataTypeGetInfo(data_type);\n}\nCIMGUI_API int igDataTypeFormatString(char* buf,int buf_size,ImGuiDataType data_type,const void* p_data,const char* format)\n{\n    return ImGui::DataTypeFormatString(buf,buf_size,data_type,p_data,format);\n}\nCIMGUI_API void igDataTypeApplyOp(ImGuiDataType data_type,int op,void* output,const void* arg_1,const void* arg_2)\n{\n    return ImGui::DataTypeApplyOp(data_type,op,output,arg_1,arg_2);\n}\nCIMGUI_API bool igDataTypeApplyFromText(const char* buf,ImGuiDataType data_type,void* p_data,const char* format,void* p_data_when_empty)\n{\n    return ImGui::DataTypeApplyFromText(buf,data_type,p_data,format,p_data_when_empty);\n}\nCIMGUI_API int igDataTypeCompare(ImGuiDataType data_type,const void* arg_1,const void* arg_2)\n{\n    return ImGui::DataTypeCompare(data_type,arg_1,arg_2);\n}\nCIMGUI_API bool igDataTypeClamp(ImGuiDataType data_type,void* p_data,const void* p_min,const void* p_max)\n{\n    return ImGui::DataTypeClamp(data_type,p_data,p_min,p_max);\n}\nCIMGUI_API bool igDataTypeIsZero(ImGuiDataType data_type,const void* p_data)\n{\n    return ImGui::DataTypeIsZero(data_type,p_data);\n}\nCIMGUI_API bool igInputTextEx(const char* label,const char* hint,char* buf,int buf_size,const ImVec2 size_arg,ImGuiInputTextFlags flags,ImGuiInputTextCallback callback,void* user_data)\n{\n    return ImGui::InputTextEx(label,hint,buf,buf_size,size_arg,flags,callback,user_data);\n}\nCIMGUI_API void igInputTextDeactivateHook(ImGuiID id)\n{\n    return ImGui::InputTextDeactivateHook(id);\n}\nCIMGUI_API bool igTempInputText(const ImRect bb,ImGuiID id,const char* label,char* buf,int buf_size,ImGuiInputTextFlags flags)\n{\n    return ImGui::TempInputText(bb,id,label,buf,buf_size,flags);\n}\nCIMGUI_API bool igTempInputScalar(const ImRect bb,ImGuiID id,const char* label,ImGuiDataType data_type,void* p_data,const char* format,const void* p_clamp_min,const void* p_clamp_max)\n{\n    return ImGui::TempInputScalar(bb,id,label,data_type,p_data,format,p_clamp_min,p_clamp_max);\n}\nCIMGUI_API bool igTempInputIsActive(ImGuiID id)\n{\n    return ImGui::TempInputIsActive(id);\n}\nCIMGUI_API ImGuiInputTextState* igGetInputTextState(ImGuiID id)\n{\n    return ImGui::GetInputTextState(id);\n}\nCIMGUI_API void igSetNextItemRefVal(ImGuiDataType data_type,void* p_data)\n{\n    return ImGui::SetNextItemRefVal(data_type,p_data);\n}\nCIMGUI_API bool igIsItemActiveAsInputText()\n{\n    return ImGui::IsItemActiveAsInputText();\n}\nCIMGUI_API void igColorTooltip(const char* text,const float* col,ImGuiColorEditFlags flags)\n{\n    return ImGui::ColorTooltip(text,col,flags);\n}\nCIMGUI_API void igColorEditOptionsPopup(const float* col,ImGuiColorEditFlags flags)\n{\n    return ImGui::ColorEditOptionsPopup(col,flags);\n}\nCIMGUI_API void igColorPickerOptionsPopup(const float* ref_col,ImGuiColorEditFlags flags)\n{\n    return ImGui::ColorPickerOptionsPopup(ref_col,flags);\n}\nCIMGUI_API int igPlotEx(ImGuiPlotType plot_type,const char* label,float(*values_getter)(void* data,int idx),void* data,int values_count,int values_offset,const char* overlay_text,float scale_min,float scale_max,const ImVec2 size_arg)\n{\n    return ImGui::PlotEx(plot_type,label,values_getter,data,values_count,values_offset,overlay_text,scale_min,scale_max,size_arg);\n}\nCIMGUI_API void igShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,ImVec2 gradient_p0,ImVec2 gradient_p1,ImU32 col0,ImU32 col1)\n{\n    return ImGui::ShadeVertsLinearColorGradientKeepAlpha(draw_list,vert_start_idx,vert_end_idx,gradient_p0,gradient_p1,col0,col1);\n}\nCIMGUI_API void igShadeVertsLinearUV(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,const ImVec2 a,const ImVec2 b,const ImVec2 uv_a,const ImVec2 uv_b,bool clamp)\n{\n    return ImGui::ShadeVertsLinearUV(draw_list,vert_start_idx,vert_end_idx,a,b,uv_a,uv_b,clamp);\n}\nCIMGUI_API void igShadeVertsTransformPos(ImDrawList* draw_list,int vert_start_idx,int vert_end_idx,const ImVec2 pivot_in,float cos_a,float sin_a,const ImVec2 pivot_out)\n{\n    return ImGui::ShadeVertsTransformPos(draw_list,vert_start_idx,vert_end_idx,pivot_in,cos_a,sin_a,pivot_out);\n}\nCIMGUI_API void igGcCompactTransientMiscBuffers()\n{\n    return ImGui::GcCompactTransientMiscBuffers();\n}\nCIMGUI_API void igGcCompactTransientWindowBuffers(ImGuiWindow* window)\n{\n    return ImGui::GcCompactTransientWindowBuffers(window);\n}\nCIMGUI_API void igGcAwakeTransientWindowBuffers(ImGuiWindow* window)\n{\n    return ImGui::GcAwakeTransientWindowBuffers(window);\n}\nCIMGUI_API bool igErrorLog(const char* msg)\n{\n    return ImGui::ErrorLog(msg);\n}\nCIMGUI_API void igErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out)\n{\n    return ImGui::ErrorRecoveryStoreState(state_out);\n}\nCIMGUI_API void igErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in)\n{\n    return ImGui::ErrorRecoveryTryToRecoverState(state_in);\n}\nCIMGUI_API void igErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in)\n{\n    return ImGui::ErrorRecoveryTryToRecoverWindowState(state_in);\n}\nCIMGUI_API void igErrorCheckUsingSetCursorPosToExtendParentBoundaries()\n{\n    return ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries();\n}\nCIMGUI_API void igErrorCheckEndFrameFinalizeErrorTooltip()\n{\n    return ImGui::ErrorCheckEndFrameFinalizeErrorTooltip();\n}\nCIMGUI_API bool igBeginErrorTooltip()\n{\n    return ImGui::BeginErrorTooltip();\n}\nCIMGUI_API void igEndErrorTooltip()\n{\n    return ImGui::EndErrorTooltip();\n}\nCIMGUI_API void igDebugAllocHook(ImGuiDebugAllocInfo* info,int frame_count,void* ptr,size_t size)\n{\n    return ImGui::DebugAllocHook(info,frame_count,ptr,size);\n}\nCIMGUI_API void igDebugDrawCursorPos(ImU32 col)\n{\n    return ImGui::DebugDrawCursorPos(col);\n}\nCIMGUI_API void igDebugDrawLineExtents(ImU32 col)\n{\n    return ImGui::DebugDrawLineExtents(col);\n}\nCIMGUI_API void igDebugDrawItemRect(ImU32 col)\n{\n    return ImGui::DebugDrawItemRect(col);\n}\nCIMGUI_API void igDebugTextUnformattedWithLocateItem(const char* line_begin,const char* line_end)\n{\n    return ImGui::DebugTextUnformattedWithLocateItem(line_begin,line_end);\n}\nCIMGUI_API void igDebugLocateItem(ImGuiID target_id)\n{\n    return ImGui::DebugLocateItem(target_id);\n}\nCIMGUI_API void igDebugLocateItemOnHover(ImGuiID target_id)\n{\n    return ImGui::DebugLocateItemOnHover(target_id);\n}\nCIMGUI_API void igDebugLocateItemResolveWithLastItem()\n{\n    return ImGui::DebugLocateItemResolveWithLastItem();\n}\nCIMGUI_API void igDebugBreakClearData()\n{\n    return ImGui::DebugBreakClearData();\n}\nCIMGUI_API bool igDebugBreakButton(const char* label,const char* description_of_location)\n{\n    return ImGui::DebugBreakButton(label,description_of_location);\n}\nCIMGUI_API void igDebugBreakButtonTooltip(bool keyboard_only,const char* description_of_location)\n{\n    return ImGui::DebugBreakButtonTooltip(keyboard_only,description_of_location);\n}\nCIMGUI_API void igShowFontAtlas(ImFontAtlas* atlas)\n{\n    return ImGui::ShowFontAtlas(atlas);\n}\nCIMGUI_API void igDebugHookIdInfo(ImGuiID id,ImGuiDataType data_type,const void* data_id,const void* data_id_end)\n{\n    return ImGui::DebugHookIdInfo(id,data_type,data_id,data_id_end);\n}\nCIMGUI_API void igDebugNodeColumns(ImGuiOldColumns* columns)\n{\n    return ImGui::DebugNodeColumns(columns);\n}\nCIMGUI_API void igDebugNodeDockNode(ImGuiDockNode* node,const char* label)\n{\n    return ImGui::DebugNodeDockNode(node,label);\n}\nCIMGUI_API void igDebugNodeDrawList(ImGuiWindow* window,ImGuiViewportP* viewport,const ImDrawList* draw_list,const char* label)\n{\n    return ImGui::DebugNodeDrawList(window,viewport,draw_list,label);\n}\nCIMGUI_API void igDebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list,const ImDrawList* draw_list,const ImDrawCmd* draw_cmd,bool show_mesh,bool show_aabb)\n{\n    return ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(out_draw_list,draw_list,draw_cmd,show_mesh,show_aabb);\n}\nCIMGUI_API void igDebugNodeFont(ImFont* font)\n{\n    return ImGui::DebugNodeFont(font);\n}\nCIMGUI_API void igDebugNodeFontGlyph(ImFont* font,const ImFontGlyph* glyph)\n{\n    return ImGui::DebugNodeFontGlyph(font,glyph);\n}\nCIMGUI_API void igDebugNodeTexture(ImTextureData* tex,int int_id,const ImFontAtlasRect* highlight_rect)\n{\n    return ImGui::DebugNodeTexture(tex,int_id,highlight_rect);\n}\nCIMGUI_API void igDebugNodeStorage(ImGuiStorage* storage,const char* label)\n{\n    return ImGui::DebugNodeStorage(storage,label);\n}\nCIMGUI_API void igDebugNodeTabBar(ImGuiTabBar* tab_bar,const char* label)\n{\n    return ImGui::DebugNodeTabBar(tab_bar,label);\n}\nCIMGUI_API void igDebugNodeTable(ImGuiTable* table)\n{\n    return ImGui::DebugNodeTable(table);\n}\nCIMGUI_API void igDebugNodeTableSettings(ImGuiTableSettings* settings)\n{\n    return ImGui::DebugNodeTableSettings(settings);\n}\nCIMGUI_API void igDebugNodeInputTextState(ImGuiInputTextState* state)\n{\n    return ImGui::DebugNodeInputTextState(state);\n}\nCIMGUI_API void igDebugNodeTypingSelectState(ImGuiTypingSelectState* state)\n{\n    return ImGui::DebugNodeTypingSelectState(state);\n}\nCIMGUI_API void igDebugNodeMultiSelectState(ImGuiMultiSelectState* state)\n{\n    return ImGui::DebugNodeMultiSelectState(state);\n}\nCIMGUI_API void igDebugNodeWindow(ImGuiWindow* window,const char* label)\n{\n    return ImGui::DebugNodeWindow(window,label);\n}\nCIMGUI_API void igDebugNodeWindowSettings(ImGuiWindowSettings* settings)\n{\n    return ImGui::DebugNodeWindowSettings(settings);\n}\nCIMGUI_API void igDebugNodeWindowsList(ImVector_ImGuiWindowPtr* windows,const char* label)\n{\n    return ImGui::DebugNodeWindowsList(windows,label);\n}\nCIMGUI_API void igDebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows,int windows_size,ImGuiWindow* parent_in_begin_stack)\n{\n    return ImGui::DebugNodeWindowsListByBeginStackParent(windows,windows_size,parent_in_begin_stack);\n}\nCIMGUI_API void igDebugNodeViewport(ImGuiViewportP* viewport)\n{\n    return ImGui::DebugNodeViewport(viewport);\n}\nCIMGUI_API void igDebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor,const char* label,int idx)\n{\n    return ImGui::DebugNodePlatformMonitor(monitor,label,idx);\n}\nCIMGUI_API void igDebugRenderKeyboardPreview(ImDrawList* draw_list)\n{\n    return ImGui::DebugRenderKeyboardPreview(draw_list);\n}\nCIMGUI_API void igDebugRenderViewportThumbnail(ImDrawList* draw_list,ImGuiViewportP* viewport,const ImRect bb)\n{\n    return ImGui::DebugRenderViewportThumbnail(draw_list,viewport,bb);\n}\nCIMGUI_API ImFontLoader* ImFontLoader_ImFontLoader(void)\n{\n    return IM_NEW(ImFontLoader)();\n}\nCIMGUI_API void ImFontLoader_destroy(ImFontLoader* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API int igImFontAtlasRectId_GetIndex(ImFontAtlasRectId id)\n{\n    return ImFontAtlasRectId_GetIndex(id);\n}\nCIMGUI_API int igImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id)\n{\n    return ImFontAtlasRectId_GetGeneration(id);\n}\nCIMGUI_API ImFontAtlasRectId igImFontAtlasRectId_Make(int index_idx,int gen_idx)\n{\n    return ImFontAtlasRectId_Make(index_idx,gen_idx);\n}\nCIMGUI_API ImFontAtlasBuilder* ImFontAtlasBuilder_ImFontAtlasBuilder(void)\n{\n    return IM_NEW(ImFontAtlasBuilder)();\n}\nCIMGUI_API void ImFontAtlasBuilder_destroy(ImFontAtlasBuilder* self)\n{\n    IM_DELETE(self);\n}\nCIMGUI_API void igImFontAtlasBuildInit(ImFontAtlas* atlas)\n{\n    return ImFontAtlasBuildInit(atlas);\n}\nCIMGUI_API void igImFontAtlasBuildDestroy(ImFontAtlas* atlas)\n{\n    return ImFontAtlasBuildDestroy(atlas);\n}\nCIMGUI_API void igImFontAtlasBuildMain(ImFontAtlas* atlas)\n{\n    return ImFontAtlasBuildMain(atlas);\n}\nCIMGUI_API void igImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas,const ImFontLoader* font_loader)\n{\n    return ImFontAtlasBuildSetupFontLoader(atlas,font_loader);\n}\nCIMGUI_API void igImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas)\n{\n    return ImFontAtlasBuildUpdatePointers(atlas);\n}\nCIMGUI_API void igImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas,int x,int y,int w,int h,const char* in_str,char in_marker_char)\n{\n    return ImFontAtlasBuildRenderBitmapFromString(atlas,x,y,w,h,in_str,in_marker_char);\n}\nCIMGUI_API void igImFontAtlasBuildClear(ImFontAtlas* atlas)\n{\n    return ImFontAtlasBuildClear(atlas);\n}\nCIMGUI_API ImTextureData* igImFontAtlasTextureAdd(ImFontAtlas* atlas,int w,int h)\n{\n    return ImFontAtlasTextureAdd(atlas,w,h);\n}\nCIMGUI_API void igImFontAtlasTextureMakeSpace(ImFontAtlas* atlas)\n{\n    return ImFontAtlasTextureMakeSpace(atlas);\n}\nCIMGUI_API void igImFontAtlasTextureRepack(ImFontAtlas* atlas,int w,int h)\n{\n    return ImFontAtlasTextureRepack(atlas,w,h);\n}\nCIMGUI_API void igImFontAtlasTextureGrow(ImFontAtlas* atlas,int old_w,int old_h)\n{\n    return ImFontAtlasTextureGrow(atlas,old_w,old_h);\n}\nCIMGUI_API void igImFontAtlasTextureCompact(ImFontAtlas* atlas)\n{\n    return ImFontAtlasTextureCompact(atlas);\n}\nCIMGUI_API void igImFontAtlasTextureGetSizeEstimate(ImVec2i *pOut,ImFontAtlas* atlas)\n{\n    *pOut = ImFontAtlasTextureGetSizeEstimate(atlas);\n}\nCIMGUI_API void igImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas,ImFont* font,ImFontConfig* src)\n{\n    return ImFontAtlasBuildSetupFontSpecialGlyphs(atlas,font,src);\n}\nCIMGUI_API void igImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas)\n{\n    return ImFontAtlasBuildLegacyPreloadAllGlyphRanges(atlas);\n}\nCIMGUI_API void igImFontAtlasBuildGetOversampleFactors(ImFontConfig* src,ImFontBaked* baked,int* out_oversample_h,int* out_oversample_v)\n{\n    return ImFontAtlasBuildGetOversampleFactors(src,baked,out_oversample_h,out_oversample_v);\n}\nCIMGUI_API void igImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas,int unused_frames)\n{\n    return ImFontAtlasBuildDiscardBakes(atlas,unused_frames);\n}\nCIMGUI_API bool igImFontAtlasFontSourceInit(ImFontAtlas* atlas,ImFontConfig* src)\n{\n    return ImFontAtlasFontSourceInit(atlas,src);\n}\nCIMGUI_API void igImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas,ImFont* font,ImFontConfig* src)\n{\n    return ImFontAtlasFontSourceAddToFont(atlas,font,src);\n}\nCIMGUI_API void igImFontAtlasFontDestroySourceData(ImFontAtlas* atlas,ImFontConfig* src)\n{\n    return ImFontAtlasFontDestroySourceData(atlas,src);\n}\nCIMGUI_API bool igImFontAtlasFontInitOutput(ImFontAtlas* atlas,ImFont* font)\n{\n    return ImFontAtlasFontInitOutput(atlas,font);\n}\nCIMGUI_API void igImFontAtlasFontDestroyOutput(ImFontAtlas* atlas,ImFont* font)\n{\n    return ImFontAtlasFontDestroyOutput(atlas,font);\n}\nCIMGUI_API void igImFontAtlasFontDiscardBakes(ImFontAtlas* atlas,ImFont* font,int unused_frames)\n{\n    return ImFontAtlasFontDiscardBakes(atlas,font,unused_frames);\n}\nCIMGUI_API ImGuiID igImFontAtlasBakedGetId(ImGuiID font_id,float baked_size,float rasterizer_density)\n{\n    return ImFontAtlasBakedGetId(font_id,baked_size,rasterizer_density);\n}\nCIMGUI_API ImFontBaked* igImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density)\n{\n    return ImFontAtlasBakedGetOrAdd(atlas,font,font_size,font_rasterizer_density);\n}\nCIMGUI_API ImFontBaked* igImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density)\n{\n    return ImFontAtlasBakedGetClosestMatch(atlas,font,font_size,font_rasterizer_density);\n}\nCIMGUI_API ImFontBaked* igImFontAtlasBakedAdd(ImFontAtlas* atlas,ImFont* font,float font_size,float font_rasterizer_density,ImGuiID baked_id)\n{\n    return ImFontAtlasBakedAdd(atlas,font,font_size,font_rasterizer_density,baked_id);\n}\nCIMGUI_API void igImFontAtlasBakedDiscard(ImFontAtlas* atlas,ImFont* font,ImFontBaked* baked)\n{\n    return ImFontAtlasBakedDiscard(atlas,font,baked);\n}\nCIMGUI_API ImFontGlyph* igImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas,ImFontBaked* baked,ImFontConfig* src,const ImFontGlyph* in_glyph)\n{\n    return ImFontAtlasBakedAddFontGlyph(atlas,baked,src,in_glyph);\n}\nCIMGUI_API void igImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas,ImFont* font,ImFontBaked* baked,ImFontGlyph* glyph)\n{\n    return ImFontAtlasBakedDiscardFontGlyph(atlas,font,baked,glyph);\n}\nCIMGUI_API void igImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas,ImFontBaked* baked,ImFontConfig* src,ImFontGlyph* glyph,ImTextureRect* r,const unsigned char* src_pixels,ImTextureFormat src_fmt,int src_pitch)\n{\n    return ImFontAtlasBakedSetFontGlyphBitmap(atlas,baked,src,glyph,r,src_pixels,src_fmt,src_pitch);\n}\nCIMGUI_API void igImFontAtlasPackInit(ImFontAtlas* atlas)\n{\n    return ImFontAtlasPackInit(atlas);\n}\nCIMGUI_API ImFontAtlasRectId igImFontAtlasPackAddRect(ImFontAtlas* atlas,int w,int h,ImFontAtlasRectEntry* overwrite_entry)\n{\n    return ImFontAtlasPackAddRect(atlas,w,h,overwrite_entry);\n}\nCIMGUI_API ImTextureRect* igImFontAtlasPackGetRect(ImFontAtlas* atlas,ImFontAtlasRectId id)\n{\n    return ImFontAtlasPackGetRect(atlas,id);\n}\nCIMGUI_API ImTextureRect* igImFontAtlasPackGetRectSafe(ImFontAtlas* atlas,ImFontAtlasRectId id)\n{\n    return ImFontAtlasPackGetRectSafe(atlas,id);\n}\nCIMGUI_API void igImFontAtlasPackDiscardRect(ImFontAtlas* atlas,ImFontAtlasRectId id)\n{\n    return ImFontAtlasPackDiscardRect(atlas,id);\n}\nCIMGUI_API void igImFontAtlasUpdateNewFrame(ImFontAtlas* atlas,int frame_count,bool renderer_has_textures)\n{\n    return ImFontAtlasUpdateNewFrame(atlas,frame_count,renderer_has_textures);\n}\nCIMGUI_API void igImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas,ImDrawListSharedData* data)\n{\n    return ImFontAtlasAddDrawListSharedData(atlas,data);\n}\nCIMGUI_API void igImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas,ImDrawListSharedData* data)\n{\n    return ImFontAtlasRemoveDrawListSharedData(atlas,data);\n}\nCIMGUI_API void igImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas,ImTextureRef old_tex,ImTextureRef new_tex)\n{\n    return ImFontAtlasUpdateDrawListsTextures(atlas,old_tex,new_tex);\n}\nCIMGUI_API void igImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas)\n{\n    return ImFontAtlasUpdateDrawListsSharedData(atlas);\n}\nCIMGUI_API void igImFontAtlasTextureBlockConvert(const unsigned char* src_pixels,ImTextureFormat src_fmt,int src_pitch,unsigned char* dst_pixels,ImTextureFormat dst_fmt,int dst_pitch,int w,int h)\n{\n    return ImFontAtlasTextureBlockConvert(src_pixels,src_fmt,src_pitch,dst_pixels,dst_fmt,dst_pitch,w,h);\n}\nCIMGUI_API void igImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data)\n{\n    return ImFontAtlasTextureBlockPostProcess(data);\n}\nCIMGUI_API void igImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data,float multiply_factor)\n{\n    return ImFontAtlasTextureBlockPostProcessMultiply(data,multiply_factor);\n}\nCIMGUI_API void igImFontAtlasTextureBlockFill(ImTextureData* dst_tex,int dst_x,int dst_y,int w,int h,ImU32 col)\n{\n    return ImFontAtlasTextureBlockFill(dst_tex,dst_x,dst_y,w,h,col);\n}\nCIMGUI_API void igImFontAtlasTextureBlockCopy(ImTextureData* src_tex,int src_x,int src_y,ImTextureData* dst_tex,int dst_x,int dst_y,int w,int h)\n{\n    return ImFontAtlasTextureBlockCopy(src_tex,src_x,src_y,dst_tex,dst_x,dst_y,w,h);\n}\nCIMGUI_API void igImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas,ImTextureData* tex,int x,int y,int w,int h)\n{\n    return ImFontAtlasTextureBlockQueueUpload(atlas,tex,x,y,w,h);\n}\nCIMGUI_API int igImTextureDataGetFormatBytesPerPixel(ImTextureFormat format)\n{\n    return ImTextureDataGetFormatBytesPerPixel(format);\n}\nCIMGUI_API const char* igImTextureDataGetStatusName(ImTextureStatus status)\n{\n    return ImTextureDataGetStatusName(status);\n}\nCIMGUI_API const char* igImTextureDataGetFormatName(ImTextureFormat format)\n{\n    return ImTextureDataGetFormatName(format);\n}\nCIMGUI_API void igImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas)\n{\n    return ImFontAtlasDebugLogTextureRequests(atlas);\n}\nCIMGUI_API bool igImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas,ImGuiMouseCursor cursor_type,ImVec2* out_offset,ImVec2* out_size,ImVec2 out_uv_border[2],ImVec2 out_uv_fill[2])\n{\n    return ImFontAtlasGetMouseCursorTexData(atlas,cursor_type,out_offset,out_size,out_uv_border,out_uv_fill);\n}\n#ifdef IMGUI_ENABLE_FREETYPE\nCIMGUI_API const ImFontLoader* ImGuiFreeType_GetFontLoader()\n{\n    return ImGuiFreeType::GetFontLoader();\n}\n\nCIMGUI_API void ImGuiFreeType_SetAllocatorFunctions(void*(*alloc_func)(size_t sz,void* user_data),void(*free_func)(void* ptr,void* user_data),void* user_data)\n{\n    return ImGuiFreeType::SetAllocatorFunctions(alloc_func,free_func,user_data);\n}\n\nCIMGUI_API bool ImGuiFreeType_DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags)\n{\n    return ImGuiFreeType::DebugEditFontLoaderFlags(p_font_loader_flags);\n}\n\n#endif\n\n\n\n/////////////////////////////manual written functions\n\nCIMGUI_API void ImGuiTextBuffer_appendf(ImGuiTextBuffer *self, const char *fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    self->appendfv(fmt, args);\n    va_end(args);\n}\n\nCIMGUI_API float igGET_FLT_MAX()\n{\n    return FLT_MAX;\n}\n\nCIMGUI_API float igGET_FLT_MIN()\n{\n    return FLT_MIN;\n}\n\n\nCIMGUI_API ImVector_ImWchar* ImVector_ImWchar_create()\n{\n\treturn IM_NEW(ImVector<ImWchar>) ();\n}\n\nCIMGUI_API void ImVector_ImWchar_destroy(ImVector_ImWchar* self)\n{\n    IM_DELETE(self);\n}\n\nCIMGUI_API void ImVector_ImWchar_Init(ImVector_ImWchar* p)\n{\n\tIM_PLACEMENT_NEW(p) ImVector<ImWchar>();\n}\nCIMGUI_API void ImVector_ImWchar_UnInit(ImVector_ImWchar* p)\n{\n\tp->~ImVector<ImWchar>();\n}\n\n\n#ifdef IMGUI_HAS_DOCK\n\n// NOTE: Some function pointers in the ImGuiPlatformIO structure are not C-compatible because of their\n// use of a complex return type. To work around this, we store a custom CimguiStorage object inside\n// ImGuiIO::BackendLanguageUserData, which contains C-compatible function pointer variants for these\n// functions. When a user function pointer is provided, we hook up the underlying ImGuiPlatformIO\n// function pointer to a thunk which accesses the user function pointer through CimguiStorage.\n\nstruct CimguiStorage\n{\n    void(*Platform_GetWindowPos)(ImGuiViewport* vp, ImVec2* out_pos);\n    void(*Platform_GetWindowSize)(ImGuiViewport* vp, ImVec2* out_pos);\n};\n\n// Gets a reference to the CimguiStorage object stored in the current ImGui context's BackendLanguageUserData.\nCimguiStorage& GetCimguiStorage()\n{\n    ImGuiIO& io = ImGui::GetIO();\n    if (io.BackendLanguageUserData == NULL)\n    {\n        io.BackendLanguageUserData = IM_NEW(CimguiStorage)();\n    }\n\n    return *(CimguiStorage*)io.BackendLanguageUserData;\n}\n\n// Thunk satisfying the signature of ImGuiPlatformIO::Platform_GetWindowPos.\nImVec2 Platform_GetWindowPos_hook(ImGuiViewport* vp)\n{\n    ImVec2 pos;\n    GetCimguiStorage().Platform_GetWindowPos(vp, &pos);\n    return pos;\n};\n\n// Fully C-compatible function pointer setter for ImGuiPlatformIO::Platform_GetWindowPos.\nCIMGUI_API void ImGuiPlatformIO_Set_Platform_GetWindowPos(ImGuiPlatformIO* platform_io, void(*user_callback)(ImGuiViewport* vp, ImVec2* out_pos))\n{\n    CimguiStorage& storage = GetCimguiStorage();\n    storage.Platform_GetWindowPos = user_callback;\n    platform_io->Platform_GetWindowPos = &Platform_GetWindowPos_hook;\n}\n\n// Thunk satisfying the signature of ImGuiPlatformIO::Platform_GetWindowSize.\nImVec2 Platform_GetWindowSize_hook(ImGuiViewport* vp)\n{\n    ImVec2 size;\n    GetCimguiStorage().Platform_GetWindowSize(vp, &size);\n    return size;\n};\n\n// Fully C-compatible function pointer setter for ImGuiPlatformIO::Platform_GetWindowSize.\nCIMGUI_API void ImGuiPlatformIO_Set_Platform_GetWindowSize(ImGuiPlatformIO* platform_io, void(*user_callback)(ImGuiViewport* vp, ImVec2* out_size))\n{\n    CimguiStorage& storage = GetCimguiStorage();\n    storage.Platform_GetWindowSize = user_callback;\n    platform_io->Platform_GetWindowSize = &Platform_GetWindowSize_hook;\n}\n\n#endif\n\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\n# https://github.com/ocornut/imgui\nproject(imgui_imgui)\n\nset(CMAKE_CXX_STANDARD 23)\n\nif (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)\n    add_library(imgui_imgui OBJECT\n            source/imgui.cpp\n            source/imgui_demo.cpp\n            source/imgui_draw.cpp\n            source/imgui_tables.cpp\n            source/imgui_widgets.cpp\n\n            source/misc/freetype/imgui_freetype.cpp\n    )\n\n    target_include_directories(imgui_imgui PUBLIC\n        include\n        include/misc/freetype\n    )\n\n    if (CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\")\n        target_compile_options(imgui_imgui PRIVATE -Wno-unknown-warning-option)\n    endif()\n\n    find_package(Freetype REQUIRED)\n\n    target_include_directories(imgui_imgui PUBLIC ${FREETYPE_INCLUDE_DIRS})\n    target_link_directories(imgui_imgui PUBLIC ${FREETYPE_LIBRARY_DIRS})\n    target_link_libraries(imgui_imgui PUBLIC ${FREETYPE_LIBRARIES} ${LUNASVG_LIBRARIES})\n    target_compile_definitions(imgui_imgui PRIVATE EXPORT_SYMBOLS=1)\nendif()\n\nadd_library(imgui_includes INTERFACE)\ntarget_include_directories(imgui_includes INTERFACE include)\ntarget_include_directories(imgui_all_includes INTERFACE include include/misc/freetype)"
  },
  {
    "path": "lib/third_party/imgui/imgui/LICENSE.txt",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014-2023 Omar Cornut\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/README.md",
    "content": "Dear ImGui\n=====\n\n<center><b><i>\"Give someone state and they'll have a bug one day, but teach them how to represent state in two separate locations that have to be kept in sync and they'll have bugs for a lifetime.\"</i></b></center> <a href=\"https://twitter.com/rygorous/status/1507178315886444544\">-ryg</a>\n\n----\n\n[![Build Status](https://github.com/ocornut/imgui/workflows/build/badge.svg)](https://github.com/ocornut/imgui/actions?workflow=build) [![Static Analysis Status](https://github.com/ocornut/imgui/workflows/static-analysis/badge.svg)](https://github.com/ocornut/imgui/actions?workflow=static-analysis) [![Tests Status](https://github.com/ocornut/imgui_test_engine/workflows/tests/badge.svg)](https://github.com/ocornut/imgui_test_engine/actions?workflow=tests)\n\n<sub>(This library is available under a free and permissive license, but needs financial support to sustain its continued improvements. In addition to maintenance and stability there are many desirable features yet to be added. If your company is using Dear ImGui, please consider reaching out.)</sub>\n\nBusinesses: support continued development and maintenance via invoiced sponsoring/support contracts:\n<br>&nbsp;&nbsp;_E-mail: contact @ dearimgui dot com_\n<br>Individuals: support continued development and maintenance [here](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WGHNC6MBFLZ2S). Also see [Sponsors](https://github.com/ocornut/imgui/wiki/Sponsors) page.\n\n| [The Pitch](#the-pitch) - [Usage](#usage) - [How it works](#how-it-works) - [Releases & Changelogs](#releases--changelogs) - [Demo](#demo) - [Integration](#integration) |\n:----------------------------------------------------------: |\n| [Gallery](#gallery) - [Support, FAQ](#support-frequently-asked-questions-faq) -  [How to help](#how-to-help) - [Sponsors](https://github.com/ocornut/imgui/wiki/Sponsors) - [Credits](#credits) - [License](#license) |\n| [Wiki](https://github.com/ocornut/imgui/wiki) - [Languages & frameworks backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [User quotes](https://github.com/ocornut/imgui/wiki/Quotes) |\n\n### The Pitch\n\nDear ImGui is a **bloat-free graphical user interface library for C++**. It outputs optimized vertex buffers that you can render anytime in your 3D-pipeline-enabled application. It is fast, portable, renderer agnostic, and self-contained (no external dependencies).\n\nDear ImGui is designed to **enable fast iterations** and to **empower programmers** to create **content creation tools and visualization / debug tools** (as opposed to UI for the average end-user). It favors simplicity and productivity toward this goal and lacks certain features commonly found in more high-level libraries.\n\nDear ImGui is particularly suited to integration in game engines (for tooling), real-time 3D applications, fullscreen applications, embedded applications, or any applications on console platforms where operating system features are non-standard.\n\n - Minimize state synchronization.\n - Minimize UI-related state storage on user side.\n - Minimize setup and maintenance.\n - Easy to use to create dynamic UI which are the reflection of a dynamic data set.\n - Easy to use to create code-driven and data-driven tools.\n - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.\n - Easy to hack and improve.\n - Portable, minimize dependencies, run on target (consoles, phones, etc.).\n - Efficient runtime and memory consumption.\n - Battle-tested, used by [many major actors in the game industry](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui).\n\n### Usage\n\n**The core of Dear ImGui is self-contained within a few platform-agnostic files** which you can easily compile in your application/engine. They are all the files in the root folder of the repository (imgui*.cpp, imgui*.h). **No specific build process is required**. You can add the .cpp files into your existing project. \n\n**Backends for a variety of graphics API and rendering platforms** are provided in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder, along with example applications in the [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder. You may also create your own backend. Anywhere where you can render textured triangles, you can render Dear ImGui.\n\nSee the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide and [Integration](#integration) section of this document for more details.\n\nAfter Dear ImGui is set up in your application, you can use it from \\_anywhere\\_ in your program loop:\n```cpp\nImGui::Text(\"Hello, world %d\", 123);\nif (ImGui::Button(\"Save\"))\n    MySaveFunction();\nImGui::InputText(\"string\", buf, IM_ARRAYSIZE(buf));\nImGui::SliderFloat(\"float\", &f, 0.0f, 1.0f);\n```\n![sample code output (dark, segoeui font, freetype)](https://user-images.githubusercontent.com/8225057/191050833-b7ecf528-bfae-4a9f-ac1b-f3d83437a2f4.png)\n![sample code output (light, segoeui font, freetype)](https://user-images.githubusercontent.com/8225057/191050838-8742efd4-504d-4334-a9a2-e756d15bc2ab.png)\n\n```cpp\n// Create a window called \"My First Tool\", with a menu bar.\nImGui::Begin(\"My First Tool\", &my_tool_active, ImGuiWindowFlags_MenuBar);\nif (ImGui::BeginMenuBar())\n{\n    if (ImGui::BeginMenu(\"File\"))\n    {\n        if (ImGui::MenuItem(\"Open..\", \"Ctrl+O\")) { /* Do stuff */ }\n        if (ImGui::MenuItem(\"Save\", \"Ctrl+S\"))   { /* Do stuff */ }\n        if (ImGui::MenuItem(\"Close\", \"Ctrl+W\"))  { my_tool_active = false; }\n        ImGui::EndMenu();\n    }\n    ImGui::EndMenuBar();\n}\n\n// Edit a color stored as 4 floats\nImGui::ColorEdit4(\"Color\", my_color);\n\n// Generate samples and plot them\nfloat samples[100];\nfor (int n = 0; n < 100; n++)\n    samples[n] = sinf(n * 0.2f + ImGui::GetTime() * 1.5f);\nImGui::PlotLines(\"Samples\", samples, 100);\n\n// Display contents in a scrolling region\nImGui::TextColored(ImVec4(1,1,0,1), \"Important Stuff\");\nImGui::BeginChild(\"Scrolling\");\nfor (int n = 0; n < 50; n++)\n    ImGui::Text(\"%04d: Some text\", n);\nImGui::EndChild();\nImGui::End();\n```\n![my_first_tool_v188](https://user-images.githubusercontent.com/8225057/191055698-690a5651-458f-4856-b5a9-e8cc95c543e2.gif)\n\nDear ImGui allows you to **create elaborate tools** as well as very short-lived ones. On the extreme side of short-livedness: using the Edit&Continue (hot code reload) feature of modern compilers you can add a few widgets to tweak variables while your application is running, and remove the code a minute later! Dear ImGui is not just for tweaking values. You can use it to trace a running algorithm by just emitting text commands. You can use it along with your own reflection data to browse your dataset live. You can use it to expose the internals of a subsystem in your engine, to create a logger, an inspection tool, a profiler, a debugger, an entire game-making editor/framework, etc.\n\n### How it works\n\nThe IMGUI paradigm through its API tries to minimize superfluous state duplication, state synchronization, and state retention from the user's point of view. It is less error-prone (less code and fewer bugs) than traditional retained-mode interfaces, and lends itself to creating dynamic user interfaces. Check out the Wiki's [About the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) section for more details.\n\nDear ImGui outputs vertex buffers and command lists that you can easily render in your application. The number of draw calls and state changes required to render them is fairly small. Because Dear ImGui doesn't know or touch graphics state directly, you can call its functions  anywhere in your code (e.g. in the middle of a running algorithm, or in the middle of your own rendering process). Refer to the sample applications in the examples/ folder for instructions on how to integrate Dear ImGui with your existing codebase.\n\n_A common misunderstanding is to mistake immediate mode GUI for immediate mode rendering, which usually implies hammering your driver/GPU with a bunch of inefficient draw calls and state changes as the GUI functions are called. This is NOT what Dear ImGui does. Dear ImGui outputs vertex buffers and a small list of draw calls batches. It never touches your GPU directly. The draw call batches are decently optimal and you can render them later, in your app or even remotely._\n\n### Releases & Changelogs\n\nSee [Releases](https://github.com/ocornut/imgui/releases) page for decorated Changelogs.\nReading the changelogs is a good way to keep up to date with the things Dear ImGui has to offer, and maybe will give you ideas of some features that you've been ignoring until now!\n\n### Demo\n\nCalling the `ImGui::ShowDemoWindow()` function will create a demo window showcasing a variety of features and examples. The code is always available for reference in `imgui_demo.cpp`. [Here's how the demo looks](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v167/v167-misc.png).\n\nYou should be able to build the examples from sources. If you don't, let us know! If you want to have a quick look at some Dear ImGui features, you can download Windows binaries of the demo app here:\n- [imgui-demo-binaries-20230704.zip](https://www.dearimgui.com/binaries/imgui-demo-binaries-20230704.zip) (Windows, 1.89.7, built 2023/07/04, master) or [older binaries](https://www.dearimgui.com/binaries).\n\nThe demo applications are not DPI aware so expect some blurriness on a 4K screen. For DPI awareness in your application, you can load/reload your font at a different scale and scale your style with `style.ScaleAllSizes()` (see [FAQ](https://www.dearimgui.com/faq)).\n\n### Integration\n\nSee the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide for details.\n\nOn most platforms and when using C++, **you should be able to use a combination of the [imgui_impl_xxxx](https://github.com/ocornut/imgui/tree/master/backends) backends without modification** (e.g. `imgui_impl_win32.cpp` + `imgui_impl_dx11.cpp`). If your engine supports multiple platforms, consider using more imgui_impl_xxxx files instead of rewriting them: this will be less work for you, and you can get Dear ImGui running immediately. You can _later_ decide to rewrite a custom backend using your custom engine functions if you wish so.\n\nIntegrating Dear ImGui within your custom engine is a matter of 1) wiring mouse/keyboard/gamepad inputs 2) uploading a texture to your GPU/render engine 3) providing a render function that can bind textures and render textured triangles, which is essentially what Backends are doing. The [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder is populated with applications doing just that: setting up a window and using backends. If you follow the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide it should in theory takes you less than an hour to integrate Dear ImGui.  **Make sure to spend time reading the [FAQ](https://www.dearimgui.com/faq), comments, and the examples applications!**\n\nOfficially maintained backends/bindings (in repository):\n- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, SDL_Renderer, Vulkan, WebGPU.\n- Platforms: GLFW, SDL2/SDL3, Win32, Glut, OSX, Android.\n- Frameworks: Allegro5, Emscripten.\n\n[Third-party backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) wiki page:\n- Languages: C, C# and: Beef, ChaiScript, CovScript, Crystal, D, Go, Haskell, Haxe/hxcpp, Java, JavaScript, Julia, Kotlin, Lobster, Lua, Nim, Odin, Pascal, PureBasic, Python, ReaScript, Ruby, Rust, Swift, Zig...\n- Frameworks: AGS/Adventure Game Studio, Amethyst, Blender, bsf, Cinder, Cocos2d-x, Defold, Diligent Engine, Ebiten, Flexium, GML/Game Maker Studio, GLEQ, Godot, GTK3, Irrlicht Engine, JUCE, LÖVE+LUA, Mach Engine, Magnum, Marmalade, Monogame, NanoRT, nCine, Nim Game Lib, Nintendo 3DS/Switch/WiiU (homebrew), Ogre, openFrameworks, OSG/OpenSceneGraph, Orx, Photoshop, px_render, Qt/QtDirect3D, raylib, SFML, Sokol, Unity, Unreal Engine 4/5, UWP, vtk, VulkanHpp, VulkanSceneGraph, Win32 GDI, WxWidgets.\n- Many bindings are auto-generated (by good old [cimgui](https://github.com/cimgui/cimgui) or newer/experimental [dear_bindings](https://github.com/dearimgui/dear_bindings)), you can use their metadata output to generate bindings for other languages.\n\n[Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page:\n- Automation/testing, Text editors, node editors, timeline editors, plotting, software renderers, remote network access, memory editors, gizmos, etc. Notable and well supported extensions include [ImPlot](https://github.com/epezent/implot) and [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine).\n\nAlso see [Wiki](https://github.com/ocornut/imgui/wiki) for more links and ideas.\n\n### Gallery\n\nFor more user-submitted screenshots of projects using Dear ImGui, check out the [Gallery Threads](https://github.com/ocornut/imgui/issues/6897)!\n\nFor a list of third-party widgets and extensions, check out the [Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page.\n\n|  |  |\n|--|--|\n| Custom engine [erhe](https://github.com/tksuoran/erhe) (docking branch)<BR>[![erhe](https://user-images.githubusercontent.com/8225057/190203358-6988b846-0686-480e-8663-1311fbd18abd.jpg)](https://user-images.githubusercontent.com/994606/147875067-a848991e-2ad2-4fd3-bf71-4aeb8a547bcf.png) | Custom engine for [Wonder Boy: The Dragon's Trap](http://www.TheDragonsTrap.com) (2017)<BR>[![the dragon's trap](https://user-images.githubusercontent.com/8225057/190203379-57fcb80e-4aec-4fec-959e-17ddd3cd71e5.jpg)](https://cloud.githubusercontent.com/assets/8225057/20628927/33e14cac-b329-11e6-80f6-9524e93b048a.png) |\n| Custom engine (untitled)<BR>[![editor white](https://user-images.githubusercontent.com/8225057/190203393-c5ac9f22-b900-4d1e-bfeb-6027c63e3d92.jpg)](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v160/editor_white.png) | Tracy Profiler ([github](https://github.com/wolfpld/tracy))<BR>[![tracy profiler](https://user-images.githubusercontent.com/8225057/190203401-7b595f6e-607c-44d3-97ea-4c2673244dfb.jpg)](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v176/tracy_profiler.png) |\n\n### Support, Frequently Asked Questions (FAQ)\n\nSee: [Frequently Asked Questions (FAQ)](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md) where common questions are answered.\n\nSee: [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) and [Wiki](https://github.com/ocornut/imgui/wiki) for many links, references, articles.\n\nSee: [Articles about the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) to read/learn about the Immediate Mode GUI paradigm.\n\nSee: [Upcoming Changes](https://github.com/ocornut/imgui/wiki/Upcoming-Changes).\n\nSee: [Dear ImGui Test Engine + Test Suite](https://github.com/ocornut/imgui_test_engine) for Automation & Testing.\n\nGetting started? For first-time users having issues compiling/linking/running or issues loading fonts, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions). For ANY other questions, bug reports, requests, feedback, please post on [GitHub Issues](https://github.com/ocornut/imgui/issues). Please read and fill the New Issue template carefully.\n\nPrivate support is available for paying business customers (E-mail: _contact @ dearimgui dot com_).\n\n**Which version should I get?**\n\nWe occasionally tag [Releases](https://github.com/ocornut/imgui/releases) (with nice releases notes) but it is generally safe and recommended to sync to latest `master` or `docking` branch. The library is fairly stable and regressions tend to be fixed fast when reported. Advanced users may want to use the `docking` branch with [Multi-Viewport](https://github.com/ocornut/imgui/issues/1542) and [Docking](https://github.com/ocornut/imgui/issues/2109) features. This branch is kept in sync with master regularly.\n\n**Who uses Dear ImGui?**\n\nSee the [Quotes](https://github.com/ocornut/imgui/wiki/Quotes), [Sponsors](https://github.com/ocornut/imgui/wiki/Sponsors), and [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) Wiki pages for an idea of who is using Dear ImGui. Please add your game/software if you can! Also, see the [Gallery Threads](https://github.com/ocornut/imgui/issues/6897)!\n\nHow to help\n-----------\n\n**How can I help?**\n\n- See [GitHub Forum/Issues](https://github.com/ocornut/imgui/issues).\n- You may help with development and submit pull requests! Please understand that by submitting a PR you are also submitting a request for the maintainer to review your code and then take over its maintenance forever. PR should be crafted both in the interest of the end-users and also to ease the maintainer into understanding and accepting it.\n- See [Help wanted](https://github.com/ocornut/imgui/wiki/Help-Wanted) on the [Wiki](https://github.com/ocornut/imgui/wiki/) for some more ideas.\n- Be a [sponsor](https://github.com/ocornut/imgui/wiki/Sponsors)! Have your company financially support this project via invoiced sponsors/maintenance or by buying a license for [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine) (please reach out: omar AT dearimgui DOT com).\n\nSponsors\n--------\n\nOngoing Dear ImGui development is and has been financially supported by users and private sponsors.\n<BR>Please see the **[detailed list of current and past Dear ImGui supporters](https://github.com/ocornut/imgui/wiki/Sponsors)** for details.\n<BR>From November 2014 to December 2019, ongoing development has also been financially supported by its users on Patreon and through individual donations.\n\n**THANK YOU to all past and present supporters for helping to keep this project alive and thriving!**\n\nDear ImGui is using software and services provided free of charge for open source projects:\n- [PVS-Studio](https://www.viva64.com/en/b/0570/) for static analysis.\n- [GitHub actions](https://github.com/features/actions) for continuous integration systems.\n- [OpenCppCoverage](https://github.com/OpenCppCoverage/OpenCppCoverage) for code coverage analysis.\n\nCredits\n-------\n\nDeveloped by [Omar Cornut](https://www.miracleworld.net) and every direct or indirect [contributors](https://github.com/ocornut/imgui/graphs/contributors) to the GitHub. The early version of this library was developed with the support of [Media Molecule](https://www.mediamolecule.com) and first used internally on the game [Tearaway](https://tearaway.mediamolecule.com) (PS Vita).\n\nRecurring contributors include Rokas Kupstys [@rokups](https://github.com/rokups) (2020-2022): a good portion of work on automation system and regression tests now available in [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine).\n\nSponsoring, maintenance/support contracts and other B2B transactions are hosted and handled by [Disco Hello](https://www.discohello.com).\n\nOmar: \"I first discovered the IMGUI paradigm at [Q-Games](https://www.q-games.com) where Atman Binstock had dropped his own simple implementation in the codebase, which I spent quite some time improving and thinking about. It turned out that Atman was exposed to the concept directly by working with Casey. When I moved to Media Molecule I rewrote a new library trying to overcome the flaws and limitations of the first one I've worked with. It became this library and since then I have spent an unreasonable amount of time iterating and improving it.\"\n\nEmbeds [ProggyClean.ttf](http://upperbounds.net) font by Tristan Grimmer (MIT license).\n<br>Embeds [stb_textedit.h, stb_truetype.h, stb_rect_pack.h](https://github.com/nothings/stb/) by Sean Barrett (public domain).\n\nInspiration, feedback, and testing for early versions: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Anton Mikhailov, Matt Willis. Also thank you to everyone posting feedback, questions and patches on GitHub.\n\nLicense\n-------\n\nDear ImGui is licensed under the MIT License, see [LICENSE.txt](https://github.com/ocornut/imgui/blob/master/LICENSE.txt) for more information.\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/include/imconfig.h",
    "content": "//-----------------------------------------------------------------------------\n// COMPILE-TIME OPTIONS FOR DEAR IMGUI\n// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.\n// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.\n//-----------------------------------------------------------------------------\n// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)\n// B) or '#define IMGUI_USER_CONFIG \"my_imgui_config.h\"' in your project and then add directives in your own file without touching this template.\n//-----------------------------------------------------------------------------\n// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp\n// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.\n// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.\n// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.\n//-----------------------------------------------------------------------------\n\n#pragma once\n\n#include <assert.h>\n\n// For alloc() on ARM64 Windows\n#if __has_include(<malloc.h>)\n    #include <malloc.h>\n#endif\n\n//---- Define assertion handler. Defaults to calling assert().\n// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.\n//#define IM_ASSERT(_EXPR)  MyAssert(_EXPR)\n//#define IM_ASSERT(_EXPR)  ((void)(_EXPR))     // Disable asserts\nnamespace hex::dbg {\n    [[noreturn]] void assertionHandler(const char* file, int line, const char *function, const char* exprString);\n}\n\n#if defined(__PRETTY_FUNCTION__)\n    #define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __PRETTY_FUNCTION__, #_EXPR); } } while(0)\n#elif defined(__FUNCSIG__)\n    #define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __FUNCSIG__, #_EXPR); } } while(0)\n#else\n    #define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __FUNCTION__, #_EXPR); } } while(0)\n#endif\n\n//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows\n// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.\n// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()\n// for each static/DLL boundary you are calling from. Read \"Context and Memory Allocators\" section of imgui.cpp for more details.\n//#define IMGUI_API __declspec( dllexport )\n//#define IMGUI_API __declspec( dllimport )\n\n#if defined(_MSC_VER)\n    #if EXPORT_SYMBOLS == 1\n        #define IMGUI_API           __declspec(dllexport)\n        #define IMGUI_IMPL_API      __declspec(dllexport)\n        #define IMPLOT_API          __declspec(dllexport)\n        #define IMPLOT_IMPL_API     __declspec(dllexport)\n        #define IMPLOT3D_API        __declspec(dllexport)\n        #define IMPLOT3D_IMPL_API   __declspec(dllexport)\n    #else\n        #define IMGUI_API           __declspec(dllimport)\n        #define IMGUI_IMPL_API      __declspec(dllimport)\n        #define IMPLOT_API          __declspec(dllimport)\n        #define IMPLOT_IMPL_API     __declspec(dllimport)\n        #define IMPLOT3D_API        __declspec(dllimport)\n        #define IMPLOT3D_IMPL_API   __declspec(dllimport)\n    #endif\n#endif\n\n//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.\n//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n//#define IMGUI_DISABLE_OBSOLETE_KEYIO                      // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.\n\n//---- Disable all of Dear ImGui or don't implement standard windows/tools.\n// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.\n//#define IMGUI_DISABLE                                     // Disable everything: all headers and source files will be empty.\n//#define IMGUI_DISABLE_DEMO_WINDOWS                        // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.\n//#define IMGUI_DISABLE_DEBUG_TOOLS                         // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).\n\n//---- Don't implement some functions to reduce linkage requirements.\n//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS   // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)\n//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS          // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)\n//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS         // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)\n//#define IMGUI_DISABLE_WIN32_FUNCTIONS                     // [Win32] Won't use and link with any Win32 function (clipboard, ime).\n//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS      // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).\n//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS            // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)\n//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS              // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.\n//#define IMGUI_DISABLE_FILE_FUNCTIONS                      // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)\n//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS              // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.\n//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS                  // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().\n//#define IMGUI_DISABLE_SSE                                 // Disable use of SSE intrinsics even if available\n\n//---- Include imgui_user.h at the end of imgui.h as a convenience\n//#define IMGUI_INCLUDE_IMGUI_USER_H\n\n//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)\n//#define IMGUI_USE_BGRA_PACKED_COLOR\n\n//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)\n//#define IMGUI_USE_WCHAR32\n\n//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version\n// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.\n//#define IMGUI_STB_TRUETYPE_FILENAME   \"my_folder/stb_truetype.h\"\n//#define IMGUI_STB_RECT_PACK_FILENAME  \"my_folder/stb_rect_pack.h\"\n//#define IMGUI_STB_SPRINTF_FILENAME    \"my_folder/stb_sprintf.h\"    // only used if enabled\n//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION\n//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION\n\n//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)\n// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.\n//#define IMGUI_USE_STB_SPRINTF\n\n//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)\n// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).\n// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.\n//#define IMGUI_ENABLE_FREETYPE\n\n//---- Use stb_truetype to build and rasterize the font atlas (default)\n// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.\n//#define IMGUI_ENABLE_STB_TRUETYPE\n\n//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.\n// This will be inlined as part of ImVec2 and ImVec4 class declarations.\n/*\n#define IM_VEC2_CLASS_EXTRA                                                     \\\n        constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {}                   \\\n        operator MyVec2() const { return MyVec2(x,y); }\n\n#define IM_VEC4_CLASS_EXTRA                                                     \\\n        constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {}   \\\n        operator MyVec4() const { return MyVec4(x,y,z,w); }\n*/\n//---- ...Or use Dear ImGui's own very basic math operators.\n//#define IMGUI_DEFINE_MATH_OPERATORS\n\n//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.\n// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).\n// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.\n// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.\n//#define ImDrawIdx unsigned int\n\n//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)\n//struct ImDrawList;\n//struct ImDrawCmd;\n//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);\n//#define ImDrawCallback MyImDrawCallback\n\n//---- Debug Tools: Macro to break in Debugger\n// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)\n//#define IM_DEBUG_BREAK  IM_ASSERT(0)\n//#define IM_DEBUG_BREAK  __debugbreak()\n\n//---- Debug Tools: Enable slower asserts\n//#define IMGUI_DEBUG_PARANOID\n\n//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.\n/*\nnamespace ImGui\n{\n    void MyFunction(const char* name, const MyMatrix44& v);\n}\n*/\n\n#define IMGUI_ENABLE_TEST_ENGINE\n\n\n// IMPLOT CONFIG\n#define IMPLOT_CUSTOM_NUMERIC_TYPES (ImS8)(ImU8)(ImS16)(ImU16)(ImS32)(ImU32)(ImS64)(ImU64)(float)(double)(long double)\n\n#define IM_OFFSETOF(_TYPE,_MEMBER)  offsetof(_TYPE, _MEMBER)\n#define IM_FLOOR IM_TRUNC\n\n#define IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n#define IMGUI_DISABLE_OBSOLETE_KEYIO\n#define IMGUI_ENABLE_FREETYPE\n#define ImDrawIdx unsigned int\n#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX\n#define IMGUI_USE_WCHAR32\n#define IMGUI_USE_LEGACY_CRC32_ADLER 1\n\n#if defined(IMGUI_TEST_ENGINE)\n    #include \"imgui_te_imconfig.h\"\n#endif"
  },
  {
    "path": "lib/third_party/imgui/imgui/include/imgui.h",
    "content": "// dear imgui, v1.92.7 WIP\n// (headers)\n\n// Help:\n// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.\n// - Read top of imgui.cpp for more details, links and comments.\n// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including imgui.h (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4.\n\n// Resources:\n// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)\n// - Homepage ................... https://github.com/ocornut/imgui\n// - Releases & Changelog ....... https://github.com/ocornut/imgui/releases\n// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!)\n// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)\n//   - Getting Started            https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)\n//   - Third-party Extensions     https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)\n//   - Bindings/Backends          https://github.com/ocornut/imgui/wiki/Bindings (language bindings + backends for various tech/engines)\n//   - Debug Tools                https://github.com/ocornut/imgui/wiki/Debug-Tools\n//   - Glossary                   https://github.com/ocornut/imgui/wiki/Glossary\n//   - Software using Dear ImGui  https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui\n// - Issues & support ........... https://github.com/ocornut/imgui/issues\n// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)\n// - Web version of the Demo .... https://pthom.github.io/imgui_manual (w/ source code browser)\n\n// For FIRST-TIME users having issues compiling/linking/running:\n// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.\n// EVERYTHING ELSE should be asked in 'Issues'! We are building a database of cross-linked knowledge there.\n// Since 1.92, we encourage font loading questions to also be posted in 'Issues'.\n\n// Library Version\n// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')\n#define IMGUI_VERSION       \"1.92.7 WIP\"\n#define IMGUI_VERSION_NUM   19265\n#define IMGUI_HAS_TABLE             // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000\n#define IMGUI_HAS_TEXTURES          // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198\n#define IMGUI_HAS_VIEWPORT          // In 'docking' WIP branch.\n#define IMGUI_HAS_DOCK              // In 'docking' WIP branch.\n\n/*\n\nIndex of this file:\n// [SECTION] Header mess\n// [SECTION] Forward declarations and basic types\n// [SECTION] Texture identifiers (ImTextureID, ImTextureRef)\n// [SECTION] Dear ImGui end-user API functions\n// [SECTION] Flags & Enumerations\n// [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)\n// [SECTION] Helpers: Debug log, Memory allocations macros, ImVector<>\n// [SECTION] ImGuiStyle\n// [SECTION] ImGuiIO\n// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiWindowClass, ImGuiPayload)\n// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor)\n// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO, ImGuiSelectionRequest, ImGuiSelectionBasicStorage, ImGuiSelectionExternalStorage)\n// [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData)\n// [SECTION] Texture API (ImTextureFormat, ImTextureStatus, ImTextureRect, ImTextureData)\n// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFontBaked, ImFont)\n// [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport)\n// [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformMonitor, ImGuiPlatformImeData)\n// [SECTION] Obsolete functions and types\n\n*/\n\n#pragma once\n\n// Configuration file with compile-time options\n// (edit imconfig.h or '#define IMGUI_USER_CONFIG \"myfilename.h\" from your build system)\n#ifdef IMGUI_USER_CONFIG\n#include IMGUI_USER_CONFIG\n#endif\n#include \"imconfig.h\"\n\n#ifndef IMGUI_DISABLE\n\n//-----------------------------------------------------------------------------\n// [SECTION] Header mess\n//-----------------------------------------------------------------------------\n\n// Includes\n#include <float.h>                  // FLT_MIN, FLT_MAX\n#include <stdarg.h>                 // va_list, va_start, va_end\n#include <stddef.h>                 // ptrdiff_t, NULL\n#include <string.h>                 // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp\n\n// Define attributes of all API symbols declarations (e.g. for DLL under Windows)\n// IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default backends files (imgui_impl_xxx.h)\n// Using dear imgui via a shared library is not recommended: we don't guarantee backward nor forward ABI compatibility + this is a call-heavy library and function call overhead adds up.\n#ifndef IMGUI_API\n#define IMGUI_API\n#endif\n#ifndef IMGUI_IMPL_API\n#define IMGUI_IMPL_API              IMGUI_API\n#endif\n\n// Helper Macros\n// (note: compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes.)\n#ifndef IM_ASSERT\n#include <assert.h>\n#define IM_ASSERT(_EXPR)            assert(_EXPR)                               // You can override the default assert handler by editing imconfig.h\n#endif\n#define IM_COUNTOF(_ARR)            ((int)(sizeof(_ARR) / sizeof(*(_ARR))))     // Size of a static C-style array. Don't use on pointers!\n#define IM_UNUSED(_VAR)             ((void)(_VAR))                              // Used to silence \"unused variable warnings\". Often useful as asserts may be stripped out from final builds.\n#define IM_STRINGIFY_HELPER(_EXPR)  #_EXPR\n#define IM_STRINGIFY(_EXPR)         IM_STRINGIFY_HELPER(_EXPR)                  // Preprocessor idiom to stringify e.g. an integer or a macro.\n\n// Check that version and structures layouts are matching between compiled imgui code and caller. Read comments above DebugCheckVersionAndDataLayout() for details.\n#define IMGUI_CHECKVERSION()        ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))\n\n// Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions.\n// (MSVC provides an equivalent mechanism via SAL Annotations but it requires the macros in a different\n//  location. e.g. #include <sal.h> + void myprintf(_Printf_format_string_ const char* format, ...),\n//  and only works when using Code Analysis, rather than just normal compiling).\n// (see https://github.com/ocornut/imgui/issues/8871 for a patch to enable this for MSVC's Code Analysis)\n#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__)\n#define IM_FMTARGS(FMT)             __attribute__((format(gnu_printf, FMT, FMT+1)))\n#define IM_FMTLIST(FMT)             __attribute__((format(gnu_printf, FMT, 0)))\n#elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__))\n#define IM_FMTARGS(FMT)             __attribute__((format(printf, FMT, FMT+1)))\n#define IM_FMTLIST(FMT)             __attribute__((format(printf, FMT, 0)))\n#else\n#define IM_FMTARGS(FMT)\n#define IM_FMTLIST(FMT)\n#endif\n\n// Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions)\n#if defined(_MSC_VER) && !defined(__clang__)  && !defined(__INTEL_COMPILER) && !defined(IMGUI_DEBUG_PARANOID)\n#define IM_MSVC_RUNTIME_CHECKS_OFF      __pragma(runtime_checks(\"\",off))     __pragma(check_stack(off)) __pragma(strict_gs_check(push,off))\n#define IM_MSVC_RUNTIME_CHECKS_RESTORE  __pragma(runtime_checks(\"\",restore)) __pragma(check_stack())    __pragma(strict_gs_check(pop))\n#else\n#define IM_MSVC_RUNTIME_CHECKS_OFF\n#define IM_MSVC_RUNTIME_CHECKS_RESTORE\n#endif\n\n// Warnings\n#ifdef _MSC_VER\n#pragma warning (push)\n#pragma warning (disable: 26495)    // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).\n#endif\n#if defined(__clang__)\n#pragma clang diagnostic push\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast\n#pragma clang diagnostic ignored \"-Wfloat-equal\"                    // warning: comparing floating point with == or != is unsafe\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant\n#pragma clang diagnostic ignored \"-Wreserved-identifier\"            // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter\n#pragma clang diagnostic ignored \"-Wunsafe-buffer-usage\"            // warning: 'xxx' is an unsafe pointer used for buffer access\n#pragma clang diagnostic ignored \"-Wnontrivial-memaccess\"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type\n#elif defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpragmas\"                          // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"                      // warning: comparing floating-point with '==' or '!=' is unsafe\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Forward declarations and basic types\n//-----------------------------------------------------------------------------\n\n// Scalar data types\ntypedef unsigned int        ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string)\ntypedef signed char         ImS8;   // 8-bit signed integer\ntypedef unsigned char       ImU8;   // 8-bit unsigned integer\ntypedef signed short        ImS16;  // 16-bit signed integer\ntypedef unsigned short      ImU16;  // 16-bit unsigned integer\ntypedef signed int          ImS32;  // 32-bit signed integer == int\ntypedef unsigned int        ImU32;  // 32-bit unsigned integer (often used to store packed colors)\ntypedef signed   long long  ImS64;  // 64-bit signed integer\ntypedef unsigned long long  ImU64;  // 64-bit unsigned integer\n\n// Forward declarations: ImDrawList, ImFontAtlas layer\nstruct ImDrawChannel;               // Temporary storage to output draw commands out of order, used by ImDrawListSplitter and ImDrawList::ChannelsSplit()\nstruct ImDrawCmd;                   // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call, unless it is a callback)\nstruct ImDrawData;                  // All draw command lists required to render the frame + pos/size coordinates to use for the projection matrix.\nstruct ImDrawList;                  // A single draw command list (generally one per window, conceptually you may see this as a dynamic \"mesh\" builder)\nstruct ImDrawListSharedData;        // Data shared among multiple draw lists (typically owned by parent ImGui context, but you may create one yourself)\nstruct ImDrawListSplitter;          // Helper to split a draw list into different layers which can be drawn into out of order, then flattened back.\nstruct ImDrawVert;                  // A single vertex (pos + uv + col = 20 bytes by default. Override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT)\nstruct ImFont;                      // Runtime data for a single font within a parent ImFontAtlas\nstruct ImFontAtlas;                 // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF/OTF font loader\nstruct ImFontAtlasBuilder;          // Opaque storage for building a ImFontAtlas\nstruct ImFontAtlasRect;             // Output of ImFontAtlas::GetCustomRect() when using custom rectangles.\nstruct ImFontBaked;                 // Baked data for a ImFont at a given size.\nstruct ImFontConfig;                // Configuration data when adding a font or merging fonts\nstruct ImFontGlyph;                 // A single font glyph (code point + coordinates within in ImFontAtlas + offset)\nstruct ImFontGlyphRangesBuilder;    // Helper to build glyph ranges from text/string data\nstruct ImFontLoader;                // Opaque interface to a font loading backend (stb_truetype, FreeType etc.).\nstruct ImTextureData;               // Specs and pixel storage for a texture used by Dear ImGui.\nstruct ImTextureRect;               // Coordinates of a rectangle within a texture.\nstruct ImColor;                     // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using)\n\n// Forward declarations: ImGui layer\nstruct ImGuiContext;                // Dear ImGui context (opaque structure, unless including imgui_internal.h)\nstruct ImGuiIO;                     // Main configuration and I/O between your application and ImGui (also see: ImGuiPlatformIO)\nstruct ImGuiInputTextCallbackData;  // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use)\nstruct ImGuiKeyData;                // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions.\nstruct ImGuiListClipper;            // Helper to manually clip large list of items\nstruct ImGuiMultiSelectIO;          // Structure to interact with a BeginMultiSelect()/EndMultiSelect() block\nstruct ImGuiOnceUponAFrame;         // Helper for running a block of code not more than once a frame\nstruct ImGuiPayload;                // User data payload for drag and drop operations\nstruct ImGuiPlatformIO;             // Interface between platform/renderer backends and ImGui (e.g. Clipboard, IME, Multi-Viewport support). Extends ImGuiIO.\nstruct ImGuiPlatformImeData;        // Platform IME data for io.PlatformSetImeDataFn() function.\nstruct ImGuiPlatformMonitor;        // Multi-viewport support: user-provided bounds for each connected monitor/display. Used when positioning popups and tooltips to avoid them straddling monitors\nstruct ImGuiSelectionBasicStorage;  // Optional helper to store multi-selection state + apply multi-selection requests.\nstruct ImGuiSelectionExternalStorage;//Optional helper to apply multi-selection requests to existing randomly accessible storage.\nstruct ImGuiSelectionRequest;       // A selection request (stored in ImGuiMultiSelectIO)\nstruct ImGuiSizeCallbackData;       // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use)\nstruct ImGuiStorage;                // Helper for key->value storage (container sorted by key)\nstruct ImGuiStoragePair;            // Helper for key->value storage (pair)\nstruct ImGuiStyle;                  // Runtime data for styling/colors\nstruct ImGuiTableSortSpecs;         // Sorting specifications for a table (often handling sort specs for a single column, occasionally more)\nstruct ImGuiTableColumnSortSpecs;   // Sorting specification for one column of a table\nstruct ImGuiTextBuffer;             // Helper to hold and append into a text buffer (~string builder)\nstruct ImGuiTextFilter;             // Helper to parse and apply text filters (e.g. \"aaaaa[,bbbbb][,ccccc]\")\nstruct ImGuiViewport;               // A Platform Window (always 1 unless multi-viewport are enabled. One per platform window to output to). In the future may represent Platform Monitor\nstruct ImGuiWindowClass;            // Window class (rare/advanced uses: provide hints to the platform backend via altered viewport flags and parent/child info)\n\n// Enumerations\n// - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration)\n// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!\n//   - In Visual Studio: Ctrl+Comma (\"Edit.GoToAll\") can follow symbols inside comments, whereas Ctrl+F12 (\"Edit.GoToImplementation\") cannot.\n//   - In Visual Studio w/ Visual Assist installed: Alt+G (\"VAssistX.GoToImplementation\") can also follow symbols inside comments.\n//   - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.\nenum ImGuiDir : int;                // -> enum ImGuiDir              // Enum: A cardinal direction (Left, Right, Up, Down)\nenum ImGuiKey : int;                // -> enum ImGuiKey              // Enum: A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value)\nenum ImGuiMouseSource : int;        // -> enum ImGuiMouseSource      // Enum; A mouse input source identifier (Mouse, TouchScreen, Pen)\nenum ImGuiSortDirection : ImU8;     // -> enum ImGuiSortDirection    // Enum: A sorting direction (ascending or descending)\ntypedef int ImGuiCol;               // -> enum ImGuiCol_             // Enum: A color identifier for styling\ntypedef int ImGuiCond;              // -> enum ImGuiCond_            // Enum: A condition for many Set*() functions\ntypedef int ImGuiDataType;          // -> enum ImGuiDataType_        // Enum: A primary data type\ntypedef int ImGuiMouseButton;       // -> enum ImGuiMouseButton_     // Enum: A mouse button identifier (0=left, 1=right, 2=middle)\ntypedef int ImGuiMouseCursor;       // -> enum ImGuiMouseCursor_     // Enum: A mouse cursor shape\ntypedef int ImGuiStyleVar;          // -> enum ImGuiStyleVar_        // Enum: A variable identifier for styling\ntypedef int ImGuiTableBgTarget;     // -> enum ImGuiTableBgTarget_   // Enum: A color target for TableSetBgColor()\n\n// Flags (declared as int to allow using as flags without overhead, and to not pollute the top of this file)\n// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!\n//   - In Visual Studio: Ctrl+Comma (\"Edit.GoToAll\") can follow symbols inside comments, whereas Ctrl+F12 (\"Edit.GoToImplementation\") cannot.\n//   - In Visual Studio w/ Visual Assist installed: Alt+G (\"VAssistX.GoToImplementation\") can also follow symbols inside comments.\n//   - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.\ntypedef int ImDrawFlags;            // -> enum ImDrawFlags_          // Flags: for ImDrawList functions\ntypedef int ImDrawListFlags;        // -> enum ImDrawListFlags_      // Flags: for ImDrawList instance\ntypedef int ImDrawTextFlags;        // -> enum ImDrawTextFlags_      // Internal, do not use!\ntypedef int ImFontFlags;            // -> enum ImFontFlags_          // Flags: for ImFont\ntypedef int ImFontAtlasFlags;       // -> enum ImFontAtlasFlags_     // Flags: for ImFontAtlas\ntypedef int ImGuiBackendFlags;      // -> enum ImGuiBackendFlags_    // Flags: for io.BackendFlags\ntypedef int ImGuiButtonFlags;       // -> enum ImGuiButtonFlags_     // Flags: for InvisibleButton()\ntypedef int ImGuiChildFlags;        // -> enum ImGuiChildFlags_      // Flags: for BeginChild()\ntypedef int ImGuiColorEditFlags;    // -> enum ImGuiColorEditFlags_  // Flags: for ColorEdit4(), ColorPicker4() etc.\ntypedef int ImGuiConfigFlags;       // -> enum ImGuiConfigFlags_     // Flags: for io.ConfigFlags\ntypedef int ImGuiComboFlags;        // -> enum ImGuiComboFlags_      // Flags: for BeginCombo()\ntypedef int ImGuiDockNodeFlags;     // -> enum ImGuiDockNodeFlags_   // Flags: for DockSpace()\ntypedef int ImGuiDragDropFlags;     // -> enum ImGuiDragDropFlags_   // Flags: for BeginDragDropSource(), AcceptDragDropPayload()\ntypedef int ImGuiFocusedFlags;      // -> enum ImGuiFocusedFlags_    // Flags: for IsWindowFocused()\ntypedef int ImGuiHoveredFlags;      // -> enum ImGuiHoveredFlags_    // Flags: for IsItemHovered(), IsWindowHovered() etc.\ntypedef int ImGuiInputFlags;        // -> enum ImGuiInputFlags_      // Flags: for Shortcut(), SetNextItemShortcut()\ntypedef int ImGuiInputTextFlags;    // -> enum ImGuiInputTextFlags_  // Flags: for InputText(), InputTextMultiline()\ntypedef int ImGuiItemFlags;         // -> enum ImGuiItemFlags_       // Flags: for PushItemFlag(), shared by all items\ntypedef int ImGuiKeyChord;          // -> ImGuiKey | ImGuiMod_XXX    // Flags: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values.\ntypedef int ImGuiListClipperFlags;  // -> enum ImGuiListClipperFlags_// Flags: for ImGuiListClipper\ntypedef int ImGuiPopupFlags;        // -> enum ImGuiPopupFlags_      // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen()\ntypedef int ImGuiMultiSelectFlags;  // -> enum ImGuiMultiSelectFlags_// Flags: for BeginMultiSelect()\ntypedef int ImGuiSelectableFlags;   // -> enum ImGuiSelectableFlags_ // Flags: for Selectable()\ntypedef int ImGuiSliderFlags;       // -> enum ImGuiSliderFlags_     // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.\ntypedef int ImGuiTabBarFlags;       // -> enum ImGuiTabBarFlags_     // Flags: for BeginTabBar()\ntypedef int ImGuiTabItemFlags;      // -> enum ImGuiTabItemFlags_    // Flags: for BeginTabItem()\ntypedef int ImGuiTableFlags;        // -> enum ImGuiTableFlags_      // Flags: For BeginTable()\ntypedef int ImGuiTableColumnFlags;  // -> enum ImGuiTableColumnFlags_// Flags: For TableSetupColumn()\ntypedef int ImGuiTableRowFlags;     // -> enum ImGuiTableRowFlags_   // Flags: For TableNextRow()\ntypedef int ImGuiTreeNodeFlags;     // -> enum ImGuiTreeNodeFlags_   // Flags: for TreeNode(), TreeNodeEx(), CollapsingHeader()\ntypedef int ImGuiViewportFlags;     // -> enum ImGuiViewportFlags_   // Flags: for ImGuiViewport\ntypedef int ImGuiWindowFlags;       // -> enum ImGuiWindowFlags_     // Flags: for Begin(), BeginChild()\n\n// Character types\n// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display)\ntypedef unsigned int ImWchar32;     // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings.\ntypedef unsigned short ImWchar16;   // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings.\n#ifdef IMGUI_USE_WCHAR32            // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16]\ntypedef ImWchar32 ImWchar;\n#else\ntypedef ImWchar16 ImWchar;\n#endif\n\n// Multi-Selection item index or identifier when using BeginMultiSelect()\n// - Used by SetNextItemSelectionUserData() + and inside ImGuiMultiSelectIO structure.\n// - Most users are likely to use this store an item INDEX but this may be used to store a POINTER/ID as well. Read comments near ImGuiMultiSelectIO for details.\ntypedef ImS64 ImGuiSelectionUserData;\n\n// Callback and functions types\ntypedef int     (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data);    // Callback function for ImGui::InputText()\ntypedef void    (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data);              // Callback function for ImGui::SetNextWindowSizeConstraints()\ntypedef void*   (*ImGuiMemAllocFunc)(size_t sz, void* user_data);               // Function signature for ImGui::SetAllocatorFunctions()\ntypedef void    (*ImGuiMemFreeFunc)(void* ptr, void* user_data);                // Function signature for ImGui::SetAllocatorFunctions()\n\n// ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type]\n// - This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type.\n// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4.\nIM_MSVC_RUNTIME_CHECKS_OFF\nstruct ImVec2\n{\n    float                                   x, y;\n    constexpr ImVec2()                      : x(0.0f), y(0.0f) { }\n    constexpr ImVec2(float _x, float _y)    : x(_x), y(_y) { }\n    float& operator[] (size_t idx)          { IM_ASSERT(idx == 0 || idx == 1); return ((float*)(void*)(char*)this)[idx]; } // We very rarely use this [] operator, so the assert overhead is fine.\n    float  operator[] (size_t idx) const    { IM_ASSERT(idx == 0 || idx == 1); return ((const float*)(const void*)(const char*)this)[idx]; }\n#ifdef IM_VEC2_CLASS_EXTRA\n    IM_VEC2_CLASS_EXTRA     // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2.\n#endif\n};\n\n// ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type]\nstruct ImVec4\n{\n    float                                                     x, y, z, w;\n    constexpr ImVec4()                                        : x(0.0f), y(0.0f), z(0.0f), w(0.0f) { }\n    constexpr ImVec4(float _x, float _y, float _z, float _w)  : x(_x), y(_y), z(_z), w(_w) { }\n#ifdef IM_VEC4_CLASS_EXTRA\n    IM_VEC4_CLASS_EXTRA     // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4.\n#endif\n};\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n//-----------------------------------------------------------------------------\n// [SECTION] Texture identifiers (ImTextureID, ImTextureRef)\n//-----------------------------------------------------------------------------\n\n// ImTextureID = backend specific, low-level identifier for a texture uploaded in GPU/graphics system.\n// [Compile-time configurable type]\n// - When a Rendered Backend creates a texture, it store its native identifier into a ImTextureID value.\n//   (e.g. Used by DX11 backend to a `ID3D11ShaderResourceView*`; Used by OpenGL backends to store `GLuint`;\n//         Used by SDLGPU backend to store a `SDL_GPUTextureSamplerBinding*`, etc.).\n// - User may submit their own textures to e.g. ImGui::Image() function by passing this value.\n// - During the rendering loop, the Renderer Backend retrieve the ImTextureID, which stored inside a\n//   ImTextureRef, which is stored inside a ImDrawCmd.\n// - Compile-time type configuration:\n//   - To use something other than a 64-bit value: add '#define ImTextureID MyTextureType*' in your imconfig.h file.\n//   - This can be whatever to you want it to be! read the FAQ entry about textures for details.\n//   - You may decide to store a higher-level structure containing texture, sampler, shader etc. with various\n//     constructors if you like. You will need to implement ==/!= operators.\n// History:\n// - In v1.91.4 (2024/10/08): the default type for ImTextureID was changed from 'void*' to 'ImU64'. This allowed backends requiring 64-bit worth of data to build on 32-bit architectures. Use intermediary intptr_t cast and read FAQ if you have casting warnings.\n// - In v1.92.0 (2025/06/11): added ImTextureRef which carry either a ImTextureID either a pointer to internal texture atlas. All user facing functions taking ImTextureID changed to ImTextureRef\n#ifndef ImTextureID\ntypedef ImU64 ImTextureID;      // Default: store up to 64-bits (any pointer or integer). A majority of backends are ok with that.\n#endif\n\n// Define this if you need to change the invalid value for your backend.\n// - in v1.92.7 (2025/03/12): we changed default value from 0 to -1 as it is a better default, which supports storing offsets/indices.\n// - If this is causing problem with your custom ImTextureID definition, you can add '#define ImTextureID_Invalid' to your imconfig + please report this to GitHub.\n#ifndef ImTextureID_Invalid\n#define ImTextureID_Invalid     ((ImTextureID)-1)\n#endif\n\n// ImTextureRef = higher-level identifier for a texture. Store a ImTextureID _or_ a ImTextureData*.\n// The identifier is valid even before the texture has been uploaded to the GPU/graphics system.\n// This is what gets passed to functions such as `ImGui::Image()`, `ImDrawList::AddImage()`.\n// This is what gets stored in draw commands (`ImDrawCmd`) to identify a texture during rendering.\n// - When a texture is created by user code (e.g. custom images), we directly store the low-level ImTextureID.\n//   Because of this, when displaying your own texture you are likely to ever only manage ImTextureID values on your side.\n// - When a texture is created by the backend, we stores a ImTextureData* which becomes an indirection\n//   to extract the ImTextureID value during rendering, after texture upload has happened.\n// - To create a ImTextureRef from a ImTextureData you can use ImTextureData::GetTexRef().\n//   We intentionally do not provide an ImTextureRef constructor for this: we don't expect this\n//   to be frequently useful to the end-user, and it would be erroneously called by many legacy code.\n// - If you want to bind the current atlas when using custom rectangle, you can use io.Fonts->TexRef.\n// - Binding generators for languages such as C (which don't have constructors), should provide a helper, e.g.\n//      inline ImTextureRef ImTextureRefFromID(ImTextureID tex_id) { ImTextureRef tex_ref = { ._TexData = NULL, .TexID = tex_id }; return tex_ref; }\n// In 1.92 we changed most drawing functions using ImTextureID to use ImTextureRef.\n// We intentionally do not provide an implicit ImTextureRef -> ImTextureID cast operator because it is technically lossy to convert ImTextureRef to ImTextureID before rendering.\nIM_MSVC_RUNTIME_CHECKS_OFF\nstruct ImTextureRef\n{\n    ImTextureRef()                          { _TexData = NULL; _TexID = ImTextureID_Invalid; }\n    ImTextureRef(ImTextureID tex_id)        { _TexData = NULL; _TexID = tex_id; }\n#if !defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && !defined(ImTextureID)\n    ImTextureRef(void* tex_id)              { _TexData = NULL; _TexID = (ImTextureID)(size_t)tex_id; }  // For legacy backends casting to ImTextureID\n#endif\n\n    inline ImTextureID  GetTexID() const;   // == (_TexData ? _TexData->TexID : _TexID) // Implemented below in the file.\n\n    // Members (either are set, never both!)\n    ImTextureData*      _TexData;           //      A texture, generally owned by a ImFontAtlas. Will convert to ImTextureID during render loop, after texture has been uploaded.\n    ImTextureID         _TexID;             // _OR_ Low-level backend texture identifier, if already uploaded or created by user/app. Generally provided to e.g. ImGui::Image() calls.\n};\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n//-----------------------------------------------------------------------------\n// [SECTION] Dear ImGui end-user API functions\n// (Note that ImGui:: being a namespace, you can add extra ImGui:: functions in your own separate file. Please don't modify imgui source files!)\n//-----------------------------------------------------------------------------\n\nnamespace ImGui\n{\n    // Context creation and access\n    // - Each context create its own ImFontAtlas by default. You may instance one yourself and pass it to CreateContext() to share a font atlas between contexts.\n    // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()\n    //   for each static/DLL boundary you are calling from. Read \"Context and Memory Allocators\" section of imgui.cpp for details.\n    IMGUI_API ImGuiContext* CreateContext(ImFontAtlas* shared_font_atlas = NULL);\n    IMGUI_API void          DestroyContext(ImGuiContext* ctx = NULL);   // NULL = destroy current context\n    IMGUI_API ImGuiContext* GetCurrentContext();\n    IMGUI_API void          SetCurrentContext(ImGuiContext* ctx);\n\n    // Main\n    IMGUI_API ImGuiIO&      GetIO();                                    // access the ImGuiIO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags)\n    IMGUI_API ImGuiPlatformIO& GetPlatformIO();                         // access the ImGuiPlatformIO structure (mostly hooks/functions to connect to platform/renderer and OS Clipboard, IME etc.)\n    IMGUI_API ImGuiStyle&   GetStyle();                                 // access the Style structure (colors, sizes). Always use PushStyleColor(), PushStyleVar() to modify style mid-frame!\n    IMGUI_API void          NewFrame();                                 // start a new Dear ImGui frame, you can submit any command from this point until Render()/EndFrame().\n    IMGUI_API void          EndFrame();                                 // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all!\n    IMGUI_API void          Render();                                   // ends the Dear ImGui frame, finalize the draw data. You can then get call GetDrawData().\n    IMGUI_API ImDrawData*   GetDrawData();                              // valid after Render() and until the next call to NewFrame(). Call ImGui_ImplXXXX_RenderDrawData() function in your Renderer Backend to render.\n\n    // Demo, Debug, Information\n    IMGUI_API void          ShowDemoWindow(bool* p_open = NULL);        // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application!\n    IMGUI_API void          ShowMetricsWindow(bool* p_open = NULL);     // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc.\n    IMGUI_API void          ShowDebugLogWindow(bool* p_open = NULL);    // create Debug Log window. display a simplified log of important dear imgui events.\n    IMGUI_API void          ShowIDStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID.\n    IMGUI_API void          ShowAboutWindow(bool* p_open = NULL);       // create About window. display Dear ImGui version, credits and build/system information.\n    IMGUI_API void          ShowStyleEditor(ImGuiStyle* ref = NULL);    // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style)\n    IMGUI_API bool          ShowStyleSelector(const char* label);       // add style selector block (not a window), essentially a combo listing the default styles.\n    IMGUI_API void          ShowFontSelector(const char* label);        // add font selector block (not a window), essentially a combo listing the loaded fonts.\n    IMGUI_API void          ShowUserGuide();                            // add basic help/info block (not a window): how to manipulate ImGui as an end-user (mouse/keyboard controls).\n    IMGUI_API const char*   GetVersion();                               // get the compiled version string e.g. \"1.80 WIP\" (essentially the value for IMGUI_VERSION from the compiled version of imgui.cpp)\n\n    // Styles\n    IMGUI_API void          StyleColorsDark(ImGuiStyle* dst = NULL);    // new, recommended style (default)\n    IMGUI_API void          StyleColorsLight(ImGuiStyle* dst = NULL);   // best used with borders and a custom, thicker font\n    IMGUI_API void          StyleColorsClassic(ImGuiStyle* dst = NULL); // classic imgui style\n\n    // Windows\n    // - Begin() = push window to the stack and start appending to it. End() = pop window from the stack.\n    // - Passing 'bool* p_open != NULL' shows a window-closing widget in the upper-right corner of the window,\n    //   which clicking will set the boolean to false when clicked.\n    // - You may append multiple times to the same window during the same frame by calling Begin()/End() pairs multiple times.\n    //   Some information such as 'flags' or 'p_open' will only be considered by the first call to Begin().\n    // - Begin() return false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting\n    //   anything to the window. Always call a matching End() for each Begin() call, regardless of its return value!\n    //   [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions\n    //    such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding\n    //    BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.]\n    // - Note that the bottom of window stack always contains a window called \"Debug\".\n    IMGUI_API bool          Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0);\n    IMGUI_API void          End();\n\n    // Child Windows\n    // - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child.\n    // - Before 1.90 (November 2023), the \"ImGuiChildFlags child_flags = 0\" parameter was \"bool border = false\".\n    //   This API is backward compatible with old code, as we guarantee that ImGuiChildFlags_Borders == true.\n    //   Consider updating your old code:\n    //      BeginChild(\"Name\", size, false)   -> Begin(\"Name\", size, 0); or Begin(\"Name\", size, ImGuiChildFlags_None);\n    //      BeginChild(\"Name\", size, true)    -> Begin(\"Name\", size, ImGuiChildFlags_Borders);\n    // - Manual sizing (each axis can use a different setting e.g. ImVec2(0.0f, 400.0f)):\n    //     == 0.0f: use remaining parent window size for this axis.\n    //      > 0.0f: use specified size for this axis.\n    //      < 0.0f: right/bottom-align to specified distance from available content boundaries.\n    // - Specifying ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY makes the sizing automatic based on child contents.\n    //   Combining both ImGuiChildFlags_AutoResizeX _and_ ImGuiChildFlags_AutoResizeY defeats purpose of a scrolling region and is NOT recommended.\n    // - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting\n    //   anything to the window. Always call a matching EndChild() for each BeginChild() call, regardless of its return value.\n    //   [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions\n    //    such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding\n    //    BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.]\n    IMGUI_API bool          BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0);\n    IMGUI_API bool          BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0);\n    IMGUI_API void          EndChild();\n\n    // Windows Utilities\n    // - 'current window' = the window we are appending into while inside a Begin()/End() block. 'next window' = next window we will Begin() into.\n    IMGUI_API bool          IsWindowAppearing();\n    IMGUI_API bool          IsWindowCollapsed();\n    IMGUI_API bool          IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options.\n    IMGUI_API bool          IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options. IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! Refer to FAQ entry \"How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?\" for details.\n    IMGUI_API ImDrawList*   GetWindowDrawList();                        // get draw list associated to the current window, to append your own drawing primitives\n    IMGUI_API float         GetWindowDpiScale();                        // get DPI scale currently associated to the current window's viewport.\n    IMGUI_API ImVec2        GetWindowPos();                             // get current window position in screen space (IT IS UNLIKELY YOU EVER NEED TO USE THIS. Consider always using GetCursorScreenPos() and GetContentRegionAvail() instead)\n    IMGUI_API ImVec2        GetWindowSize();                            // get current window size (IT IS UNLIKELY YOU EVER NEED TO USE THIS. Consider always using GetCursorScreenPos() and GetContentRegionAvail() instead)\n    IMGUI_API float         GetWindowWidth();                           // get current window width (IT IS UNLIKELY YOU EVER NEED TO USE THIS). Shortcut for GetWindowSize().x.\n    IMGUI_API float         GetWindowHeight();                          // get current window height (IT IS UNLIKELY YOU EVER NEED TO USE THIS). Shortcut for GetWindowSize().y.\n    IMGUI_API ImGuiViewport*GetWindowViewport();                        // get viewport currently associated to the current window.\n\n    // Window manipulation\n    // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin).\n    IMGUI_API void          SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc.\n    IMGUI_API void          SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0);                  // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin()\n    IMGUI_API void          SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use 0.0f or FLT_MAX if you don't want limits. Use -1 for both min and max of same axis to preserve current size (which itself is a constraint). Use callback to apply non-trivial programmatic constraints.\n    IMGUI_API void          SetNextWindowContentSize(const ImVec2& size);                               // set next window content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding. set an axis to 0.0f to leave it automatic. call before Begin()\n    IMGUI_API void          SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0);                 // set next window collapsed state. call before Begin()\n    IMGUI_API void          SetNextWindowFocus();                                                       // set next window to be focused / top-most. call before Begin()\n    IMGUI_API void          SetNextWindowScroll(const ImVec2& scroll);                                  // set next window scrolling value (use < 0.0f to not affect a given axis).\n    IMGUI_API void          SetNextWindowBgAlpha(float alpha);                                          // set next window background color alpha. helper to easily override the Alpha component of ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground.\n    IMGUI_API void          SetNextWindowViewport(ImGuiID viewport_id);                                 // set next window viewport\n    IMGUI_API void          SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0);                        // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects.\n    IMGUI_API void          SetWindowSize(const ImVec2& size, ImGuiCond cond = 0);                      // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0, 0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects.\n    IMGUI_API void          SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0);                     // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed().\n    IMGUI_API void          SetWindowFocus();                                                           // (not recommended) set current window to be focused / top-most. prefer using SetNextWindowFocus().\n    IMGUI_API void          SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0);      // set named window position.\n    IMGUI_API void          SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0);    // set named window size. set axis to 0.0f to force an auto-fit on this axis.\n    IMGUI_API void          SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0);   // set named window collapsed state\n    IMGUI_API void          SetWindowFocus(const char* name);                                           // set named window to be focused / top-most. use NULL to remove focus.\n\n    // Windows Scrolling\n    // - Any change of Scroll will be applied at the beginning of next frame in the first call to Begin().\n    // - You may instead use SetNextWindowScroll() prior to calling Begin() to avoid this delay, as an alternative to using SetScrollX()/SetScrollY().\n    IMGUI_API float         GetScrollX();                                                   // get scrolling amount [0 .. GetScrollMaxX()]\n    IMGUI_API float         GetScrollY();                                                   // get scrolling amount [0 .. GetScrollMaxY()]\n    IMGUI_API void          SetScrollX(float scroll_x);                                     // set scrolling amount [0 .. GetScrollMaxX()]\n    IMGUI_API void          SetScrollY(float scroll_y);                                     // set scrolling amount [0 .. GetScrollMaxY()]\n    IMGUI_API float         GetScrollMaxX();                                                // get maximum scrolling amount ~~ ContentSize.x - WindowSize.x - DecorationsSize.x\n    IMGUI_API float         GetScrollMaxY();                                                // get maximum scrolling amount ~~ ContentSize.y - WindowSize.y - DecorationsSize.y\n    IMGUI_API void          SetScrollHereX(float center_x_ratio = 0.5f);                    // adjust scrolling amount to make current cursor position visible. center_x_ratio=0.0: left, 0.5: center, 1.0: right. When using to make a \"default/current item\" visible, consider using SetItemDefaultFocus() instead.\n    IMGUI_API void          SetScrollHereY(float center_y_ratio = 0.5f);                    // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a \"default/current item\" visible, consider using SetItemDefaultFocus() instead.\n    IMGUI_API void          SetScrollFromPosX(float local_x, float center_x_ratio = 0.5f);  // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position.\n    IMGUI_API void          SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f);  // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position.\n\n    // Parameters stacks (font)\n    //  - PushFont(font, 0.0f)                       // Change font and keep current size\n    //  - PushFont(NULL, 20.0f)                      // Keep font and change current size\n    //  - PushFont(font, 20.0f)                      // Change font and set size to 20.0f\n    //  - PushFont(font, style.FontSizeBase * 2.0f)  // Change font and set size to be twice bigger than current size.\n    //  - PushFont(font, font->LegacySize)           // Change font and set size to size passed to AddFontXXX() function. Same as pre-1.92 behavior.\n    // *IMPORTANT* before 1.92, fonts had a single size. They can now be dynamically be adjusted.\n    //  - In 1.92 we have REMOVED the single parameter version of PushFont() because it seems like the easiest way to provide an error-proof transition.\n    //  - PushFont(font) before 1.92 = PushFont(font, font->LegacySize) after 1.92          // Use default font size as passed to AddFontXXX() function.\n    // *IMPORTANT* global scale factors are applied over the provided size.\n    //  - Global scale factors are: 'style.FontScaleMain', 'style.FontScaleDpi' and maybe more.\n    // -  If you want to apply a factor to the _current_ font size:\n    //  - CORRECT:   PushFont(NULL, style.FontSizeBase)         // use current unscaled size    == does nothing\n    //  - CORRECT:   PushFont(NULL, style.FontSizeBase * 2.0f)  // use current unscaled size x2 == make text twice bigger\n    //  - INCORRECT: PushFont(NULL, GetFontSize())              // INCORRECT! using size after global factors already applied == GLOBAL SCALING FACTORS WILL APPLY TWICE!\n    //  - INCORRECT: PushFont(NULL, GetFontSize() * 2.0f)       // INCORRECT! using size after global factors already applied == GLOBAL SCALING FACTORS WILL APPLY TWICE!\n    IMGUI_API void          PushFont(ImFont* font, float font_size_base_unscaled);          // Use NULL as a shortcut to keep current font. Use 0.0f to keep current size.\n    IMGUI_API void          PopFont();\n    IMGUI_API ImFont*       GetFont();                                                      // get current font\n    IMGUI_API float         GetFontSize();                                                  // get current scaled font size (= height in pixels). AFTER global scale factors applied. *IMPORTANT* DO NOT PASS THIS VALUE TO PushFont()! Use ImGui::GetStyle().FontSizeBase to get value before global scale factors.\n    IMGUI_API ImFontBaked*  GetFontBaked();                                                 // get current font bound at current size // == GetFont()->GetFontBaked(GetFontSize())\n\n    // Parameters stacks (shared)\n    IMGUI_API void          PushStyleColor(ImGuiCol idx, ImU32 col);                        // modify a style color. always use this if you modify the style after NewFrame().\n    IMGUI_API void          PushStyleColor(ImGuiCol idx, const ImVec4& col);\n    IMGUI_API void          PopStyleColor(int count = 1);\n    IMGUI_API void          PushStyleVar(ImGuiStyleVar idx, float val);                     // modify a style float variable. always use this if you modify the style after NewFrame()!\n    IMGUI_API void          PushStyleVar(ImGuiStyleVar idx, const ImVec2& val);             // modify a style ImVec2 variable. \"\n    IMGUI_API void          PushStyleVarX(ImGuiStyleVar idx, float val_x);                  // modify X component of a style ImVec2 variable. \"\n    IMGUI_API void          PushStyleVarY(ImGuiStyleVar idx, float val_y);                  // modify Y component of a style ImVec2 variable. \"\n    IMGUI_API void          PopStyleVar(int count = 1);\n    IMGUI_API void          PushItemFlag(ImGuiItemFlags option, bool enabled);              // modify specified shared item flag, e.g. PushItemFlag(ImGuiItemFlags_NoTabStop, true)\n    IMGUI_API void          PopItemFlag();\n\n    // Parameters stacks (current window)\n    IMGUI_API void          PushItemWidth(float item_width);                                // push width of items for common large \"item+label\" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side).\n    IMGUI_API void          PopItemWidth();\n    IMGUI_API void          SetNextItemWidth(float item_width);                             // set width of the _next_ common large \"item+label\" widget. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side)\n    IMGUI_API float         CalcItemWidth();                                                // width of item given pushed settings and current cursor position. NOT necessarily the width of last item unlike most 'Item' functions.\n    IMGUI_API void          PushTextWrapPos(float wrap_local_pos_x = 0.0f);                 // push word-wrapping position for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space\n    IMGUI_API void          PopTextWrapPos();\n\n    // Style read access\n    // - Use the ShowStyleEditor() function to interactively see/edit the colors.\n    IMGUI_API ImVec2        GetFontTexUvWhitePixel();                                       // get UV coordinate for a white pixel, useful to draw custom shapes via the ImDrawList API\n    IMGUI_API ImU32         GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f);              // retrieve given style color with style alpha applied and optional extra alpha multiplier, packed as a 32-bit value suitable for ImDrawList\n    IMGUI_API ImU32         GetColorU32(const ImVec4& col);                                 // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList\n    IMGUI_API ImU32         GetColorU32(ImU32 col, float alpha_mul = 1.0f);                 // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList\n    IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx);                                // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in.\n\n    // Layout cursor positioning\n    // - By \"cursor\" we mean the current output position.\n    // - The typical widget behavior is to output themselves at the current cursor position, then move the cursor one line down.\n    // - You can call SameLine() between widgets to undo the last carriage return and output at the right of the preceding widget.\n    // - YOU CAN DO 99% OF WHAT YOU NEED WITH ONLY GetCursorScreenPos() and GetContentRegionAvail().\n    // - Attention! We currently have inconsistencies between window-local and absolute positions we will aim to fix with future API:\n    //    - Absolute coordinate:        GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. -> this is the preferred way forward.\n    //    - Window-local coordinates:   SameLine(offset), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), PushTextWrapPos()\n    //    - Window-local coordinates:   GetContentRegionMax(), GetWindowContentRegionMin(), GetWindowContentRegionMax() --> all obsoleted. YOU DON'T NEED THEM.\n    // - GetCursorScreenPos() = GetCursorPos() + GetWindowPos(). GetWindowPos() is almost only ever useful to convert from window-local to absolute coordinates. Try not to use it.\n    IMGUI_API ImVec2        GetCursorScreenPos();                                           // cursor position, absolute coordinates. THIS IS YOUR BEST FRIEND (prefer using this rather than GetCursorPos(), also more useful to work with ImDrawList API).\n    IMGUI_API void          SetCursorScreenPos(const ImVec2& pos);                          // cursor position, absolute coordinates. THIS IS YOUR BEST FRIEND.\n    IMGUI_API ImVec2        GetContentRegionAvail();                                        // available space from current position. THIS IS YOUR BEST FRIEND.\n    IMGUI_API ImVec2        GetCursorPos();                                                 // [window-local] cursor position in window-local coordinates. This is not your best friend.\n    IMGUI_API float         GetCursorPosX();                                                // [window-local] \"\n    IMGUI_API float         GetCursorPosY();                                                // [window-local] \"\n    IMGUI_API void          SetCursorPos(const ImVec2& local_pos);                          // [window-local] \"\n    IMGUI_API void          SetCursorPosX(float local_x);                                   // [window-local] \"\n    IMGUI_API void          SetCursorPosY(float local_y);                                   // [window-local] \"\n    IMGUI_API ImVec2        GetCursorStartPos();                                            // [window-local] initial cursor position, in window-local coordinates. Call GetCursorScreenPos() after Begin() to get the absolute coordinates version.\n\n    // Other layout functions\n    IMGUI_API void          Separator();                                                    // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator.\n    IMGUI_API void          SameLine(float offset_from_start_x=0.0f, float spacing=-1.0f);  // call between widgets or groups to layout them horizontally. X position given in window coordinates.\n    IMGUI_API void          NewLine();                                                      // undo a SameLine() or force a new line when in a horizontal-layout context.\n    IMGUI_API void          Spacing();                                                      // add vertical spacing.\n    IMGUI_API void          Dummy(const ImVec2& size);                                      // add a dummy item of given size. unlike InvisibleButton(), Dummy() won't take the mouse click or be navigable into.\n    IMGUI_API void          Indent(float indent_w = 0.0f);                                  // move content position toward the right, by indent_w, or style.IndentSpacing if indent_w <= 0\n    IMGUI_API void          Unindent(float indent_w = 0.0f);                                // move content position back to the left, by indent_w, or style.IndentSpacing if indent_w <= 0\n    IMGUI_API void          BeginGroup();                                                   // lock horizontal starting position\n    IMGUI_API void          EndGroup();                                                     // unlock horizontal starting position + capture the whole group bounding box into one \"item\" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)\n    IMGUI_API void          AlignTextToFramePadding();                                      // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item)\n    IMGUI_API float         GetTextLineHeight();                                            // ~ FontSize\n    IMGUI_API float         GetTextLineHeightWithSpacing();                                 // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text)\n    IMGUI_API float         GetFrameHeight();                                               // ~ FontSize + style.FramePadding.y * 2\n    IMGUI_API float         GetFrameHeightWithSpacing();                                    // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets)\n\n    // ID stack/scopes\n    // Read the FAQ (docs/FAQ.md or http://dearimgui.com/faq) for more details about how ID are handled in dear imgui.\n    // - Those questions are answered and impacted by understanding of the ID stack system:\n    //   - \"Q: Why is my widget not reacting when I click on it?\"\n    //   - \"Q: How can I have widgets with an empty label?\"\n    //   - \"Q: How can I have multiple widgets with the same label?\"\n    // - Short version: ID are hashes of the entire ID stack. If you are creating widgets in a loop you most likely\n    //   want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them.\n    // - You can also use the \"Label##foobar\" syntax within widget label to distinguish them from each others.\n    // - In this header file we use the \"label\"/\"name\" terminology to denote a string that will be displayed + used as an ID,\n    //   whereas \"str_id\" denote a string that is only used as an ID and not normally displayed.\n    IMGUI_API void          PushID(const char* str_id);                                     // push string into the ID stack (will hash string).\n    IMGUI_API void          PushID(const char* str_id_begin, const char* str_id_end);       // push string into the ID stack (will hash string).\n    IMGUI_API void          PushID(const void* ptr_id);                                     // push pointer into the ID stack (will hash pointer).\n    IMGUI_API void          PushID(int int_id);                                             // push integer into the ID stack (will hash integer).\n    IMGUI_API void          PopID();                                                        // pop from the ID stack.\n    IMGUI_API ImGuiID       GetID(const char* str_id);                                      // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself\n    IMGUI_API ImGuiID       GetID(const char* str_id_begin, const char* str_id_end);\n    IMGUI_API ImGuiID       GetID(const void* ptr_id);\n    IMGUI_API ImGuiID       GetID(int int_id);\n\n    // Widgets: Text\n    IMGUI_API void          TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text(\"%s\", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text.\n    IMGUI_API void          Text(const char* fmt, ...)                                      IM_FMTARGS(1); // formatted text\n    IMGUI_API void          TextV(const char* fmt, va_list args)                            IM_FMTLIST(1);\n    IMGUI_API void          TextColored(const ImVec4& col, const char* fmt, ...)            IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor();\n    IMGUI_API void          TextColoredV(const ImVec4& col, const char* fmt, va_list args)  IM_FMTLIST(2);\n    IMGUI_API void          TextDisabled(const char* fmt, ...)                              IM_FMTARGS(1); // shortcut for PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); Text(fmt, ...); PopStyleColor();\n    IMGUI_API void          TextDisabledV(const char* fmt, va_list args)                    IM_FMTLIST(1);\n    IMGUI_API void          TextWrapped(const char* fmt, ...)                               IM_FMTARGS(1); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();. Note that this won't work on an auto-resizing window if there's no other widgets to extend the window width, yoy may need to set a size using SetNextWindowSize().\n    IMGUI_API void          TextWrappedV(const char* fmt, va_list args)                     IM_FMTLIST(1);\n    IMGUI_API void          LabelText(const char* label, const char* fmt, ...)              IM_FMTARGS(2); // display text+label aligned the same way as value+label widgets\n    IMGUI_API void          LabelTextV(const char* label, const char* fmt, va_list args)    IM_FMTLIST(2);\n    IMGUI_API void          BulletText(const char* fmt, ...)                                IM_FMTARGS(1); // shortcut for Bullet()+Text()\n    IMGUI_API void          BulletTextV(const char* fmt, va_list args)                      IM_FMTLIST(1);\n    IMGUI_API void          SeparatorText(const char* label);                               // currently: formatted text with a horizontal line\n\n    // Widgets: Main\n    // - Most widgets return true when the value has been changed or when pressed/selected\n    // - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state.\n    IMGUI_API bool          Button(const char* label, const ImVec2& size = ImVec2(0, 0));   // button\n    IMGUI_API bool          SmallButton(const char* label);                                 // button with (FramePadding.y == 0) to easily embed within text\n    IMGUI_API bool          InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.)\n    IMGUI_API bool          ArrowButton(const char* str_id, ImGuiDir dir);                  // square button with an arrow shape\n    IMGUI_API bool          Checkbox(const char* label, bool* v);\n    IMGUI_API bool          CheckboxFlags(const char* label, int* flags, int flags_value);\n    IMGUI_API bool          CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value);\n    IMGUI_API bool          RadioButton(const char* label, bool active);                    // use with e.g. if (RadioButton(\"one\", my_value==1)) { my_value = 1; }\n    IMGUI_API bool          RadioButton(const char* label, int* v, int v_button);           // shortcut to handle the above pattern when value is an integer\n    IMGUI_API void          ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-FLT_MIN, 0), const char* overlay = NULL);\n    IMGUI_API void          Bullet();                                                       // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses\n    IMGUI_API bool          TextLink(const char* label);                                    // hyperlink text button, return true when clicked\n    IMGUI_API bool          TextLinkOpenURL(const char* label, const char* url = NULL);     // hyperlink text button, automatically open file/url when clicked\n\n    // Widgets: Images\n    // - Read about ImTextureID/ImTextureRef  here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples\n    // - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.\n    // - Image() pads adds style.ImageBorderSize on each side, ImageButton() adds style.FramePadding on each side.\n    // - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified.\n    // - An obsolete version of Image(), before 1.91.9 (March 2025), had a 'tint_col' parameter which is now supported by the ImageWithBg() function.\n    IMGUI_API void          Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1));\n    IMGUI_API void          ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1));\n    IMGUI_API bool          ImageButton(const char* str_id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1));\n\n    // Widgets: Combo Box (Dropdown)\n    // - The BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items.\n    // - The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. This is analogous to how ListBox are created.\n    IMGUI_API bool          BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0);\n    IMGUI_API void          EndCombo(); // only call EndCombo() if BeginCombo() returns true!\n    IMGUI_API bool          Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1);\n    IMGUI_API bool          Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1);      // Separate items with \\0 within a string, end item-list with \\0\\0. e.g. \"One\\0Two\\0Three\\0\"\n    IMGUI_API bool          Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items = -1);\n\n    // Widgets: Drag Sliders\n    // - Ctrl+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.\n    // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every function, note that a 'float v[X]' function argument is the same as 'float* v',\n    //   the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x\n    // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. \"%.3f\" -> 1.234; \"%5.2f secs\" -> 01.23 secs; \"Biscuit: %.0f\" -> Biscuit: 1; etc.\n    // - Format string may also be set to NULL or use the default format (\"%f\" or \"%d\").\n    // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For keyboard/gamepad navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision).\n    // - Use v_min < v_max to clamp edits to given limits. Note that Ctrl+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used.\n    // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum.\n    // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.\n    // - Legacy: Pre-1.78 there are DragXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.\n    //   If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361\n    IMGUI_API bool          DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);     // If v_min >= v_max we have no bound\n    IMGUI_API bool          DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = \"%.3f\", const char* format_max = NULL, ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = \"%d\", ImGuiSliderFlags flags = 0);  // If v_min >= v_max we have no bound\n    IMGUI_API bool          DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = \"%d\", const char* format_max = NULL, ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);\n\n    // Widgets: Regular Sliders\n    // - Ctrl+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.\n    // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. \"%.3f\" -> 1.234; \"%5.2f secs\" -> 01.23 secs; \"Biscuit: %.0f\" -> Biscuit: 1; etc.\n    // - Format string may also be set to NULL or use the default format (\"%f\" or \"%d\").\n    // - Legacy: Pre-1.78 there are SliderXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.\n    //   If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361\n    IMGUI_API bool          SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);     // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display.\n    IMGUI_API bool          SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f, const char* format = \"%.0f deg\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);\n\n    // Widgets: Input with Keyboard\n    // - If you want to use InputText() with std::string or any custom dynamic string type, use the wrapper in misc/cpp/imgui_stdlib.h/.cpp!\n    // - Most of the ImGuiInputTextFlags flags are only useful for InputText() and not for InputFloatX, InputIntX, InputDouble etc.\n    IMGUI_API bool          InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);\n    IMGUI_API bool          InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);\n    IMGUI_API bool          InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);\n    IMGUI_API bool          InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = \"%.3f\", ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputFloat2(const char* label, float v[2], const char* format = \"%.3f\", ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputFloat3(const char* label, float v[3], const char* format = \"%.3f\", ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputFloat4(const char* label, float v[4], const char* format = \"%.3f\", ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = \"%.6f\", ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0);\n\n    // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little color square that can be left-clicked to open a picker, and right-clicked to open an option menu.)\n    // - Note that in C++ a 'float v[X]' function argument is the _same_ as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible.\n    // - You can pass the address of a first float element out of a contiguous structure, e.g. &myvector.x\n    IMGUI_API bool          ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);\n    IMGUI_API bool          ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0);\n    IMGUI_API bool          ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);\n    IMGUI_API bool          ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL);\n    IMGUI_API bool          ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed.\n    IMGUI_API void          SetColorEditOptions(ImGuiColorEditFlags flags);                     // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls.\n\n    // Widgets: Trees\n    // - TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents.\n    IMGUI_API bool          TreeNode(const char* label);\n    IMGUI_API bool          TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2);   // helper variation to easily decorrelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet().\n    IMGUI_API bool          TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2);   // \"\n    IMGUI_API bool          TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2);\n    IMGUI_API bool          TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2);\n    IMGUI_API bool          TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags = 0);\n    IMGUI_API bool          TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3);\n    IMGUI_API bool          TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3);\n    IMGUI_API bool          TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3);\n    IMGUI_API bool          TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3);\n    IMGUI_API void          TreePush(const char* str_id);                                       // ~ Indent()+PushID(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired.\n    IMGUI_API void          TreePush(const void* ptr_id);                                       // \"\n    IMGUI_API void          TreePop();                                                          // ~ Unindent()+PopID()\n    IMGUI_API float         GetTreeNodeToLabelSpacing();                                        // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode\n    IMGUI_API bool          CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0);  // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop().\n    IMGUI_API bool          CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags = 0); // when 'p_visible != NULL': if '*p_visible==true' display an additional small close button on upper right of the header which will set the bool to false when clicked, if '*p_visible==false' don't display the header.\n    IMGUI_API void          SetNextItemOpen(bool is_open, ImGuiCond cond = 0);                  // set next TreeNode/CollapsingHeader open state.\n    IMGUI_API void          SetNextItemStorageID(ImGuiID storage_id);                           // set id to use for open/close storage (default to same as item id).\n    IMGUI_API bool          TreeNodeGetOpen(ImGuiID storage_id);                                // retrieve tree node open/close state.\n\n    // Widgets: Selectables\n    // - A selectable highlights when hovered, and can display another color when selected.\n    // - Neighbors selectable extend their highlight bounds in order to leave no gap between them. This is so a series of selected Selectable appear contiguous.\n    IMGUI_API bool          Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // \"bool selected\" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height\n    IMGUI_API bool          Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0));      // \"bool* p_selected\" point to the selection state (read-write), as a convenient helper.\n\n    // Multi-selection system for Selectable(), Checkbox(), TreeNode() functions [BETA]\n    // - This enables standard multi-selection/range-selection idioms (Ctrl+Mouse/Keyboard, Shift+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used.\n    // - ImGuiSelectionUserData is often used to store your item index within the current view (but may store something else).\n    // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo.\n    // - TreeNode() is technically supported but... using this correctly is more complicated. You need some sort of linear/random access to your tree,\n    //   which is suited to advanced trees setups already implementing filters and clipper. We will work simplifying the current demo.\n    // - 'selection_size' and 'items_count' parameters are optional and used by a few features. If they are costly for you to compute, you may avoid them.\n    IMGUI_API ImGuiMultiSelectIO*   BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size = -1, int items_count = -1);\n    IMGUI_API ImGuiMultiSelectIO*   EndMultiSelect();\n    IMGUI_API void                  SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data);\n    IMGUI_API bool                  IsItemToggledSelection();                                   // Was the last item selection state toggled? Useful if you need the per-item information _before_ reaching EndMultiSelect(). We only returns toggle _event_ in order to handle clipping correctly.\n\n    // Widgets: List Boxes\n    // - This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.\n    // - If you don't need a label you can probably simply use BeginChild() with the ImGuiChildFlags_FrameStyle flag for the same result.\n    // - You can submit contents and manage your selection state however you want it, by creating e.g. Selectable() or any other items.\n    // - The simplified/old ListBox() api are helpers over BeginListBox()/EndListBox() which are kept available for convenience purpose. This is analogous to how Combos are created.\n    // - Choose frame width:   size.x > 0.0f: custom  /  size.x < 0.0f or -FLT_MIN: right-align   /  size.x = 0.0f (default): use current ItemWidth\n    // - Choose frame height:  size.y > 0.0f: custom  /  size.y < 0.0f or -FLT_MIN: bottom-align  /  size.y = 0.0f (default): arbitrary default height which can fit ~7 items\n    IMGUI_API bool          BeginListBox(const char* label, const ImVec2& size = ImVec2(0, 0)); // open a framed scrolling region\n    IMGUI_API void          EndListBox();                                                       // only call EndListBox() if BeginListBox() returned true!\n    IMGUI_API bool          ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1);\n    IMGUI_API bool          ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items = -1);\n\n    // Widgets: Data Plotting\n    // - Consider using ImPlot (https://github.com/epezent/implot) which is much better!\n    IMGUI_API void          PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));\n    IMGUI_API void          PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0));\n    IMGUI_API void          PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));\n    IMGUI_API void          PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0));\n\n    // Widgets: Value() Helpers.\n    // - Those are merely shortcut to calling Text() with a format string. Output single value in \"name: value\" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace)\n    IMGUI_API void          Value(const char* prefix, bool b);\n    IMGUI_API void          Value(const char* prefix, int v);\n    IMGUI_API void          Value(const char* prefix, unsigned int v);\n    IMGUI_API void          Value(const char* prefix, float v, const char* float_format = NULL);\n\n    // Widgets: Menus\n    // - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar.\n    // - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it.\n    // - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it.\n    // - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment.\n    IMGUI_API bool          BeginMenuBar();                                                     // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window).\n    IMGUI_API void          EndMenuBar();                                                       // only call EndMenuBar() if BeginMenuBar() returns true!\n    IMGUI_API bool          BeginMainMenuBar();                                                 // create and append to a full screen menu-bar.\n    IMGUI_API void          EndMainMenuBar();                                                   // only call EndMainMenuBar() if BeginMainMenuBar() returns true!\n    IMGUI_API bool          BeginMenu(const char* label, bool enabled = true);                  // create a sub-menu entry. only call EndMenu() if this returns true!\n    IMGUI_API void          EndMenu();                                                          // only call EndMenu() if BeginMenu() returns true!\n    IMGUI_API bool          MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true);  // return true when activated.\n    IMGUI_API bool          MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true);              // return true when activated + toggle (*p_selected) if p_selected != NULL\n\n    // Tooltips\n    // - Tooltips are windows following the mouse. They do not take focus away.\n    // - A tooltip window can contain items of any types.\n    // - SetTooltip() is more or less a shortcut for the 'if (BeginTooltip()) { Text(...); EndTooltip(); }' idiom (with a subtlety that it discard any previously submitted tooltip)\n    IMGUI_API bool          BeginTooltip();                                                     // begin/append a tooltip window.\n    IMGUI_API void          EndTooltip();                                                       // only call EndTooltip() if BeginTooltip()/BeginItemTooltip() returns true!\n    IMGUI_API void          SetTooltip(const char* fmt, ...) IM_FMTARGS(1);                     // set a text-only tooltip. Often used after a ImGui::IsItemHovered() check. Override any previous call to SetTooltip().\n    IMGUI_API void          SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1);\n\n    // Tooltips: helpers for showing a tooltip when hovering an item\n    // - BeginItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip) && BeginTooltip())' idiom.\n    // - SetItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) { SetTooltip(...); }' idiom.\n    // - Where 'ImGuiHoveredFlags_ForTooltip' itself is a shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on active input type. For mouse it defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'.\n    IMGUI_API bool          BeginItemTooltip();                                                 // begin/append a tooltip window if preceding item was hovered.\n    IMGUI_API void          SetItemTooltip(const char* fmt, ...) IM_FMTARGS(1);                 // set a text-only tooltip if preceding item was hovered. override any previous call to SetTooltip().\n    IMGUI_API void          SetItemTooltipV(const char* fmt, va_list args) IM_FMTLIST(1);\n\n    // Popups, Modals\n    //  - They block normal mouse hovering detection (and therefore most mouse interactions) behind them.\n    //  - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE.\n    //  - Their visibility state (~bool) is held internally instead of being held by the programmer as we are used to with regular Begin*() calls.\n    //  - The 3 properties above are related: we need to retain popup visibility state in the library because popups may be closed as any time.\n    //  - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered().\n    //  - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack.\n    //    This is sometimes leading to confusing mistakes. May rework this in the future.\n    //  - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards if returned true. ImGuiWindowFlags are forwarded to the window.\n    //  - BeginPopupModal(): block every interaction behind the window, cannot be closed by user, add a dimming background, has a title bar.\n    IMGUI_API bool          BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0);                         // return true if the popup is open, and you can start outputting to it.\n    IMGUI_API bool          BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it.\n    IMGUI_API void          EndPopup();                                                                         // only call EndPopup() if BeginPopupXXX() returns true!\n\n    // Popups: open/close functions\n    //  - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options.\n    //  - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE.\n    //  - CloseCurrentPopup(): use inside the BeginPopup()/EndPopup() scope to close manually.\n    //  - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options).\n    //  - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup().\n    //  - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened.\n    IMGUI_API void          OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0);                     // call to mark popup as open (don't call every frame!).\n    IMGUI_API void          OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0);                             // id overload to facilitate calling from nested stacks\n    IMGUI_API void          OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0);   // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors)\n    IMGUI_API void          CloseCurrentPopup();                                                                // manually close the popup we have begin-ed into.\n\n    // Popups: Open+Begin popup combined functions helpers to create context menus.\n    //  - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking.\n    //  - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future.\n    //  - IMPORTANT: If you ever used the left mouse button with BeginPopupContextXXX() helpers before 1.92.6:\n    //    - Before this version, OpenPopupOnItemClick(), BeginPopupContextItem(), BeginPopupContextWindow(), BeginPopupContextVoid() had 'a ImGuiPopupFlags popup_flags = 1' default value in their function signature.\n    //    - Before: Explicitly passing a literal 0 meant ImGuiPopupFlags_MouseButtonLeft. The default = 1 meant ImGuiPopupFlags_MouseButtonRight.\n    //    - After: The default = 0 means ImGuiPopupFlags_MouseButtonRight. Explicitly passing a literal 1 also means ImGuiPopupFlags_MouseButtonRight (if legacy behavior are enabled) or will assert (if legacy behavior are disabled).\n    //    - TL;DR: if you don't want to use right mouse button for popups, always specify it explicitly using a named ImGuiPopupFlags_MouseButtonXXXX value.\n    //    - Read \"API BREAKING CHANGES\" 2026/01/07 (1.92.6) entry in imgui.cpp or GitHub topic #9157 for all details.\n    IMGUI_API bool          BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0);  // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp!\n    IMGUI_API bool          BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0);// open+begin popup when clicked on current window.\n    IMGUI_API bool          BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0);  // open+begin popup when clicked in void (where there are no windows).\n\n    // Popups: query functions\n    //  - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack.\n    //  - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack.\n    //  - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId + ImGuiPopupFlags_AnyPopupLevel: return true if any popup is open.\n    IMGUI_API bool          IsPopupOpen(const char* str_id, ImGuiPopupFlags flags = 0);                         // return true if the popup is open.\n\n    // Tables\n    // - Full-featured replacement for old Columns API.\n    // - See Demo->Tables for demo code. See top of imgui_tables.cpp for general commentary.\n    // - See ImGuiTableFlags_ and ImGuiTableColumnFlags_ enums for a description of available flags.\n    // The typical call flow is:\n    // - 1. Call BeginTable(), early out if returning false.\n    // - 2. Optionally call TableSetupColumn() to submit column name/flags/defaults.\n    // - 3. Optionally call TableSetupScrollFreeze() to request scroll freezing of columns/rows.\n    // - 4. Optionally call TableHeadersRow() to submit a header row. Names are pulled from TableSetupColumn() data.\n    // - 5. Populate contents:\n    //    - In most situations you can use TableNextRow() + TableSetColumnIndex(N) to start appending into a column.\n    //    - If you are using tables as a sort of grid, where every column is holding the same type of contents,\n    //      you may prefer using TableNextColumn() instead of TableNextRow() + TableSetColumnIndex().\n    //      TableNextColumn() will automatically wrap-around into the next row if needed.\n    //    - IMPORTANT: Comparatively to the old Columns() API, we need to call TableNextColumn() for the first column!\n    //    - Summary of possible call flow:\n    //        - TableNextRow() -> TableSetColumnIndex(0) -> Text(\"Hello 0\") -> TableSetColumnIndex(1) -> Text(\"Hello 1\")  // OK\n    //        - TableNextRow() -> TableNextColumn()      -> Text(\"Hello 0\") -> TableNextColumn()      -> Text(\"Hello 1\")  // OK\n    //        -                   TableNextColumn()      -> Text(\"Hello 0\") -> TableNextColumn()      -> Text(\"Hello 1\")  // OK: TableNextColumn() automatically gets to next row!\n    //        - TableNextRow()                           -> Text(\"Hello 0\")                                               // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear!\n    // - 5. Call EndTable()\n    IMGUI_API bool          BeginTable(const char* str_id, int columns, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f);\n    IMGUI_API void          EndTable();                                         // only call EndTable() if BeginTable() returns true!\n    IMGUI_API void          TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. 'min_row_height' include the minimum top and bottom padding aka CellPadding.y * 2.0f.\n    IMGUI_API bool          TableNextColumn();                                  // append into the next column (or first column of next row if currently in last column). Return true when column is visible.\n    IMGUI_API bool          TableSetColumnIndex(int column_n);                  // append into the specified column. Return true when column is visible.\n\n    // Tables: Headers & Columns declaration\n    // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc.\n    // - Use TableHeadersRow() to create a header row and automatically submit a TableHeader() for each column.\n    //   Headers are required to perform: reordering, sorting, and opening the context menu.\n    //   The context menu can also be made available in columns body using ImGuiTableFlags_ContextMenuInBody.\n    // - You may manually submit headers using TableNextRow() + TableHeader() calls, but this is only useful in\n    //   some advanced use cases (e.g. adding custom widgets in header row).\n    // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled.\n    IMGUI_API void          TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0);\n    IMGUI_API void          TableSetupScrollFreeze(int cols, int rows);         // lock columns/rows so they stay visible when scrolled.\n    IMGUI_API void          TableHeader(const char* label);                     // submit one header cell manually (rarely used)\n    IMGUI_API void          TableHeadersRow();                                  // submit a row with headers cells based on data provided to TableSetupColumn() + submit context menu\n    IMGUI_API void          TableAngledHeadersRow();                            // submit a row with angled headers for every column with the ImGuiTableColumnFlags_AngledHeader flag. MUST BE FIRST ROW.\n\n    // Tables: Sorting & Miscellaneous functions\n    // - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting.\n    //   When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have\n    //   changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting,\n    //   else you may wastefully sort your data every frame!\n    // - Functions args 'int column_n' treat the default value of -1 as the same as passing the current column index.\n    IMGUI_API ImGuiTableSortSpecs*  TableGetSortSpecs();                        // get latest sort specs for the table (NULL if not sorting).  Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable().\n    IMGUI_API int                   TableGetColumnCount();                      // return number of columns (value passed to BeginTable)\n    IMGUI_API int                   TableGetColumnIndex();                      // return current column index.\n    IMGUI_API int                   TableGetRowIndex();                         // return current row index (header rows are accounted for)\n    IMGUI_API const char*           TableGetColumnName(int column_n = -1);      // return \"\" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column.\n    IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1);     // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column.\n    IMGUI_API void                  TableSetColumnEnabled(int column_n, bool v);// change user accessible enabled/disabled state of a column. Set to false to hide the column. User can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody)\n    IMGUI_API int                   TableGetHoveredColumn();                    // return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. Can also use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead.\n    IMGUI_API void                  TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n = -1);  // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details.\n\n    // Legacy Columns API (prefer using Tables!)\n    // - You can also use SameLine(pos_x) to mimic simplified columns.\n    IMGUI_API void          Columns(int count = 1, const char* id = NULL, bool borders = true);\n    IMGUI_API void          NextColumn();                                                       // next column, defaults to current row or next row if the current row is finished\n    IMGUI_API int           GetColumnIndex();                                                   // get current column index\n    IMGUI_API float         GetColumnWidth(int column_index = -1);                              // get column width (in pixels). pass -1 to use current column\n    IMGUI_API void          SetColumnWidth(int column_index, float width);                      // set column width (in pixels). pass -1 to use current column\n    IMGUI_API float         GetColumnOffset(int column_index = -1);                             // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetColumnsCount() inclusive. column 0 is typically 0.0f\n    IMGUI_API void          SetColumnOffset(int column_index, float offset_x);                  // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column\n    IMGUI_API int           GetColumnsCount();\n\n    // Tab Bars, Tabs\n    // - Note: Tabs are automatically created by the docking system (when in 'docking' branch). Use this to create tab bars/tabs yourself.\n    IMGUI_API bool          BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0);        // create and append into a TabBar\n    IMGUI_API void          EndTabBar();                                                        // only call EndTabBar() if BeginTabBar() returns true!\n    IMGUI_API bool          BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0); // create a Tab. Returns true if the Tab is selected.\n    IMGUI_API void          EndTabItem();                                                       // only call EndTabItem() if BeginTabItem() returns true!\n    IMGUI_API bool          TabItemButton(const char* label, ImGuiTabItemFlags flags = 0);      // create a Tab behaving like a button. return true when clicked. cannot be selected in the tab bar.\n    IMGUI_API void          SetTabItemClosed(const char* tab_or_docked_window_label);           // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name.\n\n    // Docking\n    // - Read https://github.com/ocornut/imgui/wiki/Docking for details.\n    // - Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable.\n    // - You can use many Docking facilities without calling any API.\n    //   - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n    //   - Drag from window menu button (upper-left button) to undock an entire node (all windows).\n    //   - When io.ConfigDockingWithShift == true, you instead need to hold SHIFT to enable docking.\n    // - DockSpaceOverViewport:\n    //   - This is a helper to create an invisible window covering a viewport, then submit a DockSpace() into it.\n    //   - Most applications can simply call DockSpaceOverViewport() once to allow docking windows into e.g. the edge of your screen.\n    //     e.g. ImGui::NewFrame(); ImGui::DockSpaceOverViewport();                                                   // Create a dockspace in main viewport.\n    //      or: ImGui::NewFrame(); ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode); // Create a dockspace in main viewport, central node is transparent.\n    // - Dockspaces:\n    //   - A dockspace is an explicit dock node within an existing window.\n    //   - IMPORTANT: Dockspaces need to be submitted _before_ any window they can host. Submit them early in your frame!\n    //   - IMPORTANT: Dockspaces need to be kept alive if hidden, otherwise windows docked into it will be undocked.\n    //     If you have e.g. multiple tabs with a dockspace inside each tab: submit the non-visible dockspaces with ImGuiDockNodeFlags_KeepAliveOnly.\n    //   - See 'Demo->Examples->Dockspace' or 'Demo->Examples->Documents' for more detailed demos.\n    // - Programmatic docking:\n    //   - There is no public API yet other than the very limited SetNextWindowDockID() function. Sorry for that!\n    //   - Read https://github.com/ocornut/imgui/wiki/Docking for examples of how to use current internal API.\n    IMGUI_API ImGuiID       DockSpace(ImGuiID dockspace_id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL);\n    IMGUI_API ImGuiID       DockSpaceOverViewport(ImGuiID dockspace_id = 0, const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL);\n    IMGUI_API void          SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0);           // set next window dock id\n    IMGUI_API void          SetNextWindowClass(const ImGuiWindowClass* window_class);           // set next window class (control docking compatibility + provide hints to platform backend via custom viewport flags and platform parent/child relationship)\n    IMGUI_API ImGuiID       GetWindowDockID();                                                  // get dock id of current window, or 0 if not associated to any docking node.\n    IMGUI_API bool          IsWindowDocked();                                                   // is current window docked into another window?\n\n    // Logging/Capture\n    // - All text output from the interface can be captured into tty/file/clipboard. By default, tree nodes are automatically opened during logging.\n    IMGUI_API void          LogToTTY(int auto_open_depth = -1);                                 // start logging to tty (stdout)\n    IMGUI_API void          LogToFile(int auto_open_depth = -1, const char* filename = NULL);   // start logging to file\n    IMGUI_API void          LogToClipboard(int auto_open_depth = -1);                           // start logging to OS clipboard\n    IMGUI_API void          LogFinish();                                                        // stop logging (close file, etc.)\n    IMGUI_API void          LogButtons();                                                       // helper to display buttons for logging to tty/file/clipboard\n    IMGUI_API void          LogText(const char* fmt, ...) IM_FMTARGS(1);                        // pass text data straight to log (without being displayed)\n    IMGUI_API void          LogTextV(const char* fmt, va_list args) IM_FMTLIST(1);\n\n    // Drag and Drop\n    // - On source items, call BeginDragDropSource(), if it returns true also call SetDragDropPayload() + EndDragDropSource().\n    // - On target candidates, call BeginDragDropTarget(), if it returns true also call AcceptDragDropPayload() + EndDragDropTarget().\n    // - If you stop calling BeginDragDropSource() the payload is preserved however it won't have a preview tooltip (we currently display a fallback \"...\" tooltip, see #1725)\n    // - An item can be both drag source and drop target.\n    IMGUI_API bool          BeginDragDropSource(ImGuiDragDropFlags flags = 0);                                      // call after submitting an item which may be dragged. when this return true, you can call SetDragDropPayload() + EndDragDropSource()\n    IMGUI_API bool          SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0);  // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. Return true when payload has been accepted.\n    IMGUI_API void          EndDragDropSource();                                                                    // only call EndDragDropSource() if BeginDragDropSource() returns true!\n    IMGUI_API bool                  BeginDragDropTarget();                                                          // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget()\n    IMGUI_API const ImGuiPayload*   AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0);          // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released.\n    IMGUI_API void                  EndDragDropTarget();                                                            // only call EndDragDropTarget() if BeginDragDropTarget() returns true!\n    IMGUI_API const ImGuiPayload*   GetDragDropPayload();                                                           // peek directly into the current payload from anywhere. returns NULL when drag and drop is finished or inactive. use ImGuiPayload::IsDataType() to test for the payload type.\n\n    // Disabling [BETA API]\n    // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors)\n    // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)\n    // - Tooltips windows are automatically opted out of disabling. Note that IsItemHovered() by default returns false on disabled items, unless using ImGuiHoveredFlags_AllowWhenDisabled.\n    // - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls)\n    IMGUI_API void          BeginDisabled(bool disabled = true);\n    IMGUI_API void          EndDisabled();\n\n    // Clipping\n    // - Mouse hovering is affected by ImGui::PushClipRect() calls, unlike direct calls to ImDrawList::PushClipRect() which are render only.\n    IMGUI_API void          PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect);\n    IMGUI_API void          PopClipRect();\n\n    // Focus, Activation\n    IMGUI_API void          SetItemDefaultFocus();                                              // make last item the default focused item of a newly appearing window.\n    IMGUI_API void          SetKeyboardFocusHere(int offset = 0);                               // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget.\n\n    // Keyboard/Gamepad Navigation\n    IMGUI_API void          SetNavCursorVisible(bool visible);                                  // alter visibility of keyboard/gamepad cursor. by default: show when using an arrow key, hide when clicking with mouse.\n\n    // Overlapping mode\n    IMGUI_API void          SetNextItemAllowOverlap();                                          // allow next item to be overlapped by a subsequent item. Useful with invisible buttons, selectable, treenode covering an area where subsequent items may need to be added. Note that both Selectable() and TreeNode() have dedicated flags doing this.\n\n    // Item/Widgets Utilities and Query Functions\n    // - Most of the functions are referring to the previous Item that has been submitted.\n    // - See Demo Window under \"Widgets->Querying Status\" for an interactive visualization of most of those functions.\n    IMGUI_API bool          IsItemHovered(ImGuiHoveredFlags flags = 0);                         // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options.\n    IMGUI_API bool          IsItemActive();                                                     // is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item. Items that don't interact will always return false)\n    IMGUI_API bool          IsItemFocused();                                                    // is the last item focused for keyboard/gamepad navigation?\n    IMGUI_API bool          IsItemClicked(ImGuiMouseButton mouse_button = 0);                   // is the last item hovered and mouse clicked on? (**)  == IsMouseClicked(mouse_button) && IsItemHovered()Important. (**) this is NOT equivalent to the behavior of e.g. Button(). Read comments in function definition.\n    IMGUI_API bool          IsItemVisible();                                                    // is the last item visible? (items may be out of sight because of clipping/scrolling)\n    IMGUI_API bool          IsItemEdited();                                                     // did the last item modify its underlying value this frame? or was pressed? This is generally the same as the \"bool\" return value of many widgets.\n    IMGUI_API bool          IsItemActivated();                                                  // was the last item just made active (item was previously inactive).\n    IMGUI_API bool          IsItemDeactivated();                                                // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that require continuous editing.\n    IMGUI_API bool          IsItemDeactivatedAfterEdit();                                       // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that require continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item).\n    IMGUI_API bool          IsItemToggledOpen();                                                // was the last item open state toggled? set by TreeNode().\n    IMGUI_API bool          IsAnyItemHovered();                                                 // is any item hovered?\n    IMGUI_API bool          IsAnyItemActive();                                                  // is any item active?\n    IMGUI_API bool          IsAnyItemFocused();                                                 // is any item focused?\n    IMGUI_API ImGuiID       GetItemID();                                                        // get ID of last item (~~ often same ImGui::GetID(label) beforehand)\n    IMGUI_API ImVec2        GetItemRectMin();                                                   // get upper-left bounding rectangle of the last item (screen space)\n    IMGUI_API ImVec2        GetItemRectMax();                                                   // get lower-right bounding rectangle of the last item (screen space)\n    IMGUI_API ImVec2        GetItemRectSize();                                                  // get size of last item\n    IMGUI_API ImGuiItemFlags GetItemFlags();                                                    // get generic flags of last item\n\n    // Viewports\n    // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.\n    // - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports.\n    // - In the future we will extend this concept further to also represent Platform Monitor and support a \"no main platform window\" operation mode.\n    IMGUI_API ImGuiViewport* GetMainViewport();                                                 // return primary/default viewport. This can never be NULL.\n\n    // Background/Foreground Draw Lists\n    IMGUI_API ImDrawList*   GetBackgroundDrawList(ImGuiViewport* viewport = NULL);              // get background draw list for the given viewport or viewport associated to the current window. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents.\n    IMGUI_API ImDrawList*   GetForegroundDrawList(ImGuiViewport* viewport = NULL);              // get foreground draw list for the given viewport or viewport associated to the current window. this draw list will be the top-most rendered one. Useful to quickly draw shapes/text over dear imgui contents.\n\n    // Miscellaneous Utilities\n    IMGUI_API bool          IsRectVisible(const ImVec2& size);                                  // test if rectangle (of given size, starting from cursor position) is visible / not clipped.\n    IMGUI_API bool          IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max);      // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side.\n    IMGUI_API double        GetTime();                                                          // get global imgui time. incremented by io.DeltaTime every frame.\n    IMGUI_API int           GetFrameCount();                                                    // get global imgui frame count. incremented by 1 every frame.\n    IMGUI_API ImDrawListSharedData* GetDrawListSharedData();                                    // you may use this when creating your own ImDrawList instances.\n    IMGUI_API const char*   GetStyleColorName(ImGuiCol idx);                                    // get a string corresponding to the enum value (for display, saving, etc.).\n    IMGUI_API void          SetStateStorage(ImGuiStorage* storage);                             // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it)\n    IMGUI_API ImGuiStorage* GetStateStorage();\n\n    // Text Utilities\n    IMGUI_API ImVec2        CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);\n\n    // Color Utilities\n    IMGUI_API ImVec4        ColorConvertU32ToFloat4(ImU32 in);\n    IMGUI_API ImU32         ColorConvertFloat4ToU32(const ImVec4& in);\n    IMGUI_API void          ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v);\n    IMGUI_API void          ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b);\n\n    // Inputs Utilities: Raw Keyboard/Mouse/Gamepad Access\n    // - Consider using the Shortcut() function instead of IsKeyPressed()/IsKeyChordPressed()! Shortcut() is easier to use and better featured (can do focus routing check).\n    // - the ImGuiKey enum contains all possible keyboard, mouse and gamepad inputs (e.g. ImGuiKey_A, ImGuiKey_MouseLeft, ImGuiKey_GamepadDpadUp...).\n    // - (legacy: before v1.87 (2022-02), we used ImGuiKey < 512 values to carry native/user indices as defined by each backends. This was obsoleted in 1.87 (2022-02) and completely removed in 1.91.5 (2024-11). See https://github.com/ocornut/imgui/issues/4921)\n    IMGUI_API bool          IsKeyDown(ImGuiKey key);                                            // is key being held.\n    IMGUI_API bool          IsKeyPressed(ImGuiKey key, bool repeat = true);                     // was key pressed (went from !Down to Down)? Repeat rate uses io.KeyRepeatDelay / KeyRepeatRate.\n    IMGUI_API bool          IsKeyReleased(ImGuiKey key);                                        // was key released (went from Down to !Down)?\n    IMGUI_API bool          IsKeyChordPressed(ImGuiKeyChord key_chord);                         // was key chord (mods + key) pressed, e.g. you can pass 'ImGuiMod_Ctrl | ImGuiKey_S' as a key-chord. This doesn't do any routing or focus check, please consider using Shortcut() function instead.\n    IMGUI_API int           GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float rate);  // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate\n    IMGUI_API const char*   GetKeyName(ImGuiKey key);                                           // [DEBUG] returns English name of the key. Those names are provided for debugging purpose and are not meant to be saved persistently nor compared.\n    IMGUI_API void          SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard);        // Override io.WantCaptureKeyboard flag next frame (said flag is left for your application to handle, typically when true it instructs your app to ignore inputs). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting \"io.WantCaptureKeyboard = want_capture_keyboard\"; after the next NewFrame() call.\n\n    // Inputs Utilities: Shortcut Testing & Routing\n    // - Typical use is e.g.: 'if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_S)) { ... }'.\n    // - Flags: Default route use ImGuiInputFlags_RouteFocused, but see ImGuiInputFlags_RouteGlobal and other options in ImGuiInputFlags_!\n    // - Flags: Use ImGuiInputFlags_Repeat to support repeat.\n    // - ImGuiKeyChord = a ImGuiKey + optional ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super.\n    //       ImGuiKey_C                          // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments\n    //       ImGuiMod_Ctrl | ImGuiKey_C          // Accepted by functions taking ImGuiKeyChord arguments\n    //   only ImGuiMod_XXX values are legal to combine with an ImGuiKey. You CANNOT combine two ImGuiKey values.\n    // - The general idea is that several callers may register interest in a shortcut, and only one owner gets it.\n    //      Parent   -> call Shortcut(Ctrl+S)    // When Parent is focused, Parent gets the shortcut.\n    //        Child1 -> call Shortcut(Ctrl+S)    // When Child1 is focused, Child1 gets the shortcut (Child1 overrides Parent shortcuts)\n    //        Child2 -> no call                  // When Child2 is focused, Parent gets the shortcut.\n    //   The whole system is order independent, so if Child1 makes its calls before Parent, results will be identical.\n    //   This is an important property as it facilitate working with foreign code or larger codebase.\n    // - To understand the difference:\n    //   - IsKeyChordPressed() compares mods and call IsKeyPressed()\n    //     -> the function has no side-effect.\n    //   - Shortcut() submits a route, routes are resolved, if it currently can be routed it calls IsKeyChordPressed()\n    //     -> the function has (desirable) side-effects as it can prevents another call from getting the route.\n    // - Visualize registered routes in 'Metrics/Debugger->Inputs'.\n    IMGUI_API bool          Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0);\n    IMGUI_API void          SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0);\n\n    // Inputs Utilities: Key/Input Ownership [BETA]\n    // - One common use case would be to allow your items to disable standard inputs behaviors such\n    //   as Tab or Alt key handling, Mouse Wheel scrolling, etc.\n    //   e.g. Button(...); SetItemKeyOwner(ImGuiKey_MouseWheelY); to make hovering/activating a button disable wheel for scrolling.\n    // - Reminder ImGuiKey enum include access to mouse buttons and gamepad, so key ownership can apply to them.\n    // - Many related features are still in imgui_internal.h. For instance, most IsKeyXXX()/IsMouseXXX() functions have an owner-id-aware version.\n    IMGUI_API void          SetItemKeyOwner(ImGuiKey key);                                      // Set key owner to last item ID if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.\n\n    // Inputs Utilities: Mouse\n    // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right.\n    // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle.\n    // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold')\n    IMGUI_API bool          IsMouseDown(ImGuiMouseButton button);                               // is mouse button held?\n    IMGUI_API bool          IsMouseClicked(ImGuiMouseButton button, bool repeat = false);       // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1.\n    IMGUI_API bool          IsMouseReleased(ImGuiMouseButton button);                           // did mouse button released? (went from Down to !Down)\n    IMGUI_API bool          IsMouseDoubleClicked(ImGuiMouseButton button);                      // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true)\n    IMGUI_API bool          IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay);     // delayed mouse release (use very sparingly!). Generally used with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount==1' test. This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename.\n    IMGUI_API int           GetMouseClickedCount(ImGuiMouseButton button);                      // return the number of successive mouse-clicks at the time where a click happen (otherwise 0).\n    IMGUI_API bool          IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block.\n    IMGUI_API bool          IsMousePosValid(const ImVec2* mouse_pos = NULL);                    // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available\n    IMGUI_API bool          IsAnyMouseDown();                                                   // [WILL OBSOLETE] is any mouse button held? This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.\n    IMGUI_API ImVec2        GetMousePos();                                                      // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls\n    IMGUI_API ImVec2        GetMousePosOnOpeningCurrentPopup();                                 // retrieve mouse position at the time of opening popup we have BeginPopup() into (helper to avoid user backing that value themselves)\n    IMGUI_API bool          IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f);         // is mouse dragging? (uses io.MouseDraggingThreshold if lock_threshold < 0.0f)\n    IMGUI_API ImVec2        GetMouseDragDelta(ImGuiMouseButton button = 0, float lock_threshold = -1.0f);   // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once (uses io.MouseDraggingThreshold if lock_threshold < 0.0f)\n    IMGUI_API void          ResetMouseDragDelta(ImGuiMouseButton button = 0);                   //\n    IMGUI_API ImGuiMouseCursor GetMouseCursor();                                                // get desired mouse cursor shape. Important: reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you\n    IMGUI_API void          SetMouseCursor(ImGuiMouseCursor cursor_type);                       // set desired mouse cursor shape\n    IMGUI_API void          SetNextFrameWantCaptureMouse(bool want_capture_mouse);              // Override io.WantCaptureMouse flag next frame (said flag is left for your application to handle, typical when true it instructs your app to ignore inputs). This is equivalent to setting \"io.WantCaptureMouse = want_capture_mouse;\" after the next NewFrame() call.\n\n    // Clipboard Utilities\n    // - Also see the LogToClipboard() function to capture GUI into clipboard, or easily output text data to the clipboard.\n    IMGUI_API const char*   GetClipboardText();\n    IMGUI_API void          SetClipboardText(const char* text);\n\n    // Settings/.Ini Utilities\n    // - The disk functions are automatically called if io.IniFilename != NULL (default is \"imgui.ini\").\n    // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually.\n    // - Important: default value \"imgui.ini\" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables).\n    IMGUI_API void          LoadIniSettingsFromDisk(const char* ini_filename);                  // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename).\n    IMGUI_API void          LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source.\n    IMGUI_API void          SaveIniSettingsToDisk(const char* ini_filename);                    // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext).\n    IMGUI_API const char*   SaveIniSettingsToMemory(size_t* out_ini_size = NULL);               // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.\n\n    // Debug Utilities\n    // - Your main debugging friend is the ShowMetricsWindow() function.\n    // - Interactive tools are all accessible from the 'Dear ImGui Demo->Tools' menu.\n    // - Read https://github.com/ocornut/imgui/wiki/Debug-Tools for a description of all available debug tools.\n    IMGUI_API void          DebugTextEncoding(const char* text);\n    IMGUI_API void          DebugFlashStyleColor(ImGuiCol idx);\n    IMGUI_API void          DebugStartItemPicker();\n    IMGUI_API bool          DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro.\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    IMGUI_API void          DebugLog(const char* fmt, ...)           IM_FMTARGS(1); // Call via IMGUI_DEBUG_LOG() for maximum stripping in caller code!\n    IMGUI_API void          DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1);\n#endif\n\n    // Memory Allocators\n    // - Those functions are not reliant on the current context.\n    // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()\n    //   for each static/DLL boundary you are calling from. Read \"Context and Memory Allocators\" section of imgui.cpp for more details.\n    IMGUI_API void          SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data = NULL);\n    IMGUI_API void          GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data);\n    IMGUI_API void*         MemAlloc(size_t size);\n    IMGUI_API void          MemFree(void* ptr);\n\n    // (Optional) Platform/OS interface for multi-viewport support\n    // Read comments around the ImGuiPlatformIO structure for more details.\n    // Note: You may use GetWindowViewport() to get the current viewport of the current window.\n    IMGUI_API void          UpdatePlatformWindows();                                // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport.\n    IMGUI_API void          RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs.\n    IMGUI_API void          DestroyPlatformWindows();                               // call DestroyWindow platform functions for all viewports. call from backend Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext().\n    IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID viewport_id);                 // this is a helper for backends.\n    IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle);   // this is a helper for backends. the type platform_handle is decided by the backend (e.g. HWND, MyWindow*, GLFWwindow* etc.)\n\n} // namespace ImGui\n\n//-----------------------------------------------------------------------------\n// [SECTION] Flags & Enumerations\n//-----------------------------------------------------------------------------\n\n// Flags for ImGui::Begin()\n// (Those are per-window flags. There are shared flags in ImGuiIO: io.ConfigWindowsResizeFromEdges and io.ConfigWindowsMoveFromTitleBarOnly)\nenum ImGuiWindowFlags_\n{\n    ImGuiWindowFlags_None                   = 0,\n    ImGuiWindowFlags_NoTitleBar             = 1 << 0,   // Disable title-bar\n    ImGuiWindowFlags_NoResize               = 1 << 1,   // Disable user resizing with the lower-right grip\n    ImGuiWindowFlags_NoMove                 = 1 << 2,   // Disable user moving the window\n    ImGuiWindowFlags_NoScrollbar            = 1 << 3,   // Disable scrollbars (window can still scroll with mouse or programmatically)\n    ImGuiWindowFlags_NoScrollWithMouse      = 1 << 4,   // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set.\n    ImGuiWindowFlags_NoCollapse             = 1 << 5,   // Disable user collapsing window by double-clicking on it. Also referred to as Window Menu Button (e.g. within a docking node).\n    ImGuiWindowFlags_AlwaysAutoResize       = 1 << 6,   // Resize every window to its content every frame\n    ImGuiWindowFlags_NoBackground           = 1 << 7,   // Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f).\n    ImGuiWindowFlags_NoSavedSettings        = 1 << 8,   // Never load/save settings in .ini file\n    ImGuiWindowFlags_NoMouseInputs          = 1 << 9,   // Disable catching mouse, hovering test with pass through.\n    ImGuiWindowFlags_MenuBar                = 1 << 10,  // Has a menu-bar\n    ImGuiWindowFlags_HorizontalScrollbar    = 1 << 11,  // Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the \"Horizontal Scrolling\" section.\n    ImGuiWindowFlags_NoFocusOnAppearing     = 1 << 12,  // Disable taking focus when transitioning from hidden to visible state\n    ImGuiWindowFlags_NoBringToFrontOnFocus  = 1 << 13,  // Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus)\n    ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14,  // Always show vertical scrollbar (even if ContentSize.y < Size.y)\n    ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15,  // Always show horizontal scrollbar (even if ContentSize.x < Size.x)\n    ImGuiWindowFlags_NoNavInputs            = 1 << 16,  // No keyboard/gamepad navigation within the window\n    ImGuiWindowFlags_NoNavFocus             = 1 << 17,  // No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by Ctrl+Tab)\n    ImGuiWindowFlags_UnsavedDocument        = 1 << 18,  // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.\n    ImGuiWindowFlags_NoDocking              = 1 << 19,  // Disable docking of this window\n    ImGuiWindowFlags_NoNav                  = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,\n    ImGuiWindowFlags_NoDecoration           = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse,\n    ImGuiWindowFlags_NoInputs               = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,\n\n    // [Internal]\n    ImGuiWindowFlags_DockNodeHost           = 1 << 23,  // Don't use! For internal use by Begin()/NewFrame()\n    ImGuiWindowFlags_ChildWindow            = 1 << 24,  // Don't use! For internal use by BeginChild()\n    ImGuiWindowFlags_Tooltip                = 1 << 25,  // Don't use! For internal use by BeginTooltip()\n    ImGuiWindowFlags_Popup                  = 1 << 26,  // Don't use! For internal use by BeginPopup()\n    ImGuiWindowFlags_Modal                  = 1 << 27,  // Don't use! For internal use by BeginPopupModal()\n    ImGuiWindowFlags_ChildMenu              = 1 << 28,  // Don't use! For internal use by BeginMenu()\n\n    // Obsolete names\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    //ImGuiWindowFlags_NavFlattened           = 1 << 29,  // Obsoleted in 1.90.9: moved to ImGuiChildFlags. BeginChild(name, size, 0, ImGuiWindowFlags_NavFlattened)           --> BeginChild(name, size, ImGuiChildFlags_NavFlattened, 0)\n    //ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30,  // Obsoleted in 1.90.0: moved to ImGuiChildFlags. BeginChild(name, size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding) --> BeginChild(name, size, ImGuiChildFlags_AlwaysUseWindowPadding, 0)\n#endif\n};\n\n// Flags for ImGui::BeginChild()\n// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'.)\n// About using AutoResizeX/AutoResizeY flags:\n// - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see \"Demo->Child->Auto-resize with Constraints\").\n// - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing.\n//   - This allows BeginChild() to return false when not within boundaries (e.g. when scrolling), which is more optimal. BUT it won't update its auto-size while clipped.\n//     While not perfect, it is a better default behavior as the always-on performance gain is more valuable than the occasional \"resizing after becoming visible again\" glitch.\n//   - You may also use ImGuiChildFlags_AlwaysAutoResize to force an update even when child window is not in view.\n//     HOWEVER PLEASE UNDERSTAND THAT DOING SO WILL PREVENT BeginChild() FROM EVER RETURNING FALSE, disabling benefits of coarse clipping.\nenum ImGuiChildFlags_\n{\n    ImGuiChildFlags_None                    = 0,\n    ImGuiChildFlags_Borders                 = 1 << 0,   // Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason)\n    ImGuiChildFlags_AlwaysUseWindowPadding  = 1 << 1,   // Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense)\n    ImGuiChildFlags_ResizeX                 = 1 << 2,   // Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags)\n    ImGuiChildFlags_ResizeY                 = 1 << 3,   // Allow resize from bottom border (layout direction). \"\n    ImGuiChildFlags_AutoResizeX             = 1 << 4,   // Enable auto-resizing width. Read \"IMPORTANT: Size measurement\" details above.\n    ImGuiChildFlags_AutoResizeY             = 1 << 5,   // Enable auto-resizing height. Read \"IMPORTANT: Size measurement\" details above.\n    ImGuiChildFlags_AlwaysAutoResize        = 1 << 6,   // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED.\n    ImGuiChildFlags_FrameStyle              = 1 << 7,   // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.\n    ImGuiChildFlags_NavFlattened            = 1 << 8,   // [BETA] Share focus scope, allow keyboard/gamepad navigation to cross over parent border to this child or between sibling child windows.\n\n    // Obsolete names\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    //ImGuiChildFlags_Border                = ImGuiChildFlags_Borders,  // Renamed in 1.91.1 (August 2024) for consistency.\n#endif\n};\n\n// Flags for ImGui::PushItemFlag()\n// (Those are shared by all submitted items)\nenum ImGuiItemFlags_\n{\n    ImGuiItemFlags_None                     = 0,        // (Default)\n    ImGuiItemFlags_NoTabStop                = 1 << 0,   // false    // Disable keyboard tabbing. This is a \"lighter\" version of ImGuiItemFlags_NoNav.\n    ImGuiItemFlags_NoNav                    = 1 << 1,   // false    // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls).\n    ImGuiItemFlags_NoNavDefaultFocus        = 1 << 2,   // false    // Disable item being a candidate for default focus (e.g. used by title bar items).\n    ImGuiItemFlags_ButtonRepeat             = 1 << 3,   // false    // Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held.\n    ImGuiItemFlags_AutoClosePopups          = 1 << 4,   // true     // MenuItem()/Selectable() automatically close their parent popup window.\n    ImGuiItemFlags_AllowDuplicateId         = 1 << 5,   // false    // Allow submitting an item with the same identifier as an item already submitted this frame without triggering a warning tooltip if io.ConfigDebugHighlightIdConflicts is set.\n    ImGuiItemFlags_Disabled                 = 1 << 6,   // false    // [Internal] Disable interactions. DOES NOT affect visuals. This is used by BeginDisabled()/EndDisabled() and only provided here so you can read back via GetItemFlags().\n};\n\n// Flags for ImGui::InputText()\n// (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigInputTextCursorBlink and io.ConfigInputTextEnterKeepActive)\nenum ImGuiInputTextFlags_\n{\n    // Basic filters (also see ImGuiInputTextFlags_CallbackCharFilter)\n    ImGuiInputTextFlags_None                = 0,\n    ImGuiInputTextFlags_CharsDecimal        = 1 << 0,   // Allow 0123456789.+-*/\n    ImGuiInputTextFlags_CharsHexadecimal    = 1 << 1,   // Allow 0123456789ABCDEFabcdef\n    ImGuiInputTextFlags_CharsScientific     = 1 << 2,   // Allow 0123456789.+-*/eE (Scientific notation input)\n    ImGuiInputTextFlags_CharsUppercase      = 1 << 3,   // Turn a..z into A..Z\n    ImGuiInputTextFlags_CharsNoBlank        = 1 << 4,   // Filter out spaces, tabs\n\n    // Inputs\n    ImGuiInputTextFlags_AllowTabInput       = 1 << 5,   // Pressing TAB input a '\\t' character into the text field\n    ImGuiInputTextFlags_EnterReturnsTrue    = 1 << 6,   // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider using IsItemDeactivatedAfterEdit() instead!\n    ImGuiInputTextFlags_EscapeClearsAll     = 1 << 7,   // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert)\n    ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 8,   // In multi-line mode: validate with Enter, add new line with Ctrl+Enter (default is opposite: validate with Ctrl+Enter, add line with Enter). Note that Shift+Enter always enter a new line either way.\n\n    // Other options\n    ImGuiInputTextFlags_ReadOnly            = 1 << 9,   // Read-only mode\n    ImGuiInputTextFlags_Password            = 1 << 10,  // Password mode, display all characters as '*', disable copy\n    ImGuiInputTextFlags_AlwaysOverwrite     = 1 << 11,  // Overwrite mode\n    ImGuiInputTextFlags_AutoSelectAll       = 1 << 12,  // Select entire text when first taking mouse focus\n    ImGuiInputTextFlags_ParseEmptyRefVal    = 1 << 13,  // InputFloat(), InputInt(), InputScalar() etc. only: parse empty string as zero value.\n    ImGuiInputTextFlags_DisplayEmptyRefVal  = 1 << 14,  // InputFloat(), InputInt(), InputScalar() etc. only: when value is zero, do not display it. Generally used with ImGuiInputTextFlags_ParseEmptyRefVal.\n    ImGuiInputTextFlags_NoHorizontalScroll  = 1 << 15,  // Disable following the cursor horizontally\n    ImGuiInputTextFlags_NoUndoRedo          = 1 << 16,  // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID().\n\n    // Elide display / Alignment\n    ImGuiInputTextFlags_ElideLeft           = 1 << 17,  // When text doesn't fit, elide left side to ensure right side stays visible. Useful for path/filenames. Single-line only!\n\n    // Callback features\n    ImGuiInputTextFlags_CallbackCompletion  = 1 << 18,  // Callback on pressing TAB (for completion handling)\n    ImGuiInputTextFlags_CallbackHistory     = 1 << 19,  // Callback on pressing Up/Down arrows (for history handling)\n    ImGuiInputTextFlags_CallbackAlways      = 1 << 20,  // Callback on each iteration. User code may query cursor position, modify text buffer.\n    ImGuiInputTextFlags_CallbackCharFilter  = 1 << 21,  // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard.\n    ImGuiInputTextFlags_CallbackResize      = 1 << 22,  // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this)\n    ImGuiInputTextFlags_CallbackEdit        = 1 << 23,  // Callback on any edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active.\n\n    // Multi-line Word-Wrapping [BETA]\n    // - Not well tested yet. Please report any incorrect cursor movement, selection behavior etc. bug to https://github.com/ocornut/imgui/issues/3237.\n    // - Wrapping style is not ideal. Wrapping of long words/sections (e.g. words larger than total available width) may be particularly unpleasing.\n    // - Wrapping width needs to always account for the possibility of a vertical scrollbar.\n    // - It is much slower than regular text fields.\n    //   Ballpark estimate of cost on my 2019 desktop PC: for a 100 KB text buffer: +~0.3 ms (Optimized) / +~1.0 ms (Debug build).\n    //   The CPU cost is very roughly proportional to text length, so a 10 KB buffer should cost about ten times less.\n    ImGuiInputTextFlags_WordWrap            = 1 << 24,  // InputTextMultiline(): word-wrap lines that are too long.\n\n    // Obsolete names\n    //ImGuiInputTextFlags_AlwaysInsertMode  = ImGuiInputTextFlags_AlwaysOverwrite   // [renamed in 1.82] name was not matching behavior\n};\n\n// Flags for ImGui::TreeNodeEx(), ImGui::CollapsingHeader*()\nenum ImGuiTreeNodeFlags_\n{\n    ImGuiTreeNodeFlags_None                 = 0,\n    ImGuiTreeNodeFlags_Selected             = 1 << 0,   // Draw as selected\n    ImGuiTreeNodeFlags_Framed               = 1 << 1,   // Draw frame with background (e.g. for CollapsingHeader)\n    ImGuiTreeNodeFlags_AllowOverlap         = 1 << 2,   // Hit testing to allow subsequent widgets to overlap this one\n    ImGuiTreeNodeFlags_NoTreePushOnOpen     = 1 << 3,   // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack\n    ImGuiTreeNodeFlags_NoAutoOpenOnLog      = 1 << 4,   // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes)\n    ImGuiTreeNodeFlags_DefaultOpen          = 1 << 5,   // Default node to be open\n    ImGuiTreeNodeFlags_OpenOnDoubleClick    = 1 << 6,   // Open on double-click instead of simple click (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined.\n    ImGuiTreeNodeFlags_OpenOnArrow          = 1 << 7,   // Open when clicking on the arrow part (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined.\n    ImGuiTreeNodeFlags_Leaf                 = 1 << 8,   // No collapsing, no arrow (use as a convenience for leaf nodes). Note: will always open a tree/id scope and return true. If you never use that scope, add ImGuiTreeNodeFlags_NoTreePushOnOpen.\n    ImGuiTreeNodeFlags_Bullet               = 1 << 9,   // Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag!\n    ImGuiTreeNodeFlags_FramePadding         = 1 << 10,  // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding() before the node.\n    ImGuiTreeNodeFlags_SpanAvailWidth       = 1 << 11,  // Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line without using AllowOverlap mode.\n    ImGuiTreeNodeFlags_SpanFullWidth        = 1 << 12,  // Extend hit box to the left-most and right-most edges (cover the indent area).\n    ImGuiTreeNodeFlags_SpanLabelWidth       = 1 << 13,  // Narrow hit box + narrow hovering highlight, will only cover the label text.\n    ImGuiTreeNodeFlags_SpanAllColumns       = 1 << 14,  // Frame will span all columns of its container table (label will still fit in current column)\n    ImGuiTreeNodeFlags_LabelSpanAllColumns  = 1 << 15,  // Label will span all columns of its container table\n    //ImGuiTreeNodeFlags_NoScrollOnOpen     = 1 << 16,  // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible\n    ImGuiTreeNodeFlags_NavLeftJumpsToParent = 1 << 17,  // Nav: left arrow moves back to parent. This is processed in TreePop() when there's an unfulfilled Left nav request remaining.\n    ImGuiTreeNodeFlags_CollapsingHeader     = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog,\n\n    // [EXPERIMENTAL] Draw lines connecting TreeNode hierarchy. Discuss in GitHub issue #2920.\n    // Default value is pulled from style.TreeLinesFlags. May be overridden in TreeNode calls.\n    ImGuiTreeNodeFlags_DrawLinesNone        = 1 << 18,  // No lines drawn\n    ImGuiTreeNodeFlags_DrawLinesFull        = 1 << 19,  // Horizontal lines to child nodes. Vertical line drawn down to TreePop() position: cover full contents. Faster (for large trees).\n    ImGuiTreeNodeFlags_DrawLinesToNodes     = 1 << 20,  // Horizontal lines to child nodes. Vertical line drawn down to bottom-most child node. Slower (for large trees).\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiTreeNodeFlags_NavLeftJumpsBackHere = ImGuiTreeNodeFlags_NavLeftJumpsToParent,  // Renamed in 1.92.0\n    ImGuiTreeNodeFlags_SpanTextWidth        = ImGuiTreeNodeFlags_SpanLabelWidth,        // Renamed in 1.90.7\n    //ImGuiTreeNodeFlags_AllowItemOverlap   = ImGuiTreeNodeFlags_AllowOverlap,          // Renamed in 1.89.7\n#endif\n};\n\n// Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions.\n// - IMPORTANT: If you ever used the left mouse button with BeginPopupContextXXX() helpers before 1.92.6: Read \"API BREAKING CHANGES\" 2026/01/07 (1.92.6) entry in imgui.cpp or GitHub topic #9157.\n// - Multiple buttons currently cannot be combined/or-ed in those functions (we could allow it later).\nenum ImGuiPopupFlags_\n{\n    ImGuiPopupFlags_None                    = 0,\n    ImGuiPopupFlags_MouseButtonLeft         = 1 << 2,   // For BeginPopupContext*(): open on Left Mouse release. Only one button allowed!\n    ImGuiPopupFlags_MouseButtonRight        = 2 << 2,   // For BeginPopupContext*(): open on Right Mouse release. Only one button allowed! (default)\n    ImGuiPopupFlags_MouseButtonMiddle       = 3 << 2,   // For BeginPopupContext*(): open on Middle Mouse release. Only one button allowed!\n    ImGuiPopupFlags_NoReopen                = 1 << 5,   // For OpenPopup*(), BeginPopupContext*(): don't reopen same popup if already open (won't reposition, won't reinitialize navigation)\n    //ImGuiPopupFlags_NoReopenAlwaysNavInit = 1 << 6,   // For OpenPopup*(), BeginPopupContext*(): focus and initialize navigation even when not reopening.\n    ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 7,   // For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack\n    ImGuiPopupFlags_NoOpenOverItems         = 1 << 8,   // For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space\n    ImGuiPopupFlags_AnyPopupId              = 1 << 10,  // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup.\n    ImGuiPopupFlags_AnyPopupLevel           = 1 << 11,  // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level)\n    ImGuiPopupFlags_AnyPopup                = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel,\n    ImGuiPopupFlags_MouseButtonShift_       = 2,        // [Internal]\n    ImGuiPopupFlags_MouseButtonMask_        = 0x0C,     // [Internal]\n    ImGuiPopupFlags_InvalidMask_            = 0x03,     // [Internal] Reserve legacy bits 0-1 to detect incorrectly passing 1 or 2 to the function.\n};\n\n// Flags for ImGui::Selectable()\nenum ImGuiSelectableFlags_\n{\n    ImGuiSelectableFlags_None               = 0,\n    ImGuiSelectableFlags_NoAutoClosePopups  = 1 << 0,   // Clicking this doesn't close parent popup window (overrides ImGuiItemFlags_AutoClosePopups)\n    ImGuiSelectableFlags_SpanAllColumns     = 1 << 1,   // Frame will span all columns of its container table (text will still fit in current column)\n    ImGuiSelectableFlags_AllowDoubleClick   = 1 << 2,   // Generate press events on double clicks too\n    ImGuiSelectableFlags_Disabled           = 1 << 3,   // Cannot be selected, display grayed out text\n    ImGuiSelectableFlags_AllowOverlap       = 1 << 4,   // (WIP) Hit testing to allow subsequent widgets to overlap this one\n    ImGuiSelectableFlags_Highlight          = 1 << 5,   // Make the item be displayed as if it is hovered\n    ImGuiSelectableFlags_SelectOnNav        = 1 << 6,   // Auto-select when moved into, unless Ctrl is held. Automatic when in a BeginMultiSelect() block.\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiSelectableFlags_DontClosePopups    = ImGuiSelectableFlags_NoAutoClosePopups,   // Renamed in 1.91.0\n    //ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap,        // Renamed in 1.89.7\n#endif\n};\n\n// Flags for ImGui::BeginCombo()\nenum ImGuiComboFlags_\n{\n    ImGuiComboFlags_None                    = 0,\n    ImGuiComboFlags_PopupAlignLeft          = 1 << 0,   // Align the popup toward the left by default\n    ImGuiComboFlags_HeightSmall             = 1 << 1,   // Max ~4 items visible. Tip: If you want your combo popup to be a specific size you can use SetNextWindowSizeConstraints() prior to calling BeginCombo()\n    ImGuiComboFlags_HeightRegular           = 1 << 2,   // Max ~8 items visible (default)\n    ImGuiComboFlags_HeightLarge             = 1 << 3,   // Max ~20 items visible\n    ImGuiComboFlags_HeightLargest           = 1 << 4,   // As many fitting items as possible\n    ImGuiComboFlags_NoArrowButton           = 1 << 5,   // Display on the preview box without the square arrow button\n    ImGuiComboFlags_NoPreview               = 1 << 6,   // Display only a square arrow button\n    ImGuiComboFlags_WidthFitPreview         = 1 << 7,   // Width dynamically calculated from preview contents\n    ImGuiComboFlags_HeightMask_             = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest,\n};\n\n// Flags for ImGui::BeginTabBar()\nenum ImGuiTabBarFlags_\n{\n    ImGuiTabBarFlags_None                           = 0,\n    ImGuiTabBarFlags_Reorderable                    = 1 << 0,   // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list\n    ImGuiTabBarFlags_AutoSelectNewTabs              = 1 << 1,   // Automatically select new tabs when they appear\n    ImGuiTabBarFlags_TabListPopupButton             = 1 << 2,   // Disable buttons to open the tab list popup\n    ImGuiTabBarFlags_NoCloseWithMiddleMouseButton   = 1 << 3,   // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.\n    ImGuiTabBarFlags_NoTabListScrollingButtons      = 1 << 4,   // Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll)\n    ImGuiTabBarFlags_NoTooltip                      = 1 << 5,   // Disable tooltips when hovering a tab\n    ImGuiTabBarFlags_DrawSelectedOverline           = 1 << 6,   // Draw selected overline markers over selected tab\n\n    // Fitting/Resize policy\n    ImGuiTabBarFlags_FittingPolicyMixed             = 1 << 7,   // Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling buttons.\n    ImGuiTabBarFlags_FittingPolicyShrink            = 1 << 8,   // Shrink down tabs when they don't fit\n    ImGuiTabBarFlags_FittingPolicyScroll            = 1 << 9,   // Enable scrolling buttons when tabs don't fit\n    ImGuiTabBarFlags_FittingPolicyMask_             = ImGuiTabBarFlags_FittingPolicyMixed | ImGuiTabBarFlags_FittingPolicyShrink | ImGuiTabBarFlags_FittingPolicyScroll,\n    ImGuiTabBarFlags_FittingPolicyDefault_          = ImGuiTabBarFlags_FittingPolicyMixed,\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiTabBarFlags_FittingPolicyResizeDown        = ImGuiTabBarFlags_FittingPolicyShrink, // Renamed in 1.92.2\n#endif\n};\n\n// Flags for ImGui::BeginTabItem()\nenum ImGuiTabItemFlags_\n{\n    ImGuiTabItemFlags_None                          = 0,\n    ImGuiTabItemFlags_UnsavedDocument               = 1 << 0,   // Display a dot next to the title + set ImGuiTabItemFlags_NoAssumedClosure.\n    ImGuiTabItemFlags_SetSelected                   = 1 << 1,   // Trigger flag to programmatically make the tab selected when calling BeginTabItem()\n    ImGuiTabItemFlags_NoCloseWithMiddleMouseButton  = 1 << 2,   // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.\n    ImGuiTabItemFlags_NoPushId                      = 1 << 3,   // Don't call PushID()/PopID() on BeginTabItem()/EndTabItem()\n    ImGuiTabItemFlags_NoTooltip                     = 1 << 4,   // Disable tooltip for the given tab\n    ImGuiTabItemFlags_NoReorder                     = 1 << 5,   // Disable reordering this tab or having another tab cross over this tab\n    ImGuiTabItemFlags_Leading                       = 1 << 6,   // Enforce the tab position to the left of the tab bar (after the tab list popup button)\n    ImGuiTabItemFlags_Trailing                      = 1 << 7,   // Enforce the tab position to the right of the tab bar (before the scrolling buttons)\n    ImGuiTabItemFlags_NoAssumedClosure              = 1 << 8,   // Tab is selected when trying to close + closure is not immediately assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.\n};\n\n// Flags for ImGui::IsWindowFocused()\nenum ImGuiFocusedFlags_\n{\n    ImGuiFocusedFlags_None                          = 0,\n    ImGuiFocusedFlags_ChildWindows                  = 1 << 0,   // Return true if any children of the window is focused\n    ImGuiFocusedFlags_RootWindow                    = 1 << 1,   // Test from root window (top most parent of the current hierarchy)\n    ImGuiFocusedFlags_AnyWindow                     = 1 << 2,   // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ!\n    ImGuiFocusedFlags_NoPopupHierarchy              = 1 << 3,   // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)\n    ImGuiFocusedFlags_DockHierarchy                 = 1 << 4,   // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)\n    ImGuiFocusedFlags_RootAndChildWindows           = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows,\n};\n\n// Flags for ImGui::IsItemHovered(), ImGui::IsWindowHovered()\n// Note: if you are trying to check whether your mouse should be dispatched to Dear ImGui or to your app, you should use 'io.WantCaptureMouse' instead! Please read the FAQ!\n// Note: windows with the ImGuiWindowFlags_NoInputs flag are ignored by IsWindowHovered() calls.\nenum ImGuiHoveredFlags_\n{\n    ImGuiHoveredFlags_None                          = 0,        // Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them.\n    ImGuiHoveredFlags_ChildWindows                  = 1 << 0,   // IsWindowHovered() only: Return true if any children of the window is hovered\n    ImGuiHoveredFlags_RootWindow                    = 1 << 1,   // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy)\n    ImGuiHoveredFlags_AnyWindow                     = 1 << 2,   // IsWindowHovered() only: Return true if any window is hovered\n    ImGuiHoveredFlags_NoPopupHierarchy              = 1 << 3,   // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)\n    ImGuiHoveredFlags_DockHierarchy                 = 1 << 4,   // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)\n    ImGuiHoveredFlags_AllowWhenBlockedByPopup       = 1 << 5,   // Return true even if a popup window is normally blocking access to this item/window\n    //ImGuiHoveredFlags_AllowWhenBlockedByModal     = 1 << 6,   // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet.\n    ImGuiHoveredFlags_AllowWhenBlockedByActiveItem  = 1 << 7,   // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns.\n    ImGuiHoveredFlags_AllowWhenOverlappedByItem     = 1 << 8,   // IsItemHovered() only: Return true even if the item uses AllowOverlap mode and is overlapped by another hoverable item.\n    ImGuiHoveredFlags_AllowWhenOverlappedByWindow   = 1 << 9,   // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window.\n    ImGuiHoveredFlags_AllowWhenDisabled             = 1 << 10,  // IsItemHovered() only: Return true even if the item is disabled\n    ImGuiHoveredFlags_NoNavOverride                 = 1 << 11,  // IsItemHovered() only: Disable using keyboard/gamepad navigation state when active, always query mouse\n    ImGuiHoveredFlags_AllowWhenOverlapped           = ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenOverlappedByWindow,\n    ImGuiHoveredFlags_RectOnly                      = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped,\n    ImGuiHoveredFlags_RootAndChildWindows           = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows,\n\n    // Tooltips mode\n    // - typically used in IsItemHovered() + SetTooltip() sequence.\n    // - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior.\n    //   e.g. 'HoverFlagsForTooltipMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled'.\n    // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often.\n    // - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay.\n    ImGuiHoveredFlags_ForTooltip                    = 1 << 12,  // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence.\n\n    // (Advanced) Mouse Hovering delays.\n    // - generally you can use ImGuiHoveredFlags_ForTooltip to use application-standardized flags.\n    // - use those if you need specific overrides.\n    ImGuiHoveredFlags_Stationary                    = 1 << 13,  // Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay.\n    ImGuiHoveredFlags_DelayNone                     = 1 << 14,  // IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this.\n    ImGuiHoveredFlags_DelayShort                    = 1 << 15,  // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).\n    ImGuiHoveredFlags_DelayNormal                   = 1 << 16,  // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).\n    ImGuiHoveredFlags_NoSharedDelay                 = 1 << 17,  // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays)\n};\n\n// Flags for ImGui::DockSpace(), shared/inherited by child nodes.\n// (Some flags can be applied to individual nodes directly)\n// FIXME-DOCK: Also see ImGuiDockNodeFlagsPrivate_ which may involve using the WIP and internal DockBuilder api.\nenum ImGuiDockNodeFlags_\n{\n    ImGuiDockNodeFlags_None                         = 0,\n    ImGuiDockNodeFlags_KeepAliveOnly                = 1 << 0,   //       // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked.\n    //ImGuiDockNodeFlags_NoCentralNode              = 1 << 1,   //       // Disable Central Node (the node which can stay empty)\n    ImGuiDockNodeFlags_NoDockingOverCentralNode     = 1 << 2,   //       // Disable docking over the Central Node, which will be always kept empty.\n    ImGuiDockNodeFlags_PassthruCentralNode          = 1 << 3,   //       // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details.\n    ImGuiDockNodeFlags_NoDockingSplit               = 1 << 4,   //       // Disable other windows/nodes from splitting this node.\n    ImGuiDockNodeFlags_NoResize                     = 1 << 5,   // Saved // Disable resizing node using the splitter/separators. Useful with programmatically setup dockspaces.\n    ImGuiDockNodeFlags_AutoHideTabBar               = 1 << 6,   //       // Tab bar will automatically hide when there is a single window in the dock node.\n    ImGuiDockNodeFlags_NoUndocking                  = 1 << 7,   //       // Disable undocking this node.\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiDockNodeFlags_NoSplit                      = ImGuiDockNodeFlags_NoDockingSplit, // Renamed in 1.90\n    ImGuiDockNodeFlags_NoDockingInCentralNode       = ImGuiDockNodeFlags_NoDockingOverCentralNode, // Renamed in 1.90\n#endif\n};\n\n// Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload()\nenum ImGuiDragDropFlags_\n{\n    ImGuiDragDropFlags_None                         = 0,\n    // BeginDragDropSource() flags\n    ImGuiDragDropFlags_SourceNoPreviewTooltip       = 1 << 0,   // Disable preview tooltip. By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disables this behavior.\n    ImGuiDragDropFlags_SourceNoDisableHover         = 1 << 1,   // By default, when dragging we clear data so that IsItemHovered() will return false, to avoid subsequent user code submitting tooltips. This flag disables this behavior so you can still call IsItemHovered() on the source item.\n    ImGuiDragDropFlags_SourceNoHoldToOpenOthers     = 1 << 2,   // Disable the behavior that allows to open tree nodes and collapsing header by holding over them while dragging a source item.\n    ImGuiDragDropFlags_SourceAllowNullID            = 1 << 3,   // Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit.\n    ImGuiDragDropFlags_SourceExtern                 = 1 << 4,   // External source (from outside of dear imgui), won't attempt to read current item/window info. Will always return true. Only one Extern source can be active simultaneously.\n    ImGuiDragDropFlags_PayloadAutoExpire            = 1 << 5,   // Automatically expire the payload if the source cease to be submitted (otherwise payloads are persisting while being dragged)\n    ImGuiDragDropFlags_PayloadNoCrossContext        = 1 << 6,   // Hint to specify that the payload may not be copied outside current dear imgui context.\n    ImGuiDragDropFlags_PayloadNoCrossProcess        = 1 << 7,   // Hint to specify that the payload may not be copied outside current process.\n    // AcceptDragDropPayload() flags\n    ImGuiDragDropFlags_AcceptBeforeDelivery         = 1 << 10,  // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered.\n    ImGuiDragDropFlags_AcceptNoDrawDefaultRect      = 1 << 11,  // Do not draw the default highlight rectangle when hovering over target.\n    ImGuiDragDropFlags_AcceptNoPreviewTooltip       = 1 << 12,  // Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site.\n    ImGuiDragDropFlags_AcceptDrawAsHovered          = 1 << 13,  // Accepting item will render as if hovered. Useful for e.g. a Button() used as a drop target.\n    ImGuiDragDropFlags_AcceptPeekOnly               = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect, // For peeking ahead and inspecting the payload before delivery.\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiDragDropFlags_SourceAutoExpirePayload = ImGuiDragDropFlags_PayloadAutoExpire, // Renamed in 1.90.9\n#endif\n};\n\n// Standard Drag and Drop payload types. You can define you own payload types using short strings. Types starting with '_' are defined by Dear ImGui.\n#define IMGUI_PAYLOAD_TYPE_COLOR_3F     \"_COL3F\"    // float[3]: Standard type for colors, without alpha. User code may use this type.\n#define IMGUI_PAYLOAD_TYPE_COLOR_4F     \"_COL4F\"    // float[4]: Standard type for colors. User code may use this type.\n\n// A primary data type\nenum ImGuiDataType_\n{\n    ImGuiDataType_S8,       // signed char / char (with sensible compilers)\n    ImGuiDataType_U8,       // unsigned char\n    ImGuiDataType_S16,      // short\n    ImGuiDataType_U16,      // unsigned short\n    ImGuiDataType_S32,      // int\n    ImGuiDataType_U32,      // unsigned int\n    ImGuiDataType_S64,      // long long / __int64\n    ImGuiDataType_U64,      // unsigned long long / unsigned __int64\n    ImGuiDataType_Float,    // float\n    ImGuiDataType_Double,   // double\n    ImGuiDataType_Bool,     // bool (provided for user convenience, not supported by scalar widgets)\n    ImGuiDataType_String,   // char* (provided for user convenience, not supported by scalar widgets)\n    ImGuiDataType_COUNT\n};\n\n// A cardinal direction\nenum ImGuiDir : int\n{\n    ImGuiDir_None    = -1,\n    ImGuiDir_Left    = 0,\n    ImGuiDir_Right   = 1,\n    ImGuiDir_Up      = 2,\n    ImGuiDir_Down    = 3,\n    ImGuiDir_COUNT\n};\n\n// A sorting direction\nenum ImGuiSortDirection : ImU8\n{\n    ImGuiSortDirection_None         = 0,\n    ImGuiSortDirection_Ascending    = 1,    // Ascending = 0->9, A->Z etc.\n    ImGuiSortDirection_Descending   = 2     // Descending = 9->0, Z->A etc.\n};\n\n// A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value): can represent Keyboard, Mouse and Gamepad values.\n// All our named keys are >= 512. Keys value 0 to 511 are left unused and were legacy native/opaque key values (< 1.87).\n// Support for legacy keys was completely removed in 1.91.5.\n// Read details about the 1.87+ transition : https://github.com/ocornut/imgui/issues/4921\n// Note that \"Keys\" related to physical keys and are not the same concept as input \"Characters\", the latter are submitted via io.AddInputCharacter().\n// The keyboard key enum values are named after the keys on a standard US keyboard, and on other keyboard types the keys reported may not match the keycaps.\nenum ImGuiKey : int\n{\n    // Keyboard\n    ImGuiKey_None = 0,\n    ImGuiKey_NamedKey_BEGIN = 512,  // First valid key value (other than 0)\n\n    ImGuiKey_Tab = 512,             // == ImGuiKey_NamedKey_BEGIN\n    ImGuiKey_LeftArrow,\n    ImGuiKey_RightArrow,\n    ImGuiKey_UpArrow,\n    ImGuiKey_DownArrow,\n    ImGuiKey_PageUp,\n    ImGuiKey_PageDown,\n    ImGuiKey_Home,\n    ImGuiKey_End,\n    ImGuiKey_Insert,\n    ImGuiKey_Delete,\n    ImGuiKey_Backspace,\n    ImGuiKey_Space,\n    ImGuiKey_Enter,\n    ImGuiKey_Escape,\n    ImGuiKey_LeftCtrl, ImGuiKey_LeftShift, ImGuiKey_LeftAlt, ImGuiKey_LeftSuper,     // Also see ImGuiMod_Ctrl, ImGuiMod_Shift, ImGuiMod_Alt, ImGuiMod_Super below!\n    ImGuiKey_RightCtrl, ImGuiKey_RightShift, ImGuiKey_RightAlt, ImGuiKey_RightSuper,\n    ImGuiKey_Menu,\n    ImGuiKey_0, ImGuiKey_1, ImGuiKey_2, ImGuiKey_3, ImGuiKey_4, ImGuiKey_5, ImGuiKey_6, ImGuiKey_7, ImGuiKey_8, ImGuiKey_9,\n    ImGuiKey_A, ImGuiKey_B, ImGuiKey_C, ImGuiKey_D, ImGuiKey_E, ImGuiKey_F, ImGuiKey_G, ImGuiKey_H, ImGuiKey_I, ImGuiKey_J,\n    ImGuiKey_K, ImGuiKey_L, ImGuiKey_M, ImGuiKey_N, ImGuiKey_O, ImGuiKey_P, ImGuiKey_Q, ImGuiKey_R, ImGuiKey_S, ImGuiKey_T,\n    ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z,\n    ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6,\n    ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12,\n    ImGuiKey_F13, ImGuiKey_F14, ImGuiKey_F15, ImGuiKey_F16, ImGuiKey_F17, ImGuiKey_F18,\n    ImGuiKey_F19, ImGuiKey_F20, ImGuiKey_F21, ImGuiKey_F22, ImGuiKey_F23, ImGuiKey_F24,\n    ImGuiKey_Apostrophe,        // '\n    ImGuiKey_Comma,             // ,\n    ImGuiKey_Minus,             // -\n    ImGuiKey_Period,            // .\n    ImGuiKey_Slash,             // /\n    ImGuiKey_Semicolon,         // ;\n    ImGuiKey_Equal,             // =\n    ImGuiKey_LeftBracket,       // [\n    ImGuiKey_Backslash,         // \\ (this text inhibit multiline comment caused by backslash)\n    ImGuiKey_RightBracket,      // ]\n    ImGuiKey_GraveAccent,       // `\n    ImGuiKey_CapsLock,\n    ImGuiKey_ScrollLock,\n    ImGuiKey_NumLock,\n    ImGuiKey_PrintScreen,\n    ImGuiKey_Pause,\n    ImGuiKey_Keypad0, ImGuiKey_Keypad1, ImGuiKey_Keypad2, ImGuiKey_Keypad3, ImGuiKey_Keypad4,\n    ImGuiKey_Keypad5, ImGuiKey_Keypad6, ImGuiKey_Keypad7, ImGuiKey_Keypad8, ImGuiKey_Keypad9,\n    ImGuiKey_KeypadDecimal,\n    ImGuiKey_KeypadDivide,\n    ImGuiKey_KeypadMultiply,\n    ImGuiKey_KeypadSubtract,\n    ImGuiKey_KeypadAdd,\n    ImGuiKey_KeypadEnter,\n    ImGuiKey_KeypadEqual,\n    ImGuiKey_AppBack,               // Available on some keyboard/mouses. Often referred as \"Browser Back\"\n    ImGuiKey_AppForward,\n    ImGuiKey_Oem102,                // Non-US backslash.\n\n    // Gamepad\n    // (analog values are 0.0f to 1.0f)\n    // (download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets)\n    //                              // XBOX        | SWITCH  | PLAYSTA. | -> ACTION\n    ImGuiKey_GamepadStart,          // Menu        | +       | Options  |\n    ImGuiKey_GamepadBack,           // View        | -       | Share    |\n    ImGuiKey_GamepadFaceLeft,       // X           | Y       | Square   | Toggle Menu. Hold for Windowing mode (Focus/Move/Resize windows)\n    ImGuiKey_GamepadFaceRight,      // B           | A       | Circle   | Cancel / Close / Exit\n    ImGuiKey_GamepadFaceUp,         // Y           | X       | Triangle | Open Context Menu\n    ImGuiKey_GamepadFaceDown,       // A           | B       | Cross    | Activate / Open / Toggle. Hold for 0.60f to Activate in Text Input mode (e.g. wired to an on-screen keyboard).\n    ImGuiKey_GamepadDpadLeft,       // D-pad Left  | \"       | \"        | Move / Tweak / Resize Window (in Windowing mode)\n    ImGuiKey_GamepadDpadRight,      // D-pad Right | \"       | \"        | Move / Tweak / Resize Window (in Windowing mode)\n    ImGuiKey_GamepadDpadUp,         // D-pad Up    | \"       | \"        | Move / Tweak / Resize Window (in Windowing mode)\n    ImGuiKey_GamepadDpadDown,       // D-pad Down  | \"       | \"        | Move / Tweak / Resize Window (in Windowing mode)\n    ImGuiKey_GamepadL1,             // L Bumper    | L       | L1       | Tweak Slower / Focus Previous (in Windowing mode)\n    ImGuiKey_GamepadR1,             // R Bumper    | R       | R1       | Tweak Faster / Focus Next (in Windowing mode)\n    ImGuiKey_GamepadL2,             // L Trigger   | ZL      | L2       | [Analog]\n    ImGuiKey_GamepadR2,             // R Trigger   | ZR      | R2       | [Analog]\n    ImGuiKey_GamepadL3,             // L Stick     | L3      | L3       |\n    ImGuiKey_GamepadR3,             // R Stick     | R3      | R3       |\n    ImGuiKey_GamepadLStickLeft,     //             |         |          | [Analog] Move Window (in Windowing mode)\n    ImGuiKey_GamepadLStickRight,    //             |         |          | [Analog] Move Window (in Windowing mode)\n    ImGuiKey_GamepadLStickUp,       //             |         |          | [Analog] Move Window (in Windowing mode)\n    ImGuiKey_GamepadLStickDown,     //             |         |          | [Analog] Move Window (in Windowing mode)\n    ImGuiKey_GamepadRStickLeft,     //             |         |          | [Analog]\n    ImGuiKey_GamepadRStickRight,    //             |         |          | [Analog]\n    ImGuiKey_GamepadRStickUp,       //             |         |          | [Analog]\n    ImGuiKey_GamepadRStickDown,     //             |         |          | [Analog]\n\n    // Aliases: Mouse Buttons (auto-submitted from AddMouseButtonEvent() calls)\n    // - This is mirroring the data also written to io.MouseDown[], io.MouseWheel, in a format allowing them to be accessed via standard key API.\n    ImGuiKey_MouseLeft, ImGuiKey_MouseRight, ImGuiKey_MouseMiddle, ImGuiKey_MouseX1, ImGuiKey_MouseX2, ImGuiKey_MouseWheelX, ImGuiKey_MouseWheelY,\n\n    // [Internal] Reserved for mod storage\n    ImGuiKey_ReservedForModCtrl, ImGuiKey_ReservedForModShift, ImGuiKey_ReservedForModAlt, ImGuiKey_ReservedForModSuper,\n\n    // [Internal] If you need to iterate all keys (for e.g. an input mapper) you may use ImGuiKey_NamedKey_BEGIN..ImGuiKey_NamedKey_END.\n    ImGuiKey_NamedKey_END,\n    ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN,\n\n    // Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls)\n    // - Any functions taking a ImGuiKeyChord parameter can binary-or those with regular keys, e.g. Shortcut(ImGuiMod_Ctrl | ImGuiKey_S).\n    // - Those are written back into io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper for convenience,\n    //   but may be accessed via standard key API such as IsKeyPressed(), IsKeyReleased(), querying duration etc.\n    // - Code polling every key (e.g. an interface to detect a key press for input mapping) might want to ignore those\n    //   and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiMod_Ctrl).\n    // - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys.\n    //   In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and\n    //   backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user...\n    // - On macOS, we swap Cmd(Super) and Ctrl keys at the time of the io.AddKeyEvent() call.\n    ImGuiMod_None                   = 0,\n    ImGuiMod_Ctrl                   = 1 << 12, // Ctrl (non-macOS), Cmd (macOS)\n    ImGuiMod_Shift                  = 1 << 13, // Shift\n    ImGuiMod_Alt                    = 1 << 14, // Option/Menu\n    ImGuiMod_Super                  = 1 << 15, // Windows/Super (non-macOS), Ctrl (macOS)\n    ImGuiMod_Mask_                  = 0xF000,  // 4-bits\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiKey_COUNT                  = ImGuiKey_NamedKey_END,    // Obsoleted in 1.91.5 because it was misleading (since named keys don't start at 0 anymore)\n    ImGuiMod_Shortcut               = ImGuiMod_Ctrl,            // Removed in 1.90.7, you can now simply use ImGuiMod_Ctrl\n    //ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89\n    //ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter,              // Renamed in 1.87\n#endif\n};\n\n// Flags for Shortcut(), SetNextItemShortcut(),\n// (and for upcoming extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner() that are still in imgui_internal.h)\n// Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function)\nenum ImGuiInputFlags_\n{\n    ImGuiInputFlags_None                    = 0,\n    ImGuiInputFlags_Repeat                  = 1 << 0,   // Enable repeat. Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1.\n\n    // Flags for Shortcut(), SetNextItemShortcut()\n    // - Routing policies: RouteGlobal+OverActive >> RouteActive or RouteFocused (if owner is active item) >> RouteGlobal+OverFocused >> RouteFocused (if in focused window stack) >> RouteGlobal.\n    // - Default policy is RouteFocused. Can select only 1 policy among all available.\n    ImGuiInputFlags_RouteActive             = 1 << 10,  // Route to active item only.\n    ImGuiInputFlags_RouteFocused            = 1 << 11,  // Route to windows in the focus stack (DEFAULT). Deep-most focused window takes inputs. Active item takes inputs over deep-most focused window.\n    ImGuiInputFlags_RouteGlobal             = 1 << 12,  // Global route (unless a focused window or active item registered the route).\n    ImGuiInputFlags_RouteAlways             = 1 << 13,  // Do not register route, poll keys directly.\n    // - Routing options\n    ImGuiInputFlags_RouteOverFocused        = 1 << 14,  // Option: global route: higher priority than focused route (unless active item in focused route).\n    ImGuiInputFlags_RouteOverActive         = 1 << 15,  // Option: global route: higher priority than active item. Unlikely you need to use that: will interfere with every active items, e.g. Ctrl+A registered by InputText will be overridden by this. May not be fully honored as user/internal code is likely to always assume they can access keys when active.\n    ImGuiInputFlags_RouteUnlessBgFocused    = 1 << 16,  // Option: global route: will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications.\n    ImGuiInputFlags_RouteFromRootWindow     = 1 << 17,  // Option: route evaluated from the point of view of root window rather than current window.\n\n    // Flags for SetNextItemShortcut()\n    ImGuiInputFlags_Tooltip                 = 1 << 18,  // Automatically display a tooltip when hovering item [BETA] Unsure of right api (opt-in/opt-out)\n};\n\n// Configuration flags stored in io.ConfigFlags. Set by user/application.\nenum ImGuiConfigFlags_\n{\n    ImGuiConfigFlags_None                   = 0,\n    ImGuiConfigFlags_NavEnableKeyboard      = 1 << 0,   // Master keyboard navigation enable flag. Enable full Tabbing + directional arrows + Space/Enter to activate. Note: some features such as basic Tabbing and CtrL+Tab are enabled by regardless of this flag (and may be disabled via other means, see #4828, #9218).\n    ImGuiConfigFlags_NavEnableGamepad       = 1 << 1,   // Master gamepad navigation enable flag. Backend also needs to set ImGuiBackendFlags_HasGamepad.\n    ImGuiConfigFlags_NoMouse                = 1 << 4,   // Instruct dear imgui to disable mouse inputs and interactions.\n    ImGuiConfigFlags_NoMouseCursorChange    = 1 << 5,   // Instruct backend to not alter mouse cursor shape and visibility. Use if the backend cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead.\n    ImGuiConfigFlags_NoKeyboard             = 1 << 6,   // Instruct dear imgui to disable keyboard inputs and interactions. This is done by ignoring keyboard events and clearing existing states.\n\n    // [BETA] Docking\n    ImGuiConfigFlags_DockingEnable          = 1 << 7,   // Docking enable flags.\n\n    // [BETA] Viewports\n    // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable.\n    ImGuiConfigFlags_ViewportsEnable        = 1 << 10,  // Viewport enable flags (require both ImGuiBackendFlags_PlatformHasViewports + ImGuiBackendFlags_RendererHasViewports set by the respective backends)\n\n    // User storage (to allow your backend/engine to communicate to code that may be shared between multiple projects. Those flags are NOT used by core Dear ImGui)\n    ImGuiConfigFlags_IsSRGB                 = 1 << 20,  // Application is SRGB-aware.\n    ImGuiConfigFlags_IsTouchScreen          = 1 << 21,  // Application is using a touch screen instead of a mouse.\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiConfigFlags_NavEnableSetMousePos   = 1 << 2,   // [moved/renamed in 1.91.4] -> use bool io.ConfigNavMoveSetMousePos\n    ImGuiConfigFlags_NavNoCaptureKeyboard   = 1 << 3,   // [moved/renamed in 1.91.4] -> use bool io.ConfigNavCaptureKeyboard\n    ImGuiConfigFlags_DpiEnableScaleFonts    = 1 << 14,  // [moved/renamed in 1.92.0] -> use bool io.ConfigDpiScaleFonts\n    ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 15,  // [moved/renamed in 1.92.0] -> use bool io.ConfigDpiScaleViewports\n#endif\n};\n\n// Backend capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom backend.\nenum ImGuiBackendFlags_\n{\n    ImGuiBackendFlags_None                  = 0,\n    ImGuiBackendFlags_HasGamepad            = 1 << 0,   // Backend Platform supports gamepad and currently has one connected.\n    ImGuiBackendFlags_HasMouseCursors       = 1 << 1,   // Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape.\n    ImGuiBackendFlags_HasSetMousePos        = 1 << 2,   // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if io.ConfigNavMoveSetMousePos is set).\n    ImGuiBackendFlags_RendererHasVtxOffset  = 1 << 3,   // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices.\n    ImGuiBackendFlags_RendererHasTextures   = 1 << 4,   // Backend Renderer supports ImTextureData requests to create/update/destroy textures. This enables incremental texture updates and texture reloads. See https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md for instructions on how to upgrade your custom backend.\n\n    // [BETA] Multi-Viewports\n    ImGuiBackendFlags_RendererHasViewports  = 1 << 10,  // Backend Renderer supports multiple viewports.\n    ImGuiBackendFlags_PlatformHasViewports  = 1 << 11,  // Backend Platform supports multiple viewports.\n    ImGuiBackendFlags_HasMouseHoveredViewport=1 << 12,  // Backend Platform supports calling io.AddMouseViewportEvent() with the viewport under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags_NoInputs flag (Win32 backend, GLFW 3.30+ backend can do this, SDL backend cannot). If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under.\n    ImGuiBackendFlags_HasParentViewport     = 1 << 13,  // Backend Platform supports honoring viewport->ParentViewport/ParentViewportId value, by applying the corresponding parent/child relation at the Platform level.\n};\n\n// Enumeration for PushStyleColor() / PopStyleColor()\nenum ImGuiCol_\n{\n    ImGuiCol_Text,\n    ImGuiCol_TextDisabled,\n    ImGuiCol_WindowBg,              // Background of normal windows\n    ImGuiCol_ChildBg,               // Background of child windows\n    ImGuiCol_PopupBg,               // Background of popups, menus, tooltips windows\n    ImGuiCol_Border,\n    ImGuiCol_BorderShadow,\n    ImGuiCol_FrameBg,               // Background of checkbox, radio button, plot, slider, text input\n    ImGuiCol_FrameBgHovered,\n    ImGuiCol_FrameBgActive,\n    ImGuiCol_TitleBg,               // Title bar\n    ImGuiCol_TitleBgActive,         // Title bar when focused\n    ImGuiCol_TitleBgCollapsed,      // Title bar when collapsed\n    ImGuiCol_MenuBarBg,\n    ImGuiCol_ScrollbarBg,\n    ImGuiCol_ScrollbarGrab,\n    ImGuiCol_ScrollbarGrabHovered,\n    ImGuiCol_ScrollbarGrabActive,\n    ImGuiCol_CheckMark,             // Checkbox tick and RadioButton circle\n    ImGuiCol_SliderGrab,\n    ImGuiCol_SliderGrabActive,\n    ImGuiCol_Button,\n    ImGuiCol_ButtonHovered,\n    ImGuiCol_ButtonActive,\n    ImGuiCol_Header,                // Header* colors are used for CollapsingHeader, TreeNode, Selectable, MenuItem\n    ImGuiCol_HeaderHovered,\n    ImGuiCol_HeaderActive,\n    ImGuiCol_Separator,\n    ImGuiCol_SeparatorHovered,\n    ImGuiCol_SeparatorActive,\n    ImGuiCol_ResizeGrip,            // Resize grip in lower-right and lower-left corners of windows.\n    ImGuiCol_ResizeGripHovered,\n    ImGuiCol_ResizeGripActive,\n    ImGuiCol_InputTextCursor,       // InputText cursor/caret\n    ImGuiCol_TabHovered,            // Tab background, when hovered\n    ImGuiCol_Tab,                   // Tab background, when tab-bar is focused & tab is unselected\n    ImGuiCol_TabSelected,           // Tab background, when tab-bar is focused & tab is selected\n    ImGuiCol_TabSelectedOverline,   // Tab horizontal overline, when tab-bar is focused & tab is selected\n    ImGuiCol_TabDimmed,             // Tab background, when tab-bar is unfocused & tab is unselected\n    ImGuiCol_TabDimmedSelected,     // Tab background, when tab-bar is unfocused & tab is selected\n    ImGuiCol_TabDimmedSelectedOverline,//..horizontal overline, when tab-bar is unfocused & tab is selected\n    ImGuiCol_DockingPreview,        // Preview overlay color when about to docking something\n    ImGuiCol_DockingEmptyBg,        // Background color for empty node (e.g. CentralNode with no window docked into it)\n    ImGuiCol_PlotLines,\n    ImGuiCol_PlotLinesHovered,\n    ImGuiCol_PlotHistogram,\n    ImGuiCol_PlotHistogramHovered,\n    ImGuiCol_TableHeaderBg,         // Table header background\n    ImGuiCol_TableBorderStrong,     // Table outer and header borders (prefer using Alpha=1.0 here)\n    ImGuiCol_TableBorderLight,      // Table inner borders (prefer using Alpha=1.0 here)\n    ImGuiCol_TableRowBg,            // Table row background (even rows)\n    ImGuiCol_TableRowBgAlt,         // Table row background (odd rows)\n    ImGuiCol_TextLink,              // Hyperlink color\n    ImGuiCol_TextSelectedBg,        // Selected text inside an InputText\n    ImGuiCol_TreeLines,             // Tree node hierarchy outlines when using ImGuiTreeNodeFlags_DrawLines\n    ImGuiCol_DragDropTarget,        // Rectangle border highlighting a drop target\n    ImGuiCol_DragDropTargetBg,      // Rectangle background highlighting a drop target\n    ImGuiCol_UnsavedMarker,         // Unsaved Document marker (in window title and tabs)\n    ImGuiCol_NavCursor,             // Color of keyboard/gamepad navigation cursor/rectangle, when visible\n    ImGuiCol_NavWindowingHighlight, // Highlight window when using Ctrl+Tab\n    ImGuiCol_NavWindowingDimBg,     // Darken/colorize entire screen behind the Ctrl+Tab window list, when active\n    ImGuiCol_ModalWindowDimBg,      // Darken/colorize entire screen behind a modal window, when one is active\n    ImGuiCol_WindowShadow,          // Window shadows\n    ImGuiCol_COUNT,\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiCol_TabActive = ImGuiCol_TabSelected,                  // [renamed in 1.90.9]\n    ImGuiCol_TabUnfocused = ImGuiCol_TabDimmed,                 // [renamed in 1.90.9]\n    ImGuiCol_TabUnfocusedActive = ImGuiCol_TabDimmedSelected,   // [renamed in 1.90.9]\n    ImGuiCol_NavHighlight = ImGuiCol_NavCursor,                 // [renamed in 1.91.4]\n#endif\n};\n\n// Enumeration for PushStyleVar() / PopStyleVar() to temporarily modify the ImGuiStyle structure.\n// - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code.\n//   During initialization or between frames, feel free to just poke into ImGuiStyle directly.\n// - Tip: Use your programming IDE navigation facilities on the names in the _second column_ below to find the actual members and their description.\n//   - In Visual Studio: Ctrl+Comma (\"Edit.GoToAll\") can follow symbols inside comments, whereas Ctrl+F12 (\"Edit.GoToImplementation\") cannot.\n//   - In Visual Studio w/ Visual Assist installed: Alt+G (\"VAssistX.GoToImplementation\") can also follow symbols inside comments.\n//   - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.\n// - When changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type.\nenum ImGuiStyleVar_\n{\n    // Enum name -------------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions)\n    ImGuiStyleVar_Alpha,                    // float     Alpha\n    ImGuiStyleVar_DisabledAlpha,            // float     DisabledAlpha\n    ImGuiStyleVar_WindowPadding,            // ImVec2    WindowPadding\n    ImGuiStyleVar_WindowRounding,           // float     WindowRounding\n    ImGuiStyleVar_WindowBorderSize,         // float     WindowBorderSize\n    ImGuiStyleVar_WindowMinSize,            // ImVec2    WindowMinSize\n    ImGuiStyleVar_WindowTitleAlign,         // ImVec2    WindowTitleAlign\n    ImGuiStyleVar_ChildRounding,            // float     ChildRounding\n    ImGuiStyleVar_ChildBorderSize,          // float     ChildBorderSize\n    ImGuiStyleVar_PopupRounding,            // float     PopupRounding\n    ImGuiStyleVar_PopupBorderSize,          // float     PopupBorderSize\n    ImGuiStyleVar_FramePadding,             // ImVec2    FramePadding\n    ImGuiStyleVar_FrameRounding,            // float     FrameRounding\n    ImGuiStyleVar_FrameBorderSize,          // float     FrameBorderSize\n    ImGuiStyleVar_ItemSpacing,              // ImVec2    ItemSpacing\n    ImGuiStyleVar_ItemInnerSpacing,         // ImVec2    ItemInnerSpacing\n    ImGuiStyleVar_IndentSpacing,            // float     IndentSpacing\n    ImGuiStyleVar_CellPadding,              // ImVec2    CellPadding\n    ImGuiStyleVar_ScrollbarSize,            // float     ScrollbarSize\n    ImGuiStyleVar_ScrollbarRounding,        // float     ScrollbarRounding\n    ImGuiStyleVar_ScrollbarPadding,         // float     ScrollbarPadding\n    ImGuiStyleVar_GrabMinSize,              // float     GrabMinSize\n    ImGuiStyleVar_GrabRounding,             // float     GrabRounding\n    ImGuiStyleVar_ImageRounding,            // float     ImageRounding\n    ImGuiStyleVar_ImageBorderSize,          // float     ImageBorderSize\n    ImGuiStyleVar_TabRounding,              // float     TabRounding\n    ImGuiStyleVar_TabBorderSize,            // float     TabBorderSize\n    ImGuiStyleVar_TabMinWidthBase,          // float     TabMinWidthBase\n    ImGuiStyleVar_TabMinWidthShrink,        // float     TabMinWidthShrink\n    ImGuiStyleVar_TabBarBorderSize,         // float     TabBarBorderSize\n    ImGuiStyleVar_TabBarOverlineSize,       // float     TabBarOverlineSize\n    ImGuiStyleVar_TableAngledHeadersAngle,  // float     TableAngledHeadersAngle\n    ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2  TableAngledHeadersTextAlign\n    ImGuiStyleVar_TreeLinesSize,            // float     TreeLinesSize\n    ImGuiStyleVar_TreeLinesRounding,        // float     TreeLinesRounding\n    ImGuiStyleVar_ButtonTextAlign,          // ImVec2    ButtonTextAlign\n    ImGuiStyleVar_SelectableTextAlign,      // ImVec2    SelectableTextAlign\n    ImGuiStyleVar_SeparatorSize,            // float     SeparatorSize\n    ImGuiStyleVar_SeparatorTextBorderSize,  // float     SeparatorTextBorderSize\n    ImGuiStyleVar_SeparatorTextAlign,       // ImVec2    SeparatorTextAlign\n    ImGuiStyleVar_SeparatorTextPadding,     // ImVec2    SeparatorTextPadding\n    ImGuiStyleVar_DockingSeparatorSize,     // float     DockingSeparatorSize\n    ImGuiStyleVar_COUNT\n};\n\n// Flags for InvisibleButton() [extended in imgui_internal.h]\nenum ImGuiButtonFlags_\n{\n    ImGuiButtonFlags_None                   = 0,\n    ImGuiButtonFlags_MouseButtonLeft        = 1 << 0,   // React on left mouse button (default)\n    ImGuiButtonFlags_MouseButtonRight       = 1 << 1,   // React on right mouse button\n    ImGuiButtonFlags_MouseButtonMiddle      = 1 << 2,   // React on center mouse button\n    ImGuiButtonFlags_MouseButtonMask_       = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle, // [Internal]\n    ImGuiButtonFlags_EnableNav              = 1 << 3,   // InvisibleButton(): do not disable navigation/tabbing. Otherwise disabled by default.\n};\n\n// Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton()\nenum ImGuiColorEditFlags_\n{\n    ImGuiColorEditFlags_None            = 0,\n    ImGuiColorEditFlags_NoAlpha         = 1 << 1,   //              // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (will only read 3 components from the input pointer).\n    ImGuiColorEditFlags_NoPicker        = 1 << 2,   //              // ColorEdit: disable picker when clicking on color square.\n    ImGuiColorEditFlags_NoOptions       = 1 << 3,   //              // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview.\n    ImGuiColorEditFlags_NoSmallPreview  = 1 << 4,   //              // ColorEdit, ColorPicker: disable color square preview next to the inputs. (e.g. to show only the inputs)\n    ImGuiColorEditFlags_NoInputs        = 1 << 5,   //              // ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the small preview color square).\n    ImGuiColorEditFlags_NoTooltip       = 1 << 6,   //              // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview.\n    ImGuiColorEditFlags_NoLabel         = 1 << 7,   //              // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker).\n    ImGuiColorEditFlags_NoSidePreview   = 1 << 8,   //              // ColorPicker: disable bigger color preview on right side of the picker, use small color square preview instead.\n    ImGuiColorEditFlags_NoDragDrop      = 1 << 9,   //              // ColorEdit: disable drag and drop target/source. ColorButton: disable drag and drop source.\n    ImGuiColorEditFlags_NoBorder        = 1 << 10,  //              // ColorButton: disable border (which is enforced by default)\n    ImGuiColorEditFlags_NoColorMarkers  = 1 << 11,  //              // ColorEdit: disable rendering R/G/B/A color marker. May also be disabled globally by setting style.ColorMarkerSize = 0.\n\n    // Alpha preview\n    // - Prior to 1.91.8 (2025/01/21): alpha was made opaque in the preview by default using old name ImGuiColorEditFlags_AlphaPreview.\n    // - We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior.\n    // - The new flags may be combined better and allow finer controls.\n    ImGuiColorEditFlags_AlphaOpaque     = 1 << 12,  //              // ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha.\n    ImGuiColorEditFlags_AlphaNoBg       = 1 << 13,  //              // ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color.\n    ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 14,  //              // ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview.\n\n    // User Options (right-click on widget to change some of them).\n    ImGuiColorEditFlags_AlphaBar        = 1 << 18,  //              // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker.\n    ImGuiColorEditFlags_HDR             = 1 << 19,  //              // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well).\n    ImGuiColorEditFlags_DisplayRGB      = 1 << 20,  // [Display]    // ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex.\n    ImGuiColorEditFlags_DisplayHSV      = 1 << 21,  // [Display]    // \"\n    ImGuiColorEditFlags_DisplayHex      = 1 << 22,  // [Display]    // \"\n    ImGuiColorEditFlags_Uint8           = 1 << 23,  // [DataType]   // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255.\n    ImGuiColorEditFlags_Float           = 1 << 24,  // [DataType]   // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers.\n    ImGuiColorEditFlags_PickerHueBar    = 1 << 25,  // [Picker]     // ColorPicker: bar for Hue, rectangle for Sat/Value.\n    ImGuiColorEditFlags_PickerHueWheel  = 1 << 26,  // [Picker]     // ColorPicker: wheel for Hue, triangle for Sat/Value.\n    ImGuiColorEditFlags_InputRGB        = 1 << 27,  // [Input]      // ColorEdit, ColorPicker: input and output data in RGB format.\n    ImGuiColorEditFlags_InputHSV        = 1 << 28,  // [Input]      // ColorEdit, ColorPicker: input and output data in HSV format.\n\n    // Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to\n    // override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup.\n    ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar,\n\n    // [Internal] Masks\n    ImGuiColorEditFlags_AlphaMask_      = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque | ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf,\n    ImGuiColorEditFlags_DisplayMask_    = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex,\n    ImGuiColorEditFlags_DataTypeMask_   = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float,\n    ImGuiColorEditFlags_PickerMask_     = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar,\n    ImGuiColorEditFlags_InputMask_      = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV,\n\n    // Obsolete names\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiColorEditFlags_AlphaPreview = 0, // Removed in 1.91.8. This is the default now. Will display a checkerboard unless ImGuiColorEditFlags_AlphaNoBg is set.\n#endif\n    //ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex  // [renamed in 1.69]\n};\n\n// Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.\n// We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.\n// (Those are per-item flags. There is shared behavior flag too: ImGuiIO: io.ConfigDragClickToInputText)\nenum ImGuiSliderFlags_\n{\n    ImGuiSliderFlags_None               = 0,\n    ImGuiSliderFlags_Logarithmic        = 1 << 5,       // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits.\n    ImGuiSliderFlags_NoRoundToFormat    = 1 << 6,       // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits).\n    ImGuiSliderFlags_NoInput            = 1 << 7,       // Disable Ctrl+Click or Enter key allowing to input text directly into the widget.\n    ImGuiSliderFlags_WrapAround         = 1 << 8,       // Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now.\n    ImGuiSliderFlags_ClampOnInput       = 1 << 9,       // Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds.\n    ImGuiSliderFlags_ClampZeroRange     = 1 << 10,      // Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it.\n    ImGuiSliderFlags_NoSpeedTweaks      = 1 << 11,      // Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.\n    ImGuiSliderFlags_ColorMarkers       = 1 << 12,      // DragScalarN(), SliderScalarN(): Draw R/G/B/A color markers on each component.\n    ImGuiSliderFlags_AlwaysClamp        = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange,\n    ImGuiSliderFlags_InvalidMask_       = 0x7000000F,   // [Internal] We treat using those bits as being potentially a 'float power' argument from legacy API (obsoleted 2020-08) that has got miscast to this enum, and will trigger an assert if needed.\n};\n\n// Identify a mouse button.\n// Those values are guaranteed to be stable and we frequently use 0/1 directly. Named enums provided for convenience.\nenum ImGuiMouseButton_\n{\n    ImGuiMouseButton_Left = 0,\n    ImGuiMouseButton_Right = 1,\n    ImGuiMouseButton_Middle = 2,\n    ImGuiMouseButton_COUNT = 5\n};\n\n// Enumeration for GetMouseCursor()\n// User code may request backend to display given cursor by calling SetMouseCursor(), which is why we have some cursors that are marked unused here\nenum ImGuiMouseCursor_\n{\n    ImGuiMouseCursor_None = -1,\n    ImGuiMouseCursor_Arrow = 0,\n    ImGuiMouseCursor_TextInput,         // When hovering over InputText, etc.\n    ImGuiMouseCursor_ResizeAll,         // (Unused by Dear ImGui functions)\n    ImGuiMouseCursor_ResizeNS,          // When hovering over a horizontal border\n    ImGuiMouseCursor_ResizeEW,          // When hovering over a vertical border or a column\n    ImGuiMouseCursor_ResizeNESW,        // When hovering over the bottom-left corner of a window\n    ImGuiMouseCursor_ResizeNWSE,        // When hovering over the bottom-right corner of a window\n    ImGuiMouseCursor_Hand,              // (Unused by Dear ImGui functions. Use for e.g. hyperlinks)\n    ImGuiMouseCursor_Wait,              // When waiting for something to process/load.\n    ImGuiMouseCursor_Progress,          // When waiting for something to process/load, but application is still interactive.\n    ImGuiMouseCursor_NotAllowed,        // When hovering something with disallowed interaction. Usually a crossed circle.\n    ImGuiMouseCursor_COUNT\n};\n\n// Enumeration for AddMouseSourceEvent() actual source of Mouse Input data.\n// Historically we use \"Mouse\" terminology everywhere to indicate pointer data, e.g. MousePos, IsMousePressed(), io.AddMousePosEvent()\n// But that \"Mouse\" data can come from different source which occasionally may be useful for application to know about.\n// You can submit a change of pointer type using io.AddMouseSourceEvent().\nenum ImGuiMouseSource : int\n{\n    ImGuiMouseSource_Mouse = 0,         // Input is coming from an actual mouse.\n    ImGuiMouseSource_TouchScreen,       // Input is coming from a touch screen (no hovering prior to initial press, less precise initial press aiming, dual-axis wheeling possible).\n    ImGuiMouseSource_Pen,               // Input is coming from a pressure/magnetic pen (often used in conjunction with high-sampling rates).\n    ImGuiMouseSource_COUNT\n};\n\n// Enumeration for ImGui::SetNextWindow***(), SetWindow***(), SetNextItem***() functions\n// Represent a condition.\n// Important: Treat as a regular enum! Do NOT combine multiple values using binary operators! All the functions above treat 0 as a shortcut to ImGuiCond_Always.\nenum ImGuiCond_\n{\n    ImGuiCond_None          = 0,        // No condition (always set the variable), same as _Always\n    ImGuiCond_Always        = 1 << 0,   // No condition (always set the variable), same as _None\n    ImGuiCond_Once          = 1 << 1,   // Set the variable once per runtime session (only the first call will succeed)\n    ImGuiCond_FirstUseEver  = 1 << 2,   // Set the variable if the object/window has no persistently saved data (no entry in .ini file)\n    ImGuiCond_Appearing     = 1 << 3,   // Set the variable if the object/window is appearing after being hidden/inactive (or the first time)\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)\n//-----------------------------------------------------------------------------\n\n// Flags for ImGui::BeginTable()\n// - Important! Sizing policies have complex and subtle side effects, much more so than you would expect.\n//   Read comments/demos carefully + experiment with live demos to get acquainted with them.\n// - The DEFAULT sizing policies are:\n//    - Default to ImGuiTableFlags_SizingFixedFit    if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize.\n//    - Default to ImGuiTableFlags_SizingStretchSame if ScrollX is off.\n// - When ScrollX is off:\n//    - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight.\n//    - Columns sizing policy allowed: Stretch (default), Fixed/Auto.\n//    - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all).\n//    - Stretch Columns will share the remaining width according to their respective weight.\n//    - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors.\n//      The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.\n//      (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing).\n// - When ScrollX is on:\n//    - Table defaults to ImGuiTableFlags_SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed\n//    - Columns sizing policy allowed: Fixed/Auto mostly.\n//    - Fixed Columns can be enlarged as needed. Table will show a horizontal scrollbar if needed.\n//    - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop.\n//    - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable().\n//      If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again.\n// - Read on documentation at the top of imgui_tables.cpp for details.\nenum ImGuiTableFlags_\n{\n    // Features\n    ImGuiTableFlags_None                       = 0,\n    ImGuiTableFlags_Resizable                  = 1 << 0,   // Enable resizing columns.\n    ImGuiTableFlags_Reorderable                = 1 << 1,   // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers)\n    ImGuiTableFlags_Hideable                   = 1 << 2,   // Enable hiding/disabling columns in context menu.\n    ImGuiTableFlags_Sortable                   = 1 << 3,   // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate.\n    ImGuiTableFlags_NoSavedSettings            = 1 << 4,   // Disable persisting columns order, width, visibility and sort settings in the .ini file.\n    ImGuiTableFlags_ContextMenuInBody          = 1 << 5,   // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow().\n    // Decorations\n    ImGuiTableFlags_RowBg                      = 1 << 6,   // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually)\n    ImGuiTableFlags_BordersInnerH              = 1 << 7,   // Draw horizontal borders between rows.\n    ImGuiTableFlags_BordersOuterH              = 1 << 8,   // Draw horizontal borders at the top and bottom.\n    ImGuiTableFlags_BordersInnerV              = 1 << 9,   // Draw vertical borders between columns.\n    ImGuiTableFlags_BordersOuterV              = 1 << 10,  // Draw vertical borders on the left and right sides.\n    ImGuiTableFlags_BordersH                   = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders.\n    ImGuiTableFlags_BordersV                   = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders.\n    ImGuiTableFlags_BordersInner               = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders.\n    ImGuiTableFlags_BordersOuter               = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders.\n    ImGuiTableFlags_Borders                    = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter,   // Draw all borders.\n    ImGuiTableFlags_NoBordersInBody            = 1 << 11,  // [ALPHA] Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style\n    ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12,  // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style\n    // Sizing Policy (read above for defaults)\n    ImGuiTableFlags_SizingFixedFit             = 1 << 13,  // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width.\n    ImGuiTableFlags_SizingFixedSame            = 2 << 13,  // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible.\n    ImGuiTableFlags_SizingStretchProp          = 3 << 13,  // Columns default to _WidthStretch with default weights proportional to each columns contents widths.\n    ImGuiTableFlags_SizingStretchSame          = 4 << 13,  // Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn().\n    // Sizing Extra Options\n    ImGuiTableFlags_NoHostExtendX              = 1 << 16,  // Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.\n    ImGuiTableFlags_NoHostExtendY              = 1 << 17,  // Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.\n    ImGuiTableFlags_NoKeepColumnsVisible       = 1 << 18,  // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable.\n    ImGuiTableFlags_PreciseWidths              = 1 << 19,  // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.\n    // Clipping\n    ImGuiTableFlags_NoClip                     = 1 << 20,  // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze().\n    // Padding\n    ImGuiTableFlags_PadOuterX                  = 1 << 21,  // Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers.\n    ImGuiTableFlags_NoPadOuterX                = 1 << 22,  // Default if BordersOuterV is off. Disable outermost padding.\n    ImGuiTableFlags_NoPadInnerX                = 1 << 23,  // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off).\n    // Scrolling\n    ImGuiTableFlags_ScrollX                    = 1 << 24,  // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX.\n    ImGuiTableFlags_ScrollY                    = 1 << 25,  // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size.\n    // Sorting\n    ImGuiTableFlags_SortMulti                  = 1 << 26,  // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).\n    ImGuiTableFlags_SortTristate               = 1 << 27,  // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).\n    // Miscellaneous\n    ImGuiTableFlags_HighlightHoveredColumn     = 1 << 28,  // Highlight column headers when hovered (may evolve into a fuller highlight)\n\n    // [Internal] Combinations and masks\n    ImGuiTableFlags_SizingMask_                = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame,\n};\n\n// Flags for ImGui::TableSetupColumn()\nenum ImGuiTableColumnFlags_\n{\n    // Input configuration flags\n    ImGuiTableColumnFlags_None                  = 0,\n    ImGuiTableColumnFlags_Disabled              = 1 << 0,   // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state)\n    ImGuiTableColumnFlags_DefaultHide           = 1 << 1,   // Default as a hidden/disabled column.\n    ImGuiTableColumnFlags_DefaultSort           = 1 << 2,   // Default as a sorting column.\n    ImGuiTableColumnFlags_WidthStretch          = 1 << 3,   // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp).\n    ImGuiTableColumnFlags_WidthFixed            = 1 << 4,   // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable).\n    ImGuiTableColumnFlags_NoResize              = 1 << 5,   // Disable manual resizing.\n    ImGuiTableColumnFlags_NoReorder             = 1 << 6,   // Disable manual reordering this column, this will also prevent other columns from crossing over this column.\n    ImGuiTableColumnFlags_NoHide                = 1 << 7,   // Disable ability to hide/disable this column.\n    ImGuiTableColumnFlags_NoClip                = 1 << 8,   // Disable clipping for this column (all NoClip columns will render in a same draw command).\n    ImGuiTableColumnFlags_NoSort                = 1 << 9,   // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table).\n    ImGuiTableColumnFlags_NoSortAscending       = 1 << 10,  // Disable ability to sort in the ascending direction.\n    ImGuiTableColumnFlags_NoSortDescending      = 1 << 11,  // Disable ability to sort in the descending direction.\n    ImGuiTableColumnFlags_NoHeaderLabel         = 1 << 12,  // TableHeadersRow() will submit an empty label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers. You may append into this cell by calling TableSetColumnIndex() right after the TableHeadersRow() call.\n    ImGuiTableColumnFlags_NoHeaderWidth         = 1 << 13,  // Disable header text width contribution to automatic column width.\n    ImGuiTableColumnFlags_PreferSortAscending   = 1 << 14,  // Make the initial sort direction Ascending when first sorting on this column (default).\n    ImGuiTableColumnFlags_PreferSortDescending  = 1 << 15,  // Make the initial sort direction Descending when first sorting on this column.\n    ImGuiTableColumnFlags_IndentEnable          = 1 << 16,  // Use current Indent value when entering cell (default for column 0).\n    ImGuiTableColumnFlags_IndentDisable         = 1 << 17,  // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored.\n    ImGuiTableColumnFlags_AngledHeader          = 1 << 18,  // TableHeadersRow() will submit an angled header row for this column. Note this will add an extra row.\n\n    // Output status flags, read-only via TableGetColumnFlags()\n    ImGuiTableColumnFlags_IsEnabled             = 1 << 24,  // Status: is enabled == not hidden by user/api (referred to as \"Hide\" in _DefaultHide and _NoHide) flags.\n    ImGuiTableColumnFlags_IsVisible             = 1 << 25,  // Status: is visible == is enabled AND not clipped by scrolling.\n    ImGuiTableColumnFlags_IsSorted              = 1 << 26,  // Status: is currently part of the sort specs\n    ImGuiTableColumnFlags_IsHovered             = 1 << 27,  // Status: is hovered by mouse\n\n    // [Internal] Combinations and masks\n    ImGuiTableColumnFlags_WidthMask_            = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed,\n    ImGuiTableColumnFlags_IndentMask_           = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable,\n    ImGuiTableColumnFlags_StatusMask_           = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered,\n    ImGuiTableColumnFlags_NoDirectResize_       = 1 << 30,  // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge)\n};\n\n// Flags for ImGui::TableNextRow()\nenum ImGuiTableRowFlags_\n{\n    ImGuiTableRowFlags_None                     = 0,\n    ImGuiTableRowFlags_Headers                  = 1 << 0,   // Identify header row (set default background color + width of its contents accounted differently for auto column width)\n};\n\n// Enum for ImGui::TableSetBgColor()\n// Background colors are rendering in 3 layers:\n//  - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.\n//  - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.\n//  - Layer 2: draw with CellBg color if set.\n// The purpose of the two row/columns layers is to let you decide if a background color change should override or blend with the existing color.\n// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows.\n// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.\n// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.\nenum ImGuiTableBgTarget_\n{\n    ImGuiTableBgTarget_None                     = 0,\n    ImGuiTableBgTarget_RowBg0                   = 1,        // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used)\n    ImGuiTableBgTarget_RowBg1                   = 2,        // Set row background color 1 (generally used for selection marking)\n    ImGuiTableBgTarget_CellBg                   = 3,        // Set cell background color (top-most color)\n};\n\n// Sorting specifications for a table (often handling sort specs for a single column, occasionally more)\n// Obtained by calling TableGetSortSpecs().\n// When 'SpecsDirty == true' you can sort your data. It will be true with sorting specs have changed since last call, or the first time.\n// Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame!\nstruct ImGuiTableSortSpecs\n{\n    const ImGuiTableColumnSortSpecs* Specs;     // Pointer to sort spec array.\n    int                         SpecsCount;     // Sort spec count. Most often 1. May be > 1 when ImGuiTableFlags_SortMulti is enabled. May be == 0 when ImGuiTableFlags_SortTristate is enabled.\n    bool                        SpecsDirty;     // Set to true when specs have changed since last time! Use this to sort again, then clear the flag.\n\n    ImGuiTableSortSpecs()       { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// Sorting specification for one column of a table (sizeof == 12 bytes)\nstruct ImGuiTableColumnSortSpecs\n{\n    ImGuiID                     ColumnUserID;       // User id of the column (if specified by a TableSetupColumn() call)\n    ImS16                       ColumnIndex;        // Index of the column\n    ImS16                       SortOrder;          // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here)\n    ImGuiSortDirection          SortDirection;      // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending\n\n    ImGuiTableColumnSortSpecs() { memset((void*)this, 0, sizeof(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Helpers: Debug log, memory allocations macros, ImVector<>\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// Debug Logging into ShowDebugLogWindow(), tty and more.\n//-----------------------------------------------------------------------------\n\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n#define IMGUI_DEBUG_LOG(...)        ImGui::DebugLog(__VA_ARGS__)\n#else\n#define IMGUI_DEBUG_LOG(...)        ((void)0)\n#endif\n\n//-----------------------------------------------------------------------------\n// IM_MALLOC(), IM_FREE(), IM_NEW(), IM_PLACEMENT_NEW(), IM_DELETE()\n// We call C++ constructor on own allocated memory via the placement \"new(ptr) Type()\" syntax.\n// Defining a custom placement new() with a custom parameter allows us to bypass including <new> which on some platforms complains when user has disabled exceptions.\n//-----------------------------------------------------------------------------\n\nstruct ImNewWrapper {};\ninline void* operator new(size_t, ImNewWrapper, void* ptr) { return ptr; }\ninline void  operator delete(void*, ImNewWrapper, void*)   {} // This is only required so we can use the symmetrical new()\n#define IM_ALLOC(_SIZE)                     ImGui::MemAlloc(_SIZE)\n#define IM_FREE(_PTR)                       ImGui::MemFree(_PTR)\n#define IM_PLACEMENT_NEW(_PTR)              new(ImNewWrapper(), _PTR)\n#define IM_NEW(_TYPE)                       new(ImNewWrapper(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE\ntemplate<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }\n\n//-----------------------------------------------------------------------------\n// ImVector<>\n// Lightweight std::vector<>-like class to avoid dragging dependencies (also, some implementations of STL with debug enabled are absurdly slow, we bypass it so our code runs fast in debug).\n//-----------------------------------------------------------------------------\n// - You generally do NOT need to care or use this ever. But we need to make it available in imgui.h because some of our public structures are relying on it.\n// - We use std-like naming convention here, which is a little unusual for this codebase.\n// - Important: clear() frees memory, resize(0) keep the allocated buffer. We use resize(0) a lot to intentionally recycle allocated buffers across frames and amortize our costs.\n// - Important: our implementation does NOT call C++ constructors/destructors, we treat everything as raw data! This is intentional but be extra mindful of that,\n//   Do NOT use this class as a std::vector replacement in your own code! Many of the structures used by dear imgui can be safely initialized by a zero-memset.\n//-----------------------------------------------------------------------------\n\nIM_MSVC_RUNTIME_CHECKS_OFF\ntemplate<typename T>\nstruct ImVector\n{\n    int                 Size;\n    int                 Capacity;\n    T*                  Data;\n\n    // Provide standard typedefs but we don't use them ourselves.\n    typedef T                   value_type;\n    typedef value_type*         iterator;\n    typedef const value_type*   const_iterator;\n\n    // Constructors, destructor\n    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }\n    inline ImVector(const ImVector<T>& src)                 { Size = Capacity = 0; Data = NULL; operator=(src); }\n    inline ImVector<T>& operator=(const ImVector<T>& src)   { clear(); resize(src.Size); if (Data && src.Data) memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; }\n    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything\n\n    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything\n    inline void         clear_delete()                      { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); }     // Important: never called automatically! always explicit.\n    inline void         clear_destruct()                    { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); }           // Important: never called automatically! always explicit.\n\n    inline bool         empty() const                       { return Size == 0; }\n    inline int          size() const                        { return Size; }\n    inline int          size_in_bytes() const               { return Size * (int)sizeof(T); }\n    inline int          max_size() const                    { return 0x7FFFFFFF / (int)sizeof(T); }\n    inline int          capacity() const                    { return Capacity; }\n    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }\n    inline const T&     operator[](int i) const             { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }\n\n    inline T*           begin()                             { return Data; }\n    inline const T*     begin() const                       { return Data; }\n    inline T*           end()                               { return Data + Size; }\n    inline const T*     end() const                         { return Data + Size; }\n    inline T&           front()                             { IM_ASSERT(Size > 0); return Data[0]; }\n    inline const T&     front() const                       { IM_ASSERT(Size > 0); return Data[0]; }\n    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }\n    inline const T&     back() const                        { IM_ASSERT(Size > 0); return Data[Size - 1]; }\n    inline void         swap(ImVector<T>& rhs)              { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; T* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; }\n\n    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }\n    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }\n    inline void         resize(int new_size, const T& v)    { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; }\n    inline void         shrink(int new_size)                { IM_ASSERT(new_size <= Size); Size = new_size; } // Resize a vector to a smaller size, guaranteed not to cause a reallocation\n    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }\n    inline void         reserve_discard(int new_capacity)   { if (new_capacity <= Capacity) return; if (Data) IM_FREE(Data); Data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); Capacity = new_capacity; }\n\n    // NB: It is illegal to call push_back/push_front/insert with a reference pointing inside the ImVector data itself! e.g. v.push_back(v[10]) is forbidden.\n    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }\n    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }\n    inline void         push_front(const T& v)              { if (Size == 0) push_back(v); else insert(Data, v); }\n    inline T*           erase(const T* it)                  { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; }\n    inline T*           erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last >= it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - (size_t)count) * sizeof(T)); Size -= (int)count; return Data + off; }\n    inline T*           erase_unsorted(const T* it)         { IM_ASSERT(it >= Data && it < Data + Size);  const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; }\n    inline T*           insert(const T* it, const T& v)     { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; }\n    inline bool         contains(const T& v) const          { const T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; }\n    inline T*           find(const T& v)                    { T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }\n    inline const T*     find(const T& v) const              { const T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }\n    inline int          find_index(const T& v) const        { const T* data_end = Data + Size; const T* it = find(v); if (it == data_end) return -1; const ptrdiff_t off = it - Data; return (int)off; }\n    inline bool         find_erase(const T& v)              { const T* it = find(v); if (it < Data + Size) { erase(it); return true; } return false; }\n    inline bool         find_erase_unsorted(const T& v)     { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; }\n    inline int          index_from_ptr(const T* it) const   { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; }\n};\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiStyle\n//-----------------------------------------------------------------------------\n// You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame().\n// During the frame, use ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values,\n// and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors.\n//-----------------------------------------------------------------------------\n\nstruct ImGuiStyle\n{\n    // Font scaling\n    // - recap: ImGui::GetFontSize() == FontSizeBase * (FontScaleMain * FontScaleDpi * other_scaling_factors)\n    float       FontSizeBase;               // Current base font size before external global factors are applied. Use PushFont(NULL, size) to modify. Use ImGui::GetFontSize() to obtain scaled value.\n    float       FontScaleMain;              // Main global scale factor. May be set by application once, or exposed to end-user.\n    float       FontScaleDpi;               // Additional global scale factor from viewport/monitor contents scale. In docking branch: when io.ConfigDpiScaleFonts is enabled, this is automatically overwritten when changing monitor DPI.\n\n    float       Alpha;                      // Global alpha applies to everything in Dear ImGui.\n    float       DisabledAlpha;              // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.\n    ImVec2      WindowPadding;              // Padding within a window.\n    float       WindowRounding;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.\n    float       WindowBorderSize;           // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).\n    float       WindowBorderHoverPadding;   // Hit-testing extent outside/inside resizing border. Also extend determination of hovered window. Generally meaningfully larger than WindowBorderSize to make it easy to reach borders.\n    ImVec2      WindowMinSize;              // Minimum window size. This is a global setting. If you want to constrain individual windows, use SetNextWindowSizeConstraints().\n    ImVec2      WindowTitleAlign;           // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered.\n    ImGuiDir    WindowMenuButtonPosition;   // Side of the collapsing/docking button in the title bar (None/Left/Right). Defaults to ImGuiDir_Left.\n    float       ChildRounding;              // Radius of child window corners rounding. Set to 0.0f to have rectangular windows.\n    float       ChildBorderSize;            // Thickness of border around child windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).\n    float       PopupRounding;              // Radius of popup window corners rounding. (Note that tooltip windows use WindowRounding)\n    float       PopupBorderSize;            // Thickness of border around popup/tooltip windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).\n    ImVec2      FramePadding;               // Padding within a framed rectangle (used by most widgets).\n    float       FrameRounding;              // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets).\n    float       FrameBorderSize;            // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).\n    ImVec2      ItemSpacing;                // Horizontal and vertical spacing between widgets/lines.\n    ImVec2      ItemInnerSpacing;           // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label).\n    ImVec2      CellPadding;                // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.\n    ImVec2      TouchExtraPadding;          // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!\n    float       IndentSpacing;              // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).\n    float       ColumnsMinSpacing;          // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).\n    float       ScrollbarSize;              // Width of the vertical scrollbar, Height of the horizontal scrollbar.\n    float       ScrollbarRounding;          // Radius of grab corners for scrollbar.\n    float       ScrollbarPadding;           // Padding of scrollbar grab within its frame (same for both axes).\n    float       GrabMinSize;                // Minimum width/height of a grab box for slider/scrollbar.\n    float       GrabRounding;               // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.\n    float       LogSliderDeadzone;          // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.\n    float       ImageRounding;              // Rounding of Image() calls.\n    float       ImageBorderSize;            // Thickness of border around Image() calls.\n    float       TabRounding;                // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.\n    float       TabBorderSize;              // Thickness of border around tabs.\n    float       TabMinWidthBase;            // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected.\n    float       TabMinWidthShrink;          // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy.\n    float       TabCloseButtonMinWidthSelected;     // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width.\n    float       TabCloseButtonMinWidthUnselected;   // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected.\n    float       TabBarBorderSize;           // Thickness of tab-bar separator, which takes on the tab active color to denote focus.\n    float       TabBarOverlineSize;         // Thickness of tab-bar overline, which highlights the selected tab-bar.\n    float       TableAngledHeadersAngle;    // Angle of angled headers (supported values range from -50.0f degrees to +50.0f degrees).\n    ImVec2      TableAngledHeadersTextAlign;// Alignment of angled headers within the cell\n    ImGuiTreeNodeFlags TreeLinesFlags;      // Default way to draw lines connecting TreeNode hierarchy. ImGuiTreeNodeFlags_DrawLinesNone or ImGuiTreeNodeFlags_DrawLinesFull or ImGuiTreeNodeFlags_DrawLinesToNodes.\n    float       TreeLinesSize;              // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.\n    float       TreeLinesRounding;          // Radius of lines connecting child nodes to the vertical line.\n    float       DragDropTargetRounding;     // Radius of the drag and drop target frame.\n    float       DragDropTargetBorderSize;   // Thickness of the drag and drop target border.\n    float       DragDropTargetPadding;      // Size to expand the drag and drop target from actual target item size.\n    float       ColorMarkerSize;            // Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags_ColorMarkers.\n    ImGuiDir    ColorButtonPosition;        // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.\n    ImVec2      ButtonTextAlign;            // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered).\n    ImVec2      SelectableTextAlign;        // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.\n    float       SeparatorSize;              // Thickness of border in Separator()\n    float       SeparatorTextBorderSize;    // Thickness of border in SeparatorText()\n    ImVec2      SeparatorTextAlign;         // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).\n    ImVec2      SeparatorTextPadding;       // Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.\n    ImVec2      DisplayWindowPadding;       // Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen.\n    ImVec2      DisplaySafeAreaPadding;     // Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).\n    bool        DockingNodeHasCloseButton;  // Docking node has their own CloseButton() to close all docked windows.\n    float       DockingSeparatorSize;       // Thickness of resizing border between docked windows\n    float       MouseCursorScale;           // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). We apply per-monitor DPI scaling over this scale. May be removed later.\n    bool        AntiAliasedLines;           // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).\n    bool        AntiAliasedLinesUseTex;     // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). Latched at the beginning of the frame (copied to ImDrawList).\n    bool        AntiAliasedFill;            // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).\n    float       CurveTessellationTol;       // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.\n    float       CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.\n\n    float       WindowShadowSize;           // Size (in pixels) of window shadows. Set this to zero to disable shadows.\n    float       WindowShadowOffsetDist;     // Offset distance (in pixels) of window shadows from casting window.\n    float       WindowShadowOffsetAngle;    // Offset angle of window shadows from casting window (0.0f = left, 0.5f*PI = bottom, 1.0f*PI = right, 1.5f*PI = top).\n\n    // Colors\n    ImVec4      Colors[ImGuiCol_COUNT];\n\n    // Behaviors\n    // (It is possible to modify those fields mid-frame if specific behavior need it, unlike e.g. configuration fields in ImGuiIO)\n    float             HoverStationaryDelay;     // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.\n    float             HoverDelayShort;          // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.\n    float             HoverDelayNormal;         // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). \"\n    ImGuiHoveredFlags HoverFlagsForTooltipMouse;// Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.\n    ImGuiHoveredFlags HoverFlagsForTooltipNav;  // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.\n\n    // [Internal]\n    float       _MainScale;                 // FIXME-WIP: Reference scale, as applied by ScaleAllSizes(). PLEASE DO NOT USE THIS FOR NOW.\n    float       _NextFrameFontSizeBase;     // FIXME: Temporary hack until we finish remaining work.\n\n    // Functions\n    IMGUI_API   ImGuiStyle();\n    IMGUI_API   void ScaleAllSizes(float scale_factor); // Scale all spacing/padding/thickness values. Do not scale fonts.\n\n    // Obsolete names\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    // TabMinWidthForCloseButton = TabCloseButtonMinWidthUnselected // Renamed in 1.91.9.\n#endif\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiIO\n//-----------------------------------------------------------------------------\n// Communicate most settings and inputs/outputs to Dear ImGui using this structure.\n// Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage.\n// It is generally expected that:\n// - initialization: backends and user code writes to ImGuiIO.\n// - main loop: backends writes to ImGuiIO, user code and imgui code reads from ImGuiIO.\n//-----------------------------------------------------------------------------\n// Also see ImGui::GetPlatformIO() and ImGuiPlatformIO struct for OS/platform related functions: clipboard, IME etc.\n//-----------------------------------------------------------------------------\n\n// [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions.\n// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and *NOT* io.KeysData[key]->DownDuration.\nstruct ImGuiKeyData\n{\n    bool        Down;               // True for if key is down\n    float       DownDuration;       // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held)\n    float       DownDurationPrev;   // Last frame duration the key has been down\n    float       AnalogValue;        // 0.0f..1.0f for gamepad values\n};\n\nstruct ImGuiIO\n{\n    //------------------------------------------------------------------\n    // Configuration                            // Default value\n    //------------------------------------------------------------------\n\n    ImGuiConfigFlags   ConfigFlags;             // = 0              // See ImGuiConfigFlags_ enum. Set by user/application. Keyboard/Gamepad navigation options, etc.\n    ImGuiBackendFlags  BackendFlags;            // = 0              // See ImGuiBackendFlags_ enum. Set by backend (imgui_impl_xxx files or custom backend) to communicate features supported by the backend.\n    ImVec2      DisplaySize;                    // <unset>          // Main display size, in pixels (== GetMainViewport()->Size). May change every frame.\n    ImVec2      DisplayFramebufferScale;        // = (1, 1)         // Main display density. For retina display where window coordinates are different from framebuffer coordinates. This will affect font density + will end up in ImDrawData::FramebufferScale.\n    float       DeltaTime;                      // = 1.0f/60.0f     // Time elapsed since last frame, in seconds. May change every frame.\n    float       IniSavingRate;                  // = 5.0f           // Minimum time between saving positions/sizes to .ini file, in seconds.\n    const char* IniFilename;                    // = \"imgui.ini\"    // Path to .ini file (important: default \"imgui.ini\" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions.\n    const char* LogFilename;                    // = \"imgui_log.txt\"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified).\n    void*       UserData;                       // = NULL           // Store your own data.\n\n    // Font system\n    ImFontAtlas*Fonts;                          // <auto>           // Font atlas: load, rasterize and pack one or more fonts into a single texture.\n    ImFont*     FontDefault;                    // = NULL           // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0].\n    bool        FontAllowUserScaling;           // = false          // Allow user scaling text of individual window with Ctrl+Wheel.\n\n    // Keyboard/Gamepad Navigation options\n    bool        ConfigNavSwapGamepadButtons;    // = false          // Swap Activate<>Cancel (A<>B) buttons, matching typical \"Nintendo/Japanese style\" gamepad layout.\n    bool        ConfigNavMoveSetMousePos;       // = false          // Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true.\n    bool        ConfigNavCaptureKeyboard;       // = true           // Sets io.WantCaptureKeyboard when io.NavActive is set.\n    bool        ConfigNavEscapeClearFocusItem;  // = true           // Pressing Escape can clear focused item + navigation id/highlight. Set to false if you want to always keep highlight on.\n    bool        ConfigNavEscapeClearFocusWindow;// = false          // Pressing Escape can clear focused window as well (super set of io.ConfigNavEscapeClearFocusItem).\n    bool        ConfigNavCursorVisibleAuto;     // = true           // Using directional navigation key makes the cursor visible. Mouse click hides the cursor.\n    bool        ConfigNavCursorVisibleAlways;   // = false          // Navigation cursor is always visible.\n\n    // Docking options (when ImGuiConfigFlags_DockingEnable is set)\n    bool        ConfigDockingNoSplit;           // = false          // Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars.\n    bool        ConfigDockingNoDockingOver;     // = false          // Simplified docking mode: disable window merging into a same tab-bar, so docking is limited to splitting windows.\n    bool        ConfigDockingWithShift;         // = false          // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space)\n    bool        ConfigDockingAlwaysTabBar;      // = false          // [BETA] [FIXME: This currently creates regression with auto-sizing and general overhead] Make every single floating window display within a docking node.\n    bool        ConfigDockingTransparentPayload;// = false          // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge.\n\n    // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set)\n    bool        ConfigViewportsNoAutoMerge;     // = false;         // Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it. May also set ImGuiViewportFlags_NoAutoMerge on individual viewport.\n    bool        ConfigViewportsNoTaskBarIcon;   // = false          // Disable default OS task bar icon flag for secondary viewports. When a viewport doesn't want a task bar icon, ImGuiViewportFlags_NoTaskBarIcon will be set on it.\n    bool        ConfigViewportsNoDecoration;    // = true           // Disable default OS window decoration flag for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size).\n    bool        ConfigViewportsNoDefaultParent; // = true           // When false: set secondary viewports' ParentViewportId to main viewport ID by default. Expects the platform backend to setup a parent/child relationship between the OS windows based on this value. Some backend may ignore this. Set to true if you want viewports to automatically be parent of main viewport, otherwise all viewports will be top-level OS windows.\n    bool        ConfigViewportsPlatformFocusSetsImGuiFocus;//= true // When a platform window is focused (e.g. using Alt+Tab, clicking Platform Title Bar), apply corresponding focus on imgui windows (may clear focus/active id from imgui windows location in other platform windows). In principle this is better enabled but we provide an opt-out, because some Linux window managers tend to eagerly focus windows (e.g. on mouse hover, or even a simple window pos/size change).\n\n    // DPI/Scaling options\n    // This may keep evolving during 1.92.x releases. Expect some turbulence.\n    bool        ConfigDpiScaleFonts;            // = false          // [EXPERIMENTAL] Automatically overwrite style.FontScaleDpi when Monitor DPI changes. This will scale fonts but _NOT_ scale sizes/padding for now.\n    bool        ConfigDpiScaleViewports;        // = false          // [EXPERIMENTAL] Scale Dear ImGui and Platform Windows when Monitor DPI changes.\n\n    // Miscellaneous options\n    // (you can visualize and interact with all options in 'Demo->Configuration')\n    bool        MouseDrawCursor;                // = false          // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.\n    bool        ConfigMacOSXBehaviors;          // = defined(__APPLE__) // Swap Cmd<>Ctrl keys + OS X style text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl.\n    bool        ConfigInputTrickleEventQueue;   // = true           // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.\n    bool        ConfigInputTextCursorBlink;     // = true           // Enable blinking cursor (optional as some users consider it to be distracting).\n    bool        ConfigInputTextEnterKeepActive; // = false          // [BETA] Pressing Enter will reactivate item and select all text (single-line only).\n    bool        ConfigDragClickToInputText;     // = false          // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard.\n    bool        ConfigWindowsResizeFromEdges;   // = true           // Enable resizing of windows from their edges and from the lower-left corner. This requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)\n    bool        ConfigWindowsMoveFromTitleBarOnly;  // = false      // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.\n    bool        ConfigWindowsCopyContentsWithCtrlC; // = false      // [EXPERIMENTAL] Ctrl+C copy the contents of focused window into the clipboard. Experimental because: (1) has known issues with nested Begin/End pairs (2) text output quality varies (3) text output is in submission order rather than spatial order.\n    bool        ConfigScrollbarScrollByPage;    // = true           // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location.\n    float       ConfigMemoryCompactTimer;       // = 60.0f          // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.\n\n    // Inputs Behaviors\n    // (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle)\n    float       MouseDoubleClickTime;           // = 0.30f          // Time for a double-click, in seconds.\n    float       MouseDoubleClickMaxDist;        // = 6.0f           // Distance threshold to stay in to validate a double-click, in pixels.\n    float       MouseDragThreshold;             // = 6.0f           // Distance threshold before considering we are dragging.\n    float       KeyRepeatDelay;                 // = 0.275f         // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.).\n    float       KeyRepeatRate;                  // = 0.050f         // When holding a key/button, rate at which it repeats, in seconds.\n\n    //------------------------------------------------------------------\n    // Debug options\n    //------------------------------------------------------------------\n\n    // Options to configure Error Handling and how we handle recoverable errors [EXPERIMENTAL]\n    // - Error recovery is provided as a way to facilitate:\n    //    - Recovery after a programming error (native code or scripting language - the latter tends to facilitate iterating on code while running).\n    //    - Recovery after running an exception handler or any error processing which may skip code after an error has been detected.\n    // - Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n    //   You not are not supposed to rely on it in the course of a normal application run.\n    // - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().\n    // - By design, we do NOT allow error recovery to be 100% silent. One of the three options needs to be checked!\n    // - Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled when making direct imgui API calls!\n    //   Otherwise it would severely hinder your ability to catch and correct mistakes!\n    // Read https://github.com/ocornut/imgui/wiki/Error-Handling for details.\n    // - Programmer seats: keep asserts (default), or disable asserts and keep error tooltips (new and nice!)\n    // - Non-programmer seats: maybe disable asserts, but make sure errors are resurfaced (tooltips, visible log entries, use callback etc.)\n    // - Recovery after error/exception: record stack sizes with ErrorRecoveryStoreState(), disable assert, set log callback (to e.g. trigger high-level breakpoint), recover with ErrorRecoveryTryToRecoverState(), restore settings.\n    bool        ConfigErrorRecovery;                // = true       // Enable error recovery support. Some errors won't be detected and lead to direct crashes if recovery is disabled.\n    bool        ConfigErrorRecoveryEnableAssert;    // = true       // Enable asserts on recoverable error. By default call IM_ASSERT() when returning from a failing IM_ASSERT_USER_ERROR()\n    bool        ConfigErrorRecoveryEnableDebugLog;  // = true       // Enable debug log output on recoverable errors.\n    bool        ConfigErrorRecoveryEnableTooltip;   // = true       // Enable tooltip on recoverable errors. The tooltip include a way to enable asserts if they were disabled.\n\n    // Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro.\n    // - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability.\n    // - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.\n    //   e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version.\n    bool        ConfigDebugIsDebuggerPresent;   // = false          // Enable various tools calling IM_DEBUG_BREAK().\n\n    // Tools to detect code submitting items with conflicting/duplicate IDs\n    // - Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers.\n    // - Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!\n    // - See FAQ https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-about-the-id-stack-system\n    bool        ConfigDebugHighlightIdConflicts;// = true           // Highlight and show an error message popup when multiple items have conflicting identifiers.\n    bool        ConfigDebugHighlightIdConflictsShowItemPicker;//=true // Show \"Item Picker\" button in aforementioned popup.\n\n    // Tools to test correct Begin/End and BeginChild/EndChild behaviors.\n    // - Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX()\n    // - This is inconsistent with other BeginXXX functions and create confusion for many users.\n    // - We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior.\n    bool        ConfigDebugBeginReturnValueOnce;// = false          // First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows.\n    bool        ConfigDebugBeginReturnValueLoop;// = false          // Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add \"io.ConfigDebugBeginReturnValue = io.KeyShift\" in your main loop then occasionally press SHIFT. Windows should be flickering while running.\n\n    // Option to deactivate io.AddFocusEvent(false) handling.\n    // - May facilitate interactions with a debugger when focus loss leads to clearing inputs data.\n    // - Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them.\n    bool        ConfigDebugIgnoreFocusLoss;     // = false          // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys()/io.ClearInputMouse() in input processing.\n\n    // Option to audit .ini data\n    bool        ConfigDebugIniSettings;         // = false          // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)\n\n    //------------------------------------------------------------------\n    // Platform Identifiers\n    // (the imgui_impl_xxxx backend files are setting those up for you)\n    //------------------------------------------------------------------\n\n    // Nowadays those would be stored in ImGuiPlatformIO but we are leaving them here for legacy reasons.\n    // Optional: Platform/Renderer backend name (informational only! will be displayed in About Window) + User data for backend/wrappers to store their own stuff.\n    const char* BackendPlatformName;            // = NULL\n    const char* BackendRendererName;            // = NULL\n    void*       BackendPlatformUserData;        // = NULL           // User data for platform backend\n    void*       BackendRendererUserData;        // = NULL           // User data for renderer backend\n    void*       BackendLanguageUserData;        // = NULL           // User data for non C++ programming language backend\n\n    //------------------------------------------------------------------\n    // Input - Call before calling NewFrame()\n    //------------------------------------------------------------------\n\n    // Input Functions\n    IMGUI_API void  AddKeyEvent(ImGuiKey key, bool down);                   // Queue a new key down/up event. Key should be \"translated\" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)\n    IMGUI_API void  AddKeyAnalogEvent(ImGuiKey key, bool down, float v);    // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend.\n    IMGUI_API void  AddMousePosEvent(float x, float y);                     // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered)\n    IMGUI_API void  AddMouseButtonEvent(int button, bool down);             // Queue a mouse button change\n    IMGUI_API void  AddMouseWheelEvent(float wheel_x, float wheel_y);       // Queue a mouse wheel update. wheel_y<0: scroll down, wheel_y>0: scroll up, wheel_x<0: scroll right, wheel_x>0: scroll left.\n    IMGUI_API void  AddMouseSourceEvent(ImGuiMouseSource source);           // Queue a mouse source change (Mouse/TouchScreen/Pen)\n    IMGUI_API void  AddMouseViewportEvent(ImGuiID id);                      // Queue a mouse hovered viewport. Requires backend to set ImGuiBackendFlags_HasMouseHoveredViewport to call this (for multi-viewport support).\n    IMGUI_API void  AddFocusEvent(bool focused);                            // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window)\n    IMGUI_API void  AddInputCharacter(unsigned int c);                      // Queue a new character input\n    IMGUI_API void  AddInputCharacterUTF16(ImWchar16 c);                    // Queue a new character input from a UTF-16 character, it can be a surrogate\n    IMGUI_API void  AddInputCharactersUTF8(const char* str);                // Queue a new characters input from a UTF-8 string\n\n    IMGUI_API void  SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode.\n    IMGUI_API void  SetAppAcceptingEvents(bool accepting_events);           // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.\n    IMGUI_API void  ClearEventsQueue();                                     // Clear all incoming events.\n    IMGUI_API void  ClearInputKeys();                                       // Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.\n    IMGUI_API void  ClearInputMouse();                                      // Clear current mouse state.\n\n    //------------------------------------------------------------------\n    // Output - Updated by NewFrame() or EndFrame()/Render()\n    // (when reading from the io.WantCaptureMouse, io.WantCaptureKeyboard flags to dispatch your inputs, it is\n    //  generally easier and more correct to use their state BEFORE calling NewFrame(). See FAQ for details!)\n    //------------------------------------------------------------------\n\n    bool        WantCaptureMouse;                   // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.).\n    bool        WantCaptureKeyboard;                // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.).\n    bool        WantTextInput;                      // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active).\n    bool        WantSetMousePos;                    // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when io.ConfigNavMoveSetMousePos is enabled.\n    bool        WantSaveIniSettings;                // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!\n    bool        NavActive;                          // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.\n    bool        NavVisible;                         // Keyboard/Gamepad navigation highlight is visible and allowed (will handle ImGuiKey_NavXXX events).\n    float       Framerate;                          // Estimate of application framerate (rolling average over 60 frames, based on io.DeltaTime), in frame per second. Solely for convenience. Slow applications may not want to use a moving average or may want to reset underlying buffers occasionally.\n    int         MetricsRenderVertices;              // Vertices output during last call to Render()\n    int         MetricsRenderIndices;               // Indices output during last call to Render() = number of triangles * 3\n    int         MetricsRenderWindows;               // Number of visible windows\n    int         MetricsActiveWindows;               // Number of active windows\n    ImVec2      MouseDelta;                         // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta.\n\n    //------------------------------------------------------------------\n    // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed!\n    //------------------------------------------------------------------\n\n    ImGuiContext* Ctx;                              // Parent UI context (needs to be set explicitly by parent).\n\n    // Main Input State\n    // (this block used to be written by backend, since 1.87 it is best to NOT write to those directly, call the AddXXX functions above instead)\n    // (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere)\n    ImVec2      MousePos;                           // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.)\n    bool        MouseDown[5];                       // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Other buttons allow us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API.\n    float       MouseWheel;                         // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. >0 scrolls Up, <0 scrolls Down. Hold Shift to turn vertical scroll into horizontal scroll.\n    float       MouseWheelH;                        // Mouse wheel Horizontal. >0 scrolls Left, <0 scrolls Right. Most users don't have a mouse with a horizontal wheel, may not be filled by all backends.\n    ImGuiMouseSource MouseSource;                   // Mouse actual input peripheral (Mouse/TouchScreen/Pen).\n    ImGuiID     MouseHoveredViewport;               // (Optional) Modify using io.AddMouseViewportEvent(). With multi-viewports: viewport the OS mouse is hovering. If possible _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag is much better (few backends can handle that). Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows).\n    bool        KeyCtrl;                            // Keyboard modifier down: Ctrl (non-macOS), Cmd (macOS)\n    bool        KeyShift;                           // Keyboard modifier down: Shift\n    bool        KeyAlt;                             // Keyboard modifier down: Alt\n    bool        KeySuper;                           // Keyboard modifier down: Windows/Super (non-macOS), Ctrl (macOS)\n\n    // Other state maintained from data above + IO function calls\n    ImGuiKeyChord KeyMods;                          // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags). Read-only, updated by NewFrame()\n    ImGuiKeyData  KeysData[ImGuiKey_NamedKey_COUNT];// Key state for all known keys. MUST use 'key - ImGuiKey_NamedKey_BEGIN' as index. Use IsKeyXXX() functions to access this.\n    bool        WantCaptureMouseUnlessPopupClose;   // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.\n    ImVec2      MousePosPrev;                       // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)\n    ImVec2      MouseClickedPos[5];                 // Position at time of clicking\n    double      MouseClickedTime[5];                // Time of last click (used to figure out double-click)\n    bool        MouseClicked[5];                    // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0)\n    bool        MouseDoubleClicked[5];              // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2)\n    ImU16       MouseClickedCount[5];               // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down\n    ImU16       MouseClickedLastCount[5];           // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done.\n    bool        MouseReleased[5];                   // Mouse button went from Down to !Down\n    double      MouseReleasedTime[5];               // Time of last released (rarely used! but useful to handle delayed single-click when trying to disambiguate them from double-click).\n    bool        MouseDownOwned[5];                  // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds.\n    bool        MouseDownOwnedUnlessPopupClose[5];  // Track if button was clicked inside a dear imgui window.\n    bool        MouseWheelRequestAxisSwap;          // On a non-Mac system, holding Shift requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system.\n    bool        MouseCtrlLeftAsRightClick;          // (OSX) Set to true when the current click was a Ctrl+Click that spawned a simulated right click\n    float       MouseDownDuration[5];               // Duration the mouse button has been down (0.0f == just clicked)\n    float       MouseDownDurationPrev[5];           // Previous time the mouse button has been down\n    ImVec2      MouseDragMaxDistanceAbs[5];         // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point\n    float       MouseDragMaxDistanceSqr[5];         // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds)\n    float       PenPressure;                        // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui.\n    bool        AppFocusLost;                       // Only modify via AddFocusEvent()\n    bool        AppAcceptingEvents;                 // Only modify via SetAppAcceptingEvents()\n    ImWchar16   InputQueueSurrogate;                // For AddInputCharacterUTF16()\n    ImVector<ImWchar> InputQueueCharacters;         // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper.\n\n    // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame.\n    // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent().\n    //   Old (<1.87):  ImGui::IsKeyPressed(ImGui::GetIO().KeyMap[ImGuiKey_Space]) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space)\n    //   Old (<1.87):  ImGui::IsKeyPressed(MYPLATFORM_KEY_SPACE)                  --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space)\n    // Read https://github.com/ocornut/imgui/issues/4921 for details.\n    //int       KeyMap[ImGuiKey_COUNT];             // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your \"native\" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512.\n    //bool      KeysDown[ImGuiKey_COUNT];           // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the \"native\" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow.\n    //float     NavInputs[ImGuiNavInput_COUNT];     // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums.\n    //void*     ImeWindowHandle;                    // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning.\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    float       FontGlobalScale;                    // Moved io.FontGlobalScale to style.FontScaleMain in 1.92 (June 2025)\n\n    // Legacy: before 1.91.1, clipboard functions were stored in ImGuiIO instead of ImGuiPlatformIO.\n    // As this is will affect all users of custom engines/backends, we are providing proper legacy redirection (will obsolete).\n    const char* (*GetClipboardTextFn)(void* user_data);\n    void        (*SetClipboardTextFn)(void* user_data, const char* text);\n    void*       ClipboardUserData;\n\n    //void ClearInputCharacters() { InputQueueCharacters.resize(0); } // [Obsoleted in 1.89.8] Clear the current frame text input buffer. Now included within ClearInputKeys(). Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue.\n#endif\n\n    IMGUI_API   ImGuiIO();\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload)\n//-----------------------------------------------------------------------------\n\n// Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used.\n// The callback function should return 0 by default.\n// Callbacks (follow a flag name and see comments in ImGuiInputTextFlags_ declarations for more details)\n// - ImGuiInputTextFlags_CallbackEdit:        Callback on buffer edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active.\n// - ImGuiInputTextFlags_CallbackAlways:      Callback on each iteration\n// - ImGuiInputTextFlags_CallbackCompletion:  Callback on pressing TAB\n// - ImGuiInputTextFlags_CallbackHistory:     Callback on pressing Up/Down arrows\n// - ImGuiInputTextFlags_CallbackCharFilter:  Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard.\n// - ImGuiInputTextFlags_CallbackResize:      Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow.\nstruct ImGuiInputTextCallbackData\n{\n    ImGuiContext*       Ctx;            // Parent UI context\n    ImGuiInputTextFlags EventFlag;      // One ImGuiInputTextFlags_Callback*    // Read-only\n    ImGuiInputTextFlags Flags;          // What user passed to InputText()      // Read-only\n    void*               UserData;       // What user passed to InputText()      // Read-only\n    ImGuiID             ID;             // Widget ID                             // Read-only\n\n    // Arguments for the different callback events\n    // - During Resize callback, Buf will be same as your input buffer.\n    // - However, during Completion/History/Always callback, Buf always points to our own internal data (it is not the same as your buffer)! Changes to it will be reflected into your own buffer shortly after the callback.\n    // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary.\n    // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state.\n    ImGuiKey            EventKey;       // Key pressed (Up/Down/TAB)            // Read-only    // [Completion,History]\n    ImWchar             EventChar;      // Character input                      // Read-write   // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0;\n    bool                EventActivated; // Input field just got activated       // Read-only    // [Always]\n    bool                BufDirty;       // Set if you modify Buf/BufTextLen!    // Write        // [Completion,History,Always]\n    char*               Buf;            // Text buffer                          // Read-write   // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer!\n    int                 BufTextLen;     // Text length (in bytes)               // Read-write   // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length()\n    int                 BufSize;        // Buffer size (in bytes) = capacity+1  // Read-only    // [Resize,Completion,History,Always] Include zero-terminator storage. In C land: == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1\n    int                 CursorPos;      //                                      // Read-write   // [Completion,History,Always]\n    int                 SelectionStart; //                                      // Read-write   // [Completion,History,Always] == to SelectionEnd when no selection\n    int                 SelectionEnd;   //                                      // Read-write   // [Completion,History,Always]\n\n    // Helper functions for text manipulation.\n    // Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection.\n    IMGUI_API ImGuiInputTextCallbackData();\n    IMGUI_API void      DeleteChars(int pos, int bytes_count);\n    IMGUI_API void      InsertChars(int pos, const char* text, const char* text_end = NULL);\n    void                SelectAll()                 { SelectionStart = 0; CursorPos = SelectionEnd = BufTextLen; }\n    void                SetSelection(int s, int e)  { IM_ASSERT(s >= 0 && s <= BufTextLen); IM_ASSERT(e >= 0 && e <= BufTextLen); SelectionStart = s; CursorPos = SelectionEnd = e; }\n    void                ClearSelection()            { SelectionStart = SelectionEnd = BufTextLen; }\n    bool                HasSelection() const        { return SelectionStart != SelectionEnd; }\n};\n\n// Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin().\n// NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough.\nstruct ImGuiSizeCallbackData\n{\n    void*   UserData;       // Read-only.   What user passed to SetNextWindowSizeConstraints(). Generally store an integer or float in here (need reinterpret_cast<>).\n    ImVec2  Pos;            // Read-only.   Window position, for reference.\n    ImVec2  CurrentSize;    // Read-only.   Current window size.\n    ImVec2  DesiredSize;    // Read-write.  Desired size, based on user's mouse position. Write to this field to restrain resizing.\n};\n\n// [ALPHA] Rarely used / very advanced uses only. Use with SetNextWindowClass() and DockSpace() functions.\n// Important: the content of this class is still highly WIP and likely to change and be refactored\n// before we stabilize Docking features. Please be mindful if using this.\n// Provide hints:\n// - To the platform backend via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.)\n// - To the platform backend for OS level parent/child relationships of viewport.\n// - To the docking system for various options and filtering.\nstruct ImGuiWindowClass\n{\n    ImGuiID             ClassId;                    // User data. 0 = Default class (unclassed). Windows of different classes cannot be docked with each others.\n    ImGuiID             ParentViewportId;           // Hint for the platform backend. -1: use default. 0: request platform backend to not parent the platform. != 0: request platform backend to create a parent<>child relationship between the platform windows. Not conforming backends are free to e.g. parent every viewport to the main viewport or not.\n    ImGuiID             FocusRouteParentWindowId;   // ID of parent window for shortcut focus route evaluation, e.g. Shortcut() call from Parent Window will succeed when this window is focused.\n    ImGuiViewportFlags  ViewportFlagsOverrideSet;   // Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis.\n    ImGuiViewportFlags  ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis.\n    ImGuiTabItemFlags   TabItemFlagsOverrideSet;    // [EXPERIMENTAL] TabItem flags to set when a window of this class gets submitted into a dock node tab bar. May use with ImGuiTabItemFlags_Leading or ImGuiTabItemFlags_Trailing.\n    ImGuiDockNodeFlags  DockNodeFlagsOverrideSet;   // [EXPERIMENTAL] Dock node flags to set when a window of this class is hosted by a dock node (it doesn't have to be selected!)\n    bool                DockingAlwaysTabBar;        // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar)\n    bool                DockingAllowUnclassed;      // Set to true to allow windows of this class to be docked/merged with an unclassed window. // FIXME-DOCK: Move to DockNodeFlags override?\n\n    ImGuiWindowClass() { memset((void*)this, 0, sizeof(*this)); ParentViewportId = (ImGuiID)-1; DockingAllowUnclassed = true; }\n};\n\n// Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload()\nstruct ImGuiPayload\n{\n    // Members\n    void*           Data;               // Data (copied and owned by dear imgui)\n    int             DataSize;           // Data size\n\n    // [Internal]\n    ImGuiID         SourceId;           // Source item id\n    ImGuiID         SourceParentId;     // Source parent id (if available)\n    int             DataFrameCount;     // Data timestamp\n    char            DataType[32 + 1];   // Data type tag (short user-supplied string, 32 characters max)\n    bool            Preview;            // Set when AcceptDragDropPayload() was called and mouse has been hovering the target item (nb: handle overlapping drag targets)\n    bool            Delivery;           // Set when AcceptDragDropPayload() was called and mouse button is released over the target item.\n\n    ImGuiPayload()  { Clear(); }\n    void Clear()    { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; }\n    bool IsDataType(const char* type) const { return DataFrameCount != -1 && strcmp(type, DataType) == 0; }\n    bool IsPreview() const                  { return Preview; }\n    bool IsDelivery() const                 { return Delivery; }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor)\n//-----------------------------------------------------------------------------\n\n// Helper: Unicode defines\n#define IM_UNICODE_CODEPOINT_INVALID 0xFFFD     // Invalid Unicode code point (standard value).\n#ifdef IMGUI_USE_WCHAR32\n#define IM_UNICODE_CODEPOINT_MAX     0x10FFFF   // Maximum Unicode code point supported by this build.\n#else\n#define IM_UNICODE_CODEPOINT_MAX     0xFFFF     // Maximum Unicode code point supported by this build.\n#endif\n\n// Helper: Execute a block of code at maximum once a frame. Convenient if you want to quickly create a UI within deep-nested code that runs multiple times every frame.\n// Usage: static ImGuiOnceUponAFrame oaf; if (oaf) ImGui::Text(\"This will be called only once per frame\");\nstruct ImGuiOnceUponAFrame\n{\n    ImGuiOnceUponAFrame() { RefFrame = -1; }\n    mutable int RefFrame;\n    operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; }\n};\n\n// Helper: Parse and apply text filters. In format \"aaaaa[,bbbb][,ccccc]\"\nstruct ImGuiTextFilter\n{\n    IMGUI_API           ImGuiTextFilter(const char* default_filter = \"\");\n    IMGUI_API bool      Draw(const char* label = \"Filter (inc,-exc)\", float width = 0.0f);  // Helper calling InputText+Build\n    IMGUI_API bool      PassFilter(const char* text, const char* text_end = NULL) const;\n    IMGUI_API void      Build();\n    void                Clear()          { InputBuf[0] = 0; Build(); }\n    bool                IsActive() const { return !Filters.empty(); }\n\n    // [Internal]\n    struct ImGuiTextRange\n    {\n        const char*     b;\n        const char*     e;\n\n        ImGuiTextRange()                                { b = e = NULL; }\n        ImGuiTextRange(const char* _b, const char* _e)  { b = _b; e = _e; }\n        bool            empty() const                   { return b == e; }\n        IMGUI_API void  split(char separator, ImVector<ImGuiTextRange>* out) const;\n    };\n    char                    InputBuf[256];\n    ImVector<ImGuiTextRange>Filters;\n    int                     CountGrep;\n};\n\n// Helper: Growable text buffer for logging/accumulating text\n// (this could be called 'ImGuiTextBuilder' / 'ImGuiStringBuilder')\nstruct ImGuiTextBuffer\n{\n    ImVector<char>      Buf;\n    IMGUI_API static char EmptyString[1];\n\n    ImGuiTextBuffer()   { }\n    inline char         operator[](int i) const { IM_ASSERT(Buf.Data != NULL); return Buf.Data[i]; }\n    const char*         begin() const           { return Buf.Data ? &Buf.front() : EmptyString; }\n    const char*         end() const             { return Buf.Data ? &Buf.back() : EmptyString; } // Buf is zero-terminated, so end() will point on the zero-terminator\n    int                 size() const            { return Buf.Size ? Buf.Size - 1 : 0; }\n    bool                empty() const           { return Buf.Size <= 1; }\n    void                clear()                 { Buf.clear(); }\n    void                resize(int size)        { if (Buf.Size > size) Buf.Data[size] = 0; Buf.resize(size ? size + 1 : 0, 0); } // Similar to resize(0) on ImVector: empty string but don't free buffer.\n    void                reserve(int capacity)   { Buf.reserve(capacity); }\n    const char*         c_str() const           { return Buf.Data ? Buf.Data : EmptyString; }\n    IMGUI_API void      append(const char* str, const char* str_end = NULL);\n    IMGUI_API void      appendf(const char* fmt, ...) IM_FMTARGS(2);\n    IMGUI_API void      appendfv(const char* fmt, va_list args) IM_FMTLIST(2);\n};\n\n// [Internal] Key+Value for ImGuiStorage\nstruct ImGuiStoragePair\n{\n    ImGuiID     key;\n    union       { int val_i; float val_f; void* val_p; };\n    ImGuiStoragePair(ImGuiID _key, int _val)    { key = _key; val_i = _val; }\n    ImGuiStoragePair(ImGuiID _key, float _val)  { key = _key; val_f = _val; }\n    ImGuiStoragePair(ImGuiID _key, void* _val)  { key = _key; val_p = _val; }\n};\n\n// Helper: Key->Value storage\n// Typically you don't have to worry about this since a storage is held within each Window.\n// We use it to e.g. store collapse state for a tree (Int 0/1)\n// This is optimized for efficient lookup (dichotomy into a contiguous buffer) and rare insertion (typically tied to user interactions aka max once a frame)\n// You can use it as custom user storage for temporary values. Declare your own storage if, for example:\n// - You want to manipulate the open/close state of a particular sub-tree in your interface (tree node uses Int 0/1 to store their state).\n// - You want to store custom debug data easily without adding or editing structures in your code (probably not efficient, but convenient)\n// Types are NOT stored, so it is up to you to make sure your Key don't collide with different types.\nstruct ImGuiStorage\n{\n    // [Internal]\n    ImVector<ImGuiStoragePair>      Data;\n\n    // - Get***() functions find pair, never add/allocate. Pairs are sorted so a query is O(log N)\n    // - Set***() functions find pair, insertion on demand if missing.\n    // - Sorted insertion is costly, paid once. A typical frame shouldn't need to insert any new pair.\n    void                Clear() { Data.clear(); }\n    IMGUI_API int       GetInt(ImGuiID key, int default_val = 0) const;\n    IMGUI_API void      SetInt(ImGuiID key, int val);\n    IMGUI_API bool      GetBool(ImGuiID key, bool default_val = false) const;\n    IMGUI_API void      SetBool(ImGuiID key, bool val);\n    IMGUI_API float     GetFloat(ImGuiID key, float default_val = 0.0f) const;\n    IMGUI_API void      SetFloat(ImGuiID key, float val);\n    IMGUI_API void*     GetVoidPtr(ImGuiID key) const; // default_val is NULL\n    IMGUI_API void      SetVoidPtr(ImGuiID key, void* val);\n\n    // - Get***Ref() functions finds pair, insert on demand if missing, return pointer. Useful if you intend to do Get+Set.\n    // - References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.\n    // - A typical use case where this is convenient for quick hacking (e.g. add storage during a live Edit&Continue session if you can't modify existing struct)\n    //      float* pvar = ImGui::GetFloatRef(key); ImGui::SliderFloat(\"var\", pvar, 0, 100.0f); some_var += *pvar;\n    IMGUI_API int*      GetIntRef(ImGuiID key, int default_val = 0);\n    IMGUI_API bool*     GetBoolRef(ImGuiID key, bool default_val = false);\n    IMGUI_API float*    GetFloatRef(ImGuiID key, float default_val = 0.0f);\n    IMGUI_API void**    GetVoidPtrRef(ImGuiID key, void* default_val = NULL);\n\n    // Advanced: for quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.\n    IMGUI_API void      BuildSortByKey();\n    // Obsolete: use on your own storage if you know only integer are being stored (open/close all tree nodes)\n    IMGUI_API void      SetAllInt(int val);\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    //typedef ::ImGuiStoragePair ImGuiStoragePair;  // 1.90.8: moved type outside struct\n#endif\n};\n\n// Flags for ImGuiListClipper (currently not fully exposed in function calls: a future refactor will likely add this to ImGuiListClipper::Begin function equivalent)\nenum ImGuiListClipperFlags_\n{\n    ImGuiListClipperFlags_None                  = 0,\n    ImGuiListClipperFlags_NoSetTableRowCounters = 1 << 0,   // [Internal] Disabled modifying table row counters. Avoid assumption that 1 clipper item == 1 table row.\n};\n\n// Helper: Manually clip large list of items.\n// If you have lots evenly spaced items and you have random access to the list, you can perform coarse\n// clipping based on visibility to only submit items that are in view.\n// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped.\n// (Dear ImGui already clip items based on their bounds but: it needs to first layout the item to do so, and generally\n//  fetching/submitting your own data incurs additional cost. Coarse clipping using ImGuiListClipper allows you to easily\n//  scale using lists with tens of thousands of items without a problem)\n// Usage:\n//   ImGuiListClipper clipper;\n//   clipper.Begin(1000);         // We have 1000 elements, evenly spaced.\n//   while (clipper.Step())\n//       for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)\n//           ImGui::Text(\"line number %d\", i);\n// Generally what happens is:\n// - Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not.\n// - User code submit that one element.\n// - Clipper can measure the height of the first element\n// - Clipper calculate the actual range of elements to display based on the current clipping rectangle, position the cursor before the first visible element.\n// - User code submit visible elements.\n// - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc.\nstruct ImGuiListClipper\n{\n    ImGuiContext*   Ctx;                // Parent UI context\n    int             DisplayStart;       // First item to display, updated by each call to Step()\n    int             DisplayEnd;         // End of items to display (exclusive)\n    int             UserIndex;          // Helper storage for user convenience/code. Optional, and otherwise unused if you don't use it.\n    int             ItemsCount;         // [Internal] Number of items\n    float           ItemsHeight;        // [Internal] Height of item after a first step and item submission can calculate it\n    double          StartPosY;          // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed\n    double          StartSeekOffsetY;   // [Internal] Account for frozen rows in a table and initial loss of precision in very large windows.\n    void*           TempData;           // [Internal] Internal data\n    ImGuiListClipperFlags Flags;        // [Internal] Flags, currently not yet well exposed.\n\n    // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step, and you can call SeekCursorForItem() manually if you need)\n    // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing().\n    IMGUI_API ImGuiListClipper();\n    IMGUI_API ~ImGuiListClipper();\n    IMGUI_API void  Begin(int items_count, float items_height = -1.0f);\n    IMGUI_API void  End();             // Automatically called on the last call of Step() that returns false.\n    IMGUI_API bool  Step();            // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items.\n\n    // Call IncludeItemByIndex() or IncludeItemsByIndex() *BEFORE* first call to Step() if you need a range of items to not be clipped, regardless of their visibility.\n    // (Due to alignment / padding of certain items it is possible that an extra item may be included on either end of the display range).\n    inline void     IncludeItemByIndex(int item_index)                  { IncludeItemsByIndex(item_index, item_index + 1); }\n    IMGUI_API void  IncludeItemsByIndex(int item_begin, int item_end);  // item_end is exclusive e.g. use (42, 42+1) to make item 42 never clipped.\n\n    // Seek cursor toward given item. This is automatically called while stepping.\n    // - The only reason to call this is: you can use ImGuiListClipper::Begin(INT_MAX) if you don't know item count ahead of time.\n    // - In this case, after all steps are done, you'll want to call SeekCursorForItem(item_count).\n    IMGUI_API void  SeekCursorForItem(int item_index);\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    //inline void IncludeRangeByIndices(int item_begin, int item_end)      { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9]\n    //inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6]\n    //inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset((void*)this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79]\n#endif\n};\n\n// Helpers: ImVec2/ImVec4 operators\n// - It is important that we are keeping those disabled by default so they don't leak in user space.\n// - This is in order to allow user enabling implicit cast operators between ImVec2/ImVec4 and their own types (using IM_VEC2_CLASS_EXTRA in imconfig.h)\n// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4.\n// - We intentionally provide ImVec2*float but not float*ImVec2: this is rare enough and we want to reduce the surface for possible user mistake.\n#ifdef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED\nIM_MSVC_RUNTIME_CHECKS_OFF\n// ImVec2 operators\ninline ImVec2  operator*(const ImVec2& lhs, const float rhs)    { return ImVec2(lhs.x * rhs, lhs.y * rhs); }\ninline ImVec2  operator/(const ImVec2& lhs, const float rhs)    { return ImVec2(lhs.x / rhs, lhs.y / rhs); }\ninline ImVec2  operator+(const ImVec2& lhs, const ImVec2& rhs)  { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); }\ninline ImVec2  operator-(const ImVec2& lhs, const ImVec2& rhs)  { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); }\ninline ImVec2  operator*(const ImVec2& lhs, const ImVec2& rhs)  { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }\ninline ImVec2  operator/(const ImVec2& lhs, const ImVec2& rhs)  { return ImVec2(lhs.x / rhs.x, lhs.y / rhs.y); }\ninline ImVec2  operator-(const ImVec2& lhs)                     { return ImVec2(-lhs.x, -lhs.y); }\ninline ImVec2& operator*=(ImVec2& lhs, const float rhs)         { lhs.x *= rhs; lhs.y *= rhs; return lhs; }\ninline ImVec2& operator/=(ImVec2& lhs, const float rhs)         { lhs.x /= rhs; lhs.y /= rhs; return lhs; }\ninline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs)       { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; }\ninline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs)       { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; }\ninline ImVec2& operator*=(ImVec2& lhs, const ImVec2& rhs)       { lhs.x *= rhs.x; lhs.y *= rhs.y; return lhs; }\ninline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs)       { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; }\ninline bool    operator==(const ImVec2& lhs, const ImVec2& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; }\ninline bool    operator!=(const ImVec2& lhs, const ImVec2& rhs) { return lhs.x != rhs.x || lhs.y != rhs.y; }\n// ImVec4 operators\ninline ImVec4  operator*(const ImVec4& lhs, const float rhs)    { return ImVec4(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs); }\ninline ImVec4  operator/(const ImVec4& lhs, const float rhs)    { return ImVec4(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs, lhs.w / rhs); }\ninline ImVec4  operator+(const ImVec4& lhs, const ImVec4& rhs)  { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); }\ninline ImVec4  operator-(const ImVec4& lhs, const ImVec4& rhs)  { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); }\ninline ImVec4  operator*(const ImVec4& lhs, const ImVec4& rhs)  { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }\ninline ImVec4  operator/(const ImVec4& lhs, const ImVec4& rhs)  { return ImVec4(lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z, lhs.w / rhs.w); }\ninline ImVec4  operator-(const ImVec4& lhs)                     { return ImVec4(-lhs.x, -lhs.y, -lhs.z, -lhs.w); }\ninline bool    operator==(const ImVec4& lhs, const ImVec4& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w; }\ninline bool    operator!=(const ImVec4& lhs, const ImVec4& rhs) { return lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z || lhs.w != rhs.w; }\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n#endif\n\n// Helpers macros to generate 32-bit encoded colors\n// - User can declare their own format by #defining the 5 _SHIFT/_MASK macros in their imconfig file.\n// - Any setting other than the default will need custom backend support. The only standard backend that supports anything else than the default is DirectX9.\n#ifndef IM_COL32_R_SHIFT\n#ifdef IMGUI_USE_BGRA_PACKED_COLOR\n#define IM_COL32_R_SHIFT    16\n#define IM_COL32_G_SHIFT    8\n#define IM_COL32_B_SHIFT    0\n#define IM_COL32_A_SHIFT    24\n#define IM_COL32_A_MASK     0xFF000000\n#else\n#define IM_COL32_R_SHIFT    0\n#define IM_COL32_G_SHIFT    8\n#define IM_COL32_B_SHIFT    16\n#define IM_COL32_A_SHIFT    24\n#define IM_COL32_A_MASK     0xFF000000\n#endif\n#endif\n#define IM_COL32(R,G,B,A)    (((ImU32)(A)<<IM_COL32_A_SHIFT) | ((ImU32)(B)<<IM_COL32_B_SHIFT) | ((ImU32)(G)<<IM_COL32_G_SHIFT) | ((ImU32)(R)<<IM_COL32_R_SHIFT))\n#define IM_COL32_WHITE       IM_COL32(255,255,255,255)  // Opaque white = 0xFFFFFFFF\n#define IM_COL32_BLACK       IM_COL32(0,0,0,255)        // Opaque black\n#define IM_COL32_BLACK_TRANS IM_COL32(0,0,0,0)          // Transparent black = 0x00000000\n\n// Helper: ImColor() implicitly converts colors to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float)\n// Prefer using IM_COL32() macros if you want a guaranteed compile-time ImU32 for usage with ImDrawList API.\n// **Avoid storing ImColor! Store either u32 of ImVec4. This is not a full-featured color class. MAY OBSOLETE.\n// **None of the ImGui API are using ImColor directly but you can use it as a convenience to pass colors in either ImU32 or ImVec4 formats. Explicitly cast to ImU32 or ImVec4 if needed.\nstruct ImColor\n{\n    ImVec4          Value;\n\n    constexpr ImColor()                                             { }\n    constexpr ImColor(float r, float g, float b, float a = 1.0f)    : Value(r, g, b, a) { }\n    constexpr ImColor(const ImVec4& col)                            : Value(col) {}\n    constexpr ImColor(int r, int g, int b, int a = 255)             : Value((float)r * (1.0f / 255.0f), (float)g * (1.0f / 255.0f), (float)b * (1.0f / 255.0f), (float)a* (1.0f / 255.0f)) {}\n    constexpr ImColor(ImU32 rgba)                                   : Value((float)((rgba >> IM_COL32_R_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * (1.0f / 255.0f)) {}\n    inline operator ImU32() const                                   { return ImGui::ColorConvertFloat4ToU32(Value); }\n    inline operator ImVec4() const                                  { return Value; }\n\n    // FIXME-OBSOLETE: May need to obsolete/cleanup those helpers.\n    inline void    SetHSV(float h, float s, float v, float a = 1.0f){ ImGui::ColorConvertHSVtoRGB(h, s, v, Value.x, Value.y, Value.z); Value.w = a; }\n    static ImColor HSV(float h, float s, float v, float a = 1.0f)   { float r, g, b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r, g, b, a); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO, ImGuiSelectionBasicStorage)\n//-----------------------------------------------------------------------------\n\n// Multi-selection system\n// Documentation at: https://github.com/ocornut/imgui/wiki/Multi-Select\n// - Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos using this.\n// - This system implements standard multi-selection idioms (Ctrl+Mouse/Keyboard, Shift+Mouse/Keyboard, etc)\n//   with support for clipper (skipping non-visible items), box-select and many other details.\n// - Selectable(), Checkbox() are supported but custom widgets may use it as well.\n// - TreeNode() is technically supported but... using this correctly is more complicated: you need some sort of linear/random access to your tree,\n//   which is suited to advanced trees setups also implementing filters and clipper. We will work toward simplifying and demoing it.\n// - In the spirit of Dear ImGui design, your code owns actual selection data.\n//   This is designed to allow all kinds of selection storage you may use in your application e.g. set/map/hash.\n// About ImGuiSelectionBasicStorage:\n// - This is an optional helper to store a selection state and apply selection requests.\n// - It is used by our demos and provided as a convenience to quickly implement multi-selection.\n// Usage:\n// - Identify submitted items with SetNextItemSelectionUserData(), most likely using an index into your current data-set.\n// - Store and maintain actual selection data using persistent object identifiers.\n// - Usage flow:\n//     BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result.\n//           - (2) Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 6.\n//           - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper.IncludeItemByIndex(). If storing indices in ImGuiSelectionUserData, a simple clipper.IncludeItemByIndex(ms_io->RangeSrcItem) call will work.\n//     LOOP  - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls.\n//     END   - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result.\n//           - (6) Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 2.\n//     If you submit all items (no clipper), Step 2 and 3 are optional and will be handled by each item themselves. It is fine to always honor those steps.\n// About ImGuiSelectionUserData:\n// - This can store an application-defined identifier (e.g. index or pointer) submitted via SetNextItemSelectionUserData().\n// - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO.\n// - Most applications will store an object INDEX, hence the chosen name and type. Storing an index is natural, because\n//   SetRange requests will give you two end-points and you will need to iterate/interpolate between them to update your selection.\n// - However it is perfectly possible to store a POINTER or another IDENTIFIER inside ImGuiSelectionUserData.\n//   Our system never assume that you identify items by indices, it never attempts to interpolate between two values.\n// - If you enable ImGuiMultiSelectFlags_NoRangeSelect then it is guaranteed that you will never have to interpolate\n//   between two ImGuiSelectionUserData, which may be a convenient way to use part of the feature with less code work.\n// - As most users will want to store an index, for convenience and to reduce confusion we use ImS64 instead of void*,\n//   being syntactically easier to downcast. Feel free to reinterpret_cast and store a pointer inside.\n\n// Flags for BeginMultiSelect()\nenum ImGuiMultiSelectFlags_\n{\n    ImGuiMultiSelectFlags_None                  = 0,\n    ImGuiMultiSelectFlags_SingleSelect          = 1 << 0,   // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho!\n    ImGuiMultiSelectFlags_NoSelectAll           = 1 << 1,   // Disable Ctrl+A shortcut to select all.\n    ImGuiMultiSelectFlags_NoRangeSelect         = 1 << 2,   // Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). With BoxSelect is also ensure contiguous SetRange requests are not combined into one. This allows not handling interpolation in SetRange requests.\n    ImGuiMultiSelectFlags_NoAutoSelect          = 1 << 3,   // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes).\n    ImGuiMultiSelectFlags_NoAutoClear           = 1 << 4,   // Disable clearing selection when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes).\n    ImGuiMultiSelectFlags_NoAutoClearOnReselect = 1 << 5,   // Disable clearing selection when clicking/selecting an already selected item.\n    ImGuiMultiSelectFlags_BoxSelect1d           = 1 << 6,   // Enable box-selection with same width and same x pos items (e.g. full row Selectable()). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space.\n    ImGuiMultiSelectFlags_BoxSelect2d           = 1 << 7,   // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This is slower: alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items.\n    ImGuiMultiSelectFlags_BoxSelectNoScroll     = 1 << 8,   // Disable scrolling when box-selecting near edges of scope.\n    ImGuiMultiSelectFlags_ClearOnEscape         = 1 << 9,   // Clear selection when pressing Escape while scope is focused.\n    ImGuiMultiSelectFlags_ClearOnClickVoid      = 1 << 10,  // Clear selection when clicking on empty location within scope.\n    ImGuiMultiSelectFlags_ScopeWindow           = 1 << 11,  // Scope for _BoxSelect and _ClearOnClickVoid is whole window (Default). Use if BeginMultiSelect() covers a whole window or used a single time in same window.\n    ImGuiMultiSelectFlags_ScopeRect             = 1 << 12,  // Scope for _BoxSelect and _ClearOnClickVoid is rectangle encompassing BeginMultiSelect()/EndMultiSelect(). Use if BeginMultiSelect() is called multiple times in same window.\n    ImGuiMultiSelectFlags_SelectOnClick         = 1 << 13,  // Apply selection on mouse down when clicking on unselected item. (Default)\n    ImGuiMultiSelectFlags_SelectOnClickRelease  = 1 << 14,  // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection.\n    //ImGuiMultiSelectFlags_RangeSelect2d       = 1 << 15,  // Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does.\n    ImGuiMultiSelectFlags_NavWrapX              = 1 << 16,  // [Temporary] Enable navigation wrapping on X axis. Provided as a convenience because we don't have a design for the general Nav API for this yet. When the more general feature be public we may obsolete this flag in favor of new one.\n    ImGuiMultiSelectFlags_NoSelectOnRightClick  = 1 << 17,  // Disable default right-click processing, which selects item on mouse down, and is designed for context-menus.\n};\n\n// Main IO structure returned by BeginMultiSelect()/EndMultiSelect().\n// This mainly contains a list of selection requests.\n// - Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen.\n// - Some fields are only useful if your list is dynamic and allows deletion (getting post-deletion focus/state right is shown in the demo)\n// - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code.\nstruct ImGuiMultiSelectIO\n{\n    //------------------------------------------// BeginMultiSelect / EndMultiSelect\n    ImVector<ImGuiSelectionRequest> Requests;   //  ms:w, app:r     /  ms:w  app:r   // Requests to apply to your selection data.\n    ImGuiSelectionUserData      RangeSrcItem;   //  ms:w  app:r     /                // (If using clipper) Begin: Source item (often the first selected item) must never be clipped: use clipper.IncludeItemByIndex() to ensure it is submitted.\n    ImGuiSelectionUserData      NavIdItem;      //  ms:w, app:r     /                // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items).\n    bool                        NavIdSelected;  //  ms:w, app:r     /        app:r   // (If using deletion) Last known selection state for NavId (if part of submitted items).\n    bool                        RangeSrcReset;  //        app:w     /  ms:r          // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection).\n    int                         ItemsCount;     //  ms:w, app:r     /        app:r   // 'int items_count' parameter to BeginMultiSelect() is copied here for convenience, allowing simpler calls to your ApplyRequests handler. Not used internally.\n};\n\n// Selection request type\nenum ImGuiSelectionRequestType\n{\n    ImGuiSelectionRequestType_None = 0,\n    ImGuiSelectionRequestType_SetAll,           // Request app to clear selection (if Selected==false) or select all items (if Selected==true). We cannot set RangeFirstItem/RangeLastItem as its contents is entirely up to user (not necessarily an index)\n    ImGuiSelectionRequestType_SetRange,         // Request app to select/unselect [RangeFirstItem..RangeLastItem] items (inclusive) based on value of Selected. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false.\n};\n\n// Selection request item\nstruct ImGuiSelectionRequest\n{\n    //------------------------------------------// BeginMultiSelect / EndMultiSelect\n    ImGuiSelectionRequestType   Type;           //  ms:w, app:r     /  ms:w, app:r   // Request type. You'll most often receive 1 Clear + 1 SetRange with a single-item range.\n    bool                        Selected;       //  ms:w, app:r     /  ms:w, app:r   // Parameter for SetAll/SetRange requests (true = select, false = unselect)\n    ImS8                        RangeDirection; //                  /  ms:w  app:r   // Parameter for SetRange request: +1 when RangeFirstItem comes before RangeLastItem, -1 otherwise. Useful if you want to preserve selection order on a backward Shift+Click.\n    ImGuiSelectionUserData      RangeFirstItem; //                  /  ms:w, app:r   // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom).\n    ImGuiSelectionUserData      RangeLastItem;  //                  /  ms:w, app:r   // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top). Inclusive!\n};\n\n// Optional helper to store multi-selection state + apply multi-selection requests.\n// - Used by our demos and provided as a convenience to easily implement basic multi-selection.\n// - Iterate selection with 'void* it = NULL; ImGuiID id; while (selection.GetNextSelectedItem(&it, &id)) { ... }'\n//   Or you can check 'if (Contains(id)) { ... }' for each possible object if their number is not too high to iterate.\n// - USING THIS IS NOT MANDATORY. This is only a helper and not a required API.\n// To store a multi-selection, in your application you could:\n// - Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set<ImGuiID> replacement.\n// - Use your own external storage: e.g. std::set<MyObjectId>, std::vector<MyObjectId>, interval trees, intrusively stored selection etc.\n// In ImGuiSelectionBasicStorage we:\n// - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO)\n// - use the AdapterIndexToStorageId() indirection layer to abstract how persistent selection data is derived from an index.\n// - use decently optimized logic to allow queries and insertion of very large selection sets.\n// - do not preserve selection order.\n// Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection.\n// Large applications are likely to eventually want to get rid of this indirection layer and do their own thing.\n// See https://github.com/ocornut/imgui/wiki/Multi-Select for details and pseudo-code using this helper.\nstruct ImGuiSelectionBasicStorage\n{\n    // Members\n    int             Size;           //          // Number of selected items, maintained by this helper.\n    bool            PreserveOrder;  // = false  // GetNextSelectedItem() will return ordered selection (currently implemented by two additional sorts of selection. Could be improved)\n    void*           UserData;       // = NULL   // User data for use by adapter function        // e.g. selection.UserData = (void*)my_items;\n    ImGuiID         (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx);      // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; };\n    int             _SelectionOrder;// [Internal] Increasing counter to store selection order\n    ImGuiStorage    _Storage;       // [Internal] Selection set. Think of this as similar to e.g. std::set<ImGuiID>. Prefer not accessing directly: iterate with GetNextSelectedItem().\n\n    // Methods\n    IMGUI_API ImGuiSelectionBasicStorage();\n    IMGUI_API void  ApplyRequests(ImGuiMultiSelectIO* ms_io);   // Apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. It uses 'items_count' passed to BeginMultiSelect()\n    IMGUI_API bool  Contains(ImGuiID id) const;                 // Query if an item id is in selection.\n    IMGUI_API void  Clear();                                    // Clear selection\n    IMGUI_API void  Swap(ImGuiSelectionBasicStorage& r);        // Swap two selections\n    IMGUI_API void  SetItemSelected(ImGuiID id, bool selected); // Add/remove an item from selection (generally done by ApplyRequests() function)\n    IMGUI_API bool  GetNextSelectedItem(void** opaque_it, ImGuiID* out_id); // Iterate selection with 'void* it = NULL; ImGuiID id; while (selection.GetNextSelectedItem(&it, &id)) { ... }'\n    inline ImGuiID  GetStorageIdFromIndex(int idx)              { return AdapterIndexToStorageId(this, idx); }  // Convert index to item id based on provided adapter.\n};\n\n// Optional helper to apply multi-selection requests to existing randomly accessible storage.\n// Convenient if you want to quickly wire multi-select API on e.g. an array of bool or items storing their own selection state.\nstruct ImGuiSelectionExternalStorage\n{\n    // Members\n    void*           UserData;       // User data for use by adapter function                                // e.g. selection.UserData = (void*)my_items;\n    void            (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected); // e.g. AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int idx, bool selected) { ((MyItems**)self->UserData)[idx]->Selected = selected; }\n\n    // Methods\n    IMGUI_API ImGuiSelectionExternalStorage();\n    IMGUI_API void  ApplyRequests(ImGuiMultiSelectIO* ms_io);   // Apply selection requests by using AdapterSetItemSelected() calls\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData)\n// Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList.\n//-----------------------------------------------------------------------------\n\n// The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoBakedLines to disable baking.\n#ifndef IM_DRAWLIST_TEX_LINES_WIDTH_MAX\n#define IM_DRAWLIST_TEX_LINES_WIDTH_MAX     (32)\n#endif\n\n// ImDrawIdx: vertex index. [Compile-time configurable type]\n// - To use 16-bit indices + allow large meshes: backend need to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset (recommended).\n// - To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in your imconfig.h file.\n#ifndef ImDrawIdx\ntypedef unsigned short ImDrawIdx;   // Default: 16-bit (for maximum compatibility with renderer backends)\n#endif\n\n// ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h]\n// NB: You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering,\n// you can poke into the draw list for that! Draw callback may be useful for example to:\n//  A) Change your GPU render state,\n//  B) render a complex 3D scene inside a UI element without an intermediate texture/render target, etc.\n// The expected behavior from your rendering function is 'if (cmd.UserCallback != NULL) { cmd.UserCallback(parent_list, cmd); } else { RenderTriangles() }'\n// If you want to override the signature of ImDrawCallback, you can simply use e.g. '#define ImDrawCallback MyDrawCallback' (in imconfig.h) + update rendering backend accordingly.\n#ifndef ImDrawCallback\ntypedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd);\n#endif\n\n// Special Draw callback value to request renderer backend to reset the graphics/render state.\n// The renderer backend needs to handle this special value, otherwise it will crash trying to call a function at this address.\n// This is useful, for example, if you submitted callbacks which you know have altered the render state and you want it to be restored.\n// Render state is not reset by default because they are many perfectly useful way of altering render state (e.g. changing shader/blending settings before an Image call).\n#define ImDrawCallback_ResetRenderState     (ImDrawCallback)(-8)\n\n// Typically, 1 command = 1 GPU draw call (unless command is a callback)\n// - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled,\n//   this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices.\n//   Backends made for <1.71. will typically ignore the VtxOffset fields.\n// - The ClipRect/TexRef/VtxOffset fields must be contiguous as we memcmp() them together (this is asserted for).\nstruct ImDrawCmd\n{\n    ImVec4          ClipRect;           // 4*4  // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in \"viewport\" coordinates\n    ImTextureRef    TexRef;             // 16   // Reference to a font/texture atlas (where backend called ImTextureData::SetTexID()) or to a user-provided texture ID (via e.g. ImGui::Image() calls). Both will lead to a ImTextureID value.\n    unsigned int    VtxOffset;          // 4    // Start offset in vertex buffer. ImGuiBackendFlags_RendererHasVtxOffset: always 0, otherwise may be >0 to support meshes larger than 64K vertices with 16-bit indices.\n    unsigned int    IdxOffset;          // 4    // Start offset in index buffer.\n    unsigned int    ElemCount;          // 4    // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[].\n    ImDrawCallback  UserCallback;       // 4-8  // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally.\n    void*           UserCallbackData;   // 4-8  // Callback user data (when UserCallback != NULL). If called AddCallback() with size == 0, this is a copy of the AddCallback() argument. If called AddCallback() with size > 0, this is pointing to a buffer where data is stored.\n    int             UserCallbackDataSize;  // 4 // Size of callback user data when using storage, otherwise 0.\n    int             UserCallbackDataOffset;// 4 // [Internal] Offset of callback user data when using storage, otherwise -1.\n\n    ImDrawCmd()     { memset((void*)this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed\n\n    // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature)\n    // Since 1.92: removed ImDrawCmd::TextureId field, the getter function must be used!\n    inline ImTextureID GetTexID() const;    // == (TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID)\n};\n\n// Vertex layout\n#ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT\nstruct ImDrawVert\n{\n    ImVec2  pos;\n    ImVec2  uv;\n    ImU32   col;\n};\n#else\n// You can override the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT in imconfig.h\n// The code expect ImVec2 pos (8 bytes), ImVec2 uv (8 bytes), ImU32 col (4 bytes), but you can re-order them or add other fields as needed to simplify integration in your engine.\n// The type has to be described within the macro (you can either declare the struct or use a typedef). This is because ImVec2/ImU32 are likely not declared at the time you'd want to set your type up.\n// NOTE: IMGUI DOESN'T CLEAR THE STRUCTURE AND DOESN'T CALL A CONSTRUCTOR SO ANY CUSTOM FIELD WILL BE UNINITIALIZED. IF YOU ADD EXTRA FIELDS (SUCH AS A 'Z' COORDINATES) YOU WILL NEED TO CLEAR THEM DURING RENDER OR TO IGNORE THEM.\nIMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT;\n#endif\n\n// [Internal] For use by ImDrawList\nstruct ImDrawCmdHeader\n{\n    ImVec4          ClipRect;\n    ImTextureRef    TexRef;\n    unsigned int    VtxOffset;\n};\n\n// [Internal] For use by ImDrawListSplitter\nstruct ImDrawChannel\n{\n    ImVector<ImDrawCmd>         _CmdBuffer;\n    ImVector<ImDrawIdx>         _IdxBuffer;\n};\n\n// Split/Merge functions are used to split the draw list into different layers which can be drawn into out of order.\n// This is used by the Columns/Tables API, so items of each column can be batched together in a same draw call.\nstruct ImDrawListSplitter\n{\n    int                         _Current;    // Current channel number (0)\n    int                         _Count;      // Number of active channels (1+)\n    ImVector<ImDrawChannel>     _Channels;   // Draw channels (not resized down so _Count might be < Channels.Size)\n\n    inline ImDrawListSplitter()  { memset((void*)this, 0, sizeof(*this)); }\n    inline ~ImDrawListSplitter() { ClearFreeMemory(); }\n    inline void                 Clear() { _Current = 0; _Count = 1; } // Do not clear Channels[] so our allocations are reused next frame\n    IMGUI_API void              ClearFreeMemory();\n    IMGUI_API void              Split(ImDrawList* draw_list, int count);\n    IMGUI_API void              Merge(ImDrawList* draw_list);\n    IMGUI_API void              SetCurrentChannel(ImDrawList* draw_list, int channel_idx);\n};\n\n// Flags for ImDrawList functions\n// (Legacy: bit 0 must always correspond to ImDrawFlags_Closed to be backward compatible with old API using a bool. Bits 1..3 must be unused)\nenum ImDrawFlags_\n{\n    ImDrawFlags_None                        = 0,\n    ImDrawFlags_Closed                      = 1 << 0, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason)\n    ImDrawFlags_RoundCornersTopLeft         = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01.\n    ImDrawFlags_RoundCornersTopRight        = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02.\n    ImDrawFlags_RoundCornersBottomLeft      = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04.\n    ImDrawFlags_RoundCornersBottomRight     = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08.\n    ImDrawFlags_RoundCornersNone            = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag!\n    ImDrawFlags_RoundCornersTop             = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight,\n    ImDrawFlags_RoundCornersBottom          = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,\n    ImDrawFlags_RoundCornersLeft            = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft,\n    ImDrawFlags_RoundCornersRight           = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight,\n    ImDrawFlags_RoundCornersAll             = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,\n    ImDrawFlags_RoundCornersDefault_        = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified.\n    ImDrawFlags_RoundCornersMask_           = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone,\n    ImDrawFlags_ShadowCutOutShapeBackground = 1 << 9, // Do not render the shadow shape under the objects to be shadowed to save on fill-rate or facilitate blending. Slower on CPU.\n};\n\n// Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly.\n// It is however possible to temporarily alter flags between calls to ImDrawList:: functions.\nenum ImDrawListFlags_\n{\n    ImDrawListFlags_None                    = 0,\n    ImDrawListFlags_AntiAliasedLines        = 1 << 0,  // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles)\n    ImDrawListFlags_AntiAliasedLinesUseTex  = 1 << 1,  // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).\n    ImDrawListFlags_AntiAliasedFill         = 1 << 2,  // Enable anti-aliased edge around filled shapes (rounded rectangles, circles).\n    ImDrawListFlags_AllowVtxOffset          = 1 << 3,  // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled.\n};\n\n// Draw command list\n// This is the low-level list of polygons that ImGui:: functions are filling. At the end of the frame,\n// all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering.\n// Each dear imgui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to\n// access the current window draw list and draw custom primitives.\n// You can interleave normal ImGui:: calls and adding primitives to the current draw list.\n// In single viewport mode, top-left is == GetMainViewport()->Pos (generally 0,0), bottom-right is == GetMainViewport()->Pos+Size (generally io.DisplaySize).\n// You are totally free to apply whatever transformation matrix you want to the data (depending on the use of the transformation you may want to apply it to ClipRect as well!)\n// Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects.\nstruct ImDrawList\n{\n    // This is what you have to render\n    ImVector<ImDrawCmd>     CmdBuffer;          // Draw commands. Typically 1 command = 1 GPU draw call, unless the command is a callback.\n    ImVector<ImDrawIdx>     IdxBuffer;          // Index buffer. Each command consume ImDrawCmd::ElemCount of those\n    ImVector<ImDrawVert>    VtxBuffer;          // Vertex buffer.\n    ImDrawListFlags         Flags;              // Flags, you may poke into these to adjust anti-aliasing settings per-primitive.\n\n    // [Internal, used while building lists]\n    unsigned int            _VtxCurrentIdx;     // [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0.\n    ImDrawListSharedData*   _Data;              // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context)\n    ImDrawVert*             _VtxWritePtr;       // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)\n    ImDrawIdx*              _IdxWritePtr;       // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)\n    ImVector<ImVec2>        _Path;              // [Internal] current path building\n    ImDrawCmdHeader         _CmdHeader;         // [Internal] template of active commands. Fields should match those of CmdBuffer.back().\n    ImDrawListSplitter      _Splitter;          // [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!)\n    ImVector<ImVec4>        _ClipRectStack;     // [Internal]\n    ImVector<ImTextureRef>  _TextureStack;      // [Internal]\n    ImVector<ImU8>          _CallbacksDataBuf;  // [Internal]\n    float                   _FringeScale;       // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content\n    const char*             _OwnerName;         // Pointer to owner window's name for debugging\n\n    // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData().\n    // (advanced: you may create and use your own ImDrawListSharedData so you can use ImDrawList without ImGui, but that's more involved)\n    IMGUI_API ImDrawList(ImDrawListSharedData* shared_data);\n    IMGUI_API ~ImDrawList();\n\n    IMGUI_API void  PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false);  // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)\n    IMGUI_API void  PushClipRectFullScreen();\n    IMGUI_API void  PopClipRect();\n    IMGUI_API void  PushTexture(ImTextureRef tex_ref);\n    IMGUI_API void  PopTexture();\n    inline ImVec2   GetClipRectMin() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.x, cr.y); }\n    inline ImVec2   GetClipRectMax() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.z, cr.w); }\n\n    // Primitives\n    // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have \"inward\" anti-aliasing.\n    // - For rectangular primitives, \"p_min\" and \"p_max\" represent the upper-left and lower-right corners.\n    // - For circle primitives, use \"num_segments == 0\" to automatically calculate tessellation (preferred).\n    //   In older versions (until Dear ImGui 1.77) the AddCircle functions defaulted to num_segments == 12.\n    //   In future versions we will use textures to provide cheaper and higher-quality circles.\n    //   Use AddNgon() and AddNgonFilled() functions if you need to guarantee a specific number of sides.\n    IMGUI_API void  AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f);\n    IMGUI_API void  AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f);   // a: upper-left, b: lower-right (== upper-left + size)\n    IMGUI_API void  AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0);                     // a: upper-left, b: lower-right (== upper-left + size)\n    IMGUI_API void  AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left);\n    IMGUI_API void  AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness = 1.0f);\n    IMGUI_API void  AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col);\n    IMGUI_API void  AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness = 1.0f);\n    IMGUI_API void  AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col);\n    IMGUI_API void  AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments = 0, float thickness = 1.0f);\n    IMGUI_API void  AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments = 0);\n    IMGUI_API void  AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness = 1.0f);\n    IMGUI_API void  AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments);\n    IMGUI_API void  AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f);\n    IMGUI_API void  AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0);\n    IMGUI_API void  AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL);\n    IMGUI_API void  AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL);\n    IMGUI_API void  AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points)\n    IMGUI_API void  AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0);               // Quadratic Bezier (3 control points)\n\n    // General polygon\n    // - Only simple polygons are supported by filling functions (no self-intersections, no holes).\n    // - Concave polygon fill is more expensive than convex one: it has O(N^2) complexity. Provided as a convenience for the user but not used by the main library.\n    IMGUI_API void  AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);\n    IMGUI_API void  AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col);\n    IMGUI_API void  AddConcavePolyFilled(const ImVec2* points, int num_points, ImU32 col);\n\n    // Image primitives\n    // - Read FAQ to understand what ImTextureID/ImTextureRef are.\n    // - \"p_min\" and \"p_max\" represent the upper-left and lower-right corners of the rectangle.\n    // - \"uv_min\" and \"uv_max\" represent the normalized texture coordinates to use for those corners. Using (0,0)->(1,1) texture coordinates will generally display the entire texture.\n    IMGUI_API void  AddImage(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min = ImVec2(0, 0), const ImVec2& uv_max = ImVec2(1, 1), ImU32 col = IM_COL32_WHITE);\n    IMGUI_API void  AddImageQuad(ImTextureRef tex_ref, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1 = ImVec2(0, 0), const ImVec2& uv2 = ImVec2(1, 0), const ImVec2& uv3 = ImVec2(1, 1), const ImVec2& uv4 = ImVec2(0, 1), ImU32 col = IM_COL32_WHITE);\n    IMGUI_API void  AddImageRounded(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0);\n\n    // Shadows primitives\n    // [BETA] API\n    // - Add shadow for a object, with min/max or center/radius describing the object extents, and offset shifting the shadow.\n    // - Rounding parameters refer to the object itself, not the shadow!\n    // - By default, the area under the object is filled, because this is simpler to process.\n    //   Using the ImDrawFlags_ShadowCutOutShapeBackground flag makes the function not render this area and leave a hole under the object.\n    //    - Shadows w/ fill under the object: a bit faster for CPU, more pixels rendered, visible/darkening if used behind a transparent shape.\n    //      Typically used by: small, frequent objects, opaque objects, transparent objects if shadow darkening isn't an issue.\n    //    - Shadows w/ hole under the object: a bit slower for CPU, less pixels rendered, no difference if used behind a transparent shape.\n    //      Typically used by: large, infrequent objects, transparent objects if exact blending/color matter.\n    // - FIXME-SHADOWS: 'offset' + ImDrawFlags_ShadowCutOutShapeBackground are not currently supported together with AddShadowCircle(), AddShadowConvexPoly(), AddShadowNGon().\n    #define IMGUI_HAS_SHADOWS 1\n    IMGUI_API void  AddShadowRect(const ImVec2& obj_min, const ImVec2& obj_max, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags = 0, float obj_rounding = 0.0f);\n    IMGUI_API void  AddShadowCircle(const ImVec2& obj_center, float obj_radius, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags = 0, int obj_num_segments = 12);\n    IMGUI_API void  AddShadowConvexPoly(const ImVec2* points, int points_count, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags = 0);\n    IMGUI_API void  AddShadowNGon(const ImVec2& obj_center, float obj_radius, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags, int obj_num_segments);\n\n    // Stateful path API, add points then finish with PathFillConvex() or PathStroke()\n    // - Important: filled shapes must always use clockwise winding order! The anti-aliasing fringe depends on it. Counter-clockwise shapes will have \"inward\" anti-aliasing.\n    //   so e.g. 'PathArcTo(center, radius, PI * -0.5f, PI)' is ok, whereas 'PathArcTo(center, radius, PI, PI * -0.5f)' won't have correct anti-aliasing when followed by PathFillConvex().\n    inline    void  PathClear()                                                 { _Path.Size = 0; }\n    inline    void  PathLineTo(const ImVec2& pos)                               { _Path.push_back(pos); }\n    inline    void  PathLineToMergeDuplicate(const ImVec2& pos)                 { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); }\n    inline    void  PathFillConvex(ImU32 col)                                   { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; }\n    inline    void  PathFillConcave(ImU32 col)                                  { AddConcavePolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; }\n    inline    void  PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; }\n    IMGUI_API void  PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0);\n    IMGUI_API void  PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12);                // Use precomputed angles for a 12 steps circle\n    IMGUI_API void  PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments = 0); // Ellipse\n    IMGUI_API void  PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points)\n    IMGUI_API void  PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0);               // Quadratic Bezier (3 control points)\n    IMGUI_API void  PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawFlags flags = 0);\n\n    // Advanced: Draw Callbacks\n    // - May be used to alter render state (change sampler, blending, current shader). May be used to emit custom rendering commands (difficult to do correctly, but possible).\n    // - Use special ImDrawCallback_ResetRenderState callback to instruct backend to reset its render state to the default.\n    // - Your rendering loop must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. All standard backends are honoring this.\n    // - For some backends, the callback may access selected render-states exposed by the backend in a ImGui_ImplXXXX_RenderState structure pointed to by platform_io.Renderer_RenderState.\n    // - IMPORTANT: please be mindful of the different level of indirection between using size==0 (copying argument) and using size>0 (copying pointed data into a buffer).\n    //   - If userdata_size == 0: we copy/store the 'userdata' argument as-is. It will be available unmodified in ImDrawCmd::UserCallbackData during render.\n    //   - If userdata_size > 0,  we copy/store 'userdata_size' bytes pointed to by 'userdata'. We store them in a buffer stored inside the drawlist. ImDrawCmd::UserCallbackData will point inside that buffer so you have to retrieve data from there. Your callback may need to use ImDrawCmd::UserCallbackDataSize if you expect dynamically-sized data.\n    //   - Support for userdata_size > 0 was added in v1.91.4, October 2024. So earlier code always only allowed to copy/store a simple void*.\n    IMGUI_API void  AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size = 0);\n\n    // Advanced: Miscellaneous\n    IMGUI_API void  AddDrawCmd();                                               // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible\n    IMGUI_API ImDrawList* CloneOutput() const;                                  // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer. For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club instead.\n\n    // Advanced: Channels\n    // - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit FG primitives before BG primitives)\n    // - Use to minimize draw calls (e.g. if going back-and-forth between multiple clipping rectangles, prefer to append into separate channels then merge at the end)\n    // - This API shouldn't have been in ImDrawList in the first place!\n    //   Prefer using your own persistent instance of ImDrawListSplitter as you can stack them.\n    //   Using the ImDrawList::ChannelsXXXX you cannot stack a split over another.\n    inline void     ChannelsSplit(int count)    { _Splitter.Split(this, count); }\n    inline void     ChannelsMerge()             { _Splitter.Merge(this); }\n    inline void     ChannelsSetCurrent(int n)   { _Splitter.SetCurrentChannel(this, n); }\n\n    // Advanced: Primitives allocations\n    // - We render triangles (three vertices)\n    // - All primitives needs to be reserved via PrimReserve() beforehand.\n    IMGUI_API void  PrimReserve(int idx_count, int vtx_count);\n    IMGUI_API void  PrimUnreserve(int idx_count, int vtx_count);\n    IMGUI_API void  PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col);      // Axis aligned rectangle (composed of two triangles)\n    IMGUI_API void  PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col);\n    IMGUI_API void  PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col);\n    inline    void  PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col)    { _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; }\n    inline    void  PrimWriteIdx(ImDrawIdx idx)                                     { *_IdxWritePtr = idx; _IdxWritePtr++; }\n    inline    void  PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col)         { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index\n\n    // Obsolete names\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    inline    void  PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); }   // RENAMED in 1.92.0\n    inline    void  PopTextureID()                      { PopTexture(); }           // RENAMED in 1.92.0\n#endif\n    //inline  void  AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024)\n    //inline  void  AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); }                        // OBSOLETED in 1.90.5 (Mar 2024)\n    //inline  void  PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments = 0) { PathEllipticalArcTo(center, ImVec2(radius_x, radius_y), rot, a_min, a_max, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024)\n    //inline  void  AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); }                         // OBSOLETED in 1.80 (Jan 2021)\n    //inline  void  PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); }                                                                                // OBSOLETED in 1.80 (Jan 2021)\n\n    // [Internal helpers]\n    IMGUI_API void  _SetDrawListSharedData(ImDrawListSharedData* data);\n    IMGUI_API void  _ResetForNewFrame();\n    IMGUI_API void  _ClearFreeMemory();\n    IMGUI_API void  _PopUnusedDrawCmd();\n    IMGUI_API void  _TryMergeDrawCmds();\n    IMGUI_API void  _OnChangedClipRect();\n    IMGUI_API void  _OnChangedTexture();\n    IMGUI_API void  _OnChangedVtxOffset();\n    IMGUI_API void  _SetTexture(ImTextureRef tex_ref);\n    IMGUI_API int   _CalcCircleAutoSegmentCount(float radius) const;\n    IMGUI_API void  _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step);\n    IMGUI_API void  _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments);\n};\n\n// All draw data to render a Dear ImGui frame\n// (NB: the style and the naming convention here is a little inconsistent, we currently preserve them for backward compatibility purpose,\n// as this is one of the oldest structure exposed by the library! Basically, ImDrawList == CmdList)\nstruct ImDrawData\n{\n    bool                Valid;              // Only valid after Render() is called and before the next NewFrame() is called.\n    int                 CmdListsCount;      // == CmdLists.Size. (OBSOLETE: exists for legacy reasons). Number of ImDrawList* to render.\n    int                 TotalIdxCount;      // For convenience, sum of all ImDrawList's IdxBuffer.Size\n    int                 TotalVtxCount;      // For convenience, sum of all ImDrawList's VtxBuffer.Size\n    ImVector<ImDrawList*> CmdLists;         // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here.\n    ImVec2              DisplayPos;         // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications)\n    ImVec2              DisplaySize;        // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications)\n    ImVec2              FramebufferScale;   // Amount of pixels for each unit of DisplaySize. Copied from viewport->FramebufferScale (== io.DisplayFramebufferScale for main viewport). Generally (1,1) on normal display, (2,2) on OSX with Retina display.\n    ImGuiViewport*      OwnerViewport;      // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not).\n    ImVector<ImTextureData*>* Textures;     // List of textures to update. Most of the times the list is shared by all ImDrawData, has only 1 texture and it doesn't need any update. This almost always points to ImGui::GetPlatformIO().Textures[]. May be overridden or set to NULL if you want to manually update textures.\n\n    // Functions\n    ImDrawData()    { Clear(); }\n    IMGUI_API void  Clear();\n    IMGUI_API void  AddDrawList(ImDrawList* draw_list);     // Helper to add an external draw list into an existing ImDrawData.\n    IMGUI_API void  DeIndexAllBuffers();                    // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!\n    IMGUI_API void  ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution.\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Texture API (ImTextureFormat, ImTextureStatus, ImTextureRect, ImTextureData)\n//-----------------------------------------------------------------------------\n// In principle, the only data types that user/application code should care about are 'ImTextureRef' and 'ImTextureID'.\n// They are defined above in this header file. Read their description to the difference between ImTextureRef and ImTextureID.\n// FOR ALL OTHER ImTextureXXXX TYPES: ONLY CORE LIBRARY AND RENDERER BACKENDS NEED TO KNOW AND CARE ABOUT THEM.\n//-----------------------------------------------------------------------------\n\n#undef Status // X11 headers are leaking this.\n\n// We intentionally support a limited amount of texture formats to limit burden on CPU-side code and extension.\n// Most standard backends only support RGBA32 but we provide a single channel option for low-resource/embedded systems.\nenum ImTextureFormat\n{\n    ImTextureFormat_RGBA32,         // 4 components per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight * 4\n    ImTextureFormat_Alpha8,         // 1 component per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight\n};\n\n// Status of a texture to communicate with Renderer Backend.\nenum ImTextureStatus\n{\n    ImTextureStatus_OK,\n    ImTextureStatus_Destroyed,      // Backend destroyed the texture.\n    ImTextureStatus_WantCreate,     // Requesting backend to create the texture. Set status OK when done.\n    ImTextureStatus_WantUpdates,    // Requesting backend to update specific blocks of pixels (write to texture portions which have never been used before). Set status OK when done.\n    ImTextureStatus_WantDestroy,    // Requesting backend to destroy the texture. Set status to Destroyed when done.\n};\n\n// Coordinates of a rectangle within a texture.\n// When a texture is in ImTextureStatus_WantUpdates state, we provide a list of individual rectangles to copy to the graphics system.\n// You may use ImTextureData::Updates[] for the list, or ImTextureData::UpdateBox for a single bounding box.\nstruct ImTextureRect\n{\n    unsigned short      x, y;       // Upper-left coordinates of rectangle to update\n    unsigned short      w, h;       // Size of rectangle to update (in pixels)\n};\n\n// Specs and pixel storage for a texture used by Dear ImGui.\n// This is only useful for (1) core library and (2) backends. End-user/applications do not need to care about this.\n// Renderer Backends will create a GPU-side version of this.\n// Why does we store two identifiers: TexID and BackendUserData?\n// - ImTextureID    TexID           = lower-level identifier stored in ImDrawCmd. ImDrawCmd can refer to textures not created by the backend, and for which there's no ImTextureData.\n// - void*          BackendUserData = higher-level opaque storage for backend own book-keeping. Some backends may have enough with TexID and not need both.\n // In columns below: who reads/writes each fields? 'r'=read, 'w'=write, 'core'=main library, 'backend'=renderer backend\nstruct ImTextureData\n{\n    //------------------------------------------ core / backend ---------------------------------------\n    int                 UniqueID;               // w    -   // [DEBUG] Sequential index to facilitate identifying a texture when debugging/printing. Unique per atlas.\n    ImTextureStatus     Status;                 // rw   rw  // ImTextureStatus_OK/_WantCreate/_WantUpdates/_WantDestroy. Always use SetStatus() to modify!\n    void*               BackendUserData;        // -    rw  // Convenience storage for backend. Some backends may have enough with TexID.\n    ImTextureID         TexID;                  // r    w   // Backend-specific texture identifier. Always use SetTexID() to modify! The identifier will stored in ImDrawCmd::GetTexID() and passed to backend's RenderDrawData function.\n    ImTextureFormat     Format;                 // w    r   // ImTextureFormat_RGBA32 (default) or ImTextureFormat_Alpha8\n    int                 Width;                  // w    r   // Texture width\n    int                 Height;                 // w    r   // Texture height\n    int                 BytesPerPixel;          // w    r   // 4 or 1\n    unsigned char*      Pixels;                 // w    r   // Pointer to buffer holding 'Width*Height' pixels and 'Width*Height*BytesPerPixels' bytes.\n    ImTextureRect       UsedRect;               // w    r   // Bounding box encompassing all past and queued Updates[].\n    ImTextureRect       UpdateRect;             // w    r   // Bounding box encompassing all queued Updates[].\n    ImVector<ImTextureRect> Updates;            // w    r   // Array of individual updates.\n    int                 UnusedFrames;           // w    r   // In order to facilitate handling Status==WantDestroy in some backend: this is a count successive frames where the texture was not used. Always >0 when Status==WantDestroy.\n    unsigned short      RefCount;               // w    r   // Number of contexts using this texture. Used during backend shutdown.\n    bool                UseColors;              // w    r   // Tell whether our texture data is known to use colors (rather than just white + alpha).\n    bool                WantDestroyNextFrame;   // rw   -   // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame.\n\n    // Functions\n    ImTextureData()     { memset((void*)this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; }\n    ~ImTextureData()    { DestroyPixels(); }\n    IMGUI_API void      Create(ImTextureFormat format, int w, int h);\n    IMGUI_API void      DestroyPixels();\n    void*               GetPixels()                 { IM_ASSERT(Pixels != NULL); return Pixels; }\n    void*               GetPixelsAt(int x, int y)   { IM_ASSERT(Pixels != NULL); return Pixels + (x + y * Width) * BytesPerPixel; }\n    int                 GetSizeInBytes() const      { return Width * Height * BytesPerPixel; }\n    int                 GetPitch() const            { return Width * BytesPerPixel; }\n    ImTextureRef        GetTexRef()                 { ImTextureRef tex_ref; tex_ref._TexData = this; tex_ref._TexID = ImTextureID_Invalid; return tex_ref; }\n    ImTextureID         GetTexID() const            { return TexID; }\n\n    // Called by Renderer backend\n    // - Call SetTexID() and SetStatus() after honoring texture requests. Never modify TexID and Status directly!\n    // - A backend may decide to destroy a texture that we did not request to destroy, which is fine (e.g. freeing resources), but we immediately set the texture back in _WantCreate mode.\n    void    SetTexID(ImTextureID tex_id)            { TexID = tex_id; }\n    void    SetStatus(ImTextureStatus status)       { Status = status; if (status == ImTextureStatus_Destroyed && !WantDestroyNextFrame && Pixels != nullptr) Status = ImTextureStatus_WantCreate; }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont)\n//-----------------------------------------------------------------------------\n\n// [Internal] Shadow texture baking config\nstruct ImFontAtlasShadowTexConfig\n{\n    int     TexCornerSize;          // Size of the corner areas.\n    int     TexEdgeSize;            // Size of the edge areas (and by extension the center). Changing this is normally unnecessary.\n    float   TexFalloffPower;        // The power factor for the shadow falloff curve.\n    float   TexDistanceFieldOffset; // How much to offset the distance field by (allows over/under-shadowing, potentially useful for accommodating rounded corners on the \"casting\" shape).\n    bool    TexBlur;                // Do we want to Gaussian blur the shadow texture?\n\n    inline ImFontAtlasShadowTexConfig() { memset(this, 0, sizeof(*this)); }\n    IMGUI_API void SetupDefaults();\n    int     GetRectTexPadding() const   { return 2; }                                                   // Number of pixels of padding to add to the rectangular texture to avoid sampling artifacts at the edges.\n    int     CalcRectTexSize() const     { return TexCornerSize + TexEdgeSize + GetRectTexPadding(); }   // The size of the texture area required for the actual 2x2 rectangle shadow texture (after the redundant corners have been removed). Padding is required here to avoid sampling artifacts at the edge adjoining the removed corners.    int     CalcConvexTexWidth() const;                                                                // The width of the texture area required for the convex shape shadow texture.\n    int     GetConvexTexPadding() const { return 8; }                                                   // Number of pixels of padding to add to the convex shape texture to avoid sampling artifacts at the edges. This also acts as padding for the expanded corner triangles.\n    int     CalcConvexTexWidth() const;                                                                 // The width of the texture area required for the convex shape shadow texture.\n    int     CalcConvexTexHeight() const;                                                                // The height of the texture area required for the convex shape shadow texture.\n};\n\n// A font input/source (we may rename this to ImFontSource in the future)\nstruct ImFontConfig\n{\n    // Data Source\n    char            Name[40];               // <auto>   // Name (strictly to ease debugging, hence limited size buffer)\n    void*           FontData;               //          // TTF/OTF data\n    int             FontDataSize;           //          // TTF/OTF data size\n    bool            FontDataOwnedByAtlas;   // true     // TTF/OTF data ownership taken by the owner ImFontAtlas (will delete memory itself). SINCE 1.92, THE DATA NEEDS TO PERSIST FOR WHOLE DURATION OF ATLAS.\n\n    // Options\n    bool            MergeMode;              // false    // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights.\n    bool            PixelSnapH;             // false    // Align every glyph AdvanceX to pixel boundaries. Prevents fractional font size from working correctly! Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, OversampleH/V will default to 1.\n    ImS8            OversampleH;            // 0 (2)    // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1 or 2 depending on size. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details.\n    ImS8            OversampleV;            // 0 (1)    // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1. This is not really useful as we don't use sub-pixel positions on the Y axis.\n    ImWchar         EllipsisChar;           // 0        // Explicitly specify Unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.\n    float           SizePixels;             //          // Output size in pixels for rasterizer (more or less maps to the resulting font height).\n    const ImWchar*  GlyphRanges;            // NULL     // *LEGACY* THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list).\n    const ImWchar*  GlyphExcludeRanges;     // NULL     // Pointer to a small user-provided list of Unicode ranges (2 value per range, values are inclusive, zero-terminated list). This is very close to GlyphRanges[] but designed to exclude ranges from a font source, when merging fonts with overlapping glyphs. Use \"Input Glyphs Overlap Detection Tool\" to find about your overlapping ranges.\n    //ImVec2        GlyphExtraSpacing;      // 0, 0     // (REMOVED AT IT SEEMS LARGELY OBSOLETE. PLEASE REPORT IF YOU WERE USING THIS). Extra spacing (in pixels) between glyphs when rendered: essentially add to glyph->AdvanceX. Only X axis is supported for now.\n    ImVec2          GlyphOffset;            // 0, 0     // Offset (in pixels) all glyphs from this font input. Absolute value for default size, other sizes will scale this value.\n    float           GlyphMinAdvanceX;       // 0        // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font. Absolute value for default size, other sizes will scale this value.\n    float           GlyphMaxAdvanceX;       // FLT_MAX  // Maximum AdvanceX for glyphs\n    float           GlyphExtraAdvanceX;     // 0        // Extra spacing (in pixels) between glyphs. Please contact us if you are using this. // FIXME-NEWATLAS: Intentionally unscaled\n    ImU32           FontNo;                 // 0        // Index of font within TTF/OTF file\n    unsigned int    FontLoaderFlags;        // 0        // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure.\n    //unsigned int  FontBuilderFlags;       // --       // [Renamed in 1.92] Use FontLoaderFlags.\n    float           RasterizerMultiply;     // 1.0f     // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future.\n    float           RasterizerDensity;      // 1.0f     // [LEGACY: this only makes sense when ImGuiBackendFlags_RendererHasTextures is not supported] DPI scale multiplier for rasterization. Not altering other font metrics: makes it easy to swap between e.g. a 100% and a 400% fonts for a zooming display, or handle Retina screen. IMPORTANT: If you change this it is expected that you increase/decrease font scale roughly to the inverse of this, otherwise quality may look lowered.\n    float           ExtraSizeScale;         // 1.0f     // Extra rasterizer scale over SizePixels.\n\n    // [Internal]\n    ImFontFlags     Flags;                  // Font flags (don't use just yet, will be exposed in upcoming 1.92.X updates)\n    ImFont*         DstFont;                // Target font (as we merging fonts, multiple ImFontConfig may target the same font)\n    const ImFontLoader* FontLoader;         // Custom font backend for this source (default source is the one stored in ImFontAtlas)\n    void*           FontLoaderData;         // Font loader opaque storage (per font config)\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    bool            PixelSnapV;             // true    // [Obsoleted in 1.91.6] Align Scaled GlyphOffset.y to pixel boundaries.\n#endif\n    IMGUI_API ImFontConfig();\n};\n\n// Hold rendering data for one glyph.\n// (Note: some language parsers may fail to convert the bitfield members, in this case maybe drop store a single u32 or we can rework this)\nstruct ImFontGlyph\n{\n    unsigned int    Colored : 1;        // Flag to indicate glyph is colored and should generally ignore tinting (make it usable with no shift on little-endian as this is used in loops)\n    unsigned int    Visible : 1;        // Flag to indicate glyph has no visible pixels (e.g. space). Allow early out when rendering.\n    unsigned int    SourceIdx : 4;      // Index of source in parent font\n    unsigned int    Codepoint : 26;     // 0x0000..0x10FFFF\n    float           AdvanceX;           // Horizontal distance to advance cursor/layout position.\n    float           X0, Y0, X1, Y1;     // Glyph corners. Offsets from current cursor/layout position.\n    float           U0, V0, U1, V1;     // Texture coordinates for the current value of ImFontAtlas->TexRef. Cached equivalent of calling GetCustomRect() with PackId.\n    int             PackId;             // [Internal] ImFontAtlasRectId value (FIXME: Cold data, could be moved elsewhere?)\n\n    ImFontGlyph()   { memset((void*)this, 0, sizeof(*this)); PackId = -1; }\n};\n\n// Helper to build glyph ranges from text/string data. Feed your application strings/characters to it then call BuildRanges().\n// This is essentially a tightly packed of vector of 64k booleans = 8KB storage.\nstruct ImFontGlyphRangesBuilder\n{\n    ImVector<ImU32> UsedChars;            // Store 1-bit per Unicode code point (0=unused, 1=used)\n\n    ImFontGlyphRangesBuilder()              { Clear(); }\n    inline void     Clear()                 { int size_in_bytes = (IM_UNICODE_CODEPOINT_MAX + 1) / 8; UsedChars.resize(size_in_bytes / (int)sizeof(ImU32)); memset(UsedChars.Data, 0, (size_t)size_in_bytes); }\n    inline bool     GetBit(size_t n) const  { int off = (int)(n >> 5); ImU32 mask = 1u << (n & 31); return (UsedChars[off] & mask) != 0; }  // Get bit n in the array\n    inline void     SetBit(size_t n)        { int off = (int)(n >> 5); ImU32 mask = 1u << (n & 31); UsedChars[off] |= mask; }               // Set bit n in the array\n    inline void     AddChar(ImWchar c)      { SetBit(c); }                      // Add character\n    IMGUI_API void  AddText(const char* text, const char* text_end = NULL);     // Add string (each character of the UTF-8 string are added)\n    IMGUI_API void  AddRanges(const ImWchar* ranges);                           // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault()) to force add all of ASCII/Latin+Ext\n    IMGUI_API void  BuildRanges(ImVector<ImWchar>* out_ranges);                 // Output new ranges\n};\n\n// An opaque identifier to a rectangle in the atlas. -1 when invalid.\n// The rectangle may move and UV may be invalidated, use GetCustomRect() to retrieve it.\ntypedef int ImFontAtlasRectId;\n#define ImFontAtlasRectId_Invalid -1\n\n// Output of ImFontAtlas::GetCustomRect() when using custom rectangles.\n// Those values may not be cached/stored as they are only valid for the current value of atlas->TexRef\n// (this is in theory derived from ImTextureRect but we use separate structures for reasons)\nstruct ImFontAtlasRect\n{\n    unsigned short  x, y;               // Position (in current texture)\n    unsigned short  w, h;               // Size\n    ImVec2          uv0, uv1;           // UV coordinates (in current texture)\n\n    ImFontAtlasRect() { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// Flags for ImFontAtlas build\nenum ImFontAtlasFlags_\n{\n    ImFontAtlasFlags_None               = 0,\n    ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0,   // Don't round the height to next power of two\n    ImFontAtlasFlags_NoMouseCursors     = 1 << 1,   // Don't build software mouse cursors into the atlas (save a little texture memory)\n    ImFontAtlasFlags_NoBakedLines       = 1 << 2,   // Don't build thick line textures into the atlas (save a little texture memory, allow support for point/nearest filtering). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU).\n};\n\n// Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding:\n//  - One or more fonts.\n//  - Custom graphics data needed to render the shapes needed by Dear ImGui.\n//  - Mouse cursor shapes for software cursor rendering (unless setting 'Flags |= ImFontAtlasFlags_NoMouseCursors' in the font atlas).\n//  - If you don't call any AddFont*** functions, the default font embedded in the code will be loaded for you.\n// It is the rendering backend responsibility to upload texture into your graphics API:\n//  - ImGui_ImplXXXX_RenderDrawData() functions generally iterate platform_io->Textures[] to create/update/destroy each ImTextureData instance.\n//  - Backend then set ImTextureData's TexID and BackendUserData.\n//  - Texture id are passed back to you during rendering to identify the texture. Read FAQ entry about ImTextureID/ImTextureRef for more details.\n// Legacy path:\n//  - Call Build() + GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data.\n//  - Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture in a format natural to your graphics API.\n// Common pitfalls:\n// - If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persists up until the\n//   atlas is build (when calling GetTexData*** or Build()). We only copy the pointer, not the data.\n// - Important: By default, AddFontFromMemoryTTF() takes ownership of the data. Even though we are not writing to it, we will free the pointer on destruction.\n//   You can set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed,\n// - Even though many functions are suffixed with \"TTF\", OTF data is supported just as well.\n// - This is an old API and it is currently awkward for those and various other reasons! We will address them in the future!\nstruct ImFontAtlas\n{\n    IMGUI_API ImFontAtlas();\n    IMGUI_API ~ImFontAtlas();\n    IMGUI_API ImFont*           AddFont(const ImFontConfig* font_cfg);\n    IMGUI_API ImFont*           AddFontDefault(const ImFontConfig* font_cfg = NULL);        // Selects between AddFontDefaultVector() and AddFontDefaultBitmap().\n    IMGUI_API ImFont*           AddFontDefaultVector(const ImFontConfig* font_cfg = NULL);  // Embedded scalable font. Recommended at any higher size.\n    IMGUI_API ImFont*           AddFontDefaultBitmap(const ImFontConfig* font_cfg = NULL);  // Embedded classic pixel-clean font. Recommended at Size 13px with no scaling.\n    IMGUI_API ImFont*           AddFontFromFileTTF(const char* filename, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL);\n    IMGUI_API ImFont*           AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed.\n    IMGUI_API ImFont*           AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp.\n    IMGUI_API ImFont*           AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL);              // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter.\n    IMGUI_API void              RemoveFont(ImFont* font);\n\n    IMGUI_API void              Clear();                    // Clear everything (input fonts, output glyphs/textures).\n    IMGUI_API void              CompactCache();             // Compact cached glyphs and texture.\n    IMGUI_API void              SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime.\n\n    // As we are transitioning toward a new font system, we expect to obsolete those soon:\n    IMGUI_API void              ClearInputData();           // [OBSOLETE] Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts.\n    IMGUI_API void              ClearFonts();               // [OBSOLETE] Clear input+output font data (same as ClearInputData() + glyphs storage, UV coordinates).\n    IMGUI_API void              ClearTexData();             // [OBSOLETE] Clear CPU-side copy of the texture data. Saves RAM once the texture has been copied to graphics memory.\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    // Legacy path for build atlas + retrieving pixel data.\n    // - User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID().\n    // - The pitch is always = Width * BytesPerPixels (1 or 4)\n    // - Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into\n    //   the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste).\n    // - From 1.92 with backends supporting ImGuiBackendFlags_RendererHasTextures:\n    //   - Calling Build(), GetTexDataAsAlpha8(), GetTexDataAsRGBA32() is not needed.\n    //   - In backend: replace calls to ImFontAtlas::SetTexID() with calls to ImTextureData::SetTexID() after honoring texture creation.\n    IMGUI_API bool  Build();                    // Build pixels data. This is called automatically for you by the GetTexData*** functions.\n    IMGUI_API void  GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel\n    IMGUI_API void  GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel\n    void            SetTexID(ImTextureID id)    { IM_ASSERT(TexRef._TexID == ImTextureID_Invalid); TexRef._TexData->TexID = id; }                               // Called by legacy backends. May be called before texture creation.\n    void            SetTexID(ImTextureRef id)   { IM_ASSERT(TexRef._TexID == ImTextureID_Invalid && id._TexData == NULL); TexRef._TexData->TexID = id._TexID; } // Called by legacy backends.\n    bool            IsBuilt() const { return Fonts.Size > 0 && TexIsBuilt; } // Bit ambiguous: used to detect when user didn't build texture but effectively we should check TexID != 0 except that would be backend dependent..\n#endif\n\n    //-------------------------------------------\n    // Glyph Ranges\n    //-------------------------------------------\n\n    // Since 1.92: specifying glyph ranges is only useful/necessary if your backend doesn't support ImGuiBackendFlags_RendererHasTextures!\n    IMGUI_API const ImWchar*    GetGlyphRangesDefault();                // Basic Latin, Extended Latin\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list)\n    // NB: Make sure that your string are UTF-8 and NOT in your local code page.\n    // Read https://github.com/ocornut/imgui/blob/master/docs/FONTS.md/#about-utf-8-encoding for details.\n    // NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data.\n    IMGUI_API const ImWchar*    GetGlyphRangesGreek();                  // Default + Greek and Coptic\n    IMGUI_API const ImWchar*    GetGlyphRangesKorean();                 // Default + Korean characters\n    IMGUI_API const ImWchar*    GetGlyphRangesJapanese();               // Default + Hiragana, Katakana, Half-Width, Selection of 2999 Ideographs\n    IMGUI_API const ImWchar*    GetGlyphRangesChineseFull();            // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs\n    IMGUI_API const ImWchar*    GetGlyphRangesChineseSimplifiedCommon();// Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese\n    IMGUI_API const ImWchar*    GetGlyphRangesCyrillic();               // Default + about 400 Cyrillic characters\n    IMGUI_API const ImWchar*    GetGlyphRangesThai();                   // Default + Thai characters\n    IMGUI_API const ImWchar*    GetGlyphRangesVietnamese();             // Default + Vietnamese characters\n#endif\n\n    //-------------------------------------------\n    // [ALPHA] Custom Rectangles/Glyphs API\n    //-------------------------------------------\n\n    // Register and retrieve custom rectangles\n    // - You can request arbitrary rectangles to be packed into the atlas, for your own purpose.\n    // - Since 1.92.0, packing is done immediately in the function call (previously packing was done during the Build call)\n    // - You can render your pixels into the texture right after calling the AddCustomRect() functions.\n    // - VERY IMPORTANT:\n    //   - Texture may be created/resized at any time when calling ImGui or ImFontAtlas functions.\n    //   - IT WILL INVALIDATE RECTANGLE DATA SUCH AS UV COORDINATES. Always use latest values from GetCustomRect().\n    //   - UV coordinates are associated to the current texture identifier aka 'atlas->TexRef'. Both TexRef and UV coordinates are typically changed at the same time.\n    // - If you render colored output into your custom rectangles: set 'atlas->TexPixelsUseColors = true' as this may help some backends decide of preferred texture format.\n    // - Read docs/FONTS.md for more details about using colorful icons.\n    // - Note: this API may be reworked further in order to facilitate supporting e.g. multi-monitor, varying DPI settings.\n    // - (Pre-1.92 names) ------------> (1.92 names)\n    //   - GetCustomRectByIndex()   --> Use GetCustomRect()\n    //   - CalcCustomRectUV()       --> Use GetCustomRect() and read uv0, uv1 fields.\n    //   - AddCustomRectRegular()   --> Renamed to AddCustomRect()\n    //   - AddCustomRectFontGlyph() --> Prefer using custom ImFontLoader inside ImFontConfig\n    //   - ImFontAtlasCustomRect    --> Renamed to ImFontAtlasRect\n    IMGUI_API ImFontAtlasRectId AddCustomRect(int width, int height, ImFontAtlasRect* out_r = NULL);// Register a rectangle. Return -1 (ImFontAtlasRectId_Invalid) on error.\n    IMGUI_API void              RemoveCustomRect(ImFontAtlasRectId id);                             // Unregister a rectangle. Existing pixels will stay in texture until resized / garbage collected.\n    IMGUI_API bool              GetCustomRect(ImFontAtlasRectId id, ImFontAtlasRect* out_r) const;  // Get rectangle coordinates for current texture. Valid immediately, never store this (read above)!\n\n    //-------------------------------------------\n    // Members\n    //-------------------------------------------\n\n    // Input\n    ImFontAtlasFlags            Flags;              // Build flags (see ImFontAtlasFlags_)\n    ImTextureFormat             TexDesiredFormat;   // Desired texture format (default to ImTextureFormat_RGBA32 but may be changed to ImTextureFormat_Alpha8).\n    int                         TexGlyphPadding;    // FIXME: Should be called \"TexPackPadding\". Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false).\n    int                         TexMinWidth;        // Minimum desired texture width. Must be a power of two. Default to 512.\n    int                         TexMinHeight;       // Minimum desired texture height. Must be a power of two. Default to 128.\n    int                         TexMaxWidth;        // Maximum desired texture width. Must be a power of two. Default to 8192.\n    int                         TexMaxHeight;       // Maximum desired texture height. Must be a power of two. Default to 8192.\n    void*                       UserData;           // Store your own atlas related user-data (if e.g. you have multiple font atlas).\n\n    // Output\n    // - Because textures are dynamically created/resized, the current texture identifier may changed at *ANY TIME* during the frame.\n    // - This should not affect you as you can always use the latest value. But note that any precomputed UV coordinates are only valid for the current TexRef.\n#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImTextureRef                TexRef;             // Latest texture identifier == TexData->GetTexRef().\n#else\n    union { ImTextureRef TexRef; ImTextureRef TexID; }; // Latest texture identifier == TexData->GetTexRef(). // RENAMED TexID to TexRef in 1.92.0.\n#endif\n    ImTextureData*              TexData;            // Latest texture.\n\n    // [Internal]\n    ImVector<ImTextureData*>    TexList;            // Texture list (most often TexList.Size == 1). TexData is always == TexList.back(). DO NOT USE DIRECTLY, USE GetDrawData().Textures[]/GetPlatformIO().Textures[] instead!\n    bool                        Locked;             // Marked as locked during ImGui::NewFrame()..EndFrame() scope if TexUpdates are not supported. Any attempt to modify the atlas will assert.\n    bool                        RendererHasTextures;// Copy of (BackendFlags & ImGuiBackendFlags_RendererHasTextures) from supporting context.\n    bool                        TexIsBuilt;         // Set when texture was built matching current font input. Mostly useful for legacy IsBuilt() call.\n    bool                        TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format or conversion process.\n    ImVec2                      TexUvScale;         // = (1.0f/TexData->TexWidth, 1.0f/TexData->TexHeight). May change as new texture gets created.\n    ImVec2                      TexUvWhitePixel;    // Texture coordinates to a white pixel. May change as new texture gets created.\n    ImVector<ImFont*>           Fonts;              // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font.\n    ImVector<ImFontConfig>      Sources;            // Source/configuration data\n    ImVec4                      TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1];  // UVs for baked anti-aliased lines\n    int                         TexNextUniqueID;    // Next value to be stored in TexData->UniqueID\n    int                         FontNextUniqueID;   // Next value to be stored in ImFont->FontID\n    ImVector<ImDrawListSharedData*> DrawListSharedDatas; // List of users for this atlas. Typically one per Dear ImGui context.\n    ImFontAtlasBuilder*         Builder;            // Opaque interface to our data that doesn't need to be public and may be discarded when rebuilding.\n    const ImFontLoader*         FontLoader;         // Font loader opaque interface (default to use FreeType when IMGUI_ENABLE_FREETYPE is defined, otherwise default to use stb_truetype). Use SetFontLoader() to change this at runtime.\n    const char*                 FontLoaderName;     // Font loader name (for display e.g. in About box) == FontLoader->Name\n    void*                       FontLoaderData;     // Font backend opaque storage\n    unsigned int                FontLoaderFlags;    // Shared flags (for all fonts) for font loader. THIS IS BUILD IMPLEMENTATION DEPENDENT (e.g. Per-font override is also available in ImFontConfig).\n    int                         RefCount;           // Number of contexts using this atlas\n    ImGuiContext*               OwnerContext;       // Context which own the atlas will be in charge of updating and destroying it.\n\n    // [Internal] Shadow data\n    ImFontAtlasRectId           ShadowRectIds[2];   // IDs of rect for shadow textures\n    ImVec4                      ShadowRectUvs[10];  // UV coordinates for shadow textures, 9 for the rectangle shadows and the final entry for the convex shape shadows\n    ImFontAtlasShadowTexConfig  ShadowTexConfig;    // Shadow texture baking config\n\n    // [Obsolete]\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    // Legacy: You can request your rectangles to be mapped as font glyph (given a font + Unicode point), so you can render e.g. custom colorful icons and use them as regular glyphs. --> Prefer using a custom ImFontLoader.\n    ImFontAtlasRect             TempRect;           // For old GetCustomRectByIndex() API\n    inline ImFontAtlasRectId    AddCustomRectRegular(int w, int h)                                                          { return AddCustomRect(w, h); }                             // RENAMED in 1.92.0\n    inline const ImFontAtlasRect* GetCustomRectByIndex(ImFontAtlasRectId id)                                                { return GetCustomRect(id, &TempRect) ? &TempRect : NULL; } // OBSOLETED in 1.92.0\n    inline void                 CalcCustomRectUV(const ImFontAtlasRect* r, ImVec2* out_uv_min, ImVec2* out_uv_max) const    { *out_uv_min = r->uv0; *out_uv_max = r->uv1; }             // OBSOLETED in 1.92.0\n    IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0));                            // OBSOLETED in 1.92.0: Use custom ImFontLoader in ImFontConfig\n    IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0));    // ADDED AND OBSOLETED in 1.92.0\n#endif\n    //unsigned int                      FontBuilderFlags;        // OBSOLETED in 1.92.0: Renamed to FontLoaderFlags.\n    //int                               TexDesiredWidth;         // OBSOLETED in 1.92.0: Force texture width before calling Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height.\n    //typedef ImFontAtlasRect           ImFontAtlasCustomRect;   // OBSOLETED in 1.92.0\n    //typedef ImFontAtlasCustomRect     CustomRect;              // OBSOLETED in 1.72+\n    //typedef ImFontGlyphRangesBuilder  GlyphRangesBuilder;      // OBSOLETED in 1.67+\n};\n\n// Font runtime data for a given size\n// Important: pointers to ImFontBaked are only valid for the current frame.\nstruct ImFontBaked\n{\n    // [Internal] Members: Hot ~20/24 bytes (for CalcTextSize)\n    ImVector<float>             IndexAdvanceX;      // 12-16 // out // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI).\n    float                       FallbackAdvanceX;   // 4     // out // FindGlyph(FallbackChar)->AdvanceX\n    float                       Size;               // 4     // in  // Height of characters/line, set during loading (doesn't change after loading)\n    float                       RasterizerDensity;  // 4     // in  // Density this is baked at\n\n    // [Internal] Members: Hot ~28/36 bytes (for RenderText loop)\n    ImVector<ImU16>             IndexLookup;        // 12-16 // out // Sparse. Index glyphs by Unicode code-point.\n    ImVector<ImFontGlyph>       Glyphs;             // 12-16 // out // All glyphs.\n    int                         FallbackGlyphIndex; // 4     // out // Index of FontFallbackChar\n\n    // [Internal] Members: Cold\n    float                       Ascent, Descent;    // 4+4   // out // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled)\n    unsigned int                MetricsTotalSurface:26;// 3  // out // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs)\n    unsigned int                WantDestroy:1;         // 0  //     // Queued for destroy\n    unsigned int                LoadNoFallback:1;      // 0  //     // Disable loading fallback in lower-level calls.\n    unsigned int                LoadNoRenderOnLayout:1;// 0  //     // Enable a two-steps mode where CalcTextSize() calls will load AdvanceX *without* rendering/packing glyphs. Only advantageous if you know that the glyph is unlikely to actually be rendered, otherwise it is slower because we'd do one query on the first CalcTextSize and one query on the first Draw.\n    int                         LastUsedFrame;         // 4  //     // Record of that time this was bounds\n    ImGuiID                     BakedId;            // 4     //     // Unique ID for this baked storage\n    ImFont*                     OwnerFont;          // 4-8   // in  // Parent font\n    void*                       FontLoaderDatas;    // 4-8   //     // Font loader opaque storage (per baked font * sources): single contiguous buffer allocated by imgui, passed to loader.\n\n    // Functions\n    IMGUI_API ImFontBaked();\n    IMGUI_API void              ClearOutputData();\n    IMGUI_API ImFontGlyph*      FindGlyph(ImWchar c);               // Return U+FFFD glyph if requested glyph doesn't exists.\n    IMGUI_API ImFontGlyph*      FindGlyphNoFallback(ImWchar c);     // Return NULL if glyph doesn't exist\n    IMGUI_API float             GetCharAdvance(ImWchar c);\n    IMGUI_API bool              IsGlyphLoaded(ImWchar c);\n};\n\n// Font flags\n// (in future versions as we redesign font loading API, this will become more important and better documented. for now please consider this as internal/advanced use)\nenum ImFontFlags_\n{\n    ImFontFlags_None                    = 0,\n    ImFontFlags_NoLoadError             = 1 << 1,   // Disable throwing an error/assert when calling AddFontXXX() with missing file/data. Calling code is expected to check AddFontXXX() return value.\n    ImFontFlags_NoLoadGlyphs            = 1 << 2,   // [Internal] Disable loading new glyphs.\n    ImFontFlags_LockBakedSizes          = 1 << 3,   // [Internal] Disable loading new baked sizes, disable garbage collecting current ones. e.g. if you want to lock a font to a single size. Important: if you use this to preload given sizes, consider the possibility of multiple font density used on Retina display.\n};\n\n// Font runtime data and rendering\n// - ImFontAtlas automatically loads a default embedded font for you if you didn't load one manually.\n// - Since 1.92.0 a font may be rendered as any size! Therefore a font doesn't have one specific size.\n// - Use 'font->GetFontBaked(size)' to retrieve the ImFontBaked* corresponding to a given size.\n// - If you used g.Font + g.FontSize (which is frequent from the ImGui layer), you can use g.FontBaked as a shortcut, as g.FontBaked == g.Font->GetFontBaked(g.FontSize).\nstruct ImFont\n{\n    // [Internal] Members: Hot ~12-20 bytes\n    ImFontBaked*                LastBaked;          // 4-8   // Cache last bound baked. NEVER USE DIRECTLY. Use GetFontBaked().\n    ImFontAtlas*                OwnerAtlas;         // 4-8   // What we have been loaded into.\n    ImFontFlags                 Flags;              // 4     // Font flags.\n    float                       CurrentRasterizerDensity;    // Current rasterizer density. This is a varying state of the font.\n\n    // [Internal] Members: Cold ~24-52 bytes\n    // Conceptually Sources[] is the list of font sources merged to create this font.\n    ImGuiID                     FontId;             // Unique identifier for the font\n    float                       LegacySize;         // 4     // in  // Font size passed to AddFont(). Use for old code calling PushFont() expecting to use that size. (use ImGui::GetFontBaked() to get font baked at current bound size).\n    ImVector<ImFontConfig*>     Sources;            // 16    // in  // List of sources. Pointers within OwnerAtlas->Sources[]\n    ImWchar                     EllipsisChar;       // 2-4   // out // Character used for ellipsis rendering ('...'). If you ever want to temporarily swap this for an alternative/dummy char, make sure to clear EllipsisAutoBake.\n    ImWchar                     FallbackChar;       // 2-4   // out // Character used if a glyph isn't found (U+FFFD, '?')\n    ImU8                        Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/8192/8]; // 1 bytes if ImWchar=ImWchar16, 17 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 8K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints.\n    bool                        EllipsisAutoBake;   // 1     //     // Mark when the \"...\" glyph (== EllipsisChar) needs to be generated by combining multiple '.'.\n    ImGuiStorage                RemapPairs;         // 16    //     // Remapping pairs when using AddRemapChar(), otherwise empty.\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    float                       Scale;              // 4     // in  // Legacy base font scale (~1.0f), multiplied by the per-window font scale which you can adjust with SetWindowFontScale()\n#endif\n\n    // Methods\n    IMGUI_API ImFont();\n    IMGUI_API ~ImFont();\n    IMGUI_API bool              IsGlyphInFont(ImWchar c);\n    bool                        IsLoaded() const                { return OwnerAtlas != NULL; }\n    const char*                 GetDebugName() const            { return Sources.Size ? Sources[0]->Name : \"<unknown>\"; } // Fill ImFontConfig::Name.\n\n    // [Internal] Don't use!\n    // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable.\n    // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable.\n    IMGUI_API ImFontBaked*      GetFontBaked(float font_size, float density = -1.0f);  // Get or create baked data for given size\n    IMGUI_API ImVec2            CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** out_remaining = NULL);\n    IMGUI_API const char*       CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width);\n    IMGUI_API void              RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip = NULL);\n    IMGUI_API void              RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, ImDrawTextFlags flags = 0);\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    inline const char*          CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); }\n#endif\n\n    // [Internal] Don't use!\n    IMGUI_API void              ClearOutputData();\n    IMGUI_API void              AddRemapChar(ImWchar from_codepoint, ImWchar to_codepoint); // Makes 'from_codepoint' character points to 'to_codepoint' glyph.\n    IMGUI_API bool              IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last);\n};\n\n// This is provided for consistency (but we don't actually use this)\ninline ImTextureID ImTextureRef::GetTexID() const\n{\n    IM_ASSERT(!(_TexData != NULL && _TexID != ImTextureID_Invalid));\n    return _TexData ? _TexData->TexID : _TexID;\n}\n\n// Using an indirection to avoid patching ImDrawCmd after a SetTexID() call (but this could be an alternative solution too)\ninline ImTextureID ImDrawCmd::GetTexID() const\n{\n    // If you are getting this assert with ImTextureID_Invalid == 0 and your ImTextureID is used to store an index:\n    // - You can add '#define ImTextureID_Invalid ((ImTextureID)-1)' in your imconfig file.\n    // If you are getting this assert with a renderer backend with support for ImGuiBackendFlags_RendererHasTextures (1.92+):\n    // - You must correctly iterate and handle ImTextureData requests stored in ImDrawData::Textures[]. See docs/BACKENDS.md.\n    ImTextureID tex_id = TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID; // == TexRef.GetTexID() above.\n    if (TexRef._TexData != NULL)\n        IM_ASSERT(tex_id != ImTextureID_Invalid && \"ImDrawCmd is referring to ImTextureData that wasn't uploaded to graphics system. Backend must call ImTextureData::SetTexID() after handling ImTextureStatus_WantCreate request!\");\n    return tex_id;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Viewports\n//-----------------------------------------------------------------------------\n\n// Flags stored in ImGuiViewport::Flags, giving indications to the platform backends.\nenum ImGuiViewportFlags_\n{\n    ImGuiViewportFlags_None                     = 0,\n    ImGuiViewportFlags_IsPlatformWindow         = 1 << 0,   // Represent a Platform Window\n    ImGuiViewportFlags_IsPlatformMonitor        = 1 << 1,   // Represent a Platform Monitor (unused yet)\n    ImGuiViewportFlags_OwnedByApp               = 1 << 2,   // Platform Window: Is created/managed by the user application? (rather than our backend)\n    ImGuiViewportFlags_NoDecoration             = 1 << 3,   // Platform Window: Disable platform decorations: title bar, borders, etc. (generally set all windows, but if ImGuiConfigFlags_ViewportsDecoration is set we only set this on popups/tooltips)\n    ImGuiViewportFlags_NoTaskBarIcon            = 1 << 4,   // Platform Window: Disable platform task bar icon (generally set on popups/tooltips, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcon is set)\n    ImGuiViewportFlags_NoFocusOnAppearing       = 1 << 5,   // Platform Window: Don't take focus when created.\n    ImGuiViewportFlags_NoFocusOnClick           = 1 << 6,   // Platform Window: Don't take focus when clicked on.\n    ImGuiViewportFlags_NoInputs                 = 1 << 7,   // Platform Window: Make mouse pass through so we can drag this window while peaking behind it.\n    ImGuiViewportFlags_NoRendererClear          = 1 << 8,   // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely).\n    ImGuiViewportFlags_NoAutoMerge              = 1 << 9,   // Platform Window: Avoid merging this window into another host window. This can only be set via ImGuiWindowClass viewport flags override (because we need to now ahead if we are going to create a viewport in the first place!).\n    ImGuiViewportFlags_TopMost                  = 1 << 10,  // Platform Window: Display on top (for tooltips only).\n    ImGuiViewportFlags_CanHostOtherWindows      = 1 << 11,  // Viewport can host multiple imgui windows (secondary viewports are associated to a single window). // FIXME: In practice there's still probably code making the assumption that this is always and only on the MainViewport. Will fix once we add support for \"no main viewport\".\n\n    // Output status flags (from Platform)\n    ImGuiViewportFlags_IsMinimized              = 1 << 12,  // Platform Window: Window is minimized, can skip render. When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport.\n    ImGuiViewportFlags_IsFocused                = 1 << 13,  // Platform Window: Window is focused (last call to Platform_GetWindowFocus() returned true)\n};\n\n// - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.\n// - With multi-viewport enabled, we extend this concept to have multiple active viewports.\n// - In the future we will extend this concept further to also represent Platform Monitor and support a \"no main platform window\" operation mode.\n// - About Main Area vs Work Area:\n//   - Main Area = entire viewport.\n//   - Work Area = entire viewport minus sections used by main menu bars (for platform windows), or by task bar (for platform monitor).\n//   - Windows are generally trying to stay within the Work Area of their host viewport.\nstruct ImGuiViewport\n{\n    ImGuiID             ID;                     // Unique identifier for the viewport\n    ImGuiViewportFlags  Flags;                  // See ImGuiViewportFlags_\n    ImVec2              Pos;                    // Main Area: Position of the viewport (Dear ImGui coordinates are the same as OS desktop/native coordinates)\n    ImVec2              Size;                   // Main Area: Size of the viewport.\n    ImVec2              FramebufferScale;       // Density of the viewport for Retina display (always 1,1 on Windows, may be 2,2 etc on macOS/iOS). This will affect font rasterizer density.\n    ImVec2              WorkPos;                // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos)\n    ImVec2              WorkSize;               // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size)\n    float               DpiScale;               // 1.0f = 96 DPI = No extra scale.\n    ImGuiID             ParentViewportId;       // (Advanced) 0: no parent. Instruct the platform backend to setup a parent/child relationship between platform windows.\n    ImGuiViewport*      ParentViewport;         // (Advanced) Direct shortcut to ImGui::FindViewportByID(ParentViewportId). NULL: no parent.\n    ImDrawData*         DrawData;               // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame().\n\n    // Platform/Backend Dependent Data\n    // Our design separate the Renderer and Platform backends to facilitate combining default backends with each others.\n    // When our create your own backend for a custom engine, it is possible that both Renderer and Platform will be handled\n    // by the same system and you may not need to use all the UserData/Handle fields.\n    // The library never uses those fields, they are merely storage to facilitate backend implementation.\n    void*               RendererUserData;       // void* to hold custom data structure for the renderer (e.g. swap chain, framebuffers etc.). generally set by your Renderer_CreateWindow function.\n    void*               PlatformUserData;       // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). generally set by your Platform_CreateWindow function.\n    void*               PlatformHandle;         // void* to hold higher-level, platform window handle (e.g. HWND for Win32 backend, Uint32 WindowID for SDL, GLFWWindow* for GLFW), for FindViewportByPlatformHandle().\n    void*               PlatformHandleRaw;      // void* to hold lower-level, platform-native window handle (always HWND on Win32 platform, unused for other platforms).\n    bool                PlatformWindowCreated;  // Platform window has been created (Platform_CreateWindow() has been called). This is false during the first frame where a viewport is being created.\n    bool                PlatformRequestMove;    // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position)\n    bool                PlatformRequestResize;  // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size)\n    bool                PlatformRequestClose;   // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4)\n\n    ImGuiViewport()     { memset((void*)this, 0, sizeof(*this)); }\n    ~ImGuiViewport()    { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); }\n\n    // Helpers\n    ImVec2              GetCenter() const       { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); }\n    ImVec2              GetWorkCenter() const   { return ImVec2(WorkPos.x + WorkSize.x * 0.5f, WorkPos.y + WorkSize.y * 0.5f); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformMonitor, ImGuiPlatformImeData)\n//-----------------------------------------------------------------------------\n\n// [BETA] (Optional) Multi-Viewport Support!\n// If you are new to Dear ImGui and trying to integrate it into your engine, you can probably ignore this for now.\n//\n// This feature allows you to seamlessly drag Dear ImGui windows outside of your application viewport.\n// This is achieved by creating new Platform/OS windows on the fly, and rendering into them.\n// Dear ImGui manages the viewport structures, and the backend create and maintain one Platform/OS window for each of those viewports.\n//\n// See Recap:   https://github.com/ocornut/imgui/wiki/Multi-Viewports\n// See Glossary https://github.com/ocornut/imgui/wiki/Glossary for details about some of the terminology.\n//\n// About the coordinates system:\n// - When multi-viewports are enabled, all Dear ImGui coordinates become absolute coordinates (same as OS coordinates!)\n// - So e.g. ImGui::SetNextWindowPos(ImVec2(0,0)) will position a window relative to your primary monitor!\n// - If you want to position windows relative to your main application viewport, use ImGui::GetMainViewport()->Pos as a base position.\n//\n// Steps to use multi-viewports in your application, when using a default backend from the examples/ folder:\n// - Application:  Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.\n// - Backend:      The backend initialization will setup all necessary ImGuiPlatformIO's functions and update monitors info every frame.\n// - Application:  In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render().\n// - Application:  Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls.\n//\n// Steps to use multi-viewports in your application, when using a custom backend:\n// - Important:    THIS IS NOT EASY TO DO and comes with many subtleties not described here!\n//                 It's also an experimental feature, so some of the requirements may evolve.\n//                 Consider using default backends if you can. Either way, carefully follow and refer to examples/ backends for details.\n// - Application:  Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.\n// - Backend:      Hook ImGuiPlatformIO's Platform_* and Renderer_* callbacks (see below).\n//                 Set 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports' and 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports'.\n//                 Update ImGuiPlatformIO's Monitors list every frame.\n//                 Update MousePos every frame, in absolute coordinates.\n// - Application:  In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render().\n//                 You may skip calling RenderPlatformWindowsDefault() if its API is not convenient for your needs. Read comments below.\n// - Application:  Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls.\n//\n// About ImGui::RenderPlatformWindowsDefault():\n// - This function is a mostly a _helper_ for the common-most cases, and to facilitate using default backends.\n// - You can check its simple source code to understand what it does.\n//   It basically iterates secondary viewports and call 4 functions that are setup in ImGuiPlatformIO, if available:\n//     Platform_RenderWindow(), Renderer_RenderWindow(), Platform_SwapBuffers(), Renderer_SwapBuffers()\n//   Those functions pointers exists only for the benefit of RenderPlatformWindowsDefault().\n// - If you have very specific rendering needs (e.g. flipping multiple swap-chain simultaneously, unusual sync/threading issues, etc.),\n//   you may be tempted to ignore RenderPlatformWindowsDefault() and write customized code to perform your renderingg.\n//   You may decide to setup the platform_io's *RenderWindow and *SwapBuffers pointers and call your functions through those pointers,\n//   or you may decide to never setup those pointers and call your code directly. They are a convenience, not an obligatory interface.\n//-----------------------------------------------------------------------------\n\n// Access via ImGui::GetPlatformIO()\nstruct ImGuiPlatformIO\n{\n    IMGUI_API ImGuiPlatformIO();\n\n    //------------------------------------------------------------------\n    // Input - Interface with OS and Platform backend (most common stuff)\n    //------------------------------------------------------------------\n\n    // Optional: Access OS clipboard\n    // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures)\n    const char* (*Platform_GetClipboardTextFn)(ImGuiContext* ctx);                      // Should return NULL on failure (e.g. clipboard data is not text).\n    void        (*Platform_SetClipboardTextFn)(ImGuiContext* ctx, const char* text);\n    void*       Platform_ClipboardUserData;\n\n    // Optional: Open link/folder/file in OS Shell\n    // (default to use ShellExecuteW() on Windows, system() on Linux/Mac. expected to return false on failure, but some platforms may always return true)\n    bool        (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path);\n    void*       Platform_OpenInShellUserData;\n\n    // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows)\n    // (default to use native imm32 api on Windows)\n    void        (*Platform_SetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);\n    void*       Platform_ImeUserData;\n    //void      (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); // [Renamed to platform_io.PlatformSetImeDataFn in 1.91.1]\n\n    // Optional: Platform locale\n    // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point\n    ImWchar     Platform_LocaleDecimalPoint;     // '.'\n\n    //------------------------------------------------------------------\n    // Input - Interface with Renderer Backend\n    //------------------------------------------------------------------\n\n    // Optional: Maximum texture size supported by renderer (used to adjust how we size textures). 0 if not known.\n    int         Renderer_TextureMaxWidth;\n    int         Renderer_TextureMaxHeight;\n\n    // Written by some backends during ImGui_ImplXXXX_RenderDrawData() call to point backend_specific ImGui_ImplXXXX_RenderState* structure.\n    void*       Renderer_RenderState;\n\n    //------------------------------------------------------------------\n    // Input - Interface with Platform & Renderer backends for Multi-Viewport support\n    //------------------------------------------------------------------\n\n    // For reference, the second column shows which function are generally calling the Platform Functions:\n    //   N = ImGui::NewFrame()                        ~ beginning of the dear imgui frame: read info from platform/OS windows (latest size/position)\n    //   F = ImGui::Begin(), ImGui::EndFrame()        ~ during the dear imgui frame\n    //   U = ImGui::UpdatePlatformWindows()           ~ after the dear imgui frame: create and update all platform/OS windows\n    //   R = ImGui::RenderPlatformWindowsDefault()    ~ render\n    //   D = ImGui::DestroyPlatformWindows()          ~ shutdown\n    // The general idea is that NewFrame() we will read the current Platform/OS state, and UpdatePlatformWindows() will write to it.\n\n    // The handlers are designed so we can mix and match two imgui_impl_xxxx files, one Platform backend and one Renderer backend.\n    // Custom engine backends will often provide both Platform and Renderer interfaces together and so may not need to use all functions.\n    // Platform functions are typically called _before_ their Renderer counterpart, apart from Destroy which are called the other way.\n\n    // Platform Backend functions (e.g. Win32, GLFW, SDL) ------------------- Called by -----\n    void    (*Platform_CreateWindow)(ImGuiViewport* vp);                    // . . U . .  // Create a new platform window for the given viewport\n    void    (*Platform_DestroyWindow)(ImGuiViewport* vp);                   // N . U . D  //\n    void    (*Platform_ShowWindow)(ImGuiViewport* vp);                      // . . U . .  // Newly created windows are initially hidden so SetWindowPos/Size/Title can be called on them before showing the window\n    void    (*Platform_SetWindowPos)(ImGuiViewport* vp, ImVec2 pos);        // . . U . .  // Set platform window position (given the upper-left corner of client area)\n    ImVec2  (*Platform_GetWindowPos)(ImGuiViewport* vp);                    // N . . . .  //\n    void    (*Platform_SetWindowSize)(ImGuiViewport* vp, ImVec2 size);      // . . U . .  // Set platform window client area size (ignoring OS decorations such as OS title bar etc.)\n    ImVec2  (*Platform_GetWindowSize)(ImGuiViewport* vp);                   // N . . . .  // Get platform window client area size\n    ImVec2  (*Platform_GetWindowFramebufferScale)(ImGuiViewport* vp);       // N . . . .  // Return viewport density. Always 1,1 on Windows, often 2,2 on Retina display on macOS/iOS. MUST BE INTEGER VALUES.\n    void    (*Platform_SetWindowFocus)(ImGuiViewport* vp);                  // N . . . .  // Move window to front and set input focus\n    bool    (*Platform_GetWindowFocus)(ImGuiViewport* vp);                  // . . U . .  //\n    bool    (*Platform_GetWindowMinimized)(ImGuiViewport* vp);              // N . . . .  // Get platform window minimized state. When minimized, we generally won't attempt to get/set size and contents will be culled more easily\n    void    (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* str); // . . U . .  // Set platform window title (given an UTF-8 string)\n    void    (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha);     // . . U . .  // (Optional) Setup global transparency (not per-pixel transparency)\n    void    (*Platform_UpdateWindow)(ImGuiViewport* vp);                    // . . U . .  // (Optional) Called by UpdatePlatformWindows(). Optional hook to allow the platform backend from doing general book-keeping every frame.\n    void    (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg);  // . . . R .  // (Optional) Main rendering (platform side! This is often unused, or just setting a \"current\" context for OpenGL bindings). 'render_arg' is the value passed to RenderPlatformWindowsDefault().\n    void    (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg);   // . . . R .  // (Optional) Call Present/SwapBuffers (platform side! This is often unused!). 'render_arg' is the value passed to RenderPlatformWindowsDefault().\n    float   (*Platform_GetWindowDpiScale)(ImGuiViewport* vp);               // N . . . .  // (Optional) [BETA] FIXME-DPI: DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI.\n    void    (*Platform_OnChangedViewport)(ImGuiViewport* vp);               // . F . . .  // (Optional) [BETA] FIXME-DPI: DPI handling: Called during Begin() every time the viewport we are outputting into changes, so backend has a chance to swap fonts to adjust style.\n    ImVec4  (*Platform_GetWindowWorkAreaInsets)(ImGuiViewport* vp);         // N . . . .  // (Optional) [BETA] Get initial work area inset for the viewport (won't be covered by main menu bar, dockspace over viewport etc.). Default to (0,0),(0,0). 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land.\n    int     (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For a Vulkan Renderer to call into Platform code (since the surface creation needs to tie them both).\n\n    // Renderer Backend functions (e.g. DirectX, OpenGL, Vulkan) ------------ Called by -----\n    void    (*Renderer_CreateWindow)(ImGuiViewport* vp);                    // . . U . .  // Create swap chain, frame buffers etc. (called after Platform_CreateWindow)\n    void    (*Renderer_DestroyWindow)(ImGuiViewport* vp);                   // N . U . D  // Destroy swap chain, frame buffers etc. (called before Platform_DestroyWindow)\n    void    (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size);      // . . U . .  // Resize swap chain, frame buffers etc. (called after Platform_SetWindowSize)\n    void    (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg);  // . . . R .  // (Optional) Clear framebuffer, setup render target, then render the viewport->DrawData. 'render_arg' is the value passed to RenderPlatformWindowsDefault().\n    void    (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg);   // . . . R .  // (Optional) Call Present/SwapBuffers. 'render_arg' is the value passed to RenderPlatformWindowsDefault().\n\n    // (Optional) Monitor list\n    // - Updated by: app/backend. Update every frame to dynamically support changing monitor or DPI configuration.\n    // - Used by: dear imgui to query DPI info, clamp popups/tooltips within same monitor and not have them straddle monitors.\n    ImVector<ImGuiPlatformMonitor>  Monitors;\n\n    //------------------------------------------------------------------\n    // Output\n    //------------------------------------------------------------------\n\n    // Textures list (the list is updated by calling ImGui::EndFrame or ImGui::Render)\n    // The ImGui_ImplXXXX_RenderDrawData() function of each backend generally access this via ImDrawData::Textures which points to this. The array is available here mostly because backends will want to destroy textures on shutdown.\n    ImVector<ImTextureData*>        Textures;           // List of textures used by Dear ImGui (most often 1) + contents of external texture list is automatically appended into this.\n\n    // Viewports list (the list is updated by calling ImGui::EndFrame or ImGui::Render)\n    // (in the future we will attempt to organize this feature to remove the need for a \"main viewport\")\n    ImVector<ImGuiViewport*>        Viewports;          // Main viewports, followed by all secondary viewports.\n\n    //------------------------------------------------------------------\n    // Functions\n    //------------------------------------------------------------------\n\n    IMGUI_API void ClearPlatformHandlers();    // Clear all Platform_XXX fields. Typically called on Platform Backend shutdown.\n    IMGUI_API void ClearRendererHandlers();    // Clear all Renderer_XXX fields. Typically called on Renderer Backend shutdown.\n};\n\n// (Optional) This is required when enabling multi-viewport. Represent the bounds of each connected monitor/display and their DPI.\n// We use this information for multiple DPI support + clamping the position of popups and tooltips so they don't straddle multiple monitors.\nstruct ImGuiPlatformMonitor\n{\n    ImVec2  MainPos, MainSize;      // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right)\n    ImVec2  WorkPos, WorkSize;      // Coordinates without task bars / side bars / menu bars. Used to avoid positioning popups/tooltips inside this region. If you don't have this info, please copy the value for MainPos/MainSize.\n    float   DpiScale;               // 1.0f = 96 DPI\n    void*   PlatformHandle;         // Backend dependant data (e.g. HMONITOR, GLFWmonitor*, SDL Display Index, NSScreen*)\n    ImGuiPlatformMonitor()          { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0, 0); DpiScale = 1.0f; PlatformHandle = NULL; }\n};\n\n// (Optional) Support for IME (Input Method Editor) via the platform_io.Platform_SetImeDataFn() function. Handler is called during EndFrame().\nstruct ImGuiPlatformImeData\n{\n    bool    WantVisible;            // A widget wants the IME to be visible.\n    bool    WantTextInput;          // A widget wants text input, not necessarily IME to be visible. This is automatically set to the upcoming value of io.WantTextInput.\n    ImVec2  InputPos;               // Position of input cursor (for IME).\n    float   InputLineHeight;        // Line height (for IME).\n    ImGuiID ViewportId;             // ID of platform window/viewport.\n\n    ImGuiPlatformImeData()          { memset((void*)this, 0, sizeof(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Obsolete functions and types\n// (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details)\n// Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead.\n//-----------------------------------------------------------------------------\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\nnamespace ImGui\n{\n    // OBSOLETED in 1.92.0 (from June 2025)\n    inline void         PushFont(ImFont* font)                                  { PushFont(font, font ? font->LegacySize : 0.0f); }\n    IMGUI_API void      SetWindowFontScale(float scale);                        // Set font scale factor for current window. Prefer using PushFont(NULL, style.FontSizeBase * factor) or use style.FontScaleMain to scale all windows.\n    // OBSOLETED in 1.91.9 (from February 2025)\n    IMGUI_API void      Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col); // <-- 'border_col' was removed in favor of ImGuiCol_ImageBorder. If you use 'tint_col', use ImageWithBg() instead.\n    // OBSOLETED in 1.91.0 (from July 2024)\n    inline void         PushButtonRepeat(bool repeat)                           { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); }\n    inline void         PopButtonRepeat()                                       { PopItemFlag(); }\n    inline void         PushTabStop(bool tab_stop)                              { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); }\n    inline void         PopTabStop()                                            { PopItemFlag(); }\n    // You do not need those functions! See #7838 on GitHub for more info.\n    IMGUI_API ImVec2    GetContentRegionMax();                                  // Content boundaries max (e.g. window boundaries including scrolling, or current column boundaries). You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()!\n    IMGUI_API ImVec2    GetWindowContentRegionMin();                            // Content boundaries min for the window (roughly (0,0)-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()!\n    IMGUI_API ImVec2    GetWindowContentRegionMax();                            // Content boundaries max for the window (roughly (0,0)+Size-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()!\n\n    // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE)\n    // OBSOLETED in 1.90.0 (from September 2023)\n    //IMGUI_API bool      Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1); // Getter signature changed. See 2023/09/15 and 2026/02/27 commits.\n    //IMGUI_API bool      ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1);         // Getter signature changed. See 2023/09/15 and 2026/02/27 commits.\n    //inline bool         BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders\n    //inline bool         BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags)         { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags);     } // Unnecessary as true == ImGuiChildFlags_Borders\n    //inline bool         BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, flags); }\n    //inline void         EndChildFrame()                                                             { EndChild(); }\n    //inline void         ShowStackToolWindow(bool* p_open = NULL)                                    { ShowIDStackToolWindow(p_open); }\n    // OBSOLETED in 1.89.7 (from June 2023)\n    //IMGUI_API void      SetItemAllowOverlap();                                                      // Use SetNextItemAllowOverlap() _before_ item.\n    //-- OBSOLETED in 1.89.4 (from March 2023)\n    //static inline void  PushAllowKeyboardFocus(bool tab_stop)                                       { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); }\n    //static inline void  PopAllowKeyboardFocus()                                                     { PopItemFlag(); }\n    //-- OBSOLETED in 1.89 (from August 2022)\n    //IMGUI_API bool      ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // --> Use new ImageButton() signature (explicit item id, regular FramePadding). Refer to code in 1.91 if you want to grab a copy of this version.\n    //-- OBSOLETED in 1.88 (from May 2022)\n    //static inline void  CaptureKeyboardFromApp(bool want_capture_keyboard = true)                   { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value.\n    //static inline void  CaptureMouseFromApp(bool want_capture_mouse = true)                         { SetNextFrameWantCaptureMouse(want_capture_mouse); }       // Renamed as name was misleading + removed default value.\n    //-- OBSOLETED in 1.87 (from February 2022, more formally obsoleted April 2024)\n    //IMGUI_API ImGuiKey  GetKeyIndex(ImGuiKey key);                                                  { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); const ImGuiKeyData* key_data = GetKeyData(key); return (ImGuiKey)(key_data - g.IO.KeysData); } // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value!\n    //static inline ImGuiKey GetKeyIndex(ImGuiKey key)                                                { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; }\n    //-- OBSOLETED in 1.86 (from November 2021)\n    //IMGUI_API void      CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Code removed, see 1.90 for last version of the code. Calculate range of visible items for large list of evenly sized items. Prefer using ImGuiListClipper.\n    //-- OBSOLETED in 1.85 (from August 2021)\n    //static inline float GetWindowContentRegionWidth()                                               { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; }\n    //-- OBSOLETED in 1.81 (from February 2021)\n    //static inline bool  ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0))         { return BeginListBox(label, size); }\n    //static inline bool  ListBoxHeader(const char* label, int items_count, int height_in_items = -1) { float height = GetTextLineHeightWithSpacing() * ((height_in_items < 0 ? ImMin(items_count, 7) : height_in_items) + 0.25f) + GetStyle().FramePadding.y * 2.0f; return BeginListBox(label, ImVec2(0.0f, height)); } // Helper to calculate size from items_count and height_in_items\n    //static inline void  ListBoxFooter()                                                             { EndListBox(); }\n    //-- OBSOLETED in 1.79 (from August 2020)\n    //static inline void  OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1)    { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry!\n    //-- OBSOLETED in 1.78 (from June 2020): Old drag/sliders functions that took a 'float power > 1.0f' argument instead of ImGuiSliderFlags_Logarithmic. See github.com/ocornut/imgui/issues/3361 for details.\n    //IMGUI_API bool      DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f)                                                            // OBSOLETED in 1.78 (from June 2020)\n    //IMGUI_API bool      DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f);                                          // OBSOLETED in 1.78 (from June 2020)\n    //IMGUI_API bool      SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power = 1.0f);                                                                        // OBSOLETED in 1.78 (from June 2020)\n    //IMGUI_API bool      SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format, float power = 1.0f);                                                       // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power = 1.0f)    { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); }     // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power = 1.0f)                 { return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); }            // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power = 1.0f)              { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); }        // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power = 1.0f)              { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); }        // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power = 1.0f)              { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); }        // OBSOLETED in 1.78 (from June 2020)\n    //-- OBSOLETED in 1.77 and before\n    //static inline bool  BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } // OBSOLETED in 1.77 (from June 2020)\n    //static inline void  TreeAdvanceToLabelPos()               { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); }   // OBSOLETED in 1.72 (from July 2019)\n    //static inline void  SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); }                       // OBSOLETED in 1.71 (from June 2019)\n    //static inline float GetContentRegionAvailWidth()          { return GetContentRegionAvail().x; }                               // OBSOLETED in 1.70 (from May 2019)\n    //static inline ImDrawList* GetOverlayDrawList()            { return GetForegroundDrawList(); }                                 // OBSOLETED in 1.69 (from Mar 2019)\n    //static inline void  SetScrollHere(float ratio = 0.5f)     { SetScrollHereY(ratio); }                                          // OBSOLETED in 1.66 (from Nov 2018)\n    //static inline bool  IsItemDeactivatedAfterChange()        { return IsItemDeactivatedAfterEdit(); }                            // OBSOLETED in 1.63 (from Aug 2018)\n    //-- OBSOLETED in 1.60 and before\n    //static inline bool  IsAnyWindowFocused()                  { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); }            // OBSOLETED in 1.60 (from Apr 2018)\n    //static inline bool  IsAnyWindowHovered()                  { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); }            // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018)\n    //static inline void  ShowTestWindow()                      { return ShowDemoWindow(); }                                        // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)\n    //static inline bool  IsRootWindowFocused()                 { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); }           // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)\n    //static inline bool  IsRootWindowOrAnyChildFocused()       { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); }  // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)\n    //static inline void  SetNextWindowContentWidth(float w)    { SetNextWindowContentSize(ImVec2(w, 0.0f)); }                      // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)\n    //static inline float GetItemsLineHeightWithSpacing()       { return GetFrameHeightWithSpacing(); }                             // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)\n    //IMGUI_API bool      Begin(char* name, bool* p_open, ImVec2 size_first_use, float bg_alpha = -1.0f, ImGuiWindowFlags flags=0); // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017): Equivalent of using SetNextWindowSize(size, ImGuiCond_FirstUseEver) and SetNextWindowBgAlpha().\n    //static inline bool  IsRootWindowOrAnyChildHovered()       { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); }  // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017)\n    //static inline void  AlignFirstTextHeightToWidgets()       { AlignTextToFramePadding(); }                                      // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017)\n    //static inline void  SetNextWindowPosCenter(ImGuiCond c=0) { SetNextWindowPos(GetMainViewport()->GetCenter(), c, ImVec2(0.5f,0.5f)); } // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017)\n    //static inline bool  IsItemHoveredRect()                   { return IsItemHovered(ImGuiHoveredFlags_RectOnly); }               // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017)\n    //static inline bool  IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; }                                     // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017): This was misleading and partly broken. You probably want to use the io.WantCaptureMouse flag instead.\n    //static inline bool  IsMouseHoveringAnyWindow()            { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); }            // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017)\n    //static inline bool  IsMouseHoveringWindow()               { return IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); }       // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017)\n    //-- OBSOLETED in 1.50 and before\n    //static inline bool  CollapsingHeader(char* label, const char* str_id, bool framed = true, bool default_open = false) { return CollapsingHeader(label, (default_open ? (1 << 5) : 0)); } // OBSOLETED in 1.49\n    //static inline ImFont*GetWindowFont()                      { return GetFont(); }                                               // OBSOLETED in 1.48\n    //static inline float GetWindowFontSize()                   { return GetFontSize(); }                                           // OBSOLETED in 1.48\n    //static inline void  SetScrollPosHere()                    { SetScrollHere(); }                                                // OBSOLETED in 1.42\n}\n\n//-- OBSOLETED in 1.92.0: ImFontAtlasCustomRect becomes ImTextureRect\n// - ImFontAtlasCustomRect::X,Y          --> ImTextureRect::x,y\n// - ImFontAtlasCustomRect::Width,Height --> ImTextureRect::w,h\n// - ImFontAtlasCustomRect::GlyphColored --> if you need to write to this, instead you can write to 'font->Glyphs.back()->Colored' after calling AddCustomRectFontGlyph()\n// We could make ImTextureRect an union to use old names, but 1) this would be confusing 2) the fix is easy 3) ImFontAtlasCustomRect was always a rather esoteric api.\ntypedef ImFontAtlasRect ImFontAtlasCustomRect;\n/*struct ImFontAtlasCustomRect\n{\n    unsigned short  X, Y;           // Output   // Packed position in Atlas\n    unsigned short  Width, Height;  // Input    // [Internal] Desired rectangle dimension\n    unsigned int    GlyphID:31;     // Input    // [Internal] For custom font glyphs only (ID < 0x110000)\n    unsigned int    GlyphColored:1; // Input    // [Internal] For custom font glyphs only: glyph is colored, removed tinting.\n    float           GlyphAdvanceX;  // Input    // [Internal] For custom font glyphs only: glyph xadvance\n    ImVec2          GlyphOffset;    // Input    // [Internal] For custom font glyphs only: glyph display offset\n    ImFont*         Font;           // Input    // [Internal] For custom font glyphs only: target font\n    ImFontAtlasCustomRect()         { X = Y = 0xFFFF; Width = Height = 0; GlyphID = 0; GlyphColored = 0; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0, 0); Font = NULL; }\n    bool IsPacked() const           { return X != 0xFFFF; }\n};*/\n\n//-- OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect()\n//typedef ImDrawFlags ImDrawCornerFlags;\n//enum ImDrawCornerFlags_\n//{\n//    ImDrawCornerFlags_None      = ImDrawFlags_RoundCornersNone,         // Was == 0 prior to 1.82, this is now == ImDrawFlags_RoundCornersNone which is != 0 and not implicit\n//    ImDrawCornerFlags_TopLeft   = ImDrawFlags_RoundCornersTopLeft,      // Was == 0x01 (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally).\n//    ImDrawCornerFlags_TopRight  = ImDrawFlags_RoundCornersTopRight,     // Was == 0x02 (1 << 1) prior to 1.82.\n//    ImDrawCornerFlags_BotLeft   = ImDrawFlags_RoundCornersBottomLeft,   // Was == 0x04 (1 << 2) prior to 1.82.\n//    ImDrawCornerFlags_BotRight  = ImDrawFlags_RoundCornersBottomRight,  // Was == 0x08 (1 << 3) prior to 1.82.\n//    ImDrawCornerFlags_All       = ImDrawFlags_RoundCornersAll,          // Was == 0x0F prior to 1.82\n//    ImDrawCornerFlags_Top       = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight,\n//    ImDrawCornerFlags_Bot       = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight,\n//    ImDrawCornerFlags_Left      = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft,\n//    ImDrawCornerFlags_Right     = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight,\n//};\n\n// RENAMED and MERGED both ImGuiKey_ModXXX and ImGuiModFlags_XXX into ImGuiMod_XXX (from September 2022)\n// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obsolescence schedule to reduce confusion and because they were not meant to be used in the first place.\n//typedef ImGuiKeyChord ImGuiModFlags;      // == int. We generally use ImGuiKeyChord to mean \"a ImGuiKey or-ed with any number of ImGuiMod_XXX value\", so you may store mods in there.\n//enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiModFlags_Shift = ImGuiMod_Shift, ImGuiModFlags_Alt = ImGuiMod_Alt, ImGuiModFlags_Super = ImGuiMod_Super };\n//typedef ImGuiKeyChord ImGuiKeyModFlags; // == int\n//enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = 0, ImGuiKeyModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiKeyModFlags_Shift = ImGuiMod_Shift, ImGuiKeyModFlags_Alt = ImGuiMod_Alt, ImGuiKeyModFlags_Super = ImGuiMod_Super };\n\n//#define IM_OFFSETOF(_TYPE,_MEMBER)  offsetof(_TYPE, _MEMBER)  // OBSOLETED IN 1.90 (now using C++11 standard version)\n\n#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n\n#define IM_ARRAYSIZE                IM_COUNTOF                  // RENAMED IN 1.92.6: IM_ARRAYSIZE -> IM_COUNTOF\n\n// RENAMED IMGUI_DISABLE_METRICS_WINDOW > IMGUI_DISABLE_DEBUG_TOOLS in 1.88 (from June 2022)\n#ifdef IMGUI_DISABLE_METRICS_WINDOW\n#error IMGUI_DISABLE_METRICS_WINDOW was renamed to IMGUI_DISABLE_DEBUG_TOOLS, please use new name.\n#endif\n\n//-----------------------------------------------------------------------------\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#elif defined(__GNUC__)\n#pragma GCC diagnostic pop\n#endif\n\n#ifdef _MSC_VER\n#pragma warning (pop)\n#endif\n\n// Include imgui_user.h at the end of imgui.h\n// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.\n#ifdef IMGUI_INCLUDE_IMGUI_USER_H\n#ifdef IMGUI_USER_H_FILENAME\n#include IMGUI_USER_H_FILENAME\n#else\n#include \"imgui_user.h\"\n#endif\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/include/imgui_internal.h",
    "content": "// dear imgui, v1.92.7 WIP\n// (internal structures/api)\n\n// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.\n\n/*\n\nIndex of this file:\n\n// [SECTION] Header mess\n// [SECTION] Forward declarations\n// [SECTION] Context pointer\n// [SECTION] STB libraries includes\n// [SECTION] Macros\n// [SECTION] Generic helpers\n// [SECTION] ImDrawList support\n// [SECTION] Style support\n// [SECTION] Data types support\n// [SECTION] Widgets support: flags, enums, data structures\n// [SECTION] Popup support\n// [SECTION] Inputs support\n// [SECTION] Clipper support\n// [SECTION] Navigation support\n// [SECTION] Typing-select support\n// [SECTION] Columns support\n// [SECTION] Box-select support\n// [SECTION] Multi-select support\n// [SECTION] Docking support\n// [SECTION] Viewport support\n// [SECTION] Settings support\n// [SECTION] Localization support\n// [SECTION] Error handling, State recovery support\n// [SECTION] Metrics, Debug tools\n// [SECTION] Generic context hooks\n// [SECTION] ImGuiContext (main imgui context)\n// [SECTION] ImGuiWindowTempData, ImGuiWindow\n// [SECTION] Tab bar, Tab item support\n// [SECTION] Table support\n// [SECTION] ImGui internal API\n// [SECTION] ImFontLoader\n// [SECTION] ImFontAtlas internal API\n// [SECTION] Test Engine specific hooks (imgui_test_engine)\n\n*/\n\n#pragma once\n#ifndef IMGUI_DISABLE\n\n//-----------------------------------------------------------------------------\n// [SECTION] Header mess\n//-----------------------------------------------------------------------------\n\n#ifndef IMGUI_VERSION\n#include \"imgui.h\"\n#endif\n\n#include <stdio.h>      // FILE*, sscanf\n#include <stdlib.h>     // NULL, malloc, free, qsort, atoi, atof\n#include <math.h>       // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf\n#include <limits.h>     // INT_MIN, INT_MAX\n\n// Enable SSE intrinsics if available\n#if (defined __SSE__ || defined __x86_64__ || defined _M_X64 || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))) && !defined(IMGUI_DISABLE_SSE) && !defined(_M_ARM64) && !defined(_M_ARM64EC)\n#define IMGUI_ENABLE_SSE\n#include <immintrin.h>\n#if (defined __AVX__ || defined __SSE4_2__)\n#define IMGUI_ENABLE_SSE4_2\n#include <nmmintrin.h>\n#endif\n#endif\n// Emscripten has partial SSE 4.2 support where _mm_crc32_u32 is not available. See https://emscripten.org/docs/porting/simd.html#id11 and #8213\n#if defined(IMGUI_ENABLE_SSE4_2) && !defined(IMGUI_USE_LEGACY_CRC32_ADLER) && !defined(__EMSCRIPTEN__)\n#define IMGUI_ENABLE_SSE4_2_CRC\n#endif\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (push)\n#pragma warning (disable: 4251)     // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport)\n#pragma warning (disable: 26495)    // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).\n#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).\n#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later\n#pragma warning (disable: 5054)     // operator '|': deprecated between enumerations of different types\n#endif\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#pragma clang diagnostic push\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wfloat-equal\"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloor()\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"               // warning: implicit conversion from 'float' to 'double' when passing argument to function\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#pragma clang diagnostic ignored \"-Wmissing-noreturn\"               // warning: function 'xxx' could be declared with attribute 'noreturn'\n#pragma clang diagnostic ignored \"-Wdeprecated-enum-enum-conversion\"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated\n#pragma clang diagnostic ignored \"-Wunsafe-buffer-usage\"            // warning: 'xxx' is an unsafe pointer used for buffer access\n#pragma clang diagnostic ignored \"-Wnontrivial-memaccess\"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type\n#elif defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpragmas\"                          // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"                      // warning: comparing floating-point with '==' or '!=' is unsafe\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#pragma GCC diagnostic ignored \"-Wdeprecated-enum-enum-conversion\"  // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"                  // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result\n#endif\n\n// In 1.89.4, we moved the implementation of \"courtesy maths operators\" from imgui_internal.h in imgui.h\n// As they are frequently requested, we do not want to encourage to many people using imgui_internal.h\n#if defined(IMGUI_DEFINE_MATH_OPERATORS) && !defined(IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED)\n#error Please '#define IMGUI_DEFINE_MATH_OPERATORS' _BEFORE_ including imgui.h!\n#endif\n\n// Legacy defines\n#ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS            // Renamed in 1.74\n#error Use IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n#endif\n#ifdef IMGUI_DISABLE_MATH_FUNCTIONS                     // Renamed in 1.74\n#error Use IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS\n#endif\n\n// Enable stb_truetype by default unless FreeType is enabled.\n// You can compile with both by defining both IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE together.\n#ifndef IMGUI_ENABLE_FREETYPE\n#define IMGUI_ENABLE_STB_TRUETYPE\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Forward declarations\n//-----------------------------------------------------------------------------\n\n// Utilities\n// (other types which are not forwarded declared are: ImBitArray<>, ImSpan<>, ImSpanAllocator<>, ImStableVector<>, ImPool<>, ImChunkStream<>)\nstruct ImBitVector;                 // Store 1-bit per value\nstruct ImRect;                      // An axis-aligned rectangle (2 points)\nstruct ImGuiTextIndex;              // Maintain a line index for a text buffer.\n\n// ImDrawList/ImFontAtlas\nstruct ImDrawDataBuilder;           // Helper to build a ImDrawData instance\nstruct ImDrawListSharedData;        // Data shared between all ImDrawList instances\nstruct ImFontAtlasBuilder;          // Internal storage for incrementally packing and building a ImFontAtlas\nstruct ImFontAtlasPostProcessData;  // Data available to potential texture post-processing functions\nstruct ImFontAtlasRectEntry;        // Packed rectangle lookup entry\n\n// ImGui\nstruct ImGuiBoxSelectState;         // Box-selection state (currently used by multi-selection, could potentially be used by others)\nstruct ImGuiColorMod;               // Stacked color modifier, backup of modified data so we can restore it\nstruct ImGuiContext;                // Main Dear ImGui context\nstruct ImGuiContextHook;            // Hook for extensions like ImGuiTestEngine\nstruct ImGuiDataTypeInfo;           // Type information associated to a ImGuiDataType enum\nstruct ImGuiDeactivatedItemData;    // Data for IsItemDeactivated()/IsItemDeactivatedAfterEdit() function.\nstruct ImGuiDockContext;            // Docking system context\nstruct ImGuiDockRequest;            // Docking system dock/undock queued request\nstruct ImGuiDockNode;               // Docking system node (hold a list of Windows OR two child dock nodes)\nstruct ImGuiDockNodeSettings;       // Storage for a dock node in .ini file (we preserve those even if the associated dock node isn't active during the session)\nstruct ImGuiErrorRecoveryState;     // Storage of stack sizes for error handling and recovery\nstruct ImGuiGroupData;              // Stacked storage data for BeginGroup()/EndGroup()\nstruct ImGuiInputTextState;         // Internal state of the currently focused/edited text input box\nstruct ImGuiInputTextDeactivateData;// Short term storage to backup text of a deactivating InputText() while another is stealing active id\nstruct ImGuiLastItemData;           // Status storage for last submitted items\nstruct ImGuiLocEntry;               // A localization entry.\nstruct ImGuiMenuColumns;            // Simple column measurement, currently used for MenuItem() only\nstruct ImGuiMultiSelectState;       // Multi-selection persistent state (for focused selection).\nstruct ImGuiMultiSelectTempData;    // Multi-selection temporary state (while traversing).\nstruct ImGuiNavItemData;            // Result of a keyboard/gamepad directional navigation move query result\nstruct ImGuiMetricsConfig;          // Storage for ShowMetricsWindow() and DebugNodeXXX() functions\nstruct ImGuiNextWindowData;         // Storage for SetNextWindow** functions\nstruct ImGuiNextItemData;           // Storage for SetNextItem** functions\nstruct ImGuiOldColumnData;          // Storage data for a single column for legacy Columns() api\nstruct ImGuiOldColumns;             // Storage data for a columns set for legacy Columns() api\nstruct ImGuiPopupData;              // Storage for current popup stack\nstruct ImGuiSettingsHandler;        // Storage for one type registered in the .ini file\nstruct ImGuiStyleMod;               // Stacked style modifier, backup of modified data so we can restore it\nstruct ImGuiStyleShadowTexConfig;   // Shadow Texture baking config\nstruct ImGuiStyleVarInfo;           // Style variable information (e.g. to access style variables from an enum)\nstruct ImGuiTabBar;                 // Storage for a tab bar\nstruct ImGuiTabItem;                // Storage for a tab item (within a tab bar)\nstruct ImGuiTable;                  // Storage for a table\nstruct ImGuiTableHeaderData;        // Storage for TableAngledHeadersRow()\nstruct ImGuiTableColumn;            // Storage for one column of a table\nstruct ImGuiTableInstanceData;      // Storage for one instance of a same table\nstruct ImGuiTableTempData;          // Temporary storage for one table (one per table in the stack), shared between tables.\nstruct ImGuiTableSettings;          // Storage for a table .ini settings\nstruct ImGuiTableColumnsSettings;   // Storage for a column .ini settings\nstruct ImGuiTreeNodeStackData;      // Temporary storage for TreeNode().\nstruct ImGuiTypingSelectState;      // Storage for GetTypingSelectRequest()\nstruct ImGuiTypingSelectRequest;    // Storage for GetTypingSelectRequest() (aimed to be public)\nstruct ImGuiWindow;                 // Storage for one window\nstruct ImGuiWindowDockStyle;        // Storage for window-style data which needs to be stored for docking purpose\nstruct ImGuiWindowTempData;         // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window)\nstruct ImGuiWindowSettings;         // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session)\n\n// Enumerations\n// Use your programming IDE \"Go to definition\" facility on the names of the center columns to find the actual flags/enum lists.\nenum ImGuiLocKey : int;                 // -> enum ImGuiLocKey              // Enum: a localization entry for translation.\ntypedef int ImGuiDataAuthority;         // -> enum ImGuiDataAuthority_      // Enum: for storing the source authority (dock node vs window) of a field\ntypedef int ImGuiLayoutType;            // -> enum ImGuiLayoutType_         // Enum: Horizontal or vertical\n\n// Flags\ntypedef int ImDrawTextFlags;            // -> enum ImDrawTextFlags_         // Flags: for ImTextCalcWordWrapPositionEx()\ntypedef int ImGuiActivateFlags;         // -> enum ImGuiActivateFlags_      // Flags: for navigation/focus function (will be for ActivateItem() later)\ntypedef int ImGuiDebugLogFlags;         // -> enum ImGuiDebugLogFlags_      // Flags: for ShowDebugLogWindow(), g.DebugLogFlags\ntypedef int ImGuiFocusRequestFlags;     // -> enum ImGuiFocusRequestFlags_  // Flags: for FocusWindow()\ntypedef int ImGuiItemStatusFlags;       // -> enum ImGuiItemStatusFlags_    // Flags: for g.LastItemData.StatusFlags\ntypedef int ImGuiOldColumnFlags;        // -> enum ImGuiOldColumnFlags_     // Flags: for BeginColumns()\ntypedef int ImGuiLogFlags;              // -> enum ImGuiLogFlags_           // Flags: for LogBegin() text capturing function\ntypedef int ImGuiNavRenderCursorFlags;  // -> enum ImGuiNavRenderCursorFlags_//Flags: for RenderNavCursor()\ntypedef int ImGuiNavMoveFlags;          // -> enum ImGuiNavMoveFlags_       // Flags: for navigation requests\ntypedef int ImGuiNextItemDataFlags;     // -> enum ImGuiNextItemDataFlags_  // Flags: for SetNextItemXXX() functions\ntypedef int ImGuiNextWindowDataFlags;   // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions\ntypedef int ImGuiScrollFlags;           // -> enum ImGuiScrollFlags_        // Flags: for ScrollToItem() and navigation requests\ntypedef int ImGuiSeparatorFlags;        // -> enum ImGuiSeparatorFlags_     // Flags: for SeparatorEx()\ntypedef int ImGuiTextFlags;             // -> enum ImGuiTextFlags_          // Flags: for TextEx()\ntypedef int ImGuiTooltipFlags;          // -> enum ImGuiTooltipFlags_       // Flags: for BeginTooltipEx()\ntypedef int ImGuiTypingSelectFlags;     // -> enum ImGuiTypingSelectFlags_  // Flags: for GetTypingSelectRequest()\ntypedef int ImGuiWindowBgClickFlags;    // -> enum ImGuiWindowBgClickFlags_ // Flags: for overriding behavior of clicking on window background/void.\ntypedef int ImGuiWindowRefreshFlags;    // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy()\n\n// Table column indexing\ntypedef ImS16 ImGuiTableColumnIdx;\ntypedef ImU16 ImGuiTableDrawChannelIdx;\n\n//-----------------------------------------------------------------------------\n// [SECTION] Context pointer\n// See implementation of this variable in imgui.cpp for comments and details.\n//-----------------------------------------------------------------------------\n\n#ifndef GImGui\nextern IMGUI_API ImGuiContext* GImGui;  // Current implicit context pointer\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Macros\n//-----------------------------------------------------------------------------\n\n// Internal Drag and Drop payload types. String starting with '_' are reserved for Dear ImGui.\n#define IMGUI_PAYLOAD_TYPE_WINDOW       \"_IMWINDOW\"     // Payload == ImGuiWindow*\n\n// Debug Printing Into TTY\n// (since IMGUI_VERSION_NUM >= 18729: IMGUI_DEBUG_LOG was reworked into IMGUI_DEBUG_PRINTF (and removed framecount from it). If you were using a #define IMGUI_DEBUG_LOG please rename)\n#ifndef IMGUI_DEBUG_PRINTF\n#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n#define IMGUI_DEBUG_PRINTF(_FMT,...)    printf(_FMT, __VA_ARGS__)\n#else\n#define IMGUI_DEBUG_PRINTF(_FMT,...)    ((void)0)\n#endif\n#endif\n\n// Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam.\n#define IMGUI_DEBUG_LOG_ERROR(...)      do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventError)       IMGUI_DEBUG_LOG(__VA_ARGS__); else g.DebugLogSkippedErrors++; } while (0)\n#define IMGUI_DEBUG_LOG_ACTIVEID(...)   do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId)    IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_FOCUS(...)      do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus)       IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_POPUP(...)      do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup)       IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_NAV(...)        do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav)         IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_SELECTION(...)  do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)   IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_CLIPPER(...)    do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper)     IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_IO(...)         do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO)          IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_FONT(...)       do { ImGuiContext* g2 = GImGui; if (g2 && g2->DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Called from ImFontAtlas function which may operate without a context.\n#define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_DOCKING(...)    do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventDocking)     IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_VIEWPORT(...)   do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventViewport)    IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n\n// Static Asserts\n#define IM_STATIC_ASSERT(_COND)         static_assert(_COND, \"\")\n\n// \"Paranoid\" Debug Asserts are meant to only be enabled during specific debugging/work, otherwise would slow down the code too much.\n// We currently don't have many of those so the effect is currently negligible, but onward intent to add more aggressive ones in the code.\n//#define IMGUI_DEBUG_PARANOID\n#ifdef IMGUI_DEBUG_PARANOID\n#define IM_ASSERT_PARANOID(_EXPR)       IM_ASSERT(_EXPR)\n#else\n#define IM_ASSERT_PARANOID(_EXPR)\n#endif\n\n// Misc Macros\n#define IM_PI                           3.14159265358979323846f\n#ifdef _WIN32\n#define IM_NEWLINE                      \"\\r\\n\"   // Play it nice with Windows users (Update: since 2018-05, Notepad finally appears to support Unix-style carriage returns!)\n#else\n#define IM_NEWLINE                      \"\\n\"\n#endif\n#ifndef IM_TABSIZE                      // Until we move this to runtime and/or add proper tab support, at least allow users to compile-time override\n#define IM_TABSIZE                      (4)\n#endif\n#define IM_MEMALIGN(_OFF,_ALIGN)        (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1))           // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8\n#define IM_F32_TO_INT8_UNBOUND(_VAL)    ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f)))   // Unsaturated, for display purpose\n#define IM_F32_TO_INT8_SAT(_VAL)        ((int)(ImSaturate(_VAL) * 255.0f + 0.5f))               // Saturated, always output 0..255\n#define IM_TRUNC(_VAL)                  ((float)(int)(_VAL))                                    // Positive values only! ImTrunc() is not inlined in MSVC debug builds\n#define IM_ROUND(_VAL)                  ((float)(int)((_VAL) + 0.5f))                           // Positive values only! \n//#define IM_FLOOR IM_TRUNC             // [OBSOLETE] Renamed in 1.90.0 (Sept 2023)\n\n// Hint for branch prediction\n#if (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L))\n#define IM_LIKELY   [[likely]]\n#define IM_UNLIKELY [[unlikely]]\n#else\n#define IM_LIKELY\n#define IM_UNLIKELY\n#endif\n\n// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall\n#ifdef _MSC_VER\n#define IMGUI_CDECL __cdecl\n#else\n#define IMGUI_CDECL\n#endif\n\n// Warnings\n#if defined(_MSC_VER) && !defined(__clang__)\n#define IM_MSVC_WARNING_SUPPRESS(XXXX)  __pragma(warning(suppress: XXXX))\n#else\n#define IM_MSVC_WARNING_SUPPRESS(XXXX)\n#endif\n\n// Debug Tools\n// Use 'Metrics/Debugger->Tools->Item Picker' to break into the call-stack of a specific item.\n// This will call IM_DEBUG_BREAK() which you may redefine yourself. See https://github.com/scottt/debugbreak for more reference.\n#ifndef IM_DEBUG_BREAK\n#if defined (_MSC_VER)\n#define IM_DEBUG_BREAK()    __debugbreak()\n#elif defined(__clang__)\n#define IM_DEBUG_BREAK()    __builtin_debugtrap()\n#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))\n#define IM_DEBUG_BREAK()    __asm__ volatile(\"int3;nop\")\n#elif defined(__GNUC__) && defined(__thumb__)\n#define IM_DEBUG_BREAK()    __asm__ volatile(\".inst 0xde01\")\n#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__)\n#define IM_DEBUG_BREAK()    __asm__ volatile(\".inst 0xe7f001f0\")\n#else\n#define IM_DEBUG_BREAK()    IM_ASSERT(0)    // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger!\n#endif\n#endif // #ifndef IM_DEBUG_BREAK\n\n// Format specifiers, printing 64-bit hasn't been decently standardized...\n// In a real application you should be using PRId64 and PRIu64 from <inttypes.h> (non-windows) and on Windows define them yourself.\n#if defined(_MSC_VER) && !defined(__clang__)\n#define IM_PRId64   \"I64d\"\n#define IM_PRIu64   \"I64u\"\n#define IM_PRIX64   \"I64X\"\n#else\n#define IM_PRId64   \"lld\"\n#define IM_PRIu64   \"llu\"\n#define IM_PRIX64   \"llX\"\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Generic helpers\n// Note that the ImXXX helpers functions are lower-level than ImGui functions.\n// ImGui functions or the ImGui context are never called/used from other ImXXX functions.\n//-----------------------------------------------------------------------------\n// - Helpers: Hashing\n// - Helpers: Sorting\n// - Helpers: Bit manipulation\n// - Helpers: String\n// - Helpers: Formatting\n// - Helpers: UTF-8 <> wchar conversions\n// - Helpers: ImVec2/ImVec4 operators\n// - Helpers: Maths\n// - Helpers: Geometry\n// - Helper: ImVec1\n// - Helper: ImVec2ih\n// - Helper: ImRect\n// - Helper: ImBitArray\n// - Helper: ImBitVector\n// - Helper: ImSpan<>, ImSpanAllocator<>\n// - Helper: ImStableVector<>\n// - Helper: ImPool<>\n// - Helper: ImChunkStream<>\n// - Helper: ImGuiTextIndex\n// - Helper: ImGuiStorage\n//-----------------------------------------------------------------------------\n\n// Helpers: Hashing\nIMGUI_API ImGuiID       ImHashData(const void* data, size_t data_size, ImGuiID seed = 0);\nIMGUI_API ImGuiID       ImHashStr(const char* data, size_t data_size = 0, ImGuiID seed = 0);\nIMGUI_API const char*   ImHashSkipUncontributingPrefix(const char* label);\n\n// Helpers: Sorting\n#ifndef ImQsort\ninline void             ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); }\n#endif\n\n// Helpers: Color Blending\nIMGUI_API ImU32         ImAlphaBlendColors(ImU32 col_a, ImU32 col_b);\n\n// Helpers: Bit manipulation\ninline bool             ImIsPowerOfTwo(int v)               { return v != 0 && (v & (v - 1)) == 0; }\ninline bool             ImIsPowerOfTwo(ImU64 v)             { return v != 0 && (v & (v - 1)) == 0; }\ninline int              ImUpperPowerOfTwo(int v)            { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }\ninline unsigned int     ImCountSetBits(unsigned int v)      { unsigned int count = 0; while (v > 0) { v = v & (v - 1); count++; } return count; }\n\n// Helpers: String\n#define ImStrlen strlen\n#define ImMemchr memchr\nIMGUI_API int           ImStricmp(const char* str1, const char* str2);                      // Case insensitive compare.\nIMGUI_API int           ImStrnicmp(const char* str1, const char* str2, size_t count);       // Case insensitive compare to a certain count.\nIMGUI_API void          ImStrncpy(char* dst, const char* src, size_t count);                // Copy to a certain count and always zero terminate (strncpy doesn't).\nIMGUI_API char*         ImStrdup(const char* str);                                          // Duplicate a string.\nIMGUI_API void*         ImMemdup(const void* src, size_t size);                             // Duplicate a chunk of memory.\nIMGUI_API char*         ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str);        // Copy in provided buffer, recreate buffer if needed.\nIMGUI_API const char*   ImStrchrRange(const char* str_begin, const char* str_end, char c);  // Find first occurrence of 'c' in string range.\nIMGUI_API const char*   ImStreolRange(const char* str, const char* str_end);                // End end-of-line\nIMGUI_API const char*   ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);  // Find a substring in a string range.\nIMGUI_API void          ImStrTrimBlanks(char* str);                                         // Remove leading and trailing blanks from a buffer.\nIMGUI_API const char*   ImStrSkipBlank(const char* str);                                    // Find first non-blank character.\nIMGUI_API int           ImStrlenW(const ImWchar* str);                                      // Computer string length (ImWchar string)\nIMGUI_API const char*   ImStrbol(const char* buf_mid_line, const char* buf_begin);          // Find beginning-of-line\nIM_MSVC_RUNTIME_CHECKS_OFF\ninline char             ImToUpper(char c)               { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }\ninline bool             ImCharIsBlankA(char c)          { return c == ' ' || c == '\\t'; }\ninline bool             ImCharIsBlankW(unsigned int c)  { return c == ' ' || c == '\\t' || c == 0x3000; }\ninline bool             ImCharIsXdigitA(char c)         { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); }\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n// Helpers: Formatting\nIMGUI_API int           ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3);\nIMGUI_API int           ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3);\nIMGUI_API void          ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) IM_FMTARGS(3);\nIMGUI_API void          ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args) IM_FMTLIST(3);\nIMGUI_API const char*   ImParseFormatFindStart(const char* format);\nIMGUI_API const char*   ImParseFormatFindEnd(const char* format);\nIMGUI_API const char*   ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size);\nIMGUI_API void          ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size);\nIMGUI_API const char*   ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size);\nIMGUI_API int           ImParseFormatPrecision(const char* format, int default_value);\n\n// Helpers: UTF-8 <> wchar conversions\nIMGUI_API int           ImTextCharToUtf8(char out_buf[5], unsigned int c);                                                      // return output UTF-8 bytes count\nIMGUI_API int           ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end);   // return output UTF-8 bytes count\nIMGUI_API int           ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end);               // read one character. return input UTF-8 bytes count\nIMGUI_API int           ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL);   // return input UTF-8 bytes count\nIMGUI_API int           ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end);                                 // return number of UTF-8 code-points (NOT bytes count)\nIMGUI_API int           ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end);                             // return number of bytes to express one char in UTF-8\nIMGUI_API int           ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end);                        // return number of bytes to express string in UTF-8\nIMGUI_API const char*   ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_p);                           // return previous UTF-8 code-point.\nIMGUI_API const char*   ImTextFindValidUtf8CodepointEnd(const char* in_text_start, const char* in_text_end, const char* in_p);  // return previous UTF-8 code-point if 'in_p' is not the end of a valid one.\nIMGUI_API int           ImTextCountLines(const char* in_text, const char* in_text_end);                                         // return number of lines taken by text. trailing carriage return doesn't count as an extra line.\n\n// Helpers: High-level text functions (DO NOT USE!!! THIS IS A MINIMAL SUBSET OF LARGER UPCOMING CHANGES)\nenum ImDrawTextFlags_\n{\n    ImDrawTextFlags_None                = 0,\n    ImDrawTextFlags_CpuFineClip         = 1 << 0,    // Must be == 1/true for legacy with 'bool cpu_fine_clip' arg to RenderText()\n    ImDrawTextFlags_WrapKeepBlanks      = 1 << 1,\n    ImDrawTextFlags_StopOnNewLine       = 1 << 2,\n};\nIMGUI_API ImVec2        ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags);\nIMGUI_API const char*   ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags = 0);\nIMGUI_API const char*   ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags = 0); // trim trailing space and find beginning of next line\n\n// Character classification for word-wrapping logic\nenum ImWcharClass\n{\n    ImWcharClass_Blank, ImWcharClass_Punct, ImWcharClass_Other\n};\nIMGUI_API void          ImTextInitClassifiers();\nIMGUI_API void          ImTextClassifierClear(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class);\nIMGUI_API void          ImTextClassifierSetCharClass(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, unsigned int c);\nIMGUI_API void          ImTextClassifierSetCharClassFromStr(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, const char* s);\n\n// Helpers: File System\n#ifdef IMGUI_DISABLE_FILE_FUNCTIONS\n#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\ntypedef void* ImFileHandle;\ninline ImFileHandle         ImFileOpen(const char*, const char*)                    { return NULL; }\ninline bool                 ImFileClose(ImFileHandle)                               { return false; }\ninline ImU64                ImFileGetSize(ImFileHandle)                             { return (ImU64)-1; }\ninline ImU64                ImFileRead(void*, ImU64, ImU64, ImFileHandle)           { return 0; }\ninline ImU64                ImFileWrite(const void*, ImU64, ImU64, ImFileHandle)    { return 0; }\n#endif\n#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\ntypedef FILE* ImFileHandle;\nIMGUI_API ImFileHandle      ImFileOpen(const char* filename, const char* mode);\nIMGUI_API bool              ImFileClose(ImFileHandle file);\nIMGUI_API ImU64             ImFileGetSize(ImFileHandle file);\nIMGUI_API ImU64             ImFileRead(void* data, ImU64 size, ImU64 count, ImFileHandle file);\nIMGUI_API ImU64             ImFileWrite(const void* data, ImU64 size, ImU64 count, ImFileHandle file);\n#else\n#define IMGUI_DISABLE_TTY_FUNCTIONS // Can't use stdout, fflush if we are not using default file functions\n#endif\nIMGUI_API void*             ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0);\n\n// Helpers: Maths\nIM_MSVC_RUNTIME_CHECKS_OFF\n// - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy)\n#ifndef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS\n#define ImFabs(X)           fabsf(X)\n#define ImSqrt(X)           sqrtf(X)\n#define ImFmod(X, Y)        fmodf((X), (Y))\n#define ImCos(X)            cosf(X)\n#define ImSin(X)            sinf(X)\n#define ImAcos(X)           acosf(X)\n#define ImAtan2(Y, X)       atan2f((Y), (X))\n#define ImAtof(STR)         atof(STR)\n#define ImCeil(X)           ceilf(X)\ninline float  ImPow(float x, float y)    { return powf(x, y); }          // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision\ninline double ImPow(double x, double y)  { return pow(x, y); }\ninline float  ImLog(float x)             { return logf(x); }             // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision\ninline double ImLog(double x)            { return log(x); }\ninline int    ImAbs(int x)               { return x < 0 ? -x : x; }\ninline float  ImAbs(float x)             { return fabsf(x); }\ninline double ImAbs(double x)            { return fabs(x); }\ninline float  ImSign(float x)            { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument\ninline double ImSign(double x)           { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; }\n#ifdef IMGUI_ENABLE_SSE\ninline float  ImRsqrt(float x)           { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); }\n#else\ninline float  ImRsqrt(float x)           { return 1.0f / sqrtf(x); }\n#endif\ninline double ImRsqrt(double x)          { return 1.0 / sqrt(x); }\n#endif\n// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double\n// (Exceptionally using templates here but we could also redefine them for those types)\ntemplate<typename T> T ImMin(T lhs, T rhs)                              { return lhs < rhs ? lhs : rhs; }\ntemplate<typename T> T ImMax(T lhs, T rhs)                              { return lhs >= rhs ? lhs : rhs; }\ntemplate<typename T> T ImClamp(T v, T mn, T mx)                         { return (v < mn) ? mn : (v > mx) ? mx : v; }\ntemplate<typename T> T ImLerp(T a, T b, float t)                        { return (T)(a + (b - a) * t); }\ntemplate<typename T> void ImSwap(T& a, T& b)                            { T tmp = a; a = b; b = tmp; }\ntemplate<typename T> T ImAddClampOverflow(T a, T b, T mn, T mx)         { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; }\ntemplate<typename T> T ImSubClampOverflow(T a, T b, T mn, T mx)         { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; }\n// - Misc maths helpers\ninline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs)               { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); }\ninline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs)               { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); }\ninline ImVec2 ImClamp(const ImVec2& v, const ImVec2&mn, const ImVec2&mx){ return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); }\ninline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t)         { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); }\ninline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); }\ninline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t)         { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); }\ninline float  ImSaturate(float f)                                       { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }\ninline float  ImLengthSqr(const ImVec2& lhs)                            { return (lhs.x * lhs.x) + (lhs.y * lhs.y); }\ninline float  ImLengthSqr(const ImVec4& lhs)                            { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }\ninline float  ImLength(const ImVec2& lhs, float fail_value)             { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImSqrt(d); return fail_value; }\ninline float  ImInvLength(const ImVec2& lhs, float fail_value)          { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; }\ninline float  ImTrunc(float f)                                          { return (float)(int)(f); }\ninline ImVec2 ImTrunc(const ImVec2& v)                                  { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }\ninline float  ImFloor(float f)                                          { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()\ninline ImVec2 ImFloor(const ImVec2& v)                                  { return ImVec2(ImFloor(v.x), ImFloor(v.y)); }\ninline float  ImTrunc64(float f)                                        { return (float)(ImS64)(f); }\ninline float  ImRound64(float f)                                        { return (float)(ImS64)(f + 0.5f); } // FIXME: Positive values only.\ninline int    ImModPositive(int a, int b)                               { return (a + b) % b; }\ninline float  ImDot(const ImVec2& a, const ImVec2& b)                   { return a.x * b.x + a.y * b.y; }\ninline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a)       { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }\ninline float  ImLinearSweep(float current, float target, float speed)   { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; }\ninline float  ImLinearRemapClamp(float s0, float s1, float d0, float d1, float x) { return ImSaturate((x - s0) / (s1 - s0)) * (d1 - d0) + d0; }\ninline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs)               { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }\ninline bool   ImIsFloatAboveGuaranteedIntegerPrecision(float f)         { return f <= -16777216 || f >= 16777216; }\ninline float  ImExponentialMovingAverage(float avg, float sample, int n){ avg -= avg / (float)n; avg += sample / (float)n; return avg; }\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n// Helpers: Geometry\nIMGUI_API ImVec2     ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t);\nIMGUI_API ImVec2     ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments);       // For curves with explicit number of segments\nIMGUI_API ImVec2     ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol);// For auto-tessellated curves you can use tess_tol = style.CurveTessellationTol\nIMGUI_API ImVec2     ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t);\nIMGUI_API ImVec2     ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p);\nIMGUI_API bool       ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p);\nIMGUI_API ImVec2     ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p);\nIMGUI_API void       ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w);\ninline float         ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c)          { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; }\ninline bool          ImTriangleIsClockwise(const ImVec2& a, const ImVec2& b, const ImVec2& c)   { return ((b.x - a.x) * (c.y - b.y)) - ((c.x - b.x) * (b.y - a.y)) > 0.0f; }\n\n// Helper: ImVec1 (1D vector)\n// (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches)\nIM_MSVC_RUNTIME_CHECKS_OFF\nstruct ImVec1\n{\n    float   x;\n    constexpr ImVec1()         : x(0.0f) { }\n    constexpr ImVec1(float _x) : x(_x) { }\n};\n\n// Helper: ImVec2i (2D vector, integer)\nstruct ImVec2i\n{\n    int         x, y;\n    constexpr ImVec2i()                             : x(0), y(0) {}\n    constexpr ImVec2i(int _x, int _y)               : x(_x), y(_y) {}\n};\n\n// Helper: ImVec2ih (2D vector, half-size integer, for long-term packed storage)\nstruct ImVec2ih\n{\n    short   x, y;\n    constexpr ImVec2ih()                           : x(0), y(0) {}\n    constexpr ImVec2ih(short _x, short _y)         : x(_x), y(_y) {}\n    constexpr explicit ImVec2ih(const ImVec2& rhs) : x((short)rhs.x), y((short)rhs.y) {}\n};\n\n// Helper: ImRect (2D axis aligned bounding-box)\n// NB: we can't rely on ImVec2 math operators being available here!\nstruct IMGUI_API ImRect\n{\n    ImVec2      Min;    // Upper-left\n    ImVec2      Max;    // Lower-right\n\n    constexpr ImRect()                                        : Min(0.0f, 0.0f), Max(0.0f, 0.0f)  {}\n    constexpr ImRect(const ImVec2& min, const ImVec2& max)    : Min(min), Max(max)                {}\n    constexpr ImRect(const ImVec4& v)                         : Min(v.x, v.y), Max(v.z, v.w)      {}\n    constexpr ImRect(float x1, float y1, float x2, float y2)  : Min(x1, y1), Max(x2, y2)          {}\n\n    ImVec2      GetCenter() const                   { return ImVec2((Min.x + Max.x) * 0.5f, (Min.y + Max.y) * 0.5f); }\n    ImVec2      GetSize() const                     { return ImVec2(Max.x - Min.x, Max.y - Min.y); }\n    float       GetWidth() const                    { return Max.x - Min.x; }\n    float       GetHeight() const                   { return Max.y - Min.y; }\n    float       GetArea() const                     { return (Max.x - Min.x) * (Max.y - Min.y); }\n    ImVec2      GetTL() const                       { return Min; }                   // Top-left\n    ImVec2      GetTR() const                       { return ImVec2(Max.x, Min.y); }  // Top-right\n    ImVec2      GetBL() const                       { return ImVec2(Min.x, Max.y); }  // Bottom-left\n    ImVec2      GetBR() const                       { return Max; }                   // Bottom-right\n    bool        Contains(const ImVec2& p) const     { return p.x     >= Min.x && p.y     >= Min.y && p.x     <  Max.x && p.y     <  Max.y; }\n    bool        Contains(const ImRect& r) const     { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; }\n    bool        ContainsWithPad(const ImVec2& p, const ImVec2& pad) const { return p.x >= Min.x - pad.x && p.y >= Min.y - pad.y && p.x < Max.x + pad.x && p.y < Max.y + pad.y; }\n    bool        Overlaps(const ImRect& r) const     { return r.Min.y <  Max.y && r.Max.y >  Min.y && r.Min.x <  Max.x && r.Max.x >  Min.x; }\n    void        Add(const ImVec2& p)                { if (Min.x > p.x)     Min.x = p.x;     if (Min.y > p.y)     Min.y = p.y;     if (Max.x < p.x)     Max.x = p.x;     if (Max.y < p.y)     Max.y = p.y; }\n    void        Add(const ImRect& r)                { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; }\n    void        Expand(const float amount)          { Min.x -= amount;   Min.y -= amount;   Max.x += amount;   Max.y += amount; }\n    void        Expand(const ImVec2& amount)        { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; }\n    void        Translate(const ImVec2& d)          { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; }\n    void        TranslateX(float dx)                { Min.x += dx; Max.x += dx; }\n    void        TranslateY(float dy)                { Min.y += dy; Max.y += dy; }\n    void        ClipWith(const ImRect& r)           { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); }                   // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display.\n    void        ClipWithFull(const ImRect& r)       { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped.\n    bool        IsInverted() const                  { return Min.x > Max.x || Min.y > Max.y; }\n    ImVec4      ToVec4() const                      { return ImVec4(Min.x, Min.y, Max.x, Max.y); }\n    const ImVec4& AsVec4() const                    { return *(const ImVec4*)&Min.x; }\n};\n\n// Helper: ImBitArray\n#define         IM_BITARRAY_TESTBIT(_ARRAY, _N)                 ((_ARRAY[(_N) >> 5] & ((ImU32)1 << ((_N) & 31))) != 0) // Macro version of ImBitArrayTestBit(): ensure args have side-effect or are costly!\n#define         IM_BITARRAY_CLEARBIT(_ARRAY, _N)                ((_ARRAY[(_N) >> 5] &= ~((ImU32)1 << ((_N) & 31))))    // Macro version of ImBitArrayClearBit(): ensure args have side-effect or are costly!\ninline size_t   ImBitArrayGetStorageSizeInBytes(int bitcount)   { return (size_t)((bitcount + 31) >> 5) << 2; }\ninline void     ImBitArrayClearAllBits(ImU32* arr, int bitcount){ memset(arr, 0, ImBitArrayGetStorageSizeInBytes(bitcount)); }\ninline bool     ImBitArrayTestBit(const ImU32* arr, int n)      { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; }\ninline void     ImBitArrayClearBit(ImU32* arr, int n)           { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] &= ~mask; }\ninline void     ImBitArraySetBit(ImU32* arr, int n)             { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] |= mask; }\ninline void     ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on range [n..n2)\n{\n    n2--;\n    while (n <= n2)\n    {\n        int a_mod = (n & 31);\n        int b_mod = (n2 > (n | 31) ? 31 : (n2 & 31)) + 1;\n        ImU32 mask = (ImU32)(((ImU64)1 << b_mod) - 1) & ~(ImU32)(((ImU64)1 << a_mod) - 1);\n        arr[n >> 5] |= mask;\n        n = (n + 32) & ~31;\n    }\n}\n\ntypedef ImU32* ImBitArrayPtr; // Name for use in structs\n\n// Helper: ImBitArray class (wrapper over ImBitArray functions)\n// Store 1-bit per value.\ntemplate<int BITCOUNT, int OFFSET = 0>\nstruct ImBitArray\n{\n    ImU32           Data[(BITCOUNT + 31) >> 5];\n    ImBitArray()                                { ClearAllBits(); }\n    void            ClearAllBits()              { memset(Data, 0, sizeof(Data)); }\n    void            SetAllBits()                { memset(Data, 255, sizeof(Data)); }\n    bool            TestBit(int n) const        { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Data, n); }\n    void            SetBit(int n)               { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Data, n); }\n    void            ClearBit(int n)             { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Data, n); }\n    void            SetBitRange(int n, int n2)  { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Data, n, n2); } // Works on range [n..n2)\n    bool            operator[](int n) const     { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Data, n); }\n};\n\n// Helper: ImBitVector\n// Store 1-bit per value.\nstruct IMGUI_API ImBitVector\n{\n    ImVector<ImU32> Storage;\n    void            Create(int sz)              { Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); }\n    void            Clear()                     { Storage.clear(); }\n    bool            TestBit(int n) const        { IM_ASSERT(n < (Storage.Size << 5)); return IM_BITARRAY_TESTBIT(Storage.Data, n); }\n    void            SetBit(int n)               { IM_ASSERT(n < (Storage.Size << 5)); ImBitArraySetBit(Storage.Data, n); }\n    void            ClearBit(int n)             { IM_ASSERT(n < (Storage.Size << 5)); ImBitArrayClearBit(Storage.Data, n); }\n};\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n// Helper: ImSpan<>\n// Pointing to a span of data we don't own.\ntemplate<typename T>\nstruct ImSpan\n{\n    T*                  Data;\n    T*                  DataEnd;\n\n    // Constructors, destructor\n    inline ImSpan()                                 { Data = DataEnd = NULL; }\n    inline ImSpan(T* data, int size)                { Data = data; DataEnd = data + size; }\n    inline ImSpan(T* data, T* data_end)             { Data = data; DataEnd = data_end; }\n\n    inline void         set(T* data, int size)      { Data = data; DataEnd = data + size; }\n    inline void         set(T* data, T* data_end)   { Data = data; DataEnd = data_end; }\n    inline int          size() const                { return (int)(ptrdiff_t)(DataEnd - Data); }\n    inline int          size_in_bytes() const       { return (int)(ptrdiff_t)(DataEnd - Data) * (int)sizeof(T); }\n    inline T&           operator[](int i)           { T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; }\n    inline const T&     operator[](int i) const     { const T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; }\n\n    inline T*           begin()                     { return Data; }\n    inline const T*     begin() const               { return Data; }\n    inline T*           end()                       { return DataEnd; }\n    inline const T*     end() const                 { return DataEnd; }\n\n    // Utilities\n    inline int  index_from_ptr(const T* it) const   { IM_ASSERT(it >= Data && it < DataEnd); const ptrdiff_t off = it - Data; return (int)off; }\n};\n\n// Helper: ImSpanAllocator<>\n// Facilitate storing multiple chunks into a single large block (the \"arena\")\n// - Usage: call Reserve() N times, allocate GetArenaSizeInBytes() worth, pass it to SetArenaBasePtr(), call GetSpan() N times to retrieve the aligned ranges.\ntemplate<int CHUNKS>\nstruct ImSpanAllocator\n{\n    char*   BasePtr;\n    int     CurrOff;\n    int     CurrIdx;\n    int     Offsets[CHUNKS];\n    int     Sizes[CHUNKS];\n\n    ImSpanAllocator()                               { memset((void*)this, 0, sizeof(*this)); }\n    inline void  Reserve(int n, size_t sz, int a=4) { IM_ASSERT(n == CurrIdx && n < CHUNKS); CurrOff = IM_MEMALIGN(CurrOff, a); Offsets[n] = CurrOff; Sizes[n] = (int)sz; CurrIdx++; CurrOff += (int)sz; }\n    inline int   GetArenaSizeInBytes()              { return CurrOff; }\n    inline void  SetArenaBasePtr(void* base_ptr)    { BasePtr = (char*)base_ptr; }\n    inline void* GetSpanPtrBegin(int n)             { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n]); }\n    inline void* GetSpanPtrEnd(int n)               { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n] + Sizes[n]); }\n    template<typename T>\n    inline void  GetSpan(int n, ImSpan<T>* span)    { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); }\n};\n\n// Helper: ImStableVector<>\n// Allocating chunks of BLOCKSIZE items. Objects pointers are never invalidated when growing, only by clear().\n// Important: does not destruct anything!\n// Implemented only the minimum set of functions we need for it.\ntemplate<typename T, int BLOCKSIZE>\nstruct ImStableVector\n{\n    int                 Size = 0;\n    int                 Capacity = 0;\n    ImVector<T*>        Blocks;\n\n    // Functions\n    inline ~ImStableVector()                        { for (T* block : Blocks) IM_FREE(block); }\n\n    inline void         clear()                     { Size = Capacity = 0; Blocks.clear_delete(); }\n    inline void         resize(int new_size)        { if (new_size > Capacity) reserve(new_size); Size = new_size; }\n    inline void         reserve(int new_cap)\n    {\n        new_cap = IM_MEMALIGN(new_cap, BLOCKSIZE);\n        int old_count = Capacity / BLOCKSIZE;\n        int new_count = new_cap / BLOCKSIZE;\n        if (new_count <= old_count)\n            return;\n        Blocks.resize(new_count);\n        for (int n = old_count; n < new_count; n++)\n            Blocks[n] = (T*)IM_ALLOC(sizeof(T) * BLOCKSIZE);\n        Capacity = new_cap;\n    }\n    inline T&           operator[](int i)           { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; }\n    inline const T&     operator[](int i) const     { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; }\n    inline T*           push_back(const T& v)       { int i = Size; IM_ASSERT(i >= 0); if (Size == Capacity) reserve(Capacity + BLOCKSIZE); void* ptr = &Blocks[i / BLOCKSIZE][i % BLOCKSIZE]; memcpy(ptr, &v, sizeof(v)); Size++; return (T*)ptr; }\n};\n\n// Helper: ImPool<>\n// Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer,\n// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object.\ntypedef int ImPoolIdx;\ntemplate<typename T>\nstruct ImPool\n{\n    ImVector<T>     Buf;        // Contiguous data\n    ImGuiStorage    Map;        // ID->Index\n    ImPoolIdx       FreeIdx;    // Next free idx to use\n    ImPoolIdx       AliveCount; // Number of active/alive items (for display purpose)\n\n    ImPool()    { FreeIdx = AliveCount = 0; }\n    ~ImPool()   { Clear(); }\n    T*          GetByKey(ImGuiID key)               { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Buf[idx] : NULL; }\n    T*          GetByIndex(ImPoolIdx n)             { return &Buf[n]; }\n    ImPoolIdx   GetIndex(const T* p) const          { IM_ASSERT(p >= Buf.Data && p < Buf.Data + Buf.Size); return (ImPoolIdx)(p - Buf.Data); }\n    T*          GetOrAddByKey(ImGuiID key)          { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Buf[*p_idx]; *p_idx = FreeIdx; return Add(); }\n    bool        Contains(const T* p) const          { return (p >= Buf.Data && p < Buf.Data + Buf.Size); }\n    void        Clear()                             { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; }\n    T*          Add()                               { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); AliveCount++; return &Buf[idx]; }\n    void        Remove(ImGuiID key, const T* p)     { Remove(key, GetIndex(p)); }\n    void        Remove(ImGuiID key, ImPoolIdx idx)  { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); AliveCount--; }\n    void        Reserve(int capacity)               { Buf.reserve(capacity); Map.Data.reserve(capacity); }\n\n    // To iterate a ImPool: for (int n = 0; n < pool.GetMapSize(); n++) if (T* t = pool.TryGetMapData(n)) { ... }\n    // Can be avoided if you know .Remove() has never been called on the pool, or AliveCount == GetMapSize()\n    int         GetAliveCount() const               { return AliveCount; }      // Number of active/alive items in the pool (for display purpose)\n    int         GetBufSize() const                  { return Buf.Size; }\n    int         GetMapSize() const                  { return Map.Data.Size; }   // It is the map we need iterate to find valid items, since we don't have \"alive\" storage anywhere\n    T*          TryGetMapData(ImPoolIdx n)          { int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx); }\n};\n\n// Helper: ImChunkStream<>\n// Build and iterate a contiguous stream of variable-sized structures.\n// This is used by Settings to store persistent data while reducing allocation count.\n// We store the chunk size first, and align the final size on 4 bytes boundaries.\n// The tedious/zealous amount of casting is to avoid -Wcast-align warnings.\ntemplate<typename T>\nstruct ImChunkStream\n{\n    ImVector<char>  Buf;\n\n    void    clear()                     { Buf.clear(); }\n    bool    empty() const               { return Buf.Size == 0; }\n    int     size() const                { return Buf.Size; }\n    T*      alloc_chunk(size_t sz)      { size_t HDR_SZ = 4; sz = IM_MEMALIGN(HDR_SZ + sz, 4u); int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); }\n    T*      begin()                     { size_t HDR_SZ = 4; if (!Buf.Data) return NULL; return (T*)(void*)(Buf.Data + HDR_SZ); }\n    T*      next_chunk(T* p)            { size_t HDR_SZ = 4; IM_ASSERT(p >= begin() && p < end()); p = (T*)(void*)((char*)(void*)p + chunk_size(p)); if (p == (T*)(void*)((char*)end() + HDR_SZ)) return (T*)0; IM_ASSERT(p < end()); return p; }\n    int     chunk_size(const T* p)      { return ((const int*)p)[-1]; }\n    T*      end()                       { return (T*)(void*)(Buf.Data + Buf.Size); }\n    int     offset_from_ptr(const T* p) { IM_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; }\n    T*      ptr_from_offset(int off)    { IM_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); }\n    void    swap(ImChunkStream<T>& rhs) { rhs.Buf.swap(Buf); }\n};\n\n// Helper: ImGuiTextIndex\n// Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API.\nstruct ImGuiTextIndex\n{\n    ImVector<int>   Offsets;\n    int             EndOffset = 0;                          // Because we don't own text buffer we need to maintain EndOffset (may bake in LineOffsets?)\n\n    void            clear()                                 { Offsets.clear(); EndOffset = 0; }\n    int             size()                                  { return Offsets.Size; }\n    const char*     get_line_begin(const char* base, int n) { return base + (Offsets.Size != 0 ? Offsets[n] : 0); }\n    const char*     get_line_end(const char* base, int n)   { return base + (n + 1 < Offsets.Size ? (Offsets[n + 1] - 1) : EndOffset); }\n    void            append(const char* base, int old_size, int new_size);\n};\n\n// Helper: ImGuiStorage\nIMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key);\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImDrawList support\n//-----------------------------------------------------------------------------\n\n// ImDrawList: Helper function to calculate a circle's segment count given its radius and a \"maximum error\" value.\n// Estimation of number of circle segment based on error is derived using method described in https://stackoverflow.com/a/2244088/15194693\n// Number of segments (N) is calculated using equation:\n//   N = ceil ( pi / acos(1 - error / r) )     where r > 0, error <= r\n// Our equation is significantly simpler that one in the post thanks for choosing segment that is\n// perpendicular to X axis. Follow steps in the article from this starting condition and you will\n// will get this result.\n//\n// Rendering circles with an odd number of segments, while mathematically correct will produce\n// asymmetrical results on the raster grid. Therefore we're rounding N to next even number (7->8, 8->8, 9->10 etc.)\n#define IM_ROUNDUP_TO_EVEN(_V)                                  ((((_V) + 1) / 2) * 2)\n#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN                     4\n#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX                     512\n#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR)    ImClamp(IM_ROUNDUP_TO_EVEN((int)ImCeil(IM_PI / ImAcos(1 - ImMin((_MAXERROR), (_RAD)) / (_RAD)))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX)\n\n// Raw equation from IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC rewritten for 'r' and 'error'.\n#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(_N,_MAXERROR)    ((_MAXERROR) / (1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))))\n#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_ERROR(_N,_RAD)     ((1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))) / (_RAD))\n\n// ImDrawList: Lookup table size for adaptive arc drawing, cover full circle.\n#ifndef IM_DRAWLIST_ARCFAST_TABLE_SIZE\n#define IM_DRAWLIST_ARCFAST_TABLE_SIZE                          48 // Number of samples in lookup table.\n#endif\n#define IM_DRAWLIST_ARCFAST_SAMPLE_MAX                          IM_DRAWLIST_ARCFAST_TABLE_SIZE // Sample index _PathArcToFastEx() for 360 angle.\n\n// Data shared between all ImDrawList instances\n// Conceptually this could have been called e.g. ImDrawListSharedContext\n// Typically one ImGui context would create and maintain one of this.\n// You may want to create your own instance of you try to ImDrawList completely without ImGui. In that case, watch out for future changes to this structure.\nstruct IMGUI_API ImDrawListSharedData\n{\n    ImVec2          TexUvWhitePixel;            // UV of white pixel in the atlas (== FontAtlas->TexUvWhitePixel)\n    const ImVec4*   TexUvLines;                 // UV of anti-aliased lines in the atlas (== FontAtlas->TexUvLines)\n    ImFontAtlas*    FontAtlas;                  // Current font atlas\n    ImFont*         Font;                       // Current font (used for simplified AddText overload)\n    float           FontSize;                   // Current font size (used for for simplified AddText overload)\n    float           FontScale;                  // Current font scale (== FontSize / Font->FontSize)\n    float           CurveTessellationTol;       // Tessellation tolerance when using PathBezierCurveTo()\n    float           CircleSegmentMaxError;      // Number of circle segments to use per pixel of radius for AddCircle() etc\n    float           InitialFringeScale;         // Initial scale to apply to AA fringe\n    ImDrawListFlags InitialFlags;               // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)\n    ImVec4          ClipRectFullscreen;         // Value for PushClipRectFullscreen()\n    ImVector<ImVec2> TempBuffer;                // Temporary write buffer\n    ImVector<ImDrawList*> DrawLists;            // All draw lists associated to this ImDrawListSharedData\n    ImGuiContext*   Context;                    // [OPTIONAL] Link to Dear ImGui context. 99% of ImDrawList/ImFontAtlas can function without an ImGui context, but this facilitate handling one legacy edge case.\n\n    // Lookup tables\n    ImVec2          ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle.\n    float           ArcFastRadiusCutoff;                        // Cutoff radius after which arc drawing will fallback to slower PathArcTo()\n    ImU8            CircleSegmentCounts[64];    // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)\n\n    int*            ShadowRectIds;              // IDs of rects for shadow texture (2 entries)\n    const ImVec4*   ShadowRectUvs;              // UV coordinates for shadow texture (10 entries)\n\n    ImDrawListSharedData();\n    ~ImDrawListSharedData();\n    void SetCircleTessellationMaxError(float max_error);\n};\n\nstruct ImDrawDataBuilder\n{\n    ImVector<ImDrawList*>*  Layers[2];      // Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData.\n    ImVector<ImDrawList*>   LayerData1;\n\n    ImDrawDataBuilder()                     { memset((void*)this, 0, sizeof(*this)); }\n};\n\nstruct ImFontStackData\n{\n    ImFont*     Font;\n    float       FontSizeBeforeScaling;      // ~~ style.FontSizeBase\n    float       FontSizeAfterScaling;       // ~~ g.FontSize\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Style support\n//-----------------------------------------------------------------------------\n\nstruct ImGuiStyleVarInfo\n{\n    ImU32           Count : 8;      // 1+\n    ImGuiDataType   DataType : 8;\n    ImU32           Offset : 16;    // Offset in parent structure\n    void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); }\n};\n\n// Stacked color modifier, backup of modified data so we can restore it\nstruct ImGuiColorMod\n{\n    ImGuiCol        Col;\n    ImVec4          BackupValue;\n};\n\n// Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable.\nstruct ImGuiStyleMod\n{\n    ImGuiStyleVar   VarIdx;\n    union           { int BackupInt[2]; float BackupFloat[2]; };\n    ImGuiStyleMod(ImGuiStyleVar idx, int v)     { VarIdx = idx; BackupInt[0] = v; }\n    ImGuiStyleMod(ImGuiStyleVar idx, float v)   { VarIdx = idx; BackupFloat[0] = v; }\n    ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v)  { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Data types support\n//-----------------------------------------------------------------------------\n\nstruct ImGuiDataTypeStorage\n{\n    ImU8        Data[8];        // Opaque storage to fit any data up to ImGuiDataType_COUNT\n};\n\n// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo().\nstruct ImGuiDataTypeInfo\n{\n    size_t      Size;           // Size in bytes\n    const char* Name;           // Short descriptive name for the type, for debugging\n    const char* PrintFmt;       // Default printf format for the type\n    const char* ScanFmt;        // Default scanf format for the type\n};\n\n// Extend ImGuiDataType_\nenum ImGuiDataTypePrivate_\n{\n    ImGuiDataType_Pointer = ImGuiDataType_COUNT,\n    ImGuiDataType_ID,\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Widgets support: flags, enums, data structures\n//-----------------------------------------------------------------------------\n\n// Extend ImGuiItemFlags\n// - input: PushItemFlag() manipulates g.CurrentItemFlags, g.NextItemData.ItemFlags, ItemAdd() calls may add extra flags too.\n// - output: stored in g.LastItemData.ItemFlags\nenum ImGuiItemFlagsPrivate_\n{\n    // Controlled by user\n    ImGuiItemFlags_ReadOnly                 = 1 << 11, // false     // [ALPHA] Allow hovering interactions but underlying value is not changed.\n    ImGuiItemFlags_MixedValue               = 1 << 12, // false     // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)\n    ImGuiItemFlags_NoWindowHoverableCheck   = 1 << 13, // false     // Disable hoverable check in ItemHoverable()\n    ImGuiItemFlags_AllowOverlap             = 1 << 14, // false     // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.\n    ImGuiItemFlags_NoNavDisableMouseHover   = 1 << 15, // false     // Nav keyboard/gamepad mode doesn't disable hover highlight (behave as if NavHighlightItemUnderNav==false).\n    ImGuiItemFlags_NoMarkEdited             = 1 << 16, // false     // Skip calling MarkItemEdited()\n    ImGuiItemFlags_NoFocus                  = 1 << 17, // false     // [EXPERIMENTAL: Not very well specced] Clicking doesn't take focus. Automatically sets ImGuiButtonFlags_NoFocus + ImGuiButtonFlags_NoNavFocus in ButtonBehavior().\n\n    // Controlled by widget code\n    ImGuiItemFlags_Inputable                = 1 << 20, // false     // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.\n    ImGuiItemFlags_HasSelectionUserData     = 1 << 21, // false     // Set by SetNextItemSelectionUserData()\n    ImGuiItemFlags_IsMultiSelect            = 1 << 22, // false     // Set by SetNextItemSelectionUserData()\n\n    ImGuiItemFlags_Default_                 = ImGuiItemFlags_AutoClosePopups,    // Please don't change, use PushItemFlag() instead.\n\n    // Obsolete\n    //ImGuiItemFlags_SelectableDontClosePopup = !ImGuiItemFlags_AutoClosePopups, // Can't have a redirect as we inverted the behavior\n};\n\n// Status flags for an already submitted item\n// - output: stored in g.LastItemData.StatusFlags\nenum ImGuiItemStatusFlags_\n{\n    ImGuiItemStatusFlags_None               = 0,\n    ImGuiItemStatusFlags_HoveredRect        = 1 << 0,   // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test)\n    ImGuiItemStatusFlags_HasDisplayRect     = 1 << 1,   // g.LastItemData.DisplayRect is valid\n    ImGuiItemStatusFlags_Edited             = 1 << 2,   // Value exposed by item was edited in the current frame (should match the bool return value of most widgets)\n    ImGuiItemStatusFlags_ToggledSelection   = 1 << 3,   // Set when Selectable(), TreeNode() reports toggling a selection. We can't report \"Selected\", only state changes, in order to easily handle clipping with less issues.\n    ImGuiItemStatusFlags_ToggledOpen        = 1 << 4,   // Set when TreeNode() reports toggling their open state.\n    ImGuiItemStatusFlags_HasDeactivated     = 1 << 5,   // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag.\n    ImGuiItemStatusFlags_Deactivated        = 1 << 6,   // Only valid if ImGuiItemStatusFlags_HasDeactivated is set.\n    ImGuiItemStatusFlags_HoveredWindow      = 1 << 7,   // Override the HoveredWindow test to allow cross-window hover testing.\n    ImGuiItemStatusFlags_Visible            = 1 << 8,   // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()).\n    ImGuiItemStatusFlags_HasClipRect        = 1 << 9,   // g.LastItemData.ClipRect is valid.\n    ImGuiItemStatusFlags_HasShortcut        = 1 << 10,  // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd().\n    //ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8,   // Removed IN 1.90.1 (Dec 2023). The trigger is part of g.NavActivateId. See commit 54c1bdeceb.\n\n    // Additional status + semantic for ImGuiTestEngine\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    ImGuiItemStatusFlags_Openable           = 1 << 20,  // Item is an openable (e.g. TreeNode)\n    ImGuiItemStatusFlags_Opened             = 1 << 21,  // Opened status\n    ImGuiItemStatusFlags_Checkable          = 1 << 22,  // Item is a checkable (e.g. CheckBox, MenuItem)\n    ImGuiItemStatusFlags_Checked            = 1 << 23,  // Checked status\n    ImGuiItemStatusFlags_Inputable          = 1 << 24,  // Item is a text-inputable (e.g. InputText, SliderXXX, DragXXX)\n#endif\n};\n\n// Extend ImGuiHoveredFlags_\nenum ImGuiHoveredFlagsPrivate_\n{\n    ImGuiHoveredFlags_DelayMask_                    = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay,\n    ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary,\n    ImGuiHoveredFlags_AllowedMaskForIsItemHovered   = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_,\n};\n\n// Extend ImGuiInputTextFlags_\nenum ImGuiInputTextFlagsPrivate_\n{\n    // [Internal]\n    ImGuiInputTextFlags_Multiline           = 1 << 26,  // For internal use by InputTextMultiline()\n    ImGuiInputTextFlags_MergedItem          = 1 << 27,  // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match.\n    ImGuiInputTextFlags_LocalizeDecimalPoint= 1 << 28,  // For internal use by InputScalar() and TempInputScalar()\n};\n\n// Extend ImGuiButtonFlags_\nenum ImGuiButtonFlagsPrivate_\n{\n    ImGuiButtonFlags_PressedOnClick         = 1 << 4,   // return true on click (mouse down event)\n    ImGuiButtonFlags_PressedOnClickRelease  = 1 << 5,   // [Default] return true on click + release on same item <-- this is what the majority of Button are using\n    ImGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 6, // return true on click + release even if the release event is not done while hovering the item\n    ImGuiButtonFlags_PressedOnRelease       = 1 << 7,   // return true on release (default requires click+release)\n    ImGuiButtonFlags_PressedOnDoubleClick   = 1 << 8,   // return true on double-click (default requires click+release)\n    ImGuiButtonFlags_PressedOnDragDropHold  = 1 << 9,   // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers)\n    //ImGuiButtonFlags_Repeat               = 1 << 10,  // hold to repeat -> use ImGuiItemFlags_ButtonRepeat instead.\n    ImGuiButtonFlags_FlattenChildren        = 1 << 11,  // allow interactions even if a child window is overlapping\n    ImGuiButtonFlags_AllowOverlap           = 1 << 12,  // require previous frame HoveredId to either match id or be null before being usable.\n    //ImGuiButtonFlags_DontClosePopups      = 1 << 13,  // disable automatically closing parent popup on press\n    //ImGuiButtonFlags_Disabled             = 1 << 14,  // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled\n    ImGuiButtonFlags_AlignTextBaseLine      = 1 << 15,  // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine\n    ImGuiButtonFlags_NoKeyModsAllowed       = 1 << 16,  // disable mouse interaction if a key modifier is held\n    ImGuiButtonFlags_NoHoldingActiveId      = 1 << 17,  // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only)\n    ImGuiButtonFlags_NoNavFocus             = 1 << 18,  // don't override navigation focus when activated (FIXME: this is essentially used every time an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.ItemFlags)\n    ImGuiButtonFlags_NoHoveredOnFocus       = 1 << 19,  // don't report as hovered when nav focus is on this item\n    ImGuiButtonFlags_NoSetKeyOwner          = 1 << 20,  // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)\n    ImGuiButtonFlags_NoTestKeyOwner         = 1 << 21,  // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)\n    ImGuiButtonFlags_NoFocus                = 1 << 22,  // [EXPERIMENTAL: Not very well specced]. Don't focus parent window when clicking.\n    ImGuiButtonFlags_PressedOnMask_         = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold,\n    ImGuiButtonFlags_PressedOnDefault_      = ImGuiButtonFlags_PressedOnClickRelease,\n    //ImGuiButtonFlags_NoKeyModifiers       = ImGuiButtonFlags_NoKeyModsAllowed, // Renamed in 1.91.4\n};\n\n// Extend ImGuiComboFlags_\nenum ImGuiComboFlagsPrivate_\n{\n    ImGuiComboFlags_CustomPreview           = 1 << 20,  // enable BeginComboPreview()\n};\n\n// Extend ImGuiSliderFlags_\nenum ImGuiSliderFlagsPrivate_\n{\n    ImGuiSliderFlags_Vertical               = 1 << 20,  // Should this slider be orientated vertically?\n    ImGuiSliderFlags_ReadOnly               = 1 << 21,  // Consider using g.NextItemData.ItemFlags |= ImGuiItemFlags_ReadOnly instead.\n};\n\n// Extend ImGuiSelectableFlags_\nenum ImGuiSelectableFlagsPrivate_\n{\n    // NB: need to be in sync with last value of ImGuiSelectableFlags_\n    ImGuiSelectableFlags_NoHoldingActiveID      = 1 << 20,\n    ImGuiSelectableFlags_SelectOnClick          = 1 << 22,  // Override button behavior to react on Click (default is Click+Release)\n    ImGuiSelectableFlags_SelectOnRelease        = 1 << 23,  // Override button behavior to react on Release (default is Click+Release)\n    ImGuiSelectableFlags_SpanAvailWidth         = 1 << 24,  // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus)\n    ImGuiSelectableFlags_SetNavIdOnHover        = 1 << 25,  // Set Nav/Focus ID on mouse hover (used by MenuItem)\n    ImGuiSelectableFlags_NoPadWithHalfSpacing   = 1 << 26,  // Disable padding each side with ItemSpacing * 0.5f\n    ImGuiSelectableFlags_NoSetKeyOwner          = 1 << 27,  // Don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)\n};\n\n// Extend ImGuiTreeNodeFlags_\nenum ImGuiTreeNodeFlagsPrivate_\n{\n    ImGuiTreeNodeFlags_NoNavFocus                 = 1 << 27,// Don't claim nav focus when interacting with this item (#8551)\n    ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader()\n    ImGuiTreeNodeFlags_UpsideDownArrow            = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, for reversed trees (#6517)\n    ImGuiTreeNodeFlags_OpenOnMask_                = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow,\n    ImGuiTreeNodeFlags_DrawLinesMask_             = ImGuiTreeNodeFlags_DrawLinesNone | ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes,\n};\n\nenum ImGuiSeparatorFlags_\n{\n    ImGuiSeparatorFlags_None                    = 0,\n    ImGuiSeparatorFlags_Horizontal              = 1 << 0,   // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar\n    ImGuiSeparatorFlags_Vertical                = 1 << 1,\n    ImGuiSeparatorFlags_SpanAllColumns          = 1 << 2,   // Make separator cover all columns of a legacy Columns() set.\n};\n\n// Flags for FocusWindow(). This is not called ImGuiFocusFlags to avoid confusion with public-facing ImGuiFocusedFlags.\n// FIXME: Once we finishing replacing more uses of GetTopMostPopupModal()+IsWindowWithinBeginStackOf()\n// and FindBlockingModal() with this, we may want to change the flag to be opt-out instead of opt-in.\nenum ImGuiFocusRequestFlags_\n{\n    ImGuiFocusRequestFlags_None                 = 0,\n    ImGuiFocusRequestFlags_RestoreFocusedChild  = 1 << 0,   // Find last focused child (if any) and focus it instead.\n    ImGuiFocusRequestFlags_UnlessBelowModal     = 1 << 1,   // Do not set focus if the window is below a modal.\n};\n\nenum ImGuiTextFlags_\n{\n    ImGuiTextFlags_None                         = 0,\n    ImGuiTextFlags_NoWidthForLargeClippedText   = 1 << 0,\n};\n\nenum ImGuiTooltipFlags_\n{\n    ImGuiTooltipFlags_None                      = 0,\n    ImGuiTooltipFlags_OverridePrevious          = 1 << 1,   // Clear/ignore previously submitted tooltip (defaults to append)\n};\n\n// FIXME: this is in development, not exposed/functional as a generic feature yet.\n// Horizontal/Vertical enums are fixed to 0/1 so they may be used to index ImVec2\nenum ImGuiLayoutType_\n{\n    ImGuiLayoutType_Horizontal = 0,\n    ImGuiLayoutType_Vertical = 1\n};\n\n// Flags for LogBegin() text capturing function\nenum ImGuiLogFlags_\n{\n    ImGuiLogFlags_None = 0,\n\n    ImGuiLogFlags_OutputTTY         = 1 << 0,\n    ImGuiLogFlags_OutputFile        = 1 << 1,\n    ImGuiLogFlags_OutputBuffer      = 1 << 2,\n    ImGuiLogFlags_OutputClipboard   = 1 << 3,\n    ImGuiLogFlags_OutputMask_       = ImGuiLogFlags_OutputTTY | ImGuiLogFlags_OutputFile | ImGuiLogFlags_OutputBuffer | ImGuiLogFlags_OutputClipboard,\n};\n\n// X/Y enums are fixed to 0/1 so they may be used to index ImVec2\nenum ImGuiAxis\n{\n    ImGuiAxis_None = -1,\n    ImGuiAxis_X = 0,\n    ImGuiAxis_Y = 1\n};\n\nenum ImGuiPlotType\n{\n    ImGuiPlotType_Lines,\n    ImGuiPlotType_Histogram,\n};\n\n// Storage data for BeginComboPreview()/EndComboPreview()\nstruct IMGUI_API ImGuiComboPreviewData\n{\n    ImRect          PreviewRect;\n    ImVec2          BackupCursorPos;\n    ImVec2          BackupCursorMaxPos;\n    ImVec2          BackupCursorPosPrevLine;\n    float           BackupPrevLineTextBaseOffset;\n    ImGuiLayoutType BackupLayout;\n\n    ImGuiComboPreviewData() { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// Stacked storage data for BeginGroup()/EndGroup()\nstruct IMGUI_API ImGuiGroupData\n{\n    ImGuiID     WindowID;\n    ImVec2      BackupCursorPos;\n    ImVec2      BackupCursorMaxPos;\n    ImVec2      BackupCursorPosPrevLine;\n    ImVec1      BackupIndent;\n    ImVec1      BackupGroupOffset;\n    ImVec2      BackupCurrLineSize;\n    float       BackupCurrLineTextBaseOffset;\n    ImGuiID     BackupActiveIdIsAlive;\n    bool        BackupActiveIdHasBeenEditedThisFrame;\n    bool        BackupDeactivatedIdIsAlive;\n    bool        BackupHoveredIdIsAlive;\n    bool        BackupIsSameLine;\n    bool        EmitItem;\n};\n\n// Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper.\nstruct IMGUI_API ImGuiMenuColumns\n{\n    ImU32       TotalWidth;\n    ImU32       NextTotalWidth;\n    ImU16       Spacing;\n    ImU16       OffsetIcon;         // Always zero for now\n    ImU16       OffsetLabel;        // Offsets are locked in Update()\n    ImU16       OffsetShortcut;\n    ImU16       OffsetMark;\n    ImU16       Widths[4];          // Width of:   Icon, Label, Shortcut, Mark  (accumulators for current frame)\n\n    ImGuiMenuColumns() { memset((void*)this, 0, sizeof(*this)); }\n    void        Update(float spacing, bool window_reappearing);\n    float       DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark);\n    void        CalcNextTotalWidth(bool update_offsets);\n};\n\n// Internal temporary state for deactivating InputText() instances.\nstruct IMGUI_API ImGuiInputTextDeactivatedState\n{\n    ImGuiID            ID;              // widget id owning the text state (which just got deactivated)\n    ImVector<char>     TextA;           // text buffer\n\n    ImGuiInputTextDeactivatedState()    { memset((void*)this, 0, sizeof(*this)); }\n    void    ClearFreeMemory()           { ID = 0; TextA.clear(); }\n};\n\n// Forward declare imstb_textedit.h structure + make its main configuration define accessible\n#undef IMSTB_TEXTEDIT_STRING\n#undef IMSTB_TEXTEDIT_CHARTYPE\n#define IMSTB_TEXTEDIT_STRING             ImGuiInputTextState\n#define IMSTB_TEXTEDIT_CHARTYPE           char\n#define IMSTB_TEXTEDIT_GETWIDTH_NEWLINE   (-1.0f)\n#define IMSTB_TEXTEDIT_UNDOSTATECOUNT     99\n#define IMSTB_TEXTEDIT_UNDOCHARCOUNT      999\nnamespace ImStb { struct STB_TexteditState; }\ntypedef ImStb::STB_TexteditState ImStbTexteditState;\n\n// Internal state of the currently focused/edited text input box\n// For a given item ID, access with ImGui::GetInputTextState()\nstruct IMGUI_API ImGuiInputTextState\n{\n    ImGuiContext*           Ctx;                    // parent UI context (needs to be set explicitly by parent).\n    ImStbTexteditState*     Stb;                    // State for stb_textedit.h\n    ImGuiInputTextFlags     Flags;                  // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.\n    ImGuiID                 ID;                     // widget id owning the text state\n    int                     TextLen;                // UTF-8 length of the string in TextA (in bytes)\n    const char*             TextSrc;                // == TextA.Data unless read-only, in which case == buf passed to InputText(). For _ReadOnly fields, pointer will be null outside the InputText() call.\n    ImVector<char>          TextA;                  // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1).\n    ImVector<char>          TextToRevertTo;         // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)\n    ImVector<char>          CallbackTextBackup;     // temporary storage for callback to support automatic reconcile of undo-stack\n    int                     BufCapacity;            // end-user buffer capacity (include zero terminator)\n    ImVec2                  Scroll;                 // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)\n    int                     LineCount;              // last line count (solely for debugging)\n    float                   WrapWidth;              // word-wrapping width\n    float                   CursorAnim;             // timer for cursor blink, reset on every user action so the cursor reappears immediately\n    bool                    CursorFollow;           // set when we want scrolling to follow the current cursor position (not always!)\n    bool                    CursorCenterY;          // set when we want scrolling to be centered over the cursor position (while resizing a word-wrapping field)\n    bool                    SelectedAllMouseLock;   // after a double-click to select all, we ignore further mouse drags to update selection\n    bool                    Edited;                 // edited this frame\n    bool                    WantReloadUserBuf;      // force a reload of user buf so it may be modified externally. may be automatic in future version.\n    ImS8                    LastMoveDirectionLR;    // ImGuiDir_Left or ImGuiDir_Right. track last movement direction so when cursor cross over a word-wrapping boundaries we can display it on either line depending on last move.s\n    int                     ReloadSelectionStart;\n    int                     ReloadSelectionEnd;\n\n    ImGuiInputTextState();\n    ~ImGuiInputTextState();\n    void        ClearText()                 { TextLen = 0; TextA[0] = 0; CursorClamp(); }\n    void        ClearFreeMemory()           { TextA.clear(); TextToRevertTo.clear(); }\n    void        OnKeyPressed(int key);      // Cannot be inline because we call in code in stb_textedit.h implementation\n    void        OnCharPressed(unsigned int c);\n    float       GetPreferredOffsetX() const;\n\n    // Cursor & Selection\n    void        CursorAnimReset();\n    void        CursorClamp();\n    bool        HasSelection() const;\n    void        ClearSelection();\n    int         GetCursorPos() const;\n    int         GetSelectionStart() const;\n    int         GetSelectionEnd() const;\n    void        SetSelection(int start, int end);\n    void        SelectAll();\n\n    // Reload user buf (WIP #2890)\n    // If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this)\n    //   strcpy(my_buf, \"hello\");\n    //   if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item\n    //       state->ReloadUserBufAndSelectAll();\n    void        ReloadUserBufAndSelectAll();\n    void        ReloadUserBufAndKeepSelection();\n    void        ReloadUserBufAndMoveToEnd();\n};\n\nenum ImGuiWindowRefreshFlags_\n{\n    ImGuiWindowRefreshFlags_None                = 0,\n    ImGuiWindowRefreshFlags_TryToAvoidRefresh   = 1 << 0,   // [EXPERIMENTAL] Try to keep existing contents, USER MUST NOT HONOR BEGIN() RETURNING FALSE AND NOT APPEND.\n    ImGuiWindowRefreshFlags_RefreshOnHover      = 1 << 1,   // [EXPERIMENTAL] Always refresh on hover\n    ImGuiWindowRefreshFlags_RefreshOnFocus      = 1 << 2,   // [EXPERIMENTAL] Always refresh on focus\n    // Refresh policy/frequency, Load Balancing etc.\n};\n\nenum ImGuiWindowBgClickFlags_\n{\n    ImGuiWindowBgClickFlags_None                = 0,\n    ImGuiWindowBgClickFlags_Move                = 1 << 0,   // Click on bg/void + drag to move window. Cleared by default when using io.ConfigWindowsMoveFromTitleBarOnly.\n};\n\nenum ImGuiNextWindowDataFlags_\n{\n    ImGuiNextWindowDataFlags_None               = 0,\n    ImGuiNextWindowDataFlags_HasPos             = 1 << 0,\n    ImGuiNextWindowDataFlags_HasSize            = 1 << 1,\n    ImGuiNextWindowDataFlags_HasContentSize     = 1 << 2,\n    ImGuiNextWindowDataFlags_HasCollapsed       = 1 << 3,\n    ImGuiNextWindowDataFlags_HasSizeConstraint  = 1 << 4,\n    ImGuiNextWindowDataFlags_HasFocus           = 1 << 5,\n    ImGuiNextWindowDataFlags_HasBgAlpha         = 1 << 6,\n    ImGuiNextWindowDataFlags_HasScroll          = 1 << 7,\n    ImGuiNextWindowDataFlags_HasWindowFlags     = 1 << 8,\n    ImGuiNextWindowDataFlags_HasChildFlags      = 1 << 9,\n    ImGuiNextWindowDataFlags_HasRefreshPolicy   = 1 << 10,\n    ImGuiNextWindowDataFlags_HasViewport        = 1 << 11,\n    ImGuiNextWindowDataFlags_HasDock            = 1 << 12,\n    ImGuiNextWindowDataFlags_HasWindowClass     = 1 << 13,\n};\n\n// Storage for SetNexWindow** functions\nstruct ImGuiNextWindowData\n{\n    ImGuiNextWindowDataFlags    HasFlags;\n\n    // Members below are NOT cleared. Always rely on HasFlags.\n    ImGuiCond                   PosCond;\n    ImGuiCond                   SizeCond;\n    ImGuiCond                   CollapsedCond;\n    ImGuiCond                   DockCond;\n    ImVec2                      PosVal;\n    ImVec2                      PosPivotVal;\n    ImVec2                      SizeVal;\n    ImVec2                      ContentSizeVal;\n    ImVec2                      ScrollVal;\n    ImGuiWindowFlags            WindowFlags;            // Only honored by BeginTable()\n    ImGuiChildFlags             ChildFlags;\n    bool                        PosUndock;\n    bool                        CollapsedVal;\n    ImRect                      SizeConstraintRect;\n    ImGuiSizeCallback           SizeCallback;\n    void*                       SizeCallbackUserData;\n    float                       BgAlphaVal;             // Override background alpha\n    ImGuiID                     ViewportId;\n    ImGuiID                     DockId;\n    ImGuiWindowClass            WindowClass;\n    ImVec2                      MenuBarOffsetMinVal;    // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?)\n    ImGuiWindowRefreshFlags     RefreshFlagsVal;\n\n    ImGuiNextWindowData()       { memset((void*)this, 0, sizeof(*this)); }\n    inline void ClearFlags()    { HasFlags = ImGuiNextWindowDataFlags_None; }\n};\n\nenum ImGuiNextItemDataFlags_\n{\n    ImGuiNextItemDataFlags_None             = 0,\n    ImGuiNextItemDataFlags_HasWidth         = 1 << 0,\n    ImGuiNextItemDataFlags_HasOpen          = 1 << 1,\n    ImGuiNextItemDataFlags_HasShortcut      = 1 << 2,\n    ImGuiNextItemDataFlags_HasRefVal        = 1 << 3,\n    ImGuiNextItemDataFlags_HasStorageID     = 1 << 4,\n    ImGuiNextItemDataFlags_HasColorMarker   = 1 << 5,\n};\n\nstruct ImGuiNextItemData\n{\n    ImGuiNextItemDataFlags      HasFlags;           // Called HasFlags instead of Flags to avoid mistaking this\n    ImGuiItemFlags              ItemFlags;          // Currently only tested/used for ImGuiItemFlags_AllowOverlap and ImGuiItemFlags_HasSelectionUserData.\n\n    // Members below are NOT cleared by ItemAdd() meaning they are still valid during e.g. NavProcessItem(). Always rely on HasFlags.\n    ImGuiID                     FocusScopeId;       // Set by SetNextItemSelectionUserData()\n    ImGuiSelectionUserData      SelectionUserData;  // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values)\n    float                       Width;              // Set by SetNextItemWidth()\n    ImGuiKeyChord               Shortcut;           // Set by SetNextItemShortcut()\n    ImGuiInputFlags             ShortcutFlags;      // Set by SetNextItemShortcut()\n    bool                        OpenVal;            // Set by SetNextItemOpen()\n    ImU8                        OpenCond;           // Set by SetNextItemOpen()\n    ImGuiDataTypeStorage        RefVal;             // Not exposed yet, for ImGuiInputTextFlags_ParseEmptyAsRefVal\n    ImGuiID                     StorageId;          // Set by SetNextItemStorageID()\n    ImU32                       ColorMarker;        // Set by SetNextItemColorMarker(). Not exposed yet, supported by DragScalar,SliderScalar and for ImGuiSliderFlags_ColorMarkers.\n\n    ImGuiNextItemData()         { memset((void*)this, 0, sizeof(*this)); SelectionUserData = -1; }\n    inline void ClearFlags()    { HasFlags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()!\n};\n\n// Status storage for the last submitted item\nstruct ImGuiLastItemData\n{\n    ImGuiID                 ID;\n    ImGuiItemFlags          ItemFlags;          // See ImGuiItemFlags_ (called 'InFlags' before v1.91.4).\n    ImGuiItemStatusFlags    StatusFlags;        // See ImGuiItemStatusFlags_\n    ImRect                  Rect;               // Full rectangle\n    ImRect                  NavRect;            // Navigation scoring rectangle (not displayed)\n    // Rarely used fields are not explicitly cleared, only valid when the corresponding ImGuiItemStatusFlags are set.\n    ImRect                  DisplayRect;        // Display rectangle. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) is set.\n    ImRect                  ClipRect;           // Clip rectangle at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasClipRect) is set..\n    ImGuiKeyChord           Shortcut;           // Shortcut at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasShortcut) is set..\n\n    ImGuiLastItemData()     { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// Store data emitted by TreeNode() for usage by TreePop()\n// - To implement ImGuiTreeNodeFlags_NavLeftJumpsToParent: store the minimum amount of data\n//   which we can't infer in TreePop(), to perform the equivalent of NavApplyItemToResult().\n//   Only stored when the node is a potential candidate for landing on a Left arrow jump.\nstruct ImGuiTreeNodeStackData\n{\n    ImGuiID                 ID;\n    ImGuiTreeNodeFlags      TreeFlags;\n    ImGuiItemFlags          ItemFlags;      // Used for nav landing\n    ImRect                  NavRect;        // Used for nav landing\n    float                   DrawLinesX1;\n    float                   DrawLinesToNodesY2;\n    ImGuiTableColumnIdx     DrawLinesTableColumn;\n};\n\n// sizeof() = 20\nstruct IMGUI_API ImGuiErrorRecoveryState\n{\n    short   SizeOfWindowStack;\n    short   SizeOfIDStack;\n    short   SizeOfTreeStack;\n    short   SizeOfColorStack;\n    short   SizeOfStyleVarStack;\n    short   SizeOfFontStack;\n    short   SizeOfFocusScopeStack;\n    short   SizeOfGroupStack;\n    short   SizeOfItemFlagsStack;\n    short   SizeOfBeginPopupStack;\n    short   SizeOfDisabledStack;\n\n    ImGuiErrorRecoveryState() { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// Data saved for each window pushed into the stack\nstruct ImGuiWindowStackData\n{\n    ImGuiWindow*            Window;\n    ImGuiLastItemData       ParentLastItemDataBackup;\n    ImGuiErrorRecoveryState StackSizesInBegin;          // Store size of various stacks for asserting\n    bool                    DisabledOverrideReenable;   // Non-child window override disabled flag\n    float                   DisabledOverrideReenableAlphaBackup;\n};\n\nstruct ImGuiShrinkWidthItem\n{\n    int         Index;\n    float       Width;\n    float       InitialWidth;\n};\n\nstruct ImGuiPtrOrIndex\n{\n    void*       Ptr;            // Either field can be set, not both. e.g. Dock node tab bars are loose while BeginTabBar() ones are in a pool.\n    int         Index;          // Usually index in a main pool.\n\n    ImGuiPtrOrIndex(void* ptr)  { Ptr = ptr; Index = -1; }\n    ImGuiPtrOrIndex(int index)  { Ptr = NULL; Index = index; }\n};\n\n// Data used by IsItemDeactivated()/IsItemDeactivatedAfterEdit() functions\nstruct ImGuiDeactivatedItemData\n{\n    ImGuiID     ID;\n    int         ElapseFrame;\n    bool        HasBeenEditedBefore;\n    bool        IsAlive;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Popup support\n//-----------------------------------------------------------------------------\n\nenum ImGuiPopupPositionPolicy\n{\n    ImGuiPopupPositionPolicy_Default,\n    ImGuiPopupPositionPolicy_ComboBox,\n    ImGuiPopupPositionPolicy_Tooltip,\n};\n\n// Storage for popup stacks (g.OpenPopupStack and g.BeginPopupStack)\nstruct ImGuiPopupData\n{\n    ImGuiID             PopupId;        // Set on OpenPopup()\n    ImGuiWindow*        Window;         // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup()\n    ImGuiWindow*        RestoreNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close\n    int                 ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as \"not any of layers\" value\n    int                 OpenFrameCount; // Set on OpenPopup()\n    ImGuiID             OpenParentId;   // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items)\n    ImVec2              OpenPopupPos;   // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse)\n    ImVec2              OpenMousePos;   // Set on OpenPopup(), copy of mouse position at the time of opening popup\n\n    ImGuiPopupData()    { memset((void*)this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Inputs support\n//-----------------------------------------------------------------------------\n\n// Bit array for named keys\ntypedef ImBitArray<ImGuiKey_NamedKey_COUNT, -ImGuiKey_NamedKey_BEGIN>    ImBitArrayForNamedKeys;\n\n// [Internal] Key ranges\n#define ImGuiKey_LegacyNativeKey_BEGIN  0\n#define ImGuiKey_LegacyNativeKey_END    512\n#define ImGuiKey_Keyboard_BEGIN         (ImGuiKey_NamedKey_BEGIN)\n#define ImGuiKey_Keyboard_END           (ImGuiKey_GamepadStart)\n#define ImGuiKey_Gamepad_BEGIN          (ImGuiKey_GamepadStart)\n#define ImGuiKey_Gamepad_END            (ImGuiKey_GamepadRStickDown + 1)\n#define ImGuiKey_Mouse_BEGIN            (ImGuiKey_MouseLeft)\n#define ImGuiKey_Mouse_END              (ImGuiKey_MouseWheelY + 1)\n#define ImGuiKey_Aliases_BEGIN          (ImGuiKey_Mouse_BEGIN)\n#define ImGuiKey_Aliases_END            (ImGuiKey_Mouse_END)\n\n// [Internal] Named shortcuts for Navigation\n#define ImGuiKey_NavKeyboardTweakSlow   ImGuiMod_Ctrl\n#define ImGuiKey_NavKeyboardTweakFast   ImGuiMod_Shift\n#define ImGuiKey_NavGamepadTweakSlow    ImGuiKey_GamepadL1\n#define ImGuiKey_NavGamepadTweakFast    ImGuiKey_GamepadR1\n#define ImGuiKey_NavGamepadActivate     (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceRight : ImGuiKey_GamepadFaceDown)\n#define ImGuiKey_NavGamepadCancel       (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceDown : ImGuiKey_GamepadFaceRight)\n#define ImGuiKey_NavGamepadMenu         ImGuiKey_GamepadFaceLeft    // Toggle menu layer. Hold to enable Windowing.\n#define ImGuiKey_NavGamepadContextMenu  ImGuiKey_GamepadFaceUp      // Open context menu (same as Shift+F10)\n\nenum ImGuiInputEventType\n{\n    ImGuiInputEventType_None = 0,\n    ImGuiInputEventType_MousePos,\n    ImGuiInputEventType_MouseWheel,\n    ImGuiInputEventType_MouseButton,\n    ImGuiInputEventType_MouseViewport,\n    ImGuiInputEventType_Key,\n    ImGuiInputEventType_Text,\n    ImGuiInputEventType_Focus,\n    ImGuiInputEventType_COUNT\n};\n\nenum ImGuiInputSource : int\n{\n    ImGuiInputSource_None = 0,\n    ImGuiInputSource_Mouse,         // Note: may be Mouse or TouchScreen or Pen. See io.MouseSource to distinguish them.\n    ImGuiInputSource_Keyboard,\n    ImGuiInputSource_Gamepad,\n    ImGuiInputSource_COUNT\n};\n\n// FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension?\n// Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor'\nstruct ImGuiInputEventMousePos      { float PosX, PosY; ImGuiMouseSource MouseSource; };\nstruct ImGuiInputEventMouseWheel    { float WheelX, WheelY; ImGuiMouseSource MouseSource; };\nstruct ImGuiInputEventMouseButton   { int Button; bool Down; ImGuiMouseSource MouseSource; };\nstruct ImGuiInputEventMouseViewport { ImGuiID HoveredViewportID; };\nstruct ImGuiInputEventKey           { ImGuiKey Key; bool Down; float AnalogValue; };\nstruct ImGuiInputEventText          { unsigned int Char; };\nstruct ImGuiInputEventAppFocused    { bool Focused; };\n\nstruct ImGuiInputEvent\n{\n    ImGuiInputEventType             Type;\n    ImGuiInputSource                Source;\n    ImU32                           EventId;        // Unique, sequential increasing integer to identify an event (if you need to correlate them to other data).\n    union\n    {\n        ImGuiInputEventMousePos     MousePos;       // if Type == ImGuiInputEventType_MousePos\n        ImGuiInputEventMouseWheel   MouseWheel;     // if Type == ImGuiInputEventType_MouseWheel\n        ImGuiInputEventMouseButton  MouseButton;    // if Type == ImGuiInputEventType_MouseButton\n        ImGuiInputEventMouseViewport MouseViewport; // if Type == ImGuiInputEventType_MouseViewport\n        ImGuiInputEventKey          Key;            // if Type == ImGuiInputEventType_Key\n        ImGuiInputEventText         Text;           // if Type == ImGuiInputEventType_Text\n        ImGuiInputEventAppFocused   AppFocused;     // if Type == ImGuiInputEventType_Focus\n    };\n    bool                            AddedByTestEngine;\n\n    ImGuiInputEvent() { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// Input function taking an 'ImGuiID owner_id' argument defaults to (ImGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior.\n#define ImGuiKeyOwner_Any           ((ImGuiID)0)    // Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.\n#define ImGuiKeyOwner_NoOwner       ((ImGuiID)-1)   // Require key to have no owner.\n//#define ImGuiKeyOwner_None ImGuiKeyOwner_NoOwner  // We previously called this 'ImGuiKeyOwner_None' but it was inconsistent with our pattern that _None values == 0 and quite dangerous. Also using _NoOwner makes the IsKeyPressed() calls more explicit.\n\ntypedef ImS16 ImGuiKeyRoutingIndex;\n\n// Routing table entry (sizeof() == 16 bytes)\nstruct ImGuiKeyRoutingData\n{\n    ImGuiKeyRoutingIndex            NextEntryIndex;\n    ImU16                           Mods;               // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits.\n    ImU16                           RoutingCurrScore;   // [DEBUG] For debug display\n    ImU16                           RoutingNextScore;   // Lower is better (0: perfect score)\n    ImGuiID                         RoutingCurr;\n    ImGuiID                         RoutingNext;\n\n    ImGuiKeyRoutingData()           { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 0; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; }\n};\n\n// Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching.\n// Stored in main context (1 instance)\nstruct ImGuiKeyRoutingTable\n{\n    ImGuiKeyRoutingIndex            Index[ImGuiKey_NamedKey_COUNT]; // Index of first entry in Entries[]\n    ImVector<ImGuiKeyRoutingData>   Entries;\n    ImVector<ImGuiKeyRoutingData>   EntriesNext;                    // Double-buffer to avoid reallocation (could use a shared buffer)\n\n    ImGuiKeyRoutingTable()          { Clear(); }\n    void Clear()                    { for (int n = 0; n < IM_COUNTOF(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); }\n};\n\n// This extends ImGuiKeyData but only for named keys (legacy keys don't support the new features)\n// Stored in main context (1 per named key). In the future it might be merged into ImGuiKeyData.\nstruct ImGuiKeyOwnerData\n{\n    ImGuiID     OwnerCurr;\n    ImGuiID     OwnerNext;\n    bool        LockThisFrame;      // Reading this key requires explicit owner id (until end of frame). Set by ImGuiInputFlags_LockThisFrame.\n    bool        LockUntilRelease;   // Reading this key requires explicit owner id (until key is released). Set by ImGuiInputFlags_LockUntilRelease. When this is true LockThisFrame is always true as well.\n\n    ImGuiKeyOwnerData()             { OwnerCurr = OwnerNext = ImGuiKeyOwner_NoOwner; LockThisFrame = LockUntilRelease = false; }\n};\n\n// Extend ImGuiInputFlags_\n// Flags for extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner()\n// Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function)\nenum ImGuiInputFlagsPrivate_\n{\n    // Flags for IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(), Shortcut()\n    // - Repeat mode: Repeat rate selection\n    ImGuiInputFlags_RepeatRateDefault           = 1 << 1,   // Repeat rate: Regular (default)\n    ImGuiInputFlags_RepeatRateNavMove           = 1 << 2,   // Repeat rate: Fast\n    ImGuiInputFlags_RepeatRateNavTweak          = 1 << 3,   // Repeat rate: Faster\n    // - Repeat mode: Specify when repeating key pressed can be interrupted.\n    // - In theory ImGuiInputFlags_RepeatUntilOtherKeyPress may be a desirable default, but it would break too many behavior so everything is opt-in.\n    ImGuiInputFlags_RepeatUntilRelease          = 1 << 4,   // Stop repeating when released (default for all functions except Shortcut). This only exists to allow overriding Shortcut() default behavior.\n    ImGuiInputFlags_RepeatUntilKeyModsChange    = 1 << 5,   // Stop repeating when released OR if keyboard mods are changed (default for Shortcut)\n    ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone = 1 << 6,  // Stop repeating when released OR if keyboard mods are leaving the None state. Allows going from Mod+Key to Key by releasing Mod.\n    ImGuiInputFlags_RepeatUntilOtherKeyPress    = 1 << 7,   // Stop repeating when released OR if any other keyboard key is pressed during the repeat\n\n    // Flags for SetKeyOwner(), SetItemKeyOwner()\n    // - Locking key away from non-input aware code. Locking is useful to make input-owner-aware code steal keys from non-input-owner-aware code. If all code is input-owner-aware locking would never be necessary.\n    ImGuiInputFlags_LockThisFrame               = 1 << 20,  // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame.\n    ImGuiInputFlags_LockUntilRelease            = 1 << 21,  // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released.\n\n    // - Condition for SetItemKeyOwner()\n    ImGuiInputFlags_CondHovered                 = 1 << 22,  // Only set if item is hovered (default to both)\n    ImGuiInputFlags_CondActive                  = 1 << 23,  // Only set if item is active (default to both)\n    ImGuiInputFlags_CondDefault_                = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,\n\n    // [Internal] Mask of which function support which flags\n    ImGuiInputFlags_RepeatRateMask_             = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak,\n    ImGuiInputFlags_RepeatUntilMask_            = ImGuiInputFlags_RepeatUntilRelease | ImGuiInputFlags_RepeatUntilKeyModsChange | ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone | ImGuiInputFlags_RepeatUntilOtherKeyPress,\n    ImGuiInputFlags_RepeatMask_                 = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_,\n    ImGuiInputFlags_CondMask_                   = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,\n    ImGuiInputFlags_RouteTypeMask_              = ImGuiInputFlags_RouteActive | ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteAlways,\n    ImGuiInputFlags_RouteOptionsMask_           = ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused | ImGuiInputFlags_RouteFromRootWindow,\n    ImGuiInputFlags_SupportedByIsKeyPressed     = ImGuiInputFlags_RepeatMask_,\n    ImGuiInputFlags_SupportedByIsMouseClicked   = ImGuiInputFlags_Repeat,\n    ImGuiInputFlags_SupportedByShortcut         = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_,\n    ImGuiInputFlags_SupportedBySetNextItemShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_ | ImGuiInputFlags_Tooltip,\n    ImGuiInputFlags_SupportedBySetKeyOwner      = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease,\n    ImGuiInputFlags_SupportedBySetItemKeyOwner  = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_,\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Clipper support\n//-----------------------------------------------------------------------------\n\n// Note that Max is exclusive, so perhaps should be using a Begin/End convention.\nstruct ImGuiListClipperRange\n{\n    int     Min;\n    int     Max;\n    bool    PosToIndexConvert;      // Begin/End are absolute position (will be converted to indices later)\n    ImS8    PosToIndexOffsetMin;    // Add to Min after converting to indices\n    ImS8    PosToIndexOffsetMax;    // Add to Min after converting to indices\n\n    static ImGuiListClipperRange    FromIndices(int min, int max)                               { ImGuiListClipperRange r = { min, max, false, 0, 0 }; return r; }\n    static ImGuiListClipperRange    FromPositions(float y1, float y2, int off_min, int off_max) { ImGuiListClipperRange r = { (int)y1, (int)y2, true, (ImS8)off_min, (ImS8)off_max }; return r; }\n};\n\n// Temporary clipper data, buffers shared/reused between instances\nstruct ImGuiListClipperData\n{\n    ImGuiListClipper*               ListClipper;\n    float                           LossynessOffset;\n    int                             StepNo;\n    int                             ItemsFrozen;\n    ImVector<ImGuiListClipperRange> Ranges;\n\n    ImGuiListClipperData()          { memset((void*)this, 0, sizeof(*this)); }\n    void                            Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Navigation support\n//-----------------------------------------------------------------------------\n\nenum ImGuiActivateFlags_\n{\n    ImGuiActivateFlags_None                 = 0,\n    ImGuiActivateFlags_PreferInput          = 1 << 0,       // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default for Enter key.\n    ImGuiActivateFlags_PreferTweak          = 1 << 1,       // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default for Space key and if keyboard is not used.\n    ImGuiActivateFlags_TryToPreserveState   = 1 << 2,       // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection)\n    ImGuiActivateFlags_FromTabbing          = 1 << 3,       // Activation requested by a tabbing request (ImGuiNavMoveFlags_IsTabbing)\n    ImGuiActivateFlags_FromShortcut         = 1 << 4,       // Activation requested by an item shortcut via SetNextItemShortcut() function.\n    ImGuiActivateFlags_FromFocusApi         = 1 << 5,       // Activation requested by an api request (ImGuiNavMoveFlags_FocusApi)\n};\n\n// Early work-in-progress API for ScrollToItem()\nenum ImGuiScrollFlags_\n{\n    ImGuiScrollFlags_None                   = 0,\n    ImGuiScrollFlags_KeepVisibleEdgeX       = 1 << 0,       // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis]\n    ImGuiScrollFlags_KeepVisibleEdgeY       = 1 << 1,       // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible]\n    ImGuiScrollFlags_KeepVisibleCenterX     = 1 << 2,       // If item is not visible: scroll to make the item centered on X axis [rarely used]\n    ImGuiScrollFlags_KeepVisibleCenterY     = 1 << 3,       // If item is not visible: scroll to make the item centered on Y axis\n    ImGuiScrollFlags_AlwaysCenterX          = 1 << 4,       // Always center the result item on X axis [rarely used]\n    ImGuiScrollFlags_AlwaysCenterY          = 1 << 5,       // Always center the result item on Y axis [default for Y axis for appearing window)\n    ImGuiScrollFlags_NoScrollParent         = 1 << 6,       // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to).\n    ImGuiScrollFlags_MaskX_                 = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX,\n    ImGuiScrollFlags_MaskY_                 = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY,\n};\n\nenum ImGuiNavRenderCursorFlags_\n{\n    ImGuiNavRenderCursorFlags_None          = 0,\n    ImGuiNavRenderCursorFlags_Compact       = 1 << 1,       // Compact highlight, no padding/distance from focused item\n    ImGuiNavRenderCursorFlags_AlwaysDraw    = 1 << 2,       // Draw rectangular highlight if (g.NavId == id) even when g.NavCursorVisible == false, aka even when using the mouse.\n    ImGuiNavRenderCursorFlags_NoRounding    = 1 << 3,\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiNavHighlightFlags_None             = ImGuiNavRenderCursorFlags_None,       // Renamed in 1.91.4\n    ImGuiNavHighlightFlags_Compact          = ImGuiNavRenderCursorFlags_Compact,    // Renamed in 1.91.4\n    ImGuiNavHighlightFlags_AlwaysDraw       = ImGuiNavRenderCursorFlags_AlwaysDraw, // Renamed in 1.91.4\n    ImGuiNavHighlightFlags_NoRounding       = ImGuiNavRenderCursorFlags_NoRounding, // Renamed in 1.91.4\n    //ImGuiNavHighlightFlags_TypeThin       = ImGuiNavRenderCursorFlags_Compact,    // Renamed in 1.90.2\n#endif\n};\n\nenum ImGuiNavMoveFlags_\n{\n    ImGuiNavMoveFlags_None                  = 0,\n    ImGuiNavMoveFlags_LoopX                 = 1 << 0,   // On failed request, restart from opposite side\n    ImGuiNavMoveFlags_LoopY                 = 1 << 1,\n    ImGuiNavMoveFlags_WrapX                 = 1 << 2,   // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left)\n    ImGuiNavMoveFlags_WrapY                 = 1 << 3,   // This is not super useful but provided for completeness\n    ImGuiNavMoveFlags_WrapMask_             = ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_WrapY,\n    ImGuiNavMoveFlags_AllowCurrentNavId     = 1 << 4,   // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place)\n    ImGuiNavMoveFlags_AlsoScoreVisibleSet   = 1 << 5,   // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown)\n    ImGuiNavMoveFlags_ScrollToEdgeY         = 1 << 6,   // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword as ImGuiScrollFlags\n    ImGuiNavMoveFlags_Forwarded             = 1 << 7,\n    ImGuiNavMoveFlags_DebugNoResult         = 1 << 8,   // Dummy scoring for debug purpose, don't apply result\n    ImGuiNavMoveFlags_FocusApi              = 1 << 9,   // Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details)\n    ImGuiNavMoveFlags_IsTabbing             = 1 << 10,  // == Focus + Activate if item is Inputable + DontChangeNavHighlight\n    ImGuiNavMoveFlags_IsPageMove            = 1 << 11,  // Identify a PageDown/PageUp request.\n    ImGuiNavMoveFlags_Activate              = 1 << 12,  // Activate/select target item.\n    ImGuiNavMoveFlags_NoSelect              = 1 << 13,  // Don't trigger selection by not setting g.NavJustMovedTo\n    ImGuiNavMoveFlags_NoSetNavCursorVisible = 1 << 14,  // Do not alter the nav cursor visible state\n    ImGuiNavMoveFlags_NoClearActiveId       = 1 << 15,  // (Experimental) Do not clear active id when applying move result\n};\n\nenum ImGuiNavLayer\n{\n    ImGuiNavLayer_Main  = 0,    // Main scrolling layer\n    ImGuiNavLayer_Menu  = 1,    // Menu layer (access with Alt)\n    ImGuiNavLayer_COUNT\n};\n\n// Storage for navigation query/results\nstruct ImGuiNavItemData\n{\n    ImGuiWindow*        Window;         // Init,Move    // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window)\n    ImGuiID             ID;             // Init,Move    // Best candidate item ID\n    ImGuiID             FocusScopeId;   // Init,Move    // Best candidate focus scope ID\n    ImRect              RectRel;        // Init,Move    // Best candidate bounding box in window relative space\n    ImGuiItemFlags      ItemFlags;      // ????,Move    // Best candidate item flags\n    float               DistBox;        //      Move    // Best candidate box distance to current NavId\n    float               DistCenter;     //      Move    // Best candidate center distance to current NavId\n    float               DistAxial;      //      Move    // Best candidate axial distance to current NavId\n    ImGuiSelectionUserData SelectionUserData;//I+Mov    // Best candidate SetNextItemSelectionUserData() value. Valid if (ItemFlags & ImGuiItemFlags_HasSelectionUserData)\n\n    ImGuiNavItemData()  { Clear(); }\n    void Clear()        { Window = NULL; ID = FocusScopeId = 0; ItemFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; }\n};\n\n// Storage for PushFocusScope(), g.FocusScopeStack[], g.NavFocusRoute[]\nstruct ImGuiFocusScopeData\n{\n    ImGuiID             ID;\n    ImGuiID             WindowID;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Typing-select support\n//-----------------------------------------------------------------------------\n\n// Flags for GetTypingSelectRequest()\nenum ImGuiTypingSelectFlags_\n{\n    ImGuiTypingSelectFlags_None                 = 0,\n    ImGuiTypingSelectFlags_AllowBackspace       = 1 << 0,   // Backspace to delete character inputs. If using: ensure GetTypingSelectRequest() is not called more than once per frame (filter by e.g. focus state)\n    ImGuiTypingSelectFlags_AllowSingleCharMode  = 1 << 1,   // Allow \"single char\" search mode which is activated when pressing the same character multiple times.\n};\n\n// Returned by GetTypingSelectRequest(), designed to eventually be public.\nstruct IMGUI_API ImGuiTypingSelectRequest\n{\n    ImGuiTypingSelectFlags  Flags;              // Flags passed to GetTypingSelectRequest()\n    int                     SearchBufferLen;\n    const char*             SearchBuffer;       // Search buffer contents (use full string. unless SingleCharMode is set, in which case use SingleCharSize).\n    bool                    SelectRequest;      // Set when buffer was modified this frame, requesting a selection.\n    bool                    SingleCharMode;     // Notify when buffer contains same character repeated, to implement special mode. In this situation it preferred to not display any on-screen search indication.\n    ImS8                    SingleCharSize;     // Length in bytes of first letter codepoint (1 for ascii, 2-4 for UTF-8). If (SearchBufferLen==RepeatCharSize) only 1 letter has been input.\n};\n\n// Storage for GetTypingSelectRequest()\nstruct IMGUI_API ImGuiTypingSelectState\n{\n    ImGuiTypingSelectRequest Request;           // User-facing data\n    char            SearchBuffer[64];           // Search buffer: no need to make dynamic as this search is very transient.\n    ImGuiID         FocusScope;\n    int             LastRequestFrame = 0;\n    float           LastRequestTime = 0.0f;\n    bool            SingleCharModeLock = false; // After a certain single char repeat count we lock into SingleCharMode. Two benefits: 1) buffer never fill, 2) we can provide an immediate SingleChar mode without timer elapsing.\n\n    ImGuiTypingSelectState() { memset((void*)this, 0, sizeof(*this)); }\n    void            Clear()  { SearchBuffer[0] = 0; SingleCharModeLock = false; } // We preserve remaining data for easier debugging\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Columns support\n//-----------------------------------------------------------------------------\n\n// Flags for internal's BeginColumns(). This is an obsolete API. Prefer using BeginTable() nowadays!\nenum ImGuiOldColumnFlags_\n{\n    ImGuiOldColumnFlags_None                    = 0,\n    ImGuiOldColumnFlags_NoBorder                = 1 << 0,   // Disable column dividers\n    ImGuiOldColumnFlags_NoResize                = 1 << 1,   // Disable resizing columns when clicking on the dividers\n    ImGuiOldColumnFlags_NoPreserveWidths        = 1 << 2,   // Disable column width preservation when adjusting columns\n    ImGuiOldColumnFlags_NoForceWithinWindow     = 1 << 3,   // Disable forcing columns to fit within window\n    ImGuiOldColumnFlags_GrowParentContentsSize  = 1 << 4,   // Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove.\n\n    // Obsolete names (will be removed)\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    //ImGuiColumnsFlags_None                    = ImGuiOldColumnFlags_None,\n    //ImGuiColumnsFlags_NoBorder                = ImGuiOldColumnFlags_NoBorder,\n    //ImGuiColumnsFlags_NoResize                = ImGuiOldColumnFlags_NoResize,\n    //ImGuiColumnsFlags_NoPreserveWidths        = ImGuiOldColumnFlags_NoPreserveWidths,\n    //ImGuiColumnsFlags_NoForceWithinWindow     = ImGuiOldColumnFlags_NoForceWithinWindow,\n    //ImGuiColumnsFlags_GrowParentContentsSize  = ImGuiOldColumnFlags_GrowParentContentsSize,\n#endif\n};\n\nstruct ImGuiOldColumnData\n{\n    float               OffsetNorm;             // Column start offset, normalized 0.0 (far left) -> 1.0 (far right)\n    float               OffsetNormBeforeResize;\n    ImGuiOldColumnFlags Flags;                  // Not exposed\n    ImRect              ClipRect;\n\n    ImGuiOldColumnData() { memset((void*)this, 0, sizeof(*this)); }\n};\n\nstruct ImGuiOldColumns\n{\n    ImGuiID             ID;\n    ImGuiOldColumnFlags Flags;\n    bool                IsFirstFrame;\n    bool                IsBeingResized;\n    int                 Current;\n    int                 Count;\n    float               OffMinX, OffMaxX;       // Offsets from HostWorkRect.Min.x\n    float               LineMinY, LineMaxY;\n    float               HostCursorPosY;         // Backup of CursorPos at the time of BeginColumns()\n    float               HostCursorMaxPosX;      // Backup of CursorMaxPos at the time of BeginColumns()\n    ImRect              HostInitialClipRect;    // Backup of ClipRect at the time of BeginColumns()\n    ImRect              HostBackupClipRect;     // Backup of ClipRect during PushColumnsBackground()/PopColumnsBackground()\n    ImRect              HostBackupParentWorkRect;//Backup of WorkRect at the time of BeginColumns()\n    ImVector<ImGuiOldColumnData> Columns;\n    ImDrawListSplitter  Splitter;\n\n    ImGuiOldColumns()   { memset((void*)this, 0, sizeof(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Box-select support\n//-----------------------------------------------------------------------------\n\nstruct ImGuiBoxSelectState\n{\n    // Active box-selection data (persistent, 1 active at a time)\n    ImGuiID                 ID;\n    bool                    IsActive;\n    bool                    IsStarting;\n    bool                    IsStartedFromVoid;  // Starting click was not from an item.\n    bool                    IsStartedSetNavIdOnce;\n    bool                    RequestClear;\n    ImGuiKeyChord           KeyMods : 16;       // Latched key-mods for box-select logic.\n    ImVec2                  StartPosRel;        // Start position in window-contents relative space (to support scrolling)\n    ImVec2                  EndPosRel;          // End position in window-contents relative space\n    ImVec2                  ScrollAccum;        // Scrolling accumulator (to behave at high-frame spaces)\n    ImGuiWindow*            Window;\n\n    // Temporary/Transient data\n    bool                    UnclipMode;         // (Temp/Transient, here in hot area). Set/cleared by the BeginMultiSelect()/EndMultiSelect() owning active box-select.\n    ImRect                  UnclipRect;         // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets.\n    ImRect                  BoxSelectRectPrev;  // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos)\n    ImRect                  BoxSelectRectCurr;\n\n    ImGuiBoxSelectState()   { memset((void*)this, 0, sizeof(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Multi-select support\n//-----------------------------------------------------------------------------\n\n// We always assume that -1 is an invalid value (which works for indices and pointers)\n#define ImGuiSelectionUserData_Invalid        ((ImGuiSelectionUserData)-1)\n\n// Temporary storage for multi-select\nstruct IMGUI_API ImGuiMultiSelectTempData\n{\n    ImGuiMultiSelectIO      IO;                 // MUST BE FIRST FIELD. Requests are set and returned by BeginMultiSelect()/EndMultiSelect() + written to by user during the loop.\n    ImGuiMultiSelectState*  Storage;\n    ImGuiID                 FocusScopeId;       // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually)\n    ImGuiMultiSelectFlags   Flags;\n    ImVec2                  ScopeRectMin;\n    ImVec2                  BackupCursorMaxPos;\n    ImGuiSelectionUserData  LastSubmittedItem;  // Copy of last submitted item data, used to merge output ranges.\n    ImGuiID                 BoxSelectId;\n    ImGuiKeyChord           KeyMods;\n    ImS8                    LoopRequestSetAll;  // -1: no operation, 0: clear all, 1: select all.\n    bool                    IsEndIO;            // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state.\n    bool                    IsFocused;          // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection.\n    bool                    IsKeyboardSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation.\n    bool                    NavIdPassedBy;\n    bool                    RangeSrcPassedBy;   // Set by the item that matches RangeSrcItem.\n    bool                    RangeDstPassedBy;   // Set by the item that matches NavJustMovedToId when IsSetRange is set.\n\n    ImGuiMultiSelectTempData()  { Clear(); }\n    void Clear()            { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation.\n    void ClearIO()          { IO.Requests.resize(0); IO.RangeSrcItem = IO.NavIdItem = ImGuiSelectionUserData_Invalid; IO.NavIdSelected = IO.RangeSrcReset = false; }\n};\n\n// Persistent storage for multi-select (as long as selection is alive)\nstruct IMGUI_API ImGuiMultiSelectState\n{\n    ImGuiWindow*            Window;\n    ImGuiID                 ID;\n    int                     LastFrameActive;    // Last used frame-count, for GC.\n    int                     LastSelectionSize;  // Set by BeginMultiSelect() based on optional info provided by user. May be -1 if unknown.\n    ImS8                    RangeSelected;      // -1 (don't have) or true/false\n    ImS8                    NavIdSelected;      // -1 (don't have) or true/false\n    ImGuiSelectionUserData  RangeSrcItem;       //\n    ImGuiSelectionUserData  NavIdItem;          // SetNextItemSelectionUserData() value for NavId (if part of submitted items)\n\n    ImGuiMultiSelectState() { Window = NULL; ID = 0; LastFrameActive = LastSelectionSize = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Docking support\n//-----------------------------------------------------------------------------\n\n#define DOCKING_HOST_DRAW_CHANNEL_BG 0  // Dock host: background fill\n#define DOCKING_HOST_DRAW_CHANNEL_FG 1  // Dock host: decorations and contents\n\n#ifdef IMGUI_HAS_DOCK\n\n// Extend ImGuiDockNodeFlags_\nenum ImGuiDockNodeFlagsPrivate_\n{\n    // [Internal]\n    ImGuiDockNodeFlags_DockSpace                = 1 << 10,  // Saved // A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window.\n    ImGuiDockNodeFlags_CentralNode              = 1 << 11,  // Saved // The central node has 2 main properties: stay visible when empty, only use \"remaining\" spaces from its neighbor.\n    ImGuiDockNodeFlags_NoTabBar                 = 1 << 12,  // Saved // Tab bar is completely unavailable. No triangle in the corner to enable it back.\n    ImGuiDockNodeFlags_HiddenTabBar             = 1 << 13,  // Saved // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar)\n    ImGuiDockNodeFlags_NoWindowMenuButton       = 1 << 14,  // Saved // Disable window/docking menu (that one that appears instead of the collapse button)\n    ImGuiDockNodeFlags_NoCloseButton            = 1 << 15,  // Saved // Disable close button\n    ImGuiDockNodeFlags_NoResizeX                = 1 << 16,  //       //\n    ImGuiDockNodeFlags_NoResizeY                = 1 << 17,  //       //\n    ImGuiDockNodeFlags_DockedWindowsInFocusRoute= 1 << 18,  //       // Any docked window will be automatically be focus-route chained (window->ParentWindowForFocusRoute set to this) so Shortcut() in this window can run when any docked window is focused.\n\n    // Disable docking/undocking actions in this dockspace or individual node (existing docked nodes will be preserved)\n    // Those are not exposed in public because the desirable sharing/inheriting/copy-flag-on-split behaviors are quite difficult to design and understand.\n    // The two public flags ImGuiDockNodeFlags_NoDockingOverCentralNode/ImGuiDockNodeFlags_NoDockingSplit don't have those issues.\n    ImGuiDockNodeFlags_NoDockingSplitOther      = 1 << 19,  //       // Disable this node from splitting other windows/nodes.\n    ImGuiDockNodeFlags_NoDockingOverMe          = 1 << 20,  //       // Disable other windows/nodes from being docked over this node.\n    ImGuiDockNodeFlags_NoDockingOverOther       = 1 << 21,  //       // Disable this node from being docked over another window or non-empty node.\n    ImGuiDockNodeFlags_NoDockingOverEmpty       = 1 << 22,  //       // Disable this node from being docked over an empty node (e.g. DockSpace with no other windows)\n    ImGuiDockNodeFlags_NoDocking                = ImGuiDockNodeFlags_NoDockingOverMe | ImGuiDockNodeFlags_NoDockingOverOther | ImGuiDockNodeFlags_NoDockingOverEmpty | ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoDockingSplitOther,\n\n    // Masks\n    ImGuiDockNodeFlags_SharedFlagsInheritMask_  = ~0,\n    ImGuiDockNodeFlags_NoResizeFlagsMask_       = (int)ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY,\n\n    // When splitting, those local flags are moved to the inheriting child, never duplicated\n    ImGuiDockNodeFlags_LocalFlagsTransferMask_  = (int)ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | (int)ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton,\n    ImGuiDockNodeFlags_SavedFlagsMask_          = ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton,\n};\n\n// Store the source authority (dock node vs window) of a field\nenum ImGuiDataAuthority_\n{\n    ImGuiDataAuthority_Auto,\n    ImGuiDataAuthority_DockNode,\n    ImGuiDataAuthority_Window,\n};\n\nenum ImGuiDockNodeState\n{\n    ImGuiDockNodeState_Unknown,\n    ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow,\n    ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing,\n    ImGuiDockNodeState_HostWindowVisible,\n};\n\n// sizeof() 156~192\nstruct IMGUI_API ImGuiDockNode\n{\n    ImGuiID                 ID;\n    ImGuiDockNodeFlags      SharedFlags;                // (Write) Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node)\n    ImGuiDockNodeFlags      LocalFlags;                 // (Write) Flags specific to this node\n    ImGuiDockNodeFlags      LocalFlagsInWindows;        // (Write) Flags specific to this node, applied from windows\n    ImGuiDockNodeFlags      MergedFlags;                // (Read)  Effective flags (== SharedFlags | LocalFlagsInNode | LocalFlagsInWindows)\n    ImGuiDockNodeState      State;\n    ImGuiDockNode*          ParentNode;\n    ImGuiDockNode*          ChildNodes[2];              // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array.\n    ImVector<ImGuiWindow*>  Windows;                    // Note: unordered list! Iterate TabBar->Tabs for user-order.\n    ImGuiTabBar*            TabBar;\n    ImVec2                  Pos;                        // Current position\n    ImVec2                  Size;                       // Current size\n    ImVec2                  SizeRef;                    // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size.\n    ImGuiAxis               SplitAxis;                  // [Split node only] Split axis (X or Y)\n    ImGuiWindowClass        WindowClass;                // [Root node only]\n    ImU32                   LastBgColor;\n\n    ImGuiWindow*            HostWindow;\n    ImGuiWindow*            VisibleWindow;              // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window.\n    ImGuiDockNode*          CentralNode;                // [Root node only] Pointer to central node.\n    ImGuiDockNode*          OnlyNodeWithWindows;        // [Root node only] Set when there is a single visible node within the hierarchy.\n    int                     CountNodeWithWindows;       // [Root node only]\n    int                     LastFrameAlive;             // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly\n    int                     LastFrameActive;            // Last frame number the node was updated.\n    int                     LastFrameFocused;           // Last frame number the node was focused.\n    ImGuiID                 LastFocusedNodeId;          // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused.\n    ImGuiID                 SelectedTabId;              // [Leaf node only] Which of our tab/window is selected.\n    ImGuiID                 WantCloseTabId;             // [Leaf node only] Set when closing a specific tab/window.\n    ImGuiID                 RefViewportId;              // Reference viewport ID from visible window when HostWindow == NULL.\n    ImGuiDataAuthority      AuthorityForPos         :3;\n    ImGuiDataAuthority      AuthorityForSize        :3;\n    ImGuiDataAuthority      AuthorityForViewport    :3;\n    bool                    IsVisible               :1; // Set to false when the node is hidden (usually disabled as it has no active window)\n    bool                    IsFocused               :1;\n    bool                    IsBgDrawnThisFrame      :1;\n    bool                    HasCloseButton          :1; // Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one.\n    bool                    HasWindowMenuButton     :1;\n    bool                    HasCentralNodeChild     :1;\n    bool                    WantCloseAll            :1; // Set when closing all tabs at once.\n    bool                    WantLockSizeOnce        :1;\n    bool                    WantMouseMove           :1; // After a node extraction we need to transition toward moving the newly created host window\n    bool                    WantHiddenTabBarUpdate  :1;\n    bool                    WantHiddenTabBarToggle  :1;\n\n    ImGuiDockNode(ImGuiID id);\n    ~ImGuiDockNode();\n    bool                    IsRootNode() const      { return ParentNode == NULL; }\n    bool                    IsDockSpace() const     { return (MergedFlags & ImGuiDockNodeFlags_DockSpace) != 0; }\n    bool                    IsFloatingNode() const  { return ParentNode == NULL && (MergedFlags & ImGuiDockNodeFlags_DockSpace) == 0; }\n    bool                    IsCentralNode() const   { return (MergedFlags & ImGuiDockNodeFlags_CentralNode) != 0; }\n    bool                    IsHiddenTabBar() const  { return (MergedFlags & ImGuiDockNodeFlags_HiddenTabBar) != 0; } // Hidden tab bar can be shown back by clicking the small triangle\n    bool                    IsNoTabBar() const      { return (MergedFlags & ImGuiDockNodeFlags_NoTabBar) != 0; }     // Never show a tab bar\n    bool                    IsSplitNode() const     { return ChildNodes[0] != NULL; }\n    bool                    IsLeafNode() const      { return ChildNodes[0] == NULL; }\n    bool                    IsEmpty() const         { return ChildNodes[0] == NULL && Windows.Size == 0; }\n    ImRect                  Rect() const            { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }\n\n    void                    SetLocalFlags(ImGuiDockNodeFlags flags) { LocalFlags = flags; UpdateMergedFlags(); }\n    void                    UpdateMergedFlags()     { MergedFlags = SharedFlags | LocalFlags | LocalFlagsInWindows; }\n};\n\n// List of colors that are stored at the time of Begin() into Docked Windows.\n// We currently store the packed colors in a simple array window->DockStyle.Colors[].\n// A better solution may involve appending into a log of colors in ImGuiContext + store offsets into those arrays in ImGuiWindow,\n// but it would be more complex as we'd need to double-buffer both as e.g. drop target may refer to window from last frame.\nenum ImGuiWindowDockStyleCol\n{\n    ImGuiWindowDockStyleCol_Text,\n    ImGuiWindowDockStyleCol_TabHovered,\n    ImGuiWindowDockStyleCol_TabFocused,\n    ImGuiWindowDockStyleCol_TabSelected,\n    ImGuiWindowDockStyleCol_TabSelectedOverline,\n    ImGuiWindowDockStyleCol_TabDimmed,\n    ImGuiWindowDockStyleCol_TabDimmedSelected,\n    ImGuiWindowDockStyleCol_TabDimmedSelectedOverline,\n    ImGuiWindowDockStyleCol_UnsavedMarker,\n    ImGuiWindowDockStyleCol_COUNT\n};\n\n// We don't store style.Alpha: dock_node->LastBgColor embeds it and otherwise it would only affect the docking tab, which intuitively I would say we don't want to.\nstruct ImGuiWindowDockStyle\n{\n    ImU32 Colors[ImGuiWindowDockStyleCol_COUNT];\n};\n\nstruct ImGuiDockContext\n{\n    ImGuiStorage                    Nodes;          // Map ID -> ImGuiDockNode*: Active nodes\n    ImVector<ImGuiDockRequest>      Requests;\n    ImVector<ImGuiDockNodeSettings> NodesSettings;\n    bool                            WantFullRebuild;\n    ImGuiDockContext()              { memset((void*)this, 0, sizeof(*this)); }\n};\n\n#endif // #ifdef IMGUI_HAS_DOCK\n\n//-----------------------------------------------------------------------------\n// [SECTION] Viewport support\n//-----------------------------------------------------------------------------\n\n// ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!)\n// Every instance of ImGuiViewport is in fact a ImGuiViewportP.\nstruct ImGuiViewportP : public ImGuiViewport\n{\n    ImGuiWindow*        Window;                 // Set when the viewport is owned by a window (and ImGuiViewportFlags_CanHostOtherWindows is NOT set)\n    int                 Idx;\n    int                 LastFrameActive;        // Last frame number this viewport was activated by a window\n    int                 LastFocusedStampCount;  // Last stamp number from when a window hosted by this viewport was focused (by comparing this value between two viewport we have an implicit viewport z-order we use as fallback)\n    ImGuiID             LastNameHash;\n    ImVec2              LastPos;\n    ImVec2              LastSize;\n    float               Alpha;                  // Window opacity (when dragging dockable windows/viewports we make them transparent)\n    float               LastAlpha;\n    bool                LastFocusedHadNavWindow;// Instead of maintaining a LastFocusedWindow (which may harder to correctly maintain), we merely store weither NavWindow != NULL last time the viewport was focused.\n    short               PlatformMonitor;\n    int                 BgFgDrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used\n    ImDrawList*         BgFgDrawLists[2];       // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays.\n    ImDrawData          DrawDataP;\n    ImDrawDataBuilder   DrawDataBuilder;        // Temporary data while building final ImDrawData\n    ImVec2              LastPlatformPos;\n    ImVec2              LastPlatformSize;\n    ImVec2              LastRendererSize;\n\n    // Per-viewport work area\n    // - Insets are >= 0.0f values, distance from viewport corners to work area.\n    // - BeginMainMenuBar() and DockspaceOverViewport() tend to use work area to avoid stepping over existing contents.\n    // - Generally 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land.\n    ImVec2              WorkInsetMin;           // Work Area inset locked for the frame. GetWorkRect() always fits within GetMainRect().\n    ImVec2              WorkInsetMax;           // \"\n    ImVec2              BuildWorkInsetMin;      // Work Area inset accumulator for current frame, to become next frame's WorkInset\n    ImVec2              BuildWorkInsetMax;      // \"\n\n    ImGuiViewportP()                    { Window = NULL; Idx = -1; LastFrameActive = BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = LastFocusedStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; LastFocusedHadNavWindow = false; PlatformMonitor = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); }\n    ~ImGuiViewportP()                   { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); }\n    void    ClearRequestFlags()         { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; }\n\n    // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect)\n    ImVec2  CalcWorkRectPos(const ImVec2& inset_min) const                           { return ImVec2(Pos.x + inset_min.x, Pos.y + inset_min.y); }\n    ImVec2  CalcWorkRectSize(const ImVec2& inset_min, const ImVec2& inset_max) const { return ImVec2(ImMax(0.0f, Size.x - inset_min.x - inset_max.x), ImMax(0.0f, Size.y - inset_min.y - inset_max.y)); }\n    void    UpdateWorkRect()            { WorkPos = CalcWorkRectPos(WorkInsetMin); WorkSize = CalcWorkRectSize(WorkInsetMin, WorkInsetMax); } // Update public fields\n\n    // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry)\n    ImRect  GetMainRect() const         { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }\n    ImRect  GetWorkRect() const         { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); }\n    ImRect  GetBuildWorkRect() const    { ImVec2 pos = CalcWorkRectPos(BuildWorkInsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkInsetMin, BuildWorkInsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Settings support\n//-----------------------------------------------------------------------------\n\n// Windows data saved in imgui.ini file\n// Because we never destroy or rename ImGuiWindowSettings, we can store the names in a separate buffer easily.\n// (this is designed to be stored in a ImChunkStream buffer, with the variable-length Name following our structure)\nstruct ImGuiWindowSettings\n{\n    ImGuiID     ID;\n    ImVec2ih    Pos;            // NB: Settings position are stored RELATIVE to the viewport! Whereas runtime ones are absolute positions.\n    ImVec2ih    Size;\n    ImVec2ih    ViewportPos;\n    ImGuiID     ViewportId;\n    ImGuiID     DockId;         // ID of last known DockNode (even if the DockNode is invisible because it has only 1 active window), or 0 if none.\n    ImGuiID     ClassId;        // ID of window class if specified\n    short       DockOrder;      // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible.\n    bool        Collapsed;\n    bool        IsChild;\n    bool        WantApply;      // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)\n    bool        WantDelete;     // Set to invalidate/delete the settings entry\n\n    ImGuiWindowSettings()       { memset((void*)this, 0, sizeof(*this)); DockOrder = -1; }\n    char* GetName()             { return (char*)(this + 1); }\n};\n\nstruct ImGuiSettingsHandler\n{\n    const char* TypeName;       // Short description stored in .ini file. Disallowed characters: '[' ']'\n    ImGuiID     TypeHash;       // == ImHashStr(TypeName)\n    void        (*ClearAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);                                // Clear all settings data\n    void        (*ReadInitFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);                                // Read: Called before reading (in registration order)\n    void*       (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name);              // Read: Called when entering into a new ini entry e.g. \"[Window][Name]\"\n    void        (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry\n    void        (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);                                // Read: Called after reading (in registration order)\n    void        (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf);      // Write: Output every entries into 'out_buf'\n    void*       UserData;\n\n    ImGuiSettingsHandler() { memset((void*)this, 0, sizeof(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Localization support\n//-----------------------------------------------------------------------------\n\n// This is experimental and not officially supported, it'll probably fall short of features, if/when it does we may backtrack.\nenum ImGuiLocKey : int\n{\n    ImGuiLocKey_VersionStr,\n    ImGuiLocKey_TableSizeOne,\n    ImGuiLocKey_TableSizeAllFit,\n    ImGuiLocKey_TableSizeAllDefault,\n    ImGuiLocKey_TableResetOrder,\n    ImGuiLocKey_WindowingMainMenuBar,\n    ImGuiLocKey_WindowingPopup,\n    ImGuiLocKey_WindowingUntitled,\n    ImGuiLocKey_OpenLink_s,\n    ImGuiLocKey_CopyLink,\n    ImGuiLocKey_DockingHideTabBar,\n    ImGuiLocKey_DockingHoldShiftToDock,\n    ImGuiLocKey_DockingDragToUndockOrMoveNode,\n    ImGuiLocKey_COUNT\n};\n\nstruct ImGuiLocEntry\n{\n    ImGuiLocKey     Key;\n    const char*     Text;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Error handling, State recovery support\n//-----------------------------------------------------------------------------\n\n// Macros used by Recoverable Error handling\n// - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro).\n// - The message will always be a string literal, in order to increase likelihood of being display by an assert handler.\n// - See 'Demo->Configuration->Error Handling' and ImGuiIO definitions for details on error handling.\n// - Read https://github.com/ocornut/imgui/wiki/Error-Handling for details on error handling.\n#ifndef IM_ASSERT_USER_ERROR\n#define IM_ASSERT_USER_ERROR(_EXPR,_MSG)            do { if (!(_EXPR)) { if (ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } } while (0)               // Recoverable User Error\n#define IM_ASSERT_USER_ERROR_RET(_EXPR,_MSG)        do { if (!(_EXPR)) { if (ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } return; } } while (0)       // Recoverable User Error\n#define IM_ASSERT_USER_ERROR_RETV(_EXPR,_RETV,_MSG) do { if (!(_EXPR)) { if (ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } return _RETV; } } while (0) // Recoverable User Error\n#endif\n\n// The error callback is currently not public, as it is expected that only advanced users will rely on it.\ntypedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback\n\n//-----------------------------------------------------------------------------\n// [SECTION] Metrics, Debug Tools\n//-----------------------------------------------------------------------------\n\n// See IMGUI_DEBUG_LOG() and IMGUI_DEBUG_LOG_XXX() macros.\nenum ImGuiDebugLogFlags_\n{\n    // Event types\n    ImGuiDebugLogFlags_None                 = 0,\n    ImGuiDebugLogFlags_EventError           = 1 << 0,   // Error submitted by IM_ASSERT_USER_ERROR()\n    ImGuiDebugLogFlags_EventActiveId        = 1 << 1,\n    ImGuiDebugLogFlags_EventFocus           = 1 << 2,\n    ImGuiDebugLogFlags_EventPopup           = 1 << 3,\n    ImGuiDebugLogFlags_EventNav             = 1 << 4,\n    ImGuiDebugLogFlags_EventClipper         = 1 << 5,\n    ImGuiDebugLogFlags_EventSelection       = 1 << 6,\n    ImGuiDebugLogFlags_EventIO              = 1 << 7,\n    ImGuiDebugLogFlags_EventFont            = 1 << 8,\n    ImGuiDebugLogFlags_EventInputRouting    = 1 << 9,\n    ImGuiDebugLogFlags_EventDocking         = 1 << 10,\n    ImGuiDebugLogFlags_EventViewport        = 1 << 11,\n\n    ImGuiDebugLogFlags_EventMask_           = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventFont | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport,\n    ImGuiDebugLogFlags_OutputToTTY          = 1 << 20,  // Also send output to TTY\n    ImGuiDebugLogFlags_OutputToDebugger     = 1 << 21,  // Also send output to Debugger Console [Windows only]\n    ImGuiDebugLogFlags_OutputToTestEngine   = 1 << 22,  // Also send output to Dear ImGui Test Engine\n};\n\nstruct ImGuiDebugAllocEntry\n{\n    int         FrameCount;\n    ImS16       AllocCount;\n    ImS16       FreeCount;\n};\n\nstruct ImGuiDebugAllocInfo\n{\n    int         TotalAllocCount;            // Number of call to MemAlloc().\n    int         TotalFreeCount;\n    ImS16       LastEntriesIdx;             // Current index in buffer\n    ImGuiDebugAllocEntry LastEntriesBuf[6]; // Track last 6 frames that had allocations\n\n    ImGuiDebugAllocInfo() { memset((void*)this, 0, sizeof(*this)); }\n};\n\nstruct ImGuiMetricsConfig\n{\n    bool        ShowDebugLog = false;\n    bool        ShowIDStackTool = false;\n    bool        ShowWindowsRects = false;\n    bool        ShowWindowsBeginOrder = false;\n    bool        ShowTablesRects = false;\n    bool        ShowDrawCmdMesh = true;\n    bool        ShowDrawCmdBoundingBoxes = true;\n    bool        ShowTextEncodingViewer = false;\n    bool        ShowTextureUsedRect = false;\n    bool        ShowDockingNodes = false;\n    int         ShowWindowsRectsType = -1;\n    int         ShowTablesRectsType = -1;\n    int         HighlightMonitorIdx = -1;\n    ImGuiID     HighlightViewportID = 0;\n    bool        ShowFontPreview = true;\n};\n\nstruct ImGuiStackLevelInfo\n{\n    ImGuiID                 ID;\n    ImS8                    QueryFrameCount;            // >= 1: Sub-query in progress\n    bool                    QuerySuccess;               // Sub-query obtained result from DebugHookIdInfo()\n    ImS8                    DataType;                   // ImGuiDataType\n    int                     DescOffset;                 // -1 or offset into parent's ResultsPathsBuf\n\n    ImGuiStackLevelInfo()   { memset((void*)this, 0, sizeof(*this)); DataType = -1; DescOffset = -1; }\n};\n\nstruct ImGuiDebugItemPathQuery\n{\n    ImGuiID                 MainID;                     // ID to query details for.\n    bool                    Active;                     // Used to disambiguate the case when ID == 0 and e.g. some code calls PushOverrideID(0).\n    bool                    Complete;                   // All sub-queries are finished (some may have failed).\n    ImS8                    Step;                       // -1: query stack + init Results, >= 0: filling individual stack level.\n    ImVector<ImGuiStackLevelInfo> Results;\n    ImGuiTextBuffer         ResultsDescBuf;\n    ImGuiTextBuffer         ResultPathBuf;\n\n    ImGuiDebugItemPathQuery() { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// State for ID Stack tool queries\nstruct ImGuiIDStackTool\n{\n    bool                    OptHexEncodeNonAsciiChars;\n    bool                    OptCopyToClipboardOnCtrlC;\n    int                     LastActiveFrame;\n    float                   CopyToClipboardLastTime;\n\n    ImGuiIDStackTool()      { memset((void*)this, 0, sizeof(*this)); LastActiveFrame = -1; OptHexEncodeNonAsciiChars = true; CopyToClipboardLastTime = -FLT_MAX; }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Generic context hooks\n//-----------------------------------------------------------------------------\n\ntypedef void (*ImGuiContextHookCallback)(ImGuiContext* ctx, ImGuiContextHook* hook);\nenum ImGuiContextHookType { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown, ImGuiContextHookType_PendingRemoval_ };\n\nstruct ImGuiContextHook\n{\n    ImGuiID                     HookId;     // A unique ID assigned by AddContextHook()\n    ImGuiContextHookType        Type;\n    ImGuiID                     Owner;\n    ImGuiContextHookCallback    Callback;\n    void*                       UserData;\n\n    ImGuiContextHook()          { memset((void*)this, 0, sizeof(*this)); }\n};\n\ntypedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section);\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiContext (main Dear ImGui context)\n//-----------------------------------------------------------------------------\n\nstruct ImGuiContext\n{\n    bool                    Initialized;\n    bool                    WithinFrameScope;                   // Set by NewFrame(), cleared by EndFrame()\n    bool                    WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed\n    bool                    TestEngineHookItems;                // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log()\n    int                     FrameCount;\n    int                     FrameCountEnded;\n    int                     FrameCountPlatformEnded;\n    int                     FrameCountRendered;\n    double                  Time;\n    char                    ContextName[16];                    // Storage for a context name (to facilitate debugging multi-context setups)\n    ImGuiIO                 IO;\n    ImGuiPlatformIO         PlatformIO;\n    ImGuiStyle              Style;\n    ImGuiConfigFlags        ConfigFlagsCurrFrame;               // = g.IO.ConfigFlags at the time of NewFrame()\n    ImGuiConfigFlags        ConfigFlagsLastFrame;\n    ImVector<ImFontAtlas*>  FontAtlases;                        // List of font atlases used by the context (generally only contains g.IO.Fonts aka the main font atlas)\n    ImFont*                 Font;                               // Currently bound font. (== FontStack.back().Font)\n    ImFontBaked*            FontBaked;                          // Currently bound font at currently bound size. (== Font->GetFontBaked(FontSize))\n    float                   FontSize;                           // Currently bound font size == line height (== FontSizeBase + externals scales applied in the UpdateCurrentFontSize() function).\n    float                   FontSizeBase;                       // Font size before scaling == style.FontSizeBase == value passed to PushFont() when specified.\n    float                   FontBakedScale;                     // == FontBaked->Size / FontSize. Scale factor over baked size. Rarely used nowadays, very often == 1.0f.\n    float                   FontRasterizerDensity;              // Current font density. Used by all calls to GetFontBaked().\n    float                   CurrentDpiScale;                    // Current window/viewport DpiScale == CurrentViewport->DpiScale\n    ImDrawListSharedData    DrawListSharedData;\n    ImGuiID                 WithinEndChildID;                   // Set within EndChild()\n    void*                   TestEngine;                         // Test engine user data\n\n    // Inputs\n    ImVector<ImGuiInputEvent> InputEventsQueue;                 // Input events which will be trickled/written into IO structure.\n    ImVector<ImGuiInputEvent> InputEventsTrail;                 // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail.\n    ImGuiMouseSource        InputEventsNextMouseSource;\n    ImU32                   InputEventsNextEventId;\n\n    // Windows state\n    ImVector<ImGuiWindow*>  Windows;                            // Windows, sorted in display order, back to front\n    ImVector<ImGuiWindow*>  WindowsFocusOrder;                  // Root windows, sorted in focus order, back to front.\n    ImVector<ImGuiWindow*>  WindowsTempSortBuffer;              // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child\n    ImVector<ImGuiWindowStackData> CurrentWindowStack;\n    ImGuiStorage            WindowsById;                        // Map window's ImGuiID to ImGuiWindow*\n    int                     WindowsActiveCount;                 // Number of unique windows submitted by frame\n    float                   WindowsBorderHoverPadding;          // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, style.WindowBorderHoverPadding). This isn't so multi-dpi friendly.\n    ImGuiID                 DebugBreakInWindow;                 // Set to break in Begin() call.\n    ImGuiWindow*            CurrentWindow;                      // Window being drawn into\n    ImGuiWindow*            HoveredWindow;                      // Window the mouse is hovering. Will typically catch mouse inputs.\n    ImGuiWindow*            HoveredWindowUnderMovingWindow;     // Hovered window ignoring MovingWindow. Only set if MovingWindow is set.\n    ImGuiWindow*            HoveredWindowBeforeClear;           // Window the mouse is hovering. Filled even with _NoMouse. This is currently useful for multi-context compositors.\n    ImGuiWindow*            MovingWindow;                       // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindowDockTree.\n    ImGuiWindow*            WheelingWindow;                     // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window.\n    ImVec2                  WheelingWindowRefMousePos;\n    int                     WheelingWindowStartFrame;           // This may be set one frame before WheelingWindow is != NULL\n    int                     WheelingWindowScrolledFrame;\n    float                   WheelingWindowReleaseTimer;\n    ImVec2                  WheelingWindowWheelRemainder;\n    ImVec2                  WheelingAxisAvg;\n\n    // Item/widgets state and tracking information\n    ImGuiID                 DebugDrawIdConflictsId;             // Set when we detect multiple items with the same identifier\n    ImGuiID                 DebugHookIdInfoId;                  // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]\n    ImGuiID                 HoveredId;                          // Hovered widget, filled during the frame\n    ImGuiID                 HoveredIdPreviousFrame;\n    int                     HoveredIdPreviousFrameItemCount;    // Count numbers of items using the same ID as last frame's hovered id\n    float                   HoveredIdTimer;                     // Measure contiguous hovering time\n    float                   HoveredIdNotActiveTimer;            // Measure contiguous hovering time where the item has not been active\n    bool                    HoveredIdAllowOverlap;\n    bool                    HoveredIdIsDisabled;                // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0.\n    bool                    ItemUnclipByLog;                    // Disable ItemAdd() clipping, essentially a memory-locality friendly copy of LogEnabled\n    ImGuiID                 ActiveId;                           // Active widget\n    ImGuiID                 ActiveIdIsAlive;                    // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame)\n    float                   ActiveIdTimer;\n    bool                    ActiveIdIsJustActivated;            // Set at the time of activation for one frame\n    bool                    ActiveIdAllowOverlap;               // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always)\n    bool                    ActiveIdNoClearOnFocusLoss;         // Disable losing active id if the active id window gets unfocused.\n    bool                    ActiveIdHasBeenPressedBefore;       // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch.\n    bool                    ActiveIdHasBeenEditedBefore;        // Was the value associated to the widget Edited over the course of the Active state.\n    bool                    ActiveIdHasBeenEditedThisFrame;\n    bool                    ActiveIdFromShortcut;\n    ImS8                    ActiveIdMouseButton;\n    ImGuiID                 ActiveIdDisabledId;                 // When clicking a disabled item we set ActiveId=window->MoveId to avoid interference with widget code. Actual item ID is stored here.\n    ImVec2                  ActiveIdClickOffset;                // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)\n    ImGuiInputSource        ActiveIdSource;                     // Activating source: ImGuiInputSource_Mouse OR ImGuiInputSource_Keyboard OR ImGuiInputSource_Gamepad\n    ImGuiWindow*            ActiveIdWindow;\n    ImGuiID                 ActiveIdPreviousFrame;\n    ImGuiDeactivatedItemData DeactivatedItemData;\n    ImGuiDataTypeStorage    ActiveIdValueOnActivation;          // Backup of initial value at the time of activation. ONLY SET BY SPECIFIC WIDGETS: DragXXX and SliderXXX.\n    ImGuiID                 LastActiveId;                       // Store the last non-zero ActiveId, useful for animation.\n    float                   LastActiveIdTimer;                  // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.\n\n    // Key/Input Ownership + Shortcut Routing system\n    // - The idea is that instead of \"eating\" a given key, we can link to an owner.\n    // - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID.\n    // - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame().\n    double                  LastKeyModsChangeTime;              // Record the last time key mods changed (affect repeat delay when using shortcut logic)\n    double                  LastKeyModsChangeFromNoneTime;      // Record the last time key mods changed away from being 0 (affect repeat delay when using shortcut logic)\n    double                  LastKeyboardKeyPressTime;           // Record the last time a keyboard key (ignore mouse/gamepad ones) was pressed.\n    ImBitArrayForNamedKeys  KeysMayBeCharInput;                 // Lookup to tell if a key can emit char input, see IsKeyChordPotentiallyCharInput(). sizeof() = 20 bytes\n    ImGuiKeyOwnerData       KeysOwnerData[ImGuiKey_NamedKey_COUNT];\n    ImGuiKeyRoutingTable    KeysRoutingTable;\n    ImU32                   ActiveIdUsingNavDirMask;            // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)\n    bool                    ActiveIdUsingAllKeyboardKeys;       // Active widget will want to read all keyboard keys inputs. (this is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations)\n    ImGuiKeyChord           DebugBreakInShortcutRouting;        // Set to break in SetShortcutRouting()/Shortcut() calls.\n    //ImU32                 ActiveIdUsingNavInputMask;          // [OBSOLETE] Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes --> 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);'\n\n    // Next window/item data\n    ImGuiID                 CurrentFocusScopeId;                // Value for currently appending items == g.FocusScopeStack.back(). Not to be mistaken with g.NavFocusScopeId.\n    ImGuiItemFlags          CurrentItemFlags;                   // Value for currently appending items == g.ItemFlagsStack.back()\n    ImGuiID                 DebugLocateId;                      // Storage for DebugLocateItemOnHover() feature: this is read by ItemAdd() so we keep it in a hot/cached location\n    ImGuiNextItemData       NextItemData;                       // Storage for SetNextItem** functions\n    ImGuiLastItemData       LastItemData;                       // Storage for last submitted item (setup by ItemAdd)\n    ImGuiNextWindowData     NextWindowData;                     // Storage for SetNextWindow** functions\n    bool                    DebugShowGroupRects;\n    bool                    GcCompactAll;                       // Request full GC\n\n    // Shared stacks\n    ImGuiCol                        DebugFlashStyleColorIdx;    // (Keep close to ColorStack to share cache line)\n    ImVector<ImGuiColorMod>         ColorStack;                 // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()\n    ImVector<ImGuiStyleMod>         StyleVarStack;              // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()\n    ImVector<ImFontStackData>       FontStack;                  // Stack for PushFont()/PopFont() - inherited by Begin()\n    ImVector<ImGuiFocusScopeData>   FocusScopeStack;            // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin()\n    ImVector<ImGuiItemFlags>        ItemFlagsStack;             // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()\n    ImVector<ImGuiGroupData>        GroupStack;                 // Stack for BeginGroup()/EndGroup() - not inherited by Begin()\n    ImVector<ImGuiPopupData>        OpenPopupStack;             // Which popups are open (persistent)\n    ImVector<ImGuiPopupData>        BeginPopupStack;            // Which level of BeginPopup() we are in (reset every frame)\n    ImVector<ImGuiTreeNodeStackData>TreeNodeStack;              // Stack for TreeNode()\n\n    // Viewports\n    ImVector<ImGuiViewportP*> Viewports;                        // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData.\n    ImGuiViewportP*         CurrentViewport;                    // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport()\n    ImGuiViewportP*         MouseViewport;\n    ImGuiViewportP*         MouseLastHoveredViewport;           // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag.\n    ImGuiID                 PlatformLastFocusedViewportId;\n    ImGuiPlatformMonitor    FallbackMonitor;                    // Virtual monitor used as fallback if backend doesn't provide monitor information.\n    ImRect                  PlatformMonitorsFullWorkRect;       // Bounding box of all platform monitors\n    int                     ViewportCreatedCount;               // Unique sequential creation counter (mostly for testing/debugging)\n    int                     PlatformWindowsCreatedCount;        // Unique sequential creation counter (mostly for testing/debugging)\n    int                     ViewportFocusedStampCount;          // Every time the front-most window changes, we stamp its viewport with an incrementing counter\n\n    // Keyboard/Gamepad Navigation\n    bool                    NavCursorVisible;                   // Nav focus cursor/rectangle is visible? We hide it after a mouse click. We show it after a nav move.\n    bool                    NavHighlightItemUnderNav;           // Disable mouse hovering highlight. Highlight navigation focused item instead of mouse hovered item.\n    //bool                  NavDisableHighlight;                // Old name for !g.NavCursorVisible before 1.91.4 (2024/10/18). OPPOSITE VALUE (g.NavDisableHighlight == !g.NavCursorVisible)\n    //bool                  NavDisableMouseHover;               // Old name for g.NavHighlightItemUnderNav before 1.91.1 (2024/10/18) this was called When user starts using keyboard/gamepad, we hide mouse hovering highlight until mouse is touched again.\n    bool                    NavMousePosDirty;                   // When set we will update mouse position if io.ConfigNavMoveSetMousePos is set (not enabled by default)\n    bool                    NavIdIsAlive;                       // Nav widget has been seen this frame ~~ NavRectRel is valid\n    ImGuiID                 NavId;                              // Focused item for navigation\n    ImGuiWindow*            NavWindow;                          // Focused window for navigation. Could be called 'FocusedWindow'\n    ImGuiID                 NavFocusScopeId;                    // Focused focus scope (e.g. selection code often wants to \"clear other items\" when landing on an item of the same scope)\n    ImGuiNavLayer           NavLayer;                           // Focused layer (main scrolling layer, or menu/title bar layer)\n    ImGuiItemFlags          NavIdItemFlags;\n    ImGuiID                 NavActivateId;                      // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItemByID()\n    ImGuiID                 NavActivateDownId;                  // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0\n    ImGuiID                 NavActivatePressedId;               // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat)\n    ImGuiActivateFlags      NavActivateFlags;\n    ImVector<ImGuiFocusScopeData> NavFocusRoute;                // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain.\n    ImGuiID                 NavHighlightActivatedId;\n    float                   NavHighlightActivatedTimer;\n    ImGuiID                 NavOpenContextMenuItemId;\n    ImGuiID                 NavOpenContextMenuWindowId;\n    ImGuiID                 NavNextActivateId;                  // Set by ActivateItemByID(), queued until next frame.\n    ImGuiActivateFlags      NavNextActivateFlags;\n    ImGuiInputSource        NavInputSource;                     // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Gamepad\n    ImGuiSelectionUserData  NavLastValidSelectionUserData;      // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.\n    ImS8                    NavCursorHideFrames;\n    //ImGuiID               NavActivateInputId;                 // Removed in 1.89.4 (July 2023). This is now part of g.NavActivateId and sets g.NavActivateFlags |= ImGuiActivateFlags_PreferInput. See commit c9a53aa74, issue #5606.\n\n    // Navigation: Init & Move Requests\n    bool                    NavAnyRequest;                      // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd()\n    bool                    NavInitRequest;                     // Init request for appearing window to select first item\n    bool                    NavInitRequestFromMove;\n    ImGuiNavItemData        NavInitResult;                      // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called)\n    bool                    NavMoveSubmitted;                   // Move request submitted, will process result on next NewFrame()\n    bool                    NavMoveScoringItems;                // Move request submitted, still scoring incoming items\n    bool                    NavMoveForwardToNextFrame;\n    ImGuiNavMoveFlags       NavMoveFlags;\n    ImGuiScrollFlags        NavMoveScrollFlags;\n    ImGuiKeyChord           NavMoveKeyMods;\n    ImGuiDir                NavMoveDir;                         // Direction of the move request (left/right/up/down)\n    ImGuiDir                NavMoveDirForDebug;\n    ImGuiDir                NavMoveClipDir;                     // FIXME-NAV: Describe the purpose of this better. Might want to rename?\n    ImRect                  NavScoringRect;                     // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring.\n    ImRect                  NavScoringNoClipRect;               // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted. Unset/invalid if inverted.\n    int                     NavScoringDebugCount;               // Metrics for debugging\n    int                     NavTabbingDir;                      // Generally -1 or +1, 0 when tabbing without a nav id\n    int                     NavTabbingCounter;                  // >0 when counting items for tabbing\n    ImGuiNavItemData        NavMoveResultLocal;                 // Best move request candidate within NavWindow\n    ImGuiNavItemData        NavMoveResultLocalVisible;          // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag)\n    ImGuiNavItemData        NavMoveResultOther;                 // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag)\n    ImGuiNavItemData        NavTabbingResultFirst;              // First tabbing request candidate within NavWindow and flattened hierarchy\n\n    // Navigation: record of last move request\n    ImGuiID                 NavJustMovedFromFocusScopeId;       // Just navigated from this focus scope id (result of a successfully MoveRequest).\n    ImGuiID                 NavJustMovedToId;                   // Just navigated to this id (result of a successfully MoveRequest).\n    ImGuiID                 NavJustMovedToFocusScopeId;         // Just navigated to this focus scope id (result of a successfully MoveRequest).\n    ImGuiKeyChord           NavJustMovedToKeyMods;\n    bool                    NavJustMovedToIsTabbing;            // Copy of ImGuiNavMoveFlags_IsTabbing. Maybe we should store whole flags.\n    bool                    NavJustMovedToHasSelectionData;     // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData.\n\n    // Navigation: extra config options (will be made public eventually)\n    // - Tabbing (Tab, Shift+Tab) and Windowing (Ctrl+Tab, Ctrl+Shift+Tab) are enabled REGARDLESS of ImGuiConfigFlags_NavEnableKeyboard being set.\n    // - Ctrl+Tab is reconfigurable because it is the only shortcut that may be polled when no window are focused. It also doesn't work e.g. Web platforms.\n    bool                    ConfigNavEnableTabbing;             // = true. Enable tabbing (Tab, Shift+Tab). PLEASE LET ME KNOW IF YOU USE THIS.\n    bool                    ConfigNavWindowingWithGamepad;      // = true. Enable Ctrl+Tab by holding ImGuiKey_GamepadFaceLeft (== ImGuiKey_NavGamepadMenu). When false, the button may still be used to toggle Menu layer.\n    ImGuiKeyChord           ConfigNavWindowingKeyNext;          // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). Set to 0 to disable. For reconfiguration (see #4828)\n    ImGuiKeyChord           ConfigNavWindowingKeyPrev;          // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X)\n\n    // Navigation: Windowing (Ctrl+Tab for list, or Menu button + keys or directional pads to move/resize)\n    ImGuiWindow*            NavWindowingTarget;                 // Target window when doing Ctrl+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!\n    ImGuiWindow*            NavWindowingTargetAnim;             // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it.\n    ImGuiWindow*            NavWindowingListWindow;             // Internal window actually listing the Ctrl+Tab contents\n    float                   NavWindowingTimer;\n    float                   NavWindowingHighlightAlpha;\n    ImGuiInputSource        NavWindowingInputSource;\n    bool                    NavWindowingToggleLayer;            // Set while Alt or GamepadMenu is held, may be cleared by other operations, and processed when releasing the key.\n    ImGuiKey                NavWindowingToggleKey;              // Keyboard/gamepad key used when toggling to menu layer.\n    ImVec2                  NavWindowingAccumDeltaPos;\n    ImVec2                  NavWindowingAccumDeltaSize;\n\n    // Render\n    float                   DimBgRatio;                         // 0.0..1.0 animation when fading in a dimming background (for modal window and Ctrl+Tab list)\n\n    // Drag and Drop\n    bool                    DragDropActive;\n    bool                    DragDropWithinSource;               // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag source.\n    bool                    DragDropWithinTarget;               // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag target.\n    ImGuiDragDropFlags      DragDropSourceFlags;\n    int                     DragDropSourceFrameCount;\n    int                     DragDropMouseButton;\n    ImGuiPayload            DragDropPayload;\n    ImRect                  DragDropTargetRect;                 // Store rectangle of current target candidate (we favor small targets when overlapping)\n    ImRect                  DragDropTargetClipRect;             // Store ClipRect at the time of item's drawing\n    ImGuiID                 DragDropTargetId;\n    ImGuiID                 DragDropTargetFullViewport;\n    ImGuiDragDropFlags      DragDropAcceptFlagsCurr;\n    ImGuiDragDropFlags      DragDropAcceptFlagsPrev;\n    float                   DragDropAcceptIdCurrRectSurface;    // Target item surface (we resolve overlapping targets by prioritizing the smaller surface)\n    ImGuiID                 DragDropAcceptIdCurr;               // Target item id (set at the time of accepting the payload)\n    ImGuiID                 DragDropAcceptIdPrev;               // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets)\n    int                     DragDropAcceptFrameCount;           // Last time a target expressed a desire to accept the source\n    ImGuiID                 DragDropHoldJustPressedId;          // Set when holding a payload just made ButtonBehavior() return a press.\n    ImVector<unsigned char> DragDropPayloadBufHeap;             // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size\n    unsigned char           DragDropPayloadBufLocal[16];        // Local buffer for small payloads\n\n    // Clipper\n    int                             ClipperTempDataStacked;\n    ImVector<ImGuiListClipperData>  ClipperTempData;\n\n    // Tables\n    ImGuiTable*                     CurrentTable;\n    ImGuiID                         DebugBreakInTable;          // Set to break in BeginTable() call.\n    int                             TablesTempDataStacked;      // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size)\n    ImVector<ImGuiTableTempData>    TablesTempData;             // Temporary table data (buffers reused/shared across instances, support nesting)\n    ImPool<ImGuiTable>              Tables;                     // Persistent table data\n    ImVector<float>                 TablesLastTimeActive;       // Last used timestamp of each tables (SOA, for efficient GC)\n    ImVector<ImDrawChannel>         DrawChannelsTempMergeBuffer;\n\n    // Tab bars\n    ImGuiTabBar*                    CurrentTabBar;\n    ImPool<ImGuiTabBar>             TabBars;\n    ImVector<ImGuiPtrOrIndex>       CurrentTabBarStack;\n    ImVector<ImGuiShrinkWidthItem>  ShrinkWidthBuffer;\n\n    // Multi-Select state\n    ImGuiBoxSelectState             BoxSelectState;\n    ImGuiMultiSelectTempData*       CurrentMultiSelect;\n    int                             MultiSelectTempDataStacked; // Temporary multi-select data size (because we leave previous instances undestructed, we generally don't use MultiSelectTempData.Size)\n    ImVector<ImGuiMultiSelectTempData> MultiSelectTempData;\n    ImPool<ImGuiMultiSelectState>   MultiSelectStorage;\n\n    // Hover Delay system\n    ImGuiID                 HoverItemDelayId;\n    ImGuiID                 HoverItemDelayIdPreviousFrame;\n    float                   HoverItemDelayTimer;                // Currently used by IsItemHovered()\n    float                   HoverItemDelayClearTimer;           // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared.\n    ImGuiID                 HoverItemUnlockedStationaryId;      // Mouse has once been stationary on this item. Only reset after departing the item.\n    ImGuiID                 HoverWindowUnlockedStationaryId;    // Mouse has once been stationary on this window. Only reset after departing the window.\n\n    // Mouse state\n    ImGuiMouseCursor        MouseCursor;\n    float                   MouseStationaryTimer;               // Time the mouse has been stationary (with some loose heuristic)\n    ImVec2                  MouseLastValidPos;\n\n    // Widget state\n    ImGuiInputTextState     InputTextState;\n    ImGuiTextIndex          InputTextLineIndex;                 // Temporary storage\n    ImGuiInputTextDeactivatedState InputTextDeactivatedState;\n    ImFontBaked             InputTextPasswordFontBackupBaked;\n    ImFontFlags             InputTextPasswordFontBackupFlags;\n    ImGuiID                 InputTextReactivateId;              // ID of InputText to reactivate on next frame (for io.ConfigInputTextEnterKeepActive behavior)\n    ImGuiID                 TempInputId;                        // Temporary text input when using Ctrl+Click on a slider, etc.\n    ImGuiDataTypeStorage    DataTypeZeroValue;                  // 0 for all data types\n    int                     BeginMenuDepth;\n    int                     BeginComboDepth;\n    ImGuiColorEditFlags     ColorEditOptions;                   // Store user options for color edit widgets\n    ImGuiID                 ColorEditCurrentID;                 // Set temporarily while inside of the parent-most ColorEdit4/ColorPicker4 (because they call each others).\n    ImGuiID                 ColorEditSavedID;                   // ID we are saving/restoring HS for\n    float                   ColorEditSavedHue;                  // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips\n    float                   ColorEditSavedSat;                  // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips\n    ImU32                   ColorEditSavedColor;                // RGB value with alpha set to 0.\n    ImVec4                  ColorPickerRef;                     // Initial/reference color at the time of opening the color picker.\n    ImGuiComboPreviewData   ComboPreviewData;\n    ImRect                  WindowResizeBorderExpectedRect;     // Expected border rect, switch to relative edit if moving\n    bool                    WindowResizeRelativeMode;\n    short                   ScrollbarSeekMode;                  // 0: scroll to clicked location, -1/+1: prev/next page.\n    float                   ScrollbarClickDeltaToGrabCenter;    // When scrolling to mouse location: distance between mouse and center of grab box, normalized in parent space.\n    float                   SliderGrabClickOffset;\n    float                   SliderCurrentAccum;                 // Accumulated slider delta when using navigation controls.\n    bool                    SliderCurrentAccumDirty;            // Has the accumulated slider delta changed since last time we tried to apply it?\n    bool                    DragCurrentAccumDirty;\n    float                   DragCurrentAccum;                   // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings\n    float                   DragSpeedDefaultRatio;              // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio\n    float                   DisabledAlphaBackup;                // Backup for style.Alpha for BeginDisabled()\n    short                   DisabledStackSize;\n    short                   TooltipOverrideCount;\n    ImGuiWindow*            TooltipPreviousWindow;              // Window of last tooltip submitted during the frame\n    ImVector<char>          ClipboardHandlerData;               // If no custom clipboard handler is defined\n    ImVector<ImGuiID>       MenusIdSubmittedThisFrame;          // A list of menu IDs that were rendered at least once\n    ImGuiTypingSelectState  TypingSelectState;                  // State for GetTypingSelectRequest()\n\n    // Platform support\n    ImGuiPlatformImeData    PlatformImeData;                    // Data updated by current frame. Will be applied at end of the frame. For some backends, this is required to have WantVisible=true in order to receive text message.\n    ImGuiPlatformImeData    PlatformImeDataPrev;                // Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler.\n\n    // Extensions\n    // FIXME: We could provide an API to register one slot in an array held in ImGuiContext?\n    ImVector<ImTextureData*> UserTextures;                      // List of textures created/managed by user or third-party extension. Automatically appended into platform_io.Textures[].\n    ImGuiDockContext        DockContext;\n    void                    (*DockNodeWindowMenuHandler)(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar);\n\n    // Settings\n    bool                    SettingsLoaded;\n    float                   SettingsDirtyTimer;                 // Save .ini Settings to memory when time reaches zero\n    ImGuiTextBuffer         SettingsIniData;                    // In memory .ini settings\n    ImVector<ImGuiSettingsHandler>      SettingsHandlers;       // List of .ini settings handlers\n    ImChunkStream<ImGuiWindowSettings>  SettingsWindows;        // ImGuiWindow .ini settings entries\n    ImChunkStream<ImGuiTableSettings>   SettingsTables;         // ImGuiTable .ini settings entries\n\n    // Hooks\n    ImVector<ImGuiContextHook>          Hooks;                  // Hooks for extensions (e.g. test engine)\n    ImGuiID                             HookIdNext;             // Next available HookId\n    ImGuiDemoMarkerCallback             DemoMarkerCallback;\n\n    // Localization\n    const char*             LocalizationTable[ImGuiLocKey_COUNT];\n\n    // Capture/Logging\n    bool                    LogEnabled;                         // Currently capturing\n    bool                    LogLineFirstItem;\n    ImGuiLogFlags           LogFlags;                           // Capture flags/type\n    ImGuiWindow*            LogWindow;\n    ImFileHandle            LogFile;                            // If != NULL log to stdout/ file\n    ImGuiTextBuffer         LogBuffer;                          // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators.\n    const char*             LogNextPrefix;                      // See comment in LogSetNextTextDecoration(): doesn't copy underlying data, use carefully!\n    const char*             LogNextSuffix;\n    float                   LogLinePosY;\n    int                     LogDepthRef;\n    int                     LogDepthToExpand;\n    int                     LogDepthToExpandDefault;            // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.\n\n    // Error Handling\n    ImGuiErrorCallback      ErrorCallback;                      // = NULL. May be exposed in public API eventually.\n    void*                   ErrorCallbackUserData;              // = NULL\n    ImVec2                  ErrorTooltipLockedPos;\n    bool                    ErrorFirst;\n    int                     ErrorCountCurrentFrame;             // [Internal] Number of errors submitted this frame.\n    ImGuiErrorRecoveryState StackSizesInNewFrame;               // [Internal]\n    ImGuiErrorRecoveryState*StackSizesInBeginForCurrentWindow;  // [Internal]\n\n    // Debug Tools\n    // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.)\n    int                     DebugDrawIdConflictsCount;          // Locked count (preserved when holding Ctrl)\n    ImGuiDebugLogFlags      DebugLogFlags;\n    ImGuiTextBuffer         DebugLogBuf;\n    ImGuiTextIndex          DebugLogIndex;\n    int                     DebugLogSkippedErrors;\n    ImGuiDebugLogFlags      DebugLogAutoDisableFlags;\n    ImU8                    DebugLogAutoDisableFrames;\n    ImU8                    DebugLocateFrames;                  // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above.\n    bool                    DebugBreakInLocateId;               // Debug break in ItemAdd() call for g.DebugLocateId.\n    ImGuiKeyChord           DebugBreakKeyChord;                 // = ImGuiKey_Pause\n    ImS8                    DebugBeginReturnValueCullDepth;     // Cycle between 0..9 then wrap around.\n    bool                    DebugItemPickerActive;              // Item picker is active (started with DebugStartItemPicker())\n    ImU8                    DebugItemPickerMouseButton;\n    ImGuiID                 DebugItemPickerBreakId;             // Will call IM_DEBUG_BREAK() when encountering this ID\n    float                   DebugFlashStyleColorTime;\n    ImVec4                  DebugFlashStyleColorBackup;\n    ImGuiMetricsConfig      DebugMetricsConfig;\n    ImGuiDebugItemPathQuery DebugItemPathQuery;\n    ImGuiIDStackTool        DebugIDStackTool;\n    ImGuiDebugAllocInfo     DebugAllocInfo;\n    ImGuiDockNode*          DebugHoveredDockNode;               // Hovered dock node.\n#if defined(IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS) && !defined(IMGUI_DISABLE_DEBUG_TOOLS)\n    ImGuiStorage            DebugDrawIdConflictsAliveCount;\n    ImGuiStorage            DebugDrawIdConflictsHighlightSet;\n#endif\n\n    // Misc\n    float                   FramerateSecPerFrame[60];           // Calculate estimate of framerate for user over the last 60 frames..\n    int                     FramerateSecPerFrameIdx;\n    int                     FramerateSecPerFrameCount;\n    float                   FramerateSecPerFrameAccum;\n    int                     WantCaptureMouseNextFrame;          // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1.\n    int                     WantCaptureKeyboardNextFrame;       // \"\n    int                     WantTextInputNextFrame;             // Copied in EndFrame() from g.PlatformImeData.WantTextInput. Needs to be set for some backends (SDL3) to emit character inputs.\n    ImVector<char>          TempBuffer;                         // Temporary text buffer\n    char                    TempKeychordName[64];\n\n    ImGuiContext(ImFontAtlas* shared_font_atlas);\n    ~ImGuiContext();\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiWindowTempData, ImGuiWindow\n//-----------------------------------------------------------------------------\n\n#define IMGUI_WINDOW_HARD_MIN_SIZE 4.0f\n\n// Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow.\n// (That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered..)\n// (This doesn't need a constructor because we zero-clear it as part of ImGuiWindow and all frame-temporary data are setup on Begin)\nstruct IMGUI_API ImGuiWindowTempData\n{\n    // Layout\n    ImVec2                  CursorPos;              // Current emitting position, in absolute coordinates.\n    ImVec2                  CursorPosPrevLine;\n    ImVec2                  CursorStartPos;         // Initial position after Begin(), generally ~ window position + WindowPadding.\n    ImVec2                  CursorMaxPos;           // Used to implicitly calculate ContentSize at the beginning of next frame, for scrolling range and auto-resize. Always growing during the frame.\n    ImVec2                  IdealMaxPos;            // Used to implicitly calculate ContentSizeIdeal at the beginning of next frame, for auto-resize only. Always growing during the frame.\n    ImVec2                  CurrLineSize;\n    ImVec2                  PrevLineSize;\n    float                   CurrLineTextBaseOffset; // Baseline offset (0.0f by default on a new line, generally == style.FramePadding.y when a framed item has been added).\n    float                   PrevLineTextBaseOffset;\n    bool                    IsSameLine;\n    bool                    IsSetPos;\n    ImVec1                  Indent;                 // Indentation / start position from left of window (increased by TreePush/TreePop, etc.)\n    ImVec1                  ColumnsOffset;          // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API.\n    ImVec1                  GroupOffset;\n    ImVec2                  CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensate and fix the most common use case of large scroll area.\n\n    // Keyboard/Gamepad navigation\n    ImGuiNavLayer           NavLayerCurrent;        // Current layer, 0..31 (we currently only use 0..1)\n    short                   NavLayersActiveMask;    // Which layers have been written to (result from previous frame)\n    short                   NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame)\n    bool                    NavIsScrollPushableX;   // Set when current work location may be scrolled horizontally when moving left / right. This is generally always true UNLESS within a column.\n    bool                    NavHideHighlightOneFrame;\n    bool                    NavWindowHasScrollY;    // Set per window when scrolling can be used (== ScrollMax.y > 0.0f)\n\n    // Miscellaneous\n    bool                    MenuBarAppending;       // FIXME: Remove this\n    ImVec2                  MenuBarOffset;          // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs.\n    ImGuiMenuColumns        MenuColumns;            // Simplified columns storage for menu items measurement\n    int                     TreeDepth;              // Current tree depth.\n    ImU32                   TreeHasStackDataDepthMask;      // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary.\n    ImU32                   TreeRecordsClippedNodesY2Mask;  // Store whether we should keep recording Y2. Cleared when passing clip max. Equivalent TreeHasStackDataDepthMask value should always be set.\n    ImVector<ImGuiWindow*>  ChildWindows;\n    ImGuiStorage*           StateStorage;           // Current persistent per-window storage (store e.g. tree node open/close state)\n    ImGuiOldColumns*        CurrentColumns;         // Current columns set\n    int                     CurrentTableIdx;        // Current table index (into g.Tables)\n    ImGuiLayoutType         LayoutType;\n    ImGuiLayoutType         ParentLayoutType;       // Layout type of parent window at the time of Begin()\n    ImU32                   ModalDimBgColor;\n\n    // Status flags\n    ImGuiItemStatusFlags    WindowItemStatusFlags;\n    ImGuiItemStatusFlags    ChildItemStatusFlags;\n    ImGuiItemStatusFlags    DockTabItemStatusFlags;\n    ImRect                  DockTabItemRect;\n\n    // Local parameters stacks\n    // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings.\n    float                   ItemWidth;              // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window).\n    float                   ItemWidthDefault;\n    float                   TextWrapPos;            // Current text wrap pos.\n    ImVector<float>         ItemWidthStack;         // Store item widths to restore (attention: .back() is not == ItemWidth)\n    ImVector<float>         TextWrapPosStack;       // Store text wrap pos to restore (attention: .back() is not == TextWrapPos)\n};\n\n// Storage for one window\nstruct IMGUI_API ImGuiWindow\n{\n    ImGuiContext*           Ctx;                                // Parent UI context (needs to be set explicitly by parent).\n    char*                   Name;                               // Window name, owned by the window.\n    ImGuiID                 ID;                                 // == ImHashStr(Name)\n    ImGuiWindowFlags        Flags, FlagsPreviousFrame;          // See enum ImGuiWindowFlags_\n    ImGuiChildFlags         ChildFlags;                         // Set when window is a child window. See enum ImGuiChildFlags_\n    ImGuiWindowClass        WindowClass;                        // Advanced users only. Set with SetNextWindowClass()\n    ImGuiViewportP*         Viewport;                           // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded.\n    ImGuiID                 ViewportId;                         // We backup the viewport id (since the viewport may disappear or never be created if the window is inactive)\n    ImVec2                  ViewportPos;                        // We backup the viewport position (since the viewport may disappear or never be created if the window is inactive)\n    int                     ViewportAllowPlatformMonitorExtend; // Reset to -1 every frame (index is guaranteed to be valid between NewFrame..EndFrame), only used in the Appearing frame of a tooltip/popup to enforce clamping to a given monitor\n    ImVec2                  Pos;                                // Position (always rounded-up to nearest pixel)\n    ImVec2                  Size;                               // Current size (==SizeFull or collapsed title bar size)\n    ImVec2                  SizeFull;                           // Size when non collapsed\n    ImVec2                  ContentSize;                        // Size of contents/scrollable client area (calculated from the extents reach of the cursor) from previous frame. Does not include window decoration or window padding.\n    ImVec2                  ContentSizeIdeal;\n    ImVec2                  ContentSizeExplicit;                // Size of contents/scrollable client area explicitly request by the user via SetNextWindowContentSize().\n    ImVec2                  WindowPadding;                      // Window padding at the time of Begin().\n    float                   WindowRounding;                     // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc.\n    float                   WindowBorderSize;                   // Window border size at the time of Begin().\n    float                   TitleBarHeight, MenuBarHeight;      // Note that those used to be function before 2024/05/28. If you have old code calling TitleBarHeight() you can change it to TitleBarHeight.\n    float                   DecoOuterSizeX1, DecoOuterSizeY1;   // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin().\n    float                   DecoOuterSizeX2, DecoOuterSizeY2;   // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y).\n    float                   DecoInnerSizeX1, DecoInnerSizeY1;   // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes).\n    int                     NameBufLen;                         // Size of buffer storing Name. May be larger than strlen(Name)!\n    ImGuiID                 MoveId;                             // == window->GetID(\"#MOVE\")\n    ImGuiID                 TabId;                              // == window->GetID(\"#TAB\")\n    ImGuiID                 ChildId;                            // ID of corresponding item in parent window (for navigation to return from child window to parent window)\n    ImGuiID                 PopupId;                            // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling)\n    ImVec2                  Scroll;\n    ImVec2                  ScrollMax;\n    ImVec2                  ScrollTarget;                       // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change)\n    ImVec2                  ScrollTargetCenterRatio;            // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered\n    ImVec2                  ScrollTargetEdgeSnapDist;           // 0.0f = no snapping, >0.0f snapping threshold\n    ImVec2                  ScrollbarSizes;                     // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar.\n    bool                    ScrollbarX, ScrollbarY;             // Are scrollbars visible?\n    bool                    ScrollbarXStabilizeEnabled;         // Was ScrollbarX previously auto-stabilized?\n    ImU8                    ScrollbarXStabilizeToggledHistory;  // Used to stabilize scrollbar visibility in case of feedback loops\n    bool                    ViewportOwned;\n    bool                    Active;                             // Set to true on Begin(), unless Collapsed\n    bool                    WasActive;\n    bool                    WriteAccessed;                      // Set to true when any widget access the current window\n    bool                    Collapsed;                          // Set when collapsing window to become only title-bar\n    bool                    WantCollapseToggle;\n    bool                    SkipItems;                          // Set when items can safely be all clipped (e.g. window not visible or collapsed)\n    bool                    SkipRefresh;                        // [EXPERIMENTAL] Reuse previous frame drawn contents, Begin() returns false.\n    bool                    Appearing;                          // Set during the frame where the window is appearing (or re-appearing)\n    bool                    Hidden;                             // Do not display (== HiddenFrames*** > 0)\n    bool                    IsFallbackWindow;                   // Set on the \"Debug##Default\" window.\n    bool                    IsExplicitChild;                    // Set when passed _ChildWindow, left to false by BeginDocked()\n    bool                    HasCloseButton;                     // Set when the window has a close button (p_open != NULL)\n    signed char             ResizeBorderHovered;                // Current border being hovered for resize (-1: none, otherwise 0-3)\n    signed char             ResizeBorderHeld;                   // Current border being held for resize (-1: none, otherwise 0-3)\n    short                   BeginCount;                         // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)\n    short                   BeginCountPreviousFrame;            // Number of Begin() during the previous frame\n    short                   BeginOrderWithinParent;             // Begin() order within immediate parent window, if we are a child window. Otherwise 0.\n    short                   BeginOrderWithinContext;            // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues.\n    short                   FocusOrder;                         // Order within WindowsFocusOrder[], altered when windows are focused.\n    ImGuiDir                AutoPosLastDirection;\n    ImS8                    AutoFitFramesX, AutoFitFramesY;\n    bool                    AutoFitOnlyGrows;\n    ImS8                    HiddenFramesCanSkipItems;           // Hide the window for N frames\n    ImS8                    HiddenFramesCannotSkipItems;        // Hide the window for N frames while allowing items to be submitted so we can measure their size\n    ImS8                    HiddenFramesForRenderOnly;          // Hide the window until frame N at Render() time only\n    ImS8                    DisableInputsFrames;                // Disable window interactions for N frames\n    ImGuiWindowBgClickFlags BgClickFlags : 8;                   // Configure behavior of click+dragging on window bg/void or over items. Default sets by io.ConfigWindowsMoveFromTitleBarOnly. If you use this please report in #3379.\n    ImGuiCond               SetWindowPosAllowFlags : 8;         // store acceptable condition flags for SetNextWindowPos() use.\n    ImGuiCond               SetWindowSizeAllowFlags : 8;        // store acceptable condition flags for SetNextWindowSize() use.\n    ImGuiCond               SetWindowCollapsedAllowFlags : 8;   // store acceptable condition flags for SetNextWindowCollapsed() use.\n    ImGuiCond               SetWindowDockAllowFlags : 8;        // store acceptable condition flags for SetNextWindowDock() use.\n    ImVec2                  SetWindowPosVal;                    // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size)\n    ImVec2                  SetWindowPosPivot;                  // store window pivot for positioning. ImVec2(0, 0) when positioning from top-left corner; ImVec2(0.5f, 0.5f) for centering; ImVec2(1, 1) for bottom right.\n\n    ImVector<ImGuiID>       IDStack;                            // ID stack. ID are hashes seeded with the value at the top of the stack. (In theory this should be in the TempData structure)\n    ImGuiWindowTempData     DC;                                 // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the \"DC\" variable name.\n\n    // The best way to understand what those rectangles are is to use the 'Metrics->Tools->Show Windows Rectangles' viewer.\n    // The main 'OuterRect', omitted as a field, is window->Rect().\n    ImRect                  OuterRectClipped;                   // == Window->Rect() just after setup in Begin(). == window->Rect() for root window.\n    ImRect                  InnerRect;                          // Inner rectangle (omit title bar, menu bar, scroll bar)\n    ImRect                  InnerClipRect;                      // == InnerRect shrunk by WindowPadding*0.5f on each side, clipped within viewport or parent clip rect.\n    ImRect                  WorkRect;                           // Initially covers the whole scrolling region. Reduced by containers e.g columns/tables when active. Shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentRegionRect over time (from 1.71+ onward).\n    ImRect                  ParentWorkRect;                     // Backup of WorkRect before entering a container such as columns/tables. Used by e.g. SpanAllColumns functions to easily access. Stacked containers are responsible for maintaining this. // FIXME-WORKRECT: Could be a stack?\n    ImRect                  ClipRect;                           // Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList->clip_rect_stack.back().\n    ImRect                  ContentRegionRect;                  // FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on.\n    ImVec2ih                HitTestHoleSize;                    // Define an optional rectangular hole where mouse will pass-through the window.\n    ImVec2ih                HitTestHoleOffset;\n\n    int                     LastFrameActive;                    // Last frame number the window was Active.\n    int                     LastFrameJustFocused;               // Last frame number the window was made Focused.\n    float                   LastTimeActive;                     // Last timestamp the window was Active (using float as we don't need high precision there)\n    ImGuiStorage            StateStorage;\n    ImVector<ImGuiOldColumns> ColumnsStorage;\n    float                   FontWindowScale;                    // User scale multiplier per-window, via SetWindowFontScale()\n    float                   FontWindowScaleParents;\n    float                   FontRefSize;                        // This is a copy of window->CalcFontSize() at the time of Begin(), trying to phase out CalcFontSize() especially as it may be called on non-current window.\n    int                     SettingsOffset;                     // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back)\n\n    ImDrawList*             DrawList;                           // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer)\n    ImDrawList              DrawListInst;\n    ImGuiWindow*            ParentWindow;                       // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL.\n    ImGuiWindow*            ParentWindowInBeginStack;\n    ImGuiWindow*            RootWindow;                         // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes.\n    ImGuiWindow*            RootWindowPopupTree;                // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child.\n    ImGuiWindow*            RootWindowDockTree;                 // Point to ourself or first ancestor that is not a child window. Cross through dock nodes.\n    ImGuiWindow*            RootWindowForTitleBarHighlight;     // Point to ourself or first ancestor which will display TitleBgActive color when this window is active.\n    ImGuiWindow*            RootWindowForNav;                   // Point to ourself or first ancestor which doesn't have the NavFlattened flag.\n    ImGuiWindow*            ParentWindowForFocusRoute;          // Set to manual link a window to its logical parent so that Shortcut() chain are honored (e.g. Tool linked to Document)\n\n    ImGuiWindow*            NavLastChildNavWindow;              // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.)\n    ImGuiID                 NavLastIds[ImGuiNavLayer_COUNT];    // Last known NavId for this window, per layer (0/1)\n    ImRect                  NavRectRel[ImGuiNavLayer_COUNT];    // Reference rectangle, in window relative space\n    ImVec2                  NavPreferredScoringPosRel[ImGuiNavLayer_COUNT]; // Preferred X/Y position updated when moving on a given axis, reset to FLT_MAX.\n    ImGuiID                 NavRootFocusScopeId;                // Focus Scope ID at the time of Begin()\n\n    int                     MemoryDrawListIdxCapacity;          // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy\n    int                     MemoryDrawListVtxCapacity;\n    bool                    MemoryCompacted;                    // Set when window extraneous data have been garbage collected\n\n    // Docking\n    bool                    DockIsActive        :1;             // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1).\n    bool                    DockNodeIsVisible   :1;\n    bool                    DockTabIsVisible    :1;             // Is our window visible this frame? ~~ is the corresponding tab selected?\n    bool                    DockTabWantClose    :1;\n    short                   DockOrder;                          // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible.\n    ImGuiWindowDockStyle    DockStyle;\n    ImGuiDockNode*          DockNode;                           // Which node are we docked into. Important: Prefer testing DockIsActive in many cases as this will still be set when the dock node is hidden.\n    ImGuiDockNode*          DockNodeAsHost;                     // Which node are we owning (for parent windows)\n    ImGuiID                 DockId;                             // Backup of last valid DockNode->ID, so single window remember their dock node id even when they are not bound any more\n\npublic:\n    ImGuiWindow(ImGuiContext* context, const char* name);\n    ~ImGuiWindow();\n\n    ImGuiID     GetID(const char* str, const char* str_end = NULL);\n    ImGuiID     GetID(const void* ptr);\n    ImGuiID     GetID(int n);\n    ImGuiID     GetIDFromPos(const ImVec2& p_abs);\n    ImGuiID     GetIDFromRectangle(const ImRect& r_abs);\n\n    // We don't use g.FontSize because the window may be != g.CurrentWindow.\n    ImRect      Rect() const            { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }\n    ImRect      TitleBarRect() const    { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); }\n    ImRect      MenuBarRect() const     { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); }\n\n    // [OBSOLETE] ImGuiWindow::CalcFontSize() was removed in 1.92.0 because error-prone/misleading. You can use window->FontRefSize for a copy of g.FontSize at the time of the last Begin() call for this window.\n    //float     CalcFontSize() const    { ImGuiContext& g = *Ctx; return g.FontSizeBase * FontWindowScale * FontDpiScale * FontWindowScaleParents;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Tab bar, Tab item support\n//-----------------------------------------------------------------------------\n\n// Extend ImGuiTabBarFlags_\nenum ImGuiTabBarFlagsPrivate_\n{\n    ImGuiTabBarFlags_DockNode                   = 1 << 20,  // Part of a dock node [we don't use this in the master branch but it facilitate branch syncing to keep this around]\n    ImGuiTabBarFlags_IsFocused                  = 1 << 21,\n    ImGuiTabBarFlags_SaveSettings               = 1 << 22,  // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs\n};\n\n// Extend ImGuiTabItemFlags_\nenum ImGuiTabItemFlagsPrivate_\n{\n    ImGuiTabItemFlags_SectionMask_              = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing,\n    ImGuiTabItemFlags_NoCloseButton             = 1 << 20,  // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout)\n    ImGuiTabItemFlags_Button                    = 1 << 21,  // Used by TabItemButton, change the tab item behavior to mimic a button\n    ImGuiTabItemFlags_Invisible                 = 1 << 22,  // To reserve space e.g. with ImGuiTabItemFlags_Leading\n    ImGuiTabItemFlags_Unsorted                  = 1 << 23,  // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window.\n};\n\n// Storage for one active tab item (sizeof() 48 bytes)\nstruct ImGuiTabItem\n{\n    ImGuiID             ID;\n    ImGuiTabItemFlags   Flags;\n    ImGuiWindow*        Window;                 // When TabItem is part of a DockNode's TabBar, we hold on to a window.\n    int                 LastFrameVisible;\n    int                 LastFrameSelected;      // This allows us to infer an ordered list of the last activated tabs with little maintenance\n    float               Offset;                 // Position relative to beginning of tab bar\n    float               Width;                  // Width currently displayed\n    float               ContentWidth;           // Width of label + padding, stored during BeginTabItem() call (misnamed as \"Content\" would normally imply width of label only)\n    float               RequestedWidth;         // Width optionally requested by caller, -1.0f is unused\n    ImS32               NameOffset;             // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames\n    ImS16               BeginOrder;             // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable\n    ImS16               IndexDuringLayout;      // Index only used during TabBarLayout(). Tabs gets reordered so 'Tabs[n].IndexDuringLayout == n' but may mismatch during additions.\n    bool                WantClose;              // Marked as closed by SetTabItemClosed()\n\n    ImGuiTabItem()      { memset((void*)this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; }\n};\n\n// Storage for a tab bar (sizeof() 160 bytes)\nstruct IMGUI_API ImGuiTabBar\n{\n    ImGuiWindow*        Window;\n    ImVector<ImGuiTabItem> Tabs;\n    ImGuiTabBarFlags    Flags;\n    ImGuiID             ID;                     // Zero for tab-bars used by docking\n    ImGuiID             SelectedTabId;          // Selected tab/window\n    ImGuiID             NextSelectedTabId;      // Next selected tab/window. Will also trigger a scrolling animation\n    ImGuiID             NextScrollToTabId;\n    ImGuiID             VisibleTabId;           // Can occasionally be != SelectedTabId (e.g. when previewing contents for Ctrl+Tab preview)\n    int                 CurrFrameVisible;\n    int                 PrevFrameVisible;\n    ImRect              BarRect;\n    float               BarRectPrevWidth;       // Backup of previous width. When width change we enforce keep horizontal scroll on focused tab.\n    float               CurrTabsContentsHeight;\n    float               PrevTabsContentsHeight; // Record the height of contents submitted below the tab bar\n    float               WidthAllTabs;           // Actual width of all tabs (locked during layout)\n    float               WidthAllTabsIdeal;      // Ideal width if all tabs were visible and not clipped\n    float               ScrollingAnim;\n    float               ScrollingTarget;\n    float               ScrollingTargetDistToVisibility;\n    float               ScrollingSpeed;\n    float               ScrollingRectMinX;\n    float               ScrollingRectMaxX;\n    float               SeparatorMinX;\n    float               SeparatorMaxX;\n    ImGuiID             ReorderRequestTabId;\n    ImS16               ReorderRequestOffset;\n    ImS8                BeginCount;\n    bool                WantLayout;\n    bool                VisibleTabWasSubmitted;\n    bool                TabsAddedNew;           // Set to true when a new tab item or button has been added to the tab bar during last frame\n    bool                ScrollButtonEnabled;\n    ImS16               TabsActiveCount;        // Number of tabs submitted this frame.\n    ImS16               LastTabItemIdx;         // Index of last BeginTabItem() tab for use by EndTabItem()\n    float               ItemSpacingY;\n    ImVec2              FramePadding;           // style.FramePadding locked at the time of BeginTabBar()\n    ImVec2              BackupCursorPos;\n    ImGuiTextBuffer     TabsNames;              // For non-docking tab bar we re-append names in a contiguous buffer.\n\n    ImGuiTabBar();\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Table support\n//-----------------------------------------------------------------------------\n\n#define IM_COL32_DISABLE                IM_COL32(0,0,0,1)   // Special sentinel code which cannot be used as a regular color.\n#define IMGUI_TABLE_MAX_COLUMNS         512                 // Arbitrary \"safety\" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx.\n\n// [Internal] sizeof() ~ 112\n// We use the terminology \"Enabled\" to refer to a column that is not Hidden by user/api.\n// We use the terminology \"Clipped\" to refer to a column that is out of sight because of scrolling/clipping.\n// This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use \"Visible\" to mean \"not clipped\".\nstruct ImGuiTableColumn\n{\n    ImGuiTableColumnFlags   Flags;                          // Flags after some patching (not directly same as provided by user). See ImGuiTableColumnFlags_\n    float                   WidthGiven;                     // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be > WidthRequest to honor minimum width, may be < WidthRequest to honor shrinking columns down in tight space.\n    float                   MinX;                           // Absolute positions\n    float                   MaxX;\n    float                   WidthRequest;                   // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout()\n    float                   WidthAuto;                      // Automatic width\n    float                   WidthMax;                       // Maximum width (FIXME: overwritten by each instance)\n    float                   StretchWeight;                  // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially.\n    float                   InitStretchWeightOrWidth;       // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_).\n    ImRect                  ClipRect;                       // Clipping rectangle for the column\n    ImGuiID                 UserID;                         // Optional, value passed to TableSetupColumn()\n    float                   WorkMinX;                       // Contents region min ~(MinX + CellPaddingX + CellSpacingX1) == cursor start position when entering column\n    float                   WorkMaxX;                       // Contents region max ~(MaxX - CellPaddingX - CellSpacingX2)\n    float                   ItemWidth;                      // Current item width for the column, preserved across rows\n    float                   ContentMaxXFrozen;              // Contents maximum position for frozen rows (apart from headers), from which we can infer content width.\n    float                   ContentMaxXUnfrozen;\n    float                   ContentMaxXHeadersUsed;         // Contents maximum position for headers rows (regardless of freezing). TableHeader() automatically softclip itself + report ideal desired size, to avoid creating extraneous draw calls\n    float                   ContentMaxXHeadersIdeal;\n    ImS16                   NameOffset;                     // Offset into parent ColumnsNames[]\n    ImGuiTableColumnIdx     DisplayOrder;                   // Index within Table's IndexToDisplayOrder[] (column may be reordered by users)\n    ImGuiTableColumnIdx     IndexWithinEnabledSet;          // Index within enabled/visible set (<= IndexToDisplayOrder)\n    ImGuiTableColumnIdx     PrevEnabledColumn;              // Index of prev enabled/visible column within Columns[], -1 if first enabled/visible column\n    ImGuiTableColumnIdx     NextEnabledColumn;              // Index of next enabled/visible column within Columns[], -1 if last enabled/visible column\n    ImGuiTableColumnIdx     SortOrder;                      // Index of this column within sort specs, -1 if not sorting on this column, 0 for single-sort, may be >0 on multi-sort\n    ImGuiTableDrawChannelIdx DrawChannelCurrent;            // Index within DrawSplitter.Channels[]\n    ImGuiTableDrawChannelIdx DrawChannelFrozen;             // Draw channels for frozen rows (often headers)\n    ImGuiTableDrawChannelIdx DrawChannelUnfrozen;           // Draw channels for unfrozen rows\n    bool                    IsEnabled;                      // IsUserEnabled && (Flags & ImGuiTableColumnFlags_Disabled) == 0\n    bool                    IsUserEnabled;                  // Is the column not marked Hidden by the user? (unrelated to being off view, e.g. clipped by scrolling).\n    bool                    IsUserEnabledNextFrame;\n    bool                    IsVisibleX;                     // Is actually in view (e.g. overlapping the host window clipping rectangle, not scrolled).\n    bool                    IsVisibleY;\n    bool                    IsRequestOutput;                // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not.\n    bool                    IsSkipItems;                    // Do we want item submissions to this column to be completely ignored (no layout will happen).\n    bool                    IsPreserveWidthAuto;\n    ImS8                    NavLayerCurrent;                // ImGuiNavLayer in 1 byte\n    ImU8                    AutoFitQueue;                   // Queue of 8 values for the next 8 frames to request auto-fit\n    ImU8                    CannotSkipItemsQueue;           // Queue of 8 values for the next 8 frames to disable Clipped/SkipItem\n    ImU8                    SortDirection : 2;              // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending\n    ImU8                    SortDirectionsAvailCount : 2;   // Number of available sort directions (0 to 3)\n    ImU8                    SortDirectionsAvailMask : 4;    // Mask of available sort directions (1-bit each)\n    ImU8                    SortDirectionsAvailList;        // Ordered list of available sort directions (2-bits each, total 8-bits)\n\n    ImGuiTableColumn()\n    {\n        memset((void*)this, 0, sizeof(*this));\n        StretchWeight = WidthRequest = -1.0f;\n        NameOffset = -1;\n        DisplayOrder = IndexWithinEnabledSet = -1;\n        PrevEnabledColumn = NextEnabledColumn = -1;\n        SortOrder = -1;\n        SortDirection = ImGuiSortDirection_None;\n        DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU8)-1;\n    }\n};\n\n// Transient cell data stored per row.\n// sizeof() ~ 6 bytes\nstruct ImGuiTableCellData\n{\n    ImU32                       BgColor;    // Actual color\n    ImGuiTableColumnIdx         Column;     // Column number\n};\n\n// Parameters for TableAngledHeadersRowEx()\n// This may end up being refactored for more general purpose.\n// sizeof() ~ 12 bytes\nstruct ImGuiTableHeaderData\n{\n    ImGuiTableColumnIdx         Index;      // Column index\n    ImU32                       TextColor;\n    ImU32                       BgColor0;\n    ImU32                       BgColor1;\n};\n\n// Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs. Does that means they could be moved to ImGuiTableTempData?)\n// sizeof() ~ 24 bytes\nstruct ImGuiTableInstanceData\n{\n    ImGuiID                     TableInstanceID;\n    float                       LastOuterHeight;            // Outer height from last frame\n    float                       LastTopHeadersRowHeight;    // Height of first consecutive header rows from last frame (FIXME: this is used assuming consecutive headers are in same frozen set)\n    float                       LastFrozenHeight;           // Height of frozen section from last frame\n    int                         HoveredRowLast;             // Index of row which was hovered last frame.\n    int                         HoveredRowNext;             // Index of row hovered this frame, set after encountering it.\n\n    ImGuiTableInstanceData()    { TableInstanceID = 0; LastOuterHeight = LastTopHeadersRowHeight = LastFrozenHeight = 0.0f; HoveredRowLast = HoveredRowNext = -1; }\n};\n\n// sizeof() ~ 592 bytes + heap allocs described in TableBeginInitMemory()\nstruct IMGUI_API ImGuiTable\n{\n    ImGuiID                     ID;\n    ImGuiTableFlags             Flags;\n    void*                       RawData;                    // Single allocation to hold Columns[], DisplayOrderToIndex[], and RowCellData[]\n    ImGuiTableTempData*         TempData;                   // Transient data while table is active. Point within g.CurrentTableStack[]\n    ImSpan<ImGuiTableColumn>    Columns;                    // Point within RawData[]\n    ImSpan<ImGuiTableColumnIdx> DisplayOrderToIndex;        // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1)\n    ImSpan<ImGuiTableCellData>  RowCellData;                // Point within RawData[]. Store cells background requests for current row.\n    ImBitArrayPtr               EnabledMaskByDisplayOrder;  // Column DisplayOrder -> IsEnabled map\n    ImBitArrayPtr               EnabledMaskByIndex;         // Column Index -> IsEnabled map (== not hidden by user/api) in a format adequate for iterating column without touching cold data\n    ImBitArrayPtr               VisibleMaskByIndex;         // Column Index -> IsVisibleX|IsVisibleY map (== not hidden by user/api && not hidden by scrolling/cliprect)\n    ImGuiTableFlags             SettingsLoadedFlags;        // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order)\n    int                         SettingsOffset;             // Offset in g.SettingsTables\n    int                         LastFrameActive;\n    int                         ColumnsCount;               // Number of columns declared in BeginTable()\n    int                         CurrentRow;\n    int                         CurrentColumn;\n    ImS16                       InstanceCurrent;            // Count of BeginTable() calls with same ID in the same frame (generally 0). This is a little bit similar to BeginCount for a window, but multiple tables with the same ID are multiple tables, they are just synced.\n    ImS16                       InstanceInteracted;         // Mark which instance (generally 0) of the same ID is being interacted with\n    float                       RowPosY1;\n    float                       RowPosY2;\n    float                       RowMinHeight;               // Height submitted to TableNextRow()\n    float                       RowCellPaddingY;            // Top and bottom padding. Reloaded during row change.\n    float                       RowTextBaseline;\n    float                       RowIndentOffsetX;\n    ImGuiTableRowFlags          RowFlags : 16;              // Current row flags, see ImGuiTableRowFlags_\n    ImGuiTableRowFlags          LastRowFlags : 16;\n    int                         RowBgColorCounter;          // Counter for alternating background colors (can be fast-forwarded by e.g clipper), not same as CurrentRow because header rows typically don't increase this.\n    ImU32                       RowBgColor[2];              // Background color override for current row.\n    ImU32                       BorderColorStrong;\n    ImU32                       BorderColorLight;\n    float                       BorderX1;\n    float                       BorderX2;\n    float                       HostIndentX;\n    float                       MinColumnWidth;\n    float                       OuterPaddingX;\n    float                       CellPaddingX;               // Padding from each borders. Locked in BeginTable()/Layout.\n    float                       CellSpacingX1;              // Spacing between non-bordered cells. Locked in BeginTable()/Layout.\n    float                       CellSpacingX2;\n    float                       InnerWidth;                 // User value passed to BeginTable(), see comments at the top of BeginTable() for details.\n    float                       ColumnsGivenWidth;          // Sum of current column width\n    float                       ColumnsAutoFitWidth;        // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window\n    float                       ColumnsStretchSumWeights;   // Sum of weight of all enabled stretching columns\n    float                       ResizedColumnNextWidth;\n    float                       ResizeLockMinContentsX2;    // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table.\n    float                       RefScale;                   // Reference scale to be able to rescale columns on font/dpi changes.\n    float                       AngledHeadersHeight;        // Set by TableAngledHeadersRow(), used in TableUpdateLayout()\n    float                       AngledHeadersSlope;         // Set by TableAngledHeadersRow(), used in TableUpdateLayout()\n    ImRect                      OuterRect;                  // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable().\n    ImRect                      InnerRect;                  // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is \"\n    ImRect                      WorkRect;\n    ImRect                      InnerClipRect;\n    ImRect                      BgClipRect;                 // We use this to cpu-clip cell background color fill, evolve during the frame as we cross frozen rows boundaries\n    ImRect                      Bg0ClipRectForDrawCmd;      // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped\n    ImRect                      Bg2ClipRectForDrawCmd;      // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect.\n    ImRect                      HostClipRect;               // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window.\n    ImRect                      HostBackupInnerClipRect;    // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground()\n    ImGuiWindow*                OuterWindow;                // Parent window for the table\n    ImGuiWindow*                InnerWindow;                // Window holding the table data (== OuterWindow or a child window)\n    ImGuiTextBuffer             ColumnsNames;               // Contiguous buffer holding columns names\n    ImDrawListSplitter*         DrawSplitter;               // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly\n    ImGuiTableInstanceData      InstanceDataFirst;\n    ImVector<ImGuiTableInstanceData>    InstanceDataExtra;  // FIXME-OPT: Using a small-vector pattern would be good.\n    ImGuiTableColumnSortSpecs   SortSpecsSingle;\n    ImVector<ImGuiTableColumnSortSpecs> SortSpecsMulti;     // FIXME-OPT: Using a small-vector pattern would be good.\n    ImGuiTableSortSpecs         SortSpecs;                  // Public facing sorts specs, this is what we return in TableGetSortSpecs()\n    ImGuiTableColumnIdx         SortSpecsCount;\n    ImGuiTableColumnIdx         ColumnsEnabledCount;        // Number of enabled columns (<= ColumnsCount)\n    ImGuiTableColumnIdx         ColumnsEnabledFixedCount;   // Number of enabled columns using fixed width (<= ColumnsCount)\n    ImGuiTableColumnIdx         DeclColumnsCount;           // Count calls to TableSetupColumn()\n    ImGuiTableColumnIdx         AngledHeadersCount;         // Count columns with angled headers\n    ImGuiTableColumnIdx         HoveredColumnBody;          // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column!\n    ImGuiTableColumnIdx         HoveredColumnBorder;        // Index of column whose right-border is being hovered (for resizing).\n    ImGuiTableColumnIdx         HighlightColumnHeader;      // Index of column which should be highlighted.\n    ImGuiTableColumnIdx         AutoFitSingleColumn;        // Index of single column requesting auto-fit.\n    ImGuiTableColumnIdx         ResizedColumn;              // Index of column being resized. Reset when InstanceCurrent==0.\n    ImGuiTableColumnIdx         LastResizedColumn;          // Index of column being resized from previous frame.\n    ImGuiTableColumnIdx         HeldHeaderColumn;           // Index of column header being held.\n    ImGuiTableColumnIdx         ReorderColumn;              // Index of column being reordered. (not cleared)\n    ImGuiTableColumnIdx         ReorderColumnDir;           // -1 or +1\n    ImGuiTableColumnIdx         LeftMostEnabledColumn;      // Index of left-most non-hidden column.\n    ImGuiTableColumnIdx         RightMostEnabledColumn;     // Index of right-most non-hidden column.\n    ImGuiTableColumnIdx         LeftMostStretchedColumn;    // Index of left-most stretched column.\n    ImGuiTableColumnIdx         RightMostStretchedColumn;   // Index of right-most stretched column.\n    ImGuiTableColumnIdx         ContextPopupColumn;         // Column right-clicked on, of -1 if opening context menu from a neutral/empty spot\n    ImGuiTableColumnIdx         FreezeRowsRequest;          // Requested frozen rows count\n    ImGuiTableColumnIdx         FreezeRowsCount;            // Actual frozen row count (== FreezeRowsRequest, or == 0 when no scrolling offset)\n    ImGuiTableColumnIdx         FreezeColumnsRequest;       // Requested frozen columns count\n    ImGuiTableColumnIdx         FreezeColumnsCount;         // Actual frozen columns count (== FreezeColumnsRequest, or == 0 when no scrolling offset)\n    ImGuiTableColumnIdx         RowCellDataCurrent;         // Index of current RowCellData[] entry in current row\n    ImGuiTableDrawChannelIdx    DummyDrawChannel;           // Redirect non-visible columns here.\n    ImGuiTableDrawChannelIdx    Bg2DrawChannelCurrent;      // For Selectable() and other widgets drawing across columns after the freezing line. Index within DrawSplitter.Channels[]\n    ImGuiTableDrawChannelIdx    Bg2DrawChannelUnfrozen;\n    ImS8                        NavLayer;                   // ImGuiNavLayer at the time of BeginTable().\n    bool                        IsLayoutLocked;             // Set by TableUpdateLayout() which is called when beginning the first row.\n    bool                        IsInsideRow;                // Set when inside TableBeginRow()/TableEndRow().\n    bool                        IsInitializing;\n    bool                        IsSortSpecsDirty;\n    bool                        IsUsingHeaders;             // Set when the first row had the ImGuiTableRowFlags_Headers flag.\n    bool                        IsContextPopupOpen;         // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).\n    bool                        DisableDefaultContextMenu;  // Disable default context menu. You may submit your own using TableBeginContextMenuPopup()/EndPopup()\n    bool                        IsSettingsRequestLoad;\n    bool                        IsSettingsDirty;            // Set when table settings have changed and needs to be reported into ImGuiTableSettings data.\n    bool                        IsDefaultDisplayOrder;      // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)\n    bool                        IsResetAllRequest;\n    bool                        IsResetDisplayOrderRequest;\n    bool                        IsUnfrozenRows;             // Set when we got past the frozen row.\n    bool                        IsDefaultSizingPolicy;      // Set if user didn't explicitly set a sizing policy in BeginTable()\n    bool                        IsActiveIdAliveBeforeTable;\n    bool                        IsActiveIdInTable;\n    bool                        HasScrollbarYCurr;          // Whether ANY instance of this table had a vertical scrollbar during the current frame.\n    bool                        HasScrollbarYPrev;          // Whether ANY instance of this table had a vertical scrollbar during the previous.\n    bool                        MemoryCompacted;\n    bool                        HostSkipItems;              // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis\n\n    ImGuiTable()                { memset((void*)this, 0, sizeof(*this)); LastFrameActive = -1; }\n    ~ImGuiTable()               { IM_FREE(RawData); }\n};\n\n// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table).\n// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure.\n// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics.\n// FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs.\n// sizeof() ~ 136 bytes.\nstruct IMGUI_API ImGuiTableTempData\n{\n    ImGuiID                     WindowID;                   // Shortcut to g.Tables[TableIndex]->OuterWindow->ID.\n    int                         TableIndex;                 // Index in g.Tables.Buf[] pool\n    float                       LastTimeActive;             // Last timestamp this structure was used\n    float                       AngledHeadersExtraWidth;    // Used in EndTable()\n    ImVector<ImGuiTableHeaderData> AngledHeadersRequests;   // Used in TableAngledHeadersRow()\n\n    ImVec2                      UserOuterSize;              // outer_size.x passed to BeginTable()\n    ImDrawListSplitter          DrawSplitter;\n\n    ImRect                      HostBackupWorkRect;         // Backup of InnerWindow->WorkRect at the end of BeginTable()\n    ImRect                      HostBackupParentWorkRect;   // Backup of InnerWindow->ParentWorkRect at the end of BeginTable()\n    ImVec2                      HostBackupPrevLineSize;     // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable()\n    ImVec2                      HostBackupCurrLineSize;     // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable()\n    ImVec2                      HostBackupCursorMaxPos;     // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable()\n    ImVec1                      HostBackupColumnsOffset;    // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable()\n    float                       HostBackupItemWidth;        // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable()\n    int                         HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable()\n\n    ImGuiTableTempData()        { memset((void*)this, 0, sizeof(*this)); LastTimeActive = -1.0f; }\n};\n\n// sizeof() ~ 16\nstruct ImGuiTableColumnSettings\n{\n    float                   WidthOrWeight;\n    ImGuiID                 UserID;\n    ImGuiTableColumnIdx     Index;\n    ImGuiTableColumnIdx     DisplayOrder;\n    ImGuiTableColumnIdx     SortOrder;\n    ImU8                    SortDirection : 2;\n    ImS8                    IsEnabled : 2; // \"Visible\" in ini file\n    ImU8                    IsStretch : 1;\n\n    ImGuiTableColumnSettings()\n    {\n        WidthOrWeight = 0.0f;\n        UserID = 0;\n        Index = -1;\n        DisplayOrder = SortOrder = -1;\n        SortDirection = ImGuiSortDirection_None;\n        IsEnabled = -1;\n        IsStretch = 0;\n    }\n};\n\n// This is designed to be stored in a single ImChunkStream (1 header followed by N ImGuiTableColumnSettings, etc.)\nstruct ImGuiTableSettings\n{\n    ImGuiID                     ID;                     // Set to 0 to invalidate/delete the setting\n    ImGuiTableFlags             SaveFlags;              // Indicate data we want to save using the Resizable/Reorderable/Sortable/Hideable flags (could be using its own flags..)\n    float                       RefScale;               // Reference scale to be able to rescale columns on font/dpi changes.\n    ImGuiTableColumnIdx         ColumnsCount;\n    ImGuiTableColumnIdx         ColumnsCountMax;        // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher\n    bool                        WantApply;              // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)\n\n    ImGuiTableSettings()        { memset((void*)this, 0, sizeof(*this)); }\n    ImGuiTableColumnSettings*   GetColumnSettings()     { return (ImGuiTableColumnSettings*)(this + 1); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGui internal API\n// No guarantee of forward compatibility here!\n//-----------------------------------------------------------------------------\n\nnamespace ImGui\n{\n    // Windows\n    // We should always have a CurrentWindow in the stack (there is an implicit \"Debug\" window)\n    // If this ever crashes because g.CurrentWindow is NULL, it means that either:\n    // - ImGui::NewFrame() has never been called, which is illegal.\n    // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal.\n    IMGUI_API ImGuiIO&         GetIO(ImGuiContext* ctx);\n    IMGUI_API ImGuiPlatformIO& GetPlatformIO(ImGuiContext* ctx);\n    inline    float         GetScale()                  { ImGuiContext& g = *GImGui; return g.Style._MainScale; } // FIXME-DPI: I don't want to formalize this just yet. Because reasons. Please don't use.\n    inline    ImGuiWindow*  GetCurrentWindowRead()      { ImGuiContext& g = *GImGui; return g.CurrentWindow; }\n    inline    ImGuiWindow*  GetCurrentWindow()          { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; }\n    IMGUI_API ImGuiWindow*  FindWindowByID(ImGuiID id);\n    IMGUI_API ImGuiWindow*  FindWindowByName(const char* name);\n    IMGUI_API void          UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);\n    IMGUI_API void          UpdateWindowSkipRefresh(ImGuiWindow* window);\n    IMGUI_API ImVec2        CalcWindowNextAutoFitSize(ImGuiWindow* window);\n    IMGUI_API bool          IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy);\n    IMGUI_API bool          IsWindowInBeginStack(ImGuiWindow* window);\n    IMGUI_API bool          IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent);\n    IMGUI_API bool          IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below);\n    IMGUI_API bool          IsWindowNavFocusable(ImGuiWindow* window);\n    IMGUI_API void          SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0);\n    IMGUI_API void          SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0);\n    IMGUI_API void          SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0);\n    IMGUI_API void          SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size);\n    IMGUI_API void          SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window);\n    inline void             SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; } // You may also use SetNextWindowClass()'s FocusRouteParentWindowId field.\n    inline ImRect           WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); }\n    inline ImRect           WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); }\n    inline ImVec2           WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p)  { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); }\n    inline ImVec2           WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p)  { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); }\n\n    // Windows: Display Order and Focus Order\n    IMGUI_API void          FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0);\n    IMGUI_API void          FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags);\n    IMGUI_API void          BringWindowToFocusFront(ImGuiWindow* window);\n    IMGUI_API void          BringWindowToDisplayFront(ImGuiWindow* window);\n    IMGUI_API void          BringWindowToDisplayBack(ImGuiWindow* window);\n    IMGUI_API void          BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* above_window);\n    IMGUI_API int           FindWindowDisplayIndex(ImGuiWindow* window);\n    IMGUI_API ImGuiWindow*  FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window);\n\n    // Windows: Idle, Refresh Policies [EXPERIMENTAL]\n    IMGUI_API void          SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags);\n\n    // Fonts, drawing\n    IMGUI_API void          RegisterUserTexture(ImTextureData* tex); // Register external texture. EXPERIMENTAL: DO NOT USE YET.\n    IMGUI_API void          UnregisterUserTexture(ImTextureData* tex);\n    IMGUI_API void          RegisterFontAtlas(ImFontAtlas* atlas);\n    IMGUI_API void          UnregisterFontAtlas(ImFontAtlas* atlas);\n    IMGUI_API void          SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling);\n    IMGUI_API void          UpdateCurrentFontSize(float restore_font_size_after_scaling);\n    IMGUI_API void          SetFontRasterizerDensity(float rasterizer_density);\n    inline float            GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; }\n    inline float            GetRoundedFontSize(float size) { return IM_ROUND(size); }\n    IMGUI_API ImFont*       GetDefaultFont();\n    IMGUI_API void          PushPasswordFont();\n    IMGUI_API void          PopPasswordFont();\n    inline ImDrawList*      GetForegroundDrawList(ImGuiWindow* window) { return GetForegroundDrawList(window->Viewport); }\n    IMGUI_API void          AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);\n\n    // Init\n    IMGUI_API void          Initialize();\n    IMGUI_API void          Shutdown();    // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext().\n\n    // Context name & generic context hooks\n    IMGUI_API void          SetContextName(ImGuiContext* ctx, const char* name);\n    IMGUI_API ImGuiID       AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook);\n    IMGUI_API void          RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_to_remove);\n    IMGUI_API void          CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType type);\n\n    // NewFrame\n    IMGUI_API void          UpdateInputEvents(bool trickle_fast_inputs);\n    IMGUI_API void          UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos);\n    IMGUI_API void          FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window);\n    IMGUI_API void          StartMouseMovingWindow(ImGuiWindow* window);\n    IMGUI_API void          StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock);\n    IMGUI_API void          StopMouseMovingWindow();\n    IMGUI_API void          UpdateMouseMovingWindowNewFrame();\n    IMGUI_API void          UpdateMouseMovingWindowEndFrame();\n\n    // Viewports\n    IMGUI_API void          TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos, const ImVec2& old_size, const ImVec2& new_size);\n    IMGUI_API void          ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale);\n    IMGUI_API void          DestroyPlatformWindow(ImGuiViewportP* viewport);\n    IMGUI_API void          SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport);\n    IMGUI_API void          SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport);\n    IMGUI_API const ImGuiPlatformMonitor*   GetViewportPlatformMonitor(ImGuiViewport* viewport);\n    IMGUI_API ImGuiViewportP*               FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos);\n\n    // Settings\n    IMGUI_API void                  MarkIniSettingsDirty();\n    IMGUI_API void                  MarkIniSettingsDirty(ImGuiWindow* window);\n    IMGUI_API void                  ClearIniSettings();\n    IMGUI_API void                  AddSettingsHandler(const ImGuiSettingsHandler* handler);\n    IMGUI_API void                  RemoveSettingsHandler(const char* type_name);\n    IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name);\n\n    // Settings - Windows\n    IMGUI_API ImGuiWindowSettings*  CreateNewWindowSettings(const char* name);\n    IMGUI_API ImGuiWindowSettings*  FindWindowSettingsByID(ImGuiID id);\n    IMGUI_API ImGuiWindowSettings*  FindWindowSettingsByWindow(ImGuiWindow* window);\n    IMGUI_API void                  ClearWindowSettings(const char* name);\n\n    // Localization\n    IMGUI_API void          LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count);\n    inline const char*      LocalizeGetMsg(ImGuiLocKey key) { ImGuiContext& g = *GImGui; const char* msg = g.LocalizationTable[key]; return msg ? msg : \"*Missing Text*\"; }\n\n    // Scrolling\n    IMGUI_API void          SetScrollX(ImGuiWindow* window, float scroll_x);\n    IMGUI_API void          SetScrollY(ImGuiWindow* window, float scroll_y);\n    IMGUI_API void          SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio);\n    IMGUI_API void          SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio);\n\n    // Early work-in-progress API (ScrollToItem() will become public)\n    IMGUI_API void          ScrollToItem(ImGuiScrollFlags flags = 0);\n    IMGUI_API void          ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0);\n    IMGUI_API ImVec2        ScrollToRectEx(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0);\n//#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    inline void             ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& rect) { ScrollToRect(window, rect, ImGuiScrollFlags_KeepVisibleEdgeY); }\n//#endif\n\n    // Basic Accessors\n    inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; }\n    inline ImGuiID          GetActiveID()   { ImGuiContext& g = *GImGui; return g.ActiveId; }\n    inline ImGuiID          GetFocusID()    { ImGuiContext& g = *GImGui; return g.NavId; }\n    IMGUI_API void          SetActiveID(ImGuiID id, ImGuiWindow* window);\n    IMGUI_API void          SetFocusID(ImGuiID id, ImGuiWindow* window);\n    IMGUI_API void          ClearActiveID();\n    IMGUI_API ImGuiID       GetHoveredID();\n    IMGUI_API void          SetHoveredID(ImGuiID id);\n    IMGUI_API void          KeepAliveID(ImGuiID id);\n    IMGUI_API void          MarkItemEdited(ImGuiID id);     // Mark data associated to given item as \"edited\", used by IsItemDeactivatedAfterEdit() function.\n    IMGUI_API void          PushOverrideID(ImGuiID id);     // Push given value as-is at the top of the ID stack (whereas PushID combines old and new hashes)\n    IMGUI_API ImGuiID       GetIDWithSeed(const char* str_id_begin, const char* str_id_end, ImGuiID seed);\n    IMGUI_API ImGuiID       GetIDWithSeed(int n, ImGuiID seed);\n\n    // Basic Helpers for widget code\n    IMGUI_API void          ItemSize(const ImVec2& size, float text_baseline_y = -1.0f);\n    inline void             ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min.\n    IMGUI_API bool          ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0);\n    IMGUI_API bool          ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags);\n    IMGUI_API bool          IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0);\n    IMGUI_API bool          IsClippedEx(const ImRect& bb, ImGuiID id);\n    IMGUI_API void          SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect);\n    IMGUI_API ImVec2        CalcItemSize(ImVec2 size, float default_w, float default_h);\n    IMGUI_API float         CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);\n    IMGUI_API void          PushMultiItemsWidths(int components, float width_full);\n    IMGUI_API void          ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess, float width_min);\n    IMGUI_API void          CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end);\n\n    // Parameter stacks (shared)\n    IMGUI_API const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx);\n    IMGUI_API void          BeginDisabledOverrideReenable();\n    IMGUI_API void          EndDisabledOverrideReenable();\n\n    // Logging/Capture\n    IMGUI_API void          LogBegin(ImGuiLogFlags flags, int auto_open_depth);         // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name.\n    IMGUI_API void          LogToBuffer(int auto_open_depth = -1);                      // Start logging/capturing to internal buffer\n    IMGUI_API void          LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL);\n    IMGUI_API void          LogSetNextTextDecoration(const char* prefix, const char* suffix);\n\n    // Childs\n    IMGUI_API bool          BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags);\n\n    // Popups, Modals\n    IMGUI_API bool          BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags);\n    IMGUI_API bool          BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags extra_window_flags);\n    IMGUI_API void          OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None);\n    IMGUI_API void          ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup);\n    IMGUI_API void          ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup);\n    IMGUI_API void          ClosePopupsExceptModals();\n    IMGUI_API bool          IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags);\n    IMGUI_API ImRect        GetPopupAllowedExtentRect(ImGuiWindow* window);\n    IMGUI_API ImGuiWindow*  GetTopMostPopupModal();\n    IMGUI_API ImGuiWindow*  GetTopMostAndVisiblePopupModal();\n    IMGUI_API ImGuiWindow*  FindBlockingModal(ImGuiWindow* window);\n    IMGUI_API ImVec2        FindBestWindowPosForPopup(ImGuiWindow* window);\n    IMGUI_API ImVec2        FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy);\n    IMGUI_API ImGuiMouseButton GetMouseButtonFromPopupFlags(ImGuiPopupFlags flags);\n    IMGUI_API bool          IsPopupOpenRequestForItem(ImGuiPopupFlags flags, ImGuiID id);\n    IMGUI_API bool          IsPopupOpenRequestForWindow(ImGuiPopupFlags flags);\n\n    // Tooltips\n    IMGUI_API bool          BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags);\n    IMGUI_API bool          BeginTooltipHidden();\n\n    // Menus\n    IMGUI_API bool          BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags);\n    IMGUI_API bool          BeginMenuEx(const char* label, const char* icon, bool enabled = true);\n    IMGUI_API bool          MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true);\n\n    // Combos\n    IMGUI_API bool          BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags);\n    IMGUI_API bool          BeginComboPreview();\n    IMGUI_API void          EndComboPreview();\n\n    // Keyboard/Gamepad Navigation\n    IMGUI_API void          NavInitWindow(ImGuiWindow* window, bool force_reinit);\n    IMGUI_API void          NavInitRequestApplyResult();\n    IMGUI_API bool          NavMoveRequestButNoResultYet();\n    IMGUI_API void          NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);\n    IMGUI_API void          NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);\n    IMGUI_API void          NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result);\n    IMGUI_API void          NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data);\n    IMGUI_API void          NavMoveRequestCancel();\n    IMGUI_API void          NavMoveRequestApplyResult();\n    IMGUI_API void          NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);\n    IMGUI_API void          NavHighlightActivated(ImGuiID id);\n    IMGUI_API void          NavClearPreferredPosForAxis(ImGuiAxis axis);\n    IMGUI_API void          SetNavCursorVisibleAfterMove();\n    IMGUI_API void          NavUpdateCurrentWindowIsScrollPushableX();\n    IMGUI_API void          SetNavWindow(ImGuiWindow* window);\n    IMGUI_API void          SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel);\n    IMGUI_API void          SetNavFocusScope(ImGuiID focus_scope_id);\n\n    // Focus/Activation\n    // This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are\n    // much harder to design and implement than expected. I have a couple of private branches on this matter but it's not simple. For now implementing the easy ones.\n    IMGUI_API void          FocusItem();                    // Focus last item (no selection/activation).\n    IMGUI_API void          ActivateItemByID(ImGuiID id);   // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again. Was called 'ActivateItem()' before 1.89.7.\n\n    // Inputs\n    // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.\n    inline bool             IsNamedKey(ImGuiKey key)                    { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; }\n    inline bool             IsNamedKeyOrMod(ImGuiKey key)               { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super; }\n    inline bool             IsLegacyKey(ImGuiKey key)                   { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; }\n    inline bool             IsKeyboardKey(ImGuiKey key)                 { return key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END; }\n    inline bool             IsGamepadKey(ImGuiKey key)                  { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; }\n    inline bool             IsMouseKey(ImGuiKey key)                    { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; }\n    inline bool             IsAliasKey(ImGuiKey key)                    { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; }\n    inline bool             IsLRModKey(ImGuiKey key)                    { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; }\n    ImGuiKeyChord           FixupKeyChord(ImGuiKeyChord key_chord);\n    inline ImGuiKey         ConvertSingleModFlagToKey(ImGuiKey key)\n    {\n        if (key == ImGuiMod_Ctrl) return ImGuiKey_ReservedForModCtrl;\n        if (key == ImGuiMod_Shift) return ImGuiKey_ReservedForModShift;\n        if (key == ImGuiMod_Alt) return ImGuiKey_ReservedForModAlt;\n        if (key == ImGuiMod_Super) return ImGuiKey_ReservedForModSuper;\n        return key;\n    }\n\n    IMGUI_API ImGuiKeyData* GetKeyData(ImGuiContext* ctx, ImGuiKey key);\n    inline ImGuiKeyData*    GetKeyData(ImGuiKey key)                                    { ImGuiContext& g = *GImGui; return GetKeyData(&g, key); }\n    IMGUI_API const char*   GetKeyChordName(ImGuiKeyChord key_chord);\n    inline ImGuiKey         MouseButtonToKey(ImGuiMouseButton button)                   { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return (ImGuiKey)(ImGuiKey_MouseLeft + button); }\n    IMGUI_API bool          IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f);\n    IMGUI_API ImVec2        GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down);\n    IMGUI_API float         GetNavTweakPressedAmount(ImGuiAxis axis);\n    IMGUI_API int           CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate);\n    IMGUI_API void          GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate);\n    IMGUI_API void          TeleportMousePos(const ImVec2& pos);\n    IMGUI_API void          SetActiveIdUsingAllKeyboardKeys();\n    inline bool             IsActiveIdUsingNavDir(ImGuiDir dir)                         { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }\n\n    // [EXPERIMENTAL] Low-Level: Key/Input Ownership\n    // - The idea is that instead of \"eating\" a given input, we can link to an owner id.\n    // - Ownership is most often claimed as a result of reacting to a press/down event (but occasionally may be claimed ahead).\n    // - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID.\n    // - Legacy input queries (without specifying an owner or _Any or _None) are equivalent to using ImGuiKeyOwner_Any (== 0).\n    // - Input ownership is automatically released on the frame after a key is released. Therefore:\n    //   - for ownership registration happening as a result of a down/press event, the SetKeyOwner() call may be done once (common case).\n    //   - for ownership registration happening ahead of a down/press event, the SetKeyOwner() call needs to be made every frame (happens if e.g. claiming ownership on hover).\n    // - SetItemKeyOwner() is a shortcut for common simple case. A custom widget will probably want to call SetKeyOwner() multiple times directly based on its interaction state.\n    // - This is marked experimental because not all widgets are fully honoring the Set/Test idioms. We will need to move forward step by step.\n    //   Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved.\n    IMGUI_API ImGuiID       GetKeyOwner(ImGuiKey key);\n    IMGUI_API void          SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);\n    IMGUI_API void          SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0);\n    IMGUI_API void          SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags);       // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.\n    IMGUI_API bool          TestKeyOwner(ImGuiKey key, ImGuiID owner_id);               // Test that key is either not owned, either owned by 'owner_id'\n    inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key)          { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; }\n\n    // [EXPERIMENTAL] High-Level: Input Access functions w/ support for Key/Input Ownership\n    // - Important: legacy IsKeyPressed(ImGuiKey, bool repeat=true) _DEFAULTS_ to repeat, new IsKeyPressed() requires _EXPLICIT_ ImGuiInputFlags_Repeat flag.\n    // - Expected to be later promoted to public API, the prototypes are designed to replace existing ones (since owner_id can default to Any == 0)\n    // - Specifying a value for 'ImGuiID owner' will test that EITHER the key is NOT owned (UNLESS locked), EITHER the key is owned by 'owner'.\n    //   Legacy functions use ImGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.\n    // - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API.\n    IMGUI_API bool          IsKeyDown(ImGuiKey key, ImGuiID owner_id);\n    IMGUI_API bool          IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);    // Important: when transitioning from old to new IsKeyPressed(): old API has \"bool repeat = true\", so would default to repeat. New API requires explicit ImGuiInputFlags_Repeat.\n    IMGUI_API bool          IsKeyReleased(ImGuiKey key, ImGuiID owner_id);\n    IMGUI_API bool          IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id = 0);\n    IMGUI_API bool          IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id);\n    IMGUI_API bool          IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);\n    IMGUI_API bool          IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id);\n    IMGUI_API bool          IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id);\n\n    // Shortcut Testing & Routing\n    // - Set Shortcut() and SetNextItemShortcut() in imgui.h\n    // - When a policy (except for ImGuiInputFlags_RouteAlways *) is set, Shortcut() will register itself with SetShortcutRouting(),\n    //   allowing the system to decide where to route the input among other route-aware calls.\n    //   (* using ImGuiInputFlags_RouteAlways is roughly equivalent to calling IsKeyChordPressed(key) and bypassing route registration and check)\n    // - When using one of the routing option:\n    //   - The default route is ImGuiInputFlags_RouteFocused (accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.)\n    //   - Routes are requested given a chord (key + modifiers) and a routing policy.\n    //   - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame.\n    //   - Each route may be granted to a single owner. When multiple requests are made we have policies to select the winning route (e.g. deep most window).\n    //   - Multiple read sites may use the same owner id can all access the granted route.\n    //   - When owner_id is 0 we use the current Focus Scope ID as a owner ID in order to identify our location.\n    // - You can chain two unrelated windows in the focus stack using SetWindowParentWindowForFocusRoute()\n    //   e.g. if you have a tool window associated to a document, and you want document shortcuts to run when the tool is focused.\n    IMGUI_API bool          Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id);\n    IMGUI_API bool          SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id); // owner_id needs to be explicit and cannot be 0\n    IMGUI_API bool          TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id);\n    IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord);\n\n    // Docking\n    // (some functions are only declared in imgui.cpp, see Docking section)\n    IMGUI_API void          DockContextInitialize(ImGuiContext* ctx);\n    IMGUI_API void          DockContextShutdown(ImGuiContext* ctx);\n    IMGUI_API void          DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs); // Use root_id==0 to clear all\n    IMGUI_API void          DockContextRebuildNodes(ImGuiContext* ctx);\n    IMGUI_API void          DockContextNewFrameUpdateUndocking(ImGuiContext* ctx);\n    IMGUI_API void          DockContextNewFrameUpdateDocking(ImGuiContext* ctx);\n    IMGUI_API void          DockContextEndFrame(ImGuiContext* ctx);\n    IMGUI_API ImGuiID       DockContextGenNodeID(ImGuiContext* ctx);\n    IMGUI_API void          DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer);\n    IMGUI_API void          DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window);\n    IMGUI_API void          DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node);\n    IMGUI_API void          DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref = true);\n    IMGUI_API void          DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node);\n    IMGUI_API bool          DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos);\n    IMGUI_API ImGuiDockNode*DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id);\n    IMGUI_API void          DockNodeWindowMenuHandler_Default(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar);\n    IMGUI_API bool          DockNodeBeginAmendTabBar(ImGuiDockNode* node);\n    IMGUI_API void          DockNodeEndAmendTabBar();\n    inline ImGuiDockNode*   DockNodeGetRootNode(ImGuiDockNode* node)                 { while (node->ParentNode) node = node->ParentNode; return node; }\n    inline bool             DockNodeIsInHierarchyOf(ImGuiDockNode* node, ImGuiDockNode* parent) { while (node) { if (node == parent) return true; node = node->ParentNode; } return false; }\n    inline int              DockNodeGetDepth(const ImGuiDockNode* node)              { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; }\n    inline ImGuiID          DockNodeGetWindowMenuButtonId(const ImGuiDockNode* node) { return ImHashStr(\"#COLLAPSE\", 0, node->ID); }\n    inline ImGuiDockNode*   GetWindowDockNode()                                      { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; }\n    IMGUI_API bool          GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window);\n    IMGUI_API void          BeginDocked(ImGuiWindow* window, bool* p_open);\n    IMGUI_API void          BeginDockableDragDropSource(ImGuiWindow* window);\n    IMGUI_API void          BeginDockableDragDropTarget(ImGuiWindow* window);\n    IMGUI_API void          SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond);\n\n    // Docking - Builder function needs to be generally called before the node is used/submitted.\n    // - The DockBuilderXXX functions are designed to _eventually_ become a public API, but it is too early to expose it and guarantee stability.\n    // - Do not hold on ImGuiDockNode* pointers! They may be invalidated by any split/merge/remove operation and every frame.\n    // - To create a DockSpace() node, make sure to set the ImGuiDockNodeFlags_DockSpace flag when calling DockBuilderAddNode().\n    //   You can create dockspace nodes (attached to a window) _or_ floating nodes (carry its own window) with this API.\n    // - DockBuilderSplitNode() create 2 child nodes within 1 node. The initial node becomes a parent node.\n    // - If you intend to split the node immediately after creation using DockBuilderSplitNode(), make sure\n    //   to call DockBuilderSetNodeSize() beforehand. If you don't, the resulting split sizes may not be reliable.\n    // - Call DockBuilderFinish() after you are done.\n    IMGUI_API void          DockBuilderDockWindow(const char* window_name, ImGuiID node_id);\n    IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id);\n    inline ImGuiDockNode*   DockBuilderGetCentralNode(ImGuiID node_id)              { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; }\n    IMGUI_API ImGuiID       DockBuilderAddNode(ImGuiID node_id = 0, ImGuiDockNodeFlags flags = 0);\n    IMGUI_API void          DockBuilderRemoveNode(ImGuiID node_id);                 // Remove node and all its child, undock all windows\n    IMGUI_API void          DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_settings_refs = true);\n    IMGUI_API void          DockBuilderRemoveNodeChildNodes(ImGuiID node_id);       // Remove all split/hierarchy. All remaining docked windows will be re-docked to the remaining root node (node_id).\n    IMGUI_API void          DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos);\n    IMGUI_API void          DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size);\n    IMGUI_API ImGuiID       DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir); // Create 2 child nodes in this parent node.\n    IMGUI_API void          DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector<const char*>* in_window_remap_pairs);\n    IMGUI_API void          DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector<ImGuiID>* out_node_remap_pairs);\n    IMGUI_API void          DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name);\n    IMGUI_API void          DockBuilderFinish(ImGuiID node_id);\n\n    // [EXPERIMENTAL] Focus Scope\n    // This is generally used to identify a unique input location (for e.g. a selection set)\n    // There is one per window (automatically set in Begin), but:\n    // - Selection patterns generally need to react (e.g. clear a selection) when landing on one item of the set.\n    //   So in order to identify a set multiple lists in same window may each need a focus scope.\n    //   If you imagine an hypothetical BeginSelectionGroup()/EndSelectionGroup() api, it would likely call PushFocusScope()/EndFocusScope()\n    // - Shortcut routing also use focus scope as a default location identifier if an owner is not provided.\n    // We don't use the ID Stack for this as it is common to want them separate.\n    IMGUI_API void          PushFocusScope(ImGuiID id);\n    IMGUI_API void          PopFocusScope();\n    inline ImGuiID          GetCurrentFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentFocusScopeId; }   // Focus scope we are outputting into, set by PushFocusScope()\n\n    // Drag and Drop\n    IMGUI_API bool          IsDragDropActive();\n    IMGUI_API bool          BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);\n    IMGUI_API bool          BeginDragDropTargetViewport(ImGuiViewport* viewport, const ImRect* p_bb = NULL);\n    IMGUI_API void          ClearDragDrop();\n    IMGUI_API bool          IsDragDropPayloadBeingAccepted();\n    IMGUI_API void          RenderDragDropTargetRectForItem(const ImRect& bb);\n    IMGUI_API void          RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb);\n\n    // Typing-Select API\n    // (provide Windows Explorer style \"select items by typing partial name\" + \"cycle through items by typing same letter\" feature)\n    // (this is currently not documented nor used by main library, but should work. See \"widgets_typingselect\" in imgui_test_suite for usage code. Please let us know if you use this!)\n    IMGUI_API ImGuiTypingSelectRequest* GetTypingSelectRequest(ImGuiTypingSelectFlags flags = ImGuiTypingSelectFlags_None);\n    IMGUI_API int           TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);\n    IMGUI_API int           TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);\n    IMGUI_API int           TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data);\n\n    // Box-Select API\n    IMGUI_API bool          BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags);\n    IMGUI_API void          EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags);\n\n    // Multi-Select API\n    IMGUI_API void          MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags);\n    IMGUI_API void          MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed);\n    IMGUI_API void          MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected);\n    IMGUI_API void          MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item);\n    inline ImGuiBoxSelectState*     GetBoxSelectState(ImGuiID id)   { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; }\n    inline ImGuiMultiSelectState*   GetMultiSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return g.MultiSelectStorage.GetByKey(id); }\n\n    // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API)\n    IMGUI_API void          SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect);\n    IMGUI_API void          BeginColumns(const char* str_id, int count, ImGuiOldColumnFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns().\n    IMGUI_API void          EndColumns();                                                               // close columns\n    IMGUI_API void          PushColumnClipRect(int column_index);\n    IMGUI_API void          PushColumnsBackground();\n    IMGUI_API void          PopColumnsBackground();\n    IMGUI_API ImGuiID       GetColumnsID(const char* str_id, int count);\n    IMGUI_API ImGuiOldColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id);\n    IMGUI_API float         GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm);\n    IMGUI_API float         GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset);\n\n    // Tables: Candidates for public API\n    IMGUI_API void          TableOpenContextMenu(int column_n = -1);\n    IMGUI_API void          TableSetColumnWidth(int column_n, float width);\n    IMGUI_API void          TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs);\n    IMGUI_API int           TableGetHoveredRow();       // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet.\n    IMGUI_API float         TableGetHeaderRowHeight();\n    IMGUI_API float         TableGetHeaderAngledMaxLabelWidth();\n    IMGUI_API void          TablePushBackgroundChannel();\n    IMGUI_API void          TablePopBackgroundChannel();\n    IMGUI_API void          TablePushColumnChannel(int column_n);\n    IMGUI_API void          TablePopColumnChannel();\n    IMGUI_API void          TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count);\n\n    // Tables: Internals\n    inline    ImGuiTable*   GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; }\n    IMGUI_API ImGuiTable*   TableFindByID(ImGuiID id);\n    IMGUI_API bool          BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);\n    IMGUI_API void          TableBeginInitMemory(ImGuiTable* table, int columns_count);\n    IMGUI_API void          TableBeginApplyRequests(ImGuiTable* table);\n    IMGUI_API void          TableSetupDrawChannels(ImGuiTable* table);\n    IMGUI_API void          TableUpdateLayout(ImGuiTable* table);\n    IMGUI_API void          TableUpdateBorders(ImGuiTable* table);\n    IMGUI_API void          TableUpdateColumnsWeightFromWidth(ImGuiTable* table);\n    IMGUI_API void          TableDrawBorders(ImGuiTable* table);\n    IMGUI_API void          TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display);\n    IMGUI_API bool          TableBeginContextMenuPopup(ImGuiTable* table);\n    IMGUI_API void          TableMergeDrawChannels(ImGuiTable* table);\n    inline ImGuiTableInstanceData*  TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; }\n    inline ImGuiID                  TableGetInstanceID(ImGuiTable* table, int instance_no)   { return TableGetInstanceData(table, instance_no)->TableInstanceID; }\n    IMGUI_API void          TableFixDisplayOrder(ImGuiTable* table);\n    IMGUI_API void          TableSortSpecsSanitize(ImGuiTable* table);\n    IMGUI_API void          TableSortSpecsBuild(ImGuiTable* table);\n    IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column);\n    IMGUI_API void          TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column);\n    IMGUI_API float         TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column);\n    IMGUI_API void          TableBeginRow(ImGuiTable* table);\n    IMGUI_API void          TableEndRow(ImGuiTable* table);\n    IMGUI_API void          TableBeginCell(ImGuiTable* table, int column_n);\n    IMGUI_API void          TableEndCell(ImGuiTable* table);\n    IMGUI_API ImRect        TableGetCellBgRect(const ImGuiTable* table, int column_n);\n    IMGUI_API const char*   TableGetColumnName(const ImGuiTable* table, int column_n);\n    IMGUI_API ImGuiID       TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no = 0);\n    IMGUI_API float         TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n);\n    IMGUI_API void          TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n);\n    IMGUI_API void          TableSetColumnWidthAutoAll(ImGuiTable* table);\n    IMGUI_API void          TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order);\n    IMGUI_API void          TableRemove(ImGuiTable* table);\n    IMGUI_API void          TableGcCompactTransientBuffers(ImGuiTable* table);\n    IMGUI_API void          TableGcCompactTransientBuffers(ImGuiTableTempData* table);\n    IMGUI_API void          TableGcCompactSettings();\n\n    // Tables: Settings\n    IMGUI_API void                  TableLoadSettings(ImGuiTable* table);\n    IMGUI_API void                  TableSaveSettings(ImGuiTable* table);\n    IMGUI_API void                  TableResetSettings(ImGuiTable* table);\n    IMGUI_API ImGuiTableSettings*   TableGetBoundSettings(ImGuiTable* table);\n    IMGUI_API void                  TableSettingsAddSettingsHandler();\n    IMGUI_API ImGuiTableSettings*   TableSettingsCreate(ImGuiID id, int columns_count);\n    IMGUI_API ImGuiTableSettings*   TableSettingsFindByID(ImGuiID id);\n\n    // Tab Bars\n    inline    ImGuiTabBar*  GetCurrentTabBar() { ImGuiContext& g = *GImGui; return g.CurrentTabBar; }\n    IMGUI_API ImGuiTabBar*  TabBarFindByID(ImGuiID id);\n    IMGUI_API void          TabBarRemove(ImGuiTabBar* tab_bar);\n    IMGUI_API bool          BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags);\n    IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id);\n    IMGUI_API ImGuiTabItem* TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order);\n    IMGUI_API ImGuiTabItem* TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar);\n    IMGUI_API ImGuiTabItem* TabBarGetCurrentTab(ImGuiTabBar* tab_bar);\n    inline int              TabBarGetTabOrder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { return tab_bar->Tabs.index_from_ptr(tab); }\n    IMGUI_API const char*   TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);\n    IMGUI_API void          TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window);\n    IMGUI_API void          TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id);\n    IMGUI_API void          TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);\n    IMGUI_API void          TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);\n    IMGUI_API void          TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name);\n    IMGUI_API void          TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset);\n    IMGUI_API void          TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImVec2 mouse_pos);\n    IMGUI_API bool          TabBarProcessReorder(ImGuiTabBar* tab_bar);\n    IMGUI_API bool          TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window);\n    IMGUI_API void          TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width);\n    IMGUI_API ImVec2        TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker);\n    IMGUI_API ImVec2        TabItemCalcSize(ImGuiWindow* window);\n    IMGUI_API void          TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col);\n    IMGUI_API void          TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped);\n\n    // Render helpers\n    // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT.\n    // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally)\n    IMGUI_API void          RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true);\n    IMGUI_API void          RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);\n    IMGUI_API void          RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);\n    IMGUI_API void          RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);\n    IMGUI_API void          RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);\n    IMGUI_API void          RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f);\n    IMGUI_API void          RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);\n    IMGUI_API void          RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding);\n    IMGUI_API void          RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);\n    IMGUI_API void          RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None); // Navigation highlight\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    inline    void          RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None) { RenderNavCursor(bb, id, flags); } // Renamed in 1.91.4\n#endif\n    IMGUI_API const char*   FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text.\n    IMGUI_API void          RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow);\n\n    // Render helpers (those functions don't access any ImGui state!)\n    IMGUI_API void          RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f);\n    IMGUI_API void          RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col);\n    IMGUI_API void          RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz);\n    IMGUI_API void          RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col);\n    IMGUI_API void          RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col);\n    IMGUI_API void          RenderRectFilledInRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float fill_x0, float fill_x1, float rounding);\n    IMGUI_API void          RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding);\n    IMGUI_API ImDrawFlags   CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold);\n\n    // Widgets: Text\n    IMGUI_API void          TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0);\n    IMGUI_API void          TextAligned(float align_x, float size_x, const char* fmt, ...);               // FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)\n    IMGUI_API void          TextAlignedV(float align_x, float size_x, const char* fmt, va_list args);\n\n    // Widgets\n    IMGUI_API bool          ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);\n    IMGUI_API bool          ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);\n    IMGUI_API bool          ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);\n    IMGUI_API void          SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f);\n    IMGUI_API void          SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width);\n    IMGUI_API bool          CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value);\n    IMGUI_API bool          CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value);\n\n    // Widgets: Window Decorations\n    IMGUI_API bool          CloseButton(ImGuiID id, const ImVec2& pos);\n    IMGUI_API bool          CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node);\n    IMGUI_API void          Scrollbar(ImGuiAxis axis);\n    IMGUI_API bool          ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags draw_rounding_flags = 0);\n    IMGUI_API ImRect        GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis);\n    IMGUI_API ImGuiID       GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis);\n    IMGUI_API ImGuiID       GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners\n    IMGUI_API ImGuiID       GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir);\n    IMGUI_API void          ExtendHitBoxWhenNearViewportEdge(ImGuiWindow* window, ImRect* bb, float threshold, ImGuiAxis axis);\n\n    // Widgets low-level behaviors\n    IMGUI_API bool          ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0);\n    IMGUI_API bool          DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags);\n    IMGUI_API bool          SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);\n    IMGUI_API bool          SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0);\n\n    // Widgets: Tree Nodes\n    IMGUI_API bool          TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);\n    IMGUI_API void          TreeNodeDrawLineToChildNode(const ImVec2& target_pos);\n    IMGUI_API void          TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data);\n    IMGUI_API void          TreePushOverrideID(ImGuiID id);\n    IMGUI_API void          TreeNodeSetOpen(ImGuiID storage_id, bool open);\n    IMGUI_API bool          TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags);   // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging.\n\n    // Template functions are instantiated in imgui_widgets.cpp for a finite number of types.\n    // To use them externally (for custom widget) you may need an \"extern template\" statement in your code in order to link to existing instances and silence Clang warnings (see #2036).\n    // e.g. \" extern template IMGUI_API float RoundScalarWithFormatT<float, float>(const char* format, ImGuiDataType data_type, float v); \"\n    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API float ScaleRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float logarithmic_zero_epsilon, float zero_deadzone_size);\n    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API T     ScaleValueFromRatioT(ImGuiDataType data_type, float t, T v_min, T v_max, float logarithmic_zero_epsilon, float zero_deadzone_size);\n    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API bool  DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags);\n    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API bool  SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);\n    template<typename T>                                        IMGUI_API T     RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);\n    template<typename T>                                        IMGUI_API bool  CheckboxFlagsT(const char* label, T* flags, T flags_value);\n\n    // Data type helpers\n    IMGUI_API const ImGuiDataTypeInfo*  DataTypeGetInfo(ImGuiDataType data_type);\n    IMGUI_API int           DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format);\n    IMGUI_API void          DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2);\n    IMGUI_API bool          DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty = NULL);\n    IMGUI_API int           DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2);\n    IMGUI_API bool          DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max);\n    IMGUI_API bool          DataTypeIsZero(ImGuiDataType data_type, const void* p_data);\n\n    // InputText\n    IMGUI_API bool          InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);\n    IMGUI_API void          InputTextDeactivateHook(ImGuiID id);\n    IMGUI_API bool          TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags);\n    IMGUI_API bool          TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL);\n    inline bool             TempInputIsActive(ImGuiID id)       { ImGuiContext& g = *GImGui; return g.ActiveId == id && g.TempInputId == id; }\n    inline ImGuiInputTextState* GetInputTextState(ImGuiID id)   { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active\n    IMGUI_API void          SetNextItemRefVal(ImGuiDataType data_type, void* p_data);\n    inline bool             IsItemActiveAsInputText() { ImGuiContext& g = *GImGui; return g.ActiveId != 0 && g.ActiveId == g.LastItemData.ID && g.InputTextState.ID == g.LastItemData.ID; } // This may be useful to apply workaround that a based on distinguish whenever an item is active as a text input field.\n\n    // Color\n    IMGUI_API void          ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags);\n    IMGUI_API void          ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags);\n    IMGUI_API void          ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags);\n    inline void             SetNextItemColorMarker(ImU32 col) { ImGuiContext& g = *GImGui; g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasColorMarker; g.NextItemData.ColorMarker = col; }\n\n    // Plot\n    IMGUI_API int           PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg);\n\n    // Shade functions (write over already created vertices)\n    IMGUI_API void          ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1);\n    IMGUI_API void          ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp);\n    IMGUI_API void          ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out);\n\n    // Garbage collection\n    IMGUI_API void          GcCompactTransientMiscBuffers();\n    IMGUI_API void          GcCompactTransientWindowBuffers(ImGuiWindow* window);\n    IMGUI_API void          GcAwakeTransientWindowBuffers(ImGuiWindow* window);\n\n    // Error handling, State Recovery\n    IMGUI_API bool          ErrorLog(const char* msg);\n    IMGUI_API void          ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out);\n    IMGUI_API void          ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in);\n    IMGUI_API void          ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in);\n    IMGUI_API void          ErrorCheckUsingSetCursorPosToExtendParentBoundaries();\n    IMGUI_API void          ErrorCheckEndFrameFinalizeErrorTooltip();\n    IMGUI_API bool          BeginErrorTooltip();\n    IMGUI_API void          EndErrorTooltip();\n\n    // Demo Doc Marker for e.g. imgui_explorer\n    IMGUI_API void          DemoMarker(const char* file, int line, const char* section);\n\n    // Debug Tools\n    IMGUI_API void          DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free\n    IMGUI_API void          DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255));\n    IMGUI_API void          DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255));\n    IMGUI_API void          DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255));\n    IMGUI_API void          DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end);\n    IMGUI_API void          DebugLocateItem(ImGuiID target_id);                     // Call sparingly: only 1 at the same time!\n    IMGUI_API void          DebugLocateItemOnHover(ImGuiID target_id);              // Only call on reaction to a mouse Hover: because only 1 at the same time!\n    IMGUI_API void          DebugLocateItemResolveWithLastItem();\n    IMGUI_API void          DebugBreakClearData();\n    IMGUI_API bool          DebugBreakButton(const char* label, const char* description_of_location);\n    IMGUI_API void          DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location);\n    IMGUI_API void          ShowFontAtlas(ImFontAtlas* atlas);\n    IMGUI_API ImU64         DebugTextureIDToU64(ImTextureID tex_id);\n    IMGUI_API void          DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end);\n    IMGUI_API void          DebugNodeColumns(ImGuiOldColumns* columns);\n    IMGUI_API void          DebugNodeDockNode(ImGuiDockNode* node, const char* label);\n    IMGUI_API void          DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label);\n    IMGUI_API void          DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb);\n    IMGUI_API void          DebugNodeFont(ImFont* font);\n    IMGUI_API void          DebugNodeFontGlyphsForSrcMask(ImFont* font, ImFontBaked* baked, int src_mask);\n    IMGUI_API void          DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph);\n    IMGUI_API void          DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRect* highlight_rect = NULL); // ID used to facilitate persisting the \"current\" texture.\n    IMGUI_API void          DebugNodeStorage(ImGuiStorage* storage, const char* label);\n    IMGUI_API void          DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label);\n    IMGUI_API void          DebugNodeTable(ImGuiTable* table);\n    IMGUI_API void          DebugNodeTableSettings(ImGuiTableSettings* settings);\n    IMGUI_API void          DebugNodeInputTextState(ImGuiInputTextState* state);\n    IMGUI_API void          DebugNodeTypingSelectState(ImGuiTypingSelectState* state);\n    IMGUI_API void          DebugNodeMultiSelectState(ImGuiMultiSelectState* state);\n    IMGUI_API void          DebugNodeWindow(ImGuiWindow* window, const char* label);\n    IMGUI_API void          DebugNodeWindowSettings(ImGuiWindowSettings* settings);\n    IMGUI_API void          DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label);\n    IMGUI_API void          DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack);\n    IMGUI_API void          DebugNodeViewport(ImGuiViewportP* viewport);\n    IMGUI_API void          DebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor, const char* label, int idx);\n    IMGUI_API void          DebugRenderKeyboardPreview(ImDrawList* draw_list);\n    IMGUI_API void          DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb);\n\n    // Obsolete functions\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    //inline void   SetItemUsingMouseWheel()                                            { SetItemKeyOwner(ImGuiKey_MouseWheelY); }      // Changed in 1.89\n    //inline bool   TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0)    { return TreeNodeUpdateNextOpen(id, flags); }   // Renamed in 1.89\n    //inline bool   IsKeyPressedMap(ImGuiKey key, bool repeat = true)                   { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity!\n\n    // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister():\n    //  (Old) IMGUI_VERSION_NUM  < 18209: using 'ItemAdd(....)'                              and 'bool tab_focused = FocusableItemRegister(...)'\n    //  (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)'  and 'bool tab_focused = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0'\n    //  (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)'     and 'bool tab_focused = (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))'\n    //inline bool   FocusableItemRegister(ImGuiWindow* window, ImGuiID id)              // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd()\n    //inline void   FocusableItemUnregister(ImGuiWindow* window)                        // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem\n#endif\n\n} // namespace ImGui\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFontLoader\n//-----------------------------------------------------------------------------\n\n// Hooks and storage for a given font backend.\n// This structure is likely to evolve as we add support for incremental atlas updates.\n// Conceptually this could be public, but API is still going to be evolve.\nstruct ImFontLoader\n{\n    const char*     Name;\n    bool            (*LoaderInit)(ImFontAtlas* atlas);\n    void            (*LoaderShutdown)(ImFontAtlas* atlas);\n    bool            (*FontSrcInit)(ImFontAtlas* atlas, ImFontConfig* src);\n    void            (*FontSrcDestroy)(ImFontAtlas* atlas, ImFontConfig* src);\n    bool            (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint);\n    bool            (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);\n    void            (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);\n    bool            (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x);\n\n    // Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations.\n    // FIXME: At this point the two other types of buffers may be managed by core to be consistent?\n    size_t          FontBakedSrcLoaderDataSize;\n\n    ImFontLoader()  { memset((void*)this, 0, sizeof(*this)); }\n};\n\n#ifdef IMGUI_ENABLE_STB_TRUETYPE\nIMGUI_API const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype();\n#endif\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\ntypedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92.0] The types are not actually compatible but we provide this as a compile-time error report helper.\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFontAtlas internal API\n//-----------------------------------------------------------------------------\n\n#define IMGUI_FONT_SIZE_MAX                                     (512.0f)\n#define IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE      (128.0f)\n\n// Helpers: ImTextureRef ==/!= operators provided as convenience\n// (note that _TexID and _TexData are never set simultaneously)\ninline bool operator==(const ImTextureRef& lhs, const ImTextureRef& rhs)    { return lhs._TexID == rhs._TexID && lhs._TexData == rhs._TexData; }\ninline bool operator!=(const ImTextureRef& lhs, const ImTextureRef& rhs)    { return lhs._TexID != rhs._TexID || lhs._TexData != rhs._TexData; }\n\n// Refer to ImFontAtlasPackGetRect() to better understand how this works.\n#define ImFontAtlasRectId_IndexMask_        (0x0007FFFF)    // 20-bits signed: index to access builder->RectsIndex[].\n#define ImFontAtlasRectId_GenerationMask_   (0x3FF00000)    // 10-bits: entry generation, so each ID is unique and get can safely detected old identifiers.\n#define ImFontAtlasRectId_GenerationShift_  (20)\ninline int               ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id)       { return (id & ImFontAtlasRectId_IndexMask_); }\ninline unsigned int      ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id)  { return (unsigned int)(id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; }\ninline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx)     { IM_ASSERT(index_idx >= 0 && index_idx <= ImFontAtlasRectId_IndexMask_ && gen_idx <= (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); }\n\n// Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles)\n// User are returned ImFontAtlasRectId values which are meant to be persistent.\n// We handle this with an indirection. While Rects[] may be in theory shuffled, compacted etc., RectsIndex[] cannot it is keyed by ImFontAtlasRectId.\n// RectsIndex[] is used both as an index into Rects[] and an index into itself. This is basically a free-list. See ImFontAtlasBuildAllocRectIndexEntry() code.\n// Having this also makes it easier to e.g. sort rectangles during repack.\nstruct ImFontAtlasRectEntry\n{\n    int                 TargetIndex : 20;   // When Used: ImFontAtlasRectId -> into Rects[]. When unused: index to next unused RectsIndex[] slot to consume free-list.\n    unsigned int        Generation : 10;    // Increased each time the entry is reused for a new rectangle.\n    unsigned int        IsUsed : 1;\n};\n\n// Data available to potential texture post-processing functions\nstruct ImFontAtlasPostProcessData\n{\n    ImFontAtlas*        FontAtlas;\n    ImFont*             Font;\n    ImFontConfig*       FontSrc;\n    ImFontBaked*        FontBaked;\n    ImFontGlyph*        Glyph;\n\n    // Pixel data\n    void*               Pixels;\n    ImTextureFormat     Format;\n    int                 Pitch;\n    int                 Width;\n    int                 Height;\n};\n\n// We avoid dragging imstb_rectpack.h into public header (partly because binding generators are having issues with it)\n#ifdef IMGUI_STB_NAMESPACE\nnamespace IMGUI_STB_NAMESPACE { struct stbrp_node; }\ntypedef IMGUI_STB_NAMESPACE::stbrp_node stbrp_node_im;\n#else\nstruct stbrp_node;\ntypedef stbrp_node stbrp_node_im;\n#endif\nstruct stbrp_context_opaque { char data[80]; };\n\n// Internal storage for incrementally packing and building a ImFontAtlas\nstruct ImFontAtlasBuilder\n{\n    stbrp_context_opaque        PackContext;            // Actually 'stbrp_context' but we don't want to define this in the header file.\n    ImVector<stbrp_node_im>     PackNodes;\n    ImVector<ImTextureRect>     Rects;\n    ImVector<ImFontAtlasRectEntry> RectsIndex;          // ImFontAtlasRectId -> index into Rects[]\n    ImVector<unsigned char>     TempBuffer;             // Misc scratch buffer\n    int                         RectsIndexFreeListStart;// First unused entry\n    int                         RectsPackedCount;       // Number of packed rectangles.\n    int                         RectsPackedSurface;     // Number of packed pixels. Used when compacting to heuristically find the ideal texture size.\n    int                         RectsDiscardedCount;\n    int                         RectsDiscardedSurface;\n    int                         FrameCount;             // Current frame count\n    ImVec2i                     MaxRectSize;            // Largest rectangle to pack (de-facto used as a \"minimum texture size\")\n    ImVec2i                     MaxRectBounds;          // Bottom-right most used pixels\n    bool                        LockDisableResize;      // Disable resizing texture\n    bool                        PreloadedAllGlyphsRanges; // Set when missing ImGuiBackendFlags_RendererHasTextures features forces atlas to preload everything.\n\n    // Cache of all ImFontBaked\n    ImStableVector<ImFontBaked,32> BakedPool;\n    ImGuiStorage                BakedMap;               // BakedId --> ImFontBaked*\n    int                         BakedDiscardedCount;\n\n    // Custom rectangle identifiers\n    ImFontAtlasRectId           PackIdMouseCursors;     // White pixel + mouse cursors. Also happen to be fallback in case of packing failure.\n    ImFontAtlasRectId           PackIdLinesTexData;\n\n    ImFontAtlasBuilder()        { memset((void*)this, 0, sizeof(*this)); FrameCount = -1; RectsIndexFreeListStart = -1; PackIdMouseCursors = PackIdLinesTexData = -1; }\n};\n\nIMGUI_API void              ImFontAtlasBuildInit(ImFontAtlas* atlas);\nIMGUI_API void              ImFontAtlasBuildDestroy(ImFontAtlas* atlas);\nIMGUI_API void              ImFontAtlasBuildMain(ImFontAtlas* atlas);\nIMGUI_API void              ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* font_loader);\nIMGUI_API void              ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font);\nIMGUI_API void              ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas);\nIMGUI_API void              ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char);\nIMGUI_API void              ImFontAtlasBuildClear(ImFontAtlas* atlas); // Clear output and custom rects\n\nIMGUI_API ImTextureData*    ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h);\nIMGUI_API void              ImFontAtlasTextureMakeSpace(ImFontAtlas* atlas);\nIMGUI_API void              ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h);\nIMGUI_API void              ImFontAtlasTextureGrow(ImFontAtlas* atlas, int old_w = -1, int old_h = -1);\nIMGUI_API void              ImFontAtlasTextureCompact(ImFontAtlas* atlas);\nIMGUI_API ImVec2i           ImFontAtlasTextureGetSizeEstimate(ImFontAtlas* atlas);\n\nIMGUI_API void              ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src);\nIMGUI_API void              ImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas); // Legacy\nIMGUI_API void              ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v);\nIMGUI_API void              ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames);\n\nIMGUI_API bool              ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src);\nIMGUI_API void              ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src);\nIMGUI_API void              ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src);\nIMGUI_API bool              ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font); // Using FontDestroyOutput/FontInitOutput sequence useful notably if font loader params have changed\nIMGUI_API void              ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font);\nIMGUI_API void              ImFontAtlasFontRebuildOutput(ImFontAtlas* atlas, ImFont* font);\nIMGUI_API void              ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_frames);\n\nIMGUI_API ImGuiID           ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float rasterizer_density);\nIMGUI_API ImFontBaked*      ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density);\nIMGUI_API ImFontBaked*      ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density);\nIMGUI_API ImFontBaked*      ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id);\nIMGUI_API void              ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked);\nIMGUI_API ImFontGlyph*      ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph);\nIMGUI_API void              ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x);\nIMGUI_API void              ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph);\nIMGUI_API void              ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch);\n\nIMGUI_API void              ImFontAtlasPackInit(ImFontAtlas* atlas);\nIMGUI_API ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFontAtlasRectEntry* overwrite_entry = NULL);\nIMGUI_API ImTextureRect*    ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id);\nIMGUI_API ImTextureRect*    ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id);\nIMGUI_API void              ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id);\n\nIMGUI_API void              ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures);\nIMGUI_API void              ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);\nIMGUI_API void              ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);\nIMGUI_API void              ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex);\nIMGUI_API void              ImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas);\n\nIMGUI_API void              ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h);\nIMGUI_API void              ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data);\nIMGUI_API void              ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor);\nIMGUI_API void              ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col);\nIMGUI_API void              ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h);\nIMGUI_API void              ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h);\n\nIMGUI_API int               ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format);\nIMGUI_API const char*       ImTextureDataGetStatusName(ImTextureStatus status);\nIMGUI_API const char*       ImTextureDataGetFormatName(ImTextureFormat format);\n\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\nIMGUI_API void              ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas);\n#endif\n\nIMGUI_API bool      ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Test Engine specific hooks (imgui_test_engine)\n//-----------------------------------------------------------------------------\n\n#ifdef IMGUI_ENABLE_TEST_ENGINE\nextern void         ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data);           // item_data may be NULL\nextern void         ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags);\nextern void         ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...);\nextern const char*  ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id);\n\n// In IMGUI_VERSION_NUM >= 18934: changed IMGUI_TEST_ENGINE_ITEM_ADD(bb,id) to IMGUI_TEST_ENGINE_ITEM_ADD(id,bb,item_data);\n#define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA)      if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _ID, _BB, _ITEM_DATA)    // Register item bounding box\n#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS)      if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS)    // Register item label and status flags (optional)\n#define IMGUI_TEST_ENGINE_LOG(_FMT,...)                     ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__)                                      // Custom log entry from user land into test log\n#else\n#define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA)      ((void)0)\n#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS)      ((void)g)\n#endif\n\n//-----------------------------------------------------------------------------\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#elif defined(__GNUC__)\n#pragma GCC diagnostic pop\n#endif\n\n#ifdef _MSC_VER\n#pragma warning (pop)\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/include/imstb_rectpack.h",
    "content": "// [DEAR IMGUI]\n// This is a slightly modified version of stb_rect_pack.h 1.01.\n// Grep for [DEAR IMGUI] to find the changes.\n// \n// stb_rect_pack.h - v1.01 - public domain - rectangle packing\n// Sean Barrett 2014\n//\n// Useful for e.g. packing rectangular textures into an atlas.\n// Does not do rotation.\n//\n// Before #including,\n//\n//    #define STB_RECT_PACK_IMPLEMENTATION\n//\n// in the file that you want to have the implementation.\n//\n// Not necessarily the awesomest packing method, but better than\n// the totally naive one in stb_truetype (which is primarily what\n// this is meant to replace).\n//\n// Has only had a few tests run, may have issues.\n//\n// More docs to come.\n//\n// No memory allocations; uses qsort() and assert() from stdlib.\n// Can override those by defining STBRP_SORT and STBRP_ASSERT.\n//\n// This library currently uses the Skyline Bottom-Left algorithm.\n//\n// Please note: better rectangle packers are welcome! Please\n// implement them to the same API, but with a different init\n// function.\n//\n// Credits\n//\n//  Library\n//    Sean Barrett\n//  Minor features\n//    Martins Mozeiko\n//    github:IntellectualKitty\n//\n//  Bugfixes / warning fixes\n//    Jeremy Jaussaud\n//    Fabian Giesen\n//\n// Version history:\n//\n//     1.01  (2021-07-11)  always use large rect mode, expose STBRP__MAXVAL in public section\n//     1.00  (2019-02-25)  avoid small space waste; gracefully fail too-wide rectangles\n//     0.99  (2019-02-07)  warning fixes\n//     0.11  (2017-03-03)  return packing success/fail result\n//     0.10  (2016-10-25)  remove cast-away-const to avoid warnings\n//     0.09  (2016-08-27)  fix compiler warnings\n//     0.08  (2015-09-13)  really fix bug with empty rects (w=0 or h=0)\n//     0.07  (2015-09-13)  fix bug with empty rects (w=0 or h=0)\n//     0.06  (2015-04-15)  added STBRP_SORT to allow replacing qsort\n//     0.05:  added STBRP_ASSERT to allow replacing assert\n//     0.04:  fixed minor bug in STBRP_LARGE_RECTS support\n//     0.01:  initial release\n//\n// LICENSE\n//\n//   See end of file for license information.\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//       INCLUDE SECTION\n//\n\n#ifndef STB_INCLUDE_STB_RECT_PACK_H\n#define STB_INCLUDE_STB_RECT_PACK_H\n\n#define STB_RECT_PACK_VERSION  1\n\n#ifdef STBRP_STATIC\n#define STBRP_DEF static\n#else\n#define STBRP_DEF extern\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct stbrp_context stbrp_context;\ntypedef struct stbrp_node    stbrp_node;\ntypedef struct stbrp_rect    stbrp_rect;\n\ntypedef int            stbrp_coord;\n\n#define STBRP__MAXVAL  0x7fffffff\n// Mostly for internal use, but this is the maximum supported coordinate value.\n\nSTBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);\n// Assign packed locations to rectangles. The rectangles are of type\n// 'stbrp_rect' defined below, stored in the array 'rects', and there\n// are 'num_rects' many of them.\n//\n// Rectangles which are successfully packed have the 'was_packed' flag\n// set to a non-zero value and 'x' and 'y' store the minimum location\n// on each axis (i.e. bottom-left in cartesian coordinates, top-left\n// if you imagine y increasing downwards). Rectangles which do not fit\n// have the 'was_packed' flag set to 0.\n//\n// You should not try to access the 'rects' array from another thread\n// while this function is running, as the function temporarily reorders\n// the array while it executes.\n//\n// To pack into another rectangle, you need to call stbrp_init_target\n// again. To continue packing into the same rectangle, you can call\n// this function again. Calling this multiple times with multiple rect\n// arrays will probably produce worse packing results than calling it\n// a single time with the full rectangle array, but the option is\n// available.\n//\n// The function returns 1 if all of the rectangles were successfully\n// packed and 0 otherwise.\n\nstruct stbrp_rect\n{\n   // reserved for your use:\n   int            id;\n\n   // input:\n   stbrp_coord    w, h;\n\n   // output:\n   stbrp_coord    x, y;\n   int            was_packed;  // non-zero if valid packing\n\n}; // 16 bytes, nominally\n\n\nSTBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);\n// Initialize a rectangle packer to:\n//    pack a rectangle that is 'width' by 'height' in dimensions\n//    using temporary storage provided by the array 'nodes', which is 'num_nodes' long\n//\n// You must call this function every time you start packing into a new target.\n//\n// There is no \"shutdown\" function. The 'nodes' memory must stay valid for\n// the following stbrp_pack_rects() call (or calls), but can be freed after\n// the call (or calls) finish.\n//\n// Note: to guarantee best results, either:\n//       1. make sure 'num_nodes' >= 'width'\n//   or  2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'\n//\n// If you don't do either of the above things, widths will be quantized to multiples\n// of small integers to guarantee the algorithm doesn't run out of temporary storage.\n//\n// If you do #2, then the non-quantized algorithm will be used, but the algorithm\n// may run out of temporary storage and be unable to pack some rectangles.\n\nSTBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);\n// Optionally call this function after init but before doing any packing to\n// change the handling of the out-of-temp-memory scenario, described above.\n// If you call init again, this will be reset to the default (false).\n\n\nSTBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);\n// Optionally select which packing heuristic the library should use. Different\n// heuristics will produce better/worse results for different data sets.\n// If you call init again, this will be reset to the default.\n\nenum\n{\n   STBRP_HEURISTIC_Skyline_default=0,\n   STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,\n   STBRP_HEURISTIC_Skyline_BF_sortHeight\n};\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// the details of the following structures don't matter to you, but they must\n// be visible so you can handle the memory allocations for them\n\nstruct stbrp_node\n{\n   stbrp_coord  x,y;\n   stbrp_node  *next;\n};\n\nstruct stbrp_context\n{\n   int width;\n   int height;\n   int align;\n   int init_mode;\n   int heuristic;\n   int num_nodes;\n   stbrp_node *active_head;\n   stbrp_node *free_head;\n   stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'\n};\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//     IMPLEMENTATION SECTION\n//\n\n#ifdef STB_RECT_PACK_IMPLEMENTATION\n#ifndef STBRP_SORT\n#include <stdlib.h>\n#define STBRP_SORT qsort\n#endif\n\n#ifndef STBRP_ASSERT\n#include <assert.h>\n#define STBRP_ASSERT assert\n#endif\n\n#ifdef _MSC_VER\n#define STBRP__NOTUSED(v)  (void)(v)\n#define STBRP__CDECL       __cdecl\n#else\n#define STBRP__NOTUSED(v)  (void)sizeof(v)\n#define STBRP__CDECL\n#endif\n\nenum\n{\n   STBRP__INIT_skyline = 1\n};\n\nSTBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)\n{\n   switch (context->init_mode) {\n      case STBRP__INIT_skyline:\n         STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);\n         context->heuristic = heuristic;\n         break;\n      default:\n         STBRP_ASSERT(0);\n   }\n}\n\nSTBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)\n{\n   if (allow_out_of_mem)\n      // if it's ok to run out of memory, then don't bother aligning them;\n      // this gives better packing, but may fail due to OOM (even though\n      // the rectangles easily fit). @TODO a smarter approach would be to only\n      // quantize once we've hit OOM, then we could get rid of this parameter.\n      context->align = 1;\n   else {\n      // if it's not ok to run out of memory, then quantize the widths\n      // so that num_nodes is always enough nodes.\n      //\n      // I.e. num_nodes * align >= width\n      //                  align >= width / num_nodes\n      //                  align = ceil(width/num_nodes)\n\n      context->align = (context->width + context->num_nodes-1) / context->num_nodes;\n   }\n}\n\nSTBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)\n{\n   int i;\n\n   for (i=0; i < num_nodes-1; ++i)\n      nodes[i].next = &nodes[i+1];\n   nodes[i].next = NULL;\n   context->init_mode = STBRP__INIT_skyline;\n   context->heuristic = STBRP_HEURISTIC_Skyline_default;\n   context->free_head = &nodes[0];\n   context->active_head = &context->extra[0];\n   context->width = width;\n   context->height = height;\n   context->num_nodes = num_nodes;\n   stbrp_setup_allow_out_of_mem(context, 0);\n\n   // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)\n   context->extra[0].x = 0;\n   context->extra[0].y = 0;\n   context->extra[0].next = &context->extra[1];\n   context->extra[1].x = (stbrp_coord) width;\n   context->extra[1].y = (1<<30);\n   context->extra[1].next = NULL;\n}\n\n// find minimum y position if it starts at x1\nstatic int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)\n{\n   stbrp_node *node = first;\n   int x1 = x0 + width;\n   int min_y, visited_width, waste_area;\n\n   STBRP__NOTUSED(c);\n\n   STBRP_ASSERT(first->x <= x0);\n\n   #if 0\n   // skip in case we're past the node\n   while (node->next->x <= x0)\n      ++node;\n   #else\n   STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency\n   #endif\n\n   STBRP_ASSERT(node->x <= x0);\n\n   min_y = 0;\n   waste_area = 0;\n   visited_width = 0;\n   while (node->x < x1) {\n      if (node->y > min_y) {\n         // raise min_y higher.\n         // we've accounted for all waste up to min_y,\n         // but we'll now add more waste for everything we've visited\n         waste_area += visited_width * (node->y - min_y);\n         min_y = node->y;\n         // the first time through, visited_width might be reduced\n         if (node->x < x0)\n            visited_width += node->next->x - x0;\n         else\n            visited_width += node->next->x - node->x;\n      } else {\n         // add waste area\n         int under_width = node->next->x - node->x;\n         if (under_width + visited_width > width)\n            under_width = width - visited_width;\n         waste_area += under_width * (min_y - node->y);\n         visited_width += under_width;\n      }\n      node = node->next;\n   }\n\n   *pwaste = waste_area;\n   return min_y;\n}\n\ntypedef struct\n{\n   int x,y;\n   stbrp_node **prev_link;\n} stbrp__findresult;\n\nstatic stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)\n{\n   int best_waste = (1<<30), best_x, best_y = (1 << 30);\n   stbrp__findresult fr;\n   stbrp_node **prev, *node, *tail, **best = NULL;\n\n   // align to multiple of c->align\n   width = (width + c->align - 1);\n   width -= width % c->align;\n   STBRP_ASSERT(width % c->align == 0);\n\n   // if it can't possibly fit, bail immediately\n   if (width > c->width || height > c->height) {\n      fr.prev_link = NULL;\n      fr.x = fr.y = 0;\n      return fr;\n   }\n\n   node = c->active_head;\n   prev = &c->active_head;\n   while (node->x + width <= c->width) {\n      int y,waste;\n      y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);\n      if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL\n         // bottom left\n         if (y < best_y) {\n            best_y = y;\n            best = prev;\n         }\n      } else {\n         // best-fit\n         if (y + height <= c->height) {\n            // can only use it if it first vertically\n            if (y < best_y || (y == best_y && waste < best_waste)) {\n               best_y = y;\n               best_waste = waste;\n               best = prev;\n            }\n         }\n      }\n      prev = &node->next;\n      node = node->next;\n   }\n\n   best_x = (best == NULL) ? 0 : (*best)->x;\n\n   // if doing best-fit (BF), we also have to try aligning right edge to each node position\n   //\n   // e.g, if fitting\n   //\n   //     ____________________\n   //    |____________________|\n   //\n   //            into\n   //\n   //   |                         |\n   //   |             ____________|\n   //   |____________|\n   //\n   // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned\n   //\n   // This makes BF take about 2x the time\n\n   if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {\n      tail = c->active_head;\n      node = c->active_head;\n      prev = &c->active_head;\n      // find first node that's admissible\n      while (tail->x < width)\n         tail = tail->next;\n      while (tail) {\n         int xpos = tail->x - width;\n         int y,waste;\n         STBRP_ASSERT(xpos >= 0);\n         // find the left position that matches this\n         while (node->next->x <= xpos) {\n            prev = &node->next;\n            node = node->next;\n         }\n         STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);\n         y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);\n         if (y + height <= c->height) {\n            if (y <= best_y) {\n               if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {\n                  best_x = xpos;\n                  //STBRP_ASSERT(y <= best_y); [DEAR IMGUI]\n                  best_y = y;\n                  best_waste = waste;\n                  best = prev;\n               }\n            }\n         }\n         tail = tail->next;\n      }\n   }\n\n   fr.prev_link = best;\n   fr.x = best_x;\n   fr.y = best_y;\n   return fr;\n}\n\nstatic stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)\n{\n   // find best position according to heuristic\n   stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);\n   stbrp_node *node, *cur;\n\n   // bail if:\n   //    1. it failed\n   //    2. the best node doesn't fit (we don't always check this)\n   //    3. we're out of memory\n   if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {\n      res.prev_link = NULL;\n      return res;\n   }\n\n   // on success, create new node\n   node = context->free_head;\n   node->x = (stbrp_coord) res.x;\n   node->y = (stbrp_coord) (res.y + height);\n\n   context->free_head = node->next;\n\n   // insert the new node into the right starting point, and\n   // let 'cur' point to the remaining nodes needing to be\n   // stitched back in\n\n   cur = *res.prev_link;\n   if (cur->x < res.x) {\n      // preserve the existing one, so start testing with the next one\n      stbrp_node *next = cur->next;\n      cur->next = node;\n      cur = next;\n   } else {\n      *res.prev_link = node;\n   }\n\n   // from here, traverse cur and free the nodes, until we get to one\n   // that shouldn't be freed\n   while (cur->next && cur->next->x <= res.x + width) {\n      stbrp_node *next = cur->next;\n      // move the current node to the free list\n      cur->next = context->free_head;\n      context->free_head = cur;\n      cur = next;\n   }\n\n   // stitch the list back in\n   node->next = cur;\n\n   if (cur->x < res.x + width)\n      cur->x = (stbrp_coord) (res.x + width);\n\n#ifdef _DEBUG\n   cur = context->active_head;\n   while (cur->x < context->width) {\n      STBRP_ASSERT(cur->x < cur->next->x);\n      cur = cur->next;\n   }\n   STBRP_ASSERT(cur->next == NULL);\n\n   {\n      int count=0;\n      cur = context->active_head;\n      while (cur) {\n         cur = cur->next;\n         ++count;\n      }\n      cur = context->free_head;\n      while (cur) {\n         cur = cur->next;\n         ++count;\n      }\n      STBRP_ASSERT(count == context->num_nodes+2);\n   }\n#endif\n\n   return res;\n}\n\nstatic int STBRP__CDECL rect_height_compare(const void *a, const void *b)\n{\n   const stbrp_rect *p = (const stbrp_rect *) a;\n   const stbrp_rect *q = (const stbrp_rect *) b;\n   if (p->h > q->h)\n      return -1;\n   if (p->h < q->h)\n      return  1;\n   return (p->w > q->w) ? -1 : (p->w < q->w);\n}\n\nstatic int STBRP__CDECL rect_original_order(const void *a, const void *b)\n{\n   const stbrp_rect *p = (const stbrp_rect *) a;\n   const stbrp_rect *q = (const stbrp_rect *) b;\n   return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);\n}\n\nSTBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)\n{\n   int i, all_rects_packed = 1;\n\n   // we use the 'was_packed' field internally to allow sorting/unsorting\n   for (i=0; i < num_rects; ++i) {\n      rects[i].was_packed = i;\n   }\n\n   // sort according to heuristic\n   STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);\n\n   for (i=0; i < num_rects; ++i) {\n      if (rects[i].w == 0 || rects[i].h == 0) {\n         rects[i].x = rects[i].y = 0;  // empty rect needs no space\n      } else {\n         stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);\n         if (fr.prev_link) {\n            rects[i].x = (stbrp_coord) fr.x;\n            rects[i].y = (stbrp_coord) fr.y;\n         } else {\n            rects[i].x = rects[i].y = STBRP__MAXVAL;\n         }\n      }\n   }\n\n   // unsort\n   STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);\n\n   // set was_packed flags and all_rects_packed status\n   for (i=0; i < num_rects; ++i) {\n      rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);\n      if (!rects[i].was_packed)\n         all_rects_packed = 0;\n   }\n\n   // return the all_rects_packed status\n   return all_rects_packed;\n}\n#endif\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/include/imstb_textedit.h",
    "content": "// [DEAR IMGUI]\n// This is a slightly modified version of stb_textedit.h 1.14.\n// Those changes would need to be pushed into nothings/stb:\n// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)\n// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)\n// - Added name to struct or it may be forward declared in our code.\n// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)\n// - Changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.\n// Grep for [DEAR IMGUI] to find some changes.\n// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*\n\n// stb_textedit.h - v1.14  - public domain - Sean Barrett\n// Development of this library was sponsored by RAD Game Tools\n//\n// This C header file implements the guts of a multi-line text-editing\n// widget; you implement display, word-wrapping, and low-level string\n// insertion/deletion, and stb_textedit will map user inputs into\n// insertions & deletions, plus updates to the cursor position,\n// selection state, and undo state.\n//\n// It is intended for use in games and other systems that need to build\n// their own custom widgets and which do not have heavy text-editing\n// requirements (this library is not recommended for use for editing large\n// texts, as its performance does not scale and it has limited undo).\n//\n// Non-trivial behaviors are modelled after Windows text controls.\n//\n//\n// LICENSE\n//\n// See end of file for license information.\n//\n//\n// DEPENDENCIES\n//\n// Uses the C runtime function 'memmove', which you can override\n// by defining IMSTB_TEXTEDIT_memmove before the implementation.\n// Uses no other functions. Performs no runtime allocations.\n//\n//\n// VERSION HISTORY\n//\n//   !!!! (2025-10-23) changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.\n//   1.14 (2021-07-11) page up/down, various fixes\n//   1.13 (2019-02-07) fix bug in undo size management\n//   1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash\n//   1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield\n//   1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual\n//   1.9  (2016-08-27) customizable move-by-word\n//   1.8  (2016-04-02) better keyboard handling when mouse button is down\n//   1.7  (2015-09-13) change y range handling in case baseline is non-0\n//   1.6  (2015-04-15) allow STB_TEXTEDIT_memmove\n//   1.5  (2014-09-10) add support for secondary keys for OS X\n//   1.4  (2014-08-17) fix signed/unsigned warnings\n//   1.3  (2014-06-19) fix mouse clicking to round to nearest char boundary\n//   1.2  (2014-05-27) fix some RAD types that had crept into the new code\n//   1.1  (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE )\n//   1.0  (2012-07-26) improve documentation, initial public release\n//   0.3  (2012-02-24) bugfixes, single-line mode; insert mode\n//   0.2  (2011-11-28) fixes to undo/redo\n//   0.1  (2010-07-08) initial version\n//\n// ADDITIONAL CONTRIBUTORS\n//\n//   Ulf Winklemann: move-by-word in 1.1\n//   Fabian Giesen: secondary key inputs in 1.5\n//   Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6\n//   Louis Schnellbach: page up/down in 1.14\n//\n//   Bugfixes:\n//      Scott Graham\n//      Daniel Keller\n//      Omar Cornut\n//      Dan Thompson\n//\n// USAGE\n//\n// This file behaves differently depending on what symbols you define\n// before including it.\n//\n//\n// Header-file mode:\n//\n//   If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this,\n//   it will operate in \"header file\" mode. In this mode, it declares a\n//   single public symbol, STB_TexteditState, which encapsulates the current\n//   state of a text widget (except for the string, which you will store\n//   separately).\n//\n//   To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a\n//   primitive type that defines a single character (e.g. char, wchar_t, etc).\n//\n//   To save space or increase undo-ability, you can optionally define the\n//   following things that are used by the undo system:\n//\n//      STB_TEXTEDIT_POSITIONTYPE         small int type encoding a valid cursor position\n//      STB_TEXTEDIT_UNDOSTATECOUNT       the number of undo states to allow\n//      STB_TEXTEDIT_UNDOCHARCOUNT        the number of characters to store in the undo buffer\n//\n//   If you don't define these, they are set to permissive types and\n//   moderate sizes. The undo system does no memory allocations, so\n//   it grows STB_TexteditState by the worst-case storage which is (in bytes):\n//\n//        [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATECOUNT\n//      +          sizeof(STB_TEXTEDIT_CHARTYPE)      * STB_TEXTEDIT_UNDOCHARCOUNT\n//\n//\n// Implementation mode:\n//\n//   If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it\n//   will compile the implementation of the text edit widget, depending\n//   on a large number of symbols which must be defined before the include.\n//\n//   The implementation is defined only as static functions. You will then\n//   need to provide your own APIs in the same file which will access the\n//   static functions.\n//\n//   The basic concept is that you provide a \"string\" object which\n//   behaves like an array of characters. stb_textedit uses indices to\n//   refer to positions in the string, implicitly representing positions\n//   in the displayed textedit. This is true for both plain text and\n//   rich text; even with rich text stb_truetype interacts with your\n//   code as if there was an array of all the displayed characters.\n//\n// Symbols that must be the same in header-file and implementation mode:\n//\n//     STB_TEXTEDIT_CHARTYPE             the character type\n//     STB_TEXTEDIT_POSITIONTYPE         small type that is a valid cursor position\n//     STB_TEXTEDIT_UNDOSTATECOUNT       the number of undo states to allow\n//     STB_TEXTEDIT_UNDOCHARCOUNT        the number of characters to store in the undo buffer\n//\n// Symbols you must define for implementation mode:\n//\n//    STB_TEXTEDIT_STRING               the type of object representing a string being edited,\n//                                      typically this is a wrapper object with other data you need\n//\n//    STB_TEXTEDIT_STRINGLEN(obj)       the length of the string (ideally O(1))\n//    STB_TEXTEDIT_LAYOUTROW(&r,obj,n)  returns the results of laying out a line of characters\n//                                        starting from character #n (see discussion below)\n//    STB_TEXTEDIT_GETWIDTH(obj,n,i)    returns the pixel delta from the xpos of the i'th character\n//                                        to the xpos of the i+1'th char for a line of characters\n//                                        starting at character #n (i.e. accounts for kerning\n//                                        with previous char)\n//    STB_TEXTEDIT_KEYTOTEXT(k)         maps a keyboard input to an insertable character\n//                                        (return type is int, -1 means not valid to insert)\n//                                        (not supported if you want to use UTF-8, see below)\n//    STB_TEXTEDIT_GETCHAR(obj,i)       returns the i'th character of obj, 0-based\n//    STB_TEXTEDIT_NEWLINE              the character returned by _GETCHAR() we recognize\n//                                        as manually wordwrapping for end-of-line positioning\n//\n//    STB_TEXTEDIT_DELETECHARS(obj,i,n)      delete n characters starting at i\n//    STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n)   try to insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)\n//                                           returns number of characters actually inserted. [DEAR IMGUI]\n//\n//    STB_TEXTEDIT_K_SHIFT       a power of two that is or'd in to a keyboard input to represent the shift key\n//\n//    STB_TEXTEDIT_K_LEFT        keyboard input to move cursor left\n//    STB_TEXTEDIT_K_RIGHT       keyboard input to move cursor right\n//    STB_TEXTEDIT_K_UP          keyboard input to move cursor up\n//    STB_TEXTEDIT_K_DOWN        keyboard input to move cursor down\n//    STB_TEXTEDIT_K_PGUP        keyboard input to move cursor up a page\n//    STB_TEXTEDIT_K_PGDOWN      keyboard input to move cursor down a page\n//    STB_TEXTEDIT_K_LINESTART   keyboard input to move cursor to start of line  // e.g. HOME\n//    STB_TEXTEDIT_K_LINEEND     keyboard input to move cursor to end of line    // e.g. END\n//    STB_TEXTEDIT_K_TEXTSTART   keyboard input to move cursor to start of text  // e.g. ctrl-HOME\n//    STB_TEXTEDIT_K_TEXTEND     keyboard input to move cursor to end of text    // e.g. ctrl-END\n//    STB_TEXTEDIT_K_DELETE      keyboard input to delete selection or character under cursor\n//    STB_TEXTEDIT_K_BACKSPACE   keyboard input to delete selection or character left of cursor\n//    STB_TEXTEDIT_K_UNDO        keyboard input to perform undo\n//    STB_TEXTEDIT_K_REDO        keyboard input to perform redo\n//\n// Optional:\n//    STB_TEXTEDIT_K_INSERT              keyboard input to toggle insert mode\n//    STB_TEXTEDIT_IS_SPACE(ch)          true if character is whitespace (e.g. 'isspace'),\n//                                          required for default WORDLEFT/WORDRIGHT handlers\n//    STB_TEXTEDIT_MOVEWORDLEFT(obj,i)   custom handler for WORDLEFT, returns index to move cursor to\n//    STB_TEXTEDIT_MOVEWORDRIGHT(obj,i)  custom handler for WORDRIGHT, returns index to move cursor to\n//    STB_TEXTEDIT_K_WORDLEFT            keyboard input to move cursor left one word // e.g. ctrl-LEFT\n//    STB_TEXTEDIT_K_WORDRIGHT           keyboard input to move cursor right one word // e.g. ctrl-RIGHT\n//    STB_TEXTEDIT_K_LINESTART2          secondary keyboard input to move cursor to start of line\n//    STB_TEXTEDIT_K_LINEEND2            secondary keyboard input to move cursor to end of line\n//    STB_TEXTEDIT_K_TEXTSTART2          secondary keyboard input to move cursor to start of text\n//    STB_TEXTEDIT_K_TEXTEND2            secondary keyboard input to move cursor to end of text\n//\n// To support UTF-8:\n//\n//    STB_TEXTEDIT_GETPREVCHARINDEX      returns index of previous character\n//    STB_TEXTEDIT_GETNEXTCHARINDEX      returns index of next character\n//    Do NOT define STB_TEXTEDIT_KEYTOTEXT.\n//    Instead, call stb_textedit_text() directly for text contents.\n//\n// Keyboard input must be encoded as a single integer value; e.g. a character code\n// and some bitflags that represent shift states. to simplify the interface, SHIFT must\n// be a bitflag, so we can test the shifted state of cursor movements to allow selection,\n// i.e. (STB_TEXTEDIT_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow.\n//\n// You can encode other things, such as CONTROL or ALT, in additional bits, and\n// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example,\n// my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN\n// bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit,\n// and I pass both WM_KEYDOWN and WM_CHAR events to the \"key\" function in the\n// API below. The control keys will only match WM_KEYDOWN events because of the\n// keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN\n// bit so it only decodes WM_CHAR events.\n//\n// STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed\n// row of characters assuming they start on the i'th character--the width and\n// the height and the number of characters consumed. This allows this library\n// to traverse the entire layout incrementally. You need to compute word-wrapping\n// here.\n//\n// Each textfield keeps its own insert mode state, which is not how normal\n// applications work. To keep an app-wide insert mode, update/copy the\n// \"insert_mode\" field of STB_TexteditState before/after calling API functions.\n//\n// API\n//\n//    void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line)\n//\n//    void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)\n//    void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)\n//    int  stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n//    int  stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)\n//    void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)\n//    void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)\n//\n//    Each of these functions potentially updates the string and updates the\n//    state.\n//\n//      initialize_state:\n//          set the textedit state to a known good default state when initially\n//          constructing the textedit.\n//\n//      click:\n//          call this with the mouse x,y on a mouse down; it will update the cursor\n//          and reset the selection start/end to the cursor point. the x,y must\n//          be relative to the text widget, with (0,0) being the top left.\n//\n//      drag:\n//          call this with the mouse x,y on a mouse drag/up; it will update the\n//          cursor and the selection end point\n//\n//      cut:\n//          call this to delete the current selection; returns true if there was\n//          one. you should FIRST copy the current selection to the system paste buffer.\n//          (To copy, just copy the current selection out of the string yourself.)\n//\n//      paste:\n//          call this to paste text at the current cursor point or over the current\n//          selection if there is one.\n//\n//      key:\n//          call this for keyboard inputs sent to the textfield. you can use it\n//          for \"key down\" events or for \"translated\" key events. if you need to\n//          do both (as in Win32), or distinguish Unicode characters from control\n//          inputs, set a high bit to distinguish the two; then you can define the\n//          various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit\n//          set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is\n//          clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to\n//          anything other type you want before including.\n//          if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are\n//          transformed into text and stb_textedit_text() is automatically called.\n//\n//      text: (added 2025)\n//          call this to directly send text input the textfield, which is required\n//          for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()\n//          cannot infer text length.\n//\n//\n//   When rendering, you can read the cursor position and selection state from\n//   the STB_TexteditState.\n//\n//\n// Notes:\n//\n// This is designed to be usable in IMGUI, so it allows for the possibility of\n// running in an IMGUI that has NOT cached the multi-line layout. For this\n// reason, it provides an interface that is compatible with computing the\n// layout incrementally--we try to make sure we make as few passes through\n// as possible. (For example, to locate the mouse pointer in the text, we\n// could define functions that return the X and Y positions of characters\n// and binary search Y and then X, but if we're doing dynamic layout this\n// will run the layout algorithm many times, so instead we manually search\n// forward in one pass. Similar logic applies to e.g. up-arrow and\n// down-arrow movement.)\n//\n// If it's run in a widget that *has* cached the layout, then this is less\n// efficient, but it's not horrible on modern computers. But you wouldn't\n// want to edit million-line files with it.\n\n\n////////////////////////////////////////////////////////////////////////////\n////////////////////////////////////////////////////////////////////////////\n////\n////   Header-file mode\n////\n////\n\n#ifndef INCLUDE_IMSTB_TEXTEDIT_H\n#define INCLUDE_IMSTB_TEXTEDIT_H\n\n////////////////////////////////////////////////////////////////////////\n//\n//     STB_TexteditState\n//\n// Definition of STB_TexteditState which you should store\n// per-textfield; it includes cursor position, selection state,\n// and undo state.\n//\n\n#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT\n#define IMSTB_TEXTEDIT_UNDOSTATECOUNT   99\n#endif\n#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT\n#define IMSTB_TEXTEDIT_UNDOCHARCOUNT   999\n#endif\n#ifndef IMSTB_TEXTEDIT_CHARTYPE\n#define IMSTB_TEXTEDIT_CHARTYPE        int\n#endif\n#ifndef IMSTB_TEXTEDIT_POSITIONTYPE\n#define IMSTB_TEXTEDIT_POSITIONTYPE    int\n#endif\n\ntypedef struct\n{\n   // private data\n   IMSTB_TEXTEDIT_POSITIONTYPE  where;\n   IMSTB_TEXTEDIT_POSITIONTYPE  insert_length;\n   IMSTB_TEXTEDIT_POSITIONTYPE  delete_length;\n   int                        char_storage;\n} StbUndoRecord;\n\ntypedef struct\n{\n   // private data\n   StbUndoRecord          undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];\n   IMSTB_TEXTEDIT_CHARTYPE  undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];\n   short undo_point, redo_point;\n   int undo_char_point, redo_char_point;\n} StbUndoState;\n\ntypedef struct STB_TexteditState\n{\n   /////////////////////\n   //\n   // public data\n   //\n\n   int cursor;\n   // position of the text cursor within the string\n\n   int select_start;          // selection start point\n   int select_end;\n   // selection start and end point in characters; if equal, no selection.\n   // note that start may be less than or greater than end (e.g. when\n   // dragging the mouse, start is where the initial click was, and you\n   // can drag in either direction)\n\n   unsigned char insert_mode;\n   // each textfield keeps its own insert mode state. to keep an app-wide\n   // insert mode, copy this value in/out of the app state\n\n   int row_count_per_page;\n   // page size in number of row.\n   // this value MUST be set to >0 for pageup or pagedown in multilines documents.\n\n   /////////////////////\n   //\n   // private data\n   //\n   unsigned char cursor_at_end_of_line; // not implemented yet\n   unsigned char initialized;\n   unsigned char has_preferred_x;\n   unsigned char single_line;\n   unsigned char padding1, padding2, padding3;\n   float preferred_x; // this determines where the cursor up/down tries to seek to along x\n   StbUndoState undostate;\n} STB_TexteditState;\n\n\n////////////////////////////////////////////////////////////////////////\n//\n//     StbTexteditRow\n//\n// Result of layout query, used by stb_textedit to determine where\n// the text in each row is.\n\n// result of layout query\ntypedef struct\n{\n   float x0,x1;             // starting x location, end x location (allows for align=right, etc)\n   float baseline_y_delta;  // position of baseline relative to previous row's baseline\n   float ymin,ymax;         // height of row above and below baseline\n   int num_chars;\n} StbTexteditRow;\n#endif //INCLUDE_IMSTB_TEXTEDIT_H\n\n\n////////////////////////////////////////////////////////////////////////////\n////////////////////////////////////////////////////////////////////////////\n////\n////   Implementation mode\n////\n////\n\n\n// implementation isn't include-guarded, since it might have indirectly\n// included just the \"header\" portion\n#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION\n\n#ifndef IMSTB_TEXTEDIT_memmove\n#include <string.h>\n#define IMSTB_TEXTEDIT_memmove memmove\n#endif\n\n// [DEAR IMGUI]\n// Functions must be implemented for UTF8 support\n// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.\n// There is not necessarily a '[DEAR IMGUI]' at the usage sites.\n#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX\n#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)\n#endif\n#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX\n#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)\n#endif\n\n/////////////////////////////////////////////////////////////////////////////\n//\n//      Mouse input handling\n//\n\n// traverse the layout to locate the nearest character to a display position\nstatic int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line)\n{\n   StbTexteditRow r;\n   int n = STB_TEXTEDIT_STRINGLEN(str);\n   float base_y = 0, prev_x;\n   int i=0, k;\n\n   r.x0 = r.x1 = 0;\n   r.ymin = r.ymax = 0;\n   r.num_chars = 0;\n   *out_side_on_line = 0;\n\n   // search rows to find one that straddles 'y'\n   while (i < n) {\n      STB_TEXTEDIT_LAYOUTROW(&r, str, i);\n      if (r.num_chars <= 0)\n         return n;\n\n      if (i==0 && y < base_y + r.ymin)\n         return 0;\n\n      if (y < base_y + r.ymax)\n         break;\n\n      i += r.num_chars;\n      base_y += r.baseline_y_delta;\n   }\n\n   // below all text, return 'after' last character\n   if (i >= n)\n   {\n      *out_side_on_line = 1;\n      return n;\n   }\n\n   // check if it's before the beginning of the line\n   if (x < r.x0)\n      return i;\n\n   // check if it's before the end of the line\n   if (x < r.x1) {\n      // search characters in row for one that straddles 'x'\n      prev_x = r.x0;\n      for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {\n         float w = STB_TEXTEDIT_GETWIDTH(str, i, k);\n         if (x < prev_x+w) {\n            *out_side_on_line = (k == 0) ? 0 : 1;\n            if (x < prev_x+w/2)\n               return k+i;\n            else\n               return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);\n         }\n         prev_x += w;\n      }\n      // shouldn't happen, but if it does, fall through to end-of-line case\n   }\n\n   // if the last character is a newline, return that. otherwise return 'after' the last character\n   *out_side_on_line = 1;\n   if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)\n      return i+r.num_chars-1;\n   else\n      return i+r.num_chars;\n}\n\n// API click: on mouse down, move the cursor to the clicked location, and reset the selection\nstatic void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)\n{\n   // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse\n   // goes off the top or bottom of the text\n   int side_on_line;\n   if( state->single_line )\n   {\n      StbTexteditRow r;\n      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);\n      y = r.ymin;\n   }\n\n   state->cursor = stb_text_locate_coord(str, x, y, &side_on_line);\n   state->select_start = state->cursor;\n   state->select_end = state->cursor;\n   state->has_preferred_x = 0;\n   str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);\n}\n\n// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location\nstatic void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)\n{\n   int p = 0;\n   int side_on_line;\n\n   // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse\n   // goes off the top or bottom of the text\n   if( state->single_line )\n   {\n      StbTexteditRow r;\n      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);\n      y = r.ymin;\n   }\n\n   if (state->select_start == state->select_end)\n      state->select_start = state->cursor;\n\n   p = stb_text_locate_coord(str, x, y, &side_on_line);\n   state->cursor = state->select_end = p;\n   str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);\n}\n\n/////////////////////////////////////////////////////////////////////////////\n//\n//      Keyboard input handling\n//\n\n// forward declarations\nstatic void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);\nstatic void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);\nstatic void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);\nstatic void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);\nstatic void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);\n\ntypedef struct\n{\n   float x,y;    // position of n'th character\n   float height; // height of line\n   int first_char, length; // first char of row, and length\n   int prev_first;  // first char of previous row\n} StbFindState;\n\n// find the x/y location of a character, and remember info about the previous row in\n// case we get a move-up event (for page up, we'll have to rescan)\nstatic void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line)\n{\n   StbTexteditRow r;\n   int prev_start = 0;\n   int z = STB_TEXTEDIT_STRINGLEN(str);\n   int i=0, first;\n\n   if (n == z && single_line) {\n      // special case if it's at the end (may not be needed?)\n      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);\n      find->y = 0;\n      find->first_char = 0;\n      find->length = z;\n      find->height = r.ymax - r.ymin;\n      find->x = r.x1;\n      return;\n   }\n\n   // search rows to find the one that straddles character n\n   find->y = 0;\n\n   for(;;) {\n      STB_TEXTEDIT_LAYOUTROW(&r, str, i);\n      if (n < i + r.num_chars)\n         break;\n      if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor > 0 && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling\n         break;\n      if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE)  // [DEAR IMGUI] special handling for last line\n         break;   // [DEAR IMGUI]\n      prev_start = i;\n      i += r.num_chars;\n      find->y += r.baseline_y_delta;\n      if (i == z) // [DEAR IMGUI]\n      {\n         r.num_chars = 0; // [DEAR IMGUI]\n         break;   // [DEAR IMGUI]\n      }\n   }\n\n   find->first_char = first = i;\n   find->length = r.num_chars;\n   find->height = r.ymax - r.ymin;\n   find->prev_first = prev_start;\n\n   // now scan to find xpos\n   find->x = r.x0;\n   for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)\n      find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);\n}\n\n#define STB_TEXT_HAS_SELECTION(s)   ((s)->select_start != (s)->select_end)\n\n// make the selection/cursor state valid if client altered the string\nstatic void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   int n = STB_TEXTEDIT_STRINGLEN(str);\n   if (STB_TEXT_HAS_SELECTION(state)) {\n      if (state->select_start > n) state->select_start = n;\n      if (state->select_end   > n) state->select_end = n;\n      // if clamping forced them to be equal, move the cursor to match\n      if (state->select_start == state->select_end)\n         state->cursor = state->select_start;\n   }\n   if (state->cursor > n) state->cursor = n;\n}\n\n// delete characters while updating undo\nstatic void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)\n{\n   stb_text_makeundo_delete(str, state, where, len);\n   STB_TEXTEDIT_DELETECHARS(str, where, len);\n   state->has_preferred_x = 0;\n}\n\n// delete the section\nstatic void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   stb_textedit_clamp(str, state);\n   if (STB_TEXT_HAS_SELECTION(state)) {\n      if (state->select_start < state->select_end) {\n         stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start);\n         state->select_end = state->cursor = state->select_start;\n      } else {\n         stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end);\n         state->select_start = state->cursor = state->select_end;\n      }\n      state->has_preferred_x = 0;\n   }\n}\n\n// canoncialize the selection so start <= end\nstatic void stb_textedit_sortselection(STB_TexteditState *state)\n{\n   if (state->select_end < state->select_start) {\n      int temp = state->select_end;\n      state->select_end = state->select_start;\n      state->select_start = temp;\n   }\n}\n\n// move cursor to first character of selection\nstatic void stb_textedit_move_to_first(STB_TexteditState *state)\n{\n   if (STB_TEXT_HAS_SELECTION(state)) {\n      stb_textedit_sortselection(state);\n      state->cursor = state->select_start;\n      state->select_end = state->select_start;\n      state->has_preferred_x = 0;\n   }\n}\n\n// move cursor to last character of selection\nstatic void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   if (STB_TEXT_HAS_SELECTION(state)) {\n      stb_textedit_sortselection(state);\n      stb_textedit_clamp(str, state);\n      state->cursor = state->select_end;\n      state->select_start = state->select_end;\n      state->has_preferred_x = 0;\n   }\n}\n\n// [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping.\n#ifndef STB_TEXTEDIT_MOVELINESTART\nstatic int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)\n{\n   if (state->single_line)\n      return 0;\n   while (cursor > 0) {\n      int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor);\n      if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)\n         break;\n      cursor = prev;\n   }\n   return cursor;\n}\n#define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start\n#endif\n#ifndef STB_TEXTEDIT_MOVELINEEND\nstatic int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)\n{\n   int n = STB_TEXTEDIT_STRINGLEN(str);\n   if (state->single_line)\n      return n;\n   while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE)\n      cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor);\n   return cursor;\n}\n#define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end\n#endif\n\n#ifdef STB_TEXTEDIT_IS_SPACE\nstatic int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )\n{\n   return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;\n}\n\n#ifndef STB_TEXTEDIT_MOVEWORDLEFT\nstatic int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )\n{\n   c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character\n   while (c >= 0 && !is_word_boundary(str, c))\n      c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);\n\n   if( c < 0 )\n      c = 0;\n\n   return c;\n}\n#define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous\n#endif\n\n#ifndef STB_TEXTEDIT_MOVEWORDRIGHT\nstatic int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )\n{\n   const int len = STB_TEXTEDIT_STRINGLEN(str);\n   c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character\n   while( c < len && !is_word_boundary( str, c ) )\n      c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);\n\n   if( c > len )\n      c = len;\n\n   return c;\n}\n#define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next\n#endif\n\n#endif\n\n// update selection and cursor to match each other\nstatic void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)\n{\n   if (!STB_TEXT_HAS_SELECTION(state))\n      state->select_start = state->select_end = state->cursor;\n   else\n      state->cursor = state->select_end;\n}\n\n// API cut: delete selection\nstatic int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   if (STB_TEXT_HAS_SELECTION(state)) {\n      stb_textedit_delete_selection(str,state); // implicitly clamps\n      state->has_preferred_x = 0;\n      return 1;\n   }\n   return 0;\n}\n\n// API paste: replace existing selection with passed-in text\nstatic int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len)\n{\n   // if there's a selection, the paste should delete it\n   stb_textedit_clamp(str, state);\n   stb_textedit_delete_selection(str,state);\n   // try to insert the characters\n   len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len);\n   if (len) {\n      stb_text_makeundo_insert(state, state->cursor, len);\n      state->cursor += len;\n      state->has_preferred_x = 0;\n      return 1;\n   }\n   // note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details)\n   return 0;\n}\n\n#ifndef STB_TEXTEDIT_KEYTYPE\n#define STB_TEXTEDIT_KEYTYPE int\n#endif\n\n// API key: process text input\n// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.\nstatic void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)\n{\n   // can't add newline in single-line mode\n   if (text[0] == '\\n' && state->single_line)\n      return;\n\n   if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {\n      stb_text_makeundo_replace(str, state, state->cursor, 1, 1);\n      STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);\n      text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);\n      if (text_len) {\n         state->cursor += text_len;\n         state->has_preferred_x = 0;\n      }\n   } else {\n      stb_textedit_delete_selection(str, state); // implicitly clamps\n      text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);\n      if (text_len) {\n         stb_text_makeundo_insert(state, state->cursor, text_len);\n         state->cursor += text_len;\n         state->has_preferred_x = 0;\n      }\n   }\n}\n\n// API key: process a keyboard input\nstatic void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)\n{\nretry:\n   switch (key) {\n      default: {\n#ifdef STB_TEXTEDIT_KEYTOTEXT\n         // This is not suitable for UTF-8 support.\n         int c = STB_TEXTEDIT_KEYTOTEXT(key);\n         if (c > 0) {\n            IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;\n            stb_textedit_text(str, state, &ch, 1);\n         }\n#endif\n         break;\n      }\n\n#ifdef STB_TEXTEDIT_K_INSERT\n      case STB_TEXTEDIT_K_INSERT:\n         state->insert_mode = !state->insert_mode;\n         break;\n#endif\n\n      case STB_TEXTEDIT_K_UNDO:\n         stb_text_undo(str, state);\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_REDO:\n         stb_text_redo(str, state);\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_LEFT:\n         // if currently there's a selection, move cursor to start of selection\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_first(state);\n         else\n            if (state->cursor > 0)\n               state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_RIGHT:\n         // if currently there's a selection, move cursor to end of selection\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_last(str, state);\n         else\n            state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);\n         stb_textedit_clamp(str, state);\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT:\n         stb_textedit_clamp(str, state);\n         stb_textedit_prep_selection_at_cursor(state);\n         // move selection left\n         if (state->select_end > 0)\n            state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);\n         state->cursor = state->select_end;\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_MOVEWORDLEFT\n      case STB_TEXTEDIT_K_WORDLEFT:\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_first(state);\n         else {\n            state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);\n            stb_textedit_clamp( str, state );\n         }\n         break;\n\n      case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT:\n         if( !STB_TEXT_HAS_SELECTION( state ) )\n            stb_textedit_prep_selection_at_cursor(state);\n\n         state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);\n         state->select_end = state->cursor;\n\n         stb_textedit_clamp( str, state );\n         break;\n#endif\n\n#ifdef STB_TEXTEDIT_MOVEWORDRIGHT\n      case STB_TEXTEDIT_K_WORDRIGHT:\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_last(str, state);\n         else {\n            state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);\n            stb_textedit_clamp( str, state );\n         }\n         break;\n\n      case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT:\n         if( !STB_TEXT_HAS_SELECTION( state ) )\n            stb_textedit_prep_selection_at_cursor(state);\n\n         state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);\n         state->select_end = state->cursor;\n\n         stb_textedit_clamp( str, state );\n         break;\n#endif\n\n      case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:\n         stb_textedit_prep_selection_at_cursor(state);\n         // move selection right\n         state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);\n         stb_textedit_clamp(str, state);\n         state->cursor = state->select_end;\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_DOWN:\n      case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT:\n      case STB_TEXTEDIT_K_PGDOWN:\n      case STB_TEXTEDIT_K_PGDOWN | STB_TEXTEDIT_K_SHIFT: {\n         StbFindState find;\n         StbTexteditRow row;\n         int i, j, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;\n         int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGDOWN;\n         int row_count = is_page ? state->row_count_per_page : 1;\n\n         if (!is_page && state->single_line) {\n            // on windows, up&down in single-line behave like left&right\n            key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT);\n            goto retry;\n         }\n\n         if (sel)\n            stb_textedit_prep_selection_at_cursor(state);\n         else if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_last(str, state);\n\n         // compute current position of cursor point\n         stb_textedit_clamp(str, state);\n         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);\n\n         for (j = 0; j < row_count; ++j) {\n            float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;\n            int start = find.first_char + find.length;\n\n            if (find.length == 0)\n               break;\n\n            // [DEAR IMGUI]\n            // going down while being on the last line shouldn't bring us to that line end\n            //if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)\n            //   break;\n\n            // now find character position down a row\n            state->cursor = start;\n            STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);\n            x = row.x0;\n            for (i=0; i < row.num_chars; ) {\n               float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);\n               int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);\n               #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE\n               if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)\n                  break;\n               #endif\n               x += dx;\n               if (x > goal_x)\n                  break;\n               i += next - state->cursor;\n               state->cursor = next;\n            }\n            stb_textedit_clamp(str, state);\n\n            if (state->cursor == find.first_char + find.length)\n               str->LastMoveDirectionLR = ImGuiDir_Left;\n            state->has_preferred_x = 1;\n            state->preferred_x = goal_x;\n\n            if (sel)\n               state->select_end = state->cursor;\n\n            // go to next line\n            find.first_char = find.first_char + find.length;\n            find.length = row.num_chars;\n         }\n         break;\n      }\n\n      case STB_TEXTEDIT_K_UP:\n      case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT:\n      case STB_TEXTEDIT_K_PGUP:\n      case STB_TEXTEDIT_K_PGUP | STB_TEXTEDIT_K_SHIFT: {\n         StbFindState find;\n         StbTexteditRow row;\n         int i, j, prev_scan, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;\n         int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGUP;\n         int row_count = is_page ? state->row_count_per_page : 1;\n\n         if (!is_page && state->single_line) {\n            // on windows, up&down become left&right\n            key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT);\n            goto retry;\n         }\n\n         if (sel)\n            stb_textedit_prep_selection_at_cursor(state);\n         else if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_first(state);\n\n         // compute current position of cursor point\n         stb_textedit_clamp(str, state);\n         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);\n\n         for (j = 0; j < row_count; ++j) {\n            float  x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;\n\n            // can only go up if there's a previous row\n            if (find.prev_first == find.first_char)\n               break;\n\n            // now find character position up a row\n            state->cursor = find.prev_first;\n            STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);\n            x = row.x0;\n            for (i=0; i < row.num_chars; ) {\n               float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);\n               int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);\n               #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE\n               if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)\n                  break;\n               #endif\n               x += dx;\n               if (x > goal_x)\n                  break;\n               i += next - state->cursor;\n               state->cursor = next;\n            }\n            stb_textedit_clamp(str, state);\n\n            if (state->cursor == find.first_char)\n               str->LastMoveDirectionLR = ImGuiDir_Right;\n            else if (state->cursor == find.prev_first)\n               str->LastMoveDirectionLR = ImGuiDir_Left;\n            state->has_preferred_x = 1;\n            state->preferred_x = goal_x;\n\n            if (sel)\n               state->select_end = state->cursor;\n\n            // go to previous line\n            // (we need to scan previous line the hard way. maybe we could expose this as a new API function?)\n            prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;\n            while (prev_scan > 0)\n            {\n               int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);\n               if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)\n                  break;\n               prev_scan = prev;\n            }\n            find.first_char = find.prev_first;\n            find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan);\n         }\n         break;\n      }\n\n      case STB_TEXTEDIT_K_DELETE:\n      case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT:\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_delete_selection(str, state);\n         else {\n            int n = STB_TEXTEDIT_STRINGLEN(str);\n            if (state->cursor < n)\n               stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);\n         }\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_BACKSPACE:\n      case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT:\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_delete_selection(str, state);\n         else {\n            stb_textedit_clamp(str, state);\n            if (state->cursor > 0) {\n               int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);\n               stb_textedit_delete(str, state, prev, state->cursor - prev);\n               state->cursor = prev;\n            }\n         }\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_TEXTSTART2\n      case STB_TEXTEDIT_K_TEXTSTART2:\n#endif\n      case STB_TEXTEDIT_K_TEXTSTART:\n         state->cursor = state->select_start = state->select_end = 0;\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_TEXTEND2\n      case STB_TEXTEDIT_K_TEXTEND2:\n#endif\n      case STB_TEXTEDIT_K_TEXTEND:\n         state->cursor = STB_TEXTEDIT_STRINGLEN(str);\n         state->select_start = state->select_end = 0;\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_TEXTSTART2\n      case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT:\n#endif\n      case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT:\n         stb_textedit_prep_selection_at_cursor(state);\n         state->cursor = state->select_end = 0;\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_TEXTEND2\n      case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT:\n#endif\n      case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT:\n         stb_textedit_prep_selection_at_cursor(state);\n         state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str);\n         state->has_preferred_x = 0;\n         break;\n\n\n#ifdef STB_TEXTEDIT_K_LINESTART2\n      case STB_TEXTEDIT_K_LINESTART2:\n#endif\n      case STB_TEXTEDIT_K_LINESTART:\n         stb_textedit_clamp(str, state);\n         stb_textedit_move_to_first(state);\n         state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_LINEEND2\n      case STB_TEXTEDIT_K_LINEEND2:\n#endif\n      case STB_TEXTEDIT_K_LINEEND: {\n         stb_textedit_clamp(str, state);\n         stb_textedit_move_to_last(str, state);\n         state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);\n         state->has_preferred_x = 0;\n         break;\n      }\n\n#ifdef STB_TEXTEDIT_K_LINESTART2\n      case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT:\n#endif\n      case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:\n         stb_textedit_clamp(str, state);\n         stb_textedit_prep_selection_at_cursor(state);\n         state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);\n         state->select_end = state->cursor;\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_LINEEND2\n      case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:\n#endif\n      case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {\n         stb_textedit_clamp(str, state);\n         stb_textedit_prep_selection_at_cursor(state);\n         state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);\n         state->select_end = state->cursor;\n         state->has_preferred_x = 0;\n         break;\n      }\n   }\n}\n\n/////////////////////////////////////////////////////////////////////////////\n//\n//      Undo processing\n//\n// @OPTIMIZE: the undo/redo buffer should be circular\n\nstatic void stb_textedit_flush_redo(StbUndoState *state)\n{\n   state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;\n   state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;\n}\n\n// discard the oldest entry in the undo list\nstatic void stb_textedit_discard_undo(StbUndoState *state)\n{\n   if (state->undo_point > 0) {\n      // if the 0th undo state has characters, clean those up\n      if (state->undo_rec[0].char_storage >= 0) {\n         int n = state->undo_rec[0].insert_length, i;\n         // delete n characters from all other records\n         state->undo_char_point -= n;\n         IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));\n         for (i=0; i < state->undo_point; ++i)\n            if (state->undo_rec[i].char_storage >= 0)\n               state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it\n      }\n      --state->undo_point;\n      IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));\n   }\n}\n\n// discard the oldest entry in the redo list--it's bad if this\n// ever happens, but because undo & redo have to store the actual\n// characters in different cases, the redo character buffer can\n// fill up even though the undo buffer didn't\nstatic void stb_textedit_discard_redo(StbUndoState *state)\n{\n   int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1;\n\n   if (state->redo_point <= k) {\n      // if the k'th undo state has characters, clean those up\n      if (state->undo_rec[k].char_storage >= 0) {\n         int n = state->undo_rec[k].insert_length, i;\n         // move the remaining redo character data to the end of the buffer\n         state->redo_char_point += n;\n         IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));\n         // adjust the position of all the other records to account for above memmove\n         for (i=state->redo_point; i < k; ++i)\n            if (state->undo_rec[i].char_storage >= 0)\n               state->undo_rec[i].char_storage += n;\n      }\n      // now move all the redo records towards the end of the buffer; the first one is at 'redo_point'\n      // [DEAR IMGUI]\n      size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));\n      const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;\n      const char* buf_end   = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;\n      IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);\n      IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);\n      IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);\n\n      // now move redo_point to point to the new one\n      ++state->redo_point;\n   }\n}\n\nstatic StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars)\n{\n   // any time we create a new undo record, we discard redo\n   stb_textedit_flush_redo(state);\n\n   // if we have no free records, we have to make room, by sliding the\n   // existing records down\n   if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)\n      stb_textedit_discard_undo(state);\n\n   // if the characters to store won't possibly fit in the buffer, we can't undo\n   if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) {\n      state->undo_point = 0;\n      state->undo_char_point = 0;\n      return NULL;\n   }\n\n   // if we don't have enough free characters in the buffer, we have to make room\n   while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT)\n      stb_textedit_discard_undo(state);\n\n   return &state->undo_rec[state->undo_point++];\n}\n\nstatic IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)\n{\n   StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);\n   if (r == NULL)\n      return NULL;\n\n   r->where = pos;\n   r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;\n   r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;\n\n   if (insert_len == 0) {\n      r->char_storage = -1;\n      return NULL;\n   } else {\n      r->char_storage = state->undo_char_point;\n      state->undo_char_point += insert_len;\n      return &state->undo_char[r->char_storage];\n   }\n}\n\nstatic void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   StbUndoState *s = &state->undostate;\n   StbUndoRecord u, *r;\n   if (s->undo_point == 0)\n      return;\n\n   // we need to do two things: apply the undo record, and create a redo record\n   u = s->undo_rec[s->undo_point-1];\n   r = &s->undo_rec[s->redo_point-1];\n   r->char_storage = -1;\n\n   r->insert_length = u.delete_length;\n   r->delete_length = u.insert_length;\n   r->where = u.where;\n\n   if (u.delete_length) {\n      // if the undo record says to delete characters, then the redo record will\n      // need to re-insert the characters that get deleted, so we need to store\n      // them.\n\n      // there are three cases:\n      //    there's enough room to store the characters\n      //    characters stored for *redoing* don't leave room for redo\n      //    characters stored for *undoing* don't leave room for redo\n      // if the last is true, we have to bail\n\n      if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) {\n         // the undo records take up too much character space; there's no space to store the redo characters\n         r->insert_length = 0;\n      } else {\n         int i;\n\n         // there's definitely room to store the characters eventually\n         while (s->undo_char_point + u.delete_length > s->redo_char_point) {\n            // should never happen:\n            if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)\n               return;\n            // there's currently not enough room, so discard a redo record\n            stb_textedit_discard_redo(s);\n         }\n         r = &s->undo_rec[s->redo_point-1];\n\n         r->char_storage = s->redo_char_point - u.delete_length;\n         s->redo_char_point = s->redo_char_point - u.delete_length;\n\n         // now save the characters\n         for (i=0; i < u.delete_length; ++i)\n            s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i);\n      }\n\n      // now we can carry out the deletion\n      STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length);\n   }\n\n   // check type of recorded action:\n   if (u.insert_length) {\n      // easy case: was a deletion, so we need to insert n characters\n      u.insert_length = STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);\n      s->undo_char_point -= u.insert_length;\n   }\n\n   state->cursor = u.where + u.insert_length;\n\n   s->undo_point--;\n   s->redo_point--;\n}\n\nstatic void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   StbUndoState *s = &state->undostate;\n   StbUndoRecord *u, r;\n   if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)\n      return;\n\n   // we need to do two things: apply the redo record, and create an undo record\n   u = &s->undo_rec[s->undo_point];\n   r = s->undo_rec[s->redo_point];\n\n   // we KNOW there must be room for the undo record, because the redo record\n   // was derived from an undo record\n\n   u->delete_length = r.insert_length;\n   u->insert_length = r.delete_length;\n   u->where = r.where;\n   u->char_storage = -1;\n\n   if (r.delete_length) {\n      // the redo record requires us to delete characters, so the undo record\n      // needs to store the characters\n\n      if (s->undo_char_point + u->insert_length > s->redo_char_point) {\n         u->insert_length = 0;\n         u->delete_length = 0;\n      } else {\n         int i;\n         u->char_storage = s->undo_char_point;\n         s->undo_char_point = s->undo_char_point + u->insert_length;\n\n         // now save the characters\n         for (i=0; i < u->insert_length; ++i)\n            s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i);\n      }\n\n      STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length);\n   }\n\n   if (r.insert_length) {\n      // easy case: need to insert n characters\n      r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);\n      s->redo_char_point += r.insert_length;\n   }\n\n   state->cursor = r.where + r.insert_length;\n\n   s->undo_point++;\n   s->redo_point++;\n}\n\nstatic void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length)\n{\n   stb_text_createundo(&state->undostate, where, 0, length);\n}\n\nstatic void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)\n{\n   int i;\n   IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);\n   if (p) {\n      for (i=0; i < length; ++i)\n         p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);\n   }\n}\n\nstatic void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)\n{\n   int i;\n   IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);\n   if (p) {\n      for (i=0; i < old_length; ++i)\n         p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);\n   }\n}\n\n// reset the state to default\nstatic void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line)\n{\n   state->undostate.undo_point = 0;\n   state->undostate.undo_char_point = 0;\n   state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;\n   state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;\n   state->select_end = state->select_start = 0;\n   state->cursor = 0;\n   state->has_preferred_x = 0;\n   state->preferred_x = 0;\n   state->cursor_at_end_of_line = 0;\n   state->initialized = 1;\n   state->single_line = (unsigned char) is_single_line;\n   state->insert_mode = 0;\n   state->row_count_per_page = 0;\n}\n\n// API initialize\nstatic void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line)\n{\n   stb_textedit_clear_state(state, is_single_line);\n}\n\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wcast-qual\"\n#endif\n\nstatic int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len)\n{\n   return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len);\n}\n\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n\n#endif//IMSTB_TEXTEDIT_IMPLEMENTATION\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/include/imstb_truetype.h",
    "content": "// [DEAR IMGUI]\n// This is a slightly modified version of stb_truetype.h 1.26.\n// Mostly fixing for compiler and static analyzer warnings.\n// Grep for [DEAR IMGUI] to find the changes.\n\n// stb_truetype.h - v1.26 - public domain\n// authored from 2009-2021 by Sean Barrett / RAD Game Tools\n//\n// =======================================================================\n//\n//    NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES\n//\n// This library does no range checking of the offsets found in the file,\n// meaning an attacker can use it to read arbitrary memory.\n//\n// =======================================================================\n//\n//   This library processes TrueType files:\n//        parse files\n//        extract glyph metrics\n//        extract glyph shapes\n//        render glyphs to one-channel bitmaps with antialiasing (box filter)\n//        render glyphs to one-channel SDF bitmaps (signed-distance field/function)\n//\n//   Todo:\n//        non-MS cmaps\n//        crashproof on bad data\n//        hinting? (no longer patented)\n//        cleartype-style AA?\n//        optimize: use simple memory allocator for intermediates\n//        optimize: build edge-list directly from curves\n//        optimize: rasterize directly from curves?\n//\n// ADDITIONAL CONTRIBUTORS\n//\n//   Mikko Mononen: compound shape support, more cmap formats\n//   Tor Andersson: kerning, subpixel rendering\n//   Dougall Johnson: OpenType / Type 2 font handling\n//   Daniel Ribeiro Maciel: basic GPOS-based kerning\n//\n//   Misc other:\n//       Ryan Gordon\n//       Simon Glass\n//       github:IntellectualKitty\n//       Imanol Celaya\n//       Daniel Ribeiro Maciel\n//\n//   Bug/warning reports/fixes:\n//       \"Zer\" on mollyrocket       Fabian \"ryg\" Giesen   github:NiLuJe\n//       Cass Everitt               Martins Mozeiko       github:aloucks\n//       stoiko (Haemimont Games)   Cap Petschulat        github:oyvindjam\n//       Brian Hook                 Omar Cornut           github:vassvik\n//       Walter van Niftrik         Ryan Griege\n//       David Gow                  Peter LaValle\n//       David Given                Sergey Popov\n//       Ivan-Assen Ivanov          Giumo X. Clanjor\n//       Anthony Pesch              Higor Euripedes\n//       Johan Duparc               Thomas Fields\n//       Hou Qiming                 Derek Vinyard\n//       Rob Loach                  Cort Stratton\n//       Kenney Phillis Jr.         Brian Costabile\n//       Ken Voskuil (kaesve)\n//\n// VERSION HISTORY\n//\n//   1.26 (2021-08-28) fix broken rasterizer\n//   1.25 (2021-07-11) many fixes\n//   1.24 (2020-02-05) fix warning\n//   1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)\n//   1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined\n//   1.21 (2019-02-25) fix warning\n//   1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()\n//   1.19 (2018-02-11) GPOS kerning, STBTT_fmod\n//   1.18 (2018-01-29) add missing function\n//   1.17 (2017-07-23) make more arguments const; doc fix\n//   1.16 (2017-07-12) SDF support\n//   1.15 (2017-03-03) make more arguments const\n//   1.14 (2017-01-16) num-fonts-in-TTC function\n//   1.13 (2017-01-02) support OpenType fonts, certain Apple fonts\n//   1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual\n//   1.11 (2016-04-02) fix unused-variable warning\n//   1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef\n//   1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly\n//   1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges\n//   1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;\n//                     variant PackFontRanges to pack and render in separate phases;\n//                     fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);\n//                     fixed an assert() bug in the new rasterizer\n//                     replace assert() with STBTT_assert() in new rasterizer\n//\n//   Full history can be found at the end of this file.\n//\n// LICENSE\n//\n//   See end of file for license information.\n//\n// USAGE\n//\n//   Include this file in whatever places need to refer to it. In ONE C/C++\n//   file, write:\n//      #define STB_TRUETYPE_IMPLEMENTATION\n//   before the #include of this file. This expands out the actual\n//   implementation into that C/C++ file.\n//\n//   To make the implementation private to the file that generates the implementation,\n//      #define STBTT_STATIC\n//\n//   Simple 3D API (don't ship this, but it's fine for tools and quick start)\n//           stbtt_BakeFontBitmap()               -- bake a font to a bitmap for use as texture\n//           stbtt_GetBakedQuad()                 -- compute quad to draw for a given char\n//\n//   Improved 3D API (more shippable):\n//           #include \"stb_rect_pack.h\"           -- optional, but you really want it\n//           stbtt_PackBegin()\n//           stbtt_PackSetOversampling()          -- for improved quality on small fonts\n//           stbtt_PackFontRanges()               -- pack and renders\n//           stbtt_PackEnd()\n//           stbtt_GetPackedQuad()\n//\n//   \"Load\" a font file from a memory buffer (you have to keep the buffer loaded)\n//           stbtt_InitFont()\n//           stbtt_GetFontOffsetForIndex()        -- indexing for TTC font collections\n//           stbtt_GetNumberOfFonts()             -- number of fonts for TTC font collections\n//\n//   Render a unicode codepoint to a bitmap\n//           stbtt_GetCodepointBitmap()           -- allocates and returns a bitmap\n//           stbtt_MakeCodepointBitmap()          -- renders into bitmap you provide\n//           stbtt_GetCodepointBitmapBox()        -- how big the bitmap must be\n//\n//   Character advance/positioning\n//           stbtt_GetCodepointHMetrics()\n//           stbtt_GetFontVMetrics()\n//           stbtt_GetFontVMetricsOS2()\n//           stbtt_GetCodepointKernAdvance()\n//\n//   Starting with version 1.06, the rasterizer was replaced with a new,\n//   faster and generally-more-precise rasterizer. The new rasterizer more\n//   accurately measures pixel coverage for anti-aliasing, except in the case\n//   where multiple shapes overlap, in which case it overestimates the AA pixel\n//   coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If\n//   this turns out to be a problem, you can re-enable the old rasterizer with\n//        #define STBTT_RASTERIZER_VERSION 1\n//   which will incur about a 15% speed hit.\n//\n// ADDITIONAL DOCUMENTATION\n//\n//   Immediately after this block comment are a series of sample programs.\n//\n//   After the sample programs is the \"header file\" section. This section\n//   includes documentation for each API function.\n//\n//   Some important concepts to understand to use this library:\n//\n//      Codepoint\n//         Characters are defined by unicode codepoints, e.g. 65 is\n//         uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is\n//         the hiragana for \"ma\".\n//\n//      Glyph\n//         A visual character shape (every codepoint is rendered as\n//         some glyph)\n//\n//      Glyph index\n//         A font-specific integer ID representing a glyph\n//\n//      Baseline\n//         Glyph shapes are defined relative to a baseline, which is the\n//         bottom of uppercase characters. Characters extend both above\n//         and below the baseline.\n//\n//      Current Point\n//         As you draw text to the screen, you keep track of a \"current point\"\n//         which is the origin of each character. The current point's vertical\n//         position is the baseline. Even \"baked fonts\" use this model.\n//\n//      Vertical Font Metrics\n//         The vertical qualities of the font, used to vertically position\n//         and space the characters. See docs for stbtt_GetFontVMetrics.\n//\n//      Font Size in Pixels or Points\n//         The preferred interface for specifying font sizes in stb_truetype\n//         is to specify how tall the font's vertical extent should be in pixels.\n//         If that sounds good enough, skip the next paragraph.\n//\n//         Most font APIs instead use \"points\", which are a common typographic\n//         measurement for describing font size, defined as 72 points per inch.\n//         stb_truetype provides a point API for compatibility. However, true\n//         \"per inch\" conventions don't make much sense on computer displays\n//         since different monitors have different number of pixels per\n//         inch. For example, Windows traditionally uses a convention that\n//         there are 96 pixels per inch, thus making 'inch' measurements have\n//         nothing to do with inches, and thus effectively defining a point to\n//         be 1.333 pixels. Additionally, the TrueType font data provides\n//         an explicit scale factor to scale a given font's glyphs to points,\n//         but the author has observed that this scale factor is often wrong\n//         for non-commercial fonts, thus making fonts scaled in points\n//         according to the TrueType spec incoherently sized in practice.\n//\n// DETAILED USAGE:\n//\n//  Scale:\n//    Select how high you want the font to be, in points or pixels.\n//    Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute\n//    a scale factor SF that will be used by all other functions.\n//\n//  Baseline:\n//    You need to select a y-coordinate that is the baseline of where\n//    your text will appear. Call GetFontBoundingBox to get the baseline-relative\n//    bounding box for all characters. SF*-y0 will be the distance in pixels\n//    that the worst-case character could extend above the baseline, so if\n//    you want the top edge of characters to appear at the top of the\n//    screen where y=0, then you would set the baseline to SF*-y0.\n//\n//  Current point:\n//    Set the current point where the first character will appear. The\n//    first character could extend left of the current point; this is font\n//    dependent. You can either choose a current point that is the leftmost\n//    point and hope, or add some padding, or check the bounding box or\n//    left-side-bearing of the first character to be displayed and set\n//    the current point based on that.\n//\n//  Displaying a character:\n//    Compute the bounding box of the character. It will contain signed values\n//    relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,\n//    then the character should be displayed in the rectangle from\n//    <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).\n//\n//  Advancing for the next character:\n//    Call GlyphHMetrics, and compute 'current_point += SF * advance'.\n//\n//\n// ADVANCED USAGE\n//\n//   Quality:\n//\n//    - Use the functions with Subpixel at the end to allow your characters\n//      to have subpixel positioning. Since the font is anti-aliased, not\n//      hinted, this is very import for quality. (This is not possible with\n//      baked fonts.)\n//\n//    - Kerning is now supported, and if you're supporting subpixel rendering\n//      then kerning is worth using to give your text a polished look.\n//\n//   Performance:\n//\n//    - Convert Unicode codepoints to glyph indexes and operate on the glyphs;\n//      if you don't do this, stb_truetype is forced to do the conversion on\n//      every call.\n//\n//    - There are a lot of memory allocations. We should modify it to take\n//      a temp buffer and allocate from the temp buffer (without freeing),\n//      should help performance a lot.\n//\n// NOTES\n//\n//   The system uses the raw data found in the .ttf file without changing it\n//   and without building auxiliary data structures. This is a bit inefficient\n//   on little-endian systems (the data is big-endian), but assuming you're\n//   caching the bitmaps or glyph shapes this shouldn't be a big deal.\n//\n//   It appears to be very hard to programmatically determine what font a\n//   given file is in a general way. I provide an API for this, but I don't\n//   recommend it.\n//\n//\n// PERFORMANCE MEASUREMENTS FOR 1.06:\n//\n//                      32-bit     64-bit\n//   Previous release:  8.83 s     7.68 s\n//   Pool allocations:  7.72 s     6.34 s\n//   Inline sort     :  6.54 s     5.65 s\n//   New rasterizer  :  5.63 s     5.00 s\n\n//////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////\n////\n////  SAMPLE PROGRAMS\n////\n//\n//  Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.\n//  See \"tests/truetype_demo_win32.c\" for a complete version.\n#if 0\n#define STB_TRUETYPE_IMPLEMENTATION  // force following include to generate implementation\n#include \"stb_truetype.h\"\n\nunsigned char ttf_buffer[1<<20];\nunsigned char temp_bitmap[512*512];\n\nstbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs\nGLuint ftex;\n\nvoid my_stbtt_initfont(void)\n{\n   fread(ttf_buffer, 1, 1<<20, fopen(\"c:/windows/fonts/times.ttf\", \"rb\"));\n   stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!\n   // can free ttf_buffer at this point\n   glGenTextures(1, &ftex);\n   glBindTexture(GL_TEXTURE_2D, ftex);\n   glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);\n   // can free temp_bitmap at this point\n   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n}\n\nvoid my_stbtt_print(float x, float y, char *text)\n{\n   // assume orthographic projection with units = screen pixels, origin at top left\n   glEnable(GL_BLEND);\n   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n   glEnable(GL_TEXTURE_2D);\n   glBindTexture(GL_TEXTURE_2D, ftex);\n   glBegin(GL_QUADS);\n   while (*text) {\n      if (*text >= 32 && *text < 128) {\n         stbtt_aligned_quad q;\n         stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9\n         glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0);\n         glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);\n         glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);\n         glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);\n      }\n      ++text;\n   }\n   glEnd();\n}\n#endif\n//\n//\n//////////////////////////////////////////////////////////////////////////////\n//\n// Complete program (this compiles): get a single bitmap, print as ASCII art\n//\n#if 0\n#include <stdio.h>\n#define STB_TRUETYPE_IMPLEMENTATION  // force following include to generate implementation\n#include \"stb_truetype.h\"\n\nchar ttf_buffer[1<<25];\n\nint main(int argc, char **argv)\n{\n   stbtt_fontinfo font;\n   unsigned char *bitmap;\n   int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);\n\n   fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : \"c:/windows/fonts/arialbd.ttf\", \"rb\"));\n\n   stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));\n   bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);\n\n   for (j=0; j < h; ++j) {\n      for (i=0; i < w; ++i)\n         putchar(\" .:ioVM@\"[bitmap[j*w+i]>>5]);\n      putchar('\\n');\n   }\n   return 0;\n}\n#endif\n//\n// Output:\n//\n//     .ii.\n//    @@@@@@.\n//   V@Mio@@o\n//   :i.  V@V\n//     :oM@@M\n//   :@@@MM@M\n//   @@o  o@M\n//  :@@.  M@M\n//   @@@o@@@@\n//   :M@@V:@@.\n//\n//////////////////////////////////////////////////////////////////////////////\n//\n// Complete program: print \"Hello World!\" banner, with bugs\n//\n#if 0\nchar buffer[24<<20];\nunsigned char screen[20][79];\n\nint main(int arg, char **argv)\n{\n   stbtt_fontinfo font;\n   int i,j,ascent,baseline,ch=0;\n   float scale, xpos=2; // leave a little padding in case the character extends left\n   char *text = \"Heljo World!\"; // intentionally misspelled to show 'lj' brokenness\n\n   fread(buffer, 1, 1000000, fopen(\"c:/windows/fonts/arialbd.ttf\", \"rb\"));\n   stbtt_InitFont(&font, buffer, 0);\n\n   scale = stbtt_ScaleForPixelHeight(&font, 15);\n   stbtt_GetFontVMetrics(&font, &ascent,0,0);\n   baseline = (int) (ascent*scale);\n\n   while (text[ch]) {\n      int advance,lsb,x0,y0,x1,y1;\n      float x_shift = xpos - (float) floor(xpos);\n      stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);\n      stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);\n      stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);\n      // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong\n      // because this API is really for baking character bitmaps into textures. if you want to render\n      // a sequence of characters, you really need to render each bitmap to a temp buffer, then\n      // \"alpha blend\" that into the working buffer\n      xpos += (advance * scale);\n      if (text[ch+1])\n         xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);\n      ++ch;\n   }\n\n   for (j=0; j < 20; ++j) {\n      for (i=0; i < 78; ++i)\n         putchar(\" .:ioVM@\"[screen[j][i]>>5]);\n      putchar('\\n');\n   }\n\n   return 0;\n}\n#endif\n\n\n//////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////\n////\n////   INTEGRATION WITH YOUR CODEBASE\n////\n////   The following sections allow you to supply alternate definitions\n////   of C library functions used by stb_truetype, e.g. if you don't\n////   link with the C runtime library.\n\n#ifdef STB_TRUETYPE_IMPLEMENTATION\n   // #define your own (u)stbtt_int8/16/32 before including to override this\n   #ifndef stbtt_uint8\n   typedef unsigned char   stbtt_uint8;\n   typedef signed   char   stbtt_int8;\n   typedef unsigned short  stbtt_uint16;\n   typedef signed   short  stbtt_int16;\n   typedef unsigned int    stbtt_uint32;\n   typedef signed   int    stbtt_int32;\n   #endif\n\n   typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];\n   typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];\n\n   // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h\n   #ifndef STBTT_ifloor\n   #include <math.h>\n   #define STBTT_ifloor(x)   ((int) floor(x))\n   #define STBTT_iceil(x)    ((int) ceil(x))\n   #endif\n\n   #ifndef STBTT_sqrt\n   #include <math.h>\n   #define STBTT_sqrt(x)      sqrt(x)\n   #define STBTT_pow(x,y)     pow(x,y)\n   #endif\n\n   #ifndef STBTT_fmod\n   #include <math.h>\n   #define STBTT_fmod(x,y)    fmod(x,y)\n   #endif\n\n   #ifndef STBTT_cos\n   #include <math.h>\n   #define STBTT_cos(x)       cos(x)\n   #define STBTT_acos(x)      acos(x)\n   #endif\n\n   #ifndef STBTT_fabs\n   #include <math.h>\n   #define STBTT_fabs(x)      fabs(x)\n   #endif\n\n   // #define your own functions \"STBTT_malloc\" / \"STBTT_free\" to avoid malloc.h\n   #ifndef STBTT_malloc\n   #include <stdlib.h>\n   #define STBTT_malloc(x,u)  ((void)(u),malloc(x))\n   #define STBTT_free(x,u)    ((void)(u),free(x))\n   #endif\n\n   #ifndef STBTT_assert\n   #include <assert.h>\n   #define STBTT_assert(x)    assert(x)\n   #endif\n\n   #ifndef STBTT_strlen\n   #include <string.h>\n   #define STBTT_strlen(x)    strlen(x)\n   #endif\n\n   #ifndef STBTT_memcpy\n   #include <string.h>\n   #define STBTT_memcpy       memcpy\n   #define STBTT_memset       memset\n   #endif\n#endif\n\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n////\n////   INTERFACE\n////\n////\n\n#ifndef __STB_INCLUDE_STB_TRUETYPE_H__\n#define __STB_INCLUDE_STB_TRUETYPE_H__\n\n#ifdef STBTT_STATIC\n#define STBTT_DEF static\n#else\n#define STBTT_DEF extern\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// private structure\ntypedef struct\n{\n   unsigned char *data;\n   int cursor;\n   int size;\n} stbtt__buf;\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// TEXTURE BAKING API\n//\n// If you use this API, you only have to call two functions ever.\n//\n\ntypedef struct\n{\n   unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap\n   float xoff,yoff,xadvance;\n} stbtt_bakedchar;\n\nSTBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,  // font location (use offset=0 for plain .ttf)\n                                float pixel_height,                     // height of font in pixels\n                                unsigned char *pixels, int pw, int ph,  // bitmap to be filled in\n                                int first_char, int num_chars,          // characters to bake\n                                stbtt_bakedchar *chardata);             // you allocate this, it's num_chars long\n// if return is positive, the first unused row of the bitmap\n// if return is negative, returns the negative of the number of characters that fit\n// if return is 0, no characters fit and no rows were used\n// This uses a very crappy packing.\n\ntypedef struct\n{\n   float x0,y0,s0,t0; // top-left\n   float x1,y1,s1,t1; // bottom-right\n} stbtt_aligned_quad;\n\nSTBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph,  // same data as above\n                               int char_index,             // character to display\n                               float *xpos, float *ypos,   // pointers to current position in screen pixel space\n                               stbtt_aligned_quad *q,      // output: quad to draw\n                               int opengl_fillrule);       // true if opengl fill rule; false if DX9 or earlier\n// Call GetBakedQuad with char_index = 'character - first_char', and it\n// creates the quad you need to draw and advances the current position.\n//\n// The coordinate system used assumes y increases downwards.\n//\n// Characters will extend both above and below the current position;\n// see discussion of \"BASELINE\" above.\n//\n// It's inefficient; you might want to c&p it and optimize it.\n\nSTBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap);\n// Query the font vertical metrics without having to create a font first.\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// NEW TEXTURE BAKING API\n//\n// This provides options for packing multiple fonts into one atlas, not\n// perfectly but better than nothing.\n\ntypedef struct\n{\n   unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap\n   float xoff,yoff,xadvance;\n   float xoff2,yoff2;\n} stbtt_packedchar;\n\ntypedef struct stbtt_pack_context stbtt_pack_context;\ntypedef struct stbtt_fontinfo stbtt_fontinfo;\n#ifndef STB_RECT_PACK_VERSION\ntypedef struct stbrp_rect stbrp_rect;\n#endif\n\nSTBTT_DEF int  stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);\n// Initializes a packing context stored in the passed-in stbtt_pack_context.\n// Future calls using this context will pack characters into the bitmap passed\n// in here: a 1-channel bitmap that is width * height. stride_in_bytes is\n// the distance from one row to the next (or 0 to mean they are packed tightly\n// together). \"padding\" is the amount of padding to leave between each\n// character (normally you want '1' for bitmaps you'll use as textures with\n// bilinear filtering).\n//\n// Returns 0 on failure, 1 on success.\n\nSTBTT_DEF void stbtt_PackEnd  (stbtt_pack_context *spc);\n// Cleans up the packing context and frees all memory.\n\n#define STBTT_POINT_SIZE(x)   (-(x))\n\nSTBTT_DEF int  stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,\n                                int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);\n// Creates character bitmaps from the font_index'th font found in fontdata (use\n// font_index=0 if you don't know what that is). It creates num_chars_in_range\n// bitmaps for characters with unicode values starting at first_unicode_char_in_range\n// and increasing. Data for how to render them is stored in chardata_for_range;\n// pass these to stbtt_GetPackedQuad to get back renderable quads.\n//\n// font_size is the full height of the character from ascender to descender,\n// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed\n// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()\n// and pass that result as 'font_size':\n//       ...,                  20 , ... // font max minus min y is 20 pixels tall\n//       ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall\n\ntypedef struct\n{\n   float font_size;\n   int first_unicode_codepoint_in_range;  // if non-zero, then the chars are continuous, and this is the first codepoint\n   int *array_of_unicode_codepoints;       // if non-zero, then this is an array of unicode codepoints\n   int num_chars;\n   stbtt_packedchar *chardata_for_range; // output\n   unsigned char h_oversample, v_oversample; // don't set these, they're used internally\n} stbtt_pack_range;\n\nSTBTT_DEF int  stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);\n// Creates character bitmaps from multiple ranges of characters stored in\n// ranges. This will usually create a better-packed bitmap than multiple\n// calls to stbtt_PackFontRange. Note that you can call this multiple\n// times within a single PackBegin/PackEnd.\n\nSTBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);\n// Oversampling a font increases the quality by allowing higher-quality subpixel\n// positioning, and is especially valuable at smaller text sizes.\n//\n// This function sets the amount of oversampling for all following calls to\n// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given\n// pack context. The default (no oversampling) is achieved by h_oversample=1\n// and v_oversample=1. The total number of pixels required is\n// h_oversample*v_oversample larger than the default; for example, 2x2\n// oversampling requires 4x the storage of 1x1. For best results, render\n// oversampled textures with bilinear filtering. Look at the readme in\n// stb/tests/oversample for information about oversampled fonts\n//\n// To use with PackFontRangesGather etc., you must set it before calls\n// call to PackFontRangesGatherRects.\n\nSTBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);\n// If skip != 0, this tells stb_truetype to skip any codepoints for which\n// there is no corresponding glyph. If skip=0, which is the default, then\n// codepoints without a glyph received the font's \"missing character\" glyph,\n// typically an empty box by convention.\n\nSTBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph,  // same data as above\n                               int char_index,             // character to display\n                               float *xpos, float *ypos,   // pointers to current position in screen pixel space\n                               stbtt_aligned_quad *q,      // output: quad to draw\n                               int align_to_integer);\n\nSTBTT_DEF int  stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);\nSTBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);\nSTBTT_DEF int  stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);\n// Calling these functions in sequence is roughly equivalent to calling\n// stbtt_PackFontRanges(). If you more control over the packing of multiple\n// fonts, or if you want to pack custom data into a font texture, take a look\n// at the source to of stbtt_PackFontRanges() and create a custom version\n// using these functions, e.g. call GatherRects multiple times,\n// building up a single array of rects, then call PackRects once,\n// then call RenderIntoRects repeatedly. This may result in a\n// better packing than calling PackFontRanges multiple times\n// (or it may not).\n\n// this is an opaque structure that you shouldn't mess with which holds\n// all the context needed from PackBegin to PackEnd.\nstruct stbtt_pack_context {\n   void *user_allocator_context;\n   void *pack_info;\n   int   width;\n   int   height;\n   int   stride_in_bytes;\n   int   padding;\n   int   skip_missing;\n   unsigned int   h_oversample, v_oversample;\n   unsigned char *pixels;\n   void  *nodes;\n};\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// FONT LOADING\n//\n//\n\nSTBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data);\n// This function will determine the number of fonts in a font file.  TrueType\n// collection (.ttc) files may contain multiple fonts, while TrueType font\n// (.ttf) files only contain one font. The number of fonts can be used for\n// indexing with the previous function where the index is between zero and one\n// less than the total fonts. If an error occurs, -1 is returned.\n\nSTBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);\n// Each .ttf/.ttc file may have more than one font. Each font has a sequential\n// index number starting from 0. Call this function to get the font offset for\n// a given index; it returns -1 if the index is out of range. A regular .ttf\n// file will only define one font and it always be at offset 0, so it will\n// return '0' for index 0, and -1 for all other indices.\n\n// The following structure is defined publicly so you can declare one on\n// the stack or as a global or etc, but you should treat it as opaque.\nstruct stbtt_fontinfo\n{\n   void           * userdata;\n   unsigned char  * data;              // pointer to .ttf file\n   int              fontstart;         // offset of start of font\n\n   int numGlyphs;                     // number of glyphs, needed for range checking\n\n   int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf\n   int index_map;                     // a cmap mapping for our chosen character encoding\n   int indexToLocFormat;              // format needed to map from glyph index to glyph\n\n   stbtt__buf cff;                    // cff font data\n   stbtt__buf charstrings;            // the charstring index\n   stbtt__buf gsubrs;                 // global charstring subroutines index\n   stbtt__buf subrs;                  // private charstring subroutines index\n   stbtt__buf fontdicts;              // array of font dicts\n   stbtt__buf fdselect;               // map from glyph to fontdict\n};\n\nSTBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);\n// Given an offset into the file that defines a font, this function builds\n// the necessary cached info for the rest of the system. You must allocate\n// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't\n// need to do anything special to free it, because the contents are pure\n// value data with no additional data structures. Returns 0 on failure.\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// CHARACTER TO GLYPH-INDEX CONVERSIOn\n\nSTBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);\n// If you're going to perform multiple operations on the same character\n// and you want a speed-up, call this function with the character you're\n// going to process, then use glyph-based functions instead of the\n// codepoint-based functions.\n// Returns 0 if the character codepoint is not defined in the font.\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// CHARACTER PROPERTIES\n//\n\nSTBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);\n// computes a scale factor to produce a font whose \"height\" is 'pixels' tall.\n// Height is measured as the distance from the highest ascender to the lowest\n// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics\n// and computing:\n//       scale = pixels / (ascent - descent)\n// so if you prefer to measure height by the ascent only, use a similar calculation.\n\nSTBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);\n// computes a scale factor to produce a font whose EM size is mapped to\n// 'pixels' tall. This is probably what traditional APIs compute, but\n// I'm not positive.\n\nSTBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);\n// ascent is the coordinate above the baseline the font extends; descent\n// is the coordinate below the baseline the font extends (i.e. it is typically negative)\n// lineGap is the spacing between one row's descent and the next row's ascent...\n// so you should advance the vertical position by \"*ascent - *descent + *lineGap\"\n//   these are expressed in unscaled coordinates, so you must multiply by\n//   the scale factor for a given size\n\nSTBTT_DEF int  stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap);\n// analogous to GetFontVMetrics, but returns the \"typographic\" values from the OS/2\n// table (specific to MS/Windows TTF files).\n//\n// Returns 1 on success (table present), 0 on failure.\n\nSTBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);\n// the bounding box around all possible characters\n\nSTBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);\n// leftSideBearing is the offset from the current horizontal position to the left edge of the character\n// advanceWidth is the offset from the current horizontal position to the next horizontal position\n//   these are expressed in unscaled coordinates\n\nSTBTT_DEF int  stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);\n// an additional amount to add to the 'advance' value between ch1 and ch2\n\nSTBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);\n// Gets the bounding box of the visible part of the glyph, in unscaled coordinates\n\nSTBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);\nSTBTT_DEF int  stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);\nSTBTT_DEF int  stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);\n// as above, but takes one or more glyph indices for greater efficiency\n\ntypedef struct stbtt_kerningentry\n{\n   int glyph1; // use stbtt_FindGlyphIndex\n   int glyph2;\n   int advance;\n} stbtt_kerningentry;\n\nSTBTT_DEF int  stbtt_GetKerningTableLength(const stbtt_fontinfo *info);\nSTBTT_DEF int  stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length);\n// Retrieves a complete list of all of the kerning pairs provided by the font\n// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write.\n// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1)\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// GLYPH SHAPES (you probably don't need these, but they have to go before\n// the bitmaps for C declaration-order reasons)\n//\n\n#ifndef STBTT_vmove // you can predefine these to use different values (but why?)\n   enum {\n      STBTT_vmove=1,\n      STBTT_vline,\n      STBTT_vcurve,\n      STBTT_vcubic\n   };\n#endif\n\n#ifndef stbtt_vertex // you can predefine this to use different values\n                   // (we share this with other code at RAD)\n   #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file\n   typedef struct\n   {\n      stbtt_vertex_type x,y,cx,cy,cx1,cy1;\n      unsigned char type,padding;\n   } stbtt_vertex;\n#endif\n\nSTBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);\n// returns non-zero if nothing is drawn for this glyph\n\nSTBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);\nSTBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);\n// returns # of vertices and fills *vertices with the pointer to them\n//   these are expressed in \"unscaled\" coordinates\n//\n// The shape is a series of contours. Each one starts with\n// a STBTT_moveto, then consists of a series of mixed\n// STBTT_lineto and STBTT_curveto segments. A lineto\n// draws a line from previous endpoint to its x,y; a curveto\n// draws a quadratic bezier from previous endpoint to\n// its x,y, using cx,cy as the bezier control point.\n\nSTBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);\n// frees the data allocated above\n\nSTBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl);\nSTBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg);\nSTBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg);\n// fills svg with the character's SVG data.\n// returns data size or 0 if SVG not found.\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// BITMAP RENDERING\n//\n\nSTBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);\n// frees the bitmap allocated below\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);\n// allocates a large-enough single-channel 8bpp bitmap and renders the\n// specified character/glyph at the specified scale into it, with\n// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).\n// *width & *height are filled out with the width & height of the bitmap,\n// which is stored left-to-right, top-to-bottom.\n//\n// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);\n// the same as stbtt_GetCodepointBitmap, but you can specify a subpixel\n// shift for the character\n\nSTBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);\n// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap\n// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap\n// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the\n// width and height and positioning info for it first.\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);\n// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel\n// shift for the character\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint);\n// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering\n// is performed (see stbtt_PackSetOversampling)\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);\n// get the bbox of the bitmap centered around the glyph origin; so the\n// bitmap width is ix1-ix0, height is iy1-iy0, and location to place\n// the bitmap top left is (leftSideBearing*scale,iy0).\n// (Note that the bitmap uses y-increases-down, but the shape uses\n// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);\n// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel\n// shift for the character\n\n// the following functions are equivalent to the above functions, but operate\n// on glyph indices instead of Unicode codepoints (for efficiency)\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);\nSTBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph);\nSTBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);\nSTBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);\n\n\n// @TODO: don't expose this structure\ntypedef struct\n{\n   int w,h,stride;\n   unsigned char *pixels;\n} stbtt__bitmap;\n\n// rasterize a shape with quadratic beziers into a bitmap\nSTBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result,        // 1-channel bitmap to draw into\n                               float flatness_in_pixels,     // allowable error of curve in pixels\n                               stbtt_vertex *vertices,       // array of vertices defining shape\n                               int num_verts,                // number of vertices in above array\n                               float scale_x, float scale_y, // scale applied to input vertices\n                               float shift_x, float shift_y, // translation applied to input vertices\n                               int x_off, int y_off,         // another translation applied to input\n                               int invert,                   // if non-zero, vertically flip shape\n                               void *userdata);              // context for to STBTT_MALLOC\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Signed Distance Function (or Field) rendering\n\nSTBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata);\n// frees the SDF bitmap allocated below\n\nSTBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);\nSTBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);\n// These functions compute a discretized SDF field for a single character, suitable for storing\n// in a single-channel texture, sampling with bilinear filtering, and testing against\n// larger than some threshold to produce scalable fonts.\n//        info              --  the font\n//        scale             --  controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap\n//        glyph/codepoint   --  the character to generate the SDF for\n//        padding           --  extra \"pixels\" around the character which are filled with the distance to the character (not 0),\n//                                 which allows effects like bit outlines\n//        onedge_value      --  value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character)\n//        pixel_dist_scale  --  what value the SDF should increase by when moving one SDF \"pixel\" away from the edge (on the 0..255 scale)\n//                                 if positive, > onedge_value is inside; if negative, < onedge_value is inside\n//        width,height      --  output height & width of the SDF bitmap (including padding)\n//        xoff,yoff         --  output origin of the character\n//        return value      --  a 2D array of bytes 0..255, width*height in size\n//\n// pixel_dist_scale & onedge_value are a scale & bias that allows you to make\n// optimal use of the limited 0..255 for your application, trading off precision\n// and special effects. SDF values outside the range 0..255 are clamped to 0..255.\n//\n// Example:\n//      scale = stbtt_ScaleForPixelHeight(22)\n//      padding = 5\n//      onedge_value = 180\n//      pixel_dist_scale = 180/5.0 = 36.0\n//\n//      This will create an SDF bitmap in which the character is about 22 pixels\n//      high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled\n//      shape, sample the SDF at each pixel and fill the pixel if the SDF value\n//      is greater than or equal to 180/255. (You'll actually want to antialias,\n//      which is beyond the scope of this example.) Additionally, you can compute\n//      offset outlines (e.g. to stroke the character border inside & outside,\n//      or only outside). For example, to fill outside the character up to 3 SDF\n//      pixels, you would compare against (180-36.0*3)/255 = 72/255. The above\n//      choice of variables maps a range from 5 pixels outside the shape to\n//      2 pixels inside the shape to 0..255; this is intended primarily for apply\n//      outside effects only (the interior range is needed to allow proper\n//      antialiasing of the font at *smaller* sizes)\n//\n// The function computes the SDF analytically at each SDF pixel, not by e.g.\n// building a higher-res bitmap and approximating it. In theory the quality\n// should be as high as possible for an SDF of this size & representation, but\n// unclear if this is true in practice (perhaps building a higher-res bitmap\n// and computing from that can allow drop-out prevention).\n//\n// The algorithm has not been optimized at all, so expect it to be slow\n// if computing lots of characters or very large sizes.\n\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Finding the right font...\n//\n// You should really just solve this offline, keep your own tables\n// of what font is what, and don't try to get it out of the .ttf file.\n// That's because getting it out of the .ttf file is really hard, because\n// the names in the file can appear in many possible encodings, in many\n// possible languages, and e.g. if you need a case-insensitive comparison,\n// the details of that depend on the encoding & language in a complex way\n// (actually underspecified in truetype, but also gigantic).\n//\n// But you can use the provided functions in two possible ways:\n//     stbtt_FindMatchingFont() will use *case-sensitive* comparisons on\n//             unicode-encoded names to try to find the font you want;\n//             you can run this before calling stbtt_InitFont()\n//\n//     stbtt_GetFontNameString() lets you get any of the various strings\n//             from the file yourself and do your own comparisons on them.\n//             You have to have called stbtt_InitFont() first.\n\n\nSTBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);\n// returns the offset (not index) of the font that matches, or -1 if none\n//   if you use STBTT_MACSTYLE_DONTCARE, use a font name like \"Arial Bold\".\n//   if you use any other flag, use a font name like \"Arial\"; this checks\n//     the 'macStyle' header field; i don't know if fonts set this consistently\n#define STBTT_MACSTYLE_DONTCARE     0\n#define STBTT_MACSTYLE_BOLD         1\n#define STBTT_MACSTYLE_ITALIC       2\n#define STBTT_MACSTYLE_UNDERSCORE   4\n#define STBTT_MACSTYLE_NONE         8   // <= not same as 0, this makes us check the bitfield is 0\n\nSTBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);\n// returns 1/0 whether the first string interpreted as utf8 is identical to\n// the second string interpreted as big-endian utf16... useful for strings from next func\n\nSTBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);\n// returns the string (which may be big-endian double byte, e.g. for unicode)\n// and puts the length in bytes in *length.\n//\n// some of the values for the IDs are below; for more see the truetype spec:\n//     http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html\n//     http://www.microsoft.com/typography/otspec/name.htm\n\nenum { // platformID\n   STBTT_PLATFORM_ID_UNICODE   =0,\n   STBTT_PLATFORM_ID_MAC       =1,\n   STBTT_PLATFORM_ID_ISO       =2,\n   STBTT_PLATFORM_ID_MICROSOFT =3\n};\n\nenum { // encodingID for STBTT_PLATFORM_ID_UNICODE\n   STBTT_UNICODE_EID_UNICODE_1_0    =0,\n   STBTT_UNICODE_EID_UNICODE_1_1    =1,\n   STBTT_UNICODE_EID_ISO_10646      =2,\n   STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,\n   STBTT_UNICODE_EID_UNICODE_2_0_FULL=4\n};\n\nenum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT\n   STBTT_MS_EID_SYMBOL        =0,\n   STBTT_MS_EID_UNICODE_BMP   =1,\n   STBTT_MS_EID_SHIFTJIS      =2,\n   STBTT_MS_EID_UNICODE_FULL  =10\n};\n\nenum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes\n   STBTT_MAC_EID_ROMAN        =0,   STBTT_MAC_EID_ARABIC       =4,\n   STBTT_MAC_EID_JAPANESE     =1,   STBTT_MAC_EID_HEBREW       =5,\n   STBTT_MAC_EID_CHINESE_TRAD =2,   STBTT_MAC_EID_GREEK        =6,\n   STBTT_MAC_EID_KOREAN       =3,   STBTT_MAC_EID_RUSSIAN      =7\n};\n\nenum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...\n       // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs\n   STBTT_MS_LANG_ENGLISH     =0x0409,   STBTT_MS_LANG_ITALIAN     =0x0410,\n   STBTT_MS_LANG_CHINESE     =0x0804,   STBTT_MS_LANG_JAPANESE    =0x0411,\n   STBTT_MS_LANG_DUTCH       =0x0413,   STBTT_MS_LANG_KOREAN      =0x0412,\n   STBTT_MS_LANG_FRENCH      =0x040c,   STBTT_MS_LANG_RUSSIAN     =0x0419,\n   STBTT_MS_LANG_GERMAN      =0x0407,   STBTT_MS_LANG_SPANISH     =0x0409,\n   STBTT_MS_LANG_HEBREW      =0x040d,   STBTT_MS_LANG_SWEDISH     =0x041D\n};\n\nenum { // languageID for STBTT_PLATFORM_ID_MAC\n   STBTT_MAC_LANG_ENGLISH      =0 ,   STBTT_MAC_LANG_JAPANESE     =11,\n   STBTT_MAC_LANG_ARABIC       =12,   STBTT_MAC_LANG_KOREAN       =23,\n   STBTT_MAC_LANG_DUTCH        =4 ,   STBTT_MAC_LANG_RUSSIAN      =32,\n   STBTT_MAC_LANG_FRENCH       =1 ,   STBTT_MAC_LANG_SPANISH      =6 ,\n   STBTT_MAC_LANG_GERMAN       =2 ,   STBTT_MAC_LANG_SWEDISH      =5 ,\n   STBTT_MAC_LANG_HEBREW       =10,   STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,\n   STBTT_MAC_LANG_ITALIAN      =3 ,   STBTT_MAC_LANG_CHINESE_TRAD =19\n};\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // __STB_INCLUDE_STB_TRUETYPE_H__\n\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n////\n////   IMPLEMENTATION\n////\n////\n\n#ifdef STB_TRUETYPE_IMPLEMENTATION\n\n#ifndef STBTT_MAX_OVERSAMPLE\n#define STBTT_MAX_OVERSAMPLE   8\n#endif\n\n#if STBTT_MAX_OVERSAMPLE > 255\n#error \"STBTT_MAX_OVERSAMPLE cannot be > 255\"\n#endif\n\ntypedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];\n\n#ifndef STBTT_RASTERIZER_VERSION\n#define STBTT_RASTERIZER_VERSION 2\n#endif\n\n#ifdef _MSC_VER\n#define STBTT__NOTUSED(v)  (void)(v)\n#else\n#define STBTT__NOTUSED(v)  (void)sizeof(v)\n#endif\n\n//////////////////////////////////////////////////////////////////////////\n//\n// stbtt__buf helpers to parse data from file\n//\n\nstatic stbtt_uint8 stbtt__buf_get8(stbtt__buf *b)\n{\n   if (b->cursor >= b->size)\n      return 0;\n   return b->data[b->cursor++];\n}\n\nstatic stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b)\n{\n   if (b->cursor >= b->size)\n      return 0;\n   return b->data[b->cursor];\n}\n\nstatic void stbtt__buf_seek(stbtt__buf *b, int o)\n{\n   STBTT_assert(!(o > b->size || o < 0));\n   b->cursor = (o > b->size || o < 0) ? b->size : o;\n}\n\nstatic void stbtt__buf_skip(stbtt__buf *b, int o)\n{\n   stbtt__buf_seek(b, b->cursor + o);\n}\n\nstatic stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n)\n{\n   stbtt_uint32 v = 0;\n   int i;\n   STBTT_assert(n >= 1 && n <= 4);\n   for (i = 0; i < n; i++)\n      v = (v << 8) | stbtt__buf_get8(b);\n   return v;\n}\n\nstatic stbtt__buf stbtt__new_buf(const void *p, size_t size)\n{\n   stbtt__buf r;\n   STBTT_assert(size < 0x40000000);\n   r.data = (stbtt_uint8*) p;\n   r.size = (int) size;\n   r.cursor = 0;\n   return r;\n}\n\n#define stbtt__buf_get16(b)  stbtt__buf_get((b), 2)\n#define stbtt__buf_get32(b)  stbtt__buf_get((b), 4)\n\nstatic stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s)\n{\n   stbtt__buf r = stbtt__new_buf(NULL, 0);\n   if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r;\n   r.data = b->data + o;\n   r.size = s;\n   return r;\n}\n\nstatic stbtt__buf stbtt__cff_get_index(stbtt__buf *b)\n{\n   int count, start, offsize;\n   start = b->cursor;\n   count = stbtt__buf_get16(b);\n   if (count) {\n      offsize = stbtt__buf_get8(b);\n      STBTT_assert(offsize >= 1 && offsize <= 4);\n      stbtt__buf_skip(b, offsize * count);\n      stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1);\n   }\n   return stbtt__buf_range(b, start, b->cursor - start);\n}\n\nstatic stbtt_uint32 stbtt__cff_int(stbtt__buf *b)\n{\n   int b0 = stbtt__buf_get8(b);\n   if (b0 >= 32 && b0 <= 246)       return b0 - 139;\n   else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108;\n   else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108;\n   else if (b0 == 28)               return stbtt__buf_get16(b);\n   else if (b0 == 29)               return stbtt__buf_get32(b);\n   STBTT_assert(0);\n   return 0;\n}\n\nstatic void stbtt__cff_skip_operand(stbtt__buf *b) {\n   int v, b0 = stbtt__buf_peek8(b);\n   STBTT_assert(b0 >= 28);\n   if (b0 == 30) {\n      stbtt__buf_skip(b, 1);\n      while (b->cursor < b->size) {\n         v = stbtt__buf_get8(b);\n         if ((v & 0xF) == 0xF || (v >> 4) == 0xF)\n            break;\n      }\n   } else {\n      stbtt__cff_int(b);\n   }\n}\n\nstatic stbtt__buf stbtt__dict_get(stbtt__buf *b, int key)\n{\n   stbtt__buf_seek(b, 0);\n   while (b->cursor < b->size) {\n      int start = b->cursor, end, op;\n      while (stbtt__buf_peek8(b) >= 28)\n         stbtt__cff_skip_operand(b);\n      end = b->cursor;\n      op = stbtt__buf_get8(b);\n      if (op == 12)  op = stbtt__buf_get8(b) | 0x100;\n      if (op == key) return stbtt__buf_range(b, start, end-start);\n   }\n   return stbtt__buf_range(b, 0, 0);\n}\n\nstatic void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out)\n{\n   int i;\n   stbtt__buf operands = stbtt__dict_get(b, key);\n   for (i = 0; i < outcount && operands.cursor < operands.size; i++)\n      out[i] = stbtt__cff_int(&operands);\n}\n\nstatic int stbtt__cff_index_count(stbtt__buf *b)\n{\n   stbtt__buf_seek(b, 0);\n   return stbtt__buf_get16(b);\n}\n\nstatic stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i)\n{\n   int count, offsize, start, end;\n   stbtt__buf_seek(&b, 0);\n   count = stbtt__buf_get16(&b);\n   offsize = stbtt__buf_get8(&b);\n   STBTT_assert(i >= 0 && i < count);\n   STBTT_assert(offsize >= 1 && offsize <= 4);\n   stbtt__buf_skip(&b, i*offsize);\n   start = stbtt__buf_get(&b, offsize);\n   end = stbtt__buf_get(&b, offsize);\n   return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start);\n}\n\n//////////////////////////////////////////////////////////////////////////\n//\n// accessors to parse data from file\n//\n\n// on platforms that don't allow misaligned reads, if we want to allow\n// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE\n\n#define ttBYTE(p)     (* (stbtt_uint8 *) (p))\n#define ttCHAR(p)     (* (stbtt_int8 *) (p))\n#define ttFixed(p)    ttLONG(p)\n\nstatic stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }\nstatic stbtt_int16 ttSHORT(stbtt_uint8 *p)   { return p[0]*256 + p[1]; }\nstatic stbtt_uint32 ttULONG(stbtt_uint8 *p)  { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }\nstatic stbtt_int32 ttLONG(stbtt_uint8 *p)    { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }\n\n#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))\n#define stbtt_tag(p,str)           stbtt_tag4(p,str[0],str[1],str[2],str[3])\n\nstatic int stbtt__isfont(stbtt_uint8 *font)\n{\n   // check the version number\n   if (stbtt_tag4(font, '1',0,0,0))  return 1; // TrueType 1\n   if (stbtt_tag(font, \"typ1\"))   return 1; // TrueType with type 1 font -- we don't support this!\n   if (stbtt_tag(font, \"OTTO\"))   return 1; // OpenType with CFF\n   if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0\n   if (stbtt_tag(font, \"true\"))   return 1; // Apple specification for TrueType fonts\n   return 0;\n}\n\n// @OPTIMIZE: binary search\nstatic stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)\n{\n   stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);\n   stbtt_uint32 tabledir = fontstart + 12;\n   stbtt_int32 i;\n   for (i=0; i < num_tables; ++i) {\n      stbtt_uint32 loc = tabledir + 16*i;\n      if (stbtt_tag(data+loc+0, tag))\n         return ttULONG(data+loc+8);\n   }\n   return 0;\n}\n\nstatic int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index)\n{\n   // if it's just a font, there's only one valid index\n   if (stbtt__isfont(font_collection))\n      return index == 0 ? 0 : -1;\n\n   // check if it's a TTC\n   if (stbtt_tag(font_collection, \"ttcf\")) {\n      // version 1?\n      if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {\n         stbtt_int32 n = ttLONG(font_collection+8);\n         if (index >= n)\n            return -1;\n         return ttULONG(font_collection+12+index*4);\n      }\n   }\n   return -1;\n}\n\nstatic int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection)\n{\n   // if it's just a font, there's only one valid font\n   if (stbtt__isfont(font_collection))\n      return 1;\n\n   // check if it's a TTC\n   if (stbtt_tag(font_collection, \"ttcf\")) {\n      // version 1?\n      if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {\n         return ttLONG(font_collection+8);\n      }\n   }\n   return 0;\n}\n\nstatic stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict)\n{\n   stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 };\n   stbtt__buf pdict;\n   stbtt__dict_get_ints(&fontdict, 18, 2, private_loc);\n   if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0);\n   pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]);\n   stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff);\n   if (!subrsoff) return stbtt__new_buf(NULL, 0);\n   stbtt__buf_seek(&cff, private_loc[1]+subrsoff);\n   return stbtt__cff_get_index(&cff);\n}\n\n// since most people won't use this, find this table the first time it's needed\nstatic int stbtt__get_svg(stbtt_fontinfo *info)\n{\n   stbtt_uint32 t;\n   if (info->svg < 0) {\n      t = stbtt__find_table(info->data, info->fontstart, \"SVG \");\n      if (t) {\n         stbtt_uint32 offset = ttULONG(info->data + t + 2);\n         info->svg = t + offset;\n      } else {\n         info->svg = 0;\n      }\n   }\n   return info->svg;\n}\n\nstatic int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart)\n{\n   stbtt_uint32 cmap, t;\n   stbtt_int32 i,numTables;\n\n   info->data = data;\n   info->fontstart = fontstart;\n   info->cff = stbtt__new_buf(NULL, 0);\n\n   cmap = stbtt__find_table(data, fontstart, \"cmap\");       // required\n   info->loca = stbtt__find_table(data, fontstart, \"loca\"); // required\n   info->head = stbtt__find_table(data, fontstart, \"head\"); // required\n   info->glyf = stbtt__find_table(data, fontstart, \"glyf\"); // required\n   info->hhea = stbtt__find_table(data, fontstart, \"hhea\"); // required\n   info->hmtx = stbtt__find_table(data, fontstart, \"hmtx\"); // required\n   info->kern = stbtt__find_table(data, fontstart, \"kern\"); // not required\n   info->gpos = stbtt__find_table(data, fontstart, \"GPOS\"); // not required\n\n   if (!cmap || !info->head || !info->hhea || !info->hmtx)\n      return 0;\n   if (info->glyf) {\n      // required for truetype\n      if (!info->loca) return 0;\n   } else {\n      // initialization for CFF / Type2 fonts (OTF)\n      stbtt__buf b, topdict, topdictidx;\n      stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0;\n      stbtt_uint32 cff;\n\n      cff = stbtt__find_table(data, fontstart, \"CFF \");\n      if (!cff) return 0;\n\n      info->fontdicts = stbtt__new_buf(NULL, 0);\n      info->fdselect = stbtt__new_buf(NULL, 0);\n\n      // @TODO this should use size from table (not 512MB)\n      info->cff = stbtt__new_buf(data+cff, 512*1024*1024);\n      b = info->cff;\n\n      // read the header\n      stbtt__buf_skip(&b, 2);\n      stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize\n\n      // @TODO the name INDEX could list multiple fonts,\n      // but we just use the first one.\n      stbtt__cff_get_index(&b);  // name INDEX\n      topdictidx = stbtt__cff_get_index(&b);\n      topdict = stbtt__cff_index_get(topdictidx, 0);\n      stbtt__cff_get_index(&b);  // string INDEX\n      info->gsubrs = stbtt__cff_get_index(&b);\n\n      stbtt__dict_get_ints(&topdict, 17, 1, &charstrings);\n      stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype);\n      stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff);\n      stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff);\n      info->subrs = stbtt__get_subrs(b, topdict);\n\n      // we only support Type 2 charstrings\n      if (cstype != 2) return 0;\n      if (charstrings == 0) return 0;\n\n      if (fdarrayoff) {\n         // looks like a CID font\n         if (!fdselectoff) return 0;\n         stbtt__buf_seek(&b, fdarrayoff);\n         info->fontdicts = stbtt__cff_get_index(&b);\n         info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff);\n      }\n\n      stbtt__buf_seek(&b, charstrings);\n      info->charstrings = stbtt__cff_get_index(&b);\n   }\n\n   t = stbtt__find_table(data, fontstart, \"maxp\");\n   if (t)\n      info->numGlyphs = ttUSHORT(data+t+4);\n   else\n      info->numGlyphs = 0xffff;\n\n   info->svg = -1;\n\n   // find a cmap encoding table we understand *now* to avoid searching\n   // later. (todo: could make this installable)\n   // the same regardless of glyph.\n   numTables = ttUSHORT(data + cmap + 2);\n   info->index_map = 0;\n   for (i=0; i < numTables; ++i) {\n      stbtt_uint32 encoding_record = cmap + 4 + 8 * i;\n      // find an encoding we understand:\n      switch(ttUSHORT(data+encoding_record)) {\n         case STBTT_PLATFORM_ID_MICROSOFT:\n            switch (ttUSHORT(data+encoding_record+2)) {\n               case STBTT_MS_EID_UNICODE_BMP:\n               case STBTT_MS_EID_UNICODE_FULL:\n                  // MS/Unicode\n                  info->index_map = cmap + ttULONG(data+encoding_record+4);\n                  break;\n            }\n            break;\n        case STBTT_PLATFORM_ID_UNICODE:\n            // Mac/iOS has these\n            // all the encodingIDs are unicode, so we don't bother to check it\n            info->index_map = cmap + ttULONG(data+encoding_record+4);\n            break;\n      }\n   }\n   if (info->index_map == 0)\n      return 0;\n\n   info->indexToLocFormat = ttUSHORT(data+info->head + 50);\n   return 1;\n}\n\nSTBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)\n{\n   stbtt_uint8 *data = info->data;\n   stbtt_uint32 index_map = info->index_map;\n\n   stbtt_uint16 format = ttUSHORT(data + index_map + 0);\n   if (format == 0) { // apple byte encoding\n      stbtt_int32 bytes = ttUSHORT(data + index_map + 2);\n      if (unicode_codepoint < bytes-6)\n         return ttBYTE(data + index_map + 6 + unicode_codepoint);\n      return 0;\n   } else if (format == 6) {\n      stbtt_uint32 first = ttUSHORT(data + index_map + 6);\n      stbtt_uint32 count = ttUSHORT(data + index_map + 8);\n      if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)\n         return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);\n      return 0;\n   } else if (format == 2) {\n      STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean\n      return 0;\n   } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges\n      stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;\n      stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;\n      stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);\n      stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;\n\n      // do a binary search of the segments\n      stbtt_uint32 endCount = index_map + 14;\n      stbtt_uint32 search = endCount;\n\n      if (unicode_codepoint > 0xffff)\n         return 0;\n\n      // they lie from endCount .. endCount + segCount\n      // but searchRange is the nearest power of two, so...\n      if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))\n         search += rangeShift*2;\n\n      // now decrement to bias correctly to find smallest\n      search -= 2;\n      while (entrySelector) {\n         stbtt_uint16 end;\n         searchRange >>= 1;\n         end = ttUSHORT(data + search + searchRange*2);\n         if (unicode_codepoint > end)\n            search += searchRange*2;\n         --entrySelector;\n      }\n      search += 2;\n\n      {\n         stbtt_uint16 offset, start, last;\n         stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);\n\n         start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);\n         last = ttUSHORT(data + endCount + 2*item);\n         if (unicode_codepoint < start || unicode_codepoint > last)\n            return 0;\n\n         offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);\n         if (offset == 0)\n            return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));\n\n         return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);\n      }\n   } else if (format == 12 || format == 13) {\n      stbtt_uint32 ngroups = ttULONG(data+index_map+12);\n      stbtt_int32 low,high;\n      low = 0; high = (stbtt_int32)ngroups;\n      // Binary search the right group.\n      while (low < high) {\n         stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high\n         stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);\n         stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);\n         if ((stbtt_uint32) unicode_codepoint < start_char)\n            high = mid;\n         else if ((stbtt_uint32) unicode_codepoint > end_char)\n            low = mid+1;\n         else {\n            stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);\n            if (format == 12)\n               return start_glyph + unicode_codepoint-start_char;\n            else // format == 13\n               return start_glyph;\n         }\n      }\n      return 0; // not found\n   }\n   // @TODO\n   STBTT_assert(0);\n   return 0;\n}\n\nSTBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)\n{\n   return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);\n}\n\nstatic void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)\n{\n   v->type = type;\n   v->x = (stbtt_int16) x;\n   v->y = (stbtt_int16) y;\n   v->cx = (stbtt_int16) cx;\n   v->cy = (stbtt_int16) cy;\n}\n\nstatic int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)\n{\n   int g1,g2;\n\n   STBTT_assert(!info->cff.size);\n\n   if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range\n   if (info->indexToLocFormat >= 2)    return -1; // unknown index->glyph map format\n\n   if (info->indexToLocFormat == 0) {\n      g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;\n      g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;\n   } else {\n      g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);\n      g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);\n   }\n\n   return g1==g2 ? -1 : g1; // if length is 0, return -1\n}\n\nstatic int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);\n\nSTBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)\n{\n   if (info->cff.size) {\n      stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1);\n   } else {\n      int g = stbtt__GetGlyfOffset(info, glyph_index);\n      if (g < 0) return 0;\n\n      if (x0) *x0 = ttSHORT(info->data + g + 2);\n      if (y0) *y0 = ttSHORT(info->data + g + 4);\n      if (x1) *x1 = ttSHORT(info->data + g + 6);\n      if (y1) *y1 = ttSHORT(info->data + g + 8);\n   }\n   return 1;\n}\n\nSTBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)\n{\n   return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);\n}\n\nSTBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)\n{\n   stbtt_int16 numberOfContours;\n   int g;\n   if (info->cff.size)\n      return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0;\n   g = stbtt__GetGlyfOffset(info, glyph_index);\n   if (g < 0) return 1;\n   numberOfContours = ttSHORT(info->data + g);\n   return numberOfContours == 0;\n}\n\nstatic int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,\n    stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)\n{\n   if (start_off) {\n      if (was_off)\n         stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);\n      stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);\n   } else {\n      if (was_off)\n         stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);\n      else\n         stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);\n   }\n   return num_vertices;\n}\n\nstatic int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)\n{\n   stbtt_int16 numberOfContours;\n   stbtt_uint8 *endPtsOfContours;\n   stbtt_uint8 *data = info->data;\n   stbtt_vertex *vertices=0;\n   int num_vertices=0;\n   int g = stbtt__GetGlyfOffset(info, glyph_index);\n\n   *pvertices = NULL;\n\n   if (g < 0) return 0;\n\n   numberOfContours = ttSHORT(data + g);\n\n   if (numberOfContours > 0) {\n      stbtt_uint8 flags=0,flagcount;\n      stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;\n      stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;\n      stbtt_uint8 *points;\n      endPtsOfContours = (data + g + 10);\n      ins = ttUSHORT(data + g + 10 + numberOfContours * 2);\n      points = data + g + 10 + numberOfContours * 2 + 2 + ins;\n\n      n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);\n\n      m = n + 2*numberOfContours;  // a loose bound on how many vertices we might need\n      vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);\n      if (vertices == 0)\n         return 0;\n\n      next_move = 0;\n      flagcount=0;\n\n      // in first pass, we load uninterpreted data into the allocated array\n      // above, shifted to the end of the array so we won't overwrite it when\n      // we create our final data starting from the front\n\n      off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated\n\n      // first load flags\n\n      for (i=0; i < n; ++i) {\n         if (flagcount == 0) {\n            flags = *points++;\n            if (flags & 8)\n               flagcount = *points++;\n         } else\n            --flagcount;\n         vertices[off+i].type = flags;\n      }\n\n      // now load x coordinates\n      x=0;\n      for (i=0; i < n; ++i) {\n         flags = vertices[off+i].type;\n         if (flags & 2) {\n            stbtt_int16 dx = *points++;\n            x += (flags & 16) ? dx : -dx; // ???\n         } else {\n            if (!(flags & 16)) {\n               x = x + (stbtt_int16) (points[0]*256 + points[1]);\n               points += 2;\n            }\n         }\n         vertices[off+i].x = (stbtt_int16) x;\n      }\n\n      // now load y coordinates\n      y=0;\n      for (i=0; i < n; ++i) {\n         flags = vertices[off+i].type;\n         if (flags & 4) {\n            stbtt_int16 dy = *points++;\n            y += (flags & 32) ? dy : -dy; // ???\n         } else {\n            if (!(flags & 32)) {\n               y = y + (stbtt_int16) (points[0]*256 + points[1]);\n               points += 2;\n            }\n         }\n         vertices[off+i].y = (stbtt_int16) y;\n      }\n\n      // now convert them to our format\n      num_vertices=0;\n      sx = sy = cx = cy = scx = scy = 0;\n      for (i=0; i < n; ++i) {\n         flags = vertices[off+i].type;\n         x     = (stbtt_int16) vertices[off+i].x;\n         y     = (stbtt_int16) vertices[off+i].y;\n\n         if (next_move == i) {\n            if (i != 0)\n               num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);\n\n            // now start the new one\n            start_off = !(flags & 1);\n            if (start_off) {\n               // if we start off with an off-curve point, then when we need to find a point on the curve\n               // where we can start, and we need to save some state for when we wraparound.\n               scx = x;\n               scy = y;\n               if (!(vertices[off+i+1].type & 1)) {\n                  // next point is also a curve point, so interpolate an on-point curve\n                  sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;\n                  sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;\n               } else {\n                  // otherwise just use the next point as our start point\n                  sx = (stbtt_int32) vertices[off+i+1].x;\n                  sy = (stbtt_int32) vertices[off+i+1].y;\n                  ++i; // we're using point i+1 as the starting point, so skip it\n               }\n            } else {\n               sx = x;\n               sy = y;\n            }\n            stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);\n            was_off = 0;\n            next_move = 1 + ttUSHORT(endPtsOfContours+j*2);\n            ++j;\n         } else {\n            if (!(flags & 1)) { // if it's a curve\n               if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint\n                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);\n               cx = x;\n               cy = y;\n               was_off = 1;\n            } else {\n               if (was_off)\n                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);\n               else\n                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);\n               was_off = 0;\n            }\n         }\n      }\n      num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);\n   } else if (numberOfContours < 0) {\n      // Compound shapes.\n      int more = 1;\n      stbtt_uint8 *comp = data + g + 10;\n      num_vertices = 0;\n      vertices = 0;\n      while (more) {\n         stbtt_uint16 flags, gidx;\n         int comp_num_verts = 0, i;\n         stbtt_vertex *comp_verts = 0, *tmp = 0;\n         float mtx[6] = {1,0,0,1,0,0}, m, n;\n\n         flags = ttSHORT(comp); comp+=2;\n         gidx = ttSHORT(comp); comp+=2;\n\n         if (flags & 2) { // XY values\n            if (flags & 1) { // shorts\n               mtx[4] = ttSHORT(comp); comp+=2;\n               mtx[5] = ttSHORT(comp); comp+=2;\n            } else {\n               mtx[4] = ttCHAR(comp); comp+=1;\n               mtx[5] = ttCHAR(comp); comp+=1;\n            }\n         }\n         else {\n            // @TODO handle matching point\n            STBTT_assert(0);\n         }\n         if (flags & (1<<3)) { // WE_HAVE_A_SCALE\n            mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[1] = mtx[2] = 0;\n         } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE\n            mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[1] = mtx[2] = 0;\n            mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;\n         } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO\n            mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;\n         }\n\n         // Find transformation scales.\n         m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);\n         n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);\n\n         // Get indexed glyph.\n         comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);\n         if (comp_num_verts > 0) {\n            // Transform vertices.\n            for (i = 0; i < comp_num_verts; ++i) {\n               stbtt_vertex* v = &comp_verts[i];\n               stbtt_vertex_type x,y;\n               x=v->x; y=v->y;\n               v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));\n               v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));\n               x=v->cx; y=v->cy;\n               v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));\n               v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));\n            }\n            // Append vertices.\n            tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);\n            if (!tmp) {\n               if (vertices) STBTT_free(vertices, info->userdata);\n               if (comp_verts) STBTT_free(comp_verts, info->userdata);\n               return 0;\n            }\n            if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));\n            STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));\n            if (vertices) STBTT_free(vertices, info->userdata);\n            vertices = tmp;\n            STBTT_free(comp_verts, info->userdata);\n            num_vertices += comp_num_verts;\n         }\n         // More components ?\n         more = flags & (1<<5);\n      }\n   } else {\n      // numberOfCounters == 0, do nothing\n   }\n\n   *pvertices = vertices;\n   return num_vertices;\n}\n\ntypedef struct\n{\n   int bounds;\n   int started;\n   float first_x, first_y;\n   float x, y;\n   stbtt_int32 min_x, max_x, min_y, max_y;\n\n   stbtt_vertex *pvertices;\n   int num_vertices;\n} stbtt__csctx;\n\n#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0}\n\nstatic void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y)\n{\n   if (x > c->max_x || !c->started) c->max_x = x;\n   if (y > c->max_y || !c->started) c->max_y = y;\n   if (x < c->min_x || !c->started) c->min_x = x;\n   if (y < c->min_y || !c->started) c->min_y = y;\n   c->started = 1;\n}\n\nstatic void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1)\n{\n   if (c->bounds) {\n      stbtt__track_vertex(c, x, y);\n      if (type == STBTT_vcubic) {\n         stbtt__track_vertex(c, cx, cy);\n         stbtt__track_vertex(c, cx1, cy1);\n      }\n   } else {\n      stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy);\n      c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1;\n      c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1;\n   }\n   c->num_vertices++;\n}\n\nstatic void stbtt__csctx_close_shape(stbtt__csctx *ctx)\n{\n   if (ctx->first_x != ctx->x || ctx->first_y != ctx->y)\n      stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0);\n}\n\nstatic void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy)\n{\n   stbtt__csctx_close_shape(ctx);\n   ctx->first_x = ctx->x = ctx->x + dx;\n   ctx->first_y = ctx->y = ctx->y + dy;\n   stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);\n}\n\nstatic void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy)\n{\n   ctx->x += dx;\n   ctx->y += dy;\n   stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);\n}\n\nstatic void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)\n{\n   float cx1 = ctx->x + dx1;\n   float cy1 = ctx->y + dy1;\n   float cx2 = cx1 + dx2;\n   float cy2 = cy1 + dy2;\n   ctx->x = cx2 + dx3;\n   ctx->y = cy2 + dy3;\n   stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2);\n}\n\nstatic stbtt__buf stbtt__get_subr(stbtt__buf idx, int n)\n{\n   int count = stbtt__cff_index_count(&idx);\n   int bias = 107;\n   if (count >= 33900)\n      bias = 32768;\n   else if (count >= 1240)\n      bias = 1131;\n   n += bias;\n   if (n < 0 || n >= count)\n      return stbtt__new_buf(NULL, 0);\n   return stbtt__cff_index_get(idx, n);\n}\n\nstatic stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index)\n{\n   stbtt__buf fdselect = info->fdselect;\n   int nranges, start, end, v, fmt, fdselector = -1, i;\n\n   stbtt__buf_seek(&fdselect, 0);\n   fmt = stbtt__buf_get8(&fdselect);\n   if (fmt == 0) {\n      // untested\n      stbtt__buf_skip(&fdselect, glyph_index);\n      fdselector = stbtt__buf_get8(&fdselect);\n   } else if (fmt == 3) {\n      nranges = stbtt__buf_get16(&fdselect);\n      start = stbtt__buf_get16(&fdselect);\n      for (i = 0; i < nranges; i++) {\n         v = stbtt__buf_get8(&fdselect);\n         end = stbtt__buf_get16(&fdselect);\n         if (glyph_index >= start && glyph_index < end) {\n            fdselector = v;\n            break;\n         }\n         start = end;\n      }\n   }\n   if (fdselector == -1) return stbtt__new_buf(NULL, 0); // [DEAR IMGUI] fixed, see #6007 and nothings/stb#1422\n   return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));\n}\n\nstatic int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c)\n{\n   int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0;\n   int has_subrs = 0, clear_stack;\n   float s[48];\n   stbtt__buf subr_stack[10], subrs = info->subrs, b;\n   float f;\n\n#define STBTT__CSERR(s) (0)\n\n   // this currently ignores the initial width value, which isn't needed if we have hmtx\n   b = stbtt__cff_index_get(info->charstrings, glyph_index);\n   while (b.cursor < b.size) {\n      i = 0;\n      clear_stack = 1;\n      b0 = stbtt__buf_get8(&b);\n      switch (b0) {\n      // @TODO implement hinting\n      case 0x13: // hintmask\n      case 0x14: // cntrmask\n         if (in_header)\n            maskbits += (sp / 2); // implicit \"vstem\"\n         in_header = 0;\n         stbtt__buf_skip(&b, (maskbits + 7) / 8);\n         break;\n\n      case 0x01: // hstem\n      case 0x03: // vstem\n      case 0x12: // hstemhm\n      case 0x17: // vstemhm\n         maskbits += (sp / 2);\n         break;\n\n      case 0x15: // rmoveto\n         in_header = 0;\n         if (sp < 2) return STBTT__CSERR(\"rmoveto stack\");\n         stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]);\n         break;\n      case 0x04: // vmoveto\n         in_header = 0;\n         if (sp < 1) return STBTT__CSERR(\"vmoveto stack\");\n         stbtt__csctx_rmove_to(c, 0, s[sp-1]);\n         break;\n      case 0x16: // hmoveto\n         in_header = 0;\n         if (sp < 1) return STBTT__CSERR(\"hmoveto stack\");\n         stbtt__csctx_rmove_to(c, s[sp-1], 0);\n         break;\n\n      case 0x05: // rlineto\n         if (sp < 2) return STBTT__CSERR(\"rlineto stack\");\n         for (; i + 1 < sp; i += 2)\n            stbtt__csctx_rline_to(c, s[i], s[i+1]);\n         break;\n\n      // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical\n      // starting from a different place.\n\n      case 0x07: // vlineto\n         if (sp < 1) return STBTT__CSERR(\"vlineto stack\");\n         goto vlineto;\n      case 0x06: // hlineto\n         if (sp < 1) return STBTT__CSERR(\"hlineto stack\");\n         for (;;) {\n            if (i >= sp) break;\n            stbtt__csctx_rline_to(c, s[i], 0);\n            i++;\n      vlineto:\n            if (i >= sp) break;\n            stbtt__csctx_rline_to(c, 0, s[i]);\n            i++;\n         }\n         break;\n\n      case 0x1F: // hvcurveto\n         if (sp < 4) return STBTT__CSERR(\"hvcurveto stack\");\n         goto hvcurveto;\n      case 0x1E: // vhcurveto\n         if (sp < 4) return STBTT__CSERR(\"vhcurveto stack\");\n         for (;;) {\n            if (i + 3 >= sp) break;\n            stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f);\n            i += 4;\n      hvcurveto:\n            if (i + 3 >= sp) break;\n            stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]);\n            i += 4;\n         }\n         break;\n\n      case 0x08: // rrcurveto\n         if (sp < 6) return STBTT__CSERR(\"rcurveline stack\");\n         for (; i + 5 < sp; i += 6)\n            stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);\n         break;\n\n      case 0x18: // rcurveline\n         if (sp < 8) return STBTT__CSERR(\"rcurveline stack\");\n         for (; i + 5 < sp - 2; i += 6)\n            stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);\n         if (i + 1 >= sp) return STBTT__CSERR(\"rcurveline stack\");\n         stbtt__csctx_rline_to(c, s[i], s[i+1]);\n         break;\n\n      case 0x19: // rlinecurve\n         if (sp < 8) return STBTT__CSERR(\"rlinecurve stack\");\n         for (; i + 1 < sp - 6; i += 2)\n            stbtt__csctx_rline_to(c, s[i], s[i+1]);\n         if (i + 5 >= sp) return STBTT__CSERR(\"rlinecurve stack\");\n         stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);\n         break;\n\n      case 0x1A: // vvcurveto\n      case 0x1B: // hhcurveto\n         if (sp < 4) return STBTT__CSERR(\"(vv|hh)curveto stack\");\n         f = 0.0;\n         if (sp & 1) { f = s[i]; i++; }\n         for (; i + 3 < sp; i += 4) {\n            if (b0 == 0x1B)\n               stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0);\n            else\n               stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]);\n            f = 0.0;\n         }\n         break;\n\n      case 0x0A: // callsubr\n         if (!has_subrs) {\n            if (info->fdselect.size)\n               subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);\n            has_subrs = 1;\n         }\n         // FALLTHROUGH\n      case 0x1D: // callgsubr\n         if (sp < 1) return STBTT__CSERR(\"call(g|)subr stack\");\n         v = (int) s[--sp];\n         if (subr_stack_height >= 10) return STBTT__CSERR(\"recursion limit\");\n         subr_stack[subr_stack_height++] = b;\n         b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v);\n         if (b.size == 0) return STBTT__CSERR(\"subr not found\");\n         b.cursor = 0;\n         clear_stack = 0;\n         break;\n\n      case 0x0B: // return\n         if (subr_stack_height <= 0) return STBTT__CSERR(\"return outside subr\");\n         b = subr_stack[--subr_stack_height];\n         clear_stack = 0;\n         break;\n\n      case 0x0E: // endchar\n         stbtt__csctx_close_shape(c);\n         return 1;\n\n      case 0x0C: { // two-byte escape\n         float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6;\n         float dx, dy;\n         int b1 = stbtt__buf_get8(&b);\n         switch (b1) {\n         // @TODO These \"flex\" implementations ignore the flex-depth and resolution,\n         // and always draw beziers.\n         case 0x22: // hflex\n            if (sp < 7) return STBTT__CSERR(\"hflex stack\");\n            dx1 = s[0];\n            dx2 = s[1];\n            dy2 = s[2];\n            dx3 = s[3];\n            dx4 = s[4];\n            dx5 = s[5];\n            dx6 = s[6];\n            stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0);\n            stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0);\n            break;\n\n         case 0x23: // flex\n            if (sp < 13) return STBTT__CSERR(\"flex stack\");\n            dx1 = s[0];\n            dy1 = s[1];\n            dx2 = s[2];\n            dy2 = s[3];\n            dx3 = s[4];\n            dy3 = s[5];\n            dx4 = s[6];\n            dy4 = s[7];\n            dx5 = s[8];\n            dy5 = s[9];\n            dx6 = s[10];\n            dy6 = s[11];\n            //fd is s[12]\n            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);\n            stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);\n            break;\n\n         case 0x24: // hflex1\n            if (sp < 9) return STBTT__CSERR(\"hflex1 stack\");\n            dx1 = s[0];\n            dy1 = s[1];\n            dx2 = s[2];\n            dy2 = s[3];\n            dx3 = s[4];\n            dx4 = s[5];\n            dx5 = s[6];\n            dy5 = s[7];\n            dx6 = s[8];\n            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0);\n            stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5));\n            break;\n\n         case 0x25: // flex1\n            if (sp < 11) return STBTT__CSERR(\"flex1 stack\");\n            dx1 = s[0];\n            dy1 = s[1];\n            dx2 = s[2];\n            dy2 = s[3];\n            dx3 = s[4];\n            dy3 = s[5];\n            dx4 = s[6];\n            dy4 = s[7];\n            dx5 = s[8];\n            dy5 = s[9];\n            dx6 = dy6 = s[10];\n            dx = dx1+dx2+dx3+dx4+dx5;\n            dy = dy1+dy2+dy3+dy4+dy5;\n            if (STBTT_fabs(dx) > STBTT_fabs(dy))\n               dy6 = -dy;\n            else\n               dx6 = -dx;\n            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);\n            stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);\n            break;\n\n         default:\n            return STBTT__CSERR(\"unimplemented\");\n         }\n      } break;\n\n      default:\n         if (b0 != 255 && b0 != 28 && b0 < 32)\n            return STBTT__CSERR(\"reserved operator\");\n\n         // push immediate\n         if (b0 == 255) {\n            f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000;\n         } else {\n            stbtt__buf_skip(&b, -1);\n            f = (float)(stbtt_int16)stbtt__cff_int(&b);\n         }\n         if (sp >= 48) return STBTT__CSERR(\"push stack overflow\");\n         s[sp++] = f;\n         clear_stack = 0;\n         break;\n      }\n      if (clear_stack) sp = 0;\n   }\n   return STBTT__CSERR(\"no endchar\");\n\n#undef STBTT__CSERR\n}\n\nstatic int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)\n{\n   // runs the charstring twice, once to count and once to output (to avoid realloc)\n   stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1);\n   stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0);\n   if (stbtt__run_charstring(info, glyph_index, &count_ctx)) {\n      *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata);\n      output_ctx.pvertices = *pvertices;\n      if (stbtt__run_charstring(info, glyph_index, &output_ctx)) {\n         STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices);\n         return output_ctx.num_vertices;\n      }\n   }\n   *pvertices = NULL;\n   return 0;\n}\n\nstatic int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)\n{\n   stbtt__csctx c = STBTT__CSCTX_INIT(1);\n   int r = stbtt__run_charstring(info, glyph_index, &c);\n   if (x0)  *x0 = r ? c.min_x : 0;\n   if (y0)  *y0 = r ? c.min_y : 0;\n   if (x1)  *x1 = r ? c.max_x : 0;\n   if (y1)  *y1 = r ? c.max_y : 0;\n   return r ? c.num_vertices : 0;\n}\n\nSTBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)\n{\n   if (!info->cff.size)\n      return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices);\n   else\n      return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices);\n}\n\nSTBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)\n{\n   stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);\n   if (glyph_index < numOfLongHorMetrics) {\n      if (advanceWidth)     *advanceWidth    = ttSHORT(info->data + info->hmtx + 4*glyph_index);\n      if (leftSideBearing)  *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);\n   } else {\n      if (advanceWidth)     *advanceWidth    = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));\n      if (leftSideBearing)  *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));\n   }\n}\n\nSTBTT_DEF int  stbtt_GetKerningTableLength(const stbtt_fontinfo *info)\n{\n   stbtt_uint8 *data = info->data + info->kern;\n\n   // we only look at the first table. it must be 'horizontal' and format 0.\n   if (!info->kern)\n      return 0;\n   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1\n      return 0;\n   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format\n      return 0;\n\n   return ttUSHORT(data+10);\n}\n\nSTBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length)\n{\n   stbtt_uint8 *data = info->data + info->kern;\n   int k, length;\n\n   // we only look at the first table. it must be 'horizontal' and format 0.\n   if (!info->kern)\n      return 0;\n   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1\n      return 0;\n   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format\n      return 0;\n\n   length = ttUSHORT(data+10);\n   if (table_length < length)\n      length = table_length;\n\n   for (k = 0; k < length; k++)\n   {\n      table[k].glyph1 = ttUSHORT(data+18+(k*6));\n      table[k].glyph2 = ttUSHORT(data+20+(k*6));\n      table[k].advance = ttSHORT(data+22+(k*6));\n   }\n\n   return length;\n}\n\nstatic int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)\n{\n   stbtt_uint8 *data = info->data + info->kern;\n   stbtt_uint32 needle, straw;\n   int l, r, m;\n\n   // we only look at the first table. it must be 'horizontal' and format 0.\n   if (!info->kern)\n      return 0;\n   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1\n      return 0;\n   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format\n      return 0;\n\n   l = 0;\n   r = ttUSHORT(data+10) - 1;\n   needle = glyph1 << 16 | glyph2;\n   while (l <= r) {\n      m = (l + r) >> 1;\n      straw = ttULONG(data+18+(m*6)); // note: unaligned read\n      if (needle < straw)\n         r = m - 1;\n      else if (needle > straw)\n         l = m + 1;\n      else\n         return ttSHORT(data+22+(m*6));\n   }\n   return 0;\n}\n\nstatic stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)\n{\n   stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);\n   switch (coverageFormat) {\n      case 1: {\n         stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);\n\n         // Binary search.\n         stbtt_int32 l=0, r=glyphCount-1, m;\n         int straw, needle=glyph;\n         while (l <= r) {\n            stbtt_uint8 *glyphArray = coverageTable + 4;\n            stbtt_uint16 glyphID;\n            m = (l + r) >> 1;\n            glyphID = ttUSHORT(glyphArray + 2 * m);\n            straw = glyphID;\n            if (needle < straw)\n               r = m - 1;\n            else if (needle > straw)\n               l = m + 1;\n            else {\n               return m;\n            }\n         }\n         break;\n      }\n\n      case 2: {\n         stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);\n         stbtt_uint8 *rangeArray = coverageTable + 4;\n\n         // Binary search.\n         stbtt_int32 l=0, r=rangeCount-1, m;\n         int strawStart, strawEnd, needle=glyph;\n         while (l <= r) {\n            stbtt_uint8 *rangeRecord;\n            m = (l + r) >> 1;\n            rangeRecord = rangeArray + 6 * m;\n            strawStart = ttUSHORT(rangeRecord);\n            strawEnd = ttUSHORT(rangeRecord + 2);\n            if (needle < strawStart)\n               r = m - 1;\n            else if (needle > strawEnd)\n               l = m + 1;\n            else {\n               stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);\n               return startCoverageIndex + glyph - strawStart;\n            }\n         }\n         break;\n      }\n\n      default: return -1; // unsupported\n   }\n\n   return -1;\n}\n\nstatic stbtt_int32  stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)\n{\n   stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);\n   switch (classDefFormat)\n   {\n      case 1: {\n         stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);\n         stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);\n         stbtt_uint8 *classDef1ValueArray = classDefTable + 6;\n\n         if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)\n            return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));\n         break;\n      }\n\n      case 2: {\n         stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);\n         stbtt_uint8 *classRangeRecords = classDefTable + 4;\n\n         // Binary search.\n         stbtt_int32 l=0, r=classRangeCount-1, m;\n         int strawStart, strawEnd, needle=glyph;\n         while (l <= r) {\n            stbtt_uint8 *classRangeRecord;\n            m = (l + r) >> 1;\n            classRangeRecord = classRangeRecords + 6 * m;\n            strawStart = ttUSHORT(classRangeRecord);\n            strawEnd = ttUSHORT(classRangeRecord + 2);\n            if (needle < strawStart)\n               r = m - 1;\n            else if (needle > strawEnd)\n               l = m + 1;\n            else\n               return (stbtt_int32)ttUSHORT(classRangeRecord + 4);\n         }\n         break;\n      }\n\n      default:\n         return -1; // Unsupported definition type, return an error.\n   }\n\n   // \"All glyphs not assigned to a class fall into class 0\". (OpenType spec)\n   return 0;\n}\n\n// Define to STBTT_assert(x) if you want to break on unimplemented formats.\n#define STBTT_GPOS_TODO_assert(x)\n\nstatic stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)\n{\n   stbtt_uint16 lookupListOffset;\n   stbtt_uint8 *lookupList;\n   stbtt_uint16 lookupCount;\n   stbtt_uint8 *data;\n   stbtt_int32 i, sti;\n\n   if (!info->gpos) return 0;\n\n   data = info->data + info->gpos;\n\n   if (ttUSHORT(data+0) != 1) return 0; // Major version 1\n   if (ttUSHORT(data+2) != 0) return 0; // Minor version 0\n\n   lookupListOffset = ttUSHORT(data+8);\n   lookupList = data + lookupListOffset;\n   lookupCount = ttUSHORT(lookupList);\n\n   for (i=0; i<lookupCount; ++i) {\n      stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);\n      stbtt_uint8 *lookupTable = lookupList + lookupOffset;\n\n      stbtt_uint16 lookupType = ttUSHORT(lookupTable);\n      stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);\n      stbtt_uint8 *subTableOffsets = lookupTable + 6;\n      if (lookupType != 2) // Pair Adjustment Positioning Subtable\n         continue;\n\n      for (sti=0; sti<subTableCount; sti++) {\n         stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);\n         stbtt_uint8 *table = lookupTable + subtableOffset;\n         stbtt_uint16 posFormat = ttUSHORT(table);\n         stbtt_uint16 coverageOffset = ttUSHORT(table + 2);\n         stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);\n         if (coverageIndex == -1) continue;\n\n         switch (posFormat) {\n            case 1: {\n               stbtt_int32 l, r, m;\n               int straw, needle;\n               stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);\n               stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);\n               if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?\n                  stbtt_int32 valueRecordPairSizeInBytes = 2;\n                  stbtt_uint16 pairSetCount = ttUSHORT(table + 8);\n                  stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);\n                  stbtt_uint8 *pairValueTable = table + pairPosOffset;\n                  stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);\n                  stbtt_uint8 *pairValueArray = pairValueTable + 2;\n\n                  if (coverageIndex >= pairSetCount) return 0;\n\n                  needle=glyph2;\n                  r=pairValueCount-1;\n                  l=0;\n\n                  // Binary search.\n                  while (l <= r) {\n                     stbtt_uint16 secondGlyph;\n                     stbtt_uint8 *pairValue;\n                     m = (l + r) >> 1;\n                     pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;\n                     secondGlyph = ttUSHORT(pairValue);\n                     straw = secondGlyph;\n                     if (needle < straw)\n                        r = m - 1;\n                     else if (needle > straw)\n                        l = m + 1;\n                     else {\n                        stbtt_int16 xAdvance = ttSHORT(pairValue + 2);\n                        return xAdvance;\n                     }\n                  }\n               } else\n                  return 0;\n               break;\n            }\n\n            case 2: {\n               stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);\n               stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);\n               if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?\n                  stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);\n                  stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);\n                  int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);\n                  int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);\n\n                  stbtt_uint16 class1Count = ttUSHORT(table + 12);\n                  stbtt_uint16 class2Count = ttUSHORT(table + 14);\n                  stbtt_uint8 *class1Records, *class2Records;\n                  stbtt_int16 xAdvance;\n\n                  if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed\n                  if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed\n\n                  class1Records = table + 16;\n                  class2Records = class1Records + 2 * (glyph1class * class2Count);\n                  xAdvance = ttSHORT(class2Records + 2 * glyph2class);\n                  return xAdvance;\n               } else\n                  return 0;\n               break;\n            }\n\n            default:\n               return 0; // Unsupported position format\n         }\n      }\n   }\n\n   return 0;\n}\n\nSTBTT_DEF int  stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)\n{\n   int xAdvance = 0;\n\n   if (info->gpos)\n      xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);\n   else if (info->kern)\n      xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);\n\n   return xAdvance;\n}\n\nSTBTT_DEF int  stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)\n{\n   if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs\n      return 0;\n   return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));\n}\n\nSTBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)\n{\n   stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);\n}\n\nSTBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)\n{\n   if (ascent ) *ascent  = ttSHORT(info->data+info->hhea + 4);\n   if (descent) *descent = ttSHORT(info->data+info->hhea + 6);\n   if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);\n}\n\nSTBTT_DEF int  stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap)\n{\n   int tab = stbtt__find_table(info->data, info->fontstart, \"OS/2\");\n   if (!tab)\n      return 0;\n   if (typoAscent ) *typoAscent  = ttSHORT(info->data+tab + 68);\n   if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70);\n   if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72);\n   return 1;\n}\n\nSTBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)\n{\n   *x0 = ttSHORT(info->data + info->head + 36);\n   *y0 = ttSHORT(info->data + info->head + 38);\n   *x1 = ttSHORT(info->data + info->head + 40);\n   *y1 = ttSHORT(info->data + info->head + 42);\n}\n\nSTBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)\n{\n   int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);\n   return (float) height / fheight;\n}\n\nSTBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)\n{\n   int unitsPerEm = ttUSHORT(info->data + info->head + 18);\n   return pixels / unitsPerEm;\n}\n\nSTBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)\n{\n   STBTT_free(v, info->userdata);\n}\n\nSTBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl)\n{\n   int i;\n   stbtt_uint8 *data = info->data;\n   stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info);\n\n   int numEntries = ttUSHORT(svg_doc_list);\n   stbtt_uint8 *svg_docs = svg_doc_list + 2;\n\n   for(i=0; i<numEntries; i++) {\n      stbtt_uint8 *svg_doc = svg_docs + (12 * i);\n      if ((gl >= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2)))\n         return svg_doc;\n   }\n   return 0;\n}\n\nSTBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg)\n{\n   stbtt_uint8 *data = info->data;\n   stbtt_uint8 *svg_doc;\n\n   if (info->svg == 0)\n      return 0;\n\n   svg_doc = stbtt_FindSVGDoc(info, gl);\n   if (svg_doc != NULL) {\n      *svg = (char *) data + info->svg + ttULONG(svg_doc + 4);\n      return ttULONG(svg_doc + 8);\n   } else {\n      return 0;\n   }\n}\n\nSTBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg)\n{\n   return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// antialiasing software rasterizer\n//\n\nSTBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning\n   if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {\n      // e.g. space character\n      if (ix0) *ix0 = 0;\n      if (iy0) *iy0 = 0;\n      if (ix1) *ix1 = 0;\n      if (iy1) *iy1 = 0;\n   } else {\n      // move to integral bboxes (treating pixels as little squares, what pixels get touched)?\n      if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x);\n      if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y);\n      if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x);\n      if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y);\n   }\n}\n\nSTBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);\n}\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);\n}\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//  Rasterizer\n\ntypedef struct stbtt__hheap_chunk\n{\n   struct stbtt__hheap_chunk *next;\n} stbtt__hheap_chunk;\n\ntypedef struct stbtt__hheap\n{\n   struct stbtt__hheap_chunk *head;\n   void   *first_free;\n   int    num_remaining_in_head_chunk;\n} stbtt__hheap;\n\nstatic void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)\n{\n   if (hh->first_free) {\n      void *p = hh->first_free;\n      hh->first_free = * (void **) p;\n      return p;\n   } else {\n      if (hh->num_remaining_in_head_chunk == 0) {\n         int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);\n         stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata);\n         if (c == NULL)\n            return NULL;\n         c->next = hh->head;\n         hh->head = c;\n         hh->num_remaining_in_head_chunk = count;\n      }\n      --hh->num_remaining_in_head_chunk;\n      return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk;\n   }\n}\n\nstatic void stbtt__hheap_free(stbtt__hheap *hh, void *p)\n{\n   *(void **) p = hh->first_free;\n   hh->first_free = p;\n}\n\nstatic void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)\n{\n   stbtt__hheap_chunk *c = hh->head;\n   while (c) {\n      stbtt__hheap_chunk *n = c->next;\n      STBTT_free(c, userdata);\n      c = n;\n   }\n}\n\ntypedef struct stbtt__edge {\n   float x0,y0, x1,y1;\n   int invert;\n} stbtt__edge;\n\n\ntypedef struct stbtt__active_edge\n{\n   struct stbtt__active_edge *next;\n   #if STBTT_RASTERIZER_VERSION==1\n   int x,dx;\n   float ey;\n   int direction;\n   #elif STBTT_RASTERIZER_VERSION==2\n   float fx,fdx,fdy;\n   float direction;\n   float sy;\n   float ey;\n   #else\n   #error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n   #endif\n} stbtt__active_edge;\n\n#if STBTT_RASTERIZER_VERSION == 1\n#define STBTT_FIXSHIFT   10\n#define STBTT_FIX        (1 << STBTT_FIXSHIFT)\n#define STBTT_FIXMASK    (STBTT_FIX-1)\n\nstatic stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)\n{\n   stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);\n   float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);\n   STBTT_assert(z != NULL);\n   if (!z) return z;\n\n   // round dx down to avoid overshooting\n   if (dxdy < 0)\n      z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);\n   else\n      z->dx = STBTT_ifloor(STBTT_FIX * dxdy);\n\n   z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount\n   z->x -= off_x * STBTT_FIX;\n\n   z->ey = e->y1;\n   z->next = 0;\n   z->direction = e->invert ? 1 : -1;\n   return z;\n}\n#elif STBTT_RASTERIZER_VERSION == 2\nstatic stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)\n{\n   stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);\n   float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);\n   STBTT_assert(z != NULL);\n   //STBTT_assert(e->y0 <= start_point);\n   if (!z) return z;\n   z->fdx = dxdy;\n   z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;\n   z->fx = e->x0 + dxdy * (start_point - e->y0);\n   z->fx -= off_x;\n   z->direction = e->invert ? 1.0f : -1.0f;\n   z->sy = e->y0;\n   z->ey = e->y1;\n   z->next = 0;\n   return z;\n}\n#else\n#error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n#endif\n\n#if STBTT_RASTERIZER_VERSION == 1\n// note: this routine clips fills that extend off the edges... ideally this\n// wouldn't happen, but it could happen if the truetype glyph bounding boxes\n// are wrong, or if the user supplies a too-small bitmap\nstatic void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)\n{\n   // non-zero winding fill\n   int x0=0, w=0;\n\n   while (e) {\n      if (w == 0) {\n         // if we're currently at zero, we need to record the edge start point\n         x0 = e->x; w += e->direction;\n      } else {\n         int x1 = e->x; w += e->direction;\n         // if we went to zero, we need to draw\n         if (w == 0) {\n            int i = x0 >> STBTT_FIXSHIFT;\n            int j = x1 >> STBTT_FIXSHIFT;\n\n            if (i < len && j >= 0) {\n               if (i == j) {\n                  // x0,x1 are the same pixel, so compute combined coverage\n                  scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT);\n               } else {\n                  if (i >= 0) // add antialiasing for x0\n                     scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT);\n                  else\n                     i = -1; // clip\n\n                  if (j < len) // add antialiasing for x1\n                     scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT);\n                  else\n                     j = len; // clip\n\n                  for (++i; i < j; ++i) // fill pixels between x0 and x1\n                     scanline[i] = scanline[i] + (stbtt_uint8) max_weight;\n               }\n            }\n         }\n      }\n\n      e = e->next;\n   }\n}\n\nstatic void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)\n{\n   stbtt__hheap hh = { 0, 0, 0 };\n   stbtt__active_edge *active = NULL;\n   int y,j=0;\n   int max_weight = (255 / vsubsample);  // weight per vertical scanline\n   int s; // vertical subsample index\n   unsigned char scanline_data[512], *scanline;\n\n   if (result->w > 512)\n      scanline = (unsigned char *) STBTT_malloc(result->w, userdata);\n   else\n      scanline = scanline_data;\n\n   y = off_y * vsubsample;\n   e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;\n\n   while (j < result->h) {\n      STBTT_memset(scanline, 0, result->w);\n      for (s=0; s < vsubsample; ++s) {\n         // find center of pixel for this scanline\n         float scan_y = y + 0.5f;\n         stbtt__active_edge **step = &active;\n\n         // update all active edges;\n         // remove all active edges that terminate before the center of this scanline\n         while (*step) {\n            stbtt__active_edge * z = *step;\n            if (z->ey <= scan_y) {\n               *step = z->next; // delete from list\n               STBTT_assert(z->direction);\n               z->direction = 0;\n               stbtt__hheap_free(&hh, z);\n            } else {\n               z->x += z->dx; // advance to position for current scanline\n               step = &((*step)->next); // advance through list\n            }\n         }\n\n         // resort the list if needed\n         for(;;) {\n            int changed=0;\n            step = &active;\n            while (*step && (*step)->next) {\n               if ((*step)->x > (*step)->next->x) {\n                  stbtt__active_edge *t = *step;\n                  stbtt__active_edge *q = t->next;\n\n                  t->next = q->next;\n                  q->next = t;\n                  *step = q;\n                  changed = 1;\n               }\n               step = &(*step)->next;\n            }\n            if (!changed) break;\n         }\n\n         // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline\n         while (e->y0 <= scan_y) {\n            if (e->y1 > scan_y) {\n               stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);\n               if (z != NULL) {\n                  // find insertion point\n                  if (active == NULL)\n                     active = z;\n                  else if (z->x < active->x) {\n                     // insert at front\n                     z->next = active;\n                     active = z;\n                  } else {\n                     // find thing to insert AFTER\n                     stbtt__active_edge *p = active;\n                     while (p->next && p->next->x < z->x)\n                        p = p->next;\n                     // at this point, p->next->x is NOT < z->x\n                     z->next = p->next;\n                     p->next = z;\n                  }\n               }\n            }\n            ++e;\n         }\n\n         // now process all active edges in XOR fashion\n         if (active)\n            stbtt__fill_active_edges(scanline, result->w, active, max_weight);\n\n         ++y;\n      }\n      STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);\n      ++j;\n   }\n\n   stbtt__hheap_cleanup(&hh, userdata);\n\n   if (scanline != scanline_data)\n      STBTT_free(scanline, userdata);\n}\n\n#elif STBTT_RASTERIZER_VERSION == 2\n\n// the edge passed in here does not cross the vertical line at x or the vertical line at x+1\n// (i.e. it has already been clipped to those)\nstatic void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1)\n{\n   if (y0 == y1) return;\n   STBTT_assert(y0 < y1);\n   STBTT_assert(e->sy <= e->ey);\n   if (y0 > e->ey) return;\n   if (y1 < e->sy) return;\n   if (y0 < e->sy) {\n      x0 += (x1-x0) * (e->sy - y0) / (y1-y0);\n      y0 = e->sy;\n   }\n   if (y1 > e->ey) {\n      x1 += (x1-x0) * (e->ey - y1) / (y1-y0);\n      y1 = e->ey;\n   }\n\n   if (x0 == x)\n      STBTT_assert(x1 <= x+1);\n   else if (x0 == x+1)\n      STBTT_assert(x1 >= x);\n   else if (x0 <= x)\n      STBTT_assert(x1 <= x);\n   else if (x0 >= x+1)\n      STBTT_assert(x1 >= x+1);\n   else\n      STBTT_assert(x1 >= x && x1 <= x+1);\n\n   if (x0 <= x && x1 <= x)\n      scanline[x] += e->direction * (y1-y0);\n   else if (x0 >= x+1 && x1 >= x+1)\n      ;\n   else {\n      STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);\n      scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position\n   }\n}\n\nstatic float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)\n{\n   STBTT_assert(top_width >= 0);\n   STBTT_assert(bottom_width >= 0);\n   return (top_width + bottom_width) / 2.0f * height;\n}\n\nstatic float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)\n{\n   return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);\n}\n\nstatic float stbtt__sized_triangle_area(float height, float width)\n{\n   return height * width / 2;\n}\n\nstatic void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)\n{\n   float y_bottom = y_top+1;\n\n   while (e) {\n      // brute force every pixel\n\n      // compute intersection points with top & bottom\n      STBTT_assert(e->ey >= y_top);\n\n      if (e->fdx == 0) {\n         float x0 = e->fx;\n         if (x0 < len) {\n            if (x0 >= 0) {\n               stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);\n               stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);\n            } else {\n               stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);\n            }\n         }\n      } else {\n         float x0 = e->fx;\n         float dx = e->fdx;\n         float xb = x0 + dx;\n         float x_top, x_bottom;\n         float sy0,sy1;\n         float dy = e->fdy;\n         STBTT_assert(e->sy <= y_bottom && e->ey >= y_top);\n\n         // compute endpoints of line segment clipped to this scanline (if the\n         // line segment starts on this scanline. x0 is the intersection of the\n         // line with y_top, but that may be off the line segment.\n         if (e->sy > y_top) {\n            x_top = x0 + dx * (e->sy - y_top);\n            sy0 = e->sy;\n         } else {\n            x_top = x0;\n            sy0 = y_top;\n         }\n         if (e->ey < y_bottom) {\n            x_bottom = x0 + dx * (e->ey - y_top);\n            sy1 = e->ey;\n         } else {\n            x_bottom = xb;\n            sy1 = y_bottom;\n         }\n\n         if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {\n            // from here on, we don't have to range check x values\n\n            if ((int) x_top == (int) x_bottom) {\n               float height;\n               // simple case, only spans one pixel\n               int x = (int) x_top;\n               height = (sy1 - sy0) * e->direction;\n               STBTT_assert(x >= 0 && x < len);\n               scanline[x]      += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);\n               scanline_fill[x] += height; // everything right of this pixel is filled\n            } else {\n               int x,x1,x2;\n               float y_crossing, y_final, step, sign, area;\n               // covers 2+ pixels\n               if (x_top > x_bottom) {\n                  // flip scanline vertically; signed area is the same\n                  float t;\n                  sy0 = y_bottom - (sy0 - y_top);\n                  sy1 = y_bottom - (sy1 - y_top);\n                  t = sy0, sy0 = sy1, sy1 = t;\n                  t = x_bottom, x_bottom = x_top, x_top = t;\n                  dx = -dx;\n                  dy = -dy;\n                  t = x0, x0 = xb, xb = t;\n               }\n               STBTT_assert(dy >= 0);\n               STBTT_assert(dx >= 0);\n\n               x1 = (int) x_top;\n               x2 = (int) x_bottom;\n               // compute intersection with y axis at x1+1\n               y_crossing = y_top + dy * (x1+1 - x0);\n\n               // compute intersection with y axis at x2\n               y_final = y_top + dy * (x2 - x0);\n\n               //           x1    x_top                            x2    x_bottom\n               //     y_top  +------|-----+------------+------------+--------|---+------------+\n               //            |            |            |            |            |            |\n               //            |            |            |            |            |            |\n               //       sy0  |      Txxxxx|............|............|............|............|\n               // y_crossing |            *xxxxx.......|............|............|............|\n               //            |            |     xxxxx..|............|............|............|\n               //            |            |     /-   xx*xxxx........|............|............|\n               //            |            | dy <       |    xxxxxx..|............|............|\n               //   y_final  |            |     \\-     |          xx*xxx.........|............|\n               //       sy1  |            |            |            |   xxxxxB...|............|\n               //            |            |            |            |            |            |\n               //            |            |            |            |            |            |\n               //  y_bottom  +------------+------------+------------+------------+------------+\n               //\n               // goal is to measure the area covered by '.' in each pixel\n\n               // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057\n               // @TODO: maybe test against sy1 rather than y_bottom?\n               if (y_crossing > y_bottom)\n                  y_crossing = y_bottom;\n\n               sign = e->direction;\n\n               // area of the rectangle covered from sy0..y_crossing\n               area = sign * (y_crossing-sy0);\n\n               // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)\n               scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);\n\n               // check if final y_crossing is blown up; no test case for this\n               if (y_final > y_bottom) {\n                  int denom = (x2 - (x1+1));\n                  y_final = y_bottom;\n                  if (denom != 0) { // [DEAR IMGUI] Avoid div by zero (https://github.com/nothings/stb/issues/1316)\n                     dy = (y_final - y_crossing ) / denom; // if denom=0, y_final = y_crossing, so y_final <= y_bottom\n                  }\n               }\n\n               // in second pixel, area covered by line segment found in first pixel\n               // is always a rectangle 1 wide * the height of that line segment; this\n               // is exactly what the variable 'area' stores. it also gets a contribution\n               // from the line segment within it. the THIRD pixel will get the first\n               // pixel's rectangle contribution, the second pixel's rectangle contribution,\n               // and its own contribution. the 'own contribution' is the same in every pixel except\n               // the leftmost and rightmost, a trapezoid that slides down in each pixel.\n               // the second pixel's contribution to the third pixel will be the\n               // rectangle 1 wide times the height change in the second pixel, which is dy.\n\n               step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,\n               // which multiplied by 1-pixel-width is how much pixel area changes for each step in x\n               // so the area advances by 'step' every time\n\n               for (x = x1+1; x < x2; ++x) {\n                  scanline[x] += area + step/2; // area of trapezoid is 1*step/2\n                  area += step;\n               }\n               STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down\n               STBTT_assert(sy1 > y_final-0.01f);\n\n               // area covered in the last pixel is the rectangle from all the pixels to the left,\n               // plus the trapezoid filled by the line segment in this pixel all the way to the right edge\n               scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);\n\n               // the rest of the line is filled based on the total height of the line segment in this pixel\n               scanline_fill[x2] += sign * (sy1-sy0);\n            }\n         } else {\n            // if edge goes outside of box we're drawing, we require\n            // clipping logic. since this does not match the intended use\n            // of this library, we use a different, very slow brute\n            // force implementation\n            // note though that this does happen some of the time because\n            // x_top and x_bottom can be extrapolated at the top & bottom of\n            // the shape and actually lie outside the bounding box\n            int x;\n            for (x=0; x < len; ++x) {\n               // cases:\n               //\n               // there can be up to two intersections with the pixel. any intersection\n               // with left or right edges can be handled by splitting into two (or three)\n               // regions. intersections with top & bottom do not necessitate case-wise logic.\n               //\n               // the old way of doing this found the intersections with the left & right edges,\n               // then used some simple logic to produce up to three segments in sorted order\n               // from top-to-bottom. however, this had a problem: if an x edge was epsilon\n               // across the x border, then the corresponding y position might not be distinct\n               // from the other y segment, and it might ignored as an empty segment. to avoid\n               // that, we need to explicitly produce segments based on x positions.\n\n               // rename variables to clearly-defined pairs\n               float y0 = y_top;\n               float x1 = (float) (x);\n               float x2 = (float) (x+1);\n               float x3 = xb;\n               float y3 = y_bottom;\n\n               // x = e->x + e->dx * (y-y_top)\n               // (y-y_top) = (x - e->x) / e->dx\n               // y = (x - e->x) / e->dx + y_top\n               float y1 = (x - x0) / dx + y_top;\n               float y2 = (x+1 - x0) / dx + y_top;\n\n               if (x0 < x1 && x3 > x2) {         // three segments descending down-right\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);\n               } else if (x3 < x1 && x0 > x2) {  // three segments descending down-left\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);\n               } else if (x0 < x1 && x3 > x1) {  // two segments across x, down-right\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);\n               } else if (x3 < x1 && x0 > x1) {  // two segments across x, down-left\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);\n               } else if (x0 < x2 && x3 > x2) {  // two segments across x+1, down-right\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);\n               } else if (x3 < x2 && x0 > x2) {  // two segments across x+1, down-left\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);\n               } else {  // one segment\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);\n               }\n            }\n         }\n      }\n      e = e->next;\n   }\n}\n\n// directly AA rasterize edges w/o supersampling\nstatic void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)\n{\n   stbtt__hheap hh = { 0, 0, 0 };\n   stbtt__active_edge *active = NULL;\n   int y,j=0, i;\n   float scanline_data[129], *scanline, *scanline2;\n\n   STBTT__NOTUSED(vsubsample);\n\n   if (result->w > 64)\n      scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);\n   else\n      scanline = scanline_data;\n\n   scanline2 = scanline + result->w;\n\n   y = off_y;\n   e[n].y0 = (float) (off_y + result->h) + 1;\n\n   while (j < result->h) {\n      // find center of pixel for this scanline\n      float scan_y_top    = y + 0.0f;\n      float scan_y_bottom = y + 1.0f;\n      stbtt__active_edge **step = &active;\n\n      STBTT_memset(scanline , 0, result->w*sizeof(scanline[0]));\n      STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0]));\n\n      // update all active edges;\n      // remove all active edges that terminate before the top of this scanline\n      while (*step) {\n         stbtt__active_edge * z = *step;\n         if (z->ey <= scan_y_top) {\n            *step = z->next; // delete from list\n            STBTT_assert(z->direction);\n            z->direction = 0;\n            stbtt__hheap_free(&hh, z);\n         } else {\n            step = &((*step)->next); // advance through list\n         }\n      }\n\n      // insert all edges that start before the bottom of this scanline\n      while (e->y0 <= scan_y_bottom) {\n         if (e->y0 != e->y1) {\n            stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);\n            if (z != NULL) {\n               if (j == 0 && off_y != 0) {\n                  if (z->ey < scan_y_top) {\n                     // this can happen due to subpixel positioning and some kind of fp rounding error i think\n                     z->ey = scan_y_top;\n                  }\n               }\n               STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds\n               // insert at front\n               z->next = active;\n               active = z;\n            }\n         }\n         ++e;\n      }\n\n      // now process all active edges\n      if (active)\n         stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);\n\n      {\n         float sum = 0;\n         for (i=0; i < result->w; ++i) {\n            float k;\n            int m;\n            sum += scanline2[i];\n            k = scanline[i] + sum;\n            k = (float) STBTT_fabs(k)*255 + 0.5f;\n            m = (int) k;\n            if (m > 255) m = 255;\n            result->pixels[j*result->stride + i] = (unsigned char) m;\n         }\n      }\n      // advance all the edges\n      step = &active;\n      while (*step) {\n         stbtt__active_edge *z = *step;\n         z->fx += z->fdx; // advance to position for current scanline\n         step = &((*step)->next); // advance through list\n      }\n\n      ++y;\n      ++j;\n   }\n\n   stbtt__hheap_cleanup(&hh, userdata);\n\n   if (scanline != scanline_data)\n      STBTT_free(scanline, userdata);\n}\n#else\n#error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n#endif\n\n#define STBTT__COMPARE(a,b)  ((a)->y0 < (b)->y0)\n\nstatic void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)\n{\n   int i,j;\n   for (i=1; i < n; ++i) {\n      stbtt__edge t = p[i], *a = &t;\n      j = i;\n      while (j > 0) {\n         stbtt__edge *b = &p[j-1];\n         int c = STBTT__COMPARE(a,b);\n         if (!c) break;\n         p[j] = p[j-1];\n         --j;\n      }\n      if (i != j)\n         p[j] = t;\n   }\n}\n\nstatic void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)\n{\n   /* threshold for transitioning to insertion sort */\n   while (n > 12) {\n      stbtt__edge t;\n      int c01,c12,c,m,i,j;\n\n      /* compute median of three */\n      m = n >> 1;\n      c01 = STBTT__COMPARE(&p[0],&p[m]);\n      c12 = STBTT__COMPARE(&p[m],&p[n-1]);\n      /* if 0 >= mid >= end, or 0 < mid < end, then use mid */\n      if (c01 != c12) {\n         /* otherwise, we'll need to swap something else to middle */\n         int z;\n         c = STBTT__COMPARE(&p[0],&p[n-1]);\n         /* 0>mid && mid<n:  0>n => n; 0<n => 0 */\n         /* 0<mid && mid>n:  0>n => 0; 0<n => n */\n         z = (c == c12) ? 0 : n-1;\n         t = p[z];\n         p[z] = p[m];\n         p[m] = t;\n      }\n      /* now p[m] is the median-of-three */\n      /* swap it to the beginning so it won't move around */\n      t = p[0];\n      p[0] = p[m];\n      p[m] = t;\n\n      /* partition loop */\n      i=1;\n      j=n-1;\n      for(;;) {\n         /* handling of equality is crucial here */\n         /* for sentinels & efficiency with duplicates */\n         for (;;++i) {\n            if (!STBTT__COMPARE(&p[i], &p[0])) break;\n         }\n         for (;;--j) {\n            if (!STBTT__COMPARE(&p[0], &p[j])) break;\n         }\n         /* make sure we haven't crossed */\n         if (i >= j) break;\n         t = p[i];\n         p[i] = p[j];\n         p[j] = t;\n\n         ++i;\n         --j;\n      }\n      /* recurse on smaller side, iterate on larger */\n      if (j < (n-i)) {\n         stbtt__sort_edges_quicksort(p,j);\n         p = p+i;\n         n = n-i;\n      } else {\n         stbtt__sort_edges_quicksort(p+i, n-i);\n         n = j;\n      }\n   }\n}\n\nstatic void stbtt__sort_edges(stbtt__edge *p, int n)\n{\n   stbtt__sort_edges_quicksort(p, n);\n   stbtt__sort_edges_ins_sort(p, n);\n}\n\ntypedef struct\n{\n   float x,y;\n} stbtt__point;\n\nstatic void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata)\n{\n   float y_scale_inv = invert ? -scale_y : scale_y;\n   stbtt__edge *e;\n   int n,i,j,k,m;\n#if STBTT_RASTERIZER_VERSION == 1\n   int vsubsample = result->h < 8 ? 15 : 5;\n#elif STBTT_RASTERIZER_VERSION == 2\n   int vsubsample = 1;\n#else\n   #error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n#endif\n   // vsubsample should divide 255 evenly; otherwise we won't reach full opacity\n\n   // now we have to blow out the windings into explicit edge lists\n   n = 0;\n   for (i=0; i < windings; ++i)\n      n += wcount[i];\n\n   e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel\n   if (e == 0) return;\n   n = 0;\n\n   m=0;\n   for (i=0; i < windings; ++i) {\n      stbtt__point *p = pts + m;\n      m += wcount[i];\n      j = wcount[i]-1;\n      for (k=0; k < wcount[i]; j=k++) {\n         int a=k,b=j;\n         // skip the edge if horizontal\n         if (p[j].y == p[k].y)\n            continue;\n         // add edge from j to k to the list\n         e[n].invert = 0;\n         if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {\n            e[n].invert = 1;\n            a=j,b=k;\n         }\n         e[n].x0 = p[a].x * scale_x + shift_x;\n         e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample;\n         e[n].x1 = p[b].x * scale_x + shift_x;\n         e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample;\n         ++n;\n      }\n   }\n\n   // now sort the edges by their highest point (should snap to integer, and then by x)\n   //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);\n   stbtt__sort_edges(e, n);\n\n   // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule\n   stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);\n\n   STBTT_free(e, userdata);\n}\n\nstatic void stbtt__add_point(stbtt__point *points, int n, float x, float y)\n{\n   if (!points) return; // during first pass, it's unallocated\n   points[n].x = x;\n   points[n].y = y;\n}\n\n// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching\nstatic int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)\n{\n   // midpoint\n   float mx = (x0 + 2*x1 + x2)/4;\n   float my = (y0 + 2*y1 + y2)/4;\n   // versus directly drawn line\n   float dx = (x0+x2)/2 - mx;\n   float dy = (y0+y2)/2 - my;\n   if (n > 16) // 65536 segments on one curve better be enough!\n      return 1;\n   if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA\n      stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);\n      stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);\n   } else {\n      stbtt__add_point(points, *num_points,x2,y2);\n      *num_points = *num_points+1;\n   }\n   return 1;\n}\n\nstatic void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n)\n{\n   // @TODO this \"flatness\" calculation is just made-up nonsense that seems to work well enough\n   float dx0 = x1-x0;\n   float dy0 = y1-y0;\n   float dx1 = x2-x1;\n   float dy1 = y2-y1;\n   float dx2 = x3-x2;\n   float dy2 = y3-y2;\n   float dx = x3-x0;\n   float dy = y3-y0;\n   float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2));\n   float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy);\n   float flatness_squared = longlen*longlen-shortlen*shortlen;\n\n   if (n > 16) // 65536 segments on one curve better be enough!\n      return;\n\n   if (flatness_squared > objspace_flatness_squared) {\n      float x01 = (x0+x1)/2;\n      float y01 = (y0+y1)/2;\n      float x12 = (x1+x2)/2;\n      float y12 = (y1+y2)/2;\n      float x23 = (x2+x3)/2;\n      float y23 = (y2+y3)/2;\n\n      float xa = (x01+x12)/2;\n      float ya = (y01+y12)/2;\n      float xb = (x12+x23)/2;\n      float yb = (y12+y23)/2;\n\n      float mx = (xa+xb)/2;\n      float my = (ya+yb)/2;\n\n      stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1);\n      stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1);\n   } else {\n      stbtt__add_point(points, *num_points,x3,y3);\n      *num_points = *num_points+1;\n   }\n}\n\n// returns number of contours\nstatic stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)\n{\n   stbtt__point *points=0;\n   int num_points=0;\n\n   float objspace_flatness_squared = objspace_flatness * objspace_flatness;\n   int i,n=0,start=0, pass;\n\n   // count how many \"moves\" there are to get the contour count\n   for (i=0; i < num_verts; ++i)\n      if (vertices[i].type == STBTT_vmove)\n         ++n;\n\n   *num_contours = n;\n   if (n == 0) return 0;\n\n   *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);\n\n   if (*contour_lengths == 0) {\n      *num_contours = 0;\n      return 0;\n   }\n\n   // make two passes through the points so we don't need to realloc\n   for (pass=0; pass < 2; ++pass) {\n      float x=0,y=0;\n      if (pass == 1) {\n         points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);\n         if (points == NULL) goto error;\n      }\n      num_points = 0;\n      n= -1;\n      for (i=0; i < num_verts; ++i) {\n         switch (vertices[i].type) {\n            case STBTT_vmove:\n               // start the next contour\n               if (n >= 0)\n                  (*contour_lengths)[n] = num_points - start;\n               ++n;\n               start = num_points;\n\n               x = vertices[i].x, y = vertices[i].y;\n               stbtt__add_point(points, num_points++, x,y);\n               break;\n            case STBTT_vline:\n               x = vertices[i].x, y = vertices[i].y;\n               stbtt__add_point(points, num_points++, x, y);\n               break;\n            case STBTT_vcurve:\n               stbtt__tesselate_curve(points, &num_points, x,y,\n                                        vertices[i].cx, vertices[i].cy,\n                                        vertices[i].x,  vertices[i].y,\n                                        objspace_flatness_squared, 0);\n               x = vertices[i].x, y = vertices[i].y;\n               break;\n            case STBTT_vcubic:\n               stbtt__tesselate_cubic(points, &num_points, x,y,\n                                        vertices[i].cx, vertices[i].cy,\n                                        vertices[i].cx1, vertices[i].cy1,\n                                        vertices[i].x,  vertices[i].y,\n                                        objspace_flatness_squared, 0);\n               x = vertices[i].x, y = vertices[i].y;\n               break;\n         }\n      }\n      (*contour_lengths)[n] = num_points - start;\n   }\n\n   return points;\nerror:\n   STBTT_free(points, userdata);\n   STBTT_free(*contour_lengths, userdata);\n   *contour_lengths = 0;\n   *num_contours = 0;\n   return NULL;\n}\n\nSTBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)\n{\n   float scale            = scale_x > scale_y ? scale_y : scale_x;\n   int winding_count      = 0;\n   int *winding_lengths   = NULL;\n   stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);\n   if (windings) {\n      stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);\n      STBTT_free(winding_lengths, userdata);\n      STBTT_free(windings, userdata);\n   }\n}\n\nSTBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)\n{\n   STBTT_free(bitmap, userdata);\n}\n\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)\n{\n   int ix0,iy0,ix1,iy1;\n   stbtt__bitmap gbm;\n   stbtt_vertex *vertices;\n   int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);\n\n   if (scale_x == 0) scale_x = scale_y;\n   if (scale_y == 0) {\n      if (scale_x == 0) {\n         STBTT_free(vertices, info->userdata);\n         return NULL;\n      }\n      scale_y = scale_x;\n   }\n\n   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);\n\n   // now we get the size\n   gbm.w = (ix1 - ix0);\n   gbm.h = (iy1 - iy0);\n   gbm.pixels = NULL; // in case we error\n\n   if (width ) *width  = gbm.w;\n   if (height) *height = gbm.h;\n   if (xoff  ) *xoff   = ix0;\n   if (yoff  ) *yoff   = iy0;\n\n   if (gbm.w && gbm.h) {\n      gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);\n      if (gbm.pixels) {\n         gbm.stride = gbm.w;\n\n         stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata);\n      }\n   }\n   STBTT_free(vertices, info->userdata);\n   return gbm.pixels;\n}\n\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);\n}\n\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)\n{\n   int ix0,iy0;\n   stbtt_vertex *vertices;\n   int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);\n   stbtt__bitmap gbm;\n\n   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);\n   gbm.pixels = output;\n   gbm.w = out_w;\n   gbm.h = out_h;\n   gbm.stride = out_stride;\n\n   if (gbm.w && gbm.h)\n      stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata);\n\n   STBTT_free(vertices, info->userdata);\n}\n\nSTBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)\n{\n   stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);\n}\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);\n}\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint)\n{\n   stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint));\n}\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)\n{\n   stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));\n}\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);\n}\n\nSTBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)\n{\n   stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// bitmap baking\n//\n// This is SUPER-CRAPPY packing to keep source code small\n\nstatic int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset,  // font location (use offset=0 for plain .ttf)\n                                float pixel_height,                     // height of font in pixels\n                                unsigned char *pixels, int pw, int ph,  // bitmap to be filled in\n                                int first_char, int num_chars,          // characters to bake\n                                stbtt_bakedchar *chardata)\n{\n   float scale;\n   int x,y,bottom_y, i;\n   stbtt_fontinfo f;\n   f.userdata = NULL;\n   if (!stbtt_InitFont(&f, data, offset))\n      return -1;\n   STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels\n   x=y=1;\n   bottom_y = 1;\n\n   scale = stbtt_ScaleForPixelHeight(&f, pixel_height);\n\n   for (i=0; i < num_chars; ++i) {\n      int advance, lsb, x0,y0,x1,y1,gw,gh;\n      int g = stbtt_FindGlyphIndex(&f, first_char + i);\n      stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);\n      stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);\n      gw = x1-x0;\n      gh = y1-y0;\n      if (x + gw + 1 >= pw)\n         y = bottom_y, x = 1; // advance to next row\n      if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row\n         return -i;\n      STBTT_assert(x+gw < pw);\n      STBTT_assert(y+gh < ph);\n      stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);\n      chardata[i].x0 = (stbtt_int16) x;\n      chardata[i].y0 = (stbtt_int16) y;\n      chardata[i].x1 = (stbtt_int16) (x + gw);\n      chardata[i].y1 = (stbtt_int16) (y + gh);\n      chardata[i].xadvance = scale * advance;\n      chardata[i].xoff     = (float) x0;\n      chardata[i].yoff     = (float) y0;\n      x = x + gw + 1;\n      if (y+gh+1 > bottom_y)\n         bottom_y = y+gh+1;\n   }\n   return bottom_y;\n}\n\nSTBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)\n{\n   float d3d_bias = opengl_fillrule ? 0 : -0.5f;\n   float ipw = 1.0f / pw, iph = 1.0f / ph;\n   const stbtt_bakedchar *b = chardata + char_index;\n   int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);\n   int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);\n\n   q->x0 = round_x + d3d_bias;\n   q->y0 = round_y + d3d_bias;\n   q->x1 = round_x + b->x1 - b->x0 + d3d_bias;\n   q->y1 = round_y + b->y1 - b->y0 + d3d_bias;\n\n   q->s0 = b->x0 * ipw;\n   q->t0 = b->y0 * iph;\n   q->s1 = b->x1 * ipw;\n   q->t1 = b->y1 * iph;\n\n   *xpos += b->xadvance;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// rectangle packing replacement routines if you don't have stb_rect_pack.h\n//\n\n#ifndef STB_RECT_PACK_VERSION\n\ntypedef int stbrp_coord;\n\n////////////////////////////////////////////////////////////////////////////////////\n//                                                                                //\n//                                                                                //\n// COMPILER WARNING ?!?!?                                                         //\n//                                                                                //\n//                                                                                //\n// if you get a compile warning due to these symbols being defined more than      //\n// once, move #include \"stb_rect_pack.h\" before #include \"stb_truetype.h\"         //\n//                                                                                //\n////////////////////////////////////////////////////////////////////////////////////\n\ntypedef struct\n{\n   int width,height;\n   int x,y,bottom_y;\n} stbrp_context;\n\ntypedef struct\n{\n   unsigned char x;\n} stbrp_node;\n\nstruct stbrp_rect\n{\n   stbrp_coord x,y;\n   int id,w,h,was_packed;\n};\n\nstatic void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)\n{\n   con->width  = pw;\n   con->height = ph;\n   con->x = 0;\n   con->y = 0;\n   con->bottom_y = 0;\n   STBTT__NOTUSED(nodes);\n   STBTT__NOTUSED(num_nodes);\n}\n\nstatic void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)\n{\n   int i;\n   for (i=0; i < num_rects; ++i) {\n      if (con->x + rects[i].w > con->width) {\n         con->x = 0;\n         con->y = con->bottom_y;\n      }\n      if (con->y + rects[i].h > con->height)\n         break;\n      rects[i].x = con->x;\n      rects[i].y = con->y;\n      rects[i].was_packed = 1;\n      con->x += rects[i].w;\n      if (con->y + rects[i].h > con->bottom_y)\n         con->bottom_y = con->y + rects[i].h;\n   }\n   for (   ; i < num_rects; ++i)\n      rects[i].was_packed = 0;\n}\n#endif\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// bitmap baking\n//\n// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If\n// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.\n\nSTBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)\n{\n   stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context)            ,alloc_context);\n   int            num_nodes = pw - padding;\n   stbrp_node    *nodes   = (stbrp_node    *) STBTT_malloc(sizeof(*nodes  ) * num_nodes,alloc_context);\n\n   if (context == NULL || nodes == NULL) {\n      if (context != NULL) STBTT_free(context, alloc_context);\n      if (nodes   != NULL) STBTT_free(nodes  , alloc_context);\n      return 0;\n   }\n\n   spc->user_allocator_context = alloc_context;\n   spc->width = pw;\n   spc->height = ph;\n   spc->pixels = pixels;\n   spc->pack_info = context;\n   spc->nodes = nodes;\n   spc->padding = padding;\n   spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;\n   spc->h_oversample = 1;\n   spc->v_oversample = 1;\n   spc->skip_missing = 0;\n\n   stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);\n\n   if (pixels)\n      STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels\n\n   return 1;\n}\n\nSTBTT_DEF void stbtt_PackEnd  (stbtt_pack_context *spc)\n{\n   STBTT_free(spc->nodes    , spc->user_allocator_context);\n   STBTT_free(spc->pack_info, spc->user_allocator_context);\n}\n\nSTBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)\n{\n   STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);\n   STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);\n   if (h_oversample <= STBTT_MAX_OVERSAMPLE)\n      spc->h_oversample = h_oversample;\n   if (v_oversample <= STBTT_MAX_OVERSAMPLE)\n      spc->v_oversample = v_oversample;\n}\n\nSTBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip)\n{\n   spc->skip_missing = skip;\n}\n\n#define STBTT__OVER_MASK  (STBTT_MAX_OVERSAMPLE-1)\n\nstatic void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)\n{\n   unsigned char buffer[STBTT_MAX_OVERSAMPLE];\n   int safe_w = w - kernel_width;\n   int j;\n   STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze\n   for (j=0; j < h; ++j) {\n      int i;\n      unsigned int total;\n      STBTT_memset(buffer, 0, kernel_width);\n\n      total = 0;\n\n      // make kernel_width a constant in common cases so compiler can optimize out the divide\n      switch (kernel_width) {\n         case 2:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 2);\n            }\n            break;\n         case 3:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 3);\n            }\n            break;\n         case 4:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 4);\n            }\n            break;\n         case 5:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 5);\n            }\n            break;\n         default:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / kernel_width);\n            }\n            break;\n      }\n\n      for (; i < w; ++i) {\n         STBTT_assert(pixels[i] == 0);\n         total -= buffer[i & STBTT__OVER_MASK];\n         pixels[i] = (unsigned char) (total / kernel_width);\n      }\n\n      pixels += stride_in_bytes;\n   }\n}\n\nstatic void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)\n{\n   unsigned char buffer[STBTT_MAX_OVERSAMPLE];\n   int safe_h = h - kernel_width;\n   int j;\n   STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze\n   for (j=0; j < w; ++j) {\n      int i;\n      unsigned int total;\n      STBTT_memset(buffer, 0, kernel_width);\n\n      total = 0;\n\n      // make kernel_width a constant in common cases so compiler can optimize out the divide\n      switch (kernel_width) {\n         case 2:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 2);\n            }\n            break;\n         case 3:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 3);\n            }\n            break;\n         case 4:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 4);\n            }\n            break;\n         case 5:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 5);\n            }\n            break;\n         default:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);\n            }\n            break;\n      }\n\n      for (; i < h; ++i) {\n         STBTT_assert(pixels[i*stride_in_bytes] == 0);\n         total -= buffer[i & STBTT__OVER_MASK];\n         pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);\n      }\n\n      pixels += 1;\n   }\n}\n\nstatic float stbtt__oversample_shift(int oversample)\n{\n   if (!oversample)\n      return 0.0f;\n\n   // The prefilter is a box filter of width \"oversample\",\n   // which shifts phase by (oversample - 1)/2 pixels in\n   // oversampled space. We want to shift in the opposite\n   // direction to counter this.\n   return (float)-(oversample - 1) / (2.0f * (float)oversample);\n}\n\n// rects array must be big enough to accommodate all characters in the given ranges\nSTBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)\n{\n   int i,j,k;\n   int missing_glyph_added = 0;\n\n   k=0;\n   for (i=0; i < num_ranges; ++i) {\n      float fh = ranges[i].font_size;\n      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);\n      ranges[i].h_oversample = (unsigned char) spc->h_oversample;\n      ranges[i].v_oversample = (unsigned char) spc->v_oversample;\n      for (j=0; j < ranges[i].num_chars; ++j) {\n         int x0,y0,x1,y1;\n         int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];\n         int glyph = stbtt_FindGlyphIndex(info, codepoint);\n         if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) {\n            rects[k].w = rects[k].h = 0;\n         } else {\n            stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,\n                                            scale * spc->h_oversample,\n                                            scale * spc->v_oversample,\n                                            0,0,\n                                            &x0,&y0,&x1,&y1);\n            rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);\n            rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);\n            if (glyph == 0)\n               missing_glyph_added = 1;\n         }\n         ++k;\n      }\n   }\n\n   return k;\n}\n\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph)\n{\n   stbtt_MakeGlyphBitmapSubpixel(info,\n                                 output,\n                                 out_w - (prefilter_x - 1),\n                                 out_h - (prefilter_y - 1),\n                                 out_stride,\n                                 scale_x,\n                                 scale_y,\n                                 shift_x,\n                                 shift_y,\n                                 glyph);\n\n   if (prefilter_x > 1)\n      stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x);\n\n   if (prefilter_y > 1)\n      stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y);\n\n   *sub_x = stbtt__oversample_shift(prefilter_x);\n   *sub_y = stbtt__oversample_shift(prefilter_y);\n}\n\n// rects array must be big enough to accommodate all characters in the given ranges\nSTBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)\n{\n   int i,j,k, missing_glyph = -1, return_value = 1;\n\n   // save current values\n   int old_h_over = spc->h_oversample;\n   int old_v_over = spc->v_oversample;\n\n   k = 0;\n   for (i=0; i < num_ranges; ++i) {\n      float fh = ranges[i].font_size;\n      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);\n      float recip_h,recip_v,sub_x,sub_y;\n      spc->h_oversample = ranges[i].h_oversample;\n      spc->v_oversample = ranges[i].v_oversample;\n      recip_h = 1.0f / spc->h_oversample;\n      recip_v = 1.0f / spc->v_oversample;\n      sub_x = stbtt__oversample_shift(spc->h_oversample);\n      sub_y = stbtt__oversample_shift(spc->v_oversample);\n      for (j=0; j < ranges[i].num_chars; ++j) {\n         stbrp_rect *r = &rects[k];\n         if (r->was_packed && r->w != 0 && r->h != 0) {\n            stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];\n            int advance, lsb, x0,y0,x1,y1;\n            int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];\n            int glyph = stbtt_FindGlyphIndex(info, codepoint);\n            stbrp_coord pad = (stbrp_coord) spc->padding;\n\n            // pad on left and top\n            r->x += pad;\n            r->y += pad;\n            r->w -= pad;\n            r->h -= pad;\n            stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);\n            stbtt_GetGlyphBitmapBox(info, glyph,\n                                    scale * spc->h_oversample,\n                                    scale * spc->v_oversample,\n                                    &x0,&y0,&x1,&y1);\n            stbtt_MakeGlyphBitmapSubpixel(info,\n                                          spc->pixels + r->x + r->y*spc->stride_in_bytes,\n                                          r->w - spc->h_oversample+1,\n                                          r->h - spc->v_oversample+1,\n                                          spc->stride_in_bytes,\n                                          scale * spc->h_oversample,\n                                          scale * spc->v_oversample,\n                                          0,0,\n                                          glyph);\n\n            if (spc->h_oversample > 1)\n               stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,\n                                  r->w, r->h, spc->stride_in_bytes,\n                                  spc->h_oversample);\n\n            if (spc->v_oversample > 1)\n               stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,\n                                  r->w, r->h, spc->stride_in_bytes,\n                                  spc->v_oversample);\n\n            bc->x0       = (stbtt_int16)  r->x;\n            bc->y0       = (stbtt_int16)  r->y;\n            bc->x1       = (stbtt_int16) (r->x + r->w);\n            bc->y1       = (stbtt_int16) (r->y + r->h);\n            bc->xadvance =                scale * advance;\n            bc->xoff     =       (float)  x0 * recip_h + sub_x;\n            bc->yoff     =       (float)  y0 * recip_v + sub_y;\n            bc->xoff2    =                (x0 + r->w) * recip_h + sub_x;\n            bc->yoff2    =                (y0 + r->h) * recip_v + sub_y;\n\n            if (glyph == 0)\n               missing_glyph = j;\n         } else if (spc->skip_missing) {\n            return_value = 0;\n         } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) {\n            ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph];\n         } else {\n            return_value = 0; // if any fail, report failure\n         }\n\n         ++k;\n      }\n   }\n\n   // restore original values\n   spc->h_oversample = old_h_over;\n   spc->v_oversample = old_v_over;\n\n   return return_value;\n}\n\nSTBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects)\n{\n   stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);\n}\n\nSTBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)\n{\n   stbtt_fontinfo info;\n   int i, j, n, return_value; // [DEAR IMGUI] removed = 1;\n   //stbrp_context *context = (stbrp_context *) spc->pack_info;\n   stbrp_rect    *rects;\n\n   // flag all characters as NOT packed\n   for (i=0; i < num_ranges; ++i)\n      for (j=0; j < ranges[i].num_chars; ++j)\n         ranges[i].chardata_for_range[j].x0 =\n         ranges[i].chardata_for_range[j].y0 =\n         ranges[i].chardata_for_range[j].x1 =\n         ranges[i].chardata_for_range[j].y1 = 0;\n\n   n = 0;\n   for (i=0; i < num_ranges; ++i)\n      n += ranges[i].num_chars;\n\n   rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);\n   if (rects == NULL)\n      return 0;\n\n   info.userdata = spc->user_allocator_context;\n   stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));\n\n   n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);\n\n   stbtt_PackFontRangesPackRects(spc, rects, n);\n\n   return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);\n\n   STBTT_free(rects, spc->user_allocator_context);\n   return return_value;\n}\n\nSTBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,\n            int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)\n{\n   stbtt_pack_range range;\n   range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range;\n   range.array_of_unicode_codepoints = NULL;\n   range.num_chars                   = num_chars_in_range;\n   range.chardata_for_range          = chardata_for_range;\n   range.font_size                   = font_size;\n   return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);\n}\n\nSTBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap)\n{\n   int i_ascent, i_descent, i_lineGap;\n   float scale;\n   stbtt_fontinfo info;\n   stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index));\n   scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size);\n   stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap);\n   *ascent  = (float) i_ascent  * scale;\n   *descent = (float) i_descent * scale;\n   *lineGap = (float) i_lineGap * scale;\n}\n\nSTBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)\n{\n   float ipw = 1.0f / pw, iph = 1.0f / ph;\n   const stbtt_packedchar *b = chardata + char_index;\n\n   if (align_to_integer) {\n      float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);\n      float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);\n      q->x0 = x;\n      q->y0 = y;\n      q->x1 = x + b->xoff2 - b->xoff;\n      q->y1 = y + b->yoff2 - b->yoff;\n   } else {\n      q->x0 = *xpos + b->xoff;\n      q->y0 = *ypos + b->yoff;\n      q->x1 = *xpos + b->xoff2;\n      q->y1 = *ypos + b->yoff2;\n   }\n\n   q->s0 = b->x0 * ipw;\n   q->t0 = b->y0 * iph;\n   q->s1 = b->x1 * ipw;\n   q->t1 = b->y1 * iph;\n\n   *xpos += b->xadvance;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// sdf computation\n//\n\n#define STBTT_min(a,b)  ((a) < (b) ? (a) : (b))\n#define STBTT_max(a,b)  ((a) < (b) ? (b) : (a))\n\nstatic int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2])\n{\n   float q0perp = q0[1]*ray[0] - q0[0]*ray[1];\n   float q1perp = q1[1]*ray[0] - q1[0]*ray[1];\n   float q2perp = q2[1]*ray[0] - q2[0]*ray[1];\n   float roperp = orig[1]*ray[0] - orig[0]*ray[1];\n\n   float a = q0perp - 2*q1perp + q2perp;\n   float b = q1perp - q0perp;\n   float c = q0perp - roperp;\n\n   float s0 = 0., s1 = 0.;\n   int num_s = 0;\n\n   if (a != 0.0) {\n      float discr = b*b - a*c;\n      if (discr > 0.0) {\n         float rcpna = -1 / a;\n         float d = (float) STBTT_sqrt(discr);\n         s0 = (b+d) * rcpna;\n         s1 = (b-d) * rcpna;\n         if (s0 >= 0.0 && s0 <= 1.0)\n            num_s = 1;\n         if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) {\n            if (num_s == 0) s0 = s1;\n            ++num_s;\n         }\n      }\n   } else {\n      // 2*b*s + c = 0\n      // s = -c / (2*b)\n      s0 = c / (-2 * b);\n      if (s0 >= 0.0 && s0 <= 1.0)\n         num_s = 1;\n   }\n\n   if (num_s == 0)\n      return 0;\n   else {\n      float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]);\n      float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2;\n\n      float q0d =   q0[0]*rayn_x +   q0[1]*rayn_y;\n      float q1d =   q1[0]*rayn_x +   q1[1]*rayn_y;\n      float q2d =   q2[0]*rayn_x +   q2[1]*rayn_y;\n      float rod = orig[0]*rayn_x + orig[1]*rayn_y;\n\n      float q10d = q1d - q0d;\n      float q20d = q2d - q0d;\n      float q0rd = q0d - rod;\n\n      hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d;\n      hits[0][1] = a*s0+b;\n\n      if (num_s > 1) {\n         hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d;\n         hits[1][1] = a*s1+b;\n         return 2;\n      } else {\n         return 1;\n      }\n   }\n}\n\nstatic int equal(float *a, float *b)\n{\n   return (a[0] == b[0] && a[1] == b[1]);\n}\n\nstatic int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts)\n{\n   int i;\n   float orig[2], ray[2] = { 1, 0 };\n   float y_frac;\n   int winding = 0;\n\n   // make sure y never passes through a vertex of the shape\n   y_frac = (float) STBTT_fmod(y, 1.0f);\n   if (y_frac < 0.01f)\n      y += 0.01f;\n   else if (y_frac > 0.99f)\n      y -= 0.01f;\n\n   orig[0] = x;\n   orig[1] = y;\n\n   // test a ray from (-infinity,y) to (x,y)\n   for (i=0; i < nverts; ++i) {\n      if (verts[i].type == STBTT_vline) {\n         int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y;\n         int x1 = (int) verts[i  ].x, y1 = (int) verts[i  ].y;\n         if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {\n            float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;\n            if (x_inter < x)\n               winding += (y0 < y1) ? 1 : -1;\n         }\n      }\n      if (verts[i].type == STBTT_vcurve) {\n         int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ;\n         int x1 = (int) verts[i  ].cx, y1 = (int) verts[i  ].cy;\n         int x2 = (int) verts[i  ].x , y2 = (int) verts[i  ].y ;\n         int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2));\n         int by = STBTT_max(y0,STBTT_max(y1,y2));\n         if (y > ay && y < by && x > ax) {\n            float q0[2],q1[2],q2[2];\n            float hits[2][2];\n            q0[0] = (float)x0;\n            q0[1] = (float)y0;\n            q1[0] = (float)x1;\n            q1[1] = (float)y1;\n            q2[0] = (float)x2;\n            q2[1] = (float)y2;\n            if (equal(q0,q1) || equal(q1,q2)) {\n               x0 = (int)verts[i-1].x; //-V1048\n               y0 = (int)verts[i-1].y; //-V1048\n               x1 = (int)verts[i  ].x;\n               y1 = (int)verts[i  ].y;\n               if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {\n                  float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;\n                  if (x_inter < x)\n                     winding += (y0 < y1) ? 1 : -1;\n               }\n            } else {\n               int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits);\n               if (num_hits >= 1)\n                  if (hits[0][0] < 0)\n                     winding += (hits[0][1] < 0 ? -1 : 1);\n               if (num_hits >= 2)\n                  if (hits[1][0] < 0)\n                     winding += (hits[1][1] < 0 ? -1 : 1);\n            }\n         }\n      }\n   }\n   return winding;\n}\n\nstatic float stbtt__cuberoot( float x )\n{\n   if (x<0)\n      return -(float) STBTT_pow(-x,1.0f/3.0f);\n   else\n      return  (float) STBTT_pow( x,1.0f/3.0f);\n}\n\n// x^3 + a*x^2 + b*x + c = 0\nstatic int stbtt__solve_cubic(float a, float b, float c, float* r)\n{\n   float s = -a / 3;\n   float p = b - a*a / 3;\n   float q = a * (2*a*a - 9*b) / 27 + c;\n   float p3 = p*p*p;\n   float d = q*q + 4*p3 / 27;\n   if (d >= 0) {\n      float z = (float) STBTT_sqrt(d);\n      float u = (-q + z) / 2;\n      float v = (-q - z) / 2;\n      u = stbtt__cuberoot(u);\n      v = stbtt__cuberoot(v);\n      r[0] = s + u + v;\n      return 1;\n   } else {\n      float u = (float) STBTT_sqrt(-p/3);\n      float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative\n      float m = (float) STBTT_cos(v);\n      float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;\n      r[0] = s + u * 2 * m;\n      r[1] = s - u * (m + n);\n      r[2] = s - u * (m - n);\n\n      //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f);  // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?\n      //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);\n      //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);\n      return 3;\n   }\n}\n\nSTBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)\n{\n   float scale_x = scale, scale_y = scale;\n   int ix0,iy0,ix1,iy1;\n   int w,h;\n   unsigned char *data;\n\n   if (scale == 0) return NULL;\n\n   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1);\n\n   // if empty, return NULL\n   if (ix0 == ix1 || iy0 == iy1)\n      return NULL;\n\n   ix0 -= padding;\n   iy0 -= padding;\n   ix1 += padding;\n   iy1 += padding;\n\n   w = (ix1 - ix0);\n   h = (iy1 - iy0);\n\n   if (width ) *width  = w;\n   if (height) *height = h;\n   if (xoff  ) *xoff   = ix0;\n   if (yoff  ) *yoff   = iy0;\n\n   // invert for y-downwards bitmaps\n   scale_y = -scale_y;\n\n   {\n      int x,y,i,j;\n      float *precompute;\n      stbtt_vertex *verts;\n      int num_verts = stbtt_GetGlyphShape(info, glyph, &verts);\n      data = (unsigned char *) STBTT_malloc(w * h, info->userdata);\n      precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata);\n\n      for (i=0,j=num_verts-1; i < num_verts; j=i++) {\n         if (verts[i].type == STBTT_vline) {\n            float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;\n            float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;\n            float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));\n            precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist;\n         } else if (verts[i].type == STBTT_vcurve) {\n            float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;\n            float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;\n            float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;\n            float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;\n            float len2 = bx*bx + by*by;\n            if (len2 != 0.0f)\n               precompute[i] = 1.0f / (bx*bx + by*by);\n            else\n               precompute[i] = 0.0f;\n         } else\n            precompute[i] = 0.0f;\n      }\n\n      for (y=iy0; y < iy1; ++y) {\n         for (x=ix0; x < ix1; ++x) {\n            float val;\n            float min_dist = 999999.0f;\n            float sx = (float) x + 0.5f;\n            float sy = (float) y + 0.5f;\n            float x_gspace = (sx / scale_x);\n            float y_gspace = (sy / scale_y);\n\n            int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path\n\n            for (i=0; i < num_verts; ++i) {\n               float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;\n\n               if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) {\n                  float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y;\n\n                  float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);\n                  if (dist2 < min_dist*min_dist)\n                     min_dist = (float) STBTT_sqrt(dist2);\n\n                  // coarse culling against bbox\n                  //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist &&\n                  //    sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist)\n                  dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];\n                  STBTT_assert(i != 0);\n                  if (dist < min_dist) {\n                     // check position along line\n                     // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0)\n                     // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy)\n                     float dx = x1-x0, dy = y1-y0;\n                     float px = x0-sx, py = y0-sy;\n                     // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy\n                     // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve\n                     float t = -(px*dx + py*dy) / (dx*dx + dy*dy);\n                     if (t >= 0.0f && t <= 1.0f)\n                        min_dist = dist;\n                  }\n               } else if (verts[i].type == STBTT_vcurve) {\n                  float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y;\n                  float x1 = verts[i  ].cx*scale_x, y1 = verts[i  ].cy*scale_y;\n                  float box_x0 = STBTT_min(STBTT_min(x0,x1),x2);\n                  float box_y0 = STBTT_min(STBTT_min(y0,y1),y2);\n                  float box_x1 = STBTT_max(STBTT_max(x0,x1),x2);\n                  float box_y1 = STBTT_max(STBTT_max(y0,y1),y2);\n                  // coarse culling against bbox to avoid computing cubic unnecessarily\n                  if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) {\n                     int num=0;\n                     float ax = x1-x0, ay = y1-y0;\n                     float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;\n                     float mx = x0 - sx, my = y0 - sy;\n                     float res[3] = {0.f,0.f,0.f};\n                     float px,py,t,it,dist2;\n                     float a_inv = precompute[i];\n                     if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula\n                        float a = 3*(ax*bx + ay*by);\n                        float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);\n                        float c = mx*ax+my*ay;\n                        if (a == 0.0) { // if a is 0, it's linear\n                           if (b != 0.0) {\n                              res[num++] = -c/b;\n                           }\n                        } else {\n                           float discriminant = b*b - 4*a*c;\n                           if (discriminant < 0)\n                              num = 0;\n                           else {\n                              float root = (float) STBTT_sqrt(discriminant);\n                              res[0] = (-b - root)/(2*a);\n                              res[1] = (-b + root)/(2*a);\n                              num = 2; // don't bother distinguishing 1-solution case, as code below will still work\n                           }\n                        }\n                     } else {\n                        float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point\n                        float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv;\n                        float d = (mx*ax+my*ay) * a_inv;\n                        num = stbtt__solve_cubic(b, c, d, res);\n                     }\n                     dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);\n                     if (dist2 < min_dist*min_dist)\n                        min_dist = (float) STBTT_sqrt(dist2);\n\n                     if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {\n                        t = res[0], it = 1.0f - t;\n                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;\n                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;\n                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);\n                        if (dist2 < min_dist * min_dist)\n                           min_dist = (float) STBTT_sqrt(dist2);\n                     }\n                     if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) {\n                        t = res[1], it = 1.0f - t;\n                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;\n                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;\n                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);\n                        if (dist2 < min_dist * min_dist)\n                           min_dist = (float) STBTT_sqrt(dist2);\n                     }\n                     if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) {\n                        t = res[2], it = 1.0f - t;\n                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;\n                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;\n                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);\n                        if (dist2 < min_dist * min_dist)\n                           min_dist = (float) STBTT_sqrt(dist2);\n                     }\n                  }\n               }\n            }\n            if (winding == 0)\n               min_dist = -min_dist;  // if outside the shape, value is negative\n            val = onedge_value + pixel_dist_scale * min_dist;\n            if (val < 0)\n               val = 0;\n            else if (val > 255)\n               val = 255;\n            data[(y-iy0)*w+(x-ix0)] = (unsigned char) val;\n         }\n      }\n      STBTT_free(precompute, info->userdata);\n      STBTT_free(verts, info->userdata);\n   }\n   return data;\n}\n\nSTBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff);\n}\n\nSTBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata)\n{\n   STBTT_free(bitmap, userdata);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// font name matching -- recommended not to use this\n//\n\n// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string\nstatic stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)\n{\n   stbtt_int32 i=0;\n\n   // convert utf16 to utf8 and compare the results while converting\n   while (len2) {\n      stbtt_uint16 ch = s2[0]*256 + s2[1];\n      if (ch < 0x80) {\n         if (i >= len1) return -1;\n         if (s1[i++] != ch) return -1;\n      } else if (ch < 0x800) {\n         if (i+1 >= len1) return -1;\n         if (s1[i++] != 0xc0 + (ch >> 6)) return -1;\n         if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;\n      } else if (ch >= 0xd800 && ch < 0xdc00) {\n         stbtt_uint32 c;\n         stbtt_uint16 ch2 = s2[2]*256 + s2[3];\n         if (i+3 >= len1) return -1;\n         c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;\n         if (s1[i++] != 0xf0 + (c >> 18)) return -1;\n         if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;\n         if (s1[i++] != 0x80 + ((c >>  6) & 0x3f)) return -1;\n         if (s1[i++] != 0x80 + ((c      ) & 0x3f)) return -1;\n         s2 += 2; // plus another 2 below\n         len2 -= 2;\n      } else if (ch >= 0xdc00 && ch < 0xe000) {\n         return -1;\n      } else {\n         if (i+2 >= len1) return -1;\n         if (s1[i++] != 0xe0 + (ch >> 12)) return -1;\n         if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;\n         if (s1[i++] != 0x80 + ((ch     ) & 0x3f)) return -1;\n      }\n      s2 += 2;\n      len2 -= 2;\n   }\n   return i;\n}\n\nstatic int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2)\n{\n   return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);\n}\n\n// returns results in whatever encoding you request... but note that 2-byte encodings\n// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare\nSTBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)\n{\n   stbtt_int32 i,count,stringOffset;\n   stbtt_uint8 *fc = font->data;\n   stbtt_uint32 offset = font->fontstart;\n   stbtt_uint32 nm = stbtt__find_table(fc, offset, \"name\");\n   if (!nm) return NULL;\n\n   count = ttUSHORT(fc+nm+2);\n   stringOffset = nm + ttUSHORT(fc+nm+4);\n   for (i=0; i < count; ++i) {\n      stbtt_uint32 loc = nm + 6 + 12 * i;\n      if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)\n          && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {\n         *length = ttUSHORT(fc+loc+8);\n         return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10));\n      }\n   }\n   return NULL;\n}\n\nstatic int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)\n{\n   stbtt_int32 i;\n   stbtt_int32 count = ttUSHORT(fc+nm+2);\n   stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);\n\n   for (i=0; i < count; ++i) {\n      stbtt_uint32 loc = nm + 6 + 12 * i;\n      stbtt_int32 id = ttUSHORT(fc+loc+6);\n      if (id == target_id) {\n         // find the encoding\n         stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);\n\n         // is this a Unicode encoding?\n         if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {\n            stbtt_int32 slen = ttUSHORT(fc+loc+8);\n            stbtt_int32 off = ttUSHORT(fc+loc+10);\n\n            // check if there's a prefix match\n            stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);\n            if (matchlen >= 0) {\n               // check for target_id+1 immediately following, with same encoding & language\n               if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {\n                  slen = ttUSHORT(fc+loc+12+8);\n                  off = ttUSHORT(fc+loc+12+10);\n                  if (slen == 0) {\n                     if (matchlen == nlen)\n                        return 1;\n                  } else if (matchlen < nlen && name[matchlen] == ' ') {\n                     ++matchlen;\n                     if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))\n                        return 1;\n                  }\n               } else {\n                  // if nothing immediately following\n                  if (matchlen == nlen)\n                     return 1;\n               }\n            }\n         }\n\n         // @TODO handle other encodings\n      }\n   }\n   return 0;\n}\n\nstatic int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)\n{\n   stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name);\n   stbtt_uint32 nm,hd;\n   if (!stbtt__isfont(fc+offset)) return 0;\n\n   // check italics/bold/underline flags in macStyle...\n   if (flags) {\n      hd = stbtt__find_table(fc, offset, \"head\");\n      if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;\n   }\n\n   nm = stbtt__find_table(fc, offset, \"name\");\n   if (!nm) return 0;\n\n   if (flags) {\n      // if we checked the macStyle flags, then just check the family and ignore the subfamily\n      if (stbtt__matchpair(fc, nm, name, nlen, 16, -1))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  1, -1))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  3, -1))  return 1;\n   } else {\n      if (stbtt__matchpair(fc, nm, name, nlen, 16, 17))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  1,  2))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  3, -1))  return 1;\n   }\n\n   return 0;\n}\n\nstatic int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags)\n{\n   stbtt_int32 i;\n   for (i=0;;++i) {\n      stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);\n      if (off < 0) return off;\n      if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))\n         return off;\n   }\n}\n\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wcast-qual\"\n#endif\n\nSTBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,\n                                float pixel_height, unsigned char *pixels, int pw, int ph,\n                                int first_char, int num_chars, stbtt_bakedchar *chardata)\n{\n   return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata);\n}\n\nSTBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index)\n{\n   return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index);\n}\n\nSTBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data)\n{\n   return stbtt_GetNumberOfFonts_internal((unsigned char *) data);\n}\n\nSTBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset)\n{\n   return stbtt_InitFont_internal(info, (unsigned char *) data, offset);\n}\n\nSTBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags)\n{\n   return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags);\n}\n\nSTBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)\n{\n   return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2);\n}\n\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n\n#endif // STB_TRUETYPE_IMPLEMENTATION\n\n\n// FULL VERSION HISTORY\n//\n//   1.25 (2021-07-11) many fixes\n//   1.24 (2020-02-05) fix warning\n//   1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)\n//   1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined\n//   1.21 (2019-02-25) fix warning\n//   1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()\n//   1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod\n//   1.18 (2018-01-29) add missing function\n//   1.17 (2017-07-23) make more arguments const; doc fix\n//   1.16 (2017-07-12) SDF support\n//   1.15 (2017-03-03) make more arguments const\n//   1.14 (2017-01-16) num-fonts-in-TTC function\n//   1.13 (2017-01-02) support OpenType fonts, certain Apple fonts\n//   1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual\n//   1.11 (2016-04-02) fix unused-variable warning\n//   1.10 (2016-04-02) allow user-defined fabs() replacement\n//                     fix memory leak if fontsize=0.0\n//                     fix warning from duplicate typedef\n//   1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges\n//   1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges\n//   1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;\n//                     allow PackFontRanges to pack and render in separate phases;\n//                     fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);\n//                     fixed an assert() bug in the new rasterizer\n//                     replace assert() with STBTT_assert() in new rasterizer\n//   1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)\n//                     also more precise AA rasterizer, except if shapes overlap\n//                     remove need for STBTT_sort\n//   1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC\n//   1.04 (2015-04-15) typo in example\n//   1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes\n//   1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++\n//   1.01 (2014-12-08) fix subpixel position when oversampling to exactly match\n//                        non-oversampled; STBTT_POINT_SIZE for packed case only\n//   1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling\n//   0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)\n//   0.9  (2014-08-07) support certain mac/iOS fonts without an MS platformID\n//   0.8b (2014-07-07) fix a warning\n//   0.8  (2014-05-25) fix a few more warnings\n//   0.7  (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back\n//   0.6c (2012-07-24) improve documentation\n//   0.6b (2012-07-20) fix a few more warnings\n//   0.6  (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,\n//                        stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty\n//   0.5  (2011-12-09) bugfixes:\n//                        subpixel glyph renderer computed wrong bounding box\n//                        first vertex of shape can be off-curve (FreeSans)\n//   0.4b (2011-12-03) fixed an error in the font baking example\n//   0.4  (2011-12-01) kerning, subpixel rendering (tor)\n//                    bugfixes for:\n//                        codepoint-to-glyph conversion using table fmt=12\n//                        codepoint-to-glyph conversion using table fmt=4\n//                        stbtt_GetBakedQuad with non-square texture (Zer)\n//                    updated Hello World! sample to use kerning and subpixel\n//                    fixed some warnings\n//   0.3  (2009-06-24) cmap fmt=12, compound shapes (MM)\n//                    userdata, malloc-from-userdata, non-zero fill (stb)\n//   0.2  (2009-03-11) Fix unsigned/signed char warnings\n//   0.1  (2009-03-09) First public release\n//\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/include/misc/freetype/imgui_freetype.h",
    "content": "// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)\n// (headers)\n\n#pragma once\n#include \"imgui.h\"      // IMGUI_API\n#ifndef IMGUI_DISABLE\n\n// Usage:\n// - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to automatically enable support\n//   for imgui_freetype in imgui. It is equivalent to selecting the default loader with:\n//      io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())\n\n// Optional support for OpenType SVG fonts:\n// - Add '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG' to use plutosvg (not provided). See #7927.\n// - Add '#define IMGUI_ENABLE_FREETYPE_LUNASVG' to use lunasvg (not provided). See #6591.\n\n// Forward declarations\nstruct ImFontAtlas;\nstruct ImFontLoader;\n\n// Hinting greatly impacts visuals (and glyph sizes).\n// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter.\n// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h\n// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way.\n// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer.\n// You can set those flags globally in ImFontAtlas::FontLoaderFlags\n// You can set those flags on a per font basis in ImFontConfig::FontLoaderFlags\ntypedef unsigned int ImGuiFreeTypeLoaderFlags;\nenum ImGuiFreeTypeLoaderFlags_\n{\n    ImGuiFreeTypeLoaderFlags_NoHinting     = 1 << 0,   // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.\n    ImGuiFreeTypeLoaderFlags_NoAutoHint    = 1 << 1,   // Disable auto-hinter.\n    ImGuiFreeTypeLoaderFlags_ForceAutoHint = 1 << 2,   // Indicates that the auto-hinter is preferred over the font's native hinter.\n    ImGuiFreeTypeLoaderFlags_LightHinting  = 1 << 3,   // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.\n    ImGuiFreeTypeLoaderFlags_MonoHinting   = 1 << 4,   // Strong hinting algorithm that should only be used for monochrome output.\n    ImGuiFreeTypeLoaderFlags_Bold          = 1 << 5,   // Styling: Should we artificially embolden the font?\n    ImGuiFreeTypeLoaderFlags_Oblique       = 1 << 6,   // Styling: Should we slant the font, emulating italic style?\n    ImGuiFreeTypeLoaderFlags_Monochrome    = 1 << 7,   // Disable anti-aliasing. Combine this with MonoHinting for best results!\n    ImGuiFreeTypeLoaderFlags_LoadColor     = 1 << 8,   // Enable FreeType color-layered glyphs\n    ImGuiFreeTypeLoaderFlags_Bitmap        = 1 << 9,   // Enable FreeType bitmap glyphs\n    // IMHEX PATCH BEGIN\n    ImGuiFreeTypeLoaderFlags_SubPixel      = 1 << 10   // Atlas was generated with sub-pixel rendering enabled\n    // IMHEX PATCH END\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiFreeTypeBuilderFlags_NoHinting     = ImGuiFreeTypeLoaderFlags_NoHinting,\n    ImGuiFreeTypeBuilderFlags_NoAutoHint    = ImGuiFreeTypeLoaderFlags_NoAutoHint,\n    ImGuiFreeTypeBuilderFlags_ForceAutoHint = ImGuiFreeTypeLoaderFlags_ForceAutoHint,\n    ImGuiFreeTypeBuilderFlags_LightHinting  = ImGuiFreeTypeLoaderFlags_LightHinting,\n    ImGuiFreeTypeBuilderFlags_MonoHinting   = ImGuiFreeTypeLoaderFlags_MonoHinting,\n    ImGuiFreeTypeBuilderFlags_Bold          = ImGuiFreeTypeLoaderFlags_Bold,\n    ImGuiFreeTypeBuilderFlags_Oblique       = ImGuiFreeTypeLoaderFlags_Oblique,\n    ImGuiFreeTypeBuilderFlags_Monochrome    = ImGuiFreeTypeLoaderFlags_Monochrome,\n    ImGuiFreeTypeBuilderFlags_LoadColor     = ImGuiFreeTypeLoaderFlags_LoadColor,\n    ImGuiFreeTypeBuilderFlags_Bitmap        = ImGuiFreeTypeLoaderFlags_Bitmap,\n#endif\n};\n\n// Obsolete names (will be removed)\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\ntypedef ImGuiFreeTypeLoaderFlags_ ImGuiFreeTypeBuilderFlags_;\n#endif\n\nnamespace ImGuiFreeType\n{\n    // This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'.\n    // If you need to dynamically select between multiple builders:\n    // - you can manually assign this builder with 'atlas->SetFontLoader(ImGuiFreeType::GetFontLoader())'\n    // - prefer deep-copying this into your own ImFontLoader instance if you use hot-reloading that messes up static data.\n    IMGUI_API const ImFontLoader*       GetFontLoader();\n\n    // Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE()\n    // However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired.\n    IMGUI_API void                      SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = nullptr);\n\n    // Display UI to edit ImFontAtlas::FontLoaderFlags (shared) or ImFontConfig::FontLoaderFlags (single source)\n    IMGUI_API bool                      DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags);\n\n    // Obsolete names (will be removed)\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    //IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())' if you need runtime selection.\n    //static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontLoaderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE'\n#endif\n}\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/source/imgui.cpp",
    "content": "// dear imgui, v1.92.7 WIP\n// (main code and documentation)\n\n// Help:\n// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.\n// - Read top of imgui.cpp for more details, links and comments.\n// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including imgui.h (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4.\n\n// Resources:\n// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)\n// - Homepage ................... https://github.com/ocornut/imgui\n// - Releases & Changelog ....... https://github.com/ocornut/imgui/releases\n// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!)\n// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)\n//   - Getting Started            https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)\n//   - Third-party Extensions     https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)\n//   - Bindings/Backends          https://github.com/ocornut/imgui/wiki/Bindings (language bindings + backends for various tech/engines)\n//   - Debug Tools                https://github.com/ocornut/imgui/wiki/Debug-Tools\n//   - Glossary                   https://github.com/ocornut/imgui/wiki/Glossary\n//   - Software using Dear ImGui  https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui\n// - Issues & support ........... https://github.com/ocornut/imgui/issues\n// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)\n// - Web version of the Demo .... https://pthom.github.io/imgui_explorer (w/ source code browser)\n\n// For FIRST-TIME users having issues compiling/linking/running:\n// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.\n// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.\n// Since 1.92, we encourage font loading questions to also be posted in 'Issues'.\n\n// Copyright (c) 2014-2026 Omar Cornut\n// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.\n// See LICENSE.txt for copyright and licensing details (standard MIT License).\n// This library is free but needs your support to sustain development and maintenance.\n// Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts.\n// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding\n// Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.\n\n// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.\n// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without\n// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't\n// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you\n// to a better solution or official support for them.\n\n/*\n\nIndex of this file:\n\nDOCUMENTATION\n\n- MISSION STATEMENT\n- CONTROLS GUIDE\n- PROGRAMMER GUIDE\n  - READ FIRST\n  - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI\n  - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE\n  - HOW A SIMPLE APPLICATION MAY LOOK LIKE\n  - USING CUSTOM BACKEND / CUSTOM ENGINE\n- API BREAKING CHANGES (read me when you update!)\n- FREQUENTLY ASKED QUESTIONS (FAQ)\n  - Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer)\n\nCODE\n(search for \"[SECTION]\" in the code to find them)\n\n// [SECTION] INCLUDES\n// [SECTION] FORWARD DECLARATIONS\n// [SECTION] CONTEXT AND MEMORY ALLOCATORS\n// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)\n// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)\n// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)\n// [SECTION] MISC HELPERS/UTILITIES (File functions)\n// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)\n// [SECTION] MISC HELPERS/UTILITIES (Color functions)\n// [SECTION] ImGuiStorage\n// [SECTION] ImGuiTextFilter\n// [SECTION] ImGuiTextBuffer, ImGuiTextIndex\n// [SECTION] ImGuiListClipper\n// [SECTION] STYLING\n// [SECTION] RENDER HELPERS\n// [SECTION] INITIALIZATION, SHUTDOWN\n// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)\n// [SECTION] FONTS, TEXTURES\n// [SECTION] ID STACK\n// [SECTION] INPUTS\n// [SECTION] ERROR CHECKING, STATE RECOVERY\n// [SECTION] ITEM SUBMISSION\n// [SECTION] LAYOUT\n// [SECTION] SCROLLING\n// [SECTION] TOOLTIPS\n// [SECTION] POPUPS\n// [SECTION] WINDOW FOCUS\n// [SECTION] KEYBOARD/GAMEPAD NAVIGATION\n// [SECTION] DRAG AND DROP\n// [SECTION] LOGGING/CAPTURING\n// [SECTION] SETTINGS\n// [SECTION] LOCALIZATION\n// [SECTION] VIEWPORTS, PLATFORM WINDOWS\n// [SECTION] DOCKING\n// [SECTION] PLATFORM DEPENDENT HELPERS\n// [SECTION] METRICS/DEBUGGER WINDOW\n// [SECTION] DEBUG LOG WINDOW\n// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)\n\n*/\n\n//-----------------------------------------------------------------------------\n// DOCUMENTATION\n//-----------------------------------------------------------------------------\n\n/*\n\n MISSION STATEMENT\n =================\n\n - Easy to use to create code-driven and data-driven tools.\n - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.\n - Easy to hack and improve.\n - Minimize setup and maintenance.\n - Minimize state storage on user side.\n - Minimize state synchronization.\n - Portable, minimize dependencies, run on target (consoles, phones, etc.).\n - Efficient runtime and memory consumption.\n\n Designed primarily for developers and content-creators, not the typical end-user!\n Some of the current weaknesses (which we aim to address in the future) includes:\n\n - Doesn't look fancy by default.\n - Limited layout features, intricate layouts are typically crafted in code.\n\n\n CONTROLS GUIDE\n ==============\n\n - MOUSE CONTROLS\n   - Mouse wheel:                   Scroll vertically.\n   - Shift+Mouse wheel:             Scroll horizontally.\n   - Click [X]:                     Close a window, available when 'bool* p_open' is passed to ImGui::Begin().\n   - Click ^, Double-Click title:   Collapse window.\n   - Drag on corner/border:         Resize window (double-click to auto fit window to its contents).\n   - Drag on any empty space:       Move window (unless io.ConfigWindowsMoveFromTitleBarOnly = true).\n   - Left-click outside popup:      Close popup stack (right-click over underlying popup: Partially close popup stack).\n\n - TEXT EDITOR\n   - Hold Shift or Drag Mouse:      Select text.\n   - Ctrl+Left/Right:               Word jump.\n   - Ctrl+Shift+Left/Right:         Select words.\n   - Ctrl+A or Double-Click:        Select All.\n   - Ctrl+X, Ctrl+C, Ctrl+V:        Use OS clipboard.\n   - Ctrl+Z                         Undo.\n   - Ctrl+Y or Ctrl+Shift+Z:        Redo.\n   - ESCAPE:                        Revert text to its original value.\n   - On macOS, controls are automatically adjusted to match standard macOS text editing and behaviors.\n     (for 99% of shortcuts, Ctrl is replaced by Cmd on macOS).\n\n - KEYBOARD CONTROLS\n   - Basic:\n     - Tab, Shift+Tab               Cycle through text editable fields.\n     - Ctrl+Tab, Ctrl+Shift+Tab     Cycle through windows.\n     - Ctrl+Click                   Input text into a Slider or Drag widget.\n   - Extended features with `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard`:\n     - Tab, Shift+Tab:              Cycle through every items.\n     - Arrow keys                   Move through items using directional navigation. Tweak value.\n     - Arrow keys + Alt, Shift      Tweak slower, tweak faster (when using arrow keys).\n     - Enter                        Activate item (prefer text input when possible).\n     - Space                        Activate item (prefer tweaking with arrows when possible).\n     - Escape                       Deactivate item, leave child window, close popup.\n     - Page Up, Page Down           Previous page, next page.\n     - Home, End                    Scroll to top, scroll to bottom.\n     - Alt                          Toggle between scrolling layer and menu layer.\n     - Ctrl+Tab then Ctrl+Arrows    Move window. Hold Shift to resize instead of moving.\n     - Menu or Shift+F10            Open context menu.\n   - Output when ImGuiConfigFlags_NavEnableKeyboard set,\n     - io.WantCaptureKeyboard flag is set when keyboard is claimed.\n     - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.\n     - io.NavVisible: true when the navigation cursor is visible (usually goes to back false when mouse is used).\n\n - GAMEPAD CONTROLS\n   - Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.\n   - Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!\n   - Download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets\n   - Backend support: backend needs to:\n      - Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.\n      - For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.\n        Backend code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.).\n   - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,\n     with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.\n\n - REMOTE INPUTS SHARING & MOUSE EMULATION\n   - PS4/PS5 users: Consider emulating a mouse cursor with DualShock touch pad or a spare analog stick as a mouse-emulation fallback.\n   - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + run examples/libs/synergy/uSynergy.c (on your console/tablet/phone app)\n     in order to share your PC mouse/keyboard.\n   - See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.\n   - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the io.ConfigNavMoveSetMousePos flag.\n     Enabling io.ConfigNavMoveSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.\n     When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.\n     When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.\n     (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, Dear ImGui will misbehave as it will see your mouse moving back & forth!)\n     (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want\n     to set a boolean to ignore your other external mouse positions until the external source is moved again.)\n\n\n PROGRAMMER GUIDE\n ================\n\n READ FIRST\n ----------\n - Remember to check the wonderful Wiki: https://github.com/ocornut/imgui/wiki\n - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone!\n   The UI can be highly dynamic, there are no construction or destruction steps, less superfluous\n   data retention on your side, less state duplication, less state synchronization, fewer bugs.\n - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.\n   Or browse pthom's online imgui_explorer: https://pthom.github.io/imgui_explorer for a web version w/ source code browser.\n - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.\n - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).\n   You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.\n - Dear ImGui is a \"single pass\" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.\n   For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI,\n   where the UI code is called multiple times (\"multiple passes\") from a single entry point. There are pros and cons to both approaches.\n - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.\n - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).\n   If you get an assert, read the messages and comments around the assert.\n - This codebase aims to be highly optimized:\n   - A typical idle frame should never call malloc/free.\n   - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible.\n   - We put particular energy in making sure performances are decent with typical \"Debug\" build settings as well.\n     Which mean we tend to avoid over-relying on \"zero-cost abstraction\" as they aren't zero-cost at all.\n - This codebase aims to be both highly opinionated and highly flexible:\n   - This code works because of the things it choose to solve or not solve.\n   - C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers,\n     and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now).\n     This is to increase compatibility, increase maintainability and facilitate use from other languages.\n   - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.\n     See FAQ \"How can I use my own math types instead of ImVec2/ImVec4?\" for details about setting up imconfig.h for that.\n     We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally.\n   - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction\n     (so don't use ImVector in your code or at our own risk!).\n   - Building: We don't use nor mandate a build system for the main library.\n     This is in an effort to ensure that it works in the real world aka with any esoteric build setup.\n     This is also because providing a build system for the main library would be of little-value.\n     The build problems are almost never coming from the main library but from specific backends.\n\n\n HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI\n ----------------------------------------------\n - Update submodule or copy/overwrite every file.\n - About imconfig.h:\n   - You may modify your copy of imconfig.h, in this case don't overwrite it.\n   - or you may locally branch to modify imconfig.h and merge/rebase latest.\n   - or you may '#define IMGUI_USER_CONFIG \"my_config_file.h\"' globally from your build system to\n     specify a custom path for your imconfig.h file and instead not have to modify the default one.\n\n - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)\n - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over \"master\".\n - You can also use '#define IMGUI_USER_CONFIG \"my_config_file.h\" to redirect configuration to your own file.\n - Read the \"API BREAKING CHANGES\" section (below). This is where we list occasional API breaking changes.\n   If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed\n   from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will\n   likely be a comment about it. Please report any issue to the GitHub page!\n - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.\n - Try to keep your copy of Dear ImGui reasonably up to date!\n\n\n GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE\n ---------------------------------------------------------------\n - See https://github.com/ocornut/imgui/wiki/Getting-Started.\n - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.\n - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.\n - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.\n   It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).\n - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types.\n - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.\n - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.\n   Effectively it means you can create widgets at any time in your code, regardless of considerations of being in \"update\" vs \"render\"\n   phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().\n - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.\n - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder.\n\n\n HOW A SIMPLE APPLICATION MAY LOOK LIKE\n --------------------------------------\n\n USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).\n The sub-folders in examples/ contain examples applications following this structure.\n\n     // Application init: create a dear imgui context, setup some options, load fonts\n     ImGui::CreateContext();\n     ImGuiIO& io = ImGui::GetIO();\n     // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.\n     // TODO: Fill optional fields of the io structure later.\n     // TODO: Load TTF/OTF fonts if you don't want to use the default font.\n\n     // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)\n     ImGui_ImplWin32_Init(hwnd);\n     ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);\n\n     // Application main loop\n     while (true)\n     {\n         // Feed inputs to dear imgui, start new frame\n         ImGui_ImplDX11_NewFrame();\n         ImGui_ImplWin32_NewFrame();\n         ImGui::NewFrame();\n\n         // Any application code here\n         ImGui::Text(\"Hello, world!\");\n\n         // Render dear imgui into framebuffer\n         ImGui::Render();\n         ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());\n         g_pSwapChain->Present(1, 0);\n     }\n\n     // Shutdown\n     ImGui_ImplDX11_Shutdown();\n     ImGui_ImplWin32_Shutdown();\n     ImGui::DestroyContext();\n\n To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,\n you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!\n Please read the FAQ entry \"How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?\" about this.\n\n\nUSING CUSTOM BACKEND / CUSTOM ENGINE\n------------------------------------\n\nIMPLEMENTING YOUR PLATFORM BACKEND:\n -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md for basic instructions.\n -> the Platform backends in impl_impl_XXX.cpp files contain many implementations.\n\nIMPLEMENTING YOUR RenderDrawData() function:\n -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md\n -> the Renderer Backends in impl_impl_XXX.cpp files contain many implementations of a ImGui_ImplXXXX_RenderDrawData() function.\n\nIMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:\n -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md\n -> the Renderer Backends in impl_impl_XXX.cpp files contain many implementations of a ImGui_ImplXXXX_UpdateTexture() function.\n\n Basic application/backend skeleton:\n\n     // Application init: create a Dear ImGui context, setup some options, load fonts\n     ImGui::CreateContext();\n     ImGuiIO& io = ImGui::GetIO();\n     // TODO: set io.ConfigXXX values, e.g.\n     io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;  // Enable keyboard controls\n\n     // TODO: Load TTF/OTF fonts if you don't want to use the default font.\n     io.Fonts->AddFontFromFileTTF(\"NotoSans.ttf\");\n\n     // Application main loop\n     while (true)\n     {\n        // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.\n        // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)\n        io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)\n        io.DisplaySize.x = 1920.0f;             // set the current display width\n        io.DisplaySize.y = 1280.0f;             // set the current display height here\n        io.AddMousePosEvent(mouse_x, mouse_y);  // update mouse position\n        io.AddMouseButtonEvent(0, mouse_b[0]);  // update mouse button states\n        io.AddMouseButtonEvent(1, mouse_b[1]);  // update mouse button states\n\n        // Call NewFrame(), after this point you can use ImGui::* functions anytime\n        // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)\n        ImGui::NewFrame();\n\n        // Most of your application code here\n        ImGui::Text(\"Hello, world!\");\n        MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin(\"My window\"); ImGui::Text(\"Hello, world!\"); ImGui::End();\n        MyGameRender(); // may use any Dear ImGui functions as well!\n\n        // End the dear imgui frame\n        // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code)\n        ImGui::EndFrame(); // this is automatically called by Render(), but available\n        ImGui::Render();\n\n        // Update textures\n        ImDrawData* draw_data = ImGui::GetDrawData();\n        for (ImTextureData* tex : *draw_data->Textures)\n            if (tex->Status != ImTextureStatus_OK)\n                MyImGuiBackend_UpdateTexture(tex);\n\n        // Render dear imgui contents, swap buffers\n        MyImGuiBackend_RenderDrawData(draw_data);\n        SwapBuffers();\n     }\n\n     // Shutdown\n     ImGui::DestroyContext();\n\n\n\n API BREAKING CHANGES\n ====================\n\n Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.\n Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code.\n When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.\n You can read releases logs https://github.com/ocornut/imgui/releases for more details.\n\n(Docking/Viewport Branch)\n - 2026/XX/XX (1.XXXX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that:\n                          - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore.\n                            you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos)\n                          - likewise io.MousePos and GetMousePos() will use OS coordinates.\n                            If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos.\n\n - 2026/03/12 (1.92.7) - Changed default ImTextureID_Invalid to -1 instead of 0 if not #define-d. (#9293, #8745, #8465, #7090)\n                         It seems like a better default since it will work with backends storing indices or memory offsets inside ImTextureID, where 0 might be a valid value.\n                         If this is causing problem with e.g. your custom ImTextureID definition, you can add '#define ImTextureID_Invalid 0' to your imconfig.h + PLEASE report this to GitHub.\n                         If you have hard-coded e.g. 'if (tex_id == 0)' checks they should be updated. e.g. OpenGL2, OpenGL3 and SDLRenderer3 backends incorrectly had 'IM_ASSERT(tex->TexID == 0)' lines which were replaced with 'IM_ASSERT(tex->TexID == ImTextureID_Invalid)'. (#9295)\n - 2026/02/26 (1.92.7) - Separator: fixed a legacy quirk where Separator() was submitting a zero-height item for layout purpose, even though it draws a 1-pixel separator.\n                         The fix could affect code e.g. computing height from multiple widgets in order to allocate vertical space for a footer or multi-line status bar. (#2657, #9263)\n                         The \"Console\" example had such a bug:\n                            float footer_height = style.ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();\n                            BeginChild(\"ScrollingRegion\", { 0, -footer_height });\n                         Should be:\n                            float footer_height = style.ItemSpacing.y + style.SeparatorSize + ImGui::GetFrameHeightWithSpacing();\n                            BeginChild(\"ScrollingRegion\", { 0, -footer_height });\n                         When such idiom was used and assuming zero-height Separator, it is likely that in 1.92.7 the resulting window will have unexpected 1 pixel scrolling range.\n - 2026/02/23 (1.92.7) - Commented out legacy signature for Combo(), ListBox(), signatures which were obsoleted in 1.90 (Nov 2023), when the getter callback type was changed.\n                         - Old getter type:   bool (*getter)(void* user_data, int idx, const char** out_text)   // Set label + return bool. False replaced label with placeholder.\n                         - New getter type:   const char* (*getter)(void* user_data, int idx)                   // Return label or NULL/empty label if missing\n - 2026/01/08 (1.92.6) - Commented out legacy names obsoleted in 1.90 (Sept 2023): 'BeginChildFrame()' --> 'BeginChild()' with 'ImGuiChildFlags_FrameStyle'. 'EndChildFrame()' --> 'EndChild()'. 'ShowStackToolWindow()' --> 'ShowIDStackToolWindow()'. 'IM_OFFSETOF()' --> 'offsetof()'.\n - 2026/01/07 (1.92.6) - Popups: changed compile-time 'ImGuiPopupFlags popup_flags = 1' default value to be '= 0' for BeginPopupContextItem(), BeginPopupContextWindow(), BeginPopupContextVoid(), OpenPopupOnItemClick(). Default value has same meaning before and after.\n                         - Refer to GitHub topic #9157 if you have any question.\n                         - Before this version, those functions had a 'ImGuiPopupFlags popup_flags = 1' default value in their function signature.\n                           Explicitly passing a literal 0 meant ImGuiPopupFlags_MouseButtonLeft. The default literal 1 meant ImGuiPopupFlags_MouseButtonRight.\n                           This was introduced by a change on 2020/06/23 (1.77) while changing the signature from 'int mouse_button' to 'ImGuiPopupFlags popup_flags' and trying to preserve then-legacy behavior.\n                           We have now changed this behavior to cleanup a very old API quirk, facilitate use by bindings, and to remove the last and error-prone non-zero default value.\n                           Also because we deemed it extremely rare to use those helper functions with the Left mouse button! As using the LMB would generally be triggered via another widget, e.g. a Button() + a OpenPopup()/BeginPopup() call.\n                         - Before: The default = 1 means ImGuiPopupFlags_MouseButtonRight. Explicitly passing a literal 0 means ImGuiPopupFlags_MouseButtonLeft.\n                         - After:  The default = 0 means ImGuiPopupFlags_MouseButtonRight. Explicitly passing a literal 1 also means ImGuiPopupFlags_MouseButtonRight (if legacy behavior are enabled) or will assert (if legacy behavior are disabled).\n                         - TL;DR: if you don't want to use right mouse button for popups, always specify it explicitly using a named ImGuiPopupFlags_MouseButtonXXXX value.\n                         Recap:\n                         - BeginPopupContextItem(\"foo\");                                         // Behavior unchanged (use Right button)\n                         - BeginPopupContextItem(\"foo\", ImGuiPopupFlags_MouseButtonLeft);        // Behavior unchanged (use Left button)\n                         - BeginPopupContextItem(\"foo\", ImGuiPopupFlags_MouseButtonLeft | xxx);  // Behavior unchanged (use Left button + flags)\n                         - BeginPopupContextItem(\"foo\", ImGuiPopupFlags_MouseButtonRight | xxx); // Behavior unchanged (use Right button + flags)\n                         - BeginPopupContextItem(\"foo\", 1);                                      // Behavior unchanged (as a courtesy we legacy interpret 1 as ImGuiPopupFlags_MouseButtonRight, will assert if disabling legacy behaviors.\n                         - BeginPopupContextItem(\"foo\", 0);                                      // !! Behavior changed !! Was Left button. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft.\n                         - BeginPopupContextItem(\"foo\", ImGuiPopupFlags_NoReopen);               // !! Behavior changed !! Was Left button + flags. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft | xxx.\n - 2025/12/23 (1.92.6) - Fonts: AddFontDefault() now automatically selects an embedded font between the new scalable AddFontDefaultVector() and the classic pixel-clean AddFontDefaultBitmap().\n                         The default selection is based on (style.FontSizeBase * FontScaleMain * FontScaleDpi) reaching a small threshold, but old codebases may not set any of them properly. As as a result, it is likely that old codebase may still default to AddFontDefaultBitmap().\n                         Prefer calling either based on your own logic. You can call AddFontDefaultBitmap() to ensure legacy behavior.\n - 2025/12/23 (1.92.6) - Fonts: removed ImFontConfig::PixelSnapV added in 1.92 which turns out is unnecessary (and misdocumented). Post-rescale GlyphOffset is always rounded.\n - 2025/12/17 (1.92.6) - Renamed helper macro IM_ARRAYSIZE() -> IM_COUNTOF(). Kept redirection/legacy name for now.\n - 2025/12/11 (1.92.6) - Hashing: handling of \"###\" operator to reset to seed within a string identifier doesn't include the \"###\" characters in the output hash anymore.\n                         - Before: GetID(\"Hello###World\") == GetID(\"###World\") != GetID(\"World\")\n                         - After:  GetID(\"Hello###World\") == GetID(\"###World\") == GetID(\"World\")\n                         - This has the property of facilitating concatenating and manipulating identifiers using \"###\", and will allow fixing other dangling issues.\n                         - This will invalidate hashes (stored in .ini data) for Tables and Windows that are using the \"###\" operators. (#713, #1698)\n - 2025/11/24 (1.92.6) - Fonts: Fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which did erroneously make a copy of the font data, essentially defeating the purpose of this flag and wasting memory.\n                         (trivia: undetected since July 2015, this is perhaps the oldest bug in Dear ImGui history, albeit for a rarely used feature, see #9086)\n                         HOWEVER, fixing this bug is likely to surface bugs in user code using `FontDataOwnedByAtlas = false`.\n                         - Prior to 1.92, font data only needed to be available during the atlas->AddFontXXX() call.\n                         - Since 1.92, font data needs to available until atlas->RemoveFont(), or more typically until a shutdown of the owning context or font atlas.\n                         - The fact that handling of `FontDataOwnedByAtlas = false` was broken bypassed the issue altogether.\n - 2025/11/06 (1.92.5) - BeginChild: commented out some legacy names which were obsoleted in 1.90.0 (Nov 2023), 1.90.9 (July 2024), 1.91.1 (August 2024):\n                         - ImGuiChildFlags_Border                    --> ImGuiChildFlags_Borders\n                         - ImGuiWindowFlags_NavFlattened             --> ImGuiChildFlags_NavFlattened (moved to ImGuiChildFlags). BeginChild(name, size, 0, ImGuiWindowFlags_NavFlattened) --> BeginChild(name, size, ImGuiChildFlags_NavFlattened, 0)\n                         - ImGuiWindowFlags_AlwaysUseWindowPadding   --> ImGuiChildFlags_AlwaysUseWindowPadding (moved to ImGuiChildFlags). BeginChild(name, size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding) --> BeginChild(name, size, ImGuiChildFlags_AlwaysUseWindowPadding, 0)\n - 2025/11/06 (1.92.5) - Keys: commented out legacy names which were obsoleted in 1.89.0 (August 2022):\n                         - ImGuiKey_ModCtrl  --> ImGuiMod_Ctrl\n                         - ImGuiKey_ModShift --> ImGuiMod_Shift\n                         - ImGuiKey_ModAlt   --> ImGuiMod_Alt\n                         - ImGuiKey_ModSuper --> ImGuiMod_Super\n - 2025/11/06 (1.92.5) - IO: commented out legacy io.ClearInputCharacters() obsoleted in 1.89.8 (Aug 2023). Calling io.ClearInputKeys() is enough.\n - 2025/11/06 (1.92.5) - Commented out legacy SetItemAllowOverlap() obsoleted in 1.89.7: this never worked right. Use SetNextItemAllowOverlap() _before_ item instead.\n - 2025/10/14 (1.92.4) - TreeNode, Selectable, Clipper: commented out legacy names which were obsoleted in 1.89.7 (July 2023) and 1.89.9 (Sept 2023);\n                         - ImGuiTreeNodeFlags_AllowItemOverlap       --> ImGuiTreeNodeFlags_AllowOverlap\n                         - ImGuiSelectableFlags_AllowItemOverlap     --> ImGuiSelectableFlags_AllowOverlap\n                         - ImGuiListClipper::IncludeRangeByIndices() --> ImGuiListClipper::IncludeItemsByIndex()\n - 2025/09/22 (1.92.4) - Viewports: renamed io.ConfigViewportPlatformFocusSetsImGuiFocus to io.ConfigViewportsPlatformFocusSetsImGuiFocus. Was a typo in the first place. (#6299, #6462)\n - 2025/08/08 (1.92.2) - Backends: SDL_GPU3: Changed ImTextureID type from SDL_GPUTextureSamplerBinding* to SDL_GPUTexture*, which is more natural and easier for user to manage. If you need to change the current sampler, you can access the ImGui_ImplSDLGPU3_RenderState struct. (#8866, #8163, #7998, #7988)\n - 2025/07/31 (1.92.2) - Tabs: Renamed ImGuiTabBarFlags_FittingPolicyResizeDown to ImGuiTabBarFlags_FittingPolicyShrink. Kept inline redirection enum (will obsolete).\n - 2025/06/25 (1.92.0) - Layout: commented out legacy ErrorCheckUsingSetCursorPosToExtendParentBoundaries() fallback obsoleted in 1.89 (August 2022) which allowed a SetCursorPos()/SetCursorScreenPos() call WITHOUT AN ITEM\n                         to extend parent window/cell boundaries. Replaced with assert/tooltip that would already happens if previously using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#5548, #4510, #3355, #1760, #1490, #4152, #150)\n                         - Incorrect way to make a window content size 200x200:\n                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();\n                         - Correct ways to make a window content size 200x200:\n                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();\n                              Begin(...) + Dummy(ImVec2(200,200)) + End();\n                         - TL;DR; if the assert triggers, you can add a Dummy({0,0}) call to validate extending parent boundaries.\n - 2025/06/11 (1.92.0) - Renamed/moved ImGuiConfigFlags_DpiEnableScaleFonts -> bool io.ConfigDpiScaleFonts.\n                       - Renamed/moved ImGuiConfigFlags_DpiEnableScaleViewports -> bool io.ConfigDpiScaleViewports. **Neither of those flags are very useful in current code. They will be useful once we merge font changes.**\n                         [there was a bug on 2025/06/12: when using the old config flags names, they were not imported correctly into the new ones, fixed on 2025/09/12]\n - 2025/06/11 (1.92.0) - THIS VERSION CONTAINS THE LARGEST AMOUNT OF BREAKING CHANGES SINCE 2015! I TRIED REALLY HARD TO KEEP THEM TO A MINIMUM, REDUCE THE AMOUNT OF INTERFERENCES, BUT INEVITABLY SOME USERS WILL BE AFFECTED.\n                         IN ORDER TO HELP US IMPROVE THE TRANSITION PROCESS, INCL. DOCUMENTATION AND COMMENTS, PLEASE REPORT **ANY** DOUBT, CONFUSION, QUESTIONS, FEEDBACK TO: https://github.com/ocornut/imgui/issues/\n                         As part of the plan to reduce impact of API breaking changes, several unfinished changes/features/refactors related to font and text systems and scaling will be part of subsequent releases (1.92.1+).\n                         If you are updating from an old version, and expecting a massive or difficult update, consider first updating to 1.91.9 to reduce the amount of changes.\n                       - Hard to read? Refer to 'docs/Changelog.txt' for a less compact and more complete version of this!\n                       - Fonts: **IMPORTANT**: if your app was solving the OSX/iOS Retina screen specific logical vs display scale problem by setting io.DisplayFramebufferScale (e.g. to 2.0f) + setting io.FontGlobalScale (e.g. to 1.0f/2.0f) + loading fonts at scaled sizes (e.g. size X * 2.0f):\n                         This WILL NOT map correctly to the new system! Because font will rasterize as requested size.\n                         - With a legacy backend (< 1.92): Instead of setting io.FontGlobalScale = 1.0f/N -> set ImFontCfg::RasterizerDensity = N. This already worked before, but is now pretty much required.\n                         - With a new backend (1.92+): This should be all automatic. FramebufferScale is automatically used to set current font RasterizerDensity. FramebufferScale is a per-viewport property provided by backend through the Platform_GetWindowFramebufferScale() handler in 'docking' branch.\n                       - Fonts: **IMPORTANT** on Font Sizing: Before 1.92, fonts were of a single size. They can now be dynamically sized.\n                         - PushFont() API now has a REQUIRED size parameter.\n                         - Before 1.92: PushFont() always used font \"default\" size specified in AddFont() call. It is equivalent to calling PushFont(font, font->LegacySize).\n                         - Since  1.92: PushFont(font, 0.0f) preserve the current font size which is a shared value.\n                         - To use old behavior: use 'ImGui::PushFont(font, font->LegacySize)' at call site.\n                         - Kept inline single parameter function. Will obsolete.\n                       - Fonts: **IMPORTANT** on Font Merging:\n                         - When searching for a glyph in multiple merged fonts: we search for the FIRST font source which contains the desired glyph.\n                           Because the user doesn't need to provide glyph ranges any more, it is possible that a glyph that you expected to fetch from a secondary/merged icon font may be erroneously fetched from the primary font.\n                         - When searching for a glyph in multiple merged fonts: we now search for the FIRST font source which contains the desired glyph. This is technically a different behavior than before!\n                         - e.g. If you are merging fonts you may have glyphs that you expected to load from Font Source 2 which exists in Font Source 1.\n                           After the update and when using a new backend, those glyphs may now loaded from Font Source 1!\n                         - We added `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to exclude from a given font source:\n                             // Add Font Source 1 but ignore ICON_MIN_FA..ICON_MAX_FA range\n                             static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };\n                             ImFontConfig cfg1;\n                             cfg1.GlyphExcludeRanges = exclude_ranges;\n                             io.Fonts->AddFontFromFileTTF(\"segoeui.ttf\", 0.0f, &cfg1);\n                             // Add Font Source 2, which expects to use the range above\n                             ImFontConfig cfg2;\n                             cfg2.MergeMode = true;\n                             io.Fonts->AddFontFromFileTTF(\"FontAwesome4.ttf\", 0.0f, &cfg2);\n                         - You can use `Metrics/Debugger->Fonts->Font->Input Glyphs Overlap Detection Tool` to see list of glyphs available in multiple font sources. This can facilitate understanding which font input is providing which glyph.\n                       - Fonts: **IMPORTANT** on Thread Safety:\n                          - A few functions such as font->CalcTextSizeA() were, by sheer luck (== accidentally) thread-safe even though we had never provided that guarantee. They are definitively not thread-safe anymore as new glyphs may be loaded.\n                       - Fonts: ImFont::FontSize was removed and does not make sense anymore. ImFont::LegacySize is the size passed to AddFont().\n                       - Fonts: Removed support for PushFont(NULL) which was a shortcut for \"default font\".\n                       - Fonts: Renamed/moved 'io.FontGlobalScale' to 'style.FontScaleMain'.\n                       - Textures: all API functions taking a 'ImTextureID' parameter are now taking a 'ImTextureRef'. Affected functions are: ImGui::Image(), ImGui::ImageWithBg(), ImGui::ImageButton(), ImDrawList::AddImage(), ImDrawList::AddImageQuad(), ImDrawList::AddImageRounded().\n                       - Fonts: obsoleted ImFontAtlas::GetTexDataAsRGBA32(), GetTexDataAsAlpha8(), Build(), SetTexID(), IsBuilt() functions. The new protocol for backends to handle textures doesn't need them. Kept redirection functions (will obsolete).\n                       - Fonts: ImFontConfig::OversampleH/OversampleV default to automatic (== 0) since v1.91.8. It is quite important you keep it automatic until we decide if we want to provide a way to express finer policy, otherwise you will likely waste texture space when using large glyphs. Note that the imgui_freetype backend doesn't use and does not need oversampling.\n                       - Fonts: specifying glyph ranges is now unnecessary. The value of ImFontConfig::GlyphRanges[] is only useful for legacy backends. All GetGlyphRangesXXXX() functions are now marked obsolete: GetGlyphRangesDefault(), GetGlyphRangesGreek(), GetGlyphRangesKorean(), GetGlyphRangesJapanese(), GetGlyphRangesChineseSimplifiedCommon(), GetGlyphRangesChineseFull(), GetGlyphRangesCyrillic(), GetGlyphRangesThai(), GetGlyphRangesVietnamese().\n                       - Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (#327)\n                       - Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one), you'll need to call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't.\n                       - Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using 'PushFont(NULL, style.FontSizeBase * factor)' or to manipulate other scaling factors.\n                       - Fonts: obsoleted ImFont::Scale which is not useful anymore.\n                       - Fonts: generally reworked Internals of ImFontAtlas and ImFont. While in theory a vast majority of users shouldn't be affected, some use cases or extensions might be. Among other things:\n                          - ImDrawCmd::TextureId has been changed to ImDrawCmd::TexRef.\n                          - ImFontAtlas::TexID has been changed to ImFontAtlas::TexRef.\n                          - ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[]\n                          - ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourceCount.\n                          - Each ImFont has a number of ImFontBaked instances corresponding to actively used sizes. ImFont::GetFontBaked(size) retrieves the one for a given size.\n                          - Fields moved from ImFont to ImFontBaked: IndexAdvanceX[], Glyphs[], Ascent, Descent, FindGlyph(), FindGlyphNoFallback(), GetCharAdvance().\n                          - Fields moved from ImFontAtlas to ImFontAtlas->Tex: ImFontAtlas::TexWidth => TexData->Width, ImFontAtlas::TexHeight => TexData->Height, ImFontAtlas::TexPixelsAlpha8/TexPixelsRGBA32 => TexData->GetPixels().\n                          - Widget code may use ImGui::GetFontBaked() instead of ImGui::GetFont() to access font data for current font at current font size (and you may use font->GetFontBaked(size) to access it for any other size.)\n                       - Fonts: (users of imgui_freetype): renamed ImFontAtlas::FontBuilderFlags to ImFontAtlas::FontLoaderFlags. Renamed ImFontConfig::FontBuilderFlags to ImFontConfig::FontLoaderFlags. Renamed ImGuiFreeTypeBuilderFlags to ImGuiFreeTypeLoaderFlags.\n                         If you used runtime imgui_freetype selection rather than the default IMGUI_ENABLE_FREETYPE compile-time option: Renamed/reworked ImFontBuilderIO into ImFontLoader. Renamed ImGuiFreeType::GetBuilderForFreeType() to ImGuiFreeType::GetFontLoader().\n                           - old:  io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()\n                           - new:  io.Fonts->FontLoader = ImGuiFreeType::GetFontLoader()\n                           - new:  io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()) to change dynamically at runtime [from 1.92.1]\n                       - Fonts: (users of custom rectangles, see #8466): Renamed AddCustomRectRegular() to AddCustomRect(). Added GetCustomRect() as a replacement for GetCustomRectByIndex() + CalcCustomRectUV().\n                           - The output type of GetCustomRect() is now ImFontAtlasRect, which include UV coordinates. X->x, Y->y, Width->w, Height->h.\n                           - old:\n                                const ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(custom_rect_id);\n                                ImVec2 uv0, uv1;\n                                atlas->GetCustomRectUV(r, &uv0, &uv1);\n                                ImGui::Image(atlas->TexRef, ImVec2(r->w, r->h), uv0, uv1);\n                           - new;\n                                ImFontAtlasRect r;\n                                atlas->GetCustomRect(custom_rect_id, &r);\n                                ImGui::Image(atlas->TexRef, ImVec2(r.w, r.h), r.uv0, r.uv1);\n                           - We added a redirecting typedef but haven't attempted to magically redirect the field names, as this API is rarely used and the fix is simple.\n                           - Obsoleted AddCustomRectFontGlyph() as the API does not make sense for scalable fonts. Kept existing function which uses the font \"default size\" (Sources[0]->LegacySize). Added a helper AddCustomRectFontGlyphForSize() which is immediately marked obsolete, but can facilitate transitioning old code.\n                           - Prefer adding a font source (ImFontConfig) using a custom/procedural loader.\n                       - DrawList: Renamed ImDrawList::PushTextureID()/PopTextureID() to PushTexture()/PopTexture().\n                       - Backends: removed ImGui_ImplXXXX_CreateFontsTexture()/ImGui_ImplXXXX_DestroyFontsTexture() for all backends that had them. They should not be necessary any more.\n - 2025/05/23 (1.92.0) - Fonts: changed ImFont::CalcWordWrapPositionA() to ImFont::CalcWordWrapPosition()\n                            - old:  const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, ....);\n                            - new:  const char* ImFont::CalcWordWrapPosition (float size,  const char* text, ....);\n                         The leading 'float scale' parameters was changed to 'float size'. This was necessary as 'scale' is assuming standard font size which is a concept we aim to eliminate in an upcoming update. Kept inline redirection function.\n - 2025/05/15 (1.92.0) - TreeNode: renamed ImGuiTreeNodeFlags_NavLeftJumpsBackHere to ImGuiTreeNodeFlags_NavLeftJumpsToParent for clarity. Kept inline redirection enum (will obsolete).\n - 2025/05/15 (1.92.0) - Commented out PushAllowKeyboardFocus()/PopAllowKeyboardFocus() which was obsoleted in 1.89.4. Use PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop)/PopItemFlag() instead. (#3092)\n - 2025/05/15 (1.92.0) - Commented out ImGuiListClipper::ForceDisplayRangeByIndices() which was obsoleted in 1.89.6. Use ImGuiListClipper::IncludeItemsByIndex() instead.\n - 2025/03/05 (1.91.9) - BeginMenu(): Internals: reworked mangling of menu windows to use \"###Menu_00\" etc. instead of \"##Menu_00\", allowing them to also store the menu name before it. This shouldn't affect code unless directly accessing menu window from their mangled name.\n - 2025/04/16 (1.91.9) - Internals: RenderTextEllipsis() function removed the 'float clip_max_x' parameter directly preceding 'float ellipsis_max_x'. Values were identical for a vast majority of users. (#8387)\n - 2025/02/27 (1.91.9) - Image(): removed 'tint_col' and 'border_col' parameter from Image() function. Added ImageWithBg() replacement. (#8131, #8238)\n                            - old: void Image      (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1), ImVec4 tint_col = (1,1,1,1), ImVec4 border_col = (0,0,0,0));\n                            - new: void Image      (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1));\n                            - new: void ImageWithBg(ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1), ImVec4 bg_col = (0,0,0,0), ImVec4 tint_col = (1,1,1,1));\n                            - TL;DR: 'border_col' had misleading side-effect on layout, 'bg_col' was missing, parameter order couldn't be consistent with ImageButton().\n                            - new behavior always use ImGuiCol_Border color + style.ImageBorderSize / ImGuiStyleVar_ImageBorderSize.\n                            - old behavior altered border size (and therefore layout) based on border color's alpha, which caused variety of problems + old behavior a fixed 1.0f for border size which was not tweakable.\n                            - kept legacy signature (will obsolete), which mimics the old behavior,  but uses Max(1.0f, style.ImageBorderSize) when border_col is specified.\n                            - added ImageWithBg() function which has both 'bg_col' (which was missing) and 'tint_col'. It was impossible to add 'bg_col' to Image() with a parameter order consistent with other functions, so we decided to remove 'tint_col' and introduce ImageWithBg().\n - 2025/02/25 (1.91.9) - internals: fonts: ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[]. ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourcesCount.\n - 2025/02/06 (1.91.9) - renamed ImFontConfig::GlyphExtraSpacing.x to ImFontConfig::GlyphExtraAdvanceX.\n - 2025/01/22 (1.91.8) - removed ImGuiColorEditFlags_AlphaPreview (made value 0): it is now the default behavior.\n                         prior to 1.91.8: alpha was made opaque in the preview by default _unless_ using ImGuiColorEditFlags_AlphaPreview. We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior.\n                         the new flags (ImGuiColorEditFlags_AlphaOpaque, ImGuiColorEditFlags_AlphaNoBg + existing ImGuiColorEditFlags_AlphaPreviewHalf) may be combined better and allow finer controls:\n - 2025/01/14 (1.91.7) - renamed ImGuiTreeNodeFlags_SpanTextWidth to ImGuiTreeNodeFlags_SpanLabelWidth for consistency with other names. Kept redirection enum (will obsolete). (#6937)\n - 2024/11/27 (1.91.6) - changed CRC32 table from CRC32-adler to CRC32c polynomial in order to be compatible with the result of SSE 4.2 instructions.\n                         As a result, old .ini data may be partially lost (docking and tables information particularly).\n                         Because some users have crafted and storing .ini data as a way to workaround limitations of the docking API, we are providing a '#define IMGUI_USE_LEGACY_CRC32_ADLER' compile-time option to keep using old CRC32 tables if you cannot afford invalidating old .ini data.\n - 2024/11/06 (1.91.5) - commented/obsoleted out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before)\n                            - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022).\n                            - io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022).\n                            - GetKeyIndex() is removed (obsoleted March 2022). The indirection is now unnecessary.\n                            - pre-1.87 backends are not supported:\n                               - backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields.\n                               - backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields.\n                            - for more reference:\n                              - read 1.87 and 1.88 part of this section or read Changelog for 1.87 and 1.88.\n                              - read https://github.com/ocornut/imgui/issues/4921\n                            - if you have trouble updating a very old codebase using legacy backend-specific key codes: consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest.\n                       - obsoleted ImGuiKey_COUNT (it is unusually error-prone/misleading since valid keys don't start at 0). probably use ImGuiKey_NamedKey_BEGIN/ImGuiKey_NamedKey_END?\n                       - fonts: removed const qualifiers from most font functions in prevision for upcoming font improvements.\n - 2024/10/18 (1.91.4) - renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor (for consistency with newly exposed and reworked features). Kept inline redirection enum (will obsolete).\n - 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.\n                         moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).\n                         kept legacy names (will obsolete) + code that copies settings once the first time. Dynamically changing the old value won't work. Switch to using the new value!\n - 2024/10/10 (1.91.4) - the typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)\n                         this removes the requirement to redefine it for backends which are e.g. storing descriptor sets or other 64-bits structures when building on 32-bits archs. It therefore simplify various building scripts/helpers.\n                         you may have compile-time issues if you were casting to 'void*' instead of 'ImTextureID' when passing your types to functions taking ImTextureID values, e.g. ImGui::Image().\n                         in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning:\n                            - May warn:    ImGui::Image((void*)MyTextureData, ...);\n                            - May warn:    ImGui::Image((void*)(intptr_t)MyTextureData, ...);\n                            - Won't warn:  ImGui::Image((ImTextureID)(intptr_t)MyTextureData, ...);\n  -                      note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like.\n - 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)\n                       - drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).\n                         although unlikely, it you wish to only clamp on text input but want v_min==v_max==0.0f to mean unclamped drags, you can use _ClampOnInput instead of _AlwaysClamp. (#7968, #3361, #76)\n - 2024/09/10 (1.91.2) - internals: using multiple overlaid ButtonBehavior() with same ID will now have io.ConfigDebugHighlightIdConflicts=true feature emit a warning. (#8030)\n                         it was one of the rare case where using same ID is legal. workarounds: (1) use single ButtonBehavior() call with multiple _MouseButton flags, or (2) surround the calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()\n - 2024/08/23 (1.91.1) - renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. kept inline redirection flag.\n - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure:\n                            - io.GetClipboardTextFn         -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData.\n                            - io.SetClipboardTextFn         -> platform_io.Platform_SetClipboardTextFn + same as above line.\n                            - io.PlatformOpenInShellFn      -> platform_io.Platform_OpenInShellFn (#7660)\n                            - io.PlatformSetImeDataFn       -> platform_io.Platform_SetImeDataFn\n                            - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278)\n                            - access those via GetPlatformIO() instead of GetIO().\n                         some were introduced very recently and often automatically setup by core library and backends, so for those we are exceptionally not maintaining a legacy redirection symbol.\n                       - commented the old ImageButton() signature obsoleted in 1.89 (~August 2022). As a reminder:\n                            - old ImageButton() before 1.89 used ImTextureId as item id (created issue with e.g. multiple buttons in same scope, transient texture id values, opaque computation of ID)\n                            - new ImageButton() since 1.89 requires an explicit 'const char* str_id'\n                            - old ImageButton() before 1.89 had frame_padding' override argument.\n                            - new ImageButton() since 1.89 always use style.FramePadding, which you can freely override with PushStyleVar()/PopStyleVar().\n - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info)\n                         you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way.\n                            - instead of:  GetWindowContentRegionMax().x - GetCursorPos().x\n                            - you can use: GetContentRegionAvail().x\n                            - instead of:  GetWindowContentRegionMax().x + GetWindowPos().x\n                            - you can use: GetCursorScreenPos().x + GetContentRegionAvail().x // when called from left edge of window\n                            - instead of:  GetContentRegionMax()\n                            - you can use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in local coordinates\n                            - instead of:  GetWindowContentRegionMax().x - GetWindowContentRegionMin().x\n                            - you can use: GetContentRegionAvail() // when called from left edge of window\n - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573)\n                         (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors)\n - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag().\n - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456)\n                       - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456)\n                            - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc.\n - 2024/07/02 (1.91.0) - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness.\n                            - old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data);\n                            - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);\n - 2024/06/21 (1.90.9) - BeginChild: added ImGuiChildFlags_NavFlattened as a replacement for the window flag ImGuiWindowFlags_NavFlattened: the feature only ever made sense for BeginChild() anyhow.\n                            - old: BeginChild(\"Name\", size, 0, ImGuiWindowFlags_NavFlattened);\n                            - new: BeginChild(\"Name\", size, ImGuiChildFlags_NavFlattened, 0);\n - 2024/06/21 (1.90.9) - io: ClearInputKeys() (first exposed in 1.89.8) doesn't clear mouse data, newly added ClearInputMouse() does.\n - 2024/06/20 (1.90.9) - renamed ImGuiDragDropFlags_SourceAutoExpirePayload to ImGuiDragDropFlags_PayloadAutoExpire.\n - 2024/06/18 (1.90.9) - style: renamed ImGuiCol_TabActive -> ImGuiCol_TabSelected, ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed, ImGuiCol_TabUnfocusedActive -> ImGuiCol_TabDimmedSelected.\n - 2024/06/10 (1.90.9) - removed old nested structure: renaming ImGuiStorage::ImGuiStoragePair type to ImGuiStoragePair (simpler for many languages).\n - 2024/06/06 (1.90.8) - reordered ImGuiInputTextFlags values. This should not be breaking unless you are using generated headers that have values not matching the main library.\n - 2024/06/06 (1.90.8) - removed 'ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft', was mostly unused and misleading.\n - 2024/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022):\n                            - old: CaptureKeyboardFromApp(bool)\n                            - new: SetNextFrameWantCaptureKeyboard(bool)\n                            - old: CaptureMouseFromApp(bool)\n                            - new: SetNextFrameWantCaptureMouse(bool)\n - 2024/05/22 (1.90.7) - inputs (internals): renamed ImGuiKeyOwner_None to ImGuiKeyOwner_NoOwner, to make use more explicit and reduce confusion with the default it is a non-zero value and cannot be the default value (never made public, but disclosing as I expect a few users caught on owner-aware inputs).\n                       - inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest.\n                       - inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures:\n                            - old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);\n                            - new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0);\n                       - inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures.\n                            - old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);\n                            - new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);\n                            - old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);\n                            - new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);\n                         for various reasons those changes makes sense. They are being made because making some of those API public.\n                         only past users of imgui_internal.h with the extra parameters will be affected. Added asserts for valid flags in various functions to detect _some_ misuses, BUT NOT ALL.\n - 2024/05/21 (1.90.7) - docking: changed signature of DockSpaceOverViewport() to add explicit dockspace id if desired. pass 0 to use old behavior. (#7611)\n                           - old: DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...);\n                           - new: DockSpaceOverViewport(ImGuiID dockspace_id = 0, const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...);\n - 2024/05/16 (1.90.7) - inputs: on macOS X, Cmd and Ctrl keys are now automatically swapped by io.AddKeyEvent() as this naturally align with how macOS X uses those keys.\n                           - it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps.\n                           - removed ImGuiMod_Shortcut which was previously dynamically remapping to Ctrl or Cmd/Super. It is now unnecessary to specific cross-platform idiomatic shortcuts. (#2343, #4084, #5923, #456)\n - 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions.\n - 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282)\n                           - old: TreeNode(\"##Hidden\"); SameLine(); Text(\"Hello\");     // <-- This was actually incorrect! BUT appeared to look ok with the default style where ItemSpacing.x == FramePadding.x * 2 (it didn't look aligned otherwise).\n                           - new: TreeNode(\"##Hidden\"); SameLine(0, 0); Text(\"Hello\"); // <-- This is correct for all styles values.\n                         with the fix, IF you were successfully using TreeNode(\"\")+SameLine(); you will now have extra spacing between your TreeNode and the following item.\n                         You'll need to change the SameLine() call to SameLine(0,0) to remove this extraneous spacing. This seemed like the more sensible fix that's not making things less consistent.\n                         (Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth).\n - 2024/03/18 (1.90.5) - merged the radius_x/radius_y parameters in ImDrawList::AddEllipse(), AddEllipseFilled() and PathEllipticalArcTo() into a single ImVec2 parameter. Exceptionally, because those functions were added in 1.90, we are not adding inline redirection functions. The transition is easy and should affect few users. (#2743, #7417)\n - 2024/03/08 (1.90.5) - inputs: more formally obsoleted GetKeyIndex() when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is set. It has been unnecessary and a no-op since 1.87 (it returns the same value as passed when used with a 1.87+ backend using io.AddKeyEvent() function). (#4921)\n                           - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)\n - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.\n - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter.\n - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges.\n - 2023/11/05 (1.90.1) - imgui_freetype: commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. prefer using #define IMGUI_ENABLE_FREETYPE or see commented code for manual calls.\n - 2023/11/05 (1.90.1) - internals,columns: commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted from imgui_internal.h in 1.80.\n - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete).\n - 2023/11/07 (1.90.0) - removed BeginChildFrame()/EndChildFrame() in favor of using BeginChild() with the ImGuiChildFlags_FrameStyle flag. kept inline redirection function (will obsolete).\n                         those functions were merely PushStyle/PopStyle helpers, the removal isn't so much motivated by needing to add the feature in BeginChild(), but by the necessity to avoid BeginChildFrame() signature mismatching BeginChild() signature and features.\n - 2023/11/02 (1.90.0) - BeginChild: upgraded 'bool border = true' parameter to 'ImGuiChildFlags flags' type, added ImGuiChildFlags_Border equivalent. As with our prior \"bool-to-flags\" API updates, the ImGuiChildFlags_Border value is guaranteed to be == true forever to ensure a smoother transition, meaning all existing calls will still work.\n                           - old: BeginChild(\"Name\", size, true)\n                           - new: BeginChild(\"Name\", size, ImGuiChildFlags_Border)\n                           - old: BeginChild(\"Name\", size, false)\n                           - new: BeginChild(\"Name\", size) or BeginChild(\"Name\", 0) or BeginChild(\"Name\", size, ImGuiChildFlags_None)\n                         **AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'**\n - 2023/11/02 (1.90.0) - BeginChild: added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense for BeginChild() anyhow.\n                           - old: BeginChild(\"Name\", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding);\n                           - new: BeginChild(\"Name\", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0);\n - 2023/09/27 (1.90.0) - io: removed io.MetricsActiveAllocations introduced in 1.63. Same as 'g.DebugMemAllocCount - g.DebugMemFreeCount' (still displayed in Metrics, unlikely to be accessed by end-user).\n - 2023/09/26 (1.90.0) - debug tools: Renamed ShowStackToolWindow() (\"Stack Tool\") to ShowIDStackToolWindow() (\"ID Stack Tool\"), as earlier name was misleading. Kept inline redirection function. (#4631)\n - 2023/09/15 (1.90.0) - ListBox, Combo: changed signature of \"name getter\" callback in old one-liner ListBox()/Combo() apis. kept inline redirection function (will obsolete).\n                           - old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...)\n                           - new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);\n                           - old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...);\n                           - new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);\n - 2023/09/08 (1.90.0) - commented out obsolete redirecting functions:\n                           - GetWindowContentRegionWidth()  -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful.\n                           - ImDrawCornerFlags_XXX          -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources.\n                       - commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded() -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details\n - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry!\n - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector<ImDrawList*>. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878)\n - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15).\n - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete).\n - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior.\n - 2023/06/28 (1.89.7) - overlapping items: Selectable and TreeNode don't allow overlap when active so overlapping widgets won't appear as hovered. While this fixes a common small visual issue, it also means that calling IsItemHovered() after a non-reactive elements - e.g. Text() - overlapping an active one may fail if you don't use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem). (#6610)\n - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage.\n - 2023/05/30 (1.89.6) - backends: renamed \"imgui_impl_sdlrenderer.cpp\" to \"imgui_impl_sdlrenderer2.cpp\" and \"imgui_impl_sdlrenderer.h\" to \"imgui_impl_sdlrenderer2.h\". This is in prevision for the future release of SDL3.\n - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:\n                           - ListBoxHeader()  -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)\n                           - ListBoxFooter()  -> use EndListBox()\n - 2023/05/15 (1.89.6) - clipper: commented out obsolete redirection constructor 'ImGuiListClipper(int items_count, float items_height = -1.0f)' that was marked obsolete in 1.79. Use default constructor + clipper.Begin().\n - 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices().\n - 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago:\n                           - ImGuiSliderFlags_ClampOnInput        -> use ImGuiSliderFlags_AlwaysClamp\n                           - ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite\n                           - ImDrawList::AddBezierCurve()         -> use ImDrawList::AddBezierCubic()\n                           - ImDrawList::PathBezierCurveTo()      -> use ImDrawList::PathBezierCubicCurveTo()\n - 2023/03/09 (1.89.4) - renamed PushAllowKeyboardFocus()/PopAllowKeyboardFocus() to PushTabStop()/PopTabStop(). Kept inline redirection functions (will obsolete).\n - 2023/03/09 (1.89.4) - tooltips: Added 'bool' return value to BeginTooltip() for API consistency. Please only submit contents and call EndTooltip() if BeginTooltip() returns true. In reality the function will _currently_ always return true, but further changes down the line may change this, best to clarify API sooner.\n - 2023/02/15 (1.89.4) - moved the optional \"courtesy maths operators\" implementation from imgui_internal.h in imgui.h.\n                         Even though we encourage using your own maths types and operators by setting up IM_VEC2_CLASS_EXTRA,\n                         it has been frequently requested by people to use our own. We had an opt-in define which was\n                         previously fulfilled in imgui_internal.h. It is now fulfilled in imgui.h. (#6164)\n                           - OK:     #define IMGUI_DEFINE_MATH_OPERATORS / #include \"imgui.h\" / #include \"imgui_internal.h\"\n                           - Error:  #include \"imgui.h\" / #define IMGUI_DEFINE_MATH_OPERATORS / #include \"imgui_internal.h\"\n - 2023/02/07 (1.89.3) - backends: renamed \"imgui_impl_sdl.cpp\" to \"imgui_impl_sdl2.cpp\" and \"imgui_impl_sdl.h\" to \"imgui_impl_sdl2.h\". (#6146) This is in prevision for the future release of SDL3.\n - 2022/10/26 (1.89)   - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79.\n - 2022/10/12 (1.89)   - removed runtime patching of invalid \"%f\"/\"%0.f\" format strings for DragInt()/SliderInt(). This was obsoleted in 1.61 (May 2018). See 1.61 changelog for details.\n - 2022/09/26 (1.89)   - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete).\n                           - ImGuiKey_ModCtrl  and ImGuiModFlags_Ctrl  -> ImGuiMod_Ctrl\n                           - ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift\n                           - ImGuiKey_ModAlt   and ImGuiModFlags_Alt   -> ImGuiMod_Alt\n                           - ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super\n                         the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends.\n                         the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions.\n                         exceptionally commenting out the older ImGuiKeyModFlags_XXX names ahead of obsolescence schedule to reduce confusion and because they were not meant to be used anyway.\n - 2022/09/20 (1.89)   - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers.\n                         this will require uses of legacy backend-dependent indices to be casted, e.g.\n                            - with imgui_impl_glfw:  IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A);\n                            - with imgui_impl_win32: IsKeyPressed('A')        -> IsKeyPressed((ImGuiKey)'A')\n                            - etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now!\n - 2022/09/12 (1.89) - removed the bizarre legacy default argument for 'TreePush(const void* ptr = NULL)', always pass a pointer value explicitly. NULL/nullptr is ok but require cast, e.g. TreePush((void*)nullptr);\n - 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020):\n                         - DragScalar(), DragScalarN(), DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.\n                         - SliderScalar(), SliderScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.\n                         - BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags)\n - 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries.\n                       this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item.\n                         - previously this would make the window content size ~200x200:\n                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();\n                         - instead, please submit an item:\n                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();\n                         - alternative:\n                              Begin(...) + Dummy(ImVec2(200,200)) + End();\n                         - content size is now only extended when submitting an item!\n                         - with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert.\n                         - without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it.\n - 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete).\n                        - added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter.\n                        - old signature: bool ImageButton(ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), int frame_padding = -1, ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));\n                          - used the ImTextureID value to create an ID. This was inconsistent with other functions, led to ID conflicts, and caused problems with engines using transient ImTextureID values.\n                          - had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer.\n                        - new signature: bool ImageButton(const char* str_id, ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));\n                          - requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier.\n                          - always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this.\n - 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes).\n                        - Official backends from 1.87+                  -> no issue.\n                        - Official backends from 1.60 to 1.86           -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating!\n                        - Custom backends not writing to io.NavInputs[] -> no issue.\n                        - Custom backends writing to io.NavInputs[]     -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing!\n                        - TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[].\n - 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete).\n - 2022/05/03 (1.88) - backends: osx: removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. All ImGui_ImplOSX_HandleEvent() calls should be removed as they are now unnecessary.\n - 2022/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO.\n - 2022/01/20 (1.87) - inputs: reworded gamepad IO.\n                        - Backend writing to io.NavInputs[]            -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values.\n - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputting text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used).\n - 2022/01/17 (1.87) - inputs: reworked mouse IO.\n                        - Backend writing to io.MousePos               -> backend should call io.AddMousePosEvent()\n                        - Backend writing to io.MouseDown[]            -> backend should call io.AddMouseButtonEvent()\n                        - Backend writing to io.MouseWheel             -> backend should call io.AddMouseWheelEvent()\n                        - Backend writing to io.MouseHoveredViewport   -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]\n                       note: for all calls to IO new functions, the Dear ImGui context should be bound/current.\n                       read https://github.com/ocornut/imgui/issues/4921 for details.\n - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(), ImGui::IsKeyDown(). Removed GetKeyIndex(), now unnecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details.\n                        - IsKeyPressed(MY_NATIVE_KEY_XXX)              -> use IsKeyPressed(ImGuiKey_XXX)\n                        - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX))      -> use IsKeyPressed(ImGuiKey_XXX)\n                        - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to still function with legacy key codes).\n                        - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiMod_XXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiMod_XXX values.*\n                     - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. \"io.KeyMap[ImGuiKey_A] = ImGuiKey_A\") because those values are now larger than the legacy KeyDown[] array. Will assert.\n                     - inputs: added ImGuiKey_ModCtrl/ImGuiKey_ModShift/ImGuiKey_ModAlt/ImGuiKey_ModSuper values to submit keyboard modifiers using io.AddKeyEvent(), instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper.\n - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum.\n - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.\n - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019)\n                        - ImGui::SetNextTreeNodeOpen()        -> use ImGui::SetNextItemOpen()\n                        - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x\n                        - ImGui::TreeAdvanceToLabelPos()      -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());\n                        - ImFontAtlas::CustomRect             -> use ImFontAtlasCustomRect\n                        - ImGuiColorEditFlags_RGB/HSV/HEX     -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex\n - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings\n - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function.\n - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful.\n - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):\n                        - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()\n                        - ImFont::GlyphRangesBuilder  -> use ImFontGlyphRangesBuilder\n - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().\n                        - if you are using official backends from the source tree: you have nothing to do.\n                        - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().\n - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.\n                        - ImDrawCornerFlags_TopLeft  -> use ImDrawFlags_RoundCornersTopLeft\n                        - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight\n                        - ImDrawCornerFlags_None     -> use ImDrawFlags_RoundCornersNone etc.\n                       flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.\n                       breaking: the default with rounding > 0.0f is now \"round all corners\" vs old implicit \"round no corners\":\n                        - rounding == 0.0f + flags == 0 --> meant no rounding  --> unchanged (common use)\n                        - rounding  > 0.0f + flags != 0 --> meant rounding     --> unchanged (common use)\n                        - rounding == 0.0f + flags != 0 --> meant no rounding  --> unchanged (unlikely use)\n                        - rounding  > 0.0f + flags == 0 --> meant no rounding  --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.\n                       this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.\n                       the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify \"round all corners\" and we sometimes encouraged using them as shortcuts.\n                       legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise).\n - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):\n                        - ImGui::SetScrollHere()              -> use ImGui::SetScrollHereY()\n - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing.\n - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() \"bool closed\" parameter to \"ImDrawFlags flags\". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future.\n - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file  with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'.\n - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed.\n - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).\n                     - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).\n                     - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).\n - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags.\n                     - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.\n                     - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.\n - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):\n                        - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().\n                        - ImGuiCol_ModalWindowDarkening       -> use ImGuiCol_ModalWindowDimBg\n                        - ImGuiInputTextCallback              -> use ImGuiTextEditCallback\n                        - ImGuiInputTextCallbackData          -> use ImGuiTextEditCallbackData\n - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).\n - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!\n - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.\n - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures\n - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.\n - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):\n                        - io.RenderDrawListsFn pointer        -> use ImGui::GetDrawData() value and call the render function of your backend\n                        - ImGui::IsAnyWindowFocused()         -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)\n                        - ImGui::IsAnyWindowHovered()         -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)\n                        - ImGuiStyleVar_Count_                -> use ImGuiStyleVar_COUNT\n                        - ImGuiMouseCursor_Count_             -> use ImGuiMouseCursor_COUNT\n                      - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):\n                        - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = \"%.Xf\" where X is your value for decimal_precision.\n                        - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.\n - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed).\n - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).\n - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.\n - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory.\n - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result.\n - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now!\n - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar().\n                       replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).\n                       worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:\n                       - if you omitted the 'power' parameter (likely!), you are not affected.\n                       - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct.\n                       - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f.\n                       see https://github.com/ocornut/imgui/issues/3361 for all details.\n                       kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used.\n                       for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.\n                     - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime.\n - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems.\n - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]\n - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.\n - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().\n - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.\n - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead.\n - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value.\n - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):\n                       - ShowTestWindow()                    -> use ShowDemoWindow()\n                       - IsRootWindowFocused()               -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)\n                       - IsRootWindowOrAnyChildFocused()     -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)\n                       - SetNextWindowContentWidth(w)        -> use SetNextWindowContentSize(ImVec2(w, 0.0f))\n                       - GetItemsLineHeightWithSpacing()     -> use GetFrameHeightWithSpacing()\n                       - ImGuiCol_ChildWindowBg              -> use ImGuiCol_ChildBg\n                       - ImGuiStyleVar_ChildWindowRounding   -> use ImGuiStyleVar_ChildRounding\n                       - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap\n                       - IMGUI_DISABLE_TEST_WINDOWS          -> use IMGUI_DISABLE_DEMO_WINDOWS\n - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API.\n - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it).\n - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert.\n - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.\n - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.\n - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):\n                       - Begin() [old 5 args version]        -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed\n                       - IsRootWindowOrAnyChildHovered()     -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)\n                       - AlignFirstTextHeightToWidgets()     -> use AlignTextToFramePadding()\n                       - SetNextWindowPosCenter()            -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)\n                       - ImFont::Glyph                       -> use ImFontGlyph\n - 2019/10/14 (1.74) - inputs: Fixed a miscalculation in the keyboard/mouse \"typematic\" repeat delay/rate calculation, used by keys and e.g. repeating mouse buttons as well as the GetKeyPressedAmount() function.\n                       if you were using a non-default value for io.KeyRepeatRate (previous default was 0.250), you can add +io.KeyRepeatDelay to it to compensate for the fix.\n                       The function was triggering on: 0.0 and (delay+rate*N) where (N>=1). Fixed formula responds to (N>=0). Effectively it made io.KeyRepeatRate behave like it was set to (io.KeyRepeatRate + io.KeyRepeatDelay).\n                       If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.\n - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).\n - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).\n - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names, or see how they were implemented until 1.71.\n - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have\n                       overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering.\n                       This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows.\n                       Please reach out if you are affected.\n - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).\n - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).\n - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now.\n - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).\n - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).\n - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).\n - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrarily small value!\n - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).\n - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!\n - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).\n - 2018/12/20 (1.67) - made it illegal to call Begin(\"\") with an empty string. This somehow half-worked before but had various undesirable side-effects.\n - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.\n - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.\n - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).\n - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h.\n                       If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.\n - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)\n - 2018/08/31 (1.64) - added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. Re-ordered some of the code remaining in imgui.cpp.\n                       NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.\n                       Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.\n - 2018/08/22 (1.63) - renamed IsItemDeactivatedAfterChange() to IsItemDeactivatedAfterEdit() for consistency with new IsItemEdited() API. Kept redirection function (will obsolete soonish as IsItemDeactivatedAfterChange() is very recent).\n - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).\n - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).\n - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges [update 1.67 renamed to ConfigWindowsResizeFromEdges] to enable the feature.\n - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.\n - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.\n - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).\n - 2018/06/08 (1.62) - examples: the imgui_impl_XXX files have been split to separate platform (Win32, GLFW, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan,  etc.).\n                       old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.\n                       when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.\n                       in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.\n - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.\n - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.\n - 2018/05/03 (1.61) - DragInt(): the default compile-time format string has been changed from \"%.0f\" to \"%d\", as we are not using integers internally any more.\n                       If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.\n                       To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code.\n                       If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. \"DragInt.*%f\" to help you find them.\n - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional \"int decimal_precision\" in favor of an equivalent and more flexible \"const char* format\",\n                       consistent with other functions. Kept redirection functions (will obsolete).\n - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value.\n - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some backend ahead of merging the Nav branch).\n - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.\n - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically.\n - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.\n - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment.\n - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display.\n - 2018/02/07 (1.60) - reorganized context handling to be more explicit,\n                       - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.\n                       - removed Shutdown() function, as DestroyContext() serve this purpose.\n                       - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.\n                       - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.\n                       - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.\n - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths.\n - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).\n - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).\n - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.\n - 2017/12/29 (1.60) - removed CalcItemRectClosestPoint() which was weird and not really used by anyone except demo code. If you need it it's easy to replicate on your side.\n - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).\n - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags\n - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame.\n - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set.\n - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).\n - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).\n                     - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).\n - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).\n - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).\n - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed.\n - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up.\n                       Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions.\n - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.\n - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.\n - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.\n - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);\n - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency.\n - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it.\n - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details.\n                       removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.\n                         IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)\n                         IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)\n                         IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]\n - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!\n - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).\n - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).\n - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete).\n - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your backend if you need to support unavailable mouse, make sure to replace \"io.MousePos = ImVec2(-1,-1)\" with \"io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)\".\n - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!\n                     - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).\n                     - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).\n - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.\n - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an \"ambiguous call\" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicitly to fix.\n - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.\n - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely.\n - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete).\n - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete).\n - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().\n - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu.\n                     - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under \"Color/Picker Widgets\", to understand the various new options.\n                     - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0, 0))'\n - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse\n - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.\n - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.\n - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetID() and use it instead of passing string to BeginChild().\n - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.\n - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.\n - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully, breakage should be minimal.\n - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.\n                       If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.\n                       This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color:\n                       ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); }\n                       If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.\n - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().\n - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.\n - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the \"default_open = true\" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).\n - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.\n - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref GitHub issue #337).\n - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337)\n - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).\n - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert.\n - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you.\n - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.\n - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.\n - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.\n                       GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side.\n                       GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out!\n - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize\n - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project.\n - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason\n - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure.\n                       you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.\n - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost.\n                       this necessary change will break your rendering function! the fix should be very easy. sorry for that :(\n                     - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.\n                     - the signature of the io.RenderDrawListsFn handler has changed!\n                       old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)\n                       new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).\n                         parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'\n                         ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.\n                         ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.\n                     - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer.\n                     - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering!\n                     - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!\n - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.\n - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).\n - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount.\n - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence\n - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry!\n - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).\n - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).\n - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons.\n - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the \"open\" state of a popup. BeginPopup() returns true if the popup is opened.\n - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).\n - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.\n - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API\n - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.\n - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.\n - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.\n - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing\n - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.\n - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)\n - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.\n - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.\n - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.\n - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior\n - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()\n - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)\n - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.\n - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.\n - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.\n                       - old:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];\n                       - new:  unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);\n                       you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation.\n - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID()\n - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)\n - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets\n - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)\n - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)\n - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility\n - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()\n - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly)\n - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)\n - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()\n - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn\n - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)\n - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite\n - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes\n\n\n FREQUENTLY ASKED QUESTIONS (FAQ)\n ================================\n\n Read all answers online:\n   https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)\n Read all answers locally (with a text editor or ideally a Markdown viewer):\n   docs/FAQ.md\n Some answers are copied down here to facilitate searching in code.\n\n Q&A: Basics\n ===========\n\n Q: Where is the documentation?\n A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.\n    - Run the examples/ applications and explore them.\n    - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide.\n    - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.\n    - See pthom's online imgui_explorer (https://pthom.github.io/imgui_explorer) which is a web\n      version of the demo with a source code browser.\n    - The demo covers most features of Dear ImGui, so you can read the code and see its output.\n    - See documentation and comments at the top of imgui.cpp + effectively imgui.h.\n    - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the\n      examples/ folder to explain how to integrate Dear ImGui with your own engine/application.\n    - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.\n    - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.\n    - Your programming IDE is your friend, find the type or function declaration to find comments\n      associated with it.\n\n Q: What is this library called?\n Q: What is the difference between Dear ImGui and traditional UI toolkits?\n Q: Which version should I get?\n >> This library is called \"Dear ImGui\", please don't call it \"ImGui\" :)\n >> See https://www.dearimgui.com/faq for details.\n\n Q&A: Integration\n ================\n\n Q: How to get started?\n A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.\n\n Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?\n A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!\n >> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this.\n\n Q. How can I enable keyboard or gamepad controls?\n Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)\n Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...\n Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...\n Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...\n >> See https://www.dearimgui.com/faq\n\n Q&A: Usage\n ----------\n\n Q: About the ID Stack system..\n   - Why is my widget not reacting when I click on it?\n   - How can I have widgets with an empty label?\n   - How can I have multiple widgets with the same label?\n   - How can I have multiple windows with the same label?\n Q: How can I display an image? What is ImTextureID, how does it work?\n Q: How can I use my own math types instead of ImVec2?\n Q: How can I interact with standard C++ types (such as std::string and std::vector)?\n Q: How can I display custom shapes? (using low-level ImDrawList API)\n >> See https://www.dearimgui.com/faq\n\n Q&A: Fonts, Text\n ================\n\n Q: How should I handle DPI in my application?\n Q: How can I load a different font than the default?\n Q: How can I easily use icons in my application?\n Q: How can I load multiple fonts?\n Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?\n >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md\n\n Q&A: Concerns\n =============\n\n Q: Who uses Dear ImGui?\n Q: Can you create elaborate/serious tools with Dear ImGui?\n Q: Can you reskin the look of Dear ImGui?\n Q: Why using C++ (as opposed to C)?\n >> See https://www.dearimgui.com/faq\n\n Q&A: Community\n ==============\n\n Q: How can I help?\n A: - Businesses: please reach out to \"omar AT dearimgui DOT com\" if you work in a place using Dear ImGui!\n      We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.\n      This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project.\n      >>> See https://github.com/ocornut/imgui/wiki/Funding\n    - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.\n    - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, and see how you want to help and can help!\n    - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.\n      You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.\n      But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.\n    - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on GitHub or privately).\n\n*/\n\n//-------------------------------------------------------------------------\n// [SECTION] INCLUDES\n//-------------------------------------------------------------------------\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#ifndef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS\n#endif\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_internal.h\"\n\n// System includes\n#include <stdio.h>      // vsnprintf, sscanf, printf\n#include <stdint.h>     // intptr_t\n\n// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled\n#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)\n#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS\n#endif\n\n// [Windows] OS specific includes (optional)\n#if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)\n#define IMGUI_DISABLE_WIN32_FUNCTIONS\n#endif\n#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#ifndef NOMINMAX\n#define NOMINMAX\n#endif\n#ifndef __MINGW32__\n#include <Windows.h>        // _wfopen, OpenClipboard\n#else\n#include <windows.h>\n#endif\n#if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_APP) && WINAPI_FAMILY == WINAPI_FAMILY_APP) || (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES))\n// The UWP and GDK Win32 API subsets don't support clipboard nor IME functions\n#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS\n#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS\n#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS\n#endif\n#endif\n\n// [Apple] OS specific includes\n#if defined(__APPLE__)\n#include <TargetConditionals.h>\n#endif\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4127)             // condition expression is constant\n#pragma warning (disable: 4996)             // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#if defined(_MSC_VER) && _MSC_VER >= 1922   // MSVC 2019 16.2 or later\n#pragma warning (disable: 5054)             // operator '|': deprecated between enumerations of different types\n#endif\n#pragma warning (disable: 26451)            // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).\n#pragma warning (disable: 26495)            // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).\n#pragma warning (disable: 26812)            // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast                            // yes, they are more terse.\n#pragma clang diagnostic ignored \"-Wfloat-equal\"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.\n#pragma clang diagnostic ignored \"-Wformat\"                         // warning: format specifies type 'int' but the argument has type 'unsigned int'\n#pragma clang diagnostic ignored \"-Wformat-nonliteral\"              // warning: format string is not a string literal            // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.\n#pragma clang diagnostic ignored \"-Wformat-pedantic\"                // warning: format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.\n#pragma clang diagnostic ignored \"-Wexit-time-destructors\"          // warning: declaration requires an exit-time destructor     // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.\n#pragma clang diagnostic ignored \"-Wglobal-constructors\"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.\n#pragma clang diagnostic ignored \"-Wsign-conversion\"                // warning: implicit conversion changes signedness\n#pragma clang diagnostic ignored \"-Wint-to-void-pointer-cast\"       // warning: cast to 'void *' from smaller integer type 'int'\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#pragma clang diagnostic ignored \"-Wunsafe-buffer-usage\"            // warning: 'xxx' is an unsafe pointer used for buffer access\n#pragma clang diagnostic ignored \"-Wnontrivial-memaccess\"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type\n#pragma clang diagnostic ignored \"-Wswitch-default\"                 // warning: 'switch' missing 'default' label\n#elif defined(__GNUC__)\n// We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.\n#pragma GCC diagnostic ignored \"-Wpragmas\"                          // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wunused-function\"                  // warning: 'xxxx' defined but not used\n#pragma GCC diagnostic ignored \"-Wint-to-pointer-cast\"              // warning: cast to pointer from integer of different size\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"                      // warning: comparing floating-point with '==' or '!=' is unsafe\n#pragma GCC diagnostic ignored \"-Wformat\"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'\n#pragma GCC diagnostic ignored \"-Wdouble-promotion\"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function\n#pragma GCC diagnostic ignored \"-Wconversion\"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value\n#pragma GCC diagnostic ignored \"-Wformat-nonliteral\"                // warning: format not a string literal, format string not checked\n#pragma GCC diagnostic ignored \"-Wstrict-overflow\"                  // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#pragma GCC diagnostic ignored \"-Wcast-qual\"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"                  // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result\n#endif\n\n// Debug options\n#define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Hold Ctrl to display for all candidates. Ctrl+Arrow to change last direction.\n#define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window\n\n// Default font size if unspecified in both style.FontSizeBase and AddFontXXX() calls.\nstatic const float FONT_DEFAULT_SIZE_BASE = 20.0f;\n\n// When using Ctrl+Tab (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch.\nstatic const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in\nstatic const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear\nstatic const float NAV_ACTIVATE_HIGHLIGHT_TIMER             = 0.10f;    // Time to highlight an item activated by a shortcut.\nstatic const float NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY    = 0.60f;    // Time to hold activation button (e.g. FaceDown) to turn the activation into a text input.\nstatic const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.\nstatic const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER    = 0.70f;    // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.\n\n// Tooltip offset\nstatic const ImVec2 TOOLTIP_DEFAULT_OFFSET_MOUSE = ImVec2(16, 10);      // Multiplied by g.Style.MouseCursorScale\nstatic const ImVec2 TOOLTIP_DEFAULT_OFFSET_TOUCH = ImVec2(0, -20);      // Multiplied by g.Style.MouseCursorScale\nstatic const ImVec2 TOOLTIP_DEFAULT_PIVOT_TOUCH = ImVec2(0.5f, 1.0f);   // Multiplied by g.Style.MouseCursorScale\n\n// Docking\nstatic const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA        = 0.50f;    // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport.\n\n//-------------------------------------------------------------------------\n// [SECTION] FORWARD DECLARATIONS\n//-------------------------------------------------------------------------\n\nstatic void             SetCurrentWindow(ImGuiWindow* window);\nstatic ImGuiWindow*     CreateNewWindow(const char* name, ImGuiWindowFlags flags);\nstatic ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);\n\nstatic void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);\n\n// Settings\nstatic void             WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);\nstatic void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);\nstatic void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);\nstatic void             WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);\nstatic void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);\n\n// Platform Dependents default implementation for ImGuiPlatformIO functions\nstatic const char*      Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx);\nstatic void             Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text);\nstatic void             Platform_SetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);\nstatic bool             Platform_OpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path);\n\nnamespace ImGui\n{\n// Item\nstatic void             ItemHandleShortcut(ImGuiID id);\n\n// Window Focus\nstatic int              FindWindowFocusIndex(ImGuiWindow* window);\nstatic void             UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags);\n\n// Navigation\nstatic void             NavUpdate();\nstatic void             NavUpdateWindowing();\nstatic void             NavUpdateWindowingApplyFocus(ImGuiWindow* window);\nstatic void             NavUpdateWindowingOverlay();\nstatic void             NavUpdateCancelRequest();\nstatic void             NavUpdateContextMenuRequest();\nstatic void             NavUpdateCreateMoveRequest();\nstatic void             NavUpdateCreateTabbingRequest();\nstatic float            NavUpdatePageUpPageDown();\nstatic inline void      NavUpdateAnyRequestFlag();\nstatic void             NavUpdateCreateWrappingRequest();\nstatic void             NavEndFrame();\nstatic bool             NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb);\nstatic void             NavApplyItemToResult(ImGuiNavItemData* result);\nstatic void             NavProcessItem();\nstatic void             NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);\nstatic ImGuiInputSource NavCalcPreferredRefPosSource(ImGuiWindowFlags window_type);\nstatic ImVec2           NavCalcPreferredRefPos(ImGuiWindowFlags window_type);\nstatic void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);\nstatic ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);\nstatic void             NavRestoreLayer(ImGuiNavLayer layer);\n\n// Error Checking and Debug Tools\nstatic void             ErrorCheckNewFrameSanityChecks();\nstatic void             ErrorCheckEndFrameSanityChecks();\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\nstatic void             UpdateDebugToolItemPicker();\nstatic void             UpdateDebugToolItemPathQuery();\nstatic void             UpdateDebugToolFlashStyleColor();\n#endif\n\n// Inputs\nstatic void             UpdateKeyboardInputs();\nstatic void             UpdateMouseInputs();\nstatic void             UpdateMouseWheel();\nstatic void             UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt);\n\n// Misc\nstatic void             UpdateFontsNewFrame();\nstatic void             UpdateFontsEndFrame();\nstatic void             UpdateTexturesNewFrame();\nstatic void             UpdateTexturesEndFrame();\nstatic void             UpdateSettings();\nstatic int              UpdateWindowManualResize(ImGuiWindow* window, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);\nstatic void             RenderWindowShadow(ImGuiWindow* window);\nstatic void             RenderWindowOuterBorders(ImGuiWindow* window);\nstatic void             RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);\nstatic void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);\nstatic void             RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);\nstatic void             RenderDimmedBackgrounds();\nstatic void             SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect);\nstatic void             SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect);\n\n// Viewports\nconst ImGuiID           IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr(\"ViewportDefault\", 0); so it's easier to spot in the debugger. The exact value doesn't matter.\nstatic ImGuiViewportP*  AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags);\nstatic void             DestroyViewport(ImGuiViewportP* viewport);\nstatic void             UpdateViewportsNewFrame();\nstatic void             UpdateViewportsEndFrame();\nstatic void             WindowSelectViewport(ImGuiWindow* window);\nstatic void             WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack);\nstatic bool             UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport);\nstatic bool             UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window);\nstatic bool             GetWindowAlwaysWantOwnViewport(ImGuiWindow* window);\nstatic int              FindPlatformMonitorForPos(const ImVec2& pos);\nstatic int              FindPlatformMonitorForRect(const ImRect& r);\nstatic void             UpdateViewportPlatformMonitor(ImGuiViewportP* viewport);\n\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] CONTEXT AND MEMORY ALLOCATORS\n//-----------------------------------------------------------------------------\n\n// DLL users:\n// - Heaps and globals are not shared across DLL boundaries!\n// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.\n// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).\n// - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.\n// - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in).\n\n// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.\n// - ImGui::CreateContext() will automatically set this pointer if it is NULL.\n//   Change to a different context by calling ImGui::SetCurrentContext().\n// - Important: Dear ImGui functions are not thread-safe because of this pointer.\n//   If you want thread-safety to allow N threads to access N different contexts:\n//   - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:\n//         struct ImGuiContext;\n//         extern thread_local ImGuiContext* MyImGuiTLS;\n//         #define GImGui MyImGuiTLS\n//     And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.\n//   - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586\n//   - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.\n// - DLL users: read comments above.\n#ifndef GImGui\nImGuiContext*   GImGui = NULL;\n#endif\n\n// Memory Allocator functions. Use SetAllocatorFunctions() to change them.\n// - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.\n// - DLL users: read comments above.\n#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS\nstatic void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }\nstatic void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }\n#else\nstatic void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }\nstatic void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }\n#endif\nstatic ImGuiMemAllocFunc    GImAllocatorAllocFunc = MallocWrapper;\nstatic ImGuiMemFreeFunc     GImAllocatorFreeFunc = FreeWrapper;\nstatic void*                GImAllocatorUserData = NULL;\n\n//-----------------------------------------------------------------------------\n// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)\n//-----------------------------------------------------------------------------\n\nImGuiStyle::ImGuiStyle()\n{\n    FontSizeBase                = 0.0f;             // Will default to io.Fonts->Fonts[0] on first frame.\n    FontScaleMain               = 1.0f;             // Main scale factor. May be set by application once, or exposed to end-user.\n    FontScaleDpi                = 1.0f;             // Additional scale factor from viewport/monitor contents scale. When io.ConfigDpiScaleFonts is enabled, this is automatically overwritten when changing monitor DPI.\n\n    Alpha                       = 1.0f;             // Global alpha applies to everything in Dear ImGui.\n    DisabledAlpha               = 0.60f;            // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.\n    WindowPadding               = ImVec2(8,8);      // Padding within a window\n    WindowRounding              = 0.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.\n    WindowBorderSize            = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.\n    WindowBorderHoverPadding    = 4.0f;             // Hit-testing extent outside/inside resizing border. Also extend determination of hovered window. Generally meaningfully larger than WindowBorderSize to make it easy to reach borders.\n    WindowMinSize               = ImVec2(32,32);    // Minimum window size\n    WindowTitleAlign            = ImVec2(0.0f,0.5f);// Alignment for title bar text\n    WindowMenuButtonPosition    = ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.\n    ChildRounding               = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows\n    ChildBorderSize             = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.\n    PopupRounding               = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows\n    PopupBorderSize             = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.\n    FramePadding                = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)\n    FrameRounding               = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).\n    FrameBorderSize             = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.\n    ItemSpacing                 = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines\n    ItemInnerSpacing            = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)\n    CellPadding                 = ImVec2(4,2);      // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.\n    TouchExtraPadding           = ImVec2(0,0);      // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!\n    IndentSpacing               = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).\n    ColumnsMinSpacing           = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).\n    ScrollbarSize               = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar\n    ScrollbarRounding           = 9.0f;             // Radius of grab corners rounding for scrollbar\n    ScrollbarPadding            = 2.0f;             // Padding of scrollbar grab within its frame (same for both axes)\n    GrabMinSize                 = 12.0f;            // Minimum width/height of a grab box for slider/scrollbar\n    GrabRounding                = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.\n    LogSliderDeadzone           = 4.0f;             // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.\n    ImageRounding               = 0.0f;             // Rounding of Image() calls.\n    ImageBorderSize             = 0.0f;             // Thickness of border around tabs.\n    TabRounding                 = 5.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.\n    TabBorderSize               = 0.0f;             // Thickness of border around tabs.\n    TabMinWidthBase             = 1.0f;             // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected.\n    TabMinWidthShrink           = 80.0f;            // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy.\n    TabCloseButtonMinWidthSelected   = -1.0f;       // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width.\n    TabCloseButtonMinWidthUnselected = 0.0f;        // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected.\n    TabBarBorderSize            = 1.0f;             // Thickness of tab-bar separator, which takes on the tab active color to denote focus.\n    TabBarOverlineSize          = 1.0f;             // Thickness of tab-bar overline, which highlights the selected tab-bar.\n    TableAngledHeadersAngle     = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).\n    TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell\n    TreeLinesFlags              = ImGuiTreeNodeFlags_DrawLinesNone;\n    TreeLinesSize               = 1.0f;             // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.\n    TreeLinesRounding           = 0.0f;             // Radius of lines connecting child nodes to the vertical line.\n    DragDropTargetRounding      = 0.0f;             // Radius of the drag and drop target frame.\n    DragDropTargetBorderSize    = 2.0f;             // Thickness of the drag and drop target border.\n    DragDropTargetPadding       = 3.0f;             // Size to expand the drag and drop target from actual target item size.\n    ColorMarkerSize             = 3.0f;             // Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags_ColorMarkers.\n    ColorButtonPosition         = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.\n    ButtonTextAlign             = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.\n    SelectableTextAlign         = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.\n    SeparatorSize               = 1.0f;             // Thickness of border in Separator().\n    SeparatorTextBorderSize     = 3.0f;             // Thickness of border in SeparatorText().\n    SeparatorTextAlign          = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).\n    SeparatorTextPadding        = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.\n    DisplayWindowPadding        = ImVec2(19,19);    // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.\n    DisplaySafeAreaPadding      = ImVec2(3,3);      // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.\n    DockingNodeHasCloseButton   = true;             // Docking nodes have their own CloseButton() to close all docked windows.\n    DockingSeparatorSize        = 2.0f;             // Thickness of resizing border between docked windows\n    MouseCursorScale            = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.\n    AntiAliasedLines            = true;             // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.\n    AntiAliasedLinesUseTex      = true;             // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).\n    AntiAliasedFill             = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).\n    CurveTessellationTol        = 1.25f;            // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.\n    CircleTessellationMaxError  = 0.30f;            // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.\n    WindowShadowSize            = 100.0f;           // Size (in pixels) of window shadows.\n    WindowShadowOffsetDist      = 0.0f;             // Offset distance (in pixels) of window shadows from casting window.\n    WindowShadowOffsetAngle     = IM_PI * 0.25f;    // Offset angle of window shadows from casting window (0.0f = left, 0.5f*PI = bottom, 1.0f*PI = right, 1.5f*PI = top).\n\n    // Behaviors\n    HoverStationaryDelay        = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.\n    HoverDelayShort             = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.\n    HoverDelayNormal            = 0.40f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). \"\n    HoverFlagsForTooltipMouse   = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled;    // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.\n    HoverFlagsForTooltipNav     = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled;  // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.\n\n    // [Internal]\n    _MainScale                  = 1.0f;\n    _NextFrameFontSizeBase      = 0.0f;\n\n    // Default theme\n    ImGui::StyleColorsDark(this);\n}\n\n\n// Scale all spacing/padding/thickness values. Do not scale fonts.\n// Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.\nvoid ImGuiStyle::ScaleAllSizes(float scale_factor)\n{\n    _MainScale *= scale_factor;\n    WindowPadding = ImTrunc(WindowPadding * scale_factor);\n    WindowRounding = ImTrunc(WindowRounding * scale_factor);\n    WindowBorderSize = ImTrunc(WindowBorderSize * scale_factor);\n    WindowMinSize = ImTrunc(WindowMinSize * scale_factor);\n    WindowBorderHoverPadding = ImTrunc(WindowBorderHoverPadding * scale_factor);\n    ChildRounding = ImTrunc(ChildRounding * scale_factor);\n    ChildBorderSize = ImTrunc(ChildBorderSize * scale_factor);\n    PopupRounding = ImTrunc(PopupRounding * scale_factor);\n    PopupBorderSize = ImTrunc(PopupBorderSize * scale_factor);\n    FramePadding = ImTrunc(FramePadding * scale_factor);\n    FrameBorderSize = ImTrunc(FrameBorderSize * scale_factor);\n    FrameRounding = ImTrunc(FrameRounding * scale_factor);\n    ItemSpacing = ImTrunc(ItemSpacing * scale_factor);\n    ItemInnerSpacing = ImTrunc(ItemInnerSpacing * scale_factor);\n    CellPadding = ImTrunc(CellPadding * scale_factor);\n    TouchExtraPadding = ImTrunc(TouchExtraPadding * scale_factor);\n    IndentSpacing = ImTrunc(IndentSpacing * scale_factor);\n    ColumnsMinSpacing = ImTrunc(ColumnsMinSpacing * scale_factor);\n    ScrollbarSize = ImTrunc(ScrollbarSize * scale_factor);\n    ScrollbarRounding = ImTrunc(ScrollbarRounding * scale_factor);\n    ScrollbarPadding = ImTrunc(ScrollbarPadding * scale_factor);\n    GrabMinSize = ImTrunc(GrabMinSize * scale_factor);\n    GrabRounding = ImTrunc(GrabRounding * scale_factor);\n    LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor);\n    ImageRounding = ImTrunc(ImageRounding * scale_factor);\n    ImageBorderSize = ImTrunc(ImageBorderSize * scale_factor);\n    TabRounding = ImTrunc(TabRounding * scale_factor);\n    TabBorderSize = ImTrunc(TabBorderSize * scale_factor);\n    TabMinWidthBase = ImTrunc(TabMinWidthBase * scale_factor);\n    TabMinWidthShrink = ImTrunc(TabMinWidthShrink * scale_factor);\n    TabCloseButtonMinWidthSelected = (TabCloseButtonMinWidthSelected > 0.0f && TabCloseButtonMinWidthSelected != FLT_MAX) ? ImTrunc(TabCloseButtonMinWidthSelected * scale_factor) : TabCloseButtonMinWidthSelected;\n    TabCloseButtonMinWidthUnselected = (TabCloseButtonMinWidthUnselected > 0.0f && TabCloseButtonMinWidthUnselected != FLT_MAX) ? ImTrunc(TabCloseButtonMinWidthUnselected * scale_factor) : TabCloseButtonMinWidthUnselected;\n    TabBarBorderSize = ImTrunc(TabBarBorderSize * scale_factor);\n    TabBarOverlineSize = ImTrunc(TabBarOverlineSize * scale_factor);\n    TreeLinesSize = ImTrunc(TreeLinesSize * scale_factor);\n    TreeLinesRounding = ImTrunc(TreeLinesRounding * scale_factor);\n    DragDropTargetRounding = ImTrunc(DragDropTargetRounding * scale_factor);\n    DragDropTargetBorderSize = ImTrunc(DragDropTargetBorderSize * scale_factor);\n    DragDropTargetPadding = ImTrunc(DragDropTargetPadding * scale_factor);\n    ColorMarkerSize = ImTrunc(ColorMarkerSize * scale_factor);\n    SeparatorSize = ImTrunc(SeparatorSize * scale_factor);\n    SeparatorTextBorderSize = ImTrunc(SeparatorTextBorderSize * scale_factor);\n    SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor);\n    DockingSeparatorSize = ImTrunc(DockingSeparatorSize * scale_factor);\n    DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor);\n    DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor);\n    MouseCursorScale = ImTrunc(MouseCursorScale * scale_factor);\n}\n\nImGuiIO::ImGuiIO()\n{\n    // Most fields are initialized with zero\n    memset((void*)this, 0, sizeof(*this));\n    IM_STATIC_ASSERT(IM_COUNTOF(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_COUNTOF(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT);\n\n    // Settings\n    ConfigFlags = ImGuiConfigFlags_None;\n    BackendFlags = ImGuiBackendFlags_None;\n    DisplaySize = ImVec2(-1.0f, -1.0f);\n    DeltaTime = 1.0f / 60.0f;\n    IniSavingRate = 5.0f;\n    IniFilename = \"imgui.ini\"; // Important: \"imgui.ini\" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables).\n    LogFilename = \"imgui_log.txt\";\n    UserData = NULL;\n\n    Fonts = NULL;\n    FontDefault = NULL;\n    FontAllowUserScaling = false;\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    FontGlobalScale = 1.0f; // Use style.FontScaleMain instead!\n#endif\n    DisplayFramebufferScale = ImVec2(1.0f, 1.0f);\n\n    // Keyboard/Gamepad Navigation options\n    ConfigNavSwapGamepadButtons = false;\n    ConfigNavMoveSetMousePos = false;\n    ConfigNavCaptureKeyboard = true;\n    ConfigNavEscapeClearFocusItem = true;\n    ConfigNavEscapeClearFocusWindow = false;\n    ConfigNavCursorVisibleAuto = true;\n    ConfigNavCursorVisibleAlways = false;\n\n    // Docking options (when ImGuiConfigFlags_DockingEnable is set)\n    ConfigDockingNoSplit = false;\n    ConfigDockingNoDockingOver = false;\n    ConfigDockingWithShift = false;\n    ConfigDockingAlwaysTabBar = false;\n    ConfigDockingTransparentPayload = false;\n\n    // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set)\n    ConfigViewportsNoAutoMerge = false;\n    ConfigViewportsNoTaskBarIcon = false;\n    ConfigViewportsNoDecoration = true;\n    ConfigViewportsNoDefaultParent = true;\n    ConfigViewportsPlatformFocusSetsImGuiFocus = true;\n\n    // Miscellaneous options\n    MouseDrawCursor = false;\n#ifdef __APPLE__\n    ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag\n#else\n    ConfigMacOSXBehaviors = false;\n#endif\n    ConfigInputTrickleEventQueue = true;\n    ConfigInputTextCursorBlink = true;\n    ConfigInputTextEnterKeepActive = false;\n    ConfigDragClickToInputText = false;\n    ConfigWindowsResizeFromEdges = true;\n    ConfigWindowsMoveFromTitleBarOnly = false;\n    ConfigWindowsCopyContentsWithCtrlC = false;\n    ConfigScrollbarScrollByPage = true;\n    ConfigMemoryCompactTimer = 60.0f;\n    ConfigDebugIsDebuggerPresent = false;\n    ConfigDebugHighlightIdConflicts = true;\n    ConfigDebugHighlightIdConflictsShowItemPicker = true;\n    ConfigDebugBeginReturnValueOnce = false;\n    ConfigDebugBeginReturnValueLoop = false;\n\n    ConfigErrorRecovery = true;\n    ConfigErrorRecoveryEnableAssert = true;\n    ConfigErrorRecoveryEnableDebugLog = true;\n    ConfigErrorRecoveryEnableTooltip = true;\n\n    // Inputs Behaviors\n    MouseDoubleClickTime = 0.30f;\n    MouseDoubleClickMaxDist = 6.0f;\n    MouseDragThreshold = 6.0f;\n    KeyRepeatDelay = 0.275f;\n    KeyRepeatRate = 0.050f;\n\n    // Platform Functions\n    // Note: Initialize() will setup default clipboard/ime handlers.\n    BackendPlatformName = BackendRendererName = NULL;\n    BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;\n\n    // Input (NB: we already have memset zero the entire structure!)\n    MousePos = ImVec2(-FLT_MAX, -FLT_MAX);\n    MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);\n    MouseSource = ImGuiMouseSource_Mouse;\n    for (int i = 0; i < IM_COUNTOF(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;\n    for (int i = 0; i < IM_COUNTOF(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }\n    AppAcceptingEvents = true;\n}\n\n// Pass in translated ASCII characters for text input.\n// - with glfw you can get those from the callback set in glfwSetCharCallback()\n// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message\n// FIXME: Should in theory be called \"AddCharacterEvent()\" to be consistent with new API\nvoid ImGuiIO::AddInputCharacter(unsigned int c)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n    if (c == 0 || !AppAcceptingEvents)\n        return;\n\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_Text;\n    e.Source = ImGuiInputSource_Keyboard;\n    e.EventId = g.InputEventsNextEventId++;\n    e.Text.Char = c;\n    g.InputEventsQueue.push_back(e);\n}\n\n// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so\n// we should save the high surrogate.\nvoid ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)\n{\n    if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents)\n        return;\n\n    if ((c & 0xFC00) == 0xD800) // High surrogate, must save\n    {\n        if (InputQueueSurrogate != 0)\n            AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);\n        InputQueueSurrogate = c;\n        return;\n    }\n\n    ImWchar cp = c;\n    if (InputQueueSurrogate != 0)\n    {\n        if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate\n        {\n            AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);\n        }\n        else\n        {\n#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF\n            cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar\n#else\n            cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);\n#endif\n        }\n\n        InputQueueSurrogate = 0;\n    }\n    AddInputCharacter((unsigned)cp);\n}\n\nvoid ImGuiIO::AddInputCharactersUTF8(const char* str)\n{\n    if (!AppAcceptingEvents)\n        return;\n    const char* str_end = str + strlen(str);\n    while (*str != 0)\n    {\n        unsigned int c = 0;\n        str += ImTextCharFromUtf8(&c, str, str_end);\n        AddInputCharacter(c);\n    }\n}\n\n// Clear all incoming events.\nvoid ImGuiIO::ClearEventsQueue()\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n    g.InputEventsQueue.clear();\n}\n\n// Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.\nvoid ImGuiIO::ClearInputKeys()\n{\n    ImGuiContext& g = *Ctx;\n    for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)\n    {\n        if (ImGui::IsMouseKey((ImGuiKey)key))\n            continue;\n        ImGuiKeyData* key_data = &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];\n        key_data->Down = false;\n        key_data->DownDuration = -1.0f;\n        key_data->DownDurationPrev = -1.0f;\n    }\n    KeyCtrl = KeyShift = KeyAlt = KeySuper = false;\n    KeyMods = ImGuiMod_None;\n    InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters().\n}\n\nvoid ImGuiIO::ClearInputMouse()\n{\n    for (ImGuiKey key = ImGuiKey_Mouse_BEGIN; key < ImGuiKey_Mouse_END; key = (ImGuiKey)(key + 1))\n    {\n        ImGuiKeyData* key_data = &KeysData[key - ImGuiKey_NamedKey_BEGIN];\n        key_data->Down = false;\n        key_data->DownDuration = -1.0f;\n        key_data->DownDurationPrev = -1.0f;\n    }\n    MousePos = ImVec2(-FLT_MAX, -FLT_MAX);\n    for (int n = 0; n < IM_COUNTOF(MouseDown); n++)\n    {\n        MouseDown[n] = false;\n        MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f;\n    }\n    MouseWheel = MouseWheelH = 0.0f;\n}\n\nstatic ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1)\n{\n    ImGuiContext& g = *ctx;\n    for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--)\n    {\n        ImGuiInputEvent* e = &g.InputEventsQueue[n];\n        if (e->Type != type)\n            continue;\n        if (type == ImGuiInputEventType_Key && e->Key.Key != arg)\n            continue;\n        if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg)\n            continue;\n        return e;\n    }\n    return NULL;\n}\n\n// Queue a new key down/up event.\n// - ImGuiKey key:       Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)\n// - bool down:          Is the key down? use false to signify a key release.\n// - float analog_value: 0.0f..1.0f\n// IMPORTANT: THIS FUNCTION AND OTHER \"ADD\" GRABS THE CONTEXT FROM OUR INSTANCE.\n// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.\nvoid ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)\n{\n    //if (e->Down) { IMGUI_DEBUG_LOG_IO(\"AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\\n\", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); }\n    IM_ASSERT(Ctx != NULL);\n    if (key == ImGuiKey_None || !AppAcceptingEvents)\n        return;\n    ImGuiContext& g = *Ctx;\n    IM_ASSERT(ImGui::IsNamedKeyOrMod(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API.\n    IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.\n\n    // MacOS: swap Cmd(Super) and Ctrl\n    if (g.IO.ConfigMacOSXBehaviors)\n    {\n        if (key == ImGuiMod_Super)          { key = ImGuiMod_Ctrl; }\n        else if (key == ImGuiMod_Ctrl)      { key = ImGuiMod_Super; }\n        else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; }\n        else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; }\n        else if (key == ImGuiKey_LeftCtrl)  { key = ImGuiKey_LeftSuper; }\n        else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; }\n    }\n\n    // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)\n    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key);\n    const ImGuiKeyData* key_data = ImGui::GetKeyData(&g, key);\n    const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down;\n    const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue;\n    if (latest_key_down == down && latest_key_analog == analog_value)\n        return;\n\n    // Add event\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_Key;\n    e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard;\n    e.EventId = g.InputEventsNextEventId++;\n    e.Key.Key = key;\n    e.Key.Down = down;\n    e.Key.AnalogValue = analog_value;\n    g.InputEventsQueue.push_back(e);\n}\n\nvoid ImGuiIO::AddKeyEvent(ImGuiKey key, bool down)\n{\n    if (!AppAcceptingEvents)\n        return;\n    AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f);\n}\n\n// [Optional] Call after AddKeyEvent().\n// Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices.\n// If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this.\nvoid ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index)\n{\n    if (key == ImGuiKey_None)\n        return;\n    IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512\n    IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511\n    IM_UNUSED(key);                 // Yet unused\n    IM_UNUSED(native_keycode);      // Yet unused\n    IM_UNUSED(native_scancode);     // Yet unused\n    IM_UNUSED(native_legacy_index); // Yet unused\n}\n\n// Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.\nvoid ImGuiIO::SetAppAcceptingEvents(bool accepting_events)\n{\n    AppAcceptingEvents = accepting_events;\n}\n\n// Queue a mouse move event\nvoid ImGuiIO::AddMousePosEvent(float x, float y)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n    if (!AppAcceptingEvents)\n        return;\n\n    // Apply same flooring as UpdateMouseInputs()\n    ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y);\n\n    // Filter duplicate\n    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos);\n    const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos;\n    if (latest_pos.x == pos.x && latest_pos.y == pos.y)\n        return;\n\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_MousePos;\n    e.Source = ImGuiInputSource_Mouse;\n    e.EventId = g.InputEventsNextEventId++;\n    e.MousePos.PosX = pos.x;\n    e.MousePos.PosY = pos.y;\n    e.MousePos.MouseSource = g.InputEventsNextMouseSource;\n    g.InputEventsQueue.push_back(e);\n}\n\nvoid ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n    IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);\n    if (!AppAcceptingEvents)\n        return;\n\n    // On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button.\n    if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick)\n    {\n        // Order of both statements matters: this event will still release mouse button 1\n        mouse_button = 1;\n        if (!down)\n            MouseCtrlLeftAsRightClick = false;\n    }\n\n    // Filter duplicate\n    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button);\n    const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];\n    if (latest_button_down == down)\n        return;\n\n    // On MacOS X: Convert Ctrl(Super)+Left click into Right-click.\n    // - Note that this is actual physical Ctrl which is ImGuiMod_Super for us.\n    // - At this point we want from !down to down, so this is handling the initial press.\n    if (ConfigMacOSXBehaviors && mouse_button == 0 && down)\n    {\n        const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)ImGuiMod_Super);\n        if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper)\n        {\n            IMGUI_DEBUG_LOG_IO(\"[io] Super+Left Click aliased into Right Click\\n\");\n            MouseCtrlLeftAsRightClick = true;\n            AddMouseButtonEvent(1, true); // This is just quicker to write that passing through, as we need to filter duplicate again.\n            return;\n        }\n    }\n\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_MouseButton;\n    e.Source = ImGuiInputSource_Mouse;\n    e.EventId = g.InputEventsNextEventId++;\n    e.MouseButton.Button = mouse_button;\n    e.MouseButton.Down = down;\n    e.MouseButton.MouseSource = g.InputEventsNextMouseSource;\n    g.InputEventsQueue.push_back(e);\n}\n\n// Queue a mouse wheel event (some mouse/API may only have a Y component)\nvoid ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n\n    // Filter duplicate (unlike most events, wheel values are relative and easy to filter)\n    if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f))\n        return;\n\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_MouseWheel;\n    e.Source = ImGuiInputSource_Mouse;\n    e.EventId = g.InputEventsNextEventId++;\n    e.MouseWheel.WheelX = wheel_x;\n    e.MouseWheel.WheelY = wheel_y;\n    e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;\n    g.InputEventsQueue.push_back(e);\n}\n\n// This is not a real event, the data is latched in order to be stored in actual Mouse events.\n// This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes.\nvoid ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n    g.InputEventsNextMouseSource = source;\n}\n\nvoid ImGuiIO::AddMouseViewportEvent(ImGuiID viewport_id)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n    //IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport);\n    if (!AppAcceptingEvents)\n        return;\n\n    // Filter duplicate\n    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseViewport);\n    const ImGuiID latest_viewport_id = latest_event ? latest_event->MouseViewport.HoveredViewportID : g.IO.MouseHoveredViewport;\n    if (latest_viewport_id == viewport_id)\n        return;\n\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_MouseViewport;\n    e.Source = ImGuiInputSource_Mouse;\n    e.MouseViewport.HoveredViewportID = viewport_id;\n    g.InputEventsQueue.push_back(e);\n}\n\nvoid ImGuiIO::AddFocusEvent(bool focused)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n\n    // Filter duplicate\n    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Focus);\n    const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost;\n    if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused))\n        return;\n\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_Focus;\n    e.EventId = g.InputEventsNextEventId++;\n    e.AppFocused.Focused = focused;\n    g.InputEventsQueue.push_back(e);\n}\n\nImGuiPlatformIO::ImGuiPlatformIO()\n{\n    // Most fields are initialized with zero\n    memset((void*)this, 0, sizeof(*this));\n    Platform_LocaleDecimalPoint = '.';\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)\n//-----------------------------------------------------------------------------\n\nImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)\n{\n    IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()\n    ImVec2 p_last = p1;\n    ImVec2 p_closest;\n    float p_closest_dist2 = FLT_MAX;\n    float t_step = 1.0f / (float)num_segments;\n    for (int i_step = 1; i_step <= num_segments; i_step++)\n    {\n        ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);\n        ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);\n        float dist2 = ImLengthSqr(p - p_line);\n        if (dist2 < p_closest_dist2)\n        {\n            p_closest = p_line;\n            p_closest_dist2 = dist2;\n        }\n        p_last = p_current;\n    }\n    return p_closest;\n}\n\n// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp\nstatic void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)\n{\n    float dx = x4 - x1;\n    float dy = y4 - y1;\n    float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);\n    float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);\n    d2 = (d2 >= 0) ? d2 : -d2;\n    d3 = (d3 >= 0) ? d3 : -d3;\n    if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))\n    {\n        ImVec2 p_current(x4, y4);\n        ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);\n        float dist2 = ImLengthSqr(p - p_line);\n        if (dist2 < p_closest_dist2)\n        {\n            p_closest = p_line;\n            p_closest_dist2 = dist2;\n        }\n        p_last = p_current;\n    }\n    else if (level < 10)\n    {\n        float x12 = (x1 + x2)*0.5f,       y12 = (y1 + y2)*0.5f;\n        float x23 = (x2 + x3)*0.5f,       y23 = (y2 + y3)*0.5f;\n        float x34 = (x3 + x4)*0.5f,       y34 = (y3 + y4)*0.5f;\n        float x123 = (x12 + x23)*0.5f,    y123 = (y12 + y23)*0.5f;\n        float x234 = (x23 + x34)*0.5f,    y234 = (y23 + y34)*0.5f;\n        float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;\n        ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);\n        ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);\n    }\n}\n\n// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol\n// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.\nImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)\n{\n    IM_ASSERT(tess_tol > 0.0f);\n    ImVec2 p_last = p1;\n    ImVec2 p_closest;\n    float p_closest_dist2 = FLT_MAX;\n    ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);\n    return p_closest;\n}\n\nImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)\n{\n    ImVec2 ap = p - a;\n    ImVec2 ab_dir = b - a;\n    float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;\n    if (dot < 0.0f)\n        return a;\n    float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;\n    if (dot > ab_len_sqr)\n        return b;\n    return a + ab_dir * dot / ab_len_sqr;\n}\n\nbool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)\n{\n    bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;\n    bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;\n    bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;\n    return (b1 == b2) && (b2 == b3);\n}\n\nvoid ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)\n{\n    ImVec2 v0 = b - a;\n    ImVec2 v1 = c - a;\n    ImVec2 v2 = p - a;\n    const float denom = v0.x * v1.y - v1.x * v0.y;\n    out_v = (v2.x * v1.y - v1.x * v2.y) / denom;\n    out_w = (v0.x * v2.y - v2.x * v0.y) / denom;\n    out_u = 1.0f - out_v - out_w;\n}\n\nImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)\n{\n    ImVec2 proj_ab = ImLineClosestPoint(a, b, p);\n    ImVec2 proj_bc = ImLineClosestPoint(b, c, p);\n    ImVec2 proj_ca = ImLineClosestPoint(c, a, p);\n    float dist2_ab = ImLengthSqr(p - proj_ab);\n    float dist2_bc = ImLengthSqr(p - proj_bc);\n    float dist2_ca = ImLengthSqr(p - proj_ca);\n    float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));\n    if (m == dist2_ab)\n        return proj_ab;\n    if (m == dist2_bc)\n        return proj_bc;\n    return proj_ca;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)\n//-----------------------------------------------------------------------------\n\n// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.\nint ImStricmp(const char* str1, const char* str2)\n{\n    int d;\n    while ((d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; }\n    return d;\n}\n\nint ImStrnicmp(const char* str1, const char* str2, size_t count)\n{\n    int d = 0;\n    while (count > 0 && (d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; count--; }\n    return d;\n}\n\nvoid ImStrncpy(char* dst, const char* src, size_t count)\n{\n    if (count < 1)\n        return;\n    if (count > 1)\n        strncpy(dst, src, count - 1); // FIXME-OPT: strncpy not only doesn't guarantee 0-termination, it also always writes the whole array\n    dst[count - 1] = 0;\n}\n\nchar* ImStrdup(const char* str)\n{\n    size_t len = ImStrlen(str);\n    void* buf = IM_ALLOC(len + 1);\n    return (char*)memcpy(buf, (const void*)str, len + 1);\n}\n\nvoid* ImMemdup(const void* src, size_t size)\n{\n    void* dst = IM_ALLOC(size);\n    return memcpy(dst, src, size);\n}\n\nchar* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)\n{\n    size_t dst_buf_size = p_dst_size ? *p_dst_size : ImStrlen(dst) + 1;\n    size_t src_size = ImStrlen(src) + 1;\n    if (dst_buf_size < src_size)\n    {\n        IM_FREE(dst);\n        dst = (char*)IM_ALLOC(src_size);\n        if (p_dst_size)\n            *p_dst_size = src_size;\n    }\n    return (char*)memcpy(dst, (const void*)src, src_size);\n}\n\nconst char* ImStrchrRange(const char* str, const char* str_end, char c)\n{\n    const char* p = (const char*)ImMemchr(str, (int)c, str_end - str);\n    return p;\n}\n\nint ImStrlenW(const ImWchar* str)\n{\n    //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit\n    int n = 0;\n    while (*str++) n++;\n    return n;\n}\n\n// Find end-of-line. Return pointer will point to either first \\n, either str_end.\nconst char* ImStreolRange(const char* str, const char* str_end)\n{\n    const char* p = (const char*)ImMemchr(str, '\\n', str_end - str);\n    return p ? p : str_end;\n}\n\nconst char* ImStrbol(const char* buf_mid_line, const char* buf_begin) // find beginning-of-line\n{\n    IM_ASSERT_PARANOID(buf_mid_line >= buf_begin && buf_mid_line <= buf_begin + ImStrlen(buf_begin));\n    while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\\n')\n        buf_mid_line--;\n    return buf_mid_line;\n}\n\nconst char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)\n{\n    if (!needle_end)\n        needle_end = needle + ImStrlen(needle);\n\n    const char un0 = (char)ImToUpper(*needle);\n    while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))\n    {\n        if (ImToUpper(*haystack) == un0)\n        {\n            const char* b = needle + 1;\n            for (const char* a = haystack + 1; b < needle_end; a++, b++)\n                if (ImToUpper(*a) != ImToUpper(*b))\n                    break;\n            if (b == needle_end)\n                return haystack;\n        }\n        haystack++;\n    }\n    return NULL;\n}\n\n// Trim str by offsetting contents when there's leading data + writing a \\0 at the trailing position. We use this in situation where the cost is negligible.\nvoid ImStrTrimBlanks(char* buf)\n{\n    char* p = buf;\n    while (p[0] == ' ' || p[0] == '\\t')     // Leading blanks\n        p++;\n    char* p_start = p;\n    while (*p != 0)                         // Find end of string\n        p++;\n    while (p > p_start && (p[-1] == ' ' || p[-1] == '\\t'))  // Trailing blanks\n        p--;\n    if (p_start != buf)                     // Copy memory if we had leading blanks\n        memmove(buf, p_start, p - p_start);\n    buf[p - p_start] = 0;                   // Zero terminate\n}\n\nconst char* ImStrSkipBlank(const char* str)\n{\n    while (str[0] == ' ' || str[0] == '\\t')\n        str++;\n    return str;\n}\n\n// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).\n// Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.\n// B) When buf==NULL vsnprintf() will return the output size.\n#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n\n// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)\n// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are\n// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)\n#ifdef IMGUI_USE_STB_SPRINTF\n#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION\n#define STB_SPRINTF_IMPLEMENTATION\n#endif\n#ifdef IMGUI_STB_SPRINTF_FILENAME\n#include IMGUI_STB_SPRINTF_FILENAME\n#else\n#include \"stb_sprintf.h\"\n#endif\n#endif // #ifdef IMGUI_USE_STB_SPRINTF\n\n#if defined(_MSC_VER) && !defined(vsnprintf)\n#define vsnprintf _vsnprintf\n#endif\n\nint ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n#ifdef IMGUI_USE_STB_SPRINTF\n    int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);\n#else\n    int w = vsnprintf(buf, buf_size, fmt, args);\n#endif\n    va_end(args);\n    if (buf == NULL)\n        return w;\n    if (w == -1 || w >= (int)buf_size)\n        w = (int)buf_size - 1;\n    buf[w] = 0;\n    return w;\n}\n\nint ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)\n{\n#ifdef IMGUI_USE_STB_SPRINTF\n    int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);\n#else\n    int w = vsnprintf(buf, buf_size, fmt, args);\n#endif\n    if (buf == NULL)\n        return w;\n    if (w == -1 || w >= (int)buf_size)\n        w = (int)buf_size - 1;\n    buf[w] = 0;\n    return w;\n}\n#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n\nvoid ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);\n    va_end(args);\n}\n\n// FIXME: Should rework API toward allowing multiple in-flight temp buffers (easier and safer for caller)\n// by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g.\n//  ImGuiTempBufferToken token;\n//  ImFormatStringToTempBuffer(token, ...);\nvoid ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)\n{\n    ImGuiContext& g = *GImGui;\n    if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)\n    {\n        const char* buf = va_arg(args, const char*); // Skip formatting when using \"%s\"\n        if (buf == NULL)\n            buf = \"(null)\";\n        *out_buf = buf;\n        if (out_buf_end) { *out_buf_end = buf + ImStrlen(buf); }\n    }\n    else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)\n    {\n        int buf_len = va_arg(args, int); // Skip formatting when using \"%.*s\"\n        const char* buf = va_arg(args, const char*);\n        if (buf == NULL)\n        {\n            buf = \"(null)\";\n            buf_len = ImMin(buf_len, 6);\n        }\n        *out_buf = buf;\n        *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.\n    }\n    else\n    {\n        int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);\n        *out_buf = g.TempBuffer.Data;\n        if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }\n    }\n}\n\n#ifndef IMGUI_ENABLE_SSE4_2_CRC\n// CRC32 needs a 1KB lookup table (not cache friendly)\n// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:\n// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.\nstatic const ImU32 GCrc32LookupTable[256] =\n{\n#ifdef IMGUI_USE_LEGACY_CRC32_ADLER\n    // Legacy CRC32-adler table used pre 1.91.6 (before 2024/11/27). Only use if you cannot afford invalidating old .ini data.\n    0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,\n    0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,\n    0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,\n    0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,\n    0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,\n    0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,\n    0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,\n    0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,\n    0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,\n    0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,\n    0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,\n    0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,\n    0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,\n    0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,\n    0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,\n    0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,\n#else\n    // CRC32c table compatible with SSE 4.2 instructions\n    0x00000000,0xF26B8303,0xE13B70F7,0x1350F3F4,0xC79A971F,0x35F1141C,0x26A1E7E8,0xD4CA64EB,0x8AD958CF,0x78B2DBCC,0x6BE22838,0x9989AB3B,0x4D43CFD0,0xBF284CD3,0xAC78BF27,0x5E133C24,\n    0x105EC76F,0xE235446C,0xF165B798,0x030E349B,0xD7C45070,0x25AFD373,0x36FF2087,0xC494A384,0x9A879FA0,0x68EC1CA3,0x7BBCEF57,0x89D76C54,0x5D1D08BF,0xAF768BBC,0xBC267848,0x4E4DFB4B,\n    0x20BD8EDE,0xD2D60DDD,0xC186FE29,0x33ED7D2A,0xE72719C1,0x154C9AC2,0x061C6936,0xF477EA35,0xAA64D611,0x580F5512,0x4B5FA6E6,0xB93425E5,0x6DFE410E,0x9F95C20D,0x8CC531F9,0x7EAEB2FA,\n    0x30E349B1,0xC288CAB2,0xD1D83946,0x23B3BA45,0xF779DEAE,0x05125DAD,0x1642AE59,0xE4292D5A,0xBA3A117E,0x4851927D,0x5B016189,0xA96AE28A,0x7DA08661,0x8FCB0562,0x9C9BF696,0x6EF07595,\n    0x417B1DBC,0xB3109EBF,0xA0406D4B,0x522BEE48,0x86E18AA3,0x748A09A0,0x67DAFA54,0x95B17957,0xCBA24573,0x39C9C670,0x2A993584,0xD8F2B687,0x0C38D26C,0xFE53516F,0xED03A29B,0x1F682198,\n    0x5125DAD3,0xA34E59D0,0xB01EAA24,0x42752927,0x96BF4DCC,0x64D4CECF,0x77843D3B,0x85EFBE38,0xDBFC821C,0x2997011F,0x3AC7F2EB,0xC8AC71E8,0x1C661503,0xEE0D9600,0xFD5D65F4,0x0F36E6F7,\n    0x61C69362,0x93AD1061,0x80FDE395,0x72966096,0xA65C047D,0x5437877E,0x4767748A,0xB50CF789,0xEB1FCBAD,0x197448AE,0x0A24BB5A,0xF84F3859,0x2C855CB2,0xDEEEDFB1,0xCDBE2C45,0x3FD5AF46,\n    0x7198540D,0x83F3D70E,0x90A324FA,0x62C8A7F9,0xB602C312,0x44694011,0x5739B3E5,0xA55230E6,0xFB410CC2,0x092A8FC1,0x1A7A7C35,0xE811FF36,0x3CDB9BDD,0xCEB018DE,0xDDE0EB2A,0x2F8B6829,\n    0x82F63B78,0x709DB87B,0x63CD4B8F,0x91A6C88C,0x456CAC67,0xB7072F64,0xA457DC90,0x563C5F93,0x082F63B7,0xFA44E0B4,0xE9141340,0x1B7F9043,0xCFB5F4A8,0x3DDE77AB,0x2E8E845F,0xDCE5075C,\n    0x92A8FC17,0x60C37F14,0x73938CE0,0x81F80FE3,0x55326B08,0xA759E80B,0xB4091BFF,0x466298FC,0x1871A4D8,0xEA1A27DB,0xF94AD42F,0x0B21572C,0xDFEB33C7,0x2D80B0C4,0x3ED04330,0xCCBBC033,\n    0xA24BB5A6,0x502036A5,0x4370C551,0xB11B4652,0x65D122B9,0x97BAA1BA,0x84EA524E,0x7681D14D,0x2892ED69,0xDAF96E6A,0xC9A99D9E,0x3BC21E9D,0xEF087A76,0x1D63F975,0x0E330A81,0xFC588982,\n    0xB21572C9,0x407EF1CA,0x532E023E,0xA145813D,0x758FE5D6,0x87E466D5,0x94B49521,0x66DF1622,0x38CC2A06,0xCAA7A905,0xD9F75AF1,0x2B9CD9F2,0xFF56BD19,0x0D3D3E1A,0x1E6DCDEE,0xEC064EED,\n    0xC38D26C4,0x31E6A5C7,0x22B65633,0xD0DDD530,0x0417B1DB,0xF67C32D8,0xE52CC12C,0x1747422F,0x49547E0B,0xBB3FFD08,0xA86F0EFC,0x5A048DFF,0x8ECEE914,0x7CA56A17,0x6FF599E3,0x9D9E1AE0,\n    0xD3D3E1AB,0x21B862A8,0x32E8915C,0xC083125F,0x144976B4,0xE622F5B7,0xF5720643,0x07198540,0x590AB964,0xAB613A67,0xB831C993,0x4A5A4A90,0x9E902E7B,0x6CFBAD78,0x7FAB5E8C,0x8DC0DD8F,\n    0xE330A81A,0x115B2B19,0x020BD8ED,0xF0605BEE,0x24AA3F05,0xD6C1BC06,0xC5914FF2,0x37FACCF1,0x69E9F0D5,0x9B8273D6,0x88D28022,0x7AB90321,0xAE7367CA,0x5C18E4C9,0x4F48173D,0xBD23943E,\n    0xF36E6F75,0x0105EC76,0x12551F82,0xE03E9C81,0x34F4F86A,0xC69F7B69,0xD5CF889D,0x27A40B9E,0x79B737BA,0x8BDCB4B9,0x988C474D,0x6AE7C44E,0xBE2DA0A5,0x4C4623A6,0x5F16D052,0xAD7D5351\n#endif\n};\n#endif\n\n// Known size hash\n// It is ok to call ImHashData on a string with known length but the ### operator won't be supported.\n// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.\nImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)\n{\n    ImU32 crc = ~seed;\n    const unsigned char* data = (const unsigned char*)data_p;\n    const unsigned char *data_end = (const unsigned char*)data_p + data_size;\n#ifndef IMGUI_ENABLE_SSE4_2_CRC\n    const ImU32* crc32_lut = GCrc32LookupTable;\n    while (data < data_end)\n        crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];\n    return ~crc;\n#else\n    while (data + 4 <= data_end)\n    {\n        crc = _mm_crc32_u32(crc, *(ImU32*)data);\n        data += 4;\n    }\n    while (data < data_end)\n        crc = _mm_crc32_u8(crc, *data++);\n    return ~crc;\n#endif\n}\n\n// Zero-terminated string hash, with support for ### to reset back to seed value.\n// e.g. \"label###id\" outputs the same hash as \"id\" (and \"label\" is generally displayed by the UI functions)\n// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.\nImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)\n{\n    seed = ~seed;\n    ImU32 crc = seed;\n    const unsigned char* data = (const unsigned char*)data_p;\n#ifndef IMGUI_ENABLE_SSE4_2_CRC\n    const ImU32* crc32_lut = GCrc32LookupTable;\n#endif\n    if (data_size != 0)\n    {\n        while (data_size-- > 0)\n        {\n            unsigned char c = *data++;\n            if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')\n            {\n                crc = seed;\n                data += 2;\n                data_size -= 2;\n                continue;\n            }\n#ifndef IMGUI_ENABLE_SSE4_2_CRC\n            crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];\n#else\n            crc = _mm_crc32_u8(crc, c);\n#endif\n        }\n    }\n    else\n    {\n        while (unsigned char c = *data++)\n        {\n            if (c == '#' && data[0] == '#' && data[1] == '#')\n            {\n                crc = seed;\n                data += 2;\n                continue;\n            }\n#ifndef IMGUI_ENABLE_SSE4_2_CRC\n            crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];\n#else\n            crc = _mm_crc32_u8(crc, c);\n#endif\n        }\n    }\n    return ~crc;\n}\n\n// Skip to the \"###\" marker if any. We don't skip past to match the behavior of GetID()\n// FIXME-OPT: This is not designed to be optimal. Use with care.\nconst char* ImHashSkipUncontributingPrefix(const char* label)\n{\n    const char* result = label;\n    while (unsigned char c = *label++)\n        if (c == '#' && label[0] == '#' && label[1] == '#')\n            result = label + 2;\n    return result;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] MISC HELPERS/UTILITIES (File functions)\n//-----------------------------------------------------------------------------\n\n// Default file functions\n#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\n\nImFileHandle ImFileOpen(const char* filename, const char* mode)\n{\n#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (defined(__MINGW32__) || (!defined(__CYGWIN__) && !defined(__GNUC__)))\n    // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.\n    // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!\n    const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);\n    const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);\n\n    // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.\n    // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).\n    wchar_t local_temp_stack[FILENAME_MAX];\n    ImVector<wchar_t> local_temp_heap;\n    if (filename_wsize + mode_wsize > IM_COUNTOF(local_temp_stack))\n        local_temp_heap.resize(filename_wsize + mode_wsize);\n    wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;\n    wchar_t* mode_wbuf = filename_wbuf + filename_wsize;\n    ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize);\n    ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize);\n    return ::_wfopen(filename_wbuf, mode_wbuf);\n#else\n    return fopen(filename, mode);\n#endif\n}\n\n// We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way.\nbool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }\nImU64   ImFileGetSize(ImFileHandle f)   { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }\nImU64   ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f)           { return fread(data, (size_t)sz, (size_t)count, f); }\nImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }\n#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\n\n// Helper: Load file content into memory\n// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()\n// This can't really be used with \"rt\" because fseek size won't match read size.\nvoid*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)\n{\n    IM_ASSERT(filename && mode);\n    if (out_file_size)\n        *out_file_size = 0;\n\n    ImFileHandle f;\n    if ((f = ImFileOpen(filename, mode)) == NULL)\n        return NULL;\n\n    size_t file_size = (size_t)ImFileGetSize(f);\n    if (file_size == (size_t)-1)\n    {\n        ImFileClose(f);\n        return NULL;\n    }\n\n    void* file_data = IM_ALLOC(file_size + padding_bytes);\n    if (file_data == NULL)\n    {\n        ImFileClose(f);\n        return NULL;\n    }\n    if (ImFileRead(file_data, 1, file_size, f) != file_size)\n    {\n        ImFileClose(f);\n        IM_FREE(file_data);\n        return NULL;\n    }\n    if (padding_bytes > 0)\n        memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);\n\n    ImFileClose(f);\n    if (out_file_size)\n        *out_file_size = file_size;\n\n    return file_data;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)\n//-----------------------------------------------------------------------------\n\nIM_MSVC_RUNTIME_CHECKS_OFF\n\n// Convert UTF-8 to 32-bit character, process single character input.\n// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).\n// We handle UTF-8 decoding error by skipping forward.\nint ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)\n{\n    static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 };\n    static const int masks[]  = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };\n    static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };\n    static const int shiftc[] = { 0, 18, 12, 6, 0 };\n    static const int shifte[] = { 0, 6, 4, 2, 0 };\n    int len = lengths[*(const unsigned char*)in_text >> 3];\n    int wanted = len + (len ? 0 : 1);\n\n    // IMPORTANT: if in_text_end == NULL it assume we have enough space!\n    if (in_text_end == NULL)\n        in_text_end = in_text + wanted; // Max length, nulls will be taken into account.\n\n    // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,\n    // so it is fast even with excessive branching.\n    unsigned char s[4];\n    s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;\n    s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;\n    s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;\n    s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;\n\n    // Assume a four-byte character and load four bytes. Unused bits are shifted out.\n    *out_char  = (uint32_t)(s[0] & masks[len]) << 18;\n    *out_char |= (uint32_t)(s[1] & 0x3f) << 12;\n    *out_char |= (uint32_t)(s[2] & 0x3f) <<  6;\n    *out_char |= (uint32_t)(s[3] & 0x3f) <<  0;\n    *out_char >>= shiftc[len];\n\n    // Accumulate the various error conditions.\n    int e = 0;\n    e  = (*out_char < mins[len]) << 6; // non-canonical encoding\n    e |= ((*out_char >> 11) == 0x1b) << 7;  // surrogate half?\n    e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8;  // out of range we can store in ImWchar (FIXME: May evolve)\n    e |= (s[1] & 0xc0) >> 2;\n    e |= (s[2] & 0xc0) >> 4;\n    e |= (s[3]       ) >> 6;\n    e ^= 0x2a; // top two bits of each tail byte correct?\n    e >>= shifte[len];\n\n    if (e)\n    {\n        // No bytes are consumed when *in_text == 0 || in_text == in_text_end.\n        // One byte is consumed in case of invalid first byte of in_text.\n        // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.\n        // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.\n        wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);\n        *out_char = IM_UNICODE_CODEPOINT_INVALID;\n    }\n\n    return wanted;\n}\n\nint ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)\n{\n    ImWchar* buf_out = buf;\n    ImWchar* buf_end = buf + buf_size;\n    while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)\n    {\n        unsigned int c;\n        in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);\n        *buf_out++ = (ImWchar)c;\n    }\n    *buf_out = 0;\n    if (in_text_remaining)\n        *in_text_remaining = in_text;\n    return (int)(buf_out - buf);\n}\n\nint ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)\n{\n    int char_count = 0;\n    while ((!in_text_end || in_text < in_text_end) && *in_text)\n    {\n        unsigned int c;\n        in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);\n        char_count++;\n    }\n    return char_count;\n}\n\n// Based on stb_to_utf8() from github.com/nothings/stb/\nstatic inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)\n{\n    if (c < 0x80)\n    {\n        buf[0] = (char)c;\n        return 1;\n    }\n    if (c < 0x800)\n    {\n        if (buf_size < 2) return 0;\n        buf[0] = (char)(0xc0 + (c >> 6));\n        buf[1] = (char)(0x80 + (c & 0x3f));\n        return 2;\n    }\n    if (c < 0x10000)\n    {\n        if (buf_size < 3) return 0;\n        buf[0] = (char)(0xe0 + (c >> 12));\n        buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));\n        buf[2] = (char)(0x80 + ((c ) & 0x3f));\n        return 3;\n    }\n    if (c <= 0x10FFFF)\n    {\n        if (buf_size < 4) return 0;\n        buf[0] = (char)(0xf0 + (c >> 18));\n        buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));\n        buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));\n        buf[3] = (char)(0x80 + ((c ) & 0x3f));\n        return 4;\n    }\n    // Invalid code point, the max unicode is 0x10FFFF\n    return 0;\n}\n\nint ImTextCharToUtf8(char out_buf[5], unsigned int c)\n{\n    int count = ImTextCharToUtf8_inline(out_buf, 5, c);\n    out_buf[count] = 0;\n    return count;\n}\n\n// Not optimal but we very rarely use this function.\nint ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)\n{\n    unsigned int unused = 0;\n    return ImTextCharFromUtf8(&unused, in_text, in_text_end);\n}\n\nstatic inline int ImTextCountUtf8BytesFromChar(unsigned int c)\n{\n    if (c < 0x80) return 1;\n    if (c < 0x800) return 2;\n    if (c < 0x10000) return 3;\n    if (c <= 0x10FFFF) return 4;\n    return 3;\n}\n\nint ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)\n{\n    char* buf_p = out_buf;\n    const char* buf_end = out_buf + out_buf_size;\n    while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)\n    {\n        unsigned int c = (unsigned int)(*in_text++);\n        if (c < 0x80)\n            *buf_p++ = (char)c;\n        else\n            buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);\n    }\n    *buf_p = 0;\n    return (int)(buf_p - out_buf);\n}\n\nint ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)\n{\n    int bytes_count = 0;\n    while ((!in_text_end || in_text < in_text_end) && *in_text)\n    {\n        unsigned int c = (unsigned int)(*in_text++);\n        if (c < 0x80)\n            bytes_count++;\n        else\n            bytes_count += ImTextCountUtf8BytesFromChar(c);\n    }\n    return bytes_count;\n}\n\nconst char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_p)\n{\n    while (in_p > in_text_start)\n    {\n        in_p--;\n        if ((*in_p & 0xC0) != 0x80)\n            return in_p;\n    }\n    return in_text_start;\n}\n\nconst char* ImTextFindValidUtf8CodepointEnd(const char* in_text_start, const char* in_text_end, const char* in_p)\n{\n    if (in_text_start == in_p)\n        return in_text_start;\n    const char* prev = ImTextFindPreviousUtf8Codepoint(in_text_start, in_p);\n    unsigned int prev_c;\n    int prev_c_len = ImTextCharFromUtf8(&prev_c, prev, in_text_end);\n    if (prev_c != IM_UNICODE_CODEPOINT_INVALID && prev_c_len <= (int)(in_p - prev))\n        return in_p;\n    return prev;\n}\n\nint ImTextCountLines(const char* in_text, const char* in_text_end)\n{\n    if (in_text_end == NULL)\n        in_text_end = in_text + ImStrlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now.\n    int count = 0;\n    while (in_text < in_text_end)\n    {\n        const char* line_end = (const char*)ImMemchr(in_text, '\\n', in_text_end - in_text);\n        in_text = line_end ? line_end + 1 : in_text_end;\n        count++;\n    }\n    return count;\n}\n\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n//-----------------------------------------------------------------------------\n// [SECTION] MISC HELPERS/UTILITIES (Color functions)\n// Note: The Convert functions are early design which are not consistent with other API.\n//-----------------------------------------------------------------------------\n\nIMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)\n{\n    float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;\n    int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);\n    int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);\n    int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);\n    return IM_COL32(r, g, b, 0xFF);\n}\n\nImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)\n{\n    float s = 1.0f / 255.0f;\n    return ImVec4(\n        ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,\n        ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,\n        ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,\n        ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);\n}\n\nImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)\n{\n    ImU32 out;\n    out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;\n    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;\n    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;\n    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;\n    return out;\n}\n\n// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592\n// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv\nvoid ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)\n{\n    float K = 0.f;\n    if (g < b)\n    {\n        ImSwap(g, b);\n        K = -1.f;\n    }\n    if (r < g)\n    {\n        ImSwap(r, g);\n        K = -2.f / 6.f - K;\n    }\n\n    const float chroma = r - (g < b ? g : b);\n    out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));\n    out_s = chroma / (r + 1e-20f);\n    out_v = r;\n}\n\n// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593\n// also http://en.wikipedia.org/wiki/HSL_and_HSV\nvoid ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)\n{\n    if (s == 0.0f)\n    {\n        // gray\n        out_r = out_g = out_b = v;\n        return;\n    }\n\n    h = ImFmod(h, 1.0f) / (60.0f / 360.0f);\n    int   i = (int)h;\n    float f = h - (float)i;\n    float p = v * (1.0f - s);\n    float q = v * (1.0f - s * f);\n    float t = v * (1.0f - s * (1.0f - f));\n\n    switch (i)\n    {\n    case 0: out_r = v; out_g = t; out_b = p; break;\n    case 1: out_r = q; out_g = v; out_b = p; break;\n    case 2: out_r = p; out_g = v; out_b = t; break;\n    case 3: out_r = p; out_g = q; out_b = v; break;\n    case 4: out_r = t; out_g = p; out_b = v; break;\n    case 5: default: out_r = v; out_g = p; out_b = q; break;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiStorage\n// Helper: Key->value storage\n//-----------------------------------------------------------------------------\n\n// std::lower_bound but without the bullshit\nImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key)\n{\n    ImGuiStoragePair* in_p = in_begin;\n    for (size_t count = (size_t)(in_end - in_p); count > 0; )\n    {\n        size_t count2 = count >> 1;\n        ImGuiStoragePair* mid = in_p + count2;\n        if (mid->key < key)\n        {\n            in_p = ++mid;\n            count -= count2 + 1;\n        }\n        else\n        {\n            count = count2;\n        }\n    }\n    return in_p;\n}\n\nIM_MSVC_RUNTIME_CHECKS_OFF\nstatic int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs)\n{\n    // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.\n    ImGuiID lhs_v = ((const ImGuiStoragePair*)lhs)->key;\n    ImGuiID rhs_v = ((const ImGuiStoragePair*)rhs)->key;\n    return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);\n}\n\n// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.\nvoid ImGuiStorage::BuildSortByKey()\n{\n    ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), PairComparerByID);\n}\n\nint ImGuiStorage::GetInt(ImGuiID key, int default_val) const\n{\n    ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);\n    if (it == Data.Data + Data.Size || it->key != key)\n        return default_val;\n    return it->val_i;\n}\n\nbool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const\n{\n    return GetInt(key, default_val ? 1 : 0) != 0;\n}\n\nfloat ImGuiStorage::GetFloat(ImGuiID key, float default_val) const\n{\n    ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);\n    if (it == Data.Data + Data.Size || it->key != key)\n        return default_val;\n    return it->val_f;\n}\n\nvoid* ImGuiStorage::GetVoidPtr(ImGuiID key) const\n{\n    ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);\n    if (it == Data.Data + Data.Size || it->key != key)\n        return NULL;\n    return it->val_p;\n}\n\n// References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.\nint* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)\n{\n    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);\n    if (it == Data.Data + Data.Size || it->key != key)\n        it = Data.insert(it, ImGuiStoragePair(key, default_val));\n    return &it->val_i;\n}\n\nbool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)\n{\n    return (bool*)GetIntRef(key, default_val ? 1 : 0);\n}\n\nfloat* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)\n{\n    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);\n    if (it == Data.Data + Data.Size || it->key != key)\n        it = Data.insert(it, ImGuiStoragePair(key, default_val));\n    return &it->val_f;\n}\n\nvoid** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)\n{\n    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);\n    if (it == Data.Data + Data.Size || it->key != key)\n        it = Data.insert(it, ImGuiStoragePair(key, default_val));\n    return &it->val_p;\n}\n\n// FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)\nvoid ImGuiStorage::SetInt(ImGuiID key, int val)\n{\n    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);\n    if (it == Data.Data + Data.Size || it->key != key)\n        Data.insert(it, ImGuiStoragePair(key, val));\n    else\n        it->val_i = val;\n}\n\nvoid ImGuiStorage::SetBool(ImGuiID key, bool val)\n{\n    SetInt(key, val ? 1 : 0);\n}\n\nvoid ImGuiStorage::SetFloat(ImGuiID key, float val)\n{\n    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);\n    if (it == Data.Data + Data.Size || it->key != key)\n        Data.insert(it, ImGuiStoragePair(key, val));\n    else\n        it->val_f = val;\n}\n\nvoid ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)\n{\n    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);\n    if (it == Data.Data + Data.Size || it->key != key)\n        Data.insert(it, ImGuiStoragePair(key, val));\n    else\n        it->val_p = val;\n}\n\nvoid ImGuiStorage::SetAllInt(int v)\n{\n    for (int i = 0; i < Data.Size; i++)\n        Data[i].val_i = v;\n}\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiTextFilter\n//-----------------------------------------------------------------------------\n\n// Helper: Parse and apply text filters. In format \"aaaaa[,bbbb][,ccccc]\"\nImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077\n{\n    InputBuf[0] = 0;\n    CountGrep = 0;\n    if (default_filter)\n    {\n        ImStrncpy(InputBuf, default_filter, IM_COUNTOF(InputBuf));\n        Build();\n    }\n}\n\nbool ImGuiTextFilter::Draw(const char* label, float width)\n{\n    if (width != 0.0f)\n        ImGui::SetNextItemWidth(width);\n    bool value_changed = ImGui::InputText(label, InputBuf, IM_COUNTOF(InputBuf));\n    if (value_changed)\n        Build();\n    return value_changed;\n}\n\nvoid ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const\n{\n    out->resize(0);\n    const char* wb = b;\n    const char* we = wb;\n    while (we < e)\n    {\n        if (*we == separator)\n        {\n            out->push_back(ImGuiTextRange(wb, we));\n            wb = we + 1;\n        }\n        we++;\n    }\n    if (wb != we)\n        out->push_back(ImGuiTextRange(wb, we));\n}\n\nvoid ImGuiTextFilter::Build()\n{\n    Filters.resize(0);\n    ImGuiTextRange input_range(InputBuf, InputBuf + ImStrlen(InputBuf));\n    input_range.split(',', &Filters);\n\n    CountGrep = 0;\n    for (ImGuiTextRange& f : Filters)\n    {\n        while (f.b < f.e && ImCharIsBlankA(f.b[0]))\n            f.b++;\n        while (f.e > f.b && ImCharIsBlankA(f.e[-1]))\n            f.e--;\n        if (f.empty())\n            continue;\n        if (f.b[0] != '-')\n            CountGrep += 1;\n    }\n}\n\nbool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const\n{\n    if (Filters.Size == 0)\n        return true;\n\n    if (text == NULL)\n        text = text_end = \"\";\n\n    for (const ImGuiTextRange& f : Filters)\n    {\n        if (f.b == f.e)\n            continue;\n        if (f.b[0] == '-')\n        {\n            // Subtract\n            if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)\n                return false;\n        }\n        else\n        {\n            // Grep\n            if (ImStristr(text, text_end, f.b, f.e) != NULL)\n                return true;\n        }\n    }\n\n    // Implicit * grep\n    if (CountGrep == 0)\n        return true;\n\n    return false;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiTextBuffer, ImGuiTextIndex\n//-----------------------------------------------------------------------------\n\n// On some platform vsnprintf() takes va_list by reference and modifies it.\n// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.\n#ifndef va_copy\n#if defined(__GNUC__) || defined(__clang__)\n#define va_copy(dest, src) __builtin_va_copy(dest, src)\n#else\n#define va_copy(dest, src) (dest = src)\n#endif\n#endif\n\nchar ImGuiTextBuffer::EmptyString[1] = { 0 };\n\nvoid ImGuiTextBuffer::append(const char* str, const char* str_end)\n{\n    int len = str_end ? (int)(str_end - str) : (int)ImStrlen(str);\n\n    // Add zero-terminator the first time\n    const int write_off = (Buf.Size != 0) ? Buf.Size : 1;\n    const int needed_sz = write_off + len;\n    if (write_off + len >= Buf.Capacity)\n    {\n        int new_capacity = Buf.Capacity * 2;\n        Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);\n    }\n\n    Buf.resize(needed_sz);\n    memcpy(&Buf[write_off - 1], str, (size_t)len);\n    Buf[write_off - 1 + len] = 0;\n}\n\nvoid ImGuiTextBuffer::appendf(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    appendfv(fmt, args);\n    va_end(args);\n}\n\n// Helper: Text buffer for logging/accumulating text\nvoid ImGuiTextBuffer::appendfv(const char* fmt, va_list args)\n{\n    va_list args_copy;\n    va_copy(args_copy, args);\n\n    int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.\n    if (len <= 0)\n    {\n        va_end(args_copy);\n        return;\n    }\n\n    // Add zero-terminator the first time\n    const int write_off = (Buf.Size != 0) ? Buf.Size : 1;\n    const int needed_sz = write_off + len;\n    if (write_off + len >= Buf.Capacity)\n    {\n        int new_capacity = Buf.Capacity * 2;\n        Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);\n    }\n\n    Buf.resize(needed_sz);\n    ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);\n    va_end(args_copy);\n}\n\nIM_MSVC_RUNTIME_CHECKS_OFF\nvoid ImGuiTextIndex::append(const char* base, int old_size, int new_size)\n{\n    IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset);\n    if (old_size == new_size)\n        return;\n    if (EndOffset == 0 || base[EndOffset - 1] == '\\n')\n        Offsets.push_back(EndOffset);\n    const char* base_end = base + new_size;\n    for (const char* p = base + old_size; (p = (const char*)ImMemchr(p, '\\n', base_end - p)) != 0; )\n        if (++p < base_end) // Don't push a trailing offset on last \\n\n            Offsets.push_back((int)(intptr_t)(p - base));\n    EndOffset = ImMax(EndOffset, new_size);\n}\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiListClipper\n//-----------------------------------------------------------------------------\n\n// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.\n// The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.\nstatic bool GetSkipItemForListClipping()\n{\n    ImGuiContext& g = *GImGui;\n    return g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems;\n}\n\nstatic void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>& ranges, int offset = 0)\n{\n    if (ranges.Size - offset <= 1)\n        return;\n\n    // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)\n    for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end)\n        for (int i = offset; i < sort_end + offset; ++i)\n            if (ranges[i].Min > ranges[i + 1].Min)\n                ImSwap(ranges[i], ranges[i + 1]);\n\n    // Now fuse ranges together as much as possible.\n    for (int i = 1 + offset; i < ranges.Size; i++)\n    {\n        IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert);\n        if (ranges[i - 1].Max < ranges[i].Min)\n            continue;\n        ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min);\n        ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max);\n        ranges.erase(ranges.Data + i);\n        i--;\n    }\n}\n\nstatic void ImGuiListClipper_SeekCursorAndSetupPrevLine(ImGuiListClipper* clipper, float pos_y, float line_height)\n{\n    // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.\n    // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.\n    // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek?\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    float off_y = pos_y - window->DC.CursorPos.y;\n    window->DC.CursorPos.y = pos_y;\n    window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y);\n    window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height;  // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage.\n    window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y);      // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.\n    if (ImGuiOldColumns* columns = window->DC.CurrentColumns)\n        columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly\n    if (ImGuiTable* table = g.CurrentTable)\n    {\n        if (table->IsInsideRow)\n            ImGui::TableEndRow(table);\n        const int row_increase = (int)((off_y / line_height) + 0.5f);\n        if (row_increase > 0 && (clipper->Flags & ImGuiListClipperFlags_NoSetTableRowCounters) == 0) // If your clipper item height is != from actual table row height, consider using ImGuiListClipperFlags_NoSetTableRowCounters. See #8886.\n        {\n            table->CurrentRow += row_increase;\n            table->RowBgColorCounter += row_increase;\n        }\n        table->RowPosY2 = window->DC.CursorPos.y;\n    }\n}\n\nImGuiListClipper::ImGuiListClipper()\n{\n    memset((void*)this, 0, sizeof(*this));\n}\n\nImGuiListClipper::~ImGuiListClipper()\n{\n    End();\n}\n\nvoid ImGuiListClipper::Begin(int items_count, float items_height)\n{\n    if (Ctx == NULL)\n        Ctx = ImGui::GetCurrentContext();\n\n    ImGuiContext& g = *Ctx;\n    ImGuiWindow* window = g.CurrentWindow;\n    IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: Begin(%d,%.2f) in '%s'\\n\", items_count, items_height, window->Name);\n\n    if (ImGuiTable* table = g.CurrentTable)\n        if (table->IsInsideRow)\n            ImGui::TableEndRow(table);\n\n    StartPosY = window->DC.CursorPos.y;\n    ItemsHeight = items_height;\n    ItemsCount = items_count;\n    DisplayStart = -1;\n    DisplayEnd = 0;\n\n    // Acquire temporary buffer\n    if (++g.ClipperTempDataStacked > g.ClipperTempData.Size)\n        g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData());\n    ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];\n    data->Reset(this);\n    data->LossynessOffset = window->DC.CursorStartPosLossyness.y;\n    TempData = data;\n    StartSeekOffsetY = data->LossynessOffset;\n}\n\nvoid ImGuiListClipper::End()\n{\n    if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)\n    {\n        // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.\n        ImGuiContext& g = *Ctx;\n        IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: End() in '%s'\\n\", g.CurrentWindow->Name);\n        if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)\n            SeekCursorForItem(ItemsCount);\n\n        // Restore temporary buffer and fix back pointers which may be invalidated when nesting\n        IM_ASSERT(data->ListClipper == this);\n        data->StepNo = data->Ranges.Size;\n        if (--g.ClipperTempDataStacked > 0)\n        {\n            data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];\n            data->ListClipper->TempData = data;\n        }\n        TempData = NULL;\n    }\n    DisplayStart = DisplayEnd = ItemsCount; // Clear this so code which may be reused past last Step() won't trip on a non-empty range.\n    ItemsCount = -1;\n}\n\nvoid ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)\n{\n    ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;\n    IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet.\n    IM_ASSERT(item_begin <= item_end);\n    if (item_begin < item_end)\n        data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end));\n}\n\n// This is already called while stepping.\n// The ONLY reason you may want to call this is if you passed INT_MAX to ImGuiListClipper::Begin() because you couldn't step item count beforehand.\nvoid ImGuiListClipper::SeekCursorForItem(int item_n)\n{\n    // - Perform the add and multiply with double to allow seeking through larger ranges.\n    // - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight).\n    // - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done.\n    float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight);\n    ImGuiListClipper_SeekCursorAndSetupPrevLine(this, pos_y, ItemsHeight);\n}\n\nstatic bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)\n{\n    ImGuiContext& g = *clipper->Ctx;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;\n    IM_ASSERT(data != NULL && \"Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?\");\n\n    ImGuiTable* table = g.CurrentTable;\n    if (table && table->IsInsideRow)\n        ImGui::TableEndRow(table);\n\n    // No items\n    if (clipper->ItemsCount == 0 || GetSkipItemForListClipping())\n        return false;\n\n    // While we are in frozen row state, keep displaying items one by one, unclipped\n    // FIXME: Could be stored as a table-agnostic state.\n    if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows)\n    {\n        clipper->DisplayStart = data->ItemsFrozen;\n        clipper->DisplayEnd = ImMin(data->ItemsFrozen + 1, clipper->ItemsCount);\n        if (clipper->DisplayStart < clipper->DisplayEnd)\n            data->ItemsFrozen++;\n        return true;\n    }\n\n    // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)\n    bool calc_clipping = false;\n    if (data->StepNo == 0)\n    {\n        clipper->StartPosY = window->DC.CursorPos.y;\n        if (clipper->ItemsHeight <= 0.0f)\n        {\n            // Submit the first item (or range) so we can measure its height (generally the first range is 0..1)\n            data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1));\n            clipper->DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen);\n            clipper->DisplayEnd = ImMin(data->Ranges[0].Max, clipper->ItemsCount);\n            data->StepNo = 1;\n            return true;\n        }\n        calc_clipping = true;   // If on the first step with known item height, calculate clipping.\n    }\n\n    // Step 1: Let the clipper infer height from first range\n    if (clipper->ItemsHeight <= 0.0f)\n    {\n        IM_ASSERT(data->StepNo == 1);\n        if (table)\n            IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);\n\n        bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision((float)clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y);\n        if (affected_by_floating_point_precision)\n        {\n            // Mitigation/hack for very large range: assume last time height constitute line height.\n            clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries.\n            window->DC.CursorPos.y = (float)(clipper->StartPosY + clipper->ItemsHeight);\n        }\n        else\n        {\n            clipper->ItemsHeight = (float)(window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart);\n        }\n        if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode.\n            return false;\n        IM_ASSERT(clipper->ItemsHeight > 0.0f && \"Unable to calculate item height! First item hasn't moved the cursor vertically!\");\n        calc_clipping = true;   // If item height had to be calculated, calculate clipping afterwards.\n    }\n\n    // Step 0 or 1: Calculate the actual ranges of visible elements.\n    const int already_submitted = clipper->DisplayEnd;\n    if (calc_clipping)\n    {\n        // Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done\n        clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight;\n\n        if (g.LogEnabled)\n        {\n            // If logging is active, do not perform any clipping\n            data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, clipper->ItemsCount));\n        }\n        else\n        {\n            // Add range selected to be included for navigation\n            const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);\n            const int nav_off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;\n            const int nav_off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;\n            if (is_nav_request)\n            {\n                data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringRect.Min.y, g.NavScoringRect.Max.y, nav_off_min, nav_off_max));\n                if (!g.NavScoringNoClipRect.IsInverted())\n                    data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, nav_off_min, nav_off_max));\n            }\n            if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1)\n                data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount));\n\n            // Add focused/active item\n            ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]);\n            if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)\n                data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0));\n\n            float min_y = window->ClipRect.Min.y;\n            float max_y = window->ClipRect.Max.y;\n\n            // Add box selection range\n            ImGuiBoxSelectState* bs = &g.BoxSelectState;\n            if (bs->IsActive && bs->Window == window)\n            {\n                // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos.\n                // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that.\n                // As a workaround we currently half ItemSpacing worth on each side.\n                min_y -= g.Style.ItemSpacing.y;\n                max_y += g.Style.ItemSpacing.y;\n\n                // Box-select on 2D area requires different clipping.\n                if (bs->UnclipMode)\n                    data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0));\n            }\n\n            // Add main visible range\n            data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, nav_off_min, nav_off_max));\n        }\n\n        // Convert position ranges to item index ranges\n        // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping.\n        // - Due to how Selectable extra padding they tend to be \"unaligned\" with exact unit in the item list,\n        //   which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.\n        for (ImGuiListClipperRange& range : data->Ranges)\n            if (range.PosToIndexConvert)\n            {\n                int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);\n                int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);\n                range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1);\n                range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount);\n                range.PosToIndexConvert = false;\n            }\n        ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo);\n    }\n\n    // Step 0+ (if item height is given in advance) or 1+: Display the next range in line.\n    while (data->StepNo < data->Ranges.Size)\n    {\n        clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);\n        clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount);\n        data->StepNo++;\n        if (clipper->DisplayStart >= clipper->DisplayEnd)\n            continue;\n        if (clipper->DisplayStart > already_submitted)\n            clipper->SeekCursorForItem(clipper->DisplayStart);\n        return true;\n    }\n\n    // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),\n    // Advance the cursor to the end of the list and then returns 'false' to end the loop.\n    if (clipper->ItemsCount < INT_MAX)\n        clipper->SeekCursorForItem(clipper->ItemsCount);\n\n    return false;\n}\n\nbool ImGuiListClipper::Step()\n{\n    ImGuiContext& g = *Ctx;\n    bool need_items_height = (ItemsHeight <= 0.0f);\n    bool ret = ImGuiListClipper_StepInternal(this);\n    if (ret && (DisplayStart >= DisplayEnd))\n        ret = false;\n    if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false)\n        IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: Step(): inside frozen table row.\\n\");\n    if (need_items_height && ItemsHeight > 0.0f)\n        IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: Step(): computed ItemsHeight: %.2f.\\n\", ItemsHeight);\n    if (ret)\n    {\n        IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: Step(): display %d to %d.\\n\", DisplayStart, DisplayEnd);\n    }\n    else\n    {\n        IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: Step(): End.\\n\");\n        End();\n    }\n    return ret;\n}\n\n// Generic helper, equivalent to old ImGui::CalcListClipping() but stateless\nvoid ImGui::CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end)\n{\n    *out_visible_start = ImMax((int)((clip_rect.Min.y - pos.y) / items_height), 0);\n    *out_visible_end = ImMax((int)ImCeil((clip_rect.Max.y - pos.y) / items_height), *out_visible_start);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] STYLING\n//-----------------------------------------------------------------------------\n\nImGuiStyle& ImGui::GetStyle()\n{\n    IM_ASSERT(GImGui != NULL && \"No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?\");\n    return GImGui->Style;\n}\n\nImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)\n{\n    ImGuiStyle& style = GImGui->Style;\n    ImVec4 c = style.Colors[idx];\n    c.w *= style.Alpha * alpha_mul;\n    return ColorConvertFloat4ToU32(c);\n}\n\nImU32 ImGui::GetColorU32(const ImVec4& col)\n{\n    ImGuiStyle& style = GImGui->Style;\n    ImVec4 c = col;\n    c.w *= style.Alpha;\n    return ColorConvertFloat4ToU32(c);\n}\n\nconst ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)\n{\n    ImGuiStyle& style = GImGui->Style;\n    return style.Colors[idx];\n}\n\nImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul)\n{\n    ImGuiStyle& style = GImGui->Style;\n    alpha_mul *= style.Alpha;\n    if (alpha_mul >= 1.0f)\n        return col;\n    ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;\n    a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range.\n    return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);\n}\n\n// FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32\nvoid ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiColorMod backup;\n    backup.Col = idx;\n    backup.BackupValue = g.Style.Colors[idx];\n    g.ColorStack.push_back(backup);\n    if (g.DebugFlashStyleColorIdx != idx)\n        g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);\n}\n\nvoid ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiColorMod backup;\n    backup.Col = idx;\n    backup.BackupValue = g.Style.Colors[idx];\n    g.ColorStack.push_back(backup);\n    if (g.DebugFlashStyleColorIdx != idx)\n        g.Style.Colors[idx] = col;\n}\n\nvoid ImGui::PopStyleColor(int count)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.ColorStack.Size < count)\n    {\n        IM_ASSERT_USER_ERROR(0, \"Calling PopStyleColor() too many times!\");\n        count = g.ColorStack.Size;\n    }\n    while (count > 0)\n    {\n        ImGuiColorMod& backup = g.ColorStack.back();\n        g.Style.Colors[backup.Col] = backup.BackupValue;\n        g.ColorStack.pop_back();\n        count--;\n    }\n}\n\nstatic const ImGuiCol GWindowDockStyleColors[ImGuiWindowDockStyleCol_COUNT] =\n{\n    ImGuiCol_Text, ImGuiCol_TabHovered, ImGuiCol_Tab, ImGuiCol_TabSelected, ImGuiCol_TabSelectedOverline, ImGuiCol_TabDimmed, ImGuiCol_TabDimmedSelected, ImGuiCol_TabDimmedSelectedOverline, ImGuiCol_UnsavedMarker,\n};\n\nstatic const ImGuiStyleVarInfo GStyleVarsInfo[] =\n{\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, Alpha) },                     // ImGuiStyleVar_Alpha\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) },             // ImGuiStyleVar_DisabledAlpha\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowPadding) },             // ImGuiStyleVar_WindowPadding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowRounding) },            // ImGuiStyleVar_WindowRounding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) },          // ImGuiStyleVar_WindowBorderSize\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowMinSize) },             // ImGuiStyleVar_WindowMinSize\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) },          // ImGuiStyleVar_WindowTitleAlign\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ChildRounding) },             // ImGuiStyleVar_ChildRounding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) },           // ImGuiStyleVar_ChildBorderSize\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, PopupRounding) },             // ImGuiStyleVar_PopupRounding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) },           // ImGuiStyleVar_PopupBorderSize\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FramePadding) },              // ImGuiStyleVar_FramePadding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FrameRounding) },             // ImGuiStyleVar_FrameRounding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) },           // ImGuiStyleVar_FrameBorderSize\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ItemSpacing) },               // ImGuiStyleVar_ItemSpacing\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) },          // ImGuiStyleVar_ItemInnerSpacing\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, IndentSpacing) },             // ImGuiStyleVar_IndentSpacing\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, CellPadding) },               // ImGuiStyleVar_CellPadding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) },             // ImGuiStyleVar_ScrollbarSize\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) },         // ImGuiStyleVar_ScrollbarRounding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarPadding) },          // ImGuiStyleVar_ScrollbarPadding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabMinSize) },               // ImGuiStyleVar_GrabMinSize\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabRounding) },              // ImGuiStyleVar_GrabRounding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageRounding) },             // ImGuiStyleVar_ImageRounding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageBorderSize) },           // ImGuiStyleVar_ImageBorderSize\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabRounding) },               // ImGuiStyleVar_TabRounding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBorderSize) },             // ImGuiStyleVar_TabBorderSize\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabMinWidthBase) },           // ImGuiStyleVar_TabMinWidthBase\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabMinWidthShrink) },         // ImGuiStyleVar_TabMinWidthShrink\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) },          // ImGuiStyleVar_TabBarBorderSize\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) },        // ImGuiStyleVar_TabBarOverlineSize\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)},    // ImGuiStyleVar_TableAngledHeadersAngle\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesSize)},              // ImGuiStyleVar_TreeLinesSize\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesRounding)},          // ImGuiStyleVar_TreeLinesRounding\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) },           // ImGuiStyleVar_ButtonTextAlign\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) },       // ImGuiStyleVar_SelectableTextAlign\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorSize)},              // ImGuiStyleVar_SeparatorSize\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)},    // ImGuiStyleVar_SeparatorTextBorderSize\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) },        // ImGuiStyleVar_SeparatorTextAlign\n    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) },      // ImGuiStyleVar_SeparatorTextPadding\n    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DockingSeparatorSize) },      // ImGuiStyleVar_DockingSeparatorSize\n};\n\nconst ImGuiStyleVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)\n{\n    IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);\n    IM_STATIC_ASSERT(IM_COUNTOF(GStyleVarsInfo) == ImGuiStyleVar_COUNT);\n    return &GStyleVarsInfo[idx];\n}\n\nvoid ImGui::PushStyleVar(ImGuiStyleVar idx, float val)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);\n    IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 1, \"Calling PushStyleVar() variant with wrong type!\");\n    float* pvar = (float*)var_info->GetVarPtr(&g.Style);\n    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));\n    *pvar = val;\n}\n\nvoid ImGui::PushStyleVarX(ImGuiStyleVar idx, float val_x)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);\n    IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 2, \"Calling PushStyleVar() variant with wrong type!\");\n    ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);\n    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));\n    pvar->x = val_x;\n}\n\nvoid ImGui::PushStyleVarY(ImGuiStyleVar idx, float val_y)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);\n    IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 2, \"Calling PushStyleVar() variant with wrong type!\");\n    ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);\n    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));\n    pvar->y = val_y;\n}\n\nvoid ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);\n    IM_ASSERT_USER_ERROR_RET(var_info->DataType == ImGuiDataType_Float && var_info->Count == 2, \"Calling PushStyleVar() variant with wrong type!\");\n    ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);\n    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));\n    *pvar = val;\n}\n\nvoid ImGui::PopStyleVar(int count)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.StyleVarStack.Size < count)\n    {\n        IM_ASSERT_USER_ERROR(0, \"Calling PopStyleVar() too many times!\");\n        count = g.StyleVarStack.Size;\n    }\n    while (count > 0)\n    {\n        // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.\n        ImGuiStyleMod& backup = g.StyleVarStack.back();\n        const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(backup.VarIdx);\n        void* data = var_info->GetVarPtr(&g.Style);\n        if (var_info->DataType == ImGuiDataType_Float && var_info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }\n        else if (var_info->DataType == ImGuiDataType_Float && var_info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }\n        g.StyleVarStack.pop_back();\n        count--;\n    }\n}\n\nconst char* ImGui::GetStyleColorName(ImGuiCol idx)\n{\n    // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\\1: return \"\\1\";\n    switch (idx)\n    {\n    case ImGuiCol_Text: return \"Text\";\n    case ImGuiCol_TextDisabled: return \"TextDisabled\";\n    case ImGuiCol_WindowBg: return \"WindowBg\";\n    case ImGuiCol_ChildBg: return \"ChildBg\";\n    case ImGuiCol_PopupBg: return \"PopupBg\";\n    case ImGuiCol_Border: return \"Border\";\n    case ImGuiCol_BorderShadow: return \"BorderShadow\";\n    case ImGuiCol_FrameBg: return \"FrameBg\";\n    case ImGuiCol_FrameBgHovered: return \"FrameBgHovered\";\n    case ImGuiCol_FrameBgActive: return \"FrameBgActive\";\n    case ImGuiCol_TitleBg: return \"TitleBg\";\n    case ImGuiCol_TitleBgActive: return \"TitleBgActive\";\n    case ImGuiCol_TitleBgCollapsed: return \"TitleBgCollapsed\";\n    case ImGuiCol_MenuBarBg: return \"MenuBarBg\";\n    case ImGuiCol_ScrollbarBg: return \"ScrollbarBg\";\n    case ImGuiCol_ScrollbarGrab: return \"ScrollbarGrab\";\n    case ImGuiCol_ScrollbarGrabHovered: return \"ScrollbarGrabHovered\";\n    case ImGuiCol_ScrollbarGrabActive: return \"ScrollbarGrabActive\";\n    case ImGuiCol_CheckMark: return \"CheckMark\";\n    case ImGuiCol_SliderGrab: return \"SliderGrab\";\n    case ImGuiCol_SliderGrabActive: return \"SliderGrabActive\";\n    case ImGuiCol_Button: return \"Button\";\n    case ImGuiCol_ButtonHovered: return \"ButtonHovered\";\n    case ImGuiCol_ButtonActive: return \"ButtonActive\";\n    case ImGuiCol_Header: return \"Header\";\n    case ImGuiCol_HeaderHovered: return \"HeaderHovered\";\n    case ImGuiCol_HeaderActive: return \"HeaderActive\";\n    case ImGuiCol_Separator: return \"Separator\";\n    case ImGuiCol_SeparatorHovered: return \"SeparatorHovered\";\n    case ImGuiCol_SeparatorActive: return \"SeparatorActive\";\n    case ImGuiCol_ResizeGrip: return \"ResizeGrip\";\n    case ImGuiCol_ResizeGripHovered: return \"ResizeGripHovered\";\n    case ImGuiCol_ResizeGripActive: return \"ResizeGripActive\";\n    case ImGuiCol_InputTextCursor: return \"InputTextCursor\";\n    case ImGuiCol_TabHovered: return \"TabHovered\";\n    case ImGuiCol_Tab: return \"Tab\";\n    case ImGuiCol_TabSelected: return \"TabSelected\";\n    case ImGuiCol_TabSelectedOverline: return \"TabSelectedOverline\";\n    case ImGuiCol_TabDimmed: return \"TabDimmed\";\n    case ImGuiCol_TabDimmedSelected: return \"TabDimmedSelected\";\n    case ImGuiCol_TabDimmedSelectedOverline: return \"TabDimmedSelectedOverline\";\n    case ImGuiCol_DockingPreview: return \"DockingPreview\";\n    case ImGuiCol_DockingEmptyBg: return \"DockingEmptyBg\";\n    case ImGuiCol_PlotLines: return \"PlotLines\";\n    case ImGuiCol_PlotLinesHovered: return \"PlotLinesHovered\";\n    case ImGuiCol_PlotHistogram: return \"PlotHistogram\";\n    case ImGuiCol_PlotHistogramHovered: return \"PlotHistogramHovered\";\n    case ImGuiCol_TableHeaderBg: return \"TableHeaderBg\";\n    case ImGuiCol_TableBorderStrong: return \"TableBorderStrong\";\n    case ImGuiCol_TableBorderLight: return \"TableBorderLight\";\n    case ImGuiCol_TableRowBg: return \"TableRowBg\";\n    case ImGuiCol_TableRowBgAlt: return \"TableRowBgAlt\";\n    case ImGuiCol_TextLink: return \"TextLink\";\n    case ImGuiCol_TextSelectedBg: return \"TextSelectedBg\";\n    case ImGuiCol_TreeLines: return \"TreeLines\";\n    case ImGuiCol_DragDropTarget: return \"DragDropTarget\";\n    case ImGuiCol_DragDropTargetBg: return \"DragDropTargetBg\";\n    case ImGuiCol_UnsavedMarker: return \"UnsavedMarker\";\n    case ImGuiCol_NavCursor: return \"NavCursor\";\n    case ImGuiCol_NavWindowingHighlight: return \"NavWindowingHighlight\";\n    case ImGuiCol_NavWindowingDimBg: return \"NavWindowingDimBg\";\n    case ImGuiCol_ModalWindowDimBg: return \"ModalWindowDimBg\";\n    case ImGuiCol_WindowShadow: return \"WindowShadow\";\n    }\n    IM_ASSERT(0);\n    return \"Unknown\";\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] RENDER HELPERS\n// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,\n// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.\n// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.\n//-----------------------------------------------------------------------------\n\nconst char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)\n{\n    const char* text_display_end = text;\n    if (!text_end)\n        text_end = (const char*)-1;\n\n    while (text_display_end < text_end && *text_display_end != '\\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))\n        text_display_end++;\n    return text_display_end;\n}\n\n// Internal ImGui functions to render text\n// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()\nvoid ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Hide anything after a '##' string\n    const char* text_display_end;\n    if (hide_text_after_hash)\n    {\n        text_display_end = FindRenderedTextEnd(text, text_end);\n    }\n    else\n    {\n        if (!text_end)\n            text_end = text + ImStrlen(text); // FIXME-OPT\n        text_display_end = text_end;\n    }\n\n    if (text != text_display_end)\n    {\n        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);\n        if (g.LogEnabled)\n            LogRenderedText(&pos, text, text_display_end);\n    }\n}\n\nvoid ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    if (!text_end)\n        text_end = text + ImStrlen(text); // FIXME-OPT\n\n    if (text != text_end)\n    {\n        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);\n        if (g.LogEnabled)\n            LogRenderedText(&pos, text, text_end);\n    }\n}\n\n// Default clip_rect uses (pos_min,pos_max)\n// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)\n// FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especially for text above draw_list->DrawList.\n// Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take\n// better advantage of the render function taking size into account for coarse clipping.\nvoid ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)\n{\n    // Perform CPU side clipping for single clipped element to avoid using scissor state\n    ImVec2 pos = pos_min;\n    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);\n\n    const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;\n    const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;\n    bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);\n    if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min\n        need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);\n\n    // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.\n    if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);\n    if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);\n\n    // Render\n    if (need_clipping)\n    {\n        ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);\n        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);\n    }\n    else\n    {\n        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);\n    }\n}\n\nvoid ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)\n{\n    // Hide anything after a '##' string\n    const char* text_display_end = FindRenderedTextEnd(text, text_end);\n    const int text_len = (int)(text_display_end - text);\n    if (text_len == 0)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);\n    if (g.LogEnabled)\n        LogRenderedText(&pos_min, text, text_display_end);\n}\n\n// Another overly complex function until we reorganize everything into a nice all-in-one helper.\n// This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) from 'ellipsis_max_x' which may be beyond it.\n// This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.\n// (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceding 'float ellipsis_max' and was the same value for 99% of users.\nvoid ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)\n{\n    ImGuiContext& g = *GImGui;\n    if (text_end_full == NULL)\n        text_end_full = FindRenderedTextEnd(text);\n    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);\n\n    //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 6), IM_COL32(0, 0, 255, 255));\n    //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y - 2), ImVec2(ellipsis_max_x, pos_max.y + 3), IM_COL32(0, 255, 0, 255));\n\n    // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.\n    if (text_size.x > pos_max.x - pos_min.x)\n    {\n        // Hello wo...\n        // |       |   |\n        // min   max   ellipsis_max\n        //          <-> this is generally some padding value\n\n        ImFont* font = draw_list->_Data->Font;\n        const float font_size = draw_list->_Data->FontSize;\n        const float font_scale = draw_list->_Data->FontScale;\n        const char* text_end_ellipsis = NULL;\n        ImFontBaked* baked = font->GetFontBaked(font_size);\n        const float ellipsis_width = baked->GetCharAdvance(font->EllipsisChar) * font_scale;\n\n        // We can now claim the space between pos_max.x and ellipsis_max.x\n        const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);\n        const float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;\n\n        // Render text, render ellipsis\n        RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));\n        ImVec4 cpu_fine_clip_rect(pos_min.x, pos_min.y, pos_max.x, pos_max.y);\n        ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));\n        font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar, &cpu_fine_clip_rect);\n    }\n    else\n    {\n        RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));\n    }\n\n    if (g.LogEnabled)\n        LogRenderedText(&pos_min, text, text_end_full);\n}\n\n// Render a rectangle shaped with optional rounding and borders\nvoid ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders, float rounding)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);\n    const float border_size = g.Style.FrameBorderSize;\n    if (borders && border_size > 0.0f)\n    {\n        window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);\n        window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);\n    }\n}\n\nvoid ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    const float border_size = g.Style.FrameBorderSize;\n    if (border_size > 0.0f)\n    {\n        window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);\n        window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);\n    }\n}\n\nvoid ImGui::RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding)\n{\n    if (bb.Min.x + 1 >= bb.Max.x)\n        return;\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    RenderRectFilledInRangeH(window->DrawList, bb, col, bb.Min.x, ImMin(bb.Min.x + g.Style.ColorMarkerSize, bb.Max.x), rounding);\n}\n\nvoid ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (id != g.NavId)\n        return;\n    if (!g.NavCursorVisible && !(flags & ImGuiNavRenderCursorFlags_AlwaysDraw))\n        return;\n    if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))\n        return;\n\n    // We don't early out on 'window->Flags & ImGuiWindowFlags_NoNavInputs' because it would be inconsistent with\n    // other code directly checking NavCursorVisible. Instead we aim for NavCursorVisible to always be false.\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->DC.NavHideHighlightOneFrame)\n        return;\n\n    float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;\n    ImRect display_rect = bb;\n    display_rect.ClipWith(window->ClipRect);\n    const float thickness = 2.0f;\n    if (flags & ImGuiNavRenderCursorFlags_Compact)\n    {\n        window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);\n    }\n    else\n    {\n        const float distance = 3.0f + thickness * 0.5f;\n        display_rect.Expand(ImVec2(distance, distance));\n        bool fully_visible = window->ClipRect.Contains(display_rect);\n        if (!fully_visible)\n            window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);\n        window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);\n        if (!fully_visible)\n            window->DrawList->PopClipRect();\n    }\n}\n\nvoid ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)\n{\n    ImGuiContext& g = *GImGui;\n    if (mouse_cursor <= ImGuiMouseCursor_None || mouse_cursor >= ImGuiMouseCursor_COUNT) // We intentionally accept out of bound values.\n        mouse_cursor = ImGuiMouseCursor_Arrow;\n    ImFontAtlas* font_atlas = g.DrawListSharedData.FontAtlas;\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        // We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.\n        ImVec2 offset, size, uv[4];\n        if (!ImFontAtlasGetMouseCursorTexData(font_atlas, mouse_cursor, &offset, &size, &uv[0], &uv[2]))\n            continue;\n        const ImVec2 pos = base_pos - offset;\n        const float scale = base_scale * viewport->DpiScale;\n        if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)))\n            continue;\n        ImDrawList* draw_list = GetForegroundDrawList(viewport);\n        ImTextureRef tex_ref = font_atlas->TexRef;\n        draw_list->PushTexture(tex_ref);\n        draw_list->AddImage(tex_ref, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);\n        draw_list->AddImage(tex_ref, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);\n        draw_list->AddImage(tex_ref, pos,                        pos + size * scale,                  uv[2], uv[3], col_border);\n        draw_list->AddImage(tex_ref, pos,                        pos + size * scale,                  uv[0], uv[1], col_fill);\n        if (mouse_cursor == ImGuiMouseCursor_Wait || mouse_cursor == ImGuiMouseCursor_Progress)\n        {\n            float a_min = ImFmod((float)g.Time * 5.0f, 2.0f * IM_PI);\n            float a_max = a_min + IM_PI * 1.65f;\n            draw_list->PathArcTo(pos + ImVec2(14, -1) * scale, 6.0f * scale, a_min, a_max);\n            draw_list->PathStroke(col_fill, ImDrawFlags_None, 3.0f * scale);\n        }\n        draw_list->PopTexture();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] INITIALIZATION, SHUTDOWN\n//-----------------------------------------------------------------------------\n\n// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself\n// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module\nImGuiContext* ImGui::GetCurrentContext()\n{\n    return GImGui;\n}\n\nvoid ImGui::SetCurrentContext(ImGuiContext* ctx)\n{\n#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC\n    IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.\n#else\n    GImGui = ctx;\n#endif\n}\n\nvoid ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)\n{\n    GImAllocatorAllocFunc = alloc_func;\n    GImAllocatorFreeFunc = free_func;\n    GImAllocatorUserData = user_data;\n}\n\n// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)\nvoid ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)\n{\n    *p_alloc_func = GImAllocatorAllocFunc;\n    *p_free_func = GImAllocatorFreeFunc;\n    *p_user_data = GImAllocatorUserData;\n}\n\nImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)\n{\n    ImGuiContext* prev_ctx = GetCurrentContext();\n    ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);\n    SetCurrentContext(ctx);\n    Initialize();\n    if (prev_ctx != NULL)\n        SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one.\n    return ctx;\n}\n\nvoid ImGui::DestroyContext(ImGuiContext* ctx)\n{\n    ImGuiContext* prev_ctx = GetCurrentContext();\n    if (ctx == NULL) //-V1051\n        ctx = prev_ctx;\n    SetCurrentContext(ctx);\n    Shutdown();\n    SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL);\n    IM_DELETE(ctx);\n}\n\n// IMPORTANT: interactive elements requires a fixed ###xxx suffix, it must be same in ALL languages to allow for automation.\nstatic const ImGuiLocEntry GLocalizationEntriesEnUS[] =\n{\n    { ImGuiLocKey_VersionStr,           \"Dear ImGui \" IMGUI_VERSION \" (\" IM_STRINGIFY(IMGUI_VERSION_NUM) \")\" },\n    { ImGuiLocKey_TableSizeOne,         \"Size column to fit###SizeOne\"          },\n    { ImGuiLocKey_TableSizeAllFit,      \"Size all columns to fit###SizeAll\"     },\n    { ImGuiLocKey_TableSizeAllDefault,  \"Size all columns to default###SizeAll\" },\n    { ImGuiLocKey_TableResetOrder,      \"Reset order###ResetOrder\"              },\n    { ImGuiLocKey_WindowingMainMenuBar, \"(Main menu bar)\"                       },\n    { ImGuiLocKey_WindowingPopup,       \"(Popup)\"                               },\n    { ImGuiLocKey_WindowingUntitled,    \"(Untitled)\"                            },\n    { ImGuiLocKey_OpenLink_s,           \"Open '%s'\"                             },\n    { ImGuiLocKey_CopyLink,             \"Copy Link###CopyLink\"                  },\n    { ImGuiLocKey_DockingHideTabBar,            \"Hide tab bar###HideTabBar\"             },\n    { ImGuiLocKey_DockingHoldShiftToDock,       \"Hold SHIFT to enable Docking window.\"  },\n    { ImGuiLocKey_DockingDragToUndockOrMoveNode,\"Click and drag to move or undock whole node.\"    },\n};\n\nImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)\n{\n    IO.Ctx = this;\n    InputTextState.Ctx = this;\n\n    Initialized = false;\n    WithinFrameScope = WithinFrameScopeWithImplicitWindow = false;\n    TestEngineHookItems = false;\n    FrameCount = 0;\n    FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1;\n    Time = 0.0f;\n    memset(ContextName, 0, sizeof(ContextName));\n    ConfigFlagsCurrFrame = ConfigFlagsLastFrame = ImGuiConfigFlags_None;\n\n    Font = NULL;\n    FontBaked = NULL;\n    FontSize = FontSizeBase = FontBakedScale = CurrentDpiScale = 0.0f;\n    FontRasterizerDensity = 1.0f;\n    IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();\n    if (shared_font_atlas == NULL)\n        IO.Fonts->OwnerContext = this;\n    WithinEndChildID = 0;\n    TestEngine = NULL;\n\n    InputEventsNextMouseSource = ImGuiMouseSource_Mouse;\n    InputEventsNextEventId = 1;\n\n    WindowsActiveCount = 0;\n    WindowsBorderHoverPadding = 0.0f;\n    CurrentWindow = NULL;\n    HoveredWindow = NULL;\n    HoveredWindowUnderMovingWindow = NULL;\n    HoveredWindowBeforeClear = NULL;\n    MovingWindow = NULL;\n    WheelingWindow = NULL;\n    WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;\n    WheelingWindowReleaseTimer = 0.0f;\n\n    DebugDrawIdConflictsId = 0;\n    DebugHookIdInfoId = 0;\n    HoveredId = HoveredIdPreviousFrame = 0;\n    HoveredIdPreviousFrameItemCount = 0;\n    HoveredIdAllowOverlap = false;\n    HoveredIdIsDisabled = false;\n    HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;\n    ItemUnclipByLog = false;\n    ActiveId = 0;\n    ActiveIdIsAlive = 0;\n    ActiveIdTimer = 0.0f;\n    ActiveIdIsJustActivated = false;\n    ActiveIdAllowOverlap = false;\n    ActiveIdNoClearOnFocusLoss = false;\n    ActiveIdHasBeenPressedBefore = false;\n    ActiveIdHasBeenEditedBefore = false;\n    ActiveIdHasBeenEditedThisFrame = false;\n    ActiveIdFromShortcut = false;\n    ActiveIdClickOffset = ImVec2(-1, -1);\n    ActiveIdSource = ImGuiInputSource_None;\n    ActiveIdWindow = NULL;\n    ActiveIdMouseButton = -1;\n    ActiveIdDisabledId = 0;\n    ActiveIdPreviousFrame = 0;\n    memset(&DeactivatedItemData, 0, sizeof(DeactivatedItemData));\n    memset(&ActiveIdValueOnActivation, 0, sizeof(ActiveIdValueOnActivation));\n    LastActiveId = 0;\n    LastActiveIdTimer = 0.0f;\n\n    LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;\n\n    ActiveIdUsingNavDirMask = 0x00;\n    ActiveIdUsingAllKeyboardKeys = false;\n\n    CurrentFocusScopeId = 0;\n    CurrentItemFlags = ImGuiItemFlags_None;\n    DebugShowGroupRects = false;\n    GcCompactAll = false;\n\n    CurrentViewport = NULL;\n    MouseViewport = MouseLastHoveredViewport = NULL;\n    PlatformLastFocusedViewportId = 0;\n    ViewportCreatedCount = PlatformWindowsCreatedCount = 0;\n    ViewportFocusedStampCount = 0;\n\n    NavCursorVisible = false;\n    NavHighlightItemUnderNav = false;\n    NavMousePosDirty = false;\n    NavIdIsAlive = false;\n    NavId = 0;\n    NavWindow = NULL;\n    NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;\n    NavLayer = ImGuiNavLayer_Main;\n    NavIdItemFlags = ImGuiItemFlags_None;\n    NavOpenContextMenuItemId = NavOpenContextMenuWindowId = 0;\n    NavNextActivateId = 0;\n    NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;\n    NavHighlightActivatedId = 0;\n    NavHighlightActivatedTimer = 0.0f;\n    NavInputSource = ImGuiInputSource_Keyboard;\n    NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;\n    NavCursorHideFrames = 0;\n\n    NavAnyRequest = false;\n    NavInitRequest = false;\n    NavInitRequestFromMove = false;\n    NavMoveSubmitted = false;\n    NavMoveScoringItems = false;\n    NavMoveForwardToNextFrame = false;\n    NavMoveFlags = ImGuiNavMoveFlags_None;\n    NavMoveScrollFlags = ImGuiScrollFlags_None;\n    NavMoveKeyMods = ImGuiMod_None;\n    NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;\n    NavScoringDebugCount = 0;\n    NavTabbingDir = 0;\n    NavTabbingCounter = 0;\n\n    NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0;\n    NavJustMovedToKeyMods = ImGuiMod_None;\n    NavJustMovedToIsTabbing = false;\n    NavJustMovedToHasSelectionData = false;\n\n    // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...\n    // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..\n    ConfigNavEnableTabbing = true;\n    ConfigNavWindowingWithGamepad = true;\n    ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);\n    ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab);\n    NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;\n    NavWindowingInputSource = ImGuiInputSource_None;\n    NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;\n    NavWindowingToggleLayer = false;\n    NavWindowingToggleKey = ImGuiKey_None;\n\n    DimBgRatio = 0.0f;\n\n    DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;\n    DragDropSourceFlags = ImGuiDragDropFlags_None;\n    DragDropSourceFrameCount = -1;\n    DragDropMouseButton = -1;\n    DragDropTargetId = 0;\n    DragDropTargetFullViewport = 0;\n    DragDropAcceptFlagsCurr = DragDropAcceptFlagsPrev = ImGuiDragDropFlags_None;\n    DragDropAcceptIdCurrRectSurface = 0.0f;\n    DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;\n    DragDropAcceptFrameCount = -1;\n    DragDropHoldJustPressedId = 0;\n    memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));\n\n    ClipperTempDataStacked = 0;\n\n    CurrentTable = NULL;\n    TablesTempDataStacked = 0;\n    CurrentTabBar = NULL;\n    CurrentMultiSelect = NULL;\n    MultiSelectTempDataStacked = 0;\n\n    HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;\n    HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;\n\n    MouseCursor = ImGuiMouseCursor_Arrow;\n    MouseStationaryTimer = 0.0f;\n\n    InputTextPasswordFontBackupFlags = ImFontFlags_None;\n    InputTextReactivateId = 0;\n    TempInputId = 0;\n    memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue));\n    BeginMenuDepth = BeginComboDepth = 0;\n    ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;\n    ColorEditCurrentID = ColorEditSavedID = 0;\n    ColorEditSavedHue = ColorEditSavedSat = 0.0f;\n    ColorEditSavedColor = 0;\n    WindowResizeRelativeMode = false;\n    ScrollbarSeekMode = 0;\n    ScrollbarClickDeltaToGrabCenter = 0.0f;\n    SliderGrabClickOffset = 0.0f;\n    SliderCurrentAccum = 0.0f;\n    SliderCurrentAccumDirty = false;\n    DragCurrentAccumDirty = false;\n    DragCurrentAccum = 0.0f;\n    DragSpeedDefaultRatio = 1.0f / 100.0f;\n    DisabledAlphaBackup = 0.0f;\n    DisabledStackSize = 0;\n    TooltipOverrideCount = 0;\n    TooltipPreviousWindow = NULL;\n\n    PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);\n    PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission\n\n    DockNodeWindowMenuHandler = NULL;\n\n    SettingsLoaded = false;\n    SettingsDirtyTimer = 0.0f;\n    HookIdNext = 0;\n    DemoMarkerCallback = NULL;\n\n    memset(LocalizationTable, 0, sizeof(LocalizationTable));\n\n    LogEnabled = false;\n    LogLineFirstItem = false;\n    LogFlags = ImGuiLogFlags_None;\n    LogWindow = NULL;\n    LogNextPrefix = LogNextSuffix = NULL;\n    LogFile = NULL;\n    LogLinePosY = FLT_MAX;\n    LogDepthRef = 0;\n    LogDepthToExpand = LogDepthToExpandDefault = 2;\n\n    ErrorCallback = NULL;\n    ErrorCallbackUserData = NULL;\n    ErrorFirst = true;\n    ErrorCountCurrentFrame = 0;\n    StackSizesInBeginForCurrentWindow = NULL;\n\n    DebugDrawIdConflictsCount = 0;\n    DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY;\n    DebugLocateId = 0;\n    DebugLogSkippedErrors = 0;\n    DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;\n    DebugLogAutoDisableFrames = 0;\n    DebugLocateFrames = 0;\n    DebugBeginReturnValueCullDepth = -1;\n    DebugItemPickerActive = false;\n    DebugItemPickerMouseButton = ImGuiMouseButton_Left;\n    DebugItemPickerBreakId = 0;\n    DebugFlashStyleColorTime = 0.0f;\n    DebugFlashStyleColorIdx = ImGuiCol_COUNT;\n    DebugHoveredDockNode = NULL;\n\n    // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations\n    DebugBreakInWindow = 0;\n    DebugBreakInTable = 0;\n    DebugBreakInLocateId = false;\n    DebugBreakKeyChord = ImGuiKey_Pause;\n    DebugBreakInShortcutRouting = ImGuiKey_None;\n\n    memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));\n    FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;\n    FramerateSecPerFrameAccum = 0.0f;\n    WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;\n    memset(TempKeychordName, 0, sizeof(TempKeychordName));\n}\n\nImGuiContext::~ImGuiContext()\n{\n    IM_ASSERT(Initialized == false && \"Forgot to call DestroyContext()?\");\n}\n\nvoid ImGui::Initialize()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(!g.Initialized && !g.SettingsLoaded);\n\n    // Add .ini handle for ImGuiWindow and ImGuiTable types\n    {\n        ImGuiSettingsHandler ini_handler;\n        ini_handler.TypeName = \"Window\";\n        ini_handler.TypeHash = ImHashStr(\"Window\");\n        ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;\n        ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;\n        ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;\n        ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;\n        ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;\n        AddSettingsHandler(&ini_handler);\n    }\n    TableSettingsAddSettingsHandler();\n\n    // Setup default localization table\n    LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_COUNTOF(GLocalizationEntriesEnUS));\n\n    // Setup default ImGuiPlatformIO clipboard/IME handlers.\n    g.PlatformIO.Platform_GetClipboardTextFn = Platform_GetClipboardTextFn_DefaultImpl;    // Platform dependent default implementations\n    g.PlatformIO.Platform_SetClipboardTextFn = Platform_SetClipboardTextFn_DefaultImpl;\n    g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl;\n    g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl;\n\n    // Create default viewport\n    ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();\n    viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;\n    viewport->Idx = 0;\n    viewport->PlatformWindowCreated = true;\n    viewport->Flags = ImGuiViewportFlags_OwnedByApp;\n    g.Viewports.push_back(viewport);\n    g.TempBuffer.resize(1024 * 3 + 1, 0);\n    g.ViewportCreatedCount++;\n    g.PlatformIO.Viewports.push_back(g.Viewports[0]);\n\n    // Build KeysMayBeCharInput[] lookup table (1 bit per named key)\n    for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))\n        if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9)\n            || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period\n            || key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent\n            || key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual)\n            g.KeysMayBeCharInput.SetBit(key);\n\n#ifdef IMGUI_HAS_DOCK\n    // Initialize Docking\n    DockContextInitialize(&g);\n#endif\n\n    // Print a debug message when running with debug feature IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS because it is very slow.\n    // DO NOT COMMENT OUT THIS MESSAGE. IT IS DESIGNED TO REMIND YOU THAT IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS SHOULD ONLY BE TEMPORARILY ENABLED.\n#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS\n    DebugLog(\"IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS is enabled.\\nMust disable after use! Otherwise Dear ImGui will run slower.\\n\");\n#endif\n\n    // ImDrawList/ImFontAtlas are designed to function without ImGui, and 99% of it works without an ImGui context.\n    // But this link allows us to facilitate/handle a few edge cases better.\n    ImFontAtlas* atlas = g.IO.Fonts;\n    g.DrawListSharedData.Context = &g;\n    RegisterFontAtlas(atlas);\n\n    g.Initialized = true;\n}\n\n// This function is merely here to free heap allocations.\nvoid ImGui::Shutdown()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, \"Forgot to shutdown Platform backend?\");\n    IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, \"Forgot to shutdown Renderer backend?\");\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        IM_UNUSED(viewport);\n        IM_ASSERT_USER_ERROR(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL, \"Backend or app forgot to call DestroyPlatformWindows()?\");\n    }\n\n    // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)\n    for (ImFontAtlas* atlas : g.FontAtlases)\n    {\n        UnregisterFontAtlas(atlas);\n        if (atlas->RefCount == 0)\n        {\n            atlas->Locked = false;\n            IM_DELETE(atlas);\n        }\n    }\n    g.DrawListSharedData.TempBuffer.clear();\n\n    // Cleanup of other data are conditional on actually having initialized Dear ImGui.\n    if (!g.Initialized)\n        return;\n\n    // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)\n    if (g.SettingsLoaded && g.IO.IniFilename != NULL)\n        SaveIniSettingsToDisk(g.IO.IniFilename);\n\n    // Shutdown extensions\n    DockContextShutdown(&g);\n\n    CallContextHooks(&g, ImGuiContextHookType_Shutdown);\n\n    // Clear everything else\n    g.Windows.clear_delete();\n    g.WindowsFocusOrder.clear();\n    g.WindowsTempSortBuffer.clear();\n    g.CurrentWindow = NULL;\n    g.CurrentWindowStack.clear();\n    g.WindowsById.Clear();\n    g.NavWindow = NULL;\n    g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;\n    g.ActiveIdWindow = NULL;\n    g.MovingWindow = NULL;\n\n    g.KeysRoutingTable.Clear();\n\n    g.ColorStack.clear();\n    g.StyleVarStack.clear();\n    g.FontStack.clear();\n    g.OpenPopupStack.clear();\n    g.BeginPopupStack.clear();\n    g.TreeNodeStack.clear();\n\n    g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL;\n    g.Viewports.clear_delete();\n\n    g.TabBars.Clear();\n    g.CurrentTabBarStack.clear();\n    g.ShrinkWidthBuffer.clear();\n\n    g.ClipperTempData.clear_destruct();\n\n    g.Tables.Clear();\n    g.TablesTempData.clear_destruct();\n    g.DrawChannelsTempMergeBuffer.clear();\n\n    g.MultiSelectStorage.Clear();\n    g.MultiSelectTempData.clear_destruct();\n\n    g.ClipboardHandlerData.clear();\n    g.MenusIdSubmittedThisFrame.clear();\n    g.InputTextState.ClearFreeMemory();\n    g.InputTextLineIndex.clear();\n    g.InputTextDeactivatedState.ClearFreeMemory();\n\n    g.SettingsWindows.clear();\n    g.SettingsHandlers.clear();\n\n    if (g.LogFile)\n    {\n#ifndef IMGUI_DISABLE_TTY_FUNCTIONS\n        if (g.LogFile != stdout)\n#endif\n            ImFileClose(g.LogFile);\n        g.LogFile = NULL;\n    }\n    g.LogBuffer.clear();\n    g.DebugLogBuf.clear();\n    g.DebugLogIndex.clear();\n\n    g.Initialized = false;\n}\n\n// When using multiple context it can be helpful to give name a name.\n// (A) Will be visible in debugger, (B) Will be included in all IMGUI_DEBUG_LOG() calls, (C) Should be <= 15 characters long.\nvoid ImGui::SetContextName(ImGuiContext* ctx, const char* name)\n{\n    ImStrncpy(ctx->ContextName, name, IM_COUNTOF(ctx->ContextName));\n}\n\n// No specific ordering/dependency support, will see as needed\nImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)\n{\n    ImGuiContext& g = *ctx;\n    IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);\n    g.Hooks.push_back(*hook);\n    g.Hooks.back().HookId = ++g.HookIdNext;\n    return g.HookIdNext;\n}\n\n// Deferred removal, avoiding issue with changing vector while iterating it\nvoid ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)\n{\n    ImGuiContext& g = *ctx;\n    IM_ASSERT(hook_id != 0);\n    for (ImGuiContextHook& hook : g.Hooks)\n        if (hook.HookId == hook_id)\n            hook.Type = ImGuiContextHookType_PendingRemoval_;\n}\n\n// Call context hooks (used by e.g. test engine)\n// We assume a small number of hooks so all stored in same array\nvoid ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)\n{\n    ImGuiContext& g = *ctx;\n    for (ImGuiContextHook& hook : g.Hooks)\n        if (hook.Type == hook_type)\n            hook.Callback(&g, &hook);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)\n//-----------------------------------------------------------------------------\n\n// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods\nImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL)\n{\n    memset((void*)this, 0, sizeof(*this));\n    Ctx = ctx;\n    Name = ImStrdup(name);\n    NameBufLen = (int)ImStrlen(name) + 1;\n    ID = ImHashStr(name);\n    IDStack.push_back(ID);\n    ViewportAllowPlatformMonitorExtend = -1;\n    ViewportPos = ImVec2(FLT_MAX, FLT_MAX);\n    MoveId = GetID(\"#MOVE\");\n    TabId = GetID(\"#TAB\");\n    ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);\n    ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);\n    AutoPosLastDirection = ImGuiDir_None;\n    AutoFitFramesX = AutoFitFramesY = -1;\n    SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = 0;\n    SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);\n    LastFrameActive = -1;\n    LastFrameJustFocused = -1;\n    LastTimeActive = -1.0f;\n    FontRefSize = 0.0f;\n    FontWindowScale = FontWindowScaleParents = 1.0f;\n    SettingsOffset = -1;\n    DockOrder = -1;\n    DrawList = &DrawListInst;\n    DrawList->_OwnerName = Name;\n    DrawList->_SetDrawListSharedData(&Ctx->DrawListSharedData);\n    NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);\n    IM_PLACEMENT_NEW(&WindowClass) ImGuiWindowClass();\n}\n\nImGuiWindow::~ImGuiWindow()\n{\n    IM_ASSERT(DrawList == &DrawListInst);\n    IM_DELETE(Name);\n    ColumnsStorage.clear_destruct();\n}\n\nstatic void SetCurrentWindow(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    g.CurrentWindow = window;\n    g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL;\n    g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;\n    if (window)\n    {\n        if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)\n        {\n            ImGuiViewport* viewport = window->Viewport;\n            g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity()\n        }\n        const bool backup_skip_items = window->SkipItems;\n        window->SkipItems = false;\n        ImGui::UpdateCurrentFontSize(0.0f);\n        window->SkipItems = backup_skip_items;\n        ImGui::NavUpdateCurrentWindowIsScrollPushableX();\n    }\n}\n\nvoid ImGui::GcCompactTransientMiscBuffers()\n{\n    ImGuiContext& g = *GImGui;\n    g.ItemFlagsStack.clear();\n    g.GroupStack.clear();\n    g.InputTextLineIndex.clear();\n    g.MultiSelectTempDataStacked = 0;\n    g.MultiSelectTempData.clear_destruct();\n    TableGcCompactSettings();\n    for (ImFontAtlas* atlas : g.FontAtlases)\n        atlas->CompactCache();\n}\n\n// Free up/compact internal window buffers, we can use this when a window becomes unused.\n// Not freed:\n// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)\n// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.\nvoid ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)\n{\n    window->MemoryCompacted = true;\n    window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;\n    window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;\n    window->IDStack.clear();\n    window->DrawList->_ClearFreeMemory();\n    window->DC.ChildWindows.clear();\n    window->DC.ItemWidthStack.clear();\n    window->DC.TextWrapPosStack.clear();\n}\n\nvoid ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)\n{\n    // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.\n    // The other buffers tends to amortize much faster.\n    window->MemoryCompacted = false;\n    window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);\n    window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);\n    window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;\n}\n\nvoid ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Clear previous active id\n    if (g.ActiveId != 0)\n    {\n        // Store deactivate data\n        ImGuiDeactivatedItemData* deactivated_data = &g.DeactivatedItemData;\n        deactivated_data->ID = g.ActiveId;\n        deactivated_data->ElapseFrame = (g.LastItemData.ID == g.ActiveId) ? g.FrameCount : g.FrameCount + 1; // FIXME: OK to use LastItemData?\n        deactivated_data->HasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;\n        deactivated_data->IsAlive = (g.ActiveIdIsAlive == g.ActiveId);\n\n        // This could be written in a more general way (e.g associate a hook to ActiveId),\n        // but since this is currently quite an exception we'll leave it as is.\n        // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveID()\n        if (g.InputTextState.ID == g.ActiveId)\n            InputTextDeactivateHook(g.ActiveId);\n\n        // While most behaved code would make an effort to not steal active id during window move/drag operations,\n        // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch\n        // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that.\n        if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId)\n        {\n            IMGUI_DEBUG_LOG_ACTIVEID(\"SetActiveID() cancel MovingWindow\\n\");\n            StopMouseMovingWindow();\n        }\n    }\n\n    // Set active id\n    g.ActiveIdIsJustActivated = (g.ActiveId != id);\n    if (g.ActiveIdIsJustActivated)\n    {\n        IMGUI_DEBUG_LOG_ACTIVEID(\"SetActiveID() old:0x%08X (window \\\"%s\\\") -> new:0x%08X (window \\\"%s\\\")\\n\", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : \"\", id, window ? window->Name : \"\");\n        g.ActiveIdTimer = 0.0f;\n        g.ActiveIdHasBeenPressedBefore = false;\n        g.ActiveIdHasBeenEditedBefore = false;\n        g.ActiveIdMouseButton = -1;\n        if (id != 0)\n        {\n            g.LastActiveId = id;\n            g.LastActiveIdTimer = 0.0f;\n        }\n    }\n    g.ActiveId = id;\n    g.ActiveIdAllowOverlap = false;\n    g.ActiveIdNoClearOnFocusLoss = false;\n    g.ActiveIdWindow = window;\n    g.ActiveIdHasBeenEditedThisFrame = false;\n    g.ActiveIdFromShortcut = false;\n    g.ActiveIdDisabledId = 0;\n    if (id)\n    {\n        g.ActiveIdIsAlive = id;\n        g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;\n        IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None);\n    }\n\n    // Clear declaration of inputs claimed by the widget\n    // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)\n    g.ActiveIdUsingNavDirMask = 0x00;\n    g.ActiveIdUsingAllKeyboardKeys = false;\n}\n\nvoid ImGui::ClearActiveID()\n{\n    SetActiveID(0, NULL); // g.ActiveId = 0;\n}\n\nvoid ImGui::SetHoveredID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    g.HoveredId = id;\n    g.HoveredIdAllowOverlap = false;\n    if (id != 0 && g.HoveredIdPreviousFrame != id)\n        g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;\n}\n\nImGuiID ImGui::GetHoveredID()\n{\n    ImGuiContext& g = *GImGui;\n    return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;\n}\n\nvoid ImGui::MarkItemEdited(ImGuiID id)\n{\n    // This marking is to be able to provide info for IsItemDeactivatedAfterEdit().\n    // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data.\n    ImGuiContext& g = *GImGui;\n    if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited)\n        return;\n    if (g.ActiveId == id || g.ActiveId == 0)\n    {\n        // FIXME: Can't we fully rely on LastItemData yet?\n        g.ActiveIdHasBeenEditedThisFrame = true;\n        g.ActiveIdHasBeenEditedBefore = true;\n        if (g.DeactivatedItemData.ID == id)\n            g.DeactivatedItemData.HasBeenEditedBefore = true;\n    }\n\n    // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343)\n    // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)\n    // FIXME: This assert is getting a bit meaningless over time. It helped detect some unusual use cases but eventually it is becoming an unnecessary restriction.\n    IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || g.NavJustMovedToId || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive));\n\n    //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);\n    g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;\n}\n\nbool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)\n{\n    // An active popup disable hovering on other windows (apart from its own children)\n    // FIXME-OPT: This could be cached/stored within the window.\n    ImGuiContext& g = *GImGui;\n    if (g.NavWindow)\n        if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindowDockTree)\n            if (focused_root_window->WasActive && focused_root_window != window->RootWindowDockTree)\n            {\n                // For the purpose of those flags we differentiate \"standard popup\" from \"modal popup\"\n                // NB: The 'else' is important because Modal windows are also Popups.\n                bool want_inhibit = false;\n                if (focused_root_window->Flags & ImGuiWindowFlags_Modal)\n                    want_inhibit = true;\n                else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))\n                    want_inhibit = true;\n\n                // Inhibit hover unless the window is within the stack of our modal/popup\n                if (want_inhibit)\n                    if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window))\n                        return false;\n            }\n\n    // Filter by viewport\n    if (window->Viewport != g.MouseViewport)\n        if (g.MovingWindow == NULL || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree)\n            return false;\n\n    return true;\n}\n\nstatic inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (flags & ImGuiHoveredFlags_DelayNormal)\n        return g.Style.HoverDelayNormal;\n    if (flags & ImGuiHoveredFlags_DelayShort)\n        return g.Style.HoverDelayShort;\n    return 0.0f;\n}\n\nstatic ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags)\n{\n    // Allow instance flags to override shared flags\n    if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal))\n        shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal);\n    return user_flags | shared_flags;\n}\n\n// This is roughly matching the behavior of internal-facing ItemHoverable()\n// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()\n// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId\nbool ImGui::IsItemHovered(ImGuiHoveredFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0, \"Invalid flags for IsItemHovered()!\");\n\n    if (g.NavHighlightItemUnderNav && g.NavCursorVisible && !(flags & ImGuiHoveredFlags_NoNavOverride))\n    {\n        if (!IsItemFocused())\n            return false;\n        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))\n            return false;\n\n        if (flags & ImGuiHoveredFlags_ForTooltip)\n            flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav);\n    }\n    else\n    {\n        // Test for bounding box overlap, as updated as ItemAdd()\n        ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;\n        if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))\n            return false;\n\n        if (flags & ImGuiHoveredFlags_ForTooltip)\n            flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);\n\n        // Done with rectangle culling so we can perform heavier checks now\n        // Test if we are hovering the right window (our window could be behind another window)\n        // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)\n        // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable\n        // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was\n        // the test that has been running for a long while.\n        if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)\n            if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0)\n                return false;\n\n        // Test if another item is active (e.g. being dragged)\n        const ImGuiID id = g.LastItemData.ID;\n        if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)\n            if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap && !g.ActiveIdFromShortcut)\n            {\n                // When ActiveId == MoveId it means that either:\n                // - (1) user clicked on void _or_ an item with no id, which triggers moving window (ActiveId is set even when window has _NoMove flag)\n                //   - the (id == 0) test handles it, however, IsItemHovered() will leak between id==0 items (mostly visible when using _NoMove). // FIXME: May be fixed.\n                // - (2) user clicked a disabled item. UpdateMouseMovingWindowEndFrame() uses ActiveId == MoveId to avoid interference with item logic + sets ActiveIdDisabledId.\n                bool cancel_is_hovered = true;\n                if (g.ActiveId == window->MoveId && (id == 0 || g.ActiveIdDisabledId == id))\n                    cancel_is_hovered = false;\n                // When ActiveId == TabId it means user clicked docking tab for the window.\n                if (g.ActiveId == window->TabId)\n                    cancel_is_hovered = false;\n                if (cancel_is_hovered)\n                    return false;\n            }\n\n        // Test if interactions on this window are blocked by an active popup or modal.\n        // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.\n        if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_NoWindowHoverableCheck))\n            return false;\n\n        // Test if the item is disabled\n        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))\n            return false;\n\n        // Special handling for calling after Begin() which represent the title bar or tab.\n        // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin)\n        // will never be overwritten so we need to detect the case.\n        if (id == window->MoveId && window->WriteAccessed)\n            return false;\n\n        // Test if using AllowOverlap and overlapped\n        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap) && id != 0)\n            if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)\n                if (g.HoveredIdPreviousFrame != g.LastItemData.ID)\n                    return false;\n    }\n\n    // Handle hover delay\n    // (some ideas: https://www.nngroup.com/articles/timing-exposing-content)\n    const float delay = CalcDelayFromHoveredFlags(flags);\n    if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))\n    {\n        ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromPos(g.LastItemData.Rect.Min);\n        if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))\n            g.HoverItemDelayTimer = 0.0f;\n        g.HoverItemDelayId = hover_delay_id;\n\n        // When changing hovered item we requires a bit of stationary delay before activating hover timer,\n        // but once unlocked on a given item we also moving.\n        //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG(\"HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\\n\", g.HoverDelayTimer, delay, g.MouseStationaryTimer); }\n        if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)\n            return false;\n\n        if (g.HoverItemDelayTimer < delay)\n            return false;\n    }\n\n    return true;\n}\n\n// Internal facing ItemHoverable() used when submitting widgets. THIS IS A SUBMISSION NOT A HOVER CHECK.\n// Returns whether the item was hovered, logic differs slightly from IsItemHovered().\n// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)\n// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.\n// If you used this in your legacy/custom widgets code:\n// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.ItemFlags'.\n// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.\nbool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Detect ID conflicts\n    // (this is specifically done here by comparing on hover because it allows us a detection of duplicates that is algorithmically extra cheap, 1 u32 compare per item. No O(log N) lookup whatsoever)\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (id != 0 && g.HoveredIdPreviousFrame == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0)\n    {\n        g.HoveredIdPreviousFrameItemCount++;\n        if (g.DebugDrawIdConflictsId == id)\n            window->DrawList->AddRect(bb.Min - ImVec2(1,1), bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f);\n    }\n#endif\n\n    if (g.HoveredWindow != window)\n        return false;\n    if (!IsMouseHoveringRect(bb.Min, bb.Max))\n        return false;\n\n    if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)\n        return false;\n    if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)\n        if (!g.ActiveIdFromShortcut)\n            return false;\n\n    // We are done with rectangle culling so we can perform heavier checks now.\n    if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))\n    {\n        g.HoveredIdIsDisabled = true;\n        return false;\n    }\n\n    // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level\n    // hover test in widgets code. We could also decide to split this function is two.\n    if (id != 0)\n    {\n        // Drag source doesn't report as hovered\n        if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))\n            return false;\n\n        SetHoveredID(id);\n\n        // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match.\n        // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.\n        if (item_flags & ImGuiItemFlags_AllowOverlap)\n        {\n            g.HoveredIdAllowOverlap = true;\n            if (g.HoveredIdPreviousFrame != id)\n                return false;\n        }\n\n        // Display shortcut (only works with mouse)\n        // (ImGuiItemStatusFlags_HasShortcut in LastItemData denotes we want a tooltip)\n        if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut) && g.ActiveId != id)\n            if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal))\n                SetTooltip(\"%s\", GetKeyChordName(g.LastItemData.Shortcut));\n    }\n\n    // When disabled we'll return false but still set HoveredId\n    if (item_flags & ImGuiItemFlags_Disabled)\n    {\n        // Release active id if turning disabled\n        if (g.ActiveId == id && id != 0)\n            ClearActiveID();\n        g.HoveredIdIsDisabled = true;\n        return false;\n    }\n\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (id != 0)\n    {\n        // [DEBUG] Item Picker tool!\n        // We perform the check here because reaching is path is rare (1~ time a frame),\n        // making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered\n        // items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost.\n        if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)\n            GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));\n        if (g.DebugItemPickerBreakId == id)\n            IM_DEBUG_BREAK();\n    }\n#endif\n\n    if (g.NavHighlightItemUnderNav && (item_flags & ImGuiItemFlags_NoNavDisableMouseHover) == 0)\n        return false;\n\n    return true;\n}\n\n// FIXME: This is inlined/duplicated in ItemAdd()\n// FIXME: The id != 0 path is not used by our codebase, may get rid of it?\nbool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (!bb.Overlaps(window->ClipRect))\n        if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))\n            if (!g.ItemUnclipByLog)\n                return true;\n    return false;\n}\n\n// This is also inlined in ItemAdd()\n// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect.\nvoid ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect)\n{\n    ImGuiContext& g = *GImGui;\n    g.LastItemData.ID = item_id;\n    g.LastItemData.ItemFlags = item_flags;\n    g.LastItemData.StatusFlags = status_flags;\n    g.LastItemData.Rect = g.LastItemData.NavRect = item_rect;\n}\n\nstatic void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect)\n{\n    ImGuiContext& g = *GImGui;\n    if (window->DockIsActive)\n        SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.DockTabItemStatusFlags, window->DC.DockTabItemRect);\n    else\n        SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.WindowItemStatusFlags, rect);\n}\n\nstatic void ImGui::SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect)\n{\n    ImGuiContext& g = *GImGui;\n    SetLastItemData(window->ChildId, g.CurrentItemFlags, window->DC.ChildItemStatusFlags, rect);\n}\n\nfloat ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)\n{\n    if (wrap_pos_x < 0.0f)\n        return 0.0f;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (wrap_pos_x == 0.0f)\n    {\n        // We could decide to setup a default wrapping max point for auto-resizing windows,\n        // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?\n        //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))\n        //    wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);\n        //else\n        wrap_pos_x = window->WorkRect.Max.x;\n    }\n    else if (wrap_pos_x > 0.0f)\n    {\n        wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space\n    }\n\n    return ImMax(wrap_pos_x - pos.x, 1.0f);\n}\n\n// IM_ALLOC() == ImGui::MemAlloc()\nvoid* ImGui::MemAlloc(size_t size)\n{\n    void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (ImGuiContext* ctx = GImGui)\n        DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size);\n#endif\n    return ptr;\n}\n\n// IM_FREE() == ImGui::MemFree()\nvoid ImGui::MemFree(void* ptr)\n{\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (ptr != NULL)\n        if (ImGuiContext* ctx = GImGui)\n            DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1);\n#endif\n    return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);\n}\n\nvoid ImGui::DemoMarker(const char* file, int line, const char* section)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.DemoMarkerCallback != NULL)\n        g.DemoMarkerCallback(file, line, section);\n}\n\n// We record the number of allocation in recent frames, as a way to audit/sanitize our guiding principles of \"no allocations on idle/repeating frames\"\nvoid ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size)\n{\n    ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx];\n    IM_UNUSED(ptr);\n    if (entry->FrameCount != frame_count)\n    {\n        info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_COUNTOF(info->LastEntriesBuf);\n        entry = &info->LastEntriesBuf[info->LastEntriesIdx];\n        entry->FrameCount = frame_count;\n        entry->AllocCount = entry->FreeCount = 0;\n    }\n    if (size != (size_t)-1)\n    {\n        //printf(\"[%05d] MemAlloc(%d) -> 0x%p\\n\", frame_count, (int)size, ptr);\n        entry->AllocCount++;\n        info->TotalAllocCount++;\n    }\n    else\n    {\n        //printf(\"[%05d] MemFree(0x%p)\\n\", frame_count, ptr);\n        entry->FreeCount++;\n        info->TotalFreeCount++;\n    }\n}\n\n// A conformant backend should return NULL on failure (e.g. clipboard data is not text).\nconst char* ImGui::GetClipboardText()\n{\n    ImGuiContext& g = *GImGui;\n    return g.PlatformIO.Platform_GetClipboardTextFn ? g.PlatformIO.Platform_GetClipboardTextFn(&g) : NULL;\n}\n\nvoid ImGui::SetClipboardText(const char* text)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)\n        g.PlatformIO.Platform_SetClipboardTextFn(&g, text);\n}\n\nconst char* ImGui::GetVersion()\n{\n    return IMGUI_VERSION;\n}\n\nImGuiIO& ImGui::GetIO()\n{\n    IM_ASSERT(GImGui != NULL && \"No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?\");\n    return GImGui->IO;\n}\n\n// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)\nImGuiIO& ImGui::GetIO(ImGuiContext* ctx)\n{\n    IM_ASSERT(ctx != NULL);\n    return ctx->IO;\n}\n\nImGuiPlatformIO& ImGui::GetPlatformIO()\n{\n    IM_ASSERT(GImGui != NULL && \"No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext()?\");\n    return GImGui->PlatformIO;\n}\n\n// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)\nImGuiPlatformIO& ImGui::GetPlatformIO(ImGuiContext* ctx)\n{\n    IM_ASSERT(ctx != NULL);\n    return ctx->PlatformIO;\n}\n\n// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()\nImDrawData* ImGui::GetDrawData()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiViewportP* viewport = g.Viewports[0];\n    return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;\n}\n\ndouble ImGui::GetTime()\n{\n    return GImGui->Time;\n}\n\nint ImGui::GetFrameCount()\n{\n    return GImGui->FrameCount;\n}\n\nstatic ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)\n{\n    // Create the draw list on demand, because they are not frequently used for all viewports\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(drawlist_no < IM_COUNTOF(viewport->BgFgDrawLists));\n    ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no];\n    if (draw_list == NULL)\n    {\n        draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);\n        draw_list->_OwnerName = drawlist_name;\n        viewport->BgFgDrawLists[drawlist_no] = draw_list;\n    }\n\n    // Our ImDrawList system requires that there is always a command\n    if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount)\n    {\n        draw_list->_ResetForNewFrame();\n        draw_list->PushTexture(g.IO.Fonts->TexRef);\n        draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);\n        viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount;\n    }\n    return draw_list;\n}\n\nImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)\n{\n    if (viewport == NULL)\n        viewport = GImGui->CurrentWindow->Viewport;\n    return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, \"##Background\");\n}\n\nImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)\n{\n    if (viewport == NULL)\n        viewport = GImGui->CurrentWindow->Viewport;\n    return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, \"##Foreground\");\n}\n\nImDrawListSharedData* ImGui::GetDrawListSharedData()\n{\n    return &GImGui->DrawListSharedData;\n}\n\nvoid ImGui::StartMouseMovingWindow(ImGuiWindow* window)\n{\n    // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.\n    // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.\n    // This is because we want ActiveId to be set even when the window is not permitted to move.\n    ImGuiContext& g = *GImGui;\n    FocusWindow(window);\n    SetActiveID(window->MoveId, window);\n    if (g.IO.ConfigNavCursorVisibleAuto)\n        g.NavCursorVisible = false;\n    g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos;\n    g.ActiveIdNoClearOnFocusLoss = true;\n    SetActiveIdUsingAllKeyboardKeys();\n\n    bool can_move_window = true;\n    if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoMove))\n        can_move_window = false;\n    if (ImGuiDockNode* node = window->DockNodeAsHost)\n        if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove))\n            can_move_window = false;\n    if (can_move_window)\n        g.MovingWindow = window;\n}\n\n// We use 'undock == false' when dragging from title bar to allow moving groups of floating nodes without undocking them.\nvoid ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock)\n{\n    ImGuiContext& g = *GImGui;\n    bool can_undock_node = false;\n    if (undock && node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0 && (node->MergedFlags & ImGuiDockNodeFlags_NoUndocking) == 0)\n    {\n        // Can undock if:\n        // - part of a hierarchy with more than one visible node (if only one is visible, we'll just move the root window)\n        // - part of a dockspace node hierarchy: so we can undock the last single visible node too. Undocking from a fixed/central node will create a new node and copy windows.\n        ImGuiDockNode* root_node = DockNodeGetRootNode(node);\n        if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL)   // -V1051 PVS-Studio thinks node should be root_node and is wrong about that.\n            can_undock_node = true;\n    }\n\n    const bool clicked = IsMouseClicked(0);\n    const bool dragging = IsMouseDragging(0);\n    if (can_undock_node && dragging)\n        DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame\n    else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window)\n        StartMouseMovingWindow(window);\n}\n\n// This is not 100% symmetric with StartMouseMovingWindow().\n// We do NOT clear ActiveID, because:\n// - It would lead to rather confusing recursive code paths. Caller can call ClearActiveID() if desired.\n// - Some code intentionally cancel moving but keep the ActiveID to lock inputs (e.g. code path taken when clicking a disabled item).\nvoid ImGui::StopMouseMovingWindow()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.MovingWindow;\n\n    // Ref commits 6b7766817, 36055213c for some partial history on checking if viewport != NULL.\n    if (window && window->Viewport)\n    {\n        // Try to merge the window back into the main viewport.\n        // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports)\n        if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)\n            UpdateTryMergeWindowIntoHostViewport(window->RootWindowDockTree, g.MouseViewport);\n\n        // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button.\n        if (!IsDragDropPayloadBeingAccepted())\n            g.MouseViewport = window->Viewport;\n\n        // Clear the NoInputs window flag set by the Viewport system in AddUpdateViewport()\n        const bool window_can_use_inputs = ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) == false;\n        if (window_can_use_inputs)\n            window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs;\n    }\n    g.MovingWindow = NULL;\n}\n\n// Handle mouse moving window\n// Note: moving window with the navigation keys (Square + d-pad / Ctrl+Tab + Arrows) are processed in NavUpdateWindowing()\n// FIXME: We don't have strong guarantee that g.MovingWindow stay synced with g.ActiveId == g.MovingWindow->MoveId.\n// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,\n// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.\nvoid ImGui::UpdateMouseMovingWindowNewFrame()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.MovingWindow != NULL)\n    {\n        // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).\n        // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.\n        KeepAliveID(g.ActiveId);\n        IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree);\n        ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree;\n\n        // When a window stop being submitted while being dragged, it may will its viewport until next Begin()\n        const bool window_disappeared = (!moving_window->WasActive && !moving_window->Active);\n        if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappeared)\n        {\n            ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;\n            if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)\n            {\n                SetWindowPos(moving_window, pos, ImGuiCond_Always);\n                if (moving_window->Viewport && moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window.\n                {\n                    moving_window->Viewport->Pos = pos;\n                    moving_window->Viewport->UpdateWorkRect();\n                }\n            }\n            FocusWindow(g.MovingWindow);\n        }\n        else\n        {\n            StopMouseMovingWindow();\n            ClearActiveID();\n        }\n    }\n    else\n    {\n        // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.\n        if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)\n        {\n            KeepAliveID(g.ActiveId);\n            if (!g.IO.MouseDown[0])\n                ClearActiveID();\n        }\n    }\n}\n\n// Initiate focusing and moving window when clicking on empty space or title bar.\n// Initiate focusing window when clicking on a disabled item.\n// Handle left-click and right-click focus.\nvoid ImGui::UpdateMouseMovingWindowEndFrame()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveId != 0 || (g.HoveredId != 0 && !g.HoveredIdIsDisabled))\n        return;\n\n    // Unless we just made a window/popup appear\n    if (g.NavWindow && g.NavWindow->Appearing)\n        return;\n\n    ImGuiWindow* hovered_window = g.HoveredWindow;\n\n    // Click on empty space to focus window and start moving\n    // (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!)\n    if (g.IO.MouseClicked[0])\n    {\n        // Handle the edge case of a popup being closed while clicking in its empty space.\n        // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.\n        ImGuiWindow* hovered_root = hovered_window ? hovered_window->RootWindow : NULL;\n        const bool is_closed_popup = hovered_root && (hovered_root->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(hovered_root->PopupId, ImGuiPopupFlags_AnyPopupLevel);\n\n        if (hovered_window != NULL && !is_closed_popup)\n        {\n            StartMouseMovingWindow(hovered_window); //-V595\n\n            // FIXME: In principle we might be able to call StopMouseMovingWindow() below.\n            // Please note how StartMouseMovingWindow() and StopMouseMovingWindow() and not entirely symmetrical, at the later doesn't clear ActiveId.\n\n            // Cancel moving if clicked outside of title bar\n            if ((hovered_window->BgClickFlags & ImGuiWindowBgClickFlags_Move) == 0) // set by io.ConfigWindowsMoveFromTitleBarOnly\n                if (!(hovered_root->Flags & ImGuiWindowFlags_NoTitleBar) || hovered_root->DockIsActive)\n                    if (!hovered_root->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))\n                        g.MovingWindow = NULL;\n\n            // Cancel moving if clicked over an item which was disabled or inhibited by popups\n            // (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item)\n            if (g.HoveredIdIsDisabled)\n            {\n                g.MovingWindow = NULL;\n                g.ActiveIdDisabledId = g.HoveredId;\n            }\n        }\n        else if (hovered_window == NULL && g.NavWindow != NULL)\n        {\n            // Clicking on void disable focus\n            FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal);\n        }\n    }\n\n    // With right mouse button we close popups without changing focus based on where the mouse is aimed\n    // Instead, focus will be restored to the window under the bottom-most closed popup.\n    // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)\n    if (g.IO.MouseClicked[1] && g.HoveredId == 0)\n    {\n        // Find the top-most window between HoveredWindow and the top-most Modal Window.\n        // This is where we can trim the popup stack.\n        ImGuiWindow* modal = GetTopMostPopupModal();\n        bool hovered_window_above_modal = hovered_window && (modal == NULL || IsWindowAbove(hovered_window, modal));\n        ClosePopupsOverWindow(hovered_window_above_modal ? hovered_window : modal, true);\n    }\n}\n\n// This is called during NewFrame()->UpdateViewportsNewFrame() only.\n// Need to keep in sync with SetWindowPos()\nstatic void TranslateWindow(ImGuiWindow* window, const ImVec2& delta)\n{\n    window->Pos += delta;\n    window->ClipRect.Translate(delta);\n    window->OuterRectClipped.Translate(delta);\n    window->InnerRect.Translate(delta);\n    window->DC.CursorPos += delta;\n    window->DC.CursorStartPos += delta;\n    window->DC.CursorMaxPos += delta;\n    window->DC.IdealMaxPos += delta;\n}\n\nstatic void ScaleWindow(ImGuiWindow* window, float scale)\n{\n    ImVec2 origin = window->Viewport->Pos;\n    window->Pos = ImFloor((window->Pos - origin) * scale + origin);\n    window->Size = ImTrunc(window->Size * scale);\n    window->SizeFull = ImTrunc(window->SizeFull * scale);\n    window->ContentSize = ImTrunc(window->ContentSize * scale);\n}\n\nstatic bool IsWindowActiveAndVisible(ImGuiWindow* window)\n{\n    return window->Active && !window->Hidden;\n}\n\n// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)\nvoid ImGui::UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    // FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING\n    // by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow.\n    g.WindowsBorderHoverPadding = ImMax(ImMax(g.Style.TouchExtraPadding.x, g.Style.TouchExtraPadding.y), g.Style.WindowBorderHoverPadding);\n\n    // Find the window hovered by mouse:\n    // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.\n    // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame.\n    // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.\n    bool clear_hovered_windows = false;\n    FindHoveredWindowEx(mouse_pos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow);\n    IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport);\n    g.HoveredWindowBeforeClear = g.HoveredWindow;\n\n    // Modal windows prevents mouse from hovering behind them.\n    ImGuiWindow* modal_window = GetTopMostPopupModal();\n    if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) // FIXME-MERGE: RootWindowDockTree ?\n        clear_hovered_windows = true;\n\n    // Disabled mouse hovering (we don't currently clear MousePos, we could)\n    if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)\n        clear_hovered_windows = true;\n\n    // We track click ownership. When clicked outside of a window the click is owned by the application and\n    // won't report hovering nor request capture even while dragging over our windows afterward.\n    const bool has_open_popup = (g.OpenPopupStack.Size > 0);\n    const bool has_open_modal = (modal_window != NULL);\n    int mouse_earliest_down = -1;\n    bool mouse_any_down = false;\n    for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++)\n    {\n        if (io.MouseClicked[i])\n        {\n            io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;\n            io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;\n        }\n        mouse_any_down |= io.MouseDown[i];\n        if (io.MouseDown[i] || io.MouseReleased[i]) // Increase release frame for our evaluation of earliest button (#1392)\n            if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])\n                mouse_earliest_down = i;\n    }\n    const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];\n    const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];\n\n    // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.\n    // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)\n    const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;\n    if (!mouse_avail && !mouse_dragging_extern_payload)\n        clear_hovered_windows = true;\n\n    if (clear_hovered_windows)\n        g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;\n\n    // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)\n    // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag\n    if (g.WantCaptureMouseNextFrame != -1)\n    {\n        io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);\n    }\n    else\n    {\n        io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;\n        io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;\n    }\n\n    // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)\n    io.WantCaptureKeyboard = false;\n    if ((io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) == 0)\n    {\n        if ((g.ActiveId != 0) || (modal_window != NULL))\n            io.WantCaptureKeyboard = true;\n        else if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && io.ConfigNavCaptureKeyboard)\n            io.WantCaptureKeyboard = true;\n    }\n    if (g.WantCaptureKeyboardNextFrame != -1) // Manual override\n        io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);\n\n    // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible\n    io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;\n}\n\n// Called once a frame. Followed by SetCurrentFont() which sets up the remaining data.\n// FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!\nstatic void SetupDrawListSharedData()\n{\n    ImGuiContext& g = *GImGui;\n    ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);\n    for (ImGuiViewportP* viewport : g.Viewports)\n        virtual_space.Add(viewport->GetMainRect());\n    g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();\n    g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;\n    g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);\n    g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;\n    if (g.Style.AntiAliasedLines)\n        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;\n    if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines))\n        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;\n    if (g.Style.AntiAliasedFill)\n        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;\n    if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)\n        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;\n    g.DrawListSharedData.InitialFringeScale = 1.0f; // FIXME-DPI: Change this for some DPI scaling experiments.\n}\n\nvoid ImGui::NewFrame()\n{\n    IM_ASSERT(GImGui != NULL && \"No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?\");\n    ImGuiContext& g = *GImGui;\n\n    // Remove pending delete hooks before frame start.\n    // This deferred removal avoid issues of removal while iterating the hook vector\n    for (int n = g.Hooks.Size - 1; n >= 0; n--)\n        if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)\n            g.Hooks.erase(&g.Hooks[n]);\n\n    CallContextHooks(&g, ImGuiContextHookType_NewFramePre);\n\n    // Check and assert for various common IO and Configuration mistakes\n    g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame;\n    ErrorCheckNewFrameSanityChecks();\n    g.ConfigFlagsCurrFrame = g.IO.ConfigFlags;\n\n    // Load settings on first frame, save settings when modified (after a delay)\n    UpdateSettings();\n\n    g.Time += g.IO.DeltaTime;\n    g.FrameCount += 1;\n    g.TooltipOverrideCount = 0;\n    g.WindowsActiveCount = 0;\n    g.MenusIdSubmittedThisFrame.resize(0);\n\n    // Calculate frame-rate for the user, as a purely luxurious feature\n    g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];\n    g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;\n    g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_COUNTOF(g.FramerateSecPerFrame);\n    g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_COUNTOF(g.FramerateSecPerFrame));\n    g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;\n\n    // Process input queue (trickle as many events as possible), turn events into writes to IO structure\n    g.InputEventsTrail.resize(0);\n    UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue);\n\n    // Update viewports (after processing input queue, so io.MouseHoveredViewport is set)\n    UpdateViewportsNewFrame();\n\n    // Update texture list (collect destroyed textures, etc.)\n    UpdateTexturesNewFrame();\n\n    // Setup current font and draw list shared data\n    SetupDrawListSharedData();\n    UpdateFontsNewFrame();\n\n    g.WithinFrameScope = true;\n\n    // Mark rendering data as invalid to prevent user who may have a handle on it to use it.\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        viewport->DrawData = NULL;\n        viewport->DrawDataP.Valid = false;\n    }\n\n    // Drag and drop keep the source ID alive so even if the source disappear our state is consistent\n    if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)\n        KeepAliveID(g.DragDropPayload.SourceId);\n\n    // [DEBUG]\n    if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding Ctrl\n        g.DebugDrawIdConflictsId = 0;\n    if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1)\n        g.DebugDrawIdConflictsId = g.HoveredIdPreviousFrame;\n\n    // Update HoveredId data\n    if (!g.HoveredIdPreviousFrame)\n        g.HoveredIdTimer = 0.0f;\n    if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))\n        g.HoveredIdNotActiveTimer = 0.0f;\n    if (g.HoveredId)\n        g.HoveredIdTimer += g.IO.DeltaTime;\n    if (g.HoveredId && g.ActiveId != g.HoveredId)\n        g.HoveredIdNotActiveTimer += g.IO.DeltaTime;\n    g.HoveredIdPreviousFrame = g.HoveredId;\n    g.HoveredIdPreviousFrameItemCount = 0;\n    g.HoveredId = 0;\n    g.HoveredIdAllowOverlap = false;\n    g.HoveredIdIsDisabled = false;\n\n    // Clear ActiveID if the item is not alive anymore.\n    // In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd().\n    // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves.\n    if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId)\n    {\n        IMGUI_DEBUG_LOG_ACTIVEID(\"NewFrame(): ClearActiveID() because it isn't marked alive anymore!\\n\");\n        ClearActiveID();\n    }\n\n    // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)\n    if (g.ActiveId)\n        g.ActiveIdTimer += g.IO.DeltaTime;\n    g.LastActiveIdTimer += g.IO.DeltaTime;\n    g.ActiveIdPreviousFrame = g.ActiveId;\n    g.ActiveIdIsAlive = 0;\n    g.ActiveIdHasBeenEditedThisFrame = false;\n    g.ActiveIdIsJustActivated = false;\n    if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)\n        g.TempInputId = 0;\n    if (g.InputTextReactivateId != 0 && g.InputTextReactivateId != g.DeactivatedItemData.ID)\n        g.InputTextReactivateId = 0;\n    if (g.ActiveId == 0)\n    {\n        g.ActiveIdUsingNavDirMask = 0x00;\n        g.ActiveIdUsingAllKeyboardKeys = false;\n    }\n    if (g.DeactivatedItemData.ElapseFrame < g.FrameCount)\n        g.DeactivatedItemData.ID = 0;\n    g.DeactivatedItemData.IsAlive = false;\n\n    // Record when we have been stationary as this state is preserved while over same item.\n    // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.\n    // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.\n    if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)\n        g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;\n    else if (g.HoverItemDelayId == 0)\n        g.HoverItemUnlockedStationaryId = 0;\n    if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)\n        g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;\n    else if (g.HoveredWindow == NULL)\n        g.HoverWindowUnlockedStationaryId = 0;\n\n    // Update hover delay for IsItemHovered() with delays and tooltips\n    g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;\n    if (g.HoverItemDelayId != 0)\n    {\n        g.HoverItemDelayTimer += g.IO.DeltaTime;\n        g.HoverItemDelayClearTimer = 0.0f;\n        g.HoverItemDelayId = 0;\n    }\n    else if (g.HoverItemDelayTimer > 0.0f)\n    {\n        // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps\n        // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.\n        g.HoverItemDelayClearTimer += g.IO.DeltaTime;\n        if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate\n            g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer.\n    }\n\n    // Close popups on focus lost (currently wip/opt-in)\n    //if (g.IO.AppFocusLost)\n    //    ClosePopupsExceptModals();\n\n    // Update keyboard input state\n    UpdateKeyboardInputs();\n\n    //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl));\n    //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift));\n    //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));\n    //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));\n\n    // Drag and drop\n    g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;\n    g.DragDropAcceptIdCurr = 0;\n    g.DragDropAcceptFlagsPrev = g.DragDropAcceptFlagsCurr;\n    g.DragDropAcceptFlagsCurr = ImGuiDragDropFlags_None;\n    g.DragDropAcceptIdCurrRectSurface = FLT_MAX;\n    g.DragDropWithinSource = false;\n    g.DragDropWithinTarget = false;\n    g.DragDropHoldJustPressedId = 0;\n    if (g.DragDropActive)\n    {\n        // Also works when g.ActiveId==0 (aka leftover payload in progress, no active id)\n        // You may disable this externally by hijacking the input route:\n        //  'if (GetDragDropPayload() != NULL) { Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive); }\n        // but you will not get a return value from Shortcut() due to ActiveIdUsingAllKeyboardKeys logic. You can however poll IsKeyPressed(ImGuiKey_Escape) afterwards.\n        ImGuiID owner_id = g.ActiveId ? g.ActiveId : ImHashStr(\"##DragDropCancelHandler\");\n        if (Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteGlobal, owner_id))\n        {\n            ClearActiveID();\n            ClearDragDrop();\n        }\n    }\n    g.TooltipPreviousWindow = NULL;\n\n    // Update keyboard/gamepad navigation\n    NavUpdate();\n\n    // Update mouse input state\n    UpdateMouseInputs();\n\n    // Undocking\n    // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame)\n    DockContextNewFrameUpdateUndocking(&g);\n\n    // Mark all windows as not visible and compact unused memory.\n    IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);\n    const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;\n    for (ImGuiWindow* window : g.Windows)\n    {\n        window->WasActive = window->Active;\n        window->Active = false;\n        window->WriteAccessed = false;\n        window->BeginCountPreviousFrame = window->BeginCount;\n        window->BeginCount = 0;\n\n        // Garbage collect transient buffers of recently unused windows\n        if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)\n            GcCompactTransientWindowBuffers(window);\n    }\n\n    // Find hovered window\n    // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)\n    // (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active)\n    UpdateHoveredWindowAndCaptureFlags(g.IO.MousePos);\n\n    // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)\n    UpdateMouseMovingWindowNewFrame();\n\n    // Background darkening/whitening\n    if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))\n        g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);\n    else\n        g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);\n\n    g.MouseCursor = ImGuiMouseCursor_Arrow;\n    g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;\n\n    // Platform IME data: reset for the frame\n    g.PlatformImeDataPrev = g.PlatformImeData;\n    g.PlatformImeData.WantVisible = g.PlatformImeData.WantTextInput = false;\n\n    // Mouse wheel scrolling, scale\n    UpdateMouseWheel();\n\n    // Garbage collect transient buffers of recently unused tables\n    for (int i = 0; i < g.TablesLastTimeActive.Size; i++)\n        if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)\n            TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));\n    for (ImGuiTableTempData& table_temp_data : g.TablesTempData)\n        if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)\n            TableGcCompactTransientBuffers(&table_temp_data);\n    if (g.GcCompactAll)\n        GcCompactTransientMiscBuffers();\n    g.GcCompactAll = false;\n\n    // Closing the focused window restore focus to the first active root window in descending z-order\n    if (g.NavWindow && !g.NavWindow->WasActive)\n        FocusTopMostWindowUnderOne(NULL, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild);\n\n    // No window should be open at the beginning of the frame.\n    // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.\n    g.CurrentWindowStack.resize(0);\n    g.BeginPopupStack.resize(0);\n    g.ItemFlagsStack.resize(0);\n    g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags\n    g.CurrentItemFlags = g.ItemFlagsStack.back();\n    g.GroupStack.resize(0);\n\n    // Docking\n    DockContextNewFrameUpdateDocking(&g);\n\n    // [DEBUG] Update debug features\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    UpdateDebugToolItemPicker();\n    UpdateDebugToolItemPathQuery();\n    UpdateDebugToolFlashStyleColor();\n    if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)\n    {\n        g.DebugLocateId = 0;\n        g.DebugBreakInLocateId = false;\n    }\n    if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)\n    {\n        DebugLog(\"(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\\n\");\n        g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags;\n        g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;\n    }\n#endif\n\n    // Create implicit/fallback window - which we will only render it if the user has added something to it.\n    // We don't use \"Debug\" to avoid colliding with user trying to create a \"Debug\" window with custom flags.\n    // This fallback is particularly important as it prevents ImGui:: calls from crashing.\n    g.WithinFrameScopeWithImplicitWindow = true;\n    SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);\n    Begin(\"Debug##Default\");\n    IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);\n\n    // Store stack sizes\n    g.ErrorCountCurrentFrame = 0;\n    ErrorRecoveryStoreState(&g.StackSizesInNewFrame);\n\n    // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,\n    // allowing to validate correct Begin/End behavior in user code.\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (g.IO.ConfigDebugBeginReturnValueLoop)\n        g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10);\n    else\n        g.DebugBeginReturnValueCullDepth = -1;\n#endif\n\n    CallContextHooks(&g, ImGuiContextHookType_NewFramePost);\n}\n\n// FIXME: Add a more explicit sort order in the window structure.\nstatic int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)\n{\n    const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;\n    const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;\n    if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))\n        return d;\n    if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))\n        return d;\n    return a->BeginOrderWithinParent - b->BeginOrderWithinParent;\n}\n\nstatic void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)\n{\n    out_sorted_windows->push_back(window);\n    if (window->Active)\n    {\n        int count = window->DC.ChildWindows.Size;\n        ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);\n        for (int i = 0; i < count; i++)\n        {\n            ImGuiWindow* child = window->DC.ChildWindows[i];\n            if (child->Active)\n                AddWindowToSortBuffer(out_sorted_windows, child);\n        }\n    }\n}\n\nstatic void AddWindowToDrawData(ImGuiWindow* window, int layer)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiViewportP* viewport = window->Viewport;\n    IM_ASSERT(viewport != NULL);\n    g.IO.MetricsRenderWindows++;\n    if (window->DrawList->_Splitter._Count > 1)\n        window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows.\n    ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList);\n    for (ImGuiWindow* child : window->DC.ChildWindows)\n        if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active\n            AddWindowToDrawData(child, layer);\n}\n\nstatic inline int GetWindowDisplayLayer(ImGuiWindow* window)\n{\n    return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;\n}\n\n// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)\nstatic inline void AddRootWindowToDrawData(ImGuiWindow* window)\n{\n    AddWindowToDrawData(window, GetWindowDisplayLayer(window));\n}\n\nstatic void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder)\n{\n    int n = builder->Layers[0]->Size;\n    int full_size = n;\n    for (int i = 1; i < IM_COUNTOF(builder->Layers); i++)\n        full_size += builder->Layers[i]->Size;\n    builder->Layers[0]->resize(full_size);\n    for (int layer_n = 1; layer_n < IM_COUNTOF(builder->Layers); layer_n++)\n    {\n        ImVector<ImDrawList*>* layer = builder->Layers[layer_n];\n        if (layer->empty())\n            continue;\n        memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*));\n        n += layer->Size;\n        layer->resize(0);\n    }\n}\n\nstatic void InitViewportDrawData(ImGuiViewportP* viewport)\n{\n    ImGuiIO& io = ImGui::GetIO();\n    ImDrawData* draw_data = &viewport->DrawDataP;\n\n    viewport->DrawData = draw_data; // Make publicly accessible\n    viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists;\n    viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1;\n    viewport->DrawDataBuilder.Layers[0]->resize(0);\n    viewport->DrawDataBuilder.Layers[1]->resize(0);\n\n    // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode,\n    // and to allow applications/backends to easily skip rendering.\n    // FIXME: Note that we however do NOT attempt to report \"zero drawlist / vertices\" into the ImDrawData structure.\n    // This is because the work has been done already, and its wasted! We should fix that and add optimizations for\n    // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline.\n    const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_IsMinimized) != 0;\n\n    draw_data->Valid = true;\n    draw_data->CmdListsCount = 0;\n    draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;\n    draw_data->DisplayPos = viewport->Pos;\n    draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size;\n    draw_data->FramebufferScale = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale : io.DisplayFramebufferScale;\n    draw_data->OwnerViewport = viewport;\n    draw_data->Textures = &ImGui::GetPlatformIO().Textures;\n}\n\n// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.\n// - When using this function it is sane to ensure that float are perfectly rounded to integer values,\n//   so that e.g. (int)(max.x-min.x) in user's render produce correct result.\n// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():\n//   some frequently called functions which to modify both channels and clipping simultaneously tend to use the\n//   more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.\n// - This is analogous to PushFont()/PopFont() in the sense that are a mixing a global stack and a window stack,\n//   which in the case of ClipRect is not so problematic but tends to be more restrictive for fonts.\nvoid ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);\n    window->ClipRect = window->DrawList->_ClipRectStack.back();\n}\n\nvoid ImGui::PopClipRect()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DrawList->PopClipRect();\n    window->ClipRect = window->DrawList->_ClipRectStack.back();\n}\n\nstatic ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window)\n{\n    for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--)\n        if (IsWindowActiveAndVisible(window->DC.ChildWindows[n]))\n            return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]);\n    return window;\n}\n\nstatic void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    ImGuiViewportP* viewport = window->Viewport;\n    ImRect viewport_rect = viewport->GetMainRect();\n\n    // Draw behind window by moving the draw command at the FRONT of the draw list\n    {\n        // Draw list have been trimmed already, hence the explicit recreation of a draw command if missing.\n        // FIXME: This is a little bit complicated, solely to avoid creating/injecting an extra drawlist in drawdata.\n        ImDrawList* draw_list = window->RootWindowDockTree->DrawList;\n        draw_list->ChannelsMerge();\n        if (draw_list->CmdBuffer.Size == 0)\n            draw_list->AddDrawCmd();\n        draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to strictly ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that)\n        ImDrawCmd cmd = draw_list->CmdBuffer.back();\n        IM_ASSERT(cmd.ElemCount == 0);\n        draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col);\n        cmd = draw_list->CmdBuffer.back();\n        draw_list->CmdBuffer.pop_back();\n        draw_list->CmdBuffer.push_front(cmd);\n        draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.\n        draw_list->PopClipRect();\n    }\n\n    // Draw over sibling docking nodes in a same docking tree\n    if (window->RootWindow->DockIsActive)\n    {\n        ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList;\n        draw_list->ChannelsMerge();\n        if (draw_list->CmdBuffer.Size == 0)\n            draw_list->AddDrawCmd();\n        draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false);\n        RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding);\n        draw_list->PopClipRect();\n    }\n}\n\nImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* bottom_most_visible_window = parent_window;\n    for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--)\n    {\n        ImGuiWindow* window = g.Windows[i];\n        if (window->Flags & ImGuiWindowFlags_ChildWindow)\n            continue;\n        if (!IsWindowWithinBeginStackOf(window, parent_window))\n            break;\n        if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window))\n            bottom_most_visible_window = window;\n    }\n    return bottom_most_visible_window;\n}\n\n// Important: AddWindowToDrawData() has not been called yet, meaning DockNodeHost windows needs a DrawList->ChannelsMerge() before usage.\n// We call ChannelsMerge() lazily here at it is faster that doing a full iteration of g.Windows[] prior to calling RenderDimmedBackgrounds().\nstatic void ImGui::RenderDimmedBackgrounds()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal();\n    if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)\n        return;\n    const bool dim_bg_for_modal = (modal_window != NULL);\n    const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active);\n    if (!dim_bg_for_modal && !dim_bg_for_window_list)\n        return;\n\n    ImGuiViewport* viewports_already_dimmed[2] = { NULL, NULL };\n    if (dim_bg_for_modal)\n    {\n        // Draw dimming behind modal or a begin stack child, whichever comes first in draw order.\n        ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);\n        RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio));\n        viewports_already_dimmed[0] = modal_window->Viewport;\n    }\n    else if (dim_bg_for_window_list)\n    {\n        // Draw dimming behind Ctrl+Tab target window and behind Ctrl+Tab UI window\n        RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));\n        viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport;\n        if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Active && g.NavWindowingListWindow->Viewport && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport)\n        {\n            RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));\n            viewports_already_dimmed[1] = g.NavWindowingListWindow->Viewport;\n        }\n\n        // Draw border around Ctrl+Tab target window\n        ImGuiWindow* window = g.NavWindowingTargetAnim;\n        ImGuiViewport* viewport = window->Viewport;\n        float distance = g.FontSize;\n        ImRect bb = window->Rect();\n        bb.Expand(distance);\n        if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y)\n            bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward\n        window->DrawList->ChannelsMerge();\n        if (window->DrawList->CmdBuffer.Size == 0)\n            window->DrawList->AddDrawCmd();\n        window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size);\n        window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); // FIXME-DPI\n        window->DrawList->PopClipRect();\n    }\n\n    // Draw dimming background on _other_ viewports than the ones our windows are in\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        if (viewport == viewports_already_dimmed[0] || viewport == viewports_already_dimmed[1])\n            continue;\n        if (modal_window && viewport->Window && IsWindowAbove(viewport->Window, modal_window))\n            continue;\n        ImDrawList* draw_list = GetForegroundDrawList(viewport);\n        const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);\n        draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col);\n    }\n}\n\n// This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.\nvoid ImGui::EndFrame()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.Initialized);\n\n    // Don't process EndFrame() multiple times.\n    if (g.FrameCountEnded == g.FrameCount)\n        return;\n    IM_ASSERT_USER_ERROR_RET(g.WithinFrameScope, \"Forgot to call ImGui::NewFrame()?\");\n\n    CallContextHooks(&g, ImGuiContextHookType_EndFramePre);\n\n    // [EXPERIMENTAL] Recover from errors\n    if (g.IO.ConfigErrorRecovery)\n        ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame);\n    ErrorCheckEndFrameSanityChecks();\n    ErrorCheckEndFrameFinalizeErrorTooltip();\n\n    // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)\n    ImGuiPlatformImeData* ime_data = &g.PlatformImeData;\n    if (g.PlatformIO.Platform_SetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0)\n    {\n        ImGuiViewport* viewport = FindViewportByID(ime_data->ViewportId);\n        if (viewport == NULL)\n            viewport = GetMainViewport();\n        IMGUI_DEBUG_LOG_IO(\"[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f) for Viewport 0x%08X\\n\", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y, viewport->ID);\n        g.PlatformIO.Platform_SetImeDataFn(&g, viewport, ime_data);\n    }\n    g.WantTextInputNextFrame = ime_data->WantTextInput ? 1 : 0;\n\n    // Hide and unfocus implicit/fallback \"Debug\" window if it hasn't been used\n    g.WithinFrameScopeWithImplicitWindow = false;\n    if (g.CurrentWindow && g.CurrentWindow->IsFallbackWindow && g.CurrentWindow->WriteAccessed == false)\n    {\n        g.CurrentWindow->Active = false;\n        if (g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow)\n            FocusWindow(NULL);\n    }\n    End();\n\n    // Update navigation: Ctrl+Tab, wrap-around requests\n    NavEndFrame();\n\n    // Update docking\n    DockContextEndFrame(&g);\n\n    SetCurrentViewport(NULL, NULL);\n\n    // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)\n    if (g.DragDropActive)\n    {\n        bool is_delivered = g.DragDropPayload.Delivery;\n        bool is_elapsed = (g.DragDropSourceFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_PayloadAutoExpire) || g.DragDropMouseButton == -1 || !IsMouseDown(g.DragDropMouseButton));\n        if (is_delivered || is_elapsed)\n            ClearDragDrop();\n    }\n\n    // Drag and Drop: Fallback for missing source tooltip. This is not ideal but better than nothing.\n    // If you want to handle source item disappearing: instead of submitting your description tooltip\n    // in the BeginDragDropSource() block of the dragged item, you can submit them from a safe single spot\n    // (e.g. end of your item loop, or before EndFrame) by reading payload data.\n    // In the typical case, the contents of drag tooltip should be possible to infer solely from payload data.\n    if (g.DragDropActive && g.DragDropSourceFrameCount + 1 < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))\n    {\n        g.DragDropWithinSource = true;\n        SetTooltip(\"...\");\n        g.DragDropWithinSource = false;\n    }\n\n    // End frame\n    g.WithinFrameScope = false;\n    g.FrameCountEnded = g.FrameCount;\n    UpdateFontsEndFrame();\n\n    // Initiate moving window + handle left-click and right-click focus\n    UpdateMouseMovingWindowEndFrame();\n\n    // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)\n    UpdateViewportsEndFrame();\n\n    // Sort the window list so that all child windows are after their parent\n    // We cannot do that on FocusWindow() because children may not exist yet\n    g.WindowsTempSortBuffer.resize(0);\n    g.WindowsTempSortBuffer.reserve(g.Windows.Size);\n    for (ImGuiWindow* window : g.Windows)\n    {\n        if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it\n            continue;\n        AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);\n    }\n\n    // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong.\n    IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);\n    g.Windows.swap(g.WindowsTempSortBuffer);\n    g.IO.MetricsActiveWindows = g.WindowsActiveCount;\n\n    UpdateTexturesEndFrame();\n\n    // Unlock font atlas\n    for (ImFontAtlas* atlas : g.FontAtlases)\n        atlas->Locked = false;\n\n    // Clear Input data for next frame\n    g.IO.MousePosPrev = g.IO.MousePos;\n    g.IO.AppFocusLost = false;\n    g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;\n    g.IO.InputQueueCharacters.resize(0);\n\n    CallContextHooks(&g, ImGuiContextHookType_EndFramePost);\n}\n\n// Prepare the data for rendering so you can call GetDrawData()\n// (As with anything within the ImGui:: namespace this doesn't touch your GPU or graphics API at all:\n// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)\nvoid ImGui::Render()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.Initialized);\n\n    if (g.FrameCountEnded != g.FrameCount)\n        EndFrame();\n    if (g.FrameCountRendered == g.FrameCount)\n        return;\n    g.FrameCountRendered = g.FrameCount;\n\n    g.IO.MetricsRenderWindows = 0;\n    CallContextHooks(&g, ImGuiContextHookType_RenderPre);\n\n    // Add background ImDrawList (for each active viewport)\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        InitViewportDrawData(viewport);\n        if (viewport->BgFgDrawLists[0] != NULL)\n            AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));\n    }\n\n    // Draw modal/window whitening backgrounds\n    RenderDimmedBackgrounds();\n\n    // Add ImDrawList to render\n    ImGuiWindow* windows_to_render_top_most[2];\n    windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindowDockTree : NULL;\n    windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);\n    for (ImGuiWindow* window : g.Windows)\n    {\n        IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive \"warning C6011: Dereferencing NULL pointer 'window'\"\n        if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])\n            AddRootWindowToDrawData(window);\n    }\n    for (int n = 0; n < IM_COUNTOF(windows_to_render_top_most); n++)\n        if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window\n            AddRootWindowToDrawData(windows_to_render_top_most[n]);\n\n    // Draw software mouse cursor if requested by io.MouseDrawCursor flag\n    if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)\n        RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));\n\n    // Setup ImDrawData structures for end-user\n    g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder);\n\n        // Add foreground ImDrawList (for each active viewport)\n        if (viewport->BgFgDrawLists[1] != NULL)\n            AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));\n\n        // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).\n        ImDrawData* draw_data = &viewport->DrawDataP;\n        IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount);\n        for (ImDrawList* draw_list : draw_data->CmdLists)\n            draw_list->_PopUnusedDrawCmd();\n\n        g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;\n        g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;\n    }\n\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)\n        for (ImFontAtlas* atlas : g.FontAtlases)\n            ImFontAtlasDebugLogTextureRequests(atlas);\n#endif\n\n    CallContextHooks(&g, ImGuiContextHookType_RenderPost);\n}\n\n// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.\n// CalcTextSize(\"\") should return ImVec2(0.0f, g.FontSize)\nImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)\n{\n    ImGuiContext& g = *GImGui;\n\n    const char* text_display_end;\n    if (hide_text_after_double_hash)\n        text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string\n    else\n        text_display_end = text_end;\n\n    ImFont* font = g.Font;\n    const float font_size = g.FontSize;\n    if (text == text_display_end)\n        return ImVec2(0.0f, font_size);\n    ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);\n\n    // Round\n    // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.\n    // FIXME: Investigate using ceilf or e.g.\n    // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c\n    // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html\n    text_size.x = IM_TRUNC(text_size.x + 0.99999f);\n\n    return text_size;\n}\n\n// Find window given position, search front-to-back\n// - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow.\n// - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically\n//   with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is\n//   called, aka before the next Begin(). Moving window isn't affected.\n// - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here.\nvoid ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* hovered_window = NULL;\n    ImGuiWindow* hovered_window_under_moving_window = NULL;\n\n    // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame)\n    ImGuiViewportP* backup_moving_window_viewport = NULL;\n    if (find_first_and_in_any_viewport == false && g.MovingWindow)\n    {\n        backup_moving_window_viewport = g.MovingWindow->Viewport;\n        g.MovingWindow->Viewport = g.MouseViewport;\n        if (!(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))\n            hovered_window = g.MovingWindow;\n    }\n\n    ImVec2 padding_regular = g.Style.TouchExtraPadding;\n    ImVec2 padding_for_resize = ImMax(g.Style.TouchExtraPadding, ImVec2(g.Style.WindowBorderHoverPadding, g.Style.WindowBorderHoverPadding));\n    for (int i = g.Windows.Size - 1; i >= 0; i--)\n    {\n        ImGuiWindow* window = g.Windows[i];\n        IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.\n        if (!window->WasActive || window->Hidden)\n            continue;\n        if (window->Flags & ImGuiWindowFlags_NoMouseInputs)\n            continue;\n        IM_ASSERT(window->Viewport);\n        if (window->Viewport != g.MouseViewport)\n            continue;\n\n        // Using the clipped AABB, a child window will typically be clipped by its parent (not always)\n        ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;\n        if (!window->OuterRectClipped.ContainsWithPad(pos, hit_padding))\n            continue;\n\n        // Support for one rectangular hole in any given window\n        // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)\n        if (window->HitTestHoleSize.x != 0)\n        {\n            ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);\n            ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);\n            if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos))\n                continue;\n        }\n\n        if (find_first_and_in_any_viewport)\n        {\n            hovered_window = window;\n            break;\n        }\n        else\n        {\n            if (hovered_window == NULL)\n                hovered_window = window;\n            IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.\n            if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree))\n                hovered_window_under_moving_window = window;\n            if (hovered_window && hovered_window_under_moving_window)\n                break;\n        }\n    }\n\n    *out_hovered_window = hovered_window;\n    if (out_hovered_window_under_moving_window != NULL)\n        *out_hovered_window_under_moving_window = hovered_window_under_moving_window;\n    if (find_first_and_in_any_viewport == false && g.MovingWindow)\n        g.MovingWindow->Viewport = backup_moving_window_viewport;\n}\n\nbool ImGui::IsItemActive()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveId)\n        return g.ActiveId == g.LastItemData.ID;\n    return false;\n}\n\nbool ImGui::IsItemActivated()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveId)\n        if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)\n            return true;\n    return false;\n}\n\nbool ImGui::IsItemDeactivated()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)\n        return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;\n    return g.DeactivatedItemData.ID == g.LastItemData.ID && g.LastItemData.ID != 0 && g.DeactivatedItemData.ElapseFrame >= g.FrameCount;\n}\n\nbool ImGui::IsItemDeactivatedAfterEdit()\n{\n    ImGuiContext& g = *GImGui;\n    return IsItemDeactivated() && g.DeactivatedItemData.HasBeenEditedBefore;\n}\n\n// == (GetItemID() == GetFocusID() && GetFocusID() != 0)\nbool ImGui::IsItemFocused()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.NavId != g.LastItemData.ID || g.NavId == 0)\n        return false;\n\n    // Special handling for the dummy item after Begin() which represent the title bar or tab.\n    // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.\n    ImGuiWindow* window = g.CurrentWindow;\n    if (g.LastItemData.ID == window->ID && window->WriteAccessed)\n        return false;\n\n    return true;\n}\n\n// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!\n// Most widgets have specific reactions based on mouse-up/down state, mouse position etc.\nbool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)\n{\n    return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);\n}\n\nbool ImGui::IsItemToggledOpen()\n{\n    ImGuiContext& g = *GImGui;\n    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;\n}\n\n// Call after a Selectable() or TreeNode() involved in multi-selection.\n// Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose.\n// This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block.\n// (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets\n// return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.)\nbool ImGui::IsItemToggledSelection()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.CurrentMultiSelect != NULL); // Can only be used inside a BeginMultiSelect()/EndMultiSelect()\n    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;\n}\n\n// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,\n// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!\n// Refer to FAQ entry \"How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?\" for details.\nbool ImGui::IsAnyItemHovered()\n{\n    ImGuiContext& g = *GImGui;\n    return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;\n}\n\nbool ImGui::IsAnyItemActive()\n{\n    ImGuiContext& g = *GImGui;\n    return g.ActiveId != 0;\n}\n\nbool ImGui::IsAnyItemFocused()\n{\n    ImGuiContext& g = *GImGui;\n    return g.NavId != 0 && g.NavCursorVisible;\n}\n\nbool ImGui::IsItemVisible()\n{\n    ImGuiContext& g = *GImGui;\n    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0;\n}\n\nbool ImGui::IsItemEdited()\n{\n    ImGuiContext& g = *GImGui;\n    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;\n}\n\n// Allow next item to be overlapped by subsequent items.\n// This works by requiring HoveredId to match for two subsequent frames,\n// so if a following items overwrite it our interactions will naturally be disabled.\nvoid ImGui::SetNextItemAllowOverlap()\n{\n    ImGuiContext& g = *GImGui;\n    g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap;\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.\n// Use SetNextItemAllowOverlap() *before* your item instead of calling this!\n//void ImGui::SetItemAllowOverlap()\n//{\n//    ImGuiContext& g = *GImGui;\n//    ImGuiID id = g.LastItemData.ID;\n//    if (g.HoveredId == id)\n//        g.HoveredIdAllowOverlap = true;\n//    if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id.\n//        g.ActiveIdAllowOverlap = true;\n//}\n#endif\n\n// This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations.\n// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version if needed?\nvoid ImGui::SetActiveIdUsingAllKeyboardKeys()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.ActiveId != 0);\n    g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1;\n    g.ActiveIdUsingAllKeyboardKeys = true;\n    NavMoveRequestCancel();\n}\n\nImGuiID ImGui::GetItemID()\n{\n    ImGuiContext& g = *GImGui;\n    return g.LastItemData.ID;\n}\n\nImVec2 ImGui::GetItemRectMin()\n{\n    ImGuiContext& g = *GImGui;\n    return g.LastItemData.Rect.Min;\n}\n\nImVec2 ImGui::GetItemRectMax()\n{\n    ImGuiContext& g = *GImGui;\n    return g.LastItemData.Rect.Max;\n}\n\nImVec2 ImGui::GetItemRectSize()\n{\n    ImGuiContext& g = *GImGui;\n    return g.LastItemData.Rect.GetSize();\n}\n\nImGuiItemFlags ImGui::GetItemFlags()\n{\n    ImGuiContext& g = *GImGui;\n    return g.LastItemData.ItemFlags;\n}\n\n// Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.\n// ImGuiChildFlags_Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details!\nbool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)\n{\n    ImGuiID id = GetCurrentWindow()->GetID(str_id);\n    return BeginChildEx(str_id, id, size_arg, child_flags, window_flags);\n}\n\nbool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)\n{\n    return BeginChildEx(NULL, id, size_arg, child_flags, window_flags);\n}\n\nbool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* parent_window = g.CurrentWindow;\n    IM_ASSERT(id != 0);\n\n    // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.\n    const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened;\n    IM_UNUSED(ImGuiChildFlags_SupportedMask_);\n    IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && \"Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?\");\n    IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && \"Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!\");\n    if (child_flags & ImGuiChildFlags_AlwaysAutoResize)\n    {\n        IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && \"Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!\");\n        IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && \"Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!\");\n    }\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    //if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding) { child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding; }\n    //if (window_flags & ImGuiWindowFlags_NavFlattened) { child_flags |= ImGuiChildFlags_NavFlattened; }\n#endif\n    if (child_flags & ImGuiChildFlags_AutoResizeX)\n        child_flags &= ~ImGuiChildFlags_ResizeX;\n    if (child_flags & ImGuiChildFlags_AutoResizeY)\n        child_flags &= ~ImGuiChildFlags_ResizeY;\n\n    // Set window flags\n    window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking;\n    window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag\n    if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))\n        window_flags |= ImGuiWindowFlags_AlwaysAutoResize;\n    if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)\n        window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;\n\n    // Special framed style\n    if (child_flags & ImGuiChildFlags_FrameStyle)\n    {\n        PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]);\n        PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding);\n        PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize);\n        PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding);\n        child_flags |= ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding;\n        window_flags |= ImGuiWindowFlags_NoMove;\n    }\n\n    // Forward size\n    // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.\n    // (the alternative would to store conditional flags per axis, which is possible but more code)\n    const ImVec2 size_avail = GetContentRegionAvail();\n    const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);\n    ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);\n\n    // A SetNextWindowSize() call always has priority (#8020)\n    // (since the code in Begin() never supported SizeVal==0.0f aka auto-resize via SetNextWindowSize() call, we don't support it here for now)\n    // FIXME: We only support ImGuiCond_Always in this path. Supporting other paths would requires to obtain window pointer.\n    if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) != 0 && (g.NextWindowData.SizeCond & ImGuiCond_Always) != 0)\n    {\n        if (g.NextWindowData.SizeVal.x > 0.0f)\n        {\n            size.x = g.NextWindowData.SizeVal.x;\n            child_flags &= ~ImGuiChildFlags_ResizeX;\n        }\n        if (g.NextWindowData.SizeVal.y > 0.0f)\n        {\n            size.y = g.NextWindowData.SizeVal.y;\n            child_flags &= ~ImGuiChildFlags_ResizeY;\n        }\n    }\n    SetNextWindowSize(size);\n\n    // Forward child flags (we allow prior settings to merge but it'll only work for adding flags)\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags)\n        g.NextWindowData.ChildFlags |= child_flags;\n    else\n        g.NextWindowData.ChildFlags = child_flags;\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasChildFlags;\n\n    // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.\n    // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.\n    // e.g. \"ParentName###ParentIdentifier/ChildName###ChildIdentifier\" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.\n    const char* temp_window_name;\n    /*if (name && parent_window->IDStack.back() == parent_window->ID)\n        ImFormatStringToTempBuffer(&temp_window_name, NULL, \"%s/%s\", parent_window->Name, name); // May omit ID if in root of ID stack\n    else*/\n    if (name)\n        ImFormatStringToTempBuffer(&temp_window_name, NULL, \"%s/%s_%08X\", parent_window->Name, name, id);\n    else\n        ImFormatStringToTempBuffer(&temp_window_name, NULL, \"%s/%08X\", parent_window->Name, id);\n\n    // Set style\n    const float backup_border_size = g.Style.ChildBorderSize;\n    if ((child_flags & ImGuiChildFlags_Borders) == 0)\n        g.Style.ChildBorderSize = 0.0f;\n\n    // Begin into window\n    const bool ret = Begin(temp_window_name, NULL, window_flags);\n\n    // Restore style\n    g.Style.ChildBorderSize = backup_border_size;\n    if (child_flags & ImGuiChildFlags_FrameStyle)\n    {\n        PopStyleVar(3);\n        PopStyleColor();\n    }\n\n    ImGuiWindow* child_window = g.CurrentWindow;\n    child_window->ChildId = id;\n\n    // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.\n    // While this is not really documented/defined, it seems that the expected thing to do.\n    if (child_window->BeginCount == 1)\n        parent_window->DC.CursorPos = child_window->Pos;\n\n    // Process navigation-in immediately so NavInit can run on first frame\n    // Can enter a child if (A) it has navigable items or (B) it can be scrolled.\n    const ImGuiID temp_id_for_activation = ImHashStr(\"##Child\", 0, id);\n    if (g.ActiveId == temp_id_for_activation)\n        ClearActiveID();\n    if (g.NavActivateId == id && !(child_flags & ImGuiChildFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))\n    {\n        FocusWindow(child_window);\n        NavInitWindow(child_window, false);\n        SetActiveID(temp_id_for_activation, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item\n        g.ActiveIdSource = g.NavInputSource;\n    }\n    return ret;\n}\n\nvoid ImGui::EndChild()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* child_window = g.CurrentWindow;\n\n    const ImGuiID backup_within_end_child_id = g.WithinEndChildID;\n    IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() calls\n\n    g.WithinEndChildID = child_window->ID;\n    ImVec2 child_size = child_window->Size;\n    End();\n    if (child_window->BeginCount == 1)\n    {\n        ImGuiWindow* parent_window = g.CurrentWindow;\n        ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size);\n        ItemSize(child_size);\n        const bool nav_flattened = (child_window->ChildFlags & ImGuiChildFlags_NavFlattened) != 0;\n        if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !nav_flattened)\n        {\n            ItemAdd(bb, child_window->ChildId);\n            RenderNavCursor(bb, child_window->ChildId);\n\n            // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying)\n            if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)\n                RenderNavCursor(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavRenderCursorFlags_Compact);\n        }\n        else\n        {\n            // Not navigable into\n            // - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs.\n            // - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set.\n            ItemAdd(bb, child_window->ChildId, NULL, ImGuiItemFlags_NoNav);\n\n            // But when flattened we directly reach items, adjust active layer mask accordingly\n            if (nav_flattened)\n                parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext;\n        }\n        if (g.HoveredWindow == child_window)\n            g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;\n        child_window->DC.ChildItemStatusFlags = g.LastItemData.StatusFlags;\n        //SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); // Not needed, effectively done by ItemAdd()\n    }\n    else\n    {\n        SetLastItemDataForChildWindowItem(child_window, child_window->Rect());\n    }\n\n    g.WithinEndChildID = backup_within_end_child_id;\n    g.LogLinePosY = -FLT_MAX; // To enforce a carriage return\n}\n\nstatic void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)\n{\n    window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);\n    window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);\n    window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);\n    window->SetWindowDockAllowFlags      = enabled ? (window->SetWindowDockAllowFlags      | flags) : (window->SetWindowDockAllowFlags      & ~flags);\n}\n\nImGuiWindow* ImGui::FindWindowByID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);\n}\n\nImGuiWindow* ImGui::FindWindowByName(const char* name)\n{\n    ImGuiID id = ImHashStr(name);\n    return FindWindowByID(id);\n}\n\nstatic void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)\n{\n    const ImGuiViewport* main_viewport = ImGui::GetMainViewport();\n    window->ViewportPos = main_viewport->Pos;\n    if (settings->ViewportId)\n    {\n        window->ViewportId = settings->ViewportId;\n        window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y);\n    }\n    window->Pos = ImTrunc(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y));\n    if (settings->Size.x > 0 && settings->Size.y > 0)\n        window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y));\n    window->Collapsed = settings->Collapsed;\n    window->DockId = settings->DockId;\n    window->DockOrder = settings->DockOrder;\n}\n\nstatic void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)\n{\n    // Initial window state with e.g. default/arbitrary window position\n    // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.\n    const ImGuiViewport* main_viewport = ImGui::GetMainViewport();\n    window->Pos = main_viewport->Pos + ImVec2(60, 60);\n    window->Size = window->SizeFull = ImVec2(0, 0);\n    window->ViewportPos = main_viewport->Pos;\n    window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = window->SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;\n\n    if (settings != NULL)\n    {\n        SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);\n        ApplyWindowSettings(window, settings);\n    }\n    window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values\n\n    if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)\n    {\n        window->AutoFitFramesX = window->AutoFitFramesY = 2;\n        window->AutoFitOnlyGrows = false;\n    }\n    else\n    {\n        if (window->Size.x <= 0.0f)\n            window->AutoFitFramesX = 2;\n        if (window->Size.y <= 0.0f)\n            window->AutoFitFramesY = 2;\n        window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);\n    }\n}\n\nstatic ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)\n{\n    // Create window the first time\n    //IMGUI_DEBUG_LOG(\"CreateNewWindow '%s', flags = 0x%08X\\n\", name, flags);\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);\n    window->Flags = flags;\n    g.WindowsById.SetVoidPtr(window->ID, window);\n\n    ImGuiWindowSettings* settings = NULL;\n    if (!(flags & ImGuiWindowFlags_NoSavedSettings))\n        if ((settings = ImGui::FindWindowSettingsByWindow(window)) != 0)\n            window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);\n\n    InitOrLoadWindowSettings(window, settings);\n\n    if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)\n        g.Windows.push_front(window); // Quite slow but rare and only once\n    else\n        g.Windows.push_back(window);\n\n    return window;\n}\n\nstatic ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window)\n{\n    return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window;\n}\n\nstatic ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window)\n{\n    return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window;\n}\n\nstatic inline ImVec2 CalcWindowMinSize(ImGuiWindow* window)\n{\n    // We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)\n    // FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller.\n    // Perhaps should tend further a neater test for this.\n    ImGuiContext& g = *GImGui;\n    ImVec2 size_min;\n    if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup))\n    {\n        size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : IMGUI_WINDOW_HARD_MIN_SIZE;\n        size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : IMGUI_WINDOW_HARD_MIN_SIZE;\n    }\n    else\n    {\n        size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : IMGUI_WINDOW_HARD_MIN_SIZE;\n        size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : IMGUI_WINDOW_HARD_MIN_SIZE;\n    }\n\n    // Reduce artifacts with very small windows\n    ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window);\n    size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(0.0f, g.Style.WindowRounding - 1.0f));\n    return size_min;\n}\n\nstatic ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)\n{\n    ImGuiContext& g = *GImGui;\n    ImVec2 new_size = size_desired;\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint)\n    {\n        // See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.\n        ImRect cr = g.NextWindowData.SizeConstraintRect;\n        new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;\n        new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;\n        if (g.NextWindowData.SizeCallback)\n        {\n            ImGuiSizeCallbackData data;\n            data.UserData = g.NextWindowData.SizeCallbackUserData;\n            data.Pos = window->Pos;\n            data.CurrentSize = window->SizeFull;\n            data.DesiredSize = new_size;\n            g.NextWindowData.SizeCallback(&data);\n            new_size = data.DesiredSize;\n        }\n        new_size.x = IM_TRUNC(new_size.x);\n        new_size.y = IM_TRUNC(new_size.y);\n    }\n\n    // Minimum size\n    ImVec2 size_min = CalcWindowMinSize(window);\n    return ImMax(new_size, size_min);\n}\n\nstatic void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)\n{\n    bool preserve_old_content_sizes = false;\n    if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)\n        preserve_old_content_sizes = true;\n    else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)\n        preserve_old_content_sizes = true;\n    if (preserve_old_content_sizes)\n    {\n        *content_size_current = window->ContentSize;\n        *content_size_ideal = window->ContentSizeIdeal;\n        return;\n    }\n\n    content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : ImTrunc64(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);\n    content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);\n    content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : ImTrunc64(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);\n    content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);\n}\n\nstatic ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents, int axis_mask)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n    const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x;\n    const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y;\n    ImVec2 size_pad = window->WindowPadding * 2.0f;\n    ImVec2 size_desired;\n    size_desired[ImGuiAxis_X] = (axis_mask & 1) ? size_contents.x + size_pad.x + decoration_w_without_scrollbars : window->Size.x;\n    size_desired[ImGuiAxis_Y] = (axis_mask & 2) ? size_contents.y + size_pad.y + decoration_h_without_scrollbars : window->Size.y;\n\n    // Determine maximum window size\n    // Child windows are laid within their parent (unless they are also popups/menus) and thus have no restriction\n    ImVec2 size_max = ImVec2(FLT_MAX, FLT_MAX);\n    if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || (window->Flags & ImGuiWindowFlags_Popup) != 0)\n    {\n        if (!window->ViewportOwned)\n            size_max = ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f;\n        const int monitor_idx = window->ViewportAllowPlatformMonitorExtend;\n        if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)\n            size_max = g.PlatformIO.Monitors[monitor_idx].WorkSize - style.DisplaySafeAreaPadding * 2.0f;\n    }\n\n    if (window->Flags & ImGuiWindowFlags_Tooltip)\n    {\n        // Tooltip always resize (up to maximum size)\n        return ImMin(size_desired, size_max);\n    }\n    else\n    {\n        ImVec2 size_min = CalcWindowMinSize(window);\n        ImVec2 size_auto_fit = ImClamp(size_desired, ImMin(size_min, size_max), size_max);\n\n        // When the window cannot fit all contents (either because of constraints, either because screen is too small),\n        // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.\n        ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);\n        bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);\n        bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);\n        if (will_have_scrollbar_x)\n            size_auto_fit.y += style.ScrollbarSize;\n        if (will_have_scrollbar_y)\n            size_auto_fit.x += style.ScrollbarSize;\n        return size_auto_fit;\n    }\n}\n\nImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)\n{\n    ImVec2 size_contents_current;\n    ImVec2 size_contents_ideal;\n    CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);\n    ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal, ~0);\n    ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);\n    return size_final;\n}\n\nstatic ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window)\n{\n    if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))\n        return ImGuiCol_PopupBg;\n    if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive)\n        return ImGuiCol_ChildBg;\n    return ImGuiCol_WindowBg;\n}\n\nstatic void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target_arg, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)\n{\n    ImVec2 corner_target = corner_target_arg;\n    if (window->Flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent\n    {\n        ImGuiWindow* parent_window = window->ParentWindow;\n        ImGuiWindowFlags parent_flags = parent_window->Flags;\n        ImRect limit_rect = parent_window->InnerRect;\n        limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize)));\n        if ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar))\n            corner_target.x = ImClamp(corner_target.x, limit_rect.Min.x, limit_rect.Max.x);\n        if (parent_flags & ImGuiWindowFlags_NoScrollbar)\n            corner_target.y = ImClamp(corner_target.y, limit_rect.Min.y, limit_rect.Max.y);\n    }\n    ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left\n    ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right\n    ImVec2 size_expected = pos_max - pos_min;\n    ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);\n    *out_pos = pos_min;\n    if (corner_norm.x == 0.0f)\n        out_pos->x -= (size_constrained.x - size_expected.x);\n    if (corner_norm.y == 0.0f)\n        out_pos->y -= (size_constrained.y - size_expected.y);\n    *out_size = size_constrained;\n}\n\n// Data for resizing from resize grip / corner\nstruct ImGuiResizeGripDef\n{\n    ImVec2  CornerPosN;\n    ImVec2  InnerDir;\n    int     AngleMin12, AngleMax12;\n};\nstatic const ImGuiResizeGripDef resize_grip_def[4] =\n{\n    { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 },  // Lower-right\n    { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 },  // Lower-left\n    { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 },  // Upper-left (Unused)\n    { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }  // Upper-right (Unused)\n};\n\n// Data for resizing from borders\nstruct ImGuiResizeBorderDef\n{\n    ImVec2  InnerDir;               // Normal toward inside\n    ImVec2  SegmentN1, SegmentN2;   // End positions, normalized (0,0: upper left)\n    float   OuterAngle;             // Angle toward outside\n};\nstatic const ImGuiResizeBorderDef resize_border_def[4] =\n{\n    { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left\n    { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right\n    { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up\n    { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }  // Down\n};\n\nstatic ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)\n{\n    ImRect rect = window->Rect();\n    if (thickness == 0.0f)\n        rect.Max -= ImVec2(1, 1);\n    if (border_n == ImGuiDir_Left)  { return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding); }\n    if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding); }\n    if (border_n == ImGuiDir_Up)    { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);    }\n    if (border_n == ImGuiDir_Down)  { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);    }\n    IM_ASSERT(0);\n    return ImRect();\n}\n\n// 0..3: corners (Lower-right, Lower-left, Unused, Unused)\nImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)\n{\n    IM_ASSERT(n >= 0 && n < 4);\n    ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;\n    id = ImHashStr(\"#RESIZE\", 0, id);\n    id = ImHashData(&n, sizeof(int), id);\n    return id;\n}\n\n// Borders (Left, Right, Up, Down)\nImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)\n{\n    IM_ASSERT(dir >= 0 && dir < 4);\n    int n = (int)dir + 4;\n    ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;\n    id = ImHashStr(\"#RESIZE\", 0, id);\n    id = ImHashData(&n, sizeof(int), id);\n    return id;\n}\n\n// Handle resize for: Resize Grips, Borders, Gamepad\n// Return true when using auto-fit (double-click on resize grip)\nstatic int ImGui::UpdateWindowManualResize(ImGuiWindow* window, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindowFlags flags = window->Flags;\n\n    if ((flags & ImGuiWindowFlags_NoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)\n        return false;\n    if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && (window->ChildFlags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)\n        return false;\n    if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.\n        return false;\n\n    int ret_auto_fit_mask = 0x00;\n    const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));\n    const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f;\n    const float grip_hover_outer_size = g.WindowsBorderHoverPadding;\n\n    ImRect clamp_rect = visibility_rect;\n    const bool window_move_from_title_bar = !(window->BgClickFlags & ImGuiWindowBgClickFlags_Move) && !(window->Flags & ImGuiWindowFlags_NoTitleBar);\n    if (window_move_from_title_bar)\n        clamp_rect.Min.y -= window->TitleBarHeight;\n\n    ImVec2 pos_target(FLT_MAX, FLT_MAX);\n    ImVec2 size_target(FLT_MAX, FLT_MAX);\n\n    // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time).\n    // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits.\n    //   This is however not the case with current backends under Win32, but a custom borderless window implementation would benefit from it.\n    // - When decoration are enabled we typically benefit from that distance, but then our resize elements would be conflicting with OS resize elements, so we also narrow.\n    // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold).\n    // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup.\n    const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration);\n    if (clip_with_viewport_rect)\n        window->ClipRect = window->Viewport->GetMainRect();\n\n    // Resize grips and borders are on layer 1\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;\n\n    // Manual resize grips\n    PushID(\"#RESIZE\");\n    for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)\n    {\n        const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];\n        const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);\n\n        // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window\n        bool hovered, held;\n        ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);\n        if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);\n        if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);\n        ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()\n        ItemAdd(resize_rect, resize_grip_id, NULL, ImGuiItemFlags_NoNav);\n        ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);\n        //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));\n        if (hovered || held)\n            SetMouseCursor((resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE);\n\n        if (held && g.IO.MouseDoubleClicked[0])\n        {\n            // Auto-fit when double-clicking\n            ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, ~0);\n            size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);\n            ret_auto_fit_mask = 0x03; // Both axes\n            ClearActiveID();\n        }\n        else if (held)\n        {\n            // Resize from any of the four corners\n            // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position\n            ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? clamp_rect.Min.x : -FLT_MAX, (def.CornerPosN.y == 1.0f || (def.CornerPosN.y == 0.0f && window_move_from_title_bar)) ? clamp_rect.Min.y : -FLT_MAX);\n            ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? clamp_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? clamp_rect.Max.y : +FLT_MAX);\n            ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip\n            corner_target = ImClamp(corner_target, clamp_min, clamp_max);\n            CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);\n        }\n\n        // Only lower-left grip is visible before hovering/activating\n        const bool resize_grip_visible = held || hovered || (resize_grip_n == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0);\n        if (resize_grip_visible)\n            resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);\n    }\n\n    int resize_border_mask = 0x00;\n    if (window->Flags & ImGuiWindowFlags_ChildWindow)\n        resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0);\n    else\n        resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00;\n    for (int border_n = 0; border_n < 4; border_n++)\n    {\n        if ((resize_border_mask & (1 << border_n)) == 0)\n            continue;\n        const ImGuiResizeBorderDef& def = resize_border_def[border_n];\n        const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;\n\n        bool hovered, held;\n        ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, g.WindowsBorderHoverPadding);\n        ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()\n        ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav);\n        ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);\n        //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));\n        if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)\n            hovered = false;\n        if (hovered || held)\n            SetMouseCursor((axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS);\n        if (held && g.IO.MouseDoubleClicked[0])\n        {\n            // Double-clicking bottom or right border auto-fit on this axis\n            // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.\n            if (border_n == 1 || border_n == 3) // Right and bottom border\n            {\n                ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, 1 << axis);\n                size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis];\n                ret_auto_fit_mask |= (1 << axis);\n                hovered = held = false; // So border doesn't show highlighted at new position\n            }\n            ClearActiveID();\n        }\n        else if (held)\n        {\n            // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.\n            // Currently only using relative mode on resizable child windows, as the problem to solve is more likely noticeable for them, but could apply for all windows eventually.\n            // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!\n            const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false, true));\n            if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing)\n            {\n                g.WindowResizeBorderExpectedRect = border_rect;\n                g.WindowResizeRelativeMode = false;\n            }\n            if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0)\n                g.WindowResizeRelativeMode = true;\n\n            const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size);\n            const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis];\n            const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + g.WindowsBorderHoverPadding; // Match ButtonBehavior() padding above.\n\n            // Use absolute mode position\n            ImVec2 border_target = window->Pos;\n            border_target[axis] = border_target_abs_mode_for_axis;\n\n            // Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.\n            bool ignore_resize = false;\n            if (g.WindowResizeRelativeMode)\n            {\n                //GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, \"Relative Mode\");\n                border_target[axis] = border_target_rel_mode_for_axis;\n                if (g.IO.MouseDelta[axis] == 0.0f || (g.IO.MouseDelta[axis] > 0.0f) == (border_target_rel_mode_for_axis > border_target_abs_mode_for_axis))\n                    ignore_resize = true;\n            }\n\n            // Clamp, apply\n            ImVec2 clamp_min(border_n == ImGuiDir_Right ? clamp_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down || (border_n == ImGuiDir_Up && window_move_from_title_bar) ? clamp_rect.Min.y : -FLT_MAX);\n            ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX);\n            border_target = ImClamp(border_target, clamp_min, clamp_max);\n            if (!ignore_resize)\n                CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);\n        }\n        if (hovered)\n            *border_hovered = border_n;\n        if (held)\n            *border_held = border_n;\n    }\n    PopID();\n\n    // Restore nav layer\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n\n    // Navigation resize (keyboard/gamepad)\n    // FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user.\n    // Not even sure the callback works here.\n    if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindowDockTree == window)\n    {\n        ImVec2 nav_resize_dir;\n        if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)\n            nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);\n        if (g.NavInputSource == ImGuiInputSource_Gamepad)\n            nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown);\n        if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)\n        {\n            const float NAV_RESIZE_SPEED = 600.0f;\n            const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * GetScale();\n            g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;\n            g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, clamp_rect.Min - window->Pos - window->Size); // We need Pos+Size >= clmap_rect.Min, so Size >= clmap_rect.Min - Pos, so size_delta >= clmap_rect.Min - window->Pos - window->Size\n            g.NavWindowingToggleLayer = false;\n            g.NavHighlightItemUnderNav = true;\n            resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);\n            ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize);\n            if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)\n            {\n                // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.\n                size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored);\n                g.NavWindowingAccumDeltaSize -= accum_floored;\n            }\n        }\n    }\n\n    // Apply back modified position/size to window\n    const ImVec2 old_pos = window->Pos;\n    const ImVec2 old_size = window->SizeFull;\n    if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x))\n        window->Size.x = window->SizeFull.x = size_target.x;\n    if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y))\n        window->Size.y = window->SizeFull.y = size_target.y;\n    if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(pos_target.x))\n        window->Pos.x = ImTrunc(pos_target.x);\n    if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y))\n        window->Pos.y = ImTrunc(pos_target.y);\n    if (old_pos.x != window->Pos.x || old_pos.y != window->Pos.y || old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)\n        MarkIniSettingsDirty(window);\n\n    // Recalculate next expected border expected coordinates\n    if (*border_held != -1)\n        g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, g.WindowsBorderHoverPadding);\n\n    return ret_auto_fit_mask;\n}\n\nstatic inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect)\n{\n    ImVec2 size_for_clamping = window->Size;\n    const bool move_from_title_bar_only = (window->BgClickFlags & ImGuiWindowBgClickFlags_Move) == 0;\n    if (move_from_title_bar_only && window->DockNodeAsHost)\n        size_for_clamping.y = ImGui::GetFrameHeight(); // Not using window->TitleBarHeight() as DockNodeAsHost will report 0.0f here.\n    else if (move_from_title_bar_only && !(window->Flags & ImGuiWindowFlags_NoTitleBar))\n        size_for_clamping.y = window->TitleBarHeight;\n    window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);\n}\n\nstatic void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size)\n{\n    const ImGuiResizeBorderDef& def = resize_border_def[border_n];\n    const float rounding = window->WindowRounding;\n    const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f);\n    window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);\n    window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);\n    window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size);\n}\n\nstatic void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    const float border_size = window->WindowBorderSize;\n    const ImU32 border_col = GetColorU32(ImGuiCol_Border);\n    if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0)\n        window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize);\n    else if (border_size > 0.0f)\n    {\n        if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.\n            RenderWindowOuterSingleBorder(window, 1, border_col, border_size);\n        if (window->ChildFlags & ImGuiChildFlags_ResizeY)\n            RenderWindowOuterSingleBorder(window, 3, border_col, border_size);\n    }\n    if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1)\n    {\n        const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered;\n        const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);\n        RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual\n    }\n    if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)\n    {\n        float y = window->Pos.y + window->TitleBarHeight - 1;\n        window->DrawList->AddLine(ImVec2(window->Pos.x + border_size * 0.5f, y), ImVec2(window->Pos.x + window->Size.x - border_size * 0.5f, y), border_col, g.Style.FrameBorderSize);\n    }\n}\n\n// Draw background and borders\n// Draw and handle scrollbars\nvoid ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n    ImGuiWindowFlags flags = window->Flags;\n\n    // Ensure that Scrollbar() doesn't read last frame's SkipItems\n    IM_ASSERT(window->BeginCount == 0);\n    window->SkipItems = false;\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;\n\n    // Draw window + handle manual resize\n    // As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame.\n    const float window_rounding = window->WindowRounding;\n    const float window_border_size = window->WindowBorderSize;\n    if (window->Collapsed)\n    {\n        // Title bar only\n        const float backup_border_size = style.FrameBorderSize;\n        g.Style.FrameBorderSize = window->WindowBorderSize;\n        ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && g.NavCursorVisible) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);\n        if (window->ViewportOwned)\n            title_bar_col |= IM_COL32_A_MASK; // No alpha (we don't support is_docking_transparent_payload here because simpler and less meaningful, but could with a bit of code shuffle/reuse)\n        RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);\n        g.Style.FrameBorderSize = backup_border_size;\n    }\n    else\n    {\n        // Window background\n        if (!(flags & ImGuiWindowFlags_NoBackground))\n        {\n            bool is_docking_transparent_payload = false;\n            if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload)\n                if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window)\n                    is_docking_transparent_payload = true;\n\n            ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window));\n            if (window->ViewportOwned)\n            {\n                bg_col |= IM_COL32_A_MASK; // No alpha\n                if (is_docking_transparent_payload)\n                    window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA;\n            }\n            else\n            {\n                // Adjust alpha. For docking\n                bool override_alpha = false;\n                float alpha = 1.0f;\n                if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasBgAlpha)\n                {\n                    alpha = g.NextWindowData.BgAlphaVal;\n                    override_alpha = true;\n                }\n                if (is_docking_transparent_payload)\n                {\n                    alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override?\n                    override_alpha = true;\n                }\n                if (override_alpha)\n                    bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);\n            }\n\n            // Render, for docked windows and host windows we ensure BG goes before decorations\n            if (window->DockIsActive)\n                window->DockNode->LastBgColor = bg_col;\n            if (flags & ImGuiWindowFlags_DockNodeHost)\n                bg_col = 0;\n            if (bg_col & IM_COL32_A_MASK)\n            {\n                ImRect bg_rect(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size);\n                ImDrawFlags bg_rounding_flags;\n                if (window->DockIsActive)\n                    bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, window->DockNode->HostWindow->Rect(), 0.0f);\n                else\n                    bg_rounding_flags = (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersBottom;\n                ImDrawList* bg_draw_list = window->DockIsActive ? window->DockNode->HostWindow->DrawList : window->DrawList;\n                if (window->DockIsActive)\n                    bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);\n                bg_draw_list->AddRectFilled(bg_rect.Min, bg_rect.Max, bg_col, window_rounding, bg_rounding_flags);\n                if (window->DockIsActive)\n                    bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG);\n            }\n        }\n        if (window->DockIsActive)\n            window->DockNode->IsBgDrawnThisFrame = true;\n\n        // Draw window shadow\n        if (style.WindowShadowSize > 0.0f && (!(flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_Popup)))\n            if (style.Colors[ImGuiCol_WindowShadow].w > 0.0f)\n                RenderWindowShadow(window);\n\n        // Title bar\n        // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag,\n        // in order for their pos/size to be matching their undocking state.)\n        if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)\n        {\n            ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);\n            if (window->ViewportOwned)\n                title_bar_col |= IM_COL32_A_MASK; // No alpha\n            window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);\n        }\n\n        // Menu bar\n        if (flags & ImGuiWindowFlags_MenuBar)\n        {\n            ImRect menu_bar_rect = window->MenuBarRect();\n            menu_bar_rect.ClipWith(window->Rect());  // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.\n            window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop);\n            if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)\n                window->DrawList->AddLine(menu_bar_rect.GetBL() + ImVec2(window_border_size * 0.5f, 0.0f), menu_bar_rect.GetBR() - ImVec2(window_border_size * 0.5f, 0.0f), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);\n        }\n\n        // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock\n        ImGuiDockNode* node = window->DockNode;\n        if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar())\n        {\n            float unhide_sz_draw = ImTrunc(g.FontSize * 0.70f);\n            float unhide_sz_hit = ImTrunc(g.FontSize * 0.55f);\n            ImVec2 p = node->Pos;\n            ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit));\n            ImGuiID unhide_id = window->GetID(\"#UNHIDE\");\n            KeepAliveID(unhide_id);\n            bool hovered, held;\n            if (ButtonBehavior(r, unhide_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren))\n                node->WantHiddenTabBarToggle = true;\n            else if (held && IsMouseDragging(0))\n                StartMouseMovingWindowOrNode(window, node, true); // Undock from tab-bar triangle = same as window/collapse menu button\n\n            // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size..\n            ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n            window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col);\n        }\n\n        // Scrollbars\n        if (window->ScrollbarX)\n            Scrollbar(ImGuiAxis_X);\n        if (window->ScrollbarY)\n            Scrollbar(ImGuiAxis_Y);\n\n        // Render resize grips (after their input handling so we don't have a frame of latency)\n        if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))\n        {\n            for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)\n            {\n                const ImU32 col = resize_grip_col[resize_grip_n];\n                if ((col & IM_COL32_A_MASK) == 0)\n                    continue;\n                const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];\n                const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);\n                const float border_inner = IM_ROUND(window_border_size * 0.5f);\n                window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(border_inner, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, border_inner)));\n                window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, border_inner) : ImVec2(border_inner, resize_grip_draw_size)));\n                window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + border_inner), corner.y + grip.InnerDir.y * (window_rounding + border_inner)), window_rounding, grip.AngleMin12, grip.AngleMax12);\n                window->DrawList->PathFillConvex(col);\n            }\n        }\n\n        // Borders (for dock node host they will be rendered over after the tab bar)\n        if (handle_borders_and_resize_grips && !window->DockNodeAsHost)\n            RenderWindowOuterBorders(window);\n    }\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n}\n\nvoid ImGui::RenderWindowShadow(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n    float shadow_size = style.WindowShadowSize;\n    ImU32 shadow_col = GetColorU32(ImGuiCol_WindowShadow);\n    ImVec2 shadow_offset = ImVec2(ImCos(style.WindowShadowOffsetAngle), ImSin(style.WindowShadowOffsetAngle)) * style.WindowShadowOffsetDist;\n    window->DrawList->AddShadowRect(window->Pos, window->Pos + window->Size, shadow_col, shadow_size, shadow_offset, ImDrawFlags_ShadowCutOutShapeBackground, window->WindowRounding);\n}\n\n// When inside a dock node, this is handled in DockNodeCalcTabBarLayout() instead.\n// Render title text, collapse button, close button\nvoid ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n    ImGuiWindowFlags flags = window->Flags;\n\n    const bool has_close_button = (p_open != NULL);\n    const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);\n\n    // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)\n    // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref?\n    const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;\n    g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;\n\n    // Layout buttons\n    // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.\n    float pad_l = style.FramePadding.x;\n    float pad_r = style.FramePadding.x;\n    float button_sz = g.FontSize;\n    ImVec2 close_button_pos;\n    ImVec2 collapse_button_pos;\n    if (has_close_button)\n    {\n        close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);\n        pad_r += button_sz + style.ItemInnerSpacing.x;\n    }\n    if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)\n    {\n        collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);\n        pad_r += button_sz + style.ItemInnerSpacing.x;\n    }\n    if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)\n    {\n        collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y);\n        pad_l += button_sz + style.ItemInnerSpacing.x;\n    }\n\n    // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)\n    if (has_collapse_button)\n        if (CollapseButton(window->GetID(\"#COLLAPSE\"), collapse_button_pos, NULL))\n            window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function\n\n    // Close button\n    if (has_close_button)\n    {\n        ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;\n        g.CurrentItemFlags |= ImGuiItemFlags_NoFocus;\n        if (CloseButton(window->GetID(\"#CLOSE\"), close_button_pos))\n            *p_open = false;\n        g.CurrentItemFlags = backup_item_flags;\n    }\n\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n    g.CurrentItemFlags = item_flags_backup;\n\n    // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional \"unsaved document\" marker)\n    // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..\n    const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;\n    const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);\n\n    // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,\n    // while uncentered title text will still reach edges correctly.\n    if (pad_l > style.FramePadding.x)\n        pad_l += g.Style.ItemInnerSpacing.x;\n    if (pad_r > style.FramePadding.x)\n        pad_r += g.Style.ItemInnerSpacing.x;\n    if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)\n    {\n        float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center\n        float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);\n        pad_l = ImMax(pad_l, pad_extend * centerness);\n        pad_r = ImMax(pad_r, pad_extend * centerness);\n    }\n\n    ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y);\n    ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y);\n    if (flags & ImGuiWindowFlags_UnsavedDocument)\n    {\n        ImVec2 marker_pos;\n        marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x);\n        marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;\n        if (marker_pos.x > layout_r.Min.x)\n        {\n            RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_UnsavedMarker));\n            clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));\n        }\n    }\n    //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]\n    //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]\n    RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);\n}\n\nvoid ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)\n{\n    window->ParentWindow = parent_window;\n    window->RootWindow = window->RootWindowPopupTree = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;\n    if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))\n    {\n        window->RootWindowDockTree = parent_window->RootWindowDockTree;\n        if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost))\n            window->RootWindow = parent_window->RootWindow;\n    }\n    if (parent_window && (flags & ImGuiWindowFlags_Popup))\n        window->RootWindowPopupTree = parent_window->RootWindowPopupTree;\n    if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip))) // FIXME: simply use _NoTitleBar ?\n        window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;\n    while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened)\n    {\n        IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);\n        window->RootWindowForNav = window->RootWindowForNav->ParentWindow;\n    }\n}\n\n// [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point.\n// This is designed as a toy/test-bed for\nvoid ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    window->SkipRefresh = false;\n    if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0)\n        return;\n    if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh)\n    {\n        // FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused.\n        if (window->Appearing) // If currently appearing\n            return;\n        if (window->Hidden) // If was hidden (previous frame)\n            return;\n        if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow)\n            if (window->RootWindow == g.HoveredWindow->RootWindow || IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, window))\n                return;\n        if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow)\n            if (window->RootWindow == g.NavWindow->RootWindow || IsWindowWithinBeginStackOf(g.NavWindow->RootWindow, window))\n                return;\n        window->DrawList = NULL;\n        window->SkipRefresh = true;\n    }\n}\n\nstatic void SetWindowActiveForSkipRefresh(ImGuiWindow* window)\n{\n    window->Active = true;\n    for (ImGuiWindow* child : window->DC.ChildWindows)\n        if (!child->Hidden)\n        {\n            child->Active = child->SkipRefresh = true;\n            SetWindowActiveForSkipRefresh(child);\n        }\n}\n\n// Push a new Dear ImGui window to add widgets to.\n// - A default window called \"Debug\" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.\n// - Begin/End can be called multiple times during the frame with the same window name to append content.\n// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).\n//   You can use the \"##\" or \"###\" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.\n// - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned.\n// - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.\nbool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    IM_ASSERT(name != NULL && name[0] != '\\0');     // Window name required\n    IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()\n    IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet\n\n    // Find or create\n    ImGuiWindow* window = FindWindowByName(name);\n    const bool window_just_created = (window == NULL);\n    if (window_just_created)\n        window = CreateNewWindow(name, flags);\n\n    // [DEBUG] Debug break requested by user\n    if (g.DebugBreakInWindow == window->ID)\n        IM_DEBUG_BREAK();\n\n    // Automatically disable manual moving/resizing when NoInputs is set\n    if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)\n        flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;\n\n    const int current_frame = g.FrameCount;\n    const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);\n    window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);\n\n    // Update the Appearing flag (note: the BeginDocked() path may also set this to true later)\n    bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit \"Debug\" window would always toggle off->on\n    if (flags & ImGuiWindowFlags_Popup)\n    {\n        ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];\n        window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed\n        window_just_activated_by_user |= (window != popup_ref.Window);\n    }\n\n    // Update Flags, LastFrameActive, BeginOrderXXX fields\n    const bool window_was_appearing = window->Appearing;\n    if (first_begin_of_the_frame)\n    {\n        UpdateWindowInFocusOrderList(window, window_just_created, flags);\n        window->Appearing = window_just_activated_by_user;\n        if (window->Appearing)\n            SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);\n        window->FlagsPreviousFrame = window->Flags;\n        window->Flags = (ImGuiWindowFlags)flags;\n        window->ChildFlags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0;\n        window->LastFrameActive = current_frame;\n        window->LastTimeActive = (float)g.Time;\n        window->BeginOrderWithinParent = 0;\n        window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);\n    }\n    else\n    {\n        flags = window->Flags;\n    }\n\n    // Docking\n    // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1)\n    IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasDock)\n        SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond);\n    if (first_begin_of_the_frame)\n    {\n        const bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL);\n        const bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window);\n        const bool dock_node_was_visible = window->DockNodeIsVisible;\n        const bool dock_tab_was_visible = window->DockTabIsVisible;\n        window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false;\n\n        if (has_dock_node || new_auto_dock_node)\n        {\n            BeginDocked(window, p_open);\n            flags = window->Flags;\n            if (window->DockIsActive)\n            {\n                IM_ASSERT(window->DockNode != NULL);\n                g.NextWindowData.HasFlags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; // Docking currently override constraints\n            }\n\n            // Amend the Appearing flag\n            if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing)\n            {\n                window->Appearing = true;\n                SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);\n            }\n        }\n    }\n\n    // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack\n    ImGuiWindow* parent_window_in_stack = (window->DockIsActive && window->DockNode->HostWindow) ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;\n    ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) ? parent_window_in_stack : NULL) : window->ParentWindow;\n    IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));\n\n    // We allow window memory to be compacted so recreate the base stack when needed.\n    if (window->IDStack.Size == 0)\n        window->IDStack.push_back(window->ID);\n\n    // Add to stack\n    g.CurrentWindow = window;\n    g.CurrentWindowStack.resize(g.CurrentWindowStack.Size + 1);\n    ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();\n    window_stack_data.Window = window;\n    window_stack_data.ParentLastItemDataBackup = g.LastItemData;\n    window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled);\n    window_stack_data.DisabledOverrideReenableAlphaBackup = 0.0f;\n    ErrorRecoveryStoreState(&window_stack_data.StackSizesInBegin);\n    g.StackSizesInBeginForCurrentWindow = &window_stack_data.StackSizesInBegin;\n    if (flags & ImGuiWindowFlags_ChildMenu)\n        g.BeginMenuDepth++;\n\n    // Update ->RootWindow and others pointers (before any possible call to FocusWindow)\n    if (first_begin_of_the_frame)\n    {\n        UpdateWindowParentAndRootLinks(window, flags, parent_window);\n        window->ParentWindowInBeginStack = parent_window_in_stack;\n\n        // Focus route\n        // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack,\n        // Use for e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798)\n        window->ParentWindowForFocusRoute = (window->RootWindow != window) ? parent_window_in_stack : NULL;\n        if (window->ParentWindowForFocusRoute == NULL && window->DockNode != NULL)\n            if (window->DockNode->MergedFlags & ImGuiDockNodeFlags_DockedWindowsInFocusRoute)\n                window->ParentWindowForFocusRoute = window->DockNode->HostWindow;\n\n        // Override with SetNextWindowClass() field or direct call to SetWindowParentWindowForFocusRoute()\n        if (window->WindowClass.FocusRouteParentWindowId != 0)\n        {\n            window->ParentWindowForFocusRoute = FindWindowByID(window->WindowClass.FocusRouteParentWindowId);\n            IM_ASSERT(window->ParentWindowForFocusRoute != 0); // Invalid value for FocusRouteParentWindowId.\n        }\n\n        // Inherit SetWindowFontScale() from parent until we fix this system...\n        window->FontWindowScaleParents = parent_window ? parent_window->FontWindowScaleParents * parent_window->FontWindowScale : 1.0f;\n    }\n\n    // Add to focus scope stack\n    PushFocusScope((window->ChildFlags & ImGuiChildFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID);\n    window->NavRootFocusScopeId = g.CurrentFocusScopeId;\n\n    // Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[]\n    if (flags & ImGuiWindowFlags_Popup)\n    {\n        ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];\n        popup_ref.Window = window;\n        popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent;\n        g.BeginPopupStack.push_back(popup_ref);\n        window->PopupId = popup_ref.PopupId;\n    }\n\n    // Process SetNextWindow***() calls\n    // (FIXME: Consider splitting the HasXXX flags into X/Y components)\n    bool window_pos_set_by_api = false;\n    bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos)\n    {\n        window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;\n        if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)\n        {\n            // May be processed on the next frame if this is our first frame and we are measuring size\n            // FIXME: Look into removing the branch so everything can go through this same code path for consistency.\n            window->SetWindowPosVal = g.NextWindowData.PosVal;\n            window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;\n            window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);\n        }\n        else\n        {\n            SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);\n        }\n    }\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize)\n    {\n        window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);\n        window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);\n        if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild()\n            g.NextWindowData.SizeVal.x = window->SizeFull.x;\n        if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0)\n            g.NextWindowData.SizeVal.y = window->SizeFull.y;\n        SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);\n    }\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll)\n    {\n        if (g.NextWindowData.ScrollVal.x >= 0.0f)\n        {\n            window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;\n            window->ScrollTargetCenterRatio.x = 0.0f;\n        }\n        if (g.NextWindowData.ScrollVal.y >= 0.0f)\n        {\n            window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;\n            window->ScrollTargetCenterRatio.y = 0.0f;\n        }\n    }\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasContentSize)\n        window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;\n    else if (first_begin_of_the_frame)\n        window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowClass)\n        window->WindowClass = g.NextWindowData.WindowClass;\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasCollapsed)\n        SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasFocus)\n        FocusWindow(window);\n    if (window->Appearing)\n        SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);\n\n    // [EXPERIMENTAL] Skip Refresh mode\n    UpdateWindowSkipRefresh(window);\n\n    // Nested root windows (typically tooltips) override disabled state\n    if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)\n        BeginDisabledOverrideReenable();\n\n    // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()\n    g.CurrentWindow = NULL;\n\n    // When reusing window again multiple times a frame, just append content (don't need to setup again)\n    if (first_begin_of_the_frame && !window->SkipRefresh)\n    {\n        // Initialize\n        const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)\n        const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);\n        window->Active = true;\n        window->HasCloseButton = (p_open != NULL);\n        window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);\n        window->IDStack.resize(1);\n        window->DrawList->_ResetForNewFrame();\n        window->DC.CurrentTableIdx = -1;\n        if (flags & ImGuiWindowFlags_DockNodeHost)\n        {\n            window->DrawList->ChannelsSplit(2);\n            window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); // Render decorations on channel 1 as we will render the backgrounds manually later\n        }\n\n        // Restore buffer capacity when woken from a compacted state, to avoid\n        if (window->MemoryCompacted)\n            GcAwakeTransientWindowBuffers(window);\n\n        // Update stored window name when it changes (which can _only_ happen with the \"###\" operator, so the ID would stay unchanged).\n        // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere.\n        bool window_title_visible_elsewhere = false;\n        if ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive))\n            window_title_visible_elsewhere = true;\n        else if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->WasActive && (flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using Ctrl+Tab\n            window_title_visible_elsewhere = true;\n        else if (flags & ImGuiWindowFlags_ChildMenu)\n            window_title_visible_elsewhere = true;\n        if ((window_title_visible_elsewhere || window_just_activated_by_user) && !window_just_created && strcmp(name, window->Name) != 0)\n        {\n            size_t buf_len = (size_t)window->NameBufLen;\n            window->Name = ImStrdupcpy(window->Name, &buf_len, name);\n            window->NameBufLen = (int)buf_len;\n        }\n\n        // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS\n\n        // Update contents size from last frame for auto-fitting (or use explicit size)\n        CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);\n\n        // FIXME: These flags are decremented before they are used. This means that in order to have these fields produce their intended behaviors\n        // for one frame we must set them to at least 2, which is counter-intuitive. HiddenFramesCannotSkipItems is a more complicated case because\n        // it has a single usage before this code block and may be set below before it is finally checked.\n        if (window->HiddenFramesCanSkipItems > 0)\n            window->HiddenFramesCanSkipItems--;\n        if (window->HiddenFramesCannotSkipItems > 0)\n            window->HiddenFramesCannotSkipItems--;\n        if (window->HiddenFramesForRenderOnly > 0)\n            window->HiddenFramesForRenderOnly--;\n\n        // Hide new windows for one frame until they calculate their size\n        if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))\n            window->HiddenFramesCannotSkipItems = 1;\n\n        // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)\n        // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.\n        if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)\n        {\n            window->HiddenFramesCannotSkipItems = 1;\n            if (flags & ImGuiWindowFlags_AlwaysAutoResize)\n            {\n                if (!window_size_x_set_by_api)\n                    window->Size.x = window->SizeFull.x = 0.f;\n                if (!window_size_y_set_by_api)\n                    window->Size.y = window->SizeFull.y = 0.f;\n                window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);\n            }\n        }\n\n        // SELECT VIEWPORT\n        // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes.\n\n        WindowSelectViewport(window);\n        SetCurrentViewport(window, window->Viewport);\n        SetCurrentWindow(window);\n        flags = window->Flags;\n\n        // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)\n        // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style.\n\n        if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow))\n            window->WindowBorderSize = style.ChildBorderSize;\n        else\n            window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;\n        window->WindowPadding = style.WindowPadding;\n        if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f)\n            window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);\n\n        // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.\n        window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);\n        window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;\n        window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f;\n        window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f;\n        window->FontRefSize = g.FontSize; // Lock this to discourage calling window->CalcFontSize() outside of current window.\n\n        // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible.\n        // Those flags will be altered further down in the function depending on more conditions.\n        bool use_current_size_for_scrollbar_x = window_just_created;\n        bool use_current_size_for_scrollbar_y = window_just_created;\n        if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f)\n            use_current_size_for_scrollbar_x = true;\n        if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252\n            use_current_size_for_scrollbar_y = true;\n\n        // Collapse window by double-clicking on title bar\n        // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing\n        if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive)\n        {\n            // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed),\n            // so verify that we don't have items over the title bar.\n            ImRect title_bar_rect = window->TitleBarRect();\n            if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))\n                if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)\n                    window->WantCollapseToggle = true;\n            if (window->WantCollapseToggle)\n            {\n                window->Collapsed = !window->Collapsed;\n                if (!window->Collapsed)\n                    use_current_size_for_scrollbar_y = true;\n                MarkIniSettingsDirty(window);\n            }\n        }\n        else\n        {\n            window->Collapsed = false;\n        }\n        window->WantCollapseToggle = false;\n\n        // SIZE\n\n        // Outer Decoration Sizes\n        // (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations).\n        const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes;\n        window->DecoOuterSizeX1 = 0.0f;\n        window->DecoOuterSizeX2 = 0.0f;\n        window->DecoOuterSizeY1 = window->TitleBarHeight + window->MenuBarHeight;\n        window->DecoOuterSizeY2 = 0.0f;\n        window->ScrollbarSizes = ImVec2(0.0f, 0.0f);\n\n        // Calculate auto-fit size, handle automatic resize\n        // - Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.\n        // - We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.\n        // - Auto-fit may only grow window during the first few frames.\n        {\n            const bool size_auto_fit_x_always = !window_size_x_set_by_api && (flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed;\n            const bool size_auto_fit_y_always = !window_size_y_set_by_api && (flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed;\n            const bool size_auto_fit_x_current = !window_size_x_set_by_api && (window->AutoFitFramesX > 0);\n            const bool size_auto_fit_y_current = !window_size_y_set_by_api && (window->AutoFitFramesY > 0);\n            int size_auto_fit_mask = 0;\n            if (size_auto_fit_x_always || size_auto_fit_x_current)\n                size_auto_fit_mask |= (1 << ImGuiAxis_X);\n            if (size_auto_fit_y_always || size_auto_fit_y_current)\n                size_auto_fit_mask |= (1 << ImGuiAxis_Y);\n            const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, size_auto_fit_mask);\n\n            const ImVec2 old_size = window->SizeFull;\n            if (size_auto_fit_x_always || size_auto_fit_x_current)\n            {\n                if (size_auto_fit_x_always)\n                    window->SizeFull.x = size_auto_fit.x;\n                else\n                    window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;\n                use_current_size_for_scrollbar_x = true;\n            }\n            if (size_auto_fit_y_always || size_auto_fit_y_current)\n            {\n                if (size_auto_fit_y_always)\n                    window->SizeFull.y = size_auto_fit.y;\n                else\n                    window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;\n                use_current_size_for_scrollbar_y = true;\n            }\n            if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)\n                MarkIniSettingsDirty(window);\n        }\n\n        // Apply minimum/maximum window size constraints and final size\n        window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);\n        window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;\n\n        // POSITION\n\n        // Popup latch its initial position, will position itself when it appears next frame\n        if (window_just_activated_by_user)\n        {\n            window->AutoPosLastDirection = ImGuiDir_None;\n            if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()\n                window->Pos = g.BeginPopupStack.back().OpenPopupPos;\n        }\n\n        // Position child window\n        if (flags & ImGuiWindowFlags_ChildWindow)\n        {\n            IM_ASSERT(parent_window && parent_window->Active);\n            window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;\n            parent_window->DC.ChildWindows.push_back(window);\n            if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)\n                window->Pos = parent_window->DC.CursorPos;\n        }\n\n        const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);\n        if (window_pos_with_pivot)\n            SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)\n        else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)\n            window->Pos = FindBestWindowPosForPopup(window);\n        else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)\n            window->Pos = FindBestWindowPosForPopup(window);\n        else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)\n            window->Pos = FindBestWindowPosForPopup(window);\n\n        // Late create viewport if we don't fit within our current host viewport.\n        if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_IsMinimized))\n            if (!window->Viewport->GetMainRect().Contains(window->Rect()))\n            {\n                // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport)\n                //ImGuiViewport* old_viewport = window->Viewport;\n                window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);\n\n                // FIXME-DPI\n                //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong\n                SetCurrentViewport(window, window->Viewport);\n                SetCurrentWindow(window);\n            }\n\n        if (window->ViewportOwned)\n            WindowSyncOwnedViewport(window, parent_window_in_stack);\n\n        // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)\n        // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.\n        ImRect viewport_rect(window->Viewport->GetMainRect());\n        ImRect viewport_work_rect(window->Viewport->GetWorkRect());\n        ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);\n        ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);\n\n        // Clamp position/size so window stays visible within its viewport or monitor\n        // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.\n        // FIXME: Similar to code in GetWindowAllowedExtentRect()\n        if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))\n        {\n            if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)\n            {\n                ClampWindowPos(window, visibility_rect);\n            }\n            else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0)\n            {\n                if (g.MovingWindow != NULL && window->RootWindowDockTree == g.MovingWindow->RootWindowDockTree)\n                {\n                    // While moving windows we allow them to straddle monitors (#7299, #3071)\n                    visibility_rect = g.PlatformMonitorsFullWorkRect;\n                }\n                else\n                {\n                    // When not moving ensure visible in its monitor\n                    // Lost windows (e.g. a monitor disconnected) will naturally moved to the fallback/dummy monitor aka the main viewport.\n                    const ImGuiPlatformMonitor* monitor = GetViewportPlatformMonitor(window->Viewport);\n                    visibility_rect = ImRect(monitor->WorkPos, monitor->WorkPos + monitor->WorkSize);\n                }\n                visibility_rect.Expand(-visibility_padding);\n                ClampWindowPos(window, visibility_rect);\n            }\n        }\n        window->Pos = ImTrunc(window->Pos);\n\n        // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)\n        // Large values tend to lead to variety of artifacts and are not recommended.\n        if ((flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive)\n            window->WindowRounding = style.ChildRounding;\n        else if (window->RootWindowDockTree->ViewportOwned)\n            window->WindowRounding = 0.0f;\n        else\n            window->WindowRounding = ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;\n\n        // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.\n        //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))\n        //    window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);\n\n        // Apply window focus (new and reactivated windows are moved to front)\n        bool want_focus = false;\n        if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))\n        {\n            if (flags & ImGuiWindowFlags_Popup)\n                want_focus = true;\n            else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip))\n                want_focus = true;\n        }\n\n        // [Test Engine] Register whole window in the item system (before submitting further decorations)\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n        if (g.TestEngineHookItems)\n        {\n            IM_ASSERT(window->IDStack.Size == 1);\n            window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.\n            window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;\n            IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);\n            IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);\n            window->IDStack.Size = 1;\n            window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n\n        }\n#endif\n\n        // Decide if we are going to handle borders and resize grips\n        // 'window->SkipItems' is not updated yet so for child windows we rely on ParentWindow to avoid submitting decorations. (#8815)\n        // Whenever we add support for full decorated child windows we will likely make this logic more general.\n        bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive);\n        if ((flags & ImGuiWindowFlags_ChildWindow) && window->ParentWindow->SkipItems)\n            handle_borders_and_resize_grips = false;\n\n        // Handle manual resize: Resize Grips, Borders, Gamepad\n        // Child windows can only be resized when they have the flags set. The resize grip allows resizing in both directions, so it should appear only if both flags are set.\n        int border_hovered = -1, border_held = -1;\n        ImU32 resize_grip_col[4] = {};\n        int resize_grip_count;\n        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))\n            resize_grip_count = ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY)) ? 1 : 0;\n        else\n            resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.\n\n        const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));\n        if (handle_borders_and_resize_grips && !window->Collapsed)\n            if (int auto_fit_mask = UpdateWindowManualResize(window, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))\n            {\n                if (auto_fit_mask & (1 << ImGuiAxis_X))\n                    use_current_size_for_scrollbar_x = true;\n                if (auto_fit_mask & (1 << ImGuiAxis_Y))\n                    use_current_size_for_scrollbar_y = true;\n            }\n        window->ResizeBorderHovered = (signed char)border_hovered;\n        window->ResizeBorderHeld = (signed char)border_held;\n\n        // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either)\n        if (window->ViewportOwned)\n        {\n            if (!window->Viewport->PlatformRequestMove)\n                window->Viewport->Pos = window->Pos;\n            if (!window->Viewport->PlatformRequestResize)\n                window->Viewport->Size = window->Size;\n            window->Viewport->UpdateWorkRect();\n            viewport_rect = window->Viewport->GetMainRect();\n        }\n\n        // Save last known viewport position within the window itself (so it can be saved in .ini file and restored)\n        window->ViewportPos = window->Viewport->Pos;\n\n        // SCROLLBAR VISIBILITY\n\n        // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).\n        if (!window->Collapsed)\n        {\n            // When reading the current size we need to read it after size constraints have been applied.\n            // Intentionally use previous frame values for InnerRect and ScrollbarSizes.\n            // And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet.\n            ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2));\n            ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame;\n            ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;\n            float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;\n            float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;\n            bool scrollbar_x_prev = window->ScrollbarX;\n            //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?\n            window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));\n            window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));\n\n            // Track when ScrollbarX visibility keeps toggling, which is a sign of a feedback loop, and stabilize by enforcing visibility (#3285, #8488)\n            // (Feedback loops of this sort can manifest in various situations, but combining horizontal + vertical scrollbar + using a clipper with varying width items is one frequent cause.\n            //  The better solution is to, either (1) enforce visibility by using ImGuiWindowFlags_AlwaysHorizontalScrollbar or (2) declare stable contents width with SetNextWindowContentSize(), if you can compute it)\n            window->ScrollbarXStabilizeToggledHistory <<= 1;\n            window->ScrollbarXStabilizeToggledHistory |= (scrollbar_x_prev != window->ScrollbarX) ? 0x01 : 0x00;\n            const bool scrollbar_x_stabilize = (window->ScrollbarXStabilizeToggledHistory != 0) && ImCountSetBits(window->ScrollbarXStabilizeToggledHistory) >= 4; // 4 == half of bits in our U8 history.\n            if (scrollbar_x_stabilize)\n                window->ScrollbarX = true;\n            //if (scrollbar_x_stabilize && !window->ScrollbarXStabilizeEnabled)\n            //    IMGUI_DEBUG_LOG(\"[scroll] Stabilize ScrollbarX for Window '%s'\\n\", window->Name);\n            window->ScrollbarXStabilizeEnabled = scrollbar_x_stabilize;\n\n            if (window->ScrollbarX && !window->ScrollbarY)\n                window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);\n            window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);\n\n            // Amend the partially filled window->DecorationXXX values.\n            window->DecoOuterSizeX2 += window->ScrollbarSizes.x;\n            window->DecoOuterSizeY2 += window->ScrollbarSizes.y;\n        }\n\n        // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)\n        // Update various regions. Variables they depend on should be set above in this function.\n        // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.\n\n        // Outer rectangle\n        // Not affected by window border size. Used by:\n        // - FindHoveredWindow() (w/ extra padding when border resize is enabled)\n        // - Begin() initial clipping rect for drawing window background and borders.\n        // - Begin() clipping whole child\n        const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;\n        const ImRect outer_rect = window->Rect();\n        const ImRect title_bar_rect = window->TitleBarRect();\n        window->OuterRectClipped = outer_rect;\n        if (window->DockIsActive)\n            window->OuterRectClipped.Min.y += window->TitleBarHeight;\n        window->OuterRectClipped.ClipWith(host_rect);\n\n        // Inner rectangle\n        // Not affected by window border size. Used by:\n        // - InnerClipRect\n        // - ScrollToRectEx()\n        // - NavUpdatePageUpPageDown()\n        // - Scrollbar()\n        window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1;\n        window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1;\n        window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2;\n        window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2;\n\n        // Inner clipping rectangle.\n        // - Extend a outside of normal work region up to borders.\n        // - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.\n        // - It also makes clipped items be more noticeable.\n        // - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312\n        // - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.\n        // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.\n        // Affected by window/frame border size. Used by:\n        // - Begin() initial clip rect\n        float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);\n\n        // Try to match the fact that our border is drawn centered over the window rectangle, rather than inner.\n        // This is why we do a *0.5f here. We don't currently even technically support large values for WindowBorderSize,\n        // see e.g #7887 #7888, but may do after we move the window border to become an inner border (and then we can remove the 0.5f here).\n        window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize * 0.5f);\n        window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size * 0.5f);\n        window->InnerClipRect.Max.x = ImFloor(window->InnerRect.Max.x - window->WindowBorderSize * 0.5f);\n        window->InnerClipRect.Max.y = ImFloor(window->InnerRect.Max.y - window->WindowBorderSize * 0.5f);\n        window->InnerClipRect.ClipWithFull(host_rect);\n\n        // SCROLLING\n\n        // Lock down maximum scrolling\n        // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate\n        // for right/bottom aligned items without creating a scrollbar.\n        window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());\n        window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());\n\n        // Apply scrolling\n        window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);\n        window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);\n        window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f;\n\n        // DRAWING\n\n        // Setup draw list and outer clipping rectangle\n        IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);\n        window->DrawList->PushTexture(g.Font->OwnerAtlas->TexRef);\n        PushClipRect(host_rect.Min, host_rect.Max, false);\n\n        // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)\n        // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.\n        // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493)\n        const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible;\n        if (is_undocked_or_docked_visible)\n        {\n            bool render_decorations_in_parent = false;\n            if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)\n            {\n                // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)\n                // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs\n                ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;\n                bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;\n                bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0);\n                if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping)\n                    render_decorations_in_parent = true;\n            }\n            if (render_decorations_in_parent)\n                window->DrawList = parent_window->DrawList;\n\n            // Handle title bar, scrollbar, resize grips and resize borders\n            const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;\n            const bool title_bar_is_highlight = want_focus || (window_to_highlight && (window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight || (window->DockNode && window->DockNode == window_to_highlight->DockNode)));\n            RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);\n\n            if (render_decorations_in_parent)\n                window->DrawList = &window->DrawListInst;\n        }\n\n        // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)\n\n        // Work rectangle.\n        // Affected by window padding and border size. Used by:\n        // - Columns() for right-most edge\n        // - TreeNode(), CollapsingHeader() for right-most edge\n        // - BeginTabBar() for right-most edge\n        const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);\n        const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);\n        const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));\n        const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));\n        window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));\n        window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));\n        window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;\n        window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;\n        window->ParentWorkRect = window->WorkRect;\n\n        // [LEGACY] Content Region\n        // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.\n        // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.\n        // Used by:\n        // - Mouse wheel scrolling + many other things\n        window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1;\n        window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1;\n        window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));\n        window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));\n\n        // Setup drawing context\n        // (NB: That term \"drawing context / DC\" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)\n        window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x;\n        window->DC.GroupOffset.x = 0.0f;\n        window->DC.ColumnsOffset.x = 0.0f;\n\n        // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount.\n        // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64.\n        double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x;\n        double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1;\n        window->DC.CursorStartPos  = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y);\n        window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y));\n        window->DC.CursorPos = window->DC.CursorStartPos;\n        window->DC.CursorPosPrevLine = window->DC.CursorPos;\n        window->DC.CursorMaxPos = window->DC.CursorStartPos;\n        window->DC.IdealMaxPos = window->DC.CursorStartPos;\n        window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);\n        window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;\n        window->DC.IsSameLine = window->DC.IsSetPos = false;\n\n        window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n        window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;\n        window->DC.NavLayersActiveMaskNext = 0x00;\n        window->DC.NavIsScrollPushableX = true;\n        window->DC.NavHideHighlightOneFrame = false;\n        window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f);\n\n        window->DC.MenuBarAppending = false;\n        window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);\n        window->DC.TreeDepth = 0;\n        window->DC.TreeHasStackDataDepthMask = window->DC.TreeRecordsClippedNodesY2Mask = 0x00;\n        window->DC.ChildWindows.resize(0);\n        window->DC.StateStorage = &window->StateStorage;\n        window->DC.CurrentColumns = NULL;\n        window->DC.LayoutType = ImGuiLayoutType_Vertical;\n        window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;\n\n        // Default item width. Make it proportional to window size if window manually resizes\n        const bool is_resizable_window = (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize));\n        if (is_resizable_window)\n            window->DC.ItemWidthDefault = ImTrunc(window->Size.x * 0.65f);\n        else\n            window->DC.ItemWidthDefault = ImTrunc(g.FontSize * 16.0f);\n        window->DC.ItemWidth = window->DC.ItemWidthDefault;\n        window->DC.ItemWidthStack.resize(0);\n        window->DC.TextWrapPos = -1.0f; // Disabled\n        window->DC.TextWrapPosStack.resize(0);\n        if (flags & ImGuiWindowFlags_Modal)\n            window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg));\n\n        if (window->AutoFitFramesX > 0)\n            window->AutoFitFramesX--;\n        if (window->AutoFitFramesY > 0)\n            window->AutoFitFramesY--;\n\n        // Clear SetNextWindowXXX data (can aim to move this higher in the function)\n        g.NextWindowData.ClearFlags();\n\n        // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)\n        // We ImGuiFocusRequestFlags_UnlessBelowModal to:\n        // - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed.\n        // - Position window behind the modal that is not a begin-parent of this window.\n        if (want_focus)\n            FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);\n        if (want_focus && window == g.NavWindow)\n            NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls\n\n        // Close requested by platform window (apply to all windows in this viewport)\n        // FIXME: Investigate removing the 'window->Viewport != GetMainViewport()' test, which seems superfluous.\n        if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport())\n            if (window->DockNode == NULL || (window->DockNode->MergedFlags & ImGuiDockNodeFlags_DockSpace) == 0)\n            {\n                IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] Window '%s' closed by PlatformRequestClose\\n\", window->Name);\n                *p_open = false;\n                g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on Alt-F4 so we disable Alt for menu toggle. False positive not an issue. // FIXME-NAV: Try removing.\n            }\n\n        // Pressing Ctrl+C copy window content into the clipboard\n        // [EXPERIMENTAL] Breaks on nested Begin/End pairs. We need to work that out and add better logging scope.\n        // [EXPERIMENTAL] Text outputs has many issues.\n        if (g.IO.ConfigWindowsCopyContentsWithCtrlC)\n            if (g.NavWindow && g.NavWindow->RootWindow == window && g.ActiveId == 0 && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C))\n                LogToClipboard(0);\n\n        // Title bar\n        if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)\n            RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);\n        else if (!(flags & ImGuiWindowFlags_NoTitleBar) && window->DockIsActive)\n            LogText(\"%.*s\\n\", (int)(FindRenderedTextEnd(window->Name) - window->Name), window->Name);\n\n        // Clear hit test shape every frame\n        window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;\n\n        if (flags & ImGuiWindowFlags_Tooltip)\n            g.TooltipPreviousWindow = window;\n\n        if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)\n        {\n            // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source.\n            // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it.\n            if (g.MovingWindow == window && (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0)\n                BeginDockableDragDropSource(window);\n\n            // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead.\n            if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking))\n                if (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != window)\n                    if ((window == window->RootWindowDockTree) && !(window->Flags & ImGuiWindowFlags_DockNodeHost))\n                        BeginDockableDragDropTarget(window);\n        }\n\n        // Set default BgClickFlags\n        // This is set at the end of this function, so UpdateWindowManualResize()/ClampWindowPos() may use last-frame value if overridden by user code.\n        // FIXME: The general intent is that we will later expose config options to default to enable scrolling + select scrolling mouse button.\n        window->BgClickFlags = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->BgClickFlags : (g.IO.ConfigWindowsMoveFromTitleBarOnly ? ImGuiWindowBgClickFlags_None : ImGuiWindowBgClickFlags_Move);\n\n        // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().\n        // This is useful to allow creating context menus on title bar only, etc.\n        window->DC.WindowItemStatusFlags = ImGuiItemStatusFlags_None;\n        window->DC.WindowItemStatusFlags |= IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;\n        SetLastItemDataForWindow(window, title_bar_rect);\n\n        // [DEBUG]\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n        if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId))\n            DebugLocateItemResolveWithLastItem();\n#endif\n\n        // [Test Engine] Register title bar / tab with MoveId.\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n        if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))\n        {\n            window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;\n            IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);\n            window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n        }\n#endif\n    }\n    else\n    {\n        // Skip refresh always mark active\n        if (window->SkipRefresh)\n            SetWindowActiveForSkipRefresh(window);\n\n        // Append\n        SetCurrentViewport(window, window->Viewport);\n        SetCurrentWindow(window);\n        g.NextWindowData.ClearFlags();\n        SetLastItemDataForWindow(window, window->TitleBarRect());\n    }\n\n    if (!(flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh)\n        PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);\n\n    // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default \"Debug\" window is unused)\n    window->WriteAccessed = false;\n    window->BeginCount++;\n\n    // Update visibility\n    if (first_begin_of_the_frame && !window->SkipRefresh)\n    {\n        // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems.\n        // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents.\n        // This is analogous to regular windows being hidden from one frame.\n        // It is especially important as e.g. nested TabBars would otherwise generate flicker in the form of one empty frame, or focus requests won't be processed.\n        if (window->DockIsActive && !window->DockTabIsVisible)\n        {\n            if (window->LastFrameJustFocused == g.FrameCount)\n                window->HiddenFramesCannotSkipItems = 1;\n            else\n                window->HiddenFramesCanSkipItems = 1;\n        }\n\n        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu))\n        {\n            // Child window can be out of sight and have \"negative\" clip windows.\n            // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).\n            IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0 || window->DockIsActive);\n            const bool nav_request = (window->ChildFlags & ImGuiChildFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);\n            if (!g.LogEnabled && !nav_request)\n                if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)\n                {\n                    if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)\n                        window->HiddenFramesCannotSkipItems = 1;\n                    else\n                        window->HiddenFramesCanSkipItems = 1;\n                }\n\n            // Hide along with parent or if parent is collapsed\n            if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))\n                window->HiddenFramesCanSkipItems = 1;\n            if (parent_window && parent_window->HiddenFramesCannotSkipItems > 0)\n                window->HiddenFramesCannotSkipItems = 1;\n        }\n\n        // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point)\n        if (style.Alpha <= 0.0f)\n            window->HiddenFramesCanSkipItems = 1;\n\n        // Update the Hidden flag\n        bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);\n        window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0);\n\n        // Disable inputs for requested number of frames\n        if (window->DisableInputsFrames > 0)\n        {\n            window->DisableInputsFrames--;\n            window->Flags |= ImGuiWindowFlags_NoInputs;\n        }\n\n        // Update the SkipItems flag, used to early out of all items functions (no layout required)\n        bool skip_items = false;\n        if (window->Collapsed || !window->Active || hidden_regular)\n            if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)\n                skip_items = true;\n        window->SkipItems = skip_items;\n\n        // Restore NavLayersActiveMaskNext to previous value when not visible, so a CTRL+Tab back can use a safe value.\n        if (window->SkipItems)\n            window->DC.NavLayersActiveMaskNext = window->DC.NavLayersActiveMask;\n\n        // Sanity check: there are two spots which can set Appearing = true\n        // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false\n        // - in BeginDocked() path when DockNodeIsVisible == DockTabIsVisible == true -> hidden _should_ be all zero // FIXME: Not formally proven, hence the assert.\n        if (window->SkipItems && !window->Appearing)\n            IM_ASSERT(window->Appearing == false); // Please report on GitHub if this triggers: https://github.com/ocornut/imgui/issues/4177\n    }\n    else if (first_begin_of_the_frame)\n    {\n        // Skip refresh mode\n        window->SkipItems = true;\n    }\n\n    // [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.\n    // (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (!window->IsFallbackWindow)\n        if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size))\n        {\n            if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }\n            if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }\n            return false;\n        }\n#endif\n\n    return !window->SkipItems;\n}\n\nvoid ImGui::End()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Error checking: verify that user hasn't called End() too many times!\n    if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)\n    {\n        IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, \"Calling End() too many times!\");\n        return;\n    }\n    ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();\n\n    // Error checking: verify that user doesn't directly call End() on a child window.\n    if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->DockIsActive)\n        IM_ASSERT_USER_ERROR(g.WithinEndChildID == window->ID, \"Must call EndChild() and not End()!\");\n\n    // Close anything that is open\n    if (window->DC.CurrentColumns)\n        EndColumns();\n    if (!(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh)   // Pop inner window clip rectangle\n        PopClipRect();\n    PopFocusScope();\n    if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)\n        EndDisabledOverrideReenable();\n\n    if (window->SkipRefresh)\n    {\n        IM_ASSERT(window->DrawList == NULL);\n        window->DrawList = &window->DrawListInst;\n    }\n\n    // Stop logging\n    if (g.LogWindow == window) // FIXME: add more options for scope of logging\n        LogFinish();\n\n    if (window->DC.IsSetPos)\n        ErrorCheckUsingSetCursorPosToExtendParentBoundaries();\n\n    // Docking: report contents sizes to parent to allow for auto-resize\n    if (window->DockNode && window->DockTabIsVisible)\n        if (ImGuiWindow* host_window = window->DockNode->HostWindow)         // FIXME-DOCK\n            host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding;\n\n    // Pop from window stack\n    g.LastItemData = window_stack_data.ParentLastItemDataBackup;\n    if (window->Flags & ImGuiWindowFlags_ChildMenu)\n        g.BeginMenuDepth--;\n    if (window->Flags & ImGuiWindowFlags_Popup)\n        g.BeginPopupStack.pop_back();\n\n    // Error handling, state recovery\n    if (g.IO.ConfigErrorRecovery)\n        ErrorRecoveryTryToRecoverWindowState(&window_stack_data.StackSizesInBegin);\n\n    g.CurrentWindowStack.pop_back();\n    SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);\n    if (g.CurrentWindow)\n        SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport);\n}\n\nvoid ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiItemFlags item_flags = g.CurrentItemFlags;\n    IM_ASSERT(item_flags == g.ItemFlagsStack.back());\n    if (enabled)\n        item_flags |= option;\n    else\n        item_flags &= ~option;\n    g.CurrentItemFlags = item_flags;\n    g.ItemFlagsStack.push_back(item_flags);\n}\n\nvoid ImGui::PopItemFlag()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT_USER_ERROR_RET(g.ItemFlagsStack.Size > 1, \"Calling PopItemFlag() too many times!\");\n    g.ItemFlagsStack.pop_back();\n    g.CurrentItemFlags = g.ItemFlagsStack.back();\n}\n\n// BeginDisabled()/EndDisabled()\n// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)\n// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.\n// - Feedback welcome at https://github.com/ocornut/imgui/issues/211\n// - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions.\n//   (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls)\n// - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED.\nvoid ImGui::BeginDisabled(bool disabled)\n{\n    ImGuiContext& g = *GImGui;\n    bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;\n    if (!was_disabled && disabled)\n    {\n        g.DisabledAlphaBackup = g.Style.Alpha;\n        g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);\n    }\n    if (was_disabled || disabled)\n        g.CurrentItemFlags |= ImGuiItemFlags_Disabled;\n    g.ItemFlagsStack.push_back(g.CurrentItemFlags); // FIXME-OPT: can we simply skip this and use DisabledStackSize?\n    g.DisabledStackSize++;\n}\n\nvoid ImGui::EndDisabled()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT_USER_ERROR_RET(g.DisabledStackSize > 0, \"Calling EndDisabled() too many times!\");\n    g.DisabledStackSize--;\n    bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;\n    //PopItemFlag();\n    g.ItemFlagsStack.pop_back();\n    g.CurrentItemFlags = g.ItemFlagsStack.back();\n    if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)\n        g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();\n}\n\n// Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name.\n// Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal.\n// The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled().\nvoid ImGui::BeginDisabledOverrideReenable()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.CurrentItemFlags & ImGuiItemFlags_Disabled);\n    g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup = g.Style.Alpha;\n    g.Style.Alpha = g.DisabledAlphaBackup;\n    g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled;\n    g.ItemFlagsStack.push_back(g.CurrentItemFlags);\n    g.DisabledStackSize++;\n}\n\nvoid ImGui::EndDisabledOverrideReenable()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.DisabledStackSize > 0);\n    g.DisabledStackSize--;\n    g.ItemFlagsStack.pop_back();\n    g.CurrentItemFlags = g.ItemFlagsStack.back();\n    g.Style.Alpha = g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup;\n}\n\n// ATTENTION THIS IS IN LEGACY LOCAL SPACE.\nvoid ImGui::PushTextWrapPos(float wrap_local_pos_x)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);\n    window->DC.TextWrapPos = wrap_local_pos_x;\n}\n\nvoid ImGui::PopTextWrapPos()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT_USER_ERROR_RET(window->DC.TextWrapPosStack.Size > 0, \"Calling PopTextWrapPos() too many times!\");\n    window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();\n    window->DC.TextWrapPosStack.pop_back();\n}\n\nstatic ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy)\n{\n    ImGuiWindow* last_window = NULL;\n    while (last_window != window)\n    {\n        last_window = window;\n        window = window->RootWindow;\n        if (popup_hierarchy)\n            window = window->RootWindowPopupTree;\n\t\tif (dock_hierarchy)\n\t\t\twindow = window->RootWindowDockTree;\n\t}\n    return window;\n}\n\nbool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy)\n{\n    ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy);\n    if (window_root == potential_parent)\n        return true;\n    while (window != NULL)\n    {\n        if (window == potential_parent)\n            return true;\n        if (window == window_root) // end of chain\n            return false;\n        window = window->ParentWindow;\n    }\n    return false;\n}\n\nbool ImGui::IsWindowInBeginStack(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    for (int n = g.CurrentWindowStack.Size - 1; n >= 0; n--)\n        if (g.CurrentWindowStack[n].Window == window)\n            return true;\n    return false;\n}\n\nbool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent)\n{\n    if (window->RootWindow == potential_parent)\n        return true;\n    while (window != NULL)\n    {\n        if (window == potential_parent)\n            return true;\n        window = window->ParentWindowInBeginStack;\n    }\n    return false;\n}\n\nbool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)\n{\n    ImGuiContext& g = *GImGui;\n\n    // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array\n    const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below);\n    if (display_layer_delta != 0)\n        return display_layer_delta > 0;\n\n    for (int i = g.Windows.Size - 1; i >= 0; i--)\n    {\n        ImGuiWindow* candidate_window = g.Windows[i];\n        if (candidate_window == potential_above)\n            return true;\n        if (candidate_window == potential_below)\n            return false;\n    }\n    return false;\n}\n\n// Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options.\n// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,\n// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!\n// Refer to FAQ entry \"How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?\" for details.\nbool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0, \"Invalid flags for IsWindowHovered()!\");\n\n    ImGuiWindow* ref_window = g.HoveredWindow;\n    ImGuiWindow* cur_window = g.CurrentWindow;\n    if (ref_window == NULL)\n        return false;\n\n    if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)\n    {\n        IM_ASSERT(cur_window); // Not inside a Begin()/End()\n        const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;\n        const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0;\n        if (flags & ImGuiHoveredFlags_RootWindow)\n            cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);\n\n        bool result;\n        if (flags & ImGuiHoveredFlags_ChildWindows)\n            result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);\n        else\n            result = (ref_window == cur_window);\n        if (!result)\n            return false;\n    }\n\n    if (!IsWindowContentHoverable(ref_window, flags))\n        return false;\n    if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))\n        if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)\n            return false;\n\n    // When changing hovered window we requires a bit of stationary delay before activating hover timer.\n    // FIXME: We don't support delay other than stationary one for now, other delay would need a way\n    // to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true\n    // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.\n    // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.\n    if (flags & ImGuiHoveredFlags_ForTooltip)\n        flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);\n    if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)\n        return false;\n\n    return true;\n}\n\nImGuiID ImGui::GetWindowDockID()\n{\n    ImGuiContext& g = *GImGui;\n    return g.CurrentWindow->DockId;\n}\n\nbool ImGui::IsWindowDocked()\n{\n    ImGuiContext& g = *GImGui;\n    return g.CurrentWindow->DockIsActive;\n}\n\nfloat ImGui::GetWindowWidth()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->Size.x;\n}\n\nfloat ImGui::GetWindowHeight()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->Size.y;\n}\n\nImVec2 ImGui::GetWindowPos()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    return window->Pos;\n}\n\nvoid ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)\n{\n    // Test condition (NB: bit 0 is always true) and clear flags for next time\n    if (cond && (window->SetWindowPosAllowFlags & cond) == 0)\n        return;\n\n    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.\n    window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);\n    window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);\n\n    // Set\n    const ImVec2 old_pos = window->Pos;\n    window->Pos = ImTrunc(pos);\n    ImVec2 offset = window->Pos - old_pos;\n    if (offset.x == 0.0f && offset.y == 0.0f)\n        return;\n    MarkIniSettingsDirty(window);\n    // FIXME: share code with TranslateWindow(), need to confirm whether the 3 rect modified by TranslateWindow() are desirable here.\n    window->DC.CursorPos += offset;         // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor\n    window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.\n    window->DC.IdealMaxPos += offset;\n    window->DC.CursorStartPos += offset;\n}\n\nvoid ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    SetWindowPos(window, pos, cond);\n}\n\nvoid ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)\n{\n    if (ImGuiWindow* window = FindWindowByName(name))\n        SetWindowPos(window, pos, cond);\n}\n\nImVec2 ImGui::GetWindowSize()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->Size;\n}\n\nvoid ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)\n{\n    // Test condition (NB: bit 0 is always true) and clear flags for next time\n    if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)\n        return;\n\n    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.\n    window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);\n\n    // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)\n    if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)\n        window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;\n    if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)\n        window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;\n\n    // Set\n    ImVec2 old_size = window->SizeFull;\n    if (size.x <= 0.0f)\n        window->AutoFitOnlyGrows = false;\n    else\n        window->SizeFull.x = IM_TRUNC(size.x);\n    if (size.y <= 0.0f)\n        window->AutoFitOnlyGrows = false;\n    else\n        window->SizeFull.y = IM_TRUNC(size.y);\n    if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)\n        MarkIniSettingsDirty(window);\n}\n\nvoid ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)\n{\n    SetWindowSize(GImGui->CurrentWindow, size, cond);\n}\n\nvoid ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)\n{\n    if (ImGuiWindow* window = FindWindowByName(name))\n        SetWindowSize(window, size, cond);\n}\n\nvoid ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)\n{\n    // Test condition (NB: bit 0 is always true) and clear flags for next time\n    if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)\n        return;\n    window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);\n\n    // Queue applying in Begin()\n    if (window->WantCollapseToggle)\n        window->Collapsed ^= 1;\n    window->WantCollapseToggle = (window->Collapsed != collapsed);\n}\n\nvoid ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)\n{\n    IM_ASSERT(window->HitTestHoleSize.x == 0);     // We don't support multiple holes/hit test filters\n    window->HitTestHoleSize = ImVec2ih(size);\n    window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);\n}\n\nvoid ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window)\n{\n    window->Hidden = window->SkipItems = true;\n    window->HiddenFramesCanSkipItems = 1;\n}\n\nvoid ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)\n{\n    SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);\n}\n\nbool ImGui::IsWindowCollapsed()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->Collapsed;\n}\n\nbool ImGui::IsWindowAppearing()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->Appearing;\n}\n\nvoid ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)\n{\n    if (ImGuiWindow* window = FindWindowByName(name))\n        SetWindowCollapsed(window, collapsed, cond);\n}\n\nvoid ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasPos;\n    g.NextWindowData.PosVal = pos;\n    g.NextWindowData.PosPivotVal = pivot;\n    g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;\n    g.NextWindowData.PosUndock = true;\n}\n\nvoid ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasSize;\n    g.NextWindowData.SizeVal = size;\n    g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;\n}\n\n// For each axis:\n// - Use 0.0f as min or FLT_MAX as max if you don't want limits, e.g. size_min = (500.0f, 0.0f), size_max = (FLT_MAX, FLT_MAX) sets a minimum width.\n// - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.\n// - See \"Demo->Examples->Constrained-resizing window\" for examples.\nvoid ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasSizeConstraint;\n    g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);\n    g.NextWindowData.SizeCallback = custom_callback;\n    g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;\n}\n\n// Content size = inner scrollable rectangle, padded with WindowPadding.\n// SetNextWindowContentSize(ImVec2(100,100)) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.\nvoid ImGui::SetNextWindowContentSize(const ImVec2& size)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasContentSize;\n    g.NextWindowData.ContentSizeVal = ImTrunc(size);\n}\n\nvoid ImGui::SetNextWindowScroll(const ImVec2& scroll)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasScroll;\n    g.NextWindowData.ScrollVal = scroll;\n}\n\nvoid ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasCollapsed;\n    g.NextWindowData.CollapsedVal = collapsed;\n    g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;\n}\n\nvoid ImGui::SetNextWindowBgAlpha(float alpha)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasBgAlpha;\n    g.NextWindowData.BgAlphaVal = alpha;\n}\n\nvoid ImGui::SetNextWindowViewport(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasViewport;\n    g.NextWindowData.ViewportId = id;\n}\n\nvoid ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasDock;\n    g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always;\n    g.NextWindowData.DockId = id;\n}\n\nvoid ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasWindowClass;\n    g.NextWindowData.WindowClass = *window_class;\n}\n\n// This is experimental and meant to be a toy for exploring a future/wider range of features.\nvoid ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasRefreshPolicy;\n    g.NextWindowData.RefreshFlagsVal = flags;\n}\n\nImDrawList* ImGui::GetWindowDrawList()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    return window->DrawList;\n}\n\nfloat ImGui::GetWindowDpiScale()\n{\n    ImGuiContext& g = *GImGui;\n    return g.CurrentDpiScale;\n}\n\nImGuiViewport* ImGui::GetWindowViewport()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport);\n    return g.CurrentViewport;\n}\n\nImFont* ImGui::GetFont()\n{\n    return GImGui->Font;\n}\n\nImFontBaked* ImGui::GetFontBaked()\n{\n    return GImGui->FontBaked;\n}\n\n// Get current font size (= height in pixels) of current font, with global scale factors applied.\n// - Use style.FontSizeBase to get value before global scale factors.\n// - recap: ImGui::GetFontSize() == style.FontSizeBase * (style.FontScaleMain * style.FontScaleDpi * other_scaling_factors)\nfloat ImGui::GetFontSize()\n{\n    return GImGui->FontSize;\n}\n\nImVec2 ImGui::GetFontTexUvWhitePixel()\n{\n    return GImGui->DrawListSharedData.TexUvWhitePixel;\n}\n\n// Prefer using PushFont(NULL, style.FontSizeBase * factor), or use style.FontScaleMain to scale all windows.\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\nvoid ImGui::SetWindowFontScale(float scale)\n{\n    IM_ASSERT(scale > 0.0f);\n    ImGuiWindow* window = GetCurrentWindow();\n    window->FontWindowScale = scale;\n    UpdateCurrentFontSize(0.0f);\n}\n#endif\n\nvoid ImGui::PushFocusScope(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiFocusScopeData data;\n    data.ID = id;\n    data.WindowID = g.CurrentWindow->ID;\n    g.FocusScopeStack.push_back(data);\n    g.CurrentFocusScopeId = id;\n}\n\nvoid ImGui::PopFocusScope()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT_USER_ERROR_RET(g.FocusScopeStack.Size > g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack, \"Calling PopFocusScope() too many times!\");\n    g.FocusScopeStack.pop_back();\n    g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0;\n}\n\nvoid ImGui::SetNavFocusScope(ImGuiID focus_scope_id)\n{\n    ImGuiContext& g = *GImGui;\n    g.NavFocusScopeId = focus_scope_id;\n    g.NavFocusRoute.resize(0); // Invalidate\n    if (focus_scope_id == 0)\n        return;\n    IM_ASSERT(g.NavWindow != NULL);\n\n    // Store current path (in reverse order)\n    if (focus_scope_id == g.CurrentFocusScopeId)\n    {\n        // Top of focus stack contains local focus scopes inside current window\n        for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--)\n            g.NavFocusRoute.push_back(g.FocusScopeStack.Data[n]);\n    }\n    else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId)\n        g.NavFocusRoute.push_back({ focus_scope_id, g.NavWindow->ID });\n    else\n        return;\n\n    // Then follow on manually set ParentWindowForFocusRoute field (#6798)\n    for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute)\n        g.NavFocusRoute.push_back({ window->NavRootFocusScopeId, window->ID });\n    IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3\n}\n\n// Focus = move navigation cursor, set scrolling, set focus window.\nvoid ImGui::FocusItem()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IMGUI_DEBUG_LOG_FOCUS(\"FocusItem(0x%08x) in window \\\"%s\\\"\\n\", g.LastItemData.ID, window->Name);\n    if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this?\n    {\n        IMGUI_DEBUG_LOG_FOCUS(\"FocusItem() ignored while DragDropActive!\\n\");\n        return;\n    }\n\n    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible | ImGuiNavMoveFlags_NoSelect;\n    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;\n    SetNavWindow(window);\n    NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags);\n    NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);\n}\n\nvoid ImGui::ActivateItemByID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    g.NavNextActivateId = id;\n    g.NavNextActivateFlags = ImGuiActivateFlags_None;\n}\n\n// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!\n// But ActivateItem() should function without altering scroll/focus?\nvoid ImGui::SetKeyboardFocusHere(int offset)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(offset >= -1);    // -1 is allowed but not below\n    IMGUI_DEBUG_LOG_FOCUS(\"SetKeyboardFocusHere(%d) in window \\\"%s\\\"\\n\", offset, window->Name);\n\n    // It makes sense in the vast majority of cases to never interrupt a drag and drop.\n    // When we refactor this function into ActivateItem() we may want to make this an option.\n    // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but\n    // is also automatically dropped in the event g.ActiveId is stolen.\n    if (g.DragDropActive || g.MovingWindow != NULL)\n    {\n        IMGUI_DEBUG_LOG_FOCUS(\"SetKeyboardFocusHere() ignored while DragDropActive!\\n\");\n        return;\n    }\n\n    SetNavWindow(window);\n\n    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible;\n    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;\n    NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.\n    if (offset == -1)\n    {\n        NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);\n    }\n    else\n    {\n        g.NavTabbingDir = 1;\n        g.NavTabbingCounter = offset + 1;\n    }\n}\n\nvoid ImGui::SetItemDefaultFocus()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (!window->Appearing)\n        return;\n    if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent)\n        return;\n\n    g.NavInitRequest = false;\n    NavApplyItemToResult(&g.NavInitResult);\n    NavUpdateAnyRequestFlag();\n\n    // Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll)\n    if (!window->ClipRect.Contains(g.LastItemData.Rect))\n        ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None);\n}\n\nvoid ImGui::SetStateStorage(ImGuiStorage* tree)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    window->DC.StateStorage = tree ? tree : &window->StateStorage;\n}\n\nImGuiStorage* ImGui::GetStateStorage()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->DC.StateStorage;\n}\n\nbool ImGui::IsRectVisible(const ImVec2& size)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));\n}\n\nbool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] FONTS, TEXTURES\n//-----------------------------------------------------------------------------\n// Most of the relevant font logic is in imgui_draw.cpp.\n// Those are high-level support functions.\n//-----------------------------------------------------------------------------\n// - UpdateTexturesNewFrame() [Internal]\n// - UpdateTexturesEndFrame() [Internal]\n// - UpdateFontsNewFrame() [Internal]\n// - UpdateFontsEndFrame() [Internal]\n// - GetDefaultFont() [Internal]\n// - RegisterUserTexture() [Internal]\n// - UnregisterUserTexture() [Internal]\n// - RegisterFontAtlas() [Internal]\n// - UnregisterFontAtlas() [Internal]\n// - SetCurrentFont() [Internal]\n// - UpdateCurrentFontSize() [Internal]\n// - SetFontRasterizerDensity() [Internal]\n// - PushFont()\n// - PopFont()\n//-----------------------------------------------------------------------------\n\nstatic void ImGui::UpdateTexturesNewFrame()\n{\n    // Cannot update every atlases based on atlas's FrameCount < g.FrameCount, because an atlas may be shared by multiple contexts with different frame count.\n    ImGuiContext& g = *GImGui;\n    const bool has_textures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0;\n    for (ImFontAtlas* atlas : g.FontAtlases)\n    {\n        if (atlas->OwnerContext == &g)\n        {\n            ImFontAtlasUpdateNewFrame(atlas, g.FrameCount, has_textures);\n        }\n        else\n        {\n            // (1) If you manage font atlases yourself, e.g. create a ImFontAtlas yourself you need to call ImFontAtlasUpdateNewFrame() on it.\n            // Otherwise, calling ImGui::CreateContext() without parameter will create an atlas owned by the context.\n            // (2) If you have multiple font atlases, make sure the 'atlas->RendererHasTextures' as specified in the ImFontAtlasUpdateNewFrame() call matches for that.\n            // (3) If you have multiple imgui contexts, they also need to have a matching value for ImGuiBackendFlags_RendererHasTextures.\n            IM_ASSERT(atlas->Builder != NULL && atlas->Builder->FrameCount != -1);\n            IM_ASSERT(atlas->RendererHasTextures == has_textures);\n        }\n    }\n}\n\n// Build a single texture list\nstatic void ImGui::UpdateTexturesEndFrame()\n{\n    ImGuiContext& g = *GImGui;\n    g.PlatformIO.Textures.resize(0);\n    for (ImFontAtlas* atlas : g.FontAtlases)\n        for (ImTextureData* tex : atlas->TexList)\n        {\n            // We provide this information so backends can decide whether to destroy textures.\n            // This means in practice that if N imgui contexts are created with a shared atlas, we assume all of them have a backend initialized.\n            tex->RefCount = (unsigned short)atlas->RefCount;\n            g.PlatformIO.Textures.push_back(tex);\n        }\n    for (ImTextureData* tex : g.UserTextures)\n        g.PlatformIO.Textures.push_back(tex);\n}\n\nvoid ImGui::UpdateFontsNewFrame()\n{\n    ImGuiContext& g = *GImGui;\n    if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)\n        for (ImFontAtlas* atlas : g.FontAtlases)\n            atlas->Locked = true;\n\n    if (g.Style._NextFrameFontSizeBase != 0.0f)\n    {\n        g.Style.FontSizeBase = g.Style._NextFrameFontSizeBase;\n        g.Style._NextFrameFontSizeBase = 0.0f;\n    }\n\n    // Apply default font size the first time\n    ImFont* font = ImGui::GetDefaultFont();\n    if (g.Style.FontSizeBase <= 0.0f)\n        g.Style.FontSizeBase = (font->LegacySize > 0.0f ? font->LegacySize : FONT_DEFAULT_SIZE_BASE);\n\n    // Set initial font\n    g.Font = font;\n    g.FontSizeBase = g.Style.FontSizeBase;\n    g.FontSize = 0.0f;\n    ImFontStackData font_stack_data = { font, g.Style.FontSizeBase, g.Style.FontSizeBase };           // <--- Will restore FontSize\n    SetCurrentFont(font_stack_data.Font, font_stack_data.FontSizeBeforeScaling, 0.0f); // <--- but use 0.0f to enable scale\n    g.FontStack.push_back(font_stack_data);\n    IM_ASSERT(g.Font->IsLoaded());\n}\n\nvoid ImGui::UpdateFontsEndFrame()\n{\n    PopFont();\n}\n\nImFont* ImGui::GetDefaultFont()\n{\n    ImGuiContext& g = *GImGui;\n    ImFontAtlas* atlas = g.IO.Fonts;\n    if (atlas->Builder == NULL || atlas->Fonts.Size == 0)\n        ImFontAtlasBuildMain(atlas);\n    return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0];\n}\n\n// EXPERIMENTAL: DO NOT USE YET.\nvoid ImGui::RegisterUserTexture(ImTextureData* tex)\n{\n    ImGuiContext& g = *GImGui;\n    tex->RefCount++;\n    g.UserTextures.push_back(tex);\n}\n\nvoid ImGui::UnregisterUserTexture(ImTextureData* tex)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(tex->RefCount > 0);\n    tex->RefCount--;\n    g.UserTextures.find_erase(tex);\n}\n\nvoid ImGui::RegisterFontAtlas(ImFontAtlas* atlas)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.FontAtlases.Size == 0)\n        IM_ASSERT(atlas == g.IO.Fonts);\n    atlas->RefCount++;\n    g.FontAtlases.push_back(atlas);\n    ImFontAtlasAddDrawListSharedData(atlas, &g.DrawListSharedData);\n    for (ImTextureData* tex : atlas->TexList)\n        tex->RefCount = (unsigned short)atlas->RefCount;\n}\n\nvoid ImGui::UnregisterFontAtlas(ImFontAtlas* atlas)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(atlas->RefCount > 0);\n    ImFontAtlasRemoveDrawListSharedData(atlas, &g.DrawListSharedData);\n    g.FontAtlases.find_erase(atlas);\n    atlas->RefCount--;\n    for (ImTextureData* tex : atlas->TexList)\n        tex->RefCount = (unsigned short)atlas->RefCount;\n}\n\n// Use ImDrawList::_SetTexture(), making our shared g.FontStack[] authoritative against window-local ImDrawList.\n// - Whereas ImDrawList::PushTexture()/PopTexture() is not to be used across Begin() calls.\n// - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did...\n//   - Some code paths never really fully worked with multiple atlas textures.\n//   - The right-ish solution may be to remove _SetTexture() and make AddText/RenderText lazily call PushTexture()/PopTexture()\n//     the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem\n//     because we have a concrete need and a test bed for multiple atlas textures.\n// FIXME-NEWATLAS-V2: perhaps we can now leverage ImFontAtlasUpdateDrawListsTextures() ?\nvoid ImGui::SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling)\n{\n    ImGuiContext& g = *GImGui;\n    g.Font = font;\n    g.FontSizeBase = font_size_before_scaling;\n    UpdateCurrentFontSize(font_size_after_scaling);\n\n    if (font != NULL)\n    {\n        IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n        IM_ASSERT(font->Scale > 0.0f);\n#endif\n        ImFontAtlas* atlas = font->OwnerAtlas;\n        g.DrawListSharedData.FontAtlas = atlas;\n        g.DrawListSharedData.Font = font;\n        ImFontAtlasUpdateDrawListsSharedData(atlas);\n        if (g.CurrentWindow != NULL)\n            g.CurrentWindow->DrawList->_SetTexture(atlas->TexRef);\n    }\n}\n\nvoid ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    g.Style.FontSizeBase = g.FontSizeBase;\n\n    // Restoring is pretty much only used by PopFont()\n    float final_size = (restore_font_size_after_scaling > 0.0f) ? restore_font_size_after_scaling : 0.0f;\n    if (final_size == 0.0f)\n    {\n        final_size = g.FontSizeBase;\n\n        // Global scale factors\n        final_size *= g.Style.FontScaleMain;    // Main global scale factor\n        final_size *= g.Style.FontScaleDpi;     // Per-monitor/viewport DPI scale factor (in docking branch: automatically updated when io.ConfigDpiScaleFonts is enabled).\n\n        // Window scale (mostly obsolete now)\n        if (window != NULL)\n            final_size *= window->FontWindowScale;\n\n        // Legacy scale factors\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n        final_size *= g.IO.FontGlobalScale; // Use style.FontScaleMain instead!\n        if (g.Font != NULL)\n            final_size *= g.Font->Scale;    // Was never really useful.\n#endif\n    }\n\n    // Round font size\n    // - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.\n    // - We may support it better later and remove this rounding.\n    final_size = GetRoundedFontSize(final_size);\n    final_size = ImClamp(final_size, 1.0f, IMGUI_FONT_SIZE_MAX);\n    if (g.Font != NULL && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures))\n        g.Font->CurrentRasterizerDensity = g.FontRasterizerDensity;\n\n    g.FontSize = final_size;\n    g.DrawListSharedData.FontSize = g.FontSize;\n\n    // Early out to avoid hidden window keeping bakes referenced and out of GC reach.\n    // - However this leave a pretty subtle and damning error surface area if g.FontBaked was mismatching.\n    //   Probably needs to be reevaluated into e.g. setting g.FontBaked = nullptr to mark it as dirty.\n    // - Note that 'PushFont(); Begin(); End(); PopFont()' from within any collapsed window is not compromised, because Begin() calls SetCurrentWindow()->...->UpdateCurrentSize()\n    if (window != NULL && window->SkipItems)\n    {\n        ImGuiTable* table = g.CurrentTable;\n        const bool allow_early_out = table == NULL || (table->CurrentColumn != -1 && table->Columns[table->CurrentColumn].IsSkipItems == false); // See 8465#issuecomment-2951509561 and #8865. Ideally the SkipItems=true in tables would be amended with extra data.\n        if (allow_early_out)\n            return;\n    }\n\n    g.FontBaked = (g.Font != NULL && window != NULL) ? g.Font->GetFontBaked(final_size) : NULL;\n    g.FontBakedScale = (g.Font != NULL && window != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f;\n    g.DrawListSharedData.FontScale = g.FontBakedScale;\n    if (g.Font)\n    {\n        ImFontAtlas* atlas = g.Font->OwnerAtlas;\n        g.DrawListSharedData.ShadowRectIds = &atlas->ShadowRectIds[0];\n        g.DrawListSharedData.ShadowRectUvs = &atlas->ShadowRectUvs[0];\n    }\n}\n\n// Exposed in case user may want to override setting density.\n// IMPORTANT: Begin()/End() is overriding density. Be considerate of this you change it.\nvoid ImGui::SetFontRasterizerDensity(float rasterizer_density)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures);\n    if (g.FontRasterizerDensity == rasterizer_density)\n        return;\n    g.FontRasterizerDensity = rasterizer_density;\n    UpdateCurrentFontSize(0.0f);\n}\n\n// If you want to scale an existing font size! Read comments in imgui.h!\nvoid ImGui::PushFont(ImFont* font, float font_size_base)\n{\n    ImGuiContext& g = *GImGui;\n    if (font == NULL) // Before 1.92 (June 2025), PushFont(NULL) == PushFont(GetDefaultFont())\n        font = g.Font;\n    IM_ASSERT(font != NULL);\n    IM_ASSERT(font_size_base >= 0.0f);\n\n    g.FontStack.push_back({ g.Font, g.FontSizeBase, g.FontSize });\n    if (font_size_base == 0.0f)\n        font_size_base = g.FontSizeBase; // Keep current font size\n    SetCurrentFont(font, font_size_base, 0.0f);\n}\n\nvoid  ImGui::PopFont()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT_USER_ERROR_RET(g.FontStack.Size > 0, \"Calling PopFont() too many times!\");\n    ImFontStackData* font_stack_data = &g.FontStack.back();\n    SetCurrentFont(font_stack_data->Font, font_stack_data->FontSizeBeforeScaling, font_stack_data->FontSizeAfterScaling);\n    g.FontStack.pop_back();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ID STACK\n//-----------------------------------------------------------------------------\n\n// This is one of the very rare legacy case where we use ImGuiWindow methods,\n// it should ideally be flattened at some point but it's been used a lots by widgets.\nIM_MSVC_RUNTIME_CHECKS_OFF\nImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)\n{\n    ImGuiID seed = IDStack.back();\n    ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    ImGuiContext& g = *Ctx;\n    if (g.DebugHookIdInfoId == id)\n        ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);\n#endif\n    return id;\n}\n\nImGuiID ImGuiWindow::GetID(const void* ptr)\n{\n    ImGuiID seed = IDStack.back();\n    ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    ImGuiContext& g = *Ctx;\n    if (g.DebugHookIdInfoId == id)\n        ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);\n#endif\n    return id;\n}\n\nImGuiID ImGuiWindow::GetID(int n)\n{\n    ImGuiID seed = IDStack.back();\n    ImGuiID id = ImHashData(&n, sizeof(n), seed);\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    ImGuiContext& g = *Ctx;\n    if (g.DebugHookIdInfoId == id)\n        ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);\n#endif\n    return id;\n}\n\n// This is only used in rare/specific situations to manufacture an ID out of nowhere.\n// FIXME: Consider instead storing last non-zero ID + count of successive zero-ID, and combine those?\nImGuiID ImGuiWindow::GetIDFromPos(const ImVec2& p_abs)\n{\n    ImGuiID seed = IDStack.back();\n    ImVec2 p_rel = ImGui::WindowPosAbsToRel(this, p_abs);\n    ImGuiID id = ImHashData(&p_rel, sizeof(p_rel), seed);\n    return id;\n}\n\n// \"\nImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)\n{\n    ImGuiID seed = IDStack.back();\n    ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);\n    ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);\n    return id;\n}\n\nvoid ImGui::PushID(const char* str_id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiID id = window->GetID(str_id);\n    window->IDStack.push_back(id);\n}\n\nvoid ImGui::PushID(const char* str_id_begin, const char* str_id_end)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiID id = window->GetID(str_id_begin, str_id_end);\n    window->IDStack.push_back(id);\n}\n\nvoid ImGui::PushID(const void* ptr_id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiID id = window->GetID(ptr_id);\n    window->IDStack.push_back(id);\n}\n\nvoid ImGui::PushID(int int_id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiID id = window->GetID(int_id);\n    window->IDStack.push_back(id);\n}\n\n// Push a given id value ignoring the ID stack as a seed.\nvoid ImGui::PushOverrideID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (g.DebugHookIdInfoId == id)\n        DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);\n#endif\n    window->IDStack.push_back(id);\n}\n\n// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call\n// (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.\n//  for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)\nImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)\n{\n    ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    ImGuiContext& g = *GImGui;\n    if (g.DebugHookIdInfoId == id)\n        DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);\n#endif\n    return id;\n}\n\nImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)\n{\n    ImGuiID id = ImHashData(&n, sizeof(n), seed);\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    ImGuiContext& g = *GImGui;\n    if (g.DebugHookIdInfoId == id)\n        DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);\n#endif\n    return id;\n}\n\nvoid ImGui::PopID()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    IM_ASSERT_USER_ERROR_RET(window->IDStack.Size > 1, \"Calling PopID() too many times!\");\n    window->IDStack.pop_back();\n}\n\nImGuiID ImGui::GetID(const char* str_id)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->GetID(str_id);\n}\n\nImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->GetID(str_id_begin, str_id_end);\n}\n\nImGuiID ImGui::GetID(const void* ptr_id)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->GetID(ptr_id);\n}\n\nImGuiID ImGui::GetID(int int_id)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->GetID(int_id);\n}\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n//-----------------------------------------------------------------------------\n// [SECTION] INPUTS\n//-----------------------------------------------------------------------------\n// - GetModForLRModKey() [Internal]\n// - FixupKeyChord() [Internal]\n// - GetKeyData() [Internal]\n// - GetKeyIndex() [Internal]\n// - GetKeyName()\n// - GetKeyChordName() [Internal]\n// - CalcTypematicRepeatAmount() [Internal]\n// - GetTypematicRepeatRate() [Internal]\n// - GetKeyPressedAmount() [Internal]\n// - GetKeyMagnitude2d() [Internal]\n//-----------------------------------------------------------------------------\n// - UpdateKeyRoutingTable() [Internal]\n// - GetRoutingIdFromOwnerId() [Internal]\n// - GetShortcutRoutingData() [Internal]\n// - CalcRoutingScore() [Internal]\n// - SetShortcutRouting() [Internal]\n// - TestShortcutRouting() [Internal]\n//-----------------------------------------------------------------------------\n// - IsKeyDown()\n// - IsKeyPressed()\n// - IsKeyReleased()\n//-----------------------------------------------------------------------------\n// - IsMouseDown()\n// - IsMouseClicked()\n// - IsMouseReleased()\n// - IsMouseDoubleClicked()\n// - GetMouseClickedCount()\n// - IsMouseHoveringRect() [Internal]\n// - IsMouseDragPastThreshold() [Internal]\n// - IsMouseDragging()\n// - GetMousePos()\n// - SetMousePos() [Internal]\n// - GetMousePosOnOpeningCurrentPopup()\n// - IsMousePosValid()\n// - IsAnyMouseDown()\n// - GetMouseDragDelta()\n// - ResetMouseDragDelta()\n// - GetMouseCursor()\n// - SetMouseCursor()\n//-----------------------------------------------------------------------------\n// - UpdateAliasKey()\n// - GetMergedModsFromKeys()\n// - UpdateKeyboardInputs()\n// - UpdateMouseInputs()\n//-----------------------------------------------------------------------------\n// - LockWheelingWindow [Internal]\n// - FindBestWheelingWindow [Internal]\n// - UpdateMouseWheel() [Internal]\n//-----------------------------------------------------------------------------\n// - SetNextFrameWantCaptureKeyboard()\n// - SetNextFrameWantCaptureMouse()\n//-----------------------------------------------------------------------------\n// - GetInputSourceName() [Internal]\n// - DebugPrintInputEvent() [Internal]\n// - UpdateInputEvents() [Internal]\n//-----------------------------------------------------------------------------\n// - GetKeyOwner() [Internal]\n// - TestKeyOwner() [Internal]\n// - SetKeyOwner() [Internal]\n// - SetItemKeyOwner() [Internal]\n// - Shortcut() [Internal]\n//-----------------------------------------------------------------------------\n\nstatic ImGuiKeyChord GetModForLRModKey(ImGuiKey key)\n{\n    if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl)\n        return ImGuiMod_Ctrl;\n    if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift)\n        return ImGuiMod_Shift;\n    if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt)\n        return ImGuiMod_Alt;\n    if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper)\n        return ImGuiMod_Super;\n    return ImGuiMod_None;\n}\n\nImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord)\n{\n    // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.\n    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);\n    if (IsLRModKey(key))\n        key_chord |= GetModForLRModKey(key);\n    return key_chord;\n}\n\nImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)\n{\n    ImGuiContext& g = *ctx;\n\n    // Special storage location for mods\n    if (key & ImGuiMod_Mask_)\n        key = ConvertSingleModFlagToKey(key);\n\n    IM_ASSERT(IsNamedKey(key) && \"Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.\");\n    return &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];\n}\n\n// Those names are provided for debugging purpose and are not meant to be saved persistently nor compared.\nstatic const char* const GKeyNames[] =\n{\n    \"Tab\", \"LeftArrow\", \"RightArrow\", \"UpArrow\", \"DownArrow\", \"PageUp\", \"PageDown\",\n    \"Home\", \"End\", \"Insert\", \"Delete\", \"Backspace\", \"Space\", \"Enter\", \"Escape\",\n    \"LeftCtrl\", \"LeftShift\", \"LeftAlt\", \"LeftSuper\", \"RightCtrl\", \"RightShift\", \"RightAlt\", \"RightSuper\", \"Menu\",\n    \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\",\n    \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\",\n    \"F1\", \"F2\", \"F3\", \"F4\", \"F5\", \"F6\", \"F7\", \"F8\", \"F9\", \"F10\", \"F11\", \"F12\",\n    \"F13\", \"F14\", \"F15\", \"F16\", \"F17\", \"F18\", \"F19\", \"F20\", \"F21\", \"F22\", \"F23\", \"F24\",\n    \"Apostrophe\", \"Comma\", \"Minus\", \"Period\", \"Slash\", \"Semicolon\", \"Equal\", \"LeftBracket\",\n    \"Backslash\", \"RightBracket\", \"GraveAccent\", \"CapsLock\", \"ScrollLock\", \"NumLock\", \"PrintScreen\",\n    \"Pause\", \"Keypad0\", \"Keypad1\", \"Keypad2\", \"Keypad3\", \"Keypad4\", \"Keypad5\", \"Keypad6\",\n    \"Keypad7\", \"Keypad8\", \"Keypad9\", \"KeypadDecimal\", \"KeypadDivide\", \"KeypadMultiply\",\n    \"KeypadSubtract\", \"KeypadAdd\", \"KeypadEnter\", \"KeypadEqual\",\n    \"AppBack\", \"AppForward\", \"Oem102\",\n    \"GamepadStart\", \"GamepadBack\",\n    \"GamepadFaceLeft\", \"GamepadFaceRight\", \"GamepadFaceUp\", \"GamepadFaceDown\",\n    \"GamepadDpadLeft\", \"GamepadDpadRight\", \"GamepadDpadUp\", \"GamepadDpadDown\",\n    \"GamepadL1\", \"GamepadR1\", \"GamepadL2\", \"GamepadR2\", \"GamepadL3\", \"GamepadR3\",\n    \"GamepadLStickLeft\", \"GamepadLStickRight\", \"GamepadLStickUp\", \"GamepadLStickDown\",\n    \"GamepadRStickLeft\", \"GamepadRStickRight\", \"GamepadRStickUp\", \"GamepadRStickDown\",\n    \"MouseLeft\", \"MouseRight\", \"MouseMiddle\", \"MouseX1\", \"MouseX2\", \"MouseWheelX\", \"MouseWheelY\",\n    \"ModCtrl\", \"ModShift\", \"ModAlt\", \"ModSuper\", // ReservedForModXXX are showing the ModXXX names.\n};\nIM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_COUNTOF(GKeyNames));\n\nconst char* ImGui::GetKeyName(ImGuiKey key)\n{\n    if (key == ImGuiKey_None)\n        return \"None\";\n    IM_ASSERT(IsNamedKeyOrMod(key) && \"Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.\");\n    if (key & ImGuiMod_Mask_)\n        key = ConvertSingleModFlagToKey(key);\n    if (!IsNamedKey(key))\n        return \"Unknown\";\n\n    return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];\n}\n\n// Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super.\n// Lifetime of return value: valid until next call to same function.\nconst char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord)\n{\n    ImGuiContext& g = *GImGui;\n\n    const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);\n    if (IsLRModKey(key))\n        key_chord &= ~GetModForLRModKey(key); // Return \"Ctrl+LeftShift\" instead of \"Ctrl+Shift+LeftShift\"\n    ImFormatString(g.TempKeychordName, IM_COUNTOF(g.TempKeychordName), \"%s%s%s%s%s\",\n        (key_chord & ImGuiMod_Ctrl) ? \"Ctrl+\" : \"\",\n        (key_chord & ImGuiMod_Shift) ? \"Shift+\" : \"\",\n        (key_chord & ImGuiMod_Alt) ? \"Alt+\" : \"\",\n        (key_chord & ImGuiMod_Super) ? \"Super+\" : \"\",\n        (key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : \"\");\n    size_t len;\n    if (key == ImGuiKey_None && key_chord != 0)\n        if ((len = ImStrlen(g.TempKeychordName)) != 0) // Remove trailing '+'\n            g.TempKeychordName[len - 1] = 0;\n    return g.TempKeychordName;\n}\n\n// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)\n// t1 = current time (e.g.: g.Time)\n// An event is triggered at:\n//  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N\nint ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)\n{\n    if (t1 == 0.0f)\n        return 1;\n    if (t0 >= t1)\n        return 0;\n    if (repeat_rate <= 0.0f)\n        return t0 < repeat_delay && t1 >= repeat_delay;\n    const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);\n    const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);\n    const int count = count_t1 - count_t0;\n    return count;\n}\n\nvoid ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate)\n{\n    ImGuiContext& g = *GImGui;\n    switch (flags & ImGuiInputFlags_RepeatRateMask_)\n    {\n    case ImGuiInputFlags_RepeatRateNavMove:             *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return;\n    case ImGuiInputFlags_RepeatRateNavTweak:            *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return;\n    case ImGuiInputFlags_RepeatRateDefault: default:    *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return;\n    }\n}\n\n// Return value representing the number of presses in the last time period, for the given repeat rate\n// (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)\nint ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiKeyData* key_data = GetKeyData(key);\n    if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)\n        return 0;\n    const float t = key_data->DownDuration;\n    return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);\n}\n\n// Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values).\nImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down)\n{\n    return ImVec2(\n        GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue,\n        GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue);\n}\n\n// Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.\n//   Entries   D,A,B,B,A,C,B     --> A,A,B,B,B,C,D\n//   Index     A:1 B:2 C:5 D:0   --> A:0 B:2 C:5 D:6\n// See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.\nstatic void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)\n{\n    ImGuiContext& g = *GImGui;\n    rt->EntriesNext.resize(0);\n    for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))\n    {\n        const int new_routing_start_idx = rt->EntriesNext.Size;\n        ImGuiKeyRoutingData* routing_entry;\n        for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)\n        {\n            routing_entry = &rt->Entries[old_routing_idx];\n            routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore;\n            routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry\n            routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner;\n            routing_entry->RoutingNextScore = 0;\n            if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner)\n                continue;\n            rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer\n\n            // Apply routing to owner if there's no owner already (RoutingCurr == None at this point)\n            // This is the result of previous frame's SetShortcutRouting() call.\n            if (routing_entry->Mods == g.IO.KeyMods)\n            {\n                ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);\n                if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)\n                {\n                    owner_data->OwnerCurr = routing_entry->RoutingCurr;\n                    //IMGUI_DEBUG_LOG(\"SetKeyOwner(%s, owner_id=0x%08X) via Routing\\n\", GetKeyName(key), routing_entry->RoutingCurr);\n                }\n            }\n        }\n\n        // Rewrite linked-list\n        rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1);\n        for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++)\n            rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1);\n    }\n    rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes\n}\n\n// owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope().\nstatic inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;\n}\n\nImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)\n{\n    // Majority of shortcuts will be Key + any number of Mods\n    // We accept _Single_ mod with ImGuiKey_None.\n    //  - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl);                    // Legal\n    //  - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift);   // Legal\n    //  - Shortcut(ImGuiMod_Ctrl);                                 // Legal\n    //  - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift);                // Not legal\n    ImGuiContext& g = *GImGui;\n    ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;\n    ImGuiKeyRoutingData* routing_data;\n    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);\n    ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);\n    if (key == ImGuiKey_None)\n        key = ConvertSingleModFlagToKey(mods);\n    IM_ASSERT(IsNamedKey(key));\n\n    // Get (in the majority of case, the linked list will have one element so this should be 2 reads.\n    // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame).\n    for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex)\n    {\n        routing_data = &rt->Entries[idx];\n        if (routing_data->Mods == mods)\n            return routing_data;\n    }\n\n    // Add to linked-list\n    ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size;\n    rt->Entries.push_back(ImGuiKeyRoutingData());\n    routing_data = &rt->Entries[routing_data_idx];\n    routing_data->Mods = (ImU16)mods;\n    routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list\n    rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx;\n    return routing_data;\n}\n\n// Current score encoding\n//  -        0: Never route\n//  -        1: ImGuiInputFlags_RouteGlobal    (lower priority)\n//  - 100..199: ImGuiInputFlags_RouteFocused   (if window in focus-stack)\n//         200: ImGuiInputFlags_RouteGlobal  | ImGuiInputFlags_RouteOverFocused\n//         300: ImGuiInputFlags_RouteActive or ImGuiInputFlags_RouteFocused (if item active)\n//         400: ImGuiInputFlags_RouteGlobal  | ImGuiInputFlags_RouteOverActive\n//  - 500..599: ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteOverActive (if window in focus-stack) (higher priority)\n// 'flags' should include an explicit routing policy\nstatic int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (flags & ImGuiInputFlags_RouteFocused)\n    {\n        // ActiveID gets high priority\n        // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)\n        if (owner_id != 0 && g.ActiveId == owner_id)\n            return 300;\n\n        // Score based on distance to focused window (lower is better)\n        // Assuming both windows are submitting a routing request,\n        // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)\n        // - When Window/ChildB is focused -> Window scores 4,        Window/ChildB scores 3 (best)\n        // Assuming only WindowA is submitting a routing request,\n        // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.\n        // This essentially follow the window->ParentWindowForFocusRoute chain.\n        if (focus_scope_id == 0)\n            return 0;\n        for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++)\n            if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id)\n            {\n                if (flags & ImGuiInputFlags_RouteOverActive) // && g.ActiveId != 0 && g.ActiveId != owner_id)\n                    return 599 - index_in_focus_path;\n                else\n                    return 199 - index_in_focus_path;\n            }\n        return 0;\n    }\n    else if (flags & ImGuiInputFlags_RouteActive)\n    {\n        if (owner_id != 0 && g.ActiveId == owner_id)\n            return 300;\n        return 0;\n    }\n    else if (flags & ImGuiInputFlags_RouteGlobal)\n    {\n        if (flags & ImGuiInputFlags_RouteOverActive)\n            return 400;\n        if (owner_id != 0 && g.ActiveId == owner_id)\n            return 300;\n        if (flags & ImGuiInputFlags_RouteOverFocused)\n            return 200;\n        return 1;\n    }\n    IM_ASSERT(0);\n    return 0;\n}\n\n// - We need this to filter some Shortcut() routes when an item e.g. an InputText() is active\n//   e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be.\n// - This is also used by UpdateInputEvents() to avoid trickling in the most common case of e.g. pressing ImGuiKey_G also emitting a G character.\nstatic bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord)\n{\n    // Mimic 'ignore_char_inputs' logic in InputText()\n    ImGuiContext& g = *GImGui;\n\n    // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out.\n    ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);\n    const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl));\n    if (ignore_char_inputs)\n        return false;\n\n    // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered.\n    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);\n    if (key == ImGuiKey_None)\n        return false;\n    return g.KeysMayBeCharInput.TestBit(key);\n}\n\n// Request a desired route for an input chord (key + mods).\n// Return true if the route is available this frame.\n// - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.\n//   (Conceptually this does a \"Submit for next frame\" + \"Test for current frame\".\n//   As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)\nbool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)\n        flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()\n    else\n        IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used\n    IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner);\n    if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteUnlessBgFocused))\n        IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal);\n    if (flags & ImGuiInputFlags_RouteOverActive)\n        IM_ASSERT(flags & (ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteFocused));\n\n    // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.\n    key_chord = FixupKeyChord(key_chord);\n\n    // [DEBUG] Debug break requested by user\n    if (g.DebugBreakInShortcutRouting == key_chord)\n        IM_DEBUG_BREAK();\n\n    if (flags & ImGuiInputFlags_RouteUnlessBgFocused)\n        if (g.NavWindow == NULL)\n            return false;\n\n    // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this?\n    if (flags & ImGuiInputFlags_RouteAlways)\n    {\n        IMGUI_DEBUG_LOG_INPUTROUTING(\"SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\\n\", GetKeyChordName(key_chord), flags, owner_id);\n        return true;\n    }\n\n    // Specific culling when there's an active item.\n    if (g.ActiveId != 0 && g.ActiveId != owner_id)\n    {\n        if (flags & ImGuiInputFlags_RouteActive)\n            return false;\n\n        // Cull shortcuts with no modifiers when it could generate a character.\n        // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active.\n        // but  Shortcut(Ctrl+G) should generally trigger when InputText() is active.\n        // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active.\n        // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined)\n        if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord))\n        {\n            IMGUI_DEBUG_LOG_INPUTROUTING(\"SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\\n\", GetKeyChordName(key_chord), flags, owner_id);\n            return false;\n        }\n\n        // ActiveIdUsingAllKeyboardKeys trumps all for ActiveId\n        if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys)\n        {\n            ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);\n            if (key == ImGuiKey_None)\n                key = ConvertSingleModFlagToKey((ImGuiKey)(key_chord & ImGuiMod_Mask_));\n            if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)\n                return false;\n        }\n    }\n\n    // Where do we evaluate route for?\n    ImGuiID focus_scope_id = g.CurrentFocusScopeId;\n    if (flags & ImGuiInputFlags_RouteFromRootWindow)\n        focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin()\n\n    const int score = CalcRoutingScore(focus_scope_id, owner_id, flags);\n    IMGUI_DEBUG_LOG_INPUTROUTING(\"SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\\n\", GetKeyChordName(key_chord), flags, owner_id, score);\n    if (score == 0)\n        return false;\n\n    // Submit routing for NEXT frame (assuming score is sufficient)\n    // FIXME: Could expose a way to use a \"serve last\" policy for same score resolution (using >= instead of >).\n    ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);\n    //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score >= routing_data->RoutingNextScore) : (score > routing_data->RoutingNextScore);\n    if (score > routing_data->RoutingNextScore)\n    {\n        routing_data->RoutingNext = owner_id;\n        routing_data->RoutingNextScore = (ImU16)score;\n    }\n\n    // Return routing state for CURRENT frame\n    if (routing_data->RoutingCurr == owner_id)\n        IMGUI_DEBUG_LOG_INPUTROUTING(\"--> granting current route\\n\");\n    return routing_data->RoutingCurr == owner_id;\n}\n\n// Currently unused by core (but used by tests)\n// Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.\nbool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)\n{\n    const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);\n    key_chord = FixupKeyChord(key_chord);\n    ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.\n    return routing_data->RoutingCurr == routing_id;\n}\n\n// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.\n// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)\nbool ImGui::IsKeyDown(ImGuiKey key)\n{\n    return IsKeyDown(key, ImGuiKeyOwner_Any);\n}\n\nbool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)\n{\n    const ImGuiKeyData* key_data = GetKeyData(key);\n    if (!key_data->Down)\n        return false;\n    if (!TestKeyOwner(key, owner_id))\n        return false;\n    return true;\n}\n\nbool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)\n{\n    return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);\n}\n\n// Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.\nbool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id)\n{\n    const ImGuiKeyData* key_data = GetKeyData(key);\n    if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)\n        return false;\n    const float t = key_data->DownDuration;\n    if (t < 0.0f)\n        return false;\n    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!\n    if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat\n        flags |= ImGuiInputFlags_Repeat;\n\n    bool pressed = (t == 0.0f);\n    if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0)\n    {\n        float repeat_delay, repeat_rate;\n        GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);\n        pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;\n        if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_))\n        {\n            // Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value.\n            // Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code.\n            ImGuiContext& g = *GImGui;\n            double key_pressed_time = g.Time - t + 0.00001f;\n            if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time))\n                pressed = false;\n            if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time))\n                pressed = false;\n            if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time))\n                pressed = false;\n        }\n    }\n    if (!pressed)\n        return false;\n    if (!TestKeyOwner(key, owner_id))\n        return false;\n    return true;\n}\n\nbool ImGui::IsKeyReleased(ImGuiKey key)\n{\n    return IsKeyReleased(key, ImGuiKeyOwner_Any);\n}\n\nbool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id)\n{\n    const ImGuiKeyData* key_data = GetKeyData(key);\n    if (key_data->DownDurationPrev < 0.0f || key_data->Down)\n        return false;\n    if (!TestKeyOwner(key, owner_id))\n        return false;\n    return true;\n}\n\nbool ImGui::IsMouseDown(ImGuiMouseButton button)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array.\n}\n\nbool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array.\n}\n\nbool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)\n{\n    return IsMouseClicked(button, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);\n}\n\nbool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)\n        return false;\n    const float t = g.IO.MouseDownDuration[button];\n    if (t < 0.0f)\n        return false;\n    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here.\n\n    const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;\n    const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);\n    if (!pressed)\n        return false;\n\n    if (!TestKeyOwner(MouseButtonToKey(button), owner_id))\n        return false;\n\n    return true;\n}\n\nbool ImGui::IsMouseReleased(ImGuiMouseButton button)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any)\n}\n\nbool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)\n}\n\n// Use if you absolutely need to distinguish single-click from double-click by introducing a delay.\n// Generally use with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount == 1' test.\n// This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename.\nbool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    const float time_since_release = (float)(g.Time - g.IO.MouseReleasedTime[button]);\n    return !IsMouseDown(button) && (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay);\n}\n\nbool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any);\n}\n\nbool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id);\n}\n\nint ImGui::GetMouseClickedCount(ImGuiMouseButton button)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    return g.IO.MouseClickedCount[button];\n}\n\n// Test if mouse cursor is hovering given rectangle\n// NB- Rectangle is clipped by our current clip setting\n// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)\nbool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Clip\n    ImRect rect_clipped(r_min, r_max);\n    if (clip)\n        rect_clipped.ClipWith(g.CurrentWindow->ClipRect);\n\n    // Hit testing, expanded for touch input\n    if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding))\n        return false;\n    if (!g.MouseViewport->GetMainRect().Overlaps(rect_clipped))\n        return false;\n    return true;\n}\n\n// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.\n// [Internal] This doesn't test if the button is pressed\nbool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    if (lock_threshold < 0.0f)\n        lock_threshold = g.IO.MouseDragThreshold;\n    return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;\n}\n\nbool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    if (!g.IO.MouseDown[button])\n        return false;\n    return IsMouseDragPastThreshold(button, lock_threshold);\n}\n\nImVec2 ImGui::GetMousePos()\n{\n    ImGuiContext& g = *GImGui;\n    return g.IO.MousePos;\n}\n\n// This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.\n// It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend.\nvoid ImGui::TeleportMousePos(const ImVec2& pos)\n{\n    ImGuiContext& g = *GImGui;\n    g.IO.MousePos = g.IO.MousePosPrev = pos;\n    g.IO.MouseDelta = ImVec2(0.0f, 0.0f);\n    g.IO.WantSetMousePos = true;\n    //IMGUI_DEBUG_LOG_IO(\"TeleportMousePos: (%.1f,%.1f)\\n\", io.MousePos.x, io.MousePos.y);\n}\n\n// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!\nImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.BeginPopupStack.Size > 0)\n        return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;\n    return g.IO.MousePos;\n}\n\n// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.\nbool ImGui::IsMousePosValid(const ImVec2* mouse_pos)\n{\n    // The assert is only to silence a false-positive in XCode Static Analysis.\n    // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions).\n    IM_ASSERT(GImGui != NULL);\n    const float MOUSE_INVALID = -256000.0f;\n    ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;\n    return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;\n}\n\n// [WILL OBSOLETE] This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.\nbool ImGui::IsAnyMouseDown()\n{\n    ImGuiContext& g = *GImGui;\n    for (int n = 0; n < IM_COUNTOF(g.IO.MouseDown); n++)\n        if (g.IO.MouseDown[n])\n            return true;\n    return false;\n}\n\n// Return the delta from the initial clicking position while the mouse button is clicked or was just released.\n// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.\n// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.\nImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    if (lock_threshold < 0.0f)\n        lock_threshold = g.IO.MouseDragThreshold;\n    if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])\n        if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)\n            if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))\n                return g.IO.MousePos - g.IO.MouseClickedPos[button];\n    return ImVec2(0.0f, 0.0f);\n}\n\nvoid ImGui::ResetMouseDragDelta(ImGuiMouseButton button)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));\n    // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr\n    g.IO.MouseClickedPos[button] = g.IO.MousePos;\n}\n\n// Get desired mouse cursor shape.\n// Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(),\n// updated during the frame, and locked in EndFrame()/Render().\n// If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you\nImGuiMouseCursor ImGui::GetMouseCursor()\n{\n    ImGuiContext& g = *GImGui;\n    return g.MouseCursor;\n}\n\n// We intentionally accept values of ImGuiMouseCursor that are outside our bounds, in case users needs to hack-in a custom cursor value.\n// Custom cursors may be handled by custom backends. If you are using a standard backend and want to hack in a custom cursor, you may\n// handle it before the backend _NewFrame() call and temporarily set ImGuiConfigFlags_NoMouseCursorChange during the backend _NewFrame() call.\nvoid ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)\n{\n    ImGuiContext& g = *GImGui;\n    g.MouseCursor = cursor_type;\n}\n\nstatic void UpdateAliasKey(ImGuiKey key, bool v, float analog_value)\n{\n    IM_ASSERT(ImGui::IsAliasKey(key));\n    ImGuiKeyData* key_data = ImGui::GetKeyData(key);\n    key_data->Down = v;\n    key_data->AnalogValue = analog_value;\n}\n\n// [Internal] Do not use directly\nstatic ImGuiKeyChord GetMergedModsFromKeys()\n{\n    ImGuiKeyChord mods = 0;\n    if (ImGui::IsKeyDown(ImGuiMod_Ctrl))     { mods |= ImGuiMod_Ctrl; }\n    if (ImGui::IsKeyDown(ImGuiMod_Shift))    { mods |= ImGuiMod_Shift; }\n    if (ImGui::IsKeyDown(ImGuiMod_Alt))      { mods |= ImGuiMod_Alt; }\n    if (ImGui::IsKeyDown(ImGuiMod_Super))    { mods |= ImGuiMod_Super; }\n    return mods;\n}\n\nstatic void ImGui::UpdateKeyboardInputs()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)\n        io.ClearInputKeys();\n\n    // Update aliases\n    for (int n = 0; n < ImGuiMouseButton_COUNT; n++)\n        UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f);\n    UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH);\n    UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel);\n\n    // Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values.\n    // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) ->                                      -> (here) deriving io.KeyMods + io.KeyXXX from key array.\n    // - Legacy backends:      set io.KeyXXX bools               -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.\n    // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.\n    const ImGuiKeyChord prev_key_mods = io.KeyMods;\n    io.KeyMods = GetMergedModsFromKeys();\n    io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;\n    io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;\n    io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;\n    io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;\n    if (prev_key_mods != io.KeyMods)\n        g.LastKeyModsChangeTime = g.Time;\n    if (prev_key_mods != io.KeyMods && prev_key_mods == 0)\n        g.LastKeyModsChangeFromNoneTime = g.Time;\n\n    // Clear gamepad data if disabled\n    if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)\n        for (int key = ImGuiKey_Gamepad_BEGIN; key < ImGuiKey_Gamepad_END; key++)\n        {\n            io.KeysData[key - ImGuiKey_NamedKey_BEGIN].Down = false;\n            io.KeysData[key - ImGuiKey_NamedKey_BEGIN].AnalogValue = 0.0f;\n        }\n\n    // Update keys\n    for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)\n    {\n        ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];\n        key_data->DownDurationPrev = key_data->DownDuration;\n        key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;\n        if (key_data->DownDuration == 0.0f)\n        {\n            if (IsKeyboardKey((ImGuiKey)key))\n                g.LastKeyboardKeyPressTime = g.Time;\n            else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper)\n                g.LastKeyboardKeyPressTime = g.Time;\n        }\n    }\n\n    // Update keys/input owner (named keys only): one entry per key\n    for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))\n    {\n        ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];\n        ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];\n        owner_data->OwnerCurr = owner_data->OwnerNext;\n        if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp.\n            owner_data->OwnerNext = ImGuiKeyOwner_NoOwner;\n        owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down;  // Clear LockUntilRelease when key is not Down anymore\n    }\n\n    // Update key routing (for e.g. shortcuts)\n    UpdateKeyRoutingTable(&g.KeysRoutingTable);\n}\n\nstatic void ImGui::UpdateMouseInputs()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    // Mouse Wheel swapping flag\n    // As a standard behavior holding Shift while using Vertical Mouse Wheel triggers Horizontal scroll instead\n    // - We avoid doing it on OSX as it the OS input layer handles this already.\n    // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature.\n    // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source.\n    io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors;\n\n    // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)\n    if (IsMousePosValid(&io.MousePos))\n        io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos);\n\n    // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta\n    if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev))\n        io.MouseDelta = io.MousePos - io.MousePosPrev;\n    else\n        io.MouseDelta = ImVec2(0.0f, 0.0f);\n\n    // Update stationary timer.\n    // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.\n    const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.\n    const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold);\n    g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;\n    //IMGUI_DEBUG_LOG(\"%.4f\\n\", g.MouseStationaryTimer);\n\n    // If mouse moved we re-enable mouse hovering in case it was disabled by keyboard/gamepad. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.\n    if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)\n        g.NavHighlightItemUnderNav = false;\n\n    for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++)\n    {\n        io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f;\n        io.MouseClickedCount[i] = 0; // Will be filled below\n        io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f;\n        if (io.MouseReleased[i])\n            io.MouseReleasedTime[i] = g.Time;\n        io.MouseDownDurationPrev[i] = io.MouseDownDuration[i];\n        io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f;\n        if (io.MouseClicked[i])\n        {\n            bool is_repeated_click = false;\n            if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime)\n            {\n                ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);\n                if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist)\n                    is_repeated_click = true;\n            }\n            if (is_repeated_click)\n                io.MouseClickedLastCount[i]++;\n            else\n                io.MouseClickedLastCount[i] = 1;\n            io.MouseClickedTime[i] = g.Time;\n            io.MouseClickedPos[i] = io.MousePos;\n            io.MouseClickedCount[i] = io.MouseClickedLastCount[i];\n            io.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);\n            io.MouseDragMaxDistanceSqr[i] = 0.0f;\n        }\n        else if (io.MouseDown[i])\n        {\n            // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold\n            ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);\n            io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));\n            io.MouseDragMaxDistanceAbs[i].x = ImMax(io.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x);\n            io.MouseDragMaxDistanceAbs[i].y = ImMax(io.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y);\n        }\n\n        // We provide io.MouseDoubleClicked[] as a legacy service\n        io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2);\n\n        // Clicking any mouse button reactivate mouse hovering which may have been deactivated by keyboard/gamepad navigation\n        if (io.MouseClicked[i])\n            g.NavHighlightItemUnderNav = false;\n    }\n}\n\nstatic void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)\n{\n    ImGuiContext& g = *GImGui;\n    if (window)\n        g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);\n    else\n        g.WheelingWindowReleaseTimer = 0.0f;\n    if (g.WheelingWindow == window)\n        return;\n    IMGUI_DEBUG_LOG_IO(\"[io] LockWheelingWindow() \\\"%s\\\"\\n\", window ? window->Name : \"NULL\");\n    g.WheelingWindow = window;\n    g.WheelingWindowRefMousePos = g.IO.MousePos;\n    if (window == NULL)\n    {\n        g.WheelingWindowStartFrame = -1;\n        g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);\n    }\n}\n\nstatic ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)\n{\n    // For each axis, find window in the hierarchy that may want to use scrolling\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* windows[2] = { NULL, NULL };\n    for (int axis = 0; axis < 2; axis++)\n        if (wheel[axis] != 0.0f)\n            for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)\n            {\n                // Bubble up into parent window if:\n                // - a child window doesn't allow any scrolling.\n                // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.\n                //// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)\n                const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);\n                const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);\n                //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);\n                if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)\n                    break; // select this window\n            }\n    if (windows[0] == NULL && windows[1] == NULL)\n        return NULL;\n\n    // If there's only one window or only one axis then there's no ambiguity\n    if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)\n        return windows[1] ? windows[1] : windows[0];\n\n    // If candidate are different windows we need to decide which one to prioritize\n    // - First frame: only find a winner if one axis is zero.\n    // - Subsequent frames: only find a winner when one is more than the other.\n    if (g.WheelingWindowStartFrame == -1)\n        g.WheelingWindowStartFrame = g.FrameCount;\n    if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))\n    {\n        g.WheelingWindowWheelRemainder = wheel;\n        return NULL;\n    }\n    return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];\n}\n\n// Called by NewFrame()\nvoid ImGui::UpdateMouseWheel()\n{\n    // Reset the locked window if we move the mouse or after the timer elapses.\n    // FIXME: Ideally we could refactor to have one timer for \"changing window w/ same axis\" and a shorter timer for \"changing window or axis w/ other axis\" (#3795)\n    ImGuiContext& g = *GImGui;\n    if (g.WheelingWindow != NULL)\n    {\n        g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;\n        if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)\n            g.WheelingWindowReleaseTimer = 0.0f;\n        if (g.WheelingWindowReleaseTimer <= 0.0f)\n            LockWheelingWindow(NULL, 0.0f);\n    }\n\n    ImVec2 wheel;\n    wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f;\n    wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f;\n\n    //IMGUI_DEBUG_LOG(\"MouseWheel X:%.3f Y:%.3f\\n\", wheel_x, wheel_y);\n    ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;\n    if (!mouse_window || mouse_window->Collapsed)\n        return;\n\n    // Zoom / Scale window\n    // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.\n    if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)\n    {\n        LockWheelingWindow(mouse_window, wheel.y);\n        ImGuiWindow* window = mouse_window;\n        const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);\n        const float scale = new_font_scale / window->FontWindowScale;\n        window->FontWindowScale = new_font_scale;\n        if (window == window->RootWindow)\n        {\n            const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;\n            SetWindowPos(window, window->Pos + offset, 0);\n            window->Size = ImTrunc(window->Size * scale); // FIXME: Legacy-ish code, call SetWindowSize()?\n            window->SizeFull = ImTrunc(window->SizeFull * scale);\n            MarkIniSettingsDirty(window);\n        }\n        return;\n    }\n    if (g.IO.KeyCtrl)\n        return;\n\n    // Mouse wheel scrolling\n    // Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs()\n    if (g.IO.MouseWheelRequestAxisSwap)\n        wheel = ImVec2(wheel.y, 0.0f);\n\n    // Maintain a rough average of moving magnitude on both axes\n    // FIXME: should by based on wall clock time rather than frame-counter\n    g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);\n    g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);\n\n    // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.\n    wheel += g.WheelingWindowWheelRemainder;\n    g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);\n    if (wheel.x == 0.0f && wheel.y == 0.0f)\n        return;\n\n    // Mouse wheel scrolling: find target and apply\n    // - don't renew lock if axis doesn't apply on the window.\n    // - select a main axis when both axes are being moved.\n    if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))\n        if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))\n        {\n            bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };\n            if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])\n                do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;\n            if (do_scroll[ImGuiAxis_X])\n            {\n                LockWheelingWindow(window, wheel.x);\n                float max_step = window->InnerRect.GetWidth() * 0.67f;\n                float scroll_step = ImTrunc(ImMin(2 * window->FontRefSize, max_step));\n                SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);\n                g.WheelingWindowScrolledFrame = g.FrameCount;\n            }\n            if (do_scroll[ImGuiAxis_Y])\n            {\n                LockWheelingWindow(window, wheel.y);\n                float max_step = window->InnerRect.GetHeight() * 0.67f;\n                float scroll_step = ImTrunc(ImMin(5 * window->FontRefSize, max_step));\n                SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);\n                g.WheelingWindowScrolledFrame = g.FrameCount;\n            }\n        }\n}\n\nvoid ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)\n{\n    ImGuiContext& g = *GImGui;\n    g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0;\n}\n\nvoid ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse)\n{\n    ImGuiContext& g = *GImGui;\n    g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0;\n}\n\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\nstatic const char* GetInputSourceName(ImGuiInputSource source)\n{\n    const char* input_source_names[] = { \"None\", \"Mouse\", \"Keyboard\", \"Gamepad\" };\n    IM_ASSERT(IM_COUNTOF(input_source_names) == ImGuiInputSource_COUNT);\n    if (source < 0 || source >= ImGuiInputSource_COUNT)\n        return \"Unknown\";\n    return input_source_names[source];\n}\nstatic const char* GetMouseSourceName(ImGuiMouseSource source)\n{\n    const char* mouse_source_names[] = { \"Mouse\", \"TouchScreen\", \"Pen\" };\n    IM_ASSERT(IM_COUNTOF(mouse_source_names) == ImGuiMouseSource_COUNT);\n    if (source < 0 || source >= ImGuiMouseSource_COUNT)\n        return \"Unknown\";\n    return mouse_source_names[source];\n}\nstatic void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)\n{\n    ImGuiContext& g = *GImGui;\n    char buf[5];\n    if (e->Type == ImGuiInputEventType_MousePos)    { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO(\"[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\\n\", prefix); else IMGUI_DEBUG_LOG_IO(\"[io] %s: MousePos (%.1f, %.1f) (%s)\\n\", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; }\n    if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO(\"[io] %s: MouseButton %d %s (%s)\\n\", prefix, e->MouseButton.Button, e->MouseButton.Down ? \"Down\" : \"Up\", GetMouseSourceName(e->MouseButton.MouseSource)); return; }\n    if (e->Type == ImGuiInputEventType_MouseWheel)  { IMGUI_DEBUG_LOG_IO(\"[io] %s: MouseWheel (%.3f, %.3f) (%s)\\n\", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; }\n    if (e->Type == ImGuiInputEventType_MouseViewport){IMGUI_DEBUG_LOG_IO(\"[io] %s: MouseViewport (0x%08X)\\n\", prefix, e->MouseViewport.HoveredViewportID); return; }\n    if (e->Type == ImGuiInputEventType_Key)         { IMGUI_DEBUG_LOG_IO(\"[io] %s: Key \\\"%s\\\" %s\\n\", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? \"Down\" : \"Up\"); return; }\n    if (e->Type == ImGuiInputEventType_Text)        { ImTextCharToUtf8(buf, e->Text.Char); IMGUI_DEBUG_LOG_IO(\"[io] %s: Text: '%s' (U+%08X)\\n\", prefix, buf, e->Text.Char); return; }\n    if (e->Type == ImGuiInputEventType_Focus)       { IMGUI_DEBUG_LOG_IO(\"[io] %s: AppFocused %d\\n\", prefix, e->AppFocused.Focused); return; }\n}\n#endif\n\n// Process input queue\n// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.\n// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)\n// - trickle_fast_inputs = true  : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87)\nvoid ImGui::UpdateInputEvents(bool trickle_fast_inputs)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    // Only trickle chars<>key when working with InputText()\n    // FIXME: InputText() could parse event trail?\n    // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)\n    const bool trickle_interleaved_nonchar_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1);\n\n    bool mouse_moved = false, mouse_wheeled = false, key_changed = false, key_changed_nonchar = false, text_inputted = false;\n    int  mouse_button_changed = 0x00;\n    ImBitArray<ImGuiKey_NamedKey_COUNT> key_changed_mask;\n\n    int event_n = 0;\n    for (; event_n < g.InputEventsQueue.Size; event_n++)\n    {\n        ImGuiInputEvent* e = &g.InputEventsQueue[event_n];\n        if (e->Type == ImGuiInputEventType_MousePos)\n        {\n            if (g.IO.WantSetMousePos)\n                continue;\n            // Trickling Rule: Stop processing queued events if we already handled a mouse button change\n            ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);\n            if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))\n                break;\n            io.MousePos = event_pos;\n            io.MouseSource = e->MousePos.MouseSource;\n            mouse_moved = true;\n        }\n        else if (e->Type == ImGuiInputEventType_MouseButton)\n        {\n            // Trickling Rule: Stop processing queued events if we got multiple action on the same button\n            const ImGuiMouseButton button = e->MouseButton.Button;\n            IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT);\n            if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled))\n                break;\n            if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover.\n                break;\n            io.MouseDown[button] = e->MouseButton.Down;\n            io.MouseSource = e->MouseButton.MouseSource;\n            mouse_button_changed |= (1 << button);\n        }\n        else if (e->Type == ImGuiInputEventType_MouseWheel)\n        {\n            // Trickling Rule: Stop processing queued events if we got multiple action on the event\n            if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0))\n                break;\n            io.MouseWheelH += e->MouseWheel.WheelX;\n            io.MouseWheel += e->MouseWheel.WheelY;\n            io.MouseSource = e->MouseWheel.MouseSource;\n            mouse_wheeled = true;\n        }\n        else if (e->Type == ImGuiInputEventType_MouseViewport)\n        {\n            io.MouseHoveredViewport = e->MouseViewport.HoveredViewportID;\n        }\n        else if (e->Type == ImGuiInputEventType_Key)\n        {\n            // Trickling Rule: Stop processing queued events if we got multiple action on the same button\n            if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)\n                continue;\n            ImGuiKey key = e->Key.Key;\n            IM_ASSERT(key != ImGuiKey_None);\n            ImGuiKeyData* key_data = GetKeyData(key);\n            const int key_data_index = (int)(key_data - g.IO.KeysData);\n            if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || mouse_button_changed != 0))\n                break;\n\n            const bool key_is_potentially_for_char_input = IsKeyChordPotentiallyCharInput(GetMergedModsFromKeys() | key);\n            if (trickle_interleaved_nonchar_keys_and_text && (text_inputted && !key_is_potentially_for_char_input))\n                break;\n\n            if (key_data->Down != e->Key.Down) // Analog change only do not trigger this, so it won't block e.g. further mouse pos events testing key_changed.\n            {\n                key_changed = true;\n                key_changed_mask.SetBit(key_data_index);\n                if (trickle_interleaved_nonchar_keys_and_text && !key_is_potentially_for_char_input)\n                    key_changed_nonchar = true;\n            }\n\n            key_data->Down = e->Key.Down;\n            key_data->AnalogValue = e->Key.AnalogValue;\n        }\n        else if (e->Type == ImGuiInputEventType_Text)\n        {\n            if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)\n                continue;\n            // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with\n            if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_moved || mouse_wheeled))\n                break;\n            if (trickle_interleaved_nonchar_keys_and_text && key_changed_nonchar)\n                break;\n            unsigned int c = e->Text.Char;\n            io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);\n            if (trickle_interleaved_nonchar_keys_and_text)\n                text_inputted = true;\n        }\n        else if (e->Type == ImGuiInputEventType_Focus)\n        {\n            // We intentionally overwrite this and process in NewFrame(), in order to give a chance\n            // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.\n            const bool focus_lost = !e->AppFocused.Focused;\n            io.AppFocusLost = focus_lost;\n        }\n        else\n        {\n            IM_ASSERT(0 && \"Unknown event!\");\n        }\n    }\n\n    // Record trail (for domain-specific applications wanting to access a precise trail)\n    //if (event_n != 0) IMGUI_DEBUG_LOG_IO(\"Processed: %d / Remaining: %d\\n\", event_n, g.InputEventsQueue.Size - event_n);\n    for (int n = 0; n < event_n; n++)\n        g.InputEventsTrail.push_back(g.InputEventsQueue[n]);\n\n    // [DEBUG]\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO))\n        for (int n = 0; n < g.InputEventsQueue.Size; n++)\n            DebugPrintInputEvent(n < event_n ? \"Processed\" : \"Remaining\", &g.InputEventsQueue[n]);\n#endif\n\n    // Remaining events will be processed on the next frame\n    // FIXME-MULTITHREADING: io.AddKeyEvent() etc. calls are mostly thread-safe apart from the fact they push to this\n    // queue which may be resized here. Could potentially rework this to narrow down the section needing a mutex? (#5772)\n    if (event_n == g.InputEventsQueue.Size)\n        g.InputEventsQueue.resize(0);\n    else\n        g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n);\n\n    // Clear buttons state when focus is lost\n    // - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle.\n    // - we clear in EndFrame() and not now in order allow application/user code polling this flag\n    //   (e.g. custom backend may want to clear additional data, custom widgets may want to react with a \"canceling\" event).\n    if (g.IO.AppFocusLost)\n    {\n        g.IO.ClearInputKeys();\n        g.IO.ClearInputMouse();\n    }\n}\n\nImGuiID ImGui::GetKeyOwner(ImGuiKey key)\n{\n    if (!IsNamedKeyOrMod(key))\n        return ImGuiKeyOwner_NoOwner;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);\n    ImGuiID owner_id = owner_data->OwnerCurr;\n\n    if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)\n        if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)\n            return ImGuiKeyOwner_NoOwner;\n\n    return owner_id;\n}\n\n// TestKeyOwner(..., ID)   : (owner == None || owner == ID)\n// TestKeyOwner(..., None) : (owner == None)\n// TestKeyOwner(..., Any)  : no owner test\n// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.\nbool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)\n{\n    if (!IsNamedKeyOrMod(key))\n        return true;\n\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)\n        if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)\n            return false;\n\n    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);\n    if (owner_id == ImGuiKeyOwner_Any)\n        return owner_data->LockThisFrame == false;\n\n    // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId\n    // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things.\n    // Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions.\n    if (owner_data->OwnerCurr != owner_id)\n    {\n        if (owner_data->LockThisFrame)\n            return false;\n        if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner)\n            return false;\n    }\n\n    return true;\n}\n\n// _LockXXX flags are useful to lock keys away from code which is not input-owner aware.\n// When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.\n// - SetKeyOwner(..., None)              : clears owner\n// - SetKeyOwner(..., Any, !Lock)        : illegal (assert)\n// - SetKeyOwner(..., Any or None, Lock) : set lock\nvoid ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(IsNamedKeyOrMod(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it)\n    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!\n    //IMGUI_DEBUG_LOG(\"SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\\n\", GetKeyName(key), owner_id, flags);\n\n    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);\n    owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;\n\n    // We cannot lock by default as it would likely break lots of legacy code.\n    // In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test)\n    owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0;\n    owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease);\n}\n\n// Rarely used helper\nvoid ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)\n{\n    if (key_chord & ImGuiMod_Ctrl)      { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); }\n    if (key_chord & ImGuiMod_Shift)     { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); }\n    if (key_chord & ImGuiMod_Alt)       { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); }\n    if (key_chord & ImGuiMod_Super)     { SetKeyOwner(ImGuiMod_Super, owner_id, flags); }\n    if (key_chord & ~ImGuiMod_Mask_)    { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }\n}\n\n// This is more or less equivalent to:\n//   if (IsItemHovered() || IsItemActive())\n//       SetKeyOwner(key, GetItemID());\n// Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times.\n// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.\n// Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority.\nvoid ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiID id = g.LastItemData.ID;\n    if (id == 0 || (g.HoveredId != id && g.ActiveId != id))\n        return;\n    if ((flags & ImGuiInputFlags_CondMask_) == 0)\n        flags |= ImGuiInputFlags_CondDefault_;\n    if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))\n    {\n        IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!\n        SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_);\n    }\n}\n\nvoid ImGui::SetItemKeyOwner(ImGuiKey key)\n{\n    SetItemKeyOwner(key, ImGuiInputFlags_None);\n}\n\n// This is the only public API until we expose owner_id versions of the API as replacements.\nbool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord)\n{\n    return IsKeyChordPressed(key_chord, ImGuiInputFlags_None, ImGuiKeyOwner_Any);\n}\n\n// This is equivalent to comparing KeyMods + doing a IsKeyPressed()\nbool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    key_chord = FixupKeyChord(key_chord);\n    ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);\n    if (g.IO.KeyMods != mods)\n        return false;\n\n    // Special storage location for mods\n    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);\n    if (key == ImGuiKey_None)\n        key = ConvertSingleModFlagToKey(mods);\n    if (!IsKeyPressed(key, (flags & ImGuiInputFlags_RepeatMask_), owner_id))\n        return false;\n    return true;\n}\n\nvoid ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasShortcut;\n    g.NextItemData.Shortcut = key_chord;\n    g.NextItemData.ShortcutFlags = flags;\n}\n\n// Called from within ItemAdd: at this point we can read from NextItemData and write to LastItemData\nvoid ImGui::ItemHandleShortcut(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiInputFlags flags = g.NextItemData.ShortcutFlags;\n    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()!\n\n    if (g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled)\n        return;\n    if (flags & ImGuiInputFlags_Tooltip)\n    {\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut;\n        g.LastItemData.Shortcut = g.NextItemData.Shortcut;\n    }\n    if (!Shortcut(g.NextItemData.Shortcut, flags & ImGuiInputFlags_SupportedByShortcut, id) || g.NavActivateId != 0)\n        return;\n\n    // FIXME: Generalize Activation queue?\n    g.NavActivateId = id; // Will effectively disable clipping.\n    g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut;\n    //if (g.ActiveId == 0 || g.ActiveId == id)\n    g.NavActivateDownId = g.NavActivatePressedId = id;\n    NavHighlightActivated(id);\n}\n\nbool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)\n{\n    return Shortcut(key_chord, flags, ImGuiKeyOwner_Any);\n}\n\nbool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    //IMGUI_DEBUG_LOG(\"Shortcut(%s, flags=%X, owner_id=0x%08X)\\n\", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id);\n\n    // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.\n    if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)\n        flags |= ImGuiInputFlags_RouteFocused;\n\n    // Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)\n    // Effectively makes Shortcut() always input-owner aware.\n    if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner)\n        owner_id = GetRoutingIdFromOwnerId(owner_id);\n\n    if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)\n        return false;\n\n    // Submit route\n    if (!SetShortcutRouting(key_chord, flags, owner_id))\n        return false;\n\n    // Default repeat behavior for Shortcut()\n    // So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut.\n    if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0)\n        flags |= ImGuiInputFlags_RepeatUntilKeyModsChange;\n\n    if (!IsKeyChordPressed(key_chord, flags, owner_id))\n        return false;\n\n    // Claim mods during the press\n    SetKeyOwnersForKeyChord(key_chord & ImGuiMod_Mask_, owner_id);\n\n    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ERROR CHECKING, STATE RECOVERY\n//-----------------------------------------------------------------------------\n// - DebugCheckVersionAndDataLayout() (called via IMGUI_CHECKVERSION() macros)\n// - ErrorCheckUsingSetCursorPosToExtendParentBoundaries()\n// - ErrorCheckNewFrameSanityChecks()\n// - ErrorCheckEndFrameSanityChecks()\n// - ErrorRecoveryStoreState()\n// - ErrorRecoveryTryToRecoverState()\n// - ErrorRecoveryTryToRecoverWindowState()\n// - ErrorLog()\n//-----------------------------------------------------------------------------\n\n// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.\n// Called by IMGUI_CHECKVERSION().\n// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit\n// If this triggers you have mismatched headers and compiled code versions.\n// - It could be because of a build issue (using new headers with old compiled code)\n// - It could be because of mismatched configuration #define, compilation settings, packing pragma etc.\n//   THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI.\n//   Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h).\n//   Otherwise it is possible that different compilation units would see different structure layout.\n//   If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename.\nbool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)\n{\n    bool error = false;\n    if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && \"Mismatched version string!\"); }\n    if (sz_io    != sizeof(ImGuiIO))    { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && \"Mismatched struct layout!\"); }\n    if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && \"Mismatched struct layout!\"); }\n    if (sz_vec2  != sizeof(ImVec2))     { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && \"Mismatched struct layout!\"); }\n    if (sz_vec4  != sizeof(ImVec4))     { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && \"Mismatched struct layout!\"); }\n    if (sz_vert  != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && \"Mismatched struct layout!\"); }\n    if (sz_idx   != sizeof(ImDrawIdx))  { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && \"Mismatched struct layout!\"); }\n    return !error;\n}\n\n// Until 1.89 (August 2022, IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos()/SetCursorScreenPos()\n// to extend contents size of our parent container (e.g. window contents size, which is used for auto-resizing\n// windows, table column contents size used for auto-resizing columns, group size).\n// This was causing issues and ambiguities and we needed to retire that.\n// From 1.89, extending contents size boundaries REQUIRES AN ITEM TO BE SUBMITTED.\n//\n//  Previously this would make the window content size ~200x200:\n//    Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();                      // NOT OK ANYMORE\n//  Instead, please submit an item:\n//    Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK\n//  Alternative:\n//    Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK\n//\n// The assert below detects when the _last_ call in a window was a SetCursorPos() not followed by an Item,\n// and with a position that would grow the parent contents size.\n//\n// Advanced:\n// - For reference, old logic was causing issues because it meant that SetCursorScreenPos(GetCursorScreenPos())\n//   had a side-effect on layout! In particular this caused problem to compute group boundaries.\n//   e.g. BeginGroup() + SomeItem() + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup() would cause the\n//   group to be taller because auto-sizing generally adds padding on bottom and right side.\n// - While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect.\n//   Using vertical alignment patterns would frequently trigger this sorts of issue.\n// - See https://github.com/ocornut/imgui/issues/5548 for more details.\nvoid ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(window->DC.IsSetPos);\n    window->DC.IsSetPos = false;\n    if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y)\n        return;\n    if (window->SkipItems)\n        return;\n    IM_ASSERT_USER_ERROR(0, \"Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries.\\nPlease submit an item e.g. Dummy() afterwards in order to grow window/parent boundaries.\");\n\n    // For reference, the old behavior was essentially:\n    //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);\n}\n\nstatic void ImGui::ErrorCheckNewFrameSanityChecks()\n{\n    ImGuiContext& g = *GImGui;\n\n    // Check user IM_ASSERT macro\n    // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!\n    //  If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.\n    //  This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)\n    // #define IM_ASSERT(EXPR)   if (SomeCode(EXPR)) SomeMoreCode();                    // Wrong!\n    // #define IM_ASSERT(EXPR)   do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0)   // Correct!\n    if (true) IM_ASSERT(1); else IM_ASSERT(0);\n\n    // Emscripten backends are often imprecise in their submission of DeltaTime. (#6114, #3644)\n    // Ideally the Emscripten app/backend should aim to fix or smooth this value and avoid feeding zero, but we tolerate it.\n#ifdef __EMSCRIPTEN__\n    if (g.IO.DeltaTime <= 0.0f && g.FrameCount > 0)\n        g.IO.DeltaTime = 0.00001f;\n#endif\n\n    // Check user data\n    // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument)\n    IM_ASSERT(g.Initialized);\n    IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && \"Need a positive DeltaTime!\");\n    IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && \"Forgot to call Render() or EndFrame() at the end of the previous frame?\");\n    IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && \"Invalid DisplaySize value!\");\n    IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && \"Invalid style setting!\");\n    IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f                 && \"Invalid style setting!\");\n    IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && \"Invalid style setting!\"); // Allows us to avoid a few clamps in color computations\n    IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && \"Invalid style setting!\");\n    IM_ASSERT(g.Style.WindowBorderHoverPadding > 0.0f                   && \"Invalid style setting!\"); // Required otherwise cannot resize from borders.\n    IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);\n    IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);\n    IM_ASSERT(g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesNone || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesFull || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesToNodes);\n\n    // Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.\n    if (g.IO.ConfigErrorRecovery)\n        IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL);\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    if (g.IO.FontGlobalScale > 1.0f)\n        IM_ASSERT(g.Style.FontScaleMain == 1.0f && \"Since 1.92: use style.FontScaleMain instead of g.IO.FontGlobalScale!\");\n\n    // Remap legacy names\n    if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)\n    {\n        g.IO.ConfigNavMoveSetMousePos = true;\n        g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavEnableSetMousePos;\n    }\n    if (g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)\n    {\n        g.IO.ConfigNavCaptureKeyboard = false;\n        g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavNoCaptureKeyboard;\n    }\n    if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts)\n    {\n        g.IO.ConfigDpiScaleFonts = true;\n        g.IO.ConfigFlags &= ~ImGuiConfigFlags_DpiEnableScaleFonts;\n    }\n    if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports)\n    {\n        g.IO.ConfigDpiScaleViewports = true;\n        g.IO.ConfigFlags &= ~ImGuiConfigFlags_DpiEnableScaleViewports;\n    }\n\n    // Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024)\n    if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl))\n        g.PlatformIO.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) { return ctx->IO.GetClipboardTextFn(ctx->IO.ClipboardUserData); };\n    if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl))\n        g.PlatformIO.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) { return ctx->IO.SetClipboardTextFn(ctx->IO.ClipboardUserData, text); };\n#endif\n\n    // Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data.\n    if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0)\n        IM_ASSERT(0 && \"Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!\");\n    if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0)\n        IM_ASSERT(0 && \"Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!\");\n\n    // Perform simple checks: multi-viewport and platform windows support\n    if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)\n    {\n        if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports))\n        {\n            IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && \"Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference.\");\n            IM_ASSERT(g.PlatformIO.Platform_CreateWindow  != NULL && \"Platform init didn't install handlers?\");\n            IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && \"Platform init didn't install handlers?\");\n            IM_ASSERT(g.PlatformIO.Platform_GetWindowPos  != NULL && \"Platform init didn't install handlers?\");\n            IM_ASSERT(g.PlatformIO.Platform_SetWindowPos  != NULL && \"Platform init didn't install handlers?\");\n            IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && \"Platform init didn't install handlers?\");\n            IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && \"Platform init didn't install handlers?\");\n            IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && \"Platform init didn't setup Monitors list?\");\n            IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && \"Platform init didn't setup main viewport.\");\n            if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))\n                IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && \"Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!\");\n        }\n        else\n        {\n            // Disable feature, our backends do not support it\n            g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;\n        }\n\n        // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs\n        for (ImGuiPlatformMonitor& mon : g.PlatformIO.Monitors)\n        {\n            IM_UNUSED(mon);\n            IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && \"Monitor main bounds not setup properly.\");\n            IM_ASSERT(ImRect(mon.MainPos, mon.MainPos + mon.MainSize).Contains(ImRect(mon.WorkPos, mon.WorkPos + mon.WorkSize)) && \"Monitor work bounds not setup properly. If you don't have work area information, just copy MainPos/MainSize into them.\");\n            IM_ASSERT(mon.DpiScale > 0.0f && mon.DpiScale < 99.0f && \"Monitor DpiScale is invalid.\"); // Typical correct values would be between 1.0f and 4.0f\n        }\n    }\n}\n\nstatic void ImGui::ErrorCheckEndFrameSanityChecks()\n{\n    // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()\n    // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().\n    // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will\n    // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.\n    // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),\n    // while still correctly asserting on mid-frame key press events.\n    ImGuiContext& g = *GImGui;\n    const ImGuiKeyChord key_mods = GetMergedModsFromKeys();\n    IM_UNUSED(g);\n    IM_UNUSED(key_mods);\n    IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && \"Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods\");\n    IM_UNUSED(key_mods);\n\n    IM_ASSERT(g.CurrentWindowStack.Size == 1);\n    IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);\n}\n\n// Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery.\nvoid ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out)\n{\n    ImGuiContext& g = *GImGui;\n    state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size;\n    state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size;\n    state_out->SizeOfTreeStack = (short)g.CurrentWindow->DC.TreeDepth; // NOT g.TreeNodeStack.Size which is a partial stack!\n    state_out->SizeOfColorStack = (short)g.ColorStack.Size;\n    state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size;\n    state_out->SizeOfFontStack = (short)g.FontStack.Size;\n    state_out->SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;\n    state_out->SizeOfGroupStack = (short)g.GroupStack.Size;\n    state_out->SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;\n    state_out->SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;\n    state_out->SizeOfDisabledStack = (short)g.DisabledStackSize;\n}\n\n// Chosen name \"Try to recover\" over e.g. \"Restore\" to suggest this is not a 100% guaranteed recovery.\n// Called by e.g. EndFrame() but may be called for manual recovery.\n// Attempt to recover full window stack.\nvoid ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in)\n{\n    // PVS-Studio V1044 is \"Loop break conditions do not depend on the number of iterations\"\n    ImGuiContext& g = *GImGui;\n    while (g.CurrentWindowStack.Size > state_in->SizeOfWindowStack) //-V1044\n    {\n        // Recap:\n        // - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped.\n        // - Always call a matching End() for each Begin() call, regardless of its return value!\n        // - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS.\n        // - We will fix that in a future major update.\n        ImGuiWindow* window = g.CurrentWindow;\n        if (window->Flags & ImGuiWindowFlags_ChildWindow)\n        {\n            if (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow)\n            {\n                IM_ASSERT_USER_ERROR(0, \"Missing EndTable()\");\n                EndTable();\n            }\n            else\n            {\n                IM_ASSERT_USER_ERROR(0, \"Missing EndChild()\");\n                EndChild();\n            }\n        }\n        else\n        {\n            IM_ASSERT_USER_ERROR(0, \"Missing End()\");\n            End();\n        }\n    }\n    if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack)\n        ErrorRecoveryTryToRecoverWindowState(state_in);\n}\n\n// Called by e.g. End() but may be called for manual recovery.\n// Read '// Error Handling [BETA]' block in imgui_internal.h for details.\n// Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.\nvoid    ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in)\n{\n    ImGuiContext& g = *GImGui;\n\n    while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing EndTable()\");\n        EndTable();\n    }\n\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.\n    while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing EndTabBar()\");\n        EndTabBar();\n    }\n    while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing EndMultiSelect()\");\n        EndMultiSelect();\n    }\n    if (window->DC.MenuBarAppending) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing EndMenuBar()\");\n        EndMenuBar();\n    }\n    while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing TreePop()\");\n        TreePop();\n    }\n    while (g.GroupStack.Size > state_in->SizeOfGroupStack) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing EndGroup()\");\n        EndGroup();\n    }\n    IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack);\n    while (window->IDStack.Size > state_in->SizeOfIDStack) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing PopID()\");\n        PopID();\n    }\n    while (g.DisabledStackSize > state_in->SizeOfDisabledStack) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing EndDisabled()\");\n        if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)\n            EndDisabled();\n        else\n        {\n            EndDisabledOverrideReenable();\n            g.CurrentWindowStack.back().DisabledOverrideReenable = false;\n        }\n    }\n    IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack);\n    while (g.ColorStack.Size > state_in->SizeOfColorStack) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing PopStyleColor()\");\n        PopStyleColor();\n    }\n    while (g.ItemFlagsStack.Size > state_in->SizeOfItemFlagsStack) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing PopItemFlag()\");\n        PopItemFlag();\n    }\n    while (g.StyleVarStack.Size > state_in->SizeOfStyleVarStack) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing PopStyleVar()\");\n        PopStyleVar();\n    }\n    while (g.FontStack.Size > state_in->SizeOfFontStack) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing PopFont()\");\n        PopFont();\n    }\n    while (g.FocusScopeStack.Size > state_in->SizeOfFocusScopeStack) //-V1044\n    {\n        IM_ASSERT_USER_ERROR(0, \"Missing PopFocusScope()\");\n        PopFocusScope();\n    }\n    //IM_ASSERT(g.FocusScopeStack.Size == state_in->SizeOfFocusScopeStack);\n}\n\nbool    ImGui::ErrorLog(const char* msg)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Output to debug log\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    ImGuiWindow* window = g.CurrentWindow;\n\n    if (g.IO.ConfigErrorRecoveryEnableDebugLog)\n    {\n        if (g.ErrorFirst)\n            IMGUI_DEBUG_LOG_ERROR(\"[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\\n\",\n                g.IO.ConfigErrorRecoveryEnableAssert, g.IO.ConfigErrorRecoveryEnableDebugLog, g.IO.ConfigErrorRecoveryEnableTooltip);\n        IMGUI_DEBUG_LOG_ERROR(\"[imgui-error] In window '%s': %s\\n\", window ? window->Name : \"NULL\", msg);\n    }\n    g.ErrorFirst = false;\n\n    // Output to tooltip\n    if (g.IO.ConfigErrorRecoveryEnableTooltip)\n    {\n        if (g.WithinFrameScope && BeginErrorTooltip())\n        {\n            if (g.ErrorCountCurrentFrame < 20)\n            {\n                Text(\"In window '%s': %s\", window ? window->Name : \"NULL\", msg);\n                if (window && (!window->IsFallbackWindow || window->WasActive))\n                    GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 0, 0, 255));\n            }\n            if (g.ErrorCountCurrentFrame == 20)\n                Text(\"(and more errors)\");\n            // EndFrame() will amend debug buttons to this window, after all errors have been submitted.\n            EndErrorTooltip();\n        }\n        g.ErrorCountCurrentFrame++;\n    }\n#endif\n\n    // Output to callback\n    if (g.ErrorCallback != NULL)\n        g.ErrorCallback(&g, g.ErrorCallbackUserData, msg);\n\n    // Return whether we should assert\n    return g.IO.ConfigErrorRecoveryEnableAssert;\n}\n\nvoid ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()\n{\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    ImGuiContext& g = *GImGui;\n    if (g.DebugDrawIdConflictsId != 0 && g.IO.KeyCtrl == false)\n        g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount;\n    if (g.DebugDrawIdConflictsId != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip())\n    {\n        Text(\"Programmer error: %d visible items with conflicting ID!\", g.DebugDrawIdConflictsCount);\n        BulletText(\"Code should use PushID()/PopID() in loops, or append \\\"##xx\\\" to same-label identifiers!\");\n        BulletText(\"Empty label e.g. Button(\\\"\\\") == same ID as parent widget/node. Use Button(\\\"##xx\\\") instead!\");\n        //BulletText(\"Code intending to use duplicate ID may use e.g. PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()\"); // Not making this too visible for fear of it being abused.\n        BulletText(\"Set io.ConfigDebugHighlightIdConflicts=false to disable this warning in non-programmers builds.\");\n        Separator();\n        if (g.IO.ConfigDebugHighlightIdConflictsShowItemPicker)\n        {\n            Text(\"(Hold Ctrl to: use \");\n            SameLine(0.0f, 0.0f);\n            if (SmallButton(\"Item Picker\"))\n                DebugStartItemPicker();\n            SameLine(0.0f, 0.0f);\n            Text(\" to break in item call-stack, or \");\n        }\n        else\n        {\n            Text(\"(Hold Ctrl to: \");\n        }\n        SameLine(0.0f, 0.0f);\n        TextLinkOpenURL(\"read FAQ \\\"About ID Stack System\\\"\", \"https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage\");\n        SameLine(0.0f, 0.0f);\n        Text(\")\");\n        EndErrorTooltip();\n    }\n\n    if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame\n    {\n        Separator();\n        Text(\"(Hold Ctrl to: \");\n        SameLine(0.0f, 0.0f);\n        if (SmallButton(\"Enable Asserts\"))\n            g.IO.ConfigErrorRecoveryEnableAssert = true;\n        //SameLine();\n        //if (SmallButton(\"Hide Error Tooltips\"))\n        //    g.IO.ConfigErrorRecoveryEnableTooltip = false; // Too dangerous\n        SameLine(0, 0);\n        Text(\")\");\n        EndErrorTooltip();\n    }\n#endif\n}\n\n// Pseudo-tooltip. Follow mouse until Ctrl is held. When Ctrl is held we lock position, allowing to click it.\nbool ImGui::BeginErrorTooltip()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = FindWindowByName(\"##Tooltip_Error\");\n    const bool use_locked_pos = (g.IO.KeyCtrl && window && window->WasActive);\n    PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.15f));\n    if (use_locked_pos)\n        SetNextWindowPos(g.ErrorTooltipLockedPos);\n    bool is_visible = Begin(\"##Tooltip_Error\", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);\n    PopStyleColor();\n    if (is_visible && g.CurrentWindow->BeginCount == 1)\n    {\n        SeparatorText(\"MESSAGE FROM DEAR IMGUI\");\n        BringWindowToDisplayFront(g.CurrentWindow);\n        BringWindowToFocusFront(g.CurrentWindow);\n        g.ErrorTooltipLockedPos = GetWindowPos();\n    }\n    else if (!is_visible)\n    {\n        End();\n    }\n    return is_visible;\n}\n\nvoid ImGui::EndErrorTooltip()\n{\n    End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ITEM SUBMISSION\n//-----------------------------------------------------------------------------\n// - KeepAliveID()\n// - ItemAdd()\n//-----------------------------------------------------------------------------\n\n// Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID().\nvoid ImGui::KeepAliveID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveId == id)\n        g.ActiveIdIsAlive = id;\n    if (g.DeactivatedItemData.ID == id)\n        g.DeactivatedItemData.IsAlive = true;\n}\n\n// Declare item bounding box for clipping and interaction.\n// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface\n// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.\n// THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN)\nIM_MSVC_RUNTIME_CHECKS_OFF\nbool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Set item data\n    // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)\n    g.LastItemData.ID = id;\n    g.LastItemData.Rect = bb;\n    g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;\n    g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;\n    g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;\n    // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.\n\n    if (id != 0)\n    {\n        KeepAliveID(id);\n\n        // Directional navigation processing\n        // Runs prior to clipping early-out\n        //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget\n        //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests\n        //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of\n        //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.\n        //      We could early out with \"if (is_clipped && !g.NavInitRequest) return false;\" but when we wouldn't be able\n        //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).\n        // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.\n        // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.\n        if (!(g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))\n        {\n            // FIXME-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.\n            window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);\n            if (g.NavId == id || g.NavAnyRequest)\n                if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)\n                    if (window == g.NavWindow || ((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened))\n                        NavProcessItem();\n        }\n\n        if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasShortcut)\n            ItemHandleShortcut(id);\n    }\n\n    // Lightweight clear of SetNextItemXXX data.\n    g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None;\n    g.NextItemData.ItemFlags = ImGuiItemFlags_None;\n\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    if (id != 0)\n        IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);\n#endif\n\n    // Clipping test\n    // (this is an inline copy of IsClippedEx() so we can reuse the is_rect_visible value, otherwise we'd do 'if (IsClippedEx(bb, id)) return false')\n    // g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts)\n    const bool is_rect_visible = bb.Overlaps(window->ClipRect);\n    if (!is_rect_visible)\n        if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))\n            if (!g.ItemUnclipByLog)\n                return false;\n\n    // [DEBUG]\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (id != 0)\n    {\n        if (id == g.DebugLocateId)\n            DebugLocateItemResolveWithLastItem();\n\n        // [DEBUG] People keep stumbling on this problem and using \"\" as identifier in the root of a window instead of \"##something\".\n        // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use \"##something\".\n        // READ THE FAQ: https://dearimgui.com/faq\n        IM_ASSERT(id != window->ID && \"Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!\");\n\n        // [DEBUG] Highlight all conflicts WITHOUT needing to hover. THIS WILL SLOW DOWN DEAR IMGUI. DON'T KEEP ACTIVATED.\n        // This will only work for items submitted with ItemAdd(). Some very rare/odd/unrecommended code patterns are calling ButtonBehavior() without ItemAdd().\n#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS\n        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowDuplicateId) == 0)\n        {\n            int* p_alive = g.DebugDrawIdConflictsAliveCount.GetIntRef(id, -1); // Could halve lookups if we knew ImGuiStorage can store 64-bit, or by storing FrameCount as 30-bits + highlight as 2-bits. But the point is that we should not pretend that this is fast.\n            int* p_highlight = g.DebugDrawIdConflictsHighlightSet.GetIntRef(id, -1);\n            if (*p_alive == g.FrameCount)\n                *p_highlight = g.FrameCount;\n            *p_alive = g.FrameCount;\n            if (*p_highlight >= g.FrameCount - 1)\n                window->DrawList->AddRect(bb.Min - ImVec2(1, 1), bb.Max + ImVec2(1, 1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f);\n        }\n#endif\n    }\n    //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]\n    //if ((g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav) == 0)\n    //    window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]\n#endif\n\n    if (id != 0 && g.DeactivatedItemData.ID == id)\n        g.DeactivatedItemData.ElapseFrame = g.FrameCount;\n\n    // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)\n    if (is_rect_visible)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;\n    if (IsMouseHoveringRect(bb.Min, bb.Max))\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;\n    return true;\n}\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n//-----------------------------------------------------------------------------\n// [SECTION] LAYOUT\n//-----------------------------------------------------------------------------\n// - ItemSize()\n// - SameLine()\n// - GetCursorScreenPos()\n// - SetCursorScreenPos()\n// - GetCursorPos(), GetCursorPosX(), GetCursorPosY()\n// - SetCursorPos(), SetCursorPosX(), SetCursorPosY()\n// - GetCursorStartPos()\n// - Indent()\n// - Unindent()\n// - SetNextItemWidth()\n// - PushItemWidth()\n// - PushMultiItemsWidths()\n// - PopItemWidth()\n// - CalcItemWidth()\n// - CalcItemSize()\n// - GetTextLineHeight()\n// - GetTextLineHeightWithSpacing()\n// - GetFrameHeight()\n// - GetFrameHeightWithSpacing()\n// - GetContentRegionMax()\n// - GetContentRegionAvail(),\n// - BeginGroup()\n// - EndGroup()\n// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.\n//-----------------------------------------------------------------------------\n\n// Advance cursor given item size for layout.\n// Register minimum needed size so it can extend the bounding box used for auto-fit calculation.\n// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.\n// THIS IS IN THE PERFORMANCE CRITICAL PATH.\nIM_MSVC_RUNTIME_CHECKS_OFF\nvoid ImGui::ItemSize(const ImVec2& size, float text_baseline_y)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    // We increase the height in this function to accommodate for baseline offset.\n    // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,\n    // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.\n    const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;\n\n    const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y;\n    const float line_height = ImMax(window->DC.CurrLineSize.y, /*ImMax(*/window->DC.CursorPos.y - line_y1/*, 0.0f)*/ + size.y + offset_to_match_baseline_y);\n\n    // Always align ourselves on pixel boundaries\n    //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]\n    window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;\n    window->DC.CursorPosPrevLine.y = line_y1;\n    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line\n    window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y);                       // Next line\n    window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);\n    window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);\n    //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]\n\n    window->DC.PrevLineSize.y = line_height;\n    window->DC.CurrLineSize.y = 0.0f;\n    window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);\n    window->DC.CurrLineTextBaseOffset = 0.0f;\n    window->DC.IsSameLine = window->DC.IsSetPos = false;\n\n    // Horizontal layout mode\n    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)\n        SameLine();\n}\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n// Gets back to previous line and continue with horizontal layout\n//      offset_from_start_x == 0 : follow right after previous item\n//      offset_from_start_x != 0 : align to specified x position (relative to window/group left)\n//      spacing_w < 0            : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0\n//      spacing_w >= 0           : enforce spacing amount\nvoid ImGui::SameLine(float offset_from_start_x, float spacing_w)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    if (offset_from_start_x != 0.0f)\n    {\n        if (spacing_w < 0.0f)\n            spacing_w = 0.0f;\n        window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;\n        window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;\n    }\n    else\n    {\n        if (spacing_w < 0.0f)\n            spacing_w = g.Style.ItemSpacing.x;\n        window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;\n        window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;\n    }\n    window->DC.CurrLineSize = window->DC.PrevLineSize;\n    window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;\n    window->DC.IsSameLine = true;\n}\n\nImVec2 ImGui::GetCursorScreenPos()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CursorPos;\n}\n\nvoid ImGui::SetCursorScreenPos(const ImVec2& pos)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.CursorPos = pos;\n    //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);\n    window->DC.IsSetPos = true;\n}\n\n// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.\n// Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.\nImVec2 ImGui::GetCursorPos()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CursorPos - window->Pos + window->Scroll;\n}\n\nfloat ImGui::GetCursorPosX()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;\n}\n\nfloat ImGui::GetCursorPosY()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;\n}\n\nvoid ImGui::SetCursorPos(const ImVec2& local_pos)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.CursorPos = window->Pos - window->Scroll + local_pos;\n    //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);\n    window->DC.IsSetPos = true;\n}\n\nvoid ImGui::SetCursorPosX(float x)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;\n    //window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);\n    window->DC.IsSetPos = true;\n}\n\nvoid ImGui::SetCursorPosY(float y)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;\n    //window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);\n    window->DC.IsSetPos = true;\n}\n\nImVec2 ImGui::GetCursorStartPos()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CursorStartPos - window->Pos;\n}\n\nvoid ImGui::Indent(float indent_w)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;\n    window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;\n}\n\nvoid ImGui::Unindent(float indent_w)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;\n    window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;\n}\n\n// Affect large frame+labels widgets only.\nvoid ImGui::SetNextItemWidth(float item_width)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasWidth;\n    g.NextItemData.Width = item_width;\n}\n\n// FIXME: Remove the == 0.0f behavior?\nvoid ImGui::PushItemWidth(float item_width)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width\n    window->DC.ItemWidth = (item_width == 0.0f ? window->DC.ItemWidthDefault : item_width);\n    g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;\n}\n\nvoid ImGui::PushMultiItemsWidths(int components, float w_full)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(components > 0);\n    const ImGuiStyle& style = g.Style;\n    window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width\n    float w_items = w_full - style.ItemInnerSpacing.x * (components - 1);\n    float prev_split = w_items;\n    for (int i = components - 1; i > 0; i--)\n    {\n        float next_split = IM_TRUNC(w_items * i / components);\n        window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f));\n        prev_split = next_split;\n    }\n    window->DC.ItemWidth = ImMax(prev_split, 1.0f);\n    g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;\n}\n\nvoid ImGui::PopItemWidth()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->DC.ItemWidthStack.Size <= 0)\n    {\n        IM_ASSERT_USER_ERROR(0, \"Calling PopItemWidth() too many times!\");\n        return;\n    }\n    window->DC.ItemWidth = window->DC.ItemWidthStack.back();\n    window->DC.ItemWidthStack.pop_back();\n}\n\n// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().\n// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()\nfloat ImGui::CalcItemWidth()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    float w;\n    if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)\n        w = g.NextItemData.Width;\n    else\n        w = window->DC.ItemWidth;\n    if (w < 0.0f)\n    {\n        float region_avail_x = GetContentRegionAvail().x;\n        w = ImMax(1.0f, region_avail_x + w);\n    }\n    w = IM_TRUNC(w);\n    return w;\n}\n\n// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().\n// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.\n// Note that only CalcItemWidth() is publicly exposed.\n// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)\nImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)\n{\n    ImVec2 avail;\n    if (size.x < 0.0f || size.y < 0.0f)\n        avail = GetContentRegionAvail();\n\n    if (size.x == 0.0f)\n        size.x = default_w;\n    else if (size.x < 0.0f)\n        size.x = ImMax(4.0f, avail.x + size.x); // <-- size.x is negative here so we are subtracting\n\n    if (size.y == 0.0f)\n        size.y = default_h;\n    else if (size.y < 0.0f)\n        size.y = ImMax(4.0f, avail.y + size.y); // <-- size.y is negative here so we are subtracting\n\n    return size;\n}\n\nfloat ImGui::GetTextLineHeight()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize;\n}\n\nfloat ImGui::GetTextLineHeightWithSpacing()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize + g.Style.ItemSpacing.y;\n}\n\nfloat ImGui::GetFrameHeight()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize + g.Style.FramePadding.y * 2.0f;\n}\n\nfloat ImGui::GetFrameHeightWithSpacing()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;\n}\n\nImVec2 ImGui::GetContentRegionAvail()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;\n    return mx - window->DC.CursorPos;\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n\n// You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()!\n// They are bizarre local-coordinates which don't play well with scrolling.\nImVec2 ImGui::GetContentRegionMax()\n{\n    return GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos();\n}\n\nImVec2 ImGui::GetWindowContentRegionMin()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ContentRegionRect.Min - window->Pos;\n}\n\nImVec2 ImGui::GetWindowContentRegionMax()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ContentRegionRect.Max - window->Pos;\n}\n#endif\n\n// Lock horizontal starting position + capture group bounding box into one \"item\" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)\n// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.\n// FIXME-OPT: Could we safely early out on ->SkipItems?\nvoid ImGui::BeginGroup()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    g.GroupStack.resize(g.GroupStack.Size + 1);\n    ImGuiGroupData& group_data = g.GroupStack.back();\n    group_data.WindowID = window->ID;\n    group_data.BackupCursorPos = window->DC.CursorPos;\n    group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;\n    group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;\n    group_data.BackupIndent = window->DC.Indent;\n    group_data.BackupGroupOffset = window->DC.GroupOffset;\n    group_data.BackupCurrLineSize = window->DC.CurrLineSize;\n    group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;\n    group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;\n    group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;\n    group_data.BackupIsSameLine = window->DC.IsSameLine;\n    group_data.BackupActiveIdHasBeenEditedThisFrame = g.ActiveIdHasBeenEditedThisFrame;\n    group_data.BackupDeactivatedIdIsAlive = g.DeactivatedItemData.IsAlive;\n    group_data.EmitItem = true;\n\n    window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;\n    window->DC.Indent = window->DC.GroupOffset;\n    window->DC.CursorMaxPos = window->DC.CursorPos;\n    window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);\n    if (g.LogEnabled)\n        g.LogLinePosY = -FLT_MAX; // To enforce a carriage return\n}\n\nvoid ImGui::EndGroup()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls\n\n    ImGuiGroupData& group_data = g.GroupStack.back();\n    IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?\n\n    if (window->DC.IsSetPos)\n        ErrorCheckUsingSetCursorPosToExtendParentBoundaries();\n\n    // Include LastItemData.Rect.Max as a workaround for e.g. EndTable() undershooting with CursorMaxPos report. (#7543)\n    ImRect group_bb(group_data.BackupCursorPos, ImMax(ImMax(window->DC.CursorMaxPos, g.LastItemData.Rect.Max), group_data.BackupCursorPos));\n    window->DC.CursorPos = group_data.BackupCursorPos;\n    window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine;\n    window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, group_bb.Max);\n    window->DC.Indent = group_data.BackupIndent;\n    window->DC.GroupOffset = group_data.BackupGroupOffset;\n    window->DC.CurrLineSize = group_data.BackupCurrLineSize;\n    window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;\n    window->DC.IsSameLine = group_data.BackupIsSameLine;\n    if (g.LogEnabled)\n        g.LogLinePosY = -FLT_MAX; // To enforce a carriage return\n\n    if (!group_data.EmitItem)\n    {\n        g.GroupStack.pop_back();\n        return;\n    }\n\n    window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.\n    ItemSize(group_bb.GetSize());\n    ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop);\n\n    // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group.\n    // It would be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets.\n    // Also if you grep for LastItemId you'll notice it is only used in that context.\n    // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)\n    const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;\n    const bool group_contains_deactivated_id = (group_data.BackupDeactivatedIdIsAlive == false) && (g.DeactivatedItemData.IsAlive == true);\n    if (group_contains_curr_active_id)\n        g.LastItemData.ID = g.ActiveId;\n    else if (group_contains_deactivated_id)\n        g.LastItemData.ID = g.DeactivatedItemData.ID;\n    g.LastItemData.Rect = group_bb;\n\n    // Forward Hovered flag\n    const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;\n    if (group_contains_curr_hovered_id)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;\n\n    // Forward Edited flag\n    if (g.ActiveIdHasBeenEditedThisFrame && !group_data.BackupActiveIdHasBeenEditedThisFrame)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;\n\n    // Forward Deactivated flag\n    g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;\n    if (group_contains_deactivated_id)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;\n\n    g.GroupStack.pop_back();\n    if (g.DebugShowGroupRects)\n        window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] SCROLLING\n//-----------------------------------------------------------------------------\n\n// Helper to snap on edges when aiming at an item very close to the edge,\n// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.\n// When we refactor the scrolling API this may be configurable with a flag?\n// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.\nstatic float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)\n{\n    if (target <= snap_min + snap_threshold)\n        return ImLerp(snap_min, target, center_ratio);\n    if (target >= snap_max - snap_threshold)\n        return ImLerp(target, snap_max, center_ratio);\n    return target;\n}\n\nstatic ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)\n{\n    ImVec2 scroll = window->Scroll;\n    ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2);\n    for (int axis = 0; axis < 2; axis++)\n    {\n        if (window->ScrollTarget[axis] < FLT_MAX)\n        {\n            float center_ratio = window->ScrollTargetCenterRatio[axis];\n            float scroll_target = window->ScrollTarget[axis];\n            if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f)\n            {\n                float snap_min = 0.0f;\n                float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis];\n                scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio);\n            }\n            scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);\n        }\n        scroll[axis] = ImRound64(ImMax(scroll[axis], 0.0f));\n        if (!window->Collapsed && !window->SkipItems)\n            scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]);\n    }\n    return scroll;\n}\n\nvoid ImGui::ScrollToItem(ImGuiScrollFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ScrollToRectEx(window, g.LastItemData.NavRect, flags);\n}\n\nvoid ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)\n{\n    ScrollToRectEx(window, item_rect, flags);\n}\n\n// Scroll to keep newly navigated item fully into view\nImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));\n    scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x);\n    scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y);\n    //GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG]\n    //GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG]\n\n    // Check that only one behavior is selected per axis\n    IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));\n    IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));\n\n    // Defaults\n    ImGuiScrollFlags in_flags = flags;\n    if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)\n        flags |= ImGuiScrollFlags_KeepVisibleEdgeX;\n    if ((flags & ImGuiScrollFlags_MaskY_) == 0)\n        flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;\n\n    const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x;\n    const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y;\n    const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= scroll_rect.GetWidth() || (window->AutoFitFramesX > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;\n    const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= scroll_rect.GetHeight() || (window->AutoFitFramesY > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;\n\n    if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)\n    {\n        if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x)\n            SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f);\n        else if (item_rect.Max.x >= scroll_rect.Max.x)\n            SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f);\n    }\n    else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))\n    {\n        if (can_be_fully_visible_x)\n            SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f);\n        else\n            SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f);\n    }\n\n    if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)\n    {\n        if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y)\n            SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f);\n        else if (item_rect.Max.y >= scroll_rect.Max.y)\n            SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f);\n    }\n    else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))\n    {\n        if (can_be_fully_visible_y)\n            SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f);\n        else\n            SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f);\n    }\n\n    ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);\n    ImVec2 delta_scroll = next_scroll - window->Scroll;\n\n    // Also scroll parent window to keep us into view if necessary\n    if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))\n    {\n        // FIXME-SCROLL: May be an option?\n        if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)\n            in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;\n        if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)\n            in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;\n        delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags);\n    }\n\n    return delta_scroll;\n}\n\nfloat ImGui::GetScrollX()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->Scroll.x;\n}\n\nfloat ImGui::GetScrollY()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->Scroll.y;\n}\n\nfloat ImGui::GetScrollMaxX()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ScrollMax.x;\n}\n\nfloat ImGui::GetScrollMaxY()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ScrollMax.y;\n}\n\nvoid ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)\n{\n    window->ScrollTarget.x = scroll_x;\n    window->ScrollTargetCenterRatio.x = 0.0f;\n    window->ScrollTargetEdgeSnapDist.x = 0.0f;\n}\n\nvoid ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)\n{\n    window->ScrollTarget.y = scroll_y;\n    window->ScrollTargetCenterRatio.y = 0.0f;\n    window->ScrollTargetEdgeSnapDist.y = 0.0f;\n}\n\nvoid ImGui::SetScrollX(float scroll_x)\n{\n    ImGuiContext& g = *GImGui;\n    SetScrollX(g.CurrentWindow, scroll_x);\n}\n\nvoid ImGui::SetScrollY(float scroll_y)\n{\n    ImGuiContext& g = *GImGui;\n    SetScrollY(g.CurrentWindow, scroll_y);\n}\n\n// Note that a local position will vary depending on initial scroll value,\n// This is a little bit confusing so bear with us:\n//  - local_pos = (absolution_pos - window->Pos)\n//  - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,\n//    and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.\n//  - They mostly exist because of legacy API.\n// Following the rules above, when trying to work with scrolling code, consider that:\n//  - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!\n//  - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense\n// We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size\nvoid ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)\n{\n    IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);\n    window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset\n    window->ScrollTargetCenterRatio.x = center_x_ratio;\n    window->ScrollTargetEdgeSnapDist.x = 0.0f;\n}\n\nvoid ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)\n{\n    IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);\n    window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset\n    window->ScrollTargetCenterRatio.y = center_y_ratio;\n    window->ScrollTargetEdgeSnapDist.y = 0.0f;\n}\n\nvoid ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)\n{\n    ImGuiContext& g = *GImGui;\n    SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);\n}\n\nvoid ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)\n{\n    ImGuiContext& g = *GImGui;\n    SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);\n}\n\n// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.\nvoid ImGui::SetScrollHereX(float center_x_ratio)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);\n    float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);\n    SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos\n\n    // Tweak: snap on edges when aiming at an item very close to the edge\n    window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);\n}\n\n// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.\nvoid ImGui::SetScrollHereY(float center_y_ratio)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);\n    float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);\n    SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos\n\n    // Tweak: snap on edges when aiming at an item very close to the edge\n    window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] TOOLTIPS\n//-----------------------------------------------------------------------------\n\nbool ImGui::BeginTooltip()\n{\n    return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);\n}\n\nbool ImGui::BeginItemTooltip()\n{\n    if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n        return false;\n    return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);\n}\n\nbool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)\n{\n    ImGuiContext& g = *GImGui;\n\n    const bool is_dragdrop_tooltip = g.DragDropWithinSource || g.DragDropWithinTarget;\n    if (is_dragdrop_tooltip)\n    {\n        // Drag and Drop tooltips are positioning differently than other tooltips:\n        // - offset visibility to increase visibility around mouse.\n        // - never clamp within outer viewport boundary.\n        // We call SetNextWindowPos() to enforce position and disable clamping.\n        // See FindBestWindowPosForPopup() for positioning logic of other tooltips (not drag and drop ones).\n        //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;\n        const bool is_touchscreen = (g.IO.MouseSource == ImGuiMouseSource_TouchScreen);\n        if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) == 0)\n        {\n            ImVec2 tooltip_pos = is_touchscreen ? (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_TOUCH * g.Style.MouseCursorScale) : (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * g.Style.MouseCursorScale);\n            ImVec2 tooltip_pivot = is_touchscreen ? TOOLTIP_DEFAULT_PIVOT_TOUCH : ImVec2(0.0f, 0.0f);\n            SetNextWindowPos(tooltip_pos, ImGuiCond_None, tooltip_pivot);\n        }\n\n        SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);\n        //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkerboard has issue with transparent colors :(\n        tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;\n    }\n\n    // Hide previous tooltip from being displayed. We can't easily \"reset\" the content of a window so we create a new one.\n    if ((tooltip_flags & ImGuiTooltipFlags_OverridePrevious) && g.TooltipPreviousWindow != NULL && g.TooltipPreviousWindow->Active && !IsWindowInBeginStack(g.TooltipPreviousWindow))\n    {\n        //IMGUI_DEBUG_LOG(\"[tooltip] '%s' already active, using +1 for this frame\\n\", window_name);\n        SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow);\n        g.TooltipOverrideCount++;\n    }\n\n    const char* window_name_template = is_dragdrop_tooltip ? \"##Tooltip_DragDrop_%02d\" : \"##Tooltip_%02d\";\n    char window_name[32];\n    ImFormatString(window_name, IM_COUNTOF(window_name), window_name_template, g.TooltipOverrideCount);\n    ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking;\n    Begin(window_name, NULL, flags | extra_window_flags);\n    // 2023-03-09: Added bool return value to the API, but currently always returning true.\n    // If this ever returns false we need to update BeginDragDropSource() accordingly.\n    //if (!ret)\n    //    End();\n    //return ret;\n    return true;\n}\n\nvoid ImGui::EndTooltip()\n{\n    IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls\n    End();\n}\n\nvoid ImGui::SetTooltip(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    SetTooltipV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::SetTooltipV(const char* fmt, va_list args)\n{\n    if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))\n        return;\n    TextV(fmt, args);\n    EndTooltip();\n}\n\n// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.\n// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.\nvoid ImGui::SetItemTooltip(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n        SetTooltipV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::SetItemTooltipV(const char* fmt, va_list args)\n{\n    if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n        SetTooltipV(fmt, args);\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] POPUPS\n//-----------------------------------------------------------------------------\n\n// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel\nbool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (popup_flags & ImGuiPopupFlags_AnyPopupId)\n    {\n        // Return true if any popup is open at the current BeginPopup() level of the popup stack\n        // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.\n        IM_ASSERT(id == 0);\n        if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)\n            return g.OpenPopupStack.Size > 0;\n        else\n            return g.OpenPopupStack.Size > g.BeginPopupStack.Size;\n    }\n    else\n    {\n        if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)\n        {\n            // Return true if the popup is open anywhere in the popup stack\n            for (ImGuiPopupData& popup_data : g.OpenPopupStack)\n                if (popup_data.PopupId == id)\n                    return true;\n            return false;\n        }\n        else\n        {\n            // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)\n            return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;\n        }\n    }\n}\n\nbool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);\n    if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)\n        IM_ASSERT(0 && \"Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel.\"); // But non-string version is legal and used internally\n    return IsPopupOpen(id, popup_flags);\n}\n\n// Also see FindBlockingModal(NULL)\nImGuiWindow* ImGui::GetTopMostPopupModal()\n{\n    ImGuiContext& g = *GImGui;\n    for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)\n        if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)\n            if (popup->Flags & ImGuiWindowFlags_Modal)\n                return popup;\n    return NULL;\n}\n\n// See Demo->Stacked Modal to confirm what this is for.\nImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal()\n{\n    ImGuiContext& g = *GImGui;\n    for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)\n        if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)\n            if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup))\n                return popup;\n    return NULL;\n}\n\n\n// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)\n// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.\n// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.\n// - WindowA            // FindBlockingModal() returns Modal1\n//   - WindowB          //                  .. returns Modal1\n//   - Modal1           //                  .. returns Modal2\n//      - WindowC       //                  .. returns Modal2\n//          - WindowD   //                  .. returns Modal2\n//          - Modal2    //                  .. returns Modal2\n//            - WindowE //                  .. returns NULL\n// Notes:\n// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.\n//   Only difference is here we check for ->Active/WasActive but it may be unnecessary.\nImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.OpenPopupStack.Size <= 0)\n        return NULL;\n\n    // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.\n    for (ImGuiPopupData& popup_data : g.OpenPopupStack)\n    {\n        ImGuiWindow* popup_window = popup_data.Window;\n        if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))\n            continue;\n        if (!popup_window->Active && !popup_window->WasActive)  // Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows.\n            continue;\n        if (window == NULL)                                     // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.\n            return popup_window;\n        if (IsWindowWithinBeginStackOf(window, popup_window))   // Window may be over modal\n            continue;\n        return popup_window;                                    // Place window right below first block modal\n    }\n    return NULL;\n}\n\nvoid ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiID id = g.CurrentWindow->GetID(str_id);\n    IMGUI_DEBUG_LOG_POPUP(\"[popup] OpenPopup(\\\"%s\\\" -> 0x%08X)\\n\", str_id, id);\n    OpenPopupEx(id, popup_flags);\n}\n\nvoid ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)\n{\n    OpenPopupEx(id, popup_flags);\n}\n\n// Mark popup as open (toggle toward open state).\n// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.\n// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).\n// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)\nvoid ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* parent_window = g.CurrentWindow;\n    const int current_stack_size = g.BeginPopupStack.Size;\n\n    if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)\n        if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId))\n            return;\n\n    ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.\n    popup_ref.PopupId = id;\n    popup_ref.Window = NULL;\n    popup_ref.RestoreNavWindow = g.NavWindow;           // When popup closes focus may be restored to NavWindow (depend on window type).\n    popup_ref.OpenFrameCount = g.FrameCount;\n    popup_ref.OpenParentId = parent_window->IDStack.back();\n    popup_ref.OpenPopupPos = NavCalcPreferredRefPos(ImGuiWindowFlags_Popup);\n    popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;\n\n    IMGUI_DEBUG_LOG_POPUP(\"[popup] OpenPopupEx(0x%08X)\\n\", id);\n    if (g.OpenPopupStack.Size < current_stack_size + 1)\n    {\n        g.OpenPopupStack.push_back(popup_ref);\n    }\n    else\n    {\n        // Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake!\n        // However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be\n        // in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer.\n        // Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified.\n        bool keep_existing = false;\n        if (g.OpenPopupStack[current_stack_size].PopupId == id)\n            if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen))\n                keep_existing = true;\n        if (keep_existing)\n        {\n            // No reopen\n            g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;\n        }\n        else\n        {\n            // Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation)\n            ClosePopupToLevel(current_stack_size, true);\n            g.OpenPopupStack.push_back(popup_ref);\n        }\n\n        // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().\n        // This is equivalent to what ClosePopupToLevel() does.\n        //if (g.OpenPopupStack[current_stack_size].PopupId == id)\n        //    FocusWindow(parent_window);\n    }\n}\n\n// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.\n// This function closes any popups that are over 'ref_window'.\nvoid ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.OpenPopupStack.Size == 0)\n        return;\n\n    // Don't close our own child popup windows.\n    //IMGUI_DEBUG_LOG_POPUP(\"[popup] ClosePopupsOverWindow(\\\"%s\\\") restore_under=%d\\n\", ref_window ? ref_window->Name : \"<NULL>\", restore_focus_to_window_under_popup);\n    int popup_count_to_keep = 0;\n    if (ref_window)\n    {\n        // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)\n        for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)\n        {\n            ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];\n            if (!popup.Window)\n                continue;\n            IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);\n\n            // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)\n            // - Clicking/Focusing Window2 won't close Popup1:\n            //     Window -> Popup1 -> Window2(Ref)\n            // - Clicking/focusing Popup1 will close Popup2 and Popup3:\n            //     Window -> Popup1(Ref) -> Popup2 -> Popup3\n            // - Each popups may contain child windows, which is why we compare ->RootWindowDockTree!\n            //     Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child\n            // We step through every popup from bottom to top to validate their position relative to reference window.\n            bool ref_window_is_descendant_of_popup = false;\n            for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)\n                if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)\n                    //if (popup_window->RootWindowDockTree == ref_window->RootWindowDockTree) // FIXME-MERGE\n                    if (IsWindowWithinBeginStackOf(ref_window, popup_window))\n                    {\n                        ref_window_is_descendant_of_popup = true;\n                        break;\n                    }\n            if (!ref_window_is_descendant_of_popup)\n                break;\n        }\n    }\n    if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below\n    {\n        IMGUI_DEBUG_LOG_POPUP(\"[popup] ClosePopupsOverWindow(\\\"%s\\\")\\n\", ref_window ? ref_window->Name : \"<NULL>\");\n        ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);\n    }\n}\n\nvoid ImGui::ClosePopupsExceptModals()\n{\n    ImGuiContext& g = *GImGui;\n\n    int popup_count_to_keep;\n    for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)\n    {\n        ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;\n        if (!window || (window->Flags & ImGuiWindowFlags_Modal))\n            break;\n    }\n    if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below\n        ClosePopupToLevel(popup_count_to_keep, true);\n}\n\nvoid ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)\n{\n    ImGuiContext& g = *GImGui;\n    IMGUI_DEBUG_LOG_POPUP(\"[popup] ClosePopupToLevel(%d), restore_under=%d\\n\", remaining, restore_focus_to_window_under_popup);\n    IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);\n    if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup)\n        for (int n = remaining; n < g.OpenPopupStack.Size; n++)\n            IMGUI_DEBUG_LOG_POPUP(\"[popup] - Closing PopupID 0x%08X Window \\\"%s\\\"\\n\", g.OpenPopupStack[n].PopupId, g.OpenPopupStack[n].Window ? g.OpenPopupStack[n].Window->Name : NULL);\n\n    // Trim open popup stack\n    ImGuiPopupData prev_popup = g.OpenPopupStack[remaining];\n    g.OpenPopupStack.resize(remaining);\n\n    // Restore focus (unless popup window was not yet submitted, and didn't have a chance to take focus anyhow. See #7325 for an edge case)\n    if (restore_focus_to_window_under_popup && prev_popup.Window)\n    {\n        ImGuiWindow* popup_window = prev_popup.Window;\n        ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow;\n        if (focus_window && !focus_window->WasActive)\n            FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback\n        else\n            FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None);\n    }\n}\n\n// Close the popup we have begin-ed into.\nvoid ImGui::CloseCurrentPopup()\n{\n    ImGuiContext& g = *GImGui;\n    int popup_idx = g.BeginPopupStack.Size - 1;\n    if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)\n        return;\n\n    // Closing a menu closes its top-most parent popup (unless a modal)\n    while (popup_idx > 0)\n    {\n        ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;\n        ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;\n        bool close_parent = false;\n        if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))\n            if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar))\n                close_parent = true;\n        if (!close_parent)\n            break;\n        popup_idx--;\n    }\n    IMGUI_DEBUG_LOG_POPUP(\"[popup] CloseCurrentPopup %d -> %d\\n\", g.BeginPopupStack.Size - 1, popup_idx);\n    ClosePopupToLevel(popup_idx, true);\n\n    // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.\n    // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.\n    // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.\n    if (ImGuiWindow* window = g.NavWindow)\n        window->DC.NavHideHighlightOneFrame = true;\n}\n\n// Attention! BeginPopup() adds default flags when calling BeginPopupEx()!\nbool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (!IsPopupOpen(id, ImGuiPopupFlags_None))\n    {\n        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n        return false;\n    }\n\n    char name[20];\n    IM_ASSERT((extra_window_flags & ImGuiWindowFlags_ChildMenu) == 0); // Use BeginPopupMenuEx()\n    ImFormatString(name, IM_COUNTOF(name), \"##Popup_%08x\", id); // No recycling, so we can close/open during the same frame\n\n    bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking);\n    if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)\n        EndPopup();\n    //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;\n    return is_open;\n}\n\nbool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags extra_window_flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (!IsPopupOpen(id, ImGuiPopupFlags_None))\n    {\n        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n        return false;\n    }\n\n    char name[128];\n    IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu);\n    ImFormatString(name, IM_COUNTOF(name), \"%s###Menu_%02d\", label, g.BeginMenuDepth); // Recycle windows based on depth\n    bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup);\n    if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)\n        EndPopup();\n    //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;\n    return is_open;\n}\n\nbool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance\n    {\n        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n        return false;\n    }\n    flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;\n    ImGuiID id = g.CurrentWindow->GetID(str_id);\n    return BeginPopupEx(id, flags);\n}\n\n// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.\n// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).\n// - *p_open set back to false in BeginPopupModal() when popup is not open.\n// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup.\nbool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    const ImGuiID id = window->GetID(name);\n    if (!IsPopupOpen(id, ImGuiPopupFlags_None))\n    {\n        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n        if (p_open && *p_open)\n            *p_open = false;\n        return false;\n    }\n\n    // Center modal windows by default for increased visibility\n    // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)\n    // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.\n    if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) == 0)\n    {\n        const ImGuiViewport* viewport = window->WasActive ? window->Viewport : GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport?\n        SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));\n    }\n\n    flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking;\n    const bool is_open = Begin(name, p_open, flags);\n    if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)\n    {\n        EndPopup();\n        if (is_open)\n            ClosePopupToLevel(g.BeginPopupStack.Size, true);\n        return false;\n    }\n    return is_open;\n}\n\nvoid ImGui::EndPopup()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT_USER_ERROR_RET((window->Flags & ImGuiWindowFlags_Popup) != 0 && g.BeginPopupStack.Size > 0, \"Calling EndPopup() in wrong window!\");\n\n    // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)\n    if (g.NavWindow == window)\n        NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);\n\n    // Child-popups don't need to be laid out\n    const ImGuiID backup_within_end_child_id = g.WithinEndChildID;\n    if (window->Flags & ImGuiWindowFlags_ChildWindow)\n        g.WithinEndChildID = window->ID;\n    End();\n    g.WithinEndChildID = backup_within_end_child_id;\n}\n\nImGuiMouseButton ImGui::GetMouseButtonFromPopupFlags(ImGuiPopupFlags flags)\n{\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    if ((flags & ImGuiPopupFlags_InvalidMask_) != 0) // 1,2 --> ImGuiMouseButton_Right, ImGuiMouseButton_Middle\n        return (flags & ImGuiPopupFlags_InvalidMask_);\n#else\n    IM_ASSERT((flags & ImGuiPopupFlags_InvalidMask_) == 0);\n#endif\n    if (flags & ImGuiPopupFlags_MouseButtonMask_)\n        return ((flags & ImGuiPopupFlags_MouseButtonMask_) >> ImGuiPopupFlags_MouseButtonShift_) - 1;\n    return ImGuiMouseButton_Right; // Default == 1\n}\n\nbool ImGui::IsPopupOpenRequestForItem(ImGuiPopupFlags popup_flags, ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags);\n    if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))\n        return true;\n    if (g.NavOpenContextMenuItemId == id && (IsItemFocused() || id == g.CurrentWindow->MoveId))\n        return true;\n    return false;\n}\n\nbool ImGui::IsPopupOpenRequestForWindow(ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags);\n    if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))\n        if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())\n            return true;\n    if (g.NavOpenContextMenuWindowId && g.CurrentWindow->ID)\n        if (IsWindowChildOf(g.NavWindow, g.CurrentWindow, false, false)) // This enable ordering to be used to disambiguate item vs window (#8803)\n            return true;\n    return false;\n}\n\n// Helper to open a popup if mouse button is released over the item\n// - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()\nvoid ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID))\n    {\n        ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!\n        IM_ASSERT(id != 0);                                              // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)\n        OpenPopupEx(id, popup_flags);\n    }\n}\n\n// This is a helper to handle the simplest case of associating one named popup to one given widget.\n// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.\n// - To create a popup with a specific identifier, pass it in str_id.\n//    - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.\n//    - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.\n// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).\n//   This is essentially the same as:\n//       id = str_id ? GetID(str_id) : GetItemID();\n//       OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight);\n//       return BeginPopup(id);\n//   Which is essentially the same as:\n//       id = str_id ? GetID(str_id) : GetItemID();\n//       if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))\n//           OpenPopup(id);\n//       return BeginPopup(id);\n//   The main difference being that this is tweaked to avoid computing the ID twice.\nbool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n    ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItem ID. Using LastItem ID as a Popup ID won't conflict!\n    IM_ASSERT(id != 0);                                              // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)\n    if (IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID))\n        OpenPopupEx(id, popup_flags);\n    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);\n}\n\nbool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (!str_id)\n        str_id = \"window_context\";\n    ImGuiID id = window->GetID(str_id);\n    if (IsPopupOpenRequestForWindow(popup_flags))\n        OpenPopupEx(id, popup_flags);\n    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);\n}\n\nbool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (!str_id)\n        str_id = \"void_context\";\n    ImGuiID id = window->GetID(str_id);\n    ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags);\n    if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))\n        if (GetTopMostPopupModal() == NULL)\n            OpenPopupEx(id, popup_flags);\n    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);\n}\n\n// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)\n// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.\n// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor\n//  information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.\n//  this allows us to have tooltips/popups displayed out of the parent viewport.)\nImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)\n{\n    ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);\n    //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));\n    //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));\n\n    // Combo Box policy (we want a connecting edge)\n    if (policy == ImGuiPopupPositionPolicy_ComboBox)\n    {\n        const ImGuiDir dir_preferred_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };\n        for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)\n        {\n            const ImGuiDir dir = (n == -1) ? *last_dir : dir_preferred_order[n];\n            if (n != -1 && dir == *last_dir) // Already tried this direction?\n                continue;\n            ImVec2 pos;\n            if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)\n            if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right\n            if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left\n            if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left\n            if (!r_outer.Contains(ImRect(pos, pos + size)))\n                continue;\n            *last_dir = dir;\n            return pos;\n        }\n    }\n\n    // Tooltip and Default popup policy\n    // (Always first try the direction we used on the last frame, if any)\n    if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)\n    {\n        const ImGuiDir dir_preferred_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };\n        for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)\n        {\n            const ImGuiDir dir = (n == -1) ? *last_dir : dir_preferred_order[n];\n            if (n != -1 && dir == *last_dir) // Already tried this direction?\n                continue;\n\n            const float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);\n            const float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);\n\n            // If there's not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width)\n            if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))\n                continue;\n            if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))\n                continue;\n\n            ImVec2 pos;\n            pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;\n            pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;\n\n            // Clamp top-left corner of popup\n            pos.x = ImMax(pos.x, r_outer.Min.x);\n            pos.y = ImMax(pos.y, r_outer.Min.y);\n\n            *last_dir = dir;\n            return pos;\n        }\n    }\n\n    // Fallback when not enough room:\n    *last_dir = ImGuiDir_None;\n\n    // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.\n    if (policy == ImGuiPopupPositionPolicy_Tooltip)\n        return ref_pos + ImVec2(2, 2);\n\n    // Otherwise try to keep within display\n    ImVec2 pos = ref_pos;\n    pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);\n    pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);\n    return pos;\n}\n\n// Note that this is used for popups, which can overlap the non work-area of individual viewports.\nImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    ImRect r_screen;\n    if (window->ViewportAllowPlatformMonitorExtend >= 0)\n    {\n        // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here)\n        const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend];\n        r_screen.Min = monitor.WorkPos;\n        r_screen.Max = monitor.WorkPos + monitor.WorkSize;\n    }\n    else\n    {\n        // Use the full viewport area (not work area) for popups\n        r_screen = window->Viewport->GetMainRect();\n    }\n    ImVec2 padding = g.Style.DisplaySafeAreaPadding;\n    r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));\n    return r_screen;\n}\n\nImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n\n    ImRect r_outer = GetPopupAllowedExtentRect(window);\n    if (window->Flags & ImGuiWindowFlags_ChildMenu)\n    {\n        // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.\n        // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.\n        ImGuiWindow* parent_window = window->ParentWindow;\n        float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).\n        ImRect r_avoid;\n        if (parent_window->DC.MenuBarAppending)\n            r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field\n        else\n            r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);\n        return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);\n    }\n    if (window->Flags & ImGuiWindowFlags_Popup)\n    {\n        return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here\n    }\n    if (window->Flags & ImGuiWindowFlags_Tooltip)\n    {\n        // Position tooltip (always follows mouse + clamp within outer boundaries)\n        // FIXME:\n        // - Too many paths. One problem is that FindBestWindowPosForPopupEx() doesn't allow passing a suggested position (so touch screen path doesn't use it by default).\n        // - Drag and drop tooltips are not using this path either: BeginTooltipEx() manually sets their position.\n        // - Require some tidying up. In theory we could handle both cases in same location, but requires a bit of shuffling\n        //   as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin().\n        IM_ASSERT(g.CurrentWindow == window);\n        const float scale = g.Style.MouseCursorScale;\n        const ImVec2 ref_pos = NavCalcPreferredRefPos(ImGuiWindowFlags_Tooltip);\n\n        if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource(ImGuiWindowFlags_Tooltip) == ImGuiInputSource_Mouse)\n        {\n            ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);\n            if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size)))\n                return tooltip_pos;\n        }\n\n        ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;\n        ImRect r_avoid;\n        if (g.NavCursorVisible && g.NavHighlightItemUnderNav && !g.IO.ConfigNavMoveSetMousePos)\n            r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);\n        else\n            r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.\n        //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));\n\n        return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);\n    }\n    IM_ASSERT(0);\n    return window->Pos;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] WINDOW FOCUS\n//----------------------------------------------------------------------------\n// - SetWindowFocus()\n// - SetNextWindowFocus()\n// - IsWindowFocused()\n// - UpdateWindowInFocusOrderList() [Internal]\n// - BringWindowToFocusFront() [Internal]\n// - BringWindowToDisplayFront() [Internal]\n// - BringWindowToDisplayBack() [Internal]\n// - BringWindowToDisplayBehind() [Internal]\n// - FindWindowDisplayIndex() [Internal]\n// - FocusWindow() [Internal]\n// - FocusTopMostWindowUnderOne() [Internal]\n//-----------------------------------------------------------------------------\n\nvoid ImGui::SetWindowFocus()\n{\n    FocusWindow(GImGui->CurrentWindow);\n}\n\nvoid ImGui::SetWindowFocus(const char* name)\n{\n    if (name)\n    {\n        if (ImGuiWindow* window = FindWindowByName(name))\n            FocusWindow(window);\n    }\n    else\n    {\n        FocusWindow(NULL);\n    }\n}\n\nvoid ImGui::SetNextWindowFocus()\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasFocus;\n}\n\n// Similar to IsWindowHovered()\nbool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* ref_window = g.NavWindow;\n    ImGuiWindow* cur_window = g.CurrentWindow;\n\n    if (ref_window == NULL)\n        return false;\n    if (flags & ImGuiFocusedFlags_AnyWindow)\n        return true;\n\n    IM_ASSERT(cur_window); // Not inside a Begin()/End()\n    const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;\n    const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0;\n    if (flags & ImGuiFocusedFlags_RootWindow)\n        cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);\n\n    if (flags & ImGuiFocusedFlags_ChildWindows)\n        return IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);\n    else\n        return ref_window == cur_window;\n}\n\nstatic int ImGui::FindWindowFocusIndex(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    IM_UNUSED(g);\n    int order = window->FocusOrder;\n    IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking)\n    IM_ASSERT(g.WindowsFocusOrder[order] == window);\n    return order;\n}\n\nstatic void ImGui::UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)\n{\n    ImGuiContext& g = *GImGui;\n\n    const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0);\n    const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild;\n    if ((just_created || child_flag_changed) && !new_is_explicit_child)\n    {\n        IM_ASSERT(!g.WindowsFocusOrder.contains(window));\n        g.WindowsFocusOrder.push_back(window);\n        window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);\n    }\n    else if (!just_created && child_flag_changed && new_is_explicit_child)\n    {\n        IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window);\n        for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++)\n            g.WindowsFocusOrder[n]->FocusOrder--;\n        g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder);\n        window->FocusOrder = -1;\n    }\n    window->IsExplicitChild = new_is_explicit_child;\n}\n\nvoid ImGui::BringWindowToFocusFront(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(window == window->RootWindow);\n\n    const int cur_order = window->FocusOrder;\n    IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);\n    if (g.WindowsFocusOrder.back() == window)\n        return;\n\n    const int new_order = g.WindowsFocusOrder.Size - 1;\n    for (int n = cur_order; n < new_order; n++)\n    {\n        g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];\n        g.WindowsFocusOrder[n]->FocusOrder--;\n        IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);\n    }\n    g.WindowsFocusOrder[new_order] = window;\n    window->FocusOrder = (short)new_order;\n}\n\n// Note technically focus related but rather adjacent and close to BringWindowToFocusFront()\n// FIXME-FOCUS: Could opt-in/opt-out enable modal check like in FocusWindow().\nvoid ImGui::BringWindowToDisplayFront(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* current_front_window = g.Windows.back();\n    if (current_front_window == window || current_front_window->RootWindowDockTree == window) // Cheap early out (could be better)\n        return;\n    for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window\n        if (g.Windows[i] == window)\n        {\n            memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));\n            g.Windows[g.Windows.Size - 1] = window;\n            break;\n        }\n}\n\nvoid ImGui::BringWindowToDisplayBack(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.Windows[0] == window)\n        return;\n    for (int i = 0; i < g.Windows.Size; i++)\n        if (g.Windows[i] == window)\n        {\n            memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));\n            g.Windows[0] = window;\n            break;\n        }\n}\n\nvoid ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window)\n{\n    IM_ASSERT(window != NULL && behind_window != NULL);\n    ImGuiContext& g = *GImGui;\n    window = window->RootWindow;\n    behind_window = behind_window->RootWindow;\n    int pos_wnd = FindWindowDisplayIndex(window);\n    int pos_beh = FindWindowDisplayIndex(behind_window);\n    if (pos_wnd < pos_beh)\n    {\n        size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*);\n        memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes);\n        g.Windows[pos_beh - 1] = window;\n    }\n    else\n    {\n        size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*);\n        memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes);\n        g.Windows[pos_beh] = window;\n    }\n}\n\nint ImGui::FindWindowDisplayIndex(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    return g.Windows.index_from_ptr(g.Windows.find(window));\n}\n\n// Moving window to front of display and set focus (which happens to be back of our sorted list)\nvoid ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Modal check?\n    if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case.\n        if (ImGuiWindow* blocking_modal = FindBlockingModal(window))\n        {\n            // This block would typically be reached in two situations:\n            // - API call to FocusWindow() with a window under a modal and ImGuiFocusRequestFlags_UnlessBelowModal flag.\n            // - User clicking on void or anything behind a modal while a modal is open (window == NULL)\n            IMGUI_DEBUG_LOG_FOCUS(\"[focus] FocusWindow(\\\"%s\\\", UnlessBelowModal): prevented by \\\"%s\\\".\\n\", window ? window->Name : \"<NULL>\", blocking_modal->Name);\n            if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)\n                BringWindowToDisplayBehind(window, blocking_modal); // Still bring right under modal. (FIXME: Could move in focus list too?)\n            ClosePopupsOverWindow(GetTopMostPopupModal(), false); // Note how we need to use GetTopMostPopupModal() aad NOT blocking_modal, to handle nested modals\n            return;\n        }\n\n    // Find last focused child (if any) and focus it instead.\n    if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL)\n        window = NavRestoreLastChildNavWindow(window);\n\n    // Apply focus\n    if (g.NavWindow != window)\n    {\n        SetNavWindow(window);\n        if (window && g.NavHighlightItemUnderNav)\n            g.NavMousePosDirty = true;\n        g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId\n        g.NavLayer = ImGuiNavLayer_Main;\n        SetNavFocusScope(window ? window->NavRootFocusScopeId : 0);\n        g.NavIdIsAlive = false;\n        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;\n\n        // Close popups if any\n        ClosePopupsOverWindow(window, false);\n    }\n\n    // Move the root window to the top of the pile\n    IM_ASSERT(window == NULL || window->RootWindowDockTree != NULL);\n    ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL;\n    ImGuiWindow* display_front_window = window ? window->RootWindowDockTree : NULL;\n    ImGuiDockNode* dock_node = window ? window->DockNode : NULL;\n    bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow);\n\n    // Steal active widgets. Some of the cases it triggers includes:\n    // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.\n    // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)\n    // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window.\n    if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)\n        if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host)\n            ClearActiveID();\n\n    // Passing NULL allow to disable keyboard focus\n    if (!window)\n        return;\n    window->LastFrameJustFocused = g.FrameCount;\n\n    // Select in dock node\n    // For #2304 we avoid applying focus immediately before the tabbar is visible.\n    //if (dock_node && dock_node->TabBar)\n    //    dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId;\n\n    // Bring to front\n    BringWindowToFocusFront(focus_front_window);\n    if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)\n        BringWindowToDisplayFront(display_front_window);\n}\n\nvoid ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    int start_idx = g.WindowsFocusOrder.Size - 1;\n    if (under_this_window != NULL)\n    {\n        // Aim at root window behind us, if we are in a child window that's our own root (see #4640)\n        int offset = -1;\n        while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow)\n        {\n            under_this_window = under_this_window->ParentWindow;\n            offset = 0;\n        }\n        start_idx = FindWindowFocusIndex(under_this_window) + offset;\n    }\n    for (int i = start_idx; i >= 0; i--)\n    {\n        // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user.\n        ImGuiWindow* window = g.WindowsFocusOrder[i];\n        if (window == ignore_window || !window->WasActive)\n            continue;\n        if (filter_viewport != NULL && window->Viewport != filter_viewport)\n            continue;\n        if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))\n        {\n            // FIXME-DOCK: When ImGuiFocusRequestFlags_RestoreFocusedChild is set...\n            // This is failing (lagging by one frame) for docked windows.\n            // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B.\n            // We might leverage the tab order implicitly stored in window->DockNodeAsHost->TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update)\n            // to tell which is the \"previous\" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself?\n            FocusWindow(window, flags);\n            return;\n        }\n    }\n    FocusWindow(NULL, flags);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] KEYBOARD/GAMEPAD NAVIGATION\n//-----------------------------------------------------------------------------\n\n// FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked.\n// In our terminology those should be interchangeable, yet right now this is super confusing.\n// Those two functions are merely a legacy artifact, so at minimum naming should be clarified.\n\nvoid ImGui::SetNavCursorVisible(bool visible)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))\n        visible = false;\n    else if (g.IO.ConfigNavCursorVisibleAlways)\n        visible = true;\n    g.NavCursorVisible = visible;\n}\n\n// (was called NavRestoreHighlightAfterMove() before 1.91.4)\nvoid ImGui::SetNavCursorVisibleAfterMove()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))\n        g.NavCursorVisible = false;\n    else if (g.NavInputSource == ImGuiInputSource_Keyboard && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) == 0)\n        g.NavCursorVisible = false;\n    else if (g.NavInputSource == ImGuiInputSource_Gamepad && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)\n        g.NavCursorVisible = false;\n    else if (g.IO.ConfigNavCursorVisibleAuto)\n        g.NavCursorVisible = true;\n    g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;\n}\n\nvoid ImGui::SetNavWindow(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.NavWindow != window)\n    {\n        IMGUI_DEBUG_LOG_FOCUS(\"[focus] SetNavWindow(\\\"%s\\\")\\n\", window ? window->Name : \"<NULL>\");\n        g.NavWindow = window;\n        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;\n    }\n    g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;\n    NavUpdateAnyRequestFlag();\n}\n\nvoid ImGui::NavHighlightActivated(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    g.NavHighlightActivatedId = id;\n    g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER;\n}\n\nvoid ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)\n{\n    ImGuiContext& g = *GImGui;\n    g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX;\n}\n\nvoid ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.NavWindow != NULL);\n    IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);\n    g.NavId = id;\n    g.NavLayer = nav_layer;\n    SetNavFocusScope(focus_scope_id);\n    g.NavWindow->NavLastIds[nav_layer] = id;\n    g.NavWindow->NavRectRel[nav_layer] = rect_rel;\n\n    // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)\n    NavClearPreferredPosForAxis(ImGuiAxis_X);\n    NavClearPreferredPosForAxis(ImGuiAxis_Y);\n}\n\nvoid ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(id != 0);\n\n    if (g.NavWindow != window)\n       SetNavWindow(window);\n\n    // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid.\n    // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)\n    const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;\n    g.NavId = id;\n    g.NavLayer = nav_layer;\n    SetNavFocusScope(g.CurrentFocusScopeId);\n    window->NavLastIds[nav_layer] = id;\n    if (g.LastItemData.ID == id)\n        window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect);\n    g.NavIdItemFlags = (g.LastItemData.ID == id) ? g.LastItemData.ItemFlags : ImGuiItemFlags_None;\n    if (id == g.ActiveIdIsAlive)\n        g.NavIdIsAlive = true;\n\n    if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)\n        g.NavHighlightItemUnderNav = true;\n    else if (g.IO.ConfigNavCursorVisibleAuto)\n        g.NavCursorVisible = false;\n\n    // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)\n    NavClearPreferredPosForAxis(ImGuiAxis_X);\n    NavClearPreferredPosForAxis(ImGuiAxis_Y);\n}\n\nstatic ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)\n{\n    if (ImFabs(dx) > ImFabs(dy))\n        return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;\n    return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;\n}\n\nstatic float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max)\n{\n    if (cand_max < curr_min)\n        return cand_max - curr_min;\n    if (curr_max < cand_min)\n        return cand_min - curr_max;\n    return 0.0f;\n}\n\n// Scoring function for keyboard/gamepad directional navigation. Based on https://gist.github.com/rygorous/6981057\nstatic bool ImGui::NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (g.NavLayer != window->DC.NavLayerCurrent)\n        return false;\n\n    // FIXME: Those are not good variables names\n    ImRect cand = nav_bb;                   // Current item nav rectangle\n    const ImRect curr = g.NavScoringRect;   // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)\n    g.NavScoringDebugCount++;\n\n    // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring\n    if (window->ParentWindow == g.NavWindow)\n    {\n        IM_ASSERT((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened);\n        if (!window->ClipRect.Overlaps(cand))\n            return false;\n        cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window\n    }\n\n    // Compute distance between boxes\n    // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.\n    float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);\n    float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items\n    if (dby != 0.0f && dbx != 0.0f)\n        dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);\n    float dist_box = ImFabs(dbx) + ImFabs(dby);\n\n    // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter)\n    float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);\n    float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);\n    float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)\n\n    // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance\n    ImGuiDir quadrant;\n    float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;\n    if (dbx != 0.0f || dby != 0.0f)\n    {\n        // For non-overlapping boxes, use distance between boxes\n        // FIXME-NAV: Quadrant may be incorrect because of (1) dbx bias and (2) curr.Max.y bias applied by NavBiasScoringRect() where typically curr.Max.y==curr.Min.y\n        // One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir_Right, pressing Left to navigate from Close to Collapse tends to fail.\n        // Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty.\n        dax = dbx;\n        day = dby;\n        dist_axial = dist_box;\n        quadrant = ImGetDirQuadrantFromDelta(dbx, dby);\n    }\n    else if (dcx != 0.0f || dcy != 0.0f)\n    {\n        // For overlapping boxes with different centers, use distance between centers\n        dax = dcx;\n        day = dcy;\n        dist_axial = dist_center;\n        quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);\n    }\n    else\n    {\n        // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter)\n        quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;\n    }\n\n    const ImGuiDir move_dir = g.NavMoveDir;\n#if IMGUI_DEBUG_NAV_SCORING\n    char buf[200];\n    if (g.IO.KeyCtrl) // Hold Ctrl to preview score in matching quadrant. Ctrl+Arrow to rotate.\n    {\n        if (quadrant == move_dir)\n        {\n            ImFormatString(buf, IM_COUNTOF(buf), \"%.0f/%.0f\", dist_box, dist_center);\n            ImDrawList* draw_list = GetForegroundDrawList(window);\n            draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80));\n            draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200));\n            draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);\n        }\n    }\n    const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max);\n    const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space));\n    if (debug_hovering || debug_tty)\n    {\n        ImFormatString(buf, IM_COUNTOF(buf),\n            \"d-box    (%7.3f,%7.3f) -> %7.3f\\nd-center (%7.3f,%7.3f) -> %7.3f\\nd-axial  (%7.3f,%7.3f) -> %7.3f\\nnav %c, quadrant %c\",\n            dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, \"-WENS\"[move_dir+1], \"-WENS\"[quadrant+1]);\n        if (debug_hovering)\n        {\n            ImDrawList* draw_list = GetForegroundDrawList(window);\n            draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));\n            draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));\n            draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200));\n            draw_list->AddText(cand.Max, ~0U, buf);\n        }\n        if (debug_tty) { IMGUI_DEBUG_LOG_NAV(\"id 0x%08X\\n%s\\n\", g.LastItemData.ID, buf); }\n    }\n#endif\n\n    // Is it in the quadrant we're interested in moving to?\n    bool new_best = false;\n    if (quadrant == move_dir)\n    {\n        // Does it beat the current best candidate?\n        if (dist_box < result->DistBox)\n        {\n            result->DistBox = dist_box;\n            result->DistCenter = dist_center;\n            return true;\n        }\n        if (dist_box == result->DistBox)\n        {\n            // Try using distance between center points to break ties\n            if (dist_center < result->DistCenter)\n            {\n                result->DistCenter = dist_center;\n                new_best = true;\n            }\n            else if (dist_center == result->DistCenter)\n            {\n                // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving \"later\" items\n                // (with higher index) to the right/downwards by an infinitesimal amount since we the current \"best\" button already (so it must have a lower index),\n                // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis.\n                if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance\n                    new_best = true;\n            }\n        }\n    }\n\n    // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no \"real\" matches\n    // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)\n    // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too.\n    // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward.\n    // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?\n    if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match\n        if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))\n            if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f))\n            {\n                result->DistAxial = dist_axial;\n                new_best = true;\n            }\n\n    return new_best;\n}\n\nstatic void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    result->Window = window;\n    result->ID = g.LastItemData.ID;\n    result->FocusScopeId = g.CurrentFocusScopeId;\n    result->ItemFlags = g.LastItemData.ItemFlags;\n    result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);\n    if (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData)\n    {\n        IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);\n        result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.\n    }\n}\n\n// True when current work location may be scrolled horizontally when moving left / right.\n// This is generally always true UNLESS within a column. We don't have a vertical equivalent.\nvoid ImGui::NavUpdateCurrentWindowIsScrollPushableX()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL);\n}\n\n// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)\n// This is called after LastItemData is set, but NextItemData is also still valid.\nstatic void ImGui::NavProcessItem()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    const ImGuiID id = g.LastItemData.ID;\n    const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags;\n\n    // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221, #8816)\n    ImRect nav_bb = g.LastItemData.NavRect;\n    if (window->DC.NavIsScrollPushableX == false)\n    {\n        nav_bb.Min.x = ImClamp(nav_bb.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);\n        nav_bb.Max.x = ImClamp(nav_bb.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);\n    }\n\n    // Process Init Request\n    if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0)\n    {\n        // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback\n        const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0;\n        if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0)\n        {\n            NavApplyItemToResult(&g.NavInitResult);\n        }\n        if (candidate_for_nav_default_focus)\n        {\n            g.NavInitRequest = false; // Found a match, clear request\n            NavUpdateAnyRequestFlag();\n        }\n    }\n\n    // Process Move Request (scoring for navigation)\n    // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)\n    if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)\n    {\n        if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0)\n        {\n            const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;\n            if (is_tabbing)\n            {\n                NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);\n            }\n            else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))\n            {\n                ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;\n                if (NavScoreItem(result, nav_bb))\n                    NavApplyItemToResult(result);\n\n                // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.\n                const float VISIBLE_RATIO = 0.70f;\n                if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)\n                {\n                    const ImRect& r = window->InnerRect; // window->ClipRect\n                    if (r.Overlaps(nav_bb))\n                        if (ImClamp(nav_bb.Max.y, r.Min.y, r.Max.y) - ImClamp(nav_bb.Min.y, r.Min.y, r.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)\n                            if (NavScoreItem(&g.NavMoveResultLocalVisible, nav_bb))\n                                NavApplyItemToResult(&g.NavMoveResultLocalVisible);\n                }\n            }\n        }\n    }\n\n    // Update information for currently focused/navigated item\n    if (g.NavId == id)\n    {\n        if (g.NavWindow != window)\n            SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.\n        g.NavLayer = window->DC.NavLayerCurrent;\n        SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath\n        g.NavFocusScopeId = g.CurrentFocusScopeId;\n        g.NavIdIsAlive = true;\n        g.NavIdItemFlags = item_flags;\n        if (g.LastItemData.ItemFlags & ImGuiItemFlags_HasSelectionUserData)\n        {\n            IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);\n            g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.\n        }\n        window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position)\n    }\n}\n\n// Handle \"scoring\" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest().\n// Note that SetKeyboardFocusHere() API calls are considered tabbing requests!\n// - Case 1: no nav/active id:    set result to first eligible item, stop storing.\n// - Case 2: tab forward:         on ref id set counter, on counter elapse store result\n// - Case 3: tab forward wrap:    set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request\n// - Case 4: tab backward:        store all results, on ref id pick prev, stop storing\n// - Case 5: tab backward wrap:   store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested\nvoid ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags)\n{\n    ImGuiContext& g = *GImGui;\n\n    if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)\n    {\n        if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)\n            return;\n        if (g.NavFocusScopeId != g.CurrentFocusScopeId)\n            return;\n    }\n\n    // - Can always land on an item when using API call.\n    // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.\n    // - Tabbing without _NavEnableKeyboard: goes through inputable items only.\n    bool can_stop;\n    if (move_flags & ImGuiNavMoveFlags_FocusApi)\n        can_stop = true;\n    else\n        can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable));\n\n    // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)\n    ImGuiNavItemData* result = &g.NavMoveResultLocal;\n    if (g.NavTabbingDir == +1)\n    {\n        // Tab Forward or SetKeyboardFocusHere() with >= 0\n        if (can_stop && g.NavTabbingResultFirst.ID == 0)\n            NavApplyItemToResult(&g.NavTabbingResultFirst);\n        if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0)\n            NavMoveRequestResolveWithLastItem(result);\n        else if (g.NavId == id)\n            g.NavTabbingCounter = 1;\n    }\n    else if (g.NavTabbingDir == -1)\n    {\n        // Tab Backward\n        if (g.NavId == id)\n        {\n            if (result->ID)\n            {\n                g.NavMoveScoringItems = false;\n                NavUpdateAnyRequestFlag();\n            }\n        }\n        else if (can_stop)\n        {\n            // Keep applying until reaching NavId\n            NavApplyItemToResult(result);\n        }\n    }\n    else if (g.NavTabbingDir == 0)\n    {\n        if (can_stop && g.NavId == id)\n            NavMoveRequestResolveWithLastItem(result);\n        if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init\n            NavApplyItemToResult(&g.NavTabbingResultFirst);\n    }\n}\n\nbool ImGui::NavMoveRequestButNoResultYet()\n{\n    ImGuiContext& g = *GImGui;\n    return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;\n}\n\n// FIXME: ScoringRect is not set\nvoid ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.NavWindow != NULL);\n    //IMGUI_DEBUG_LOG_NAV(\"[nav] NavMoveRequestSubmit: dir %c, window \\\"%s\\\"\\n\", \"-WENS\"[move_dir + 1], g.NavWindow->Name);\n\n    if (move_flags & ImGuiNavMoveFlags_IsTabbing)\n        move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;\n\n    g.NavMoveSubmitted = g.NavMoveScoringItems = true;\n    g.NavMoveDir = move_dir;\n    g.NavMoveDirForDebug = move_dir;\n    g.NavMoveClipDir = clip_dir;\n    g.NavMoveFlags = move_flags;\n    g.NavMoveScrollFlags = scroll_flags;\n    g.NavMoveForwardToNextFrame = false;\n    g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods;\n    g.NavMoveResultLocal.Clear();\n    g.NavMoveResultLocalVisible.Clear();\n    g.NavMoveResultOther.Clear();\n    g.NavTabbingCounter = 0;\n    g.NavTabbingResultFirst.Clear();\n    NavUpdateAnyRequestFlag();\n}\n\nvoid ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)\n{\n    ImGuiContext& g = *GImGui;\n    g.NavMoveScoringItems = false; // Ensure request doesn't need more processing\n    NavApplyItemToResult(result);\n    NavUpdateAnyRequestFlag();\n}\n\n// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsToParent\nvoid ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data)\n{\n    ImGuiContext& g = *GImGui;\n    g.NavMoveScoringItems = false;\n    g.LastItemData.ID = tree_node_data->ID;\n    g.LastItemData.ItemFlags = tree_node_data->ItemFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).\n    g.LastItemData.NavRect = tree_node_data->NavRect;\n    NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()\n    NavClearPreferredPosForAxis(ImGuiAxis_Y);\n    NavUpdateAnyRequestFlag();\n}\n\nvoid ImGui::NavMoveRequestCancel()\n{\n    ImGuiContext& g = *GImGui;\n    g.NavMoveSubmitted = g.NavMoveScoringItems = false;\n    NavUpdateAnyRequestFlag();\n}\n\n// Forward will reuse the move request again on the next frame (generally with modifications done to it)\nvoid ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.NavMoveForwardToNextFrame == false);\n    NavMoveRequestCancel();\n    g.NavMoveForwardToNextFrame = true;\n    g.NavMoveDir = move_dir;\n    g.NavMoveClipDir = clip_dir;\n    g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;\n    g.NavMoveScrollFlags = scroll_flags;\n}\n\n// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire\n// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.\nvoid ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY\n\n    // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it:\n    // as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest().\n    if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == window->DC.NavLayerCurrent)\n        g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags;\n}\n\n// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).\n// This way we could find the last focused window among our children. It would be much less confusing this way?\nstatic void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)\n{\n    ImGuiWindow* parent = nav_window;\n    while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)\n        parent = parent->ParentWindow;\n    if (parent && parent != nav_window)\n        parent->NavLastChildNavWindow = nav_window;\n}\n\n// Restore the last focused child.\n// Call when we are expected to land on the Main Layer (0) after FocusWindow()\nstatic ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)\n{\n    if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)\n        return window->NavLastChildNavWindow;\n    if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar)\n        if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(window->DockNodeAsHost->TabBar))\n            return tab->Window;\n    return window;\n}\n\nvoid ImGui::NavRestoreLayer(ImGuiNavLayer layer)\n{\n    ImGuiContext& g = *GImGui;\n    if (layer == ImGuiNavLayer_Main)\n    {\n        ImGuiWindow* prev_nav_window = g.NavWindow;\n        g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow);    // FIXME-NAV: Should clear ongoing nav requests?\n        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;\n        if (prev_nav_window)\n            IMGUI_DEBUG_LOG_FOCUS(\"[focus] NavRestoreLayer: from \\\"%s\\\" to SetNavWindow(\\\"%s\\\")\\n\", prev_nav_window->Name, g.NavWindow->Name);\n    }\n    ImGuiWindow* window = g.NavWindow;\n    if (window->NavLastIds[layer] != 0)\n    {\n        SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);\n    }\n    else\n    {\n        g.NavLayer = layer;\n        NavInitWindow(window, true);\n    }\n}\n\nstatic inline void ImGui::NavUpdateAnyRequestFlag()\n{\n    ImGuiContext& g = *GImGui;\n    g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);\n    if (g.NavAnyRequest)\n        IM_ASSERT(g.NavWindow != NULL);\n}\n\n// This needs to be called before we submit any widget (aka in or before Begin)\nvoid ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)\n{\n    // FIXME: ChildWindow test here is wrong for docking\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(window == g.NavWindow);\n\n    if (window->Flags & ImGuiWindowFlags_NoNavInputs)\n    {\n        g.NavId = 0;\n        SetNavFocusScope(window->NavRootFocusScopeId);\n        return;\n    }\n\n    bool init_for_nav = false;\n    if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)\n        init_for_nav = true;\n    IMGUI_DEBUG_LOG_NAV(\"[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\\\"%s\\\", layer=%d\\n\", init_for_nav, window->Name, g.NavLayer);\n    if (init_for_nav)\n    {\n        SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect());\n        g.NavInitRequest = true;\n        g.NavInitRequestFromMove = false;\n        g.NavInitResult.ID = 0;\n        NavUpdateAnyRequestFlag();\n    }\n    else\n    {\n        g.NavId = window->NavLastIds[0];\n        SetNavFocusScope(window->NavRootFocusScopeId);\n    }\n}\n\n// Positioning logic altered slightly for remote activation: for Popup we want to use item rect, for Tooltip we leave things alone. (#9138)\n// When calling for ImGuiWindowFlags_Popup we use LastItemData.\nstatic ImGuiInputSource ImGui::NavCalcPreferredRefPosSource(ImGuiWindowFlags window_type)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.NavWindow;\n\n    const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;\n    if ((window_type & ImGuiWindowFlags_Popup) && activated_shortcut)\n        return ImGuiInputSource_Keyboard;\n\n    if (!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window)\n        return ImGuiInputSource_Mouse;\n    else\n        return ImGuiInputSource_Keyboard; // or Nav in general\n}\n\nstatic ImVec2 ImGui::NavCalcPreferredRefPos(ImGuiWindowFlags window_type)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.NavWindow;\n    ImGuiInputSource source = NavCalcPreferredRefPosSource(window_type);\n\n    if (source == ImGuiInputSource_Mouse)\n    {\n        // Mouse (we need a fallback in case the mouse becomes invalid after being used)\n        // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard.\n        // In theory we could move that +1.0f offset in OpenPopupEx()\n        ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos;\n        return ImVec2(p.x + 1.0f, p.y);\n    }\n    else\n    {\n        // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item\n        const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;\n        ImRect ref_rect;\n        if (activated_shortcut && (window_type & ImGuiWindowFlags_Popup))\n            ref_rect = g.LastItemData.NavRect;\n        else if (window != NULL)\n            ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);\n\n        // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)\n        if (window != NULL && window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))\n        {\n            ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);\n            ref_rect.Translate(window->Scroll - next_scroll);\n        }\n        ImVec2 pos = ImVec2(ref_rect.Min.x + ImMin(g.Style.FramePadding.x * 4, ref_rect.GetWidth()), ref_rect.Max.y - ImMin(g.Style.FramePadding.y, ref_rect.GetHeight()));\n        if (window != NULL)\n            if (ImGuiViewport* viewport = window->Viewport)\n                pos = ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size);\n        return ImTrunc(pos); // ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.\n    }\n}\n\nfloat ImGui::GetNavTweakPressedAmount(ImGuiAxis axis)\n{\n    ImGuiContext& g = *GImGui;\n    float repeat_delay, repeat_rate;\n    GetTypematicRepeatRate(ImGuiInputFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate);\n\n    ImGuiKey key_less, key_more;\n    if (g.NavInputSource == ImGuiInputSource_Gamepad)\n    {\n        key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp;\n        key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown;\n    }\n    else\n    {\n        key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow;\n        key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow;\n    }\n    float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate);\n    if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase\n        amount = 0.0f;\n    return amount;\n}\n\nstatic void ImGui::NavUpdate()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    io.WantSetMousePos = false;\n    //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG_NAV(\"[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\\n\", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : \"NULL\", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);\n\n    // Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)\n    // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?\n    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;\n    const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };\n    if (nav_gamepad_active)\n        for (ImGuiKey key : nav_gamepad_keys_to_change_source)\n            if (IsKeyDown(key))\n                g.NavInputSource = ImGuiInputSource_Gamepad;\n    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n    const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };\n    if (nav_keyboard_active)\n        for (ImGuiKey key : nav_keyboard_keys_to_change_source)\n            if (IsKeyDown(key))\n                g.NavInputSource = ImGuiInputSource_Keyboard;\n\n    // Process navigation init request (select first/default focus)\n    g.NavJustMovedToId = 0;\n    g.NavJustMovedToFocusScopeId = g.NavJustMovedFromFocusScopeId = 0;\n    if (g.NavInitResult.ID != 0)\n        NavInitRequestApplyResult();\n    g.NavInitRequest = false;\n    g.NavInitRequestFromMove = false;\n    g.NavInitResult.ID = 0;\n\n    // Process navigation move request\n    if (g.NavMoveSubmitted)\n        NavMoveRequestApplyResult();\n    g.NavTabbingCounter = 0;\n    g.NavMoveSubmitted = g.NavMoveScoringItems = false;\n    if (g.NavCursorHideFrames > 0)\n        if (--g.NavCursorHideFrames == 0)\n            g.NavCursorVisible = true;\n\n    // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)\n    bool set_mouse_pos = false;\n    if (g.NavMousePosDirty && g.NavIdIsAlive)\n        if (g.NavCursorVisible && g.NavHighlightItemUnderNav && g.NavWindow)\n            set_mouse_pos = true;\n    g.NavMousePosDirty = false;\n    IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);\n\n    // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0\n    if (g.NavWindow)\n        NavSaveLastChildNavWindowIntoParent(g.NavWindow);\n    if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)\n        g.NavWindow->NavLastChildNavWindow = NULL;\n\n    // Update Ctrl+Tab and Windowing features (hold Square to move/resize/etc.)\n    NavUpdateWindowing();\n\n    // Set output flags for user application\n    io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);\n    io.NavVisible = (io.NavActive && g.NavId != 0 && g.NavCursorVisible) || (g.NavWindowingTarget != NULL);\n\n    // Process NavCancel input (to close a popup, get back to parent, clear focus)\n    NavUpdateCancelRequest();\n    NavUpdateContextMenuRequest();\n\n    // Process manual activation request\n    g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0;\n    g.NavActivateFlags = ImGuiActivateFlags_None;\n    if (g.NavId != 0 && g.NavCursorVisible && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))\n    {\n        const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner));\n        const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner)));\n        const bool input_pressed_keyboard = nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, 0, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_KeypadEnter, 0, ImGuiKeyOwner_NoOwner));\n        bool input_pressed_gamepad = false;\n        if (activate_down && nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner) && (g.NavIdItemFlags & ImGuiItemFlags_Inputable)) // requires ImGuiItemFlags_Inputable to avoid retriggering regular buttons.\n            if (GetKeyData(ImGuiKey_NavGamepadActivate)->DownDurationPrev < NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY && GetKeyData(ImGuiKey_NavGamepadActivate)->DownDuration >= NAV_ACTIVATE_INPUT_WITH_GAMEPAD_DELAY)\n                input_pressed_gamepad = true;\n\n        if (g.ActiveId == 0 && activate_pressed)\n        {\n            g.NavActivateId = g.NavId;\n            g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;\n        }\n        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (input_pressed_keyboard || input_pressed_gamepad))\n        {\n            g.NavActivateId = g.NavId;\n            g.NavActivateFlags = ImGuiActivateFlags_PreferInput;\n        }\n        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_pressed_keyboard || input_pressed_gamepad)) // FIXME-NAV: Unsure why input_pressed_xxx (migrated from input_down which was already dubious)\n            g.NavActivateDownId = g.NavId;\n        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed_keyboard || input_pressed_gamepad))\n        {\n            g.NavActivatePressedId = g.NavId;\n            NavHighlightActivated(g.NavId);\n        }\n    }\n    if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))\n        g.NavCursorVisible = false;\n    else if (g.IO.ConfigNavCursorVisibleAlways && g.NavCursorHideFrames == 0)\n        g.NavCursorVisible = true;\n    if (g.NavActivateId != 0)\n        IM_ASSERT(g.NavActivateDownId == g.NavActivateId);\n\n    // Highlight\n    if (g.NavHighlightActivatedTimer > 0.0f)\n        g.NavHighlightActivatedTimer = ImMax(0.0f, g.NavHighlightActivatedTimer - io.DeltaTime);\n    if (g.NavHighlightActivatedTimer == 0.0f)\n        g.NavHighlightActivatedId = 0;\n\n    // Process programmatic activation request\n    // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)\n    if (g.NavNextActivateId != 0)\n    {\n        g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;\n        g.NavActivateFlags = g.NavNextActivateFlags;\n    }\n    g.NavNextActivateId = 0;\n\n    // Process move requests\n    NavUpdateCreateMoveRequest();\n    if (g.NavMoveDir == ImGuiDir_None)\n        NavUpdateCreateTabbingRequest();\n    NavUpdateAnyRequestFlag();\n    g.NavIdIsAlive = false;\n\n    // Scrolling\n    if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)\n    {\n        // *Fallback* manual-scroll with Nav directional keys when window has no navigable item\n        ImGuiWindow* window = g.NavWindow;\n        const float scroll_speed = IM_ROUND(window->FontRefSize * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.\n        const ImGuiDir move_dir = g.NavMoveDir;\n        if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None)\n        {\n            if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)\n                SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));\n            if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)\n                SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));\n        }\n\n        // *Normal* Manual scroll with LStick\n        // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.\n        if (nav_gamepad_active)\n        {\n            const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);\n            const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;\n            if (scroll_dir.x != 0.0f && window->ScrollbarX)\n                SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));\n            if (scroll_dir.y != 0.0f)\n                SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));\n        }\n    }\n\n    // Always prioritize mouse highlight if navigation is disabled\n    if (!nav_keyboard_active && !nav_gamepad_active)\n    {\n        g.NavCursorVisible = false;\n        g.NavHighlightItemUnderNav = set_mouse_pos = false;\n    }\n\n    // Update mouse position if requested\n    // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied)\n    if (set_mouse_pos && io.ConfigNavMoveSetMousePos && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))\n        TeleportMousePos(NavCalcPreferredRefPos(ImGuiWindowFlags_Popup));\n\n    // [DEBUG]\n    g.NavScoringDebugCount = 0;\n#if IMGUI_DEBUG_NAV_RECTS\n    if (ImGuiWindow* debug_window = g.NavWindow)\n    {\n        ImDrawList* draw_list = GetForegroundDrawList(debug_window);\n        int layer = g.NavLayer; /* for (int layer = 0; layer < 2; layer++)*/ { ImRect r = WindowRectRelToAbs(debug_window, debug_window->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 200, 0, 255)); }\n        //if (1) { ImU32 col = (!debug_window->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, \"%d\", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }\n    }\n#endif\n}\n\nvoid ImGui::NavInitRequestApplyResult()\n{\n    // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void)\n    ImGuiContext& g = *GImGui;\n    if (!g.NavWindow)\n        return;\n\n    ImGuiNavItemData* result = &g.NavInitResult;\n    if (g.NavId != result->ID)\n    {\n        g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;\n        g.NavJustMovedToId = result->ID;\n        g.NavJustMovedToFocusScopeId = result->FocusScopeId;\n        g.NavJustMovedToKeyMods = 0;\n        g.NavJustMovedToIsTabbing = false;\n        g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;\n    }\n\n    // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)\n    // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.\n    IMGUI_DEBUG_LOG_NAV(\"[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \\\"%s\\\"\\n\", result->ID, g.NavLayer, g.NavWindow->Name);\n    SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);\n    g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result\n    if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)\n        g.NavLastValidSelectionUserData = result->SelectionUserData;\n    if (g.NavInitRequestFromMove)\n        SetNavCursorVisibleAfterMove();\n}\n\n// Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position\nstatic void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags)\n{\n    // Bias initial rect\n    ImGuiContext& g = *GImGui;\n    const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos;\n\n    // Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.\n    // - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.\n    // - But each successful move sets new bias on one axis, only cleared when using mouse.\n    if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0)\n    {\n        if (preferred_pos_rel.x == FLT_MAX)\n            preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x;\n        if (preferred_pos_rel.y == FLT_MAX)\n            preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y;\n    }\n\n    // Apply general bias on the other axis\n    if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX)\n        r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x;\n    else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX)\n        r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y;\n}\n\nvoid ImGui::NavUpdateCreateMoveRequest()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n    ImGuiWindow* window = g.NavWindow;\n    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;\n    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n\n    if (g.NavMoveForwardToNextFrame && window != NULL)\n    {\n        // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)\n        // (preserve most state, which were already set by the NavMoveRequestForward() function)\n        IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);\n        IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);\n        IMGUI_DEBUG_LOG_NAV(\"[nav] NavMoveRequestForward %d\\n\", g.NavMoveDir);\n    }\n    else\n    {\n        // Initiate directional inputs request\n        g.NavMoveDir = ImGuiDir_None;\n        g.NavMoveFlags = ImGuiNavMoveFlags_None;\n        g.NavMoveScrollFlags = ImGuiScrollFlags_None;\n        if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))\n        {\n            const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove;\n            if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft,  repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow,  repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Left; }\n            if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Right; }\n            if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp,    repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow,    repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Up; }\n            if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown,  repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow,  repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Down; }\n        }\n        g.NavMoveClipDir = g.NavMoveDir;\n        g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);\n    }\n\n    // Update PageUp/PageDown/Home/End scroll\n    // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?\n    float scoring_page_offset_y = 0.0f;\n    if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)\n        scoring_page_offset_y = NavUpdatePageUpPageDown();\n\n    // [DEBUG] Always send a request when holding Ctrl. Hold Ctrl + Arrow change the direction.\n#if IMGUI_DEBUG_NAV_SCORING\n    //if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C))\n    //    g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);\n    if (io.KeyCtrl)\n    {\n        if (g.NavMoveDir == ImGuiDir_None)\n            g.NavMoveDir = g.NavMoveDirForDebug;\n        g.NavMoveClipDir = g.NavMoveDir;\n        g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;\n    }\n#endif\n\n    // Submit\n    g.NavMoveForwardToNextFrame = false;\n    if (g.NavMoveDir != ImGuiDir_None)\n        NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags);\n\n    // Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match)\n    if (g.NavMoveSubmitted && g.NavId == 0)\n    {\n        IMGUI_DEBUG_LOG_NAV(\"[nav] NavInitRequest: from move, window \\\"%s\\\", layer=%d\\n\", window ? window->Name : \"<NULL>\", g.NavLayer);\n        g.NavInitRequest = g.NavInitRequestFromMove = true;\n        g.NavInitResult.ID = 0;\n        if (g.IO.ConfigNavCursorVisibleAuto) // NO check for _NoNavInputs here as we assume MoveRequests cannot be created.\n            g.NavCursorVisible = true;\n    }\n\n    // When using gamepad, we project the reference nav bounding box into window visible area.\n    // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling,\n    // since with gamepad all movements are relative (can't focus a visible object like we can with the mouse).\n    if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))\n    {\n        bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;\n        bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;\n        ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)));\n\n        // Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171)\n        // Otherwise 'inner_rect_rel' would be off on the move result frame.\n        inner_rect_rel.Translate(CalcNextScrollFromScrollTargetAndClamp(window) - window->Scroll);\n\n        if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer]))\n        {\n            IMGUI_DEBUG_LOG_NAV(\"[nav] NavMoveRequest: clamp NavRectRel for gamepad move\\n\");\n            float pad_x = ImMin(inner_rect_rel.GetWidth(), window->FontRefSize * 0.5f);\n            float pad_y = ImMin(inner_rect_rel.GetHeight(), window->FontRefSize * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item\n            inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX;\n            inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX;\n            inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX;\n            inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX;\n            window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel);\n            g.NavId = 0;\n        }\n    }\n\n    // Prepare scoring rectangle.\n    // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)\n    ImRect scoring_rect;\n    if (window != NULL)\n    {\n        ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);\n        scoring_rect = WindowRectRelToAbs(window, nav_rect_rel);\n\n        if (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)\n        {\n            // When we start from a visible location, score visible items and prioritize this result.\n            if (window->InnerRect.Contains(scoring_rect))\n                g.NavMoveFlags |= ImGuiNavMoveFlags_AlsoScoreVisibleSet;\n            g.NavScoringNoClipRect = scoring_rect;\n            scoring_rect.TranslateY(scoring_page_offset_y);\n            g.NavScoringNoClipRect.Add(scoring_rect);\n        }\n\n        //GetForegroundDrawList()->AddRectFilled(scoring_rect.Min - ImVec2(1, 1), scoring_rect.Max + ImVec2(1, 1), IM_COL32(255, 100, 0, 80)); // [DEBUG] Pre-bias\n        if (g.NavMoveSubmitted)\n            NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags);\n        IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().\n        //GetForegroundDrawList()->AddRectFilled(scoring_rect.Min - ImVec2(1, 1), scoring_rect.Max + ImVec2(1, 1), IM_COL32(255, 100, 0, 80)); // [DEBUG] Post-bias\n        //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRectFilled(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(100, 255, 0, 80)); } // [DEBUG]\n    }\n    g.NavScoringRect = scoring_rect;\n    //g.NavScoringNoClipRect.Add(scoring_rect);\n}\n\nvoid ImGui::NavUpdateCreateTabbingRequest()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.NavWindow;\n    IM_ASSERT(g.NavMoveDir == ImGuiDir_None);\n    if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs) || !g.ConfigNavEnableTabbing)\n        return;\n\n    const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt;\n    if (!tab_pressed)\n        return;\n\n    // Initiate tabbing request\n    // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)\n    // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.\n    const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n    if (nav_keyboard_active)\n        g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavCursorVisible == false && g.ActiveId == 0) ? 0 : +1;\n    else\n        g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;\n    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;\n    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;\n    ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;\n    NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.\n    g.NavTabbingCounter = -1;\n}\n\n// Apply result from previous frame navigation directional move request. Always called from NavUpdate()\nvoid ImGui::NavMoveRequestApplyResult()\n{\n    ImGuiContext& g = *GImGui;\n#if IMGUI_DEBUG_NAV_SCORING\n    if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times\n        return;\n#endif\n\n    // Select which result to use\n    ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;\n\n    // Tabbing forward wrap\n    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL)\n        if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)\n            result = &g.NavTabbingResultFirst;\n\n    // In a situation when there are no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)\n    const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;\n    if (result == NULL)\n    {\n        if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)\n            g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavCursorVisible;\n        if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)\n            SetNavCursorVisibleAfterMove();\n        NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.\n        IMGUI_DEBUG_LOG_NAV(\"[nav] NavMoveSubmitted but not led to a result!\\n\");\n        return;\n    }\n\n    // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.\n    if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)\n        if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)\n            result = &g.NavMoveResultLocalVisible;\n\n    // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.\n    if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)\n        if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))\n            result = &g.NavMoveResultOther;\n    IM_ASSERT(g.NavWindow && result->Window);\n\n    // Scroll to keep newly navigated item fully into view.\n    if (g.NavLayer == ImGuiNavLayer_Main)\n    {\n        ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel);\n        ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);\n\n        if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)\n        {\n            // FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge?\n            float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;\n            SetScrollY(result->Window, scroll_target);\n        }\n    }\n\n    if (g.NavWindow != result->Window)\n    {\n        IMGUI_DEBUG_LOG_FOCUS(\"[focus] NavMoveRequest: SetNavWindow(\\\"%s\\\")\\n\", result->Window->Name);\n        g.NavWindow = result->Window;\n        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;\n    }\n\n    // Clear active id unless requested not to\n    // FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction,\n    // so this is mostly provided as a gateway for further experiments (see #1418, #2890)\n    if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0)\n        ClearActiveID();\n\n    // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)\n    // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.\n    if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0)\n    {\n        g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;\n        g.NavJustMovedToId = result->ID;\n        g.NavJustMovedToFocusScopeId = result->FocusScopeId;\n        g.NavJustMovedToKeyMods = g.NavMoveKeyMods;\n        g.NavJustMovedToIsTabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;\n        g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;\n        //IMGUI_DEBUG_LOG_NAV(\"[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\\n\", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId);\n    }\n\n    // Apply new NavID/Focus\n    IMGUI_DEBUG_LOG_NAV(\"[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \\\"%s\\\"\\n\", result->ID, g.NavLayer, g.NavWindow->Name);\n    ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];\n    SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);\n    if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)\n        g.NavLastValidSelectionUserData = result->SelectionUserData;\n\n    // Restore last preferred position for current axis\n    // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)\n    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0)\n    {\n        preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];\n        g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;\n    }\n\n    // Tabbing: Activates Inputable, otherwise only Focus\n    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->ItemFlags & ImGuiItemFlags_Inputable) == 0)\n        g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;\n\n    // Activate\n    if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)\n    {\n        g.NavNextActivateId = result->ID;\n        g.NavNextActivateFlags = ImGuiActivateFlags_None;\n        if (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi)\n            g.NavNextActivateFlags |= ImGuiActivateFlags_FromFocusApi;\n        if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)\n            g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;\n    }\n\n    // Make nav cursor visible\n    if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)\n        SetNavCursorVisibleAfterMove();\n}\n\n// Process Escape/NavCancel input (to close a popup, get back to parent, clear focus)\n// FIXME: In order to support e.g. Escape to clear a selection we'll need:\n// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.\n// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept\nstatic void ImGui::NavUpdateCancelRequest()\n{\n    ImGuiContext& g = *GImGui;\n    const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;\n    const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n    if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, 0, ImGuiKeyOwner_NoOwner)))\n        return;\n\n    IMGUI_DEBUG_LOG_NAV(\"[nav] NavUpdateCancelRequest()\\n\");\n    if (g.ActiveId != 0)\n    {\n        ClearActiveID();\n    }\n    else if (g.NavLayer != ImGuiNavLayer_Main)\n    {\n        // Leave the \"menu\" layer\n        NavRestoreLayer(ImGuiNavLayer_Main);\n        SetNavCursorVisibleAfterMove();\n    }\n    else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow)\n    {\n        // Exit child window\n        ImGuiWindow* child_window = g.NavWindow->RootWindowForNav;\n        ImGuiWindow* parent_window = child_window->ParentWindow;\n        IM_ASSERT(child_window->ChildId != 0);\n        FocusWindow(parent_window);\n        SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect()));\n        SetNavCursorVisibleAfterMove();\n    }\n    else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))\n    {\n        // Close open popup/menu\n        ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);\n    }\n    else\n    {\n        // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were\n        // FIXME-NAV: This should happen on window appearing.\n        if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)\n            if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup)))// || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))\n                g.NavWindow->NavLastIds[0] = 0;\n\n        // Clear nav focus\n        if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)\n            g.NavId = 0;\n        if (g.IO.ConfigNavEscapeClearFocusWindow)\n            FocusWindow(NULL);\n    }\n}\n\nstatic void ImGui::NavUpdateContextMenuRequest()\n{\n    ImGuiContext& g = *GImGui;\n    g.NavOpenContextMenuItemId = g.NavOpenContextMenuWindowId = 0;\n    const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n    const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n    if ((!nav_keyboard_active && !nav_gamepad_active) || g.NavWindow == NULL)\n        return;\n\n    bool request = false;\n    request |= nav_keyboard_active && (IsKeyReleased(ImGuiKey_Menu, ImGuiKeyOwner_NoOwner) || (IsKeyPressed(ImGuiKey_F10, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner) && g.IO.KeyMods == ImGuiMod_Shift));\n    request |= nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadContextMenu, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner);\n    if (!request)\n        return;\n    g.NavOpenContextMenuItemId = g.NavId;\n    g.NavOpenContextMenuWindowId = g.NavWindow->ID;\n\n    // Allow triggering for Begin()..BeginPopupContextItem(). A possible alternative would be to use g.NavLayer == ImGuiNavLayer_Menu.\n    if (g.NavId == g.NavWindow->GetID(\"#CLOSE\") || g.NavId == g.NavWindow->GetID(\"#COLLAPSE\"))\n        g.NavOpenContextMenuItemId = g.NavWindow->MoveId;\n\n    g.NavInputSource = ImGuiInputSource_Keyboard;\n    SetNavCursorVisibleAfterMove();\n}\n\n// Handle PageUp/PageDown/Home/End keys\n// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request\n// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference\n// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?\nstatic float ImGui::NavUpdatePageUpPageDown()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.NavWindow;\n    if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)\n        return 0.0f;\n\n    const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner);\n    const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner);\n    const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);\n    const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);\n    if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out\n        return 0.0f;\n\n    if (g.NavLayer != ImGuiNavLayer_Main)\n        NavRestoreLayer(ImGuiNavLayer_Main);\n\n    if ((window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Main)) == 0 && window->DC.NavWindowHasScrollY)\n    {\n        // Fallback manual-scroll when window has no navigable item\n        if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))\n            SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());\n        else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))\n            SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());\n        else if (home_pressed)\n            SetScrollY(window, 0.0f);\n        else if (end_pressed)\n            SetScrollY(window, window->ScrollMax.y);\n    }\n    else\n    {\n        ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];\n        const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->FontRefSize * 1.0f + nav_rect_rel.GetHeight());\n        float nav_scoring_rect_offset_y = 0.0f;\n        if (IsKeyPressed(ImGuiKey_PageUp, true))\n        {\n            nav_scoring_rect_offset_y = -page_offset_y;\n            g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)\n            g.NavMoveClipDir = ImGuiDir_Up;\n            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_IsPageMove; // ImGuiNavMoveFlags_AlsoScoreVisibleSet may be added later\n        }\n        else if (IsKeyPressed(ImGuiKey_PageDown, true))\n        {\n            nav_scoring_rect_offset_y = +page_offset_y;\n            g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)\n            g.NavMoveClipDir = ImGuiDir_Down;\n            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_IsPageMove; // ImGuiNavMoveFlags_AlsoScoreVisibleSet may be added later\n        }\n        else if (home_pressed)\n        {\n            // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y\n            // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.\n            // Preserve current horizontal position if we have any.\n            nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f;\n            if (nav_rect_rel.IsInverted())\n                nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;\n            g.NavMoveDir = ImGuiDir_Down;\n            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;\n            // FIXME-NAV: MoveClipDir left to _None, intentional?\n        }\n        else if (end_pressed)\n        {\n            nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y;\n            if (nav_rect_rel.IsInverted())\n                nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;\n            g.NavMoveDir = ImGuiDir_Up;\n            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;\n            // FIXME-NAV: MoveClipDir left to _None, intentional?\n        }\n        return nav_scoring_rect_offset_y;\n    }\n    return 0.0f;\n}\n\nstatic void ImGui::NavEndFrame()\n{\n    ImGuiContext& g = *GImGui;\n\n    // Show Ctrl+Tab list window\n    if (g.NavWindowingTarget != NULL)\n        NavUpdateWindowingOverlay();\n\n    // Perform wrap-around in menus\n    // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly.\n    // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.\n    if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)\n        NavUpdateCreateWrappingRequest();\n}\n\nstatic void ImGui::NavUpdateCreateWrappingRequest()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.NavWindow;\n\n    bool do_forward = false;\n    ImRect bb_rel = window->NavRectRel[g.NavLayer];\n    ImGuiDir clip_dir = g.NavMoveDir;\n\n    const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;\n    //const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;\n\n    // Menu layer does not maintain scrolling / content size (#9178)\n    ImVec2 wrap_size = (g.NavLayer == ImGuiNavLayer_Menu) ? window->Size : window->ContentSize + window->WindowPadding;\n\n    if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))\n    {\n        bb_rel.Min.x = bb_rel.Max.x = wrap_size.x;\n        if (move_flags & ImGuiNavMoveFlags_WrapX)\n        {\n            bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row\n            clip_dir = ImGuiDir_Up;\n        }\n        do_forward = true;\n    }\n    if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))\n    {\n        bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x;\n        if (move_flags & ImGuiNavMoveFlags_WrapX)\n        {\n            bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row\n            clip_dir = ImGuiDir_Down;\n        }\n        do_forward = true;\n    }\n    if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))\n    {\n        bb_rel.Min.y = bb_rel.Max.y = wrap_size.y;\n        if (move_flags & ImGuiNavMoveFlags_WrapY)\n        {\n            bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column\n            clip_dir = ImGuiDir_Left;\n        }\n        do_forward = true;\n    }\n    if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))\n    {\n        bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y;\n        if (move_flags & ImGuiNavMoveFlags_WrapY)\n        {\n            bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column\n            clip_dir = ImGuiDir_Right;\n        }\n        do_forward = true;\n    }\n    if (!do_forward)\n        return;\n    window->NavRectRel[g.NavLayer] = bb_rel;\n    NavClearPreferredPosForAxis(ImGuiAxis_X);\n    NavClearPreferredPosForAxis(ImGuiAxis_Y);\n    NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);\n}\n\n// Can we focus this window with Ctrl+Tab (or PadMenu + PadFocusPrev/PadFocusNext)\n// Note that NoNavFocus makes the window not reachable with Ctrl+Tab but it can still be focused with mouse or programmatically.\n// If you want a window to never be focused, you may use the e.g. NoInputs flag.\nbool ImGui::IsWindowNavFocusable(ImGuiWindow* window)\n{\n    return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);\n}\n\nstatic ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)\n{\n    ImGuiContext& g = *GImGui;\n    for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)\n        if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))\n            return g.WindowsFocusOrder[i];\n    return NULL;\n}\n\nstatic void NavUpdateWindowingTarget(int focus_change_dir)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.NavWindowingTarget);\n    if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)\n        return;\n\n    const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);\n    ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);\n    if (!window_target)\n        window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);\n    if (window_target) // Don't reset windowing target if there's a single window in the list\n    {\n        g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;\n        g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);\n    }\n    g.NavWindowingToggleLayer = false;\n}\n\n// Apply focus and close overlay\nstatic void ImGui::NavUpdateWindowingApplyFocus(ImGuiWindow* apply_focus_window)\n{\n    // FIXME: Many actions here could be part of a higher-level/reused function. Why aren't they in FocusWindow() ?\n    // Investigate for each of them: ClearActiveID(), NavRestoreHighlightAfterMove(), NavRestoreLastChildNavWindow(), ClosePopupsOverWindow(), NavInitWindow()\n    ImGuiContext& g = *GImGui;\n    if (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)\n    {\n        ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL;\n        ClearActiveID();\n        SetNavCursorVisibleAfterMove();\n        ClosePopupsOverWindow(apply_focus_window, false);\n        FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild);\n        IM_ASSERT(g.NavWindow != NULL);\n        apply_focus_window = g.NavWindow;\n        if (apply_focus_window->NavLastIds[0] == 0) // FIXME: This is the equivalent of the 'if (g.NavId == 0) { NavInitWindow() }' in DockNodeUpdateTabBar().\n            NavInitWindow(apply_focus_window, false);\n\n        // If the window has ONLY a menu layer (no main layer), select it directly\n        // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,\n        // so Ctrl+Tab where the keys are only held for 1 frame will be able to use correct layers mask since\n        // the target window as already been previewed once.\n        // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,\n        // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*\n        // won't be valid.\n        if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))\n            g.NavLayer = ImGuiNavLayer_Menu;\n\n        // Request OS level focus\n        if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus)\n            g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport);\n    }\n    g.NavWindowingTarget = NULL;\n}\n\n// Windowing management mode\n// Keyboard: Ctrl+Tab (change focus/move/resize), Alt (toggle menu layer)\n// Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)\nstatic void ImGui::NavUpdateWindowing()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    ImGuiWindow* apply_focus_window = NULL;\n    bool apply_toggle_layer = false;\n\n    ImGuiWindow* modal_window = GetTopMostPopupModal();\n    bool allow_windowing = (modal_window == NULL); // FIXME: This prevent Ctrl+Tab from being usable with windows that are inside the Begin-stack of that modal.\n    if (!allow_windowing)\n        g.NavWindowingTarget = NULL;\n\n    // Fade out\n    if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)\n    {\n        g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);\n        if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)\n            g.NavWindowingTargetAnim = NULL;\n    }\n\n    // Start Ctrl+Tab or Square+L/R window selection\n    // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab)\n    const ImGuiID owner_id = ImHashStr(\"##NavUpdateWindowing\");\n    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;\n    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n    const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);\n    const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);\n    const bool start_toggling_with_gamepad = nav_gamepad_active && !g.NavWindowingTarget && Shortcut(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_RouteAlways, owner_id);\n    const bool start_windowing_with_gamepad = allow_windowing && start_toggling_with_gamepad;\n    const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!\n    bool just_started_windowing_from_null_focus = false;\n    if (start_toggling_with_gamepad)\n    {\n        g.NavWindowingToggleLayer = true; // Gamepad starts toggling layer\n        g.NavWindowingToggleKey = ImGuiKey_NavGamepadMenu;\n        g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Gamepad;\n    }\n    if (start_windowing_with_gamepad || start_windowing_with_keyboard)\n        if (ImGuiWindow* window = (g.NavWindow && IsWindowNavFocusable(g.NavWindow)) ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))\n        {\n            if (start_windowing_with_keyboard || g.ConfigNavWindowingWithGamepad)\n                g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location\n            g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;\n            g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);\n            g.NavWindowingInputSource = g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;\n            if (g.NavWindow == NULL)\n                just_started_windowing_from_null_focus = true;\n\n            // Manually register ownership of our mods. Using a global route in the Shortcut() calls instead would probably be correct but may have more side-effects.\n            if (keyboard_next_window || keyboard_prev_window)\n                SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id);\n        }\n\n    // Gamepad update\n    if ((g.NavWindowingTarget || g.NavWindowingToggleLayer) && g.NavWindowingInputSource == ImGuiInputSource_Gamepad)\n    {\n        if (g.NavWindowingTarget != NULL)\n        {\n            // Highlight only appears after a brief time holding the button, so that a fast tap on ImGuiKey_NavGamepadMenu (to toggle NavLayer) doesn't add visual noise\n            // However inputs are accepted immediately, so you press ImGuiKey_NavGamepadMenu + L1/R1 fast.\n            g.NavWindowingTimer += io.DeltaTime;\n            g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));\n\n            // Select window to focus\n            const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1);\n            if (focus_change_dir != 0 && !just_started_windowing_from_null_focus)\n            {\n                NavUpdateWindowingTarget(focus_change_dir);\n                g.NavWindowingHighlightAlpha = 1.0f;\n            }\n        }\n\n        // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)\n        if (!IsKeyDown(ImGuiKey_NavGamepadMenu))\n        {\n            g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.\n            if (g.NavWindowingToggleLayer && g.NavWindow)\n                apply_toggle_layer = true;\n            else if (!g.NavWindowingToggleLayer)\n                apply_focus_window = g.NavWindowingTarget;\n            g.NavWindowingTarget = NULL;\n            g.NavWindowingToggleLayer = false;\n        }\n    }\n\n    // Keyboard: Focus\n    if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_Keyboard)\n    {\n        // Visuals only appears after a brief time after pressing TAB the first time, so that a fast Ctrl+Tab doesn't add visual noise\n        ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_;\n        IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to \"hold\", otherwise Prev actions would keep cycling between two windows.\n        g.NavWindowingTimer += io.DeltaTime;\n        g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f\n        if ((keyboard_next_window || keyboard_prev_window) && !just_started_windowing_from_null_focus)\n            NavUpdateWindowingTarget(keyboard_next_window ? -1 : +1);\n        else if ((io.KeyMods & shared_mods) != shared_mods)\n            apply_focus_window = g.NavWindowingTarget;\n    }\n\n    // Keyboard: Press and Release Alt to toggle menu layer\n    const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };\n    bool windowing_toggle_layer_start = false;\n    if (g.NavWindow != NULL && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))\n        for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)\n            if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))\n            {\n                windowing_toggle_layer_start = true;\n                g.NavWindowingToggleLayer = true;\n                g.NavWindowingToggleKey = windowing_toggle_key;\n                g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Keyboard;\n                break;\n            }\n    if (g.NavWindowingToggleLayer && g.NavWindowingInputSource == ImGuiInputSource_Keyboard)\n    {\n        // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)\n        // We cancel toggling nav layer when other modifiers are pressed. (See #4439)\n        // - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl).\n        // We cancel toggling nav layer if an owner has claimed the key.\n        if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)\n            g.NavWindowingToggleLayer = false;\n        else if (windowing_toggle_layer_start == false && g.LastKeyboardKeyPressTime == g.Time)\n            g.NavWindowingToggleLayer = false;\n        else if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false)\n            g.NavWindowingToggleLayer = false;\n\n        // Apply layer toggle on Alt release\n        // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.\n        if (IsKeyReleased(g.NavWindowingToggleKey) && g.NavWindowingToggleLayer)\n            if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)\n                if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))\n                    apply_toggle_layer = true;\n        if (!IsKeyDown(g.NavWindowingToggleKey))\n            g.NavWindowingToggleLayer = false;\n    }\n\n    // Move window\n    if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))\n    {\n        ImVec2 nav_move_dir;\n        if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)\n            nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);\n        if (g.NavInputSource == ImGuiInputSource_Gamepad)\n            nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);\n        if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)\n        {\n            const float NAV_MOVE_SPEED = 800.0f;\n            const float move_step = NAV_MOVE_SPEED * io.DeltaTime * GetScale();\n            g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;\n            g.NavHighlightItemUnderNav = true;\n            ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos);\n            if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)\n            {\n                ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindowDockTree;\n                SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always);\n                g.NavWindowingAccumDeltaPos -= accum_floored;\n            }\n        }\n    }\n\n    // Apply final focus\n    if (apply_focus_window)\n        NavUpdateWindowingApplyFocus(apply_focus_window);\n\n    // Apply menu/layer toggle\n    if (apply_toggle_layer && g.NavWindow)\n    {\n        ClearActiveID();\n\n        // Move to parent menu if necessary\n        ImGuiWindow* new_nav_window = g.NavWindow;\n        while (new_nav_window->ParentWindow\n            && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0\n            && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0\n            && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)\n            new_nav_window = new_nav_window->ParentWindow;\n        if (new_nav_window != g.NavWindow)\n        {\n            ImGuiWindow* old_nav_window = g.NavWindow;\n            FocusWindow(new_nav_window);\n            new_nav_window->NavLastChildNavWindow = old_nav_window;\n        }\n\n        // Toggle layer\n        const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;\n        if (new_nav_layer != g.NavLayer)\n        {\n            // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)\n            const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);\n            if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id)\n                g.NavWindow->NavLastIds[new_nav_layer] = 0;\n            NavRestoreLayer(new_nav_layer);\n            SetNavCursorVisibleAfterMove();\n        }\n    }\n}\n\n// Window has already passed the IsWindowNavFocusable()\nstatic const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)\n{\n    if (window->Flags & ImGuiWindowFlags_Popup)\n        return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup);\n    if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, \"##MainMenuBar\") == 0)\n        return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar);\n    if (window->DockNodeAsHost)\n        return \"(Dock node)\"; // Not normally shown to user.\n    return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled);\n}\n\n// Overlay displayed when using Ctrl+Tab. Called by EndFrame().\nvoid ImGui::NavUpdateWindowingOverlay()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.NavWindowingTarget != NULL);\n\n    if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)\n        return;\n\n    const ImGuiViewport* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ GetMainViewport();\n    SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));\n    SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));\n    PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);\n    Begin(\"##NavWindowingOverlay\", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);\n    g.NavWindowingListWindow = g.CurrentWindow;\n    if (g.ContextName[0] != 0)\n        SeparatorText(g.ContextName);\n    for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)\n    {\n        ImGuiWindow* window = g.WindowsFocusOrder[n];\n        IM_ASSERT(window != NULL); // Fix static analyzers\n        if (!IsWindowNavFocusable(window))\n            continue;\n        const char* label = window->Name;\n        if (label == FindRenderedTextEnd(label))\n            label = GetFallbackWindowNameForWindowingList(window);\n        Selectable(label, g.NavWindowingTarget == window);\n    }\n    End();\n    PopStyleVar();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DRAG AND DROP\n//-----------------------------------------------------------------------------\n\nbool ImGui::IsDragDropActive()\n{\n    ImGuiContext& g = *GImGui;\n    return g.DragDropActive;\n}\n\nvoid ImGui::ClearDragDrop()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.DragDropActive)\n        IMGUI_DEBUG_LOG_ACTIVEID(\"[dragdrop] ClearDragDrop()\\n\");\n    g.DragDropActive = false;\n    g.DragDropPayload.Clear();\n    g.DragDropAcceptFlagsCurr = ImGuiDragDropFlags_None;\n    g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;\n    g.DragDropAcceptIdCurrRectSurface = FLT_MAX;\n    g.DragDropAcceptFrameCount = -1;\n\n    g.DragDropPayloadBufHeap.clear();\n    memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));\n}\n\nbool ImGui::BeginTooltipHidden()\n{\n    ImGuiContext& g = *GImGui;\n    bool ret = Begin(\"##Tooltip_Hidden\", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);\n    SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow);\n    return ret;\n}\n\n// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()\n// If the item has an identifier:\n// - This assume/require the item to be activated (typically via ButtonBehavior).\n// - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button.\n// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.\n// If the item has no identifier:\n// - Currently always assume left mouse button.\nbool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // FIXME-DRAGDROP: While in the common-most \"drag from non-zero active id\" case we can tell the mouse button,\n    // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).\n    ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;\n\n    bool source_drag_active = false;\n    ImGuiID source_id = 0;\n    ImGuiID source_parent_id = 0;\n    if ((flags & ImGuiDragDropFlags_SourceExtern) == 0)\n    {\n        source_id = g.LastItemData.ID;\n        if (source_id != 0)\n        {\n            // Common path: items with ID\n            if (g.ActiveId != source_id)\n                return false;\n            if (g.ActiveIdMouseButton != -1)\n                mouse_button = g.ActiveIdMouseButton;\n            if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)\n                return false;\n            g.ActiveIdAllowOverlap = false;\n        }\n        else\n        {\n            // Uncommon path: items without ID\n            if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)\n                return false;\n            if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))\n                return false;\n\n            // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:\n            // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag.\n            if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))\n            {\n                IM_ASSERT(0);\n                return false;\n            }\n\n            // Magic fallback to handle items with no assigned ID, e.g. Text(), Image()\n            // We build a throwaway ID based on current ID stack + relative AABB of items in window.\n            // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.\n            // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.\n            // Rely on keeping other window->LastItemXXX fields intact.\n            source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);\n            KeepAliveID(source_id);\n            bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.ItemFlags);\n            if (is_hovered && g.IO.MouseClicked[mouse_button])\n            {\n                SetActiveID(source_id, window);\n                FocusWindow(window);\n            }\n            if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.\n                g.ActiveIdAllowOverlap = is_hovered;\n        }\n        if (g.ActiveId != source_id)\n            return false;\n        source_parent_id = window->IDStack.back();\n        source_drag_active = IsMouseDragging(mouse_button);\n\n        // Disable navigation and key inputs while dragging + cancel existing request if any\n        SetActiveIdUsingAllKeyboardKeys();\n    }\n    else\n    {\n        // When ImGuiDragDropFlags_SourceExtern is set:\n        window = NULL;\n        source_id = ImHashStr(\"#SourceExtern\");\n        source_drag_active = true;\n        mouse_button = g.IO.MouseDown[0] ? 0 : -1;\n        KeepAliveID(source_id);\n        SetActiveID(source_id, NULL);\n    }\n\n    IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()\n    if (!source_drag_active)\n        return false;\n\n    // Activate drag and drop\n    if (!g.DragDropActive)\n    {\n        IM_ASSERT(source_id != 0);\n        ClearDragDrop();\n        IMGUI_DEBUG_LOG_ACTIVEID(\"[dragdrop] BeginDragDropSource() DragDropActive = true, source_id = 0x%08X%s\\n\",\n            source_id, (flags & ImGuiDragDropFlags_SourceExtern) ? \" (EXTERN)\" : \"\");\n        ImGuiPayload& payload = g.DragDropPayload;\n        payload.SourceId = source_id;\n        payload.SourceParentId = source_parent_id;\n        g.DragDropActive = true;\n        g.DragDropSourceFlags = flags;\n        g.DragDropMouseButton = mouse_button;\n        if (payload.SourceId == g.ActiveId)\n            g.ActiveIdNoClearOnFocusLoss = true;\n    }\n    g.DragDropSourceFrameCount = g.FrameCount;\n    g.DragDropWithinSource = true;\n\n    if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))\n    {\n        // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)\n        // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.\n        bool ret;\n        if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlagsPrev & ImGuiDragDropFlags_AcceptNoPreviewTooltip))\n            ret = BeginTooltipHidden();\n        else\n            ret = BeginTooltip();\n        IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin(\"##Hidden\", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddenAndSkipItemsForCurrentFrame().\n        IM_UNUSED(ret);\n    }\n\n    if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))\n        g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;\n\n    return true;\n}\n\nvoid ImGui::EndDragDropSource()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.DragDropActive);\n    IM_ASSERT(g.DragDropWithinSource && \"Not after a BeginDragDropSource()?\");\n\n    if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))\n        EndTooltip();\n\n    // Discard the drag if have not called SetDragDropPayload()\n    if (g.DragDropPayload.DataFrameCount == -1)\n        ClearDragDrop();\n    g.DragDropWithinSource = false;\n}\n\n// Use 'cond' to choose to submit payload on drag start or every frame\nbool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiPayload& payload = g.DragDropPayload;\n    if (cond == 0)\n        cond = ImGuiCond_Always;\n\n    IM_ASSERT(type != NULL);\n    IM_ASSERT(ImStrlen(type) < IM_COUNTOF(payload.DataType) && \"Payload type can be at most 32 characters long\");\n    IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));\n    IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);\n    IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()\n\n    if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)\n    {\n        // Copy payload\n        ImStrncpy(payload.DataType, type, IM_COUNTOF(payload.DataType));\n        g.DragDropPayloadBufHeap.resize(0);\n        if (data_size > sizeof(g.DragDropPayloadBufLocal))\n        {\n            // Store in heap\n            g.DragDropPayloadBufHeap.resize((int)data_size);\n            payload.Data = g.DragDropPayloadBufHeap.Data;\n            memcpy(payload.Data, data, data_size);\n        }\n        else if (data_size > 0)\n        {\n            // Store locally\n            memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));\n            payload.Data = g.DragDropPayloadBufLocal;\n            memcpy(payload.Data, data, data_size);\n        }\n        else\n        {\n            payload.Data = NULL;\n        }\n        payload.DataSize = (int)data_size;\n    }\n    payload.DataFrameCount = g.FrameCount;\n\n    // Return whether the payload has been accepted\n    return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);\n}\n\nbool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    if (!g.DragDropActive)\n        return false;\n\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;\n    if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree)\n        return false;\n    IM_ASSERT(id != 0);\n    if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))\n        return false;\n    if (window->SkipItems)\n        return false;\n\n    IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()\n    g.DragDropTargetRect = bb;\n    g.DragDropTargetClipRect = window->ClipRect; // May want to be overridden by user depending on use case?\n    g.DragDropTargetId = id;\n    g.DragDropTargetFullViewport = 0;\n    g.DragDropWithinTarget = true;\n    return true;\n}\n\n// Typical usage would be:\n//   if (!ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))\n//       if (ImGui::BeginDragDropTargetViewport(ImGui::GetMainViewport(), NULL))\n// But we are leaving the hover test to the caller for maximum flexibility.\nbool ImGui::BeginDragDropTargetViewport(ImGuiViewport* viewport, const ImRect* p_bb)\n{\n    ImGuiContext& g = *GImGui;\n    if (!g.DragDropActive)\n        return false;\n\n    ImRect bb = p_bb ? *p_bb : ((ImGuiViewportP*)viewport)->GetWorkRect();\n    ImGuiID id = viewport->ID;\n    if (g.MouseViewport != viewport || !IsMouseHoveringRect(bb.Min, bb.Max, false) || (id == g.DragDropPayload.SourceId))\n        return false;\n\n    IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()\n    g.DragDropTargetRect = bb;\n    g.DragDropTargetClipRect = bb;\n    g.DragDropTargetId = id;\n    g.DragDropTargetFullViewport = id;\n    g.DragDropWithinTarget = true;\n    return true;\n}\n\n// We don't use BeginDragDropTargetCustom() and duplicate its code because:\n// 1) we use LastItemData's ImGuiItemStatusFlags_HoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.\n// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.\n// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)\nbool ImGui::BeginDragDropTarget()\n{\n    ImGuiContext& g = *GImGui;\n    if (!g.DragDropActive)\n        return false;\n\n    ImGuiWindow* window = g.CurrentWindow;\n    if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))\n        return false;\n    ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;\n    if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree || window->SkipItems)\n        return false;\n\n    const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;\n    ImGuiID id = g.LastItemData.ID;\n    if (id == 0)\n    {\n        id = window->GetIDFromRectangle(display_rect);\n        KeepAliveID(id);\n    }\n    if (g.DragDropPayload.SourceId == id)\n        return false;\n\n    IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()\n    g.DragDropTargetRect = display_rect;\n    g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect;\n    g.DragDropTargetId = id;\n    g.DragDropWithinTarget = true;\n    return true;\n}\n\nbool ImGui::IsDragDropPayloadBeingAccepted()\n{\n    ImGuiContext& g = *GImGui;\n    return g.DragDropActive && g.DragDropAcceptIdPrev != 0;\n}\n\nconst ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiPayload& payload = g.DragDropPayload;\n    IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?\n    IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?\n    if (type != NULL && !payload.IsDataType(type))\n        return NULL;\n\n    // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.\n    // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!\n    const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);\n    ImRect r = g.DragDropTargetRect;\n    float r_surface = r.GetWidth() * r.GetHeight();\n    if (r_surface > g.DragDropAcceptIdCurrRectSurface)\n        return NULL;\n\n    g.DragDropAcceptFlagsCurr = flags;\n    g.DragDropAcceptIdCurr = g.DragDropTargetId;\n    g.DragDropAcceptIdCurrRectSurface = r_surface;\n    //IMGUI_DEBUG_LOG(\"AcceptDragDropPayload(): %08X: accept\\n\", g.DragDropTargetId);\n\n    // Render default drop visuals\n    payload.Preview = was_accepted_previously;\n    flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame)\n    const bool draw_target_rect = payload.Preview && !(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect);\n    if (draw_target_rect && g.DragDropTargetFullViewport != 0)\n    {\n        ImGuiViewport* viewport = FindViewportByID(g.DragDropTargetFullViewport);\n        IM_ASSERT(viewport != NULL);\n        ImRect bb = g.DragDropTargetRect;\n        bb.Expand(-3.5f);\n        RenderDragDropTargetRectEx(GetForegroundDrawList(viewport), bb);\n    }\n    else if (draw_target_rect)\n    {\n        RenderDragDropTargetRectForItem(r);\n    }\n\n    g.DragDropAcceptFrameCount = g.FrameCount;\n    if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1)\n        payload.Delivery = was_accepted_previously && (g.DragDropSourceFrameCount < g.FrameCount);\n    else\n        payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting OS window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()\n    if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))\n        return NULL;\n\n    if (payload.Delivery)\n        IMGUI_DEBUG_LOG_ACTIVEID(\"[dragdrop] AcceptDragDropPayload(): 0x%08X: payload delivery\\n\", g.DragDropTargetId);\n    return &payload;\n}\n\n// FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.\nvoid ImGui::RenderDragDropTargetRectForItem(const ImRect& bb)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImRect bb_display = bb;\n    bb_display.ClipWith(g.DragDropTargetClipRect); // Clip THEN expand so we have a way to visualize that target is not entirely visible.\n    bb_display.Expand(g.Style.DragDropTargetPadding);\n    bool push_clip_rect = !window->ClipRect.Contains(bb_display);\n    if (push_clip_rect)\n        window->DrawList->PushClipRectFullScreen();\n    RenderDragDropTargetRectEx(window->DrawList, bb_display);\n    if (push_clip_rect)\n        window->DrawList->PopClipRect();\n}\n\nvoid ImGui::RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb)\n{\n    ImGuiContext& g = *GImGui;\n    draw_list->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTargetBg), g.Style.DragDropTargetRounding, 0);\n    draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTarget), g.Style.DragDropTargetRounding, 0, g.Style.DragDropTargetBorderSize);\n}\n\nconst ImGuiPayload* ImGui::GetDragDropPayload()\n{\n    ImGuiContext& g = *GImGui;\n    return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL;\n}\n\nvoid ImGui::EndDragDropTarget()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.DragDropActive);\n    IM_ASSERT(g.DragDropWithinTarget);\n    g.DragDropWithinTarget = false;\n\n    // Clear drag and drop state payload right after delivery\n    if (g.DragDropPayload.Delivery)\n        ClearDragDrop();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] LOGGING/CAPTURING\n//-----------------------------------------------------------------------------\n// All text output from the interface can be captured into tty/file/clipboard.\n// By default, tree nodes are automatically opened during logging.\n//-----------------------------------------------------------------------------\n\n// Pass text data straight to log (without being displayed)\nstatic inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)\n{\n    if (g.LogFile)\n    {\n        g.LogBuffer.Buf.resize(0);\n        g.LogBuffer.appendfv(fmt, args);\n        ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);\n    }\n    else\n    {\n        g.LogBuffer.appendfv(fmt, args);\n    }\n}\n\nvoid ImGui::LogText(const char* fmt, ...)\n{\n    ImGuiContext& g = *GImGui;\n    if (!g.LogEnabled)\n        return;\n\n    va_list args;\n    va_start(args, fmt);\n    LogTextV(g, fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::LogTextV(const char* fmt, va_list args)\n{\n    ImGuiContext& g = *GImGui;\n    if (!g.LogEnabled)\n        return;\n\n    LogTextV(g, fmt, args);\n}\n\n// Internal version that takes a position to decide on newline placement and pad items according to their depth.\n// We split text into individual lines to add current tree level padding\n// FIXME: This code is a little complicated perhaps, considering simplifying the whole system.\nvoid ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    const char* prefix = g.LogNextPrefix;\n    const char* suffix = g.LogNextSuffix;\n    g.LogNextPrefix = g.LogNextSuffix = NULL;\n\n    if (!text_end)\n        text_end = FindRenderedTextEnd(text, text_end);\n\n    const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + ImMax(g.Style.FramePadding.y, g.Style.ItemSpacing.y) + 1);\n    if (ref_pos)\n        g.LogLinePosY = ref_pos->y;\n    if (log_new_line)\n    {\n        LogText(IM_NEWLINE);\n        g.LogLineFirstItem = true;\n    }\n\n    if (prefix)\n        LogRenderedText(ref_pos, prefix, prefix + ImStrlen(prefix)); // Calculate end ourself to ensure \"##\" are included here.\n\n    // Re-adjust padding if we have popped out of our starting depth\n    if (g.LogDepthRef > window->DC.TreeDepth)\n        g.LogDepthRef = window->DC.TreeDepth;\n    const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);\n\n    const char* text_remaining = text;\n    for (;;)\n    {\n        // Split the string. Each new line (after a '\\n') is followed by indentation corresponding to the current depth of our log entry.\n        // We don't add a trailing \\n yet to allow a subsequent item on the same line to be captured.\n        const char* line_start = text_remaining;\n        const char* line_end = ImStreolRange(line_start, text_end);\n        const bool is_last_line = (line_end == text_end);\n        if (line_start != line_end || !is_last_line)\n        {\n            const int line_length = (int)(line_end - line_start);\n            const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;\n            LogText(\"%*s%.*s\", indentation, \"\", line_length, line_start);\n            g.LogLineFirstItem = false;\n            if (*line_end == '\\n')\n            {\n                LogText(IM_NEWLINE);\n                g.LogLineFirstItem = true;\n            }\n        }\n        if (is_last_line)\n            break;\n        text_remaining = line_end + 1;\n    }\n\n    if (suffix)\n        LogRenderedText(ref_pos, suffix, suffix + ImStrlen(suffix));\n}\n\n// Start logging/capturing text output\nvoid ImGui::LogBegin(ImGuiLogFlags flags, int auto_open_depth)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(g.LogEnabled == false);\n    IM_ASSERT(g.LogFile == NULL && g.LogBuffer.empty());\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiLogFlags_OutputMask_)); // Check that only 1 type flag is used\n\n    g.LogEnabled = g.ItemUnclipByLog = true;\n    g.LogFlags = flags;\n    g.LogWindow = window;\n    g.LogNextPrefix = g.LogNextSuffix = NULL;\n    g.LogDepthRef = window->DC.TreeDepth;\n    g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);\n    g.LogLinePosY = FLT_MAX;\n    g.LogLineFirstItem = true;\n}\n\n// Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)\nvoid ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)\n{\n    ImGuiContext& g = *GImGui;\n    g.LogNextPrefix = prefix;\n    g.LogNextSuffix = suffix;\n}\n\nvoid ImGui::LogToTTY(int auto_open_depth)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.LogEnabled)\n        return;\n    IM_UNUSED(auto_open_depth);\n#ifndef IMGUI_DISABLE_TTY_FUNCTIONS\n    LogBegin(ImGuiLogFlags_OutputTTY, auto_open_depth);\n    g.LogFile = stdout;\n#endif\n}\n\n// Start logging/capturing text output to given file\nvoid ImGui::LogToFile(int auto_open_depth, const char* filename)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.LogEnabled)\n        return;\n\n    // FIXME: We could probably open the file in text mode \"at\", however note that clipboard/buffer logging will still\n    // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.\n    // By opening the file in binary mode \"ab\" we have consistent output everywhere.\n    if (!filename)\n        filename = g.IO.LogFilename;\n    if (!filename || !filename[0])\n        return;\n    ImFileHandle f = ImFileOpen(filename, \"ab\");\n    if (!f)\n    {\n        IM_ASSERT(0);\n        return;\n    }\n\n    LogBegin(ImGuiLogFlags_OutputFile, auto_open_depth);\n    g.LogFile = f;\n}\n\n// Start logging/capturing text output to clipboard\nvoid ImGui::LogToClipboard(int auto_open_depth)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.LogEnabled)\n        return;\n    LogBegin(ImGuiLogFlags_OutputClipboard, auto_open_depth);\n}\n\nvoid ImGui::LogToBuffer(int auto_open_depth)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.LogEnabled)\n        return;\n    LogBegin(ImGuiLogFlags_OutputBuffer, auto_open_depth);\n}\n\nvoid ImGui::LogFinish()\n{\n    ImGuiContext& g = *GImGui;\n    if (!g.LogEnabled)\n        return;\n\n    LogText(IM_NEWLINE);\n    switch (g.LogFlags & ImGuiLogFlags_OutputMask_)\n    {\n    case ImGuiLogFlags_OutputTTY:\n#ifndef IMGUI_DISABLE_TTY_FUNCTIONS\n        fflush(g.LogFile);\n#endif\n        break;\n    case ImGuiLogFlags_OutputFile:\n        ImFileClose(g.LogFile);\n        break;\n    case ImGuiLogFlags_OutputBuffer:\n        break;\n    case ImGuiLogFlags_OutputClipboard:\n        if (!g.LogBuffer.empty())\n            SetClipboardText(g.LogBuffer.begin());\n        break;\n    default:\n        IM_ASSERT(0);\n        break;\n    }\n\n    g.LogEnabled = g.ItemUnclipByLog = false;\n    g.LogFlags = ImGuiLogFlags_None;\n    g.LogFile = NULL;\n    g.LogBuffer.clear();\n}\n\n// Helper to display logging buttons\n// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)\nvoid ImGui::LogButtons()\n{\n    ImGuiContext& g = *GImGui;\n\n    PushID(\"LogButtons\");\n#ifndef IMGUI_DISABLE_TTY_FUNCTIONS\n    const bool log_to_tty = Button(\"Log To TTY\"); SameLine();\n#else\n    const bool log_to_tty = false;\n#endif\n    const bool log_to_file = Button(\"Log To File\"); SameLine();\n    const bool log_to_clipboard = Button(\"Log To Clipboard\"); SameLine();\n    PushItemFlag(ImGuiItemFlags_NoTabStop, true);\n    SetNextItemWidth(CalcTextSize(\"999\").x);\n    SliderInt(\"Default Depth\", &g.LogDepthToExpandDefault, 0, 9, NULL);\n    PopItemFlag();\n    PopID();\n\n    // Start logging at the end of the function so that the buttons don't appear in the log\n    if (log_to_tty)\n        LogToTTY();\n    if (log_to_file)\n        LogToFile();\n    if (log_to_clipboard)\n        LogToClipboard();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] SETTINGS\n//-----------------------------------------------------------------------------\n// - UpdateSettings() [Internal]\n// - MarkIniSettingsDirty() [Internal]\n// - FindSettingsHandler() [Internal]\n// - ClearIniSettings() [Internal]\n// - LoadIniSettingsFromDisk()\n// - LoadIniSettingsFromMemory()\n// - SaveIniSettingsToDisk()\n// - SaveIniSettingsToMemory()\n//-----------------------------------------------------------------------------\n// - CreateNewWindowSettings() [Internal]\n// - FindWindowSettingsByID() [Internal]\n// - FindWindowSettingsByWindow() [Internal]\n// - ClearWindowSettings() [Internal]\n// - WindowSettingsHandler_***() [Internal]\n//-----------------------------------------------------------------------------\n\n// Called by NewFrame()\nvoid ImGui::UpdateSettings()\n{\n    // Load settings on first frame (if not explicitly loaded manually before)\n    ImGuiContext& g = *GImGui;\n    if (!g.SettingsLoaded)\n    {\n        IM_ASSERT(g.SettingsWindows.empty());\n        if (g.IO.IniFilename)\n            LoadIniSettingsFromDisk(g.IO.IniFilename);\n        g.SettingsLoaded = true;\n    }\n\n    // Save settings (with a delay after the last modification, so we don't spam disk too much)\n    if (g.SettingsDirtyTimer > 0.0f)\n    {\n        g.SettingsDirtyTimer -= g.IO.DeltaTime;\n        if (g.SettingsDirtyTimer <= 0.0f)\n        {\n            if (g.IO.IniFilename != NULL)\n                SaveIniSettingsToDisk(g.IO.IniFilename);\n            else\n                g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.\n            g.SettingsDirtyTimer = 0.0f;\n        }\n    }\n}\n\nvoid ImGui::MarkIniSettingsDirty()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.SettingsDirtyTimer <= 0.0f)\n        g.SettingsDirtyTimer = g.IO.IniSavingRate;\n}\n\nvoid ImGui::MarkIniSettingsDirty(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))\n        if (g.SettingsDirtyTimer <= 0.0f)\n            g.SettingsDirtyTimer = g.IO.IniSavingRate;\n}\n\nvoid ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL);\n    g.SettingsHandlers.push_back(*handler);\n}\n\nvoid ImGui::RemoveSettingsHandler(const char* type_name)\n{\n    ImGuiContext& g = *GImGui;\n    if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name))\n        g.SettingsHandlers.erase(handler);\n}\n\nImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiID type_hash = ImHashStr(type_name);\n    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n        if (handler.TypeHash == type_hash)\n            return &handler;\n    return NULL;\n}\n\n// Clear all settings (windows, tables, docking etc.)\nvoid ImGui::ClearIniSettings()\n{\n    ImGuiContext& g = *GImGui;\n    g.SettingsIniData.clear();\n    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n        if (handler.ClearAllFn != NULL)\n            handler.ClearAllFn(&g, &handler);\n}\n\nvoid ImGui::LoadIniSettingsFromDisk(const char* ini_filename)\n{\n    size_t file_data_size = 0;\n    char* file_data = (char*)ImFileLoadToMemory(ini_filename, \"rb\", &file_data_size);\n    if (!file_data)\n        return;\n    if (file_data_size > 0)\n        LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);\n    IM_FREE(file_data);\n}\n\n// Zero-tolerance, no error reporting, cheap .ini parsing\n// Set ini_size==0 to let us use strlen(ini_data). Do not call this function with a 0 if your buffer is actually empty!\nvoid ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.Initialized);\n    //IM_ASSERT(!g.WithinFrameScope && \"Cannot be called between NewFrame() and EndFrame()\");\n    //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);\n\n    // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).\n    // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy..\n    if (ini_size == 0)\n        ini_size = ImStrlen(ini_data);\n    g.SettingsIniData.Buf.resize((int)ini_size + 1);\n    char* const buf = g.SettingsIniData.Buf.Data;\n    char* const buf_end = buf + ini_size;\n    memcpy(buf, ini_data, ini_size);\n    buf_end[0] = 0;\n\n    // Call pre-read handlers\n    // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)\n    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n        if (handler.ReadInitFn != NULL)\n            handler.ReadInitFn(&g, &handler);\n\n    void* entry_data = NULL;\n    ImGuiSettingsHandler* entry_handler = NULL;\n\n    char* line_end = NULL;\n    for (char* line = buf; line < buf_end; line = line_end + 1)\n    {\n        // Skip new lines markers, then find end of the line\n        while (*line == '\\n' || *line == '\\r')\n            line++;\n        line_end = line;\n        while (line_end < buf_end && *line_end != '\\n' && *line_end != '\\r')\n            line_end++;\n        line_end[0] = 0;\n        if (line[0] == ';')\n            continue;\n        if (line[0] == '[' && line_end > line && line_end[-1] == ']')\n        {\n            // Parse \"[Type][Name]\". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.\n            line_end[-1] = 0;\n            const char* name_end = line_end - 1;\n            const char* type_start = line + 1;\n            char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');\n            const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;\n            if (!type_end || !name_start)\n                continue;\n            *type_end = 0; // Overwrite first ']'\n            name_start++;  // Skip second '['\n            entry_handler = FindSettingsHandler(type_start);\n            entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;\n        }\n        else if (entry_handler != NULL && entry_data != NULL)\n        {\n            // Let type handler parse the line\n            entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);\n        }\n    }\n    g.SettingsLoaded = true;\n\n    // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)\n    memcpy(buf, ini_data, ini_size);\n\n    // Call post-read handlers\n    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n        if (handler.ApplyAllFn != NULL)\n            handler.ApplyAllFn(&g, &handler);\n}\n\nvoid ImGui::SaveIniSettingsToDisk(const char* ini_filename)\n{\n    ImGuiContext& g = *GImGui;\n    g.SettingsDirtyTimer = 0.0f;\n    if (!ini_filename)\n        return;\n\n    size_t ini_data_size = 0;\n    const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);\n    ImFileHandle f = ImFileOpen(ini_filename, \"wt\");\n    if (!f)\n        return;\n    ImFileWrite(ini_data, sizeof(char), ini_data_size, f);\n    ImFileClose(f);\n}\n\n// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer\nconst char* ImGui::SaveIniSettingsToMemory(size_t* out_size)\n{\n    ImGuiContext& g = *GImGui;\n    g.SettingsDirtyTimer = 0.0f;\n    g.SettingsIniData.Buf.resize(0);\n    g.SettingsIniData.Buf.push_back(0);\n    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n        handler.WriteAllFn(&g, &handler, &g.SettingsIniData);\n    if (out_size)\n        *out_size = (size_t)g.SettingsIniData.size();\n    return g.SettingsIniData.c_str();\n}\n\nImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.\n    if (g.IO.ConfigDebugIniSettings == false)\n        name = ImHashSkipUncontributingPrefix(name);\n    const size_t name_len = ImStrlen(name);\n\n    // Allocate chunk\n    const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;\n    ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);\n    IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();\n    settings->ID = ImHashStr(name, name_len);\n    memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator\n\n    return settings;\n}\n\n// We don't provide a FindWindowSettingsByName() because Docking system doesn't always hold on names.\n// This is called once per window .ini entry + once per newly instantiated window.\nImGuiWindowSettings* ImGui::FindWindowSettingsByID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n        if (settings->ID == id && !settings->WantDelete)\n            return settings;\n    return NULL;\n}\n\n// This is faster if you are holding on a Window already as we don't need to perform a search.\nImGuiWindowSettings* ImGui::FindWindowSettingsByWindow(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (window->SettingsOffset != -1)\n        return g.SettingsWindows.ptr_from_offset(window->SettingsOffset);\n    return FindWindowSettingsByID(window->ID);\n}\n\n// This will revert window to its initial state, including enabling the ImGuiCond_FirstUseEver/ImGuiCond_Once conditions once more.\nvoid ImGui::ClearWindowSettings(const char* name)\n{\n    //IMGUI_DEBUG_LOG(\"ClearWindowSettings('%s')\\n\", name);\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = FindWindowByName(name);\n    if (window != NULL)\n    {\n        window->Flags |= ImGuiWindowFlags_NoSavedSettings;\n        InitOrLoadWindowSettings(window, NULL);\n        if (window->DockId != 0)\n            DockContextProcessUndockWindow(&g, window, true);\n    }\n    if (ImGuiWindowSettings* settings = window ? FindWindowSettingsByWindow(window) : FindWindowSettingsByID(ImHashStr(name)))\n        settings->WantDelete = true;\n}\n\nstatic void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)\n{\n    ImGuiContext& g = *ctx;\n    for (ImGuiWindow* window : g.Windows)\n        window->SettingsOffset = -1;\n    g.SettingsWindows.clear();\n}\n\nstatic void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)\n{\n    ImGuiID id = ImHashStr(name);\n    ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByID(id);\n    if (settings)\n        *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry\n    else\n        settings = ImGui::CreateNewWindowSettings(name);\n    settings->ID = id;\n    settings->WantApply = true;\n    return (void*)settings;\n}\n\nstatic void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)\n{\n    ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;\n    int x, y;\n    int i;\n    ImU32 u1;\n    if (sscanf(line, \"Pos=%i,%i\", &x, &y) == 2)             { settings->Pos = ImVec2ih((short)x, (short)y); }\n    else if (sscanf(line, \"Size=%i,%i\", &x, &y) == 2)       { settings->Size = ImVec2ih((short)x, (short)y); }\n    else if (sscanf(line, \"ViewportId=0x%08X\", &u1) == 1)   { settings->ViewportId = u1; }\n    else if (sscanf(line, \"ViewportPos=%i,%i\", &x, &y) == 2){ settings->ViewportPos = ImVec2ih((short)x, (short)y); }\n    else if (sscanf(line, \"Collapsed=%d\", &i) == 1)         { settings->Collapsed = (i != 0); }\n    else if (sscanf(line, \"IsChild=%d\", &i) == 1)           { settings->IsChild = (i != 0); }\n    else if (sscanf(line, \"DockId=0x%X,%d\", &u1, &i) == 2)  { settings->DockId = u1; settings->DockOrder = (short)i; }\n    else if (sscanf(line, \"DockId=0x%X\", &u1) == 1)         { settings->DockId = u1; settings->DockOrder = -1; }\n    else if (sscanf(line, \"ClassId=0x%X\", &u1) == 1)        { settings->ClassId = u1; }\n}\n\n// Apply to existing windows (if any)\nstatic void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)\n{\n    ImGuiContext& g = *ctx;\n    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n        if (settings->WantApply)\n        {\n            if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))\n                ApplyWindowSettings(window, settings);\n            settings->WantApply = false;\n        }\n}\n\nstatic void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)\n{\n    // Gather data from windows that were active during this session\n    // (if a window wasn't opened in this session we preserve its settings)\n    ImGuiContext& g = *ctx;\n    for (ImGuiWindow* window : g.Windows)\n    {\n        if (window->Flags & ImGuiWindowFlags_NoSavedSettings)\n            continue;\n\n        ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByWindow(window);\n        if (!settings)\n        {\n            settings = ImGui::CreateNewWindowSettings(window->Name);\n            window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);\n        }\n        IM_ASSERT(settings->ID == window->ID);\n        settings->Pos = ImVec2ih(window->Pos - window->ViewportPos);\n        settings->Size = ImVec2ih(window->SizeFull);\n        settings->ViewportId = window->ViewportId;\n        settings->ViewportPos = ImVec2ih(window->ViewportPos);\n        IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId);\n        settings->DockId = window->DockId;\n        settings->ClassId = window->WindowClass.ClassId;\n        settings->DockOrder = window->DockOrder;\n        settings->Collapsed = window->Collapsed;\n        settings->IsChild = (window->RootWindow != window); // Cannot rely on ImGuiWindowFlags_ChildWindow here as docked windows have this set.\n        settings->WantDelete = false;\n    }\n\n    // Write to text buffer\n    buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve\n    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n    {\n        if (settings->WantDelete)\n            continue;\n        const char* settings_name = settings->GetName();\n        buf->appendf(\"[%s][%s]\\n\", handler->TypeName, settings_name);\n        if (settings->IsChild)\n        {\n            buf->appendf(\"IsChild=1\\n\");\n            buf->appendf(\"Size=%d,%d\\n\", settings->Size.x, settings->Size.y);\n        }\n        else\n        {\n            if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID)\n            {\n                buf->appendf(\"ViewportPos=%d,%d\\n\", settings->ViewportPos.x, settings->ViewportPos.y);\n                buf->appendf(\"ViewportId=0x%08X\\n\", settings->ViewportId);\n            }\n            if (settings->Pos.x != 0 || settings->Pos.y != 0 || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID)\n                buf->appendf(\"Pos=%d,%d\\n\", settings->Pos.x, settings->Pos.y);\n            if (settings->Size.x != 0 || settings->Size.y != 0)\n                buf->appendf(\"Size=%d,%d\\n\", settings->Size.x, settings->Size.y);\n            buf->appendf(\"Collapsed=%d\\n\", settings->Collapsed);\n            if (settings->DockId != 0)\n            {\n                //buf->appendf(\"TabId=0x%08X\\n\", ImHashStr(\"#TAB\", 4, settings->ID)); // window->TabId: this is not read back but writing it makes \"debugging\" the .ini data easier.\n                if (settings->DockOrder == -1)\n                    buf->appendf(\"DockId=0x%08X\\n\", settings->DockId);\n                else\n                    buf->appendf(\"DockId=0x%08X,%d\\n\", settings->DockId, settings->DockOrder);\n                if (settings->ClassId != 0)\n                    buf->appendf(\"ClassId=0x%08X\\n\", settings->ClassId);\n            }\n        }\n        buf->append(\"\\n\");\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] LOCALIZATION\n//-----------------------------------------------------------------------------\n\nvoid ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count)\n{\n    ImGuiContext& g = *GImGui;\n    for (int n = 0; n < count; n++)\n        g.LocalizationTable[entries[n].Key] = entries[n].Text;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] VIEWPORTS, PLATFORM WINDOWS\n//-----------------------------------------------------------------------------\n// - GetMainViewport()\n// - FindViewportByID()\n// - FindViewportByPlatformHandle()\n// - SetCurrentViewport() [Internal]\n// - SetWindowViewport() [Internal]\n// - GetWindowAlwaysWantOwnViewport() [Internal]\n// - UpdateTryMergeWindowIntoHostViewport() [Internal]\n// - UpdateTryMergeWindowIntoHostViewports() [Internal]\n// - TranslateWindowsInViewport() [Internal]\n// - ScaleWindowsInViewport() [Internal]\n// - FindHoveredViewportFromPlatformWindowStack() [Internal]\n// - UpdateViewportsNewFrame() [Internal]\n// - UpdateViewportsEndFrame() [Internal]\n// - AddUpdateViewport() [Internal]\n// - WindowSelectViewport() [Internal]\n// - WindowSyncOwnedViewport() [Internal]\n// - UpdatePlatformWindows()\n// - RenderPlatformWindowsDefault()\n// - FindPlatformMonitorForPos() [Internal]\n// - FindPlatformMonitorForRect() [Internal]\n// - UpdateViewportPlatformMonitor() [Internal]\n// - DestroyPlatformWindow() [Internal]\n// - DestroyPlatformWindows()\n//-----------------------------------------------------------------------------\n\nvoid ImGuiPlatformIO::ClearPlatformHandlers()\n{\n    Platform_GetClipboardTextFn = NULL;\n    Platform_SetClipboardTextFn = NULL;\n    Platform_OpenInShellFn = NULL;\n    Platform_SetImeDataFn = NULL;\n    Platform_ClipboardUserData = Platform_OpenInShellUserData = Platform_ImeUserData = NULL;\n    Platform_CreateWindow = Platform_DestroyWindow = Platform_ShowWindow = NULL;\n    Platform_SetWindowPos = Platform_SetWindowSize = NULL;\n    Platform_GetWindowPos = Platform_GetWindowSize = Platform_GetWindowFramebufferScale = NULL;\n    Platform_SetWindowFocus = NULL;\n    Platform_GetWindowFocus = Platform_GetWindowMinimized = NULL;\n    Platform_SetWindowTitle = NULL;\n    Platform_SetWindowAlpha = NULL;\n    Platform_UpdateWindow = NULL;\n    Platform_RenderWindow = Platform_SwapBuffers = NULL;\n    Platform_GetWindowDpiScale = NULL;\n    Platform_OnChangedViewport = NULL;\n    Platform_GetWindowWorkAreaInsets = NULL;\n    Platform_CreateVkSurface = NULL;\n}\n\nvoid ImGuiPlatformIO::ClearRendererHandlers()\n{\n    Renderer_TextureMaxWidth = Renderer_TextureMaxHeight = 0;\n    Renderer_RenderState = NULL;\n    Renderer_CreateWindow = Renderer_DestroyWindow = NULL;\n    Renderer_SetWindowSize = NULL;\n    Renderer_RenderWindow = Renderer_SwapBuffers = NULL;\n}\n\nImGuiViewport* ImGui::GetMainViewport()\n{\n    ImGuiContext& g = *GImGui;\n    return g.Viewports[0];\n}\n\n// FIXME: This leaks access to viewports not listed in PlatformIO.Viewports[]. Problematic? (#4236)\nImGuiViewport* ImGui::FindViewportByID(ImGuiID viewport_id)\n{\n    ImGuiContext& g = *GImGui;\n    for (ImGuiViewportP* viewport : g.Viewports)\n        if (viewport->ID == viewport_id)\n            return viewport;\n    return NULL;\n}\n\nImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle)\n{\n    ImGuiContext& g = *GImGui;\n    for (ImGuiViewportP* viewport : g.Viewports)\n        if (viewport->PlatformHandle == platform_handle)\n            return viewport;\n    return NULL;\n}\n\nvoid ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport)\n{\n    ImGuiContext& g = *GImGui;\n    (void)current_window;\n\n    if (viewport)\n        viewport->LastFrameActive = g.FrameCount;\n    if (g.CurrentViewport == viewport)\n        return;\n    g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f;\n    g.CurrentViewport = viewport;\n    IM_ASSERT(g.CurrentDpiScale > 0.0f && g.CurrentDpiScale < 99.0f); // Typical correct values would be between 1.0f and 4.0f\n    //IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] SetCurrentViewport changed '%s' 0x%08X\\n\", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0);\n    if (g.IO.ConfigDpiScaleFonts)\n        g.Style.FontScaleDpi = g.CurrentDpiScale;\n\n    // Notify platform layer of viewport changes\n    // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI\n    if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport)\n        g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport);\n}\n\nvoid ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)\n{\n    // Abandon viewport\n    if (window->ViewportOwned && window->Viewport->Window == window)\n        window->Viewport->Size = ImVec2(0.0f, 0.0f);\n\n    window->Viewport = viewport;\n    window->ViewportId = viewport->ID;\n    window->ViewportOwned = (viewport->Window == window);\n}\n\nstatic bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window)\n{\n    // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protrude and create their own.\n    ImGuiContext& g = *GImGui;\n    if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge))\n        if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)\n            if (!window->DockIsActive)\n                if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0)\n                    if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || (window->Flags & ImGuiWindowFlags_Modal) != 0)\n                        return true;\n    return false;\n}\n\n\n// Heuristic, see #8948: depends on how backends handle OS-level parenting.\n// Due to how parent viewport stack is layed out, note that IsViewportAbove(a,b) isn't always the same as !IsViewportAbove(b,a).\nstatic bool IsViewportAbove(ImGuiViewportP* potential_above, ImGuiViewportP* potential_below)\n{\n    // If ImGuiBackendFlags_HasParentViewport if set, ->ParentViewport chain should be accurate.\n    ImGuiContext& g = *GImGui;\n    if (g.IO.BackendFlags & ImGuiBackendFlags_HasParentViewport)\n    {\n        for (ImGuiViewport* v = potential_above; v != NULL && v->ParentViewport; v = v->ParentViewport)\n            if (v->ParentViewport == potential_below)\n                return true;\n    }\n    else\n    {\n        if (potential_above->ParentViewport == potential_below)\n            return true;\n    }\n\n    if (potential_above->LastFocusedStampCount > potential_below->LastFocusedStampCount)\n        return true;\n    return false;\n}\n\nstatic bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport_dst)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(window == window->RootWindowDockTree);\n    ImGuiViewportP* viewport_src = window->Viewport; // Current viewport\n    if (viewport_src == viewport_dst)\n        return false;\n    if ((viewport_dst->Flags & ImGuiViewportFlags_CanHostOtherWindows) == 0)\n        return false;\n    if ((viewport_dst->Flags & ImGuiViewportFlags_IsMinimized) != 0)\n        return false;\n    if (!viewport_dst->GetMainRect().Contains(window->Rect()))\n        return false;\n    if (GetWindowAlwaysWantOwnViewport(window))\n        return false;\n\n    for (ImGuiViewportP* viewport_obstructing : g.Viewports)\n    {\n        if (viewport_obstructing == viewport_src || viewport_obstructing == viewport_dst)\n            continue;\n        if (viewport_obstructing->GetMainRect().Overlaps(window->Rect()))\n            if (IsViewportAbove(viewport_obstructing, viewport_dst))\n                if (viewport_src == NULL || IsViewportAbove(viewport_src, viewport_obstructing))\n                    return false; // viewport_obstructing is between viewport_src and viewport_dst -> Cannot merge.\n    }\n\n    // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child)\n    IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] Window '%s' merge into Viewport 0X%08X\\n\", window->Name, viewport_dst->ID);\n    if (window->ViewportOwned)\n        for (int n = 0; n < g.Windows.Size; n++)\n            if (g.Windows[n]->Viewport == viewport_src)\n                SetWindowViewport(g.Windows[n], viewport_dst);\n    SetWindowViewport(window, viewport_dst);\n    if ((window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)\n        BringWindowToDisplayFront(window);\n\n    return true;\n}\n\n// FIXME: handle 0 to N host viewports\nstatic bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]);\n}\n\n// Translate Dear ImGui windows when a Host Viewport has been moved\n// (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)\nvoid ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos, const ImVec2& old_size, const ImVec2& new_size)\n{\n    ImGuiContext& g = *GImGui;\n    //IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] TranslateWindowsInViewport 0x%08X\\n\", viewport->ID);\n    IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows));\n\n    // 1) We test if ImGuiConfigFlags_ViewportsEnable was just toggled, which allows us to conveniently\n    // translate imgui windows from OS-window-local to absolute coordinates or vice-versa.\n    // 2) If it's not going to fit into the new size, keep it at same absolute position.\n    // One problem with this is that most Win32 applications doesn't update their render while dragging,\n    // and so the window will appear to teleport when releasing the mouse.\n    const bool translate_all_windows = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable);\n    ImRect test_still_fit_rect(old_pos, old_pos + old_size);\n    ImVec2 delta_pos = new_pos - old_pos;\n    for (ImGuiWindow* window : g.Windows) // FIXME-OPT\n        if (translate_all_windows || (window->Viewport == viewport && (old_size == new_size || test_still_fit_rect.Contains(window->Rect()))))\n            TranslateWindow(window, delta_pos);\n}\n\n// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)\nvoid ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale)\n{\n    ImGuiContext& g = *GImGui;\n    //IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] ScaleWindowsInViewport 0x%08X\\n\", viewport->ID);\n    if (viewport->Window)\n    {\n        ScaleWindow(viewport->Window, scale);\n    }\n    else\n    {\n        for (ImGuiWindow* window : g.Windows)\n            if (window->Viewport == viewport)\n                ScaleWindow(window, scale);\n    }\n}\n\n// If the backend doesn't support ImGuiBackendFlags_HasMouseHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs for it, we do a search ourselves.\n// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.\n// B) It requires Platform_GetWindowFocus to be implemented by backend.\nImGuiViewportP* ImGui::FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiViewportP* best_candidate = NULL;\n    for (ImGuiViewportP* viewport : g.Viewports)\n        if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_IsMinimized)) && viewport->GetMainRect().Contains(mouse_platform_pos))\n            if (best_candidate == NULL || best_candidate->LastFocusedStampCount < viewport->LastFocusedStampCount)\n                if (viewport->PlatformWindowCreated)\n                    best_candidate = viewport;\n    return best_candidate;\n}\n\n// Update viewports and monitor infos\n// Note that this is running even if 'ImGuiConfigFlags_ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info.\nstatic void ImGui::UpdateViewportsNewFrame()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size);\n\n    // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport)\n    // Update Focused status\n    const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0;\n    if (viewports_enabled)\n    {\n        ImGuiViewportP* focused_viewport = NULL;\n        for (ImGuiViewportP* viewport : g.Viewports)\n        {\n            const bool platform_funcs_available = viewport->PlatformWindowCreated;\n            if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available)\n            {\n                bool is_minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport);\n                if (is_minimized)\n                    viewport->Flags |= ImGuiViewportFlags_IsMinimized;\n                else\n                    viewport->Flags &= ~ImGuiViewportFlags_IsMinimized;\n            }\n\n            // Update our implicit z-order knowledge of platform windows, which is used when the backend cannot provide io.MouseHoveredViewport.\n            // When setting Platform_GetWindowFocus, it is expected that the platform backend can handle calls without crashing if it doesn't have data stored.\n            if (g.PlatformIO.Platform_GetWindowFocus && platform_funcs_available)\n            {\n                bool is_focused = g.PlatformIO.Platform_GetWindowFocus(viewport);\n                if (is_focused)\n                    viewport->Flags |= ImGuiViewportFlags_IsFocused;\n                else\n                    viewport->Flags &= ~ImGuiViewportFlags_IsFocused;\n                if (is_focused)\n                    focused_viewport = viewport;\n            }\n        }\n\n        // Focused viewport has changed?\n        if (focused_viewport && g.PlatformLastFocusedViewportId != focused_viewport->ID)\n        {\n            IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] Focused viewport changed %08X -> %08X '%s', attempting to apply our focus.\\n\", g.PlatformLastFocusedViewportId, focused_viewport->ID, focused_viewport->Window ? focused_viewport->Window->Name : \"n/a\");\n            const ImGuiViewport* prev_focused_viewport = FindViewportByID(g.PlatformLastFocusedViewportId);\n            const bool prev_focused_has_been_destroyed = (prev_focused_viewport == NULL) || (prev_focused_viewport->PlatformWindowCreated == false);\n\n            // Store a tag so we can infer z-order easily from all our windows\n            // We compare PlatformLastFocusedViewportId so newly created viewports with _NoFocusOnAppearing flag\n            // will keep the front most stamp instead of losing it back to their parent viewport.\n            if (focused_viewport->LastFocusedStampCount != g.ViewportFocusedStampCount)\n                focused_viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount;\n            g.PlatformLastFocusedViewportId = focused_viewport->ID;\n\n            // Focus associated dear imgui window\n            // - if focus didn't happen with a click within imgui boundaries, e.g. Clicking platform title bar. (#6299)\n            // - if focus didn't happen because we destroyed another window (#6462)\n            // FIXME: perhaps 'FocusTopMostWindowUnderOne()' can handle the 'focused_window->Window != NULL' case as well.\n            const bool apply_imgui_focus_on_focused_viewport = !IsAnyMouseDown() && !prev_focused_has_been_destroyed;\n            if (apply_imgui_focus_on_focused_viewport && g.IO.ConfigViewportsPlatformFocusSetsImGuiFocus)\n            {\n                focused_viewport->LastFocusedHadNavWindow |= (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport); // Update so a window changing viewport won't lose focus.\n                ImGuiFocusRequestFlags focus_request_flags = ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild;\n                if (focused_viewport->Window != NULL)\n                    FocusWindow(focused_viewport->Window, focus_request_flags);\n                else if (focused_viewport->LastFocusedHadNavWindow)\n                    FocusTopMostWindowUnderOne(NULL, NULL, focused_viewport, focus_request_flags); // Focus top most in viewport\n                else\n                    FocusWindow(NULL, focus_request_flags); // No window had focus last time viewport was focused\n            }\n        }\n        if (focused_viewport)\n            focused_viewport->LastFocusedHadNavWindow = (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport);\n    }\n\n    // Create/update main viewport with current platform position.\n    // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.\n    ImGuiViewportP* main_viewport = g.Viewports[0];\n    IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID);\n    IM_ASSERT(main_viewport->Window == NULL);\n    ImVec2 main_viewport_pos = viewports_enabled ? g.PlatformIO.Platform_GetWindowPos(main_viewport) : ImVec2(0.0f, 0.0f);\n    ImVec2 main_viewport_size = g.IO.DisplaySize;\n    ImVec2 main_viewport_framebuffer_scale = g.IO.DisplayFramebufferScale;\n    if (viewports_enabled && (main_viewport->Flags & ImGuiViewportFlags_IsMinimized))\n    {\n        main_viewport_pos = main_viewport->Pos; // Preserve last pos/size when minimized (FIXME: We don't do the same for Size outside of the viewport path)\n        main_viewport_size = main_viewport->Size;\n        main_viewport_framebuffer_scale = main_viewport->FramebufferScale;\n    }\n    AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_OwnedByApp | ImGuiViewportFlags_CanHostOtherWindows);\n\n    g.CurrentDpiScale = 0.0f;\n    g.CurrentViewport = NULL;\n    g.MouseViewport = NULL;\n    for (int n = 0; n < g.Viewports.Size; n++)\n    {\n        ImGuiViewportP* viewport = g.Viewports[n];\n        viewport->Idx = n;\n\n        // Erase unused viewports\n        if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2)\n        {\n            DestroyViewport(viewport);\n            n--;\n            continue;\n        }\n\n        const bool platform_funcs_available = viewport->PlatformWindowCreated;\n        if (viewports_enabled)\n        {\n            // Update Position and Size (from Platform Window to ImGui) if requested.\n            // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities.\n            if (!(viewport->Flags & ImGuiViewportFlags_IsMinimized) && platform_funcs_available)\n            {\n                // Viewport->WorkPos and WorkSize will be updated below\n                if (viewport->PlatformRequestMove)\n                    viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport);\n                if (viewport->PlatformRequestResize)\n                    viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport);\n                if (g.PlatformIO.Platform_GetWindowFramebufferScale != NULL)\n                    viewport->FramebufferScale = g.PlatformIO.Platform_GetWindowFramebufferScale(viewport);\n            }\n        }\n\n        // Update/copy monitor info\n        UpdateViewportPlatformMonitor(viewport);\n\n        // Lock down space taken by menu bars and status bars + query initial insets from backend\n        // Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc.\n        viewport->WorkInsetMin = viewport->BuildWorkInsetMin;\n        viewport->WorkInsetMax = viewport->BuildWorkInsetMax;\n        viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f);\n        if (g.PlatformIO.Platform_GetWindowWorkAreaInsets != NULL && platform_funcs_available)\n        {\n            ImVec4 insets = g.PlatformIO.Platform_GetWindowWorkAreaInsets(viewport);\n            IM_ASSERT(insets.x >= 0.0f && insets.y >= 0.0f && insets.z >= 0.0f && insets.w >= 0.0f);\n            viewport->BuildWorkInsetMin = ImVec2(insets.x, insets.y);\n            viewport->BuildWorkInsetMax = ImVec2(insets.z, insets.w);\n        }\n        viewport->UpdateWorkRect();\n\n        // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back.\n        viewport->Alpha = 1.0f;\n\n        // Translate Dear ImGui windows when a Host Viewport has been moved\n        // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)\n        const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos;\n        if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f))\n            TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos, viewport->LastSize, viewport->Size);\n\n        // Update DPI scale\n        float new_dpi_scale;\n        if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available)\n            new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport);\n        else if (viewport->PlatformMonitor != -1)\n            new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;\n        else\n            new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f;\n        IM_ASSERT(new_dpi_scale > 0.0f && new_dpi_scale < 99.0f); // Typical correct values would be between 1.0f and 4.0f\n        if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale)\n        {\n            float scale_factor = new_dpi_scale / viewport->DpiScale;\n            if (g.IO.ConfigDpiScaleViewports)\n                ScaleWindowsInViewport(viewport, scale_factor);\n            //if (viewport == GetMainViewport())\n            //    g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor);\n\n            // Scale our window moving pivot so that the window will rescale roughly around the mouse position.\n            // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border.\n            // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.)\n            //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport)\n            //    g.ActiveIdClickOffset = ImTrunc(g.ActiveIdClickOffset * scale_factor);\n        }\n        viewport->DpiScale = new_dpi_scale;\n    }\n\n    // Update fallback monitor\n    g.PlatformMonitorsFullWorkRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);\n    if (g.PlatformIO.Monitors.Size == 0)\n    {\n        ImGuiPlatformMonitor* monitor = &g.FallbackMonitor;\n        monitor->MainPos = main_viewport->Pos;\n        monitor->MainSize = main_viewport->Size;\n        monitor->WorkPos = main_viewport->WorkPos;\n        monitor->WorkSize = main_viewport->WorkSize;\n        monitor->DpiScale = main_viewport->DpiScale;\n        g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos);\n        g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos + monitor->WorkSize);\n    }\n    else\n    {\n        g.FallbackMonitor = g.PlatformIO.Monitors[0];\n    }\n    for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)\n    {\n        g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos);\n        g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos + monitor.WorkSize);\n    }\n\n    if (!viewports_enabled)\n    {\n        g.MouseViewport = main_viewport;\n        return;\n    }\n\n    // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport.\n    // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set.\n    ImGuiViewportP* viewport_hovered = NULL;\n    if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)\n    {\n        viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL;\n        if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))\n            viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); // Backend failed to handle _NoInputs viewport: revert to our fallback.\n    }\n    else\n    {\n        // If the backend doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search:\n        // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.\n        // B) won't take account of how the backend apply parent<>child relationship to secondary viewports, which affects their Z order.\n        // C) uses LastFocusedStampCount as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO)\n        viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);\n    }\n    if (viewport_hovered != NULL)\n        g.MouseLastHoveredViewport = viewport_hovered;\n    else if (g.MouseLastHoveredViewport == NULL)\n        g.MouseLastHoveredViewport = g.Viewports[0];\n\n    // Update mouse reference viewport\n    // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode)\n    // (MovingViewport->Viewport will be NULL in the rare situation where the window disappared while moving, set UpdateMouseMovingWindowNewFrame() for details)\n    if (g.MovingWindow && g.MovingWindow->Viewport)\n        g.MouseViewport = g.MovingWindow->Viewport;\n    else\n        g.MouseViewport = g.MouseLastHoveredViewport;\n\n    // When dragging something, always refer to the last hovered viewport.\n    // - when releasing a moving window we will revert to aiming behind (at viewport_hovered)\n    // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info)\n    // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release.\n    // FIXME-VIEWPORT: This is essentially broken, when ImGuiBackendFlags_HasMouseHoveredViewport is set we want to trust when viewport_hovered==NULL and use that.\n    const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive;\n    if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL)\n        viewport_hovered = g.MouseLastHoveredViewport;\n    if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown())\n        if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))\n            g.MouseViewport = viewport_hovered;\n\n    IM_ASSERT(g.MouseViewport != NULL);\n}\n\n// Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)\nstatic void ImGui::UpdateViewportsEndFrame()\n{\n    ImGuiContext& g = *GImGui;\n    g.PlatformIO.Viewports.resize(0);\n    for (int i = 0; i < g.Viewports.Size; i++)\n    {\n        ImGuiViewportP* viewport = g.Viewports[i];\n        viewport->LastPos = viewport->Pos;\n        viewport->LastSize = viewport->Size;\n        if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f)\n            if (i > 0) // Always include main viewport in the list\n                continue;\n        if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window))\n            continue;\n        if (i > 0)\n            IM_ASSERT(viewport->Window != NULL);\n        g.PlatformIO.Viewports.push_back(viewport);\n    }\n    g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called\n}\n\n// FIXME: We should ideally refactor the system to call this every frame (we currently don't)\nImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(id != 0);\n\n    flags |= ImGuiViewportFlags_IsPlatformWindow;\n    if (window != NULL)\n    {\n        const bool window_can_use_inputs = ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) == false;\n        if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window)\n            flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing;\n        if (!window_can_use_inputs)\n            flags |= ImGuiViewportFlags_NoInputs;\n        if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)\n            flags |= ImGuiViewportFlags_NoFocusOnAppearing;\n    }\n\n    ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id);\n    if (viewport)\n    {\n        // Always update for main viewport as we are already pulling correct platform pos/size (see #4900)\n        ImVec2 prev_pos = viewport->Pos;\n        ImVec2 prev_size = viewport->Size;\n        if (!viewport->PlatformRequestMove || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID)\n            viewport->Pos = pos;\n        if (!viewport->PlatformRequestResize || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID)\n            viewport->Size = size;\n        viewport->Flags = flags | (viewport->Flags & (ImGuiViewportFlags_IsMinimized | ImGuiViewportFlags_IsFocused)); // Preserve existing flags\n        if (prev_pos != viewport->Pos || prev_size != viewport->Size)\n            UpdateViewportPlatformMonitor(viewport);\n    }\n    else\n    {\n        // New viewport\n        viewport = IM_NEW(ImGuiViewportP)();\n        viewport->ID = id;\n        viewport->Idx = g.Viewports.Size;\n        viewport->Pos = viewport->LastPos = pos;\n        viewport->Size = viewport->LastSize = size;\n        viewport->Flags = flags;\n        UpdateViewportPlatformMonitor(viewport);\n        g.Viewports.push_back(viewport);\n        g.ViewportCreatedCount++;\n        IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] Add Viewport %08X '%s'\\n\", id, window ? window->Name : \"<NULL>\");\n\n        // We assume the window becomes front-most (even when ImGuiViewportFlags_NoFocusOnAppearing is used).\n        // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available.\n        viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount;\n\n        // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport.\n        // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame\n        g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport->Pos.x);\n        g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport->Pos.y);\n        g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x);\n        g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y);\n\n        // Store initial DpiScale before the OS platform window creation, based on expected monitor data.\n        // This is so we can select an appropriate font size on the first frame of our window lifetime\n        viewport->DpiScale = GetViewportPlatformMonitor(viewport)->DpiScale;\n    }\n\n    viewport->Window = window;\n    viewport->LastFrameActive = g.FrameCount;\n    viewport->UpdateWorkRect();\n    IM_ASSERT(window == NULL || viewport->ID == window->ID);\n\n    if (window != NULL)\n        window->ViewportOwned = true;\n\n    return viewport;\n}\n\nstatic void ImGui::DestroyViewport(ImGuiViewportP* viewport)\n{\n    // Clear references to this viewport in windows (window->ViewportId becomes the master data)\n    ImGuiContext& g = *GImGui;\n    for (ImGuiWindow* window : g.Windows)\n    {\n        if (window->Viewport != viewport)\n            continue;\n        window->Viewport = NULL;\n        window->ViewportOwned = false;\n    }\n    if (viewport == g.MouseLastHoveredViewport)\n        g.MouseLastHoveredViewport = NULL;\n\n    // Destroy\n    IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] Delete Viewport %08X '%s'\\n\", viewport->ID, viewport->Window ? viewport->Window->Name : \"n/a\");\n    DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here.\n    IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false);\n    IM_ASSERT(g.Viewports[viewport->Idx] == viewport);\n    g.Viewports.erase(g.Viewports.Data + viewport->Idx);\n    IM_DELETE(viewport);\n}\n\n// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten.\nstatic void ImGui::WindowSelectViewport(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindowFlags flags = window->Flags;\n    window->ViewportAllowPlatformMonitorExtend = -1;\n\n    // Restore main viewport if multi-viewport is not supported by the backend\n    ImGuiViewportP* main_viewport = (ImGuiViewportP*)(void*)GetMainViewport();\n    if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))\n    {\n        SetWindowViewport(window, main_viewport);\n        return;\n    }\n    window->ViewportOwned = false;\n\n    // Appearing popups reset their viewport so they can inherit again\n    if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing)\n    {\n        window->Viewport = NULL;\n        window->ViewportId = 0;\n    }\n\n    if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasViewport) == 0)\n    {\n        // By default inherit from parent window\n        if (window->Viewport == NULL && window->ParentWindow && (!window->ParentWindow->IsFallbackWindow || window->ParentWindow->WasActive))\n            window->Viewport = window->ParentWindow->Viewport;\n\n        // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file\n        if (window->Viewport == NULL && window->ViewportId != 0)\n        {\n            window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId);\n            if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX)\n                window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None);\n        }\n    }\n\n    bool lock_viewport = false;\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasViewport)\n    {\n        // Code explicitly request a viewport\n        window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId);\n        window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet.\n        if (window->Viewport && (window->Flags & ImGuiWindowFlags_DockNodeHost) != 0 && window->Viewport->Window != NULL)\n        {\n            window->Viewport->Window = window;\n            window->Viewport->ID = window->ViewportId = window->ID; // Overwrite ID (always owned by node)\n        }\n        lock_viewport = true;\n    }\n    else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu))\n    {\n        // Always inherit viewport from parent window\n        if (window->DockNode && window->DockNode->HostWindow)\n            IM_ASSERT(window->DockNode->HostWindow->Viewport == window->ParentWindow->Viewport);\n        window->Viewport = window->ParentWindow->Viewport;\n    }\n    else if (window->DockNode && window->DockNode->HostWindow)\n    {\n        // This covers the \"always inherit viewport from parent window\" case for when a window reattach to a node that was just created mid-frame\n        window->Viewport = window->DockNode->HostWindow->Viewport;\n    }\n    else if (flags & ImGuiWindowFlags_Tooltip)\n    {\n        window->Viewport = g.MouseViewport;\n    }\n    else if (GetWindowAlwaysWantOwnViewport(window))\n    {\n        window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);\n    }\n    else if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window && IsMousePosValid())\n    {\n        if (window->Viewport != NULL && window->Viewport->Window == window)\n            window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);\n    }\n    else\n    {\n        // Merge into host viewport?\n        // We cannot test window->ViewportOwned as it set lower in the function.\n        // Testing (g.ActiveId == 0 || g.ActiveIdAllowOverlap) to avoid merging during a short-term widget interaction. Main intent was to avoid during resize (see #4212)\n        bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && (g.ActiveId == 0 || g.ActiveIdAllowOverlap));\n        if (try_to_merge_into_host_viewport)\n            UpdateTryMergeWindowIntoHostViewports(window);\n    }\n\n    // Fallback: merge in default viewport if z-order matches, otherwise create a new viewport\n    if (window->Viewport == NULL)\n        if (!UpdateTryMergeWindowIntoHostViewport(window, main_viewport))\n            window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);\n\n    // Mark window as allowed to protrude outside of its viewport and into the current monitor\n    if (!lock_viewport)\n    {\n        if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))\n        {\n            // We need to take account of the possibility that mouse may become invalid.\n            // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds.\n            ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos;\n            bool use_mouse_ref = (!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !g.NavWindow);\n            bool mouse_valid = IsMousePosValid(&mouse_ref);\n            if ((window->Appearing || (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildMenu))) && (!use_mouse_ref || mouse_valid))\n                window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos(window->Flags));\n            else\n                window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;\n        }\n        else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow) && window->DockNode == NULL)\n        {\n            // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code.\n            const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true;\n            if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible)\n            {\n                // Steal/transfer ownership\n                IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] Window '%s' steal Viewport %08X from Window '%s'\\n\", window->Name, window->Viewport->ID, window->Viewport->Window->Name);\n                window->Viewport->Window = window;\n                window->Viewport->ID = window->ID;\n                window->Viewport->LastNameHash = 0;\n            }\n            else if (!UpdateTryMergeWindowIntoHostViewports(window)) // Merge?\n            {\n                // New viewport\n                window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);\n            }\n        }\n        else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0)\n        {\n            // Regular (non-child, non-popup) windows by default are also allowed to protrude\n            // Child windows are kept contained within their parent.\n            window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;\n        }\n    }\n\n    // Update flags\n    window->ViewportOwned = (window == window->Viewport->Window);\n    window->ViewportId = window->Viewport->ID;\n\n    // If the OS window has a title bar, hide our imgui title bar\n    //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration))\n    //    window->Flags |= ImGuiWindowFlags_NoTitleBar;\n}\n\nvoid ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack)\n{\n    ImGuiContext& g = *GImGui;\n\n    bool viewport_rect_changed = false;\n\n    // Synchronize window --> viewport in most situations\n    // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM\n    if (window->Viewport->PlatformRequestMove)\n    {\n        window->Pos = window->Viewport->Pos;\n        MarkIniSettingsDirty(window);\n    }\n    else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0)\n    {\n        viewport_rect_changed = true;\n        window->Viewport->Pos = window->Pos;\n    }\n\n    if (window->Viewport->PlatformRequestResize)\n    {\n        window->Size = window->SizeFull = window->Viewport->Size;\n        MarkIniSettingsDirty(window);\n    }\n    else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0)\n    {\n        viewport_rect_changed = true;\n        window->Viewport->Size = window->Size;\n    }\n    window->Viewport->UpdateWorkRect();\n\n    // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame()\n    // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect.\n    if (viewport_rect_changed)\n        UpdateViewportPlatformMonitor(window->Viewport);\n\n    // Update common viewport flags\n    const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear;\n    ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear;\n    ImGuiWindowFlags window_flags = window->Flags;\n    const bool is_modal = (window_flags & ImGuiWindowFlags_Modal) != 0;\n    const bool is_short_lived_floating_window = (window_flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0;\n    if (window_flags & ImGuiWindowFlags_Tooltip)\n        viewport_flags |= ImGuiViewportFlags_TopMost;\n    if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal)\n        viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon;\n    if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window)\n        viewport_flags |= ImGuiViewportFlags_NoDecoration;\n\n    // Not correct to set modal as topmost because:\n    // - Because other popups can be stacked above a modal (e.g. combo box in a modal)\n    // - ImGuiViewportFlags_TopMost is currently handled different in backends: in Win32 it is \"appear top most\" whereas in GLFW and SDL it is \"stay topmost\"\n    //if (flags & ImGuiWindowFlags_Modal)\n    //    viewport_flags |= ImGuiViewportFlags_TopMost;\n\n    // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them\n    // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration).\n    // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app,\n    // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere.\n    if (is_short_lived_floating_window && !is_modal)\n        viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick;\n\n    // We can overwrite viewport flags using ImGuiWindowClass (advanced users)\n    if (window->WindowClass.ViewportFlagsOverrideSet)\n        viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet;\n    if (window->WindowClass.ViewportFlagsOverrideClear)\n        viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear;\n\n    // We can also tell the backend that clearing the platform window won't be necessary,\n    // as our window background is filling the viewport and we have disabled BgAlpha.\n    // FIXME: Work on support for per-viewport transparency (#2766)\n    if (!(window_flags & ImGuiWindowFlags_NoBackground))\n        viewport_flags |= ImGuiViewportFlags_NoRendererClear;\n\n    window->Viewport->Flags = viewport_flags;\n\n    // Update parent viewport ID\n    // (the !IsFallbackWindow test mimic the one done in WindowSelectViewport())\n    if (window->WindowClass.ParentViewportId != (ImGuiID)-1)\n    {\n        ImGuiID old_parent_viewport_id = window->Viewport->ParentViewportId;\n        window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId;\n        if (window->Viewport->ParentViewportId != old_parent_viewport_id)\n            window->Viewport->ParentViewport = FindViewportByID(window->Viewport->ParentViewportId);\n    }\n    else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack && (!parent_window_in_stack->IsFallbackWindow || parent_window_in_stack->WasActive))\n    {\n        window->Viewport->ParentViewport = parent_window_in_stack->Viewport;\n        window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID;\n    }\n    else\n    {\n        window->Viewport->ParentViewport = g.IO.ConfigViewportsNoDefaultParent ? NULL : GetMainViewport();\n        window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID;\n    }\n}\n\n// Called by user at the end of the main loop, after EndFrame()\n// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api.\nvoid ImGui::UpdatePlatformWindows()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.FrameCountEnded == g.FrameCount && \"Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?\");\n    IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount);\n    g.FrameCountPlatformEnded = g.FrameCount;\n    if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))\n        return;\n\n    // Create/resize/destroy platform windows to match each active viewport.\n    // Skip the main viewport (index 0), which is always fully handled by the application!\n    for (int i = 1; i < g.Viewports.Size; i++)\n    {\n        ImGuiViewportP* viewport = g.Viewports[i];\n\n        // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window\n        // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame)\n        bool destroy_platform_window = false;\n        destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1);\n        destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window));\n        if (destroy_platform_window)\n        {\n            DestroyPlatformWindow(viewport);\n            continue;\n        }\n\n        // New windows that appears directly in a new viewport won't always have a size on their first frame\n        if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0)\n            continue;\n\n        // Create window\n        const bool is_new_platform_window = (viewport->PlatformWindowCreated == false);\n        if (is_new_platform_window)\n        {\n            IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] Create Platform Window %08X '%s'\\n\", viewport->ID, viewport->Window ? viewport->Window->Name : \"n/a\");\n            g.PlatformIO.Platform_CreateWindow(viewport);\n            if (g.PlatformIO.Renderer_CreateWindow != NULL)\n                g.PlatformIO.Renderer_CreateWindow(viewport);\n            g.PlatformWindowsCreatedCount++;\n            viewport->LastNameHash = 0;\n            viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?)\n            viewport->LastRendererSize = viewport->Size;                                       // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it.\n            viewport->PlatformWindowCreated = true;\n        }\n\n        // Apply Position and Size (from ImGui to Platform/Renderer backends)\n        if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove)\n            g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos);\n        if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize)\n            g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size);\n        if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize)\n            g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size);\n        viewport->LastPlatformPos = viewport->Pos;\n        viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size;\n\n        // Update title bar (if it changed)\n        if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window))\n        {\n            const char* title_begin = window_for_title->Name;\n            char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin);\n            const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin);\n            if (viewport->LastNameHash != title_hash)\n            {\n                char title_end_backup_c = *title_end;\n                *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain.\n                g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin);\n                *title_end = title_end_backup_c;\n                viewport->LastNameHash = title_hash;\n            }\n        }\n\n        // Update alpha (if it changed)\n        if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha)\n            g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha);\n        viewport->LastAlpha = viewport->Alpha;\n\n        // Optional, general purpose call to allow the backend to perform general book-keeping even if things haven't changed.\n        if (g.PlatformIO.Platform_UpdateWindow)\n            g.PlatformIO.Platform_UpdateWindow(viewport);\n\n        if (is_new_platform_window)\n        {\n            // On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late)\n            if (g.FrameCount < 3)\n                viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing;\n\n            // Show window\n            g.PlatformIO.Platform_ShowWindow(viewport);\n        }\n\n        // Clear request flags\n        viewport->ClearRequestFlags();\n    }\n}\n\n// This is a default/basic function for performing the rendering/swap of multiple Platform Windows.\n// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves.\n// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself:\n//\n//    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();\n//    for (int i = 1; i < platform_io.Viewports.Size; i++)\n//        if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)\n//            MyRenderFunction(platform_io.Viewports[i], my_args);\n//    for (int i = 1; i < platform_io.Viewports.Size; i++)\n//        if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)\n//            MySwapBufferFunction(platform_io.Viewports[i], my_args);\n//\nvoid ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg)\n{\n    // Skip the main viewport (index 0), which is always fully handled by the application!\n    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();\n    for (int i = 1; i < platform_io.Viewports.Size; i++)\n    {\n        ImGuiViewport* viewport = platform_io.Viewports[i];\n        if (viewport->Flags & ImGuiViewportFlags_IsMinimized)\n            continue;\n        if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg);\n        if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg);\n    }\n    for (int i = 1; i < platform_io.Viewports.Size; i++)\n    {\n        ImGuiViewport* viewport = platform_io.Viewports[i];\n        if (viewport->Flags & ImGuiViewportFlags_IsMinimized)\n            continue;\n        if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg);\n        if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg);\n    }\n}\n\nstatic int ImGui::FindPlatformMonitorForPos(const ImVec2& pos)\n{\n    ImGuiContext& g = *GImGui;\n    for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)\n    {\n        const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];\n        if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos))\n            return monitor_n;\n    }\n    return -1;\n}\n\n// Search for the monitor with the largest intersection area with the given rectangle\n// We generally try to avoid searching loops but the monitor count should be very small here\n// FIXME-OPT: We could test the last monitor used for that viewport first, and early\nstatic int ImGui::FindPlatformMonitorForRect(const ImRect& rect)\n{\n    ImGuiContext& g = *GImGui;\n\n    const int monitor_count = g.PlatformIO.Monitors.Size;\n    if (monitor_count <= 1)\n        return monitor_count - 1;\n\n    // Use a minimum threshold of 1.0f so a zero-sized rect won't false positive, and will still find the correct monitor given its position.\n    // This is necessary for tooltips which always resize down to zero at first.\n    const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f);\n    int best_monitor_n = 0; // Default to the first monitor as fallback\n    float best_monitor_surface = 0.001f;\n\n    for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++)\n    {\n        const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];\n        const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize);\n        if (monitor_rect.Contains(rect))\n            return monitor_n;\n        ImRect overlapping_rect = rect;\n        overlapping_rect.ClipWithFull(monitor_rect);\n        float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight();\n        if (overlapping_surface < best_monitor_surface)\n            continue;\n        best_monitor_surface = overlapping_surface;\n        best_monitor_n = monitor_n;\n    }\n    return best_monitor_n;\n}\n\n// Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor)\nstatic void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport)\n{\n    viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect());\n}\n\n// Return value is always != NULL, but don't hold on it across frames.\nconst ImGuiPlatformMonitor* ImGui::GetViewportPlatformMonitor(ImGuiViewport* viewport_p)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)viewport_p;\n    int monitor_idx = viewport->PlatformMonitor;\n    if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)\n        return &g.PlatformIO.Monitors[monitor_idx];\n    return &g.FallbackMonitor;\n}\n\nvoid ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport)\n{\n    ImGuiContext& g = *GImGui;\n    if (viewport->PlatformWindowCreated)\n    {\n        IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] Destroy Platform Window %08X '%s'\\n\", viewport->ID, viewport->Window ? viewport->Window->Name : \"n/a\");\n        if (g.PlatformIO.Renderer_DestroyWindow)\n            g.PlatformIO.Renderer_DestroyWindow(viewport);\n        if (g.PlatformIO.Platform_DestroyWindow)\n            g.PlatformIO.Platform_DestroyWindow(viewport);\n        IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL);\n\n        // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize()\n        // The righter way may be to leave it to the backend to set this flag all-together, and made the flag public.\n        if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID)\n            viewport->PlatformWindowCreated = false;\n    }\n    else\n    {\n        IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL);\n    }\n    viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL;\n    viewport->ClearRequestFlags();\n}\n\nvoid ImGui::DestroyPlatformWindows()\n{\n    // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the backend\n    // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData.\n    // It is convenient for the platform backend code to store something in the main viewport, in order for e.g. the mouse handling\n    // code to operator a consistent manner.\n    // It is expected that the backend can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without\n    // crashing if it doesn't have data stored.\n    ImGuiContext& g = *GImGui;\n    for (ImGuiViewportP* viewport : g.Viewports)\n        DestroyPlatformWindow(viewport);\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] DOCKING\n//-----------------------------------------------------------------------------\n// Docking: Internal Types\n// Docking: Forward Declarations\n// Docking: ImGuiDockContext\n// Docking: ImGuiDockContext Docking/Undocking functions\n// Docking: ImGuiDockNode\n// Docking: ImGuiDockNode Tree manipulation functions\n// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)\n// Docking: Builder Functions\n// Docking: Begin/End Support Functions (called from Begin/End)\n// Docking: Settings\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// Typical Docking call flow: (root level is generally public API):\n//-----------------------------------------------------------------------------\n// - NewFrame()                               new dear imgui frame\n//    | DockContextNewFrameUpdateUndocking()  - process queued undocking requests\n//    | - DockContextProcessUndockWindow()    - process one window undocking request\n//    | - DockContextProcessUndockNode()      - process one whole node undocking request\n//    | DockContextNewFrameUpdateUndocking()  - process queue docking requests, create floating dock nodes\n//    | - update g.HoveredDockNode            - [debug] update node hovered by mouse\n//    | - DockContextProcessDock()            - process one docking request\n//    | - DockNodeUpdate()\n//    |   - DockNodeUpdateForRootNode()\n//    |     - DockNodeUpdateFlagsAndCollapse()\n//    |     - DockNodeFindInfo()\n//    |   - destroy unused node or tab bar\n//    |   - create dock node host window\n//    |      - Begin() etc.\n//    |   - DockNodeStartMouseMovingWindow()\n//    |   - DockNodeTreeUpdatePosSize()\n//    |   - DockNodeTreeUpdateSplitter()\n//    |   - draw node background\n//    |   - DockNodeUpdateTabBar()            - create/update tab bar for a docking node\n//    |     - DockNodeAddTabBar()\n//    |     - DockNodeWindowMenuUpdate()\n//    |     - DockNodeCalcTabBarLayout()\n//    |     - BeginTabBarEx()\n//    |     - TabItemEx() calls\n//    |     - EndTabBar()\n//    |   - BeginDockableDragDropTarget()\n//    |      - DockNodeUpdate()               - recurse into child nodes...\n//-----------------------------------------------------------------------------\n// - DockSpace()                              user submit a dockspace into a window\n//    | Begin(Child)                          - create a child window\n//    | DockNodeUpdate()                      - call main dock node update function\n//    | End(Child)\n//    | ItemSize()\n//-----------------------------------------------------------------------------\n// - Begin()\n//    | BeginDocked()\n//    | BeginDockableDragDropSource()\n//    | BeginDockableDragDropTarget()\n//    | - DockNodePreviewDockRender()\n//-----------------------------------------------------------------------------\n// - EndFrame()\n//    | DockContextEndFrame()\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// Docking: Internal Types\n//-----------------------------------------------------------------------------\n// - ImGuiDockRequestType\n// - ImGuiDockRequest\n// - ImGuiDockPreviewData\n// - ImGuiDockNodeSettings\n// - ImGuiDockContext\n//-----------------------------------------------------------------------------\n\nenum ImGuiDockRequestType\n{\n    ImGuiDockRequestType_None = 0,\n    ImGuiDockRequestType_Dock,\n    ImGuiDockRequestType_Undock,\n    ImGuiDockRequestType_Split                  // Split is the same as Dock but without a DockPayload\n};\n\nstruct ImGuiDockRequest\n{\n    ImGuiDockRequestType    Type;\n    ImGuiWindow*            DockTargetWindow;   // Destination/Target Window to dock into (may be a loose window or a DockNode, might be NULL in which case DockTargetNode cannot be NULL)\n    ImGuiDockNode*          DockTargetNode;     // Destination/Target Node to dock into\n    ImGuiWindow*            DockPayload;        // Source/Payload window to dock (may be a loose window or a DockNode), [Optional]\n    ImGuiDir                DockSplitDir;\n    float                   DockSplitRatio;\n    bool                    DockSplitOuter;\n    ImGuiWindow*            UndockTargetWindow;\n    ImGuiDockNode*          UndockTargetNode;\n\n    ImGuiDockRequest()\n    {\n        Type = ImGuiDockRequestType_None;\n        DockTargetWindow = DockPayload = UndockTargetWindow = NULL;\n        DockTargetNode = UndockTargetNode = NULL;\n        DockSplitDir = ImGuiDir_None;\n        DockSplitRatio = 0.5f;\n        DockSplitOuter = false;\n    }\n};\n\nstruct ImGuiDockPreviewData\n{\n    ImGuiDockNode   FutureNode;\n    bool            IsDropAllowed;\n    bool            IsCenterAvailable;\n    bool            IsSidesAvailable;           // Hold your breath, grammar freaks..\n    bool            IsSplitDirExplicit;         // Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window)\n    ImGuiDockNode*  SplitNode;\n    ImGuiDir        SplitDir;\n    float           SplitRatio;\n    ImRect          DropRectsDraw[ImGuiDir_COUNT + 1];  // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects()\n\n    ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; for (int n = 0; n < IM_COUNTOF(DropRectsDraw); n++) DropRectsDraw[n] = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); }\n};\n\n// Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes)\nstruct ImGuiDockNodeSettings\n{\n    ImGuiID             ID;\n    ImGuiID             ParentNodeId;\n    ImGuiID             ParentWindowId;\n    ImGuiID             SelectedTabId;\n    signed char         SplitAxis;\n    char                Depth;\n    ImGuiDockNodeFlags  Flags;                  // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_)\n    ImVec2ih            Pos;\n    ImVec2ih            Size;\n    ImVec2ih            SizeRef;\n    ImGuiDockNodeSettings() { memset((void*)this, 0, sizeof(*this)); SplitAxis = ImGuiAxis_None; }\n};\n\n//-----------------------------------------------------------------------------\n// Docking: Forward Declarations\n//-----------------------------------------------------------------------------\n\nnamespace ImGui\n{\n    // ImGuiDockContext\n    static ImGuiDockNode*   DockContextAddNode(ImGuiContext* ctx, ImGuiID id);\n    static void             DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node);\n    static void             DockContextDeleteNode(ImGuiContext* ctx, ImGuiDockNode* node);\n    static void             DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node);\n    static void             DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req);\n    static void             DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx);\n    static ImGuiDockNode*   DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window);\n    static void             DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count);\n    static void             DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id);                            // Use root_id==0 to add all\n\n    // ImGuiDockNode\n    static void             DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar);\n    static void             DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);\n    static void             DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);\n    static ImGuiWindow*     DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id);\n    static void             DockNodeApplyPosSizeToWindows(ImGuiDockNode* node);\n    static void             DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id);\n    static void             DockNodeHideHostWindow(ImGuiDockNode* node);\n    static void             DockNodeUpdate(ImGuiDockNode* node);\n    static void             DockNodeUpdateForRootNode(ImGuiDockNode* node);\n    static void             DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node);\n    static void             DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node);\n    static void             DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window);\n    static void             DockNodeAddTabBar(ImGuiDockNode* node);\n    static void             DockNodeRemoveTabBar(ImGuiDockNode* node);\n    static void             DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar);\n    static void             DockNodeUpdateVisibleFlag(ImGuiDockNode* node);\n    static void             DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window);\n    static bool             DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window);\n    static void             DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking);\n    static void             DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data);\n    static void             DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos);\n    static void             DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired);\n    static bool             DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos);\n    static const char*      DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, \"##DockNode_%02X\", node->ID); return buf; }\n    static int              DockNodeGetTabOrder(ImGuiWindow* window);\n\n    // ImGuiDockNode tree manipulations\n    static void             DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node);\n    static void             DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child);\n    static void             DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node = NULL);\n    static void             DockNodeTreeUpdateSplitter(ImGuiDockNode* node);\n    static ImGuiDockNode*   DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos);\n    static ImGuiDockNode*   DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node);\n\n    // Settings\n    static void             DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id);\n    static void             DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count);\n    static ImGuiDockNodeSettings*   DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id);\n    static void             DockSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);\n    static void             DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);\n    static void*            DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);\n    static void             DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);\n    static void             DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);\n}\n\n//-----------------------------------------------------------------------------\n// Docking: ImGuiDockContext\n//-----------------------------------------------------------------------------\n// The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings,\n// or we always hold the entire docking node tree. Nodes are frequently hidden, e.g. if the window(s) or child nodes they host are not active.\n// At boot time only, we run a simple GC to remove nodes that have no references.\n// Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures),\n// we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does).\n// This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data.\n//-----------------------------------------------------------------------------\n// - DockContextInitialize()\n// - DockContextShutdown()\n// - DockContextClearNodes()\n// - DockContextRebuildNodes()\n// - DockContextNewFrameUpdateUndocking()\n// - DockContextNewFrameUpdateDocking()\n// - DockContextEndFrame()\n// - DockContextFindNodeByID()\n// - DockContextBindNodeToWindow()\n// - DockContextGenNodeID()\n// - DockContextAddNode()\n// - DockContextRemoveNode()\n// - ImGuiDockContextPruneNodeData\n// - DockContextPruneUnusedSettingsNodes()\n// - DockContextBuildNodesFromSettings()\n// - DockContextBuildAddWindowsToNodes()\n//-----------------------------------------------------------------------------\n\nvoid ImGui::DockContextInitialize(ImGuiContext* ctx)\n{\n    ImGuiContext& g = *ctx;\n\n    // Add .ini handle for persistent docking data\n    ImGuiSettingsHandler ini_handler;\n    ini_handler.TypeName = \"Docking\";\n    ini_handler.TypeHash = ImHashStr(\"Docking\");\n    ini_handler.ClearAllFn = DockSettingsHandler_ClearAll;\n    ini_handler.ReadInitFn = DockSettingsHandler_ClearAll; // Also clear on read\n    ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen;\n    ini_handler.ReadLineFn = DockSettingsHandler_ReadLine;\n    ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll;\n    ini_handler.WriteAllFn = DockSettingsHandler_WriteAll;\n    g.SettingsHandlers.push_back(ini_handler);\n\n    g.DockNodeWindowMenuHandler = &DockNodeWindowMenuHandler_Default;\n}\n\nvoid ImGui::DockContextShutdown(ImGuiContext* ctx)\n{\n    ImGuiDockContext* dc = &ctx->DockContext;\n    for (int n = 0; n < dc->Nodes.Data.Size; n++)\n        if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)\n            DockContextDeleteNode(ctx, node);\n}\n\nvoid ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs)\n{\n    IM_UNUSED(ctx);\n    IM_ASSERT(ctx == GImGui);\n    DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs);\n    DockBuilderRemoveNodeChildNodes(root_id);\n}\n\n// [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch\n// (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!)\nvoid ImGui::DockContextRebuildNodes(ImGuiContext* ctx)\n{\n    ImGuiContext& g = *ctx;\n    ImGuiDockContext* dc = &ctx->DockContext;\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockContextRebuildNodes\\n\");\n    SaveIniSettingsToMemory();\n    ImGuiID root_id = 0; // Rebuild all\n    DockContextClearNodes(ctx, root_id, false);\n    DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);\n    DockContextBuildAddWindowsToNodes(ctx, root_id);\n}\n\n// Docking context update function, called by NewFrame()\nvoid ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx)\n{\n    ImGuiContext& g = *ctx;\n    ImGuiDockContext* dc = &ctx->DockContext;\n    if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))\n    {\n        if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0)\n            DockContextClearNodes(ctx, 0, true);\n        return;\n    }\n\n    // Setting NoSplit at runtime merges all nodes\n    if (g.IO.ConfigDockingNoSplit)\n        for (int n = 0; n < dc->Nodes.Data.Size; n++)\n            if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)\n                if (node->IsRootNode() && node->IsSplitNode())\n                {\n                    DockBuilderRemoveNodeChildNodes(node->ID);\n                    //dc->WantFullRebuild = true;\n                }\n\n    // Process full rebuild\n#if 0\n    if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))\n        dc->WantFullRebuild = true;\n#endif\n    if (dc->WantFullRebuild)\n    {\n        DockContextRebuildNodes(ctx);\n        dc->WantFullRebuild = false;\n    }\n\n    // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame)\n    for (ImGuiDockRequest& req : dc->Requests)\n    {\n        if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetWindow)\n            DockContextProcessUndockWindow(ctx, req.UndockTargetWindow);\n        else if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetNode)\n            DockContextProcessUndockNode(ctx, req.UndockTargetNode);\n    }\n}\n\n// Docking context update function, called by NewFrame()\nvoid ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx)\n{\n    ImGuiContext& g = *ctx;\n    ImGuiDockContext* dc = &ctx->DockContext;\n    if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))\n        return;\n\n    // [DEBUG] Store hovered dock node.\n    // We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut.\n    // Note this is mostly a debug thing and isn't actually used for docking target, because docking involve more detailed filtering.\n    g.DebugHoveredDockNode = NULL;\n    if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow)\n    {\n        if (hovered_window->DockNodeAsHost)\n            g.DebugHoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos);\n        else if (hovered_window->RootWindow->DockNode)\n            g.DebugHoveredDockNode = hovered_window->RootWindow->DockNode;\n    }\n\n    // Process Docking requests\n    for (ImGuiDockRequest& req : dc->Requests)\n        if (req.Type == ImGuiDockRequestType_Dock)\n            DockContextProcessDock(ctx, &req);\n    dc->Requests.resize(0);\n\n    // Create windows for each automatic docking nodes\n    // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high)\n    for (int n = 0; n < dc->Nodes.Data.Size; n++)\n        if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)\n            if (node->IsFloatingNode())\n                DockNodeUpdate(node);\n}\n\nvoid ImGui::DockContextEndFrame(ImGuiContext* ctx)\n{\n    // Draw backgrounds of node missing their window\n    ImGuiContext& g = *ctx;\n    ImGuiDockContext* dc = &g.DockContext;\n    for (int n = 0; n < dc->Nodes.Data.Size; n++)\n        if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)\n            if (node->LastFrameActive == g.FrameCount && node->IsVisible && node->HostWindow && node->IsLeafNode() && !node->IsBgDrawnThisFrame)\n            {\n                ImRect bg_rect(node->Pos + ImVec2(0.0f, GetFrameHeight()), node->Pos + node->Size);\n                ImDrawFlags bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, node->HostWindow->Rect(), g.Style.DockingSeparatorSize);\n                node->HostWindow->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);\n                node->HostWindow->DrawList->AddRectFilled(bg_rect.Min, bg_rect.Max, node->LastBgColor, node->HostWindow->WindowRounding, bg_rounding_flags);\n            }\n}\n\nImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id)\n{\n    return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id);\n}\n\nImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx)\n{\n    // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used)\n    // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry.0\n    // We should poke in ctx->Nodes to find a suitable ID faster. Even more so trivial that ctx->Nodes lookup is already sorted.\n    ImGuiID id = 0x0001;\n    while (DockContextFindNodeByID(ctx, id) != NULL)\n        id++;\n    return id;\n}\n\nstatic ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id)\n{\n    // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window.\n    ImGuiContext& g = *ctx;\n    if (id == 0)\n        id = DockContextGenNodeID(ctx);\n    else\n        IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL);\n\n    // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings!\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockContextAddNode 0x%08X\\n\", id);\n    ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id);\n    ctx->DockContext.Nodes.SetVoidPtr(node->ID, node);\n    return node;\n}\n\nstatic void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node)\n{\n    ImGuiContext& g = *ctx;\n\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockContextRemoveNode 0x%08X\\n\", node->ID);\n    IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node);\n    IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL);\n    IM_ASSERT(node->Windows.Size == 0);\n\n    if (node->HostWindow)\n        node->HostWindow->DockNodeAsHost = NULL;\n\n    ImGuiDockNode* parent_node = node->ParentNode;\n    const bool merge = (merge_sibling_into_parent_node && parent_node != NULL);\n    if (merge)\n    {\n        IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node);\n        ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]);\n        DockNodeTreeMerge(&g, parent_node, sibling_node);\n    }\n    else\n    {\n        for (int n = 0; parent_node && n < IM_COUNTOF(parent_node->ChildNodes); n++)\n            if (parent_node->ChildNodes[n] == node)\n                node->ParentNode->ChildNodes[n] = NULL;\n        DockContextDeleteNode(ctx, node);\n    }\n}\n\n// Raw-ish delete\nstatic void ImGui::DockContextDeleteNode(ImGuiContext* ctx, ImGuiDockNode* node)\n{\n    ImGuiDockContext* dc = &ctx->DockContext;\n    if (node->TabBar)\n        IM_DELETE(node->TabBar);\n    node->TabBar = NULL;\n    dc->Nodes.SetVoidPtr(node->ID, NULL);\n    IM_DELETE(node);\n}\n\nstatic int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs)\n{\n    const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs;\n    const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs;\n    return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a);\n}\n\n// Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here.\nstruct ImGuiDockContextPruneNodeData\n{\n    int         CountWindows, CountChildWindows, CountChildNodes;\n    ImGuiID     RootId;\n    ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootId = 0; }\n};\n\n// Garbage collect unused nodes (run once at init time)\nstatic void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx)\n{\n    ImGuiContext& g = *ctx;\n    ImGuiDockContext* dc = &ctx->DockContext;\n    IM_ASSERT(g.Windows.Size == 0);\n\n    ImPool<ImGuiDockContextPruneNodeData> pool;\n    pool.Reserve(dc->NodesSettings.Size);\n\n    // Count child nodes and compute RootID\n    for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)\n    {\n        ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];\n        if (pool.GetByKey(settings->ID) != 0)\n        {\n            settings->ID = 0; // Duplicate\n            continue;\n        }\n        ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0;\n        pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID;\n        if (settings->ParentNodeId)\n            pool.GetOrAddByKey(settings->ParentNodeId)->CountChildNodes++;\n    }\n\n    // Count reference to dock ids from dockspaces\n    // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes()\n    for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)\n    {\n        ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];\n        if (settings->ParentWindowId != 0)\n            if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->ParentWindowId))\n                if (window_settings->DockId)\n                    if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId))\n                        data->CountChildNodes++;\n    }\n\n    // Count reference to dock ids from window settings\n    // We guard against the possibility of an invalid .ini file (RootID may point to a missing node)\n    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n        if (ImGuiID dock_id = settings->DockId)\n            if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id))\n            {\n                data->CountWindows++;\n                if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId))\n                    data_root->CountChildWindows++;\n            }\n\n    // Prune\n    for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)\n    {\n        ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];\n        ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID);\n        if (data == NULL || data->CountWindows > 1)\n            continue;\n        ImGuiDockContextPruneNodeData* data_root = (settings->ID == data->RootId) ? data : pool.GetByKey(data->RootId);\n        ImGuiDockContextPruneNodeData* data_parent = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : NULL;\n\n        bool remove = false;\n        remove |= (data->CountWindows == 1 && settings->ParentNodeId == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode));  // Floating root node with only 1 window\n        remove |= (data->CountWindows == 0 && settings->ParentNodeId == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window\n        remove |= (data_root == NULL || data_root->CountChildWindows == 0);\n        if (remove)\n        {\n            IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockContextPruneUnusedSettingsNodes: Prune 0x%08X\\n\", settings->ID);\n            DockSettingsRemoveNodeReferences(&settings->ID, 1);\n            settings->ID = 0;\n        }\n        else if (data_parent && data_parent->CountChildNodes == 1)\n        {\n            IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockContextPruneUnusedSettingsNodes: Merge 0x%08X->0X%08X\\n\", settings->ID, settings->ParentNodeId);\n            DockSettingsRenameNodeReferences(settings->ID, settings->ParentNodeId);\n            settings->ID = 0;\n        }\n    }\n}\n\nstatic void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count)\n{\n    // Build nodes\n    ImGuiContext& g = *ctx; IM_UNUSED(g);\n    for (int node_n = 0; node_n < node_settings_count; node_n++)\n    {\n        ImGuiDockNodeSettings* settings = &node_settings_array[node_n];\n        if (settings->ID == 0)\n            continue;\n        if (DockContextFindNodeByID(ctx, settings->ID) != NULL)\n        {\n            IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockContextBuildNodesFromSettings: skip duplicate node 0x%08X\\n\", settings->ID);\n            continue;\n        }\n        ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID);\n        node->ParentNode = settings->ParentNodeId ? DockContextFindNodeByID(ctx, settings->ParentNodeId) : NULL;\n        node->Pos = ImVec2(settings->Pos.x, settings->Pos.y);\n        node->Size = ImVec2(settings->Size.x, settings->Size.y);\n        node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y);\n        node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_DockNode;\n        if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL)\n            node->ParentNode->ChildNodes[0] = node;\n        else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL)\n            node->ParentNode->ChildNodes[1] = node;\n        node->SelectedTabId = settings->SelectedTabId;\n        node->SplitAxis = (ImGuiAxis)settings->SplitAxis;\n        node->SetLocalFlags(settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_);\n\n        // Bind host window immediately if it already exist (in case of a rebuild)\n        // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set.\n        char host_window_title[20];\n        ImGuiDockNode* root_node = DockNodeGetRootNode(node);\n        node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_COUNTOF(host_window_title)));\n    }\n}\n\nvoid ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id)\n{\n    // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame)\n    ImGuiContext& g = *ctx;\n    for (ImGuiWindow* window : g.Windows)\n    {\n        if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1)\n            continue;\n        if (window->DockNode != NULL)\n            continue;\n\n        ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);\n        IM_ASSERT(node != NULL);   // This should have been called after DockContextBuildNodesFromSettings()\n        if (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id)\n            DockNodeAddWindow(node, window, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Docking: ImGuiDockContext Docking/Undocking functions\n//-----------------------------------------------------------------------------\n// - DockContextQueueDock()\n// - DockContextQueueUndockWindow()\n// - DockContextQueueUndockNode()\n// - DockContextQueueNotifyRemovedNode()\n// - DockContextProcessDock()\n// - DockContextProcessUndockWindow()\n// - DockContextProcessUndockNode()\n// - DockContextCalcDropPosForDocking()\n//-----------------------------------------------------------------------------\n\nvoid ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer)\n{\n    IM_ASSERT(target != payload);\n    ImGuiDockRequest req;\n    req.Type = ImGuiDockRequestType_Dock;\n    req.DockTargetWindow = target;\n    req.DockTargetNode = target_node;\n    req.DockPayload = payload;\n    req.DockSplitDir = split_dir;\n    req.DockSplitRatio = split_ratio;\n    req.DockSplitOuter = split_outer;\n    ctx->DockContext.Requests.push_back(req);\n}\n\nvoid ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window)\n{\n    ImGuiDockRequest req;\n    req.Type = ImGuiDockRequestType_Undock;\n    req.UndockTargetWindow = window;\n    ctx->DockContext.Requests.push_back(req);\n}\n\nvoid ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)\n{\n    ImGuiDockRequest req;\n    req.Type = ImGuiDockRequestType_Undock;\n    req.UndockTargetNode = node;\n    ctx->DockContext.Requests.push_back(req);\n}\n\nvoid ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node)\n{\n    ImGuiDockContext* dc = &ctx->DockContext;\n    for (ImGuiDockRequest& req : dc->Requests)\n        if (req.DockTargetNode == node)\n            req.Type = ImGuiDockRequestType_None;\n}\n\nvoid ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)\n{\n    IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL));\n    IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL);\n\n    ImGuiContext& g = *ctx;\n    IM_UNUSED(g);\n\n    ImGuiWindow* payload_window = req->DockPayload;     // Optional\n    ImGuiWindow* target_window = req->DockTargetWindow;\n    ImGuiDockNode* node = req->DockTargetNode;\n    if (payload_window)\n        IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockContextProcessDock node 0x%08X target '%s' dock window '%s', split_dir %d\\n\", node ? node->ID : 0, target_window ? target_window->Name : \"NULL\", payload_window->Name, req->DockSplitDir);\n    else\n        IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockContextProcessDock node 0x%08X, split_dir %d\\n\", node ? node->ID : 0, req->DockSplitDir);\n\n    // Decide which Tab will be selected at the end of the operation\n    ImGuiID next_selected_id = 0;\n    ImGuiDockNode* payload_node = NULL;\n    if (payload_window)\n    {\n        payload_node = payload_window->DockNodeAsHost;\n        payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later.\n        if (payload_node && payload_node->IsLeafNode())\n            next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId;\n        if (payload_node == NULL)\n            next_selected_id = payload_window->TabId;\n    }\n\n    // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well\n    // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==.\n    if (node)\n        IM_ASSERT(node->LastFrameAlive <= g.FrameCount);\n    if (node && target_window && node == target_window->DockNodeAsHost)\n        IM_ASSERT(node->Windows.Size > 0 || node->IsSplitNode() || node->IsCentralNode());\n\n    // Create new node and add existing window to it\n    if (node == NULL)\n    {\n        node = DockContextAddNode(ctx, 0);\n        node->Pos = target_window->Pos;\n        node->Size = target_window->Size;\n        if (target_window->DockNodeAsHost == NULL)\n        {\n            DockNodeAddWindow(node, target_window, true);\n            node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted;\n            target_window->DockIsActive = true;\n        }\n    }\n\n    ImGuiDir split_dir = req->DockSplitDir;\n    if (split_dir != ImGuiDir_None)\n    {\n        // Split into two, one side will be our payload node unless we are dropping a loose window\n        const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;\n        const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; // Current contents will be moved to the opposite side\n        const float split_ratio = req->DockSplitRatio;\n        DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node);  // payload_node may be NULL here!\n        ImGuiDockNode* new_node = node->ChildNodes[split_inheritor_child_idx ^ 1];\n        new_node->HostWindow = node->HostWindow;\n        node = new_node;\n    }\n    node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);\n\n    if (node != payload_node)\n    {\n        // Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!)\n        if (node->Windows.Size > 0 && node->TabBar == NULL)\n        {\n            DockNodeAddTabBar(node);\n            for (int n = 0; n < node->Windows.Size; n++)\n                TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);\n        }\n\n        if (payload_node != NULL)\n        {\n            // Transfer full payload node (with 1+ child windows or child nodes)\n            if (payload_node->IsSplitNode())\n            {\n                if (node->Windows.Size > 0)\n                {\n                    // We can dock a split payload into a node that already has windows _only_ if our payload is a node tree with a single visible node.\n                    // In this situation, we move the windows of the target node into the currently visible node of the payload.\n                    // This allows us to preserve some of the underlying dock tree settings nicely.\n                    IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted.\n                    ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows;\n                    if (visible_node->TabBar)\n                        IM_ASSERT(visible_node->TabBar->Tabs.Size > 0);\n                    DockNodeMoveWindows(node, visible_node);\n                    DockNodeMoveWindows(visible_node, node);\n                    DockSettingsRenameNodeReferences(node->ID, visible_node->ID);\n                }\n                if (node->IsCentralNode())\n                {\n                    // Central node property needs to be moved to a leaf node, pick the last focused one.\n                    // FIXME-DOCK: If we had to transfer other flags here, what would the policy be?\n                    ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeId);\n                    IM_ASSERT(last_focused_node != NULL);\n                    ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node);\n                    IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node));\n                    last_focused_node->SetLocalFlags(last_focused_node->LocalFlags | ImGuiDockNodeFlags_CentralNode);\n                    node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_CentralNode);\n                    last_focused_root_node->CentralNode = last_focused_node;\n                }\n\n                IM_ASSERT(node->Windows.Size == 0);\n                DockNodeMoveChildNodes(node, payload_node);\n            }\n            else\n            {\n                const ImGuiID payload_dock_id = payload_node->ID;\n                DockNodeMoveWindows(node, payload_node);\n                DockSettingsRenameNodeReferences(payload_dock_id, node->ID);\n            }\n            DockContextRemoveNode(ctx, payload_node, true);\n        }\n        else if (payload_window)\n        {\n            // Transfer single window\n            const ImGuiID payload_dock_id = payload_window->DockId;\n            node->VisibleWindow = payload_window;\n            DockNodeAddWindow(node, payload_window, true);\n            if (payload_dock_id != 0)\n                DockSettingsRenameNodeReferences(payload_dock_id, node->ID);\n        }\n    }\n    else\n    {\n        // When docking a floating single window node we want to reevaluate auto-hiding of the tab bar\n        node->WantHiddenTabBarUpdate = true;\n    }\n\n    // Update selection immediately\n    if (ImGuiTabBar* tab_bar = node->TabBar)\n        tab_bar->NextSelectedTabId = next_selected_id;\n    MarkIniSettingsDirty();\n}\n\n// Problem:\n//   Undocking a large (~full screen) window would leave it so large that the bottom right sizing corner would more\n//   than likely be off the screen and the window would be hard to resize to fit on screen. This can be particularly problematic\n//   with 'ConfigWindowsMoveFromTitleBarOnly=true' and/or with 'ConfigWindowsResizeFromEdges=false' as well (the later can be\n//   due to missing ImGuiBackendFlags_HasMouseCursors backend flag).\n// Solution:\n//   When undocking a window we currently force its maximum size to 90% of the host viewport or monitor.\n// Reevaluate this when we implement preserving docked/undocked size (\"docking_wip/undocked_size\" branch).\nstatic ImVec2 FixLargeWindowsWhenUndocking(const ImVec2& size, ImGuiViewport* ref_viewport)\n{\n    if (ref_viewport == NULL)\n        return size;\n\n    ImGuiContext& g = *GImGui;\n    ImVec2 max_size = ImTrunc(ref_viewport->WorkSize * 0.90f);\n    if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)\n    {\n        const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ref_viewport);\n        max_size = ImTrunc(monitor->WorkSize * 0.90f);\n    }\n    return ImMin(size, max_size);\n}\n\nvoid ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref)\n{\n    ImGuiContext& g = *ctx;\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\\n\", window->Name, clear_persistent_docking_ref);\n    if (window->DockNode)\n        DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId);\n    else\n        window->DockId = 0;\n    window->Collapsed = false;\n    window->DockIsActive = false;\n    window->DockNodeIsVisible = window->DockTabIsVisible = false;\n    window->Size = window->SizeFull = FixLargeWindowsWhenUndocking(window->SizeFull, window->Viewport);\n\n    MarkIniSettingsDirty();\n}\n\nvoid ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)\n{\n    ImGuiContext& g = *ctx;\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockContextProcessUndockNode node %08X\\n\", node->ID);\n    IM_ASSERT(node->IsLeafNode());\n    IM_ASSERT(node->Windows.Size >= 1);\n\n    if (node->IsRootNode() || node->IsCentralNode())\n    {\n        // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload.\n        ImGuiDockNode* new_node = DockContextAddNode(ctx, 0);\n        new_node->Pos = node->Pos;\n        new_node->Size = node->Size;\n        new_node->SizeRef = node->SizeRef;\n        DockNodeMoveWindows(new_node, node);\n        DockSettingsRenameNodeReferences(node->ID, new_node->ID);\n        node = new_node;\n    }\n    else\n    {\n        // Otherwise extract our node and merge our sibling back into the parent node.\n        IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);\n        int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1;\n        node->ParentNode->ChildNodes[index_in_parent] = NULL;\n        DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]);\n        node->ParentNode->AuthorityForViewport = ImGuiDataAuthority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport\n        node->ParentNode = NULL;\n    }\n    for (ImGuiWindow* window : node->Windows)\n    {\n        window->Flags &= ~ImGuiWindowFlags_ChildWindow;\n        if (window->ParentWindow)\n            window->ParentWindow->DC.ChildWindows.find_erase(window);\n        UpdateWindowParentAndRootLinks(window, window->Flags, NULL);\n    }\n    node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_DockNode;\n    node->Size = FixLargeWindowsWhenUndocking(node->Size, node->Windows[0]->Viewport);\n    node->WantMouseMove = true;\n    MarkIniSettingsDirty();\n}\n\n// This is mostly used for automation.\nbool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos)\n{\n    if (target != NULL && target_node == NULL)\n        target_node = target->DockNode;\n\n    // In DockNodePreviewDockSetup() for a root central node instead of showing both \"inner\" and \"outer\" drop rects\n    // (which would be functionally identical) we only show the outer one. Reflect this here.\n    if (target_node && target_node->ParentNode == NULL && target_node->IsCentralNode() && split_dir != ImGuiDir_None)\n        split_outer = true;\n    ImGuiDockPreviewData split_data;\n    DockNodePreviewDockSetup(target, target_node, payload_window, payload_node, &split_data, false, split_outer);\n    if (split_data.DropRectsDraw[split_dir+1].IsInverted())\n        return false;\n    *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter();\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Docking: ImGuiDockNode\n//-----------------------------------------------------------------------------\n// - DockNodeGetTabOrder()\n// - DockNodeAddWindow()\n// - DockNodeRemoveWindow()\n// - DockNodeMoveChildNodes()\n// - DockNodeMoveWindows()\n// - DockNodeApplyPosSizeToWindows()\n// - DockNodeHideHostWindow()\n// - ImGuiDockNodeFindInfoResults\n// - DockNodeFindInfo()\n// - DockNodeFindWindowByID()\n// - DockNodeUpdateFlagsAndCollapse()\n// - DockNodeUpdateHasCentralNodeFlag()\n// - DockNodeUpdateVisibleFlag()\n// - DockNodeStartMouseMovingWindow()\n// - DockNodeUpdate()\n// - DockNodeUpdateWindowMenu()\n// - DockNodeBeginAmendTabBar()\n// - DockNodeEndAmendTabBar()\n// - DockNodeUpdateTabBar()\n// - DockNodeAddTabBar()\n// - DockNodeRemoveTabBar()\n// - DockNodeIsDropAllowedOne()\n// - DockNodeIsDropAllowed()\n// - DockNodeCalcTabBarLayout()\n// - DockNodeCalcSplitRects()\n// - DockNodeCalcDropRectsAndTestMousePos()\n// - DockNodePreviewDockSetup()\n// - DockNodePreviewDockRender()\n//-----------------------------------------------------------------------------\n\nImGuiDockNode::ImGuiDockNode(ImGuiID id)\n{\n    ID = id;\n    SharedFlags = LocalFlags = LocalFlagsInWindows = MergedFlags = ImGuiDockNodeFlags_None;\n    ParentNode = ChildNodes[0] = ChildNodes[1] = NULL;\n    TabBar = NULL;\n    SplitAxis = ImGuiAxis_None;\n\n    State = ImGuiDockNodeState_Unknown;\n    LastBgColor = IM_COL32_WHITE;\n    HostWindow = VisibleWindow = NULL;\n    CentralNode = OnlyNodeWithWindows = NULL;\n    CountNodeWithWindows = 0;\n    LastFrameAlive = LastFrameActive = LastFrameFocused = -1;\n    LastFocusedNodeId = 0;\n    SelectedTabId = 0;\n    WantCloseTabId = 0;\n    RefViewportId = 0;\n    AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode;\n    AuthorityForViewport = ImGuiDataAuthority_Auto;\n    IsVisible = true;\n    IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false;\n    IsBgDrawnThisFrame = false;\n    WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false;\n}\n\nImGuiDockNode::~ImGuiDockNode()\n{\n    IM_ASSERT(TabBar == NULL);\n    ChildNodes[0] = ChildNodes[1] = NULL;\n}\n\nint ImGui::DockNodeGetTabOrder(ImGuiWindow* window)\n{\n    ImGuiTabBar* tab_bar = window->DockNode->TabBar;\n    if (tab_bar == NULL)\n        return -1;\n    ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->TabId);\n    return tab ? TabBarGetTabOrder(tab_bar, tab) : -1;\n}\n\nstatic void DockNodeHideWindowDuringHostWindowCreation(ImGuiWindow* window)\n{\n    window->Hidden = true;\n    window->HiddenFramesCanSkipItems = window->Active ? 1 : 2;\n}\n\nstatic void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar)\n{\n    ImGuiContext& g = *GImGui; (void)g;\n    if (window->DockNode)\n    {\n        // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace node)\n        IM_ASSERT(window->DockNode->ID != node->ID);\n        DockNodeRemoveWindow(window->DockNode, window, 0);\n    }\n    IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL);\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockNodeAddWindow node 0x%08X window '%s'\\n\", node->ID, window->Name);\n\n    // If more than 2 windows appeared on the same frame leading to the creation of a new hosting window,\n    // we'll hide windows until the host window is ready. Hide the 1st window after its been output (so it is not visible for one frame).\n    // We will call DockNodeHideWindowDuringHostWindowCreation() on ourselves in Begin()\n    if (node->HostWindow == NULL && node->Windows.Size == 1 && node->Windows[0]->WasActive == false)\n        DockNodeHideWindowDuringHostWindowCreation(node->Windows[0]);\n\n    node->Windows.push_back(window);\n    node->WantHiddenTabBarUpdate = true;\n    window->DockNode = node;\n    window->DockId = node->ID;\n    window->DockIsActive = (node->Windows.Size > 1);\n    window->DockTabWantClose = false;\n\n    // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage.\n    // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one.\n    if (node->HostWindow == NULL && node->IsFloatingNode())\n    {\n        if (node->AuthorityForPos == ImGuiDataAuthority_Auto)\n            node->AuthorityForPos = ImGuiDataAuthority_Window;\n        if (node->AuthorityForSize == ImGuiDataAuthority_Auto)\n            node->AuthorityForSize = ImGuiDataAuthority_Window;\n        if (node->AuthorityForViewport == ImGuiDataAuthority_Auto)\n            node->AuthorityForViewport = ImGuiDataAuthority_Window;\n    }\n\n    // Add to tab bar if requested\n    if (add_to_tab_bar)\n    {\n        if (node->TabBar == NULL)\n        {\n            DockNodeAddTabBar(node);\n            node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId;\n\n            // Add existing windows\n            for (int n = 0; n < node->Windows.Size - 1; n++)\n                TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);\n        }\n        TabBarAddTab(node->TabBar, ImGuiTabItemFlags_Unsorted, window);\n    }\n\n    DockNodeUpdateVisibleFlag(node);\n\n    // Update this without waiting for the next time we Begin() in the window, so our host window will have the proper title bar color on its first frame.\n    if (node->HostWindow)\n        UpdateWindowParentAndRootLinks(window, window->Flags | ImGuiWindowFlags_ChildWindow, node->HostWindow);\n}\n\nstatic void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(window->DockNode == node);\n    //IM_ASSERT(window->RootWindowDockTree == node->HostWindow);\n    //IM_ASSERT(window->LastFrameActive < g.FrameCount);    // We may call this from Begin()\n    IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID);\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockNodeRemoveWindow node 0x%08X window '%s'\\n\", node->ID, window->Name);\n\n    window->DockNode = NULL;\n    window->DockIsActive = window->DockTabWantClose = false;\n    window->DockId = save_dock_id;\n    window->Flags &= ~ImGuiWindowFlags_ChildWindow;\n    if (window->ParentWindow)\n        window->ParentWindow->DC.ChildWindows.find_erase(window);\n    UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately\n\n    if (node->HostWindow && node->HostWindow->ViewportOwned)\n    {\n        // When undocking from a user interaction this will always run in NewFrame() and have not much effect.\n        // But mid-frame, if we clear viewport we need to mark window as hidden as well.\n        window->Viewport = NULL;\n        window->ViewportId = 0;\n        window->ViewportOwned = false;\n        window->Hidden = true;\n    }\n\n    // Remove window\n    bool erased = false;\n    for (int n = 0; n < node->Windows.Size; n++)\n        if (node->Windows[n] == window)\n        {\n            node->Windows.erase(node->Windows.Data + n);\n            erased = true;\n            break;\n        }\n    if (!erased)\n        IM_ASSERT(erased);\n    if (node->VisibleWindow == window)\n        node->VisibleWindow = NULL;\n\n    // Remove tab and possibly tab bar\n    node->WantHiddenTabBarUpdate = true;\n    if (node->TabBar)\n    {\n        TabBarRemoveTab(node->TabBar, window->TabId);\n        const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2;\n        if (node->Windows.Size < tab_count_threshold_for_tab_bar)\n            DockNodeRemoveTabBar(node);\n    }\n\n    if (node->Windows.Size == 0 && !node->IsCentralNode() && !node->IsDockSpace() && window->DockId != node->ID)\n    {\n        // Automatic dock node delete themselves if they are not holding at least one tab\n        DockContextRemoveNode(&g, node, true);\n        return;\n    }\n\n    if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow)\n    {\n        ImGuiWindow* remaining_window = node->Windows[0];\n        // Note: we used to transport viewport ownership here.\n        remaining_window->Collapsed = node->HostWindow->Collapsed;\n    }\n\n    // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree\n    DockNodeUpdateVisibleFlag(node);\n}\n\nstatic void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)\n{\n    IM_ASSERT(dst_node->Windows.Size == 0);\n    dst_node->ChildNodes[0] = src_node->ChildNodes[0];\n    dst_node->ChildNodes[1] = src_node->ChildNodes[1];\n    if (dst_node->ChildNodes[0])\n        dst_node->ChildNodes[0]->ParentNode = dst_node;\n    if (dst_node->ChildNodes[1])\n        dst_node->ChildNodes[1]->ParentNode = dst_node;\n    dst_node->SplitAxis = src_node->SplitAxis;\n    dst_node->SizeRef = src_node->SizeRef;\n    src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL;\n}\n\nstatic void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)\n{\n    // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered)\n    IM_ASSERT(src_node && dst_node && dst_node != src_node);\n    ImGuiTabBar* src_tab_bar = src_node->TabBar;\n    if (src_tab_bar != NULL)\n        IM_ASSERT(src_node->Windows.Size <= src_node->TabBar->Tabs.Size);\n\n    // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.)\n    bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL);\n    if (move_tab_bar)\n    {\n        dst_node->TabBar = src_node->TabBar;\n        src_node->TabBar = NULL;\n    }\n\n    // Tab order is not important here, it is preserved by sorting in DockNodeUpdateTabBar().\n    for (ImGuiWindow* window : src_node->Windows)\n    {\n        window->DockNode = NULL;\n        window->DockIsActive = false;\n        DockNodeAddWindow(dst_node, window, !move_tab_bar);\n    }\n    src_node->Windows.clear();\n\n    if (!move_tab_bar && src_node->TabBar)\n    {\n        if (dst_node->TabBar)\n            dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId;\n        DockNodeRemoveTabBar(src_node);\n    }\n}\n\nstatic void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node)\n{\n    for (ImGuiWindow* window : node->Windows)\n    {\n        SetWindowPos(window, node->Pos, ImGuiCond_Always); // We don't assign directly to Pos because it can break the calculation of SizeContents on next frame\n        SetWindowSize(window, node->Size, ImGuiCond_Always);\n    }\n}\n\nstatic void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node)\n{\n    if (node->HostWindow)\n    {\n        if (node->HostWindow->DockNodeAsHost == node)\n            node->HostWindow->DockNodeAsHost = NULL;\n        node->HostWindow = NULL;\n    }\n\n    if (node->Windows.Size == 1)\n    {\n        node->VisibleWindow = node->Windows[0];\n        node->Windows[0]->DockIsActive = false;\n    }\n\n    if (node->TabBar)\n        DockNodeRemoveTabBar(node);\n}\n\n// Search function called once by root node in DockNodeUpdate()\nstruct ImGuiDockNodeTreeInfo\n{\n    ImGuiDockNode*      CentralNode;\n    ImGuiDockNode*      FirstNodeWithWindows;\n    int                 CountNodesWithWindows;\n    //ImGuiWindowClass  WindowClassForMerges;\n\n    ImGuiDockNodeTreeInfo() { memset((void*)this, 0, sizeof(*this)); }\n};\n\nstatic void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeTreeInfo* info)\n{\n    if (node->Windows.Size > 0)\n    {\n        if (info->FirstNodeWithWindows == NULL)\n            info->FirstNodeWithWindows = node;\n        info->CountNodesWithWindows++;\n    }\n    if (node->IsCentralNode())\n    {\n        IM_ASSERT(info->CentralNode == NULL); // Should be only one\n        IM_ASSERT(node->IsLeafNode() && \"If you get this assert: please submit .ini file + repro of actions leading to this.\");\n        info->CentralNode = node;\n    }\n    if (info->CountNodesWithWindows > 1 && info->CentralNode != NULL)\n        return;\n    if (node->ChildNodes[0])\n        DockNodeFindInfo(node->ChildNodes[0], info);\n    if (node->ChildNodes[1])\n        DockNodeFindInfo(node->ChildNodes[1], info);\n}\n\nstatic ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id)\n{\n    IM_ASSERT(id != 0);\n    for (ImGuiWindow* window : node->Windows)\n        if (window->ID == id)\n            return window;\n    return NULL;\n}\n\n// - Remove inactive windows/nodes.\n// - Update visibility flag.\nstatic void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);\n\n    // Inherit most flags\n    if (node->ParentNode)\n        node->SharedFlags = node->ParentNode->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;\n\n    // Recurse into children\n    // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'.\n    // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node'\n    // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the \"remove inactive windows\" loop will have run twice on those windows (harmless)\n    node->HasCentralNodeChild = false;\n    if (node->ChildNodes[0])\n        DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]);\n    if (node->ChildNodes[1])\n        DockNodeUpdateFlagsAndCollapse(node->ChildNodes[1]);\n\n    // Remove inactive windows, collapse nodes\n    // Merge node flags overrides stored in windows\n    node->LocalFlagsInWindows = ImGuiDockNodeFlags_None;\n    for (int window_n = 0; window_n < node->Windows.Size; window_n++)\n    {\n        ImGuiWindow* window = node->Windows[window_n];\n        IM_ASSERT(window->DockNode == node);\n\n        bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);\n        bool remove = false;\n        remove |= node_was_active && (window->WasActive == false); // Can't use 'window->LastFrameActive + 1 < g.FrameCount'. (see #9151)\n        remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument);  // Submit all _expected_ closure from last frame\n        remove |= (window->DockTabWantClose);\n        if (remove)\n        {\n            window->DockTabWantClose = false;\n            if (node->Windows.Size == 1 && !node->IsCentralNode())\n            {\n                DockNodeHideHostWindow(node);\n                node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;\n                DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return\n                return;\n            }\n            DockNodeRemoveWindow(node, window, node->ID);\n            window_n--;\n            continue;\n        }\n\n        // FIXME-DOCKING: Missing policies for conflict resolution, hence the \"Experimental\" tag on this.\n        //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear;\n        node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet;\n    }\n    node->UpdateMergedFlags();\n\n    // Auto-hide tab bar option\n    ImGuiDockNodeFlags node_flags = node->MergedFlags;\n    if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar())\n        node->WantHiddenTabBarToggle = true;\n    node->WantHiddenTabBarUpdate = false;\n\n    // Cancel toggling if we know our tab bar is enforced to be hidden at all times\n    if (node->WantHiddenTabBarToggle && node->VisibleWindow && (node->VisibleWindow->WindowClass.DockNodeFlagsOverrideSet & ImGuiDockNodeFlags_HiddenTabBar))\n        node->WantHiddenTabBarToggle = false;\n\n    // Apply toggles at a single point of the frame (here!)\n    if (node->Windows.Size > 1)\n        node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);\n    else if (node->WantHiddenTabBarToggle)\n        node->SetLocalFlags(node->LocalFlags ^ ImGuiDockNodeFlags_HiddenTabBar);\n    node->WantHiddenTabBarToggle = false;\n\n    DockNodeUpdateVisibleFlag(node);\n}\n\n// This is rarely called as DockNodeUpdateForRootNode() generally does it most frames.\nstatic void ImGui::DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node)\n{\n    node->HasCentralNodeChild = false;\n    if (node->ChildNodes[0])\n        DockNodeUpdateHasCentralNodeChild(node->ChildNodes[0]);\n    if (node->ChildNodes[1])\n        DockNodeUpdateHasCentralNodeChild(node->ChildNodes[1]);\n    if (node->IsRootNode())\n    {\n        ImGuiDockNode* mark_node = node->CentralNode;\n        while (mark_node)\n        {\n            mark_node->HasCentralNodeChild = true;\n            mark_node = mark_node->ParentNode;\n        }\n    }\n}\n\nstatic void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node)\n{\n    // Update visibility flag\n    bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode();\n    is_visible |= (node->Windows.Size > 0);\n    is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible);\n    is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible);\n    node->IsVisible = is_visible;\n}\n\nstatic void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(node->WantMouseMove == true);\n    StartMouseMovingWindow(window);\n    g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos;\n    g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision.\n    node->WantMouseMove = false;\n}\n\n// Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class.\nstatic void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node)\n{\n    DockNodeUpdateFlagsAndCollapse(node);\n\n    // - Setup central node pointers\n    // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!)\n    // Cannot merge this with DockNodeUpdateFlagsAndCollapse() because FirstNodeWithWindows is found after window removal and child collapsing\n    ImGuiDockNodeTreeInfo info;\n    DockNodeFindInfo(node, &info);\n    node->CentralNode = info.CentralNode;\n    node->OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) ? info.FirstNodeWithWindows : NULL;\n    node->CountNodeWithWindows = info.CountNodesWithWindows;\n    if (node->LastFocusedNodeId == 0 && info.FirstNodeWithWindows != NULL)\n        node->LastFocusedNodeId = info.FirstNodeWithWindows->ID;\n\n    // Copy the window class from of our first window so it can be used for proper dock filtering.\n    // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy.\n    // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec.\n    if (ImGuiDockNode* first_node_with_windows = info.FirstNodeWithWindows)\n    {\n        node->WindowClass = first_node_with_windows->Windows[0]->WindowClass;\n        for (int n = 1; n < first_node_with_windows->Windows.Size; n++)\n            if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false)\n            {\n                node->WindowClass = first_node_with_windows->Windows[n]->WindowClass;\n                break;\n            }\n    }\n\n    ImGuiDockNode* mark_node = node->CentralNode;\n    while (mark_node)\n    {\n        mark_node->HasCentralNodeChild = true;\n        mark_node = mark_node->ParentNode;\n    }\n}\n\nstatic void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window)\n{\n    // Remove ourselves from any previous different host window\n    // This can happen if a user mistakenly does (see #4295 for details):\n    //  - N+0: DockBuilderAddNode(id, 0)    // missing ImGuiDockNodeFlags_DockSpace\n    //  - N+1: NewFrame()                   // will create floating host window for that node\n    //  - N+1: DockSpace(id)                // requalify node as dockspace, moving host window\n    if (node->HostWindow && node->HostWindow != host_window && node->HostWindow->DockNodeAsHost == node)\n        node->HostWindow->DockNodeAsHost = NULL;\n\n    host_window->DockNodeAsHost = node;\n    node->HostWindow = host_window;\n}\n\nstatic void ImGui::DockNodeUpdate(ImGuiDockNode* node)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(node->LastFrameActive != g.FrameCount);\n    node->LastFrameAlive = g.FrameCount;\n    node->IsBgDrawnThisFrame = false;\n\n    node->CentralNode = node->OnlyNodeWithWindows = NULL;\n    if (node->IsRootNode())\n        DockNodeUpdateForRootNode(node);\n\n    // Remove tab bar if not needed\n    if (node->TabBar && node->IsNoTabBar())\n        DockNodeRemoveTabBar(node);\n\n    // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId)\n    bool want_to_hide_host_window = false;\n    if (node->IsFloatingNode())\n    {\n        if (node->Windows.Size <= 1 && node->IsLeafNode())\n            if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar))\n                want_to_hide_host_window = true;\n        if (node->CountNodeWithWindows == 0)\n            want_to_hide_host_window = true;\n    }\n    if (want_to_hide_host_window)\n    {\n        if (node->Windows.Size == 1)\n        {\n            // Floating window pos/size is authoritative\n            ImGuiWindow* single_window = node->Windows[0];\n            node->Pos = single_window->Pos;\n            node->Size = single_window->SizeFull;\n            node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;\n\n            // Transfer focus immediately so when we revert to a regular window it is immediately selected\n            if (node->HostWindow && g.NavWindow == node->HostWindow)\n                FocusWindow(single_window);\n            if (node->HostWindow)\n            {\n                IMGUI_DEBUG_LOG_VIEWPORT(\"[viewport] Node %08X transfer Viewport %08X->%08X to Window '%s'\\n\", node->ID, node->HostWindow->Viewport->ID, single_window->ID, single_window->Name);\n                single_window->Viewport = node->HostWindow->Viewport;\n                single_window->ViewportId = node->HostWindow->ViewportId;\n                if (node->HostWindow->ViewportOwned)\n                {\n                    single_window->Viewport->ID = single_window->ID;\n                    single_window->Viewport->Window = single_window;\n                    single_window->ViewportOwned = true;\n                }\n            }\n            node->RefViewportId = single_window->ViewportId;\n        }\n\n        DockNodeHideHostWindow(node);\n        node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;\n        node->WantCloseAll = false;\n        node->WantCloseTabId = 0;\n        node->HasCloseButton = node->HasWindowMenuButton = false;\n        node->LastFrameActive = g.FrameCount;\n\n        if (node->WantMouseMove && node->Windows.Size == 1)\n            DockNodeStartMouseMovingWindow(node, node->Windows[0]);\n        return;\n    }\n\n    // In some circumstance we will defer creating the host window (so everything will be kept hidden),\n    // while the expected visible window is resizing itself.\n    // This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled,\n    // otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up:\n    //   N+0: Begin(): window created (with no known size), node is created\n    //   N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible\n    //   N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible\n    // We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code.\n    // It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin().\n    // In reality it isn't very important as user quickly ends up with size data in .ini file.\n    if (node->IsVisible && node->HostWindow == NULL && node->IsFloatingNode() && node->IsLeafNode())\n    {\n        IM_ASSERT(node->Windows.Size > 0);\n        ImGuiWindow* ref_window = NULL;\n        if (node->SelectedTabId != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them!\n            ref_window = DockNodeFindWindowByID(node, node->SelectedTabId);\n        if (ref_window == NULL)\n            ref_window = node->Windows[0];\n        if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0)\n        {\n            node->State = ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing;\n            return;\n        }\n    }\n\n    const ImGuiDockNodeFlags node_flags = node->MergedFlags;\n\n    // Decide if the node will have a close button and a window menu button\n    node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0;\n    node->HasCloseButton = false;\n    for (ImGuiWindow* window : node->Windows)\n    {\n        // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call.\n        node->HasCloseButton |= window->HasCloseButton;\n        window->DockIsActive = (node->Windows.Size > 1);\n    }\n    if ((node_flags & ImGuiDockNodeFlags_NoCloseButton) || !g.Style.DockingNodeHasCloseButton)\n        node->HasCloseButton = false;\n\n    // Bind or create host window\n    ImGuiWindow* host_window = NULL;\n    bool beginned_into_host_window = false;\n    if (node->IsDockSpace())\n    {\n        // [Explicit root dockspace node]\n        IM_ASSERT(node->HostWindow);\n        host_window = node->HostWindow;\n    }\n    else\n    {\n        // [Automatic root or child nodes]\n        if (node->IsRootNode() && node->IsVisible)\n        {\n            ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL;\n\n            // Sync Pos\n            if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window)\n                SetNextWindowPos(ref_window->Pos);\n            else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode)\n                SetNextWindowPos(node->Pos);\n\n            // Sync Size\n            if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)\n                SetNextWindowSize(ref_window->SizeFull);\n            else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode)\n                SetNextWindowSize(node->Size);\n\n            // Sync Collapsed\n            if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)\n                SetNextWindowCollapsed(ref_window->Collapsed);\n\n            // Sync Viewport\n            if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window)\n                SetNextWindowViewport(ref_window->ViewportId);\n            else if (node->AuthorityForViewport == ImGuiDataAuthority_Window && node->RefViewportId != 0)\n                SetNextWindowViewport(node->RefViewportId);\n\n            SetNextWindowClass(&node->WindowClass);\n\n            // Begin into the host window\n            char window_label[20];\n            DockNodeGetHostWindowTitle(node, window_label, IM_COUNTOF(window_label));\n            ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost;\n            window_flags |= ImGuiWindowFlags_NoFocusOnAppearing;\n            window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse;\n            window_flags |= ImGuiWindowFlags_NoTitleBar;\n\n            SetNextWindowBgAlpha(0.0f); // Don't set ImGuiWindowFlags_NoBackground because it disables borders\n            PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));\n            Begin(window_label, NULL, window_flags);\n            PopStyleVar();\n            beginned_into_host_window = true;\n\n            host_window = g.CurrentWindow;\n            DockNodeSetupHostWindow(node, host_window);\n            host_window->DC.CursorPos = host_window->Pos;\n            node->Pos = host_window->Pos;\n            node->Size = host_window->Size;\n\n            // We set ImGuiWindowFlags_NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow)\n            // But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags.\n            // One simple case to ponder if: window A has a toggle to create windows B/C/D. Dock B/C/D together, clear the toggle and enable it again.\n            // When reappearing B/C/D will request focus and be moved to the top of the display pile, but they are not linked to the dock host window\n            // during the frame they appear. The dock host window would keep its old display order, and the sorting in EndFrame would move B/C/D back\n            // after the dock host window, losing their top-most status.\n            if (node->HostWindow->Appearing)\n                BringWindowToDisplayFront(node->HostWindow);\n\n            node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;\n        }\n        else if (node->ParentNode)\n        {\n            node->HostWindow = host_window = node->ParentNode->HostWindow;\n            node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;\n        }\n        if (node->WantMouseMove && node->HostWindow)\n            DockNodeStartMouseMovingWindow(node, node->HostWindow);\n    }\n    node->RefViewportId = 0; // Clear when we have a host window\n\n    // Update focused node (the one whose title bar is highlight) within a node tree\n    if (node->IsSplitNode())\n        IM_ASSERT(node->TabBar == NULL);\n    if (node->IsRootNode())\n        if (ImGuiWindow* p_window = g.NavWindow ? g.NavWindow->RootWindow : NULL)\n            while (p_window != NULL && p_window->DockNode != NULL)\n            {\n                ImGuiDockNode* p_node = DockNodeGetRootNode(p_window->DockNode);\n                if (p_node == node)\n                {\n                    node->LastFocusedNodeId = p_window->DockNode->ID; // Note: not using root node ID!\n                    break;\n                }\n                p_window = p_node->HostWindow ? p_node->HostWindow->RootWindow : NULL;\n            }\n\n    // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace\n    ImGuiDockNode* central_node = node->CentralNode;\n    const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty();\n    bool central_node_hole_register_hit_test_hole = central_node_hole;\n    if (central_node_hole)\n        if (const ImGuiPayload* payload = ImGui::GetDragDropPayload())\n            if (payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && DockNodeIsDropAllowed(host_window, *(ImGuiWindow**)payload->Data))\n                central_node_hole_register_hit_test_hole = false;\n    if (central_node_hole_register_hit_test_hole)\n    {\n        // We add a little padding to match the \"resize from edges\" behavior and allow grabbing the splitter easily.\n        // (But we only add it if there's something else on the other side of the hole, otherwise for e.g. fullscreen\n        // covering passthru node we'd have a gap on the edge not covered by the hole)\n        IM_ASSERT(node->IsDockSpace()); // We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window->ParentNode\n        ImGuiDockNode* root_node = DockNodeGetRootNode(central_node);\n        ImRect root_rect(root_node->Pos, root_node->Pos + root_node->Size);\n        ImRect hole_rect(central_node->Pos, central_node->Pos + central_node->Size);\n        if (hole_rect.Min.x > root_rect.Min.x) { hole_rect.Min.x += g.WindowsBorderHoverPadding; }\n        if (hole_rect.Max.x < root_rect.Max.x) { hole_rect.Max.x -= g.WindowsBorderHoverPadding; }\n        if (hole_rect.Min.y > root_rect.Min.y) { hole_rect.Min.y += g.WindowsBorderHoverPadding; }\n        if (hole_rect.Max.y < root_rect.Max.y) { hole_rect.Max.y -= g.WindowsBorderHoverPadding; }\n        //GetForegroundDrawList()->AddRect(hole_rect.Min, hole_rect.Max, IM_COL32(255, 0, 0, 255));\n        if (central_node_hole && !hole_rect.IsInverted())\n        {\n            SetWindowHitTestHole(host_window, hole_rect.Min, hole_rect.Max - hole_rect.Min);\n            if (host_window->ParentWindow)\n                SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min);\n        }\n    }\n\n    // Update position/size, process and draw resizing splitters\n    if (node->IsRootNode() && host_window)\n    {\n        DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size);\n        PushStyleColor(ImGuiCol_Separator, g.Style.Colors[ImGuiCol_Border]);\n        PushStyleColor(ImGuiCol_SeparatorActive, g.Style.Colors[ImGuiCol_ResizeGripActive]);\n        PushStyleColor(ImGuiCol_SeparatorHovered, g.Style.Colors[ImGuiCol_ResizeGripHovered]);\n        DockNodeTreeUpdateSplitter(node);\n        PopStyleColor(3);\n    }\n\n    // Draw empty node background (currently can only be the Central Node)\n    if (host_window && node->IsEmpty() && node->IsVisible)\n    {\n        host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);\n        node->LastBgColor = (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) ? 0 : GetColorU32(ImGuiCol_DockingEmptyBg);\n        if (node->LastBgColor != 0)\n            host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, node->LastBgColor);\n        node->IsBgDrawnThisFrame = true;\n    }\n\n    // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set.\n    // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size\n    // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order!\n    const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0;\n    if (render_dockspace_bg && node->IsVisible)\n    {\n        host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);\n        if (central_node_hole)\n            RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f);\n        else\n            host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f);\n    }\n\n    // Draw and populate Tab Bar\n    if (host_window)\n        host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG);\n    if (host_window && node->Windows.Size > 0)\n    {\n        DockNodeUpdateTabBar(node, host_window);\n    }\n    else\n    {\n        node->WantCloseAll = false;\n        node->WantCloseTabId = 0;\n        node->IsFocused = false;\n    }\n    if (node->TabBar && node->TabBar->SelectedTabId)\n        node->SelectedTabId = node->TabBar->SelectedTabId;\n    else if (node->Windows.Size > 0)\n        node->SelectedTabId = node->Windows[0]->TabId;\n\n    // Draw payload drop target\n    if (host_window && node->IsVisible)\n        if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != host_window))\n            BeginDockableDragDropTarget(host_window);\n\n    // We update this after DockNodeUpdateTabBar()\n    node->LastFrameActive = g.FrameCount;\n\n    // Recurse into children\n    // FIXME-DOCK FIXME-OPT: Should not need to recurse into children\n    if (host_window)\n    {\n        if (node->ChildNodes[0])\n            DockNodeUpdate(node->ChildNodes[0]);\n        if (node->ChildNodes[1])\n            DockNodeUpdate(node->ChildNodes[1]);\n\n        // Render outer borders last (after the tab bar)\n        if (node->IsRootNode())\n            RenderWindowOuterBorders(host_window);\n    }\n\n    // End host window\n    if (beginned_into_host_window) //-V1020\n        End();\n}\n\n// Compare TabItem nodes given the last known DockOrder (will persist in .ini file as hint), used to sort tabs when multiple tabs are added on the same frame.\nstatic int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* rhs)\n{\n    ImGuiWindow* a = ((const ImGuiTabItem*)lhs)->Window;\n    ImGuiWindow* b = ((const ImGuiTabItem*)rhs)->Window;\n    if (int d = ((a->DockOrder == -1) ? INT_MAX : a->DockOrder) - ((b->DockOrder == -1) ? INT_MAX : b->DockOrder))\n        return d;\n    return (a->BeginOrderWithinContext - b->BeginOrderWithinContext);\n}\n\n// Default handler for g.DockNodeWindowMenuHandler(): display the list of windows for a given dock-node.\n// This is exceptionally stored in a function pointer to also user applications to tweak this menu (undocumented)\n// Custom overrides may want to decorate, group, sort entries.\n// Please note those are internal structures: if you copy this expect occasional breakage.\n// (if you don't need to modify the \"Tabs.Size == 1\" behavior/path it is recommend you call this function in your handler)\nvoid ImGui::DockNodeWindowMenuHandler_Default(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar)\n{\n    IM_UNUSED(ctx);\n    if (tab_bar->Tabs.Size == 1)\n    {\n        // \"Hide tab bar\" option. Being one of our rare user-facing string we pull it from a table.\n        if (MenuItem(LocalizeGetMsg(ImGuiLocKey_DockingHideTabBar), NULL, node->IsHiddenTabBar()))\n            node->WantHiddenTabBarToggle = true;\n    }\n    else\n    {\n        // Display a selectable list of windows in this docking node\n        for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)\n        {\n            ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];\n            if (tab->Flags & ImGuiTabItemFlags_Button)\n                continue;\n            if (Selectable(TabBarGetTabName(tab_bar, tab), tab->ID == tab_bar->SelectedTabId))\n                TabBarQueueFocus(tab_bar, tab);\n            SameLine();\n            Text(\"   \");\n        }\n    }\n}\n\nstatic void ImGui::DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar)\n{\n    // Try to position the menu so it is more likely to stays within the same viewport\n    ImGuiContext& g = *GImGui;\n    if (g.Style.WindowMenuButtonPosition == ImGuiDir_Left)\n        SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f));\n    else\n        SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f));\n    if (BeginPopup(\"#WindowMenu\"))\n    {\n        node->IsFocused = true;\n        g.DockNodeWindowMenuHandler(&g, node, tab_bar);\n        EndPopup();\n    }\n}\n\n// User helper to append/amend into a dock node tab bar. Most commonly used to add e.g. a \"+\" button.\nbool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node)\n{\n    if (node->TabBar == NULL || node->HostWindow == NULL)\n        return false;\n    if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)\n        return false;\n    if (node->TabBar->ID == 0)\n        return false;\n    Begin(node->HostWindow->Name);\n    PushOverrideID(node->ID);\n    bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags);\n    IM_UNUSED(ret);\n    IM_ASSERT(ret);\n    return true;\n}\n\nvoid ImGui::DockNodeEndAmendTabBar()\n{\n    EndTabBar();\n    PopID();\n    End();\n}\n\nstatic bool IsDockNodeTitleBarHighlighted(ImGuiDockNode* node, ImGuiDockNode* root_node)\n{\n    // CTRL+Tab highlight (only highlighting leaf node, not whole hierarchy)\n    ImGuiContext& g = *GImGui;\n    if (g.NavWindowingTarget)\n        return (g.NavWindowingTarget->DockNode == node);\n\n    // FIXME-DOCKING: May want alternative to treat central node void differently? e.g. if (g.NavWindow == host_window)\n    if (g.NavWindow && root_node->LastFocusedNodeId == node->ID)\n    {\n        // FIXME: This could all be backed in RootWindowForTitleBarHighlight? Probably need to reorganize for both dock nodes + other RootWindowForTitleBarHighlight users (not-node)\n        ImGuiWindow* parent_window = g.NavWindow->RootWindow;\n        while (parent_window->Flags & ImGuiWindowFlags_ChildMenu)\n            parent_window = parent_window->ParentWindow->RootWindow;\n        ImGuiDockNode* start_parent_node = parent_window->DockNodeAsHost ? parent_window->DockNodeAsHost : parent_window->DockNode;\n        for (ImGuiDockNode* parent_node = start_parent_node; parent_node != NULL; parent_node = parent_node->HostWindow ? parent_node->HostWindow->RootWindow->DockNode : NULL)\n            if ((parent_node = ImGui::DockNodeGetRootNode(parent_node)) == root_node)\n                return true;\n    }\n    return false;\n}\n\n// Submit the tab bar corresponding to a dock node and various housekeeping details.\nstatic void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n\n    const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);\n    node->WantCloseAll = false;\n    node->WantCloseTabId = 0;\n\n    // Decide if we should use a focused title bar color\n    bool is_focused = false;\n    ImGuiDockNode* root_node = DockNodeGetRootNode(node);\n    if (IsDockNodeTitleBarHighlighted(node, root_node))\n        is_focused = true;\n\n    // Hidden tab bar will show a triangle on the upper-left (in Begin)\n    if (node->IsHiddenTabBar() || node->IsNoTabBar())\n    {\n        node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL;\n        node->IsFocused = is_focused;\n        if (is_focused)\n            node->LastFrameFocused = g.FrameCount;\n        if (node->VisibleWindow)\n        {\n            // Notify root of visible window (used to display title in OS task bar)\n            if (is_focused || root_node->VisibleWindow == NULL)\n                root_node->VisibleWindow = node->VisibleWindow;\n            if (node->TabBar)\n                node->TabBar->VisibleTabId = node->VisibleWindow->TabId;\n        }\n        return;\n    }\n\n    // Move ourselves to the Menu layer (so we can be accessed by tapping Alt) + undo SkipItems flag in order to draw over the title bar even if the window is collapsed\n    bool backup_skip_item = host_window->SkipItems;\n    if (!node->IsDockSpace())\n    {\n        host_window->SkipItems = false;\n        host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;\n    }\n\n    // Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID.\n    // This is to facilitate computing those ID from the outside, and will affect more or less only the ID of the collapse button, popup and tabs,\n    // as docked windows themselves will override the stack with their own root ID.\n    PushOverrideID(node->ID);\n    ImGuiTabBar* tab_bar = node->TabBar;\n    bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden\n    if (tab_bar == NULL)\n    {\n        DockNodeAddTabBar(node);\n        tab_bar = node->TabBar;\n    }\n\n    ImGuiID focus_tab_id = 0;\n    node->IsFocused = is_focused;\n\n    const ImGuiDockNodeFlags node_flags = node->MergedFlags;\n    const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0 && (style.WindowMenuButtonPosition != ImGuiDir_None);\n\n    // In a dock node, the Collapse Button turns into the Window Menu button.\n    // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes?\n    if (has_window_menu_button && IsPopupOpen(\"#WindowMenu\"))\n    {\n        ImGuiID next_selected_tab_id = tab_bar->NextSelectedTabId;\n        DockNodeWindowMenuUpdate(node, tab_bar);\n        if (tab_bar->NextSelectedTabId != 0 && tab_bar->NextSelectedTabId != next_selected_tab_id)\n            focus_tab_id = tab_bar->NextSelectedTabId;\n        is_focused |= node->IsFocused;\n    }\n\n    // Layout\n    ImRect title_bar_rect, tab_bar_rect;\n    ImVec2 window_menu_button_pos;\n    ImVec2 close_button_pos;\n    DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos, &close_button_pos);\n\n    // Submit new tabs, they will be added as Unsorted and sorted below based on relative DockOrder value.\n    const int tabs_count_old = tab_bar->Tabs.Size;\n    for (int window_n = 0; window_n < node->Windows.Size; window_n++)\n    {\n        ImGuiWindow* window = node->Windows[window_n];\n        if (TabBarFindTabByID(tab_bar, window->TabId) == NULL)\n            TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window);\n    }\n\n    // Title bar\n    if (is_focused)\n        node->LastFrameFocused = g.FrameCount;\n    ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);\n    ImDrawFlags rounding_flags = CalcRoundingFlagsForRectInRect(title_bar_rect, host_window->Rect(), g.Style.DockingSeparatorSize);\n    host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, rounding_flags);\n\n    // Docking/Collapse button\n    if (has_window_menu_button)\n    {\n        if (CollapseButton(host_window->GetID(\"#COLLAPSE\"), window_menu_button_pos, node)) // == DockNodeGetWindowMenuButtonId(node)\n            OpenPopup(\"#WindowMenu\");\n        if (IsItemActive())\n            focus_tab_id = tab_bar->SelectedTabId;\n        if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal) && g.HoveredIdTimer > 0.5f)\n            SetTooltip(\"%s\", LocalizeGetMsg(ImGuiLocKey_DockingDragToUndockOrMoveNode));\n    }\n\n    // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value\n    int tabs_unsorted_start = tab_bar->Tabs.Size;\n    for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--)\n    {\n        // FIXME-DOCK: Consider only clearing the flag after the tab has been alive for a few consecutive frames, allowing late comers to not break sorting?\n        tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted;\n        tabs_unsorted_start = tab_n;\n    }\n    if (tab_bar->Tabs.Size > tabs_unsorted_start)\n    {\n        IMGUI_DEBUG_LOG_DOCKING(\"[docking] In node 0x%08X: %d new appearing tabs:%s\\n\", node->ID, tab_bar->Tabs.Size - tabs_unsorted_start, (tab_bar->Tabs.Size > tabs_unsorted_start + 1) ? \" (will sort)\" : \"\");\n        for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++)\n        {\n            ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];\n            IM_UNUSED(tab);\n            IMGUI_DEBUG_LOG_DOCKING(\"[docking] - Tab 0x%08X '%s' Order %d\\n\", tab->ID, TabBarGetTabName(tab_bar, tab), tab->Window ? tab->Window->DockOrder : -1);\n        }\n        IMGUI_DEBUG_LOG_DOCKING(\"[docking] SelectedTabId = 0x%08X, NavWindow->TabId = 0x%08X\\n\", node->SelectedTabId, g.NavWindow ? g.NavWindow->TabId : -1);\n        if (tab_bar->Tabs.Size > tabs_unsorted_start + 1)\n            ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder);\n    }\n\n    // Apply NavWindow focus back to the tab bar\n    if (g.NavWindow && g.NavWindow->RootWindow->DockNode == node)\n        tab_bar->SelectedTabId = g.NavWindow->RootWindow->TabId;\n\n    // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated\n    if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL)\n        tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId;\n    else if (tab_bar->Tabs.Size > tabs_count_old)\n        tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->TabId;\n\n    // Begin tab bar\n    ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons);\n    tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode;\n    tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyMixed; // Enforce default policy. Since 1.92.2 this is now reasonable. May expose later if needed. (#8800, #3421)\n    tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline;\n    if (!host_window->Collapsed && is_focused)\n        tab_bar_flags |= ImGuiTabBarFlags_IsFocused;\n    tab_bar->ID = node->ID;// GetID(\"#TabBar\");\n    tab_bar->SeparatorMinX = node->Pos.x + host_window->WindowBorderSize; // Separator cover the whole node width\n    tab_bar->SeparatorMaxX = node->Pos.x + node->Size.x - host_window->WindowBorderSize;\n    BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags);\n    //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255));\n\n    // Backup style colors\n    ImVec4 backup_style_cols[ImGuiWindowDockStyleCol_COUNT];\n    for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)\n        backup_style_cols[color_n] = g.Style.Colors[GWindowDockStyleColors[color_n]];\n\n    // Submit actual tabs\n    node->VisibleWindow = NULL;\n    for (int window_n = 0; window_n < node->Windows.Size; window_n++)\n    {\n        ImGuiWindow* window = node->Windows[window_n];\n        if (window->LastFrameActive + 1 < g.FrameCount && node_was_active)\n            continue; // FIXME: Not sure if that's still taken/useful, as windows are normally removed in DockNodeUpdateFlagsAndCollapse().\n\n        ImGuiTabItemFlags tab_item_flags = 0;\n        tab_item_flags |= window->WindowClass.TabItemFlagsOverrideSet;\n        if (window->Flags & ImGuiWindowFlags_UnsavedDocument)\n            tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument;\n        if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)\n            tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;\n\n        // Apply stored style overrides for the window\n        for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)\n            g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]);\n\n        // Note that TabItemEx() calls TabBarCalcTabID() so our tab item ID will ignore the current ID stack (rightly so)\n        bool tab_open = true;\n        TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window);\n        if (!tab_open)\n            node->WantCloseTabId = window->TabId;\n        if (tab_bar->VisibleTabId == window->TabId)\n            node->VisibleWindow = window;\n\n        // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call\n        window->DC.DockTabItemStatusFlags = g.LastItemData.StatusFlags;\n        window->DC.DockTabItemRect = g.LastItemData.Rect;\n\n        // Update navigation ID on menu layer\n        if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0)\n            host_window->NavLastIds[1] = window->TabId;\n    }\n\n    // Restore style colors\n    for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)\n        g.Style.Colors[GWindowDockStyleColors[color_n]] = backup_style_cols[color_n];\n\n    // Notify root of visible window (used to display title in OS task bar)\n    if (node->VisibleWindow)\n        if (is_focused || root_node->VisibleWindow == NULL)\n            root_node->VisibleWindow = node->VisibleWindow;\n\n    // Close button (after VisibleWindow was updated)\n    // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->TabId may be != from tab_bar->SelectedTabId\n    const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton;\n    const bool close_button_is_visible = node->HasCloseButton;\n    //const bool close_button_is_visible = close_button_is_enabled; // Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one)\n    if (close_button_is_visible)\n    {\n        if (!close_button_is_enabled)\n        {\n            PushItemFlag(ImGuiItemFlags_Disabled, true);\n            PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.4f));\n        }\n        if (CloseButton(host_window->GetID(\"#CLOSE\"), close_button_pos))\n        {\n            node->WantCloseAll = true;\n            for (int n = 0; n < tab_bar->Tabs.Size; n++)\n                TabBarCloseTab(tab_bar, &tab_bar->Tabs[n]);\n        }\n        //if (IsItemActive())\n        //    focus_tab_id = tab_bar->SelectedTabId;\n        if (!close_button_is_enabled)\n        {\n            PopStyleColor();\n            PopItemFlag();\n        }\n    }\n\n    // When clicking on the title bar outside of tabs, we still focus the selected tab for that node\n    // FIXME: TabItems submitted earlier use AllowItemOverlap so we manually perform a more specific test for now (hovered || held) in order to not cover them.\n    ImGuiID title_bar_id = host_window->GetID(\"#TITLEBAR\");\n    if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id)\n    {\n        // AllowOverlap mode required for appending into dock node tab bar,\n        // otherwise dragging window will steal HoveredId and amended tabs cannot get them.\n        bool held;\n        KeepAliveID(title_bar_id);\n        ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held, ImGuiButtonFlags_AllowOverlap);\n        if (g.HoveredId == title_bar_id)\n        {\n            g.LastItemData.ID = title_bar_id;\n        }\n        if (held)\n        {\n            if (IsMouseClicked(0))\n                focus_tab_id = tab_bar->SelectedTabId;\n\n            // Forward moving request to selected window\n            if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))\n                StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false); // Undock from tab bar empty space\n        }\n    }\n\n    // Forward focus from host node to selected window\n    //if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget)\n    //    focus_tab_id = tab_bar->SelectedTabId;\n\n    // When clicked on a tab we requested focus to the docked child\n    // This overrides the value set by \"forward focus from host node to selected window\".\n    if (tab_bar->NextSelectedTabId)\n        focus_tab_id = tab_bar->NextSelectedTabId;\n\n    // Apply navigation focus\n    if (focus_tab_id != 0)\n        if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id))\n            if (tab->Window)\n            {\n                FocusWindow(tab->Window);\n                if (g.NavId == 0) // only init if FocusWindow() didn't restore anything.\n                    NavInitWindow(tab->Window, false);\n            }\n\n    EndTabBar();\n    PopID();\n\n    // Restore SkipItems flag\n    if (!node->IsDockSpace())\n    {\n        host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n        host_window->SkipItems = backup_skip_item;\n    }\n}\n\nstatic void ImGui::DockNodeAddTabBar(ImGuiDockNode* node)\n{\n    IM_ASSERT(node->TabBar == NULL);\n    node->TabBar = IM_NEW(ImGuiTabBar);\n}\n\nstatic void ImGui::DockNodeRemoveTabBar(ImGuiDockNode* node)\n{\n    if (node->TabBar == NULL)\n        return;\n    IM_DELETE(node->TabBar);\n    node->TabBar = NULL;\n}\n\nstatic bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window)\n{\n    if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext)\n        return false;\n\n    ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass;\n    ImGuiWindowClass* payload_class = &payload->WindowClass;\n    if (host_class->ClassId != payload_class->ClassId)\n    {\n        bool pass = false;\n        if (host_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0)\n            pass = true;\n        if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0)\n            pass = true;\n        if (!pass)\n            return false;\n    }\n\n    // Prevent docking any window created above a popup\n    // Technically we should support it (e.g. in the case of a long-lived modal window that had fancy docking features),\n    // by e.g. adding a 'if (!ImGui::IsWindowWithinBeginStackOf(host_window, popup_window))' test.\n    // But it would requires more work on our end because the dock host windows is technically created in NewFrame()\n    // and our ->ParentXXX and ->RootXXX pointers inside windows are currently mislading or lacking.\n    ImGuiContext& g = *GImGui;\n    for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--)\n        if (ImGuiWindow* popup_window = g.OpenPopupStack[i].Window)\n            if (ImGui::IsWindowWithinBeginStackOf(payload, popup_window))   // Payload is created from within a popup begin stack.\n                return false;\n\n    return true;\n}\n\nstatic bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload)\n{\n    if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) // FIXME-DOCK: Missing filtering\n        return true;\n\n    const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1;\n    for (int payload_n = 0; payload_n < payload_count; payload_n++)\n    {\n        ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload;\n        if (DockNodeIsDropAllowedOne(payload, host_window))\n            return true;\n    }\n    return false;\n}\n\n// window menu button == collapse button when not in a dock node.\n// FIXME: This is similar to RenderWindowTitleBarContents(), may want to share code.\nstatic void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n\n    ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + g.FontSize + g.Style.FramePadding.y * 2.0f);\n    if (out_title_rect) { *out_title_rect = r; }\n\n    r.Min.x += style.WindowBorderSize;\n    r.Max.x -= style.WindowBorderSize;\n\n    float button_sz = g.FontSize;\n    r.Min.x += style.FramePadding.x;\n    r.Max.x -= style.FramePadding.x;\n    ImVec2 window_menu_button_pos = ImVec2(r.Min.x, r.Min.y + style.FramePadding.y);\n    if (node->HasCloseButton)\n    {\n        if (out_close_button_pos) *out_close_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y);\n        r.Max.x -= button_sz + style.ItemInnerSpacing.x;\n    }\n    if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Left)\n    {\n        r.Min.x += button_sz + style.ItemInnerSpacing.x;\n    }\n    else if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Right)\n    {\n        window_menu_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y);\n        r.Max.x -= button_sz + style.ItemInnerSpacing.x;\n    }\n    if (out_tab_bar_rect) { *out_tab_bar_rect = r; }\n    if (out_window_menu_button_pos) { *out_window_menu_button_pos = window_menu_button_pos; }\n}\n\nvoid ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired)\n{\n    ImGuiContext& g = *GImGui;\n    const float dock_spacing = g.Style.ItemInnerSpacing.x;\n    const ImGuiAxis axis = (dir == ImGuiDir_Left || dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;\n    pos_new[axis ^ 1] = pos_old[axis ^ 1];\n    size_new[axis ^ 1] = size_old[axis ^ 1];\n\n    // Distribute size on given axis (with a desired size or equally)\n    const float w_avail = size_old[axis] - dock_spacing;\n    if (size_new_desired[axis] > 0.0f && size_new_desired[axis] <= w_avail * 0.5f)\n    {\n        size_new[axis] = size_new_desired[axis];\n        size_old[axis] = IM_TRUNC(w_avail - size_new[axis]);\n    }\n    else\n    {\n        size_new[axis] = IM_TRUNC(w_avail * 0.5f);\n        size_old[axis] = IM_TRUNC(w_avail - size_new[axis]);\n    }\n\n    // Position each node\n    if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)\n    {\n        pos_new[axis] = pos_old[axis] + size_old[axis] + dock_spacing;\n    }\n    else if (dir == ImGuiDir_Left || dir == ImGuiDir_Up)\n    {\n        pos_new[axis] = pos_old[axis];\n        pos_old[axis] = pos_new[axis] + size_new[axis] + dock_spacing;\n    }\n}\n\n// Retrieve the drop rectangles for a given direction or for the center + perform hit testing.\nbool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos)\n{\n    ImGuiContext& g = *GImGui;\n\n    const float parent_smaller_axis = ImMin(parent.GetWidth(), parent.GetHeight());\n    const float hs_for_central_nodes = ImMin(g.FontSize * 1.5f, ImMax(g.FontSize * 0.5f, parent_smaller_axis / 8.0f));\n    float hs_w; // Half-size, longer axis\n    float hs_h; // Half-size, smaller axis\n    ImVec2 off; // Distance from edge or center\n    if (outer_docking)\n    {\n        //hs_w = ImTrunc(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0f, g.FontSize * 0.5f, g.FontSize * 8.0f));\n        //hs_h = ImTrunc(hs_w * 0.15f);\n        //off = ImVec2(ImTrunc(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImTrunc(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h));\n        hs_w = ImTrunc(hs_for_central_nodes * 1.50f);\n        hs_h = ImTrunc(hs_for_central_nodes * 0.80f);\n        off = ImTrunc(ImVec2(parent.GetWidth() * 0.5f - hs_h, parent.GetHeight() * 0.5f - hs_h));\n    }\n    else\n    {\n        hs_w = ImTrunc(hs_for_central_nodes);\n        hs_h = ImTrunc(hs_for_central_nodes * 0.90f);\n        off = ImTrunc(ImVec2(hs_w * 2.40f, hs_w * 2.40f));\n    }\n\n    ImVec2 c = ImTrunc(parent.GetCenter());\n    if      (dir == ImGuiDir_None)  { out_r = ImRect(c.x - hs_w, c.y - hs_w,         c.x + hs_w, c.y + hs_w);         }\n    else if (dir == ImGuiDir_Up)    { out_r = ImRect(c.x - hs_w, c.y - off.y - hs_h, c.x + hs_w, c.y - off.y + hs_h); }\n    else if (dir == ImGuiDir_Down)  { out_r = ImRect(c.x - hs_w, c.y + off.y - hs_h, c.x + hs_w, c.y + off.y + hs_h); }\n    else if (dir == ImGuiDir_Left)  { out_r = ImRect(c.x - off.x - hs_h, c.y - hs_w, c.x - off.x + hs_h, c.y + hs_w); }\n    else if (dir == ImGuiDir_Right) { out_r = ImRect(c.x + off.x - hs_h, c.y - hs_w, c.x + off.x + hs_h, c.y + hs_w); }\n\n    if (test_mouse_pos == NULL)\n        return false;\n\n    ImRect hit_r = out_r;\n    if (!outer_docking)\n    {\n        // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides\n        hit_r.Expand(ImTrunc(hs_w * 0.30f));\n        ImVec2 mouse_delta = (*test_mouse_pos - c);\n        float mouse_delta_len2 = ImLengthSqr(mouse_delta);\n        float r_threshold_center = hs_w * 1.4f;\n        float r_threshold_sides = hs_w * (1.4f + 1.2f);\n        if (mouse_delta_len2 < r_threshold_center * r_threshold_center)\n            return (dir == ImGuiDir_None);\n        if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides)\n            return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y));\n    }\n    return hit_r.Contains(*test_mouse_pos);\n}\n\n// host_node may be NULL if the window doesn't have a DockNode already.\n// FIXME-DOCK: This is misnamed since it's also doing the filtering.\nstatic void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking)\n{\n    ImGuiContext& g = *GImGui;\n\n    // There is an edge case when docking into a dockspace which only has inactive nodes.\n    // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive.\n    // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference.\n    if (payload_node == NULL)\n        payload_node = payload_window->DockNodeAsHost;\n    ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node;\n    if (ref_node_for_rect)\n        IM_ASSERT(ref_node_for_rect->IsVisible == true);\n\n    // Filter, figure out where we are allowed to dock\n    ImGuiDockNodeFlags src_node_flags = payload_node ? payload_node->MergedFlags : payload_window->WindowClass.DockNodeFlagsOverrideSet;\n    ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->MergedFlags : host_window->WindowClass.DockNodeFlagsOverrideSet;\n    data->IsCenterAvailable = true;\n    if (is_outer_docking)\n        data->IsCenterAvailable = false;\n    else if (g.IO.ConfigDockingNoDockingOver)\n        data->IsCenterAvailable = false;\n    else if (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe)\n        data->IsCenterAvailable = false;\n    else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverCentralNode) && host_node->IsCentralNode())\n        data->IsCenterAvailable = false;\n    else if ((!host_node || !host_node->IsEmpty()) && payload_node && payload_node->IsSplitNode() && (payload_node->OnlyNodeWithWindows == NULL)) // Is _visibly_ split?\n        data->IsCenterAvailable = false;\n    else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther) && (!host_node || !host_node->IsEmpty()))\n        data->IsCenterAvailable = false;\n    else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverEmpty) && host_node && host_node->IsEmpty())\n        data->IsCenterAvailable = false;\n\n    data->IsSidesAvailable = true;\n    if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplit) || g.IO.ConfigDockingNoSplit)\n        data->IsSidesAvailable = false;\n    else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode())\n        data->IsSidesAvailable = false;\n    else if (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther)\n        data->IsSidesAvailable = false;\n\n    // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split)\n    data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (payload_window->HasCloseButton);\n    data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0);\n    data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos;\n    data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size;\n\n    // Calculate drop shapes geometry for allowed splitting directions\n    IM_ASSERT(ImGuiDir_None == -1);\n    data->SplitNode = host_node;\n    data->SplitDir = ImGuiDir_None;\n    data->IsSplitDirExplicit = false;\n    if (!host_window->Collapsed)\n        for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)\n        {\n            if (dir == ImGuiDir_None && !data->IsCenterAvailable)\n                continue;\n            if (dir != ImGuiDir_None && !data->IsSidesAvailable)\n                continue;\n            if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos))\n            {\n                data->SplitDir = (ImGuiDir)dir;\n                data->IsSplitDirExplicit = true;\n            }\n        }\n\n    // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar\n    data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable);\n    if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift)\n        data->IsDropAllowed = false;\n\n    // Calculate split area\n    data->SplitRatio = 0.0f;\n    if (data->SplitDir != ImGuiDir_None)\n    {\n        ImGuiDir split_dir = data->SplitDir;\n        ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;\n        ImVec2 pos_new, pos_old = data->FutureNode.Pos;\n        ImVec2 size_new, size_old = data->FutureNode.Size;\n        DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, payload_window->Size);\n\n        // Calculate split ratio so we can pass it down the docking request\n        float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]);\n        data->FutureNode.Pos = pos_new;\n        data->FutureNode.Size = size_new;\n        data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio);\n    }\n}\n\nstatic void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.CurrentWindow == host_window);   // Because we rely on font size to calculate tab sizes\n\n    // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent.\n    // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes.\n    const bool is_transparent_payload = g.IO.ConfigDockingTransparentPayload;\n\n    // In case the two windows involved are on different viewports, we will draw the overlay on each of them.\n    int overlay_draw_lists_count = 0;\n    ImDrawList* overlay_draw_lists[2];\n    overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(host_window->Viewport);\n    if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload)\n        overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport);\n\n    // Draw main preview rectangle\n    const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f);\n    const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f);\n    const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f);\n    const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, is_transparent_payload ? 0.80f : 0.60f);\n\n    // Display area preview\n    const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0);\n    if (data->IsDropAllowed)\n    {\n        ImRect overlay_rect = data->FutureNode.Rect();\n        if (data->SplitDir == ImGuiDir_None && can_preview_tabs)\n            overlay_rect.Min.y += GetFrameHeight();\n        if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable)\n            for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)\n                overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding, CalcRoundingFlagsForRectInRect(overlay_rect, host_window->Rect(), g.Style.DockingSeparatorSize));\n    }\n\n    // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read)\n    if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable)\n    {\n        // Compute target tab bar geometry so we can locate our preview tabs\n        ImRect tab_bar_rect;\n        DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL, NULL);\n        ImVec2 tab_pos = tab_bar_rect.Min;\n        if (host_node && host_node->TabBar)\n        {\n            if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar())\n                tab_pos.x += host_node->TabBar->WidthAllTabs + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission.\n            else\n                tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]).x;\n        }\n        else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost))\n        {\n            tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window).x; // Account for slight offset which will be added when changing from title bar to tab bar\n        }\n\n        // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows)\n        if (root_payload->DockNodeAsHost)\n            IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size <= root_payload->DockNodeAsHost->TabBar->Tabs.Size);\n        ImGuiTabBar* tab_bar_with_payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar : NULL;\n        const int payload_count = tab_bar_with_payload ? tab_bar_with_payload->Tabs.Size : 1;\n        for (int payload_n = 0; payload_n < payload_count; payload_n++)\n        {\n            // DockNode's TabBar may have non-window Tabs manually appended by user\n            ImGuiWindow* payload_window = tab_bar_with_payload ? tab_bar_with_payload->Tabs[payload_n].Window : root_payload;\n            if (tab_bar_with_payload && payload_window == NULL)\n                continue;\n            if (!DockNodeIsDropAllowedOne(payload_window, host_window))\n                continue;\n\n            // Calculate the tab bounding box for each payload window\n            ImVec2 tab_size = TabItemCalcSize(payload_window);\n            ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y);\n            tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x;\n            const ImU32 overlay_col_text = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_Text]);\n            const ImU32 overlay_col_tabs = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_TabSelected]);\n            const ImU32 overlay_col_unsaved_marker = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_UnsavedMarker]);\n            PushStyleColor(ImGuiCol_Text, overlay_col_text);\n            PushStyleColor(ImGuiCol_UnsavedMarker, overlay_col_unsaved_marker);\n            for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)\n            {\n                ImGuiTabItemFlags tab_flags = (payload_window->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0;\n                if (!tab_bar_rect.Contains(tab_bb))\n                    overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max);\n                TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs);\n                TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload_window->Name, 0, 0, false, NULL, NULL);\n                if (!tab_bar_rect.Contains(tab_bb))\n                    overlay_draw_lists[overlay_n]->PopClipRect();\n            }\n            PopStyleColor(2);\n        }\n    }\n\n    // Display drop boxes\n    const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding);\n    for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)\n    {\n        if (!data->DropRectsDraw[dir + 1].IsInverted())\n        {\n            ImRect draw_r = data->DropRectsDraw[dir + 1];\n            ImRect draw_r_in = draw_r;\n            draw_r_in.Expand(-2.0f);\n            ImU32 overlay_col = (data->SplitDir == (ImGuiDir)dir && data->IsSplitDirExplicit) ? overlay_col_drop_hovered : overlay_col_drop;\n            for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)\n            {\n                ImVec2 center = ImFloor(draw_r_in.GetCenter());\n                overlay_draw_lists[overlay_n]->AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding);\n                overlay_draw_lists[overlay_n]->AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding);\n                if (dir == ImGuiDir_Left || dir == ImGuiDir_Right)\n                    overlay_draw_lists[overlay_n]->AddLine(ImVec2(center.x, draw_r_in.Min.y), ImVec2(center.x, draw_r_in.Max.y), overlay_col_lines);\n                if (dir == ImGuiDir_Up || dir == ImGuiDir_Down)\n                    overlay_draw_lists[overlay_n]->AddLine(ImVec2(draw_r_in.Min.x, center.y), ImVec2(draw_r_in.Max.x, center.y), overlay_col_lines);\n            }\n        }\n\n        // Stop after ImGuiDir_None\n        if ((host_node && (host_node->MergedFlags & ImGuiDockNodeFlags_NoDockingSplit)) || g.IO.ConfigDockingNoSplit)\n            return;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Docking: ImGuiDockNode Tree manipulation functions\n//-----------------------------------------------------------------------------\n// - DockNodeTreeSplit()\n// - DockNodeTreeMerge()\n// - DockNodeTreeUpdatePosSize()\n// - DockNodeTreeUpdateSplitterFindTouchingNode()\n// - DockNodeTreeUpdateSplitter()\n// - DockNodeTreeFindFallbackLeafNode()\n// - DockNodeTreeFindNodeByPos()\n//-----------------------------------------------------------------------------\n\nvoid ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(split_axis != ImGuiAxis_None);\n\n    ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0);\n    child_0->ParentNode = parent_node;\n\n    ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, 0);\n    child_1->ParentNode = parent_node;\n\n    ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1;\n    DockNodeMoveChildNodes(child_inheritor, parent_node);\n    parent_node->ChildNodes[0] = child_0;\n    parent_node->ChildNodes[1] = child_1;\n    parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow;\n    parent_node->SplitAxis = split_axis;\n    parent_node->VisibleWindow = NULL;\n    parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode;\n\n    float size_avail = (parent_node->Size[split_axis] - g.Style.DockingSeparatorSize);\n    size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f);\n    IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting.\n    child_0->SizeRef = child_1->SizeRef = parent_node->Size;\n    child_0->SizeRef[split_axis] = ImTrunc(size_avail * split_ratio);\n    child_1->SizeRef[split_axis] = ImTrunc(size_avail - child_0->SizeRef[split_axis]);\n\n    DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node);\n    DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID);\n    DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(parent_node));\n    DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size);\n\n    // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property)\n    child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;\n    child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;\n    child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_;\n    parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_;\n    child_0->UpdateMergedFlags();\n    child_1->UpdateMergedFlags();\n    parent_node->UpdateMergedFlags();\n    if (child_inheritor->IsCentralNode())\n        DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor;\n}\n\nvoid ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child)\n{\n    // When called from DockContextProcessUndockNode() it is possible that one of the child is NULL.\n    ImGuiContext& g = *GImGui;\n    ImGuiDockNode* child_0 = parent_node->ChildNodes[0];\n    ImGuiDockNode* child_1 = parent_node->ChildNodes[1];\n    IM_ASSERT(child_0 || child_1);\n    IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1);\n    if ((child_0 && child_0->Windows.Size > 0) || (child_1 && child_1->Windows.Size > 0))\n    {\n        IM_ASSERT(parent_node->TabBar == NULL);\n        IM_ASSERT(parent_node->Windows.Size == 0);\n    }\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockNodeTreeMerge: 0x%08X + 0x%08X back into parent 0x%08X\\n\", child_0 ? child_0->ID : 0, child_1 ? child_1->ID : 0, parent_node->ID);\n\n    ImVec2 backup_last_explicit_size = parent_node->SizeRef;\n    DockNodeMoveChildNodes(parent_node, merge_lead_child);\n    if (child_0)\n    {\n        DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows\n        DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID);\n    }\n    if (child_1)\n    {\n        DockNodeMoveWindows(parent_node, child_1);\n        DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID);\n    }\n    DockNodeApplyPosSizeToWindows(parent_node);\n    parent_node->AuthorityForPos = parent_node->AuthorityForSize = parent_node->AuthorityForViewport = ImGuiDataAuthority_Auto;\n    parent_node->VisibleWindow = merge_lead_child->VisibleWindow;\n    parent_node->SizeRef = backup_last_explicit_size;\n\n    // Flags transfer\n    parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag\n    parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;\n    parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;\n    parent_node->LocalFlagsInWindows = (child_0 ? child_0->LocalFlagsInWindows : 0) | (child_1 ? child_1->LocalFlagsInWindows : 0); // FIXME: Would be more consistent to update from actual windows\n    parent_node->UpdateMergedFlags();\n\n    if (child_0)\n        DockContextDeleteNode(ctx, child_0);\n    if (child_1)\n        DockContextDeleteNode(ctx, child_1);\n}\n\n// Update Pos/Size for a node hierarchy (don't affect child Windows yet)\n// (Depth-first, Pre-Order)\nvoid ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node)\n{\n    // During the regular dock node update we write to all nodes.\n    // 'only_write_to_single_node' is only set when turning a node visible mid-frame and we need its size right-away.\n    ImGuiContext& g = *GImGui;\n    const bool write_to_node = only_write_to_single_node == NULL || only_write_to_single_node == node;\n    if (write_to_node)\n    {\n        node->Pos = pos;\n        node->Size = size;\n    }\n\n    if (node->IsLeafNode())\n        return;\n\n    ImGuiDockNode* child_0 = node->ChildNodes[0];\n    ImGuiDockNode* child_1 = node->ChildNodes[1];\n    ImVec2 child_0_pos = pos, child_1_pos = pos;\n    ImVec2 child_0_size = size, child_1_size = size;\n\n    const bool child_0_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_0));\n    const bool child_1_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_1));\n    const bool child_0_is_or_will_be_visible = child_0->IsVisible || child_0_is_toward_single_node;\n    const bool child_1_is_or_will_be_visible = child_1->IsVisible || child_1_is_toward_single_node;\n\n    if (child_0_is_or_will_be_visible && child_1_is_or_will_be_visible)\n    {\n        const float spacing = g.Style.DockingSeparatorSize;\n        const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;\n        const float size_avail = ImMax(size[axis] - spacing, 0.0f);\n\n        // Size allocation policy\n        // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows.\n        const float size_min_each = ImTrunc(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f);\n\n        // FIXME: Blocks 2) and 3) are essentially doing nearly the same thing.\n        // Difference are: write-back to SizeRef; application of a minimum size; rounding before ImTrunc()\n        // Clarify and rework differences between Size & SizeRef and purpose of WantLockSizeOnce\n\n        // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge)\n        if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce)\n        {\n            child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]);\n            child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);\n            IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);\n        }\n        else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce)\n        {\n            child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]);\n            child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]);\n            IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);\n        }\n        else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce)\n        {\n            // FIXME-DOCK: We cannot honor the requested size, so apply ratio.\n            // Currently this path will only be taken if code programmatically sets WantLockSizeOnce\n            float split_ratio = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]);\n            child_0_size[axis] = child_0->SizeRef[axis] = ImTrunc(size_avail * split_ratio);\n            child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);\n            IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);\n        }\n\n        // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node\n        else if (child_0->SizeRef[axis] != 0.0f && child_1->HasCentralNodeChild)\n        {\n            child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]);\n            child_1_size[axis] = (size_avail - child_0_size[axis]);\n        }\n        else if (child_1->SizeRef[axis] != 0.0f && child_0->HasCentralNodeChild)\n        {\n            child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]);\n            child_0_size[axis] = (size_avail - child_1_size[axis]);\n        }\n        else\n        {\n            // 4) Otherwise distribute according to the relative ratio of each SizeRef value\n            float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]);\n            child_0_size[axis] = ImMax(size_min_each, ImTrunc(size_avail * split_ratio + 0.5f));\n            child_1_size[axis] = (size_avail - child_0_size[axis]);\n        }\n\n        child_1_pos[axis] += spacing + child_0_size[axis];\n    }\n\n    if (only_write_to_single_node == NULL)\n        child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false;\n\n    const bool child_0_recurse = only_write_to_single_node ? child_0_is_toward_single_node : child_0->IsVisible;\n    const bool child_1_recurse = only_write_to_single_node ? child_1_is_toward_single_node : child_1->IsVisible;\n    if (child_0_recurse)\n        DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size);\n    if (child_1_recurse)\n        DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size);\n}\n\nstatic void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector<ImGuiDockNode*>* touching_nodes)\n{\n    if (node->IsLeafNode())\n    {\n        touching_nodes->push_back(node);\n        return;\n    }\n    if (node->ChildNodes[0]->IsVisible)\n        if (node->SplitAxis != axis || side == 0 || !node->ChildNodes[1]->IsVisible)\n            DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[0], axis, side, touching_nodes);\n    if (node->ChildNodes[1]->IsVisible)\n        if (node->SplitAxis != axis || side == 1 || !node->ChildNodes[0]->IsVisible)\n            DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes);\n}\n\n// (Depth-First, Pre-Order)\nvoid ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)\n{\n    if (node->IsLeafNode())\n        return;\n\n    ImGuiContext& g = *GImGui;\n\n    ImGuiDockNode* child_0 = node->ChildNodes[0];\n    ImGuiDockNode* child_1 = node->ChildNodes[1];\n    if (child_0->IsVisible && child_1->IsVisible)\n    {\n        // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally)\n        const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;\n        IM_ASSERT(axis != ImGuiAxis_None);\n        ImRect bb;\n        bb.Min = child_0->Pos;\n        bb.Max = child_1->Pos;\n        bb.Min[axis] += child_0->Size[axis];\n        bb.Max[axis ^ 1] += child_1->Size[axis ^ 1];\n        //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255));\n\n        const ImGuiDockNodeFlags merged_flags = child_0->MergedFlags | child_1->MergedFlags; // Merged flags for BOTH childs\n        const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY;\n        if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag))\n        {\n            ImGuiWindow* window = g.CurrentWindow;\n            window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding);\n        }\n        else\n        {\n            //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node.\n            //bb.Max[axis] -= 1;\n            PushID(node->ID);\n\n            // Find resizing limits by gathering list of nodes that are touching the splitter line.\n            ImVector<ImGuiDockNode*> touching_nodes[2];\n            float min_size = g.Style.WindowMinSize[axis];\n            float resize_limits[2];\n            resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size;\n            resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size;\n\n            ImGuiID splitter_id = GetID(\"##Splitter\");\n            if (g.ActiveId == splitter_id) // Only process when splitter is active\n            {\n                DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]);\n                DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]);\n                for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++)\n                    resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size);\n                for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++)\n                    resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size);\n\n                // [DEBUG] Render touching nodes & limits\n                /*\n                ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());\n                for (int n = 0; n < 2; n++)\n                {\n                    for (int touching_node_n = 0; touching_node_n < touching_nodes[n].Size; touching_node_n++)\n                        draw_list->AddRect(touching_nodes[n][touching_node_n]->Pos, touching_nodes[n][touching_node_n]->Pos + touching_nodes[n][touching_node_n]->Size, IM_COL32(0, 255, 0, 255));\n                    if (axis == ImGuiAxis_X)\n                        draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f);\n                    else\n                        draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f);\n                }\n                */\n            }\n\n            // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters\n            float cur_size_0 = child_0->Size[axis];\n            float cur_size_1 = child_1->Size[axis];\n            float min_size_0 = resize_limits[0] - child_0->Pos[axis];\n            float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1];\n            ImU32 bg_col = GetColorU32(ImGuiCol_WindowBg);\n            if (SplitterBehavior(bb, GetID(\"##Splitter\"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, g.WindowsBorderHoverPadding, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER, bg_col))\n            {\n                if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0)\n                {\n                    child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0;\n                    child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis];\n                    child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1;\n\n                    // Lock the size of every node that is a sibling of the node we are touching\n                    // This might be less desirable if we can merge sibling of a same axis into the same parental level.\n                    for (int side_n = 0; side_n < 2; side_n++)\n                        for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++)\n                        {\n                            ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n];\n                            //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());\n                            //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255));\n                            while (touching_node->ParentNode != node)\n                            {\n                                if (touching_node->ParentNode->SplitAxis == axis)\n                                {\n                                    // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize().\n                                    ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n];\n                                    node_to_preserve->WantLockSizeOnce = true;\n                                    //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255));\n                                    //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100));\n                                }\n                                touching_node = touching_node->ParentNode;\n                            }\n                        }\n\n                    DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size);\n                    DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size);\n                    MarkIniSettingsDirty();\n                }\n            }\n            PopID();\n        }\n    }\n\n    if (child_0->IsVisible)\n        DockNodeTreeUpdateSplitter(child_0);\n    if (child_1->IsVisible)\n        DockNodeTreeUpdateSplitter(child_1);\n}\n\nImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node)\n{\n    if (node->IsLeafNode())\n        return node;\n    if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[0]))\n        return leaf_node;\n    if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[1]))\n        return leaf_node;\n    return NULL;\n}\n\nImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos)\n{\n    if (!node->IsVisible)\n        return NULL;\n\n    const float dock_spacing = 0.0f;// g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE?\n    ImRect r(node->Pos, node->Pos + node->Size);\n    r.Expand(dock_spacing * 0.5f);\n    bool inside = r.Contains(pos);\n    if (!inside)\n        return NULL;\n\n    if (node->IsLeafNode())\n        return node;\n    if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos))\n        return hovered_node;\n    if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos))\n        return hovered_node;\n\n    // This means we are hovering over the splitter/spacing of a parent node\n    return node;\n}\n\n//-----------------------------------------------------------------------------\n// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)\n//-----------------------------------------------------------------------------\n// - SetWindowDock() [Internal]\n// - DockSpace()\n// - DockSpaceOverViewport()\n//-----------------------------------------------------------------------------\n\n// [Internal] Called via SetNextWindowDockID()\nvoid ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond)\n{\n    // Test condition (NB: bit 0 is always true) and clear flags for next time\n    if (cond && (window->SetWindowDockAllowFlags & cond) == 0)\n        return;\n    window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);\n\n    if (window->DockId == dock_id)\n        return;\n\n    // If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot\n    ImGuiContext& g = *GImGui;\n    if (ImGuiDockNode* new_node = DockContextFindNodeByID(&g, dock_id))\n        if (new_node->IsSplitNode())\n        {\n            // Policy: Find central node or latest focused node. We first move back to our root node.\n            new_node = DockNodeGetRootNode(new_node);\n            if (new_node->CentralNode)\n            {\n                IM_ASSERT(new_node->CentralNode->IsCentralNode());\n                dock_id = new_node->CentralNode->ID;\n            }\n            else\n            {\n                dock_id = new_node->LastFocusedNodeId;\n            }\n        }\n\n    if (window->DockId == dock_id)\n        return;\n\n    if (window->DockNode)\n        DockNodeRemoveWindow(window->DockNode, window, 0);\n    window->DockId = dock_id;\n}\n\n// Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default.\n// The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors.\n// DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app.\n// When ImGuiDockNodeFlags_KeepAliveOnly is set, nothing is submitted in the current window (function may be called from any location).\nImGuiID ImGui::DockSpace(ImGuiID dockspace_id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindowRead();\n    if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))\n        return 0;\n\n    // Early out if parent window is hidden/collapsed\n    // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960.\n    // If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true.\n    if (window->SkipItems)\n        flags |= ImGuiDockNodeFlags_KeepAliveOnly;\n    if ((flags & ImGuiDockNodeFlags_KeepAliveOnly) == 0)\n        window = GetCurrentWindow(); // call to set window->WriteAccessed = true;\n\n    IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0); // Flag is automatically set by DockSpace() as LocalFlags, not SharedFlags!\n    IM_ASSERT((flags & ImGuiDockNodeFlags_CentralNode) == 0); // Flag is automatically set by DockSpace() as LocalFlags, not SharedFlags! (#8145)\n\n    IM_ASSERT(dockspace_id != 0);\n    ImGuiDockNode* node = DockContextFindNodeByID(&g, dockspace_id);\n    if (node == NULL)\n    {\n        IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockSpace: dockspace node 0x%08X created\\n\", dockspace_id);\n        node = DockContextAddNode(&g, dockspace_id);\n        node->SetLocalFlags(ImGuiDockNodeFlags_CentralNode);\n    }\n    if (window_class && window_class->ClassId != node->WindowClass.ClassId)\n        IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockSpace: dockspace node 0x%08X: setup WindowClass 0x%08X -> 0x%08X\\n\", dockspace_id, node->WindowClass.ClassId, window_class->ClassId);\n    node->SharedFlags = flags;\n    node->WindowClass = window_class ? *window_class : ImGuiWindowClass();\n\n    // When a DockSpace transitioned form implicit to explicit this may be called a second time\n    // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again.\n    if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly))\n    {\n        IM_ASSERT(node->IsDockSpace() == false && \"Cannot call DockSpace() twice a frame with the same ID\");\n        node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace);\n        return dockspace_id;\n    }\n    node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace);\n\n    // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible\n    if (flags & ImGuiDockNodeFlags_KeepAliveOnly)\n    {\n        node->LastFrameAlive = g.FrameCount;\n        return dockspace_id;\n    }\n\n    const ImVec2 content_avail = GetContentRegionAvail();\n    ImVec2 size = ImTrunc(size_arg);\n    if (size.x <= 0.0f)\n        size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)\n    if (size.y <= 0.0f)\n        size.y = ImMax(content_avail.y + size.y, 4.0f);\n    IM_ASSERT(size.x > 0.0f && size.y > 0.0f);\n\n    node->Pos = window->DC.CursorPos;\n    node->Size = node->SizeRef = size;\n    SetNextWindowPos(node->Pos);\n    SetNextWindowSize(node->Size);\n    g.NextWindowData.PosUndock = false;\n\n    // FIXME-DOCK: Why do we need a child window to host a dockspace, could we host it in the existing window?\n    // FIXME-DOCK: What is the reason for not simply calling BeginChild()? (OK to have a reason but should be commented)\n    ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost;\n    window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar;\n    window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;\n    window_flags |= ImGuiWindowFlags_NoBackground;\n\n    char title[256];\n    ImFormatString(title, IM_COUNTOF(title), \"%s/DockSpace_%08X\", window->Name, dockspace_id);\n\n    PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);\n    Begin(title, NULL, window_flags);\n    PopStyleVar();\n\n    ImGuiWindow* host_window = g.CurrentWindow;\n    DockNodeSetupHostWindow(node, host_window);\n    host_window->ChildId = window->GetID(title);\n    node->OnlyNodeWithWindows = NULL;\n\n    IM_ASSERT(node->IsRootNode());\n\n    // We need to handle the rare case were a central node is missing.\n    // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace.\n    // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split.\n    // It would also be ambiguous to attempt to assign a central node while there are split nodes, so we wait until there's a single node remaining.\n    // The specific sub-property of _CentralNode we are interested in recovering here is the \"Don't delete when empty\" property,\n    // as it doesn't make sense for an empty dockspace to not have this property.\n    if (node->IsLeafNode() && !node->IsCentralNode())\n        node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_CentralNode);\n\n    // Update the node\n    DockNodeUpdate(node);\n\n    End();\n\n    ImRect bb(node->Pos, node->Pos + size);\n    ItemSize(size);\n    ItemAdd(bb, dockspace_id, NULL, ImGuiItemFlags_NoNav); // Not a nav point (could be, would need to draw the nav rect and replicate/refactor activation from BeginChild(), but seems like CTRL+Tab works better here?)\n    if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && IsWindowChildOf(g.HoveredWindow, host_window, false, true)) // To fullfill IsItemHovered(), similar to EndChild()\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;\n\n    return dockspace_id;\n}\n\n// Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode!\n// The limitation with this call is that your window won't have a local menu bar, but you can also use BeginMainMenuBar().\n// Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function.\n// If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it.\nImGuiID ImGui::DockSpaceOverViewport(ImGuiID dockspace_id, const ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class)\n{\n    if (viewport == NULL)\n        viewport = GetMainViewport();\n\n    // Submit a window filling the entire viewport\n    SetNextWindowPos(viewport->WorkPos);\n    SetNextWindowSize(viewport->WorkSize);\n    SetNextWindowViewport(viewport->ID);\n\n    ImGuiWindowFlags host_window_flags = 0;\n    host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;\n    host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;\n    if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)\n        host_window_flags |= ImGuiWindowFlags_NoBackground;\n\n    // FIXME-OPT: When using ImGuiDockNodeFlags_KeepAliveOnly with DockSpaceOverViewport() we might be able to spare submitting the window,\n    // since DockSpace() with that flag doesn't need a window. We'd only need to compute the default ID accordingly.\n    if (dockspace_flags & ImGuiDockNodeFlags_KeepAliveOnly)\n        host_window_flags |= ImGuiWindowFlags_NoMouseInputs;\n\n    char label[32];\n    ImFormatString(label, IM_COUNTOF(label), \"WindowOverViewport_%08X\", viewport->ID);\n\n    PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);\n    PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);\n    PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));\n    Begin(label, NULL, host_window_flags);\n    PopStyleVar(3);\n\n    // Submit the dockspace\n    if (dockspace_id == 0)\n        dockspace_id = GetID(\"DockSpace\");\n    DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class);\n\n    End();\n\n    return dockspace_id;\n}\n\n//-----------------------------------------------------------------------------\n// Docking: Builder Functions\n//-----------------------------------------------------------------------------\n// Very early end-user API to manipulate dock nodes.\n// Only available in imgui_internal.h. Expect this API to change/break!\n// It is expected that those functions are all called _before_ the dockspace node submission.\n//-----------------------------------------------------------------------------\n// - DockBuilderDockWindow()\n// - DockBuilderGetNode()\n// - DockBuilderSetNodePos()\n// - DockBuilderSetNodeSize()\n// - DockBuilderAddNode()\n// - DockBuilderRemoveNode()\n// - DockBuilderRemoveNodeChildNodes()\n// - DockBuilderRemoveNodeDockedWindows()\n// - DockBuilderSplitNode()\n// - DockBuilderCopyNodeRec()\n// - DockBuilderCopyNode()\n// - DockBuilderCopyWindowSettings()\n// - DockBuilderCopyDockSpace()\n// - DockBuilderFinish()\n//-----------------------------------------------------------------------------\n\nvoid ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id)\n{\n    // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1)\n    ImGuiContext& g = *GImGui; IM_UNUSED(g);\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockBuilderDockWindow '%s' to node 0x%08X\\n\", window_name, node_id);\n    ImGuiID window_id = ImHashStr(window_name);\n    if (ImGuiWindow* window = FindWindowByID(window_id))\n    {\n        // Apply to created window\n        ImGuiID prev_node_id = window->DockId;\n        SetWindowDock(window, node_id, ImGuiCond_Always);\n        if (window->DockId != prev_node_id)\n            window->DockOrder = -1;\n    }\n    else\n    {\n        // Apply to settings\n        ImGuiWindowSettings* settings = FindWindowSettingsByID(window_id);\n        if (settings == NULL)\n            settings = CreateNewWindowSettings(window_name);\n        if (settings->DockId != node_id)\n            settings->DockOrder = -1;\n        settings->DockId = node_id;\n    }\n}\n\nImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id)\n{\n    ImGuiContext& g = *GImGui;\n    return DockContextFindNodeByID(&g, node_id);\n}\n\nvoid ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);\n    if (node == NULL)\n        return;\n    node->Pos = pos;\n    node->AuthorityForPos = ImGuiDataAuthority_DockNode;\n}\n\nvoid ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);\n    if (node == NULL)\n        return;\n    IM_ASSERT(size.x > 0.0f && size.y > 0.0f);\n    node->Size = node->SizeRef = size;\n    node->AuthorityForSize = ImGuiDataAuthority_DockNode;\n}\n\n// Make sure to use the ImGuiDockNodeFlags_DockSpace flag to create a dockspace node! Otherwise this will create a floating node!\n// - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node.\n// - Dockspace node: calling DockBuilderSetNodePos() is unnecessary.\n// - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand!\n//   For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect.\n// - Use (id == 0) to let the system allocate a node identifier.\n// - Existing node with a same id will be removed.\nImGuiID ImGui::DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags)\n{\n    ImGuiContext& g = *GImGui; IM_UNUSED(g);\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockBuilderAddNode 0x%08X flags=%08X\\n\", node_id, flags);\n\n    if (node_id != 0)\n        DockBuilderRemoveNode(node_id);\n\n    ImGuiDockNode* node = NULL;\n    if (flags & ImGuiDockNodeFlags_DockSpace)\n    {\n        DockSpace(node_id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly);\n        node = DockContextFindNodeByID(&g, node_id);\n    }\n    else\n    {\n        node = DockContextAddNode(&g, node_id);\n        node->SetLocalFlags(flags);\n    }\n    node->LastFrameAlive = g.FrameCount;   // Set this otherwise BeginDocked will undock during the same frame.\n    return node->ID;\n}\n\nvoid ImGui::DockBuilderRemoveNode(ImGuiID node_id)\n{\n    ImGuiContext& g = *GImGui; IM_UNUSED(g);\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockBuilderRemoveNode 0x%08X\\n\", node_id);\n\n    ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);\n    if (node == NULL)\n        return;\n    DockBuilderRemoveNodeDockedWindows(node_id, true);\n    DockBuilderRemoveNodeChildNodes(node_id);\n    // Node may have moved or deleted if e.g. any merge happened\n    node = DockContextFindNodeByID(&g, node_id);\n    if (node == NULL)\n        return;\n    if (node->IsCentralNode() && node->ParentNode)\n        node->ParentNode->SetLocalFlags(node->ParentNode->LocalFlags | ImGuiDockNodeFlags_CentralNode);\n    DockContextRemoveNode(&g, node, true);\n}\n\n// root_id = 0 to remove all, root_id != 0 to remove child of given node.\nvoid ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiDockContext* dc = &g.DockContext;\n\n    ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(&g, root_id) : NULL;\n    if (root_id && root_node == NULL)\n        return;\n    bool has_central_node = false;\n\n    ImGuiDataAuthority backup_root_node_authority_for_pos = root_node ? root_node->AuthorityForPos : ImGuiDataAuthority_Auto;\n    ImGuiDataAuthority backup_root_node_authority_for_size = root_node ? root_node->AuthorityForSize : ImGuiDataAuthority_Auto;\n\n    // Process active windows\n    ImVector<ImGuiDockNode*> nodes_to_remove;\n    for (int n = 0; n < dc->Nodes.Data.Size; n++)\n        if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)\n        {\n            bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id);\n            if (want_removal)\n            {\n                if (node->IsCentralNode())\n                    has_central_node = true;\n                if (root_id != 0)\n                    DockContextQueueNotifyRemovedNode(&g, node);\n                if (root_node)\n                {\n                    DockNodeMoveWindows(root_node, node);\n                    DockSettingsRenameNodeReferences(node->ID, root_node->ID);\n                }\n                nodes_to_remove.push_back(node);\n            }\n        }\n\n    // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge)\n    // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead)\n    if (root_node)\n    {\n        root_node->AuthorityForPos = backup_root_node_authority_for_pos;\n        root_node->AuthorityForSize = backup_root_node_authority_for_size;\n    }\n\n    // Apply to settings\n    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n        if (ImGuiID window_settings_dock_id = settings->DockId)\n            for (int n = 0; n < nodes_to_remove.Size; n++)\n                if (nodes_to_remove[n]->ID == window_settings_dock_id)\n                {\n                    settings->DockId = root_id;\n                    break;\n                }\n\n    // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes\n    if (nodes_to_remove.Size > 1)\n        ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst);\n    for (int n = 0; n < nodes_to_remove.Size; n++)\n        DockContextRemoveNode(&g, nodes_to_remove[n], false);\n\n    if (root_id == 0)\n    {\n        dc->Nodes.Clear();\n        dc->Requests.clear();\n    }\n    else if (has_central_node)\n    {\n        root_node->CentralNode = root_node;\n        root_node->SetLocalFlags(root_node->LocalFlags | ImGuiDockNodeFlags_CentralNode);\n    }\n}\n\nvoid ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs)\n{\n    // Clear references in settings\n    ImGuiContext& g = *GImGui;\n    if (clear_settings_refs)\n    {\n        for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n        {\n            bool want_removal = (root_id == 0) || (settings->DockId == root_id);\n            if (!want_removal && settings->DockId != 0)\n                if (ImGuiDockNode* node = DockContextFindNodeByID(&g, settings->DockId))\n                    if (DockNodeGetRootNode(node)->ID == root_id)\n                        want_removal = true;\n            if (want_removal)\n                settings->DockId = 0;\n        }\n    }\n\n    // Clear references in windows\n    for (int n = 0; n < g.Windows.Size; n++)\n    {\n        ImGuiWindow* window = g.Windows[n];\n        bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id);\n        if (want_removal)\n        {\n            const ImGuiID backup_dock_id = window->DockId;\n            IM_UNUSED(backup_dock_id);\n            DockContextProcessUndockWindow(&g, window, clear_settings_refs);\n            if (!clear_settings_refs)\n                IM_ASSERT(window->DockId == backup_dock_id);\n        }\n    }\n}\n\n// If 'out_id_at_dir' or 'out_id_at_opposite_dir' are non NULL, the function will write out the ID of the two new nodes created.\n// Return value is ID of the node at the specified direction, so same as (*out_id_at_dir) if that pointer is set.\n// FIXME-DOCK: We are not exposing nor using split_outer.\nImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(split_dir != ImGuiDir_None);\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockBuilderSplitNode: node 0x%08X, split_dir %d\\n\", id, split_dir);\n\n    ImGuiDockNode* node = DockContextFindNodeByID(&g, id);\n    if (node == NULL)\n    {\n        IM_ASSERT(node != NULL);\n        return 0;\n    }\n\n    ImGuiDockRequest req;\n    req.Type = ImGuiDockRequestType_Split;\n    req.DockTargetWindow = NULL;\n    req.DockTargetNode = node;\n    req.DockPayload = NULL;\n    req.DockSplitDir = split_dir;\n    req.DockSplitRatio = ImSaturate((split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? size_ratio_for_node_at_dir : 1.0f - size_ratio_for_node_at_dir);\n    req.DockSplitOuter = false;\n    DockContextProcessDock(&g, &req);\n\n    ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID;\n    ImGuiID id_at_opposite_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID;\n    if (out_id_at_dir)\n        *out_id_at_dir = id_at_dir;\n    if (out_id_at_opposite_dir)\n        *out_id_at_opposite_dir = id_at_opposite_dir;\n    return id_at_dir;\n}\n\nstatic ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector<ImGuiID>* out_node_remap_pairs)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiDockNode* dst_node = ImGui::DockContextAddNode(&g, dst_node_id_if_known);\n    dst_node->SharedFlags = src_node->SharedFlags;\n    dst_node->LocalFlags = src_node->LocalFlags;\n    dst_node->LocalFlagsInWindows = ImGuiDockNodeFlags_None;\n    dst_node->Pos = src_node->Pos;\n    dst_node->Size = src_node->Size;\n    dst_node->SizeRef = src_node->SizeRef;\n    dst_node->SplitAxis = src_node->SplitAxis;\n    dst_node->UpdateMergedFlags();\n\n    out_node_remap_pairs->push_back(src_node->ID);\n    out_node_remap_pairs->push_back(dst_node->ID);\n\n    for (int child_n = 0; child_n < IM_COUNTOF(src_node->ChildNodes); child_n++)\n        if (src_node->ChildNodes[child_n])\n        {\n            dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node->ChildNodes[child_n], 0, out_node_remap_pairs);\n            dst_node->ChildNodes[child_n]->ParentNode = dst_node;\n        }\n\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] Fork node %08X -> %08X (%d childs)\\n\", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0);\n    return dst_node;\n}\n\nvoid ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector<ImGuiID>* out_node_remap_pairs)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(src_node_id != 0);\n    IM_ASSERT(dst_node_id != 0);\n    IM_ASSERT(out_node_remap_pairs != NULL);\n\n    DockBuilderRemoveNode(dst_node_id);\n\n    ImGuiDockNode* src_node = DockContextFindNodeByID(&g, src_node_id);\n    IM_ASSERT(src_node != NULL);\n\n    out_node_remap_pairs->clear();\n    DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs);\n\n    IM_ASSERT((out_node_remap_pairs->Size % 2) == 0);\n}\n\nvoid ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name)\n{\n    ImGuiWindow* src_window = FindWindowByName(src_name);\n    if (src_window == NULL)\n        return;\n    if (ImGuiWindow* dst_window = FindWindowByName(dst_name))\n    {\n        dst_window->Pos = src_window->Pos;\n        dst_window->Size = src_window->Size;\n        dst_window->SizeFull = src_window->SizeFull;\n        dst_window->Collapsed = src_window->Collapsed;\n    }\n    else\n    {\n        ImGuiWindowSettings* dst_settings = FindWindowSettingsByID(ImHashStr(dst_name));\n        if (!dst_settings)\n            dst_settings = CreateNewWindowSettings(dst_name);\n        ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos);\n        if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID)\n        {\n            dst_settings->ViewportPos = window_pos_2ih;\n            dst_settings->ViewportId = src_window->ViewportId;\n            dst_settings->Pos = ImVec2ih(0, 0);\n        }\n        else\n        {\n            dst_settings->Pos = window_pos_2ih;\n        }\n        dst_settings->Size = ImVec2ih(src_window->SizeFull);\n        dst_settings->Collapsed = src_window->Collapsed;\n    }\n}\n\n// FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed.\nvoid ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector<const char*>* in_window_remap_pairs)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(src_dockspace_id != 0);\n    IM_ASSERT(dst_dockspace_id != 0);\n    IM_ASSERT(in_window_remap_pairs != NULL);\n    IM_ASSERT((in_window_remap_pairs->Size % 2) == 0);\n\n    // Duplicate entire dock\n    // FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace window class but that are docked in a same node will be split apart,\n    // whereas we could attempt to at least keep them together in a new, same floating node.\n    ImVector<ImGuiID> node_remap_pairs;\n    DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs);\n\n    // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes\n    // (The windows associated to src_dockspace_id are staying in place)\n    ImVector<ImGuiID> src_windows;\n    for (int remap_window_n = 0; remap_window_n < in_window_remap_pairs->Size; remap_window_n += 2)\n    {\n        const char* src_window_name = (*in_window_remap_pairs)[remap_window_n];\n        const char* dst_window_name = (*in_window_remap_pairs)[remap_window_n + 1];\n        ImGuiID src_window_id = ImHashStr(src_window_name);\n        src_windows.push_back(src_window_id);\n\n        // Search in the remapping tables\n        ImGuiID src_dock_id = 0;\n        if (ImGuiWindow* src_window = FindWindowByID(src_window_id))\n            src_dock_id = src_window->DockId;\n        else if (ImGuiWindowSettings* src_window_settings = FindWindowSettingsByID(src_window_id))\n            src_dock_id = src_window_settings->DockId;\n        ImGuiID dst_dock_id = 0;\n        for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)\n            if (node_remap_pairs[dock_remap_n] == src_dock_id)\n            {\n                dst_dock_id = node_remap_pairs[dock_remap_n + 1];\n                //node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0; // Clear\n                break;\n            }\n\n        if (dst_dock_id != 0)\n        {\n            // Docked windows gets redocked into the new node hierarchy.\n            IMGUI_DEBUG_LOG_DOCKING(\"[docking] Remap live window '%s' 0x%08X -> '%s' 0x%08X\\n\", src_window_name, src_dock_id, dst_window_name, dst_dock_id);\n            DockBuilderDockWindow(dst_window_name, dst_dock_id);\n        }\n        else\n        {\n            // Floating windows gets their settings transferred (regardless of whether the new window already exist or not)\n            // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together?\n            IMGUI_DEBUG_LOG_DOCKING(\"[docking] Remap window settings '%s' -> '%s'\\n\", src_window_name, dst_window_name);\n            DockBuilderCopyWindowSettings(src_window_name, dst_window_name);\n        }\n    }\n\n    // Anything else in the source nodes of 'node_remap_pairs' are windows that are not included in the remapping list.\n    // Find those windows and move to them to the cloned dock node. This may be optional?\n    // Dock those are a second step as undocking would invalidate source dock nodes.\n    struct DockRemainingWindowTask { ImGuiWindow* Window; ImGuiID DockId; DockRemainingWindowTask(ImGuiWindow* window, ImGuiID dock_id) { Window = window; DockId = dock_id; } };\n    ImVector<DockRemainingWindowTask> dock_remaining_windows;\n    for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)\n        if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n])\n        {\n            ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1];\n            ImGuiDockNode* node = DockBuilderGetNode(src_dock_id);\n            for (int window_n = 0; window_n < node->Windows.Size; window_n++)\n            {\n                ImGuiWindow* window = node->Windows[window_n];\n                if (src_windows.contains(window->ID))\n                    continue;\n\n                // Docked windows gets redocked into the new node hierarchy.\n                IMGUI_DEBUG_LOG_DOCKING(\"[docking] Remap window '%s' %08X -> %08X\\n\", window->Name, src_dock_id, dst_dock_id);\n                dock_remaining_windows.push_back(DockRemainingWindowTask(window, dst_dock_id));\n            }\n        }\n    for (const DockRemainingWindowTask& task : dock_remaining_windows)\n        DockBuilderDockWindow(task.Window->Name, task.DockId);\n}\n\n// FIXME-DOCK: This is awkward because in series of split user is likely to loose access to its root node.\nvoid ImGui::DockBuilderFinish(ImGuiID root_id)\n{\n    ImGuiContext& g = *GImGui;\n    //DockContextRebuild(&g);\n    DockContextBuildAddWindowsToNodes(&g, root_id);\n}\n\n//-----------------------------------------------------------------------------\n// Docking: Begin/End Support Functions (called from Begin/End)\n//-----------------------------------------------------------------------------\n// - GetWindowAlwaysWantOwnTabBar()\n// - DockContextBindNodeToWindow()\n// - BeginDocked()\n// - BeginDockableDragDropSource()\n// - BeginDockableDragDropTarget()\n//-----------------------------------------------------------------------------\n\nbool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar)\n        if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0)\n            if (!window->IsFallbackWindow)    // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise\n                return true;\n    return false;\n}\n\nstatic ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window)\n{\n    ImGuiContext& g = *ctx;\n    ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);\n    IM_ASSERT(window->DockNode == NULL);\n\n    // We should not be docking into a split node (SetWindowDock should avoid this)\n    if (node && node->IsSplitNode())\n    {\n        DockContextProcessUndockWindow(ctx, window);\n        return NULL;\n    }\n\n    // Create node\n    if (node == NULL)\n    {\n        node = DockContextAddNode(ctx, window->DockId);\n        node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;\n        node->LastFrameAlive = g.FrameCount;\n    }\n\n    // If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet,\n    // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node).\n    // If we don't do this, the window will be assigned a zero-size on its first frame, which won't ideally warm up the layout.\n    // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame.\n    if (!node->IsVisible)\n    {\n        ImGuiDockNode* ancestor_node = node;\n        while (!ancestor_node->IsVisible && ancestor_node->ParentNode)\n            ancestor_node = ancestor_node->ParentNode;\n        IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f);\n        DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node));\n        DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, node);\n    }\n\n    // Add window to node\n    bool node_was_visible = node->IsVisible;\n    DockNodeAddWindow(node, window, true);\n    node->IsVisible = node_was_visible; // Don't mark visible right away (so DockContextEndFrame() doesn't render it, maybe other side effects? will see)\n    IM_ASSERT(node == window->DockNode);\n    return node;\n}\n\nstatic void StoreDockStyleForWindow(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)\n        window->DockStyle.Colors[color_n] = ImGui::ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]);\n}\n\nvoid ImGui::BeginDocked(ImGuiWindow* window, bool* p_open)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Specific extra processing for fallback window (#9151), could be in Begin() as well.\n    if (window->IsFallbackWindow && !window->WasActive)\n    {\n        DockNodeHideWindowDuringHostWindowCreation(window);\n        return;\n    }\n\n    const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window);\n    if (auto_dock_node)\n    {\n        if (window->DockId == 0)\n        {\n            IM_ASSERT(window->DockNode == NULL);\n            window->DockId = DockContextGenNodeID(&g);\n        }\n    }\n    else\n    {\n        // Calling SetNextWindowPos() undock windows by default (by setting PosUndock)\n        bool want_undock = false;\n        want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0;\n        want_undock |= (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock;\n        if (want_undock)\n        {\n            DockContextProcessUndockWindow(&g, window);\n            return;\n        }\n    }\n\n    // Bind to our dock node\n    ImGuiDockNode* node = window->DockNode;\n    if (node != NULL)\n        IM_ASSERT(window->DockId == node->ID);\n    if (window->DockId != 0 && node == NULL)\n    {\n        node = DockContextBindNodeToWindow(&g, window);\n        if (node == NULL)\n            return;\n    }\n\n#if 0\n    // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set\n    if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode))\n    {\n        DockContextProcessUndockWindow(ctx, window);\n        return;\n    }\n#endif\n\n    // Undock if our dockspace node disappeared\n    // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly.\n    if (node->LastFrameAlive < g.FrameCount)\n    {\n        // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking()\n        ImGuiDockNode* root_node = DockNodeGetRootNode(node);\n        if (root_node->LastFrameAlive < g.FrameCount)\n            DockContextProcessUndockWindow(&g, window);\n        else\n            window->DockIsActive = true;\n        return;\n    }\n\n    // Store style overrides\n    StoreDockStyleForWindow(window);\n\n    // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window,\n    // and never create neither a host window neither a tab bar.\n    // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test)\n    if (node->HostWindow == NULL)\n    {\n        if (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing)\n            window->DockIsActive = true;\n        if (node->Windows.Size > 1 && window->Appearing) // Only hide appearing window\n            DockNodeHideWindowDuringHostWindowCreation(window);\n        return;\n    }\n\n    // We can have zero-sized nodes (e.g. children of a small-size dockspace)\n    IM_ASSERT(node->HostWindow);\n    IM_ASSERT(node->IsLeafNode());\n    IM_ASSERT(node->Size.x >= 0.0f && node->Size.y >= 0.0f);\n    node->State = ImGuiDockNodeState_HostWindowVisible;\n\n    // Undock if we are submitted earlier than the host window\n    if (!(node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) && window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext)\n    {\n        DockContextProcessUndockWindow(&g, window);\n        return;\n    }\n\n    // Position/Size window\n    SetNextWindowPos(node->Pos);\n    SetNextWindowSize(node->Size);\n    g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos()\n    window->DockIsActive = true;\n    window->DockNodeIsVisible = true;\n    window->DockTabIsVisible = false;\n    if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)\n        return;\n\n    // When the window is selected we mark it as visible.\n    if (node->VisibleWindow == window)\n        window->DockTabIsVisible = true;\n\n    // Update window flag\n    IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0);\n    window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize;\n    window->ChildFlags |= ImGuiChildFlags_AlwaysUseWindowPadding;\n    if (node->IsHiddenTabBar() || node->IsNoTabBar())\n        window->Flags |= ImGuiWindowFlags_NoTitleBar;\n    else\n        window->Flags &= ~ImGuiWindowFlags_NoTitleBar;      // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed!\n\n    // Save new dock order only if the window has been visible once already\n    // This allows multiple windows to be created in the same frame and have their respective dock orders preserved.\n    if (node->TabBar && window->WasActive)\n        window->DockOrder = (short)DockNodeGetTabOrder(window);\n\n    if ((node->WantCloseAll || node->WantCloseTabId == window->TabId) && p_open != NULL)\n        *p_open = false;\n\n    // Update ChildId to allow returning from Child to Parent with Escape\n    ImGuiWindow* parent_window = window->DockNode->HostWindow;\n    window->ChildId = parent_window->GetID(window->Name);\n}\n\nvoid ImGui::BeginDockableDragDropSource(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.ActiveId == window->MoveId);\n    IM_ASSERT(g.MovingWindow == window);\n    IM_ASSERT(g.CurrentWindow == window);\n\n    // 0: Hold SHIFT to disable docking, 1: Hold SHIFT to enable docking.\n    if (g.IO.ConfigDockingWithShift != g.IO.KeyShift)\n    {\n        // When ConfigDockingWithShift is set, display a tooltip to increase UI affordance.\n        // We cannot set for HoveredWindowUnderMovingWindow != NULL here, as it is only valid/useful when drag and drop is already active\n        // (because of the 'is_mouse_dragging_with_an_expected_destination' logic in UpdateViewportsNewFrame() function)\n        IM_ASSERT(g.NextWindowData.HasFlags == 0);\n        if (g.IO.ConfigDockingWithShift && g.MouseStationaryTimer >= 1.0f && g.ActiveId >= 1.0f)\n            SetTooltip(\"%s\", LocalizeGetMsg(ImGuiLocKey_DockingHoldShiftToDock));\n        return;\n    }\n\n    g.LastItemData.ID = window->MoveId;\n    window = window->RootWindowDockTree;\n    IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);\n    bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit\n    ImGuiDragDropFlags drag_drop_flags = ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_PayloadAutoExpire | ImGuiDragDropFlags_PayloadNoCrossContext | ImGuiDragDropFlags_PayloadNoCrossProcess;\n    if (is_drag_docking && BeginDragDropSource(drag_drop_flags))\n    {\n        SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window));\n        EndDragDropSource();\n        StoreDockStyleForWindow(window); // Store style overrides while dragging (even when not docked) because docking preview may need it.\n    }\n}\n\nvoid ImGui::BeginDockableDragDropTarget(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n\n    //IM_ASSERT(window->RootWindowDockTree == window); // May also be a DockSpace\n    IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);\n    if (!g.DragDropActive)\n        return;\n    //GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));\n    if (!BeginDragDropTargetCustom(window->Rect(), window->ID))\n        return;\n\n    // Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering\n    // (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly)\n    const ImGuiPayload* payload = &g.DragDropPayload;\n    if (!payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) || !DockNodeIsDropAllowed(window, *(ImGuiWindow**)payload->Data))\n    {\n        EndDragDropTarget();\n        return;\n    }\n\n    ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data;\n    if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect))\n    {\n        // Select target node\n        // (Important: we cannot use g.HoveredDockNode here! Because each of our target node have filters based on payload, each candidate drop target will do its own evaluation)\n        bool dock_into_floating_window = false;\n        ImGuiDockNode* node = NULL;\n        if (window->DockNodeAsHost)\n        {\n            // Cannot assume that node will != NULL even though we passed the rectangle test: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos().\n            node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos);\n\n            // There is an edge case when docking into a dockspace which only has _inactive_ nodes (because none of the windows are active)\n            // In this case we need to fallback into any leaf mode, possibly the central node.\n            // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first.\n            if (node && node->IsDockSpace() && node->IsRootNode())\n                node = (node->CentralNode && node->IsLeafNode()) ? node->CentralNode : DockNodeTreeFindFallbackLeafNode(node);\n        }\n        else\n        {\n            if (window->DockNode)\n                node = window->DockNode;\n            else\n                dock_into_floating_window = true; // Dock into a regular window\n        }\n\n        const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight()));\n        const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max);\n\n        // Preview docking request and find out split direction/ratio\n        //const bool do_preview = true;     // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window.\n        const bool do_preview = payload->IsPreview() || payload->IsDelivery();\n        if (do_preview && (node != NULL || dock_into_floating_window))\n        {\n            // If we have a non-leaf node it means we are hovering the border of a parent node, in which case only outer markers will appear.\n            ImGuiDockPreviewData split_inner;\n            ImGuiDockPreviewData split_outer;\n            ImGuiDockPreviewData* split_data = &split_inner;\n            if (node && (node->ParentNode || node->IsCentralNode() || !node->IsLeafNode()))\n                if (ImGuiDockNode* root_node = DockNodeGetRootNode(node))\n                {\n                    DockNodePreviewDockSetup(window, root_node, payload_window, NULL, &split_outer, is_explicit_target, true);\n                    if (split_outer.IsSplitDirExplicit)\n                        split_data = &split_outer;\n                }\n            if (!node || node->IsLeafNode())\n                DockNodePreviewDockSetup(window, node, payload_window, NULL, &split_inner, is_explicit_target, false);\n            if (split_data == &split_outer)\n                split_inner.IsDropAllowed = false;\n\n            // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes\n            DockNodePreviewDockRender(window, node, payload_window, &split_inner);\n            DockNodePreviewDockRender(window, node, payload_window, &split_outer);\n\n            // Queue docking request\n            if (split_data->IsDropAllowed && payload->IsDelivery())\n                DockContextQueueDock(&g, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer);\n        }\n    }\n    EndDragDropTarget();\n}\n\n//-----------------------------------------------------------------------------\n// Docking: Settings\n//-----------------------------------------------------------------------------\n// - DockSettingsRenameNodeReferences()\n// - DockSettingsRemoveNodeReferences()\n// - DockSettingsFindNodeSettings()\n// - DockSettingsHandler_ApplyAll()\n// - DockSettingsHandler_ReadOpen()\n// - DockSettingsHandler_ReadLine()\n// - DockSettingsHandler_DockNodeToSettings()\n// - DockSettingsHandler_WriteAll()\n//-----------------------------------------------------------------------------\n\nstatic void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id)\n{\n    ImGuiContext& g = *GImGui;\n    IMGUI_DEBUG_LOG_DOCKING(\"[docking] DockSettingsRenameNodeReferences: from 0x%08X -> to 0x%08X\\n\", old_node_id, new_node_id);\n    for (int window_n = 0; window_n < g.Windows.Size; window_n++)\n    {\n        ImGuiWindow* window = g.Windows[window_n];\n        if (window->DockId == old_node_id && window->DockNode == NULL)\n            window->DockId = new_node_id;\n    }\n    //// FIXME-OPT: We could remove this loop by storing the index in the map\n    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n        if (settings->DockId == old_node_id)\n            settings->DockId = new_node_id;\n}\n\n// Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings\nstatic void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count)\n{\n    ImGuiContext& g = *GImGui;\n    int found = 0;\n    //// FIXME-OPT: We could remove this loop by storing the index in the map\n    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n        for (int node_n = 0; node_n < node_ids_count; node_n++)\n            if (settings->DockId == node_ids[node_n])\n            {\n                settings->DockId = 0;\n                settings->DockOrder = -1;\n                if (++found < node_ids_count)\n                    break;\n                return;\n            }\n}\n\nstatic ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id)\n{\n    // FIXME-OPT\n    ImGuiDockContext* dc = &ctx->DockContext;\n    for (int n = 0; n < dc->NodesSettings.Size; n++)\n        if (dc->NodesSettings[n].ID == id)\n            return &dc->NodesSettings[n];\n    return NULL;\n}\n\n// Clear settings data\nstatic void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)\n{\n    ImGuiDockContext* dc = &ctx->DockContext;\n    dc->NodesSettings.clear();\n    DockContextClearNodes(ctx, 0, true);\n}\n\n// Recreate nodes based on settings data\nstatic void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)\n{\n    // Prune settings at boot time only\n    ImGuiDockContext* dc = &ctx->DockContext;\n    if (ctx->Windows.Size == 0)\n        DockContextPruneUnusedSettingsNodes(ctx);\n    DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);\n    DockContextBuildAddWindowsToNodes(ctx, 0);\n}\n\nstatic void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)\n{\n    if (strcmp(name, \"Data\") != 0)\n        return NULL;\n    return (void*)1;\n}\n\nstatic void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void*, const char* line)\n{\n    char c = 0;\n    int x = 0, y = 0;\n    int r = 0;\n\n    // Parsing, e.g.\n    // \" DockNode   ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 \"\n    // \"   DockNode ID=0x00000002 Parent=0x00000001 \"\n    // Important: this code expect currently fields in a fixed order.\n    ImGuiDockNodeSettings node;\n    line = ImStrSkipBlank(line);\n    if      (strncmp(line, \"DockNode\", 8) == 0)  { line = ImStrSkipBlank(line + strlen(\"DockNode\")); }\n    else if (strncmp(line, \"DockSpace\", 9) == 0) { line = ImStrSkipBlank(line + strlen(\"DockSpace\")); node.Flags |= ImGuiDockNodeFlags_DockSpace; }\n    else return;\n    if (sscanf(line, \"ID=0x%08X%n\",      &node.ID, &r) == 1)            { line += r; } else return;\n    if (sscanf(line, \" Parent=0x%08X%n\", &node.ParentNodeId, &r) == 1)  { line += r; if (node.ParentNodeId == 0) return; }\n    if (sscanf(line, \" Window=0x%08X%n\", &node.ParentWindowId, &r) ==1) { line += r; if (node.ParentWindowId == 0) return; }\n    if (node.ParentNodeId == 0)\n    {\n        if (sscanf(line, \" Pos=%i,%i%n\",  &x, &y, &r) == 2)         { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return;\n        if (sscanf(line, \" Size=%i,%i%n\", &x, &y, &r) == 2)         { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return;\n    }\n    else\n    {\n        if (sscanf(line, \" SizeRef=%i,%i%n\", &x, &y, &r) == 2)      { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); }\n    }\n    if (sscanf(line, \" Split=%c%n\", &c, &r) == 1)                   { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; }\n    if (sscanf(line, \" NoResize=%d%n\", &x, &r) == 1)                { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; }\n    if (sscanf(line, \" CentralNode=%d%n\", &x, &r) == 1)             { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; }\n    if (sscanf(line, \" NoTabBar=%d%n\", &x, &r) == 1)                { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; }\n    if (sscanf(line, \" HiddenTabBar=%d%n\", &x, &r) == 1)            { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; }\n    if (sscanf(line, \" NoWindowMenuButton=%d%n\", &x, &r) == 1)      { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; }\n    if (sscanf(line, \" NoCloseButton=%d%n\", &x, &r) == 1)           { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; }\n    if (sscanf(line, \" Selected=0x%08X%n\", &node.SelectedTabId,&r) == 1) { line += r; }\n    if (node.ParentNodeId != 0)\n        if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId))\n            node.Depth = parent_settings->Depth + 1;\n    ctx->DockContext.NodesSettings.push_back(node);\n}\n\nstatic void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth)\n{\n    ImGuiDockNodeSettings node_settings;\n    IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3)));\n    node_settings.ID = node->ID;\n    node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0;\n    node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0;\n    node_settings.SelectedTabId = node->SelectedTabId;\n    node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None);\n    node_settings.Depth = (char)depth;\n    node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_);\n    node_settings.Pos = ImVec2ih(node->Pos);\n    node_settings.Size = ImVec2ih(node->Size);\n    node_settings.SizeRef = ImVec2ih(node->SizeRef);\n    dc->NodesSettings.push_back(node_settings);\n    if (node->ChildNodes[0])\n        DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1);\n    if (node->ChildNodes[1])\n        DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1);\n}\n\nstatic void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)\n{\n    ImGuiContext& g = *ctx;\n    ImGuiDockContext* dc = &ctx->DockContext;\n    if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))\n        return;\n\n    // Gather settings data\n    // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer)\n    dc->NodesSettings.resize(0);\n    dc->NodesSettings.reserve(dc->Nodes.Data.Size);\n    for (int n = 0; n < dc->Nodes.Data.Size; n++)\n        if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)\n            if (node->IsRootNode())\n                DockSettingsHandler_DockNodeToSettings(dc, node, 0);\n\n    int max_depth = 0;\n    for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)\n        max_depth = ImMax((int)dc->NodesSettings[node_n].Depth, max_depth);\n\n    // Write to text buffer\n    buf->appendf(\"[%s][Data]\\n\", handler->TypeName);\n    for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)\n    {\n        const int line_start_pos = buf->size(); (void)line_start_pos;\n        const ImGuiDockNodeSettings* node_settings = &dc->NodesSettings[node_n];\n        buf->appendf(\"%*s%s%*s\", node_settings->Depth * 2, \"\", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? \"DockSpace\" : \"DockNode \", (max_depth - node_settings->Depth) * 2, \"\");  // Text align nodes to facilitate looking at .ini file\n        buf->appendf(\" ID=0x%08X\", node_settings->ID);\n        if (node_settings->ParentNodeId)\n        {\n            buf->appendf(\" Parent=0x%08X SizeRef=%d,%d\", node_settings->ParentNodeId, node_settings->SizeRef.x, node_settings->SizeRef.y);\n        }\n        else\n        {\n            if (node_settings->ParentWindowId)\n                buf->appendf(\" Window=0x%08X\", node_settings->ParentWindowId);\n            buf->appendf(\" Pos=%d,%d Size=%d,%d\", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y);\n        }\n        if (node_settings->SplitAxis != ImGuiAxis_None)\n            buf->appendf(\" Split=%c\", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y');\n        if (node_settings->Flags & ImGuiDockNodeFlags_NoResize)\n            buf->appendf(\" NoResize=1\");\n        if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode)\n            buf->appendf(\" CentralNode=1\");\n        if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar)\n            buf->appendf(\" NoTabBar=1\");\n        if (node_settings->Flags & ImGuiDockNodeFlags_HiddenTabBar)\n            buf->appendf(\" HiddenTabBar=1\");\n        if (node_settings->Flags & ImGuiDockNodeFlags_NoWindowMenuButton)\n            buf->appendf(\" NoWindowMenuButton=1\");\n        if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton)\n            buf->appendf(\" NoCloseButton=1\");\n        if (node_settings->SelectedTabId)\n            buf->appendf(\" Selected=0x%08X\", node_settings->SelectedTabId);\n\n        // [DEBUG] Include comments in the .ini file to ease debugging (this makes saving slower!)\n        if (g.IO.ConfigDebugIniSettings)\n            if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID))\n            {\n                buf->appendf(\"%*s\", ImMax(2, (line_start_pos + 92) - buf->size()), \"\");     // Align everything\n                if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow)\n                    buf->appendf(\" ; in '%s'\", node->HostWindow->ParentWindow->Name);\n                // Iterate settings so we can give info about windows that didn't exist during the session.\n                int contains_window = 0;\n                for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n                    if (settings->DockId == node_settings->ID)\n                    {\n                        if (contains_window++ == 0)\n                            buf->appendf(\" ; contains \");\n                        buf->appendf(\"'%s' \", settings->GetName());\n                    }\n            }\n\n        buf->appendf(\"\\n\");\n    }\n    buf->appendf(\"\\n\");\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] PLATFORM DEPENDENT HELPERS\n//-----------------------------------------------------------------------------\n// - Default clipboard handlers\n// - Default shell function handlers\n// - Default IME handlers\n//-----------------------------------------------------------------------------\n\n#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)\n\n#ifdef _MSC_VER\n#pragma comment(lib, \"user32\")\n#pragma comment(lib, \"kernel32\")\n#endif\n\n// Win32 clipboard implementation\n// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()\nstatic const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)\n{\n    ImGuiContext& g = *ctx;\n    g.ClipboardHandlerData.clear();\n    if (!::OpenClipboard(NULL))\n        return NULL;\n    HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);\n    if (wbuf_handle == NULL)\n    {\n        ::CloseClipboard();\n        return NULL;\n    }\n    if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))\n    {\n        int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);\n        g.ClipboardHandlerData.resize(buf_len);\n        ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);\n    }\n    ::GlobalUnlock(wbuf_handle);\n    ::CloseClipboard();\n    return g.ClipboardHandlerData.Data;\n}\n\nstatic void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)\n{\n    if (!::OpenClipboard(NULL))\n        return;\n    const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);\n    HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));\n    if (wbuf_handle == NULL)\n    {\n        ::CloseClipboard();\n        return;\n    }\n    WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);\n    ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);\n    ::GlobalUnlock(wbuf_handle);\n    ::EmptyClipboard();\n    if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)\n        ::GlobalFree(wbuf_handle);\n    ::CloseClipboard();\n}\n\n#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)\n\n#include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file\nstatic PasteboardRef main_clipboard = 0;\n\n// OSX clipboard implementation\n// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!\nstatic void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)\n{\n    if (!main_clipboard)\n        PasteboardCreate(kPasteboardClipboard, &main_clipboard);\n    PasteboardClear(main_clipboard);\n    CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, ImStrlen(text));\n    if (cf_data)\n    {\n        PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR(\"public.utf8-plain-text\"), cf_data, 0);\n        CFRelease(cf_data);\n    }\n}\n\nstatic const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)\n{\n    ImGuiContext& g = *ctx;\n    if (!main_clipboard)\n        PasteboardCreate(kPasteboardClipboard, &main_clipboard);\n    PasteboardSynchronize(main_clipboard);\n\n    ItemCount item_count = 0;\n    PasteboardGetItemCount(main_clipboard, &item_count);\n    for (ItemCount i = 0; i < item_count; i++)\n    {\n        PasteboardItemID item_id = 0;\n        PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);\n        CFArrayRef flavor_type_array = 0;\n        PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);\n        for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)\n        {\n            CFDataRef cf_data;\n            if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR(\"public.utf8-plain-text\"), &cf_data) == noErr)\n            {\n                g.ClipboardHandlerData.clear();\n                int length = (int)CFDataGetLength(cf_data);\n                g.ClipboardHandlerData.resize(length + 1);\n                CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);\n                g.ClipboardHandlerData[length] = 0;\n                CFRelease(cf_data);\n                return g.ClipboardHandlerData.Data;\n            }\n        }\n    }\n    return NULL;\n}\n\n#else\n\n// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.\nstatic const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)\n{\n    ImGuiContext& g = *ctx;\n    return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();\n}\n\nstatic void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text)\n{\n    ImGuiContext& g = *ctx;\n    g.ClipboardHandlerData.clear();\n    const char* text_end = text + ImStrlen(text);\n    g.ClipboardHandlerData.resize((int)(text_end - text) + 1);\n    memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));\n    g.ClipboardHandlerData[(int)(text_end - text)] = 0;\n}\n\n#endif // Default clipboard handlers\n\n//-----------------------------------------------------------------------------\n\n#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS\n#if defined(__APPLE__) && TARGET_OS_IPHONE\n#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS\n#endif\n#if defined(__3DS__)\n#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS\n#endif\n#if defined(_WIN32) && defined(IMGUI_DISABLE_WIN32_FUNCTIONS)\n#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS\n#endif\n#endif // #ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS\n\n#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS\n#ifdef _WIN32\n#include <shellapi.h>   // ShellExecuteA()\n#ifdef _MSC_VER\n#pragma comment(lib, \"shell32\")\n#endif\nstatic bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)\n{\n    const int path_wsize = ::MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);\n    ImVector<wchar_t> path_wbuf;\n    path_wbuf.resize(path_wsize);\n    ::MultiByteToWideChar(CP_UTF8, 0, path, -1, path_wbuf.Data, path_wsize);\n    return (INT_PTR)::ShellExecuteW(NULL, L\"open\", path_wbuf.Data, NULL, NULL, SW_SHOWDEFAULT) > 32;\n}\n#else\n#include <sys/wait.h>\n#include <unistd.h>\nstatic bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)\n{\n#if defined(__APPLE__)\n    const char* args[] { \"open\", \"--\", path, NULL };\n#else\n    const char* args[] { \"xdg-open\", path, NULL };\n#endif\n    pid_t pid = fork();\n    if (pid < 0)\n        return false;\n    if (!pid)\n    {\n        execvp(args[0], const_cast<char **>(args));\n        exit(-1);\n    }\n    else\n    {\n        int status;\n        waitpid(pid, &status, 0);\n        return WEXITSTATUS(status) == 0;\n    }\n}\n#endif\n#else\nstatic bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; }\n#endif // Default shell handlers\n\n//-----------------------------------------------------------------------------\n\n// Win32 API IME support (for Asian languages, etc.)\n#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)\n\n#include <imm.h>\n#ifdef _MSC_VER\n#pragma comment(lib, \"imm32\")\n#endif\n\nstatic void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)\n{\n    // Notify OS Input Method Editor of text input position\n    HWND hwnd = (HWND)viewport->PlatformHandleRaw;\n    if (hwnd == 0)\n        return;\n\n    //::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0);\n    if (HIMC himc = ::ImmGetContext(hwnd))\n    {\n        COMPOSITIONFORM composition_form = {};\n        composition_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x);\n        composition_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y);\n        composition_form.dwStyle = CFS_FORCE_POSITION;\n        ::ImmSetCompositionWindow(himc, &composition_form);\n        CANDIDATEFORM candidate_form = {};\n        candidate_form.dwStyle = CFS_CANDIDATEPOS;\n        candidate_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x);\n        candidate_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y);\n        ::ImmSetCandidateWindow(himc, &candidate_form);\n        ::ImmReleaseContext(hwnd, himc);\n    }\n}\n\n#else\n\nstatic void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData*) {}\n\n#endif // Default IME handlers\n\n//-----------------------------------------------------------------------------\n// [SECTION] METRICS/DEBUGGER WINDOW\n//-----------------------------------------------------------------------------\n// - MetricsHelpMarker() [Internal]\n// - DebugRenderViewportThumbnail() [Internal]\n// - RenderViewportsThumbnails() [Internal]\n// - DebugRenderKeyboardPreview() [Internal]\n// - DebugTextEncoding()\n// - DebugFlashStyleColorStop() [Internal]\n// - DebugFlashStyleColor()\n// - UpdateDebugToolFlashStyleColor() [Internal]\n// - ShowFontAtlas() [Internal but called by Demo!]\n// - DebugNodeTexture() [Internal]\n// - ShowMetricsWindow()\n// - DebugNodeColumns() [Internal]\n// - DebugNodeDockNode() [Internal]\n// - DebugNodeDrawList() [Internal]\n// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]\n// - DebugNodeFont() [Internal]\n// - DebugNodeFontGlyph() [Internal]\n// - DebugNodeStorage() [Internal]\n// - DebugNodeTabBar() [Internal]\n// - DebugNodeViewport() [Internal]\n// - DebugNodeWindow() [Internal]\n// - DebugNodeWindowSettings() [Internal]\n// - DebugNodeWindowsList() [Internal]\n// - DebugNodeWindowsListByBeginStackParent() [Internal]\n// - ShowFontSelector()\n//-----------------------------------------------------------------------------\n\n#if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)\n// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.\nstatic void MetricsHelpMarker(const char* desc)\n{\n    ImGui::TextDisabled(\"(?)\");\n    if (ImGui::BeginItemTooltip())\n    {\n        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);\n        ImGui::TextUnformatted(desc);\n        ImGui::PopTextWrapPos();\n        ImGui::EndTooltip();\n    }\n}\n#endif\n\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n\nvoid ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    ImVec2 scale = bb.GetSize() / viewport->Size;\n    ImVec2 off = bb.Min - viewport->Pos * scale;\n    float alpha_mul = (viewport->Flags & ImGuiViewportFlags_IsMinimized) ? 0.30f : 1.00f;\n    window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));\n    for (ImGuiWindow* thumb_window : g.Windows)\n    {\n        if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))\n            continue;\n        if (thumb_window->Viewport != viewport)\n            continue;\n\n        ImRect thumb_r = thumb_window->Rect();\n        ImRect title_r = thumb_window->TitleBarRect();\n        thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off +  thumb_r.Max * scale));\n        title_r = ImRect(ImTrunc(off + title_r.Min * scale), ImTrunc(off +  ImVec2(title_r.Max.x, title_r.Min.y + title_r.GetHeight() * 3.0f) * scale)); // Exaggerate title bar height\n        thumb_r.ClipWithFull(bb);\n        title_r.ClipWithFull(bb);\n        const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);\n        window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));\n        window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));\n        window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));\n        window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));\n    }\n    draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));\n    if (viewport->ID == g.DebugMetricsConfig.HighlightViewportID)\n        window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));\n}\n\nstatic void RenderViewportsThumbnails()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Draw monitor and calculate their boundaries\n    float SCALE = 1.0f / 8.0f;\n    ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);\n    for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)\n        bb_full.Add(ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize));\n    ImVec2 p = window->DC.CursorPos;\n    ImVec2 off = p - bb_full.Min * SCALE;\n    for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)\n    {\n        ImRect monitor_draw_bb(off + (monitor.MainPos) * SCALE, off + (monitor.MainPos + monitor.MainSize) * SCALE);\n        window->DrawList->AddRect(monitor_draw_bb.Min, monitor_draw_bb.Max, (g.DebugMetricsConfig.HighlightMonitorIdx == g.PlatformIO.Monitors.index_from_ptr(&monitor)) ? IM_COL32(255, 255, 0, 255) : ImGui::GetColorU32(ImGuiCol_Border), 4.0f);\n        window->DrawList->AddRectFilled(monitor_draw_bb.Min, monitor_draw_bb.Max, ImGui::GetColorU32(ImGuiCol_Border, 0.10f), 4.0f);\n    }\n\n    // Draw viewports\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);\n        ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);\n    }\n    ImGui::Dummy(bb_full.GetSize() * SCALE);\n}\n\nstatic int IMGUI_CDECL ViewportComparerByLastFocusedStampCount(const void* lhs, const void* rhs)\n{\n    const ImGuiViewportP* a = *(const ImGuiViewportP* const*)lhs;\n    const ImGuiViewportP* b = *(const ImGuiViewportP* const*)rhs;\n    return b->LastFocusedStampCount - a->LastFocusedStampCount;\n}\n\n// Draw an arbitrary US keyboard layout to visualize translated keys\nvoid ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)\n{\n    const float scale = ImGui::GetFontSize() / 13.0f;\n    const ImVec2 key_size = ImVec2(35.0f, 35.0f) * scale;\n    const float  key_rounding = 3.0f * scale;\n    const ImVec2 key_face_size = ImVec2(25.0f, 25.0f) * scale;\n    const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f) * scale;\n    const float  key_face_rounding = 2.0f * scale;\n    const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f) * scale;\n    const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);\n    const float  key_row_offset = 9.0f * scale;\n\n    ImVec2 board_min = GetCursorScreenPos();\n    ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f);\n    ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y);\n\n    struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; };\n    const KeyLayoutData keys_to_display[] =\n    {\n        { 0, 0, \"\", ImGuiKey_Tab },      { 0, 1, \"Q\", ImGuiKey_Q }, { 0, 2, \"W\", ImGuiKey_W }, { 0, 3, \"E\", ImGuiKey_E }, { 0, 4, \"R\", ImGuiKey_R },\n        { 1, 0, \"\", ImGuiKey_CapsLock }, { 1, 1, \"A\", ImGuiKey_A }, { 1, 2, \"S\", ImGuiKey_S }, { 1, 3, \"D\", ImGuiKey_D }, { 1, 4, \"F\", ImGuiKey_F },\n        { 2, 0, \"\", ImGuiKey_LeftShift },{ 2, 1, \"Z\", ImGuiKey_Z }, { 2, 2, \"X\", ImGuiKey_X }, { 2, 3, \"C\", ImGuiKey_C }, { 2, 4, \"V\", ImGuiKey_V }\n    };\n\n    // Elements rendered manually via ImDrawList API are not clipped automatically.\n    // While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.\n    Dummy(board_max - board_min);\n    if (!IsItemVisible())\n        return;\n    draw_list->PushClipRect(board_min, board_max, true);\n    for (int n = 0; n < IM_COUNTOF(keys_to_display); n++)\n    {\n        const KeyLayoutData* key_data = &keys_to_display[n];\n        ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y);\n        ImVec2 key_max = key_min + key_size;\n        draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding);\n        draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);\n        ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);\n        ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);\n        draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);\n        draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);\n        ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);\n        draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);\n        if (IsKeyDown(key_data->Key))\n            draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);\n    }\n    draw_list->PopClipRect();\n}\n\n// Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct.\nvoid ImGui::DebugTextEncoding(const char* str)\n{\n    Text(\"Text: \\\"%s\\\"\", str);\n    if (!BeginTable(\"##DebugTextEncoding\", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable))\n        return;\n    TableSetupColumn(\"Offset\");\n    TableSetupColumn(\"UTF-8\");\n    TableSetupColumn(\"Glyph\");\n    TableSetupColumn(\"Codepoint\");\n    TableHeadersRow();\n    const char* str_end = str + strlen(str); // As we may receive malformed UTF-8, pass an explicit end instead of relying on ImTextCharFromUtf8() assuming enough space.\n    for (const char* p = str; *p != 0; )\n    {\n        unsigned int c;\n        const int c_utf8_len = ImTextCharFromUtf8(&c, p, str_end);\n        TableNextColumn();\n        Text(\"%d\", (int)(p - str));\n        TableNextColumn();\n        for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)\n        {\n            if (byte_index > 0)\n                SameLine();\n            Text(\"0x%02X\", (int)(unsigned char)p[byte_index]);\n        }\n        TableNextColumn();\n        TextUnformatted(p, p + c_utf8_len);\n        if (!GetFont()->IsGlyphInFont((ImWchar)c))\n        {\n            SameLine();\n            TextUnformatted(\"[missing]\");\n        }\n        TableNextColumn();\n        Text(\"U+%04X\", (int)c);\n        p += c_utf8_len;\n    }\n    EndTable();\n}\n\nstatic void DebugFlashStyleColorStop()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.DebugFlashStyleColorIdx != ImGuiCol_COUNT)\n        g.Style.Colors[g.DebugFlashStyleColorIdx] = g.DebugFlashStyleColorBackup;\n    g.DebugFlashStyleColorIdx = ImGuiCol_COUNT;\n}\n\n// Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls.\nvoid ImGui::DebugFlashStyleColor(ImGuiCol idx)\n{\n    ImGuiContext& g = *GImGui;\n    DebugFlashStyleColorStop();\n    g.DebugFlashStyleColorTime = 0.5f;\n    g.DebugFlashStyleColorIdx = idx;\n    g.DebugFlashStyleColorBackup = g.Style.Colors[idx];\n}\n\nvoid ImGui::UpdateDebugToolFlashStyleColor()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.DebugFlashStyleColorTime <= 0.0f)\n        return;\n    ColorConvertHSVtoRGB(ImCos(g.DebugFlashStyleColorTime * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, g.Style.Colors[g.DebugFlashStyleColorIdx].x, g.Style.Colors[g.DebugFlashStyleColorIdx].y, g.Style.Colors[g.DebugFlashStyleColorIdx].z);\n    g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f;\n    if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f)\n        DebugFlashStyleColorStop();\n}\n\nImU64 ImGui::DebugTextureIDToU64(ImTextureID tex_id)\n{\n    ImU64 v = 0;\n    memcpy(&v, &tex_id, ImMin(sizeof(ImU64), sizeof(ImTextureID)));\n    return v;\n}\n\nstatic const char* FormatTextureRefForDebugDisplay(char* buf, int buf_size, ImTextureRef tex_ref)\n{\n    char* buf_p = buf;\n    char* buf_end = buf + buf_size;\n    if (tex_ref._TexData != NULL)\n        buf_p += ImFormatString(buf_p, buf_end - buf_p, \"#%03d: \", tex_ref._TexData->UniqueID);\n    ImFormatString(buf_p, buf_end - buf_p, \"0x%X\", ImGui::DebugTextureIDToU64(tex_ref.GetTexID()));\n    return buf;\n}\n\n#ifdef IMGUI_ENABLE_FREETYPE\nnamespace ImGuiFreeType { IMGUI_API const ImFontLoader* GetFontLoader(); IMGUI_API bool DebugEditFontLoaderFlags(unsigned int* p_font_builder_flags); }\n#endif\n\n// [DEBUG] List fonts in a font atlas and display its texture\nvoid ImGui::ShowFontAtlas(ImFontAtlas* atlas)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n    ImGuiStyle& style = g.Style;\n\n    BeginDisabled();\n    CheckboxFlags(\"io.BackendFlags: RendererHasTextures\", &io.BackendFlags, ImGuiBackendFlags_RendererHasTextures);\n    EndDisabled();\n    ShowFontSelector(\"Font\");\n    //BeginDisabled((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0);\n    if (DragFloat(\"FontSizeBase\", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, \"%.0f\"))\n        style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work.\n    SameLine(0.0f, 0.0f); Text(\" (out %.2f)\", GetFontSize());\n    SameLine(); MetricsHelpMarker(\"- This is scaling font only. General scaling will come later.\");\n    DragFloat(\"FontScaleMain\", &style.FontScaleMain, 0.02f, 0.5f, 4.0f);\n    //BeginDisabled(io.ConfigDpiScaleFonts);\n    DragFloat(\"FontScaleDpi\", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f);\n    //SetItemTooltip(\"When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.\");\n    //EndDisabled();\n    if ((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)\n    {\n        BulletText(\"Warning: Font scaling will NOT be smooth, because\\nImGuiBackendFlags_RendererHasTextures is not set!\");\n        BulletText(\"For instructions, see:\");\n        SameLine();\n        TextLinkOpenURL(\"docs/BACKENDS.md\", \"https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md\");\n    }\n    BulletText(\"Load a nice font for better results!\");\n    BulletText(\"Please submit feedback:\");\n    SameLine(); TextLinkOpenURL(\"#8465\", \"https://github.com/ocornut/imgui/issues/8465\");\n    BulletText(\"Read FAQ for more details:\");\n    SameLine(); TextLinkOpenURL(\"dearimgui.com/faq\", \"https://www.dearimgui.com/faq/\");\n    //EndDisabled();\n\n    SeparatorText(\"Font List\");\n\n    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;\n    Checkbox(\"Show font preview\", &cfg->ShowFontPreview);\n\n    // Font loaders\n    if (TreeNode(\"Loader\", \"Loader: \\'%s\\'\", atlas->FontLoaderName ? atlas->FontLoaderName : \"NULL\"))\n    {\n        const ImFontLoader* loader_current = atlas->FontLoader;\n        BeginDisabled(!atlas->RendererHasTextures);\n#ifdef IMGUI_ENABLE_STB_TRUETYPE\n        const ImFontLoader* loader_stbtruetype = ImFontAtlasGetFontLoaderForStbTruetype();\n        if (RadioButton(\"stb_truetype\", loader_current == loader_stbtruetype))\n            atlas->SetFontLoader(loader_stbtruetype);\n#else\n        BeginDisabled();\n        RadioButton(\"stb_truetype\", false);\n        SetItemTooltip(\"Requires #define IMGUI_ENABLE_STB_TRUETYPE\");\n        EndDisabled();\n#endif\n        SameLine();\n#ifdef IMGUI_ENABLE_FREETYPE\n        const ImFontLoader* loader_freetype = ImGuiFreeType::GetFontLoader();\n        if (RadioButton(\"FreeType\", loader_current == loader_freetype))\n            atlas->SetFontLoader(loader_freetype);\n        if (loader_current == loader_freetype)\n        {\n            unsigned int loader_flags = atlas->FontLoaderFlags;\n            Text(\"Shared FreeType Loader Flags:  0x%08X\", loader_flags);\n            if (ImGuiFreeType::DebugEditFontLoaderFlags(&loader_flags))\n            {\n                for (ImFont* font : atlas->Fonts)\n                    ImFontAtlasFontDestroyOutput(atlas, font);\n                atlas->FontLoaderFlags = loader_flags;\n                for (ImFont* font : atlas->Fonts)\n                    ImFontAtlasFontInitOutput(atlas, font);\n            }\n        }\n#else\n        BeginDisabled();\n        RadioButton(\"FreeType\", false);\n        SetItemTooltip(\"Requires #define IMGUI_ENABLE_FREETYPE + imgui_freetype.cpp.\");\n        EndDisabled();\n#endif\n        EndDisabled();\n        TreePop();\n    }\n\n    // Font list\n    for (ImFont* font : atlas->Fonts)\n    {\n        PushID(font);\n        DebugNodeFont(font);\n        PopID();\n    }\n\n    SeparatorText(\"Font Atlas\");\n    if (Button(\"Compact\"))\n        atlas->CompactCache();\n    SameLine();\n    if (Button(\"Grow\"))\n        ImFontAtlasTextureGrow(atlas);\n    SameLine();\n    if (Button(\"Clear All\"))\n        ImFontAtlasBuildClear(atlas);\n    SetItemTooltip(\"Destroy cache and custom rectangles.\");\n\n    for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)\n    {\n        ImTextureData* tex = atlas->TexList[tex_n];\n        if (tex_n > 0)\n            SameLine();\n        Text(\"Tex: %dx%d\", tex->Width, tex->Height);\n    }\n    const int packed_surface_sqrt = (int)sqrtf((float)atlas->Builder->RectsPackedSurface);\n    const int discarded_surface_sqrt = (int)sqrtf((float)atlas->Builder->RectsDiscardedSurface);\n    Text(\"Packed rects: %d, area: about %d px ~%dx%d px\", atlas->Builder->RectsPackedCount, atlas->Builder->RectsPackedSurface, packed_surface_sqrt, packed_surface_sqrt);\n    Text(\"incl. Discarded rects: %d, area: about %d px ~%dx%d px\", atlas->Builder->RectsDiscardedCount, atlas->Builder->RectsDiscardedSurface, discarded_surface_sqrt, discarded_surface_sqrt);\n\n    ImFontAtlasRectId highlight_r_id = ImFontAtlasRectId_Invalid;\n    if (TreeNode(\"Rects Index\", \"Rects Index (%d)\", atlas->Builder->RectsPackedCount)) // <-- Use count of used rectangles\n    {\n        PushStyleVar(ImGuiStyleVar_ImageBorderSize, 1.0f);\n        if (BeginTable(\"##table\", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY, ImVec2(0.0f, GetTextLineHeightWithSpacing() * 12)))\n        {\n            for (const ImFontAtlasRectEntry& entry : atlas->Builder->RectsIndex)\n                if (entry.IsUsed)\n                {\n                    ImFontAtlasRectId id = ImFontAtlasRectId_Make(atlas->Builder->RectsIndex.index_from_ptr(&entry), entry.Generation);\n                    ImFontAtlasRect r = {};\n                    atlas->GetCustomRect(id, &r);\n                    const char* buf;\n                    ImFormatStringToTempBuffer(&buf, NULL, \"ID:%08X, used:%d, { w:%3d, h:%3d } { x:%4d, y:%4d }\", id, entry.IsUsed, r.w, r.h, r.x, r.y);\n                    TableNextColumn();\n                    Selectable(buf);\n                    if (IsItemHovered())\n                        highlight_r_id = id;\n                    TableNextColumn();\n                    Image(atlas->TexRef, ImVec2(r.w, r.h), r.uv0, r.uv1);\n                }\n            EndTable();\n        }\n        PopStyleVar();\n        TreePop();\n    }\n\n    // Texture list\n    // (ensure the last texture always use the same ID, so we can keep it open neatly)\n    ImFontAtlasRect highlight_r;\n    if (highlight_r_id != ImFontAtlasRectId_Invalid)\n        atlas->GetCustomRect(highlight_r_id, &highlight_r);\n    for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)\n    {\n        if (tex_n == atlas->TexList.Size - 1)\n            SetNextItemOpen(true, ImGuiCond_Once);\n        DebugNodeTexture(atlas->TexList[tex_n], atlas->TexList.Size - 1 - tex_n, (highlight_r_id != ImFontAtlasRectId_Invalid) ? &highlight_r : NULL);\n    }\n}\n\nvoid ImGui::DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRect* highlight_rect)\n{\n    ImGuiContext& g = *GImGui;\n    PushID(int_id);\n    if (TreeNode(\"\", \"Texture #%03d (%dx%d pixels)\", tex->UniqueID, tex->Width, tex->Height))\n    {\n        ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;\n        Checkbox(\"Show used rect\", &cfg->ShowTextureUsedRect);\n        PushStyleVar(ImGuiStyleVar_ImageBorderSize, ImMax(1.0f, g.Style.ImageBorderSize));\n        ImVec2 p = GetCursorScreenPos();\n        if (tex->WantDestroyNextFrame)\n            Dummy(ImVec2((float)tex->Width, (float)tex->Height));\n        else\n            ImageWithBg(tex->GetTexRef(), ImVec2((float)tex->Width, (float)tex->Height), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), ImVec4(0.0f, 0.0f, 0.0f, 1.0f));\n        if (cfg->ShowTextureUsedRect)\n            GetWindowDrawList()->AddRect(ImVec2(p.x + tex->UsedRect.x, p.y + tex->UsedRect.y), ImVec2(p.x + tex->UsedRect.x + tex->UsedRect.w, p.y + tex->UsedRect.y + tex->UsedRect.h), IM_COL32(255, 0, 255, 255));\n        if (highlight_rect != NULL)\n        {\n            ImRect r_outer(p.x, p.y, p.x + tex->Width, p.y + tex->Height);\n            ImRect r_inner(p.x + highlight_rect->x, p.y + highlight_rect->y, p.x + highlight_rect->x + highlight_rect->w, p.y + highlight_rect->y + highlight_rect->h);\n            RenderRectFilledWithHole(GetWindowDrawList(), r_outer, r_inner, IM_COL32(0, 0, 0, 100), 0.0f);\n            GetWindowDrawList()->AddRect(r_inner.Min - ImVec2(1, 1), r_inner.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255));\n        }\n        PopStyleVar();\n\n        char texref_desc[30];\n        Text(\"Status = %s (%d), Format = %s (%d), UseColors = %d\", ImTextureDataGetStatusName(tex->Status), tex->Status, ImTextureDataGetFormatName(tex->Format), tex->Format, tex->UseColors);\n        Text(\"TexRef = %s, BackendUserData = %p\", FormatTextureRefForDebugDisplay(texref_desc, IM_COUNTOF(texref_desc), tex->GetTexRef()), tex->BackendUserData);\n        TreePop();\n    }\n    PopID();\n}\n\nvoid ImGui::ShowMetricsWindow(bool* p_open)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;\n    if (cfg->ShowDebugLog)\n        ShowDebugLogWindow(&cfg->ShowDebugLog);\n    if (cfg->ShowIDStackTool)\n        ShowIDStackToolWindow(&cfg->ShowIDStackTool);\n\n    if (!Begin(\"Dear ImGui Metrics/Debugger\", p_open) || GetCurrentWindow()->BeginCount > 1)\n    {\n        End();\n        return;\n    }\n\n    // [DEBUG] Clear debug breaks hooks after exactly one cycle.\n    DebugBreakClearData();\n\n    // Basic info\n    Text(\"Dear ImGui %s (%d)\", IMGUI_VERSION, IMGUI_VERSION_NUM);\n    if (g.ContextName[0] != 0)\n    {\n        SameLine();\n        Text(\"(Context Name: \\\"%s\\\")\", g.ContextName);\n    }\n    Text(\"Application average %.3f ms/frame (%.1f FPS)\", 1000.0f / io.Framerate, io.Framerate);\n    Text(\"%d vertices, %d indices (%d triangles)\", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);\n    Text(\"%d visible windows, %d current allocations\", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount);\n    //SameLine(); if (SmallButton(\"GC\")) { g.GcCompactAll = true; }\n\n    Separator();\n\n    // Debugging enums\n    enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type\n    const char* wrt_rects_names[WRT_Count] = { \"OuterRect\", \"OuterRectClipped\", \"InnerRect\", \"InnerClipRect\", \"WorkRect\", \"Content\", \"ContentIdeal\", \"ContentRegionRect\" };\n    enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type\n    const char* trt_rects_names[TRT_Count] = { \"OuterRect\", \"InnerRect\", \"WorkRect\", \"HostClipRect\", \"InnerClipRect\", \"BackgroundClipRect\", \"ColumnsRect\", \"ColumnsWorkRect\", \"ColumnsClipRect\", \"ColumnsContentHeadersUsed\", \"ColumnsContentHeadersIdeal\", \"ColumnsContentFrozen\", \"ColumnsContentUnfrozen\" };\n    if (cfg->ShowWindowsRectsType < 0)\n        cfg->ShowWindowsRectsType = WRT_WorkRect;\n    if (cfg->ShowTablesRectsType < 0)\n        cfg->ShowTablesRectsType = TRT_WorkRect;\n\n    struct Funcs\n    {\n        static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)\n        {\n            ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance\n            if (rect_type == TRT_OuterRect)                     { return table->OuterRect; }\n            else if (rect_type == TRT_InnerRect)                { return table->InnerRect; }\n            else if (rect_type == TRT_WorkRect)                 { return table->WorkRect; }\n            else if (rect_type == TRT_HostClipRect)             { return table->HostClipRect; }\n            else if (rect_type == TRT_InnerClipRect)            { return table->InnerClipRect; }\n            else if (rect_type == TRT_BackgroundClipRect)       { return table->BgClipRect; }\n            else if (rect_type == TRT_ColumnsRect)              { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); }\n            else if (rect_type == TRT_ColumnsWorkRect)          { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }\n            else if (rect_type == TRT_ColumnsClipRect)          { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }\n            else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } // Note: y1/y2 not always accurate\n            else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); }\n            else if (rect_type == TRT_ColumnsContentFrozen)     { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); }\n            else if (rect_type == TRT_ColumnsContentUnfrozen)   { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }\n            IM_ASSERT(0);\n            return ImRect();\n        }\n\n        static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)\n        {\n            if (rect_type == WRT_OuterRect)                 { return window->Rect(); }\n            else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }\n            else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }\n            else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }\n            else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }\n            else if (rect_type == WRT_Content)              { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }\n            else if (rect_type == WRT_ContentIdeal)         { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }\n            else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }\n            IM_ASSERT(0);\n            return ImRect();\n        }\n    };\n\n#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS\n    TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), \"IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS is enabled.\\nMust disable after use! Otherwise Dear ImGui will run slower.\\n\");\n#endif\n\n    // Tools\n    if (TreeNode(\"Tools\"))\n    {\n        // Debug Break features\n        // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.\n        SeparatorTextEx(0, \"Debug breaks\", NULL, CalcTextSize(\"(?)\").x + g.Style.SeparatorTextPadding.x);\n        SameLine();\n        MetricsHelpMarker(\"Will call the IM_DEBUG_BREAK() macro to break in debugger.\\nWarning: If you don't have a debugger attached, this will probably crash.\");\n        if (Checkbox(\"Show Item Picker\", &g.DebugItemPickerActive) && g.DebugItemPickerActive)\n            DebugStartItemPicker();\n        Checkbox(\"Show \\\"Debug Break\\\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)\", &g.IO.ConfigDebugIsDebuggerPresent);\n\n        SeparatorText(\"Visualize\");\n\n        Checkbox(\"Show Debug Log\", &cfg->ShowDebugLog);\n        SameLine();\n        MetricsHelpMarker(\"You can also call ImGui::ShowDebugLogWindow() from your code.\");\n\n        Checkbox(\"Show ID Stack Tool\", &cfg->ShowIDStackTool);\n        SameLine();\n        MetricsHelpMarker(\"You can also call ImGui::ShowIDStackToolWindow() from your code.\");\n\n        Checkbox(\"Show windows begin order\", &cfg->ShowWindowsBeginOrder);\n        Checkbox(\"Show windows rectangles\", &cfg->ShowWindowsRects);\n        SameLine();\n        SetNextItemWidth(GetFontSize() * 12);\n        cfg->ShowWindowsRects |= Combo(\"##show_windows_rect_type\", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);\n        if (cfg->ShowWindowsRects && g.NavWindow != NULL)\n        {\n            BulletText(\"'%s':\", g.NavWindow->Name);\n            Indent();\n            for (int rect_n = 0; rect_n < WRT_Count; rect_n++)\n            {\n                ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);\n                Text(\"(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s\", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]);\n            }\n            Unindent();\n        }\n\n        Checkbox(\"Show tables rectangles\", &cfg->ShowTablesRects);\n        SameLine();\n        SetNextItemWidth(GetFontSize() * 12);\n        cfg->ShowTablesRects |= Combo(\"##show_table_rects_type\", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);\n        if (cfg->ShowTablesRects && g.NavWindow != NULL)\n        {\n            for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)\n            {\n                ImGuiTable* table = g.Tables.TryGetMapData(table_n);\n                if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))\n                    continue;\n\n                BulletText(\"Table 0x%08X (%d columns, in '%s')\", table->ID, table->ColumnsCount, table->OuterWindow->Name);\n                if (IsItemHovered())\n                    GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);\n                Indent();\n                char buf[128];\n                for (int rect_n = 0; rect_n < TRT_Count; rect_n++)\n                {\n                    if (rect_n >= TRT_ColumnsRect)\n                    {\n                        if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)\n                            continue;\n                        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n                        {\n                            ImRect r = Funcs::GetTableRect(table, rect_n, column_n);\n                            ImFormatString(buf, IM_COUNTOF(buf), \"(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s\", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);\n                            Selectable(buf);\n                            if (IsItemHovered())\n                                GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);\n                        }\n                    }\n                    else\n                    {\n                        ImRect r = Funcs::GetTableRect(table, rect_n, -1);\n                        ImFormatString(buf, IM_COUNTOF(buf), \"(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s\", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]);\n                        Selectable(buf);\n                        if (IsItemHovered())\n                            GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);\n                    }\n                }\n                Unindent();\n            }\n        }\n        Checkbox(\"Show groups rectangles\", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data\n\n        SeparatorText(\"Validate\");\n\n        Checkbox(\"Debug Begin/BeginChild return value\", &io.ConfigDebugBeginReturnValueLoop);\n        SameLine();\n        MetricsHelpMarker(\"Some calls to Begin()/BeginChild() will return false.\\n\\nWill cycle through window depths then repeat. Windows should be flickering while running.\");\n\n        Checkbox(\"UTF-8 Encoding viewer\", &cfg->ShowTextEncodingViewer);\n        SameLine();\n        MetricsHelpMarker(\"You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.\");\n        if (cfg->ShowTextEncodingViewer)\n        {\n            static char buf[64] = \"\";\n            SetNextItemWidth(-FLT_MIN);\n            InputText(\"##DebugTextEncodingBuf\", buf, IM_COUNTOF(buf));\n            if (buf[0] != 0)\n                DebugTextEncoding(buf);\n        }\n\n        TreePop();\n    }\n\n    // Windows\n    if (TreeNode(\"Windows\", \"Windows (%d)\", g.Windows.Size))\n    {\n        //SetNextItemOpen(true, ImGuiCond_Once);\n        DebugNodeWindowsList(&g.Windows, \"By display order\");\n        DebugNodeWindowsList(&g.WindowsFocusOrder, \"By focus order (root windows)\");\n        if (TreeNode(\"By submission order (begin stack)\"))\n        {\n            // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship!\n            ImVector<ImGuiWindow*>& temp_buffer = g.WindowsTempSortBuffer;\n            temp_buffer.resize(0);\n            for (ImGuiWindow* window : g.Windows)\n                if (window->LastFrameActive + 1 >= g.FrameCount)\n                    temp_buffer.push_back(window);\n            struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } };\n            ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder);\n            DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL);\n            TreePop();\n        }\n\n        TreePop();\n    }\n\n    // DrawLists\n    int drawlist_count = 0;\n    for (ImGuiViewportP* viewport : g.Viewports)\n        drawlist_count += viewport->DrawDataP.CmdLists.Size;\n    if (TreeNode(\"DrawLists\", \"DrawLists (%d)\", drawlist_count))\n    {\n        Checkbox(\"Show ImDrawCmd mesh when hovering\", &cfg->ShowDrawCmdMesh);\n        Checkbox(\"Show ImDrawCmd bounding boxes when hovering\", &cfg->ShowDrawCmdBoundingBoxes);\n        for (ImGuiViewportP* viewport : g.Viewports)\n        {\n            bool viewport_has_drawlist = false;\n            for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)\n            {\n                if (!viewport_has_drawlist)\n                    Text(\"Active DrawLists in Viewport #%d, ID: 0x%08X\", viewport->Idx, viewport->ID);\n                viewport_has_drawlist = true;\n                DebugNodeDrawList(NULL, viewport, draw_list, \"DrawList\");\n            }\n        }\n        TreePop();\n    }\n\n    // Viewports\n    if (TreeNode(\"Viewports\", \"Viewports (%d)\", g.Viewports.Size))\n    {\n        cfg->HighlightMonitorIdx = -1;\n        bool open = TreeNode(\"Monitors\", \"Monitors (%d)\", g.PlatformIO.Monitors.Size);\n        SameLine();\n        MetricsHelpMarker(\"Dear ImGui uses monitor data:\\n- to query DPI settings on a per monitor basis\\n- to position popup/tooltips so they don't straddle monitors.\");\n        if (open)\n        {\n            for (int i = 0; i < g.PlatformIO.Monitors.Size; i++)\n            {\n                DebugNodePlatformMonitor(&g.PlatformIO.Monitors[i], \"Monitor\", i);\n                if (IsItemHovered())\n                    cfg->HighlightMonitorIdx = i;\n            }\n            DebugNodePlatformMonitor(&g.FallbackMonitor, \"Fallback\", 0);\n            TreePop();\n        }\n\n        SetNextItemOpen(true, ImGuiCond_Once);\n        if (TreeNode(\"Windows Minimap\"))\n        {\n            RenderViewportsThumbnails();\n            TreePop();\n        }\n        cfg->HighlightViewportID = 0;\n\n        BulletText(\"MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)\", g.MouseViewport ? g.MouseViewport->ID : 0, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);\n        if (TreeNode(\"Inferred Z order (front-to-back)\"))\n        {\n            static ImVector<ImGuiViewportP*> viewports;\n            viewports.resize(g.Viewports.Size);\n            memcpy(viewports.Data, g.Viewports.Data, g.Viewports.size_in_bytes());\n            if (viewports.Size > 1)\n                ImQsort(viewports.Data, viewports.Size, sizeof(ImGuiViewport*), ViewportComparerByLastFocusedStampCount);\n            for (ImGuiViewportP* viewport : viewports)\n            {\n                BulletText(\"Viewport #%d, ID: 0x%08X, LastFocused = %08d, PlatformFocused = %s, Window: \\\"%s\\\"\",\n                    viewport->Idx, viewport->ID, viewport->LastFocusedStampCount,\n                    (g.PlatformIO.Platform_GetWindowFocus && viewport->PlatformWindowCreated) ? (g.PlatformIO.Platform_GetWindowFocus(viewport) ? \"1\" : \"0\") : \"N/A\",\n                    viewport->Window ? viewport->Window->Name : \"N/A\");\n                if (IsItemHovered())\n                    cfg->HighlightViewportID = viewport->ID;\n            }\n            TreePop();\n        }\n\n        for (ImGuiViewportP* viewport : g.Viewports)\n            DebugNodeViewport(viewport);\n        TreePop();\n    }\n\n    // Details for Fonts\n    for (ImFontAtlas* atlas : g.FontAtlases)\n        if (TreeNode((void*)atlas, \"Fonts (%d), Textures (%d)\", atlas->Fonts.Size, atlas->TexList.Size))\n        {\n            ShowFontAtlas(atlas);\n            TreePop();\n        }\n\n    // Details for Popups\n    if (TreeNode(\"Popups\", \"Popups (%d)\", g.OpenPopupStack.Size))\n    {\n        for (const ImGuiPopupData& popup_data : g.OpenPopupStack)\n        {\n            // As it's difficult to interact with tree nodes while popups are open, we display everything inline.\n            ImGuiWindow* window = popup_data.Window;\n            BulletText(\"PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'\",\n                popup_data.PopupId, window ? window->Name : \"NULL\", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? \"Child;\" : \"\", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? \"Menu;\" : \"\",\n                popup_data.RestoreNavWindow ? popup_data.RestoreNavWindow->Name : \"NULL\", window && window->ParentWindow ? window->ParentWindow->Name : \"NULL\");\n        }\n        TreePop();\n    }\n\n    // Details for TabBars\n    if (TreeNode(\"TabBars\", \"Tab Bars (%d)\", g.TabBars.GetAliveCount()))\n    {\n        for (int n = 0; n < g.TabBars.GetMapSize(); n++)\n            if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))\n            {\n                PushID(tab_bar);\n                DebugNodeTabBar(tab_bar, \"TabBar\");\n                PopID();\n            }\n        TreePop();\n    }\n\n    // Details for Tables\n    if (TreeNode(\"Tables\", \"Tables (%d)\", g.Tables.GetAliveCount()))\n    {\n        for (int n = 0; n < g.Tables.GetMapSize(); n++)\n            if (ImGuiTable* table = g.Tables.TryGetMapData(n))\n                DebugNodeTable(table);\n        TreePop();\n    }\n\n    // Details for InputText\n    if (TreeNode(\"InputText\"))\n    {\n        DebugNodeInputTextState(&g.InputTextState);\n        TreePop();\n    }\n\n    // Details for TypingSelect\n    if (TreeNode(\"TypingSelect\", \"TypingSelect (%d)\", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0))\n    {\n        DebugNodeTypingSelectState(&g.TypingSelectState);\n        TreePop();\n    }\n\n    // Details for MultiSelect\n    if (TreeNode(\"MultiSelect\", \"MultiSelect (%d)\", g.MultiSelectStorage.GetAliveCount()))\n    {\n        ImGuiBoxSelectState* bs = &g.BoxSelectState;\n        BulletText(\"BoxSelect ID=0x%08X, Starting = %d, Active %d\", bs->ID, bs->IsStarting, bs->IsActive);\n        for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++)\n            if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n))\n                DebugNodeMultiSelectState(state);\n        TreePop();\n    }\n\n    // Details for Docking\n#ifdef IMGUI_HAS_DOCK\n    if (TreeNode(\"Docking\"))\n    {\n        static bool root_nodes_only = true;\n        ImGuiDockContext* dc = &g.DockContext;\n        Checkbox(\"List root nodes\", &root_nodes_only);\n        Checkbox(\"Ctrl shows window dock info\", &cfg->ShowDockingNodes);\n        if (SmallButton(\"Clear nodes\")) { DockContextClearNodes(&g, 0, true); }\n        SameLine();\n        if (SmallButton(\"Rebuild all\")) { dc->WantFullRebuild = true; }\n        for (int n = 0; n < dc->Nodes.Data.Size; n++)\n            if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)\n                if (!root_nodes_only || node->IsRootNode())\n                    DebugNodeDockNode(node, \"Node\");\n        TreePop();\n    }\n#endif // #ifdef IMGUI_HAS_DOCK\n\n    // Settings\n    if (TreeNode(\"Settings\"))\n    {\n        if (SmallButton(\"Clear\"))\n            ClearIniSettings();\n        SameLine();\n        if (SmallButton(\"Save to memory\"))\n            SaveIniSettingsToMemory();\n        SameLine();\n        if (SmallButton(\"Save to disk\"))\n            SaveIniSettingsToDisk(g.IO.IniFilename);\n        SameLine();\n        if (g.IO.IniFilename)\n            Text(\"\\\"%s\\\"\", g.IO.IniFilename);\n        else\n            TextUnformatted(\"<NULL>\");\n        Checkbox(\"io.ConfigDebugIniSettings\", &io.ConfigDebugIniSettings);\n        Text(\"SettingsDirtyTimer %.2f\", g.SettingsDirtyTimer);\n        if (TreeNode(\"SettingsHandlers\", \"Settings handlers: (%d)\", g.SettingsHandlers.Size))\n        {\n            for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n                BulletText(\"\\\"%s\\\"\", handler.TypeName);\n            TreePop();\n        }\n        if (TreeNode(\"SettingsWindows\", \"Settings packed data: Windows: %d bytes\", g.SettingsWindows.size()))\n        {\n            for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n                DebugNodeWindowSettings(settings);\n            TreePop();\n        }\n\n        if (TreeNode(\"SettingsTables\", \"Settings packed data: Tables: %d bytes\", g.SettingsTables.size()))\n        {\n            for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))\n                DebugNodeTableSettings(settings);\n            TreePop();\n        }\n\n#ifdef IMGUI_HAS_DOCK\n        if (TreeNode(\"SettingsDocking\", \"Settings packed data: Docking\"))\n        {\n            ImGuiDockContext* dc = &g.DockContext;\n            Text(\"In SettingsWindows:\");\n            for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n                if (settings->DockId != 0)\n                    BulletText(\"Window '%s' -> DockId %08X DockOrder=%d\", settings->GetName(), settings->DockId, settings->DockOrder);\n            Text(\"In SettingsNodes:\");\n            for (int n = 0; n < dc->NodesSettings.Size; n++)\n            {\n                ImGuiDockNodeSettings* settings = &dc->NodesSettings[n];\n                const char* selected_tab_name = NULL;\n                if (settings->SelectedTabId)\n                {\n                    if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabId))\n                        selected_tab_name = window->Name;\n                    else if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->SelectedTabId))\n                        selected_tab_name = window_settings->GetName();\n                }\n                BulletText(\"Node %08X, Parent %08X, SelectedTab %08X ('%s')\", settings->ID, settings->ParentNodeId, settings->SelectedTabId, selected_tab_name ? selected_tab_name : settings->SelectedTabId ? \"N/A\" : \"\");\n            }\n            TreePop();\n        }\n#endif // #ifdef IMGUI_HAS_DOCK\n\n        if (TreeNode(\"SettingsIniData\", \"Settings unpacked data (.ini): %d bytes\", g.SettingsIniData.size()))\n        {\n            InputTextMultiline(\"##Ini\", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);\n            TreePop();\n        }\n        TreePop();\n    }\n\n    // Settings\n    if (TreeNode(\"Memory allocations\"))\n    {\n        ImGuiDebugAllocInfo* info = &g.DebugAllocInfo;\n        Text(\"%d current allocations\", info->TotalAllocCount - info->TotalFreeCount);\n        if (SmallButton(\"GC now\")) { g.GcCompactAll = true; }\n        Text(\"Recent frames with allocations:\");\n        int buf_size = IM_COUNTOF(info->LastEntriesBuf);\n        for (int n = buf_size - 1; n >= 0; n--)\n        {\n            ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size];\n            BulletText(\"Frame %06d: %+3d ( %2d alloc, %2d free )\", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount);\n            if (n == 0)\n            {\n                SameLine();\n                Text(\"<- %d frames ago\", g.FrameCount - entry->FrameCount);\n            }\n        }\n        TreePop();\n    }\n\n    if (TreeNode(\"Inputs\"))\n    {\n        Text(\"KEYBOARD/GAMEPAD/MOUSE KEYS\");\n        {\n            // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.\n            Indent();\n            Text(\"Keys down:\");         for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyDown(key)) continue;     SameLine(); Text(IsNamedKey(key) ? \"\\\"%s\\\"\" : \"\\\"%s\\\" %d\", GetKeyName(key), key); SameLine(); Text(\"(%.02f)\", GetKeyData(key)->DownDuration); }\n            Text(\"Keys pressed:\");      for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyPressed(key)) continue;  SameLine(); Text(IsNamedKey(key) ? \"\\\"%s\\\"\" : \"\\\"%s\\\" %d\", GetKeyName(key), key); }\n            Text(\"Keys released:\");     for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? \"\\\"%s\\\"\" : \"\\\"%s\\\" %d\", GetKeyName(key), key); }\n            Text(\"Keys mods: %s%s%s%s\", io.KeyCtrl ? \"Ctrl \" : \"\", io.KeyShift ? \"Shift \" : \"\", io.KeyAlt ? \"Alt \" : \"\", io.KeySuper ? \"Super \" : \"\");\n            Text(\"Chars queue:\");       for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text(\"\\'%c\\' (0x%04X)\", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.\n            DebugRenderKeyboardPreview(GetWindowDrawList());\n            Unindent();\n        }\n\n        Text(\"MOUSE STATE\");\n        {\n            Indent();\n            if (IsMousePosValid())\n                Text(\"Mouse pos: (%g, %g)\", io.MousePos.x, io.MousePos.y);\n            else\n                Text(\"Mouse pos: <INVALID>\");\n            Text(\"Mouse delta: (%g, %g)\", io.MouseDelta.x, io.MouseDelta.y);\n            int count = IM_COUNTOF(io.MouseDown);\n            Text(\"Mouse down:\");     for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text(\"b%d (%.02f secs)\", i, io.MouseDownDuration[i]); }\n            Text(\"Mouse clicked:\");  for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text(\"b%d (%d)\", i, io.MouseClickedCount[i]); }\n            Text(\"Mouse released:\"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text(\"b%d\", i); }\n            Text(\"Mouse wheel: %.1f\", io.MouseWheel);\n            Text(\"MouseStationaryTimer: %.2f\", g.MouseStationaryTimer);\n            Text(\"Mouse source: %s\", GetMouseSourceName(io.MouseSource));\n            Text(\"Pen Pressure: %.1f\", io.PenPressure); // Note: currently unused\n            Unindent();\n        }\n\n        Text(\"MOUSE WHEELING\");\n        {\n            Indent();\n            Text(\"WheelingWindow: '%s'\", g.WheelingWindow ? g.WheelingWindow->Name : \"NULL\");\n            Text(\"WheelingWindowReleaseTimer: %.2f\", g.WheelingWindowReleaseTimer);\n            Text(\"WheelingAxisAvg[] = { %.3f, %.3f }, Main Axis: %s\", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y, (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? \"X\" : (g.WheelingAxisAvg.x < g.WheelingAxisAvg.y) ? \"Y\" : \"<none>\");\n            Unindent();\n        }\n\n        Text(\"KEY OWNERS\");\n        {\n            Indent();\n            if (BeginChild(\"##owners\", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))\n                for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))\n                {\n                    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);\n                    if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)\n                        continue;\n                    Text(\"%s: 0x%08X%s\", GetKeyName(key), owner_data->OwnerCurr,\n                        owner_data->LockUntilRelease ? \" LockUntilRelease\" : owner_data->LockThisFrame ? \" LockThisFrame\" : \"\");\n                    DebugLocateItemOnHover(owner_data->OwnerCurr);\n                }\n            EndChild();\n            Unindent();\n        }\n        Text(\"SHORTCUT ROUTING\");\n        SameLine();\n        MetricsHelpMarker(\"Declared shortcut routes automatically set key owner when mods matches.\");\n        {\n            Indent();\n            if (BeginChild(\"##routes\", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))\n                for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))\n                {\n                    ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;\n                    for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )\n                    {\n                        ImGuiKeyRoutingData* routing_data = &rt->Entries[idx];\n                        ImGuiKeyChord key_chord = key | routing_data->Mods;\n                        Text(\"%s: 0x%08X (scored %d)\", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore);\n                        DebugLocateItemOnHover(routing_data->RoutingCurr);\n                        if (g.IO.ConfigDebugIsDebuggerPresent)\n                        {\n                            SameLine();\n                            if (DebugBreakButton(\"**DebugBreak**\", \"in SetShortcutRouting() for this KeyChord\"))\n                                g.DebugBreakInShortcutRouting = key_chord;\n                        }\n                        idx = routing_data->NextEntryIndex;\n                    }\n                }\n            EndChild();\n            Text(\"(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)\", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);\n            Unindent();\n        }\n        TreePop();\n    }\n\n    if (TreeNode(\"Internal state\"))\n    {\n        Text(\"WINDOWING\");\n        Indent();\n        Text(\"HoveredWindow: '%s'\", g.HoveredWindow ? g.HoveredWindow->Name : \"NULL\");\n        Text(\"HoveredWindow->Root: '%s'\", g.HoveredWindow ? g.HoveredWindow->RootWindowDockTree->Name : \"NULL\");\n        Text(\"HoveredWindowUnderMovingWindow: '%s'\", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : \"NULL\");\n        Text(\"HoveredDockNode: 0x%08X\", g.DebugHoveredDockNode ? g.DebugHoveredDockNode->ID : 0);\n        Text(\"MovingWindow: '%s'\", g.MovingWindow ? g.MovingWindow->Name : \"NULL\");\n        Text(\"MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)\", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);\n        Unindent();\n\n        Text(\"ITEMS\");\n        Indent();\n        Text(\"ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s\", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource));\n        DebugLocateItemOnHover(g.ActiveId);\n        Text(\"ActiveIdWindow: '%s'\", g.ActiveIdWindow ? g.ActiveIdWindow->Name : \"NULL\");\n        Text(\"ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X\", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);\n        Text(\"HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d\", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame\n        Text(\"HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f\", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer);\n        Text(\"DragDrop: %d, SourceId = 0x%08X, Payload \\\"%s\\\" (%d bytes)\", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);\n        DebugLocateItemOnHover(g.DragDropPayload.SourceId);\n        Unindent();\n\n        Text(\"NAV,FOCUS\");\n        Indent();\n        Text(\"NavWindow: '%s'\", g.NavWindow ? g.NavWindow->Name : \"NULL\");\n        Text(\"NavId: 0x%08X, NavLayer: %d\", g.NavId, g.NavLayer);\n        DebugLocateItemOnHover(g.NavId);\n        Text(\"NavInputSource: %s\", GetInputSourceName(g.NavInputSource));\n        Text(\"NavLastValidSelectionUserData = %\" IM_PRId64 \" (0x%\" IM_PRIX64 \")\", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData);\n        Text(\"NavActive: %d, NavVisible: %d\", g.IO.NavActive, g.IO.NavVisible);\n        Text(\"NavActivateId/DownId/PressedId: %08X/%08X/%08X\", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);\n        Text(\"NavActivateFlags: %04X\", g.NavActivateFlags);\n        Text(\"NavCursorVisible: %d, NavHighlightItemUnderNav: %d\", g.NavCursorVisible, g.NavHighlightItemUnderNav);\n        Text(\"NavFocusScopeId = 0x%08X\", g.NavFocusScopeId);\n        Text(\"NavFocusRoute[] = \");\n        for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--)\n        {\n            const ImGuiFocusScopeData& focus_scope = g.NavFocusRoute[path_n];\n            SameLine(0.0f, 0.0f);\n            Text(\"0x%08X/\", focus_scope.ID);\n            SetItemTooltip(\"In window \\\"%s\\\"\", FindWindowByID(focus_scope.WindowID)->Name);\n        }\n        Text(\"NavWindowingTarget: '%s'\", g.NavWindowingTarget ? g.NavWindowingTarget->Name : \"NULL\");\n        Unindent();\n\n        TreePop();\n    }\n\n    // Overlay: Display windows Rectangles and Begin Order\n    if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)\n    {\n        for (ImGuiWindow* window : g.Windows)\n        {\n            if (!window->WasActive)\n                continue;\n            ImDrawList* draw_list = GetForegroundDrawList(window);\n            if (cfg->ShowWindowsRects)\n            {\n                ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);\n                draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));\n            }\n            if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))\n            {\n                char buf[32];\n                ImFormatString(buf, IM_COUNTOF(buf), \"%d\", window->BeginOrderWithinContext);\n                float font_size = GetFontSize();\n                draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));\n                draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);\n            }\n        }\n    }\n\n    // Overlay: Display Tables Rectangles\n    if (cfg->ShowTablesRects)\n    {\n        for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)\n        {\n            ImGuiTable* table = g.Tables.TryGetMapData(table_n);\n            if (table == NULL || table->LastFrameActive < g.FrameCount - 1)\n                continue;\n            ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);\n            if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)\n            {\n                for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n                {\n                    ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);\n                    ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);\n                    float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;\n                    draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);\n                }\n            }\n            else\n            {\n                ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);\n                draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));\n            }\n        }\n    }\n\n#ifdef IMGUI_HAS_DOCK\n    // Overlay: Display Docking info\n    if (cfg->ShowDockingNodes && g.IO.KeyCtrl && g.DebugHoveredDockNode)\n    {\n        char buf[64] = \"\";\n        char* p = buf;\n        ImGuiDockNode* node = g.DebugHoveredDockNode;\n        ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());\n        p += ImFormatString(p, buf + IM_COUNTOF(buf) - p, \"DockId: %X%s\\n\", node->ID, node->IsCentralNode() ? \" *CentralNode*\" : \"\");\n        p += ImFormatString(p, buf + IM_COUNTOF(buf) - p, \"WindowClass: %08X\\n\", node->WindowClass.ClassId);\n        p += ImFormatString(p, buf + IM_COUNTOF(buf) - p, \"Size: (%.0f, %.0f)\\n\", node->Size.x, node->Size.y);\n        p += ImFormatString(p, buf + IM_COUNTOF(buf) - p, \"SizeRef: (%.0f, %.0f)\\n\", node->SizeRef.x, node->SizeRef.y);\n        int depth = DockNodeGetDepth(node);\n        overlay_draw_list->AddRect(node->Pos + ImVec2(3, 3) * (float)depth, node->Pos + node->Size - ImVec2(3, 3) * (float)depth, IM_COL32(200, 100, 100, 255));\n        ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth;\n        overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255));\n        overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf);\n    }\n#endif // #ifdef IMGUI_HAS_DOCK\n\n    End();\n}\n\nvoid ImGui::DebugBreakClearData()\n{\n    // Those fields are scattered in their respective subsystem to stay in hot-data locations\n    ImGuiContext& g = *GImGui;\n    g.DebugBreakInWindow = 0;\n    g.DebugBreakInTable = 0;\n    g.DebugBreakInShortcutRouting = ImGuiKey_None;\n}\n\nvoid ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location)\n{\n    if (!BeginItemTooltip())\n        return;\n    Text(\"To call IM_DEBUG_BREAK() %s:\", description_of_location);\n    Separator();\n    TextUnformatted(keyboard_only ? \"- Press 'Pause/Break' on keyboard.\" : \"- Press 'Pause/Break' on keyboard.\\n- or Click (may alter focus/active id).\\n- or navigate using keyboard and press space.\");\n    Separator();\n    TextUnformatted(\"Choose one way that doesn't interfere with what you are trying to debug!\\nYou need a debugger attached or this will crash!\");\n    EndTooltip();\n}\n\n// Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.\n// In order to reduce interferences with the contents we are trying to debug into.\nbool ImGui::DebugBreakButton(const char* label, const char* description_of_location)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiID id = window->GetID(label);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset);\n    ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y);\n\n    const ImRect bb(pos, pos + size);\n    ItemSize(size, 0.0f);\n    if (!ItemAdd(bb, id))\n        return false;\n\n    // WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.\n    bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags);\n    bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id);\n    DebugBreakButtonTooltip(false, description_of_location);\n\n    ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n    ImVec4 hsv;\n    ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);\n    ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);\n\n    RenderNavCursor(bb, id);\n    RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);\n    RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n    return pressed;\n}\n\n// [DEBUG] Display contents of Columns\nvoid ImGui::DebugNodeColumns(ImGuiOldColumns* columns)\n{\n    if (!TreeNode((void*)(uintptr_t)columns->ID, \"Columns Id: 0x%08X, Count: %d, Flags: 0x%04X\", columns->ID, columns->Count, columns->Flags))\n        return;\n    BulletText(\"Width: %.1f (MinX: %.1f, MaxX: %.1f)\", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);\n    for (ImGuiOldColumnData& column : columns->Columns)\n        BulletText(\"Column %02d: OffsetNorm %.3f (= %.1f px)\", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm));\n    TreePop();\n}\n\nstatic void DebugNodeDockNodeFlags(ImGuiDockNodeFlags* p_flags, const char* label, bool enabled)\n{\n    using namespace ImGui;\n    PushID(label);\n    PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));\n    Text(\"%s:\", label);\n    if (!enabled)\n        BeginDisabled();\n    CheckboxFlags(\"NoResize\", p_flags, ImGuiDockNodeFlags_NoResize);\n    CheckboxFlags(\"NoResizeX\", p_flags, ImGuiDockNodeFlags_NoResizeX);\n    CheckboxFlags(\"NoResizeY\",p_flags, ImGuiDockNodeFlags_NoResizeY);\n    CheckboxFlags(\"NoTabBar\", p_flags, ImGuiDockNodeFlags_NoTabBar);\n    CheckboxFlags(\"HiddenTabBar\", p_flags, ImGuiDockNodeFlags_HiddenTabBar);\n    CheckboxFlags(\"NoWindowMenuButton\", p_flags, ImGuiDockNodeFlags_NoWindowMenuButton);\n    CheckboxFlags(\"NoCloseButton\", p_flags, ImGuiDockNodeFlags_NoCloseButton);\n    CheckboxFlags(\"DockedWindowsInFocusRoute\", p_flags, ImGuiDockNodeFlags_DockedWindowsInFocusRoute);\n    CheckboxFlags(\"NoDocking\", p_flags, ImGuiDockNodeFlags_NoDocking); // Multiple flags\n    CheckboxFlags(\"NoDockingSplit\", p_flags, ImGuiDockNodeFlags_NoDockingSplit);\n    CheckboxFlags(\"NoDockingSplitOther\", p_flags, ImGuiDockNodeFlags_NoDockingSplitOther);\n    CheckboxFlags(\"NoDockingOver\", p_flags, ImGuiDockNodeFlags_NoDockingOverMe);\n    CheckboxFlags(\"NoDockingOverOther\", p_flags, ImGuiDockNodeFlags_NoDockingOverOther);\n    CheckboxFlags(\"NoDockingOverEmpty\", p_flags, ImGuiDockNodeFlags_NoDockingOverEmpty);\n    CheckboxFlags(\"NoUndocking\", p_flags, ImGuiDockNodeFlags_NoUndocking);\n    if (!enabled)\n        EndDisabled();\n    PopStyleVar();\n    PopID();\n}\n\n// [DEBUG] Display contents of ImDockNode\nvoid ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label)\n{\n    ImGuiContext& g = *GImGui;\n    const bool is_alive = (g.FrameCount - node->LastFrameAlive < 2);    // Submitted with ImGuiDockNodeFlags_KeepAliveOnly\n    const bool is_active = (g.FrameCount - node->LastFrameActive < 2);  // Submitted\n    if (!is_alive) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }\n    bool open;\n    ImGuiTreeNodeFlags tree_node_flags = node->IsFocused ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;\n    if (node->Windows.Size > 0)\n        open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, \"%s 0x%04X%s: %d windows (vis: '%s')\", label, node->ID, node->IsVisible ? \"\" : \" (hidden)\", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : \"NULL\");\n    else\n        open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, \"%s 0x%04X%s: %s (vis: '%s')\", label, node->ID, node->IsVisible ? \"\" : \" (hidden)\", (node->SplitAxis == ImGuiAxis_X) ? \"horizontal split\" : (node->SplitAxis == ImGuiAxis_Y) ? \"vertical split\" : \"empty\", node->VisibleWindow ? node->VisibleWindow->Name : \"NULL\");\n    if (!is_alive) { PopStyleColor(); }\n    if (is_active && IsItemHovered())\n        if (ImGuiWindow* window = node->HostWindow ? node->HostWindow : node->VisibleWindow)\n            GetForegroundDrawList(window)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255));\n    if (open)\n    {\n        IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node);\n        IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node);\n        BulletText(\"Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)\",\n            node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y);\n        DebugNodeWindow(node->HostWindow, \"HostWindow\");\n        DebugNodeWindow(node->VisibleWindow, \"VisibleWindow\");\n        BulletText(\"SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X\", node->SelectedTabId, node->LastFocusedNodeId);\n        BulletText(\"Misc:%s%s%s%s%s%s%s\",\n            node->IsDockSpace() ? \" IsDockSpace\" : \"\",\n            node->IsCentralNode() ? \" IsCentralNode\" : \"\",\n            is_alive ? \" IsAlive\" : \"\", is_active ? \" IsActive\" : \"\", node->IsFocused ? \" IsFocused\" : \"\",\n            node->WantLockSizeOnce ? \" WantLockSizeOnce\" : \"\",\n            node->HasCentralNodeChild ? \" HasCentralNodeChild\" : \"\");\n        if (TreeNode(\"flags\", \"Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X\", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags))\n        {\n            if (BeginTable(\"flags\", 4))\n            {\n                TableNextColumn(); DebugNodeDockNodeFlags(&node->MergedFlags, \"MergedFlags\", false);\n                TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlags, \"LocalFlags\", true);\n                TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlagsInWindows, \"LocalFlagsInWindows\", false);\n                TableNextColumn(); DebugNodeDockNodeFlags(&node->SharedFlags, \"SharedFlags\", true);\n                EndTable();\n            }\n            TreePop();\n        }\n        if (node->ParentNode)\n            DebugNodeDockNode(node->ParentNode, \"ParentNode\");\n        if (node->ChildNodes[0])\n            DebugNodeDockNode(node->ChildNodes[0], \"Child[0]\");\n        if (node->ChildNodes[1])\n            DebugNodeDockNode(node->ChildNodes[1], \"Child[1]\");\n        if (node->TabBar)\n            DebugNodeTabBar(node->TabBar, \"TabBar\");\n        DebugNodeWindowsList(&node->Windows, \"Windows\");\n\n        TreePop();\n    }\n}\n\n// [DEBUG] Display contents of ImDrawList\n// Note that both 'window' and 'viewport' may be NULL here. Viewport is generally null of destroyed popups which previously owned a viewport.\nvoid ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;\n    int cmd_count = draw_list->CmdBuffer.Size;\n    if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)\n        cmd_count--;\n    bool node_open = TreeNode(draw_list, \"%s: '%s' %d vtx, %d indices, %d cmds\", label, draw_list->_OwnerName ? draw_list->_OwnerName : \"\", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, cmd_count);\n    if (draw_list == GetWindowDrawList())\n    {\n        SameLine();\n        TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), \"CURRENTLY APPENDING\"); // Can't display stats for active draw list! (we don't have the data double-buffered)\n        if (node_open)\n            TreePop();\n        return;\n    }\n\n    ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list\n    if (window && IsItemHovered() && fg_draw_list)\n        fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));\n    if (!node_open)\n        return;\n\n    if (window && !window->WasActive)\n        TextDisabled(\"Warning: owning Window is inactive. This DrawList is not being rendered!\");\n\n    for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)\n    {\n        if (pcmd->UserCallback)\n        {\n            BulletText(\"Callback %p, user_data %p\", pcmd->UserCallback, pcmd->UserCallbackData);\n            continue;\n        }\n\n        char texid_desc[30];\n        FormatTextureRefForDebugDisplay(texid_desc, IM_COUNTOF(texid_desc), pcmd->TexRef);\n        char buf[300];\n        ImFormatString(buf, IM_COUNTOF(buf), \"DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)\",\n            pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);\n        bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), \"%s\", buf);\n        if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)\n            DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);\n        if (!pcmd_node_open)\n            continue;\n\n        // Calculate approximate coverage area (touched pixel count)\n        // This will be in pixels squared as long there's no post-scaling happening to the renderer output.\n        const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;\n        const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;\n        float total_area = 0.0f;\n        for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )\n        {\n            ImVec2 triangle[3];\n            for (int n = 0; n < 3; n++, idx_n++)\n                triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;\n            total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);\n        }\n\n        // Display vertex information summary. Hover to get all triangles drawn in wire-frame\n        ImFormatString(buf, IM_COUNTOF(buf), \"Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px\", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);\n        Selectable(buf);\n        if (IsItemHovered() && fg_draw_list)\n            DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);\n\n        // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.\n        ImGuiListClipper clipper;\n        clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.\n        while (clipper.Step())\n            for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)\n            {\n                char* buf_p = buf, * buf_end = buf + IM_COUNTOF(buf);\n                ImVec2 triangle[3];\n                for (int n = 0; n < 3; n++, idx_i++)\n                {\n                    const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];\n                    triangle[n] = v.pos;\n                    buf_p += ImFormatString(buf_p, buf_end - buf_p, \"%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\\n\",\n                        (n == 0) ? \"Vert:\" : \"     \", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);\n                }\n\n                Selectable(buf, false);\n                if (fg_draw_list && IsItemHovered())\n                {\n                    ImDrawListFlags backup_flags = fg_draw_list->Flags;\n                    fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.\n                    fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);\n                    fg_draw_list->Flags = backup_flags;\n                }\n            }\n        TreePop();\n    }\n    TreePop();\n}\n\n// [DEBUG] Display mesh/aabb of a ImDrawCmd\nvoid ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)\n{\n    IM_ASSERT(show_mesh || show_aabb);\n\n    // Draw wire-frame version of all triangles\n    ImRect clip_rect = draw_cmd->ClipRect;\n    ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);\n    ImDrawListFlags backup_flags = out_draw_list->Flags;\n    out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.\n    for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )\n    {\n        ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list\n        ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;\n\n        ImVec2 triangle[3];\n        for (int n = 0; n < 3; n++, idx_n++)\n            vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));\n        if (show_mesh)\n            out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles\n    }\n    // Draw bounding boxes\n    if (show_aabb)\n    {\n        out_draw_list->AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU\n        out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles\n    }\n    out_draw_list->Flags = backup_flags;\n}\n\n// [DEBUG] Compute mask of inputs with the same codepoint.\nstatic int CalcFontGlyphSrcOverlapMask(ImFontAtlas* atlas, ImFont* font, unsigned int codepoint)\n{\n    int mask = 0, count = 0;\n    for (int src_n = 0; src_n < font->Sources.Size; src_n++)\n    {\n        ImFontConfig* src = font->Sources[src_n];\n        if (!(src->FontLoader ? src->FontLoader : atlas->FontLoader)->FontSrcContainsGlyph(atlas, src, (ImWchar)codepoint))\n            continue;\n        mask |= (1 << src_n);\n        count++;\n    }\n    return count > 1 ? mask : 0;\n}\n\n// [DEBUG] Display details for a single font, called by ShowStyleEditor().\nvoid ImGui::DebugNodeFont(ImFont* font)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;\n    ImFontAtlas* atlas = font->OwnerAtlas;\n    bool opened = TreeNode(font, \"Font: \\\"%s\\\": %d sources(s)\", font->GetDebugName(), font->Sources.Size);\n\n    // Display preview text\n    if (!opened)\n        Indent();\n    Indent();\n    if (cfg->ShowFontPreview)\n    {\n        PushFont(font, 0.0f);\n        Text(\"The quick brown fox jumps over the lazy dog\");\n        PopFont();\n    }\n    if (!opened)\n    {\n        Unindent();\n        Unindent();\n        return;\n    }\n    if (SmallButton(\"Set as default\"))\n        GetIO().FontDefault = font;\n    SameLine();\n    BeginDisabled(atlas->Fonts.Size <= 1 || atlas->Locked);\n    if (SmallButton(\"Remove\"))\n        atlas->RemoveFont(font);\n    EndDisabled();\n    SameLine();\n    if (SmallButton(\"Clear bakes\"))\n        ImFontAtlasFontDiscardBakes(atlas, font, 0);\n    SameLine();\n    if (SmallButton(\"Clear unused\"))\n        ImFontAtlasFontDiscardBakes(atlas, font, 2);\n\n    // Display details\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    SetNextItemWidth(GetFontSize() * 8);\n    DragFloat(\"Font scale\", &font->Scale, 0.005f, 0.3f, 2.0f, \"%.1f\");\n    /*SameLine(); MetricsHelpMarker(\n        \"Note that the default embedded font is NOT meant to be scaled.\\n\\n\"\n        \"Font are currently rendered into bitmaps at a given size at the time of building the atlas. \"\n        \"You may oversample them to get some flexibility with scaling. \"\n        \"You can also render at multiple sizes and select which one to use at runtime.\\n\\n\"\n        \"(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)\");*/\n#endif\n\n    char c_str[5];\n    ImTextCharToUtf8(c_str, font->FallbackChar);\n    Text(\"Fallback character: '%s' (U+%04X)\", c_str, font->FallbackChar);\n    ImTextCharToUtf8(c_str, font->EllipsisChar);\n    Text(\"Ellipsis character: '%s' (U+%04X)\", c_str, font->EllipsisChar);\n\n    for (int src_n = 0; src_n < font->Sources.Size; src_n++)\n    {\n        ImFontConfig* src = font->Sources[src_n];\n        if (TreeNode(src, \"Input %d: \\'%s\\' [%d], Oversample: %d,%d, PixelSnapH: %d, Offset: (%.1f,%.1f)\",\n            src_n, src->Name, src->FontNo, src->OversampleH, src->OversampleV, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y))\n        {\n            const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;\n            Text(\"Loader: '%s'\", loader->Name ? loader->Name : \"N/A\");\n\n            //if (DragFloat(\"ExtraSizeScale\", &src->ExtraSizeScale, 0.01f, 0.10f, 2.0f))\n            //    ImFontAtlasFontRebuildOutput(atlas, font);\n#ifdef IMGUI_ENABLE_FREETYPE\n            if (loader->Name != NULL && strcmp(loader->Name, \"FreeType\") == 0)\n            {\n                unsigned int loader_flags = src->FontLoaderFlags;\n                Text(\"FreeType Loader Flags: 0x%08X\", loader_flags);\n                if (ImGuiFreeType::DebugEditFontLoaderFlags(&loader_flags))\n                {\n                    ImFontAtlasFontDestroyOutput(atlas, font);\n                    src->FontLoaderFlags = loader_flags;\n                    ImFontAtlasFontInitOutput(atlas, font);\n                }\n            }\n#endif\n            TreePop();\n        }\n    }\n    if (font->Sources.Size > 1 && TreeNode(\"Input Glyphs Overlap Detection Tool\"))\n    {\n        TextWrapped(\"- First Input that contains the glyph is used.\\n\"\n            \"- Use ImFontConfig::GlyphExcludeRanges[] to specify ranges to ignore glyph in given Input.\\n- Prefer using a small number of ranges as the list is scanned every time a new glyph is loaded,\\n  - e.g. GlyphExcludeRanges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };\\n- This tool doesn't cache results and is slow, don't keep it open!\");\n        if (BeginTable(\"table\", 2))\n        {\n            for (unsigned int c = 0; c < 0x10000; c++)\n                if (int overlap_mask = CalcFontGlyphSrcOverlapMask(atlas, font, c))\n                {\n                    unsigned int c_end = c + 1;\n                    while (c_end < 0x10000 && CalcFontGlyphSrcOverlapMask(atlas, font, c_end) == overlap_mask)\n                        c_end++;\n                    if (TableNextColumn() && TreeNode((void*)(intptr_t)c, \"U+%04X-U+%04X: %d codepoints in %d inputs\", c, c_end - 1, c_end - c, ImCountSetBits(overlap_mask)))\n                    {\n                        char utf8_buf[5];\n                        for (unsigned int n = c; n < c_end; n++)\n                        {\n                            ImTextCharToUtf8(utf8_buf, n);\n                            BulletText(\"Codepoint U+%04X (%s)\", n, utf8_buf);\n                        }\n                        TreePop();\n                    }\n                    TableNextColumn();\n                    for (int src_n = 0; src_n < font->Sources.Size; src_n++)\n                        if (overlap_mask & (1 << src_n))\n                        {\n                            Text(\"%d \", src_n);\n                            SameLine();\n                        }\n                    c = c_end - 1;\n                }\n            EndTable();\n        }\n        TreePop();\n    }\n\n    // Display all glyphs of the fonts in separate pages of 256 characters\n    for (int baked_n = 0; baked_n < atlas->Builder->BakedPool.Size; baked_n++)\n    {\n        ImFontBaked* baked = &atlas->Builder->BakedPool[baked_n];\n        if (baked->OwnerFont != font)\n            continue;\n        PushID(baked->BakedId);\n        if (TreeNode(\"Glyphs\", \"Baked at { %.2fpx, d.%.2f }: %d glyphs%s\", baked->Size, baked->RasterizerDensity, baked->Glyphs.Size, (baked->LastUsedFrame < atlas->Builder->FrameCount - 1) ? \" *Unused*\" : \"\"))\n        {\n            if (SmallButton(\"Load all\"))\n                for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base++)\n                    baked->FindGlyph((ImWchar)base);\n\n            const int surface_sqrt = (int)ImSqrt((float)baked->MetricsTotalSurface);\n            Text(\"Ascent: %f, Descent: %f, Ascent-Descent: %f\", baked->Ascent, baked->Descent, baked->Ascent - baked->Descent);\n            Text(\"Texture Area: about %d px ~%dx%d px\", baked->MetricsTotalSurface, surface_sqrt, surface_sqrt);\n            for (int src_n = 0; src_n < font->Sources.Size; src_n++)\n            {\n                ImFontConfig* src = font->Sources[src_n];\n                int oversample_h, oversample_v;\n                ImFontAtlasBuildGetOversampleFactors(src, baked, &oversample_h, &oversample_v);\n                BulletText(\"Input %d: \\'%s\\', Oversample: (%d=>%d,%d=>%d), PixelSnapH: %d, Offset: (%.1f,%.1f)\",\n                    src_n, src->Name, src->OversampleH, oversample_h, src->OversampleV, oversample_v, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y);\n            }\n\n            DebugNodeFontGlyphsForSrcMask(font, baked, ~0);\n            TreePop();\n        }\n        PopID();\n    }\n    TreePop();\n    Unindent();\n}\n\nvoid ImGui::DebugNodeFontGlyphsForSrcMask(ImFont* font, ImFontBaked* baked, int src_mask)\n{\n    ImDrawList* draw_list = GetWindowDrawList();\n    const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);\n    const float cell_size = baked->Size * 1;\n    const float cell_spacing = GetStyle().ItemSpacing.y;\n    for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)\n    {\n        // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)\n        // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT\n        // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)\n        if (!(base & 8191) && font->IsGlyphRangeUnused(base, base + 8191))\n        {\n            base += 8192 - 256;\n            continue;\n        }\n\n        int count = 0;\n        for (unsigned int n = 0; n < 256; n++)\n            if (const ImFontGlyph* glyph = baked->IsGlyphLoaded((ImWchar)(base + n)) ? baked->FindGlyph((ImWchar)(base + n)) : NULL)\n                if (src_mask & (1 << glyph->SourceIdx))\n                    count++;\n        if (count <= 0)\n            continue;\n        if (!TreeNode((void*)(intptr_t)base, \"U+%04X..U+%04X (%d %s)\", base, base + 255, count, count > 1 ? \"glyphs\" : \"glyph\"))\n            continue;\n\n        // Draw a 16x16 grid of glyphs\n        ImVec2 base_pos = GetCursorScreenPos();\n        for (unsigned int n = 0; n < 256; n++)\n        {\n            // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions\n            // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.\n            ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));\n            ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);\n            const ImFontGlyph* glyph = baked->IsGlyphLoaded((ImWchar)(base + n)) ? baked->FindGlyph((ImWchar)(base + n)) : NULL;\n            draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));\n            if (!glyph || (src_mask & (1 << glyph->SourceIdx)) == 0)\n                continue;\n            font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));\n            if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip())\n            {\n                DebugNodeFontGlyph(font, glyph);\n                EndTooltip();\n            }\n        }\n        Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));\n        TreePop();\n    }\n}\n\nvoid ImGui::DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph)\n{\n    Text(\"Codepoint: U+%04X\", glyph->Codepoint);\n    Separator();\n    Text(\"Visible: %d\", glyph->Visible);\n    Text(\"AdvanceX: %.1f\", glyph->AdvanceX);\n    Text(\"Pos: (%.2f,%.2f)->(%.2f,%.2f)\", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);\n    Text(\"UV: (%.3f,%.3f)->(%.3f,%.3f)\", glyph->U0, glyph->V0, glyph->U1, glyph->V1);\n    if (glyph->PackId >= 0)\n    {\n        ImTextureRect* r = ImFontAtlasPackGetRect(font->OwnerAtlas, glyph->PackId);\n        Text(\"PackId: 0x%X (%dx%d rect at %d,%d)\", glyph->PackId, r->w, r->h, r->x, r->y);\n    }\n    Text(\"SourceIdx: %d\", glyph->SourceIdx);\n}\n\n// [DEBUG] Display contents of ImGuiStorage\nvoid ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)\n{\n    if (!TreeNode(label, \"%s: %d entries, %d bytes\", label, storage->Data.Size, storage->Data.size_in_bytes()))\n        return;\n    for (const ImGuiStoragePair& p : storage->Data)\n    {\n        BulletText(\"Key 0x%08X Value { i: %d }\", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer.\n        DebugLocateItemOnHover(p.key);\n    }\n    TreePop();\n}\n\n// [DEBUG] Display contents of ImGuiTabBar\nvoid ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)\n{\n    // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.\n    char buf[256];\n    char* p = buf;\n    const char* buf_end = buf + IM_COUNTOF(buf);\n    const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);\n    p += ImFormatString(p, buf_end - p, \"%s 0x%08X (%d tabs)%s  {\", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? \"\" : \" *Inactive*\");\n    for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)\n    {\n        ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];\n        p += ImFormatString(p, buf_end - p, \"%s'%s'\", tab_n > 0 ? \", \" : \"\", TabBarGetTabName(tab_bar, tab));\n    }\n    p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? \" ... }\" : \" } \");\n    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }\n    bool open = TreeNode(label, \"%s\", buf);\n    if (!is_active) { PopStyleColor(); }\n    if (is_active && IsItemHovered())\n    {\n        ImDrawList* draw_list = GetForegroundDrawList(tab_bar->Window);\n        draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));\n        draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));\n        draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));\n    }\n    if (open)\n    {\n        for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)\n        {\n            ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];\n            PushID(tab);\n            if (SmallButton(\"<\")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);\n            if (SmallButton(\">\")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();\n            Text(\"%02d%c Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f\",\n                tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, TabBarGetTabName(tab_bar, tab), tab->Offset, tab->Width, tab->ContentWidth);\n            PopID();\n        }\n        TreePop();\n    }\n}\n\nvoid ImGui::DebugNodeViewport(ImGuiViewportP* viewport)\n{\n    ImGuiContext& g = *GImGui;\n    SetNextItemOpen(true, ImGuiCond_Once);\n    bool open = TreeNode((void*)(intptr_t)viewport->ID, \"Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \\\"%s\\\"\", viewport->Idx, viewport->ID, viewport->ParentViewportId, viewport->Window ? viewport->Window->Name : \"N/A\");\n    if (IsItemHovered())\n        g.DebugMetricsConfig.HighlightViewportID = viewport->ID;\n    if (open)\n    {\n        ImGuiWindowFlags flags = viewport->Flags;\n        BulletText(\"Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\\nFrameBufferScale: (%.2f,%.2f)\\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\\nMonitor: %d, DpiScale: %.0f%%\",\n            viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,\n            viewport->FramebufferScale.x, viewport->FramebufferScale.y,\n            viewport->WorkInsetMin.x, viewport->WorkInsetMin.y, viewport->WorkInsetMax.x, viewport->WorkInsetMax.y,\n            viewport->PlatformMonitor, viewport->DpiScale * 100.0f);\n        if (viewport->Idx > 0) { SameLine(); if (SmallButton(\"Reset Pos\")) { viewport->Pos = ImVec2(200, 200); viewport->UpdateWorkRect(); if (viewport->Window) viewport->Window->Pos = viewport->Pos; } }\n        BulletText(\"Flags: 0x%04X =%s%s%s%s%s%s%s%s%s%s%s%s%s\", viewport->Flags,\n            //(flags & ImGuiViewportFlags_IsPlatformWindow) ? \" IsPlatformWindow\" : \"\", // Omitting because it is the standard\n            (flags & ImGuiViewportFlags_IsPlatformMonitor) ? \" IsPlatformMonitor\" : \"\",\n            (flags & ImGuiViewportFlags_IsMinimized) ? \" IsMinimized\" : \"\",\n            (flags & ImGuiViewportFlags_IsFocused) ? \" IsFocused\" : \"\",\n            (flags & ImGuiViewportFlags_OwnedByApp) ? \" OwnedByApp\" : \"\",\n            (flags & ImGuiViewportFlags_NoDecoration) ? \" NoDecoration\" : \"\",\n            (flags & ImGuiViewportFlags_NoTaskBarIcon) ? \" NoTaskBarIcon\" : \"\",\n            (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? \" NoFocusOnAppearing\" : \"\",\n            (flags & ImGuiViewportFlags_NoFocusOnClick) ? \" NoFocusOnClick\" : \"\",\n            (flags & ImGuiViewportFlags_NoInputs) ? \" NoInputs\" : \"\",\n            (flags & ImGuiViewportFlags_NoRendererClear) ? \" NoRendererClear\" : \"\",\n            (flags & ImGuiViewportFlags_NoAutoMerge) ? \" NoAutoMerge\" : \"\",\n            (flags & ImGuiViewportFlags_TopMost) ? \" TopMost\" : \"\",\n            (flags & ImGuiViewportFlags_CanHostOtherWindows) ? \" CanHostOtherWindows\" : \"\");\n        for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)\n            DebugNodeDrawList(NULL, viewport, draw_list, \"DrawList\");\n        TreePop();\n    }\n}\n\nvoid ImGui::DebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor, const char* label, int idx)\n{\n    BulletText(\"%s %d: DPI %.0f%%\\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)\",\n        label, idx, monitor->DpiScale * 100.0f,\n        monitor->MainPos.x, monitor->MainPos.y, monitor->MainPos.x + monitor->MainSize.x, monitor->MainPos.y + monitor->MainSize.y, monitor->MainSize.x, monitor->MainSize.y,\n        monitor->WorkPos.x, monitor->WorkPos.y, monitor->WorkPos.x + monitor->WorkSize.x, monitor->WorkPos.y + monitor->WorkSize.y, monitor->WorkSize.x, monitor->WorkSize.y);\n}\n\nvoid ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)\n{\n    if (window == NULL)\n    {\n        BulletText(\"%s: NULL\", label);\n        return;\n    }\n\n    ImGuiContext& g = *GImGui;\n    const bool is_active = window->WasActive;\n    ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;\n    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }\n    const bool open = TreeNodeEx(label, tree_node_flags, \"%s '%s'%s\", label, window->Name, is_active ? \"\" : \" *Inactive*\");\n    if (!is_active) { PopStyleColor(); }\n    if (IsItemHovered() && is_active)\n        GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));\n    if (!open)\n        return;\n\n    if (window->MemoryCompacted)\n        TextDisabled(\"Note: some memory buffers have been compacted/freed.\");\n\n    if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton(\"**DebugBreak**\", \"in Begin()\"))\n        g.DebugBreakInWindow = window->ID;\n\n    ImGuiWindowFlags flags = window->Flags;\n    DebugNodeDrawList(window, window->Viewport, window->DrawList, \"DrawList\");\n    BulletText(\"Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)\", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);\n    BulletText(\"Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)\", flags,\n        (flags & ImGuiWindowFlags_ChildWindow)  ? \"Child \" : \"\",      (flags & ImGuiWindowFlags_Tooltip)     ? \"Tooltip \"   : \"\",  (flags & ImGuiWindowFlags_Popup) ? \"Popup \" : \"\",\n        (flags & ImGuiWindowFlags_Modal)        ? \"Modal \" : \"\",      (flags & ImGuiWindowFlags_ChildMenu)   ? \"ChildMenu \" : \"\",  (flags & ImGuiWindowFlags_NoSavedSettings) ? \"NoSavedSettings \" : \"\",\n        (flags & ImGuiWindowFlags_NoMouseInputs)? \"NoMouseInputs\":\"\", (flags & ImGuiWindowFlags_NoNavInputs) ? \"NoNavInputs\" : \"\", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? \"AlwaysAutoResize\" : \"\");\n    if (flags & ImGuiWindowFlags_ChildWindow)\n        BulletText(\"ChildFlags: 0x%08X (%s%s%s%s..)\", window->ChildFlags,\n            (window->ChildFlags & ImGuiChildFlags_Borders) ? \"Borders \" : \"\",\n            (window->ChildFlags & ImGuiChildFlags_ResizeX) ? \"ResizeX \" : \"\",\n            (window->ChildFlags & ImGuiChildFlags_ResizeY) ? \"ResizeY \" : \"\",\n            (window->ChildFlags & ImGuiChildFlags_NavFlattened) ? \"NavFlattened \" : \"\");\n    BulletText(\"WindowClassId: 0x%08X\", window->WindowClass.ClassId);\n    BulletText(\"Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s\", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? \"X\" : \"\", window->ScrollbarY ? \"Y\" : \"\");\n    BulletText(\"Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d\", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);\n    BulletText(\"Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d\", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);\n    for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)\n    {\n        ImRect r = window->NavRectRel[layer];\n        if (r.Min.x >= r.Max.x && r.Min.y >= r.Max.y)\n            BulletText(\"NavLastIds[%d]: 0x%08X\", layer, window->NavLastIds[layer]);\n        else\n            BulletText(\"NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)\", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y);\n        DebugLocateItemOnHover(window->NavLastIds[layer]);\n    }\n    const ImVec2* pr = window->NavPreferredScoringPosRel;\n    for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)\n        BulletText(\"NavPreferredScoringPosRel[%d] = (%.1f,%.1f)\", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater.\n    BulletText(\"NavLayersActiveMask: %X, NavLastChildNavWindow: %s\", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : \"NULL\");\n\n    BulletText(\"Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)\", window->Viewport ? window->Viewport->Idx : -1, window->ViewportOwned ? \" (Owned)\" : \"\", window->ViewportId, window->ViewportPos.x, window->ViewportPos.y);\n    BulletText(\"ViewportMonitor: %d\", window->Viewport ? window->Viewport->PlatformMonitor : -1);\n    BulletText(\"DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d\", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible);\n    if (window->DockNode || window->DockNodeAsHost)\n        DebugNodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? \"DockNodeAsHost\" : \"DockNode\");\n\n    if (window->RootWindow != window)               { DebugNodeWindow(window->RootWindow, \"RootWindow\"); }\n    if (window->RootWindowDockTree != window->RootWindow) { DebugNodeWindow(window->RootWindowDockTree, \"RootWindowDockTree\"); }\n    if (window->ParentWindow != NULL)               { DebugNodeWindow(window->ParentWindow, \"ParentWindow\"); }\n    if (window->ParentWindowForFocusRoute != NULL)  { DebugNodeWindow(window->ParentWindowForFocusRoute, \"ParentWindowForFocusRoute\"); }\n    if (window->DC.ChildWindows.Size > 0)           { DebugNodeWindowsList(&window->DC.ChildWindows, \"ChildWindows\"); }\n    if (window->ColumnsStorage.Size > 0 && TreeNode(\"Columns\", \"Columns sets (%d)\", window->ColumnsStorage.Size))\n    {\n        for (ImGuiOldColumns& columns : window->ColumnsStorage)\n            DebugNodeColumns(&columns);\n        TreePop();\n    }\n    DebugNodeStorage(&window->StateStorage, \"Storage\");\n    TreePop();\n}\n\nvoid ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)\n{\n    if (settings->WantDelete)\n        BeginDisabled();\n    Text(\"0x%08X \\\"%s\\\" Pos (%d,%d) Size (%d,%d) Collapsed=%d\",\n        settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);\n    if (settings->WantDelete)\n        EndDisabled();\n}\n\nvoid ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)\n{\n    if (!TreeNode(label, \"%s (%d)\", label, windows->Size))\n        return;\n    for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back\n    {\n        PushID((*windows)[i]);\n        DebugNodeWindow((*windows)[i], \"Window\");\n        PopID();\n    }\n    TreePop();\n}\n\n// FIXME-OPT: This is technically suboptimal, but it is simpler this way.\nvoid ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack)\n{\n    for (int i = 0; i < windows_size; i++)\n    {\n        ImGuiWindow* window = windows[i];\n        if (window->ParentWindowInBeginStack != parent_in_begin_stack)\n            continue;\n        char buf[20];\n        ImFormatString(buf, IM_COUNTOF(buf), \"[%04d] Window\", window->BeginOrderWithinContext);\n        //BulletText(\"[%04d] Window '%s'\", window->BeginOrderWithinContext, window->Name);\n        DebugNodeWindow(window, buf);\n        TreePush(buf);\n        DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window);\n        TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DEBUG LOG WINDOW\n//-----------------------------------------------------------------------------\n\nvoid ImGui::DebugLog(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    DebugLogV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::DebugLogV(const char* fmt, va_list args)\n{\n    ImGuiContext& g = *GImGui;\n    const int old_size = g.DebugLogBuf.size();\n    if (g.ContextName[0] != 0)\n        g.DebugLogBuf.appendf(\"[%s] [%05d] \", g.ContextName, g.FrameCount);\n    else\n        g.DebugLogBuf.appendf(\"[%05d] \", g.FrameCount);\n    g.DebugLogBuf.appendfv(fmt, args);\n    g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size());\n\n    const char* str = g.DebugLogBuf.begin() + old_size;\n    if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY)\n        IMGUI_DEBUG_PRINTF(\"%s\", str);\n#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)\n    if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToDebugger)\n    {\n        ::OutputDebugStringA(\"[imgui] \");\n        ::OutputDebugStringA(str);\n    }\n#endif\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    // IMGUI_TEST_ENGINE_LOG() adds a trailing \\n automatically\n    const int new_size = g.DebugLogBuf.size();\n    const bool trailing_carriage_return = (g.DebugLogBuf[new_size - 1] == '\\n');\n    if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine)\n        IMGUI_TEST_ENGINE_LOG(\"%.*s\", new_size - old_size - (trailing_carriage_return ? 1 : 0), str);\n#endif\n}\n\n// FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout.\nstatic void SameLineOrWrap(const ImVec2& size)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y);\n    if (window->WorkRect.Contains(ImRect(pos, pos + size)))\n        ImGui::SameLine();\n}\n\nstatic void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight());\n    SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.\n\n    bool highlight_errors = (flags == ImGuiDebugLogFlags_EventError && g.DebugLogSkippedErrors > 0);\n    if (highlight_errors)\n        ImGui::PushStyleColor(ImGuiCol_Text, ImLerp(g.Style.Colors[ImGuiCol_Text], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.30f));\n    if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0)\n    {\n        g.DebugLogAutoDisableFrames = 2;\n        g.DebugLogAutoDisableFlags |= flags;\n    }\n    if (highlight_errors)\n    {\n        ImGui::PopStyleColor();\n        ImGui::SetItemTooltip(\"%d past errors skipped.\", g.DebugLogSkippedErrors);\n    }\n    else\n    {\n        ImGui::SetItemTooltip(\"Hold Shift when clicking to enable for 2 frames only (useful for spammy log entries)\");\n    }\n}\n\nvoid ImGui::ShowDebugLogWindow(bool* p_open)\n{\n    ImGuiContext& g = *GImGui;\n    if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0)\n        SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver);\n    if (!Begin(\"Dear ImGui Debug Log\", p_open) || GetCurrentWindow()->BeginCount > 1)\n    {\n        End();\n        return;\n    }\n\n    ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting;\n    CheckboxFlags(\"All\", &g.DebugLogFlags, all_enable_flags);\n    SetItemTooltip(\"(except InputRouting which is spammy)\");\n\n    ShowDebugLogFlag(\"Errors\", ImGuiDebugLogFlags_EventError);\n    ShowDebugLogFlag(\"ActiveId\", ImGuiDebugLogFlags_EventActiveId);\n    ShowDebugLogFlag(\"Clipper\", ImGuiDebugLogFlags_EventClipper);\n    ShowDebugLogFlag(\"Docking\", ImGuiDebugLogFlags_EventDocking);\n    ShowDebugLogFlag(\"Focus\", ImGuiDebugLogFlags_EventFocus);\n    ShowDebugLogFlag(\"IO\", ImGuiDebugLogFlags_EventIO);\n    ShowDebugLogFlag(\"Font\", ImGuiDebugLogFlags_EventFont);\n    ShowDebugLogFlag(\"Nav\", ImGuiDebugLogFlags_EventNav);\n    ShowDebugLogFlag(\"Popup\", ImGuiDebugLogFlags_EventPopup);\n    ShowDebugLogFlag(\"Selection\", ImGuiDebugLogFlags_EventSelection);\n    ShowDebugLogFlag(\"Viewport\", ImGuiDebugLogFlags_EventViewport);\n    ShowDebugLogFlag(\"InputRouting\", ImGuiDebugLogFlags_EventInputRouting);\n\n    if (SmallButton(\"Clear\"))\n    {\n        g.DebugLogBuf.clear();\n        g.DebugLogIndex.clear();\n        g.DebugLogSkippedErrors = 0;\n    }\n    SameLine();\n    if (SmallButton(\"Copy\"))\n        SetClipboardText(g.DebugLogBuf.c_str());\n    SameLine();\n    if (SmallButton(\"Configure Outputs..\"))\n        OpenPopup(\"Outputs\");\n    if (BeginPopup(\"Outputs\"))\n    {\n        CheckboxFlags(\"OutputToTTY\", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTTY);\n        CheckboxFlags(\"OutputToDebugger\", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToDebugger);\n#ifndef IMGUI_ENABLE_TEST_ENGINE\n        BeginDisabled();\n#endif\n        CheckboxFlags(\"OutputToTestEngine\", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTestEngine);\n#ifndef IMGUI_ENABLE_TEST_ENGINE\n        EndDisabled();\n#endif\n        EndPopup();\n    }\n\n    BeginChild(\"##log\", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);\n\n    const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags;\n    g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper;\n\n    ImGuiListClipper clipper;\n    clipper.Begin(g.DebugLogIndex.size());\n    while (clipper.Step())\n        for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)\n            DebugTextUnformattedWithLocateItem(g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no), g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no));\n    g.DebugLogFlags = backup_log_flags;\n    if (GetScrollY() >= GetScrollMaxY())\n        SetScrollHereY(1.0f);\n    EndChild();\n\n    End();\n}\n\n// Display line, search for 0xXXXXXXXX identifiers and call DebugLocateItemOnHover() when hovered.\nvoid ImGui::DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end)\n{\n    TextUnformatted(line_begin, line_end);\n    if (!IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))\n        return;\n    ImGuiContext& g = *GImGui;\n    ImRect text_rect = g.LastItemData.Rect;\n    for (const char* p = line_begin; p <= line_end - 10; p++)\n    {\n        ImGuiID id = 0;\n        if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, \"%X\", &id) != 1 || ImCharIsXdigitA(p[10]))\n            continue;\n        ImVec2 p0 = CalcTextSize(line_begin, p);\n        ImVec2 p1 = CalcTextSize(p, p + 10);\n        g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y));\n        if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true))\n            DebugLocateItemOnHover(id);\n        p += 10;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)\n//-----------------------------------------------------------------------------\n\n// Draw a small cross at current CursorPos in current window's DrawList\nvoid ImGui::DebugDrawCursorPos(ImU32 col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImVec2 pos = window->DC.CursorPos;\n    window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f);\n    window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f);\n}\n\n// Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList\nvoid ImGui::DebugDrawLineExtents(ImU32 col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    float curr_x = window->DC.CursorPos.x;\n    float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);\n    float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);\n    window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f);\n    window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f);\n    window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f);\n}\n\n// Draw last item rect in ForegroundDrawList (so it is always visible)\nvoid ImGui::DebugDrawItemRect(ImU32 col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col);\n}\n\n// [DEBUG] Locate item position/rectangle given an ID.\nstatic const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255);  // Green\n\nvoid ImGui::DebugLocateItem(ImGuiID target_id)\n{\n    ImGuiContext& g = *GImGui;\n    g.DebugLocateId = target_id;\n    g.DebugLocateFrames = 2;\n    g.DebugBreakInLocateId = false;\n}\n\n// FIXME: Doesn't work over through a modal window, because they clear HoveredWindow.\nvoid ImGui::DebugLocateItemOnHover(ImGuiID target_id)\n{\n    if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup))\n        return;\n    ImGuiContext& g = *GImGui;\n    DebugLocateItem(target_id);\n    GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR);\n\n    // Can't easily use a context menu here because it will mess with focus, active id etc.\n    if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f)\n    {\n        DebugBreakButtonTooltip(false, \"in ItemAdd()\");\n        if (IsKeyChordPressed(g.DebugBreakKeyChord))\n            g.DebugBreakInLocateId = true;\n    }\n}\n\nvoid ImGui::DebugLocateItemResolveWithLastItem()\n{\n    ImGuiContext& g = *GImGui;\n\n    // [DEBUG] Debug break requested by user\n    if (g.DebugBreakInLocateId)\n        IM_DEBUG_BREAK();\n\n    ImGuiLastItemData item_data = g.LastItemData;\n    g.DebugLocateId = 0;\n    ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);\n    ImRect r = item_data.Rect;\n    r.Expand(3.0f);\n    ImVec2 p1 = g.IO.MousePos;\n    ImVec2 p2 = ImVec2((p1.x < r.Min.x) ? r.Min.x : (p1.x > r.Max.x) ? r.Max.x : p1.x, (p1.y < r.Min.y) ? r.Min.y : (p1.y > r.Max.y) ? r.Max.y : p1.y);\n    draw_list->AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR);\n    draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR);\n}\n\nvoid ImGui::DebugStartItemPicker()\n{\n    ImGuiContext& g = *GImGui;\n    g.DebugItemPickerActive = true;\n}\n\n// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.\nvoid ImGui::UpdateDebugToolItemPicker()\n{\n    ImGuiContext& g = *GImGui;\n    g.DebugItemPickerBreakId = 0;\n    if (!g.DebugItemPickerActive)\n        return;\n\n    const ImGuiID hovered_id = g.HoveredIdPreviousFrame;\n    SetMouseCursor(ImGuiMouseCursor_Hand);\n    if (IsKeyPressed(ImGuiKey_Escape))\n        g.DebugItemPickerActive = false;\n    const bool change_mapping = g.IO.KeyMods == (ImGuiMod_Ctrl | ImGuiMod_Shift);\n    if (!change_mapping && IsMouseClicked(g.DebugItemPickerMouseButton) && hovered_id)\n    {\n        g.DebugItemPickerBreakId = hovered_id;\n        g.DebugItemPickerActive = false;\n    }\n    for (int mouse_button = 0; mouse_button < 3; mouse_button++)\n        if (change_mapping && IsMouseClicked(mouse_button))\n            g.DebugItemPickerMouseButton = (ImU8)mouse_button;\n    SetNextWindowBgAlpha(0.70f);\n    if (!BeginTooltip())\n        return;\n    Text(\"HoveredId: 0x%08X\", hovered_id);\n    Text(\"Press ESC to abort picking.\");\n    const char* mouse_button_names[] = { \"Left\", \"Right\", \"Middle\" };\n    if (change_mapping)\n        Text(\"Remap w/ Ctrl+Shift: click anywhere to select new mouse button.\");\n    else\n        TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), \"Click %s Button to break in debugger! (remap w/ Ctrl+Shift)\", mouse_button_names[g.DebugItemPickerMouseButton]);\n    EndTooltip();\n}\n\n// Update queries. The steps are: -1: query Stack, >= 0: query each stack item\n// We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time\nstatic ImGuiID DebugItemPathQuery_UpdateAndGetHookId(ImGuiDebugItemPathQuery* query, ImGuiID id)\n{\n    // Update query. Clear hook when no active query\n    if (query->MainID != id)\n    {\n        query->MainID = id;\n        query->Step = -1;\n        query->Complete = false;\n        query->Results.resize(0);\n        query->ResultsDescBuf.resize(0);\n    }\n    query->Active = false;\n    if (id == 0)\n        return 0;\n\n    // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)\n    if (query->Step >= 0 && query->Step < query->Results.Size)\n        if (query->Results[query->Step].QuerySuccess || query->Results[query->Step].QueryFrameCount > 2)\n            query->Step++;\n\n    // Update status and hook\n    query->Complete = (query->Step == query->Results.Size);\n    if (query->Step == -1)\n    {\n        query->Active = true;\n        return id;\n    }\n    else if (query->Step >= 0 && query->Step < query->Results.Size)\n    {\n        query->Results[query->Step].QueryFrameCount++;\n        query->Active = true;\n        return query->Results[query->Step].ID;\n    }\n    return 0;\n}\n\n// [DEBUG] ID Stack Tool: update query. Called by NewFrame()\nvoid ImGui::UpdateDebugToolItemPathQuery()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiID id = 0;\n    if (g.DebugIDStackTool.LastActiveFrame + 1 == g.FrameCount)\n        id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId;\n    g.DebugHookIdInfoId = DebugItemPathQuery_UpdateAndGetHookId(&g.DebugItemPathQuery, id);\n}\n\n// [DEBUG] ID Stack tool: hooks called by GetID() family functions\nvoid ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiDebugItemPathQuery* query = &g.DebugItemPathQuery;\n    if (query->Active == false)\n    {\n        IM_ASSERT(id == 0);\n        return;\n    }\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Step -1: stack query\n    // This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.\n    if (query->Step == -1)\n    {\n        IM_ASSERT(query->Results.Size == 0);\n        query->Step++;\n        query->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());\n        for (int n = 0; n < window->IDStack.Size + 1; n++)\n            query->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id;\n        return;\n    }\n\n    // Step 0+: query for individual level\n    IM_ASSERT(query->Step >= 0);\n    if (query->Step != window->IDStack.Size)\n        return;\n    ImGuiStackLevelInfo* info = &query->Results[query->Step];\n    IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);\n\n    if (info->DescOffset == -1)\n    {\n        const char* result = NULL;\n        const char* result_end = NULL;\n        switch (data_type)\n        {\n        case ImGuiDataType_S32:\n            ImFormatStringToTempBuffer(&result, &result_end, \"%d\", (int)(intptr_t)data_id);\n            break;\n        case ImGuiDataType_String:\n            ImFormatStringToTempBuffer(&result, &result_end, \"%.*s\", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)ImStrlen((const char*)data_id), (const char*)data_id);\n            break;\n        case ImGuiDataType_Pointer:\n            ImFormatStringToTempBuffer(&result, &result_end, \"(void*)0x%p\", data_id);\n            break;\n        case ImGuiDataType_ID:\n            // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.\n            ImFormatStringToTempBuffer(&result, &result_end, \"0x%08X [override]\", id);\n            break;\n        default:\n            IM_ASSERT(0);\n        }\n        info->DescOffset = query->ResultsDescBuf.size();\n        query->ResultsDescBuf.append(result, result_end + 1); // Include zero terminator\n    }\n    info->QuerySuccess = true;\n    if (info->DataType == -1)\n        info->DataType = (ImS8)data_type;\n}\n\nstatic int DebugItemPathQuery_FormatLevelInfo(ImGuiDebugItemPathQuery* query, int n, bool format_for_ui, char* buf, size_t buf_size)\n{\n    ImGuiStackLevelInfo* info = &query->Results[n];\n    ImGuiWindow* window = (info->DescOffset == -1 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;\n    if (window)                                 // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)\n        return ImFormatString(buf, buf_size, format_for_ui ? \"\\\"%s\\\" [window]\" : \"%s\", ImHashSkipUncontributingPrefix(window->Name));\n    if (info->QuerySuccess)                     // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button(\"\") where they both have same id)\n        return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? \"\\\"%s\\\"\" : \"%s\", ImHashSkipUncontributingPrefix(&query->ResultsDescBuf.Buf[info->DescOffset]));\n    if (query->Step < query->Results.Size)      // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.\n        return (*buf = 0);\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID)) // Source: ImGuiTestEngine's ItemInfo()\n        return ImFormatString(buf, buf_size, format_for_ui ? \"??? \\\"%s\\\"\" : \"%s\", ImHashSkipUncontributingPrefix(label));\n#endif\n    return ImFormatString(buf, buf_size, \"???\");\n}\n\nstatic const char* DebugItemPathQuery_GetResultAsPath(ImGuiDebugItemPathQuery* query, bool hex_encode_non_ascii_chars)\n{\n    ImGuiTextBuffer* buf = &query->ResultPathBuf;\n    buf->resize(0);\n    for (int stack_n = 0; stack_n < query->Results.Size; stack_n++)\n    {\n        char level_desc[256];\n        DebugItemPathQuery_FormatLevelInfo(query, stack_n, false, level_desc, IM_COUNTOF(level_desc));\n        buf->append(stack_n == 0 ? \"//\" : \"/\");\n        for (const char* p = level_desc; *p != 0; )\n        {\n            unsigned int c;\n            const char* p_next = p + ImTextCharFromUtf8(&c, p, NULL);\n            if (c == '/')\n                buf->append(\"\\\\\");\n            if (c < 256 || !hex_encode_non_ascii_chars)\n                buf->append(p, p_next);\n            else for (; p < p_next; p++)\n                buf->appendf(\"\\\\x%02x\", (unsigned char)*p);\n            p = p_next;\n        }\n    }\n    return buf->c_str();\n}\n\n// ID Stack Tool: Display UI\nvoid ImGui::ShowIDStackToolWindow(bool* p_open)\n{\n    ImGuiContext& g = *GImGui;\n    if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0)\n        SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver);\n    if (!Begin(\"Dear ImGui ID Stack Tool\", p_open) || GetCurrentWindow()->BeginCount > 1)\n    {\n        End();\n        return;\n    }\n\n    ImGuiDebugItemPathQuery* query = &g.DebugItemPathQuery;\n    ImGuiIDStackTool* tool = &g.DebugIDStackTool;\n    tool->LastActiveFrame = g.FrameCount;\n    const char* result_path = DebugItemPathQuery_GetResultAsPath(query, tool->OptHexEncodeNonAsciiChars);\n    Text(\"0x%08X\", query->MainID);\n    SameLine();\n    MetricsHelpMarker(\"Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\\nEach level of the stack correspond to a PushID() call.\\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\\nRead FAQ entry about the ID stack for details.\");\n\n    // Ctrl+C to copy path\n    const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime;\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f);\n    Checkbox(\"Hex-encode non-ASCII\", &tool->OptHexEncodeNonAsciiChars);\n    SameLine();\n    Checkbox(\"Ctrl+C: copy path\", &tool->OptCopyToClipboardOnCtrlC);\n    PopStyleVar();\n    SameLine();\n    TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), \"*COPIED*\");\n    if (tool->OptCopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))\n    {\n        tool->CopyToClipboardLastTime = (float)g.Time;\n        SetClipboardText(result_path);\n    }\n\n    Text(\"- Path \\\"%s\\\"\", query->Complete ? result_path : \"\");\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    Text(\"- Label \\\"%s\\\"\", query->MainID ? ImGuiTestEngine_FindItemDebugLabel(&g, query->MainID) : \"\");\n#endif\n    Separator();\n\n    // Display decorated stack\n    if (query->Results.Size > 0 && BeginTable(\"##table\", 3, ImGuiTableFlags_Borders))\n    {\n        const float id_width = CalcTextSize(\"0xDDDDDDDD\").x;\n        TableSetupColumn(\"Seed\", ImGuiTableColumnFlags_WidthFixed, id_width);\n        TableSetupColumn(\"PushID\", ImGuiTableColumnFlags_WidthStretch);\n        TableSetupColumn(\"Result\", ImGuiTableColumnFlags_WidthFixed, id_width);\n        TableHeadersRow();\n        for (int n = 0; n < query->Results.Size; n++)\n        {\n            ImGuiStackLevelInfo* info = &query->Results[n];\n            TableNextColumn();\n            Text(\"0x%08X\", (n > 0) ? query->Results[n - 1].ID : 0);\n            TableNextColumn();\n            DebugItemPathQuery_FormatLevelInfo(query, n, true, g.TempBuffer.Data, g.TempBuffer.Size);\n            TextUnformatted(g.TempBuffer.Data);\n            TableNextColumn();\n            Text(\"0x%08X\", info->ID);\n            if (n == query->Results.Size - 1)\n                TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header));\n        }\n        EndTable();\n    }\n    End();\n}\n\n#else\n\nvoid ImGui::ShowMetricsWindow(bool*) {}\nvoid ImGui::ShowFontAtlas(ImFontAtlas*) {}\nvoid ImGui::DebugNodeColumns(ImGuiOldColumns*) {}\nvoid ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}\nvoid ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}\nvoid ImGui::DebugNodeFont(ImFont*) {}\nvoid ImGui::DebugNodeFontGlyphsForSrcMask(ImFont*, ImFontBaked*, int) {}\nvoid ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}\nvoid ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}\nvoid ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}\nvoid ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}\nvoid ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}\nvoid ImGui::DebugNodeViewport(ImGuiViewportP*) {}\n\nvoid ImGui::ShowDebugLogWindow(bool*) {}\nvoid ImGui::ShowIDStackToolWindow(bool*) {}\nvoid ImGui::DebugStartItemPicker() {}\nvoid ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}\n\n#endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS\n\n#if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)\n// Demo helper function to select among loaded fonts.\n// Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one.\nvoid ImGui::ShowFontSelector(const char* label)\n{\n    ImGuiIO& io = GetIO();\n    ImFont* font_current = GetFont();\n    if (BeginCombo(label, font_current->GetDebugName()))\n    {\n        for (ImFont* font : io.Fonts->Fonts)\n        {\n            PushID((void*)font);\n            if (Selectable(font->GetDebugName(), font == font_current, ImGuiSelectableFlags_SelectOnNav))\n                io.FontDefault = font;\n            if (font == font_current)\n                SetItemDefaultFocus();\n            PopID();\n        }\n        EndCombo();\n    }\n    SameLine();\n    if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures)\n        MetricsHelpMarker(\n            \"- Load additional fonts with io.Fonts->AddFontXXX() functions.\\n\"\n            \"- Read FAQ and docs/FONTS.md for more details.\");\n    else\n        MetricsHelpMarker(\n            \"- Load additional fonts with io.Fonts->AddFontXXX() functions.\\n\"\n            \"- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\\n\"\n            \"- Read FAQ and docs/FONTS.md for more details.\\n\"\n            \"- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame().\");\n}\n#endif // #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)\n\n//-----------------------------------------------------------------------------\n\n// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.\n// Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.\n#ifdef IMGUI_INCLUDE_IMGUI_USER_INL\n#include \"imgui_user.inl\"\n#endif\n\n//-----------------------------------------------------------------------------\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/source/imgui_demo.cpp",
    "content": "// dear imgui, v1.92.7 WIP\n// (demo code)\n\n// Help:\n// - Read FAQ at http://dearimgui.com/faq\n// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.\n// - Need help integrating Dear ImGui in your codebase?\n//   - Read Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started\n//   - Read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase.\n// Read top of imgui.cpp and imgui.h for many details, documentation, comments, links.\n// Get the latest version at https://github.com/ocornut/imgui\n\n// How to easily locate code?\n// - Use Tools->Item Picker to debug break in code by clicking any widgets: https://github.com/ocornut/imgui/wiki/Debug-Tools\n// - Browse pthom's online imgui_explorer: web version the demo w/ source code browser: https://pthom.github.io/imgui_explorer\n// - Find a visible string and search for it in the code!\n\n//---------------------------------------------------\n// PLEASE DO NOT REMOVE THIS FILE FROM YOUR PROJECT!\n//---------------------------------------------------\n// Message to the person tempted to delete this file when integrating Dear ImGui into their codebase:\n// Think again! It is the most useful reference code that you and other coders will want to refer to and call.\n// Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of your game/app!\n// Also include Metrics! ItemPicker! DebugLog! and other debug features.\n// Removing this file from your project is hindering access to documentation for everyone in your team,\n// likely leading you to poorer usage of the library.\n// Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow().\n// If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be\n// linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty.\n// In another situation, whenever you have Dear ImGui available you probably want this to be available for reference.\n// Thank you,\n// -Your beloved friend, imgui_demo.cpp (which you won't delete)\n\n//--------------------------------------------\n// ABOUT THE MEANING OF THE 'static' KEYWORD:\n//--------------------------------------------\n// In this demo code, we frequently use 'static' variables inside functions.\n// A static variable persists across calls. It is essentially a global variable but declared inside the scope of the function.\n// Think of \"static int n = 0;\" as \"global int n = 0;\" !\n// We do this IN THE DEMO because we want:\n// - to gather code and data in the same place.\n// - to make the demo source code faster to read, faster to change, smaller in size.\n// - it is also a convenient way of storing simple UI related information as long as your function\n//   doesn't need to be reentrant or used in multiple threads.\n// This might be a pattern you will want to use in your code, but most of the data you would be working\n// with in a complex codebase is likely going to be stored outside your functions.\n\n//-----------------------------------------\n// ABOUT THE CODING STYLE OF OUR DEMO CODE\n//-----------------------------------------\n// The Demo code in this file is designed to be easy to copy-and-paste into your application!\n// Because of this:\n// - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace.\n// - We try to declare static variables in the local scope, as close as possible to the code using them.\n// - We never use any of the helpers/facilities used internally by Dear ImGui, unless available in the public API.\n// - We never use maths operators on ImVec2/ImVec4. For our other sources files we use them, and they are provided\n//   by imgui.h using the IMGUI_DEFINE_MATH_OPERATORS define. For your own sources file they are optional\n//   and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h.\n//   Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp.\n\n// Navigating this file:\n// - In Visual Studio: Ctrl+Comma (\"Edit.GoToAll\") can follow symbols inside comments, whereas Ctrl+F12 (\"Edit.GoToImplementation\") cannot.\n// - In Visual Studio w/ Visual Assist installed: Alt+G (\"VAssistX.GoToImplementation\") can also follow symbols inside comments.\n// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.\n// - You can search/grep for all sections listed in the index to find the section.\n\n/*\n\nIndex of this file:\n\n// [SECTION] Forward Declarations\n// [SECTION] Helpers\n// [SECTION] Demo Window / ShowDemoWindow()\n// [SECTION] DemoWindowMenuBar()\n// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)\n// [SECTION] DemoWindowWidgetsBasic()\n// [SECTION] DemoWindowWidgetsBullets()\n// [SECTION] DemoWindowWidgetsCollapsingHeaders()\n// [SECTION] DemoWindowWidgetsComboBoxes()\n// [SECTION] DemoWindowWidgetsColorAndPickers()\n// [SECTION] DemoWindowWidgetsDataTypes()\n// [SECTION] DemoWindowWidgetsDisableBlocks()\n// [SECTION] DemoWindowWidgetsDragAndDrop()\n// [SECTION] DemoWindowWidgetsDragsAndSliders()\n// [SECTION] DemoWindowWidgetsFonts()\n// [SECTION] DemoWindowWidgetsImages()\n// [SECTION] DemoWindowWidgetsListBoxes()\n// [SECTION] DemoWindowWidgetsMultiComponents()\n// [SECTION] DemoWindowWidgetsPlotting()\n// [SECTION] DemoWindowWidgetsProgressBars()\n// [SECTION] DemoWindowWidgetsQueryingStatuses()\n// [SECTION] DemoWindowWidgetsSelectables()\n// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()\n// [SECTION] DemoWindowWidgetsTabs()\n// [SECTION] DemoWindowWidgetsText()\n// [SECTION] DemoWindowWidgetsTextFilter()\n// [SECTION] DemoWindowWidgetsTextInput()\n// [SECTION] DemoWindowWidgetsTooltips()\n// [SECTION] DemoWindowWidgetsTreeNodes()\n// [SECTION] DemoWindowWidgetsVerticalSliders()\n// [SECTION] DemoWindowWidgets()\n// [SECTION] DemoWindowLayout()\n// [SECTION] DemoWindowPopups()\n// [SECTION] DemoWindowTables()\n// [SECTION] DemoWindowInputs()\n// [SECTION] About Window / ShowAboutWindow()\n// [SECTION] Style Editor / ShowStyleEditor()\n// [SECTION] User Guide / ShowUserGuide()\n// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()\n// [SECTION] Example App: Debug Console / ShowExampleAppConsole()\n// [SECTION] Example App: Debug Log / ShowExampleAppLog()\n// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()\n// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()\n// [SECTION] Example App: Long Text / ShowExampleAppLongText()\n// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()\n// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()\n// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()\n// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()\n// [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles()\n// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()\n// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()\n// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()\n// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()\n\n*/\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n\n// System includes\n#include <ctype.h>          // toupper\n#include <limits.h>         // INT_MIN, INT_MAX\n#include <math.h>           // sqrtf, powf, cosf, sinf, floorf, ceilf\n#include <stdio.h>          // vsnprintf, sscanf, printf\n#include <stdlib.h>         // NULL, malloc, free, atoi\n#include <stdint.h>         // intptr_t\n#if !defined(_MSC_VER) || _MSC_VER >= 1800\n#include <inttypes.h>       // PRId64/PRIu64, not avail in some MinGW headers.\n#endif\n#ifdef __EMSCRIPTEN__\n#include <emscripten/version.h>     // __EMSCRIPTEN_MAJOR__ etc.\n#endif\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4127)     // condition expression is constant\n#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'                     // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast                           // yes, they are more terse.\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"        // warning: 'xx' is deprecated: The POSIX name for this..   // for strdup used in demo code (so user can copy & paste the code)\n#pragma clang diagnostic ignored \"-Wint-to-void-pointer-cast\"       // warning: cast to 'void *' from smaller integer type\n#pragma clang diagnostic ignored \"-Wformat\"                         // warning: format specifies type 'int' but the argument has type 'unsigned int'\n#pragma clang diagnostic ignored \"-Wformat-security\"                // warning: format string is not a string literal\n#pragma clang diagnostic ignored \"-Wexit-time-destructors\"          // warning: declaration requires an exit-time destructor    // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.\n#pragma clang diagnostic ignored \"-Wunused-macros\"                  // warning: macro is not used                               // we define snprintf/vsnprintf on Windows so they are available, but not always used.\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant                   // some standard header variations use #define NULL 0\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.\n#pragma clang diagnostic ignored \"-Wreserved-id-macro\"              // warning: macro name is a reserved identifier\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#pragma clang diagnostic ignored \"-Wunsafe-buffer-usage\"            // warning: 'xxx' is an unsafe pointer used for buffer access\n#pragma clang diagnostic ignored \"-Wswitch-default\"                 // warning: 'switch' missing 'default' label\n#elif defined(__GNUC__)\n#pragma GCC diagnostic ignored \"-Wpragmas\"                          // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"                      // warning: comparing floating-point with '==' or '!=' is unsafe\n#pragma GCC diagnostic ignored \"-Wint-to-pointer-cast\"              // warning: cast to pointer from integer of different size\n#pragma GCC diagnostic ignored \"-Wformat\"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'\n#pragma GCC diagnostic ignored \"-Wformat-security\"                  // warning: format string is not a string literal (potentially insecure)\n#pragma GCC diagnostic ignored \"-Wdouble-promotion\"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function\n#pragma GCC diagnostic ignored \"-Wconversion\"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value\n#pragma GCC diagnostic ignored \"-Wmisleading-indentation\"           // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement      // GCC 6.0+ only. See #883 on GitHub.\n#pragma GCC diagnostic ignored \"-Wstrict-overflow\"                  // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1\n#pragma GCC diagnostic ignored \"-Wcast-qual\"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers\n#endif\n\n// Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)\n#ifdef _WIN32\n#define IM_NEWLINE  \"\\r\\n\"\n#else\n#define IM_NEWLINE  \"\\n\"\n#endif\n\n// Helpers\n#if defined(_MSC_VER) && !defined(snprintf)\n#define snprintf    _snprintf\n#endif\n#if defined(_MSC_VER) && !defined(vsnprintf)\n#define vsnprintf   _vsnprintf\n#endif\n\n// Format specifiers for 64-bit values (hasn't been decently standardized before VS2013)\n#if !defined(PRId64) && defined(_MSC_VER)\n#define PRId64 \"I64d\"\n#define PRIu64 \"I64u\"\n#elif !defined(PRId64)\n#define PRId64 \"lld\"\n#define PRIu64 \"llu\"\n#endif\n\n// Helpers macros\n// We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste,\n// but making an exception here as those are largely simplifying code...\n// In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo.\n#define IM_MIN(A, B)            (((A) < (B)) ? (A) : (B))\n#define IM_MAX(A, B)            (((A) >= (B)) ? (A) : (B))\n#define IM_CLAMP(V, MN, MX)     ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V))\n\n// Enforce cdecl calling convention for functions called by the standard library,\n// in case compilation settings changed the default to e.g. __vectorcall\n#ifndef IMGUI_CDECL\n#ifdef _MSC_VER\n#define IMGUI_CDECL __cdecl\n#else\n#define IMGUI_CDECL\n#endif\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Forward Declarations\n//-----------------------------------------------------------------------------\n\n#if !defined(IMGUI_DISABLE_DEMO_WINDOWS)\n\n// Forward Declarations\nstruct ImGuiDemoWindowData;\nstatic void ShowExampleAppMainMenuBar();\nstatic void ShowExampleAppAssetsBrowser(bool* p_open);\nstatic void ShowExampleAppConsole(bool* p_open);\nstatic void ShowExampleAppCustomRendering(bool* p_open);\nstatic void ShowExampleAppDockSpace(bool* p_open);\nstatic void ShowExampleAppDocuments(bool* p_open);\nstatic void ShowExampleAppLog(bool* p_open);\nstatic void ShowExampleAppLayout(bool* p_open);\nstatic void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data);\nstatic void ShowExampleAppSimpleOverlay(bool* p_open);\nstatic void ShowExampleAppAutoResize(bool* p_open);\nstatic void ShowExampleAppConstrainedResize(bool* p_open);\nstatic void ShowExampleAppFullscreen(bool* p_open);\nstatic void ShowExampleAppLongText(bool* p_open);\nstatic void ShowExampleAppWindowTitles(bool* p_open);\nstatic void ShowExampleMenuFile();\n\n// We split the contents of the big ShowDemoWindow() function into smaller functions\n// (because the link time of very large functions tends to grow non-linearly)\nstatic void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data);\nstatic void DemoWindowWidgets(ImGuiDemoWindowData* demo_data);\nstatic void DemoWindowLayout();\nstatic void DemoWindowPopups();\nstatic void DemoWindowTables();\nstatic void DemoWindowColumns();\nstatic void DemoWindowInputs();\n\n// Helper tree functions used by Property Editor & Multi-Select demos\nstruct ExampleTreeNode;\nstatic ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent);\nstatic void             ExampleTree_DestroyNode(ExampleTreeNode* node);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Helpers\n//-----------------------------------------------------------------------------\n\n// Helper to display a little (?) mark which shows a tooltip when hovered.\n// In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md)\nstatic void HelpMarker(const char* desc)\n{\n    ImGui::TextDisabled(\"(?)\");\n    if (ImGui::BeginItemTooltip())\n    {\n        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);\n        ImGui::TextUnformatted(desc);\n        ImGui::PopTextWrapPos();\n        ImGui::EndTooltip();\n    }\n}\n\nstatic void ShowDockingDisabledMessage()\n{\n    ImGuiIO& io = ImGui::GetIO();\n    ImGui::Text(\"ERROR: Docking is not enabled! See Demo > Configuration.\");\n    ImGui::Text(\"Set io.ConfigFlags |= ImGuiConfigFlags_DockingEnable in your code, or \");\n    ImGui::SameLine(0.0f, 0.0f);\n    if (ImGui::SmallButton(\"click here\"))\n        io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;\n}\n\n// Helper to wire demo markers located in code to an interactive browser (e.g. https://pthom.github.io/imgui_explorer)\n#if IMGUI_VERSION_NUM >= 19263\nnamespace ImGui { extern IMGUI_API void DemoMarker(const char* file, int line, const char* section); }\n#define IMGUI_DEMO_MARKER(section)  do { ImGui::DemoMarker(\"imgui_demo.cpp\", __LINE__, section); } while (0)\n#endif\n\n// Sneakily forward declare functions which aren't worth putting in public API yet\nnamespace ImGui\n{\n    IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas);\n    IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool is_open);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Demo Window / ShowDemoWindow()\n//-----------------------------------------------------------------------------\n\n// Data to be shared across different functions of the demo.\nstruct ImGuiDemoWindowData\n{\n    // Examples Apps (accessible from the \"Examples\" menu)\n    bool ShowMainMenuBar = false;\n    bool ShowAppAssetsBrowser = false;\n    bool ShowAppConsole = false;\n    bool ShowAppCustomRendering = false;\n    bool ShowAppDocuments = false;\n    bool ShowAppDockSpace = false;\n    bool ShowAppLog = false;\n    bool ShowAppLayout = false;\n    bool ShowAppPropertyEditor = false;\n    bool ShowAppSimpleOverlay = false;\n    bool ShowAppAutoResize = false;\n    bool ShowAppConstrainedResize = false;\n    bool ShowAppFullscreen = false;\n    bool ShowAppLongText = false;\n    bool ShowAppWindowTitles = false;\n\n    // Dear ImGui Tools (accessible from the \"Tools\" menu)\n    bool ShowMetrics = false;\n    bool ShowDebugLog = false;\n    bool ShowIDStackTool = false;\n    bool ShowStyleEditor = false;\n    bool ShowAbout = false;\n\n    // Other data\n    bool DisableSections = false;\n    ExampleTreeNode* DemoTree = NULL;\n\n    ~ImGuiDemoWindowData() { if (DemoTree) ExampleTree_DestroyNode(DemoTree); }\n};\n\n// Demonstrate most Dear ImGui features (this is big function!)\n// You may execute this function to experiment with the UI and understand what it does.\n// You may then search for keywords in the code when you are interested by a specific feature.\nvoid ImGui::ShowDemoWindow(bool* p_open)\n{\n    // Exceptionally add an extra assert here for people confused about initial Dear ImGui setup\n    // Most functions would normally just assert/crash if the context is missing.\n    IM_ASSERT(ImGui::GetCurrentContext() != NULL && \"Missing Dear ImGui context. Refer to examples app!\");\n\n    // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.\n    IMGUI_CHECKVERSION();\n\n    // Stored data\n    static ImGuiDemoWindowData demo_data;\n\n    // Examples Apps (accessible from the \"Examples\" menu)\n    if (demo_data.ShowMainMenuBar)          { ShowExampleAppMainMenuBar(); }\n    if (demo_data.ShowAppDockSpace)         { ShowExampleAppDockSpace(&demo_data.ShowAppDockSpace); } // Important: Process the Docking app first, as explicit DockSpace() nodes needs to be submitted early (read comments near the DockSpace function)\n    if (demo_data.ShowAppDocuments)         { ShowExampleAppDocuments(&demo_data.ShowAppDocuments); } // ...process the Document app next, as it may also use a DockSpace()\n    if (demo_data.ShowAppAssetsBrowser)     { ShowExampleAppAssetsBrowser(&demo_data.ShowAppAssetsBrowser); }\n    if (demo_data.ShowAppConsole)           { ShowExampleAppConsole(&demo_data.ShowAppConsole); }\n    if (demo_data.ShowAppCustomRendering)   { ShowExampleAppCustomRendering(&demo_data.ShowAppCustomRendering); }\n    if (demo_data.ShowAppLog)               { ShowExampleAppLog(&demo_data.ShowAppLog); }\n    if (demo_data.ShowAppLayout)            { ShowExampleAppLayout(&demo_data.ShowAppLayout); }\n    if (demo_data.ShowAppPropertyEditor)    { ShowExampleAppPropertyEditor(&demo_data.ShowAppPropertyEditor, &demo_data); }\n    if (demo_data.ShowAppSimpleOverlay)     { ShowExampleAppSimpleOverlay(&demo_data.ShowAppSimpleOverlay); }\n    if (demo_data.ShowAppAutoResize)        { ShowExampleAppAutoResize(&demo_data.ShowAppAutoResize); }\n    if (demo_data.ShowAppConstrainedResize) { ShowExampleAppConstrainedResize(&demo_data.ShowAppConstrainedResize); }\n    if (demo_data.ShowAppFullscreen)        { ShowExampleAppFullscreen(&demo_data.ShowAppFullscreen); }\n    if (demo_data.ShowAppLongText)          { ShowExampleAppLongText(&demo_data.ShowAppLongText); }\n    if (demo_data.ShowAppWindowTitles)      { ShowExampleAppWindowTitles(&demo_data.ShowAppWindowTitles); }\n\n    // Dear ImGui Tools (accessible from the \"Tools\" menu)\n    if (demo_data.ShowMetrics)              { ImGui::ShowMetricsWindow(&demo_data.ShowMetrics); }\n    if (demo_data.ShowDebugLog)             { ImGui::ShowDebugLogWindow(&demo_data.ShowDebugLog); }\n    if (demo_data.ShowIDStackTool)          { ImGui::ShowIDStackToolWindow(&demo_data.ShowIDStackTool); }\n    if (demo_data.ShowAbout)                { ImGui::ShowAboutWindow(&demo_data.ShowAbout); }\n    if (demo_data.ShowStyleEditor)\n    {\n        ImGui::Begin(\"Dear ImGui Style Editor\", &demo_data.ShowStyleEditor);\n        ImGui::ShowStyleEditor();\n        ImGui::End();\n    }\n\n    // Demonstrate the various window flags. Typically you would just use the default!\n    static bool no_titlebar = false;\n    static bool no_scrollbar = false;\n    static bool no_menu = false;\n    static bool no_move = false;\n    static bool no_resize = false;\n    static bool no_collapse = false;\n    static bool no_close = false;\n    static bool no_nav = false;\n    static bool no_background = false;\n    static bool no_bring_to_front = false;\n    static bool no_docking = false;\n    static bool unsaved_document = false;\n\n    ImGuiWindowFlags window_flags = 0;\n    if (no_titlebar)        window_flags |= ImGuiWindowFlags_NoTitleBar;\n    if (no_scrollbar)       window_flags |= ImGuiWindowFlags_NoScrollbar;\n    if (!no_menu)           window_flags |= ImGuiWindowFlags_MenuBar;\n    if (no_move)            window_flags |= ImGuiWindowFlags_NoMove;\n    if (no_resize)          window_flags |= ImGuiWindowFlags_NoResize;\n    if (no_collapse)        window_flags |= ImGuiWindowFlags_NoCollapse;\n    if (no_nav)             window_flags |= ImGuiWindowFlags_NoNav;\n    if (no_background)      window_flags |= ImGuiWindowFlags_NoBackground;\n    if (no_bring_to_front)  window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;\n    if (no_docking)         window_flags |= ImGuiWindowFlags_NoDocking;\n    if (unsaved_document)   window_flags |= ImGuiWindowFlags_UnsavedDocument;\n    if (no_close)           p_open = NULL; // Don't pass our bool* to Begin\n\n    // We specify a default position/size in case there's no data in the .ini file.\n    // We only do it to make the demo applications a little more welcoming, but typically this isn't required.\n    const ImGuiViewport* main_viewport = ImGui::GetMainViewport();\n    ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver);\n    ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver);\n\n    // Main body of the Demo window starts here.\n    if (!ImGui::Begin(\"Dear ImGui Demo\", p_open, window_flags))\n    {\n        // Early out if the window is collapsed, as an optimization.\n        ImGui::End();\n        return;\n    }\n\n    // Most framed widgets share a common width settings. Remaining width is used for the label.\n    // The width of the frame may be changed with PushItemWidth() or SetNextItemWidth().\n    // - Positive value for absolute size, negative value for right-alignment.\n    // - The default value is about GetWindowWidth() * 0.65f.\n    // - See 'Demo->Layout->Widgets Width' for details.\n    // Here we change the frame width based on how much width we want to give to the label.\n    const float label_width_base = ImGui::GetFontSize() * 12;               // Some amount of width for label, based on font size.\n    const float label_width_max = ImGui::GetContentRegionAvail().x * 0.40f; // ...but always leave some room for framed widgets.\n    const float label_width = IM_MIN(label_width_base, label_width_max);\n    ImGui::PushItemWidth(-label_width);                                     // Right-align: framed items will leave 'label_width' available for the label.\n    //ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.40f);       // e.g. Use 40% width for framed widgets, leaving 60% width for labels.\n    //ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.40f);      // e.g. Use 40% width for labels, leaving 60% width for framed widgets.\n    //ImGui::PushItemWidth(ImGui::GetFontSize() * -12);                     // e.g. Use XXX width for labels, leaving the rest for framed widgets.\n\n    // Menu Bar\n    DemoWindowMenuBar(&demo_data);\n\n    ImGui::Text(\"dear imgui says hello! (%s) (%d)\", IMGUI_VERSION, IMGUI_VERSION_NUM);\n    ImGui::Spacing();\n\n    if (ImGui::CollapsingHeader(\"Help\"))\n    {\n        IMGUI_DEMO_MARKER(\"Help\");\n        ImGui::SeparatorText(\"ABOUT THIS DEMO:\");\n        ImGui::BulletText(\"Sections below are demonstrating many aspects of the library.\");\n        ImGui::BulletText(\"The \\\"Examples\\\" menu above leads to more demo contents.\");\n        ImGui::BulletText(\"The \\\"Tools\\\" menu above gives access to: About Box, Style Editor,\\n\"\n                          \"and Metrics/Debugger (general purpose Dear ImGui debugging tool).\");\n        ImGui::BulletText(\"Web demo (w/ source code browser): \");\n        ImGui::SameLine(0, 0);\n        ImGui::TextLinkOpenURL(\"https://pthom.github.io/imgui_explorer\");\n\n        ImGui::SeparatorText(\"PROGRAMMER GUIDE:\");\n        ImGui::BulletText(\"See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!\");\n        ImGui::BulletText(\"See comments in imgui.cpp.\");\n        ImGui::BulletText(\"See example applications in the examples/ folder.\");\n        ImGui::BulletText(\"Read the FAQ at \");\n        ImGui::SameLine(0, 0);\n        ImGui::TextLinkOpenURL(\"https://www.dearimgui.com/faq/\");\n        ImGui::BulletText(\"Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls.\");\n        ImGui::BulletText(\"Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls.\");\n\n        ImGui::SeparatorText(\"USER GUIDE:\");\n        ImGui::ShowUserGuide();\n    }\n\n    if (ImGui::CollapsingHeader(\"Configuration\"))\n    {\n        ImGuiIO& io = ImGui::GetIO();\n\n        if (ImGui::TreeNode(\"Configuration##2\"))\n        {\n            IMGUI_DEMO_MARKER(\"Configuration\");\n            ImGui::SeparatorText(\"General\");\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NavEnableKeyboard\",    &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);\n            ImGui::SameLine(); HelpMarker(\"Enable keyboard controls.\");\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NavEnableGamepad\",     &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);\n            ImGui::SameLine(); HelpMarker(\"Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\\n\\nRead instructions in imgui.cpp for details.\");\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NoMouse\",              &io.ConfigFlags, ImGuiConfigFlags_NoMouse);\n            ImGui::SameLine(); HelpMarker(\"Instruct dear imgui to disable mouse inputs and interactions.\");\n\n            // The \"NoMouse\" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix it:\n            if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)\n            {\n                if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f)\n                {\n                    ImGui::SameLine();\n                    ImGui::Text(\"<<PRESS SPACE TO DISABLE>>\");\n                }\n                // Prevent both being checked\n                if (ImGui::IsKeyPressed(ImGuiKey_Space) || (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard))\n                    io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;\n            }\n\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NoMouseCursorChange\",  &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange);\n            ImGui::SameLine(); HelpMarker(\"Instruct backend to not alter mouse cursor shape and visibility.\");\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NoKeyboard\", &io.ConfigFlags, ImGuiConfigFlags_NoKeyboard);\n            ImGui::SameLine(); HelpMarker(\"Instruct dear imgui to disable keyboard inputs and interactions.\");\n\n            ImGui::Checkbox(\"io.ConfigInputTrickleEventQueue\", &io.ConfigInputTrickleEventQueue);\n            ImGui::SameLine(); HelpMarker(\"Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.\");\n            ImGui::Checkbox(\"io.MouseDrawCursor\", &io.MouseDrawCursor);\n            ImGui::SameLine(); HelpMarker(\"Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\\n\\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something).\");\n\n            ImGui::SeparatorText(\"Keyboard/Gamepad Navigation\");\n            ImGui::Checkbox(\"io.ConfigNavSwapGamepadButtons\", &io.ConfigNavSwapGamepadButtons);\n            ImGui::Checkbox(\"io.ConfigNavMoveSetMousePos\", &io.ConfigNavMoveSetMousePos);\n            ImGui::SameLine(); HelpMarker(\"Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult\");\n            ImGui::Checkbox(\"io.ConfigNavCaptureKeyboard\", &io.ConfigNavCaptureKeyboard);\n            ImGui::Checkbox(\"io.ConfigNavEscapeClearFocusItem\", &io.ConfigNavEscapeClearFocusItem);\n            ImGui::SameLine(); HelpMarker(\"Pressing Escape clears focused item.\");\n            ImGui::Checkbox(\"io.ConfigNavEscapeClearFocusWindow\", &io.ConfigNavEscapeClearFocusWindow);\n            ImGui::SameLine(); HelpMarker(\"Pressing Escape clears focused window.\");\n            ImGui::Checkbox(\"io.ConfigNavCursorVisibleAuto\", &io.ConfigNavCursorVisibleAuto);\n            ImGui::SameLine(); HelpMarker(\"Using directional navigation key makes the cursor visible. Mouse click hides the cursor.\");\n            ImGui::Checkbox(\"io.ConfigNavCursorVisibleAlways\", &io.ConfigNavCursorVisibleAlways);\n            ImGui::SameLine(); HelpMarker(\"Navigation cursor is always visible.\");\n\n            ImGui::SeparatorText(\"Docking\");\n            ImGui::CheckboxFlags(\"io.ConfigFlags: DockingEnable\", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable);\n            ImGui::SameLine();\n            if (io.ConfigDockingWithShift)\n                HelpMarker(\"Drag from window title bar or their tab to dock/undock. Hold SHIFT to enable docking.\\n\\nDrag from window menu button (upper-left button) to undock an entire node (all windows).\");\n            else\n                HelpMarker(\"Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\\n\\nDrag from window menu button (upper-left button) to undock an entire node (all windows).\");\n            if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)\n            {\n                ImGui::Indent();\n                ImGui::Checkbox(\"io.ConfigDockingNoSplit\", &io.ConfigDockingNoSplit);\n                ImGui::SameLine(); HelpMarker(\"Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars.\");\n                ImGui::Checkbox(\"io.ConfigDockingNoDockingOver\", &io.ConfigDockingNoDockingOver);\n                ImGui::SameLine(); HelpMarker(\"Simplified docking mode: disable window merging into a same tab-bar, so docking is limited to splitting windows.\");\n                ImGui::Checkbox(\"io.ConfigDockingWithShift\", &io.ConfigDockingWithShift);\n                ImGui::SameLine(); HelpMarker(\"Enable docking when holding Shift only (allow to drop in wider space, reduce visual noise)\");\n                ImGui::Checkbox(\"io.ConfigDockingAlwaysTabBar\", &io.ConfigDockingAlwaysTabBar);\n                ImGui::SameLine(); HelpMarker(\"Create a docking node and tab-bar on single floating windows.\");\n                ImGui::Checkbox(\"io.ConfigDockingTransparentPayload\", &io.ConfigDockingTransparentPayload);\n                ImGui::SameLine(); HelpMarker(\"Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge.\");\n                ImGui::Unindent();\n            }\n\n            ImGui::SeparatorText(\"Multi-viewports\");\n            ImGui::CheckboxFlags(\"io.ConfigFlags: ViewportsEnable\", &io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable);\n            ImGui::SameLine(); HelpMarker(\"[beta] Enable beta multi-viewports support. See ImGuiPlatformIO for details.\");\n            if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)\n            {\n                ImGui::Indent();\n                ImGui::Checkbox(\"io.ConfigViewportsNoAutoMerge\", &io.ConfigViewportsNoAutoMerge);\n                ImGui::SameLine(); HelpMarker(\"Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it.\");\n                ImGui::Checkbox(\"io.ConfigViewportsNoTaskBarIcon\", &io.ConfigViewportsNoTaskBarIcon);\n                ImGui::SameLine(); HelpMarker(\"(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)\");\n                ImGui::Checkbox(\"io.ConfigViewportsNoDecoration\", &io.ConfigViewportsNoDecoration);\n                ImGui::SameLine(); HelpMarker(\"(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)\");\n                ImGui::Checkbox(\"io.ConfigViewportsNoDefaultParent\", &io.ConfigViewportsNoDefaultParent);\n                ImGui::SameLine(); HelpMarker(\"(note: some platform backends may not reflect a change of this value for existing viewports, and may need the viewport to be recreated)\");\n                ImGui::Checkbox(\"io.ConfigViewportsPlatformFocusSetsImGuiFocus\", &io.ConfigViewportsPlatformFocusSetsImGuiFocus);\n                ImGui::SameLine(); HelpMarker(\"When a platform window is focused (e.g. using Alt+Tab, clicking Platform Title Bar), apply corresponding focus on imgui windows (may clear focus/active id from imgui windows location in other platform windows). In principle this is better enabled but we provide an opt-out, because some Linux window managers tend to eagerly focus windows (e.g. on mouse hover, or even a simple window pos/size change).\");\n                ImGui::Unindent();\n            }\n\n            //ImGui::SeparatorText(\"DPI/Scaling\");\n            //ImGui::Checkbox(\"io.ConfigDpiScaleFonts\", &io.ConfigDpiScaleFonts);\n            //ImGui::SameLine(); HelpMarker(\"Experimental: Automatically update style.FontScaleDpi when Monitor DPI changes. This will scale fonts but NOT style sizes/padding for now.\");\n            //ImGui::Checkbox(\"io.ConfigDpiScaleViewports\", &io.ConfigDpiScaleViewports);\n            //ImGui::SameLine(); HelpMarker(\"Experimental: Scale Dear ImGui and Platform Windows when Monitor DPI changes.\");\n\n            ImGui::SeparatorText(\"Windows\");\n            ImGui::Checkbox(\"io.ConfigWindowsResizeFromEdges\", &io.ConfigWindowsResizeFromEdges);\n            ImGui::SameLine(); HelpMarker(\"Enable resizing of windows from their edges and from the lower-left corner.\\nThis requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback.\");\n            ImGui::Checkbox(\"io.ConfigWindowsMoveFromTitleBarOnly\", &io.ConfigWindowsMoveFromTitleBarOnly);\n            ImGui::Checkbox(\"io.ConfigWindowsCopyContentsWithCtrlC\", &io.ConfigWindowsCopyContentsWithCtrlC); // [EXPERIMENTAL]\n            ImGui::SameLine(); HelpMarker(\"*EXPERIMENTAL* Ctrl+C copy the contents of focused window into the clipboard.\\n\\nExperimental because:\\n- (1) has known issues with nested Begin/End pairs.\\n- (2) text output quality varies.\\n- (3) text output is in submission order rather than spatial order.\");\n            ImGui::Checkbox(\"io.ConfigScrollbarScrollByPage\", &io.ConfigScrollbarScrollByPage);\n            ImGui::SameLine(); HelpMarker(\"Enable scrolling page by page when clicking outside the scrollbar grab.\\nWhen disabled, always scroll to clicked location.\\nWhen enabled, Shift+Click scrolls to clicked location.\");\n\n            ImGui::SeparatorText(\"Widgets\");\n            ImGui::Checkbox(\"io.ConfigInputTextCursorBlink\", &io.ConfigInputTextCursorBlink);\n            ImGui::SameLine(); HelpMarker(\"Enable blinking cursor (optional as some users consider it to be distracting).\");\n            ImGui::Checkbox(\"io.ConfigInputTextEnterKeepActive\", &io.ConfigInputTextEnterKeepActive);\n            ImGui::SameLine(); HelpMarker(\"Pressing Enter will reactivate item and select all text (single-line only).\");\n            ImGui::Checkbox(\"io.ConfigDragClickToInputText\", &io.ConfigDragClickToInputText);\n            ImGui::SameLine(); HelpMarker(\"Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).\");\n            ImGui::Checkbox(\"io.ConfigMacOSXBehaviors\", &io.ConfigMacOSXBehaviors);\n            ImGui::SameLine(); HelpMarker(\"Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.\");\n            ImGui::Text(\"Also see Style->Rendering for rendering options.\");\n\n            // Also read: https://github.com/ocornut/imgui/wiki/Error-Handling\n            ImGui::SeparatorText(\"Error Handling\");\n\n            ImGui::Checkbox(\"io.ConfigErrorRecovery\", &io.ConfigErrorRecovery);\n            ImGui::SameLine(); HelpMarker(\n                \"Options to configure how we handle recoverable errors.\\n\"\n                \"- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\\n\"\n                \"- You not are not supposed to rely on it in the course of a normal application run.\\n\"\n                \"- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\\n\"\n                \"- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call! \"\n                \"Otherwise it would severely hinder your ability to catch and correct mistakes!\");\n            ImGui::Checkbox(\"io.ConfigErrorRecoveryEnableAssert\", &io.ConfigErrorRecoveryEnableAssert);\n            ImGui::Checkbox(\"io.ConfigErrorRecoveryEnableDebugLog\", &io.ConfigErrorRecoveryEnableDebugLog);\n            ImGui::Checkbox(\"io.ConfigErrorRecoveryEnableTooltip\", &io.ConfigErrorRecoveryEnableTooltip);\n            if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip)\n                io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true;\n\n            // Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools\n            ImGui::SeparatorText(\"Debug\");\n            ImGui::Checkbox(\"io.ConfigDebugIsDebuggerPresent\", &io.ConfigDebugIsDebuggerPresent);\n            ImGui::SameLine(); HelpMarker(\"Enable various tools calling IM_DEBUG_BREAK().\\n\\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.\");\n            ImGui::Checkbox(\"io.ConfigDebugHighlightIdConflicts\", &io.ConfigDebugHighlightIdConflicts);\n            ImGui::SameLine(); HelpMarker(\"Highlight and show an error message when multiple items have conflicting identifiers.\");\n            ImGui::BeginDisabled();\n            ImGui::Checkbox(\"io.ConfigDebugBeginReturnValueOnce\", &io.ConfigDebugBeginReturnValueOnce);\n            ImGui::EndDisabled();\n            ImGui::SameLine(); HelpMarker(\"First calls to Begin()/BeginChild() will return false.\\n\\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover.\");\n            ImGui::Checkbox(\"io.ConfigDebugBeginReturnValueLoop\", &io.ConfigDebugBeginReturnValueLoop);\n            ImGui::SameLine(); HelpMarker(\"Some calls to Begin()/BeginChild() will return false.\\n\\nWill cycle through window depths then repeat. Windows should be flickering while running.\");\n            ImGui::Checkbox(\"io.ConfigDebugIgnoreFocusLoss\", &io.ConfigDebugIgnoreFocusLoss);\n            ImGui::SameLine(); HelpMarker(\"Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data.\");\n            ImGui::Checkbox(\"io.ConfigDebugIniSettings\", &io.ConfigDebugIniSettings);\n            ImGui::SameLine(); HelpMarker(\"Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower).\");\n\n            ImGui::TreePop();\n            ImGui::Spacing();\n        }\n\n        if (ImGui::TreeNode(\"Backend Flags\"))\n        {\n            IMGUI_DEMO_MARKER(\"Configuration/Backend Flags\");\n            HelpMarker(\n                \"Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\\n\"\n                \"Here we expose them as read-only fields to avoid breaking interactions with your backend.\");\n\n            // Make a local copy to avoid modifying actual backend flags.\n            // FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright?\n            ImGui::BeginDisabled();\n            ImGui::CheckboxFlags(\"io.BackendFlags: HasGamepad\",             &io.BackendFlags, ImGuiBackendFlags_HasGamepad);\n            ImGui::CheckboxFlags(\"io.BackendFlags: HasMouseCursors\",        &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);\n            ImGui::CheckboxFlags(\"io.BackendFlags: HasSetMousePos\",         &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos);\n            ImGui::CheckboxFlags(\"io.BackendFlags: PlatformHasViewports\",   &io.BackendFlags, ImGuiBackendFlags_PlatformHasViewports);\n            ImGui::CheckboxFlags(\"io.BackendFlags: HasMouseHoveredViewport\",&io.BackendFlags, ImGuiBackendFlags_HasMouseHoveredViewport);\n            ImGui::CheckboxFlags(\"io.BackendFlags: HasParentViewport\",      &io.BackendFlags, ImGuiBackendFlags_HasParentViewport);\n            ImGui::CheckboxFlags(\"io.BackendFlags: RendererHasVtxOffset\",   &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset);\n            ImGui::CheckboxFlags(\"io.BackendFlags: RendererHasTextures\",    &io.BackendFlags, ImGuiBackendFlags_RendererHasTextures);\n            ImGui::CheckboxFlags(\"io.BackendFlags: RendererHasViewports\",   &io.BackendFlags, ImGuiBackendFlags_RendererHasViewports);\n            ImGui::EndDisabled();\n\n            ImGui::TreePop();\n            ImGui::Spacing();\n        }\n\n        if (ImGui::TreeNode(\"Style, Fonts\"))\n        {\n            IMGUI_DEMO_MARKER(\"Configuration/Style, Fonts\");\n            ImGui::Checkbox(\"Style Editor\", &demo_data.ShowStyleEditor);\n            ImGui::SameLine();\n            HelpMarker(\"The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function.\");\n            ImGui::TreePop();\n            ImGui::Spacing();\n        }\n\n        if (ImGui::TreeNode(\"Capture/Logging\"))\n        {\n            IMGUI_DEMO_MARKER(\"Configuration/Capture, Logging\");\n            HelpMarker(\n                \"The logging API redirects all text output so you can easily capture the content of \"\n                \"a window or a block. Tree nodes can be automatically expanded.\\n\"\n                \"Try opening any of the contents below in this window and then click one of the \\\"Log To\\\" button.\");\n            ImGui::LogButtons();\n\n            HelpMarker(\"You can also call ImGui::LogText() to output directly to the log without a visual output.\");\n            if (ImGui::Button(\"Copy \\\"Hello, world!\\\" to clipboard\"))\n            {\n                ImGui::LogToClipboard();\n                ImGui::LogText(\"Hello, world!\");\n                ImGui::LogFinish();\n            }\n            ImGui::TreePop();\n        }\n    }\n\n    if (ImGui::CollapsingHeader(\"Window options\"))\n    {\n        IMGUI_DEMO_MARKER(\"Window options\");\n        if (ImGui::BeginTable(\"split\", 3))\n        {\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No titlebar\", &no_titlebar);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No scrollbar\", &no_scrollbar);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No menu\", &no_menu);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No move\", &no_move);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No resize\", &no_resize);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No collapse\", &no_collapse);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No close\", &no_close);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No nav\", &no_nav);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No background\", &no_background);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No bring to front\", &no_bring_to_front);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No docking\", &no_docking);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"Unsaved document\", &unsaved_document);\n            ImGui::EndTable();\n        }\n    }\n\n    // All demo contents\n    DemoWindowWidgets(&demo_data);\n    DemoWindowLayout();\n    DemoWindowPopups();\n    DemoWindowTables();\n    DemoWindowInputs();\n\n    // End of ShowDemoWindow()\n    ImGui::PopItemWidth();\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowMenuBar()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data)\n{\n    if (ImGui::BeginMenuBar())\n    {\n        if (ImGui::BeginMenu(\"Menu\"))\n        {\n            IMGUI_DEMO_MARKER(\"Menu/File\");\n            ShowExampleMenuFile();\n            ImGui::EndMenu();\n        }\n        if (ImGui::BeginMenu(\"Examples\"))\n        {\n            IMGUI_DEMO_MARKER(\"Menu/Examples\");\n            ImGui::MenuItem(\"Main menu bar\", NULL, &demo_data->ShowMainMenuBar);\n\n            ImGui::SeparatorText(\"Mini apps\");\n            ImGui::MenuItem(\"Assets Browser\", NULL, &demo_data->ShowAppAssetsBrowser);\n            ImGui::MenuItem(\"Console\", NULL, &demo_data->ShowAppConsole);\n            ImGui::MenuItem(\"Custom rendering\", NULL, &demo_data->ShowAppCustomRendering);\n            ImGui::MenuItem(\"Documents\", NULL, &demo_data->ShowAppDocuments);\n            ImGui::MenuItem(\"Dockspace\", NULL, &demo_data->ShowAppDockSpace);\n            ImGui::MenuItem(\"Log\", NULL, &demo_data->ShowAppLog);\n            ImGui::MenuItem(\"Property editor\", NULL, &demo_data->ShowAppPropertyEditor);\n            ImGui::MenuItem(\"Simple layout\", NULL, &demo_data->ShowAppLayout);\n            ImGui::MenuItem(\"Simple overlay\", NULL, &demo_data->ShowAppSimpleOverlay);\n\n            ImGui::SeparatorText(\"Concepts\");\n            ImGui::MenuItem(\"Auto-resizing window\", NULL, &demo_data->ShowAppAutoResize);\n            ImGui::MenuItem(\"Constrained-resizing window\", NULL, &demo_data->ShowAppConstrainedResize);\n            ImGui::MenuItem(\"Fullscreen window\", NULL, &demo_data->ShowAppFullscreen);\n            ImGui::MenuItem(\"Long text display\", NULL, &demo_data->ShowAppLongText);\n            ImGui::MenuItem(\"Manipulating window titles\", NULL, &demo_data->ShowAppWindowTitles);\n\n            ImGui::EndMenu();\n        }\n        //if (ImGui::MenuItem(\"MenuItem\")) {} // You can also use MenuItem() inside a menu bar!\n        if (ImGui::BeginMenu(\"Tools\"))\n        {\n            IMGUI_DEMO_MARKER(\"Menu/Tools\");\n            ImGuiIO& io = ImGui::GetIO();\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n            const bool has_debug_tools = true;\n#else\n            const bool has_debug_tools = false;\n#endif\n            ImGui::MenuItem(\"Metrics/Debugger\", NULL, &demo_data->ShowMetrics, has_debug_tools);\n            if (ImGui::BeginMenu(\"Debug Options\"))\n            {\n                ImGui::BeginDisabled(!has_debug_tools);\n                ImGui::Checkbox(\"Highlight ID Conflicts\", &io.ConfigDebugHighlightIdConflicts);\n                ImGui::EndDisabled();\n                ImGui::Checkbox(\"Assert on error recovery\", &io.ConfigErrorRecoveryEnableAssert);\n                ImGui::TextDisabled(\"(see Demo->Configuration for details & more)\");\n                ImGui::EndMenu();\n            }\n            ImGui::MenuItem(\"Debug Log\", NULL, &demo_data->ShowDebugLog, has_debug_tools);\n            ImGui::MenuItem(\"ID Stack Tool\", NULL, &demo_data->ShowIDStackTool, has_debug_tools);\n            bool is_debugger_present = io.ConfigDebugIsDebuggerPresent;\n            if (ImGui::MenuItem(\"Item Picker\", NULL, false, has_debug_tools))// && is_debugger_present))\n                ImGui::DebugStartItemPicker();\n            if (!is_debugger_present)\n                ImGui::SetItemTooltip(\"Requires io.ConfigDebugIsDebuggerPresent=true to be set.\\n\\nWe otherwise disable some extra features to avoid casual users crashing the application.\");\n            ImGui::MenuItem(\"Style Editor\", NULL, &demo_data->ShowStyleEditor);\n            ImGui::MenuItem(\"About Dear ImGui\", NULL, &demo_data->ShowAbout);\n\n            ImGui::EndMenu();\n        }\n        ImGui::EndMenuBar();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)\n//-----------------------------------------------------------------------------\n\n// Simple representation for a tree\n// (this is designed to be simple to understand for our demos, not to be fancy or efficient etc.)\nstruct ExampleTreeNode\n{\n    // Tree structure\n    char                        Name[28] = \"\";\n    int                         UID = 0;\n    ExampleTreeNode*            Parent = NULL;\n    ImVector<ExampleTreeNode*>  Childs;\n    int                         IndexInParent = 0;  // Maintaining this allows us to implement linear traversal more easily\n\n    // Leaf Data\n    bool                        HasData = false;    // All leaves have data\n    bool                        DataMyBool = true;\n    int                         DataMyInt = 128;\n    ImVec2                      DataMyVec2 = ImVec2(0.0f, 3.141592f);\n};\n\n// Simple representation of struct metadata/serialization data.\n// (this is a minimal version of what a typical advanced application may provide)\nstruct ExampleMemberInfo\n{\n    const char*     Name;       // Member name\n    ImGuiDataType   DataType;   // Member type\n    int             DataCount;  // Member count (1 when scalar)\n    int             Offset;     // Offset inside parent structure\n};\n\n// Metadata description of ExampleTreeNode struct.\nstatic const ExampleMemberInfo ExampleTreeNodeMemberInfos[]\n{\n    { \"MyName\",     ImGuiDataType_String,  1, offsetof(ExampleTreeNode, Name) },\n    { \"MyBool\",     ImGuiDataType_Bool,    1, offsetof(ExampleTreeNode, DataMyBool) },\n    { \"MyInt\",      ImGuiDataType_S32,     1, offsetof(ExampleTreeNode, DataMyInt) },\n    { \"MyVec2\",     ImGuiDataType_Float,   2, offsetof(ExampleTreeNode, DataMyVec2) },\n};\n\nstatic ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent)\n{\n    ExampleTreeNode* node = IM_NEW(ExampleTreeNode);\n    snprintf(node->Name, IM_COUNTOF(node->Name), \"%s\", name);\n    node->UID = uid;\n    node->Parent = parent;\n    node->IndexInParent = parent ? parent->Childs.Size : 0;\n    if (parent)\n        parent->Childs.push_back(node);\n    return node;\n}\n\nstatic void ExampleTree_DestroyNode(ExampleTreeNode* node)\n{\n    for (ExampleTreeNode* child_node : node->Childs)\n        ExampleTree_DestroyNode(child_node);\n    IM_DELETE(node);\n}\n\n// Create example tree data\n// (warning: this can allocates MANY MANY more times than other code in all of Dear ImGui + demo combined)\n// (a real application managing one million nodes would likely store its tree data differently)\nstatic ExampleTreeNode* ExampleTree_CreateDemoTree()\n{\n    //     20 root nodes ->    211 total nodes,   ~261 allocs.\n    //   1000 root nodes ->   ~11K total nodes,   ~14K allocs.\n    //  10000 root nodes ->  ~123K total nodes,  ~154K allocs.\n    // 100000 root nodes -> ~1338K total nodes, ~1666K allocs.\n    const int ROOT_ITEMS_COUNT = 20;\n\n    static const char* category_names[] = { \"Apple\", \"Banana\", \"Cherry\", \"Kiwi\", \"Mango\", \"Orange\", \"Pear\", \"Pineapple\", \"Strawberry\", \"Watermelon\" };\n    const int category_count = IM_COUNTOF(category_names);\n    const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name);\n    char name_buf[NAME_MAX_LEN];\n    int uid = 0;\n    ExampleTreeNode* node_L0 = ExampleTree_CreateNode(\"<ROOT>\", ++uid, NULL);\n    for (int idx_L0 = 0; idx_L0 < ROOT_ITEMS_COUNT; idx_L0++)\n    {\n        snprintf(name_buf, IM_COUNTOF(name_buf), \"%s %d\", category_names[idx_L0 / (ROOT_ITEMS_COUNT / category_count)], idx_L0 % (ROOT_ITEMS_COUNT / category_count));\n        ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0);\n        const int number_of_childs = (int)strlen(node_L1->Name);\n        for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++)\n        {\n            snprintf(name_buf, IM_COUNTOF(name_buf), \"Child %d\", idx_L1);\n            ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1);\n            node_L2->HasData = true;\n            if (idx_L1 == 0)\n            {\n                snprintf(name_buf, IM_COUNTOF(name_buf), \"Sub-child %d\", 0);\n                ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2);\n                node_L3->HasData = true;\n            }\n        }\n    }\n    return node_L0;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsBasic()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsBasic()\n{\n    if (ImGui::TreeNode(\"Basic\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Basic\");\n        ImGui::SeparatorText(\"General\");\n\n        IMGUI_DEMO_MARKER(\"Widgets/Basic/Button\");\n        static int clicked = 0;\n        if (ImGui::Button(\"Button\"))\n            clicked++;\n        if (clicked & 1)\n        {\n            ImGui::SameLine();\n            ImGui::Text(\"Thanks for clicking me!\");\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Basic/Checkbox\");\n        static bool check = true;\n        ImGui::Checkbox(\"checkbox\", &check);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Basic/RadioButton\");\n        static int e = 0;\n        ImGui::RadioButton(\"radio a\", &e, 0); ImGui::SameLine();\n        ImGui::RadioButton(\"radio b\", &e, 1); ImGui::SameLine();\n        ImGui::RadioButton(\"radio c\", &e, 2);\n\n        ImGui::AlignTextToFramePadding();\n        ImGui::TextLinkOpenURL(\"Hyperlink\", \"https://github.com/ocornut/imgui/wiki/Error-Handling\");\n\n        // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.\n        IMGUI_DEMO_MARKER(\"Widgets/Basic/Buttons (Colored)\");\n        for (int i = 0; i < 7; i++)\n        {\n            if (i > 0)\n                ImGui::SameLine();\n            ImGui::PushID(i);\n            ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f));\n            ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f));\n            ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f));\n            ImGui::Button(\"Click\");\n            ImGui::PopStyleColor(3);\n            ImGui::PopID();\n        }\n\n        // Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements\n        // (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!)\n        // See 'Demo->Layout->Text Baseline Alignment' for details.\n        ImGui::AlignTextToFramePadding();\n        ImGui::Text(\"Hold to repeat:\");\n        ImGui::SameLine();\n\n        // Arrow buttons with Repeater\n        IMGUI_DEMO_MARKER(\"Widgets/Basic/Buttons (Repeating)\");\n        static int counter = 0;\n        float spacing = ImGui::GetStyle().ItemInnerSpacing.x;\n        ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);\n        if (ImGui::ArrowButton(\"##left\", ImGuiDir_Left)) { counter--; }\n        ImGui::SameLine(0.0f, spacing);\n        if (ImGui::ArrowButton(\"##right\", ImGuiDir_Right)) { counter++; }\n        ImGui::PopItemFlag();\n        ImGui::SameLine();\n        ImGui::Text(\"%d\", counter);\n\n        ImGui::Button(\"Tooltip\");\n        ImGui::SetItemTooltip(\"I am a tooltip\");\n\n        ImGui::LabelText(\"label\", \"Value\");\n\n        ImGui::SeparatorText(\"Inputs\");\n\n        {\n            // If you want to use InputText() with std::string or any custom dynamic string type:\n            // - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp\n            // - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize.\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/InputText\");\n            static char str0[128] = \"Hello, world!\";\n            ImGui::InputText(\"input text\", str0, IM_COUNTOF(str0));\n            ImGui::SameLine(); HelpMarker(\n                \"USER:\\n\"\n                \"Hold Shift or use mouse to select text.\\n\"\n                \"Ctrl+Left/Right to word jump.\\n\"\n                \"Ctrl+A or Double-Click to select all.\\n\"\n                \"Ctrl+X,Ctrl+C,Ctrl+V for clipboard.\\n\"\n                \"Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo.\\n\"\n                \"Escape to revert.\\n\\n\"\n                \"PROGRAMMER:\\n\"\n                \"You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() \"\n                \"to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated \"\n                \"in imgui_demo.cpp).\");\n\n            static char str1[128] = \"\";\n            ImGui::InputTextWithHint(\"input text (w/ hint)\", \"enter text here\", str1, IM_COUNTOF(str1));\n\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/InputInt, InputFloat\");\n            static int i0 = 123;\n            ImGui::InputInt(\"input int\", &i0);\n\n            static float f0 = 0.001f;\n            ImGui::InputFloat(\"input float\", &f0, 0.01f, 1.0f, \"%.3f\");\n\n            static double d0 = 999999.00000001;\n            ImGui::InputDouble(\"input double\", &d0, 0.01f, 1.0f, \"%.8f\");\n\n            static float f1 = 1.e10f;\n            ImGui::InputFloat(\"input scientific\", &f1, 0.0f, 0.0f, \"%e\");\n            ImGui::SameLine(); HelpMarker(\n                \"You can input value using the scientific notation,\\n\"\n                \"  e.g. \\\"1e+8\\\" becomes \\\"100000000\\\".\");\n\n            static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f };\n            ImGui::InputFloat3(\"input float3\", vec4a);\n        }\n\n        ImGui::SeparatorText(\"Drags\");\n\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/DragInt, DragFloat\");\n            static int i1 = 50, i2 = 42, i3 = 128;\n            ImGui::DragInt(\"drag int\", &i1, 1);\n            ImGui::SameLine(); HelpMarker(\n                \"Click and drag to edit value.\\n\"\n                \"Hold Shift/Alt for faster/slower edit.\\n\"\n                \"Double-Click or Ctrl+Click to input value.\");\n            ImGui::DragInt(\"drag int 0..100\", &i2, 1, 0, 100, \"%d%%\", ImGuiSliderFlags_AlwaysClamp);\n            ImGui::DragInt(\"drag int wrap 100..200\", &i3, 1, 100, 200, \"%d\", ImGuiSliderFlags_WrapAround);\n\n            static float f1 = 1.00f, f2 = 0.0067f;\n            ImGui::DragFloat(\"drag float\", &f1, 0.005f);\n            ImGui::DragFloat(\"drag small float\", &f2, 0.0001f, 0.0f, 0.0f, \"%.06f ns\");\n            //ImGui::DragFloat(\"drag wrap -1..1\", &f3, 0.005f, -1.0f, 1.0f, NULL, ImGuiSliderFlags_WrapAround);\n        }\n\n        ImGui::SeparatorText(\"Sliders\");\n\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/SliderInt, SliderFloat\");\n            static int i1 = 0;\n            ImGui::SliderInt(\"slider int\", &i1, -1, 3);\n            ImGui::SameLine(); HelpMarker(\"Ctrl+Click to input value.\");\n\n            static float f1 = 0.123f, f2 = 0.0f;\n            ImGui::SliderFloat(\"slider float\", &f1, 0.0f, 1.0f, \"ratio = %.3f\");\n            ImGui::SliderFloat(\"slider float (log)\", &f2, -10.0f, 10.0f, \"%.4f\", ImGuiSliderFlags_Logarithmic);\n\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/SliderAngle\");\n            static float angle = 0.0f;\n            ImGui::SliderAngle(\"slider angle\", &angle);\n\n            // Using the format string to display a name instead of an integer.\n            // Here we completely omit '%d' from the format string, so it'll only display a name.\n            // This technique can also be used with DragInt().\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/Slider (enum)\");\n            enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT };\n            static int elem = Element_Fire;\n            const char* elems_names[Element_COUNT] = { \"Fire\", \"Earth\", \"Air\", \"Water\" };\n            const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : \"Unknown\";\n            ImGui::SliderInt(\"slider enum\", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable Ctrl+Click here.\n            ImGui::SameLine(); HelpMarker(\"Using the format string parameter to display a name instead of the underlying integer.\");\n        }\n\n        ImGui::SeparatorText(\"Selectors/Pickers\");\n\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/ColorEdit3, ColorEdit4\");\n            static float col1[3] = { 1.0f, 0.0f, 0.2f };\n            static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };\n            ImGui::ColorEdit3(\"color 1\", col1);\n            ImGui::SameLine(); HelpMarker(\n                \"Click on the color square to open a color picker.\\n\"\n                \"Click and hold to use drag and drop.\\n\"\n                \"Right-Click on the color square to show options.\\n\"\n                \"Ctrl+Click on individual component to input value.\\n\");\n\n            ImGui::ColorEdit4(\"color 2\", col2);\n        }\n\n        {\n            // Using the _simplified_ one-liner Combo() api here\n            // See \"Combo\" section for examples of how to use the more flexible BeginCombo()/EndCombo() api.\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/Combo\");\n            const char* items[] = { \"AAAA\", \"BBBB\", \"CCCC\", \"DDDD\", \"EEEE\", \"FFFF\", \"GGGG\", \"HHHH\", \"IIIIIII\", \"JJJJ\", \"KKKKKKK\" };\n            static int item_current = 0;\n            ImGui::Combo(\"combo\", &item_current, items, IM_COUNTOF(items));\n            ImGui::SameLine(); HelpMarker(\n                \"Using the simplified one-liner Combo API here.\\n\"\n                \"Refer to the \\\"Combo\\\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API.\");\n        }\n\n        {\n            // Using the _simplified_ one-liner ListBox() api here\n            // See \"List boxes\" section for examples of how to use the more flexible BeginListBox()/EndListBox() api.\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/ListBox\");\n            const char* items[] = { \"Apple\", \"Banana\", \"Cherry\", \"Kiwi\", \"Mango\", \"Orange\", \"Pineapple\", \"Strawberry\", \"Watermelon\" };\n            static int item_current = 1;\n            ImGui::ListBox(\"listbox\", &item_current, items, IM_COUNTOF(items), 4);\n            ImGui::SameLine(); HelpMarker(\n                \"Using the simplified one-liner ListBox API here.\\n\"\n                \"Refer to the \\\"List boxes\\\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API.\");\n        }\n\n        // Testing ImGuiOnceUponAFrame helper.\n        //static ImGuiOnceUponAFrame once;\n        //for (int i = 0; i < 5; i++)\n        //    if (once)\n        //        ImGui::Text(\"This will be displayed only once.\");\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsBullets()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsBullets()\n{\n    if (ImGui::TreeNode(\"Bullets\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Bullets\");\n        ImGui::BulletText(\"Bullet point 1\");\n        ImGui::BulletText(\"Bullet point 2\\nOn multiple lines\");\n        if (ImGui::TreeNode(\"Tree node\"))\n        {\n            ImGui::BulletText(\"Another bullet point\");\n            ImGui::TreePop();\n        }\n        ImGui::Bullet(); ImGui::Text(\"Bullet point 3 (two calls)\");\n        ImGui::Bullet(); ImGui::SmallButton(\"Button\");\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsCollapsingHeaders()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsCollapsingHeaders()\n{\n    if (ImGui::TreeNode(\"Collapsing Headers\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Collapsing Headers\");\n        static bool closable_group = true;\n        ImGui::Checkbox(\"Show 2nd header\", &closable_group);\n        if (ImGui::CollapsingHeader(\"Header\", ImGuiTreeNodeFlags_None))\n        {\n            ImGui::Text(\"IsItemHovered: %d\", ImGui::IsItemHovered());\n            for (int i = 0; i < 5; i++)\n                ImGui::Text(\"Some content %d\", i);\n        }\n        if (ImGui::CollapsingHeader(\"Header with a close button\", &closable_group))\n        {\n            ImGui::Text(\"IsItemHovered: %d\", ImGui::IsItemHovered());\n            for (int i = 0; i < 5; i++)\n                ImGui::Text(\"More content %d\", i);\n        }\n        /*\n        if (ImGui::CollapsingHeader(\"Header with a bullet\", ImGuiTreeNodeFlags_Bullet))\n            ImGui::Text(\"IsItemHovered: %d\", ImGui::IsItemHovered());\n        */\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsColorAndPickers()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsColorAndPickers()\n{\n    if (ImGui::TreeNode(\"Color/Picker Widgets\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Color\");\n        static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f);\n        static ImGuiColorEditFlags base_flags = ImGuiColorEditFlags_None;\n\n        ImGui::SeparatorText(\"Options\");\n        ImGui::CheckboxFlags(\"ImGuiColorEditFlags_NoAlpha\", &base_flags, ImGuiColorEditFlags_NoAlpha);\n        ImGui::CheckboxFlags(\"ImGuiColorEditFlags_AlphaOpaque\", &base_flags, ImGuiColorEditFlags_AlphaOpaque);\n        ImGui::CheckboxFlags(\"ImGuiColorEditFlags_AlphaNoBg\", &base_flags, ImGuiColorEditFlags_AlphaNoBg);\n        ImGui::CheckboxFlags(\"ImGuiColorEditFlags_AlphaPreviewHalf\", &base_flags, ImGuiColorEditFlags_AlphaPreviewHalf);\n        ImGui::CheckboxFlags(\"ImGuiColorEditFlags_NoOptions\", &base_flags, ImGuiColorEditFlags_NoOptions); ImGui::SameLine(); HelpMarker(\"Right-click on the individual color widget to show options.\");\n        ImGui::CheckboxFlags(\"ImGuiColorEditFlags_NoDragDrop\", &base_flags, ImGuiColorEditFlags_NoDragDrop);\n        ImGui::CheckboxFlags(\"ImGuiColorEditFlags_NoColorMarkers\", &base_flags, ImGuiColorEditFlags_NoColorMarkers);\n        ImGui::CheckboxFlags(\"ImGuiColorEditFlags_HDR\", &base_flags, ImGuiColorEditFlags_HDR); ImGui::SameLine(); HelpMarker(\"Currently all this does is to lift the 0..1 limits on dragging widgets.\");\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorEdit\");\n        ImGui::SeparatorText(\"Inline color editor\");\n        ImGui::Text(\"Color widget:\");\n        ImGui::SameLine(); HelpMarker(\n            \"Click on the color square to open a color picker.\\n\"\n            \"Ctrl+Click on individual component to input value.\\n\");\n        ImGui::ColorEdit3(\"MyColor##1\", (float*)&color, base_flags);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorEdit (HSV, with Alpha)\");\n        ImGui::Text(\"Color widget HSV with Alpha:\");\n        ImGui::ColorEdit4(\"MyColor##2\", (float*)&color, ImGuiColorEditFlags_DisplayHSV | base_flags);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorEdit (float display)\");\n        ImGui::Text(\"Color widget with Float Display:\");\n        ImGui::ColorEdit4(\"MyColor##2f\", (float*)&color, ImGuiColorEditFlags_Float | base_flags);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorButton (with Picker)\");\n        ImGui::Text(\"Color button with Picker:\");\n        ImGui::SameLine(); HelpMarker(\n            \"With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\\n\"\n            \"With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only \"\n            \"be used for the tooltip and picker popup.\");\n        ImGui::ColorEdit4(\"MyColor##3\", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | base_flags);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorButton (with custom Picker popup)\");\n        ImGui::Text(\"Color button with Custom Picker Popup:\");\n\n        // Generate a default palette. The palette will persist and can be edited.\n        static bool saved_palette_init = true;\n        static ImVec4 saved_palette[32] = {};\n        if (saved_palette_init)\n        {\n            for (int n = 0; n < IM_COUNTOF(saved_palette); n++)\n            {\n                ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f,\n                    saved_palette[n].x, saved_palette[n].y, saved_palette[n].z);\n                saved_palette[n].w = 1.0f; // Alpha\n            }\n            saved_palette_init = false;\n        }\n\n        static ImVec4 backup_color;\n        bool open_popup = ImGui::ColorButton(\"MyColor##3b\", color, base_flags);\n        ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);\n        open_popup |= ImGui::Button(\"Palette\");\n        if (open_popup)\n        {\n            ImGui::OpenPopup(\"mypicker\");\n            backup_color = color;\n        }\n        if (ImGui::BeginPopup(\"mypicker\"))\n        {\n            ImGui::Text(\"MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!\");\n            ImGui::Separator();\n            ImGui::ColorPicker4(\"##picker\", (float*)&color, base_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview);\n            ImGui::SameLine();\n\n            ImGui::BeginGroup(); // Lock X position\n            ImGui::Text(\"Current\");\n            ImGui::ColorButton(\"##current\", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40));\n            ImGui::Text(\"Previous\");\n            if (ImGui::ColorButton(\"##previous\", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40)))\n                color = backup_color;\n            ImGui::Separator();\n            ImGui::Text(\"Palette\");\n            for (int n = 0; n < IM_COUNTOF(saved_palette); n++)\n            {\n                ImGui::PushID(n);\n                if ((n % 8) != 0)\n                    ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);\n\n                ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip;\n                if (ImGui::ColorButton(\"##palette\", saved_palette[n], palette_button_flags, ImVec2(20, 20)))\n                    color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha!\n\n                // Allow user to drop colors into each palette entry. Note that ColorButton() is already a\n                // drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag.\n                if (ImGui::BeginDragDropTarget())\n                {\n                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))\n                        memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3);\n                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))\n                        memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4);\n                    ImGui::EndDragDropTarget();\n                }\n\n                ImGui::PopID();\n            }\n            ImGui::EndGroup();\n            ImGui::EndPopup();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorButton (simple)\");\n        ImGui::Text(\"Color button only:\");\n        static bool no_border = false;\n        ImGui::Checkbox(\"ImGuiColorEditFlags_NoBorder\", &no_border);\n        ImGui::ColorButton(\"MyColor##3c\", *(ImVec4*)&color, base_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80));\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorPicker\");\n        ImGui::SeparatorText(\"Color picker\");\n\n        static bool ref_color = false;\n        static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f);\n        static int picker_mode = 0;\n        static int display_mode = 0;\n        static ImGuiColorEditFlags color_picker_flags = ImGuiColorEditFlags_AlphaBar;\n\n        ImGui::PushID(\"Color picker\");\n        ImGui::CheckboxFlags(\"ImGuiColorEditFlags_NoAlpha\", &color_picker_flags, ImGuiColorEditFlags_NoAlpha);\n        ImGui::CheckboxFlags(\"ImGuiColorEditFlags_AlphaBar\", &color_picker_flags, ImGuiColorEditFlags_AlphaBar);\n        ImGui::CheckboxFlags(\"ImGuiColorEditFlags_NoSidePreview\", &color_picker_flags, ImGuiColorEditFlags_NoSidePreview);\n        if (color_picker_flags & ImGuiColorEditFlags_NoSidePreview)\n        {\n            ImGui::SameLine();\n            ImGui::Checkbox(\"With Ref Color\", &ref_color);\n            if (ref_color)\n            {\n                ImGui::SameLine();\n                ImGui::ColorEdit4(\"##RefColor\", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | base_flags);\n            }\n        }\n\n        ImGui::Combo(\"Picker Mode\", &picker_mode, \"Auto/Current\\0ImGuiColorEditFlags_PickerHueBar\\0ImGuiColorEditFlags_PickerHueWheel\\0\");\n        ImGui::SameLine(); HelpMarker(\"When not specified explicitly, user can right-click the picker to change mode.\");\n\n        ImGui::Combo(\"Display Mode\", &display_mode, \"Auto/Current\\0ImGuiColorEditFlags_NoInputs\\0ImGuiColorEditFlags_DisplayRGB\\0ImGuiColorEditFlags_DisplayHSV\\0ImGuiColorEditFlags_DisplayHex\\0\");\n        ImGui::SameLine(); HelpMarker(\n            \"ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, \"\n            \"but the user can change it with a right-click on those inputs.\\n\\nColorPicker defaults to displaying RGB+HSV+Hex \"\n            \"if you don't specify a display mode.\\n\\nYou can change the defaults using SetColorEditOptions().\");\n\n        ImGuiColorEditFlags flags = base_flags | color_picker_flags;\n        if (picker_mode == 1)  flags |= ImGuiColorEditFlags_PickerHueBar;\n        if (picker_mode == 2)  flags |= ImGuiColorEditFlags_PickerHueWheel;\n        if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs;       // Disable all RGB/HSV/Hex displays\n        if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB;     // Override display mode\n        if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV;\n        if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex;\n        ImGui::ColorPicker4(\"MyColor##4\", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL);\n\n        ImGui::Text(\"Set defaults in code:\");\n        ImGui::SameLine(); HelpMarker(\n            \"SetColorEditOptions() is designed to allow you to set boot-time default.\\n\"\n            \"We don't have Push/Pop functions because you can force options on a per-widget basis if needed, \"\n            \"and the user can change non-forced ones with the options menu.\\nWe don't have a getter to avoid \"\n            \"encouraging you to persistently save values that aren't forward-compatible.\");\n        if (ImGui::Button(\"Default: Uint8 + HSV + Hue Bar\"))\n            ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar);\n        if (ImGui::Button(\"Default: Float + HDR + Hue Wheel\"))\n            ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel);\n\n        // Always display a small version of both types of pickers\n        // (that's in order to make it more visible in the demo to people who are skimming quickly through it)\n        ImGui::Text(\"Both types:\");\n        float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f;\n        ImGui::SetNextItemWidth(w);\n        ImGui::ColorPicker3(\"##MyColor##5\", (float*)&color, ImGuiColorEditFlags_PickerHueBar | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);\n        ImGui::SameLine();\n        ImGui::SetNextItemWidth(w);\n        ImGui::ColorPicker3(\"##MyColor##6\", (float*)&color, ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);\n        ImGui::PopID();\n\n        // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0)\n        static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV!\n        ImGui::Spacing();\n        ImGui::Text(\"HSV encoded colors\");\n        ImGui::SameLine(); HelpMarker(\n            \"By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV \"\n            \"allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the \"\n            \"added benefit that you can manipulate hue values with the picker even when saturation or value are zero.\");\n        ImGui::Text(\"Color widget with InputHSV:\");\n        ImGui::ColorEdit4(\"HSV shown as RGB##1\", (float*)&color_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);\n        ImGui::ColorEdit4(\"HSV shown as HSV##1\", (float*)&color_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);\n        ImGui::DragFloat4(\"Raw HSV values\", (float*)&color_hsv, 0.01f, 0.0f, 1.0f);\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsComboBoxes()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsComboBoxes()\n{\n    if (ImGui::TreeNode(\"Combo\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Combo\");\n        // Combo Boxes are also called \"Dropdown\" in other systems\n        // Expose flags as checkbox for the demo\n        static ImGuiComboFlags flags = 0;\n        ImGui::CheckboxFlags(\"ImGuiComboFlags_PopupAlignLeft\", &flags, ImGuiComboFlags_PopupAlignLeft);\n        ImGui::SameLine(); HelpMarker(\"Only makes a difference if the popup is larger than the combo\");\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_NoArrowButton\", &flags, ImGuiComboFlags_NoArrowButton))\n            flags &= ~ImGuiComboFlags_NoPreview;     // Clear incompatible flags\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_NoPreview\", &flags, ImGuiComboFlags_NoPreview))\n            flags &= ~(ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_WidthFitPreview); // Clear incompatible flags\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_WidthFitPreview\", &flags, ImGuiComboFlags_WidthFitPreview))\n            flags &= ~ImGuiComboFlags_NoPreview;\n\n        // Override default popup height\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_HeightSmall\", &flags, ImGuiComboFlags_HeightSmall))\n            flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightSmall);\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_HeightRegular\", &flags, ImGuiComboFlags_HeightRegular))\n            flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightRegular);\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_HeightLargest\", &flags, ImGuiComboFlags_HeightLargest))\n            flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightLargest);\n\n        // Using the generic BeginCombo() API, you have full control over how to display the combo contents.\n        // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively\n        // stored in the object itself, etc.)\n        const char* items[] = { \"AAAA\", \"BBBB\", \"CCCC\", \"DDDD\", \"EEEE\", \"FFFF\", \"GGGG\", \"HHHH\", \"IIII\", \"JJJJ\", \"KKKK\", \"LLLLLLL\", \"MMMM\", \"OOOOOOO\" };\n        static int item_selected_idx = 0; // Here we store our selection data as an index.\n\n        // Pass in the preview value visible before opening the combo (it could technically be different contents or not pulled from items[])\n        const char* combo_preview_value = items[item_selected_idx];\n        if (ImGui::BeginCombo(\"combo 1\", combo_preview_value, flags))\n        {\n            for (int n = 0; n < IM_COUNTOF(items); n++)\n            {\n                const bool is_selected = (item_selected_idx == n);\n                if (ImGui::Selectable(items[n], is_selected))\n                    item_selected_idx = n;\n\n                // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)\n                if (is_selected)\n                    ImGui::SetItemDefaultFocus();\n            }\n            ImGui::EndCombo();\n        }\n\n        // Show case embedding a filter using a simple trick: displaying the filter inside combo contents.\n        // See https://github.com/ocornut/imgui/issues/718 for advanced/esoteric alternatives.\n        if (ImGui::BeginCombo(\"combo 2 (w/ filter)\", combo_preview_value, flags))\n        {\n            static ImGuiTextFilter filter;\n            if (ImGui::IsWindowAppearing())\n            {\n                ImGui::SetKeyboardFocusHere();\n                filter.Clear();\n            }\n            ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F);\n            filter.Draw(\"##Filter\", -FLT_MIN);\n\n            for (int n = 0; n < IM_COUNTOF(items); n++)\n            {\n                const bool is_selected = (item_selected_idx == n);\n                if (filter.PassFilter(items[n]))\n                    if (ImGui::Selectable(items[n], is_selected))\n                        item_selected_idx = n;\n            }\n            ImGui::EndCombo();\n        }\n\n        ImGui::Spacing();\n        ImGui::SeparatorText(\"One-liner variants\");\n        HelpMarker(\"The Combo() function is not greatly useful apart from cases were you want to embed all options in a single strings.\\nFlags above don't apply to this section.\");\n\n        // Simplified one-liner Combo() API, using values packed in a single constant string\n        // This is a convenience for when the selection set is small and known at compile-time.\n        static int item_current_2 = 0;\n        ImGui::Combo(\"combo 3 (one-liner)\", &item_current_2, \"aaaa\\0bbbb\\0cccc\\0dddd\\0eeee\\0\\0\");\n\n        // Simplified one-liner Combo() using an array of const char*\n        // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control.\n        static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview\n        ImGui::Combo(\"combo 4 (array)\", &item_current_3, items, IM_COUNTOF(items));\n\n        // Simplified one-liner Combo() using an accessor function\n        static int item_current_4 = 0;\n        ImGui::Combo(\"combo 5 (function)\", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_COUNTOF(items));\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsDataTypes()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsDataTypes()\n{\n    if (ImGui::TreeNode(\"Data Types\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Data Types\");\n        // DragScalar/InputScalar/SliderScalar functions allow various data types\n        // - signed/unsigned\n        // - 8/16/32/64-bits\n        // - integer/float/double\n        // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum\n        // to pass the type, and passing all arguments by pointer.\n        // This is the reason the test code below creates local variables to hold \"zero\" \"one\" etc. for each type.\n        // In practice, if you frequently use a given type that is not covered by the normal API entry points,\n        // you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*,\n        // and then pass their address to the generic function. For example:\n        //   bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = \"%lld\")\n        //   {\n        //      return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format);\n        //   }\n\n        // Setup limits (as helper variables so we can take their address, as explained above)\n        // Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2.\n        #ifndef LLONG_MIN\n        ImS64 LLONG_MIN = -9223372036854775807LL - 1;\n        ImS64 LLONG_MAX = 9223372036854775807LL;\n        ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1);\n        #endif\n        const char    s8_zero  = 0,   s8_one  = 1,   s8_fifty  = 50, s8_min  = -128,        s8_max = 127;\n        const ImU8    u8_zero  = 0,   u8_one  = 1,   u8_fifty  = 50, u8_min  = 0,           u8_max = 255;\n        const short   s16_zero = 0,   s16_one = 1,   s16_fifty = 50, s16_min = -32768,      s16_max = 32767;\n        const ImU16   u16_zero = 0,   u16_one = 1,   u16_fifty = 50, u16_min = 0,           u16_max = 65535;\n        const ImS32   s32_zero = 0,   s32_one = 1,   s32_fifty = 50, s32_min = INT_MIN/2,   s32_max = INT_MAX/2,    s32_hi_a = INT_MAX/2 - 100,    s32_hi_b = INT_MAX/2;\n        const ImU32   u32_zero = 0,   u32_one = 1,   u32_fifty = 50, u32_min = 0,           u32_max = UINT_MAX/2,   u32_hi_a = UINT_MAX/2 - 100,   u32_hi_b = UINT_MAX/2;\n        const ImS64   s64_zero = 0,   s64_one = 1,   s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2,  s64_hi_a = LLONG_MAX/2 - 100,  s64_hi_b = LLONG_MAX/2;\n        const ImU64   u64_zero = 0,   u64_one = 1,   u64_fifty = 50, u64_min = 0,           u64_max = ULLONG_MAX/2, u64_hi_a = ULLONG_MAX/2 - 100, u64_hi_b = ULLONG_MAX/2;\n        const float   f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f;\n        const double  f64_zero = 0.,  f64_one = 1.,  f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0;\n\n        // State\n        static char   s8_v  = 127;\n        static ImU8   u8_v  = 255;\n        static short  s16_v = 32767;\n        static ImU16  u16_v = 65535;\n        static ImS32  s32_v = -1;\n        static ImU32  u32_v = (ImU32)-1;\n        static ImS64  s64_v = -1;\n        static ImU64  u64_v = (ImU64)-1;\n        static float  f32_v = 0.123f;\n        static double f64_v = 90000.01234567890123456789;\n\n        const float drag_speed = 0.2f;\n        static bool drag_clamp = false;\n        IMGUI_DEMO_MARKER(\"Widgets/Data Types/Drags\");\n        ImGui::SeparatorText(\"Drags\");\n        ImGui::Checkbox(\"Clamp integers to 0..50\", &drag_clamp);\n        ImGui::SameLine(); HelpMarker(\n            \"As with every widget in dear imgui, we never modify values unless there is a user interaction.\\n\"\n            \"You can override the clamping limits by using Ctrl+Click to input a value.\");\n        ImGui::DragScalar(\"drag s8\",        ImGuiDataType_S8,     &s8_v,  drag_speed, drag_clamp ? &s8_zero  : NULL, drag_clamp ? &s8_fifty  : NULL);\n        ImGui::DragScalar(\"drag u8\",        ImGuiDataType_U8,     &u8_v,  drag_speed, drag_clamp ? &u8_zero  : NULL, drag_clamp ? &u8_fifty  : NULL, \"%u ms\");\n        ImGui::DragScalar(\"drag s16\",       ImGuiDataType_S16,    &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL);\n        ImGui::DragScalar(\"drag u16\",       ImGuiDataType_U16,    &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, \"%u ms\");\n        ImGui::DragScalar(\"drag s32\",       ImGuiDataType_S32,    &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL);\n        ImGui::DragScalar(\"drag s32 hex\",   ImGuiDataType_S32,    &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL, \"0x%08X\");\n        ImGui::DragScalar(\"drag u32\",       ImGuiDataType_U32,    &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, \"%u ms\");\n        ImGui::DragScalar(\"drag s64\",       ImGuiDataType_S64,    &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL);\n        ImGui::DragScalar(\"drag u64\",       ImGuiDataType_U64,    &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL);\n        ImGui::DragScalar(\"drag float\",     ImGuiDataType_Float,  &f32_v, 0.005f,  &f32_zero, &f32_one, \"%f\");\n        ImGui::DragScalar(\"drag float log\", ImGuiDataType_Float,  &f32_v, 0.005f,  &f32_zero, &f32_one, \"%f\", ImGuiSliderFlags_Logarithmic);\n        ImGui::DragScalar(\"drag double\",    ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL,     \"%.10f grams\");\n        ImGui::DragScalar(\"drag double log\",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, \"0 < %.10f < 1\", ImGuiSliderFlags_Logarithmic);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Data Types/Sliders\");\n        ImGui::SeparatorText(\"Sliders\");\n        ImGui::SliderScalar(\"slider s8 full\",       ImGuiDataType_S8,     &s8_v,  &s8_min,   &s8_max,   \"%d\");\n        ImGui::SliderScalar(\"slider u8 full\",       ImGuiDataType_U8,     &u8_v,  &u8_min,   &u8_max,   \"%u\");\n        ImGui::SliderScalar(\"slider s16 full\",      ImGuiDataType_S16,    &s16_v, &s16_min,  &s16_max,  \"%d\");\n        ImGui::SliderScalar(\"slider u16 full\",      ImGuiDataType_U16,    &u16_v, &u16_min,  &u16_max,  \"%u\");\n        ImGui::SliderScalar(\"slider s32 low\",       ImGuiDataType_S32,    &s32_v, &s32_zero, &s32_fifty,\"%d\");\n        ImGui::SliderScalar(\"slider s32 high\",      ImGuiDataType_S32,    &s32_v, &s32_hi_a, &s32_hi_b, \"%d\");\n        ImGui::SliderScalar(\"slider s32 full\",      ImGuiDataType_S32,    &s32_v, &s32_min,  &s32_max,  \"%d\");\n        ImGui::SliderScalar(\"slider s32 hex\",       ImGuiDataType_S32,    &s32_v, &s32_zero, &s32_fifty, \"0x%04X\");\n        ImGui::SliderScalar(\"slider u32 low\",       ImGuiDataType_U32,    &u32_v, &u32_zero, &u32_fifty,\"%u\");\n        ImGui::SliderScalar(\"slider u32 high\",      ImGuiDataType_U32,    &u32_v, &u32_hi_a, &u32_hi_b, \"%u\");\n        ImGui::SliderScalar(\"slider u32 full\",      ImGuiDataType_U32,    &u32_v, &u32_min,  &u32_max,  \"%u\");\n        ImGui::SliderScalar(\"slider s64 low\",       ImGuiDataType_S64,    &s64_v, &s64_zero, &s64_fifty,\"%\" PRId64);\n        ImGui::SliderScalar(\"slider s64 high\",      ImGuiDataType_S64,    &s64_v, &s64_hi_a, &s64_hi_b, \"%\" PRId64);\n        ImGui::SliderScalar(\"slider s64 full\",      ImGuiDataType_S64,    &s64_v, &s64_min,  &s64_max,  \"%\" PRId64);\n        ImGui::SliderScalar(\"slider u64 low\",       ImGuiDataType_U64,    &u64_v, &u64_zero, &u64_fifty,\"%\" PRIu64 \" ms\");\n        ImGui::SliderScalar(\"slider u64 high\",      ImGuiDataType_U64,    &u64_v, &u64_hi_a, &u64_hi_b, \"%\" PRIu64 \" ms\");\n        ImGui::SliderScalar(\"slider u64 full\",      ImGuiDataType_U64,    &u64_v, &u64_min,  &u64_max,  \"%\" PRIu64 \" ms\");\n        ImGui::SliderScalar(\"slider float low\",     ImGuiDataType_Float,  &f32_v, &f32_zero, &f32_one);\n        ImGui::SliderScalar(\"slider float low log\", ImGuiDataType_Float,  &f32_v, &f32_zero, &f32_one,  \"%.10f\", ImGuiSliderFlags_Logarithmic);\n        ImGui::SliderScalar(\"slider float high\",    ImGuiDataType_Float,  &f32_v, &f32_lo_a, &f32_hi_a, \"%e\");\n        ImGui::SliderScalar(\"slider double low\",    ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one,  \"%.10f grams\");\n        ImGui::SliderScalar(\"slider double low log\",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one,  \"%.10f\", ImGuiSliderFlags_Logarithmic);\n        ImGui::SliderScalar(\"slider double high\",   ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, \"%e grams\");\n\n        ImGui::SeparatorText(\"Sliders (reverse)\");\n        ImGui::SliderScalar(\"slider s8 reverse\",    ImGuiDataType_S8,   &s8_v,  &s8_max,    &s8_min,   \"%d\");\n        ImGui::SliderScalar(\"slider u8 reverse\",    ImGuiDataType_U8,   &u8_v,  &u8_max,    &u8_min,   \"%u\");\n        ImGui::SliderScalar(\"slider s32 reverse\",   ImGuiDataType_S32,  &s32_v, &s32_fifty, &s32_zero, \"%d\");\n        ImGui::SliderScalar(\"slider u32 reverse\",   ImGuiDataType_U32,  &u32_v, &u32_fifty, &u32_zero, \"%u\");\n        ImGui::SliderScalar(\"slider s64 reverse\",   ImGuiDataType_S64,  &s64_v, &s64_fifty, &s64_zero, \"%\" PRId64);\n        ImGui::SliderScalar(\"slider u64 reverse\",   ImGuiDataType_U64,  &u64_v, &u64_fifty, &u64_zero, \"%\" PRIu64 \" ms\");\n\n        IMGUI_DEMO_MARKER(\"Widgets/Data Types/Inputs\");\n        static bool inputs_step = true;\n        static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;\n        ImGui::SeparatorText(\"Inputs\");\n        ImGui::Checkbox(\"Show step buttons\", &inputs_step);\n        ImGui::CheckboxFlags(\"ImGuiInputTextFlags_ReadOnly\", &flags, ImGuiInputTextFlags_ReadOnly);\n        ImGui::CheckboxFlags(\"ImGuiInputTextFlags_ParseEmptyRefVal\", &flags, ImGuiInputTextFlags_ParseEmptyRefVal);\n        ImGui::CheckboxFlags(\"ImGuiInputTextFlags_DisplayEmptyRefVal\", &flags, ImGuiInputTextFlags_DisplayEmptyRefVal);\n        ImGui::InputScalar(\"input s8\",      ImGuiDataType_S8,     &s8_v,  inputs_step ? &s8_one  : NULL, NULL, \"%d\", flags);\n        ImGui::InputScalar(\"input u8\",      ImGuiDataType_U8,     &u8_v,  inputs_step ? &u8_one  : NULL, NULL, \"%u\", flags);\n        ImGui::InputScalar(\"input s16\",     ImGuiDataType_S16,    &s16_v, inputs_step ? &s16_one : NULL, NULL, \"%d\", flags);\n        ImGui::InputScalar(\"input u16\",     ImGuiDataType_U16,    &u16_v, inputs_step ? &u16_one : NULL, NULL, \"%u\", flags);\n        ImGui::InputScalar(\"input s32\",     ImGuiDataType_S32,    &s32_v, inputs_step ? &s32_one : NULL, NULL, \"%d\", flags);\n        ImGui::InputScalar(\"input s32 hex\", ImGuiDataType_S32,    &s32_v, inputs_step ? &s32_one : NULL, NULL, \"%04X\", flags);\n        ImGui::InputScalar(\"input u32\",     ImGuiDataType_U32,    &u32_v, inputs_step ? &u32_one : NULL, NULL, \"%u\", flags);\n        ImGui::InputScalar(\"input u32 hex\", ImGuiDataType_U32,    &u32_v, inputs_step ? &u32_one : NULL, NULL, \"%08X\", flags);\n        ImGui::InputScalar(\"input s64\",     ImGuiDataType_S64,    &s64_v, inputs_step ? &s64_one : NULL, NULL, NULL, flags);\n        ImGui::InputScalar(\"input u64\",     ImGuiDataType_U64,    &u64_v, inputs_step ? &u64_one : NULL, NULL, NULL, flags);\n        ImGui::InputScalar(\"input float\",   ImGuiDataType_Float,  &f32_v, inputs_step ? &f32_one : NULL, NULL, NULL, flags);\n        ImGui::InputScalar(\"input double\",  ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL, NULL, NULL, flags);\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsDisableBlocks()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsDisableBlocks(ImGuiDemoWindowData* demo_data)\n{\n    if (ImGui::TreeNode(\"Disable Blocks\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Disable Blocks\");\n        ImGui::Checkbox(\"Disable entire section above\", &demo_data->DisableSections);\n        ImGui::SameLine(); HelpMarker(\"Demonstrate using BeginDisabled()/EndDisabled() across other sections.\");\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsDragAndDrop()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsDragAndDrop()\n{\n    if (ImGui::TreeNode(\"Drag and Drop\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Drag and drop\");\n        if (ImGui::TreeNode(\"Drag and drop in standard widgets\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Drag and drop/Standard widgets\");\n            // ColorEdit widgets automatically act as drag source and drag target.\n            // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F\n            // to allow your own widgets to use colors in their drag and drop interaction.\n            // Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo.\n            HelpMarker(\"You can drag from the color squares.\");\n            static float col1[3] = { 1.0f, 0.0f, 0.2f };\n            static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };\n            ImGui::ColorEdit3(\"color 1\", col1);\n            ImGui::ColorEdit4(\"color 2\", col2);\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Drag and drop to copy/swap items\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Drag and drop/Copy-swap items\");\n            enum Mode\n            {\n                Mode_Copy,\n                Mode_Move,\n                Mode_Swap\n            };\n            static int mode = 0;\n            if (ImGui::RadioButton(\"Copy\", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine();\n            if (ImGui::RadioButton(\"Move\", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine();\n            if (ImGui::RadioButton(\"Swap\", mode == Mode_Swap)) { mode = Mode_Swap; }\n            static const char* names[9] =\n            {\n                \"Bobby\", \"Beatrice\", \"Betty\",\n                \"Brianna\", \"Barry\", \"Bernard\",\n                \"Bibi\", \"Blaine\", \"Bryn\"\n            };\n            for (int n = 0; n < IM_COUNTOF(names); n++)\n            {\n                ImGui::PushID(n);\n                if ((n % 3) != 0)\n                    ImGui::SameLine();\n                ImGui::Button(names[n], ImVec2(60, 60));\n\n                // Our buttons are both drag sources and drag targets here!\n                if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))\n                {\n                    // Set payload to carry the index of our item (could be anything)\n                    ImGui::SetDragDropPayload(\"DND_DEMO_CELL\", &n, sizeof(int));\n\n                    // Display preview (could be anything, e.g. when dragging an image we could decide to display\n                    // the filename and a small preview of the image, etc.)\n                    if (mode == Mode_Copy) { ImGui::Text(\"Copy %s\", names[n]); }\n                    if (mode == Mode_Move) { ImGui::Text(\"Move %s\", names[n]); }\n                    if (mode == Mode_Swap) { ImGui::Text(\"Swap %s\", names[n]); }\n                    ImGui::EndDragDropSource();\n                }\n                if (ImGui::BeginDragDropTarget())\n                {\n                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(\"DND_DEMO_CELL\"))\n                    {\n                        IM_ASSERT(payload->DataSize == sizeof(int));\n                        int payload_n = *(const int*)payload->Data;\n                        if (mode == Mode_Copy)\n                        {\n                            names[n] = names[payload_n];\n                        }\n                        if (mode == Mode_Move)\n                        {\n                            names[n] = names[payload_n];\n                            names[payload_n] = \"\";\n                        }\n                        if (mode == Mode_Swap)\n                        {\n                            const char* tmp = names[n];\n                            names[n] = names[payload_n];\n                            names[payload_n] = tmp;\n                        }\n                    }\n                    ImGui::EndDragDropTarget();\n                }\n                ImGui::PopID();\n            }\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Drag to reorder items (simple)\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Drag and Drop/Drag to reorder items (simple)\");\n            // FIXME: there is temporary (usually single-frame) ID Conflict during reordering as a same item may be submitting twice.\n            // This code was always slightly faulty but in a way which was not easily noticeable.\n            // Until we fix this, enable ImGuiItemFlags_AllowDuplicateId to disable detecting the issue.\n            ImGui::PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true);\n\n            // Simple reordering\n            HelpMarker(\n                \"We don't use the drag and drop api at all here! \"\n                \"Instead we query when the item is held but not hovered, and order items accordingly.\");\n            static const char* item_names[] = { \"Item One\", \"Item Two\", \"Item Three\", \"Item Four\", \"Item Five\" };\n            for (int n = 0; n < IM_COUNTOF(item_names); n++)\n            {\n                const char* item = item_names[n];\n                ImGui::Selectable(item);\n\n                if (ImGui::IsItemActive() && !ImGui::IsItemHovered())\n                {\n                    int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1);\n                    if (n_next >= 0 && n_next < IM_COUNTOF(item_names))\n                    {\n                        item_names[n] = item_names[n_next];\n                        item_names[n_next] = item;\n                        ImGui::ResetMouseDragDelta();\n                    }\n                }\n            }\n\n            ImGui::PopItemFlag();\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Tooltip at target location\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Drag and Drop/Tooltip at target location\");\n            for (int n = 0; n < 2; n++)\n            {\n                // Drop targets\n                ImGui::Button(n ? \"drop here##1\" : \"drop here##0\");\n                if (ImGui::BeginDragDropTarget())\n                {\n                    ImGuiDragDropFlags drop_target_flags = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoPreviewTooltip;\n                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, drop_target_flags))\n                    {\n                        IM_UNUSED(payload);\n                        ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);\n                        ImGui::SetTooltip(\"Cannot drop here!\");\n                    }\n                    ImGui::EndDragDropTarget();\n                }\n\n                // Drop source\n                static ImVec4 col4 = { 1.0f, 0.0f, 0.2f, 1.0f };\n                if (n == 0)\n                    ImGui::ColorButton(\"drag me\", col4);\n\n            }\n            ImGui::TreePop();\n        }\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsDragsAndSliders()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsDragsAndSliders()\n{\n    if (ImGui::TreeNode(\"Drag/Slider Flags\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Drag and Slider Flags\");\n        // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!\n        static ImGuiSliderFlags flags = ImGuiSliderFlags_None;\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_AlwaysClamp\", &flags, ImGuiSliderFlags_AlwaysClamp);\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_ClampOnInput\", &flags, ImGuiSliderFlags_ClampOnInput);\n        ImGui::SameLine(); HelpMarker(\"Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds.\");\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_ClampZeroRange\", &flags, ImGuiSliderFlags_ClampZeroRange);\n        ImGui::SameLine(); HelpMarker(\"Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp.\");\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_Logarithmic\", &flags, ImGuiSliderFlags_Logarithmic);\n        ImGui::SameLine(); HelpMarker(\"Enable logarithmic editing (more precision for small values).\");\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_NoRoundToFormat\", &flags, ImGuiSliderFlags_NoRoundToFormat);\n        ImGui::SameLine(); HelpMarker(\"Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits).\");\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_NoInput\", &flags, ImGuiSliderFlags_NoInput);\n        ImGui::SameLine(); HelpMarker(\"Disable Ctrl+Click or Enter key allowing to input text directly into the widget.\");\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_NoSpeedTweaks\", &flags, ImGuiSliderFlags_NoSpeedTweaks);\n        ImGui::SameLine(); HelpMarker(\"Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.\");\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_WrapAround\", &flags, ImGuiSliderFlags_WrapAround);\n        ImGui::SameLine(); HelpMarker(\"Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)\");\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_ColorMarkers\", &flags, ImGuiSliderFlags_ColorMarkers);\n\n        // Drags\n        static float drag_f = 0.5f;\n        static float drag_f4[4];\n        static int drag_i = 50;\n        ImGui::Text(\"Underlying float value: %f\", drag_f);\n        ImGui::DragFloat(\"DragFloat (0 -> 1)\", &drag_f, 0.005f, 0.0f, 1.0f, \"%.3f\", flags);\n        ImGui::DragFloat(\"DragFloat (0 -> +inf)\", &drag_f, 0.005f, 0.0f, FLT_MAX, \"%.3f\", flags);\n        ImGui::DragFloat(\"DragFloat (-inf -> 1)\", &drag_f, 0.005f, -FLT_MAX, 1.0f, \"%.3f\", flags);\n        ImGui::DragFloat(\"DragFloat (-inf -> +inf)\", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, \"%.3f\", flags);\n        //ImGui::DragFloat(\"DragFloat (0 -> 0)\", &drag_f, 0.005f, 0.0f, 0.0f, \"%.3f\", flags);           // To test ClampZeroRange\n        //ImGui::DragFloat(\"DragFloat (100 -> 100)\", &drag_f, 0.005f, 100.0f, 100.0f, \"%.3f\", flags);\n        ImGui::DragInt(\"DragInt (0 -> 100)\", &drag_i, 0.5f, 0, 100, \"%d\", flags);\n        ImGui::DragFloat4(\"DragFloat4 (0 -> 1)\", drag_f4, 0.005f, 0.0f, 1.0f, \"%.3f\", flags); // Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers.\n\n        // Sliders\n        static float slider_f = 0.5f;\n        static float slider_f4[4];\n        static int slider_i = 50;\n        const ImGuiSliderFlags flags_for_sliders = (flags & ~ImGuiSliderFlags_WrapAround);\n        ImGui::Text(\"Underlying float value: %f\", slider_f);\n        ImGui::SliderFloat(\"SliderFloat (0 -> 1)\", &slider_f, 0.0f, 1.0f, \"%.3f\", flags_for_sliders);\n        ImGui::SliderInt(\"SliderInt (0 -> 100)\", &slider_i, 0, 100, \"%d\", flags_for_sliders);\n        ImGui::SliderFloat4(\"SliderFloat4 (0 -> 1)\", slider_f4, 0.0f, 1.0f, \"%.3f\", flags); // Multi-component item, mostly here to document the effect of ImGuiSliderFlags_ColorMarkers.\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsFonts()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsFonts()\n{\n    if (ImGui::TreeNode(\"Fonts\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Fonts\");\n        ImFontAtlas* atlas = ImGui::GetIO().Fonts;\n        ImGui::ShowFontAtlas(atlas);\n        // FIXME-NEWATLAS: Provide a demo to add/create a procedural font?\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsImages()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsImages()\n{\n    if (ImGui::TreeNode(\"Images\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Images\");\n        ImGuiIO& io = ImGui::GetIO();\n        ImGui::TextWrapped(\n            \"Below we are displaying the font texture (which is the only texture we have access to in this demo). \"\n            \"Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. \"\n            \"Hover the texture for a zoomed view!\");\n\n        // Below we are displaying the font texture because it is the only texture we have access to inside the demo!\n        // Read description about ImTextureID/ImTextureRef and FAQ for details about texture identifiers.\n        // If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top\n        // of their respective source file to specify what they are using as texture identifier, for example:\n        // - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer.\n        // - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc.\n        // So with the DirectX11 backend, you call ImGui::Image() with a 'ID3D11ShaderResourceView*' cast to ImTextureID.\n        // - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers\n        //   to ImGui::Image(), and gather width/height through your own functions, etc.\n        // - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer,\n        //   it will help you debug issues if you are confused about it.\n        // - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage().\n        // - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md\n        // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples\n\n        // Grab the current texture identifier used by the font atlas.\n        ImTextureRef my_tex_id = io.Fonts->TexRef;\n\n        // Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it.\n        float my_tex_w = (float)io.Fonts->TexData->Width;\n        float my_tex_h = (float)io.Fonts->TexData->Height;\n\n        {\n            ImGui::Text(\"%.0fx%.0f\", my_tex_w, my_tex_h);\n            ImVec2 pos = ImGui::GetCursorScreenPos();\n            ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left\n            ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right\n            ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize));\n            ImGui::ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));\n            if (ImGui::BeginItemTooltip())\n            {\n                float region_sz = 32.0f;\n                float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;\n                float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;\n                float zoom = 4.0f;\n                if (region_x < 0.0f) { region_x = 0.0f; }\n                else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; }\n                if (region_y < 0.0f) { region_y = 0.0f; }\n                else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; }\n                ImGui::Text(\"Min: (%.2f, %.2f)\", region_x, region_y);\n                ImGui::Text(\"Max: (%.2f, %.2f)\", region_x + region_sz, region_y + region_sz);\n                ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);\n                ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);\n                ImGui::ImageWithBg(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));\n                ImGui::EndTooltip();\n            }\n            ImGui::PopStyleVar();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Images/Textured buttons\");\n        ImGui::TextWrapped(\"And now some textured buttons..\");\n        static int pressed_count = 0;\n        for (int i = 0; i < 8; i++)\n        {\n            // UV coordinates are often (0.0f, 0.0f) and (1.0f, 1.0f) to display an entire textures.\n            // Here are trying to display only a 32x32 pixels area of the texture, hence the UV computation.\n            // Read about UV coordinates here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples\n            ImGui::PushID(i);\n            if (i > 0)\n                ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(i - 1.0f, i - 1.0f));\n            ImVec2 size = ImVec2(32.0f, 32.0f);                         // Size of the image we want to make visible\n            ImVec2 uv0 = ImVec2(0.0f, 0.0f);                            // UV coordinates for lower-left\n            ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32.0f / my_tex_h);    // UV coordinates for (32,32) in our texture\n            ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);             // Black background\n            ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);           // No tint\n            if (ImGui::ImageButton(\"\", my_tex_id, size, uv0, uv1, bg_col, tint_col))\n                pressed_count += 1;\n            if (i > 0)\n                ImGui::PopStyleVar();\n            ImGui::PopID();\n            ImGui::SameLine();\n        }\n        ImGui::NewLine();\n        ImGui::Text(\"Pressed %d times.\", pressed_count);\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsListBoxes()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsListBoxes()\n{\n    if (ImGui::TreeNode(\"List Boxes\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/List Boxes\");\n        // BeginListBox() is essentially a thin wrapper to using BeginChild()/EndChild()\n        // using the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.\n        // You may be tempted to simply use BeginChild() directly. However note that BeginChild() requires EndChild()\n        // to always be called (inconsistent with BeginListBox()/EndListBox()).\n\n        // Using the generic BeginListBox() API, you have full control over how to display the combo contents.\n        // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively\n        // stored in the object itself, etc.)\n        const char* items[] = { \"AAAA\", \"BBBB\", \"CCCC\", \"DDDD\", \"EEEE\", \"FFFF\", \"GGGG\", \"HHHH\", \"IIII\", \"JJJJ\", \"KKKK\", \"LLLLLLL\", \"MMMM\", \"OOOOOOO\" };\n        static int item_selected_idx = 0; // Here we store our selected data as an index.\n\n        static bool item_highlight = false;\n        int item_highlighted_idx = -1; // Here we store our highlighted data as an index.\n        ImGui::Checkbox(\"Highlight hovered item in second listbox\", &item_highlight);\n\n        if (ImGui::BeginListBox(\"listbox 1\"))\n        {\n            for (int n = 0; n < IM_COUNTOF(items); n++)\n            {\n                const bool is_selected = (item_selected_idx == n);\n                if (ImGui::Selectable(items[n], is_selected))\n                    item_selected_idx = n;\n\n                if (item_highlight && ImGui::IsItemHovered())\n                    item_highlighted_idx = n;\n\n                // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)\n                if (is_selected)\n                    ImGui::SetItemDefaultFocus();\n            }\n            ImGui::EndListBox();\n        }\n        ImGui::SameLine(); HelpMarker(\"Here we are sharing selection state between both boxes.\");\n\n        // Custom size: use all width, 5 items tall\n        ImGui::Text(\"Full-width:\");\n        if (ImGui::BeginListBox(\"##listbox 2\", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing())))\n        {\n            for (int n = 0; n < IM_COUNTOF(items); n++)\n            {\n                bool is_selected = (item_selected_idx == n);\n                ImGuiSelectableFlags flags = (item_highlighted_idx == n) ? ImGuiSelectableFlags_Highlight : 0;\n                if (ImGui::Selectable(items[n], is_selected, flags))\n                    item_selected_idx = n;\n\n                // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)\n                if (is_selected)\n                    ImGui::SetItemDefaultFocus();\n            }\n            ImGui::EndListBox();\n        }\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsMultiComponents()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsMultiComponents()\n{\n    if (ImGui::TreeNode(\"Multi-component Widgets\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Multi-component Widgets\");\n        static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f };\n        static int vec4i[4] = { 1, 5, 100, 255 };\n\n        static ImGuiSliderFlags flags = 0;\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_ColorMarkers\", &flags, ImGuiSliderFlags_ColorMarkers); // Only passing this to Drag/Sliders\n\n        ImGui::SeparatorText(\"2-wide\");\n        ImGui::InputFloat2(\"input float2\", vec4f);\n        ImGui::InputInt2(\"input int2\", vec4i);\n        ImGui::DragFloat2(\"drag float2\", vec4f, 0.01f, 0.0f, 1.0f, NULL, flags);\n        ImGui::DragInt2(\"drag int2\", vec4i, 1, 0, 255, NULL, flags);\n        ImGui::SliderFloat2(\"slider float2\", vec4f, 0.0f, 1.0f, NULL, flags);\n        ImGui::SliderInt2(\"slider int2\", vec4i, 0, 255, NULL, flags);\n\n        ImGui::SeparatorText(\"3-wide\");\n        ImGui::InputFloat3(\"input float3\", vec4f);\n        ImGui::InputInt3(\"input int3\", vec4i);\n        ImGui::DragFloat3(\"drag float3\", vec4f, 0.01f, 0.0f, 1.0f, NULL, flags);\n        ImGui::DragInt3(\"drag int3\", vec4i, 1, 0, 255, NULL, flags);\n        ImGui::SliderFloat3(\"slider float3\", vec4f, 0.0f, 1.0f, NULL, flags);\n        ImGui::SliderInt3(\"slider int3\", vec4i, 0, 255, NULL, flags);\n\n        ImGui::SeparatorText(\"4-wide\");\n        ImGui::InputFloat4(\"input float4\", vec4f);\n        ImGui::InputInt4(\"input int4\", vec4i);\n        ImGui::DragFloat4(\"drag float4\", vec4f, 0.01f, 0.0f, 1.0f, NULL, flags);\n        ImGui::DragInt4(\"drag int4\", vec4i, 1, 0, 255, NULL, flags);\n        ImGui::SliderFloat4(\"slider float4\", vec4f, 0.0f, 1.0f, NULL, flags);\n        ImGui::SliderInt4(\"slider int4\", vec4i, 0, 255, NULL, flags);\n\n        ImGui::SeparatorText(\"Ranges\");\n        static float begin = 10, end = 90;\n        static int begin_i = 100, end_i = 1000;\n        ImGui::DragFloatRange2(\"range float\", &begin, &end, 0.25f, 0.0f, 100.0f, \"Min: %.1f %%\", \"Max: %.1f %%\", ImGuiSliderFlags_AlwaysClamp);\n        ImGui::DragIntRange2(\"range int\", &begin_i, &end_i, 5, 0, 1000, \"Min: %d units\", \"Max: %d units\");\n        ImGui::DragIntRange2(\"range int (no bounds)\", &begin_i, &end_i, 5, 0, 0, \"Min: %d units\", \"Max: %d units\");\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsPlotting()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsPlotting()\n{\n    // Plot/Graph widgets are not very good.\n// Consider using a third-party library such as ImPlot: https://github.com/epezent/implot\n// (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions)\n    if (ImGui::TreeNode(\"Plotting\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Plotting\");\n        ImGui::Text(\"Need better plotting and graphing? Consider using ImPlot:\");\n        ImGui::TextLinkOpenURL(\"https://github.com/epezent/implot\");\n        ImGui::Separator();\n\n        static bool animate = true;\n        ImGui::Checkbox(\"Animate\", &animate);\n\n        // Plot as lines and plot as histogram\n        static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };\n        ImGui::PlotLines(\"Frame Times\", arr, IM_COUNTOF(arr));\n        ImGui::PlotHistogram(\"Histogram\", arr, IM_COUNTOF(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f));\n        //ImGui::SameLine(); HelpMarker(\"Consider using ImPlot instead!\");\n\n        // Fill an array of contiguous float values to plot\n        // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float\n        // and the sizeof() of your structure in the \"stride\" parameter.\n        static float values[90] = {};\n        static int values_offset = 0;\n        static double refresh_time = 0.0;\n        if (!animate || refresh_time == 0.0)\n            refresh_time = ImGui::GetTime();\n        while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo\n        {\n            static float phase = 0.0f;\n            values[values_offset] = cosf(phase);\n            values_offset = (values_offset + 1) % IM_COUNTOF(values);\n            phase += 0.10f * values_offset;\n            refresh_time += 1.0f / 60.0f;\n        }\n\n        // Plots can display overlay texts\n        // (in this example, we will display an average value)\n        {\n            float average = 0.0f;\n            for (int n = 0; n < IM_COUNTOF(values); n++)\n                average += values[n];\n            average /= (float)IM_COUNTOF(values);\n            char overlay[32];\n            sprintf(overlay, \"avg %f\", average);\n            ImGui::PlotLines(\"Lines\", values, IM_COUNTOF(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f));\n        }\n\n        // Use functions to generate output\n        // FIXME: This is actually VERY awkward because current plot API only pass in indices.\n        // We probably want an API passing floats and user provide sample rate/count.\n        struct Funcs\n        {\n            static float Sin(void*, int i) { return sinf(i * 0.1f); }\n            static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; }\n        };\n        static int func_type = 0, display_count = 70;\n        ImGui::SeparatorText(\"Functions\");\n        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n        ImGui::Combo(\"func\", &func_type, \"Sin\\0Saw\\0\");\n        ImGui::SameLine();\n        ImGui::SliderInt(\"Sample count\", &display_count, 1, 400);\n        float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw;\n        ImGui::PlotLines(\"Lines##2\", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));\n        ImGui::PlotHistogram(\"Histogram##2\", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsProgressBars()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsProgressBars()\n{\n    if (ImGui::TreeNode(\"Progress Bars\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Progress Bars\");\n        // Animate a simple progress bar\n        static float progress_accum = 0.0f, progress_dir = 1.0f;\n        progress_accum += progress_dir * 0.4f * ImGui::GetIO().DeltaTime;\n        if (progress_accum >= +1.1f) { progress_accum = +1.1f; progress_dir *= -1.0f; }\n        if (progress_accum <= -0.1f) { progress_accum = -0.1f; progress_dir *= -1.0f; }\n\n        const float progress = IM_CLAMP(progress_accum, 0.0f, 1.0f);\n\n        // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width,\n        // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth.\n        ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f));\n        ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);\n        ImGui::Text(\"Progress Bar\");\n\n        char buf[32];\n        sprintf(buf, \"%d/%d\", (int)(progress * 1753), 1753);\n        ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf);\n\n        // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value.\n        // Adjust the factor if you want to adjust the animation speed.\n        ImGui::ProgressBar(-1.0f * (float)ImGui::GetTime(), ImVec2(0.0f, 0.0f), \"Searching..\");\n        ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);\n        ImGui::Text(\"Indeterminate\");\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsQueryingStatuses()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsQueryingStatuses()\n{\n    if (ImGui::TreeNode(\"Querying Item Status (Edited/Active/Hovered etc.)\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Querying Item Status (Edited,Active,Hovered etc.)\");\n        // Select an item type\n        const char* item_names[] =\n        {\n            \"Text\", \"Button\", \"Button (w/ repeat)\", \"Checkbox\", \"SliderFloat\", \"InputText\", \"InputTextMultiline\", \"InputFloat\",\n            \"InputFloat3\", \"ColorEdit4\", \"Selectable\", \"MenuItem\", \"TreeNode\", \"TreeNode (w/ double-click)\", \"Combo\", \"ListBox\"\n        };\n        static int item_type = 4;\n        static bool item_disabled = false;\n        ImGui::Combo(\"Item Type\", &item_type, item_names, IM_COUNTOF(item_names), IM_COUNTOF(item_names));\n        ImGui::SameLine();\n        HelpMarker(\"Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered().\");\n        ImGui::Checkbox(\"Item Disabled\", &item_disabled);\n\n        // Submit selected items so we can query their status in the code following it.\n        bool ret = false;\n        static bool b = false;\n        static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f };\n        static char str[16] = {};\n        if (item_disabled)\n            ImGui::BeginDisabled(true);\n        if (item_type == 0) { ImGui::Text(\"ITEM: Text\"); }                                              // Testing text items with no identifier/interaction\n        if (item_type == 1) { ret = ImGui::Button(\"ITEM: Button\"); }                                    // Testing button\n        if (item_type == 2) { ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); ret = ImGui::Button(\"ITEM: Button\"); ImGui::PopItemFlag(); } // Testing button (with repeater)\n        if (item_type == 3) { ret = ImGui::Checkbox(\"ITEM: Checkbox\", &b); }                            // Testing checkbox\n        if (item_type == 4) { ret = ImGui::SliderFloat(\"ITEM: SliderFloat\", &col4f[0], 0.0f, 1.0f); }   // Testing basic item\n        if (item_type == 5) { ret = ImGui::InputText(\"ITEM: InputText\", &str[0], IM_COUNTOF(str)); }  // Testing input text (which handles tabbing)\n        if (item_type == 6) { ret = ImGui::InputTextMultiline(\"ITEM: InputTextMultiline\", &str[0], IM_COUNTOF(str)); } // Testing input text (which uses a child window)\n        if (item_type == 7) { ret = ImGui::InputFloat(\"ITEM: InputFloat\", col4f, 1.0f); }               // Testing +/- buttons on scalar input\n        if (item_type == 8) { ret = ImGui::InputFloat3(\"ITEM: InputFloat3\", col4f); }                   // Testing multi-component items (IsItemXXX flags are reported merged)\n        if (item_type == 9) { ret = ImGui::ColorEdit4(\"ITEM: ColorEdit4\", col4f); }                     // Testing multi-component items (IsItemXXX flags are reported merged)\n        if (item_type == 10) { ret = ImGui::Selectable(\"ITEM: Selectable\"); }                            // Testing selectable item\n        if (item_type == 11) { ret = ImGui::MenuItem(\"ITEM: MenuItem\"); }                                // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy)\n        if (item_type == 12) { ret = ImGui::TreeNode(\"ITEM: TreeNode\"); if (ret) ImGui::TreePop(); }     // Testing tree node\n        if (item_type == 13) { ret = ImGui::TreeNodeEx(\"ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick\", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy.\n        if (item_type == 14) { const char* items[] = { \"Apple\", \"Banana\", \"Cherry\", \"Kiwi\" }; static int current = 1; ret = ImGui::Combo(\"ITEM: Combo\", &current, items, IM_COUNTOF(items)); }\n        if (item_type == 15) { const char* items[] = { \"Apple\", \"Banana\", \"Cherry\", \"Kiwi\" }; static int current = 1; ret = ImGui::ListBox(\"ITEM: ListBox\", &current, items, IM_COUNTOF(items), IM_COUNTOF(items)); }\n\n        bool hovered_delay_none = ImGui::IsItemHovered();\n        bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary);\n        bool hovered_delay_short = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort);\n        bool hovered_delay_normal = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal);\n        bool hovered_delay_tooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary\n\n        // Display the values of IsItemHovered() and other common item state functions.\n        // Note that the ImGuiHoveredFlags_XXX flags can be combined.\n        // Because BulletText is an item itself and that would affect the output of IsItemXXX functions,\n        // we query every state in a single call to avoid storing them and to simplify the code.\n        ImGui::BulletText(\n            \"Return value = %d\\n\"\n            \"IsItemFocused() = %d\\n\"\n            \"IsItemHovered() = %d\\n\"\n            \"IsItemHovered(_AllowWhenBlockedByPopup) = %d\\n\"\n            \"IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\\n\"\n            \"IsItemHovered(_AllowWhenOverlappedByItem) = %d\\n\"\n            \"IsItemHovered(_AllowWhenOverlappedByWindow) = %d\\n\"\n            \"IsItemHovered(_AllowWhenDisabled) = %d\\n\"\n            \"IsItemHovered(_RectOnly) = %d\\n\"\n            \"IsItemActive() = %d\\n\"\n            \"IsItemEdited() = %d\\n\"\n            \"IsItemActivated() = %d\\n\"\n            \"IsItemDeactivated() = %d\\n\"\n            \"IsItemDeactivatedAfterEdit() = %d\\n\"\n            \"IsItemVisible() = %d\\n\"\n            \"IsItemClicked() = %d\\n\"\n            \"IsItemToggledOpen() = %d\\n\"\n            \"GetItemRectMin() = (%.1f, %.1f)\\n\"\n            \"GetItemRectMax() = (%.1f, %.1f)\\n\"\n            \"GetItemRectSize() = (%.1f, %.1f)\",\n            ret,\n            ImGui::IsItemFocused(),\n            ImGui::IsItemHovered(),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByItem),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly),\n            ImGui::IsItemActive(),\n            ImGui::IsItemEdited(),\n            ImGui::IsItemActivated(),\n            ImGui::IsItemDeactivated(),\n            ImGui::IsItemDeactivatedAfterEdit(),\n            ImGui::IsItemVisible(),\n            ImGui::IsItemClicked(),\n            ImGui::IsItemToggledOpen(),\n            ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y,\n            ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y,\n            ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y\n        );\n        ImGui::BulletText(\n            \"with Hovering Delay or Stationary test:\\n\"\n            \"IsItemHovered() = %d\\n\"\n            \"IsItemHovered(_Stationary) = %d\\n\"\n            \"IsItemHovered(_DelayShort) = %d\\n\"\n            \"IsItemHovered(_DelayNormal) = %d\\n\"\n            \"IsItemHovered(_Tooltip) = %d\",\n            hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip);\n\n        if (item_disabled)\n            ImGui::EndDisabled();\n\n        char buf[1] = \"\";\n        ImGui::InputText(\"unused\", buf, IM_COUNTOF(buf), ImGuiInputTextFlags_ReadOnly);\n        ImGui::SameLine();\n        HelpMarker(\"This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status.\");\n\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Querying Window Status (Focused/Hovered etc.)\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Querying Window Status (Focused,Hovered etc.)\");\n        static bool embed_all_inside_a_child_window = false;\n        ImGui::Checkbox(\"Embed everything inside a child window for testing _RootWindow flag.\", &embed_all_inside_a_child_window);\n        if (embed_all_inside_a_child_window)\n            ImGui::BeginChild(\"outer_child\", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Borders);\n\n        // Testing IsWindowFocused() function with its various flags.\n        ImGui::BulletText(\n            \"IsWindowFocused() = %d\\n\"\n            \"IsWindowFocused(_ChildWindows) = %d\\n\"\n            \"IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowFocused(_ChildWindows|_DockHierarchy) = %d\\n\"\n            \"IsWindowFocused(_ChildWindows|_RootWindow) = %d\\n\"\n            \"IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\\n\"\n            \"IsWindowFocused(_RootWindow) = %d\\n\"\n            \"IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowFocused(_RootWindow|_DockHierarchy) = %d\\n\"\n            \"IsWindowFocused(_AnyWindow) = %d\\n\",\n            ImGui::IsWindowFocused(),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_DockHierarchy),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow));\n\n        // Testing IsWindowHovered() function with its various flags.\n        ImGui::BulletText(\n            \"IsWindowHovered() = %d\\n\"\n            \"IsWindowHovered(_AllowWhenBlockedByPopup) = %d\\n\"\n            \"IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows|_DockHierarchy) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows|_RootWindow) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\\n\"\n            \"IsWindowHovered(_RootWindow) = %d\\n\"\n            \"IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowHovered(_RootWindow|_DockHierarchy) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\\n\"\n            \"IsWindowHovered(_AnyWindow) = %d\\n\"\n            \"IsWindowHovered(_Stationary) = %d\\n\",\n            ImGui::IsWindowHovered(),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_DockHierarchy),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary));\n\n        ImGui::BeginChild(\"child\", ImVec2(0, 50), ImGuiChildFlags_Borders);\n        ImGui::Text(\"This is another child window for testing the _ChildWindows flag.\");\n        ImGui::EndChild();\n        if (embed_all_inside_a_child_window)\n            ImGui::EndChild();\n\n        // Calling IsItemHovered() after begin returns the hovered status of the title bar.\n        // This is useful in particular if you want to create a context menu associated to the title bar of a window.\n        // This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties).\n        static bool test_window = false;\n        ImGui::Checkbox(\"Hovered/Active tests after Begin() for title bar testing\", &test_window);\n        if (test_window)\n        {\n            // FIXME-DOCK: This window cannot be docked within the ImGui Demo window, this will cause a feedback loop and get them stuck.\n            // Could we fix this through an ImGuiWindowClass feature? Or an API call to tag our parent as \"don't skip items\"?\n            ImGui::Begin(\"Title bar Hovered/Active tests\", &test_window);\n            if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered()\n            {\n                if (ImGui::MenuItem(\"Close\")) { test_window = false; }\n                ImGui::EndPopup();\n            }\n            ImGui::Text(\n                \"IsItemHovered() after begin = %d (== is title bar hovered)\\n\"\n                \"IsItemActive() after begin = %d (== is window being clicked/moved)\\n\",\n                ImGui::IsItemHovered(), ImGui::IsItemActive());\n            ImGui::End();\n        }\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsSelectables()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsSelectables()\n{\n    //ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n    if (ImGui::TreeNode(\"Selectables\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Selectables\");\n        // Selectable() has 2 overloads:\n        // - The one taking \"bool selected\" as a read-only selection information.\n        //   When Selectable() has been clicked it returns true and you can alter selection state accordingly.\n        // - The one taking \"bool* p_selected\" as a read-write selection information (convenient in some cases)\n        // The earlier is more flexible, as in real application your selection may be stored in many different ways\n        // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc).\n        IMGUI_DEMO_MARKER(\"Widgets/Selectables/Basic\");\n        if (ImGui::TreeNode(\"Basic\"))\n        {\n            static bool selection[5] = { false, true, false, false };\n            ImGui::Selectable(\"1. I am selectable\", &selection[0]);\n            ImGui::Selectable(\"2. I am selectable\", &selection[1]);\n            ImGui::Selectable(\"3. I am selectable\", &selection[2]);\n            if (ImGui::Selectable(\"4. I am double clickable\", selection[3], ImGuiSelectableFlags_AllowDoubleClick))\n                if (ImGui::IsMouseDoubleClicked(0))\n                    selection[3] = !selection[3];\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Selectables/Rendering more items on the same line\");\n        if (ImGui::TreeNode(\"Multiple items on the same line\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selectables/Multiple items on the same line\");\n            // - Using SetNextItemAllowOverlap()\n            // - Using the Selectable() override that takes \"bool* p_selected\" parameter, the bool value is toggled automatically.\n            {\n                static bool selected[3] = {};\n                ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(\"main.c\", &selected[0]); ImGui::SameLine(); ImGui::SmallButton(\"Link 1\");\n                ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(\"hello.cpp\", &selected[1]); ImGui::SameLine(); ImGui::SmallButton(\"Link 2\");\n                ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(\"hello.h\", &selected[2]); ImGui::SameLine(); ImGui::SmallButton(\"Link 3\");\n            }\n\n            // (2)\n            // - Using ImGuiSelectableFlags_AllowOverlap is a shortcut for calling SetNextItemAllowOverlap()\n            // - No visible label, display contents inside the selectable bounds.\n            // - We don't maintain actual selection in this example to keep things simple.\n            ImGui::Spacing();\n            {\n                static bool checked[5] = {};\n                static int selected_n = 0;\n                const float color_marker_w = ImGui::CalcTextSize(\"x\").x;\n                for (int n = 0; n < 5; n++)\n                {\n                    ImGui::PushID(n);\n                    ImGui::AlignTextToFramePadding();\n                    if (ImGui::Selectable(\"##selectable\", selected_n == n, ImGuiSelectableFlags_AllowOverlap))\n                        selected_n = n;\n                    ImGui::SameLine(0, 0);\n                    ImGui::Checkbox(\"##check\", &checked[n]);\n                    ImGui::SameLine();\n                    ImVec4 color((n & 1) ? 1.0f : 0.2f, (n & 2) ? 1.0f : 0.2f, 0.2f, 1.0f);\n                    ImGui::ColorButton(\"##color\", color, ImGuiColorEditFlags_NoTooltip, ImVec2(color_marker_w, 0));\n                    ImGui::SameLine();\n                    ImGui::Text(\"Some label\");\n                    ImGui::PopID();\n                }\n            }\n\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"In Tables\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selectables/In Tables\");\n            static bool selected[10] = {};\n\n            if (ImGui::BeginTable(\"split1\", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))\n            {\n                for (int i = 0; i < 10; i++)\n                {\n                    char label[32];\n                    sprintf(label, \"Item %d\", i);\n                    ImGui::TableNextColumn();\n                    ImGui::Selectable(label, &selected[i]); // FIXME-TABLE: Selection overlap\n                }\n                ImGui::EndTable();\n            }\n            ImGui::Spacing();\n            if (ImGui::BeginTable(\"split2\", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))\n            {\n                for (int i = 0; i < 10; i++)\n                {\n                    char label[32];\n                    sprintf(label, \"Item %d\", i);\n                    ImGui::TableNextRow();\n                    ImGui::TableNextColumn();\n                    ImGui::Selectable(label, &selected[i], ImGuiSelectableFlags_SpanAllColumns);\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"Some other contents\");\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"123456\");\n                }\n                ImGui::EndTable();\n            }\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Grid\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selectables/Grid\");\n            static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };\n\n            // Add in a bit of silly fun...\n            const float time = (float)ImGui::GetTime();\n            const bool winning_state = memchr(selected, 0, sizeof(selected)) == NULL; // If all cells are selected...\n            if (winning_state)\n                ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f)));\n\n            const float size = ImGui::CalcTextSize(\"Sailor\").x;\n            for (int y = 0; y < 4; y++)\n                for (int x = 0; x < 4; x++)\n                {\n                    if (x > 0)\n                        ImGui::SameLine();\n                    ImGui::PushID(y * 4 + x);\n                    if (ImGui::Selectable(\"Sailor\", selected[y][x] != 0, 0, ImVec2(size, size)))\n                    {\n                        // Toggle clicked cell + toggle neighbors\n                        selected[y][x] ^= 1;\n                        if (x > 0) { selected[y][x - 1] ^= 1; }\n                        if (x < 3) { selected[y][x + 1] ^= 1; }\n                        if (y > 0) { selected[y - 1][x] ^= 1; }\n                        if (y < 3) { selected[y + 1][x] ^= 1; }\n                    }\n                    ImGui::PopID();\n                }\n\n            if (winning_state)\n                ImGui::PopStyleVar();\n            ImGui::TreePop();\n        }\n        if (ImGui::TreeNode(\"Alignment\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selectables/Alignment\");\n            HelpMarker(\n                \"By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item \"\n                \"basis using PushStyleVar(). You'll probably want to always keep your default situation to \"\n                \"left-align otherwise it becomes difficult to layout multiple items on a same line\");\n\n            static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true };\n            const float size = ImGui::CalcTextSize(\"(1.0,1.0)\").x;\n            for (int y = 0; y < 3; y++)\n            {\n                for (int x = 0; x < 3; x++)\n                {\n                    ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f);\n                    char name[32];\n                    sprintf(name, \"(%.1f,%.1f)\", alignment.x, alignment.y);\n                    if (x > 0) ImGui::SameLine();\n                    ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment);\n                    ImGui::Selectable(name, &selected[3 * y + x], ImGuiSelectableFlags_None, ImVec2(size, size));\n                    ImGui::PopStyleVar();\n                }\n            }\n            ImGui::TreePop();\n        }\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()\n//-----------------------------------------------------------------------------\n// Multi-selection demos\n// Also read: https://github.com/ocornut/imgui/wiki/Multi-Select\n//-----------------------------------------------------------------------------\n\nstatic const char* ExampleNames[] =\n{\n    \"Artichoke\", \"Arugula\", \"Asparagus\", \"Avocado\", \"Bamboo Shoots\", \"Bean Sprouts\", \"Beans\", \"Beet\", \"Belgian Endive\", \"Bell Pepper\",\n    \"Bitter Gourd\", \"Bok Choy\", \"Broccoli\", \"Brussels Sprouts\", \"Burdock Root\", \"Cabbage\", \"Calabash\", \"Capers\", \"Carrot\", \"Cassava\",\n    \"Cauliflower\", \"Celery\", \"Celery Root\", \"Celcuce\", \"Chayote\", \"Chinese Broccoli\", \"Corn\", \"Cucumber\"\n};\n\n// Extra functions to add deletion support to ImGuiSelectionBasicStorage\nstruct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage\n{\n    // Find which item should be Focused after deletion.\n    // Call _before_ item submission. Return an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it.\n    // The subsequent ApplyDeletionPostLoop() code will use it to apply Selection.\n    // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data.\n    // - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility.\n    // - Important: Deletion only works if the underlying ImGuiID for your items are stable: aka not depend on their index, but on e.g. item id/ptr.\n    // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or scroll offset.\n    int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count)\n    {\n        if (Size == 0)\n            return -1;\n\n        // If focused item is not selected...\n        const int focused_idx = (int)ms_io->NavIdItem;  // Index of currently focused item\n        if (ms_io->NavIdSelected == false)  // This is merely a shortcut, == Contains(adapter->IndexToStorage(items, focused_idx))\n        {\n            ms_io->RangeSrcReset = true;    // Request to recover RangeSrc from NavId next frame. Would be ok to reset even when NavIdSelected==true, but it would take an extra frame to recover RangeSrc when deleting a selected item.\n            return focused_idx;             // Request to focus same item after deletion.\n        }\n\n        // If focused item is selected: land on first unselected item after focused item.\n        for (int idx = focused_idx + 1; idx < items_count; idx++)\n            if (!Contains(GetStorageIdFromIndex(idx)))\n                return idx;\n\n        // If focused item is selected: otherwise return last unselected item before focused item.\n        for (int idx = IM_MIN(focused_idx, items_count) - 1; idx >= 0; idx--)\n            if (!Contains(GetStorageIdFromIndex(idx)))\n                return idx;\n\n        return -1;\n    }\n\n    // Rewrite item list (delete items) + update selection.\n    // - Call after EndMultiSelect()\n    // - We cannot provide this logic in core Dear ImGui because we don't have access to your items, nor to selection data.\n    template<typename ITEM_TYPE>\n    void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector<ITEM_TYPE>& items, int item_curr_idx_to_select)\n    {\n        // Rewrite item list (delete items) + convert old selection index (before deletion) to new selection index (after selection).\n        // If NavId was not part of selection, we will stay on same item.\n        ImVector<ITEM_TYPE> new_items;\n        new_items.reserve(items.Size - Size);\n        int item_next_idx_to_select = -1;\n        for (int idx = 0; idx < items.Size; idx++)\n        {\n            if (!Contains(GetStorageIdFromIndex(idx)))\n                new_items.push_back(items[idx]);\n            if (item_curr_idx_to_select == idx)\n                item_next_idx_to_select = new_items.Size - 1;\n        }\n        items.swap(new_items);\n\n        // Update selection\n        Clear();\n        if (item_next_idx_to_select != -1 && ms_io->NavIdSelected)\n            SetItemSelected(GetStorageIdFromIndex(item_next_idx_to_select), true);\n    }\n};\n\n// Example: Implement dual list box storage and interface\nstruct ExampleDualListBox\n{\n    ImVector<ImGuiID>           Items[2];               // ID is index into ExampleName[]\n    ImGuiSelectionBasicStorage  Selections[2];          // Store ExampleItemId into selection\n    bool                        OptKeepSorted = true;\n\n    void MoveAll(int src, int dst)\n    {\n        IM_ASSERT((src == 0 && dst == 1) || (src == 1 && dst == 0));\n        for (ImGuiID item_id : Items[src])\n            Items[dst].push_back(item_id);\n        Items[src].clear();\n        SortItems(dst);\n        Selections[src].Swap(Selections[dst]);\n        Selections[src].Clear();\n    }\n    void MoveSelected(int src, int dst)\n    {\n        for (int src_n = 0; src_n < Items[src].Size; src_n++)\n        {\n            ImGuiID item_id = Items[src][src_n];\n            if (!Selections[src].Contains(item_id))\n                continue;\n            Items[src].erase(&Items[src][src_n]); // FIXME-OPT: Could be implemented more optimally (rebuild src items and swap)\n            Items[dst].push_back(item_id);\n            src_n--;\n        }\n        if (OptKeepSorted)\n            SortItems(dst);\n        Selections[src].Swap(Selections[dst]);\n        Selections[src].Clear();\n    }\n    void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, int side)\n    {\n        // In this example we store item id in selection (instead of item index)\n        Selections[side].UserData = Items[side].Data;\n        Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->UserData; return items[idx]; };\n        Selections[side].ApplyRequests(ms_io);\n    }\n    static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs)\n    {\n        const int* a = (const int*)lhs;\n        const int* b = (const int*)rhs;\n        return *a - *b;\n    }\n    void SortItems(int n)\n    {\n        qsort(Items[n].Data, (size_t)Items[n].Size, sizeof(Items[n][0]), CompareItemsByValue);\n    }\n    void Show()\n    {\n        //if (ImGui::Checkbox(\"Sorted\", &OptKeepSorted) && OptKeepSorted) { SortItems(0); SortItems(1); }\n        if (ImGui::BeginTable(\"split\", 3, ImGuiTableFlags_None))\n        {\n            ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthStretch);    // Left side\n            ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthFixed);      // Buttons\n            ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthStretch);    // Right side\n            ImGui::TableNextRow();\n\n            int request_move_selected = -1;\n            int request_move_all = -1;\n            float child_height_0 = 0.0f;\n            for (int side = 0; side < 2; side++)\n            {\n                // FIXME-MULTISELECT: Dual List Box: Add context menus\n                // FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues.\n                ImVector<ImGuiID>& items = Items[side];\n                ImGuiSelectionBasicStorage& selection = Selections[side];\n\n                ImGui::TableSetColumnIndex((side == 0) ? 0 : 2);\n                ImGui::Text(\"%s (%d)\", (side == 0) ? \"Available\" : \"Basket\", items.Size);\n\n                // Submit scrolling range to avoid glitches on moving/deletion\n                const float items_height = ImGui::GetTextLineHeightWithSpacing();\n                ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));\n\n                bool child_visible;\n                if (side == 0)\n                {\n                    // Left child is resizable\n                    ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetFrameHeightWithSpacing() * 4), ImVec2(FLT_MAX, FLT_MAX));\n                    child_visible = ImGui::BeginChild(\"0\", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY);\n                    child_height_0 = ImGui::GetWindowSize().y;\n                }\n                else\n                {\n                    // Right child use same height as left one\n                    child_visible = ImGui::BeginChild(\"1\", ImVec2(-FLT_MIN, child_height_0), ImGuiChildFlags_FrameStyle);\n                }\n                if (child_visible)\n                {\n                    ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None;\n                    ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);\n                    ApplySelectionRequests(ms_io, side);\n\n                    for (int item_n = 0; item_n < items.Size; item_n++)\n                    {\n                        ImGuiID item_id = items[item_n];\n                        bool item_is_selected = selection.Contains(item_id);\n                        ImGui::SetNextItemSelectionUserData(item_n);\n                        ImGui::Selectable(ExampleNames[item_id], item_is_selected, ImGuiSelectableFlags_AllowDoubleClick);\n                        if (ImGui::IsItemFocused())\n                        {\n                            // FIXME-MULTISELECT: Dual List Box: Transfer focus\n                            if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))\n                                request_move_selected = side;\n                            if (ImGui::IsMouseDoubleClicked(0)) // FIXME-MULTISELECT: Double-click on multi-selection?\n                                request_move_selected = side;\n                        }\n                    }\n\n                    ms_io = ImGui::EndMultiSelect();\n                    ApplySelectionRequests(ms_io, side);\n                }\n                ImGui::EndChild();\n            }\n\n            // Buttons columns\n            ImGui::TableSetColumnIndex(1);\n            ImGui::NewLine();\n            //ImVec2 button_sz = { ImGui::CalcTextSize(\">>\").x + ImGui::GetStyle().FramePadding.x * 2.0f, ImGui::GetFrameHeight() + padding.y * 2.0f };\n            ImVec2 button_sz = { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() };\n\n            // (Using BeginDisabled()/EndDisabled() works but feels distracting given how it is currently visualized)\n            if (ImGui::Button(\">>\", button_sz))\n                request_move_all = 0;\n            if (ImGui::Button(\">\", button_sz))\n                request_move_selected = 0;\n            if (ImGui::Button(\"<\", button_sz))\n                request_move_selected = 1;\n            if (ImGui::Button(\"<<\", button_sz))\n                request_move_all = 1;\n\n            // Process requests\n            if (request_move_all != -1)\n                MoveAll(request_move_all, request_move_all ^ 1);\n            if (request_move_selected != -1)\n                MoveSelected(request_move_selected, request_move_selected ^ 1);\n\n            // FIXME-MULTISELECT: Support action from outside\n            /*\n            if (OptKeepSorted == false)\n            {\n                ImGui::NewLine();\n                if (ImGui::ArrowButton(\"MoveUp\", ImGuiDir_Up)) {}\n                if (ImGui::ArrowButton(\"MoveDown\", ImGuiDir_Down)) {}\n            }\n            */\n\n            ImGui::EndTable();\n        }\n    }\n};\n\nstatic void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_data)\n{\n    if (ImGui::TreeNode(\"Selection State & Multi-Select\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Selection State & Multi-Select\");\n        HelpMarker(\"Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data.\");\n\n        ImGui::BulletText(\"Wiki page:\");\n        ImGui::SameLine();\n        ImGui::TextLinkOpenURL(\"imgui/wiki/Multi-Select\", \"https://github.com/ocornut/imgui/wiki/Multi-Select\");\n\n        // Without any fancy API: manage single-selection yourself.\n        if (ImGui::TreeNode(\"Single-Select\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selection State/Single-Select\");\n            static int selected = -1;\n            for (int n = 0; n < 5; n++)\n            {\n                char buf[32];\n                sprintf(buf, \"Object %d\", n);\n                if (ImGui::Selectable(buf, selected == n))\n                    selected = n;\n            }\n            ImGui::TreePop();\n        }\n\n        // Demonstrate implementation a most-basic form of multi-selection manually\n        // This doesn't support the Shift modifier which requires BeginMultiSelect()!\n        if (ImGui::TreeNode(\"Multi-Select (manual/simplified, without BeginMultiSelect)\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)\");\n            HelpMarker(\"Hold Ctrl and Click to select multiple items.\");\n            static bool selection[5] = { false, false, false, false, false };\n            for (int n = 0; n < 5; n++)\n            {\n                char buf[32];\n                sprintf(buf, \"Object %d\", n);\n                if (ImGui::Selectable(buf, selection[n]))\n                {\n                    if (!ImGui::GetIO().KeyCtrl) // Clear selection when Ctrl is not held\n                        memset(selection, 0, sizeof(selection));\n                    selection[n] ^= 1; // Toggle current item\n                }\n            }\n            ImGui::TreePop();\n        }\n\n        // Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API.\n        // Shift+Click w/ Ctrl and other standard features are supported.\n        // We use the ImGuiSelectionBasicStorage helper which you may freely reimplement.\n        if (ImGui::TreeNode(\"Multi-Select\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selection State/Multi-Select\");\n            ImGui::Text(\"Supported features:\");\n            ImGui::BulletText(\"Keyboard navigation (arrows, page up/down, home/end, space).\");\n            ImGui::BulletText(\"Ctrl modifier to preserve and toggle selection.\");\n            ImGui::BulletText(\"Shift modifier for range selection.\");\n            ImGui::BulletText(\"Ctrl+A to select all.\");\n            ImGui::BulletText(\"Escape to clear selection.\");\n            ImGui::BulletText(\"Click and drag to box-select.\");\n            ImGui::Text(\"Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.\");\n\n            // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection\n            const int ITEMS_COUNT = 50;\n            static ImGuiSelectionBasicStorage selection;\n            ImGui::Text(\"Selection: %d/%d\", selection.Size, ITEMS_COUNT);\n\n            // The BeginChild() has no purpose for selection logic, other that offering a scrolling region.\n            if (ImGui::BeginChild(\"##Basket\", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))\n            {\n                ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;\n                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);\n                selection.ApplyRequests(ms_io);\n\n                for (int n = 0; n < ITEMS_COUNT; n++)\n                {\n                    char label[64];\n                    sprintf(label, \"Object %05d: %s\", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]);\n                    bool item_is_selected = selection.Contains((ImGuiID)n);\n                    ImGui::SetNextItemSelectionUserData(n);\n                    ImGui::Selectable(label, item_is_selected);\n                }\n\n                ms_io = ImGui::EndMultiSelect();\n                selection.ApplyRequests(ms_io);\n            }\n            ImGui::EndChild();\n            ImGui::TreePop();\n        }\n\n        // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()\n        if (ImGui::TreeNode(\"Multi-Select (with clipper)\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selection State/Multi-Select (with clipper)\");\n            // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection\n            static ImGuiSelectionBasicStorage selection;\n\n            ImGui::Text(\"Added features:\");\n            ImGui::BulletText(\"Using ImGuiListClipper.\");\n\n            const int ITEMS_COUNT = 10000;\n            ImGui::Text(\"Selection: %d/%d\", selection.Size, ITEMS_COUNT);\n            if (ImGui::BeginChild(\"##Basket\", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))\n            {\n                ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;\n                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);\n                selection.ApplyRequests(ms_io);\n\n                ImGuiListClipper clipper;\n                clipper.Begin(ITEMS_COUNT);\n                if (ms_io->RangeSrcItem != -1)\n                    clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.\n                while (clipper.Step())\n                {\n                    for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)\n                    {\n                        char label[64];\n                        sprintf(label, \"Object %05d: %s\", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]);\n                        bool item_is_selected = selection.Contains((ImGuiID)n);\n                        ImGui::SetNextItemSelectionUserData(n);\n                        ImGui::Selectable(label, item_is_selected);\n                    }\n                }\n\n                ms_io = ImGui::EndMultiSelect();\n                selection.ApplyRequests(ms_io);\n            }\n            ImGui::EndChild();\n            ImGui::TreePop();\n        }\n\n        // Demonstrate dynamic item list + deletion support using the BeginMultiSelect/EndMultiSelect API.\n        // In order to support Deletion without any glitches you need to:\n        // - (1) If items are submitted in their own scrolling area, submit contents size SetNextWindowContentSize() ahead of time to prevent one-frame readjustment of scrolling.\n        // - (2) Items needs to have persistent ID Stack identifier = ID needs to not depends on their index. PushID(index) = KO. PushID(item_id) = OK. This is in order to focus items reliably after a selection.\n        // - (3) BeginXXXX process\n        // - (4) Focus process\n        // - (5) EndXXXX process\n        if (ImGui::TreeNode(\"Multi-Select (with deletion)\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selection State/Multi-Select (with deletion)\");\n            // Storing items data separately from selection data.\n            // (you may decide to store selection data inside your item (aka intrusive storage) if you don't need multiple views over same items)\n            // Use a custom selection.Adapter: store item identifier in Selection (instead of index)\n            static ImVector<ImGuiID> items;\n            static ExampleSelectionWithDeletion selection;\n            selection.UserData = (void*)&items;\n            selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImVector<ImGuiID>* p_items = (ImVector<ImGuiID>*)self->UserData; return (*p_items)[idx]; }; // Index -> ID\n\n            ImGui::Text(\"Added features:\");\n            ImGui::BulletText(\"Dynamic list with Delete key support.\");\n            ImGui::Text(\"Selection size: %d/%d\", selection.Size, items.Size);\n\n            // Initialize default list with 50 items + button to add/remove items.\n            static ImGuiID items_next_id = 0;\n            if (items_next_id == 0)\n                for (ImGuiID n = 0; n < 50; n++)\n                    items.push_back(items_next_id++);\n            if (ImGui::SmallButton(\"Add 20 items\"))     { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } }\n            ImGui::SameLine();\n            if (ImGui::SmallButton(\"Remove 20 items\"))  { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.SetItemSelected(items.back(), false); items.pop_back(); } }\n\n            // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion\n            const float items_height = ImGui::GetTextLineHeightWithSpacing();\n            ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));\n\n            if (ImGui::BeginChild(\"##Basket\", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))\n            {\n                ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;\n                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);\n                selection.ApplyRequests(ms_io);\n\n                const bool want_delete = ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0);\n                const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1;\n\n                for (int n = 0; n < items.Size; n++)\n                {\n                    const ImGuiID item_id = items[n];\n                    char label[64];\n                    sprintf(label, \"Object %05u: %s\", item_id, ExampleNames[item_id % IM_COUNTOF(ExampleNames)]);\n\n                    bool item_is_selected = selection.Contains(item_id);\n                    ImGui::SetNextItemSelectionUserData(n);\n                    ImGui::Selectable(label, item_is_selected);\n                    if (item_curr_idx_to_focus == n)\n                        ImGui::SetKeyboardFocusHere(-1);\n                }\n\n                // Apply multi-select requests\n                ms_io = ImGui::EndMultiSelect();\n                selection.ApplyRequests(ms_io);\n                if (want_delete)\n                    selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus);\n            }\n            ImGui::EndChild();\n            ImGui::TreePop();\n        }\n\n        // Implement a Dual List Box (#6648)\n        if (ImGui::TreeNode(\"Multi-Select (dual list box)\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selection State/Multi-Select (dual list box)\");\n            // Init default state\n            static ExampleDualListBox dlb;\n            if (dlb.Items[0].Size == 0 && dlb.Items[1].Size == 0)\n                for (int item_id = 0; item_id < IM_COUNTOF(ExampleNames); item_id++)\n                    dlb.Items[0].push_back((ImGuiID)item_id);\n\n            // Show\n            dlb.Show();\n\n            ImGui::TreePop();\n        }\n\n        // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()\n        if (ImGui::TreeNode(\"Multi-Select (in a table)\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selection State/Multi-Select (in a table)\");\n            static ImGuiSelectionBasicStorage selection;\n\n            const int ITEMS_COUNT = 10000;\n            ImGui::Text(\"Selection: %d/%d\", selection.Size, ITEMS_COUNT);\n            if (ImGui::BeginTable(\"##Basket\", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter, ImVec2(0.0f, ImGui::GetFontSize() * 20)))\n            {\n                ImGui::TableSetupColumn(\"Object\");\n                ImGui::TableSetupColumn(\"Action\");\n                ImGui::TableSetupScrollFreeze(0, 1);\n                ImGui::TableHeadersRow();\n\n                ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;\n                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);\n                selection.ApplyRequests(ms_io);\n\n                ImGuiListClipper clipper;\n                clipper.Begin(ITEMS_COUNT);\n                if (ms_io->RangeSrcItem != -1)\n                    clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.\n                while (clipper.Step())\n                {\n                    for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)\n                    {\n                        ImGui::TableNextRow();\n                        ImGui::TableNextColumn();\n                        ImGui::PushID(n);\n                        char label[64];\n                        sprintf(label, \"Object %05d: %s\", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]);\n                        bool item_is_selected = selection.Contains((ImGuiID)n);\n                        ImGui::SetNextItemSelectionUserData(n);\n                        ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);\n                        ImGui::TableNextColumn();\n                        ImGui::SmallButton(\"hello\");\n                        ImGui::PopID();\n                    }\n                }\n\n                ms_io = ImGui::EndMultiSelect();\n                selection.ApplyRequests(ms_io);\n                ImGui::EndTable();\n            }\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Multi-Select (checkboxes)\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selection State/Multi-Select (checkboxes)\");\n            ImGui::Text(\"In a list of checkboxes (not selectable):\");\n            ImGui::BulletText(\"Using _NoAutoSelect + _NoAutoClear flags.\");\n            ImGui::BulletText(\"Shift+Click to check multiple boxes.\");\n            ImGui::BulletText(\"Shift+Keyboard to copy current value to other boxes.\");\n\n            // If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper.\n            static bool items[20] = {};\n            static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape;\n            ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_NoAutoSelect\", &flags, ImGuiMultiSelectFlags_NoAutoSelect);\n            ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_NoAutoClear\", &flags, ImGuiMultiSelectFlags_NoAutoClear);\n            ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_BoxSelect2d\", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width.\n\n            if (ImGui::BeginChild(\"##Basket\", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))\n            {\n                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_COUNTOF(items));\n                ImGuiSelectionExternalStorage storage_wrapper;\n                storage_wrapper.UserData = (void*)items;\n                storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; };\n                storage_wrapper.ApplyRequests(ms_io);\n                for (int n = 0; n < 20; n++)\n                {\n                    char label[32];\n                    sprintf(label, \"Item %d\", n);\n                    ImGui::SetNextItemSelectionUserData(n);\n                    ImGui::Checkbox(label, &items[n]);\n                }\n                ms_io = ImGui::EndMultiSelect();\n                storage_wrapper.ApplyRequests(ms_io);\n            }\n            ImGui::EndChild();\n\n            ImGui::TreePop();\n        }\n\n        // Demonstrate individual selection scopes in same window\n        if (ImGui::TreeNode(\"Multi-Select (multiple scopes)\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selection State/Multi-Select (multiple scopes)\");\n            // Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection\n            const int SCOPES_COUNT = 3;\n            const int ITEMS_COUNT = 8; // Per scope\n            static ImGuiSelectionBasicStorage selections_data[SCOPES_COUNT];\n\n            // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window.\n            static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid;\n            if (ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_ScopeWindow\", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))\n                flags &= ~ImGuiMultiSelectFlags_ScopeRect;\n            if (ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_ScopeRect\", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))\n                flags &= ~ImGuiMultiSelectFlags_ScopeWindow;\n            ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_ClearOnClickVoid\", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid);\n            ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_BoxSelect1d\", &flags, ImGuiMultiSelectFlags_BoxSelect1d);\n\n            for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++)\n            {\n                ImGui::PushID(selection_scope_n);\n                ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n];\n                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size, ITEMS_COUNT);\n                selection->ApplyRequests(ms_io);\n\n                ImGui::SeparatorText(\"Selection scope\");\n                ImGui::Text(\"Selection size: %d/%d\", selection->Size, ITEMS_COUNT);\n\n                for (int n = 0; n < ITEMS_COUNT; n++)\n                {\n                    char label[64];\n                    sprintf(label, \"Object %05d: %s\", n, ExampleNames[n % IM_COUNTOF(ExampleNames)]);\n                    bool item_is_selected = selection->Contains((ImGuiID)n);\n                    ImGui::SetNextItemSelectionUserData(n);\n                    ImGui::Selectable(label, item_is_selected);\n                }\n\n                // Apply multi-select requests\n                ms_io = ImGui::EndMultiSelect();\n                selection->ApplyRequests(ms_io);\n                ImGui::PopID();\n            }\n            ImGui::TreePop();\n        }\n\n        // See ShowExampleAppAssetsBrowser()\n        if (ImGui::TreeNode(\"Multi-Select (tiled assets browser)\"))\n        {\n            ImGui::Checkbox(\"Assets Browser\", &demo_data->ShowAppAssetsBrowser);\n            ImGui::Text(\"(also access from 'Examples->Assets Browser' in menu)\");\n            ImGui::TreePop();\n        }\n\n        // Demonstrate supporting multiple-selection in a tree.\n        // - We don't use linear indices for selection user data, but our ExampleTreeNode* pointer directly!\n        //   This showcase how SetNextItemSelectionUserData() never assume indices!\n        // - The difficulty here is to \"interpolate\" from RangeSrcItem to RangeDstItem in the SetAll/SetRange request.\n        //   We want this interpolation to match what the user sees: in visible order, skipping closed nodes.\n        //   This is implemented by our TreeGetNextNodeInVisibleOrder() user-space helper.\n        // - Important: In a real codebase aiming to implement full-featured selectable tree with custom filtering, you\n        //   are more likely to build an array mapping sequential indices to visible tree nodes, since your\n        //   filtering/search + clipping process will benefit from it. Having this will make this interpolation much easier.\n        // - Consider this a prototype: we are working toward simplifying some of it.\n        if (ImGui::TreeNode(\"Multi-Select (trees)\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selection State/Multi-Select (trees)\");\n            HelpMarker(\n                \"This is rather advanced and experimental. If you are getting started with multi-select, \"\n                \"please don't start by looking at how to use it for a tree!\\n\\n\"\n                \"Future versions will try to simplify and formalize some of this.\");\n\n            struct ExampleTreeFuncs\n            {\n                static void DrawNode(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection)\n                {\n                    ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;\n                    tree_node_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Enable pressing left to jump to parent\n                    if (node->Childs.Size == 0)\n                        tree_node_flags |= ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_Leaf;\n                    if (selection->Contains((ImGuiID)node->UID))\n                        tree_node_flags |= ImGuiTreeNodeFlags_Selected;\n\n                    // Using SetNextItemStorageID() to specify storage id, so we can easily peek into\n                    // the storage holding open/close stage, using our TreeNodeGetOpen/TreeNodeSetOpen() functions.\n                    ImGui::SetNextItemSelectionUserData((ImGuiSelectionUserData)(intptr_t)node);\n                    ImGui::SetNextItemStorageID((ImGuiID)node->UID);\n                    if (ImGui::TreeNodeEx(node->Name, tree_node_flags))\n                    {\n                        for (ExampleTreeNode* child : node->Childs)\n                            DrawNode(child, selection);\n                        ImGui::TreePop();\n                    }\n                    else if (ImGui::IsItemToggledOpen())\n                    {\n                        TreeCloseAndUnselectChildNodes(node, selection);\n                    }\n                }\n\n                // When closing a node: 1) close and unselect all child nodes, 2) select parent if any child was selected.\n                // FIXME: This is currently handled by user logic but I'm hoping to eventually provide tree node\n                // features to do this automatically, e.g. a ImGuiTreeNodeFlags_AutoCloseChildNodes etc.\n                static int TreeCloseAndUnselectChildNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, int depth = 0)\n                {\n                    // Recursive close (the test for depth == 0 is because we call this on a node that was just closed!)\n                    int unselected_count = selection->Contains((ImGuiID)node->UID) ? 1 : 0;\n                    if (depth == 0 || ImGui::TreeNodeGetOpen((ImGuiID)node->UID))\n                    {\n                        for (ExampleTreeNode* child : node->Childs)\n                            unselected_count += TreeCloseAndUnselectChildNodes(child, selection, depth + 1);\n                        ImGui::TreeNodeSetOpen((ImGuiID)node->UID, false);\n                    }\n\n                    // Select root node if any of its child was selected, otherwise unselect\n                    selection->SetItemSelected((ImGuiID)node->UID, (depth == 0 && unselected_count > 0));\n                    return unselected_count;\n                }\n\n                // Apply multi-selection requests\n                static void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, ExampleTreeNode* tree, ImGuiSelectionBasicStorage* selection)\n                {\n                    for (ImGuiSelectionRequest& req : ms_io->Requests)\n                    {\n                        if (req.Type == ImGuiSelectionRequestType_SetAll)\n                        {\n                            if (req.Selected)\n                                TreeSetAllInOpenNodes(tree, selection, req.Selected);\n                            else\n                                selection->Clear();\n                        }\n                        else if (req.Type == ImGuiSelectionRequestType_SetRange)\n                        {\n                            ExampleTreeNode* first_node = (ExampleTreeNode*)(intptr_t)req.RangeFirstItem;\n                            ExampleTreeNode* last_node = (ExampleTreeNode*)(intptr_t)req.RangeLastItem;\n                            for (ExampleTreeNode* node = first_node; node != NULL; node = TreeGetNextNodeInVisibleOrder(node, last_node))\n                                selection->SetItemSelected((ImGuiID)node->UID, req.Selected);\n                        }\n                    }\n                }\n\n                static void TreeSetAllInOpenNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, bool selected)\n                {\n                    if (node->Parent != NULL) // Root node isn't visible nor selectable in our scheme\n                        selection->SetItemSelected((ImGuiID)node->UID, selected);\n                    if (node->Parent == NULL || ImGui::TreeNodeGetOpen((ImGuiID)node->UID))\n                        for (ExampleTreeNode* child : node->Childs)\n                            TreeSetAllInOpenNodes(child, selection, selected);\n                }\n\n                // Interpolate in *user-visible order* AND only *over opened nodes*.\n                // If you have a sequential mapping tables (e.g. generated after a filter/search pass) this would be simpler.\n                // Here the tricks are that:\n                // - we store/maintain ExampleTreeNode::IndexInParent which allows implementing a linear iterator easily, without searches, without recursion.\n                //   this could be replaced by a search in parent, aka 'int index_in_parent = curr_node->Parent->Childs.find_index(curr_node)'\n                //   which would only be called when crossing from child to a parent, aka not too much.\n                // - we call SetNextItemStorageID() before our TreeNode() calls with an ID which doesn't relate to UI stack,\n                //   making it easier to call TreeNodeGetOpen()/TreeNodeSetOpen() from any location.\n                static ExampleTreeNode* TreeGetNextNodeInVisibleOrder(ExampleTreeNode* curr_node, ExampleTreeNode* last_node)\n                {\n                    // Reached last node\n                    if (curr_node == last_node)\n                        return NULL;\n\n                    // Recurse into childs. Query storage to tell if the node is open.\n                    if (curr_node->Childs.Size > 0 && ImGui::TreeNodeGetOpen((ImGuiID)curr_node->UID))\n                        return curr_node->Childs[0];\n\n                    // Next sibling, then into our own parent\n                    while (curr_node->Parent != NULL)\n                    {\n                        if (curr_node->IndexInParent + 1 < curr_node->Parent->Childs.Size)\n                            return curr_node->Parent->Childs[curr_node->IndexInParent + 1];\n                        curr_node = curr_node->Parent;\n                    }\n                    return NULL;\n                }\n\n            }; // ExampleTreeFuncs\n\n            static ImGuiSelectionBasicStorage selection;\n            if (demo_data->DemoTree == NULL)\n                demo_data->DemoTree = ExampleTree_CreateDemoTree(); // Create tree once\n            ImGui::Text(\"Selection size: %d\", selection.Size);\n\n            if (ImGui::BeginChild(\"##Tree\", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))\n            {\n                ExampleTreeNode* tree = demo_data->DemoTree;\n                ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect2d;\n                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, selection.Size, -1);\n                ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection);\n                for (ExampleTreeNode* node : tree->Childs)\n                    ExampleTreeFuncs::DrawNode(node, &selection);\n                ms_io = ImGui::EndMultiSelect();\n                ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection);\n            }\n            ImGui::EndChild();\n\n            ImGui::TreePop();\n        }\n\n        // Advanced demonstration of BeginMultiSelect()\n        // - Showcase clipping.\n        // - Showcase deletion.\n        // - Showcase basic drag and drop.\n        // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing).\n        // - Showcase using inside a table.\n        //ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n        if (ImGui::TreeNode(\"Multi-Select (advanced)\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Selection State/Multi-Select (advanced)\");\n            // Options\n            enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode };\n            static bool use_clipper = true;\n            static bool use_deletion = true;\n            static bool use_drag_drop = true;\n            static bool show_in_table = false;\n            static bool show_color_button = true;\n            static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;\n            static WidgetType widget_type = WidgetType_Selectable;\n\n            if (ImGui::TreeNode(\"Options\"))\n            {\n                if (ImGui::RadioButton(\"Selectables\", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; }\n                ImGui::SameLine();\n                if (ImGui::RadioButton(\"Tree nodes\", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; }\n                ImGui::SameLine();\n                HelpMarker(\"TreeNode() is technically supported but... using this correctly is more complicated (you need some sort of linear/random access to your tree, which is suited to advanced trees setups already implementing filters and clipper. We will work toward simplifying and demoing this.\\n\\nFor now the tree demo is actually a little bit meaningless because it is an empty tree with only root nodes.\");\n                ImGui::Checkbox(\"Enable clipper\", &use_clipper);\n                ImGui::Checkbox(\"Enable deletion\", &use_deletion);\n                ImGui::Checkbox(\"Enable drag & drop\", &use_drag_drop);\n                ImGui::Checkbox(\"Show in a table\", &show_in_table);\n                ImGui::Checkbox(\"Show color button\", &show_color_button);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_SingleSelect\", &flags, ImGuiMultiSelectFlags_SingleSelect);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_NoSelectAll\", &flags, ImGuiMultiSelectFlags_NoSelectAll);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_NoRangeSelect\", &flags, ImGuiMultiSelectFlags_NoRangeSelect);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_NoAutoSelect\", &flags, ImGuiMultiSelectFlags_NoAutoSelect);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_NoAutoClear\", &flags, ImGuiMultiSelectFlags_NoAutoClear);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_NoAutoClearOnReselect\", &flags, ImGuiMultiSelectFlags_NoAutoClearOnReselect);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_NoSelectOnRightClick\", &flags, ImGuiMultiSelectFlags_NoSelectOnRightClick);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_BoxSelect1d\", &flags, ImGuiMultiSelectFlags_BoxSelect1d);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_BoxSelect2d\", &flags, ImGuiMultiSelectFlags_BoxSelect2d);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_BoxSelectNoScroll\", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_ClearOnEscape\", &flags, ImGuiMultiSelectFlags_ClearOnEscape);\n                ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_ClearOnClickVoid\", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid);\n                if (ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_ScopeWindow\", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))\n                    flags &= ~ImGuiMultiSelectFlags_ScopeRect;\n                if (ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_ScopeRect\", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))\n                    flags &= ~ImGuiMultiSelectFlags_ScopeWindow;\n                if (ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_SelectOnClick\", &flags, ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick))\n                    flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease;\n                if (ImGui::CheckboxFlags(\"ImGuiMultiSelectFlags_SelectOnClickRelease\", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease))\n                    flags &= ~ImGuiMultiSelectFlags_SelectOnClick;\n                ImGui::SameLine(); HelpMarker(\"Allow dragging an unselected item without altering selection.\");\n                ImGui::TreePop();\n            }\n\n            // Initialize default list with 1000 items.\n            // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection\n            static ImVector<int> items;\n            static int items_next_id = 0;\n            if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } }\n            static ExampleSelectionWithDeletion selection;\n            static bool request_deletion_from_menu = false; // Queue deletion triggered from context menu\n\n            ImGui::Text(\"Selection size: %d/%d\", selection.Size, items.Size);\n\n            const float items_height = (widget_type == WidgetType_TreeNode) ? ImGui::GetTextLineHeight() : ImGui::GetTextLineHeightWithSpacing();\n            ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));\n            if (ImGui::BeginChild(\"##Basket\", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))\n            {\n                ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize());\n                if (widget_type == WidgetType_TreeNode)\n                    ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f);\n\n                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);\n                selection.ApplyRequests(ms_io);\n\n                const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu;\n                const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1;\n                request_deletion_from_menu = false;\n\n                if (show_in_table)\n                {\n                    if (widget_type == WidgetType_TreeNode)\n                        ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f));\n                    ImGui::BeginTable(\"##Split\", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX);\n                    ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthStretch, 0.70f);\n                    ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthStretch, 0.30f);\n                    //ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f);\n                }\n\n                ImGuiListClipper clipper;\n                if (use_clipper)\n                {\n                    clipper.Begin(items.Size);\n                    if (item_curr_idx_to_focus != -1)\n                        clipper.IncludeItemByIndex(item_curr_idx_to_focus); // Ensure focused item is not clipped.\n                    if (ms_io->RangeSrcItem != -1)\n                        clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.\n                }\n\n                while (!use_clipper || clipper.Step())\n                {\n                    const int item_begin = use_clipper ? clipper.DisplayStart : 0;\n                    const int item_end = use_clipper ? clipper.DisplayEnd : items.Size;\n                    for (int n = item_begin; n < item_end; n++)\n                    {\n                        if (show_in_table)\n                            ImGui::TableNextColumn();\n\n                        const int item_id = items[n];\n                        const char* item_category = ExampleNames[item_id % IM_COUNTOF(ExampleNames)];\n                        char label[64];\n                        sprintf(label, \"Object %05d: %s\", item_id, item_category);\n\n                        // IMPORTANT: for deletion refocus to work we need object ID to be stable,\n                        // aka not depend on their index in the list. Here we use our persistent item_id\n                        // instead of index to build a unique ID that will persist.\n                        // (If we used PushID(index) instead, focus wouldn't be restored correctly after deletion).\n                        ImGui::PushID(item_id);\n\n                        // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part\n                        // of the selection scope doesn't erroneously alter our selection.\n                        if (show_color_button)\n                        {\n                            ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK;\n                            ImGui::ColorButton(\"##\", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz);\n                            ImGui::SameLine();\n                        }\n\n                        // Submit item\n                        bool item_is_selected = selection.Contains((ImGuiID)n);\n                        bool item_is_open = false;\n                        ImGui::SetNextItemSelectionUserData(n);\n                        if (widget_type == WidgetType_Selectable)\n                        {\n                            ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_None);\n                        }\n                        else if (widget_type == WidgetType_TreeNode)\n                        {\n                            ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;\n                            if (item_is_selected)\n                                tree_node_flags |= ImGuiTreeNodeFlags_Selected;\n                            item_is_open = ImGui::TreeNodeEx(label, tree_node_flags);\n                        }\n\n                        // Focus (for after deletion)\n                        if (item_curr_idx_to_focus == n)\n                            ImGui::SetKeyboardFocusHere(-1);\n\n                        // Drag and Drop\n                        if (use_drag_drop && ImGui::BeginDragDropSource())\n                        {\n                            // Create payload with full selection OR single unselected item.\n                            // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)\n                            if (ImGui::GetDragDropPayload() == NULL)\n                            {\n                                ImVector<int> payload_items;\n                                void* it = NULL;\n                                ImGuiID id = 0;\n                                if (!item_is_selected)\n                                    payload_items.push_back(item_id);\n                                else\n                                    while (selection.GetNextSelectedItem(&it, &id))\n                                        payload_items.push_back((int)id);\n                                ImGui::SetDragDropPayload(\"MULTISELECT_DEMO_ITEMS\", payload_items.Data, (size_t)payload_items.size_in_bytes());\n                            }\n\n                            // Display payload content in tooltip\n                            const ImGuiPayload* payload = ImGui::GetDragDropPayload();\n                            const int* payload_items = (int*)payload->Data;\n                            const int payload_count = (int)payload->DataSize / (int)sizeof(int);\n                            if (payload_count == 1)\n                                ImGui::Text(\"Object %05d: %s\", payload_items[0], ExampleNames[payload_items[0] % IM_COUNTOF(ExampleNames)]);\n                            else\n                                ImGui::Text(\"Dragging %d objects\", payload_count);\n\n                            ImGui::EndDragDropSource();\n                        }\n\n                        if (widget_type == WidgetType_TreeNode && item_is_open)\n                            ImGui::TreePop();\n\n                        // Right-click: context menu\n                        if (ImGui::BeginPopupContextItem())\n                        {\n                            ImGui::BeginDisabled(!use_deletion || selection.Size == 0);\n                            sprintf(label, \"Delete %d item(s)###DeleteSelected\", selection.Size);\n                            if (ImGui::Selectable(label))\n                                request_deletion_from_menu = true;\n                            ImGui::EndDisabled();\n                            ImGui::Selectable(\"Close\");\n                            ImGui::EndPopup();\n                        }\n\n                        // Demo content within a table\n                        if (show_in_table)\n                        {\n                            ImGui::TableNextColumn();\n                            ImGui::SetNextItemWidth(-FLT_MIN);\n                            ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));\n                            ImGui::InputText(\"##NoLabel\", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly);\n                            ImGui::PopStyleVar();\n                        }\n\n                        ImGui::PopID();\n                    }\n                    if (!use_clipper)\n                        break;\n                }\n\n                if (show_in_table)\n                {\n                    ImGui::EndTable();\n                    if (widget_type == WidgetType_TreeNode)\n                        ImGui::PopStyleVar();\n                }\n\n                // Apply multi-select requests\n                ms_io = ImGui::EndMultiSelect();\n                selection.ApplyRequests(ms_io);\n                if (want_delete)\n                    selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus);\n\n                if (widget_type == WidgetType_TreeNode)\n                    ImGui::PopStyleVar();\n            }\n            ImGui::EndChild();\n            ImGui::TreePop();\n        }\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsTabs()\n//-----------------------------------------------------------------------------\n\nstatic void EditTabBarFittingPolicyFlags(ImGuiTabBarFlags* p_flags)\n{\n    if ((*p_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)\n        *p_flags |= ImGuiTabBarFlags_FittingPolicyDefault_;\n    if (ImGui::CheckboxFlags(\"ImGuiTabBarFlags_FittingPolicyMixed\", p_flags, ImGuiTabBarFlags_FittingPolicyMixed))\n        *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyMixed);\n    if (ImGui::CheckboxFlags(\"ImGuiTabBarFlags_FittingPolicyShrink\", p_flags, ImGuiTabBarFlags_FittingPolicyShrink))\n        *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyShrink);\n    if (ImGui::CheckboxFlags(\"ImGuiTabBarFlags_FittingPolicyScroll\", p_flags, ImGuiTabBarFlags_FittingPolicyScroll))\n        *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);\n}\n\nstatic void DemoWindowWidgetsTabs()\n{\n    if (ImGui::TreeNode(\"Tabs\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Tabs\");\n        if (ImGui::TreeNode(\"Basic\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Tabs/Basic\");\n            ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None;\n            if (ImGui::BeginTabBar(\"MyTabBar\", tab_bar_flags))\n            {\n                if (ImGui::BeginTabItem(\"Avocado\"))\n                {\n                    ImGui::Text(\"This is the Avocado tab!\\nblah blah blah blah blah\");\n                    ImGui::EndTabItem();\n                }\n                if (ImGui::BeginTabItem(\"Broccoli\"))\n                {\n                    ImGui::Text(\"This is the Broccoli tab!\\nblah blah blah blah blah\");\n                    ImGui::EndTabItem();\n                }\n                if (ImGui::BeginTabItem(\"Cucumber\"))\n                {\n                    ImGui::Text(\"This is the Cucumber tab!\\nblah blah blah blah blah\");\n                    ImGui::EndTabItem();\n                }\n                ImGui::EndTabBar();\n            }\n            ImGui::Separator();\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Advanced & Close Button\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Tabs/Advanced & Close Button\");\n            // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0).\n            static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable;\n            ImGui::CheckboxFlags(\"ImGuiTabBarFlags_Reorderable\", &tab_bar_flags, ImGuiTabBarFlags_Reorderable);\n            ImGui::CheckboxFlags(\"ImGuiTabBarFlags_AutoSelectNewTabs\", &tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs);\n            ImGui::CheckboxFlags(\"ImGuiTabBarFlags_TabListPopupButton\", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton);\n            ImGui::CheckboxFlags(\"ImGuiTabBarFlags_NoCloseWithMiddleMouseButton\", &tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton);\n            ImGui::CheckboxFlags(\"ImGuiTabBarFlags_DrawSelectedOverline\", &tab_bar_flags, ImGuiTabBarFlags_DrawSelectedOverline);\n            EditTabBarFittingPolicyFlags(&tab_bar_flags);\n\n            // Tab Bar\n            ImGui::AlignTextToFramePadding();\n            ImGui::Text(\"Opened:\");\n            const char* names[4] = { \"Artichoke\", \"Beetroot\", \"Celery\", \"Daikon\" };\n            static bool opened[4] = { true, true, true, true }; // Persistent user state\n            for (int n = 0; n < IM_COUNTOF(opened); n++)\n            {\n                ImGui::SameLine();\n                ImGui::Checkbox(names[n], &opened[n]);\n            }\n\n            // Passing a bool* to BeginTabItem() is similar to passing one to Begin():\n            // the underlying bool will be set to false when the tab is closed.\n            if (ImGui::BeginTabBar(\"MyTabBar\", tab_bar_flags))\n            {\n                for (int n = 0; n < IM_COUNTOF(opened); n++)\n                    if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n], ImGuiTabItemFlags_None))\n                    {\n                        ImGui::Text(\"This is the %s tab!\", names[n]);\n                        if (n & 1)\n                            ImGui::Text(\"I am an odd tab.\");\n                        ImGui::EndTabItem();\n                    }\n                ImGui::EndTabBar();\n            }\n            ImGui::Separator();\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"TabItemButton & Leading/Trailing flags\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Tabs/TabItemButton & Leading-Trailing flags\");\n            static ImVector<int> active_tabs;\n            static int next_tab_id = 0;\n            if (next_tab_id == 0) // Initialize with some default tabs\n                for (int i = 0; i < 3; i++)\n                    active_tabs.push_back(next_tab_id++);\n\n            // TabItemButton() and Leading/Trailing flags are distinct features which we will demo together.\n            // (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without Leading/Trailing flags...\n            // but they tend to make more sense together)\n            static bool show_leading_button = true;\n            static bool show_trailing_button = true;\n            ImGui::Checkbox(\"Show Leading TabItemButton()\", &show_leading_button);\n            ImGui::Checkbox(\"Show Trailing TabItemButton()\", &show_trailing_button);\n\n            // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs\n            static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyShrink;\n            EditTabBarFittingPolicyFlags(&tab_bar_flags);\n\n            if (ImGui::BeginTabBar(\"MyTabBar\", tab_bar_flags))\n            {\n                // Demo a Leading TabItemButton(): click the \"?\" button to open a menu\n                if (show_leading_button)\n                    if (ImGui::TabItemButton(\"?\", ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_NoTooltip))\n                        ImGui::OpenPopup(\"MyHelpMenu\");\n                if (ImGui::BeginPopup(\"MyHelpMenu\"))\n                {\n                    ImGui::Selectable(\"Hello!\");\n                    ImGui::EndPopup();\n                }\n\n                // Demo Trailing Tabs: click the \"+\" button to add a new tab.\n                // (In your app you may want to use a font icon instead of the \"+\")\n                // We submit it before the regular tabs, but thanks to the ImGuiTabItemFlags_Trailing flag it will always appear at the end.\n                if (show_trailing_button)\n                    if (ImGui::TabItemButton(\"+\", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip))\n                        active_tabs.push_back(next_tab_id++); // Add new tab\n\n                // Submit our regular tabs\n                for (int n = 0; n < active_tabs.Size; )\n                {\n                    bool open = true;\n                    char name[16];\n                    snprintf(name, IM_COUNTOF(name), \"%04d\", active_tabs[n]);\n                    if (ImGui::BeginTabItem(name, &open, ImGuiTabItemFlags_None))\n                    {\n                        ImGui::Text(\"This is the %s tab!\", name);\n                        ImGui::EndTabItem();\n                    }\n\n                    if (!open)\n                        active_tabs.erase(active_tabs.Data + n);\n                    else\n                        n++;\n                }\n\n                ImGui::EndTabBar();\n            }\n            ImGui::Separator();\n            ImGui::TreePop();\n        }\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsText()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsText()\n{\n    if (ImGui::TreeNode(\"Text\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Text\");\n        if (ImGui::TreeNode(\"Colorful Text\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Text/Colored Text\");\n            // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.\n            ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), \"Pink\");\n            ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), \"Yellow\");\n            ImGui::TextDisabled(\"Disabled\");\n            ImGui::SameLine(); HelpMarker(\"The TextDisabled color is stored in ImGuiStyle.\");\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Font Size\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Text/Font Size\");\n            ImGuiStyle& style = ImGui::GetStyle();\n            const float global_scale = style.FontScaleMain * style.FontScaleDpi;\n            ImGui::Text(\"style.FontScaleMain = %0.2f\", style.FontScaleMain);\n            ImGui::Text(\"style.FontScaleDpi = %0.2f\", style.FontScaleDpi);\n            ImGui::Text(\"global_scale = ~%0.2f\", global_scale); // This is not technically accurate as internal scales may apply, but conceptually let's pretend it is.\n            ImGui::Text(\"FontSize = %0.2f\", ImGui::GetFontSize());\n\n            ImGui::SeparatorText(\"\");\n            static float custom_size = 16.0f;\n            ImGui::SliderFloat(\"custom_size\", &custom_size, 10.0f, 100.0f, \"%.0f\");\n            ImGui::Text(\"ImGui::PushFont(nullptr, custom_size);\");\n            ImGui::PushFont(NULL, custom_size);\n            ImGui::Text(\"FontSize = %.2f (== %.2f * global_scale)\", ImGui::GetFontSize(), custom_size);\n            ImGui::PopFont();\n\n            ImGui::SeparatorText(\"\");\n            static float custom_scale = 1.0f;\n            ImGui::SliderFloat(\"custom_scale\", &custom_scale, 0.5f, 4.0f, \"%.2f\");\n            ImGui::Text(\"ImGui::PushFont(nullptr, style.FontSizeBase * custom_scale);\");\n            ImGui::PushFont(NULL, style.FontSizeBase * custom_scale);\n            ImGui::Text(\"FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)\", ImGui::GetFontSize(), custom_scale);\n            ImGui::PopFont();\n\n            ImGui::SeparatorText(\"\");\n            for (float scaling = 0.5f; scaling <= 4.0f; scaling += 0.5f)\n            {\n                ImGui::PushFont(NULL, style.FontSizeBase * scaling);\n                ImGui::Text(\"FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)\", ImGui::GetFontSize(), scaling);\n                ImGui::PopFont();\n            }\n\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Word Wrapping\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Text/Word Wrapping\");\n            // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility.\n            ImGui::TextWrapped(\n                \"This text should automatically wrap on the edge of the window. The current implementation \"\n                \"for text wrapping follows simple rules suitable for English and possibly other languages.\");\n            ImGui::Spacing();\n\n            static float wrap_width = 200.0f;\n            ImGui::SliderFloat(\"Wrap width\", &wrap_width, -20, 600, \"%.0f\");\n\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n            for (int n = 0; n < 2; n++)\n            {\n                ImGui::Text(\"Test paragraph %d:\", n);\n                ImVec2 pos = ImGui::GetCursorScreenPos();\n                ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y);\n                ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight());\n                ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);\n                if (n == 0)\n                    ImGui::Text(\"The lazy dog is a good dog. This paragraph should fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.\", wrap_width);\n                else\n                    ImGui::Text(\"aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee   ffffffff. gggggggg!hhhhhhhh\");\n\n                // Draw actual text bounding box, following by marker of our expected limit (should not overlap!)\n                draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255));\n                draw_list->AddRectFilled(marker_min, marker_max, IM_COL32(255, 0, 255, 255));\n                ImGui::PopTextWrapPos();\n            }\n\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"UTF-8 Text\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Text/UTF-8 Text\");\n            // UTF-8 test with Japanese characters\n            // (Needs a suitable font? Try \"Google Noto\" or \"Arial Unicode\". See docs/FONTS.md for details.)\n            // - From C++11 you can use the u8\"my text\" syntax to encode literal strings as UTF-8\n            // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you\n            //   can save your source files as 'UTF-8 without signature').\n            // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8\n            //   CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants.\n            //   Don't do this in your application! Please use u8\"text in any language\" in your application!\n            // Note that characters values are preserved even by InputText() if the font cannot be displayed,\n            // so you can safely copy & paste garbled characters into another application.\n            ImGui::TextWrapped(\n                \"CJK text will only appear if the font was loaded with the appropriate CJK character ranges. \"\n                \"Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. \"\n                \"Read docs/FONTS.md for details.\");\n            ImGui::Text(\"Hiragana: \\xe3\\x81\\x8b\\xe3\\x81\\x8d\\xe3\\x81\\x8f\\xe3\\x81\\x91\\xe3\\x81\\x93 (kakikukeko)\");\n            ImGui::Text(\"Kanjis: \\xe6\\x97\\xa5\\xe6\\x9c\\xac\\xe8\\xaa\\x9e (nihongo)\");\n            static char buf[32] = \"\\xe6\\x97\\xa5\\xe6\\x9c\\xac\\xe8\\xaa\\x9e\";\n            //static char buf[32] = u8\"NIHONGO\"; // <- this is how you would write it with C++11, using real kanjis\n            ImGui::InputText(\"UTF-8 input\", buf, IM_COUNTOF(buf));\n            ImGui::TreePop();\n        }\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsTextFilter()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsTextFilter()\n{\n    if (ImGui::TreeNode(\"Text Filter\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Text Filter\");\n        // Helper class to easy setup a text filter.\n        // You may want to implement a more feature-full filtering scheme in your own application.\n        HelpMarker(\"Not a widget per-se, but ImGuiTextFilter is a helper to perform simple filtering on text strings.\");\n        static ImGuiTextFilter filter;\n        ImGui::Text(\"Filter usage:\\n\"\n            \"  \\\"\\\"         display all lines\\n\"\n            \"  \\\"xxx\\\"      display lines containing \\\"xxx\\\"\\n\"\n            \"  \\\"xxx,yyy\\\"  display lines containing \\\"xxx\\\" or \\\"yyy\\\"\\n\"\n            \"  \\\"-xxx\\\"     hide lines containing \\\"xxx\\\"\");\n        filter.Draw();\n        const char* lines[] = { \"aaa1.c\", \"bbb1.c\", \"ccc1.c\", \"aaa2.cpp\", \"bbb2.cpp\", \"ccc2.cpp\", \"abc.h\", \"hello, world\" };\n        for (int i = 0; i < IM_COUNTOF(lines); i++)\n            if (filter.PassFilter(lines[i]))\n                ImGui::BulletText(\"%s\", lines[i]);\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsTextInput()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsTextInput()\n{\n    // To wire InputText() with std::string or any other custom string type,\n    // see the \"Text Input > Resize Callback\" section of this demo, and the misc/cpp/imgui_stdlib.h file.\n    if (ImGui::TreeNode(\"Text Input\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Text Input\");\n        if (ImGui::TreeNode(\"Multi-line Text Input\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Text Input/Multi-line Text Input\");\n            // WE ARE USING A FIXED-SIZE BUFFER FOR SIMPLICITY HERE.\n            // If you want to use InputText() with std::string or any custom dynamic string type:\n            // - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp\n            // - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize.\n            static char text[1024 * 16] =\n                \"/*\\n\"\n                \" The Pentium F00F bug, shorthand for F0 0F C7 C8,\\n\"\n                \" the hexadecimal encoding of one offending instruction,\\n\"\n                \" more formally, the invalid operand with locked CMPXCHG8B\\n\"\n                \" instruction bug, is a design flaw in the majority of\\n\"\n                \" Intel Pentium, Pentium MMX, and Pentium OverDrive\\n\"\n                \" processors (all in the P5 microarchitecture).\\n\"\n                \"*/\\n\\n\"\n                \"label:\\n\"\n                \"\\tlock cmpxchg8b eax\\n\";\n\n            static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;\n            HelpMarker(\"You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include <string> in here)\");\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_ReadOnly\", &flags, ImGuiInputTextFlags_ReadOnly);\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_WordWrap\", &flags, ImGuiInputTextFlags_WordWrap);\n            ImGui::SameLine(); HelpMarker(\"Feature is currently in Beta. Please read comments in imgui.h\");\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_AllowTabInput\", &flags, ImGuiInputTextFlags_AllowTabInput);\n            ImGui::SameLine(); HelpMarker(\"When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets.\");\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_CtrlEnterForNewLine\", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine);\n            ImGui::InputTextMultiline(\"##source\", text, IM_COUNTOF(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Filtered Text Input\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Text Input/Filtered Text Input\");\n            struct TextFilters\n            {\n                // Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback)\n                static int FilterCasingSwap(ImGuiInputTextCallbackData* data)\n                {\n                    if (data->EventChar >= 'a' && data->EventChar <= 'z') { data->EventChar -= 'a' - 'A'; } // Lowercase becomes uppercase\n                    else if (data->EventChar >= 'A' && data->EventChar <= 'Z') { data->EventChar += 'a' - 'A'; } // Uppercase becomes lowercase\n                    return 0;\n                }\n\n                // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i', otherwise return 1 (filter out)\n                static int FilterImGuiLetters(ImGuiInputTextCallbackData* data)\n                {\n                    if (data->EventChar < 256 && strchr(\"imgui\", (char)data->EventChar))\n                        return 0;\n                    return 1;\n                }\n            };\n\n            static char buf1[32] = \"\"; ImGui::InputText(\"default\", buf1, IM_COUNTOF(buf1));\n            static char buf2[32] = \"\"; ImGui::InputText(\"decimal\", buf2, IM_COUNTOF(buf2), ImGuiInputTextFlags_CharsDecimal);\n            static char buf3[32] = \"\"; ImGui::InputText(\"hexadecimal\", buf3, IM_COUNTOF(buf3), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);\n            static char buf4[32] = \"\"; ImGui::InputText(\"uppercase\", buf4, IM_COUNTOF(buf4), ImGuiInputTextFlags_CharsUppercase);\n            static char buf5[32] = \"\"; ImGui::InputText(\"no blank\", buf5, IM_COUNTOF(buf5), ImGuiInputTextFlags_CharsNoBlank);\n            static char buf6[32] = \"\"; ImGui::InputText(\"casing swap\", buf6, IM_COUNTOF(buf6), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters.\n            static char buf7[32] = \"\"; ImGui::InputText(\"\\\"imgui\\\"\", buf7, IM_COUNTOF(buf7), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters.\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Password Input\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Text Input/Password input\");\n            static char password[64] = \"password123\";\n            ImGui::InputText(\"password\", password, IM_COUNTOF(password), ImGuiInputTextFlags_Password);\n            ImGui::SameLine(); HelpMarker(\"Display all characters as '*'.\\nDisable clipboard cut and copy.\\nDisable logging.\\n\");\n            ImGui::InputTextWithHint(\"password (w/ hint)\", \"<password>\", password, IM_COUNTOF(password), ImGuiInputTextFlags_Password);\n            ImGui::InputText(\"password (clear)\", password, IM_COUNTOF(password));\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Completion, History, Edit Callbacks\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Text Input/Completion, History, Edit Callbacks\");\n            struct Funcs\n            {\n                static int MyCallback(ImGuiInputTextCallbackData* data)\n                {\n                    if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion)\n                    {\n                        data->InsertChars(data->CursorPos, \"..\");\n                    }\n                    else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory)\n                    {\n                        if (data->EventKey == ImGuiKey_UpArrow)\n                        {\n                            data->DeleteChars(0, data->BufTextLen);\n                            data->InsertChars(0, \"Pressed Up!\");\n                            data->SelectAll();\n                        }\n                        else if (data->EventKey == ImGuiKey_DownArrow)\n                        {\n                            data->DeleteChars(0, data->BufTextLen);\n                            data->InsertChars(0, \"Pressed Down!\");\n                            data->SelectAll();\n                        }\n                    }\n                    else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit)\n                    {\n                        // Toggle casing of first character\n                        char c = data->Buf[0];\n                        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) data->Buf[0] ^= 32;\n                        data->BufDirty = true;\n\n                        // Increment a counter\n                        int* p_int = (int*)data->UserData;\n                        *p_int = *p_int + 1;\n                    }\n                    return 0;\n                }\n            };\n            static char buf1[64];\n            ImGui::InputText(\"Completion\", buf1, IM_COUNTOF(buf1), ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback);\n            ImGui::SameLine(); HelpMarker(\n                \"Here we append \\\"..\\\" each time Tab is pressed. \"\n                \"See 'Examples>Console' for a more meaningful demonstration of using this callback.\");\n\n            static char buf2[64];\n            ImGui::InputText(\"History\", buf2, IM_COUNTOF(buf2), ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback);\n            ImGui::SameLine(); HelpMarker(\n                \"Here we replace and select text each time Up/Down are pressed. \"\n                \"See 'Examples>Console' for a more meaningful demonstration of using this callback.\");\n\n            static char buf3[64];\n            static int edit_count = 0;\n            ImGui::InputText(\"Edit\", buf3, IM_COUNTOF(buf3), ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count);\n            ImGui::SameLine(); HelpMarker(\n                \"Here we toggle the casing of the first character on every edit + count edits.\");\n            ImGui::SameLine(); ImGui::Text(\"(%d)\", edit_count);\n\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Resize Callback\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Text Input/Resize Callback\");\n            // To wire InputText() with std::string or any other custom string type,\n            // you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper\n            // using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string.\n            HelpMarker(\n                \"Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\\n\\n\"\n                \"See misc/cpp/imgui_stdlib.h for an implementation of this for std::string.\");\n            struct Funcs\n            {\n                static int MyResizeCallback(ImGuiInputTextCallbackData* data)\n                {\n                    if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)\n                    {\n                        ImVector<char>* my_str = (ImVector<char>*)data->UserData;\n                        IM_ASSERT(my_str->begin() == data->Buf);\n                        my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1\n                        data->Buf = my_str->begin();\n                    }\n                    return 0;\n                }\n\n                // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.\n                // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'\n                static bool MyInputTextMultiline(const char* label, ImVector<char>* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0)\n                {\n                    IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);\n                    return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size, flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void*)my_str);\n                }\n            };\n\n            // For this demo we are using ImVector as a string container.\n            // Note that because we need to store a terminating zero character, our size/capacity are 1 more\n            // than usually reported by a typical string class.\n            static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_WordWrap\", &flags, ImGuiInputTextFlags_WordWrap);\n\n            static ImVector<char> my_str;\n            if (my_str.empty())\n                my_str.push_back(0);\n            Funcs::MyInputTextMultiline(\"##MyStr\", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);\n            ImGui::Text(\"Data: %p\\nSize: %d\\nCapacity: %d\", (void*)my_str.begin(), my_str.size(), my_str.capacity());\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Eliding, Alignment\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Text Input/Eliding, Alignment\");\n            static char buf1[128] = \"/path/to/some/folder/with/long/filename.cpp\";\n            static ImGuiInputTextFlags flags = ImGuiInputTextFlags_ElideLeft;\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_ElideLeft\", &flags, ImGuiInputTextFlags_ElideLeft);\n            ImGui::InputText(\"Path\", buf1, IM_COUNTOF(buf1), flags);\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Miscellaneous\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Text Input/Miscellaneous\");\n            static char buf1[16];\n            static ImGuiInputTextFlags flags = ImGuiInputTextFlags_EscapeClearsAll;\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_EscapeClearsAll\", &flags, ImGuiInputTextFlags_EscapeClearsAll);\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_ReadOnly\", &flags, ImGuiInputTextFlags_ReadOnly);\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_NoUndoRedo\", &flags, ImGuiInputTextFlags_NoUndoRedo);\n            ImGui::InputText(\"Hello\", buf1, IM_COUNTOF(buf1), flags);\n            ImGui::TreePop();\n        }\n\n        ImGui::TreePop();\n    }\n\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsTooltips()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsTooltips()\n{\n    if (ImGui::TreeNode(\"Tooltips\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Tooltips\");\n        // Tooltips are windows following the mouse. They do not take focus away.\n        ImGui::SeparatorText(\"General\");\n\n        // Typical use cases:\n        // - Short-form (text only):      SetItemTooltip(\"Hello\");\n        // - Short-form (any contents):   if (BeginItemTooltip()) { Text(\"Hello\"); EndTooltip(); }\n\n        // - Full-form (text only):       if (IsItemHovered(...)) { SetTooltip(\"Hello\"); }\n        // - Full-form (any contents):    if (IsItemHovered(...) && BeginTooltip()) { Text(\"Hello\"); EndTooltip(); }\n\n        HelpMarker(\n            \"Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\\n\\n\"\n            \"We provide a helper SetItemTooltip() function to perform the two with standards flags.\");\n\n        ImVec2 sz = ImVec2(-FLT_MIN, 0.0f);\n\n        ImGui::Button(\"Basic\", sz);\n        ImGui::SetItemTooltip(\"I am a tooltip\");\n\n        ImGui::Button(\"Fancy\", sz);\n        if (ImGui::BeginItemTooltip())\n        {\n            ImGui::Text(\"I am a fancy tooltip\");\n            static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };\n            ImGui::PlotLines(\"Curve\", arr, IM_COUNTOF(arr));\n            ImGui::Text(\"Sin(time) = %f\", sinf((float)ImGui::GetTime()));\n            ImGui::EndTooltip();\n        }\n\n        ImGui::SeparatorText(\"Always On\");\n\n        // Showcase NOT relying on a IsItemHovered() to emit a tooltip.\n        // Here the tooltip is always emitted when 'always_on == true'.\n        static int always_on = 0;\n        ImGui::RadioButton(\"Off\", &always_on, 0);\n        ImGui::SameLine();\n        ImGui::RadioButton(\"Always On (Simple)\", &always_on, 1);\n        ImGui::SameLine();\n        ImGui::RadioButton(\"Always On (Advanced)\", &always_on, 2);\n        if (always_on == 1)\n            ImGui::SetTooltip(\"I am following you around.\");\n        else if (always_on == 2 && ImGui::BeginTooltip())\n        {\n            ImGui::ProgressBar(sinf((float)ImGui::GetTime()) * 0.5f + 0.5f, ImVec2(ImGui::GetFontSize() * 25, 0.0f));\n            ImGui::EndTooltip();\n        }\n\n        ImGui::SeparatorText(\"Custom\");\n\n        HelpMarker(\n            \"Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() is the preferred way to standardize \"\n            \"tooltip activation details across your application. You may however decide to use custom \"\n            \"flags for a specific tooltip instance.\");\n\n        // The following examples are passed for documentation purpose but may not be useful to most users.\n        // Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from\n        // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or keyboard/gamepad is being used.\n        // With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary.\n        ImGui::Button(\"Manual\", sz);\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n            ImGui::SetTooltip(\"I am a manually emitted tooltip.\");\n\n        ImGui::Button(\"DelayNone\", sz);\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNone))\n            ImGui::SetTooltip(\"I am a tooltip with no delay.\");\n\n        ImGui::Button(\"DelayShort\", sz);\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay))\n            ImGui::SetTooltip(\"I am a tooltip with a short delay (%0.2f sec).\", ImGui::GetStyle().HoverDelayShort);\n\n        ImGui::Button(\"DelayLong\", sz);\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay))\n            ImGui::SetTooltip(\"I am a tooltip with a long delay (%0.2f sec).\", ImGui::GetStyle().HoverDelayNormal);\n\n        ImGui::Button(\"Stationary\", sz);\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))\n            ImGui::SetTooltip(\"I am a tooltip requiring mouse to be stationary before activating.\");\n\n        // Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav',\n        // which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag.\n        ImGui::BeginDisabled();\n        ImGui::Button(\"Disabled item\", sz);\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n            ImGui::SetTooltip(\"I am a a tooltip for a disabled item.\");\n        ImGui::EndDisabled();\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsTreeNodes()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsTreeNodes()\n{\n    if (ImGui::TreeNode(\"Tree Nodes\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Tree Nodes\");\n        // See see \"Examples -> Property Editor\" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree.\n        if (ImGui::TreeNode(\"Basic trees\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Tree Nodes/Basic trees\");\n            for (int i = 0; i < 5; i++)\n            {\n                // Use SetNextItemOpen() so set the default state of a node to be open. We could\n                // also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing!\n                if (i == 0)\n                    ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n\n                // Here we use PushID() to generate a unique base ID, and then the \"\" used as TreeNode id won't conflict.\n                // An alternative to using 'PushID() + TreeNode(\"\", ...)' to generate a unique ID is to use 'TreeNode((void*)(intptr_t)i, ...)',\n                // aka generate a dummy pointer-sized value to be hashed. The demo below uses that technique. Both are fine.\n                ImGui::PushID(i);\n                if (ImGui::TreeNode(\"\", \"Child %d\", i))\n                {\n                    ImGui::Text(\"blah blah\");\n                    ImGui::SameLine();\n                    if (ImGui::SmallButton(\"button\")) {}\n                    ImGui::TreePop();\n                }\n                ImGui::PopID();\n            }\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Hierarchy lines\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Tree Nodes/Hierarchy lines\");\n            static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DefaultOpen;\n            HelpMarker(\"Default option for DrawLinesXXX is stored in style.TreeLinesFlags\");\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_DrawLinesNone\", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_DrawLinesFull\", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_DrawLinesToNodes\", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);\n\n            if (ImGui::TreeNodeEx(\"Parent\", base_flags))\n            {\n                if (ImGui::TreeNodeEx(\"Child 1\", base_flags))\n                {\n                    ImGui::Button(\"Button for Child 1\");\n                    ImGui::TreePop();\n                }\n                if (ImGui::TreeNodeEx(\"Child 2\", base_flags))\n                {\n                    ImGui::Button(\"Button for Child 2\");\n                    ImGui::TreePop();\n                }\n                ImGui::Text(\"Remaining contents\");\n                ImGui::Text(\"Remaining contents\");\n                ImGui::TreePop();\n            }\n\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Clipping Large Trees\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Tree Nodes/Clipping Large Trees\");\n            ImGui::TextWrapped(\n                \"- Using ImGuiListClipper with trees is a less easy than on arrays or grids.\\n\"\n                \"- Refer to 'Demo->Examples->Property Editor' for an example of how to do that.\\n\"\n                \"- Discuss in #3823\");\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Advanced, with Selectable nodes\"))\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Tree Nodes/Advanced, with Selectable nodes\");\n            HelpMarker(\n                \"This is a more typical looking tree with selectable nodes.\\n\"\n                \"Click to select, Ctrl+Click to toggle, click on arrows or double-click to open.\");\n            static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;\n            static bool align_label_with_current_x_position = false;\n            static bool test_drag_and_drop = false;\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_OpenOnArrow\", &base_flags, ImGuiTreeNodeFlags_OpenOnArrow);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_OpenOnDoubleClick\", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanAvailWidth\", &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker(\"Extend hit area to all available width instead of allowing more items to be laid out after the node.\");\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanFullWidth\", &base_flags, ImGuiTreeNodeFlags_SpanFullWidth);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanLabelWidth\", &base_flags, ImGuiTreeNodeFlags_SpanLabelWidth); ImGui::SameLine(); HelpMarker(\"Reduce hit area to the text label and a bit of margin.\");\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanAllColumns\", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker(\"For use in Tables only.\");\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_AllowOverlap\", &base_flags, ImGuiTreeNodeFlags_AllowOverlap);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_Framed\", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker(\"Draw frame with background (e.g. for CollapsingHeader)\");\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_FramePadding\", &base_flags, ImGuiTreeNodeFlags_FramePadding);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_NavLeftJumpsToParent\", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsToParent);\n\n            HelpMarker(\"Default option for DrawLinesXXX is stored in style.TreeLinesFlags\");\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_DrawLinesNone\", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_DrawLinesFull\", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_DrawLinesToNodes\", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);\n\n            ImGui::Checkbox(\"Align label with current X position\", &align_label_with_current_x_position);\n            ImGui::Checkbox(\"Test tree node as drag source\", &test_drag_and_drop);\n            ImGui::Text(\"Hello!\");\n            if (align_label_with_current_x_position)\n                ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());\n\n            // 'selection_mask' is dumb representation of what may be user-side selection state.\n            //  You may retain selection state inside or outside your objects in whatever format you see fit.\n            // 'node_clicked' is temporary storage of what node we have clicked to process selection at the end\n            /// of the loop. May be a pointer to your own node type, etc.\n            static int selection_mask = (1 << 2);\n            int node_clicked = -1;\n            for (int i = 0; i < 6; i++)\n            {\n                // Disable the default \"open on single-click behavior\" + set Selected flag according to our selection.\n                // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection.\n                ImGuiTreeNodeFlags node_flags = base_flags;\n                const bool is_selected = (selection_mask & (1 << i)) != 0;\n                if (is_selected)\n                    node_flags |= ImGuiTreeNodeFlags_Selected;\n                if (i < 3)\n                {\n                    // Items 0..2 are Tree Node\n                    bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, \"Selectable Node %d\", i);\n                    if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())\n                        node_clicked = i;\n                    if (test_drag_and_drop && ImGui::BeginDragDropSource())\n                    {\n                        ImGui::SetDragDropPayload(\"_TREENODE\", NULL, 0);\n                        ImGui::Text(\"This is a drag and drop source\");\n                        ImGui::EndDragDropSource();\n                    }\n                    if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth))\n                    {\n                        // Item 2 has an additional inline button to help demonstrate SpanLabelWidth.\n                        ImGui::SameLine();\n                        if (ImGui::SmallButton(\"button\")) {}\n                    }\n                    if (node_open)\n                    {\n                        ImGui::BulletText(\"Blah blah\\nBlah Blah\");\n                        ImGui::SameLine();\n                        ImGui::SmallButton(\"Button\");\n                        ImGui::TreePop();\n                    }\n                }\n                else\n                {\n                    // Items 3..5 are Tree Leaves\n                    // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can\n                    // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().\n                    node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet\n                    ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, \"Selectable Leaf %d\", i);\n                    if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())\n                        node_clicked = i;\n                    if (test_drag_and_drop && ImGui::BeginDragDropSource())\n                    {\n                        ImGui::SetDragDropPayload(\"_TREENODE\", NULL, 0);\n                        ImGui::Text(\"This is a drag and drop source\");\n                        ImGui::EndDragDropSource();\n                    }\n                }\n            }\n            if (node_clicked != -1)\n            {\n                // Update selection state\n                // (process outside of tree loop to avoid visual inconsistencies during the clicking frame)\n                if (ImGui::GetIO().KeyCtrl)\n                    selection_mask ^= (1 << node_clicked);          // Ctrl+Click to toggle\n                else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection\n                    selection_mask = (1 << node_clicked);           // Click to single-select\n            }\n            if (align_label_with_current_x_position)\n                ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());\n            ImGui::TreePop();\n        }\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgetsVerticalSliders()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgetsVerticalSliders()\n{\n    if (ImGui::TreeNode(\"Vertical Sliders\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Vertical Sliders\");\n        const float spacing = 4;\n        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));\n\n        static int int_value = 0;\n        ImGui::VSliderInt(\"##int\", ImVec2(18, 160), &int_value, 0, 5);\n        ImGui::SameLine();\n\n        static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f };\n        ImGui::PushID(\"set1\");\n        for (int i = 0; i < 7; i++)\n        {\n            if (i > 0) ImGui::SameLine();\n            ImGui::PushID(i);\n            ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i / 7.0f, 0.5f, 0.5f));\n            ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.5f));\n            ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.5f));\n            ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i / 7.0f, 0.9f, 0.9f));\n            ImGui::VSliderFloat(\"##v\", ImVec2(18, 160), &values[i], 0.0f, 1.0f, \"\");\n            if (ImGui::IsItemActive() || ImGui::IsItemHovered())\n                ImGui::SetTooltip(\"%.3f\", values[i]);\n            ImGui::PopStyleColor(4);\n            ImGui::PopID();\n        }\n        ImGui::PopID();\n\n        ImGui::SameLine();\n        ImGui::PushID(\"set2\");\n        static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f };\n        const int rows = 3;\n        const ImVec2 small_slider_size(18, (float)(int)((160.0f - (rows - 1) * spacing) / rows));\n        for (int nx = 0; nx < 4; nx++)\n        {\n            if (nx > 0) ImGui::SameLine();\n            ImGui::BeginGroup();\n            for (int ny = 0; ny < rows; ny++)\n            {\n                ImGui::PushID(nx * rows + ny);\n                ImGui::VSliderFloat(\"##v\", small_slider_size, &values2[nx], 0.0f, 1.0f, \"\");\n                if (ImGui::IsItemActive() || ImGui::IsItemHovered())\n                    ImGui::SetTooltip(\"%.3f\", values2[nx]);\n                ImGui::PopID();\n            }\n            ImGui::EndGroup();\n        }\n        ImGui::PopID();\n\n        ImGui::SameLine();\n        ImGui::PushID(\"set3\");\n        for (int i = 0; i < 4; i++)\n        {\n            if (i > 0) ImGui::SameLine();\n            ImGui::PushID(i);\n            ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40);\n            ImGui::VSliderFloat(\"##v\", ImVec2(40, 160), &values[i], 0.0f, 1.0f, \"%.2f\\nsec\");\n            ImGui::PopStyleVar();\n            ImGui::PopID();\n        }\n        ImGui::PopID();\n        ImGui::PopStyleVar();\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowWidgets()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowWidgets(ImGuiDemoWindowData* demo_data)\n{\n    //ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n    if (!ImGui::CollapsingHeader(\"Widgets\"))\n        return;\n    // IMGUI_DEMO_MARKER(\"Widgets\");\n\n    const bool disable_all = demo_data->DisableSections; // The Checkbox for that is inside the \"Disabled\" section at the bottom\n    if (disable_all)\n        ImGui::BeginDisabled();\n\n    DemoWindowWidgetsBasic();\n    DemoWindowWidgetsBullets();\n    DemoWindowWidgetsCollapsingHeaders();\n    DemoWindowWidgetsComboBoxes();\n    DemoWindowWidgetsColorAndPickers();\n    DemoWindowWidgetsDataTypes();\n\n    if (disable_all)\n        ImGui::EndDisabled();\n    DemoWindowWidgetsDisableBlocks(demo_data);\n    if (disable_all)\n        ImGui::BeginDisabled();\n\n    DemoWindowWidgetsDragAndDrop();\n    DemoWindowWidgetsDragsAndSliders();\n    DemoWindowWidgetsFonts();\n    DemoWindowWidgetsImages();\n    DemoWindowWidgetsListBoxes();\n    DemoWindowWidgetsMultiComponents();\n    DemoWindowWidgetsPlotting();\n    DemoWindowWidgetsProgressBars();\n    DemoWindowWidgetsQueryingStatuses();\n    DemoWindowWidgetsSelectables();\n    DemoWindowWidgetsSelectionAndMultiSelect(demo_data);\n    DemoWindowWidgetsTabs();\n    DemoWindowWidgetsText();\n    DemoWindowWidgetsTextFilter();\n    DemoWindowWidgetsTextInput();\n    DemoWindowWidgetsTooltips();\n    DemoWindowWidgetsTreeNodes();\n    DemoWindowWidgetsVerticalSliders();\n\n    if (disable_all)\n        ImGui::EndDisabled();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowLayout()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowLayout()\n{\n    if (!ImGui::CollapsingHeader(\"Layout & Scrolling\"))\n        return;\n\n    if (ImGui::TreeNode(\"Child windows\"))\n    {\n        IMGUI_DEMO_MARKER(\"Layout/Child windows\");\n        ImGui::SeparatorText(\"Child windows\");\n\n        HelpMarker(\"Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window.\");\n        static bool disable_mouse_wheel = false;\n        static bool disable_menu = false;\n        ImGui::Checkbox(\"Disable Mouse Wheel\", &disable_mouse_wheel);\n        ImGui::Checkbox(\"Disable Menu\", &disable_menu);\n\n        // Child 1: no border, enable horizontal scrollbar\n        {\n            ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar;\n            if (disable_mouse_wheel)\n                window_flags |= ImGuiWindowFlags_NoScrollWithMouse;\n            ImGui::BeginChild(\"ChildL\", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), ImGuiChildFlags_None, window_flags);\n            for (int i = 0; i < 100; i++)\n                ImGui::Text(\"%04d: scrollable region\", i);\n            ImGui::EndChild();\n        }\n\n        ImGui::SameLine();\n\n        // Child 2: rounded border\n        {\n            ImGuiWindowFlags window_flags = ImGuiWindowFlags_None;\n            if (disable_mouse_wheel)\n                window_flags |= ImGuiWindowFlags_NoScrollWithMouse;\n            if (!disable_menu)\n                window_flags |= ImGuiWindowFlags_MenuBar;\n            ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);\n            ImGui::BeginChild(\"ChildR\", ImVec2(0, 260), ImGuiChildFlags_Borders, window_flags);\n            if (!disable_menu && ImGui::BeginMenuBar())\n            {\n                if (ImGui::BeginMenu(\"Menu\"))\n                {\n                    ShowExampleMenuFile();\n                    ImGui::EndMenu();\n                }\n                ImGui::EndMenuBar();\n            }\n            if (ImGui::BeginTable(\"split\", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings))\n            {\n                for (int i = 0; i < 100; i++)\n                {\n                    char buf[32];\n                    sprintf(buf, \"%03d\", i);\n                    ImGui::TableNextColumn();\n                    ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));\n                }\n                ImGui::EndTable();\n            }\n            ImGui::EndChild();\n            ImGui::PopStyleVar();\n        }\n\n        // Child 3: manual-resize\n        ImGui::SeparatorText(\"Manual-resize\");\n        {\n            HelpMarker(\"Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents.\");\n            //if (ImGui::Button(\"Set Height to 200\"))\n            //    ImGui::SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f));\n\n            ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));\n            if (ImGui::BeginChild(\"ResizableChild\", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))\n                for (int n = 0; n < 10; n++)\n                    ImGui::Text(\"Line %04d\", n);\n            ImGui::PopStyleColor();\n            ImGui::EndChild();\n        }\n\n        // Child 4: auto-resizing height with a limit\n        ImGui::SeparatorText(\"Auto-resize with constraints\");\n        {\n            static int draw_lines = 3;\n            static int max_height_in_lines = 10;\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n            ImGui::DragInt(\"Lines Count\", &draw_lines, 0.2f);\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n            ImGui::DragInt(\"Max Height (in Lines)\", &max_height_in_lines, 0.2f);\n\n            ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines));\n            if (ImGui::BeginChild(\"ConstrainedChild\", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY))\n                for (int n = 0; n < draw_lines; n++)\n                    ImGui::Text(\"Line %04d\", n);\n            ImGui::EndChild();\n        }\n\n        ImGui::SeparatorText(\"Misc/Advanced\");\n\n        // Demonstrate a few extra things\n        // - Changing ImGuiCol_ChildBg (which is transparent black in default styles)\n        // - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window)\n        //   You can also call SetNextWindowPos() to position the child window. The parent window will effectively\n        //   layout from this position.\n        // - Using ImGui::GetItemRectMin/Max() to query the \"item\" state (because the child window is an item from\n        //   the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details.\n        {\n            static int offset_x = 0;\n            static bool override_bg_color = true;\n            static ImGuiChildFlags child_flags = ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY;\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n            ImGui::DragInt(\"Offset X\", &offset_x, 1.0f, -1000, 1000);\n            ImGui::Checkbox(\"Override ChildBg color\", &override_bg_color);\n            ImGui::CheckboxFlags(\"ImGuiChildFlags_Borders\", &child_flags, ImGuiChildFlags_Borders);\n            ImGui::CheckboxFlags(\"ImGuiChildFlags_AlwaysUseWindowPadding\", &child_flags, ImGuiChildFlags_AlwaysUseWindowPadding);\n            ImGui::CheckboxFlags(\"ImGuiChildFlags_ResizeX\", &child_flags, ImGuiChildFlags_ResizeX);\n            ImGui::CheckboxFlags(\"ImGuiChildFlags_ResizeY\", &child_flags, ImGuiChildFlags_ResizeY);\n            ImGui::CheckboxFlags(\"ImGuiChildFlags_FrameStyle\", &child_flags, ImGuiChildFlags_FrameStyle);\n            ImGui::SameLine(); HelpMarker(\"Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.\");\n            if (child_flags & ImGuiChildFlags_FrameStyle)\n                override_bg_color = false;\n\n            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x);\n            if (override_bg_color)\n                ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100));\n            ImGui::BeginChild(\"Red\", ImVec2(200, 100), child_flags, ImGuiWindowFlags_None);\n            if (override_bg_color)\n                ImGui::PopStyleColor();\n\n            for (int n = 0; n < 50; n++)\n                ImGui::Text(\"Some test %d\", n);\n            ImGui::EndChild();\n            bool child_is_hovered = ImGui::IsItemHovered();\n            ImVec2 child_rect_min = ImGui::GetItemRectMin();\n            ImVec2 child_rect_max = ImGui::GetItemRectMax();\n            ImGui::Text(\"Hovered: %d\", child_is_hovered);\n            ImGui::Text(\"Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)\", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y);\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Widgets Width\"))\n    {\n        IMGUI_DEMO_MARKER(\"Layout/Widgets Width\");\n        static float f = 0.0f;\n        static bool show_indented_items = true;\n        ImGui::Checkbox(\"Show indented items\", &show_indented_items);\n\n        // Use SetNextItemWidth() to set the width of a single upcoming item.\n        // Use PushItemWidth()/PopItemWidth() to set the width of a group of items.\n        // In real code use you'll probably want to choose width values that are proportional to your font size\n        // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc.\n\n        ImGui::Text(\"SetNextItemWidth/PushItemWidth(100)\");\n        ImGui::SameLine(); HelpMarker(\"Fixed width.\");\n        ImGui::PushItemWidth(100);\n        ImGui::DragFloat(\"float##1b\", &f);\n        if (show_indented_items)\n        {\n            ImGui::Indent();\n            ImGui::DragFloat(\"float (indented)##1b\", &f);\n            ImGui::Unindent();\n        }\n        ImGui::PopItemWidth();\n\n        ImGui::Text(\"SetNextItemWidth/PushItemWidth(-100)\");\n        ImGui::SameLine(); HelpMarker(\"Align to right edge minus 100\");\n        ImGui::PushItemWidth(-100);\n        ImGui::DragFloat(\"float##2a\", &f);\n        if (show_indented_items)\n        {\n            ImGui::Indent();\n            ImGui::DragFloat(\"float (indented)##2b\", &f);\n            ImGui::Unindent();\n        }\n        ImGui::PopItemWidth();\n\n        ImGui::Text(\"SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)\");\n        ImGui::SameLine(); HelpMarker(\"Half of available width.\\n(~ right-cursor_pos)\\n(works within a column set)\");\n        ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f);\n        ImGui::DragFloat(\"float##3a\", &f);\n        if (show_indented_items)\n        {\n            ImGui::Indent();\n            ImGui::DragFloat(\"float (indented)##3b\", &f);\n            ImGui::Unindent();\n        }\n        ImGui::PopItemWidth();\n\n        ImGui::Text(\"SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)\");\n        ImGui::SameLine(); HelpMarker(\"Align to right edge minus half\");\n        ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);\n        ImGui::DragFloat(\"float##4a\", &f);\n        if (show_indented_items)\n        {\n            ImGui::Indent();\n            ImGui::DragFloat(\"float (indented)##4b\", &f);\n            ImGui::Unindent();\n        }\n        ImGui::PopItemWidth();\n\n        ImGui::Text(\"SetNextItemWidth/PushItemWidth(-Min(GetContentRegionAvail().x * 0.40f, GetFontSize() * 12))\");\n        ImGui::PushItemWidth(-IM_MIN(ImGui::GetFontSize() * 12, ImGui::GetContentRegionAvail().x * 0.40f));\n        ImGui::DragFloat(\"float##5a\", &f);\n        if (show_indented_items)\n        {\n            ImGui::Indent();\n            ImGui::DragFloat(\"float (indented)##5b\", &f);\n            ImGui::Unindent();\n        }\n        ImGui::PopItemWidth();\n\n        // Demonstrate using PushItemWidth to surround three items.\n        // Calling SetNextItemWidth() before each of them would have the same effect.\n        ImGui::Text(\"SetNextItemWidth/PushItemWidth(-FLT_MIN)\");\n        ImGui::SameLine(); HelpMarker(\"Align to right edge\");\n        ImGui::PushItemWidth(-FLT_MIN);\n        ImGui::DragFloat(\"##float6a\", &f);\n        if (show_indented_items)\n        {\n            ImGui::Indent();\n            ImGui::DragFloat(\"float (indented)##6b\", &f);\n            ImGui::Unindent();\n        }\n        ImGui::PopItemWidth();\n\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Basic Horizontal Layout\"))\n    {\n        IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout\");\n        ImGui::TextWrapped(\"(Use ImGui::SameLine() to keep adding items to the right of the preceding item)\");\n\n        // Text\n        IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout/SameLine\");\n        ImGui::Text(\"Two items: Hello\"); ImGui::SameLine();\n        ImGui::TextColored(ImVec4(1, 1, 0, 1), \"Sailor\");\n\n        // Adjust spacing\n        ImGui::Text(\"More spacing: Hello\"); ImGui::SameLine(0, 20);\n        ImGui::TextColored(ImVec4(1, 1, 0, 1), \"Sailor\");\n\n        // Button\n        ImGui::AlignTextToFramePadding();\n        ImGui::Text(\"Normal buttons\"); ImGui::SameLine();\n        ImGui::Button(\"Banana\"); ImGui::SameLine();\n        ImGui::Button(\"Apple\"); ImGui::SameLine();\n        ImGui::Button(\"Corniflower\");\n\n        // Button\n        ImGui::Text(\"Small buttons\"); ImGui::SameLine();\n        ImGui::SmallButton(\"Like this one\"); ImGui::SameLine();\n        ImGui::Text(\"can fit within a text block.\");\n\n        // Aligned to arbitrary position. Easy/cheap column.\n        IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout/SameLine (with offset)\");\n        ImGui::Text(\"Aligned\");\n        ImGui::SameLine(150); ImGui::Text(\"x=150\");\n        ImGui::SameLine(300); ImGui::Text(\"x=300\");\n        ImGui::Text(\"Aligned\");\n        ImGui::SameLine(150); ImGui::SmallButton(\"x=150\");\n        ImGui::SameLine(300); ImGui::SmallButton(\"x=300\");\n\n        // Checkbox\n        IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout/SameLine (more)\");\n        static bool c1 = false, c2 = false, c3 = false, c4 = false;\n        ImGui::Checkbox(\"My\", &c1); ImGui::SameLine();\n        ImGui::Checkbox(\"Tailor\", &c2); ImGui::SameLine();\n        ImGui::Checkbox(\"Is\", &c3); ImGui::SameLine();\n        ImGui::Checkbox(\"Rich\", &c4);\n\n        // Various\n        static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f;\n        ImGui::PushItemWidth(ImGui::CalcTextSize(\"AAAAAAA\").x);\n        const char* items[] = { \"AAAA\", \"BBBB\", \"CCCC\", \"DDDD\" };\n        static int item = -1;\n        ImGui::Combo(\"Combo\", &item, items, IM_COUNTOF(items)); ImGui::SameLine();\n        ImGui::SliderFloat(\"X\", &f0, 0.0f, 5.0f); ImGui::SameLine();\n        ImGui::SliderFloat(\"Y\", &f1, 0.0f, 5.0f); ImGui::SameLine();\n        ImGui::SliderFloat(\"Z\", &f2, 0.0f, 5.0f);\n\n        ImGui::Text(\"Lists:\");\n        static int selection[4] = { 0, 1, 2, 3 };\n        for (int i = 0; i < 4; i++)\n        {\n            if (i > 0) ImGui::SameLine();\n            ImGui::PushID(i);\n            ImGui::ListBox(\"\", &selection[i], items, IM_COUNTOF(items));\n            ImGui::PopID();\n            //ImGui::SetItemTooltip(\"ListBox %d hovered\", i);\n        }\n        ImGui::PopItemWidth();\n\n        // Dummy\n        IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout/Dummy\");\n        ImVec2 button_sz(40, 40);\n        ImGui::Button(\"A\", button_sz); ImGui::SameLine();\n        ImGui::Dummy(button_sz); ImGui::SameLine();\n        ImGui::Button(\"B\", button_sz);\n\n        // Manually wrapping\n        // (we should eventually provide this as an automatic layout feature, but for now you can do it manually)\n        IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout/Manual wrapping\");\n        ImGui::Text(\"Manual wrapping:\");\n        ImGuiStyle& style = ImGui::GetStyle();\n        int buttons_count = 20;\n        float window_visible_x2 = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x;\n        for (int n = 0; n < buttons_count; n++)\n        {\n            ImGui::PushID(n);\n            ImGui::Button(\"Box\", button_sz);\n            float last_button_x2 = ImGui::GetItemRectMax().x;\n            float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line\n            if (n + 1 < buttons_count && next_button_x2 < window_visible_x2)\n                ImGui::SameLine();\n            ImGui::PopID();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Groups\"))\n    {\n        IMGUI_DEMO_MARKER(\"Layout/Groups\");\n        HelpMarker(\n            \"BeginGroup() basically locks the horizontal position for new line. \"\n            \"EndGroup() bundles the whole group so that you can use \\\"item\\\" functions such as \"\n            \"IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group.\");\n        ImGui::BeginGroup();\n        {\n            ImGui::BeginGroup();\n            ImGui::Button(\"AAA\");\n            ImGui::SameLine();\n            ImGui::Button(\"BBB\");\n            ImGui::SameLine();\n            ImGui::BeginGroup();\n            ImGui::Button(\"CCC\");\n            ImGui::Button(\"DDD\");\n            ImGui::EndGroup();\n            ImGui::SameLine();\n            ImGui::Button(\"EEE\");\n            ImGui::EndGroup();\n            ImGui::SetItemTooltip(\"First group hovered\");\n        }\n        // Capture the group size and create widgets using the same size\n        ImVec2 size = ImGui::GetItemRectSize();\n        const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f };\n        ImGui::PlotHistogram(\"##values\", values, IM_COUNTOF(values), 0, NULL, 0.0f, 1.0f, size);\n\n        ImGui::Button(\"ACTION\", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));\n        ImGui::SameLine();\n        ImGui::Button(\"REACTION\", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));\n        ImGui::EndGroup();\n        ImGui::SameLine();\n\n        ImGui::Button(\"LEVERAGE\\nBUZZWORD\", size);\n        ImGui::SameLine();\n\n        if (ImGui::BeginListBox(\"List\", size))\n        {\n            ImGui::Selectable(\"Selected\", true);\n            ImGui::Selectable(\"Not Selected\", false);\n            ImGui::EndListBox();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Text Baseline Alignment\"))\n    {\n        IMGUI_DEMO_MARKER(\"Layout/Text Baseline Alignment\");\n        {\n            ImGui::BulletText(\"Text baseline:\");\n            ImGui::SameLine(); HelpMarker(\n                \"This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. \"\n                \"Lines only composed of text or \\\"small\\\" widgets use less vertical space than lines with framed widgets.\");\n            ImGui::Indent();\n\n            ImGui::Text(\"KO Blahblah\"); ImGui::SameLine();\n            ImGui::Button(\"Some framed item\"); ImGui::SameLine();\n            HelpMarker(\"Baseline of button will look misaligned with text..\");\n\n            // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.\n            // (because we don't know what's coming after the Text() statement, we need to move the text baseline\n            // down by FramePadding.y ahead of time)\n            ImGui::AlignTextToFramePadding();\n            ImGui::Text(\"OK Blahblah\"); ImGui::SameLine();\n            ImGui::Button(\"Some framed item##2\"); ImGui::SameLine();\n            HelpMarker(\"We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y\");\n\n            // SmallButton() uses the same vertical padding as Text\n            ImGui::Button(\"TEST##1\"); ImGui::SameLine();\n            ImGui::Text(\"TEST\"); ImGui::SameLine();\n            ImGui::SmallButton(\"TEST##2\");\n\n            // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.\n            ImGui::AlignTextToFramePadding();\n            ImGui::Text(\"Text aligned to framed item\"); ImGui::SameLine();\n            ImGui::Button(\"Item##1\"); ImGui::SameLine();\n            ImGui::Text(\"Item\"); ImGui::SameLine();\n            ImGui::SmallButton(\"Item##2\"); ImGui::SameLine();\n            ImGui::Button(\"Item##3\");\n\n            ImGui::Unindent();\n        }\n\n        ImGui::Spacing();\n\n        {\n            ImGui::BulletText(\"Multi-line text:\");\n            ImGui::Indent();\n            ImGui::Text(\"One\\nTwo\\nThree\"); ImGui::SameLine();\n            ImGui::Text(\"Hello\\nWorld\"); ImGui::SameLine();\n            ImGui::Text(\"Banana\");\n\n            ImGui::Text(\"Banana\"); ImGui::SameLine();\n            ImGui::Text(\"Hello\\nWorld\"); ImGui::SameLine();\n            ImGui::Text(\"One\\nTwo\\nThree\");\n\n            ImGui::Button(\"HOP##1\"); ImGui::SameLine();\n            ImGui::Text(\"Banana\"); ImGui::SameLine();\n            ImGui::Text(\"Hello\\nWorld\"); ImGui::SameLine();\n            ImGui::Text(\"Banana\");\n\n            ImGui::Button(\"HOP##2\"); ImGui::SameLine();\n            ImGui::Text(\"Hello\\nWorld\"); ImGui::SameLine();\n            ImGui::Text(\"Banana\");\n            ImGui::Unindent();\n        }\n\n        ImGui::Spacing();\n\n        {\n            ImGui::BulletText(\"Misc items:\");\n            ImGui::Indent();\n\n            // SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button.\n            ImGui::Button(\"80x80\", ImVec2(80, 80));\n            ImGui::SameLine();\n            ImGui::Button(\"50x50\", ImVec2(50, 50));\n            ImGui::SameLine();\n            ImGui::Button(\"Button()\");\n            ImGui::SameLine();\n            ImGui::SmallButton(\"SmallButton()\");\n\n            // Tree\n            // (here the node appears after a button and has odd intent, so we use ImGuiTreeNodeFlags_DrawLinesNone to disable hierarchy outline)\n            const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;\n            ImGui::Button(\"Button##1\"); // Will make line higher\n            ImGui::SameLine(0.0f, spacing);\n            if (ImGui::TreeNodeEx(\"Node##1\", ImGuiTreeNodeFlags_DrawLinesNone))\n            {\n                // Placeholder tree data\n                for (int i = 0; i < 6; i++)\n                    ImGui::BulletText(\"Item %d..\", i);\n                ImGui::TreePop();\n            }\n\n            const float padding = (float)(int)(ImGui::GetFontSize() * 1.20f); // Large padding\n            ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, padding);\n            ImGui::Button(\"Button##2\");\n            ImGui::PopStyleVar();\n            ImGui::SameLine(0.0f, spacing);\n            if (ImGui::TreeNodeEx(\"Node##2\", ImGuiTreeNodeFlags_DrawLinesNone))\n                ImGui::TreePop();\n\n            // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget.\n            // Otherwise you can use SmallButton() (smaller fit).\n            ImGui::AlignTextToFramePadding();\n\n            // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add\n            // other contents \"inside\" the node.\n            bool node_open = ImGui::TreeNode(\"Node##3\");\n            ImGui::SameLine(0.0f, spacing); ImGui::Button(\"Button##3\");\n            if (node_open)\n            {\n                // Placeholder tree data\n                for (int i = 0; i < 6; i++)\n                    ImGui::BulletText(\"Item %d..\", i);\n                ImGui::TreePop();\n            }\n\n            // Bullet\n            ImGui::Button(\"Button##4\");\n            ImGui::SameLine(0.0f, spacing);\n            ImGui::BulletText(\"Bullet text\");\n\n            ImGui::AlignTextToFramePadding();\n            ImGui::BulletText(\"Node\");\n            ImGui::SameLine(0.0f, spacing); ImGui::Button(\"Button##5\");\n            ImGui::Unindent();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Scrolling\"))\n    {\n        IMGUI_DEMO_MARKER(\"Layout/Scrolling/Vertical\");\n        // Vertical scroll functions\n        HelpMarker(\"Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position.\");\n\n        static int track_item = 50;\n        static bool enable_track = true;\n        static bool enable_extra_decorations = false;\n        static float scroll_to_off_px = 0.0f;\n        static float scroll_to_pos_px = 200.0f;\n\n        ImGui::Checkbox(\"Decoration\", &enable_extra_decorations);\n\n        ImGui::PushItemWidth(ImGui::GetFontSize() * 10);\n        enable_track |= ImGui::DragInt(\"##item\", &track_item, 0.25f, 0, 99, \"Item = %d\");\n        ImGui::SameLine();\n        ImGui::Checkbox(\"Track\", &enable_track);\n\n        bool scroll_to_off = ImGui::DragFloat(\"##off\", &scroll_to_off_px, 1.00f, 0, FLT_MAX, \"+%.0f px\");\n        ImGui::SameLine();\n        scroll_to_off |= ImGui::Button(\"Scroll Offset\");\n\n        bool scroll_to_pos = ImGui::DragFloat(\"##pos\", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, \"X/Y = %.0f px\");\n        ImGui::SameLine();\n        scroll_to_pos |= ImGui::Button(\"Scroll To Pos\");\n        ImGui::PopItemWidth();\n\n        if (scroll_to_off || scroll_to_pos)\n            enable_track = false;\n\n        ImGuiStyle& style = ImGui::GetStyle();\n        float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5;\n        if (child_w < 1.0f)\n            child_w = 1.0f;\n        ImGui::PushID(\"##VerticalScrolling\");\n        for (int i = 0; i < 5; i++)\n        {\n            if (i > 0) ImGui::SameLine();\n            ImGui::BeginGroup();\n            const char* names[] = { \"Top\", \"25%\", \"Center\", \"75%\", \"Bottom\" };\n            ImGui::TextUnformatted(names[i]);\n\n            const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;\n            const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);\n            const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Borders, child_flags);\n            if (ImGui::BeginMenuBar())\n            {\n                ImGui::TextUnformatted(\"abc\");\n                ImGui::EndMenuBar();\n            }\n            if (scroll_to_off)\n                ImGui::SetScrollY(scroll_to_off_px);\n            if (scroll_to_pos)\n                ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f);\n            if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items\n            {\n                for (int item = 0; item < 100; item++)\n                {\n                    if (enable_track && item == track_item)\n                    {\n                        ImGui::TextColored(ImVec4(1, 1, 0, 1), \"Item %d\", item);\n                        ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom\n                    }\n                    else\n                    {\n                        ImGui::Text(\"Item %d\", item);\n                    }\n                }\n            }\n            float scroll_y = ImGui::GetScrollY();\n            float scroll_max_y = ImGui::GetScrollMaxY();\n            ImGui::EndChild();\n            ImGui::Text(\"%.0f/%.0f\", scroll_y, scroll_max_y);\n            ImGui::EndGroup();\n        }\n        ImGui::PopID();\n\n        // Horizontal scroll functions\n        IMGUI_DEMO_MARKER(\"Layout/Scrolling/Horizontal\");\n        ImGui::Spacing();\n        HelpMarker(\n            \"Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\\n\\n\"\n            \"Because the clipping rectangle of most window hides half worth of WindowPadding on the \"\n            \"left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the \"\n            \"equivalent SetScrollFromPosY(+1) wouldn't.\");\n        ImGui::PushID(\"##HorizontalScrolling\");\n        for (int i = 0; i < 5; i++)\n        {\n            float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f;\n            ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0);\n            ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);\n            bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Borders, child_flags);\n            if (scroll_to_off)\n                ImGui::SetScrollX(scroll_to_off_px);\n            if (scroll_to_pos)\n                ImGui::SetScrollFromPosX(ImGui::GetCursorStartPos().x + scroll_to_pos_px, i * 0.25f);\n            if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items\n            {\n                for (int item = 0; item < 100; item++)\n                {\n                    if (item > 0)\n                        ImGui::SameLine();\n                    if (enable_track && item == track_item)\n                    {\n                        ImGui::TextColored(ImVec4(1, 1, 0, 1), \"Item %d\", item);\n                        ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right\n                    }\n                    else\n                    {\n                        ImGui::Text(\"Item %d\", item);\n                    }\n                }\n            }\n            float scroll_x = ImGui::GetScrollX();\n            float scroll_max_x = ImGui::GetScrollMaxX();\n            ImGui::EndChild();\n            ImGui::SameLine();\n            const char* names[] = { \"Left\", \"25%\", \"Center\", \"75%\", \"Right\" };\n            ImGui::Text(\"%s\\n%.0f/%.0f\", names[i], scroll_x, scroll_max_x);\n            ImGui::Spacing();\n        }\n        ImGui::PopID();\n\n        // Miscellaneous Horizontal Scrolling Demo\n        IMGUI_DEMO_MARKER(\"Layout/Scrolling/Horizontal (more)\");\n        HelpMarker(\n            \"Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\\n\\n\"\n            \"You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin().\");\n        static int lines = 7;\n        ImGui::SliderInt(\"Lines\", &lines, 1, 15);\n        ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);\n        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f));\n        ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30);\n        ImGui::BeginChild(\"scrolling\", scrolling_child_size, ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar);\n        for (int line = 0; line < lines; line++)\n        {\n            // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine()\n            // If you want to create your own time line for a real application you may be better off manipulating\n            // the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets\n            // yourself. You may also want to use the lower-level ImDrawList API.\n            int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3);\n            for (int n = 0; n < num_buttons; n++)\n            {\n                if (n > 0) ImGui::SameLine();\n                ImGui::PushID(n + line * 1000);\n                char num_buf[16];\n                sprintf(num_buf, \"%d\", n);\n                const char* label = (!(n % 15)) ? \"FizzBuzz\" : (!(n % 3)) ? \"Fizz\" : (!(n % 5)) ? \"Buzz\" : num_buf;\n                float hue = n * 0.05f;\n                ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f));\n                ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f));\n                ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f));\n                ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f));\n                ImGui::PopStyleColor(3);\n                ImGui::PopID();\n            }\n        }\n        float scroll_x = ImGui::GetScrollX();\n        float scroll_max_x = ImGui::GetScrollMaxX();\n        ImGui::EndChild();\n        ImGui::PopStyleVar(2);\n        float scroll_x_delta = 0.0f;\n        ImGui::SmallButton(\"<<\");\n        if (ImGui::IsItemActive())\n            scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f;\n        ImGui::SameLine();\n        ImGui::Text(\"Scroll from code\"); ImGui::SameLine();\n        ImGui::SmallButton(\">>\");\n        if (ImGui::IsItemActive())\n            scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f;\n        ImGui::SameLine();\n        ImGui::Text(\"%.0f/%.0f\", scroll_x, scroll_max_x);\n        if (scroll_x_delta != 0.0f)\n        {\n            // Demonstrate a trick: you can use Begin to set yourself in the context of another window\n            // (here we are already out of your child window)\n            ImGui::BeginChild(\"scrolling\");\n            ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta);\n            ImGui::EndChild();\n        }\n        ImGui::Spacing();\n\n        static bool show_horizontal_contents_size_demo_window = false;\n        ImGui::Checkbox(\"Show Horizontal contents size demo window\", &show_horizontal_contents_size_demo_window);\n\n        if (show_horizontal_contents_size_demo_window)\n        {\n            static bool show_h_scrollbar = true;\n            static bool show_button = true;\n            static bool show_tree_nodes = true;\n            static bool show_text_wrapped = false;\n            static bool show_columns = true;\n            static bool show_tab_bar = true;\n            static bool show_child = false;\n            static bool explicit_content_size = false;\n            static float contents_size_x = 300.0f;\n            if (explicit_content_size)\n                ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f));\n            ImGui::Begin(\"Horizontal contents size demo window\", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0);\n            IMGUI_DEMO_MARKER(\"Layout/Scrolling/Horizontal contents size demo window\");\n            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0));\n            ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0));\n            HelpMarker(\n                \"Test how different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\\n\\n\"\n                \"Use 'Metrics->Tools->Show windows rectangles' to visualize rectangles.\");\n            ImGui::Checkbox(\"H-scrollbar\", &show_h_scrollbar);\n            ImGui::Checkbox(\"Button\", &show_button);            // Will grow contents size (unless explicitly overwritten)\n            ImGui::Checkbox(\"Tree nodes\", &show_tree_nodes);    // Will grow contents size and display highlight over full width\n            ImGui::Checkbox(\"Text wrapped\", &show_text_wrapped);// Will grow and use contents size\n            ImGui::Checkbox(\"Columns\", &show_columns);          // Will use contents size\n            ImGui::Checkbox(\"Tab bar\", &show_tab_bar);          // Will use contents size\n            ImGui::Checkbox(\"Child\", &show_child);              // Will grow and use contents size\n            ImGui::Checkbox(\"Explicit content size\", &explicit_content_size);\n            ImGui::Text(\"Scroll %.1f/%.1f %.1f/%.1f\", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(), ImGui::GetScrollMaxY());\n            if (explicit_content_size)\n            {\n                ImGui::SameLine();\n                ImGui::SetNextItemWidth(ImGui::CalcTextSize(\"123456\").x);\n                ImGui::DragFloat(\"##csx\", &contents_size_x);\n                ImVec2 p = ImGui::GetCursorScreenPos();\n                ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE);\n                ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(p.x + contents_size_x - 10, p.y), ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE);\n                ImGui::Dummy(ImVec2(0, 10));\n            }\n            ImGui::PopStyleVar(2);\n            ImGui::Separator();\n            if (show_button)\n            {\n                ImGui::Button(\"this is a 300-wide button\", ImVec2(300, 0));\n            }\n            if (show_tree_nodes)\n            {\n                bool open = true;\n                if (ImGui::TreeNode(\"this is a tree node\"))\n                {\n                    if (ImGui::TreeNode(\"another one of those tree node...\"))\n                    {\n                        ImGui::Text(\"Some tree contents\");\n                        ImGui::TreePop();\n                    }\n                    ImGui::TreePop();\n                }\n                ImGui::CollapsingHeader(\"CollapsingHeader\", &open);\n            }\n            if (show_text_wrapped)\n            {\n                ImGui::TextWrapped(\"This text should automatically wrap on the edge of the work rectangle.\");\n            }\n            if (show_columns)\n            {\n                ImGui::Text(\"Tables:\");\n                if (ImGui::BeginTable(\"table\", 4, ImGuiTableFlags_Borders))\n                {\n                    for (int n = 0; n < 4; n++)\n                    {\n                        ImGui::TableNextColumn();\n                        ImGui::Text(\"Width %.2f\", ImGui::GetContentRegionAvail().x);\n                    }\n                    ImGui::EndTable();\n                }\n                ImGui::Text(\"Columns:\");\n                ImGui::Columns(4);\n                for (int n = 0; n < 4; n++)\n                {\n                    ImGui::Text(\"Width %.2f\", ImGui::GetColumnWidth());\n                    ImGui::NextColumn();\n                }\n                ImGui::Columns(1);\n            }\n            if (show_tab_bar && ImGui::BeginTabBar(\"Hello\"))\n            {\n                if (ImGui::BeginTabItem(\"OneOneOne\")) { ImGui::EndTabItem(); }\n                if (ImGui::BeginTabItem(\"TwoTwoTwo\")) { ImGui::EndTabItem(); }\n                if (ImGui::BeginTabItem(\"ThreeThreeThree\")) { ImGui::EndTabItem(); }\n                if (ImGui::BeginTabItem(\"FourFourFour\")) { ImGui::EndTabItem(); }\n                ImGui::EndTabBar();\n            }\n            if (show_child)\n            {\n                ImGui::BeginChild(\"child\", ImVec2(0, 0), ImGuiChildFlags_Borders);\n                ImGui::EndChild();\n            }\n            ImGui::End();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Text Clipping\"))\n    {\n        IMGUI_DEMO_MARKER(\"Layout/Text Clipping\");\n        static ImVec2 size(100.0f, 100.0f);\n        static ImVec2 offset(30.0f, 30.0f);\n        ImGui::DragFloat2(\"size\", (float*)&size, 0.5f, 1.0f, 200.0f, \"%.0f\");\n        ImGui::TextWrapped(\"(Click and drag to scroll)\");\n\n        HelpMarker(\n            \"(Left) Using ImGui::PushClipRect():\\n\"\n            \"Will alter ImGui hit-testing logic + ImDrawList rendering.\\n\"\n            \"(use this if you want your clipping rectangle to affect interactions)\\n\\n\"\n            \"(Center) Using ImDrawList::PushClipRect():\\n\"\n            \"Will alter ImDrawList rendering only.\\n\"\n            \"(use this as a shortcut if you are only using ImDrawList calls)\\n\\n\"\n            \"(Right) Using ImDrawList::AddText() with a fine ClipRect:\\n\"\n            \"Will alter only this specific ImDrawList::AddText() rendering.\\n\"\n            \"This is often used internally to avoid altering the clipping rectangle and minimize draw calls.\");\n\n        for (int n = 0; n < 3; n++)\n        {\n            if (n > 0)\n                ImGui::SameLine();\n\n            ImGui::PushID(n);\n            ImGui::InvisibleButton(\"##canvas\", size);\n            if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left))\n            {\n                offset.x += ImGui::GetIO().MouseDelta.x;\n                offset.y += ImGui::GetIO().MouseDelta.y;\n            }\n            ImGui::PopID();\n            if (!ImGui::IsItemVisible()) // Skip rendering as ImDrawList elements are not clipped.\n                continue;\n\n            const ImVec2 p0 = ImGui::GetItemRectMin();\n            const ImVec2 p1 = ImGui::GetItemRectMax();\n            const char* text_str = \"Line 1 hello\\nLine 2 clip me!\";\n            const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y);\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n            switch (n)\n            {\n            case 0:\n                ImGui::PushClipRect(p0, p1, true);\n                draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));\n                draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);\n                ImGui::PopClipRect();\n                break;\n            case 1:\n                draw_list->PushClipRect(p0, p1, true);\n                draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));\n                draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);\n                draw_list->PopClipRect();\n                break;\n            case 2:\n                ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.\n                draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));\n                draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect);\n                break;\n            }\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Overlap Mode\"))\n    {\n        IMGUI_DEMO_MARKER(\"Layout/Overlap Mode\");\n        static bool enable_allow_overlap = true;\n\n        HelpMarker(\n            \"Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\\n\\n\"\n            \"By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. \"\n            \"Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state.\");\n        ImGui::Checkbox(\"Enable AllowOverlap\", &enable_allow_overlap);\n\n        ImVec2 button1_pos = ImGui::GetCursorScreenPos();\n        ImVec2 button2_pos = ImVec2(button1_pos.x + 50.0f, button1_pos.y + 50.0f);\n        if (enable_allow_overlap)\n            ImGui::SetNextItemAllowOverlap();\n        ImGui::Button(\"Button 1\", ImVec2(80, 80));\n        ImGui::SetCursorScreenPos(button2_pos);\n        ImGui::Button(\"Button 2\", ImVec2(80, 80));\n\n        // This is typically used with width-spanning items.\n        // (note that Selectable() has a dedicated flag ImGuiSelectableFlags_AllowOverlap, which is a shortcut\n        // for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.)\n        if (enable_allow_overlap)\n            ImGui::SetNextItemAllowOverlap();\n        ImGui::Selectable(\"Some Selectable\", false);\n        ImGui::SameLine();\n        ImGui::SmallButton(\"++\");\n\n        ImGui::TreePop();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowPopups()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowPopups()\n{\n    if (!ImGui::CollapsingHeader(\"Popups & Modal windows\"))\n        return;\n\n    // The properties of popups windows are:\n    // - They block normal mouse hovering detection outside them. (*)\n    // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE.\n    // - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as\n    //   we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup().\n    // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even\n    //     when normally blocked by a popup.\n    // Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close\n    // popups at any time.\n\n    // Typical use for regular windows:\n    //   bool my_tool_is_active = false; if (ImGui::Button(\"Open\")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin(\"My Tool\", &my_tool_is_active) { [...] } End();\n    // Typical use for popups:\n    //   if (ImGui::Button(\"Open\")) ImGui::OpenPopup(\"MyPopup\"); if (ImGui::BeginPopup(\"MyPopup\")) { [...] EndPopup(); }\n\n    // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.\n    // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.\n\n    if (ImGui::TreeNode(\"Popups\"))\n    {\n        IMGUI_DEMO_MARKER(\"Popups/Popups\");\n        ImGui::TextWrapped(\n            \"When a popup is active, it inhibits interacting with windows that are behind the popup. \"\n            \"Clicking outside the popup closes it.\");\n\n        static int selected_fish = -1;\n        const char* names[] = { \"Bream\", \"Haddock\", \"Mackerel\", \"Pollock\", \"Tilefish\" };\n        static bool toggles[] = { true, false, false, false, false };\n\n        // Simple selection popup (if you want to show the current selection inside the Button itself,\n        // you may want to build a string using the \"###\" operator to preserve a constant ID with a variable label)\n        if (ImGui::Button(\"Select..\"))\n            ImGui::OpenPopup(\"my_select_popup\");\n        ImGui::SameLine();\n        ImGui::TextUnformatted(selected_fish == -1 ? \"<None>\" : names[selected_fish]);\n        if (ImGui::BeginPopup(\"my_select_popup\"))\n        {\n            ImGui::SeparatorText(\"Aquarium\");\n            for (int i = 0; i < IM_COUNTOF(names); i++)\n                if (ImGui::Selectable(names[i]))\n                    selected_fish = i;\n            ImGui::EndPopup();\n        }\n\n        // Showing a menu with toggles\n        if (ImGui::Button(\"Toggle..\"))\n            ImGui::OpenPopup(\"my_toggle_popup\");\n        if (ImGui::BeginPopup(\"my_toggle_popup\"))\n        {\n            for (int i = 0; i < IM_COUNTOF(names); i++)\n                ImGui::MenuItem(names[i], \"\", &toggles[i]);\n            if (ImGui::BeginMenu(\"Sub-menu\"))\n            {\n                ImGui::MenuItem(\"Click me\");\n                ImGui::EndMenu();\n            }\n\n            ImGui::Separator();\n            ImGui::Text(\"Tooltip here\");\n            ImGui::SetItemTooltip(\"I am a tooltip over a popup\");\n\n            if (ImGui::Button(\"Stacked Popup\"))\n                ImGui::OpenPopup(\"another popup\");\n            if (ImGui::BeginPopup(\"another popup\"))\n            {\n                for (int i = 0; i < IM_COUNTOF(names); i++)\n                    ImGui::MenuItem(names[i], \"\", &toggles[i]);\n                if (ImGui::BeginMenu(\"Sub-menu\"))\n                {\n                    ImGui::MenuItem(\"Click me\");\n                    if (ImGui::Button(\"Stacked Popup\"))\n                        ImGui::OpenPopup(\"another popup\");\n                    if (ImGui::BeginPopup(\"another popup\"))\n                    {\n                        ImGui::Text(\"I am the last one here.\");\n                        ImGui::EndPopup();\n                    }\n                    ImGui::EndMenu();\n                }\n                ImGui::EndPopup();\n            }\n            ImGui::EndPopup();\n        }\n\n        // Call the more complete ShowExampleMenuFile which we use in various places of this demo\n        if (ImGui::Button(\"With a menu..\"))\n            ImGui::OpenPopup(\"my_file_popup\");\n        if (ImGui::BeginPopup(\"my_file_popup\", ImGuiWindowFlags_MenuBar))\n        {\n            if (ImGui::BeginMenuBar())\n            {\n                if (ImGui::BeginMenu(\"File\"))\n                {\n                    ShowExampleMenuFile();\n                    ImGui::EndMenu();\n                }\n                if (ImGui::BeginMenu(\"Edit\"))\n                {\n                    ImGui::MenuItem(\"Dummy\");\n                    ImGui::EndMenu();\n                }\n                ImGui::EndMenuBar();\n            }\n            ImGui::Text(\"Hello from popup!\");\n            ImGui::Button(\"This is a dummy button..\");\n            ImGui::EndPopup();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Context menus\"))\n    {\n        IMGUI_DEMO_MARKER(\"Popups/Context menus\");\n        HelpMarker(\"\\\"Context\\\" functions are simple helpers to associate a Popup to a given Item or Window identifier.\");\n\n        // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing:\n        //     if (id == 0)\n        //         id = GetItemID(); // Use last item id\n        //     if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))\n        //         OpenPopup(id);\n        //     return BeginPopup(id);\n        // For advanced uses you may want to replicate and customize this code.\n        // See more details in BeginPopupContextItem().\n\n        // Example 1\n        // When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(),\n        // and BeginPopupContextItem() will use the last item ID as the popup ID.\n        {\n            const char* names[5] = { \"Label1\", \"Label2\", \"Label3\", \"Label4\", \"Label5\" };\n            static int selected = -1;\n            for (int n = 0; n < 5; n++)\n            {\n                if (ImGui::Selectable(names[n], selected == n))\n                    selected = n;\n                if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id\n                {\n                    selected = n;\n                    ImGui::Text(\"This is a popup for \\\"%s\\\"!\", names[n]);\n                    if (ImGui::Button(\"Close\"))\n                        ImGui::CloseCurrentPopup();\n                    ImGui::EndPopup();\n                }\n                ImGui::SetItemTooltip(\"Right-click to open popup\");\n            }\n        }\n\n        // Example 2\n        // Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem().\n        // Using an explicit identifier is also convenient if you want to activate the popups from different locations.\n        {\n            HelpMarker(\"Text() elements don't have stable identifiers so we need to provide one.\");\n            static float value = 0.5f;\n            ImGui::Text(\"Value = %.3f <-- (1) right-click this text\", value);\n            if (ImGui::BeginPopupContextItem(\"my popup\"))\n            {\n                if (ImGui::Selectable(\"Set to zero\")) value = 0.0f;\n                if (ImGui::Selectable(\"Set to PI\")) value = 3.1415f;\n                ImGui::SetNextItemWidth(-FLT_MIN);\n                ImGui::DragFloat(\"##Value\", &value, 0.1f, 0.0f, 0.0f);\n                ImGui::EndPopup();\n            }\n\n            // We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup.\n            // Here we make it that right-clicking this other text element opens the same popup as above.\n            // The popup itself will be submitted by the code above.\n            ImGui::Text(\"(2) Or right-click this text\");\n            ImGui::OpenPopupOnItemClick(\"my popup\", ImGuiPopupFlags_MouseButtonRight);\n\n            // Back to square one: manually open the same popup.\n            if (ImGui::Button(\"(3) Or click this button\"))\n                ImGui::OpenPopup(\"my popup\");\n        }\n\n        // Example 3\n        // When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID),\n        // we need to make sure your item identifier is stable.\n        // In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ).\n        {\n            HelpMarker(\"Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator.\");\n            static char name[32] = \"Label1\";\n            char buf[64];\n            sprintf(buf, \"Button: %s###Button\", name); // ### operator override ID ignoring the preceding label\n            ImGui::Button(buf);\n            if (ImGui::BeginPopupContextItem())\n            {\n                ImGui::Text(\"Edit name:\");\n                ImGui::InputText(\"##edit\", name, IM_COUNTOF(name));\n                if (ImGui::Button(\"Close\"))\n                    ImGui::CloseCurrentPopup();\n                ImGui::EndPopup();\n            }\n            ImGui::SameLine(); ImGui::Text(\"(<-- right-click here)\");\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Modals\"))\n    {\n        IMGUI_DEMO_MARKER(\"Popups/Modals\");\n        ImGui::TextWrapped(\"Modal windows are like popups but the user cannot close them by clicking outside.\");\n\n        if (ImGui::Button(\"Delete..\"))\n            ImGui::OpenPopup(\"Delete?\");\n\n        // Always center this window when appearing\n        ImVec2 center = ImGui::GetMainViewport()->GetCenter();\n        ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));\n\n        if (ImGui::BeginPopupModal(\"Delete?\", NULL, ImGuiWindowFlags_AlwaysAutoResize))\n        {\n            ImGui::Text(\"All those beautiful files will be deleted.\\nThis operation cannot be undone!\");\n            ImGui::Separator();\n\n            //static int unused_i = 0;\n            //ImGui::Combo(\"Combo\", &unused_i, \"Delete\\0Delete harder\\0\");\n\n            static bool dont_ask_me_next_time = false;\n            ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));\n            ImGui::Checkbox(\"Don't ask me next time\", &dont_ask_me_next_time);\n            ImGui::PopStyleVar();\n\n            if (ImGui::Button(\"OK\", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }\n            ImGui::SetItemDefaultFocus();\n            ImGui::SameLine();\n            if (ImGui::Button(\"Cancel\", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }\n            ImGui::EndPopup();\n        }\n\n        if (ImGui::Button(\"Stacked modals..\"))\n            ImGui::OpenPopup(\"Stacked 1\");\n        if (ImGui::BeginPopupModal(\"Stacked 1\", NULL, ImGuiWindowFlags_MenuBar))\n        {\n            if (ImGui::BeginMenuBar())\n            {\n                if (ImGui::BeginMenu(\"File\"))\n                {\n                    if (ImGui::MenuItem(\"Some menu item\")) {}\n                    ImGui::EndMenu();\n                }\n                ImGui::EndMenuBar();\n            }\n            ImGui::Text(\"Hello from Stacked The First\\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it.\");\n\n            // Testing behavior of widgets stacking their own regular popups over the modal.\n            static int item = 1;\n            static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f };\n            ImGui::Combo(\"Combo\", &item, \"aaaa\\0bbbb\\0cccc\\0dddd\\0eeee\\0\\0\");\n            ImGui::ColorEdit4(\"Color\", color);\n\n            if (ImGui::Button(\"Add another modal..\"))\n                ImGui::OpenPopup(\"Stacked 2\");\n\n            // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which\n            // will close the popup. Note that the visibility state of popups is owned by imgui, so the input value\n            // of the bool actually doesn't matter here.\n            bool unused_open = true;\n            if (ImGui::BeginPopupModal(\"Stacked 2\", &unused_open))\n            {\n                ImGui::Text(\"Hello from Stacked The Second!\");\n                ImGui::ColorEdit4(\"Color\", color); // Allow opening another nested popup\n                if (ImGui::Button(\"Close\"))\n                    ImGui::CloseCurrentPopup();\n                ImGui::EndPopup();\n            }\n\n            if (ImGui::Button(\"Close\"))\n                ImGui::CloseCurrentPopup();\n            ImGui::EndPopup();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Menus inside a regular window\"))\n    {\n        IMGUI_DEMO_MARKER(\"Popups/Menus inside a regular window\");\n        ImGui::TextWrapped(\"Below we are testing adding menu items to a regular window. It's rather unusual but should work!\");\n        ImGui::Separator();\n\n        ImGui::MenuItem(\"Menu item\", \"Ctrl+M\");\n        if (ImGui::BeginMenu(\"Menu inside a regular window\"))\n        {\n            ShowExampleMenuFile();\n            ImGui::EndMenu();\n        }\n        ImGui::Separator();\n        ImGui::TreePop();\n    }\n}\n\n// Dummy data structure that we use for the Table demo.\n// (pre-C++11 doesn't allow us to instantiate ImVector<MyItem> template if this structure is defined inside the demo function)\nnamespace\n{\n// We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code.\n// This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID.\n// But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead! (ImGuiTableSortSpec::ColumnIndex)\n// If you don't use sorting, you will generally never care about giving column an ID!\nenum MyItemColumnID\n{\n    MyItemColumnID_ID,\n    MyItemColumnID_Name,\n    MyItemColumnID_Action,\n    MyItemColumnID_Quantity,\n    MyItemColumnID_Description\n};\n\nstruct MyItem\n{\n    int         ID;\n    const char* Name;\n    int         Quantity;\n\n    // We have a problem which is affecting _only this demo_ and should not affect your code:\n    // As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(),\n    // however qsort doesn't allow passing user data to comparing function.\n    // As a workaround, we are storing the sort specs in a static/global for the comparing function to access.\n    // In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global.\n    // We could technically call ImGui::TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called\n    // very often by the sorting algorithm it would be a little wasteful.\n    static const ImGuiTableSortSpecs* s_current_sort_specs;\n\n    static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, MyItem* items, int items_count)\n    {\n        s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.\n        if (items_count > 1)\n            qsort(items, (size_t)items_count, sizeof(items[0]), MyItem::CompareWithSortSpecs);\n        s_current_sort_specs = NULL;\n    }\n\n    // Compare function to be used by qsort()\n    static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)\n    {\n        const MyItem* a = (const MyItem*)lhs;\n        const MyItem* b = (const MyItem*)rhs;\n        for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)\n        {\n            // Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn()\n            // We could also choose to identify columns based on their index (sort_spec->ColumnIndex), which is simpler!\n            const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];\n            int delta = 0;\n            switch (sort_spec->ColumnUserID)\n            {\n            case MyItemColumnID_ID:             delta = (a->ID - b->ID);                break;\n            case MyItemColumnID_Name:           delta = (strcmp(a->Name, b->Name));     break;\n            case MyItemColumnID_Quantity:       delta = (a->Quantity - b->Quantity);    break;\n            case MyItemColumnID_Description:    delta = (strcmp(a->Name, b->Name));     break;\n            default: IM_ASSERT(0); break;\n            }\n            if (delta > 0)\n                return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;\n            if (delta < 0)\n                return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;\n        }\n\n        // qsort() is instable so always return a way to differentiate items.\n        // Your own compare function may want to avoid fallback on implicit sort specs.\n        // e.g. a Name compare if it wasn't already part of the sort specs.\n        return a->ID - b->ID;\n    }\n};\nconst ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL;\n}\n\n// Make the UI compact because there are so many fields\nstatic void PushStyleCompact()\n{\n    ImGuiStyle& style = ImGui::GetStyle();\n    ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, (float)(int)(style.FramePadding.y * 0.60f));\n    ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, (float)(int)(style.ItemSpacing.y * 0.60f));\n}\n\nstatic void PopStyleCompact()\n{\n    ImGui::PopStyleVar(2);\n}\n\n// Show a combo box with a choice of sizing policies\nstatic void EditTableSizingFlags(ImGuiTableFlags* p_flags)\n{\n    struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; };\n    static const EnumDesc policies[] =\n    {\n        { ImGuiTableFlags_None,               \"Default\",                            \"Use default sizing policy:\\n- ImGuiTableFlags_SizingFixedFit if ScrollX is on or if host window has ImGuiWindowFlags_AlwaysAutoResize.\\n- ImGuiTableFlags_SizingStretchSame otherwise.\" },\n        { ImGuiTableFlags_SizingFixedFit,     \"ImGuiTableFlags_SizingFixedFit\",     \"Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width.\" },\n        { ImGuiTableFlags_SizingFixedSame,    \"ImGuiTableFlags_SizingFixedSame\",    \"Columns are all the same width, matching the maximum contents width.\\nImplicitly disable ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible.\" },\n        { ImGuiTableFlags_SizingStretchProp,  \"ImGuiTableFlags_SizingStretchProp\",  \"Columns default to _WidthStretch with weights proportional to their widths.\" },\n        { ImGuiTableFlags_SizingStretchSame,  \"ImGuiTableFlags_SizingStretchSame\",  \"Columns default to _WidthStretch with same weights.\" }\n    };\n    int idx;\n    for (idx = 0; idx < IM_COUNTOF(policies); idx++)\n        if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_))\n            break;\n    const char* preview_text = (idx < IM_COUNTOF(policies)) ? policies[idx].Name + (idx > 0 ? strlen(\"ImGuiTableFlags\") : 0) : \"\";\n    if (ImGui::BeginCombo(\"Sizing Policy\", preview_text))\n    {\n        for (int n = 0; n < IM_COUNTOF(policies); n++)\n            if (ImGui::Selectable(policies[n].Name, idx == n))\n                *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value;\n        ImGui::EndCombo();\n    }\n    ImGui::SameLine();\n    ImGui::TextDisabled(\"(?)\");\n    if (ImGui::BeginItemTooltip())\n    {\n        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f);\n        for (int m = 0; m < IM_COUNTOF(policies); m++)\n        {\n            ImGui::Separator();\n            ImGui::Text(\"%s:\", policies[m].Name);\n            ImGui::Separator();\n            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f);\n            ImGui::TextUnformatted(policies[m].Tooltip);\n        }\n        ImGui::PopTextWrapPos();\n        ImGui::EndTooltip();\n    }\n}\n\nstatic void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags)\n{\n    ImGui::CheckboxFlags(\"_Disabled\", p_flags, ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker(\"Master disable flag (also hide from context menu)\");\n    ImGui::CheckboxFlags(\"_DefaultHide\", p_flags, ImGuiTableColumnFlags_DefaultHide);\n    ImGui::CheckboxFlags(\"_DefaultSort\", p_flags, ImGuiTableColumnFlags_DefaultSort);\n    if (ImGui::CheckboxFlags(\"_WidthStretch\", p_flags, ImGuiTableColumnFlags_WidthStretch))\n        *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthStretch);\n    if (ImGui::CheckboxFlags(\"_WidthFixed\", p_flags, ImGuiTableColumnFlags_WidthFixed))\n        *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthFixed);\n    ImGui::CheckboxFlags(\"_NoResize\", p_flags, ImGuiTableColumnFlags_NoResize);\n    ImGui::CheckboxFlags(\"_NoReorder\", p_flags, ImGuiTableColumnFlags_NoReorder);\n    ImGui::CheckboxFlags(\"_NoHide\", p_flags, ImGuiTableColumnFlags_NoHide);\n    ImGui::CheckboxFlags(\"_NoClip\", p_flags, ImGuiTableColumnFlags_NoClip);\n    ImGui::CheckboxFlags(\"_NoSort\", p_flags, ImGuiTableColumnFlags_NoSort);\n    ImGui::CheckboxFlags(\"_NoSortAscending\", p_flags, ImGuiTableColumnFlags_NoSortAscending);\n    ImGui::CheckboxFlags(\"_NoSortDescending\", p_flags, ImGuiTableColumnFlags_NoSortDescending);\n    ImGui::CheckboxFlags(\"_NoHeaderLabel\", p_flags, ImGuiTableColumnFlags_NoHeaderLabel);\n    ImGui::CheckboxFlags(\"_NoHeaderWidth\", p_flags, ImGuiTableColumnFlags_NoHeaderWidth);\n    ImGui::CheckboxFlags(\"_PreferSortAscending\", p_flags, ImGuiTableColumnFlags_PreferSortAscending);\n    ImGui::CheckboxFlags(\"_PreferSortDescending\", p_flags, ImGuiTableColumnFlags_PreferSortDescending);\n    ImGui::CheckboxFlags(\"_IndentEnable\", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker(\"Default for column 0\");\n    ImGui::CheckboxFlags(\"_IndentDisable\", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker(\"Default for column >0\");\n    ImGui::CheckboxFlags(\"_AngledHeader\", p_flags, ImGuiTableColumnFlags_AngledHeader);\n}\n\nstatic void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags)\n{\n    ImGui::CheckboxFlags(\"_IsEnabled\", &flags, ImGuiTableColumnFlags_IsEnabled);\n    ImGui::CheckboxFlags(\"_IsVisible\", &flags, ImGuiTableColumnFlags_IsVisible);\n    ImGui::CheckboxFlags(\"_IsSorted\", &flags, ImGuiTableColumnFlags_IsSorted);\n    ImGui::CheckboxFlags(\"_IsHovered\", &flags, ImGuiTableColumnFlags_IsHovered);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowTables()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowTables()\n{\n    //ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n    if (!ImGui::CollapsingHeader(\"Tables & Columns\"))\n        return;\n\n    // Using those as a base value to create width/height that are factor of the size of our font\n    const float TEXT_BASE_WIDTH = ImGui::CalcTextSize(\"A\").x;\n    const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();\n\n    ImGui::PushID(\"Tables\");\n\n    int open_action = -1;\n    if (ImGui::Button(\"Expand all\"))\n        open_action = 1;\n    ImGui::SameLine();\n    if (ImGui::Button(\"Collapse all\"))\n        open_action = 0;\n    ImGui::SameLine();\n\n    // Options\n    static bool disable_indent = false;\n    ImGui::Checkbox(\"Disable tree indentation\", &disable_indent);\n    ImGui::SameLine();\n    HelpMarker(\"Disable the indenting of tree nodes so demo tables can use the full window width.\");\n    ImGui::Separator();\n    if (disable_indent)\n        ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0.0f);\n\n    // About Styling of tables\n    // Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns APIs.\n    // There are however a few settings that a shared and part of the ImGuiStyle structure:\n    //   style.CellPadding                          // Padding within each cell\n    //   style.Colors[ImGuiCol_TableHeaderBg]       // Table header background\n    //   style.Colors[ImGuiCol_TableBorderStrong]   // Table outer and header borders\n    //   style.Colors[ImGuiCol_TableBorderLight]    // Table inner borders\n    //   style.Colors[ImGuiCol_TableRowBg]          // Table row background when ImGuiTableFlags_RowBg is enabled (even rows)\n    //   style.Colors[ImGuiCol_TableRowBgAlt]       // Table row background when ImGuiTableFlags_RowBg is enabled (odds rows)\n\n    // Demos\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Basic\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Basic\");\n        // Here we will showcase three different ways to output a table.\n        // They are very simple variations of a same thing!\n\n        // [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column.\n        // In many situations, this is the most flexible and easy to use pattern.\n        HelpMarker(\"Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop.\");\n        if (ImGui::BeginTable(\"table1\", 3))\n        {\n            for (int row = 0; row < 4; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Row %d Column %d\", row, column);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        // [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop + TableSetColumnIndex().\n        // This is generally more convenient when you have code manually submitting the contents of each column.\n        HelpMarker(\"Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually.\");\n        if (ImGui::BeginTable(\"table2\", 3))\n        {\n            for (int row = 0; row < 4; row++)\n            {\n                ImGui::TableNextRow();\n                ImGui::TableNextColumn();\n                ImGui::Text(\"Row %d\", row);\n                ImGui::TableNextColumn();\n                ImGui::Text(\"Some contents\");\n                ImGui::TableNextColumn();\n                ImGui::Text(\"123.456\");\n            }\n            ImGui::EndTable();\n        }\n\n        // [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(),\n        // as TableNextColumn() will automatically wrap around and create new rows as needed.\n        // This is generally more convenient when your cells all contains the same type of data.\n        HelpMarker(\n            \"Only using TableNextColumn(), which tends to be convenient for tables where every cell contains \"\n            \"the same type of contents.\\n This is also more similar to the old NextColumn() function of the \"\n            \"Columns API, and provided to facilitate the Columns->Tables API transition.\");\n        if (ImGui::BeginTable(\"table3\", 3))\n        {\n            for (int item = 0; item < 14; item++)\n            {\n                ImGui::TableNextColumn();\n                ImGui::Text(\"Item %d\", item);\n            }\n            ImGui::EndTable();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Borders, background\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Borders, background\");\n        // Expose a few Borders related flags interactively\n        enum ContentsType { CT_Text, CT_FillButton };\n        static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;\n        static bool display_headers = false;\n        static int contents_type = CT_Text;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_RowBg\", &flags, ImGuiTableFlags_RowBg);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Borders\", &flags, ImGuiTableFlags_Borders);\n        ImGui::SameLine(); HelpMarker(\"ImGuiTableFlags_Borders\\n = ImGuiTableFlags_BordersInnerV\\n | ImGuiTableFlags_BordersOuterV\\n | ImGuiTableFlags_BordersInnerH\\n | ImGuiTableFlags_BordersOuterH\");\n        ImGui::Indent();\n\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersH\", &flags, ImGuiTableFlags_BordersH);\n        ImGui::Indent();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterH\", &flags, ImGuiTableFlags_BordersOuterH);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerH\", &flags, ImGuiTableFlags_BordersInnerH);\n        ImGui::Unindent();\n\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersV\", &flags, ImGuiTableFlags_BordersV);\n        ImGui::Indent();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterV\", &flags, ImGuiTableFlags_BordersOuterV);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerV\", &flags, ImGuiTableFlags_BordersInnerV);\n        ImGui::Unindent();\n\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuter\", &flags, ImGuiTableFlags_BordersOuter);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInner\", &flags, ImGuiTableFlags_BordersInner);\n        ImGui::Unindent();\n\n        ImGui::AlignTextToFramePadding(); ImGui::Text(\"Cell contents:\");\n        ImGui::SameLine(); ImGui::RadioButton(\"Text\", &contents_type, CT_Text);\n        ImGui::SameLine(); ImGui::RadioButton(\"FillButton\", &contents_type, CT_FillButton);\n        ImGui::Checkbox(\"Display headers\", &display_headers);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBody\", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker(\"Disable vertical borders in columns Body (borders will always appear in Headers)\");\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table1\", 3, flags))\n        {\n            // Display headers so we can inspect their interaction with borders\n            // (Headers are not the main purpose of this section of the demo, so we are not elaborating on them now. See other sections for details)\n            if (display_headers)\n            {\n                ImGui::TableSetupColumn(\"One\");\n                ImGui::TableSetupColumn(\"Two\");\n                ImGui::TableSetupColumn(\"Three\");\n                ImGui::TableHeadersRow();\n            }\n\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    char buf[32];\n                    sprintf(buf, \"Hello %d,%d\", column, row);\n                    if (contents_type == CT_Text)\n                        ImGui::TextUnformatted(buf);\n                    else if (contents_type == CT_FillButton)\n                        ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Resizable, stretch\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Resizable, stretch\");\n        // By default, if we don't enable ScrollX the sizing policy for each column is \"Stretch\"\n        // All columns maintain a sizing weight, and they will occupy all available width.\n        static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersV\", &flags, ImGuiTableFlags_BordersV);\n        ImGui::SameLine(); HelpMarker(\n            \"Using the _Resizable flag automatically enables the _BordersInnerV flag as well, \"\n            \"this is why the resize borders are still showing when unchecking this.\");\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table1\", 3, flags))\n        {\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Hello %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Resizable, fixed\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Resizable, fixed\");\n        // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set)\n        // So columns will adopt the \"Fixed\" policy and will maintain a fixed width regardless of the whole available width (unless table is small)\n        // If there is not enough available width to fit all columns, they will however be resized down.\n        // FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings\n        HelpMarker(\n            \"Using _Resizable + _SizingFixedFit flags.\\n\"\n            \"Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\\n\\n\"\n            \"Double-click a column border to auto-fit the column to its contents.\");\n        PushStyleCompact();\n        static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendX\", &flags, ImGuiTableFlags_NoHostExtendX);\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table1\", 3, flags))\n        {\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Hello %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Resizable, mixed\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Resizable, mixed\");\n        HelpMarker(\n            \"Using TableSetupColumn() to alter resizing policy on a per-column basis.\\n\\n\"\n            \"When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch.\");\n        static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;\n\n        if (ImGui::BeginTable(\"table1\", 3, flags))\n        {\n            ImGui::TableSetupColumn(\"AAA\", ImGuiTableColumnFlags_WidthFixed);\n            ImGui::TableSetupColumn(\"BBB\", ImGuiTableColumnFlags_WidthFixed);\n            ImGui::TableSetupColumn(\"CCC\", ImGuiTableColumnFlags_WidthStretch);\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"%s %d,%d\", (column == 2) ? \"Stretch\" : \"Fixed\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        if (ImGui::BeginTable(\"table2\", 6, flags))\n        {\n            ImGui::TableSetupColumn(\"AAA\", ImGuiTableColumnFlags_WidthFixed);\n            ImGui::TableSetupColumn(\"BBB\", ImGuiTableColumnFlags_WidthFixed);\n            ImGui::TableSetupColumn(\"CCC\", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultHide);\n            ImGui::TableSetupColumn(\"DDD\", ImGuiTableColumnFlags_WidthStretch);\n            ImGui::TableSetupColumn(\"EEE\", ImGuiTableColumnFlags_WidthStretch);\n            ImGui::TableSetupColumn(\"FFF\", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultHide);\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 6; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"%s %d,%d\", (column >= 3) ? \"Stretch\" : \"Fixed\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Reorderable, hideable, with headers\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Reorderable, hideable, with headers\");\n        HelpMarker(\n            \"Click and drag column headers to reorder columns.\\n\\n\"\n            \"Right-click on a header to open a context menu.\");\n        static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Reorderable\", &flags, ImGuiTableFlags_Reorderable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Hideable\", &flags, ImGuiTableFlags_Hideable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBody\", &flags, ImGuiTableFlags_NoBordersInBody);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBodyUntilResize\", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker(\"Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_HighlightHoveredColumn\", &flags, ImGuiTableFlags_HighlightHoveredColumn);\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table1\", 3, flags))\n        {\n            // Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in each column.\n            // (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight etc.)\n            ImGui::TableSetupColumn(\"One\");\n            ImGui::TableSetupColumn(\"Two\");\n            ImGui::TableSetupColumn(\"Three\");\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 6; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Hello %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        // Use outer_size.x == 0.0f instead of default to make the table as tight as possible\n        // (only valid when no scrolling and no stretch column)\n        if (ImGui::BeginTable(\"table2\", 3, flags | ImGuiTableFlags_SizingFixedFit, ImVec2(0.0f, 0.0f)))\n        {\n            ImGui::TableSetupColumn(\"One\");\n            ImGui::TableSetupColumn(\"Two\");\n            ImGui::TableSetupColumn(\"Three\");\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 6; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Fixed %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Padding\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Padding\");\n        // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding.\n        // We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding.\n        HelpMarker(\n            \"We often want outer padding activated when any using features which makes the edges of a column visible:\\n\"\n            \"e.g.:\\n\"\n            \"- BorderOuterV\\n\"\n            \"- any form of row selection\\n\"\n            \"Because of this, activating BorderOuterV sets the default to PadOuterX. \"\n            \"Using PadOuterX or NoPadOuterX you can override the default.\\n\\n\"\n            \"Actual padding values are using style.CellPadding.\\n\\n\"\n            \"In this demo we don't show horizontal borders to emphasize how they don't affect default horizontal padding.\");\n\n        static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_PadOuterX\", &flags1, ImGuiTableFlags_PadOuterX);\n        ImGui::SameLine(); HelpMarker(\"Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoPadOuterX\", &flags1, ImGuiTableFlags_NoPadOuterX);\n        ImGui::SameLine(); HelpMarker(\"Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoPadInnerX\", &flags1, ImGuiTableFlags_NoPadInnerX);\n        ImGui::SameLine(); HelpMarker(\"Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterV\", &flags1, ImGuiTableFlags_BordersOuterV);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerV\", &flags1, ImGuiTableFlags_BordersInnerV);\n        static bool show_headers = false;\n        ImGui::Checkbox(\"show_headers\", &show_headers);\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table_padding\", 3, flags1))\n        {\n            if (show_headers)\n            {\n                ImGui::TableSetupColumn(\"One\");\n                ImGui::TableSetupColumn(\"Two\");\n                ImGui::TableSetupColumn(\"Three\");\n                ImGui::TableHeadersRow();\n            }\n\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    if (row == 0)\n                    {\n                        ImGui::Text(\"Avail %.2f\", ImGui::GetContentRegionAvail().x);\n                    }\n                    else\n                    {\n                        char buf[32];\n                        sprintf(buf, \"Hello %d,%d\", column, row);\n                        ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));\n                    }\n                    //if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered)\n                    //    ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(0, 100, 0, 255));\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        // Second example: set style.CellPadding to (0.0) or a custom value.\n        // FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one...\n        HelpMarker(\"Setting style.CellPadding to (0,0) or a custom value.\");\n        static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;\n        static ImVec2 cell_padding(0.0f, 0.0f);\n        static bool show_widget_frame_bg = true;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Borders\", &flags2, ImGuiTableFlags_Borders);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersH\", &flags2, ImGuiTableFlags_BordersH);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersV\", &flags2, ImGuiTableFlags_BordersV);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInner\", &flags2, ImGuiTableFlags_BordersInner);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuter\", &flags2, ImGuiTableFlags_BordersOuter);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_RowBg\", &flags2, ImGuiTableFlags_RowBg);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags2, ImGuiTableFlags_Resizable);\n        ImGui::Checkbox(\"show_widget_frame_bg\", &show_widget_frame_bg);\n        ImGui::SliderFloat2(\"CellPadding\", &cell_padding.x, 0.0f, 10.0f, \"%.0f\");\n        PopStyleCompact();\n\n        ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding);\n        if (ImGui::BeginTable(\"table_padding_2\", 3, flags2))\n        {\n            static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells\n            static bool init = true;\n            if (!show_widget_frame_bg)\n                ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);\n            for (int cell = 0; cell < 3 * 5; cell++)\n            {\n                ImGui::TableNextColumn();\n                if (init)\n                    strcpy(text_bufs[cell], \"edit me\");\n                ImGui::SetNextItemWidth(-FLT_MIN);\n                ImGui::PushID(cell);\n                ImGui::InputText(\"##cell\", text_bufs[cell], IM_COUNTOF(text_bufs[cell]));\n                ImGui::PopID();\n            }\n            if (!show_widget_frame_bg)\n                ImGui::PopStyleColor();\n            init = false;\n            ImGui::EndTable();\n        }\n        ImGui::PopStyleVar();\n\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Sizing policies\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Explicit widths\");\n        static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags1, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendX\", &flags1, ImGuiTableFlags_NoHostExtendX);\n        PopStyleCompact();\n\n        static ImGuiTableFlags sizing_policy_flags[4] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame };\n        for (int table_n = 0; table_n < 4; table_n++)\n        {\n            ImGui::PushID(table_n);\n            ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30);\n            EditTableSizingFlags(&sizing_policy_flags[table_n]);\n\n            // To make it easier to understand the different sizing policy,\n            // For each policy: we display one table where the columns have equal contents width,\n            // and one where the columns have different contents width.\n            if (ImGui::BeginTable(\"table1\", 3, sizing_policy_flags[table_n] | flags1))\n            {\n                for (int row = 0; row < 3; row++)\n                {\n                    ImGui::TableNextRow();\n                    ImGui::TableNextColumn(); ImGui::Text(\"Oh dear\");\n                    ImGui::TableNextColumn(); ImGui::Text(\"Oh dear\");\n                    ImGui::TableNextColumn(); ImGui::Text(\"Oh dear\");\n                }\n                ImGui::EndTable();\n            }\n            if (ImGui::BeginTable(\"table2\", 3, sizing_policy_flags[table_n] | flags1))\n            {\n                for (int row = 0; row < 3; row++)\n                {\n                    ImGui::TableNextRow();\n                    ImGui::TableNextColumn(); ImGui::Text(\"AAAA\");\n                    ImGui::TableNextColumn(); ImGui::Text(\"BBBBBBBB\");\n                    ImGui::TableNextColumn(); ImGui::Text(\"CCCCCCCCCCCC\");\n                }\n                ImGui::EndTable();\n            }\n            ImGui::PopID();\n        }\n\n        ImGui::Spacing();\n        ImGui::TextUnformatted(\"Advanced\");\n        ImGui::SameLine();\n        HelpMarker(\n            \"This section allows you to interact and see the effect of various sizing policies \"\n            \"depending on whether Scroll is enabled and the contents of your columns.\");\n\n        enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText };\n        static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable;\n        static int contents_type = CT_ShowWidth;\n        static int column_count = 3;\n\n        PushStyleCompact();\n        ImGui::PushID(\"Advanced\");\n        ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);\n        EditTableSizingFlags(&flags);\n        ImGui::Combo(\"Contents\", &contents_type, \"Show width\\0Short Text\\0Long Text\\0Button\\0Fill Button\\0InputText\\0\");\n        if (contents_type == CT_FillButton)\n        {\n            ImGui::SameLine();\n            HelpMarker(\n                \"Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop \"\n                \"where contents width can feed into auto-column width can feed into contents width.\");\n        }\n        ImGui::DragInt(\"Columns\", &column_count, 0.1f, 1, 64, \"%d\", ImGuiSliderFlags_AlwaysClamp);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_PreciseWidths\", &flags, ImGuiTableFlags_PreciseWidths);\n        ImGui::SameLine(); HelpMarker(\"Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollX\", &flags, ImGuiTableFlags_ScrollX);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollY\", &flags, ImGuiTableFlags_ScrollY);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoClip\", &flags, ImGuiTableFlags_NoClip);\n        ImGui::PopItemWidth();\n        ImGui::PopID();\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table2\", column_count, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 7)))\n        {\n            for (int cell = 0; cell < 10 * column_count; cell++)\n            {\n                ImGui::TableNextColumn();\n                int column = ImGui::TableGetColumnIndex();\n                int row = ImGui::TableGetRowIndex();\n\n                ImGui::PushID(cell);\n                char label[32];\n                static char text_buf[32] = \"\";\n                sprintf(label, \"Hello %d,%d\", column, row);\n                switch (contents_type)\n                {\n                case CT_ShortText:  ImGui::TextUnformatted(label); break;\n                case CT_LongText:   ImGui::Text(\"Some %s text %d,%d\\nOver two lines..\", column == 0 ? \"long\" : \"longeeer\", column, row); break;\n                case CT_ShowWidth:  ImGui::Text(\"W: %.1f\", ImGui::GetContentRegionAvail().x); break;\n                case CT_Button:     ImGui::Button(label); break;\n                case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break;\n                case CT_InputText:  ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText(\"##\", text_buf, IM_COUNTOF(text_buf)); break;\n                }\n                ImGui::PopID();\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Vertical scrolling, with clipping\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Vertical scrolling, with clipping\");\n        HelpMarker(\n            \"Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\\n\\n\"\n            \"We also demonstrate using ImGuiListClipper to virtualize the submission of many items.\");\n        static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollY\", &flags, ImGuiTableFlags_ScrollY);\n        PopStyleCompact();\n\n        // When using ScrollX or ScrollY we need to specify a size for our table container!\n        // Otherwise by default the table will fit all available space, like a BeginChild() call.\n        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);\n        if (ImGui::BeginTable(\"table_scrolly\", 3, flags, outer_size))\n        {\n            ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible\n            ImGui::TableSetupColumn(\"One\", ImGuiTableColumnFlags_None);\n            ImGui::TableSetupColumn(\"Two\", ImGuiTableColumnFlags_None);\n            ImGui::TableSetupColumn(\"Three\", ImGuiTableColumnFlags_None);\n            ImGui::TableHeadersRow();\n\n            // Demonstrate using clipper for large vertical lists\n            ImGuiListClipper clipper;\n            clipper.Begin(1000);\n            while (clipper.Step())\n            {\n                for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++)\n                {\n                    ImGui::TableNextRow();\n                    for (int column = 0; column < 3; column++)\n                    {\n                        ImGui::TableSetColumnIndex(column);\n                        ImGui::Text(\"Hello %d,%d\", column, row);\n                    }\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Horizontal scrolling\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Horizontal scrolling\");\n        HelpMarker(\n            \"When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, \"\n            \"as automatically stretching columns doesn't make much sense with horizontal scrolling.\\n\\n\"\n            \"Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX, \"\n            \"because the container window won't automatically extend vertically to fix contents \"\n            \"(this may be improved in future versions).\");\n        static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;\n        static int freeze_cols = 1;\n        static int freeze_rows = 1;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollX\", &flags, ImGuiTableFlags_ScrollX);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollY\", &flags, ImGuiTableFlags_ScrollY);\n        ImGui::SetNextItemWidth(ImGui::GetFrameHeight());\n        ImGui::DragInt(\"freeze_cols\", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);\n        ImGui::SetNextItemWidth(ImGui::GetFrameHeight());\n        ImGui::DragInt(\"freeze_rows\", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);\n        PopStyleCompact();\n\n        // When using ScrollX or ScrollY we need to specify a size for our table container!\n        // Otherwise by default the table will fit all available space, like a BeginChild() call.\n        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);\n        if (ImGui::BeginTable(\"table_scrollx\", 7, flags, outer_size))\n        {\n            ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);\n            ImGui::TableSetupColumn(\"Line #\", ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze()\n            ImGui::TableSetupColumn(\"One\");\n            ImGui::TableSetupColumn(\"Two\");\n            ImGui::TableSetupColumn(\"Three\");\n            ImGui::TableSetupColumn(\"Four\");\n            ImGui::TableSetupColumn(\"Five\");\n            ImGui::TableSetupColumn(\"Six\");\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 20; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 7; column++)\n                {\n                    // Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or performing width measurement.\n                    // Because here we know that:\n                    // - A) all our columns are contributing the same to row height\n                    // - B) column 0 is always visible,\n                    // We only always submit this one column and can skip others.\n                    // More advanced per-column clipping behaviors may benefit from polling the status flags via TableGetColumnFlags().\n                    if (!ImGui::TableSetColumnIndex(column) && column > 0)\n                        continue;\n                    if (column == 0)\n                        ImGui::Text(\"Line %d\", row);\n                    else\n                        ImGui::Text(\"Hello world %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        ImGui::Spacing();\n        ImGui::TextUnformatted(\"Stretch + ScrollX\");\n        ImGui::SameLine();\n        HelpMarker(\n            \"Showcase using Stretch columns + ScrollX together: \"\n            \"this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\\n\"\n            \"Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns \"\n            \"along with ScrollX doesn't make sense.\");\n        static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;\n        static float inner_width = 1000.0f;\n        PushStyleCompact();\n        ImGui::PushID(\"flags3\");\n        ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollX\", &flags2, ImGuiTableFlags_ScrollX);\n        ImGui::DragFloat(\"inner_width\", &inner_width, 1.0f, 0.0f, FLT_MAX, \"%.1f\");\n        ImGui::PopItemWidth();\n        ImGui::PopID();\n        PopStyleCompact();\n        if (ImGui::BeginTable(\"table2\", 7, flags2, outer_size, inner_width))\n        {\n            for (int cell = 0; cell < 20 * 7; cell++)\n            {\n                ImGui::TableNextColumn();\n                ImGui::Text(\"Hello world %d,%d\", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex());\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Columns flags\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Columns flags\");\n        // Create a first table just to show all the options/flags we want to make visible in our example!\n        const int column_count = 3;\n        const char* column_names[column_count] = { \"One\", \"Two\", \"Three\" };\n        static ImGuiTableColumnFlags column_flags[column_count] = { ImGuiTableColumnFlags_DefaultSort, ImGuiTableColumnFlags_None, ImGuiTableColumnFlags_DefaultHide };\n        static ImGuiTableColumnFlags column_flags_out[column_count] = { 0, 0, 0 }; // Output from TableGetColumnFlags()\n\n        if (ImGui::BeginTable(\"table_columns_flags_checkboxes\", column_count, ImGuiTableFlags_None))\n        {\n            PushStyleCompact();\n            for (int column = 0; column < column_count; column++)\n            {\n                ImGui::TableNextColumn();\n                ImGui::PushID(column);\n                ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation across columns\n                ImGui::Text(\"'%s'\", column_names[column]);\n                ImGui::Spacing();\n                ImGui::Text(\"Input flags:\");\n                EditTableColumnsFlags(&column_flags[column]);\n                ImGui::Spacing();\n                ImGui::Text(\"Output flags:\");\n                ImGui::BeginDisabled();\n                ShowTableColumnsStatusFlags(column_flags_out[column]);\n                ImGui::EndDisabled();\n                ImGui::PopID();\n            }\n            PopStyleCompact();\n            ImGui::EndTable();\n        }\n\n        // Create the real table we care about for the example!\n        // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above,\n        // otherwise in a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible\n        // + resizing the parent window down).\n        const ImGuiTableFlags flags\n            = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY\n            | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV\n            | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable;\n        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9);\n        if (ImGui::BeginTable(\"table_columns_flags\", column_count, flags, outer_size))\n        {\n            bool has_angled_header = false;\n            for (int column = 0; column < column_count; column++)\n            {\n                has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0;\n                ImGui::TableSetupColumn(column_names[column], column_flags[column]);\n            }\n            if (has_angled_header)\n                ImGui::TableAngledHeadersRow();\n            ImGui::TableHeadersRow();\n            for (int column = 0; column < column_count; column++)\n                column_flags_out[column] = ImGui::TableGetColumnFlags(column);\n            float indent_step = (float)((int)TEXT_BASE_WIDTH / 2);\n            for (int row = 0; row < 8; row++)\n            {\n                // Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags.\n                ImGui::Indent(indent_step);\n                ImGui::TableNextRow();\n                for (int column = 0; column < column_count; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"%s %s\", (column == 0) ? \"Indented\" : \"Hello\", ImGui::TableGetColumnName(column));\n                }\n            }\n            ImGui::Unindent(indent_step * 8.0f);\n\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Columns widths\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Columns widths\");\n        HelpMarker(\"Using TableSetupColumn() to setup default width.\");\n\n        static ImGuiTableFlags flags1 = ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags1, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBodyUntilResize\", &flags1, ImGuiTableFlags_NoBordersInBodyUntilResize);\n        PopStyleCompact();\n        if (ImGui::BeginTable(\"table1\", 3, flags1))\n        {\n            // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed.\n            ImGui::TableSetupColumn(\"one\", ImGuiTableColumnFlags_WidthFixed, 100.0f); // Default to 100.0f\n            ImGui::TableSetupColumn(\"two\", ImGuiTableColumnFlags_WidthFixed, 200.0f); // Default to 200.0f\n            ImGui::TableSetupColumn(\"three\", ImGuiTableColumnFlags_WidthFixed);       // Default to auto\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 4; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    if (row == 0)\n                        ImGui::Text(\"(w: %5.1f)\", ImGui::GetContentRegionAvail().x);\n                    else\n                        ImGui::Text(\"Hello %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        HelpMarker(\n            \"Using TableSetupColumn() to setup explicit width.\\n\\nUnless _NoKeepColumnsVisible is set, \"\n            \"fixed columns with set width may still be shrunk down if there's not enough space in the host.\");\n\n        static ImGuiTableFlags flags2 = ImGuiTableFlags_None;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoKeepColumnsVisible\", &flags2, ImGuiTableFlags_NoKeepColumnsVisible);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerV\", &flags2, ImGuiTableFlags_BordersInnerV);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterV\", &flags2, ImGuiTableFlags_BordersOuterV);\n        PopStyleCompact();\n        if (ImGui::BeginTable(\"table2\", 4, flags2))\n        {\n            // We could also set ImGuiTableFlags_SizingFixedFit on the table and then all columns\n            // will default to ImGuiTableColumnFlags_WidthFixed.\n            ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthFixed, 100.0f);\n            ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);\n            ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f);\n            ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 4; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    if (row == 0)\n                        ImGui::Text(\"(w: %5.1f)\", ImGui::GetContentRegionAvail().x);\n                    else\n                        ImGui::Text(\"Hello %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Nested tables\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Nested tables\");\n        HelpMarker(\"This demonstrates embedding a table into another table cell.\");\n\n        if (ImGui::BeginTable(\"table_nested1\", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))\n        {\n            ImGui::TableSetupColumn(\"A0\");\n            ImGui::TableSetupColumn(\"A1\");\n            ImGui::TableHeadersRow();\n\n            ImGui::TableNextColumn();\n            ImGui::Text(\"A0 Row 0\");\n            {\n                float rows_height = (TEXT_BASE_HEIGHT * 2.0f) + (ImGui::GetStyle().CellPadding.y * 2.0f);\n                if (ImGui::BeginTable(\"table_nested2\", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))\n                {\n                    ImGui::TableSetupColumn(\"B0\");\n                    ImGui::TableSetupColumn(\"B1\");\n                    ImGui::TableHeadersRow();\n\n                    ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"B0 Row 0\");\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"B1 Row 0\");\n                    ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"B0 Row 1\");\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"B1 Row 1\");\n\n                    ImGui::EndTable();\n                }\n            }\n            ImGui::TableNextColumn(); ImGui::Text(\"A1 Row 0\");\n            ImGui::TableNextColumn(); ImGui::Text(\"A0 Row 1\");\n            ImGui::TableNextColumn(); ImGui::Text(\"A1 Row 1\");\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Row height\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Row height\");\n        HelpMarker(\n            \"You can pass a 'min_row_height' to TableNextRow().\\n\\nRows are padded with 'style.CellPadding.y' on top and bottom, \"\n            \"so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\\n\\n\"\n            \"We cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row.\");\n        if (ImGui::BeginTable(\"table_row_height\", 1, ImGuiTableFlags_Borders))\n        {\n            for (int row = 0; row < 8; row++)\n            {\n                float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row + ImGui::GetStyle().CellPadding.y * 2.0f);\n                ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height);\n                ImGui::TableNextColumn();\n                ImGui::Text(\"min_row_height = %.2f\", min_row_height);\n            }\n            ImGui::EndTable();\n        }\n\n        HelpMarker(\n            \"Showcase using SameLine(0,0) to share Current Line Height between cells.\\n\\n\"\n            \"Please note that Tables Row Height is not the same thing as Current Line Height, \"\n            \"as a table cell may contains multiple lines.\");\n        if (ImGui::BeginTable(\"table_share_lineheight\", 2, ImGuiTableFlags_Borders))\n        {\n            ImGui::TableNextRow();\n            ImGui::TableNextColumn();\n            ImGui::ColorButton(\"##1\", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40));\n            ImGui::TableNextColumn();\n            ImGui::Text(\"Line 1\");\n            ImGui::Text(\"Line 2\");\n\n            ImGui::TableNextRow();\n            ImGui::TableNextColumn();\n            ImGui::ColorButton(\"##2\", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40));\n            ImGui::TableNextColumn();\n            ImGui::SameLine(0.0f, 0.0f); // Reuse line height from previous column\n            ImGui::Text(\"Line 1, with SameLine(0,0)\");\n            ImGui::Text(\"Line 2\");\n\n            ImGui::EndTable();\n        }\n\n        HelpMarker(\"Showcase altering CellPadding.y between rows. Note that CellPadding.x is locked for the entire table.\");\n        if (ImGui::BeginTable(\"table_changing_cellpadding_y\", 1, ImGuiTableFlags_Borders))\n        {\n            ImGuiStyle& style = ImGui::GetStyle();\n            for (int row = 0; row < 8; row++)\n            {\n                if ((row % 3) == 2)\n                    ImGui::PushStyleVarY(ImGuiStyleVar_CellPadding, 20.0f);\n                ImGui::TableNextRow(ImGuiTableRowFlags_None);\n                ImGui::TableNextColumn();\n                ImGui::Text(\"CellPadding.y = %.2f\", style.CellPadding.y);\n                if ((row % 3) == 2)\n                    ImGui::PopStyleVar();\n            }\n            ImGui::EndTable();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Outer size\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Outer size\");\n        // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY\n        // Important to that note how the two flags have slightly different behaviors!\n        ImGui::Text(\"Using NoHostExtendX and NoHostExtendY:\");\n        PushStyleCompact();\n        static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendX\", &flags, ImGuiTableFlags_NoHostExtendX);\n        ImGui::SameLine(); HelpMarker(\"Make outer width auto-fit to columns, overriding outer_size.x value.\\n\\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used.\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendY\", &flags, ImGuiTableFlags_NoHostExtendY);\n        ImGui::SameLine(); HelpMarker(\"Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\\n\\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.\");\n        PopStyleCompact();\n\n        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);\n        if (ImGui::BeginTable(\"table1\", 3, flags, outer_size))\n        {\n            for (int row = 0; row < 10; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"Cell %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::SameLine();\n        ImGui::Text(\"Hello!\");\n\n        ImGui::Spacing();\n\n        ImGui::Text(\"Using explicit size:\");\n        if (ImGui::BeginTable(\"table2\", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))\n        {\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"Cell %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::SameLine();\n        if (ImGui::BeginTable(\"table3\", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))\n        {\n            const float rows_height = TEXT_BASE_HEIGHT * 1.5f + ImGui::GetStyle().CellPadding.y * 2.0f;\n            for (int row = 0; row < 3; row++)\n            {\n                ImGui::TableNextRow(0, rows_height);\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"Cell %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Background color\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Background color\");\n        static ImGuiTableFlags flags = ImGuiTableFlags_RowBg;\n        static int row_bg_type = 1;\n        static int row_bg_target = 1;\n        static int cell_bg_type = 1;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Borders\", &flags, ImGuiTableFlags_Borders);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_RowBg\", &flags, ImGuiTableFlags_RowBg);\n        ImGui::SameLine(); HelpMarker(\"ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style.\");\n        ImGui::Combo(\"row bg type\", (int*)&row_bg_type, \"None\\0Red\\0Gradient\\0\");\n        ImGui::Combo(\"row bg target\", (int*)&row_bg_target, \"RowBg0\\0RowBg1\\0\"); ImGui::SameLine(); HelpMarker(\"Target RowBg0 to override the alternating odd/even colors,\\nTarget RowBg1 to blend with them.\");\n        ImGui::Combo(\"cell bg type\", (int*)&cell_bg_type, \"None\\0Blue\\0\"); ImGui::SameLine(); HelpMarker(\"We are colorizing cells to B1->C2 here.\");\n        IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2);\n        IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1);\n        IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1);\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table1\", 5, flags))\n        {\n            for (int row = 0; row < 6; row++)\n            {\n                ImGui::TableNextRow();\n\n                // Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX, ...)'\n                // We use a transparent color so we can see the one behind in case our target is RowBg1 and RowBg0 was already targeted by the ImGuiTableFlags_RowBg flag.\n                if (row_bg_type != 0)\n                {\n                    ImU32 row_bg_color = ImGui::GetColorU32(row_bg_type == 1 ? ImVec4(0.7f, 0.3f, 0.3f, 0.65f) : ImVec4(0.2f + row * 0.1f, 0.2f, 0.2f, 0.65f)); // Flat or Gradient?\n                    ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0 + row_bg_target, row_bg_color);\n                }\n\n                // Fill cells\n                for (int column = 0; column < 5; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"%c%c\", 'A' + row, '0' + column);\n\n                    // Change background of Cells B1->C2\n                    // Demonstrate setting a cell background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)'\n                    // (the CellBg color will be blended over the RowBg and ColumnBg colors)\n                    // We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop.\n                    if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1)\n                    {\n                        ImU32 cell_bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 0.65f));\n                        ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color);\n                    }\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Tree view\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Tree view\");\n        static ImGuiTableFlags table_flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;\n\n        static ImGuiTreeNodeFlags tree_node_flags_base = ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_DrawLinesFull;\n        ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanFullWidth\",  &tree_node_flags_base, ImGuiTreeNodeFlags_SpanFullWidth);\n        ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanLabelWidth\",  &tree_node_flags_base, ImGuiTreeNodeFlags_SpanLabelWidth);\n        ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanAllColumns\", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanAllColumns);\n        ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_LabelSpanAllColumns\", &tree_node_flags_base, ImGuiTreeNodeFlags_LabelSpanAllColumns);\n        ImGui::SameLine(); HelpMarker(\"Useful if you know that you aren't displaying contents in other columns\");\n\n        HelpMarker(\"See \\\"Columns flags\\\" section to configure how indentation is applied to individual columns.\");\n        if (ImGui::BeginTable(\"3ways\", 3, table_flags))\n        {\n            // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On\n            ImGui::TableSetupColumn(\"Name\", ImGuiTableColumnFlags_NoHide);\n            ImGui::TableSetupColumn(\"Size\", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);\n            ImGui::TableSetupColumn(\"Type\", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f);\n            ImGui::TableHeadersRow();\n\n            // Simple storage to output a dummy file-system.\n            struct MyTreeNode\n            {\n                const char*     Name;\n                const char*     Type;\n                int             Size;\n                int             ChildIdx;\n                int             ChildCount;\n                static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes)\n                {\n                    ImGui::TableNextRow();\n                    ImGui::TableNextColumn();\n                    const bool is_folder = (node->ChildCount > 0);\n\n                    ImGuiTreeNodeFlags node_flags = tree_node_flags_base;\n                    if (node != &all_nodes[0])\n                        node_flags &= ~ImGuiTreeNodeFlags_LabelSpanAllColumns; // Only demonstrate this on the root node.\n\n                    if (is_folder)\n                    {\n                        bool open = ImGui::TreeNodeEx(node->Name, node_flags);\n                        if ((node_flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) == 0)\n                        {\n                            ImGui::TableNextColumn();\n                            ImGui::TextDisabled(\"--\");\n                            ImGui::TableNextColumn();\n                            ImGui::TextUnformatted(node->Type);\n                        }\n                        if (open)\n                        {\n                            for (int child_n = 0; child_n < node->ChildCount; child_n++)\n                                DisplayNode(&all_nodes[node->ChildIdx + child_n], all_nodes);\n                            ImGui::TreePop();\n                        }\n                    }\n                    else\n                    {\n                        ImGui::TreeNodeEx(node->Name, node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen);\n                        ImGui::TableNextColumn();\n                        ImGui::Text(\"%d\", node->Size);\n                        ImGui::TableNextColumn();\n                        ImGui::TextUnformatted(node->Type);\n                    }\n                }\n            };\n            static const MyTreeNode nodes[] =\n            {\n                { \"Root with Long Name\",          \"Folder\",       -1,       1, 3    }, // 0\n                { \"Music\",                        \"Folder\",       -1,       4, 2    }, // 1\n                { \"Textures\",                     \"Folder\",       -1,       6, 3    }, // 2\n                { \"desktop.ini\",                  \"System file\",  1024,    -1,-1    }, // 3\n                { \"File1_a.wav\",                  \"Audio file\",   123000,  -1,-1    }, // 4\n                { \"File1_b.wav\",                  \"Audio file\",   456000,  -1,-1    }, // 5\n                { \"Image001.png\",                 \"Image file\",   203128,  -1,-1    }, // 6\n                { \"Copy of Image001.png\",         \"Image file\",   203256,  -1,-1    }, // 7\n                { \"Copy of Image001 (Final2).png\",\"Image file\",   203512,  -1,-1    }, // 8\n            };\n\n            MyTreeNode::DisplayNode(&nodes[0], nodes);\n\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Item width\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Item width\");\n        HelpMarker(\n            \"Showcase using PushItemWidth() and how it is preserved on a per-column basis.\\n\\n\"\n            \"Note that on auto-resizing non-resizable fixed columns, querying the content width for \"\n            \"e.g. right-alignment doesn't make sense.\");\n        if (ImGui::BeginTable(\"table_item_width\", 3, ImGuiTableFlags_Borders))\n        {\n            ImGui::TableSetupColumn(\"small\");\n            ImGui::TableSetupColumn(\"half\");\n            ImGui::TableSetupColumn(\"right-align\");\n            ImGui::TableHeadersRow();\n\n            for (int row = 0; row < 3; row++)\n            {\n                ImGui::TableNextRow();\n                if (row == 0)\n                {\n                    // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient)\n                    ImGui::TableSetColumnIndex(0);\n                    ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small\n                    ImGui::TableSetColumnIndex(1);\n                    ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);\n                    ImGui::TableSetColumnIndex(2);\n                    ImGui::PushItemWidth(-FLT_MIN); // Right-aligned\n                }\n\n                // Draw our contents\n                static float dummy_f = 0.0f;\n                ImGui::PushID(row);\n                ImGui::TableSetColumnIndex(0);\n                ImGui::SliderFloat(\"float0\", &dummy_f, 0.0f, 1.0f);\n                ImGui::TableSetColumnIndex(1);\n                ImGui::SliderFloat(\"float1\", &dummy_f, 0.0f, 1.0f);\n                ImGui::TableSetColumnIndex(2);\n                ImGui::SliderFloat(\"##float2\", &dummy_f, 0.0f, 1.0f); // No visible label since right-aligned\n                ImGui::PopID();\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    // Demonstrate using TableHeader() calls instead of TableHeadersRow()\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Custom headers\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Custom headers\");\n        const int COLUMNS_COUNT = 3;\n        if (ImGui::BeginTable(\"table_custom_headers\", COLUMNS_COUNT, ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))\n        {\n            ImGui::TableSetupColumn(\"Apricot\");\n            ImGui::TableSetupColumn(\"Banana\");\n            ImGui::TableSetupColumn(\"Cherry\");\n\n            // Dummy entire-column selection storage\n            // FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox.\n            static bool column_selected[3] = {};\n\n            // Instead of calling TableHeadersRow() we'll submit custom headers ourselves.\n            // (A different approach is also possible:\n            //    - Specify ImGuiTableColumnFlags_NoHeaderLabel in some TableSetupColumn() call.\n            //    - Call TableHeadersRow() normally. This will submit TableHeader() with no name.\n            //    - Then call TableSetColumnIndex() to position yourself in the column and submit your stuff e.g. Checkbox().)\n            ImGui::TableNextRow(ImGuiTableRowFlags_Headers);\n            for (int column = 0; column < COLUMNS_COUNT; column++)\n            {\n                ImGui::TableSetColumnIndex(column);\n                const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()\n                ImGui::PushID(column);\n                ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));\n                ImGui::Checkbox(\"##checkall\", &column_selected[column]);\n                ImGui::PopStyleVar();\n                ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);\n                ImGui::TableHeader(column_name);\n                ImGui::PopID();\n            }\n\n            // Submit table contents\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    char buf[32];\n                    sprintf(buf, \"Cell %d,%d\", column, row);\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Selectable(buf, column_selected[column]);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    // Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Angled headers\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Angled headers\");\n        const char* column_names[] = { \"Track\", \"cabasa\", \"ride\", \"smash\", \"tom-hi\", \"tom-mid\", \"tom-low\", \"hihat-o\", \"hihat-c\", \"snare-s\", \"snare-c\", \"clap\", \"rim\", \"kick\" };\n        const int columns_count = IM_COUNTOF(column_names);\n        const int rows_count = 12;\n\n        static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn;\n        static ImGuiTableColumnFlags column_flags = ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed;\n        static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage\n        static int frozen_cols = 1;\n        static int frozen_rows = 2;\n        ImGui::CheckboxFlags(\"_ScrollX\", &table_flags, ImGuiTableFlags_ScrollX);\n        ImGui::CheckboxFlags(\"_ScrollY\", &table_flags, ImGuiTableFlags_ScrollY);\n        ImGui::CheckboxFlags(\"_Resizable\", &table_flags, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"_Sortable\", &table_flags, ImGuiTableFlags_Sortable);\n        ImGui::CheckboxFlags(\"_NoBordersInBody\", &table_flags, ImGuiTableFlags_NoBordersInBody);\n        ImGui::CheckboxFlags(\"_HighlightHoveredColumn\", &table_flags, ImGuiTableFlags_HighlightHoveredColumn);\n        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n        ImGui::SliderInt(\"Frozen columns\", &frozen_cols, 0, 2);\n        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n        ImGui::SliderInt(\"Frozen rows\", &frozen_rows, 0, 2);\n        ImGui::CheckboxFlags(\"Disable header contributing to column width\", &column_flags, ImGuiTableColumnFlags_NoHeaderWidth);\n\n        if (ImGui::TreeNode(\"Style settings\"))\n        {\n            ImGui::SameLine();\n            HelpMarker(\"Giving access to some ImGuiStyle value in this demo for convenience.\");\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n            ImGui::SliderAngle(\"style.TableAngledHeadersAngle\", &ImGui::GetStyle().TableAngledHeadersAngle, -50.0f, +50.0f);\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n            ImGui::SliderFloat2(\"style.TableAngledHeadersTextAlign\", (float*)&ImGui::GetStyle().TableAngledHeadersTextAlign, 0.0f, 1.0f, \"%.2f\");\n            ImGui::TreePop();\n        }\n\n        if (ImGui::BeginTable(\"table_angled_headers\", columns_count, table_flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 12)))\n        {\n            ImGui::TableSetupColumn(column_names[0], ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder);\n            for (int n = 1; n < columns_count; n++)\n                ImGui::TableSetupColumn(column_names[n], column_flags);\n            ImGui::TableSetupScrollFreeze(frozen_cols, frozen_rows);\n\n            ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag.\n            ImGui::TableHeadersRow();       // Draw remaining headers and allow access to context-menu and other functions.\n            for (int row = 0; row < rows_count; row++)\n            {\n                ImGui::PushID(row);\n                ImGui::TableNextRow();\n                ImGui::TableSetColumnIndex(0);\n                ImGui::AlignTextToFramePadding();\n                ImGui::Text(\"Track %d\", row);\n                for (int column = 1; column < columns_count; column++)\n                    if (ImGui::TableSetColumnIndex(column))\n                    {\n                        ImGui::PushID(column);\n                        ImGui::Checkbox(\"\", &bools[row * columns_count + column]);\n                        ImGui::PopID();\n                    }\n                ImGui::PopID();\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    // Demonstrate creating custom context menus inside columns,\n    // while playing it nice with context menus provided by TableHeadersRow()/TableHeader()\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Context menus\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Context menus\");\n        HelpMarker(\n            \"By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\\n\"\n            \"Using ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body.\");\n        static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ContextMenuInBody\", &flags1, ImGuiTableFlags_ContextMenuInBody);\n        PopStyleCompact();\n\n        // Context Menus: first example\n        // [1.1] Right-click on the TableHeadersRow() line to open the default table context menu.\n        // [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set)\n        const int COLUMNS_COUNT = 3;\n        if (ImGui::BeginTable(\"table_context_menu\", COLUMNS_COUNT, flags1))\n        {\n            ImGui::TableSetupColumn(\"One\");\n            ImGui::TableSetupColumn(\"Two\");\n            ImGui::TableSetupColumn(\"Three\");\n\n            // [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu.\n            ImGui::TableHeadersRow();\n\n            // Submit dummy contents\n            for (int row = 0; row < 4; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < COLUMNS_COUNT; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Cell %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        // Context Menus: second example\n        // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.\n        // [2.2] Right-click on the \"..\" to open a custom popup\n        // [2.3] Right-click in columns to open another custom popup\n        HelpMarker(\n            \"Demonstrate mixing table context menu (over header), item context button (over button) \"\n            \"and custom per-column context menu (over column body).\");\n        ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders;\n        if (ImGui::BeginTable(\"table_context_menu_2\", COLUMNS_COUNT, flags2))\n        {\n            ImGui::TableSetupColumn(\"One\");\n            ImGui::TableSetupColumn(\"Two\");\n            ImGui::TableSetupColumn(\"Three\");\n\n            // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 4; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < COLUMNS_COUNT; column++)\n                {\n                    // Submit dummy contents\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Cell %d,%d\", column, row);\n                    ImGui::SameLine();\n\n                    // [2.2] Right-click on the \"..\" to open a custom popup\n                    ImGui::PushID(row * COLUMNS_COUNT + column);\n                    ImGui::SmallButton(\"..\");\n                    if (ImGui::BeginPopupContextItem())\n                    {\n                        ImGui::Text(\"This is the popup for Button(\\\"..\\\") in Cell %d,%d\", column, row);\n                        if (ImGui::Button(\"Close\"))\n                            ImGui::CloseCurrentPopup();\n                        ImGui::EndPopup();\n                    }\n                    ImGui::PopID();\n                }\n            }\n\n            // [2.3] Right-click anywhere in columns to open another custom popup\n            // (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup\n            // to manage popup priority as the popups triggers, here \"are we hovering a column\" are overlapping)\n            int hovered_column = -1;\n            for (int column = 0; column < COLUMNS_COUNT + 1; column++)\n            {\n                ImGui::PushID(column);\n                if (ImGui::TableGetColumnFlags(column) & ImGuiTableColumnFlags_IsHovered)\n                    hovered_column = column;\n                if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(1))\n                    ImGui::OpenPopup(\"MyPopup\");\n                if (ImGui::BeginPopup(\"MyPopup\"))\n                {\n                    if (column == COLUMNS_COUNT)\n                        ImGui::Text(\"This is a custom popup for unused space after the last column.\");\n                    else\n                        ImGui::Text(\"This is a custom popup for Column %d\", column);\n                    if (ImGui::Button(\"Close\"))\n                        ImGui::CloseCurrentPopup();\n                    ImGui::EndPopup();\n                }\n                ImGui::PopID();\n            }\n\n            ImGui::EndTable();\n            ImGui::Text(\"Hovered column: %d\", hovered_column);\n        }\n        ImGui::TreePop();\n    }\n\n    // Demonstrate creating multiple tables with the same ID\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Synced instances\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Synced instances\");\n        HelpMarker(\"Multiple tables with the same identifier will share their settings, width, visibility, order etc.\");\n\n        static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings;\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollY\", &flags, ImGuiTableFlags_ScrollY);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_SizingFixedFit\", &flags, ImGuiTableFlags_SizingFixedFit);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_HighlightHoveredColumn\", &flags, ImGuiTableFlags_HighlightHoveredColumn);\n        for (int n = 0; n < 3; n++)\n        {\n            char buf[32];\n            sprintf(buf, \"Synced Table %d\", n);\n            bool open = ImGui::CollapsingHeader(buf, ImGuiTreeNodeFlags_DefaultOpen);\n            if (open && ImGui::BeginTable(\"Table\", 3, flags, ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 5)))\n            {\n                ImGui::TableSetupColumn(\"One\");\n                ImGui::TableSetupColumn(\"Two\");\n                ImGui::TableSetupColumn(\"Three\");\n                ImGui::TableHeadersRow();\n                const int cell_count = (n == 1) ? 27 : 9; // Make second table have a scrollbar to verify that additional decoration is not affecting column positions.\n                for (int cell = 0; cell < cell_count; cell++)\n                {\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"this cell %d\", cell);\n                }\n                ImGui::EndTable();\n            }\n        }\n        ImGui::TreePop();\n    }\n\n    // Demonstrate using Sorting facilities\n    // This is a simplified version of the \"Advanced\" example, where we mostly focus on the code necessary to handle sorting.\n    // Note that the \"Advanced\" example also showcase manually triggering a sort (e.g. if item quantities have been modified)\n    static const char* template_items_names[] =\n    {\n        \"Banana\", \"Apple\", \"Cherry\", \"Watermelon\", \"Grapefruit\", \"Strawberry\", \"Mango\",\n        \"Kiwi\", \"Orange\", \"Pineapple\", \"Blueberry\", \"Plum\", \"Coconut\", \"Pear\", \"Apricot\"\n    };\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Sorting\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Sorting\");\n        // Create item list\n        static ImVector<MyItem> items;\n        if (items.Size == 0)\n        {\n            items.resize(50, MyItem());\n            for (int n = 0; n < items.Size; n++)\n            {\n                const int template_n = n % IM_COUNTOF(template_items_names);\n                MyItem& item = items[n];\n                item.ID = n;\n                item.Name = template_items_names[template_n];\n                item.Quantity = (n * n - n) % 20; // Assign default quantities\n            }\n        }\n\n        // Options\n        static ImGuiTableFlags flags =\n            ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti\n            | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody\n            | ImGuiTableFlags_ScrollY;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_SortMulti\", &flags, ImGuiTableFlags_SortMulti);\n        ImGui::SameLine(); HelpMarker(\"When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_SortTristate\", &flags, ImGuiTableFlags_SortTristate);\n        ImGui::SameLine(); HelpMarker(\"When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).\");\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table_sorting\", 4, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 15), 0.0f))\n        {\n            // Declare columns\n            // We use the \"user_id\" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.\n            // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!\n            // Demonstrate using a mixture of flags among available sort-related flags:\n            // - ImGuiTableColumnFlags_DefaultSort\n            // - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending / ImGuiTableColumnFlags_NoSortDescending\n            // - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending\n            ImGui::TableSetupColumn(\"ID\",       ImGuiTableColumnFlags_DefaultSort          | ImGuiTableColumnFlags_WidthFixed,   0.0f, MyItemColumnID_ID);\n            ImGui::TableSetupColumn(\"Name\",                                                  ImGuiTableColumnFlags_WidthFixed,   0.0f, MyItemColumnID_Name);\n            ImGui::TableSetupColumn(\"Action\",   ImGuiTableColumnFlags_NoSort               | ImGuiTableColumnFlags_WidthFixed,   0.0f, MyItemColumnID_Action);\n            ImGui::TableSetupColumn(\"Quantity\", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Quantity);\n            ImGui::TableSetupScrollFreeze(0, 1); // Make row always visible\n            ImGui::TableHeadersRow();\n\n            // Sort our data if sort specs have been changed!\n            if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())\n                if (sort_specs->SpecsDirty)\n                {\n                    MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size);\n                    sort_specs->SpecsDirty = false;\n                }\n\n            // Demonstrate using clipper for large vertical lists\n            ImGuiListClipper clipper;\n            clipper.Begin(items.Size);\n            while (clipper.Step())\n                for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)\n                {\n                    // Display a data item\n                    MyItem* item = &items[row_n];\n                    ImGui::PushID(item->ID);\n                    ImGui::TableNextRow();\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"%04d\", item->ID);\n                    ImGui::TableNextColumn();\n                    ImGui::TextUnformatted(item->Name);\n                    ImGui::TableNextColumn();\n                    ImGui::SmallButton(\"None\");\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"%d\", item->Quantity);\n                    ImGui::PopID();\n                }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    // In this example we'll expose most table flags and settings.\n    // For specific flags and settings refer to the corresponding section for more detailed explanation.\n    // This section is mostly useful to experiment with combining certain flags or settings with each others.\n    //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG]\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    if (ImGui::TreeNode(\"Advanced\"))\n    {\n        IMGUI_DEMO_MARKER(\"Tables/Advanced\");\n        static ImGuiTableFlags flags =\n            ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable\n            | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti\n            | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody\n            | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY\n            | ImGuiTableFlags_SizingFixedFit;\n        static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None;\n\n        enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow };\n        static int contents_type = CT_SelectableSpanRow;\n        const char* contents_type_names[] = { \"Text\", \"Button\", \"SmallButton\", \"FillButton\", \"Selectable\", \"Selectable (span row)\" };\n        static int freeze_cols = 1;\n        static int freeze_rows = 1;\n        static int items_count = IM_COUNTOF(template_items_names) * 2;\n        static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12);\n        static float row_min_height = 0.0f; // Auto\n        static float inner_width_with_scroll = 0.0f; // Auto-extend\n        static bool outer_size_enabled = true;\n        static bool show_headers = true;\n        static bool show_wrapped_text = false;\n        //static ImGuiTextFilter filter;\n        //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // FIXME-TABLE: Enabling this results in initial clipped first pass on table which tend to affect column sizing\n        if (ImGui::TreeNode(\"Options\"))\n        {\n            // Make the UI compact because there are so many fields\n            PushStyleCompact();\n            ImGui::PushItemWidth(TEXT_BASE_WIDTH * 28.0f);\n\n            if (ImGui::TreeNodeEx(\"Features:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags, ImGuiTableFlags_Resizable);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_Reorderable\", &flags, ImGuiTableFlags_Reorderable);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_Hideable\", &flags, ImGuiTableFlags_Hideable);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_Sortable\", &flags, ImGuiTableFlags_Sortable);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoSavedSettings\", &flags, ImGuiTableFlags_NoSavedSettings);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_ContextMenuInBody\", &flags, ImGuiTableFlags_ContextMenuInBody);\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Decorations:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_RowBg\", &flags, ImGuiTableFlags_RowBg);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersV\", &flags, ImGuiTableFlags_BordersV);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterV\", &flags, ImGuiTableFlags_BordersOuterV);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerV\", &flags, ImGuiTableFlags_BordersInnerV);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersH\", &flags, ImGuiTableFlags_BordersH);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterH\", &flags, ImGuiTableFlags_BordersOuterH);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerH\", &flags, ImGuiTableFlags_BordersInnerH);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBody\", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker(\"Disable vertical borders in columns Body (borders will always appear in Headers)\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBodyUntilResize\", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker(\"Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)\");\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Sizing:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                EditTableSizingFlags(&flags);\n                ImGui::SameLine(); HelpMarker(\"In the Advanced demo we override the policy of each column so those table-wide settings have less effect that typical.\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendX\", &flags, ImGuiTableFlags_NoHostExtendX);\n                ImGui::SameLine(); HelpMarker(\"Make outer width auto-fit to columns, overriding outer_size.x value.\\n\\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used.\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendY\", &flags, ImGuiTableFlags_NoHostExtendY);\n                ImGui::SameLine(); HelpMarker(\"Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\\n\\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoKeepColumnsVisible\", &flags, ImGuiTableFlags_NoKeepColumnsVisible);\n                ImGui::SameLine(); HelpMarker(\"Only available if ScrollX is disabled.\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_PreciseWidths\", &flags, ImGuiTableFlags_PreciseWidths);\n                ImGui::SameLine(); HelpMarker(\"Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoClip\", &flags, ImGuiTableFlags_NoClip);\n                ImGui::SameLine(); HelpMarker(\"Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with ScrollFreeze options.\");\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Padding:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_PadOuterX\", &flags, ImGuiTableFlags_PadOuterX);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoPadOuterX\", &flags, ImGuiTableFlags_NoPadOuterX);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoPadInnerX\", &flags, ImGuiTableFlags_NoPadInnerX);\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Scrolling:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollX\", &flags, ImGuiTableFlags_ScrollX);\n                ImGui::SameLine();\n                ImGui::SetNextItemWidth(ImGui::GetFrameHeight());\n                ImGui::DragInt(\"freeze_cols\", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollY\", &flags, ImGuiTableFlags_ScrollY);\n                ImGui::SameLine();\n                ImGui::SetNextItemWidth(ImGui::GetFrameHeight());\n                ImGui::DragInt(\"freeze_rows\", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Sorting:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_SortMulti\", &flags, ImGuiTableFlags_SortMulti);\n                ImGui::SameLine(); HelpMarker(\"When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_SortTristate\", &flags, ImGuiTableFlags_SortTristate);\n                ImGui::SameLine(); HelpMarker(\"When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).\");\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Headers:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::Checkbox(\"show_headers\", &show_headers);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_HighlightHoveredColumn\", &flags, ImGuiTableFlags_HighlightHoveredColumn);\n                ImGui::CheckboxFlags(\"ImGuiTableColumnFlags_AngledHeader\", &columns_base_flags, ImGuiTableColumnFlags_AngledHeader);\n                ImGui::SameLine(); HelpMarker(\"Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \\\"Angled headers\\\" section of the demo).\");\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Other:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::Checkbox(\"show_wrapped_text\", &show_wrapped_text);\n\n                ImGui::DragFloat2(\"##OuterSize\", &outer_size_value.x);\n                ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);\n                ImGui::Checkbox(\"outer_size\", &outer_size_enabled);\n                ImGui::SameLine();\n                HelpMarker(\"If scrolling is disabled (ScrollX and ScrollY not set):\\n\"\n                    \"- The table is output directly in the parent window.\\n\"\n                    \"- OuterSize.x < 0.0f will right-align the table.\\n\"\n                    \"- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch columns.\\n\"\n                    \"- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set).\");\n\n                // From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling.\n                // To facilitate toying with this demo we will actually pass 0.0f to the BeginTable() when ScrollX is disabled.\n                ImGui::DragFloat(\"inner_width (when ScrollX active)\", &inner_width_with_scroll, 1.0f, 0.0f, FLT_MAX);\n\n                ImGui::DragFloat(\"row_min_height\", &row_min_height, 1.0f, 0.0f, FLT_MAX);\n                ImGui::SameLine(); HelpMarker(\"Specify height of the Selectable item.\");\n\n                ImGui::DragInt(\"items_count\", &items_count, 0.1f, 0, 9999);\n                ImGui::Combo(\"items_type (first column)\", &contents_type, contents_type_names, IM_COUNTOF(contents_type_names));\n                //filter.Draw(\"filter\");\n                ImGui::TreePop();\n            }\n\n            ImGui::PopItemWidth();\n            PopStyleCompact();\n            ImGui::Spacing();\n            ImGui::TreePop();\n        }\n\n        // Update item list if we changed the number of items\n        static ImVector<MyItem> items;\n        static ImVector<int> selection;\n        static bool items_need_sort = false;\n        if (items.Size != items_count)\n        {\n            items.resize(items_count, MyItem());\n            for (int n = 0; n < items_count; n++)\n            {\n                const int template_n = n % IM_COUNTOF(template_items_names);\n                MyItem& item = items[n];\n                item.ID = n;\n                item.Name = template_items_names[template_n];\n                item.Quantity = (template_n == 3) ? 10 : (template_n == 4) ? 20 : 0; // Assign default quantities\n            }\n        }\n\n        const ImDrawList* parent_draw_list = ImGui::GetWindowDrawList();\n        const int parent_draw_list_draw_cmd_count = parent_draw_list->CmdBuffer.Size;\n        ImVec2 table_scroll_cur, table_scroll_max; // For debug display\n        const ImDrawList* table_draw_list = NULL;  // \"\n\n        // Submit table\n        const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f;\n        if (ImGui::BeginTable(\"table_advanced\", 6, flags, outer_size_enabled ? outer_size_value : ImVec2(0, 0), inner_width_to_use))\n        {\n            // Declare columns\n            // We use the \"user_id\" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.\n            // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!\n            ImGui::TableSetupColumn(\"ID\",           columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID);\n            ImGui::TableSetupColumn(\"Name\",         columns_base_flags | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name);\n            ImGui::TableSetupColumn(\"Action\",       columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action);\n            ImGui::TableSetupColumn(\"Quantity\",     columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity);\n            ImGui::TableSetupColumn(\"Description\",  columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), 0.0f, MyItemColumnID_Description);\n            ImGui::TableSetupColumn(\"Hidden\",       columns_base_flags |  ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort);\n            ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);\n\n            // Sort our data if sort specs have been changed!\n            ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs();\n            if (sort_specs && sort_specs->SpecsDirty)\n                items_need_sort = true;\n            if (sort_specs && items_need_sort && items.Size > 1)\n            {\n                MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size);\n                sort_specs->SpecsDirty = false;\n            }\n            items_need_sort = false;\n\n            // Take note of whether we are currently sorting based on the Quantity field,\n            // we will use this to trigger sorting when we know the data of this column has been modified.\n            const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0;\n\n            // Show headers\n            if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0)\n                ImGui::TableAngledHeadersRow();\n            if (show_headers)\n                ImGui::TableHeadersRow();\n\n            // Show data\n            // FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here?\n#if 1\n            // Demonstrate using clipper for large vertical lists\n            ImGuiListClipper clipper;\n            clipper.Begin(items.Size);\n            while (clipper.Step())\n            {\n                for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)\n#else\n            // Without clipper\n            {\n                for (int row_n = 0; row_n < items.Size; row_n++)\n#endif\n                {\n                    MyItem* item = &items[row_n];\n                    //if (!filter.PassFilter(item->Name))\n                    //    continue;\n\n                    const bool item_is_selected = selection.contains(item->ID);\n                    ImGui::PushID(item->ID);\n                    ImGui::TableNextRow(ImGuiTableRowFlags_None, row_min_height);\n\n                    // For the demo purpose we can select among different type of items submitted in the first column\n                    ImGui::TableSetColumnIndex(0);\n                    char label[32];\n                    sprintf(label, \"%04d\", item->ID);\n                    if (contents_type == CT_Text)\n                        ImGui::TextUnformatted(label);\n                    else if (contents_type == CT_Button)\n                        ImGui::Button(label);\n                    else if (contents_type == CT_SmallButton)\n                        ImGui::SmallButton(label);\n                    else if (contents_type == CT_FillButton)\n                        ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f));\n                    else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow)\n                    {\n                        ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap : ImGuiSelectableFlags_None;\n                        if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height)))\n                        {\n                            if (ImGui::GetIO().KeyCtrl)\n                            {\n                                if (item_is_selected)\n                                    selection.find_erase_unsorted(item->ID);\n                                else\n                                    selection.push_back(item->ID);\n                            }\n                            else\n                            {\n                                selection.clear();\n                                selection.push_back(item->ID);\n                            }\n                        }\n                    }\n\n                    if (ImGui::TableSetColumnIndex(1))\n                        ImGui::TextUnformatted(item->Name);\n\n                    // Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity,\n                    // and we are currently sorting on the column showing the Quantity.\n                    // To avoid triggering a sort while holding the button, we only trigger it when the button has been released.\n                    // You will probably need some extra logic if you want to automatically sort when a specific entry changes.\n                    if (ImGui::TableSetColumnIndex(2))\n                    {\n                        if (ImGui::SmallButton(\"Chop\")) { item->Quantity += 1; }\n                        if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }\n                        ImGui::SameLine();\n                        if (ImGui::SmallButton(\"Eat\")) { item->Quantity -= 1; }\n                        if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }\n                    }\n\n                    if (ImGui::TableSetColumnIndex(3))\n                        ImGui::Text(\"%d\", item->Quantity);\n\n                    ImGui::TableSetColumnIndex(4);\n                    if (show_wrapped_text)\n                        ImGui::TextWrapped(\"Lorem ipsum dolor sit amet\");\n                    else\n                        ImGui::Text(\"Lorem ipsum dolor sit amet\");\n\n                    if (ImGui::TableSetColumnIndex(5))\n                        ImGui::Text(\"1234\");\n\n                    ImGui::PopID();\n                }\n            }\n\n            // Store some info to display debug details below\n            table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY());\n            table_scroll_max = ImVec2(ImGui::GetScrollMaxX(), ImGui::GetScrollMaxY());\n            table_draw_list = ImGui::GetWindowDrawList();\n            ImGui::EndTable();\n        }\n        static bool show_debug_details = false;\n        ImGui::Checkbox(\"Debug details\", &show_debug_details);\n        if (show_debug_details && table_draw_list)\n        {\n            ImGui::SameLine(0.0f, 0.0f);\n            const int table_draw_list_draw_cmd_count = table_draw_list->CmdBuffer.Size;\n            if (table_draw_list == parent_draw_list)\n                ImGui::Text(\": DrawCmd: +%d (in same window)\",\n                    table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count);\n            else\n                ImGui::Text(\": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)\",\n                    table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x, table_scroll_cur.y, table_scroll_max.y);\n        }\n        ImGui::TreePop();\n    }\n\n    ImGui::PopID();\n\n    DemoWindowColumns();\n\n    if (disable_indent)\n        ImGui::PopStyleVar();\n}\n\n// Demonstrate old/legacy Columns API!\n// [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!]\nstatic void DemoWindowColumns()\n{\n    bool open = ImGui::TreeNode(\"Legacy Columns API\");\n    ImGui::SameLine();\n    HelpMarker(\"Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!\");\n    if (!open)\n        return;\n\n    // Basic columns\n    if (ImGui::TreeNode(\"Basic\"))\n    {\n        IMGUI_DEMO_MARKER(\"Columns (legacy API)/Basic\");\n        ImGui::Text(\"Without border:\");\n        ImGui::Columns(3, \"mycolumns3\", false);  // 3-ways, no border\n        ImGui::Separator();\n        for (int n = 0; n < 14; n++)\n        {\n            char label[32];\n            sprintf(label, \"Item %d\", n);\n            if (ImGui::Selectable(label)) {}\n            //if (ImGui::Button(label, ImVec2(-FLT_MIN,0.0f))) {}\n            ImGui::NextColumn();\n        }\n        ImGui::Columns(1);\n        ImGui::Separator();\n\n        ImGui::Text(\"With border:\");\n        ImGui::Columns(4, \"mycolumns\"); // 4-ways, with border\n        ImGui::Separator();\n        ImGui::Text(\"ID\"); ImGui::NextColumn();\n        ImGui::Text(\"Name\"); ImGui::NextColumn();\n        ImGui::Text(\"Path\"); ImGui::NextColumn();\n        ImGui::Text(\"Hovered\"); ImGui::NextColumn();\n        ImGui::Separator();\n        const char* names[3] = { \"One\", \"Two\", \"Three\" };\n        const char* paths[3] = { \"/path/one\", \"/path/two\", \"/path/three\" };\n        static int selected = -1;\n        for (int i = 0; i < 3; i++)\n        {\n            char label[32];\n            sprintf(label, \"%04d\", i);\n            if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns))\n                selected = i;\n            bool hovered = ImGui::IsItemHovered();\n            ImGui::NextColumn();\n            ImGui::Text(names[i]); ImGui::NextColumn();\n            ImGui::Text(paths[i]); ImGui::NextColumn();\n            ImGui::Text(\"%d\", hovered); ImGui::NextColumn();\n        }\n        ImGui::Columns(1);\n        ImGui::Separator();\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Borders\"))\n    {\n        IMGUI_DEMO_MARKER(\"Columns (legacy API)/Borders\");\n        // NB: Future columns API should allow automatic horizontal borders.\n        static bool h_borders = true;\n        static bool v_borders = true;\n        static int columns_count = 4;\n        const int lines_count = 3;\n        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n        ImGui::DragInt(\"##columns_count\", &columns_count, 0.1f, 2, 10, \"%d columns\");\n        if (columns_count < 2)\n            columns_count = 2;\n        ImGui::SameLine();\n        ImGui::Checkbox(\"horizontal\", &h_borders);\n        ImGui::SameLine();\n        ImGui::Checkbox(\"vertical\", &v_borders);\n        ImGui::Columns(columns_count, NULL, v_borders);\n        for (int i = 0; i < columns_count * lines_count; i++)\n        {\n            if (h_borders && ImGui::GetColumnIndex() == 0)\n                ImGui::Separator();\n            ImGui::PushID(i);\n            ImGui::Text(\"%c%c%c\", 'a' + i, 'a' + i, 'a' + i);\n            ImGui::Text(\"Width %.2f\", ImGui::GetColumnWidth());\n            ImGui::Text(\"Avail %.2f\", ImGui::GetContentRegionAvail().x);\n            ImGui::Text(\"Offset %.2f\", ImGui::GetColumnOffset());\n            ImGui::Text(\"Long text that is likely to clip\");\n            ImGui::Button(\"Button\", ImVec2(-FLT_MIN, 0.0f));\n            ImGui::PopID();\n            ImGui::NextColumn();\n        }\n        ImGui::Columns(1);\n        if (h_borders)\n            ImGui::Separator();\n        ImGui::TreePop();\n    }\n\n    // Create multiple items in a same cell before switching to next column\n    if (ImGui::TreeNode(\"Mixed items\"))\n    {\n        IMGUI_DEMO_MARKER(\"Columns (legacy API)/Mixed items\");\n        ImGui::Columns(3, \"mixed\");\n        ImGui::Separator();\n\n        ImGui::Text(\"Hello\");\n        ImGui::Button(\"Banana\");\n        ImGui::NextColumn();\n\n        ImGui::Text(\"ImGui\");\n        ImGui::Button(\"Apple\");\n        static float foo = 1.0f;\n        ImGui::InputFloat(\"red\", &foo, 0.05f, 0, \"%.3f\");\n        ImGui::Text(\"An extra line here.\");\n        ImGui::NextColumn();\n\n        ImGui::Text(\"Sailor\");\n        ImGui::Button(\"Corniflower\");\n        static float bar = 1.0f;\n        ImGui::InputFloat(\"blue\", &bar, 0.05f, 0, \"%.3f\");\n        ImGui::NextColumn();\n\n        if (ImGui::CollapsingHeader(\"Category A\")) { ImGui::Text(\"Blah blah blah\"); } ImGui::NextColumn();\n        if (ImGui::CollapsingHeader(\"Category B\")) { ImGui::Text(\"Blah blah blah\"); } ImGui::NextColumn();\n        if (ImGui::CollapsingHeader(\"Category C\")) { ImGui::Text(\"Blah blah blah\"); } ImGui::NextColumn();\n        ImGui::Columns(1);\n        ImGui::Separator();\n        ImGui::TreePop();\n    }\n\n    // Word wrapping\n    if (ImGui::TreeNode(\"Word-wrapping\"))\n    {\n        IMGUI_DEMO_MARKER(\"Columns (legacy API)/Word-wrapping\");\n        ImGui::Columns(2, \"word-wrapping\");\n        ImGui::Separator();\n        ImGui::TextWrapped(\"The quick brown fox jumps over the lazy dog.\");\n        ImGui::TextWrapped(\"Hello Left\");\n        ImGui::NextColumn();\n        ImGui::TextWrapped(\"The quick brown fox jumps over the lazy dog.\");\n        ImGui::TextWrapped(\"Hello Right\");\n        ImGui::Columns(1);\n        ImGui::Separator();\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Horizontal Scrolling\"))\n    {\n        IMGUI_DEMO_MARKER(\"Columns (legacy API)/Horizontal Scrolling\");\n        ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f));\n        ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f);\n        ImGui::BeginChild(\"##ScrollingRegion\", child_size, ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar);\n        ImGui::Columns(10);\n\n        // Also demonstrate using clipper for large vertical lists\n        int ITEMS_COUNT = 2000;\n        ImGuiListClipper clipper;\n        clipper.Begin(ITEMS_COUNT);\n        while (clipper.Step())\n        {\n            for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)\n                for (int j = 0; j < 10; j++)\n                {\n                    ImGui::Text(\"Line %d Column %d...\", i, j);\n                    ImGui::NextColumn();\n                }\n        }\n        ImGui::Columns(1);\n        ImGui::EndChild();\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Tree\"))\n    {\n        IMGUI_DEMO_MARKER(\"Columns (legacy API)/Tree\");\n        ImGui::Columns(2, \"tree\", true);\n        for (int x = 0; x < 3; x++)\n        {\n            bool open1 = ImGui::TreeNode((void*)(intptr_t)x, \"Node%d\", x);\n            ImGui::NextColumn();\n            ImGui::Text(\"Node contents\");\n            ImGui::NextColumn();\n            if (open1)\n            {\n                for (int y = 0; y < 3; y++)\n                {\n                    bool open2 = ImGui::TreeNode((void*)(intptr_t)y, \"Node%d.%d\", x, y);\n                    ImGui::NextColumn();\n                    ImGui::Text(\"Node contents\");\n                    if (open2)\n                    {\n                        ImGui::Text(\"Even more contents\");\n                        if (ImGui::TreeNode(\"Tree in column\"))\n                        {\n                            ImGui::Text(\"The quick brown fox jumps over the lazy dog\");\n                            ImGui::TreePop();\n                        }\n                    }\n                    ImGui::NextColumn();\n                    if (open2)\n                        ImGui::TreePop();\n                }\n                ImGui::TreePop();\n            }\n        }\n        ImGui::Columns(1);\n        ImGui::TreePop();\n    }\n\n    ImGui::TreePop();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DemoWindowInputs()\n//-----------------------------------------------------------------------------\n\nstatic void DemoWindowInputs()\n{\n    if (ImGui::CollapsingHeader(\"Inputs & Focus\"))\n    {\n        ImGuiIO& io = ImGui::GetIO();\n\n        // Display inputs submitted to ImGuiIO\n        ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n        bool inputs_opened = ImGui::TreeNode(\"Inputs\");\n        ImGui::SameLine();\n        HelpMarker(\n            \"This is a simplified view. See more detailed input state:\\n\"\n            \"- in 'Tools->Metrics/Debugger->Inputs'.\\n\"\n            \"- in 'Tools->Debug Log->IO'.\");\n        if (inputs_opened)\n        {\n            IMGUI_DEMO_MARKER(\"Inputs & Focus/Inputs\");\n            if (ImGui::IsMousePosValid())\n                ImGui::Text(\"Mouse pos: (%g, %g)\", io.MousePos.x, io.MousePos.y);\n            else\n                ImGui::Text(\"Mouse pos: <INVALID>\");\n            ImGui::Text(\"Mouse delta: (%g, %g)\", io.MouseDelta.x, io.MouseDelta.y);\n            ImGui::Text(\"Mouse down:\");\n            for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text(\"b%d (%.02f secs)\", i, io.MouseDownDuration[i]); }\n            ImGui::Text(\"Mouse wheel: %.1f\", io.MouseWheel);\n            ImGui::Text(\"Mouse clicked count:\");\n            for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text(\"b%d: %d\", i, io.MouseClickedCount[i]); }\n\n            // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows\n            // displaying the data for old/new backends.\n            // User code should never have to go through such hoops!\n            // You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.\n            struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };\n            ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN;\n            ImGui::Text(\"Keys down:\");         for (ImGuiKey key = start_key; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !ImGui::IsKeyDown(key)) continue; ImGui::SameLine(); ImGui::Text((key < ImGuiKey_NamedKey_BEGIN) ? \"\\\"%s\\\"\" : \"\\\"%s\\\" %d\", ImGui::GetKeyName(key), key); }\n            ImGui::Text(\"Keys mods: %s%s%s%s\", io.KeyCtrl ? \"CTRL \" : \"\", io.KeyShift ? \"SHIFT \" : \"\", io.KeyAlt ? \"ALT \" : \"\", io.KeySuper ? \"SUPER \" : \"\");\n            ImGui::Text(\"Chars queue:\");       for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine();  ImGui::Text(\"\\'%c\\' (0x%04X)\", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.\n\n            ImGui::TreePop();\n        }\n\n        // Display ImGuiIO output flags\n        ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n        bool outputs_opened = ImGui::TreeNode(\"Outputs\");\n        ImGui::SameLine();\n        HelpMarker(\n            \"The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui \"\n            \"to instruct your application of how to route inputs. Typically, when a value is true, it means \"\n            \"Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\\n\\n\"\n            \"The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, \"\n            \"and underlying application should ignore mouse inputs (in practice there are many and more subtle \"\n            \"rules leading to how those flags are set).\");\n        if (outputs_opened)\n        {\n            IMGUI_DEMO_MARKER(\"Inputs & Focus/Outputs\");\n            ImGui::Text(\"io.WantCaptureMouse: %d\", io.WantCaptureMouse);\n            ImGui::Text(\"io.WantCaptureMouseUnlessPopupClose: %d\", io.WantCaptureMouseUnlessPopupClose);\n            ImGui::Text(\"io.WantCaptureKeyboard: %d\", io.WantCaptureKeyboard);\n            ImGui::Text(\"io.WantTextInput: %d\", io.WantTextInput);\n            ImGui::Text(\"io.WantSetMousePos: %d\", io.WantSetMousePos);\n            ImGui::Text(\"io.NavActive: %d, io.NavVisible: %d\", io.NavActive, io.NavVisible);\n\n            IMGUI_DEMO_MARKER(\"Inputs & Focus/Outputs/WantCapture override\");\n            if (ImGui::TreeNode(\"WantCapture override\"))\n            {\n                HelpMarker(\n                    \"Hovering the colored canvas will override io.WantCaptureXXX fields.\\n\"\n                    \"Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering \"\n                    \"and true when clicking.\");\n                static int capture_override_mouse = -1;\n                static int capture_override_keyboard = -1;\n                const char* capture_override_desc[] = { \"None\", \"Set to false\", \"Set to true\" };\n                ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);\n                ImGui::SliderInt(\"SetNextFrameWantCaptureMouse() on hover\", &capture_override_mouse, -1, +1, capture_override_desc[capture_override_mouse + 1], ImGuiSliderFlags_AlwaysClamp);\n                ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);\n                ImGui::SliderInt(\"SetNextFrameWantCaptureKeyboard() on hover\", &capture_override_keyboard, -1, +1, capture_override_desc[capture_override_keyboard + 1], ImGuiSliderFlags_AlwaysClamp);\n\n                ImGui::ColorButton(\"##panel\", ImVec4(0.7f, 0.1f, 0.7f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, ImVec2(128.0f, 96.0f)); // Dummy item\n                if (ImGui::IsItemHovered() && capture_override_mouse != -1)\n                    ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1);\n                if (ImGui::IsItemHovered() && capture_override_keyboard != -1)\n                    ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1);\n\n                ImGui::TreePop();\n            }\n            ImGui::TreePop();\n        }\n\n        // Demonstrate using Shortcut() and Routing Policies.\n        // The general flow is:\n        // - Code interested in a chord (e.g. \"Ctrl+A\") declares their intent.\n        // - Multiple locations may be interested in same chord! Routing helps find a winner.\n        // - Every frame, we resolve all claims and assign one owner if the modifiers are matching.\n        // - The lower-level function is 'bool SetShortcutRouting()', returns true when caller got the route.\n        // - Most of the times, SetShortcutRouting() is not called directly. User mostly calls Shortcut() with routing flags.\n        // - If you call Shortcut() WITHOUT any routing option, it uses ImGuiInputFlags_RouteFocused.\n        // TL;DR: Most uses will simply be:\n        // - Shortcut(ImGuiMod_Ctrl | ImGuiKey_A); // Use ImGuiInputFlags_RouteFocused policy.\n        if (ImGui::TreeNode(\"Shortcuts\"))\n        {\n            IMGUI_DEMO_MARKER(\"Inputs & Focus/Shortcuts\");\n            static ImGuiInputFlags route_options = ImGuiInputFlags_Repeat;\n            static ImGuiInputFlags route_type = ImGuiInputFlags_RouteFocused;\n            ImGui::CheckboxFlags(\"ImGuiInputFlags_Repeat\", &route_options, ImGuiInputFlags_Repeat);\n            ImGui::RadioButton(\"ImGuiInputFlags_RouteActive\", &route_type, ImGuiInputFlags_RouteActive);\n            ImGui::RadioButton(\"ImGuiInputFlags_RouteFocused (default)\", &route_type, ImGuiInputFlags_RouteFocused);\n            ImGui::Indent();\n            ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteFocused);\n            ImGui::CheckboxFlags(\"ImGuiInputFlags_RouteOverActive##0\", &route_options, ImGuiInputFlags_RouteOverActive);\n            ImGui::EndDisabled();\n            ImGui::Unindent();\n            ImGui::RadioButton(\"ImGuiInputFlags_RouteGlobal\", &route_type, ImGuiInputFlags_RouteGlobal);\n            ImGui::Indent();\n            ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteGlobal);\n            ImGui::CheckboxFlags(\"ImGuiInputFlags_RouteOverFocused\", &route_options, ImGuiInputFlags_RouteOverFocused);\n            ImGui::CheckboxFlags(\"ImGuiInputFlags_RouteOverActive\", &route_options, ImGuiInputFlags_RouteOverActive);\n            ImGui::CheckboxFlags(\"ImGuiInputFlags_RouteUnlessBgFocused\", &route_options, ImGuiInputFlags_RouteUnlessBgFocused);\n            ImGui::EndDisabled();\n            ImGui::Unindent();\n            ImGui::RadioButton(\"ImGuiInputFlags_RouteAlways\", &route_type, ImGuiInputFlags_RouteAlways);\n            ImGuiInputFlags flags = route_type | route_options; // Merged flags\n            if (route_type != ImGuiInputFlags_RouteGlobal)\n                flags &= ~(ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused);\n\n            ImGui::SeparatorText(\"Using SetNextItemShortcut()\");\n            ImGui::Text(\"Ctrl+S\");\n            ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, flags | ImGuiInputFlags_Tooltip);\n            ImGui::Button(\"Save\");\n            ImGui::Text(\"Alt+F\");\n            ImGui::SetNextItemShortcut(ImGuiMod_Alt | ImGuiKey_F, flags | ImGuiInputFlags_Tooltip);\n            static float f = 0.5f;\n            ImGui::SliderFloat(\"Factor\", &f, 0.0f, 1.0f);\n\n            ImGui::SeparatorText(\"Using Shortcut()\");\n            const float line_height = ImGui::GetTextLineHeightWithSpacing();\n            const ImGuiKeyChord key_chord = ImGuiMod_Ctrl | ImGuiKey_A;\n\n            ImGui::Text(\"Ctrl+A\");\n            ImGui::Text(\"IsWindowFocused: %d, Shortcut: %s\", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? \"PRESSED\" : \"...\");\n\n            ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 0.0f, 1.0f, 0.1f));\n\n            ImGui::BeginChild(\"WindowA\", ImVec2(-FLT_MIN, line_height * 14), true);\n            ImGui::Text(\"Press Ctrl+A and see who receives it!\");\n            ImGui::Separator();\n\n            // 1: Window polling for Ctrl+A\n            ImGui::Text(\"(in WindowA)\");\n            ImGui::Text(\"IsWindowFocused: %d, Shortcut: %s\", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? \"PRESSED\" : \"...\");\n\n            // 2: InputText also polling for Ctrl+A: it always uses _RouteFocused internally (gets priority when active)\n            // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)\n            //char str[16] = \"Press Ctrl+A\";\n            //ImGui::Spacing();\n            //ImGui::InputText(\"InputTextB\", str, IM_COUNTOF(str), ImGuiInputTextFlags_ReadOnly);\n            //ImGuiID item_id = ImGui::GetItemID();\n            //ImGui::SameLine(); HelpMarker(\"Internal widgets always use _RouteFocused\");\n            //ImGui::Text(\"IsWindowFocused: %d, Shortcut: %s\", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, item_id) ? \"PRESSED\" : \"...\");\n\n            // 3: Dummy child is not claiming the route: focusing them shouldn't steal route away from WindowA\n            ImGui::BeginChild(\"ChildD\", ImVec2(-FLT_MIN, line_height * 4), true);\n            ImGui::Text(\"(in ChildD: not using same Shortcut)\");\n            ImGui::Text(\"IsWindowFocused: %d\", ImGui::IsWindowFocused());\n            ImGui::EndChild();\n\n            // 4: Child window polling for Ctrl+A. It is deeper than WindowA and gets priority when focused.\n            ImGui::BeginChild(\"ChildE\", ImVec2(-FLT_MIN, line_height * 4), true);\n            ImGui::Text(\"(in ChildE: using same Shortcut)\");\n            ImGui::Text(\"IsWindowFocused: %d, Shortcut: %s\", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? \"PRESSED\" : \"...\");\n            ImGui::EndChild();\n\n            // 5: In a popup\n            if (ImGui::Button(\"Open Popup\"))\n                ImGui::OpenPopup(\"PopupF\");\n            if (ImGui::BeginPopup(\"PopupF\"))\n            {\n                ImGui::Text(\"(in PopupF)\");\n                ImGui::Text(\"IsWindowFocused: %d, Shortcut: %s\", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? \"PRESSED\" : \"...\");\n                // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)\n                //ImGui::InputText(\"InputTextG\", str, IM_COUNTOF(str), ImGuiInputTextFlags_ReadOnly);\n                //ImGui::Text(\"IsWindowFocused: %d, Shortcut: %s\", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? \"PRESSED\" : \"...\");\n                ImGui::EndPopup();\n            }\n            ImGui::EndChild();\n            ImGui::PopStyleColor();\n\n            ImGui::TreePop();\n        }\n\n        // Display mouse cursors\n        if (ImGui::TreeNode(\"Mouse Cursors\"))\n        {\n            IMGUI_DEMO_MARKER(\"Inputs & Focus/Mouse Cursors\");\n            const char* mouse_cursors_names[] = { \"Arrow\", \"TextInput\", \"ResizeAll\", \"ResizeNS\", \"ResizeEW\", \"ResizeNESW\", \"ResizeNWSE\", \"Hand\", \"Wait\", \"Progress\", \"NotAllowed\" };\n            IM_ASSERT(IM_COUNTOF(mouse_cursors_names) == ImGuiMouseCursor_COUNT);\n\n            ImGuiMouseCursor current = ImGui::GetMouseCursor();\n            const char* cursor_name = (current >= ImGuiMouseCursor_Arrow) && (current < ImGuiMouseCursor_COUNT) ? mouse_cursors_names[current] : \"N/A\";\n            ImGui::Text(\"Current mouse cursor = %d: %s\", current, cursor_name);\n            ImGui::BeginDisabled(true);\n            ImGui::CheckboxFlags(\"io.BackendFlags: HasMouseCursors\", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);\n            ImGui::EndDisabled();\n\n            ImGui::Text(\"Hover to see mouse cursors:\");\n            ImGui::SameLine(); HelpMarker(\n                \"Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. \"\n                \"If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, \"\n                \"otherwise your backend needs to handle it.\");\n            for (int i = 0; i < ImGuiMouseCursor_COUNT; i++)\n            {\n                char label[32];\n                sprintf(label, \"Mouse cursor %d: %s\", i, mouse_cursors_names[i]);\n                ImGui::Bullet(); ImGui::Selectable(label, false);\n                if (ImGui::IsItemHovered())\n                    ImGui::SetMouseCursor(i);\n            }\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Tabbing\"))\n        {\n            IMGUI_DEMO_MARKER(\"Inputs & Focus/Tabbing\");\n            ImGui::Text(\"Use Tab/Shift+Tab to cycle through keyboard editable fields.\");\n            static char buf[32] = \"hello\";\n            ImGui::InputText(\"1\", buf, IM_COUNTOF(buf));\n            ImGui::InputText(\"2\", buf, IM_COUNTOF(buf));\n            ImGui::InputText(\"3\", buf, IM_COUNTOF(buf));\n            ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true);\n            ImGui::InputText(\"4 (tab skip)\", buf, IM_COUNTOF(buf));\n            ImGui::SameLine(); HelpMarker(\"Item won't be cycled through when using TAB or Shift+Tab.\");\n            ImGui::PopItemFlag();\n            ImGui::InputText(\"5\", buf, IM_COUNTOF(buf));\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Focus from code\"))\n        {\n            IMGUI_DEMO_MARKER(\"Inputs & Focus/Focus from code\");\n            bool focus_1 = ImGui::Button(\"Focus on 1\"); ImGui::SameLine();\n            bool focus_2 = ImGui::Button(\"Focus on 2\"); ImGui::SameLine();\n            bool focus_3 = ImGui::Button(\"Focus on 3\");\n            int has_focus = 0;\n            static char buf[128] = \"click on a button to set focus\";\n\n            if (focus_1) ImGui::SetKeyboardFocusHere();\n            ImGui::InputText(\"1\", buf, IM_COUNTOF(buf));\n            if (ImGui::IsItemActive()) has_focus = 1;\n\n            if (focus_2) ImGui::SetKeyboardFocusHere();\n            ImGui::InputText(\"2\", buf, IM_COUNTOF(buf));\n            if (ImGui::IsItemActive()) has_focus = 2;\n\n            ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true);\n            if (focus_3) ImGui::SetKeyboardFocusHere();\n            ImGui::InputText(\"3 (tab skip)\", buf, IM_COUNTOF(buf));\n            if (ImGui::IsItemActive()) has_focus = 3;\n            ImGui::SameLine(); HelpMarker(\"Item won't be cycled through when using TAB or Shift+Tab.\");\n            ImGui::PopItemFlag();\n\n            if (has_focus)\n                ImGui::Text(\"Item with focus: %d\", has_focus);\n            else\n                ImGui::Text(\"Item with focus: <none>\");\n\n            // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item\n            static float f3[3] = { 0.0f, 0.0f, 0.0f };\n            int focus_ahead = -1;\n            if (ImGui::Button(\"Focus on X\")) { focus_ahead = 0; } ImGui::SameLine();\n            if (ImGui::Button(\"Focus on Y\")) { focus_ahead = 1; } ImGui::SameLine();\n            if (ImGui::Button(\"Focus on Z\")) { focus_ahead = 2; }\n            if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead);\n            ImGui::SliderFloat3(\"Float3\", &f3[0], 0.0f, 1.0f);\n\n            ImGui::TextWrapped(\"NB: Cursor & selection are preserved when refocusing last used item in code.\");\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Dragging\"))\n        {\n            IMGUI_DEMO_MARKER(\"Inputs & Focus/Dragging\");\n            ImGui::TextWrapped(\"You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.\");\n            for (int button = 0; button < 3; button++)\n            {\n                ImGui::Text(\"IsMouseDragging(%d):\", button);\n                ImGui::Text(\"  w/ default threshold: %d,\", ImGui::IsMouseDragging(button));\n                ImGui::Text(\"  w/ zero threshold: %d,\", ImGui::IsMouseDragging(button, 0.0f));\n                ImGui::Text(\"  w/ large threshold: %d,\", ImGui::IsMouseDragging(button, 20.0f));\n            }\n\n            ImGui::Button(\"Drag Me\");\n            if (ImGui::IsItemActive())\n                ImGui::GetForegroundDrawList()->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); // Draw a line between the button and the mouse cursor\n\n            // Drag operations gets \"unlocked\" when the mouse has moved past a certain threshold\n            // (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher\n            // threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta().\n            ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f);\n            ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0);\n            ImVec2 mouse_delta = io.MouseDelta;\n            ImGui::Text(\"GetMouseDragDelta(0):\");\n            ImGui::Text(\"  w/ default threshold: (%.1f, %.1f)\", value_with_lock_threshold.x, value_with_lock_threshold.y);\n            ImGui::Text(\"  w/ zero threshold: (%.1f, %.1f)\", value_raw.x, value_raw.y);\n            ImGui::Text(\"io.MouseDelta: (%.1f, %.1f)\", mouse_delta.x, mouse_delta.y);\n            ImGui::TreePop();\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] About Window / ShowAboutWindow()\n// Access from Dear ImGui Demo -> Tools -> About\n//-----------------------------------------------------------------------------\n\nvoid ImGui::ShowAboutWindow(bool* p_open)\n{\n    if (!ImGui::Begin(\"About Dear ImGui\", p_open, ImGuiWindowFlags_AlwaysAutoResize))\n    {\n        ImGui::End();\n        return;\n    }\n    IMGUI_DEMO_MARKER(\"Tools/About Dear ImGui\");\n    ImGui::Text(\"Dear ImGui %s (%d)\", IMGUI_VERSION, IMGUI_VERSION_NUM);\n\n    ImGui::TextLinkOpenURL(\"Homepage\", \"https://github.com/ocornut/imgui\");\n    ImGui::SameLine();\n    ImGui::TextLinkOpenURL(\"FAQ\", \"https://github.com/ocornut/imgui/blob/master/docs/FAQ.md\");\n    ImGui::SameLine();\n    ImGui::TextLinkOpenURL(\"Wiki\", \"https://github.com/ocornut/imgui/wiki\");\n    ImGui::SameLine();\n    ImGui::TextLinkOpenURL(\"Extensions\", \"https://github.com/ocornut/imgui/wiki/Useful-Extensions\");\n    ImGui::SameLine();\n    ImGui::TextLinkOpenURL(\"Releases\", \"https://github.com/ocornut/imgui/releases\");\n    ImGui::SameLine();\n    ImGui::TextLinkOpenURL(\"Funding\", \"https://github.com/ocornut/imgui/wiki/Funding\");\n\n    ImGui::Separator();\n    ImGui::Text(\"(c) 2014-2026 Omar Cornut\");\n    ImGui::Text(\"Developed by Omar Cornut and all Dear ImGui contributors.\");\n    ImGui::Text(\"Dear ImGui is licensed under the MIT License, see LICENSE for more information.\");\n    ImGui::Text(\"If your company uses this, please consider funding the project.\");\n\n    static bool show_config_info = false;\n    ImGui::Checkbox(\"Config/Build Information\", &show_config_info);\n    if (show_config_info)\n    {\n        ImGuiIO& io = ImGui::GetIO();\n        ImGuiStyle& style = ImGui::GetStyle();\n\n        bool copy_to_clipboard = ImGui::Button(\"Copy to clipboard\");\n        ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18);\n        ImGui::BeginChild(ImGui::GetID(\"cfg_infos\"), child_size, ImGuiChildFlags_FrameStyle);\n        if (copy_to_clipboard)\n        {\n            ImGui::LogToClipboard();\n            ImGui::LogText(\"```cpp\\n\"); // Back quotes will make text appears without formatting when pasting on GitHub\n        }\n\n        ImGui::Text(\"Dear ImGui %s (%d)\", IMGUI_VERSION, IMGUI_VERSION_NUM);\n        ImGui::Separator();\n        ImGui::Text(\"sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d\", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert));\n        ImGui::Text(\"define: __cplusplus=%d\", (int)__cplusplus);\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n        ImGui::Text(\"define: IMGUI_ENABLE_TEST_ENGINE\");\n#endif\n#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_WIN32_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_WIN32_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_FILE_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_FILE_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS\n        ImGui::Text(\"define: IMGUI_DISABLE_DEFAULT_ALLOCATORS\");\n#endif\n#ifdef IMGUI_USE_BGRA_PACKED_COLOR\n        ImGui::Text(\"define: IMGUI_USE_BGRA_PACKED_COLOR\");\n#endif\n#ifdef _WIN32\n        ImGui::Text(\"define: _WIN32\");\n#endif\n#ifdef _WIN64\n        ImGui::Text(\"define: _WIN64\");\n#endif\n#ifdef __linux__\n        ImGui::Text(\"define: __linux__\");\n#endif\n#ifdef __APPLE__\n        ImGui::Text(\"define: __APPLE__\");\n#endif\n#ifdef _MSC_VER\n        ImGui::Text(\"define: _MSC_VER=%d\", _MSC_VER);\n#endif\n#ifdef _MSVC_LANG\n        ImGui::Text(\"define: _MSVC_LANG=%d\", (int)_MSVC_LANG);\n#endif\n#ifdef __MINGW32__\n        ImGui::Text(\"define: __MINGW32__\");\n#endif\n#ifdef __MINGW64__\n        ImGui::Text(\"define: __MINGW64__\");\n#endif\n#ifdef __GNUC__\n        ImGui::Text(\"define: __GNUC__=%d\", (int)__GNUC__);\n#endif\n#ifdef __clang_version__\n        ImGui::Text(\"define: __clang_version__=%s\", __clang_version__);\n#endif\n#ifdef __EMSCRIPTEN__\n        ImGui::Text(\"define: __EMSCRIPTEN__\");\n#ifdef __EMSCRIPTEN_MAJOR__\n        ImGui::Text(\"Emscripten: %d.%d.%d\", __EMSCRIPTEN_MAJOR__, __EMSCRIPTEN_MINOR__, __EMSCRIPTEN_TINY__);\n#else\n        ImGui::Text(\"Emscripten: %d.%d.%d\", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);\n#endif\n#endif\n#ifdef IMGUI_HAS_VIEWPORT\n        ImGui::Text(\"define: IMGUI_HAS_VIEWPORT\");\n#endif\n#ifdef IMGUI_HAS_DOCK\n        ImGui::Text(\"define: IMGUI_HAS_DOCK\");\n#endif\n#ifdef NDEBUG\n        ImGui::Text(\"define: NDEBUG\");\n#endif\n\n        // Heuristic to detect no-op IM_ASSERT() macros\n        // - This is designed so people opening bug reports would convey and notice that they have disabled asserts for Dear ImGui code.\n        // - 16 is > strlen(\"((void)(_EXPR))\") which we suggested in our imconfig.h template as a possible way to disable.\n        int assert_runs_expression = 0;\n        IM_ASSERT(++assert_runs_expression);\n        int assert_expand_len = (int)strlen(IM_STRINGIFY((IM_ASSERT(true))));\n        bool assert_maybe_disabled = (!assert_runs_expression || assert_expand_len <= 16);\n        ImGui::Text(\"IM_ASSERT: runs expression: %s. expand size: %s%s\",\n            assert_runs_expression ? \"OK\" : \"KO\", (assert_expand_len > 16) ? \"OK\" : \"KO\", assert_maybe_disabled ? \" (MAYBE DISABLED?!)\" : \"\");\n        if (assert_maybe_disabled)\n        {\n            ImGui::SameLine();\n            HelpMarker(\"IM_ASSERT() calls assert() by default. Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes!\");\n        }\n\n        ImGui::Separator();\n        ImGui::Text(\"io.BackendPlatformName: %s\", io.BackendPlatformName ? io.BackendPlatformName : \"NULL\");\n        ImGui::Text(\"io.BackendRendererName: %s\", io.BackendRendererName ? io.BackendRendererName : \"NULL\");\n        ImGui::Text(\"io.ConfigFlags: 0x%08X\", io.ConfigFlags);\n        if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)        ImGui::Text(\" NavEnableKeyboard\");\n        if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad)         ImGui::Text(\" NavEnableGamepad\");\n        if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)                  ImGui::Text(\" NoMouse\");\n        if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)      ImGui::Text(\" NoMouseCursorChange\");\n        if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)               ImGui::Text(\" NoKeyboard\");\n        if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)            ImGui::Text(\" DockingEnable\");\n        if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)          ImGui::Text(\" ViewportsEnable\");\n        if (io.MouseDrawCursor)                                         ImGui::Text(\"io.MouseDrawCursor\");\n        if (io.ConfigDpiScaleFonts)                                     ImGui::Text(\"io.ConfigDpiScaleFonts\");\n        if (io.ConfigDpiScaleViewports)                                 ImGui::Text(\"io.ConfigDpiScaleViewports\");\n        if (io.ConfigViewportsNoAutoMerge)                              ImGui::Text(\"io.ConfigViewportsNoAutoMerge\");\n        if (io.ConfigViewportsNoTaskBarIcon)                            ImGui::Text(\"io.ConfigViewportsNoTaskBarIcon\");\n        if (io.ConfigViewportsNoDecoration)                             ImGui::Text(\"io.ConfigViewportsNoDecoration\");\n        if (io.ConfigViewportsNoDefaultParent)                          ImGui::Text(\"io.ConfigViewportsNoDefaultParent\");\n        if (io.ConfigDockingNoSplit)                                    ImGui::Text(\"io.ConfigDockingNoSplit\");\n        if (io.ConfigDockingNoDockingOver)                              ImGui::Text(\"io.ConfigDockingNoDockingOver\");\n        if (io.ConfigDockingWithShift)                                  ImGui::Text(\"io.ConfigDockingWithShift\");\n        if (io.ConfigDockingAlwaysTabBar)                               ImGui::Text(\"io.ConfigDockingAlwaysTabBar\");\n        if (io.ConfigDockingTransparentPayload)                         ImGui::Text(\"io.ConfigDockingTransparentPayload\");\n        if (io.ConfigMacOSXBehaviors)                                   ImGui::Text(\"io.ConfigMacOSXBehaviors\");\n        if (io.ConfigNavMoveSetMousePos)                                ImGui::Text(\"io.ConfigNavMoveSetMousePos\");\n        if (io.ConfigNavCaptureKeyboard)                                ImGui::Text(\"io.ConfigNavCaptureKeyboard\");\n        if (io.ConfigInputTextCursorBlink)                              ImGui::Text(\"io.ConfigInputTextCursorBlink\");\n        if (io.ConfigWindowsResizeFromEdges)                            ImGui::Text(\"io.ConfigWindowsResizeFromEdges\");\n        if (io.ConfigWindowsMoveFromTitleBarOnly)                       ImGui::Text(\"io.ConfigWindowsMoveFromTitleBarOnly\");\n        if (io.ConfigMemoryCompactTimer >= 0.0f)                        ImGui::Text(\"io.ConfigMemoryCompactTimer = %.1f\", io.ConfigMemoryCompactTimer);\n        ImGui::Text(\"io.BackendFlags: 0x%08X\", io.BackendFlags);\n        if (io.BackendFlags & ImGuiBackendFlags_HasGamepad)             ImGui::Text(\" HasGamepad\");\n        if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors)        ImGui::Text(\" HasMouseCursors\");\n        if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)         ImGui::Text(\" HasSetMousePos\");\n        if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports)   ImGui::Text(\" PlatformHasViewports\");\n        if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)ImGui::Text(\" HasMouseHoveredViewport\");\n        if (io.BackendFlags & ImGuiBackendFlags_HasParentViewport)      ImGui::Text(\" HasParentViewport\");\n        if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)   ImGui::Text(\" RendererHasVtxOffset\");\n        if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures)    ImGui::Text(\" RendererHasTextures\");\n        if (io.BackendFlags & ImGuiBackendFlags_RendererHasViewports)   ImGui::Text(\" RendererHasViewports\");\n        ImGui::Separator();\n        ImGui::Text(\"io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d\", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexData->Width, io.Fonts->TexData->Height);\n        ImGui::Text(\"io.Fonts->FontLoaderName: %s\", io.Fonts->FontLoaderName ? io.Fonts->FontLoaderName : \"NULL\");\n        ImGui::Text(\"io.DisplaySize: %.2f,%.2f\", io.DisplaySize.x, io.DisplaySize.y);\n        ImGui::Text(\"io.DisplayFramebufferScale: %.2f,%.2f\", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);\n        ImGui::Separator();\n        ImGui::Text(\"style.WindowPadding: %.2f,%.2f\", style.WindowPadding.x, style.WindowPadding.y);\n        ImGui::Text(\"style.WindowBorderSize: %.2f\", style.WindowBorderSize);\n        ImGui::Text(\"style.FramePadding: %.2f,%.2f\", style.FramePadding.x, style.FramePadding.y);\n        ImGui::Text(\"style.FrameRounding: %.2f\", style.FrameRounding);\n        ImGui::Text(\"style.FrameBorderSize: %.2f\", style.FrameBorderSize);\n        ImGui::Text(\"style.ItemSpacing: %.2f,%.2f\", style.ItemSpacing.x, style.ItemSpacing.y);\n        ImGui::Text(\"style.ItemInnerSpacing: %.2f,%.2f\", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y);\n\n        if (copy_to_clipboard)\n        {\n            ImGui::LogText(\"\\n```\\n\");\n            ImGui::LogFinish();\n        }\n        ImGui::EndChild();\n    }\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Style Editor / ShowStyleEditor()\n//-----------------------------------------------------------------------------\n// - ShowStyleSelector()\n// - ShowStyleEditor()\n//-----------------------------------------------------------------------------\n\n// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.\nbool ImGui::ShowStyleSelector(const char* label)\n{\n    // FIXME: This is a bit tricky to get right as style are functions, they don't register a name nor the fact that one is active.\n    // So we keep track of last active one among our limited selection.\n    static int style_idx = -1;\n    const char* style_names[] = { \"Dark\", \"Light\", \"Classic\" };\n    bool ret = false;\n    if (ImGui::BeginCombo(label, (style_idx >= 0 && style_idx < IM_COUNTOF(style_names)) ? style_names[style_idx] : \"\"))\n    {\n        for (int n = 0; n < IM_COUNTOF(style_names); n++)\n        {\n            if (ImGui::Selectable(style_names[n], style_idx == n, ImGuiSelectableFlags_SelectOnNav))\n            {\n                style_idx = n;\n                ret = true;\n                switch (style_idx)\n                {\n                case 0: ImGui::StyleColorsDark(); break;\n                case 1: ImGui::StyleColorsLight(); break;\n                case 2: ImGui::StyleColorsClassic(); break;\n                }\n            }\n            else if (style_idx == n)\n                ImGui::SetItemDefaultFocus();\n        }\n        ImGui::EndCombo();\n    }\n    return ret;\n}\n\nstatic const char* GetTreeLinesFlagsName(ImGuiTreeNodeFlags flags)\n{\n    if (flags == ImGuiTreeNodeFlags_DrawLinesNone) return \"DrawLinesNone\";\n    if (flags == ImGuiTreeNodeFlags_DrawLinesFull) return \"DrawLinesFull\";\n    if (flags == ImGuiTreeNodeFlags_DrawLinesToNodes) return \"DrawLinesToNodes\";\n    return \"\";\n}\n\n// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.\nvoid ImGui::ShowStyleEditor(ImGuiStyle* ref)\n{\n    IMGUI_DEMO_MARKER(\"Tools/Style Editor\");\n    // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to\n    // (without a reference style pointer, we will use one compared locally as a reference)\n    ImGuiStyle& style = GetStyle();\n    static ImGuiStyle ref_saved_style;\n\n    // Default to using internal storage as reference\n    static bool init = true;\n    if (init && ref == NULL)\n        ref_saved_style = style;\n    init = false;\n    if (ref == NULL)\n        ref = &ref_saved_style;\n\n    // The logic behind dynamically changing 'max_border_size' is to not encourage people to increase border size too much: it'll likely reveal lots of subtle rendering artifacts and this isn't a priority right now.\n    // Note that _MainScale is currently internal PLEASE DO NOT USE IN YOUR CODE.\n    const float default_border_size = (float)(int)style._MainScale;\n    const float max_border_size = IM_MAX(default_border_size, 2.0f);\n\n    PushItemWidth(GetWindowWidth() * 0.50f);\n\n    {\n        // General\n        SeparatorText(\"General\");\n        if ((GetIO().BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)\n        {\n            BulletText(\"Warning: Font scaling will NOT be smooth, because\\nImGuiBackendFlags_RendererHasTextures is not set!\");\n            BulletText(\"For instructions, see:\");\n            SameLine();\n            TextLinkOpenURL(\"docs/BACKENDS.md\", \"https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md\");\n        }\n\n        if (ShowStyleSelector(\"Colors##Selector\"))\n            ref_saved_style = style;\n        ShowFontSelector(\"Fonts##Selector\");\n        if (DragFloat(\"FontSizeBase\", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, \"%.0f\"))\n            style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work.\n        SameLine(0.0f, 0.0f); Text(\" (out %.2f)\", GetFontSize());\n        DragFloat(\"FontScaleMain\", &style.FontScaleMain, 0.02f, 0.5f, 4.0f);\n        BeginDisabled(GetIO().ConfigDpiScaleFonts);\n        DragFloat(\"FontScaleDpi\", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f);\n        SetItemTooltip(\"When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.\");\n        EndDisabled();\n\n        // Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)\n        if (SliderFloat(\"FrameRounding\", &style.FrameRounding, 0.0f, 12.0f, \"%.0f\"))\n            style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding\n        { bool border = (style.WindowBorderSize > 0.0f); if (Checkbox(\"WindowBorder\", &border)) { style.WindowBorderSize = border ? default_border_size : 0.0f; } }\n        SameLine();\n        { bool border = (style.FrameBorderSize > 0.0f);  if (Checkbox(\"FrameBorder\", &border)) { style.FrameBorderSize = border ? default_border_size : 0.0f; } }\n        SameLine();\n        { bool border = (style.PopupBorderSize > 0.0f);  if (Checkbox(\"PopupBorder\", &border)) { style.PopupBorderSize = border ? default_border_size : 0.0f; } }\n    }\n\n    // Save/Revert button\n    if (Button(\"Save Ref\"))\n        *ref = ref_saved_style = style;\n    SameLine();\n    if (Button(\"Revert Ref\"))\n        style = *ref;\n    SameLine();\n    HelpMarker(\n        \"Save/Revert in local non-persistent storage. Default Colors definition are not affected. \"\n        \"Use \\\"Export\\\" below to save them somewhere.\");\n\n    SeparatorText(\"Details\");\n    if (BeginTabBar(\"##tabs\", ImGuiTabBarFlags_None))\n    {\n        if (BeginTabItem(\"Sizes\"))\n        {\n            SeparatorText(\"Main\");\n            SliderFloat2(\"WindowPadding\", (float*)&style.WindowPadding, 0.0f, 20.0f, \"%.0f\");\n            SliderFloat2(\"FramePadding\", (float*)&style.FramePadding, 0.0f, 20.0f, \"%.0f\");\n            SliderFloat2(\"ItemSpacing\", (float*)&style.ItemSpacing, 0.0f, 20.0f, \"%.0f\");\n            SliderFloat2(\"ItemInnerSpacing\", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, \"%.0f\");\n            SliderFloat2(\"TouchExtraPadding\", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, \"%.0f\");\n            SliderFloat(\"IndentSpacing\", &style.IndentSpacing, 0.0f, 30.0f, \"%.0f\");\n            SliderFloat(\"GrabMinSize\", &style.GrabMinSize, 1.0f, 20.0f, \"%.0f\");\n\n            SeparatorText(\"Borders\");\n            SliderFloat(\"WindowBorderSize\", &style.WindowBorderSize, 0.0f, max_border_size, \"%.0f\");\n            SliderFloat(\"ChildBorderSize\", &style.ChildBorderSize, 0.0f, max_border_size, \"%.0f\");\n            SliderFloat(\"PopupBorderSize\", &style.PopupBorderSize, 0.0f, max_border_size, \"%.0f\");\n            SliderFloat(\"FrameBorderSize\", &style.FrameBorderSize, 0.0f, max_border_size, \"%.0f\");\n\n            SeparatorText(\"Rounding\");\n            SliderFloat(\"WindowRounding\", &style.WindowRounding, 0.0f, 12.0f, \"%.0f\");\n            SliderFloat(\"ChildRounding\", &style.ChildRounding, 0.0f, 12.0f, \"%.0f\");\n            SliderFloat(\"FrameRounding\", &style.FrameRounding, 0.0f, 12.0f, \"%.0f\");\n            SliderFloat(\"PopupRounding\", &style.PopupRounding, 0.0f, 12.0f, \"%.0f\");\n            SliderFloat(\"GrabRounding\", &style.GrabRounding, 0.0f, 12.0f, \"%.0f\");\n\n            SeparatorText(\"Scrollbar\");\n            SliderFloat(\"ScrollbarSize\", &style.ScrollbarSize, 1.0f, 20.0f, \"%.0f\");\n            SliderFloat(\"ScrollbarRounding\", &style.ScrollbarRounding, 0.0f, 12.0f, \"%.0f\");\n            SliderFloat(\"ScrollbarPadding\", &style.ScrollbarPadding, 0.0f, 10.0f, \"%.0f\");\n\n            SeparatorText(\"Tabs\");\n            SliderFloat(\"TabBorderSize\", &style.TabBorderSize, 0.0f, max_border_size, \"%.0f\");\n            SliderFloat(\"TabBarBorderSize\", &style.TabBarBorderSize, 0.0f, max_border_size, \"%.0f\");\n            SliderFloat(\"TabBarOverlineSize\", &style.TabBarOverlineSize, 0.0f, IM_MAX(3.0f, max_border_size), \"%.0f\");\n            SameLine(); HelpMarker(\"Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set.\");\n            DragFloat(\"TabMinWidthBase\", &style.TabMinWidthBase, 0.5f, 1.0f, 500.0f, \"%.0f\");\n            DragFloat(\"TabMinWidthShrink\", &style.TabMinWidthShrink, 0.5f, 1.0f, 500.0f, \"%0.f\");\n            DragFloat(\"TabCloseButtonMinWidthSelected\", &style.TabCloseButtonMinWidthSelected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthSelected < 0.0f) ? \"%.0f (Always)\" : \"%.0f\");\n            DragFloat(\"TabCloseButtonMinWidthUnselected\", &style.TabCloseButtonMinWidthUnselected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthUnselected < 0.0f) ? \"%.0f (Always)\" : \"%.0f\");\n            SliderFloat(\"TabRounding\", &style.TabRounding, 0.0f, 12.0f, \"%.0f\");\n\n            SeparatorText(\"Tables\");\n            SliderFloat2(\"CellPadding\", (float*)&style.CellPadding, 0.0f, 20.0f, \"%.0f\");\n            SliderAngle(\"TableAngledHeadersAngle\", &style.TableAngledHeadersAngle, -50.0f, +50.0f);\n            SliderFloat2(\"TableAngledHeadersTextAlign\", (float*)&style.TableAngledHeadersTextAlign, 0.0f, 1.0f, \"%.2f\");\n\n            SeparatorText(\"Trees\");\n            bool combo_open = BeginCombo(\"TreeLinesFlags\", GetTreeLinesFlagsName(style.TreeLinesFlags));\n            SameLine();\n            HelpMarker(\"[Experimental] Tree lines may not work in all situations (e.g. using a clipper) and may incurs slight traversal overhead.\\n\\nImGuiTreeNodeFlags_DrawLinesFull is faster than ImGuiTreeNodeFlags_DrawLinesToNode.\");\n            if (combo_open)\n            {\n                const ImGuiTreeNodeFlags options[] = { ImGuiTreeNodeFlags_DrawLinesNone, ImGuiTreeNodeFlags_DrawLinesFull, ImGuiTreeNodeFlags_DrawLinesToNodes };\n                for (ImGuiTreeNodeFlags option : options)\n                    if (Selectable(GetTreeLinesFlagsName(option), style.TreeLinesFlags == option))\n                        style.TreeLinesFlags = option;\n                EndCombo();\n            }\n            SliderFloat(\"TreeLinesSize\", &style.TreeLinesSize, 0.0f, max_border_size, \"%.0f\");\n            SliderFloat(\"TreeLinesRounding\", &style.TreeLinesRounding, 0.0f, 12.0f, \"%.0f\");\n\n            SeparatorText(\"Windows\");\n            SliderFloat2(\"WindowTitleAlign\", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, \"%.2f\");\n            SliderFloat(\"WindowBorderHoverPadding\", &style.WindowBorderHoverPadding, 1.0f, 20.0f, \"%.0f\");\n            int window_menu_button_position = style.WindowMenuButtonPosition + 1;\n            if (Combo(\"WindowMenuButtonPosition\", (int*)&window_menu_button_position, \"None\\0Left\\0Right\\0\"))\n                style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1);\n\n            SeparatorText(\"Widgets\");\n            SliderFloat(\"ColorMarkerSize\", &style.ColorMarkerSize, 0.0f, 8.0f, \"%.0f\");\n            Combo(\"ColorButtonPosition\", (int*)&style.ColorButtonPosition, \"Left\\0Right\\0\");\n            SliderFloat2(\"ButtonTextAlign\", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, \"%.2f\");\n            SameLine(); HelpMarker(\"Alignment applies when a button is larger than its text content.\");\n            SliderFloat2(\"SelectableTextAlign\", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, \"%.2f\");\n            SameLine(); HelpMarker(\"Alignment applies when a selectable is larger than its text content.\");\n            SliderFloat(\"SeparatorSize\", &style.SeparatorSize, 0.0f, 10.0f, \"%.0f\");\n            SliderFloat(\"SeparatorTextBorderSize\", &style.SeparatorTextBorderSize, 0.0f, 10.0f, \"%.0f\");\n            SliderFloat2(\"SeparatorTextAlign\", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, \"%.2f\");\n            SliderFloat2(\"SeparatorTextPadding\", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, \"%.0f\");\n            SliderFloat(\"LogSliderDeadzone\", &style.LogSliderDeadzone, 0.0f, 12.0f, \"%.0f\");\n            SliderFloat(\"ImageRounding\", &style.ImageRounding, 0.0f, 12.0f, \"%.0f\");\n            SliderFloat(\"ImageBorderSize\", &style.ImageBorderSize, 0.0f, max_border_size, \"%.0f\");\n\n            SeparatorText(\"Docking\");\n            //SetCursorPosX(GetCursorPosX() + CalcItemWidth() - GetFrameHeight());\n            Checkbox(\"DockingNodeHasCloseButton\", &style.DockingNodeHasCloseButton);\n            SliderFloat(\"DockingSeparatorSize\", &style.DockingSeparatorSize, 0.0f, 12.0f, \"%.0f\");\n\n            SeparatorText(\"Tooltips\");\n            for (int n = 0; n < 2; n++)\n                if (TreeNodeEx(n == 0 ? \"HoverFlagsForTooltipMouse\" : \"HoverFlagsForTooltipNav\"))\n                {\n                    ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav;\n                    CheckboxFlags(\"ImGuiHoveredFlags_DelayNone\", p, ImGuiHoveredFlags_DelayNone);\n                    CheckboxFlags(\"ImGuiHoveredFlags_DelayShort\", p, ImGuiHoveredFlags_DelayShort);\n                    CheckboxFlags(\"ImGuiHoveredFlags_DelayNormal\", p, ImGuiHoveredFlags_DelayNormal);\n                    CheckboxFlags(\"ImGuiHoveredFlags_Stationary\", p, ImGuiHoveredFlags_Stationary);\n                    CheckboxFlags(\"ImGuiHoveredFlags_NoSharedDelay\", p, ImGuiHoveredFlags_NoSharedDelay);\n                    TreePop();\n                }\n\n            SeparatorText(\"Misc\");\n            SliderFloat2(\"DisplayWindowPadding\", (float*)&style.DisplayWindowPadding, 0.0f, 30.0f, \"%.0f\"); SameLine(); HelpMarker(\"Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen.\");\n            SliderFloat2(\"DisplaySafeAreaPadding\", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, \"%.0f\"); SameLine(); HelpMarker(\"Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).\");\n\n            EndTabItem();\n        }\n\n        if (BeginTabItem(\"Colors\"))\n        {\n            static int output_dest = 0;\n            static bool output_only_modified = true;\n            if (Button(\"Export\"))\n            {\n                if (output_dest == 0)\n                    LogToClipboard();\n                else\n                    LogToTTY();\n                LogText(\"ImVec4* colors = GetStyle().Colors;\" IM_NEWLINE);\n                for (int i = 0; i < ImGuiCol_COUNT; i++)\n                {\n                    const ImVec4& col = style.Colors[i];\n                    const char* name = GetStyleColorName(i);\n                    if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)\n                        LogText(\"colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\" IM_NEWLINE,\n                            name, 23 - (int)strlen(name), \"\", col.x, col.y, col.z, col.w);\n                }\n                LogFinish();\n            }\n            SameLine(); SetNextItemWidth(GetFontSize() * 10); Combo(\"##output_type\", &output_dest, \"To Clipboard\\0To TTY\\0\");\n            SameLine(); Checkbox(\"Only Modified Colors\", &output_only_modified);\n\n            static ImGuiTextFilter filter;\n            filter.Draw(\"Filter colors\", GetFontSize() * 16);\n\n            static ImGuiColorEditFlags alpha_flags = 0;\n            if (RadioButton(\"Opaque\", alpha_flags == ImGuiColorEditFlags_AlphaOpaque))       { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } SameLine();\n            if (RadioButton(\"Alpha\",  alpha_flags == ImGuiColorEditFlags_None))              { alpha_flags = ImGuiColorEditFlags_None; } SameLine();\n            if (RadioButton(\"Both\",   alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf))  { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } SameLine();\n            HelpMarker(\n                \"In the color list:\\n\"\n                \"Left-click on color square to open color picker,\\n\"\n                \"Right-click to open edit options menu.\");\n\n            SetNextWindowSizeConstraints(ImVec2(0.0f, GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX));\n            BeginChild(\"##colors\", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);\n            PushItemWidth(GetFontSize() * -12);\n            for (int i = 0; i < ImGuiCol_COUNT; i++)\n            {\n                const char* name = GetStyleColorName(i);\n                if (!filter.PassFilter(name))\n                    continue;\n                PushID(i);\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n                if (Button(\"?\"))\n                    DebugFlashStyleColor((ImGuiCol)i);\n                SetItemTooltip(\"Flash given color to identify places where it is used.\");\n                SameLine();\n#endif\n                ColorEdit4(\"##color\", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags);\n                if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0)\n                {\n                    // Tips: in a real user application, you may want to merge and use an icon font into the main font,\n                    // so instead of \"Save\"/\"Revert\" you'd use icons!\n                    // Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!\n                    SameLine(0.0f, style.ItemInnerSpacing.x); if (Button(\"Save\")) { ref->Colors[i] = style.Colors[i]; }\n                    SameLine(0.0f, style.ItemInnerSpacing.x); if (Button(\"Revert\")) { style.Colors[i] = ref->Colors[i]; }\n                }\n                SameLine(0.0f, style.ItemInnerSpacing.x);\n                TextUnformatted(name);\n                PopID();\n            }\n            PopItemWidth();\n            EndChild();\n\n            EndTabItem();\n        }\n\n        if (BeginTabItem(\"Fonts\"))\n        {\n            ImGuiIO& io = GetIO();\n            ImFontAtlas* atlas = io.Fonts;\n            ShowFontAtlas(atlas);\n\n            // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.\n            // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows Ctrl+Click text to get out of bounds).\n            /*\n            SeparatorText(\"Legacy Scaling\");\n            const float MIN_SCALE = 0.3f;\n            const float MAX_SCALE = 2.0f;\n            HelpMarker(\n                \"Those are old settings provided for convenience.\\n\"\n                \"However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, \"\n                \"rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\\n\"\n                \"Using those settings here will give you poor quality results.\");\n            PushItemWidth(GetFontSize() * 8);\n            DragFloat(\"global scale\", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, \"%.2f\", ImGuiSliderFlags_AlwaysClamp); // Scale everything\n            //static float window_scale = 1.0f;\n            //if (DragFloat(\"window scale\", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, \"%.2f\", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window\n            //    SetWindowFontScale(window_scale);\n            PopItemWidth();\n            */\n\n            EndTabItem();\n        }\n\n        if (BeginTabItem(\"Rendering\"))\n        {\n            Checkbox(\"Anti-aliased lines\", &style.AntiAliasedLines);\n            SameLine();\n            HelpMarker(\"When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.\");\n\n            Checkbox(\"Anti-aliased lines use texture\", &style.AntiAliasedLinesUseTex);\n            SameLine();\n            HelpMarker(\"Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering).\");\n\n            Checkbox(\"Anti-aliased fill\", &style.AntiAliasedFill);\n            PushItemWidth(GetFontSize() * 8);\n            DragFloat(\"Curve Tessellation Tolerance\", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, \"%.2f\");\n            if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;\n\n            // When editing the \"Circle Segment Max Error\" value, draw a preview of its effect on auto-tessellated circles.\n            DragFloat(\"Circle Tessellation Max Error\", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, \"%.2f\", ImGuiSliderFlags_AlwaysClamp);\n            const bool show_samples = IsItemActive();\n            if (show_samples)\n                SetNextWindowPos(GetCursorScreenPos());\n            if (show_samples && BeginTooltip())\n            {\n                TextUnformatted(\"(R = radius, N = approx number of segments)\");\n                Spacing();\n                ImDrawList* draw_list = GetWindowDrawList();\n                const float min_widget_width = CalcTextSize(\"R: MMM\\nN: MMM\").x;\n                for (int n = 0; n < 8; n++)\n                {\n                    const float RAD_MIN = 5.0f;\n                    const float RAD_MAX = 70.0f;\n                    const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);\n\n                    BeginGroup();\n\n                    // N is not always exact here due to how PathArcTo() function work internally\n                    Text(\"R: %.f\\nN: %d\", rad, draw_list->_CalcCircleAutoSegmentCount(rad));\n\n                    const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f);\n                    const float offset_x     = floorf(canvas_width * 0.5f);\n                    const float offset_y     = floorf(RAD_MAX);\n\n                    const ImVec2 p1 = GetCursorScreenPos();\n                    draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, GetColorU32(ImGuiCol_Text));\n                    Dummy(ImVec2(canvas_width, RAD_MAX * 2));\n\n                    /*\n                    const ImVec2 p2 = GetCursorScreenPos();\n                    draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, GetColorU32(ImGuiCol_Text));\n                    Dummy(ImVec2(canvas_width, RAD_MAX * 2));\n                    */\n\n                    EndGroup();\n                    SameLine();\n                }\n                EndTooltip();\n            }\n            SameLine();\n            HelpMarker(\"When drawing circle primitives with \\\"num_segments == 0\\\" tessellation will be calculated automatically.\");\n\n            DragFloat(\"Global Alpha\", &style.Alpha, 0.005f, 0.20f, 1.0f, \"%.2f\"); // Not exposing zero here so user doesn't \"lose\" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero.\n            DragFloat(\"Disabled Alpha\", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, \"%.2f\"); SameLine(); HelpMarker(\"Additional alpha multiplier for disabled items (multiply over current value of Alpha).\");\n            PopItemWidth();\n\n            EndTabItem();\n        }\n\n        if (BeginTabItem(\"Shadows\"))\n        {\n            Text(\"Window shadows:\");\n            ColorEdit4(\"Color\", (float*)&style.Colors[ImGuiCol_WindowShadow], ImGuiColorEditFlags_AlphaBar);\n            SameLine();\n            HelpMarker(\"Same as 'WindowShadow' in Colors tab.\");\n\n            SliderFloat(\"Size\", &style.WindowShadowSize, 0.0f, 128.0f, \"%.1f\");\n            SameLine();\n            HelpMarker(\"Set shadow size to zero to disable shadows.\");\n            SliderFloat(\"Offset distance\", &style.WindowShadowOffsetDist, 0.0f, 64.0f, \"%.0f\");\n            SliderAngle(\"Offset angle\", &style.WindowShadowOffsetAngle);\n\n            EndTabItem();\n        }\n\n        EndTabBar();\n    }\n    PopItemWidth();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] User Guide / ShowUserGuide()\n//-----------------------------------------------------------------------------\n\n// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.\nvoid ImGui::ShowUserGuide()\n{\n    ImGuiIO& io = GetIO();\n    BulletText(\"Double-click on title bar to collapse window.\");\n    BulletText(\n        \"Click and drag on lower corner or border to resize window.\\n\"\n        \"(double-click to auto fit window to its contents)\");\n    BulletText(\"Ctrl+Click on a slider or drag box to input value as text.\");\n    BulletText(\"Tab/Shift+Tab to cycle through keyboard editable fields.\");\n    BulletText(\"Ctrl+Tab/Ctrl+Shift+Tab to focus windows.\");\n    if (io.FontAllowUserScaling)\n        BulletText(\"Ctrl+Mouse Wheel to zoom window contents.\");\n    BulletText(\"While inputting text:\\n\");\n    Indent();\n    BulletText(\"Ctrl+Left/Right to word jump.\");\n    BulletText(\"Ctrl+A or double-click to select all.\");\n    BulletText(\"Ctrl+X/C/V to use clipboard cut/copy/paste.\");\n    BulletText(\"Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo.\");\n    BulletText(\"Escape to revert.\");\n    Unindent();\n    BulletText(\"With Keyboard controls enabled:\");\n    Indent();\n    BulletText(\"Arrow keys or Home/End/PageUp/PageDown to navigate.\");\n    BulletText(\"Space to activate a widget.\");\n    BulletText(\"Return to input text into a widget.\");\n    BulletText(\"Escape to deactivate a widget, close popup,\\nexit a child window or the menu layer, clear focus.\");\n    BulletText(\"Alt to jump to the menu layer of a window.\");\n    BulletText(\"Menu or Shift+F10 to open a context menu.\");\n    Unindent();\n    BulletText(\"With Gamepad controls enabled:\");\n    Indent();\n    BulletText(\"D-Pad: Navigate / Tweak / Resize (in Windowing mode).\");\n    BulletText(\"%s Face button: Activate / Open / Toggle. Hold: activate with text input.\", io.ConfigNavSwapGamepadButtons ? \"East\" : \"South\");\n    BulletText(\"%s Face button: Cancel / Close / Exit.\", io.ConfigNavSwapGamepadButtons ? \"South\" : \"East\");\n    BulletText(\"West Face button: Toggle Menu. Hold for Windowing mode (Focus/Move/Resize windows).\");\n    BulletText(\"North Face button: Open Context Menu.\");\n    BulletText(\"L1/R1: Tweak Slower/Faster, Focus Previous/Next (in Windowing Mode).\");\n    Unindent();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()\n//-----------------------------------------------------------------------------\n// - ShowExampleAppMainMenuBar()\n// - ShowExampleMenuFile()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a \"main\" fullscreen menu bar and populating it.\n// Note the difference between BeginMainMenuBar() and BeginMenuBar():\n// - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!)\n// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it.\nstatic void ShowExampleAppMainMenuBar()\n{\n    if (ImGui::BeginMainMenuBar())\n    {\n        if (ImGui::BeginMenu(\"File\"))\n        {\n            IMGUI_DEMO_MARKER(\"Menu/File\");\n            ShowExampleMenuFile();\n            ImGui::EndMenu();\n        }\n        if (ImGui::BeginMenu(\"Edit\"))\n        {\n            IMGUI_DEMO_MARKER(\"Menu/Edit\");\n            if (ImGui::MenuItem(\"Undo\", \"Ctrl+Z\")) {}\n            if (ImGui::MenuItem(\"Redo\", \"Ctrl+Y\", false, false)) {} // Disabled item\n            ImGui::Separator();\n            if (ImGui::MenuItem(\"Cut\", \"Ctrl+X\")) {}\n            if (ImGui::MenuItem(\"Copy\", \"Ctrl+C\")) {}\n            if (ImGui::MenuItem(\"Paste\", \"Ctrl+V\")) {}\n            ImGui::EndMenu();\n        }\n        ImGui::EndMainMenuBar();\n    }\n}\n\n// Note that shortcuts are currently provided for display only\n// (future version will add explicit flags to BeginMenu() to request processing shortcuts)\nstatic void ShowExampleMenuFile()\n{\n    IMGUI_DEMO_MARKER(\"Examples/Menu\");\n    ImGui::MenuItem(\"(demo menu)\", NULL, false, false);\n    if (ImGui::MenuItem(\"New\")) {}\n    if (ImGui::MenuItem(\"Open\", \"Ctrl+O\")) {}\n    if (ImGui::BeginMenu(\"Open Recent\"))\n    {\n        ImGui::MenuItem(\"fish_hat.c\");\n        ImGui::MenuItem(\"fish_hat.inl\");\n        ImGui::MenuItem(\"fish_hat.h\");\n        if (ImGui::BeginMenu(\"More..\"))\n        {\n            ImGui::MenuItem(\"Hello\");\n            ImGui::MenuItem(\"Sailor\");\n            if (ImGui::BeginMenu(\"Recurse..\"))\n            {\n                ShowExampleMenuFile();\n                ImGui::EndMenu();\n            }\n            ImGui::EndMenu();\n        }\n        ImGui::EndMenu();\n    }\n    if (ImGui::MenuItem(\"Save\", \"Ctrl+S\")) {}\n    if (ImGui::MenuItem(\"Save As..\")) {}\n\n    ImGui::Separator();\n    if (ImGui::BeginMenu(\"Options\"))\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Menu/Options\");\n        static bool enabled = true;\n        ImGui::MenuItem(\"Enabled\", \"\", &enabled);\n        ImGui::BeginChild(\"child\", ImVec2(0, 60), ImGuiChildFlags_Borders);\n        for (int i = 0; i < 10; i++)\n            ImGui::Text(\"Scrolling Text %d\", i);\n        ImGui::EndChild();\n        static float f = 0.5f;\n        static int n = 0;\n        ImGui::SliderFloat(\"Value\", &f, 0.0f, 1.0f);\n        ImGui::InputFloat(\"Input\", &f, 0.1f);\n        ImGui::Combo(\"Combo\", &n, \"Yes\\0No\\0Maybe\\0\\0\");\n        ImGui::EndMenu();\n    }\n\n    if (ImGui::BeginMenu(\"Colors\"))\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Menu/Colors\");\n        float sz = ImGui::GetTextLineHeight();\n        for (int i = 0; i < ImGuiCol_COUNT; i++)\n        {\n            const char* name = ImGui::GetStyleColorName((ImGuiCol)i);\n            ImVec2 p = ImGui::GetCursorScreenPos();\n            ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + sz, p.y + sz), ImGui::GetColorU32((ImGuiCol)i));\n            ImGui::Dummy(ImVec2(sz, sz));\n            ImGui::SameLine();\n            ImGui::MenuItem(name);\n        }\n        ImGui::EndMenu();\n    }\n\n    // Here we demonstrate appending again to the \"Options\" menu (which we already created above)\n    // Of course in this demo it is a little bit silly that this function calls BeginMenu(\"Options\") twice.\n    // In a real code-base using it would make senses to use this feature from very different code locations.\n    if (ImGui::BeginMenu(\"Options\")) // <-- Append!\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Menu/Append to an existing menu\");\n        static bool b = true;\n        ImGui::Checkbox(\"SomeOption\", &b);\n        ImGui::EndMenu();\n    }\n\n    if (ImGui::BeginMenu(\"Disabled\", false)) // Disabled\n    {\n        IM_ASSERT(0);\n    }\n    if (ImGui::MenuItem(\"Checked\", NULL, true)) {}\n    ImGui::Separator();\n    if (ImGui::MenuItem(\"Quit\", \"Alt+F4\")) {}\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Debug Console / ShowExampleAppConsole()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a simple console window, with scrolling, filtering, completion and history.\n// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.\nstruct ExampleAppConsole\n{\n    char                  InputBuf[256];\n    ImVector<char*>       Items;\n    ImVector<const char*> Commands;\n    ImVector<char*>       History;\n    int                   HistoryPos;    // -1: new line, 0..History.Size-1 browsing history.\n    ImGuiTextFilter       Filter;\n    bool                  AutoScroll;\n    bool                  ScrollToBottom;\n\n    ExampleAppConsole()\n    {\n        ClearLog();\n        memset(InputBuf, 0, sizeof(InputBuf));\n        HistoryPos = -1;\n\n        // \"CLASSIFY\" is here to provide the test case where \"C\"+[tab] completes to \"CL\" and display multiple matches.\n        Commands.push_back(\"HELP\");\n        Commands.push_back(\"HISTORY\");\n        Commands.push_back(\"CLEAR\");\n        Commands.push_back(\"CLASSIFY\");\n        AutoScroll = true;\n        ScrollToBottom = false;\n        AddLog(\"Welcome to Dear ImGui!\");\n    }\n    ~ExampleAppConsole()\n    {\n        ClearLog();\n        for (int i = 0; i < History.Size; i++)\n            ImGui::MemFree(History[i]);\n    }\n\n    // Portable helpers\n    static int   Stricmp(const char* s1, const char* s2)         { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; }\n    static int   Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; }\n    static char* Strdup(const char* s)                           { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = ImGui::MemAlloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); }\n    static void  Strtrim(char* s)                                { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; }\n\n    void    ClearLog()\n    {\n        for (int i = 0; i < Items.Size; i++)\n            ImGui::MemFree(Items[i]);\n        Items.clear();\n    }\n\n    void    AddLog(const char* fmt, ...) IM_FMTARGS(2)\n    {\n        // FIXME-OPT\n        char buf[1024];\n        va_list args;\n        va_start(args, fmt);\n        vsnprintf(buf, IM_COUNTOF(buf), fmt, args);\n        buf[IM_COUNTOF(buf)-1] = 0;\n        va_end(args);\n        Items.push_back(Strdup(buf));\n    }\n\n    void    Draw(const char* title, bool* p_open)\n    {\n        ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);\n        if (!ImGui::Begin(title, p_open))\n        {\n            ImGui::End();\n            return;\n        }\n        IMGUI_DEMO_MARKER(\"Examples/Console\");\n\n        // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar.\n        // So e.g. IsItemHovered() will return true when hovering the title bar.\n        // Here we create a context menu only available from the title bar.\n        if (ImGui::BeginPopupContextItem())\n        {\n            if (ImGui::MenuItem(\"Close Console\"))\n                *p_open = false;\n            ImGui::EndPopup();\n        }\n\n        ImGui::TextWrapped(\n            \"This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate \"\n            \"implementation may want to store entries along with extra data such as timestamp, emitter, etc.\");\n        ImGui::TextWrapped(\"Enter 'HELP' for help.\");\n\n        // TODO: display items starting from the bottom\n\n        if (ImGui::SmallButton(\"Add Debug Text\"))  { AddLog(\"%d some text\", Items.Size); AddLog(\"some more text\"); AddLog(\"display very important message here!\"); }\n        ImGui::SameLine();\n        if (ImGui::SmallButton(\"Add Debug Error\")) { AddLog(\"[error] something went wrong\"); }\n        ImGui::SameLine();\n        if (ImGui::SmallButton(\"Clear\"))           { ClearLog(); }\n        ImGui::SameLine();\n        bool copy_to_clipboard = ImGui::SmallButton(\"Copy\");\n        //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog(\"Spam %f\", t); }\n\n        ImGui::Separator();\n\n        // Options menu\n        if (ImGui::BeginPopup(\"Options\"))\n        {\n            ImGui::Checkbox(\"Auto-scroll\", &AutoScroll);\n            ImGui::EndPopup();\n        }\n\n        // Options, Filter\n        ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_O, ImGuiInputFlags_Tooltip);\n        if (ImGui::Button(\"Options\"))\n            ImGui::OpenPopup(\"Options\");\n        ImGui::SameLine();\n        Filter.Draw(\"Filter (\\\"incl,-excl\\\") (\\\"error\\\")\", 180);\n        ImGui::Separator();\n\n        // Reserve enough left-over height for 1 separator + 1 input text\n        ImGuiStyle& style = ImGui::GetStyle();\n        const float footer_height_to_reserve = style.SeparatorSize + style.ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();\n        if (ImGui::BeginChild(\"ScrollingRegion\", ImVec2(0, -footer_height_to_reserve), ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_HorizontalScrollbar))\n        {\n            if (ImGui::BeginPopupContextWindow())\n            {\n                if (ImGui::Selectable(\"Clear\")) ClearLog();\n                ImGui::EndPopup();\n            }\n\n            // Display every line as a separate entry so we can change their color or add custom widgets.\n            // If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end());\n            // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping\n            // to only process visible items. The clipper will automatically measure the height of your first item and then\n            // \"seek\" to display only items in the visible area.\n            // To use the clipper we can replace your standard loop:\n            //      for (int i = 0; i < Items.Size; i++)\n            //   With:\n            //      ImGuiListClipper clipper;\n            //      clipper.Begin(Items.Size);\n            //      while (clipper.Step())\n            //         for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)\n            // - That your items are evenly spaced (same height)\n            // - That you have cheap random access to your elements (you can access them given their index,\n            //   without processing all the ones before)\n            // You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property.\n            // We would need random-access on the post-filtered list.\n            // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices\n            // or offsets of items that passed the filtering test, recomputing this array when user changes the filter,\n            // and appending newly elements as they are inserted. This is left as a task to the user until we can manage\n            // to improve this example code!\n            // If your items are of variable height:\n            // - Split them into same height items would be simpler and facilitate random-seeking into your list.\n            // - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items.\n            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing\n            if (copy_to_clipboard)\n                ImGui::LogToClipboard();\n            for (const char* item : Items)\n            {\n                if (!Filter.PassFilter(item))\n                    continue;\n\n                // Normally you would store more information in your item than just a string.\n                // (e.g. make Items[] an array of structure, store color/type etc.)\n                ImVec4 color;\n                bool has_color = false;\n                if (strstr(item, \"[error]\")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }\n                else if (strncmp(item, \"# \", 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; }\n                if (has_color)\n                    ImGui::PushStyleColor(ImGuiCol_Text, color);\n                ImGui::TextUnformatted(item);\n                if (has_color)\n                    ImGui::PopStyleColor();\n            }\n            if (copy_to_clipboard)\n                ImGui::LogFinish();\n\n            // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.\n            // Using a scrollbar or mouse-wheel will take away from the bottom edge.\n            if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))\n                ImGui::SetScrollHereY(1.0f);\n            ScrollToBottom = false;\n\n            ImGui::PopStyleVar();\n        }\n        ImGui::EndChild();\n        ImGui::Separator();\n\n        // Command-line\n        bool reclaim_focus = false;\n        ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;\n        if (ImGui::InputText(\"Input\", InputBuf, IM_COUNTOF(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this))\n        {\n            char* s = InputBuf;\n            Strtrim(s);\n            if (s[0])\n                ExecCommand(s);\n            strcpy(s, \"\");\n            reclaim_focus = true;\n        }\n\n        // Auto-focus on window apparition\n        ImGui::SetItemDefaultFocus();\n        if (reclaim_focus)\n            ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget\n\n        ImGui::End();\n    }\n\n    void    ExecCommand(const char* command_line)\n    {\n        AddLog(\"# %s\\n\", command_line);\n\n        // Insert into history. First find match and delete it so it can be pushed to the back.\n        // This isn't trying to be smart or optimal.\n        HistoryPos = -1;\n        for (int i = History.Size - 1; i >= 0; i--)\n            if (Stricmp(History[i], command_line) == 0)\n            {\n                ImGui::MemFree(History[i]);\n                History.erase(History.begin() + i);\n                break;\n            }\n        History.push_back(Strdup(command_line));\n\n        // Process command\n        if (Stricmp(command_line, \"CLEAR\") == 0)\n        {\n            ClearLog();\n        }\n        else if (Stricmp(command_line, \"HELP\") == 0)\n        {\n            AddLog(\"Commands:\");\n            for (int i = 0; i < Commands.Size; i++)\n                AddLog(\"- %s\", Commands[i]);\n        }\n        else if (Stricmp(command_line, \"HISTORY\") == 0)\n        {\n            int first = History.Size - 10;\n            for (int i = first > 0 ? first : 0; i < History.Size; i++)\n                AddLog(\"%3d: %s\\n\", i, History[i]);\n        }\n        else\n        {\n            AddLog(\"Unknown command: '%s'\\n\", command_line);\n        }\n\n        // On command input, we scroll to bottom even if AutoScroll==false\n        ScrollToBottom = true;\n    }\n\n    // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks\n    static int TextEditCallbackStub(ImGuiInputTextCallbackData* data)\n    {\n        ExampleAppConsole* console = (ExampleAppConsole*)data->UserData;\n        return console->TextEditCallback(data);\n    }\n\n    int     TextEditCallback(ImGuiInputTextCallbackData* data)\n    {\n        //AddLog(\"cursor: %d, selection: %d-%d\", data->CursorPos, data->SelectionStart, data->SelectionEnd);\n        switch (data->EventFlag)\n        {\n        case ImGuiInputTextFlags_CallbackCompletion:\n            {\n                // Example of TEXT COMPLETION\n\n                // Locate beginning of current word\n                const char* word_end = data->Buf + data->CursorPos;\n                const char* word_start = word_end;\n                while (word_start > data->Buf)\n                {\n                    const char c = word_start[-1];\n                    if (c == ' ' || c == '\\t' || c == ',' || c == ';')\n                        break;\n                    word_start--;\n                }\n\n                // Build a list of candidates\n                ImVector<const char*> candidates;\n                for (int i = 0; i < Commands.Size; i++)\n                    if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0)\n                        candidates.push_back(Commands[i]);\n\n                if (candidates.Size == 0)\n                {\n                    // No match\n                    AddLog(\"No match for \\\"%.*s\\\"!\\n\", (int)(word_end - word_start), word_start);\n                }\n                else if (candidates.Size == 1)\n                {\n                    // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.\n                    data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));\n                    data->InsertChars(data->CursorPos, candidates[0]);\n                    data->InsertChars(data->CursorPos, \" \");\n                }\n                else\n                {\n                    // Multiple matches. Complete as much as we can..\n                    // So inputting \"C\"+Tab will complete to \"CL\" then display \"CLEAR\" and \"CLASSIFY\" as matches.\n                    int match_len = (int)(word_end - word_start);\n                    for (;;)\n                    {\n                        int c = 0;\n                        bool all_candidates_matches = true;\n                        for (int i = 0; i < candidates.Size && all_candidates_matches; i++)\n                            if (i == 0)\n                                c = toupper(candidates[i][match_len]);\n                            else if (c == 0 || c != toupper(candidates[i][match_len]))\n                                all_candidates_matches = false;\n                        if (!all_candidates_matches)\n                            break;\n                        match_len++;\n                    }\n\n                    if (match_len > 0)\n                    {\n                        data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));\n                        data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);\n                    }\n\n                    // List matches\n                    AddLog(\"Possible matches:\\n\");\n                    for (int i = 0; i < candidates.Size; i++)\n                        AddLog(\"- %s\\n\", candidates[i]);\n                }\n\n                break;\n            }\n        case ImGuiInputTextFlags_CallbackHistory:\n            {\n                // Example of HISTORY\n                const int prev_history_pos = HistoryPos;\n                if (data->EventKey == ImGuiKey_UpArrow)\n                {\n                    if (HistoryPos == -1)\n                        HistoryPos = History.Size - 1;\n                    else if (HistoryPos > 0)\n                        HistoryPos--;\n                }\n                else if (data->EventKey == ImGuiKey_DownArrow)\n                {\n                    if (HistoryPos != -1)\n                        if (++HistoryPos >= History.Size)\n                            HistoryPos = -1;\n                }\n\n                // A better implementation would preserve the data on the current input line along with cursor position.\n                if (prev_history_pos != HistoryPos)\n                {\n                    const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : \"\";\n                    data->DeleteChars(0, data->BufTextLen);\n                    data->InsertChars(0, history_str);\n                }\n            }\n        }\n        return 0;\n    }\n};\n\nstatic void ShowExampleAppConsole(bool* p_open)\n{\n    static ExampleAppConsole console;\n    console.Draw(\"Example: Console\", p_open);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Debug Log / ShowExampleAppLog()\n//-----------------------------------------------------------------------------\n\n// Usage:\n//  static ExampleAppLog my_log;\n//  my_log.AddLog(\"Hello %d world\\n\", 123);\n//  my_log.Draw(\"title\");\nstruct ExampleAppLog\n{\n    ImGuiTextBuffer     Buf;\n    ImGuiTextFilter     Filter;\n    ImVector<int>       LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.\n    bool                AutoScroll;  // Keep scrolling if already at the bottom.\n\n    ExampleAppLog()\n    {\n        AutoScroll = true;\n        Clear();\n    }\n\n    void    Clear()\n    {\n        Buf.clear();\n        LineOffsets.clear();\n        LineOffsets.push_back(0);\n    }\n\n    void    AddLog(const char* fmt, ...) IM_FMTARGS(2)\n    {\n        int old_size = Buf.size();\n        va_list args;\n        va_start(args, fmt);\n        Buf.appendfv(fmt, args);\n        va_end(args);\n        for (int new_size = Buf.size(); old_size < new_size; old_size++)\n            if (Buf[old_size] == '\\n')\n                LineOffsets.push_back(old_size + 1);\n    }\n\n    void    Draw(const char* title, bool* p_open = NULL)\n    {\n        if (!ImGui::Begin(title, p_open))\n        {\n            ImGui::End();\n            return;\n        }\n\n        // Options menu\n        if (ImGui::BeginPopup(\"Options\"))\n        {\n            ImGui::Checkbox(\"Auto-scroll\", &AutoScroll);\n            ImGui::EndPopup();\n        }\n\n        // Main window\n        if (ImGui::Button(\"Options\"))\n            ImGui::OpenPopup(\"Options\");\n        ImGui::SameLine();\n        bool clear = ImGui::Button(\"Clear\");\n        ImGui::SameLine();\n        bool copy = ImGui::Button(\"Copy\");\n        ImGui::SameLine();\n        Filter.Draw(\"Filter\", -100.0f);\n\n        ImGui::Separator();\n\n        if (ImGui::BeginChild(\"scrolling\", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar))\n        {\n            if (clear)\n                Clear();\n            if (copy)\n                ImGui::LogToClipboard();\n\n            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));\n            const char* buf = Buf.begin();\n            const char* buf_end = Buf.end();\n            if (Filter.IsActive())\n            {\n                // In this example we don't use the clipper when Filter is enabled.\n                // This is because we don't have random access to the result of our filter.\n                // A real application processing logs with ten of thousands of entries may want to store the result of\n                // search/filter.. especially if the filtering function is not trivial (e.g. reg-exp).\n                for (int line_no = 0; line_no < LineOffsets.Size; line_no++)\n                {\n                    const char* line_start = buf + LineOffsets[line_no];\n                    const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;\n                    if (Filter.PassFilter(line_start, line_end))\n                        ImGui::TextUnformatted(line_start, line_end);\n                }\n            }\n            else\n            {\n                // The simplest and easy way to display the entire buffer:\n                //   ImGui::TextUnformatted(buf_begin, buf_end);\n                // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward\n                // to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are\n                // within the visible area.\n                // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them\n                // on your side is recommended. Using ImGuiListClipper requires\n                // - A) random access into your data\n                // - B) items all being the  same height,\n                // both of which we can handle since we have an array pointing to the beginning of each line of text.\n                // When using the filter (in the block of code above) we don't have random access into the data to display\n                // anymore, which is why we don't use the clipper. Storing or skimming through the search result would make\n                // it possible (and would be recommended if you want to search through tens of thousands of entries).\n                ImGuiListClipper clipper;\n                clipper.Begin(LineOffsets.Size);\n                while (clipper.Step())\n                {\n                    for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)\n                    {\n                        const char* line_start = buf + LineOffsets[line_no];\n                        const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;\n                        ImGui::TextUnformatted(line_start, line_end);\n                    }\n                }\n                clipper.End();\n            }\n            ImGui::PopStyleVar();\n\n            // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.\n            // Using a scrollbar or mouse-wheel will take away from the bottom edge.\n            if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())\n                ImGui::SetScrollHereY(1.0f);\n        }\n        ImGui::EndChild();\n        ImGui::End();\n    }\n};\n\n// Demonstrate creating a simple log window with basic filtering.\nstatic void ShowExampleAppLog(bool* p_open)\n{\n    static ExampleAppLog log;\n\n    // For the demo: add a debug button _BEFORE_ the normal log window contents\n    // We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window.\n    // Most of the contents of the window will be added by the log.Draw() call.\n    ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);\n    ImGui::Begin(\"Example: Log\", p_open);\n    IMGUI_DEMO_MARKER(\"Examples/Log\");\n    if (ImGui::SmallButton(\"[Debug] Add 5 entries\"))\n    {\n        static int counter = 0;\n        const char* categories[3] = { \"info\", \"warn\", \"error\" };\n        const char* words[] = { \"Bumfuzzled\", \"Cattywampus\", \"Snickersnee\", \"Abibliophobia\", \"Absquatulate\", \"Nincompoop\", \"Pauciloquent\" };\n        for (int n = 0; n < 5; n++)\n        {\n            const char* category = categories[counter % IM_COUNTOF(categories)];\n            const char* word = words[counter % IM_COUNTOF(words)];\n            log.AddLog(\"[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\\n\",\n                ImGui::GetFrameCount(), category, ImGui::GetTime(), word);\n            counter++;\n        }\n    }\n    ImGui::End();\n\n    // Actually call in the regular Log helper (which will Begin() into the same window as we just did)\n    log.Draw(\"Example: Log\", p_open);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()\n//-----------------------------------------------------------------------------\n\n// Demonstrate create a window with multiple child windows.\nstatic void ShowExampleAppLayout(bool* p_open)\n{\n    ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver);\n    if (ImGui::Begin(\"Example: Simple layout\", p_open, ImGuiWindowFlags_MenuBar))\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Simple layout\");\n        if (ImGui::BeginMenuBar())\n        {\n            if (ImGui::BeginMenu(\"File\"))\n            {\n                if (ImGui::MenuItem(\"Close\", \"Ctrl+W\")) { *p_open = false; }\n                ImGui::EndMenu();\n            }\n            ImGui::EndMenuBar();\n        }\n\n        // Left\n        static int selected = 0;\n        {\n            ImGui::BeginChild(\"left pane\", ImVec2(150, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX);\n            for (int i = 0; i < 100; i++)\n            {\n                char label[128];\n                sprintf(label, \"MyObject %d\", i);\n                if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SelectOnNav))\n                    selected = i;\n            }\n            ImGui::EndChild();\n        }\n        ImGui::SameLine();\n\n        // Right\n        {\n            ImGui::BeginGroup();\n            ImGui::BeginChild(\"item view\", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us\n            ImGui::Text(\"MyObject: %d\", selected);\n            ImGui::Separator();\n            if (ImGui::BeginTabBar(\"##Tabs\", ImGuiTabBarFlags_None))\n            {\n                if (ImGui::BeginTabItem(\"Description\"))\n                {\n                    ImGui::TextWrapped(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \");\n                    ImGui::EndTabItem();\n                }\n                if (ImGui::BeginTabItem(\"Details\"))\n                {\n                    ImGui::Text(\"ID: 0123456789\");\n                    ImGui::EndTabItem();\n                }\n                ImGui::EndTabBar();\n            }\n            ImGui::EndChild();\n            if (ImGui::Button(\"Revert\")) {}\n            ImGui::SameLine();\n            if (ImGui::Button(\"Save\")) {}\n            ImGui::EndGroup();\n        }\n    }\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()\n//-----------------------------------------------------------------------------\n// Some of the interactions are a bit lack-luster:\n// - We would want pressing validating or leaving the filter to somehow restore focus.\n// - We may want more advanced filtering (child nodes) and clipper support: both will need extra work.\n// - We would want to customize some keyboard interactions to easily keyboard navigate between the tree and the properties.\n//-----------------------------------------------------------------------------\n\nstruct ExampleAppPropertyEditor\n{\n    ImGuiTextFilter     Filter;\n    ExampleTreeNode*    SelectedNode = NULL;\n    bool                UseClipper = false;\n\n    void Draw(ExampleTreeNode* root_node)\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Property editor\");\n\n        // Left side: draw tree\n        // - Currently using a table to benefit from RowBg feature\n        // - Our tree node are all of equal height, facilitating the use of a clipper.\n        if (ImGui::BeginChild(\"##tree\", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened))\n        {\n            ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);\n            ImGui::Checkbox(\"Use Clipper\", &UseClipper);\n            ImGui::SameLine();\n            ImGui::Text(\"(%d root nodes)\", root_node->Childs.Size);\n            ImGui::SetNextItemWidth(-FLT_MIN);\n            ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);\n            if (ImGui::InputTextWithHint(\"##Filter\", \"incl,-excl\", Filter.InputBuf, IM_COUNTOF(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll))\n                Filter.Build();\n            ImGui::PopItemFlag();\n\n            if (ImGui::BeginTable(\"##list\", 1, ImGuiTableFlags_RowBg))\n            {\n                if (UseClipper)\n                    DrawClippedTree(root_node);\n                else\n                    DrawTree(root_node);\n                ImGui::EndTable();\n            }\n        }\n        ImGui::EndChild();\n\n        // Right side: draw properties\n        ImGui::SameLine();\n\n        ImGui::BeginGroup(); // Lock X position\n        if (ExampleTreeNode* node = SelectedNode)\n        {\n            ImGui::Text(\"%s\", node->Name);\n            ImGui::TextDisabled(\"UID: 0x%08X\", node->UID);\n            ImGui::Separator();\n            if (ImGui::BeginTable(\"##properties\", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY))\n            {\n                // Push object ID after we entered the table, so table is shared for all objects\n                ImGui::PushID((int)node->UID);\n                ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthFixed);\n                ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger\n                if (node->HasData)\n                {\n                    // In a typical application, the structure description would be derived from a data-driven system.\n                    // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array.\n                    // - Limits and some details are hard-coded to simplify the demo.\n                    for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos)\n                    {\n                        ImGui::TableNextRow();\n                        ImGui::PushID(field_desc.Name);\n                        ImGui::TableNextColumn();\n                        ImGui::AlignTextToFramePadding();\n                        ImGui::TextUnformatted(field_desc.Name);\n                        ImGui::TableNextColumn();\n                        void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset);\n                        switch (field_desc.DataType)\n                        {\n                        case ImGuiDataType_Bool:\n                        {\n                            IM_ASSERT(field_desc.DataCount == 1);\n                            ImGui::Checkbox(\"##Editor\", (bool*)field_ptr);\n                            break;\n                        }\n                        case ImGuiDataType_S32:\n                        {\n                            int v_min = INT_MIN, v_max = INT_MAX;\n                            ImGui::SetNextItemWidth(-FLT_MIN);\n                            ImGui::DragScalarN(\"##Editor\", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max);\n                            break;\n                        }\n                        case ImGuiDataType_Float:\n                        {\n                            float v_min = 0.0f, v_max = 1.0f;\n                            ImGui::SetNextItemWidth(-FLT_MIN);\n                            ImGui::SliderScalarN(\"##Editor\", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max);\n                            break;\n                        }\n                        case ImGuiDataType_String:\n                        {\n                            ImGui::InputText(\"##Editor\", reinterpret_cast<char*>(field_ptr), 28);\n                            break;\n                        }\n                        }\n                        ImGui::PopID();\n                    }\n                }\n                ImGui::PopID();\n                ImGui::EndTable();\n            }\n        }\n        ImGui::EndGroup();\n    }\n\n    // Custom search filter\n    // - Here we apply on root node only.\n    // - This does a case insensitive stristr which is pretty heavy. In a real large-scale app you would likely store a filtered list which in turns would be trivial to linearize.\n    inline bool IsNodePassingFilter(ExampleTreeNode* node)\n    {\n        return node->Parent->Parent != NULL || Filter.PassFilter(node->Name);\n    }\n\n    // Basic version, recursive. This is how you would generally draw a tree.\n    // - Simple but going to be noticeably costly if you have a large amount of nodes as DrawTreeNode() is called for all of them.\n    // - On my desktop PC (2020), for 10K nodes in an optimized build this takes ~1.2 ms\n    // - Unlike arrays or grids which are very easy to clip, trees are currently more difficult to clip.\n    void DrawTree(ExampleTreeNode* node)\n    {\n        for (ExampleTreeNode* child : node->Childs)\n            if (IsNodePassingFilter(child) && DrawTreeNode(child))\n            {\n                DrawTree(child);\n                ImGui::TreePop();\n            }\n    }\n\n    // More advanced version. Use a alternative clipping technique: fast-forwarding through non-visible chunks.\n    // - On my desktop PC (2020), for 10K nodes in an optimized build this takes ~0.1 ms\n    //   (in ExampleTree_CreateDemoTree(), change 'int ROOT_ITEMS_COUNT = 10000' to try with this amount of root nodes).\n    // - 1. Use clipper with indeterminate count (items_count = INT_MAX): we need to call SeekCursorForItem() at the end once we know the count.\n    // - 2. Use SetNextItemStorageID() to specify ID used for open/close storage, making it easy to call TreeNodeGetOpen() on any arbitrary node.\n    // - 3. Linearize tree during traversal: our tree data structure makes it easy to access sibling and parents.\n    // - Unlike clipping for a regular array or grid which may be done using random access limited to visible areas,\n    //   this technique requires traversing most accessible nodes. This could be made more optimal with extra work,\n    //   but this is a decent simplicity<>speed trade-off.\n    // See https://github.com/ocornut/imgui/issues/3823 for discussions about this.\n    void DrawClippedTree(ExampleTreeNode* root_node)\n    {\n        ExampleTreeNode* node = root_node->Childs[0]; // First node\n        ImGuiListClipper clipper;\n        clipper.Begin(INT_MAX);\n        while (clipper.Step())\n            while (clipper.UserIndex < clipper.DisplayEnd && node != NULL)\n                node = DrawClippedTreeNodeAndAdvanceToNext(&clipper, node);\n\n        // Keep going to count nodes and submit final count so we have a reliable scrollbar.\n        // - One could consider caching this value and only refreshing it occasionally e.g. window is focused and an action occurs.\n        // - Incorrect but cheap approximation would be to use 'clipper_current_idx = IM_MAX(clipper_current_idx, root_node->Childs.Size)' instead.\n        // - If either of those is implemented, the general cost will approach zero when scrolling is at the top of the tree.\n        while (node != NULL)\n            node = DrawClippedTreeNodeAndAdvanceToNext(&clipper, node);\n        //clipper.UserIndex = IM_MAX(clipper.UserIndex, root_node->Childs.Size); // <-- Cheap approximation instead of while() loop above.\n        clipper.SeekCursorForItem(clipper.UserIndex);\n    }\n\n    ExampleTreeNode* DrawClippedTreeNodeAndAdvanceToNext(ImGuiListClipper* clipper, ExampleTreeNode* node)\n    {\n        if (IsNodePassingFilter(node))\n        {\n            // Draw node if within visible range\n            bool is_open = false;\n            if (clipper->UserIndex >= clipper->DisplayStart && clipper->UserIndex < clipper->DisplayEnd)\n            {\n                is_open = DrawTreeNode(node);\n            }\n            else\n            {\n                is_open = (node->Childs.Size > 0 && ImGui::TreeNodeGetOpen((ImGuiID)node->UID));\n                if (is_open)\n                    ImGui::TreePush(node->Name);\n            }\n            clipper->UserIndex++;\n\n            // Next node: recurse into childs\n            if (is_open)\n                return node->Childs[0];\n        }\n\n        // Next node: next sibling, otherwise move back to parent\n        while (node != NULL)\n        {\n            if (node->IndexInParent + 1 < node->Parent->Childs.Size)\n                return node->Parent->Childs[node->IndexInParent + 1];\n            node = node->Parent;\n            if (node->Parent == NULL)\n                break;\n            ImGui::TreePop();\n        }\n        return NULL;\n    }\n\n    // To support node with same name we incorporate node->UID into the item ID.\n    // (this would more naturally be done using PushID(node->UID) + TreeNodeEx(node->Name, tree_flags),\n    //   but it would require in DrawClippedTreeNodeAndAdvanceToNext() to add PushID() before TreePush(), and PopID() after TreePop(),\n    //   so instead we use TreeNodeEx(node->UID, tree_flags, \"%s\", node->Name) here)\n    bool DrawTreeNode(ExampleTreeNode* node)\n    {\n        ImGui::TableNextRow();\n        ImGui::TableNextColumn();\n        ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None;\n        tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards\n        tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent;  // Left arrow support\n        tree_flags |= ImGuiTreeNodeFlags_SpanFullWidth;         // Span full width for easier mouse reach\n        tree_flags |= ImGuiTreeNodeFlags_DrawLinesToNodes;      // Always draw hierarchy outlines\n        if (node == SelectedNode)\n            tree_flags |= ImGuiTreeNodeFlags_Selected;          // Draw selection highlight\n        if (node->Childs.Size == 0)\n            tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen; // Use _NoTreePushOnOpen + set is_open=false to avoid unnecessarily push/pop on leaves.\n        if (node->DataMyBool == false)\n            ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);\n        ImGui::SetNextItemStorageID((ImGuiID)node->UID);        // Use node->UID as storage id\n        bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node->UID, tree_flags, \"%s\", node->Name);\n        if (node->Childs.Size == 0)\n            is_open = false;\n        if (node->DataMyBool == false)\n            ImGui::PopStyleColor();\n        if (ImGui::IsItemFocused())\n            SelectedNode = node;\n        return is_open;\n    }\n};\n\n// Demonstrate creating a simple property editor.\nstatic void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data)\n{\n    ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver);\n    if (!ImGui::Begin(\"Example: Property editor\", p_open))\n    {\n        ImGui::End();\n        return;\n    }\n\n    IMGUI_DEMO_MARKER(\"Examples/Property Editor\");\n    static ExampleAppPropertyEditor property_editor;\n    if (demo_data->DemoTree == NULL)\n        demo_data->DemoTree = ExampleTree_CreateDemoTree();\n    property_editor.Draw(demo_data->DemoTree);\n\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Long Text / ShowExampleAppLongText()\n//-----------------------------------------------------------------------------\n\n// Demonstrate/test rendering huge amount of text, and the incidence of clipping.\nstatic void ShowExampleAppLongText(bool* p_open)\n{\n    ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);\n    if (!ImGui::Begin(\"Example: Long text display\", p_open))\n    {\n        ImGui::End();\n        return;\n    }\n    IMGUI_DEMO_MARKER(\"Examples/Long text display\");\n\n    static int test_type = 0;\n    static ImGuiTextBuffer log;\n    static int lines = 0;\n    ImGui::Text(\"Printing unusually long amount of text.\");\n    ImGui::Combo(\"Test type\", &test_type,\n        \"Single call to TextUnformatted()\\0\"\n        \"Multiple calls to Text(), clipped\\0\"\n        \"Multiple calls to Text(), not clipped (slow)\\0\");\n    ImGui::Text(\"Buffer contents: %d lines, %d bytes\", lines, log.size());\n    if (ImGui::Button(\"Clear\")) { log.clear(); lines = 0; }\n    ImGui::SameLine();\n    if (ImGui::Button(\"Add 1000 lines\"))\n    {\n        for (int i = 0; i < 1000; i++)\n            log.appendf(\"%i The quick brown fox jumps over the lazy dog\\n\", lines + i);\n        lines += 1000;\n    }\n    ImGui::BeginChild(\"Log\");\n    switch (test_type)\n    {\n    case 0:\n        // Single call to TextUnformatted() with a big buffer\n        ImGui::TextUnformatted(log.begin(), log.end());\n        break;\n    case 1:\n        {\n            // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.\n            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));\n            ImGuiListClipper clipper;\n            clipper.Begin(lines);\n            while (clipper.Step())\n                for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)\n                    ImGui::Text(\"%i The quick brown fox jumps over the lazy dog\", i);\n            ImGui::PopStyleVar();\n            break;\n        }\n    case 2:\n        // Multiple calls to Text(), not clipped (slow)\n        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));\n        for (int i = 0; i < lines; i++)\n            ImGui::Text(\"%i The quick brown fox jumps over the lazy dog\", i);\n        ImGui::PopStyleVar();\n        break;\n    }\n    ImGui::EndChild();\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a window which gets auto-resized according to its content.\nstatic void ShowExampleAppAutoResize(bool* p_open)\n{\n    if (!ImGui::Begin(\"Example: Auto-resizing window\", p_open, ImGuiWindowFlags_AlwaysAutoResize))\n    {\n        ImGui::End();\n        return;\n    }\n    IMGUI_DEMO_MARKER(\"Examples/Auto-resizing window\");\n\n    static int lines = 10;\n    ImGui::TextUnformatted(\n        \"Window will resize every-frame to the size of its content.\\n\"\n        \"Note that you probably don't want to query the window size to\\n\"\n        \"output your content because that would create a feedback loop.\");\n    ImGui::SliderInt(\"Number of lines\", &lines, 1, 20);\n    for (int i = 0; i < lines; i++)\n        ImGui::Text(\"%*sThis is line %d\", i * 4, \"\", i); // Pad with space to extend size horizontally\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a window with custom resize constraints.\n// Note that size constraints currently don't work on a docked window (when in 'docking' branch)\nstatic void ShowExampleAppConstrainedResize(bool* p_open)\n{\n    struct CustomConstraints\n    {\n        // Helper functions to demonstrate programmatic constraints\n        // FIXME: This doesn't take account of decoration size (e.g. title bar), library should make this easier.\n        // FIXME: None of the three demos works consistently when resizing from borders.\n        static void AspectRatio(ImGuiSizeCallbackData* data)\n        {\n            float aspect_ratio = *(float*)data->UserData;\n            data->DesiredSize.y = (float)(int)(data->DesiredSize.x / aspect_ratio);\n        }\n        static void Square(ImGuiSizeCallbackData* data)\n        {\n            data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y);\n        }\n        static void Step(ImGuiSizeCallbackData* data)\n        {\n            float step = *(float*)data->UserData;\n            data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step);\n        }\n    };\n\n    const char* test_desc[] =\n    {\n        \"Between 100x100 and 500x500\",\n        \"At least 100x100\",\n        \"Resize vertical + lock current width\",\n        \"Resize horizontal + lock current height\",\n        \"Width Between 400 and 500\",\n        \"Height at least 400\",\n        \"Custom: Aspect Ratio 16:9\",\n        \"Custom: Always Square\",\n        \"Custom: Fixed Steps (100)\",\n    };\n\n    // Options\n    static bool auto_resize = false;\n    static bool window_padding = true;\n    static int type = 6; // Aspect Ratio\n    static int display_lines = 10;\n\n    // Submit constraint\n    float aspect_ratio = 16.0f / 9.0f;\n    float fixed_step = 100.0f;\n    if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(500, 500));         // Between 100x100 and 500x500\n    if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100\n    if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0),    ImVec2(-1, FLT_MAX));      // Resize vertical + lock current width\n    if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1),    ImVec2(FLT_MAX, -1));      // Resize horizontal + lock current height\n    if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1),  ImVec2(500, -1));          // Width Between and 400 and 500\n    if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400),  ImVec2(-1, FLT_MAX));      // Height at least 400\n    if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0),     ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::AspectRatio, (void*)&aspect_ratio);   // Aspect ratio\n    if (type == 7) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0),     ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square);                              // Always Square\n    if (type == 8) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0),     ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)&fixed_step);            // Fixed Step\n\n    // Submit window\n    if (!window_padding)\n        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));\n    const ImGuiWindowFlags window_flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0;\n    const bool window_open = ImGui::Begin(\"Example: Constrained Resize\", p_open, window_flags);\n    if (!window_padding)\n        ImGui::PopStyleVar();\n    IMGUI_DEMO_MARKER(\"Examples/Constrained Resizing window\");\n    if (window_open)\n    {\n        if (ImGui::GetIO().KeyShift)\n        {\n            // Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture)\n            ImVec2 avail_size = ImGui::GetContentRegionAvail();\n            ImVec2 pos = ImGui::GetCursorScreenPos();\n            ImGui::ColorButton(\"viewport\", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size);\n            ImGui::SetCursorScreenPos(ImVec2(pos.x + 10, pos.y + 10));\n            ImGui::Text(\"%.2f x %.2f\", avail_size.x, avail_size.y);\n        }\n        else\n        {\n            ImGui::Text(\"(Hold Shift to display a dummy viewport)\");\n            if (ImGui::IsWindowDocked())\n                ImGui::Text(\"Warning: Sizing Constraints won't work if the window is docked!\");\n            if (ImGui::Button(\"Set 200x200\")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine();\n            if (ImGui::Button(\"Set 500x500\")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine();\n            if (ImGui::Button(\"Set 800x200\")) { ImGui::SetWindowSize(ImVec2(800, 200)); }\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);\n            ImGui::Combo(\"Constraint\", &type, test_desc, IM_COUNTOF(test_desc));\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);\n            ImGui::DragInt(\"Lines\", &display_lines, 0.2f, 1, 100);\n            ImGui::Checkbox(\"Auto-resize\", &auto_resize);\n            ImGui::Checkbox(\"Window padding\", &window_padding);\n            for (int i = 0; i < display_lines; i++)\n                ImGui::Text(\"%*sHello, sailor! Making this line long enough for the example.\", i * 4, \"\");\n        }\n    }\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a simple static window with no decoration\n// + a context-menu to choose which corner of the screen to use.\nstatic void ShowExampleAppSimpleOverlay(bool* p_open)\n{\n    static int location = 0;\n    ImGuiIO& io = ImGui::GetIO();\n    ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;\n    if (location >= 0)\n    {\n        const float PAD = 10.0f;\n        const ImGuiViewport* viewport = ImGui::GetMainViewport();\n        ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any!\n        ImVec2 work_size = viewport->WorkSize;\n        ImVec2 window_pos, window_pos_pivot;\n        window_pos.x = (location & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD);\n        window_pos.y = (location & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD);\n        window_pos_pivot.x = (location & 1) ? 1.0f : 0.0f;\n        window_pos_pivot.y = (location & 2) ? 1.0f : 0.0f;\n        ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);\n        ImGui::SetNextWindowViewport(viewport->ID);\n        window_flags |= ImGuiWindowFlags_NoMove;\n    }\n    else if (location == -2)\n    {\n        // Center window\n        ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));\n        window_flags |= ImGuiWindowFlags_NoMove;\n    }\n    ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background\n    if (ImGui::Begin(\"Example: Simple overlay\", p_open, window_flags))\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Simple overlay\"); // Scroll up to the beginning of this function to see overlay flags\n        ImGui::Text(\"Simple overlay\\n\" \"(right-click to change position)\");\n        ImGui::Separator();\n        if (ImGui::IsMousePosValid())\n            ImGui::Text(\"Mouse Position: (%.1f,%.1f)\", io.MousePos.x, io.MousePos.y);\n        else\n            ImGui::Text(\"Mouse Position: <invalid>\");\n        if (ImGui::BeginPopupContextWindow())\n        {\n            if (ImGui::MenuItem(\"Custom\",       NULL, location == -1)) location = -1;\n            if (ImGui::MenuItem(\"Center\",       NULL, location == -2)) location = -2;\n            if (ImGui::MenuItem(\"Top-left\",     NULL, location == 0)) location = 0;\n            if (ImGui::MenuItem(\"Top-right\",    NULL, location == 1)) location = 1;\n            if (ImGui::MenuItem(\"Bottom-left\",  NULL, location == 2)) location = 2;\n            if (ImGui::MenuItem(\"Bottom-right\", NULL, location == 3)) location = 3;\n            if (p_open && ImGui::MenuItem(\"Close\")) *p_open = false;\n            ImGui::EndPopup();\n        }\n    }\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a window covering the entire screen/viewport\nstatic void ShowExampleAppFullscreen(bool* p_open)\n{\n    static bool use_work_area = true;\n    static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;\n\n    // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.)\n    // Based on your use case you may want one or the other.\n    const ImGuiViewport* viewport = ImGui::GetMainViewport();\n    ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);\n    ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);\n\n    if (ImGui::Begin(\"Example: Fullscreen window\", p_open, flags))\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Fullscreen window\");\n        ImGui::Checkbox(\"Use work area instead of main area\", &use_work_area);\n        ImGui::SameLine();\n        HelpMarker(\"Main Area = entire viewport,\\nWork Area = entire viewport minus sections used by the main menu bars, task bars etc.\\n\\nEnable the main-menu bar in Examples menu to see the difference.\");\n\n        ImGui::CheckboxFlags(\"ImGuiWindowFlags_NoBackground\", &flags, ImGuiWindowFlags_NoBackground);\n        ImGui::CheckboxFlags(\"ImGuiWindowFlags_NoDecoration\", &flags, ImGuiWindowFlags_NoDecoration);\n        ImGui::Indent();\n        ImGui::CheckboxFlags(\"ImGuiWindowFlags_NoTitleBar\", &flags, ImGuiWindowFlags_NoTitleBar);\n        ImGui::CheckboxFlags(\"ImGuiWindowFlags_NoCollapse\", &flags, ImGuiWindowFlags_NoCollapse);\n        ImGui::CheckboxFlags(\"ImGuiWindowFlags_NoScrollbar\", &flags, ImGuiWindowFlags_NoScrollbar);\n        ImGui::Unindent();\n\n        if (p_open && ImGui::Button(\"Close this window\"))\n            *p_open = false;\n    }\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles()\n//-----------------------------------------------------------------------------\n\n// Demonstrate the use of \"##\" and \"###\" in identifiers to manipulate ID generation.\n// This applies to all regular items as well.\n// Read FAQ section \"How can I have multiple widgets with the same label?\" for details.\nstatic void ShowExampleAppWindowTitles(bool*)\n{\n    const ImGuiViewport* viewport = ImGui::GetMainViewport();\n    const ImVec2 base_pos = viewport->Pos;\n\n    // By default, Windows are uniquely identified by their title.\n    // You can use the \"##\" and \"###\" markers to manipulate the display/ID.\n\n    // Using \"##\" to display same title but have unique identifier.\n    ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver);\n    ImGui::Begin(\"Same title as another window##1\");\n    IMGUI_DEMO_MARKER(\"Examples/Manipulating window titles##1\");\n    ImGui::Text(\"This is window 1.\\nMy title is the same as window 2, but my identifier is unique.\");\n    ImGui::End();\n\n    ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), ImGuiCond_FirstUseEver);\n    ImGui::Begin(\"Same title as another window##2\");\n    IMGUI_DEMO_MARKER(\"Examples/Manipulating window titles##2\");\n    ImGui::Text(\"This is window 2.\\nMy title is the same as window 1, but my identifier is unique.\");\n    ImGui::End();\n\n    // Using \"###\" to display a changing title but keep a static identifier \"AnimatedTitle\"\n    char buf[128];\n    sprintf(buf, \"Animated title %c %d###AnimatedTitle\", \"|/-\\\\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount());\n    ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), ImGuiCond_FirstUseEver);\n    ImGui::Begin(buf);\n    IMGUI_DEMO_MARKER(\"Examples/Manipulating window titles##3\");\n    ImGui::Text(\"This window has a changing title.\");\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()\n//-----------------------------------------------------------------------------\n\n// Add a |_| looking shape\nstatic void PathConcaveShape(ImDrawList* draw_list, float x, float y, float sz)\n{\n    const ImVec2 pos_norms[] = { { 0.0f, 0.0f }, { 0.3f, 0.0f }, { 0.3f, 0.7f }, { 0.7f, 0.7f }, { 0.7f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } };\n    for (const ImVec2& p : pos_norms)\n        draw_list->PathLineTo(ImVec2(x + 0.5f + (int)(sz * p.x), y + 0.5f + (int)(sz * p.y)));\n}\n\n// Demonstrate using the low-level ImDrawList to draw custom shapes.\nstatic void ShowExampleAppCustomRendering(bool* p_open)\n{\n    if (!ImGui::Begin(\"Example: Custom rendering\", p_open))\n    {\n        ImGui::End();\n        return;\n    }\n    IMGUI_DEMO_MARKER(\"Examples/Custom rendering\");\n\n    // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of\n    // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your\n    // types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not\n    // exposed outside (to avoid messing with your types) In this example we are not using the maths operators!\n\n    if (ImGui::BeginTabBar(\"##TabBar\"))\n    {\n        if (ImGui::BeginTabItem(\"Primitives\"))\n        {\n            IMGUI_DEMO_MARKER(\"Examples/Custom rendering/Primitives\");\n            ImGui::PushItemWidth(-ImGui::GetFontSize() * 15);\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n\n            // Draw gradients\n            // (note that those are currently exacerbating our sRGB/Linear issues)\n            // Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well..\n            ImGui::Text(\"Gradients\");\n            ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight());\n            {\n                ImVec2 p0 = ImGui::GetCursorScreenPos();\n                ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);\n                ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255));\n                ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255));\n                draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);\n                ImGui::InvisibleButton(\"##gradient1\", gradient_size);\n            }\n            {\n                ImVec2 p0 = ImGui::GetCursorScreenPos();\n                ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);\n                ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255));\n                ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255));\n                draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);\n                ImGui::InvisibleButton(\"##gradient2\", gradient_size);\n            }\n\n            // Draw a bunch of primitives\n            ImGui::Text(\"All primitives\");\n            static float sz = 36.0f;\n            static float thickness = 3.0f;\n            static int ngon_sides = 6;\n            static bool circle_segments_override = false;\n            static int circle_segments_override_v = 12;\n            static bool curve_segments_override = false;\n            static int curve_segments_override_v = 8;\n            static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f);\n            ImGui::DragFloat(\"Size\", &sz, 0.2f, 2.0f, 100.0f, \"%.0f\");\n            ImGui::DragFloat(\"Thickness\", &thickness, 0.05f, 1.0f, 8.0f, \"%.02f\");\n            ImGui::SliderInt(\"N-gon sides\", &ngon_sides, 3, 12);\n            ImGui::Checkbox(\"##circlesegmentoverride\", &circle_segments_override);\n            ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);\n            circle_segments_override |= ImGui::SliderInt(\"Circle segments override\", &circle_segments_override_v, 3, 40);\n            ImGui::Checkbox(\"##curvessegmentoverride\", &curve_segments_override);\n            ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);\n            curve_segments_override |= ImGui::SliderInt(\"Curves segments override\", &curve_segments_override_v, 3, 40);\n            ImGui::ColorEdit4(\"Color\", &colf.x);\n\n            const ImVec2 p = ImGui::GetCursorScreenPos();\n            const ImU32 col = ImColor(colf);\n            const float spacing = 10.0f;\n            const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight;\n            const float rounding = sz / 5.0f;\n            const int circle_segments = circle_segments_override ? circle_segments_override_v : 0;\n            const int curve_segments = curve_segments_override ? curve_segments_override_v : 0;\n            const ImVec2 cp3[3] = { ImVec2(0.0f, sz * 0.6f), ImVec2(sz * 0.5f, -sz * 0.4f), ImVec2(sz, sz) }; // Control points for curves\n            const ImVec2 cp4[4] = { ImVec2(0.0f, 0.0f), ImVec2(sz * 1.3f, sz * 0.3f), ImVec2(sz - sz * 1.3f, sz - sz * 0.3f), ImVec2(sz, sz) };\n\n            float x = p.x + 4.0f;\n            float y = p.y + 4.0f;\n            for (int n = 0; n < 2; n++)\n            {\n                // First line uses a thickness of 1.0f, second line uses the configurable thickness\n                float th = (n == 0) ? 1.0f : thickness;\n                draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th);                 x += sz + spacing;  // N-gon\n                draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th);          x += sz + spacing;  // Circle\n                draw_list->AddEllipse(ImVec2(x + sz*0.5f, y + sz*0.5f), ImVec2(sz*0.5f, sz*0.3f), col, -0.3f, circle_segments, th); x += sz + spacing;  // Ellipse\n                draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th);          x += sz + spacing;  // Square\n                draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th);      x += sz + spacing;  // Square with all rounded corners\n                draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br, th);         x += sz + spacing;  // Square with two rounded corners\n                draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing;  // Triangle\n                //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle\n                PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, ImDrawFlags_Closed, th);          x += sz + spacing;  // Concave Shape\n                //draw_list->AddPolyline(concave_shape, IM_COUNTOF(concave_shape), col, ImDrawFlags_Closed, th);\n                draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th);                                       x += sz + spacing;  // Horizontal line (note: drawing a filled rectangle will be faster!)\n                draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th);                                       x += spacing;       // Vertical line (note: drawing a filled rectangle will be faster!)\n                draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th);                                  x += sz + spacing;  // Diagonal line\n\n                // Path\n                draw_list->PathArcTo(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, 3.141592f, 3.141592f * -0.5f);\n                draw_list->PathStroke(col, ImDrawFlags_None, th);\n                x += sz + spacing;\n\n                // Quadratic Bezier Curve (3 control points)\n                draw_list->AddBezierQuadratic(ImVec2(x + cp3[0].x, y + cp3[0].y), ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), col, th, curve_segments);\n                x += sz + spacing;\n\n                // Cubic Bezier Curve (4 control points)\n                draw_list->AddBezierCubic(ImVec2(x + cp4[0].x, y + cp4[0].y), ImVec2(x + cp4[1].x, y + cp4[1].y), ImVec2(x + cp4[2].x, y + cp4[2].y), ImVec2(x + cp4[3].x, y + cp4[3].y), col, th, curve_segments);\n\n                x = p.x + 4;\n                y += sz + spacing;\n            }\n\n            // Filled shapes\n            draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, ngon_sides);             x += sz + spacing;  // N-gon\n            draw_list->AddCircleFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, circle_segments);      x += sz + spacing;  // Circle\n            draw_list->AddEllipseFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), ImVec2(sz * 0.5f, sz * 0.3f), col, -0.3f, circle_segments); x += sz + spacing;// Ellipse\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col);                                    x += sz + spacing;  // Square\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f);                             x += sz + spacing;  // Square with all rounded corners\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br);              x += sz + spacing;  // Square with two rounded corners\n            draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col);  x += sz + spacing;  // Triangle\n            //draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle\n            PathConcaveShape(draw_list, x, y, sz); draw_list->PathFillConcave(col);                                 x += sz + spacing;  // Concave shape\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col);                             x += sz + spacing;  // Horizontal line (faster than AddLine, but only handle integer thickness)\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col);                             x += spacing * 2.0f;// Vertical line (faster than AddLine, but only handle integer thickness)\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col);                                      x += sz;            // Pixel (faster than AddLine)\n\n            // Path\n            draw_list->PathArcTo(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, 3.141592f * -0.5f, 3.141592f);\n            draw_list->PathFillConvex(col);\n            x += sz + spacing;\n\n            // Quadratic Bezier Curve (3 control points)\n            draw_list->PathLineTo(ImVec2(x + cp3[0].x, y + cp3[0].y));\n            draw_list->PathBezierQuadraticCurveTo(ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), curve_segments);\n            draw_list->PathFillConvex(col);\n            x += sz + spacing;\n\n            draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255));\n            x += sz + spacing;\n\n            ImGui::Dummy(ImVec2((sz + spacing) * 13.2f, (sz + spacing) * 3.0f));\n            ImGui::PopItemWidth();\n            ImGui::EndTabItem();\n        }\n\n        if (ImGui::BeginTabItem(\"Canvas\"))\n        {\n            IMGUI_DEMO_MARKER(\"Examples/Custom rendering/Canvas\");\n            static ImVector<ImVec2> points;\n            static ImVec2 scrolling(0.0f, 0.0f);\n            static bool opt_enable_grid = true;\n            static bool opt_enable_context_menu = true;\n            static bool adding_line = false;\n\n            ImGui::Checkbox(\"Enable grid\", &opt_enable_grid);\n            ImGui::Checkbox(\"Enable context menu\", &opt_enable_context_menu);\n            ImGui::Text(\"Mouse Left: drag to add lines,\\nMouse Right: drag to scroll, click for context menu.\");\n\n            // Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling.\n            // Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls.\n            // To use a child window instead we could use, e.g:\n            //      ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));      // Disable padding\n            //      ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255));  // Set a background color\n            //      ImGui::BeginChild(\"canvas\", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove);\n            //      ImGui::PopStyleColor();\n            //      ImGui::PopStyleVar();\n            //      [...]\n            //      ImGui::EndChild();\n\n            // Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive()\n            ImVec2 canvas_p0 = ImGui::GetCursorScreenPos();      // ImDrawList API uses screen coordinates!\n            ImVec2 canvas_sz = ImGui::GetContentRegionAvail();   // Resize canvas to what's available\n            if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f;\n            if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f;\n            ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);\n\n            // Draw border and background color\n            ImGuiIO& io = ImGui::GetIO();\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n            draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255));\n            draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255));\n\n            // This will catch our interactions\n            ImGui::InvisibleButton(\"canvas\", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);\n            const bool is_hovered = ImGui::IsItemHovered(); // Hovered\n            const bool is_active = ImGui::IsItemActive();   // Held\n            const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin\n            const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y);\n\n            // Add first and second point\n            if (is_hovered && !adding_line && ImGui::IsMouseClicked(ImGuiMouseButton_Left))\n            {\n                points.push_back(mouse_pos_in_canvas);\n                points.push_back(mouse_pos_in_canvas);\n                adding_line = true;\n            }\n            if (adding_line)\n            {\n                points.back() = mouse_pos_in_canvas;\n                if (!ImGui::IsMouseDown(ImGuiMouseButton_Left))\n                    adding_line = false;\n            }\n\n            // Pan (we use a zero mouse threshold when there's no context menu)\n            // You may decide to make that threshold dynamic based on whether the mouse is hovering something etc.\n            const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f;\n            if (is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan))\n            {\n                scrolling.x += io.MouseDelta.x;\n                scrolling.y += io.MouseDelta.y;\n            }\n\n            // Context menu (under default mouse threshold)\n            ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);\n            if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f)\n                ImGui::OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n            if (ImGui::BeginPopup(\"context\"))\n            {\n                if (adding_line)\n                    points.resize(points.size() - 2);\n                adding_line = false;\n                if (ImGui::MenuItem(\"Remove one\", NULL, false, points.Size > 0)) { points.resize(points.size() - 2); }\n                if (ImGui::MenuItem(\"Remove all\", NULL, false, points.Size > 0)) { points.clear(); }\n                ImGui::EndPopup();\n            }\n\n            // Draw grid + all lines in the canvas\n            draw_list->PushClipRect(canvas_p0, canvas_p1, true);\n            if (opt_enable_grid)\n            {\n                const float GRID_STEP = 64.0f;\n                for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP)\n                    draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40));\n                for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP)\n                    draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40));\n            }\n            for (int n = 0; n < points.Size; n += 2)\n                draw_list->AddLine(ImVec2(origin.x + points[n].x, origin.y + points[n].y), ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), 2.0f);\n            draw_list->PopClipRect();\n\n            ImGui::EndTabItem();\n        }\n\n        if (ImGui::BeginTabItem(\"Shadows\"))\n        {\n            static float shadow_thickness = 40.0f;\n            static ImVec4 shadow_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);\n            static bool shadow_filled = false;\n            static ImVec4 shape_color = ImVec4(0.9f, 0.6f, 0.3f, 1.0f);\n            static float shape_rounding = 0.0f;\n            static ImVec2 shadow_offset(0.0f, 0.0f);\n            static ImVec4 background_color = ImVec4(0.5f, 0.5f, 0.7f, 1.0f);\n            static bool wireframe = false;\n            static bool aa = true;\n            static int poly_shape_index = 0;\n            ImGui::Checkbox(\"Shadow filled\", &shadow_filled);\n            ImGui::SameLine();\n            HelpMarker(\"This will fill the section behind the shape to shadow. It's often unnecessary and wasteful but provided for consistency.\");\n            ImGui::Checkbox(\"Wireframe shapes\", &wireframe);\n            ImGui::SameLine();\n            HelpMarker(\"This draws the shapes in wireframe so you can see the shadow underneath.\");\n            ImGui::Checkbox(\"Anti-aliasing\", &aa);\n\n            ImGui::DragFloat(\"Shadow Thickness\", &shadow_thickness, 1.0f, 0.0f, 100.0f, \"%.02f\");\n            ImGui::SliderFloat2(\"Offset\", (float*)&shadow_offset, -32.0f, 32.0f);\n            ImGui::SameLine();\n            HelpMarker(\"Note that currently circles/convex shapes do not support non-zero offsets for unfilled shadows.\");\n            ImGui::ColorEdit4(\"Background Color\", &background_color.x);\n            ImGui::ColorEdit4(\"Shadow Color\", &shadow_color.x);\n            ImGui::ColorEdit4(\"Shape Color\", &shape_color.x);\n            ImGui::DragFloat(\"Shape Rounding\", &shape_rounding, 1.0f, 0.0f, 20.0f, \"%.02f\");\n            ImGui::Combo(\"Convex shape\", &poly_shape_index, \"Shape 1\\0Shape 2\\0Shape 3\\0Shape 4\\0Shape 4 (winding reversed)\");\n\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n            ImDrawListFlags old_flags = draw_list->Flags;\n\n            if (aa)\n                draw_list->Flags |= ~ImDrawListFlags_AntiAliasedFill;\n            else\n                draw_list->Flags &= ~ImDrawListFlags_AntiAliasedFill;\n\n            // Fill a strip of background\n            {\n                ImVec2 p = ImGui::GetCursorScreenPos();\n                draw_list->AddRectFilled(p, ImVec2(p.x + ImGui::GetContentRegionAvail().x, p.y + 200.0f), ImGui::GetColorU32(background_color));\n            }\n\n            // Rectangle\n            {\n                ImVec2 p = ImGui::GetCursorScreenPos();\n                ImGui::Dummy(ImVec2(200.0f, 200.0f));\n\n                ImVec2 r1(p.x + 50.0f, p.y + 50.0f);\n                ImVec2 r2(p.x + 150.0f, p.y + 150.0f);\n                ImDrawFlags draw_flags = shadow_filled ? ImDrawFlags_None : ImDrawFlags_ShadowCutOutShapeBackground;\n                draw_list->AddShadowRect(r1, r2, ImGui::GetColorU32(shadow_color), shadow_thickness, shadow_offset, draw_flags, shape_rounding);\n\n                if (wireframe)\n                    draw_list->AddRect(r1, r2, ImGui::GetColorU32(shape_color), shape_rounding);\n                else\n                    draw_list->AddRectFilled(r1, r2, ImGui::GetColorU32(shape_color), shape_rounding);\n            }\n\n            ImGui::SameLine();\n\n            // Circle\n            {\n                ImVec2 p = ImGui::GetCursorScreenPos();\n                ImGui::Dummy(ImVec2(200.0f, 200.0f));\n\n                // FIXME-SHADOWS: Offset forced to zero when shadow is not filled because it isn't supported\n                float off = 10.0f;\n                ImVec2 r1(p.x + 50.0f + off, p.y + 50.0f + off);\n                ImVec2 r2(p.x + 150.0f - off, p.y + 150.0f - off);\n                ImVec2 center(p.x + 100.0f, p.y + 100.0f);\n                ImDrawFlags draw_flags = shadow_filled ? ImDrawFlags_None : ImDrawFlags_ShadowCutOutShapeBackground;\n                draw_list->AddShadowCircle(center, 50.0f, ImGui::GetColorU32(shadow_color), shadow_thickness, shadow_filled ? shadow_offset : ImVec2(0.0f, 0.0f), draw_flags, 0);\n\n                if (wireframe)\n                    draw_list->AddCircle(center, 50.0f, ImGui::GetColorU32(shape_color), 0);\n                else\n                    draw_list->AddCircleFilled(center, 50.0f, ImGui::GetColorU32(shape_color), 0);\n            }\n\n            ImGui::SameLine();\n\n            // Convex shape\n            {\n                ImVec2 pos = ImGui::GetCursorScreenPos();\n                ImGui::Dummy(ImVec2(200.0f, 200.0f));\n\n                const ImVec2 poly_centre(pos.x + 50.0f, pos.y + 100.0f);\n                ImVec2 poly_points[8];\n                int poly_points_count = 0;\n\n                switch (poly_shape_index)\n                {\n                default:\n                case 0:\n                {\n                    poly_points[0] = ImVec2(poly_centre.x - 32.0f, poly_centre.y);\n                    poly_points[1] = ImVec2(poly_centre.x - 24.0f, poly_centre.y + 24.0f);\n                    poly_points[2] = ImVec2(poly_centre.x, poly_centre.y + 32.0f);\n                    poly_points[3] = ImVec2(poly_centre.x + 24.0f, poly_centre.y + 24.0f);\n                    poly_points[4] = ImVec2(poly_centre.x + 32.0f, poly_centre.y);\n                    poly_points[5] = ImVec2(poly_centre.x + 24.0f, poly_centre.y - 24.0f);\n                    poly_points[6] = ImVec2(poly_centre.x, poly_centre.y - 32.0f);\n                    poly_points[7] = ImVec2(poly_centre.x - 32.0f, poly_centre.y - 32.0f);\n                    poly_points_count = 8;\n                    break;\n                }\n                case 1:\n                {\n                    poly_points[0] = ImVec2(poly_centre.x + 40.0f, poly_centre.y - 20.0f);\n                    poly_points[1] = ImVec2(poly_centre.x, poly_centre.y + 32.0f);\n                    poly_points[2] = ImVec2(poly_centre.x - 24.0f, poly_centre.y - 32.0f);\n                    poly_points_count = 3;\n                    break;\n                }\n                case 2:\n                {\n                    poly_points[0] = ImVec2(poly_centre.x - 32.0f, poly_centre.y);\n                    poly_points[1] = ImVec2(poly_centre.x, poly_centre.y + 32.0f);\n                    poly_points[2] = ImVec2(poly_centre.x + 32.0f, poly_centre.y);\n                    poly_points[3] = ImVec2(poly_centre.x, poly_centre.y - 32.0f);\n                    poly_points_count = 4;\n                    break;\n                }\n                case 3:\n                {\n                    poly_points[0] = ImVec2(poly_centre.x - 4.0f, poly_centre.y - 20.0f);\n                    poly_points[1] = ImVec2(poly_centre.x + 12.0f, poly_centre.y + 2.0f);\n                    poly_points[2] = ImVec2(poly_centre.x + 8.0f, poly_centre.y + 16.0f);\n                    poly_points[3] = ImVec2(poly_centre.x, poly_centre.y + 32.0f);\n                    poly_points[4] = ImVec2(poly_centre.x - 16.0f, poly_centre.y - 32.0f);\n                    poly_points_count = 5;\n                    break;\n                }\n                case 4: // Same as test case 3 but with reversed winding\n                {\n                    poly_points[0] = ImVec2(poly_centre.x - 16.0f, poly_centre.y - 32.0f);\n                    poly_points[1] = ImVec2(poly_centre.x, poly_centre.y + 32.0f);\n                    poly_points[2] = ImVec2(poly_centre.x + 8.0f, poly_centre.y + 16.0f);\n                    poly_points[3] = ImVec2(poly_centre.x + 12.0f, poly_centre.y + 2.0f);\n                    poly_points[4] = ImVec2(poly_centre.x - 4.0f, poly_centre.y - 20.0f);\n                    poly_points_count = 5;\n                    break;\n                }\n                }\n\n                // FIXME-SHADOWS: Offset forced to zero when shadow is not filled because it isn't supported\n                ImDrawFlags draw_flags = shadow_filled ? ImDrawFlags_None : ImDrawFlags_ShadowCutOutShapeBackground;\n                draw_list->AddShadowConvexPoly(poly_points, poly_points_count, ImGui::GetColorU32(shadow_color), shadow_thickness, shadow_filled ? shadow_offset : ImVec2(0.0f, 0.0f), draw_flags);\n\n                if (wireframe)\n                    draw_list->AddPolyline(poly_points, poly_points_count, ImGui::GetColorU32(shape_color), true, 1.0f);\n                else\n                    draw_list->AddConvexPolyFilled(poly_points, poly_points_count, ImGui::GetColorU32(shape_color));\n            }\n\n            draw_list->Flags = old_flags;\n\n            ImGui::EndTabItem();\n        }\n\n        if (ImGui::BeginTabItem(\"BG/FG draw lists\"))\n        {\n            IMGUI_DEMO_MARKER(\"Examples/Custom rendering/BG & FG draw lists\");\n            static bool draw_bg = true;\n            static bool draw_fg = true;\n            ImGui::Checkbox(\"Draw in Background draw list\", &draw_bg);\n            ImGui::SameLine(); HelpMarker(\"The Background draw list will be rendered below every Dear ImGui windows.\");\n            ImGui::Checkbox(\"Draw in Foreground draw list\", &draw_fg);\n            ImGui::SameLine(); HelpMarker(\"The Foreground draw list will be rendered over every Dear ImGui windows.\");\n            ImVec2 window_pos = ImGui::GetWindowPos();\n            ImVec2 window_size = ImGui::GetWindowSize();\n            ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f);\n            if (draw_bg)\n                ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 0, 10 + 4);\n            if (draw_fg)\n                ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 0, 10);\n            ImGui::EndTabItem();\n        }\n\n        // Demonstrate out-of-order rendering via channels splitting\n        // We use functions in ImDrawList as each draw list contains a convenience splitter,\n        // but you can also instantiate your own ImDrawListSplitter if you need to nest them.\n        if (ImGui::BeginTabItem(\"Draw Channels\"))\n        {\n            IMGUI_DEMO_MARKER(\"Examples/Custom rendering/Draw Channels\");\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n            {\n                ImGui::Text(\"Blue shape is drawn first: appears in back\");\n                ImGui::Text(\"Red shape is drawn after: appears in front\");\n                ImVec2 p0 = ImGui::GetCursorScreenPos();\n                draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50), IM_COL32(0, 0, 255, 255)); // Blue\n                draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25), ImVec2(p0.x + 75, p0.y + 75), IM_COL32(255, 0, 0, 255)); // Red\n                ImGui::Dummy(ImVec2(75, 75));\n            }\n            ImGui::Separator();\n            {\n                ImGui::Text(\"Blue shape is drawn first, into channel 1: appears in front\");\n                ImGui::Text(\"Red shape is drawn after, into channel 0: appears in back\");\n                ImVec2 p1 = ImGui::GetCursorScreenPos();\n\n                // Create 2 channels and draw a Blue shape THEN a Red shape.\n                // You can create any number of channels. Tables API use 1 channel per column in order to better batch draw calls.\n                draw_list->ChannelsSplit(2);\n                draw_list->ChannelsSetCurrent(1);\n                draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50), IM_COL32(0, 0, 255, 255)); // Blue\n                draw_list->ChannelsSetCurrent(0);\n                draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25), ImVec2(p1.x + 75, p1.y + 75), IM_COL32(255, 0, 0, 255)); // Red\n\n                // Flatten/reorder channels. Red shape is in channel 0 and it appears below the Blue shape in channel 1.\n                // This works by copying draw indices only (vertices are not copied).\n                draw_list->ChannelsMerge();\n                ImGui::Dummy(ImVec2(75, 75));\n                ImGui::Text(\"After reordering, contents of channel 0 appears below channel 1.\");\n            }\n            ImGui::EndTabItem();\n        }\n\n        ImGui::EndTabBar();\n    }\n\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace()\n//-----------------------------------------------------------------------------\n\nstruct ImGuiDemoDockspaceArgs\n{\n    bool                IsFullscreen = true;\n    bool                KeepWindowPadding = false; // Keep WindowPadding to help understand that DockSpace() is a widget inside the window.\n    ImGuiDockNodeFlags  DockSpaceFlags  = ImGuiDockNodeFlags_None;\n};\n\n// THIS IS A DEMO FOR ADVANCED USAGE OF DockSpace().\n// MOST REGULAR APPLICATIONS WANTING TO ALLOW DOCKING WINDOWS ON THE EDGE OF YOUR SCREEN CAN SIMPLY USE:\n//    ImGui::NewFrame(); + ImGui::DockSpaceOverViewport();                                                   // Create a dockspace in main viewport\n// OR:\n//    ImGui::NewFrame(); + ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode); // Create a dockspace in main viewport, where central node is transparent.\n// Demonstrate using DockSpace() to create an explicit docking node within an existing window, with various options.\n// Read https://github.com/ocornut/imgui/wiki/Docking for details.\n// The reasons we do not use DockSpaceOverViewport() in this demo is because:\n// - (1) we allow the host window to be floating/moveable instead of filling the viewport (when args->IsFullscreen == false)\n//       which is mostly to showcase the idea that DockSpace() may be submitted anywhere.\n//       Also see 'Demo->Examples->Documents' for a less abstract version of this.\n// - (2) we allow the host window to have padding (when args->UsePadding == true)\n// - (3) we expose variety of other flags.\nstatic void ShowExampleAppDockSpaceAdvanced(ImGuiDemoDockspaceArgs* args, bool* p_open)\n{\n    ImGuiDockNodeFlags dockspace_flags = args->DockSpaceFlags;\n\n    // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,\n    // because it would be confusing to have two docking targets within each others.\n    ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking;\n    if (args->IsFullscreen)\n    {\n        // Fullscreen dockspace: practically the same as calling DockSpaceOverViewport();\n        const ImGuiViewport* viewport = ImGui::GetMainViewport();\n        ImGui::SetNextWindowPos(viewport->WorkPos);\n        ImGui::SetNextWindowSize(viewport->WorkSize);\n        ImGui::SetNextWindowViewport(viewport->ID);\n        ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);\n        ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);\n        window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;\n        window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;\n        window_flags |= ImGuiWindowFlags_NoBackground;\n    }\n    else\n    {\n        // Floating dockspace\n        dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode;\n    }\n\n    // Important: note that we proceed even if Begin() returns false (aka window is collapsed).\n    // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,\n    // all active windows docked into it will lose their parent and become undocked.\n    // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise\n    // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.\n    if (!args->KeepWindowPadding)\n        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));\n    ImGui::Begin(\"Window with a DockSpace\", p_open, window_flags);\n    if (!args->KeepWindowPadding)\n        ImGui::PopStyleVar();\n\n    if (args->IsFullscreen)\n        ImGui::PopStyleVar(2);\n\n    // Submit the DockSpace widget inside our window\n    // - Note that the id here is different from the one used by DockSpaceOverViewport(), so docking state won't get transfered between \"Basic\" and \"Advanced\" demos.\n    // - If we made the ShowExampleAppDockSpaceBasic() calculate its own ID and pass it to DockSpaceOverViewport() the ID could easily match.\n    ImGuiID dockspace_id = ImGui::GetID(\"MyDockSpace\");\n    ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);\n\n    ImGui::End();\n}\n\nstatic void ShowExampleAppDockSpaceBasic(ImGuiDockNodeFlags flags)\n{\n    // Basic version which you can use in many apps:\n    // e.g:\n    //   ImGui::DockSpaceOverViewport();\n    // or:\n    //   ImGui::DockSpaceOverViewport(0, nullptr, ImGuiDockNodeFlags_PassthruCentralNode); // Central node will be transparent\n    // or:\n    //   ImGuiViewport* viewport = ImGui::GetMainViewport();\n    //   ImGui::DockSpaceOverViewport(0, viewport, ImGuiDockNodeFlags_None);\n\n    ImGui::DockSpaceOverViewport(0, nullptr, flags);\n}\n\nvoid ShowExampleAppDockSpace(bool* p_open)\n{\n    static int opt_demo_mode = 0;\n    static bool opt_demo_mode_changed = false;\n    static ImGuiDemoDockspaceArgs args;\n\n    if (opt_demo_mode == 0)\n        ShowExampleAppDockSpaceBasic(args.DockSpaceFlags);\n    else\n        ShowExampleAppDockSpaceAdvanced(&args, p_open);\n\n    // Refocus our window to minimize perceived loss of focus when changing mode (caused by the fact that each use a different window, which would not happen in a real app)\n    if (opt_demo_mode_changed)\n        ImGui::SetNextWindowFocus();\n    ImGui::Begin(\"Examples: Dockspace\", p_open, ImGuiWindowFlags_MenuBar);\n    opt_demo_mode_changed = false;\n    opt_demo_mode_changed |= ImGui::RadioButton(\"Basic demo mode\", &opt_demo_mode, 0);\n    opt_demo_mode_changed |= ImGui::RadioButton(\"Advanced demo mode\", &opt_demo_mode, 1);\n\n    ImGui::SeparatorText(\"Options\");\n\n    if ((ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable) == 0)\n    {\n        ShowDockingDisabledMessage();\n    }\n    else if (opt_demo_mode == 0)\n    {\n        args.DockSpaceFlags &= ImGuiDockNodeFlags_PassthruCentralNode; // Allowed flags\n        ImGui::CheckboxFlags(\"Flag: PassthruCentralNode\", &args.DockSpaceFlags, ImGuiDockNodeFlags_PassthruCentralNode);\n    }\n    else if (opt_demo_mode == 1)\n    {\n        ImGui::Checkbox(\"Fullscreen\", &args.IsFullscreen);\n        ImGui::Checkbox(\"Keep Window Padding\", &args.KeepWindowPadding);\n        ImGui::SameLine();\n        HelpMarker(\"This is mostly exposed to facilitate understanding that a DockSpace() is _inside_ a window.\");\n        ImGui::BeginDisabled(args.IsFullscreen == false);\n        ImGui::CheckboxFlags(\"Flag: PassthruCentralNode\",      &args.DockSpaceFlags, ImGuiDockNodeFlags_PassthruCentralNode);\n        ImGui::EndDisabled();\n        ImGui::CheckboxFlags(\"Flag: NoDockingOverCentralNode\", &args.DockSpaceFlags, ImGuiDockNodeFlags_NoDockingOverCentralNode);\n        ImGui::CheckboxFlags(\"Flag: NoDockingSplit\",           &args.DockSpaceFlags, ImGuiDockNodeFlags_NoDockingSplit);\n        ImGui::CheckboxFlags(\"Flag: NoUndocking\",              &args.DockSpaceFlags, ImGuiDockNodeFlags_NoUndocking);\n        ImGui::CheckboxFlags(\"Flag: NoResize\",                 &args.DockSpaceFlags, ImGuiDockNodeFlags_NoResize);\n        ImGui::CheckboxFlags(\"Flag: AutoHideTabBar\",           &args.DockSpaceFlags, ImGuiDockNodeFlags_AutoHideTabBar);\n    }\n\n    // Show demo options and help\n    if (ImGui::BeginMenuBar())\n    {\n        if (ImGui::BeginMenu(\"Help\"))\n        {\n            ImGui::TextUnformatted(\n                \"This demonstrates the use of ImGui::DockSpace() which allows you to manually\\ncreate a docking node _within_ another window.\" \"\\n\"\n                \"The \\\"Basic\\\" version uses the ImGui::DockSpaceOverViewport() helper. Most applications can probably use this.\");\n            ImGui::Separator();\n            ImGui::TextUnformatted(\"When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!\" \"\\n\"\n                \"- Drag from window title bar or their tab to dock/undock.\" \"\\n\"\n                \"- Drag from window menu button (upper-left button) to undock an entire node (all windows).\" \"\\n\"\n                \"- Hold SHIFT to disable docking (if io.ConfigDockingWithShift == false, default)\" \"\\n\"\n                \"- Hold SHIFT to enable docking (if io.ConfigDockingWithShift == true)\");\n            ImGui::Separator();\n            ImGui::TextUnformatted(\"More details:\"); ImGui::Bullet(); ImGui::SameLine(); ImGui::TextLinkOpenURL(\"Docking Wiki page\", \"https://github.com/ocornut/imgui/wiki/Docking\");\n            ImGui::BulletText(\"Read comments in ShowExampleAppDockSpace()\");\n            ImGui::EndMenu();\n        }\n        ImGui::EndMenuBar();\n    }\n\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()\n//-----------------------------------------------------------------------------\n\n// Simplified structure to mimic a Document model\nstruct MyDocument\n{\n    char        Name[32];   // Document title\n    int         UID;        // Unique ID (necessary as we can change title)\n    bool        Open;       // Set when open (we keep an array of all available documents to simplify demo code!)\n    bool        OpenPrev;   // Copy of Open from last update.\n    bool        Dirty;      // Set when the document has been modified\n    ImVec4      Color;      // An arbitrary variable associated to the document\n\n    MyDocument(int uid, const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f))\n    {\n        UID = uid;\n        snprintf(Name, sizeof(Name), \"%s\", name);\n        Open = OpenPrev = open;\n        Dirty = false;\n        Color = color;\n    }\n    void DoOpen()       { Open = true; }\n    void DoForceClose() { Open = false; Dirty = false; }\n    void DoSave()       { Dirty = false; }\n};\n\nstruct ExampleAppDocuments\n{\n    ImVector<MyDocument>    Documents;\n    ImVector<MyDocument*>   CloseQueue;\n    MyDocument*             RenamingDoc = NULL;\n    bool                    RenamingStarted = false;\n\n    ExampleAppDocuments()\n    {\n        Documents.push_back(MyDocument(0, \"Lettuce\",             true,  ImVec4(0.4f, 0.8f, 0.4f, 1.0f)));\n        Documents.push_back(MyDocument(1, \"Eggplant\",            true,  ImVec4(0.8f, 0.5f, 1.0f, 1.0f)));\n        Documents.push_back(MyDocument(2, \"Carrot\",              true,  ImVec4(1.0f, 0.8f, 0.5f, 1.0f)));\n        Documents.push_back(MyDocument(3, \"Tomato\",              false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f)));\n        Documents.push_back(MyDocument(4, \"A Rather Long Title\", false, ImVec4(0.4f, 0.8f, 0.8f, 1.0f)));\n        Documents.push_back(MyDocument(5, \"Some Document\",       false, ImVec4(0.8f, 0.8f, 1.0f, 1.0f)));\n    }\n\n    // As we allow to change document name, we append a never-changing document ID so tabs are stable\n    void GetTabName(MyDocument* doc, char* out_buf, size_t out_buf_size)\n    {\n        snprintf(out_buf, out_buf_size, \"%s###doc%d\", doc->Name, doc->UID);\n    }\n\n    // Display placeholder contents for the Document\n    void DisplayDocContents(MyDocument* doc)\n    {\n        ImGui::PushID(doc);\n        ImGui::Text(\"Document \\\"%s\\\"\", doc->Name);\n        ImGui::PushStyleColor(ImGuiCol_Text, doc->Color);\n        ImGui::TextWrapped(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\");\n        ImGui::PopStyleColor();\n\n        ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_R, ImGuiInputFlags_Tooltip);\n        if (ImGui::Button(\"Rename..\"))\n        {\n            RenamingDoc = doc;\n            RenamingStarted = true;\n        }\n        ImGui::SameLine();\n\n        ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_M, ImGuiInputFlags_Tooltip);\n        if (ImGui::Button(\"Modify\"))\n            doc->Dirty = true;\n\n        ImGui::SameLine();\n        ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, ImGuiInputFlags_Tooltip);\n        if (ImGui::Button(\"Save\"))\n            doc->DoSave();\n\n        ImGui::SameLine();\n        ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_W, ImGuiInputFlags_Tooltip);\n        if (ImGui::Button(\"Close\"))\n            CloseQueue.push_back(doc);\n        ImGui::ColorEdit3(\"color\", &doc->Color.x);  // Useful to test drag and drop and hold-dragged-to-open-tab behavior.\n        ImGui::PopID();\n    }\n\n    // Display context menu for the Document\n    void DisplayDocContextMenu(MyDocument* doc)\n    {\n        if (!ImGui::BeginPopupContextItem())\n            return;\n\n        char buf[256];\n        sprintf(buf, \"Save %s\", doc->Name);\n        if (ImGui::MenuItem(buf, \"Ctrl+S\", false, doc->Open))\n            doc->DoSave();\n        if (ImGui::MenuItem(\"Rename...\", \"Ctrl+R\", false, doc->Open))\n            RenamingDoc = doc;\n        if (ImGui::MenuItem(\"Close\", \"Ctrl+W\", false, doc->Open))\n            CloseQueue.push_back(doc);\n        ImGui::EndPopup();\n    }\n\n    // [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.\n    // If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,\n    // as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for\n    // the tab bar to notice its absence. During this frame there will be a gap in the tab bar, and if the tab that has\n    // disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively\n    // give the impression of a flicker for one frame.\n    // We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.\n    // Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag.\n    void NotifyOfDocumentsClosedElsewhere()\n    {\n        for (MyDocument& doc : Documents)\n        {\n            if (!doc.Open && doc.OpenPrev)\n                ImGui::SetTabItemClosed(doc.Name);\n            doc.OpenPrev = doc.Open;\n        }\n    }\n};\n\nvoid ShowExampleAppDocuments(bool* p_open)\n{\n    static ExampleAppDocuments app;\n\n    // Options\n    enum Target\n    {\n        Target_None,\n        Target_Tab,                 // Create documents as local tab into a local tab bar\n        Target_DockSpaceAndWindow   // Create documents as regular windows, and create an embedded dockspace\n    };\n    static Target opt_target = Target_Tab;\n    static bool opt_reorderable = true;\n    static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_;\n\n    // When (opt_target == Target_DockSpaceAndWindow) there is the possibily that one of our child Document window (e.g. \"Eggplant\")\n    // that we emit gets docked into the same spot as the parent window (\"Example: Documents\").\n    // This would create a problematic feedback loop because selecting the \"Eggplant\" tab would make the \"Example: Documents\" tab\n    // not visible, which in turn would stop submitting the \"Eggplant\" window.\n    // We avoid this problem by submitting our documents window even if our parent window is not currently visible.\n    // Another solution may be to make the \"Example: Documents\" window use the ImGuiWindowFlags_NoDocking.\n\n    bool window_contents_visible = ImGui::Begin(\"Example: Documents\", p_open, ImGuiWindowFlags_MenuBar);\n    if (!window_contents_visible && opt_target != Target_DockSpaceAndWindow)\n    {\n        ImGui::End();\n        return;\n    }\n    IMGUI_DEMO_MARKER(\"Examples/Documents\");\n\n    // Menu\n    if (ImGui::BeginMenuBar())\n    {\n        if (ImGui::BeginMenu(\"File\"))\n        {\n            int open_count = 0;\n            for (MyDocument& doc : app.Documents)\n                open_count += doc.Open ? 1 : 0;\n\n            if (ImGui::BeginMenu(\"Open\", open_count < app.Documents.Size))\n            {\n                for (MyDocument& doc : app.Documents)\n                    if (!doc.Open && ImGui::MenuItem(doc.Name))\n                        doc.DoOpen();\n                ImGui::EndMenu();\n            }\n            if (ImGui::MenuItem(\"Close All Documents\", NULL, false, open_count > 0))\n                for (MyDocument& doc : app.Documents)\n                    app.CloseQueue.push_back(&doc);\n            if (ImGui::MenuItem(\"Exit\") && p_open)\n                *p_open = false;\n            ImGui::EndMenu();\n        }\n        ImGui::EndMenuBar();\n    }\n\n    // [Debug] List documents with one checkbox for each\n    for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)\n    {\n        MyDocument& doc = app.Documents[doc_n];\n        if (doc_n > 0)\n            ImGui::SameLine();\n        ImGui::PushID(&doc);\n        if (ImGui::Checkbox(doc.Name, &doc.Open))\n            if (!doc.Open)\n                doc.DoForceClose();\n        ImGui::PopID();\n    }\n    ImGui::PushItemWidth(ImGui::GetFontSize() * 12);\n    ImGui::Combo(\"Output\", (int*)&opt_target, \"None\\0TabBar+Tabs\\0DockSpace+Window\\0\");\n    ImGui::PopItemWidth();\n    bool redock_all = false;\n    if (opt_target == Target_Tab)                { ImGui::SameLine(); ImGui::Checkbox(\"Reorderable Tabs\", &opt_reorderable); }\n    if (opt_target == Target_DockSpaceAndWindow) { ImGui::SameLine(); redock_all = ImGui::Button(\"Redock all\"); }\n\n    ImGui::Separator();\n\n    // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags.\n    // They have multiple effects:\n    // - Display a dot next to the title.\n    // - Tab is selected when clicking the X close button.\n    // - Closure is not assumed (will wait for user to stop submitting the tab).\n    //   Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.\n    //   We need to assume closure by default otherwise waiting for \"lack of submission\" on the next frame would leave an empty\n    //   hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window.\n    //   The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole.\n\n    // Tabs\n    if (opt_target == Target_Tab)\n    {\n        ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0);\n        tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline;\n        if (ImGui::BeginTabBar(\"##tabs\", tab_bar_flags))\n        {\n            if (opt_reorderable)\n                app.NotifyOfDocumentsClosedElsewhere();\n\n            // [DEBUG] Stress tests\n            //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1;            // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on.\n            //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name);  // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway..\n\n            // Submit Tabs\n            for (MyDocument& doc : app.Documents)\n            {\n                if (!doc.Open)\n                    continue;\n\n                // As we allow to change document name, we append a never-changing document id so tabs are stable\n                char doc_name_buf[64];\n                app.GetTabName(&doc, doc_name_buf, sizeof(doc_name_buf));\n                ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0);\n                bool visible = ImGui::BeginTabItem(doc_name_buf, &doc.Open, tab_flags);\n\n                // Cancel attempt to close when unsaved add to save queue so we can display a popup.\n                if (!doc.Open && doc.Dirty)\n                {\n                    doc.Open = true;\n                    app.CloseQueue.push_back(&doc);\n                }\n\n                app.DisplayDocContextMenu(&doc);\n                if (visible)\n                {\n                    app.DisplayDocContents(&doc);\n                    ImGui::EndTabItem();\n                }\n            }\n\n            ImGui::EndTabBar();\n        }\n    }\n    else if (opt_target == Target_DockSpaceAndWindow)\n    {\n        if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable)\n        {\n            app.NotifyOfDocumentsClosedElsewhere();\n\n            // Create a DockSpace node where any window can be docked\n            ImGuiID dockspace_id = ImGui::GetID(\"MyDockSpace\");\n            ImGui::DockSpace(dockspace_id);\n\n            // Create Windows\n            for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)\n            {\n                MyDocument* doc = &app.Documents[doc_n];\n                if (!doc->Open)\n                    continue;\n\n                ImGui::SetNextWindowDockID(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver);\n                ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0);\n                bool visible = ImGui::Begin(doc->Name, &doc->Open, window_flags);\n\n                // Cancel attempt to close when unsaved add to save queue so we can display a popup.\n                if (!doc->Open && doc->Dirty)\n                {\n                    doc->Open = true;\n                    app.CloseQueue.push_back(doc);\n                }\n\n                app.DisplayDocContextMenu(doc);\n                if (visible)\n                    app.DisplayDocContents(doc);\n\n                ImGui::End();\n            }\n        }\n        else\n        {\n            ShowDockingDisabledMessage();\n        }\n    }\n\n    // Early out other contents\n    if (!window_contents_visible)\n    {\n        ImGui::End();\n        return;\n    }\n\n    // Display renaming UI\n    if (app.RenamingDoc != NULL)\n    {\n        if (app.RenamingStarted)\n            ImGui::OpenPopup(\"Rename\");\n        if (ImGui::BeginPopup(\"Rename\"))\n        {\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30);\n            if (ImGui::InputText(\"###Name\", app.RenamingDoc->Name, IM_COUNTOF(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue))\n            {\n                ImGui::CloseCurrentPopup();\n                app.RenamingDoc = NULL;\n            }\n            if (app.RenamingStarted)\n                ImGui::SetKeyboardFocusHere(-1);\n            ImGui::EndPopup();\n        }\n        else\n        {\n            app.RenamingDoc = NULL;\n        }\n        app.RenamingStarted = false;\n    }\n\n    // Display closing confirmation UI\n    if (!app.CloseQueue.empty())\n    {\n        int close_queue_unsaved_documents = 0;\n        for (int n = 0; n < app.CloseQueue.Size; n++)\n            if (app.CloseQueue[n]->Dirty)\n                close_queue_unsaved_documents++;\n\n        if (close_queue_unsaved_documents == 0)\n        {\n            // Close documents when all are unsaved\n            for (int n = 0; n < app.CloseQueue.Size; n++)\n                app.CloseQueue[n]->DoForceClose();\n            app.CloseQueue.clear();\n        }\n        else\n        {\n            if (!ImGui::IsPopupOpen(\"Save?\"))\n                ImGui::OpenPopup(\"Save?\");\n            if (ImGui::BeginPopupModal(\"Save?\", NULL, ImGuiWindowFlags_AlwaysAutoResize))\n            {\n                ImGui::Text(\"Save change to the following items?\");\n                float item_height = ImGui::GetTextLineHeightWithSpacing();\n                if (ImGui::BeginChild(ImGui::GetID(\"frame\"), ImVec2(-FLT_MIN, 6.25f * item_height), ImGuiChildFlags_FrameStyle))\n                    for (MyDocument* doc : app.CloseQueue)\n                        if (doc->Dirty)\n                            ImGui::Text(\"%s\", doc->Name);\n                ImGui::EndChild();\n\n                ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f);\n                if (ImGui::Button(\"Yes\", button_size))\n                {\n                    for (MyDocument* doc : app.CloseQueue)\n                    {\n                        if (doc->Dirty)\n                            doc->DoSave();\n                        doc->DoForceClose();\n                    }\n                    app.CloseQueue.clear();\n                    ImGui::CloseCurrentPopup();\n                }\n                ImGui::SameLine();\n                if (ImGui::Button(\"No\", button_size))\n                {\n                    for (MyDocument* doc : app.CloseQueue)\n                        doc->DoForceClose();\n                    app.CloseQueue.clear();\n                    ImGui::CloseCurrentPopup();\n                }\n                ImGui::SameLine();\n                if (ImGui::Button(\"Cancel\", button_size))\n                {\n                    app.CloseQueue.clear();\n                    ImGui::CloseCurrentPopup();\n                }\n                ImGui::EndPopup();\n            }\n        }\n    }\n\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()\n//-----------------------------------------------------------------------------\n\n//#include \"imgui_internal.h\" // NavMoveRequestTryWrapping()\n\nstruct ExampleAsset\n{\n    ImGuiID ID;\n    int     Type;\n\n    ExampleAsset(ImGuiID id, int type) { ID = id; Type = type; }\n\n    static const ImGuiTableSortSpecs* s_current_sort_specs;\n\n    static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, ExampleAsset* items, int items_count)\n    {\n        s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.\n        if (items_count > 1)\n            qsort(items, (size_t)items_count, sizeof(items[0]), ExampleAsset::CompareWithSortSpecs);\n        s_current_sort_specs = NULL;\n    }\n\n    // Compare function to be used by qsort()\n    static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)\n    {\n        const ExampleAsset* a = (const ExampleAsset*)lhs;\n        const ExampleAsset* b = (const ExampleAsset*)rhs;\n        for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)\n        {\n            const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];\n            int delta = 0;\n            if (sort_spec->ColumnIndex == 0)\n                delta = ((int)a->ID - (int)b->ID);\n            else if (sort_spec->ColumnIndex == 1)\n                delta = (a->Type - b->Type);\n            if (delta > 0)\n                return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;\n            if (delta < 0)\n                return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;\n        }\n        return (int)a->ID - (int)b->ID;\n    }\n};\nconst ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL;\n\nstruct ExampleAssetsBrowser\n{\n    // Options\n    bool            ShowTypeOverlay = true;\n    bool            AllowSorting = true;\n    bool            AllowDragUnselected = false;\n    bool            AllowBoxSelect = true;\n    float           IconSize = 32.0f;\n    int             IconSpacing = 10;\n    int             IconHitSpacing = 4;         // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer.\n    bool            StretchSpacing = true;\n\n    // State\n    ImVector<ExampleAsset> Items;               // Our items\n    ExampleSelectionWithDeletion Selection;     // Our selection (ImGuiSelectionBasicStorage + helper funcs to handle deletion)\n    ImGuiID         NextItemId = 0;             // Unique identifier when creating new items\n    bool            RequestDelete = false;      // Deferred deletion request\n    bool            RequestSort = false;        // Deferred sort request\n    float           ZoomWheelAccum = 0.0f;      // Mouse wheel accumulator to handle smooth wheels better\n\n    // Calculated sizes for layout, output of UpdateLayoutSizes(). Could be locals but our code is simpler this way.\n    ImVec2          LayoutItemSize;\n    ImVec2          LayoutItemStep;             // == LayoutItemSize + LayoutItemSpacing\n    float           LayoutItemSpacing = 0.0f;\n    float           LayoutSelectableSpacing = 0.0f;\n    float           LayoutOuterPadding = 0.0f;\n    int             LayoutColumnCount = 0;\n    int             LayoutLineCount = 0;\n\n    // Functions\n    ExampleAssetsBrowser()\n    {\n        AddItems(10000);\n    }\n    void AddItems(int count)\n    {\n        if (Items.Size == 0)\n            NextItemId = 0;\n        Items.reserve(Items.Size + count);\n        for (int n = 0; n < count; n++, NextItemId++)\n            Items.push_back(ExampleAsset(NextItemId, (NextItemId % 20) < 15 ? 0 : (NextItemId % 20) < 18 ? 1 : 2));\n        RequestSort = true;\n    }\n    void ClearItems()\n    {\n        Items.clear();\n        Selection.Clear();\n    }\n\n    // Logic would be written in the main code BeginChild() and outputting to local variables.\n    // We extracted it into a function so we can call it easily from multiple places.\n    void UpdateLayoutSizes(float avail_width)\n    {\n        // Layout: when not stretching: allow extending into right-most spacing.\n        LayoutItemSpacing = (float)IconSpacing;\n        if (StretchSpacing == false)\n            avail_width += floorf(LayoutItemSpacing * 0.5f);\n\n        // Layout: calculate number of icon per line and number of lines\n        LayoutItemSize = ImVec2(floorf(IconSize), floorf(IconSize));\n        LayoutColumnCount = IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1);\n        LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount;\n\n        // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer.\n        if (StretchSpacing && LayoutColumnCount > 1)\n            LayoutItemSpacing = floorf(avail_width - LayoutItemSize.x * LayoutColumnCount) / LayoutColumnCount;\n\n        LayoutItemStep = ImVec2(LayoutItemSize.x + LayoutItemSpacing, LayoutItemSize.y + LayoutItemSpacing);\n        LayoutSelectableSpacing = IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f);\n        LayoutOuterPadding = floorf(LayoutItemSpacing * 0.5f);\n    }\n\n    void Draw(const char* title, bool* p_open)\n    {\n        ImGui::SetNextWindowSize(ImVec2(IconSize * 25, IconSize * 15), ImGuiCond_FirstUseEver);\n        if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_MenuBar))\n        {\n            ImGui::End();\n            return;\n        }\n        IMGUI_DEMO_MARKER(\"Examples/Assets Browser\");\n\n        // Menu bar\n        if (ImGui::BeginMenuBar())\n        {\n            if (ImGui::BeginMenu(\"File\"))\n            {\n                if (ImGui::MenuItem(\"Add 10000 items\"))\n                    AddItems(10000);\n                if (ImGui::MenuItem(\"Clear items\"))\n                    ClearItems();\n                ImGui::Separator();\n                if (ImGui::MenuItem(\"Close\", NULL, false, p_open != NULL))\n                    *p_open = false;\n                ImGui::EndMenu();\n            }\n            if (ImGui::BeginMenu(\"Edit\"))\n            {\n                if (ImGui::MenuItem(\"Delete\", \"Del\", false, Selection.Size > 0))\n                    RequestDelete = true;\n                ImGui::EndMenu();\n            }\n            if (ImGui::BeginMenu(\"Options\"))\n            {\n                ImGui::PushItemWidth(ImGui::GetFontSize() * 10);\n\n                ImGui::SeparatorText(\"Contents\");\n                ImGui::Checkbox(\"Show Type Overlay\", &ShowTypeOverlay);\n                ImGui::Checkbox(\"Allow Sorting\", &AllowSorting);\n\n                ImGui::SeparatorText(\"Selection Behavior\");\n                ImGui::Checkbox(\"Allow dragging unselected item\", &AllowDragUnselected);\n                ImGui::Checkbox(\"Allow box-selection\", &AllowBoxSelect);\n\n                ImGui::SeparatorText(\"Layout\");\n                ImGui::SliderFloat(\"Icon Size\", &IconSize, 16.0f, 128.0f, \"%.0f\");\n                ImGui::SameLine(); HelpMarker(\"Use Ctrl+Wheel to zoom\");\n                ImGui::SliderInt(\"Icon Spacing\", &IconSpacing, 0, 32);\n                ImGui::SliderInt(\"Icon Hit Spacing\", &IconHitSpacing, 0, 32);\n                ImGui::Checkbox(\"Stretch Spacing\", &StretchSpacing);\n                ImGui::PopItemWidth();\n                ImGui::EndMenu();\n            }\n            ImGui::EndMenuBar();\n        }\n\n        // Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI\n        if (AllowSorting)\n        {\n            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));\n            ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders;\n            if (ImGui::BeginTable(\"for_sort_specs_only\", 2, table_flags_for_sort_specs, ImVec2(0.0f, ImGui::GetFrameHeight())))\n            {\n                ImGui::TableSetupColumn(\"Index\");\n                ImGui::TableSetupColumn(\"Type\");\n                ImGui::TableHeadersRow();\n                if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())\n                    if (sort_specs->SpecsDirty || RequestSort)\n                    {\n                        ExampleAsset::SortWithSortSpecs(sort_specs, Items.Data, Items.Size);\n                        sort_specs->SpecsDirty = RequestSort = false;\n                    }\n                ImGui::EndTable();\n            }\n            ImGui::PopStyleVar();\n        }\n\n        ImGuiIO& io = ImGui::GetIO();\n        ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.y + LayoutItemSpacing)));\n        if (ImGui::BeginChild(\"Assets\", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove))\n        {\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n\n            const float avail_width = ImGui::GetContentRegionAvail().x;\n            UpdateLayoutSizes(avail_width);\n\n            // Calculate and store start position.\n            ImVec2 start_pos = ImGui::GetCursorScreenPos();\n            start_pos = ImVec2(start_pos.x + LayoutOuterPadding, start_pos.y + LayoutOuterPadding);\n            ImGui::SetCursorScreenPos(start_pos);\n\n            // Multi-select\n            ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid;\n\n            // - Enable box-select (in 2D mode, so that changing box-select rectangle X1/X2 boundaries will affect clipped items)\n            if (AllowBoxSelect)\n                ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d;\n\n            // - This feature allows dragging an unselected item without selecting it (rarely used)\n            if (AllowDragUnselected)\n                ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease;\n\n            // - Enable keyboard wrapping on X axis\n            // (FIXME-MULTISELECT: We haven't designed/exposed a general nav wrapping api yet, so this flag is provided as a courtesy to avoid doing:\n            //    ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX);\n            // When we finish implementing a more general API for this, we will obsolete this flag in favor of the new system)\n            ms_flags |= ImGuiMultiSelectFlags_NavWrapX;\n\n            ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size, Items.Size);\n\n            // Use custom selection adapter: store ID in selection (recommended)\n            Selection.UserData = this;\n            Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->UserData; return self->Items[idx].ID; };\n            Selection.ApplyRequests(ms_io);\n\n            const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete;\n            const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, Items.Size) : -1;\n            RequestDelete = false;\n\n            // Push LayoutSelectableSpacing (which is LayoutItemSpacing minus hit-spacing, if we decide to have hit gaps between items)\n            // Altering style ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()...\n            // But it is necessary for two reasons:\n            // - Selectables uses it by default to visually fill the space between two items.\n            // - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do).\n            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(LayoutSelectableSpacing, LayoutSelectableSpacing));\n\n            // Rendering parameters\n            const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) };\n            const ImU32 icon_bg_color = ImGui::GetColorU32(IM_COL32(35, 35, 35, 220));\n            const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f);\n            const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize(\"999\").x);\n\n            const int column_count = LayoutColumnCount;\n            ImGuiListClipper clipper;\n            clipper.Begin(LayoutLineCount, LayoutItemStep.y);\n            if (item_curr_idx_to_focus != -1)\n                clipper.IncludeItemByIndex(item_curr_idx_to_focus / column_count); // Ensure focused item line is not clipped.\n            if (ms_io->RangeSrcItem != -1)\n                clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem / column_count); // Ensure RangeSrc item line is not clipped.\n            while (clipper.Step())\n            {\n                for (int line_idx = clipper.DisplayStart; line_idx < clipper.DisplayEnd; line_idx++)\n                {\n                    const int item_min_idx_for_current_line = line_idx * column_count;\n                    const int item_max_idx_for_current_line = IM_MIN((line_idx + 1) * column_count, Items.Size);\n                    for (int item_idx = item_min_idx_for_current_line; item_idx < item_max_idx_for_current_line; ++item_idx)\n                    {\n                        ExampleAsset* item_data = &Items[item_idx];\n                        ImGui::PushID((int)item_data->ID);\n\n                        // Position item\n                        ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * LayoutItemStep.x, start_pos.y + line_idx * LayoutItemStep.y);\n                        ImGui::SetCursorScreenPos(pos);\n\n                        ImGui::SetNextItemSelectionUserData(item_idx);\n                        bool item_is_selected = Selection.Contains((ImGuiID)item_data->ID);\n                        bool item_is_visible = ImGui::IsRectVisible(LayoutItemSize);\n                        ImGui::Selectable(\"\", item_is_selected, ImGuiSelectableFlags_None, LayoutItemSize);\n\n                        // Update our selection state immediately (without waiting for EndMultiSelect() requests)\n                        // because we use this to alter the color of our text/icon.\n                        if (ImGui::IsItemToggledSelection())\n                            item_is_selected = !item_is_selected;\n\n                        // Focus (for after deletion)\n                        if (item_curr_idx_to_focus == item_idx)\n                            ImGui::SetKeyboardFocusHere(-1);\n\n                        // Drag and drop\n                        if (ImGui::BeginDragDropSource())\n                        {\n                            // Create payload with full selection OR single unselected item.\n                            // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)\n                            if (ImGui::GetDragDropPayload() == NULL)\n                            {\n                                ImVector<ImGuiID> payload_items;\n                                void* it = NULL;\n                                ImGuiID id = 0;\n                                if (!item_is_selected)\n                                    payload_items.push_back(item_data->ID);\n                                else\n                                    while (Selection.GetNextSelectedItem(&it, &id))\n                                        payload_items.push_back(id);\n                                ImGui::SetDragDropPayload(\"ASSETS_BROWSER_ITEMS\", payload_items.Data, (size_t)payload_items.size_in_bytes());\n                            }\n\n                            // Display payload content in tooltip, by extracting it from the payload data\n                            // (we could read from selection, but it is more correct and reusable to read from payload)\n                            const ImGuiPayload* payload = ImGui::GetDragDropPayload();\n                            const int payload_count = (int)payload->DataSize / (int)sizeof(ImGuiID);\n                            ImGui::Text(\"%d assets\", payload_count);\n\n                            ImGui::EndDragDropSource();\n                        }\n\n                        // Render icon (a real app would likely display an image/thumbnail here)\n                        // Because we use ImGuiMultiSelectFlags_BoxSelect2d, clipping vertical may occasionally be larger, so we coarse-clip our rendering as well.\n                        if (item_is_visible)\n                        {\n                            ImVec2 box_min(pos.x - 1, pos.y - 1);\n                            ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); // Dubious\n                            draw_list->AddRectFilled(box_min, box_max, icon_bg_color); // Background color\n                            if (ShowTypeOverlay && item_data->Type != 0)\n                            {\n                                ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_COUNTOF(icon_type_overlay_colors)];\n                                draw_list->AddRectFilled(ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), type_col);\n                            }\n                            if (display_label)\n                            {\n                                ImU32 label_col = ImGui::GetColorU32(item_is_selected ? ImGuiCol_Text : ImGuiCol_TextDisabled);\n                                char label[32];\n                                sprintf(label, \"%d\", item_data->ID);\n                                draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), label_col, label);\n                            }\n                        }\n\n                        ImGui::PopID();\n                    }\n                }\n            }\n            clipper.End();\n            ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing\n\n            // Context menu\n            if (ImGui::BeginPopupContextWindow())\n            {\n                ImGui::Text(\"Selection: %d items\", Selection.Size);\n                ImGui::Separator();\n                if (ImGui::MenuItem(\"Delete\", \"Del\", false, Selection.Size > 0))\n                    RequestDelete = true;\n                ImGui::EndPopup();\n            }\n\n            ms_io = ImGui::EndMultiSelect();\n            Selection.ApplyRequests(ms_io);\n            if (want_delete)\n                Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus);\n\n            // Zooming with Ctrl+Wheel\n            if (ImGui::IsWindowAppearing())\n                ZoomWheelAccum = 0.0f;\n            if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false)\n            {\n                ZoomWheelAccum += io.MouseWheel;\n                if (fabsf(ZoomWheelAccum) >= 1.0f)\n                {\n                    // Calculate hovered item index from mouse location\n                    // FIXME: Locking aiming on 'hovered_item_idx' (with a cool-down timer) would ensure zoom keeps on it.\n                    const float hovered_item_nx = (io.MousePos.x - start_pos.x + LayoutItemSpacing * 0.5f) / LayoutItemStep.x;\n                    const float hovered_item_ny = (io.MousePos.y - start_pos.y + LayoutItemSpacing * 0.5f) / LayoutItemStep.y;\n                    const int hovered_item_idx = ((int)hovered_item_ny * LayoutColumnCount) + (int)hovered_item_nx;\n                    //ImGui::SetTooltip(\"%f,%f -> item %d\", hovered_item_nx, hovered_item_ny, hovered_item_idx); // Move those 4 lines in block above for easy debugging\n\n                    // Zoom\n                    IconSize *= powf(1.1f, (float)(int)ZoomWheelAccum);\n                    IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f);\n                    ZoomWheelAccum -= (int)ZoomWheelAccum;\n                    UpdateLayoutSizes(avail_width);\n\n                    // Manipulate scroll to that we will land at the same Y location of currently hovered item.\n                    // - Calculate next frame position of item under mouse\n                    // - Set new scroll position to be used in next ImGui::BeginChild() call.\n                    float hovered_item_rel_pos_y = ((float)(hovered_item_idx / LayoutColumnCount) + fmodf(hovered_item_ny, 1.0f)) * LayoutItemStep.y;\n                    hovered_item_rel_pos_y += ImGui::GetStyle().WindowPadding.y;\n                    float mouse_local_y = io.MousePos.y - ImGui::GetWindowPos().y;\n                    ImGui::SetScrollY(hovered_item_rel_pos_y - mouse_local_y);\n                }\n            }\n        }\n        ImGui::EndChild();\n\n        ImGui::Text(\"Selected: %d/%d items\", Selection.Size, Items.Size);\n        ImGui::End();\n    }\n};\n\nvoid ShowExampleAppAssetsBrowser(bool* p_open)\n{\n    IMGUI_DEMO_MARKER(\"Examples/Assets Browser\");\n    static ExampleAssetsBrowser assets_browser;\n    assets_browser.Draw(\"Example: Assets Browser\", p_open);\n}\n\n// End of Demo code\n#else\n\nvoid ImGui::ShowAboutWindow(bool*) {}\nvoid ImGui::ShowDemoWindow(bool*) {}\nvoid ImGui::ShowUserGuide() {}\nvoid ImGui::ShowStyleEditor(ImGuiStyle*) {}\nbool ImGui::ShowStyleSelector(const char*) { return false; }\n\n#endif // #ifndef IMGUI_DISABLE_DEMO_WINDOWS\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/source/imgui_draw.cpp",
    "content": "// dear imgui, v1.92.7 WIP\n// (drawing and font code)\n\n/*\n\nIndex of this file:\n\n// [SECTION] STB libraries implementation\n// [SECTION] Style functions\n// [SECTION] ImDrawList\n// [SECTION] ImTriangulator, ImDrawList concave polygon fill\n// [SECTION] ImDrawList Shadow Primitives\n// [SECTION] ImDrawListSplitter\n// [SECTION] ImDrawData\n// [SECTION] Helpers ShadeVertsXXX functions\n// [SECTION] ImFontAtlasShadowTexConfig\n// [SECTION] ImFontConfig\n// [SECTION] ImFontAtlas, ImFontAtlasBuilder\n// [SECTION] ImFontAtlas: backend for stb_truetype\n// [SECTION] ImFontAtlas: glyph ranges helpers\n// [SECTION] ImFontGlyphRangesBuilder\n// [SECTION] ImFontBaked, ImFont\n// [SECTION] ImGui Internal Render Helpers\n// [SECTION] Decompression code\n// [SECTION] Default font data (ProggyClean.ttf)\n// [SECTION] Default font data (ProggyForever.ttf)\n\n*/\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#ifndef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS\n#endif\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_internal.h\"\n#ifdef IMGUI_ENABLE_FREETYPE\n#include \"misc/freetype/imgui_freetype.h\"\n#endif\n\n#include <stdio.h>      // vsnprintf, sscanf, printf\n#include <stdint.h>     // intptr_t\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4127)     // condition expression is constant\n#pragma warning (disable: 4505)     // unreferenced local function has been removed (stb stuff)\n#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).\n#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast                            // yes, they are more terse.\n#pragma clang diagnostic ignored \"-Wfloat-equal\"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok.\n#pragma clang diagnostic ignored \"-Wglobal-constructors\"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.\n#pragma clang diagnostic ignored \"-Wsign-conversion\"                // warning: implicit conversion changes signedness\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0\n#pragma clang diagnostic ignored \"-Wcomma\"                          // warning: possible misuse of comma operator here\n#pragma clang diagnostic ignored \"-Wreserved-id-macro\"              // warning: macro name is a reserved identifier\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#pragma clang diagnostic ignored \"-Wreserved-identifier\"            // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter\n#pragma clang diagnostic ignored \"-Wunsafe-buffer-usage\"            // warning: 'xxx' is an unsafe pointer used for buffer access\n#pragma clang diagnostic ignored \"-Wnontrivial-memaccess\"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type\n#pragma clang diagnostic ignored \"-Wcast-qual\"                      // warning: cast from 'const xxxx *' to 'xxx *' drops const qualifier\n#pragma clang diagnostic ignored \"-Wswitch-default\"                 // warning: 'switch' missing 'default' label\n#elif defined(__GNUC__)\n#pragma GCC diagnostic ignored \"-Wpragmas\"                          // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wunused-function\"                  // warning: 'xxxx' defined but not used\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"                      // warning: comparing floating-point with '==' or '!=' is unsafe\n#pragma GCC diagnostic ignored \"-Wdouble-promotion\"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function\n#pragma GCC diagnostic ignored \"-Wconversion\"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value\n#pragma GCC diagnostic ignored \"-Wstack-protector\"                  // warning: stack protector not protecting local variables: variable length buffer\n#pragma GCC diagnostic ignored \"-Wstrict-overflow\"                  // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#pragma GCC diagnostic ignored \"-Wcast-qual\"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"                  // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result\n#endif\n\n//-------------------------------------------------------------------------\n// [SECTION] STB libraries implementation (for stb_truetype and stb_rect_pack)\n//-------------------------------------------------------------------------\n\n// Compile time options:\n//#define IMGUI_STB_NAMESPACE           ImStb\n//#define IMGUI_STB_TRUETYPE_FILENAME   \"my_folder/stb_truetype.h\"\n//#define IMGUI_STB_RECT_PACK_FILENAME  \"my_folder/stb_rect_pack.h\"\n//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION\n//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION\n\n#ifdef IMGUI_STB_NAMESPACE\nnamespace IMGUI_STB_NAMESPACE\n{\n#endif\n\n#ifdef _MSC_VER\n#pragma warning (push)\n#pragma warning (disable: 4456)                             // declaration of 'xx' hides previous local declaration\n#pragma warning (disable: 6011)                             // (stb_rectpack) Dereferencing NULL pointer 'cur->next'.\n#pragma warning (disable: 5262)                             // (stb_truetype) implicit fall-through occurs here; are you missing a break statement? \n#pragma warning (disable: 6385)                             // (stb_truetype) Reading invalid data from 'buffer':  the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read.\n#pragma warning (disable: 28182)                            // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did.\n#endif\n\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunused-function\"        // warning: 'xxxx' defined but not used\n#pragma clang diagnostic ignored \"-Wmissing-prototypes\"\n#pragma clang diagnostic ignored \"-Wimplicit-fallthrough\"\n#endif\n\n#if defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wtype-limits\"              // warning: comparison is always true due to limited range of data type [-Wtype-limits]\n#pragma GCC diagnostic ignored \"-Wimplicit-fallthrough\"     // warning: this statement may fall through\n#endif\n\n#ifndef STB_RECT_PACK_IMPLEMENTATION                        // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)\n#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION          // in case the user already have an implementation in another compilation unit\n#define STBRP_STATIC\n#define STBRP_ASSERT(x)     do { IM_ASSERT(x); } while (0)\n#define STBRP_SORT          ImQsort\n#define STB_RECT_PACK_IMPLEMENTATION\n#endif\n#ifdef IMGUI_STB_RECT_PACK_FILENAME\n#include IMGUI_STB_RECT_PACK_FILENAME\n#else\n#include \"imstb_rectpack.h\"\n#endif\n#endif\n\n#ifdef  IMGUI_ENABLE_STB_TRUETYPE\n#ifndef STB_TRUETYPE_IMPLEMENTATION                         // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)\n#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION           // in case the user already have an implementation in another compilation unit\n#define STBTT_malloc(x,u)   ((void)(u), IM_ALLOC(x))\n#define STBTT_free(x,u)     ((void)(u), IM_FREE(x))\n#define STBTT_assert(x)     do { IM_ASSERT(x); } while(0)\n#define STBTT_fmod(x,y)     ImFmod(x,y)\n#define STBTT_sqrt(x)       ImSqrt(x)\n#define STBTT_pow(x,y)      ImPow(x,y)\n#define STBTT_fabs(x)       ImFabs(x)\n#define STBTT_ifloor(x)     ((int)ImFloor(x))\n#define STBTT_iceil(x)      ((int)ImCeil(x))\n#define STBTT_strlen(x)     ImStrlen(x)\n#define STBTT_STATIC\n#define STB_TRUETYPE_IMPLEMENTATION\n#else\n#define STBTT_DEF extern\n#endif\n#ifdef IMGUI_STB_TRUETYPE_FILENAME\n#include IMGUI_STB_TRUETYPE_FILENAME\n#else\n#include \"imstb_truetype.h\"\n#endif\n#endif\n#endif // IMGUI_ENABLE_STB_TRUETYPE\n\n#if defined(__GNUC__)\n#pragma GCC diagnostic pop\n#endif\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n\n#if defined(_MSC_VER)\n#pragma warning (pop)\n#endif\n\n#ifdef IMGUI_STB_NAMESPACE\n} // namespace ImStb\nusing namespace IMGUI_STB_NAMESPACE;\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Style functions\n//-----------------------------------------------------------------------------\n\nvoid ImGui::StyleColorsDark(ImGuiStyle* dst)\n{\n    ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();\n    ImVec4* colors = style->Colors;\n\n    colors[ImGuiCol_Text]                   = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImGuiCol_TextDisabled]           = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);\n    colors[ImGuiCol_WindowBg]               = ImVec4(0.06f, 0.06f, 0.06f, 0.94f);\n    colors[ImGuiCol_ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_PopupBg]                = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);\n    colors[ImGuiCol_Border]                 = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);\n    colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_FrameBg]                = ImVec4(0.16f, 0.29f, 0.48f, 0.54f);\n    colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);\n    colors[ImGuiCol_FrameBgActive]          = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);\n    colors[ImGuiCol_TitleBg]                = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);\n    colors[ImGuiCol_TitleBgActive]          = ImVec4(0.16f, 0.29f, 0.48f, 1.00f);\n    colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);\n    colors[ImGuiCol_MenuBarBg]              = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);\n    colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);\n    colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);\n    colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);\n    colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);\n    colors[ImGuiCol_CheckMark]              = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_SliderGrab]             = ImVec4(0.24f, 0.52f, 0.88f, 1.00f);\n    colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_Button]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);\n    colors[ImGuiCol_ButtonHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_ButtonActive]           = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);\n    colors[ImGuiCol_Header]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);\n    colors[ImGuiCol_HeaderHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);\n    colors[ImGuiCol_HeaderActive]           = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_Separator]              = colors[ImGuiCol_Border];\n    colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.10f, 0.40f, 0.75f, 0.78f);\n    colors[ImGuiCol_SeparatorActive]        = ImVec4(0.10f, 0.40f, 0.75f, 1.00f);\n    colors[ImGuiCol_ResizeGrip]             = ImVec4(0.26f, 0.59f, 0.98f, 0.20f);\n    colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);\n    colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);\n    colors[ImGuiCol_InputTextCursor]        = colors[ImGuiCol_Text];\n    colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered];\n    colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.80f);\n    colors[ImGuiCol_TabSelected]            = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);\n    colors[ImGuiCol_TabSelectedOverline]    = colors[ImGuiCol_HeaderActive];\n    colors[ImGuiCol_TabDimmed]              = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);\n    colors[ImGuiCol_TabDimmedSelected]      = ImLerp(colors[ImGuiCol_TabSelected],  colors[ImGuiCol_TitleBg], 0.40f);\n    colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 0.00f);\n    colors[ImGuiCol_DockingPreview]         = colors[ImGuiCol_HeaderActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);\n    colors[ImGuiCol_DockingEmptyBg]         = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);\n    colors[ImGuiCol_PlotLines]              = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);\n    colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);\n    colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);\n    colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);\n    colors[ImGuiCol_TableHeaderBg]          = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);\n    colors[ImGuiCol_TableBorderStrong]      = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableBorderLight]       = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_TableRowBgAlt]          = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);\n    colors[ImGuiCol_TextLink]               = colors[ImGuiCol_HeaderActive];\n    colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);\n    colors[ImGuiCol_TreeLines]              = colors[ImGuiCol_Border];\n    colors[ImGuiCol_DragDropTarget]         = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);\n    colors[ImGuiCol_DragDropTargetBg]       = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_UnsavedMarker]          = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImGuiCol_NavCursor]              = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);\n    colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);\n    colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);\n    colors[ImGuiCol_WindowShadow]           = ImVec4(0.08f, 0.08f, 0.08f, 0.35f);\n}\n\nvoid ImGui::StyleColorsClassic(ImGuiStyle* dst)\n{\n    ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();\n    ImVec4* colors = style->Colors;\n\n    colors[ImGuiCol_Text]                   = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImGuiCol_TextDisabled]           = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);\n    colors[ImGuiCol_WindowBg]               = ImVec4(0.00f, 0.00f, 0.00f, 0.85f);\n    colors[ImGuiCol_ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_PopupBg]                = ImVec4(0.11f, 0.11f, 0.14f, 0.92f);\n    colors[ImGuiCol_Border]                 = ImVec4(0.50f, 0.50f, 0.50f, 0.50f);\n    colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_FrameBg]                = ImVec4(0.43f, 0.43f, 0.43f, 0.39f);\n    colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.47f, 0.47f, 0.69f, 0.40f);\n    colors[ImGuiCol_FrameBgActive]          = ImVec4(0.42f, 0.41f, 0.64f, 0.69f);\n    colors[ImGuiCol_TitleBg]                = ImVec4(0.27f, 0.27f, 0.54f, 0.83f);\n    colors[ImGuiCol_TitleBgActive]          = ImVec4(0.32f, 0.32f, 0.63f, 0.87f);\n    colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);\n    colors[ImGuiCol_MenuBarBg]              = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);\n    colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);\n    colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);\n    colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);\n    colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.41f, 0.39f, 0.80f, 0.60f);\n    colors[ImGuiCol_CheckMark]              = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);\n    colors[ImGuiCol_SliderGrab]             = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);\n    colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.41f, 0.39f, 0.80f, 0.60f);\n    colors[ImGuiCol_Button]                 = ImVec4(0.35f, 0.40f, 0.61f, 0.62f);\n    colors[ImGuiCol_ButtonHovered]          = ImVec4(0.40f, 0.48f, 0.71f, 0.79f);\n    colors[ImGuiCol_ButtonActive]           = ImVec4(0.46f, 0.54f, 0.80f, 1.00f);\n    colors[ImGuiCol_Header]                 = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);\n    colors[ImGuiCol_HeaderHovered]          = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);\n    colors[ImGuiCol_HeaderActive]           = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);\n    colors[ImGuiCol_Separator]              = ImVec4(0.50f, 0.50f, 0.50f, 0.60f);\n    colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.60f, 0.60f, 0.70f, 1.00f);\n    colors[ImGuiCol_SeparatorActive]        = ImVec4(0.70f, 0.70f, 0.90f, 1.00f);\n    colors[ImGuiCol_ResizeGrip]             = ImVec4(1.00f, 1.00f, 1.00f, 0.10f);\n    colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);\n    colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.78f, 0.82f, 1.00f, 0.90f);\n    colors[ImGuiCol_InputTextCursor]        = colors[ImGuiCol_Text];\n    colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered];\n    colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.80f);\n    colors[ImGuiCol_TabSelected]            = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);\n    colors[ImGuiCol_TabSelectedOverline]    = colors[ImGuiCol_HeaderActive];\n    colors[ImGuiCol_TabDimmed]              = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);\n    colors[ImGuiCol_TabDimmedSelected]      = ImLerp(colors[ImGuiCol_TabSelected],  colors[ImGuiCol_TitleBg], 0.40f);\n    colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.53f, 0.53f, 0.87f, 0.00f);\n    colors[ImGuiCol_DockingPreview]         = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);\n    colors[ImGuiCol_DockingEmptyBg]         = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);\n    colors[ImGuiCol_PlotLines]              = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImGuiCol_PlotLinesHovered]       = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);\n    colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);\n    colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);\n    colors[ImGuiCol_TableHeaderBg]          = ImVec4(0.27f, 0.27f, 0.38f, 1.00f);\n    colors[ImGuiCol_TableBorderStrong]      = ImVec4(0.31f, 0.31f, 0.45f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableBorderLight]       = ImVec4(0.26f, 0.26f, 0.28f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_TableRowBgAlt]          = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);\n    colors[ImGuiCol_TextLink]               = colors[ImGuiCol_HeaderActive];\n    colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);\n    colors[ImGuiCol_TreeLines]              = colors[ImGuiCol_Border];\n    colors[ImGuiCol_DragDropTarget]         = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);\n    colors[ImGuiCol_DragDropTargetBg]       = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_UnsavedMarker]          = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImGuiCol_NavCursor]              = colors[ImGuiCol_HeaderHovered];\n    colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);\n    colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);\n    colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);\n    colors[ImGuiCol_WindowShadow]           = ImVec4(0.08f, 0.08f, 0.08f, 0.35f);\n}\n\n// Those light colors are better suited with a thicker font than the default one + FrameBorder\nvoid ImGui::StyleColorsLight(ImGuiStyle* dst)\n{\n    ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();\n    ImVec4* colors = style->Colors;\n\n    colors[ImGuiCol_Text]                   = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImGuiCol_TextDisabled]           = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);\n    colors[ImGuiCol_WindowBg]               = ImVec4(0.94f, 0.94f, 0.94f, 1.00f);\n    colors[ImGuiCol_ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_PopupBg]                = ImVec4(1.00f, 1.00f, 1.00f, 0.98f);\n    colors[ImGuiCol_Border]                 = ImVec4(0.00f, 0.00f, 0.00f, 0.30f);\n    colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_FrameBg]                = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);\n    colors[ImGuiCol_FrameBgActive]          = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);\n    colors[ImGuiCol_TitleBg]                = ImVec4(0.96f, 0.96f, 0.96f, 1.00f);\n    colors[ImGuiCol_TitleBgActive]          = ImVec4(0.82f, 0.82f, 0.82f, 1.00f);\n    colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(1.00f, 1.00f, 1.00f, 0.51f);\n    colors[ImGuiCol_MenuBarBg]              = ImVec4(0.86f, 0.86f, 0.86f, 1.00f);\n    colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.98f, 0.98f, 0.98f, 0.53f);\n    colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.69f, 0.69f, 0.69f, 0.80f);\n    colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.49f, 0.49f, 0.49f, 0.80f);\n    colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.49f, 0.49f, 0.49f, 1.00f);\n    colors[ImGuiCol_CheckMark]              = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_SliderGrab]             = ImVec4(0.26f, 0.59f, 0.98f, 0.78f);\n    colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.46f, 0.54f, 0.80f, 0.60f);\n    colors[ImGuiCol_Button]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);\n    colors[ImGuiCol_ButtonHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_ButtonActive]           = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);\n    colors[ImGuiCol_Header]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);\n    colors[ImGuiCol_HeaderHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);\n    colors[ImGuiCol_HeaderActive]           = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_Separator]              = ImVec4(0.39f, 0.39f, 0.39f, 0.62f);\n    colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.14f, 0.44f, 0.80f, 0.78f);\n    colors[ImGuiCol_SeparatorActive]        = ImVec4(0.14f, 0.44f, 0.80f, 1.00f);\n    colors[ImGuiCol_ResizeGrip]             = ImVec4(0.35f, 0.35f, 0.35f, 0.17f);\n    colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);\n    colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);\n    colors[ImGuiCol_InputTextCursor]        = colors[ImGuiCol_Text];\n    colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered];\n    colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.90f);\n    colors[ImGuiCol_TabSelected]            = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);\n    colors[ImGuiCol_TabSelectedOverline]    = colors[ImGuiCol_HeaderActive];\n    colors[ImGuiCol_TabDimmed]              = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);\n    colors[ImGuiCol_TabDimmedSelected]      = ImLerp(colors[ImGuiCol_TabSelected],  colors[ImGuiCol_TitleBg], 0.40f);\n    colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.26f, 0.59f, 1.00f, 0.00f);\n    colors[ImGuiCol_DockingPreview]         = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f);\n    colors[ImGuiCol_DockingEmptyBg]         = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);\n    colors[ImGuiCol_PlotLines]              = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);\n    colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);\n    colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);\n    colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.45f, 0.00f, 1.00f);\n    colors[ImGuiCol_TableHeaderBg]          = ImVec4(0.78f, 0.87f, 0.98f, 1.00f);\n    colors[ImGuiCol_TableBorderStrong]      = ImVec4(0.57f, 0.57f, 0.64f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableBorderLight]       = ImVec4(0.68f, 0.68f, 0.74f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_TableRowBgAlt]          = ImVec4(0.30f, 0.30f, 0.30f, 0.09f);\n    colors[ImGuiCol_TextLink]               = colors[ImGuiCol_HeaderActive];\n    colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);\n    colors[ImGuiCol_TreeLines]              = colors[ImGuiCol_Border];\n    colors[ImGuiCol_DragDropTarget]         = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);\n    colors[ImGuiCol_DragDropTargetBg]       = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_UnsavedMarker]          = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImGuiCol_NavCursor]              = colors[ImGuiCol_HeaderHovered];\n    colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(0.70f, 0.70f, 0.70f, 0.70f);\n    colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.20f, 0.20f, 0.20f, 0.20f);\n    colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);\n    colors[ImGuiCol_WindowShadow]           = ImVec4(0.08f, 0.08f, 0.08f, 0.35f);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFontAtlasShadowTexConfig\n//-----------------------------------------------------------------------------\n\nvoid ImFontAtlasShadowTexConfig::SetupDefaults()\n{\n    TexCornerSize = 16;\n    TexEdgeSize = 1;\n    TexFalloffPower = 4.8f;\n    TexDistanceFieldOffset = 3.8f;\n    TexBlur = true;\n}\n\nint ImFontAtlasShadowTexConfig::CalcConvexTexWidth() const\n{\n    // We have to pad the texture enough that we don't go off the edges when we expand the corner triangles\n    return (int)((TexCornerSize / ImCos(IM_PI * 0.25f)) + (GetConvexTexPadding() * 2));\n}\n\nint ImFontAtlasShadowTexConfig::CalcConvexTexHeight() const\n{\n    return CalcConvexTexWidth(); // Same value\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImDrawList\n//-----------------------------------------------------------------------------\n\nImDrawListSharedData::ImDrawListSharedData()\n{\n    memset((void*)this, 0, sizeof(*this));\n    InitialFringeScale = 1.0f;\n    for (int i = 0; i < IM_COUNTOF(ArcFastVtx); i++)\n    {\n        const float a = ((float)i * 2 * IM_PI) / (float)IM_COUNTOF(ArcFastVtx);\n        ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a));\n    }\n    ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);\n}\n\nImDrawListSharedData::~ImDrawListSharedData()\n{\n    IM_ASSERT(DrawLists.Size == 0);\n}\n\nvoid ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)\n{\n    if (CircleSegmentMaxError == max_error)\n        return;\n\n    IM_ASSERT(max_error > 0.0f);\n    CircleSegmentMaxError = max_error;\n    for (int i = 0; i < IM_COUNTOF(CircleSegmentCounts); i++)\n    {\n        const float radius = (float)i;\n        CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : IM_DRAWLIST_ARCFAST_SAMPLE_MAX);\n    }\n    ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);\n}\n\nImDrawList::ImDrawList(ImDrawListSharedData* shared_data)\n{\n    memset((void*)this, 0, sizeof(*this));\n    _SetDrawListSharedData(shared_data);\n}\n\nImDrawList::~ImDrawList()\n{\n    _ClearFreeMemory();\n    _SetDrawListSharedData(NULL);\n}\n\nvoid ImDrawList::_SetDrawListSharedData(ImDrawListSharedData* data)\n{\n    if (_Data != NULL)\n        _Data->DrawLists.find_erase_unsorted(this);\n    _Data = data;\n    if (_Data != NULL)\n        _Data->DrawLists.push_back(this);\n}\n\n// Initialize before use in a new frame. We always have a command ready in the buffer.\n// In the majority of cases, you would want to call PushClipRect() and PushTexture() after this.\nvoid ImDrawList::_ResetForNewFrame()\n{\n    // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory to match ImDrawCmdHeader.\n    IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0);\n    IM_STATIC_ASSERT(offsetof(ImDrawCmd, TexRef) == sizeof(ImVec4));\n    IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureRef));\n    IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == offsetof(ImDrawCmdHeader, ClipRect));\n    IM_STATIC_ASSERT(offsetof(ImDrawCmd, TexRef) == offsetof(ImDrawCmdHeader, TexRef));\n    IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == offsetof(ImDrawCmdHeader, VtxOffset));\n    if (_Splitter._Count > 1)\n        _Splitter.Merge(this);\n\n    CmdBuffer.resize(0);\n    IdxBuffer.resize(0);\n    VtxBuffer.resize(0);\n    Flags = _Data->InitialFlags;\n    memset(&_CmdHeader, 0, sizeof(_CmdHeader));\n    _VtxCurrentIdx = 0;\n    _VtxWritePtr = NULL;\n    _IdxWritePtr = NULL;\n    _ClipRectStack.resize(0);\n    _TextureStack.resize(0);\n    _CallbacksDataBuf.resize(0);\n    _Path.resize(0);\n    _Splitter.Clear();\n    CmdBuffer.push_back(ImDrawCmd());\n    _FringeScale = _Data->InitialFringeScale;\n}\n\nvoid ImDrawList::_ClearFreeMemory()\n{\n    CmdBuffer.clear();\n    IdxBuffer.clear();\n    VtxBuffer.clear();\n    Flags = ImDrawListFlags_None;\n    _VtxCurrentIdx = 0;\n    _VtxWritePtr = NULL;\n    _IdxWritePtr = NULL;\n    _ClipRectStack.clear();\n    _TextureStack.clear();\n    _CallbacksDataBuf.clear();\n    _Path.clear();\n    _Splitter.ClearFreeMemory();\n}\n\n// Note: For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club\nImDrawList* ImDrawList::CloneOutput() const\n{\n    ImDrawList* dst = IM_NEW(ImDrawList(NULL));\n    dst->CmdBuffer = CmdBuffer;\n    dst->IdxBuffer = IdxBuffer;\n    dst->VtxBuffer = VtxBuffer;\n    dst->Flags = Flags;\n    return dst;\n}\n\nvoid ImDrawList::AddDrawCmd()\n{\n    ImDrawCmd draw_cmd;\n    draw_cmd.ClipRect = _CmdHeader.ClipRect;    // Same as calling ImDrawCmd_HeaderCopy()\n    draw_cmd.TexRef = _CmdHeader.TexRef;\n    draw_cmd.VtxOffset = _CmdHeader.VtxOffset;\n    draw_cmd.IdxOffset = IdxBuffer.Size;\n\n    IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w);\n    CmdBuffer.push_back(draw_cmd);\n}\n\n// Pop trailing draw command (used before merging or presenting to user)\n// Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL\nvoid ImDrawList::_PopUnusedDrawCmd()\n{\n    while (CmdBuffer.Size > 0)\n    {\n        ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n        if (curr_cmd->ElemCount != 0 || curr_cmd->UserCallback != NULL)\n            return;// break;\n        CmdBuffer.pop_back();\n    }\n}\n\nvoid ImDrawList::AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size)\n{\n    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);\n    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    IM_ASSERT(callback != NULL);\n    IM_ASSERT(curr_cmd->UserCallback == NULL);\n    if (curr_cmd->ElemCount != 0)\n    {\n        AddDrawCmd();\n        curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    }\n\n    curr_cmd->UserCallback = callback;\n    if (userdata_size == 0)\n    {\n        // Store user data directly in command (no indirection)\n        curr_cmd->UserCallbackData = userdata;\n        curr_cmd->UserCallbackDataSize = 0;\n        curr_cmd->UserCallbackDataOffset = -1;\n    }\n    else\n    {\n        // Copy and store user data in a buffer\n        IM_ASSERT(userdata != NULL);\n        IM_ASSERT(userdata_size < (1u << 31));\n        curr_cmd->UserCallbackData = NULL; // Will be resolved during Render()\n        curr_cmd->UserCallbackDataSize = (int)userdata_size;\n        curr_cmd->UserCallbackDataOffset = _CallbacksDataBuf.Size;\n        _CallbacksDataBuf.resize(_CallbacksDataBuf.Size + (int)userdata_size);\n        memcpy(_CallbacksDataBuf.Data + (size_t)curr_cmd->UserCallbackDataOffset, userdata, userdata_size);\n    }\n\n    AddDrawCmd(); // Force a new command after us (see comment below)\n}\n\n// Compare ClipRect, TexRef and VtxOffset with a single memcmp()\n#define ImDrawCmd_HeaderSize                            (offsetof(ImDrawCmd, VtxOffset) + sizeof(unsigned int))\n#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS)       (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize))    // Compare ClipRect, TexRef, VtxOffset\n#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC)          (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize))    // Copy ClipRect, TexRef, VtxOffset\n#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1)  (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)\n\n// Try to merge two last draw commands\nvoid ImDrawList::_TryMergeDrawCmds()\n{\n    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);\n    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    ImDrawCmd* prev_cmd = curr_cmd - 1;\n    if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL)\n    {\n        prev_cmd->ElemCount += curr_cmd->ElemCount;\n        CmdBuffer.pop_back();\n    }\n}\n\n// Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack.\n// The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only.\nvoid ImDrawList::_OnChangedClipRect()\n{\n    // If current command is used with different settings we need to add a new command\n    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);\n    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0)\n    {\n        AddDrawCmd();\n        return;\n    }\n    IM_ASSERT(curr_cmd->UserCallback == NULL);\n\n    // Try to merge with previous command if it matches, else use current command\n    ImDrawCmd* prev_cmd = curr_cmd - 1;\n    if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL)\n    {\n        CmdBuffer.pop_back();\n        return;\n    }\n    curr_cmd->ClipRect = _CmdHeader.ClipRect;\n}\n\nvoid ImDrawList::_OnChangedTexture()\n{\n    // If current command is used with different settings we need to add a new command\n    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);\n    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    if (curr_cmd->ElemCount != 0 && curr_cmd->TexRef != _CmdHeader.TexRef)\n    {\n        AddDrawCmd();\n        return;\n    }\n\n    // Unlike other _OnChangedXXX functions this may be called by ImFontAtlasUpdateDrawListsTextures() in more locations so we need to handle this case.\n    if (curr_cmd->UserCallback != NULL)\n        return;\n\n    // Try to merge with previous command if it matches, else use current command\n    ImDrawCmd* prev_cmd = curr_cmd - 1;\n    if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL)\n    {\n        CmdBuffer.pop_back();\n        return;\n    }\n    curr_cmd->TexRef = _CmdHeader.TexRef;\n}\n\nvoid ImDrawList::_OnChangedVtxOffset()\n{\n    // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this.\n    _VtxCurrentIdx = 0;\n    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);\n    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349\n    if (curr_cmd->ElemCount != 0)\n    {\n        AddDrawCmd();\n        return;\n    }\n    IM_ASSERT(curr_cmd->UserCallback == NULL);\n    curr_cmd->VtxOffset = _CmdHeader.VtxOffset;\n}\n\nint ImDrawList::_CalcCircleAutoSegmentCount(float radius) const\n{\n    // Automatic segment count\n    const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy\n    if (radius_idx >= 0 && radius_idx < IM_COUNTOF(_Data->CircleSegmentCounts))\n        return _Data->CircleSegmentCounts[radius_idx]; // Use cached value\n    else\n        return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError);\n}\n\n// Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)\nvoid ImDrawList::PushClipRect(const ImVec2& cr_min, const ImVec2& cr_max, bool intersect_with_current_clip_rect)\n{\n    ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y);\n    if (intersect_with_current_clip_rect)\n    {\n        ImVec4 current = _CmdHeader.ClipRect;\n        if (cr.x < current.x) cr.x = current.x;\n        if (cr.y < current.y) cr.y = current.y;\n        if (cr.z > current.z) cr.z = current.z;\n        if (cr.w > current.w) cr.w = current.w;\n    }\n    cr.z = ImMax(cr.x, cr.z);\n    cr.w = ImMax(cr.y, cr.w);\n\n    _ClipRectStack.push_back(cr);\n    _CmdHeader.ClipRect = cr;\n    _OnChangedClipRect();\n}\n\nvoid ImDrawList::PushClipRectFullScreen()\n{\n    PushClipRect(ImVec2(_Data->ClipRectFullscreen.x, _Data->ClipRectFullscreen.y), ImVec2(_Data->ClipRectFullscreen.z, _Data->ClipRectFullscreen.w));\n}\n\nvoid ImDrawList::PopClipRect()\n{\n    _ClipRectStack.pop_back();\n    _CmdHeader.ClipRect = (_ClipRectStack.Size == 0) ? _Data->ClipRectFullscreen : _ClipRectStack.Data[_ClipRectStack.Size - 1];\n    _OnChangedClipRect();\n}\n\nvoid ImDrawList::PushTexture(ImTextureRef tex_ref)\n{\n    _TextureStack.push_back(tex_ref);\n    _CmdHeader.TexRef = tex_ref;\n    if (tex_ref._TexData != NULL)\n        IM_ASSERT(tex_ref._TexData->WantDestroyNextFrame == false);\n    _OnChangedTexture();\n}\n\nvoid ImDrawList::PopTexture()\n{\n    _TextureStack.pop_back();\n    _CmdHeader.TexRef = (_TextureStack.Size == 0) ? ImTextureRef() : _TextureStack.Data[_TextureStack.Size - 1];\n    _OnChangedTexture();\n}\n\n// This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTexture()/PopTexture().\nvoid ImDrawList::_SetTexture(ImTextureRef tex_ref)\n{\n    if (_CmdHeader.TexRef == tex_ref)\n        return;\n    _CmdHeader.TexRef = tex_ref;\n    _TextureStack.back() = tex_ref;\n    _OnChangedTexture();\n}\n\n// Reserve space for a number of vertices and indices.\n// You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or\n// submit the intermediate results. PrimUnreserve() can be used to release unused allocations.\nvoid ImDrawList::PrimReserve(int idx_count, int vtx_count)\n{\n    // Large mesh support (when enabled)\n    IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);\n    if (sizeof(ImDrawIdx) == 2 && (_VtxCurrentIdx + vtx_count >= (1 << 16)) && (Flags & ImDrawListFlags_AllowVtxOffset))\n    {\n        // FIXME: In theory we should be testing that vtx_count <64k here.\n        // In practice, RenderText() relies on reserving ahead for a worst case scenario so it is currently useful for us\n        // to not make that check until we rework the text functions to handle clipping and large horizontal lines better.\n        _CmdHeader.VtxOffset = VtxBuffer.Size;\n        _OnChangedVtxOffset();\n    }\n\n    ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    draw_cmd->ElemCount += idx_count;\n\n    int vtx_buffer_old_size = VtxBuffer.Size;\n    VtxBuffer.resize(vtx_buffer_old_size + vtx_count);\n    _VtxWritePtr = VtxBuffer.Data + vtx_buffer_old_size;\n\n    int idx_buffer_old_size = IdxBuffer.Size;\n    IdxBuffer.resize(idx_buffer_old_size + idx_count);\n    _IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size;\n}\n\n// Release the number of reserved vertices/indices from the end of the last reservation made with PrimReserve().\nvoid ImDrawList::PrimUnreserve(int idx_count, int vtx_count)\n{\n    IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);\n\n    ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    draw_cmd->ElemCount -= idx_count;\n    VtxBuffer.shrink(VtxBuffer.Size - vtx_count);\n    IdxBuffer.shrink(IdxBuffer.Size - idx_count);\n}\n\n// Fully unrolled with inline call to keep our debug builds decently fast.\nvoid ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col)\n{\n    ImVec2 b(c.x, a.y), d(a.x, c.y), uv(_Data->TexUvWhitePixel);\n    ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;\n    _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);\n    _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);\n    _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;\n    _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col;\n    _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col;\n    _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col;\n    _VtxWritePtr += 4;\n    _VtxCurrentIdx += 4;\n    _IdxWritePtr += 6;\n}\n\nvoid ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col)\n{\n    ImVec2 b(c.x, a.y), d(a.x, c.y), uv_b(uv_c.x, uv_a.y), uv_d(uv_a.x, uv_c.y);\n    ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;\n    _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);\n    _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);\n    _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col;\n    _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col;\n    _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col;\n    _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col;\n    _VtxWritePtr += 4;\n    _VtxCurrentIdx += 4;\n    _IdxWritePtr += 6;\n}\n\nvoid ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col)\n{\n    ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;\n    _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);\n    _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);\n    _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col;\n    _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col;\n    _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col;\n    _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col;\n    _VtxWritePtr += 4;\n    _VtxCurrentIdx += 4;\n    _IdxWritePtr += 6;\n}\n\n// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds.\n// - Those macros expects l-values and need to be used as their own statement.\n// - Those macros are intentionally not surrounded by the 'do {} while (0)' idiom because even that translates to runtime with debug compilers.\n#define IM_NORMALIZE2F_OVER_ZERO(VX,VY)     { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0\n#define IM_FIXNORMAL2F_MAX_INVLEN2          100.0f // 500.0f (see #4053, #3366)\n#define IM_FIXNORMAL2F(VX,VY)               { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } (void)0\n\n// TODO: Thickness anti-aliased lines cap are missing their AA fringe.\n// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.\nvoid ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, ImDrawFlags flags, float thickness)\n{\n    if (points_count < 2 || (col & IM_COL32_A_MASK) == 0)\n        return;\n\n    const bool closed = (flags & ImDrawFlags_Closed) != 0;\n    const ImVec2 opaque_uv = _Data->TexUvWhitePixel;\n    const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw\n    const bool thick_line = (thickness > _FringeScale);\n\n    if (Flags & ImDrawListFlags_AntiAliasedLines)\n    {\n        // Anti-aliased stroke\n        const float AA_SIZE = _FringeScale;\n        const ImU32 col_trans = col & ~IM_COL32_A_MASK;\n\n        // Thicknesses <1.0 should behave like thickness 1.0\n        thickness = ImMax(thickness, 1.0f);\n        const int integer_thickness = (int)thickness;\n        const float fractional_thickness = thickness - integer_thickness;\n\n        // Do we want to draw this line using a texture?\n        // - For now, only draw integer-width lines using textures to avoid issues with the way scaling occurs, could be improved.\n        // - If AA_SIZE is not 1.0f we cannot use the texture path.\n        const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f) && (AA_SIZE == 1.0f);\n\n        // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off\n        IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedLines));\n\n        const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12);\n        const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3);\n        PrimReserve(idx_count, vtx_count);\n\n        // Temporary buffer\n        // The first <points_count> items are normals at each line point, then after that there are either 2 or 4 temp points for each line point\n        _Data->TempBuffer.reserve_discard(points_count * ((use_texture || !thick_line) ? 3 : 5));\n        ImVec2* temp_normals = _Data->TempBuffer.Data;\n        ImVec2* temp_points = temp_normals + points_count;\n\n        // Calculate normals (tangents) for each line segment\n        for (int i1 = 0; i1 < count; i1++)\n        {\n            const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1;\n            float dx = points[i2].x - points[i1].x;\n            float dy = points[i2].y - points[i1].y;\n            IM_NORMALIZE2F_OVER_ZERO(dx, dy);\n            temp_normals[i1].x = dy;\n            temp_normals[i1].y = -dx;\n        }\n        if (!closed)\n            temp_normals[points_count - 1] = temp_normals[points_count - 2];\n\n        // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point\n        if (use_texture || !thick_line)\n        {\n            // [PATH 1] Texture-based lines (thick or non-thick)\n            // [PATH 2] Non texture-based lines (non-thick)\n\n            // The width of the geometry we need to draw - this is essentially <thickness> pixels for the line itself, plus \"one pixel\" for AA.\n            // - In the texture-based path, we don't use AA_SIZE here because the +1 is tied to the generated texture\n            //   (see ImFontAtlasBuildRenderLinesTexData() function), and so alternate values won't work without changes to that code.\n            // - In the non texture-based paths, we would allow AA_SIZE to potentially be != 1.0f with a patch (e.g. fringe_scale patch to\n            //   allow scaling geometry while preserving one-screen-pixel AA fringe).\n            const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : AA_SIZE;\n\n            // If line is not closed, the first and last points need to be generated differently as there are no normals to blend\n            if (!closed)\n            {\n                temp_points[0] = points[0] + temp_normals[0] * half_draw_size;\n                temp_points[1] = points[0] - temp_normals[0] * half_draw_size;\n                temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * half_draw_size;\n                temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * half_draw_size;\n            }\n\n            // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges\n            // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps)\n            // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer.\n            unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment\n            for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment\n            {\n                const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; // i2 is the second point of the line segment\n                const unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_texture ? 2 : 3)); // Vertex index for end of segment\n\n                // Average normals\n                float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;\n                float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;\n                IM_FIXNORMAL2F(dm_x, dm_y);\n                dm_x *= half_draw_size; // dm_x, dm_y are offset to the outer edge of the AA area\n                dm_y *= half_draw_size;\n\n                // Add temporary vertices for the outer edges\n                ImVec2* out_vtx = &temp_points[i2 * 2];\n                out_vtx[0].x = points[i2].x + dm_x;\n                out_vtx[0].y = points[i2].y + dm_y;\n                out_vtx[1].x = points[i2].x - dm_x;\n                out_vtx[1].y = points[i2].y - dm_y;\n\n                if (use_texture)\n                {\n                    // Add indices for two triangles\n                    _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 1); // Right tri\n                    _IdxWritePtr[3] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[4] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Left tri\n                    _IdxWritePtr += 6;\n                }\n                else\n                {\n                    // Add indexes for four triangles\n                    _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 2); // Right tri 1\n                    _IdxWritePtr[3] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Right tri 2\n                    _IdxWritePtr[6] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8] = (ImDrawIdx)(idx1 + 0); // Left tri 1\n                    _IdxWritePtr[9] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1); // Left tri 2\n                    _IdxWritePtr += 12;\n                }\n\n                idx1 = idx2;\n            }\n\n            // Add vertices for each point on the line\n            if (use_texture)\n            {\n                // If we're using textures we only need to emit the left/right edge vertices\n                ImVec4 tex_uvs = _Data->TexUvLines[integer_thickness];\n                /*if (fractional_thickness != 0.0f) // Currently always zero when use_texture==false!\n                {\n                    const ImVec4 tex_uvs_1 = _Data->TexUvLines[integer_thickness + 1];\n                    tex_uvs.x = tex_uvs.x + (tex_uvs_1.x - tex_uvs.x) * fractional_thickness; // inlined ImLerp()\n                    tex_uvs.y = tex_uvs.y + (tex_uvs_1.y - tex_uvs.y) * fractional_thickness;\n                    tex_uvs.z = tex_uvs.z + (tex_uvs_1.z - tex_uvs.z) * fractional_thickness;\n                    tex_uvs.w = tex_uvs.w + (tex_uvs_1.w - tex_uvs.w) * fractional_thickness;\n                }*/\n                ImVec2 tex_uv0(tex_uvs.x, tex_uvs.y);\n                ImVec2 tex_uv1(tex_uvs.z, tex_uvs.w);\n                for (int i = 0; i < points_count; i++)\n                {\n                    _VtxWritePtr[0].pos = temp_points[i * 2 + 0]; _VtxWritePtr[0].uv = tex_uv0; _VtxWritePtr[0].col = col; // Left-side outer edge\n                    _VtxWritePtr[1].pos = temp_points[i * 2 + 1]; _VtxWritePtr[1].uv = tex_uv1; _VtxWritePtr[1].col = col; // Right-side outer edge\n                    _VtxWritePtr += 2;\n                }\n            }\n            else\n            {\n                // If we're not using a texture, we need the center vertex as well\n                for (int i = 0; i < points_count; i++)\n                {\n                    _VtxWritePtr[0].pos = points[i];              _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col;       // Center of line\n                    _VtxWritePtr[1].pos = temp_points[i * 2 + 0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; // Left-side outer edge\n                    _VtxWritePtr[2].pos = temp_points[i * 2 + 1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; // Right-side outer edge\n                    _VtxWritePtr += 3;\n                }\n            }\n        }\n        else\n        {\n            // [PATH 2] Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point\n            const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f;\n\n            // If line is not closed, the first and last points need to be generated differently as there are no normals to blend\n            if (!closed)\n            {\n                const int points_last = points_count - 1;\n                temp_points[0] = points[0] + temp_normals[0] * (half_inner_thickness + AA_SIZE);\n                temp_points[1] = points[0] + temp_normals[0] * (half_inner_thickness);\n                temp_points[2] = points[0] - temp_normals[0] * (half_inner_thickness);\n                temp_points[3] = points[0] - temp_normals[0] * (half_inner_thickness + AA_SIZE);\n                temp_points[points_last * 4 + 0] = points[points_last] + temp_normals[points_last] * (half_inner_thickness + AA_SIZE);\n                temp_points[points_last * 4 + 1] = points[points_last] + temp_normals[points_last] * (half_inner_thickness);\n                temp_points[points_last * 4 + 2] = points[points_last] - temp_normals[points_last] * (half_inner_thickness);\n                temp_points[points_last * 4 + 3] = points[points_last] - temp_normals[points_last] * (half_inner_thickness + AA_SIZE);\n            }\n\n            // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges\n            // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps)\n            // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer.\n            unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment\n            for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment\n            {\n                const int i2 = (i1 + 1) == points_count ? 0 : (i1 + 1); // i2 is the second point of the line segment\n                const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : (idx1 + 4); // Vertex index for end of segment\n\n                // Average normals\n                float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;\n                float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;\n                IM_FIXNORMAL2F(dm_x, dm_y);\n                float dm_out_x = dm_x * (half_inner_thickness + AA_SIZE);\n                float dm_out_y = dm_y * (half_inner_thickness + AA_SIZE);\n                float dm_in_x = dm_x * half_inner_thickness;\n                float dm_in_y = dm_y * half_inner_thickness;\n\n                // Add temporary vertices\n                ImVec2* out_vtx = &temp_points[i2 * 4];\n                out_vtx[0].x = points[i2].x + dm_out_x;\n                out_vtx[0].y = points[i2].y + dm_out_y;\n                out_vtx[1].x = points[i2].x + dm_in_x;\n                out_vtx[1].y = points[i2].y + dm_in_y;\n                out_vtx[2].x = points[i2].x - dm_in_x;\n                out_vtx[2].y = points[i2].y - dm_in_y;\n                out_vtx[3].x = points[i2].x - dm_out_x;\n                out_vtx[3].y = points[i2].y - dm_out_y;\n\n                // Add indexes\n                _IdxWritePtr[0]  = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[1]  = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[2]  = (ImDrawIdx)(idx1 + 2);\n                _IdxWritePtr[3]  = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4]  = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5]  = (ImDrawIdx)(idx2 + 1);\n                _IdxWritePtr[6]  = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7]  = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8]  = (ImDrawIdx)(idx1 + 0);\n                _IdxWritePtr[9]  = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1);\n                _IdxWritePtr[12] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[13] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[14] = (ImDrawIdx)(idx1 + 3);\n                _IdxWritePtr[15] = (ImDrawIdx)(idx1 + 3); _IdxWritePtr[16] = (ImDrawIdx)(idx2 + 3); _IdxWritePtr[17] = (ImDrawIdx)(idx2 + 2);\n                _IdxWritePtr += 18;\n\n                idx1 = idx2;\n            }\n\n            // Add vertices\n            for (int i = 0; i < points_count; i++)\n            {\n                _VtxWritePtr[0].pos = temp_points[i * 4 + 0]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col_trans;\n                _VtxWritePtr[1].pos = temp_points[i * 4 + 1]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col;\n                _VtxWritePtr[2].pos = temp_points[i * 4 + 2]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col;\n                _VtxWritePtr[3].pos = temp_points[i * 4 + 3]; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col_trans;\n                _VtxWritePtr += 4;\n            }\n        }\n        _VtxCurrentIdx += (ImDrawIdx)vtx_count;\n    }\n    else\n    {\n        // [PATH 4] Non texture-based, Non anti-aliased lines\n        const int idx_count = count * 6;\n        const int vtx_count = count * 4;    // FIXME-OPT: Not sharing edges\n        PrimReserve(idx_count, vtx_count);\n\n        for (int i1 = 0; i1 < count; i1++)\n        {\n            const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1;\n            const ImVec2& p1 = points[i1];\n            const ImVec2& p2 = points[i2];\n\n            float dx = p2.x - p1.x;\n            float dy = p2.y - p1.y;\n            IM_NORMALIZE2F_OVER_ZERO(dx, dy);\n            dx *= (thickness * 0.5f);\n            dy *= (thickness * 0.5f);\n\n            _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col;\n            _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col;\n            _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col;\n            _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col;\n            _VtxWritePtr += 4;\n\n            _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + 2);\n            _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx + 2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx + 3);\n            _IdxWritePtr += 6;\n            _VtxCurrentIdx += 4;\n        }\n    }\n}\n\n// - We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds.\n// - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have \"inward\" anti-aliasing.\nvoid ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col)\n{\n    if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)\n        return;\n\n    const ImVec2 uv = _Data->TexUvWhitePixel;\n\n    if (Flags & ImDrawListFlags_AntiAliasedFill)\n    {\n        // Anti-aliased Fill\n        const float AA_SIZE = _FringeScale;\n        const ImU32 col_trans = col & ~IM_COL32_A_MASK;\n        const int idx_count = (points_count - 2)*3 + points_count * 6;\n        const int vtx_count = (points_count * 2);\n        PrimReserve(idx_count, vtx_count);\n\n        // Add indexes for fill\n        unsigned int vtx_inner_idx = _VtxCurrentIdx;\n        unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;\n        for (int i = 2; i < points_count; i++)\n        {\n            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + ((i - 1) << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (i << 1));\n            _IdxWritePtr += 3;\n        }\n\n        // Compute normals\n        _Data->TempBuffer.reserve_discard(points_count);\n        ImVec2* temp_normals = _Data->TempBuffer.Data;\n        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)\n        {\n            const ImVec2& p0 = points[i0];\n            const ImVec2& p1 = points[i1];\n            float dx = p1.x - p0.x;\n            float dy = p1.y - p0.y;\n            IM_NORMALIZE2F_OVER_ZERO(dx, dy);\n            temp_normals[i0].x = dy;\n            temp_normals[i0].y = -dx;\n        }\n\n        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)\n        {\n            // Average normals\n            const ImVec2& n0 = temp_normals[i0];\n            const ImVec2& n1 = temp_normals[i1];\n            float dm_x = (n0.x + n1.x) * 0.5f;\n            float dm_y = (n0.y + n1.y) * 0.5f;\n            IM_FIXNORMAL2F(dm_x, dm_y);\n            dm_x *= AA_SIZE * 0.5f;\n            dm_y *= AA_SIZE * 0.5f;\n\n            // Add vertices\n            _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;        // Inner\n            _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans;  // Outer\n            _VtxWritePtr += 2;\n\n            // Add indexes for fringes\n            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));\n            _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));\n            _IdxWritePtr += 6;\n        }\n        _VtxCurrentIdx += (ImDrawIdx)vtx_count;\n    }\n    else\n    {\n        // Non Anti-aliased Fill\n        const int idx_count = (points_count - 2)*3;\n        const int vtx_count = points_count;\n        PrimReserve(idx_count, vtx_count);\n        for (int i = 0; i < vtx_count; i++)\n        {\n            _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;\n            _VtxWritePtr++;\n        }\n        for (int i = 2; i < points_count; i++)\n        {\n            _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + i - 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + i);\n            _IdxWritePtr += 3;\n        }\n        _VtxCurrentIdx += (ImDrawIdx)vtx_count;\n    }\n}\n\nvoid ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step)\n{\n    if (radius < 0.5f)\n    {\n        _Path.push_back(center);\n        return;\n    }\n\n    // Calculate arc auto segment step size\n    if (a_step <= 0)\n        a_step = IM_DRAWLIST_ARCFAST_SAMPLE_MAX / _CalcCircleAutoSegmentCount(radius);\n\n    // Make sure we never do steps larger than one quarter of the circle\n    a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4);\n\n    const int sample_range = ImAbs(a_max_sample - a_min_sample);\n    const int a_next_step = a_step;\n\n    int samples = sample_range + 1;\n    bool extra_max_sample = false;\n    if (a_step > 1)\n    {\n        samples            = sample_range / a_step + 1;\n        const int overstep = sample_range % a_step;\n\n        if (overstep > 0)\n        {\n            extra_max_sample = true;\n            samples++;\n\n            // When we have overstep to avoid awkwardly looking one long line and one tiny one at the end,\n            // distribute first step range evenly between them by reducing first step size.\n            if (sample_range > 0)\n                a_step -= (a_step - overstep) / 2;\n        }\n    }\n\n    _Path.resize(_Path.Size + samples);\n    ImVec2* out_ptr = _Path.Data + (_Path.Size - samples);\n\n    int sample_index = a_min_sample;\n    if (sample_index < 0 || sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)\n    {\n        sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n        if (sample_index < 0)\n            sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n    }\n\n    if (a_max_sample >= a_min_sample)\n    {\n        for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step)\n        {\n            // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more\n            if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)\n                sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n\n            const ImVec2 s = _Data->ArcFastVtx[sample_index];\n            out_ptr->x = center.x + s.x * radius;\n            out_ptr->y = center.y + s.y * radius;\n            out_ptr++;\n        }\n    }\n    else\n    {\n        for (int a = a_min_sample; a >= a_max_sample; a -= a_step, sample_index -= a_step, a_step = a_next_step)\n        {\n            // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more\n            if (sample_index < 0)\n                sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n\n            const ImVec2 s = _Data->ArcFastVtx[sample_index];\n            out_ptr->x = center.x + s.x * radius;\n            out_ptr->y = center.y + s.y * radius;\n            out_ptr++;\n        }\n    }\n\n    if (extra_max_sample)\n    {\n        int normalized_max_sample = a_max_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n        if (normalized_max_sample < 0)\n            normalized_max_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n\n        const ImVec2 s = _Data->ArcFastVtx[normalized_max_sample];\n        out_ptr->x = center.x + s.x * radius;\n        out_ptr->y = center.y + s.y * radius;\n        out_ptr++;\n    }\n\n    IM_ASSERT_PARANOID(_Path.Data + _Path.Size == out_ptr);\n}\n\nvoid ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)\n{\n    if (radius < 0.5f)\n    {\n        _Path.push_back(center);\n        return;\n    }\n\n    // Note that we are adding a point at both a_min and a_max.\n    // If you are trying to draw a full closed circle you don't want the overlapping points!\n    _Path.reserve(_Path.Size + (num_segments + 1));\n    for (int i = 0; i <= num_segments; i++)\n    {\n        const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);\n        _Path.push_back(ImVec2(center.x + ImCos(a) * radius, center.y + ImSin(a) * radius));\n    }\n}\n\n// 0: East, 3: South, 6: West, 9: North, 12: East\nvoid ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12)\n{\n    if (radius < 0.5f)\n    {\n        _Path.push_back(center);\n        return;\n    }\n    _PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0);\n}\n\nvoid ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)\n{\n    if (radius < 0.5f)\n    {\n        _Path.push_back(center);\n        return;\n    }\n\n    if (num_segments > 0)\n    {\n        _PathArcToN(center, radius, a_min, a_max, num_segments);\n        return;\n    }\n\n    // Automatic segment count\n    if (radius <= _Data->ArcFastRadiusCutoff)\n    {\n        const bool a_is_reverse = a_max < a_min;\n\n        // We are going to use precomputed values for mid samples.\n        // Determine first and last sample in lookup table that belong to the arc.\n        const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f);\n        const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f);\n\n        const int a_min_sample = a_is_reverse ? (int)ImFloor(a_min_sample_f) : (int)ImCeil(a_min_sample_f);\n        const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloor(a_max_sample_f);\n        const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0);\n\n        const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n        const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n        const bool a_emit_start = ImAbs(a_min_segment_angle - a_min) >= 1e-5f;\n        const bool a_emit_end = ImAbs(a_max - a_max_segment_angle) >= 1e-5f;\n\n        _Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0)));\n        if (a_emit_start)\n            _Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius));\n        if (a_mid_samples > 0)\n            _PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0);\n        if (a_emit_end)\n            _Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius));\n    }\n    else\n    {\n        const float arc_length = ImAbs(a_max - a_min);\n        const int circle_segment_count = _CalcCircleAutoSegmentCount(radius);\n        const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length));\n        _PathArcToN(center, radius, a_min, a_max, arc_segment_count);\n    }\n}\n\nvoid ImDrawList::PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments)\n{\n    if (num_segments <= 0)\n        num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.\n\n    _Path.reserve(_Path.Size + (num_segments + 1));\n\n    const float cos_rot = ImCos(rot);\n    const float sin_rot = ImSin(rot);\n    for (int i = 0; i <= num_segments; i++)\n    {\n        const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);\n        ImVec2 point(ImCos(a) * radius.x, ImSin(a) * radius.y);\n        const ImVec2 rel((point.x * cos_rot) - (point.y * sin_rot), (point.x * sin_rot) + (point.y * cos_rot));\n        point.x = rel.x + center.x;\n        point.y = rel.y + center.y;\n        _Path.push_back(point);\n    }\n}\n\nImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t)\n{\n    float u = 1.0f - t;\n    float w1 = u * u * u;\n    float w2 = 3 * u * u * t;\n    float w3 = 3 * u * t * t;\n    float w4 = t * t * t;\n    return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x, w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y);\n}\n\nImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t)\n{\n    float u = 1.0f - t;\n    float w1 = u * u;\n    float w2 = 2 * u * t;\n    float w3 = t * t;\n    return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x, w1 * p1.y + w2 * p2.y + w3 * p3.y);\n}\n\n// Closely mimics ImBezierCubicClosestPointCasteljau() in imgui.cpp\nstatic void PathBezierCubicCurveToCasteljau(ImVector<ImVec2>* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)\n{\n    float dx = x4 - x1;\n    float dy = y4 - y1;\n    float d2 = (x2 - x4) * dy - (y2 - y4) * dx;\n    float d3 = (x3 - x4) * dy - (y3 - y4) * dx;\n    d2 = (d2 >= 0) ? d2 : -d2;\n    d3 = (d3 >= 0) ? d3 : -d3;\n    if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))\n    {\n        path->push_back(ImVec2(x4, y4));\n    }\n    else if (level < 10)\n    {\n        float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f;\n        float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f;\n        float x34 = (x3 + x4) * 0.5f, y34 = (y3 + y4) * 0.5f;\n        float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f;\n        float x234 = (x23 + x34) * 0.5f, y234 = (y23 + y34) * 0.5f;\n        float x1234 = (x123 + x234) * 0.5f, y1234 = (y123 + y234) * 0.5f;\n        PathBezierCubicCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);\n        PathBezierCubicCurveToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);\n    }\n}\n\nstatic void PathBezierQuadraticCurveToCasteljau(ImVector<ImVec2>* path, float x1, float y1, float x2, float y2, float x3, float y3, float tess_tol, int level)\n{\n    float dx = x3 - x1, dy = y3 - y1;\n    float det = (x2 - x3) * dy - (y2 - y3) * dx;\n    if (det * det * 4.0f < tess_tol * (dx * dx + dy * dy))\n    {\n        path->push_back(ImVec2(x3, y3));\n    }\n    else if (level < 10)\n    {\n        float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f;\n        float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f;\n        float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f;\n        PathBezierQuadraticCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, tess_tol, level + 1);\n        PathBezierQuadraticCurveToCasteljau(path, x123, y123, x23, y23, x3, y3, tess_tol, level + 1);\n    }\n}\n\nvoid ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments)\n{\n    ImVec2 p1 = _Path.back();\n    if (num_segments == 0)\n    {\n        IM_ASSERT(_Data->CurveTessellationTol > 0.0f);\n        PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated\n    }\n    else\n    {\n        float t_step = 1.0f / (float)num_segments;\n        for (int i_step = 1; i_step <= num_segments; i_step++)\n            _Path.push_back(ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step));\n    }\n}\n\nvoid ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments)\n{\n    ImVec2 p1 = _Path.back();\n    if (num_segments == 0)\n    {\n        IM_ASSERT(_Data->CurveTessellationTol > 0.0f);\n        PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated\n    }\n    else\n    {\n        float t_step = 1.0f / (float)num_segments;\n        for (int i_step = 1; i_step <= num_segments; i_step++)\n            _Path.push_back(ImBezierQuadraticCalc(p1, p2, p3, t_step * i_step));\n    }\n}\n\nstatic inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)\n{\n    /*\n    IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    // Obsoleted in 1.82 (from February 2021). This code was stripped/simplified and mostly commented in 1.90 (from September 2023)\n    // - Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)\n    if (flags == ~0)                    { return ImDrawFlags_RoundCornersAll; }\n    // - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code.\n    if (flags >= 0x01 && flags <= 0x0F) { return (flags << 4); }\n    // We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'\n#endif\n    */\n    // If this assert triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.\n    // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway.\n    // See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in \"API BREAKING CHANGES\" section.\n    IM_ASSERT((flags & 0x0F) == 0 && \"Misuse of legacy hardcoded ImDrawCornerFlags values!\");\n\n    if ((flags & ImDrawFlags_RoundCornersMask_) == 0)\n        flags |= ImDrawFlags_RoundCornersAll;\n\n    return flags;\n}\n\nvoid ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags)\n{\n    if (rounding >= 0.5f)\n    {\n        flags = FixRectCornerFlags(flags);\n        rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f);\n        rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f);\n    }\n    if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)\n    {\n        PathLineTo(a);\n        PathLineTo(ImVec2(b.x, a.y));\n        PathLineTo(b);\n        PathLineTo(ImVec2(a.x, b.y));\n    }\n    else\n    {\n        const float rounding_tl = (flags & ImDrawFlags_RoundCornersTopLeft)     ? rounding : 0.0f;\n        const float rounding_tr = (flags & ImDrawFlags_RoundCornersTopRight)    ? rounding : 0.0f;\n        const float rounding_br = (flags & ImDrawFlags_RoundCornersBottomRight) ? rounding : 0.0f;\n        const float rounding_bl = (flags & ImDrawFlags_RoundCornersBottomLeft)  ? rounding : 0.0f;\n        PathArcToFast(ImVec2(a.x + rounding_tl, a.y + rounding_tl), rounding_tl, 6, 9);\n        PathArcToFast(ImVec2(b.x - rounding_tr, a.y + rounding_tr), rounding_tr, 9, 12);\n        PathArcToFast(ImVec2(b.x - rounding_br, b.y - rounding_br), rounding_br, 0, 3);\n        PathArcToFast(ImVec2(a.x + rounding_bl, b.y - rounding_bl), rounding_bl, 3, 6);\n    }\n}\n\nvoid ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n    PathLineTo(p1 + ImVec2(0.5f, 0.5f));\n    PathLineTo(p2 + ImVec2(0.5f, 0.5f));\n    PathStroke(col, 0, thickness);\n}\n\n// p_min = upper-left, p_max = lower-right\n// Note we don't render 1 pixels sized rectangles properly.\nvoid ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n    if (Flags & ImDrawListFlags_AntiAliasedLines)\n        PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, flags);\n    else\n        PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, flags); // Better looking lower-right corner and rounded non-AA shapes.\n    PathStroke(col, ImDrawFlags_Closed, thickness);\n}\n\nvoid ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n    if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)\n    {\n        PrimReserve(6, 4);\n        PrimRect(p_min, p_max, col);\n    }\n    else\n    {\n        PathRect(p_min, p_max, rounding, flags);\n        PathFillConvex(col);\n    }\n}\n\n// p_min = upper-left, p_max = lower-right\nvoid ImDrawList::AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left)\n{\n    if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) & IM_COL32_A_MASK) == 0)\n        return;\n\n    const ImVec2 uv = _Data->TexUvWhitePixel;\n    PrimReserve(6, 4);\n    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2));\n    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 3));\n    PrimWriteVtx(p_min, uv, col_upr_left);\n    PrimWriteVtx(ImVec2(p_max.x, p_min.y), uv, col_upr_right);\n    PrimWriteVtx(p_max, uv, col_bot_right);\n    PrimWriteVtx(ImVec2(p_min.x, p_max.y), uv, col_bot_left);\n}\n\nvoid ImDrawList::AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathLineTo(p2);\n    PathLineTo(p3);\n    PathLineTo(p4);\n    PathStroke(col, ImDrawFlags_Closed, thickness);\n}\n\nvoid ImDrawList::AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathLineTo(p2);\n    PathLineTo(p3);\n    PathLineTo(p4);\n    PathFillConvex(col);\n}\n\nvoid ImDrawList::AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathLineTo(p2);\n    PathLineTo(p3);\n    PathStroke(col, ImDrawFlags_Closed, thickness);\n}\n\nvoid ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathLineTo(p2);\n    PathLineTo(p3);\n    PathFillConvex(col);\n}\n\nvoid ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f)\n        return;\n\n    if (num_segments <= 0)\n    {\n        // Use arc with automatic segment count\n        _PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0);\n        _Path.Size--;\n    }\n    else\n    {\n        // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)\n        num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX);\n\n        // Because we are filling a closed shape we remove 1 from the count of segments/points\n        const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;\n        PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);\n    }\n\n    PathStroke(col, ImDrawFlags_Closed, thickness);\n}\n\nvoid ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments)\n{\n    if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f)\n        return;\n\n    if (num_segments <= 0)\n    {\n        // Use arc with automatic segment count\n        _PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0);\n        _Path.Size--;\n    }\n    else\n    {\n        // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)\n        num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX);\n\n        // Because we are filling a closed shape we remove 1 from the count of segments/points\n        const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;\n        PathArcTo(center, radius, 0.0f, a_max, num_segments - 1);\n    }\n\n    PathFillConvex(col);\n}\n\n// Guaranteed to honor 'num_segments'\nvoid ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2)\n        return;\n\n    // Because we are filling a closed shape we remove 1 from the count of segments/points\n    const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;\n    PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);\n    PathStroke(col, ImDrawFlags_Closed, thickness);\n}\n\n// Guaranteed to honor 'num_segments'\nvoid ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments)\n{\n    if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2)\n        return;\n\n    // Because we are filling a closed shape we remove 1 from the count of segments/points\n    const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;\n    PathArcTo(center, radius, 0.0f, a_max, num_segments - 1);\n    PathFillConvex(col);\n}\n\n// Ellipse\nvoid ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    if (num_segments <= 0)\n        num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.\n\n    // Because we are filling a closed shape we remove 1 from the count of segments/points\n    const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;\n    PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);\n    PathStroke(col, true, thickness);\n}\n\nvoid ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    if (num_segments <= 0)\n        num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.\n\n    // Because we are filling a closed shape we remove 1 from the count of segments/points\n    const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;\n    PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);\n    PathFillConvex(col);\n}\n\n// Cubic Bezier takes 4 controls points\nvoid ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathBezierCubicCurveTo(p2, p3, p4, num_segments);\n    PathStroke(col, 0, thickness);\n}\n\n// Quadratic Bezier takes 3 controls points\nvoid ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathBezierQuadraticCurveTo(p2, p3, num_segments);\n    PathStroke(col, 0, thickness);\n}\n\nvoid ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    // Accept null ranges\n    if (text_begin == text_end || text_begin[0] == 0)\n        return;\n    // No need to strlen() here: font->RenderText() will do it and may early out.\n\n    // Pull default font/size from the shared ImDrawListSharedData instance\n    if (font == NULL)\n        font = _Data->Font;\n    if (font_size == 0.0f)\n        font_size = _Data->FontSize;\n\n    ImVec4 clip_rect = _CmdHeader.ClipRect;\n    if (cpu_fine_clip_rect)\n    {\n        clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x);\n        clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y);\n        clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);\n        clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);\n    }\n    font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, (cpu_fine_clip_rect != NULL) ? ImDrawTextFlags_CpuFineClip : ImDrawTextFlags_None);\n}\n\nvoid ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end)\n{\n    AddText(_Data->Font, _Data->FontSize, pos, col, text_begin, text_end);\n}\n\nvoid ImDrawList::AddImage(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    const bool push_texture_id = tex_ref != _CmdHeader.TexRef;\n    if (push_texture_id)\n        PushTexture(tex_ref);\n\n    PrimReserve(6, 4);\n    PrimRectUV(p_min, p_max, uv_min, uv_max, col);\n\n    if (push_texture_id)\n        PopTexture();\n}\n\nvoid ImDrawList::AddImageQuad(ImTextureRef tex_ref, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3, const ImVec2& uv4, ImU32 col)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    const bool push_texture_id = tex_ref != _CmdHeader.TexRef;\n    if (push_texture_id)\n        PushTexture(tex_ref);\n\n    PrimReserve(6, 4);\n    PrimQuadUV(p1, p2, p3, p4, uv1, uv2, uv3, uv4, col);\n\n    if (push_texture_id)\n        PopTexture();\n}\n\nvoid ImDrawList::AddImageRounded(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    flags = FixRectCornerFlags(flags);\n    if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)\n    {\n        AddImage(tex_ref, p_min, p_max, uv_min, uv_max, col);\n        return;\n    }\n\n    const bool push_texture_id = tex_ref != _CmdHeader.TexRef;\n    if (push_texture_id)\n        PushTexture(tex_ref);\n\n    int vert_start_idx = VtxBuffer.Size;\n    PathRect(p_min, p_max, rounding, flags);\n    PathFillConvex(col);\n    int vert_end_idx = VtxBuffer.Size;\n    ImGui::ShadeVertsLinearUV(this, vert_start_idx, vert_end_idx, p_min, p_max, uv_min, uv_max, true);\n\n    if (push_texture_id)\n        PopTexture();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImTriangulator, ImDrawList concave polygon fill\n//-----------------------------------------------------------------------------\n// Triangulate concave polygons. Based on \"Triangulation by Ear Clipping\" paper, O(N^2) complexity.\n// Reference: https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf\n// Provided as a convenience for user but not used by main library.\n//-----------------------------------------------------------------------------\n// - ImTriangulator [Internal]\n// - AddConcavePolyFilled()\n//-----------------------------------------------------------------------------\n\nenum ImTriangulatorNodeType\n{\n    ImTriangulatorNodeType_Convex,\n    ImTriangulatorNodeType_Ear,\n    ImTriangulatorNodeType_Reflex\n};\n\nstruct ImTriangulatorNode\n{\n    ImTriangulatorNodeType  Type;\n    int                     Index;\n    ImVec2                  Pos;\n    ImTriangulatorNode*     Next;\n    ImTriangulatorNode*     Prev;\n\n    void    Unlink()        { Next->Prev = Prev; Prev->Next = Next; }\n};\n\nstruct ImTriangulatorNodeSpan\n{\n    ImTriangulatorNode**    Data = NULL;\n    int                     Size = 0;\n\n    void    push_back(ImTriangulatorNode* node) { Data[Size++] = node; }\n    void    find_erase_unsorted(int idx)        { for (int i = Size - 1; i >= 0; i--) if (Data[i]->Index == idx) { Data[i] = Data[Size - 1]; Size--; return; } }\n};\n\nstruct ImTriangulator\n{\n    static int EstimateTriangleCount(int points_count)      { return (points_count < 3) ? 0 : points_count - 2; }\n    static int EstimateScratchBufferSize(int points_count)  { return sizeof(ImTriangulatorNode) * points_count + sizeof(ImTriangulatorNode*) * points_count * 2; }\n\n    void    Init(const ImVec2* points, int points_count, void* scratch_buffer);\n    void    GetNextTriangle(unsigned int out_triangle[3]);     // Return relative indexes for next triangle\n\n    // Internal functions\n    void    BuildNodes(const ImVec2* points, int points_count);\n    void    BuildReflexes();\n    void    BuildEars();\n    void    FlipNodeList();\n    bool    IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const;\n    void    ReclassifyNode(ImTriangulatorNode* node);\n\n    // Internal members\n    int                     _TrianglesLeft = 0;\n    ImTriangulatorNode*     _Nodes = NULL;\n    ImTriangulatorNodeSpan  _Ears;\n    ImTriangulatorNodeSpan  _Reflexes;\n};\n\n// Distribute storage for nodes, ears and reflexes.\n// FIXME-OPT: if everything is convex, we could report it to caller and let it switch to an convex renderer\n// (this would require first building reflexes to bail to convex if empty, without even building nodes)\nvoid ImTriangulator::Init(const ImVec2* points, int points_count, void* scratch_buffer)\n{\n    IM_ASSERT(scratch_buffer != NULL && points_count >= 3);\n    _TrianglesLeft = EstimateTriangleCount(points_count);\n    _Nodes         = (ImTriangulatorNode*)scratch_buffer;                          // points_count x Node\n    _Ears.Data     = (ImTriangulatorNode**)(_Nodes + points_count);                // points_count x Node*\n    _Reflexes.Data = (ImTriangulatorNode**)(_Nodes + points_count) + points_count; // points_count x Node*\n    BuildNodes(points, points_count);\n    BuildReflexes();\n    BuildEars();\n}\n\nvoid ImTriangulator::BuildNodes(const ImVec2* points, int points_count)\n{\n    for (int i = 0; i < points_count; i++)\n    {\n        _Nodes[i].Type = ImTriangulatorNodeType_Convex;\n        _Nodes[i].Index = i;\n        _Nodes[i].Pos = points[i];\n        _Nodes[i].Next = _Nodes + i + 1;\n        _Nodes[i].Prev = _Nodes + i - 1;\n    }\n    _Nodes[0].Prev = _Nodes + points_count - 1;\n    _Nodes[points_count - 1].Next = _Nodes;\n}\n\nvoid ImTriangulator::BuildReflexes()\n{\n    ImTriangulatorNode* n1 = _Nodes;\n    for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)\n    {\n        if (ImTriangleIsClockwise(n1->Prev->Pos, n1->Pos, n1->Next->Pos))\n            continue;\n        n1->Type = ImTriangulatorNodeType_Reflex;\n        _Reflexes.push_back(n1);\n    }\n}\n\nvoid ImTriangulator::BuildEars()\n{\n    ImTriangulatorNode* n1 = _Nodes;\n    for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)\n    {\n        if (n1->Type != ImTriangulatorNodeType_Convex)\n            continue;\n        if (!IsEar(n1->Prev->Index, n1->Index, n1->Next->Index, n1->Prev->Pos, n1->Pos, n1->Next->Pos))\n            continue;\n        n1->Type = ImTriangulatorNodeType_Ear;\n        _Ears.push_back(n1);\n    }\n}\n\nvoid ImTriangulator::GetNextTriangle(unsigned int out_triangle[3])\n{\n    if (_Ears.Size == 0)\n    {\n        FlipNodeList();\n\n        ImTriangulatorNode* node = _Nodes;\n        for (int i = _TrianglesLeft; i >= 0; i--, node = node->Next)\n            node->Type = ImTriangulatorNodeType_Convex;\n        _Reflexes.Size = 0;\n        BuildReflexes();\n        BuildEars();\n\n        // If we still don't have ears, it means geometry is degenerated.\n        if (_Ears.Size == 0)\n        {\n            // Return first triangle available, mimicking the behavior of convex fill.\n            IM_ASSERT(_TrianglesLeft > 0); // Geometry is degenerated\n            _Ears.Data[0] = _Nodes;\n            _Ears.Size    = 1;\n        }\n    }\n\n    ImTriangulatorNode* ear = _Ears.Data[--_Ears.Size];\n    out_triangle[0] = ear->Prev->Index;\n    out_triangle[1] = ear->Index;\n    out_triangle[2] = ear->Next->Index;\n\n    ear->Unlink();\n    if (ear == _Nodes)\n        _Nodes = ear->Next;\n\n    ReclassifyNode(ear->Prev);\n    ReclassifyNode(ear->Next);\n    _TrianglesLeft--;\n}\n\nvoid ImTriangulator::FlipNodeList()\n{\n    ImTriangulatorNode* prev = _Nodes;\n    ImTriangulatorNode* temp = _Nodes;\n    ImTriangulatorNode* current = _Nodes->Next;\n    prev->Next = prev;\n    prev->Prev = prev;\n    while (current != _Nodes)\n    {\n        temp = current->Next;\n\n        current->Next = prev;\n        prev->Prev = current;\n        _Nodes->Next = current;\n        current->Prev = _Nodes;\n\n        prev = current;\n        current = temp;\n    }\n    _Nodes = prev;\n}\n\n// A triangle is an ear is no other vertex is inside it. We can test reflexes vertices only (see reference algorithm)\nbool ImTriangulator::IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const\n{\n    ImTriangulatorNode** p_end = _Reflexes.Data + _Reflexes.Size;\n    for (ImTriangulatorNode** p = _Reflexes.Data; p < p_end; p++)\n    {\n        ImTriangulatorNode* reflex = *p;\n        if (reflex->Index != i0 && reflex->Index != i1 && reflex->Index != i2)\n            if (ImTriangleContainsPoint(v0, v1, v2, reflex->Pos))\n                return false;\n    }\n    return true;\n}\n\nvoid ImTriangulator::ReclassifyNode(ImTriangulatorNode* n1)\n{\n    // Classify node\n    ImTriangulatorNodeType type;\n    const ImTriangulatorNode* n0 = n1->Prev;\n    const ImTriangulatorNode* n2 = n1->Next;\n    if (!ImTriangleIsClockwise(n0->Pos, n1->Pos, n2->Pos))\n        type = ImTriangulatorNodeType_Reflex;\n    else if (IsEar(n0->Index, n1->Index, n2->Index, n0->Pos, n1->Pos, n2->Pos))\n        type = ImTriangulatorNodeType_Ear;\n    else\n        type = ImTriangulatorNodeType_Convex;\n\n    // Update lists when a type changes\n    if (type == n1->Type)\n        return;\n    if (n1->Type == ImTriangulatorNodeType_Reflex)\n        _Reflexes.find_erase_unsorted(n1->Index);\n    else if (n1->Type == ImTriangulatorNodeType_Ear)\n        _Ears.find_erase_unsorted(n1->Index);\n    if (type == ImTriangulatorNodeType_Reflex)\n        _Reflexes.push_back(n1);\n    else if (type == ImTriangulatorNodeType_Ear)\n        _Ears.push_back(n1);\n    n1->Type = type;\n}\n\n// Use ear-clipping algorithm to triangulate a simple polygon (no self-interaction, no holes).\n// (Reminder: we don't perform any coarse clipping/culling in ImDrawList layer!\n// It is up to caller to ensure not making costly calls that will be outside of visible area.\n// As concave fill is noticeably more expensive than other primitives, be mindful of this...\n// Caller can build AABB of points, and avoid filling if 'draw_list->_CmdHeader.ClipRect.Overlays(points_bb) == false')\nvoid ImDrawList::AddConcavePolyFilled(const ImVec2* points, const int points_count, ImU32 col)\n{\n    if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)\n        return;\n\n    const ImVec2 uv = _Data->TexUvWhitePixel;\n    ImTriangulator triangulator;\n    unsigned int triangle[3];\n    if (Flags & ImDrawListFlags_AntiAliasedFill)\n    {\n        // Anti-aliased Fill\n        const float AA_SIZE = _FringeScale;\n        const ImU32 col_trans = col & ~IM_COL32_A_MASK;\n        const int idx_count = (points_count - 2) * 3 + points_count * 6;\n        const int vtx_count = (points_count * 2);\n        PrimReserve(idx_count, vtx_count);\n\n        // Add indexes for fill\n        unsigned int vtx_inner_idx = _VtxCurrentIdx;\n        unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;\n\n        _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));\n        triangulator.Init(points, points_count, _Data->TempBuffer.Data);\n        while (triangulator._TrianglesLeft > 0)\n        {\n            triangulator.GetNextTriangle(triangle);\n            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (triangle[0] << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (triangle[1] << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (triangle[2] << 1));\n            _IdxWritePtr += 3;\n        }\n\n        // Compute normals\n        _Data->TempBuffer.reserve_discard(points_count);\n        ImVec2* temp_normals = _Data->TempBuffer.Data;\n        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)\n        {\n            const ImVec2& p0 = points[i0];\n            const ImVec2& p1 = points[i1];\n            float dx = p1.x - p0.x;\n            float dy = p1.y - p0.y;\n            IM_NORMALIZE2F_OVER_ZERO(dx, dy);\n            temp_normals[i0].x = dy;\n            temp_normals[i0].y = -dx;\n        }\n\n        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)\n        {\n            // Average normals\n            const ImVec2& n0 = temp_normals[i0];\n            const ImVec2& n1 = temp_normals[i1];\n            float dm_x = (n0.x + n1.x) * 0.5f;\n            float dm_y = (n0.y + n1.y) * 0.5f;\n            IM_FIXNORMAL2F(dm_x, dm_y);\n            dm_x *= AA_SIZE * 0.5f;\n            dm_y *= AA_SIZE * 0.5f;\n\n            // Add vertices\n            _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;        // Inner\n            _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans;  // Outer\n            _VtxWritePtr += 2;\n\n            // Add indexes for fringes\n            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));\n            _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));\n            _IdxWritePtr += 6;\n        }\n        _VtxCurrentIdx += (ImDrawIdx)vtx_count;\n    }\n    else\n    {\n        // Non Anti-aliased Fill\n        const int idx_count = (points_count - 2) * 3;\n        const int vtx_count = points_count;\n        PrimReserve(idx_count, vtx_count);\n        for (int i = 0; i < vtx_count; i++)\n        {\n            _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;\n            _VtxWritePtr++;\n        }\n        _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));\n        triangulator.Init(points, points_count, _Data->TempBuffer.Data);\n        while (triangulator._TrianglesLeft > 0)\n        {\n            triangulator.GetNextTriangle(triangle);\n            _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx + triangle[0]); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + triangle[1]); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + triangle[2]);\n            _IdxWritePtr += 3;\n        }\n        _VtxCurrentIdx += (ImDrawIdx)vtx_count;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImDrawList Shadow Primitives\n//-----------------------------------------------------------------------------\n// - AddSubtractedRect() [Internal]\n// - ClipPolygonShape() [Internal]\n// - AddSubtractedRect() [Internal]\n// - AddRectShadow()\n//-----------------------------------------------------------------------------\n\n// Adds a rectangle (A) with another rectangle (B) subtracted from it (i.e. the portion of A covered by B is not drawn). Does not handle rounded corners (use the version that takes a convex polygon for that).\nstatic void AddSubtractedRect(ImDrawList* draw_list, const ImVec2& a_min, const ImVec2& a_max, const ImVec2& a_min_uv, const ImVec2& a_max_uv, ImVec2 b_min, ImVec2 b_max, ImU32 col)\n{\n    // Early out without drawing anything if A is zero-size\n    if (a_min.x >= a_max.x || a_min.y >= a_max.y)\n        return;\n\n    // Early out without drawing anything if B covers A entirely\n    if (a_min.x >= b_min.x && a_max.x <= b_max.x && a_min.y >= b_min.y && a_max.y <= b_max.y)\n        return;\n\n    // First clip the extents of B to A\n    b_min = ImMax(b_min, a_min);\n    b_max = ImMin(b_max, a_max);\n    if (b_min.x >= b_max.x || b_min.y >= b_max.y)\n    {\n        // B is entirely outside A, so just draw A as-is\n        draw_list->PrimReserve(6, 4);\n        draw_list->PrimRectUV(a_min, a_max, a_min_uv, a_max_uv, col);\n        return;\n    }\n\n    // Otherwise we need to emit (up to) four quads to cover the visible area...\n    // Our layout looks like this (numbers are vertex indices, letters are quads):\n    //\n    // 0---8------9-----1\n    // |   |  B   |     |\n    // +   4------5     +\n    // | A |xxxxxx|  C  |\n    // |   |xxxxxx|     |\n    // +   7------6     +\n    // |   |  D   |     |\n    // 3---11-----10----2\n\n    const int max_verts = 12;\n    const int max_indices = 6 * 4; // At most four quads\n    draw_list->PrimReserve(max_indices, max_verts);\n\n    ImDrawIdx* idx_write = draw_list->_IdxWritePtr;\n    ImDrawVert* vtx_write = draw_list->_VtxWritePtr;\n    ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx;\n\n    // Write vertices\n    vtx_write[0].pos = ImVec2(a_min.x, a_min.y); vtx_write[0].uv = ImVec2(a_min_uv.x, a_min_uv.y); vtx_write[0].col = col;\n    vtx_write[1].pos = ImVec2(a_max.x, a_min.y); vtx_write[1].uv = ImVec2(a_max_uv.x, a_min_uv.y); vtx_write[1].col = col;\n    vtx_write[2].pos = ImVec2(a_max.x, a_max.y); vtx_write[2].uv = ImVec2(a_max_uv.x, a_max_uv.y); vtx_write[2].col = col;\n    vtx_write[3].pos = ImVec2(a_min.x, a_max.y); vtx_write[3].uv = ImVec2(a_min_uv.x, a_max_uv.y); vtx_write[3].col = col;\n\n    const ImVec2 pos_to_uv_scale = (a_max_uv - a_min_uv) / (a_max - a_min); // Guaranteed never to be a /0 because we check for zero-size A above\n    const ImVec2 pos_to_uv_offset = (a_min_uv / pos_to_uv_scale) - a_min;\n\n    // Helper that generates an interpolated UV based on position\n#define LERP_UV(x_pos, y_pos) (ImVec2(((x_pos) + pos_to_uv_offset.x) * pos_to_uv_scale.x, ((y_pos) + pos_to_uv_offset.y) * pos_to_uv_scale.y))\n    vtx_write[4].pos = ImVec2(b_min.x, b_min.y); vtx_write[4].uv = LERP_UV(b_min.x, b_min.y); vtx_write[4].col = col;\n    vtx_write[5].pos = ImVec2(b_max.x, b_min.y); vtx_write[5].uv = LERP_UV(b_max.x, b_min.y); vtx_write[5].col = col;\n    vtx_write[6].pos = ImVec2(b_max.x, b_max.y); vtx_write[6].uv = LERP_UV(b_max.x, b_max.y); vtx_write[6].col = col;\n    vtx_write[7].pos = ImVec2(b_min.x, b_max.y); vtx_write[7].uv = LERP_UV(b_min.x, b_max.y); vtx_write[7].col = col;\n    vtx_write[8].pos = ImVec2(b_min.x, a_min.y); vtx_write[8].uv = LERP_UV(b_min.x, a_min.y); vtx_write[8].col = col;\n    vtx_write[9].pos = ImVec2(b_max.x, a_min.y); vtx_write[9].uv = LERP_UV(b_max.x, a_min.y); vtx_write[9].col = col;\n    vtx_write[10].pos = ImVec2(b_max.x, a_max.y); vtx_write[10].uv = LERP_UV(b_max.x, a_max.y); vtx_write[10].col = col;\n    vtx_write[11].pos = ImVec2(b_min.x, a_max.y); vtx_write[11].uv = LERP_UV(b_min.x, a_max.y); vtx_write[11].col = col;\n#undef LERP_UV\n    draw_list->_VtxWritePtr += 12;\n    draw_list->_VtxCurrentIdx += 12;\n\n    // Write indices for each quad (if it is visible)\n    if (b_min.x > a_min.x) // A\n    {\n        idx_write[0] = (ImDrawIdx)(idx + 0); idx_write[1] = (ImDrawIdx)(idx + 8); idx_write[2] = (ImDrawIdx)(idx + 11);\n        idx_write[3] = (ImDrawIdx)(idx + 0); idx_write[4] = (ImDrawIdx)(idx + 11); idx_write[5] = (ImDrawIdx)(idx + 3);\n        idx_write += 6;\n    }\n    if (b_min.y > a_min.y) // B\n    {\n        idx_write[0] = (ImDrawIdx)(idx + 8); idx_write[1] = (ImDrawIdx)(idx + 9); idx_write[2] = (ImDrawIdx)(idx + 5);\n        idx_write[3] = (ImDrawIdx)(idx + 8); idx_write[4] = (ImDrawIdx)(idx + 5); idx_write[5] = (ImDrawIdx)(idx + 4);\n        idx_write += 6;\n    }\n    if (a_max.x > b_max.x) // C\n    {\n        idx_write[0] = (ImDrawIdx)(idx + 9); idx_write[1] = (ImDrawIdx)(idx + 1); idx_write[2] = (ImDrawIdx)(idx + 2);\n        idx_write[3] = (ImDrawIdx)(idx + 9); idx_write[4] = (ImDrawIdx)(idx + 2); idx_write[5] = (ImDrawIdx)(idx + 10);\n        idx_write += 6;\n    }\n    if (a_max.y > b_max.y) // D\n    {\n        idx_write[0] = (ImDrawIdx)(idx + 7); idx_write[1] = (ImDrawIdx)(idx + 6); idx_write[2] = (ImDrawIdx)(idx + 10);\n        idx_write[3] = (ImDrawIdx)(idx + 7); idx_write[4] = (ImDrawIdx)(idx + 10); idx_write[5] = (ImDrawIdx)(idx + 11);\n        idx_write += 6;\n    }\n\n    const int used_indices = (int)(idx_write - draw_list->_IdxWritePtr);\n    draw_list->_IdxWritePtr = idx_write;\n    draw_list->PrimUnreserve(max_indices - used_indices, 0);\n}\n\n// Clip a polygonal shape to a rectangle, writing the results into dest_points. The number of points emitted is returned (may be zero if the polygon was entirely outside the rectangle, or the source polygon was not valid). dest_points may still be written to even if zero was returned.\n// allocated_dest_points should contain the number of allocated points in dest_points - in general this should be the number of source points + 4 to accommodate the worst case. If this is exceeded data will be truncated and -1 returned. Stack space work area is allocated based on this value so it shouldn't be too large.\nstatic int ClipPolygonShape(ImVec2* src_points, int num_src_points, ImVec2* dest_points, int allocated_dest_points, ImVec2 clip_rect_min, ImVec2 clip_rect_max)\n{\n    // Early-out with an empty result if clipping region is zero-sized\n    if (clip_rect_max.x <= clip_rect_min.x || clip_rect_max.y <= clip_rect_min.y)\n        return 0;\n\n    // Early-out if there is no source geometry\n    if (num_src_points < 3)\n        return 0;\n\n    // The four clip planes here are indexed as:\n    // 0 = X-, 1 = X+, 2 = Y-, 3 = Y+\n    ImU8* outflags[2]; // Double-buffered flags for each vertex indicating which of the four clip planes it is outside of\n    outflags[0] = (ImU8*)alloca(2 * allocated_dest_points * sizeof(ImU8));\n    outflags[1] = outflags[0] + allocated_dest_points;\n\n    // Calculate initial outflags\n    ImU8 outflags_anded = 0xFF;\n    ImU8 outflags_ored = 0;\n    for (int point_idx = 0; point_idx < num_src_points; point_idx++)\n    {\n        const ImVec2 pos = src_points[point_idx];\n        const ImU8 point_outflags = (pos.x < clip_rect_min.x ? 1 : 0) | (pos.x > clip_rect_max.x ? 2 : 0) | (pos.y < clip_rect_min.y ? 4 : 0) | (pos.y > clip_rect_max.y ? 8 : 0);\n        outflags[0][point_idx] = point_outflags; // Writing to buffer 0\n        outflags_anded &= point_outflags;\n        outflags_ored |= point_outflags;\n    }\n    if (outflags_anded != 0) // Entirely clipped by any one plane, so nothing remains\n        return 0;\n\n    if (outflags_ored == 0) // Entirely within bounds, so trivial accept\n    {\n        if (allocated_dest_points < num_src_points)\n            return -1; // Not sure what the caller was thinking if this happens, but we should handle it gracefully\n\n        memcpy(dest_points, src_points, num_src_points * sizeof(ImVec2));\n        return num_src_points;\n    }\n\n    // Shape needs clipping\n    ImVec2* clip_buf[2]; // Double-buffered work area\n    clip_buf[0] = (ImVec2*)alloca(2 * allocated_dest_points * sizeof(ImVec2)); //-V630\n    clip_buf[1] = clip_buf[0] + allocated_dest_points;\n\n    memcpy(clip_buf[0], src_points, num_src_points * sizeof(ImVec2));\n    int clip_buf_size = num_src_points; // Number of vertices currently in the clip buffer\n\n    int read_buffer_idx = 0; // The index of the clip buffer/out-flags we are reading (0 or 1)\n\n    for (int clip_plane = 0; clip_plane < 4; clip_plane++) // 0 = X-, 1 = X+, 2 = Y-, 3 = Y+\n    {\n        const int clip_plane_bit = 1 << clip_plane; // Bit mask for our current plane in out-flags\n        if ((outflags_ored & clip_plane_bit) == 0)\n            continue; // All vertices are inside this plane, so no need to clip\n\n        ImVec2* read_vert = &clip_buf[read_buffer_idx][0];              // Clip buffer vertex we are currently reading\n        ImVec2* write_vert = &clip_buf[1 - read_buffer_idx][0];         // Clip buffer vertex we are currently writing\n        ImVec2* write_vert_end = write_vert + allocated_dest_points;    // End of the write buffer\n        ImU8* read_outflags = &outflags[read_buffer_idx][0];            // Out-flag we are currently reading\n        ImU8* write_outflags = &outflags[1 - read_buffer_idx][0];       // Out-flag we are currently writing\n\n        // Keep track of the last vertex visited, initially the last in the list\n        ImVec2* last_vert = &read_vert[clip_buf_size - 1];\n        ImU8 last_outflags = read_outflags[clip_buf_size - 1];\n\n        for (int vert = 0; vert < clip_buf_size; vert++)\n        {\n            ImU8 current_outflags = *(read_outflags++);\n            bool out = (current_outflags & clip_plane_bit) != 0;\n            if (((current_outflags ^ last_outflags) & clip_plane_bit) == 0) // We haven't crossed the clip plane\n            {\n                if (!out)\n                {\n                    // Emit vertex as-is\n                    if (write_vert >= write_vert_end)\n                        return -1; // Ran out of buffer space, so abort\n                    *(write_vert++) = *read_vert;\n                    *(write_outflags++) = current_outflags;\n                }\n            }\n            else\n            {\n                // Emit a vertex at the intersection point\n                float t = 0.0f;\n                ImVec2 pos0 = *last_vert;\n                ImVec2 pos1 = *read_vert;\n                ImVec2 intersect_pos;\n                switch (clip_plane)\n                {\n                case 0: t = (clip_rect_min.x - pos0.x) / (pos1.x - pos0.x); intersect_pos = ImVec2(clip_rect_min.x, pos0.y + ((pos1.y - pos0.y) * t)); break; // X-\n                case 1: t = (clip_rect_max.x - pos0.x) / (pos1.x - pos0.x); intersect_pos = ImVec2(clip_rect_max.x, pos0.y + ((pos1.y - pos0.y) * t)); break; // X+\n                case 2: t = (clip_rect_min.y - pos0.y) / (pos1.y - pos0.y); intersect_pos = ImVec2(pos0.x + ((pos1.x - pos0.x) * t), clip_rect_min.y); break; // Y-\n                case 3: t = (clip_rect_max.y - pos0.y) / (pos1.y - pos0.y); intersect_pos = ImVec2(pos0.x + ((pos1.x - pos0.x) * t), clip_rect_max.y); break; // Y+\n                }\n\n                if (write_vert >= write_vert_end)\n                    return -1; // Ran out of buffer space, so abort\n\n                // Write new out-flags for the vertex we just emitted\n                *(write_vert++) = intersect_pos;\n                *(write_outflags++) = (intersect_pos.x < clip_rect_min.x ? 1 : 0) | (intersect_pos.x > clip_rect_max.x ? 2 : 0) | (intersect_pos.y < clip_rect_min.y ? 4 : 0) | (intersect_pos.y > clip_rect_max.y ? 8 : 0);\n\n                if (!out)\n                {\n                    // When coming back in, also emit the actual vertex\n                    if (write_vert >= write_vert_end)\n                        return -1; // Ran out of buffer space, so abort\n                    *(write_vert++) = *read_vert;\n                    *(write_outflags++) = current_outflags;\n                }\n\n                last_outflags = current_outflags;\n            }\n\n            last_vert = read_vert;\n            read_vert++; // Advance to next vertex\n        }\n\n        clip_buf_size = (int)(write_vert - &clip_buf[1 - read_buffer_idx][0]); // Update buffer size\n        read_buffer_idx = 1 - read_buffer_idx; // Swap buffers\n    }\n\n    if (clip_buf_size < 3)\n        return 0; // Nothing to return\n\n    // Copy results to output buffer, removing any redundant vertices\n    int num_out_verts = 0;\n    ImVec2 last_vert = clip_buf[read_buffer_idx][clip_buf_size - 1];\n    for (int i = 0; i < clip_buf_size; i++)\n    {\n        ImVec2 vert = clip_buf[read_buffer_idx][i];\n        if (ImLengthSqr(vert - last_vert) <= 0.00001f)\n            continue;\n        dest_points[num_out_verts++] = vert;\n        last_vert = vert;\n    }\n\n    // Return size (IF this is still a valid shape)\n    return (num_out_verts > 2) ? num_out_verts : 0;\n}\n\n// Adds a rectangle (A) with a convex shape (B) subtracted from it (i.e. the portion of A covered by B is not drawn).\nstatic void AddSubtractedRect(ImDrawList* draw_list, const ImVec2& a_min, const ImVec2& a_max, const ImVec2& a_min_uv, const ImVec2& a_max_uv, ImVec2* b_points, int num_b_points, ImU32 col)\n{\n    // Early out without drawing anything if A is zero-size\n    if (a_min.x >= a_max.x || a_min.y >= a_max.y)\n        return;\n\n    // First clip B to A\n    const int max_clipped_points = num_b_points + 4;\n    ImVec2* clipped_b_points = (ImVec2*)alloca(max_clipped_points * sizeof(ImVec2)); //-V630\n    const int num_clipped_points = ClipPolygonShape(b_points, num_b_points, clipped_b_points, max_clipped_points, a_min, a_max);\n    IM_ASSERT(num_clipped_points >= 0); // -1 would indicate max_clipped_points was too small, which shouldn't happen\n\n    b_points = clipped_b_points;\n    num_b_points = num_clipped_points;\n\n    if (num_clipped_points == 0)\n    {\n        // B is entirely outside A, so just draw A as-is\n        draw_list->PrimReserve(6, 4);\n        draw_list->PrimRectUV(a_min, a_max, a_min_uv, a_max_uv, col);\n    }\n    else\n    {\n        // We need to generate clipped geometry\n        // To do this we walk the inner polygon and connect each edge to one of the four corners of our rectangle based on the quadrant their normal points at\n        const int max_verts = num_b_points + 4; // Inner points plus the four corners\n        const int max_indices = (num_b_points * 3) + (4 * 3); // Worst case is one triangle per inner edge and then four filler triangles\n        draw_list->PrimReserve(max_indices, max_verts);\n\n        ImDrawIdx* idx_write = draw_list->_IdxWritePtr;\n        ImDrawVert* vtx_write = draw_list->_VtxWritePtr;\n        ImDrawIdx inner_idx = (ImDrawIdx)draw_list->_VtxCurrentIdx; // Starting index for inner vertices\n\n        // Write inner vertices\n        const ImVec2 pos_to_uv_scale = (a_max_uv - a_min_uv) / (a_max - a_min); // Guaranteed never to be a /0 because we check for zero-size A above\n        const ImVec2 pos_to_uv_offset = (a_min_uv / pos_to_uv_scale) - a_min;\n\n        // Helper that generates an interpolated UV based on position\n#define LERP_UV(x_pos, y_pos) (ImVec2(((x_pos) + pos_to_uv_offset.x) * pos_to_uv_scale.x, ((y_pos) + pos_to_uv_offset.y) * pos_to_uv_scale.y))\n        for (int i = 0; i < num_b_points; i++)\n        {\n            vtx_write[i].pos = b_points[i];\n            vtx_write[i].uv = LERP_UV(b_points[i].x, b_points[i].y);\n            vtx_write[i].col = col;\n        }\n#undef LERP_UV\n\n        vtx_write += num_b_points;\n\n        // Write outer vertices\n        ImDrawIdx outer_idx = (ImDrawIdx)(inner_idx + num_b_points); // Starting index for outer vertices\n\n        ImVec2 outer_verts[4];\n        outer_verts[0] = ImVec2(a_min.x, a_min.y); // X- Y- (quadrant 0, top left)\n        outer_verts[1] = ImVec2(a_max.x, a_min.y); // X+ Y- (quadrant 1, top right)\n        outer_verts[2] = ImVec2(a_max.x, a_max.y); // X+ Y+ (quadrant 2, bottom right)\n        outer_verts[3] = ImVec2(a_min.x, a_max.y); // X- Y+ (quadrant 3, bottom left)\n\n        vtx_write[0].pos = outer_verts[0]; vtx_write[0].uv = ImVec2(a_min_uv.x, a_min_uv.y); vtx_write[0].col = col;\n        vtx_write[1].pos = outer_verts[1]; vtx_write[1].uv = ImVec2(a_max_uv.x, a_min_uv.y); vtx_write[1].col = col;\n        vtx_write[2].pos = outer_verts[2]; vtx_write[2].uv = ImVec2(a_max_uv.x, a_max_uv.y); vtx_write[2].col = col;\n        vtx_write[3].pos = outer_verts[3]; vtx_write[3].uv = ImVec2(a_min_uv.x, a_max_uv.y); vtx_write[3].col = col;\n\n        draw_list->_VtxCurrentIdx += num_b_points + 4;\n        draw_list->_VtxWritePtr += num_b_points + 4;\n\n        // Now walk the inner vertices in order\n        ImVec2 last_inner_vert = b_points[num_b_points - 1];\n        int last_inner_vert_idx = num_b_points - 1;\n        int last_outer_vert_idx = -1;\n        int first_outer_vert_idx = -1;\n\n        // Triangle-area based check for degenerate triangles\n        // Min area (0.1f) is doubled (* 2.0f) because we're calculating (area * 2) here\n#define IS_DEGENERATE(a, b, c) (ImFabs((((a).x * ((b).y - (c).y)) + ((b).x * ((c).y - (a).y)) + ((c).x * ((a).y - (b).y)))) < (0.1f * 2.0f))\n\n        // Check the winding order of the inner vertices using the sign of the triangle area, and set the outer vertex winding to match\n        int outer_vertex_winding = (((b_points[0].x * (b_points[1].y - b_points[2].y)) + (b_points[1].x * (b_points[2].y - b_points[0].y)) + (b_points[2].x * (b_points[0].y - b_points[1].y))) < 0.0f) ? -1 : 1;\n        for (int inner_vert_idx = 0; inner_vert_idx < num_b_points; inner_vert_idx++)\n        {\n            ImVec2 current_inner_vert = b_points[inner_vert_idx];\n\n            // Calculate normal (not actually normalized, as for our purposes here it doesn't need to be)\n            ImVec2 normal(current_inner_vert.y - last_inner_vert.y, -(current_inner_vert.x - last_inner_vert.x));\n\n            // Calculate the outer vertex index based on the quadrant the normal points at (0=top left, 1=top right, 2=bottom right, 3=bottom left)\n            int outer_vert_idx = (ImFabs(normal.x) > ImFabs(normal.y)) ? ((normal.x >= 0.0f) ? ((normal.y > 0.0f) ? 2 : 1) : ((normal.y > 0.0f) ? 3 : 0)) : ((normal.y >= 0.0f) ? ((normal.x > 0.0f) ? 2 : 3) : ((normal.x > 0.0f) ? 1 : 0));\n            ImVec2 outer_vert = outer_verts[outer_vert_idx];\n\n            // Write the main triangle (connecting the inner edge to the corner)\n            if (!IS_DEGENERATE(last_inner_vert, current_inner_vert, outer_vert))\n            {\n                idx_write[0] = (ImDrawIdx)(inner_idx + last_inner_vert_idx);\n                idx_write[1] = (ImDrawIdx)(inner_idx + inner_vert_idx);\n                idx_write[2] = (ImDrawIdx)(outer_idx + outer_vert_idx);\n                idx_write += 3;\n            }\n\n            // We don't initially know which outer vertex we are going to start from, so set that here when processing the first inner vertex\n            if (first_outer_vert_idx == -1)\n            {\n                first_outer_vert_idx = outer_vert_idx;\n                last_outer_vert_idx = outer_vert_idx;\n            }\n\n            // Now walk the outer edge and write any filler triangles needed (connecting outer edges to the inner vertex)\n            while (outer_vert_idx != last_outer_vert_idx)\n            {\n                int next_outer_vert_idx = (last_outer_vert_idx + outer_vertex_winding) & 3;\n                if (!IS_DEGENERATE(outer_verts[last_outer_vert_idx], outer_verts[next_outer_vert_idx], last_inner_vert))\n                {\n                    idx_write[0] = (ImDrawIdx)(outer_idx + last_outer_vert_idx);\n                    idx_write[1] = (ImDrawIdx)(outer_idx + next_outer_vert_idx);\n                    idx_write[2] = (ImDrawIdx)(inner_idx + last_inner_vert_idx);\n                    idx_write += 3;\n                }\n                last_outer_vert_idx = next_outer_vert_idx;\n            }\n\n            last_inner_vert = current_inner_vert;\n            last_inner_vert_idx = inner_vert_idx;\n        }\n\n        // Write remaining filler triangles for any un-traversed outer edges\n        if (first_outer_vert_idx != -1)\n        {\n            while (first_outer_vert_idx != last_outer_vert_idx)\n            {\n                int next_outer_vert_idx = (last_outer_vert_idx + outer_vertex_winding) & 3;\n                if (!IS_DEGENERATE(outer_verts[last_outer_vert_idx], outer_verts[next_outer_vert_idx], last_inner_vert))\n                {\n                    idx_write[0] = (ImDrawIdx)(outer_idx + last_outer_vert_idx);\n                    idx_write[1] = (ImDrawIdx)(outer_idx + next_outer_vert_idx);\n                    idx_write[2] = (ImDrawIdx)(inner_idx + last_inner_vert_idx);\n                    idx_write += 3;\n                }\n                last_outer_vert_idx = next_outer_vert_idx;\n            }\n        }\n#undef IS_DEGENERATE\n\n        int used_indices = (int)(idx_write - draw_list->_IdxWritePtr);\n        draw_list->_IdxWritePtr = idx_write;\n        draw_list->PrimUnreserve(max_indices - used_indices, 0);\n    }\n}\n\nvoid ImDrawList::AddShadowRect(const ImVec2& obj_min, const ImVec2& obj_max, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags, float obj_rounding)\n{\n    if ((shadow_col & IM_COL32_A_MASK) == 0)\n        return;\n\n    ImVec2* inner_rect_points = NULL; // Points that make up the shape of the inner rectangle (used when it has rounded corners)\n    int inner_rect_points_count = 0;\n\n    // Generate a path describing the inner rectangle and copy it to our buffer\n    const bool is_filled = (flags & ImDrawFlags_ShadowCutOutShapeBackground) == 0;\n    const bool is_rounded = (obj_rounding > 0.0f) && ((flags & ImDrawFlags_RoundCornersMask_) != ImDrawFlags_RoundCornersNone); // Do we have rounded corners?\n    if (is_rounded && !is_filled)\n    {\n        IM_ASSERT(_Path.Size == 0);\n        PathRect(obj_min, obj_max, obj_rounding, flags);\n        inner_rect_points_count = _Path.Size;\n        inner_rect_points = (ImVec2*)alloca(inner_rect_points_count * sizeof(ImVec2)); //-V630\n        memcpy(inner_rect_points, _Path.Data, inner_rect_points_count * sizeof(ImVec2));\n        _Path.Size = 0;\n    }\n\n    if (is_filled)\n        PrimReserve(6 * 9, 4 * 9); // Reserve space for adding unclipped chunks\n\n    // Draw the relevant chunks of the texture (the texture is split into a 3x3 grid)\n    // FIXME-OPT: Might make sense to optimize/unroll for the fast paths (filled or not rounded)\n    for (int x = 0; x < 3; x++)\n    {\n        for (int y = 0; y < 3; y++)\n        {\n            const int uv_index = x + (y + y + y); // y*3 formatted so as to ensure the compiler avoids an actual multiply\n            const ImVec4 uvs = _Data->ShadowRectUvs[uv_index];\n\n            ImVec2 draw_min, draw_max;\n            switch (x)\n            {\n            case 0: draw_min.x = obj_min.x - shadow_thickness; draw_max.x = obj_min.x; break;\n            case 1: draw_min.x = obj_min.x; draw_max.x = obj_max.x; break;\n            case 2: draw_min.x = obj_max.x; draw_max.x = obj_max.x + shadow_thickness; break;\n            }\n            switch (y)\n            {\n            case 0: draw_min.y = obj_min.y - shadow_thickness; draw_max.y = obj_min.y; break;\n            case 1: draw_min.y = obj_min.y; draw_max.y = obj_max.y; break;\n            case 2: draw_min.y = obj_max.y; draw_max.y = obj_max.y + shadow_thickness; break;\n            }\n\n            ImVec2 uv_min(uvs.x, uvs.y);\n            ImVec2 uv_max(uvs.z, uvs.w);\n            if (is_filled)\n                PrimRectUV(draw_min + shadow_offset, draw_max + shadow_offset, uv_min, uv_max, shadow_col); // No clipping path (draw entire shadow)\n            else if (is_rounded)\n                AddSubtractedRect(this, draw_min + shadow_offset, draw_max + shadow_offset, uv_min, uv_max, inner_rect_points, inner_rect_points_count, shadow_col); // Complex path for rounded rectangles\n            else\n                AddSubtractedRect(this, draw_min + shadow_offset, draw_max + shadow_offset, uv_min, uv_max, obj_min, obj_max, shadow_col); // Simple fast path for non-rounded rectangles\n        }\n    }\n}\n\n// Add a shadow for a convex shape described by points and num_points\nvoid ImDrawList::AddShadowConvexPoly(const ImVec2* points, int points_count, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags)\n{\n    const bool is_filled = (flags & ImDrawFlags_ShadowCutOutShapeBackground) == 0;\n    IM_ASSERT((is_filled || (ImLengthSqr(shadow_offset) < 0.00001f)) && \"Drawing circle/convex shape shadows with no center fill and an offset is not currently supported\");\n    IM_ASSERT(points_count >= 3);\n\n    // Calculate poly vertex order\n    const int vertex_winding = (((points[0].x * (points[1].y - points[2].y)) + (points[1].x * (points[2].y - points[0].y)) + (points[2].x * (points[0].y - points[1].y))) < 0.0f) ? -1 : 1;\n\n    // If we're using anti-aliasing, then inset the shadow by 0.5 pixels to avoid unpleasant fringing artifacts\n    const bool use_inset_distance = (Flags & ImDrawListFlags_AntiAliasedFill) && (!is_filled);\n    const float inset_distance = 0.5f;\n\n    const ImVec4 uvs = _Data->ShadowRectUvs[9];\n\n    int tex_width = _Data->Font->OwnerAtlas->TexData->Width;\n    int tex_height = _Data->Font->OwnerAtlas->TexData->Height;\n    float inv_tex_width = 1.0f / (float)tex_width;\n    float inv_tex_height = 1.0f / (float)tex_height;\n\n    ImVec2 solid_uv = ImVec2(uvs.z, uvs.w); // UV at the inside of an edge\n    ImVec2 edge_uv = ImVec2(uvs.x, uvs.w); // UV at the outside of an edge\n\n    ImVec2 solid_to_edge_delta_texels = edge_uv - solid_uv; // Delta between the solid/edge points in texel-space (we need this in pixels - or, to be more precise, to be at a 1:1 aspect ratio - for the rotation to work)\n    solid_to_edge_delta_texels.x *= (float)tex_width;\n    solid_to_edge_delta_texels.y *= (float)tex_height;\n\n    // Our basic algorithm here is that we generate a straight section along each edge, and then either one or two curved corner triangles at the corners,\n    // which use an appropriate chunk of the texture to generate a smooth curve.\n    const int num_edges = points_count;\n\n    // Normalize a vector\n#define NORMALIZE(vec) ((vec) / ImLength((vec), 0.001f))\n\n    const int required_stack_mem = (num_edges * sizeof(ImVec2)) + (num_edges * sizeof(float));\n    ImU8* base_mem_for_normals_and_edges = (ImU8*)alloca(required_stack_mem);\n    ImU8* mem_for_normals_and_edges = (ImU8*)base_mem_for_normals_and_edges;\n\n    // Calculate edge normals\n    ImVec2* edge_normals = (ImVec2*)(void*)mem_for_normals_and_edges;\n    mem_for_normals_and_edges += num_edges * sizeof(ImVec2);\n\n    for (int edge_index = 0; edge_index < num_edges; edge_index++)\n    {\n        ImVec2 edge_start = points[edge_index]; // No need to apply offset here because the normal is unaffected\n        ImVec2 edge_end = points[(edge_index + 1) % num_edges];\n        ImVec2 edge_normal = NORMALIZE(ImVec2(edge_end.y - edge_start.y, -(edge_end.x - edge_start.x)));\n        edge_normals[edge_index] = edge_normal * (float)vertex_winding; // Flip normals for reverse winding\n    }\n\n    // Pre-calculate edge scales\n    // We need to do this because we need the edge strips to have widths that match up with the corner sections, otherwise pixel cracking can occur along the boundaries\n    float* edge_size_scales = (float*)(void*)mem_for_normals_and_edges;\n    mem_for_normals_and_edges += num_edges * sizeof(float);\n    IM_ASSERT_PARANOID(mem_for_normals_and_edges == (base_mem_for_normals_and_edges + required_stack_mem)); // Check we used exactly what we allocated\n\n    {\n        ImVec2 prev_edge_normal = edge_normals[num_edges - 1];\n        for (int edge_index = 0; edge_index < num_edges; edge_index++)\n        {\n            ImVec2 edge_normal = edge_normals[edge_index];\n            float cos_angle_coverage = ImDot(edge_normal, prev_edge_normal);\n\n            if (cos_angle_coverage < 0.999999f)\n            {\n                // If we are covering more than 90 degrees we need an intermediate vertex to stop the required expansion tending towards infinity.\n                // And thus the effective angle will be halved (matches the similar code in loop below)\n                float angle_coverage = ImAcos(cos_angle_coverage);\n                if (cos_angle_coverage <= 0.0f) // -V1051\n                    angle_coverage *= 0.5f;\n                edge_size_scales[edge_index] = 1.0f / ImCos(angle_coverage * 0.5f); // How much we need to expand our size by to avoid clipping the corner of the texture off\n            }\n            else\n            {\n                edge_size_scales[edge_index] = 1.0f; // No corner, thus default scale\n            }\n\n            prev_edge_normal = edge_normal;\n        }\n    }\n\n    const int max_vertices = (4 + (3 * 2) + (is_filled ? 1 : 0)) * num_edges; // 4 vertices per edge plus 3*2 for potentially two corner triangles, plus one per vertex for fill\n    const int max_indices = ((6 + (3 * 2)) * num_edges) + (is_filled ? ((num_edges - 2) * 3) : 0); // 2 tris per edge plus up to two corner triangles, plus fill triangles\n    PrimReserve(max_indices, max_vertices);\n    ImDrawIdx* idx_write = _IdxWritePtr;\n    ImDrawVert* vtx_write = _VtxWritePtr;\n    ImDrawIdx current_idx = (ImDrawIdx)_VtxCurrentIdx;\n\n    //ImVec2 previous_edge_start = points[0] + offset;\n    ImVec2 prev_edge_normal = edge_normals[num_edges - 1];\n    ImVec2 edge_start = points[0] + shadow_offset;\n\n    if (use_inset_distance)\n        edge_start -= NORMALIZE(edge_normals[0] + prev_edge_normal) * inset_distance;\n\n    for (int edge_index = 0; edge_index < num_edges; edge_index++)\n    {\n        ImVec2 edge_end = points[(edge_index + 1) % num_edges] + shadow_offset;\n        ImVec2 edge_normal = edge_normals[edge_index];\n        const float size_scale_start = edge_size_scales[edge_index];\n        const float size_scale_end = edge_size_scales[(edge_index + 1) % num_edges];\n\n        if (use_inset_distance)\n            edge_end -= NORMALIZE(edge_normals[(edge_index + 1) % num_edges] + edge_normal) * inset_distance;\n\n        // Add corner section\n        float cos_angle_coverage = ImDot(edge_normal, prev_edge_normal);\n        if (cos_angle_coverage < 0.999999f) // Don't fill if the corner is actually straight\n        {\n            // If we are covering more than 90 degrees we need an intermediate vertex to stop the required expansion tending towards infinity.\n            // And thus the effective angle has been halved (matches the similar code in loop above)\n            int num_steps = (cos_angle_coverage <= 0.0f) ? 2 : 1;\n\n            for (int step = 0; step < num_steps; step++)\n            {\n                if (num_steps > 1)\n                {\n                    if (step == 0)\n                        edge_normal = NORMALIZE(edge_normal + prev_edge_normal); // Use half-way normal for first step\n                    else\n                        edge_normal = edge_normals[edge_index]; // Then use the \"real\" next edge normal for the second\n\n                    cos_angle_coverage = ImDot(edge_normal, prev_edge_normal); // Recalculate angle\n                }\n\n                // Calculate UV for the section of the curved texture\n\n                const float angle_coverage = ImAcos(cos_angle_coverage);\n                const float sin_angle_coverage = ImSin(angle_coverage);\n\n                ImVec2 edge_delta = solid_to_edge_delta_texels;\n                edge_delta *= size_scale_start;\n\n                ImVec2 rotated_edge_delta = ImVec2((edge_delta.x * cos_angle_coverage) + (edge_delta.y * sin_angle_coverage), (edge_delta.x * sin_angle_coverage) + (edge_delta.y * cos_angle_coverage));\n\n                // Convert from texels back into UV space\n                edge_delta.x *= inv_tex_width;\n                edge_delta.y *= inv_tex_height;\n                rotated_edge_delta.x *= inv_tex_width;\n                rotated_edge_delta.y *= inv_tex_height;\n\n                ImVec2 expanded_edge_uv = solid_uv + edge_delta;\n                ImVec2 other_edge_uv = solid_uv + rotated_edge_delta; // Rotated UV to encompass the necessary section of the curve\n\n                float expanded_thickness = shadow_thickness * size_scale_start;\n\n                // Add a triangle to fill the corner\n                ImVec2 outer_edge_start = edge_start + (prev_edge_normal * expanded_thickness);\n                ImVec2 outer_edge_end = edge_start + (edge_normal * expanded_thickness);\n\n                vtx_write->pos = edge_start; vtx_write->col = shadow_col; vtx_write->uv = solid_uv; vtx_write++;\n                vtx_write->pos = outer_edge_end;   vtx_write->col = shadow_col; vtx_write->uv = expanded_edge_uv; vtx_write++;\n                vtx_write->pos = outer_edge_start; vtx_write->col = shadow_col; vtx_write->uv = other_edge_uv; vtx_write++;\n\n                *(idx_write++) = current_idx;\n                *(idx_write++) = current_idx + 1;\n                *(idx_write++) = current_idx + 2;\n                current_idx += 3;\n\n                prev_edge_normal = edge_normal;\n            }\n        }\n\n        // Add section along edge\n        const float edge_length = ImLength(edge_end - edge_start, 0.0f);\n        if (edge_length > 0.00001f) // Don't try and process degenerate edges\n        {\n            ImVec2 outer_edge_start = edge_start + (edge_normal * shadow_thickness * size_scale_start);\n            ImVec2 outer_edge_end = edge_end + (edge_normal * shadow_thickness * size_scale_end);\n            ImVec2 scaled_edge_uv_start = solid_uv + ((edge_uv - solid_uv) * size_scale_start);\n            ImVec2 scaled_edge_uv_end = solid_uv + ((edge_uv - solid_uv) * size_scale_end);\n\n            // Write vertices, inner first, then outer\n            vtx_write->pos = edge_start; vtx_write->col = shadow_col; vtx_write->uv = solid_uv; vtx_write++;\n            vtx_write->pos = edge_end; vtx_write->col = shadow_col; vtx_write->uv = solid_uv; vtx_write++;\n            vtx_write->pos = outer_edge_end; vtx_write->col = shadow_col; vtx_write->uv = scaled_edge_uv_end; vtx_write++;\n            vtx_write->pos = outer_edge_start; vtx_write->col = shadow_col; vtx_write->uv = scaled_edge_uv_start; vtx_write++;\n\n            *(idx_write++) = current_idx;\n            *(idx_write++) = current_idx + 1;\n            *(idx_write++) = current_idx + 2;\n            *(idx_write++) = current_idx;\n            *(idx_write++) = current_idx + 2;\n            *(idx_write++) = current_idx + 3;\n            current_idx += 4;\n        }\n\n        edge_start = edge_end;\n    }\n\n    // Fill if requested\n    if (is_filled)\n    {\n        // Add vertices\n        for (int edge_index = 0; edge_index < num_edges; edge_index++)\n        {\n            vtx_write->pos = points[edge_index] + shadow_offset;\n            vtx_write->col = shadow_col;\n            vtx_write->uv = solid_uv;\n            vtx_write++;\n        }\n\n        // Add triangles\n        for (int edge_index = 2; edge_index < num_edges; edge_index++)\n        {\n            *(idx_write++) = current_idx;\n            *(idx_write++) = (ImDrawIdx)(current_idx + edge_index - 1);\n            *(idx_write++) = (ImDrawIdx)(current_idx + edge_index);\n        }\n\n        current_idx += (ImDrawIdx)num_edges;\n    }\n\n    // Release any unused vertices/indices\n    int used_indices = (int)(idx_write - _IdxWritePtr);\n    int used_vertices = (int)(vtx_write - _VtxWritePtr);\n    _IdxWritePtr = idx_write;\n    _VtxWritePtr = vtx_write;\n    _VtxCurrentIdx = current_idx;\n    PrimUnreserve(max_indices - used_indices, max_vertices - used_vertices);\n#undef NORMALIZE\n}\n\n// Draw a shadow for a circular object\n// Uses the draw path and so wipes any existing data there\nvoid ImDrawList::AddShadowCircle(const ImVec2& obj_center, float obj_radius, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags, int num_segments)\n{\n    // Obtain segment count\n    if (num_segments <= 0)\n    {\n        // Automatic segment count\n        const int radius_idx = (int)obj_radius - 1;\n        if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))\n            num_segments = _Data->CircleSegmentCounts[radius_idx]; // Use cached value\n        else\n            num_segments = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(obj_radius, _Data->CircleSegmentMaxError);\n    }\n    else\n    {\n        // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)\n        num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX);\n    }\n\n    // Generate a path describing the inner circle and copy it to our buffer\n    IM_ASSERT(_Path.Size == 0);\n    const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;\n    if (num_segments == 12)\n        PathArcToFast(obj_center, obj_radius, 0, 12 - 1);\n    else\n        PathArcTo(obj_center, obj_radius, 0.0f, a_max, num_segments - 1);\n\n    // Draw the shadow using the convex shape code\n    AddShadowConvexPoly(_Path.Data, _Path.Size, shadow_col, shadow_thickness, shadow_offset, flags);\n    _Path.Size = 0;\n}\n\nvoid ImDrawList::AddShadowNGon(const ImVec2& obj_center, float obj_radius, ImU32 shadow_col, float shadow_thickness, const ImVec2& shadow_offset, ImDrawFlags flags, int num_segments)\n{\n    IM_ASSERT(num_segments != 0);\n    AddShadowCircle(obj_center, obj_radius, shadow_col, shadow_thickness, shadow_offset, flags, num_segments);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImDrawListSplitter\n//-----------------------------------------------------------------------------\n// FIXME: This may be a little confusing, trying to be a little too low-level/optimal instead of just doing vector swap..\n//-----------------------------------------------------------------------------\n\nvoid ImDrawListSplitter::ClearFreeMemory()\n{\n    for (int i = 0; i < _Channels.Size; i++)\n    {\n        if (i == _Current)\n            memset(&_Channels[i], 0, sizeof(_Channels[i]));  // Current channel is a copy of CmdBuffer/IdxBuffer, don't destruct again\n        _Channels[i]._CmdBuffer.clear();\n        _Channels[i]._IdxBuffer.clear();\n    }\n    _Current = 0;\n    _Count = 1;\n    _Channels.clear();\n}\n\nvoid ImDrawListSplitter::Split(ImDrawList* draw_list, int channels_count)\n{\n    IM_UNUSED(draw_list);\n    IM_ASSERT(_Current == 0 && _Count <= 1 && \"Nested channel splitting is not supported. Please use separate instances of ImDrawListSplitter.\");\n    int old_channels_count = _Channels.Size;\n    if (old_channels_count < channels_count)\n    {\n        _Channels.reserve(channels_count); // Avoid over reserving since this is likely to stay stable\n        _Channels.resize(channels_count);\n    }\n    _Count = channels_count;\n\n    // Channels[] (24/32 bytes each) hold storage that we'll swap with draw_list->_CmdBuffer/_IdxBuffer\n    // The content of Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to.\n    // When we switch to the next channel, we'll copy draw_list->_CmdBuffer/_IdxBuffer into Channels[0] and then Channels[1] into draw_list->CmdBuffer/_IdxBuffer\n    memset(&_Channels[0], 0, sizeof(ImDrawChannel));\n    for (int i = 1; i < channels_count; i++)\n    {\n        if (i >= old_channels_count)\n        {\n            IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel();\n        }\n        else\n        {\n            _Channels[i]._CmdBuffer.resize(0);\n            _Channels[i]._IdxBuffer.resize(0);\n        }\n    }\n}\n\nvoid ImDrawListSplitter::Merge(ImDrawList* draw_list)\n{\n    // Note that we never use or rely on _Channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use.\n    if (_Count <= 1)\n        return;\n\n    SetCurrentChannel(draw_list, 0);\n    draw_list->_PopUnusedDrawCmd();\n\n    // Calculate our final buffer sizes. Also fix the incorrect IdxOffset values in each command.\n    int new_cmd_buffer_count = 0;\n    int new_idx_buffer_count = 0;\n    ImDrawCmd* last_cmd = (_Count > 0 && draw_list->CmdBuffer.Size > 0) ? &draw_list->CmdBuffer.back() : NULL;\n    int idx_offset = last_cmd ? last_cmd->IdxOffset + last_cmd->ElemCount : 0;\n    for (int i = 1; i < _Count; i++)\n    {\n        ImDrawChannel& ch = _Channels[i];\n        if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0 && ch._CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd()\n            ch._CmdBuffer.pop_back();\n\n        if (ch._CmdBuffer.Size > 0 && last_cmd != NULL)\n        {\n            // Do not include ImDrawCmd_AreSequentialIdxOffset() in the compare as we rebuild IdxOffset values ourselves.\n            // Manipulating IdxOffset (e.g. by reordering draw commands like done by RenderDimmedBackgroundBehindWindow()) is not supported within a splitter.\n            ImDrawCmd* next_cmd = &ch._CmdBuffer[0];\n            if (ImDrawCmd_HeaderCompare(last_cmd, next_cmd) == 0 && last_cmd->UserCallback == NULL && next_cmd->UserCallback == NULL)\n            {\n                // Merge previous channel last draw command with current channel first draw command if matching.\n                last_cmd->ElemCount += next_cmd->ElemCount;\n                idx_offset += next_cmd->ElemCount;\n                ch._CmdBuffer.erase(ch._CmdBuffer.Data); // FIXME-OPT: Improve for multiple merges.\n            }\n        }\n        if (ch._CmdBuffer.Size > 0)\n            last_cmd = &ch._CmdBuffer.back();\n        new_cmd_buffer_count += ch._CmdBuffer.Size;\n        new_idx_buffer_count += ch._IdxBuffer.Size;\n        for (int cmd_n = 0; cmd_n < ch._CmdBuffer.Size; cmd_n++)\n        {\n            ch._CmdBuffer.Data[cmd_n].IdxOffset = idx_offset;\n            idx_offset += ch._CmdBuffer.Data[cmd_n].ElemCount;\n        }\n    }\n    draw_list->CmdBuffer.resize(draw_list->CmdBuffer.Size + new_cmd_buffer_count);\n    draw_list->IdxBuffer.resize(draw_list->IdxBuffer.Size + new_idx_buffer_count);\n\n    // Write commands and indices in order (they are fairly small structures, we don't copy vertices only indices)\n    ImDrawCmd* cmd_write = draw_list->CmdBuffer.Data + draw_list->CmdBuffer.Size - new_cmd_buffer_count;\n    ImDrawIdx* idx_write = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size - new_idx_buffer_count;\n    for (int i = 1; i < _Count; i++)\n    {\n        ImDrawChannel& ch = _Channels[i];\n        if (int sz = ch._CmdBuffer.Size) { memcpy(cmd_write, ch._CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; }\n        if (int sz = ch._IdxBuffer.Size) { memcpy(idx_write, ch._IdxBuffer.Data, sz * sizeof(ImDrawIdx)); idx_write += sz; }\n    }\n    draw_list->_IdxWritePtr = idx_write;\n\n    // Ensure there's always a non-callback draw command trailing the command-buffer\n    if (draw_list->CmdBuffer.Size == 0 || draw_list->CmdBuffer.back().UserCallback != NULL)\n        draw_list->AddDrawCmd();\n\n    // If current command is used with different settings we need to add a new command\n    ImDrawCmd* curr_cmd = &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1];\n    if (curr_cmd->ElemCount == 0)\n        ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TexRef, VtxOffset\n    else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0)\n        draw_list->AddDrawCmd();\n\n    _Count = 1;\n}\n\nvoid ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx)\n{\n    IM_ASSERT(idx >= 0 && idx < _Count);\n    if (_Current == idx)\n        return;\n\n    // Overwrite ImVector (12/16 bytes), four times. This is merely a silly optimization instead of doing .swap()\n    memcpy(&_Channels.Data[_Current]._CmdBuffer, &draw_list->CmdBuffer, sizeof(draw_list->CmdBuffer));\n    memcpy(&_Channels.Data[_Current]._IdxBuffer, &draw_list->IdxBuffer, sizeof(draw_list->IdxBuffer));\n    _Current = idx;\n    memcpy(&draw_list->CmdBuffer, &_Channels.Data[idx]._CmdBuffer, sizeof(draw_list->CmdBuffer));\n    memcpy(&draw_list->IdxBuffer, &_Channels.Data[idx]._IdxBuffer, sizeof(draw_list->IdxBuffer));\n    draw_list->_IdxWritePtr = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size;\n\n    // If current command is used with different settings we need to add a new command\n    ImDrawCmd* curr_cmd = (draw_list->CmdBuffer.Size == 0) ? NULL : &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1];\n    if (curr_cmd == NULL)\n        draw_list->AddDrawCmd();\n    else if (curr_cmd->ElemCount == 0)\n        ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TexRef, VtxOffset\n    else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0)\n        draw_list->AddDrawCmd();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImDrawData\n//-----------------------------------------------------------------------------\n\nvoid ImDrawData::Clear()\n{\n    Valid = false;\n    CmdListsCount = TotalIdxCount = TotalVtxCount = 0;\n    CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them.\n    DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f);\n    OwnerViewport = NULL;\n    Textures = NULL;\n}\n\n// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list\n// as long at it is expected that the result will be later merged into draw_data->CmdLists[].\nvoid ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)\n{\n    if (draw_list->CmdBuffer.Size == 0)\n        return;\n    if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL)\n        return;\n\n    // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.\n    // May trigger for you if you are using PrimXXX functions incorrectly.\n    IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);\n    IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);\n    if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))\n        IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);\n\n    // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)\n    // If this assert triggers because you are drawing lots of stuff manually:\n    // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.\n    //   Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.\n    // - If you want large meshes with more than 64K vertices, you can either:\n    //   (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.\n    //       Most example backends already support this from 1.71. Pre-1.71 backends won't.\n    //       Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.\n    //   (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.\n    //       Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:\n    //         glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);\n    //       Your own engine or render API may use different parameters or function calls to specify index sizes.\n    //       2 and 4 bytes indices are generally supported by most graphics API.\n    // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching\n    //   the 64K limit to split your draw commands in multiple draw lists.\n    if (sizeof(ImDrawIdx) == 2)\n        IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && \"Too many vertices in ImDrawList using 16-bit indices. Read comment above\");\n\n    // Resolve callback data pointers\n    if (draw_list->_CallbacksDataBuf.Size > 0)\n        for (ImDrawCmd& cmd : draw_list->CmdBuffer)\n            if (cmd.UserCallback != NULL && cmd.UserCallbackDataOffset != -1 && cmd.UserCallbackDataSize > 0)\n                cmd.UserCallbackData = draw_list->_CallbacksDataBuf.Data + cmd.UserCallbackDataOffset;\n\n    // Add to output list + records state in ImDrawData\n    out_list->push_back(draw_list);\n    draw_data->CmdListsCount++;\n    draw_data->TotalVtxCount += draw_list->VtxBuffer.Size;\n    draw_data->TotalIdxCount += draw_list->IdxBuffer.Size;\n}\n\nvoid ImDrawData::AddDrawList(ImDrawList* draw_list)\n{\n    IM_ASSERT(CmdLists.Size == CmdListsCount);\n    draw_list->_PopUnusedDrawCmd();\n    ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list);\n}\n\n// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!\nvoid ImDrawData::DeIndexAllBuffers()\n{\n    ImVector<ImDrawVert> new_vtx_buffer;\n    TotalVtxCount = TotalIdxCount = 0;\n    for (ImDrawList* draw_list : CmdLists)\n    {\n        if (draw_list->IdxBuffer.empty())\n            continue;\n        new_vtx_buffer.resize(draw_list->IdxBuffer.Size);\n        for (int j = 0; j < draw_list->IdxBuffer.Size; j++)\n            new_vtx_buffer[j] = draw_list->VtxBuffer[draw_list->IdxBuffer[j]];\n        draw_list->VtxBuffer.swap(new_vtx_buffer);\n        draw_list->IdxBuffer.resize(0);\n        TotalVtxCount += draw_list->VtxBuffer.Size;\n    }\n}\n\n// Helper to scale the ClipRect field of each ImDrawCmd.\n// Use if your final output buffer is at a different scale than draw_data->DisplaySize,\n// or if there is a difference between your window resolution and framebuffer resolution.\nvoid ImDrawData::ScaleClipRects(const ImVec2& fb_scale)\n{\n    for (ImDrawList* draw_list : CmdLists)\n        for (ImDrawCmd& cmd : draw_list->CmdBuffer)\n            cmd.ClipRect = ImVec4(cmd.ClipRect.x * fb_scale.x, cmd.ClipRect.y * fb_scale.y, cmd.ClipRect.z * fb_scale.x, cmd.ClipRect.w * fb_scale.y);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Helpers ShadeVertsXXX functions\n//-----------------------------------------------------------------------------\n\n// Generic linear color gradient, write to RGB fields, leave A untouched.\nvoid ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1)\n{\n    ImVec2 gradient_extent = gradient_p1 - gradient_p0;\n    float gradient_inv_length2 = 1.0f / ImLengthSqr(gradient_extent);\n    ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;\n    ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;\n    const int col0_r = (int)(col0 >> IM_COL32_R_SHIFT) & 0xFF;\n    const int col0_g = (int)(col0 >> IM_COL32_G_SHIFT) & 0xFF;\n    const int col0_b = (int)(col0 >> IM_COL32_B_SHIFT) & 0xFF;\n    const int col_delta_r = ((int)(col1 >> IM_COL32_R_SHIFT) & 0xFF) - col0_r;\n    const int col_delta_g = ((int)(col1 >> IM_COL32_G_SHIFT) & 0xFF) - col0_g;\n    const int col_delta_b = ((int)(col1 >> IM_COL32_B_SHIFT) & 0xFF) - col0_b;\n    for (ImDrawVert* vert = vert_start; vert < vert_end; vert++)\n    {\n        float d = ImDot(vert->pos - gradient_p0, gradient_extent);\n        float t = ImClamp(d * gradient_inv_length2, 0.0f, 1.0f);\n        int r = (int)(col0_r + col_delta_r * t);\n        int g = (int)(col0_g + col_delta_g * t);\n        int b = (int)(col0_b + col_delta_b * t);\n        vert->col = (r << IM_COL32_R_SHIFT) | (g << IM_COL32_G_SHIFT) | (b << IM_COL32_B_SHIFT) | (vert->col & IM_COL32_A_MASK);\n    }\n}\n\n// Distribute UV over (a, b) rectangle\nvoid ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp)\n{\n    const ImVec2 size = b - a;\n    const ImVec2 uv_size = uv_b - uv_a;\n    const ImVec2 scale = ImVec2(\n        size.x != 0.0f ? (uv_size.x / size.x) : 0.0f,\n        size.y != 0.0f ? (uv_size.y / size.y) : 0.0f);\n\n    ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;\n    ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;\n    if (clamp)\n    {\n        const ImVec2 min = ImMin(uv_a, uv_b);\n        const ImVec2 max = ImMax(uv_a, uv_b);\n        for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)\n            vertex->uv = ImClamp(uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale), min, max);\n    }\n    else\n    {\n        for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)\n            vertex->uv = uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale);\n    }\n}\n\nvoid ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out)\n{\n    ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;\n    ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;\n    for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)\n        vertex->pos = ImRotate(vertex->pos- pivot_in, cos_a, sin_a) + pivot_out;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFontConfig\n//-----------------------------------------------------------------------------\n\n// FIXME-NEWATLAS: Oversample specification could be more dynamic. For now, favoring automatic selection.\nImFontConfig::ImFontConfig()\n{\n    memset((void*)this, 0, sizeof(*this));\n    FontDataOwnedByAtlas = true;\n    OversampleH = 0; // Auto == 1 or 2 depending on size\n    OversampleV = 0; // Auto == 1\n    ExtraSizeScale = 1.0f;\n    GlyphMaxAdvanceX = FLT_MAX;\n    RasterizerMultiply = 1.0f;\n    RasterizerDensity = 1.0f;\n    EllipsisChar = 0;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImTextureData\n//-----------------------------------------------------------------------------\n// - ImTextureData::Create()\n// - ImTextureData::DestroyPixels()\n//-----------------------------------------------------------------------------\n\nint ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format)\n{\n    switch (format)\n    {\n    case ImTextureFormat_Alpha8: return 1;\n    case ImTextureFormat_RGBA32: return 4;\n    }\n    IM_ASSERT(0);\n    return 0;\n}\n\nconst char* ImTextureDataGetStatusName(ImTextureStatus status)\n{\n    switch (status)\n    {\n    case ImTextureStatus_OK: return \"OK\";\n    case ImTextureStatus_Destroyed: return \"Destroyed\";\n    case ImTextureStatus_WantCreate: return \"WantCreate\";\n    case ImTextureStatus_WantUpdates: return \"WantUpdates\";\n    case ImTextureStatus_WantDestroy: return \"WantDestroy\";\n    }\n    return \"N/A\";\n}\n\nconst char* ImTextureDataGetFormatName(ImTextureFormat format)\n{\n    switch (format)\n    {\n    case ImTextureFormat_Alpha8: return \"Alpha8\";\n    case ImTextureFormat_RGBA32: return \"RGBA32\";\n    }\n    return \"N/A\";\n}\n\nvoid ImTextureData::Create(ImTextureFormat format, int w, int h)\n{\n    IM_ASSERT(Status == ImTextureStatus_Destroyed);\n    DestroyPixels();\n    Format = format;\n    Status = ImTextureStatus_WantCreate;\n    Width = w;\n    Height = h;\n    BytesPerPixel = ImTextureDataGetFormatBytesPerPixel(format);\n    UseColors = false;\n    Pixels = (unsigned char*)IM_ALLOC(Width * Height * BytesPerPixel);\n    IM_ASSERT(Pixels != NULL);\n    memset(Pixels, 0, Width * Height * BytesPerPixel);\n    UsedRect.x = UsedRect.y = UsedRect.w = UsedRect.h = 0;\n    UpdateRect.x = UpdateRect.y = (unsigned short)~0;\n    UpdateRect.w = UpdateRect.h = 0;\n}\n\nvoid ImTextureData::DestroyPixels()\n{\n    if (Pixels)\n        IM_FREE(Pixels);\n    Pixels = NULL;\n    UseColors = false;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFontAtlas, ImFontAtlasBuilder\n//-----------------------------------------------------------------------------\n// - Default texture data encoded in ASCII\n// - ImFontAtlas()\n// - ImFontAtlas::Clear()\n// - ImFontAtlas::CompactCache()\n// - ImFontAtlas::ClearInputData()\n// - ImFontAtlas::ClearTexData()\n// - ImFontAtlas::ClearFonts()\n//-----------------------------------------------------------------------------\n// - ImFontAtlasUpdateNewFrame()\n// - ImFontAtlasTextureBlockConvert()\n// - ImFontAtlasTextureBlockPostProcess()\n// - ImFontAtlasTextureBlockPostProcessMultiply()\n// - ImFontAtlasTextureBlockFill()\n// - ImFontAtlasTextureBlockCopy()\n// - ImFontAtlasTextureBlockQueueUpload()\n//-----------------------------------------------------------------------------\n// - ImFontAtlas::GetTexDataAsAlpha8() [legacy]\n// - ImFontAtlas::GetTexDataAsRGBA32() [legacy]\n// - ImFontAtlas::Build() [legacy]\n//-----------------------------------------------------------------------------\n// - ImFontAtlas::AddFont()\n// - ImFontAtlas::AddFontDefault()\n// - ImFontAtlas::AddFontDefaultBitmap()\n// - ImFontAtlas::AddFontDefaultVector()\n// - ImFontAtlas::AddFontFromFileTTF()\n// - ImFontAtlas::AddFontFromMemoryTTF()\n// - ImFontAtlas::AddFontFromMemoryCompressedTTF()\n// - ImFontAtlas::AddFontFromMemoryCompressedBase85TTF()\n// - ImFontAtlas::RemoveFont()\n// - ImFontAtlasBuildNotifySetFont()\n//-----------------------------------------------------------------------------\n// - ImFontAtlas::AddCustomRect()\n// - ImFontAtlas::RemoveCustomRect()\n// - ImFontAtlas::GetCustomRect()\n// - ImFontAtlas::AddCustomRectFontGlyph() [legacy]\n// - ImFontAtlas::AddCustomRectFontGlyphForSize() [legacy]\n// - ImFontAtlasGetMouseCursorTexData()\n//-----------------------------------------------------------------------------\n// - ImFontAtlasBuildMain()\n// - ImFontAtlasBuildSetupFontLoader()\n// - ImFontAtlasBuildPreloadAllGlyphRanges()\n// - ImFontAtlasBuildUpdatePointers()\n// - ImFontAtlasBuildRenderBitmapFromString()\n// - ImFontAtlasBuildUpdateBasicTexData()\n// - ImFontAtlasBuildUpdateLinesTexData()\n// - ImFontAtlasBuildAddFont()\n// - ImFontAtlasBuildSetupFontBakedEllipsis()\n// - ImFontAtlasBuildSetupFontBakedBlanks()\n// - ImFontAtlasBuildSetupFontBakedFallback()\n// - ImFontAtlasBuildSetupFontSpecialGlyphs()\n// - ImFontAtlasBuildDiscardBakes()\n// - ImFontAtlasBuildDiscardFontBakedGlyph()\n// - ImFontAtlasBuildDiscardFontBaked()\n// - ImFontAtlasBuildDiscardFontBakes()\n//-----------------------------------------------------------------------------\n// - ImFontAtlasAddDrawListSharedData()\n// - ImFontAtlasRemoveDrawListSharedData()\n// - ImFontAtlasUpdateDrawListsTextures()\n// - ImFontAtlasUpdateDrawListsSharedData()\n//-----------------------------------------------------------------------------\n// - ImFontAtlasBuildSetTexture()\n// - ImFontAtlasBuildAddTexture()\n// - ImFontAtlasBuildMakeSpace()\n// - ImFontAtlasBuildRepackTexture()\n// - ImFontAtlasBuildGrowTexture()\n// - ImFontAtlasBuildRepackOrGrowTexture()\n// - ImFontAtlasBuildGetTextureSizeEstimate()\n// - ImFontAtlasBuildCompactTexture()\n// - ImFontAtlasBuildInit()\n// - ImFontAtlasBuildDestroy()\n//-----------------------------------------------------------------------------\n// - ImFontAtlasPackInit()\n// - ImFontAtlasPackAllocRectEntry()\n// - ImFontAtlasPackReuseRectEntry()\n// - ImFontAtlasPackDiscardRect()\n// - ImFontAtlasPackAddRect()\n// - ImFontAtlasPackGetRect()\n//-----------------------------------------------------------------------------\n// - ImFontBaked_BuildGrowIndex()\n// - ImFontBaked_BuildLoadGlyph()\n// - ImFontBaked_BuildLoadGlyphAdvanceX()\n// - ImFontAtlasDebugLogTextureRequests()\n//-----------------------------------------------------------------------------\n// - ImFontAtlasGetFontLoaderForStbTruetype()\n//-----------------------------------------------------------------------------\n\n// A work of art lies ahead! (. = white layer, X = black layer, others are blank)\n// The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes.\n// (This is used when io.MouseDrawCursor = true)\nconst int FONT_ATLAS_DEFAULT_TEX_DATA_W = 122; // Actual texture will be 2 times that + 1 spacing.\nconst int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27;\nstatic const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] =\n{\n    \"..-         -XXXXXXX-    X    -           X           -XXXXXXX          -          XXXXXXX-     XX          - XX       XX \"\n    \"..-         -X.....X-   X.X   -          X.X          -X.....X          -          X.....X-    X..X         -X..X     X..X\"\n    \"---         -XXX.XXX-  X...X  -         X...X         -X....X           -           X....X-    X..X         -X...X   X...X\"\n    \"X           -  X.X  - X.....X -        X.....X        -X...X            -            X...X-    X..X         - X...X X...X \"\n    \"XX          -  X.X  -X.......X-       X.......X       -X..X.X           -           X.X..X-    X..X         -  X...X...X  \"\n    \"X.X         -  X.X  -XXXX.XXXX-       XXXX.XXXX       -X.X X.X          -          X.X X.X-    X..XXX       -   X.....X   \"\n    \"X..X        -  X.X  -   X.X   -          X.X          -XX   X.X         -         X.X   XX-    X..X..XXX    -    X...X    \"\n    \"X...X       -  X.X  -   X.X   -    XX    X.X    XX    -      X.X        -        X.X      -    X..X..X..XX  -     X.X     \"\n    \"X....X      -  X.X  -   X.X   -   X.X    X.X    X.X   -       X.X       -       X.X       -    X..X..X..X.X -    X...X    \"\n    \"X.....X     -  X.X  -   X.X   -  X..X    X.X    X..X  -        X.X      -      X.X        -XXX X..X..X..X..X-   X.....X   \"\n    \"X......X    -  X.X  -   X.X   - X...XXXXXX.XXXXXX...X -         X.X   XX-XX   X.X         -X..XX........X..X-  X...X...X  \"\n    \"X.......X   -  X.X  -   X.X   -X.....................X-          X.X X.X-X.X X.X          -X...X...........X- X...X X...X \"\n    \"X........X  -  X.X  -   X.X   - X...XXXXXX.XXXXXX...X -           X.X..X-X..X.X           - X..............X-X...X   X...X\"\n    \"X.........X -XXX.XXX-   X.X   -  X..X    X.X    X..X  -            X...X-X...X            -  X.............X-X..X     X..X\"\n    \"X..........X-X.....X-   X.X   -   X.X    X.X    X.X   -           X....X-X....X           -  X.............X- XX       XX \"\n    \"X......XXXXX-XXXXXXX-   X.X   -    XX    X.X    XX    -          X.....X-X.....X          -   X............X--------------\"\n    \"X...X..X    ---------   X.X   -          X.X          -          XXXXXXX-XXXXXXX          -   X...........X -             \"\n    \"X..X X..X   -       -XXXX.XXXX-       XXXX.XXXX       -------------------------------------    X..........X -             \"\n    \"X.X  X..X   -       -X.......X-       X.......X       -    XX           XX    -           -    X..........X -             \"\n    \"XX    X..X  -       - X.....X -        X.....X        -   X.X           X.X   -           -     X........X  -             \"\n    \"      X..X  -       -  X...X  -         X...X         -  X..X           X..X  -           -     X........X  -             \"\n    \"       XX   -       -   X.X   -          X.X          - X...XXXXXXXXXXXXX...X -           -     XXXXXXXXXX  -             \"\n    \"-------------       -    X    -           X           -X.....................X-           -------------------             \"\n    \"                    ----------------------------------- X...XXXXXXXXXXXXX...X -                                           \"\n    \"                                                      -  X..X           X..X  -                                           \"\n    \"                                                      -   X.X           X.X   -                                           \"\n    \"                                                      -    XX           XX    -                                           \"\n};\n\nstatic const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] =\n{\n    // Pos ........ Size ......... Offset ......\n    { ImVec2( 0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow\n    { ImVec2(13,0), ImVec2( 7,16), ImVec2( 1, 8) }, // ImGuiMouseCursor_TextInput\n    { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_ResizeAll\n    { ImVec2(21,0), ImVec2( 9,23), ImVec2( 4,11) }, // ImGuiMouseCursor_ResizeNS\n    { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 4) }, // ImGuiMouseCursor_ResizeEW\n    { ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW\n    { ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE\n    { ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand\n    { ImVec2(0,3),  ImVec2(12,19), ImVec2(0, 0) },  // ImGuiMouseCursor_Wait       // Arrow + custom code in ImGui::RenderMouseCursor()\n    { ImVec2(0,3),  ImVec2(12,19), ImVec2(0, 0) },  // ImGuiMouseCursor_Progress   // Arrow + custom code in ImGui::RenderMouseCursor()\n    { ImVec2(109,0),ImVec2(13,15), ImVec2( 6, 7) }, // ImGuiMouseCursor_NotAllowed\n};\n\n#define IM_FONTGLYPH_INDEX_UNUSED           ((ImU16)-1) // 0xFFFF\n#define IM_FONTGLYPH_INDEX_NOT_FOUND        ((ImU16)-2) // 0xFFFE\n\nImFontAtlas::ImFontAtlas()\n{\n    memset((void*)this, 0, sizeof(*this));\n    TexDesiredFormat = ImTextureFormat_RGBA32;\n    TexGlyphPadding = 1;\n    TexMinWidth = 512;\n    TexMinHeight = 128;\n    TexMaxWidth = 8192;\n    TexMaxHeight = 8192;\n    TexRef._TexID = ImTextureID_Invalid;\n    RendererHasTextures = false; // Assumed false by default, as apps can call e.g Atlas::Build() after backend init and before ImGui can update.\n    TexNextUniqueID = 1;\n    FontNextUniqueID = 1;\n    Builder = NULL;\n\n    // FIXME-SHADOWS: move elsewhere?\n    ShadowRectIds[0] = ShadowRectIds[1] = -1;\n    ShadowTexConfig.SetupDefaults();\n}\n\nImFontAtlas::~ImFontAtlas()\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas!\");\n    RendererHasTextures = false; // Full Clear() is supported, but ClearTexData() only isn't.\n    ClearFonts();\n    ClearTexData();\n    TexList.clear_delete();\n    TexData = NULL;\n}\n\n// If you call this mid-frame, you would need to add new font and bind them!\nvoid ImFontAtlas::Clear()\n{\n    bool backup_renderer_has_textures = RendererHasTextures;\n    RendererHasTextures = false; // Full Clear() is supported, but ClearTexData() only isn't.\n    ClearFonts();\n    ClearTexData();\n    RendererHasTextures = backup_renderer_has_textures;\n}\n\nvoid ImFontAtlas::CompactCache()\n{\n    ImFontAtlasTextureCompact(this);\n}\n\nvoid ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader)\n{\n    ImFontAtlasBuildSetupFontLoader(this, font_loader);\n}\n\nvoid ImFontAtlas::ClearInputData()\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas!\");\n\n    for (ImFont* font : Fonts)\n        ImFontAtlasFontDestroyOutput(this, font);\n    for (ImFontConfig& font_cfg : Sources)\n        ImFontAtlasFontDestroySourceData(this, &font_cfg);\n    for (ImFont* font : Fonts)\n    {\n        // When clearing this we lose access to the font name and other information used to build the font.\n        font->Sources.clear();\n        font->Flags |= ImFontFlags_NoLoadGlyphs;\n    }\n    Sources.clear();\n\n    // FIXME-SHADOWS: Move elsewhere?\n    ShadowRectIds[0] = ShadowRectIds[1] = -1;\n}\n\n// Clear CPU-side copy of the texture data.\nvoid ImFontAtlas::ClearTexData()\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas!\");\n    IM_ASSERT(RendererHasTextures == false && \"Not supported for dynamic atlases, but you may call Clear().\");\n    for (ImTextureData* tex : TexList)\n        tex->DestroyPixels();\n    //Locked = true; // Hoped to be able to lock this down but some reload patterns may not be happy with it.\n}\n\nvoid ImFontAtlas::ClearFonts()\n{\n    // FIXME-NEWATLAS: Illegal to remove currently bound font.\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas!\");\n    for (ImFont* font : Fonts)\n        ImFontAtlasBuildNotifySetFont(this, font, NULL);\n    ImFontAtlasBuildDestroy(this);\n    ClearInputData();\n    Fonts.clear_delete();\n    TexIsBuilt = false;\n    for (ImDrawListSharedData* shared_data : DrawListSharedDatas)\n        if (shared_data->FontAtlas == this)\n        {\n            shared_data->Font = NULL;\n            shared_data->FontScale = shared_data->FontSize = 0.0f;\n        }\n}\n\nstatic void ImFontAtlasBuildUpdateRendererHasTexturesFromContext(ImFontAtlas* atlas)\n{\n    // [LEGACY] Copy back the ImGuiBackendFlags_RendererHasTextures flag from ImGui context.\n    // - This is the 1% exceptional case where that dependency if useful, to bypass an issue where otherwise at the\n    //   time of an early call to Build(), it would be impossible for us to tell if the backend supports texture update.\n    // - Without this hack, we would have quite a pitfall as many legacy codebases have an early call to Build().\n    //   Whereas conversely, the portion of people using ImDrawList without ImGui is expected to be pathologically rare.\n    for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)\n        if (ImGuiContext* imgui_ctx = shared_data->Context)\n        {\n            atlas->RendererHasTextures = (imgui_ctx->IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0;\n            break;\n        }\n}\n\n// Called by NewFrame() for atlases owned by a context.\n// If you manually manage font atlases, you'll need to call this yourself.\n// - 'frame_count' needs to be provided because we can gc/prioritize baked fonts based on their age.\n// - 'frame_count' may not match those of all imgui contexts using this atlas, as contexts may be updated as different frequencies. But generally you can use ImGui::GetFrameCount() on one of your context.\nvoid ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures)\n{\n    IM_ASSERT(atlas->Builder == NULL || atlas->Builder->FrameCount < frame_count); // Protection against being called twice.\n    atlas->RendererHasTextures = renderer_has_textures;\n\n    // Check that font atlas was built or backend support texture reload in which case we can build now\n    if (atlas->RendererHasTextures)\n    {\n        atlas->TexIsBuilt = true;\n        if (atlas->Builder == NULL) // This will only happen if fonts were not already loaded.\n            ImFontAtlasBuildMain(atlas);\n    }\n    // Legacy backend\n    if (!atlas->RendererHasTextures)\n        IM_ASSERT_USER_ERROR(atlas->TexIsBuilt, \"Backend does not support ImGuiBackendFlags_RendererHasTextures, and font atlas is not built! Update backend OR make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8().\");\n    if (atlas->TexIsBuilt && atlas->Builder->PreloadedAllGlyphsRanges)\n        IM_ASSERT_USER_ERROR(atlas->RendererHasTextures == false, \"Called ImFontAtlas::Build() before ImGuiBackendFlags_RendererHasTextures got set! With new backends: you don't need to call Build().\");\n\n    // Clear BakedCurrent cache, this is important because it ensure the uncached path gets taken once.\n    // We also rely on ImFontBaked* pointers never crossing frames.\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    builder->FrameCount = frame_count;\n    for (ImFont* font : atlas->Fonts)\n        font->LastBaked = NULL;\n\n    // Garbage collect BakedPool\n    if (builder->BakedDiscardedCount > 0)\n    {\n        int dst_n = 0, src_n = 0;\n        for (; src_n < builder->BakedPool.Size; src_n++)\n        {\n            ImFontBaked* p_src = &builder->BakedPool[src_n];\n            if (p_src->WantDestroy)\n                continue;\n            ImFontBaked* p_dst = &builder->BakedPool[dst_n++];\n            if (p_dst == p_src)\n                continue;\n            memcpy(p_dst, p_src, sizeof(ImFontBaked));\n            builder->BakedMap.SetVoidPtr(p_dst->BakedId, p_dst);\n        }\n        IM_ASSERT(dst_n + builder->BakedDiscardedCount == src_n);\n        builder->BakedPool.Size -= builder->BakedDiscardedCount;\n        builder->BakedDiscardedCount = 0;\n    }\n\n    // Update texture status\n    for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)\n    {\n        ImTextureData* tex = atlas->TexList[tex_n];\n        bool remove_from_list = false;\n        if (tex->Status == ImTextureStatus_OK)\n        {\n            tex->Updates.resize(0);\n            tex->UpdateRect.x = tex->UpdateRect.y = (unsigned short)~0;\n            tex->UpdateRect.w = tex->UpdateRect.h = 0;\n        }\n        if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures)\n            IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && \"Backend set texture's TexID/BackendUserData but did not update Status to OK.\");\n\n        // Request destroy\n        // - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend.\n        // - We don't destroy pixels right away, as backend may have an in-flight copy from RAM.\n        if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy)\n        {\n            IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates);\n            tex->Status = ImTextureStatus_WantDestroy;\n        }\n\n        // If a texture has never reached the backend, they don't need to know about it.\n        // (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy\n        //  when invalidating graphics objects twice, which would previously remove it from the list and crash.)\n        if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL)\n            tex->Status = ImTextureStatus_Destroyed;\n\n        // Process texture being destroyed\n        if (tex->Status == ImTextureStatus_Destroyed)\n        {\n            IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && \"Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!\");\n            if (tex->WantDestroyNextFrame)\n                remove_from_list = true; // Destroy was scheduled by us\n            else\n                tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run)\n        }\n\n        // The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering.\n        // We allow the texture staying in _WantDestroy state and increment a counter which the backend can use to take its decision.\n        if (tex->Status == ImTextureStatus_WantDestroy)\n            tex->UnusedFrames++;\n\n        // Destroy and remove\n        if (remove_from_list)\n        {\n            IM_ASSERT(atlas->TexData != tex);\n            tex->DestroyPixels();\n            IM_DELETE(tex);\n            atlas->TexList.erase(atlas->TexList.begin() + tex_n);\n            tex_n--;\n        }\n    }\n}\n\nvoid ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h)\n{\n    IM_ASSERT(src_pixels != NULL && dst_pixels != NULL);\n    if (src_fmt == dst_fmt)\n    {\n        int line_sz = w * ImTextureDataGetFormatBytesPerPixel(src_fmt);\n        for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch)\n            memcpy(dst_pixels, src_pixels, line_sz);\n    }\n    else if (src_fmt == ImTextureFormat_Alpha8 && dst_fmt == ImTextureFormat_RGBA32)\n    {\n        for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch)\n        {\n            const ImU8* src_p = (const ImU8*)src_pixels;\n            ImU32* dst_p = (ImU32*)(void*)dst_pixels;\n            for (int nx = w; nx > 0; nx--)\n                *dst_p++ = IM_COL32(255, 255, 255, (unsigned int)(*src_p++));\n        }\n    }\n    else if (src_fmt == ImTextureFormat_RGBA32 && dst_fmt == ImTextureFormat_Alpha8)\n    {\n        for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch)\n        {\n            const ImU32* src_p = (const ImU32*)(void*)src_pixels;\n            ImU8* dst_p = (ImU8*)dst_pixels;\n            for (int nx = w; nx > 0; nx--)\n                *dst_p++ = ((*src_p++) >> IM_COL32_A_SHIFT) & 0xFF;\n        }\n    }\n    else\n    {\n        IM_ASSERT(0);\n    }\n}\n\n// Source buffer may be written to (used for in-place mods).\n// Post-process hooks may eventually be added here.\nvoid ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data)\n{\n    // Multiply operator (legacy)\n    if (data->FontSrc->RasterizerMultiply != 1.0f)\n        ImFontAtlasTextureBlockPostProcessMultiply(data, data->FontSrc->RasterizerMultiply);\n}\n\nvoid ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor)\n{\n    unsigned char* pixels = (unsigned char*)data->Pixels;\n    int pitch = data->Pitch;\n    if (data->Format == ImTextureFormat_Alpha8)\n    {\n        for (int ny = data->Height; ny > 0; ny--, pixels += pitch)\n        {\n            ImU8* p = (ImU8*)pixels;\n            for (int nx = data->Width; nx > 0; nx--, p++)\n            {\n                unsigned int v = ImMin((unsigned int)(*p * multiply_factor), (unsigned int)255);\n                *p = (unsigned char)v;\n            }\n        }\n    }\n    else if (data->Format == ImTextureFormat_RGBA32) //-V547\n    {\n        for (int ny = data->Height; ny > 0; ny--, pixels += pitch)\n        {\n            ImU32* p = (ImU32*)(void*)pixels;\n            for (int nx = data->Width; nx > 0; nx--, p++)\n            {\n                unsigned int a = ImMin((unsigned int)(((*p >> IM_COL32_A_SHIFT) & 0xFF) * multiply_factor), (unsigned int)255);\n                *p = IM_COL32((*p >> IM_COL32_R_SHIFT) & 0xFF, (*p >> IM_COL32_G_SHIFT) & 0xFF, (*p >> IM_COL32_B_SHIFT) & 0xFF, a);\n            }\n        }\n    }\n    else\n    {\n        IM_ASSERT(0);\n    }\n}\n\n// Fill with single color. We don't use this directly but it is convenient for anyone working on uploading custom rects.\nvoid ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col)\n{\n    if (dst_tex->Format == ImTextureFormat_Alpha8)\n    {\n        ImU8 col_a = (col >> IM_COL32_A_SHIFT) & 0xFF;\n        for (int y = 0; y < h; y++)\n            memset((ImU8*)dst_tex->GetPixelsAt(dst_x, dst_y + y), col_a, w);\n    }\n    else\n    {\n        for (int y = 0; y < h; y++)\n        {\n            ImU32* p = (ImU32*)(void*)dst_tex->GetPixelsAt(dst_x, dst_y + y);\n            for (int x = w; x > 0; x--, p++)\n                *p = col;\n        }\n    }\n}\n\n// Copy block from one texture to another\nvoid ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h)\n{\n    IM_ASSERT(src_tex->Pixels != NULL && dst_tex->Pixels != NULL);\n    IM_ASSERT(src_tex->Format == dst_tex->Format);\n    IM_ASSERT(src_x >= 0 && src_x + w <= src_tex->Width);\n    IM_ASSERT(src_y >= 0 && src_y + h <= src_tex->Height);\n    IM_ASSERT(dst_x >= 0 && dst_x + w <= dst_tex->Width);\n    IM_ASSERT(dst_y >= 0 && dst_y + h <= dst_tex->Height);\n    for (int y = 0; y < h; y++)\n        memcpy(dst_tex->GetPixelsAt(dst_x, dst_y + y), src_tex->GetPixelsAt(src_x, src_y + y), w * dst_tex->BytesPerPixel);\n}\n\n// Queue texture block update for renderer backend\nvoid ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h)\n{\n    IM_ASSERT(tex->Status != ImTextureStatus_WantDestroy && tex->Status != ImTextureStatus_Destroyed);\n    IM_ASSERT(x >= 0 && x <= 0xFFFF && y >= 0 && y <= 0xFFFF && w >= 0 && x + w <= 0x10000 && h >= 0 && y + h <= 0x10000);\n    IM_UNUSED(atlas);\n\n    ImTextureRect req = { (unsigned short)x, (unsigned short)y, (unsigned short)w, (unsigned short)h };\n    int new_x1 = ImMax(tex->UpdateRect.w == 0 ? 0 : tex->UpdateRect.x + tex->UpdateRect.w, req.x + req.w);\n    int new_y1 = ImMax(tex->UpdateRect.h == 0 ? 0 : tex->UpdateRect.y + tex->UpdateRect.h, req.y + req.h);\n    tex->UpdateRect.x = ImMin(tex->UpdateRect.x, req.x);\n    tex->UpdateRect.y = ImMin(tex->UpdateRect.y, req.y);\n    tex->UpdateRect.w = (unsigned short)(new_x1 - tex->UpdateRect.x);\n    tex->UpdateRect.h = (unsigned short)(new_y1 - tex->UpdateRect.y);\n    tex->UsedRect.x = ImMin(tex->UsedRect.x, req.x);\n    tex->UsedRect.y = ImMin(tex->UsedRect.y, req.y);\n    tex->UsedRect.w = (unsigned short)(ImMax(tex->UsedRect.x + tex->UsedRect.w, req.x + req.w) - tex->UsedRect.x);\n    tex->UsedRect.h = (unsigned short)(ImMax(tex->UsedRect.y + tex->UsedRect.h, req.y + req.h) - tex->UsedRect.y);\n    atlas->TexIsBuilt = false;\n\n    // No need to queue if status is == ImTextureStatus_WantCreate\n    if (tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantUpdates)\n    {\n        tex->Status = ImTextureStatus_WantUpdates;\n        tex->Updates.push_back(req);\n    }\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\nstatic void GetTexDataAsFormat(ImFontAtlas* atlas, ImTextureFormat format, unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)\n{\n    ImTextureData* tex = atlas->TexData;\n    if (!atlas->TexIsBuilt || tex == NULL || tex->Pixels == NULL || atlas->TexDesiredFormat != format)\n    {\n        atlas->TexDesiredFormat = format;\n        atlas->Build();\n        tex = atlas->TexData;\n    }\n    if (out_pixels) { *out_pixels = (unsigned char*)tex->Pixels; };\n    if (out_width) { *out_width = tex->Width; };\n    if (out_height) { *out_height = tex->Height; };\n    if (out_bytes_per_pixel) { *out_bytes_per_pixel = tex->BytesPerPixel; }\n}\n\nvoid ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)\n{\n    GetTexDataAsFormat(this, ImTextureFormat_Alpha8, out_pixels, out_width, out_height, out_bytes_per_pixel);\n}\n\nvoid ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)\n{\n    GetTexDataAsFormat(this, ImTextureFormat_RGBA32, out_pixels, out_width, out_height, out_bytes_per_pixel);\n}\n\nbool ImFontAtlas::Build()\n{\n    ImFontAtlasBuildMain(this);\n    return true;\n}\n#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n\nImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in)\n{\n    // Sanity Checks\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas!\");\n    IM_ASSERT((font_cfg_in->FontData != NULL && font_cfg_in->FontDataSize > 0) || (font_cfg_in->FontLoader != NULL));\n    //IM_ASSERT(font_cfg_in->SizePixels > 0.0f && \"Is ImFontConfig struct correctly initialized?\");\n    IM_ASSERT(font_cfg_in->RasterizerDensity > 0.0f && \"Is ImFontConfig struct correctly initialized?\");\n    if (font_cfg_in->GlyphOffset.x != 0.0f || font_cfg_in->GlyphOffset.y != 0.0f || font_cfg_in->GlyphMinAdvanceX != 0.0f || font_cfg_in->GlyphMaxAdvanceX != FLT_MAX)\n        IM_ASSERT(font_cfg_in->SizePixels != 0.0f && \"Specifying glyph offset/advances requires a reference size to base it on.\");\n\n    // Lazily create builder on the first call to AddFont\n    if (Builder == NULL)\n        ImFontAtlasBuildInit(this);\n\n    // Create new font\n    const bool is_first_font = (Fonts.Size == 0);\n    ImFont* font;\n    if (!font_cfg_in->MergeMode)\n    {\n        font = IM_NEW(ImFont)();\n        font->FontId = FontNextUniqueID++;\n        font->Flags = font_cfg_in->Flags;\n        font->LegacySize = font_cfg_in->SizePixels;\n        font->CurrentRasterizerDensity = font_cfg_in->RasterizerDensity;\n        Fonts.push_back(font);\n    }\n    else\n    {\n        IM_ASSERT(Fonts.Size > 0 && \"Cannot use MergeMode for the first font\"); // When using MergeMode make sure that a font has already been added before.\n        font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back();\n        ImFontAtlasFontDiscardBakes(this, font, 0); // Need to discard bakes if the font was already used, because baked->FontLoaderDatas[] will change size. (#9162)\n    }\n\n    // Add to list\n    Sources.push_back(*font_cfg_in);\n    ImFontConfig* font_cfg = &Sources.back();\n    if (font_cfg->DstFont == NULL)\n        font_cfg->DstFont = font;\n    font->Sources.push_back(font_cfg);\n    ImFontAtlasBuildUpdatePointers(this); // Pointers to Sources are otherwise dangling after we called Sources.push_back().\n\n    // Sanity check\n    // We don't round cfg.SizePixels yet as relative size of merged fonts are used afterwards.\n    if (font_cfg->GlyphExcludeRanges != NULL)\n    {\n        int size = 0;\n        for (const ImWchar* p = font_cfg->GlyphExcludeRanges; p[0] != 0; p++, size++) {}\n        IM_ASSERT((size & 1) == 0 && \"GlyphExcludeRanges[] size must be multiple of two!\");\n        IM_ASSERT((size <= 64) && \"GlyphExcludeRanges[] size must be small!\");\n        font_cfg->GlyphExcludeRanges = (ImWchar*)ImMemdup(font_cfg->GlyphExcludeRanges, sizeof(font_cfg->GlyphExcludeRanges[0]) * (size + 1));\n    }\n    if (font_cfg->FontLoader != NULL)\n    {\n        IM_ASSERT(font_cfg->FontLoader->FontBakedLoadGlyph != NULL);\n        IM_ASSERT(font_cfg->FontLoader->LoaderInit == NULL && font_cfg->FontLoader->LoaderShutdown == NULL); // FIXME-NEWATLAS: Unsupported yet.\n    }\n    IM_ASSERT(font_cfg->FontLoaderData == NULL);\n\n    if (!ImFontAtlasFontSourceInit(this, font_cfg))\n    {\n        // Rollback (this is a fragile/rarely exercised code-path. TestSuite's \"misc_atlas_add_invalid_font\" aim to test this)\n        ImFontAtlasFontDestroySourceData(this, font_cfg);\n        Sources.pop_back();\n        font->Sources.pop_back();\n        if (!font_cfg->MergeMode)\n        {\n            IM_DELETE(font);\n            Fonts.pop_back();\n        }\n        return NULL;\n    }\n    ImFontAtlasFontSourceAddToFont(this, font, font_cfg);\n\n    if (is_first_font)\n        ImFontAtlasBuildNotifySetFont(this, NULL, font);\n    return font;\n}\n\n// Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder)\nstatic unsigned int stb_decompress_length(const unsigned char* input);\nstatic unsigned int stb_decompress(unsigned char* output, const unsigned char* input, unsigned int length);\nstatic unsigned int Decode85Byte(char c)                                    { return c >= '\\\\' ? c-36 : c-35; }\nstatic void         Decode85(const unsigned char* src, unsigned char* dst)\n{\n    while (*src)\n    {\n        unsigned int tmp = Decode85Byte(src[0]) + 85 * (Decode85Byte(src[1]) + 85 * (Decode85Byte(src[2]) + 85 * (Decode85Byte(src[3]) + 85 * Decode85Byte(src[4]))));\n        dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF);   // We can't assume little-endianness.\n        src += 5;\n        dst += 4;\n    }\n}\n#ifndef IMGUI_DISABLE_DEFAULT_FONT\nstatic const char* GetDefaultCompressedFontDataProggyClean(int* out_size);\nstatic const char* GetDefaultCompressedFontDataProggyForever(int* out_size);\n#endif\n\n// This duplicates some of the logic in UpdateFontsNewFrame() which is a bit chicken-and-eggy/tricky to extract due to variety of codepaths and possible initialization ordering.\nstatic float GetExpectedContextFontSize(ImGuiContext* ctx)\n{\n    return ((ctx->Style.FontSizeBase > 0.0f) ? ctx->Style.FontSizeBase : 13.0f) * ctx->Style.FontScaleMain * ctx->Style.FontScaleDpi;\n}\n\n// Legacy function with heuristic to select Pixel or Vector font.\n// The selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold at the time of adding the default font.\n// Prefer calling AddFontDefaultVector() or AddFontDefaultBitmap() based on your own logic.\nImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg)\n{\n    if (OwnerContext == NULL || GetExpectedContextFontSize(OwnerContext) >= 15.0f)\n        return AddFontDefaultVector(font_cfg);\n    else\n        return AddFontDefaultBitmap(font_cfg);\n}\n\n// Load embedded ProggyClean.ttf. Default size 13, disable oversampling.\n// If you want a similar font which may be better scaled, consider using AddFontDefaultVector().\nImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template)\n{\n#ifndef IMGUI_DISABLE_DEFAULT_FONT\n    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();\n    if (!font_cfg_template)\n        font_cfg.PixelSnapH = true; // Prevents sub-integer scaling factors at lower-level layers.\n    if (font_cfg.SizePixels <= 0.0f)\n        font_cfg.SizePixels = 13.0f; // This only serves (1) as a reference for GlyphOffset.y setting and (2) as a default for pre-1.92 backend.\n    if (font_cfg.Name[0] == '\\0')\n        ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), \"ProggyClean.ttf\");\n    font_cfg.EllipsisChar = (ImWchar)0x0085;\n    font_cfg.GlyphOffset.y += 1.0f * (font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units\n\n    int ttf_compressed_size = 0;\n    const char* ttf_compressed = GetDefaultCompressedFontDataProggyClean(&ttf_compressed_size);\n    return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg);\n#else\n    IM_ASSERT(0 && \"Function is disabled in this build.\");\n    IM_UNUSED(font_cfg_template);\n    return NULL;\n#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT\n}\n\n// Load a minimal version of ProggyForever, designed to match our good old ProggyClean, but nicely scalable.\n// (See build script in https://github.com/ocornut/proggyforever for details)\nImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template)\n{\n#ifndef IMGUI_DISABLE_DEFAULT_FONT\n    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();\n    if (!font_cfg_template)\n        font_cfg.PixelSnapH = true; // Precisely match ProggyClean, but prevents sub-integer scaling factors at lower-level layers.\n    if (font_cfg.SizePixels <= 0.0f)\n        font_cfg.SizePixels = 13.0f;\n    if (font_cfg.Name[0] == '\\0')\n        ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), \"ProggyForever.ttf\");\n    font_cfg.ExtraSizeScale *= 1.015f; // Match ProggyClean\n    font_cfg.GlyphOffset.y += 0.5f * (font_cfg.SizePixels / 16.0f); // Closer match ProggyClean + avoid descenders going too high (with current code).\n\n    int ttf_compressed_size = 0;\n    const char* ttf_compressed = GetDefaultCompressedFontDataProggyForever(&ttf_compressed_size);\n    return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg);\n#else\n    IM_ASSERT(0 && \"Function is disabled in this build.\");\n    IM_UNUSED(font_cfg_template);\n    return NULL;\n#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT\n}\n\nImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas!\");\n    size_t data_size = 0;\n    void* data = ImFileLoadToMemory(filename, \"rb\", &data_size, 0);\n    if (!data)\n    {\n        if (font_cfg_template == NULL || (font_cfg_template->Flags & ImFontFlags_NoLoadError) == 0)\n        {\n            IMGUI_DEBUG_LOG(\"While loading '%s'\\n\", filename);\n            IM_ASSERT_USER_ERROR(0, \"Could not load font file!\");\n        }\n        return NULL;\n    }\n    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();\n    if (font_cfg.Name[0] == '\\0')\n    {\n        // Store a short copy of filename into the font name for convenience\n        const char* p;\n        for (p = filename + ImStrlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\\\'; p--) {}\n        ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), \"%s\", p);\n    }\n    return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges);\n}\n\n// NB: Transfer ownership of 'font_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build().\nImFont* ImFontAtlas::AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas!\");\n    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();\n    IM_ASSERT(font_cfg.FontData == NULL);\n    IM_ASSERT(font_data_size > 100 && \"Incorrect value for font_data_size!\"); // Heuristic to prevent accidentally passing a wrong value to font_data_size.\n    font_cfg.FontData = font_data;\n    font_cfg.FontDataSize = font_data_size;\n    font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels;\n    if (glyph_ranges)\n        font_cfg.GlyphRanges = glyph_ranges;\n    return AddFont(&font_cfg);\n}\n\nImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)\n{\n    const unsigned int buf_decompressed_size = stb_decompress_length((const unsigned char*)compressed_ttf_data);\n    unsigned char* buf_decompressed_data = (unsigned char*)IM_ALLOC(buf_decompressed_size);\n    stb_decompress(buf_decompressed_data, (const unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size);\n\n    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();\n    IM_ASSERT(font_cfg.FontData == NULL);\n    font_cfg.FontDataOwnedByAtlas = true;\n    return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, &font_cfg, glyph_ranges);\n}\n\nImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges)\n{\n    int compressed_ttf_size = (((int)ImStrlen(compressed_ttf_data_base85) + 4) / 5) * 4;\n    void* compressed_ttf = IM_ALLOC((size_t)compressed_ttf_size);\n    Decode85((const unsigned char*)compressed_ttf_data_base85, (unsigned char*)compressed_ttf);\n    ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges);\n    IM_FREE(compressed_ttf);\n    return font;\n}\n\n// On font removal we need to remove references (otherwise we could queue removal?)\n// We allow old_font == new_font which forces updating all values (e.g. sizes)\nvoid ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font)\n{\n    for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)\n    {\n        if (shared_data->Font == old_font)\n            shared_data->Font = new_font;\n        if (ImGuiContext* ctx = shared_data->Context)\n        {\n            if (ctx->FrameCount == 0 && old_font == NULL) // While this should work either way, we save ourselves the bother / debugging confusion of running ImGui code so early when it is not needed. \n                continue;\n\n            if (ctx->IO.FontDefault == old_font)\n                ctx->IO.FontDefault = new_font;\n            if (ctx->Font == old_font)\n            {\n                ImGuiContext* curr_ctx = ImGui::GetCurrentContext();\n                bool need_bind_ctx = ctx != curr_ctx;\n                if (need_bind_ctx)\n                    ImGui::SetCurrentContext(ctx);\n                ImGui::SetCurrentFont(new_font, ctx->FontSizeBase, ctx->FontSize);\n                if (need_bind_ctx)\n                    ImGui::SetCurrentContext(curr_ctx);\n            }\n            for (ImFontStackData& font_stack_data : ctx->FontStack)\n                if (font_stack_data.Font == old_font)\n                    font_stack_data.Font = new_font;\n        }\n    }\n}\n\nvoid ImFontAtlas::RemoveFont(ImFont* font)\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas!\");\n\n    ImFontAtlasFontDestroyOutput(this, font);\n    for (ImFontConfig* src : font->Sources)\n        ImFontAtlasFontDestroySourceData(this, src);\n    for (int src_n = 0; src_n < Sources.Size; src_n++)\n        if (Sources[src_n].DstFont == font)\n            Sources.erase(&Sources[src_n--]);\n\n    bool removed = Fonts.find_erase(font);\n    IM_ASSERT(removed);\n    IM_UNUSED(removed);\n\n    ImFontAtlasBuildUpdatePointers(this);\n\n    font->OwnerAtlas = NULL;\n    IM_DELETE(font);\n\n    // Notify external systems\n    ImFont* new_current_font = Fonts.empty() ? NULL : Fonts[0];\n    ImFontAtlasBuildNotifySetFont(this, font, new_current_font);\n}\n\n// At it is common to do an AddCustomRect() followed by a GetCustomRect(), we provide an optional 'ImFontAtlasRect* out_r = NULL' argument to retrieve the info straight away.\nImFontAtlasRectId ImFontAtlas::AddCustomRect(int width, int height, ImFontAtlasRect* out_r)\n{\n    IM_ASSERT(width > 0 && width <= 0xFFFF);\n    IM_ASSERT(height > 0 && height <= 0xFFFF);\n\n    if (Builder == NULL)\n        ImFontAtlasBuildInit(this);\n\n    ImFontAtlasRectId r_id = ImFontAtlasPackAddRect(this, width, height);\n    if (r_id == ImFontAtlasRectId_Invalid)\n        return ImFontAtlasRectId_Invalid;\n    if (out_r != NULL)\n        GetCustomRect(r_id, out_r);\n\n    if (RendererHasTextures)\n    {\n        ImTextureRect* r = ImFontAtlasPackGetRect(this, r_id);\n        ImFontAtlasTextureBlockQueueUpload(this, TexData, r->x, r->y, r->w, r->h);\n    }\n    return r_id;\n}\n\nvoid ImFontAtlas::RemoveCustomRect(ImFontAtlasRectId id)\n{\n    if (ImFontAtlasPackGetRectSafe(this, id) == NULL)\n        return;\n    ImFontAtlasPackDiscardRect(this, id);\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n// This API does not make sense anymore with scalable fonts.\n// - Prefer adding a font source (ImFontConfig) using a custom/procedural loader.\n// - You may use ImFontFlags_LockBakedSizes to limit an existing font to known baked sizes:\n//     ImFont* myfont = io.Fonts->AddFontFromFileTTF(....);\n//     myfont->GetFontBaked(16.0f);\n//     myfont->Flags |= ImFontFlags_LockBakedSizes;\nImFontAtlasRectId ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int width, int height, float advance_x, const ImVec2& offset)\n{\n    float font_size = font->LegacySize;\n    return AddCustomRectFontGlyphForSize(font, font_size, codepoint, width, height, advance_x, offset);\n}\n// FIXME: we automatically set glyph.Colored=true by default.\n// If you need to alter this, you can write 'font->Glyphs.back()->Colored' after calling AddCustomRectFontGlyph().\nImFontAtlasRectId ImFontAtlas::AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int width, int height, float advance_x, const ImVec2& offset)\n{\n#ifdef IMGUI_USE_WCHAR32\n    IM_ASSERT(codepoint <= IM_UNICODE_CODEPOINT_MAX);\n#endif\n    IM_ASSERT(font != NULL);\n    IM_ASSERT(width > 0 && width <= 0xFFFF);\n    IM_ASSERT(height > 0 && height <= 0xFFFF);\n\n    ImFontBaked* baked = font->GetFontBaked(font_size);\n\n    ImFontAtlasRectId r_id = ImFontAtlasPackAddRect(this, width, height);\n    if (r_id == ImFontAtlasRectId_Invalid)\n        return ImFontAtlasRectId_Invalid;\n    ImTextureRect* r = ImFontAtlasPackGetRect(this, r_id);\n    if (RendererHasTextures)\n        ImFontAtlasTextureBlockQueueUpload(this, TexData, r->x, r->y, r->w, r->h);\n\n    if (baked->IsGlyphLoaded(codepoint))\n        ImFontAtlasBakedDiscardFontGlyph(this, font, baked, baked->FindGlyph(codepoint));\n\n    ImFontGlyph glyph;\n    glyph.Codepoint = codepoint;\n    glyph.AdvanceX = advance_x;\n    glyph.X0 = offset.x;\n    glyph.Y0 = offset.y;\n    glyph.X1 = offset.x + r->w;\n    glyph.Y1 = offset.y + r->h;\n    glyph.Visible = true;\n    glyph.Colored = true; // FIXME: Arbitrary\n    glyph.PackId = r_id;\n    ImFontAtlasBakedAddFontGlyph(this, baked, font->Sources[0], &glyph);\n    return r_id;\n}\n#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n\nbool ImFontAtlas::GetCustomRect(ImFontAtlasRectId id, ImFontAtlasRect* out_r) const\n{\n    ImTextureRect* r = ImFontAtlasPackGetRectSafe((ImFontAtlas*)this, id);\n    if (r == NULL)\n        return false;\n    IM_ASSERT(TexData->Width > 0 && TexData->Height > 0);   // Font atlas needs to be built before we can calculate UV coordinates\n    if (out_r == NULL)\n        return true;\n    out_r->x = r->x;\n    out_r->y = r->y;\n    out_r->w = r->w;\n    out_r->h = r->h;\n    out_r->uv0 = ImVec2((float)(r->x), (float)(r->y)) * TexUvScale;\n    out_r->uv1 = ImVec2((float)(r->x + r->w), (float)(r->y + r->h)) * TexUvScale;\n    return true;\n}\n\nbool ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2])\n{\n    if (cursor_type <= ImGuiMouseCursor_None || cursor_type >= ImGuiMouseCursor_COUNT)\n        return false;\n    if (atlas->Flags & ImFontAtlasFlags_NoMouseCursors)\n        return false;\n\n    ImTextureRect* r = ImFontAtlasPackGetRect(atlas, atlas->Builder->PackIdMouseCursors);\n    ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r->x, (float)r->y);\n    ImVec2 size = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][1];\n    *out_size = size;\n    *out_offset = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][2];\n    out_uv_border[0] = (pos) * atlas->TexUvScale;\n    out_uv_border[1] = (pos + size) * atlas->TexUvScale;\n    pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W + 1;\n    out_uv_fill[0] = (pos) * atlas->TexUvScale;\n    out_uv_fill[1] = (pos + size) * atlas->TexUvScale;\n    return true;\n}\n\n// When atlas->RendererHasTextures = true, this is only called if no font were loaded.\nvoid ImFontAtlasBuildMain(ImFontAtlas* atlas)\n{\n    IM_ASSERT(!atlas->Locked && \"Cannot modify a locked ImFontAtlas!\");\n    if (atlas->TexData && atlas->TexData->Format != atlas->TexDesiredFormat)\n        ImFontAtlasBuildClear(atlas);\n\n    if (atlas->Builder == NULL)\n        ImFontAtlasBuildInit(atlas);\n\n    // Default font is none are specified\n    if (atlas->Sources.Size == 0)\n        atlas->AddFontDefault();\n\n    // [LEGACY] For backends not supporting RendererHasTextures: preload all glyphs\n    ImFontAtlasBuildUpdateRendererHasTexturesFromContext(atlas);\n    if (atlas->RendererHasTextures == false) // ~ImGuiBackendFlags_RendererHasTextures\n        ImFontAtlasBuildLegacyPreloadAllGlyphRanges(atlas);\n    atlas->TexIsBuilt = true;\n}\n\nvoid ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v)\n{\n    // (Only used by stb_truetype builder)\n    // Automatically disable horizontal oversampling over size 36\n    const float raster_size = baked->Size * baked->RasterizerDensity * src->RasterizerDensity;\n    *out_oversample_h = (src->OversampleH != 0) ? src->OversampleH : (raster_size > 36.0f || src->PixelSnapH) ? 1 : 2;\n    *out_oversample_v = (src->OversampleV != 0) ? src->OversampleV : 1;\n}\n\n// Setup main font loader for the atlas\n// Every font source (ImFontConfig) will use this unless ImFontConfig::FontLoader specify a custom loader.\nvoid ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* font_loader)\n{\n    if (atlas->FontLoader == font_loader)\n        return;\n    IM_ASSERT(!atlas->Locked && \"Cannot modify a locked ImFontAtlas!\");\n\n    for (ImFont* font : atlas->Fonts)\n        ImFontAtlasFontDestroyOutput(atlas, font);\n    if (atlas->Builder && atlas->FontLoader && atlas->FontLoader->LoaderShutdown)\n        atlas->FontLoader->LoaderShutdown(atlas);\n\n    atlas->FontLoader = font_loader;\n    atlas->FontLoaderName = font_loader ? font_loader->Name : \"NULL\";\n    IM_ASSERT(atlas->FontLoaderData == NULL);\n\n    if (atlas->Builder && atlas->FontLoader && atlas->FontLoader->LoaderInit)\n        atlas->FontLoader->LoaderInit(atlas);\n    for (ImFont* font : atlas->Fonts)\n        ImFontAtlasFontInitOutput(atlas, font);\n    for (ImFont* font : atlas->Fonts)\n        for (ImFontConfig* src : font->Sources)\n            ImFontAtlasFontSourceAddToFont(atlas, font, src);\n}\n\n// Preload all glyph ranges for legacy backends.\n// This may lead to multiple texture creation which might be a little slower than before.\nvoid ImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas)\n{\n    atlas->Builder->PreloadedAllGlyphsRanges = true;\n    for (ImFont* font : atlas->Fonts)\n    {\n        ImFontBaked* baked = font->GetFontBaked(font->LegacySize);\n        if (font->FallbackChar != 0)\n            baked->FindGlyph(font->FallbackChar);\n        if (font->EllipsisChar != 0)\n            baked->FindGlyph(font->EllipsisChar);\n        for (ImFontConfig* src : font->Sources)\n        {\n            const ImWchar* ranges = src->GlyphRanges ? src->GlyphRanges : atlas->GetGlyphRangesDefault();\n            for (; ranges[0]; ranges += 2)\n                for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560\n                    baked->FindGlyph((ImWchar)c);\n        }\n    }\n}\n\n// FIXME: May make ImFont::Sources a ImSpan<> and move ownership to ImFontAtlas\nvoid ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas)\n{\n    for (ImFont* font : atlas->Fonts)\n        font->Sources.resize(0);\n    for (ImFontConfig& src : atlas->Sources)\n        src.DstFont->Sources.push_back(&src);\n}\n\n// Render a white-colored bitmap encoded in a string\nvoid ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char)\n{\n    ImTextureData* tex = atlas->TexData;\n    IM_ASSERT(x >= 0 && x + w <= tex->Width);\n    IM_ASSERT(y >= 0 && y + h <= tex->Height);\n\n    switch (tex->Format)\n    {\n    case ImTextureFormat_Alpha8:\n    {\n        ImU8* out_p = (ImU8*)tex->GetPixelsAt(x, y);\n        for (int off_y = 0; off_y < h; off_y++, out_p += tex->Width, in_str += w)\n            for (int off_x = 0; off_x < w; off_x++)\n                out_p[off_x] = (in_str[off_x] == in_marker_char) ? 0xFF : 0x00;\n        break;\n    }\n    case ImTextureFormat_RGBA32:\n    {\n        ImU32* out_p = (ImU32*)tex->GetPixelsAt(x, y);\n        for (int off_y = 0; off_y < h; off_y++, out_p += tex->Width, in_str += w)\n            for (int off_x = 0; off_x < w; off_x++)\n                out_p[off_x] = (in_str[off_x] == in_marker_char) ? IM_COL32_WHITE : IM_COL32_BLACK_TRANS;\n        break;\n    }\n    }\n}\n\nstatic void ImFontAtlasBuildUpdateBasicTexData(ImFontAtlas* atlas)\n{\n    // Pack and store identifier so we can refresh UV coordinates on texture resize.\n    // FIXME-NEWATLAS: User/custom rects where user code wants to store UV coordinates will need to do the same thing.\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    ImVec2i pack_size = (atlas->Flags & ImFontAtlasFlags_NoMouseCursors) ? ImVec2i(2, 2) : ImVec2i(FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H);\n\n    ImFontAtlasRect r;\n    bool add_and_draw = (atlas->GetCustomRect(builder->PackIdMouseCursors, &r) == false);\n    if (add_and_draw)\n    {\n        builder->PackIdMouseCursors = atlas->AddCustomRect(pack_size.x, pack_size.y, &r);\n        IM_ASSERT(builder->PackIdMouseCursors != ImFontAtlasRectId_Invalid);\n\n        // Draw to texture\n        if (atlas->Flags & ImFontAtlasFlags_NoMouseCursors)\n        {\n            // 2x2 white pixels\n            ImFontAtlasBuildRenderBitmapFromString(atlas, r.x, r.y, 2, 2, \"XX\" \"XX\", 'X');\n        }\n        else\n        {\n            // 2x2 white pixels + mouse cursors\n            const int x_for_white = r.x;\n            const int x_for_black = r.x + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1;\n            ImFontAtlasBuildRenderBitmapFromString(atlas, x_for_white, r.y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.');\n            ImFontAtlasBuildRenderBitmapFromString(atlas, x_for_black, r.y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X');\n        }\n    }\n\n    // Refresh UV coordinates\n    atlas->TexUvWhitePixel = ImVec2((r.x + 0.5f) * atlas->TexUvScale.x, (r.y + 0.5f) * atlas->TexUvScale.y);\n}\n\nstatic void ImFontAtlasBuildUpdateLinesTexData(ImFontAtlas* atlas)\n{\n    if (atlas->Flags & ImFontAtlasFlags_NoBakedLines)\n        return;\n\n    // Pack and store identifier so we can refresh UV coordinates on texture resize.\n    ImTextureData* tex = atlas->TexData;\n    ImFontAtlasBuilder* builder = atlas->Builder;\n\n    ImFontAtlasRect r;\n    bool add_and_draw = atlas->GetCustomRect(builder->PackIdLinesTexData, &r) == false;\n    if (add_and_draw)\n    {\n        ImVec2i pack_size = ImVec2i(IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1);\n        builder->PackIdLinesTexData = atlas->AddCustomRect(pack_size.x, pack_size.y, &r);\n        IM_ASSERT(builder->PackIdLinesTexData != ImFontAtlasRectId_Invalid);\n    }\n\n    // Register texture region for thick lines\n    // The +2 here is to give space for the end caps, whilst height +1 is to accommodate the fact we have a zero-width row\n    // This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them\n    for (int n = 0; n < IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row\n    {\n        // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle\n        const int y = n;\n        const int line_width = n;\n        const int pad_left = (r.w - line_width) / 2;\n        const int pad_right = r.w - (pad_left + line_width);\n        IM_ASSERT(pad_left + line_width + pad_right == r.w && y < r.h); // Make sure we're inside the texture bounds before we start writing pixels\n\n        // Write each slice\n        if (add_and_draw && tex->Format == ImTextureFormat_Alpha8)\n        {\n            ImU8* write_ptr = (ImU8*)tex->GetPixelsAt(r.x, r.y + y);\n            for (int i = 0; i < pad_left; i++)\n                *(write_ptr + i) = 0x00;\n\n            for (int i = 0; i < line_width; i++)\n                *(write_ptr + pad_left + i) = 0xFF;\n\n            for (int i = 0; i < pad_right; i++)\n                *(write_ptr + pad_left + line_width + i) = 0x00;\n        }\n        else if (add_and_draw && tex->Format == ImTextureFormat_RGBA32)\n        {\n            ImU32* write_ptr = (ImU32*)(void*)tex->GetPixelsAt(r.x, r.y + y);\n            for (int i = 0; i < pad_left; i++)\n                *(write_ptr + i) = IM_COL32(255, 255, 255, 0);\n\n            for (int i = 0; i < line_width; i++)\n                *(write_ptr + pad_left + i) = IM_COL32_WHITE;\n\n            for (int i = 0; i < pad_right; i++)\n                *(write_ptr + pad_left + line_width + i) = IM_COL32(255, 255, 255, 0);\n        }\n\n        // Refresh UV coordinates\n        ImVec2 uv0 = ImVec2((float)(r.x + pad_left - 1), (float)(r.y + y)) * atlas->TexUvScale;\n        ImVec2 uv1 = ImVec2((float)(r.x + pad_left + line_width + 1), (float)(r.y + y + 1)) * atlas->TexUvScale;\n        float half_v = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts\n        atlas->TexUvLines[n] = ImVec4(uv0.x, half_v, uv1.x, half_v);\n    }\n}\n\nstatic void ImFontAtlasBuildUpdateShadowTexData(ImFontAtlas* atlas); // FIXME-SHADOWS: Move impl here.\n\n//-----------------------------------------------------------------------------------------------------------------------------\n\n// Was tempted to lazily init FontSrc but wouldn't save much + makes it more complicated to detect invalid data at AddFont()\nbool ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font)\n{\n    bool ret = true;\n    for (ImFontConfig* src : font->Sources)\n        if (!ImFontAtlasFontSourceInit(atlas, src))\n            ret = false;\n    IM_ASSERT(ret); // Unclear how to react to this meaningfully. Assume that result will be same as initial AddFont() call.\n    return ret;\n}\n\n// Keep source/input FontData\nvoid ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font)\n{\n    font->ClearOutputData();\n    for (ImFontConfig* src : font->Sources)\n    {\n        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;\n        if (loader && loader->FontSrcDestroy != NULL)\n            loader->FontSrcDestroy(atlas, src);\n    }\n}\n\nvoid ImFontAtlasFontRebuildOutput(ImFontAtlas* atlas, ImFont* font)\n{\n    ImFontAtlasFontDestroyOutput(atlas, font);\n    ImFontAtlasFontInitOutput(atlas, font);\n}\n\n//-----------------------------------------------------------------------------------------------------------------------------\n\nbool ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src)\n{\n    const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;\n    if (loader->FontSrcInit != NULL && !loader->FontSrcInit(atlas, src))\n        return false;\n    return true;\n}\n\nvoid ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src)\n{\n    if (src->MergeMode == false)\n    {\n        font->ClearOutputData();\n        //font->FontSize = src->SizePixels;\n        font->OwnerAtlas = atlas;\n        IM_ASSERT(font->Sources[0] == src);\n    }\n    atlas->TexIsBuilt = false; // For legacy backends\n    ImFontAtlasBuildSetupFontSpecialGlyphs(atlas, font, src);\n}\n\nvoid ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src)\n{\n    IM_UNUSED(atlas);\n    // IF YOU GET A CRASH IN THE IM_FREE() CALL HERE AND USED AddFontFromMemoryTTF():\n    // - DUE TO LEGACY REASON AddFontFromMemoryTTF() TRANSFERS MEMORY OWNERSHIP BY DEFAULT.\n    // - IT WILL THEREFORE CRASH WHEN PASSED DATA WHICH MAY NOT BE FREED BY IMGUI.\n    // - USE `ImFontConfig font_cfg; font_cfg.FontDataOwnedByAtlas = false; io.Fonts->AddFontFromMemoryTTF(....., &cfg);` to disable passing ownership/\n    // WE WILL ADDRESS THIS IN A FUTURE REWORK OF THE API.\n    if (src->FontDataOwnedByAtlas)\n        IM_FREE(src->FontData);\n    src->FontData = NULL;\n    if (src->GlyphExcludeRanges)\n        IM_FREE((void*)src->GlyphExcludeRanges);\n    src->GlyphExcludeRanges = NULL;\n}\n\n// Create a compact, baked \"...\" if it doesn't exist, by using the \".\".\n// This may seem overly complicated right now but the point is to exercise and improve a technique which should be increasingly used.\n// FIXME-NEWATLAS: This borrows too much from FontLoader's FontLoadGlyph() handlers and suggest that we should add further helpers.\nstatic ImFontGlyph* ImFontAtlasBuildSetupFontBakedEllipsis(ImFontAtlas* atlas, ImFontBaked* baked)\n{\n    ImFont* font = baked->OwnerFont;\n    IM_ASSERT(font->EllipsisChar != 0);\n\n    const ImFontGlyph* dot_glyph = baked->FindGlyphNoFallback((ImWchar)'.');\n    if (dot_glyph == NULL)\n        dot_glyph = baked->FindGlyphNoFallback((ImWchar)0xFF0E);\n    if (dot_glyph == NULL)\n        return NULL;\n    ImFontAtlasRectId dot_r_id = dot_glyph->PackId; // Deep copy to avoid invalidation of glyphs and rect pointers\n    ImTextureRect* dot_r = ImFontAtlasPackGetRect(atlas, dot_r_id);\n    const int dot_spacing = 1;\n    const float dot_step = (dot_glyph->X1 - dot_glyph->X0) + dot_spacing;\n\n    ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, (dot_r->w * 3 + dot_spacing * 2), dot_r->h);\n    ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id);\n\n    ImFontGlyph glyph_in = {};\n    ImFontGlyph* glyph = &glyph_in;\n    glyph->Codepoint = font->EllipsisChar;\n    glyph->AdvanceX = ImMax(dot_glyph->AdvanceX, dot_glyph->X0 + dot_step * 3.0f - dot_spacing); // FIXME: Slightly odd for normally mono-space fonts but since this is used for trailing contents.\n    glyph->X0 = dot_glyph->X0;\n    glyph->Y0 = dot_glyph->Y0;\n    glyph->X1 = dot_glyph->X0 + dot_step * 3 - dot_spacing;\n    glyph->Y1 = dot_glyph->Y1;\n    glyph->Visible = true;\n    glyph->PackId = pack_id;\n    glyph = ImFontAtlasBakedAddFontGlyph(atlas, baked, NULL, glyph);\n    dot_glyph = NULL; // Invalidated\n\n    // Copy to texture, post-process and queue update for backend\n    // FIXME-NEWATLAS-V2: Dot glyph is already post-processed as this point, so this would damage it.\n    dot_r = ImFontAtlasPackGetRect(atlas, dot_r_id);\n    ImTextureData* tex = atlas->TexData;\n    for (int n = 0; n < 3; n++)\n        ImFontAtlasTextureBlockCopy(tex, dot_r->x, dot_r->y, tex, r->x + (dot_r->w + dot_spacing) * n, r->y, dot_r->w, dot_r->h);\n    ImFontAtlasTextureBlockQueueUpload(atlas, tex, r->x, r->y, r->w, r->h);\n\n    return glyph;\n}\n\n// Load fallback in order to obtain its index\n// (this is called from in hot-path so we avoid extraneous parameters to minimize impact on code size)\nstatic void ImFontAtlasBuildSetupFontBakedFallback(ImFontBaked* baked)\n{\n    IM_ASSERT(baked->FallbackGlyphIndex == -1);\n    IM_ASSERT(baked->FallbackAdvanceX == 0.0f);\n    ImFont* font = baked->OwnerFont;\n    ImFontGlyph* fallback_glyph = NULL;\n    if (font->FallbackChar != 0)\n        fallback_glyph = baked->FindGlyphNoFallback(font->FallbackChar);\n    if (fallback_glyph == NULL)\n    {\n        ImFontGlyph* space_glyph = baked->FindGlyphNoFallback((ImWchar)' ');\n        ImFontGlyph glyph;\n        glyph.Codepoint = 0;\n        glyph.AdvanceX = space_glyph ? space_glyph->AdvanceX : IM_ROUND(baked->Size * 0.40f);\n        fallback_glyph = ImFontAtlasBakedAddFontGlyph(font->OwnerAtlas, baked, NULL, &glyph);\n    }\n    baked->FallbackGlyphIndex = baked->Glyphs.index_from_ptr(fallback_glyph); // Storing index avoid need to update pointer on growth and simplify inner loop code\n    baked->FallbackAdvanceX = fallback_glyph->AdvanceX;\n}\n\nstatic void ImFontAtlasBuildSetupFontBakedBlanks(ImFontAtlas* atlas, ImFontBaked* baked)\n{\n    // Mark space as always hidden (not strictly correct/necessary. but some e.g. icons fonts don't have a space. it tends to look neater in previews)\n    ImFontGlyph* space_glyph = baked->FindGlyphNoFallback((ImWchar)' ');\n    if (space_glyph != NULL)\n        space_glyph->Visible = false;\n\n    // Setup Tab character.\n    // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at \"column 0\" ?)\n    if (baked->FindGlyphNoFallback('\\t') == NULL && space_glyph != NULL)\n    {\n        ImFontGlyph tab_glyph;\n        tab_glyph.Codepoint = '\\t';\n        tab_glyph.AdvanceX = space_glyph->AdvanceX * IM_TABSIZE;\n        ImFontAtlasBakedAddFontGlyph(atlas, baked, NULL, &tab_glyph);\n    }\n}\n\n// Load/identify special glyphs\n// (note that this is called again for fonts with MergeMode)\nvoid ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src)\n{\n    IM_UNUSED(atlas);\n    IM_ASSERT(font->Sources.contains(src));\n\n    // Find Fallback character. Actual glyph loaded in GetFontBaked().\n    const ImWchar fallback_chars[] = { font->FallbackChar, (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' };\n    if (font->FallbackChar == 0)\n        for (ImWchar candidate_char : fallback_chars)\n            if (candidate_char != 0 && font->IsGlyphInFont(candidate_char))\n            {\n                font->FallbackChar = (ImWchar)candidate_char;\n                break;\n            }\n\n    // Setup Ellipsis character. It is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).\n    // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.\n    // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.\n    const ImWchar ellipsis_chars[] = { src->EllipsisChar, (ImWchar)0x2026, (ImWchar)0x0085 };\n    if (font->EllipsisChar == 0)\n        for (ImWchar candidate_char : ellipsis_chars)\n            if (candidate_char != 0 && font->IsGlyphInFont(candidate_char))\n            {\n                font->EllipsisChar = candidate_char;\n                break;\n            }\n    if (font->EllipsisChar == 0)\n    {\n        font->EllipsisChar = 0x0085;\n        font->EllipsisAutoBake = true;\n    }\n}\n\nvoid ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph)\n{\n    if (glyph->PackId != ImFontAtlasRectId_Invalid)\n    {\n        ImFontAtlasPackDiscardRect(atlas, glyph->PackId);\n        glyph->PackId = ImFontAtlasRectId_Invalid;\n    }\n    ImWchar c = (ImWchar)glyph->Codepoint;\n    IM_ASSERT(font->FallbackChar != c && font->EllipsisChar != c); // Unsupported for simplicity\n    IM_ASSERT(glyph >= baked->Glyphs.Data && glyph < baked->Glyphs.Data + baked->Glyphs.Size);\n    IM_UNUSED(font);\n    baked->IndexLookup[c] = IM_FONTGLYPH_INDEX_UNUSED;\n    baked->IndexAdvanceX[c] = baked->FallbackAdvanceX;\n}\n\nImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id)\n{\n    IMGUI_DEBUG_LOG_FONT(\"[font] Created baked %.2fpx\\n\", font_size);\n    ImFontBaked* baked = atlas->Builder->BakedPool.push_back(ImFontBaked());\n    baked->Size = font_size;\n    baked->RasterizerDensity = font_rasterizer_density;\n    baked->BakedId = baked_id;\n    baked->OwnerFont = font;\n    baked->LastUsedFrame = atlas->Builder->FrameCount;\n\n    // Initialize backend data\n    size_t loader_data_size = 0;\n    for (ImFontConfig* src : font->Sources) // Cannot easily be cached as we allow changing backend\n    {\n        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;\n        loader_data_size += loader->FontBakedSrcLoaderDataSize;\n    }\n    baked->FontLoaderDatas = (loader_data_size > 0) ? IM_ALLOC(loader_data_size) : NULL;\n    char* loader_data_p = (char*)baked->FontLoaderDatas;\n    for (ImFontConfig* src : font->Sources)\n    {\n        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;\n        if (loader->FontBakedInit)\n            loader->FontBakedInit(atlas, src, baked, loader_data_p);\n        loader_data_p += loader->FontBakedSrcLoaderDataSize;\n    }\n\n    ImFontAtlasBuildSetupFontBakedBlanks(atlas, baked);\n    return baked;\n}\n\n// FIXME-OPT: This is not a fast query. Adding a BakedCount field in Font might allow to take a shortcut for the most common case.\nImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density)\n{\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    for (int step_n = 0; step_n < 2; step_n++)\n    {\n        ImFontBaked* closest_larger_match = NULL;\n        ImFontBaked* closest_smaller_match = NULL;\n        for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)\n        {\n            ImFontBaked* baked = &builder->BakedPool[baked_n];\n            if (baked->OwnerFont != font || baked->WantDestroy)\n                continue;\n            if (step_n == 0 && baked->RasterizerDensity != font_rasterizer_density) // First try with same density\n                continue;\n            if (baked->Size > font_size && (closest_larger_match == NULL || baked->Size < closest_larger_match->Size))\n                closest_larger_match = baked;\n            if (baked->Size < font_size && (closest_smaller_match == NULL || baked->Size > closest_smaller_match->Size))\n                closest_smaller_match = baked;\n        }\n        if (closest_larger_match)\n            if (closest_smaller_match == NULL || (closest_larger_match->Size >= font_size * 2.0f && closest_smaller_match->Size > font_size * 0.5f))\n                return closest_larger_match;\n        if (closest_smaller_match)\n            return closest_smaller_match;\n    }\n    return NULL;\n}\n\nvoid ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked)\n{\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    IMGUI_DEBUG_LOG_FONT(\"[font] Discard baked %.2f for \\\"%s\\\"\\n\", baked->Size, font->GetDebugName());\n\n    for (ImFontGlyph& glyph : baked->Glyphs)\n        if (glyph.PackId != ImFontAtlasRectId_Invalid)\n            ImFontAtlasPackDiscardRect(atlas, glyph.PackId);\n\n    char* loader_data_p = (char*)baked->FontLoaderDatas;\n    for (ImFontConfig* src : font->Sources)\n    {\n        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;\n        if (loader->FontBakedDestroy)\n            loader->FontBakedDestroy(atlas, src, baked, loader_data_p);\n        loader_data_p += loader->FontBakedSrcLoaderDataSize;\n    }\n    if (baked->FontLoaderDatas)\n    {\n        IM_FREE(baked->FontLoaderDatas);\n        baked->FontLoaderDatas = NULL;\n    }\n    builder->BakedMap.SetVoidPtr(baked->BakedId, NULL);\n    builder->BakedDiscardedCount++;\n    baked->ClearOutputData();\n    baked->WantDestroy = true;\n    font->LastBaked = NULL;\n}\n\n// use unused_frames==0 to discard everything.\nvoid ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_frames)\n{\n    if (ImFontAtlasBuilder* builder = atlas->Builder) // This can be called from font destructor\n        for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)\n        {\n            ImFontBaked* baked = &builder->BakedPool[baked_n];\n            if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount)\n                continue;\n            if (baked->OwnerFont != font || baked->WantDestroy)\n                continue;\n            ImFontAtlasBakedDiscard(atlas, font, baked);\n        }\n}\n\n// use unused_frames==0 to discard everything.\nvoid ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames)\n{\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)\n    {\n        ImFontBaked* baked = &builder->BakedPool[baked_n];\n        if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount)\n            continue;\n        if (baked->WantDestroy || (baked->OwnerFont->Flags & ImFontFlags_LockBakedSizes))\n            continue;\n        ImFontAtlasBakedDiscard(atlas, baked->OwnerFont, baked);\n    }\n}\n\n// Those functions are designed to facilitate changing the underlying structures for ImFontAtlas to store an array of ImDrawListSharedData*\nvoid ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data)\n{\n    IM_ASSERT(!atlas->DrawListSharedDatas.contains(data));\n    atlas->DrawListSharedDatas.push_back(data);\n}\n\nvoid ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data)\n{\n    IM_ASSERT(atlas->DrawListSharedDatas.contains(data));\n    atlas->DrawListSharedDatas.find_erase(data);\n}\n\n// Update texture identifier in all active draw lists\nvoid ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex)\n{\n    for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)\n    {\n        // If Context 2 uses font owned by Context 1 which already called EndFrame()/Render(), we don't want to mess with draw commands for Context 1\n        if (shared_data->Context && !shared_data->Context->WithinFrameScope)\n            continue;\n\n        for (ImDrawList* draw_list : shared_data->DrawLists)\n        {\n            // Replace in command-buffer\n            // (there is not need to replace in ImDrawListSplitter: current channel is in ImDrawList's CmdBuffer[],\n            //  other channels will be on SetCurrentChannel() which already needs to compare CmdHeader anyhow)\n            if (draw_list->CmdBuffer.Size > 0 && draw_list->_CmdHeader.TexRef == old_tex)\n                draw_list->_SetTexture(new_tex);\n\n            // Replace in stack\n            for (ImTextureRef& stacked_tex : draw_list->_TextureStack)\n                if (stacked_tex == old_tex)\n                    stacked_tex = new_tex;\n        }\n    }\n}\n\n// Update texture coordinates in all draw list shared context\n// FIXME-NEWATLAS FIXME-OPT: Doesn't seem necessary to update for all, only one bound to current context?\nvoid ImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas)\n{\n    for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)\n        if (shared_data->FontAtlas == atlas)\n        {\n            shared_data->TexUvWhitePixel = atlas->TexUvWhitePixel;\n            shared_data->TexUvLines = atlas->TexUvLines;\n        }\n}\n\n// Set current texture. This is mostly called from AddTexture() + to handle a failed resize.\nstatic void ImFontAtlasBuildSetTexture(ImFontAtlas* atlas, ImTextureData* tex)\n{\n    ImTextureRef old_tex_ref = atlas->TexRef;\n    atlas->TexData = tex;\n    atlas->TexUvScale = ImVec2(1.0f / tex->Width, 1.0f / tex->Height);\n    atlas->TexRef._TexData = tex;\n    //atlas->TexRef._TexID = tex->TexID; // <-- We intentionally don't do that. It would be misleading and betray promise that both fields aren't set.\n    ImFontAtlasUpdateDrawListsTextures(atlas, old_tex_ref, atlas->TexRef);\n}\n\n// Create a new texture, discard previous one\nImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h)\n{\n    ImTextureData* old_tex = atlas->TexData;\n    ImTextureData* new_tex;\n\n    // FIXME: Cannot reuse texture because old UV may have been used already (unless we remap UV).\n    /*if (old_tex != NULL && old_tex->Status == ImTextureStatus_WantCreate)\n    {\n        // Reuse texture not yet used by backend.\n        IM_ASSERT(old_tex->TexID == ImTextureID_Invalid && old_tex->BackendUserData == NULL);\n        old_tex->DestroyPixels();\n        old_tex->Updates.clear();\n        new_tex = old_tex;\n        old_tex = NULL;\n    }\n    else*/\n    {\n        // Add new\n        new_tex = IM_NEW(ImTextureData)();\n        new_tex->UniqueID = atlas->TexNextUniqueID++;\n        atlas->TexList.push_back(new_tex);\n    }\n    if (old_tex != NULL)\n    {\n        // Queue old as to destroy next frame\n        old_tex->WantDestroyNextFrame = true;\n        IM_ASSERT(old_tex->Status == ImTextureStatus_OK || old_tex->Status == ImTextureStatus_WantCreate || old_tex->Status == ImTextureStatus_WantUpdates);\n    }\n\n    new_tex->Create(atlas->TexDesiredFormat, w, h);\n    atlas->TexIsBuilt = false;\n\n    ImFontAtlasBuildSetTexture(atlas, new_tex);\n\n    return new_tex;\n}\n\n#if 0\n#define STB_IMAGE_WRITE_IMPLEMENTATION\n#include \"../stb/stb_image_write.h\"\nstatic void ImFontAtlasDebugWriteTexToDisk(ImTextureData* tex, const char* description)\n{\n    ImGuiContext& g = *GImGui;\n    char buf[128];\n    ImFormatString(buf, IM_COUNTOF(buf), \"[%05d] Texture #%03d - %s.png\", g.FrameCount, tex->UniqueID, description);\n    stbi_write_png(buf, tex->Width, tex->Height, tex->BytesPerPixel, tex->Pixels, tex->GetPitch()); // tex->BytesPerPixel is technically not component, but ok for the formats we support.\n}\n#endif\n\nvoid ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h)\n{\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    builder->LockDisableResize = true;\n\n    ImTextureData* old_tex = atlas->TexData;\n    ImTextureData* new_tex = ImFontAtlasTextureAdd(atlas, w, h);\n    new_tex->UseColors = old_tex->UseColors;\n    IMGUI_DEBUG_LOG_FONT(\"[font] Texture #%03d: resize+repack %dx%d => Texture #%03d: %dx%d\\n\", old_tex->UniqueID, old_tex->Width, old_tex->Height, new_tex->UniqueID, new_tex->Width, new_tex->Height);\n    //for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)\n    //    IMGUI_DEBUG_LOG_FONT(\"[font] - Baked %.2fpx, %d glyphs, want_destroy=%d\\n\", builder->BakedPool[baked_n].FontSize, builder->BakedPool[baked_n].Glyphs.Size, builder->BakedPool[baked_n].WantDestroy);\n    //IMGUI_DEBUG_LOG_FONT(\"[font] - Old packed rects: %d, area %d px\\n\", builder->RectsPackedCount, builder->RectsPackedSurface);\n    //ImFontAtlasDebugWriteTexToDisk(old_tex, \"Before Pack\");\n\n    // Repack, lose discarded rectangle, copy pixels\n    // FIXME-NEWATLAS: This is unstable because packing order is based on RectsIndex\n    // FIXME-NEWATLAS-V2: Repacking in batch would be beneficial to packing heuristic, and fix stability.\n    // FIXME-NEWATLAS-TESTS: Test calling RepackTexture with size too small to fits existing rects.\n    ImFontAtlasPackInit(atlas);\n    ImVector<ImTextureRect> old_rects;\n    ImVector<ImFontAtlasRectEntry> old_index = builder->RectsIndex;\n    old_rects.swap(builder->Rects);\n\n    for (ImFontAtlasRectEntry& index_entry : builder->RectsIndex)\n    {\n        if (index_entry.IsUsed == false)\n            continue;\n        ImTextureRect& old_r = old_rects[index_entry.TargetIndex];\n        if (old_r.w == 0 && old_r.h == 0)\n            continue;\n        ImFontAtlasRectId new_r_id = ImFontAtlasPackAddRect(atlas, old_r.w, old_r.h, &index_entry);\n        if (new_r_id == ImFontAtlasRectId_Invalid)\n        {\n            // Undo, grow texture and try repacking again.\n            // FIXME-NEWATLAS-TESTS: This is a very rarely exercised path! It needs to be automatically tested properly.\n            IMGUI_DEBUG_LOG_FONT(\"[font] Texture #%03d: resize failed. Will grow.\\n\", new_tex->UniqueID);\n            new_tex->WantDestroyNextFrame = true;\n            builder->Rects.swap(old_rects);\n            builder->RectsIndex = old_index;\n            ImFontAtlasBuildSetTexture(atlas, old_tex);\n            ImFontAtlasTextureGrow(atlas, w, h); // Recurse\n            return;\n        }\n        IM_ASSERT(ImFontAtlasRectId_GetIndex(new_r_id) == builder->RectsIndex.index_from_ptr(&index_entry));\n        ImTextureRect* new_r = ImFontAtlasPackGetRect(atlas, new_r_id);\n        ImFontAtlasTextureBlockCopy(old_tex, old_r.x, old_r.y, new_tex, new_r->x, new_r->y, new_r->w, new_r->h);\n    }\n    IM_ASSERT(old_rects.Size == builder->Rects.Size + builder->RectsDiscardedCount);\n    builder->RectsDiscardedCount = 0;\n    builder->RectsDiscardedSurface = 0;\n\n    // Patch glyphs UV\n    for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)\n        for (ImFontGlyph& glyph : builder->BakedPool[baked_n].Glyphs)\n            if (glyph.PackId != ImFontAtlasRectId_Invalid)\n            {\n                ImTextureRect* r = ImFontAtlasPackGetRect(atlas, glyph.PackId);\n                glyph.U0 = (r->x) * atlas->TexUvScale.x;\n                glyph.V0 = (r->y) * atlas->TexUvScale.y;\n                glyph.U1 = (r->x + r->w) * atlas->TexUvScale.x;\n                glyph.V1 = (r->y + r->h) * atlas->TexUvScale.y;\n            }\n\n    // Update other cached UV\n    ImFontAtlasBuildUpdateLinesTexData(atlas);\n    ImFontAtlasBuildUpdateBasicTexData(atlas);\n    ImFontAtlasBuildUpdateShadowTexData(atlas);\n\n    builder->LockDisableResize = false;\n    ImFontAtlasUpdateDrawListsSharedData(atlas);\n    //ImFontAtlasDebugWriteTexToDisk(new_tex, \"After Pack\");\n}\n\nvoid ImFontAtlasTextureGrow(ImFontAtlas* atlas, int old_tex_w, int old_tex_h)\n{\n    //ImFontAtlasDebugWriteTexToDisk(atlas->TexData, \"Before Grow\");\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    if (old_tex_w == -1)\n        old_tex_w = atlas->TexData->Width;\n    if (old_tex_h == -1)\n        old_tex_h = atlas->TexData->Height;\n\n    // FIXME-NEWATLAS-V2: What to do when reaching limits exposed by backend?\n    // FIXME-NEWATLAS-V2: Does ImFontAtlasFlags_NoPowerOfTwoHeight makes sense now? Allow 'lock' and 'compact' operations?\n    IM_ASSERT(ImIsPowerOfTwo(old_tex_w) && ImIsPowerOfTwo(old_tex_h));\n    IM_ASSERT(ImIsPowerOfTwo(atlas->TexMinWidth) && ImIsPowerOfTwo(atlas->TexMaxWidth) && ImIsPowerOfTwo(atlas->TexMinHeight) && ImIsPowerOfTwo(atlas->TexMaxHeight));\n\n    // Grow texture so it follows roughly a square.\n    // - Grow height before width, as width imply more packing nodes.\n    // - Caller should be taking account of RectsDiscardedSurface and may not need to grow.\n    int new_tex_w = (old_tex_h <= old_tex_w) ? old_tex_w : old_tex_w * 2;\n    int new_tex_h = (old_tex_h <= old_tex_w) ? old_tex_h * 2 : old_tex_h;\n\n    // Handle minimum size first (for pathologically large packed rects)\n    const int pack_padding = atlas->TexGlyphPadding;\n    new_tex_w = ImMax(new_tex_w, ImUpperPowerOfTwo(builder->MaxRectSize.x + pack_padding));\n    new_tex_h = ImMax(new_tex_h, ImUpperPowerOfTwo(builder->MaxRectSize.y + pack_padding));\n    new_tex_w = ImClamp(new_tex_w, atlas->TexMinWidth, atlas->TexMaxWidth);\n    new_tex_h = ImClamp(new_tex_h, atlas->TexMinHeight, atlas->TexMaxHeight);\n    if (new_tex_w == old_tex_w && new_tex_h == old_tex_h)\n        return;\n\n    ImFontAtlasTextureRepack(atlas, new_tex_w, new_tex_h);\n}\n\nvoid ImFontAtlasTextureMakeSpace(ImFontAtlas* atlas)\n{\n    // Can some baked contents be ditched?\n    //IMGUI_DEBUG_LOG_FONT(\"[font] ImFontAtlasBuildMakeSpace()\\n\");\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    ImFontAtlasBuildDiscardBakes(atlas, 2);\n\n    // Currently using a heuristic for repack without growing.\n    if (builder->RectsDiscardedSurface < builder->RectsPackedSurface * 0.20f)\n        ImFontAtlasTextureGrow(atlas);\n    else\n        ImFontAtlasTextureRepack(atlas, atlas->TexData->Width, atlas->TexData->Height);\n}\n\nImVec2i ImFontAtlasTextureGetSizeEstimate(ImFontAtlas* atlas)\n{\n    int min_w = ImUpperPowerOfTwo(atlas->TexMinWidth);\n    int min_h = ImUpperPowerOfTwo(atlas->TexMinHeight);\n    if (atlas->Builder == NULL || atlas->TexData == NULL || atlas->TexData->Status == ImTextureStatus_WantDestroy)\n        return ImVec2i(min_w, min_h);\n\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    min_w = ImMax(ImUpperPowerOfTwo(builder->MaxRectSize.x), min_w);\n    min_h = ImMax(ImUpperPowerOfTwo(builder->MaxRectSize.y), min_h);\n    const int surface_approx = builder->RectsPackedSurface - builder->RectsDiscardedSurface; // Expected surface after repack\n    const int surface_sqrt = (int)sqrtf((float)surface_approx);\n\n    int new_tex_w;\n    int new_tex_h;\n    if (min_w >= min_h)\n    {\n        new_tex_w = ImMax(min_w, ImUpperPowerOfTwo(surface_sqrt));\n        new_tex_h = ImMax(min_h, (int)((surface_approx + new_tex_w - 1) / new_tex_w));\n        if ((atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) == 0)\n            new_tex_h = ImUpperPowerOfTwo(new_tex_h);\n    }\n    else\n    {\n        new_tex_h = ImMax(min_h, ImUpperPowerOfTwo(surface_sqrt));\n        if ((atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) == 0)\n            new_tex_h = ImUpperPowerOfTwo(new_tex_h);\n        new_tex_w = ImMax(min_w, (int)((surface_approx + new_tex_h - 1) / new_tex_h));\n    }\n\n    IM_ASSERT(ImIsPowerOfTwo(new_tex_w) && ImIsPowerOfTwo(new_tex_h));\n    return ImVec2i(new_tex_w, new_tex_h);\n}\n\n// Clear all output. Invalidates all AddCustomRect() return values!\nvoid ImFontAtlasBuildClear(ImFontAtlas* atlas)\n{\n    ImVec2i new_tex_size = ImFontAtlasTextureGetSizeEstimate(atlas);\n    ImFontAtlasBuildDestroy(atlas);\n    ImFontAtlasTextureAdd(atlas, new_tex_size.x, new_tex_size.y);\n    ImFontAtlasBuildInit(atlas);\n    for (ImFontConfig& src : atlas->Sources)\n        ImFontAtlasFontSourceInit(atlas, &src);\n    for (ImFont* font : atlas->Fonts)\n        for (ImFontConfig* src : font->Sources)\n            ImFontAtlasFontSourceAddToFont(atlas, font, src);\n}\n\n// You should not need to call this manually!\n// If you think you do, let us know and we can advise about policies auto-compact.\nvoid ImFontAtlasTextureCompact(ImFontAtlas* atlas)\n{\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    ImFontAtlasBuildDiscardBakes(atlas, 1);\n\n    ImTextureData* old_tex = atlas->TexData;\n    ImVec2i old_tex_size = ImVec2i(old_tex->Width, old_tex->Height);\n    ImVec2i new_tex_size = ImFontAtlasTextureGetSizeEstimate(atlas);\n    if (builder->RectsDiscardedCount == 0 && new_tex_size.x == old_tex_size.x && new_tex_size.y == old_tex_size.y)\n        return;\n\n    ImFontAtlasTextureRepack(atlas, new_tex_size.x, new_tex_size.y);\n}\n\n// Calculates the signed distance from sample_pos to the nearest point on the rectangle defined by rect_min->rect_max\nstatic float DistanceFromRectangle(const ImVec2& sample_pos, const ImVec2& rect_min, const ImVec2& rect_max)\n{\n    ImVec2 rect_centre = (rect_min + rect_max) * 0.5f;\n    ImVec2 rect_half_size = (rect_max - rect_min) * 0.5f;\n    ImVec2 local_sample_pos = sample_pos - rect_centre;\n    ImVec2 axis_dist = ImVec2(ImFabs(local_sample_pos.x), ImFabs(local_sample_pos.y)) - rect_half_size;\n    float out_dist = ImLength(ImVec2(ImMax(axis_dist.x, 0.0f), ImMax(axis_dist.y, 0.0f)), 0.00001f);\n    float in_dist = ImMin(ImMax(axis_dist.x, axis_dist.y), 0.0f);\n    return out_dist + in_dist;\n}\n\n// Calculates the signed distance from sample_pos to the point given\nstatic float DistanceFromPoint(const ImVec2& sample_pos, const ImVec2& point)\n{\n    return ImLength(sample_pos - point, 0.0f);\n}\n\n// Perform a single Gaussian blur pass with a fixed kernel size and sigma\nstatic void GaussianBlurPass(float* src, float* dest, int size, bool horizontal)\n{\n    // See http://dev.theomader.com/gaussian-kernel-calculator/\n    const float coefficients[] = { 0.0f, 0.0f, 0.000003f, 0.000229f, 0.005977f, 0.060598f, 0.24173f, 0.382925f, 0.24173f, 0.060598f, 0.005977f, 0.000229f, 0.000003f, 0.0f, 0.0f };\n    const int kernel_size = IM_ARRAYSIZE(coefficients);\n    const int sample_step = horizontal ? 1 : size;\n\n    float* read_ptr = src;\n    float* write_ptr = dest;\n    for (int y = 0; y < size; y++)\n        for (int x = 0; x < size; x++)\n        {\n            float result = 0.0f;\n            int current_offset = (horizontal ? x : y) - ((kernel_size - 1) >> 1);\n            float* sample_ptr = read_ptr - (((kernel_size - 1) >> 1) * sample_step);\n            for (int j = 0; j < kernel_size; j++)\n            {\n                if (current_offset >= 0 && current_offset < size)\n                    result += (*sample_ptr) * coefficients[j];\n                current_offset++;\n                sample_ptr += sample_step;\n            }\n            read_ptr++;\n            *(write_ptr++) = result;\n        }\n}\n\n// Perform an in-place Gaussian blur of a square array of floats with a fixed kernel size and sigma\n// Uses a stack allocation for the temporary data so potentially dangerous with large size values\nstatic void GaussianBlur(float* data, int size)\n{\n    // Do two passes, one from data into temp and then the second back to data again\n    float* temp = (float*)alloca(size * size * sizeof(float));\n    GaussianBlurPass(data, temp, size, true);\n    GaussianBlurPass(temp, data, size, false);\n}\n\n// Generate the actual pixel data for rounded corners in the atlas\nstatic void ImFontAtlasBuildUpdateShadowTexData(ImFontAtlas* atlas)\n{\n    // The actual size we want to reserve, including padding\n    const ImFontAtlasShadowTexConfig* shadow_cfg = &atlas->ShadowTexConfig;\n    const unsigned int effective_size = shadow_cfg->CalcRectTexSize() + shadow_cfg->GetRectTexPadding();\n    const int corner_size = shadow_cfg->TexCornerSize;\n    const int edge_size = shadow_cfg->TexEdgeSize;\n\n    // Because of the blur, we have to generate the full 3x3 texture here, and then we chop that down to just the 2x2 section we need later.\n    // 'size' correspond to the our 3x3 size, whereas 'shadow_tex_size' correspond to our 2x2 version where duplicate mirrored corners are not stored.\n\n    // The rectangular shadow texture\n    ImFontAtlasRect r;\n    bool add_and_draw = (atlas->GetCustomRect(atlas->ShadowRectIds[0], &r) == false);\n    if (add_and_draw)\n    {\n        atlas->ShadowRectIds[0] = atlas->AddCustomRect(effective_size, effective_size, &r);\n        IM_ASSERT(atlas->ShadowRectIds[0] != ImFontAtlasRectId_Invalid);\n\n        const int size = shadow_cfg->TexCornerSize + shadow_cfg->TexEdgeSize + shadow_cfg->TexCornerSize;\n\n        // The bounds of the rectangle we are generating the shadow from\n        const ImVec2 shadow_rect_min((float)corner_size, (float)corner_size);\n        const ImVec2 shadow_rect_max((float)(corner_size + edge_size), (float)(corner_size + edge_size));\n\n        // Remove the padding we added\n        const int padding = shadow_cfg->GetRectTexPadding();\n        r.x += (unsigned short)padding;\n        r.y += (unsigned short)padding;\n        r.w -= (unsigned short)padding * 2;\n        r.h -= (unsigned short)padding * 2;\n\n        // Generate distance field\n        // We draw the actual texture content by evaluating the distance field for the inner rectangle\n        float* tex_data = (float*)alloca(size * size * sizeof(float));\n        for (int y = 0; y < size; y++)\n            for (int x = 0; x < size; x++)\n            {\n                float dist = DistanceFromRectangle(ImVec2((float)x, (float)y), shadow_rect_min, shadow_rect_max);\n                float alpha = 1.0f - ImMin(ImMax(dist + shadow_cfg->TexDistanceFieldOffset, 0.0f) / ImMax(shadow_cfg->TexCornerSize + shadow_cfg->TexDistanceFieldOffset, 0.001f), 1.0f);\n                alpha = ImPow(alpha, shadow_cfg->TexFalloffPower);  // Apply power curve to give a nicer falloff\n                tex_data[x + (y * size)] = alpha;\n            }\n\n        // Blur\n        if (shadow_cfg->TexBlur)\n            GaussianBlur(tex_data, size);\n\n        // Copy to texture, truncating to the actual required texture size (the bottom/right of the source data is chopped off, as we don't need it - see below). The truncated size is essentially the top 2x2 of our data, plus a little bit of padding for sampling.\n        ImTextureData* tex = atlas->TexData;\n        const int shadow_tex_size = shadow_cfg->CalcRectTexSize();\n        for (int y = 0; y < shadow_tex_size; y++)\n            for (int x = 0; x < shadow_tex_size; x++)\n            {\n                const float alpha_f = tex_data[x + (y * size)];\n                const unsigned char alpha_8 = (unsigned char)(0xFF * alpha_f);\n\n                // FIXME-SHADOWS: May be optimized.\n                switch (atlas->TexData->Format)\n                {\n                case ImTextureFormat_Alpha8:\n                {\n                    ImU8* out_p = (ImU8*)(void*)tex->GetPixelsAt(r.x + x, r.y + y);\n                    *out_p = alpha_8;\n                    break;\n                }\n                case ImTextureFormat_RGBA32:\n                {\n                    ImU32* out_p = (ImU32*)(void*)tex->GetPixelsAt(r.x + x, r.y + y);\n                    *out_p = IM_COL32(255, 255, 255, alpha_8);;\n                    break;\n                }\n                }\n            }\n    }\n\n    // Refresh UV coordinates\n    // Generate UVs for each of the nine sections, which are arranged in a 3x3 grid starting from 0 in the top-left and going across then down\n    if (atlas->GetCustomRect(atlas->ShadowRectIds[0], &r))\n    {\n        // Remove the padding we added\n        const int padding = shadow_cfg->GetRectTexPadding();\n        r.x += (unsigned short)padding;\n        r.y += (unsigned short)padding;\n        r.w -= (unsigned short)padding * 2;\n        r.h -= (unsigned short)padding * 2;\n\n        for (int i = 0; i < 9; i++)\n        {\n            // The third row/column of the 3x3 grid are generated by flipping the appropriate chunks of the upper 2x2 grid.\n            bool flip_h = false; // Do we need to flip the UVs horizontally?\n            bool flip_v = false; // Do we need to flip the UVs vertically?\n\n            ImFontAtlasRect sub_rect = r;\n            switch (i % 3)\n            {\n            case 0: sub_rect.w = (unsigned short)corner_size; break;\n            case 1: sub_rect.x += (unsigned short)corner_size; sub_rect.w = (unsigned short)edge_size; break;\n            case 2: sub_rect.w = (unsigned short)corner_size; flip_h = true; break;\n            }\n\n            switch (i / 3)\n            {\n            case 0: sub_rect.h = (unsigned short)corner_size; break;\n            case 1: sub_rect.y += (unsigned short)corner_size; sub_rect.h = (unsigned short)edge_size; break;\n            case 2: sub_rect.h = (unsigned short)corner_size; flip_v = true; break;\n            }\n\n            // FIXME-SHADOWS: caching UV !!\n            ImVec2 uv0 = ImVec2((float)(sub_rect.x), (float)(sub_rect.y)) * atlas->TexUvScale;\n            ImVec2 uv1 = ImVec2((float)(sub_rect.x + sub_rect.w), (float)(sub_rect.y + sub_rect.h)) * atlas->TexUvScale;\n            atlas->ShadowRectUvs[i] = ImVec4(flip_h ? uv1.x : uv0.x, flip_v ? uv1.y : uv0.y, flip_h ? uv0.x : uv1.x, flip_v ? uv0.y : uv1.y);\n        }\n    }\n\n    // The convex shape shadow texture\n    add_and_draw = (atlas->GetCustomRect(atlas->ShadowRectIds[1], &r) == false);\n    if (add_and_draw)\n    {\n        atlas->ShadowRectIds[1] = atlas->AddCustomRect(shadow_cfg->CalcConvexTexWidth() + shadow_cfg->GetConvexTexPadding(), shadow_cfg->CalcConvexTexHeight() + shadow_cfg->GetConvexTexPadding(), &r);\n        IM_ASSERT(atlas->ShadowRectIds[1] != ImFontAtlasRectId_Invalid);\n\n        const int size = shadow_cfg->TexCornerSize * 2;\n        const int padding = shadow_cfg->GetConvexTexPadding();\n\n        // Generate distance field\n        // We draw the actual texture content by evaluating the distance field for the distance from a center point\n        ImVec2 center_point(size * 0.5f, size * 0.5f);\n        float* tex_data = (float*)alloca(size * size * sizeof(float));\n        for (int y = 0; y < size; y++)\n            for (int x = 0; x < size; x++)\n            {\n                float dist = DistanceFromPoint(ImVec2((float)x, (float)y), center_point);\n                float alpha = 1.0f - ImMin(ImMax((float)dist + shadow_cfg->TexDistanceFieldOffset, 0.0f) / ImMax((float)shadow_cfg->TexCornerSize + shadow_cfg->TexDistanceFieldOffset, 0.001f), 1.0f);\n                alpha = ImPow(alpha, shadow_cfg->TexFalloffPower);  // Apply power curve to give a nicer falloff\n                tex_data[x + (y * size)] = alpha;\n            }\n\n        // Blur\n        if (shadow_cfg->TexBlur)\n            GaussianBlur(tex_data, size);\n\n        // Copy to texture, truncating to the actual required texture size (the bottom/right of the source data is chopped off, as we don't need it - see below)\n        // We push the data down and right by the amount we padded the top of the texture (see CalcConvexTexWidth/CalcConvexTexHeight) for details\n        const int padded_size = (int)(shadow_cfg->TexCornerSize / ImCos(IM_PI * 0.25f));\n        const int src_x_offset = padding + (padded_size - shadow_cfg->TexCornerSize);\n        const int src_y_offset = padding + (padded_size - shadow_cfg->TexCornerSize);\n\n        const int tex_width = shadow_cfg->CalcConvexTexWidth();\n        const int tex_height = shadow_cfg->CalcConvexTexHeight();\n        ImTextureData* tex = atlas->TexData;\n        for (int y = 0; y < tex_height; y++)\n            for (int x = 0; x < tex_width; x++)\n            {\n                const int src_x = ImClamp(x - src_x_offset, 0, size - 1);\n                const int src_y = ImClamp(y - src_y_offset, 0, size - 1);\n                const float alpha_f = tex_data[src_x + (src_y * size)];\n                const unsigned char alpha_8 = (unsigned char)(0xFF * alpha_f);\n\n                // FIXME-SHADOWS: May be optimized.\n                switch (atlas->TexData->Format)\n                {\n                case ImTextureFormat_Alpha8:\n                {\n                    ImU8* out_p = (ImU8*)(void*)tex->GetPixelsAt(r.x + x, r.y + y);\n                    *out_p = alpha_8;\n                    break;\n                }\n                case ImTextureFormat_RGBA32:\n                {\n                    ImU32* out_p = (ImU32*)(void*)tex->GetPixelsAt(r.x + x, r.y + y);\n                    *out_p = IM_COL32(255, 255, 255, alpha_8);;\n                    break;\n                }\n                }\n            }\n    }\n\n    // Refresh UV coordinates\n    if (atlas->GetCustomRect(atlas->ShadowRectIds[1], &r))\n    {\n        // Remove the padding we added\n        const int padding = shadow_cfg->GetConvexTexPadding();\n        const int tex_width = shadow_cfg->CalcConvexTexWidth();\n        const int tex_height = shadow_cfg->CalcConvexTexHeight();\n        r.x += (unsigned short)padding;\n        r.y += (unsigned short)padding;\n        r.w = (unsigned short)(tex_width - (padding * 2));\n        r.h = (unsigned short)(tex_height - (padding * 2));\n\n        ImVec2 uv0 = ImVec2((float)(r.x), (float)(r.y)) * atlas->TexUvScale;\n        ImVec2 uv1 = ImVec2((float)(r.x + r.w), (float)(r.y + r.h)) * atlas->TexUvScale;\n        atlas->ShadowRectUvs[9] = ImVec4(uv0.x, uv0.y, uv1.x, uv1.y);\n    }\n}\n\n// Start packing over current empty texture\nvoid ImFontAtlasBuildInit(ImFontAtlas* atlas)\n{\n    // Select Backend\n    // - Note that we do not reassign to atlas->FontLoader, since it is likely to point to static data which\n    //   may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are\n    //   using a hot-reloading scheme that messes up static data, store your own instance of FontLoader somewhere\n    //   and point to it instead of pointing directly to return value of the GetFontLoaderXXX functions.\n    if (atlas->FontLoader == NULL)\n    {\n#ifdef IMGUI_ENABLE_FREETYPE\n        atlas->SetFontLoader(ImGuiFreeType::GetFontLoader());\n#elif defined(IMGUI_ENABLE_STB_TRUETYPE)\n        atlas->SetFontLoader(ImFontAtlasGetFontLoaderForStbTruetype());\n#else\n        IM_ASSERT(0); // Invalid Build function\n#endif\n    }\n\n    // Create initial texture size\n    if (atlas->TexData == NULL || atlas->TexData->Pixels == NULL)\n        ImFontAtlasTextureAdd(atlas, ImUpperPowerOfTwo(atlas->TexMinWidth), ImUpperPowerOfTwo(atlas->TexMinHeight));\n\n    atlas->Builder = IM_NEW(ImFontAtlasBuilder)();\n    if (atlas->FontLoader->LoaderInit)\n        atlas->FontLoader->LoaderInit(atlas);\n\n    ImFontAtlasBuildUpdateRendererHasTexturesFromContext(atlas);\n\n    ImFontAtlasPackInit(atlas);\n\n    // Add required texture data\n    ImFontAtlasBuildUpdateLinesTexData(atlas);\n    ImFontAtlasBuildUpdateBasicTexData(atlas);\n    ImFontAtlasBuildUpdateShadowTexData(atlas);\n\n    // Register fonts\n    ImFontAtlasBuildUpdatePointers(atlas);\n\n    // Update UV coordinates etc. stored in bound ImDrawListSharedData instance\n    ImFontAtlasUpdateDrawListsSharedData(atlas);\n\n    //atlas->TexIsBuilt = true;\n\n    // Lazily initialize char/text classifier\n    // FIXME: This could be practically anywhere, and should eventually be parameters to CalcTextSize/word-wrapping code, but there's no obvious spot now.\n    ImTextInitClassifiers();\n}\n\n// Destroy builder and all cached glyphs. Do not destroy actual fonts.\nvoid ImFontAtlasBuildDestroy(ImFontAtlas* atlas)\n{\n    for (ImFont* font : atlas->Fonts)\n        ImFontAtlasFontDestroyOutput(atlas, font);\n    if (atlas->Builder && atlas->FontLoader && atlas->FontLoader->LoaderShutdown)\n    {\n        atlas->FontLoader->LoaderShutdown(atlas);\n        IM_ASSERT(atlas->FontLoaderData == NULL);\n    }\n    IM_DELETE(atlas->Builder);\n    atlas->Builder = NULL;\n}\n\nvoid ImFontAtlasPackInit(ImFontAtlas * atlas)\n{\n    ImTextureData* tex = atlas->TexData;\n    ImFontAtlasBuilder* builder = atlas->Builder;\n\n    // In theory we could decide to reduce the number of nodes, e.g. halve them, and waste a little texture space, but it doesn't seem worth it.\n    const int pack_node_count = tex->Width / 2;\n    builder->PackNodes.resize(pack_node_count);\n    IM_STATIC_ASSERT(sizeof(stbrp_context) <= sizeof(stbrp_context_opaque));\n    stbrp_init_target((stbrp_context*)(void*)&builder->PackContext, tex->Width, tex->Height, builder->PackNodes.Data, builder->PackNodes.Size);\n    builder->RectsPackedSurface = builder->RectsPackedCount = 0;\n    builder->MaxRectSize = ImVec2i(0, 0);\n    builder->MaxRectBounds = ImVec2i(0, 0);\n}\n\n// This is essentially a free-list pattern, it may be nice to wrap it into a dedicated type.\nstatic ImFontAtlasRectId ImFontAtlasPackAllocRectEntry(ImFontAtlas* atlas, int rect_idx)\n{\n    ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;\n    int index_idx;\n    ImFontAtlasRectEntry* index_entry;\n    if (builder->RectsIndexFreeListStart < 0)\n    {\n        builder->RectsIndex.resize(builder->RectsIndex.Size + 1);\n        index_idx = builder->RectsIndex.Size - 1;\n        index_entry = &builder->RectsIndex[index_idx];\n        memset(index_entry, 0, sizeof(*index_entry));\n    }\n    else\n    {\n        index_idx = builder->RectsIndexFreeListStart;\n        index_entry = &builder->RectsIndex[index_idx];\n        IM_ASSERT(index_entry->IsUsed == false && index_entry->Generation > 0); // Generation is incremented during DiscardRect\n        builder->RectsIndexFreeListStart = index_entry->TargetIndex;\n    }\n    index_entry->TargetIndex = rect_idx;\n    index_entry->IsUsed = 1;\n    return ImFontAtlasRectId_Make(index_idx, index_entry->Generation);\n}\n\n// Overwrite existing entry\nstatic ImFontAtlasRectId ImFontAtlasPackReuseRectEntry(ImFontAtlas* atlas, ImFontAtlasRectEntry* index_entry)\n{\n    IM_ASSERT(index_entry->IsUsed);\n    index_entry->TargetIndex = atlas->Builder->Rects.Size - 1;\n    int index_idx = atlas->Builder->RectsIndex.index_from_ptr(index_entry);\n    return ImFontAtlasRectId_Make(index_idx, index_entry->Generation);\n}\n\n// This is expected to be called in batches and followed by a repack\nvoid ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id)\n{\n    IM_ASSERT(id != ImFontAtlasRectId_Invalid);\n\n    ImTextureRect* rect = ImFontAtlasPackGetRect(atlas, id);\n    if (rect == NULL)\n        return;\n\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    int index_idx = ImFontAtlasRectId_GetIndex(id);\n    ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx];\n    IM_ASSERT(index_entry->IsUsed && index_entry->TargetIndex >= 0);\n    index_entry->IsUsed = false;\n    index_entry->TargetIndex = builder->RectsIndexFreeListStart;\n    index_entry->Generation++;\n    if (index_entry->Generation == 0)\n        index_entry->Generation++; // Keep non-zero on overflow\n\n    const int pack_padding = atlas->TexGlyphPadding;\n    builder->RectsIndexFreeListStart = index_idx;\n    builder->RectsDiscardedCount++;\n    builder->RectsDiscardedSurface += (rect->w + pack_padding) * (rect->h + pack_padding);\n    rect->w = rect->h = 0; // Clear rectangle so it won't be packed again\n}\n\n// Important: Calling this may recreate a new texture and therefore change atlas->TexData\n// FIXME-NEWFONTS: Expose other glyph padding settings for custom alteration (e.g. drop shadows). See #7962\nImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFontAtlasRectEntry* overwrite_entry)\n{\n    IM_ASSERT(w > 0 && w <= 0xFFFF);\n    IM_ASSERT(h > 0 && h <= 0xFFFF);\n\n    ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;\n    const int pack_padding = atlas->TexGlyphPadding;\n    builder->MaxRectSize.x = ImMax(builder->MaxRectSize.x, w);\n    builder->MaxRectSize.y = ImMax(builder->MaxRectSize.y, h);\n\n    // Pack\n    ImTextureRect r = { 0, 0, (unsigned short)w, (unsigned short)h };\n    for (int attempts_remaining = 3; attempts_remaining >= 0; attempts_remaining--)\n    {\n        // Try packing\n        stbrp_rect pack_r = {};\n        pack_r.w = w + pack_padding;\n        pack_r.h = h + pack_padding;\n        stbrp_pack_rects((stbrp_context*)(void*)&builder->PackContext, &pack_r, 1);\n        r.x = (unsigned short)pack_r.x;\n        r.y = (unsigned short)pack_r.y;\n        if (pack_r.was_packed)\n            break;\n\n        // If we ran out of attempts, return fallback\n        if (attempts_remaining == 0 || builder->LockDisableResize)\n        {\n            IMGUI_DEBUG_LOG_FONT(\"[font] Failed packing %dx%d rectangle. Returning fallback.\\n\", w, h);\n            return ImFontAtlasRectId_Invalid;\n        }\n\n        // Resize or repack atlas! (this should be a rare event)\n        ImFontAtlasTextureMakeSpace(atlas);\n    }\n\n    builder->MaxRectBounds.x = ImMax(builder->MaxRectBounds.x, r.x + r.w + pack_padding);\n    builder->MaxRectBounds.y = ImMax(builder->MaxRectBounds.y, r.y + r.h + pack_padding);\n    builder->RectsPackedCount++;\n    builder->RectsPackedSurface += (w + pack_padding) * (h + pack_padding);\n\n    builder->Rects.push_back(r);\n    if (overwrite_entry != NULL)\n        return ImFontAtlasPackReuseRectEntry(atlas, overwrite_entry); // Write into an existing entry instead of adding one (used during repack)\n    else\n        return ImFontAtlasPackAllocRectEntry(atlas, builder->Rects.Size - 1);\n}\n\n// Generally for non-user facing functions: assert on invalid ID.\nImTextureRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id)\n{\n    IM_ASSERT(id != ImFontAtlasRectId_Invalid);\n    int index_idx = ImFontAtlasRectId_GetIndex(id);\n    ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;\n    ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx];\n    IM_ASSERT(index_entry->Generation == ImFontAtlasRectId_GetGeneration(id));\n    IM_ASSERT(index_entry->IsUsed);\n    return &builder->Rects[index_entry->TargetIndex];\n}\n\n// For user-facing functions: return NULL on invalid ID.\n// Important: return pointer is valid until next call to AddRect(), e.g. FindGlyph(), CalcTextSize() can all potentially invalidate previous pointers.\nImTextureRect* ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id)\n{\n    if (id == ImFontAtlasRectId_Invalid)\n        return NULL;\n    int index_idx = ImFontAtlasRectId_GetIndex(id);\n    if (atlas->Builder == NULL)\n        ImFontAtlasBuildInit(atlas);\n    ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;\n    IM_MSVC_WARNING_SUPPRESS(28182); // Static Analysis false positive \"warning C28182: Dereferencing NULL pointer 'builder'\"\n    if (index_idx >= builder->RectsIndex.Size)\n        return NULL;\n    ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx];\n    if (index_entry->Generation != ImFontAtlasRectId_GetGeneration(id) || !index_entry->IsUsed)\n        return NULL;\n    return &builder->Rects[index_entry->TargetIndex];\n}\n\n// Important! This assume by ImFontConfig::GlyphExcludeRanges[] is a SMALL ARRAY (e.g. <10 entries)\n// Use \"Input Glyphs Overlap Detection Tool\" to display a list of glyphs provided by multiple sources in order to set this array up.\nstatic bool ImFontAtlasBuildAcceptCodepointForSource(ImFontConfig* src, ImWchar codepoint)\n{\n    if (const ImWchar* exclude_list = src->GlyphExcludeRanges)\n        for (; exclude_list[0] != 0; exclude_list += 2)\n            if (codepoint >= exclude_list[0] && codepoint <= exclude_list[1])\n                return false;\n    return true;\n}\n\nstatic void ImFontBaked_BuildGrowIndex(ImFontBaked* baked, int new_size)\n{\n    IM_ASSERT(baked->IndexAdvanceX.Size == baked->IndexLookup.Size);\n    if (new_size <= baked->IndexLookup.Size)\n        return;\n    baked->IndexAdvanceX.resize(new_size, -1.0f);\n    baked->IndexLookup.resize(new_size, IM_FONTGLYPH_INDEX_UNUSED);\n}\n\nstatic void ImFontAtlas_FontHookRemapCodepoint(ImFontAtlas* atlas, ImFont* font, ImWchar* c)\n{\n    IM_UNUSED(atlas);\n    if (font->RemapPairs.Data.Size != 0)\n        *c = (ImWchar)font->RemapPairs.GetInt((ImGuiID)*c, (int)*c);\n}\n\nstatic ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint, float* only_load_advance_x)\n{\n    ImFont* font = baked->OwnerFont;\n    ImFontAtlas* atlas = font->OwnerAtlas;\n    if (atlas->Locked || (font->Flags & ImFontFlags_NoLoadGlyphs))\n    {\n        // Lazily load fallback glyph\n        if (baked->FallbackGlyphIndex == -1 && baked->LoadNoFallback == 0)\n            ImFontAtlasBuildSetupFontBakedFallback(baked);\n        return NULL;\n    }\n\n    // User remapping hooks\n    ImWchar src_codepoint = codepoint;\n    ImFontAtlas_FontHookRemapCodepoint(atlas, font, &codepoint);\n\n    //char utf8_buf[5];\n    //IMGUI_DEBUG_LOG(\"[font] BuildLoadGlyph U+%04X (%s)\\n\", (unsigned int)codepoint, ImTextCharToUtf8(utf8_buf, (unsigned int)codepoint));\n\n    // Special hook\n    // FIXME-NEWATLAS: it would be nicer if this used a more standardized way of hooking\n    if (codepoint == font->EllipsisChar && font->EllipsisAutoBake)\n        if (ImFontGlyph* glyph = ImFontAtlasBuildSetupFontBakedEllipsis(atlas, baked))\n            return glyph;\n\n    // Call backend\n    char* loader_user_data_p = (char*)baked->FontLoaderDatas;\n    int src_n = 0;\n    for (ImFontConfig* src : font->Sources)\n    {\n        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;\n        if (!src->GlyphExcludeRanges || ImFontAtlasBuildAcceptCodepointForSource(src, codepoint))\n        {\n            if (only_load_advance_x == NULL)\n            {\n                ImFontGlyph glyph_buf;\n                if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, &glyph_buf, NULL))\n                {\n                    // FIXME: Add hooks for e.g. #7962\n                    glyph_buf.Codepoint = src_codepoint;\n                    glyph_buf.SourceIdx = src_n;\n                    return ImFontAtlasBakedAddFontGlyph(atlas, baked, src, &glyph_buf);\n                }\n            }\n            else\n            {\n                // Special mode but only loading glyphs metrics. Will rasterize and pack later.\n                if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, NULL, only_load_advance_x))\n                {\n                    ImFontAtlasBakedAddFontGlyphAdvancedX(atlas, baked, src, codepoint, *only_load_advance_x);\n                    return NULL;\n                }\n            }\n        }\n        loader_user_data_p += loader->FontBakedSrcLoaderDataSize;\n        src_n++;\n    }\n\n    // Lazily load fallback glyph\n    if (baked->LoadNoFallback)\n        return NULL;\n    if (baked->FallbackGlyphIndex == -1)\n        ImFontAtlasBuildSetupFontBakedFallback(baked);\n\n    // Mark index as not found, so we don't attempt the search twice\n    ImFontBaked_BuildGrowIndex(baked, codepoint + 1);\n    baked->IndexAdvanceX[codepoint] = baked->FallbackAdvanceX;\n    baked->IndexLookup[codepoint] = IM_FONTGLYPH_INDEX_NOT_FOUND;\n    return NULL;\n}\n\nstatic float ImFontBaked_BuildLoadGlyphAdvanceX(ImFontBaked* baked, ImWchar codepoint)\n{\n    if (baked->Size >= IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE || baked->LoadNoRenderOnLayout)\n    {\n        // First load AdvanceX value used by CalcTextSize() API then load the rest when loaded by drawing API.\n        float only_advance_x = 0.0f;\n        ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, &only_advance_x);\n        return glyph ? glyph->AdvanceX : only_advance_x;\n    }\n    else\n    {\n        ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, NULL);\n        return glyph ? glyph->AdvanceX : baked->FallbackAdvanceX;\n    }\n}\n\n// The point of this indirection is to not be inlined in debug mode in order to not bloat inner loop.b\nIM_MSVC_RUNTIME_CHECKS_OFF\nstatic float BuildLoadGlyphGetAdvanceOrFallback(ImFontBaked* baked, unsigned int codepoint)\n{\n    return ImFontBaked_BuildLoadGlyphAdvanceX(baked, (ImWchar)codepoint);\n}\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\nvoid ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas)\n{\n    // [DEBUG] Log texture update requests\n    ImGuiContext& g = *GImGui;\n    IM_UNUSED(g);\n    for (ImTextureData* tex : atlas->TexList)\n    {\n        if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)\n            IM_ASSERT(tex->Updates.Size == 0);\n        if (tex->Status == ImTextureStatus_WantCreate)\n            IMGUI_DEBUG_LOG_FONT(\"[font] Texture #%03d: create %dx%d\\n\", tex->UniqueID, tex->Width, tex->Height);\n        else if (tex->Status == ImTextureStatus_WantDestroy)\n            IMGUI_DEBUG_LOG_FONT(\"[font] Texture #%03d: destroy %dx%d, texid=0x%\" IM_PRIX64 \", backend_data=%p\\n\", tex->UniqueID, tex->Width, tex->Height, ImGui::DebugTextureIDToU64(tex->TexID), tex->BackendUserData);\n        else if (tex->Status == ImTextureStatus_WantUpdates)\n        {\n            IMGUI_DEBUG_LOG_FONT(\"[font] Texture #%03d: update %d regions, texid=0x%\" IM_PRIX64 \", backend_data=0x%\" IM_PRIX64 \"\\n\", tex->UniqueID, tex->Updates.Size, ImGui::DebugTextureIDToU64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData);\n            for (const ImTextureRect& r : tex->Updates)\n            {\n                IM_UNUSED(r);\n                IM_ASSERT(r.x >= 0 && r.y >= 0);\n                IM_ASSERT(r.x + r.w <= tex->Width && r.y + r.h <= tex->Height); // In theory should subtract PackPadding but it's currently part of atlas and mid-frame change would wreck assert.\n                //IMGUI_DEBUG_LOG_FONT(\"[font] Texture #%03d: update (% 4d..%-4d)->(% 4d..%-4d), texid=0x%\" IM_PRIX64 \", backend_data=0x%\" IM_PRIX64 \"\\n\", tex->UniqueID, r.x, r.y, r.x + r.w, r.y + r.h, ImGui::DebugTextureIDToU64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData);\n            }\n        }\n    }\n}\n#endif\n\n//-------------------------------------------------------------------------\n// [SECTION] ImFontAtlas: backend for stb_truetype\n//-------------------------------------------------------------------------\n// (imstb_truetype.h in included near the top of this file, when IMGUI_ENABLE_STB_TRUETYPE is set)\n//-------------------------------------------------------------------------\n\n#ifdef IMGUI_ENABLE_STB_TRUETYPE\n\n// One for each ConfigData\nstruct ImGui_ImplStbTrueType_FontSrcData\n{\n    stbtt_fontinfo  FontInfo;\n    float           ScaleFactor;\n};\n\nstatic bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src)\n{\n    IM_UNUSED(atlas);\n\n    ImGui_ImplStbTrueType_FontSrcData* bd_font_data = IM_NEW(ImGui_ImplStbTrueType_FontSrcData);\n    IM_ASSERT(src->FontLoaderData == NULL);\n\n    // Initialize helper structure for font loading and verify that the TTF/OTF data is correct\n    const int font_offset = stbtt_GetFontOffsetForIndex((const unsigned char*)src->FontData, src->FontNo);\n    if (font_offset < 0)\n    {\n        IM_DELETE(bd_font_data);\n        IM_ASSERT_USER_ERROR(0, \"stbtt_GetFontOffsetForIndex(): FontData is incorrect, or FontNo cannot be found.\");\n        return false;\n    }\n    if (!stbtt_InitFont(&bd_font_data->FontInfo, (const unsigned char*)src->FontData, font_offset))\n    {\n        IM_DELETE(bd_font_data);\n        IM_ASSERT_USER_ERROR(0, \"stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize.\");\n        return false;\n    }\n    src->FontLoaderData = bd_font_data;\n\n    const float ref_size = src->DstFont->Sources[0]->SizePixels;\n    if (src->MergeMode && src->SizePixels == 0.0f)\n        src->SizePixels = ref_size;\n\n    bd_font_data->ScaleFactor = stbtt_ScaleForPixelHeight(&bd_font_data->FontInfo, 1.0f);\n    if (src->MergeMode && src->SizePixels != 0.0f && ref_size != 0.0f)\n        bd_font_data->ScaleFactor *= src->SizePixels / ref_size; // FIXME-NEWATLAS: Should tidy up that a bit\n    bd_font_data->ScaleFactor *= src->ExtraSizeScale;\n\n    return true;\n}\n\nstatic void ImGui_ImplStbTrueType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src)\n{\n    IM_UNUSED(atlas);\n    ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData;\n    IM_DELETE(bd_font_data);\n    src->FontLoaderData = NULL;\n}\n\nstatic bool ImGui_ImplStbTrueType_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint)\n{\n    IM_UNUSED(atlas);\n\n    ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData;\n    IM_ASSERT(bd_font_data != NULL);\n\n    int glyph_index = stbtt_FindGlyphIndex(&bd_font_data->FontInfo, (int)codepoint);\n    return glyph_index != 0;\n}\n\nstatic bool ImGui_ImplStbTrueType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*)\n{\n    IM_UNUSED(atlas);\n\n    ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData;\n    if (src->MergeMode == false)\n    {\n        // FIXME-NEWFONTS: reevaluate how to use sizing metrics\n        // FIXME-NEWFONTS: make use of line gap value\n        const float scale_for_layout = bd_font_data->ScaleFactor * baked->Size / src->ExtraSizeScale;\n        int unscaled_ascent, unscaled_descent, unscaled_line_gap;\n        stbtt_GetFontVMetrics(&bd_font_data->FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);\n        baked->Ascent = ImCeil(unscaled_ascent * scale_for_layout);\n        baked->Descent = ImFloor(unscaled_descent * scale_for_layout);\n    }\n    return true;\n}\n\nstatic bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x)\n{\n    // Search for first font which has the glyph\n    ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData;\n    IM_ASSERT(bd_font_data);\n    int glyph_index = stbtt_FindGlyphIndex(&bd_font_data->FontInfo, (int)codepoint);\n    if (glyph_index == 0)\n        return false;\n\n    // Fonts unit to pixels\n    int oversample_h, oversample_v;\n    ImFontAtlasBuildGetOversampleFactors(src, baked, &oversample_h, &oversample_v);\n    const float scale_for_layout = bd_font_data->ScaleFactor * baked->Size;\n    const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;\n    const float scale_for_raster_x = bd_font_data->ScaleFactor * baked->Size * rasterizer_density * oversample_h;\n    const float scale_for_raster_y = bd_font_data->ScaleFactor * baked->Size * rasterizer_density * oversample_v;\n\n    // Obtain size and advance\n    int x0, y0, x1, y1;\n    int advance, lsb;\n    stbtt_GetGlyphBitmapBoxSubpixel(&bd_font_data->FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, 0, 0, &x0, &y0, &x1, &y1);\n    stbtt_GetGlyphHMetrics(&bd_font_data->FontInfo, glyph_index, &advance, &lsb);\n\n    // Load metrics only mode\n    if (out_advance_x != NULL)\n    {\n        IM_ASSERT(out_glyph == NULL);\n        *out_advance_x = advance * scale_for_layout;\n        return true;\n    }\n\n    // Prepare glyph\n    out_glyph->Codepoint = codepoint;\n    out_glyph->AdvanceX = advance * scale_for_layout;\n\n    // Pack and retrieve position inside texture atlas\n    // (generally based on stbtt_PackFontRangesRenderIntoRects)\n    const bool is_visible = (x0 != x1 && y0 != y1);\n    if (is_visible)\n    {\n        const int w = (x1 - x0 + oversample_h - 1);\n        const int h = (y1 - y0 + oversample_v - 1);\n        ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, w, h);\n        if (pack_id == ImFontAtlasRectId_Invalid)\n        {\n            // Pathological out of memory case (TexMaxWidth/TexMaxHeight set too small?)\n            IM_ASSERT(pack_id != ImFontAtlasRectId_Invalid && \"Out of texture memory.\");\n            return false;\n        }\n        ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id);\n\n        // Render\n        stbtt_GetGlyphBitmapBox(&bd_font_data->FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, &x0, &y0, &x1, &y1);\n        ImFontAtlasBuilder* builder = atlas->Builder;\n        builder->TempBuffer.resize(w * h * 1);\n        unsigned char* bitmap_pixels = builder->TempBuffer.Data;\n        memset(bitmap_pixels, 0, w * h * 1);\n\n        // Render with oversampling\n        // (those functions conveniently assert if pixels are not cleared, which is another safety layer)\n        float sub_x, sub_y;\n        stbtt_MakeGlyphBitmapSubpixelPrefilter(&bd_font_data->FontInfo, bitmap_pixels, w, h, w,\n            scale_for_raster_x, scale_for_raster_y, 0, 0, oversample_h, oversample_v, &sub_x, &sub_y, glyph_index);\n\n        const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;\n        const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;\n        float font_off_x = ImFloor(src->GlyphOffset.x * offsets_scale + 0.5f); // Snap scaled offset.\n        float font_off_y = ImFloor(src->GlyphOffset.y * offsets_scale + 0.5f);\n        font_off_x += sub_x;\n        font_off_y += sub_y + IM_ROUND(baked->Ascent);\n        float recip_h = 1.0f / (oversample_h * rasterizer_density);\n        float recip_v = 1.0f / (oversample_v * rasterizer_density);\n\n        // Register glyph\n        // r->x r->y are coordinates inside texture (in pixels)\n        // glyph.X0, glyph.Y0 are drawing coordinates from base text position, and accounting for oversampling.\n        out_glyph->X0 = x0 * recip_h + font_off_x;\n        out_glyph->Y0 = y0 * recip_v + font_off_y;\n        out_glyph->X1 = (x0 + (int)r->w) * recip_h + font_off_x;\n        out_glyph->Y1 = (y0 + (int)r->h) * recip_v + font_off_y;\n        out_glyph->Visible = true;\n        out_glyph->PackId = pack_id;\n        ImFontAtlasBakedSetFontGlyphBitmap(atlas, baked, src, out_glyph, r, bitmap_pixels, ImTextureFormat_Alpha8, w);\n    }\n\n    return true;\n}\n\nconst ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype()\n{\n    static ImFontLoader loader;\n    loader.Name = \"stb_truetype\";\n    loader.FontSrcInit = ImGui_ImplStbTrueType_FontSrcInit;\n    loader.FontSrcDestroy = ImGui_ImplStbTrueType_FontSrcDestroy;\n    loader.FontSrcContainsGlyph = ImGui_ImplStbTrueType_FontSrcContainsGlyph;\n    loader.FontBakedInit = ImGui_ImplStbTrueType_FontBakedInit;\n    loader.FontBakedDestroy = NULL;\n    loader.FontBakedLoadGlyph = ImGui_ImplStbTrueType_FontBakedLoadGlyph;\n    return &loader;\n}\n\n#endif // IMGUI_ENABLE_STB_TRUETYPE\n\n//-------------------------------------------------------------------------\n// [SECTION] ImFontAtlas: glyph ranges helpers\n//-------------------------------------------------------------------------\n// - GetGlyphRangesDefault()\n// Obsolete functions since 1.92:\n// - GetGlyphRangesGreek()\n// - GetGlyphRangesKorean()\n// - GetGlyphRangesChineseFull()\n// - GetGlyphRangesChineseSimplifiedCommon()\n// - GetGlyphRangesJapanese()\n// - GetGlyphRangesCyrillic()\n// - GetGlyphRangesThai()\n// - GetGlyphRangesVietnamese()\n//-----------------------------------------------------------------------------\n\n// Retrieve list of range (2 int per range, values are inclusive)\nconst ImWchar*   ImFontAtlas::GetGlyphRangesDefault()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0,\n    };\n    return &ranges[0];\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\nconst ImWchar*   ImFontAtlas::GetGlyphRangesGreek()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x0370, 0x03FF, // Greek and Coptic\n        0,\n    };\n    return &ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesKorean()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x3131, 0x3163, // Korean alphabets\n        0xAC00, 0xD7A3, // Korean characters\n        0xFFFD, 0xFFFD, // Invalid\n        0,\n    };\n    return &ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesChineseFull()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x2000, 0x206F, // General Punctuation\n        0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana\n        0x31F0, 0x31FF, // Katakana Phonetic Extensions\n        0xFF00, 0xFFEF, // Half-width characters\n        0xFFFD, 0xFFFD, // Invalid\n        0x4e00, 0x9FAF, // CJK Ideograms\n        0,\n    };\n    return &ranges[0];\n}\n\nstatic void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short* accumulative_offsets, int accumulative_offsets_count, ImWchar* out_ranges)\n{\n    for (int n = 0; n < accumulative_offsets_count; n++, out_ranges += 2)\n    {\n        out_ranges[0] = out_ranges[1] = (ImWchar)(base_codepoint + accumulative_offsets[n]);\n        base_codepoint += accumulative_offsets[n];\n    }\n    out_ranges[0] = 0;\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon()\n{\n    // Store 2500 regularly used characters for Simplified Chinese.\n    // Sourced from https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8\n    // This table covers 97.97% of all characters used during the month in July, 1987.\n    // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.\n    // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.)\n    static const short accumulative_offsets_from_0x4E00[] =\n    {\n        0,1,2,4,1,1,1,1,2,1,3,2,1,2,2,1,1,1,1,1,5,2,1,2,3,3,3,2,2,4,1,1,1,2,1,5,2,3,1,2,1,2,1,1,2,1,1,2,2,1,4,1,1,1,1,5,10,1,2,19,2,1,2,1,2,1,2,1,2,\n        1,5,1,6,3,2,1,2,2,1,1,1,4,8,5,1,1,4,1,1,3,1,2,1,5,1,2,1,1,1,10,1,1,5,2,4,6,1,4,2,2,2,12,2,1,1,6,1,1,1,4,1,1,4,6,5,1,4,2,2,4,10,7,1,1,4,2,4,\n        2,1,4,3,6,10,12,5,7,2,14,2,9,1,1,6,7,10,4,7,13,1,5,4,8,4,1,1,2,28,5,6,1,1,5,2,5,20,2,2,9,8,11,2,9,17,1,8,6,8,27,4,6,9,20,11,27,6,68,2,2,1,1,\n        1,2,1,2,2,7,6,11,3,3,1,1,3,1,2,1,1,1,1,1,3,1,1,8,3,4,1,5,7,2,1,4,4,8,4,2,1,2,1,1,4,5,6,3,6,2,12,3,1,3,9,2,4,3,4,1,5,3,3,1,3,7,1,5,1,1,1,1,2,\n        3,4,5,2,3,2,6,1,1,2,1,7,1,7,3,4,5,15,2,2,1,5,3,22,19,2,1,1,1,1,2,5,1,1,1,6,1,1,12,8,2,9,18,22,4,1,1,5,1,16,1,2,7,10,15,1,1,6,2,4,1,2,4,1,6,\n        1,1,3,2,4,1,6,4,5,1,2,1,1,2,1,10,3,1,3,2,1,9,3,2,5,7,2,19,4,3,6,1,1,1,1,1,4,3,2,1,1,1,2,5,3,1,1,1,2,2,1,1,2,1,1,2,1,3,1,1,1,3,7,1,4,1,1,2,1,\n        1,2,1,2,4,4,3,8,1,1,1,2,1,3,5,1,3,1,3,4,6,2,2,14,4,6,6,11,9,1,15,3,1,28,5,2,5,5,3,1,3,4,5,4,6,14,3,2,3,5,21,2,7,20,10,1,2,19,2,4,28,28,2,3,\n        2,1,14,4,1,26,28,42,12,40,3,52,79,5,14,17,3,2,2,11,3,4,6,3,1,8,2,23,4,5,8,10,4,2,7,3,5,1,1,6,3,1,2,2,2,5,28,1,1,7,7,20,5,3,29,3,17,26,1,8,4,\n        27,3,6,11,23,5,3,4,6,13,24,16,6,5,10,25,35,7,3,2,3,3,14,3,6,2,6,1,4,2,3,8,2,1,1,3,3,3,4,1,1,13,2,2,4,5,2,1,14,14,1,2,2,1,4,5,2,3,1,14,3,12,\n        3,17,2,16,5,1,2,1,8,9,3,19,4,2,2,4,17,25,21,20,28,75,1,10,29,103,4,1,2,1,1,4,2,4,1,2,3,24,2,2,2,1,1,2,1,3,8,1,1,1,2,1,1,3,1,1,1,6,1,5,3,1,1,\n        1,3,4,1,1,5,2,1,5,6,13,9,16,1,1,1,1,3,2,3,2,4,5,2,5,2,2,3,7,13,7,2,2,1,1,1,1,2,3,3,2,1,6,4,9,2,1,14,2,14,2,1,18,3,4,14,4,11,41,15,23,15,23,\n        176,1,3,4,1,1,1,1,5,3,1,2,3,7,3,1,1,2,1,2,4,4,6,2,4,1,9,7,1,10,5,8,16,29,1,1,2,2,3,1,3,5,2,4,5,4,1,1,2,2,3,3,7,1,6,10,1,17,1,44,4,6,2,1,1,6,\n        5,4,2,10,1,6,9,2,8,1,24,1,2,13,7,8,8,2,1,4,1,3,1,3,3,5,2,5,10,9,4,9,12,2,1,6,1,10,1,1,7,7,4,10,8,3,1,13,4,3,1,6,1,3,5,2,1,2,17,16,5,2,16,6,\n        1,4,2,1,3,3,6,8,5,11,11,1,3,3,2,4,6,10,9,5,7,4,7,4,7,1,1,4,2,1,3,6,8,7,1,6,11,5,5,3,24,9,4,2,7,13,5,1,8,82,16,61,1,1,1,4,2,2,16,10,3,8,1,1,\n        6,4,2,1,3,1,1,1,4,3,8,4,2,2,1,1,1,1,1,6,3,5,1,1,4,6,9,2,1,1,1,2,1,7,2,1,6,1,5,4,4,3,1,8,1,3,3,1,3,2,2,2,2,3,1,6,1,2,1,2,1,3,7,1,8,2,1,2,1,5,\n        2,5,3,5,10,1,2,1,1,3,2,5,11,3,9,3,5,1,1,5,9,1,2,1,5,7,9,9,8,1,3,3,3,6,8,2,3,2,1,1,32,6,1,2,15,9,3,7,13,1,3,10,13,2,14,1,13,10,2,1,3,10,4,15,\n        2,15,15,10,1,3,9,6,9,32,25,26,47,7,3,2,3,1,6,3,4,3,2,8,5,4,1,9,4,2,2,19,10,6,2,3,8,1,2,2,4,2,1,9,4,4,4,6,4,8,9,2,3,1,1,1,1,3,5,5,1,3,8,4,6,\n        2,1,4,12,1,5,3,7,13,2,5,8,1,6,1,2,5,14,6,1,5,2,4,8,15,5,1,23,6,62,2,10,1,1,8,1,2,2,10,4,2,2,9,2,1,1,3,2,3,1,5,3,3,2,1,3,8,1,1,1,11,3,1,1,4,\n        3,7,1,14,1,2,3,12,5,2,5,1,6,7,5,7,14,11,1,3,1,8,9,12,2,1,11,8,4,4,2,6,10,9,13,1,1,3,1,5,1,3,2,4,4,1,18,2,3,14,11,4,29,4,2,7,1,3,13,9,2,2,5,\n        3,5,20,7,16,8,5,72,34,6,4,22,12,12,28,45,36,9,7,39,9,191,1,1,1,4,11,8,4,9,2,3,22,1,1,1,1,4,17,1,7,7,1,11,31,10,2,4,8,2,3,2,1,4,2,16,4,32,2,\n        3,19,13,4,9,1,5,2,14,8,1,1,3,6,19,6,5,1,16,6,2,10,8,5,1,2,3,1,5,5,1,11,6,6,1,3,3,2,6,3,8,1,1,4,10,7,5,7,7,5,8,9,2,1,3,4,1,1,3,1,3,3,2,6,16,\n        1,4,6,3,1,10,6,1,3,15,2,9,2,10,25,13,9,16,6,2,2,10,11,4,3,9,1,2,6,6,5,4,30,40,1,10,7,12,14,33,6,3,6,7,3,1,3,1,11,14,4,9,5,12,11,49,18,51,31,\n        140,31,2,2,1,5,1,8,1,10,1,4,4,3,24,1,10,1,3,6,6,16,3,4,5,2,1,4,2,57,10,6,22,2,22,3,7,22,6,10,11,36,18,16,33,36,2,5,5,1,1,1,4,10,1,4,13,2,7,\n        5,2,9,3,4,1,7,43,3,7,3,9,14,7,9,1,11,1,1,3,7,4,18,13,1,14,1,3,6,10,73,2,2,30,6,1,11,18,19,13,22,3,46,42,37,89,7,3,16,34,2,2,3,9,1,7,1,1,1,2,\n        2,4,10,7,3,10,3,9,5,28,9,2,6,13,7,3,1,3,10,2,7,2,11,3,6,21,54,85,2,1,4,2,2,1,39,3,21,2,2,5,1,1,1,4,1,1,3,4,15,1,3,2,4,4,2,3,8,2,20,1,8,7,13,\n        4,1,26,6,2,9,34,4,21,52,10,4,4,1,5,12,2,11,1,7,2,30,12,44,2,30,1,1,3,6,16,9,17,39,82,2,2,24,7,1,7,3,16,9,14,44,2,1,2,1,2,3,5,2,4,1,6,7,5,3,\n        2,6,1,11,5,11,2,1,18,19,8,1,3,24,29,2,1,3,5,2,2,1,13,6,5,1,46,11,3,5,1,1,5,8,2,10,6,12,6,3,7,11,2,4,16,13,2,5,1,1,2,2,5,2,28,5,2,23,10,8,4,\n        4,22,39,95,38,8,14,9,5,1,13,5,4,3,13,12,11,1,9,1,27,37,2,5,4,4,63,211,95,2,2,2,1,3,5,2,1,1,2,2,1,1,1,3,2,4,1,2,1,1,5,2,2,1,1,2,3,1,3,1,1,1,\n        3,1,4,2,1,3,6,1,1,3,7,15,5,3,2,5,3,9,11,4,2,22,1,6,3,8,7,1,4,28,4,16,3,3,25,4,4,27,27,1,4,1,2,2,7,1,3,5,2,28,8,2,14,1,8,6,16,25,3,3,3,14,3,\n        3,1,1,2,1,4,6,3,8,4,1,1,1,2,3,6,10,6,2,3,18,3,2,5,5,4,3,1,5,2,5,4,23,7,6,12,6,4,17,11,9,5,1,1,10,5,12,1,1,11,26,33,7,3,6,1,17,7,1,5,12,1,11,\n        2,4,1,8,14,17,23,1,2,1,7,8,16,11,9,6,5,2,6,4,16,2,8,14,1,11,8,9,1,1,1,9,25,4,11,19,7,2,15,2,12,8,52,7,5,19,2,16,4,36,8,1,16,8,24,26,4,6,2,9,\n        5,4,36,3,28,12,25,15,37,27,17,12,59,38,5,32,127,1,2,9,17,14,4,1,2,1,1,8,11,50,4,14,2,19,16,4,17,5,4,5,26,12,45,2,23,45,104,30,12,8,3,10,2,2,\n        3,3,1,4,20,7,2,9,6,15,2,20,1,3,16,4,11,15,6,134,2,5,59,1,2,2,2,1,9,17,3,26,137,10,211,59,1,2,4,1,4,1,1,1,2,6,2,3,1,1,2,3,2,3,1,3,4,4,2,3,3,\n        1,4,3,1,7,2,2,3,1,2,1,3,3,3,2,2,3,2,1,3,14,6,1,3,2,9,6,15,27,9,34,145,1,1,2,1,1,1,1,2,1,1,1,1,2,2,2,3,1,2,1,1,1,2,3,5,8,3,5,2,4,1,3,2,2,2,12,\n        4,1,1,1,10,4,5,1,20,4,16,1,15,9,5,12,2,9,2,5,4,2,26,19,7,1,26,4,30,12,15,42,1,6,8,172,1,1,4,2,1,1,11,2,2,4,2,1,2,1,10,8,1,2,1,4,5,1,2,5,1,8,\n        4,1,3,4,2,1,6,2,1,3,4,1,2,1,1,1,1,12,5,7,2,4,3,1,1,1,3,3,6,1,2,2,3,3,3,2,1,2,12,14,11,6,6,4,12,2,8,1,7,10,1,35,7,4,13,15,4,3,23,21,28,52,5,\n        26,5,6,1,7,10,2,7,53,3,2,1,1,1,2,163,532,1,10,11,1,3,3,4,8,2,8,6,2,2,23,22,4,2,2,4,2,1,3,1,3,3,5,9,8,2,1,2,8,1,10,2,12,21,20,15,105,2,3,1,1,\n        3,2,3,1,1,2,5,1,4,15,11,19,1,1,1,1,5,4,5,1,1,2,5,3,5,12,1,2,5,1,11,1,1,15,9,1,4,5,3,26,8,2,1,3,1,1,15,19,2,12,1,2,5,2,7,2,19,2,20,6,26,7,5,\n        2,2,7,34,21,13,70,2,128,1,1,2,1,1,2,1,1,3,2,2,2,15,1,4,1,3,4,42,10,6,1,49,85,8,1,2,1,1,4,4,2,3,6,1,5,7,4,3,211,4,1,2,1,2,5,1,2,4,2,2,6,5,6,\n        10,3,4,48,100,6,2,16,296,5,27,387,2,2,3,7,16,8,5,38,15,39,21,9,10,3,7,59,13,27,21,47,5,21,6\n    };\n    static ImWchar base_ranges[] = // not zero-terminated\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x2000, 0x206F, // General Punctuation\n        0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana\n        0x31F0, 0x31FF, // Katakana Phonetic Extensions\n        0xFF00, 0xFFEF, // Half-width characters\n        0xFFFD, 0xFFFD  // Invalid\n    };\n    static ImWchar full_ranges[IM_COUNTOF(base_ranges) + IM_COUNTOF(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 };\n    if (!full_ranges[0])\n    {\n        memcpy(full_ranges, base_ranges, sizeof(base_ranges));\n        UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_COUNTOF(accumulative_offsets_from_0x4E00), full_ranges + IM_COUNTOF(base_ranges));\n    }\n    return &full_ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesJapanese()\n{\n    // 2999 ideograms code points for Japanese\n    // - 2136 Joyo (meaning \"for regular use\" or \"for common use\") Kanji code points\n    // - 863 Jinmeiyo (meaning \"for personal name\") Kanji code points\n    // - Sourced from official information provided by the government agencies of Japan:\n    //   - List of Joyo Kanji by the Agency for Cultural Affairs\n    //     - https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kijun/naikaku/kanji/\n    //   - List of Jinmeiyo Kanji by the Ministry of Justice\n    //     - http://www.moj.go.jp/MINJI/minji86.html\n    //   - Available under the terms of the Creative Commons Attribution 4.0 International (CC BY 4.0).\n    //     - https://creativecommons.org/licenses/by/4.0/legalcode\n    // - You can generate this code by the script at:\n    //   - https://github.com/vaiorabbit/everyday_use_kanji\n    // - References:\n    //   - List of Joyo Kanji\n    //     - (Wikipedia) https://en.wikipedia.org/wiki/List_of_j%C5%8Dy%C5%8D_kanji\n    //   - List of Jinmeiyo Kanji\n    //     - (Wikipedia) https://en.wikipedia.org/wiki/Jinmeiy%C5%8D_kanji\n    // - Missing 1 Joyo Kanji: U+20B9F (Kun'yomi: Shikaru, On'yomi: Shitsu,shichi), see https://github.com/ocornut/imgui/pull/3627 for details.\n    // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.\n    // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.)\n    static const short accumulative_offsets_from_0x4E00[] =\n    {\n        0,1,2,4,1,1,1,1,2,1,3,3,2,2,1,5,3,5,7,5,6,1,2,1,7,2,6,3,1,8,1,1,4,1,1,18,2,11,2,6,2,1,2,1,5,1,2,1,3,1,2,1,2,3,3,1,1,2,3,1,1,1,12,7,9,1,4,5,1,\n        1,2,1,10,1,1,9,2,2,4,5,6,9,3,1,1,1,1,9,3,18,5,2,2,2,2,1,6,3,7,1,1,1,1,2,2,4,2,1,23,2,10,4,3,5,2,4,10,2,4,13,1,6,1,9,3,1,1,6,6,7,6,3,1,2,11,3,\n        2,2,3,2,15,2,2,5,4,3,6,4,1,2,5,2,12,16,6,13,9,13,2,1,1,7,16,4,7,1,19,1,5,1,2,2,7,7,8,2,6,5,4,9,18,7,4,5,9,13,11,8,15,2,1,1,1,2,1,2,2,1,2,2,8,\n        2,9,3,3,1,1,4,4,1,1,1,4,9,1,4,3,5,5,2,7,5,3,4,8,2,1,13,2,3,3,1,14,1,1,4,5,1,3,6,1,5,2,1,1,3,3,3,3,1,1,2,7,6,6,7,1,4,7,6,1,1,1,1,1,12,3,3,9,5,\n        2,6,1,5,6,1,2,3,18,2,4,14,4,1,3,6,1,1,6,3,5,5,3,2,2,2,2,12,3,1,4,2,3,2,3,11,1,7,4,1,2,1,3,17,1,9,1,24,1,1,4,2,2,4,1,2,7,1,1,1,3,1,2,2,4,15,1,\n        1,2,1,1,2,1,5,2,5,20,2,5,9,1,10,8,7,6,1,1,1,1,1,1,6,2,1,2,8,1,1,1,1,5,1,1,3,1,1,1,1,3,1,1,12,4,1,3,1,1,1,1,1,10,3,1,7,5,13,1,2,3,4,6,1,1,30,\n        2,9,9,1,15,38,11,3,1,8,24,7,1,9,8,10,2,1,9,31,2,13,6,2,9,4,49,5,2,15,2,1,10,2,1,1,1,2,2,6,15,30,35,3,14,18,8,1,16,10,28,12,19,45,38,1,3,2,3,\n        13,2,1,7,3,6,5,3,4,3,1,5,7,8,1,5,3,18,5,3,6,1,21,4,24,9,24,40,3,14,3,21,3,2,1,2,4,2,3,1,15,15,6,5,1,1,3,1,5,6,1,9,7,3,3,2,1,4,3,8,21,5,16,4,\n        5,2,10,11,11,3,6,3,2,9,3,6,13,1,2,1,1,1,1,11,12,6,6,1,4,2,6,5,2,1,1,3,3,6,13,3,1,1,5,1,2,3,3,14,2,1,2,2,2,5,1,9,5,1,1,6,12,3,12,3,4,13,2,14,\n        2,8,1,17,5,1,16,4,2,2,21,8,9,6,23,20,12,25,19,9,38,8,3,21,40,25,33,13,4,3,1,4,1,2,4,1,2,5,26,2,1,1,2,1,3,6,2,1,1,1,1,1,1,2,3,1,1,1,9,2,3,1,1,\n        1,3,6,3,2,1,1,6,6,1,8,2,2,2,1,4,1,2,3,2,7,3,2,4,1,2,1,2,2,1,1,1,1,1,3,1,2,5,4,10,9,4,9,1,1,1,1,1,1,5,3,2,1,6,4,9,6,1,10,2,31,17,8,3,7,5,40,1,\n        7,7,1,6,5,2,10,7,8,4,15,39,25,6,28,47,18,10,7,1,3,1,1,2,1,1,1,3,3,3,1,1,1,3,4,2,1,4,1,3,6,10,7,8,6,2,2,1,3,3,2,5,8,7,9,12,2,15,1,1,4,1,2,1,1,\n        1,3,2,1,3,3,5,6,2,3,2,10,1,4,2,8,1,1,1,11,6,1,21,4,16,3,1,3,1,4,2,3,6,5,1,3,1,1,3,3,4,6,1,1,10,4,2,7,10,4,7,4,2,9,4,3,1,1,1,4,1,8,3,4,1,3,1,\n        6,1,4,2,1,4,7,2,1,8,1,4,5,1,1,2,2,4,6,2,7,1,10,1,1,3,4,11,10,8,21,4,6,1,3,5,2,1,2,28,5,5,2,3,13,1,2,3,1,4,2,1,5,20,3,8,11,1,3,3,3,1,8,10,9,2,\n        10,9,2,3,1,1,2,4,1,8,3,6,1,7,8,6,11,1,4,29,8,4,3,1,2,7,13,1,4,1,6,2,6,12,12,2,20,3,2,3,6,4,8,9,2,7,34,5,1,18,6,1,1,4,4,5,7,9,1,2,2,4,3,4,1,7,\n        2,2,2,6,2,3,25,5,3,6,1,4,6,7,4,2,1,4,2,13,6,4,4,3,1,5,3,4,4,3,2,1,1,4,1,2,1,1,3,1,11,1,6,3,1,7,3,6,2,8,8,6,9,3,4,11,3,2,10,12,2,5,11,1,6,4,5,\n        3,1,8,5,4,6,6,3,5,1,1,3,2,1,2,2,6,17,12,1,10,1,6,12,1,6,6,19,9,6,16,1,13,4,4,15,7,17,6,11,9,15,12,6,7,2,1,2,2,15,9,3,21,4,6,49,18,7,3,2,3,1,\n        6,8,2,2,6,2,9,1,3,6,4,4,1,2,16,2,5,2,1,6,2,3,5,3,1,2,5,1,2,1,9,3,1,8,6,4,8,11,3,1,1,1,1,3,1,13,8,4,1,3,2,2,1,4,1,11,1,5,2,1,5,2,5,8,6,1,1,7,\n        4,3,8,3,2,7,2,1,5,1,5,2,4,7,6,2,8,5,1,11,4,5,3,6,18,1,2,13,3,3,1,21,1,1,4,1,4,1,1,1,8,1,2,2,7,1,2,4,2,2,9,2,1,1,1,4,3,6,3,12,5,1,1,1,5,6,3,2,\n        4,8,2,2,4,2,7,1,8,9,5,2,3,2,1,3,2,13,7,14,6,5,1,1,2,1,4,2,23,2,1,1,6,3,1,4,1,15,3,1,7,3,9,14,1,3,1,4,1,1,5,8,1,3,8,3,8,15,11,4,14,4,4,2,5,5,\n        1,7,1,6,14,7,7,8,5,15,4,8,6,5,6,2,1,13,1,20,15,11,9,2,5,6,2,11,2,6,2,5,1,5,8,4,13,19,25,4,1,1,11,1,34,2,5,9,14,6,2,2,6,1,1,14,1,3,14,13,1,6,\n        12,21,14,14,6,32,17,8,32,9,28,1,2,4,11,8,3,1,14,2,5,15,1,1,1,1,3,6,4,1,3,4,11,3,1,1,11,30,1,5,1,4,1,5,8,1,1,3,2,4,3,17,35,2,6,12,17,3,1,6,2,\n        1,1,12,2,7,3,3,2,1,16,2,8,3,6,5,4,7,3,3,8,1,9,8,5,1,2,1,3,2,8,1,2,9,12,1,1,2,3,8,3,24,12,4,3,7,5,8,3,3,3,3,3,3,1,23,10,3,1,2,2,6,3,1,16,1,16,\n        22,3,10,4,11,6,9,7,7,3,6,2,2,2,4,10,2,1,1,2,8,7,1,6,4,1,3,3,3,5,10,12,12,2,3,12,8,15,1,1,16,6,6,1,5,9,11,4,11,4,2,6,12,1,17,5,13,1,4,9,5,1,11,\n        2,1,8,1,5,7,28,8,3,5,10,2,17,3,38,22,1,2,18,12,10,4,38,18,1,4,44,19,4,1,8,4,1,12,1,4,31,12,1,14,7,75,7,5,10,6,6,13,3,2,11,11,3,2,5,28,15,6,18,\n        18,5,6,4,3,16,1,7,18,7,36,3,5,3,1,7,1,9,1,10,7,2,4,2,6,2,9,7,4,3,32,12,3,7,10,2,23,16,3,1,12,3,31,4,11,1,3,8,9,5,1,30,15,6,12,3,2,2,11,19,9,\n        14,2,6,2,3,19,13,17,5,3,3,25,3,14,1,1,1,36,1,3,2,19,3,13,36,9,13,31,6,4,16,34,2,5,4,2,3,3,5,1,1,1,4,3,1,17,3,2,3,5,3,1,3,2,3,5,6,3,12,11,1,3,\n        1,2,26,7,12,7,2,14,3,3,7,7,11,25,25,28,16,4,36,1,2,1,6,2,1,9,3,27,17,4,3,4,13,4,1,3,2,2,1,10,4,2,4,6,3,8,2,1,18,1,1,24,2,2,4,33,2,3,63,7,1,6,\n        40,7,3,4,4,2,4,15,18,1,16,1,1,11,2,41,14,1,3,18,13,3,2,4,16,2,17,7,15,24,7,18,13,44,2,2,3,6,1,1,7,5,1,7,1,4,3,3,5,10,8,2,3,1,8,1,1,27,4,2,1,\n        12,1,2,1,10,6,1,6,7,5,2,3,7,11,5,11,3,6,6,2,3,15,4,9,1,1,2,1,2,11,2,8,12,8,5,4,2,3,1,5,2,2,1,14,1,12,11,4,1,11,17,17,4,3,2,5,5,7,3,1,5,9,9,8,\n        2,5,6,6,13,13,2,1,2,6,1,2,2,49,4,9,1,2,10,16,7,8,4,3,2,23,4,58,3,29,1,14,19,19,11,11,2,7,5,1,3,4,6,2,18,5,12,12,17,17,3,3,2,4,1,6,2,3,4,3,1,\n        1,1,1,5,1,1,9,1,3,1,3,6,1,8,1,1,2,6,4,14,3,1,4,11,4,1,3,32,1,2,4,13,4,1,2,4,2,1,3,1,11,1,4,2,1,4,4,6,3,5,1,6,5,7,6,3,23,3,5,3,5,3,3,13,3,9,10,\n        1,12,10,2,3,18,13,7,160,52,4,2,2,3,2,14,5,4,12,4,6,4,1,20,4,11,6,2,12,27,1,4,1,2,2,7,4,5,2,28,3,7,25,8,3,19,3,6,10,2,2,1,10,2,5,4,1,3,4,1,5,\n        3,2,6,9,3,6,2,16,3,3,16,4,5,5,3,2,1,2,16,15,8,2,6,21,2,4,1,22,5,8,1,1,21,11,2,1,11,11,19,13,12,4,2,3,2,3,6,1,8,11,1,4,2,9,5,2,1,11,2,9,1,1,2,\n        14,31,9,3,4,21,14,4,8,1,7,2,2,2,5,1,4,20,3,3,4,10,1,11,9,8,2,1,4,5,14,12,14,2,17,9,6,31,4,14,1,20,13,26,5,2,7,3,6,13,2,4,2,19,6,2,2,18,9,3,5,\n        12,12,14,4,6,2,3,6,9,5,22,4,5,25,6,4,8,5,2,6,27,2,35,2,16,3,7,8,8,6,6,5,9,17,2,20,6,19,2,13,3,1,1,1,4,17,12,2,14,7,1,4,18,12,38,33,2,10,1,1,\n        2,13,14,17,11,50,6,33,20,26,74,16,23,45,50,13,38,33,6,6,7,4,4,2,1,3,2,5,8,7,8,9,3,11,21,9,13,1,3,10,6,7,1,2,2,18,5,5,1,9,9,2,68,9,19,13,2,5,\n        1,4,4,7,4,13,3,9,10,21,17,3,26,2,1,5,2,4,5,4,1,7,4,7,3,4,2,1,6,1,1,20,4,1,9,2,2,1,3,3,2,3,2,1,1,1,20,2,3,1,6,2,3,6,2,4,8,1,3,2,10,3,5,3,4,4,\n        3,4,16,1,6,1,10,2,4,2,1,1,2,10,11,2,2,3,1,24,31,4,10,10,2,5,12,16,164,15,4,16,7,9,15,19,17,1,2,1,1,5,1,1,1,1,1,3,1,4,3,1,3,1,3,1,2,1,1,3,3,7,\n        2,8,1,2,2,2,1,3,4,3,7,8,12,92,2,10,3,1,3,14,5,25,16,42,4,7,7,4,2,21,5,27,26,27,21,25,30,31,2,1,5,13,3,22,5,6,6,11,9,12,1,5,9,7,5,5,22,60,3,5,\n        13,1,1,8,1,1,3,3,2,1,9,3,3,18,4,1,2,3,7,6,3,1,2,3,9,1,3,1,3,2,1,3,1,1,1,2,1,11,3,1,6,9,1,3,2,3,1,2,1,5,1,1,4,3,4,1,2,2,4,4,1,7,2,1,2,2,3,5,13,\n        18,3,4,14,9,9,4,16,3,7,5,8,2,6,48,28,3,1,1,4,2,14,8,2,9,2,1,15,2,4,3,2,10,16,12,8,7,1,1,3,1,1,1,2,7,4,1,6,4,38,39,16,23,7,15,15,3,2,12,7,21,\n        37,27,6,5,4,8,2,10,8,8,6,5,1,2,1,3,24,1,16,17,9,23,10,17,6,1,51,55,44,13,294,9,3,6,2,4,2,2,15,1,1,1,13,21,17,68,14,8,9,4,1,4,9,3,11,7,1,1,1,\n        5,6,3,2,1,1,1,2,3,8,1,2,2,4,1,5,5,2,1,4,3,7,13,4,1,4,1,3,1,1,1,5,5,10,1,6,1,5,2,1,5,2,4,1,4,5,7,3,18,2,9,11,32,4,3,3,2,4,7,11,16,9,11,8,13,38,\n        32,8,4,2,1,1,2,1,2,4,4,1,1,1,4,1,21,3,11,1,16,1,1,6,1,3,2,4,9,8,57,7,44,1,3,3,13,3,10,1,1,7,5,2,7,21,47,63,3,15,4,7,1,16,1,1,2,8,2,3,42,15,4,\n        1,29,7,22,10,3,78,16,12,20,18,4,67,11,5,1,3,15,6,21,31,32,27,18,13,71,35,5,142,4,10,1,2,50,19,33,16,35,37,16,19,27,7,1,133,19,1,4,8,7,20,1,4,\n        4,1,10,3,1,6,1,2,51,5,40,15,24,43,22928,11,1,13,154,70,3,1,1,7,4,10,1,2,1,1,2,1,2,1,2,2,1,1,2,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,\n        3,2,1,1,1,1,2,1,1,\n    };\n    static ImWchar base_ranges[] = // not zero-terminated\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana\n        0x31F0, 0x31FF, // Katakana Phonetic Extensions\n        0xFF00, 0xFFEF, // Half-width characters\n        0xFFFD, 0xFFFD  // Invalid\n    };\n    static ImWchar full_ranges[IM_COUNTOF(base_ranges) + IM_COUNTOF(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 };\n    if (!full_ranges[0])\n    {\n        memcpy(full_ranges, base_ranges, sizeof(base_ranges));\n        UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_COUNTOF(accumulative_offsets_from_0x4E00), full_ranges + IM_COUNTOF(base_ranges));\n    }\n    return &full_ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesCyrillic()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x0400, 0x052F, // Cyrillic + Cyrillic Supplement\n        0x2DE0, 0x2DFF, // Cyrillic Extended-A\n        0xA640, 0xA69F, // Cyrillic Extended-B\n        0,\n    };\n    return &ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesThai()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin\n        0x2010, 0x205E, // Punctuations\n        0x0E00, 0x0E7F, // Thai\n        0,\n    };\n    return &ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesVietnamese()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin\n        0x0102, 0x0103,\n        0x0110, 0x0111,\n        0x0128, 0x0129,\n        0x0168, 0x0169,\n        0x01A0, 0x01A1,\n        0x01AF, 0x01B0,\n        0x1EA0, 0x1EF9,\n        0,\n    };\n    return &ranges[0];\n}\n#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFontGlyphRangesBuilder\n//-----------------------------------------------------------------------------\n\nvoid ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end)\n{\n    if (text_end == NULL)\n        text_end = text + strlen(text);\n    while (text < text_end)\n    {\n        unsigned int c = 0;\n        int c_len = ImTextCharFromUtf8(&c, text, text_end);\n        text += c_len;\n        if (c_len == 0)\n            break;\n        AddChar((ImWchar)c);\n    }\n}\n\nvoid ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges)\n{\n    for (; ranges[0]; ranges += 2)\n        for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560\n            AddChar((ImWchar)c);\n}\n\nvoid ImFontGlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)\n{\n    const int max_codepoint = IM_UNICODE_CODEPOINT_MAX;\n    for (int n = 0; n <= max_codepoint; n++)\n        if (GetBit(n))\n        {\n            out_ranges->push_back((ImWchar)n);\n            while (n < max_codepoint && GetBit(n + 1))\n                n++;\n            out_ranges->push_back((ImWchar)n);\n        }\n    out_ranges->push_back(0);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFontBaked, ImFont\n//-----------------------------------------------------------------------------\n\nImFontBaked::ImFontBaked()\n{\n    memset((void*)this, 0, sizeof(*this));\n    FallbackGlyphIndex = -1;\n}\n\nvoid ImFontBaked::ClearOutputData()\n{\n    FallbackAdvanceX = 0.0f;\n    Glyphs.clear();\n    IndexAdvanceX.clear();\n    IndexLookup.clear();\n    FallbackGlyphIndex = -1;\n    Ascent = Descent = 0.0f;\n    MetricsTotalSurface = 0;\n}\n\nImFont::ImFont()\n{\n    memset((void*)this, 0, sizeof(*this));\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    Scale = 1.0f;\n#endif\n}\n\nImFont::~ImFont()\n{\n    ClearOutputData();\n}\n\nvoid ImFont::ClearOutputData()\n{\n    if (ImFontAtlas* atlas = OwnerAtlas)\n        ImFontAtlasFontDiscardBakes(atlas, this, 0);\n    //FallbackChar = EllipsisChar = 0;\n    memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap));\n    LastBaked = NULL;\n}\n\n// API is designed this way to avoid exposing the 8K page size\n// e.g. use with IsGlyphRangeUnused(0, 255)\nbool ImFont::IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last)\n{\n    unsigned int page_begin = (c_begin / 8192);\n    unsigned int page_last = (c_last / 8192);\n    for (unsigned int page_n = page_begin; page_n <= page_last; page_n++)\n        if ((page_n >> 3) < sizeof(Used8kPagesMap))\n            if (Used8kPagesMap[page_n >> 3] & (1 << (page_n & 7)))\n                return false;\n    return true;\n}\n\n// x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero.\n// Not to be mistaken with texture coordinates, which are held by u0/v0/u1/v1 in normalized format (0.0..1.0 on each texture axis).\n// - 'src' is not necessarily == 'this->Sources' because multiple source fonts+configs can be used to build one target font.\nImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph)\n{\n    int glyph_idx = baked->Glyphs.Size;\n    baked->Glyphs.push_back(*in_glyph);\n    ImFontGlyph* glyph = &baked->Glyphs[glyph_idx];\n    IM_ASSERT(baked->Glyphs.Size < 0xFFFE); // IndexLookup[] hold 16-bit values and -1/-2 are reserved.\n\n    // Set UV from packed rectangle\n    if (glyph->PackId != ImFontAtlasRectId_Invalid)\n    {\n        ImTextureRect* r = ImFontAtlasPackGetRect(atlas, glyph->PackId);\n        IM_ASSERT(glyph->U0 == 0.0f && glyph->V0 == 0.0f && glyph->U1 == 0.0f && glyph->V1 == 0.0f);\n        glyph->U0 = (r->x) * atlas->TexUvScale.x;\n        glyph->V0 = (r->y) * atlas->TexUvScale.y;\n        glyph->U1 = (r->x + r->w) * atlas->TexUvScale.x;\n        glyph->V1 = (r->y + r->h) * atlas->TexUvScale.y;\n        baked->MetricsTotalSurface += r->w * r->h;\n    }\n\n    if (src != NULL)\n    {\n        // Clamp & recenter if needed\n        const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;\n        const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;\n        float advance_x = ImClamp(glyph->AdvanceX, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale);\n        if (advance_x != glyph->AdvanceX)\n        {\n            float char_off_x = src->PixelSnapH ? ImTrunc((advance_x - glyph->AdvanceX) * 0.5f) : (advance_x - glyph->AdvanceX) * 0.5f;\n            glyph->X0 += char_off_x;\n            glyph->X1 += char_off_x;\n        }\n\n        // Snap to pixel\n        if (src->PixelSnapH)\n            advance_x = IM_ROUND(advance_x);\n\n        // Bake spacing\n        glyph->AdvanceX = advance_x + src->GlyphExtraAdvanceX;\n    }\n    if (glyph->Colored)\n        atlas->TexPixelsUseColors = atlas->TexData->UseColors = true;\n\n    // Update lookup tables\n    const int codepoint = glyph->Codepoint;\n    ImFontBaked_BuildGrowIndex(baked, codepoint + 1);\n    baked->IndexAdvanceX[codepoint] = glyph->AdvanceX;\n    baked->IndexLookup[codepoint] = (ImU16)glyph_idx;\n    const int page_n = codepoint / 8192;\n    baked->OwnerFont->Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7);\n\n    return glyph;\n}\n\n// FIXME: Code is duplicated with code above.\nvoid ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x)\n{\n    IM_UNUSED(atlas);\n    if (src != NULL)\n    {\n        // Clamp & recenter if needed\n        const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;\n        const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;\n        advance_x = ImClamp(advance_x, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale);\n\n        // Snap to pixel\n        if (src->PixelSnapH)\n            advance_x = IM_ROUND(advance_x);\n\n        // Bake spacing\n        advance_x += src->GlyphExtraAdvanceX;\n    }\n\n    ImFontBaked_BuildGrowIndex(baked, codepoint + 1);\n    baked->IndexAdvanceX[codepoint] = advance_x;\n}\n\n// Copy to texture, post-process and queue update for backend\nvoid ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch)\n{\n    ImTextureData* tex = atlas->TexData;\n    IM_ASSERT(r->x + r->w <= tex->Width && r->y + r->h <= tex->Height);\n    ImFontAtlasTextureBlockConvert(src_pixels, src_fmt, src_pitch, (unsigned char*)tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h);\n    ImFontAtlasPostProcessData pp_data = { atlas, baked->OwnerFont, src, baked, glyph, tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h };\n    ImFontAtlasTextureBlockPostProcess(&pp_data);\n    ImFontAtlasTextureBlockQueueUpload(atlas, tex, r->x, r->y, r->w, r->h);\n}\n\nvoid ImFont::AddRemapChar(ImWchar from_codepoint, ImWchar to_codepoint)\n{\n    RemapPairs.SetInt((ImGuiID)from_codepoint, (int)to_codepoint);\n}\n\n// Find glyph, load if necessary, return fallback if missing\nImFontGlyph* ImFontBaked::FindGlyph(ImWchar c)\n{\n    if (c < (size_t)IndexLookup.Size) IM_LIKELY\n    {\n        const int i = (int)IndexLookup.Data[c];\n        if (i == IM_FONTGLYPH_INDEX_NOT_FOUND)\n            return &Glyphs.Data[FallbackGlyphIndex];\n        if (i != IM_FONTGLYPH_INDEX_UNUSED)\n            return &Glyphs.Data[i];\n    }\n    ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL);\n    return glyph ? glyph : &Glyphs.Data[FallbackGlyphIndex];\n}\n\n// Attempt to load but when missing, return NULL instead of FallbackGlyph\nImFontGlyph* ImFontBaked::FindGlyphNoFallback(ImWchar c)\n{\n    if (c < (size_t)IndexLookup.Size) IM_LIKELY\n    {\n        const int i = (int)IndexLookup.Data[c];\n        if (i == IM_FONTGLYPH_INDEX_NOT_FOUND)\n            return NULL;\n        if (i != IM_FONTGLYPH_INDEX_UNUSED)\n            return &Glyphs.Data[i];\n    }\n    LoadNoFallback = true; // This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites.\n    ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL);\n    LoadNoFallback = false;\n    return glyph;\n}\n\nbool ImFontBaked::IsGlyphLoaded(ImWchar c)\n{\n    if (c < (size_t)IndexLookup.Size) IM_LIKELY\n    {\n        const int i = (int)IndexLookup.Data[c];\n        if (i == IM_FONTGLYPH_INDEX_NOT_FOUND)\n            return false;\n        if (i != IM_FONTGLYPH_INDEX_UNUSED)\n            return true;\n    }\n    return false;\n}\n\n// This is not fast query\nbool ImFont::IsGlyphInFont(ImWchar c)\n{\n    ImFontAtlas* atlas = OwnerAtlas;\n    ImFontAtlas_FontHookRemapCodepoint(atlas, this, &c);\n    for (ImFontConfig* src : Sources)\n    {\n        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;\n        if (loader->FontSrcContainsGlyph != NULL && loader->FontSrcContainsGlyph(atlas, src, c))\n            return true;\n    }\n    return false;\n}\n\n// This is manually inlined in CalcTextSizeA() and CalcWordWrapPosition(), with a non-inline call to BuildLoadGlyphGetAdvanceOrFallback().\nIM_MSVC_RUNTIME_CHECKS_OFF\nfloat ImFontBaked::GetCharAdvance(ImWchar c)\n{\n    if ((int)c < IndexAdvanceX.Size)\n    {\n        // Missing glyphs fitting inside index will have stored FallbackAdvanceX already.\n        const float x = IndexAdvanceX.Data[c];\n        if (x >= 0.0f)\n            return x;\n    }\n    return ImFontBaked_BuildLoadGlyphAdvanceX(this, c);\n}\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\nImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float rasterizer_density)\n{\n    struct { ImGuiID FontId; float BakedSize; float RasterizerDensity; } hashed_data;\n    hashed_data.FontId = font_id;\n    hashed_data.BakedSize = baked_size;\n    hashed_data.RasterizerDensity = rasterizer_density;\n    return ImHashData(&hashed_data, sizeof(hashed_data));\n}\n\n// ImFontBaked pointers are valid for the entire frame but shall never be kept between frames.\nImFontBaked* ImFont::GetFontBaked(float size, float density)\n{\n    ImFontBaked* baked = LastBaked;\n\n    // Round font size\n    // - ImGui::PushFont() will already round, but other paths calling GetFontBaked() directly also needs it (e.g. ImFontAtlasBuildPreloadAllGlyphRanges)\n    size = ImGui::GetRoundedFontSize(size);\n\n    if (density < 0.0f)\n        density = CurrentRasterizerDensity;\n    if (baked && baked->Size == size && baked->RasterizerDensity == density)\n        return baked;\n\n    ImFontAtlas* atlas = OwnerAtlas;\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    baked = ImFontAtlasBakedGetOrAdd(atlas, this, size, density);\n    if (baked == NULL)\n        return NULL;\n    baked->LastUsedFrame = builder->FrameCount;\n    LastBaked = baked;\n    return baked;\n}\n\nImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density)\n{\n    // FIXME-NEWATLAS: Design for picking a nearest size based on some criteria?\n    // FIXME-NEWATLAS: Altering font density won't work right away.\n    IM_ASSERT(font_size > 0.0f && font_rasterizer_density > 0.0f);\n    ImGuiID baked_id = ImFontAtlasBakedGetId(font->FontId, font_size, font_rasterizer_density);\n    ImFontAtlasBuilder* builder = atlas->Builder;\n    ImFontBaked** p_baked_in_map = (ImFontBaked**)builder->BakedMap.GetVoidPtrRef(baked_id);\n    ImFontBaked* baked = *p_baked_in_map;\n    if (baked != NULL)\n    {\n        IM_ASSERT(baked->Size == font_size && baked->OwnerFont == font && baked->BakedId == baked_id);\n        return baked;\n    }\n\n    // If atlas is locked, find closest match\n    // FIXME-OPT: This is not an optimal query.\n    if ((font->Flags & ImFontFlags_LockBakedSizes) || atlas->Locked)\n    {\n        baked = ImFontAtlasBakedGetClosestMatch(atlas, font, font_size, font_rasterizer_density);\n        if (baked != NULL)\n            return baked;\n        if (atlas->Locked)\n        {\n            IM_ASSERT(!atlas->Locked && \"Cannot use dynamic font size with a locked ImFontAtlas!\"); // Locked because rendering backend does not support ImGuiBackendFlags_RendererHasTextures!\n            return NULL;\n        }\n    }\n\n    // Create new\n    baked = ImFontAtlasBakedAdd(atlas, font, font_size, font_rasterizer_density, baked_id);\n    *p_baked_in_map = baked; // To avoid 'builder->BakedMap.SetVoidPtr(baked_id, baked);' while we can.\n    return baked;\n}\n\n// Trim trailing space and find beginning of next line\nconst char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags)\n{\n    if ((flags & ImDrawTextFlags_WrapKeepBlanks) == 0)\n        while (text < text_end && ImCharIsBlankA(*text))\n            text++;\n    if (text < text_end && *text == '\\n')\n        text++;\n    return text;\n}\n\nvoid ImTextClassifierClear(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class)\n{\n    for (unsigned int c = codepoint_min; c < codepoint_end; c++)\n        ImTextClassifierSetCharClass(bits, codepoint_min, codepoint_end, char_class, c);\n}\n\nvoid ImTextClassifierSetCharClass(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, unsigned int c)\n{\n    IM_ASSERT(c >= codepoint_min && c < codepoint_end);\n    IM_UNUSED(codepoint_end);\n    c -= codepoint_min;\n    const ImU32 shift = (c & 15) << 1;\n    bits[c >> 4] = (bits[c >> 4] & ~(0x03 << shift)) | (char_class << shift);\n}\n\nvoid ImTextClassifierSetCharClassFromStr(ImU32* bits, unsigned int codepoint_min, unsigned int codepoint_end, ImWcharClass char_class, const char* s)\n{\n    const char* s_end = s + strlen(s);\n    while (*s)\n    {\n        unsigned int c;\n        s += ImTextCharFromUtf8(&c, s, s_end);\n        ImTextClassifierSetCharClass(bits, codepoint_min, codepoint_end, char_class, c);\n    }\n}\n\n#define ImTextClassifierGet(_BITS, _CHAR_OFFSET)    ((_BITS[(_CHAR_OFFSET) >> 4] >> (((_CHAR_OFFSET) & 15) << 1)) & 0x03)\n\n// 2-bit per character\nstatic ImU32 g_CharClassifierIsSeparator_0000_007f[128 / 16] = {};\nstatic ImU32 g_CharClassifierIsSeparator_3000_300f[ 16 / 16] = {};\n\nvoid ImTextInitClassifiers()\n{\n    if (ImTextClassifierGet(g_CharClassifierIsSeparator_0000_007f, ',') != 0)\n        return;\n\n    // List of hardcoded separators: .,;!?'\"\n    // Making this dynamic given known ranges is trivial BUT requires us to standardize where you pass them as parameters. (#3002, #8503)\n    ImTextClassifierClear(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Other);\n    ImTextClassifierSetCharClassFromStr(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Blank, \" \\t\");\n    ImTextClassifierSetCharClassFromStr(g_CharClassifierIsSeparator_0000_007f, 0, 128, ImWcharClass_Punct, \".,;!?\\\"\");\n\n    ImTextClassifierClear(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Other);\n    ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Blank, 0x3000);\n    ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Punct, 0x3001);\n    ImTextClassifierSetCharClass(g_CharClassifierIsSeparator_3000_300f, 0x3000, 0x300F, ImWcharClass_Punct, 0x3002);\n}\n\n// Simple word-wrapping for English, not full-featured. Please submit failing cases!\n// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.\n// Refer to imgui_test_suite's \"drawlist_text_wordwrap_1\" for tests.\nconst char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags)\n{\n    // For references, possible wrap point marked with ^\n    //  \"aaa bbb, ccc,ddd. eee   fff. ggg!\"\n    //      ^    ^    ^   ^   ^__    ^    ^\n\n    // Skip extra blanks after a line returns (that includes not counting them in width computation)\n    // e.g. \"Hello    world\" --> \"Hello\" \"World\"\n\n    // Cut words that cannot possibly fit within one line.\n    // e.g.: \"The tropical fish\" with ~5 characters worth of width --> \"The tr\" \"opical\" \"fish\"\n\n    ImFontBaked* baked = font->GetFontBaked(size);\n    const float scale = size / baked->Size;\n\n    float line_width = 0.0f;\n    float blank_width = 0.0f;\n    wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters\n\n    const char* s = text;\n    IM_ASSERT(text_end != NULL);\n\n    int prev_type = ImWcharClass_Other;\n    const bool keep_blanks = (flags & ImDrawTextFlags_WrapKeepBlanks) != 0;\n\n    // Find next wrapping point\n    //const char* span_begin = s;\n    const char* span_end = s;\n    float span_width = 0.0f;\n\n    while (s < text_end)\n    {\n        unsigned int c = (unsigned int)*s;\n        const char* next_s;\n        if (c < 0x80)\n            next_s = s + 1;\n        else\n            next_s = s + ImTextCharFromUtf8(&c, s, text_end);\n\n        if (c < 32)\n        {\n            if (c == '\\n')\n                return s; // Direct return, skip \"Wrap_width is too small to fit anything\" path.\n            if (c == '\\r')\n            {\n                s = next_s; // Fast-skip\n                continue;\n            }\n        }\n\n        // Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'\n        float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f;\n        if (char_width < 0.0f)\n            char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c);\n\n        // Classify current character\n        int curr_type;\n        if (c < 128)\n            curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_0000_007f, c);\n        else if (c >= 0x3000 && c < 0x3010)\n            curr_type = ImTextClassifierGet(g_CharClassifierIsSeparator_3000_300f, c & 15); //-V578\n        else\n            curr_type = ImWcharClass_Other;\n\n        if (curr_type == ImWcharClass_Blank)\n        {\n            // End span: 'A ' or '. '\n            if (prev_type != ImWcharClass_Blank && !keep_blanks)\n            {\n                span_end = s;\n                line_width += span_width;\n                span_width = 0.0f;\n            }\n            blank_width += char_width;\n        }\n        else\n        {\n            // End span: '.X' unless X is a digit\n            if (prev_type == ImWcharClass_Punct && curr_type != ImWcharClass_Punct && !(c >= '0' && c <= '9')) // FIXME: Digit checks might be removed if allow custom separators (#8503)\n            {\n                span_end = s;\n                line_width += span_width + blank_width;\n                span_width = blank_width = 0.0f;\n            }\n            // End span: 'A ' or '. '\n            else if (prev_type == ImWcharClass_Blank && keep_blanks)\n            {\n                span_end = s;\n                line_width += span_width + blank_width;\n                span_width = blank_width = 0.0f;\n            }\n            span_width += char_width;\n        }\n\n        if (span_width + blank_width + line_width > wrap_width)\n        {\n            if (span_width + blank_width > wrap_width)\n                break;\n            // FIXME: Narrow wrapping e.g. \"A quick brown\" -> \"Quic|k br|own\", would require knowing if span is going to be longer than wrap_width.\n            //if (span_width > wrap_width && !is_blank && !was_blank)\n            //    return s;\n            return span_end;\n        }\n\n        prev_type = curr_type;\n        s = next_s;\n    }\n\n    // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.\n    // +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).\n    if (s == text && text < text_end)\n        return s + ImTextCountUtf8BytesFromChar(s, text_end);\n    return s;\n}\n\nconst char* ImFont::CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width)\n{\n    return ImFontCalcWordWrapPositionEx(this, size, text, text_end, wrap_width, ImDrawTextFlags_None);\n}\n\nImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)\n{\n    if (!text_end)\n        text_end = text_begin + ImStrlen(text_begin); // FIXME-OPT: Need to avoid this.\n    if (!text_end_display)\n        text_end_display = text_end;\n\n    ImFontBaked* baked = font->GetFontBaked(size);\n    const float line_height = size;\n    const float scale = line_height / baked->Size;\n\n    ImVec2 text_size = ImVec2(0, 0);\n    float line_width = 0.0f;\n\n    const bool word_wrap_enabled = (wrap_width > 0.0f);\n    const char* word_wrap_eol = NULL;\n\n    const char* s = text_begin;\n    while (s < text_end_display)\n    {\n        // Word-wrapping\n        if (word_wrap_enabled)\n        {\n            // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.\n            if (!word_wrap_eol)\n                word_wrap_eol = ImFontCalcWordWrapPositionEx(font, size, s, text_end, wrap_width - line_width, flags);\n\n            if (s >= word_wrap_eol)\n            {\n                if (text_size.x < line_width)\n                    text_size.x = line_width;\n                text_size.y += line_height;\n                line_width = 0.0f;\n                s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks\n                if (flags & ImDrawTextFlags_StopOnNewLine)\n                    break;\n                word_wrap_eol = NULL;\n                continue;\n            }\n        }\n\n        // Decode and advance source\n        const char* prev_s = s;\n        unsigned int c = (unsigned int)*s;\n        if (c < 0x80)\n            s += 1;\n        else\n            s += ImTextCharFromUtf8(&c, s, text_end);\n\n        if (c == '\\n')\n        {\n            text_size.x = ImMax(text_size.x, line_width);\n            text_size.y += line_height;\n            line_width = 0.0f;\n            if (flags & ImDrawTextFlags_StopOnNewLine)\n                break;\n            continue;\n        }\n        if (c == '\\r')\n            continue;\n\n        // Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'\n        float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f;\n        if (char_width < 0.0f)\n            char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c);\n        char_width *= scale;\n\n        if (line_width + char_width >= max_width)\n        {\n            s = prev_s;\n            break;\n        }\n\n        line_width += char_width;\n    }\n\n    if (text_size.x < line_width)\n        text_size.x = line_width;\n\n    if (out_offset != NULL)\n        *out_offset = ImVec2(line_width, text_size.y + line_height);  // offset allow for the possibility of sitting after a trailing \\n\n\n    if (line_width > 0 || text_size.y == 0.0f)                        // whereas size.y will ignore the trailing \\n\n        text_size.y += line_height;\n\n    if (out_remaining != NULL)\n        *out_remaining = s;\n\n    return text_size;\n}\n\nImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** out_remaining)\n{\n    return ImFontCalcTextSizeEx(this, size, max_width, wrap_width, text_begin, text_end, text_end, out_remaining, NULL, ImDrawTextFlags_None);\n}\n\n// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.\nvoid ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip)\n{\n    ImFontBaked* baked = GetFontBaked(size);\n    const ImFontGlyph* glyph = baked->FindGlyph(c);\n    if (!glyph || !glyph->Visible)\n        return;\n    if (glyph->Colored)\n        col |= ~IM_COL32_A_MASK;\n    float scale = (size >= 0.0f) ? (size / baked->Size) : 1.0f;\n    float x = IM_TRUNC(pos.x);\n    float y = IM_TRUNC(pos.y);\n\n    float x1 = x + glyph->X0 * scale;\n    float x2 = x + glyph->X1 * scale;\n    if (cpu_fine_clip && (x1 > cpu_fine_clip->z || x2 < cpu_fine_clip->x))\n        return;\n    float y1 = y + glyph->Y0 * scale;\n    float y2 = y + glyph->Y1 * scale;\n    float u1 = glyph->U0;\n    float v1 = glyph->V0;\n    float u2 = glyph->U1;\n    float v2 = glyph->V1;\n\n    // Always CPU fine clip. Code extracted from RenderText().\n    // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads.\n    if (cpu_fine_clip != NULL)\n    {\n        if (x1 < cpu_fine_clip->x) { u1 = u1 + (1.0f - (x2 - cpu_fine_clip->x) / (x2 - x1)) * (u2 - u1); x1 = cpu_fine_clip->x; }\n        if (y1 < cpu_fine_clip->y) { v1 = v1 + (1.0f - (y2 - cpu_fine_clip->y) / (y2 - y1)) * (v2 - v1); y1 = cpu_fine_clip->y; }\n        if (x2 > cpu_fine_clip->z) { u2 = u1 + ((cpu_fine_clip->z - x1) / (x2 - x1)) * (u2 - u1); x2 = cpu_fine_clip->z; }\n        if (y2 > cpu_fine_clip->w) { v2 = v1 + ((cpu_fine_clip->w - y1) / (y2 - y1)) * (v2 - v1); y2 = cpu_fine_clip->w; }\n        if (y1 >= y2)\n            return;\n    }\n    draw_list->PrimReserve(6, 4);\n    draw_list->PrimRectUV(ImVec2(x1, y1), ImVec2(x2, y2), ImVec2(u1, v1), ImVec2(u2, v2), col);\n}\n\n// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.\n// DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2026. Use ImDrawList::AddText().\nvoid ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags)\n{\n    // Align to be pixel perfect\nbegin:\n    float x = IM_TRUNC(pos.x);\n    float y = IM_TRUNC(pos.y);\n    if (y > clip_rect.w)\n        return;\n\n    if (!text_end)\n        text_end = text_begin + ImStrlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.\n\n    const float line_height = size;\n    ImFontBaked* baked = GetFontBaked(size);\n\n    const float scale = size / baked->Size;\n    const float origin_x = x;\n    const bool word_wrap_enabled = (wrap_width > 0.0f);\n\n    // Fast-forward to first visible line\n    const char* s = text_begin;\n    if (y + line_height < clip_rect.y)\n        while (y + line_height < clip_rect.y && s < text_end)\n        {\n            const char* line_end = (const char*)ImMemchr(s, '\\n', text_end - s);\n            if (word_wrap_enabled)\n            {\n                // FIXME-OPT: This is not optimal as do first do a search for \\n before calling CalcWordWrapPosition().\n                // If the specs for CalcWordWrapPosition() were reworked to optionally return on \\n we could combine both.\n                // However it is still better than nothing performing the fast-forward!\n                s = ImFontCalcWordWrapPositionEx(this, size, s, line_end ? line_end : text_end, wrap_width, flags);\n                s = ImTextCalcWordWrapNextLineStart(s, text_end, flags);\n            }\n            else\n            {\n                s = line_end ? line_end + 1 : text_end;\n            }\n            y += line_height;\n        }\n\n    // For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve()\n    // Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer without a newline will likely crash atm)\n    if (text_end - s > 10000 && !word_wrap_enabled)\n    {\n        const char* s_end = s;\n        float y_end = y;\n        while (y_end < clip_rect.w && s_end < text_end)\n        {\n            s_end = (const char*)ImMemchr(s_end, '\\n', text_end - s_end);\n            s_end = s_end ? s_end + 1 : text_end;\n            y_end += line_height;\n        }\n        text_end = s_end;\n    }\n    if (s == text_end)\n        return;\n\n    // IMHEX PATCH BEGIN\n    bool is_subpixel = false;\n    if (!this->Sources.empty()) {\n        const int flags = this->Sources[0]->FontLoaderFlags;\n        is_subpixel = (flags & ImGuiFreeTypeLoaderFlags_SubPixel) != 0;\n    }\n\n    void ImGui_ImplOpenGL3_TurnFontShadersOn(const ImDrawList *parent_list, const ImDrawCmd *cmd);\n    void ImGui_ImplOpenGL3_TurnFontShadersOff(const ImDrawList *parent_list, const ImDrawCmd *cmd);\n\n    if (is_subpixel) {\n        draw_list->AddCallback(ImGui_ImplOpenGL3_TurnFontShadersOn, NULL);\n        draw_list->AddCallback(ImDrawCallback_ResetRenderState, NULL);\n    }\n    // IMHEX PATCH END\n\n    // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized)\n    const int vtx_count_max = (int)(text_end - s) * 4;\n    const int idx_count_max = (int)(text_end - s) * 6;\n    const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max;\n    draw_list->PrimReserve(idx_count_max, vtx_count_max);\n    ImDrawVert*  vtx_write = draw_list->_VtxWritePtr;\n    ImDrawIdx*   idx_write = draw_list->_IdxWritePtr;\n    unsigned int vtx_index = draw_list->_VtxCurrentIdx;\n    const int cmd_count = draw_list->CmdBuffer.Size;\n    const bool cpu_fine_clip = (flags & ImDrawTextFlags_CpuFineClip) != 0;\n\n    const ImU32 col_untinted = col | ~IM_COL32_A_MASK;\n    const char* word_wrap_eol = NULL;\n\n    while (s < text_end)\n    {\n        if (word_wrap_enabled)\n        {\n            // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.\n            if (!word_wrap_eol)\n                word_wrap_eol = ImFontCalcWordWrapPositionEx(this, size, s, text_end, wrap_width - (x - origin_x), flags);\n\n            if (s >= word_wrap_eol)\n            {\n                x = origin_x;\n                y += line_height;\n                if (y > clip_rect.w)\n                    break; // break out of main loop\n                word_wrap_eol = NULL;\n                s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks\n                continue;\n            }\n        }\n\n        // Decode and advance source\n        unsigned int c = (unsigned int)*s;\n        if (c < 0x80)\n            s += 1;\n        else\n            s += ImTextCharFromUtf8(&c, s, text_end);\n\n        if (c < 32)\n        {\n            if (c == '\\n')\n            {\n                x = origin_x;\n                y += line_height;\n                if (y > clip_rect.w)\n                    break; // break out of main loop\n                continue;\n            }\n            if (c == '\\r')\n                continue;\n        }\n\n        const ImFontGlyph* glyph = baked->FindGlyph((ImWchar)c);\n        //if (glyph == NULL)\n        //    continue;\n\n        float char_width = glyph->AdvanceX * scale;\n        if (glyph->Visible)\n        {\n            // We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w\n            float x1 = x + glyph->X0 * scale;\n            float x2 = x + glyph->X1 * scale;\n            float y1 = y + glyph->Y0 * scale;\n            float y2 = y + glyph->Y1 * scale;\n            if (x1 <= clip_rect.z && x2 >= clip_rect.x)\n            {\n                // Render a character\n                float u1 = glyph->U0;\n                float v1 = glyph->V0;\n                float u2 = glyph->U1;\n                float v2 = glyph->V1;\n\n                // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads.\n                if (cpu_fine_clip)\n                {\n                    if (x1 < clip_rect.x)\n                    {\n                        u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1);\n                        x1 = clip_rect.x;\n                    }\n                    if (y1 < clip_rect.y)\n                    {\n                        v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1);\n                        y1 = clip_rect.y;\n                    }\n                    if (x2 > clip_rect.z)\n                    {\n                        u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1);\n                        x2 = clip_rect.z;\n                    }\n                    if (y2 > clip_rect.w)\n                    {\n                        v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1);\n                        y2 = clip_rect.w;\n                    }\n                    if (y1 >= y2)\n                    {\n                        x += char_width;\n                        continue;\n                    }\n                }\n\n                // Support for untinted glyphs\n                ImU32 glyph_col = glyph->Colored ? col_untinted : col;\n\n                // We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:\n                {\n                    vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1;\n                    vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1;\n                    vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2;\n                    vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = glyph_col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2;\n                    idx_write[0] = (ImDrawIdx)(vtx_index); idx_write[1] = (ImDrawIdx)(vtx_index + 1); idx_write[2] = (ImDrawIdx)(vtx_index + 2);\n                    idx_write[3] = (ImDrawIdx)(vtx_index); idx_write[4] = (ImDrawIdx)(vtx_index + 2); idx_write[5] = (ImDrawIdx)(vtx_index + 3);\n                    vtx_write += 4;\n                    vtx_index += 4;\n                    idx_write += 6;\n                }\n            }\n        }\n        x += char_width;\n    }\n\n    // Edge case: calling RenderText() with unloaded glyphs triggering texture change. It doesn't happen via ImGui:: calls because CalcTextSize() is always used.\n    if (cmd_count != draw_list->CmdBuffer.Size) //-V547\n    {\n        IM_ASSERT(draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount == 0);\n        draw_list->CmdBuffer.pop_back();\n        draw_list->PrimUnreserve(idx_count_max, vtx_count_max);\n        draw_list->AddDrawCmd();\n        //IMGUI_DEBUG_LOG(\"RenderText: cancel and retry to missing glyphs.\\n\"); // [DEBUG]\n        //draw_list->AddRectFilled(pos, pos + ImVec2(10, 10), IM_COL32(255, 0, 0, 255)); // [DEBUG]\n        goto begin;\n        //RenderText(draw_list, size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip); // FIXME-OPT: Would a 'goto begin' be better for code-gen?\n        //return;\n    }\n\n    // Give back unused vertices (clipped ones, blanks) ~ this is essentially a PrimUnreserve() action.\n    draw_list->VtxBuffer.Size = (int)(vtx_write - draw_list->VtxBuffer.Data); // Same as calling shrink()\n    draw_list->IdxBuffer.Size = (int)(idx_write - draw_list->IdxBuffer.Data);\n    draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size);\n    draw_list->_VtxWritePtr = vtx_write;\n    draw_list->_IdxWritePtr = idx_write;\n    draw_list->_VtxCurrentIdx = vtx_index;\n\n    // IMHEX PATCH BEGIN\n    if (is_subpixel) {\n        draw_list->AddCallback(ImGui_ImplOpenGL3_TurnFontShadersOff, NULL);\n        draw_list->AddCallback(ImDrawCallback_ResetRenderState, NULL);\n    }\n    // IMHEX PATCH END\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGui Internal Render Helpers\n//-----------------------------------------------------------------------------\n// Vaguely redesigned to stop accessing ImGui global state:\n// - RenderArrow()\n// - RenderBullet()\n// - RenderCheckMark()\n// - RenderArrowDockMenu()\n// - RenderArrowPointingAt()\n// - RenderRectFilledInRangeH()\n// - RenderRectFilledWithHole()\n//-----------------------------------------------------------------------------\n// Function in need of a redesign (legacy mess)\n// - RenderColorRectWithAlphaCheckerboard()\n//-----------------------------------------------------------------------------\n\n// Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state\nvoid ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale)\n{\n    const float h = draw_list->_Data->FontSize * 1.00f;\n    float r = h * 0.40f * scale;\n    ImVec2 center = pos + ImVec2(h * 0.50f, h * 0.50f * scale);\n\n    ImVec2 a, b, c;\n    switch (dir)\n    {\n    case ImGuiDir_Up:\n    case ImGuiDir_Down:\n        if (dir == ImGuiDir_Up) r = -r;\n        a = ImVec2(+0.000f, +0.750f) * r;\n        b = ImVec2(-0.866f, -0.750f) * r;\n        c = ImVec2(+0.866f, -0.750f) * r;\n        break;\n    case ImGuiDir_Left:\n    case ImGuiDir_Right:\n        if (dir == ImGuiDir_Left) r = -r;\n        a = ImVec2(+0.750f, +0.000f) * r;\n        b = ImVec2(-0.750f, +0.866f) * r;\n        c = ImVec2(-0.750f, -0.866f) * r;\n        break;\n    case ImGuiDir_None:\n    case ImGuiDir_COUNT:\n        IM_ASSERT(0);\n        break;\n    }\n    draw_list->AddTriangleFilled(center + a, center + b, center + c, col);\n}\n\nvoid ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)\n{\n    // FIXME-OPT: This should be baked in font now that it's easier.\n    float font_size = draw_list->_Data->FontSize;\n    draw_list->AddCircleFilled(pos, font_size * 0.20f, col, (font_size < 22) ? 8 : (font_size < 40) ? 12 : 0); // Hardcode optimal/nice tessellation threshold\n}\n\nvoid ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz)\n{\n    float thickness = ImMax(sz / 5.0f, 1.0f);\n    sz -= thickness * 0.5f;\n    pos += ImVec2(thickness * 0.25f, thickness * 0.25f);\n\n    float third = sz / 3.0f;\n    float bx = pos.x + third;\n    float by = pos.y + sz - third * 0.5f;\n    draw_list->PathLineTo(ImVec2(bx - third, by - third));\n    draw_list->PathLineTo(ImVec2(bx, by));\n    draw_list->PathLineTo(ImVec2(bx + third * 2.0f, by - third * 2.0f));\n    draw_list->PathStroke(col, 0, thickness);\n}\n\n// Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side.\nvoid ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col)\n{\n    switch (direction)\n    {\n    case ImGuiDir_Left:  draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return;\n    case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return;\n    case ImGuiDir_Up:    draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return;\n    case ImGuiDir_Down:  draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return;\n    case ImGuiDir_None: case ImGuiDir_COUNT: break; // Fix warnings\n    }\n}\n\n// This is less wide than RenderArrow() and we use in dock nodes instead of the regular RenderArrow() to denote a change of functionality,\n// and because the saved space means that the left-most tab label can stay at exactly the same position as the label of a loose window.\nvoid ImGui::RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col)\n{\n    draw_list->AddRectFilled(p_min + ImVec2(sz * 0.20f, sz * 0.15f), p_min + ImVec2(sz * 0.80f, sz * 0.30f), col);\n    RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.50f, sz * 0.85f), ImVec2(sz * 0.30f, sz * 0.40f), ImGuiDir_Down, col);\n}\n\nstatic inline float ImAcos01(float x)\n{\n    if (x <= 0.0f) return IM_PI * 0.5f;\n    if (x >= 1.0f) return 0.0f;\n    return ImAcos(x);\n    //return (-0.69813170079773212f * x * x - 0.87266462599716477f) * x + 1.5707963267948966f; // Cheap approximation, may be enough for what we do.\n}\n\n// FIXME: Cleanup and move code to ImDrawList.\n// - Before 2025-12-04: RenderRectFilledRangeH()   with 'float x_start_norm, float x_end_norm` <- normalized\n// - After  2025-12-04: RenderRectFilledInRangeH() with 'float x1, float x2'                   <- absolute coords!!\nvoid ImGui::RenderRectFilledInRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float fill_x0, float fill_x1, float rounding)\n{\n    if (fill_x0 > fill_x1)\n        return;\n\n    ImVec2 p0 = ImVec2(fill_x0, rect.Min.y);\n    ImVec2 p1 = ImVec2(fill_x1, rect.Max.y);\n    if (rounding == 0.0f)\n    {\n        draw_list->AddRectFilled(p0, p1, col, 0.0f);\n        return;\n    }\n\n    rounding = ImClamp(ImMin((rect.Max.x - rect.Min.x) * 0.5f, (rect.Max.y - rect.Min.y) * 0.5f) - 1.0f, 0.0f, rounding);\n    const float inv_rounding = 1.0f / rounding;\n    const float arc0_b = ImAcos01(1.0f - (p0.x - rect.Min.x) * inv_rounding);\n    const float arc0_e = ImAcos01(1.0f - (p1.x - rect.Min.x) * inv_rounding);\n    const float half_pi = IM_PI * 0.5f; // We will == compare to this because we know this is the exact value ImAcos01 can return.\n    const float x0 = ImMax(p0.x, rect.Min.x + rounding);\n    if (arc0_b == arc0_e)\n    {\n        draw_list->PathLineTo(ImVec2(x0, p1.y));\n        draw_list->PathLineTo(ImVec2(x0, p0.y));\n    }\n    else if (arc0_b == 0.0f && arc0_e == half_pi)\n    {\n        draw_list->PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3, 6); // BL\n        draw_list->PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6, 9); // TR\n    }\n    else\n    {\n        draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b); // BL\n        draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e); // TR\n    }\n    if (p1.x > rect.Min.x + rounding)\n    {\n        const float arc1_b = ImAcos01(1.0f - (rect.Max.x - p1.x) * inv_rounding);\n        const float arc1_e = ImAcos01(1.0f - (rect.Max.x - p0.x) * inv_rounding);\n        const float x1 = ImMin(p1.x, rect.Max.x - rounding);\n        if (arc1_b == arc1_e)\n        {\n            draw_list->PathLineTo(ImVec2(x1, p0.y));\n            draw_list->PathLineTo(ImVec2(x1, p1.y));\n        }\n        else if (arc1_b == 0.0f && arc1_e == half_pi)\n        {\n            draw_list->PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9, 12); // TR\n            draw_list->PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0, 3);  // BR\n        }\n        else\n        {\n            draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b); // TR\n            draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e); // BR\n        }\n    }\n    draw_list->PathFillConvex(col);\n}\n\nvoid ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding)\n{\n    const bool fill_L = (inner.Min.x > outer.Min.x);\n    const bool fill_R = (inner.Max.x < outer.Max.x);\n    const bool fill_U = (inner.Min.y > outer.Min.y);\n    const bool fill_D = (inner.Max.y < outer.Max.y);\n    if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft)    | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft));\n    if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight)   | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight));\n    if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft)    | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight));\n    if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight));\n    if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopLeft);\n    if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopRight);\n    if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomLeft);\n    if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight);\n}\n\nImDrawFlags ImGui::CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold)\n{\n    bool round_l = r_in.Min.x <= r_outer.Min.x + threshold;\n    bool round_r = r_in.Max.x >= r_outer.Max.x - threshold;\n    bool round_t = r_in.Min.y <= r_outer.Min.y + threshold;\n    bool round_b = r_in.Max.y >= r_outer.Max.y - threshold;\n    return ImDrawFlags_RoundCornersNone\n        | ((round_t && round_l) ? ImDrawFlags_RoundCornersTopLeft : 0) | ((round_t && round_r) ? ImDrawFlags_RoundCornersTopRight : 0)\n        | ((round_b && round_l) ? ImDrawFlags_RoundCornersBottomLeft : 0) | ((round_b && round_r) ? ImDrawFlags_RoundCornersBottomRight : 0);\n}\n\n// Helper for ColorPicker4()\n// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.\n// Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether.\n// FIXME: uses ImGui::GetColorU32\nvoid ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, ImDrawFlags flags)\n{\n    if ((flags & ImDrawFlags_RoundCornersMask_) == 0)\n        flags = ImDrawFlags_RoundCornersDefault_;\n    if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF)\n    {\n        ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col));\n        ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col));\n        draw_list->AddRectFilled(p_min, p_max, col_bg1, rounding, flags);\n\n        int yi = 0;\n        for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++)\n        {\n            float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y);\n            if (y2 <= y1)\n                continue;\n            for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)\n            {\n                float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x);\n                if (x2 <= x1)\n                    continue;\n                ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone;\n                if (y1 <= p_min.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersTopLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersTopRight; }\n                if (y2 >= p_max.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersBottomLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersBottomRight; }\n\n                // Combine flags\n                cell_flags = (flags == ImDrawFlags_RoundCornersNone || cell_flags == ImDrawFlags_RoundCornersNone) ? ImDrawFlags_RoundCornersNone : (cell_flags & flags);\n                draw_list->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), col_bg2, rounding, cell_flags);\n            }\n        }\n    }\n    else\n    {\n        draw_list->AddRectFilled(p_min, p_max, col, rounding, flags);\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Decompression code\n//-----------------------------------------------------------------------------\n// Compressed with stb_compress() then converted to a C array and encoded as base85.\n// Use the program in misc/fonts/binary_to_compressed_c.cpp to create the array from a TTF file.\n// The purpose of encoding as base85 instead of \"0x00,0x01,...\" style is only save on _source code_ size.\n// Decompression from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h\n//-----------------------------------------------------------------------------\n\nstatic unsigned int stb_decompress_length(const unsigned char *input)\n{\n    return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11];\n}\n\nstatic unsigned char *stb__barrier_out_e, *stb__barrier_out_b;\nstatic const unsigned char *stb__barrier_in_b;\nstatic unsigned char *stb__dout;\nstatic void stb__match(const unsigned char *data, unsigned int length)\n{\n    // INVERSE of memmove... write each byte before copying the next...\n    IM_ASSERT(stb__dout + length <= stb__barrier_out_e);\n    if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; }\n    if (data < stb__barrier_out_b) { stb__dout = stb__barrier_out_e+1; return; }\n    while (length--) *stb__dout++ = *data++;\n}\n\nstatic void stb__lit(const unsigned char *data, unsigned int length)\n{\n    IM_ASSERT(stb__dout + length <= stb__barrier_out_e);\n    if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; }\n    if (data < stb__barrier_in_b) { stb__dout = stb__barrier_out_e+1; return; }\n    memcpy(stb__dout, data, length);\n    stb__dout += length;\n}\n\n#define stb__in2(x)   ((i[x] << 8) + i[(x)+1])\n#define stb__in3(x)   ((i[x] << 16) + stb__in2((x)+1))\n#define stb__in4(x)   ((i[x] << 24) + stb__in3((x)+1))\n\nstatic const unsigned char *stb_decompress_token(const unsigned char *i)\n{\n    if (*i >= 0x20) { // use fewer if's for cases that expand small\n        if (*i >= 0x80)       stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2;\n        else if (*i >= 0x40)  stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3;\n        else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1);\n    } else { // more ifs for cases that expand large, since overhead is amortized\n        if (*i >= 0x18)       stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4;\n        else if (*i >= 0x10)  stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5;\n        else if (*i >= 0x08)  stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1);\n        else if (*i == 0x07)  stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1);\n        else if (*i == 0x06)  stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5;\n        else if (*i == 0x04)  stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6;\n    }\n    return i;\n}\n\nstatic unsigned int stb_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen)\n{\n    const unsigned long ADLER_MOD = 65521;\n    unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16;\n    unsigned long blocklen = buflen % 5552;\n\n    unsigned long i;\n    while (buflen) {\n        for (i=0; i + 7 < blocklen; i += 8) {\n            s1 += buffer[0], s2 += s1;\n            s1 += buffer[1], s2 += s1;\n            s1 += buffer[2], s2 += s1;\n            s1 += buffer[3], s2 += s1;\n            s1 += buffer[4], s2 += s1;\n            s1 += buffer[5], s2 += s1;\n            s1 += buffer[6], s2 += s1;\n            s1 += buffer[7], s2 += s1;\n\n            buffer += 8;\n        }\n\n        for (; i < blocklen; ++i)\n            s1 += *buffer++, s2 += s1;\n\n        s1 %= ADLER_MOD, s2 %= ADLER_MOD;\n        buflen -= blocklen;\n        blocklen = 5552;\n    }\n    return (unsigned int)(s2 << 16) + (unsigned int)s1;\n}\n\nstatic unsigned int stb_decompress(unsigned char *output, const unsigned char *i, unsigned int /*length*/)\n{\n    if (stb__in4(0) != 0x57bC0000) return 0;\n    if (stb__in4(4) != 0)          return 0; // error! stream is > 4GB\n    const unsigned int olen = stb_decompress_length(i);\n    stb__barrier_in_b = i;\n    stb__barrier_out_e = output + olen;\n    stb__barrier_out_b = output;\n    i += 16;\n\n    stb__dout = output;\n    for (;;) {\n        const unsigned char *old_i = i;\n        i = stb_decompress_token(i);\n        if (i == old_i) {\n            if (*i == 0x05 && i[1] == 0xfa) {\n                IM_ASSERT(stb__dout == output + olen);\n                if (stb__dout != output + olen) return 0;\n                if (stb_adler32(1, output, olen) != (unsigned int) stb__in4(2))\n                    return 0;\n                return olen;\n            } else {\n                IM_ASSERT(0); /* NOTREACHED */\n                return 0;\n            }\n        }\n        IM_ASSERT(stb__dout <= output + olen);\n        if (stb__dout > output + olen)\n            return 0;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Default font data (ProggyClean.ttf)\n//-----------------------------------------------------------------------------\n// MIT License / Copyright (c) 2004, 2005 Tristan Grimmer\n// Download and more information at https://github.com/bluescan/proggyfonts\n//-----------------------------------------------------------------------------\n\n#ifndef IMGUI_DISABLE_DEFAULT_FONT\n\n// File: 'ProggyClean.ttf' (41208 bytes)\n// Exported using binary_to_compressed_c.exe -u8 \"ProggyClean.ttf\" proggy_clean_ttf\nstatic const unsigned int proggy_clean_ttf_compressed_size = 9583;\nstatic const unsigned char proggy_clean_ttf_compressed_data[9583] =\n{\n    87,188,0,0,0,0,0,0,0,0,160,248,0,4,0,0,55,0,1,0,0,0,12,0,128,0,3,0,64,79,83,47,50,136,235,116,144,0,0,1,72,130,21,44,78,99,109,97,112,2,18,35,117,0,0,3,160,130,19,36,82,99,118,116,\n    32,130,23,130,2,33,4,252,130,4,56,2,103,108,121,102,18,175,137,86,0,0,7,4,0,0,146,128,104,101,97,100,215,145,102,211,130,27,32,204,130,3,33,54,104,130,16,39,8,66,1,195,0,0,1,4,130,\n    15,59,36,104,109,116,120,138,0,126,128,0,0,1,152,0,0,2,6,108,111,99,97,140,115,176,216,0,0,5,130,30,41,2,4,109,97,120,112,1,174,0,218,130,31,32,40,130,16,44,32,110,97,109,101,37,89,\n    187,150,0,0,153,132,130,19,44,158,112,111,115,116,166,172,131,239,0,0,155,36,130,51,44,210,112,114,101,112,105,2,1,18,0,0,4,244,130,47,32,8,132,203,46,1,0,0,60,85,233,213,95,15,60,\n    245,0,3,8,0,131,0,34,183,103,119,130,63,43,0,0,189,146,166,215,0,0,254,128,3,128,131,111,130,241,33,2,0,133,0,32,1,130,65,38,192,254,64,0,0,3,128,131,16,130,5,32,1,131,7,138,3,33,2,\n    0,130,17,36,1,1,0,144,0,130,121,130,23,38,2,0,8,0,64,0,10,130,9,32,118,130,9,130,6,32,0,130,59,33,1,144,131,200,35,2,188,2,138,130,16,32,143,133,7,37,1,197,0,50,2,0,131,0,33,4,9,131,\n    5,145,3,43,65,108,116,115,0,64,0,0,32,172,8,0,131,0,35,5,0,1,128,131,77,131,3,33,3,128,191,1,33,1,128,130,184,35,0,0,128,0,130,3,131,11,32,1,130,7,33,0,128,131,1,32,1,136,9,32,0,132,\n    15,135,5,32,1,131,13,135,27,144,35,32,1,149,25,131,21,32,0,130,0,32,128,132,103,130,35,132,39,32,0,136,45,136,97,133,17,130,5,33,0,0,136,19,34,0,128,1,133,13,133,5,32,128,130,15,132,\n    131,32,3,130,5,32,3,132,27,144,71,32,0,133,27,130,29,130,31,136,29,131,63,131,3,65,63,5,132,5,132,205,130,9,33,0,0,131,9,137,119,32,3,132,19,138,243,130,55,32,1,132,35,135,19,131,201,\n    136,11,132,143,137,13,130,41,32,0,131,3,144,35,33,128,0,135,1,131,223,131,3,141,17,134,13,136,63,134,15,136,53,143,15,130,96,33,0,3,131,4,130,3,34,28,0,1,130,5,34,0,0,76,130,17,131,\n    9,36,28,0,4,0,48,130,17,46,8,0,8,0,2,0,0,0,127,0,255,32,172,255,255,130,9,34,0,0,129,132,9,130,102,33,223,213,134,53,132,22,33,1,6,132,6,64,4,215,32,129,165,216,39,177,0,1,141,184,\n    1,255,133,134,45,33,198,0,193,1,8,190,244,1,28,1,158,2,20,2,136,2,252,3,20,3,88,3,156,3,222,4,20,4,50,4,80,4,98,4,162,5,22,5,102,5,188,6,18,6,116,6,214,7,56,7,126,7,236,8,78,8,108,\n    8,150,8,208,9,16,9,74,9,136,10,22,10,128,11,4,11,86,11,200,12,46,12,130,12,234,13,94,13,164,13,234,14,80,14,150,15,40,15,176,16,18,16,116,16,224,17,82,17,182,18,4,18,110,18,196,19,\n    76,19,172,19,246,20,88,20,174,20,234,21,64,21,128,21,166,21,184,22,18,22,126,22,198,23,52,23,142,23,224,24,86,24,186,24,238,25,54,25,150,25,212,26,72,26,156,26,240,27,92,27,200,28,\n    4,28,76,28,150,28,234,29,42,29,146,29,210,30,64,30,142,30,224,31,36,31,118,31,166,31,166,32,16,130,1,52,46,32,138,32,178,32,200,33,20,33,116,33,152,33,238,34,98,34,134,35,12,130,1,\n    33,128,35,131,1,60,152,35,176,35,216,36,0,36,74,36,104,36,144,36,174,37,6,37,96,37,130,37,248,37,248,38,88,38,170,130,1,8,190,216,39,64,39,154,40,10,40,104,40,168,41,14,41,32,41,184,\n    41,248,42,54,42,96,42,96,43,2,43,42,43,94,43,172,43,230,44,32,44,52,44,154,45,40,45,92,45,120,45,170,45,232,46,38,46,166,47,38,47,182,47,244,48,94,48,200,49,62,49,180,50,30,50,158,\n    51,30,51,130,51,238,52,92,52,206,53,58,53,134,53,212,54,38,54,114,54,230,55,118,55,216,56,58,56,166,57,18,57,116,57,174,58,46,58,154,59,6,59,124,59,232,60,58,60,150,61,34,61,134,61,\n    236,62,86,62,198,63,42,63,154,64,18,64,106,64,208,65,54,65,162,66,8,66,64,66,122,66,184,66,240,67,98,67,204,68,42,68,138,68,238,69,88,69,182,69,226,70,84,70,180,71,20,71,122,71,218,\n    72,84,72,198,73,64,0,36,70,21,8,8,77,3,0,7,0,11,0,15,0,19,0,23,0,27,0,31,0,35,0,39,0,43,0,47,0,51,0,55,0,59,0,63,0,67,0,71,0,75,0,79,0,83,0,87,0,91,0,95,0,99,0,103,0,107,0,111,0,115,\n    0,119,0,123,0,127,0,131,0,135,0,139,0,143,0,0,17,53,51,21,49,150,3,32,5,130,23,32,33,130,3,211,7,151,115,32,128,133,0,37,252,128,128,2,128,128,190,5,133,74,32,4,133,6,206,5,42,0,7,\n    1,128,0,0,2,0,4,0,0,65,139,13,37,0,1,53,51,21,7,146,3,32,3,130,19,32,1,141,133,32,3,141,14,131,13,38,255,0,128,128,0,6,1,130,84,35,2,128,4,128,140,91,132,89,32,51,65,143,6,139,7,33,\n    1,0,130,57,32,254,130,3,32,128,132,4,32,4,131,14,138,89,35,0,0,24,0,130,0,33,3,128,144,171,66,55,33,148,115,65,187,19,32,5,130,151,143,155,163,39,32,1,136,182,32,253,134,178,132,7,\n    132,200,145,17,32,3,65,48,17,165,17,39,0,0,21,0,128,255,128,3,65,175,17,65,3,27,132,253,131,217,139,201,155,233,155,27,131,67,131,31,130,241,33,255,0,131,181,137,232,132,15,132,4,138,\n    247,34,255,0,128,179,238,32,0,130,0,32,20,65,239,48,33,0,19,67,235,10,32,51,65,203,14,65,215,11,32,7,154,27,135,39,32,33,130,35,33,128,128,130,231,32,253,132,231,32,128,132,232,34,\n    128,128,254,133,13,136,8,32,253,65,186,5,130,36,130,42,176,234,133,231,34,128,0,0,66,215,44,33,0,1,68,235,6,68,211,19,32,49,68,239,14,139,207,139,47,66,13,7,32,51,130,47,33,1,0,130,\n    207,35,128,128,1,0,131,222,131,5,130,212,130,6,131,212,32,0,130,10,133,220,130,233,130,226,32,254,133,255,178,233,39,3,1,128,3,0,2,0,4,68,15,7,68,99,12,130,89,130,104,33,128,4,133,\n    93,130,10,38,0,0,11,1,0,255,0,68,63,16,70,39,9,66,215,8,32,7,68,77,6,68,175,14,32,29,68,195,6,132,7,35,2,0,128,255,131,91,132,4,65,178,5,141,111,67,129,23,165,135,140,107,142,135,33,\n    21,5,69,71,6,131,7,33,1,0,140,104,132,142,130,4,137,247,140,30,68,255,12,39,11,0,128,0,128,3,0,3,69,171,15,67,251,7,65,15,8,66,249,11,65,229,7,67,211,7,66,13,7,35,1,128,128,254,133,\n    93,32,254,131,145,132,4,132,18,32,2,151,128,130,23,34,0,0,9,154,131,65,207,8,68,107,15,68,51,7,32,7,70,59,7,135,121,130,82,32,128,151,111,41,0,0,4,0,128,255,0,1,128,1,137,239,33,0,\n    37,70,145,10,65,77,10,65,212,14,37,0,0,0,5,0,128,66,109,5,70,123,10,33,0,19,72,33,18,133,237,70,209,11,33,0,2,130,113,137,119,136,115,33,1,0,133,43,130,5,34,0,0,10,69,135,6,70,219,\n    13,66,155,7,65,9,12,66,157,11,66,9,11,32,7,130,141,132,252,66,151,9,137,9,66,15,30,36,0,20,0,128,0,130,218,71,11,42,68,51,8,65,141,7,73,19,15,69,47,23,143,39,66,81,7,32,1,66,55,6,34,\n    1,128,128,68,25,5,69,32,6,137,6,136,25,32,254,131,42,32,3,66,88,26,148,26,32,0,130,0,32,14,164,231,70,225,12,66,233,7,67,133,19,71,203,15,130,161,32,255,130,155,32,254,139,127,134,\n    12,164,174,33,0,15,164,159,33,59,0,65,125,20,66,25,7,32,5,68,191,6,66,29,7,144,165,65,105,9,35,128,128,255,0,137,2,133,182,164,169,33,128,128,197,171,130,155,68,235,7,32,21,70,77,19,\n    66,21,10,68,97,8,66,30,5,66,4,43,34,0,17,0,71,19,41,65,253,20,71,25,23,65,91,15,65,115,7,34,2,128,128,66,9,8,130,169,33,1,0,66,212,13,132,28,72,201,43,35,0,0,0,18,66,27,38,76,231,5,\n    68,157,20,135,157,32,7,68,185,13,65,129,28,66,20,5,32,253,66,210,11,65,128,49,133,61,32,0,65,135,6,74,111,37,72,149,12,66,203,19,65,147,19,68,93,7,68,85,8,76,4,5,33,255,0,133,129,34,\n    254,0,128,68,69,8,181,197,34,0,0,12,65,135,32,65,123,20,69,183,27,133,156,66,50,5,72,87,10,67,137,32,33,0,19,160,139,78,251,13,68,55,20,67,119,19,65,91,36,69,177,15,32,254,143,16,65,\n    98,53,32,128,130,0,32,0,66,43,54,70,141,23,66,23,15,131,39,69,47,11,131,15,70,129,19,74,161,9,36,128,255,0,128,254,130,153,65,148,32,67,41,9,34,0,0,4,79,15,5,73,99,10,71,203,8,32,3,\n    72,123,6,72,43,8,32,2,133,56,131,99,130,9,34,0,0,6,72,175,5,73,159,14,144,63,135,197,132,189,133,66,33,255,0,73,6,7,70,137,12,35,0,0,0,10,130,3,73,243,25,67,113,12,65,73,7,69,161,7,\n    138,7,37,21,2,0,128,128,254,134,3,73,116,27,33,128,128,130,111,39,12,0,128,1,0,3,128,2,72,219,21,35,43,0,47,0,67,47,20,130,111,33,21,1,68,167,13,81,147,8,133,230,32,128,77,73,6,32,\n    128,131,142,134,18,130,6,32,255,75,18,12,131,243,37,128,0,128,3,128,3,74,231,21,135,123,32,29,134,107,135,7,32,21,74,117,7,135,7,134,96,135,246,74,103,23,132,242,33,0,10,67,151,28,\n    67,133,20,66,141,11,131,11,32,3,77,71,6,32,128,130,113,32,1,81,4,6,134,218,66,130,24,131,31,34,0,26,0,130,0,77,255,44,83,15,11,148,155,68,13,7,32,49,78,231,18,79,7,11,73,243,11,32,\n    33,65,187,10,130,63,65,87,8,73,239,19,35,0,128,1,0,131,226,32,252,65,100,6,32,128,139,8,33,1,0,130,21,32,253,72,155,44,73,255,20,32,128,71,67,8,81,243,39,67,15,20,74,191,23,68,121,\n    27,32,1,66,150,6,32,254,79,19,11,131,214,32,128,130,215,37,2,0,128,253,0,128,136,5,65,220,24,147,212,130,210,33,0,24,72,219,42,84,255,13,67,119,16,69,245,19,72,225,19,65,3,15,69,93,\n    19,131,55,132,178,71,115,14,81,228,6,142,245,33,253,0,132,43,172,252,65,16,11,75,219,8,65,219,31,66,223,24,75,223,10,33,29,1,80,243,10,66,175,8,131,110,134,203,133,172,130,16,70,30,\n    7,164,183,130,163,32,20,65,171,48,65,163,36,65,143,23,65,151,19,65,147,13,65,134,17,133,17,130,216,67,114,5,164,217,65,137,12,72,147,48,79,71,19,74,169,22,80,251,8,65,173,7,66,157,\n    15,74,173,15,32,254,65,170,8,71,186,45,72,131,6,77,143,40,187,195,152,179,65,123,38,68,215,57,68,179,15,65,85,7,69,187,14,32,21,66,95,15,67,19,25,32,1,83,223,6,32,2,76,240,7,77,166,\n    43,65,8,5,130,206,32,0,67,39,54,143,167,66,255,19,82,193,11,151,47,85,171,5,67,27,17,132,160,69,172,11,69,184,56,66,95,6,33,12,1,130,237,32,2,68,179,27,68,175,16,80,135,15,72,55,7,\n    71,87,12,73,3,12,132,12,66,75,32,76,215,5,169,139,147,135,148,139,81,12,12,81,185,36,75,251,7,65,23,27,76,215,9,87,165,12,65,209,15,72,157,7,65,245,31,32,128,71,128,6,32,1,82,125,5,\n    34,0,128,254,131,169,32,254,131,187,71,180,9,132,27,32,2,88,129,44,32,0,78,47,40,65,79,23,79,171,14,32,21,71,87,8,72,15,14,65,224,33,130,139,74,27,62,93,23,7,68,31,7,75,27,7,139,15,\n    74,3,7,74,23,27,65,165,11,65,177,15,67,123,5,32,1,130,221,32,252,71,96,5,74,12,12,133,244,130,25,34,1,0,128,130,2,139,8,93,26,8,65,9,32,65,57,14,140,14,32,0,73,79,67,68,119,11,135,\n    11,32,51,90,75,14,139,247,65,43,7,131,19,139,11,69,159,11,65,247,6,36,1,128,128,253,0,90,71,9,33,1,0,132,14,32,128,89,93,14,69,133,6,130,44,131,30,131,6,65,20,56,33,0,16,72,179,40,\n    75,47,12,65,215,19,74,95,19,65,43,11,131,168,67,110,5,75,23,17,69,106,6,75,65,5,71,204,43,32,0,80,75,47,71,203,15,159,181,68,91,11,67,197,7,73,101,13,68,85,6,33,128,128,130,214,130,\n    25,32,254,74,236,48,130,194,37,0,18,0,128,255,128,77,215,40,65,139,64,32,51,80,159,10,65,147,39,130,219,84,212,43,130,46,75,19,97,74,33,11,65,201,23,65,173,31,33,1,0,79,133,6,66,150,\n    5,67,75,48,85,187,6,70,207,37,32,71,87,221,13,73,163,14,80,167,15,132,15,83,193,19,82,209,8,78,99,9,72,190,11,77,110,49,89,63,5,80,91,35,99,63,32,70,235,23,81,99,10,69,148,10,65,110,\n    36,32,0,65,99,47,95,219,11,68,171,51,66,87,7,72,57,7,74,45,17,143,17,65,114,50,33,14,0,65,111,40,159,195,98,135,15,35,7,53,51,21,100,78,9,95,146,16,32,254,82,114,6,32,128,67,208,37,\n    130,166,99,79,58,32,17,96,99,14,72,31,19,72,87,31,82,155,7,67,47,14,32,21,131,75,134,231,72,51,17,72,78,8,133,8,80,133,6,33,253,128,88,37,9,66,124,36,72,65,12,134,12,71,55,43,66,139,\n    27,85,135,10,91,33,12,65,35,11,66,131,11,71,32,8,90,127,6,130,244,71,76,11,168,207,33,0,12,66,123,32,32,0,65,183,15,68,135,11,66,111,7,67,235,11,66,111,15,32,254,97,66,12,160,154,67,\n    227,52,80,33,15,87,249,15,93,45,31,75,111,12,93,45,11,77,99,9,160,184,81,31,12,32,15,98,135,30,104,175,7,77,249,36,69,73,15,78,5,12,32,254,66,151,19,34,128,128,4,87,32,12,149,35,133,\n    21,96,151,31,32,19,72,35,5,98,173,15,143,15,32,21,143,99,158,129,33,0,0,65,35,52,65,11,15,147,15,98,75,11,33,1,0,143,151,132,15,32,254,99,200,37,132,43,130,4,39,0,10,0,128,1,128,3,\n    0,104,151,14,97,187,20,69,131,15,67,195,11,87,227,7,33,128,128,132,128,33,254,0,68,131,9,65,46,26,42,0,0,0,7,0,0,255,128,3,128,0,88,223,15,33,0,21,89,61,22,66,209,12,65,2,12,37,0,2,\n    1,0,3,128,101,83,8,36,0,1,53,51,29,130,3,34,21,1,0,66,53,8,32,0,68,215,6,100,55,25,107,111,9,66,193,11,72,167,8,73,143,31,139,31,33,1,0,131,158,32,254,132,5,33,253,128,65,16,9,133,\n    17,89,130,25,141,212,33,0,0,93,39,8,90,131,25,93,39,14,66,217,6,106,179,8,159,181,71,125,15,139,47,138,141,87,11,14,76,23,14,65,231,26,140,209,66,122,8,81,179,5,101,195,26,32,47,74,\n    75,13,69,159,11,83,235,11,67,21,16,136,167,131,106,130,165,130,15,32,128,101,90,24,134,142,32,0,65,103,51,108,23,11,101,231,15,75,173,23,74,237,23,66,15,6,66,46,17,66,58,17,65,105,\n    49,66,247,55,71,179,12,70,139,15,86,229,7,84,167,15,32,1,95,72,12,89,49,6,33,128,128,65,136,38,66,30,9,32,0,100,239,7,66,247,29,70,105,20,65,141,19,69,81,15,130,144,32,128,83,41,5,\n    32,255,131,177,68,185,5,133,126,65,97,37,32,0,130,0,33,21,0,130,55,66,195,28,67,155,13,34,79,0,83,66,213,13,73,241,19,66,59,19,65,125,11,135,201,66,249,16,32,128,66,44,11,66,56,17,\n    68,143,8,68,124,38,67,183,12,96,211,9,65,143,29,112,171,5,32,0,68,131,63,34,33,53,51,71,121,11,32,254,98,251,16,32,253,74,231,10,65,175,37,133,206,37,0,0,8,1,0,0,107,123,11,113,115,\n    9,33,0,1,130,117,131,3,73,103,7,66,51,18,66,44,5,133,75,70,88,5,32,254,65,39,12,68,80,9,34,12,0,128,107,179,28,68,223,6,155,111,86,147,15,32,2,131,82,141,110,33,254,0,130,15,32,4,103,\n    184,15,141,35,87,176,5,83,11,5,71,235,23,114,107,11,65,189,16,70,33,15,86,153,31,135,126,86,145,30,65,183,41,32,0,130,0,32,10,65,183,24,34,35,0,39,67,85,9,65,179,15,143,15,33,1,0,65,\n    28,17,157,136,130,123,32,20,130,3,32,0,97,135,24,115,167,19,80,71,12,32,51,110,163,14,78,35,19,131,19,155,23,77,229,8,78,9,17,151,17,67,231,46,94,135,8,73,31,31,93,215,56,82,171,25,\n    72,77,8,162,179,169,167,99,131,11,69,85,19,66,215,15,76,129,13,68,115,22,72,79,35,67,113,5,34,0,0,19,70,31,46,65,89,52,73,223,15,85,199,33,95,33,8,132,203,73,29,32,67,48,16,177,215,\n    101,13,15,65,141,43,69,141,15,75,89,5,70,0,11,70,235,21,178,215,36,10,0,128,0,0,71,207,24,33,0,19,100,67,6,80,215,11,66,67,7,80,43,12,71,106,7,80,192,5,65,63,5,66,217,26,33,0,13,156,\n    119,68,95,5,72,233,12,134,129,85,81,11,76,165,20,65,43,8,73,136,8,75,10,31,38,128,128,0,0,0,13,1,130,4,32,3,106,235,29,114,179,12,66,131,23,32,7,77,133,6,67,89,12,131,139,116,60,9,\n    89,15,37,32,0,74,15,7,103,11,22,65,35,5,33,55,0,93,81,28,67,239,23,78,85,5,107,93,14,66,84,17,65,193,26,74,183,10,66,67,34,143,135,79,91,15,32,7,117,111,8,75,56,9,84,212,9,154,134,\n    32,0,130,0,32,18,130,3,70,171,41,83,7,16,70,131,19,84,191,15,84,175,19,84,167,30,84,158,12,154,193,68,107,15,33,0,0,65,79,42,65,71,7,73,55,7,118,191,16,83,180,9,32,255,76,166,9,154,\n    141,32,0,130,0,69,195,52,65,225,15,151,15,75,215,31,80,56,10,68,240,17,100,32,9,70,147,39,65,93,12,71,71,41,92,85,15,84,135,23,78,35,15,110,27,10,84,125,8,107,115,29,136,160,38,0,0,\n    14,0,128,255,0,82,155,24,67,239,8,119,255,11,69,131,11,77,29,6,112,31,8,134,27,105,203,8,32,2,75,51,11,75,195,12,74,13,29,136,161,37,128,0,0,0,11,1,130,163,82,115,8,125,191,17,69,35,\n    12,74,137,15,143,15,32,1,65,157,12,136,12,161,142,65,43,40,65,199,6,65,19,24,102,185,11,76,123,11,99,6,12,135,12,32,254,130,8,161,155,101,23,9,39,8,0,0,1,128,3,128,2,78,63,17,72,245,\n    12,67,41,11,90,167,9,32,128,97,49,9,32,128,109,51,14,132,97,81,191,8,130,97,125,99,12,121,35,9,127,75,15,71,79,12,81,151,23,87,97,7,70,223,15,80,245,16,105,97,15,32,254,113,17,6,32,\n    128,130,8,105,105,8,76,122,18,65,243,21,74,63,7,38,4,1,0,255,0,2,0,119,247,28,133,65,32,255,141,91,35,0,0,0,16,67,63,36,34,59,0,63,77,59,9,119,147,11,143,241,66,173,15,66,31,11,67,\n    75,8,81,74,16,32,128,131,255,87,181,42,127,43,5,34,255,128,2,120,235,11,37,19,0,23,0,0,37,109,191,14,118,219,7,127,43,14,65,79,14,35,0,0,0,3,73,91,5,130,5,38,3,0,7,0,11,0,0,70,205,\n    11,88,221,12,32,0,73,135,7,87,15,22,73,135,10,79,153,15,97,71,19,65,49,11,32,1,131,104,121,235,11,80,65,11,142,179,144,14,81,123,46,32,1,88,217,5,112,5,8,65,201,15,83,29,15,122,147,\n    11,135,179,142,175,143,185,67,247,39,66,199,7,35,5,0,128,3,69,203,15,123,163,12,67,127,7,130,119,71,153,10,141,102,70,175,8,32,128,121,235,30,136,89,100,191,11,116,195,11,111,235,15,\n    72,39,7,32,2,97,43,5,132,5,94,67,8,131,8,125,253,10,32,3,65,158,16,146,16,130,170,40,0,21,0,128,0,0,3,128,5,88,219,15,24,64,159,32,135,141,65,167,15,68,163,10,97,73,49,32,255,82,58,\n    7,93,80,8,97,81,16,24,67,87,52,34,0,0,5,130,231,33,128,2,80,51,13,65,129,8,113,61,6,132,175,65,219,5,130,136,77,152,17,32,0,95,131,61,70,215,6,33,21,51,90,53,10,78,97,23,105,77,31,\n    65,117,7,139,75,24,68,195,9,24,64,22,9,33,0,128,130,11,33,128,128,66,25,5,121,38,5,134,5,134,45,66,40,36,66,59,18,34,128,0,0,66,59,81,135,245,123,103,19,120,159,19,77,175,12,33,255,\n    0,87,29,10,94,70,21,66,59,54,39,3,1,128,3,0,2,128,4,24,65,7,15,66,47,7,72,98,12,37,0,0,0,3,1,0,24,65,55,21,131,195,32,1,67,178,6,33,4,0,77,141,8,32,6,131,47,74,67,16,24,69,3,20,24,\n    65,251,7,133,234,130,229,94,108,17,35,0,0,6,0,141,175,86,59,5,162,79,85,166,8,70,112,13,32,13,24,64,67,26,24,71,255,7,123,211,12,80,121,11,69,215,15,66,217,11,69,71,10,131,113,132,\n    126,119,90,9,66,117,19,132,19,32,0,130,0,24,64,47,59,33,7,0,73,227,5,68,243,15,85,13,12,76,37,22,74,254,15,130,138,33,0,4,65,111,6,137,79,65,107,16,32,1,77,200,6,34,128,128,3,75,154,\n    12,37,0,16,0,0,2,0,104,115,36,140,157,68,67,19,68,51,15,106,243,15,134,120,70,37,10,68,27,10,140,152,65,121,24,32,128,94,155,7,67,11,8,24,74,11,25,65,3,12,83,89,18,82,21,37,67,200,\n    5,130,144,24,64,172,12,33,4,0,134,162,74,80,14,145,184,32,0,130,0,69,251,20,32,19,81,243,5,82,143,8,33,5,53,89,203,5,133,112,79,109,15,33,0,21,130,71,80,175,41,36,75,0,79,0,83,121,\n    117,9,87,89,27,66,103,11,70,13,15,75,191,11,135,67,87,97,20,109,203,5,69,246,8,108,171,5,78,195,38,65,51,13,107,203,11,77,3,17,24,75,239,17,65,229,28,79,129,39,130,175,32,128,123,253,\n    7,132,142,24,65,51,15,65,239,41,36,128,128,0,0,13,65,171,5,66,163,28,136,183,118,137,11,80,255,15,67,65,7,74,111,8,32,0,130,157,32,253,24,76,35,10,103,212,5,81,175,9,69,141,7,66,150,\n    29,131,158,24,75,199,28,124,185,7,76,205,15,68,124,14,32,3,123,139,16,130,16,33,128,128,108,199,6,33,0,3,65,191,35,107,11,6,73,197,11,24,70,121,15,83,247,15,24,70,173,23,69,205,14,\n    32,253,131,140,32,254,136,4,94,198,9,32,3,78,4,13,66,127,13,143,13,32,0,130,0,33,16,0,24,69,59,39,109,147,12,76,253,19,24,69,207,15,69,229,15,130,195,71,90,10,139,10,130,152,73,43,\n    40,91,139,10,65,131,37,35,75,0,79,0,84,227,12,143,151,68,25,15,80,9,23,95,169,11,34,128,2,128,112,186,5,130,6,83,161,19,76,50,6,130,37,65,145,44,110,83,5,32,16,67,99,6,71,67,15,76,\n    55,17,140,215,67,97,23,76,69,15,77,237,11,104,211,23,77,238,11,65,154,43,33,0,10,83,15,28,83,13,20,67,145,19,67,141,14,97,149,21,68,9,15,86,251,5,66,207,5,66,27,37,82,1,23,127,71,12,\n    94,235,10,110,175,24,98,243,15,132,154,132,4,24,66,69,10,32,4,67,156,43,130,198,35,2,1,0,4,75,27,9,69,85,9,95,240,7,32,128,130,35,32,28,66,43,40,24,82,63,23,83,123,12,72,231,15,127,\n    59,23,116,23,19,117,71,7,24,77,99,15,67,111,15,71,101,8,36,2,128,128,252,128,127,60,11,32,1,132,16,130,18,141,24,67,107,9,32,3,68,194,15,175,15,38,0,11,0,128,1,128,2,80,63,25,32,0,\n    24,65,73,11,69,185,15,83,243,16,32,0,24,81,165,8,130,86,77,35,6,155,163,88,203,5,24,66,195,30,70,19,19,24,80,133,15,32,1,75,211,8,32,254,108,133,8,79,87,20,65,32,9,41,0,0,7,0,128,0,\n    0,2,128,2,68,87,15,66,1,16,92,201,16,24,76,24,17,133,17,34,128,0,30,66,127,64,34,115,0,119,73,205,9,66,43,11,109,143,15,24,79,203,11,90,143,15,131,15,155,31,65,185,15,86,87,11,35,128,\n    128,253,0,69,7,6,130,213,33,1,0,119,178,15,142,17,66,141,74,83,28,6,36,7,0,0,4,128,82,39,18,76,149,12,67,69,21,32,128,79,118,15,32,0,130,0,32,8,131,206,32,2,79,83,9,100,223,14,102,\n    113,23,115,115,7,24,65,231,12,130,162,32,4,68,182,19,130,102,93,143,8,69,107,29,24,77,255,12,143,197,72,51,7,76,195,15,132,139,85,49,15,130,152,131,18,71,81,23,70,14,11,36,0,10,0,128,\n    2,69,59,9,89,151,15,66,241,11,76,165,12,71,43,15,75,49,13,65,12,23,132,37,32,0,179,115,130,231,95,181,16,132,77,32,254,67,224,8,65,126,20,79,171,8,32,2,89,81,5,75,143,6,80,41,8,34,\n    2,0,128,24,81,72,9,32,0,130,0,35,17,0,0,255,77,99,39,95,65,36,67,109,15,24,69,93,11,77,239,5,95,77,23,35,128,1,0,128,24,86,7,8,132,167,32,2,69,198,41,130,202,33,0,26,120,75,44,24,89,\n    51,15,71,243,12,70,239,11,24,84,3,11,66,7,11,71,255,10,32,21,69,155,35,88,151,12,32,128,74,38,10,65,210,8,74,251,5,65,226,5,75,201,13,32,3,65,9,41,146,41,40,0,0,0,9,1,0,1,0,2,91,99,\n    19,32,35,106,119,13,70,219,15,83,239,12,137,154,32,2,67,252,19,36,128,0,0,4,1,130,196,32,2,130,8,91,107,8,32,0,135,81,24,73,211,8,132,161,73,164,13,36,0,8,0,128,2,105,123,26,139,67,\n    76,99,15,34,1,0,128,135,76,83,156,20,92,104,8,67,251,30,24,86,47,27,123,207,12,24,86,7,15,71,227,8,32,4,65,20,20,131,127,32,0,130,123,32,0,71,223,26,32,19,90,195,22,71,223,15,84,200,\n    6,32,128,133,241,24,84,149,9,67,41,25,36,0,0,0,22,0,88,111,49,32,87,66,21,5,77,3,27,123,75,7,71,143,19,135,183,71,183,19,130,171,74,252,5,131,5,89,87,17,32,1,132,18,130,232,68,11,10,\n    33,1,128,70,208,16,66,230,18,147,18,130,254,223,255,75,27,23,65,59,15,135,39,155,255,34,128,128,254,104,92,8,33,0,128,65,32,11,65,1,58,33,26,0,130,0,72,71,18,78,55,17,76,11,19,86,101,\n    12,75,223,11,89,15,11,24,76,87,15,75,235,15,131,15,72,95,7,85,71,11,72,115,11,73,64,6,34,1,128,128,66,215,9,34,128,254,128,134,14,33,128,255,67,102,5,32,0,130,16,70,38,11,66,26,57,\n    88,11,8,24,76,215,34,78,139,7,95,245,7,32,7,24,73,75,23,32,128,131,167,130,170,101,158,9,82,49,22,118,139,6,32,18,67,155,44,116,187,9,108,55,14,80,155,23,66,131,15,93,77,10,131,168,\n    32,128,73,211,12,24,75,187,22,32,4,96,71,20,67,108,19,132,19,120,207,8,32,5,76,79,15,66,111,21,66,95,8,32,3,190,211,111,3,8,211,212,32,20,65,167,44,34,75,0,79,97,59,13,32,33,112,63,\n    10,65,147,19,69,39,19,143,39,24,66,71,9,130,224,65,185,43,94,176,12,65,183,24,71,38,8,24,72,167,7,65,191,38,136,235,24,96,167,12,65,203,62,115,131,13,65,208,42,175,235,67,127,6,32,\n    4,76,171,29,114,187,5,32,71,65,211,5,65,203,68,72,51,8,164,219,32,0,172,214,71,239,58,78,3,27,66,143,15,77,19,15,147,31,35,33,53,51,21,66,183,10,173,245,66,170,30,150,30,34,0,0,23,\n    80,123,54,76,1,16,73,125,15,82,245,11,167,253,24,76,85,12,70,184,5,32,254,131,185,37,254,0,128,1,0,128,133,16,117,158,18,92,27,38,65,3,17,130,251,35,17,0,128,254,24,69,83,39,140,243,\n    121,73,19,109,167,7,81,41,15,24,95,175,12,102,227,15,121,96,11,24,95,189,7,32,3,145,171,154,17,24,77,47,9,33,0,5,70,71,37,68,135,7,32,29,117,171,11,69,87,15,24,79,97,19,24,79,149,23,\n    131,59,32,1,75,235,5,72,115,11,72,143,7,132,188,71,27,46,131,51,32,0,69,95,6,175,215,32,21,131,167,81,15,19,151,191,151,23,131,215,71,43,5,32,254,24,79,164,24,74,109,8,77,166,13,65,\n    176,26,88,162,5,98,159,6,171,219,120,247,6,79,29,8,99,169,10,103,59,19,65,209,35,131,35,91,25,19,112,94,15,83,36,8,173,229,33,20,0,88,75,43,71,31,12,65,191,71,33,1,0,130,203,32,254,\n    131,4,68,66,7,67,130,6,104,61,13,173,215,38,13,1,0,0,0,2,128,67,111,28,74,129,16,104,35,19,79,161,16,87,14,7,138,143,132,10,67,62,36,114,115,5,162,151,67,33,16,108,181,15,143,151,67,\n    5,5,24,100,242,15,170,153,34,0,0,14,65,51,34,32,55,79,75,9,32,51,74,7,10,65,57,38,132,142,32,254,72,0,14,139,163,32,128,80,254,8,67,158,21,65,63,7,32,4,72,227,27,95,155,12,67,119,19,\n    124,91,24,149,154,72,177,34,97,223,8,155,151,24,108,227,15,88,147,16,72,117,19,68,35,11,92,253,15,70,199,15,24,87,209,17,32,2,87,233,7,32,1,24,88,195,10,119,24,8,32,3,81,227,24,65,\n    125,21,35,128,128,0,25,76,59,48,24,90,187,9,97,235,12,66,61,11,91,105,19,24,79,141,11,24,79,117,15,24,79,129,27,90,53,13,130,13,32,253,131,228,24,79,133,40,69,70,8,66,137,31,65,33,\n    19,96,107,8,68,119,29,66,7,5,68,125,16,65,253,19,65,241,27,24,90,179,13,24,79,143,18,33,128,128,130,246,32,254,130,168,68,154,36,77,51,9,97,47,5,167,195,32,21,131,183,78,239,27,155,\n    195,78,231,14,201,196,77,11,6,32,5,73,111,37,97,247,12,77,19,31,155,207,78,215,19,162,212,69,17,14,66,91,19,80,143,57,78,203,39,159,215,32,128,93,134,8,24,80,109,24,66,113,15,169,215,\n    66,115,6,32,4,69,63,33,32,0,101,113,7,86,227,35,143,211,36,49,53,51,21,1,77,185,14,65,159,28,69,251,34,67,56,8,33,9,0,24,107,175,25,90,111,12,110,251,11,119,189,24,119,187,34,87,15,\n    9,32,4,66,231,37,90,39,7,66,239,8,84,219,15,69,105,23,24,85,27,27,87,31,11,33,1,128,76,94,6,32,1,85,241,7,33,128,128,106,48,10,33,128,128,69,136,11,133,13,24,79,116,49,84,236,8,24,\n    91,87,9,32,5,165,255,69,115,12,66,27,15,159,15,24,72,247,12,74,178,5,24,80,64,15,33,0,128,143,17,77,89,51,130,214,24,81,43,7,170,215,74,49,8,159,199,143,31,139,215,69,143,5,32,254,\n    24,81,50,35,181,217,84,123,70,143,195,159,15,65,187,16,66,123,7,65,175,15,65,193,29,68,207,39,79,27,5,70,131,6,32,4,68,211,33,33,67,0,83,143,14,159,207,143,31,140,223,33,0,128,24,80,\n    82,14,24,93,16,23,32,253,65,195,5,68,227,40,133,214,107,31,7,32,5,67,115,27,87,9,8,107,31,43,66,125,6,32,0,103,177,23,131,127,72,203,36,32,0,110,103,8,155,163,73,135,6,32,19,24,112,\n    99,10,65,71,11,73,143,19,143,31,126,195,5,24,85,21,9,24,76,47,14,32,254,24,93,77,36,68,207,11,39,25,0,0,255,128,3,128,4,66,51,37,95,247,13,82,255,24,76,39,19,147,221,66,85,27,24,118,\n    7,8,24,74,249,12,76,74,8,91,234,8,67,80,17,131,222,33,253,0,121,30,44,73,0,16,69,15,6,32,0,65,23,38,69,231,12,65,179,6,98,131,16,86,31,27,24,108,157,14,80,160,8,24,65,46,17,33,4,0,\n    96,2,18,144,191,65,226,8,68,19,5,171,199,80,9,15,180,199,67,89,5,32,255,24,79,173,28,174,201,24,79,179,50,32,1,24,122,5,10,82,61,10,180,209,83,19,8,32,128,24,80,129,27,111,248,43,131,\n    71,24,115,103,8,67,127,41,78,213,24,100,247,19,66,115,39,75,107,5,32,254,165,219,78,170,40,24,112,163,49,32,1,97,203,6,65,173,64,32,0,83,54,7,133,217,88,37,12,32,254,131,28,33,128,\n    3,67,71,44,84,183,6,32,5,69,223,33,96,7,7,123,137,16,192,211,24,112,14,9,32,255,67,88,29,68,14,10,84,197,38,33,0,22,116,47,50,32,87,106,99,9,116,49,15,89,225,15,97,231,23,70,41,19,\n    82,85,8,93,167,6,32,253,132,236,108,190,7,89,251,5,116,49,58,33,128,128,131,234,32,15,24,74,67,38,70,227,24,24,83,45,23,89,219,12,70,187,12,89,216,19,32,2,69,185,24,141,24,70,143,66,\n    24,82,119,56,78,24,10,32,253,133,149,132,6,24,106,233,7,69,198,48,178,203,81,243,12,68,211,15,106,255,23,66,91,15,69,193,7,100,39,10,24,83,72,16,176,204,33,19,0,88,207,45,68,21,12,\n    68,17,10,65,157,53,68,17,6,32,254,92,67,10,65,161,25,69,182,43,24,118,91,47,69,183,18,181,209,111,253,12,89,159,8,66,112,12,69,184,45,35,0,0,0,9,24,80,227,26,73,185,16,118,195,15,131,\n    15,33,1,0,65,59,15,66,39,27,160,111,66,205,12,148,111,143,110,33,128,128,156,112,24,81,199,8,75,199,23,66,117,20,155,121,32,254,68,126,12,72,213,29,134,239,149,123,89,27,16,148,117,\n    65,245,8,24,71,159,14,141,134,134,28,73,51,55,109,77,15,105,131,11,68,67,11,76,169,27,107,209,12,102,174,8,32,128,72,100,18,116,163,56,79,203,11,75,183,44,85,119,19,71,119,23,151,227,\n    32,1,93,27,8,65,122,5,77,102,8,110,120,20,66,23,8,66,175,17,66,63,12,133,12,79,35,8,74,235,33,67,149,16,69,243,15,78,57,15,69,235,16,67,177,7,151,192,130,23,67,84,29,141,192,174,187,\n    77,67,15,69,11,12,159,187,77,59,10,199,189,24,70,235,50,96,83,19,66,53,23,105,65,19,77,47,12,163,199,66,67,37,78,207,50,67,23,23,174,205,67,228,6,71,107,13,67,22,14,66,85,11,83,187,\n    38,124,47,49,95,7,19,66,83,23,67,23,19,24,96,78,17,80,101,16,71,98,40,33,0,7,88,131,22,24,89,245,12,84,45,12,102,213,5,123,12,9,32,2,126,21,14,43,255,0,128,128,0,0,20,0,128,255,128,\n    3,126,19,39,32,75,106,51,7,113,129,15,24,110,135,19,126,47,15,115,117,11,69,47,11,32,2,109,76,9,102,109,9,32,128,75,2,10,130,21,32,254,69,47,6,32,3,94,217,47,32,0,65,247,10,69,15,46,\n    65,235,31,65,243,15,101,139,10,66,174,14,65,247,16,72,102,28,69,17,14,84,243,9,165,191,88,47,48,66,53,12,32,128,71,108,6,203,193,32,17,75,187,42,73,65,16,65,133,52,114,123,9,167,199,\n    69,21,37,86,127,44,75,171,11,180,197,78,213,12,148,200,81,97,46,24,95,243,9,32,4,66,75,33,113,103,9,87,243,36,143,225,24,84,27,31,90,145,8,148,216,67,49,5,24,84,34,14,75,155,27,67,\n    52,13,140,13,36,0,20,0,128,255,24,135,99,46,88,59,43,155,249,80,165,7,136,144,71,161,23,32,253,132,33,32,254,88,87,44,136,84,35,128,0,0,21,81,103,5,94,47,44,76,51,12,143,197,151,15,\n    65,215,31,24,64,77,13,65,220,20,65,214,14,71,4,40,65,213,13,32,0,130,0,35,21,1,2,0,135,0,34,36,0,72,134,10,36,1,0,26,0,130,134,11,36,2,0,14,0,108,134,11,32,3,138,23,32,4,138,11,34,\n    5,0,20,134,33,34,0,0,6,132,23,32,1,134,15,32,18,130,25,133,11,37,1,0,13,0,49,0,133,11,36,2,0,7,0,38,134,11,36,3,0,17,0,45,134,11,32,4,138,35,36,5,0,10,0,62,134,23,32,6,132,23,36,3,\n    0,1,4,9,130,87,131,167,133,11,133,167,133,11,133,167,133,11,37,3,0,34,0,122,0,133,11,133,167,133,11,133,167,133,11,133,167,34,50,0,48,130,1,34,52,0,47,134,5,8,49,49,0,53,98,121,32,\n    84,114,105,115,116,97,110,32,71,114,105,109,109,101,114,82,101,103,117,108,97,114,84,84,88,32,80,114,111,103,103,121,67,108,101,97,110,84,84,50,48,48,52,47,130,2,53,49,53,0,98,0,121,\n    0,32,0,84,0,114,0,105,0,115,0,116,0,97,0,110,130,15,32,71,132,15,36,109,0,109,0,101,130,9,32,82,130,5,36,103,0,117,0,108,130,29,32,114,130,43,34,84,0,88,130,35,32,80,130,25,34,111,\n    0,103,130,1,34,121,0,67,130,27,32,101,132,59,32,84,130,31,33,0,0,65,155,9,34,20,0,0,65,11,6,130,8,135,2,33,1,1,130,9,8,120,1,1,2,1,3,1,4,1,5,1,6,1,7,1,8,1,9,1,10,1,11,1,12,1,13,1,14,\n    1,15,1,16,1,17,1,18,1,19,1,20,1,21,1,22,1,23,1,24,1,25,1,26,1,27,1,28,1,29,1,30,1,31,1,32,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0,16,0,17,0,18,0,19,0,20,0,21,0,\n    22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0,31,130,187,8,66,33,0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,41,0,42,0,43,0,44,0,45,0,46,0,47,0,48,0,49,0,50,0,51,0,52,0,53,0,54,0,55,0,56,0,\n    57,0,58,0,59,0,60,0,61,0,62,0,63,0,64,0,65,0,66,130,243,9,75,68,0,69,0,70,0,71,0,72,0,73,0,74,0,75,0,76,0,77,0,78,0,79,0,80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,90,0,91,0,\n    92,0,93,0,94,0,95,0,96,0,97,1,33,1,34,1,35,1,36,1,37,1,38,1,39,1,40,1,41,1,42,1,43,1,44,1,45,1,46,1,47,1,48,1,49,1,50,1,51,1,52,1,53,1,54,1,55,1,56,1,57,1,58,1,59,1,60,1,61,1,62,1,\n    63,1,64,1,65,0,172,0,163,0,132,0,133,0,189,0,150,0,232,0,134,0,142,0,139,0,157,0,169,0,164,0,239,0,138,0,218,0,131,0,147,0,242,0,243,0,141,0,151,0,136,0,195,0,222,0,241,0,158,0,170,\n    0,245,0,244,0,246,0,162,0,173,0,201,0,199,0,174,0,98,0,99,0,144,0,100,0,203,0,101,0,200,0,202,0,207,0,204,0,205,0,206,0,233,0,102,0,211,0,208,0,209,0,175,0,103,0,240,0,145,0,214,0,\n    212,0,213,0,104,0,235,0,237,0,137,0,106,0,105,0,107,0,109,0,108,0,110,0,160,0,111,0,113,0,112,0,114,0,115,0,117,0,116,0,118,0,119,0,234,0,120,0,122,0,121,0,123,0,125,0,124,0,184,0,\n    161,0,127,0,126,0,128,0,129,0,236,0,238,0,186,14,117,110,105,99,111,100,101,35,48,120,48,48,48,49,141,14,32,50,141,14,32,51,141,14,32,52,141,14,32,53,141,14,32,54,141,14,32,55,141,\n    14,32,56,141,14,32,57,141,14,32,97,141,14,32,98,141,14,32,99,141,14,32,100,141,14,32,101,141,14,32,102,140,14,33,49,48,141,14,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,\n    141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,45,49,102,6,100,101,108,101,116,101,4,69,117,114,\n    111,140,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,\n    32,56,141,236,32,56,141,236,32,56,65,220,13,32,57,65,220,13,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,\n    239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,35,57,102,0,0,5,250,72,249,98,247,\n};\n\nstatic const char* GetDefaultCompressedFontDataProggyClean(int* out_size)\n{\n    *out_size = proggy_clean_ttf_compressed_size;\n    return (const char*)proggy_clean_ttf_compressed_data;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Default font data (ProggyForever-Regular-minimal.ttf)\n//-----------------------------------------------------------------------------\n// Based on ProggyForever: https://github.com/ocornut/proggyforever\n// MIT license / Copyright (c) 2026 Disco Hello, Copyright (c) 2019,2023 Tristan Grimmer\n//-----------------------------------------------------------------------------\n\n// File: 'output/ProggyForever-Regular-minimal.ttf' (18556 bytes)\n// Exported using binary_to_compressed_c.exe -u8 \"output/ProggyForever-Regular-minimal.ttf\" proggy_forever_minimal_ttf\nstatic const unsigned int proggy_forever_minimal_ttf_compressed_size = 14562;\nstatic const unsigned char proggy_forever_minimal_ttf_compressed_data[14562] =\n{\n    87,188,0,0,0,0,0,0,0,0,72,124,0,4,0,0,55,0,1,0,0,0,14,0,128,0,3,0,96,70,70,84,77,176,111,174,190,0,0,72,96,130,21,40,28,71,68,69,70,0,136,0,105,130,15,32,64,130,15,44,30,79,83,47,50,\n    104,97,19,194,0,0,1,104,130,15,44,96,99,109,97,112,177,173,221,139,0,0,3,80,130,19,44,114,99,118,116,32,0,33,2,121,0,0,4,196,130,31,38,4,103,97,115,112,255,255,130,89,34,0,72,56,130,\n    15,56,8,103,108,121,102,239,245,108,207,0,0,6,76,0,0,62,224,104,101,97,100,44,57,58,3,130,27,32,236,130,3,33,54,104,130,16,35,4,62,0,230,130,75,32,36,130,15,39,36,104,109,116,120,24,\n    22,19,130,95,33,1,200,130,19,40,136,108,111,99,97,80,9,64,114,130,95,131,15,39,130,109,97,120,112,1,7,0,131,31,32,72,130,47,44,32,110,97,109,101,10,160,159,151,0,0,69,44,130,47,44,\n    68,112,111,115,116,70,77,175,253,0,0,70,112,130,15,32,197,132,235,32,1,130,9,42,224,136,151,95,15,60,245,0,11,3,232,130,55,36,0,229,175,187,66,132,7,42,178,59,232,255,225,255,68,1,\n    215,2,176,130,15,34,8,0,2,130,5,131,2,130,51,39,2,131,255,71,0,0,1,184,130,31,34,226,1,215,132,73,131,25,135,3,32,4,132,17,37,192,0,90,0,5,0,131,0,33,2,0,130,44,132,19,34,64,0,46,130,\n    11,38,0,0,4,1,184,1,144,131,29,35,2,138,2,187,130,17,32,140,133,7,38,1,223,0,49,1,2,0,138,0,37,128,0,0,7,16,0,136,123,33,0,88,130,0,37,0,64,0,32,32,172,133,131,34,2,175,0,131,63,33,\n    3,0,130,0,35,1,133,2,6,130,6,38,32,0,1,1,184,0,33,130,9,130,161,32,0,132,3,39,0,166,0,116,255,249,0,49,130,113,36,4,0,185,0,135,130,1,38,27,0,22,0,154,0,53,130,25,40,39,0,41,0,79,0,\n    50,0,45,130,17,32,49,130,11,33,46,0,131,17,36,166,0,143,0,24,132,1,34,48,255,225,130,55,34,38,0,47,130,23,44,52,0,58,0,35,0,42,0,66,0,65,0,19,130,79,32,19,130,47,38,35,0,44,0,13,0,\n    38,130,21,38,9,0,37,0,13,255,250,130,121,36,5,0,33,0,104,130,47,40,104,0,37,255,242,0,124,0,48,130,95,32,59,130,3,34,37,0,40,130,5,36,62,0,139,0,100,130,57,36,133,0,20,0,62,130,55,\n    32,50,130,19,40,69,0,69,0,87,0,54,0,29,130,167,32,20,130,107,36,64,0,63,0,188,130,3,36,22,0,21,0,159,130,7,36,40,0,55,0,5,130,17,34,64,0,109,130,33,34,92,0,50,130,5,42,109,0,101,0,\n    24,0,115,0,109,0,131,130,121,32,48,130,39,36,143,0,114,0,83,130,77,32,19,130,157,34,18,0,73,130,49,32,5,136,3,32,2,130,85,33,52,0,133,1,33,66,0,133,1,32,17,130,125,32,35,130,209,131,\n    3,36,46,0,1,0,46,132,1,32,47,130,51,32,42,130,25,32,38,130,213,135,3,34,5,0,59,130,99,32,37,132,3,34,51,0,68,132,1,32,42,130,189,33,43,0,135,1,36,24,0,12,0,61,134,1,32,26,130,131,38,\n    26,0,30,0,0,0,3,134,3,32,28,130,93,130,10,35,0,108,0,3,132,9,36,28,0,4,0,80,130,16,34,16,0,16,132,35,46,126,0,133,0,171,0,210,0,211,0,255,32,172,255,255,130,25,32,32,130,17,34,161,\n    0,174,130,17,32,212,131,17,45,255,227,255,221,255,194,255,192,255,95,255,191,224,19,132,67,130,36,139,2,34,1,6,0,136,22,35,1,2,0,0,66,53,9,32,0,133,0,32,1,130,142,8,148,4,5,6,7,8,9,\n    10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,\n    70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,0,132,133,135,137,145,149,155,160,159,161,163,162,164,166,168,167,169,170,172,171,173,174,176,178,\n    177,179,181,180,185,184,186,187,0,112,100,101,105,0,118,158,110,107,0,116,106,0,134,151,0,113,0,0,103,117,132,158,38,108,122,0,165,183,127,99,132,11,38,109,123,0,0,128,131,148,132,\n    11,130,4,37,182,0,190,60,0,191,130,8,34,0,0,119,130,5,47,130,138,129,139,136,141,142,143,140,50,147,0,146,153,154,152,130,18,32,111,130,3,32,120,130,3,130,2,34,33,2,121,130,5,33,42,\n    0,133,1,9,155,68,0,86,0,134,0,210,1,14,1,104,1,116,1,148,1,180,1,212,1,232,2,6,2,20,2,38,2,54,2,100,2,122,2,172,2,232,3,2,3,60,3,116,3,134,3,210,4,10,4,40,4,82,4,102,4,122,4,142,4,\n    204,5,36,5,60,5,118,5,158,5,184,5,208,5,228,6,24,6,44,6,68,6,94,6,118,6,134,6,160,6,182,6,224,7,14,7,64,7,106,7,182,7,200,7,238,8,0,8,28,8,52,8,72,8,96,8,114,8,130,8,148,8,166,8,180,\n    8,194,9,12,9,58,9,92,9,140,9,182,9,214,10,14,10,44,10,74,10,110,10,134,10,150,10,198,10,230,11,4,11,50,11,96,11,130,11,188,11,216,11,250,12,14,12,42,12,66,12,114,12,142,12,198,12,210,\n    13,10,13,62,13,126,13,152,13,198,13,238,14,34,14,72,14,90,14,198,14,232,15,32,15,106,15,134,15,200,15,214,15,244,16,16,16,56,16,114,16,128,16,182,16,212,16,230,17,2,17,22,17,60,17,\n    86,17,132,17,194,18,20,18,76,18,108,18,140,18,176,18,228,19,24,19,74,19,110,19,168,19,198,19,228,20,6,20,56,20,86,20,116,20,150,20,200,20,252,21,44,21,92,21,144,21,212,22,24,22,50,\n    22,112,22,148,22,186,22,226,23,28,23,56,23,90,23,160,23,246,24,76,24,164,25,18,25,124,25,226,26,94,26,150,26,198,26,248,27,44,27,114,27,144,27,174,27,208,28,2,28,74,28,136,28,174,28,\n    212,28,254,29,60,29,118,29,172,29,230,30,16,30,58,30,104,30,166,30,206,30,244,31,48,31,112,0,0,0,2,0,33,0,0,1,42,2,154,0,3,0,7,0,46,177,1,0,47,60,178,7,4,0,237,50,177,6,5,220,60,178,\n    3,2,130,10,34,0,177,3,131,22,32,5,131,22,39,178,7,6,1,252,60,178,1,131,23,52,51,17,33,17,39,51,17,35,33,1,9,232,199,199,2,154,253,102,33,2,88,131,83,38,166,255,254,1,18,2,35,130,81,\n    61,13,0,0,54,50,22,20,6,34,38,52,55,35,3,55,51,21,198,44,32,32,44,32,85,59,14,1,83,105,31,131,11,36,122,1,4,91,91,130,135,38,116,1,70,1,68,2,7,132,135,37,0,1,35,53,51,7,130,3,8,40,\n    1,67,64,64,142,65,65,1,70,192,192,192,0,2,255,248,0,3,1,191,2,3,0,3,0,31,0,0,63,1,35,7,37,35,7,51,21,35,7,35,55,132,3,34,53,51,55,130,51,35,55,51,7,51,131,3,53,254,30,92,30,1,29,104,\n    31,92,106,38,59,38,92,38,58,38,96,110,31,98,112,134,11,34,90,201,115,130,0,33,54,143,130,0,35,54,115,54,144,130,0,32,0,130,85,8,37,49,255,241,1,135,2,22,0,6,0,11,0,52,0,0,55,62,1,53,\n    52,38,47,1,53,6,21,20,23,30,3,21,20,7,21,35,53,46,1,130,16,8,109,30,2,23,53,46,3,53,52,54,55,53,51,21,30,1,31,1,21,38,39,21,50,249,32,41,36,37,60,72,137,22,40,46,28,141,60,36,67,15,\n    15,6,21,69,37,31,44,43,22,75,65,60,25,55,15,15,43,67,3,81,7,37,31,30,32,10,73,135,12,61,44,30,4,15,28,50,33,114,15,44,43,1,14,6,6,63,4,11,22,3,162,6,15,26,42,29,53,67,9,45,42,2,11,\n    5,5,62,29,4,150,0,5,130,1,36,11,1,178,1,250,130,161,52,11,0,19,0,27,0,35,0,0,1,51,1,35,36,50,54,52,38,34,6,20,65,97,7,34,2,20,22,132,17,65,111,5,53,54,50,1,74,71,254,222,71,1,16,41,\n    30,30,41,29,7,84,60,60,84,59,174,130,9,34,29,41,122,130,9,51,59,84,1,249,254,21,47,27,39,27,27,39,115,56,79,56,56,79,1,26,131,11,33,27,7,131,11,8,59,56,0,0,0,3,0,4,255,246,1,179,2,\n    21,0,9,0,28,0,62,0,0,55,50,55,46,1,39,6,21,20,22,3,28,2,30,5,23,62,1,53,52,38,35,34,6,1,23,35,38,39,6,35,34,38,53,52,54,55,46,2,130,5,8,101,51,50,22,21,20,14,2,7,22,23,54,53,55,20,\n    206,52,37,25,110,24,65,80,41,1,2,3,6,8,12,7,37,37,31,23,24,35,1,2,67,72,13,21,60,89,72,104,52,44,22,19,15,63,60,61,51,15,32,25,24,89,58,36,61,44,36,28,131,28,54,59,48,62,1,125,1,7,\n    3,7,5,10,9,13,16,9,25,40,24,21,25,32,254,143,79,15,24,49,96,66,50,74,33,25,130,17,56,41,69,55,49,20,36,34,20,17,107,69,65,72,1,108,0,1,0,185,1,70,0,255,2,8,130,189,42,0,19,35,53,51,\n    254,68,68,1,71,193,130,23,8,59,135,255,193,1,49,2,62,0,17,0,0,1,14,1,20,22,23,35,46,4,52,62,2,63,1,1,49,43,55,55,43,60,4,15,38,29,24,23,32,33,11,11,2,61,60,177,161,177,60,6,21,68,70,\n    103,97,102,75,61,16,16,130,50,32,0,138,63,34,19,30,4,130,220,40,15,1,35,62,1,52,38,39,195,137,57,32,60,131,73,44,2,61,6,22,68,71,103,97,101,75,60,16,16,132,73,32,0,131,63,38,27,0,69,\n    1,157,1,199,132,127,8,41,7,23,7,39,21,35,53,7,39,55,39,55,23,53,51,21,55,1,157,131,131,33,128,64,129,32,131,131,32,129,64,128,1,77,71,71,50,70,142,142,70,50,134,7,49,0,1,0,22,0,74,\n    1,162,1,185,0,11,0,0,19,51,21,35,130,62,130,221,37,53,51,251,166,166,62,130,2,38,1,31,59,154,154,59,154,130,39,39,154,255,129,1,30,0,107,0,130,180,32,54,65,133,5,8,37,15,1,39,54,55,\n    46,1,53,52,209,45,31,21,31,30,11,11,26,55,10,17,25,107,32,22,32,61,40,32,7,7,37,48,41,1,31,21,22,131,163,54,53,0,227,1,131,1,32,0,3,0,0,37,33,53,33,1,131,254,178,1,78,228,60,132,191,\n    40,166,255,255,1,18,0,105,0,7,67,209,9,67,203,5,37,105,31,44,31,31,44,132,35,38,39,255,222,1,144,2,39,131,63,49,9,1,35,1,1,144,254,221,69,1,34,2,39,253,184,2,72,0,130,21,8,73,41,255,\n    247,1,143,2,14,0,10,0,21,0,27,0,0,55,50,62,3,53,52,39,7,22,19,34,14,3,21,20,23,55,38,39,50,16,35,34,16,220,17,28,30,21,14,4,190,27,57,17,28,31,20,14,4,189,26,57,178,178,178,51,7,26,\n    44,78,53,40,34,230,52,1,161,8,131,10,39,39,33,229,52,58,253,234,2,130,186,41,0,1,0,79,0,0,1,104,2,7,130,91,40,0,19,39,55,51,17,51,21,33,130,5,50,81,1,106,68,105,254,233,1,105,1,103,\n    81,78,254,55,61,61,1,122,130,43,32,50,130,43,37,134,2,17,0,31,0,130,43,49,62,2,51,50,30,2,21,20,6,15,1,33,21,33,53,52,55,54,67,247,5,8,58,35,34,6,7,55,1,8,29,81,37,48,72,36,17,44,59,\n    147,1,1,254,173,13,1,165,37,40,58,45,26,75,24,1,159,65,5,16,27,30,48,46,21,40,78,58,146,61,46,14,12,1,165,38,80,33,33,47,27,13,0,130,94,41,0,45,255,246,1,139,2,16,0,41,130,12,32,22,\n    130,93,38,35,34,38,47,1,53,30,130,108,40,54,53,52,38,43,1,53,51,50,131,8,130,101,37,15,1,53,54,51,50,131,35,8,62,1,26,112,107,85,34,78,22,23,7,25,80,41,57,65,65,49,64,64,46,56,55,45,\n    33,72,20,20,74,68,78,98,51,1,22,30,109,65,84,13,7,6,69,4,12,19,51,49,46,52,58,45,38,37,43,14,7,7,64,23,74,59,50,55,130,119,34,2,0,22,130,111,45,162,2,7,0,2,0,13,0,0,37,17,3,59,1,66,\n    44,6,54,19,51,1,16,179,237,88,88,67,241,208,100,181,1,18,254,238,58,123,123,73,1,66,130,46,33,0,49,130,171,32,135,130,51,37,40,0,0,19,17,33,130,47,130,143,37,30,3,21,20,14,2,137,181,\n    36,62,2,53,52,46,130,15,8,68,6,7,72,1,33,221,24,41,25,48,51,38,24,35,61,67,38,43,70,14,14,7,24,76,38,21,39,39,23,23,40,44,25,28,56,14,1,2,1,4,59,127,11,10,25,38,63,40,49,72,38,18,11,\n    6,5,71,4,11,19,11,25,48,33,33,48,25,12,13,7,130,157,55,41,255,248,1,143,2,8,0,9,0,37,0,0,55,50,53,52,35,34,6,21,20,22,19,65,139,5,130,119,42,53,52,62,2,51,50,22,31,1,21,38,131,26,8,\n    58,54,224,101,101,41,59,59,51,31,56,49,29,91,82,87,98,36,63,79,48,24,46,11,10,31,59,69,89,36,48,119,121,57,64,63,56,1,39,20,40,68,43,90,89,114,136,73,110,63,31,8,4,3,66,25,95,98,72,\n    0,130,0,38,1,0,46,0,0,1,138,130,227,32,6,130,227,8,42,53,33,21,3,35,19,46,1,91,197,78,190,1,203,59,29,254,23,1,202,0,0,3,0,39,255,246,1,145,2,17,0,15,0,31,0,52,0,0,54,50,62,2,130,243,\n    38,34,14,2,20,30,1,18,132,6,32,2,132,18,35,1,23,30,1,65,210,5,35,53,52,55,38,68,239,7,8,50,199,42,32,32,18,18,33,32,40,32,33,18,18,32,72,37,29,29,16,17,29,28,36,28,30,16,16,29,33,48,\n    52,104,76,77,104,99,84,95,71,70,96,45,7,20,42,63,42,19,7,7,19,130,6,43,20,1,166,6,17,37,53,37,16,6,6,16,130,6,59,17,191,16,72,52,69,78,77,70,107,33,30,91,62,69,70,61,90,0,2,0,41,0,\n    0,1,143,2,16,65,43,5,43,19,34,21,20,51,50,54,53,52,38,3,34,69,108,11,65,170,5,32,22,131,26,8,44,6,215,100,100,42,59,60,51,30,57,48,29,91,82,86,99,36,64,79,47,24,46,11,11,32,59,68,89,\n    35,1,216,120,120,56,64,64,56,254,217,20,39,68,44,89,90,65,44,6,35,3,4,65,24,65,44,5,55,2,0,166,0,81,1,18,1,171,0,7,0,15,0,0,18,34,38,52,54,50,22,20,6,71,199,6,38,242,44,32,32,44,32,\n    76,132,5,39,1,65,31,44,31,31,44,165,132,5,32,0,130,0,36,2,0,143,255,209,134,59,32,24,140,59,35,21,20,14,2,68,137,8,37,243,45,31,31,45,31,130,67,41,22,30,31,11,10,27,55,10,17,25,135,\n    74,37,32,22,33,60,41,31,68,150,10,8,37,0,1,0,24,0,84,1,160,1,176,0,6,0,0,19,37,21,13,1,21,37,24,1,135,254,200,1,56,254,121,1,30,145,62,111,112,62,145,132,123,38,24,0,152,1,159,1,114,\n    72,67,5,34,37,33,53,132,1,33,1,159,130,37,32,135,131,3,35,153,59,99,59,130,39,141,79,37,5,21,5,53,45,1,131,79,32,121,130,79,36,200,1,176,145,57,131,81,132,79,42,48,0,0,1,135,2,31,0,\n    34,0,42,130,121,33,50,22,130,195,44,3,29,1,35,53,52,62,4,53,52,38,35,34,132,209,34,62,4,16,65,25,6,58,226,83,82,29,42,42,29,70,21,33,38,33,22,52,43,25,45,29,22,4,5,52,2,10,34,38,61,\n    132,239,8,33,2,31,68,52,28,47,33,30,33,17,41,41,22,39,28,29,23,31,16,30,33,16,23,23,8,8,34,4,14,35,26,22,254,75,65,71,9,47,255,225,255,233,1,215,2,32,0,7,0,64,0,0,54,50,71,233,5,39,\n    5,23,14,4,35,34,46,3,71,98,10,46,21,35,53,6,35,34,38,52,54,51,50,22,31,1,50,132,151,8,122,6,21,20,30,2,51,50,62,3,214,60,43,43,60,43,1,5,38,2,11,38,43,71,37,63,103,67,46,20,141,105,\n    105,128,15,24,21,10,55,36,43,55,79,80,55,28,49,10,11,45,84,70,81,118,39,61,75,39,35,64,40,30,14,168,56,79,57,57,79,120,46,4,11,27,21,17,33,55,74,79,42,120,163,78,67,24,35,17,7,1,198,\n    19,31,93,131,94,26,13,13,28,34,57,124,104,53,87,55,30,13,20,20,13,0,0,0,2,0,5,0,0,1,178,2,7,130,9,8,38,10,0,0,55,51,3,55,19,35,39,35,7,35,19,141,158,79,45,169,77,41,193,40,77,169,192,\n    1,9,61,253,250,136,136,2,6,0,3,0,38,130,47,32,146,130,47,36,8,0,17,0,39,130,49,32,50,69,22,5,36,21,17,21,22,55,131,10,42,35,23,30,4,21,20,14,2,43,1,17,69,159,6,8,61,220,65,52,62,45,\n    121,71,39,84,49,42,77,12,23,34,25,18,30,50,57,31,194,177,43,62,32,15,49,58,47,52,47,43,189,1,147,156,1,2,4,76,42,33,183,3,7,20,27,48,31,38,57,32,14,2,5,25,43,46,24,42,55,130,110,47,\n    0,47,255,248,1,137,2,18,0,26,0,0,37,21,6,35,67,62,7,58,23,21,38,35,34,14,2,21,20,30,1,51,50,1,136,49,79,56,86,50,24,112,104,77,51,51,77,130,65,55,15,26,66,50,77,93,73,28,42,74,95,58,\n    117,151,32,71,45,33,59,74,46,62,91,59,130,233,130,183,33,1,144,130,195,34,6,0,13,131,193,33,53,52,130,172,58,19,50,17,16,35,7,17,199,125,125,84,84,201,200,160,58,202,201,254,109,1,\n    204,254,253,254,254,1,130,247,34,1,0,52,130,136,32,131,130,51,8,34,11,0,0,19,33,21,33,23,51,21,35,21,33,21,33,53,1,77,254,253,1,227,228,1,4,254,178,2,6,59,153,59,188,59,0,130,0,38,\n    1,0,58,0,0,1,126,130,47,32,9,132,47,34,35,31,1,130,47,40,35,58,1,67,249,1,181,182,74,130,41,35,158,1,59,241,130,34,33,0,35,130,219,32,148,130,219,32,36,130,219,38,39,35,53,51,21,14,\n    4,149,224,47,62,1,1,78,1,99,170,2,7,27,32,54,30,56,85,51,130,233,33,78,50,132,233,47,14,25,66,50,27,42,11,67,141,58,223,2,7,18,13,11,143,241,37,11,7,0,1,0,42,130,108,32,141,133,191,\n    57,51,17,51,21,51,53,51,17,35,53,35,21,43,75,204,75,75,204,2,6,212,212,253,250,247,246,130,39,32,66,130,39,32,117,133,39,36,19,53,33,21,35,130,43,55,5,53,55,17,67,1,50,116,116,254,\n    206,115,1,203,59,59,254,112,58,1,57,1,1,145,132,231,36,65,255,249,1,118,130,47,32,13,130,231,8,33,51,17,20,6,35,7,53,51,50,54,53,17,35,158,216,66,78,164,147,48,37,140,2,6,254,158,80,\n    90,1,57,48,58,1,47,130,50,39,0,1,0,19,0,0,1,165,137,139,59,55,51,7,19,35,3,7,21,19,75,233,85,212,221,90,180,57,2,6,230,230,212,254,206,1,4,57,202,130,48,34,1,0,49,130,47,32,135,130,\n    47,50,5,0,0,55,17,51,17,33,21,49,75,1,10,1,2,5,254,53,58,132,31,131,79,32,164,130,31,56,12,0,0,51,17,51,23,55,51,17,7,17,3,35,3,17,20,100,99,101,100,65,107,57,106,130,130,42,254,253,\n    251,1,1,197,254,239,1,19,254,130,52,34,1,0,41,130,83,32,142,65,159,5,35,51,3,51,19,130,86,130,48,38,45,3,98,186,72,95,186,130,46,55,90,1,166,253,250,1,166,254,90,0,0,2,0,35,255,250,\n    1,149,2,18,0,5,0,25,130,229,63,50,16,35,34,16,18,50,62,3,52,46,3,34,14,3,20,30,2,220,184,184,184,167,34,27,30,20,13,13,20,30,27,131,8,33,14,14,130,22,50,17,253,234,2,22,254,37,8,26,\n    43,78,106,78,44,26,8,8,26,44,130,8,33,43,26,130,179,38,2,0,44,255,255,1,139,132,179,32,30,131,83,71,166,5,45,43,1,21,55,50,22,21,20,6,43,1,28,1,30,130,3,8,47,49,35,17,50,206,32,46,\n    22,10,10,21,42,30,93,99,85,91,85,84,108,1,1,75,174,1,10,19,31,31,17,17,31,31,18,195,252,80,70,71,89,5,21,55,48,50,29,2,6,130,90,45,0,2,0,13,255,184,1,171,2,15,0,12,0,32,131,91,39,17,\n    20,7,23,21,35,39,6,143,182,39,198,184,40,84,48,82,40,58,147,187,40,14,254,245,123,67,97,44,91,27,149,193,38,2,0,38,0,0,1,145,130,191,34,9,0,24,130,99,34,35,21,22,73,125,5,35,15,1,35,\n    17,73,20,5,8,45,7,23,35,39,38,213,99,68,39,34,51,53,139,3,72,179,79,83,57,44,122,79,111,97,1,205,184,1,2,2,46,45,43,47,242,218,2,5,89,69,45,69,16,229,217,1,130,184,42,1,0,42,255,246,\n    1,141,2,17,0,54,130,91,44,21,46,2,35,34,6,21,20,30,7,23,30,3,73,135,16,32,39,69,75,6,8,95,30,2,23,1,109,6,21,67,35,62,61,6,14,11,25,12,31,9,33,1,23,42,48,29,101,89,43,78,18,18,7,27,\n    86,45,45,66,61,69,34,48,47,24,103,87,18,40,32,27,8,1,245,71,4,13,21,41,48,11,18,15,11,10,5,7,3,5,1,4,17,33,58,39,78,74,16,9,8,73,5,16,27,46,45,46,37,14,6,17,31,50,34,72,82,5,8,9,84,\n    93,5,32,9,130,143,32,175,130,235,32,7,130,233,56,53,33,21,35,17,7,17,10,1,164,172,75,1,203,59,59,254,54,1,1,203,0,1,0,37,130,187,32,147,130,35,32,24,66,163,5,32,20,74,49,5,8,50,17,\n    51,17,20,14,3,34,46,3,37,76,6,19,43,32,67,48,75,6,22,38,68,95,70,38,22,7,166,1,96,254,153,26,35,34,18,54,67,1,95,254,173,33,48,53,33,22,21,32,50,44,130,75,32,13,130,111,32,171,130,\n    75,8,32,6,0,0,51,3,51,27,1,51,3,174,161,73,134,134,72,161,2,6,254,58,1,198,253,250,0,1,255,249,0,0,1,190,130,35,32,12,135,35,131,38,47,35,11,1,75,81,63,58,63,83,64,57,64,81,73,72,73,\n    130,47,44,99,1,157,254,98,1,158,253,250,1,184,254,72,130,55,32,255,130,55,32,185,67,123,5,8,33,19,39,51,23,55,51,7,19,35,39,7,35,184,160,80,121,123,81,165,177,80,141,141,80,1,19,243,\n    195,195,243,254,237,218,218,130,139,32,5,130,47,32,178,130,47,36,8,0,0,55,3,131,47,50,3,21,35,182,176,79,135,134,80,177,75,234,1,28,228,228,254,227,233,130,34,33,0,33,130,4,32,151,\n    130,39,32,9,65,35,5,60,1,33,21,5,53,1,41,1,102,254,224,1,40,254,138,1,24,1,203,59,53,254,106,58,1,56,1,147,0,130,42,41,0,104,255,189,1,80,2,73,0,7,130,12,55,39,17,51,21,35,17,51,1,\n    79,162,162,231,231,2,18,1,253,226,56,2,140,0,1,0,76,151,10,41,19,1,35,1,109,1,35,70,254,222,76,150,7,130,31,138,67,52,19,51,17,35,53,51,17,7,104,231,231,163,163,2,73,253,116,56,2,30,\n    1,130,90,37,0,37,1,63,1,147,65,75,5,33,19,23,131,233,45,55,252,150,82,100,101,82,150,2,6,198,141,141,198,131,139,55,255,241,255,190,1,199,255,247,0,3,0,0,5,33,53,33,1,198,254,44,1,\n    212,66,56,131,27,39,0,124,1,160,1,60,2,57,131,27,131,63,39,207,108,82,109,2,57,153,153,130,27,8,175,2,0,48,255,246,1,136,1,143,0,17,0,54,0,0,55,50,62,3,53,34,35,38,14,4,21,20,22,39,\n    52,62,3,23,48,60,1,46,6,35,7,53,50,54,51,21,50,30,2,21,7,35,54,39,6,35,34,38,204,32,48,23,13,2,10,21,24,45,39,32,22,13,51,118,28,50,69,81,45,2,3,7,9,15,18,26,16,122,18,58,34,38,58,\n    52,29,1,61,2,2,38,99,64,79,44,26,33,49,27,17,1,2,3,10,18,28,20,37,35,72,36,51,28,16,1,1,14,7,17,10,16,10,12,7,5,2,54,2,1,12,33,65,47,241,25,27,61,63,0,0,2,0,50,255,247,1,134,2,49,0,\n    9,0,30,0,0,54,50,54,53,52,38,34,6,21,20,39,62,4,67,150,5,130,127,8,57,47,1,7,35,17,51,172,112,50,51,110,52,3,1,5,18,23,42,25,71,87,88,59,32,62,16,15,7,61,68,45,82,68,67,83,83,67,68,\n    212,3,8,20,16,12,102,101,98,106,29,15,15,49,2,47,0,1,0,59,130,91,32,125,130,239,35,20,0,0,1,131,84,36,22,51,50,55,21,72,197,7,8,34,23,21,38,1,13,65,72,72,65,62,49,47,78,85,110,110,\n    85,78,47,49,1,89,74,76,76,74,43,65,32,106,196,106,32,65,43,139,159,32,7,133,159,133,158,38,19,51,17,35,39,14,4,130,155,8,35,53,52,54,51,50,30,2,31,1,122,200,48,104,48,200,68,62,6,3,\n    8,26,27,41,20,59,88,87,70,25,42,24,17,3,4,45,150,131,158,51,1,109,253,209,49,3,7,21,15,13,106,98,101,102,12,17,17,6,7,0,130,0,34,2,0,37,130,163,32,147,130,163,37,5,0,26,0,0,19,130,\n    165,35,51,52,7,20,130,168,32,54,133,169,132,95,8,42,22,29,1,32,227,47,69,224,227,62,67,42,62,46,70,82,89,109,105,79,91,90,254,218,1,89,68,48,116,165,56,79,18,21,64,29,109,95,93,110,\n    97,73,48,130,83,42,1,0,40,0,3,1,144,2,36,0,19,132,247,37,29,1,51,21,35,17,130,1,33,53,51,130,77,61,59,1,21,1,40,35,31,125,125,68,122,122,65,64,108,1,237,25,37,75,57,254,217,1,39,57,\n    52,78,62,55,133,147,36,50,255,70,1,134,130,147,32,9,73,49,5,77,51,8,44,51,17,20,6,43,1,53,51,50,62,2,61,1,137,254,8,60,22,23,222,99,99,53,51,51,159,61,90,81,130,136,28,42,23,10,1,5,\n    18,24,42,25,71,91,91,60,48,52,28,53,142,150,82,68,65,77,1,80,254,110,86,86,58,22,36,37,18,59,2,6,16,12,10,101,100,98,101,20,29,130,162,41,0,62,0,1,1,122,2,49,0,18,74,253,5,8,45,17,\n    35,53,52,38,35,34,6,29,1,35,17,51,21,54,253,53,72,68,40,37,47,56,68,68,37,1,146,78,67,255,0,250,45,48,61,59,223,2,47,218,60,0,2,0,139,130,59,38,45,2,30,0,11,0,17,130,74,8,40,35,34,\n    61,1,52,59,1,50,29,1,20,7,53,51,17,35,17,1,35,56,10,10,56,9,161,161,68,1,202,10,64,9,9,64,10,127,59,254,123,1,74,130,232,34,2,0,100,130,231,32,84,130,59,34,12,0,24,130,121,130,47,32,\n    20,132,221,35,54,53,17,55,138,72,39,165,174,127,112,105,29,37,59,132,72,43,1,76,58,254,98,161,59,52,50,1,100,126,133,79,39,0,1,0,41,0,2,1,142,132,131,38,0,55,23,35,39,7,21,130,183,\n    53,17,55,51,210,188,81,154,51,70,70,181,80,245,243,201,45,156,2,27,254,201,161,130,118,130,47,32,133,130,179,36,51,2,8,0,5,130,117,131,164,41,35,133,173,69,104,2,7,253,250,1,72,63,\n    5,46,20,255,254,1,164,1,143,0,30,0,0,1,50,22,21,130,198,36,52,35,34,6,7,138,7,48,51,21,54,51,50,23,62,1,1,68,43,52,61,52,26,26,3,132,4,52,62,62,31,42,52,26,13,48,1,143,69,53,254,234,\n    1,5,84,34,37,254,238,130,6,42,33,36,254,236,1,133,18,29,49,22,27,130,128,32,1,65,111,5,33,1,147,65,111,20,32,23,65,111,9,33,64,4,65,112,12,35,1,133,48,60,66,31,5,42,42,255,247,1,141,\n    1,144,0,9,0,17,67,179,11,32,18,130,171,8,40,20,32,53,52,174,92,58,59,90,59,21,166,94,254,158,44,72,80,80,70,70,80,80,1,28,102,102,205,205,102,0,2,0,50,255,78,1,134,1,136,0,67,79,6,\n    38,54,53,52,34,21,20,19,131,57,8,62,6,35,34,46,2,47,1,21,35,17,51,23,62,4,176,103,48,199,107,66,89,87,71,25,41,24,18,3,3,68,61,7,1,6,20,25,41,38,82,67,148,148,67,1,16,105,99,101,103,\n    12,18,17,6,6,221,2,48,49,2,8,20,16,13,152,91,35,51,17,35,53,67,171,13,38,160,104,48,200,216,62,68,66,165,5,41,70,87,89,66,23,41,26,19,4,4,134,91,44,6,253,208,221,2,8,21,15,13,103,101,\n    99,105,132,99,36,0,0,1,0,69,130,4,36,115,1,142,0,19,130,7,35,50,22,31,1,71,239,5,33,29,1,130,173,8,36,7,62,1,1,12,29,52,11,11,4,16,56,31,56,71,68,68,2,17,69,1,142,15,7,8,65,5,12,20,\n    60,35,245,1,133,53,27,35,130,54,46,0,69,255,254,1,115,1,151,0,38,0,0,55,30,1,130,250,34,39,46,1,80,126,9,39,46,2,35,34,21,20,22,23,79,243,7,8,78,47,1,69,53,109,71,91,83,57,14,32,65,\n    44,33,62,14,15,6,19,63,33,90,36,57,70,69,88,73,31,70,20,20,88,27,14,32,35,49,15,15,53,44,22,38,36,20,11,6,6,62,4,11,17,57,25,29,8,11,46,55,57,66,12,6,6,0,0,0,1,0,87,0,6,1,97,2,4,0,\n    17,130,115,41,51,21,35,34,46,3,53,17,51,21,130,9,57,21,20,216,136,125,25,33,41,24,16,68,162,162,65,58,3,15,26,50,34,1,124,118,58,204,70,132,55,40,54,255,246,1,130,1,133,0,21,130,55,\n    33,53,51,80,10,5,34,55,51,19,65,74,6,8,32,54,68,86,44,62,3,68,1,64,1,3,17,23,45,27,79,73,145,244,241,102,53,39,251,254,124,42,2,7,19,13,12,74,132,67,32,29,130,128,32,155,130,67,53,\n    6,0,0,1,51,3,35,3,51,19,1,84,70,148,86,147,71,120,1,133,254,123,130,3,32,186,130,38,39,0,1,255,248,255,255,1,192,130,39,33,12,0,132,39,33,39,7,131,42,48,55,51,23,1,124,67,97,63,68,\n    65,65,97,68,68,53,76,60,130,47,34,123,215,215,130,5,34,208,192,192,132,55,33,0,20,130,55,32,163,130,55,35,11,0,0,37,71,43,5,57,39,51,23,55,51,1,1,162,78,122,120,78,162,149,76,110,109,\n    76,203,203,155,155,204,185,141,141,77,151,5,38,68,1,137,1,134,0,31,130,12,32,18,77,244,5,44,53,51,50,62,4,53,52,53,35,34,38,53,19,130,230,59,22,51,23,3,1,134,3,12,24,47,32,195,176,\n    16,23,13,8,3,1,133,71,72,1,68,29,41,137,1,130,122,56,135,48,17,47,52,36,59,12,24,22,34,17,14,4,2,78,49,1,6,229,49,50,1,1,73,132,239,32,64,130,143,32,119,130,95,8,41,14,0,0,55,51,23,\n    33,53,62,3,39,35,53,33,21,2,137,237,1,254,202,15,50,105,69,1,229,1,45,238,52,52,59,17,58,122,82,1,50,60,254,235,130,54,8,32,0,1,0,63,255,178,1,120,2,84,0,41,0,0,1,21,20,7,22,29,1,20,\n    22,59,1,21,34,35,34,46,3,61,1,83,197,7,130,9,33,62,3,130,22,8,61,35,34,6,1,11,72,72,38,42,29,31,13,38,54,27,14,3,38,43,51,51,43,38,3,14,27,54,38,44,29,42,38,1,215,100,95,17,17,96,99,\n    34,29,62,20,26,44,30,23,86,43,32,66,32,42,87,23,30,44,26,19,62,29,130,98,53,0,188,255,200,0,252,2,63,0,3,0,0,23,35,17,51,252,64,64,55,2,117,141,135,32,19,84,60,5,36,50,51,50,30,3,134,\n    142,130,119,130,9,33,14,3,69,35,5,130,141,55,55,38,172,37,42,29,30,14,37,54,27,15,2,38,44,51,51,44,38,2,15,27,54,37,130,132,41,37,72,72,1,115,100,33,29,62,19,131,129,33,87,42,130,129,\n    45,43,86,24,29,44,26,20,62,29,33,100,96,17,16,130,248,42,1,0,22,0,177,1,161,1,93,0,33,130,148,34,23,14,4,131,238,39,7,14,1,15,1,39,62,4,131,119,54,55,62,1,55,1,103,58,5,5,17,21,40,\n    25,25,43,30,28,31,16,20,25,3,3,143,16,62,1,66,13,21,21,45,24,20,26,35,36,21,2,3,45,21,21,13,21,22,44,24,21,26,36,35,22,3,3,45,20,130,229,32,21,130,95,45,163,0,106,0,15,0,31,0,47,0,\n    0,55,50,22,130,200,36,6,43,1,34,38,130,197,34,54,59,1,157,15,43,51,104,5,7,7,5,71,4,8,8,4,222,131,9,32,70,131,4,32,221,136,9,35,105,7,5,81,131,12,150,4,42,0,0,2,0,159,255,235,1,25,\n    2,9,90,167,5,33,18,34,82,231,5,59,7,51,23,7,35,53,245,50,35,35,50,35,84,46,29,1,101,1,145,35,49,35,35,49,105,196,155,155,130,51,42,63,255,147,1,121,1,242,0,5,0,28,130,177,48,17,14,\n    1,21,20,19,17,54,55,21,6,7,21,35,53,46,1,90,19,6,8,62,22,23,21,38,243,50,58,146,57,39,45,51,38,78,102,100,80,38,61,35,40,49,1,38,6,73,67,134,1,25,254,215,2,25,59,21,3,100,101,6,107,\n    92,95,104,4,97,98,5,20,60,25,0,1,0,40,255,255,1,144,2,16,0,27,130,89,38,51,21,33,53,51,53,35,130,3,81,218,6,32,21,71,122,5,8,39,51,21,35,202,197,254,153,87,73,73,82,79,33,52,9,10,48,\n    42,50,50,136,136,59,59,59,161,50,82,88,88,10,5,5,64,30,53,63,88,50,0,131,223,38,55,0,63,1,128,1,125,130,223,35,31,0,0,54,82,67,6,8,41,54,20,7,23,7,39,6,34,39,7,39,55,38,52,55,39,55,\n    23,54,50,23,55,23,7,187,65,46,46,65,46,204,22,60,34,62,31,73,31,62,34,60,22,137,10,49,148,44,61,43,43,61,67,73,30,57,35,60,19,19,60,35,57,30,137,10,75,219,10,36,24,0,0,1,7,130,160,\n    33,7,21,88,161,9,32,39,130,194,8,37,39,51,23,55,1,178,120,91,115,33,148,148,75,148,148,33,115,91,119,79,135,134,2,6,194,39,53,11,39,182,182,39,11,53,39,194,228,228,130,178,41,0,2,0,\n    188,255,206,0,252,2,57,83,195,5,36,23,35,17,51,53,67,9,5,40,64,64,50,1,8,92,1,7,0,130,35,59,64,255,188,1,119,2,16,0,15,0,75,0,0,37,62,1,53,52,38,39,38,39,14,1,21,20,22,23,69,167,11,\n    33,53,22,85,40,5,130,28,35,46,4,53,52,89,5,5,32,54,86,84,10,133,47,8,53,4,21,20,6,1,10,21,28,48,46,25,22,22,27,48,45,25,61,25,21,69,72,25,57,16,16,58,51,36,42,37,47,8,3,28,25,39,17,\n    14,38,33,25,21,68,74,24,54,14,14,51,51,37,40,36,130,22,33,29,24,131,22,42,142,12,35,15,26,44,25,14,13,12,36,132,7,63,37,18,34,23,44,66,10,5,5,57,26,32,25,20,30,26,4,2,16,14,28,23,34,\n    19,28,50,16,18,34,24,43,59,131,25,33,27,26,133,25,39,15,15,28,23,33,20,28,50,133,251,42,109,1,212,1,75,2,27,0,11,0,23,73,51,13,33,43,1,73,63,9,39,1,66,58,9,9,58,9,155,131,5,37,8,1,\n    213,9,52,9,136,2,48,0,0,3,255,248,0,40,1,192,1,218,0,7,0,15,0,36,65,245,9,33,18,50,92,21,5,33,22,52,130,255,35,23,21,38,35,75,119,11,8,53,142,156,111,111,156,111,95,188,133,133,188,\n    134,92,78,62,57,30,43,34,47,52,52,47,46,31,36,51,61,79,105,147,105,105,147,1,34,127,179,127,127,179,152,126,66,15,38,21,48,49,49,47,18,36,17,131,179,8,41,3,0,92,0,162,1,91,2,18,0,3,\n    0,18,0,53,0,0,37,35,53,51,39,34,35,34,14,4,21,20,22,51,50,54,7,52,62,2,23,60,1,46,2,88,232,5,8,122,62,6,51,50,22,29,1,35,39,6,35,34,38,1,91,247,247,55,5,8,25,24,37,19,20,8,36,22,49,\n    41,201,37,58,70,33,8,15,32,22,26,50,11,12,2,18,7,17,12,17,18,9,57,73,45,8,39,54,47,59,163,43,178,1,3,6,11,18,13,24,24,61,16,33,43,14,7,4,8,10,22,14,11,11,6,6,44,1,6,2,6,2,3,1,57,64,\n    159,38,45,44,0,2,0,50,0,49,1,134,1,112,0,6,0,13,0,0,1,21,7,23,21,39,53,55,133,5,50,1,134,112,112,174,7,111,111,173,173,1,112,67,93,93,66,145,28,79,132,5,35,146,0,0,4,65,59,8,56,9,0,\n    27,0,35,0,43,0,0,19,22,62,2,53,52,38,43,1,23,30,1,23,35,38,39,130,9,32,21,130,224,36,50,21,20,14,1,67,78,6,65,88,7,54,175,21,36,27,15,29,21,49,86,14,25,46,52,30,17,21,24,27,47,92,103,\n    34,147,65,89,10,53,1,13,1,2,6,18,15,19,20,102,5,33,72,44,26,33,103,242,74,21,32,172,65,94,11,130,172,41,0,109,1,183,1,75,1,235,0,3,130,12,130,106,38,1,74,220,220,1,183,51,66,31,5,45,\n    101,1,50,1,82,2,16,0,7,0,15,0,0,18,93,253,14,56,190,59,42,42,59,41,22,98,69,69,98,69,1,94,39,55,39,39,55,138,65,91,65,65,91,133,59,40,24,255,255,1,159,1,151,0,11,131,59,92,65,11,56,\n    19,33,53,33,251,164,164,62,165,165,62,164,254,121,1,135,1,36,59,115,115,59,114,254,105,87,43,5,40,115,1,133,1,69,2,173,0,24,130,143,130,245,37,7,51,21,35,53,54,85,161,5,8,47,34,6,15,\n    1,53,54,51,50,22,1,66,17,62,70,152,210,77,32,42,37,28,17,42,12,13,39,51,57,58,2,94,20,33,64,61,39,38,72,29,40,36,18,25,11,6,6,41,20,130,197,32,1,130,223,38,130,1,75,2,176,0,39,131,\n    79,32,6,88,180,10,91,20,23,8,51,7,22,1,75,68,55,21,49,15,14,50,42,26,48,43,31,31,31,29,36,30,27,21,44,12,12,47,44,49,63,33,30,71,1,214,37,47,7,4,4,42,18,26,25,25,27,38,22,21,18,22,\n    8,130,13,38,13,42,33,28,29,7,13,130,252,39,1,0,131,1,181,1,53,2,79,123,5,41,51,7,35,235,73,120,56,2,57,131,130,26,51,0,1,0,37,255,107,1,146,1,132,0,36,0,0,37,50,54,63,1,21,133,147,\n    33,14,4,130,154,36,39,21,35,17,51,66,199,5,8,63,53,55,51,16,21,20,1,120,7,12,4,3,27,29,19,26,4,3,1,4,16,22,42,27,26,47,11,60,64,45,37,43,59,3,65,49,4,2,2,52,15,26,13,13,2,7,18,14,11,\n    28,26,192,2,24,241,48,53,53,38,251,254,226,24,29,130,108,42,1,0,48,255,185,1,136,2,7,0,16,76,95,7,47,16,21,6,34,47,1,17,34,38,52,54,216,175,52,70,1,130,60,8,34,69,99,99,2,6,253,182,\n    2,32,253,229,5,2,1,1,1,39,85,121,85,0,0,1,0,159,0,198,1,25,1,63,0,7,0,0,66,114,7,39,195,50,35,35,50,35,1,63,130,4,33,35,50,131,35,36,143,255,119,1,41,130,43,130,95,35,33,22,21,20,65,\n    92,8,52,39,48,58,1,1,0,40,91,14,31,8,8,24,27,91,73,21,22,41,37,59,130,173,34,46,11,94,132,151,40,114,1,132,1,70,2,167,0,10,131,151,37,21,35,53,51,53,7,130,239,56,250,75,204,76,83,85,\n    51,1,171,38,38,212,14,40,13,0,3,0,83,0,162,1,101,2,16,130,9,37,13,0,21,0,0,37,130,41,8,47,2,34,6,21,20,22,50,54,53,52,22,32,53,52,54,50,22,21,1,86,249,249,88,69,46,45,71,45,56,254,\n    239,73,127,73,163,43,1,25,47,54,54,49,49,54,54,197,143,71,130,0,130,114,67,163,15,33,55,21,130,112,35,39,53,51,23,132,7,48,223,173,111,111,166,174,174,112,112,222,28,145,66,93,93,67,\n    146,132,6,41,0,4,0,19,255,162,1,165,2,64,130,127,36,6,0,17,0,28,130,242,38,23,5,39,23,51,53,55,70,111,8,33,55,3,130,72,32,51,131,14,8,44,53,51,1,155,9,254,120,9,198,102,51,43,43,50,\n    144,134,187,82,87,48,75,203,75,1,47,37,91,37,203,144,43,188,37,65,65,42,183,1,86,14,39,13,252,39,39,130,81,137,91,34,28,0,39,133,89,32,5,66,236,15,34,39,54,51,130,224,34,20,6,1,143,\n    100,34,1,73,86,66,254,7,42,41,13,12,1,39,51,57,59,28,254,252,138,113,52,190,77,39,39,71,30,39,36,19,25,12,6,6,42,19,51,27,23,45,1,226,134,123,34,4,0,18,132,215,32,70,134,215,32,56,\n    133,125,34,23,51,39,138,215,33,39,20,67,43,10,67,42,26,132,243,34,202,102,1,130,244,8,32,49,145,134,45,72,55,21,48,13,13,41,46,36,42,38,34,33,30,31,37,33,25,24,45,10,10,46,44,50,62,\n    33,30,72,65,11,13,8,68,168,36,47,7,4,4,41,18,29,47,28,37,23,21,20,20,8,4,4,42,12,41,32,29,29,8,14,0,0,2,0,73,255,247,1,111,2,27,0,30,0,38,0,0,55,50,54,63,1,21,14,2,35,34,38,53,52,62,\n    4,61,1,51,21,20,14,3,21,20,22,73,68,7,8,56,235,29,66,18,19,7,24,69,34,82,78,19,29,34,29,19,71,26,37,38,26,47,71,44,32,32,44,32,48,24,11,12,65,5,12,21,65,54,24,43,30,30,24,31,17,69,\n    61,30,48,34,31,39,21,31,36,1,129,91,1,6,45,3,0,5,0,0,1,179,2,131,0,3,0,6,0,130,128,36,1,35,39,51,3,90,85,9,37,1,9,56,84,68,52,90,90,9,46,2,38,92,254,61,1,10,61,253,250,135,135,2,6,\n    0,130,53,143,63,34,7,35,55,139,63,36,59,84,56,72,106,132,63,39,40,193,41,77,169,2,130,92,140,64,36,3,0,5,255,255,132,127,32,6,79,77,5,32,19,130,63,34,51,23,35,138,66,38,220,61,51,78,\n    69,78,51,90,223,5,133,68,33,101,62,130,69,32,151,138,134,131,135,33,255,254,130,71,61,123,0,20,0,23,0,31,0,0,19,34,6,21,35,52,54,51,50,22,51,50,54,53,55,51,20,6,35,34,38,138,85,49,\n    169,12,12,47,36,34,26,62,15,11,13,1,46,34,36,28,58,45,138,164,45,81,19,16,33,43,34,17,9,8,25,51,35,254,109,139,103,32,4,136,175,38,2,0,10,0,22,0,34,91,131,12,81,53,11,72,74,11,138,\n    187,32,147,72,84,9,33,9,190,136,89,32,56,72,93,13,32,0,136,207,38,149,0,2,0,14,0,30,132,101,52,39,20,22,23,51,62,1,53,52,38,34,6,23,19,35,39,35,7,35,19,38,68,11,5,32,20,130,97,52,56,\n    18,16,44,16,19,33,47,34,112,159,77,40,193,41,77,159,47,60,84,60,130,98,55,110,17,26,6,6,26,17,21,31,31,101,254,25,135,135,1,231,28,51,40,55,55,40,51,130,89,38,2,255,255,1,182,2,7,130,\n    109,32,19,130,97,40,17,35,3,55,21,51,21,35,53,131,88,8,40,37,7,35,23,55,7,235,37,75,186,128,202,127,37,68,150,1,23,1,122,1,111,1,190,1,13,254,244,55,188,59,134,133,2,5,2,61,153,1,60,\n    0,78,87,5,38,119,1,137,2,16,0,40,130,12,91,224,8,49,55,21,6,7,22,21,20,6,35,34,39,53,22,51,50,53,52,39,88,3,6,8,66,23,21,38,1,8,38,57,32,14,25,66,50,77,51,37,56,33,45,38,45,25,24,31,\n    46,26,54,81,48,23,112,104,77,51,51,1,213,32,60,74,46,62,91,59,46,73,21,5,36,32,31,30,9,45,11,31,22,32,3,42,74,93,56,117,151,32,71,45,130,187,38,52,0,3,1,132,2,135,71,103,5,8,41,55,\n    33,21,33,17,33,21,35,31,1,21,35,19,35,39,51,127,1,4,254,178,1,71,253,1,241,242,142,57,84,68,63,59,2,6,59,153,1,59,1,48,93,130,175,36,2,0,52,0,4,150,59,34,7,35,55,138,59,36,173,84,56,\n    72,64,135,59,33,140,93,130,60,135,59,32,136,130,119,34,18,0,0,140,119,66,185,5,138,62,38,86,61,52,78,70,78,52,136,65,33,110,62,131,66,33,0,3,130,127,32,0,130,127,32,133,130,67,34,23,\n    0,35,74,129,25,32,3,134,213,35,23,51,21,35,74,141,11,33,9,58,137,222,39,2,63,8,52,9,9,52,8,133,5,33,253,252,132,173,42,59,0,2,0,66,255,254,1,118,2,131,65,31,5,8,49,1,35,17,23,21,33,\n    53,51,17,35,53,33,39,35,39,51,1,117,115,115,254,206,116,116,1,50,106,56,84,68,1,202,254,111,1,57,59,1,144,59,33,92,0,0,0,2,0,66,0,3,130,59,65,91,6,130,59,32,51,132,59,32,39,130,59,\n    130,227,56,1,117,116,116,254,206,115,115,1,50,60,84,56,72,1,207,254,112,59,57,1,145,1,59,125,65,31,5,133,59,65,31,6,143,59,34,51,23,35,137,62,32,148,65,30,5,137,65,32,95,65,31,7,137,\n    187,65,31,28,34,23,35,17,131,153,32,55,131,213,65,31,12,32,188,135,161,33,2,61,67,88,11,32,115,132,170,34,1,145,59,131,227,45,17,0,4,1,167,2,11,0,18,0,37,0,0,55,92,29,5,8,79,43,1,21,\n    51,21,35,21,50,19,50,30,3,20,14,3,43,1,53,35,53,51,53,50,200,19,36,42,30,20,20,30,42,36,19,62,99,99,62,1,31,56,61,45,29,29,45,61,56,30,139,46,46,138,62,9,27,42,75,98,75,42,27,8,162,\n    53,188,1,205,12,35,55,95,125,96,54,35,11,245,53,220,131,103,42,42,0,0,1,142,2,130,0,20,0,30,68,149,22,36,23,51,17,35,3,130,2,33,51,19,68,148,16,41,140,72,95,186,71,4,98,186,2,88,68,\n    145,10,32,82,92,232,5,37,2,6,254,90,0,3,92,235,6,38,135,0,5,0,9,0,29,92,237,7,35,55,35,39,51,92,241,16,37,238,57,84,68,3,33,92,245,5,34,29,28,34,133,8,42,31,2,17,253,234,2,22,25,93,\n    253,175,92,247,19,135,95,32,136,130,95,32,12,92,157,5,36,16,35,34,16,55,66,235,5,32,2,93,84,15,32,184,65,210,5,32,77,150,101,37,88,62,93,93,254,11,151,103,42,251,1,149,2,129,0,20,0,\n    26,0,46,65,41,14,33,55,53,69,191,5,33,23,50,93,202,16,38,169,11,13,47,36,35,25,69,199,6,39,35,29,58,34,184,184,184,168,145,127,32,87,65,64,10,32,70,131,241,44,254,37,8,26,44,78,105,\n    78,44,27,7,7,27,132,8,36,26,0,0,0,4,65,79,6,131,239,36,17,0,29,0,49,65,81,7,32,37,87,16,10,69,218,11,65,101,16,33,1,30,69,225,10,32,19,145,136,32,17,131,124,32,49,66,150,11,33,253,\n    244,65,17,17,8,32,1,0,46,0,65,1,137,1,137,0,11,0,0,1,7,23,7,39,7,39,55,39,55,23,55,1,137,129,129,45,128,129,45,130,5,38,129,128,1,94,121,121,42,135,2,47,0,3,0,1,255,234,1,183,2,34,\n    0,10,0,21,0,39,103,247,10,46,39,20,23,55,38,35,34,14,3,37,7,22,21,16,35,80,146,5,51,53,16,51,50,23,55,220,17,27,30,20,13,7,184,26,49,7,185,26,59,132,12,40,1,70,65,30,184,83,46,54,36,\n    133,6,32,54,131,157,54,53,56,42,248,58,208,56,42,249,58,8,26,44,78,205,88,65,105,254,245,56,72,25,130,7,48,1,11,56,72,0,0,2,0,46,255,246,1,138,2,131,0,17,130,123,40,0,1,51,17,20,35,\n    34,53,17,130,6,8,44,22,51,50,62,2,53,3,35,39,51,1,62,75,170,176,75,39,61,30,40,19,7,43,57,84,69,2,6,254,160,176,189,1,83,254,160,67,52,18,33,36,25,1,135,92,161,71,34,7,35,55,139,71,\n    35,12,84,57,72,144,71,35,227,92,92,0,130,0,139,147,33,24,0,146,147,66,196,5,139,78,32,98,66,191,5,139,81,33,17,34,130,153,33,198,62,130,82,34,3,0,47,136,227,34,29,0,41,146,81,32,19,\n    66,36,22,44,1,62,76,171,176,75,40,61,30,39,19,7,5,66,30,10,144,103,32,158,72,7,15,32,2,72,111,6,35,130,0,8,0,86,29,5,38,21,35,55,3,51,23,19,130,187,44,1,98,80,177,75,1,177,79,135,90,\n    84,57,73,130,74,39,227,233,234,1,28,229,1,97,130,170,41,0,2,0,42,255,255,1,142,2,6,130,55,32,21,99,245,10,8,41,55,50,22,21,20,43,1,21,35,17,51,21,50,210,49,61,63,47,93,103,83,95,178,\n    103,75,75,103,186,43,48,47,42,180,240,75,74,150,127,2,6,92,0,130,222,130,67,32,242,130,67,8,127,28,0,48,0,0,19,20,30,3,21,20,14,1,38,39,53,22,51,50,54,53,52,46,3,53,52,62,2,63,1,38,\n    54,46,2,35,34,6,21,17,35,17,52,54,51,50,21,34,6,234,34,48,48,33,51,78,88,38,49,51,41,45,33,47,47,33,24,35,34,12,12,1,3,10,14,38,27,46,41,68,73,82,155,47,72,1,50,20,33,27,32,53,35,43,\n    57,20,5,16,56,21,36,29,24,37,25,27,45,30,28,44,24,15,2,2,1,15,23,22,16,48,50,254,123,1,133,77,74,152,44,130,138,41,0,3,0,38,255,245,1,146,2,69,130,9,34,18,0,61,130,156,49,35,39,51,\n    19,50,62,2,39,48,42,1,35,14,1,21,20,22,55,130,3,50,31,1,35,38,39,14,1,35,34,38,53,52,54,59,1,60,2,46,5,81,15,9,8,99,1,12,57,118,72,24,36,52,26,10,1,36,47,11,60,52,51,225,10,5,5,68,\n    9,7,21,76,38,65,80,91,91,92,2,6,8,15,20,30,18,38,71,16,17,3,25,11,24,17,24,26,13,168,1,194,131,253,230,29,47,50,26,2,32,44,38,36,183,116,25,55,15,15,22,37,32,37,64,60,62,70,1,15,9,\n    18,13,16,11,10,5,18,10,9,63,1,9,3,8,3,4,2,149,171,35,7,35,55,3,131,171,33,42,2,167,170,37,84,118,57,103,80,37,131,170,33,1,35,167,171,34,2,69,131,176,172,65,87,9,39,6,0,21,0,64,0,0,\n    19,130,171,34,51,23,35,132,174,65,90,41,39,220,66,52,90,56,90,52,97,65,92,45,38,2,26,88,131,131,254,105,65,94,20,33,63,69,65,94,20,135,175,46,51,0,24,0,39,0,82,0,0,1,34,46,2,35,34,\n    75,234,5,42,30,1,51,50,54,61,1,51,20,14,2,174,191,53,1,13,21,31,16,23,11,26,45,36,37,21,34,30,13,12,13,46,5,13,30,101,173,205,50,1,209,17,20,17,54,43,55,27,26,26,13,14,15,29,33,21,\n    254,90,148,217,66,56,24,32,4,66,227,6,40,29,0,11,0,23,0,38,0,81,74,25,25,32,19,66,77,45,32,63,68,115,10,32,6,66,85,46,33,1,214,68,147,11,33,254,85,181,211,52,114,0,7,0,15,0,58,0,73,\n    0,0,18,34,6,20,22,50,54,52,6,34,88,7,5,32,19,67,183,31,67,58,12,44,240,46,33,33,46,33,14,84,60,60,84,60,63,67,191,33,32,194,139,237,46,2,70,31,44,31,31,44,118,56,80,56,56,80,254,249,\n    67,204,32,33,254,155,136,236,67,215,5,55,5,255,245,1,179,1,143,0,8,0,19,0,89,0,0,1,34,6,23,51,54,39,46,1,130,161,33,1,39,85,233,5,40,37,21,35,28,2,30,5,51,22,79,184,7,34,46,2,47,96,\n    42,8,32,59,113,200,6,83,206,5,8,164,30,2,31,1,62,4,51,50,22,23,30,1,21,1,56,31,31,3,121,1,2,3,27,217,26,26,14,1,28,53,44,37,1,75,182,2,4,6,10,14,21,12,34,52,9,10,5,15,48,25,25,42,22,\n    16,2,3,1,4,16,21,36,22,57,60,70,68,50,42,30,19,46,13,14,44,65,19,30,18,12,2,2,1,3,14,19,36,22,58,48,8,2,1,1,90,54,64,23,22,35,38,254,209,18,59,59,32,38,35,31,136,1,1,28,10,28,13,23,\n    11,14,6,1,17,9,10,60,3,10,16,11,17,16,6,5,2,7,19,15,12,60,56,57,65,28,50,39,14,8,7,58,24,9,13,13,4,5,2,6,15,11,10,59,76,18,42,12,130,248,42,1,0,59,255,119,1,125,1,142,0,37,132,243,\n    42,21,20,22,51,50,55,21,14,1,15,1,77,223,14,130,222,34,54,51,50,98,88,10,35,14,43,15,15,77,222,8,33,82,106,98,99,5,56,88,74,76,76,74,43,65,11,15,2,2,36,32,31,30,8,46,12,32,22,31,3,\n    105,96,98,98,114,5,41,0,3,0,37,255,247,1,147,2,69,130,9,34,9,0,30,69,235,5,32,23,98,25,22,42,21,23,32,1,19,57,118,72,55,46,70,98,30,12,40,92,89,1,254,217,1,194,131,236,98,33,17,145,\n    95,35,7,35,55,7,154,95,36,93,119,56,102,49,147,95,34,2,69,131,147,96,33,0,0,138,195,36,6,0,12,0,33,69,87,8,154,102,39,225,67,52,90,57,90,52,64,148,104,36,26,88,131,131,105,145,202,\n    33,0,4,65,43,6,68,51,5,34,29,0,50,68,51,25,65,65,27,44,69,57,9,9,57,9,155,58,9,9,58,9,39,147,128,68,5,13,32,125,146,137,8,58,0,0,2,0,51,255,252,1,117,2,59,0,13,0,17,0,0,37,51,21,34,\n    35,34,38,61,1,35,53,51,21,20,17,35,39,51,1,37,80,24,71,54,65,90,158,57,119,73,51,54,78,63,201,50,251,86,1,132,131,0,130,0,34,2,0,68,130,59,32,116,149,59,48,19,7,35,55,1,37,79,23,72,\n    53,66,90,158,106,119,57,103,135,60,34,2,7,131,130,61,135,59,36,69,0,6,0,20,65,105,8,32,19,140,126,39,186,66,52,90,56,90,51,40,134,68,70,121,5,32,113,134,131,130,127,32,3,134,127,38,\n    26,0,11,0,23,0,37,65,69,25,141,86,33,1,44,65,56,5,132,5,32,130,134,92,33,1,212,69,49,12,32,95,135,101,54,2,0,42,255,244,1,142,2,27,0,14,0,47,0,0,55,50,54,53,52,38,47,1,90,34,6,38,19,\n    20,30,6,21,20,6,72,186,5,8,96,23,46,1,47,1,7,39,55,39,51,23,55,23,7,22,220,45,60,20,10,10,18,50,43,59,60,145,20,6,19,8,13,6,5,89,88,88,89,107,100,5,27,10,11,103,12,88,67,81,47,106,\n    13,93,36,42,70,71,38,67,15,14,9,69,73,73,69,1,89,1,27,10,29,19,33,29,38,19,86,107,107,94,93,102,8,8,31,12,11,31,34,27,69,49,32,34,28,34,65,115,5,32,62,130,231,44,122,2,32,0,18,0,43,\n    0,0,19,50,22,21,100,121,12,34,23,54,55,71,88,17,54,35,34,46,2,253,53,72,68,39,38,47,56,68,64,4,37,3,26,45,36,37,20,35,71,51,5,40,14,29,21,21,31,17,22,1,145,100,165,9,51,1,132,47,60,\n    99,54,44,54,27,26,26,14,13,15,29,33,21,17,20,17,130,124,40,3,0,43,255,244,1,141,2,69,86,227,7,36,1,35,39,51,18,102,253,8,99,73,7,37,1,15,56,119,73,5,99,78,9,37,1,194,131,253,229,71,\n    99,82,9,35,204,204,102,0,130,65,143,75,35,7,35,55,2,145,75,36,86,118,57,103,96,137,75,34,2,69,131,145,76,137,151,36,6,0,16,0,24,66,77,8,145,78,72,242,6,32,112,138,80,37,26,88,131,131,\n    254,104,143,158,136,159,32,32,130,79,34,34,0,42,72,179,24,145,99,53,1,15,20,32,16,22,12,25,46,36,38,20,35,29,14,12,13,46,5,14,30,118,137,113,36,1,190,17,20,17,65,92,11,33,254,108,142,\n    125,32,4,65,103,6,66,215,5,34,33,0,41,66,217,25,146,126,93,1,10,33,9,11,138,118,66,224,13,32,86,142,115,32,3,130,231,36,49,1,159,1,136,130,249,42,19,0,35,0,0,37,33,53,33,39,35,96,108,\n    13,32,3,142,15,48,1,159,254,121,1,135,161,70,4,7,7,4,70,5,6,6,5,136,9,37,192,59,56,6,5,63,131,18,130,4,33,254,255,130,11,131,35,130,11,32,0,130,0,44,3,0,12,255,222,1,172,1,166,0,7,\n    0,15,131,107,39,55,50,54,53,52,39,7,22,78,242,6,37,6,37,7,22,21,20,78,241,7,8,65,52,54,51,50,23,55,22,222,47,57,10,169,27,56,9,168,27,46,45,59,1,54,61,33,178,75,44,57,34,62,29,94,83,\n    71,46,55,34,43,72,80,42,32,189,37,152,42,31,189,33,70,121,68,52,81,204,40,63,26,70,50,81,102,102,39,63,27,67,55,5,51,61,255,246,1,123,2,59,0,3,0,26,0,0,1,35,39,51,23,51,17,105,15,6,\n    38,61,1,51,21,20,22,51,130,133,8,41,1,13,57,119,73,144,68,63,6,2,5,20,23,38,21,77,61,67,38,43,44,53,1,184,131,182,254,124,57,2,10,23,18,14,72,82,244,240,52,52,59,50,130,84,33,2,0,141,\n    83,34,7,35,55,148,83,36,83,119,56,102,44,143,83,34,2,59,131,147,84,135,83,36,69,0,6,0,29,66,201,8,32,23,145,169,32,55,66,203,6,32,92,139,171,41,42,45,53,2,2,26,88,131,131,61,143,174,\n    32,235,130,175,32,3,134,175,95,67,5,32,46,66,85,25,148,197,35,63,58,8,8,95,89,6,33,9,128,140,205,130,117,95,106,13,32,80,143,125,42,0,0,2,0,26,255,107,1,158,2,59,80,107,7,34,2,7,14,\n    99,148,6,8,49,55,3,51,27,1,7,35,55,1,86,72,140,39,14,32,36,25,18,54,46,30,32,22,160,72,123,105,119,56,102,1,133,254,175,98,35,44,18,5,54,46,54,1,127,254,208,1,230,131,131,130,203,34,\n    2,0,50,130,79,39,134,2,32,0,9,0,23,0,107,131,10,33,19,50,66,59,5,53,21,35,17,51,21,54,174,89,55,57,86,56,99,80,91,171,67,32,68,68,31,42,66,245,11,37,65,202,2,180,208,61,130,75,32,3,\n    134,155,32,17,130,155,38,29,0,41,0,0,1,51,142,157,80,34,23,144,177,32,100,71,147,10,144,185,32,118,80,40,14,42,1,0,30,255,249,1,153,2,20,0,43,130,115,40,34,7,51,7,35,6,21,20,21,130,\n    6,35,30,3,51,50,107,75,6,38,39,35,55,51,38,52,55,130,5,8,76,62,1,51,50,23,21,46,1,1,44,110,20,178,18,163,1,132,17,111,6,27,38,37,23,20,61,27,47,61,88,103,14,65,18,44,2,2,62,18,47,14,\n    103,83,65,48,28,61,1,220,144,36,30,4,17,15,37,44,60,29,10,19,24,71,28,104,95,37,29,9,28,36,95,105,29,72,24,21,130,246,130,2,36,14,0,174,0,1,130,7,131,2,34,1,0,4,134,11,32,1,130,7,32,\n    10,134,11,32,2,130,7,32,16,134,11,36,3,0,29,0,78,134,11,131,43,32,112,134,11,36,5,0,9,0,134,134,11,32,6,130,7,44,148,0,3,0,1,4,9,0,0,0,2,0,0,134,11,32,1,130,11,32,6,134,11,32,2,130,\n    11,32,12,134,11,36,3,0,58,0,18,134,11,32,4,130,23,32,108,134,11,32,5,130,21,32,114,134,11,32,6,130,23,35,144,0,46,0,143,2,39,70,0,111,0,110,0,116,0,131,7,40,114,0,103,0,101,0,32,0,\n    50,130,36,32,48,130,7,32,58,130,3,32,46,134,7,36,49,0,49,0,45,130,25,131,3,32,48,130,7,51,54,0,0,70,111,110,116,70,111,114,103,101,32,50,46,48,32,58,32,46,130,3,39,49,49,45,50,45,50,\n    48,50,130,30,133,107,32,86,130,81,36,114,0,115,0,105,132,103,32,32,130,89,40,0,86,101,114,115,105,111,110,32,136,140,33,2,0,139,0,65,75,7,137,19,32,192,130,10,131,251,60,3,0,4,0,5,\n    0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0,16,0,17,130,235,8,50,19,0,20,0,21,0,22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0,31,0,32,0,33,0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,41,\n    0,42,0,43,0,44,130,211,36,46,0,47,0,48,130,221,9,48,50,0,51,0,52,0,53,0,54,0,55,0,56,0,57,0,58,0,59,0,60,0,61,0,62,0,63,0,64,0,65,0,66,0,67,0,68,0,69,0,70,0,71,0,72,0,73,0,74,0,75,\n    0,76,0,77,0,78,0,79,0,80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,90,0,91,0,92,0,93,0,94,1,2,0,96,0,97,1,3,0,163,0,132,0,133,0,189,0,150,0,232,0,134,0,142,0,139,0,157,0,169,0,\n    138,1,4,0,131,0,147,0,242,0,243,0,141,0,151,0,136,0,195,0,222,0,241,0,158,0,170,0,245,0,244,0,246,0,162,0,173,0,201,0,199,0,174,0,98,0,99,0,144,0,100,0,203,0,101,0,200,0,202,0,207,\n    0,204,0,205,0,206,0,233,0,102,0,211,0,209,0,175,0,103,0,240,0,145,0,214,0,212,0,213,0,104,0,235,0,237,0,137,0,106,0,105,0,107,0,109,0,108,0,110,0,160,0,111,0,113,0,112,0,114,0,115,\n    0,117,0,116,0,118,0,119,0,234,0,120,0,122,0,121,0,123,0,125,0,124,0,184,0,161,0,127,0,126,0,128,0,129,0,236,0,238,0,186,1,5,11,118,101,114,116,105,99,97,108,98,97,114,7,117,110,105,\n    48,48,56,53,9,111,130,20,42,115,99,111,114,101,4,69,117,114,111,0,132,0,38,1,255,255,0,2,0,1,130,11,32,12,130,3,32,22,130,3,131,13,32,98,130,183,34,1,0,4,132,13,130,4,130,2,32,1,130,\n    3,36,0,229,13,183,147,132,7,42,175,187,66,0,0,0,0,229,178,59,232,5,250,48,120,202,241,\n};\n\nstatic const char* GetDefaultCompressedFontDataProggyForever(int* out_size)\n{\n    *out_size = proggy_forever_minimal_ttf_compressed_size;\n    return (const char*)proggy_forever_minimal_ttf_compressed_data;\n}\n\n#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/source/imgui_tables.cpp",
    "content": "// dear imgui, v1.92.7 WIP\n// (tables and columns code)\n\n/*\n\nIndex of this file:\n\n// [SECTION] Commentary\n// [SECTION] Header mess\n// [SECTION] Tables: Main code\n// [SECTION] Tables: Simple accessors\n// [SECTION] Tables: Row changes\n// [SECTION] Tables: Columns changes\n// [SECTION] Tables: Columns width management\n// [SECTION] Tables: Drawing\n// [SECTION] Tables: Sorting\n// [SECTION] Tables: Headers\n// [SECTION] Tables: Context Menu\n// [SECTION] Tables: Settings (.ini data)\n// [SECTION] Tables: Garbage Collection\n// [SECTION] Tables: Debugging\n// [SECTION] Columns, BeginColumns, EndColumns, etc.\n\n*/\n\n// Navigating this file:\n// - In Visual Studio: Ctrl+Comma (\"Edit.GoToAll\") can follow symbols inside comments, whereas Ctrl+F12 (\"Edit.GoToImplementation\") cannot.\n// - In Visual Studio w/ Visual Assist installed: Alt+G (\"VAssistX.GoToImplementation\") can also follow symbols inside comments.\n// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.\n\n//-----------------------------------------------------------------------------\n// [SECTION] Commentary\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// Typical tables call flow: (root level is generally public API):\n//-----------------------------------------------------------------------------\n// - BeginTable()                               user begin into a table\n//    | BeginChild()                            - (if ScrollX/ScrollY is set)\n//    | TableBeginInitMemory()                  - first time table is used\n//    | TableResetSettings()                    - on settings reset\n//    | TableLoadSettings()                     - on settings load\n//    | TableBeginApplyRequests()               - apply queued resizing/reordering/hiding requests\n//    | - TableSetColumnWidth()                 - apply resizing width (for mouse resize, often requested by previous frame)\n//    |    - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width\n// - TableSetupColumn()                         user submit columns details (optional)\n// - TableSetupScrollFreeze()                   user submit scroll freeze information (optional)\n//-----------------------------------------------------------------------------\n// - TableUpdateLayout() [Internal]             followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow().\n//    | TableSetupDrawChannels()                - setup ImDrawList channels\n//    | TableUpdateBorders()                    - detect hovering columns for resize, ahead of contents submission\n//    | TableBeginContextMenuPopup()\n//    | - TableDrawDefaultContextMenu()         - draw right-click context menu contents\n//-----------------------------------------------------------------------------\n// - TableHeadersRow() or TableHeader()         user submit a headers row (optional)\n//    | TableSortSpecsClickColumn()             - when left-clicked: alter sort order and sort direction\n//    | TableOpenContextMenu()                  - when right-clicked: trigger opening of the default context menu\n// - TableGetSortSpecs()                        user queries updated sort specs (optional, generally after submitting headers)\n// - TableNextRow()                             user begin into a new row (also automatically called by TableHeadersRow())\n//    | TableEndRow()                           - finish existing row\n//    | TableBeginRow()                         - add a new row\n// - TableSetColumnIndex() / TableNextColumn()  user begin into a cell\n//    | TableEndCell()                          - close existing column/cell\n//    | TableBeginCell()                        - enter into current column/cell\n// - [...]                                      user emit contents\n//-----------------------------------------------------------------------------\n// - EndTable()                                 user ends the table\n//    | TableDrawBorders()                      - draw outer borders, inner vertical borders\n//    | TableMergeDrawChannels()                - merge draw channels if clipping isn't required\n//    | EndChild()                              - (if ScrollX/ScrollY is set)\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// TABLE SIZING\n//-----------------------------------------------------------------------------\n// (Read carefully because this is subtle but it does make sense!)\n//-----------------------------------------------------------------------------\n// About 'outer_size':\n// Its meaning needs to differ slightly depending on if we are using ScrollX/ScrollY flags.\n// Default value is ImVec2(0.0f, 0.0f).\n//   X\n//   - outer_size.x <= 0.0f  ->  Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0f will align exactly on right-most edge.\n//   - outer_size.x  > 0.0f  ->  Set Fixed width.\n//   Y with ScrollX/ScrollY disabled: we output table directly in current window\n//   - outer_size.y  < 0.0f  ->  Bottom-align (but will auto extend, unless _NoHostExtendY is set). Not meaningful if parent window can vertically scroll.\n//   - outer_size.y  = 0.0f  ->  No minimum height (but will auto extend, unless _NoHostExtendY is set)\n//   - outer_size.y  > 0.0f  ->  Set Minimum height (but will auto extend, unless _NoHostExtendY is set)\n//   Y with ScrollX/ScrollY enabled: using a child window for scrolling\n//   - outer_size.y  < 0.0f  ->  Bottom-align. Not meaningful if parent window can vertically scroll.\n//   - outer_size.y  = 0.0f  ->  Bottom-align, consistent with BeginChild(). Not recommended unless table is last item in parent window.\n//   - outer_size.y  > 0.0f  ->  Set Exact height. Recommended when using Scrolling on any axis.\n//-----------------------------------------------------------------------------\n// Outer size is also affected by the NoHostExtendX/NoHostExtendY flags.\n// Important to note how the two flags have slightly different behaviors!\n//   - ImGuiTableFlags_NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.\n//   - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible.\n// In theory ImGuiTableFlags_NoHostExtendY could be the default and any non-scrolling tables with outer_size.y != 0.0f would use exact height.\n// This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not useful and not easily noticeable).\n//-----------------------------------------------------------------------------\n// About 'inner_width':\n//   With ScrollX disabled:\n//   - inner_width          ->  *ignored*\n//   With ScrollX enabled:\n//   - inner_width  < 0.0f  ->  *illegal* fit in known width (right align from outer_size.x) <-- weird\n//   - inner_width  = 0.0f  ->  fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns.\n//   - inner_width  > 0.0f  ->  override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space!\n//-----------------------------------------------------------------------------\n// Details:\n// - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept\n//   of \"available space\" doesn't make sense.\n// - Even if not really useful, we allow 'inner_width < outer_size.x' for consistency and to facilitate understanding\n//   of what the value does.\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// COLUMNS SIZING POLICIES\n// (Reference: ImGuiTableFlags_SizingXXX flags and ImGuiTableColumnFlags_WidthXXX flags)\n//-----------------------------------------------------------------------------\n// About overriding column sizing policy and width/weight with TableSetupColumn():\n// We use a default parameter of -1 for 'init_width'/'init_weight'.\n//   - with ImGuiTableColumnFlags_WidthFixed,    init_width  <= 0 (default)  --> width is automatic\n//   - with ImGuiTableColumnFlags_WidthFixed,    init_width  >  0 (explicit) --> width is custom\n//   - with ImGuiTableColumnFlags_WidthStretch,  init_weight <= 0 (default)  --> weight is 1.0f\n//   - with ImGuiTableColumnFlags_WidthStretch,  init_weight >  0 (explicit) --> weight is custom\n// Widths are specified _without_ CellPadding. If you specify a width of 100.0f, the column will be cover (100.0f + Padding * 2.0f)\n// and you can fit a 100.0f wide item in it without clipping and with padding honored.\n//-----------------------------------------------------------------------------\n// About default sizing policy (if you don't specify a ImGuiTableColumnFlags_WidthXXXX flag)\n//   - with Table policy ImGuiTableFlags_SizingFixedFit      --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is equal to contents width\n//   - with Table policy ImGuiTableFlags_SizingFixedSame     --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is max of all contents width\n//   - with Table policy ImGuiTableFlags_SizingStretchSame   --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is 1.0f\n//   - with Table policy ImGuiTableFlags_SizingStretchWeight --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is proportional to contents\n// Default Width and default Weight can be overridden when calling TableSetupColumn().\n//-----------------------------------------------------------------------------\n// About mixing Fixed/Auto and Stretch columns together:\n//   - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.\n//   - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place!\n//     that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in.\n//   - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents.\n//   - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weights/widths.\n//-----------------------------------------------------------------------------\n// About using column width:\n// If a column is manually resizable or has a width specified with TableSetupColumn():\n//   - you may use GetContentRegionAvail().x to query the width available in a given column.\n//   - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width.\n// If the column is not resizable and has no width specified with TableSetupColumn():\n//   - its width will be automatic and be set to the max of items submitted.\n//   - therefore you generally cannot have ALL items of the columns use e.g. SetNextItemWidth(-FLT_MIN).\n//   - but if the column has one or more items of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN).\n//-----------------------------------------------------------------------------\n\n\n//-----------------------------------------------------------------------------\n// TABLES CLIPPING/CULLING\n//-----------------------------------------------------------------------------\n// About clipping/culling of Rows in Tables:\n// - For large numbers of rows, it is recommended you use ImGuiListClipper to submit only visible rows.\n//   ImGuiListClipper is reliant on the fact that rows are of equal height.\n//   See 'Demo->Tables->Vertical Scrolling' or 'Demo->Tables->Advanced' for a demo of using the clipper.\n// - Note that auto-resizing columns don't play well with using the clipper.\n//   By default a table with _ScrollX but without _Resizable will have column auto-resize.\n//   So, if you want to use the clipper, make sure to either enable _Resizable, either setup columns width explicitly with _WidthFixed.\n//-----------------------------------------------------------------------------\n// About clipping/culling of Columns in Tables:\n// - Both TableSetColumnIndex() and TableNextColumn() return true when the column is visible or performing\n//   width measurements. Otherwise, you may skip submitting the contents of a cell/column, BUT ONLY if you know\n//   it is not going to contribute to row height.\n//   In many situations, you may skip submitting contents for every column but one (e.g. the first one).\n// - Case A: column is not hidden by user, and at least partially in sight (most common case).\n// - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output.\n// - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.).\n//\n//                        [A]         [B]          [C]\n//  TableNextColumn():    true        false        false       -> [userland] when TableNextColumn() / TableSetColumnIndex() returns false, user can skip submitting items but only if the column doesn't contribute to row height.\n//          SkipItems:    false       false        true        -> [internal] when SkipItems is true, most widgets will early out if submitted, resulting is no layout output.\n//           ClipRect:    normal      zero-width   zero-width  -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way.\n//  ImDrawList output:    normal      dummy        dummy       -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway).\n//\n// - We need to distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row.\n//   However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer.\n//-----------------------------------------------------------------------------\n// About clipping/culling of whole Tables:\n// - Scrolling tables with a known outer size can be clipped earlier as BeginTable() will return false.\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// [SECTION] Header mess\n//-----------------------------------------------------------------------------\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#ifndef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS\n#endif\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_internal.h\"\n\n// System includes\n#include <stdint.h>     // intptr_t\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4127)     // condition expression is constant\n#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later\n#pragma warning (disable: 5054)     // operator '|': deprecated between enumerations of different types\n#endif\n#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).\n#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast                            // yes, they are more terse.\n#pragma clang diagnostic ignored \"-Wfloat-equal\"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.\n#pragma clang diagnostic ignored \"-Wformat\"                         // warning: format specifies type 'int' but the argument has type 'unsigned int'\n#pragma clang diagnostic ignored \"-Wformat-nonliteral\"              // warning: format string is not a string literal            // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.\n#pragma clang diagnostic ignored \"-Wsign-conversion\"                // warning: implicit conversion changes signedness\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.\n#pragma clang diagnostic ignored \"-Wenum-enum-conversion\"           // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')\n#pragma clang diagnostic ignored \"-Wdeprecated-enum-enum-conversion\"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#pragma clang diagnostic ignored \"-Wunsafe-buffer-usage\"            // warning: 'xxx' is an unsafe pointer used for buffer access\n#pragma clang diagnostic ignored \"-Wnontrivial-memaccess\"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type\n#pragma clang diagnostic ignored \"-Wswitch-default\"                 // warning: 'switch' missing 'default' label\n#elif defined(__GNUC__)\n#pragma GCC diagnostic ignored \"-Wpragmas\"                          // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"                      // warning: comparing floating-point with '==' or '!=' is unsafe\n#pragma GCC diagnostic ignored \"-Wformat-nonliteral\"                // warning: format not a string literal, format string not checked\n#pragma GCC diagnostic ignored \"-Wdouble-promotion\"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function\n#pragma GCC diagnostic ignored \"-Wformat\"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'\n#pragma GCC diagnostic ignored \"-Wstrict-overflow\"\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"                  // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Tables: Main code\n//-----------------------------------------------------------------------------\n// - TableFixFlags() [Internal]\n// - TableFindByID() [Internal]\n// - BeginTable()\n// - BeginTableEx() [Internal]\n// - TableBeginInitMemory() [Internal]\n// - TableBeginApplyRequests() [Internal]\n// - TableSetupColumnFlags() [Internal]\n// - TableUpdateLayout() [Internal]\n// - TableUpdateBorders() [Internal]\n// - EndTable()\n// - TableSetupColumn()\n// - TableSetupScrollFreeze()\n//-----------------------------------------------------------------------------\n\n// Configuration\nstatic const int TABLE_DRAW_CHANNEL_BG0 = 0;\nstatic const int TABLE_DRAW_CHANNEL_BG2_FROZEN = 1;\nstatic const int TABLE_DRAW_CHANNEL_NOCLIP = 2;                     // When using ImGuiTableFlags_NoClip (this becomes the last visible channel)\nstatic const float TABLE_BORDER_SIZE                     = 1.0f;    // FIXME-TABLE: Currently hard-coded because of clipping assumptions with outer borders rendering.\nstatic const float TABLE_RESIZE_SEPARATOR_HALF_THICKNESS = 4.0f;    // Extend outside inner borders.\nstatic const float TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER = 0.06f;   // Delay/timer before making the hover feedback (color+cursor) visible because tables/columns tends to be more cramped.\n\n// Helper\ninline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_window)\n{\n    // Adjust flags: set default sizing policy\n    if ((flags & ImGuiTableFlags_SizingMask_) == 0)\n        flags |= ((flags & ImGuiTableFlags_ScrollX) || (outer_window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) ? ImGuiTableFlags_SizingFixedFit : ImGuiTableFlags_SizingStretchSame;\n\n    // Adjust flags: enable NoKeepColumnsVisible when using ImGuiTableFlags_SizingFixedSame\n    if ((flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)\n        flags |= ImGuiTableFlags_NoKeepColumnsVisible;\n\n    // Adjust flags: enforce borders when resizable\n    if (flags & ImGuiTableFlags_Resizable)\n        flags |= ImGuiTableFlags_BordersInnerV;\n\n    // Adjust flags: disable NoHostExtendX/NoHostExtendY if we have any scrolling going on\n    if (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY))\n        flags &= ~(ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_NoHostExtendY);\n\n    // Adjust flags: NoBordersInBodyUntilResize takes priority over NoBordersInBody\n    if (flags & ImGuiTableFlags_NoBordersInBodyUntilResize)\n        flags &= ~ImGuiTableFlags_NoBordersInBody;\n\n    // Adjust flags: disable saved settings if there's nothing to save\n    if ((flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Sortable)) == 0)\n        flags |= ImGuiTableFlags_NoSavedSettings;\n\n    // Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set)\n    if (outer_window->RootWindow->Flags & ImGuiWindowFlags_NoSavedSettings)\n        flags |= ImGuiTableFlags_NoSavedSettings;\n\n    return flags;\n}\n\nImGuiTable* ImGui::TableFindByID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    return g.Tables.GetByKey(id);\n}\n\n// Read about \"TABLE SIZING\" at the top of this file.\nbool    ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width)\n{\n    ImGuiID id = GetID(str_id);\n    return BeginTableEx(str_id, id, columns_count, flags, outer_size, inner_width);\n}\n\nbool    ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* outer_window = GetCurrentWindow();\n    if (outer_window->SkipItems) // Consistent with other tables + beneficial side effect that assert on miscalling EndTable() will be more visible.\n        return false;\n\n    // Sanity checks\n    IM_ASSERT(columns_count > 0 && columns_count < IMGUI_TABLE_MAX_COLUMNS);\n    if (flags & ImGuiTableFlags_ScrollX)\n        IM_ASSERT(inner_width >= 0.0f);\n\n    // If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve.\n    // FIXME: coarse clipping because access to table data causes two issues:\n    // - instance numbers varying/unstable. may not be a direct problem for users, but could make outside access broken or confusing, e.g. TestEngine.\n    // - can't implement support for ImGuiChildFlags_ResizeY as we need to somehow pull the height data from somewhere. this also needs stable instance numbers.\n    // The side-effects of accessing table data on coarse clip would be:\n    // - always reserving the pooled ImGuiTable data ahead for a fully clipped table (minor IMHO). Also the 'outer_window_is_measuring_size' criteria may already be defeating this in some situations.\n    // - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].\n    const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;\n    const ImVec2 avail_size = GetContentRegionAvail();\n    const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, IMGUI_WINDOW_HARD_MIN_SIZE), use_child_window ? ImMax(avail_size.y, IMGUI_WINDOW_HARD_MIN_SIZE) : 0.0f));\n    const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);\n    const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows!\n    if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)\n    {\n        ItemSize(outer_rect);\n        ItemAdd(outer_rect, id);\n        g.NextWindowData.ClearFlags();\n        return false;\n    }\n\n    // [DEBUG] Debug break requested by user\n    if (g.DebugBreakInTable == id)\n        IM_DEBUG_BREAK();\n\n    // Acquire storage for the table\n    ImGuiTable* table = g.Tables.GetOrAddByKey(id);\n\n    // Acquire temporary buffers\n    const int table_idx = g.Tables.GetIndex(table);\n    if (++g.TablesTempDataStacked > g.TablesTempData.Size)\n        g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData());\n    ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1];\n    temp_data->TableIndex = table_idx;\n    table->DrawSplitter = &table->TempData->DrawSplitter;\n    table->DrawSplitter->Clear();\n\n    // Fix flags\n    table->IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0;\n    flags = TableFixFlags(flags, outer_window);\n\n    // Initialize\n    const int previous_frame_active = table->LastFrameActive;\n    const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1;\n    const ImGuiTableFlags previous_flags = table->Flags;\n    table->ID = id;\n    table->Flags = flags;\n    table->LastFrameActive = g.FrameCount;\n    table->OuterWindow = table->InnerWindow = outer_window;\n    table->ColumnsCount = columns_count;\n    table->IsLayoutLocked = false;\n    table->InnerWidth = inner_width;\n    table->NavLayer = (ImS8)outer_window->DC.NavLayerCurrent;\n    temp_data->UserOuterSize = outer_size;\n\n    // Instance data (for instance 0, TableID == TableInstanceID)\n    ImGuiID instance_id;\n    table->InstanceCurrent = (ImS16)instance_no;\n    if (instance_no > 0)\n    {\n        IM_ASSERT(table->ColumnsCount == columns_count && \"BeginTable(): Cannot change columns count mid-frame while preserving same ID\");\n        if (table->InstanceDataExtra.Size < instance_no)\n            table->InstanceDataExtra.push_back(ImGuiTableInstanceData());\n        instance_id = GetIDWithSeed(instance_no, GetIDWithSeed(\"##Instances\", NULL, id)); // Push \"##Instances\" followed by (int)instance_no in ID stack.\n    }\n    else\n    {\n        instance_id = id;\n    }\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    table_instance->TableInstanceID = instance_id;\n\n    // When not using a child window, WorkRect.Max will grow as we append contents.\n    if (use_child_window)\n    {\n        // Ensure no vertical scrollbar appears if we only want horizontal one, to make flag consistent\n        // (we have no other way to disable vertical scrollbar of a window while keeping the horizontal one showing)\n        ImVec2 override_content_size(FLT_MAX, FLT_MAX);\n        if ((flags & ImGuiTableFlags_ScrollX) && !(flags & ImGuiTableFlags_ScrollY))\n            override_content_size.y = FLT_MIN;\n\n        // Ensure specified width (when not specified, Stretched columns will act as if the width == OuterWidth and\n        // never lead to any scrolling). We don't handle inner_width < 0.0f, we could potentially use it to right-align\n        // based on the right side of the child window work rect, which would require knowing ahead if we are going to\n        // have decoration taking horizontal spaces (typically a vertical scrollbar).\n        if ((flags & ImGuiTableFlags_ScrollX) && inner_width > 0.0f)\n            override_content_size.x = inner_width;\n\n        if (override_content_size.x != FLT_MAX || override_content_size.y != FLT_MAX)\n            SetNextWindowContentSize(ImVec2(override_content_size.x != FLT_MAX ? override_content_size.x : 0.0f, override_content_size.y != FLT_MAX ? override_content_size.y : 0.0f));\n\n        // Reset scroll if we are reactivating it\n        if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)\n            if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll) == 0)\n                SetNextWindowScroll(ImVec2(0.0f, 0.0f));\n\n        // Create scrolling region (without border and zero window padding)\n        ImGuiChildFlags child_child_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : ImGuiChildFlags_None;\n        ImGuiWindowFlags child_window_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowFlags) ? g.NextWindowData.WindowFlags : ImGuiWindowFlags_None;\n        if (flags & ImGuiTableFlags_ScrollX)\n            child_window_flags |= ImGuiWindowFlags_HorizontalScrollbar;\n        BeginChildEx(name, instance_id, outer_rect.GetSize(), child_child_flags, child_window_flags);\n        table->InnerWindow = g.CurrentWindow;\n        table->WorkRect = table->InnerWindow->WorkRect;\n        table->OuterRect = table->InnerWindow->Rect();\n        table->InnerRect = table->InnerWindow->InnerRect;\n        IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f);\n\n        // Allow submitting when host is measuring\n        if (table->InnerWindow->SkipItems && outer_window_is_measuring_size)\n            table->InnerWindow->SkipItems = false;\n\n        // When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned\n        if (instance_no == 0)\n        {\n            table->HasScrollbarYPrev = table->HasScrollbarYCurr;\n            table->HasScrollbarYCurr = false;\n        }\n        table->HasScrollbarYCurr |= table->InnerWindow->ScrollbarY;\n    }\n    else\n    {\n        // For non-scrolling tables, WorkRect == OuterRect == InnerRect.\n        // But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().\n        table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;\n        table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;\n        table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking across a table\n    }\n\n    // Push a standardized ID for both child-using and not-child-using tables\n    PushOverrideID(id);\n    if (instance_no > 0)\n        PushOverrideID(instance_id); // FIXME: Somehow this is not resolved by stack-tool, even tho GetIDWithSeed() submitted the symbol.\n\n    // Backup a copy of host window members we will modify\n    ImGuiWindow* inner_window = table->InnerWindow;\n    table->HostIndentX = inner_window->DC.Indent.x;\n    table->HostClipRect = inner_window->ClipRect;\n    table->HostSkipItems = inner_window->SkipItems;\n    temp_data->WindowID = inner_window->ID;\n    temp_data->HostBackupWorkRect = inner_window->WorkRect;\n    temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect;\n    temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset;\n    temp_data->HostBackupPrevLineSize = inner_window->DC.PrevLineSize;\n    temp_data->HostBackupCurrLineSize = inner_window->DC.CurrLineSize;\n    temp_data->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos;\n    temp_data->HostBackupItemWidth = outer_window->DC.ItemWidth;\n    temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;\n    inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);\n\n    // Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752)\n    // (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the\n    // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap\n    // problem only affect scrolling tables in this case we can get away with doing it without extra cost).\n    if (inner_window != outer_window)\n    {\n        // FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize,\n        // it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with\n        // different x/y values to BeginChild().\n        if (flags & ImGuiTableFlags_BordersOuterV)\n        {\n            table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x);\n            if (inner_window->DecoOuterSizeX2 == 0.0f)\n                table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x);\n        }\n        if (flags & ImGuiTableFlags_BordersOuterH)\n        {\n            table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y);\n            if (inner_window->DecoOuterSizeY2 == 0.0f)\n                table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y);\n        }\n    }\n\n    // Padding and Spacing\n    // - None               ........Content..... Pad .....Content........\n    // - PadOuter           | Pad ..Content..... Pad .....Content.. Pad |\n    // - PadInner           ........Content.. Pad | Pad ..Content........\n    // - PadOuter+PadInner  | Pad ..Content.. Pad | Pad ..Content.. Pad |\n    const bool pad_outer_x = (flags & ImGuiTableFlags_NoPadOuterX) ? false : (flags & ImGuiTableFlags_PadOuterX) ? true : (flags & ImGuiTableFlags_BordersOuterV) != 0;\n    const bool pad_inner_x = (flags & ImGuiTableFlags_NoPadInnerX) ? false : true;\n    const float inner_spacing_for_border = (flags & ImGuiTableFlags_BordersInnerV) ? TABLE_BORDER_SIZE : 0.0f;\n    const float inner_spacing_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) == 0) ? g.Style.CellPadding.x : 0.0f;\n    const float inner_padding_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) != 0) ? g.Style.CellPadding.x : 0.0f;\n    table->CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border;\n    table->CellSpacingX2 = inner_spacing_explicit;\n    table->CellPaddingX = inner_padding_explicit;\n\n    const float outer_padding_for_border = (flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;\n    const float outer_padding_explicit = pad_outer_x ? g.Style.CellPadding.x : 0.0f;\n    table->OuterPaddingX = (outer_padding_for_border + outer_padding_explicit) - table->CellPaddingX;\n\n    table->CurrentColumn = -1;\n    table->CurrentRow = -1;\n    table->RowBgColorCounter = 0;\n    table->LastRowFlags = ImGuiTableRowFlags_None;\n    table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect;\n    table->InnerClipRect.ClipWith(table->WorkRect);     // We need this to honor inner_width\n    table->InnerClipRect.ClipWithFull(table->HostClipRect);\n    table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y;\n\n    table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow\n    table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow()\n    table->RowCellPaddingY = 0.0f;\n    table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any\n    table->FreezeColumnsRequest = table->FreezeColumnsCount = 0;\n    table->IsUnfrozenRows = true;\n    table->DeclColumnsCount = table->AngledHeadersCount = 0;\n    if (previous_frame_active + 1 < g.FrameCount)\n        table->IsActiveIdInTable = false;\n    table->AngledHeadersHeight = 0.0f;\n    temp_data->AngledHeadersExtraWidth = 0.0f;\n\n    // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders()\n    table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong);\n    table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight);\n\n    // Make table current\n    g.CurrentTable = table;\n    inner_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();\n    outer_window->DC.CurrentTableIdx = table_idx;\n    if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly.\n        inner_window->DC.CurrentTableIdx = table_idx;\n\n    if ((previous_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0)\n        table->IsResetDisplayOrderRequest = true;\n\n    // Mark as used to avoid GC\n    if (table_idx >= g.TablesLastTimeActive.Size)\n        g.TablesLastTimeActive.resize(table_idx + 1, -1.0f);\n    g.TablesLastTimeActive[table_idx] = (float)g.Time;\n    temp_data->LastTimeActive = (float)g.Time;\n    table->MemoryCompacted = false;\n\n    // Setup memory buffer (clear data if columns count changed)\n    ImGuiTableColumn* old_columns_to_preserve = NULL;\n    void* old_columns_raw_data = NULL;\n    const int old_columns_count = table->Columns.size();\n    if (old_columns_count != 0 && old_columns_count != columns_count)\n    {\n        // Attempt to preserve width and other settings on column count/specs change (#4046)\n        old_columns_to_preserve = table->Columns.Data;\n        old_columns_raw_data = table->RawData; // Free at end of function\n        table->RawData = NULL;\n    }\n    if (table->RawData == NULL)\n    {\n        TableBeginInitMemory(table, columns_count);\n        table->IsInitializing = table->IsSettingsRequestLoad = true;\n    }\n    if (table->IsResetAllRequest)\n        TableResetSettings(table);\n    if (table->IsInitializing)\n    {\n        // Initialize\n        table->SettingsOffset = -1;\n        table->IsSortSpecsDirty = true;\n        table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934)\n        table->InstanceInteracted = -1;\n        table->ContextPopupColumn = -1;\n        table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1;\n        table->AutoFitSingleColumn = -1;\n        table->HoveredColumnBody = table->HoveredColumnBorder = -1;\n        for (int n = 0; n < columns_count; n++)\n        {\n            ImGuiTableColumn* column = &table->Columns[n];\n            if (old_columns_to_preserve && n < old_columns_count)\n            {\n                *column = old_columns_to_preserve[n];\n            }\n            else\n            {\n                float width_auto = column->WidthAuto;\n                *column = ImGuiTableColumn();\n                column->WidthAuto = width_auto;\n                column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker\n                column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true;\n                column->DisplayOrder = (ImGuiTableColumnIdx)n;\n            }\n            table->DisplayOrderToIndex[n] = column->DisplayOrder;\n        }\n    }\n    if (old_columns_raw_data)\n        IM_FREE(old_columns_raw_data);\n\n    // Load settings\n    if (table->IsSettingsRequestLoad)\n        TableLoadSettings(table);\n\n    // Handle DPI/font resize\n    // This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well.\n    // It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition.\n    // FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory.\n    // This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path.\n    const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ?\n    if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit)\n    {\n        const float scale_factor = new_ref_scale_unit / table->RefScale;\n        //IMGUI_DEBUG_PRINT(\"[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\\n\", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor);\n        for (int n = 0; n < columns_count; n++)\n            table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor;\n    }\n    table->RefScale = new_ref_scale_unit;\n\n    // Disable output until user calls TableNextRow() or TableNextColumn() leading to the TableUpdateLayout() call..\n    // This is not strictly necessary but will reduce cases were \"out of table\" output will be misleading to the user.\n    // Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option.\n    inner_window->SkipItems = true;\n\n    // Clear names\n    // At this point the ->NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn()\n    if (table->ColumnsNames.Buf.Size > 0)\n        table->ColumnsNames.Buf.resize(0);\n\n    // Apply queued resizing/reordering/hiding requests\n    TableBeginApplyRequests(table);\n\n    return true;\n}\n\n// For reference, the average total _allocation count_ for a table is:\n// + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables[])\n// + 1 (for table->RawData allocated below)\n// + 1 (for table->ColumnsNames, if names are used)\n// Shared allocations for the maximum number of simultaneously nested tables (generally a very small number)\n// + 1 (for table->Splitter._Channels)\n// + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels)\n// Where active_channels_count is variable but often == columns_count or == columns_count + 1, see TableSetupDrawChannels() for details.\n// Unused channels don't perform their +2 allocations.\nvoid ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count)\n{\n    // Allocate single buffer for our arrays\n    const int columns_bit_array_size = (int)ImBitArrayGetStorageSizeInBytes(columns_count);\n    ImSpanAllocator<6> span_allocator;\n    span_allocator.Reserve(0, columns_count * sizeof(ImGuiTableColumn));\n    span_allocator.Reserve(1, columns_count * sizeof(ImGuiTableColumnIdx));\n    span_allocator.Reserve(2, columns_count * sizeof(ImGuiTableCellData), 4);\n    for (int n = 3; n < 6; n++)\n        span_allocator.Reserve(n, columns_bit_array_size);\n    table->RawData = IM_ALLOC(span_allocator.GetArenaSizeInBytes());\n    memset(table->RawData, 0, span_allocator.GetArenaSizeInBytes());\n    span_allocator.SetArenaBasePtr(table->RawData);\n    span_allocator.GetSpan(0, &table->Columns);\n    span_allocator.GetSpan(1, &table->DisplayOrderToIndex);\n    span_allocator.GetSpan(2, &table->RowCellData);\n    table->EnabledMaskByDisplayOrder = (ImU32*)span_allocator.GetSpanPtrBegin(3);\n    table->EnabledMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(4);\n    table->VisibleMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(5);\n}\n\n// Apply queued resizing/reordering/hiding requests\nvoid ImGui::TableBeginApplyRequests(ImGuiTable* table)\n{\n    // Handle resizing request\n    // (We process this in the TableBegin() of the first instance of each table)\n    // FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling?\n    if (table->InstanceCurrent == 0)\n    {\n        if (table->ResizedColumn != -1 && table->ResizedColumnNextWidth != FLT_MAX)\n            TableSetColumnWidth(table->ResizedColumn, table->ResizedColumnNextWidth);\n        table->LastResizedColumn = table->ResizedColumn;\n        table->ResizedColumnNextWidth = FLT_MAX;\n        table->ResizedColumn = -1;\n\n        // Process auto-fit for single column, which is a special case for stretch columns and fixed columns with FixedSame policy.\n        // FIXME-TABLE: Would be nice to redistribute available stretch space accordingly to other weights, instead of giving it all to siblings.\n        if (table->AutoFitSingleColumn != -1)\n        {\n            TableSetColumnWidth(table->AutoFitSingleColumn, table->Columns[table->AutoFitSingleColumn].WidthAuto);\n            table->AutoFitSingleColumn = -1;\n        }\n    }\n\n    // Handle reordering request\n    // Note: we don't clear ReorderColumn after handling the request (FIXME: clarify why or add a test).\n    if (table->InstanceCurrent == 0)\n    {\n        if (table->HeldHeaderColumn == -1 && table->ReorderColumn != -1)\n            table->ReorderColumn = -1;\n        table->HeldHeaderColumn = -1;\n        if (table->ReorderColumn != -1 && table->ReorderColumnDir != 0)\n        {\n            // We need to handle reordering across hidden columns.\n            // In the configuration below, moving C to the right of E will lead to:\n            //    ... C [D] E  --->  ... [D] E  C   (Column name/index)\n            //    ... 2  3  4        ...  2  3  4   (Display order)\n            IM_ASSERT(table->ReorderColumnDir == -1 || table->ReorderColumnDir == +1);\n            IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable);\n            ImGuiTableColumn* src_column = &table->Columns[table->ReorderColumn];\n            ImGuiTableColumn* dst_column = &table->Columns[(table->ReorderColumnDir < 0) ? src_column->PrevEnabledColumn : src_column->NextEnabledColumn];\n            TableSetColumnDisplayOrder(table, table->ReorderColumn, dst_column->DisplayOrder);\n            table->ReorderColumnDir = 0;\n        }\n    }\n\n    // Handle display order reset request\n    if (table->IsResetDisplayOrderRequest)\n    {\n        for (int n = 0; n < table->ColumnsCount; n++)\n            table->DisplayOrderToIndex[n] = table->Columns[n].DisplayOrder = (ImGuiTableColumnIdx)n;\n        table->IsResetDisplayOrderRequest = false;\n        table->IsSettingsDirty = true;\n    }\n}\n\n// Note that TableSetupScrollFreeze() enforce a display order range for frozen columns.\n// So reordering a column across the frozen column barrier is illegal and will be undone.\nvoid ImGui::TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order)\n{\n    IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);\n    IM_ASSERT(dst_order >= 0 && dst_order < table->ColumnsCount);\n\n    ImGuiTableColumn* src_column = &table->Columns[column_n];\n    const int src_order = src_column->DisplayOrder;\n    if (src_order == dst_order)\n        return;\n    const int reorder_dir = (dst_order < src_order) ? -1 : +1;\n\n    src_column->DisplayOrder = (ImGuiTableColumnIdx)dst_order;\n    for (int order_n = src_order + reorder_dir; order_n != dst_order + reorder_dir; order_n += reorder_dir)\n        table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder -= (ImGuiTableColumnIdx)reorder_dir;\n    //IM_ASSERT(dst_column->DisplayOrder == dst_order - reorder_dir);\n\n    // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[]. Rebuild later from the former.\n    // FIXME-OPT: If this is called multiple times we'd effectively have a O(N^2) thing going on.\n    for (int n = 0; n < table->ColumnsCount; n++)\n        table->DisplayOrderToIndex[table->Columns[n].DisplayOrder] = (ImGuiTableColumnIdx)n;\n    table->IsSettingsDirty = true;\n}\n\n// Adjust flags: default width mode + stretch columns are not allowed when auto extending\nstatic void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags flags_in)\n{\n    ImGuiTableColumnFlags flags = flags_in;\n\n    // Sizing Policy\n    if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0)\n    {\n        const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_);\n        if (table_sizing_policy == ImGuiTableFlags_SizingFixedFit || table_sizing_policy == ImGuiTableFlags_SizingFixedSame)\n            flags |= ImGuiTableColumnFlags_WidthFixed;\n        else\n            flags |= ImGuiTableColumnFlags_WidthStretch;\n    }\n    else\n    {\n        IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_WidthMask_)); // Check that only 1 of each set is used.\n    }\n\n    // Resize\n    if ((table->Flags & ImGuiTableFlags_Resizable) == 0)\n        flags |= ImGuiTableColumnFlags_NoResize;\n\n    // Sorting\n    if ((flags & ImGuiTableColumnFlags_NoSortAscending) && (flags & ImGuiTableColumnFlags_NoSortDescending))\n        flags |= ImGuiTableColumnFlags_NoSort;\n\n    // Indentation\n    if ((flags & ImGuiTableColumnFlags_IndentMask_) == 0)\n        flags |= (table->Columns.index_from_ptr(column) == 0) ? ImGuiTableColumnFlags_IndentEnable : ImGuiTableColumnFlags_IndentDisable;\n\n    // Alignment\n    //if ((flags & ImGuiTableColumnFlags_AlignMask_) == 0)\n    //    flags |= ImGuiTableColumnFlags_AlignCenter;\n    //IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_AlignMask_)); // Check that only 1 of each set is used.\n\n    // Preserve status flags\n    column->Flags = flags | (column->Flags & ImGuiTableColumnFlags_StatusMask_);\n\n    // Build an ordered list of available sort directions\n    column->SortDirectionsAvailCount = column->SortDirectionsAvailMask = column->SortDirectionsAvailList = 0;\n    if (table->Flags & ImGuiTableFlags_Sortable)\n    {\n        int count = 0, mask = 0, list = 0;\n        if ((flags & ImGuiTableColumnFlags_PreferSortAscending)  != 0 && (flags & ImGuiTableColumnFlags_NoSortAscending)  == 0) { mask |= 1 << ImGuiSortDirection_Ascending;  list |= ImGuiSortDirection_Ascending  << (count << 1); count++; }\n        if ((flags & ImGuiTableColumnFlags_PreferSortDescending) != 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; }\n        if ((flags & ImGuiTableColumnFlags_PreferSortAscending)  == 0 && (flags & ImGuiTableColumnFlags_NoSortAscending)  == 0) { mask |= 1 << ImGuiSortDirection_Ascending;  list |= ImGuiSortDirection_Ascending  << (count << 1); count++; }\n        if ((flags & ImGuiTableColumnFlags_PreferSortDescending) == 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; }\n        if ((table->Flags & ImGuiTableFlags_SortTristate) || count == 0) { mask |= 1 << ImGuiSortDirection_None; count++; }\n        column->SortDirectionsAvailList = (ImU8)list;\n        column->SortDirectionsAvailMask = (ImU8)mask;\n        column->SortDirectionsAvailCount = (ImU8)count;\n        ImGui::TableFixColumnSortDirection(table, column);\n    }\n}\n\n// Layout columns for the frame. This is in essence the followup to BeginTable() and this is our largest function.\n// Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() and other TableSetupXXXXX() functions to be called first.\n// FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for _WidthAuto columns.\n// Increase feedback side-effect with widgets relying on WorkRect.Max.x... Maybe provide a default distribution for _WidthAuto columns?\nvoid ImGui::TableUpdateLayout(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(table->IsLayoutLocked == false);\n\n    const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_);\n    table->IsDefaultDisplayOrder = true;\n    table->ColumnsEnabledCount = 0;\n    ImBitArrayClearAllBits(table->EnabledMaskByIndex, table->ColumnsCount);\n    ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, table->ColumnsCount);\n    table->LeftMostEnabledColumn = -1;\n    table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE\n\n    // [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns.\n    // Process columns in their visible orders as we are building the Prev/Next indices.\n    int count_fixed = 0;                // Number of columns that have fixed sizing policies\n    int count_stretch = 0;              // Number of columns that have stretch sizing policies\n    int prev_visible_column_idx = -1;\n    bool has_auto_fit_request = false;\n    bool has_resizable = false;\n    float stretch_sum_width_auto = 0.0f;\n    float fixed_max_width_auto = 0.0f;\n    for (int order_n = 0; order_n < table->ColumnsCount; order_n++)\n    {\n        const int column_n = table->DisplayOrderToIndex[order_n];\n        if (column_n != order_n)\n            table->IsDefaultDisplayOrder = false;\n        ImGuiTableColumn* column = &table->Columns[column_n];\n\n        // Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame.\n        // It would easily work without but we're not ready to guarantee it since e.g. names need resubmission anyway.\n        // We take a slight shortcut but in theory we could be calling TableSetupColumn() here with dummy values, it should yield the same effect.\n        if (table->DeclColumnsCount <= column_n)\n        {\n            TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None);\n            column->NameOffset = -1;\n            column->UserID = 0;\n            column->InitStretchWeightOrWidth = -1.0f;\n        }\n\n        // Update Enabled state, mark settings and sort specs dirty\n        if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide))\n            column->IsUserEnabledNextFrame = true;\n        if (column->IsUserEnabled != column->IsUserEnabledNextFrame)\n        {\n            column->IsUserEnabled = column->IsUserEnabledNextFrame;\n            table->IsSettingsDirty = true;\n        }\n        column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0;\n\n        if (column->SortOrder != -1 && !column->IsEnabled)\n            table->IsSortSpecsDirty = true;\n        if (column->SortOrder > 0 && !(table->Flags & ImGuiTableFlags_SortMulti))\n            table->IsSortSpecsDirty = true;\n\n        // Auto-fit unsized columns\n        const bool start_auto_fit = (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? (column->WidthRequest < 0.0f) : (column->StretchWeight < 0.0f);\n        if (start_auto_fit)\n            column->AutoFitQueue = column->CannotSkipItemsQueue = (1 << 3) - 1; // Fit for three frames\n\n        if (!column->IsEnabled)\n        {\n            column->IndexWithinEnabledSet = -1;\n            continue;\n        }\n\n        // Mark as enabled and link to previous/next enabled column\n        column->PrevEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx;\n        column->NextEnabledColumn = -1;\n        if (prev_visible_column_idx != -1)\n            table->Columns[prev_visible_column_idx].NextEnabledColumn = (ImGuiTableColumnIdx)column_n;\n        else\n            table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n;\n        column->IndexWithinEnabledSet = table->ColumnsEnabledCount++;\n        ImBitArraySetBit(table->EnabledMaskByIndex, column_n);\n        ImBitArraySetBit(table->EnabledMaskByDisplayOrder, column->DisplayOrder);\n        prev_visible_column_idx = column_n;\n        IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder);\n\n        // Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)\n        // Combine width from regular rows + width from headers unless requested not to.\n        if (!column->IsPreserveWidthAuto && table->InstanceCurrent == 0)\n            column->WidthAuto = TableGetColumnWidthAuto(table, column);\n\n        // Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)\n        const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0;\n        if (column_is_resizable)\n            has_resizable = true;\n        if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f && !column_is_resizable)\n            column->WidthAuto = column->InitStretchWeightOrWidth;\n\n        if (column->AutoFitQueue != 0x00)\n            has_auto_fit_request = true;\n        if (column->Flags & ImGuiTableColumnFlags_WidthStretch)\n        {\n            stretch_sum_width_auto += column->WidthAuto;\n            count_stretch++;\n        }\n        else\n        {\n            fixed_max_width_auto = ImMax(fixed_max_width_auto, column->WidthAuto);\n            count_fixed++;\n        }\n    }\n    if ((table->Flags & ImGuiTableFlags_Sortable) && table->SortSpecsCount == 0 && !(table->Flags & ImGuiTableFlags_SortTristate))\n        table->IsSortSpecsDirty = true;\n    table->RightMostEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx;\n    IM_ASSERT(table->LeftMostEnabledColumn >= 0 && table->RightMostEnabledColumn >= 0);\n\n    // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible to avoid\n    // the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). Also see #6510.\n    // FIXME-TABLE: for always auto-resizing columns may not want to do that all the time.\n    if (has_auto_fit_request && table->OuterWindow != table->InnerWindow)\n        table->InnerWindow->SkipItems = false;\n    if (has_auto_fit_request)\n        table->IsSettingsDirty = true;\n\n    // [Part 3] Fix column flags and record a few extra information.\n    float sum_width_requests = 0.0f;    // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding.\n    float stretch_sum_weights = 0.0f;   // Sum of all weights for stretch columns.\n    table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))\n            continue;\n        ImGuiTableColumn* column = &table->Columns[column_n];\n\n        const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0;\n        if (column->Flags & ImGuiTableColumnFlags_WidthFixed)\n        {\n            // Apply same widths policy\n            float width_auto = column->WidthAuto;\n            if (table_sizing_policy == ImGuiTableFlags_SizingFixedSame && (column->AutoFitQueue != 0x00 || !column_is_resizable))\n                width_auto = fixed_max_width_auto;\n\n            // Apply automatic width\n            // Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!)\n            if (column->AutoFitQueue != 0x00)\n                column->WidthRequest = width_auto;\n            else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && column->IsRequestOutput)\n                column->WidthRequest = width_auto;\n\n            // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets\n            // (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very\n            // large height (= first frame scrollbar display very off + clipper would skip lots of items).\n            // This is merely making the side-effect less extreme, but doesn't properly fixes it.\n            // FIXME: Move this to ->WidthGiven to avoid temporary lossyness?\n            // FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller.\n            if (column->AutoFitQueue > 0x01 && table->IsInitializing && !column->IsPreserveWidthAuto)\n                column->WidthRequest = ImMax(column->WidthRequest, table->MinColumnWidth * 4.0f); // FIXME-TABLE: Another constant/scale?\n            sum_width_requests += column->WidthRequest;\n        }\n        else\n        {\n            // Initialize stretch weight\n            if (column->AutoFitQueue != 0x00 || column->StretchWeight < 0.0f || !column_is_resizable)\n            {\n                if (column->InitStretchWeightOrWidth > 0.0f)\n                    column->StretchWeight = column->InitStretchWeightOrWidth;\n                else if (table_sizing_policy == ImGuiTableFlags_SizingStretchProp)\n                    column->StretchWeight = (column->WidthAuto / stretch_sum_width_auto) * count_stretch;\n                else\n                    column->StretchWeight = 1.0f;\n            }\n\n            stretch_sum_weights += column->StretchWeight;\n            if (table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder > column->DisplayOrder)\n                table->LeftMostStretchedColumn = (ImGuiTableColumnIdx)column_n;\n            if (table->RightMostStretchedColumn == -1 || table->Columns[table->RightMostStretchedColumn].DisplayOrder < column->DisplayOrder)\n                table->RightMostStretchedColumn = (ImGuiTableColumnIdx)column_n;\n        }\n        column->IsPreserveWidthAuto = false;\n        sum_width_requests += table->CellPaddingX * 2.0f;\n    }\n    table->ColumnsEnabledFixedCount = (ImGuiTableColumnIdx)count_fixed;\n    table->ColumnsStretchSumWeights = stretch_sum_weights;\n\n    // [Part 4] Apply final widths based on requested widths\n    const ImRect work_rect = table->WorkRect;\n    const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);\n    const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synced tables with mismatching scrollbar state (#5920)\n    const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed);\n    const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests;\n    float width_remaining_for_stretched_columns = width_avail_for_stretched_columns;\n    table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))\n            continue;\n        ImGuiTableColumn* column = &table->Columns[column_n];\n\n        // Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest)\n        if (column->Flags & ImGuiTableColumnFlags_WidthStretch)\n        {\n            float weight_ratio = column->StretchWeight / stretch_sum_weights;\n            column->WidthRequest = IM_TRUNC(ImMax(width_avail_for_stretched_columns * weight_ratio, table->MinColumnWidth) + 0.01f);\n            width_remaining_for_stretched_columns -= column->WidthRequest;\n        }\n\n        // [Resize Rule 1] The right-most Visible column is not resizable if there is at least one Stretch column\n        // See additional comments in TableSetColumnWidth().\n        if (column->NextEnabledColumn == -1 && table->LeftMostStretchedColumn != -1)\n            column->Flags |= ImGuiTableColumnFlags_NoDirectResize_;\n\n        // Assign final width, record width in case we will need to shrink\n        column->WidthGiven = ImTrunc(ImMax(column->WidthRequest, table->MinColumnWidth));\n        table->ColumnsGivenWidth += column->WidthGiven;\n    }\n\n    // [Part 5] Redistribute stretch remainder width due to rounding (remainder width is < 1.0f * number of Stretch column).\n    // Using right-to-left distribution (more likely to match resizing cursor).\n    if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths))\n        for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--)\n        {\n            if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))\n                continue;\n            ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]];\n            if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch))\n                continue;\n            column->WidthRequest += 1.0f;\n            column->WidthGiven += 1.0f;\n            width_remaining_for_stretched_columns -= 1.0f;\n        }\n\n    // Determine if table is hovered which will be used to flag columns as hovered.\n    // - In principle we'd like to use the equivalent of IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),\n    //   but because our item is partially submitted at this point we use ItemHoverable() and a workaround (temporarily\n    //   clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem).\n    // - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop.\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    table_instance->HoveredRowLast = table_instance->HoveredRowNext;\n    table_instance->HoveredRowNext = -1;\n    table->HoveredColumnBody = table->HoveredColumnBorder = -1;\n    const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight));\n    const ImGuiID backup_active_id = g.ActiveId;\n    g.ActiveId = 0;\n    const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None);\n    g.ActiveId = backup_active_id;\n\n    // Determine skewed MousePos.x to support angled headers.\n    float mouse_skewed_x = g.IO.MousePos.x;\n    if (table->AngledHeadersHeight > 0.0f)\n        if (g.IO.MousePos.y >= table->OuterRect.Min.y && g.IO.MousePos.y <= table->OuterRect.Min.y + table->AngledHeadersHeight)\n            mouse_skewed_x += ImTrunc((table->OuterRect.Min.y + table->AngledHeadersHeight - g.IO.MousePos.y) * table->AngledHeadersSlope);\n\n    // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column\n    // Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping.\n    int visible_n = 0;\n    bool has_at_least_one_column_requesting_output = false;\n    bool offset_x_frozen = (table->FreezeColumnsCount > 0);\n    float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1;\n    ImRect host_clip_rect = table->InnerClipRect;\n    //host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2;\n    ImBitArrayClearAllBits(table->VisibleMaskByIndex, table->ColumnsCount);\n    for (int order_n = 0; order_n < table->ColumnsCount; order_n++)\n    {\n        const int column_n = table->DisplayOrderToIndex[order_n];\n        ImGuiTableColumn* column = &table->Columns[column_n];\n\n        // Initial nav layer: using FreezeRowsCount, NOT FreezeRowsRequest, so Header line changes layer when frozen\n        column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : (ImGuiNavLayer)table->NavLayer);\n\n        if (offset_x_frozen && table->FreezeColumnsCount == visible_n)\n        {\n            offset_x += work_rect.Min.x - table->OuterRect.Min.x;\n            offset_x_frozen = false;\n        }\n\n        // Clear status flags\n        column->Flags &= ~ImGuiTableColumnFlags_StatusMask_;\n\n        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))\n        {\n            // Hidden column: clear a few fields and we are done with it for the remainder of the function.\n            // We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper.\n            column->MinX = column->MaxX = column->WorkMinX = column->ClipRect.Min.x = column->ClipRect.Max.x = offset_x;\n            column->WidthGiven = 0.0f;\n            column->ClipRect.Min.y = work_rect.Min.y;\n            column->ClipRect.Max.y = FLT_MAX;\n            column->ClipRect.ClipWithFull(host_clip_rect);\n            column->IsVisibleX = column->IsVisibleY = column->IsRequestOutput = false;\n            column->IsSkipItems = true;\n            column->ItemWidth = 1.0f;\n            continue;\n        }\n\n        // Lock start position\n        column->MinX = offset_x;\n\n        // Lock width based on start position and minimum/maximum width for this position\n        column->WidthMax = TableCalcMaxColumnWidth(table, column_n);\n        column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax);\n        column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth));\n        column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;\n\n        // Lock other positions\n        // - ClipRect.Min.x: Because merging draw commands doesn't compare min boundaries, we make ClipRect.Min.x match left bounds to be consistent regardless of merging.\n        // - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column.\n        // - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow.\n        // - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter.\n        const float previous_instance_work_min_x = column->WorkMinX;\n        column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1;\n        column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max\n        column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f);\n        column->ClipRect.Min.x = column->MinX;\n        column->ClipRect.Min.y = work_rect.Min.y;\n        column->ClipRect.Max.x = column->MaxX; //column->WorkMaxX;\n        column->ClipRect.Max.y = FLT_MAX;\n        column->ClipRect.ClipWithFull(host_clip_rect);\n\n        // Mark column as Clipped (not in sight)\n        // Note that scrolling tables (where inner_window != outer_window) handle Y clipped earlier in BeginTable() so IsVisibleY really only applies to non-scrolling tables.\n        // FIXME-TABLE: Because InnerClipRect.Max.y is conservatively ==outer_window->ClipRect.Max.y, we never can mark columns _Above_ the scroll line as not IsVisibleY.\n        // Taking advantage of LastOuterHeight would yield good results there...\n        // FIXME-TABLE: Y clipping is disabled because it effectively means not submitting will reduce contents width which is fed to outer_window->DC.CursorMaxPos.x,\n        // and this may be used (e.g. typically by outer_window using AlwaysAutoResize or outer_window's horizontal scrollbar, but could be something else).\n        // Possible solution to preserve last known content width for clipped column. Test 'table_reported_size' fails when enabling Y clipping and window is resized small.\n        column->IsVisibleX = (column->ClipRect.Max.x > column->ClipRect.Min.x);\n        column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y);\n        const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY;\n        if (is_visible)\n            ImBitArraySetBit(table->VisibleMaskByIndex, column_n);\n\n        // Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output.\n        column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0;\n\n        // Mark column as SkipItems (ignoring all items/layout)\n        // (table->HostSkipItems is a copy of inner_window->SkipItems before we cleared it above in Part 2)\n        column->IsSkipItems = !column->IsEnabled || table->HostSkipItems;\n        if (column->IsSkipItems)\n            IM_ASSERT(!is_visible);\n        if (column->IsRequestOutput && !column->IsSkipItems)\n            has_at_least_one_column_requesting_output = true;\n\n        // Update status flags\n        column->Flags |= ImGuiTableColumnFlags_IsEnabled;\n        if (is_visible)\n            column->Flags |= ImGuiTableColumnFlags_IsVisible;\n        if (column->SortOrder != -1)\n            column->Flags |= ImGuiTableColumnFlags_IsSorted;\n\n        // Detect hovered column\n        if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)\n        {\n            column->Flags |= ImGuiTableColumnFlags_IsHovered;\n            table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;\n        }\n\n        // Alignment\n        // FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in\n        // many cases (to be able to honor this we might be able to store a log of cells width, per row, for\n        // visible rows, but nav/programmatic scroll would have visible artifacts.)\n        //if (column->Flags & ImGuiTableColumnFlags_AlignRight)\n        //    column->WorkMinX = ImMax(column->WorkMinX, column->MaxX - column->ContentWidthRowsUnfrozen);\n        //else if (column->Flags & ImGuiTableColumnFlags_AlignCenter)\n        //    column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f);\n\n        // Reset content width variables\n        if (table->InstanceCurrent == 0)\n        {\n            column->ContentMaxXFrozen = column->WorkMinX;\n            column->ContentMaxXUnfrozen = column->WorkMinX;\n            column->ContentMaxXHeadersUsed = column->WorkMinX;\n            column->ContentMaxXHeadersIdeal = column->WorkMinX;\n        }\n        else\n        {\n            // As we store an absolute value to make per-cell updates faster, we need to offset values used for width computation.\n            const float offset_from_previous_instance = column->WorkMinX - previous_instance_work_min_x;\n            column->ContentMaxXFrozen += offset_from_previous_instance;\n            column->ContentMaxXUnfrozen += offset_from_previous_instance;\n            column->ContentMaxXHeadersUsed += offset_from_previous_instance;\n            column->ContentMaxXHeadersIdeal += offset_from_previous_instance;\n        }\n\n        // Don't decrement auto-fit counters until container window got a chance to submit its items\n        if (table->HostSkipItems == false && table->InstanceCurrent == 0)\n        {\n            column->AutoFitQueue >>= 1;\n            column->CannotSkipItemsQueue >>= 1;\n        }\n\n        if (visible_n < table->FreezeColumnsCount)\n            host_clip_rect.Min.x = ImClamp(column->MaxX + TABLE_BORDER_SIZE, host_clip_rect.Min.x, host_clip_rect.Max.x);\n\n        offset_x += column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;\n        visible_n++;\n    }\n\n    // In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible.\n    // Else if give no chance to a clipper-savvy user to submit rows and therefore total contents height used by scrollbar.\n    if (has_at_least_one_column_requesting_output == false)\n    {\n        table->Columns[table->LeftMostEnabledColumn].IsRequestOutput = true;\n        table->Columns[table->LeftMostEnabledColumn].IsSkipItems = false;\n    }\n\n    // [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it)\n    // Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either\n    // because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu.\n    const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x);\n    if (is_hovering_table && table->HoveredColumnBody == -1)\n        if (mouse_skewed_x >= unused_x1)\n            table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount;\n    if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable))\n        table->Flags &= ~ImGuiTableFlags_Resizable;\n\n    table->IsActiveIdAliveBeforeTable = (g.ActiveIdIsAlive != 0);\n\n    // [Part 8] Lock actual OuterRect/WorkRect right-most position.\n    // This is done late to handle the case of fixed-columns tables not claiming more widths that they need.\n    // Because of this we are careful with uses of WorkRect and InnerClipRect before this point.\n    if (table->RightMostStretchedColumn != -1)\n        table->Flags &= ~ImGuiTableFlags_NoHostExtendX;\n    if (table->Flags & ImGuiTableFlags_NoHostExtendX)\n    {\n        table->OuterRect.Max.x = table->WorkRect.Max.x = unused_x1;\n        table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1);\n    }\n    table->InnerWindow->ParentWorkRect = table->WorkRect;\n    table->BorderX1 = table->InnerClipRect.Min.x;\n    table->BorderX2 = table->InnerClipRect.Max.x;\n\n    // Setup window's WorkRect.Max.y for GetContentRegionAvail(). Other values will be updated in each TableBeginCell() call.\n    float window_content_max_y;\n    if (table->Flags & ImGuiTableFlags_NoHostExtendY)\n        window_content_max_y = table->OuterRect.Max.y;\n    else\n        window_content_max_y = ImMax(table->InnerWindow->ContentRegionRect.Max.y, (table->Flags & ImGuiTableFlags_ScrollY) ? 0.0f : table->OuterRect.Max.y);\n    table->InnerWindow->WorkRect.Max.y = ImClamp(window_content_max_y - g.Style.CellPadding.y, table->InnerWindow->WorkRect.Min.y, table->InnerWindow->WorkRect.Max.y);\n\n    // [Part 9] Allocate draw channels and setup background cliprect\n    TableSetupDrawChannels(table);\n\n    // [Part 10] Hit testing on borders\n    if (table->Flags & ImGuiTableFlags_Resizable)\n        TableUpdateBorders(table);\n    table_instance->LastTopHeadersRowHeight = 0.0f;\n    table->IsLayoutLocked = true;\n    table->IsUsingHeaders = false;\n\n    // Highlight header\n    table->HighlightColumnHeader = -1;\n    if (table->IsContextPopupOpen && table->ContextPopupColumn != -1 && table->InstanceInteracted == table->InstanceCurrent)\n        table->HighlightColumnHeader = table->ContextPopupColumn;\n    else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != table->ColumnsCount && table->HoveredColumnBorder == -1)\n        if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive))\n            table->HighlightColumnHeader = table->HoveredColumnBody;\n\n    // [Part 11] Default context menu\n    // - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().\n    // - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup().\n    // - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,\n    //   e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.\n    if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))\n    {\n        TableDrawDefaultContextMenu(table, table->Flags);\n        EndPopup();\n    }\n\n    // [Part 12] Sanitize and build sort specs before we have a chance to use them for display.\n    // This path will only be exercised when sort specs are modified before header rows (e.g. init or visibility change)\n    if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable))\n        TableSortSpecsBuild(table);\n\n    // [Part 13] Setup inner window decoration size (for scrolling / nav tracking to properly take account of frozen rows/columns)\n    if (table->FreezeColumnsRequest > 0)\n        table->InnerWindow->DecoInnerSizeX1 = table->Columns[table->DisplayOrderToIndex[table->FreezeColumnsRequest - 1]].MaxX - table->OuterRect.Min.x;\n    if (table->FreezeRowsRequest > 0)\n        table->InnerWindow->DecoInnerSizeY1 = table_instance->LastFrozenHeight;\n    table_instance->LastFrozenHeight = 0.0f;\n\n    // Initial state\n    ImGuiWindow* inner_window = table->InnerWindow;\n    if (table->Flags & ImGuiTableFlags_NoClip)\n        table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);\n    else\n        inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect?\n}\n\n// Process hit-testing on resizing borders. Actual size change will be applied in EndTable()\n// - Set table->HoveredColumnBorder with a short delay/timer to reduce visual feedback noise.\nvoid ImGui::TableUpdateBorders(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(table->Flags & ImGuiTableFlags_Resizable);\n\n    // At this point OuterRect height may be zero or under actual final height, so we rely on temporal coherency and\n    // use the final height from last frame. Because this is only affecting _interaction_ with columns, it is not\n    // really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height).\n    // Actual columns highlight/render will be performed in EndTable() and not be affected.\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    const float hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale);\n    const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight;\n    const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight - table->AngledHeadersHeight);\n    const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight;\n\n    for (int order_n = 0; order_n < table->ColumnsCount; order_n++)\n    {\n        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))\n            continue;\n\n        const int column_n = table->DisplayOrderToIndex[order_n];\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_))\n            continue;\n\n        // ImGuiTableFlags_NoBordersInBodyUntilResize will be honored in TableDrawBorders()\n        const float border_y2_hit = (table->Flags & ImGuiTableFlags_NoBordersInBody) ? hit_y2_head : hit_y2_body;\n        if ((table->Flags & ImGuiTableFlags_NoBordersInBody) && table->IsUsingHeaders == false)\n            continue;\n\n        if (!column->IsVisibleX && table->LastResizedColumn != column_n)\n            continue;\n\n        ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent);\n        ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit);\n        ItemAdd(hit_rect, column_id, NULL, ImGuiItemFlags_NoNav);\n        //GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100));\n\n        bool hovered = false, held = false;\n        bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus);\n        if (pressed && IsMouseDoubleClicked(0))\n        {\n            TableSetColumnWidthAutoSingle(table, column_n);\n            ClearActiveID();\n            held = false;\n        }\n        if (held)\n        {\n            if (table->LastResizedColumn == -1)\n                table->ResizeLockMinContentsX2 = table->RightMostEnabledColumn != -1 ? table->Columns[table->RightMostEnabledColumn].MaxX : -FLT_MAX;\n            table->ResizedColumn = (ImGuiTableColumnIdx)column_n;\n            table->InstanceInteracted = table->InstanceCurrent;\n        }\n        if ((hovered && g.HoveredIdTimer > TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER) || held)\n        {\n            table->HoveredColumnBorder = (ImGuiTableColumnIdx)column_n;\n            SetMouseCursor(ImGuiMouseCursor_ResizeEW);\n        }\n    }\n}\n\nvoid    ImGui::EndTable()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT_USER_ERROR_RET(table != NULL, \"EndTable() call should only be done while in BeginTable() scope!\");\n\n    // This assert would be very useful to catch a common error... unfortunately it would probably trigger in some\n    // cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border)\n    //IM_ASSERT(table->IsLayoutLocked && \"Table unused: never called TableNextRow(), is that the intent?\");\n\n    // If the user never got to call TableNextRow() or TableNextColumn(), we call layout ourselves to ensure all our\n    // code paths are consistent (instead of just hoping that TableBegin/TableEnd will work), get borders drawn, etc.\n    if (!table->IsLayoutLocked)\n        TableUpdateLayout(table);\n\n    const ImGuiTableFlags flags = table->Flags;\n    ImGuiWindow* inner_window = table->InnerWindow;\n    ImGuiWindow* outer_window = table->OuterWindow;\n    ImGuiTableTempData* temp_data = table->TempData;\n    IM_ASSERT(inner_window == g.CurrentWindow && inner_window->ID == temp_data->WindowID);\n    IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow);\n\n    if (table->IsInsideRow)\n        TableEndRow(table);\n\n    // Context menu in columns body\n    if (flags & ImGuiTableFlags_ContextMenuInBody)\n        if (table->HoveredColumnBody != -1 && !IsAnyItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))\n            TableOpenContextMenu((int)table->HoveredColumnBody);\n\n    // Finalize table height\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize;\n    inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize;\n    inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos;\n    const float inner_content_max_y = ImCeil(table->RowPosY2); // Rounding final position is important as we currently don't round row height ('Demo->Tables->Outer Size' demo uses non-integer heights)\n    IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y);\n    if (inner_window != outer_window)\n        inner_window->DC.CursorMaxPos.y = inner_content_max_y;\n    else if (!(flags & ImGuiTableFlags_NoHostExtendY))\n        table->OuterRect.Max.y = table->InnerRect.Max.y = ImMax(table->OuterRect.Max.y, inner_content_max_y); // Patch OuterRect/InnerRect height\n    table->WorkRect.Max.y = ImMax(table->WorkRect.Max.y, table->OuterRect.Max.y);\n    table_instance->LastOuterHeight = table->OuterRect.GetHeight();\n\n    // Setup inner scrolling range\n    // FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y,\n    // but since the later is likely to be impossible to do we'd rather update both axes together.\n    if (table->Flags & ImGuiTableFlags_ScrollX)\n    {\n        const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;\n        float max_pos_x = table->InnerWindow->DC.CursorMaxPos.x;\n        if (table->RightMostEnabledColumn != -1)\n            max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border);\n        if (table->ResizedColumn != -1)\n            max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2);\n        table->InnerWindow->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledHeadersExtraWidth;\n    }\n\n    // Pop clipping rect\n    if (!(flags & ImGuiTableFlags_NoClip))\n        inner_window->DrawList->PopClipRect();\n    inner_window->ClipRect = inner_window->DrawList->_ClipRectStack.back();\n\n    // Draw borders\n    if ((flags & ImGuiTableFlags_Borders) != 0)\n        TableDrawBorders(table);\n\n#if 0\n    // Strip out dummy channel draw calls\n    // We have no way to prevent user submitting direct ImDrawList calls into a hidden column (but ImGui:: calls will be clipped out)\n    // Pros: remove draw calls which will have no effect. since they'll have zero-size cliprect they may be early out anyway.\n    // Cons: making it harder for users watching metrics/debugger to spot the wasted vertices.\n    if (table->DummyDrawChannel != (ImGuiTableColumnIdx)-1)\n    {\n        ImDrawChannel* dummy_channel = &table->DrawSplitter._Channels[table->DummyDrawChannel];\n        dummy_channel->_CmdBuffer.resize(0);\n        dummy_channel->_IdxBuffer.resize(0);\n    }\n#endif\n\n    // Flatten channels and merge draw calls\n    ImDrawListSplitter* splitter = table->DrawSplitter;\n    splitter->SetCurrentChannel(inner_window->DrawList, 0);\n    if ((table->Flags & ImGuiTableFlags_NoClip) == 0)\n        TableMergeDrawChannels(table);\n    splitter->Merge(inner_window->DrawList);\n\n    // Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable()\n    float auto_fit_width_for_fixed = 0.0f;\n    float auto_fit_width_for_stretched = 0.0f;\n    float auto_fit_width_for_stretched_min = 0.0f;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))\n        {\n            ImGuiTableColumn* column = &table->Columns[column_n];\n            float column_width_request = ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !(column->Flags & ImGuiTableColumnFlags_NoResize)) ? column->WidthRequest : TableGetColumnWidthAuto(table, column);\n            if (column->Flags & ImGuiTableColumnFlags_WidthFixed)\n                auto_fit_width_for_fixed += column_width_request;\n            else\n                auto_fit_width_for_stretched += column_width_request;\n            if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) && (column->Flags & ImGuiTableColumnFlags_NoResize) != 0)\n                auto_fit_width_for_stretched_min = ImMax(auto_fit_width_for_stretched_min, column_width_request / (column->StretchWeight / table->ColumnsStretchSumWeights));\n        }\n    const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);\n    table->ColumnsAutoFitWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount + auto_fit_width_for_fixed + ImMax(auto_fit_width_for_stretched, auto_fit_width_for_stretched_min);\n\n    // Update scroll\n    if ((table->Flags & ImGuiTableFlags_ScrollX) == 0 && inner_window != outer_window)\n    {\n        inner_window->Scroll.x = 0.0f;\n    }\n    else if (table->LastResizedColumn != -1 && table->ResizedColumn == -1 && inner_window->ScrollbarX && table->InstanceInteracted == table->InstanceCurrent)\n    {\n        // When releasing a column being resized, scroll to keep the resulting column in sight\n        const float neighbor_width_to_keep_visible = table->MinColumnWidth + table->CellPaddingX * 2.0f;\n        ImGuiTableColumn* column = &table->Columns[table->LastResizedColumn];\n        if (column->MaxX < table->InnerClipRect.Min.x)\n            SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x - neighbor_width_to_keep_visible, 1.0f);\n        else if (column->MaxX > table->InnerClipRect.Max.x)\n            SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x + neighbor_width_to_keep_visible, 1.0f);\n    }\n\n    // Apply resizing/dragging at the end of the frame\n    if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted)\n    {\n        ImGuiTableColumn* column = &table->Columns[table->ResizedColumn];\n        const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale));\n        const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f);\n        table->ResizedColumnNextWidth = new_width;\n    }\n\n    table->IsActiveIdInTable = (g.ActiveIdIsAlive != 0 && table->IsActiveIdAliveBeforeTable == false);\n\n    // Pop from id stack\n    IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table_instance->TableInstanceID, \"Mismatching PushID/PopID!\");\n    IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, \"Too many PopItemWidth!\");\n    if (table->InstanceCurrent > 0)\n        PopID();\n    PopID();\n\n    // Restore window data that we modified\n    const ImVec2 backup_outer_max_pos = outer_window->DC.CursorMaxPos;\n    inner_window->WorkRect = temp_data->HostBackupWorkRect;\n    inner_window->ParentWorkRect = temp_data->HostBackupParentWorkRect;\n    inner_window->SkipItems = table->HostSkipItems;\n    outer_window->DC.CursorPos = table->OuterRect.Min;\n    outer_window->DC.ItemWidth = temp_data->HostBackupItemWidth;\n    outer_window->DC.ItemWidthStack.Size = temp_data->HostBackupItemWidthStackSize;\n    outer_window->DC.ColumnsOffset = temp_data->HostBackupColumnsOffset;\n\n    // Layout in outer window\n    // (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding\n    // CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414)\n    if (inner_window != outer_window)\n    {\n        short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;\n        inner_window->DC.NavLayersActiveMask |= 1 << table->NavLayer; // So empty table don't appear to navigate differently.\n        g.CurrentTable = NULL; // To avoid error recovery recursing\n        EndChild();\n        g.CurrentTable = table;\n        inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;\n    }\n    else\n    {\n        table->InnerWindow->DC.TreeDepth--;\n        ItemSize(table->OuterRect.GetSize());\n        ItemAdd(table->OuterRect, 0);\n    }\n\n    // Override declared contents width/height to enable auto-resize while not needlessly adding a scrollbar\n    if (table->Flags & ImGuiTableFlags_NoHostExtendX)\n    {\n        // FIXME-TABLE: Could we remove this section?\n        // ColumnsAutoFitWidth may be one frame ahead here since for Fixed+NoResize is calculated from latest contents\n        IM_ASSERT((table->Flags & ImGuiTableFlags_ScrollX) == 0);\n        outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth);\n    }\n    else if (temp_data->UserOuterSize.x <= 0.0f)\n    {\n        // Some references for this: #7651 + tests \"table_reported_size\", \"table_reported_size_outer\" equivalent Y block\n        // - Checking for ImGuiTableFlags_ScrollX/ScrollY flag makes us a frame ahead when disabling those flags.\n        // - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback.\n        const float inner_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; // Slightly misleading name but used for code symmetry with inner_content_max_y\n        const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.x : 0.0f);\n        outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, inner_content_max_x + decoration_size - temp_data->UserOuterSize.x);\n        outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, inner_content_max_x + decoration_size));\n    }\n    else\n    {\n        outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Max.x);\n    }\n    if (temp_data->UserOuterSize.y <= 0.0f)\n    {\n        const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.y : 0.0f;\n        outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y);\n        outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y + decoration_size));\n    }\n    else\n    {\n        // OuterRect.Max.y may already have been pushed downward from the initial value (unless ImGuiTableFlags_NoHostExtendY is set)\n        outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, table->OuterRect.Max.y);\n    }\n\n    // Save settings\n    if (table->IsSettingsDirty)\n        TableSaveSettings(table);\n    table->IsInitializing = false;\n\n    // Clear or restore current table, if any\n    IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table);\n    IM_ASSERT(g.TablesTempDataStacked > 0);\n    temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL;\n    g.CurrentTable = temp_data && (temp_data->WindowID == outer_window->ID) ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL;\n    if (g.CurrentTable)\n    {\n        g.CurrentTable->TempData = temp_data;\n        g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter;\n    }\n    outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1;\n    NavUpdateCurrentWindowIsScrollPushableX();\n}\n\n// Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings.\n// 'init_mask' specify which fields to initialize.\nstatic void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)\n{\n    ImGuiTableColumnFlags flags = column->Flags;\n    if (init_mask & ImGuiTableFlags_Resizable)\n    {\n        float init_width_or_weight = column->InitStretchWeightOrWidth;\n        column->WidthRequest = ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;\n        column->StretchWeight = (init_width_or_weight > 0.0f && (flags & ImGuiTableColumnFlags_WidthStretch)) ? init_width_or_weight : -1.0f;\n        if (init_width_or_weight > 0.0f) // Disable auto-fit if an explicit width/weight has been specified\n            column->AutoFitQueue = 0x00;\n    }\n    if (init_mask & ImGuiTableFlags_Reorderable)\n        column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column);\n    if (init_mask & ImGuiTableFlags_Hideable)\n        column->IsUserEnabled = column->IsUserEnabledNextFrame = (flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1;\n    if (init_mask & ImGuiTableFlags_Sortable)\n    {\n        // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.\n        column->SortOrder = (flags & ImGuiTableColumnFlags_DefaultSort) ? 0 : -1;\n        column->SortDirection = (flags & ImGuiTableColumnFlags_DefaultSort) ? ((flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending)) : (ImS8)ImGuiSortDirection_None;\n    }\n}\n\n// See \"COLUMNS SIZING POLICIES\" comments at the top of this file\n// If (init_width_or_weight <= 0.0f) it is ignored\nvoid ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT_USER_ERROR_RET(table != NULL, \"Call should only be done while in BeginTable() scope!\");\n    IM_ASSERT_USER_ERROR_RET(table->DeclColumnsCount < table->ColumnsCount, \"TableSetupColumn(): called too many times!\");\n    IM_ASSERT_USER_ERROR_RET(table->IsLayoutLocked == false, \"TableSetupColumn(): need to call before first row!\");\n    IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && \"Illegal to pass StatusMask values to TableSetupColumn()\");\n\n    ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount];\n    table->DeclColumnsCount++;\n\n    // Assert when passing a width or weight if policy is entirely left to default, to avoid storing width into weight and vice-versa.\n    // Give a grace to users of ImGuiTableFlags_ScrollX.\n    if (table->IsDefaultSizingPolicy && (flags & ImGuiTableColumnFlags_WidthMask_) == 0 && (flags & ImGuiTableFlags_ScrollX) == 0)\n        IM_ASSERT_USER_ERROR_RET(init_width_or_weight <= 0.0f, \"TableSetupColumn(): can only specify width/weight if sizing policy is set explicitly in either Table or Column.\");\n\n    // When passing a width automatically enforce WidthFixed policy\n    // (whereas TableSetupColumnFlags would default to WidthAuto if table is not resizable)\n    if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f)\n        if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)\n            flags |= ImGuiTableColumnFlags_WidthFixed;\n    if (flags & ImGuiTableColumnFlags_AngledHeader)\n    {\n        flags |= ImGuiTableColumnFlags_NoHeaderLabel;\n        table->AngledHeadersCount++;\n    }\n\n    TableSetupColumnFlags(table, column, flags);\n    column->UserID = user_id;\n    flags = column->Flags;\n\n    // Initialize defaults\n    column->InitStretchWeightOrWidth = init_width_or_weight;\n    if (table->IsInitializing)\n    {\n        ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags;\n        if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f)\n            init_flags |= ImGuiTableFlags_Resizable;\n        TableInitColumnDefaults(table, column, init_flags);\n    }\n\n    // Store name (append with zero-terminator in contiguous buffer)\n    // FIXME: If we recorded the number of \\n in names we could compute header row height\n    column->NameOffset = -1;\n    if (label != NULL && label[0] != 0)\n    {\n        column->NameOffset = (ImS16)table->ColumnsNames.size();\n        table->ColumnsNames.append(label, label + ImStrlen(label) + 1);\n    }\n}\n\n// [Public]\nvoid ImGui::TableSetupScrollFreeze(int columns, int rows)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT_USER_ERROR_RET(table != NULL, \"Call should only be done while in BeginTable() scope!\");\n    IM_ASSERT(table->IsLayoutLocked == false && \"TableSetupColumn(): need to call before first row!\");\n    IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS);\n    IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit\n\n    table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)ImMin(columns, table->ColumnsCount) : 0;\n    table->FreezeColumnsCount = (table->InnerWindow->Scroll.x != 0.0f) ? table->FreezeColumnsRequest : 0;\n    table->FreezeRowsRequest = (table->Flags & ImGuiTableFlags_ScrollY) ? (ImGuiTableColumnIdx)rows : 0;\n    table->FreezeRowsCount = (table->InnerWindow->Scroll.y != 0.0f) ? table->FreezeRowsRequest : 0;\n    table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b\n\n    // Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered.\n    // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section)\n    for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++)\n    {\n        int order_n = table->DisplayOrderToIndex[column_n];\n        if (order_n != column_n && order_n >= table->FreezeColumnsRequest)\n        {\n            ImSwap(table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder, table->Columns[table->DisplayOrderToIndex[column_n]].DisplayOrder);\n            ImSwap(table->DisplayOrderToIndex[order_n], table->DisplayOrderToIndex[column_n]);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Tables: Simple accessors\n//-----------------------------------------------------------------------------\n// - TableGetColumnCount()\n// - TableGetColumnName()\n// - TableGetColumnName() [Internal]\n// - TableSetColumnEnabled()\n// - TableGetColumnFlags()\n// - TableGetCellBgRect() [Internal]\n// - TableGetColumnResizeID() [Internal]\n// - TableGetHoveredColumn() [Internal]\n// - TableGetHoveredRow() [Internal]\n// - TableSetBgColor()\n//-----------------------------------------------------------------------------\n\nint ImGui::TableGetColumnCount()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    return table ? table->ColumnsCount : 0;\n}\n\nconst char* ImGui::TableGetColumnName(int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return NULL;\n    if (column_n < 0)\n        column_n = table->CurrentColumn;\n    return TableGetColumnName(table, column_n);\n}\n\nconst char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n)\n{\n    if (table->IsLayoutLocked == false && column_n >= table->DeclColumnsCount)\n        return \"\"; // NameOffset is invalid at this point\n    const ImGuiTableColumn* column = &table->Columns[column_n];\n    if (column->NameOffset == -1)\n        return \"\";\n    return &table->ColumnsNames.Buf[column->NameOffset];\n}\n\n// Change user accessible enabled/disabled state of a column (often perceived as \"showing/hiding\" from users point of view)\n// Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody)\n// - Require table to have the ImGuiTableFlags_Hideable flag because we are manipulating user accessible state.\n// - Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable().\n// - For the getter you can test (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) != 0.\n// - Alternative: the ImGuiTableColumnFlags_Disabled is an overriding/master disable flag which will also hide the column from context menu.\nvoid ImGui::TableSetColumnEnabled(int column_n, bool enabled)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT_USER_ERROR_RET(table != NULL, \"Call should only be done while in BeginTable() scope!\");\n    IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above\n    if (column_n < 0)\n        column_n = table->CurrentColumn;\n    IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);\n    ImGuiTableColumn* column = &table->Columns[column_n];\n    column->IsUserEnabledNextFrame = enabled;\n}\n\n// We allow querying for an extra column in order to poll the IsHovered state of the right-most section\nImGuiTableColumnFlags ImGui::TableGetColumnFlags(int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return ImGuiTableColumnFlags_None;\n    if (column_n < 0)\n        column_n = table->CurrentColumn;\n    if (column_n == table->ColumnsCount)\n        return (table->HoveredColumnBody == column_n) ? ImGuiTableColumnFlags_IsHovered : ImGuiTableColumnFlags_None;\n    return table->Columns[column_n].Flags;\n}\n\n// Return the cell rectangle based on currently known height.\n// - Important: we generally don't know our row height until the end of the row, so Max.y will be incorrect in many situations.\n//   The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it, or in TableEndRow() when we locked that height.\n// - Important: if ImGuiTableFlags_PadOuterX is set but ImGuiTableFlags_PadInnerX is not set, the outer-most left and right\n//   columns report a small offset so their CellBgRect can extend up to the outer border.\n//   FIXME: But the rendering code in TableEndRow() nullifies that with clamping required for scrolling.\nImRect ImGui::TableGetCellBgRect(const ImGuiTable* table, int column_n)\n{\n    const ImGuiTableColumn* column = &table->Columns[column_n];\n    float x1 = column->MinX;\n    float x2 = column->MaxX;\n    //if (column->PrevEnabledColumn == -1)\n    //    x1 -= table->OuterPaddingX;\n    //if (column->NextEnabledColumn == -1)\n    //    x2 += table->OuterPaddingX;\n    x1 = ImMax(x1, table->WorkRect.Min.x);\n    x2 = ImMin(x2, table->WorkRect.Max.x);\n    return ImRect(x1, table->RowPosY1, x2, table->RowPosY2);\n}\n\n// Return the resizing ID for the right-side of the given column.\nImGuiID ImGui::TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no)\n{\n    IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);\n    ImGuiID instance_id = TableGetInstanceID(table, instance_no);\n    return instance_id + 1 + column_n; // FIXME: #6140: still not ideal\n}\n\n// Return -1 when table is not hovered. return columns_count if hovering the unused space at the right of the right-most visible column.\nint ImGui::TableGetHoveredColumn()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return -1;\n    return (int)table->HoveredColumnBody;\n}\n\n// Return -1 when table is not hovered. Return maxrow+1 if in table but below last submitted row.\n// *IMPORTANT* Unlike TableGetHoveredColumn(), this has a one frame latency in updating the value.\n// This difference with is the reason why this is not public yet.\nint ImGui::TableGetHoveredRow()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return -1;\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    return (int)table_instance->HoveredRowLast;\n}\n\nvoid ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT_USER_ERROR_RET(table != NULL, \"Call should only be done while in BeginTable() scope!\");\n    IM_ASSERT(target != ImGuiTableBgTarget_None);\n\n    if (color == IM_COL32_DISABLE)\n        color = 0;\n\n    // We cannot draw neither the cell or row background immediately as we don't know the row height at this point in time.\n    switch (target)\n    {\n    case ImGuiTableBgTarget_CellBg:\n    {\n        if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard\n            return;\n        if (column_n == -1)\n            column_n = table->CurrentColumn;\n        if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n))\n            return;\n        if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n)\n            table->RowCellDataCurrent++;\n        ImGuiTableCellData* cell_data = &table->RowCellData[table->RowCellDataCurrent];\n        cell_data->BgColor = color;\n        cell_data->Column = (ImGuiTableColumnIdx)column_n;\n        break;\n    }\n    case ImGuiTableBgTarget_RowBg0:\n    case ImGuiTableBgTarget_RowBg1:\n    {\n        if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard\n            return;\n        IM_ASSERT(column_n == -1);\n        int bg_idx = (target == ImGuiTableBgTarget_RowBg1) ? 1 : 0;\n        table->RowBgColor[bg_idx] = color;\n        break;\n    }\n    default:\n        IM_ASSERT(0);\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Row changes\n//-------------------------------------------------------------------------\n// - TableGetRowIndex()\n// - TableNextRow()\n// - TableBeginRow() [Internal]\n// - TableEndRow() [Internal]\n//-------------------------------------------------------------------------\n\n// [Public] Note: for row coloring we use ->RowBgColorCounter which is the same value without counting header rows\nint ImGui::TableGetRowIndex()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return 0;\n    return table->CurrentRow;\n}\n\n// [Public] Starts into the first cell of a new row\nvoid ImGui::TableNextRow(ImGuiTableRowFlags row_flags, float row_min_height)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n\n    if (!table->IsLayoutLocked)\n        TableUpdateLayout(table);\n    if (table->IsInsideRow)\n        TableEndRow(table);\n\n    table->LastRowFlags = table->RowFlags;\n    table->RowFlags = row_flags;\n    table->RowCellPaddingY = g.Style.CellPadding.y;\n    table->RowMinHeight = row_min_height;\n    TableBeginRow(table);\n\n    // We honor min_row_height requested by user, but cannot guarantee per-row maximum height,\n    // because that would essentially require a unique clipping rectangle per-cell.\n    table->RowPosY2 += table->RowCellPaddingY * 2.0f;\n    table->RowPosY2 = ImMax(table->RowPosY2, table->RowPosY1 + row_min_height);\n\n    // Disable output until user calls TableNextColumn()\n    table->InnerWindow->SkipItems = true;\n}\n\n// [Internal] Only called by TableNextRow()\nvoid ImGui::TableBeginRow(ImGuiTable* table)\n{\n    ImGuiWindow* window = table->InnerWindow;\n    IM_ASSERT(!table->IsInsideRow);\n\n    // New row\n    table->CurrentRow++;\n    table->CurrentColumn = -1;\n    table->RowBgColor[0] = table->RowBgColor[1] = IM_COL32_DISABLE;\n    table->RowCellDataCurrent = -1;\n    table->IsInsideRow = true;\n\n    // Begin frozen rows\n    float next_y1 = table->RowPosY2;\n    if (table->CurrentRow == 0 && table->FreezeRowsCount > 0)\n        next_y1 = window->DC.CursorPos.y = table->OuterRect.Min.y;\n\n    table->RowPosY1 = table->RowPosY2 = next_y1;\n    table->RowTextBaseline = 0.0f;\n    table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent\n\n    window->DC.PrevLineTextBaseOffset = 0.0f;\n    window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + table->RowCellPaddingY); // This allows users to call SameLine() to share LineSize between columns.\n    window->DC.PrevLineSize = window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // This allows users to call SameLine() to share LineSize between columns, and to call it from first column too.\n    window->DC.IsSameLine = window->DC.IsSetPos = false;\n    window->DC.CursorMaxPos.y = next_y1;\n\n    // Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging.\n    if (table->RowFlags & ImGuiTableRowFlags_Headers)\n    {\n        TableSetBgColor(ImGuiTableBgTarget_RowBg0, GetColorU32(ImGuiCol_TableHeaderBg));\n        if (table->CurrentRow == 0)\n            table->IsUsingHeaders = true;\n    }\n}\n\n// [Internal] Called by TableNextRow()\nvoid ImGui::TableEndRow(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(window == table->InnerWindow);\n    IM_ASSERT(table->IsInsideRow);\n\n    if (table->CurrentColumn != -1)\n    {\n        TableEndCell(table);\n        table->CurrentColumn = -1;\n    }\n\n    // Logging\n    if (g.LogEnabled)\n        LogRenderedText(NULL, \"|\");\n\n    // Position cursor at the bottom of our row so it can be used for e.g. clipping calculation. However it is\n    // likely that the next call to TableBeginCell() will reposition the cursor to take account of vertical padding.\n    window->DC.CursorPos.y = table->RowPosY2;\n\n    // Row background fill\n    const float bg_y1 = table->RowPosY1;\n    const float bg_y2 = table->RowPosY2;\n    const bool unfreeze_rows_actual = (table->CurrentRow + 1 == table->FreezeRowsCount);\n    const bool unfreeze_rows_request = (table->CurrentRow + 1 == table->FreezeRowsRequest);\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    if ((table->RowFlags & ImGuiTableRowFlags_Headers) && (table->CurrentRow == 0 || (table->LastRowFlags & ImGuiTableRowFlags_Headers)))\n        table_instance->LastTopHeadersRowHeight += bg_y2 - bg_y1;\n\n    const bool is_visible = (bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y);\n    if (is_visible)\n    {\n        // Update data for TableGetHoveredRow()\n        if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2 && table_instance->HoveredRowNext < 0)\n            table_instance->HoveredRowNext = table->CurrentRow;\n\n        // Decide of background color for the row\n        ImU32 bg_col0 = 0;\n        ImU32 bg_col1 = 0;\n        if (table->RowBgColor[0] != IM_COL32_DISABLE)\n            bg_col0 = table->RowBgColor[0];\n        else if (table->Flags & ImGuiTableFlags_RowBg)\n            bg_col0 = GetColorU32((table->RowBgColorCounter & 1) ? ImGuiCol_TableRowBgAlt : ImGuiCol_TableRowBg);\n        if (table->RowBgColor[1] != IM_COL32_DISABLE)\n            bg_col1 = table->RowBgColor[1];\n\n        // Decide of top border color\n        ImU32 top_border_col = 0;\n        const float border_size = TABLE_BORDER_SIZE;\n        if (table->CurrentRow > 0 && (table->Flags & ImGuiTableFlags_BordersInnerH))\n            top_border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight;\n\n        const bool draw_cell_bg_color = table->RowCellDataCurrent >= 0;\n        const bool draw_strong_bottom_border = unfreeze_rows_actual;\n        if ((bg_col0 | bg_col1 | top_border_col) != 0 || draw_strong_bottom_border || draw_cell_bg_color)\n        {\n            // In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is\n            // always followed by a change of clipping rectangle we perform the smallest overwrite possible here.\n            if ((table->Flags & ImGuiTableFlags_NoClip) == 0)\n                window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4();\n            table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0);\n        }\n\n        // Draw row background\n        // We soft/cpu clip this so all backgrounds and borders can share the same clipping rectangle\n        if (bg_col0 || bg_col1)\n        {\n            ImRect row_rect(table->WorkRect.Min.x, bg_y1, table->WorkRect.Max.x, bg_y2);\n            row_rect.ClipWith(table->BgClipRect);\n            if (bg_col0 != 0 && row_rect.Min.y < row_rect.Max.y)\n                window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col0);\n            if (bg_col1 != 0 && row_rect.Min.y < row_rect.Max.y)\n                window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col1);\n        }\n\n        // Draw cell background color\n        if (draw_cell_bg_color)\n        {\n            ImGuiTableCellData* cell_data_end = &table->RowCellData[table->RowCellDataCurrent];\n            for (ImGuiTableCellData* cell_data = &table->RowCellData[0]; cell_data <= cell_data_end; cell_data++)\n            {\n                // As we render the BG here we need to clip things (for layout we would not)\n                // FIXME: This cancels the OuterPadding addition done by TableGetCellBgRect(), need to keep it while rendering correctly while scrolling.\n                const ImGuiTableColumn* column = &table->Columns[cell_data->Column];\n                ImRect cell_bg_rect = TableGetCellBgRect(table, cell_data->Column);\n                cell_bg_rect.ClipWith(table->BgClipRect);\n                cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x);     // So that first column after frozen one gets clipped when scrolling\n                cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX);\n                if (cell_bg_rect.Min.y < cell_bg_rect.Max.y)\n                    window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor);\n            }\n        }\n\n        // Draw top border\n        if (top_border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y)\n            window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), top_border_col, border_size);\n\n        // Draw bottom border at the row unfreezing mark (always strong)\n        if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y)\n            window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong, border_size);\n    }\n\n    // End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle)\n    // - We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark\n    //   end of row and get the new cursor position.\n    if (unfreeze_rows_request)\n    {\n        IM_ASSERT(table->FreezeRowsRequest > 0);\n        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n            table->Columns[column_n].NavLayerCurrent = table->NavLayer;\n        const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y);\n        table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;\n\n        if (unfreeze_rows_actual)\n        {\n            IM_ASSERT(table->IsUnfrozenRows == false);\n            table->IsUnfrozenRows = true;\n\n            // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect\n            table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, table->InnerClipRect.Max.y);\n            table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = table->InnerClipRect.Max.y;\n            table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;\n            IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);\n\n            float row_height = table->RowPosY2 - table->RowPosY1;\n            table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;\n            table->RowPosY1 = table->RowPosY2 - row_height;\n            for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n            {\n                ImGuiTableColumn* column = &table->Columns[column_n];\n                column->DrawChannelCurrent = column->DrawChannelUnfrozen;\n                column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;\n            }\n\n            // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y\n            SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);\n            table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);\n        }\n    }\n\n    if (!(table->RowFlags & ImGuiTableRowFlags_Headers))\n        table->RowBgColorCounter++;\n    table->IsInsideRow = false;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Columns changes\n//-------------------------------------------------------------------------\n// - TableGetColumnIndex()\n// - TableSetColumnIndex()\n// - TableNextColumn()\n// - TableBeginCell() [Internal]\n// - TableEndCell() [Internal]\n//-------------------------------------------------------------------------\n\nint ImGui::TableGetColumnIndex()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return 0;\n    return table->CurrentColumn;\n}\n\n// [Public] Append into a specific column\nbool ImGui::TableSetColumnIndex(int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return false;\n\n    if (table->CurrentColumn != column_n)\n    {\n        if (table->CurrentColumn != -1)\n            TableEndCell(table);\n        IM_ASSERT_USER_ERROR_RETV(column_n >= 0 && column_n < table->ColumnsCount, false, \"TableSetColumnIndex() invalid column index!\");\n        TableBeginCell(table, column_n);\n    }\n\n    // Return whether the column is visible. User may choose to skip submitting items based on this return value,\n    // however they shouldn't skip submitting for columns that may have the tallest contribution to row height.\n    return table->Columns[column_n].IsRequestOutput;\n}\n\n// [Public] Append into the next column, wrap and create a new row when already on last column\nbool ImGui::TableNextColumn()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return false;\n\n    if (table->IsInsideRow && table->CurrentColumn + 1 < table->ColumnsCount)\n    {\n        if (table->CurrentColumn != -1)\n            TableEndCell(table);\n        TableBeginCell(table, table->CurrentColumn + 1);\n    }\n    else\n    {\n        TableNextRow();\n        TableBeginCell(table, 0);\n    }\n\n    // Return whether the column is visible. User may choose to skip submitting items based on this return value,\n    // however they shouldn't skip submitting for columns that may have the tallest contribution to row height.\n    return table->Columns[table->CurrentColumn].IsRequestOutput;\n}\n\n\n// [Internal] Called by TableSetColumnIndex()/TableNextColumn()\n// This is called very frequently, so we need to be mindful of unnecessary overhead.\n// FIXME-TABLE FIXME-OPT: Could probably shortcut some things for non-active or clipped columns.\nvoid ImGui::TableBeginCell(ImGuiTable* table, int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTableColumn* column = &table->Columns[column_n];\n    ImGuiWindow* window = table->InnerWindow;\n    table->CurrentColumn = column_n;\n\n    // Start position is roughly ~~ CellRect.Min + CellPadding + Indent\n    float start_x = column->WorkMinX;\n    if (column->Flags & ImGuiTableColumnFlags_IndentEnable)\n        start_x += table->RowIndentOffsetX; // ~~ += window.DC.Indent.x - table->HostIndentX, except we locked it for the row.\n\n    window->DC.CursorPos.x = start_x;\n    window->DC.CursorPos.y = table->RowPosY1 + table->RowCellPaddingY;\n    window->DC.CursorMaxPos.x = window->DC.CursorPos.x;\n    window->DC.ColumnsOffset.x = start_x - window->Pos.x - window->DC.Indent.x; // FIXME-WORKRECT\n    window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x; // PrevLine.y is preserved. This allows users to call SameLine() to share LineSize between columns.\n    window->DC.CurrLineTextBaseOffset = table->RowTextBaseline;\n    window->DC.NavLayerCurrent = (ImGuiNavLayer)column->NavLayerCurrent;\n\n    // Note how WorkRect.Max.y is only set once during layout\n    window->WorkRect.Min.y = window->DC.CursorPos.y;\n    window->WorkRect.Min.x = column->WorkMinX;\n    window->WorkRect.Max.x = column->WorkMaxX;\n    window->DC.ItemWidth = column->ItemWidth;\n\n    window->SkipItems = column->IsSkipItems;\n    if (column->IsSkipItems)\n    {\n        g.LastItemData.ID = 0;\n        g.LastItemData.StatusFlags = 0;\n    }\n\n    // Also see TablePushColumnChannel()\n    if (table->Flags & ImGuiTableFlags_NoClip)\n    {\n        // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.\n        table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);\n        //IM_ASSERT(table->DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP);\n    }\n    else\n    {\n        // FIXME-TABLE: Could avoid this if draw channel is dummy channel?\n        SetWindowClipRectBeforeSetChannel(window, column->ClipRect);\n        table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);\n    }\n\n    // Logging\n    if (g.LogEnabled && !column->IsSkipItems)\n    {\n        LogRenderedText(&window->DC.CursorPos, \"|\");\n        g.LogLinePosY = FLT_MAX;\n    }\n}\n\n// [Internal] Called by TableNextRow()/TableSetColumnIndex()/TableNextColumn()\nvoid ImGui::TableEndCell(ImGuiTable* table)\n{\n    ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];\n    ImGuiWindow* window = table->InnerWindow;\n\n    if (window->DC.IsSetPos)\n        ErrorCheckUsingSetCursorPosToExtendParentBoundaries();\n\n    // Report maximum position so we can infer content size per column.\n    float* p_max_pos_x;\n    if (table->RowFlags & ImGuiTableRowFlags_Headers)\n        p_max_pos_x = &column->ContentMaxXHeadersUsed;  // Useful in case user submit contents in header row that is not a TableHeader() call\n    else\n        p_max_pos_x = table->IsUnfrozenRows ? &column->ContentMaxXUnfrozen : &column->ContentMaxXFrozen;\n    *p_max_pos_x = ImMax(*p_max_pos_x, window->DC.CursorMaxPos.x);\n    if (column->IsEnabled)\n        table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->RowCellPaddingY);\n    column->ItemWidth = window->DC.ItemWidth;\n\n    // Propagate text baseline for the entire row\n    // FIXME-TABLE: Here we propagate text baseline from the last line of the cell.. instead of the first one.\n    table->RowTextBaseline = ImMax(table->RowTextBaseline, window->DC.PrevLineTextBaseOffset);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Columns width management\n//-------------------------------------------------------------------------\n// - TableGetMaxColumnWidth() [Internal]\n// - TableGetColumnWidthAuto() [Internal]\n// - TableSetColumnWidth()\n// - TableSetColumnWidthAutoSingle() [Internal]\n// - TableSetColumnWidthAutoAll() [Internal]\n// - TableUpdateColumnsWeightFromWidth() [Internal]\n//-------------------------------------------------------------------------\n// Note that actual columns widths are computed in TableUpdateLayout().\n//-------------------------------------------------------------------------\n\n// Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis.\nfloat ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n)\n{\n    const ImGuiTableColumn* column = &table->Columns[column_n];\n    float max_width = FLT_MAX;\n    const float min_column_distance = table->MinColumnWidth + table->CellPaddingX * 2.0f + table->CellSpacingX1 + table->CellSpacingX2;\n    if (table->Flags & ImGuiTableFlags_ScrollX)\n    {\n        // Frozen columns can't reach beyond visible width else scrolling will naturally break.\n        // (we use DisplayOrder as within a set of multiple frozen column reordering is possible)\n        if (column->DisplayOrder < table->FreezeColumnsRequest)\n        {\n            max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - column->DisplayOrder) * min_column_distance) - column->MinX;\n            max_width = max_width - table->OuterPaddingX - table->CellPaddingX - table->CellSpacingX2;\n        }\n    }\n    else if ((table->Flags & ImGuiTableFlags_NoKeepColumnsVisible) == 0)\n    {\n        // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make\n        // sure they are all visible. Because of this we also know that all of the columns will always fit in\n        // table->WorkRect and therefore in table->InnerRect (because ScrollX is off)\n        // FIXME-TABLE: This is solved incorrectly but also quite a difficult problem to fix as we also want ClipRect width to match.\n        // See \"table_width_distrib\" and \"table_width_keep_visible\" tests\n        max_width = table->WorkRect.Max.x - (table->ColumnsEnabledCount - column->IndexWithinEnabledSet - 1) * min_column_distance - column->MinX;\n        //max_width -= table->CellSpacingX1;\n        max_width -= table->CellSpacingX2;\n        max_width -= table->CellPaddingX * 2.0f;\n        max_width -= table->OuterPaddingX;\n    }\n    return max_width;\n}\n\n// Note this is meant to be stored in column->WidthAuto, please generally use the WidthAuto field\nfloat ImGui::TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column)\n{\n    const float content_width_body = ImMax(column->ContentMaxXFrozen, column->ContentMaxXUnfrozen) - column->WorkMinX;\n    const float content_width_headers = column->ContentMaxXHeadersIdeal - column->WorkMinX;\n    float width_auto = content_width_body;\n    if (!(column->Flags & ImGuiTableColumnFlags_NoHeaderWidth))\n        width_auto = ImMax(width_auto, content_width_headers);\n\n    // Non-resizable fixed columns preserve their requested width\n    if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f)\n        if (!(table->Flags & ImGuiTableFlags_Resizable) || (column->Flags & ImGuiTableColumnFlags_NoResize))\n            width_auto = column->InitStretchWeightOrWidth;\n\n    return ImMax(width_auto, table->MinColumnWidth);\n}\n\n// 'width' = inner column width, without padding\nvoid ImGui::TableSetColumnWidth(int column_n, float width)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT(table != NULL && table->IsLayoutLocked == false);\n    IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);\n    ImGuiTableColumn* column_0 = &table->Columns[column_n];\n    float column_0_width = width;\n\n    // Apply constraints early\n    // Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)\n    IM_ASSERT(table->MinColumnWidth > 0.0f);\n    const float min_width = table->MinColumnWidth;\n    const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933)\n    column_0_width = ImClamp(column_0_width, min_width, max_width);\n    if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)\n        return;\n\n    //IMGUI_DEBUG_PRINT(\"TableSetColumnWidth(%d, %.1f->%.1f)\\n\", column_0_idx, column_0->WidthGiven, column_0_width);\n    ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL;\n\n    // In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns.\n    // - All fixed: easy.\n    // - All stretch: easy.\n    // - One or more fixed + one stretch: easy.\n    // - One or more fixed + more than one stretch: tricky.\n    // Qt when manual resize is enabled only supports a single _trailing_ stretch column, we support more cases here.\n\n    // When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1.\n    // FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user.\n    // Scenarios:\n    // - F1 F2 F3  resize from F1| or F2|   --> ok: alter ->WidthRequested of Fixed column. Subsequent columns will be offset.\n    // - F1 F2 F3  resize from F3|          --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered.\n    // - F1 F2 W3  resize from F1| or F2|   --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered, but it doesn't make much sense as the Stretch column will always be minimal size.\n    // - F1 F2 W3  resize from W3|          --> ok: no-op (disabled by Resize Rule 1)\n    // - W1 W2 W3  resize from W1| or W2|   --> ok\n    // - W1 W2 W3  resize from W3|          --> ok: no-op (disabled by Resize Rule 1)\n    // - W1 F2 F3  resize from F3|          --> ok: no-op (disabled by Resize Rule 1)\n    // - W1 F2     resize from F2|          --> ok: no-op (disabled by Resize Rule 1)\n    // - W1 W2 F3  resize from W1| or W2|   --> ok\n    // - W1 F2 W3  resize from W1| or F2|   --> ok\n    // - F1 W2 F3  resize from W2|          --> ok\n    // - F1 W3 F2  resize from W3|          --> ok\n    // - W1 F2 F3  resize from W1|          --> ok: equivalent to resizing |F2. F3 will not move.\n    // - W1 F2 F3  resize from F2|          --> ok\n    // All resizes from a Wx columns are locking other columns.\n\n    // Possible improvements:\n    // - W1 W2 W3  resize W1|               --> to not be stuck, both W2 and W3 would stretch down. Seems possible to fix. Would be most beneficial to simplify resize of all-weighted columns.\n    // - W3 F1 F2  resize W3|               --> to not be stuck past F1|, both F1 and F2 would need to stretch down, which would be lossy or ambiguous. Seems hard to fix.\n\n    // [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout().\n\n    // If we have all Fixed columns OR resizing a Fixed column that doesn't come after a Stretch one, we can do an offsetting resize.\n    // This is the preferred resize path\n    if (column_0->Flags & ImGuiTableColumnFlags_WidthFixed)\n        if (!column_1 || table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder >= column_0->DisplayOrder)\n        {\n            column_0->WidthRequest = column_0_width;\n            table->IsSettingsDirty = true;\n            return;\n        }\n\n    // We can also use previous column if there's no next one (this is used when doing an auto-fit on the right-most stretch column)\n    if (column_1 == NULL)\n        column_1 = (column_0->PrevEnabledColumn != -1) ? &table->Columns[column_0->PrevEnabledColumn] : NULL;\n    if (column_1 == NULL)\n        return;\n\n    // Resizing from right-side of a Stretch column before a Fixed column forward sizing to left-side of fixed column.\n    // (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)\n    float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width);\n    column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width;\n    IM_ASSERT(column_0_width > 0.0f && column_1_width > 0.0f);\n    column_0->WidthRequest = column_0_width;\n    column_1->WidthRequest = column_1_width;\n    if ((column_0->Flags | column_1->Flags) & ImGuiTableColumnFlags_WidthStretch)\n        TableUpdateColumnsWeightFromWidth(table);\n    table->IsSettingsDirty = true;\n}\n\n// Disable clipping then auto-fit, will take 2 frames\n// (we don't take a shortcut for unclipped columns to reduce inconsistencies when e.g. resizing multiple columns)\nvoid ImGui::TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n)\n{\n    // Single auto width uses auto-fit\n    ImGuiTableColumn* column = &table->Columns[column_n];\n    if (!column->IsEnabled)\n        return;\n    column->CannotSkipItemsQueue = (1 << 0);\n    table->AutoFitSingleColumn = (ImGuiTableColumnIdx)column_n;\n}\n\nvoid ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table)\n{\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (!column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) // Cannot reset weight of hidden stretch column\n            continue;\n        column->CannotSkipItemsQueue = (1 << 0);\n        column->AutoFitQueue = (1 << 1);\n    }\n}\n\nvoid ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table)\n{\n    IM_ASSERT(table->LeftMostStretchedColumn != -1 && table->RightMostStretchedColumn != -1);\n\n    // Measure existing quantities\n    float visible_weight = 0.0f;\n    float visible_width = 0.0f;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))\n            continue;\n        IM_ASSERT(column->StretchWeight > 0.0f);\n        visible_weight += column->StretchWeight;\n        visible_width += column->WidthRequest;\n    }\n    IM_ASSERT(visible_weight > 0.0f && visible_width > 0.0f);\n\n    // Apply new weights\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))\n            continue;\n        column->StretchWeight = (column->WidthRequest / visible_width) * visible_weight;\n        IM_ASSERT(column->StretchWeight > 0.0f);\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Drawing\n//-------------------------------------------------------------------------\n// - TablePushBackgroundChannel() [Internal]\n// - TablePopBackgroundChannel() [Internal]\n// - TableSetupDrawChannels() [Internal]\n// - TableMergeDrawChannels() [Internal]\n// - TableGetColumnBorderCol() [Internal]\n// - TableDrawBorders() [Internal]\n//-------------------------------------------------------------------------\n\n\n// FIXME: This could be abstracted and merged with PushColumnsBackground(), by creating a generic struct\n// with storage for backup cliprect + backup channel + storage for splitter pointer, new clip rect.\n// This would slightly simplify caller code.\n\n// Bg2 is used by Selectable (and possibly other widgets) to render to the background.\n// Unlike our Bg0/1 channel which we uses for RowBg/CellBg/Borders and where we guarantee all shapes to be CPU-clipped, the Bg2 channel being widgets-facing will rely on regular ClipRect.\nvoid ImGui::TablePushBackgroundChannel()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiTable* table = g.CurrentTable;\n\n    // Optimization: avoid SetCurrentChannel() + PushClipRect()\n    table->HostBackupInnerClipRect = window->ClipRect;\n    SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd);\n    table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent);\n}\n\nvoid ImGui::TablePopBackgroundChannel()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiTable* table = g.CurrentTable;\n\n    // Optimization: avoid PopClipRect() + SetCurrentChannel()\n    SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);\n    table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[table->CurrentColumn].DrawChannelCurrent);\n}\n\n// Also see TableBeginCell()\nvoid ImGui::TablePushColumnChannel(int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n\n    // Optimization: avoid SetCurrentChannel() + PushClipRect()\n    if (table->Flags & ImGuiTableFlags_NoClip)\n        return;\n    ImGuiWindow* window = g.CurrentWindow;\n    const ImGuiTableColumn* column = &table->Columns[column_n];\n    SetWindowClipRectBeforeSetChannel(window, column->ClipRect);\n    table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);\n}\n\nvoid ImGui::TablePopColumnChannel()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n\n    // Optimization: avoid PopClipRect() + SetCurrentChannel()\n    if ((table->Flags & ImGuiTableFlags_NoClip) || (table->CurrentColumn == -1)) // Calling TreePop() after TableNextRow() is supported.\n        return;\n    ImGuiWindow* window = g.CurrentWindow;\n    const ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];\n    SetWindowClipRectBeforeSetChannel(window, column->ClipRect);\n    table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);\n}\n\n// Allocate draw channels. Called by TableUpdateLayout()\n// - We allocate them following storage order instead of display order so reordering columns won't needlessly\n//   increase overall dormant memory cost.\n// - We isolate headers draw commands in their own channels instead of just altering clip rects.\n//   This is in order to facilitate merging of draw commands.\n// - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels.\n// - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other\n//   channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged.\n// - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for\n//   horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4).\n// Draw channel allocation (before merging):\n// - NoClip                       --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call)\n// - Clip                         --> 2+D+N channels\n// - FreezeRows                   --> 2+D+N*2 (unless scrolling value is zero)\n// - FreezeRows || FreezeColumns  --> 3+D+N*2 (unless scrolling value is zero)\n// Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0.\nvoid ImGui::TableSetupDrawChannels(ImGuiTable* table)\n{\n    const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1;\n    const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount;\n    const int channels_for_bg = 1 + 1 * freeze_row_multiplier;\n    const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || (memcmp(table->VisibleMaskByIndex, table->EnabledMaskByIndex, ImBitArrayGetStorageSizeInBytes(table->ColumnsCount)) != 0)) ? +1 : 0;\n    const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy;\n    table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total);\n    table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1);\n    table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN;\n    table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN);\n\n    int draw_channel_current = 2;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (column->IsVisibleX && column->IsVisibleY)\n        {\n            column->DrawChannelFrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current);\n            column->DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current + (table->FreezeRowsCount > 0 ? channels_for_row + 1 : 0));\n            if (!(table->Flags & ImGuiTableFlags_NoClip))\n                draw_channel_current++;\n        }\n        else\n        {\n            column->DrawChannelFrozen = column->DrawChannelUnfrozen = table->DummyDrawChannel;\n        }\n        column->DrawChannelCurrent = column->DrawChannelFrozen;\n    }\n\n    // Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default.\n    // All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect.\n    // (This technically isn't part of setting up draw channels, but is reasonably related to be done here)\n    table->BgClipRect = table->InnerClipRect;\n    table->Bg0ClipRectForDrawCmd = table->OuterWindow->ClipRect;\n    table->Bg2ClipRectForDrawCmd = table->HostClipRect;\n    IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y);\n}\n\n// This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable().\n// For simplicity we call it TableMergeDrawChannels() but in fact it only reorder channels + overwrite ClipRect,\n// actual merging is done by table->DrawSplitter.Merge() which is called right after TableMergeDrawChannels().\n//\n// Columns where the contents didn't stray off their local clip rectangle can be merged. To achieve\n// this we merge their clip rect and make them contiguous in the channel list, so they can be merged\n// by the call to DrawSplitter.Merge() following to the call to this function.\n// We reorder draw commands by arranging them into a maximum of 4 distinct groups:\n//\n//   1 group:               2 groups:              2 groups:              4 groups:\n//   [ 0. ] no freeze       [ 0. ] row freeze      [ 01 ] col freeze      [ 01 ] row+col freeze\n//   [ .. ]  or no scroll   [ 2. ]  and v-scroll   [ .. ]  and h-scroll   [ 23 ]  and v+h-scroll\n//\n// Each column itself can use 1 channel (row freeze disabled) or 2 channels (row freeze enabled).\n// When the contents of a column didn't stray off its limit, we move its channels into the corresponding group\n// based on its position (within frozen rows/columns groups or not).\n// At the end of the operation our 1-4 groups will each have a ImDrawCmd using the same ClipRect.\n// This function assume that each column are pointing to a distinct draw channel,\n// otherwise merge_group->ChannelsCount will not match set bit count of merge_group->ChannelsMask.\n//\n// Column channels will not be merged into one of the 1-4 groups in the following cases:\n// - The contents stray off its clipping rectangle (we only compare the MaxX value, not the MinX value).\n//   Direct ImDrawList calls won't be taken into account by default, if you use them make sure the ImGui:: bounds\n//   matches, by e.g. calling SetCursorScreenPos().\n// - The channel uses more than one draw command itself. We drop all our attempt at merging stuff here..\n//   we could do better but it's going to be rare and probably not worth the hassle.\n// Columns for which the draw channel(s) haven't been merged with other will use their own ImDrawCmd.\n//\n// This function is particularly tricky to understand.. take a breath.\nvoid ImGui::TableMergeDrawChannels(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    ImDrawListSplitter* splitter = table->DrawSplitter;\n    const bool has_freeze_v = (table->FreezeRowsCount > 0);\n    const bool has_freeze_h = (table->FreezeColumnsCount > 0);\n    IM_ASSERT(splitter->_Current == 0);\n\n    // Track which groups we are going to attempt to merge, and which channels goes into each group.\n    struct MergeGroup\n    {\n        ImRect          ClipRect;\n        int             ChannelsCount = 0;\n        ImBitArrayPtr   ChannelsMask = NULL;\n    };\n    int merge_group_mask = 0x00;\n    MergeGroup merge_groups[4];\n\n    // Use a reusable temp buffer for the merge masks as they are dynamically sized.\n    const int max_draw_channels = (4 + table->ColumnsCount * 2);\n    const int size_for_masks_bitarrays_one = (int)ImBitArrayGetStorageSizeInBytes(max_draw_channels);\n    g.TempBuffer.reserve(size_for_masks_bitarrays_one * 5);\n    memset(g.TempBuffer.Data, 0, size_for_masks_bitarrays_one * 5);\n    for (int n = 0; n < IM_COUNTOF(merge_groups); n++)\n        merge_groups[n].ChannelsMask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * n));\n    ImBitArrayPtr remaining_mask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * 4));\n\n    // 1. Scan channels and take note of those which can be merged\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n))\n            continue;\n        ImGuiTableColumn* column = &table->Columns[column_n];\n\n        const int merge_group_sub_count = has_freeze_v ? 2 : 1;\n        for (int merge_group_sub_n = 0; merge_group_sub_n < merge_group_sub_count; merge_group_sub_n++)\n        {\n            const int channel_no = (merge_group_sub_n == 0) ? column->DrawChannelFrozen : column->DrawChannelUnfrozen;\n\n            // Don't attempt to merge if there are multiple draw calls within the column\n            ImDrawChannel* src_channel = &splitter->_Channels[channel_no];\n            if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd()\n                src_channel->_CmdBuffer.pop_back();\n            if (src_channel->_CmdBuffer.Size != 1)\n                continue;\n\n            // Find out the width of this merge group and check if it will fit in our column\n            // (note that we assume that rendering didn't stray on the left direction. we should need a CursorMinPos to detect it)\n            if (!(column->Flags & ImGuiTableColumnFlags_NoClip))\n            {\n                float content_max_x;\n                if (!has_freeze_v)\n                    content_max_x = ImMax(column->ContentMaxXUnfrozen, column->ContentMaxXHeadersUsed); // No row freeze\n                else if (merge_group_sub_n == 0)\n                    content_max_x = ImMax(column->ContentMaxXFrozen, column->ContentMaxXHeadersUsed);   // Row freeze: use width before freeze\n                else\n                    content_max_x = column->ContentMaxXUnfrozen;                                        // Row freeze: use width after freeze\n                if (content_max_x > column->ClipRect.Max.x)\n                    continue;\n            }\n\n            const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2);\n            IM_ASSERT(channel_no < max_draw_channels);\n            MergeGroup* merge_group = &merge_groups[merge_group_n];\n            if (merge_group->ChannelsCount == 0)\n                merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);\n            ImBitArraySetBit(merge_group->ChannelsMask, channel_no);\n            merge_group->ChannelsCount++;\n            merge_group->ClipRect.Add(src_channel->_CmdBuffer[0].ClipRect);\n            merge_group_mask |= (1 << merge_group_n);\n        }\n\n        // Invalidate current draw channel\n        // (we don't clear DrawChannelFrozen/DrawChannelUnfrozen solely to facilitate debugging/later inspection of data)\n        column->DrawChannelCurrent = (ImGuiTableDrawChannelIdx)-1;\n    }\n\n    // [DEBUG] Display merge groups\n#if 0\n    if (g.IO.KeyShift)\n        for (int merge_group_n = 0; merge_group_n < IM_COUNTOF(merge_groups); merge_group_n++)\n        {\n            MergeGroup* merge_group = &merge_groups[merge_group_n];\n            if (merge_group->ChannelsCount == 0)\n                continue;\n            char buf[32];\n            ImFormatString(buf, 32, \"MG%d:%d\", merge_group_n, merge_group->ChannelsCount);\n            ImVec2 text_pos = merge_group->ClipRect.Min + ImVec2(4, 4);\n            ImVec2 text_size = CalcTextSize(buf, NULL);\n            GetForegroundDrawList()->AddRectFilled(text_pos, text_pos + text_size, IM_COL32(0, 0, 0, 255));\n            GetForegroundDrawList()->AddText(text_pos, IM_COL32(255, 255, 0, 255), buf, NULL);\n            GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 255, 0, 255));\n        }\n#endif\n\n    // 2. Rewrite channel list in our preferred order\n    if (merge_group_mask != 0)\n    {\n        // We skip channel 0 (Bg0/Bg1) and 1 (Bg2 frozen) from the shuffling since they won't move - see channels allocation in TableSetupDrawChannels().\n        const int LEADING_DRAW_CHANNELS = 2;\n        g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized\n        ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data;\n        ImBitArraySetBitRange(remaining_mask, LEADING_DRAW_CHANNELS, splitter->_Count);\n        ImBitArrayClearBit(remaining_mask, table->Bg2DrawChannelUnfrozen);\n        IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN);\n        int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS);\n        //ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect;\n        ImRect host_rect = table->HostClipRect;\n        for (int merge_group_n = 0; merge_group_n < IM_COUNTOF(merge_groups); merge_group_n++)\n        {\n            if (int merge_channels_count = merge_groups[merge_group_n].ChannelsCount)\n            {\n                MergeGroup* merge_group = &merge_groups[merge_group_n];\n                ImRect merge_clip_rect = merge_group->ClipRect;\n\n                // Extend outer-most clip limits to match those of host, so draw calls can be merged even if\n                // outer-most columns have some outer padding offsetting them from their parent ClipRect.\n                // The principal cases this is dealing with are:\n                // - On a same-window table (not scrolling = single group), all fitting columns ClipRect -> will extend and match host ClipRect -> will merge\n                // - Columns can use padding and have left-most ClipRect.Min.x and right-most ClipRect.Max.x != from host ClipRect -> will extend and match host ClipRect -> will merge\n                // FIXME-TABLE FIXME-WORKRECT: We are wasting a merge opportunity on tables without scrolling if column doesn't fit\n                // within host clip rect, solely because of the half-padding difference between window->WorkRect and window->InnerClipRect.\n                if ((merge_group_n & 1) == 0 || !has_freeze_h)\n                    merge_clip_rect.Min.x = ImMin(merge_clip_rect.Min.x, host_rect.Min.x);\n                if ((merge_group_n & 2) == 0 || !has_freeze_v)\n                    merge_clip_rect.Min.y = ImMin(merge_clip_rect.Min.y, host_rect.Min.y);\n                if ((merge_group_n & 1) != 0)\n                    merge_clip_rect.Max.x = ImMax(merge_clip_rect.Max.x, host_rect.Max.x);\n                if ((merge_group_n & 2) != 0 && (table->Flags & ImGuiTableFlags_NoHostExtendY) == 0)\n                    merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, host_rect.Max.y);\n                //GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, 0, 1.0f); // [DEBUG]\n                //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200));\n                //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200));\n                remaining_count -= merge_group->ChannelsCount;\n                for (int n = 0; n < (size_for_masks_bitarrays_one >> 2); n++)\n                    remaining_mask[n] &= ~merge_group->ChannelsMask[n];\n                for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++)\n                {\n                    // Copy + overwrite new clip rect\n                    if (!IM_BITARRAY_TESTBIT(merge_group->ChannelsMask, n))\n                        continue;\n                    IM_BITARRAY_CLEARBIT(merge_group->ChannelsMask, n);\n                    merge_channels_count--;\n\n                    ImDrawChannel* channel = &splitter->_Channels[n];\n                    IM_ASSERT(channel->_CmdBuffer.Size == 1 && merge_clip_rect.Contains(ImRect(channel->_CmdBuffer[0].ClipRect)));\n                    channel->_CmdBuffer[0].ClipRect = merge_clip_rect.ToVec4();\n                    memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));\n                }\n            }\n\n            // Make sure Bg2DrawChannelUnfrozen appears in the middle of our groups (whereas Bg0/Bg1 and Bg2 frozen are fixed to 0 and 1)\n            if (merge_group_n == 1 && has_freeze_v)\n                memcpy(dst_tmp++, &splitter->_Channels[table->Bg2DrawChannelUnfrozen], sizeof(ImDrawChannel));\n        }\n\n        // Append unmergeable channels that we didn't reorder at the end of the list\n        for (int n = 0; n < splitter->_Count && remaining_count != 0; n++)\n        {\n            if (!IM_BITARRAY_TESTBIT(remaining_mask, n))\n                continue;\n            ImDrawChannel* channel = &splitter->_Channels[n];\n            memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));\n            remaining_count--;\n        }\n        IM_ASSERT(dst_tmp == g.DrawChannelsTempMergeBuffer.Data + g.DrawChannelsTempMergeBuffer.Size);\n        memcpy(splitter->_Channels.Data + LEADING_DRAW_CHANNELS, g.DrawChannelsTempMergeBuffer.Data, (splitter->_Count - LEADING_DRAW_CHANNELS) * sizeof(ImDrawChannel));\n    }\n}\n\nstatic ImU32 TableGetColumnBorderCol(ImGuiTable* table, int order_n, int column_n)\n{\n    const bool is_hovered = (table->HoveredColumnBorder == column_n);\n    const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent);\n    const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1);\n    if (is_resized || is_hovered)\n        return ImGui::GetColorU32(is_resized ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);\n    if (is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)))\n        return table->BorderColorStrong;\n    return table->BorderColorLight;\n}\n\n// FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow)\nvoid ImGui::TableDrawBorders(ImGuiTable* table)\n{\n    ImGuiWindow* inner_window = table->InnerWindow;\n    if (!table->OuterWindow->ClipRect.Overlaps(table->OuterRect))\n        return;\n\n    ImDrawList* inner_drawlist = inner_window->DrawList;\n    table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0);\n    inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false);\n\n    // Draw inner border and resizing feedback\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    const float border_size = TABLE_BORDER_SIZE;\n    const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f);\n    const float draw_y2_body = table->InnerRect.Max.y;\n    const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1;\n    if (table->Flags & ImGuiTableFlags_BordersInnerV)\n    {\n        for (int order_n = 0; order_n < table->ColumnsCount; order_n++)\n        {\n            if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))\n                continue;\n\n            const int column_n = table->DisplayOrderToIndex[order_n];\n            ImGuiTableColumn* column = &table->Columns[column_n];\n            const bool is_hovered = (table->HoveredColumnBorder == column_n);\n            const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent);\n            const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0;\n            const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1);\n            if (column->MaxX > table->InnerClipRect.Max.x && !is_resized)\n                continue;\n\n            // Decide whether right-most column is visible\n            if (column->NextEnabledColumn == -1 && !is_resizable)\n                if ((table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame || (table->Flags & ImGuiTableFlags_NoHostExtendX))\n                    continue;\n            if (column->MaxX <= column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size..\n                continue;\n\n            // Draw in outer window so right-most column won't be clipped\n            float draw_y2 = draw_y2_head;\n            if (is_frozen_separator)\n                draw_y2 = draw_y2_body;\n            else if ((table->Flags & ImGuiTableFlags_NoBordersInBodyUntilResize) != 0 && (is_hovered || is_resized))\n                draw_y2 = draw_y2_body;\n            else if ((table->Flags & (ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_NoBordersInBody)) == 0)\n                draw_y2 = draw_y2_body;\n            if (draw_y2 > draw_y1)\n                inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size);\n        }\n    }\n\n    // Draw outer border\n    // FIXME: could use AddRect or explicit VLine/HLine helper?\n    if (table->Flags & ImGuiTableFlags_BordersOuter)\n    {\n        // Display outer border offset by 1 which is a simple way to display it without adding an extra draw call\n        // (Without the offset, in outer_window it would be rendered behind cells, because child windows are above their\n        // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part\n        // of it in inner window, and the part that's over scrollbars in the outer window..)\n        // Either solution currently won't allow us to use a larger border size: the border would clipped.\n        const ImRect outer_border = table->OuterRect;\n        const ImU32 outer_col = table->BorderColorStrong;\n        if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)\n        {\n            inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size);\n        }\n        else if (table->Flags & ImGuiTableFlags_BordersOuterV)\n        {\n            inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size);\n            inner_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size);\n        }\n        else if (table->Flags & ImGuiTableFlags_BordersOuterH)\n        {\n            inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size);\n            inner_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size);\n        }\n    }\n    if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y)\n    {\n        // Draw bottom-most row border between it is above outer border.\n        const float border_y = table->RowPosY2;\n        if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y)\n            inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size);\n    }\n\n    inner_drawlist->PopClipRect();\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Sorting\n//-------------------------------------------------------------------------\n// - TableGetSortSpecs()\n// - TableFixColumnSortDirection() [Internal]\n// - TableGetColumnNextSortDirection() [Internal]\n// - TableSetColumnSortDirection() [Internal]\n// - TableSortSpecsSanitize() [Internal]\n// - TableSortSpecsBuild() [Internal]\n//-------------------------------------------------------------------------\n\n// Return NULL if no sort specs (most often when ImGuiTableFlags_Sortable is not set)\n// When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have\n// changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting,\n// else you may wastefully sort your data every frame!\n// Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable()!\nImGuiTableSortSpecs* ImGui::TableGetSortSpecs()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (table == NULL || !(table->Flags & ImGuiTableFlags_Sortable))\n        return NULL;\n\n    // Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths.\n    if (!table->IsLayoutLocked)\n        TableUpdateLayout(table);\n\n    TableSortSpecsBuild(table);\n    return &table->SortSpecs;\n}\n\nstatic inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n)\n{\n    IM_ASSERT(n < column->SortDirectionsAvailCount);\n    return (ImGuiSortDirection)((column->SortDirectionsAvailList >> (n << 1)) & 0x03);\n}\n\n// Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending)\nvoid ImGui::TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column)\n{\n    if (column->SortOrder == -1 || (column->SortDirectionsAvailMask & (1 << column->SortDirection)) != 0)\n        return;\n    column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0);\n    table->IsSortSpecsDirty = true;\n}\n\n// Calculate next sort direction that would be set after clicking the column\n// - If the PreferSortDescending flag is set, we will default to a Descending direction on the first click.\n// - Note that the PreferSortAscending flag is never checked, it is essentially the default and therefore a no-op.\nIM_STATIC_ASSERT(ImGuiSortDirection_None == 0 && ImGuiSortDirection_Ascending == 1 && ImGuiSortDirection_Descending == 2);\nImGuiSortDirection ImGui::TableGetColumnNextSortDirection(ImGuiTableColumn* column)\n{\n    IM_ASSERT(column->SortDirectionsAvailCount > 0);\n    if (column->SortOrder == -1)\n        return TableGetColumnAvailSortDirection(column, 0);\n    for (int n = 0; n < 3; n++)\n        if (column->SortDirection == TableGetColumnAvailSortDirection(column, n))\n            return TableGetColumnAvailSortDirection(column, (n + 1) % column->SortDirectionsAvailCount);\n    IM_ASSERT(0);\n    return ImGuiSortDirection_None;\n}\n\n// Note that the NoSortAscending/NoSortDescending flags are processed in TableSortSpecsSanitize(), and they may change/revert\n// the value of SortDirection. We could technically also do it here but it would be unnecessary and duplicate code.\nvoid ImGui::TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n\n    if (!(table->Flags & ImGuiTableFlags_SortMulti))\n        append_to_sort_specs = false;\n    if (!(table->Flags & ImGuiTableFlags_SortTristate))\n        IM_ASSERT(sort_direction != ImGuiSortDirection_None);\n\n    ImGuiTableColumnIdx sort_order_max = 0;\n    if (append_to_sort_specs)\n        for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)\n            sort_order_max = ImMax(sort_order_max, table->Columns[other_column_n].SortOrder);\n\n    ImGuiTableColumn* column = &table->Columns[column_n];\n    column->SortDirection = (ImU8)sort_direction;\n    if (column->SortDirection == ImGuiSortDirection_None)\n        column->SortOrder = -1;\n    else if (column->SortOrder == -1 || !append_to_sort_specs)\n        column->SortOrder = append_to_sort_specs ? sort_order_max + 1 : 0;\n\n    for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)\n    {\n        ImGuiTableColumn* other_column = &table->Columns[other_column_n];\n        if (other_column != column && !append_to_sort_specs)\n            other_column->SortOrder = -1;\n        TableFixColumnSortDirection(table, other_column);\n    }\n    table->IsSettingsDirty = true;\n    table->IsSortSpecsDirty = true;\n}\n\nvoid ImGui::TableSortSpecsSanitize(ImGuiTable* table)\n{\n    IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable);\n\n    // Clear SortOrder from hidden column and verify that there's no gap or duplicate.\n    int sort_order_count = 0;\n    ImU64 sort_order_mask = 0x00;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (column->SortOrder != -1 && !column->IsEnabled)\n            column->SortOrder = -1;\n        if (column->SortOrder == -1)\n            continue;\n        sort_order_count++;\n        sort_order_mask |= ((ImU64)1 << column->SortOrder);\n        IM_ASSERT(sort_order_count < (int)sizeof(sort_order_mask) * 8);\n    }\n\n    const bool need_fix_linearize = ((ImU64)1 << sort_order_count) != (sort_order_mask + 1);\n    const bool need_fix_single_sort_order = (sort_order_count > 1) && !(table->Flags & ImGuiTableFlags_SortMulti);\n    if (need_fix_linearize || need_fix_single_sort_order)\n    {\n        ImU64 fixed_mask = 0x00;\n        for (int sort_n = 0; sort_n < sort_order_count; sort_n++)\n        {\n            // Fix: Rewrite sort order fields if needed so they have no gap or duplicate.\n            // (e.g. SortOrder 0 disappeared, SortOrder 1..2 exists --> rewrite then as SortOrder 0..1)\n            int column_with_smallest_sort_order = -1;\n            for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n                if ((fixed_mask & ((ImU64)1 << (ImU64)column_n)) == 0 && table->Columns[column_n].SortOrder != -1)\n                    if (column_with_smallest_sort_order == -1 || table->Columns[column_n].SortOrder < table->Columns[column_with_smallest_sort_order].SortOrder)\n                        column_with_smallest_sort_order = column_n;\n            IM_ASSERT(column_with_smallest_sort_order != -1);\n            fixed_mask |= ((ImU64)1 << column_with_smallest_sort_order);\n            table->Columns[column_with_smallest_sort_order].SortOrder = (ImGuiTableColumnIdx)sort_n;\n\n            // Fix: Make sure only one column has a SortOrder if ImGuiTableFlags_MultiSortable is not set.\n            if (need_fix_single_sort_order)\n            {\n                sort_order_count = 1;\n                for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n                    if (column_n != column_with_smallest_sort_order)\n                        table->Columns[column_n].SortOrder = -1;\n                break;\n            }\n        }\n    }\n\n    // Fallback default sort order (if no column with the ImGuiTableColumnFlags_DefaultSort flag)\n    if (sort_order_count == 0 && !(table->Flags & ImGuiTableFlags_SortTristate))\n        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        {\n            ImGuiTableColumn* column = &table->Columns[column_n];\n            if (column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_NoSort))\n            {\n                sort_order_count = 1;\n                column->SortOrder = 0;\n                column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0);\n                break;\n            }\n        }\n\n    table->SortSpecsCount = (ImGuiTableColumnIdx)sort_order_count;\n}\n\nvoid ImGui::TableSortSpecsBuild(ImGuiTable* table)\n{\n    bool dirty = table->IsSortSpecsDirty;\n    if (dirty)\n    {\n        TableSortSpecsSanitize(table);\n        table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount);\n        table->SortSpecs.SpecsDirty = true; // Mark as dirty for user\n        table->IsSortSpecsDirty = false; // Mark as not dirty for us\n    }\n\n    // Write output\n    // May be able to move all SortSpecs data from table (48 bytes) to ImGuiTableTempData if we decide to write it back on every BeginTable()\n    ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data;\n    if (dirty && sort_specs != NULL)\n        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        {\n            ImGuiTableColumn* column = &table->Columns[column_n];\n            if (column->SortOrder == -1)\n                continue;\n            IM_ASSERT(column->SortOrder < table->SortSpecsCount);\n            ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder];\n            sort_spec->ColumnUserID = column->UserID;\n            sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n;\n            sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;\n            sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection;\n        }\n\n    table->SortSpecs.Specs = sort_specs;\n    table->SortSpecs.SpecsCount = table->SortSpecsCount;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Headers\n//-------------------------------------------------------------------------\n// - TableGetHeaderRowHeight() [Internal]\n// - TableGetHeaderAngledMaxLabelWidth() [Internal]\n// - TableHeadersRow()\n// - TableHeader()\n// - TableAngledHeadersRow()\n// - TableAngledHeadersRowEx() [Internal]\n//-------------------------------------------------------------------------\n\nfloat ImGui::TableGetHeaderRowHeight()\n{\n    // Caring for a minor edge case:\n    // Calculate row height, for the unlikely case that some labels may be taller than others.\n    // If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height.\n    // In your custom header row you may omit this all together and just call TableNextRow() without a height...\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    float row_height = g.FontSize;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))\n            if ((table->Columns[column_n].Flags & ImGuiTableColumnFlags_NoHeaderLabel) == 0)\n                row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(table, column_n)).y);\n    return row_height + g.Style.CellPadding.y * 2.0f;\n}\n\nfloat ImGui::TableGetHeaderAngledMaxLabelWidth()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    float width = 0.0f;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))\n            if (table->Columns[column_n].Flags & ImGuiTableColumnFlags_AngledHeader)\n                width = ImMax(width, CalcTextSize(TableGetColumnName(table, column_n), NULL, true).x);\n    return width + g.Style.CellPadding.y * 2.0f; // Swap padding\n}\n\n// [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn().\n// The intent is that advanced users willing to create customized headers would not need to use this helper\n// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.\n// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.\n// This code is intentionally written to not make much use of internal functions, to give you better direction\n// if you need to write your own.\n// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.\nvoid ImGui::TableHeadersRow()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT_USER_ERROR_RET(table != NULL, \"Call should only be done while in BeginTable() scope!\");\n\n    // Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make\n    // it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this.\n    if (!table->IsLayoutLocked)\n        TableUpdateLayout(table);\n\n    // Open row\n    const float row_height = TableGetHeaderRowHeight();\n    TableNextRow(ImGuiTableRowFlags_Headers, row_height);\n    const float row_y1 = GetCursorScreenPos().y;\n    if (table->HostSkipItems) // Merely an optimization, you may skip in your own code.\n        return;\n\n    const int columns_count = TableGetColumnCount();\n    for (int column_n = 0; column_n < columns_count; column_n++)\n    {\n        if (!TableSetColumnIndex(column_n))\n            continue;\n\n        // Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation)\n        const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? \"\" : TableGetColumnName(column_n);\n        PushID(column_n);\n        TableHeader(name);\n        PopID();\n    }\n\n    // Allow opening popup from the right-most section after the last column.\n    ImVec2 mouse_pos = ImGui::GetMousePos();\n    if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count)\n        if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height)\n            TableOpenContextMenu(columns_count); // Will open a non-column-specific popup.\n}\n\n// Emit a column header (text + optional sort order)\n// We cpu-clip text here so that all columns headers can be merged into a same draw call.\n// Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader()\nvoid ImGui::TableHeader(const char* label)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT_USER_ERROR_RET(table != NULL, \"Call should only be done while in BeginTable() scope!\");\n    IM_ASSERT(table->CurrentColumn != -1);\n    const int column_n = table->CurrentColumn;\n    ImGuiTableColumn* column = &table->Columns[column_n];\n\n    // Label\n    if (label == NULL)\n        label = \"\";\n    const char* label_end = FindRenderedTextEnd(label);\n    ImVec2 label_size = CalcTextSize(label, label_end, true);\n    ImVec2 label_pos = window->DC.CursorPos;\n\n    // If we already got a row height, there's use that.\n    // FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect?\n    ImRect cell_r = TableGetCellBgRect(table, column_n);\n    float label_height = ImMax(label_size.y, table->RowMinHeight - table->RowCellPaddingY * 2.0f);\n\n    // Calculate ideal size for sort order arrow\n    float w_arrow = 0.0f;\n    float w_sort_text = 0.0f;\n    bool sort_arrow = false;\n    char sort_order_suf[4] = \"\";\n    const float ARROW_SCALE = 0.65f;\n    if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))\n    {\n        w_arrow = ImTrunc(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);\n        if (column->SortOrder != -1)\n            sort_arrow = true;\n        if (column->SortOrder > 0)\n        {\n            ImFormatString(sort_order_suf, IM_COUNTOF(sort_order_suf), \"%d\", column->SortOrder + 1);\n            w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x;\n        }\n    }\n\n    // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considered for merging.\n    float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow;\n    column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, sort_arrow ? cell_r.Max.x : ImMin(max_pos_x, cell_r.Max.x));\n    column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);\n\n    // Keep header highlighted when context menu is open.\n    ImGuiID id = window->GetID(label);\n    ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f));\n    ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal\n    if (!ItemAdd(bb, id))\n        return;\n\n    //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]\n    //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]\n\n    // Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items.\n    const bool highlight = (table->HighlightColumnHeader == column_n);\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap);\n    if (held || hovered || highlight)\n    {\n        const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);\n        //RenderFrame(bb.Min, bb.Max, col, false, 0.0f);\n        TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn);\n    }\n    else\n    {\n        // Submit single cell bg color in the case we didn't submit a full header row\n        if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)\n            TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);\n    }\n    RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding);\n    if (held)\n        table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;\n    window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;\n\n    // Drag and drop to re-order columns.\n    // FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone.\n    if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive)\n    {\n        // While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x\n        table->ReorderColumn = (ImGuiTableColumnIdx)column_n;\n        table->InstanceInteracted = table->InstanceCurrent;\n\n        // We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder.\n        if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x)\n            if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL)\n                if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder))\n                    if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))\n                        table->ReorderColumnDir = -1;\n        if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x)\n            if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL)\n                if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder))\n                    if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))\n                        table->ReorderColumnDir = +1;\n    }\n\n    // Sort order arrow\n    const float ellipsis_max = ImMax(cell_r.Max.x - w_arrow - w_sort_text, label_pos.x);\n    if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))\n    {\n        if (column->SortOrder != -1)\n        {\n            float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text);\n            float y = label_pos.y;\n            if (column->SortOrder > 0)\n            {\n                PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text, 0.70f));\n                RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf);\n                PopStyleColor();\n                x += w_sort_text;\n            }\n            RenderArrow(window->DrawList, ImVec2(x, y), GetColorU32(ImGuiCol_Text), column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE);\n        }\n\n        // Handle clicking on column header to adjust Sort Order\n        if (pressed && table->ReorderColumn != column_n)\n        {\n            ImGuiSortDirection sort_direction = TableGetColumnNextSortDirection(column);\n            TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift);\n        }\n    }\n\n    // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will\n    // be merged into a single draw call.\n    //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);\n    RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size);\n\n    const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);\n    if (text_clipped && hovered && g.ActiveId == 0)\n        SetItemTooltip(\"%.*s\", (int)(label_end - label), label);\n\n    // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden\n    if (IsMouseReleased(1) && IsItemHovered())\n        TableOpenContextMenu(column_n);\n}\n\n// Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets.\n// FIXME: No hit-testing/button on the angled header.\nvoid ImGui::TableAngledHeadersRow()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    ImGuiTableTempData* temp_data = table->TempData;\n    temp_data->AngledHeadersRequests.resize(0);\n    temp_data->AngledHeadersRequests.reserve(table->ColumnsEnabledCount);\n\n    // Which column needs highlight?\n    const ImGuiID row_id = GetID(\"##AngledHeaders\");\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    int highlight_column_n = table->HighlightColumnHeader;\n    if (highlight_column_n == -1 && table->HoveredColumnBody != -1)\n        if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive)))\n            highlight_column_n = table->HoveredColumnBody;\n\n    // Build up request\n    ImU32 col_header_bg = GetColorU32(ImGuiCol_TableHeaderBg);\n    ImU32 col_text = GetColorU32(ImGuiCol_Text);\n    for (int order_n = 0; order_n < table->ColumnsCount; order_n++)\n        if (IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))\n        {\n            const int column_n = table->DisplayOrderToIndex[order_n];\n            ImGuiTableColumn* column = &table->Columns[column_n];\n            if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here.\n                continue;\n            ImGuiTableHeaderData request = { (ImGuiTableColumnIdx)column_n, col_text, col_header_bg, (column_n == highlight_column_n) ? GetColorU32(ImGuiCol_Header) : 0 };\n            temp_data->AngledHeadersRequests.push_back(request);\n        }\n\n    // Render row\n    TableAngledHeadersRowEx(row_id, g.Style.TableAngledHeadersAngle, 0.0f, temp_data->AngledHeadersRequests.Data, temp_data->AngledHeadersRequests.Size);\n}\n\n// Important: data must be fed left to right\nvoid ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImDrawList* draw_list = window->DrawList;\n    IM_ASSERT_USER_ERROR_RET(table != NULL, \"Call should only be done while in BeginTable() scope!\");\n    IM_ASSERT(table->CurrentRow == -1 && \"Must be first row\");\n\n    if (max_label_width == 0.0f)\n        max_label_width = TableGetHeaderAngledMaxLabelWidth();\n\n    // Angle argument expressed in (-IM_PI/2 .. +IM_PI/2) as it is easier to think about for user.\n    const bool flip_label = (angle < 0.0f);\n    angle -= IM_PI * 0.5f;\n    const float cos_a = ImCos(angle);\n    const float sin_a = ImSin(angle);\n    const float label_cos_a = flip_label ? ImCos(angle + IM_PI) : cos_a;\n    const float label_sin_a = flip_label ? ImSin(angle + IM_PI) : sin_a;\n    const ImVec2 unit_right = ImVec2(cos_a, sin_a);\n\n    // Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow()\n    // FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other.\n    const float header_height = g.FontSize + g.Style.CellPadding.x * 2.0f;\n    const float row_height = ImTrunc(ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y));\n    table->AngledHeadersHeight = row_height;\n    table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f;\n    const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); // vector from bottom-left to top-left, and from bottom-right to top-right\n\n    // Declare row, override and draw our own background\n    TableNextRow(ImGuiTableRowFlags_Headers, row_height);\n    TableNextColumn();\n    const ImRect row_r(table->WorkRect.Min.x, table->BgClipRect.Min.y, table->WorkRect.Max.x, table->RowPosY2);\n    table->DrawSplitter->SetCurrentChannel(draw_list, TABLE_DRAW_CHANNEL_BG0);\n    float clip_rect_min_x = table->BgClipRect.Min.x;\n    if (table->FreezeColumnsCount > 0)\n        clip_rect_min_x = ImMax(clip_rect_min_x, table->Columns[table->FreezeColumnsCount - 1].MaxX);\n    TableSetBgColor(ImGuiTableBgTarget_RowBg0, 0); // Cancel\n    PushClipRect(table->BgClipRect.Min, table->BgClipRect.Max, false); // Span all columns\n    draw_list->AddRectFilled(ImVec2(table->BgClipRect.Min.x, row_r.Min.y), ImVec2(table->BgClipRect.Max.x, row_r.Max.y), GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color.\n    PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns\n\n    ButtonBehavior(row_r, row_id, NULL, NULL);\n    KeepAliveID(row_id);\n\n    const float ascent_scaled = g.FontBaked->Ascent * g.FontBakedScale; // FIXME: Standardize those scaling factors better\n    const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);\n    const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component\n    const ImVec2 align = g.Style.TableAngledHeadersTextAlign;\n\n    // Draw background and labels in first pass, then all borders.\n    float max_x = -FLT_MAX;\n    for (int pass = 0; pass < 2; pass++)\n        for (int order_n = 0; order_n < data_count; order_n++)\n        {\n            const ImGuiTableHeaderData* request = &data[order_n];\n            const int column_n = request->Index;\n            ImGuiTableColumn* column = &table->Columns[column_n];\n\n            ImVec2 bg_shape[4];\n            bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y);\n            bg_shape[1] = ImVec2(column->MinX, row_r.Max.y);\n            bg_shape[2] = bg_shape[1] + header_angled_vector;\n            bg_shape[3] = bg_shape[0] + header_angled_vector;\n            if (pass == 0)\n            {\n                // Draw shape\n                draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor0);\n                draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor1); // Optional highlight\n                max_x = ImMax(max_x, bg_shape[3].x);\n\n                // Draw label\n                // - First draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset.\n                // - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated.\n                const char* label_name = TableGetColumnName(table, column_n);\n                const char* label_name_end = FindRenderedTextEnd(label_name);\n                const float line_off_step_x = (g.FontSize / -sin_a);\n                const int label_lines = ImTextCountLines(label_name, label_name_end);\n\n                // Left<>Right alignment\n                float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f;\n                float line_off_for_align_x = ImFloor(ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x);\n                line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x;\n\n                // Register header width\n                column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x);\n\n                while (label_name < label_name_end)\n                {\n                    const char* label_name_eol = strchr(label_name, '\\n');\n                    if (label_name_eol == NULL)\n                        label_name_eol = label_name_end;\n\n                    // FIXME: Individual line clipping for right-most column is broken for negative angles.\n                    ImVec2 label_size = CalcTextSize(label_name, label_name_eol);\n                    float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symmetrical but hide more text.\n                    float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x);\n                    ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));\n                    int vtx_idx_begin = draw_list->_VtxCurrentIdx;\n                    PushStyleColor(ImGuiCol_Text, request->TextColor);\n                    RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);\n                    PopStyleColor();\n                    int vtx_idx_end = draw_list->_VtxCurrentIdx;\n\n                    // Up<>Down alignment\n                    const float available_space = ImMax(clip_width - label_size.x + ImAbs(padding.x * cos_a) * 2.0f - ImAbs(padding.y * sin_a) * 2.0f, 0.0f);\n                    const float vertical_offset = available_space * align.y * (flip_label ? -1.0f : 1.0f);\n\n                    // Rotate and offset label\n                    ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x - vertical_offset, window->ClipRect.Min.y + label_size.y);\n                    ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y);\n                    line_off_curr_x += flip_label ? -line_off_step_x : line_off_step_x;\n                    pivot_out += unit_right * padding.y;\n                    if (flip_label)\n                        pivot_out += unit_right * (clip_width - ImMax(0.0f, clip_width - label_size.x));\n                    pivot_out.x += flip_label ? line_off_curr_x + line_off_step_x : line_off_curr_x;\n                    ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset\n                    //if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 1.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); }\n\n                    label_name = label_name_eol + 1;\n                }\n            }\n            if (pass == 1)\n            {\n                // Draw border\n                draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n));\n            }\n        }\n    PopClipRect();\n    PopClipRect();\n    table->TempData->AngledHeadersExtraWidth = ImMax(0.0f, max_x - table->Columns[table->RightMostEnabledColumn].MaxX);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Context Menu\n//-------------------------------------------------------------------------\n// - TableOpenContextMenu() [Internal]\n// - TableBeginContextMenuPopup() [Internal]\n// - TableDrawDefaultContextMenu() [Internal]\n//-------------------------------------------------------------------------\n\n// Use -1 to open menu not specific to a given column.\nvoid ImGui::TableOpenContextMenu(int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (column_n == -1 && table->CurrentColumn != -1)   // When called within a column automatically use this one (for consistency)\n        column_n = table->CurrentColumn;\n    if (column_n == table->ColumnsCount)                // To facilitate using with TableGetHoveredColumn()\n        column_n = -1;\n    IM_ASSERT(column_n >= -1 && column_n < table->ColumnsCount);\n    if (table->Flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))\n    {\n        table->IsContextPopupOpen = true;\n        table->ContextPopupColumn = (ImGuiTableColumnIdx)column_n;\n        table->InstanceInteracted = table->InstanceCurrent;\n        const ImGuiID context_menu_id = ImHashStr(\"##ContextMenu\", 0, table->ID);\n        OpenPopupEx(context_menu_id, ImGuiPopupFlags_None);\n    }\n}\n\nbool ImGui::TableBeginContextMenuPopup(ImGuiTable* table)\n{\n    if (!table->IsContextPopupOpen || table->InstanceCurrent != table->InstanceInteracted)\n        return false;\n    const ImGuiID context_menu_id = ImHashStr(\"##ContextMenu\", 0, table->ID);\n    if (BeginPopupEx(context_menu_id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings))\n        return true;\n    table->IsContextPopupOpen = false;\n    return false;\n}\n\n// Output context menu into current window (generally a popup)\n// FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data?\n// Sections to display are pulled from 'flags_for_section_to_display', which is typically == table->Flags.\n// - ImGuiTableFlags_Resizable   -> display Sizing menu items\n// - ImGuiTableFlags_Reorderable -> display \"Reset Order\"\n////- ImGuiTableFlags_Sortable   -> display sorting options (disabled)\n// - ImGuiTableFlags_Hideable    -> display columns visibility menu items\n// It means if you have a custom context menus you can call this section and omit some sections, and add your own.\nvoid ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    bool want_separator = false;\n    const int column_n = (table->ContextPopupColumn >= 0 && table->ContextPopupColumn < table->ColumnsCount) ? table->ContextPopupColumn : -1;\n    ImGuiTableColumn* column = (column_n != -1) ? &table->Columns[column_n] : NULL;\n\n    // Sizing\n    if (flags_for_section_to_display & ImGuiTableFlags_Resizable)\n    {\n        if (column != NULL)\n        {\n            const bool can_resize = !(column->Flags & ImGuiTableColumnFlags_NoResize) && column->IsEnabled;\n            if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableSizeOne), NULL, false, can_resize)) // \"###SizeOne\"\n                TableSetColumnWidthAutoSingle(table, column_n);\n        }\n\n        const char* size_all_desc;\n        if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount && (table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame)\n            size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllFit);        // \"###SizeAll\" All fixed\n        else\n            size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllDefault);    // \"###SizeAll\" All stretch or mixed\n        if (MenuItem(size_all_desc, NULL))\n            TableSetColumnWidthAutoAll(table);\n        want_separator = true;\n    }\n\n    // Ordering\n    if (flags_for_section_to_display & ImGuiTableFlags_Reorderable)\n    {\n        if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder))\n            table->IsResetDisplayOrderRequest = true;\n        want_separator = true;\n    }\n\n    // Reset all (should work but seems unnecessary/noisy to expose?)\n    //if (MenuItem(\"Reset all\"))\n    //    table->IsResetAllRequest = true;\n\n    // Sorting\n    // (modify TableOpenContextMenu() to add _Sortable flag if enabling this)\n#if 0\n    if ((flags_for_section_to_display & ImGuiTableFlags_Sortable) && column != NULL && (column->Flags & ImGuiTableColumnFlags_NoSort) == 0)\n    {\n        if (want_separator)\n            Separator();\n        want_separator = true;\n\n        bool append_to_sort_specs = g.IO.KeyShift;\n        if (MenuItem(\"Sort in Ascending Order\", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Ascending, (column->Flags & ImGuiTableColumnFlags_NoSortAscending) == 0))\n            TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Ascending, append_to_sort_specs);\n        if (MenuItem(\"Sort in Descending Order\", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Descending, (column->Flags & ImGuiTableColumnFlags_NoSortDescending) == 0))\n            TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Descending, append_to_sort_specs);\n    }\n#endif\n\n    // Hiding / Visibility\n    if (flags_for_section_to_display & ImGuiTableFlags_Hideable)\n    {\n        if (want_separator)\n            Separator();\n        want_separator = true;\n\n        PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);\n        for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)\n        {\n            ImGuiTableColumn* other_column = &table->Columns[other_column_n];\n            if (other_column->Flags & ImGuiTableColumnFlags_Disabled)\n                continue;\n\n            const char* name = TableGetColumnName(table, other_column_n);\n            if (name == NULL || name[0] == 0)\n                name = \"<Unknown>\";\n\n            // Make sure we can't hide the last active column\n            bool menu_item_active = (other_column->Flags & ImGuiTableColumnFlags_NoHide) ? false : true;\n            if (other_column->IsUserEnabled && table->ColumnsEnabledCount <= 1)\n                menu_item_active = false;\n            if (MenuItem(name, NULL, other_column->IsUserEnabled, menu_item_active))\n                other_column->IsUserEnabledNextFrame = !other_column->IsUserEnabled;\n        }\n        PopItemFlag();\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Settings (.ini data)\n//-------------------------------------------------------------------------\n// FIXME: The binding/finding/creating flow are too confusing.\n//-------------------------------------------------------------------------\n// - TableSettingsInit() [Internal]\n// - TableSettingsCalcChunkSize() [Internal]\n// - TableSettingsCreate() [Internal]\n// - TableSettingsFindByID() [Internal]\n// - TableGetBoundSettings() [Internal]\n// - TableResetSettings()\n// - TableSaveSettings() [Internal]\n// - TableLoadSettings() [Internal]\n// - TableSettingsHandler_ClearAll() [Internal]\n// - TableSettingsHandler_ApplyAll() [Internal]\n// - TableSettingsHandler_ReadOpen() [Internal]\n// - TableSettingsHandler_ReadLine() [Internal]\n// - TableSettingsHandler_WriteAll() [Internal]\n// - TableSettingsInstallHandler() [Internal]\n//-------------------------------------------------------------------------\n// [Init] 1: TableSettingsHandler_ReadXXXX()   Load and parse .ini file into TableSettings.\n// [Main] 2: TableLoadSettings()               When table is created, bind Table to TableSettings, serialize TableSettings data into Table.\n// [Main] 3: TableSaveSettings()               When table properties are modified, serialize Table data into bound or new TableSettings, mark .ini as dirty.\n// [Main] 4: TableSettingsHandler_WriteAll()   When .ini file is dirty (which can come from other source), save TableSettings into .ini file.\n//-------------------------------------------------------------------------\n\n// Clear and initialize empty settings instance\nstatic void TableSettingsInit(ImGuiTableSettings* settings, ImGuiID id, int columns_count, int columns_count_max)\n{\n    IM_PLACEMENT_NEW(settings) ImGuiTableSettings();\n    ImGuiTableColumnSettings* settings_column = settings->GetColumnSettings();\n    for (int n = 0; n < columns_count_max; n++, settings_column++)\n        IM_PLACEMENT_NEW(settings_column) ImGuiTableColumnSettings();\n    settings->ID = id;\n    settings->ColumnsCount = (ImGuiTableColumnIdx)columns_count;\n    settings->ColumnsCountMax = (ImGuiTableColumnIdx)columns_count_max;\n    settings->WantApply = true;\n}\n\nstatic size_t TableSettingsCalcChunkSize(int columns_count)\n{\n    return sizeof(ImGuiTableSettings) + (size_t)columns_count * sizeof(ImGuiTableColumnSettings);\n}\n\nImGuiTableSettings* ImGui::TableSettingsCreate(ImGuiID id, int columns_count)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(TableSettingsCalcChunkSize(columns_count));\n    TableSettingsInit(settings, id, columns_count, columns_count);\n    return settings;\n}\n\n// Find existing settings\nImGuiTableSettings* ImGui::TableSettingsFindByID(ImGuiID id)\n{\n    // FIXME-OPT: Might want to store a lookup map for this?\n    ImGuiContext& g = *GImGui;\n    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))\n        if (settings->ID == id)\n            return settings;\n    return NULL;\n}\n\n// Get settings for a given table, NULL if none\nImGuiTableSettings* ImGui::TableGetBoundSettings(ImGuiTable* table)\n{\n    if (table->SettingsOffset != -1)\n    {\n        ImGuiContext& g = *GImGui;\n        ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset);\n        IM_ASSERT(settings->ID == table->ID);\n        if (settings->ColumnsCountMax >= table->ColumnsCount)\n            return settings; // OK\n        settings->ID = 0; // Invalidate storage, we won't fit because of a count change\n    }\n    return NULL;\n}\n\n// Restore initial state of table (with or without saved settings)\nvoid ImGui::TableResetSettings(ImGuiTable* table)\n{\n    table->IsInitializing = table->IsSettingsDirty = true;\n    table->IsResetAllRequest = false;\n    table->IsSettingsRequestLoad = false;                   // Don't reload from ini\n    table->SettingsLoadedFlags = ImGuiTableFlags_None;      // Mark as nothing loaded so our initialized data becomes authoritative\n}\n\nvoid ImGui::TableSaveSettings(ImGuiTable* table)\n{\n    table->IsSettingsDirty = false;\n    if (table->Flags & ImGuiTableFlags_NoSavedSettings)\n        return;\n\n    // Bind or create settings data\n    ImGuiContext& g = *GImGui;\n    ImGuiTableSettings* settings = TableGetBoundSettings(table);\n    if (settings == NULL)\n    {\n        settings = TableSettingsCreate(table->ID, table->ColumnsCount);\n        table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings);\n    }\n    settings->ColumnsCount = (ImGuiTableColumnIdx)table->ColumnsCount;\n\n    // Serialize ImGuiTable/ImGuiTableColumn into ImGuiTableSettings/ImGuiTableColumnSettings\n    IM_ASSERT(settings->ID == table->ID);\n    IM_ASSERT(settings->ColumnsCount == table->ColumnsCount && settings->ColumnsCountMax >= settings->ColumnsCount);\n    ImGuiTableColumn* column = table->Columns.Data;\n    ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();\n\n    bool save_ref_scale = false;\n    settings->SaveFlags = ImGuiTableFlags_None;\n    for (int n = 0; n < table->ColumnsCount; n++, column++, column_settings++)\n    {\n        const float width_or_weight = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? column->StretchWeight : column->WidthRequest;\n        column_settings->WidthOrWeight = width_or_weight;\n        column_settings->Index = (ImGuiTableColumnIdx)n;\n        column_settings->DisplayOrder = column->DisplayOrder;\n        column_settings->SortOrder = column->SortOrder;\n        column_settings->SortDirection = column->SortDirection;\n        column_settings->IsEnabled = column->IsUserEnabled;\n        column_settings->IsStretch = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? 1 : 0;\n        if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) == 0)\n            save_ref_scale = true;\n\n        // We skip saving some data in the .ini file when they are unnecessary to restore our state.\n        // Note that fixed width where initial width was derived from auto-fit will always be saved as InitStretchWeightOrWidth will be 0.0f.\n        // FIXME-TABLE: We don't have logic to easily compare SortOrder to DefaultSortOrder yet so it's always saved when present.\n        if (width_or_weight != column->InitStretchWeightOrWidth)\n            settings->SaveFlags |= ImGuiTableFlags_Resizable;\n        if (column->DisplayOrder != n)\n            settings->SaveFlags |= ImGuiTableFlags_Reorderable;\n        if (column->SortOrder != -1)\n            settings->SaveFlags |= ImGuiTableFlags_Sortable;\n        if (column->IsUserEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0))\n            settings->SaveFlags |= ImGuiTableFlags_Hideable;\n    }\n    settings->SaveFlags &= table->Flags;\n    settings->RefScale = save_ref_scale ? table->RefScale : 0.0f;\n\n    MarkIniSettingsDirty();\n}\n\nvoid ImGui::TableLoadSettings(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    table->IsSettingsRequestLoad = false;\n    if (table->Flags & ImGuiTableFlags_NoSavedSettings)\n        return;\n\n    // Bind settings\n    ImGuiTableSettings* settings;\n    if (table->SettingsOffset == -1)\n    {\n        settings = TableSettingsFindByID(table->ID);\n        if (settings == NULL)\n            return;\n        if (settings->ColumnsCount != table->ColumnsCount) // Allow settings if columns count changed. We could otherwise decide to return...\n            table->IsSettingsDirty = true;\n        table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings);\n    }\n    else\n    {\n        settings = TableGetBoundSettings(table);\n    }\n\n    table->SettingsLoadedFlags = settings->SaveFlags;\n    table->RefScale = settings->RefScale;\n\n    // Initialize default columns settings\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        TableInitColumnDefaults(table, column, ~0);\n        column->AutoFitQueue = 0x00;\n    }\n\n    // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn\n    ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();\n    for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++)\n    {\n        int column_n = column_settings->Index;\n        if (column_n < 0 || column_n >= table->ColumnsCount)\n            continue;\n\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (settings->SaveFlags & ImGuiTableFlags_Resizable)\n        {\n            if (column_settings->IsStretch)\n                column->StretchWeight = column_settings->WidthOrWeight;\n            else\n                column->WidthRequest = column_settings->WidthOrWeight;\n        }\n        if (settings->SaveFlags & ImGuiTableFlags_Reorderable)\n            column->DisplayOrder = column_settings->DisplayOrder;\n        if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)\n            column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1;\n        column->SortOrder = column_settings->SortOrder;\n        column->SortDirection = column_settings->SortDirection;\n    }\n\n    // Fix display order and build index\n    if (settings->SaveFlags & ImGuiTableFlags_Reorderable)\n        TableFixDisplayOrder(table);\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n;\n}\n\nstruct ImGuiTableFixDisplayOrderColumnData\n{\n    ImGuiTableColumnIdx     Idx;\n    ImGuiTable*             Table;  // This is unfortunate but we don't have userdata in qsort api.\n};\n\n// Sort by DisplayOrder and then Index\nstatic int IMGUI_CDECL TableFixDisplayOrderComparer(const void* lhs, const void* rhs)\n{\n    const ImGuiTable* table = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Table;\n    const ImGuiTableColumnIdx lhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Idx;\n    const ImGuiTableColumnIdx rhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)rhs)->Idx;\n    const int order_delta = (table->Columns[lhs_idx].DisplayOrder - table->Columns[rhs_idx].DisplayOrder);\n    return (order_delta > 0) ? +1 : (order_delta < 0) ? -1 : (lhs_idx > rhs_idx) ? +1 : -1;\n}\n\n// Fix invalid display order data: compact values (0,1,3 -> 0,1,2); preserve relative order (0,3,1 -> 0,2,1); deduplicate (0,4,1,1 -> 0,3,1,2)\nvoid ImGui::TableFixDisplayOrder(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    g.TempBuffer.reserve((int)(sizeof(ImGuiTableFixDisplayOrderColumnData) * table->ColumnsCount)); // FIXME: Maybe wrap those two lines as a helper.\n    ImGuiTableFixDisplayOrderColumnData* fdo_columns = (ImGuiTableFixDisplayOrderColumnData*)(void*)g.TempBuffer.Data;\n    for (int n = 0; n < table->ColumnsCount; n++)\n    {\n        fdo_columns[n].Idx = (ImGuiTableColumnIdx)n;\n        fdo_columns[n].Table = table;\n    }\n    ImQsort(fdo_columns, (size_t)table->ColumnsCount, sizeof(ImGuiTableFixDisplayOrderColumnData), TableFixDisplayOrderComparer);\n    for (int n = 0; n < table->ColumnsCount; n++)\n        table->Columns[fdo_columns[n].Idx].DisplayOrder = (ImGuiTableColumnIdx)n;\n}\n\nstatic void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)\n{\n    ImGuiContext& g = *ctx;\n    for (int i = 0; i != g.Tables.GetMapSize(); i++)\n        if (ImGuiTable* table = g.Tables.TryGetMapData(i))\n            table->SettingsOffset = -1;\n    g.SettingsTables.clear();\n}\n\n// Apply to existing windows (if any)\nstatic void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)\n{\n    ImGuiContext& g = *ctx;\n    for (int i = 0; i != g.Tables.GetMapSize(); i++)\n        if (ImGuiTable* table = g.Tables.TryGetMapData(i))\n        {\n            table->IsSettingsRequestLoad = true;\n            table->SettingsOffset = -1;\n        }\n}\n\nstatic void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)\n{\n    ImGuiID id = 0;\n    int columns_count = 0;\n    if (sscanf(name, \"0x%08X,%d\", &id, &columns_count) < 2)\n        return NULL;\n\n    if (ImGuiTableSettings* settings = ImGui::TableSettingsFindByID(id))\n    {\n        if (settings->ColumnsCountMax >= columns_count)\n        {\n            TableSettingsInit(settings, id, columns_count, settings->ColumnsCountMax); // Recycle\n            return settings;\n        }\n        settings->ID = 0; // Invalidate storage, we won't fit because of a count change\n    }\n    return ImGui::TableSettingsCreate(id, columns_count);\n}\n\nstatic void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)\n{\n    // \"Column 0  UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v\"\n    ImGuiTableSettings* settings = (ImGuiTableSettings*)entry;\n    float f = 0.0f;\n    int column_n = 0, r = 0, n = 0;\n\n    if (sscanf(line, \"RefScale=%f\", &f) == 1) { settings->RefScale = f; return; }\n\n    if (sscanf(line, \"Column %d%n\", &column_n, &r) == 1)\n    {\n        if (column_n < 0 || column_n >= settings->ColumnsCount)\n            return;\n        line = ImStrSkipBlank(line + r);\n        char c = 0;\n        ImGuiTableColumnSettings* column = settings->GetColumnSettings() + column_n;\n        column->Index = (ImGuiTableColumnIdx)column_n;\n        if (sscanf(line, \"UserID=0x%08X%n\", (ImU32*)&n, &r)==1) { line = ImStrSkipBlank(line + r); column->UserID = (ImGuiID)n; }\n        if (sscanf(line, \"Width=%d%n\", &n, &r) == 1)            { line = ImStrSkipBlank(line + r); column->WidthOrWeight = (float)n; column->IsStretch = 0; settings->SaveFlags |= ImGuiTableFlags_Resizable; }\n        if (sscanf(line, \"Weight=%f%n\", &f, &r) == 1)           { line = ImStrSkipBlank(line + r); column->WidthOrWeight = f; column->IsStretch = 1; settings->SaveFlags |= ImGuiTableFlags_Resizable; }\n        if (sscanf(line, \"Visible=%d%n\", &n, &r) == 1)          { line = ImStrSkipBlank(line + r); column->IsEnabled = (ImU8)n; settings->SaveFlags |= ImGuiTableFlags_Hideable; }\n        if (sscanf(line, \"Order=%d%n\", &n, &r) == 1)            { line = ImStrSkipBlank(line + r); column->DisplayOrder = (ImGuiTableColumnIdx)n; settings->SaveFlags |= ImGuiTableFlags_Reorderable; }\n        if (sscanf(line, \"Sort=%d%c%n\", &n, &c, &r) == 2)       { line = ImStrSkipBlank(line + r); column->SortOrder = (ImGuiTableColumnIdx)n; column->SortDirection = (c == '^') ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; settings->SaveFlags |= ImGuiTableFlags_Sortable; }\n    }\n}\n\nstatic void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)\n{\n    ImGuiContext& g = *ctx;\n    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))\n    {\n        if (settings->ID == 0) // Skip ditched settings\n            continue;\n\n        // TableSaveSettings() may clear some of those flags when we establish that the data can be stripped\n        // (e.g. Order was unchanged)\n        const bool save_size    = (settings->SaveFlags & ImGuiTableFlags_Resizable) != 0;\n        const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0;\n        const bool save_order   = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0;\n        const bool save_sort    = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0;\n        // We need to save the [Table] entry even if all the bools are false, since this records a table with \"default settings\".\n\n        buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve\n        buf->appendf(\"[%s][0x%08X,%d]\\n\", handler->TypeName, settings->ID, settings->ColumnsCount);\n        if (settings->RefScale != 0.0f)\n            buf->appendf(\"RefScale=%g\\n\", settings->RefScale);\n        ImGuiTableColumnSettings* column = settings->GetColumnSettings();\n        for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++)\n        {\n            // \"Column 0  UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v\"\n            bool save_column = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1);\n            if (!save_column)\n                continue;\n            buf->appendf(\"Column %-2d\", column_n);\n            if (column->UserID != 0)                    { buf->appendf(\" UserID=%08X\", column->UserID); }\n            if (save_size && column->IsStretch)         { buf->appendf(\" Weight=%.4f\", column->WidthOrWeight); }\n            if (save_size && !column->IsStretch)        { buf->appendf(\" Width=%d\", (int)column->WidthOrWeight); }\n            if (save_visible)                           { buf->appendf(\" Visible=%d\", column->IsEnabled); }\n            if (save_order)                             { buf->appendf(\" Order=%d\", column->DisplayOrder); }\n            if (save_sort && column->SortOrder != -1)   { buf->appendf(\" Sort=%d%c\", column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? 'v' : '^'); }\n            buf->append(\"\\n\");\n        }\n        buf->append(\"\\n\");\n    }\n}\n\nvoid ImGui::TableSettingsAddSettingsHandler()\n{\n    ImGuiSettingsHandler ini_handler;\n    ini_handler.TypeName = \"Table\";\n    ini_handler.TypeHash = ImHashStr(\"Table\");\n    ini_handler.ClearAllFn = TableSettingsHandler_ClearAll;\n    ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen;\n    ini_handler.ReadLineFn = TableSettingsHandler_ReadLine;\n    ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll;\n    ini_handler.WriteAllFn = TableSettingsHandler_WriteAll;\n    AddSettingsHandler(&ini_handler);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Garbage Collection\n//-------------------------------------------------------------------------\n// - TableRemove() [Internal]\n// - TableGcCompactTransientBuffers() [Internal]\n// - TableGcCompactSettings() [Internal]\n//-------------------------------------------------------------------------\n\n// Remove Table data (currently only used by TestEngine)\nvoid ImGui::TableRemove(ImGuiTable* table)\n{\n    //IMGUI_DEBUG_PRINT(\"TableRemove() id=0x%08X\\n\", table->ID);\n    ImGuiContext& g = *GImGui;\n    int table_idx = g.Tables.GetIndex(table);\n    //memset(table->RawData.Data, 0, table->RawData.size_in_bytes());\n    //memset(table, 0, sizeof(ImGuiTable));\n    g.Tables.Remove(table->ID, table);\n    g.TablesLastTimeActive[table_idx] = -1.0f;\n}\n\n// Free up/compact internal Table buffers for when it gets unused\nvoid ImGui::TableGcCompactTransientBuffers(ImGuiTable* table)\n{\n    //IMGUI_DEBUG_PRINT(\"TableGcCompactTransientBuffers() id=0x%08X\\n\", table->ID);\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(table->MemoryCompacted == false);\n    table->SortSpecs.Specs = NULL;\n    table->SortSpecsMulti.clear();\n    table->IsSortSpecsDirty = true; // FIXME: In theory shouldn't have to leak into user performing a sort on resume.\n    table->ColumnsNames.clear();\n    table->MemoryCompacted = true;\n    for (int n = 0; n < table->ColumnsCount; n++)\n        table->Columns[n].NameOffset = -1;\n    g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f;\n}\n\nvoid ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data)\n{\n    temp_data->DrawSplitter.ClearFreeMemory();\n    temp_data->LastTimeActive = -1.0f;\n}\n\n// Compact and remove unused settings data (currently only used by TestEngine)\nvoid ImGui::TableGcCompactSettings()\n{\n    ImGuiContext& g = *GImGui;\n    int required_memory = 0;\n    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))\n        if (settings->ID != 0)\n            required_memory += (int)TableSettingsCalcChunkSize(settings->ColumnsCount);\n    if (required_memory == g.SettingsTables.Buf.Size)\n        return;\n    ImChunkStream<ImGuiTableSettings> new_chunk_stream;\n    new_chunk_stream.Buf.reserve(required_memory);\n    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))\n        if (settings->ID != 0)\n            memcpy(new_chunk_stream.alloc_chunk(TableSettingsCalcChunkSize(settings->ColumnsCount)), settings, TableSettingsCalcChunkSize(settings->ColumnsCount));\n    g.SettingsTables.swap(new_chunk_stream);\n}\n\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Debugging\n//-------------------------------------------------------------------------\n// - DebugNodeTable() [Internal]\n//-------------------------------------------------------------------------\n\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n\nstatic const char* DebugNodeTableGetSizingPolicyDesc(ImGuiTableFlags sizing_policy)\n{\n    sizing_policy &= ImGuiTableFlags_SizingMask_;\n    if (sizing_policy == ImGuiTableFlags_SizingFixedFit)    { return \"FixedFit\"; }\n    if (sizing_policy == ImGuiTableFlags_SizingFixedSame)   { return \"FixedSame\"; }\n    if (sizing_policy == ImGuiTableFlags_SizingStretchProp) { return \"StretchProp\"; }\n    if (sizing_policy == ImGuiTableFlags_SizingStretchSame) { return \"StretchSame\"; }\n    return \"N/A\";\n}\n\nvoid ImGui::DebugNodeTable(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    const bool is_active = (table->LastFrameActive >= g.FrameCount - 2); // Note that fully clipped early out scrolling tables will appear as inactive here.\n    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }\n    bool open = TreeNode(table, \"Table 0x%08X (%d columns, in '%s')%s\", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? \"\" : \" *Inactive*\");\n    if (!is_active) { PopStyleColor(); }\n    if (IsItemHovered())\n        GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));\n    if (IsItemVisible() && table->HoveredColumnBody != -1)\n        GetForegroundDrawList(table->OuterWindow)->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255));\n    if (!open)\n        return;\n    if (table->InstanceCurrent > 0)\n        Text(\"** %d instances of same table! Some data below will refer to last instance.\", table->InstanceCurrent + 1);\n    if (g.IO.ConfigDebugIsDebuggerPresent)\n    {\n        if (DebugBreakButton(\"**DebugBreak**\", \"in BeginTable()\"))\n            g.DebugBreakInTable = table->ID;\n        SameLine();\n    }\n\n    bool clear_settings = SmallButton(\"Clear settings\");\n    BulletText(\"OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'\", table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.GetWidth(), table->OuterRect.GetHeight(), DebugNodeTableGetSizingPolicyDesc(table->Flags));\n    BulletText(\"ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s\", table->ColumnsGivenWidth, table->ColumnsAutoFitWidth, table->InnerWidth, table->InnerWidth == 0.0f ? \" (auto)\" : \"\");\n    BulletText(\"CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f\", table->CellPaddingX, table->CellSpacingX1, table->CellSpacingX2, table->OuterPaddingX);\n    BulletText(\"HoveredColumnBody: %d, HoveredColumnBorder: %d\", table->HoveredColumnBody, table->HoveredColumnBorder);\n    BulletText(\"ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d\", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn);\n    for (int n = 0; n < table->InstanceCurrent + 1; n++)\n    {\n        ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, n);\n        BulletText(\"Instance %d: HoveredRow: %d, LastOuterHeight: %.2f\", n, table_instance->HoveredRowLast, table_instance->LastOuterHeight);\n    }\n    //BulletText(\"BgDrawChannels: %d/%d\", 0, table->BgDrawChannelUnfrozen);\n    float sum_weights = 0.0f;\n    for (int n = 0; n < table->ColumnsCount; n++)\n        if (table->Columns[n].Flags & ImGuiTableColumnFlags_WidthStretch)\n            sum_weights += table->Columns[n].StretchWeight;\n    for (int n = 0; n < table->ColumnsCount; n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[n];\n        const char* name = TableGetColumnName(table, n);\n        char buf[512];\n        ImFormatString(buf, IM_COUNTOF(buf),\n            \"Column %d order %d '%s': offset %+.2f to %+.2f%s\\n\"\n            \"Enabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\\n\"\n            \"WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\\n\"\n            \"MinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\\n\"\n            \"ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\\n\"\n            \"Sort: %d%s, UserID: 0x%08X, Flags: 0x%04X: %s%s%s..\",\n            n, column->DisplayOrder, name, column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x, (n < table->FreezeColumnsRequest) ? \" (Frozen)\" : \"\",\n            column->IsEnabled, column->IsVisibleX, column->IsVisibleY, column->IsRequestOutput, column->IsSkipItems, column->DrawChannelFrozen, column->DrawChannelUnfrozen,\n            column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight, column->StretchWeight > 0.0f ? (column->StretchWeight / sum_weights) * 100.0f : 0.0f,\n            column->MinX, column->MaxX, column->MaxX - column->MinX, column->ClipRect.Min.x, column->ClipRect.Max.x, column->ClipRect.Max.x - column->ClipRect.Min.x,\n            column->ContentMaxXFrozen - column->WorkMinX, column->ContentMaxXUnfrozen - column->WorkMinX, column->ContentMaxXHeadersUsed - column->WorkMinX, column->ContentMaxXHeadersIdeal - column->WorkMinX,\n            column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? \" (Asc)\" : (column->SortDirection == ImGuiSortDirection_Descending) ? \" (Des)\" : \"\", column->UserID, column->Flags,\n            (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? \"WidthStretch \" : \"\",\n            (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? \"WidthFixed \" : \"\",\n            (column->Flags & ImGuiTableColumnFlags_NoResize) ? \"NoResize \" : \"\");\n        Bullet();\n        Selectable(buf);\n        if (IsItemHovered())\n        {\n            ImRect r(column->MinX, table->OuterRect.Min.y, column->MaxX, table->OuterRect.Max.y);\n            GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255));\n        }\n    }\n    if (ImGuiTableSettings* settings = TableGetBoundSettings(table))\n        DebugNodeTableSettings(settings);\n    if (clear_settings)\n        table->IsResetAllRequest = true;\n    TreePop();\n}\n\nvoid ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings)\n{\n    if (!TreeNode((void*)(intptr_t)settings->ID, \"Settings 0x%08X (%d columns)\", settings->ID, settings->ColumnsCount))\n        return;\n    BulletText(\"SaveFlags: 0x%08X\", settings->SaveFlags);\n    BulletText(\"ColumnsCount: %d (max %d)\", settings->ColumnsCount, settings->ColumnsCountMax);\n    for (int n = 0; n < settings->ColumnsCount; n++)\n    {\n        ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n];\n        ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None;\n        BulletText(\"Column %d Order %d SortOrder %d %s Vis %d %s %7.3f UserID 0x%08X\",\n            n, column_settings->DisplayOrder, column_settings->SortOrder,\n            (sort_dir == ImGuiSortDirection_Ascending) ? \"Asc\" : (sort_dir == ImGuiSortDirection_Descending) ? \"Des\" : \"---\",\n            column_settings->IsEnabled, column_settings->IsStretch ? \"Weight\" : \"Width \", column_settings->WidthOrWeight, column_settings->UserID);\n    }\n    TreePop();\n}\n\n#else // #ifndef IMGUI_DISABLE_DEBUG_TOOLS\n\nvoid ImGui::DebugNodeTable(ImGuiTable*) {}\nvoid ImGui::DebugNodeTableSettings(ImGuiTableSettings*) {}\n\n#endif\n\n\n//-------------------------------------------------------------------------\n// [SECTION] Columns, BeginColumns, EndColumns, etc.\n// (This is a legacy API, prefer using BeginTable/EndTable!)\n//-------------------------------------------------------------------------\n// FIXME: sizing is lossy when columns width is very small (default width may turn negative etc.)\n//-------------------------------------------------------------------------\n// - SetWindowClipRectBeforeSetChannel() [Internal]\n// - GetColumnIndex()\n// - GetColumnsCount()\n// - GetColumnOffset()\n// - GetColumnWidth()\n// - SetColumnOffset()\n// - SetColumnWidth()\n// - PushColumnClipRect() [Internal]\n// - PushColumnsBackground() [Internal]\n// - PopColumnsBackground() [Internal]\n// - FindOrCreateColumns() [Internal]\n// - GetColumnsID() [Internal]\n// - BeginColumns()\n// - NextColumn()\n// - EndColumns()\n// - Columns()\n//-------------------------------------------------------------------------\n\n// [Internal] Small optimization to avoid calls to PopClipRect/SetCurrentChannel/PushClipRect in sequences,\n// they would meddle many times with the underlying ImDrawCmd.\n// Instead, we do a preemptive overwrite of clipping rectangle _without_ altering the command-buffer and let\n// the subsequent single call to SetCurrentChannel() does it things once.\nvoid ImGui::SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect)\n{\n    ImVec4 clip_rect_vec4 = clip_rect.ToVec4();\n    window->ClipRect = clip_rect;\n    window->DrawList->_CmdHeader.ClipRect = clip_rect_vec4;\n    window->DrawList->_ClipRectStack.Data[window->DrawList->_ClipRectStack.Size - 1] = clip_rect_vec4;\n}\n\nint ImGui::GetColumnIndex()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CurrentColumns ? window->DC.CurrentColumns->Current : 0;\n}\n\nint ImGui::GetColumnsCount()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CurrentColumns ? window->DC.CurrentColumns->Count : 1;\n}\n\nfloat ImGui::GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm)\n{\n    return offset_norm * (columns->OffMaxX - columns->OffMinX);\n}\n\nfloat ImGui::GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset)\n{\n    return offset / (columns->OffMaxX - columns->OffMinX);\n}\n\nstatic const float COLUMNS_HIT_RECT_HALF_THICKNESS = 4.0f;\n\nstatic float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index)\n{\n    // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing\n    // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.\n    IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));\n\n    float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale) - window->Pos.x;\n    x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);\n    if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths))\n        x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);\n\n    return x;\n}\n\nfloat ImGui::GetColumnOffset(int column_index)\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (columns == NULL)\n        return 0.0f;\n\n    if (column_index < 0)\n        column_index = columns->Current;\n    IM_ASSERT(column_index < columns->Columns.Size);\n\n    const float t = columns->Columns[column_index].OffsetNorm;\n    const float x_offset = ImLerp(columns->OffMinX, columns->OffMaxX, t);\n    return x_offset;\n}\n\nstatic float GetColumnWidthEx(ImGuiOldColumns* columns, int column_index, bool before_resize = false)\n{\n    if (column_index < 0)\n        column_index = columns->Current;\n\n    float offset_norm;\n    if (before_resize)\n        offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;\n    else\n        offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;\n    return ImGui::GetColumnOffsetFromNorm(columns, offset_norm);\n}\n\nfloat ImGui::GetColumnWidth(int column_index)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (columns == NULL)\n        return GetContentRegionAvail().x;\n\n    if (column_index < 0)\n        column_index = columns->Current;\n    return GetColumnOffsetFromNorm(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);\n}\n\nvoid ImGui::SetColumnOffset(int column_index, float offset)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    IM_ASSERT(columns != NULL);\n\n    if (column_index < 0)\n        column_index = columns->Current;\n    IM_ASSERT(column_index < columns->Columns.Size);\n\n    const bool preserve_width = !(columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths) && (column_index < columns->Count - 1);\n    const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;\n\n    if (!(columns->Flags & ImGuiOldColumnFlags_NoForceWithinWindow))\n        offset = ImMin(offset, columns->OffMaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));\n    columns->Columns[column_index].OffsetNorm = GetColumnNormFromOffset(columns, offset - columns->OffMinX);\n\n    if (preserve_width)\n        SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));\n}\n\nvoid ImGui::SetColumnWidth(int column_index, float width)\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    IM_ASSERT(columns != NULL);\n\n    if (column_index < 0)\n        column_index = columns->Current;\n    SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);\n}\n\nvoid ImGui::PushColumnClipRect(int column_index)\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (column_index < 0)\n        column_index = columns->Current;\n\n    ImGuiOldColumnData* column = &columns->Columns[column_index];\n    PushClipRect(column->ClipRect.Min, column->ClipRect.Max, false);\n}\n\n// Get into the columns background draw command (which is generally the same draw command as before we called BeginColumns)\nvoid ImGui::PushColumnsBackground()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (columns->Count == 1)\n        return;\n\n    // Optimization: avoid SetCurrentChannel() + PushClipRect()\n    columns->HostBackupClipRect = window->ClipRect;\n    SetWindowClipRectBeforeSetChannel(window, columns->HostInitialClipRect);\n    columns->Splitter.SetCurrentChannel(window->DrawList, 0);\n}\n\nvoid ImGui::PopColumnsBackground()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (columns->Count == 1)\n        return;\n\n    // Optimization: avoid PopClipRect() + SetCurrentChannel()\n    SetWindowClipRectBeforeSetChannel(window, columns->HostBackupClipRect);\n    columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1);\n}\n\nImGuiOldColumns* ImGui::FindOrCreateColumns(ImGuiWindow* window, ImGuiID id)\n{\n    // We have few columns per window so for now we don't need bother much with turning this into a faster lookup.\n    for (int n = 0; n < window->ColumnsStorage.Size; n++)\n        if (window->ColumnsStorage[n].ID == id)\n            return &window->ColumnsStorage[n];\n\n    window->ColumnsStorage.push_back(ImGuiOldColumns());\n    ImGuiOldColumns* columns = &window->ColumnsStorage.back();\n    columns->ID = id;\n    return columns;\n}\n\nImGuiID ImGui::GetColumnsID(const char* str_id, int columns_count)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n\n    // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.\n    // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.\n    PushID(0x11223347 + (str_id ? 0 : columns_count));\n    ImGuiID id = window->GetID(str_id ? str_id : \"columns\");\n    PopID();\n\n    return id;\n}\n\nvoid ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n\n    IM_ASSERT(columns_count >= 1);\n    IM_ASSERT(window->DC.CurrentColumns == NULL);   // Nested columns are currently not supported\n\n    // Acquire storage for the columns set\n    ImGuiID id = GetColumnsID(str_id, columns_count);\n    ImGuiOldColumns* columns = FindOrCreateColumns(window, id);\n    IM_ASSERT(columns->ID == id);\n    columns->Current = 0;\n    columns->Count = columns_count;\n    columns->Flags = flags;\n    window->DC.CurrentColumns = columns;\n    window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();\n\n    columns->HostCursorPosY = window->DC.CursorPos.y;\n    columns->HostCursorMaxPosX = window->DC.CursorMaxPos.x;\n    columns->HostInitialClipRect = window->ClipRect;\n    columns->HostBackupParentWorkRect = window->ParentWorkRect;\n    window->ParentWorkRect = window->WorkRect;\n\n    // Set state for first column\n    // We aim so that the right-most column will have the same clipping width as other after being clipped by parent ClipRect\n    const float column_padding = g.Style.ItemSpacing.x;\n    const float half_clip_extend_x = ImTrunc(ImMax(window->WindowPadding.x * 0.5f, window->WindowBorderSize));\n    const float max_1 = window->WorkRect.Max.x + column_padding - ImMax(column_padding - window->WindowPadding.x, 0.0f);\n    const float max_2 = window->WorkRect.Max.x + half_clip_extend_x;\n    columns->OffMinX = window->DC.Indent.x - column_padding + ImMax(column_padding - window->WindowPadding.x, 0.0f);\n    columns->OffMaxX = ImMax(ImMin(max_1, max_2) - window->Pos.x, columns->OffMinX + 1.0f);\n    columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;\n\n    // Clear data if columns count changed\n    if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)\n        columns->Columns.resize(0);\n\n    // Initialize default widths\n    columns->IsFirstFrame = (columns->Columns.Size == 0);\n    if (columns->Columns.Size == 0)\n    {\n        columns->Columns.reserve(columns_count + 1);\n        for (int n = 0; n < columns_count + 1; n++)\n        {\n            ImGuiOldColumnData column;\n            column.OffsetNorm = n / (float)columns_count;\n            columns->Columns.push_back(column);\n        }\n    }\n\n    for (int n = 0; n < columns_count; n++)\n    {\n        // Compute clipping rectangle\n        ImGuiOldColumnData* column = &columns->Columns[n];\n        float clip_x1 = IM_ROUND(window->Pos.x + GetColumnOffset(n));\n        float clip_x2 = IM_ROUND(window->Pos.x + GetColumnOffset(n + 1) - 1.0f);\n        column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);\n        column->ClipRect.ClipWithFull(window->ClipRect);\n    }\n\n    if (columns->Count > 1)\n    {\n        columns->Splitter.Split(window->DrawList, 1 + columns->Count);\n        columns->Splitter.SetCurrentChannel(window->DrawList, 1);\n        PushColumnClipRect(0);\n    }\n\n    // We don't generally store Indent.x inside ColumnsOffset because it may be manipulated by the user.\n    float offset_0 = GetColumnOffset(columns->Current);\n    float offset_1 = GetColumnOffset(columns->Current + 1);\n    float width = offset_1 - offset_0;\n    PushItemWidth(width * 0.65f);\n    window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f);\n    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);\n    window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding;\n    window->WorkRect.Max.y = window->ContentRegionRect.Max.y;\n}\n\nvoid ImGui::NextColumn()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems || window->DC.CurrentColumns == NULL)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n\n    if (columns->Count == 1)\n    {\n        window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);\n        IM_ASSERT(columns->Current == 0);\n        return;\n    }\n\n    // Next column\n    if (++columns->Current == columns->Count)\n        columns->Current = 0;\n\n    PopItemWidth();\n\n    // Optimization: avoid PopClipRect() + SetCurrentChannel() + PushClipRect()\n    // (which would needlessly attempt to update commands in the wrong channel, then pop or overwrite them),\n    ImGuiOldColumnData* column = &columns->Columns[columns->Current];\n    SetWindowClipRectBeforeSetChannel(window, column->ClipRect);\n    columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1);\n\n    const float column_padding = g.Style.ItemSpacing.x;\n    columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);\n    if (columns->Current > 0)\n    {\n        // Columns 1+ ignore IndentX (by canceling it out)\n        // FIXME-COLUMNS: Unnecessary, could be locked?\n        window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + column_padding;\n    }\n    else\n    {\n        // New row/line: column 0 honor IndentX.\n        window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f);\n        window->DC.IsSameLine = false;\n        columns->LineMinY = columns->LineMaxY;\n    }\n    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);\n    window->DC.CursorPos.y = columns->LineMinY;\n    window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);\n    window->DC.CurrLineTextBaseOffset = 0.0f;\n\n    // FIXME-COLUMNS: Share code with BeginColumns() - move code on columns setup.\n    float offset_0 = GetColumnOffset(columns->Current);\n    float offset_1 = GetColumnOffset(columns->Current + 1);\n    float width = offset_1 - offset_0;\n    PushItemWidth(width * 0.65f);\n    window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding;\n}\n\nvoid ImGui::EndColumns()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    IM_ASSERT(columns != NULL);\n\n    PopItemWidth();\n    if (columns->Count > 1)\n    {\n        PopClipRect();\n        columns->Splitter.Merge(window->DrawList);\n    }\n\n    const ImGuiOldColumnFlags flags = columns->Flags;\n    columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);\n    window->DC.CursorPos.y = columns->LineMaxY;\n    if (!(flags & ImGuiOldColumnFlags_GrowParentContentsSize))\n        window->DC.CursorMaxPos.x = columns->HostCursorMaxPosX;  // Restore cursor max pos, as columns don't grow parent\n\n    // Draw columns borders and handle resize\n    // The IsBeingResized flag ensure we preserve pre-resize columns width so back-and-forth are not lossy\n    bool is_being_resized = false;\n    if (!(flags & ImGuiOldColumnFlags_NoBorder) && !window->SkipItems)\n    {\n        // We clip Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.\n        const float y1 = ImMax(columns->HostCursorPosY, window->ClipRect.Min.y);\n        const float y2 = ImMin(window->DC.CursorPos.y, window->ClipRect.Max.y);\n        int dragging_column = -1;\n        for (int n = 1; n < columns->Count; n++)\n        {\n            ImGuiOldColumnData* column = &columns->Columns[n];\n            float x = window->Pos.x + GetColumnOffset(n);\n            const ImGuiID column_id = columns->ID + ImGuiID(n);\n            const float column_hit_hw = ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale);\n            const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2));\n            if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav))\n                continue;\n\n            bool hovered = false, held = false;\n            if (!(flags & ImGuiOldColumnFlags_NoResize))\n            {\n                ButtonBehavior(column_hit_rect, column_id, &hovered, &held);\n                if (hovered || held)\n                    SetMouseCursor(ImGuiMouseCursor_ResizeEW);\n                if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize))\n                    dragging_column = n;\n            }\n\n            // Draw column\n            const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);\n            const float xi = IM_TRUNC(x);\n            window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col);\n        }\n\n        // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.\n        if (dragging_column != -1)\n        {\n            if (!columns->IsBeingResized)\n                for (int n = 0; n < columns->Count + 1; n++)\n                    columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;\n            columns->IsBeingResized = is_being_resized = true;\n            float x = GetDraggedColumnOffset(columns, dragging_column);\n            SetColumnOffset(dragging_column, x);\n        }\n    }\n    columns->IsBeingResized = is_being_resized;\n\n    window->WorkRect = window->ParentWorkRect;\n    window->ParentWorkRect = columns->HostBackupParentWorkRect;\n    window->DC.CurrentColumns = NULL;\n    window->DC.ColumnsOffset.x = 0.0f;\n    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);\n    NavUpdateCurrentWindowIsScrollPushableX();\n}\n\nvoid ImGui::Columns(int columns_count, const char* id, bool borders)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    IM_ASSERT(columns_count >= 1);\n\n    ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder);\n    //flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (columns != NULL && columns->Count == columns_count && columns->Flags == flags)\n        return;\n\n    if (columns != NULL)\n        EndColumns();\n\n    if (columns_count != 1)\n        BeginColumns(id, columns_count, flags);\n}\n\n//-------------------------------------------------------------------------\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/source/imgui_widgets.cpp",
    "content": "// dear imgui, v1.92.7 WIP\n// (widgets code)\n\n/*\n\nIndex of this file:\n\n// [SECTION] Forward Declarations\n// [SECTION] Widgets: Text, etc.\n// [SECTION] Widgets: Main (Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc.)\n// [SECTION] Widgets: Low-level Layout helpers (Spacing, Dummy, NewLine, Separator, etc.)\n// [SECTION] Widgets: ComboBox\n// [SECTION] Data Type and Data Formatting Helpers\n// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.\n// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.\n// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.\n// [SECTION] Widgets: InputText, InputTextMultiline\n// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.\n// [SECTION] Widgets: TreeNode, CollapsingHeader, etc.\n// [SECTION] Widgets: Selectable\n// [SECTION] Widgets: Typing-Select support\n// [SECTION] Widgets: Box-Select support\n// [SECTION] Widgets: Multi-Select support\n// [SECTION] Widgets: Multi-Select helpers\n// [SECTION] Widgets: ListBox\n// [SECTION] Widgets: PlotLines, PlotHistogram\n// [SECTION] Widgets: Value helpers\n// [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc.\n// [SECTION] Widgets: BeginTabBar, EndTabBar, etc.\n// [SECTION] Widgets: BeginTabItem, EndTabItem, etc.\n// [SECTION] Widgets: Columns, BeginColumns, EndColumns, etc.\n\n*/\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#ifndef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS\n#endif\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_internal.h\"\n\n// System includes\n#include <stdint.h>     // intptr_t\n\n//-------------------------------------------------------------------------\n// Warnings\n//-------------------------------------------------------------------------\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4127)     // condition expression is constant\n#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later\n#pragma warning (disable: 5054)     // operator '|': deprecated between enumerations of different types\n#endif\n#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).\n#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast                            // yes, they are more terse.\n#pragma clang diagnostic ignored \"-Wfloat-equal\"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.\n#pragma clang diagnostic ignored \"-Wformat\"                         // warning: format specifies type 'int' but the argument has type 'unsigned int'\n#pragma clang diagnostic ignored \"-Wformat-nonliteral\"              // warning: format string is not a string literal            // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.\n#pragma clang diagnostic ignored \"-Wsign-conversion\"                // warning: implicit conversion changes signedness\n#pragma clang diagnostic ignored \"-Wunused-macros\"                  // warning: macro is not used                                // we define snprintf/vsnprintf on Windows so they are available, but not always used.\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.\n#pragma clang diagnostic ignored \"-Wenum-enum-conversion\"           // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')\n#pragma clang diagnostic ignored \"-Wdeprecated-enum-enum-conversion\"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#pragma clang diagnostic ignored \"-Wunsafe-buffer-usage\"            // warning: 'xxx' is an unsafe pointer used for buffer access\n#pragma clang diagnostic ignored \"-Wnontrivial-memaccess\"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type\n#pragma clang diagnostic ignored \"-Wswitch-default\"                 // warning: 'switch' missing 'default' label\n#elif defined(__GNUC__)\n#pragma GCC diagnostic ignored \"-Wpragmas\"                          // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"                      // warning: comparing floating-point with '==' or '!=' is unsafe\n#pragma GCC diagnostic ignored \"-Wformat\"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'\n#pragma GCC diagnostic ignored \"-Wformat-nonliteral\"                // warning: format not a string literal, format string not checked\n#pragma GCC diagnostic ignored \"-Wdeprecated-enum-enum-conversion\"  // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated\n#pragma GCC diagnostic ignored \"-Wdouble-promotion\"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function\n#pragma GCC diagnostic ignored \"-Wstrict-overflow\"                  // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#pragma GCC diagnostic ignored \"-Wcast-qual\"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"                  // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result\n#endif\n\n//-------------------------------------------------------------------------\n// Data\n//-------------------------------------------------------------------------\n\n// Widgets\nstatic const float          DRAGDROP_HOLD_TO_OPEN_TIMER = 0.70f;    // Time for drag-hold to activate items accepting the ImGuiButtonFlags_PressedOnDragDropHold button behavior.\nstatic const float          DRAG_MOUSE_THRESHOLD_FACTOR = 0.50f;    // Multiplier for the default value of io.MouseDragThreshold to make DragFloat/DragInt react faster to mouse drags.\n\n// Those MIN/MAX values are not define because we need to point to them\nstatic const signed char    IM_S8_MIN  = -128;\nstatic const signed char    IM_S8_MAX  = 127;\nstatic const unsigned char  IM_U8_MIN  = 0;\nstatic const unsigned char  IM_U8_MAX  = 0xFF;\nstatic const signed short   IM_S16_MIN = -32768;\nstatic const signed short   IM_S16_MAX = 32767;\nstatic const unsigned short IM_U16_MIN = 0;\nstatic const unsigned short IM_U16_MAX = 0xFFFF;\nstatic const ImS32          IM_S32_MIN = INT_MIN;    // (-2147483647 - 1), (0x80000000);\nstatic const ImS32          IM_S32_MAX = INT_MAX;    // (2147483647), (0x7FFFFFFF)\nstatic const ImU32          IM_U32_MIN = 0;\nstatic const ImU32          IM_U32_MAX = UINT_MAX;   // (0xFFFFFFFF)\n#ifdef LLONG_MIN\nstatic const ImS64          IM_S64_MIN = LLONG_MIN;  // (-9223372036854775807ll - 1ll);\nstatic const ImS64          IM_S64_MAX = LLONG_MAX;  // (9223372036854775807ll);\n#else\nstatic const ImS64          IM_S64_MIN = -9223372036854775807LL - 1;\nstatic const ImS64          IM_S64_MAX = 9223372036854775807LL;\n#endif\nstatic const ImU64          IM_U64_MIN = 0;\n#ifdef ULLONG_MAX\nstatic const ImU64          IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull);\n#else\nstatic const ImU64          IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);\n#endif\n\n//-------------------------------------------------------------------------\n// [SECTION] Forward Declarations\n//-------------------------------------------------------------------------\n\n// For InputTextEx()\nstatic bool     InputTextFilterCharacter(ImGuiContext* ctx, ImGuiInputTextState* state, unsigned int* p_char, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false);\nstatic ImVec2   InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining = NULL, ImVec2* out_offset = NULL, ImDrawTextFlags flags = 0);\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Text, etc.\n//-------------------------------------------------------------------------\n// - TextEx() [Internal]\n// - TextUnformatted()\n// - Text()\n// - TextV()\n// - TextColored()\n// - TextColoredV()\n// - TextDisabled()\n// - TextDisabledV()\n// - TextWrapped()\n// - TextWrappedV()\n// - LabelText()\n// - LabelTextV()\n// - BulletText()\n// - BulletTextV()\n//-------------------------------------------------------------------------\n\nvoid ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n    ImGuiContext& g = *GImGui;\n\n    // Accept null ranges\n    if (text == text_end)\n        text = text_end = \"\";\n\n    // Calculate length\n    const char* text_begin = text;\n    if (text_end == NULL)\n        text_end = text + ImStrlen(text); // FIXME-OPT\n\n    const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);\n    const float wrap_pos_x = window->DC.TextWrapPos;\n    const bool wrap_enabled = (wrap_pos_x >= 0.0f);\n    if (text_end - text <= 2000 || wrap_enabled)\n    {\n        // Common case\n        const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;\n        const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);\n\n        ImRect bb(text_pos, text_pos + text_size);\n        ItemSize(text_size, 0.0f);\n        if (!ItemAdd(bb, 0))\n            return;\n\n        // Render (we don't hide text after ## in this end-user function)\n        RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);\n    }\n    else\n    {\n        // Long text!\n        // Perform manual coarse clipping to optimize for long multi-line text\n        // - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.\n        // - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line.\n        // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop.\n        const char* line = text;\n        const float line_height = GetTextLineHeight();\n        ImVec2 text_size(0, 0);\n\n        // Lines to skip (can't skip when logging text)\n        ImVec2 pos = text_pos;\n        if (!g.LogEnabled)\n        {\n            int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height);\n            if (lines_skippable > 0)\n            {\n                int lines_skipped = 0;\n                while (line < text_end && lines_skipped < lines_skippable)\n                {\n                    const char* line_end = (const char*)ImMemchr(line, '\\n', text_end - line);\n                    if (!line_end)\n                        line_end = text_end;\n                    if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)\n                        text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);\n                    line = line_end + 1;\n                    lines_skipped++;\n                }\n                pos.y += lines_skipped * line_height;\n            }\n        }\n\n        // Lines to render\n        if (line < text_end)\n        {\n            ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));\n            while (line < text_end)\n            {\n                if (IsClippedEx(line_rect, 0))\n                    break;\n\n                const char* line_end = (const char*)ImMemchr(line, '\\n', text_end - line);\n                if (!line_end)\n                    line_end = text_end;\n                text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);\n                RenderText(pos, line, line_end, false);\n                line = line_end + 1;\n                line_rect.Min.y += line_height;\n                line_rect.Max.y += line_height;\n                pos.y += line_height;\n            }\n\n            // Count remaining lines\n            int lines_skipped = 0;\n            while (line < text_end)\n            {\n                const char* line_end = (const char*)ImMemchr(line, '\\n', text_end - line);\n                if (!line_end)\n                    line_end = text_end;\n                if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)\n                    text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);\n                line = line_end + 1;\n                lines_skipped++;\n            }\n            pos.y += lines_skipped * line_height;\n        }\n        text_size.y = (pos - text_pos).y;\n\n        ImRect bb(text_pos, text_pos + text_size);\n        ItemSize(text_size, 0.0f);\n        ItemAdd(bb, 0);\n    }\n}\n\nvoid ImGui::TextUnformatted(const char* text, const char* text_end)\n{\n    TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);\n}\n\nvoid ImGui::Text(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    TextV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::TextV(const char* fmt, va_list args)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    const char* text, *text_end;\n    ImFormatStringToTempBufferV(&text, &text_end, fmt, args);\n    TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);\n}\n\nvoid ImGui::TextColored(const ImVec4& col, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    TextColoredV(col, fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)\n{\n    PushStyleColor(ImGuiCol_Text, col);\n    TextV(fmt, args);\n    PopStyleColor();\n}\n\nvoid ImGui::TextDisabled(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    TextDisabledV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::TextDisabledV(const char* fmt, va_list args)\n{\n    ImGuiContext& g = *GImGui;\n    PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);\n    TextV(fmt, args);\n    PopStyleColor();\n}\n\nvoid ImGui::TextWrapped(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    TextWrappedV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::TextWrappedV(const char* fmt, va_list args)\n{\n    ImGuiContext& g = *GImGui;\n    const bool need_backup = (g.CurrentWindow->DC.TextWrapPos < 0.0f);  // Keep existing wrap position if one is already set\n    if (need_backup)\n        PushTextWrapPos(0.0f);\n    TextV(fmt, args);\n    if (need_backup)\n        PopTextWrapPos();\n}\n\nvoid ImGui::TextAligned(float align_x, float size_x, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    TextAlignedV(align_x, size_x, fmt, args);\n    va_end(args);\n}\n\n// align_x: 0.0f = left, 0.5f = center, 1.0f = right.\n// size_x : 0.0f = shortcut for GetContentRegionAvail().x\n// FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)\nvoid ImGui::TextAlignedV(float align_x, float size_x, const char* fmt, va_list args)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    const char* text, *text_end;\n    ImFormatStringToTempBufferV(&text, &text_end, fmt, args);\n    const ImVec2 text_size = CalcTextSize(text, text_end);\n    size_x = CalcItemSize(ImVec2(size_x, 0.0f), 0.0f, text_size.y).x;\n\n    ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);\n    ImVec2 pos_max(pos.x + size_x, window->ClipRect.Max.y);\n    ImVec2 size(ImMin(size_x, text_size.x), text_size.y);\n    window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, pos.x + text_size.x);\n    window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, pos.x + text_size.x);\n    if (align_x > 0.0f && text_size.x < size_x)\n        pos.x += ImTrunc((size_x - text_size.x) * align_x);\n    RenderTextEllipsis(window->DrawList, pos, pos_max, pos_max.x, text, text_end, &text_size);\n\n    const ImVec2 backup_max_pos = window->DC.CursorMaxPos;\n    ItemSize(size);\n    ItemAdd(ImRect(pos, pos + size), 0);\n    window->DC.CursorMaxPos.x = backup_max_pos.x; // Cancel out extending content size because right-aligned text would otherwise mess it up.\n\n    if (size_x < text_size.x && IsItemHovered(ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_ForTooltip))\n        SetTooltip(\"%.*s\", (int)(text_end - text), text);\n}\n\nvoid ImGui::LabelText(const char* label, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    LabelTextV(label, fmt, args);\n    va_end(args);\n}\n\n// Add a label+text combo aligned to other label+value widgets\nvoid ImGui::LabelTextV(const char* label, const char* fmt, va_list args)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const float w = CalcItemWidth();\n\n    const char* value_text_begin, *value_text_end;\n    ImFormatStringToTempBufferV(&value_text_begin, &value_text_end, fmt, args);\n    const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    const ImVec2 pos = window->DC.CursorPos;\n    const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2));\n    const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2));\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, 0))\n        return;\n\n    // Render\n    RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f));\n    if (label_size.x > 0.0f)\n        RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);\n}\n\nvoid ImGui::BulletText(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    BulletTextV(fmt, args);\n    va_end(args);\n}\n\n// Text with a little bullet aligned to the typical tree node.\nvoid ImGui::BulletTextV(const char* fmt, va_list args)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n\n    const char* text_begin, *text_end;\n    ImFormatStringToTempBufferV(&text_begin, &text_end, fmt, args);\n    const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);\n    const ImVec2 total_size = ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y);  // Empty text doesn't add padding\n    ImVec2 pos = window->DC.CursorPos;\n    pos.y += window->DC.CurrLineTextBaseOffset;\n    ItemSize(total_size, 0.0f);\n    const ImRect bb(pos, pos + total_size);\n    if (!ItemAdd(bb, 0))\n        return;\n\n    // Render\n    ImU32 text_col = GetColorU32(ImGuiCol_Text);\n    RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, g.FontSize * 0.5f), text_col);\n    RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0f), text_begin, text_end, false);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Main\n//-------------------------------------------------------------------------\n// - ButtonBehavior() [Internal]\n// - Button()\n// - SmallButton()\n// - InvisibleButton()\n// - ArrowButton()\n// - CloseButton() [Internal]\n// - CollapseButton() [Internal]\n// - GetWindowScrollbarID() [Internal]\n// - GetWindowScrollbarRect() [Internal]\n// - Scrollbar() [Internal]\n// - ScrollbarEx() [Internal]\n// - Image()\n// - ImageButton()\n// - Checkbox()\n// - CheckboxFlagsT() [Internal]\n// - CheckboxFlags()\n// - RadioButton()\n// - ProgressBar()\n// - Bullet()\n// - Hyperlink()\n//-------------------------------------------------------------------------\n\n// The ButtonBehavior() function is key to many interactions and used by many/most widgets.\n// Because we handle so many cases (keyboard/gamepad navigation, drag and drop) and many specific behavior (via ImGuiButtonFlags_),\n// this code is a little complex.\n// By far the most common path is interacting with the Mouse using the default ImGuiButtonFlags_PressedOnClickRelease button behavior.\n// See the series of events below and the corresponding state reported by dear imgui:\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// with PressedOnClickRelease:             return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()\n//   Frame N+0 (mouse is outside bb)        -             -                -               -                  -                    -\n//   Frame N+1 (mouse moves inside bb)      -             true             -               -                  -                    -\n//   Frame N+2 (mouse button is down)       -             true             true            true               -                    true\n//   Frame N+3 (mouse button is down)       -             true             true            -                  -                    -\n//   Frame N+4 (mouse moves outside bb)     -             -                true            -                  -                    -\n//   Frame N+5 (mouse moves inside bb)      -             true             true            -                  -                    -\n//   Frame N+6 (mouse button is released)   true          true             -               -                  true                 -\n//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -\n//   Frame N+8 (mouse moves outside bb)     -             -                -               -                  -                    -\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// with PressedOnClick:                    return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()\n//   Frame N+2 (mouse button is down)       true          true             true            true               -                    true\n//   Frame N+3 (mouse button is down)       -             true             true            -                  -                    -\n//   Frame N+6 (mouse button is released)   -             true             -               -                  true                 -\n//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// with PressedOnRelease:                  return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()\n//   Frame N+2 (mouse button is down)       -             true             -               -                  -                    true\n//   Frame N+3 (mouse button is down)       -             true             -               -                  -                    -\n//   Frame N+6 (mouse button is released)   true          true             -               -                  -                    -\n//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// with PressedOnDoubleClick:              return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()\n//   Frame N+0 (mouse button is down)       -             true             -               -                  -                    true\n//   Frame N+1 (mouse button is down)       -             true             -               -                  -                    -\n//   Frame N+2 (mouse button is released)   -             true             -               -                  -                    -\n//   Frame N+3 (mouse button is released)   -             true             -               -                  -                    -\n//   Frame N+4 (mouse button is down)       true          true             true            true               -                    true\n//   Frame N+5 (mouse button is down)       -             true             true            -                  -                    -\n//   Frame N+6 (mouse button is released)   -             true             -               -                  true                 -\n//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// Note that some combinations are supported,\n// - PressedOnDragDropHold can generally be associated with any flag.\n// - PressedOnDoubleClick can be associated by PressedOnClickRelease/PressedOnRelease, in which case the second release event won't be reported.\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// The behavior of the return-value changes when ImGuiItemFlags_ButtonRepeat is set:\n//                                         Repeat+                  Repeat+           Repeat+             Repeat+\n//                                         PressedOnClickRelease    PressedOnClick    PressedOnRelease    PressedOnDoubleClick\n//-------------------------------------------------------------------------------------------------------------------------------------------------\n//   Frame N+0 (mouse button is down)       -                        true              -                   true\n//   ...                                    -                        -                 -                   -\n//   Frame N + RepeatDelay                  true                     true              -                   true\n//   ...                                    -                        -                 -                   -\n//   Frame N + RepeatDelay + RepeatRate*N   true                     true              -                   true\n//-------------------------------------------------------------------------------------------------------------------------------------------------\n\n// - FIXME: For refactor we could output flags, incl mouse hovered vs nav keyboard vs nav triggered etc.\n//   And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);'\n//   For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading.\n// - Since v1.91.2 (Sept 2024) we included io.ConfigDebugHighlightIdConflicts feature.\n//   One idiom which was previously valid which will now emit a warning is when using multiple overlaid ButtonBehavior()\n//   with same ID and different MouseButton (see #8030). You can fix it by:\n//       (1) switching to use a single ButtonBehavior() with multiple _MouseButton flags.\n//    or (2) surrounding those calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()\nbool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n\n    // Default behavior inherited from item flags\n    // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that.\n    ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags);\n    if (flags & ImGuiButtonFlags_AllowOverlap)\n        item_flags |= ImGuiItemFlags_AllowOverlap;\n    if (item_flags & ImGuiItemFlags_NoFocus)\n        flags |= ImGuiButtonFlags_NoFocus | ImGuiButtonFlags_NoNavFocus;\n\n    // Default only reacts to left mouse button\n    if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0)\n        flags |= ImGuiButtonFlags_MouseButtonLeft;\n\n    // Default behavior requires click + release inside bounding box\n    if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0)\n        flags |= (item_flags & ImGuiItemFlags_ButtonRepeat) ? ImGuiButtonFlags_PressedOnClick : ImGuiButtonFlags_PressedOnDefault_;\n\n    ImGuiWindow* backup_hovered_window = g.HoveredWindow;\n    const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindowDockTree == window->RootWindowDockTree;\n    if (flatten_hovered_children)\n        g.HoveredWindow = window;\n\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    // Alternate registration spot, for when caller didn't use ItemAdd()\n    if (g.LastItemData.ID != id)\n        IMGUI_TEST_ENGINE_ITEM_ADD(id, bb, NULL);\n#endif\n\n    bool pressed = false;\n    bool hovered = ItemHoverable(bb, id, item_flags);\n\n    // Special mode for Drag and Drop used by openables (tree nodes, tabs etc.)\n    // where holding the button pressed for a long time while drag a payload item triggers the button.\n    if (g.DragDropActive)\n    {\n        if ((flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))\n        {\n            hovered = true;\n            SetHoveredID(id);\n            if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER)\n            {\n                pressed = true;\n                g.DragDropHoldJustPressedId = id;\n                FocusWindow(window);\n            }\n        }\n        if (g.DragDropAcceptIdPrev == id && (g.DragDropAcceptFlagsPrev & ImGuiDragDropFlags_AcceptDrawAsHovered))\n            hovered = true;\n    }\n\n    if (flatten_hovered_children)\n        g.HoveredWindow = backup_hovered_window;\n\n    // Mouse handling\n    const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id;\n    if (hovered)\n    {\n        IM_ASSERT(id != 0); // Lazily check inside rare path.\n\n        // Poll mouse buttons\n        // - 'mouse_button_clicked' is generally carried into ActiveIdMouseButton when setting ActiveId.\n        // - Technically we only need some values in one code path, but since this is gated by hovered test this is fine.\n        int mouse_button_clicked = -1;\n        int mouse_button_released = -1;\n        for (int button = 0; button < 3; button++)\n            if (flags & (ImGuiButtonFlags_MouseButtonLeft << button)) // Handle ImGuiButtonFlags_MouseButtonRight and ImGuiButtonFlags_MouseButtonMiddle here.\n            {\n                if (IsMouseClicked(button, ImGuiInputFlags_None, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; }\n                if (IsMouseReleased(button, test_owner_id) && mouse_button_released == -1) { mouse_button_released = button; }\n            }\n\n        // Process initial action\n        const bool mods_ok = !(flags & ImGuiButtonFlags_NoKeyModsAllowed) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt);\n        if (mods_ok)\n        {\n            if (mouse_button_clicked != -1 && g.ActiveId != id)\n            {\n                if (!(flags & ImGuiButtonFlags_NoSetKeyOwner))\n                    SetKeyOwner(MouseButtonToKey(mouse_button_clicked), id);\n                if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere))\n                {\n                    SetActiveID(id, window);\n                    g.ActiveIdMouseButton = (ImS8)mouse_button_clicked;\n                    if (!(flags & ImGuiButtonFlags_NoNavFocus))\n                    {\n                        SetFocusID(id, window);\n                        FocusWindow(window);\n                    }\n                    else if (!(flags & ImGuiButtonFlags_NoFocus))\n                    {\n                        FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child\n                    }\n                }\n                if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2))\n                {\n                    pressed = true;\n                    if (flags & ImGuiButtonFlags_NoHoldingActiveId)\n                        ClearActiveID();\n                    else\n                        SetActiveID(id, window); // Hold on ID\n                    g.ActiveIdMouseButton = (ImS8)mouse_button_clicked;\n                    if (!(flags & ImGuiButtonFlags_NoNavFocus))\n                    {\n                        SetFocusID(id, window);\n                        FocusWindow(window);\n                    }\n                    else if (!(flags & ImGuiButtonFlags_NoFocus))\n                    {\n                        FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child\n                    }\n                }\n            }\n            if (flags & ImGuiButtonFlags_PressedOnRelease)\n            {\n                if (mouse_button_released != -1)\n                {\n                    const bool has_repeated_at_least_once = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior\n                    if (!has_repeated_at_least_once)\n                        pressed = true;\n                    if (!(flags & ImGuiButtonFlags_NoNavFocus))\n                        SetFocusID(id, window); // FIXME: Lack of FocusWindow() call here is inconsistent with other paths. Research why.\n                    ClearActiveID();\n                }\n            }\n\n            // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).\n            // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.\n            if (g.ActiveId == id && (item_flags & ImGuiItemFlags_ButtonRepeat))\n                if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, ImGuiInputFlags_Repeat, test_owner_id))\n                    pressed = true;\n        }\n\n        if (pressed && g.IO.ConfigNavCursorVisibleAuto)\n            g.NavCursorVisible = false;\n    }\n\n    // Keyboard/Gamepad navigation handling\n    // We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse.\n    if ((item_flags & ImGuiItemFlags_Disabled) == 0)\n    {\n        if (g.NavId == id && g.NavCursorVisible && g.NavHighlightItemUnderNav)\n            if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus))\n                hovered = true;\n        if (g.NavActivateDownId == id)\n        {\n            bool nav_activated_by_code = (g.NavActivateId == id);\n            bool nav_activated_by_inputs = (g.NavActivatePressedId == id);\n            if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat))\n            {\n                // Avoid pressing multiple keys from triggering excessive amount of repeat events\n                const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space);\n                const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_Enter);\n                const ImGuiKeyData* key3 = GetKeyData(ImGuiKey_NavGamepadActivate);\n                const float t1 = ImMax(ImMax(key1->DownDuration, key2->DownDuration), key3->DownDuration);\n                nav_activated_by_inputs = CalcTypematicRepeatAmount(t1 - g.IO.DeltaTime, t1, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;\n            }\n            if (nav_activated_by_code || nav_activated_by_inputs)\n            {\n                // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button.\n                pressed = true;\n                SetActiveID(id, window);\n                g.ActiveIdSource = g.NavInputSource;\n                if (!(flags & ImGuiButtonFlags_NoNavFocus) && !(g.NavActivateFlags & ImGuiActivateFlags_FromShortcut))\n                    SetFocusID(id, window);\n                if (g.NavActivateFlags & ImGuiActivateFlags_FromShortcut)\n                    g.ActiveIdFromShortcut = true;\n            }\n        }\n    }\n\n    // Process while held\n    bool held = false;\n    if (g.ActiveId == id)\n    {\n        if (g.ActiveIdSource == ImGuiInputSource_Mouse)\n        {\n            if (g.ActiveIdIsJustActivated)\n                g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;\n\n            const int mouse_button = g.ActiveIdMouseButton;\n            if (mouse_button == -1)\n            {\n                // Fallback for the rare situation were g.ActiveId was set programmatically or from another widget (e.g. #6304).\n                ClearActiveID();\n            }\n            else if (IsMouseDown(mouse_button, test_owner_id))\n            {\n                held = true;\n            }\n            else\n            {\n                bool release_in = hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease) != 0;\n                bool release_anywhere = (flags & ImGuiButtonFlags_PressedOnClickReleaseAnywhere) != 0;\n                if ((release_in || release_anywhere) && !g.DragDropActive)\n                {\n                    // Report as pressed when releasing the mouse (this is the most common path)\n                    bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2;\n                    bool is_repeating_already = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps <on release>\n                    bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id);\n                    if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned)\n                        pressed = true;\n                }\n                ClearActiveID();\n            }\n            if (!(flags & ImGuiButtonFlags_NoNavFocus) && g.IO.ConfigNavCursorVisibleAuto)\n                g.NavCursorVisible = false;\n        }\n        else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)\n        {\n            // When activated using Nav, we hold on the ActiveID until activation button is released\n            if (g.NavActivateDownId == id)\n                held = true; // hovered == true not true as we are already likely hovered on direct activation.\n            else\n                ClearActiveID();\n        }\n        if (pressed)\n            g.ActiveIdHasBeenPressedBefore = true;\n    }\n\n    // Activation highlight (this may be a remote activation)\n    if (g.NavHighlightActivatedId == id && (item_flags & ImGuiItemFlags_Disabled) == 0)\n        hovered = true;\n\n    if (out_hovered) *out_hovered = hovered;\n    if (out_held) *out_held = held;\n\n    return pressed;\n}\n\nbool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    ImVec2 pos = window->DC.CursorPos;\n    if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)\n        pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y;\n    ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);\n\n    const ImRect bb(pos, pos + size);\n    ItemSize(size, style.FramePadding.y);\n    if (!ItemAdd(bb, id))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n    // Render\n    const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n    RenderNavCursor(bb, id);\n    RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);\n\n    if (g.LogEnabled)\n        LogSetNextTextDecoration(\"[\", \"]\");\n    RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);\n\n    // Automatically close popups\n    //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))\n    //    CloseCurrentPopup();\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n    return pressed;\n}\n\nbool ImGui::Button(const char* label, const ImVec2& size_arg)\n{\n    return ButtonEx(label, size_arg, ImGuiButtonFlags_None);\n}\n\n// Small buttons fits within text without additional vertical spacing.\nbool ImGui::SmallButton(const char* label)\n{\n    ImGuiContext& g = *GImGui;\n    float backup_padding_y = g.Style.FramePadding.y;\n    g.Style.FramePadding.y = 0.0f;\n    bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine);\n    g.Style.FramePadding.y = backup_padding_y;\n    return pressed;\n}\n\n// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.\n// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)\nbool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    // Ensure zero-size fits to contents\n    ImVec2 size = CalcItemSize(ImVec2(size_arg.x != 0.0f ? size_arg.x : -FLT_MIN, size_arg.y != 0.0f ? size_arg.y : -FLT_MIN), 0.0f, 0.0f);\n\n    const ImGuiID id = window->GetID(str_id);\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);\n    ItemSize(size);\n    if (!ItemAdd(bb, id, NULL, (flags & ImGuiButtonFlags_EnableNav) ? ImGuiItemFlags_None : ImGuiItemFlags_NoNav))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n    RenderNavCursor(bb, id);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);\n    return pressed;\n}\n\nbool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    const ImGuiID id = window->GetID(str_id);\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);\n    const float default_size = GetFrameHeight();\n    ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : -1.0f);\n    if (!ItemAdd(bb, id))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n    // Render\n    const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n    const ImU32 text_col = GetColorU32(ImGuiCol_Text);\n    RenderNavCursor(bb, id);\n    RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding);\n    RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);\n    return pressed;\n}\n\nbool ImGui::ArrowButton(const char* str_id, ImGuiDir dir)\n{\n    float sz = GetFrameHeight();\n    return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), ImGuiButtonFlags_None);\n}\n\n// Button to close a window\nbool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825)\n    // This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible?\n    const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize));\n    ImRect bb_interact = bb;\n    const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea();\n    if (area_to_visible_ratio < 1.5f)\n        bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f));\n\n    // Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window.\n    // (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer).\n    bool is_clipped = !ItemAdd(bb_interact, id);\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb_interact, id, &hovered, &held);\n    if (is_clipped)\n        return pressed;\n\n    // Render\n    ImU32 bg_col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered);\n    if (hovered)\n        window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);\n    RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact);\n    const ImU32 cross_col = GetColorU32(ImGuiCol_Text);\n    const ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f);\n    const float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f;\n    const float cross_thickness = 1.0f * (float)(int)g.Style._MainScale; // FIXME-DPI\n    window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, cross_thickness);\n    window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, cross_thickness);\n\n    return pressed;\n}\n\n// The Collapse button also functions as a Dock Menu button.\nbool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize));\n    bool is_clipped = !ItemAdd(bb, id);\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);\n    if (is_clipped)\n        return pressed;\n\n    // Render\n    //bool is_dock_menu = (window->DockNodeAsHost && !window->Collapsed);\n    ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n    ImU32 text_col = GetColorU32(ImGuiCol_Text);\n    if (hovered || held)\n        window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);\n    RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact);\n\n    if (dock_node)\n        RenderArrowDockMenu(window->DrawList, bb.Min, g.FontSize, text_col);\n    else\n        RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);\n\n    // Switch to moving the window after mouse is moved beyond the initial drag threshold\n    if (IsItemActive() && IsMouseDragging(0))\n        StartMouseMovingWindowOrNode(window, dock_node, true); // Undock from window/collapse menu button\n\n    return pressed;\n}\n\nImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis)\n{\n    return window->GetID(axis == ImGuiAxis_X ? \"#SCROLLX\" : \"#SCROLLY\");\n}\n\n// Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set.\nImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis)\n{\n    ImGuiContext& g = *GImGui;\n    const ImRect outer_rect = window->Rect();\n    const ImRect inner_rect = window->InnerRect;\n    const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar)\n    IM_ASSERT(scrollbar_size >= 0.0f);\n    const float border_size = IM_ROUND(window->WindowBorderSize * 0.5f);\n    const float border_top = (window->Flags & ImGuiWindowFlags_MenuBar) ? IM_ROUND(g.Style.FrameBorderSize * 0.5f) : 0.0f;\n    if (axis == ImGuiAxis_X)\n        return ImRect(inner_rect.Min.x + border_size, ImMax(outer_rect.Min.y + border_size, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size);\n    else\n        return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y + border_top, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size);\n}\n\nvoid ImGui::ExtendHitBoxWhenNearViewportEdge(ImGuiWindow* window, ImRect* bb, float threshold, ImGuiAxis axis)\n{\n    ImRect window_rect = window->RootWindow->Rect();\n    ImRect viewport_rect = window->Viewport->GetMainRect();\n    if (window_rect.Min[axis] == viewport_rect.Min[axis] && bb->Min[axis] > window_rect.Min[axis] && bb->Min[axis] - threshold <= window_rect.Min[axis])\n        bb->Min[axis] = window_rect.Min[axis];\n    if (window_rect.Max[axis] == viewport_rect.Max[axis] && bb->Max[axis] < window_rect.Max[axis] && bb->Max[axis] + threshold >= window_rect.Max[axis])\n        bb->Max[axis] = window_rect.Max[axis];\n}\n\nvoid ImGui::Scrollbar(ImGuiAxis axis)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    const ImGuiID id = GetWindowScrollbarID(window, axis);\n\n    // Calculate scrollbar bounding box\n    ImRect bb = GetWindowScrollbarRect(window, axis);\n    ImRect host_rect = (window->DockIsActive ? window->DockNode->HostWindow : window)->Rect();\n    ImDrawFlags rounding_corners = CalcRoundingFlagsForRectInRect(bb, host_rect, g.Style.WindowBorderSize);\n    float size_visible = window->InnerRect.Max[axis] - window->InnerRect.Min[axis];\n    float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f;\n    ImS64 scroll = (ImS64)window->Scroll[axis];\n    ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_visible, (ImS64)size_contents, rounding_corners);\n    window->Scroll[axis] = (float)scroll;\n}\n\n// Vertical/Horizontal scrollbar\n// The entire piece of code below is rather confusing because:\n// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)\n// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar\n// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.\n// Still, the code should probably be made simpler..\nbool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_visible_v, ImS64 size_contents_v, ImDrawFlags draw_rounding_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    const float bb_frame_width = bb_frame.GetWidth();\n    const float bb_frame_height = bb_frame.GetHeight();\n    if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f)\n        return false;\n\n    // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the window resize grab)\n    float alpha = 1.0f;\n    if ((axis == ImGuiAxis_Y) && bb_frame_height < bb_frame_width)\n        alpha = ImSaturate(bb_frame_height / ImMax(bb_frame_width * 2.0f, 1.0f));\n    if (alpha <= 0.0f)\n        return false;\n\n    const ImGuiStyle& style = g.Style;\n    const bool allow_interaction = (alpha >= 1.0f);\n\n    ImRect bb = bb_frame;\n    float padding = IM_TRUNC(ImMin(style.ScrollbarPadding, ImMin(bb_frame_width, bb_frame_height) * 0.5f));\n    bb.Expand(-padding);\n\n    // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)\n    const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight();\n    if (scrollbar_size_v < 1.0f)\n        return false;\n\n    // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)\n    // But we maintain a minimum size in pixel to allow for the user to still aim inside.\n    IM_ASSERT(ImMax(size_contents_v, size_visible_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers.\n    const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_visible_v), (ImS64)1);\n    const float grab_h_minsize = ImMin(bb.GetSize()[axis], style.GrabMinSize);\n    const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_visible_v / (float)win_size_v), grab_h_minsize, scrollbar_size_v);\n    const float grab_h_norm = grab_h_pixels / scrollbar_size_v;\n\n    // As a special thing, we allow scrollbar near the edge of a screen/viewport to be reachable with mouse at the extreme edge (#9276)\n    ImRect bb_hit = bb_frame;\n    ExtendHitBoxWhenNearViewportEdge(window, &bb_hit, g.Style.WindowBorderSize, (ImGuiAxis)(axis ^ 1));\n\n    // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().\n    bool held = false;\n    bool hovered = false;\n    ItemAdd(bb_frame, id, NULL, ImGuiItemFlags_NoNav);\n    ButtonBehavior(bb_hit, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus);\n\n    const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_visible_v);\n    float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max);\n    float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space\n    if (held && allow_interaction && grab_h_norm < 1.0f)\n    {\n        const float scrollbar_pos_v = bb.Min[axis];\n        const float mouse_pos_v = g.IO.MousePos[axis];\n\n        // Click position in scrollbar normalized space (0.0f->1.0f)\n        const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);\n\n        const int held_dir = (clicked_v_norm < grab_v_norm) ? -1 : (clicked_v_norm > grab_v_norm + grab_h_norm) ? +1 : 0;\n        if (g.ActiveIdIsJustActivated)\n        {\n            // On initial click when held_dir == 0 (clicked over grab): calculate the distance between mouse and the center of the grab\n            const bool scroll_to_clicked_location = (g.IO.ConfigScrollbarScrollByPage == false || g.IO.KeyShift || held_dir == 0);\n            g.ScrollbarSeekMode = scroll_to_clicked_location ? 0 : (short)held_dir;\n            g.ScrollbarClickDeltaToGrabCenter = (held_dir == 0 && !g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f;\n        }\n\n        // Apply scroll (p_scroll_v will generally point on one member of window->Scroll)\n        // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position\n        if (g.ScrollbarSeekMode == 0)\n        {\n            // Absolute seeking\n            const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm));\n            *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max);\n        }\n        else\n        {\n            // Page by page\n            if (IsMouseClicked(ImGuiMouseButton_Left, ImGuiInputFlags_Repeat) && held_dir == g.ScrollbarSeekMode)\n            {\n                float page_dir = (g.ScrollbarSeekMode > 0.0f) ? +1.0f : -1.0f;\n                *p_scroll_v = ImClamp(*p_scroll_v + (ImS64)(page_dir * size_visible_v), (ImS64)0, scroll_max);\n            }\n        }\n\n        // Update values for rendering\n        scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max);\n        grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;\n\n        // Update distance to grab now that we have seek'ed and saturated\n        //if (seek_absolute)\n        //    g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f;\n    }\n\n    // Render\n    const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg);\n    const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha);\n    window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, draw_rounding_flags);\n    ImRect grab_rect;\n    if (axis == ImGuiAxis_X)\n        grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y);\n    else\n        grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels);\n    window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);\n\n    return held;\n}\n\n// - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples\n// - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.\nvoid ImGui::ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    const ImVec2 padding(g.Style.ImageBorderSize, g.Style.ImageBorderSize);\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f);\n    ItemSize(bb);\n    if (!ItemAdd(bb, 0))\n        return;\n\n    // Render\n    float rounding = g.Style.ImageRounding;\n    if (bg_col.w > 0.0f)\n        window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col), rounding);\n    if (rounding > 0.0f)\n        window->DrawList->AddImageRounded(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col), rounding);\n    else\n        window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));\n    if (g.Style.ImageBorderSize > 0.0f)\n        window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), rounding, ImDrawFlags_None, g.Style.ImageBorderSize);\n}\n\nvoid ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1)\n{\n    ImageWithBg(tex_ref, image_size, uv0, uv1);\n}\n\n// 1.91.9 (February 2025) removed 'tint_col' and 'border_col' parameters, made border size not depend on color value. (#8131, #8238)\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\nvoid ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)\n{\n    ImGuiContext& g = *GImGui;\n    PushStyleVar(ImGuiStyleVar_ImageBorderSize, (border_col.w > 0.0f) ? ImMax(1.0f, g.Style.ImageBorderSize) : 0.0f); // Preserve legacy behavior where border is always visible when border_col's Alpha is >0.0f\n    PushStyleColor(ImGuiCol_Border, border_col);\n    ImageWithBg(tex_ref, image_size, uv0, uv1, ImVec4(0, 0, 0, 0), tint_col);\n    PopStyleColor();\n    PopStyleVar();\n}\n#endif\n\nbool ImGui::ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    const ImVec2 padding = g.Style.FramePadding;\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f);\n    ItemSize(bb);\n    if (!ItemAdd(bb, id))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n    // Render\n    const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n    RenderNavCursor(bb, id);\n    RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding);\n    if (bg_col.w > 0.0f)\n        window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));\n    float image_rounding = ImMax(g.Style.FrameRounding - ImMax(padding.x, padding.y), g.Style.ImageRounding);\n    if (image_rounding > 0.0f)\n        window->DrawList->AddImageRounded(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col), image_rounding);\n    else\n        window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));\n\n    return pressed;\n}\n\n// - ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.\n// - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified. (#8165) // FIXME: Maybe that's not the best design?\nbool ImGui::ImageButton(const char* str_id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    return ImageButtonEx(window->GetID(str_id), tex_ref, image_size, uv0, uv1, bg_col, tint_col);\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n// Legacy API obsoleted in 1.89. Two differences with new ImageButton()\n// - old ImageButton() used ImTextureID as item id (created issue with multiple buttons with same image, transient texture id values, opaque computation of ID)\n// - new ImageButton() requires an explicit 'const char* str_id'\n// - old ImageButton() had frame_padding' override argument.\n// - new ImageButton() always use style.FramePadding.\n/*\nbool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)\n{\n    // Default to using texture ID as ID. User can still push string/integer prefixes.\n    PushID((ImTextureID)(intptr_t)user_texture_id);\n    if (frame_padding >= 0)\n        PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2((float)frame_padding, (float)frame_padding));\n    bool ret = ImageButton(\"\", user_texture_id, size, uv0, uv1, bg_col, tint_col);\n    if (frame_padding >= 0)\n        PopStyleVar();\n    PopID();\n    return ret;\n}\n*/\n#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n\nbool ImGui::Checkbox(const char* label, bool* v)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    const float square_sz = GetFrameHeight();\n    const ImVec2 pos = window->DC.CursorPos;\n    const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));\n    ItemSize(total_bb, style.FramePadding.y);\n    const bool is_visible = ItemAdd(total_bb, id);\n    const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;\n    if (!is_visible)\n        if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(total_bb)) // Extra layer of \"no logic clip\" for box-select support\n        {\n            IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));\n            return false;\n        }\n\n    // Range-Selection/Multi-selection support (header)\n    bool checked = *v;\n    if (is_multi_select)\n        MultiSelectItemHeader(id, &checked, NULL);\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);\n\n    // Range-Selection/Multi-selection support (footer)\n    if (is_multi_select)\n        MultiSelectItemFooter(id, &checked, &pressed);\n    else if (pressed)\n        checked = !checked;\n\n    if (*v != checked)\n    {\n        *v = checked;\n        pressed = true; // return value\n        MarkItemEdited(id);\n    }\n\n    const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));\n    const bool mixed_value = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0;\n    if (is_visible)\n    {\n        RenderNavCursor(total_bb, id);\n        RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);\n        ImU32 check_col = GetColorU32(ImGuiCol_CheckMark);\n        if (mixed_value)\n        {\n            // Undocumented tristate/mixed/indeterminate checkbox (#2644)\n            // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox)\n            ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)));\n            window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding);\n        }\n        else if (*v)\n        {\n            const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));\n            RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f);\n        }\n    }\n    const ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);\n    if (g.LogEnabled)\n        LogRenderedText(&label_pos, mixed_value ? \"[~]\" : *v ? \"[x]\" : \"[ ]\");\n    if (is_visible && label_size.x > 0.0f)\n        RenderText(label_pos, label);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));\n    return pressed;\n}\n\ntemplate<typename T>\nbool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value)\n{\n    bool all_on = (*flags & flags_value) == flags_value;\n    bool any_on = (*flags & flags_value) != 0;\n    bool pressed;\n    if (!all_on && any_on)\n    {\n        ImGuiContext& g = *GImGui;\n        g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue;\n        pressed = Checkbox(label, &all_on);\n    }\n    else\n    {\n        pressed = Checkbox(label, &all_on);\n\n    }\n    if (pressed)\n    {\n        if (all_on)\n            *flags |= flags_value;\n        else\n            *flags &= ~flags_value;\n    }\n    return pressed;\n}\n\nbool ImGui::CheckboxFlags(const char* label, int* flags, int flags_value)\n{\n    return CheckboxFlagsT(label, flags, flags_value);\n}\n\nbool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)\n{\n    return CheckboxFlagsT(label, flags, flags_value);\n}\n\nbool ImGui::CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value)\n{\n    return CheckboxFlagsT(label, flags, flags_value);\n}\n\nbool ImGui::CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value)\n{\n    return CheckboxFlagsT(label, flags, flags_value);\n}\n\nbool ImGui::RadioButton(const char* label, bool active)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    const float square_sz = GetFrameHeight();\n    const ImVec2 pos = window->DC.CursorPos;\n    const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));\n    const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, id))\n        return false;\n\n    ImVec2 center = check_bb.GetCenter();\n    center.x = IM_ROUND(center.x);\n    center.y = IM_ROUND(center.y);\n    const float radius = (square_sz - 1.0f) * 0.5f;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);\n    if (pressed)\n        MarkItemEdited(id);\n\n    RenderNavCursor(total_bb, id);\n    const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius);\n    window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment);\n    if (active)\n    {\n        const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));\n        window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark));\n    }\n\n    if (style.FrameBorderSize > 0.0f)\n    {\n        window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), num_segment, style.FrameBorderSize);\n        window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), num_segment, style.FrameBorderSize);\n    }\n\n    ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);\n    if (g.LogEnabled)\n        LogRenderedText(&label_pos, active ? \"(x)\" : \"( )\");\n    if (label_size.x > 0.0f)\n        RenderText(label_pos, label);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n    return pressed;\n}\n\n// FIXME: This would work nicely if it was a public template, e.g. 'template<T> RadioButton(const char* label, T* v, T v_button)', but I'm not sure how we would expose it..\nbool ImGui::RadioButton(const char* label, int* v, int v_button)\n{\n    const bool pressed = RadioButton(label, *v == v_button);\n    if (pressed)\n        *v = v_button;\n    return pressed;\n}\n\n// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size\nvoid ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n\n    ImVec2 pos = window->DC.CursorPos;\n    ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y * 2.0f);\n    ImRect bb(pos, pos + size);\n    ItemSize(size, style.FramePadding.y);\n    if (!ItemAdd(bb, 0))\n        return;\n\n    // Fraction < 0.0f will display an indeterminate progress bar animation\n    // The value must be animated along with time, so e.g. passing '-1.0f * ImGui::GetTime()' as fraction works.\n    const bool is_indeterminate = (fraction < 0.0f);\n    if (!is_indeterminate)\n        fraction = ImSaturate(fraction);\n\n    // Out of courtesy we accept a NaN fraction without crashing\n    float fill_n0 = 0.0f;\n    float fill_n1 = (fraction == fraction) ? fraction : 0.0f;\n\n    if (is_indeterminate)\n    {\n        const float fill_width_n = 0.2f;\n        fill_n0 = ImFmod(-fraction, 1.0f) * (1.0f + fill_width_n) - fill_width_n;\n        fill_n1 = ImSaturate(fill_n0 + fill_width_n);\n        fill_n0 = ImSaturate(fill_n0);\n    }\n\n    // Render\n    RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);\n    bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));\n    float fill_x0 = ImLerp(bb.Min.x, bb.Max.x, fill_n0);\n    float fill_x1 = ImLerp(bb.Min.x, bb.Max.x, fill_n1);\n    if (fill_x0 < fill_x1)\n        RenderRectFilledInRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_x0, fill_x1, style.FrameRounding);\n\n    // Default displaying the fraction as percentage string, but user can override it\n    // Don't display text for indeterminate bars by default\n    char overlay_buf[32];\n    if (!is_indeterminate || overlay != NULL)\n    {\n        if (!overlay)\n        {\n            ImFormatString(overlay_buf, IM_COUNTOF(overlay_buf), \"%.0f%%\", fraction * 100 + 0.01f);\n            overlay = overlay_buf;\n        }\n\n        ImVec2 overlay_size = CalcTextSize(overlay, NULL);\n        if (overlay_size.x > 0.0f)\n        {\n            float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : fill_x1 + style.ItemSpacing.x;\n            RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb);\n        }\n    }\n}\n\nvoid ImGui::Bullet()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), g.FontSize);\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));\n    ItemSize(bb);\n    if (!ItemAdd(bb, 0))\n    {\n        SameLine(0, style.FramePadding.x * 2);\n        return;\n    }\n\n    // Render and stay on same line\n    ImU32 text_col = GetColorU32(ImGuiCol_Text);\n    RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, line_height * 0.5f), text_col);\n    SameLine(0, style.FramePadding.x * 2.0f);\n}\n\n// This is provided as a convenience for being an often requested feature.\n// FIXME-STYLE: we delayed adding as there is a larger plan to revamp the styling system.\n// Because of this we currently don't provide many styling options for this widget\n// (e.g. hovered/active colors are automatically inferred from a single color).\nbool ImGui::TextLink(const char* label)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiID id = window->GetID(label);\n    const char* label_end = FindRenderedTextEnd(label);\n\n    ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);\n    ImVec2 size = CalcTextSize(label, label_end, true);\n    ImRect bb(pos, pos + size);\n    ItemSize(size, 0.0f);\n    if (!ItemAdd(bb, id))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held);\n    RenderNavCursor(bb, id);\n\n    if (hovered)\n        SetMouseCursor(ImGuiMouseCursor_Hand);\n\n    ImVec4 text_colf = g.Style.Colors[ImGuiCol_TextLink];\n    ImVec4 line_colf = text_colf;\n    {\n        // FIXME-STYLE: Read comments above. This widget is NOT written in the same style as some earlier widgets,\n        // as we are currently experimenting/planning a different styling system.\n        float h, s, v;\n        ColorConvertRGBtoHSV(text_colf.x, text_colf.y, text_colf.z, h, s, v);\n        if (held || hovered)\n        {\n            v = ImSaturate(v + (held ? 0.4f : 0.3f));\n            h = ImFmod(h + 0.02f, 1.0f);\n        }\n        ColorConvertHSVtoRGB(h, s, v, text_colf.x, text_colf.y, text_colf.z);\n        v = ImSaturate(v - 0.20f);\n        ColorConvertHSVtoRGB(h, s, v, line_colf.x, line_colf.y, line_colf.z);\n    }\n\n    float line_y = bb.Max.y + ImFloor(g.FontBaked->Descent * g.FontBakedScale * 0.20f);\n    window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf), 1.0f * (float)(int)g.Style._MainScale); // FIXME-TEXT: Underline mode // FIXME-DPI\n\n    PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf));\n    RenderText(bb.Min, label, label_end);\n    PopStyleColor();\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n    return pressed;\n}\n\nbool ImGui::TextLinkOpenURL(const char* label, const char* url)\n{\n    ImGuiContext& g = *GImGui;\n    if (url == NULL)\n        url = label;\n    bool pressed = TextLink(label);\n    if (pressed && g.PlatformIO.Platform_OpenInShellFn != NULL)\n        g.PlatformIO.Platform_OpenInShellFn(&g, url);\n    SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label\n    if (BeginPopupContextItem())\n    {\n        if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink)))\n            SetClipboardText(url);\n        EndPopup();\n    }\n    return pressed;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Low-level Layout helpers\n//-------------------------------------------------------------------------\n// - Spacing()\n// - Dummy()\n// - NewLine()\n// - AlignTextToFramePadding()\n// - SeparatorEx() [Internal]\n// - Separator()\n// - SplitterBehavior() [Internal]\n// - ShrinkWidths() [Internal]\n//-------------------------------------------------------------------------\n\nvoid ImGui::Spacing()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n    ItemSize(ImVec2(0, 0));\n}\n\nvoid ImGui::Dummy(const ImVec2& size)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);\n    ItemSize(size);\n    ItemAdd(bb, 0);\n}\n\nvoid ImGui::NewLine()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;\n    window->DC.LayoutType = ImGuiLayoutType_Vertical;\n    window->DC.IsSameLine = false;\n    if (window->DC.CurrLineSize.y > 0.0f)     // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.\n        ItemSize(ImVec2(0, 0));\n    else\n        ItemSize(ImVec2(0.0f, g.FontSize));\n    window->DC.LayoutType = backup_layout_type;\n}\n\nvoid ImGui::AlignTextToFramePadding()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    window->DC.CurrLineSize.y = ImMax(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y * 2);\n    window->DC.CurrLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, g.Style.FramePadding.y);\n}\n\n// Horizontal/vertical separating line\n// FIXME: Surprisingly, this seemingly trivial widget is a victim of many different legacy/tricky layout issues.\n// Note how thickness == 1.0f is handled specifically as not moving CursorPos by 'thickness', but other values are.\nvoid ImGui::SeparatorEx(ImGuiSeparatorFlags flags, float thickness)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)));   // Check that only 1 option is selected\n    IM_ASSERT(thickness > 0.0f);\n\n    if (flags & ImGuiSeparatorFlags_Vertical)\n    {\n        // Vertical separator, for menu bars (use current line height).\n        float y1 = window->DC.CursorPos.y;\n        float y2 = window->DC.CursorPos.y + window->DC.CurrLineSize.y;\n        const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + thickness, y2));\n        ItemSize(ImVec2(thickness, 0.0f));\n        if (!ItemAdd(bb, 0))\n            return;\n\n        // Draw\n        window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator));\n        if (g.LogEnabled)\n            LogText(\" |\");\n    }\n    else if (flags & ImGuiSeparatorFlags_Horizontal)\n    {\n        // Horizontal Separator\n        float x1 = window->DC.CursorPos.x;\n        float x2 = window->WorkRect.Max.x;\n\n        // Preserve legacy behavior inside Columns()\n        // Before Tables API happened, we relied on Separator() to span all columns of a Columns() set.\n        // We currently don't need to provide the same feature for tables because tables naturally have border features.\n        ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL;\n        if (columns)\n        {\n            x1 = window->Pos.x + window->DC.Indent.x; // Used to be Pos.x before 2023/10/03\n            x2 = window->Pos.x + window->Size.x;\n            PushColumnsBackground();\n        }\n\n        // We don't provide our width to the layout so that it doesn't get feed back into AutoFit\n        // FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell)\n\n        // Between 1.71 and 1.92.7, we maintained a hack where a 1.0f thin Separator() would not impact layout.\n        // This was mostly chosen to allow backward compatibility with user's code assuming zero-height when calculating height for layout (e.g. bottom alignment of a status bar).\n        // In order to handle scaling we need to scale separator thickness and it would not makes sense to have a disparity depending on height.\n        ////float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness; // FIXME: See 1.70/1.71 Separator() change: makes legacy 1-px separator not affect layout yet. Should change.\n        const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness));\n        ItemSize(ImVec2(0.0f, thickness));\n\n        if (ItemAdd(bb, 0))\n        {\n            // Draw\n            window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator));\n            if (g.LogEnabled)\n                LogRenderedText(&bb.Min, \"--------------------------------\\n\");\n\n        }\n        if (columns)\n        {\n            PopColumnsBackground();\n            columns->LineMinY = window->DC.CursorPos.y;\n        }\n    }\n}\n\nvoid ImGui::Separator()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    // Those flags should eventually be configurable by the user\n    ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;\n\n    // Only applies to legacy Columns() api as they relied on Separator() a lot.\n    if (window->DC.CurrentColumns)\n        flags |= ImGuiSeparatorFlags_SpanAllColumns;\n\n    SeparatorEx(flags, g.Style.SeparatorSize);\n}\n\nvoid ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_w)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiStyle& style = g.Style;\n\n    const ImVec2 label_size = CalcTextSize(label, label_end, false);\n    const ImVec2 pos = window->DC.CursorPos;\n    const ImVec2 padding = style.SeparatorTextPadding;\n\n    const float separator_thickness = style.SeparatorTextBorderSize;\n    const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness));\n    const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y));\n    const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f));\n    ItemSize(min_size, text_baseline_y);\n    if (!ItemAdd(bb, id))\n        return;\n\n    const float sep1_x1 = pos.x;\n    const float sep2_x2 = bb.Max.x;\n    const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f);\n\n    const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f);\n    const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN\n\n    // This allows using SameLine() to position something in the 'extra_w'\n    window->DC.CursorPosPrevLine.x = label_pos.x + label_size.x;\n\n    const ImU32 separator_col = GetColorU32(ImGuiCol_Separator);\n    if (label_size.x > 0.0f)\n    {\n        const float sep1_x2 = label_pos.x - style.ItemSpacing.x;\n        const float sep2_x1 = label_pos.x + label_size.x + extra_w + style.ItemSpacing.x;\n        if (sep1_x2 > sep1_x1 && separator_thickness > 0.0f)\n            window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep1_x2, seps_y), separator_col, separator_thickness);\n        if (sep2_x2 > sep2_x1 && separator_thickness > 0.0f)\n            window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);\n        if (g.LogEnabled)\n            LogSetNextTextDecoration(\"---\", NULL);\n        RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, label, label_end, &label_size);\n    }\n    else\n    {\n        if (g.LogEnabled)\n            LogText(\"---\");\n        if (separator_thickness > 0.0f)\n            window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);\n    }\n}\n\nvoid ImGui::SeparatorText(const char* label)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    // The SeparatorText() vs SeparatorTextEx() distinction is designed to be considerate that we may want:\n    // - allow separator-text to be draggable items (would require a stable ID + a noticeable highlight)\n    // - this high-level entry point to allow formatting? (which in turns may require ID separate from formatted string)\n    // - because of this we probably can't turn 'const char* label' into 'const char* fmt, ...'\n    // Otherwise, we can decide that users wanting to drag this would layout a dedicated drag-item,\n    // and then we can turn this into a format function.\n    SeparatorTextEx(0, label, FindRenderedTextEnd(label), 0.0f);\n}\n\n// Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise.\nbool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay, ImU32 bg_col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav))\n        return false;\n\n    // FIXME: AFAIK the only leftover reason for passing ImGuiButtonFlags_AllowOverlap here is\n    // to allow caller of SplitterBehavior() to call SetItemAllowOverlap() after the item.\n    // Nowadays we would instead want to use SetNextItemAllowOverlap() before the item.\n    ImGuiButtonFlags button_flags = ImGuiButtonFlags_FlattenChildren;\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    button_flags |= ImGuiButtonFlags_AllowOverlap;\n#endif\n\n    bool hovered, held;\n    ImRect bb_interact = bb;\n    bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));\n    ButtonBehavior(bb_interact, id, &hovered, &held, button_flags);\n    if (hovered)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb\n\n    if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay))\n        SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);\n\n    ImRect bb_render = bb;\n    if (held)\n    {\n        float mouse_delta = (g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min)[axis];\n\n        // Minimum pane size\n        float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1);\n        float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2);\n        if (mouse_delta < -size_1_maximum_delta)\n            mouse_delta = -size_1_maximum_delta;\n        if (mouse_delta > size_2_maximum_delta)\n            mouse_delta = size_2_maximum_delta;\n\n        // Apply resize\n        if (mouse_delta != 0.0f)\n        {\n            *size1 = ImMax(*size1 + mouse_delta, min_size1);\n            *size2 = ImMax(*size2 - mouse_delta, min_size2);\n            bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));\n            MarkItemEdited(id);\n        }\n    }\n\n    // Render at new position\n    if (bg_col & IM_COL32_A_MASK)\n        window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0f);\n    const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);\n    window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0f);\n\n    return held;\n}\n\nstatic int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs)\n{\n    const ImGuiShrinkWidthItem* a = (const ImGuiShrinkWidthItem*)lhs;\n    const ImGuiShrinkWidthItem* b = (const ImGuiShrinkWidthItem*)rhs;\n    if (int d = (int)(b->Width - a->Width))\n        return d;\n    return b->Index - a->Index;\n}\n\n// Shrink excess width from a set of item, by removing width from the larger items first.\n// Set items Width to -1.0f to disable shrinking this item.\nvoid ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess, float width_min)\n{\n    if (count == 1)\n    {\n        if (items[0].Width >= 0.0f)\n            items[0].Width = ImMax(items[0].Width - width_excess, width_min);\n        return;\n    }\n    ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer); // Sort largest first, smallest last.\n    int count_same_width = 1;\n    while (width_excess > 0.001f && count_same_width < count)\n    {\n        while (count_same_width < count && items[0].Width <= items[count_same_width].Width)\n            count_same_width++;\n        float max_width_to_remove_per_item = (count_same_width < count && items[count_same_width].Width >= 0.0f) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f);\n        max_width_to_remove_per_item = ImMin(items[0].Width - width_min, max_width_to_remove_per_item);\n        if (max_width_to_remove_per_item <= 0.0f)\n            break;\n        float base_width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item);\n        for (int item_n = 0; item_n < count_same_width; item_n++)\n        {\n            float width_to_remove_for_this_item = ImMin(base_width_to_remove_per_item, items[item_n].Width - width_min);\n            items[item_n].Width -= width_to_remove_for_this_item;\n            width_excess -= width_to_remove_for_this_item;\n        }\n    }\n\n    // Round width and redistribute remainder\n    // Ensure that e.g. the right-most tab of a shrunk tab-bar always reaches exactly at the same distance from the right-most edge of the tab bar separator.\n    width_excess = 0.0f;\n    for (int n = 0; n < count; n++)\n    {\n        float width_rounded = ImTrunc(items[n].Width);\n        width_excess += items[n].Width - width_rounded;\n        items[n].Width = width_rounded;\n    }\n    while (width_excess > 0.0f)\n        for (int n = 0; n < count && width_excess > 0.0f; n++)\n        {\n            float width_to_add = ImMin(items[n].InitialWidth - items[n].Width, 1.0f);\n            items[n].Width += width_to_add;\n            width_excess -= width_to_add;\n        }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: ComboBox\n//-------------------------------------------------------------------------\n// - CalcMaxPopupHeightFromItemCount() [Internal]\n// - BeginCombo()\n// - BeginComboPopup() [Internal]\n// - EndCombo()\n// - BeginComboPreview() [Internal]\n// - EndComboPreview() [Internal]\n// - Combo()\n//-------------------------------------------------------------------------\n\nstatic float CalcMaxPopupHeightFromItemCount(int items_count)\n{\n    ImGuiContext& g = *GImGui;\n    if (items_count <= 0)\n        return FLT_MAX;\n    return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);\n}\n\nbool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n\n    ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.HasFlags;\n    g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n    if (window->SkipItems)\n        return false;\n\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together\n    if (flags & ImGuiComboFlags_WidthFitPreview)\n        IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0);\n\n    const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f;\n    const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth());\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));\n    const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, id, &bb))\n        return false;\n\n    // Open on click\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held);\n    const ImGuiID popup_id = ImHashStr(\"##ComboPopup\", 0, id);\n    bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None);\n    if (pressed && !popup_open)\n    {\n        OpenPopupEx(popup_id, ImGuiPopupFlags_None);\n        popup_open = true;\n    }\n\n    // Render shape\n    const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);\n    const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size);\n    RenderNavCursor(bb, id);\n    if (!(flags & ImGuiComboFlags_NoPreview))\n        window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft);\n    if (!(flags & ImGuiComboFlags_NoArrowButton))\n    {\n        ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n        ImU32 text_col = GetColorU32(ImGuiCol_Text);\n        window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight);\n        if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x)\n            RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f);\n    }\n    RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding);\n\n    // Custom preview\n    if (flags & ImGuiComboFlags_CustomPreview)\n    {\n        g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y);\n        IM_ASSERT(preview_value == NULL || preview_value[0] == 0);\n        preview_value = NULL;\n    }\n\n    // Render preview and label\n    if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))\n    {\n        if (g.LogEnabled)\n            LogSetNextTextDecoration(\"{\", \"}\");\n        RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL);\n    }\n    if (label_size.x > 0)\n        RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label);\n\n    if (!popup_open)\n        return false;\n\n    g.NextWindowData.HasFlags = backup_next_window_data_flags;\n    return BeginComboPopup(popup_id, bb, flags);\n}\n\nbool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None))\n    {\n        g.NextWindowData.ClearFlags();\n        return false;\n    }\n\n    // Set popup size\n    float w = bb.GetWidth();\n    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint)\n    {\n        g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w);\n    }\n    else\n    {\n        if ((flags & ImGuiComboFlags_HeightMask_) == 0)\n            flags |= ImGuiComboFlags_HeightRegular;\n        IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one\n        int popup_max_height_in_items = -1;\n        if (flags & ImGuiComboFlags_HeightRegular)     popup_max_height_in_items = 8;\n        else if (flags & ImGuiComboFlags_HeightSmall)  popup_max_height_in_items = 4;\n        else if (flags & ImGuiComboFlags_HeightLarge)  popup_max_height_in_items = 20;\n        ImVec2 constraint_min(0.0f, 0.0f), constraint_max(FLT_MAX, FLT_MAX);\n        if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.x <= 0.0f) // Don't apply constraints if user specified a size\n            constraint_min.x = w;\n        if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.y <= 0.0f)\n            constraint_max.y = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items);\n        SetNextWindowSizeConstraints(constraint_min, constraint_max);\n    }\n\n    // This is essentially a specialized version of BeginPopupEx()\n    char name[16];\n    ImFormatString(name, IM_COUNTOF(name), \"##Combo_%02d\", g.BeginComboDepth); // Recycle windows based on depth\n\n    // Set position given a custom constraint (peak into expected window size so we can position it)\n    // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function?\n    // FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()?\n    if (ImGuiWindow* popup_window = FindWindowByName(name))\n        if (popup_window->WasActive)\n        {\n            // Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us.\n            ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window);\n            popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down; // Left = \"Below, Toward Left\", Down = \"Below, Toward Right (default)\"\n            ImRect r_outer = GetPopupAllowedExtentRect(popup_window);\n            ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox);\n            SetNextWindowPos(pos);\n        }\n\n    // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx()\n    ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove;\n    PushStyleVarX(ImGuiStyleVar_WindowPadding, g.Style.FramePadding.x); // Horizontally align ourselves with the framed text\n    bool ret = Begin(name, NULL, window_flags);\n    PopStyleVar();\n    if (!ret)\n    {\n        EndPopup();\n        if (!g.IO.ConfigDebugBeginReturnValueOnce && !g.IO.ConfigDebugBeginReturnValueLoop) // Begin may only return false with those debug tools activated.\n            IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above\n        return false;\n    }\n    g.BeginComboDepth++;\n    return true;\n}\n\nvoid ImGui::EndCombo()\n{\n    ImGuiContext& g = *GImGui;\n    g.BeginComboDepth--;\n    char name[16];\n    ImFormatString(name, IM_COUNTOF(name), \"##Combo_%02d\", g.BeginComboDepth); // FIXME: Move those to helpers?\n    if (strcmp(g.CurrentWindow->Name, name) != 0)\n        IM_ASSERT_USER_ERROR_RET(0, \"Calling EndCombo() in wrong window!\");\n    EndPopup();\n}\n\n// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements\n// (Experimental, see GitHub issues: #1658, #4168)\nbool ImGui::BeginComboPreview()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiComboPreviewData* preview_data = &g.ComboPreviewData;\n\n    if (window->SkipItems || !(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible))\n        return false;\n    IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag?\n    if (!window->ClipRect.Overlaps(preview_data->PreviewRect)) // Narrower test (optional)\n        return false;\n\n    // FIXME: This could be contained in a PushWorkRect() api\n    preview_data->BackupCursorPos = window->DC.CursorPos;\n    preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos;\n    preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;\n    preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;\n    preview_data->BackupLayout = window->DC.LayoutType;\n    window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding;\n    window->DC.CursorMaxPos = window->DC.CursorPos;\n    window->DC.LayoutType = ImGuiLayoutType_Horizontal;\n    window->DC.IsSameLine = false;\n    PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true);\n\n    return true;\n}\n\nvoid ImGui::EndComboPreview()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiComboPreviewData* preview_data = &g.ComboPreviewData;\n\n    // FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future\n    ImDrawList* draw_list = window->DrawList;\n    if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y)\n        if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command\n        {\n            draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect;\n            draw_list->_TryMergeDrawCmds();\n        }\n    PopClipRect();\n    window->DC.CursorPos = preview_data->BackupCursorPos;\n    window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos);\n    window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine;\n    window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset;\n    window->DC.LayoutType = preview_data->BackupLayout;\n    window->DC.IsSameLine = false;\n    preview_data->PreviewRect = ImRect();\n}\n\n// Getter for the old Combo() API: const char*[]\nstatic const char* Items_ArrayGetter(void* data, int idx)\n{\n    const char* const* items = (const char* const*)data;\n    return items[idx];\n}\n\n// Getter for the old Combo() API: \"item1\\0item2\\0item3\\0\"\nstatic const char* Items_SingleStringGetter(void* data, int idx)\n{\n    const char* items_separated_by_zeros = (const char*)data;\n    int items_count = 0;\n    const char* p = items_separated_by_zeros;\n    while (*p)\n    {\n        if (idx == items_count)\n            break;\n        p += ImStrlen(p) + 1;\n        items_count++;\n    }\n    return *p ? p : NULL;\n}\n\n// Old API, prefer using BeginCombo() nowadays if you can.\nbool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Call the getter to obtain the preview string which is a parameter to BeginCombo()\n    const char* preview_value = NULL;\n    if (*current_item >= 0 && *current_item < items_count)\n        preview_value = getter(user_data, *current_item);\n\n    // The old Combo() API exposed \"popup_max_height_in_items\". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.\n    if (popup_max_height_in_items != -1 && !(g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint))\n        SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));\n\n    if (!BeginCombo(label, preview_value, ImGuiComboFlags_None))\n        return false;\n\n    // Display items\n    bool value_changed = false;\n    ImGuiListClipper clipper;\n    clipper.Begin(items_count);\n    clipper.IncludeItemByIndex(*current_item);\n    while (clipper.Step())\n        for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)\n        {\n            const char* item_text = getter(user_data, i);\n            if (item_text == NULL)\n                item_text = \"*Unknown item*\";\n\n            PushID(i);\n            const bool item_selected = (i == *current_item);\n            if (Selectable(item_text, item_selected) && *current_item != i)\n            {\n                value_changed = true;\n                *current_item = i;\n            }\n            if (item_selected)\n                SetItemDefaultFocus();\n            PopID();\n        }\n\n    EndCombo();\n    if (value_changed)\n        MarkItemEdited(g.LastItemData.ID);\n\n    return value_changed;\n}\n\n// Combo box helper allowing to pass an array of strings.\nbool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items)\n{\n    const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);\n    return value_changed;\n}\n\n// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items \"item1\\0item2\\0\"\nbool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)\n{\n    int items_count = 0;\n    const char* p = items_separated_by_zeros;       // FIXME-OPT: Avoid computing this, or at least only when combo is open\n    while (*p)\n    {\n        p += ImStrlen(p) + 1;\n        items_count++;\n    }\n    bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);\n    return value_changed;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Data Type and Data Formatting Helpers [Internal]\n//-------------------------------------------------------------------------\n// - DataTypeGetInfo()\n// - DataTypeFormatString()\n// - DataTypeApplyOp()\n// - DataTypeApplyFromText()\n// - DataTypeCompare()\n// - DataTypeClamp()\n// - GetMinimumStepAtDecimalPrecision\n// - RoundScalarWithFormat<>()\n//-------------------------------------------------------------------------\n\nstatic const ImU32 GDefaultRgbaColorMarkers[4] =\n{\n    IM_COL32(240,20,20,255), IM_COL32(20,240,20,255), IM_COL32(20,20,240,255), IM_COL32(140,140,140,255)\n};\n\nstatic const ImGuiDataTypeInfo GDataTypeInfo[] =\n{\n    { sizeof(char),             \"S8\",   \"%d\",   \"%d\"    },  // ImGuiDataType_S8\n    { sizeof(unsigned char),    \"U8\",   \"%u\",   \"%u\"    },\n    { sizeof(short),            \"S16\",  \"%d\",   \"%d\"    },  // ImGuiDataType_S16\n    { sizeof(unsigned short),   \"U16\",  \"%u\",   \"%u\"    },\n    { sizeof(int),              \"S32\",  \"%d\",   \"%d\"    },  // ImGuiDataType_S32\n    { sizeof(unsigned int),     \"U32\",  \"%u\",   \"%u\"    },\n#ifdef _MSC_VER\n    { sizeof(ImS64),            \"S64\",  \"%I64d\",\"%I64d\" },  // ImGuiDataType_S64\n    { sizeof(ImU64),            \"U64\",  \"%I64u\",\"%I64u\" },\n#else\n    { sizeof(ImS64),            \"S64\",  \"%lld\", \"%lld\"  },  // ImGuiDataType_S64\n    { sizeof(ImU64),            \"U64\",  \"%llu\", \"%llu\"  },\n#endif\n    { sizeof(float),            \"float\", \"%.3f\",\"%f\"    },  // ImGuiDataType_Float (float are promoted to double in va_arg)\n    { sizeof(double),           \"double\",\"%f\",  \"%lf\"   },  // ImGuiDataType_Double\n    { sizeof(bool),             \"bool\", \"%d\",   \"%d\"    },  // ImGuiDataType_Bool\n    { 0,                        \"char*\",\"%s\",   \"%s\"    },  // ImGuiDataType_String\n};\nIM_STATIC_ASSERT(IM_COUNTOF(GDataTypeInfo) == ImGuiDataType_COUNT);\n\nconst ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type)\n{\n    IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);\n    return &GDataTypeInfo[data_type];\n}\n\nint ImGui::DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format)\n{\n    // Signedness doesn't matter when pushing integer arguments\n    if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32)\n        return ImFormatString(buf, buf_size, format, *(const ImU32*)p_data);\n    if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64)\n        return ImFormatString(buf, buf_size, format, *(const ImU64*)p_data);\n    if (data_type == ImGuiDataType_Float)\n        return ImFormatString(buf, buf_size, format, *(const float*)p_data);\n    if (data_type == ImGuiDataType_Double)\n        return ImFormatString(buf, buf_size, format, *(const double*)p_data);\n    if (data_type == ImGuiDataType_S8)\n        return ImFormatString(buf, buf_size, format, *(const ImS8*)p_data);\n    if (data_type == ImGuiDataType_U8)\n        return ImFormatString(buf, buf_size, format, *(const ImU8*)p_data);\n    if (data_type == ImGuiDataType_S16)\n        return ImFormatString(buf, buf_size, format, *(const ImS16*)p_data);\n    if (data_type == ImGuiDataType_U16)\n        return ImFormatString(buf, buf_size, format, *(const ImU16*)p_data);\n    IM_ASSERT(0);\n    return 0;\n}\n\nvoid ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg1, const void* arg2)\n{\n    IM_ASSERT(op == '+' || op == '-');\n    switch (data_type)\n    {\n        case ImGuiDataType_S8:\n            if (op == '+') { *(ImS8*)output  = ImAddClampOverflow(*(const ImS8*)arg1,  *(const ImS8*)arg2,  IM_S8_MIN,  IM_S8_MAX); }\n            if (op == '-') { *(ImS8*)output  = ImSubClampOverflow(*(const ImS8*)arg1,  *(const ImS8*)arg2,  IM_S8_MIN,  IM_S8_MAX); }\n            return;\n        case ImGuiDataType_U8:\n            if (op == '+') { *(ImU8*)output  = ImAddClampOverflow(*(const ImU8*)arg1,  *(const ImU8*)arg2,  IM_U8_MIN,  IM_U8_MAX); }\n            if (op == '-') { *(ImU8*)output  = ImSubClampOverflow(*(const ImU8*)arg1,  *(const ImU8*)arg2,  IM_U8_MIN,  IM_U8_MAX); }\n            return;\n        case ImGuiDataType_S16:\n            if (op == '+') { *(ImS16*)output = ImAddClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); }\n            if (op == '-') { *(ImS16*)output = ImSubClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); }\n            return;\n        case ImGuiDataType_U16:\n            if (op == '+') { *(ImU16*)output = ImAddClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); }\n            if (op == '-') { *(ImU16*)output = ImSubClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); }\n            return;\n        case ImGuiDataType_S32:\n            if (op == '+') { *(ImS32*)output = ImAddClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); }\n            if (op == '-') { *(ImS32*)output = ImSubClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); }\n            return;\n        case ImGuiDataType_U32:\n            if (op == '+') { *(ImU32*)output = ImAddClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); }\n            if (op == '-') { *(ImU32*)output = ImSubClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); }\n            return;\n        case ImGuiDataType_S64:\n            if (op == '+') { *(ImS64*)output = ImAddClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); }\n            if (op == '-') { *(ImS64*)output = ImSubClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); }\n            return;\n        case ImGuiDataType_U64:\n            if (op == '+') { *(ImU64*)output = ImAddClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); }\n            if (op == '-') { *(ImU64*)output = ImSubClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); }\n            return;\n        case ImGuiDataType_Float:\n            if (op == '+') { *(float*)output = *(const float*)arg1 + *(const float*)arg2; }\n            if (op == '-') { *(float*)output = *(const float*)arg1 - *(const float*)arg2; }\n            return;\n        case ImGuiDataType_Double:\n            if (op == '+') { *(double*)output = *(const double*)arg1 + *(const double*)arg2; }\n            if (op == '-') { *(double*)output = *(const double*)arg1 - *(const double*)arg2; }\n            return;\n        case ImGuiDataType_COUNT: break;\n    }\n    IM_ASSERT(0);\n}\n\n// User can input math operators (e.g. +100) to edit a numerical values.\n// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess..\nbool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty)\n{\n    // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all.\n    const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type);\n    ImGuiDataTypeStorage data_backup;\n    memcpy(&data_backup, p_data, type_info->Size);\n\n    while (ImCharIsBlankA(*buf))\n        buf++;\n    if (!buf[0])\n    {\n        if (p_data_when_empty != NULL)\n        {\n            memcpy(p_data, p_data_when_empty, type_info->Size);\n            return memcmp(&data_backup, p_data, type_info->Size) != 0;\n        }\n        return false;\n    }\n\n    // Sanitize format\n    // - For float/double we have to ignore format with precision (e.g. \"%.2f\") because sscanf doesn't take them in, so force them into %f and %lf\n    // - In theory could treat empty format as using default, but this would only cover rare/bizarre case of using InputScalar() + integer + format string without %.\n    char format_sanitized[32];\n    if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)\n        format = type_info->ScanFmt;\n    else\n        format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_COUNTOF(format_sanitized));\n\n    // Small types need a 32-bit buffer to receive the result from scanf()\n    int v32 = 0;\n    if (sscanf(buf, format, type_info->Size >= 4 ? p_data : &v32) < 1)\n        return false;\n    if (type_info->Size < 4)\n    {\n        if (data_type == ImGuiDataType_S8)\n            *(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX);\n        else if (data_type == ImGuiDataType_U8)\n            *(ImU8*)p_data = (ImU8)ImClamp(v32, (int)IM_U8_MIN, (int)IM_U8_MAX);\n        else if (data_type == ImGuiDataType_S16)\n            *(ImS16*)p_data = (ImS16)ImClamp(v32, (int)IM_S16_MIN, (int)IM_S16_MAX);\n        else if (data_type == ImGuiDataType_U16)\n            *(ImU16*)p_data = (ImU16)ImClamp(v32, (int)IM_U16_MIN, (int)IM_U16_MAX);\n        else\n            IM_ASSERT(0);\n    }\n\n    return memcmp(&data_backup, p_data, type_info->Size) != 0;\n}\n\ntemplate<typename T>\nstatic int DataTypeCompareT(const T* lhs, const T* rhs)\n{\n    if (*lhs < *rhs) return -1;\n    if (*lhs > *rhs) return +1;\n    return 0;\n}\n\nint ImGui::DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2)\n{\n    switch (data_type)\n    {\n    case ImGuiDataType_S8:     return DataTypeCompareT<ImS8  >((const ImS8*  )arg_1, (const ImS8*  )arg_2);\n    case ImGuiDataType_U8:     return DataTypeCompareT<ImU8  >((const ImU8*  )arg_1, (const ImU8*  )arg_2);\n    case ImGuiDataType_S16:    return DataTypeCompareT<ImS16 >((const ImS16* )arg_1, (const ImS16* )arg_2);\n    case ImGuiDataType_U16:    return DataTypeCompareT<ImU16 >((const ImU16* )arg_1, (const ImU16* )arg_2);\n    case ImGuiDataType_S32:    return DataTypeCompareT<ImS32 >((const ImS32* )arg_1, (const ImS32* )arg_2);\n    case ImGuiDataType_U32:    return DataTypeCompareT<ImU32 >((const ImU32* )arg_1, (const ImU32* )arg_2);\n    case ImGuiDataType_S64:    return DataTypeCompareT<ImS64 >((const ImS64* )arg_1, (const ImS64* )arg_2);\n    case ImGuiDataType_U64:    return DataTypeCompareT<ImU64 >((const ImU64* )arg_1, (const ImU64* )arg_2);\n    case ImGuiDataType_Float:  return DataTypeCompareT<float >((const float* )arg_1, (const float* )arg_2);\n    case ImGuiDataType_Double: return DataTypeCompareT<double>((const double*)arg_1, (const double*)arg_2);\n    case ImGuiDataType_COUNT:  break;\n    }\n    IM_ASSERT(0);\n    return 0;\n}\n\ntemplate<typename T>\nstatic bool DataTypeClampT(T* v, const T* v_min, const T* v_max)\n{\n    // Clamp, both sides are optional, return true if modified\n    if (v_min && *v < *v_min) { *v = *v_min; return true; }\n    if (v_max && *v > *v_max) { *v = *v_max; return true; }\n    return false;\n}\n\nbool ImGui::DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max)\n{\n    switch (data_type)\n    {\n    case ImGuiDataType_S8:     return DataTypeClampT<ImS8  >((ImS8*  )p_data, (const ImS8*  )p_min, (const ImS8*  )p_max);\n    case ImGuiDataType_U8:     return DataTypeClampT<ImU8  >((ImU8*  )p_data, (const ImU8*  )p_min, (const ImU8*  )p_max);\n    case ImGuiDataType_S16:    return DataTypeClampT<ImS16 >((ImS16* )p_data, (const ImS16* )p_min, (const ImS16* )p_max);\n    case ImGuiDataType_U16:    return DataTypeClampT<ImU16 >((ImU16* )p_data, (const ImU16* )p_min, (const ImU16* )p_max);\n    case ImGuiDataType_S32:    return DataTypeClampT<ImS32 >((ImS32* )p_data, (const ImS32* )p_min, (const ImS32* )p_max);\n    case ImGuiDataType_U32:    return DataTypeClampT<ImU32 >((ImU32* )p_data, (const ImU32* )p_min, (const ImU32* )p_max);\n    case ImGuiDataType_S64:    return DataTypeClampT<ImS64 >((ImS64* )p_data, (const ImS64* )p_min, (const ImS64* )p_max);\n    case ImGuiDataType_U64:    return DataTypeClampT<ImU64 >((ImU64* )p_data, (const ImU64* )p_min, (const ImU64* )p_max);\n    case ImGuiDataType_Float:  return DataTypeClampT<float >((float* )p_data, (const float* )p_min, (const float* )p_max);\n    case ImGuiDataType_Double: return DataTypeClampT<double>((double*)p_data, (const double*)p_min, (const double*)p_max);\n    case ImGuiDataType_COUNT:  break;\n    }\n    IM_ASSERT(0);\n    return false;\n}\n\nbool ImGui::DataTypeIsZero(ImGuiDataType data_type, const void* p_data)\n{\n    ImGuiContext& g = *GImGui;\n    return DataTypeCompare(data_type, p_data, &g.DataTypeZeroValue) == 0;\n}\n\nstatic float GetMinimumStepAtDecimalPrecision(int decimal_precision)\n{\n    static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f };\n    if (decimal_precision < 0)\n        return FLT_MIN;\n    return (decimal_precision < IM_COUNTOF(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision);\n}\n\ntemplate<typename TYPE>\nTYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v)\n{\n    IM_UNUSED(data_type);\n    IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double);\n    const char* fmt_start = ImParseFormatFindStart(format);\n    if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string\n        return v;\n\n    // Sanitize format\n    char fmt_sanitized[32];\n    ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_COUNTOF(fmt_sanitized));\n    fmt_start = fmt_sanitized;\n\n    // Format value with our rounding, and read back\n    char v_str[64];\n    ImFormatString(v_str, IM_COUNTOF(v_str), fmt_start, v);\n    const char* p = v_str;\n    while (*p == ' ')\n        p++;\n    v = (TYPE)ImAtof(p);\n\n    return v;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.\n//-------------------------------------------------------------------------\n// - DragBehaviorT<>() [Internal]\n// - DragBehavior() [Internal]\n// - DragScalar()\n// - DragScalarN()\n// - DragFloat()\n// - DragFloat2()\n// - DragFloat3()\n// - DragFloat4()\n// - DragFloatRange2()\n// - DragInt()\n// - DragInt2()\n// - DragInt3()\n// - DragInt4()\n// - DragIntRange2()\n//-------------------------------------------------------------------------\n\n// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls)\ntemplate<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>\nbool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;\n    const bool is_bounded = (v_min < v_max) || ((v_min == v_max) && (v_min != 0.0f || (flags & ImGuiSliderFlags_ClampZeroRange)));\n    const bool is_wrapped = is_bounded && (flags & ImGuiSliderFlags_WrapAround);\n    const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0;\n    const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);\n\n    // Default tweak speed\n    if (v_speed == 0.0f && is_bounded && (v_max - v_min < FLT_MAX))\n        v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio);\n\n    // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings\n    float adjust_delta = 0.0f;\n    if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))\n    {\n        adjust_delta = g.IO.MouseDelta[axis];\n        if (g.IO.KeyAlt && !(flags & ImGuiSliderFlags_NoSpeedTweaks))\n            adjust_delta *= 1.0f / 100.0f;\n        if (g.IO.KeyShift && !(flags & ImGuiSliderFlags_NoSpeedTweaks))\n            adjust_delta *= 10.0f;\n    }\n    else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)\n    {\n        const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0;\n        const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);\n        const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast);\n        const float tweak_factor = (flags & ImGuiSliderFlags_NoSpeedTweaks) ? 1.0f : tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f;\n        adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor;\n        v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));\n    }\n    adjust_delta *= v_speed;\n\n    // For vertical drag we currently assume that Up=higher value (like we do with vertical sliders). This may become a parameter.\n    if (axis == ImGuiAxis_Y)\n        adjust_delta = -adjust_delta;\n\n    // For logarithmic use our range is effectively 0..1 so scale the delta into that range\n    if (is_logarithmic && (v_max - v_min < FLT_MAX) && ((v_max - v_min) > 0.000001f)) // Epsilon to avoid /0\n        adjust_delta /= (float)(v_max - v_min);\n\n    // Clear current value on activation\n    // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300.\n    const bool is_just_activated = g.ActiveIdIsJustActivated;\n    const bool is_already_past_limits_and_pushing_outward = is_bounded && !is_wrapped && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f));\n    if (is_just_activated || is_already_past_limits_and_pushing_outward)\n    {\n        g.DragCurrentAccum = 0.0f;\n        g.DragCurrentAccumDirty = false;\n    }\n    else if (adjust_delta != 0.0f)\n    {\n        g.DragCurrentAccum += adjust_delta;\n        g.DragCurrentAccumDirty = true;\n    }\n\n    if (!g.DragCurrentAccumDirty)\n        return false;\n\n    TYPE v_cur = *v;\n    FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f;\n\n    float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true\n    const float zero_deadzone_halfsize = 0.0f; // Drag widgets have no deadzone (as it doesn't make sense)\n    if (is_logarithmic)\n    {\n        // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound.\n        const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1;\n        logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision);\n\n        // Convert to parametric space, apply delta, convert back\n        float v_old_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n        float v_new_parametric = v_old_parametric + g.DragCurrentAccum;\n        v_cur = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new_parametric, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n        v_old_ref_for_accum_remainder = v_old_parametric;\n    }\n    else\n    {\n        v_cur += (SIGNEDTYPE)g.DragCurrentAccum;\n    }\n\n    // Round to user desired precision based on format string\n    if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))\n        v_cur = RoundScalarWithFormatT<TYPE>(format, data_type, v_cur);\n\n    // Preserve remainder after rounding has been applied. This also allow slow tweaking of values.\n    g.DragCurrentAccumDirty = false;\n    if (is_logarithmic)\n    {\n        // Convert to parametric space, apply delta, convert back\n        float v_new_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n        g.DragCurrentAccum -= (float)(v_new_parametric - v_old_ref_for_accum_remainder);\n    }\n    else\n    {\n        g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v);\n    }\n\n    // Lose zero sign for float/double\n    if (v_cur == (TYPE)-0)\n        v_cur = (TYPE)0;\n\n    if (*v != v_cur && is_bounded)\n    {\n        if (is_wrapped)\n        {\n            // Wrap values\n            if (v_cur < v_min)\n                v_cur += v_max - v_min + (is_floating_point ? 0 : 1);\n            if (v_cur > v_max)\n                v_cur -= v_max - v_min + (is_floating_point ? 0 : 1);\n        }\n        else\n        {\n            // Clamp values + handle overflow/wrap-around for integer types.\n            if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_floating_point))\n                v_cur = v_min;\n            if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_floating_point))\n                v_cur = v_max;\n        }\n    }\n\n    // Apply result\n    if (*v == v_cur)\n        return false;\n    *v = v_cur;\n    return true;\n}\n\nbool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)\n{\n    // Read imgui.cpp \"API BREAKING CHANGES\" section for 1.78 if you hit this assert.\n    IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && \"Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.\");\n\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveId == id)\n    {\n        // Those are the things we can do easily outside the DragBehaviorT<> template, saves code generation.\n        if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0])\n            ClearActiveID();\n        else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)\n            ClearActiveID();\n    }\n    if (g.ActiveId != id)\n        return false;\n    if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))\n        return false;\n\n    switch (data_type)\n    {\n    case ImGuiDataType_S8:     { ImS32 v32 = (ImS32)*(ImS8*)p_v;  bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS8*) p_min : IM_S8_MIN,  p_max ? *(const ImS8*)p_max  : IM_S8_MAX,  format, flags); if (r) *(ImS8*)p_v = (ImS8)v32; return r; }\n    case ImGuiDataType_U8:     { ImU32 v32 = (ImU32)*(ImU8*)p_v;  bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU8*) p_min : IM_U8_MIN,  p_max ? *(const ImU8*)p_max  : IM_U8_MAX,  format, flags); if (r) *(ImU8*)p_v = (ImU8)v32; return r; }\n    case ImGuiDataType_S16:    { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS16*)p_min : IM_S16_MIN, p_max ? *(const ImS16*)p_max : IM_S16_MAX, format, flags); if (r) *(ImS16*)p_v = (ImS16)v32; return r; }\n    case ImGuiDataType_U16:    { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU16*)p_min : IM_U16_MIN, p_max ? *(const ImU16*)p_max : IM_U16_MAX, format, flags); if (r) *(ImU16*)p_v = (ImU16)v32; return r; }\n    case ImGuiDataType_S32:    return DragBehaviorT<ImS32, ImS32, float >(data_type, (ImS32*)p_v,  v_speed, p_min ? *(const ImS32* )p_min : IM_S32_MIN, p_max ? *(const ImS32* )p_max : IM_S32_MAX, format, flags);\n    case ImGuiDataType_U32:    return DragBehaviorT<ImU32, ImS32, float >(data_type, (ImU32*)p_v,  v_speed, p_min ? *(const ImU32* )p_min : IM_U32_MIN, p_max ? *(const ImU32* )p_max : IM_U32_MAX, format, flags);\n    case ImGuiDataType_S64:    return DragBehaviorT<ImS64, ImS64, double>(data_type, (ImS64*)p_v,  v_speed, p_min ? *(const ImS64* )p_min : IM_S64_MIN, p_max ? *(const ImS64* )p_max : IM_S64_MAX, format, flags);\n    case ImGuiDataType_U64:    return DragBehaviorT<ImU64, ImS64, double>(data_type, (ImU64*)p_v,  v_speed, p_min ? *(const ImU64* )p_min : IM_U64_MIN, p_max ? *(const ImU64* )p_max : IM_U64_MAX, format, flags);\n    case ImGuiDataType_Float:  return DragBehaviorT<float, float, float >(data_type, (float*)p_v,  v_speed, p_min ? *(const float* )p_min : -FLT_MAX,   p_max ? *(const float* )p_max : FLT_MAX,    format, flags);\n    case ImGuiDataType_Double: return DragBehaviorT<double,double,double>(data_type, (double*)p_v, v_speed, p_min ? *(const double*)p_min : -DBL_MAX,   p_max ? *(const double*)p_max : DBL_MAX,    format, flags);\n    case ImGuiDataType_COUNT:  break;\n    }\n    IM_ASSERT(0);\n    return false;\n}\n\n// Only clamp Ctrl+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp)\nstatic bool TempInputIsClampEnabled(ImGuiSliderFlags flags, ImGuiDataType data_type, const void* p_min, const void* p_max)\n{\n    if ((flags & ImGuiSliderFlags_ClampOnInput) && (p_min != NULL || p_max != NULL))\n    {\n        const int clamp_range_dir = (p_min != NULL && p_max != NULL) ? ImGui::DataTypeCompare(data_type, p_min, p_max) : 0; // -1 when *p_min < *p_max, == 0 when *p_min == *p_max\n        if (p_min == NULL || p_max == NULL || clamp_range_dir < 0)\n            return true;\n        if (clamp_range_dir == 0)\n            return ImGui::DataTypeIsZero(data_type, p_min) ? ((flags & ImGuiSliderFlags_ClampZeroRange) != 0) : true;\n    }\n    return false;\n}\n\n// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a Drag widget, p_min and p_max are optional.\n// Read code of e.g. DragFloat(), DragInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.\nbool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    const float w = CalcItemWidth();\n    const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0;\n\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));\n    const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));\n\n    const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))\n        return false;\n\n    // Default format string when passing NULL\n    if (format == NULL)\n        format = DataTypeGetInfo(data_type)->PrintFmt;\n\n    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);\n    bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);\n    if (!temp_input_is_active)\n    {\n        // Tabbing or Ctrl+Click on Drag turns it into an InputText\n        const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);\n        const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id));\n        const bool make_active = (clicked || double_clicked || g.NavActivateId == id);\n        if (make_active && (clicked || double_clicked))\n            SetKeyOwner(ImGuiKey_MouseLeft, id);\n        if (make_active && temp_input_allowed)\n            if ((clicked && g.IO.KeyCtrl) || double_clicked || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))\n                temp_input_is_active = true;\n\n        // (Optional) simple click (without moving) turns Drag into an InputText\n        if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active)\n            if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))\n            {\n                g.NavActivateId = id;\n                g.NavActivateFlags = ImGuiActivateFlags_PreferInput;\n                temp_input_is_active = true;\n            }\n\n        // Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert)\n        if (make_active)\n            memcpy(&g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size);\n\n        if (make_active && !temp_input_is_active)\n        {\n            SetActiveID(id, window);\n            SetFocusID(id, window);\n            FocusWindow(window);\n            g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);\n        }\n    }\n\n    if (temp_input_is_active)\n    {\n        const bool clamp_enabled = TempInputIsClampEnabled(flags, data_type, p_min, p_max);\n        return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL);\n    }\n\n    // Draw frame\n    const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);\n    RenderNavCursor(frame_bb, id);\n    RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding);\n    if (color_marker != 0 && style.ColorMarkerSize > 0.0f)\n        RenderColorComponentMarker(frame_bb, GetColorU32(color_marker), style.FrameRounding);\n    RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding);\n\n    // Drag behavior\n    const bool value_changed = DragBehavior(id, data_type, p_data, v_speed, p_min, p_max, format, flags);\n    if (value_changed)\n        MarkItemEdited(id);\n\n    // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.\n    char value_buf[64];\n    const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format);\n    if (g.LogEnabled)\n        LogSetNextTextDecoration(\"{\", \"}\");\n    RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));\n\n    if (label_size.x > 0.0f)\n        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));\n    return value_changed;\n}\n\nbool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    bool value_changed = false;\n    BeginGroup();\n    PushID(label);\n    PushMultiItemsWidths(components, CalcItemWidth());\n    size_t type_size = GDataTypeInfo[data_type].Size;\n    for (int i = 0; i < components; i++)\n    {\n        PushID(i);\n        if (i > 0)\n            SameLine(0, g.Style.ItemInnerSpacing.x);\n        if (flags & ImGuiSliderFlags_ColorMarkers)\n            SetNextItemColorMarker(GDefaultRgbaColorMarkers[i]);\n        value_changed |= DragScalar(\"\", data_type, p_data, v_speed, p_min, p_max, format, flags);\n        PopID();\n        PopItemWidth();\n        p_data = (void*)((char*)p_data + type_size);\n    }\n    PopID();\n\n    const char* label_end = FindRenderedTextEnd(label);\n    if (label != label_end)\n    {\n        SameLine(0, g.Style.ItemInnerSpacing.x);\n        TextEx(label, label_end);\n    }\n\n    EndGroup();\n    return value_changed;\n}\n\nbool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, flags);\n}\n\n// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this.\nbool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    PushID(label);\n    BeginGroup();\n    PushMultiItemsWidths(2, CalcItemWidth());\n\n    float min_min = (v_min >= v_max) ? -FLT_MAX : v_min;\n    float min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max);\n    ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0);\n    bool value_changed = DragScalar(\"##min\", ImGuiDataType_Float, v_current_min, v_speed, &min_min, &min_max, format, min_flags);\n    PopItemWidth();\n    SameLine(0, g.Style.ItemInnerSpacing.x);\n\n    float max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min);\n    float max_max = (v_min >= v_max) ? FLT_MAX : v_max;\n    ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0);\n    value_changed |= DragScalar(\"##max\", ImGuiDataType_Float, v_current_max, v_speed, &max_min, &max_max, format_max ? format_max : format, max_flags);\n    PopItemWidth();\n    SameLine(0, g.Style.ItemInnerSpacing.x);\n\n    TextEx(label, FindRenderedTextEnd(label));\n    EndGroup();\n    PopID();\n\n    return value_changed;\n}\n\n// NB: v_speed is float to allow adjusting the drag speed with more precision\nbool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format, flags);\n}\n\n// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this.\nbool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    PushID(label);\n    BeginGroup();\n    PushMultiItemsWidths(2, CalcItemWidth());\n\n    int min_min = (v_min >= v_max) ? INT_MIN : v_min;\n    int min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max);\n    ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0);\n    bool value_changed = DragInt(\"##min\", v_current_min, v_speed, min_min, min_max, format, min_flags);\n    PopItemWidth();\n    SameLine(0, g.Style.ItemInnerSpacing.x);\n\n    int max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min);\n    int max_max = (v_min >= v_max) ? INT_MAX : v_max;\n    ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0);\n    value_changed |= DragInt(\"##max\", v_current_max, v_speed, max_min, max_max, format_max ? format_max : format, max_flags);\n    PopItemWidth();\n    SameLine(0, g.Style.ItemInnerSpacing.x);\n\n    TextEx(label, FindRenderedTextEnd(label));\n    EndGroup();\n    PopID();\n\n    return value_changed;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.\n//-------------------------------------------------------------------------\n// - ScaleRatioFromValueT<> [Internal]\n// - ScaleValueFromRatioT<> [Internal]\n// - SliderBehaviorT<>() [Internal]\n// - SliderBehavior() [Internal]\n// - SliderScalar()\n// - SliderScalarN()\n// - SliderFloat()\n// - SliderFloat2()\n// - SliderFloat3()\n// - SliderFloat4()\n// - SliderAngle()\n// - SliderInt()\n// - SliderInt2()\n// - SliderInt3()\n// - SliderInt4()\n// - VSliderScalar()\n// - VSliderFloat()\n// - VSliderInt()\n//-------------------------------------------------------------------------\n\n// Convert a value v in the output space of a slider into a parametric position on the slider itself (the logical opposite of ScaleValueFromRatioT)\ntemplate<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>\nfloat ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float logarithmic_zero_epsilon, float zero_deadzone_halfsize)\n{\n    if (v_min == v_max)\n        return 0.0f;\n    IM_UNUSED(data_type);\n\n    const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);\n    if (logarithmic_zero_epsilon > 0.0f) // == is_logarithmic from caller\n    {\n        bool flipped = v_max < v_min;\n\n        if (flipped) // Handle the case where the range is backwards\n            ImSwap(v_min, v_max);\n\n        // Fudge min/max to avoid getting close to log(0)\n        FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min;\n        FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max;\n\n        // Awkward special cases - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon)\n        if ((v_min == 0.0f) && (v_max < 0.0f))\n            v_min_fudged = -logarithmic_zero_epsilon;\n        else if ((v_max == 0.0f) && (v_min < 0.0f))\n            v_max_fudged = -logarithmic_zero_epsilon;\n\n        float result;\n        if (v_clamped <= v_min_fudged)\n            result = 0.0f; // Workaround for values that are in-range but below our fudge\n        else if (v_clamped >= v_max_fudged)\n            result = 1.0f; // Workaround for values that are in-range but above our fudge\n        else if ((v_min * v_max) < 0.0f) // Range crosses zero, so split into two portions\n        {\n            float zero_point_center = (-(float)v_min) / ((float)v_max - (float)v_min); // The zero point in parametric space.  There's an argument we should take the logarithmic nature into account when calculating this, but for now this should do (and the most common case of a symmetrical range works fine)\n            float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize;\n            float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize;\n            if (v == 0.0f)\n                result = zero_point_center; // Special case for exactly zero\n            else if (v < 0.0f)\n                result = (1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(-v_min_fudged / logarithmic_zero_epsilon))) * zero_point_snap_L;\n            else\n                result = zero_point_snap_R + ((float)(ImLog((FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(v_max_fudged / logarithmic_zero_epsilon)) * (1.0f - zero_point_snap_R));\n        }\n        else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider\n            result = 1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / -v_max_fudged) / ImLog(-v_min_fudged / -v_max_fudged));\n        else\n            result = (float)(ImLog((FLOATTYPE)v_clamped / v_min_fudged) / ImLog(v_max_fudged / v_min_fudged));\n\n        return flipped ? (1.0f - result) : result;\n    }\n    else\n    {\n        // Linear slider\n        return (float)((FLOATTYPE)(SIGNEDTYPE)(v_clamped - v_min) / (FLOATTYPE)(SIGNEDTYPE)(v_max - v_min));\n    }\n}\n\n// Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT)\ntemplate<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>\nTYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, TYPE v_max, float logarithmic_zero_epsilon, float zero_deadzone_halfsize)\n{\n    // We special-case the extents because otherwise our logarithmic fudging can lead to \"mathematically correct\"\n    // but non-intuitive behaviors like a fully-left slider not actually reaching the minimum value. Also generally simpler.\n    if (t <= 0.0f || v_min == v_max)\n        return v_min;\n    if (t >= 1.0f)\n        return v_max;\n\n    TYPE result = (TYPE)0;\n    if (logarithmic_zero_epsilon > 0.0f) // == is_logarithmic from caller\n    {\n        // Fudge min/max to avoid getting silly results close to zero\n        FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min;\n        FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max;\n\n        const bool flipped = v_max < v_min; // Check if range is \"backwards\"\n        if (flipped)\n            ImSwap(v_min_fudged, v_max_fudged);\n\n        // Awkward special case - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon)\n        if ((v_max == 0.0f) && (v_min < 0.0f))\n            v_max_fudged = -logarithmic_zero_epsilon;\n\n        float t_with_flip = flipped ? (1.0f - t) : t; // t, but flipped if necessary to account for us flipping the range\n\n        if ((v_min * v_max) < 0.0f) // Range crosses zero, so we have to do this in two parts\n        {\n            float zero_point_center = (-(float)ImMin(v_min, v_max)) / ImAbs((float)v_max - (float)v_min); // The zero point in parametric space\n            float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize;\n            float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize;\n            if (t_with_flip >= zero_point_snap_L && t_with_flip <= zero_point_snap_R)\n                result = (TYPE)0.0f; // Special case to make getting exactly zero possible (the epsilon prevents it otherwise)\n            else if (t_with_flip < zero_point_center)\n                result = (TYPE)-(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, (FLOATTYPE)(1.0f - (t_with_flip / zero_point_snap_L))));\n            else\n                result = (TYPE)(logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (FLOATTYPE)((t_with_flip - zero_point_snap_R) / (1.0f - zero_point_snap_R))));\n        }\n        else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider\n            result = (TYPE)-(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, (FLOATTYPE)(1.0f - t_with_flip)));\n        else\n            result = (TYPE)(v_min_fudged * ImPow(v_max_fudged / v_min_fudged, (FLOATTYPE)t_with_flip));\n    }\n    else\n    {\n        // Linear slider\n        const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);\n        if (is_floating_point)\n        {\n            result = ImLerp(v_min, v_max, t);\n        }\n        else if (t < 1.0)\n        {\n            // - For integer values we want the clicking position to match the grab box so we round above\n            //   This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..\n            // - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64\n            //   range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits.\n            FLOATTYPE v_new_off_f = (SIGNEDTYPE)(v_max - v_min) * t;\n            result = (TYPE)((SIGNEDTYPE)v_min + (SIGNEDTYPE)(v_new_off_f + (FLOATTYPE)(v_min > v_max ? -0.5 : 0.5)));\n        }\n    }\n\n    return result;\n}\n\n// FIXME: Try to move more of the code into shared SliderBehavior()\ntemplate<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>\nbool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n\n    const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;\n    const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0;\n    const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);\n    const float v_range_f = (float)(v_min < v_max ? v_max - v_min : v_min - v_max); // We don't need high precision for what we do with it.\n\n    // Calculate bounds\n    const float grab_padding = 2.0f; // FIXME: Should be part of style.\n    const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f;\n    float grab_sz = style.GrabMinSize;\n    if (!is_floating_point && v_range_f >= 0.0f)                         // v_range_f < 0 may happen on integer overflows\n        grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit\n    grab_sz = ImMin(grab_sz, slider_sz);\n    const float slider_usable_sz = slider_sz - grab_sz;\n    const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f;\n    const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f;\n\n    float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true\n    float zero_deadzone_halfsize = 0.0f; // Only valid when is_logarithmic is true\n    if (is_logarithmic)\n    {\n        // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound.\n        const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1;\n        logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision);\n        zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f);\n    }\n\n    // Process interacting with the slider\n    bool value_changed = false;\n    if (g.ActiveId == id)\n    {\n        bool set_new_value = false;\n        float clicked_t = 0.0f;\n        if (g.ActiveIdSource == ImGuiInputSource_Mouse)\n        {\n            if (!g.IO.MouseDown[0])\n            {\n                ClearActiveID();\n            }\n            else\n            {\n                const float mouse_abs_pos = g.IO.MousePos[axis];\n                if (g.ActiveIdIsJustActivated)\n                {\n                    float grab_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n                    if (axis == ImGuiAxis_Y)\n                        grab_t = 1.0f - grab_t;\n                    const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);\n                    const bool clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_abs_pos <= grab_pos + grab_sz * 0.5f + 1.0f); // No harm being extra generous here.\n                    g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f;\n                }\n                if (slider_usable_sz > 0.0f)\n                    clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz);\n                if (axis == ImGuiAxis_Y)\n                    clicked_t = 1.0f - clicked_t;\n                set_new_value = true;\n            }\n        }\n        else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)\n        {\n            if (g.ActiveIdIsJustActivated)\n            {\n                g.SliderCurrentAccum = 0.0f; // Reset any stored nav delta upon activation\n                g.SliderCurrentAccumDirty = false;\n            }\n\n            float input_delta = (axis == ImGuiAxis_X) ? GetNavTweakPressedAmount(axis) : -GetNavTweakPressedAmount(axis);\n            if (input_delta != 0.0f)\n            {\n                const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);\n                const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast);\n                const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0;\n                if (decimal_precision > 0)\n                {\n                    input_delta /= 100.0f; // Keyboard/Gamepad tweak speeds in % of slider bounds\n                    if (tweak_slow)\n                        input_delta /= 10.0f;\n                }\n                else\n                {\n                    if ((v_range_f >= -100.0f && v_range_f <= 100.0f && v_range_f != 0.0f) || tweak_slow)\n                        input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f; // Keyboard/Gamepad tweak speeds in integer steps\n                    else\n                        input_delta /= 100.0f;\n                }\n                if (tweak_fast)\n                    input_delta *= 10.0f;\n\n                g.SliderCurrentAccum += input_delta;\n                g.SliderCurrentAccumDirty = true;\n            }\n\n            float delta = g.SliderCurrentAccum;\n            if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)\n            {\n                ClearActiveID();\n            }\n            else if (g.SliderCurrentAccumDirty)\n            {\n                clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n\n                if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits\n                {\n                    set_new_value = false;\n                    g.SliderCurrentAccum = 0.0f; // If pushing up against the limits, don't continue to accumulate\n                }\n                else\n                {\n                    set_new_value = true;\n                    float old_clicked_t = clicked_t;\n                    clicked_t = ImSaturate(clicked_t + delta);\n\n                    // Calculate what our \"new\" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator\n                    TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n                    if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))\n                        v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);\n                    float new_clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n\n                    if (delta > 0)\n                        g.SliderCurrentAccum -= ImMin(new_clicked_t - old_clicked_t, delta);\n                    else\n                        g.SliderCurrentAccum -= ImMax(new_clicked_t - old_clicked_t, delta);\n                }\n\n                g.SliderCurrentAccumDirty = false;\n            }\n        }\n\n        if (set_new_value)\n            if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))\n                set_new_value = false;\n\n        if (set_new_value)\n        {\n            TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n\n            // Round to user desired precision based on format string\n            if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))\n                v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);\n\n            // Apply result\n            if (*v != v_new)\n            {\n                *v = v_new;\n                value_changed = true;\n            }\n        }\n    }\n\n    if (slider_sz < 1.0f)\n    {\n        *out_grab_bb = ImRect(bb.Min, bb.Min);\n    }\n    else\n    {\n        // Output grab position so it can be displayed by the caller\n        float grab_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n        if (axis == ImGuiAxis_Y)\n            grab_t = 1.0f - grab_t;\n        const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);\n        if (axis == ImGuiAxis_X)\n            *out_grab_bb = ImRect(grab_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5f, bb.Max.y - grab_padding);\n        else\n            *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f);\n    }\n\n    return value_changed;\n}\n\n// For 32-bit and larger types, slider bounds are limited to half the natural type range.\n// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok.\n// It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders.\nbool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb)\n{\n    // Read imgui.cpp \"API BREAKING CHANGES\" section for 1.78 if you hit this assert.\n    IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && \"Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.\");\n    IM_ASSERT((flags & ImGuiSliderFlags_WrapAround) == 0); // Not supported by SliderXXX(), only by DragXXX()\n\n    switch (data_type)\n    {\n    case ImGuiDataType_S8:  { ImS32 v32 = (ImS32)*(ImS8*)p_v;  bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min,  *(const ImS8*)p_max,  format, flags, out_grab_bb); if (r) *(ImS8*)p_v  = (ImS8)v32;  return r; }\n    case ImGuiDataType_U8:  { ImU32 v32 = (ImU32)*(ImU8*)p_v;  bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)p_min,  *(const ImU8*)p_max,  format, flags, out_grab_bb); if (r) *(ImU8*)p_v  = (ImU8)v32;  return r; }\n    case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)p_min, *(const ImS16*)p_max, format, flags, out_grab_bb); if (r) *(ImS16*)p_v = (ImS16)v32; return r; }\n    case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)p_min, *(const ImU16*)p_max, format, flags, out_grab_bb); if (r) *(ImU16*)p_v = (ImU16)v32; return r; }\n    case ImGuiDataType_S32:\n        IM_ASSERT(*(const ImS32*)p_min >= IM_S32_MIN / 2 && *(const ImS32*)p_max <= IM_S32_MAX / 2);\n        return SliderBehaviorT<ImS32, ImS32, float >(bb, id, data_type, (ImS32*)p_v,  *(const ImS32*)p_min,  *(const ImS32*)p_max,  format, flags, out_grab_bb);\n    case ImGuiDataType_U32:\n        IM_ASSERT(*(const ImU32*)p_max <= IM_U32_MAX / 2);\n        return SliderBehaviorT<ImU32, ImS32, float >(bb, id, data_type, (ImU32*)p_v,  *(const ImU32*)p_min,  *(const ImU32*)p_max,  format, flags, out_grab_bb);\n    case ImGuiDataType_S64:\n        IM_ASSERT(*(const ImS64*)p_min >= IM_S64_MIN / 2 && *(const ImS64*)p_max <= IM_S64_MAX / 2);\n        return SliderBehaviorT<ImS64, ImS64, double>(bb, id, data_type, (ImS64*)p_v,  *(const ImS64*)p_min,  *(const ImS64*)p_max,  format, flags, out_grab_bb);\n    case ImGuiDataType_U64:\n        IM_ASSERT(*(const ImU64*)p_max <= IM_U64_MAX / 2);\n        return SliderBehaviorT<ImU64, ImS64, double>(bb, id, data_type, (ImU64*)p_v,  *(const ImU64*)p_min,  *(const ImU64*)p_max,  format, flags, out_grab_bb);\n    case ImGuiDataType_Float:\n        IM_ASSERT(*(const float*)p_min >= -FLT_MAX / 2.0f && *(const float*)p_max <= FLT_MAX / 2.0f);\n        return SliderBehaviorT<float, float, float >(bb, id, data_type, (float*)p_v,  *(const float*)p_min,  *(const float*)p_max,  format, flags, out_grab_bb);\n    case ImGuiDataType_Double:\n        IM_ASSERT(*(const double*)p_min >= -DBL_MAX / 2.0f && *(const double*)p_max <= DBL_MAX / 2.0f);\n        return SliderBehaviorT<double, double, double>(bb, id, data_type, (double*)p_v, *(const double*)p_min, *(const double*)p_max, format, flags, out_grab_bb);\n    case ImGuiDataType_COUNT: break;\n    }\n    IM_ASSERT(0);\n    return false;\n}\n\n// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a slider, they are all required.\n// Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.\nbool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    const float w = CalcItemWidth();\n    const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0;\n\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));\n    const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));\n\n    const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))\n        return false;\n\n    // Default format string when passing NULL\n    if (format == NULL)\n        format = DataTypeGetInfo(data_type)->PrintFmt;\n\n    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);\n    bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);\n    if (!temp_input_is_active)\n    {\n        // Tabbing or Ctrl+Click on Slider turns it into an input box\n        const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);\n        const bool make_active = (clicked || g.NavActivateId == id);\n        if (make_active && clicked)\n            SetKeyOwner(ImGuiKey_MouseLeft, id);\n        if (make_active && temp_input_allowed)\n            if ((clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))\n                temp_input_is_active = true;\n\n        // Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert)\n        if (make_active)\n            memcpy(&g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size);\n\n        if (make_active && !temp_input_is_active)\n        {\n            SetActiveID(id, window);\n            SetFocusID(id, window);\n            FocusWindow(window);\n            g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);\n        }\n    }\n\n    if (temp_input_is_active)\n    {\n        // Only clamp Ctrl+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp)\n        const bool clamp_enabled = (flags & ImGuiSliderFlags_ClampOnInput) != 0; // Don't use TempInputIsClampEnabled()\n        return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL);\n    }\n\n    // Draw frame\n    const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);\n    RenderNavCursor(frame_bb, id);\n    RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding);\n    if (color_marker != 0 && style.ColorMarkerSize > 0.0f)\n        RenderColorComponentMarker(frame_bb, GetColorU32(color_marker), style.FrameRounding);\n    RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding);\n\n    // Slider behavior\n    ImRect grab_bb;\n    const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags, &grab_bb);\n    if (value_changed)\n        MarkItemEdited(id);\n\n    // Render grab\n    if (grab_bb.Max.x > grab_bb.Min.x)\n        window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);\n\n    // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.\n    char value_buf[64];\n    const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format);\n    if (g.LogEnabled)\n        LogSetNextTextDecoration(\"{\", \"}\");\n    RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));\n\n    if (label_size.x > 0.0f)\n        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));\n    return value_changed;\n}\n\n// Add multiple sliders on 1 line for compact edition of multiple components\nbool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    bool value_changed = false;\n    BeginGroup();\n    PushID(label);\n    PushMultiItemsWidths(components, CalcItemWidth());\n    size_t type_size = GDataTypeInfo[data_type].Size;\n    for (int i = 0; i < components; i++)\n    {\n        PushID(i);\n        if (i > 0)\n            SameLine(0, g.Style.ItemInnerSpacing.x);\n        if (flags & ImGuiSliderFlags_ColorMarkers)\n            SetNextItemColorMarker(GDefaultRgbaColorMarkers[i]);\n        value_changed |= SliderScalar(\"\", data_type, v, v_min, v_max, format, flags);\n        PopID();\n        PopItemWidth();\n        v = (void*)((char*)v + type_size);\n    }\n    PopID();\n\n    const char* label_end = FindRenderedTextEnd(label);\n    if (label != label_end)\n    {\n        SameLine(0, g.Style.ItemInnerSpacing.x);\n        TextEx(label, label_end);\n    }\n\n    EndGroup();\n    return value_changed;\n}\n\nbool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags)\n{\n    if (format == NULL)\n        format = \"%.0f deg\";\n    float v_deg = (*v_rad) * 360.0f / (2 * IM_PI);\n    bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, flags);\n    if (value_changed)\n        *v_rad = v_deg * (2 * IM_PI) / 360.0f;\n    return value_changed;\n}\n\nbool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);\n    const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));\n\n    ItemSize(bb, style.FramePadding.y);\n    if (!ItemAdd(frame_bb, id))\n        return false;\n\n    // Default format string when passing NULL\n    if (format == NULL)\n        format = DataTypeGetInfo(data_type)->PrintFmt;\n\n    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);\n    const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);\n    if (clicked || g.NavActivateId == id)\n    {\n        if (clicked)\n            SetKeyOwner(ImGuiKey_MouseLeft, id);\n        SetActiveID(id, window);\n        SetFocusID(id, window);\n        FocusWindow(window);\n        g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);\n    }\n\n    // Draw frame\n    const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);\n    RenderNavCursor(frame_bb, id);\n    RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);\n\n    // Slider behavior\n    ImRect grab_bb;\n    const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags | ImGuiSliderFlags_Vertical, &grab_bb);\n    if (value_changed)\n        MarkItemEdited(id);\n\n    // Render grab\n    if (grab_bb.Max.y > grab_bb.Min.y)\n        window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);\n\n    // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.\n    // For the vertical slider we allow centered text to overlap the frame padding\n    char value_buf[64];\n    const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format);\n    RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f));\n    if (label_size.x > 0.0f)\n        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);\n\n    return value_changed;\n}\n\nbool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format, flags);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.\n//-------------------------------------------------------------------------\n// - ImParseFormatFindStart() [Internal]\n// - ImParseFormatFindEnd() [Internal]\n// - ImParseFormatTrimDecorations() [Internal]\n// - ImParseFormatSanitizeForPrinting() [Internal]\n// - ImParseFormatSanitizeForScanning() [Internal]\n// - ImParseFormatPrecision() [Internal]\n// - TempInputTextScalar() [Internal]\n// - InputScalar()\n// - InputScalarN()\n// - InputFloat()\n// - InputFloat2()\n// - InputFloat3()\n// - InputFloat4()\n// - InputInt()\n// - InputInt2()\n// - InputInt3()\n// - InputInt4()\n// - InputDouble()\n//-------------------------------------------------------------------------\n\n// We don't use strchr() because our strings are usually very short and often start with '%'\nconst char* ImParseFormatFindStart(const char* fmt)\n{\n    while (char c = fmt[0])\n    {\n        if (c == '%' && fmt[1] != '%')\n            return fmt;\n        else if (c == '%')\n            fmt++;\n        fmt++;\n    }\n    return fmt;\n}\n\nconst char* ImParseFormatFindEnd(const char* fmt)\n{\n    // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format.\n    if (fmt[0] != '%')\n        return fmt;\n    const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A'));\n    const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a'));\n    for (char c; (c = *fmt) != 0; fmt++)\n    {\n        if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0)\n            return fmt + 1;\n        if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0)\n            return fmt + 1;\n    }\n    return fmt;\n}\n\n// Extract the format out of a format string with leading or trailing decorations\n//  fmt = \"blah blah\"  -> return \"\"\n//  fmt = \"%.3f\"       -> return fmt\n//  fmt = \"hello %.3f\" -> return fmt + 6\n//  fmt = \"%.3f hello\" -> return buf written with \"%.3f\"\nconst char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_size)\n{\n    const char* fmt_start = ImParseFormatFindStart(fmt);\n    if (fmt_start[0] != '%')\n        return \"\";\n    const char* fmt_end = ImParseFormatFindEnd(fmt_start);\n    if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data.\n        return fmt_start;\n    ImStrncpy(buf, fmt_start, ImMin((size_t)(fmt_end - fmt_start) + 1, buf_size));\n    return buf;\n}\n\n// Sanitize format\n// - Zero terminate so extra characters after format (e.g. \"%f123\") don't confuse atof/atoi\n// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi.\nvoid ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size)\n{\n    const char* fmt_end = ImParseFormatFindEnd(fmt_in);\n    IM_UNUSED(fmt_out_size);\n    IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!\n    while (fmt_in < fmt_end)\n    {\n        char c = *fmt_in++;\n        if (c != '\\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.\n            *(fmt_out++) = c;\n    }\n    *fmt_out = 0; // Zero-terminate\n}\n\n// - For scanning we need to remove all width and precision fields and flags \"%+3.7f\" -> \"%f\". BUT don't strip types like \"%I64d\" which includes digits. ! \"%07I64d\" -> \"%I64d\"\nconst char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size)\n{\n    const char* fmt_end = ImParseFormatFindEnd(fmt_in);\n    const char* fmt_out_begin = fmt_out;\n    IM_UNUSED(fmt_out_size);\n    IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!\n    bool has_type = false;\n    while (fmt_in < fmt_end)\n    {\n        char c = *fmt_in++;\n        if (!has_type && ((c >= '0' && c <= '9') || c == '.' || c == '+' || c == '#'))\n            continue;\n        has_type |= ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); // Stop skipping digits\n        if (c != '\\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.\n            *(fmt_out++) = c;\n    }\n    *fmt_out = 0; // Zero-terminate\n    return fmt_out_begin;\n}\n\ntemplate<typename TYPE>\nstatic const char* ImAtoi(const char* src, TYPE* output)\n{\n    int negative = 0;\n    if (*src == '-') { negative = 1; src++; }\n    if (*src == '+') { src++; }\n    TYPE v = 0;\n    while (*src >= '0' && *src <= '9')\n        v = (v * 10) + (*src++ - '0');\n    *output = negative ? -v : v;\n    return src;\n}\n\n// Parse display precision back from the display format string\n// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed.\nint ImParseFormatPrecision(const char* fmt, int default_precision)\n{\n    fmt = ImParseFormatFindStart(fmt);\n    if (fmt[0] != '%')\n        return default_precision;\n    fmt++;\n    while (*fmt >= '0' && *fmt <= '9')\n        fmt++;\n    int precision = INT_MAX;\n    if (*fmt == '.')\n    {\n        fmt = ImAtoi<int>(fmt + 1, &precision);\n        if (precision < 0 || precision > 99)\n            precision = default_precision;\n    }\n    if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation\n        precision = -1;\n    if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX)\n        precision = -1;\n    return (precision == INT_MAX) ? default_precision : precision;\n}\n\n// Create text input in place of another active widget (e.g. used when doing a Ctrl+Click on drag/slider widgets)\n// FIXME: Facilitate using this in variety of other situations.\n// FIXME: Among other things, setting ImGuiItemFlags_AllowDuplicateId in LastItemData is currently correct but\n// the expected relationship between TempInputXXX functions and LastItemData is a little fishy.\nbool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags)\n{\n    // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id.\n    // We clear ActiveID on the first frame to allow the InputText() taking it back.\n    ImGuiContext& g = *GImGui;\n    const bool init = (g.TempInputId != id);\n    if (init)\n        ClearActiveID();\n\n    g.CurrentWindow->DC.CursorPos = bb.Min;\n    g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId;\n    bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem);\n    if (init)\n    {\n        // First frame we started displaying the InputText widget, we expect it to take the active id.\n        IM_ASSERT(g.ActiveId == id);\n        g.TempInputId = g.ActiveId;\n    }\n    return value_changed;\n}\n\n// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set!\n// This is intended: this way we allow Ctrl+Click manual input to set a value out of bounds, for maximum flexibility.\n// However this may not be ideal for all uses, as some user code may break on out of bound values.\nbool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max)\n{\n    // FIXME: May need to clarify display behavior if format doesn't contain %.\n    // \"%d\" -> \"%d\" / \"There are %d items\" -> \"%d\" / \"items\" -> \"%d\" (fallback). Also see #6405\n    ImGuiContext& g = *GImGui;\n    const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type);\n    char fmt_buf[32];\n    char data_buf[32];\n    format = ImParseFormatTrimDecorations(format, fmt_buf, IM_COUNTOF(fmt_buf));\n    if (format[0] == 0)\n        format = type_info->PrintFmt;\n    DataTypeFormatString(data_buf, IM_COUNTOF(data_buf), data_type, p_data, format);\n    ImStrTrimBlanks(data_buf);\n\n    ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;\n    g.LastItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; // Because TempInputText() uses ImGuiInputTextFlags_MergedItem it doesn't submit a new item, so we poke LastItemData.\n    if (!TempInputText(bb, id, label, data_buf, IM_COUNTOF(data_buf), flags))\n        return false;\n\n    // Backup old value\n    size_t data_type_size = type_info->Size;\n    ImGuiDataTypeStorage data_backup;\n    memcpy(&data_backup, p_data, data_type_size);\n\n    // Apply new value (or operations) then clamp\n    DataTypeApplyFromText(data_buf, data_type, p_data, format, NULL);\n    if (p_clamp_min || p_clamp_max)\n    {\n        if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0)\n            ImSwap(p_clamp_min, p_clamp_max);\n        DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max);\n    }\n\n    // Only mark as edited if new value is different\n    g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited;\n    bool value_changed = memcmp(&data_backup, p_data, data_type_size) != 0;\n    if (value_changed)\n        MarkItemEdited(id);\n    return value_changed;\n}\n\nvoid ImGui::SetNextItemRefVal(ImGuiDataType data_type, void* p_data)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasRefVal;\n    memcpy(&g.NextItemData.RefVal, p_data, DataTypeGetInfo(data_type)->Size);\n}\n\n// Note: p_data, p_step, p_step_fast are _pointers_ to a memory address holding the data. For an Input widget, p_step and p_step_fast are optional.\n// Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.\nbool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n    IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()!\n\n    if (format == NULL)\n        format = DataTypeGetInfo(data_type)->PrintFmt;\n\n    void* p_data_default = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasRefVal) ? &g.NextItemData.RefVal : &g.DataTypeZeroValue;\n\n    char buf[64];\n    if ((flags & ImGuiInputTextFlags_DisplayEmptyRefVal) && DataTypeCompare(data_type, p_data, p_data_default) == 0)\n        buf[0] = 0;\n    else\n        DataTypeFormatString(buf, IM_COUNTOF(buf), data_type, p_data, format);\n\n    // Disable the MarkItemEdited() call in InputText but keep ImGuiItemStatusFlags_Edited.\n    // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.\n    g.NextItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited;\n    flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;\n\n    bool value_changed = false;\n    if (p_step == NULL)\n    {\n        if (InputText(label, buf, IM_COUNTOF(buf), flags))\n            value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL);\n    }\n    else\n    {\n        const float button_size = GetFrameHeight();\n\n        BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive()\n        PushID(label);\n        SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));\n        if (InputText(\"\", buf, IM_COUNTOF(buf), flags)) // PushId(label) + \"\" gives us the expected ID from outside point of view\n            value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL);\n        IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);\n\n        // Step buttons\n        const ImVec2 backup_frame_padding = style.FramePadding;\n        style.FramePadding.x = style.FramePadding.y;\n        if (flags & ImGuiInputTextFlags_ReadOnly)\n            BeginDisabled();\n        PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);\n        SameLine(0, style.ItemInnerSpacing.x);\n        if (ButtonEx(\"-\", ImVec2(button_size, button_size)))\n        {\n            DataTypeApplyOp(data_type, '-', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);\n            value_changed = true;\n        }\n        SameLine(0, style.ItemInnerSpacing.x);\n        if (ButtonEx(\"+\", ImVec2(button_size, button_size)))\n        {\n            DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);\n            value_changed = true;\n        }\n        PopItemFlag();\n        if (flags & ImGuiInputTextFlags_ReadOnly)\n            EndDisabled();\n\n        const char* label_end = FindRenderedTextEnd(label);\n        if (label != label_end)\n        {\n            SameLine(0, style.ItemInnerSpacing.x);\n            TextEx(label, label_end);\n        }\n        style.FramePadding = backup_frame_padding;\n\n        PopID();\n        EndGroup();\n    }\n\n    g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited;\n    if (value_changed)\n        MarkItemEdited(g.LastItemData.ID);\n\n    return value_changed;\n}\n\nbool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    bool value_changed = false;\n    BeginGroup();\n    PushID(label);\n    PushMultiItemsWidths(components, CalcItemWidth());\n    size_t type_size = GDataTypeInfo[data_type].Size;\n    for (int i = 0; i < components; i++)\n    {\n        PushID(i);\n        if (i > 0)\n            SameLine(0, g.Style.ItemInnerSpacing.x);\n        value_changed |= InputScalar(\"\", data_type, p_data, p_step, p_step_fast, format, flags);\n        PopID();\n        PopItemWidth();\n        p_data = (void*)((char*)p_data + type_size);\n    }\n    PopID();\n\n    const char* label_end = FindRenderedTextEnd(label);\n    if (label != label_end)\n    {\n        SameLine(0.0f, g.Style.ItemInnerSpacing.x);\n        TextEx(label, label_end);\n    }\n\n    EndGroup();\n    return value_changed;\n}\n\nbool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags)\n{\n    return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), format, flags);\n}\n\nbool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags);\n}\n\nbool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags);\n}\n\nbool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags);\n}\n\nbool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags flags)\n{\n    // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.\n    const char* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? \"%08X\" : \"%d\";\n    return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step > 0 ? &step : NULL), (void*)(step_fast > 0 ? &step_fast : NULL), format, flags);\n}\n\nbool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, \"%d\", flags);\n}\n\nbool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, \"%d\", flags);\n}\n\nbool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, \"%d\", flags);\n}\n\nbool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags)\n{\n    return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step > 0.0 ? &step : NULL), (void*)(step_fast > 0.0 ? &step_fast : NULL), format, flags);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: InputText, InputTextMultiline, InputTextWithHint\n//-------------------------------------------------------------------------\n// - imstb_textedit.h include\n// - InputText()\n// - InputTextWithHint()\n// - InputTextMultiline()\n// - InputTextEx() [Internal]\n// - DebugNodeInputTextState() [Internal]\n//-------------------------------------------------------------------------\n\nnamespace ImStb\n{\n#include \"imstb_textedit.h\"\n}\n\n// If you want to use InputText() with std::string or any custom dynamic string type, use the wrapper in misc/cpp/imgui_stdlib.h/.cpp!\nbool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)\n{\n    IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()\n    return InputTextEx(label, NULL, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);\n}\n\nbool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)\n{\n    return InputTextEx(label, NULL, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);\n}\n\nbool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)\n{\n    IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() or  InputTextEx() manually if you need multi-line + hint.\n    return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);\n}\n\nstatic ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)\n{\n    ImGuiContext& g = *ctx;\n    ImGuiInputTextState* obj = &g.InputTextState;\n    IM_ASSERT(text_end_display >= text_begin && text_end_display <= text_end);\n    return ImFontCalcTextSizeEx(g.Font, g.FontSize, FLT_MAX, obj->WrapWidth, text_begin, text_end_display, text_end, out_remaining, out_offset, flags);\n}\n\n// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)\n// With our UTF-8 use of stb_textedit:\n// - STB_TEXTEDIT_GETCHAR is nothing more than a a \"GETBYTE\". It's only used to compare to ascii or to copy blocks of text so we are fine.\n// - One exception is the STB_TEXTEDIT_IS_SPACE feature which would expect a full char in order to handle full-width space such as 0x3000 (see ImCharIsBlankW).\n// - ...but we don't use that feature.\nnamespace ImStb\n{\nstatic int     STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj)                             { return obj->TextLen; }\nstatic char    STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx >= 0 && idx <= obj->TextLen); return obj->TextSrc[idx]; }\nstatic float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.FontBaked->GetCharAdvance((ImWchar)c) * g.FontBakedScale; }\nstatic char    STB_TEXTEDIT_NEWLINE = '\\n';\nstatic void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)\n{\n    const char* text = obj->TextSrc;\n    const char* text_remaining = NULL;\n    const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, text + obj->TextLen, &text_remaining, NULL, ImDrawTextFlags_StopOnNewLine | ImDrawTextFlags_WrapKeepBlanks);\n    r->x0 = 0.0f;\n    r->x1 = size.x;\n    r->baseline_y_delta = size.y;\n    r->ymin = 0.0f;\n    r->ymax = size.y;\n    r->num_chars = (int)(text_remaining - (text + line_start_idx));\n}\n\n#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX  IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL\n#define IMSTB_TEXTEDIT_GETPREVCHARINDEX  IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL\n\nstatic int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)\n{\n    if (idx >= obj->TextLen)\n        return obj->TextLen + 1;\n    unsigned int c;\n    return idx + ImTextCharFromUtf8(&c, obj->TextSrc + idx, obj->TextSrc + obj->TextLen);\n}\n\nstatic int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)\n{\n    if (idx <= 0)\n        return -1;\n    const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, obj->TextSrc + idx);\n    return (int)(p - obj->TextSrc);\n}\n\nstatic bool ImCharIsSeparatorW(unsigned int c)\n{\n    static const unsigned int separator_list[] =\n    {\n        ',', 0x3001, '.', 0x3002, ';', 0xFF1B, '(', 0xFF08, ')', 0xFF09, '{', 0xFF5B, '}', 0xFF5D,\n        '[', 0x300C, ']', 0x300D, '|', 0xFF5C, '!', 0xFF01, '\\\\', 0xFFE5, '/', 0x30FB, 0xFF0F,\n        '\\n', '\\r',\n    };\n    for (unsigned int separator : separator_list)\n        if (c == separator)\n            return true;\n    return false;\n}\n\nstatic int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)\n{\n    // When ImGuiInputTextFlags_Password is set, we don't want actions such as Ctrl+Arrow to leak the fact that underlying data are blanks or separators.\n    if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)\n        return 0;\n\n    const char* curr_p = obj->TextSrc + idx;\n    const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p);\n    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextSrc + obj->TextLen);\n    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextSrc + obj->TextLen);\n\n    bool prev_white = ImCharIsBlankW(prev_c);\n    bool prev_separ = ImCharIsSeparatorW(prev_c);\n    bool curr_white = ImCharIsBlankW(curr_c);\n    bool curr_separ = ImCharIsSeparatorW(curr_c);\n    return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);\n}\nstatic int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)\n{\n    if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)\n        return 0;\n\n    const char* curr_p = obj->TextSrc + idx;\n    const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p);\n    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextSrc + obj->TextLen);\n    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextSrc + obj->TextLen);\n\n    bool prev_white = ImCharIsBlankW(prev_c);\n    bool prev_separ = ImCharIsSeparatorW(prev_c);\n    bool curr_white = ImCharIsBlankW(curr_c);\n    bool curr_separ = ImCharIsSeparatorW(curr_c);\n    return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);\n}\nstatic int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx)\n{\n    idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx);\n    while (idx >= 0 && !is_word_boundary_from_right(obj, idx))\n        idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx);\n    return idx < 0 ? 0 : idx;\n}\nstatic int  STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx)\n{\n    int len = obj->TextLen;\n    idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);\n    while (idx < len && !is_word_boundary_from_left(obj, idx))\n        idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);\n    return idx > len ? len : idx;\n}\nstatic int  STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx)\n{\n    idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);\n    int len = obj->TextLen;\n    while (idx < len && !is_word_boundary_from_right(obj, idx))\n        idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);\n    return idx > len ? len : idx;\n}\nstatic int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)  { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); }\n#define STB_TEXTEDIT_MOVEWORDLEFT       STB_TEXTEDIT_MOVEWORDLEFT_IMPL  // They need to be #define for stb_textedit.h\n#define STB_TEXTEDIT_MOVEWORDRIGHT      STB_TEXTEDIT_MOVEWORDRIGHT_IMPL\n\n// Reimplementation of stb_textedit_move_line_start()/stb_textedit_move_line_end() which supports word-wrapping.\nstatic int STB_TEXTEDIT_MOVELINESTART_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor)\n{\n    if (state->single_line)\n        return 0;\n\n    if (obj->WrapWidth > 0.0f)\n    {\n        ImGuiContext& g = *obj->Ctx;\n        const char* p_cursor = obj->TextSrc + cursor;\n        const char* p_bol = ImStrbol(p_cursor, obj->TextSrc);\n        const char* p = p_bol;\n        const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough\n        while (p >= p_bol)\n        {\n            const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks);\n            if (p == p_cursor) // If we are already on a visible beginning-of-line, return real beginning-of-line (would be same as regular handler below)\n                return (int)(p_bol - obj->TextSrc);\n            if (p_eol == p_cursor && obj->TextA[cursor] != '\\n' && obj->LastMoveDirectionLR == ImGuiDir_Left)\n                return (int)(p_bol - obj->TextSrc);\n            if (p_eol >= p_cursor)\n                return (int)(p - obj->TextSrc);\n            p = (*p_eol == '\\n') ? p_eol + 1 : p_eol;\n        }\n    }\n\n    // Regular handler, same as stb_textedit_move_line_start()\n    while (cursor > 0)\n    {\n        int prev_cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, cursor);\n        if (STB_TEXTEDIT_GETCHAR(obj, prev_cursor) == STB_TEXTEDIT_NEWLINE)\n            break;\n        cursor = prev_cursor;\n    }\n    return cursor;\n}\n\nstatic int STB_TEXTEDIT_MOVELINEEND_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor)\n{\n    int n = STB_TEXTEDIT_STRINGLEN(obj);\n    if (state->single_line)\n        return n;\n\n    if (obj->WrapWidth > 0.0f)\n    {\n        ImGuiContext& g = *obj->Ctx;\n        const char* p_cursor = obj->TextSrc + cursor;\n        const char* p = ImStrbol(p_cursor, obj->TextSrc);\n        const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough\n        while (p < text_end)\n        {\n            const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks);\n            cursor = (int)(p_eol - obj->TextSrc);\n            if (p_eol == p_cursor && obj->LastMoveDirectionLR != ImGuiDir_Left) // If we are already on a visible end-of-line, switch to regular handle\n                break;\n            if (p_eol > p_cursor)\n                return cursor;\n            p = (*p_eol == '\\n') ? p_eol + 1 : p_eol;\n        }\n    }\n    // Regular handler, same as stb_textedit_move_line_end()\n    while (cursor < n && STB_TEXTEDIT_GETCHAR(obj, cursor) != STB_TEXTEDIT_NEWLINE)\n        cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, cursor);\n    return cursor;\n}\n\n#define STB_TEXTEDIT_MOVELINESTART      STB_TEXTEDIT_MOVELINESTART_IMPL\n#define STB_TEXTEDIT_MOVELINEEND        STB_TEXTEDIT_MOVELINEEND_IMPL\n\nstatic void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)\n{\n    // Offset remaining text (+ copy zero terminator)\n    IM_ASSERT(obj->TextSrc == obj->TextA.Data);\n    char* dst = obj->TextA.Data + pos;\n    char* src = obj->TextA.Data + pos + n;\n    memmove(dst, src, obj->TextLen - n - pos + 1);\n    obj->Edited = true;\n    obj->TextLen -= n;\n}\n\nstatic int STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len)\n{\n    const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;\n    const int text_len = obj->TextLen;\n    IM_ASSERT(pos <= text_len);\n\n    // We support partial insertion (with a mod in stb_textedit.h)\n    const int avail = obj->BufCapacity - 1 - obj->TextLen;\n    if (!is_resizable && new_text_len > avail)\n        new_text_len = (int)(ImTextFindValidUtf8CodepointEnd(new_text, new_text + new_text_len, new_text + avail) - new_text); // Truncate to closest UTF-8 codepoint. Alternative: return 0 to cancel insertion.\n    if (new_text_len == 0)\n        return 0;\n\n    // Grow internal buffer if needed\n    IM_ASSERT(obj->TextSrc == obj->TextA.Data);\n    if (text_len + new_text_len + 1 > obj->TextA.Size && is_resizable)\n    {\n        obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1);\n        obj->TextSrc = obj->TextA.Data;\n    }\n\n    char* text = obj->TextA.Data;\n    if (pos != text_len)\n        memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos));\n    memcpy(text + pos, new_text, (size_t)new_text_len);\n\n    obj->Edited = true;\n    obj->TextLen += new_text_len;\n    obj->TextA[obj->TextLen] = '\\0';\n\n    return new_text_len;\n}\n\n// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols)\n#define STB_TEXTEDIT_K_LEFT         0x200000 // keyboard input to move cursor left\n#define STB_TEXTEDIT_K_RIGHT        0x200001 // keyboard input to move cursor right\n#define STB_TEXTEDIT_K_UP           0x200002 // keyboard input to move cursor up\n#define STB_TEXTEDIT_K_DOWN         0x200003 // keyboard input to move cursor down\n#define STB_TEXTEDIT_K_LINESTART    0x200004 // keyboard input to move cursor to start of line\n#define STB_TEXTEDIT_K_LINEEND      0x200005 // keyboard input to move cursor to end of line\n#define STB_TEXTEDIT_K_TEXTSTART    0x200006 // keyboard input to move cursor to start of text\n#define STB_TEXTEDIT_K_TEXTEND      0x200007 // keyboard input to move cursor to end of text\n#define STB_TEXTEDIT_K_DELETE       0x200008 // keyboard input to delete selection or character under cursor\n#define STB_TEXTEDIT_K_BACKSPACE    0x200009 // keyboard input to delete selection or character left of cursor\n#define STB_TEXTEDIT_K_UNDO         0x20000A // keyboard input to perform undo\n#define STB_TEXTEDIT_K_REDO         0x20000B // keyboard input to perform redo\n#define STB_TEXTEDIT_K_WORDLEFT     0x20000C // keyboard input to move cursor left one word\n#define STB_TEXTEDIT_K_WORDRIGHT    0x20000D // keyboard input to move cursor right one word\n#define STB_TEXTEDIT_K_PGUP         0x20000E // keyboard input to move cursor up a page\n#define STB_TEXTEDIT_K_PGDOWN       0x20000F // keyboard input to move cursor down a page\n#define STB_TEXTEDIT_K_SHIFT        0x400000\n\n#define IMSTB_TEXTEDIT_IMPLEMENTATION\n#define IMSTB_TEXTEDIT_memmove memmove\n#include \"imstb_textedit.h\"\n\n// stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling\n// the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?)\nstatic void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)\n{\n    stb_text_makeundo_replace(str, state, 0, str->TextLen, text_len);\n    ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->TextLen);\n    state->cursor = state->select_start = state->select_end = 0;\n    if (text_len <= 0)\n        return;\n    int text_len_inserted = ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len);\n    if (text_len_inserted > 0)\n    {\n        state->cursor = state->select_start = state->select_end = text_len;\n        state->has_preferred_x = 0;\n        return;\n    }\n    IM_ASSERT(0); // Failed to insert character, normally shouldn't happen because of how we currently use stb_textedit_replace()\n}\n\n} // namespace ImStb\n\n// We added an extra indirection where 'Stb' is heap-allocated, in order facilitate the work of bindings generators.\nImGuiInputTextState::ImGuiInputTextState()\n{\n    memset((void*)this, 0, sizeof(*this));\n    Stb = IM_NEW(ImStbTexteditState);\n    memset(Stb, 0, sizeof(*Stb));\n}\n\nImGuiInputTextState::~ImGuiInputTextState()\n{\n    IM_DELETE(Stb);\n}\n\nvoid ImGuiInputTextState::OnKeyPressed(int key)\n{\n    stb_textedit_key(this, Stb, key);\n    CursorFollow = true;\n    CursorAnimReset();\n    const int key_u = (key & ~STB_TEXTEDIT_K_SHIFT);\n    if (key_u == STB_TEXTEDIT_K_LEFT || key_u == STB_TEXTEDIT_K_LINESTART || key_u == STB_TEXTEDIT_K_TEXTSTART || key_u == STB_TEXTEDIT_K_BACKSPACE || key_u == STB_TEXTEDIT_K_WORDLEFT)\n        LastMoveDirectionLR = ImGuiDir_Left;\n    else if (key_u == STB_TEXTEDIT_K_RIGHT || key_u == STB_TEXTEDIT_K_LINEEND || key_u == STB_TEXTEDIT_K_TEXTEND || key_u == STB_TEXTEDIT_K_DELETE || key_u == STB_TEXTEDIT_K_WORDRIGHT)\n        LastMoveDirectionLR = ImGuiDir_Right;\n}\n\nvoid ImGuiInputTextState::OnCharPressed(unsigned int c)\n{\n    // Convert the key to a UTF8 byte sequence.\n    // The changes we had to make to stb_textedit_key made it very much UTF-8 specific which is not too great.\n    char utf8[5];\n    ImTextCharToUtf8(utf8, c);\n    stb_textedit_text(this, Stb, utf8, (int)ImStrlen(utf8));\n    CursorFollow = true;\n    CursorAnimReset();\n}\n\n// Those functions are not inlined in imgui_internal.h, allowing us to hide ImStbTexteditState from that header.\nvoid ImGuiInputTextState::CursorAnimReset()                 { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking\nvoid ImGuiInputTextState::CursorClamp()                     { Stb->cursor = ImMin(Stb->cursor, TextLen); Stb->select_start = ImMin(Stb->select_start, TextLen); Stb->select_end = ImMin(Stb->select_end, TextLen); }\nbool ImGuiInputTextState::HasSelection() const              { return Stb->select_start != Stb->select_end; }\nvoid ImGuiInputTextState::ClearSelection()                  { Stb->select_start = Stb->select_end = Stb->cursor; }\nint  ImGuiInputTextState::GetCursorPos() const              { return Stb->cursor; }\nint  ImGuiInputTextState::GetSelectionStart() const         { return Stb->select_start; }\nint  ImGuiInputTextState::GetSelectionEnd() const           { return Stb->select_end; }\nvoid ImGuiInputTextState::SetSelection(int start, int end)  { Stb->select_start = start; Stb->cursor = Stb->select_end = end; }\nfloat ImGuiInputTextState::GetPreferredOffsetX() const      { return Stb->has_preferred_x ? Stb->preferred_x : -1; }\nvoid ImGuiInputTextState::SelectAll()                       { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; }\nvoid ImGuiInputTextState::ReloadUserBufAndSelectAll()       { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }\nvoid ImGuiInputTextState::ReloadUserBufAndKeepSelection()   { WantReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }\nvoid ImGuiInputTextState::ReloadUserBufAndMoveToEnd()       { WantReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }\n\nImGuiInputTextCallbackData::ImGuiInputTextCallbackData()\n{\n    memset((void*)this, 0, sizeof(*this));\n}\n\n// Public API to manipulate UTF-8 text from within a callback.\n// FIXME: The existence of this rarely exercised code path is a bit of a nuisance.\n// Historically they existed because STB_TEXTEDIT_INSERTCHARS() etc. worked on our ImWchar\n// buffer, but nowadays they both work on UTF-8 data. Should aim to merge both.\nvoid ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count)\n{\n    IM_ASSERT(pos + bytes_count <= BufTextLen);\n    char* dst = Buf + pos;\n    const char* src = Buf + pos + bytes_count;\n    memmove(dst, src, BufTextLen - bytes_count - pos + 1);\n\n    if (CursorPos >= pos + bytes_count)\n        CursorPos -= bytes_count;\n    else if (CursorPos >= pos)\n        CursorPos = pos;\n    SelectionStart = SelectionEnd = CursorPos;\n    BufDirty = true;\n    BufTextLen -= bytes_count;\n}\n\nvoid ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)\n{\n    // Accept null ranges\n    if (new_text == new_text_end)\n        return;\n\n    ImGuiContext& g = *Ctx;\n    ImGuiInputTextState* obj = &g.InputTextState;\n    IM_ASSERT(obj->ID != 0 && g.ActiveId == obj->ID);\n    const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;\n    const bool is_readonly = (Flags & ImGuiInputTextFlags_ReadOnly) != 0;\n    int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)ImStrlen(new_text);\n\n    // We support partial insertion (with a mod in stb_textedit.h)\n    const int avail = BufSize - 1 - BufTextLen;\n    if (!is_resizable && new_text_len > avail)\n        new_text_len = (int)(ImTextFindValidUtf8CodepointEnd(new_text, new_text + new_text_len, new_text + avail) - new_text); // Truncate to closest UTF-8 codepoint. Alternative: return 0 to cancel insertion.\n    if (new_text_len == 0)\n        return;\n\n    // Grow internal buffer if needed\n    if (new_text_len + BufTextLen + 1 > obj->TextA.Size && is_resizable && !is_readonly)\n    {\n        IM_ASSERT(Buf == obj->TextA.Data);\n        int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;\n        obj->TextA.resize(new_buf_size + 1);\n        obj->TextSrc = obj->TextA.Data;\n        Buf = obj->TextA.Data;\n        BufSize = obj->BufCapacity = new_buf_size;\n    }\n\n    if (BufTextLen != pos)\n        memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));\n    memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));\n    Buf[BufTextLen + new_text_len] = '\\0';\n\n    BufDirty = true;\n    BufTextLen += new_text_len;\n    if (CursorPos >= pos)\n        CursorPos += new_text_len;\n    CursorPos = ImClamp(CursorPos, 0, BufTextLen);\n    SelectionStart = SelectionEnd = CursorPos;\n}\n\nvoid ImGui::PushPasswordFont()\n{\n    ImGuiContext& g = *GImGui;\n    ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked;\n    IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0);\n    ImFontGlyph* glyph = g.FontBaked->FindGlyph('*');\n    g.InputTextPasswordFontBackupFlags = g.Font->Flags;\n    backup->FallbackGlyphIndex = g.FontBaked->FallbackGlyphIndex;\n    backup->FallbackAdvanceX = g.FontBaked->FallbackAdvanceX;\n    backup->IndexLookup.swap(g.FontBaked->IndexLookup);\n    backup->IndexAdvanceX.swap(g.FontBaked->IndexAdvanceX);\n    g.Font->Flags |= ImFontFlags_NoLoadGlyphs;\n    g.FontBaked->FallbackGlyphIndex = g.FontBaked->Glyphs.index_from_ptr(glyph);\n    g.FontBaked->FallbackAdvanceX = glyph->AdvanceX;\n}\n\nvoid ImGui::PopPasswordFont()\n{\n    ImGuiContext& g = *GImGui;\n    ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked;\n    g.Font->Flags = g.InputTextPasswordFontBackupFlags;\n    g.FontBaked->FallbackGlyphIndex = backup->FallbackGlyphIndex;\n    g.FontBaked->FallbackAdvanceX = backup->FallbackAdvanceX;\n    g.FontBaked->IndexLookup.swap(backup->IndexLookup);\n    g.FontBaked->IndexAdvanceX.swap(backup->IndexAdvanceX);\n    IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0);\n}\n\n// Return false to discard a character.\nstatic bool InputTextFilterCharacter(ImGuiContext* ctx, ImGuiInputTextState* state, unsigned int* p_char, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard)\n{\n    unsigned int c = *p_char;\n    ImGuiInputTextFlags flags = state->Flags;\n\n    // Filter non-printable (NB: isprint is unreliable! see #2467)\n    bool apply_named_filters = true;\n    if (c < 0x20)\n    {\n        bool pass = false;\n        pass |= (c == '\\n') && (flags & ImGuiInputTextFlags_Multiline) != 0;    // Note that an Enter KEY will emit \\r and be ignored (we poll for KEY in InputText() code)\n        if (c == '\\n' && input_source_is_clipboard && (flags & ImGuiInputTextFlags_Multiline) == 0) // In single line mode, replace \\n with a space\n        {\n            c = *p_char = ' ';\n            pass = true;\n        }\n        pass |= (c == '\\n') && (flags & ImGuiInputTextFlags_Multiline) != 0;\n        pass |= (c == '\\t') && (flags & ImGuiInputTextFlags_AllowTabInput) != 0;\n        if (!pass)\n            return false;\n        apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted.\n    }\n\n    if (input_source_is_clipboard == false)\n    {\n        // We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817)\n        if (c == 127)\n            return false;\n\n        // Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME)\n        if (c >= 0xE000 && c <= 0xF8FF)\n            return false;\n    }\n\n    // Filter Unicode ranges we are not handling in this build\n    if (c > IM_UNICODE_CODEPOINT_MAX)\n        return false;\n\n    // Generic named filters\n    if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint)))\n    {\n        // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, \"de_DE.UTF-8\");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'.\n        // The standard mandate that programs starts in the \"C\" locale where the decimal point is '.'.\n        // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point.\n        // Change the default decimal_point with:\n        //   ImGui::GetPlatformIO()->Platform_LocaleDecimalPoint = *localeconv()->decimal_point;\n        // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions.\n        ImGuiContext& g = *ctx;\n        const unsigned c_decimal_point = (unsigned int)g.PlatformIO.Platform_LocaleDecimalPoint;\n        if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint))\n            if (c == '.' || c == ',')\n                c = c_decimal_point;\n\n        // Full-width -> half-width conversion for numeric fields: https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)\n        // While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may\n        // scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font.\n        if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal))\n            if (c >= 0xFF01 && c <= 0xFF5E)\n                c = c - 0xFF01 + 0x21;\n\n        // Allow 0-9 . - + * /\n        if (flags & ImGuiInputTextFlags_CharsDecimal)\n            if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/'))\n                return false;\n\n        // Allow 0-9 . - + * / e E\n        if (flags & ImGuiInputTextFlags_CharsScientific)\n            if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E'))\n                return false;\n\n        // Allow 0-9 a-F A-F\n        if (flags & ImGuiInputTextFlags_CharsHexadecimal)\n            if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))\n                return false;\n\n        // Turn a-z into A-Z\n        if (flags & ImGuiInputTextFlags_CharsUppercase)\n            if (c >= 'a' && c <= 'z')\n                c += (unsigned int)('A' - 'a');\n\n        if (flags & ImGuiInputTextFlags_CharsNoBlank)\n            if (ImCharIsBlankW(c))\n                return false;\n\n        *p_char = c;\n    }\n\n    // Custom callback filter\n    if (flags & ImGuiInputTextFlags_CallbackCharFilter)\n    {\n        ImGuiContext& g = *GImGui;\n        ImGuiInputTextCallbackData callback_data;\n        callback_data.Ctx = &g;\n        callback_data.ID = state->ID;\n        callback_data.Flags = flags;\n        callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;\n        callback_data.EventChar = (ImWchar)c;\n        callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated);\n        callback_data.UserData = user_data;\n        if (callback(&callback_data) != 0)\n            return false;\n        *p_char = callback_data.EventChar;\n        if (!callback_data.EventChar)\n            return false;\n    }\n\n    return true;\n}\n\n// Find the shortest single replacement we can make to get from old_buf to new_buf\n// Note that this doesn't directly alter state->TextA, state->TextLen. They are expected to be made valid separately.\n// FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly.\nstatic void InputTextReconcileUndoState(ImGuiInputTextState* state, const char* old_buf, int old_length, const char* new_buf, int new_length)\n{\n    const int shorter_length = ImMin(old_length, new_length);\n    int first_diff;\n    for (first_diff = 0; first_diff < shorter_length; first_diff++)\n        if (old_buf[first_diff] != new_buf[first_diff])\n            break;\n    if (first_diff == old_length && first_diff == new_length)\n        return;\n\n    int old_last_diff = old_length   - 1;\n    int new_last_diff = new_length - 1;\n    for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--)\n        if (old_buf[old_last_diff] != new_buf[new_last_diff])\n            break;\n\n    const int insert_len = new_last_diff - first_diff + 1;\n    const int delete_len = old_last_diff - first_diff + 1;\n    if (insert_len > 0 || delete_len > 0)\n        if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb->undostate, first_diff, delete_len, insert_len))\n            for (int i = 0; i < delete_len; i++)\n                p[i] = old_buf[first_diff + i];\n}\n\n// As InputText() retain textual data and we currently provide a path for user to not retain it (via local variables)\n// we need some form of hook to reapply data back to user buffer on deactivation frame. (#4714)\n// It would be more desirable that we discourage users from taking advantage of the \"user not retaining data\" trick,\n// but that more likely be attractive when we do have _NoLiveEdit flag available.\nvoid ImGui::InputTextDeactivateHook(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiInputTextState* state = &g.InputTextState;\n    if (id == 0 || state->ID != id)\n        return;\n    g.InputTextDeactivatedState.ID = state->ID;\n    if (state->Flags & ImGuiInputTextFlags_ReadOnly)\n    {\n        g.InputTextDeactivatedState.TextA.resize(0); // In theory this data won't be used, but clear to be neat.\n    }\n    else\n    {\n        IM_ASSERT(state->TextA.Data != 0);\n        IM_ASSERT(state->TextA[state->TextLen] == 0);\n        g.InputTextDeactivatedState.TextA.resize(state->TextLen + 1);\n        memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->TextLen + 1);\n    }\n}\n\nstatic int* ImLowerBound(int* in_begin, int* in_end, int v)\n{\n    int* in_p = in_begin;\n    for (size_t count = (size_t)(in_end - in_p); count > 0; )\n    {\n        size_t count2 = count >> 1;\n        int* mid = in_p + count2;\n        if (*mid < v)\n        {\n            in_p = ++mid;\n            count -= count2 + 1;\n        }\n        else\n        {\n            count = count2;\n        }\n    }\n    return in_p;\n}\n\n// FIXME-WORDWRAP: Bundle some of this into ImGuiTextIndex and/or extract as a different tool?\n// 'max_output_buffer_size' happens to be a meaningful optimization to avoid writing the full line_index when not necessarily needed (e.g. very large buffer, scrolled up, inactive)\nstatic int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, float wrap_width, int max_output_buffer_size, const char** out_buf_end)\n{\n    ImGuiContext& g = *GImGui;\n    int size = 0;\n    const char* s;\n    if (flags & ImGuiInputTextFlags_WordWrap)\n    {\n        for (s = buf; s < buf_end; s = (*s == '\\n') ? s + 1 : s)\n        {\n            if (size++ <= max_output_buffer_size)\n                line_index->Offsets.push_back((int)(s - buf));\n            s = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, s, buf_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);\n        }\n    }\n    else if (buf_end != NULL)\n    {\n        for (s = buf; s < buf_end; s = s ? s + 1 : buf_end)\n        {\n            if (size++ <= max_output_buffer_size)\n                line_index->Offsets.push_back((int)(s - buf));\n            s = (const char*)ImMemchr(s, '\\n', buf_end - s);\n        }\n    }\n    else\n    {\n        const char* s_eol;\n        for (s = buf; ; s = s_eol + 1)\n        {\n            if (size++ <= max_output_buffer_size)\n                line_index->Offsets.push_back((int)(s - buf));\n            if ((s_eol = strchr(s, '\\n')) != NULL)\n                continue;\n            s += strlen(s);\n            break;\n        }\n    }\n    if (out_buf_end != NULL)\n        *out_buf_end = buf_end = s;\n    if (size == 0)\n    {\n        line_index->Offsets.push_back(0);\n        size++;\n    }\n    if (buf_end > buf && buf_end[-1] == '\\n' && size <= max_output_buffer_size)\n    {\n        line_index->Offsets.push_back((int)(buf_end - buf));\n        size++;\n    }\n    return size;\n}\n\nstatic ImVec2 InputTextLineIndexGetPosOffset(ImGuiContext& g, ImGuiInputTextState* state, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, int cursor_n)\n{\n    const char* cursor_ptr = buf + cursor_n;\n    int* it_begin = line_index->Offsets.begin();\n    int* it_end = line_index->Offsets.end();\n    const int* it = ImLowerBound(it_begin, it_end, cursor_n);\n    if (it > it_begin)\n        if (it == it_end || *it != cursor_n || (state != NULL && state->WrapWidth > 0.0f && state->LastMoveDirectionLR == ImGuiDir_Right && cursor_ptr[-1] != '\\n' && cursor_ptr[-1] != 0))\n            it--;\n\n    const int line_no = (it == it_begin) ? 0 : line_index->Offsets.index_from_ptr(it);\n    const char* line_start = line_index->get_line_begin(buf, line_no);\n    ImVec2 offset;\n    offset.x = InputTextCalcTextSize(&g, line_start, cursor_ptr, buf_end, NULL, NULL, ImDrawTextFlags_WrapKeepBlanks).x;\n    offset.y = (line_no + 1) * g.FontSize;\n    return offset;\n}\n\n// Edit a string of text\n// - buf_size account for the zero-terminator, so a buf_size of 6 can hold \"Hello\" but not \"Hello!\".\n//   This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match\n//   Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator.\n// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect.\n// - If you want to use InputText() with std::string or any custom dynamic string type, use the wrapper in misc/cpp/imgui_stdlib.h/.cpp!\nbool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    IM_ASSERT(buf != NULL && buf_size >= 0);\n    IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline)));        // Can't use both together (they both use up/down keys)\n    IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)\n    IM_ASSERT(!((flags & ImGuiInputTextFlags_ElideLeft) && (flags & ImGuiInputTextFlags_Multiline)));              // Multiline does not not work with left-trimming\n    IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Password) == 0);         // WordWrap does not work with Password mode.\n    IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Multiline) != 0);        // WordWrap does not work in single-line mode.\n\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n    const ImGuiStyle& style = g.Style;\n\n    const bool RENDER_SELECTION_WHEN_INACTIVE = false;\n    const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;\n\n    if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar)\n        BeginGroup();\n    const ImGuiID id = window->GetID(label);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f); // Arbitrary default of 8 lines high for multi-line\n    const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y);\n\n    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);\n    const ImRect total_bb(frame_bb.Min, frame_bb.Min + total_size);\n\n    ImGuiWindow* draw_window = window;\n    ImVec2 inner_size = frame_size;\n    ImGuiLastItemData item_data_backup;\n    if (is_multiline)\n    {\n        ImVec2 backup_pos = window->DC.CursorPos;\n        ItemSize(total_bb, style.FramePadding.y);\n        if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable))\n        {\n            EndGroup();\n            return false;\n        }\n        item_data_backup = g.LastItemData;\n        window->DC.CursorPos = backup_pos;\n\n        // Prevent NavActivation from explicit Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping.\n        if (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && !(g.NavActivateFlags & ImGuiActivateFlags_FromFocusApi) && (flags & ImGuiInputTextFlags_AllowTabInput))\n            g.NavActivateId = 0;\n\n        // Prevent NavActivate reactivating in BeginChild() when we are already active.\n        const ImGuiID backup_activate_id = g.NavActivateId;\n        if (g.ActiveId == id) // Prevent reactivation\n            g.NavActivateId = 0;\n\n        // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug.\n        PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);\n        PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);\n        PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);\n        PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges\n        bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove);\n        g.NavActivateId = backup_activate_id;\n        PopStyleVar(3);\n        PopStyleColor();\n        if (!child_visible)\n        {\n            EndChild();\n            EndGroup();\n            return false;\n        }\n        draw_window = g.CurrentWindow; // Child window\n        draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can \"enter\" into it.\n        draw_window->DC.CursorPos += style.FramePadding;\n        inner_size.x -= draw_window->ScrollbarSizes.x;\n    }\n    else\n    {\n        // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd)\n        ItemSize(total_bb, style.FramePadding.y);\n        if (!(flags & ImGuiInputTextFlags_MergedItem))\n            if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable))\n                return false;\n    }\n\n    // Ensure mouse cursor is set even after switching to keyboard/gamepad mode. May generalize further? (#6417)\n    bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags | ImGuiItemFlags_NoNavDisableMouseHover);\n    if (hovered)\n        SetMouseCursor(ImGuiMouseCursor_TextInput);\n    if (hovered && g.NavHighlightItemUnderNav)\n        hovered = false;\n\n    // We are only allowed to access the state if we are already the active widget.\n    ImGuiInputTextState* state = GetInputTextState(id);\n\n    if (g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly)\n        flags |= ImGuiInputTextFlags_ReadOnly;\n    const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0;\n    const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;\n    const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;\n    const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;\n    if (is_resizable)\n        IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag!\n\n    // Word-wrapping: enforcing a fixed width not altered by vertical scrollbar makes things easier, notably to track cursor reliably and avoid one-frame glitches.\n    // Instead of using ImGuiWindowFlags_AlwaysVerticalScrollbar we account for that space if the scrollbar is not visible.\n    const bool is_wordwrap = (flags & ImGuiInputTextFlags_WordWrap) != 0;\n    float wrap_width = 0.0f;\n    if (is_wordwrap)\n        wrap_width = ImMax(1.0f, GetContentRegionAvail().x + (draw_window->ScrollbarY ? 0.0f : -g.Style.ScrollbarSize));\n\n    const bool input_requested_by_nav = (g.ActiveId != id) && (g.NavActivateId == id);\n    const bool input_requested_by_reactivate = (g.InputTextReactivateId == id); // for io.ConfigInputTextEnterKeepActive\n    const bool user_clicked = hovered && io.MouseClicked[0];\n    const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);\n    const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);\n    bool clear_active_id = false;\n    bool select_all = false;\n\n    float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;\n\n    const bool init_reload_from_user_buf = (state != NULL && state->WantReloadUserBuf);\n    const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.\n    const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_reactivate);\n    const bool init_state = (init_make_active || user_scroll_active);\n    if (init_reload_from_user_buf)\n    {\n        int new_len = (int)ImStrlen(buf);\n        IM_ASSERT(new_len + 1 <= buf_size && \"Is your input buffer properly zero-terminated?\");\n        state->WantReloadUserBuf = false;\n        InputTextReconcileUndoState(state, state->TextA.Data, state->TextLen, buf, new_len);\n        state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.\n        state->TextLen = new_len;\n        memcpy(state->TextA.Data, buf, state->TextLen + 1);\n        state->Stb->select_start = state->ReloadSelectionStart;\n        state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; // will be clamped to bounds below\n    }\n    else if ((init_state && g.ActiveId != id) || init_changed_specs)\n    {\n        // Access state even if we don't own it yet.\n        state = &g.InputTextState;\n        state->CursorAnimReset();\n\n        // Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714)\n        InputTextDeactivateHook(state->ID);\n\n        // Take a copy of the initial buffer value.\n        // From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode)\n        const int buf_len = (int)ImStrlen(buf);\n        IM_ASSERT(((buf_len + 1 <= buf_size) || (buf_len == 0 && buf_size == 0)) && \"Is your input buffer properly zero-terminated?\");\n        state->TextToRevertTo.resize(buf_len + 1);    // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.\n        memcpy(state->TextToRevertTo.Data, buf, buf_len + 1);\n\n        // Preserve cursor position and undo/redo stack if we come back to same widget\n        // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?\n        bool recycle_state = (state->ID == id && !init_changed_specs);\n        if (recycle_state && (state->TextLen != buf_len || (state->TextA.Data == NULL || strncmp(state->TextA.Data, buf, buf_len) != 0)))\n            recycle_state = false;\n\n        // Start edition\n        state->ID = id;\n        state->TextLen = buf_len;\n        if (!is_readonly)\n        {\n            state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.\n            memcpy(state->TextA.Data, buf, state->TextLen + 1);\n        }\n\n        // Find initial scroll position for right alignment\n        state->Scroll = ImVec2(0.0f, 0.0f);\n        if (flags & ImGuiInputTextFlags_ElideLeft)\n            state->Scroll.x += ImMax(0.0f, CalcTextSize(buf).x - frame_size.x + style.FramePadding.x * 2.0f);\n\n        // Recycle existing cursor/selection/undo stack but clamp position\n        // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.\n        if (!recycle_state)\n            stb_textedit_initialize_state(state->Stb, !is_multiline);\n\n        if (!is_multiline)\n        {\n            if (flags & ImGuiInputTextFlags_AutoSelectAll)\n                select_all = true;\n            if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState)))\n                select_all = true;\n            if (user_clicked && io.KeyCtrl)\n                select_all = true;\n        }\n\n        if (flags & ImGuiInputTextFlags_AlwaysOverwrite)\n            state->Stb->insert_mode = 1; // stb field name is indeed incorrect (see #2863)\n    }\n\n    const bool is_osx = io.ConfigMacOSXBehaviors;\n    if (g.ActiveId != id && init_make_active)\n    {\n        IM_ASSERT(state && state->ID == id);\n        SetActiveID(id, window);\n        SetFocusID(id, window);\n        FocusWindow(window);\n        if (input_requested_by_nav)\n            SetNavCursorVisibleAfterMove();\n    }\n    if (g.ActiveId == id)\n    {\n        // Declare some inputs, the other are registered and polled via Shortcut() routing system.\n        // FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combination into individual shortcuts.\n        const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End };\n        for (ImGuiKey key : always_owned_keys)\n            SetKeyOwner(key, id);\n        if (user_clicked)\n            SetKeyOwner(ImGuiKey_MouseLeft, id);\n        g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);\n        if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory))\n        {\n            g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);\n            SetKeyOwner(ImGuiKey_UpArrow, id);\n            SetKeyOwner(ImGuiKey_DownArrow, id);\n        }\n        if (is_multiline)\n        {\n            SetKeyOwner(ImGuiKey_PageUp, id);\n            SetKeyOwner(ImGuiKey_PageDown, id);\n        }\n        // FIXME: May be a problem to always steal Alt on OSX, would ideally still allow an uninterrupted Alt down-up to toggle menu\n        if (is_osx)\n            SetKeyOwner(ImGuiMod_Alt, id);\n\n        // Expose scroll in a manner that is agnostic to us using a child window\n        if (is_multiline && state != NULL)\n            state->Scroll.y = draw_window->Scroll.y;\n\n        // Read-only mode always ever read from source buffer. Refresh TextLen when active.\n        if (is_readonly && state != NULL)\n            state->TextLen = (int)ImStrlen(buf);\n        if (state != NULL)\n            state->CursorClamp();\n        //if (is_readonly && state != NULL)\n        //    state->TextA.clear(); // Uncomment to facilitate debugging, but we otherwise prefer to keep/amortize th allocation.\n    }\n    if (state != NULL)\n        state->TextSrc = is_readonly ? buf : state->TextA.Data;\n\n    // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)\n    if (g.ActiveId == id && state == NULL)\n        ClearActiveID();\n\n    // Release focus when we click outside\n    if (g.ActiveId == id && io.MouseClicked[0] && !init_state && !init_make_active) //-V560\n        clear_active_id = true;\n\n    // Lock the decision of whether we are going to take the path displaying the cursor or selection\n    bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active);\n    bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor);\n    bool value_changed = false;\n    bool validated = false;\n\n    // Select the buffer to render.\n    const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state;\n    bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);\n\n    // Password pushes a temporary font with only a fallback glyph\n    if (is_password && !is_displaying_hint)\n        PushPasswordFont();\n\n    // Word-wrapping: attempt to keep cursor in view while resizing frame/parent\n    // FIXME-WORDWRAP: It would be better to preserve same relative offset.\n    if (is_wordwrap && state != NULL && state->ID == id && state->WrapWidth != wrap_width)\n    {\n        state->CursorCenterY = true;\n        state->WrapWidth = wrap_width;\n        render_cursor = true;\n    }\n\n    // Process mouse inputs and character inputs\n    if (g.ActiveId == id)\n    {\n        IM_ASSERT(state != NULL);\n        state->Edited = false;\n        state->BufCapacity = buf_size;\n        state->Flags = flags;\n        state->WrapWidth = wrap_width;\n\n        // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.\n        // Down the line we should have a cleaner library-wide concept of Selected vs Active.\n        g.ActiveIdAllowOverlap = !io.MouseDown[0];\n\n        // Edit in progress\n        const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->Scroll.x;\n        const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f));\n\n        if (select_all)\n        {\n            state->SelectAll();\n            state->SelectedAllMouseLock = true;\n        }\n        else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift)\n        {\n            stb_textedit_click(state, state->Stb, mouse_x, mouse_y);\n            const int multiclick_count = (io.MouseClickedCount[0] - 2);\n            if ((multiclick_count % 2) == 0)\n            {\n                // Double-click: Select word\n                // We always use the \"Mac\" word advance for double-click select vs Ctrl+Right which use the platform dependent variant:\n                // FIXME: There are likely many ways to improve this behavior, but there's no \"right\" behavior (depends on use-case, software, OS)\n                const bool is_bol = (state->Stb->cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor - 1) == '\\n';\n                if (STB_TEXT_HAS_SELECTION(state->Stb) || !is_bol)\n                    state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);\n                //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);\n                if (!STB_TEXT_HAS_SELECTION(state->Stb))\n                    ImStb::stb_textedit_prep_selection_at_cursor(state->Stb);\n                state->Stb->cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb->cursor);\n                state->Stb->select_end = state->Stb->cursor;\n                ImStb::stb_textedit_clamp(state, state->Stb);\n            }\n            else\n            {\n                // Triple-click: Select line\n                const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) == '\\n';\n                state->WrapWidth = 0.0f; // Temporarily disable wrapping so we use real line start.\n                state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART);\n                state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT);\n                state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT);\n                state->WrapWidth = wrap_width;\n                if (!is_eol && is_multiline)\n                {\n                    ImSwap(state->Stb->select_start, state->Stb->select_end);\n                    state->Stb->cursor = state->Stb->select_end;\n                }\n                state->CursorFollow = false;\n            }\n            state->CursorAnimReset();\n        }\n        else if (io.MouseClicked[0] && !state->SelectedAllMouseLock)\n        {\n            if (hovered)\n            {\n                if (io.KeyShift)\n                    stb_textedit_drag(state, state->Stb, mouse_x, mouse_y);\n                else\n                    stb_textedit_click(state, state->Stb, mouse_x, mouse_y);\n                state->CursorAnimReset();\n            }\n        }\n        else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))\n        {\n            stb_textedit_drag(state, state->Stb, mouse_x, mouse_y);\n            state->CursorAnimReset();\n            state->CursorFollow = true;\n        }\n        if (state->SelectedAllMouseLock && !io.MouseDown[0])\n            state->SelectedAllMouseLock = false;\n\n        // We expect backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336)\n        // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes)\n        if ((flags & ImGuiInputTextFlags_AllowTabInput) && !is_readonly)\n        {\n            if (Shortcut(ImGuiKey_Tab, ImGuiInputFlags_Repeat, id))\n            {\n                unsigned int c = '\\t'; // Insert TAB\n                if (InputTextFilterCharacter(&g, state, &c, callback, callback_user_data))\n                    state->OnCharPressed(c);\n            }\n            // FIXME: Implement Shift+Tab\n            /*\n            if (Shortcut(ImGuiKey_Tab | ImGuiMod_Shift, ImGuiInputFlags_Repeat, id))\n            {\n            }\n            */\n        }\n\n        // Process regular text input (before we check for Return because using some IME will effectively send a Return?)\n        // We ignore Ctrl inputs, but need to allow Alt+Ctrl as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.\n        const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeyCtrl);\n        if (io.InputQueueCharacters.Size > 0)\n        {\n            if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav)\n                for (int n = 0; n < io.InputQueueCharacters.Size; n++)\n                {\n                    // Insert character if they pass filtering\n                    unsigned int c = (unsigned int)io.InputQueueCharacters[n];\n                    if (c == '\\t') // Skip Tab, see above.\n                        continue;\n                    if (InputTextFilterCharacter(&g, state, &c, callback, callback_user_data))\n                        state->OnCharPressed(c);\n                }\n\n            // Consume characters\n            io.InputQueueCharacters.resize(0);\n        }\n    }\n\n    // Process other shortcuts/key-presses\n    bool revert_edit = false;\n    if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)\n    {\n        IM_ASSERT(state != NULL);\n\n        const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1);\n        state->Stb->row_count_per_page = row_count_per_page;\n\n        const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);\n        const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl;                     // OS X style: Text editing cursor movement using Alt instead of Ctrl\n        const bool is_startend_key_down = is_osx && io.KeyCtrl && !io.KeySuper && !io.KeyAlt;  // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End\n\n        // Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use Ctrl+A and Ctrl+B: former would be handled by InputText)\n        // Otherwise we could simply assume that we own the keys as we are active.\n        const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat;\n        const bool is_cut   = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_X, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, f_repeat, id)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());\n        const bool is_copy  = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, 0,        id) || Shortcut(ImGuiMod_Ctrl  | ImGuiKey_Insert, 0,        id)) && !is_password && (!is_multiline || state->HasSelection());\n        const bool is_paste = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_V, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, f_repeat, id)) && !is_readonly;\n        const bool is_undo  = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable;\n        const bool is_redo =  (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y, f_repeat, id) || Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable;\n        const bool is_select_all = Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, 0, id);\n\n        // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.\n        const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;\n        const bool is_enter = Shortcut(ImGuiKey_Enter, f_repeat, id) || Shortcut(ImGuiKey_KeypadEnter, f_repeat, id);\n        const bool is_ctrl_enter = Shortcut(ImGuiMod_Ctrl | ImGuiKey_Enter, f_repeat, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_KeypadEnter, f_repeat, id);\n        const bool is_shift_enter = Shortcut(ImGuiMod_Shift | ImGuiKey_Enter, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_KeypadEnter, f_repeat, id);\n        const bool is_gamepad_validate = nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, false);\n        const bool is_cancel = Shortcut(ImGuiKey_Escape, f_repeat, id) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, f_repeat, id));\n\n        // FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of.\n        // FIXME-OSX: Missing support for Alt(option)+Right/Left = go to end of line, or next line if already in end of line.\n        if (IsKeyPressed(ImGuiKey_LeftArrow))                        { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_RightArrow))                  { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline)     { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline)   { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline)      { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; }\n        else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline)    { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; }\n        else if (IsKeyPressed(ImGuiKey_Home))                        { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_End))                         { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut)\n        {\n            if (!state->HasSelection())\n            {\n                // OSX doesn't seem to have Super+Delete to delete until end-of-line, so we don't emulate that (as opposed to Super+Backspace)\n                if (is_wordmove_key_down)\n                    state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);\n            }\n            state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask);\n        }\n        else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly)\n        {\n            if (!state->HasSelection())\n            {\n                if (is_wordmove_key_down)\n                    state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT);\n                else if (is_osx && io.KeyCtrl && !io.KeyAlt && !io.KeySuper)\n                    state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT);\n            }\n            state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);\n        }\n        else if (is_enter || is_ctrl_enter || is_shift_enter || is_gamepad_validate)\n        {\n            // Determine if we turn Enter into a \\n character\n            bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;\n            bool is_new_line = is_multiline && !is_gamepad_validate && (is_shift_enter || (is_enter && !ctrl_enter_for_new_line) || (is_ctrl_enter && ctrl_enter_for_new_line));\n            if (!is_new_line)\n            {\n                validated = clear_active_id = true;\n                if (io.ConfigInputTextEnterKeepActive && !is_multiline)\n                {\n                    // Queue reactivation, so that e.g. IsItemDeactivatedAfterEdit() will work. (#9001)\n                    state->SelectAll(); // No need to scroll\n                    g.InputTextReactivateId = id; // Mark for reactivation on next frame\n                }\n            }\n            else if (!is_readonly)\n            {\n                // Insert new line\n                unsigned int c = '\\n';\n                if (InputTextFilterCharacter(&g, state, &c, callback, callback_user_data))\n                    state->OnCharPressed(c);\n            }\n        }\n        else if (is_cancel)\n        {\n            if (flags & ImGuiInputTextFlags_EscapeClearsAll)\n            {\n                if (state->TextA.Data[0] != 0)\n                {\n                    revert_edit = true;\n                }\n                else\n                {\n                    render_cursor = render_selection = false;\n                    clear_active_id = true;\n                }\n            }\n            else\n            {\n                clear_active_id = revert_edit = true;\n                render_cursor = render_selection = false;\n            }\n        }\n        else if (is_undo || is_redo)\n        {\n            state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO);\n            state->ClearSelection();\n        }\n        else if (is_select_all)\n        {\n            state->SelectAll();\n            state->CursorFollow = true;\n        }\n        else if (is_cut || is_copy)\n        {\n            // Cut, Copy\n            if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)\n            {\n                // SetClipboardText() only takes null terminated strings + state->TextSrc may point to read-only user buffer, so we need to make a copy.\n                const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0;\n                const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->TextLen;\n                g.TempBuffer.reserve(ie - ib + 1);\n                memcpy(g.TempBuffer.Data, state->TextSrc + ib, ie - ib);\n                g.TempBuffer.Data[ie - ib] = 0;\n                SetClipboardText(g.TempBuffer.Data);\n            }\n            if (is_cut)\n            {\n                if (!state->HasSelection())\n                    state->SelectAll();\n                state->CursorFollow = true;\n                stb_textedit_cut(state, state->Stb);\n            }\n        }\n        else if (is_paste)\n        {\n            if (const char* clipboard = GetClipboardText())\n            {\n                // Filter pasted buffer\n                const int clipboard_len = (int)ImStrlen(clipboard);\n                const char* clipboard_end = clipboard + clipboard_len;\n                ImVector<char> clipboard_filtered;\n                clipboard_filtered.reserve(clipboard_len + 1);\n                for (const char* s = clipboard; *s != 0; )\n                {\n                    unsigned int c;\n                    int in_len = ImTextCharFromUtf8(&c, s, clipboard_end);\n                    s += in_len;\n                    if (!InputTextFilterCharacter(&g, state, &c, callback, callback_user_data, true))\n                        continue;\n                    char c_utf8[5];\n                    ImTextCharToUtf8(c_utf8, c);\n                    int out_len = (int)ImStrlen(c_utf8);\n                    clipboard_filtered.resize(clipboard_filtered.Size + out_len);\n                    memcpy(clipboard_filtered.Data + clipboard_filtered.Size - out_len, c_utf8, out_len);\n                }\n                if (clipboard_filtered.Size > 0) // If everything was filtered, ignore the pasting operation\n                {\n                    clipboard_filtered.push_back(0);\n                    stb_textedit_paste(state, state->Stb, clipboard_filtered.Data, clipboard_filtered.Size - 1);\n                    state->CursorFollow = true;\n                }\n            }\n        }\n\n        // Update render selection flag after events have been handled, so selection highlight can be displayed during the same frame.\n        render_selection |= state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor);\n    }\n\n    // Process callbacks and apply result back to user's buffer.\n    const char* apply_new_text = NULL;\n    int apply_new_text_length = 0;\n    if (g.ActiveId == id)\n    {\n        IM_ASSERT(state != NULL);\n        if (revert_edit && !is_readonly)\n        {\n            if (flags & ImGuiInputTextFlags_EscapeClearsAll)\n            {\n                // Clear input\n                IM_ASSERT(state->TextA.Data[0] != 0);\n                apply_new_text = \"\";\n                apply_new_text_length = 0;\n                value_changed = true;\n                char empty_string = 0;\n                stb_textedit_replace(state, state->Stb, &empty_string, 0);\n            }\n            else if (strcmp(state->TextA.Data, state->TextToRevertTo.Data) != 0)\n            {\n                apply_new_text = state->TextToRevertTo.Data;\n                apply_new_text_length = state->TextToRevertTo.Size - 1;\n\n                // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.\n                // Push records into the undo stack so we can Ctrl+Z the revert operation itself\n                value_changed = true;\n                stb_textedit_replace(state, state->Stb, state->TextToRevertTo.Data, state->TextToRevertTo.Size - 1);\n            }\n        }\n\n        // FIXME-OPT: We always reapply the live buffer back to the input buffer before clearing ActiveId,\n        // even though strictly speaking it wasn't modified on this frame. Should mark dirty state from the stb_textedit callbacks.\n        // If we do that, need to ensure that as special case, 'validated == true' also writes back.\n        // This also allows the user to use InputText() without maintaining any user-side storage.\n        // (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object\n        // unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize).\n        const bool apply_edit_back_to_user_buffer = true;// !revert_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);\n        if (apply_edit_back_to_user_buffer)\n        {\n            // Apply current edited text immediately.\n            // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer\n\n            // User callback\n            if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackAlways)) != 0)\n            {\n                IM_ASSERT(callback != NULL);\n\n                // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.\n                ImGuiInputTextFlags event_flag = 0;\n                ImGuiKey event_key = ImGuiKey_None;\n                if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, 0, id))\n                {\n                    event_flag = ImGuiInputTextFlags_CallbackCompletion;\n                    event_key = ImGuiKey_Tab;\n                }\n                else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow))\n                {\n                    event_flag = ImGuiInputTextFlags_CallbackHistory;\n                    event_key = ImGuiKey_UpArrow;\n                }\n                else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow))\n                {\n                    event_flag = ImGuiInputTextFlags_CallbackHistory;\n                    event_key = ImGuiKey_DownArrow;\n                }\n                else if ((flags & ImGuiInputTextFlags_CallbackEdit) && state->Edited)\n                {\n                    event_flag = ImGuiInputTextFlags_CallbackEdit;\n                }\n                else if (flags & ImGuiInputTextFlags_CallbackAlways)\n                {\n                    event_flag = ImGuiInputTextFlags_CallbackAlways;\n                }\n\n                if (event_flag)\n                {\n                    ImGuiInputTextCallbackData callback_data;\n                    callback_data.Ctx = &g;\n                    callback_data.ID = id;\n                    callback_data.Flags = flags;\n                    callback_data.EventFlag = event_flag;\n                    callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated);\n                    callback_data.UserData = callback_user_data;\n\n                    // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925\n                    char* callback_buf = is_readonly ? buf : state->TextA.Data;\n                    IM_ASSERT(callback_buf == state->TextSrc);\n                    state->CallbackTextBackup.resize(state->TextLen + 1);\n                    memcpy(state->CallbackTextBackup.Data, callback_buf, state->TextLen + 1);\n\n                    callback_data.EventKey = event_key;\n                    callback_data.Buf = callback_buf;\n                    callback_data.BufTextLen = state->TextLen;\n                    callback_data.BufSize = state->BufCapacity;\n                    callback_data.BufDirty = false;\n                    callback_data.CursorPos = state->Stb->cursor;\n                    callback_data.SelectionStart = state->Stb->select_start;\n                    callback_data.SelectionEnd = state->Stb->select_end;\n\n                    // Call user code\n                    callback(&callback_data);\n\n                    // Read back what user may have modified\n                    callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback\n                    IM_ASSERT(callback_data.Buf == callback_buf);         // Invalid to modify those fields\n                    IM_ASSERT(callback_data.BufSize == state->BufCapacity);\n                    IM_ASSERT(callback_data.Flags == flags);\n                    if (callback_data.BufDirty || callback_data.CursorPos != state->Stb->cursor)\n                        state->CursorFollow = true;\n                    state->Stb->cursor = ImClamp(callback_data.CursorPos, 0, callback_data.BufTextLen);\n                    state->Stb->select_start = ImClamp(callback_data.SelectionStart, 0, callback_data.BufTextLen);\n                    state->Stb->select_end = ImClamp(callback_data.SelectionEnd, 0, callback_data.BufTextLen);\n                    if (callback_data.BufDirty)\n                    {\n                        // Callback may update buffer and thus set buf_dirty even in read-only mode.\n                        IM_ASSERT(callback_data.BufTextLen == (int)ImStrlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!\n                        InputTextReconcileUndoState(state, state->CallbackTextBackup.Data, state->CallbackTextBackup.Size - 1, callback_data.Buf, callback_data.BufTextLen);\n                        state->TextLen = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()\n                        state->CursorAnimReset();\n                    }\n                }\n            }\n\n            // Will copy result string if modified\n            if (!is_readonly && strcmp(state->TextSrc, buf) != 0)\n            {\n                apply_new_text = state->TextSrc;\n                apply_new_text_length = state->TextLen;\n                value_changed = true;\n            }\n        }\n    }\n\n    // Handle reapplying final data on deactivation (see InputTextDeactivateHook() for details)\n    if (g.InputTextDeactivatedState.ID == id)\n    {\n        if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0)\n        {\n            apply_new_text = g.InputTextDeactivatedState.TextA.Data;\n            apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1;\n            value_changed = true;\n            //IMGUI_DEBUG_LOG(\"InputText(): apply Deactivated data for 0x%08X: \\\"%.*s\\\".\\n\", id, apply_new_text_length, apply_new_text);\n        }\n        g.InputTextDeactivatedState.ID = 0;\n    }\n\n    // Copy result to user buffer. This can currently only happen when (g.ActiveId == id)\n    if (apply_new_text != NULL)\n    {\n        //// We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size\n        //// of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used\n        //// without any storage on user's side.\n        IM_ASSERT(apply_new_text_length >= 0);\n        if (is_resizable)\n        {\n            ImGuiInputTextCallbackData callback_data;\n            callback_data.Ctx = &g;\n            callback_data.ID = id;\n            callback_data.Flags = flags;\n            callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;\n            callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated);\n            callback_data.Buf = buf;\n            callback_data.BufTextLen = apply_new_text_length;\n            callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1);\n            callback_data.UserData = callback_user_data;\n            callback(&callback_data);\n            buf = callback_data.Buf;\n            buf_size = callback_data.BufSize;\n            apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1);\n            IM_ASSERT(apply_new_text_length <= buf_size);\n        }\n        //IMGUI_DEBUG_PRINT(\"InputText(\\\"%s\\\"): apply_new_text length %d\\n\", label, apply_new_text_length);\n\n        // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size.\n        ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size));\n    }\n\n    // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)\n    // Otherwise request text input ahead for next frame.\n    if (g.ActiveId == id && clear_active_id)\n        ClearActiveID();\n\n    // Render frame\n    if (!is_multiline)\n    {\n        RenderNavCursor(frame_bb, id);\n        RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);\n    }\n\n    ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;\n    ImRect clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size\n    if (is_multiline)\n        clip_rect.ClipWith(draw_window->ClipRect);\n\n    // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line\n    // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether.\n    // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash.\n    const int buf_display_max_length = 2 * 1024 * 1024;\n    const char* buf_display = buf_display_from_state ? state->TextA.Data : buf; //-V595\n    const char* buf_display_end = NULL; // We have specialized paths below for setting the length\n\n    // Display hint when contents is empty\n    // At this point we need to handle the possibility that a callback could have modified the underlying buffer (#8368)\n    const bool new_is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);\n    if (new_is_displaying_hint != is_displaying_hint)\n    {\n        if (is_password && !is_displaying_hint)\n            PopPasswordFont();\n        is_displaying_hint = new_is_displaying_hint;\n        if (is_password && !is_displaying_hint)\n            PushPasswordFont();\n    }\n    if (is_displaying_hint)\n    {\n        buf_display = hint;\n        buf_display_end = hint + ImStrlen(hint);\n    }\n    else\n    {\n        if (render_cursor || render_selection || g.ActiveId == id)\n            buf_display_end = buf_display + state->TextLen; //-V595\n        else if (is_multiline && !is_wordwrap)\n            buf_display_end = NULL; // Inactive multi-line: end of buffer will be output by InputTextLineIndexBuild() special strchr() path.\n        else\n            buf_display_end = buf_display + ImStrlen(buf_display);\n    }\n\n    // Calculate visibility\n    int line_visible_n0 = 0, line_visible_n1 = 1;\n    if (is_multiline)\n        CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1);\n\n    // Build line index for easy data access (makes code below simpler and faster)\n    ImGuiTextIndex* line_index = &g.InputTextLineIndex;\n    line_index->Offsets.resize(0);\n    int line_count = 1;\n    if (is_multiline)\n    {\n        // If scrolling is expected to change build full index.\n        // FIXME-OPT: Could append to index when new value of line_visible_n1 becomes bigger, see second call to CalcClipRectVisibleItemsY() below.\n        bool will_scroll_y = state && ((state->CursorFollow && render_cursor) || (state->CursorCenterY && (render_cursor || render_selection)));\n        line_count = InputTextLineIndexBuild(flags, line_index, buf_display, buf_display_end, wrap_width, will_scroll_y ? INT_MAX : line_visible_n1 + 1, buf_display_end ? NULL : &buf_display_end);\n    }\n    line_index->EndOffset = (int)(buf_display_end - buf_display);\n    line_visible_n1 = ImMin(line_visible_n1, line_count);\n\n    // Store text height (we don't need width)\n    float text_size_y = line_count * g.FontSize;\n    //GetForegroundDrawList()->AddRect(draw_pos + ImVec2(0, line_visible_n0 * g.FontSize), draw_pos + ImVec2(frame_size.x, line_visible_n1 * g.FontSize), IM_COL32(255, 0, 0, 255));\n\n    // Calculate blinking cursor position\n    const ImVec2 cursor_offset = render_cursor && state ? InputTextLineIndexGetPosOffset(g, state, line_index, buf_display, buf_display_end, state->Stb->cursor) : ImVec2(0.0f, 0.0f);\n    ImVec2 draw_scroll;\n\n    // Render text. We currently only render selection when the widget is active or while scrolling.\n    const ImU32 text_col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);\n    if (render_cursor || render_selection)\n    {\n        // Render text (with cursor and selection)\n        // This is going to be messy. We need to:\n        // - Display the text (this alone can be more easily clipped)\n        // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)\n        // - Measure text height (for scrollbar)\n        // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)\n        // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.\n        IM_ASSERT(state != NULL);\n        state->LineCount = line_count;\n\n        // Scroll\n        float new_scroll_y = scroll_y;\n        if (render_cursor && state->CursorFollow)\n        {\n            // Horizontal scroll in chunks of quarter width\n            if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))\n            {\n                const float scroll_increment_x = inner_size.x * 0.25f;\n                const float visible_width = inner_size.x - style.FramePadding.x;\n                if (cursor_offset.x < state->Scroll.x)\n                    state->Scroll.x = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x));\n                else if (cursor_offset.x - visible_width >= state->Scroll.x)\n                    state->Scroll.x = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x);\n            }\n            else\n            {\n                state->Scroll.x = 0.0f;\n            }\n\n            // Vertical scroll\n            if (is_multiline)\n            {\n                // Test if cursor is vertically visible\n                if (cursor_offset.y - g.FontSize < scroll_y)\n                    new_scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);\n                else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y)\n                    new_scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f;\n            }\n            state->CursorFollow = false;\n        }\n        if (state->CursorCenterY)\n        {\n            if (is_multiline)\n                new_scroll_y = cursor_offset.y - g.FontSize - (inner_size.y * 0.5f - style.FramePadding.y);\n            state->CursorCenterY = false;\n            render_cursor = false;\n        }\n        if (new_scroll_y != scroll_y)\n        {\n            const float scroll_max_y = ImMax((text_size_y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f);\n            scroll_y = ImClamp(new_scroll_y, 0.0f, scroll_max_y);\n            draw_pos.y += (draw_window->Scroll.y - scroll_y);   // Manipulate cursor pos immediately avoid a frame of lag\n            draw_window->Scroll.y = scroll_y;\n            CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1);\n            line_visible_n1 = ImMin(line_visible_n1, line_count);\n        }\n\n        // Draw selection\n        draw_scroll.x = state->Scroll.x;\n        if (render_selection)\n        {\n            const ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests.\n            const float bg_offy_up = is_multiline ? 0.0f : -1.0f;    // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.\n            const float bg_offy_dn = is_multiline ? 0.0f : 2.0f;\n            const float bg_eol_width = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines\n\n            const char* text_selected_begin = buf_display + ImMin(state->Stb->select_start, state->Stb->select_end);\n            const char* text_selected_end = buf_display + ImMax(state->Stb->select_start, state->Stb->select_end);\n            for (int line_n = line_visible_n0; line_n < line_visible_n1; line_n++)\n            {\n                const char* p = line_index->get_line_begin(buf_display, line_n);\n                const char* p_eol = line_index->get_line_end(buf_display, line_n);\n                const bool p_eol_is_wrap = (p_eol < buf_display_end && *p_eol != '\\n');\n                if (p_eol_is_wrap)\n                    p_eol++;\n                const char* line_selected_begin = (text_selected_begin > p) ? text_selected_begin : p;\n                const char* line_selected_end = (text_selected_end < p_eol) ? text_selected_end : p_eol;\n\n                float rect_width = 0.0f;\n                if (line_selected_begin < line_selected_end)\n                    rect_width += CalcTextSize(line_selected_begin, line_selected_end).x;\n                if (text_selected_begin <= p_eol && text_selected_end > p_eol && !p_eol_is_wrap)\n                    rect_width += bg_eol_width; // So we can see selected empty lines\n                if (rect_width == 0.0f)\n                    continue;\n\n                ImRect rect;\n                rect.Min.x = draw_pos.x - draw_scroll.x + CalcTextSize(p, line_selected_begin).x;\n                rect.Min.y = draw_pos.y - draw_scroll.y + line_n * g.FontSize;\n                rect.Max.x = rect.Min.x + rect_width;\n                rect.Max.y = rect.Min.y + bg_offy_dn + g.FontSize;\n                rect.Min.y -= bg_offy_up;\n                rect.ClipWith(clip_rect);\n                draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);\n            }\n        }\n    }\n\n    // Find render position for right alignment (single-line only)\n    if (g.ActiveId != id && (flags & ImGuiInputTextFlags_ElideLeft) && !render_cursor && !render_selection)\n        draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x - CalcTextSize(buf_display, NULL).x - style.FramePadding.x);\n    //draw_scroll.x = state->Scroll.x; // Preserve scroll when inactive?\n\n    // Render text\n    if ((is_multiline || (buf_display_end - buf_display) < buf_display_max_length) && (text_col & IM_COL32_A_MASK) && (line_visible_n0 < line_visible_n1))\n        g.Font->RenderText(draw_window->DrawList, g.FontSize,\n            draw_pos - draw_scroll + ImVec2(0.0f, line_visible_n0 * g.FontSize),\n            text_col, clip_rect.AsVec4(),\n            line_index->get_line_begin(buf_display, line_visible_n0),\n            line_index->get_line_end(buf_display, line_visible_n1 - 1),\n            wrap_width, ImDrawTextFlags_WrapKeepBlanks | ImDrawTextFlags_CpuFineClip);\n\n    // Render blinking cursor\n    if (render_cursor)\n    {\n        state->CursorAnim += io.DeltaTime;\n        bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;\n        ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);\n        ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);\n        if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))\n            draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f * (float)(int)style._MainScale); // FIXME-DPI: Cursor thickness (#7031)\n\n        // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)\n        // This is required for some backends (SDL3) to start emitting character/text inputs.\n        // As per #6341, make sure we don't set that on the deactivating frame.\n        if (!is_readonly && g.ActiveId == id)\n        {\n            ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler)\n            ime_data->WantVisible = true;\n            ime_data->WantTextInput = true;\n            ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);\n            ime_data->InputLineHeight = g.FontSize;\n            ime_data->ViewportId = window->Viewport->ID;\n        }\n    }\n\n    if (is_password && !is_displaying_hint)\n        PopPasswordFont();\n\n    if (is_multiline)\n    {\n        // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)...\n        Dummy(ImVec2(0.0f, text_size_y + style.FramePadding.y));\n        g.NextItemData.ItemFlags |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop;\n        EndChild();\n        item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow);\n\n        // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active...\n        // FIXME: This quite messy/tricky, should attempt to get rid of the child window.\n        EndGroup();\n        if (g.LastItemData.ID == 0 || g.LastItemData.ID != GetWindowScrollbarID(draw_window, ImGuiAxis_Y))\n        {\n            g.LastItemData.ID = id;\n            g.LastItemData.ItemFlags = item_data_backup.ItemFlags;\n            g.LastItemData.StatusFlags = item_data_backup.StatusFlags;\n        }\n    }\n    if (state && is_readonly)\n        state->TextSrc = NULL;\n\n    // Log as text\n    if (g.LogEnabled && (!is_password || is_displaying_hint))\n    {\n        LogSetNextTextDecoration(\"{\", \"}\");\n        LogRenderedText(&draw_pos, buf_display, buf_display_end);\n    }\n\n    if (label_size.x > 0)\n        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);\n\n    if (value_changed)\n        MarkItemEdited(id);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);\n    if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)\n        return validated;\n    else\n        return value_changed;\n}\n\nvoid ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)\n{\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    ImGuiContext& g = *GImGui;\n    ImStb::STB_TexteditState* stb_state = state->Stb;\n    ImStb::StbUndoState* undo_state = &stb_state->undostate;\n    Text(\"ID: 0x%08X, ActiveID: 0x%08X\", state->ID, g.ActiveId);\n    DebugLocateItemOnHover(state->ID);\n    Text(\"TextLen: %d, Cursor: %d%s, Selection: %d..%d\", state->TextLen, stb_state->cursor,\n        (state->Flags & ImGuiInputTextFlags_WordWrap) ? (state->LastMoveDirectionLR == ImGuiDir_Left ? \" (L)\" : \" (R)\") : \"\",\n        stb_state->select_start, stb_state->select_end);\n    Text(\"BufCapacity: %d, LineCount: %d\", state->BufCapacity, state->LineCount);\n    Text(\"(Internal Buffer: TextA Size: %d, Capacity: %d)\", state->TextA.Size, state->TextA.Capacity);\n    Text(\"has_preferred_x: %d (%.2f)\", stb_state->has_preferred_x, stb_state->preferred_x);\n    Text(\"undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d\", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);\n    if (BeginChild(\"undopoints\", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) // Visualize undo state\n    {\n        PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));\n        for (int n = 0; n < IMSTB_TEXTEDIT_UNDOSTATECOUNT; n++)\n        {\n            ImStb::StbUndoRecord* undo_rec = &undo_state->undo_rec[n];\n            const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' ';\n            if (undo_rec_type == ' ')\n                BeginDisabled();\n            const int buf_preview_len = (undo_rec_type != ' ' && undo_rec->char_storage != -1) ? undo_rec->insert_length : 0;\n            const char* buf_preview_str = undo_state->undo_char + undo_rec->char_storage;\n            Text(\"%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \\\"%.*s\\\"\",\n                undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf_preview_len, buf_preview_str);\n            if (undo_rec_type == ' ')\n                EndDisabled();\n        }\n        PopStyleVar();\n    }\n    EndChild();\n#else\n    IM_UNUSED(state);\n#endif\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.\n//-------------------------------------------------------------------------\n// - ColorEdit3()\n// - ColorEdit4()\n// - ColorPicker3()\n// - RenderColorRectWithAlphaCheckerboard() [Internal]\n// - ColorPicker4()\n// - ColorButton()\n// - SetColorEditOptions()\n// - ColorTooltip() [Internal]\n// - ColorEditOptionsPopup() [Internal]\n// - ColorPickerOptionsPopup() [Internal]\n//-------------------------------------------------------------------------\n\nbool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags)\n{\n    return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);\n}\n\nstatic void ColorEditRestoreH(const float* col, float* H)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.ColorEditCurrentID != 0);\n    if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))\n        return;\n    *H = g.ColorEditSavedHue;\n}\n\n// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation.\n// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting.\nstatic void ColorEditRestoreHS(const float* col, float* H, float* S, float* V)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.ColorEditCurrentID != 0);\n    if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))\n        return;\n\n    // When S == 0, H is undefined.\n    // When H == 1 it wraps around to 0.\n    if (*S == 0.0f || (*H == 0.0f && g.ColorEditSavedHue == 1))\n        *H = g.ColorEditSavedHue;\n\n    // When V == 0, S is undefined.\n    if (*V == 0.0f)\n        *S = g.ColorEditSavedSat;\n}\n\n// Edit colors components (each component in 0.0f..1.0f range).\n// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.\n// With typical options: Left-click on color square to open color picker. Right-click to open option menu. Ctrl+Click over input fields to edit them and TAB to go to next item.\nbool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const float square_sz = GetFrameHeight();\n    const char* label_display_end = FindRenderedTextEnd(label);\n    float w_full = CalcItemWidth();\n    g.NextItemData.ClearFlags();\n\n    BeginGroup();\n    PushID(label);\n    const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0);\n    if (set_current_color_edit_id)\n        g.ColorEditCurrentID = window->IDStack.back();\n\n    // If we're not showing any slider there's no point in doing any HSV conversions\n    const ImGuiColorEditFlags flags_untouched = flags;\n    if (flags & ImGuiColorEditFlags_NoInputs)\n        flags = (flags & (~ImGuiColorEditFlags_DisplayMask_)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions;\n\n    // Context menu: display and modify options (before defaults are applied)\n    if (!(flags & ImGuiColorEditFlags_NoOptions))\n        ColorEditOptionsPopup(col, flags);\n\n    // Read stored options\n    if (!(flags & ImGuiColorEditFlags_DisplayMask_))\n        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_);\n    if (!(flags & ImGuiColorEditFlags_DataTypeMask_))\n        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_);\n    if (!(flags & ImGuiColorEditFlags_PickerMask_))\n        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_);\n    if (!(flags & ImGuiColorEditFlags_InputMask_))\n        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_);\n    flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_));\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check that only 1 is selected\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));   // Check that only 1 is selected\n\n    const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;\n    const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;\n    const int components = alpha ? 4 : 3;\n    const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);\n    const float w_inputs = ImMax(w_full - w_button, 1.0f);\n    w_full = w_inputs + w_button;\n\n    // Convert to the formats we need\n    float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };\n    if ((flags & ImGuiColorEditFlags_InputHSV) && (flags & ImGuiColorEditFlags_DisplayRGB))\n        ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);\n    else if ((flags & ImGuiColorEditFlags_InputRGB) && (flags & ImGuiColorEditFlags_DisplayHSV))\n    {\n        // Hue is lost when converting from grayscale rgb (saturation=0). Restore it.\n        ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);\n        ColorEditRestoreHS(col, &f[0], &f[1], &f[2]);\n    }\n    int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) };\n\n    bool value_changed = false;\n    bool value_changed_as_float = false;\n\n    const ImVec2 pos = window->DC.CursorPos;\n    const float inputs_offset_x = (style.ColorButtonPosition == ImGuiDir_Left) ? w_button : 0.0f;\n    window->DC.CursorPos.x = pos.x + inputs_offset_x;\n\n    if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)\n    {\n        // RGB/HSV 0..255 Sliders\n        const float w_items = w_inputs - style.ItemInnerSpacing.x * (components - 1);\n        const float w_per_component = IM_TRUNC(w_items / components);\n        const bool draw_color_marker = (flags & (ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_NoColorMarkers)) == 0;\n\n        const bool hide_prefix = draw_color_marker || (w_per_component <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? \"M:0.000\" : \"M:000\").x);\n        static const char* ids[4] = { \"##X\", \"##Y\", \"##Z\", \"##W\" };\n        static const char* fmt_table_int[3][4] =\n        {\n            {   \"%3d\",   \"%3d\",   \"%3d\",   \"%3d\" }, // Short display\n            { \"R:%3d\", \"G:%3d\", \"B:%3d\", \"A:%3d\" }, // Long display for RGBA\n            { \"H:%3d\", \"S:%3d\", \"V:%3d\", \"A:%3d\" }  // Long display for HSVA\n        };\n        static const char* fmt_table_float[3][4] =\n        {\n            {   \"%0.3f\",   \"%0.3f\",   \"%0.3f\",   \"%0.3f\" }, // Short display\n            { \"R:%0.3f\", \"G:%0.3f\", \"B:%0.3f\", \"A:%0.3f\" }, // Long display for RGBA\n            { \"H:%0.3f\", \"S:%0.3f\", \"V:%0.3f\", \"A:%0.3f\" }  // Long display for HSVA\n        };\n        const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1;\n        const ImGuiSliderFlags drag_flags = draw_color_marker ? ImGuiSliderFlags_ColorMarkers : ImGuiSliderFlags_None;\n\n        float prev_split = 0.0f;\n        for (int n = 0; n < components; n++)\n        {\n            if (n > 0)\n                SameLine(0, style.ItemInnerSpacing.x);\n            float next_split = IM_TRUNC(w_items * (n + 1) / components);\n            SetNextItemWidth(ImMax(next_split - prev_split, 1.0f));\n            prev_split = next_split;\n            if (draw_color_marker)\n                SetNextItemColorMarker(GDefaultRgbaColorMarkers[n]);\n\n            // FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0.\n            if (flags & ImGuiColorEditFlags_Float)\n            {\n                value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n], drag_flags);\n                value_changed_as_float |= value_changed;\n            }\n            else\n            {\n                value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n], drag_flags);\n            }\n            if (!(flags & ImGuiColorEditFlags_NoOptions))\n                OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n        }\n    }\n    else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)\n    {\n        // RGB Hexadecimal Input\n        char buf[64];\n        if (alpha)\n            ImFormatString(buf, IM_COUNTOF(buf), \"#%02X%02X%02X%02X\", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255));\n        else\n            ImFormatString(buf, IM_COUNTOF(buf), \"#%02X%02X%02X\", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255));\n        SetNextItemWidth(w_inputs);\n        if (InputText(\"##Text\", buf, IM_COUNTOF(buf), ImGuiInputTextFlags_CharsUppercase))\n        {\n            value_changed = true;\n            char* p = buf;\n            while (*p == '#' || ImCharIsBlankA(*p))\n                p++;\n            i[0] = i[1] = i[2] = 0;\n            i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha)\n            int r;\n            if (alpha)\n                r = sscanf(p, \"%02X%02X%02X%02X\", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned)\n            else\n                r = sscanf(p, \"%02X%02X%02X\", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);\n            IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'.\n        }\n        if (!(flags & ImGuiColorEditFlags_NoOptions))\n            OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n    }\n\n    ImGuiWindow* picker_active_window = NULL;\n    if (!(flags & ImGuiColorEditFlags_NoSmallPreview))\n    {\n        const float button_offset_x = ((flags & ImGuiColorEditFlags_NoInputs) || (style.ColorButtonPosition == ImGuiDir_Left)) ? 0.0f : w_inputs + style.ItemInnerSpacing.x;\n        window->DC.CursorPos = ImVec2(pos.x + button_offset_x, pos.y);\n\n        const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f);\n        if (ColorButton(\"##ColorButton\", col_v4, flags))\n        {\n            if (!(flags & ImGuiColorEditFlags_NoPicker))\n            {\n                // Store current color and open a picker\n                g.ColorPickerRef = col_v4;\n                OpenPopup(\"picker\");\n                SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(0.0f, style.ItemSpacing.y));\n            }\n        }\n        if (!(flags & ImGuiColorEditFlags_NoOptions))\n            OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n\n        if (BeginPopup(\"picker\"))\n        {\n            if (g.CurrentWindow->BeginCount == 1)\n            {\n                picker_active_window = g.CurrentWindow;\n                if (label != label_display_end)\n                {\n                    TextEx(label, label_display_end);\n                    Spacing();\n                }\n                ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;\n                ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;\n                SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?\n                value_changed |= ColorPicker4(\"##picker\", col, picker_flags, &g.ColorPickerRef.x);\n            }\n            EndPopup();\n        }\n    }\n\n    if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))\n    {\n        // Position not necessarily next to last submitted button (e.g. if style.ColorButtonPosition == ImGuiDir_Left),\n        // but we need to use SameLine() to setup baseline correctly. Might want to refactor SameLine() to simplify this.\n        SameLine(0.0f, style.ItemInnerSpacing.x);\n        window->DC.CursorPos.x = pos.x + ((flags & ImGuiColorEditFlags_NoInputs) ? w_button : w_full + style.ItemInnerSpacing.x);\n        TextEx(label, label_display_end);\n    }\n\n    // Convert back\n    if (value_changed && picker_active_window == NULL)\n    {\n        if (!value_changed_as_float)\n            for (int n = 0; n < 4; n++)\n                f[n] = i[n] / 255.0f;\n        if ((flags & ImGuiColorEditFlags_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB))\n        {\n            g.ColorEditSavedHue = f[0];\n            g.ColorEditSavedSat = f[1];\n            ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);\n            g.ColorEditSavedID = g.ColorEditCurrentID;\n            g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0));\n        }\n        if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV))\n            ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);\n\n        col[0] = f[0];\n        col[1] = f[1];\n        col[2] = f[2];\n        if (alpha)\n            col[3] = f[3];\n    }\n\n    if (set_current_color_edit_id)\n        g.ColorEditCurrentID = 0;\n    PopID();\n    EndGroup();\n\n    // Drag and Drop Target\n    // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test.\n    if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget())\n    {\n        bool accepted_drag_drop = false;\n        if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))\n        {\n            memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512 //-V1086\n            value_changed = accepted_drag_drop = true;\n        }\n        if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))\n        {\n            memcpy((float*)col, payload->Data, sizeof(float) * components);\n            value_changed = accepted_drag_drop = true;\n        }\n\n        // Drag-drop payloads are always RGB\n        if (accepted_drag_drop && (flags & ImGuiColorEditFlags_InputHSV))\n            ColorConvertRGBtoHSV(col[0], col[1], col[2], col[0], col[1], col[2]);\n        EndDragDropTarget();\n    }\n\n    // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4().\n    if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)\n        g.LastItemData.ID = g.ActiveId;\n\n    if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId\n        MarkItemEdited(g.LastItemData.ID);\n\n    return value_changed;\n}\n\nbool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags)\n{\n    float col4[4] = { col[0], col[1], col[2], 1.0f };\n    if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha))\n        return false;\n    col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2];\n    return true;\n}\n\n// Helper for ColorPicker4()\nstatic void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w, float alpha)\n{\n    ImU32 alpha8 = IM_F32_TO_INT8_SAT(alpha);\n    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1,         pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32(0,0,0,alpha8));\n    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x,             pos.y), half_sz,                              ImGuiDir_Right, IM_COL32(255,255,255,alpha8));\n    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left,  IM_COL32(0,0,0,alpha8));\n    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x,     pos.y), half_sz,                              ImGuiDir_Left,  IM_COL32(255,255,255,alpha8));\n}\n\n// Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.\n// (In C++ the 'float col[4]' notation for a function argument is equivalent to 'float* col', we only specify a size to facilitate understanding of the code.)\n// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..)\n// FIXME: this is trying to be aware of style.Alpha but not fully correct. Also, the color wheel will have overlapping glitches with (style.Alpha < 1.0)\nbool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImDrawList* draw_list = window->DrawList;\n    ImGuiStyle& style = g.Style;\n    ImGuiIO& io = g.IO;\n\n    const float width = CalcItemWidth();\n    const bool is_readonly = ((g.NextItemData.ItemFlags | g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0;\n    g.NextItemData.ClearFlags();\n\n    PushID(label);\n    const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0);\n    if (set_current_color_edit_id)\n        g.ColorEditCurrentID = window->IDStack.back();\n    BeginGroup();\n\n    if (!(flags & ImGuiColorEditFlags_NoSidePreview))\n        flags |= ImGuiColorEditFlags_NoSmallPreview;\n\n    // Context menu: display and store options.\n    if (!(flags & ImGuiColorEditFlags_NoOptions))\n        ColorPickerOptionsPopup(col, flags);\n\n    // Read stored options\n    if (!(flags & ImGuiColorEditFlags_PickerMask_))\n        flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_;\n    if (!(flags & ImGuiColorEditFlags_InputMask_))\n        flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_;\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check that only 1 is selected\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));  // Check that only 1 is selected\n    if (!(flags & ImGuiColorEditFlags_NoOptions))\n        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);\n\n    // Setup\n    int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;\n    bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);\n    ImVec2 picker_pos = window->DC.CursorPos;\n    float square_sz = GetFrameHeight();\n    float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars\n    float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box\n    float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;\n    float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;\n    float bars_triangles_half_sz = IM_TRUNC(bars_width * 0.20f);\n\n    float backup_initial_col[4];\n    memcpy(backup_initial_col, col, components * sizeof(float));\n\n    float wheel_thickness = sv_picker_size * 0.08f;\n    float wheel_r_outer = sv_picker_size * 0.50f;\n    float wheel_r_inner = wheel_r_outer - wheel_thickness;\n    ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size * 0.5f);\n\n    // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.\n    float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);\n    ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point.\n    ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point.\n    ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point.\n\n    float H = col[0], S = col[1], V = col[2];\n    float R = col[0], G = col[1], B = col[2];\n    if (flags & ImGuiColorEditFlags_InputRGB)\n    {\n        // Hue is lost when converting from grayscale rgb (saturation=0). Restore it.\n        ColorConvertRGBtoHSV(R, G, B, H, S, V);\n        ColorEditRestoreHS(col, &H, &S, &V);\n    }\n    else if (flags & ImGuiColorEditFlags_InputHSV)\n    {\n        ColorConvertHSVtoRGB(H, S, V, R, G, B);\n    }\n\n    bool value_changed = false, value_changed_h = false, value_changed_sv = false;\n\n    PushItemFlag(ImGuiItemFlags_NoNav, true);\n    if (flags & ImGuiColorEditFlags_PickerHueWheel)\n    {\n        // Hue wheel + SV triangle logic\n        InvisibleButton(\"hsv\", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));\n        if (IsItemActive() && !is_readonly)\n        {\n            ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;\n            ImVec2 current_off = g.IO.MousePos - wheel_center;\n            float initial_dist2 = ImLengthSqr(initial_off);\n            if (initial_dist2 >= (wheel_r_inner - 1) * (wheel_r_inner - 1) && initial_dist2 <= (wheel_r_outer + 1) * (wheel_r_outer + 1))\n            {\n                // Interactive with Hue wheel\n                H = ImAtan2(current_off.y, current_off.x) / IM_PI * 0.5f;\n                if (H < 0.0f)\n                    H += 1.0f;\n                value_changed = value_changed_h = true;\n            }\n            float cos_hue_angle = ImCos(-H * 2.0f * IM_PI);\n            float sin_hue_angle = ImSin(-H * 2.0f * IM_PI);\n            if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))\n            {\n                // Interacting with SV triangle\n                ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle);\n                if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated))\n                    current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated);\n                float uu, vv, ww;\n                ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww);\n                V = ImClamp(1.0f - vv, 0.0001f, 1.0f);\n                S = ImClamp(uu / V, 0.0001f, 1.0f);\n                value_changed = value_changed_sv = true;\n            }\n        }\n        if (!(flags & ImGuiColorEditFlags_NoOptions))\n            OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n    }\n    else if (flags & ImGuiColorEditFlags_PickerHueBar)\n    {\n        // SV rectangle logic\n        InvisibleButton(\"sv\", ImVec2(sv_picker_size, sv_picker_size));\n        if (IsItemActive() && !is_readonly)\n        {\n            S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1));\n            V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));\n            ColorEditRestoreH(col, &H); // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square.\n            value_changed = value_changed_sv = true;\n        }\n        if (!(flags & ImGuiColorEditFlags_NoOptions))\n            OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n\n        // Hue bar logic\n        SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));\n        InvisibleButton(\"hue\", ImVec2(bars_width, sv_picker_size));\n        if (IsItemActive() && !is_readonly)\n        {\n            H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));\n            value_changed = value_changed_h = true;\n        }\n    }\n\n    // Alpha bar logic\n    if (alpha_bar)\n    {\n        SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y));\n        InvisibleButton(\"alpha\", ImVec2(bars_width, sv_picker_size));\n        if (IsItemActive())\n        {\n            col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));\n            value_changed = true;\n        }\n    }\n    PopItemFlag(); // ImGuiItemFlags_NoNav\n\n    if (!(flags & ImGuiColorEditFlags_NoSidePreview))\n    {\n        SameLine(0, style.ItemInnerSpacing.x);\n        BeginGroup();\n    }\n\n    if (!(flags & ImGuiColorEditFlags_NoLabel))\n    {\n        const char* label_display_end = FindRenderedTextEnd(label);\n        if (label != label_display_end)\n        {\n            if ((flags & ImGuiColorEditFlags_NoSidePreview))\n                SameLine(0, style.ItemInnerSpacing.x);\n            TextEx(label, label_display_end);\n        }\n    }\n\n    if (!(flags & ImGuiColorEditFlags_NoSidePreview))\n    {\n        PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);\n        ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);\n        if ((flags & ImGuiColorEditFlags_NoLabel))\n            Text(\"Current\");\n\n        ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaMask_ | ImGuiColorEditFlags_NoTooltip;\n        ColorButton(\"##current\", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2));\n        if (ref_col != NULL)\n        {\n            Text(\"Original\");\n            ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);\n            if (ColorButton(\"##original\", ref_col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)))\n            {\n                memcpy(col, ref_col, components * sizeof(float));\n                value_changed = true;\n            }\n        }\n        PopItemFlag();\n        EndGroup();\n    }\n\n    // Convert back color to RGB\n    if (value_changed_h || value_changed_sv)\n    {\n        if (flags & ImGuiColorEditFlags_InputRGB)\n        {\n            ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]);\n            g.ColorEditSavedHue = H;\n            g.ColorEditSavedSat = S;\n            g.ColorEditSavedID = g.ColorEditCurrentID;\n            g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0));\n        }\n        else if (flags & ImGuiColorEditFlags_InputHSV)\n        {\n            col[0] = H;\n            col[1] = S;\n            col[2] = V;\n        }\n    }\n\n    // R,G,B and H,S,V slider color editor\n    bool value_changed_fix_hue_wrap = false;\n    if ((flags & ImGuiColorEditFlags_NoInputs) == 0)\n    {\n        PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);\n        ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaMask_ | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview;\n        ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;\n        if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)\n            if (ColorEdit4(\"##rgb\", col, sub_flags | ImGuiColorEditFlags_DisplayRGB))\n            {\n                // FIXME: Hackily differentiating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget.\n                // For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050)\n                value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap);\n                value_changed = true;\n            }\n        if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)\n            value_changed |= ColorEdit4(\"##hsv\", col, sub_flags | ImGuiColorEditFlags_DisplayHSV);\n        if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)\n            value_changed |= ColorEdit4(\"##hex\", col, sub_flags | ImGuiColorEditFlags_DisplayHex);\n        PopItemWidth();\n    }\n\n    // Try to cancel hue wrap (after ColorEdit4 call), if any\n    if (value_changed_fix_hue_wrap && (flags & ImGuiColorEditFlags_InputRGB))\n    {\n        float new_H, new_S, new_V;\n        ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V);\n        if (new_H <= 0 && H > 0)\n        {\n            if (new_V <= 0 && V != new_V)\n                ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]);\n            else if (new_S <= 0)\n                ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]);\n        }\n    }\n\n    if (value_changed)\n    {\n        if (flags & ImGuiColorEditFlags_InputRGB)\n        {\n            R = col[0];\n            G = col[1];\n            B = col[2];\n            ColorConvertRGBtoHSV(R, G, B, H, S, V);\n            ColorEditRestoreHS(col, &H, &S, &V);   // Fix local Hue as display below will use it immediately.\n        }\n        else if (flags & ImGuiColorEditFlags_InputHSV)\n        {\n            H = col[0];\n            S = col[1];\n            V = col[2];\n            ColorConvertHSVtoRGB(H, S, V, R, G, B);\n        }\n    }\n\n    const int style_alpha8 = IM_F32_TO_INT8_SAT(style.Alpha);\n    const ImU32 col_black = IM_COL32(0,0,0,style_alpha8);\n    const ImU32 col_white = IM_COL32(255,255,255,style_alpha8);\n    const ImU32 col_midgrey = IM_COL32(128,128,128,style_alpha8);\n    const ImU32 col_hues[6 + 1] = { IM_COL32(255,0,0,style_alpha8), IM_COL32(255,255,0,style_alpha8), IM_COL32(0,255,0,style_alpha8), IM_COL32(0,255,255,style_alpha8), IM_COL32(0,0,255,style_alpha8), IM_COL32(255,0,255,style_alpha8), IM_COL32(255,0,0,style_alpha8) };\n\n    ImVec4 hue_color_f(1, 1, 1, style.Alpha); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z);\n    ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f);\n    ImU32 user_col32_striped_of_alpha = ColorConvertFloat4ToU32(ImVec4(R, G, B, style.Alpha)); // Important: this is still including the main rendering/style alpha!!\n\n    ImVec2 sv_cursor_pos;\n\n    if (flags & ImGuiColorEditFlags_PickerHueWheel)\n    {\n        // Render Hue Wheel\n        const float aeps = 0.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out).\n        const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12);\n        for (int n = 0; n < 6; n++)\n        {\n            const float a0 = (n)     /6.0f * 2.0f * IM_PI - aeps;\n            const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;\n            const int vert_start_idx = draw_list->VtxBuffer.Size;\n            draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);\n            draw_list->PathStroke(col_white, 0, wheel_thickness);\n            const int vert_end_idx = draw_list->VtxBuffer.Size;\n\n            // Paint colors over existing vertices\n            ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner);\n            ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner);\n            ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col_hues[n], col_hues[n + 1]);\n        }\n\n        // Render Cursor + preview on Hue Wheel\n        float cos_hue_angle = ImCos(H * 2.0f * IM_PI);\n        float sin_hue_angle = ImSin(H * 2.0f * IM_PI);\n        ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f);\n        float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;\n        int hue_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(hue_cursor_rad); // Lock segment count so the +1 one matches others.\n        draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);\n        draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments);\n        draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments);\n\n        // Render SV triangle (rotated according to hue)\n        ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);\n        ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);\n        ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);\n        ImVec2 uv_white = GetFontTexUvWhitePixel();\n        draw_list->PrimReserve(3, 3);\n        draw_list->PrimVtx(tra, uv_white, hue_color32);\n        draw_list->PrimVtx(trb, uv_white, col_black);\n        draw_list->PrimVtx(trc, uv_white, col_white);\n        draw_list->AddTriangle(tra, trb, trc, col_midgrey, 1.5f);\n        sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V));\n    }\n    else if (flags & ImGuiColorEditFlags_PickerHueBar)\n    {\n        // Render SV Square\n        draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), col_white, hue_color32, hue_color32, col_white);\n        draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0, 0, col_black, col_black);\n        RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0.0f);\n        sv_cursor_pos.x = ImClamp(IM_ROUND(picker_pos.x + ImSaturate(S)     * sv_picker_size), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much\n        sv_cursor_pos.y = ImClamp(IM_ROUND(picker_pos.y + ImSaturate(1 - V) * sv_picker_size), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2);\n\n        // Render Hue Bar\n        for (int i = 0; i < 6; ++i)\n            draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), col_hues[i], col_hues[i], col_hues[i + 1], col_hues[i + 1]);\n        float bar0_line_y = IM_ROUND(picker_pos.y + H * sv_picker_size);\n        RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f);\n        RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha);\n    }\n\n    // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)\n    float sv_cursor_rad = value_changed_sv ? wheel_thickness * 0.55f : wheel_thickness * 0.40f;\n    int sv_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(sv_cursor_rad); // Lock segment count so the +1 one matches others.\n    draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, sv_cursor_segments);\n    draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, sv_cursor_segments);\n    draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, sv_cursor_segments);\n\n    // Render alpha bar\n    if (alpha_bar)\n    {\n        float alpha = ImSaturate(col[3]);\n        ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size);\n        RenderColorRectWithAlphaCheckerboard(draw_list, bar1_bb.Min, bar1_bb.Max, 0, bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f));\n        draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, user_col32_striped_of_alpha, user_col32_striped_of_alpha, user_col32_striped_of_alpha & ~IM_COL32_A_MASK, user_col32_striped_of_alpha & ~IM_COL32_A_MASK);\n        float bar1_line_y = IM_ROUND(picker_pos.y + (1.0f - alpha) * sv_picker_size);\n        RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f);\n        RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha);\n    }\n\n    EndGroup();\n\n    if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0)\n        value_changed = false;\n    if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId\n        MarkItemEdited(g.LastItemData.ID);\n\n    if (set_current_color_edit_id)\n        g.ColorEditCurrentID = 0;\n    PopID();\n\n    return value_changed;\n}\n\n// A little color square. Return true when clicked.\n// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.\n// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip.\n// Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set.\nbool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, const ImVec2& size_arg)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiID id = window->GetID(desc_id);\n    const float default_size = GetFrameHeight();\n    const ImVec2 size(size_arg.x == 0.0f ? default_size : size_arg.x, size_arg.y == 0.0f ? default_size : size_arg.y);\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);\n    ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);\n    if (!ItemAdd(bb, id))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held);\n\n    if (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque))\n        flags &= ~(ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf);\n\n    ImVec4 col_rgb = col;\n    if (flags & ImGuiColorEditFlags_InputHSV)\n        ColorConvertHSVtoRGB(col_rgb.x, col_rgb.y, col_rgb.z, col_rgb.x, col_rgb.y, col_rgb.z);\n\n    ImVec4 col_rgb_without_alpha(col_rgb.x, col_rgb.y, col_rgb.z, 1.0f);\n    float grid_step = ImMin(size.x, size.y) / 2.99f;\n    float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f);\n    ImRect bb_inner = bb;\n    float off = 0.0f;\n    if ((flags & ImGuiColorEditFlags_NoBorder) == 0)\n    {\n        off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts.\n        bb_inner.Expand(off);\n    }\n    if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f)\n    {\n        float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f);\n        if ((flags & ImGuiColorEditFlags_AlphaNoBg) == 0)\n            RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight);\n        else\n            window->DrawList->AddRectFilled(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), rounding, ImDrawFlags_RoundCornersRight);\n        window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft);\n    }\n    else\n    {\n        // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha\n        ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaOpaque) ? col_rgb_without_alpha : col_rgb;\n        if (col_source.w < 1.0f && (flags & ImGuiColorEditFlags_AlphaNoBg) == 0)\n            RenderColorRectWithAlphaCheckerboard(window->DrawList, bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);\n        else\n            window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding);\n    }\n    RenderNavCursor(bb, id);\n    if ((flags & ImGuiColorEditFlags_NoBorder) == 0)\n    {\n        if (g.Style.FrameBorderSize > 0.0f)\n            RenderFrameBorder(bb.Min, bb.Max, rounding);\n        else\n            window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding, 0, 1.0f * (float)(int)g.Style._MainScale); // Color buttons are often in need of some sort of border // FIXME-DPI\n    }\n\n    // Drag and Drop Source\n    // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test.\n    if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource())\n    {\n        if (flags & ImGuiColorEditFlags_NoAlpha)\n            SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col_rgb, sizeof(float) * 3, ImGuiCond_Once);\n        else\n            SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col_rgb, sizeof(float) * 4, ImGuiCond_Once);\n        ColorButton(desc_id, col, flags);\n        SameLine();\n        TextEx(\"Color\");\n        EndDragDropSource();\n    }\n\n    // Tooltip\n    if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n        ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_AlphaMask_));\n\n    return pressed;\n}\n\n// Initialize/override default color options\n// FIXME: Could be moved to a simple IO field.\nvoid ImGui::SetColorEditOptions(ImGuiColorEditFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0)\n        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_;\n    if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0)\n        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_;\n    if ((flags & ImGuiColorEditFlags_PickerMask_) == 0)\n        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_;\n    if ((flags & ImGuiColorEditFlags_InputMask_) == 0)\n        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_;\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_));    // Check only 1 option is selected\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_));   // Check only 1 option is selected\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_));     // Check only 1 option is selected\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));      // Check only 1 option is selected\n    g.ColorEditOptions = flags;\n}\n\n// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.\nvoid ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n\n    if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))\n        return;\n    const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;\n    if (text_end > text)\n    {\n        TextEx(text, text_end);\n        Separator();\n    }\n\n    ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2);\n    ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);\n    int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);\n    ImGuiColorEditFlags flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_AlphaMask_;\n    ColorButton(\"##preview\", cf, (flags & flags_to_forward) | ImGuiColorEditFlags_NoTooltip, sz);\n    SameLine();\n    if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_))\n    {\n        if (flags & ImGuiColorEditFlags_NoAlpha)\n            Text(\"#%02X%02X%02X\\nR: %d, G: %d, B: %d\\n(%.3f, %.3f, %.3f)\", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]);\n        else\n            Text(\"#%02X%02X%02X%02X\\nR:%d, G:%d, B:%d, A:%d\\n(%.3f, %.3f, %.3f, %.3f)\", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]);\n    }\n    else if (flags & ImGuiColorEditFlags_InputHSV)\n    {\n        if (flags & ImGuiColorEditFlags_NoAlpha)\n            Text(\"H: %.3f, S: %.3f, V: %.3f\", col[0], col[1], col[2]);\n        else\n            Text(\"H: %.3f, S: %.3f, V: %.3f, A: %.3f\", col[0], col[1], col[2], col[3]);\n    }\n    EndTooltip();\n}\n\nvoid ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)\n{\n    bool allow_opt_inputs = !(flags & ImGuiColorEditFlags_DisplayMask_);\n    bool allow_opt_datatype = !(flags & ImGuiColorEditFlags_DataTypeMask_);\n    if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup(\"context\"))\n        return;\n\n    ImGuiContext& g = *GImGui;\n    PushItemFlag(ImGuiItemFlags_NoMarkEdited, true);\n    ImGuiColorEditFlags opts = g.ColorEditOptions;\n    if (allow_opt_inputs)\n    {\n        if (RadioButton(\"RGB\", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB;\n        if (RadioButton(\"HSV\", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHSV;\n        if (RadioButton(\"Hex\", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHex;\n    }\n    if (allow_opt_datatype)\n    {\n        if (allow_opt_inputs) Separator();\n        if (RadioButton(\"0..255\",     (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Uint8;\n        if (RadioButton(\"0.00..1.00\", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Float;\n    }\n\n    if (allow_opt_inputs || allow_opt_datatype)\n        Separator();\n    if (Button(\"Copy as..\", ImVec2(-1, 0)))\n        OpenPopup(\"Copy\");\n    if (BeginPopup(\"Copy\"))\n    {\n        int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);\n        char buf[64];\n        ImFormatString(buf, IM_COUNTOF(buf), \"(%.3ff, %.3ff, %.3ff, %.3ff)\", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);\n        if (Selectable(buf))\n            SetClipboardText(buf);\n        ImFormatString(buf, IM_COUNTOF(buf), \"(%d,%d,%d,%d)\", cr, cg, cb, ca);\n        if (Selectable(buf))\n            SetClipboardText(buf);\n        ImFormatString(buf, IM_COUNTOF(buf), \"#%02X%02X%02X\", cr, cg, cb);\n        if (Selectable(buf))\n            SetClipboardText(buf);\n        if (!(flags & ImGuiColorEditFlags_NoAlpha))\n        {\n            ImFormatString(buf, IM_COUNTOF(buf), \"#%02X%02X%02X%02X\", cr, cg, cb, ca);\n            if (Selectable(buf))\n                SetClipboardText(buf);\n        }\n        EndPopup();\n    }\n\n    g.ColorEditOptions = opts;\n    PopItemFlag();\n    EndPopup();\n}\n\nvoid ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags)\n{\n    bool allow_opt_picker = !(flags & ImGuiColorEditFlags_PickerMask_);\n    bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);\n    if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup(\"context\"))\n        return;\n\n    ImGuiContext& g = *GImGui;\n    PushItemFlag(ImGuiItemFlags_NoMarkEdited, true);\n    if (allow_opt_picker)\n    {\n        ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function\n        PushItemWidth(picker_size.x);\n        for (int picker_type = 0; picker_type < 2; picker_type++)\n        {\n            // Draw small/thumbnail version of each picker type (over an invisible button for selection)\n            if (picker_type > 0) Separator();\n            PushID(picker_type);\n            ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoSidePreview | (flags & ImGuiColorEditFlags_NoAlpha);\n            if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;\n            if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;\n            ImVec2 backup_pos = GetCursorScreenPos();\n            if (Selectable(\"##selectable\", false, 0, picker_size)) // By default, Selectable() is closing popup\n                g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_);\n            SetCursorScreenPos(backup_pos);\n            ImVec4 previewing_ref_col;\n            memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4));\n            ColorPicker4(\"##previewing_picker\", &previewing_ref_col.x, picker_flags);\n            PopID();\n        }\n        PopItemWidth();\n    }\n    if (allow_opt_alpha_bar)\n    {\n        if (allow_opt_picker) Separator();\n        CheckboxFlags(\"Alpha Bar\", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);\n    }\n    PopItemFlag();\n    EndPopup();\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: TreeNode, CollapsingHeader, etc.\n//-------------------------------------------------------------------------\n// - TreeNode()\n// - TreeNodeV()\n// - TreeNodeEx()\n// - TreeNodeExV()\n// - TreeNodeStoreStackData() [Internal]\n// - TreeNodeBehavior() [Internal]\n// - TreePush()\n// - TreePop()\n// - GetTreeNodeToLabelSpacing()\n// - SetNextItemOpen()\n// - CollapsingHeader()\n//-------------------------------------------------------------------------\n\nbool ImGui::TreeNode(const char* str_id, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool is_open = TreeNodeExV(str_id, 0, fmt, args);\n    va_end(args);\n    return is_open;\n}\n\nbool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);\n    va_end(args);\n    return is_open;\n}\n\nbool ImGui::TreeNode(const char* label)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n    ImGuiID id = window->GetID(label);\n    return TreeNodeBehavior(id, ImGuiTreeNodeFlags_None, label, NULL);\n}\n\nbool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)\n{\n    return TreeNodeExV(str_id, 0, fmt, args);\n}\n\nbool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)\n{\n    return TreeNodeExV(ptr_id, 0, fmt, args);\n}\n\nbool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n    ImGuiID id = window->GetID(label);\n    return TreeNodeBehavior(id, flags, label, NULL);\n}\n\nbool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool is_open = TreeNodeExV(str_id, flags, fmt, args);\n    va_end(args);\n    return is_open;\n}\n\nbool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);\n    va_end(args);\n    return is_open;\n}\n\nbool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiID id = window->GetID(str_id);\n    const char* label, *label_end;\n    ImFormatStringToTempBufferV(&label, &label_end, fmt, args);\n    return TreeNodeBehavior(id, flags, label, label_end);\n}\n\nbool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiID id = window->GetID(ptr_id);\n    const char* label, *label_end;\n    ImFormatStringToTempBufferV(&label, &label_end, fmt, args);\n    return TreeNodeBehavior(id, flags, label, label_end);\n}\n\n// The reason those two functions are not yet in public API is because I would like to design a more feature-full and generic API for this.\n// They are otherwise function (cc: #3823, #9251, #7553, #6754, #5423, #2958, #2079, #1947, #1131, #722)\nbool ImGui::TreeNodeGetOpen(ImGuiID storage_id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage;\n    return storage->GetInt(storage_id, 0) != 0;\n}\n\nvoid ImGui::TreeNodeSetOpen(ImGuiID storage_id, bool is_open)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage;\n    storage->SetInt(storage_id, is_open ? 1 : 0);\n}\n\nbool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags)\n{\n    // Leaf node always open a new tree/id scope. If you never use it, add ImGuiTreeNodeFlags_NoTreePushOnOpen.\n    if (flags & ImGuiTreeNodeFlags_Leaf)\n        return true;\n\n    // We only write to the tree storage if the user clicks, or explicitly use the SetNextItemOpen function\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiStorage* storage = window->DC.StateStorage;\n\n    bool is_open;\n    if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasOpen)\n    {\n        if (g.NextItemData.OpenCond & ImGuiCond_Always)\n        {\n            is_open = g.NextItemData.OpenVal;\n            TreeNodeSetOpen(storage_id, is_open);\n        }\n        else\n        {\n            // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.\n            const int stored_value = storage->GetInt(storage_id, -1);\n            if (stored_value == -1)\n            {\n                is_open = g.NextItemData.OpenVal;\n                TreeNodeSetOpen(storage_id, is_open);\n            }\n            else\n            {\n                is_open = stored_value != 0;\n            }\n        }\n    }\n    else\n    {\n        is_open = storage->GetInt(storage_id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;\n    }\n\n    // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).\n    // NB- If we are above max depth we still allow manually opened nodes to be logged.\n    if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && (window->DC.TreeDepth - g.LogDepthRef) < g.LogDepthToExpand)\n        is_open = true;\n\n    return is_open;\n}\n\n// Store ImGuiTreeNodeStackData for just submitted node.\n// Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase.\nstatic void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags, float x1)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    g.TreeNodeStack.resize(g.TreeNodeStack.Size + 1);\n    ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];\n    tree_node_data->ID = g.LastItemData.ID;\n    tree_node_data->TreeFlags = flags;\n    tree_node_data->ItemFlags = g.LastItemData.ItemFlags;\n    tree_node_data->NavRect = g.LastItemData.NavRect;\n\n    // Initially I tried to latch value for GetColorU32(ImGuiCol_TreeLines) but it's not a good trade-off for very large trees.\n    const bool draw_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) != 0;\n    tree_node_data->DrawLinesX1 = draw_lines ? (x1 + g.FontSize * 0.5f + g.Style.FramePadding.x) : +FLT_MAX;\n    tree_node_data->DrawLinesTableColumn = (draw_lines && g.CurrentTable) ? (ImGuiTableColumnIdx)g.CurrentTable->CurrentColumn : -1;\n    tree_node_data->DrawLinesToNodesY2 = -FLT_MAX;\n    window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth);\n    if (flags & ImGuiTreeNodeFlags_DrawLinesToNodes)\n        window->DC.TreeRecordsClippedNodesY2Mask |= (1 << window->DC.TreeDepth);\n}\n\nbool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n\n    // When not framed, we vertically increase height up to typical framed widget height\n    const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;\n    const bool use_frame_padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding));\n    const ImVec2 padding = use_frame_padding ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y));\n\n    if (!label_end)\n        label_end = FindRenderedTextEnd(label);\n    const ImVec2 label_size = CalcTextSize(label, label_end, false);\n\n    const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2);   // Collapsing arrow width + Spacing\n    const float text_offset_y = use_frame_padding ? ImMax(style.FramePadding.y, window->DC.CurrLineTextBaseOffset) : window->DC.CurrLineTextBaseOffset; // Latch before ItemSize changes it\n    const float text_width = g.FontSize + label_size.x + padding.x * 2;                         // Include collapsing arrow\n\n    const float frame_height = label_size.y + padding.y * 2;\n    const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL);\n    const bool span_all_columns_label = (flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) != 0 && (g.CurrentTable != NULL);\n    ImRect frame_bb;\n    frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x;\n    frame_bb.Min.y = window->DC.CursorPos.y + (text_offset_y - padding.y);\n    frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : (flags & ImGuiTreeNodeFlags_SpanLabelWidth) ? window->DC.CursorPos.x + text_width + padding.x : window->WorkRect.Max.x;\n    frame_bb.Max.y = window->DC.CursorPos.y + (text_offset_y - padding.y) + frame_height;\n    if (display_frame)\n    {\n        const float outer_extend = IM_TRUNC(window->WindowPadding.x * 0.5f); // Framed header expand a little outside of current limits\n        frame_bb.Min.x -= outer_extend;\n        frame_bb.Max.x += outer_extend;\n    }\n\n    ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y);\n    ItemSize(ImVec2(text_width, frame_height), padding.y);\n\n    // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing\n    ImRect interact_bb = frame_bb;\n    if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanLabelWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0)\n        interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f);\n\n    // Compute open and multi-select states before ItemAdd() as it clear NextItem data.\n    ImGuiID storage_id = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasStorageID) ? g.NextItemData.StorageId : id;\n    bool is_open = TreeNodeUpdateNextOpen(storage_id, flags);\n\n    bool is_visible;\n    if (span_all_columns || span_all_columns_label)\n    {\n        // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..\n        const float backup_clip_rect_min_x = window->ClipRect.Min.x;\n        const float backup_clip_rect_max_x = window->ClipRect.Max.x;\n        window->ClipRect.Min.x = window->ParentWorkRect.Min.x;\n        window->ClipRect.Max.x = window->ParentWorkRect.Max.x;\n        is_visible = ItemAdd(interact_bb, id);\n        window->ClipRect.Min.x = backup_clip_rect_min_x;\n        window->ClipRect.Max.x = backup_clip_rect_max_x;\n    }\n    else\n    {\n        is_visible = ItemAdd(interact_bb, id);\n    }\n    g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;\n    g.LastItemData.DisplayRect = frame_bb;\n\n    // If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsToParent enabled:\n    // Store data for the current depth to allow returning to this node from any child item.\n    // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().\n    // It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsToParent by default or move it to ImGuiStyle.\n    bool store_tree_node_stack_data = false;\n    if ((flags & ImGuiTreeNodeFlags_DrawLinesMask_) == 0)\n        flags |= g.Style.TreeLinesFlags;\n    const bool draw_tree_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) && (frame_bb.Min.y < window->ClipRect.Max.y) && (g.Style.TreeLinesSize > 0.0f);\n    if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))\n    {\n        store_tree_node_stack_data = draw_tree_lines;\n        if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsToParent) && !g.NavIdIsAlive)\n            if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())\n                store_tree_node_stack_data = true;\n    }\n\n    const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;\n    if (!is_visible)\n    {\n        if ((flags & ImGuiTreeNodeFlags_DrawLinesToNodes) && (window->DC.TreeRecordsClippedNodesY2Mask & (1 << (window->DC.TreeDepth - 1))))\n        {\n            ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];\n            parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, window->DC.CursorPos.y); // Don't need to aim to mid Y position as we are clipped anyway.\n            if (frame_bb.Min.y >= window->ClipRect.Max.y)\n                window->DC.TreeRecordsClippedNodesY2Mask &= ~(1 << (window->DC.TreeDepth - 1)); // Done\n        }\n        if (is_open && store_tree_node_stack_data)\n            TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID()\n        if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))\n            TreePushOverrideID(id);\n        IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));\n        return is_open;\n    }\n\n    if (span_all_columns || span_all_columns_label)\n    {\n        TablePushBackgroundChannel();\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect;\n        g.LastItemData.ClipRect = window->ClipRect;\n    }\n\n    ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None;\n    if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap))\n        button_flags |= ImGuiButtonFlags_AllowOverlap;\n    if (!is_leaf)\n        button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;\n\n    // We allow clicking on the arrow section with keyboard modifiers held, in order to easily\n    // allow browsing a tree while preserving selection with code implementing multi-selection patterns.\n    // When clicking on the rest of the tree node we always disallow keyboard modifiers.\n    const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x;\n    const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x;\n    const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2);\n\n    const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;\n    if (is_multi_select) // We absolutely need to distinguish open vs select so _OpenOnArrow comes by default\n        flags |= (flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 ? ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick : ImGuiTreeNodeFlags_OpenOnArrow;\n\n    // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags.\n    // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support.\n    // - Single-click on label = Toggle on MouseUp (default, when _OpenOnArrow=0)\n    // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=0)\n    // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=1)\n    // - Double-click on label = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1)\n    // - Double-click on arrow = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1 and _OpenOnArrow=0)\n    // It is rather standard that arrow click react on Down rather than Up.\n    // We set ImGuiButtonFlags_PressedOnClickRelease on OpenOnDoubleClick because we want the item to be active on the initial MouseDown in order for drag and drop to work.\n    if (is_mouse_x_over_arrow)\n        button_flags |= ImGuiButtonFlags_PressedOnClick;\n    else if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)\n        button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;\n    else\n        button_flags |= ImGuiButtonFlags_PressedOnClickRelease;\n    if (flags & ImGuiTreeNodeFlags_NoNavFocus)\n        button_flags |= ImGuiButtonFlags_NoNavFocus;\n\n    bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0;\n    const bool was_selected = selected;\n\n    // Multi-selection support (header)\n    if (is_multi_select)\n    {\n        // Handle multi-select + alter button flags for it\n        MultiSelectItemHeader(id, &selected, &button_flags);\n        if (is_mouse_x_over_arrow)\n            button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease;\n    }\n    else\n    {\n        if (window != g.HoveredWindow || !is_mouse_x_over_arrow)\n            button_flags |= ImGuiButtonFlags_NoKeyModsAllowed;\n    }\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);\n    bool toggled = false;\n    if (!is_leaf)\n    {\n        if (pressed && g.DragDropHoldJustPressedId != id)\n        {\n            if ((flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 || (g.NavActivateId == id && !is_multi_select))\n                toggled = true; // Single click\n            if (flags & ImGuiTreeNodeFlags_OpenOnArrow)\n                toggled |= is_mouse_x_over_arrow && !g.NavHighlightItemUnderNav; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job\n            if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2)\n                toggled = true; // Double click\n        }\n        else if (pressed && g.DragDropHoldJustPressedId == id)\n        {\n            IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold);\n            if (!is_open) // When using Drag and Drop \"hold to open\" we keep the node highlighted after opening, but never close it again.\n                toggled = true;\n            else\n                pressed = false; // Cancel press so it doesn't trigger selection.\n        }\n\n        if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open)\n        {\n            toggled = true;\n            NavClearPreferredPosForAxis(ImGuiAxis_X);\n            NavMoveRequestCancel();\n        }\n        if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority?\n        {\n            toggled = true;\n            NavClearPreferredPosForAxis(ImGuiAxis_X);\n            NavMoveRequestCancel();\n        }\n\n        if (toggled)\n        {\n            is_open = !is_open;\n            window->DC.StateStorage->SetInt(storage_id, is_open);\n            g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen;\n        }\n    }\n\n    // Multi-selection support (footer)\n    if (is_multi_select)\n    {\n        bool pressed_copy = pressed && !toggled;\n        MultiSelectItemFooter(id, &selected, &pressed_copy);\n        if (pressed)\n            SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, interact_bb);\n    }\n\n    if (selected != was_selected)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;\n\n    // Render\n    {\n        const ImU32 text_col = GetColorU32(ImGuiCol_Text);\n        ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact;\n        if (is_multi_select)\n            nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle\n        if (display_frame)\n        {\n            // Framed type\n            const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);\n            RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding);\n            RenderNavCursor(frame_bb, id, nav_render_cursor_flags);\n            if (span_all_columns && !span_all_columns_label)\n                TablePopBackgroundChannel();\n            if (flags & ImGuiTreeNodeFlags_Bullet)\n                RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col);\n            else if (!is_leaf)\n                RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f);\n            else // Leaf without bullet, left-adjusted text\n                text_pos.x -= text_offset_x - padding.x;\n            if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton)\n                frame_bb.Max.x -= g.FontSize + style.FramePadding.x;\n            if (g.LogEnabled)\n                LogSetNextTextDecoration(\"###\", \"###\");\n        }\n        else\n        {\n            // Unframed typed for tree nodes\n            if (hovered || selected)\n            {\n                const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);\n                RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false);\n            }\n            RenderNavCursor(frame_bb, id, nav_render_cursor_flags);\n            if (span_all_columns && !span_all_columns_label)\n                TablePopBackgroundChannel();\n            if (flags & ImGuiTreeNodeFlags_Bullet)\n                RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col);\n            else if (!is_leaf)\n                RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f);\n            if (g.LogEnabled)\n                LogSetNextTextDecoration(\">\", NULL);\n        }\n\n        if (draw_tree_lines)\n            TreeNodeDrawLineToChildNode(ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.5f));\n\n        // Label\n        if (display_frame)\n            RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);\n        else\n            RenderText(text_pos, label, label_end, false);\n\n        if (span_all_columns_label)\n            TablePopBackgroundChannel();\n    }\n\n    if (is_open && store_tree_node_stack_data)\n        TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID()\n    if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))\n        TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));\n    return is_open;\n}\n\n// Draw horizontal line from our parent node\n// This is only called for visible child nodes so we are not too fussy anymore about performances\nvoid ImGui::TreeNodeDrawLineToChildNode(const ImVec2& target_pos)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->DC.TreeDepth == 0 || (window->DC.TreeHasStackDataDepthMask & (1 << (window->DC.TreeDepth - 1))) == 0)\n        return;\n\n    ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];\n    float x1 = ImTrunc(parent_data->DrawLinesX1);\n    float x2 = ImTrunc(target_pos.x - g.Style.ItemInnerSpacing.x);\n    float y = ImTrunc(target_pos.y);\n    float rounding = (g.Style.TreeLinesRounding > 0.0f) ? ImMin(x2 - x1, g.Style.TreeLinesRounding) : 0.0f;\n    parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, y - rounding);\n    if (x1 >= x2)\n        return;\n    if (rounding > 0.0f)\n    {\n        x1 += 0.5f + rounding;\n        window->DrawList->PathArcToFast(ImVec2(x1, y - rounding), rounding, 6, 3);\n        if (x1 < x2)\n            window->DrawList->PathLineTo(ImVec2(x2, y));\n        window->DrawList->PathStroke(GetColorU32(ImGuiCol_TreeLines), ImDrawFlags_None, g.Style.TreeLinesSize);\n    }\n    else\n    {\n        window->DrawList->AddLine(ImVec2(x1, y), ImVec2(x2, y), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);\n    }\n}\n\n// Draw vertical line of the hierarchy\nvoid ImGui::TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    float y1 = ImMax(data->NavRect.Max.y, window->ClipRect.Min.y);\n    float y2 = data->DrawLinesToNodesY2;\n    if (data->TreeFlags & ImGuiTreeNodeFlags_DrawLinesFull)\n    {\n        float y2_full = window->DC.CursorPos.y;\n        if (g.CurrentTable)\n            y2_full = ImMax(g.CurrentTable->RowPosY2, y2_full);\n        y2_full = ImTrunc(y2_full - g.Style.ItemSpacing.y - g.FontSize * 0.5f);\n        if (y2 + (g.Style.ItemSpacing.y + g.Style.TreeLinesRounding) < y2_full) // FIXME: threshold to use ToNodes Y2 instead of Full Y2 when close by ItemSpacing.y\n            y2 = y2_full;\n    }\n    y2 = ImMin(y2, window->ClipRect.Max.y);\n    if (y2 <= y1)\n        return;\n    float x = ImTrunc(data->DrawLinesX1);\n    if (data->DrawLinesTableColumn != -1)\n        TablePushColumnChannel(data->DrawLinesTableColumn);\n    window->DrawList->AddLine(ImVec2(x, y1), ImVec2(x, y2), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);\n    if (data->DrawLinesTableColumn != -1)\n        TablePopColumnChannel();\n}\n\nvoid ImGui::TreePush(const char* str_id)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    Indent();\n    window->DC.TreeDepth++;\n    PushID(str_id);\n}\n\nvoid ImGui::TreePush(const void* ptr_id)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    Indent();\n    window->DC.TreeDepth++;\n    PushID(ptr_id);\n}\n\nvoid ImGui::TreePushOverrideID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    Indent();\n    window->DC.TreeDepth++;\n    PushOverrideID(id);\n}\n\nvoid ImGui::TreePop()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    Unindent();\n\n    window->DC.TreeDepth--;\n    ImU32 tree_depth_mask = (1 << window->DC.TreeDepth);\n\n    if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask)\n    {\n        const ImGuiTreeNodeStackData* data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];\n        IM_ASSERT(data->ID == window->IDStack.back());\n\n        // Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsToParent is enabled)\n        if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsToParent)\n            if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())\n                NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, data);\n\n        // Draw hierarchy lines\n        if (data->DrawLinesX1 != +FLT_MAX && window->DC.CursorPos.y >= window->ClipRect.Min.y)\n            TreeNodeDrawLineToTreePop(data);\n\n        g.TreeNodeStack.pop_back();\n        window->DC.TreeHasStackDataDepthMask &= ~tree_depth_mask;\n        window->DC.TreeRecordsClippedNodesY2Mask &= ~tree_depth_mask;\n    }\n\n    IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.\n    PopID();\n}\n\n// Horizontal distance preceding label when using TreeNode() or Bullet()\nfloat ImGui::GetTreeNodeToLabelSpacing()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize + (g.Style.FramePadding.x * 2.0f);\n}\n\n// Set next TreeNode/CollapsingHeader open state.\nvoid ImGui::SetNextItemOpen(bool is_open, ImGuiCond cond)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.CurrentWindow->SkipItems)\n        return;\n    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasOpen;\n    g.NextItemData.OpenVal = is_open;\n    g.NextItemData.OpenCond = (ImU8)(cond ? cond : ImGuiCond_Always);\n}\n\n// Set next TreeNode/CollapsingHeader storage id.\nvoid ImGui::SetNextItemStorageID(ImGuiID storage_id)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.CurrentWindow->SkipItems)\n        return;\n    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasStorageID;\n    g.NextItemData.StorageId = storage_id;\n}\n\n// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).\n// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().\nbool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n    ImGuiID id = window->GetID(label);\n    return TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader, label);\n}\n\n// p_visible == NULL                        : regular collapsing header\n// p_visible != NULL && *p_visible == true  : show a small close button on the corner of the header, clicking the button will set *p_visible = false\n// p_visible != NULL && *p_visible == false : do not show the header at all\n// Do not mistake this with the Open state of the header itself, which you can adjust with SetNextItemOpen() or ImGuiTreeNodeFlags_DefaultOpen.\nbool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    if (p_visible && !*p_visible)\n        return false;\n\n    ImGuiID id = window->GetID(label);\n    flags |= ImGuiTreeNodeFlags_CollapsingHeader;\n    if (p_visible)\n        flags |= ImGuiTreeNodeFlags_AllowOverlap | (ImGuiTreeNodeFlags)ImGuiTreeNodeFlags_ClipLabelForTrailingButton;\n    bool is_open = TreeNodeBehavior(id, flags, label);\n    if (p_visible != NULL)\n    {\n        // Create a small overlapping close button\n        // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.\n        // FIXME: CloseButton can overlap into text, need find a way to clip the text somehow.\n        ImGuiContext& g = *GImGui;\n        ImGuiLastItemData last_item_backup = g.LastItemData;\n        float button_size = g.FontSize;\n        float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x - button_size);\n        float button_y = g.LastItemData.Rect.Min.y + g.Style.FramePadding.y;\n        ImGuiID close_button_id = GetIDWithSeed(\"#CLOSE\", NULL, id);\n        if (CloseButton(close_button_id, ImVec2(button_x, button_y)))\n            *p_visible = false;\n        g.LastItemData = last_item_backup;\n    }\n\n    return is_open;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Selectable\n//-------------------------------------------------------------------------\n// - Selectable()\n//-------------------------------------------------------------------------\n\n// Tip: pass a non-visible label (e.g. \"##hello\") then you can use the space to draw other text or image.\n// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id.\n// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowOverlap are also frequently used flags.\n// FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported.\nbool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n\n    // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.\n    ImGuiID id = window->GetID(label);\n    ImVec2 label_size = CalcTextSize(label, NULL, true);\n    ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);\n    ImVec2 pos = window->DC.CursorPos;\n    pos.y += window->DC.CurrLineTextBaseOffset;\n    ItemSize(size, 0.0f);\n\n    // Fill horizontal space\n    // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets.\n    const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0;\n    const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x;\n    const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;\n    if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth))\n        size.x = ImMax(label_size.x, max_x - min_x);\n\n    // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable.\n    // FIXME: Not part of layout so not included in clipper calculation, but ItemSize currently doesn't allow offsetting CursorPos.\n    ImRect bb(min_x, pos.y, min_x + size.x, pos.y + size.y);\n    if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0)\n    {\n        const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x;\n        const float spacing_y = style.ItemSpacing.y;\n        const float spacing_L = IM_TRUNC(spacing_x * 0.50f);\n        const float spacing_U = IM_TRUNC(spacing_y * 0.50f);\n        bb.Min.x -= spacing_L;\n        bb.Min.y -= spacing_U;\n        bb.Max.x += (spacing_x - spacing_L);\n        bb.Max.y += (spacing_y - spacing_U);\n    }\n    //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); }\n\n    const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;\n    const ImGuiItemFlags extra_item_flags = disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None;\n    bool is_visible;\n    if (span_all_columns)\n    {\n        // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..\n        const float backup_clip_rect_min_x = window->ClipRect.Min.x;\n        const float backup_clip_rect_max_x = window->ClipRect.Max.x;\n        window->ClipRect.Min.x = window->ParentWorkRect.Min.x;\n        window->ClipRect.Max.x = window->ParentWorkRect.Max.x;\n        is_visible = ItemAdd(bb, id, NULL, extra_item_flags);\n        window->ClipRect.Min.x = backup_clip_rect_min_x;\n        window->ClipRect.Max.x = backup_clip_rect_max_x;\n    }\n    else\n    {\n        is_visible = ItemAdd(bb, id, NULL, extra_item_flags);\n    }\n\n    const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;\n    if (!is_visible)\n        if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(bb)) // Extra layer of \"no logic clip\" for box-select support (would be more overhead to add to ItemAdd)\n            return false;\n\n    const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;\n    if (disabled_item && !disabled_global) // Only testing this as an optimization\n        BeginDisabled();\n\n    // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,\n    // which would be advantageous since most selectable are not selected.\n    if (span_all_columns)\n    {\n        if (g.CurrentTable)\n            TablePushBackgroundChannel();\n        else if (window->DC.CurrentColumns)\n            PushColumnsBackground();\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect;\n        g.LastItemData.ClipRect = window->ClipRect;\n    }\n\n    // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries\n    ImGuiButtonFlags button_flags = 0;\n    if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }\n    if (flags & ImGuiSelectableFlags_NoSetKeyOwner)     { button_flags |= ImGuiButtonFlags_NoSetKeyOwner; }\n    if (flags & ImGuiSelectableFlags_SelectOnClick)     { button_flags |= ImGuiButtonFlags_PressedOnClick; }\n    if (flags & ImGuiSelectableFlags_SelectOnRelease)   { button_flags |= ImGuiButtonFlags_PressedOnRelease; }\n    if (flags & ImGuiSelectableFlags_AllowDoubleClick)  { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }\n    if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; }\n\n    // Multi-selection support (header)\n    const bool was_selected = selected;\n    if (is_multi_select)\n    {\n        // Handle multi-select + alter button flags for it\n        MultiSelectItemHeader(id, &selected, &button_flags);\n    }\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);\n    bool auto_selected = false;\n\n    // Multi-selection support (footer)\n    if (is_multi_select)\n    {\n        MultiSelectItemFooter(id, &selected, &pressed);\n    }\n    else\n    {\n        // Auto-select when moved into\n        // - This will be more fully fleshed in the range-select branch\n        // - This is not exposed as it won't nicely work with some user side handling of shift/control\n        // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons\n        //   - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope())\n        //   - (2) usage will fail with clipped items\n        //   The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.\n        if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId)\n            if (g.NavJustMovedToId == id && (g.NavJustMovedToKeyMods & ImGuiMod_Ctrl) == 0)\n                selected = pressed = auto_selected = true;\n    }\n\n    // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with keyboard/gamepad\n    if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))\n    {\n        if (!g.NavHighlightItemUnderNav && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)\n        {\n            SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect)\n            if (g.IO.ConfigNavCursorVisibleAuto)\n                g.NavCursorVisible = false;\n        }\n    }\n    if (pressed)\n        MarkItemEdited(id);\n\n    if (selected != was_selected)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;\n\n    // Render\n    if (is_visible)\n    {\n        const bool highlighted = hovered || (flags & ImGuiSelectableFlags_Highlight);\n        if (highlighted || selected)\n        {\n            // Between 1.91.0 and 1.91.4 we made selected Selectable use an arbitrary lerp between _Header and _HeaderHovered. Removed that now. (#8106)\n            ImU32 col = GetColorU32((held && highlighted) ? ImGuiCol_HeaderActive : highlighted ? ImGuiCol_HeaderHovered : ImGuiCol_Header);\n            RenderFrame(bb.Min, bb.Max, col, false, 0.0f);\n        }\n        if (g.NavId == id)\n        {\n            ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding;\n            if (is_multi_select)\n                nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle\n            RenderNavCursor(bb, id, nav_render_cursor_flags);\n        }\n    }\n\n    if (span_all_columns)\n    {\n        if (g.CurrentTable)\n            TablePopBackgroundChannel();\n        else if (window->DC.CurrentColumns)\n            PopColumnsBackground();\n    }\n\n    // Text stays at the submission position. Alignment/clipping extents ignore SpanAllColumns.\n    if (is_visible)\n        RenderTextClipped(pos, ImVec2(ImMin(pos.x + size.x, window->WorkRect.Max.x), pos.y + size.y), label, NULL, &label_size, style.SelectableTextAlign, &bb);\n\n    // Automatically close popups\n    if (pressed && !auto_selected && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups))\n        CloseCurrentPopup();\n\n    if (disabled_item && !disabled_global)\n        EndDisabled();\n\n    // Selectable() always returns a pressed state!\n    // Users of BeginMultiSelect()/EndMultiSelect() scope: you may call ImGui::IsItemToggledSelection() to retrieve\n    // selection toggle, only useful if you need that state updated (e.g. for rendering purpose) before reaching EndMultiSelect().\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n    return pressed; //-V1020\n}\n\nbool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)\n{\n    if (Selectable(label, *p_selected, flags, size_arg))\n    {\n        *p_selected = !*p_selected;\n        return true;\n    }\n    return false;\n}\n\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Typing-Select support\n//-------------------------------------------------------------------------\n\n// [Experimental] Currently not exposed in public API.\n// Consume character inputs and return search request, if any.\n// This would typically only be called on the focused window or location you want to grab inputs for, e.g.\n//   if (ImGui::IsWindowFocused(...))\n//       if (ImGuiTypingSelectRequest* req = ImGui::GetTypingSelectRequest())\n//           focus_idx = ImGui::TypingSelectFindMatch(req, my_items.size(), [](void*, int n) { return my_items[n]->Name; }, &my_items, -1);\n// However the code is written in a way where calling it from multiple locations is safe (e.g. to obtain buffer).\nImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTypingSelectState* data = &g.TypingSelectState;\n    ImGuiTypingSelectRequest* out_request = &data->Request;\n\n    // Clear buffer\n    const float TYPING_SELECT_RESET_TIMER = 1.80f;          // FIXME: Potentially move to IO config.\n    const int TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4; // Lock single char matching when repeating same char 4 times\n    if (data->SearchBuffer[0] != 0)\n    {\n        bool clear_buffer = false;\n        clear_buffer |= (g.NavFocusScopeId != data->FocusScope);\n        clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time);\n        clear_buffer |= g.NavAnyRequest;\n        clear_buffer |= g.ActiveId != 0 && g.NavActivateId == 0; // Allow temporary SPACE activation to not interfere\n        clear_buffer |= IsKeyPressed(ImGuiKey_Escape) || IsKeyPressed(ImGuiKey_Enter);\n        clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0;\n        //if (clear_buffer) { IMGUI_DEBUG_LOG(\"GetTypingSelectRequest(): Clear SearchBuffer.\\n\"); }\n        if (clear_buffer)\n            data->Clear();\n    }\n\n    // Append to buffer\n    const int buffer_max_len = IM_COUNTOF(data->SearchBuffer) - 1;\n    int buffer_len = (int)ImStrlen(data->SearchBuffer);\n    bool select_request = false;\n    for (ImWchar w : g.IO.InputQueueCharacters)\n    {\n        const int w_len = ImTextCountUtf8BytesFromStr(&w, &w + 1);\n        if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w)) || (buffer_len + w_len > buffer_max_len)) // Ignore leading blanks\n            continue;\n        char w_buf[5];\n        ImTextCharToUtf8(w_buf, (unsigned int)w);\n        if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0)\n        {\n            select_request = true; // Same character: don't need to append to buffer.\n            continue;\n        }\n        if (data->SingleCharModeLock)\n        {\n            data->Clear(); // Different character: clear\n            buffer_len = 0;\n        }\n        memcpy(data->SearchBuffer + buffer_len, w_buf, w_len + 1); // Append\n        buffer_len += w_len;\n        select_request = true;\n    }\n    g.IO.InputQueueCharacters.resize(0);\n\n    // Handle backspace\n    if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, ImGuiInputFlags_Repeat))\n    {\n        char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len);\n        *p = 0;\n        buffer_len = (int)(p - data->SearchBuffer);\n    }\n\n    // Return request if any\n    if (buffer_len == 0)\n        return NULL;\n    if (select_request)\n    {\n        data->FocusScope = g.NavFocusScopeId;\n        data->LastRequestFrame = g.FrameCount;\n        data->LastRequestTime = (float)g.Time;\n    }\n    out_request->Flags = flags;\n    out_request->SearchBufferLen = buffer_len;\n    out_request->SearchBuffer = data->SearchBuffer;\n    out_request->SelectRequest = (data->LastRequestFrame == g.FrameCount);\n    out_request->SingleCharMode = false;\n    out_request->SingleCharSize = 0;\n\n    // Calculate if buffer contains the same character repeated.\n    // - This can be used to implement a special search mode on first character.\n    // - Performed on UTF-8 codepoint for correctness.\n    // - SingleCharMode is always set for first input character, because it usually leads to a \"next\".\n    if (flags & ImGuiTypingSelectFlags_AllowSingleCharMode)\n    {\n        const char* buf_begin = out_request->SearchBuffer;\n        const char* buf_end = out_request->SearchBuffer + out_request->SearchBufferLen;\n        const int c0_len = ImTextCountUtf8BytesFromChar(buf_begin, buf_end);\n        const char* p = buf_begin + c0_len;\n        for (; p < buf_end; p += c0_len)\n            if (memcmp(buf_begin, p, (size_t)c0_len) != 0)\n                break;\n        const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0;\n        out_request->SingleCharMode = (single_char_count > 0 || data->SingleCharModeLock);\n        out_request->SingleCharSize = (ImS8)c0_len;\n        data->SingleCharModeLock |= (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK); // From now on we stop search matching to lock to single char mode.\n    }\n\n    return out_request;\n}\n\nstatic int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2)\n{\n    int match_len = 0;\n    while (s1 < s1_end && ImToUpper(*s1++) == ImToUpper(*s2++))\n        match_len++;\n    return match_len;\n}\n\n// Default handler for finding a result for typing-select. You may implement your own.\n// You might want to display a tooltip to visualize the current request SearchBuffer\n// When SingleCharMode is set:\n// - it is better to NOT display a tooltip of other on-screen display indicator.\n// - the index of the currently focused item is required.\n//   if your SetNextItemSelectionUserData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData.\nint ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx)\n{\n    if (req == NULL || req->SelectRequest == false) // Support NULL parameter so both calls can be done from same spot.\n        return -1;\n    int idx = -1;\n    if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode))\n        idx = TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx);\n    else\n        idx = TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data);\n    if (idx != -1)\n        SetNavCursorVisibleAfterMove();\n    return idx;\n}\n\n// Special handling when a single character is repeated: perform search on a single letter and goes to next.\nint ImGui::TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx)\n{\n    // FIXME: Assume selection user data is index. Would be extremely practical.\n    //if (nav_item_idx == -1)\n    //    nav_item_idx = (int)g.NavLastValidSelectionUserData;\n\n    int first_match_idx = -1;\n    bool return_next_match = false;\n    for (int idx = 0; idx < items_count; idx++)\n    {\n        const char* item_name = get_item_name_func(user_data, idx);\n        if (ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SingleCharSize, item_name) < req->SingleCharSize)\n            continue;\n        if (return_next_match)                           // Return next matching item after current item.\n            return idx;\n        if (first_match_idx == -1 && nav_item_idx == -1) // Return first match immediately if we don't have a nav_item_idx value.\n            return idx;\n        if (first_match_idx == -1)                       // Record first match for wrapping.\n            first_match_idx = idx;\n        if (nav_item_idx == idx)                         // Record that we encountering nav_item so we can return next match.\n            return_next_match = true;\n    }\n    return first_match_idx; // First result\n}\n\nint ImGui::TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data)\n{\n    int longest_match_idx = -1;\n    int longest_match_len = 0;\n    for (int idx = 0; idx < items_count; idx++)\n    {\n        const char* item_name = get_item_name_func(user_data, idx);\n        const int match_len = ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SearchBufferLen, item_name);\n        if (match_len <= longest_match_len)\n            continue;\n        longest_match_idx = idx;\n        longest_match_len = match_len;\n        if (match_len == req->SearchBufferLen)\n            break;\n    }\n    return longest_match_idx;\n}\n\nvoid ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data)\n{\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    Text(\"SearchBuffer = \\\"%s\\\"\", data->SearchBuffer);\n    Text(\"SingleCharMode = %d, Size = %d, Lock = %d\", data->Request.SingleCharMode, data->Request.SingleCharSize, data->SingleCharModeLock);\n    Text(\"LastRequest = time: %.2f, frame: %d\", data->LastRequestTime, data->LastRequestFrame);\n#else\n    IM_UNUSED(data);\n#endif\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Box-Select support\n// This has been extracted away from Multi-Select logic in the hope that it could eventually be used elsewhere, but hasn't been yet.\n//-------------------------------------------------------------------------\n// Extra logic in MultiSelectItemFooter() and ImGuiListClipper::Step()\n//-------------------------------------------------------------------------\n// - BoxSelectPreStartDrag() [Internal]\n// - BoxSelectActivateDrag() [Internal]\n// - BoxSelectDeactivateDrag() [Internal]\n// - BoxSelectScrollWithMouseDrag() [Internal]\n// - BeginBoxSelect() [Internal]\n// - EndBoxSelect() [Internal]\n//-------------------------------------------------------------------------\n\n// Call on the initial click.\nstatic void BoxSelectPreStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_item)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiBoxSelectState* bs = &g.BoxSelectState;\n    bs->ID = id;\n    bs->IsStarting = true; // Consider starting box-select.\n    bs->IsStartedFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid);\n    bs->IsStartedSetNavIdOnce = bs->IsStartedFromVoid;\n    bs->KeyMods = g.IO.KeyMods;\n    bs->StartPosRel = bs->EndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos);\n    bs->ScrollAccum = ImVec2(0.0f, 0.0f);\n}\n\nstatic void BoxSelectActivateDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    IMGUI_DEBUG_LOG_SELECTION(\"[selection] BeginBoxSelect() 0X%08X: Activate\\n\", bs->ID);\n    bs->IsActive = true;\n    bs->Window = window;\n    bs->IsStarting = false;\n    ImGui::SetActiveID(bs->ID, window);\n    ImGui::SetActiveIdUsingAllKeyboardKeys();\n    if (bs->IsStartedFromVoid && (bs->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0)\n        bs->RequestClear = true;\n}\n\nstatic void BoxSelectDeactivateDrag(ImGuiBoxSelectState* bs)\n{\n    ImGuiContext& g = *GImGui;\n    bs->IsActive = bs->IsStarting = false;\n    if (g.ActiveId == bs->ID)\n    {\n        IMGUI_DEBUG_LOG_SELECTION(\"[selection] BeginBoxSelect() 0X%08X: Deactivate\\n\", bs->ID);\n        ImGui::ClearActiveID();\n    }\n    bs->ID = 0;\n}\n\nstatic void BoxSelectScrollWithMouseDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window, const ImRect& inner_r)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(bs->Window == window);\n    for (int n = 0; n < 2; n++) // each axis\n    {\n        const float mouse_pos = g.IO.MousePos[n];\n        const float dist = (mouse_pos > inner_r.Max[n]) ? mouse_pos - inner_r.Max[n] : (mouse_pos < inner_r.Min[n]) ? mouse_pos - inner_r.Min[n] : 0.0f;\n        const float scroll_curr = window->Scroll[n];\n        if (dist == 0.0f || (dist < 0.0f && scroll_curr < 0.0f) || (dist > 0.0f && scroll_curr >= window->ScrollMax[n]))\n            continue;\n\n        const float speed_multiplier = ImLinearRemapClamp(g.FontSize, g.FontSize * 5.0f, 1.0f, 4.0f, ImAbs(dist)); // x1 to x4 depending on distance\n        const float scroll_step = g.FontSize * 35.0f * speed_multiplier * ImSign(dist) * g.IO.DeltaTime;\n        bs->ScrollAccum[n] += scroll_step;\n\n        // Accumulate into a stored value so we can handle high-framerate\n        const float scroll_step_i = ImFloor(bs->ScrollAccum[n]);\n        if (scroll_step_i == 0.0f)\n            continue;\n        if (n == 0)\n            ImGui::SetScrollX(window, scroll_curr + scroll_step_i);\n        else\n            ImGui::SetScrollY(window, scroll_curr + scroll_step_i);\n        bs->ScrollAccum[n] -= scroll_step_i;\n    }\n}\n\nbool ImGui::BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiBoxSelectState* bs = &g.BoxSelectState;\n    KeepAliveID(box_select_id);\n    if (bs->ID != box_select_id)\n        return false;\n\n    // IsStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry.\n    bs->UnclipMode = false;\n    bs->RequestClear = false;\n    if (bs->IsStarting && IsMouseDragPastThreshold(0))\n        BoxSelectActivateDrag(bs, window);\n    else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false)\n        BoxSelectDeactivateDrag(bs);\n    if (!bs->IsActive)\n        return false;\n\n    // Current frame absolute prev/current rectangles are used to toggle selection.\n    // They are derived from positions relative to scrolling space.\n    ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel);\n    ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already\n    ImVec2 curr_end_pos_abs = g.IO.MousePos;\n    if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow\n        curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max);\n    bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs);\n    bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs);\n    bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs);\n    bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs);\n\n    // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper)\n    // Storing an extra rect used by widgets supporting box-select.\n    if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d)\n        if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x)\n        {\n            bs->UnclipMode = true;\n            bs->UnclipRect = bs->BoxSelectRectPrev; // FIXME-OPT: UnclipRect x coordinates could be intersection of Prev and Curr rect on X axis.\n            bs->UnclipRect.Add(bs->BoxSelectRectCurr);\n        }\n\n    //GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f);\n    //GetForegroundDrawList()->AddRect(bs->BoxSelectRectPrev.Min, bs->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f);\n    //GetForegroundDrawList()->AddRect(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f);\n    return true;\n}\n\nvoid ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiBoxSelectState* bs = &g.BoxSelectState;\n    IM_ASSERT(bs->IsActive);\n    bs->UnclipMode = false;\n\n    // Render selection rectangle\n    bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view\n    ImRect box_select_r = bs->BoxSelectRectCurr;\n    box_select_r.ClipWith(scope_rect);\n    window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling\n    window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT FIXME-DPI: Styling\n\n    // Scroll\n    const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0;\n    if (enable_scroll)\n    {\n        ImRect scroll_r = scope_rect;\n        scroll_r.Expand(-g.FontSize);\n        //GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255));\n        if (!scroll_r.Contains(g.IO.MousePos))\n            BoxSelectScrollWithMouseDrag(bs, window, scroll_r);\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Multi-Select support\n//-------------------------------------------------------------------------\n// - DebugLogMultiSelectRequests() [Internal]\n// - CalcScopeRect() [Internal]\n// - BeginMultiSelect()\n// - EndMultiSelect()\n// - SetNextItemSelectionUserData()\n// - MultiSelectItemHeader() [Internal]\n// - MultiSelectItemFooter() [Internal]\n// - DebugNodeMultiSelectState() [Internal]\n//-------------------------------------------------------------------------\n\nstatic void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io)\n{\n    ImGuiContext& g = *GImGui;\n    IM_UNUSED(function);\n    for (const ImGuiSelectionRequest& req : io->Requests)\n    {\n        if (req.Type == ImGuiSelectionRequestType_SetAll)    IMGUI_DEBUG_LOG_SELECTION(\"[selection] %s: Request: SetAll %d (= %s)\\n\", function, req.Selected, req.Selected ? \"SelectAll\" : \"Clear\");\n        if (req.Type == ImGuiSelectionRequestType_SetRange)  IMGUI_DEBUG_LOG_SELECTION(\"[selection] %s: Request: SetRange %\" IM_PRId64 \"..%\" IM_PRId64 \" (0x%\" IM_PRIX64 \"..0x%\" IM_PRIX64 \") = %d (dir %d)\\n\", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.Selected, req.RangeDirection);\n    }\n}\n\nstatic ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)\n    {\n        // Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only\n        return ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin));\n    }\n    else\n    {\n        // When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970)\n        ImRect scope_rect = window->InnerClipRect;\n        if (g.CurrentTable != NULL)\n            scope_rect = g.CurrentTable->HostClipRect;\n\n        // Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect?\n        scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max);\n        return scope_rect;\n    }\n}\n\n// Return ImGuiMultiSelectIO structure.\n// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect().\n// Passing 'selection_size' and 'items_count' parameters is currently optional.\n// - 'selection_size' is useful to disable some shortcut routing: e.g. ImGuiMultiSelectFlags_ClearOnEscape won't claim Escape key when selection_size 0,\n//    allowing a first press to clear selection THEN the second press to leave child window and return to parent.\n// - 'items_count' is stored in ImGuiMultiSelectIO which makes it a convenient way to pass the information to your ApplyRequest() handler (but you may pass it differently).\n// - If they are costly for you to compute (e.g. external intrusive selection without maintaining size), you may avoid them and pass -1.\n//   - If you can easily tell if your selection is empty or not, you may pass 0/1, or you may enable ImGuiMultiSelectFlags_ClearOnEscape flag dynamically.\nImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size, int items_count)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    if (++g.MultiSelectTempDataStacked > g.MultiSelectTempData.Size)\n        g.MultiSelectTempData.resize(g.MultiSelectTempDataStacked, ImGuiMultiSelectTempData());\n    ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1];\n    IM_STATIC_ASSERT(offsetof(ImGuiMultiSelectTempData, IO) == 0); // Clear() relies on that.\n    g.CurrentMultiSelect = ms;\n    if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0)\n        flags |= ImGuiMultiSelectFlags_ScopeWindow;\n    if (flags & ImGuiMultiSelectFlags_SingleSelect)\n        flags &= ~(ImGuiMultiSelectFlags_BoxSelect2d | ImGuiMultiSelectFlags_BoxSelect1d);\n    if (flags & ImGuiMultiSelectFlags_BoxSelect2d)\n        flags &= ~ImGuiMultiSelectFlags_BoxSelect1d;\n\n    // FIXME: Workaround to the fact we override CursorMaxPos, meaning size measurement are lost. (#8250)\n    // They should perhaps be stacked properly?\n    if (ImGuiTable* table = g.CurrentTable)\n        if (table->CurrentColumn != -1)\n            TableEndCell(table); // This is currently safe to call multiple time. If that properly is lost we can extract the \"save measurement\" part of it.\n\n    // FIXME: BeginFocusScope()\n    const ImGuiID id = window->IDStack.back();\n    ms->Clear();\n    ms->FocusScopeId = id;\n    ms->Flags = flags;\n    ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId);\n    ms->BackupCursorMaxPos = window->DC.CursorMaxPos;\n    ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos;\n    PushFocusScope(ms->FocusScopeId);\n    if (flags & ImGuiMultiSelectFlags_ScopeWindow) // Mark parent child window as navigable into, with highlight. Assume user will always submit interactive items.\n        window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main;\n\n    // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame.\n    ms->KeyMods = g.NavJustMovedToId ? (g.NavJustMovedToIsTabbing ? 0 : g.NavJustMovedToKeyMods) : g.IO.KeyMods;\n    if (flags & ImGuiMultiSelectFlags_NoRangeSelect)\n        ms->KeyMods &= ~ImGuiMod_Shift;\n\n    // Bind storage\n    ImGuiMultiSelectState* storage = g.MultiSelectStorage.GetOrAddByKey(id);\n    storage->ID = id;\n    storage->LastFrameActive = g.FrameCount;\n    storage->LastSelectionSize = selection_size;\n    storage->Window = window;\n    ms->Storage = storage;\n\n    // Output to user\n    ms->IO.Requests.resize(0);\n    ms->IO.RangeSrcItem = storage->RangeSrcItem;\n    ms->IO.NavIdItem = storage->NavIdItem;\n    ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false;\n    ms->IO.ItemsCount = items_count;\n\n    // Clear when using Navigation to move within the scope\n    // (we compare FocusScopeId so it possible to use multiple selections inside a same window)\n    bool request_clear = false;\n    bool request_select_all = false;\n    if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData)\n    {\n        if (ms->KeyMods & ImGuiMod_Shift)\n            ms->IsKeyboardSetRange = true;\n        if (ms->IsKeyboardSetRange)\n            IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid); // Not ready -> could clear?\n        if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0)\n            request_clear = true;\n    }\n    else if (g.NavJustMovedFromFocusScopeId == ms->FocusScopeId)\n    {\n        // Also clear on leaving scope (may be optional?)\n        if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0)\n            request_clear = true;\n    }\n\n    // Box-select handling: update active state.\n    ImGuiBoxSelectState* bs = &g.BoxSelectState;\n    if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))\n    {\n        ms->BoxSelectId = GetID(\"##BoxSelect\");\n        if (BeginBoxSelect(CalcScopeRect(ms, window), window, ms->BoxSelectId, flags))\n            request_clear |= bs->RequestClear;\n    }\n\n    if (ms->IsFocused)\n    {\n        // Shortcut: Clear selection (Escape)\n        // - Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window.\n        // - Box select also handle Escape and needs to pass an id to bypass ActiveIdUsingAllKeyboardKeys lock.\n        if (flags & ImGuiMultiSelectFlags_ClearOnEscape)\n        {\n            if (selection_size != 0 || bs->IsActive)\n                if (Shortcut(ImGuiKey_Escape, ImGuiInputFlags_None, bs->IsActive ? bs->ID : 0))\n                {\n                    request_clear = true;\n                    if (bs->IsActive)\n                        BoxSelectDeactivateDrag(bs);\n                }\n        }\n\n        // Shortcut: Select all (Ctrl+A)\n        if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll))\n            if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A))\n                request_select_all = true;\n    }\n\n    if (request_clear || request_select_all)\n    {\n        MultiSelectAddSetAll(ms, request_select_all);\n        if (!request_select_all)\n            storage->LastSelectionSize = 0;\n    }\n    ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1;\n    ms->LastSubmittedItem = ImGuiSelectionUserData_Invalid;\n\n    if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)\n        DebugLogMultiSelectRequests(\"BeginMultiSelect\", &ms->IO);\n\n    return &ms->IO;\n}\n\n// Return updated ImGuiMultiSelectIO structure.\n// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect().\nImGuiMultiSelectIO* ImGui::EndMultiSelect()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect;\n    ImGuiMultiSelectState* storage = ms->Storage;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT_USER_ERROR(ms->FocusScopeId == g.CurrentFocusScopeId, \"EndMultiSelect() FocusScope mismatch!\");\n    IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow);\n    IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect);\n\n    ImRect scope_rect = CalcScopeRect(ms, window);\n    if (ms->IsFocused)\n    {\n        // We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here.\n        if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at beginning of the scope (see tests for easy failure)\n        {\n            IMGUI_DEBUG_LOG_SELECTION(\"[selection] EndMultiSelect: Reset RangeSrcItem.\\n\"); // Will set be to NavId.\n            storage->RangeSrcItem = ImGuiSelectionUserData_Invalid;\n        }\n        if (ms->NavIdPassedBy == false && storage->NavIdItem != ImGuiSelectionUserData_Invalid)\n        {\n            IMGUI_DEBUG_LOG_SELECTION(\"[selection] EndMultiSelect: Reset NavIdItem.\\n\");\n            storage->NavIdItem = ImGuiSelectionUserData_Invalid;\n            storage->NavIdSelected = -1;\n        }\n\n        if ((ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) && GetBoxSelectState(ms->BoxSelectId))\n            EndBoxSelect(scope_rect, ms->Flags);\n    }\n\n    if (ms->IsEndIO == false)\n        ms->IO.Requests.resize(0);\n\n    // Clear selection when clicking void?\n    // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection!\n    // The InnerRect test is necessary for non-child/decorated windows.\n    bool scope_hovered = IsWindowHovered() && window->InnerRect.Contains(g.IO.MousePos);\n    if (scope_hovered && (ms->Flags & ImGuiMultiSelectFlags_ScopeRect))\n        scope_hovered &= scope_rect.Contains(g.IO.MousePos);\n    if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0)\n    {\n        if (ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))\n        {\n            if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1)\n            {\n                BoxSelectPreStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid);\n                FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);\n                SetHoveredID(ms->BoxSelectId);\n                if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)\n                    SetNavID(0, ImGuiNavLayer_Main, ms->FocusScopeId, ImRect(g.IO.MousePos, g.IO.MousePos)); // Automatically switch FocusScope for initial click from void to box-select.\n            }\n        }\n\n        if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid)\n            if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None)\n                MultiSelectAddSetAll(ms, false);\n    }\n\n    // Courtesy nav wrapping helper flag\n    if (ms->Flags & ImGuiMultiSelectFlags_NavWrapX)\n    {\n        IM_ASSERT(ms->Flags & ImGuiMultiSelectFlags_ScopeWindow); // Only supported at window scope\n        ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX);\n    }\n\n    // Unwind\n    window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos);\n    PopFocusScope();\n\n    if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)\n        DebugLogMultiSelectRequests(\"EndMultiSelect\", &ms->IO);\n\n    ms->FocusScopeId = 0;\n    ms->Flags = ImGuiMultiSelectFlags_None;\n    g.CurrentMultiSelect = (--g.MultiSelectTempDataStacked > 0) ? &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] : NULL;\n\n    return &ms->IO;\n}\n\nvoid ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data)\n{\n    // Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code!\n    // This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api.\n    ImGuiContext& g = *GImGui;\n    g.NextItemData.SelectionUserData = selection_user_data;\n    g.NextItemData.FocusScopeId = g.CurrentFocusScopeId;\n\n    if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect)\n    {\n        // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping)\n        g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect;\n        if (ms->IO.RangeSrcItem == selection_user_data)\n            ms->RangeSrcPassedBy = true;\n    }\n    else\n    {\n        g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData;\n    }\n}\n\n// In charge of:\n// - Applying SetAll for submitted items.\n// - Applying SetRange for submitted items and record end points.\n// - Altering button behavior flags to facilitate use with drag and drop.\nvoid ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect;\n\n    bool selected = *p_selected;\n    if (ms->IsFocused)\n    {\n        ImGuiMultiSelectState* storage = ms->Storage;\n        ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData;\n        IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && \"Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope\");\n\n        // Apply SetAll (Clear/SelectAll) requests requested by BeginMultiSelect().\n        // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper.\n        // If you are using a clipper you need to process the SetAll request after calling BeginMultiSelect()\n        if (ms->LoopRequestSetAll != -1)\n            selected = (ms->LoopRequestSetAll == 1);\n\n        // When using Shift+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection)\n        // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function)\n        if (ms->IsKeyboardSetRange)\n        {\n            IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0);\n            const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id;     // Assume that g.NavJustMovedToId is not clipped.\n            if (is_range_dst)\n                ms->RangeDstPassedBy = true;\n            if (is_range_dst && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst\n            {\n                storage->RangeSrcItem = item_data;\n                storage->RangeSelected = selected ? 1 : 0;\n            }\n            const bool is_range_src = storage->RangeSrcItem == item_data;\n            if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy)\n            {\n                // Apply range-select value to visible items\n                IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1);\n                selected = (storage->RangeSelected != 0);\n            }\n            else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0 && (ms->Flags & ImGuiMultiSelectFlags_NoAutoClear) == 0)\n            {\n                // Clear other items\n                selected = false;\n            }\n        }\n        *p_selected = selected;\n    }\n\n    // Alter button behavior flags\n    // To handle drag and drop of multiple items we need to avoid clearing selection on click.\n    // Enabling this test makes actions using Ctrl+Shift delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items.\n    if (p_button_flags != NULL)\n    {\n        ImGuiButtonFlags button_flags = *p_button_flags;\n        button_flags |= ImGuiButtonFlags_NoHoveredOnFocus;\n        if ((!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) && !(ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease))\n            button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease;\n        else\n            button_flags |= ImGuiButtonFlags_PressedOnClickRelease;\n        *p_button_flags = button_flags;\n    }\n}\n\n// In charge of:\n// - Auto-select on navigation.\n// - Box-select toggle handling.\n// - Right-click handling.\n// - Altering selection based on Ctrl/Shift modifiers, both for keyboard and mouse.\n// - Record current selection state for RangeSrc\n// This is all rather complex, best to run and refer to \"widgets_multiselect_xxx\" tests in imgui_test_suite.\nvoid ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    bool selected = *p_selected;\n    bool pressed = *p_pressed;\n    ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect;\n    ImGuiMultiSelectState* storage = ms->Storage;\n    if (pressed)\n        ms->IsFocused = true;\n\n    bool hovered = false;\n    if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect)\n        hovered = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);\n    if (!ms->IsFocused && !hovered)\n        return;\n\n    ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData;\n\n    ImGuiMultiSelectFlags flags = ms->Flags;\n    const bool is_singleselect = (flags & ImGuiMultiSelectFlags_SingleSelect) != 0;\n    bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0;\n    bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0;\n\n    bool apply_to_range_src = false;\n\n    if (g.NavId == id && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid)\n        apply_to_range_src = true;\n    if (ms->IsEndIO == false)\n    {\n        ms->IO.Requests.resize(0);\n        ms->IsEndIO = true;\n    }\n\n    // Auto-select as you navigate a list\n    if (g.NavJustMovedToId == id)\n    {\n        if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)\n        {\n            if (is_ctrl && is_shift)\n                pressed = true;\n            else if (!is_ctrl)\n                selected = pressed = true;\n        }\n        else\n        {\n            // With NoAutoSelect, using Shift+keyboard performs a write/copy\n            if (is_shift)\n                pressed = true;\n            else if (!is_ctrl)\n                apply_to_range_src = true; // Since if (pressed) {} main block is not running we update this\n        }\n    }\n\n    if (apply_to_range_src)\n    {\n        storage->RangeSrcItem = item_data;\n        storage->RangeSelected = selected; // Will be updated at the end of this function anyway.\n    }\n\n    // Box-select toggle handling\n    if (ms->BoxSelectId != 0)\n        if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId))\n        {\n            const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect);\n            const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect);\n            if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr))\n            {\n                if (storage->LastSelectionSize <= 0 && bs->IsStartedSetNavIdOnce)\n                {\n                    pressed = true; // First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overridden anyway)\n                    bs->IsStartedSetNavIdOnce = false;\n                }\n                else\n                {\n                    selected = !selected;\n                    MultiSelectAddSetRange(ms, selected, +1, item_data, item_data);\n                }\n                storage->LastSelectionSize = ImMax(storage->LastSelectionSize + 1, 1);\n            }\n        }\n\n    // Right-click handling.\n    // FIXME-MULTISELECT: Maybe should be moved to Selectable()? Also see #5816, #8200, #9015\n    if (hovered && IsMouseClicked(1) && (flags & (ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoSelectOnRightClick)) == 0)\n    {\n        if (g.ActiveId != 0 && g.ActiveId != id)\n            ClearActiveID();\n        SetFocusID(id, window);\n        if (!pressed && !selected)\n        {\n            pressed = true;\n            is_ctrl = is_shift = false;\n        }\n    }\n\n    // Unlike Space, Enter doesn't alter selection (but can still return a press) unless current item is not selected.\n    // The later, \"unless current item is not select\", may become optional? It seems like a better default if Enter doesn't necessarily open something\n    // (unlike e.g. Windows explorer). For use case where Enter always open something, we might decide to make this optional?\n    const bool enter_pressed = pressed && (g.NavActivateId == id) && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput);\n\n    // Alter selection\n    if (pressed && (!enter_pressed || !selected))\n    {\n        // Box-select\n        ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;\n        if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))\n            if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1)\n                BoxSelectPreStartDrag(ms->BoxSelectId, item_data);\n\n        //----------------------------------------------------------------------------------------\n        // ACTION                      | Begin  | Pressed/Activated  | End\n        //----------------------------------------------------------------------------------------\n        // Keys Navigated:             | Clear  | Src=item, Sel=1               SetRange 1\n        // Keys Navigated: Ctrl        | n/a    | n/a\n        // Keys Navigated:      Shift  | n/a    | Dst=item, Sel=1,   => Clear + SetRange 1\n        // Keys Navigated: Ctrl+Shift  | n/a    | Dst=item, Sel=Src  => Clear + SetRange Src-Dst\n        // Keys Activated:             | n/a    | Src=item, Sel=1    => Clear + SetRange 1\n        // Keys Activated: Ctrl        | n/a    | Src=item, Sel=!Sel =>         SetSange 1\n        // Keys Activated:      Shift  | n/a    | Dst=item, Sel=1    => Clear + SetSange 1\n        //----------------------------------------------------------------------------------------\n        // Mouse Pressed:              | n/a    | Src=item, Sel=1,   => Clear + SetRange 1\n        // Mouse Pressed:  Ctrl        | n/a    | Src=item, Sel=!Sel =>         SetRange 1\n        // Mouse Pressed:       Shift  | n/a    | Dst=item, Sel=1,   => Clear + SetRange 1\n        // Mouse Pressed:  Ctrl+Shift  | n/a    | Dst=item, Sel=!Sel =>         SetRange Src-Dst\n        //----------------------------------------------------------------------------------------\n\n        if ((flags & ImGuiMultiSelectFlags_NoAutoClear) == 0)\n        {\n            bool request_clear = false;\n            if (is_singleselect)\n                request_clear = true;\n            else if ((input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) && !is_ctrl)\n                request_clear = (flags & ImGuiMultiSelectFlags_NoAutoClearOnReselect) ? !selected : true;\n            else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl)\n                request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again.\n            if (request_clear)\n                MultiSelectAddSetAll(ms, false);\n        }\n\n        int range_direction;\n        bool range_selected;\n        if (is_shift && !is_singleselect)\n        {\n            //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue);\n            if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid)\n                storage->RangeSrcItem = item_data;\n            if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)\n            {\n                // Shift+Arrow always select\n                // Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage->RangeSelected)\n                range_selected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true;\n            }\n            else\n            {\n                // Shift+Arrow copy source selection state\n                // Shift+Click always copy from target selection state\n                if (ms->IsKeyboardSetRange)\n                    range_selected = (storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true;\n                else\n                    range_selected = !selected;\n            }\n            range_direction = ms->RangeSrcPassedBy ? +1 : -1;\n        }\n        else\n        {\n            // Ctrl inverts selection, otherwise always select\n            if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)\n                selected = is_ctrl ? !selected : true;\n            else\n                selected = !selected;\n            storage->RangeSrcItem = item_data;\n            range_selected = selected;\n            range_direction = +1;\n        }\n        MultiSelectAddSetRange(ms, range_selected, range_direction, storage->RangeSrcItem, item_data);\n    }\n\n    // Update/store the selection state of the Source item (used by Ctrl+Shift, when Source is unselected we perform a range unselect)\n    if (storage->RangeSrcItem == item_data)\n        storage->RangeSelected = selected ? 1 : 0;\n\n    // Update/store the selection state of focused item\n    if (g.NavId == id)\n    {\n        storage->NavIdItem = item_data;\n        storage->NavIdSelected = selected ? 1 : 0;\n    }\n    if (storage->NavIdItem == item_data)\n        ms->NavIdPassedBy = true;\n    ms->LastSubmittedItem = item_data;\n\n    *p_selected = selected;\n    *p_pressed = pressed;\n}\n\nvoid ImGui::MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected)\n{\n    ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, selected, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid };\n    ms->IO.Requests.resize(0);      // Can always clear previous requests\n    ms->IO.Requests.push_back(req); // Add new request\n}\n\nvoid ImGui::MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item)\n{\n    // Merge contiguous spans into same request (unless NoRangeSelect is set which guarantees single-item ranges)\n    if (ms->IO.Requests.Size > 0 && first_item == last_item && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0)\n    {\n        ImGuiSelectionRequest* prev = &ms->IO.Requests.Data[ms->IO.Requests.Size - 1];\n        if (prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->LastSubmittedItem && prev->Selected == selected)\n        {\n            prev->RangeLastItem = last_item;\n            return;\n        }\n    }\n\n    ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, (ImS8)range_dir, (range_dir > 0) ? first_item : last_item, (range_dir > 0) ? last_item : first_item };\n    ms->IO.Requests.push_back(req); // Add new request\n}\n\nvoid ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage)\n{\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    const bool is_active = (storage->LastFrameActive >= GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here.\n    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }\n    bool open = TreeNode((void*)(intptr_t)storage->ID, \"MultiSelect 0x%08X in '%s'%s\", storage->ID, storage->Window ? storage->Window->Name : \"N/A\", is_active ? \"\" : \" *Inactive*\");\n    if (!is_active) { PopStyleColor(); }\n    if (!open)\n        return;\n    Text(\"RangeSrcItem = %\" IM_PRId64 \" (0x%\" IM_PRIX64 \"), RangeSelected = %d\", storage->RangeSrcItem, storage->RangeSrcItem, storage->RangeSelected);\n    Text(\"NavIdItem = %\" IM_PRId64 \" (0x%\" IM_PRIX64 \"), NavIdSelected = %d\", storage->NavIdItem, storage->NavIdItem, storage->NavIdSelected);\n    Text(\"LastSelectionSize = %d\", storage->LastSelectionSize); // Provided by user\n    TreePop();\n#else\n    IM_UNUSED(storage);\n#endif\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Multi-Select helpers\n//-------------------------------------------------------------------------\n// - ImGuiSelectionBasicStorage\n// - ImGuiSelectionExternalStorage\n//-------------------------------------------------------------------------\n\nImGuiSelectionBasicStorage::ImGuiSelectionBasicStorage()\n{\n    Size = 0;\n    PreserveOrder = false;\n    UserData = NULL;\n    AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; };\n    _SelectionOrder = 1; // Always >0\n}\n\nvoid ImGuiSelectionBasicStorage::Clear()\n{\n    Size = 0;\n    _SelectionOrder = 1; // Always >0\n    _Storage.Data.resize(0);\n}\n\nvoid ImGuiSelectionBasicStorage::Swap(ImGuiSelectionBasicStorage& r)\n{\n    ImSwap(Size, r.Size);\n    ImSwap(_SelectionOrder, r._SelectionOrder);\n    _Storage.Data.swap(r._Storage.Data);\n}\n\nbool ImGuiSelectionBasicStorage::Contains(ImGuiID id) const\n{\n    return _Storage.GetInt(id, 0) != 0;\n}\n\nstatic int IMGUI_CDECL PairComparerByValueInt(const void* lhs, const void* rhs)\n{\n    int lhs_v = ((const ImGuiStoragePair*)lhs)->val_i;\n    int rhs_v = ((const ImGuiStoragePair*)rhs)->val_i;\n    return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);\n}\n\n// GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user.\n// (e.g. store unselected vs compact down, compact down on demand, use raw ImVector<ImGuiID> instead of ImGuiStorage...)\nbool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID* out_id)\n{\n    ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it;\n    ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size;\n    if (PreserveOrder && it == NULL && it_end != NULL)\n        ImQsort(_Storage.Data.Data, (size_t)_Storage.Data.Size, sizeof(ImGuiStoragePair), PairComparerByValueInt); // ~ImGuiStorage::BuildSortByValueInt()\n    if (it == NULL)\n        it = _Storage.Data.Data;\n    IM_ASSERT(it >= _Storage.Data.Data && it <= it_end);\n    if (it != it_end)\n        while (it->val_i == 0 && it < it_end)\n            it++;\n    const bool has_more = (it != it_end);\n    *opaque_it = has_more ? (void**)(it + 1) : (void**)(it);\n    *out_id = has_more ? it->key : 0;\n    if (PreserveOrder && !has_more)\n        _Storage.BuildSortByKey();\n    return has_more;\n}\n\nvoid ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected)\n{\n    int* p_int = _Storage.GetIntRef(id, 0);\n    if (selected && *p_int == 0) { *p_int = _SelectionOrder++; Size++; }\n    else if (!selected && *p_int != 0) { *p_int = 0; Size--; }\n}\n\n// Optimized for batch edits (with same value of 'selected')\nstatic void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends, int selection_order)\n{\n    ImGuiStorage* storage = &selection->_Storage;\n    ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id);\n    const bool is_contained = (it != storage->Data.Data + size_before_amends) && (it->key == id);\n    if (selected == (is_contained && it->val_i != 0))\n        return;\n    if (selected && !is_contained)\n        storage->Data.push_back(ImGuiStoragePair(id, selection_order)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish()\n    else if (is_contained)\n        it->val_i = selected ? selection_order : 0; // Modify in-place.\n    selection->Size += selected ? +1 : -1;\n}\n\nstatic void ImGuiSelectionBasicStorage_BatchFinish(ImGuiSelectionBasicStorage* selection, bool selected, int size_before_amends)\n{\n    ImGuiStorage* storage = &selection->_Storage;\n    if (selected && selection->Size != size_before_amends)\n        storage->BuildSortByKey(); // When done selecting: sort everything\n}\n\n// Apply requests coming from BeginMultiSelect() and EndMultiSelect().\n// - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.\n// - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem.\n//   - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection.\n//   - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform\n//     a lookup in order to have some way to iterate/interpolate between two items.\n// - A full-featured application is likely to allow search/filtering which is likely to lead to using indices\n//   and constructing a view index <> object id/ptr data structure anyway.\n// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ImGuiSelectionBasicStorage' INDIRECTION LOGIC.\n// Notice that with the simplest adapter (using indices everywhere), all functions return their parameters.\n// The most simple implementation (using indices everywhere) would look like:\n//   for (ImGuiSelectionRequest& req : ms_io->Requests)\n//   {\n//      if (req.Type == ImGuiSelectionRequestType_SetAll)    { Clear(); if (req.Selected) { for (int n = 0; n < items_count; n++) { SetItemSelected(n, true); } }\n//      if (req.Type == ImGuiSelectionRequestType_SetRange)  { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { SetItemSelected(n, ms_io->Selected); } }\n//   }\nvoid ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)\n{\n    // For convenience we obtain ItemsCount as passed to BeginMultiSelect(), which is optional.\n    // It makes sense when using ImGuiSelectionBasicStorage to simply pass your items count to BeginMultiSelect().\n    // Other scheme may handle SetAll differently.\n    IM_ASSERT(ms_io->ItemsCount != -1 && \"Missing value for items_count in BeginMultiSelect() call!\");\n    IM_ASSERT(AdapterIndexToStorageId != NULL);\n\n    // This is optimized/specialized to cope with very large selections (e.g. 100k+ items)\n    // - A simpler version could call SetItemSelected() directly instead of ImGuiSelectionBasicStorage_BatchSetItemSelected() + ImGuiSelectionBasicStorage_BatchFinish().\n    // - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass.\n    // - A more optimal version wouldn't even use ImGuiStorage but directly a ImVector<ImGuiID> to reduce bandwidth, but this is a reasonable trade off to reuse code.\n    // - There are many ways this could be better optimized. The worse case scenario being: using BoxSelect2d in a grid, box-select scrolling down while wiggling\n    //   left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each.\n    // FIXME-OPT: For each block of consecutive SetRange request:\n    // - add all requests to a sorted list, store ID, selected, offset in ImGuiStorage.\n    // - rewrite sorted storage a single time.\n    for (ImGuiSelectionRequest& req : ms_io->Requests)\n    {\n        if (req.Type == ImGuiSelectionRequestType_SetAll)\n        {\n            Clear();\n            if (req.Selected)\n            {\n                _Storage.Data.reserve(ms_io->ItemsCount);\n                const int size_before_amends = _Storage.Data.Size;\n                for (int idx = 0; idx < ms_io->ItemsCount; idx++, _SelectionOrder++)\n                    ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, _SelectionOrder);\n                ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends);\n            }\n        }\n        else if (req.Type == ImGuiSelectionRequestType_SetRange)\n        {\n            const int selection_changes = (int)req.RangeLastItem - (int)req.RangeFirstItem + 1;\n            //ImGuiContext& g = *GImGui; IMGUI_DEBUG_LOG_SELECTION(\"Req %d/%d: set %d to %d\\n\", ms_io->Requests.index_from_ptr(&req), ms_io->Requests.Size, selection_changes, req.Selected);\n            if (selection_changes == 1 || (selection_changes < Size / 100))\n            {\n                // Multiple sorted insertion + copy likely to be faster.\n                // Technically we could do a single copy with a little more work (sort sequential SetRange requests)\n                for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++)\n                    SetItemSelected(GetStorageIdFromIndex(idx), req.Selected);\n            }\n            else\n            {\n                // Append insertion + single sort likely be faster.\n                // Use req.RangeDirection to set order field so that Shift+Clicking from 1 to 5 is different than Shift+Clicking from 5 to 1\n                const int size_before_amends = _Storage.Data.Size;\n                int selection_order = _SelectionOrder + ((req.RangeDirection < 0) ? selection_changes - 1 : 0);\n                for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++, selection_order += req.RangeDirection)\n                    ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, selection_order);\n                if (req.Selected)\n                    _SelectionOrder += selection_changes;\n                ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends);\n            }\n        }\n    }\n}\n\n//-------------------------------------------------------------------------\n\nImGuiSelectionExternalStorage::ImGuiSelectionExternalStorage()\n{\n    UserData = NULL;\n    AdapterSetItemSelected = NULL;\n}\n\n// Apply requests coming from BeginMultiSelect() and EndMultiSelect().\n// We also pull 'ms_io->ItemsCount' as passed for BeginMultiSelect() for consistency with ImGuiSelectionBasicStorage\n// This makes no assumption about underlying storage.\nvoid ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)\n{\n    IM_ASSERT(AdapterSetItemSelected);\n    for (ImGuiSelectionRequest& req : ms_io->Requests)\n    {\n        if (req.Type == ImGuiSelectionRequestType_SetAll)\n            for (int idx = 0; idx < ms_io->ItemsCount; idx++)\n                AdapterSetItemSelected(this, idx, req.Selected);\n        if (req.Type == ImGuiSelectionRequestType_SetRange)\n            for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++)\n                AdapterSetItemSelected(this, idx, req.Selected);\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: ListBox\n//-------------------------------------------------------------------------\n// - BeginListBox()\n// - EndListBox()\n// - ListBox()\n//-------------------------------------------------------------------------\n\n// This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.\n// This handle some subtleties with capturing info from the label.\n// If you don't need a label you can pretty much directly use ImGui::BeginChild() with ImGuiChildFlags_FrameStyle.\n// Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. \"##empty\"\n// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.5f * item_height).\nbool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = GetID(label);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    // Size default to hold ~7.25 items.\n    // Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.\n    ImVec2 size = ImTrunc(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f));\n    ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));\n    ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);\n    ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));\n    g.NextItemData.ClearFlags();\n\n    if (!IsRectVisible(bb.Min, bb.Max))\n    {\n        ItemSize(bb.GetSize(), style.FramePadding.y);\n        ItemAdd(bb, 0, &frame_bb);\n        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n        return false;\n    }\n\n    // FIXME-OPT: We could omit the BeginGroup() if label_size.x == 0.0f but would need to omit the EndGroup() as well.\n    BeginGroup();\n    if (label_size.x > 0.0f)\n    {\n        ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y);\n        RenderText(label_pos, label);\n        window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size);\n        AlignTextToFramePadding();\n    }\n\n    BeginChild(id, frame_bb.GetSize(), ImGuiChildFlags_FrameStyle);\n    return true;\n}\n\nvoid ImGui::EndListBox()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && \"Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?\");\n    IM_UNUSED(window);\n\n    EndChild();\n    EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label\n}\n\nbool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items)\n{\n    const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);\n    return value_changed;\n}\n\n// This is merely a helper around BeginListBox(), EndListBox().\n// Considering using those directly to submit custom data or store selection differently.\nbool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Calculate size from \"height_in_items\"\n    if (height_in_items < 0)\n        height_in_items = ImMin(items_count, 7);\n    float height_in_items_f = height_in_items + 0.25f;\n    ImVec2 size(0.0f, ImTrunc(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f));\n\n    if (!BeginListBox(label, size))\n        return false;\n\n    // Assume all items have even height (= 1 line of text). If you need items of different height,\n    // you can create a custom version of ListBox() in your code without using the clipper.\n    bool value_changed = false;\n    ImGuiListClipper clipper;\n    clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.\n    clipper.IncludeItemByIndex(*current_item);\n    while (clipper.Step())\n        for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)\n        {\n            const char* item_text = getter(user_data, i);\n            if (item_text == NULL)\n                item_text = \"*Unknown item*\";\n\n            PushID(i);\n            const bool item_selected = (i == *current_item);\n            if (Selectable(item_text, item_selected))\n            {\n                *current_item = i;\n                value_changed = true;\n            }\n            if (item_selected)\n                SetItemDefaultFocus();\n            PopID();\n        }\n    EndListBox();\n\n    if (value_changed)\n        MarkItemEdited(g.LastItemData.ID);\n\n    return value_changed;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: PlotLines, PlotHistogram\n//-------------------------------------------------------------------------\n// - PlotEx() [Internal]\n// - PlotLines()\n// - PlotHistogram()\n//-------------------------------------------------------------------------\n// Plot/Graph widgets are not very good.\n// Consider writing your own, or using a third-party one, see:\n// - ImPlot https://github.com/epezent/implot\n// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions\n//-------------------------------------------------------------------------\n\nint ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return -1;\n\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f);\n\n    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);\n    const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);\n    const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_NoNav))\n        return -1;\n    bool hovered;\n    ButtonBehavior(frame_bb, id, &hovered, NULL);\n\n    // Determine scale from values if not specified\n    if (scale_min == FLT_MAX || scale_max == FLT_MAX)\n    {\n        float v_min = FLT_MAX;\n        float v_max = -FLT_MAX;\n        for (int i = 0; i < values_count; i++)\n        {\n            const float v = values_getter(data, i);\n            if (v != v) // Ignore NaN values\n                continue;\n            v_min = ImMin(v_min, v);\n            v_max = ImMax(v_max, v);\n        }\n        if (scale_min == FLT_MAX)\n            scale_min = v_min;\n        if (scale_max == FLT_MAX)\n            scale_max = v_max;\n    }\n\n    RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);\n\n    const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1;\n    int idx_hovered = -1;\n    if (values_count >= values_count_min)\n    {\n        int res_w = ImMin((int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);\n        int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);\n\n        // Tooltip on hover\n        if (hovered && inner_bb.Contains(g.IO.MousePos))\n        {\n            const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);\n            const int v_idx = (int)(t * item_count);\n            IM_ASSERT(v_idx >= 0 && v_idx < values_count);\n\n            const float v0 = values_getter(data, (v_idx + values_offset) % values_count);\n            const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);\n            if (plot_type == ImGuiPlotType_Lines)\n                SetTooltip(\"%d: %8.4g\\n%d: %8.4g\", v_idx, v0, v_idx + 1, v1);\n            else if (plot_type == ImGuiPlotType_Histogram)\n                SetTooltip(\"%d: %8.4g\", v_idx, v0);\n            idx_hovered = v_idx;\n        }\n\n        const float t_step = 1.0f / (float)res_w;\n        const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min));\n\n        float v0 = values_getter(data, (0 + values_offset) % values_count);\n        float t0 = 0.0f;\n        ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) );                       // Point in the normalized space of our target rectangle\n        float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f);   // Where does the zero line stands\n\n        const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);\n        const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);\n\n        for (int n = 0; n < res_w; n++)\n        {\n            const float t1 = t0 + t_step;\n            const int v1_idx = (int)(t0 * item_count + 0.5f);\n            IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);\n            const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);\n            const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) );\n\n            // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU.\n            ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);\n            ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));\n            if (plot_type == ImGuiPlotType_Lines)\n            {\n                window->DrawList->AddLine(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);\n            }\n            else if (plot_type == ImGuiPlotType_Histogram)\n            {\n                if (pos1.x >= pos0.x + 2.0f)\n                    pos1.x -= 1.0f;\n                window->DrawList->AddRectFilled(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);\n            }\n\n            t0 = t1;\n            tp0 = tp1;\n        }\n    }\n\n    // Text overlay\n    if (overlay_text)\n        RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f));\n\n    if (label_size.x > 0.0f)\n        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);\n\n    // Return hovered index or -1 if none are hovered.\n    // This is currently not exposed in the public API because we need a larger redesign of the whole thing, but in the short-term we are making it available in PlotEx().\n    return idx_hovered;\n}\n\nstruct ImGuiPlotArrayGetterData\n{\n    const float* Values;\n    int Stride;\n\n    ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }\n};\n\nstatic float Plot_ArrayGetter(void* data, int idx)\n{\n    ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;\n    const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);\n    return v;\n}\n\nvoid ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)\n{\n    ImGuiPlotArrayGetterData data(values, stride);\n    PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);\n}\n\nvoid ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)\n{\n    PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);\n}\n\nvoid ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)\n{\n    ImGuiPlotArrayGetterData data(values, stride);\n    PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);\n}\n\nvoid ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)\n{\n    PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Value helpers\n// Those is not very useful, legacy API.\n//-------------------------------------------------------------------------\n// - Value()\n//-------------------------------------------------------------------------\n\nvoid ImGui::Value(const char* prefix, bool b)\n{\n    Text(\"%s: %s\", prefix, (b ? \"true\" : \"false\"));\n}\n\nvoid ImGui::Value(const char* prefix, int v)\n{\n    Text(\"%s: %d\", prefix, v);\n}\n\nvoid ImGui::Value(const char* prefix, unsigned int v)\n{\n    Text(\"%s: %d\", prefix, v);\n}\n\nvoid ImGui::Value(const char* prefix, float v, const char* float_format)\n{\n    if (float_format)\n    {\n        char fmt[64];\n        ImFormatString(fmt, IM_COUNTOF(fmt), \"%%s: %s\", float_format);\n        Text(fmt, prefix, v);\n    }\n    else\n    {\n        Text(\"%s: %.3f\", prefix, v);\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] MenuItem, BeginMenu, EndMenu, etc.\n//-------------------------------------------------------------------------\n// - ImGuiMenuColumns [Internal]\n// - BeginMenuBar()\n// - EndMenuBar()\n// - BeginMainMenuBar()\n// - EndMainMenuBar()\n// - BeginMenu()\n// - EndMenu()\n// - MenuItemEx() [Internal]\n// - MenuItem()\n//-------------------------------------------------------------------------\n\n// Helpers for internal use\nvoid ImGuiMenuColumns::Update(float spacing, bool window_reappearing)\n{\n    if (window_reappearing)\n        memset(Widths, 0, sizeof(Widths));\n    Spacing = (ImU16)spacing;\n    CalcNextTotalWidth(true);\n    memset(Widths, 0, sizeof(Widths));\n    TotalWidth = NextTotalWidth;\n    NextTotalWidth = 0;\n}\n\nvoid ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets)\n{\n    ImU16 offset = 0;\n    bool want_spacing = false;\n    for (int i = 0; i < IM_COUNTOF(Widths); i++)\n    {\n        ImU16 width = Widths[i];\n        if (want_spacing && width > 0)\n            offset += Spacing;\n        want_spacing |= (width > 0);\n        if (update_offsets)\n        {\n            if (i == 1) { OffsetLabel = offset; }\n            if (i == 2) { OffsetShortcut = offset; }\n            if (i == 3) { OffsetMark = offset; }\n        }\n        offset += width;\n    }\n    NextTotalWidth = offset;\n}\n\nfloat ImGuiMenuColumns::DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark)\n{\n    Widths[0] = ImMax(Widths[0], (ImU16)w_icon);\n    Widths[1] = ImMax(Widths[1], (ImU16)w_label);\n    Widths[2] = ImMax(Widths[2], (ImU16)w_shortcut);\n    Widths[3] = ImMax(Widths[3], (ImU16)w_mark);\n    CalcNextTotalWidth(false);\n    return (float)ImMax(TotalWidth, NextTotalWidth);\n}\n\n// FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere..\n// Currently the main responsibility of this function being to setup clip-rect + horizontal layout + menu navigation layer.\n// Ideally we also want this to be responsible for claiming space out of the main window scrolling rectangle, in which case ImGuiWindowFlags_MenuBar will become unnecessary.\n// Then later the same system could be used for multiple menu-bars, scrollbars, side-bars.\nbool ImGui::BeginMenuBar()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n    if (!(window->Flags & ImGuiWindowFlags_MenuBar))\n        return false;\n\n    IM_ASSERT(!window->DC.MenuBarAppending);\n    BeginGroup(); // Backup position on layer 0 // FIXME: Misleading to use a group for that backup/restore\n    PushID(\"##MenuBar\");\n\n    // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect.\n    // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy.\n    const float border_top = ImMax(IM_ROUND(window->WindowBorderSize * 0.5f - window->TitleBarHeight), 0.0f);\n    const float border_half = IM_ROUND(window->WindowBorderSize * 0.5f);\n    ImRect bar_rect = window->MenuBarRect();\n    ImRect clip_rect(ImFloor(bar_rect.Min.x + border_half), ImFloor(bar_rect.Min.y + border_top), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, border_half))), ImFloor(bar_rect.Max.y));\n    clip_rect.ClipWith(window->OuterRectClipped);\n    PushClipRect(clip_rect.Min, clip_rect.Max, false);\n\n    // We overwrite CursorMaxPos because BeginGroup sets it to CursorPos (essentially the .EmitItem hack in EndMenuBar() would need something analogous here, maybe a BeginGroupEx() with flags).\n    window->DC.CursorPos = window->DC.CursorMaxPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y);\n    window->DC.LayoutType = ImGuiLayoutType_Horizontal;\n    window->DC.IsSameLine = false;\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;\n    window->DC.MenuBarAppending = true;\n    AlignTextToFramePadding();\n    return true;\n}\n\nvoid ImGui::EndMenuBar()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n    ImGuiContext& g = *GImGui;\n\n    IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive \"warning C6011: Dereferencing NULL pointer 'window'\"\n    IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);\n    IM_ASSERT(window->DC.MenuBarAppending);\n\n    // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.\n    if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))\n    {\n        // Try to find out if the request is for one of our child menu\n        ImGuiWindow* nav_earliest_child = g.NavWindow;\n        while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))\n            nav_earliest_child = nav_earliest_child->ParentWindow;\n        if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)\n        {\n            // To do so we claim focus back, restore NavId and then process the movement request for yet another frame.\n            // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering)\n            const ImGuiNavLayer layer = ImGuiNavLayer_Menu;\n            IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check (FIXME: Seems unnecessary)\n            FocusWindow(window);\n            SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);\n            // FIXME-NAV: How to deal with this when not using g.IO.ConfigNavCursorVisibleAuto?\n            if (g.NavCursorVisible)\n            {\n                g.NavCursorVisible = false; // Hide nav cursor for the current frame so we don't see the intermediary selection. Will be set again\n                g.NavCursorHideFrames = 2;\n            }\n            g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;\n            NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat\n        }\n    }\n    else\n    {\n        NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_WrapX);\n    }\n\n    PopClipRect();\n    PopID();\n    IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive \"warning C6011: Dereferencing NULL pointer 'window'\"\n    window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.\n\n    // FIXME: Extremely confusing, cleanup by (a) working on WorkRect stack system (b) not using a Group confusingly here.\n    ImGuiGroupData& group_data = g.GroupStack.back();\n    group_data.EmitItem = false;\n    ImVec2 restore_cursor_max_pos = group_data.BackupCursorMaxPos;\n    window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, window->DC.CursorMaxPos.x - window->Scroll.x); // Convert ideal extents for scrolling layer equivalent.\n    EndGroup(); // Restore position on layer 0 // FIXME: Misleading to use a group for that backup/restore\n    window->DC.LayoutType = ImGuiLayoutType_Vertical;\n    window->DC.IsSameLine = false;\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n    window->DC.MenuBarAppending = false;\n    window->DC.CursorMaxPos = restore_cursor_max_pos;\n}\n\n// Important: calling order matters!\n// FIXME: Somehow overlapping with docking tech.\n// FIXME: The \"rect-cut\" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts)\nbool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, ImGuiDir dir, float axis_size, ImGuiWindowFlags window_flags)\n{\n    IM_ASSERT(dir != ImGuiDir_None);\n\n    ImGuiWindow* bar_window = FindWindowByName(name);\n    ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport());\n    if (bar_window == NULL || bar_window->BeginCount == 0)\n    {\n        // Calculate and set window size/position\n        ImRect avail_rect = viewport->GetBuildWorkRect();\n        ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;\n        ImVec2 pos = avail_rect.Min;\n        if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)\n            pos[axis] = avail_rect.Max[axis] - axis_size;\n        ImVec2 size = avail_rect.GetSize();\n        size[axis] = axis_size;\n        SetNextWindowPos(pos);\n        SetNextWindowSize(size);\n\n        // Report our size into work area (for next frame) using actual window size\n        if (dir == ImGuiDir_Up || dir == ImGuiDir_Left)\n            viewport->BuildWorkInsetMin[axis] += axis_size;\n        else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right)\n            viewport->BuildWorkInsetMax[axis] += axis_size;\n    }\n\n    window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;\n    \n    // Create window\n    SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set.\n    PushStyleColor(ImGuiCol_WindowShadow, ImVec4(0, 0, 0, 0));\n    PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);\n    PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint\n    bool is_open = Begin(name, NULL, window_flags);\n    PopStyleVar(2);\n    PopStyleColor();\n\n    return is_open;\n}\n\nbool ImGui::BeginMainMenuBar()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();\n\n    // Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change\n    SetCurrentViewport(NULL, viewport);\n\n    // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.\n    // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea?\n    // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings.\n    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));\n    ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;\n    float height = GetFrameHeight();\n    bool is_open = BeginViewportSideBar(\"##MainMenuBar\", viewport, ImGuiDir_Up, height, window_flags);\n    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);\n    if (!is_open)\n    {\n        End();\n        return false;\n    }\n\n    // Temporarily disable _NoSavedSettings, in the off-chance that tables or child windows submitted within the menu-bar may want to use settings. (#8356)\n    g.CurrentWindow->Flags &= ~ImGuiWindowFlags_NoSavedSettings;\n    BeginMenuBar();\n    return is_open;\n}\n\nvoid ImGui::EndMainMenuBar()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT_USER_ERROR_RET(g.CurrentWindow->DC.MenuBarAppending, \"Calling EndMainMenuBar() not from a menu-bar!\"); // Not technically testing that it is the main menu bar\n\n    EndMenuBar();\n    g.CurrentWindow->Flags |= ImGuiWindowFlags_NoSavedSettings; // Restore _NoSavedSettings (#8356)\n\n    // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window\n    // FIXME: With this strategy we won't be able to restore a NULL focus.\n    if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest && g.ActiveId == 0)\n        FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild);\n\n    End();\n}\n\nstatic bool IsRootOfOpenMenuSet()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu))\n        return false;\n\n    // Initially we used 'upper_popup->OpenParentId == window->IDStack.back()' to differentiate multiple menu sets from each others\n    // (e.g. inside menu bar vs loose menu items) based on parent ID.\n    // This would however prevent the use of e.g. PushID() user code submitting menus.\n    // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag,\n    // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects.\n    // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup\n    // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first child menu.\n    // In the end, lack of ID check made it so we could no longer differentiate between separate menu sets. To compensate for that, we at least check parent window nav layer.\n    // This fixes the most common case of menu opening on hover when moving between window content and menu bar. Multiple different menu sets in same nav layer would still\n    // open on hover, but that should be a lesser problem, because if such menus are close in proximity in window content then it won't feel weird and if they are far apart\n    // it likely won't be a problem anyone runs into.\n    const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size];\n    if (window->DC.NavLayerCurrent != upper_popup->ParentNavLayer)\n        return false;\n    return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window, true, false);\n}\n\nbool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None);\n\n    // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)\n    // The first menu in a hierarchy isn't so hovering doesn't get across (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation.\n    ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;\n    if (window->Flags & ImGuiWindowFlags_ChildMenu)\n        window_flags |= ImGuiWindowFlags_ChildWindow;\n\n    // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin().\n    // We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame.\n    // If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used.\n    if (g.MenusIdSubmittedThisFrame.contains(id))\n    {\n        if (menu_is_open)\n            menu_is_open = BeginPopupMenuEx(id, label, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)\n        else\n            g.NextWindowData.ClearFlags();          // we behave like Begin() and need to consume those values\n        return menu_is_open;\n    }\n\n    // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu\n    g.MenusIdSubmittedThisFrame.push_back(id);\n\n    ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window)\n    // This is only done for items for the menu set and not the full parent window.\n    const bool menuset_is_open = IsRootOfOpenMenuSet();\n    if (menuset_is_open)\n        PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);\n\n    // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,\n    // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().\n    // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering.\n    ImVec2 popup_pos;\n    ImVec2 pos = window->DC.CursorPos;\n    PushID(label);\n    if (!enabled)\n        BeginDisabled();\n    const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;\n    bool pressed;\n\n    // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another.\n    const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups;\n    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)\n    {\n        // Menu inside a horizontal menu bar\n        // Selectable extend their highlight by half ItemSpacing in each direction.\n        // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()\n        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);\n        PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f);\n        float w = label_size.x;\n        ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, pos.y + window->DC.CurrLineTextBaseOffset);\n        pressed = Selectable(\"\", menu_is_open, selectable_flags, ImVec2(w, label_size.y));\n        LogSetNextTextDecoration(\"[\", \"]\");\n        RenderText(text_pos, label);\n        PopStyleVar();\n        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().\n        popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), text_pos.y - style.FramePadding.y + window->MenuBarHeight);\n    }\n    else\n    {\n        // Menu inside a regular/vertical menu\n        // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.\n        //  Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.)\n        float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;\n        float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);\n        float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame\n        float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);\n        ImVec2 text_pos(window->DC.CursorPos.x, pos.y + window->DC.CurrLineTextBaseOffset);\n        pressed = Selectable(\"\", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));\n        LogSetNextTextDecoration(\"\", \">\");\n        RenderText(ImVec2(text_pos.x + offsets->OffsetLabel, text_pos.y), label);\n        if (icon_w > 0.0f)\n            RenderText(ImVec2(text_pos.x + offsets->OffsetIcon, text_pos.y), icon);\n        RenderArrow(window->DrawList, ImVec2(text_pos.x + offsets->OffsetMark + extra_w + g.FontSize * 0.30f, text_pos.y), GetColorU32(ImGuiCol_Text), ImGuiDir_Right);\n        popup_pos = ImVec2(pos.x, text_pos.y - style.WindowPadding.y);\n    }\n    if (!enabled)\n        EndDisabled();\n\n    const bool hovered = (g.HoveredId == id) && enabled && !g.NavHighlightItemUnderNav;\n    if (menuset_is_open)\n        PopItemFlag();\n\n    bool want_open = false;\n    bool want_open_nav_init = false;\n    bool want_close = false;\n    if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))\n    {\n        // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu\n        // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.\n        bool moving_toward_child_menu = false;\n        ImGuiPopupData* child_popup = (g.BeginPopupStack.Size < g.OpenPopupStack.Size) ? &g.OpenPopupStack[g.BeginPopupStack.Size] : NULL; // Popup candidate (testing below)\n        ImGuiWindow* child_menu_window = (child_popup && child_popup->Window && child_popup->Window->ParentWindow == window) ? child_popup->Window : NULL;\n        if (g.HoveredWindow == window && child_menu_window != NULL)\n        {\n            const float ref_unit = g.FontSize; // FIXME-DPI\n            const float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f;\n            const ImRect next_window_rect = child_menu_window->Rect();\n            ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta);\n            ImVec2 tb = (child_dir > 0.0f) ? next_window_rect.GetTL() : next_window_rect.GetTR();\n            ImVec2 tc = (child_dir > 0.0f) ? next_window_rect.GetBL() : next_window_rect.GetBR();\n            const float pad_farmost_h = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // Add a bit of extra slack.\n            ta.x += child_dir * -0.5f;\n            tb.x += child_dir * ref_unit;\n            tc.x += child_dir * ref_unit;\n            tb.y = ta.y + ImMax((tb.y - pad_farmost_h) - ta.y, -ref_unit * 8.0f); // Triangle has maximum height to limit the slope and the bias toward large sub-menus\n            tc.y = ta.y + ImMin((tc.y + pad_farmost_h) - ta.y, +ref_unit * 8.0f);\n            moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);\n            //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]\n        }\n\n        // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not)\n        // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon.\n        // (Remember to test this on BeginPopup(\"A\")->BeginMenu(\"B\") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.)\n        if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavHighlightItemUnderNav && g.ActiveId == 0)\n            want_close = true;\n\n        // Open\n        // (note: at this point 'hovered' actually includes the NavDisableMouseHover == false test)\n        if (!menu_is_open && pressed) // Click/activate to open\n            want_open = true;\n        else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open\n            want_open = true;\n        else if (!menu_is_open && hovered && g.HoveredIdTimer >= 0.30f && g.MouseStationaryTimer >= 0.30f) // Hover to open (timer fallback)\n            want_open = true;\n        if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open\n        {\n            want_open = want_open_nav_init = true;\n            NavMoveRequestCancel();\n            SetNavCursorVisibleAfterMove();\n        }\n    }\n    else\n    {\n        // Menu bar\n        if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it\n        {\n            want_close = true;\n            want_open = menu_is_open = false;\n        }\n        else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others\n        {\n            want_open = true;\n        }\n        else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open\n        {\n            want_open = true;\n            NavMoveRequestCancel();\n        }\n    }\n\n    if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu(\"options\", has_object)) { ..use object.. }'\n        want_close = true;\n    if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None))\n        ClosePopupToLevel(g.BeginPopupStack.Size, true);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));\n    PopID();\n\n    if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size)\n    {\n        // Don't reopen/recycle same menu level in the same frame if it is a different menu ID, first close the other menu and yield for a frame.\n        OpenPopup(label);\n    }\n    else if (want_open)\n    {\n        menu_is_open = true;\n        OpenPopup(label, ImGuiPopupFlags_NoReopen);// | (want_open_nav_init ? ImGuiPopupFlags_NoReopenAlwaysNavInit : 0));\n    }\n\n    if (menu_is_open)\n    {\n        ImGuiLastItemData last_item_in_parent = g.LastItemData;\n        SetNextWindowPos(popup_pos, ImGuiCond_Always);                  // Note: misleading: the value will serve as reference for FindBestWindowPosForPopup(), not actual pos.\n        PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding\n        menu_is_open = BeginPopupMenuEx(id, label, window_flags); // menu_is_open may be 'false' when the popup is completely clipped (e.g. zero size display)\n        PopStyleVar();\n        if (menu_is_open)\n        {\n            // Implement what ImGuiPopupFlags_NoReopenAlwaysNavInit would do:\n            // Perform an init request in the case the popup was already open (via a previous mouse hover)\n            if (want_open && want_open_nav_init && !g.NavInitRequest)\n            {\n                FocusWindow(g.CurrentWindow, ImGuiFocusRequestFlags_UnlessBelowModal);\n                NavInitWindow(g.CurrentWindow, false);\n            }\n\n            // Restore LastItemData so IsItemXXXX functions can work after BeginMenu()/EndMenu()\n            // (This fixes using IsItemClicked() and IsItemHovered(), but IsItemHovered() also relies on its support for ImGuiItemFlags_NoWindowHoverableCheck)\n            g.LastItemData = last_item_in_parent;\n            if (g.HoveredWindow == window)\n                g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;\n        }\n    }\n    else\n    {\n        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n    }\n\n    return menu_is_open;\n}\n\nbool ImGui::BeginMenu(const char* label, bool enabled)\n{\n    return BeginMenuEx(label, NULL, enabled);\n}\n\nvoid ImGui::EndMenu()\n{\n    // Nav: When a left move request our menu failed, close ourselves.\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT_USER_ERROR_RET((window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu), \"Calling EndMenu() in wrong window!\");\n\n    ImGuiWindow* parent_window = window->ParentWindow;  // Should always be != NULL is we passed assert.\n    if (window->BeginCount == window->BeginCountPreviousFrame)\n        if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet())\n            if (g.NavWindow && (g.NavWindow->RootWindowForNav == window) && parent_window->DC.LayoutType == ImGuiLayoutType_Vertical)\n            {\n                ClosePopupToLevel(g.BeginPopupStack.Size - 1, true);\n                NavMoveRequestCancel();\n            }\n\n    EndPopup();\n}\n\nbool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut, bool selected, bool enabled)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n    ImVec2 pos = window->DC.CursorPos;\n    ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    // See BeginMenuEx() for comments about this.\n    const bool menuset_is_open = IsRootOfOpenMenuSet();\n    if (menuset_is_open)\n        PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);\n\n    // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),\n    // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.\n    bool pressed;\n    PushID(label);\n    if (!enabled)\n        BeginDisabled();\n\n    // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another.\n    const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover;\n    const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;\n    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)\n    {\n        // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful\n        // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark.\n        float w = label_size.x;\n        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);\n        ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);\n        PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f);\n        pressed = Selectable(\"\", selected, selectable_flags, ImVec2(w, 0.0f));\n        PopStyleVar();\n        if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)\n            RenderText(text_pos, label);\n        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().\n    }\n    else\n    {\n        // Menu item inside a vertical menu\n        // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.\n        //  Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.)\n        float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;\n        float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f;\n        float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);\n        float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame\n        float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);\n        ImVec2 text_pos(pos.x, pos.y + window->DC.CurrLineTextBaseOffset);\n        pressed = Selectable(\"\", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));\n        if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)\n        {\n            RenderText(text_pos + ImVec2(offsets->OffsetLabel, 0.0f), label);\n            if (icon_w > 0.0f)\n                RenderText(text_pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);\n            if (shortcut_w > 0.0f)\n            {\n                PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);\n                LogSetNextTextDecoration(\"(\", \")\");\n                RenderText(text_pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false);\n                PopStyleColor();\n            }\n            if (selected)\n                RenderCheckMark(window->DrawList, text_pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f);\n        }\n    }\n    IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));\n    if (!enabled)\n        EndDisabled();\n    PopID();\n    if (menuset_is_open)\n        PopItemFlag();\n\n    return pressed;\n}\n\nbool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)\n{\n    return MenuItemEx(label, NULL, shortcut, selected, enabled);\n}\n\nbool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)\n{\n    if (MenuItemEx(label, NULL, shortcut, p_selected ? *p_selected : false, enabled))\n    {\n        if (p_selected)\n            *p_selected = !*p_selected;\n        return true;\n    }\n    return false;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: BeginTabBar, EndTabBar, etc.\n//-------------------------------------------------------------------------\n// - BeginTabBar()\n// - BeginTabBarEx() [Internal]\n// - EndTabBar()\n// - TabBarLayout() [Internal]\n// - TabBarCalcTabID() [Internal]\n// - TabBarCalcMaxTabWidth() [Internal]\n// - TabBarFindTabById() [Internal]\n// - TabBarFindTabByOrder() [Internal]\n// - TabBarFindMostRecentlySelectedTabForActiveWindow() [Internal]\n// - TabBarGetCurrentTab() [Internal]\n// - TabBarGetTabName() [Internal]\n// - TabBarAddTab() [Internal]\n// - TabBarRemoveTab() [Internal]\n// - TabBarCloseTab() [Internal]\n// - TabBarScrollClamp() [Internal]\n// - TabBarScrollToTab() [Internal]\n// - TabBarQueueFocus() [Internal]\n// - TabBarQueueReorder() [Internal]\n// - TabBarProcessReorderFromMousePos() [Internal]\n// - TabBarProcessReorder() [Internal]\n// - TabBarScrollingButtons() [Internal]\n// - TabBarTabListPopupButton() [Internal]\n//-------------------------------------------------------------------------\n\nstruct ImGuiTabBarSection\n{\n    int                 TabCount;               // Number of tabs in this section.\n    float               Width;                  // Sum of width of tabs in this section (after shrinking down)\n    float               WidthAfterShrinkMinWidth;\n    float               Spacing;                // Horizontal spacing at the end of the section.\n\n    ImGuiTabBarSection() { memset((void*)this, 0, sizeof(*this)); }\n};\n\nnamespace ImGui\n{\n    static void             TabBarLayout(ImGuiTabBar* tab_bar);\n    static ImU32            TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window);\n    static float            TabBarCalcMaxTabWidth();\n    static float            TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling);\n    static void             TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections);\n    static ImGuiTabItem*    TabBarScrollingButtons(ImGuiTabBar* tab_bar);\n    static ImGuiTabItem*    TabBarTabListPopupButton(ImGuiTabBar* tab_bar);\n}\n\nImGuiTabBar::ImGuiTabBar()\n{\n    memset((void*)this, 0, sizeof(*this));\n    CurrFrameVisible = PrevFrameVisible = -1;\n    LastTabItemIdx = -1;\n}\n\nstatic inline int TabItemGetSectionIdx(const ImGuiTabItem* tab)\n{\n    return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;\n}\n\nstatic int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs)\n{\n    const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;\n    const ImGuiTabItem* b = (const ImGuiTabItem*)rhs;\n    const int a_section = TabItemGetSectionIdx(a);\n    const int b_section = TabItemGetSectionIdx(b);\n    if (a_section != b_section)\n        return a_section - b_section;\n    return (int)(a->IndexDuringLayout - b->IndexDuringLayout);\n}\n\nstatic int IMGUI_CDECL TabItemComparerByBeginOrder(const void* lhs, const void* rhs)\n{\n    const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;\n    const ImGuiTabItem* b = (const ImGuiTabItem*)rhs;\n    return (int)(a->BeginOrder - b->BeginOrder);\n}\n\nstatic ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiPtrOrIndex& ref)\n{\n    ImGuiContext& g = *GImGui;\n    return ref.Ptr ? (ImGuiTabBar*)ref.Ptr : g.TabBars.GetByIndex(ref.Index);\n}\n\nstatic ImGuiPtrOrIndex GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.TabBars.Contains(tab_bar))\n        return ImGuiPtrOrIndex(g.TabBars.GetIndex(tab_bar));\n    return ImGuiPtrOrIndex(tab_bar);\n}\n\nImGuiTabBar* ImGui::TabBarFindByID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    return g.TabBars.GetByKey(id);\n}\n\n// Remove TabBar data (currently only used by TestEngine)\nvoid    ImGui::TabBarRemove(ImGuiTabBar* tab_bar)\n{\n    ImGuiContext& g = *GImGui;\n    g.TabBars.Remove(tab_bar->ID, tab_bar);\n}\n\nbool    ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    ImGuiID id = window->GetID(str_id);\n    ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id);\n    ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2);\n    tab_bar->ID = id;\n    tab_bar->SeparatorMinX = tab_bar_bb.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f);\n    tab_bar->SeparatorMaxX = tab_bar_bb.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f);\n    //if (g.NavWindow && IsWindowChildOf(g.NavWindow, window, false, false))\n    flags |= ImGuiTabBarFlags_IsFocused;\n    return BeginTabBarEx(tab_bar, tab_bar_bb, flags);\n}\n\nbool    ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    IM_ASSERT(tab_bar->ID != 0);\n    if ((flags & ImGuiTabBarFlags_DockNode) == 0) // Already done\n        PushOverrideID(tab_bar->ID);\n\n    // Add to stack\n    g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar));\n    g.CurrentTabBar = tab_bar;\n    tab_bar->Window = window;\n\n    // Append with multiple BeginTabBar()/EndTabBar() pairs.\n    tab_bar->BackupCursorPos = window->DC.CursorPos;\n    if (tab_bar->CurrFrameVisible == g.FrameCount)\n    {\n        window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY);\n        tab_bar->BeginCount++;\n        return true;\n    }\n\n    // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable\n    if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable)))\n        if ((flags & ImGuiTabBarFlags_DockNode) == 0) // FIXME: TabBar with DockNode can now be hybrid\n            ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder);\n    tab_bar->TabsAddedNew = false;\n\n    // Flags\n    if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)\n        flags |= ImGuiTabBarFlags_FittingPolicyDefault_;\n\n    tab_bar->Flags = flags;\n    tab_bar->BarRect = tab_bar_bb;\n    tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab()\n    tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible;\n    tab_bar->CurrFrameVisible = g.FrameCount;\n    tab_bar->PrevTabsContentsHeight = tab_bar->CurrTabsContentsHeight;\n    tab_bar->CurrTabsContentsHeight = 0.0f;\n    tab_bar->ItemSpacingY = g.Style.ItemSpacing.y;\n    tab_bar->FramePadding = g.Style.FramePadding;\n    tab_bar->TabsActiveCount = 0;\n    tab_bar->LastTabItemIdx = -1;\n    tab_bar->BeginCount = 1;\n\n    // Set cursor pos in a way which only be used in the off-chance the user erroneously submits item before BeginTabItem(): items will overlap\n    window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY);\n\n    // Draw separator\n    // (it would be misleading to draw this in EndTabBar() suggesting that it may be drawn over tabs, as tab bar are appendable)\n    const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected);\n    if (g.Style.TabBarBorderSize > 0.0f)\n    {\n        const float y = tab_bar->BarRect.Max.y;\n        window->DrawList->AddRectFilled(ImVec2(tab_bar->SeparatorMinX, y - g.Style.TabBarBorderSize), ImVec2(tab_bar->SeparatorMaxX, y), col);\n    }\n    return true;\n}\n\nvoid    ImGui::EndTabBar()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    ImGuiTabBar* tab_bar = g.CurrentTabBar;\n    IM_ASSERT_USER_ERROR_RET(tab_bar != NULL, \"Mismatched BeginTabBar()/EndTabBar()!\");\n\n    // Fallback in case no TabItem have been submitted\n    if (tab_bar->WantLayout)\n        TabBarLayout(tab_bar);\n\n    // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed().\n    const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);\n    if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing)\n    {\n        tab_bar->CurrTabsContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, tab_bar->CurrTabsContentsHeight);\n        window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->CurrTabsContentsHeight;\n    }\n    else\n    {\n        window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->PrevTabsContentsHeight;\n    }\n    if (tab_bar->BeginCount > 1)\n        window->DC.CursorPos = tab_bar->BackupCursorPos;\n\n    tab_bar->LastTabItemIdx = -1;\n    if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) // Already done\n        PopID();\n\n    g.CurrentTabBarStack.pop_back();\n    g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back());\n}\n\n// Scrolling happens only in the central section (leading/trailing sections are not scrolling)\nstatic float TabBarCalcScrollableWidth(ImGuiTabBar* tab_bar, ImGuiTabBarSection* sections)\n{\n    return tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing;\n}\n\n// This is called only once a frame before by the first call to ItemTab()\n// The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions.\nstatic void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)\n{\n    ImGuiContext& g = *GImGui;\n    tab_bar->WantLayout = false;\n\n    // Track selected tab when resizing our parent down\n    const bool scroll_to_selected_tab = (tab_bar->BarRectPrevWidth > tab_bar->BarRect.GetWidth());\n    tab_bar->BarRectPrevWidth = tab_bar->BarRect.GetWidth();\n\n    // Garbage collect by compacting list\n    // Detect if we need to sort out tab list (e.g. in rare case where a tab changed section)\n    int tab_dst_n = 0;\n    bool need_sort_by_section = false;\n    ImGuiTabBarSection sections[3]; // Layout sections: Leading, Central, Trailing\n    for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++)\n    {\n        ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n];\n        if (tab->LastFrameVisible < tab_bar->PrevFrameVisible || tab->WantClose)\n        {\n            // Remove tab\n            if (tab_bar->VisibleTabId == tab->ID) { tab_bar->VisibleTabId = 0; }\n            if (tab_bar->SelectedTabId == tab->ID) { tab_bar->SelectedTabId = 0; }\n            if (tab_bar->NextSelectedTabId == tab->ID) { tab_bar->NextSelectedTabId = 0; }\n            continue;\n        }\n        if (tab_dst_n != tab_src_n)\n            tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n];\n\n        tab = &tab_bar->Tabs[tab_dst_n];\n        tab->IndexDuringLayout = (ImS16)tab_dst_n;\n\n        // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another)\n        int curr_tab_section_n = TabItemGetSectionIdx(tab);\n        if (tab_dst_n > 0)\n        {\n            ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1];\n            int prev_tab_section_n = TabItemGetSectionIdx(prev_tab);\n            if (curr_tab_section_n == 0 && prev_tab_section_n != 0)\n                need_sort_by_section = true;\n            if (prev_tab_section_n == 2 && curr_tab_section_n != 2)\n                need_sort_by_section = true;\n        }\n\n        sections[curr_tab_section_n].TabCount++;\n        tab_dst_n++;\n    }\n    if (tab_bar->Tabs.Size != tab_dst_n)\n        tab_bar->Tabs.resize(tab_dst_n);\n\n    if (need_sort_by_section)\n        ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerBySection);\n\n    // Calculate spacing between sections\n    const float tab_spacing = g.Style.ItemInnerSpacing.x;\n    sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? tab_spacing : 0.0f;\n    sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? tab_spacing : 0.0f;\n\n    // Setup next selected tab\n    ImGuiID scroll_to_tab_id = 0;\n    if (tab_bar->NextScrollToTabId)\n    {\n        scroll_to_tab_id = tab_bar->NextScrollToTabId;\n        tab_bar->NextScrollToTabId = 0;\n    }\n    if (tab_bar->NextSelectedTabId)\n    {\n        tab_bar->SelectedTabId = tab_bar->NextSelectedTabId;\n        tab_bar->NextSelectedTabId = 0;\n        scroll_to_tab_id = tab_bar->SelectedTabId;\n    }\n\n    // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot).\n    if (tab_bar->ReorderRequestTabId != 0)\n    {\n        if (TabBarProcessReorder(tab_bar))\n            if (tab_bar->ReorderRequestTabId == tab_bar->SelectedTabId)\n                scroll_to_tab_id = tab_bar->ReorderRequestTabId;\n        tab_bar->ReorderRequestTabId = 0;\n    }\n\n    // Tab List Popup (will alter tab_bar->BarRect and therefore the available width!)\n    const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0;\n    if (tab_list_popup_button)\n        if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Min.x!\n            scroll_to_tab_id = tab_bar->SelectedTabId = tab_to_select->ID;\n\n    // Leading/Trailing tabs will be shrink only if central one aren't visible anymore, so layout the shrink data as: leading, trailing, central\n    // (whereas our tabs are stored as: leading, central, trailing)\n    int shrink_buffer_indexes[3] = { 0, sections[0].TabCount + sections[2].TabCount, sections[0].TabCount };\n    g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size);\n\n    // Minimum shrink width\n    const float shrink_min_width = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed) ? g.Style.TabMinWidthShrink : 1.0f;\n\n    // Compute ideal tabs widths + store them into shrink buffer\n    ImGuiTabItem* most_recently_selected_tab = NULL;\n    int curr_section_n = -1;\n    bool found_selected_tab_id = false;\n    for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)\n    {\n        ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];\n        IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible);\n\n        if ((most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) && !(tab->Flags & ImGuiTabItemFlags_Button))\n            most_recently_selected_tab = tab;\n        if (tab->ID == tab_bar->SelectedTabId)\n            found_selected_tab_id = true;\n        if (scroll_to_tab_id == 0 && g.NavJustMovedToId == tab->ID)\n            scroll_to_tab_id = tab->ID;\n\n        // Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar.\n        // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet,\n        // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window.\n        const char* tab_name = TabBarGetTabName(tab_bar, tab);\n        const bool has_close_button_or_unsaved_marker = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0 || (tab->Flags & ImGuiTabItemFlags_UnsavedDocument);\n        tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x;\n        if ((tab->Flags & ImGuiTabItemFlags_Button) == 0)\n            tab->ContentWidth = ImMax(tab->ContentWidth, g.Style.TabMinWidthBase);\n\n        int section_n = TabItemGetSectionIdx(tab);\n        ImGuiTabBarSection* section = &sections[section_n];\n        section->Width += tab->ContentWidth + (section_n == curr_section_n ? tab_spacing : 0.0f);\n        section->WidthAfterShrinkMinWidth += ImMin(tab->ContentWidth, shrink_min_width) + (section_n == curr_section_n ? tab_spacing : 0.0f);\n        curr_section_n = section_n;\n\n        // Store data so we can build an array sorted by width if we need to shrink tabs down\n        IM_MSVC_WARNING_SUPPRESS(6385);\n        ImGuiShrinkWidthItem* shrink_width_item = &g.ShrinkWidthBuffer[shrink_buffer_indexes[section_n]++];\n        shrink_width_item->Index = tab_n;\n        shrink_width_item->Width = shrink_width_item->InitialWidth = tab->ContentWidth;\n        tab->Width = ImMax(tab->ContentWidth, 1.0f);\n    }\n\n    // Compute total ideal width (used for e.g. auto-resizing a window)\n    float width_all_tabs_after_min_width_shrink = 0.0f;\n    tab_bar->WidthAllTabsIdeal = 0.0f;\n    for (int section_n = 0; section_n < 3; section_n++)\n    {\n        tab_bar->WidthAllTabsIdeal += sections[section_n].Width + sections[section_n].Spacing;\n        width_all_tabs_after_min_width_shrink += sections[section_n].WidthAfterShrinkMinWidth + sections[section_n].Spacing;\n    }\n\n    // Horizontal scrolling buttons\n    // Important: note that TabBarScrollButtons() will alter BarRect.Max.x.\n    const bool can_scroll = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) || (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed);\n    const float width_all_tabs_to_use_for_scroll = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) ? tab_bar->WidthAllTabs : width_all_tabs_after_min_width_shrink;\n    tab_bar->ScrollButtonEnabled = ((width_all_tabs_to_use_for_scroll > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && can_scroll);\n    if (tab_bar->ScrollButtonEnabled)\n        if (ImGuiTabItem* scroll_and_select_tab = TabBarScrollingButtons(tab_bar))\n        {\n            scroll_to_tab_id = scroll_and_select_tab->ID;\n            if ((scroll_and_select_tab->Flags & ImGuiTabItemFlags_Button) == 0)\n                tab_bar->SelectedTabId = scroll_to_tab_id;\n        }\n    if (scroll_to_tab_id == 0 && scroll_to_selected_tab)\n        scroll_to_tab_id = tab_bar->SelectedTabId;\n\n    // Shrink widths if full tabs don't fit in their allocated space\n    float section_0_w = sections[0].Width + sections[0].Spacing;\n    float section_1_w = sections[1].Width + sections[1].Spacing;\n    float section_2_w = sections[2].Width + sections[2].Spacing;\n    bool central_section_is_visible = (section_0_w + section_2_w) < tab_bar->BarRect.GetWidth();\n    float width_excess;\n    if (central_section_is_visible)\n        width_excess = ImMax(section_1_w - (tab_bar->BarRect.GetWidth() - section_0_w - section_2_w), 0.0f); // Excess used to shrink central section\n    else\n        width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth(); // Excess used to shrink leading/trailing section\n\n    // With ImGuiTabBarFlags_FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore\n    const bool can_shrink = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyShrink) || (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed);\n    if (width_excess >= 1.0f && (can_shrink || !central_section_is_visible))\n    {\n        int shrink_data_count = (central_section_is_visible ? sections[1].TabCount : sections[0].TabCount + sections[2].TabCount);\n        int shrink_data_offset = (central_section_is_visible ? sections[0].TabCount + sections[2].TabCount : 0);\n        ShrinkWidths(g.ShrinkWidthBuffer.Data + shrink_data_offset, shrink_data_count, width_excess, shrink_min_width);\n\n        // Apply shrunk values into tabs and sections\n        for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++)\n        {\n            ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index];\n            float shrinked_width = IM_TRUNC(g.ShrinkWidthBuffer[tab_n].Width);\n            if (shrinked_width < 0.0f)\n                continue;\n\n            shrinked_width = ImMax(1.0f, shrinked_width);\n            int section_n = TabItemGetSectionIdx(tab);\n            sections[section_n].Width -= (tab->Width - shrinked_width);\n            tab->Width = shrinked_width;\n        }\n    }\n\n    // Layout all active tabs\n    int section_tab_index = 0;\n    float tab_offset = 0.0f;\n    tab_bar->WidthAllTabs = 0.0f;\n    for (int section_n = 0; section_n < 3; section_n++)\n    {\n        ImGuiTabBarSection* section = &sections[section_n];\n        if (section_n == 2)\n            tab_offset = ImMin(ImMax(0.0f, tab_bar->BarRect.GetWidth() - section->Width), tab_offset);\n\n        for (int tab_n = 0; tab_n < section->TabCount; tab_n++)\n        {\n            ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n];\n            tab->Offset = tab_offset;\n            tab->NameOffset = -1;\n            tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f);\n        }\n        tab_bar->WidthAllTabs += ImMax(section->Width + section->Spacing, 0.0f);\n        tab_offset += section->Spacing;\n        section_tab_index += section->TabCount;\n    }\n\n    // Clear name buffers\n    tab_bar->TabsNames.Buf.resize(0);\n\n    // If we have lost the selected tab, select the next most recently active one\n    const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);\n    if (found_selected_tab_id == false && !tab_bar_appearing)\n        tab_bar->SelectedTabId = 0;\n    if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL)\n        scroll_to_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID;\n\n    // Lock in visible tab\n    tab_bar->VisibleTabId = tab_bar->SelectedTabId;\n    tab_bar->VisibleTabWasSubmitted = false;\n\n    // CTRL+TAB can override visible tab temporarily\n    if (g.NavWindowingTarget != NULL && g.NavWindowingTarget->DockNode && g.NavWindowingTarget->DockNode->TabBar == tab_bar)\n        tab_bar->VisibleTabId = scroll_to_tab_id = g.NavWindowingTarget->TabId;\n\n    // Apply request requests\n    if (scroll_to_tab_id != 0)\n        TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections);\n    else if (tab_bar->ScrollButtonEnabled && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, true) && IsWindowContentHoverable(g.CurrentWindow))\n    {\n        const float wheel = g.IO.MouseWheelRequestAxisSwap ? g.IO.MouseWheel : g.IO.MouseWheelH;\n        const ImGuiKey wheel_key = g.IO.MouseWheelRequestAxisSwap ? ImGuiKey_MouseWheelY : ImGuiKey_MouseWheelX;\n        if (TestKeyOwner(wheel_key, tab_bar->ID) && wheel != 0.0f)\n        {\n            const float scroll_step = wheel * TabBarCalcScrollableWidth(tab_bar, sections) / 3.0f;\n            tab_bar->ScrollingTargetDistToVisibility = 0.0f;\n            tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget - scroll_step);\n        }\n        SetKeyOwner(wheel_key, tab_bar->ID);\n    }\n\n    // Update scrolling\n    tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim);\n    tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget);\n    if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget)\n    {\n        // Scrolling speed adjust itself so we can always reach our target in 1/3 seconds.\n        // Teleport if we are aiming far off the visible line\n        tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, 70.0f * g.FontSize);\n        tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, ImFabs(tab_bar->ScrollingTarget - tab_bar->ScrollingAnim) / 0.3f);\n        const bool teleport = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) || (tab_bar->ScrollingTargetDistToVisibility > 10.0f * g.FontSize);\n        tab_bar->ScrollingAnim = teleport ? tab_bar->ScrollingTarget : ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, g.IO.DeltaTime * tab_bar->ScrollingSpeed);\n    }\n    else\n    {\n        tab_bar->ScrollingSpeed = 0.0f;\n    }\n    tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].Width + sections[0].Spacing;\n    tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].Width - sections[1].Spacing;\n\n    // Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame)\n    ImGuiWindow* window = g.CurrentWindow;\n    window->DC.CursorPos = tab_bar->BarRect.Min;\n    ItemSize(ImVec2(tab_bar->WidthAllTabs, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y);\n    window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, tab_bar->BarRect.Min.x + tab_bar->WidthAllTabsIdeal);\n}\n\n// Dockable windows uses Name/ID in the global namespace. Non-dockable items use the ID stack.\nstatic ImU32   ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window)\n{\n    if (docked_window != NULL)\n    {\n        IM_UNUSED(tab_bar);\n        IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode);\n        ImGuiID id = docked_window->TabId;\n        KeepAliveID(id);\n        return id;\n    }\n    else\n    {\n        ImGuiWindow* window = GImGui->CurrentWindow;\n        return window->GetID(label);\n    }\n}\n\nstatic float ImGui::TabBarCalcMaxTabWidth()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize * 20.0f;\n}\n\nImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id)\n{\n    if (tab_id != 0)\n        for (int n = 0; n < tab_bar->Tabs.Size; n++)\n            if (tab_bar->Tabs[n].ID == tab_id)\n                return &tab_bar->Tabs[n];\n    return NULL;\n}\n\n// Order = visible order, not submission order! (which is tab->BeginOrder)\nImGuiTabItem* ImGui::TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order)\n{\n    if (order < 0 || order >= tab_bar->Tabs.Size)\n        return NULL;\n    return &tab_bar->Tabs[order];\n}\n\n// FIXME: See references to #2304 in TODO.txt\nImGuiTabItem* ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar)\n{\n    ImGuiTabItem* most_recently_selected_tab = NULL;\n    for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)\n    {\n        ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];\n        if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected)\n            if (tab->Window && tab->Window->WasActive)\n                most_recently_selected_tab = tab;\n    }\n    return most_recently_selected_tab;\n}\n\nImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar)\n{\n    if (tab_bar->LastTabItemIdx < 0 || tab_bar->LastTabItemIdx >= tab_bar->Tabs.Size)\n        return NULL;\n    return &tab_bar->Tabs[tab_bar->LastTabItemIdx];\n}\n\nconst char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)\n{\n    if (tab->Window)\n        return tab->Window->Name;\n    if (tab->NameOffset == -1)\n        return \"N/A\";\n    IM_ASSERT(tab->NameOffset < tab_bar->TabsNames.Buf.Size);\n    return tab_bar->TabsNames.Buf.Data + tab->NameOffset;\n}\n\n// The purpose of this call is to register tab in advance so we can control their order at the time they appear.\n// Otherwise calling this is unnecessary as tabs are appending as needed by the BeginTabItem() function.\nvoid ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(TabBarFindTabByID(tab_bar, window->TabId) == NULL);\n    IM_ASSERT(g.CurrentTabBar != tab_bar);  // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame)\n\n    if (!window->HasCloseButton)\n        tab_flags |= ImGuiTabItemFlags_NoCloseButton;       // Set _NoCloseButton immediately because it will be used for first-frame width calculation.\n\n    ImGuiTabItem new_tab;\n    new_tab.ID = window->TabId;\n    new_tab.Flags = tab_flags;\n    new_tab.LastFrameVisible = tab_bar->CurrFrameVisible;   // Required so BeginTabBar() doesn't ditch the tab\n    if (new_tab.LastFrameVisible == -1)\n        new_tab.LastFrameVisible = g.FrameCount - 1;\n    new_tab.Window = window;                                // Required so tab bar layout can compute the tab width before tab submission\n    tab_bar->Tabs.push_back(new_tab);\n}\n\n// The *TabId fields are already set by the docking system _before_ the actual TabItem was created, so we clear them regardless.\nvoid ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id)\n{\n    if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))\n        tab_bar->Tabs.erase(tab);\n    if (tab_bar->VisibleTabId == tab_id)      { tab_bar->VisibleTabId = 0; }\n    if (tab_bar->SelectedTabId == tab_id)     { tab_bar->SelectedTabId = 0; }\n    if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; }\n}\n\n// Called on manual closure attempt\nvoid ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)\n{\n    if (tab->Flags & ImGuiTabItemFlags_Button)\n        return; // A button appended with TabItemButton().\n\n    if ((tab->Flags & (ImGuiTabItemFlags_UnsavedDocument | ImGuiTabItemFlags_NoAssumedClosure)) == 0)\n    {\n        // This will remove a frame of lag for selecting another tab on closure.\n        // However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure\n        tab->WantClose = true;\n        if (tab_bar->VisibleTabId == tab->ID)\n        {\n            tab->LastFrameVisible = -1;\n            tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0;\n        }\n    }\n    else\n    {\n        // Actually select before expecting closure attempt (on an UnsavedDocument tab user is expect to e.g. show a popup)\n        if (tab_bar->VisibleTabId != tab->ID)\n            TabBarQueueFocus(tab_bar, tab);\n    }\n}\n\nstatic float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling)\n{\n    scrolling = ImMin(scrolling, tab_bar->WidthAllTabs - tab_bar->BarRect.GetWidth());\n    return ImMax(scrolling, 0.0f);\n}\n\n// Note: we may scroll to tab that are not selected! e.g. using keyboard arrow keys\nstatic void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections)\n{\n    ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id);\n    if (tab == NULL)\n        return;\n    if (tab->Flags & ImGuiTabItemFlags_SectionMask_)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar)\n    int order = TabBarGetTabOrder(tab_bar, tab);\n\n    // Scrolling happens only in the central section (leading/trailing sections are not scrolling)\n    float scrollable_width = TabBarCalcScrollableWidth(tab_bar, sections);\n\n    // We make all tabs positions all relative Sections[0].Width to make code simpler\n    float tab_x1 = tab->Offset - sections[0].Width + (order > sections[0].TabCount - 1 ? -margin : 0.0f);\n    float tab_x2 = tab->Offset - sections[0].Width + tab->Width + (order + 1 < tab_bar->Tabs.Size - sections[2].TabCount ? margin : 1.0f);\n    tab_bar->ScrollingTargetDistToVisibility = 0.0f;\n    if (tab_bar->ScrollingTarget > tab_x1 || (tab_x2 - tab_x1 >= scrollable_width))\n    {\n        // Scroll to the left\n        tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f);\n        tab_bar->ScrollingTarget = tab_x1;\n    }\n    else if (tab_bar->ScrollingTarget < tab_x2 - scrollable_width)\n    {\n        // Scroll to the right\n        tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - scrollable_width) - tab_bar->ScrollingAnim, 0.0f);\n        tab_bar->ScrollingTarget = tab_x2 - scrollable_width;\n    }\n}\n\nvoid ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)\n{\n    tab_bar->NextSelectedTabId = tab->ID;\n}\n\nvoid ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name)\n{\n    IM_ASSERT((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0); // Only supported for manual/explicit tab bars\n    ImGuiID tab_id = TabBarCalcTabID(tab_bar, tab_name, NULL);\n    tab_bar->NextSelectedTabId = tab_id;\n}\n\nvoid ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset)\n{\n    IM_ASSERT(offset != 0);\n    IM_ASSERT(tab_bar->ReorderRequestTabId == 0);\n    tab_bar->ReorderRequestTabId = tab->ID;\n    tab_bar->ReorderRequestOffset = (ImS16)offset;\n}\n\nvoid ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* src_tab, ImVec2 mouse_pos)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(tab_bar->ReorderRequestTabId == 0);\n    if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0)\n        return;\n\n    const float tab_spacing = g.Style.ItemInnerSpacing.x;\n    const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;\n    const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0);\n\n    // Count number of contiguous tabs we are crossing over\n    const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1;\n    const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab);\n    int dst_idx = src_idx;\n    for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir)\n    {\n        // Reordered tabs must share the same section\n        const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i];\n        if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder)\n            break;\n        if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_))\n            break;\n        dst_idx = i;\n\n        // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered.\n        const float x1 = bar_offset + dst_tab->Offset - tab_spacing;\n        const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + tab_spacing;\n        //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255));\n        if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2))\n            break;\n    }\n\n    if (dst_idx != src_idx)\n        TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx);\n}\n\nbool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar)\n{\n    ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId);\n    if (tab1 == NULL || (tab1->Flags & ImGuiTabItemFlags_NoReorder))\n        return false;\n\n    //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools\n    int tab2_order = TabBarGetTabOrder(tab_bar, tab1) + tab_bar->ReorderRequestOffset;\n    if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size)\n        return false;\n\n    // Reordered tabs must share the same section\n    // (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too)\n    ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order];\n    if (tab2->Flags & ImGuiTabItemFlags_NoReorder)\n        return false;\n    if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_))\n        return false;\n\n    ImGuiTabItem item_tmp = *tab1;\n    ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2;\n    ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1;\n    const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset;\n    memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem));\n    *tab2 = item_tmp;\n\n    if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings)\n        MarkIniSettingsDirty();\n    return true;\n}\n\nstatic ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f);\n    const float scrolling_buttons_width = arrow_button_size.x * 2.0f;\n\n    const ImVec2 backup_cursor_pos = window->DC.CursorPos;\n    //window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255));\n\n    int select_dir = 0;\n    ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];\n    arrow_col.w *= 0.5f;\n\n    PushStyleColor(ImGuiCol_Text, arrow_col);\n    PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));\n    PushItemFlag(ImGuiItemFlags_ButtonRepeat | ImGuiItemFlags_NoNav, true);\n    const float backup_repeat_delay = g.IO.KeyRepeatDelay;\n    const float backup_repeat_rate = g.IO.KeyRepeatRate;\n    g.IO.KeyRepeatDelay = 0.250f;\n    g.IO.KeyRepeatRate = 0.200f;\n    float x = ImMax(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.x - scrolling_buttons_width);\n    window->DC.CursorPos = ImVec2(x, tab_bar->BarRect.Min.y);\n    if (ArrowButtonEx(\"##<\", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick))\n        select_dir = -1;\n    window->DC.CursorPos = ImVec2(x + arrow_button_size.x, tab_bar->BarRect.Min.y);\n    if (ArrowButtonEx(\"##>\", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick))\n        select_dir = +1;\n    PopItemFlag();\n    PopStyleColor(2);\n    g.IO.KeyRepeatRate = backup_repeat_rate;\n    g.IO.KeyRepeatDelay = backup_repeat_delay;\n\n    ImGuiTabItem* tab_to_scroll_to = NULL;\n    if (select_dir != 0)\n        if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))\n        {\n            int selected_order = TabBarGetTabOrder(tab_bar, tab_item);\n            int target_order = selected_order + select_dir;\n\n            // Skip tab item buttons until another tab item is found or end is reached\n            while (tab_to_scroll_to == NULL)\n            {\n                // If we are at the end of the list, still scroll to make our tab visible\n                tab_to_scroll_to = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order];\n\n                // Cross through buttons\n                // (even if first/last item is a button, return it so we can update the scroll)\n                if (tab_to_scroll_to->Flags & ImGuiTabItemFlags_Button)\n                {\n                    target_order += select_dir;\n                    selected_order += select_dir;\n                    tab_to_scroll_to = (target_order < 0 || target_order >= tab_bar->Tabs.Size) ? tab_to_scroll_to : NULL;\n                }\n            }\n        }\n    window->DC.CursorPos = backup_cursor_pos;\n    tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f;\n\n    return tab_to_scroll_to;\n}\n\nstatic ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // We use g.Style.FramePadding.y to match the square ArrowButton size\n    const float tab_list_popup_button_width = g.FontSize + g.Style.FramePadding.y;\n    const ImVec2 backup_cursor_pos = window->DC.CursorPos;\n    window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x - g.Style.FramePadding.y, tab_bar->BarRect.Min.y);\n    tab_bar->BarRect.Min.x += tab_list_popup_button_width;\n\n    ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];\n    arrow_col.w *= 0.5f;\n    PushStyleColor(ImGuiCol_Text, arrow_col);\n    PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));\n    bool open = BeginCombo(\"##v\", NULL, ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLargest);\n    PopStyleColor(2);\n\n    ImGuiTabItem* tab_to_select = NULL;\n    if (open)\n    {\n        for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)\n        {\n            ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];\n            if (tab->Flags & ImGuiTabItemFlags_Button)\n                continue;\n\n            const char* tab_name = TabBarGetTabName(tab_bar, tab);\n            if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID))\n                tab_to_select = tab;\n        }\n        EndCombo();\n    }\n\n    window->DC.CursorPos = backup_cursor_pos;\n    return tab_to_select;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: BeginTabItem, EndTabItem, etc.\n//-------------------------------------------------------------------------\n// - BeginTabItem()\n// - EndTabItem()\n// - TabItemButton()\n// - TabItemEx() [Internal]\n// - SetTabItemClosed()\n// - TabItemCalcSize() [Internal]\n// - TabItemBackground() [Internal]\n// - TabItemLabelAndCloseButton() [Internal]\n//-------------------------------------------------------------------------\n\nbool    ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    ImGuiTabBar* tab_bar = g.CurrentTabBar;\n    IM_ASSERT_USER_ERROR_RETV(tab_bar != NULL, false, \"Needs to be called between BeginTabBar() and EndTabBar()!\");\n    IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead!\n\n    bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL);\n    if (ret && !(flags & ImGuiTabItemFlags_NoPushId))\n    {\n        ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];\n        PushOverrideID(tab->ID); // We already hashed 'label' so push into the ID stack directly instead of doing another hash through PushID(label)\n    }\n    return ret;\n}\n\nvoid    ImGui::EndTabItem()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    ImGuiTabBar* tab_bar = g.CurrentTabBar;\n    IM_ASSERT_USER_ERROR_RET(tab_bar != NULL, \"Needs to be called between BeginTabBar() and EndTabBar()!\");\n    IM_ASSERT(tab_bar->LastTabItemIdx >= 0);\n    ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];\n    if (!(tab->Flags & ImGuiTabItemFlags_NoPushId))\n        PopID();\n}\n\nbool    ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    ImGuiTabBar* tab_bar = g.CurrentTabBar;\n    IM_ASSERT_USER_ERROR_RETV(tab_bar != NULL, false, \"Needs to be called between BeginTabBar() and EndTabBar()!\");\n    return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL);\n}\n\nvoid    ImGui::TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    ImGuiTabBar* tab_bar = g.CurrentTabBar;\n    IM_ASSERT_USER_ERROR_RET(tab_bar != NULL, \"Needs to be called between BeginTabBar() and EndTabBar()!\");\n    SetNextItemWidth(width);\n    TabItemEx(tab_bar, str_id, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder | ImGuiTabItemFlags_Invisible, NULL);\n}\n\nbool    ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window)\n{\n    // Layout whole tab bar if not already done\n    ImGuiContext& g = *GImGui;\n    if (tab_bar->WantLayout)\n    {\n        ImGuiNextItemData backup_next_item_data = g.NextItemData;\n        TabBarLayout(tab_bar);\n        g.NextItemData = backup_next_item_data;\n    }\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = TabBarCalcTabID(tab_bar, label, docked_window);\n\n    // If the user called us with *p_open == false, we early out and don't render.\n    // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID.\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n    if (p_open && !*p_open)\n    {\n        ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav);\n        return false;\n    }\n\n    IM_ASSERT(!p_open || !(flags & ImGuiTabItemFlags_Button));\n    IM_ASSERT((flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)); // Can't use both Leading and Trailing\n\n    // Store into ImGuiTabItemFlags_NoCloseButton, also honor ImGuiTabItemFlags_NoCloseButton passed by user (although not documented)\n    if (flags & ImGuiTabItemFlags_NoCloseButton)\n        p_open = NULL;\n    else if (p_open == NULL)\n        flags |= ImGuiTabItemFlags_NoCloseButton;\n\n    // Acquire tab data\n    ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id);\n    bool tab_is_new = false;\n    if (tab == NULL)\n    {\n        tab_bar->Tabs.push_back(ImGuiTabItem());\n        tab = &tab_bar->Tabs.back();\n        tab->ID = id;\n        tab_bar->TabsAddedNew = tab_is_new = true;\n    }\n    tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab);\n\n    // Calculate tab contents size\n    ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument));\n    tab->RequestedWidth = -1.0f;\n    if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)\n        size.x = tab->RequestedWidth = g.NextItemData.Width;\n    if (tab_is_new)\n        tab->Width = ImMax(1.0f, size.x);\n    tab->ContentWidth = size.x;\n    tab->BeginOrder = tab_bar->TabsActiveCount++;\n\n    const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);\n    const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0;\n    const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount);\n    const bool tab_just_unsaved = (flags & ImGuiTabItemFlags_UnsavedDocument) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument);\n    const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0;\n    tab->LastFrameVisible = g.FrameCount;\n    tab->Flags = flags;\n    tab->Window = docked_window;\n\n    // Append name _WITH_ the zero-terminator\n    // (regular tabs are permitted in a DockNode tab bar, but window tabs not permitted in a non-DockNode tab bar)\n    if (docked_window != NULL)\n    {\n        IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode);\n        tab->NameOffset = -1;\n    }\n    else\n    {\n        tab->NameOffset = (ImS32)tab_bar->TabsNames.size();\n        tab_bar->TabsNames.append(label, label + ImStrlen(label) + 1);\n    }\n\n    // Update selected tab\n    if (!is_tab_button)\n    {\n        if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0)\n            if (!tab_bar_appearing || tab_bar->SelectedTabId == 0)\n                TabBarQueueFocus(tab_bar, tab); // New tabs gets activated\n        if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // _SetSelected can only be passed on explicit tab bar\n            TabBarQueueFocus(tab_bar, tab);\n    }\n\n    // Lock visibility\n    // (Note: tab_contents_visible != tab_selected... because Ctrl+Tab operations may preview some tabs without selecting them!)\n    bool tab_contents_visible = (tab_bar->VisibleTabId == id);\n    if (tab_contents_visible)\n        tab_bar->VisibleTabWasSubmitted = true;\n\n    // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches\n    if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing && docked_window == NULL)\n        if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs))\n            tab_contents_visible = true;\n\n    // Note that tab_is_new is not necessarily the same as tab_appearing! When a tab bar stops being submitted\n    // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'.\n    if (tab_appearing && (!tab_bar_appearing || tab_is_new))\n    {\n        ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav);\n        if (is_tab_button)\n            return false;\n        return tab_contents_visible;\n    }\n\n    if (tab_bar->SelectedTabId == id)\n        tab->LastFrameSelected = g.FrameCount;\n\n    // Backup current layout position\n    const ImVec2 backup_main_cursor_pos = window->DC.CursorPos;\n\n    // Layout\n    const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;\n    size.x = tab->Width;\n    if (is_central_section)\n        window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f);\n    else\n        window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f);\n    ImVec2 pos = window->DC.CursorPos;\n    ImRect bb(pos, pos + size);\n\n    // We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation)\n    const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX);\n    if (want_clip_rect)\n        PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true);\n\n    ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos;\n    ItemSize(bb.GetSize(), style.FramePadding.y);\n    window->DC.CursorMaxPos = backup_cursor_max_pos;\n\n    if (!ItemAdd(bb, id))\n    {\n        if (want_clip_rect)\n            PopClipRect();\n        window->DC.CursorPos = backup_main_cursor_pos;\n        return tab_contents_visible;\n    }\n\n    // Click to Select a tab\n    ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap);\n    if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW)) // FIXME: May be an opt-in property of the payload to disable this\n        button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;\n    bool hovered, held, pressed;\n    if (flags & ImGuiTabItemFlags_Invisible)\n        hovered = held = pressed = false;\n    else\n        pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);\n    if (pressed && !is_tab_button)\n        TabBarQueueFocus(tab_bar, tab);\n\n    // Transfer active id window so the active id is not owned by the dock host (as StartMouseMovingWindow()\n    // will only do it on the drag). This allows FocusWindow() to be more conservative in how it clears active id.\n    if (held && docked_window && g.ActiveId == id && g.ActiveIdIsJustActivated)\n        g.ActiveIdWindow = docked_window;\n\n    // Drag and drop a single floating window node moves it\n    ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL;\n    const bool single_floating_window_node = node && node->IsFloatingNode() && (node->Windows.Size == 1);\n    if (held && single_floating_window_node && IsMouseDragging(0, 0.0f))\n    {\n        // Move\n        StartMouseMovingWindow(docked_window);\n    }\n    else if (held && !tab_appearing && IsMouseDragging(0))\n    {\n        // Drag and drop: re-order tabs\n        int drag_dir = 0;\n        float drag_distance_from_edge_x = 0.0f;\n        if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (docked_window != NULL)))\n        {\n            // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x\n            if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x)\n            {\n                drag_dir = -1;\n                drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x;\n                TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos);\n            }\n            else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x)\n            {\n                drag_dir = +1;\n                drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x;\n                TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos);\n            }\n        }\n\n        // Extract a Dockable window out of it's tab bar\n        const bool can_undock = docked_window != NULL && !(docked_window->Flags & ImGuiWindowFlags_NoMove) && !(node->MergedFlags & ImGuiDockNodeFlags_NoUndocking);\n        if (can_undock)\n        {\n            // We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar\n            bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id);\n            if (!undocking_tab) //&& (!g.IO.ConfigDockingWithShift || g.IO.KeyShift)\n            {\n                float threshold_base = g.FontSize;\n                float threshold_x = (threshold_base * 2.2f);\n                float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f);\n                //GetForegroundDrawList()->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG]\n\n                float distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y);\n                if (distance_from_edge_y >= threshold_y)\n                    undocking_tab = true;\n                if (drag_distance_from_edge_x > threshold_x)\n                    if ((drag_dir < 0 && TabBarGetTabOrder(tab_bar, tab) == 0) || (drag_dir > 0 && TabBarGetTabOrder(tab_bar, tab) == tab_bar->Tabs.Size - 1))\n                        undocking_tab = true;\n            }\n\n            if (undocking_tab)\n            {\n                // Undock\n                // FIXME: refactor to share more code with e.g. StartMouseMovingWindow\n                DockContextQueueUndockWindow(&g, docked_window);\n                g.MovingWindow = docked_window;\n                SetActiveID(g.MovingWindow->MoveId, g.MovingWindow);\n                g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min;\n                g.ActiveIdNoClearOnFocusLoss = true;\n                SetActiveIdUsingAllKeyboardKeys();\n            }\n        }\n    }\n\n#if 0\n    if (hovered && g.HoveredIdNotActiveTimer > TOOLTIP_DELAY && bb.GetWidth() < tab->ContentWidth)\n    {\n        // Enlarge tab display when hovering\n        bb.Max.x = bb.Min.x + IM_TRUNC(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)));\n        display_draw_list = GetForegroundDrawList(window);\n        TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive));\n    }\n#endif\n\n    // Render tab shape\n    const bool is_visible = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) && !(flags & ImGuiTabItemFlags_Invisible);\n    if (is_visible)\n    {\n        ImDrawList* display_draw_list = window->DrawList;\n        const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed));\n        TabItemBackground(display_draw_list, bb, flags, tab_col);\n        if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f)\n        {\n            // Might be moved to TabItemBackground() ?\n            ImVec2 tl = bb.GetTL() + ImVec2(0, 1.0f * g.CurrentDpiScale);\n            ImVec2 tr = bb.GetTR() + ImVec2(0, 1.0f * g.CurrentDpiScale);\n            ImU32 overline_col = GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline);\n            if (style.TabRounding > 0.0f)\n            {\n                float rounding = style.TabRounding;\n                display_draw_list->PathArcToFast(tl + ImVec2(+rounding, +rounding), rounding, 7, 9);\n                display_draw_list->PathArcToFast(tr + ImVec2(-rounding, +rounding), rounding, 9, 11);\n                display_draw_list->PathStroke(overline_col, 0, style.TabBarOverlineSize);\n            }\n            else\n            {\n                display_draw_list->AddLine(tl - ImVec2(0.5f, 0.5f), tr - ImVec2(0.5f, 0.5f), overline_col, style.TabBarOverlineSize);\n            }\n        }\n        RenderNavCursor(bb, id);\n\n        // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget.\n        const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);\n        if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button)\n            TabBarQueueFocus(tab_bar, tab);\n\n        if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)\n            flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;\n\n        // Render tab label, process close button\n        const ImGuiID close_button_id = p_open ? GetIDWithSeed(\"#CLOSE\", NULL, docked_window ? docked_window->ID : id) : 0;\n        bool just_closed;\n        bool text_clipped;\n        TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped);\n        if (just_closed && p_open != NULL)\n        {\n            *p_open = false;\n            TabBarCloseTab(tab_bar, tab);\n        }\n\n        // Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent)\n        // That state is copied to window->DockTabItemStatusFlags by our caller.\n        if (docked_window && (hovered || g.HoveredId == close_button_id))\n            g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;\n\n        // Tooltip\n        // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok)\n        // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores)\n        // FIXME: This is a mess.\n        // FIXME: We may want disabled tab to still display the tooltip?\n        if (text_clipped && g.HoveredId == id && !held)\n            if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip))\n                SetItemTooltip(\"%.*s\", (int)(FindRenderedTextEnd(label) - label), label);\n    }\n\n    // Restore main window position so user can draw there\n    if (want_clip_rect)\n        PopClipRect();\n    window->DC.CursorPos = backup_main_cursor_pos;\n\n    IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected\n    if (is_tab_button)\n        return pressed;\n    return tab_contents_visible;\n}\n\n// [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed.\n// To use it to need to call the function SetTabItemClosed() between BeginTabBar() and EndTabBar().\n// Tabs closed by the close button will automatically be flagged to avoid this issue.\nvoid    ImGui::SetTabItemClosed(const char* label)\n{\n    ImGuiContext& g = *GImGui;\n    bool is_within_manual_tab_bar = g.CurrentTabBar && !(g.CurrentTabBar->Flags & ImGuiTabBarFlags_DockNode);\n    if (is_within_manual_tab_bar)\n    {\n        ImGuiTabBar* tab_bar = g.CurrentTabBar;\n        ImGuiID tab_id = TabBarCalcTabID(tab_bar, label, NULL);\n        if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))\n            tab->WantClose = true; // Will be processed by next call to TabBarLayout()\n    }\n    else if (ImGuiWindow* window = FindWindowByName(label))\n    {\n        if (window->DockIsActive)\n            if (ImGuiDockNode* node = window->DockNode)\n            {\n                ImGuiID tab_id = TabBarCalcTabID(node->TabBar, label, window);\n                TabBarRemoveTab(node->TabBar, tab_id);\n                window->DockTabWantClose = true;\n            }\n    }\n}\n\nImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker)\n{\n    ImGuiContext& g = *GImGui;\n    ImVec2 label_size = CalcTextSize(label, NULL, true);\n    ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f);\n    if (has_close_button_or_unsaved_marker)\n        size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle.\n    else\n        size.x += g.Style.FramePadding.x + 1.0f;\n    return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y);\n}\n\nImVec2 ImGui::TabItemCalcSize(ImGuiWindow* window)\n{\n    return TabItemCalcSize(window->Name, window->HasCloseButton || (window->Flags & ImGuiWindowFlags_UnsavedDocument));\n}\n\nvoid ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col)\n{\n    // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking \"detached\" from it.\n    ImGuiContext& g = *GImGui;\n    const float width = bb.GetWidth();\n    IM_UNUSED(flags);\n    IM_ASSERT(width > 0.0f);\n    const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f));\n    const float y1 = bb.Min.y + 1.0f;\n    const float y2 = bb.Max.y - g.Style.TabBarBorderSize;\n    draw_list->PathLineTo(ImVec2(bb.Min.x, y2));\n    draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9);\n    draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12);\n    draw_list->PathLineTo(ImVec2(bb.Max.x, y2));\n    draw_list->PathFillConvex(col);\n    if (g.Style.TabBorderSize > 0.0f)\n    {\n        draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2));\n        draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9);\n        draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12);\n        draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2));\n        draw_list->PathStroke(GetColorU32(ImGuiCol_Border), 0, g.Style.TabBorderSize);\n    }\n}\n\n// Render text label (with custom clipping) + Unsaved Document marker + Close Button logic\n// We tend to lock style.FramePadding for a given tab-bar, hence the 'frame_padding' parameter.\nvoid ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped)\n{\n    ImGuiContext& g = *GImGui;\n    ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    if (out_just_closed)\n        *out_just_closed = false;\n    if (out_text_clipped)\n        *out_text_clipped = false;\n\n    if (bb.GetWidth() <= 1.0f)\n        return;\n\n    // In Style V2 we'll have full override of all colors per state (e.g. focused, selected)\n    // But right now if you want to alter text color of tabs this is what you need to do.\n#if 0\n    const float backup_alpha = g.Style.Alpha;\n    if (!is_contents_visible)\n        g.Style.Alpha *= 0.7f;\n#endif\n\n    // Render text label (with clipping + alpha gradient) + unsaved marker\n    ImRect text_ellipsis_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);\n\n    // Return clipped state ignoring the close button\n    if (out_text_clipped)\n    {\n        *out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_ellipsis_clip_bb.Max.x;\n        //draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255));\n    }\n\n    const float button_sz = g.FontSize;\n    const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x - button_sz), bb.Min.y + frame_padding.y);\n\n    // Close Button & Unsaved Marker\n    // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap()\n    //  'hovered' will be true when hovering the Tab but NOT when hovering the close button\n    //  'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button\n    //  'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false\n    bool close_button_pressed = false;\n    bool close_button_visible = false;\n    bool is_hovered = g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id; // Any interaction account for this too.\n\n    if (close_button_id != 0)\n    {\n        if (is_contents_visible)\n            close_button_visible = (g.Style.TabCloseButtonMinWidthSelected < 0.0f) ? true : (is_hovered && bb.GetWidth() >= ImMax(button_sz, g.Style.TabCloseButtonMinWidthSelected));\n        else\n            close_button_visible = (g.Style.TabCloseButtonMinWidthUnselected < 0.0f) ? true : (is_hovered && bb.GetWidth() >= ImMax(button_sz, g.Style.TabCloseButtonMinWidthUnselected));\n    }\n\n    // When tabs/document is unsaved, the unsaved marker takes priority over the close button.\n    const bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x) && (!close_button_visible || !is_hovered);\n    if (unsaved_marker_visible)\n    {\n        ImVec2 bullet_pos = button_pos + ImVec2(button_sz, button_sz) * 0.5f;\n        RenderBullet(draw_list, bullet_pos, GetColorU32(ImGuiCol_UnsavedMarker));\n    }\n    else if (close_button_visible)\n    {\n        ImGuiLastItemData last_item_backup = g.LastItemData;\n        if (CloseButton(close_button_id, button_pos))\n            close_button_pressed = true;\n        g.LastItemData = last_item_backup;\n\n        // Close with middle mouse button\n        if (is_hovered && !(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2))\n            close_button_pressed = true;\n    }\n\n    // This is all rather complicated\n    // (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position)\n    // FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist..\n    float ellipsis_max_x = text_ellipsis_clip_bb.Max.x;\n    if (close_button_visible || unsaved_marker_visible)\n    {\n        const bool visible_without_hover = unsaved_marker_visible || (is_contents_visible ? g.Style.TabCloseButtonMinWidthSelected : g.Style.TabCloseButtonMinWidthUnselected) < 0.0f;\n        if (visible_without_hover)\n        {\n            text_ellipsis_clip_bb.Max.x -= button_sz * 0.90f;\n            ellipsis_max_x -= button_sz * 0.90f;\n        }\n        else\n        {\n            text_ellipsis_clip_bb.Max.x -= button_sz * 1.00f;\n        }\n    }\n    LogSetNextTextDecoration(\"/\", \"\\\\\");\n    RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, ellipsis_max_x, label, NULL, &label_size);\n\n#if 0\n    if (!is_contents_visible)\n        g.Style.Alpha = backup_alpha;\n#endif\n\n    if (out_just_closed)\n        *out_just_closed = close_button_pressed;\n}\n\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/imgui/source/misc/freetype/imgui_freetype.cpp",
    "content": "// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)\n// (code)\n\n// Get the latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype\n// Original code by @vuhdo (Aleksei Skriabin) in 2017, with improvements by @mikesart.\n// Maintained since 2019 by @ocornut.\n\n// CHANGELOG\n// (minor and older changes stripped away, please see git history for details)\n//  2025/06/11: refactored for the new ImFontLoader architecture, and ImGuiBackendFlags_RendererHasTextures support.\n//  2024/10/17: added plutosvg support for SVG Fonts (seems faster/better than lunasvg). Enable by using '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG'. (#7927)\n//  2023/11/13: added support for ImFontConfig::RasterizationDensity field for scaling render density without scaling metrics.\n//  2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG'. (#6591)\n//  2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.\n//  2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns nullptr.\n//  2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.\n//  2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a preferred texture format.\n//  2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).\n//  2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().\n//  2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.\n//  2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!)\n//  2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions().\n//  2019/01/10: re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding.\n//  2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX.\n//  2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)\n//  2018/01/22: fix for addition of ImFontAtlas::TexUvscale member.\n//  2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement).\n//  2017/09/26: fixes for imgui internal changes.\n//  2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply.\n//  2017/08/16: imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks.\n\n// About Gamma Correct Blending:\n// - FreeType assumes blending in linear space rather than gamma space.\n// - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph\n// - For correct results you need to be using sRGB and convert to linear space in the pixel shader output.\n// - The default dear imgui styles will be impacted by this change (alpha values will need tweaking).\n\n// FIXME: cfg.OversampleH, OversampleV are not supported, but generally not necessary with this rasterizer because Hinting makes everything look better.\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_freetype.h\"\n#include \"imgui_internal.h\"     // ImMin,ImMax,ImFontAtlasBuild*,\n#include <stdint.h>\n#include <ft2build.h>\n#include FT_FREETYPE_H          // <freetype/freetype.h>\n#include FT_MODULE_H            // <freetype/ftmodapi.h>\n#include FT_GLYPH_H             // <freetype/ftglyph.h>\n#include FT_SIZES_H             // <freetype/ftsizes.h>\n#include FT_SYNTHESIS_H         // <freetype/ftsynth.h>\n\n// Handle LunaSVG and PlutoSVG\n#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) && defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG)\n#error \"Cannot enable both IMGUI_ENABLE_FREETYPE_LUNASVG and IMGUI_ENABLE_FREETYPE_PLUTOSVG\"\n#endif\n#ifdef  IMGUI_ENABLE_FREETYPE_LUNASVG\n#include FT_OTSVG_H             // <freetype/otsvg.h>\n#include FT_BBOX_H              // <freetype/ftbbox.h>\n#include <lunasvg.h>\n#endif\n#ifdef  IMGUI_ENABLE_FREETYPE_PLUTOSVG\n#include <plutosvg.h>\n#endif\n#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined (IMGUI_ENABLE_FREETYPE_PLUTOSVG)\n#if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))\n#error IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12\n#endif\n#endif\n\n#ifdef _MSC_VER\n#pragma warning (push)\n#pragma warning (disable: 4505)     // unreferenced local function has been removed (stb stuff)\n#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).\n#endif\n\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpragmas\"                  // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wunused-function\"          // warning: 'xxxx' defined but not used\n#ifndef __clang__\n#pragma GCC diagnostic ignored \"-Wsubobject-linkage\"        // warning: 'xxxx' has a field 'xxxx' whose type uses the anonymous namespace\n#endif\n#endif\n\n//-------------------------------------------------------------------------\n// Data\n//-------------------------------------------------------------------------\n\n// Default memory allocators\nstatic void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); }\nstatic void  ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); }\n\n// Current memory allocators\nstatic void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc;\nstatic void  (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;\nstatic void* GImGuiFreeTypeAllocatorUserData = nullptr;\n\n// Lunasvg support\n#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG\nstatic FT_Error ImGuiLunasvgPortInit(FT_Pointer* state);\nstatic void     ImGuiLunasvgPortFree(FT_Pointer* state);\nstatic FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state);\nstatic FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state);\n#endif\n\n//-------------------------------------------------------------------------\n// Code\n//-------------------------------------------------------------------------\n\n#define FT_CEIL(X)      (((X + 63) & -64) / 64) // From SDL_ttf: Handy routines for converting from fixed point\n#define FT_SCALEFACTOR  64.0f\n\n// Glyph metrics:\n// --------------\n//\n//                       xmin                     xmax\n//                        |                         |\n//                        |<-------- width -------->|\n//                        |                         |\n//              |         +-------------------------+----------------- ymax\n//              |         |    ggggggggg   ggggg    |     ^        ^\n//              |         |   g:::::::::ggg::::g    |     |        |\n//              |         |  g:::::::::::::::::g    |     |        |\n//              |         | g::::::ggggg::::::gg    |     |        |\n//              |         | g:::::g     g:::::g     |     |        |\n//    offsetX  -|-------->| g:::::g     g:::::g     |  offsetY     |\n//              |         | g:::::g     g:::::g     |     |        |\n//              |         | g::::::g    g:::::g     |     |        |\n//              |         | g:::::::ggggg:::::g     |     |        |\n//              |         |  g::::::::::::::::g     |     |      height\n//              |         |   gg::::::::::::::g     |     |        |\n//  baseline ---*---------|---- gggggggg::::::g-----*--------      |\n//            / |         |             g:::::g     |              |\n//     origin   |         | gggggg      g:::::g     |              |\n//              |         | g:::::gg   gg:::::g     |              |\n//              |         |  g::::::ggg:::::::g     |              |\n//              |         |   gg:::::::::::::g      |              |\n//              |         |     ggg::::::ggg        |              |\n//              |         |         gggggg          |              v\n//              |         +-------------------------+----------------- ymin\n//              |                                   |\n//              |------------- advanceX ----------->|\n\n// Stored in ImFontAtlas::FontLoaderData. ALLOCATED BY US.\nstruct ImGui_ImplFreeType_Data\n{\n    FT_Library                      Library;\n    FT_MemoryRec_                   MemoryManager;\n    ImGui_ImplFreeType_Data()       { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// Stored in ImFontConfig::FontLoaderData. ALLOCATED BY US.\nstruct ImGui_ImplFreeType_FontSrcData\n{\n    // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime.\n    bool                            InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_user_flags);\n    void                            CloseFont();\n    ImGui_ImplFreeType_FontSrcData()   { memset((void*)this, 0, sizeof(*this)); }\n    ~ImGui_ImplFreeType_FontSrcData()  { CloseFont(); }\n\n    // Members\n    FT_Face                         FtFace;\n    ImGuiFreeTypeLoaderFlags        UserFlags;          // = ImFontConfig::FontLoaderFlags\n    FT_Int32                        LoadFlags;\n    ImFontBaked*                    BakedLastActivated;\n};\n\n// Stored in ImFontBaked::FontLoaderDatas: pointer to SourcesCount instances of this. ALLOCATED BY CORE.\nstruct ImGui_ImplFreeType_FontSrcBakedData\n{\n    FT_Size     FtSize;             // This represent a FT_Face with a given size.\n    ImGui_ImplFreeType_FontSrcBakedData() { memset((void*)this, 0, sizeof(*this)); }\n};\n\nbool ImGui_ImplFreeType_FontSrcData::InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_font_loader_flags)\n{\n    FT_Error error = FT_New_Memory_Face(ft_library, (const FT_Byte*)src->FontData, (FT_Long)src->FontDataSize, (FT_Long)src->FontNo, &FtFace);\n    if (error != 0)\n        return false;\n    error = FT_Select_Charmap(FtFace, FT_ENCODING_UNICODE);\n    if (error != 0)\n        return false;\n\n    // Convert to FreeType flags (NB: Bold and Oblique are processed separately)\n    UserFlags = (ImGuiFreeTypeLoaderFlags)(src->FontLoaderFlags | extra_font_loader_flags);\n\n    LoadFlags = 0;\n    if ((UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) == 0)\n        LoadFlags |= FT_LOAD_NO_BITMAP;\n    if (UserFlags & ImGuiFreeTypeLoaderFlags_NoHinting)\n        LoadFlags |= FT_LOAD_NO_HINTING;\n    if (UserFlags & ImGuiFreeTypeLoaderFlags_NoAutoHint)\n        LoadFlags |= FT_LOAD_NO_AUTOHINT;\n    if (UserFlags & ImGuiFreeTypeLoaderFlags_ForceAutoHint)\n        LoadFlags |= FT_LOAD_FORCE_AUTOHINT;\n    if (UserFlags & ImGuiFreeTypeLoaderFlags_LightHinting)\n        LoadFlags |= FT_LOAD_TARGET_LIGHT;\n    else if (UserFlags & ImGuiFreeTypeLoaderFlags_MonoHinting)\n        LoadFlags |= FT_LOAD_TARGET_MONO;\n    else\n        LoadFlags |= FT_LOAD_TARGET_NORMAL;\n\n    if (UserFlags & ImGuiFreeTypeLoaderFlags_LoadColor)\n        LoadFlags |= FT_LOAD_COLOR;\n    // IMHEX PATCH BEGIN\n    if (UserFlags & ImGuiFreeTypeLoaderFlags_SubPixel)\n        LoadFlags |= FT_LOAD_TARGET_LCD;\n    // IMHEX PATCH END\n\n    return true;\n}\n\nvoid ImGui_ImplFreeType_FontSrcData::CloseFont()\n{\n    if (FtFace)\n    {\n        FT_Done_Face(FtFace);\n        FtFace = nullptr;\n    }\n}\n\nstatic const FT_Glyph_Metrics* ImGui_ImplFreeType_LoadGlyph(ImGui_ImplFreeType_FontSrcData* src_data, uint32_t codepoint)\n{\n    uint32_t glyph_index = FT_Get_Char_Index(src_data->FtFace, codepoint);\n    if (glyph_index == 0)\n        return nullptr;\n\n    // If this crash for you: FreeType 2.11.0 has a crash bug on some bitmap/colored fonts.\n    // - https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076\n    // - https://github.com/ocornut/imgui/issues/4567\n    // - https://github.com/ocornut/imgui/issues/4566\n    // You can use FreeType 2.10, or the patched version of 2.11.0 in VcPkg, or probably any upcoming FreeType version.\n    FT_Error error = FT_Load_Glyph(src_data->FtFace, glyph_index, src_data->LoadFlags);\n    if (error)\n        return nullptr;\n\n    // Need an outline for this to work\n    FT_GlyphSlot slot = src_data->FtFace->glyph;\n#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG)\n    IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG);\n#else\n#if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))\n    IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && \"The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font\");\n#endif\n    IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);\n#endif // IMGUI_ENABLE_FREETYPE_LUNASVG\n\n    // Apply convenience transform (this is not picking from real \"Bold\"/\"Italic\" fonts! Merely applying FreeType helper transform. Oblique == Slanting)\n    if (src_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bold)\n        FT_GlyphSlot_Embolden(slot);\n    if (src_data->UserFlags & ImGuiFreeTypeLoaderFlags_Oblique)\n    {\n        FT_GlyphSlot_Oblique(slot);\n        //FT_BBox bbox;\n        //FT_Outline_Get_BBox(&slot->outline, &bbox);\n        //slot->metrics.width = bbox.xMax - bbox.xMin;\n        //slot->metrics.height = bbox.yMax - bbox.yMin;\n    }\n\n    return &slot->metrics;\n}\n\nstatic void ImGui_ImplFreeType_BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch)\n{\n    IM_ASSERT(ft_bitmap != nullptr);\n    const uint32_t w = ft_bitmap->width;\n    const uint32_t h = ft_bitmap->rows;\n    const uint8_t* src = ft_bitmap->buffer;\n    const uint32_t src_pitch = ft_bitmap->pitch;\n\n    switch (ft_bitmap->pixel_mode)\n    {\n    case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel.\n        {\n            for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)\n                for (uint32_t x = 0; x < w; x++)\n                    dst[x] = IM_COL32(255, 255, 255, src[x]);\n            break;\n        }\n    case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB.\n        {\n            for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)\n            {\n                uint8_t bits = 0;\n                const uint8_t* bits_ptr = src;\n                for (uint32_t x = 0; x < w; x++, bits <<= 1)\n                {\n                    if ((x & 7) == 0)\n                        bits = *bits_ptr++;\n                    dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? 255 : 0);\n                }\n            }\n            break;\n        }\n    case FT_PIXEL_MODE_BGRA:\n        {\n            // FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.\n            #define DE_MULTIPLY(color, alpha) ImMin((ImU32)(255.0f * (float)color / (float)(alpha + FLT_MIN) + 0.5f), 255u)\n            for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)\n                for (uint32_t x = 0; x < w; x++)\n                {\n                    uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];\n                    dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a);\n                }\n            #undef DE_MULTIPLY\n            break;\n        }\n    // IMHEX PATCH BEGIN\n    case FT_PIXEL_MODE_LCD:\n        {\n            memset(dst, 0x00, (size_t)w * h * sizeof(uint32_t));\n            for (uint32_t y = 0; y < h; y++) {\n                for (uint32_t x = 0; x < w / 3; x++) {\n                    dst[x] = IM_COL32(src[x * 3 + 0], src[x * 3 + 1], src[x * 3 + 2], (src[x * 3 + 0] + src[x * 3 + 1] + src[x * 3 + 2]) / 3);\n\n                    if (dst[x] == 0x00FFFFFF)\n                        dst[x] = 0x00000000;\n                }\n\n                src += src_pitch;\n                dst += dst_pitch;\n            }\n            break;\n        }\n    // IMHEX PATCH END\n    default:\n        IM_ASSERT(0 && \"FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!\");\n    }\n}\n\n// FreeType memory allocation callbacks\nstatic void* FreeType_Alloc(FT_Memory /*memory*/, long size)\n{\n    return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData);\n}\n\nstatic void FreeType_Free(FT_Memory /*memory*/, void* block)\n{\n    GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);\n}\n\nstatic void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block)\n{\n    // Implement realloc() as we don't ask user to provide it.\n    if (block == nullptr)\n        return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);\n\n    if (new_size == 0)\n    {\n        GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);\n        return nullptr;\n    }\n\n    if (new_size > cur_size)\n    {\n        void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);\n        memcpy(new_block, block, (size_t)cur_size);\n        GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);\n        return new_block;\n    }\n\n    return block;\n}\n\nstatic bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas)\n{\n    IM_ASSERT(atlas->FontLoaderData == nullptr);\n    ImGui_ImplFreeType_Data* bd = IM_NEW(ImGui_ImplFreeType_Data)();\n\n    // FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html\n    bd->MemoryManager.user = nullptr;\n    bd->MemoryManager.alloc = &FreeType_Alloc;\n    bd->MemoryManager.free = &FreeType_Free;\n    bd->MemoryManager.realloc = &FreeType_Realloc;\n\n    // https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library\n    FT_Error error = FT_New_Library(&bd->MemoryManager, &bd->Library);\n    if (error != 0)\n    {\n        IM_DELETE(bd);\n        return false;\n    }\n\n    // If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.\n    FT_Add_Default_Modules(bd->Library);\n\n#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG\n    // Install svg hooks for FreeType\n    // https://freetype.org/freetype2/docs/reference/ft2-properties.html#svg-hooks\n    // https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html#svg_fonts\n    SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot };\n    FT_Property_Set(bd->Library, \"ot-svg\", \"svg-hooks\", &hooks);\n#endif // IMGUI_ENABLE_FREETYPE_LUNASVG\n#ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG\n    // With plutosvg, use provided hooks\n    FT_Property_Set(bd->Library, \"ot-svg\", \"svg-hooks\", plutosvg_ft_svg_hooks());\n#endif // IMGUI_ENABLE_FREETYPE_PLUTOSVG\n\n    // Store our data\n    atlas->FontLoaderData = (void*)bd;\n\n    return true;\n}\n\nstatic void ImGui_ImplFreeType_LoaderShutdown(ImFontAtlas* atlas)\n{\n    ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData;\n    IM_ASSERT(bd != nullptr);\n    FT_Done_Library(bd->Library);\n    IM_DELETE(bd);\n    atlas->FontLoaderData = nullptr;\n}\n\nstatic bool ImGui_ImplFreeType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src)\n{\n    ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData;\n    ImGui_ImplFreeType_FontSrcData* bd_font_data = IM_NEW(ImGui_ImplFreeType_FontSrcData);\n    IM_ASSERT(src->FontLoaderData == nullptr);\n    src->FontLoaderData = bd_font_data;\n\n    if (!bd_font_data->InitFont(bd->Library, src, (ImGuiFreeTypeLoaderFlags)atlas->FontLoaderFlags))\n    {\n        IM_DELETE(bd_font_data);\n        src->FontLoaderData = nullptr;\n        return false;\n    }\n\n    return true;\n}\n\nstatic void ImGui_ImplFreeType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src)\n{\n    IM_UNUSED(atlas);\n    ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;\n    IM_DELETE(bd_font_data);\n    src->FontLoaderData = nullptr;\n}\n\nstatic bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src)\n{\n    IM_UNUSED(atlas);\n    float size = baked->Size;\n    if (src->MergeMode && src->SizePixels != 0.0f)\n        size *= (src->SizePixels / baked->OwnerFont->Sources[0]->SizePixels);\n    size *= src->ExtraSizeScale;\n\n    ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;\n    bd_font_data->BakedLastActivated = baked;\n\n    // We use one FT_Size per (source + baked) combination.\n    ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src;\n    IM_ASSERT(bd_baked_data != nullptr);\n    IM_PLACEMENT_NEW(bd_baked_data) ImGui_ImplFreeType_FontSrcBakedData();\n\n    FT_New_Size(bd_font_data->FtFace, &bd_baked_data->FtSize);\n    FT_Activate_Size(bd_baked_data->FtSize);\n\n    // Vuhdo 2017: \"I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'\n    // is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.\n    // FT_Set_Pixel_Sizes() doesn't seem to get us the same result.\"\n    // (FT_Set_Pixel_Sizes() essentially calls FT_Request_Size() with FT_SIZE_REQUEST_TYPE_NOMINAL)\n    const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;\n    FT_Size_RequestRec req;\n    req.type = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;\n    req.width = 0;\n    req.height = (uint32_t)(size * 64 * rasterizer_density);\n    req.horiResolution = 0;\n    req.vertResolution = 0;\n    FT_Request_Size(bd_font_data->FtFace, &req);\n\n    // Output\n    if (src->MergeMode == false)\n    {\n        // Read metrics\n        FT_Size_Metrics metrics = bd_baked_data->FtSize->metrics;\n        const float scale = 1.0f / (rasterizer_density * src->ExtraSizeScale);\n        baked->Ascent     = (float)FT_CEIL(metrics.ascender) * scale;       // The pixel extents above the baseline in pixels (typically positive).\n        baked->Descent    = (float)FT_CEIL(metrics.descender) * scale;      // The extents below the baseline in pixels (typically negative).\n        //LineSpacing     = (float)FT_CEIL(metrics.height) * scale;         // The baseline-to-baseline distance. Note that it usually is larger than the sum of the ascender and descender taken as absolute values. There is also no guarantee that no glyphs extend above or below subsequent baselines when using this distance. Think of it as a value the designer of the font finds appropriate.\n        //LineGap         = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender) * scale; // The spacing in pixels between one row's descent and the next row's ascent.\n        //MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance) * scale;    // This field gives the maximum horizontal cursor advance for all glyphs in the font.\n    }\n    return true;\n}\n\nstatic void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src)\n{\n    IM_UNUSED(atlas);\n    IM_UNUSED(baked);\n    IM_UNUSED(src);\n    ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src;\n    IM_ASSERT(bd_baked_data != nullptr);\n    FT_Done_Size(bd_baked_data->FtSize);\n    bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); // ~IM_PLACEMENT_DELETE()\n}\n\nstatic bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x)\n{\n    ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;\n    uint32_t glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint);\n    if (glyph_index == 0)\n        return false;\n\n    if (bd_font_data->BakedLastActivated != baked) // <-- could use id\n    {\n        // Activate current size\n        ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src;\n        FT_Activate_Size(bd_baked_data->FtSize);\n        bd_font_data->BakedLastActivated = baked;\n    }\n\n    const FT_Glyph_Metrics* metrics = ImGui_ImplFreeType_LoadGlyph(bd_font_data, codepoint);\n    if (metrics == nullptr)\n        return false;\n\n    FT_Face face = bd_font_data->FtFace;\n    FT_GlyphSlot slot = face->glyph;\n    const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;\n\n    // Load metrics only mode\n    const float advance_x = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density;\n    if (out_advance_x != NULL)\n    {\n        IM_ASSERT(out_glyph == NULL);\n        *out_advance_x = advance_x;\n        return true;\n    }\n\n    // Render glyph into a bitmap (currently held by FreeType)\n    // IMHEX PATCH BEGIN\n    FT_Render_Mode render_mode;\n    if (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Monochrome)\n        render_mode = FT_RENDER_MODE_MONO;\n    else if (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_SubPixel)\n        render_mode = FT_RENDER_MODE_LCD;\n    else\n        render_mode = FT_RENDER_MODE_NORMAL;\n    // IMHEX PATCH END\n\n    FT_Error error = FT_Render_Glyph(slot, render_mode);\n    const FT_Bitmap* ft_bitmap = &slot->bitmap;\n    if (error != 0 || ft_bitmap == nullptr)\n        return false;\n\n    const int w = (int)ft_bitmap->width;\n    const int h = (int)ft_bitmap->rows;\n    const bool is_visible = (w != 0 && h != 0);\n\n    // Prepare glyph\n    out_glyph->Codepoint = codepoint;\n    out_glyph->AdvanceX = advance_x;\n\n    // Pack and retrieve position inside texture atlas\n    if (is_visible)\n    {\n        ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, w, h);\n        if (pack_id == ImFontAtlasRectId_Invalid)\n        {\n            // Pathological out of memory case (TexMaxWidth/TexMaxHeight set too small?)\n            IM_ASSERT(pack_id != ImFontAtlasRectId_Invalid && \"Out of texture memory.\");\n            return false;\n        }\n        ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id);\n\n        // Render pixels to our temporary buffer\n        atlas->Builder->TempBuffer.resize(w * h * 4);\n        uint32_t* temp_buffer = (uint32_t*)atlas->Builder->TempBuffer.Data;\n        ImGui_ImplFreeType_BlitGlyph(ft_bitmap, temp_buffer, w);\n\n        const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;\n        const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;\n        float font_off_x = ImFloor(src->GlyphOffset.x * offsets_scale + 0.5f); // Snap scaled offset.\n        float font_off_y = ImFloor(src->GlyphOffset.y * offsets_scale + 0.5f) + baked->Ascent;\n        float recip_h = 1.0f / rasterizer_density;\n        float recip_v = 1.0f / rasterizer_density;\n\n        // Register glyph\n        float glyph_off_x = (float)face->glyph->bitmap_left;\n        float glyph_off_y = (float)-face->glyph->bitmap_top;\n        out_glyph->X0 = glyph_off_x * recip_h + font_off_x;\n        out_glyph->Y0 = glyph_off_y * recip_v + font_off_y;\n        out_glyph->X1 = (glyph_off_x + w) * recip_h + font_off_x;\n        out_glyph->Y1 = (glyph_off_y + h) * recip_v + font_off_y;\n        out_glyph->Visible = true;\n        out_glyph->Colored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA);\n        out_glyph->PackId = pack_id;\n        ImFontAtlasBakedSetFontGlyphBitmap(atlas, baked, src, out_glyph, r, (const unsigned char*)temp_buffer, ImTextureFormat_RGBA32, w * 4);\n    }\n\n    return true;\n}\n\nstatic bool ImGui_ImplFreetype_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint)\n{\n    IM_UNUSED(atlas);\n    ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;\n    int glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint);\n    return glyph_index != 0;\n}\n\nconst ImFontLoader* ImGuiFreeType::GetFontLoader()\n{\n    static ImFontLoader loader;\n    loader.Name = \"FreeType\";\n    loader.LoaderInit = ImGui_ImplFreeType_LoaderInit;\n    loader.LoaderShutdown = ImGui_ImplFreeType_LoaderShutdown;\n    loader.FontSrcInit = ImGui_ImplFreeType_FontSrcInit;\n    loader.FontSrcDestroy = ImGui_ImplFreeType_FontSrcDestroy;\n    loader.FontSrcContainsGlyph = ImGui_ImplFreetype_FontSrcContainsGlyph;\n    loader.FontBakedInit = ImGui_ImplFreeType_FontBakedInit;\n    loader.FontBakedDestroy = ImGui_ImplFreeType_FontBakedDestroy;\n    loader.FontBakedLoadGlyph = ImGui_ImplFreeType_FontBakedLoadGlyph;\n    loader.FontBakedSrcLoaderDataSize = sizeof(ImGui_ImplFreeType_FontSrcBakedData);\n    return &loader;\n}\n\nvoid ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)\n{\n    GImGuiFreeTypeAllocFunc = alloc_func;\n    GImGuiFreeTypeFreeFunc = free_func;\n    GImGuiFreeTypeAllocatorUserData = user_data;\n}\n\nbool ImGuiFreeType::DebugEditFontLoaderFlags(unsigned int* p_font_loader_flags)\n{\n    bool edited = false;\n    edited |= ImGui::CheckboxFlags(\"NoHinting\",    p_font_loader_flags, ImGuiFreeTypeLoaderFlags_NoHinting);\n    edited |= ImGui::CheckboxFlags(\"NoAutoHint\",   p_font_loader_flags, ImGuiFreeTypeLoaderFlags_NoAutoHint);\n    edited |= ImGui::CheckboxFlags(\"ForceAutoHint\",p_font_loader_flags, ImGuiFreeTypeLoaderFlags_ForceAutoHint);\n    edited |= ImGui::CheckboxFlags(\"LightHinting\", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_LightHinting);\n    edited |= ImGui::CheckboxFlags(\"MonoHinting\",  p_font_loader_flags, ImGuiFreeTypeLoaderFlags_MonoHinting);\n    edited |= ImGui::CheckboxFlags(\"Bold\",         p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Bold);\n    edited |= ImGui::CheckboxFlags(\"Oblique\",      p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Oblique);\n    edited |= ImGui::CheckboxFlags(\"Monochrome\",   p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Monochrome);\n    edited |= ImGui::CheckboxFlags(\"LoadColor\",    p_font_loader_flags, ImGuiFreeTypeLoaderFlags_LoadColor);\n    edited |= ImGui::CheckboxFlags(\"Bitmap\",       p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Bitmap);\n    return edited;\n}\n\n#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG\n// For more details, see https://gitlab.freedesktop.org/freetype/freetype-demos/-/blob/master/src/rsvg-port.c\n// The original code from the demo is licensed under CeCILL-C Free Software License Agreement (https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/LICENSE.TXT)\nstruct LunasvgPortState\n{\n    FT_Error            err = FT_Err_Ok;\n    lunasvg::Matrix     matrix;\n    std::unique_ptr<lunasvg::Document> svg = nullptr;\n};\n\nstatic FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state)\n{\n    *_state = IM_NEW(LunasvgPortState)();\n    return FT_Err_Ok;\n}\n\nstatic void ImGuiLunasvgPortFree(FT_Pointer* _state)\n{\n    IM_DELETE(*(LunasvgPortState**)_state);\n}\n\nstatic FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state)\n{\n    LunasvgPortState* state = *(LunasvgPortState**)_state;\n\n    // If there was an error while loading the svg in ImGuiLunasvgPortPresetSlot(), the renderer hook still get called, so just returns the error.\n    if (state->err != FT_Err_Ok)\n        return state->err;\n\n    // rows is height, pitch (or stride) equals to width * sizeof(int32)\n    lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch);\n#if LUNASVG_VERSION_MAJOR >= 3\n    state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated\n#else\n    state->svg->setMatrix(state->svg->matrix().identity()); // Reset the svg matrix to the default value\n    state->svg->render(bitmap, state->matrix);              // state->matrix is already scaled and translated\n#endif\n    state->err = FT_Err_Ok;\n    return state->err;\n}\n\nstatic FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state)\n{\n    FT_SVG_Document   document = (FT_SVG_Document)slot->other;\n    LunasvgPortState* state = *(LunasvgPortState**)_state;\n    FT_Size_Metrics&  metrics = document->metrics;\n\n    // This function is called twice, once in the FT_Load_Glyph() and another right before ImGuiLunasvgPortRender().\n    // If it's the latter, don't do anything because it's // already done in the former.\n    if (cache)\n        return state->err;\n\n    state->svg = lunasvg::Document::loadFromData((const char*)document->svg_document, document->svg_document_length);\n    if (state->svg == nullptr)\n    {\n        state->err = FT_Err_Invalid_SVG_Document;\n        return state->err;\n    }\n\n#if LUNASVG_VERSION_MAJOR >= 3\n    lunasvg::Box box = state->svg->boundingBox();\n#else\n    lunasvg::Box box = state->svg->box();\n#endif\n    double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h);\n    double xx = (double)document->transform.xx / (1 << 16);\n    double xy = -(double)document->transform.xy / (1 << 16);\n    double yx = -(double)document->transform.yx / (1 << 16);\n    double yy = (double)document->transform.yy / (1 << 16);\n    double x0 = (double)document->delta.x / 64 * box.w / metrics.x_ppem;\n    double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem;\n\n#if LUNASVG_VERSION_MAJOR >= 3\n    // Scale, transform and pre-translate the matrix for the rendering step\n    state->matrix = lunasvg::Matrix::translated(-box.x, -box.y);\n    state->matrix.multiply(lunasvg::Matrix(xx, xy, yx, yy, x0, y0));\n    state->matrix.scale(scale, scale);\n\n    // Apply updated transformation to the bounding box\n    box.transform(state->matrix);\n#else\n    // Scale and transform, we don't translate the svg yet\n    state->matrix.identity();\n    state->matrix.scale(scale, scale);\n    state->matrix.transform(xx, xy, yx, yy, x0, y0);\n    state->svg->setMatrix(state->matrix);\n\n    // Pre-translate the matrix for the rendering step\n    state->matrix.translate(-box.x, -box.y);\n\n    // Get the box again after the transformation\n    box = state->svg->box();\n#endif\n\n    // Calculate the bitmap size\n    slot->bitmap_left = FT_Int(box.x);\n    slot->bitmap_top = FT_Int(-box.y);\n    slot->bitmap.rows = (unsigned int)(ImCeil((float)box.h));\n    slot->bitmap.width = (unsigned int)(ImCeil((float)box.w));\n    slot->bitmap.pitch = slot->bitmap.width * 4;\n    slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;\n\n    // Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box.\n    double metrics_width = box.w;\n    double metrics_height = box.h;\n    double horiBearingX = box.x;\n    double horiBearingY = -box.y;\n    double vertBearingX = slot->metrics.horiBearingX / 64.0 - slot->metrics.horiAdvance / 64.0 / 2.0;\n    double vertBearingY = (slot->metrics.vertAdvance / 64.0 - slot->metrics.height / 64.0) / 2.0;\n    slot->metrics.width = FT_Pos(IM_ROUND(metrics_width * 64.0));   // Using IM_ROUND() assume width and height are positive\n    slot->metrics.height = FT_Pos(IM_ROUND(metrics_height * 64.0));\n    slot->metrics.horiBearingX = FT_Pos(horiBearingX * 64);\n    slot->metrics.horiBearingY = FT_Pos(horiBearingY * 64);\n    slot->metrics.vertBearingX = FT_Pos(vertBearingX * 64);\n    slot->metrics.vertBearingY = FT_Pos(vertBearingY * 64);\n\n    if (slot->metrics.vertAdvance == 0)\n        slot->metrics.vertAdvance = FT_Pos(metrics_height * 1.2 * 64.0);\n\n    state->err = FT_Err_Ok;\n    return state->err;\n}\n\n#endif // #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG\n\n//-----------------------------------------------------------------------------\n\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n\n#ifdef _MSC_VER\n#pragma warning (pop)\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\n# https://github.com/ocornut/imgui_test_engine\nproject(imgui_test_engine)\n\nset(CMAKE_CXX_STANDARD 23)\n\nif (IMHEX_ENABLE_IMGUI_TEST_ENGINE AND NOT IMHEX_EXTERNAL_PLUGIN_BUILD)\n    add_library(imgui_test_engine OBJECT\n            source/imgui_capture_tool.cpp\n            source/imgui_te_context.cpp\n            source/imgui_te_coroutine.cpp\n            source/imgui_te_engine.cpp\n            source/imgui_te_exporters.cpp\n            source/imgui_te_perftool.cpp\n            source/imgui_te_ui.cpp\n            source/imgui_te_utils.cpp\n    )\n\n    target_include_directories(imgui_test_engine PUBLIC\n        include\n    )\n    target_link_libraries(imgui_test_engine PRIVATE imgui_includes imgui_implot)\n    target_compile_definitions(imgui_test_engine PUBLIC IMGUI_TEST_ENGINE=1)\n    target_compile_definitions(imgui_test_engine PRIVATE EXPORT_SYMBOLS=1)\n\n    if (MSVC)\n        target_compile_options(imgui_test_engine PUBLIC /wd4251)\n    endif()\nendif()\n\nadd_library(imgui_test_engine_includes INTERFACE)\ntarget_include_directories(imgui_test_engine_includes INTERFACE include)\ntarget_include_directories(imgui_all_includes INTERFACE include)"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/LICENSE.txt",
    "content": "Dear ImGui Test Engine License (v1.04)\nCopyright (c) 2018-2025 Omar Cornut\n\nThis document is a legal agreement (\"License\") between you (\"Licensee\") and\nDISCO HELLO (\"Licensor\") that governs your use of Dear ImGui Test Engine (\"Software\").\n\n1. LICENSE MODELS\n\n1.1. Free license\n\nThe Licensor grants you a free license (\"Free License\") if you meet ANY of the following\ncriterion:\n\n- You are a natural person;\n- You are not a legal entity, or you are a not-for-profit legal entity;\n- You are using this Software for educational purposes;\n- You are using this Software to create Derivative Software released publicly and under\n  an Open Source license, as defined by the Open Source Initiative;\n- You are a legal entity with a turnover inferior to 2 million USD (or equivalent) during\n  your last fiscal year.\n\n1.2. Paid license\n\nIf you do not meet any criterion of Article 1.1, Licensor grants you a trial period of a\nmaximum of 45 days, at no charge. Following this trial period, you must subscribe to a paid\nlicense (\"Paid License\") with the Licensor to continue using the Software.\nPaid Licenses are exclusively sold by DISCO HELLO. Paid Licenses and the associated\ninformation are available at the following URL: http://www.dearimgui.com/licenses\n\n2. GRANT OF LICENSE\n\n2.1. License scope\n\nA limited and non-exclusive worldwide license is hereby granted, to the Licensee,\nto reproduce, execute, publicly perform, and display, use, copy, modify, merge,\ndistribute, or create derivative works based on and/or derived from the Software\n(\"Derivative Software\").\n\n2.2. Right of distribution\n\nLicense holders may also publish and/or distribute the Software or any Derivative\nSoftware. The above copyright notice and this license shall be included in all copies\nor substantial portions of the Software and/or Derivative Software.\n\nLicense holders may add their own attribution notices within the Derivative Software\nthat they distribute. Such attribution notices must not directly or indirectly imply a\nmodification of the License. License holders may provide to their modifications their\nown copyright and/or additional or different terms and conditions, providing such\nconditions complies with this License.\n\n3. DISCLAIMER\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\nINCLUDING BUT NOT LIMITED TO THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES\nOR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/README.md",
    "content": "# Dear ImGui Test Engine + Test Suite\n\nAutomation system for [Dear ImGui](https://github.com/ocornut/imgui) and applications/games/engines using Dear ImGui.\n\nBecause Dear ImGui tends to be wired in many low-level subsystems within your codebase, you can leverage that to automate actions interacting with them. It may be a very efficient solution to automate and tests many things (engine systems etc.).\n\n## Contents\n\n- [imgui_test_engine/](https://github.com/ocornut/imgui_test_engine/tree/main/imgui_test_engine): dear imgui test engine / automation system (library)\n- [imgui_test_suite/](https://github.com/ocornut/imgui_test_engine/tree/main/imgui_test_suite): dear imgui test suite (app)\n- [app_minimal/](https://github.com/ocornut/imgui_test_engine/tree/main/app_minimal): minimal demo app showcasing how to integrate the test engine (app)\n- [shared/](https://github.com/ocornut/imgui_test_engine/tree/main/shared): shared C++ helpers for apps\n\n## Don't blink\n\nRunning Dear ImGui Test Suite, in Fast Mode, with full rendering enabled:\n\nhttps://user-images.githubusercontent.com/8225057/182409619-cd3bf990-b383-4a6c-a6ba-c5afe7557d6c.mp4\n\nQuick overview of what automation code may look like like:\n```cpp\nImGuiTest* test = IM_REGISTER_TEST(e, \"demo_test\", \"test1\");\ntest->TestFunc = [](ImGuiTestContext* ctx)\n{\n    ctx->SetRef(\"My Window\");           // Set a base path so we don't have to specify full path afterwards\n    ctx->ItemClick(\"My Button\");        // Click \"My Button\" inside \"My Window\"\n    ctx->ItemCheck(\"Node/Checkbox\");    // Open \"Node\", find \"Checkbox\", ensure it is checked if not checked already.\n    ctx->ItemInputValue(\"Slider\", 123); // Find \"Slider\" and set the value to 123\n    IM_CHECK_EQ(app->SliderValue, 123); // Check value on app side\n    \n    ctx->MenuCheck(\"//Dear ImGui Demo/Tools/About Dear ImGui\"); // Show Dear ImGui About Window (assume Demo window is open)\n};\n```\n\n## Overview\n\n- Designed to **automate and test Dear ImGui applications**.\n- We also use it to **self-test Dear ImGui itself**, reduce regression and facilitate contributions.\n- **Test Engine interacts mostly from the point of view of an end-user, by injecting mouse/keyboard/gamepad** inputs into Dear ImGui's IO. It means it tries to \"find its way\" toward accomplishing an action. Opening an item may mean CTRL+Tabbing into a given widow, moving things out of the way, scrolling to locate the item, querying its open status, etc.\n- It can be used for a variety of testing (smoke testing, integration/functional testing) or automation purposes (running tasks, capturing videos, etc.).\n- Your app can be controlled from Dear ImGui + You can now automate your app = **You can test anything exposed in your app/engine**! (not only UI).\n- It **can run in your windowed application**. **It can also run headless** (e.g. running GUI tests from a console or on a CI server without rendering).\n- It **can run at simulated human speed** (for watching or exporting videos) or **can run in fast mode** (e.g. teleporting mouse).\n- It **can export screenshots and videos/gifs**. They can be leveraged for some forms of testing, but also to generate assets for documentation, or notify teams of certain changes. Assets that often need to be updated are best generated from a script inside of manually recreated/cropped/exported.\n- You can use it to program high-level commands e.g. `MenuCheck(\"Edit/Options/Enable Grid\")` or run more programmatic queries (\"list openable items in that section, then open them all\"). So from your POV it could be used for simple smoke testing (\"open all our tools\") or for more elaborate testing (\"interact with xxx and xxx, check result\").\n- It **can be used as a form of \"live tutorial / demo\"** where a script can run on an actual user application to showcase features.\n- It includes a performance tool and viewer which we used to record/compare performances between builds and branches (optional, requires ImPlot).\n\n## Status\n\n- It is currently a C++ API but down the line we can expect that the commands will be better standardized, stored in data files, called from other languages.\n- It has been in use and development since 2018 and only made public at the end of 2022. We'll provide best effort to make it suitable for user needs.\n- You will run into problems and shortcomings. We are happy to hear about them and improve the software and documentation accordingly.\n\n## Documentation\n\nSee [Wiki](https://github.com/ocornut/imgui_test_engine/wiki) sections:\n- [Overview](https://github.com/ocornut/imgui_test_engine/wiki/Overview)\n- [Setting Up](https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up)\n- [Automation API](https://github.com/ocornut/imgui_test_engine/wiki/Automation-API) (ImGuiTestContext interface)\n- [Named References](https://github.com/ocornut/imgui_test_engine/wiki/Named-References) (all nice uses of ImGuiTestRef)\n- [Exporting Results](https://github.com/ocornut/imgui_test_engine/wiki/Exporting-Results)\n- [Screen & Video Captures](https://github.com/ocornut/imgui_test_engine/wiki/Screen-and-Video-Captures)\n\n## Licenses\n\n- The [imgui_test_engine/](https://github.com/ocornut/imgui_test_engine/tree/main/imgui_test_engine) folder is under the [Dear ImGui Test Engine License](https://github.com/ocornut/imgui_test_engine/blob/main/imgui_test_engine/LICENSE.txt).<BR>TL;DR: free for individuals, educational, open-source and small businesses uses. Paid for larger businesses. Read license for details. License sales to larger businesses are used to fund and sustain the development of Dear ImGui.\n- The [imgui_test_suite/](https://github.com/ocornut/imgui_test_engine/tree/main/imgui_test_suite) folder and all other folders are all under the [MIT License](https://github.com/ocornut/imgui_test_engine/blob/main/imgui_test_suite/LICENSE.txt).\n\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/imgui_capture_tool.h",
    "content": "// dear imgui test engine\n// (screen/video capture tool)\n// This is usable as a standalone applet or controlled by the test engine.\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#pragma once\n\n// Need \"imgui_te_engine.h\" included for ImFuncPtr\n\n//-----------------------------------------------------------------------------\n// Forward declarations\n//-----------------------------------------------------------------------------\n\n// Our types\nstruct ImGuiCaptureArgs;                // Parameters for Capture\nstruct ImGuiCaptureContext;             // State of an active capture tool\nstruct ImGuiCaptureImageBuf;            // Simple helper to store an RGBA image in memory\nstruct ImGuiCaptureToolUI;              // Capture tool instance + UI window\n\ntypedef unsigned int ImGuiCaptureFlags; // See enum: ImGuiCaptureFlags_\n\n// Capture function which needs to be provided by user application\ntypedef bool (ImGuiScreenCaptureFunc)(ImGuiID viewport_id, int x, int y, int w, int h, unsigned int* pixels, void* user_data);\n\n// External types\nstruct ImGuiWindow; // imgui.h\n\n//-----------------------------------------------------------------------------\n\n// [Internal]\n// Helper class for simple bitmap manipulation (not particularly efficient!)\nstruct IMGUI_API ImGuiCaptureImageBuf\n{\n    int             Width;\n    int             Height;\n    unsigned int*   Data;       // RGBA8\n\n    ImGuiCaptureImageBuf()      { Width = Height = 0; Data = nullptr; }\n    ~ImGuiCaptureImageBuf()     { Clear(); }\n\n    void Clear();                                           // Free allocated memory buffer if such exists.\n    void CreateEmpty(int w, int h);                         // Reallocate buffer for pixel data and zero it.\n    bool SaveFile(const char* filename);                    // Save pixel data to specified image file.\n    void RemoveAlpha();                                     // Clear alpha channel from all pixels.\n};\n\nenum ImGuiCaptureFlags_ : unsigned int\n{\n    ImGuiCaptureFlags_None                      = 0,\n    ImGuiCaptureFlags_StitchAll                 = 1 << 0,   // Capture entire window scroll area (by scrolling and taking multiple screenshot). Only works for a single window.\n    ImGuiCaptureFlags_IncludeOtherWindows       = 1 << 1,   // Disable hiding other windows (when CaptureAddWindow has been called by default other windows are hidden)\n    ImGuiCaptureFlags_IncludePopups             = 1 << 2,   // Expand capture area to automatically include visible popups (Unused if ImGuiCaptureFlags_IncludeOtherWindows is set)\n    ImGuiCaptureFlags_HideMouseCursor           = 1 << 3,   // Hide render software mouse cursor during capture.\n    ImGuiCaptureFlags_Instant                   = 1 << 4,   // Perform capture on very same frame. Only works when capturing a rectangular region. Unsupported features: content stitching, window hiding, window relocation.\n    ImGuiCaptureFlags_NoSave                    = 1 << 5    // Do not save output image.\n};\n\n// Defines input and output arguments for capture process.\n// When capturing from tests you can usually use the ImGuiTestContext::CaptureXXX() helpers functions.\nstruct ImGuiCaptureArgs\n{\n    // [Input]\n    ImGuiCaptureFlags       InFlags = 0;                    // Flags for customizing behavior of screenshot tool.\n    ImVector<ImGuiWindow*>  InCaptureWindows;               // Windows to capture. All other windows will be hidden. May be used with InCaptureRect to capture only some windows in specified rect.\n    ImRect                  InCaptureRect;                  // Screen rect to capture. Does not include padding.\n    float                   InPadding = 16.0f;              // Extra padding at the edges of the screenshot. Ensure that there is available space around capture rect horizontally, also vertically if ImGuiCaptureFlags_StitchFullContents is not used.\n    char                    InOutputFile[256] = \"\";         // Output will be saved to a file if InOutputImageBuf is nullptr.\n    ImGuiCaptureImageBuf*   InOutputImageBuf = nullptr;     // _OR_ Output will be saved to image buffer if specified.\n    int                     InRecordFPSTarget = 30;         // FPS target for recording videos.\n    int                     InSizeAlign = 0;                // Resolution alignment (0 = auto, 1 = no alignment, >= 2 = align width/height to be multiple of given value)\n\n    // [Output]\n    ImVec2                  OutImageSize;                   // Produced image size.\n};\n\nenum ImGuiCaptureStatus\n{\n    ImGuiCaptureStatus_InProgress,\n    ImGuiCaptureStatus_Done,\n    ImGuiCaptureStatus_Error\n};\n\nstruct ImGuiCaptureWindowData\n{\n    ImGuiWindow*            Window;\n    ImRect                  BackupRect;\n    ImVec2                  PosDuringCapture;\n};\n\n// Implements functionality for capturing images\nstruct IMGUI_API ImGuiCaptureContext\n{\n    // IO\n    ImFuncPtr(ImGuiScreenCaptureFunc) ScreenCaptureFunc = nullptr;  // Graphics backend specific function that captures specified portion of framebuffer and writes RGBA data to `pixels` buffer.\n    void*                   ScreenCaptureUserData = nullptr;        // Custom user pointer which is passed to ScreenCaptureFunc. (Optional)\n    char*                   VideoCaptureEncoderPath = nullptr;      // Video encoder path (not owned, stored externally).\n    int                     VideoCaptureEncoderPathSize = 0;        // Optional. Set in order to edit this parameter from UI.\n    char*                   VideoCaptureEncoderParams = nullptr;    // Video encoder params (not owned, stored externally).\n    int                     VideoCaptureEncoderParamsSize = 0;      // Optional. Set in order to edit this parameter from UI.\n    char*                   GifCaptureEncoderParams = nullptr;      // Video encoder params for GIF output (not owned, stored externally).\n    int                     GifCaptureEncoderParamsSize = 0;        // Optional. Set in order to edit this parameter from UI.\n\n    // [Internal]\n    ImRect                  _CaptureRect;                   // Viewport rect that is being captured.\n    ImRect                  _CapturedWindowRect;            // Top-left corner of region that covers all windows included in capture. This is not same as _CaptureRect.Min when capturing explicitly specified rect.\n    int                     _ChunkNo = 0;                   // Number of chunk that is being captured when capture spans multiple frames.\n    int                     _FrameNo = 0;                   // Frame number during capture process that spans multiple frames.\n    ImVec2                  _MouseRelativeToWindowPos;      // Mouse cursor position relative to captured window (when _StitchAll is in use).\n    ImGuiWindow*            _HoveredWindow = nullptr;       // Window which was hovered at capture start.\n    ImGuiCaptureImageBuf    _CaptureBuf;                    // Output image buffer.\n    const ImGuiCaptureArgs* _CaptureArgs = nullptr;         // Current capture args. Set only if capture is in progress.\n    ImVector<ImGuiCaptureWindowData> _WindowsData;          // Backup windows that will have their rect modified and restored. args->InCaptureWindows can not be used because popups may get closed during capture and no longer appear in that list.\n\n    // [Internal] Video recording\n    bool                    _VideoRecording = false;        // Flag indicating that video recording is in progress.\n    double                  _VideoLastFrameTime = 0;        // Time when last video frame was recorded.\n    FILE*                   _VideoEncoderPipe = nullptr;    // File writing to stdin of video encoder process.\n\n    // [Internal] Backups\n    bool                    _BackupMouseDrawCursor = false; // Initial value of g.IO.MouseDrawCursor\n    ImVec2                  _BackupDisplayWindowPadding;    // Backup padding. We set it to {0, 0} during capture.\n    ImVec2                  _BackupDisplaySafeAreaPadding;  // Backup padding. We set it to {0, 0} during capture.\n\n    //-------------------------------------------------------------------------\n    // Functions\n    //-------------------------------------------------------------------------\n\n    ImGuiCaptureContext(ImGuiScreenCaptureFunc capture_func = nullptr) { ScreenCaptureFunc = capture_func; _MouseRelativeToWindowPos = ImVec2(-FLT_MAX, -FLT_MAX); }\n\n    // These functions should be called from appropriate context hooks. See ImGui::AddContextHook() for more info.\n    // (ImGuiTestEngine automatically calls that for you, so this only apply to independently created instance)\n    void                    PreNewFrame();\n    void                    PreRender();\n    void                    PostRender();\n\n    // Update capturing. If this function returns true then it should be called again with same arguments on the next frame.\n    ImGuiCaptureStatus      CaptureUpdate(ImGuiCaptureArgs* args);\n    void                    RestoreBackedUpData();\n    void                    ClearState();\n\n    // Begin video capture. Call CaptureUpdate() every frame afterwards until it returns false.\n    void                    BeginVideoCapture(ImGuiCaptureArgs* args);\n    void                    EndVideoCapture();\n    bool                    IsCapturingVideo();\n    bool                    IsCapturing();\n};\n\n//-----------------------------------------------------------------------------\n// ImGuiCaptureToolUI\n//-----------------------------------------------------------------------------\n\n// Implements UI for capturing images\n// (when using ImGuiTestEngine scripting API you may not need to use this at all)\nstruct IMGUI_API ImGuiCaptureToolUI\n{\n    float               SnapGridSize = 32.0f;               // Size of the grid cell for \"snap to grid\" functionality.\n    char                OutputLastFilename[256] = \"\";       // File name of last captured file.\n    char*               VideoCaptureExtension = nullptr;    // Video file extension (e.g. \".gif\" or \".mp4\")\n    int                 VideoCaptureExtensionSize = 0;      // Optional. Set in order to edit this parameter from UI.\n\n    ImGuiCaptureArgs    _CaptureArgs;                       // Capture args\n    bool                _StateIsPickingWindow = false;\n    bool                _StateIsCapturing = false;\n    ImVector<ImGuiID>   _SelectedWindows;\n    char                _OutputFileTemplate[256] = \"\";      //\n    int                 _FileCounter = 0;                   // Counter which may be appended to file name when saving. By default, counting starts from 1. When done this field holds number of saved files.\n\n    // Public\n    ImGuiCaptureToolUI();\n    void    ShowCaptureToolWindow(ImGuiCaptureContext* context, bool* p_open = nullptr);   // Render a capture tool window with various options and utilities.\n\n    // [Internal]\n    void    _CaptureWindowPicker(ImGuiCaptureArgs* args);       // Render a window picker that captures picked window to file specified in file_name.\n    void    _CaptureWindowsSelector(ImGuiCaptureContext* context, ImGuiCaptureArgs* args);    // Render a selector for selecting multiple windows for capture.\n    void    _SnapWindowsToGrid(float cell_size);                // Snap edges of all visible windows to a virtual grid.\n    bool    _InitializeOutputFile();                            // Format output file template into capture args struct and ensure target directory exists.\n    bool    _ShowEncoderConfigFields(ImGuiCaptureContext* context);\n};\n\n#define IMGUI_CAPTURE_DEFAULT_VIDEO_PARAMS_FOR_FFMPEG   \"-hide_banner -loglevel error -r $FPS -f rawvideo -pix_fmt rgba -s $WIDTHx$HEIGHT -i - -threads 0 -y -preset ultrafast -pix_fmt yuv420p -crf 20 $OUTPUT\"\n#define IMGUI_CAPTURE_DEFAULT_GIF_PARAMS_FOR_FFMPEG     \"-hide_banner -loglevel error -r $FPS -f rawvideo -pix_fmt rgba -s $WIDTHx$HEIGHT -i - -threads 0 -y -filter_complex \\\"split=2 [a] [b]; [a] palettegen [pal]; [b] [pal] paletteuse\\\" $OUTPUT\"\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/imgui_te_context.h",
    "content": "// dear imgui test engine\n// (context when a running test + end user automation API)\n// This is the main (if not only) interface that your Tests will be using.\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#pragma once\n\n#include \"imgui.h\"\n#include \"imgui_internal.h\"     // ImGuiAxis, ImGuiItemStatusFlags, ImGuiInputSource, ImGuiWindow\n#include \"imgui_te_engine.h\"    // ImGuiTestStatus, ImGuiTestRunFlags, ImGuiTestActiveFunc, ImGuiTestItemInfo, ImGuiTestLogFlags\n\n/*\n\nIndex of this file:\n// [SECTION] Header mess, warnings\n// [SECTION] Forward declarations\n// [SECTION] ImGuiTestRef\n// [SECTION] Helper keys\n// [SECTION] ImGuiTestContext related Flags/Enumerations\n// [SECTION] ImGuiTestGenericVars, ImGuiTestGenericItemStatus\n// [SECTION] ImGuiTestContext\n// [SECTION] Debugging macros: IM_SUSPEND_TESTFUNC()\n// [SECTION] Testing/Checking macros: IM_CHECK(), IM_ERRORF() etc.\n\n*/\n\n//-------------------------------------------------------------------------\n// [SECTION] Header mess, warnings\n//-------------------------------------------------------------------------\n\n// Undo some of the damage done by <windows.h>\n#ifdef Yield\n#undef Yield\n#endif\n\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#elif defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpragmas\"                          // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#endif\n\n//-------------------------------------------------------------------------\n// [SECTION] Forward declarations\n//-------------------------------------------------------------------------\n\n// This file\ntypedef int ImGuiTestOpFlags;       // Flags: See ImGuiTestOpFlags_\n\n// External: imgui\nstruct ImGuiDockNode;\nstruct ImGuiTabBar;\nstruct ImGuiWindow;\n\n// External: test engine\nstruct ImGuiTest;                   // A test registered with IM_REGISTER_TEST()\nstruct ImGuiTestEngine;             // Test Engine Instance (opaque)\nstruct ImGuiTestEngineIO;           // Test Engine IO structure (configuration flags, state)\nstruct ImGuiTestItemInfo;           // Information gathered about an item: label, status, bounding box etc.\nstruct ImGuiTestItemList;           // Result of an GatherItems() query\nstruct ImGuiTestInputs;             // Test Engine Simulated Inputs structure (opaque)\nstruct ImGuiTestGatherTask;         // Test Engine task for scanning/finding items\nstruct ImGuiCaptureArgs;            // Parameters for ctx->CaptureXXX functions\nenum ImGuiTestVerboseLevel : int;\n\n//-------------------------------------------------------------------------\n// [SECTION] ImGuiTestRef\n//-------------------------------------------------------------------------\n\n// Weak reference to an Item/Window given an hashed ID _or_ a string path ID.\n// This is most often passed as argument to function and generally has a very short lifetime.\n// Documentation: https://github.com/ocornut/imgui_test_engine/wiki/Named-References\n// (SUGGESTION: add those constructors to \"VA Step Filter\" (Visual Assist) or a .natstepfilter file (Visual Studio) so they are skipped by F11 (StepInto)\nstruct IMGUI_API ImGuiTestRef\n{\n    ImGuiID         ID;             // Pre-hashed ID\n    const char*     Path;           // Relative or absolute path (string pointed to, not owned, as our lifetime is very short)\n\n    ImGuiTestRef()                  { ID = 0; Path = nullptr; }\n    ImGuiTestRef(ImGuiID id)        { ID = id; Path = nullptr; }\n    ImGuiTestRef(const char* path)  { ID = 0; Path = path; }\n    bool IsEmpty() const            { return ID == 0 && (Path == nullptr || Path[0] == 0); }\n};\n\n// Debug helper to output a string showing the Path, ID or Debug Label based on what is available (some items only have ID as we couldn't find/store a Path)\n// (The size is arbitrary, this is only used for logging info the user/debugger)\nstruct IMGUI_API ImGuiTestRefDesc\n{\n    char            Buf[80];\n\n    const char* c_str()             { return Buf; }\n    ImGuiTestRefDesc(const ImGuiTestRef& ref);\n    ImGuiTestRefDesc(const ImGuiTestRef& ref, const ImGuiTestItemInfo& item);\n};\n\n//-------------------------------------------------------------------------\n// [SECTION] ImGuiTestContext related Flags/Enumerations\n//-------------------------------------------------------------------------\n\n// Named actions. Generally you will call the named helpers e.g. ItemClick(). This is used by shared/low-level functions such as ItemAction().\nenum ImGuiTestAction\n{\n    ImGuiTestAction_Unknown = 0,\n    ImGuiTestAction_Hover,          // Move mouse\n    ImGuiTestAction_Click,          // Move mouse and click\n    ImGuiTestAction_DoubleClick,    // Move mouse and double-click\n    ImGuiTestAction_Check,          // Check item if unchecked (Checkbox, MenuItem or any widget reporting ImGuiItemStatusFlags_Checkable)\n    ImGuiTestAction_Uncheck,        // Uncheck item if checked\n    ImGuiTestAction_Open,           // Open item if closed (TreeNode, BeginMenu or any widget reporting ImGuiItemStatusFlags_Openable)\n    ImGuiTestAction_Close,          // Close item if opened\n    ImGuiTestAction_Input,          // Start text inputing into a field (e.g. CTRL+Click on Drags/Slider, click on InputText etc.)\n    ImGuiTestAction_NavActivate,    // Activate item with navigation\n    ImGuiTestAction_COUNT\n};\n\n// Generic flags for many ImGuiTestContext functions\n// Some flags are only supported by a handful of functions. Check function headers for list of supported flags.\nenum ImGuiTestOpFlags_\n{\n    ImGuiTestOpFlags_None               = 0,\n    ImGuiTestOpFlags_NoCheckHoveredId   = 1 << 1,   // Don't check for HoveredId after aiming for a widget. A few situations may want this: while e.g. dragging or another items prevents hovering, or for items that don't use ItemHoverable()\n    ImGuiTestOpFlags_NoError            = 1 << 2,   // Don't abort/error e.g. if the item cannot be found or the operation doesn't succeed.\n    ImGuiTestOpFlags_NoFocusWindow      = 1 << 3,   // Don't focus window when aiming at an item\n    ImGuiTestOpFlags_NoAutoUncollapse   = 1 << 4,   // Disable automatically uncollapsing windows (useful when specifically testing Collapsing behaviors)\n    ImGuiTestOpFlags_NoAutoOpenFullPath = 1 << 5,   // Disable automatically opening intermediaries (e.g. ItemClick(\"Hello/OK\") will automatically first open \"Hello\" if \"OK\" isn't found. Only works if ref is a string path.\n    ImGuiTestOpFlags_NoYield            = 1 << 6,   // Don't yield (only supported by a few functions), in case you need to manage rigorous per-frame timing.\n    ImGuiTestOpFlags_IsSecondAttempt    = 1 << 7,   // Used by recursing functions to indicate a second attempt\n    ImGuiTestOpFlags_MoveToEdgeL        = 1 << 8,   // Simple Dumb aiming helpers to test widget that care about clicking position. May need to replace will better functionalities.\n    ImGuiTestOpFlags_MoveToEdgeR        = 1 << 9,\n    ImGuiTestOpFlags_MoveToEdgeU        = 1 << 10,\n    ImGuiTestOpFlags_MoveToEdgeD        = 1 << 11,\n};\n\n// Advanced filtering for ItemActionAll()\nstruct IMGUI_API ImGuiTestActionFilter\n{\n    int                     MaxDepth;\n    int                     MaxPasses;\n    const int*              MaxItemCountPerDepth;\n    ImGuiItemStatusFlags    RequireAllStatusFlags;\n    ImGuiItemStatusFlags    RequireAnyStatusFlags;\n\n    ImGuiTestActionFilter() { MaxDepth = -1; MaxPasses = -1; MaxItemCountPerDepth = nullptr; RequireAllStatusFlags = RequireAnyStatusFlags = 0; }\n};\n\n//-------------------------------------------------------------------------\n// [SECTION] ImGuiTestGenericVars, ImGuiTestGenericItemStatus\n//-------------------------------------------------------------------------\n\n// Helper struct to store various query-able state of an item.\n// This facilitate interactions between GuiFunc and TestFunc, since those state are frequently used.\nstruct IMGUI_API ImGuiTestGenericItemStatus\n{\n    int     RetValue;               // return value\n    int     Hovered;                // result of IsItemHovered()\n    int     HoveredAllowDisabled;   // result of IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)\n    int     Active;                 // result of IsItemActive()\n    int     Focused;                // result of IsItemFocused()\n    int     Clicked;                // result of IsItemClicked()\n    int     Visible;                // result of IsItemVisible()\n    int     Edited;                 // result of IsItemEdited()\n    int     Activated;              // result of IsItemActivated()\n    int     Deactivated;            // result of IsItemDeactivated()\n    int     DeactivatedAfterEdit;   // result of IsItemDeactivatedAfterEdit()\n\n    ImGuiTestGenericItemStatus()        { Clear(); }\n    void Clear()                        { memset(this, 0, sizeof(*this)); }\n    void QuerySet(bool ret_val = false) { Clear(); QueryInc(ret_val); }\n    void QueryInc(bool ret_val = false) { RetValue += ret_val; Hovered += ImGui::IsItemHovered(); HoveredAllowDisabled += ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled); Active += ImGui::IsItemActive(); Focused += ImGui::IsItemFocused(); Clicked += ImGui::IsItemClicked(); Visible += ImGui::IsItemVisible(); Edited += ImGui::IsItemEdited(); Activated += ImGui::IsItemActivated(); Deactivated += ImGui::IsItemDeactivated(); DeactivatedAfterEdit += ImGui::IsItemDeactivatedAfterEdit(); }\n    void Draw()                         { ImGui::Text(\"Ret: %d, Hovered: %d, Active: %d, Focused: %d\\nClicked: %d, Visible: %d, Edited: %d\\nActivated: %d, Deactivated: %d, DeactivatedAfterEdit: %d\", RetValue, Hovered, Active, Focused, Clicked, Visible, Edited, Activated, Deactivated, DeactivatedAfterEdit); }\n};\n\n// Generic structure with various storage fields.\n// This is useful for tests to quickly share data between GuiFunc and TestFunc without creating custom data structure.\n// If those fields are not enough: using test->SetVarsDataType<>() + ctx->GetVars<>() it is possible to store custom data.\nstruct IMGUI_API ImGuiTestGenericVars\n{\n    // Generic storage with a bit of semantic to make user/test code look neater\n    int                     Step;\n    int                     Count;\n    ImGuiID                 DockId;\n    ImGuiID                 OwnerId;\n    ImVec2                  WindowSize;\n    ImGuiWindowFlags        WindowFlags;\n    ImGuiTableFlags         TableFlags;\n    ImGuiPopupFlags         PopupFlags;\n    ImGuiInputTextFlags     InputTextFlags;\n    ImGuiTestGenericItemStatus  Status;\n    bool                    ShowWindow1, ShowWindow2;\n    bool                    UseClipper;\n    bool                    UseViewports;\n    float                   Width;\n    ImVec2                  Pos;\n    ImVec2                  Pivot;\n    ImVec2                  ItemSize;\n    ImVec4                  Color1, Color2;\n\n    // Generic unnamed storage\n    int                     Int1, Int2, IntArray[10];\n    float                   Float1, Float2, FloatArray[10];\n    bool                    Bool1, Bool2, BoolArray[10];\n    ImGuiID                 Id, IdArray[10];\n    char                    Str1[256], Str2[256];\n\n    ImGuiTestGenericVars()  { Clear(); }\n    void Clear()            { memset(this, 0, sizeof(*this)); }\n};\n\n//-------------------------------------------------------------------------\n// [SECTION] ImGuiTestContext\n// Context for a running ImGuiTest\n// This is the interface that most tests will interact with.\n//-------------------------------------------------------------------------\n\nstruct IMGUI_API ImGuiTestContext\n{\n    // User variables\n    ImGuiTestGenericVars    GenericVars;                            // Generic variables holder for convenience.\n    void*                   UserVars = nullptr;                     // Access using ctx->GetVars<Type>(). Setup with test->SetVarsDataType<>().\n\n    // Public fields\n    ImGuiContext*           UiContext = nullptr;                    // UI context\n    ImGuiTestEngineIO*      EngineIO = nullptr;                     // Test Engine IO/settings\n    ImGuiTest*              Test = nullptr;                         // Test currently running\n    ImGuiTestOutput*        TestOutput = nullptr;                   // Test output (generally == &Test->Output)\n    ImGuiTestOpFlags        OpFlags = ImGuiTestOpFlags_None;        // Flags affecting all operation (supported: ImGuiTestOpFlags_NoAutoUncollapse)\n    int                     PerfStressAmount = 0;                   // Convenience copy of engine->IO.PerfStressAmount\n    int                     FrameCount = 0;                         // Test frame count (restarts from zero every time)\n    int                     FirstTestFrameCount = 0;                // First frame where TestFunc is running (after warm-up frame). This is generally -1 or 0 depending on whether we have warm up enabled\n    bool                    FirstGuiFrame = false;\n    bool                    HasDock = false;                        // #ifdef IMGUI_HAS_DOCK expressed in an easier to test value\n    ImGuiCaptureArgs*       CaptureArgs = nullptr;                  // Capture settings used by ctx->Capture*() functions\n\n    //-------------------------------------------------------------------------\n    // [Internal Fields]\n    //-------------------------------------------------------------------------\n\n    ImGuiTestEngine*        Engine = nullptr;\n    ImGuiTestInputs*        Inputs = nullptr;\n    ImGuiTestRunFlags       RunFlags = ImGuiTestRunFlags_None;\n    ImGuiTestActiveFunc     ActiveFunc = ImGuiTestActiveFunc_None;  // None/GuiFunc/TestFunc\n    double                  RunningTime = 0.0;                      // Amount of wall clock time the Test has been running. Used by safety watchdog.\n    int                     ActionDepth = 0;                        // Nested depth of ctx-> function calls (used to decorate log)\n    int                     CaptureCounter = 0;                     // Number of captures\n    int                     ErrorCounter = 0;                       // Number of errors (generally this maxxes at 1 as most functions will early out)\n    bool                    Abort = false;\n    double                  PerfRefDt = -1.0;\n    int                     PerfIterations = 400;                   // Number of frames for PerfCapture() measurements\n    char                    RefStr[256] = { 0 };                    // Reference window/path over which all named references are based\n    ImGuiID                 RefID = 0;                              // Reference ID over which all named references are based\n    ImGuiID                 RefWindowID = 0;                        // ID of a window that contains RefID item\n    ImGuiInputSource        InputMode = ImGuiInputSource_Mouse;     // Prefer interacting with mouse/keyboard/gamepad\n    ImVector<char>          TempString;\n    ImVector<char>          Clipboard;                              // Private clipboard for the test instance\n    ImVector<ImGuiWindow*>  ForeignWindowsToHide;\n    ImGuiTestItemInfo       DummyItemInfoNull;                      // Storage for ItemInfoNull()\n    bool                    CachedLinesPrintedToTTY = false;\n\n    //-------------------------------------------------------------------------\n    // Public API\n    //-------------------------------------------------------------------------\n\n    // Main control\n    void            Finish(ImGuiTestStatus status = ImGuiTestStatus_Success);                       // Set test status and stop running. Usually called when running test logic from GuiFunc() only.\n    ImGuiTestStatus RunChildTest(const char* test_name, ImGuiTestRunFlags flags = 0);               // [Experimental] Run another test from the current test.\n    template <typename T> T& GetVars()      { IM_ASSERT(UserVars != nullptr); return *(T*)(UserVars); }// Campanion to using t->SetVarsDataType<>(). FIXME: Assert to compare sizes\n\n    // Main status queries\n    bool        IsError() const             { return TestOutput->Status == ImGuiTestStatus_Error || Abort; }\n    bool        IsWarmUpGuiFrame() const    { return FrameCount < FirstTestFrameCount; }    // Unless test->Flags has ImGuiTestFlags_NoGuiWarmUp, we run GuiFunc() twice before running TestFunc(). Those frames are called \"WarmUp\" frames.\n    bool        IsFirstGuiFrame() const     { return FirstGuiFrame; }\n    bool        IsFirstTestFrame() const    { return FrameCount == FirstTestFrameCount; }   // First frame where TestFunc is running (after warm-up frame).\n    bool        IsGuiFuncOnly() const       { return (RunFlags & ImGuiTestRunFlags_GuiFuncOnly) != 0; }\n\n    // Debugging\n    bool        SuspendTestFunc(const char* file = nullptr, int line = 0);             // [DEBUG] Generally called via IM_SUSPEND_TESTFUNC\n\n    // Logging\n    void        LogEx(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, ...) IM_FMTARGS(4);\n    void        LogExV(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, va_list args) IM_FMTLIST(4);\n    void        LogToTTY(ImGuiTestVerboseLevel level, const char* message, const char* message_end = nullptr);\n    void        LogToDebugger(ImGuiTestVerboseLevel level, const char* message);\n    void        LogDebug(const char* fmt, ...)      IM_FMTARGS(2);  // ImGuiTestVerboseLevel_Debug or ImGuiTestVerboseLevel_Trace depending on context depth\n    void        LogInfo(const char* fmt, ...)       IM_FMTARGS(2);  // ImGuiTestVerboseLevel_Info\n    void        LogWarning(const char* fmt, ...)    IM_FMTARGS(2);  // ImGuiTestVerboseLevel_Warning\n    void        LogError(const char* fmt, ...)      IM_FMTARGS(2);  // ImGuiTestVerboseLevel_Error\n    void        LogBasicUiState();\n    void        LogItemList(ImGuiTestItemList* list);\n\n    // Yield, Timing\n    void        Yield(int count = 1);\n    void        Sleep(float time_in_second);            // Sleep for a given simulation time, unless in Fast mode\n    void        SleepShort();                           // Standard short delay of io.ActionDelayShort (~0.15f), unless in Fast mode.\n    void        SleepStandard();                        // Standard regular delay of io.ActionDelayStandard (~0.40f), unless in Fast mode.\n    void        SleepNoSkip(float time_in_second, float framestep_in_second);\n\n    // Base Reference\n    // - ItemClick(\"Window/Button\")                --> click \"Window/Button\"\n    // - SetRef(\"Window\"), ItemClick(\"Button\")     --> click \"Window/Button\"\n    // - SetRef(\"Window\"), ItemClick(\"/Button\")    --> click \"Window/Button\"\n    // - SetRef(\"Window\"), ItemClick(\"//Button\")   --> click \"/Button\"\n    // - SetRef(\"//$FOCUSED\"), ItemClick(\"Button\") --> click \"Button\" in focused window.\n    // See https://github.com/ocornut/imgui_test_engine/wiki/Named-References about using ImGuiTestRef in all ImGuiTestContext functions.\n    // Note: SetRef() may take multiple frames to complete if specified ref is an item id.\n    // Note: SetRef() ignores current reference, so they are always absolute path.\n    void        SetRef(ImGuiTestRef ref);\n    void        SetRef(ImGuiWindow* window); // Shortcut to SetRef(window->Name) which works for ChildWindow (see code)\n    ImGuiTestRef GetRef();\n\n    // Windows\n    // - Use WindowInfo() to access path to child windows, since the paths are internally mangled.\n    // - SetRef(WindowInfo(\"Parent/Child\")->Window) --> set ref to child window.\n    ImGuiTestItemInfo WindowInfo(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    void        WindowClose(ImGuiTestRef window_ref);\n    void        WindowCollapse(ImGuiTestRef window_ref, bool collapsed);\n    void        WindowFocus(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    void        WindowBringToFront(ImGuiTestRef window_ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    void        WindowMove(ImGuiTestRef window_ref, ImVec2 pos, ImVec2 pivot = ImVec2(0.0f, 0.0f), ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    void        WindowResize(ImGuiTestRef window_ref, ImVec2 sz);\n    bool        WindowTeleportToMakePosVisible(ImGuiTestRef window_ref, ImVec2 pos_in_window);\n    ImGuiWindow*GetWindowByRef(ImGuiTestRef window_ref);\n\n    // Popups\n    void        PopupCloseOne();\n    void        PopupCloseAll();\n    ImGuiID     PopupGetWindowID(ImGuiTestRef ref);\n\n    // Get hash for a decorated ID Path.\n    // Note: for windows you may use WindowInfo()\n    ImGuiID     GetID(ImGuiTestRef ref);\n    ImGuiID     GetID(ImGuiTestRef ref, ImGuiTestRef seed_ref);\n\n    // Miscellaneous helpers\n    ImVec2      GetPosOnVoid(ImGuiViewport* viewport);                              // Find a point that has no windows // FIXME: This needs error return and flag to enable/disable forcefully finding void.\n    ImVec2      GetWindowTitlebarPoint(ImGuiTestRef window_ref);                    // Return a clickable point on window title-bar (window tab for docked windows).\n    ImVec2      GetMainMonitorWorkPos();                                            // Work pos and size of main viewport when viewports are disabled, or work pos and size of monitor containing main viewport when viewports are enabled.\n    ImVec2      GetMainMonitorWorkSize();\n\n    // Screenshot/Video Captures\n    void        CaptureReset();                                                     // Reset state (use when doing multiple captures)\n    void        CaptureSetExtension(const char* ext);                               // Set capture file format (otherwise for video this default to EngineIO->VideoCaptureExtension)\n    bool        CaptureAddWindow(ImGuiTestRef ref);                                 // Add window to be captured (default to capture everything)\n    void        CaptureScreenshotWindow(ImGuiTestRef ref, int capture_flags = 0);   // Trigger a screen capture of a single window (== CaptureAddWindow() + CaptureScreenshot())\n    bool        CaptureScreenshot(int capture_flags = 0);                           // Trigger a screen capture\n    bool        CaptureBeginVideo();                                                // Start a video capture\n    bool        CaptureEndVideo();\n\n    // Mouse inputs\n    void        MouseMove(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    void        MouseMoveToPos(ImVec2 pos);\n    void        MouseTeleportToPos(ImVec2 pos, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    void        MouseClick(ImGuiMouseButton button = 0);\n    void        MouseClickMulti(ImGuiMouseButton button, int count);\n    void        MouseDoubleClick(ImGuiMouseButton button = 0);\n    void        MouseDown(ImGuiMouseButton button = 0);\n    void        MouseUp(ImGuiMouseButton button = 0);\n    void        MouseLiftDragThreshold(ImGuiMouseButton button = 0);\n    void        MouseDragWithDelta(ImVec2 delta, ImGuiMouseButton button = 0);\n    void        MouseWheel(ImVec2 delta);\n    void        MouseWheelX(float dx) { MouseWheel(ImVec2(dx, 0.0f)); }\n    void        MouseWheelY(float dy) { MouseWheel(ImVec2(0.0f, dy)); }\n    void        MouseMoveToVoid(ImGuiViewport* viewport = nullptr);\n    void        MouseClickOnVoid(ImGuiMouseButton button = 0, ImGuiViewport* viewport = nullptr);\n    ImGuiWindow*FindHoveredWindowAtPos(const ImVec2& pos);\n    bool        FindExistingVoidPosOnViewport(ImGuiViewport* viewport, ImVec2* out);\n\n    // Mouse inputs: Viewports\n    // - This is automatically called by SetRef() and any mouse action taking an item reference (e.g. ItemClick(\"button\"), MouseClick(\"button\"))\n    // - But when using raw position directy e.g. MouseMoveToPos() / MouseTeleportToPos() without referring to the parent window before, this needs to be set.\n    void        MouseSetViewport(ImGuiWindow* window);\n    void        MouseSetViewportID(ImGuiID viewport_id);\n\n    // Keyboard inputs\n    void        KeyDown(ImGuiKeyChord key_chord);\n    void        KeyUp(ImGuiKeyChord key_chord);\n    void        KeyPress(ImGuiKeyChord key_chord, int count = 1);\n    void        KeyHold(ImGuiKeyChord key_chord, float time);\n    void        KeySetEx(ImGuiKeyChord key_chord, bool is_down, float time);\n    void        KeyChars(const char* chars);                // Input characters\n    void        KeyCharsAppend(const char* chars);          // Input characters at end of field\n    void        KeyCharsAppendEnter(const char* chars);     // Input characters at end of field, press Enter\n    void        KeyCharsReplace(const char* chars);         // Delete existing field then input characters\n    void        KeyCharsReplaceEnter(const char* chars);    // Delete existing field then input characters, press Enter\n\n    // Navigation inputs\n    // FIXME: Need some redesign/refactoring:\n    // - This was initially intended to: replace mouse action with keyboard/gamepad\n    // - Abstract keyboard vs gamepad actions\n    // However this is widely inconsistent and unfinished at this point.\n    void        SetInputMode(ImGuiInputSource input_mode);  // Mouse or Keyboard or Gamepad. In Keyboard or Gamepad mode, actions such as ItemClick or ItemInput are using nav facilities instead of Mouse.\n    void        NavMoveTo(ImGuiTestRef ref);\n    void        NavActivate();                              // Activate current selected item: activate button, tweak sliders/drags. Equivalent of pressing Space on keyboard, ImGuiKey_GamepadFaceUp on a gamepad.\n    void        NavInput();                                 // Input into select item: input sliders/drags. Equivalent of pressing Enter on keyboard, ImGuiKey_GamepadFaceDown on a gamepad.\n\n    // Scrolling\n    void        ScrollTo(ImGuiTestRef ref, ImGuiAxis axis, float scroll_v, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    void        ScrollToX(ImGuiTestRef ref, float scroll_x) { ScrollTo(ref, ImGuiAxis_X, scroll_x); }\n    void        ScrollToY(ImGuiTestRef ref, float scroll_y) { ScrollTo(ref, ImGuiAxis_Y, scroll_y); }\n    void        ScrollToTop(ImGuiTestRef ref);\n    void        ScrollToBottom(ImGuiTestRef ref);\n    void        ScrollToPos(ImGuiTestRef window_ref, float pos_v, ImGuiAxis axis, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    void        ScrollToPosX(ImGuiTestRef window_ref, float pos_x);\n    void        ScrollToPosY(ImGuiTestRef window_ref, float pos_y);\n    void        ScrollToItem(ImGuiTestRef ref, ImGuiAxis axis, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    void        ScrollToItemX(ImGuiTestRef ref);\n    void        ScrollToItemY(ImGuiTestRef ref);\n    void        ScrollToTabItem(ImGuiTabBar* tab_bar, ImGuiID tab_id);\n    bool        ScrollErrorCheck(ImGuiAxis axis, float expected, float actual, int* remaining_attempts);\n    void        ScrollVerifyScrollMax(ImGuiTestRef ref);\n\n    // Low-level queries\n    // - ItemInfo queries never returns nullptr! Instead they return an empty instance (info->IsEmpty(), info->ID == 0) and set contexted as errored.\n    // - You can use ImGuiTestOpFlags_NoError to do a query without marking context as errored. This is what ItemExists() does.\n    ImGuiTestItemInfo   ItemInfo(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    ImGuiTestItemInfo   ItemInfoOpenFullPath(ImGuiTestRef ref, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    ImGuiID             ItemInfoHandleWildcardSearch(const char* wildcard_prefix_start, const char* wildcard_prefix_end, const char* wildcard_suffix_start);\n    ImGuiTestItemInfo   ItemInfoNull() { return ImGuiTestItemInfo(); }\n    void                GatherItems(ImGuiTestItemList* out_list, ImGuiTestRef parent, int depth = -1);\n\n    // Item/Widgets manipulation\n    void        ItemAction(ImGuiTestAction action, ImGuiTestRef ref, ImGuiTestOpFlags flags = 0, void* action_arg = nullptr);\n    void        ItemClick(ImGuiTestRef ref, ImGuiMouseButton button = 0, ImGuiTestOpFlags flags = 0) { ItemAction(ImGuiTestAction_Click, ref, flags, (void*)(size_t)button); }\n    void        ItemDoubleClick(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0)           { ItemAction(ImGuiTestAction_DoubleClick, ref, flags); }\n    void        ItemCheck(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0)                 { ItemAction(ImGuiTestAction_Check, ref, flags); }\n    void        ItemUncheck(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0)               { ItemAction(ImGuiTestAction_Uncheck, ref, flags); }\n    void        ItemOpen(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0)                  { ItemAction(ImGuiTestAction_Open, ref, flags); }\n    void        ItemClose(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0)                 { ItemAction(ImGuiTestAction_Close, ref, flags); }\n    void        ItemInput(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0)                 { ItemAction(ImGuiTestAction_Input, ref, flags); }\n    void        ItemNavActivate(ImGuiTestRef ref, ImGuiTestOpFlags flags = 0)           { ItemAction(ImGuiTestAction_NavActivate, ref, flags); }\n\n    // Item/Widgets: Batch actions over an entire scope\n    void        ItemActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent, const ImGuiTestActionFilter* filter = nullptr);\n    void        ItemOpenAll(ImGuiTestRef ref_parent, int depth = -1, int passes = -1);\n    void        ItemCloseAll(ImGuiTestRef ref_parent, int depth = -1, int passes = -1);\n\n    // Item/Widgets: Helpers to easily set a value\n    void        ItemInputValue(ImGuiTestRef ref, int v);\n    void        ItemInputValue(ImGuiTestRef ref, float f);\n    void        ItemInputValue(ImGuiTestRef ref, const char* str);\n\n    // Item/Widgets: Helpers to easily read a value by selecting Slider/Drag/Input text, copying it and parsing it.\n    // - This requires the item to be selectable (we will later provide helpers that works in more general manner)\n    // - (this temporarily use the internal test clipboard, but original clipboard value is restored afterwards)\n    // See https://github.com/ocornut/imgui_test_engine/wiki/Automation-API#accessing-your-data\n    int         ItemReadAsInt(ImGuiTestRef ref);\n    float       ItemReadAsFloat(ImGuiTestRef ref);\n    bool        ItemReadAsScalar(ImGuiTestRef ref, ImGuiDataType data_type, void* out_data, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None);\n    const char* ItemReadAsString(ImGuiTestRef ref);\n    size_t      ItemReadAsString(ImGuiTestRef ref, char* out_buf, size_t out_buf_size);\n\n    // Item/Widgets: Status query\n    bool        ItemExists(ImGuiTestRef ref);\n    bool        ItemIsChecked(ImGuiTestRef ref);\n    bool        ItemIsOpened(ImGuiTestRef ref);\n    void        ItemVerifyCheckedIfAlive(ImGuiTestRef ref, bool checked);\n\n    // Item/Widgets: Drag and Mouse operations\n    void        ItemHold(ImGuiTestRef ref, float time);\n    void        ItemHoldForFrames(ImGuiTestRef ref, int frames);\n    void        ItemDragOverAndHold(ImGuiTestRef ref_src, ImGuiTestRef ref_dst);\n    void        ItemDragAndDrop(ImGuiTestRef ref_src, ImGuiTestRef ref_dst, ImGuiMouseButton button = 0);\n    void        ItemDragWithDelta(ImGuiTestRef ref_src, ImVec2 pos_delta);\n\n    // Helpers for Tab Bars widgets\n    void        TabClose(ImGuiTestRef ref);\n    bool        TabBarCompareOrder(ImGuiTabBar* tab_bar, const char** tab_order);\n\n    // Helpers for MenuBar and Menus widgets\n    // - e.g. MenuCheck(\"File/Options/Enable grid\"); // Access menu in current ref window.\n    // - e.g. MenuClick(\"//Window/File/Quit\");       // Access menu in another window.\n    void        MenuAction(ImGuiTestAction action, ImGuiTestRef ref);\n    void        MenuActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent);\n    void        MenuClick(ImGuiTestRef ref)                 { MenuAction(ImGuiTestAction_Click, ref); }\n    void        MenuCheck(ImGuiTestRef ref)                 { MenuAction(ImGuiTestAction_Check, ref); }\n    void        MenuUncheck(ImGuiTestRef ref)               { MenuAction(ImGuiTestAction_Uncheck, ref); }\n    void        MenuCheckAll(ImGuiTestRef ref_parent)       { MenuActionAll(ImGuiTestAction_Check, ref_parent); }\n    void        MenuUncheckAll(ImGuiTestRef ref_parent)     { MenuActionAll(ImGuiTestAction_Uncheck, ref_parent); }\n\n    // Helpers for Combo Boxes\n    void        ComboClick(ImGuiTestRef ref);\n    void        ComboClickAll(ImGuiTestRef ref);\n\n    // Helpers for Tables\n    void                        TableOpenContextMenu(ImGuiTestRef ref, int column_n = -1);\n    ImGuiSortDirection          TableClickHeader(ImGuiTestRef ref, const char* label, ImGuiKeyChord key_mods = 0);\n    void                        TableSetColumnEnabled(ImGuiTestRef ref, const char* label, bool enabled);\n    void                        TableResizeColumn(ImGuiTestRef ref, int column_n, float width);\n    const ImGuiTableSortSpecs*  TableGetSortSpecs(ImGuiTestRef ref);\n\n    // Viewports\n    // IMPORTANT: Those function may alter Platform state (unless using the \"Mock Viewport\" backend). Use carefully.\n    // Those are mostly useful to simulate OS actions and testing of viewport-specific features, may not be useful to most users.\n#ifdef IMGUI_HAS_VIEWPORT\n    void        ViewportPlatform_SetWindowPos(ImGuiViewport* viewport, const ImVec2& pos);\n    void        ViewportPlatform_SetWindowSize(ImGuiViewport* viewport, const ImVec2& size);\n    void        ViewportPlatform_SetWindowFocus(ImGuiViewport* viewport);\n    void        ViewportPlatform_CloseWindow(ImGuiViewport* viewport);\n#endif\n\n    // Docking\n#ifdef IMGUI_HAS_DOCK\n    void        DockClear(const char* window_name, ...);\n    void        DockInto(ImGuiTestRef src_id, ImGuiTestRef dst_id, ImGuiDir split_dir = ImGuiDir_None, bool is_outer_docking = false, ImGuiTestOpFlags flags = 0);\n    void        UndockNode(ImGuiID dock_id);\n    void        UndockWindow(const char* window_name);\n    bool        WindowIsUndockedOrStandalone(ImGuiWindow* window);\n    bool        DockIdIsUndockedOrStandalone(ImGuiID dock_id);\n    void        DockNodeHideTabBar(ImGuiDockNode* node, bool hidden);\n#endif\n\n    // Performances Measurement (use along with Dear ImGui Perf Tool)\n    void        PerfCalcRef();\n    void        PerfCapture(const char* category = nullptr, const char* test_name = nullptr, const char* csv_file = nullptr);\n\n    // Obsolete functions\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    // Obsoleted 2025/01/20\n    bool        ItemSelectAndReadValue(ImGuiTestRef ref, ImGuiDataType data_type, void* out_data, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None) { return ItemReadAsScalar(ref, data_type, out_data, flags); }\n    void        ItemSelectAndReadValue(ImGuiTestRef ref, int* out_v) { int v = ItemReadAsInt(ref); *out_v = v; }\n    void        ItemSelectAndReadValue(ImGuiTestRef ref, float* out_v) { float v = ItemReadAsFloat(ref); *out_v = v; }\n    // Obsoleted 2024/05/21\n    void        YieldUntil(int frame_count)     { while (FrameCount < frame_count) { Yield(); } }\n    // Obsoleted 2022/10/11\n    ImGuiID     GetIDByInt(int n);                                      // Prefer using \"$$123\"\n    ImGuiID     GetIDByInt(int n, ImGuiTestRef seed_ref);\n    ImGuiID     GetIDByPtr(void* p);                                    // Prefer using \"$$(ptr)0xFFFFFFFF\"\n    ImGuiID     GetIDByPtr(void* p, ImGuiTestRef seed_ref);\n    // Obsoleted 2022/09/26\n    //void      KeyModDown(ImGuiModFlags mods)  { KeyDown(mods); }\n    //void      KeyModUp(ImGuiModFlags mods)    { KeyUp(mods); }\n    //void      KeyModPress(ImGuiModFlags mods) { KeyPress(mods); }\n#endif\n\n    // [Internal]\n    void        _MakeAimingSpaceOverPos(ImGuiViewport* viewport, ImGuiWindow* over_window, const ImVec2& over_pos); // Move windows covering 'window' at pos.\n    void        _ForeignWindowsHideOverPos(const ImVec2& pos, ImGuiWindow** ignore_list);  // FIXME: Aim to remove this system...\n    void        _ForeignWindowsUnhideAll();                                                // FIXME: Aim to remove this system...\n};\n\n//-------------------------------------------------------------------------\n// [SECTION] Debugging macros (IM_SUSPEND_TESTFUNC)\n//-------------------------------------------------------------------------\n\n// Debug: Temporarily suspend TestFunc to let user interactively inspect the GUI state (user will need to press the \"Continue\" button to resume TestFunc execution)\n#define IM_SUSPEND_TESTFUNC()               do { if (ctx->SuspendTestFunc(__FILE__, __LINE__)) return; } while (0)\n\n//-------------------------------------------------------------------------\n// [SECTION] Testing/Checking macros: IM_CHECK(), IM_ERRORF() etc.\n//-------------------------------------------------------------------------\n\n// Helpers used by IM_CHECK_OP() macros.\n// ImGuiTestEngine_GetTempStringBuilder() returns a shared instance of ImGuiTextBuffer to recycle memory allocations\ntemplate<typename T> void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, T v) { buf->append(\"???\"); IM_UNUSED(v); } // FIXME-TESTS: Could improve with some template magic\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, const char* v) { buf->appendf(\"\\\"%s\\\"\", v); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, bool v)        { buf->append(v ? \"true\" : \"false\"); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS8 v)        { buf->appendf(\"%d\", v); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU8 v)        { buf->appendf(\"%u\", v); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS16 v)       { buf->appendf(\"%hd\", v); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU16 v)       { buf->appendf(\"%hu\", v); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS32 v)       { buf->appendf(\"%d\", v); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU32 v)       { buf->appendf(\"0x%08X\", v); } // Assuming ImGuiID\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImS64 v)       { buf->appendf(\"%lld\", v); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImU64 v)       { buf->appendf(\"%llu\", v); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, float v)       { buf->appendf(\"%.3f\", v); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, double v)      { buf->appendf(\"%f\", v); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImVec2 v)      { buf->appendf(\"(%.3f, %.3f)\", v.x, v.y); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, const void* v) { buf->appendf(\"%p\", v); }\ntemplate<> inline void ImGuiTestEngineUtil_appendf_auto(ImGuiTextBuffer* buf, ImGuiWindow* v){ if (v) buf->appendf(\"\\\"%s\\\"\", v->Name); else buf->append(\"nullptr\"); }\n\n// We embed every macro in a do {} while(0) statement as a trick to allow using them as regular single statement, e.g. if (XXX) IM_CHECK(A); else IM_CHECK(B)\n// We leave the IM_DEBUG_BREAK() outside of the check function to step out faster when using a debugger. It also has the benefit of being lighter than an IM_ASSERT().\n#define IM_CHECK(_EXPR)                     do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR))          { IM_DEBUG_BREAK(); } if (!res) return; } while (0)\n#define IM_CHECK_NO_RET(_EXPR)              do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR))          { IM_DEBUG_BREAK(); } } while (0)\n#define IM_CHECK_SILENT(_EXPR)              do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_SilentSuccess, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return; } while (0)\n#define IM_CHECK_RETV(_EXPR,_RETV)          do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, res, #_EXPR))          { IM_DEBUG_BREAK(); } if (!res) return _RETV; } while (0)\n#define IM_CHECK_SILENT_RETV(_EXPR,_RETV)   do { bool res = (bool)(_EXPR); if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_SilentSuccess, res, #_EXPR)) { IM_DEBUG_BREAK(); } if (!res) return _RETV; } while (0)\n#define IM_ERRORF(_FMT,...)                 do { if (ImGuiTestEngine_Error(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, _FMT, __VA_ARGS__))                              { IM_DEBUG_BREAK(); } } while (0)\n#define IM_ERRORF_NOHDR(_FMT,...)           do { if (ImGuiTestEngine_Error(nullptr, nullptr, 0, ImGuiTestCheckFlags_None, _FMT, __VA_ARGS__))                                             { IM_DEBUG_BREAK(); } } while (0)\n\n// Those macros allow us to print out the values of both LHS and RHS expressions involved in a check.\n#define IM_CHECK_OP(_LHS, _RHS, _OP, _RETURN)                   \\\n    do                                                          \\\n    {                                                           \\\n        auto __lhs = _LHS;  /* Cache to avoid side effects */   \\\n        auto __rhs = _RHS;                                      \\\n        bool __res = __lhs _OP __rhs;                           \\\n        ImGuiTextBuffer* expr_buf = ImGuiTestEngine_GetTempStringBuilder(); \\\n        expr_buf->append(#_LHS \" [\");                           \\\n        ImGuiTestEngineUtil_appendf_auto(expr_buf, __lhs);      \\\n        expr_buf->append(\"] \" #_OP \" \" #_RHS \" [\");             \\\n        ImGuiTestEngineUtil_appendf_auto(expr_buf, __rhs);      \\\n        expr_buf->append(\"]\");                                  \\\n        if (ImGuiTestEngine_Check(__FILE__, __func__, __LINE__, ImGuiTestCheckFlags_None, __res, expr_buf->c_str())) \\\n            IM_ASSERT(__res);                                   \\\n        if (_RETURN && !__res)                                  \\\n            return;                                             \\\n    } while (0)\n\n#define IM_CHECK_STR_OP(_LHS, _RHS, _OP, _RETURN, _FLAGS)       \\\n    do                                                          \\\n    {                                                           \\\n        bool __res;                                             \\\n        if (ImGuiTestEngine_CheckStrOp(__FILE__, __func__, __LINE__, _FLAGS, #_OP, #_LHS, _LHS, #_RHS, _RHS, &__res)) \\\n            IM_ASSERT(__res);                                   \\\n        if (_RETURN && !__res)                                  \\\n            return;                                             \\\n    } while (0)\n\n// Scalar compares\n#define IM_CHECK_EQ(_LHS, _RHS)                     IM_CHECK_OP(_LHS, _RHS, ==, true)   // Equal\n#define IM_CHECK_NE(_LHS, _RHS)                     IM_CHECK_OP(_LHS, _RHS, !=, true)   // Not Equal\n#define IM_CHECK_LT(_LHS, _RHS)                     IM_CHECK_OP(_LHS, _RHS, < , true)   // Less Than\n#define IM_CHECK_LE(_LHS, _RHS)                     IM_CHECK_OP(_LHS, _RHS, <=, true)   // Less or Equal\n#define IM_CHECK_GT(_LHS, _RHS)                     IM_CHECK_OP(_LHS, _RHS, > , true)   // Greater Than\n#define IM_CHECK_GE(_LHS, _RHS)                     IM_CHECK_OP(_LHS, _RHS, >=, true)   // Greater or Equal\n\n// Scalar compares, without return on failure\n#define IM_CHECK_EQ_NO_RET(_LHS, _RHS)              IM_CHECK_OP(_LHS, _RHS, ==, false)  // Equal\n#define IM_CHECK_NE_NO_RET(_LHS, _RHS)              IM_CHECK_OP(_LHS, _RHS, !=, false)  // Not Equal\n#define IM_CHECK_LT_NO_RET(_LHS, _RHS)              IM_CHECK_OP(_LHS, _RHS, < , false)  // Less Than\n#define IM_CHECK_LE_NO_RET(_LHS, _RHS)              IM_CHECK_OP(_LHS, _RHS, <=, false)  // Less or Equal\n#define IM_CHECK_GT_NO_RET(_LHS, _RHS)              IM_CHECK_OP(_LHS, _RHS, > , false)  // Greater Than\n#define IM_CHECK_GE_NO_RET(_LHS, _RHS)              IM_CHECK_OP(_LHS, _RHS, >=, false)  // Greater or Equal\n\n// String compares\n#define IM_CHECK_STR_EQ(_LHS, _RHS)                 IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_None)\n#define IM_CHECK_STR_NE(_LHS, _RHS)                 IM_CHECK_STR_OP(_LHS, _RHS, !=, true, ImGuiTestCheckFlags_None)\n#define IM_CHECK_STR_EQ_NO_RET(_LHS, _RHS)          IM_CHECK_STR_OP(_LHS, _RHS, ==, false, ImGuiTestCheckFlags_None)\n#define IM_CHECK_STR_NE_NO_RET(_LHS, _RHS)          IM_CHECK_STR_OP(_LHS, _RHS, !=, false, ImGuiTestCheckFlags_None)\n#define IM_CHECK_STR_EQ_SILENT(_LHS, _RHS)          IM_CHECK_STR_OP(_LHS, _RHS, ==, true, ImGuiTestCheckFlags_SilentSuccess)\n\n// Floating point compares\n#define IM_CHECK_FLOAT_EQ_EPS(_LHS, _RHS)           IM_CHECK_LE(ImFabs(_LHS - (_RHS)), FLT_EPSILON)   // Float Equal\n#define IM_CHECK_FLOAT_NE_EPS(_LHS, _RHS)           IM_CHECK_GT(ImFabs(_LHS - (_RHS)), FLT_EPSILON)   // Float Not Equal\n#define IM_CHECK_FLOAT_NEAR(_LHS, _RHS, _EPS)       IM_CHECK_LE(ImFabs(_LHS - (_RHS)), _EPS)\n#define IM_CHECK_FLOAT_NEAR_NO_RET(_LHS, _RHS, _E)  IM_CHECK_LE_NO_RET(ImFabs(_LHS - (_RHS)), _E)\n\n//-------------------------------------------------------------------------\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#elif defined(__GNUC__)\n#pragma GCC diagnostic pop\n#endif\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/imgui_te_coroutine.h",
    "content": "// dear imgui test engine\n// (coroutine interface + optional implementation)\n// Read https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#pragma once\n\n#ifndef IMGUI_VERSION\n#include \"imgui.h\"\n#endif\n\n//------------------------------------------------------------------------\n// Coroutine abstraction\n//------------------------------------------------------------------------\n// Coroutines should be used like this:\n//   ImGuiTestCoroutineHandle handle = CoroutineCreate(<func>, <name>, <ctx>); // name being for debugging, and ctx being an arbitrary user context pointer\n//   while (CoroutineRun(handle)) { <do other stuff };\n//   CoroutineDestroy(handle);\n// The coroutine code itself should call CoroutineYieldFunc() whenever it wants to yield control back to the main thread.\n//------------------------------------------------------------------------\n\n// An arbitrary handle used internally to represent coroutines (nullptr indicates no handle)\ntypedef void* ImGuiTestCoroutineHandle;\n\n// A coroutine main function\ntypedef void (ImGuiTestCoroutineMainFunc)(void* data);\n\n// Coroutine support interface\n// Your app needs to return and implement this.\n// You can '#define IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL 1' in your imconfig file to use a default implementation using std::thread\n// Documentation: https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up\nstruct IMGUI_API ImGuiTestCoroutineInterface\n{\n    // Create a new coroutine\n    ImGuiTestCoroutineHandle (*CreateFunc)(ImGuiTestCoroutineMainFunc* func, const char* name, void* data);\n\n    // Destroy a coroutine (which must have completed first)\n    void                     (*DestroyFunc)(ImGuiTestCoroutineHandle handle);\n\n    // Run a coroutine until it yields or finishes, returning false if finished\n    bool                     (*RunFunc)(ImGuiTestCoroutineHandle handle);\n\n    // Yield from a coroutine back to the caller, preserving coroutine state\n    void                     (*YieldFunc)();\n};\n\n//------------------------------------------------------------------------\n// Coroutine implementation using std::thread\n// The \"coroutine\" thread and user's main thread will always block on each other (both threads will NEVER run in parallel)\n// It is just an implementation convenience that we provide an implementation using std::thread as it is widely available/standard.\n//------------------------------------------------------------------------\n\n#if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL\n\nIMGUI_API ImGuiTestCoroutineInterface*    Coroutine_ImplStdThread_GetInterface();\n\n#endif // #if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/imgui_te_engine.h",
    "content": "// dear imgui test engine\n// (core)\n// This is the interface that your initial setup (app init, main loop) will mostly be using.\n// Actual tests will mostly use the interface of imgui_te_context.h\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#pragma once\n\n#include \"imgui.h\"\n#include \"imgui_internal.h\"         // ImPool<>, ImRect, ImGuiItemStatusFlags, ImFormatString\n\n#if defined(__clang__)\n#pragma clang diagnostic push\n#elif defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#endif\n#ifdef Status // X11 headers\n#undef Status\n#endif\n\n//-----------------------------------------------------------------------------\n// Function Pointers\n//-----------------------------------------------------------------------------\n\n#if IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION\n#include <functional>\n#define ImFuncPtr(FUNC_TYPE)        std::function<FUNC_TYPE>\n#else\n#define ImFuncPtr(FUNC_TYPE)        FUNC_TYPE*\n#endif\n\n#include \"imgui_capture_tool.h\"     // ImGuiScreenCaptureFunc\n\n//-------------------------------------------------------------------------\n// Forward Declarations\n//-------------------------------------------------------------------------\n\nstruct ImGuiTest;                   // Data for a test registered with IM_REGISTER_TEST()\nstruct ImGuiTestContext;            // Context while a test is running\nstruct ImGuiTestCoroutineInterface; // Interface to expose coroutine functions (imgui_te_coroutine provides a default implementation for C++11 using std::thread, but you may use your own)\nstruct ImGuiTestEngine;             // Test engine instance\nstruct ImGuiTestEngineIO;           // Test engine public I/O\nstruct ImGuiTestEngineResultSummary;// Output of ImGuiTestEngine_GetResultSummary()\nstruct ImGuiTestItemInfo;           // Info queried from item (id, geometry, status flags, debug label)\nstruct ImGuiTestItemList;           // A list of items\nstruct ImGuiTestInputs;             // Simulated user inputs (will be fed into ImGuiIO by the test engine)\nstruct ImGuiTestRunTask;            // A queued test (test + runflags)\n\ntypedef int ImGuiTestFlags;         // Flags: See ImGuiTestFlags_\ntypedef int ImGuiTestCheckFlags;    // Flags: See ImGuiTestCheckFlags_\ntypedef int ImGuiTestLogFlags;      // Flags: See ImGuiTestLogFlags_\ntypedef int ImGuiTestRunFlags;      // Flags: See ImGuiTestRunFlags_\n\nenum ImGuiTestActiveFunc : int;\nenum ImGuiTestGroup : int;\nenum ImGuiTestRunSpeed : int;\nenum ImGuiTestStatus : int;\nenum ImGuiTestVerboseLevel : int;\nenum ImGuiTestEngineExportFormat : int;\n\n//-------------------------------------------------------------------------\n// Types\n//-------------------------------------------------------------------------\n\n// Stored in ImGuiTestContext: where we are currently running GuiFunc or TestFunc\nenum ImGuiTestActiveFunc : int\n{\n    ImGuiTestActiveFunc_None,\n    ImGuiTestActiveFunc_GuiFunc,\n    ImGuiTestActiveFunc_TestFunc\n};\n\nenum ImGuiTestRunSpeed : int\n{\n    ImGuiTestRunSpeed_Fast          = 0,    // Run tests as fast as possible (teleport mouse, skip delays, etc.)\n    ImGuiTestRunSpeed_Normal        = 1,    // Run tests at human watchable speed (for debugging)\n    ImGuiTestRunSpeed_Cinematic     = 2,    // Run tests with pauses between actions (for e.g. tutorials)\n    ImGuiTestRunSpeed_COUNT\n};\n\nenum ImGuiTestVerboseLevel : int\n{\n    ImGuiTestVerboseLevel_Silent    = 0,    // -v0\n    ImGuiTestVerboseLevel_Error     = 1,    // -v1\n    ImGuiTestVerboseLevel_Warning   = 2,    // -v2\n    ImGuiTestVerboseLevel_Info      = 3,    // -v3\n    ImGuiTestVerboseLevel_Debug     = 4,    // -v4\n    ImGuiTestVerboseLevel_Trace     = 5,\n    ImGuiTestVerboseLevel_COUNT\n};\n\n// Test status (stored in ImGuiTest)\nenum ImGuiTestStatus : int\n{\n    ImGuiTestStatus_Unknown     = 0,\n    ImGuiTestStatus_Success     = 1,\n    ImGuiTestStatus_Queued      = 2,\n    ImGuiTestStatus_Running     = 3,\n    ImGuiTestStatus_Error       = 4,\n    ImGuiTestStatus_Suspended   = 5,\n    ImGuiTestStatus_COUNT\n};\n\n// Test group: this is mostly used to categorize tests in our testing UI. (Stored in ImGuiTest)\nenum ImGuiTestGroup : int\n{\n    ImGuiTestGroup_Unknown      = -1,\n    ImGuiTestGroup_Tests        = 0,\n    ImGuiTestGroup_Perfs        = 1,\n    ImGuiTestGroup_COUNT\n};\n\n// Flags (stored in ImGuiTest)\nenum ImGuiTestFlags_\n{\n    ImGuiTestFlags_None                 = 0,\n    ImGuiTestFlags_NoGuiWarmUp          = 1 << 0,   // Disable running the GUI func for 2 frames before starting test code. For tests which absolutely need to start before GuiFunc.\n    ImGuiTestFlags_NoAutoFinish         = 1 << 1,   // By default, tests with no TestFunc (only a GuiFunc) will end after warmup. Setting this require test to call ctx->Finish().\n    ImGuiTestFlags_NoRecoveryWarnings   = 1 << 2    // Error/recovery warnings (missing End/Pop calls etc.) will be displayed as normal debug entries, for tests which may rely on those.\n    //ImGuiTestFlags_RequireViewports   = 1 << 10\n};\n\n// Flags for IM_CHECK* macros.\nenum ImGuiTestCheckFlags_\n{\n    ImGuiTestCheckFlags_None            = 0,\n    ImGuiTestCheckFlags_SilentSuccess   = 1 << 0\n};\n\n// Flags for ImGuiTestContext::Log* functions.\nenum ImGuiTestLogFlags_\n{\n    ImGuiTestLogFlags_None              = 0,\n    ImGuiTestLogFlags_NoHeader          = 1 << 0    // Do not display frame count and depth padding\n};\n\nenum ImGuiTestRunFlags_\n{\n    ImGuiTestRunFlags_None              = 0,\n    ImGuiTestRunFlags_GuiFuncDisable    = 1 << 0,   // Used internally to temporarily disable the GUI func (at the end of a test, etc)\n    ImGuiTestRunFlags_GuiFuncOnly       = 1 << 1,   // Set when user selects \"Run GUI func\"\n    ImGuiTestRunFlags_NoSuccessMsg      = 1 << 2,\n    ImGuiTestRunFlags_EnableRawInputs   = 1 << 3,   // Disable input submission to let test submission raw input event (in order to test e.g. IO queue)\n    ImGuiTestRunFlags_RunFromGui        = 1 << 4,   // Test ran manually from GUI, will disable watchdog.\n    ImGuiTestRunFlags_RunFromCommandLine= 1 << 5,   // Test queued from command-line.\n\n    // Flags for ImGuiTestContext::RunChildTest()\n    ImGuiTestRunFlags_NoError           = 1 << 10,\n    ImGuiTestRunFlags_ShareVars         = 1 << 11,  // Share generic vars and custom vars between child and parent tests (custom vars need to be same type)\n    ImGuiTestRunFlags_ShareTestContext  = 1 << 12,  // Share ImGuiTestContext instead of creating a new one (unsure what purpose this may be useful for yet)\n    // TODO: Add GuiFunc options\n};\n\nstruct ImGuiTestEngineResultSummary\n{\n    int     CountTested = 0;    // Number of tests executed\n    int     CountSuccess = 0;   // Number of tests succeeded\n    int     CountInQueue = 0;   // Number of tests remaining in queue (e.g. aborted, crashed)\n};\n\n//-------------------------------------------------------------------------\n// Functions\n//-------------------------------------------------------------------------\n\n// Hooks for core imgui/ library (generally called via macros)\nextern void         ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data);\n#if IMGUI_VERSION_NUM < 18934\nextern void         ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, const ImRect& bb, ImGuiID id);\n#endif\n#ifdef IMGUI_HAS_IMSTR\nextern void         ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, ImStrv label, ImGuiItemStatusFlags flags);\n#else\nextern void         ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags);\n#endif\nextern void         ImGuiTestEngineHook_Log(ImGuiContext* ui_ctx, const char* fmt, ...);\nextern const char*  ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ui_ctx, ImGuiID id);\n\n// Functions (generally called via IM_CHECK() macros)\nIMGUI_API bool      ImGuiTestEngine_Check(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, bool result, const char* expr);\nIMGUI_API bool      ImGuiTestEngine_CheckStrOp(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* op, const char* lhs_var, const char* lhs_value, const char* rhs_var, const char* rhs_value, bool* out_result);\nIMGUI_API bool      ImGuiTestEngine_Error(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* fmt, ...);\nIMGUI_API void      ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* function, int line);\nIMGUI_API ImGuiTextBuffer* ImGuiTestEngine_GetTempStringBuilder();\n\n//-------------------------------------------------------------------------\n// ImGuiTestEngine API\n//-------------------------------------------------------------------------\n\n// Functions: Initialization\nIMGUI_API ImGuiTestEngine*    ImGuiTestEngine_CreateContext();                                      // Create test engine\nIMGUI_API void                ImGuiTestEngine_DestroyContext(ImGuiTestEngine* engine);              // Destroy test engine. Call after ImGui::DestroyContext() so test engine specific ini data gets saved.\nIMGUI_API void                ImGuiTestEngine_Start(ImGuiTestEngine* engine, ImGuiContext* ui_ctx); // Bind to a dear imgui context. Start coroutine.\nIMGUI_API void                ImGuiTestEngine_Stop(ImGuiTestEngine* engine);                        // Stop coroutine and export if any. (Unbind will lazily happen on context shutdown)\nIMGUI_API void                ImGuiTestEngine_PostSwap(ImGuiTestEngine* engine);                    // Call every frame after framebuffer swap, will process screen capture and call test_io.ScreenCaptureFunc()\nIMGUI_API ImGuiTestEngineIO&  ImGuiTestEngine_GetIO(ImGuiTestEngine* engine);\n\n// Macros: Register Test\n#define IM_REGISTER_TEST(_ENGINE, _CATEGORY, _NAME)  ImGuiTestEngine_RegisterTest(_ENGINE, _CATEGORY, _NAME, __FILE__, __LINE__)\nIMGUI_API ImGuiTest*          ImGuiTestEngine_RegisterTest(ImGuiTestEngine* engine, const char* category, const char* name, const char* src_file = nullptr, int src_line = 0); // Prefer calling IM_REGISTER_TEST()\nIMGUI_API void                ImGuiTestEngine_UnregisterTest(ImGuiTestEngine* engine, ImGuiTest* test);\nIMGUI_API void                ImGuiTestEngine_UnregisterAllTests(ImGuiTestEngine* engine);\n\n// Functions: Main\nIMGUI_API void                ImGuiTestEngine_QueueTest(ImGuiTestEngine* engine, ImGuiTest* test, ImGuiTestRunFlags run_flags = 0);\nIMGUI_API void                ImGuiTestEngine_QueueTests(ImGuiTestEngine* engine, ImGuiTestGroup group, const char* filter = nullptr, ImGuiTestRunFlags run_flags = 0);\nIMGUI_API bool                ImGuiTestEngine_TryAbortEngine(ImGuiTestEngine* engine);\nIMGUI_API void                ImGuiTestEngine_AbortCurrentTest(ImGuiTestEngine* engine);\nIMGUI_API ImGuiTest*          ImGuiTestEngine_FindTestByName(ImGuiTestEngine* engine, const char* category, const char* name);\n\n// Functions: Status Queries\n// FIXME: Clarify API to avoid function calls vs raw bools in ImGuiTestEngineIO\nIMGUI_API bool                ImGuiTestEngine_IsTestQueueEmpty(ImGuiTestEngine* engine);\nIMGUI_API bool                ImGuiTestEngine_IsUsingSimulatedInputs(ImGuiTestEngine* engine);\nIMGUI_API void                ImGuiTestEngine_GetResultSummary(ImGuiTestEngine* engine, ImGuiTestEngineResultSummary* out_results);\nIMGUI_API void                ImGuiTestEngine_GetTestList(ImGuiTestEngine* engine, ImVector<ImGuiTest*>* out_tests);\nIMGUI_API void                ImGuiTestEngine_GetTestQueue(ImGuiTestEngine* engine, ImVector<ImGuiTestRunTask>* out_tests);\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n// Obsoleted 2025/03/17\nstatic inline void            ImGuiTestEngine_GetResult(ImGuiTestEngine* engine, int& out_count_tested, int& out_count_success) { ImGuiTestEngineResultSummary summary; ImGuiTestEngine_GetResultSummary(engine, &summary); out_count_tested = summary.CountTested; out_count_success = summary.CountSuccess; }\n#endif\n\n// Functions: Crash Handling\n// Ensure past test results are properly exported even if application crash during a test.\nIMGUI_API void                ImGuiTestEngine_InstallDefaultCrashHandler();     // Install default crash handler (if you don't have one)\nIMGUI_API void                ImGuiTestEngine_CrashHandler();                   // Default crash handler, should be called from a custom crash handler if such exists\n\n//-----------------------------------------------------------------------------\n// IO structure to configure the test engine\n//-----------------------------------------------------------------------------\n\n// Function bound to right-clicking on a test and selecting \"Open source\" in the UI\n// - Easy: you can make this function call OS shell to \"open\" the file (e.g. ImOsOpenInShell() helper).\n// - Better: bind this function to a custom setup which can pass line number to a text editor (e.g. see 'imgui_test_suite/tools/win32_open_with_sublime.cmd' example)\ntypedef void (ImGuiTestEngineSrcFileOpenFunc)(const char* filename, int line_no, void* user_data);\n\nstruct IMGUI_API ImGuiTestEngineIO\n{\n    //-------------------------------------------------------------------------\n    // Functions\n    //-------------------------------------------------------------------------\n\n    // Options: Functions\n    ImGuiTestCoroutineInterface*                CoroutineFuncs = nullptr;          // (Required) Coroutine functions (see imgui_te_coroutines.h)\n    ImFuncPtr(ImGuiTestEngineSrcFileOpenFunc)   SrcFileOpenFunc = nullptr;         // (Optional) To open source files from test engine UI (otherwise default to open file in shell)\n    ImFuncPtr(ImGuiScreenCaptureFunc)           ScreenCaptureFunc = nullptr;       // (Optional) To capture graphics output (application _MUST_ call ImGuiTestEngine_PostSwap() function after swapping is framebuffer)\n    void*                                       SrcFileOpenUserData = nullptr;     // (Optional) User data for SrcFileOpenFunc\n    void*                                       ScreenCaptureUserData = nullptr;   // (Optional) User data for ScreenCaptureFunc\n\n    // Options: Main\n    bool                        ConfigSavedSettings = true;                     // Load/Save settings in main context .ini file.\n    ImGuiTestRunSpeed           ConfigRunSpeed = ImGuiTestRunSpeed_Fast;        // Run tests in fast/normal/cinematic mode\n    bool                        ConfigStopOnError = false;                      // Stop queued tests on test error\n    bool                        ConfigBreakOnError = false;                     // Break debugger on test error by calling IM_DEBUG_BREAK()\n    bool                        ConfigKeepGuiFunc = false;                      // Keep test GUI running at the end of the test\n    ImGuiTestVerboseLevel       ConfigVerboseLevel = ImGuiTestVerboseLevel_Warning;\n    ImGuiTestVerboseLevel       ConfigVerboseLevelOnError = ImGuiTestVerboseLevel_Info;\n    bool                        ConfigLogToTTY = false;\n    bool                        ConfigLogToDebugger = false;\n    bool                        ConfigRestoreFocusAfterTests = true;// Restore focus back after running tests\n    bool                        ConfigCaptureEnabled = true;        // Master enable flags for capturing and saving captures. Disable to avoid e.g. lengthy saving of large PNG files.\n    bool                        ConfigCaptureOnError = false;\n    bool                        ConfigNoThrottle = false;           // Disable vsync for performance measurement or fast test running\n    bool                        ConfigMouseDrawCursor = true;       // Enable drawing of Dear ImGui software mouse cursor when running tests\n    float                       ConfigFixedDeltaTime = 0.0f;        // Use fixed delta time instead of calculating it from wall clock\n    int                         PerfStressAmount = 1;               // Integer to scale the amount of items submitted in test\n    char                        GitBranchName[64] = \"\";             // e.g. fill in branch name (e.g. recorded in perf samples .csv)\n\n    // Options: Speed of user simulation\n    float                       MouseSpeed = 600.0f;                // Mouse speed (pixel/second) when not running in fast mode\n    float                       MouseWobble = 0.25f;                // (0.0f..1.0f) How much wobble to apply to the mouse (pixels per pixel of move distance) when not running in fast mode\n    float                       ScrollSpeed = 1400.0f;              // Scroll speed (pixel/second) when not running in fast mode\n    float                       TypingSpeed = 20.0f;                // Char input speed (characters/second) when not running in fast mode\n    float                       ActionDelayShort = 0.15f;           // Time between short actions\n    float                       ActionDelayStandard = 0.40f;        // Time between most actions\n\n    // Options: Screen/video capture\n    char                        VideoCaptureEncoderPath[256] = \"\";  // Video encoder executable path, e.g. \"path/to/ffmpeg.exe\".\n    char                        VideoCaptureEncoderParams[256] = \"\";// Video encoder parameters for .MP4 captures, e.g. see IMGUI_CAPTURE_DEFAULT_VIDEO_PARAMS_FOR_FFMPEG\n    char                        GifCaptureEncoderParams[512] = \"\";  // Video encoder parameters for .GIF captures, e.g. see IMGUI_CAPTURE_DEFAULT_GIF_PARAMS_FOR_FFMPEG\n    char                        VideoCaptureExtension[8] = \".mp4\";  // Video file extension (default, may be overridden by test).\n\n    // Options: Watchdog. Set values to FLT_MAX to disable.\n    // Interactive GUI applications that may be slower tend to use higher values.\n    float                       ConfigWatchdogWarning = 30.0f;      // Warn when a test exceed this time (in second)\n    float                       ConfigWatchdogKillTest = 60.0f;     // Attempt to stop running a test when exceeding this time (in second)\n    float                       ConfigWatchdogKillApp = FLT_MAX;    // Stop application when exceeding this time (in second)\n\n    // Options: Export\n    // While you can manually call ImGuiTestEngine_Export(), registering filename/format here ensure the crash handler will always export if application crash.\n    const char*                 ExportResultsFilename = nullptr;\n    ImGuiTestEngineExportFormat ExportResultsFormat = (ImGuiTestEngineExportFormat)0;\n\n    // Options: Sanity Checks\n    bool                        CheckDrawDataIntegrity = false;     // Check ImDrawData integrity (buffer count, etc.). Currently cheap but may become a slow operation.\n\n    //-------------------------------------------------------------------------\n    // Output\n    //-------------------------------------------------------------------------\n\n    // Output: State of test engine\n    bool                        IsRunningTests = false;\n    bool                        IsRequestingMaxAppSpeed = false;    // When running in fast mode: request app to skip vsync or even skip rendering if it wants\n    bool                        IsCapturing = false;                // Capture is in progress\n};\n\n//-------------------------------------------------------------------------\n// ImGuiTestItemInfo\n//-------------------------------------------------------------------------\n\n// Information about a given item or window, result of an ItemInfo() or WindowInfo() query\nstruct ImGuiTestItemInfo\n{\n    ImGuiID                     ID = 0;                     // Item ID\n    char                        DebugLabel[32] = {};        // Shortened/truncated label for debugging and convenience purpose\n    ImGuiWindow*                Window = nullptr;           // Item Window\n    unsigned int                NavLayer : 1;               // Nav layer of the item (ImGuiNavLayer)\n    int                         Depth : 16;                 // Depth from requested parent id. 0 == ID is immediate child of requested parent id.\n    int                         TimestampMain;              // Timestamp of main result (all fields)\n    int                         TimestampStatus;            // Timestamp of StatusFlags\n    ImGuiID                     ParentID = 0;               // Item Parent ID (value at top of the ID stack)\n    ImRect                      RectFull = ImRect();        // Item Rectangle\n    ImRect                      RectClipped = ImRect();     // Item Rectangle (clipped with window->ClipRect at time of item submission)\n    ImGuiItemFlags              ItemFlags = 0;              // Item flags\n    //ImGuiItemFlags            InFlags = 0;                // Item flags (OBSOLETE: before 2024/10/17 ItemFlags was called InFlags)\n    ImGuiItemStatusFlags        StatusFlags = 0;            // Item Status flags (fully updated for some items only, compare TimestampStatus to FrameCount)\n\n    ImGuiTestItemInfo()         { memset(this, 0, sizeof(*this)); }\n};\n\n// Result of an GatherItems() query\nstruct IMGUI_API ImGuiTestItemList\n{\n    ImPool<ImGuiTestItemInfo>   Pool;\n\n    void                        Clear()                 { Pool.Clear(); }\n    void                        Reserve(int capacity)   { Pool.Reserve(capacity); }\n    int                         GetSize() const         { return Pool.GetMapSize(); }\n    const ImGuiTestItemInfo*    GetByIndex(int n)       { return Pool.GetByIndex(n); }\n    const ImGuiTestItemInfo*    GetByID(ImGuiID id)     { return Pool.GetByKey(id); }\n\n    // For range-for\n    size_t                      size() const            { return (size_t)Pool.GetMapSize(); }\n    const ImGuiTestItemInfo*    begin() const           { return Pool.Buf.begin(); }\n    const ImGuiTestItemInfo*    end() const             { return Pool.Buf.end(); }\n    const ImGuiTestItemInfo*    operator[] (size_t n)   { return &Pool.Buf[(int)n]; }\n};\n\n//-------------------------------------------------------------------------\n// ImGuiTestLog: store textual output of one given Test.\n//-------------------------------------------------------------------------\n\nstruct IMGUI_API ImGuiTestLogLineInfo\n{\n    ImGuiTestVerboseLevel           Level;\n    int                             LineOffset;\n};\n\nstruct IMGUI_API ImGuiTestLog\n{\n    ImGuiTextBuffer                 Buffer;\n    ImVector<ImGuiTestLogLineInfo>  LineInfo;\n    int                             CountPerLevel[ImGuiTestVerboseLevel_COUNT] = {};\n\n    // Functions\n    ImGuiTestLog() {}\n    bool    IsEmpty() const         { return Buffer.empty(); }\n    void    Clear();\n\n    // Extract log contents filtered per log-level.\n    // Output:\n    // - If 'buffer != nullptr': all extracted lines are appended to 'buffer'. Use 'buffer->c_str()' on your side to obtain the text.\n    // - Return value: number of lines extracted (should be equivalent to number of '\\n' inside buffer->c_str()).\n    // - You may call the function with buffer == nullptr to only obtain a count without getting the data.\n    // Verbose levels are inclusive:\n    // - To get ONLY Error:                     Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Error\n    // - To get ONLY Error and Warnings:        Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Warning\n    // - To get All Errors, Warnings, Debug...  Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Trace\n    int     ExtractLinesForVerboseLevels(ImGuiTestVerboseLevel level_min, ImGuiTestVerboseLevel level_max, ImGuiTextBuffer* out_buffer);\n\n    // [Internal]\n    void    UpdateLineOffsets(ImGuiTestEngineIO* engine_io, ImGuiTestVerboseLevel level, const char* start);\n};\n\n//-------------------------------------------------------------------------\n// ImGuiTest\n//-------------------------------------------------------------------------\n\ntypedef void    (ImGuiTestGuiFunc)(ImGuiTestContext* ctx);\ntypedef void    (ImGuiTestTestFunc)(ImGuiTestContext* ctx);\n\n// Wraps a placement new of a given type (where 'buffer' is the allocated memory)\ntypedef void    (ImGuiTestVarsConstructor)(void* buffer);\ntypedef void    (ImGuiTestVarsPostConstructor)(ImGuiTestContext* ctx, void* ptr, void* fn);\ntypedef void    (ImGuiTestVarsDestructor)(void* ptr);\n\n// Storage for the output of a test run\nstruct IMGUI_API ImGuiTestOutput\n{\n    ImGuiTestStatus                 Status = ImGuiTestStatus_Unknown;\n    ImGuiTestLog                    Log;\n    ImU64                           StartTime = 0;\n    ImU64                           EndTime = 0;\n};\n\n// Storage for one test\nstruct IMGUI_API ImGuiTest\n{\n    // Test Definition\n    const char*                     Category = nullptr;             // Literal, not owned\n    const char*                     Name = nullptr;                 // Literal, generally not owned unless NameOwned=true\n    ImGuiTestGroup                  Group = ImGuiTestGroup_Unknown; // Coarse groups: 'Tests' or 'Perf'\n    bool                            NameOwned = false;              //\n    int                             ArgVariant = 0;                 // User parameter. Generally we use it to run variations of a same test by sharing GuiFunc/TestFunc\n    ImGuiTestFlags                  Flags = ImGuiTestFlags_None;    // See ImGuiTestFlags_\n    ImFuncPtr(ImGuiTestGuiFunc)     GuiFunc = nullptr;              // GUI function (optional if your test are running over an existing GUI application)\n    ImFuncPtr(ImGuiTestTestFunc)    TestFunc = nullptr;             // Test function\n    void*                           UserData = nullptr;             // General purpose user data (if assigning capturing lambdas on GuiFunc/TestFunc you may not need to use this)\n    //ImVector<ImGuiTestRunTask>    Dependencies;                   // Registered via AddDependencyTest(), ran automatically before our test. This is a simpler wrapper to calling ctx->RunChildTest()\n\n    // Sources information (exposed in UI)\n    const char*                     SourceFile = nullptr;           // __FILE__\n    int                             SourceLine = 0;                 // __LINE__\n    int                             SourceLineEnd = 0;              // end of line (when calculated by ImGuiTestEngine_StartCalcSourceLineEnds())\n\n    // Last Test Output/Status\n    // (this is the only part that may change after registration)\n    ImGuiTestOutput                 Output;\n\n    // User variables (which are instantiated when running the test)\n    // Setup after test registration with SetVarsDataType<>(), access instance during test with GetVars<>().\n    // This is mostly useful to communicate between GuiFunc and TestFunc. If you don't use both you may not want to use it!\n    size_t                          VarsSize = 0;\n    ImGuiTestVarsConstructor*       VarsConstructor = nullptr;\n    ImGuiTestVarsPostConstructor*   VarsPostConstructor = nullptr;  // To override constructor default (in case the default are problematic on the first GuiFunc frame)\n    void*                           VarsPostConstructorUserFn = nullptr;\n    ImGuiTestVarsDestructor*        VarsDestructor = nullptr;\n\n    // Functions\n    ImGuiTest() {}\n    ~ImGuiTest();\n\n    void SetOwnedName(const char* name);\n\n    template <typename T>\n    void SetVarsDataType(void(*post_initialize)(ImGuiTestContext* ctx, T& vars) = nullptr)\n    {\n        VarsSize = sizeof(T);\n        VarsConstructor = [](void* ptr) { IM_PLACEMENT_NEW(ptr) T; };\n        VarsDestructor = [](void* ptr) { IM_UNUSED(ptr); reinterpret_cast<T*>(ptr)->~T(); };\n        if (post_initialize != nullptr)\n        {\n            VarsPostConstructorUserFn = (void*)post_initialize;\n            VarsPostConstructor = [](ImGuiTestContext* ctx, void* ptr, void* fn) { ((void (*)(ImGuiTestContext*, T&))(fn))(ctx, *(T*)ptr); };\n        }\n    }\n};\n\n// Stored in test queue\nstruct IMGUI_API ImGuiTestRunTask\n{\n    ImGuiTest*          Test = nullptr;\n    ImGuiTestRunFlags   RunFlags = ImGuiTestRunFlags_None;\n};\n\n//-------------------------------------------------------------------------\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#elif defined(__GNUC__)\n#pragma GCC diagnostic pop\n#endif\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/imgui_te_exporters.h",
    "content": "// dear imgui test engine\n// (result exporters)\n// Read https://github.com/ocornut/imgui_test_engine/wiki/Exporting-Results\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#pragma once\n\n//-------------------------------------------------------------------------\n// Description\n//-------------------------------------------------------------------------\n//\n// Test results may be exported in one of supported formats.\n// To enable result exporting please configure test engine as follows:\n//\n//     ImGuiTestEngineIO& test_io = ImGuiTestEngine_GetIO(engine);\n//     test_io.ExportResultsFile = \"output_file.xml\";\n//     test_io.ExportResultsFormat = ImGuiTestEngineExportFormat_<...>;\n//\n// JUnit XML format\n//------------------\n// JUnit XML format described at https://llg.cubic.org/docs/junit/. Many\n// third party applications support consumption of this format. Some of\n// of them are listed here:\n// - Jenkins\n//    - Installation guide: https://www.jenkins.io/doc/book/installing/docker/\n//    - JUnit plugin: https://plugins.jenkins.io/junit/\n// - xunit-viewer\n//    - Project: https://github.com/lukejpreston/xunit-viewer\n//    - Install npm: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm\n//    - Install viewer and view test results:\n//          npm install xunit-viewer\n//          imgui_test_suite -nopause -v2 -ve4 -nogui -export-file junit.xml tests\n//          node_modules/xunit-viewer/bin/xunit-viewer -r junit.xml -o junit.html\n//    - Open junit.html\n//\n\n//-------------------------------------------------------------------------\n// Forward Declarations\n//-------------------------------------------------------------------------\n\nstruct ImGuiTestEngine;\n\n//-------------------------------------------------------------------------\n// Types\n//-------------------------------------------------------------------------\n\nenum ImGuiTestEngineExportFormat : int\n{\n    ImGuiTestEngineExportFormat_None = 0,\n    ImGuiTestEngineExportFormat_JUnitXml,\n};\n\n//-------------------------------------------------------------------------\n// Functions\n//-------------------------------------------------------------------------\n\nvoid ImGuiTestEngine_PrintResultSummary(ImGuiTestEngine* engine);\n\nvoid ImGuiTestEngine_Export(ImGuiTestEngine* engine);\nvoid ImGuiTestEngine_ExportEx(ImGuiTestEngine* engine, ImGuiTestEngineExportFormat format, const char* filename);\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/imgui_te_imconfig.h",
    "content": "// dear imgui test engine\n// (template for compile-time configuration)\n// Replicate or #include this file in your imconfig.h to enable test engine.\n\n// Compile Dear ImGui with test engine hooks\n// (Important: This is a value-less define, to be consistent with other defines used in core dear imgui.)\n#define IMGUI_ENABLE_TEST_ENGINE\n\n// [Optional, default 0] Enable plotting of perflog data for comparing performance of different runs.\n// This feature requires ImPlot to be linked in the application.\n#ifndef IMGUI_TEST_ENGINE_ENABLE_IMPLOT\n#define IMGUI_TEST_ENGINE_ENABLE_IMPLOT 1\n#endif\n\n// [Optional, default 1] Enable screen capture and PNG/GIF saving functionalities\n// There's not much point to disable this but we provide it to reassure user that the dependencies on imstb_image_write.h and ffmpeg are technically optional.\n#ifndef IMGUI_TEST_ENGINE_ENABLE_CAPTURE\n#define IMGUI_TEST_ENGINE_ENABLE_CAPTURE 1\n#endif\n\n// [Optional, default 0] Using std::function and <functional> for function pointers such as ImGuiTest::TestFunc and ImGuiTest::GuiFunc\n#ifndef IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION\n#define IMGUI_TEST_ENGINE_ENABLE_STD_FUNCTION 1\n#endif\n\n// [Optional, default 0] Automatically fill ImGuiTestEngineIO::CoroutineFuncs with a default implementation using std::thread\n#ifndef IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL\n#define IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL 1\n#endif\n\n// [Optional, default 0] Disable calls that do not make sense on game consoles\n// (Disable: system(), popen(), sigaction(), colored TTY output)\n#ifndef IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n#if defined(__ORBIS__) || defined(__PROSPERO__) || defined(_GAMING_XBOX) || defined(_DURANGO)\n#define IMGUI_TEST_ENGINE_IS_GAME_CONSOLE 1\n#else\n#define IMGUI_TEST_ENGINE_IS_GAME_CONSOLE 0\n#endif\n#endif\n\n// Define IM_DEBUG_BREAK macros so it is accessible in imgui.h\n// (this is a conveniance for app using test engine may define an IM_ASSERT() that uses this instead of an actual assert)\n// (this is a copy of the block in imgui_internal.h. if the one in imgui_internal.h were to be defined at the top of imgui.h we wouldn't need this)\n#ifndef IM_DEBUG_BREAK\n#if defined (_MSC_VER)\n#define IM_DEBUG_BREAK()    __debugbreak()\n#elif defined(__clang__)\n#define IM_DEBUG_BREAK()    __builtin_debugtrap()\n#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))\n#define IM_DEBUG_BREAK()    __asm__ volatile(\"int $0x03\")\n#elif defined(__GNUC__) && defined(__thumb__)\n#define IM_DEBUG_BREAK()    __asm__ volatile(\".inst 0xde01\")\n#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__)\n#define IM_DEBUG_BREAK()    __asm__ volatile(\".inst 0xe7f001f0\");\n#else\n#define IM_DEBUG_BREAK()    IM_ASSERT(0)    // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger!\n#endif\n#endif // #ifndef IMGUI_DEBUG_BREAK\n\n#ifndef IMGUI_API\n#define IMGUI_API\n#endif\n\n// [Options] We provide custom assert macro used by our our test suite, which you may use:\n// - Calling IM_DEBUG_BREAK() instead of an actual assert, so we can easily recover and step over (compared to many assert implementations).\n// - If a test is running, test name will be included in the log.\n// - Macro is calling IM_DEBUG_BREAK() inline to get debugger to break in the calling function (instead of a deeper callstack level).\n// - Macro is using comma operator instead of an if() to avoid \"conditional expression is constant\" warnings.\nIMGUI_API extern void ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* func, int line);\n#define IM_TEST_ENGINE_ASSERT(_EXPR)    do { if ((void)0, !(_EXPR)) { ImGuiTestEngine_AssertLog(#_EXPR, __FILE__, __func__, __LINE__); IM_DEBUG_BREAK(); } } while (0)\n// V_ASSERT_CONTRACT, assertMacro:IM_ASSERT\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/imgui_te_internal.h",
    "content": "// dear imgui test engine\n// (internal api)\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#pragma once\n\n#include \"imgui_te_coroutine.h\"\n#include \"imgui_te_utils.h\"         // ImMovingAverage\n#include \"imgui_capture_tool.h\"     // ImGuiCaptureTool  // FIXME\n\n//-------------------------------------------------------------------------\n// FORWARD DECLARATIONS\n//-------------------------------------------------------------------------\n\nclass Str;                          // Str<> from thirdparty/Str/Str.h\nstruct ImGuiPerfTool;\n\n//-------------------------------------------------------------------------\n// DATA STRUCTURES\n//-------------------------------------------------------------------------\n\n// Query item position/window/state given ID.\nstruct ImGuiTestInfoTask\n{\n    // Input\n    ImGuiID                 ID = 0;\n    int                     FrameCount = -1;        // Timestamp of request\n    char                    DebugName[64] = \"\";     // Debug string representing the queried ID\n\n    // Output\n    ImGuiTestItemInfo       Result;\n};\n\n// Gather item list in given parent ID.\nstruct ImGuiTestGatherTask\n{\n    // Input\n    ImGuiID                 InParentID = 0;\n    int                     InMaxDepth = 0;\n    short                   InLayerMask = 0;\n\n    // Output/Temp\n    ImGuiTestItemList*      OutList = nullptr;\n    ImGuiTestItemInfo*      LastItemInfo = nullptr;\n\n    void Clear() { memset(this, 0, sizeof(*this)); }\n};\n\n// Find item ID given a label and a parent id\n// Usually used by queries with wildcards such as ItemInfo(\"hello/**/foo/bar\")\nstruct ImGuiTestFindByLabelTask\n{\n    // Input\n    ImGuiID                 InPrefixId = 0;                 // A known base ID which appears BEFORE the wildcard ID (for \"hello/**/foo/bar\" it would be hash of \"hello\")\n    int                     InSuffixDepth = 0;              // Number of labels in a path, after unknown base ID (for \"hello/**/foo/bar\" it would be 2)\n    const char*             InSuffix = nullptr;             // A label string which appears on ID stack after unknown base ID (for \"hello/**/foo/bar\" it would be \"foo/bar\")\n    const char*             InSuffixLastItem = nullptr;     // A last label string (for \"hello/**/foo/bar\" it would be \"bar\")\n    ImGuiID                 InSuffixLastItemHash = 0;\n    ImGuiItemStatusFlags    InFilterItemStatusFlags = 0;    // Flags required for item to be returned\n\n    // Output\n    ImGuiID                 OutItemId = 0;                  // Result item ID\n};\n\nenum ImGuiTestInputType\n{\n    ImGuiTestInputType_None,\n    ImGuiTestInputType_Key,\n    ImGuiTestInputType_Char,\n    ImGuiTestInputType_ViewportFocus,\n    ImGuiTestInputType_ViewportSetPos,\n    ImGuiTestInputType_ViewportSetSize,\n    ImGuiTestInputType_ViewportClose\n};\n\n// FIXME: May want to strip further now that core imgui is using its own input queue\nstruct ImGuiTestInput\n{\n    ImGuiTestInputType      Type = ImGuiTestInputType_None;\n    ImGuiKeyChord           KeyChord = ImGuiKey_None;\n    ImWchar                 Char = 0;\n    bool                    Down = false;\n    ImGuiID                 ViewportId = 0;\n    ImVec2                  ViewportPosSize;\n\n    static ImGuiTestInput   ForKeyChord(ImGuiKeyChord key_chord, bool down)\n    {\n        ImGuiTestInput inp;\n        inp.Type = ImGuiTestInputType_Key;\n        inp.KeyChord = key_chord;\n        inp.Down = down;\n        return inp;\n    }\n\n    static ImGuiTestInput   ForChar(ImWchar v)\n    {\n        ImGuiTestInput inp;\n        inp.Type = ImGuiTestInputType_Char;\n        inp.Char = v;\n        return inp;\n    }\n\n    static ImGuiTestInput   ForViewportFocus(ImGuiID viewport_id)\n    {\n        ImGuiTestInput inp;\n        inp.Type = ImGuiTestInputType_ViewportFocus;\n        inp.ViewportId = viewport_id;\n        return inp;\n    }\n\n    static ImGuiTestInput   ForViewportSetPos(ImGuiID viewport_id, const ImVec2& pos)\n    {\n        ImGuiTestInput inp;\n        inp.Type = ImGuiTestInputType_ViewportSetPos;\n        inp.ViewportId = viewport_id;\n        inp.ViewportPosSize = pos;\n        return inp;\n    }\n\n    static ImGuiTestInput   ForViewportSetSize(ImGuiID viewport_id, const ImVec2& size)\n    {\n        ImGuiTestInput inp;\n        inp.Type = ImGuiTestInputType_ViewportSetSize;\n        inp.ViewportId = viewport_id;\n        inp.ViewportPosSize = size;\n        return inp;\n    }\n\n    static ImGuiTestInput   ForViewportClose(ImGuiID viewport_id)\n    {\n        ImGuiTestInput inp;\n        inp.Type = ImGuiTestInputType_ViewportClose;\n        inp.ViewportId = viewport_id;\n        return inp;\n    }\n};\n\nstruct ImGuiTestInputs\n{\n    ImVec2                      MousePosValue;                  // Own non-rounded copy of MousePos in order facilitate simulating mouse movement very slow speed and high-framerate\n    ImVec2                      MouseWheel;\n    ImGuiID                     MouseHoveredViewport = 0;\n    int                         MouseButtonsValue = 0x00;       // FIXME-TESTS: Use simulated_io.MouseDown[] ?\n    ImVector<ImGuiTestInput>    Queue;\n    bool                        HostEscDown = false;\n    float                       HostEscDownDuration = -1.0f;    // Maintain our own DownDuration for host/backend ESC key so we can abort.\n};\n\n// [Internal] Test Engine Context\nstruct ImGuiTestEngine\n{\n    ImGuiTestEngineIO           IO;\n    ImGuiContext*               UiContextTarget = nullptr;      // imgui context for testing\n    ImGuiContext*               UiContextActive = nullptr;      // imgui context for testing == UiContextTarget or nullptr\n\n    bool                        Started = false;\n    bool                        UiContextHasHooks = false;\n    ImU64                       BatchStartTime = 0;\n    ImU64                       BatchEndTime = 0;\n    int                         FrameCount = 0;\n    float                       OverrideDeltaTime = -1.0f;      // Inject custom delta time into imgui context to simulate clock passing faster than wall clock time.\n    ImVector<ImGuiTest*>        TestsAll;\n    ImVector<ImGuiTestRunTask>  TestsQueue;\n    ImGuiTestContext*           TestContext = nullptr;          // Running test context\n    bool                        TestsSourceLinesDirty = false;\n    ImVector<ImGuiTestInfoTask*>InfoTasks;\n    ImGuiTestGatherTask         GatherTask;\n    ImGuiTestFindByLabelTask    FindByLabelTask;\n    ImGuiTestCoroutineHandle    TestQueueCoroutine = nullptr;   // Coroutine to run the test queue\n    bool                        TestQueueCoroutineShouldExit = false; // Flag to indicate that we are shutting down and the test queue coroutine should stop\n\n    // Inputs\n    ImGuiTestInputs             Inputs;\n\n    // UI support\n    bool                        Abort = false;\n    ImGuiTest*                  UiSelectAndScrollToTest = nullptr;\n    ImGuiTest*                  UiSelectedTest = nullptr;\n    Str*                        UiFilterTests;\n    Str*                        UiFilterPerfs;\n    ImU32                       UiFilterByStatusMask = ~0u;\n    bool                        UiMetricsOpen = false;\n    bool                        UiDebugLogOpen = false;\n    bool                        UiCaptureToolOpen = false;\n    bool                        UiStackToolOpen = false;\n    bool                        UiPerfToolOpen = false;\n    float                       UiLogHeight = 150.0f;\n\n    // Performance Monitor\n    double                      PerfRefDeltaTime;\n    ImMovingAverage<double>     PerfDeltaTime100;\n    ImMovingAverage<double>     PerfDeltaTime500;\n    ImGuiPerfTool*              PerfTool = nullptr;\n\n    // Screen/Video Capturing\n    ImGuiCaptureToolUI          CaptureTool;                        // Capture tool UI\n    ImGuiCaptureContext         CaptureContext;                     // Capture context used in tests\n    ImGuiCaptureArgs*           CaptureCurrentArgs = nullptr;\n\n    // Tools\n    bool                        PostSwapCalled = false;\n    bool                        ToolDebugRebootUiContext = false;   // Completely shutdown and recreate the dear imgui context in place\n    bool                        ToolSlowDown = false;\n    int                         ToolSlowDownMs = 100;\n    ImGuiTestRunSpeed           BackupConfigRunSpeed = ImGuiTestRunSpeed_Fast;\n    bool                        BackupConfigNoThrottle = false;\n\n    // Functions\n    ImGuiTestEngine();\n    ~ImGuiTestEngine();\n};\n\n//-------------------------------------------------------------------------\n// INTERNAL FUNCTIONS\n//-------------------------------------------------------------------------\n\nImGuiTestItemInfo*  ImGuiTestEngine_FindItemInfo(ImGuiTestEngine* engine, ImGuiID id, const char* debug_id);\nvoid                ImGuiTestEngine_Yield(ImGuiTestEngine* engine);\nvoid                ImGuiTestEngine_SetDeltaTime(ImGuiTestEngine* engine, float delta_time);\nint                 ImGuiTestEngine_GetFrameCount(ImGuiTestEngine* engine);\nbool                ImGuiTestEngine_PassFilter(ImGuiTest* test, const char* filter);\nvoid                ImGuiTestEngine_RunTest(ImGuiTestEngine* engine, ImGuiTestContext* ctx, ImGuiTest* test, ImGuiTestRunFlags run_flags);\n\nvoid                ImGuiTestEngine_BindImGuiContext(ImGuiTestEngine* engine, ImGuiContext* ui_ctx);\nvoid                ImGuiTestEngine_UnbindImGuiContext(ImGuiTestEngine* engine, ImGuiContext* ui_ctx);\n\nvoid                ImGuiTestEngine_RebootUiContext(ImGuiTestEngine* engine);\nImGuiPerfTool*      ImGuiTestEngine_GetPerfTool(ImGuiTestEngine* engine);\nvoid                ImGuiTestEngine_UpdateTestsSourceLines(ImGuiTestEngine* engine);\n\n// Screen/Video Capturing\nbool                ImGuiTestEngine_CaptureScreenshot(ImGuiTestEngine* engine, ImGuiCaptureArgs* args);\nbool                ImGuiTestEngine_CaptureBeginVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args);\nbool                ImGuiTestEngine_CaptureEndVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args);\n\n// Helper functions\nconst char*         ImGuiTestEngine_GetStatusName(ImGuiTestStatus v);\nconst char*         ImGuiTestEngine_GetRunSpeedName(ImGuiTestRunSpeed v);\nconst char*         ImGuiTestEngine_GetVerboseLevelName(ImGuiTestVerboseLevel v);\n\n//-------------------------------------------------------------------------\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/imgui_te_perftool.h",
    "content": "// dear imgui test engine\n// (performance tool)\n// Browse and visualize samples recorded by ctx->PerfCapture() calls.\n// User access via 'Test Engine UI -> Tools -> Perf Tool'\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#pragma once\n\n#include \"imgui.h\"\n\n// Forward Declaration\nstruct ImGuiPerfToolColumnInfo;\nstruct ImGuiTestEngine;\nstruct ImGuiCsvParser;\n\n// Configuration\n#define IMGUI_PERFLOG_DEFAULT_FILENAME  \"output/imgui_perflog.csv\"\n\n// [Internal] Perf log entry. Changes to this struct should be reflected in ImGuiTestContext::PerfCapture() and ImGuiTestEngine_Start().\n// This struct assumes strings stored here will be available until next ImGuiPerfTool::Clear() call. Fortunately we do not have to actively\n// manage lifetime of these strings. New entries are created only in two cases:\n// 1. ImGuiTestEngine_PerfToolAppendToCSV() call after perf test has run. This call receives ImGuiPerfToolEntry with const strings stored indefinitely by application.\n// 2. As a consequence of ImGuiPerfTool::LoadCSV() call, we persist the ImGuiCSVParser instance, which keeps parsed CSV text, from which strings are referenced.\n// As a result our solution also doesn't make many allocations.\nstruct IMGUI_API ImGuiPerfToolEntry\n{\n    ImU64                       Timestamp = 0;                  // Title of a particular batch of perftool entries.\n    const char*                 Category = nullptr;             // Name of category perf test is in.\n    const char*                 TestName = nullptr;             // Name of perf test.\n    double                      DtDeltaMs = 0.0;                // Result of perf test.\n    double                      DtDeltaMsMin = +FLT_MAX;        // May be used by perftool.\n    double                      DtDeltaMsMax = -FLT_MAX;        // May be used by perftool.\n    int                         NumSamples = 1;                 // Number aggregated samples.\n    int                         PerfStressAmount = 0;           //\n    const char*                 GitBranchName = nullptr;        // Build information.\n    const char*                 BuildType = nullptr;            //\n    const char*                 Cpu = nullptr;                  //\n    const char*                 OS = nullptr;                   //\n    const char*                 Compiler = nullptr;             //\n    const char*                 Date = nullptr;                 // Date of this entry or min date of combined entries.\n    //const char*               DateMax = nullptr;              // Max date of combined entries, or nullptr.\n    double                      VsBaseline = 0.0;               // Percent difference vs baseline.\n    int                         LabelIndex = 0;                 // Index of TestName in ImGuiPerfTool::_LabelsVisible.\n\n    ImGuiPerfToolEntry()        { }\n    ImGuiPerfToolEntry(const ImGuiPerfToolEntry& rhs)           { Set(rhs); }\n    ImGuiPerfToolEntry& operator=(const ImGuiPerfToolEntry& rhs){ Set(rhs); return *this; }\n    void Set(const ImGuiPerfToolEntry& rhs);\n};\n\n// [Internal] Perf log batch.\nstruct ImGuiPerfToolBatch\n{\n    ImU64                       BatchID = 0;                    // Timestamp of the batch, or unique ID of the build in combined mode.\n    int                         NumSamples = 0;                 // A number of unique batches aggregated.\n    int                         BranchIndex = 0;                // For per-branch color mapping.\n    ImVector<ImGuiPerfToolEntry> Entries;                       // Aggregated perf test entries. Order follows ImGuiPerfTool::_LabelsVisible order.\n    ~ImGuiPerfToolBatch()       { Entries.clear_destruct(); }   // FIXME: Misleading: nothing to destruct in that struct?\n};\n\nenum ImGuiPerfToolDisplayType : int\n{\n    ImGuiPerfToolDisplayType_Simple,                            // Each run will be displayed individually.\n    ImGuiPerfToolDisplayType_PerBranchColors,                   // Use one bar color per branch.\n    ImGuiPerfToolDisplayType_CombineByBuildInfo,                // Entries with same build information will be averaged.\n};\n\n//\nstruct IMGUI_API ImGuiPerfTool\n{\n    ImVector<ImGuiPerfToolEntry>_SrcData;                       // Raw entries from CSV file (with string pointer into CSV data).\n    ImVector<const char*>       _Labels;\n    ImVector<const char*>       _LabelsVisible;                 // ImPlot requires a pointer of all labels beforehand. Always contains a dummy \"\" entry at the end!\n    ImVector<ImGuiPerfToolBatch> _Batches;\n    ImGuiStorage                _LabelBarCounts;                // Number bars each label will render.\n    int                         _NumVisibleBuilds = 0;          // Cached number of visible builds.\n    int                         _NumUniqueBuilds = 0;           // Cached number of unique builds.\n    ImGuiPerfToolDisplayType    _DisplayType = ImGuiPerfToolDisplayType_CombineByBuildInfo;\n    int                         _BaselineBatchIndex = 0;        // Index of baseline build.\n    ImU64                       _BaselineTimestamp = 0;\n    ImU64                       _BaselineBuildId = 0;\n    char                        _Filter[128];                   // Context menu filtering substring.\n    char                        _FilterDateFrom[11] = {};\n    char                        _FilterDateTo[11] = {};\n    float                       _InfoTableHeight = 180.0f;\n    int                         _AlignStress = 0;               // Alignment values for build info components, so they look aligned in the legend.\n    int                         _AlignType = 0;\n    int                         _AlignOs = 0;\n    int                         _AlignCpu = 0;\n    int                         _AlignCompiler = 0;\n    int                         _AlignBranch = 0;\n    int                         _AlignSamples = 0;\n    bool                        _InfoTableSortDirty = false;\n    ImVector<ImU64>             _InfoTableSort;                 // _InfoTableSort[_LabelsVisible.Size * _Batches.Size]. Contains sorted batch indices for each label.\n    const ImGuiTableSortSpecs*  _InfoTableSortSpecs = nullptr;  // Current table sort specs.\n    ImGuiStorage                _TempSet;                       // Used as a set\n    int                         _TableHoveredTest = -1;         // Index within _VisibleLabelPointers array.\n    int                         _TableHoveredBatch = -1;\n    int                         _PlotHoverTest = -1;\n    int                         _PlotHoverBatch = -1;\n    bool                        _PlotHoverTestLabel = false;\n    bool                        _ReportGenerating = false;\n    ImGuiStorage                _Visibility;\n    ImGuiCsvParser*             _CsvParser = nullptr;           // We keep this around and point to its fields\n\n    ImGuiPerfTool();\n    ~ImGuiPerfTool();\n\n    void        Clear();\n    bool        LoadCSV(const char* filename = nullptr);\n    void        AddEntry(ImGuiPerfToolEntry* entry);\n\n    void        ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open);\n    void        ViewOnly(const char* perf_name);\n    void        ViewOnly(const char** perf_names);\n    ImGuiPerfToolEntry* GetEntryByBatchIdx(int idx, const char* perf_name = nullptr);\n    bool        SaveHtmlReport(const char* file_name, const char* image_file = nullptr);\n    inline bool Empty()         { return _SrcData.empty(); }\n\n    void        _Rebuild();\n    bool        _IsVisibleBuild(ImGuiPerfToolBatch* batch);\n    bool        _IsVisibleBuild(ImGuiPerfToolEntry* batch);\n    bool        _IsVisibleTest(const char* test_name);\n    void        _CalculateLegendAlignment();\n    void        _ShowEntriesPlot();\n    void        _ShowEntriesTable();\n    void        _SetBaseline(int batch_index);\n    void        _AddSettingsHandler();\n    void        _UnpackSortedKey(ImU64 key, int* batch_index, int* entry_index, int* monotonic_index = nullptr);\n};\n\nIMGUI_API void    ImGuiTestEngine_PerfToolAppendToCSV(ImGuiPerfTool* perf_log, ImGuiPerfToolEntry* entry, const char* filename = nullptr);\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/imgui_te_ui.h",
    "content": "// dear imgui test engine\n// (ui)\n// If you run tests in an interactive or visible application, you may want to call ImGuiTestEngine_ShowTestEngineWindows()\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n// Provide access to:\n// - \"Dear ImGui Test Engine\" main interface\n// - \"Dear ImGui Capture Tool\"\n// - \"Dear ImGui Perf Tool\"\n// - other core debug functions: Metrics, Debug Log\n\n#pragma once\n\n#ifndef IMGUI_VERSION\n#include \"imgui.h\"      // IMGUI_API\n#endif\n\n// Forward declarations\nstruct ImGuiTestEngine;\n\n// Functions\nIMGUI_API void  ImGuiTestEngine_ShowTestEngineWindows(ImGuiTestEngine* engine, bool* p_open);\nIMGUI_API void  ImGuiTestEngine_OpenSourceFile(ImGuiTestEngine* engine, const char* source_filename, int source_line_no);\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/imgui_te_utils.h",
    "content": "// dear imgui test engine\n// (helpers/utilities. do NOT use this as a general purpose library)\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#pragma once\n\n//-----------------------------------------------------------------------------\n// Includes\n//-----------------------------------------------------------------------------\n\n#include <math.h>   // fabsf\n#include <stdint.h> // uint64_t\n#include <stdio.h>  // FILE*\n#include \"imgui.h\"  // ImGuiID, ImGuiKey\nclass Str;          // Str<> from thirdparty/Str/Str.h\n\n//-----------------------------------------------------------------------------\n// Hashing Helpers\n//-----------------------------------------------------------------------------\n\nImGuiID     ImHashDecoratedPath(const char* str, const char* str_end = nullptr, ImGuiID seed = 0);\nconst char* ImFindNextDecoratedPartInPath(const char* str, const char* str_end = nullptr);\n\n//-----------------------------------------------------------------------------\n// File/Directory Helpers\n//-----------------------------------------------------------------------------\n\nbool        ImFileExist(const char* filename);\nbool        ImFileDelete(const char* filename);\nbool        ImFileCreateDirectoryChain(const char* path, const char* path_end = nullptr);\nbool        ImFileFindInParents(const char* sub_path, int max_parent_count, Str* output);\nbool        ImFileLoadSourceBlurb(const char* filename, int line_no_start, int line_no_end, ImGuiTextBuffer* out_buf);\n\n//-----------------------------------------------------------------------------\n// Path Helpers\n//-----------------------------------------------------------------------------\n\n// Those are strictly string manipulation functions\nconst char* ImPathFindFilename(const char* path, const char* path_end = nullptr);      // Return value always between path and path_end\nconst char* ImPathFindExtension(const char* path, const char* path_end = nullptr);     // Return value always between path and path_end\nvoid        ImPathFixSeparatorsForCurrentOS(char* buf);\n\n//-----------------------------------------------------------------------------\n// String Helpers\n//-----------------------------------------------------------------------------\n\nvoid        ImStrReplace(Str* s, const char* find, const char* repl);\nconst char* ImStrchrRangeWithEscaping(const char* str, const char* str_end, char find_c);\nvoid        ImStrXmlEscape(Str* s);\nint         ImStrBase64Encode(const unsigned char* src, char* dst, int length);\n\n//-----------------------------------------------------------------------------\n// Parsing Helpers\n//-----------------------------------------------------------------------------\n\nvoid        ImParseExtractArgcArgvFromCommandLine(int* out_argc, char const*** out_argv, const char* cmd_line);\nbool        ImParseFindIniSection(const char* ini_config, const char* header, ImVector<char>* result);\n\n//-----------------------------------------------------------------------------\n// Time Helpers\n//-----------------------------------------------------------------------------\n\nuint64_t    ImTimeGetInMicroseconds();\nvoid        ImTimestampToISO8601(uint64_t timestamp, Str* out_date);\n\n//-----------------------------------------------------------------------------\n// Threading Helpers\n//-----------------------------------------------------------------------------\n\nvoid        ImThreadSleepInMilliseconds(int ms);\nvoid        ImThreadSetCurrentThreadDescription(const char* description);\n\n//-----------------------------------------------------------------------------\n// Build Info helpers\n//-----------------------------------------------------------------------------\n\n// All the pointers are expect to be literals/persistent\nstruct ImBuildInfo\n{\n    const char*     Type = \"\";\n    const char*     Cpu = \"\";\n    const char*     OS = \"\";\n    const char*     Compiler = \"\";\n    char            Date[32];       // \"YYYY-MM-DD\"\n    const char*     Time = \"\";\n};\n\nconst ImBuildInfo*  ImBuildGetCompilationInfo();\nbool                ImBuildFindGitBranchName(const char* git_repo_path, Str* branch_name);\n\n//-----------------------------------------------------------------------------\n// Operating System Helpers\n//-----------------------------------------------------------------------------\n\nenum ImOsConsoleStream\n{\n    ImOsConsoleStream_StandardOutput,\n    ImOsConsoleStream_StandardError,\n};\n\nenum ImOsConsoleTextColor\n{\n    ImOsConsoleTextColor_Black,\n    ImOsConsoleTextColor_White,\n    ImOsConsoleTextColor_BrightWhite,\n    ImOsConsoleTextColor_BrightRed,\n    ImOsConsoleTextColor_BrightGreen,\n    ImOsConsoleTextColor_BrightBlue,\n    ImOsConsoleTextColor_BrightYellow,\n};\n\nbool        ImOsCreateProcess(const char* cmd_line);\nFILE*       ImOsPOpen(const char* cmd_line, const char* mode);\nvoid        ImOsPClose(FILE* fp);\nvoid        ImOsOpenInShell(const char* path);\nbool        ImOsIsDebuggerPresent();\nvoid        ImOsOutputDebugString(const char* message);\nvoid        ImOsConsoleSetTextColor(ImOsConsoleStream stream, ImOsConsoleTextColor color);\n\n//-----------------------------------------------------------------------------\n// Miscellaneous functions\n//-----------------------------------------------------------------------------\n\n// Tables functions\nstruct ImGuiTable;\nImGuiID     TableGetHeaderID(ImGuiTable* table, const char* column, int instance_no = 0);\nImGuiID     TableGetHeaderID(ImGuiTable* table, int column_n, int instance_no = 0);\nvoid        TableDiscardInstanceAndSettings(ImGuiID table_id);\n\n// DrawData functions\nvoid        DrawDataVerifyMatchingBufferCount(ImDrawData* draw_data);\n\n//-----------------------------------------------------------------------------\n// Helper: maintain/calculate moving average\n//-----------------------------------------------------------------------------\n\ntemplate<typename TYPE>\nstruct ImMovingAverage\n{\n    // Internal Fields\n    ImVector<TYPE>  Samples;\n    TYPE            Accum;\n    int             Idx;\n    int             FillAmount;\n\n    // Functions\n    ImMovingAverage()               { Accum = (TYPE)0; Idx = FillAmount = 0; }\n    void    Init(int count)         { Samples.resize(count); memset(Samples.Data, 0, (size_t)Samples.Size * sizeof(TYPE)); Accum = (TYPE)0; Idx = FillAmount = 0; }\n    void    AddSample(TYPE v)       { Accum += v - Samples[Idx]; Samples[Idx] = v; if (++Idx == Samples.Size) Idx = 0; if (FillAmount < Samples.Size) FillAmount++;  }\n    TYPE    GetAverage() const      { return Accum / (TYPE)FillAmount; }\n    int     GetSampleCount() const  { return Samples.Size; }\n    bool    IsFull() const          { return FillAmount == Samples.Size; }\n};\n\n//-----------------------------------------------------------------------------\n// Helper: Simple/dumb CSV parser\n//-----------------------------------------------------------------------------\n\nstruct ImGuiCsvParser\n{\n    // Public fields\n    int             Columns = 0;                    // Number of columns in CSV file.\n    int             Rows = 0;                       // Number of rows in CSV file.\n\n    // Internal fields\n    char*           _Data = nullptr;                   // CSV file data.\n    ImVector<char*> _Index;                         // CSV table: _Index[row * _Columns + col].\n\n    // Functions\n    ImGuiCsvParser(int columns = -1)                { Columns = columns; }\n    ~ImGuiCsvParser()                               { Clear(); }\n    bool            Load(const char* file_name);    // Open and parse a CSV file.\n    void            Clear();                        // Free allocated buffers.\n    const char*     GetCell(int row, int col)       { IM_ASSERT(0 <= row && row < Rows && 0 <= col && col < Columns); return _Index[row * Columns + col]; }\n};\n\n//-----------------------------------------------------------------------------\n// Misc Dear ImGui extensions\n//-----------------------------------------------------------------------------\n\n#if IMGUI_VERSION_NUM < 18924\nstruct ImGuiTabBar;\nstruct ImGuiTabItem;\n#endif\n\nnamespace ImGui\n{\n\nIMGUI_API void      ItemErrorFrame(ImU32 col);\n\n#if IMGUI_VERSION_NUM < 18927\nImGuiID             TableGetInstanceID(ImGuiTable* table, int instance_no = 0);\n#endif\n\n// Str support for InputText()\nIMGUI_API bool      InputText(const char* label, Str* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);\nIMGUI_API bool      InputTextWithHint(const char* label, const char* hint, Str* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);\nIMGUI_API bool      InputTextMultiline(const char* label, Str* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);\n\n// Splitter\nIMGUI_API bool      Splitter(const char* id, float* value_1, float* value_2, int axis, int anchor = 0, float min_size_0 = -1.0f, float min_size_1 = -1.0f);\n\n// Misc\nIMGUI_API ImFont*   FindFontByPrefix(const char* name);\n\n// Legacy version support\n#if IMGUI_VERSION_NUM < 18924\nIMGUI_API const char* TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);\n#endif\n\n}\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/thirdparty/README.md",
    "content": "## Third party libraries used by Test Engine\n\nAlways used:\n- `Str/Str.h` simple string type, used by `imgui_test_engine` (Public Domain)\n\nUsed if `IMGUI_TEST_ENGINE_ENABLE_CAPTURE` is defined to 1 (default: 1)\n- `stb/imstb_image_write.h` image writer, used by `imgui_capture_tool` (MIT Licence OR Public Domain)\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/thirdparty/Str/.editorconfig",
    "content": "[*.{h,cpp}]\nindent_style = space\nindent_size = 4\n\n\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/thirdparty/Str/README.md",
    "content": "```\nStr\nSimple C++ string type with an optional local buffer, by Omar Cornut\nhttps://github.com/ocornut/str\n\nLICENSE\nThis software is in the public domain. Where that dedication is not\nrecognized, you are granted a perpetual, irrevocable license to copy,\ndistribute, and modify this file as you see fit.\n\nUSAGE\nInclude Str.h in whatever places need to refer to it.\nIn ONE .cpp file, write '#define STR_IMPLEMENTATION' before the #include.\nThis expands out the actual implementation into that C/C++ file.\n\nNOTES\n- This isn't a fully featured string class. \n- It is a simple, bearable replacement to std::string that isn't heap abusive nor bloated (can actually be debugged by humans!).\n- String are mutable. We don't maintain size so length() is not-constant time. \n- Maximum string size currently limited to 2 MB (we allocate 21 bits to hold capacity).\n- Local buffer size is currently limited to 1023 bytes (we allocate 10 bits to hold local buffer size).\n- We could easily raise those limits if we are ok to increase the structure overhead in 32-bits mode.\n- In \"non-owned\" mode for literals/reference we don't do any tracking/counting of references.\n- Overhead is 8-bytes in 32-bits, 16-bytes in 64-bits (12 + alignment).\n- I'm using this code but it hasn't been tested thoroughly.\n\nThe idea is that you can provide an arbitrary sized local buffer if you expect string to fit \nmost of the time, and then you avoid using costly heap.\n\nNo local buffer, always use heap, sizeof()==8~16 (depends if your pointers are 32-bits or 64-bits)\n\n   Str s = \"hey\";   // use heap\n\nWith a local buffer of 16 bytes, sizeof() == 8~16 + 16 bytes.\n\n   Str16 s = \"filename.h\"; // copy into local buffer\n   Str16 s = \"long_filename_not_very_long_but_longer_than_expected.h\";   // use heap\n\nWith a local buffer of 256 bytes, sizeof() == 8~16 + 256 bytes.\n\n   Str256 s = \"long_filename_not_very_long_but_longer_than_expected.h\";  // copy into local buffer\n\nCommon sizes are defined at the bottom of Str.h, you may define your own.\n\nFunctions:\n\n   Str256 s;\n   s.set(\"hello sailor\");                   // set (copy)\n   s.setf(\"%s/%s.tmp\", folder, filename);   // set (w/format)\n   s.append(\"hello\");                       // append. cost a length() calculation!\n   s.appendf(\"hello %d\", 42);               // append (w/format). cost a length() calculation!\n   s.set_ref(\"Hey!\");                       // set (literal/reference, just copy pointer, no tracking)\n\nConstructor helper for format string: add a trailing 'f' to the type. Underlying type is the same.\n\n   Str256f filename(\"%s/%s.tmp\", folder, filename);             // construct (w/format)\n   fopen(Str256f(\"%s/%s.tmp, folder, filename).c_str(), \"rb\");  // construct (w/format), use as function param, destruct\n\nConstructor helper for reference/literal:\n\n   StrRef ref(\"literal\");                   // copy pointer, no allocation, no string copy\n   StrRef ref2(GetDebugName());             // copy pointer. no tracking of anything whatsoever, know what you are doing!\n\nAll StrXXX types derives from Str and instance hold the local buffer capacity.\nSo you can pass e.g. Str256* to a function taking base type Str* and it will be functional!\n\n   void MyFunc(Str& s) { s = \"Hello\"; }     // will use local buffer if available in Str instance\n\n(Using a template e.g. Str<N> we could remove the LocalBufSize storage but it would make passing typed Str<> to functions tricky.\n Instead we don't use template so you can pass them around as the base type Str*. Also, templates are ugly.)\n```\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/thirdparty/Str/Str.h",
    "content": "// Str v0.33\n// Simple C++ string type with an optional local buffer, by Omar Cornut\n// https://github.com/ocornut/str\n\n// LICENSE\n//  This software is in the public domain. Where that dedication is not\n//  recognized, you are granted a perpetual, irrevocable license to copy,\n//  distribute, and modify this file as you see fit.\n\n// USAGE\n//  Include this file in whatever places need to refer to it.\n//  In ONE .cpp file, write '#define STR_IMPLEMENTATION' before the #include of this file.\n//  This expands out the actual implementation into that C/C++ file.\n\n\n/*\n- This isn't a fully featured string class.\n- It is a simple, bearable replacement to std::string that isn't heap abusive nor bloated (can actually be debugged by humans).\n- String are mutable. We don't maintain size so length() is not-constant time.\n- Maximum string size currently limited to 2 MB (we allocate 21 bits to hold capacity).\n- Local buffer size is currently limited to 1023 bytes (we allocate 10 bits to hold local buffer size).\n- In \"non-owned\" mode for literals/reference we don't do any tracking/counting of references.\n- Overhead is 8-bytes in 32-bits, 16-bytes in 64-bits (12 + alignment).\n- This code hasn't been tested very much. it is probably incomplete or broken. Made it for my own use.\n\nThe idea is that you can provide an arbitrary sized local buffer if you expect string to fit\nmost of the time, and then you avoid using costly heap.\n\nNo local buffer, always use heap, sizeof()==8~16 (depends if your pointers are 32-bits or 64-bits)\n\n   Str s = \"hey\";\n\nWith a local buffer of 16 bytes, sizeof() == 8~16 + 16 bytes.\n\n   Str16 s = \"filename.h\"; // copy into local buffer\n   Str16 s = \"long_filename_not_very_long_but_longer_than_expected.h\";   // use heap\n\nWith a local buffer of 256 bytes, sizeof() == 8~16 + 256 bytes.\n\n   Str256 s = \"long_filename_not_very_long_but_longer_than_expected.h\";  // copy into local buffer\n\nCommon sizes are defined at the bottom of Str.h, you may define your own.\n\nFunctions:\n\n   Str256 s;\n   s.set(\"hello sailor\");                   // set (copy)\n   s.setf(\"%s/%s.tmp\", folder, filename);   // set (w/format)\n   s.append(\"hello\");                       // append. cost a length() calculation!\n   s.appendf(\"hello %d\", 42);               // append (w/format). cost a length() calculation!\n   s.set_ref(\"Hey!\");                       // set (literal/reference, just copy pointer, no tracking)\n\nConstructor helper for format string: add a trailing 'f' to the type. Underlying type is the same.\n\n   Str256f filename(\"%s/%s.tmp\", folder, filename);             // construct (w/format)\n   fopen(Str256f(\"%s/%s.tmp, folder, filename).c_str(), \"rb\");  // construct (w/format), use as function param, destruct\n\nConstructor helper for reference/literal:\n\n   StrRef ref(\"literal\");                   // copy pointer, no allocation, no string copy\n   StrRef ref2(GetDebugName());             // copy pointer. no tracking of anything whatsoever, know what you are doing!\n\nAll StrXXX types derives from Str and instance hold the local buffer capacity. So you can pass e.g. Str256* to a function taking base type Str* and it will be functional.\n\n   void MyFunc(Str& s) { s = \"Hello\"; }     // will use local buffer if available in Str instance\n\n(Using a template e.g. Str<N> we could remove the LocalBufSize storage but it would make passing typed Str<> to functions tricky.\n Instead we don't use template so you can pass them around as the base type Str*. Also, templates are ugly.)\n*/\n\n/*\n CHANGELOG\n  0.33 - fixed capacity() return value to match standard. e.g. a Str256's capacity() now returns 255, not 256.\n  0.32 - added owned() accessor.\n  0.31 - fixed various warnings.\n  0.30 - turned into a single header file, removed Str.cpp.\n  0.29 - fixed bug when calling reserve on non-owned strings (ie. when using StrRef or set_ref), and fixed <string> include.\n  0.28 - breaking change: replaced Str32 by Str30 to avoid collision with Str32 from MacTypes.h .\n  0.27 - added STR_API and basic .natvis file.\n  0.26 - fixed set(cont char* src, const char* src_end) writing null terminator to the wrong position.\n  0.25 - allow set(const char* NULL) or operator= NULL to clear the string. note that set() from range or other types are not allowed.\n  0.24 - allow set_ref(const char* NULL) to clear the string. include fixes for linux.\n  0.23 - added append(char). added append_from(int idx, XXX) functions. fixed some compilers warnings.\n  0.22 - documentation improvements, comments. fixes for some compilers.\n  0.21 - added StrXXXf() constructor to construct directly from a format string.\n*/\n\n/*\nTODO\n- Since we lose 4-bytes of padding on 64-bits architecture, perhaps just spread the header to 8-bytes and lift size limits?\n- More functions/helpers.\n*/\n\n#ifndef STR_INCLUDED\n#define STR_INCLUDED\n\n//-------------------------------------------------------------------------\n// CONFIGURATION\n//-------------------------------------------------------------------------\n\n#ifndef STR_MEMALLOC\n#define STR_MEMALLOC  malloc\n#include <stdlib.h>\n#endif\n#ifndef STR_MEMFREE\n#define STR_MEMFREE   free\n#include <stdlib.h>\n#endif\n#ifndef STR_ASSERT\n#define STR_ASSERT    assert\n#include <assert.h>\n#endif\n#ifndef STR_API\n#define STR_API\n#endif\n#include <stdarg.h>   // for va_list\n#include <string.h>   // for strlen, strcmp, memcpy, etc.\n\n// Configuration: #define STR_SUPPORT_STD_STRING 0 to disable setters variants using const std::string& (on by default)\n#ifndef STR_SUPPORT_STD_STRING\n#define STR_SUPPORT_STD_STRING  1\n#endif\n\n// Configuration: #define STR_DEFINE_STR32 1 to keep defining Str32/Str32f, but be warned: on macOS/iOS, MacTypes.h also defines a type named Str32.\n#ifndef STR_DEFINE_STR32\n#define STR_DEFINE_STR32 0\n#endif\n\n#if STR_SUPPORT_STD_STRING\n#include <string>\n#endif\n\n//-------------------------------------------------------------------------\n// HEADERS\n//-------------------------------------------------------------------------\n\n// This is the base class that you can pass around\n// Footprint is 8-bytes (32-bits arch) or 16-bytes (64-bits arch)\nclass STR_API Str\n{\n    char*               Data;                   // Point to LocalBuf() or heap allocated\n    int                 Capacity : 21;          // Max 2 MB. Exclude zero terminator.\n    int                 LocalBufSize : 10;      // Max 1023 bytes\n    unsigned int        Owned : 1;              // Set when we have ownership of the pointed data (most common, unless using set_ref() method or StrRef constructor)\n\npublic:\n    inline char*        c_str()                                 { return Data; }\n    inline const char*  c_str() const                           { return Data; }\n    inline bool         empty() const                           { return Data[0] == 0; }\n    inline int          length() const                          { return (int)strlen(Data); }    // by design, allow user to write into the buffer at any time\n    inline int          capacity() const                        { return Capacity; }\n    inline bool         owned() const                           { return Owned ? true : false; }\n\n    inline void         set_ref(const char* src);\n    int                 setf(const char* fmt, ...);\n    int                 setfv(const char* fmt, va_list args);\n    int                 setf_nogrow(const char* fmt, ...);\n    int                 setfv_nogrow(const char* fmt, va_list args);\n    int                 append(char c);\n    int                 append(const char* s, const char* s_end = NULL);\n    int                 appendf(const char* fmt, ...);\n    int                 appendfv(const char* fmt, va_list args);\n    int                 append_from(int idx, char c);\n    int                 append_from(int idx, const char* s, const char* s_end = NULL);\t\t// If you know the string length or want to append from a certain point\n    int                 appendf_from(int idx, const char* fmt, ...);\n    int                 appendfv_from(int idx, const char* fmt, va_list args);\n\n    void                clear();\n    void                reserve(int cap);\n    void                reserve_discard(int cap);\n    void                shrink_to_fit();\n\n    inline char&        operator[](size_t i)                    { return Data[i]; }\n    inline char         operator[](size_t i) const              { return Data[i]; }\n    //explicit operator const char*() const{ return Data; }\n\n    inline Str();\n    inline Str(const char* rhs);\n    inline void         set(const char* src);\n    inline void         set(const char* src, const char* src_end);\n    inline Str&         operator=(const char* rhs)              { set(rhs); return *this; }\n    inline bool         operator==(const char* rhs) const       { return strcmp(c_str(), rhs) == 0; }\n\n    inline Str(const Str& rhs);\n    inline void         set(const Str& src);\n    inline Str&         operator=(const Str& rhs)               { set(rhs); return *this; }\n    inline bool         operator==(const Str& rhs) const        { return strcmp(c_str(), rhs.c_str()) == 0; }\n\n#if STR_SUPPORT_STD_STRING\n    inline Str(const std::string& rhs);\n    inline void         set(const std::string& src);\n    inline Str&         operator=(const std::string& rhs)       { set(rhs); return *this; }\n    inline bool         operator==(const std::string& rhs)const { return strcmp(c_str(), rhs.c_str()) == 0; }\n#endif\n\n    // Destructor for all variants\n    inline ~Str()\n    {\n        if (Owned && !is_using_local_buf())\n            STR_MEMFREE(Data);\n    }\n\n    static char*        EmptyBuffer;\n\nprotected:\n    inline char*        local_buf()                             { return (char*)this + sizeof(Str); }\n    inline const char*  local_buf() const                       { return (char*)this + sizeof(Str); }\n    inline bool         is_using_local_buf() const              { return Data == local_buf() && LocalBufSize != 0; }\n\n    // Constructor for StrXXX variants with local buffer\n    Str(unsigned short local_buf_size)\n    {\n        STR_ASSERT(local_buf_size < 1024);\n        Data = local_buf();\n        Data[0] = '\\0';\n        Capacity = local_buf_size ? local_buf_size - 1 : 0;\n        LocalBufSize = local_buf_size;\n        Owned = 1;\n    }\n};\n\nvoid    Str::set(const char* src)\n{\n    // We allow set(NULL) or via = operator to clear the string.\n    if (src == NULL)\n    {\n        clear();\n        return;\n    }\n    int buf_len = (int)strlen(src);\n    if (Capacity < buf_len)\n        reserve_discard(buf_len);\n    memcpy(Data, src, (size_t)(buf_len + 1));\n    Owned = 1;\n}\n\nvoid    Str::set(const char* src, const char* src_end)\n{\n    STR_ASSERT(src != NULL && src_end >= src);\n    int buf_len = (int)(src_end - src);\n    if ((int)Capacity < buf_len)\n        reserve_discard(buf_len);\n    memcpy(Data, src, (size_t)buf_len);\n    Data[buf_len] = 0;\n    Owned = 1;\n}\n\nvoid    Str::set(const Str& src)\n{\n    int buf_len = (int)strlen(src.c_str());\n    if ((int)Capacity < buf_len)\n        reserve_discard(buf_len);\n    memcpy(Data, src.c_str(), (size_t)(buf_len + 1));\n    Owned = 1;\n}\n\n#if STR_SUPPORT_STD_STRING\nvoid    Str::set(const std::string& src)\n{\n    int buf_len = (int)src.length();\n    if ((int)Capacity < buf_len)\n        reserve_discard(buf_len);\n    memcpy(Data, src.c_str(), (size_t)(buf_len + 1));\n    Owned = 1;\n}\n#endif\n\ninline void Str::set_ref(const char* src)\n{\n    if (Owned && !is_using_local_buf())\n        STR_MEMFREE(Data);\n    Data = src ? (char*)src : EmptyBuffer;\n    Capacity = 0;\n    Owned = 0;\n}\n\nStr::Str()\n{\n    Data = EmptyBuffer;      // Shared READ-ONLY initial buffer for 0 capacity\n    Capacity = 0;\n    LocalBufSize = 0;\n    Owned = 0;\n}\n\nStr::Str(const Str& rhs) : Str()\n{\n    set(rhs);\n}\n\nStr::Str(const char* rhs) : Str()\n{\n    set(rhs);\n}\n\n#if STR_SUPPORT_STD_STRING\nStr::Str(const std::string& rhs) : Str()\n{\n    set(rhs);\n}\n#endif\n\n// Literal/reference string\nclass StrRef : public Str\n{\npublic:\n    StrRef(const char* s) : Str() { set_ref(s); }\n};\n\n// Types embedding a local buffer\n// NB: we need to override the constructor and = operator for both Str& and TYPENAME (without the later compiler will call a default copy operator)\n#if STR_SUPPORT_STD_STRING\n\n#define STR_DEFINETYPE(TYPENAME, LOCALBUFSIZE)                                      \\\nclass TYPENAME : public Str                                                         \\\n{                                                                                   \\\n    char local_buf[LOCALBUFSIZE];                                                   \\\npublic:                                                                             \\\n    TYPENAME() : Str(LOCALBUFSIZE) {}                                               \\\n    TYPENAME(const Str& rhs) : Str(LOCALBUFSIZE) { set(rhs); }                      \\\n    TYPENAME(const char* rhs) : Str(LOCALBUFSIZE) { set(rhs); }                     \\\n    TYPENAME(const TYPENAME& rhs) : Str(LOCALBUFSIZE) { set(rhs); }                 \\\n    TYPENAME(const std::string& rhs) : Str(LOCALBUFSIZE) { set(rhs); }              \\\n    TYPENAME&   operator=(const char* rhs)          { set(rhs); return *this; }     \\\n    TYPENAME&   operator=(const Str& rhs)           { set(rhs); return *this; }     \\\n    TYPENAME&   operator=(const TYPENAME& rhs)      { set(rhs); return *this; }     \\\n    TYPENAME&   operator=(const std::string& rhs)   { set(rhs); return *this; }     \\\n};\n\n#else\n\n#define STR_DEFINETYPE(TYPENAME, LOCALBUFSIZE)                                      \\\nclass TYPENAME : public Str                                                         \\\n{                                                                                   \\\n    char local_buf[LOCALBUFSIZE];                                                   \\\npublic:                                                                             \\\n    TYPENAME() : Str(LOCALBUFSIZE) {}                                               \\\n    TYPENAME(const Str& rhs) : Str(LOCALBUFSIZE) { set(rhs); }                      \\\n    TYPENAME(const char* rhs) : Str(LOCALBUFSIZE) { set(rhs); }                     \\\n    TYPENAME(const TYPENAME& rhs) : Str(LOCALBUFSIZE) { set(rhs); }                 \\\n    TYPENAME&   operator=(const char* rhs)          { set(rhs); return *this; }     \\\n    TYPENAME&   operator=(const Str& rhs)           { set(rhs); return *this; }     \\\n    TYPENAME&   operator=(const TYPENAME& rhs)      { set(rhs); return *this; }     \\\n};\n\n#endif\n\n// Disable PVS-Studio warning V730: Not all members of a class are initialized inside the constructor (local_buf is not initialized and that is fine)\n// -V:STR_DEFINETYPE:730\n\n// Helper to define StrXXXf constructors\n#define STR_DEFINETYPE_F(TYPENAME, TYPENAME_F)                                      \\\nclass TYPENAME_F : public TYPENAME                                                  \\\n{                                                                                   \\\npublic:                                                                             \\\n    TYPENAME_F(const char* fmt, ...) : TYPENAME() { va_list args; va_start(args, fmt); setfv(fmt, args); va_end(args); } \\\n};\n\n#ifdef __clang__\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunused-private-field\"         // warning : private field 'local_buf' is not used\n#endif\n\n// Declaring types for common sizes here\nSTR_DEFINETYPE(Str16, 16)\nSTR_DEFINETYPE(Str30, 30)\nSTR_DEFINETYPE(Str64, 64)\nSTR_DEFINETYPE(Str128, 128)\nSTR_DEFINETYPE(Str256, 256)\nSTR_DEFINETYPE(Str512, 512)\n\n// Declaring helper constructors to pass in format strings in one statement\nSTR_DEFINETYPE_F(Str16, Str16f)\nSTR_DEFINETYPE_F(Str30, Str30f)\nSTR_DEFINETYPE_F(Str64, Str64f)\nSTR_DEFINETYPE_F(Str128, Str128f)\nSTR_DEFINETYPE_F(Str256, Str256f)\nSTR_DEFINETYPE_F(Str512, Str512f)\n\n#if STR_DEFINE_STR32\nSTR_DEFINETYPE(Str32, 32)\nSTR_DEFINETYPE_F(Str32, Str32f)\n#endif\n\n#ifdef __clang__\n#pragma clang diagnostic pop\n#endif\n\n#endif // #ifndef STR_INCLUDED\n\n//-------------------------------------------------------------------------\n// IMPLEMENTATION\n//-------------------------------------------------------------------------\n\n#ifdef STR_IMPLEMENTATION\n\n#include <stdio.h> // for vsnprintf\n\n// On some platform vsnprintf() takes va_list by reference and modifies it.\n// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.\n#ifndef va_copy\n#define va_copy(dest, src) (dest = src)\n#endif\n\n// Static empty buffer we can point to for empty strings\n// Pointing to a literal increases the like-hood of getting a crash if someone attempts to write in the empty string buffer.\nchar*   Str::EmptyBuffer = (char*)\"\\0NULL\";\n\n// Clear\nvoid    Str::clear()\n{\n    if (Owned && !is_using_local_buf())\n        STR_MEMFREE(Data);\n    if (LocalBufSize)\n    {\n        Data = local_buf();\n        Data[0] = '\\0';\n        Capacity = LocalBufSize - 1;\n        Owned = 1;\n    }\n    else\n    {\n        Data = EmptyBuffer;\n        Capacity = 0;\n        Owned = 0;\n    }\n}\n\n// Reserve memory, preserving the current of the buffer\n// Capacity doesn't include the zero terminator, so reserve(5) is enough to store \"hello\".\nvoid    Str::reserve(int new_capacity)\n{\n    if (new_capacity <= Capacity)\n        return;\n\n    char* new_data;\n    if (new_capacity <= LocalBufSize - 1)\n    {\n        // Disowned -> LocalBuf\n        new_data = local_buf();\n        new_capacity = LocalBufSize - 1;\n    }\n    else\n    {\n        // Disowned or LocalBuf -> Heap\n        new_data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char));\n    }\n\n    // string in Data might be longer than new_capacity if it wasn't owned, don't copy too much\n#ifdef _MSC_VER\n    strncpy_s(new_data, (size_t)new_capacity + 1, Data, (size_t)new_capacity);\n#else\n    strncpy(new_data, Data, (size_t)new_capacity);\n#endif\n    new_data[new_capacity] = 0;\n\n    if (Owned && !is_using_local_buf())\n        STR_MEMFREE(Data);\n\n    Data = new_data;\n    Capacity = new_capacity;\n    Owned = 1;\n}\n\n// Reserve memory, discarding the current of the buffer (if we expect to be fully rewritten)\nvoid    Str::reserve_discard(int new_capacity)\n{\n    if (new_capacity <= Capacity)\n        return;\n\n    if (Owned && !is_using_local_buf())\n        STR_MEMFREE(Data);\n\n    if (new_capacity <= LocalBufSize - 1)\n    {\n        // Disowned -> LocalBuf\n        Data = local_buf();\n        Capacity = LocalBufSize - 1;\n    }\n    else\n    {\n        // Disowned or LocalBuf -> Heap\n        Data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char));\n        Capacity = new_capacity;\n    }\n    Owned = 1;\n}\n\nvoid    Str::shrink_to_fit()\n{\n    if (!Owned || is_using_local_buf())\n        return;\n    int new_capacity = length();\n    if (Capacity <= new_capacity)\n        return;\n\n    char* new_data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char));\n    memcpy(new_data, Data, (size_t)(new_capacity + 1));\n    STR_MEMFREE(Data);\n    Data = new_data;\n    Capacity = new_capacity;\n}\n\n// FIXME: merge setfv() and appendfv()?\nint     Str::setfv(const char* fmt, va_list args)\n{\n    // Needed for portability on platforms where va_list are passed by reference and modified by functions\n    va_list args2;\n    va_copy(args2, args);\n\n    // MSVC returns -1 on overflow when writing, which forces us to do two passes\n    // FIXME-OPT: Find a way around that.\n#ifdef _MSC_VER\n    int len = vsnprintf(NULL, 0, fmt, args);\n    STR_ASSERT(len >= 0);\n\n    if (Capacity < len)\n        reserve_discard(len);\n    len = vsnprintf(Data, (size_t)len + 1, fmt, args2);\n#else\n    // First try\n    int len = vsnprintf(Owned ? Data : NULL, Owned ? (size_t)(Capacity + 1): 0, fmt, args);\n    STR_ASSERT(len >= 0);\n\n    if (Capacity < len)\n    {\n        reserve_discard(len);\n        len = vsnprintf(Data, (size_t)len + 1, fmt, args2);\n    }\n#endif\n\n    STR_ASSERT(Owned);\n    return len;\n}\n\nint     Str::setf(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    int len = setfv(fmt, args);\n    va_end(args);\n    return len;\n}\n\nint     Str::setfv_nogrow(const char* fmt, va_list args)\n{\n    STR_ASSERT(Owned);\n\n    if (Capacity == 0)\n        return 0;\n\n    int w = vsnprintf(Data, (size_t)(Capacity + 1), fmt, args);\n    Data[Capacity] = 0;\n    Owned = 1;\n    return (w == -1) ? Capacity : w;\n}\n\nint     Str::setf_nogrow(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    int len = setfv_nogrow(fmt, args);\n    va_end(args);\n    return len;\n}\n\nint     Str::append_from(int idx, char c)\n{\n    int add_len = 1;\n    if (Capacity < idx + add_len)\n        reserve(idx + add_len);\n    Data[idx] = c;\n    Data[idx + add_len] = 0;\n    STR_ASSERT(Owned);\n    return add_len;\n}\n\nint     Str::append_from(int idx, const char* s, const char* s_end)\n{\n    if (!s_end)\n        s_end = s + strlen(s);\n    int add_len = (int)(s_end - s);\n    if (Capacity < idx + add_len)\n        reserve(idx + add_len);\n    memcpy(Data + idx, (const void*)s, (size_t)add_len);\n    Data[idx + add_len] = 0; // Our source data isn't necessarily zero terminated\n    STR_ASSERT(Owned);\n    return add_len;\n}\n\n// FIXME: merge setfv() and appendfv()?\nint     Str::appendfv_from(int idx, const char* fmt, va_list args)\n{\n    // Needed for portability on platforms where va_list are passed by reference and modified by functions\n    va_list args2;\n    va_copy(args2, args);\n\n    // MSVC returns -1 on overflow when writing, which forces us to do two passes\n    // FIXME-OPT: Find a way around that.\n#ifdef _MSC_VER\n    int add_len = vsnprintf(NULL, 0, fmt, args);\n    STR_ASSERT(add_len >= 0);\n\n    if (Capacity < idx + add_len)\n        reserve(idx + add_len);\n    add_len = vsnprintf(Data + idx, add_len + 1, fmt, args2);\n#else\n    // First try\n    int add_len = vsnprintf(Owned ? Data + idx : NULL, Owned ? (size_t)(Capacity + 1 - idx) : 0, fmt, args);\n    STR_ASSERT(add_len >= 0);\n\n    if (Capacity < idx + add_len)\n    {\n        reserve(idx + add_len);\n        add_len = vsnprintf(Data + idx, (size_t)add_len + 1, fmt, args2);\n    }\n#endif\n\n    STR_ASSERT(Owned);\n    return add_len;\n}\n\nint     Str::appendf_from(int idx, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    int len = appendfv_from(idx, fmt, args);\n    va_end(args);\n    return len;\n}\n\nint     Str::append(char c)\n{\n    int cur_len = length();\n    return append_from(cur_len, c);\n}\n\nint     Str::append(const char* s, const char* s_end)\n{\n    int cur_len = length();\n    return append_from(cur_len, s, s_end);\n}\n\nint     Str::appendfv(const char* fmt, va_list args)\n{\n    int cur_len = length();\n    return appendfv_from(cur_len, fmt, args);\n}\n\nint     Str::appendf(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    int len = appendfv(fmt, args);\n    va_end(args);\n    return len;\n}\n\n#endif // #define STR_IMPLEMENTATION\n\n//-------------------------------------------------------------------------\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/thirdparty/Str/Str.natvis",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<AutoVisualizer xmlns=\"http://schemas.microsoft.com/vstudio/debugger/natvis/2010\">\n\n<Type Name=\"Str\">\n  <DisplayString>{ Data, na }</DisplayString>\n  <Expand>\n    <Item Name=\"Data\">Data, na</Item>\n    <Item Name=\"Size\">strlen(Data)</Item>\n    <Item Name=\"Capacity\">Capacity</Item>\n  </Expand>\n</Type>\n\n</AutoVisualizer>"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/include/thirdparty/stb/imstb_image_write.h",
    "content": "/* stb_image_write - v1.13 - public domain - http://nothings.org/stb/stb_image_write.h\n   writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015\n                                     no warranty implied; use at your own risk\n\n   Before #including,\n\n       #define STB_IMAGE_WRITE_IMPLEMENTATION\n\n   in the file that you want to have the implementation.\n\n   Will probably not work correctly with strict-aliasing optimizations.\n\nABOUT:\n\n   This header file is a library for writing images to C stdio or a callback.\n\n   The PNG output is not optimal; it is 20-50% larger than the file\n   written by a decent optimizing implementation; though providing a custom\n   zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that.\n   This library is designed for source code compactness and simplicity,\n   not optimal image file size or run-time performance.\n\nBUILDING:\n\n   You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h.\n   You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace\n   malloc,realloc,free.\n   You can #define STBIW_MEMMOVE() to replace memmove()\n   You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function\n   for PNG compression (instead of the builtin one), it must have the following signature:\n   unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality);\n   The returned data will be freed with STBIW_FREE() (free() by default),\n   so it must be heap allocated with STBIW_MALLOC() (malloc() by default),\n\nUNICODE:\n\n   If compiling for Windows and you wish to use Unicode filenames, compile\n   with\n       #define STBIW_WINDOWS_UTF8\n   and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert\n   Windows wchar_t filenames to utf8.\n\nUSAGE:\n\n   There are five functions, one for each image file format:\n\n     int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);\n     int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);\n     int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);\n     int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality);\n     int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);\n\n     void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically\n\n   There are also five equivalent functions that use an arbitrary write function. You are\n   expected to open/close your file-equivalent before and after calling these:\n\n     int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data, int stride_in_bytes);\n     int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\n     int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\n     int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);\n     int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);\n\n   where the callback is:\n      void stbi_write_func(void *context, void *data, int size);\n\n   You can configure it with these global variables:\n      int stbi_write_tga_with_rle;             // defaults to true; set to 0 to disable RLE\n      int stbi_write_png_compression_level;    // defaults to 8; set to higher for more compression\n      int stbi_write_force_png_filter;         // defaults to -1; set to 0..5 to force a filter mode\n\n\n   You can define STBI_WRITE_NO_STDIO to disable the file variant of these\n   functions, so the library will not use stdio.h at all. However, this will\n   also disable HDR writing, because it requires stdio for formatted output.\n\n   Each function returns 0 on failure and non-0 on success.\n\n   The functions create an image file defined by the parameters. The image\n   is a rectangle of pixels stored from left-to-right, top-to-bottom.\n   Each pixel contains 'comp' channels of data stored interleaved with 8-bits\n   per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is\n   monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.\n   The *data pointer points to the first byte of the top-left-most pixel.\n   For PNG, \"stride_in_bytes\" is the distance in bytes from the first byte of\n   a row of pixels to the first byte of the next row of pixels.\n\n   PNG creates output files with the same number of components as the input.\n   The BMP format expands Y to RGB in the file format and does not\n   output alpha.\n\n   PNG supports writing rectangles of data even when the bytes storing rows of\n   data are not consecutive in memory (e.g. sub-rectangles of a larger image),\n   by supplying the stride between the beginning of adjacent rows. The other\n   formats do not. (Thus you cannot write a native-format BMP through the BMP\n   writer, both because it is in BGR order and because it may have padding\n   at the end of the line.)\n\n   PNG allows you to set the deflate compression level by setting the global\n   variable 'stbi_write_png_compression_level' (it defaults to 8).\n\n   HDR expects linear float data. Since the format is always 32-bit rgb(e)\n   data, alpha (if provided) is discarded, and for monochrome data it is\n   replicated across all three channels.\n\n   TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed\n   data, set the global variable 'stbi_write_tga_with_rle' to 0.\n   \n   JPEG does ignore alpha channels in input data; quality is between 1 and 100.\n   Higher quality looks better but results in a bigger image.\n   JPEG baseline (no JPEG progressive).\n\nCREDITS:\n\n\n   Sean Barrett           -    PNG/BMP/TGA \n   Baldur Karlsson        -    HDR\n   Jean-Sebastien Guay    -    TGA monochrome\n   Tim Kelsey             -    misc enhancements\n   Alan Hickman           -    TGA RLE\n   Emmanuel Julien        -    initial file IO callback implementation\n   Jon Olick              -    original jo_jpeg.cpp code\n   Daniel Gibson          -    integrate JPEG, allow external zlib\n   Aarni Koskela          -    allow choosing PNG filter\n\n   bugfixes:\n      github:Chribba\n      Guillaume Chereau\n      github:jry2\n      github:romigrou\n      Sergio Gonzalez\n      Jonas Karlsson\n      Filip Wasil\n      Thatcher Ulrich\n      github:poppolopoppo\n      Patrick Boettcher\n      github:xeekworx\n      Cap Petschulat\n      Simon Rodriguez\n      Ivan Tikhonov\n      github:ignotion\n      Adam Schackart\n\nLICENSE\n\n  See end of file for license information.\n\n*/\n\n#ifndef INCLUDE_STB_IMAGE_WRITE_H\n#define INCLUDE_STB_IMAGE_WRITE_H\n\n#include <stdlib.h>\n\n// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline'\n#ifndef STBIWDEF\n#ifdef STB_IMAGE_WRITE_STATIC\n#define STBIWDEF  static\n#else\n#ifdef __cplusplus\n#define STBIWDEF  extern \"C\"\n#else\n#define STBIWDEF  extern\n#endif\n#endif\n#endif\n\n#ifndef STB_IMAGE_WRITE_STATIC  // C++ forbids static forward declarations\nextern int stbi_write_tga_with_rle;\nextern int stbi_write_png_compression_level;\nextern int stbi_write_force_png_filter;\n#endif\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void  *data, int stride_in_bytes);\nSTBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);\nSTBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void  *data, int quality);\n\n#ifdef STBI_WINDOWS_UTF8\nSTBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);\n#endif\n#endif\n\ntypedef void stbi_write_func(void *context, void *data, int size);\n\nSTBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data, int stride_in_bytes);\nSTBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);\nSTBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void  *data, int quality);\n\nSTBIWDEF void stbi_flip_vertically_on_write(int flip_boolean);\n\n#endif//INCLUDE_STB_IMAGE_WRITE_H\n\n#ifdef STB_IMAGE_WRITE_IMPLEMENTATION\n\n#ifdef _WIN32\n   #ifndef _CRT_SECURE_NO_WARNINGS\n   #define _CRT_SECURE_NO_WARNINGS\n   #endif\n   #ifndef _CRT_NONSTDC_NO_DEPRECATE\n   #define _CRT_NONSTDC_NO_DEPRECATE\n   #endif\n#endif\n\n#ifndef STBI_WRITE_NO_STDIO\n#include <stdio.h>\n#endif // STBI_WRITE_NO_STDIO\n\n#include <stdarg.h>\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n\n#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED))\n// ok\n#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED)\n// ok\n#else\n#error \"Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED).\"\n#endif\n\n#ifndef STBIW_MALLOC\n#define STBIW_MALLOC(sz)        malloc(sz)\n#define STBIW_REALLOC(p,newsz)  realloc(p,newsz)\n#define STBIW_FREE(p)           free(p)\n#endif\n\n#ifndef STBIW_REALLOC_SIZED\n#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz)\n#endif\n\n\n#ifndef STBIW_MEMMOVE\n#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz)\n#endif\n\n\n#ifndef STBIW_ASSERT\n#include <assert.h>\n#define STBIW_ASSERT(x) assert(x)\n#endif\n\n#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)\n\n#ifdef STB_IMAGE_WRITE_STATIC\nstatic int stbi__flip_vertically_on_write=0;\nstatic int stbi_write_png_compression_level = 8;\nstatic int stbi_write_tga_with_rle = 1;\nstatic int stbi_write_force_png_filter = -1;\n#else\nint stbi_write_png_compression_level = 8;\nint stbi__flip_vertically_on_write=0;\nint stbi_write_tga_with_rle = 1;\nint stbi_write_force_png_filter = -1;\n#endif\n\nSTBIWDEF void stbi_flip_vertically_on_write(int flag)\n{\n   stbi__flip_vertically_on_write = flag;\n}\n\ntypedef struct\n{\n   stbi_write_func *func;\n   void *context;\n} stbi__write_context;\n\n// initialize a callback-based context\nstatic void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context)\n{\n   s->func    = c;\n   s->context = context;\n}\n\n#ifndef STBI_WRITE_NO_STDIO\n\nstatic void stbi__stdio_write(void *context, void *data, int size)\n{\n   fwrite(data,1,size,(FILE*) context);\n}\n\n#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)\n#ifdef __cplusplus\n#define STBIW_EXTERN extern \"C\"\n#else\n#define STBIW_EXTERN extern\n#endif\nSTBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);\nSTBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);\n\nSTBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)\n{\n\treturn WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);\n}\n#endif\n\nstatic FILE *stbiw__fopen(char const *filename, char const *mode)\n{\n   FILE *f;\n#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)\n   wchar_t wMode[64];\n   wchar_t wFilename[1024];\n\tif (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))\n      return 0;\n\t\n\tif (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))\n      return 0;\n\n#if _MSC_VER >= 1400\n\tif (0 != _wfopen_s(&f, wFilename, wMode))\n\t\tf = 0;\n#else\n   f = _wfopen(wFilename, wMode);\n#endif\n\n#elif defined(_MSC_VER) && _MSC_VER >= 1400\n   if (0 != fopen_s(&f, filename, mode))\n      f=0;\n#else\n   f = fopen(filename, mode);\n#endif\n   return f;\n}\n\nstatic int stbi__start_write_file(stbi__write_context *s, const char *filename)\n{\n   FILE *f = stbiw__fopen(filename, \"wb\");\n   stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f);\n   return f != NULL;\n}\n\nstatic void stbi__end_write_file(stbi__write_context *s)\n{\n   fclose((FILE *)s->context);\n}\n\n#endif // !STBI_WRITE_NO_STDIO\n\ntypedef unsigned int stbiw_uint32;\ntypedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];\n\nstatic void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v)\n{\n   while (*fmt) {\n      switch (*fmt++) {\n         case ' ': break;\n         case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int));\n                     s->func(s->context,&x,1);\n                     break; }\n         case '2': { int x = va_arg(v,int);\n                     unsigned char b[2];\n                     b[0] = STBIW_UCHAR(x);\n                     b[1] = STBIW_UCHAR(x>>8);\n                     s->func(s->context,b,2);\n                     break; }\n         case '4': { stbiw_uint32 x = va_arg(v,int);\n                     unsigned char b[4];\n                     b[0]=STBIW_UCHAR(x);\n                     b[1]=STBIW_UCHAR(x>>8);\n                     b[2]=STBIW_UCHAR(x>>16);\n                     b[3]=STBIW_UCHAR(x>>24);\n                     s->func(s->context,b,4);\n                     break; }\n         default:\n            STBIW_ASSERT(0);\n            return;\n      }\n   }\n}\n\nstatic void stbiw__writef(stbi__write_context *s, const char *fmt, ...)\n{\n   va_list v;\n   va_start(v, fmt);\n   stbiw__writefv(s, fmt, v);\n   va_end(v);\n}\n\nstatic void stbiw__putc(stbi__write_context *s, unsigned char c)\n{\n   s->func(s->context, &c, 1);\n}\n\nstatic void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)\n{\n   unsigned char arr[3];\n   arr[0] = a; arr[1] = b; arr[2] = c;\n   s->func(s->context, arr, 3);\n}\n\nstatic void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)\n{\n   unsigned char bg[3] = { 255, 0, 255}, px[3];\n   int k;\n\n   if (write_alpha < 0)\n      s->func(s->context, &d[comp - 1], 1);\n\n   switch (comp) {\n      case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case\n      case 1:\n         if (expand_mono)\n            stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp\n         else\n            s->func(s->context, d, 1);  // monochrome TGA\n         break;\n      case 4:\n         if (!write_alpha) {\n            // composite against pink background\n            for (k = 0; k < 3; ++k)\n               px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;\n            stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);\n            break;\n         }\n         /* FALLTHROUGH */\n      case 3:\n         stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);\n         break;\n   }\n   if (write_alpha > 0)\n      s->func(s->context, &d[comp - 1], 1);\n}\n\nstatic void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)\n{\n   stbiw_uint32 zero = 0;\n   int i,j, j_end;\n\n   if (y <= 0)\n      return;\n\n   if (stbi__flip_vertically_on_write)\n      vdir *= -1;\n\n   if (vdir < 0) {\n      j_end = -1; j = y-1;\n   } else {\n      j_end =  y; j = 0;\n   }\n\n   for (; j != j_end; j += vdir) {\n      for (i=0; i < x; ++i) {\n         unsigned char *d = (unsigned char *) data + (j*x+i)*comp;\n         stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);\n      }\n      s->func(s->context, &zero, scanline_pad);\n   }\n}\n\nstatic int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)\n{\n   if (y < 0 || x < 0) {\n      return 0;\n   } else {\n      va_list v;\n      va_start(v, fmt);\n      stbiw__writefv(s, fmt, v);\n      va_end(v);\n      stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);\n      return 1;\n   }\n}\n\nstatic int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data)\n{\n   int pad = (-x*3) & 3;\n   return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,\n           \"11 4 22 4\" \"4 44 22 444444\",\n           'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40,  // file header\n            40, x,y, 1,24, 0,0,0,0,0,0);             // bitmap header\n}\n\nSTBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s;\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_bmp_core(&s, x, y, comp, data);\n}\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s;\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_bmp_core(&s, x, y, comp, data);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif //!STBI_WRITE_NO_STDIO\n\nstatic int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data)\n{\n   int has_alpha = (comp == 2 || comp == 4);\n   int colorbytes = has_alpha ? comp-1 : comp;\n   int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3\n\n   if (y < 0 || x < 0)\n      return 0;\n\n   if (!stbi_write_tga_with_rle) {\n      return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,\n         \"111 221 2222 11\", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);\n   } else {\n      int i,j,k;\n      int jend, jdir;\n\n      stbiw__writef(s, \"111 221 2222 11\", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8);\n\n      if (stbi__flip_vertically_on_write) {\n         j = 0;\n         jend = y;\n         jdir = 1;\n      } else {\n         j = y-1;\n         jend = -1;\n         jdir = -1;\n      }\n      for (; j != jend; j += jdir) {\n         unsigned char *row = (unsigned char *) data + j * x * comp;\n         int len;\n\n         for (i = 0; i < x; i += len) {\n            unsigned char *begin = row + i * comp;\n            int diff = 1;\n            len = 1;\n\n            if (i < x - 1) {\n               ++len;\n               diff = memcmp(begin, row + (i + 1) * comp, comp);\n               if (diff) {\n                  const unsigned char *prev = begin;\n                  for (k = i + 2; k < x && len < 128; ++k) {\n                     if (memcmp(prev, row + k * comp, comp)) {\n                        prev += comp;\n                        ++len;\n                     } else {\n                        --len;\n                        break;\n                     }\n                  }\n               } else {\n                  for (k = i + 2; k < x && len < 128; ++k) {\n                     if (!memcmp(begin, row + k * comp, comp)) {\n                        ++len;\n                     } else {\n                        break;\n                     }\n                  }\n               }\n            }\n\n            if (diff) {\n               unsigned char header = STBIW_UCHAR(len - 1);\n               s->func(s->context, &header, 1);\n               for (k = 0; k < len; ++k) {\n                  stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);\n               }\n            } else {\n               unsigned char header = STBIW_UCHAR(len - 129);\n               s->func(s->context, &header, 1);\n               stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);\n            }\n         }\n      }\n   }\n   return 1;\n}\n\nSTBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s;\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_tga_core(&s, x, y, comp, (void *) data);\n}\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s;\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif\n\n// *************************************************************************************************\n// Radiance RGBE HDR writer\n// by Baldur Karlsson\n\n#define stbiw__max(a, b)  ((a) > (b) ? (a) : (b))\n\nstatic void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)\n{\n   int exponent;\n   float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));\n\n   if (maxcomp < 1e-32f) {\n      rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;\n   } else {\n      float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;\n\n      rgbe[0] = (unsigned char)(linear[0] * normalize);\n      rgbe[1] = (unsigned char)(linear[1] * normalize);\n      rgbe[2] = (unsigned char)(linear[2] * normalize);\n      rgbe[3] = (unsigned char)(exponent + 128);\n   }\n}\n\nstatic void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte)\n{\n   unsigned char lengthbyte = STBIW_UCHAR(length+128);\n   STBIW_ASSERT(length+128 <= 255);\n   s->func(s->context, &lengthbyte, 1);\n   s->func(s->context, &databyte, 1);\n}\n\nstatic void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data)\n{\n   unsigned char lengthbyte = STBIW_UCHAR(length);\n   STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code\n   s->func(s->context, &lengthbyte, 1);\n   s->func(s->context, data, length);\n}\n\nstatic void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)\n{\n   unsigned char scanlineheader[4] = { 2, 2, 0, 0 };\n   unsigned char rgbe[4];\n   float linear[3];\n   int x;\n\n   scanlineheader[2] = (width&0xff00)>>8;\n   scanlineheader[3] = (width&0x00ff);\n\n   /* skip RLE for images too small or large */\n   if (width < 8 || width >= 32768) {\n      for (x=0; x < width; x++) {\n         switch (ncomp) {\n            case 4: /* fallthrough */\n            case 3: linear[2] = scanline[x*ncomp + 2];\n                    linear[1] = scanline[x*ncomp + 1];\n                    linear[0] = scanline[x*ncomp + 0];\n                    break;\n            default:\n                    linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];\n                    break;\n         }\n         stbiw__linear_to_rgbe(rgbe, linear);\n         s->func(s->context, rgbe, 4);\n      }\n   } else {\n      int c,r;\n      /* encode into scratch buffer */\n      for (x=0; x < width; x++) {\n         switch(ncomp) {\n            case 4: /* fallthrough */\n            case 3: linear[2] = scanline[x*ncomp + 2];\n                    linear[1] = scanline[x*ncomp + 1];\n                    linear[0] = scanline[x*ncomp + 0];\n                    break;\n            default:\n                    linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];\n                    break;\n         }\n         stbiw__linear_to_rgbe(rgbe, linear);\n         scratch[x + width*0] = rgbe[0];\n         scratch[x + width*1] = rgbe[1];\n         scratch[x + width*2] = rgbe[2];\n         scratch[x + width*3] = rgbe[3];\n      }\n\n      s->func(s->context, scanlineheader, 4);\n\n      /* RLE each component separately */\n      for (c=0; c < 4; c++) {\n         unsigned char *comp = &scratch[width*c];\n\n         x = 0;\n         while (x < width) {\n            // find first run\n            r = x;\n            while (r+2 < width) {\n               if (comp[r] == comp[r+1] && comp[r] == comp[r+2])\n                  break;\n               ++r;\n            }\n            if (r+2 >= width)\n               r = width;\n            // dump up to first run\n            while (x < r) {\n               int len = r-x;\n               if (len > 128) len = 128;\n               stbiw__write_dump_data(s, len, &comp[x]);\n               x += len;\n            }\n            // if there's a run, output it\n            if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd\n               // find next byte after run\n               while (r < width && comp[r] == comp[x])\n                  ++r;\n               // output run up to r\n               while (x < r) {\n                  int len = r-x;\n                  if (len > 127) len = 127;\n                  stbiw__write_run_data(s, len, comp[x]);\n                  x += len;\n               }\n            }\n         }\n      }\n   }\n}\n\nstatic int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data)\n{\n   if (y <= 0 || x <= 0 || data == NULL)\n      return 0;\n   else {\n      // Each component is stored separately. Allocate scratch space for full output scanline.\n      unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);\n      int i, len;\n      char buffer[128];\n      char header[] = \"#?RADIANCE\\n# Written by stb_image_write.h\\nFORMAT=32-bit_rle_rgbe\\n\";\n      s->func(s->context, header, sizeof(header)-1);\n\n#ifdef __STDC_WANT_SECURE_LIB__\n      len = sprintf_s(buffer, sizeof(buffer), \"EXPOSURE=          1.0000000000000\\n\\n-Y %d +X %d\\n\", y, x);\n#else\n      len = sprintf(buffer, \"EXPOSURE=          1.0000000000000\\n\\n-Y %d +X %d\\n\", y, x);\n#endif\n      s->func(s->context, buffer, len);\n\n      for(i=0; i < y; i++)\n         stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i));\n      STBIW_FREE(scratch);\n      return 1;\n   }\n}\n\nSTBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data)\n{\n   stbi__write_context s;\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_hdr_core(&s, x, y, comp, (float *) data);\n}\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)\n{\n   stbi__write_context s;\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif // STBI_WRITE_NO_STDIO\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// PNG writer\n//\n\n#ifndef STBIW_ZLIB_COMPRESS\n// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()\n#define stbiw__sbraw(a) ((int *) (a) - 2)\n#define stbiw__sbm(a)   stbiw__sbraw(a)[0]\n#define stbiw__sbn(a)   stbiw__sbraw(a)[1]\n\n#define stbiw__sbneedgrow(a,n)  ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a))\n#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0)\n#define stbiw__sbgrow(a,n)  stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a)))\n\n#define stbiw__sbpush(a, v)      (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v))\n#define stbiw__sbcount(a)        ((a) ? stbiw__sbn(a) : 0)\n#define stbiw__sbfree(a)         ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0)\n\nstatic void *stbiw__sbgrowf(void **arr, int increment, int itemsize)\n{\n   int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1;\n   void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);\n   STBIW_ASSERT(p);\n   if (p) {\n      if (!*arr) ((int *) p)[1] = 0;\n      *arr = (void *) ((int *) p + 2);\n      stbiw__sbm(*arr) = m;\n   }\n   return *arr;\n}\n\nstatic unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)\n{\n   while (*bitcount >= 8) {\n      stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));\n      *bitbuffer >>= 8;\n      *bitcount -= 8;\n   }\n   return data;\n}\n\nstatic int stbiw__zlib_bitrev(int code, int codebits)\n{\n   int res=0;\n   while (codebits--) {\n      res = (res << 1) | (code & 1);\n      code >>= 1;\n   }\n   return res;\n}\n\nstatic unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit)\n{\n   int i;\n   for (i=0; i < limit && i < 258; ++i)\n      if (a[i] != b[i]) break;\n   return i;\n}\n\nstatic unsigned int stbiw__zhash(unsigned char *data)\n{\n   stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);\n   hash ^= hash << 3;\n   hash += hash >> 5;\n   hash ^= hash << 4;\n   hash += hash >> 17;\n   hash ^= hash << 25;\n   hash += hash >> 6;\n   return hash;\n}\n\n#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount))\n#define stbiw__zlib_add(code,codebits) \\\n      (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush())\n#define stbiw__zlib_huffa(b,c)  stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c)\n// default huffman tables\n#define stbiw__zlib_huff1(n)  stbiw__zlib_huffa(0x30 + (n), 8)\n#define stbiw__zlib_huff2(n)  stbiw__zlib_huffa(0x190 + (n)-144, 9)\n#define stbiw__zlib_huff3(n)  stbiw__zlib_huffa(0 + (n)-256,7)\n#define stbiw__zlib_huff4(n)  stbiw__zlib_huffa(0xc0 + (n)-280,8)\n#define stbiw__zlib_huff(n)  ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n))\n#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n))\n\n#define stbiw__ZHASH   16384\n\n#endif // STBIW_ZLIB_COMPRESS\n\nSTBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)\n{\n#ifdef STBIW_ZLIB_COMPRESS\n   // user provided a zlib compress implementation, use that\n   return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality);\n#else // use builtin\n   static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };\n   static unsigned char  lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,  4,  5,  5,  5,  5,  0 };\n   static unsigned short distc[]   = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };\n   static unsigned char  disteb[]  = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };\n   unsigned int bitbuf=0;\n   int i,j, bitcount=0;\n   unsigned char *out = NULL;\n   unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**));\n   if (hash_table == NULL)\n      return NULL;\n   if (quality < 5) quality = 5;\n\n   stbiw__sbpush(out, 0x78);   // DEFLATE 32K window\n   stbiw__sbpush(out, 0x5e);   // FLEVEL = 1\n   stbiw__zlib_add(1,1);  // BFINAL = 1\n   stbiw__zlib_add(1,2);  // BTYPE = 1 -- fixed huffman\n\n   for (i=0; i < stbiw__ZHASH; ++i)\n      hash_table[i] = NULL;\n\n   i=0;\n   while (i < data_len-3) {\n      // hash next 3 bytes of data to be compressed\n      int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3;\n      unsigned char *bestloc = 0;\n      unsigned char **hlist = hash_table[h];\n      int n = stbiw__sbcount(hlist);\n      for (j=0; j < n; ++j) {\n         if (hlist[j]-data > i-32768) { // if entry lies within window\n            int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i);\n            if (d >= best) { best=d; bestloc=hlist[j]; }\n         }\n      }\n      // when hash table entry is too long, delete half the entries\n      if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) {\n         STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);\n         stbiw__sbn(hash_table[h]) = quality;\n      }\n      stbiw__sbpush(hash_table[h],data+i);\n\n      if (bestloc) {\n         // \"lazy matching\" - check match at *next* byte, and if it's better, do cur byte as literal\n         h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1);\n         hlist = hash_table[h];\n         n = stbiw__sbcount(hlist);\n         for (j=0; j < n; ++j) {\n            if (hlist[j]-data > i-32767) {\n               int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1);\n               if (e > best) { // if next match is better, bail on current match\n                  bestloc = NULL;\n                  break;\n               }\n            }\n         }\n      }\n\n      if (bestloc) {\n         int d = (int) (data+i - bestloc); // distance back\n         STBIW_ASSERT(d <= 32767 && best <= 258);\n         for (j=0; best > lengthc[j+1]-1; ++j);\n         stbiw__zlib_huff(j+257);\n         if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]);\n         for (j=0; d > distc[j+1]-1; ++j);\n         stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5);\n         if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]);\n         i += best;\n      } else {\n         stbiw__zlib_huffb(data[i]);\n         ++i;\n      }\n   }\n   // write out final bytes\n   for (;i < data_len; ++i)\n      stbiw__zlib_huffb(data[i]);\n   stbiw__zlib_huff(256); // end of block\n   // pad with 0 bits to byte boundary\n   while (bitcount)\n      stbiw__zlib_add(0,1);\n\n   for (i=0; i < stbiw__ZHASH; ++i)\n      (void) stbiw__sbfree(hash_table[i]);\n   STBIW_FREE(hash_table);\n\n   {\n      // compute adler32 on input\n      unsigned int s1=1, s2=0;\n      int blocklen = (int) (data_len % 5552);\n      j=0;\n      while (j < data_len) {\n         for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; }\n         s1 %= 65521; s2 %= 65521;\n         j += blocklen;\n         blocklen = 5552;\n      }\n      stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8));\n      stbiw__sbpush(out, STBIW_UCHAR(s2));\n      stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8));\n      stbiw__sbpush(out, STBIW_UCHAR(s1));\n   }\n   *out_len = stbiw__sbn(out);\n   // make returned pointer freeable\n   STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len);\n   return (unsigned char *) stbiw__sbraw(out);\n#endif // STBIW_ZLIB_COMPRESS\n}\n\nstatic unsigned int stbiw__crc32(unsigned char *buffer, int len)\n{\n#ifdef STBIW_CRC32\n    return STBIW_CRC32(buffer, len);\n#else\n   static unsigned int crc_table[256] =\n   {\n      0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,\n      0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,\n      0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,\n      0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,\n      0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,\n      0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,\n      0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,\n      0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,\n      0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,\n      0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,\n      0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,\n      0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,\n      0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,\n      0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,\n      0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,\n      0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,\n      0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,\n      0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,\n      0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,\n      0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,\n      0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,\n      0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,\n      0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,\n      0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,\n      0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,\n      0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,\n      0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,\n      0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,\n      0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,\n      0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,\n      0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,\n      0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D\n   };\n\n   unsigned int crc = ~0u;\n   int i;\n   for (i=0; i < len; ++i)\n      crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];\n   return ~crc;\n#endif\n}\n\n#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4)\n#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));\n#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3])\n\nstatic void stbiw__wpcrc(unsigned char **data, int len)\n{\n   unsigned int crc = stbiw__crc32(*data - len - 4, len+4);\n   stbiw__wp32(*data, crc);\n}\n\nstatic unsigned char stbiw__paeth(int a, int b, int c)\n{\n   int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);\n   if (pa <= pb && pa <= pc) return STBIW_UCHAR(a);\n   if (pb <= pc) return STBIW_UCHAR(b);\n   return STBIW_UCHAR(c);\n}\n\n// @OPTIMIZE: provide an option that always forces left-predict or paeth predict\nstatic void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer)\n{\n   static int mapping[] = { 0,1,2,3,4 };\n   static int firstmap[] = { 0,1,0,5,6 };\n   int *mymap = (y != 0) ? mapping : firstmap;\n   int i;\n   int type = mymap[filter_type];\n   unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y);\n   int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes;\n    \n   if (type==0) {\n      memcpy(line_buffer, z, width*n);\n      return;\n   }\n\n   // first loop isn't optimized since it's just one pixel    \n   for (i = 0; i < n; ++i) {\n      switch (type) {\n         case 1: line_buffer[i] = z[i]; break;\n         case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break;\n         case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break;\n         case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break;\n         case 5: line_buffer[i] = z[i]; break;\n         case 6: line_buffer[i] = z[i]; break;\n      }\n   }\n   switch (type) {\n      case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break;\n      case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break;\n      case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break;\n      case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break;\n      case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break;\n      case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break;\n   }\n}\n\nSTBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)\n{\n   int force_filter = stbi_write_force_png_filter;\n   int ctype[5] = { -1, 0, 4, 2, 6 };\n   unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };\n   unsigned char *out,*o, *filt, *zlib;\n   signed char *line_buffer;\n   int j,zlen;\n\n   if (stride_bytes == 0)\n      stride_bytes = x * n;\n\n   if (force_filter >= 5) {\n      force_filter = -1;\n   }\n\n   filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0;\n   line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; }\n   for (j=0; j < y; ++j) {\n      int filter_type;\n      if (force_filter > -1) {\n         filter_type = force_filter;\n         stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer);\n      } else { // Estimate the best filter by running through all of them:\n         int best_filter = 0, best_filter_val = 0x7fffffff, est, i;\n         for (filter_type = 0; filter_type < 5; filter_type++) {\n            stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer);\n\n            // Estimate the entropy of the line using this filter; the less, the better.\n            est = 0;\n            for (i = 0; i < x*n; ++i) {\n               est += abs((signed char) line_buffer[i]);\n            }\n            if (est < best_filter_val) {\n               best_filter_val = est;\n               best_filter = filter_type;\n            }\n         }\n         if (filter_type != best_filter) {  // If the last iteration already got us the best filter, don't redo it\n            stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer);\n            filter_type = best_filter;\n         }\n      }\n      // when we get here, filter_type contains the filter type, and line_buffer contains the data\n      filt[j*(x*n+1)] = (unsigned char) filter_type;\n      STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);\n   }\n   STBIW_FREE(line_buffer);\n   zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level);\n   STBIW_FREE(filt);\n   if (!zlib) return 0;\n\n   // each tag requires 12 bytes of overhead\n   out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12);\n   if (!out) return 0;\n   *out_len = 8 + 12+13 + 12+zlen + 12;\n\n   o=out;\n   STBIW_MEMMOVE(o,sig,8); o+= 8;\n   stbiw__wp32(o, 13); // header length\n   stbiw__wptag(o, \"IHDR\");\n   stbiw__wp32(o, x);\n   stbiw__wp32(o, y);\n   *o++ = 8;\n   *o++ = STBIW_UCHAR(ctype[n]);\n   *o++ = 0;\n   *o++ = 0;\n   *o++ = 0;\n   stbiw__wpcrc(&o,13);\n\n   stbiw__wp32(o, zlen);\n   stbiw__wptag(o, \"IDAT\");\n   STBIW_MEMMOVE(o, zlib, zlen);\n   o += zlen;\n   STBIW_FREE(zlib);\n   stbiw__wpcrc(&o, zlen);\n\n   stbiw__wp32(o,0);\n   stbiw__wptag(o, \"IEND\");\n   stbiw__wpcrc(&o,0);\n\n   STBIW_ASSERT(o == out + *out_len);\n\n   return out;\n}\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)\n{\n   FILE *f;\n   int len;\n   unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);\n   if (png == NULL) return 0;\n\n   f = stbiw__fopen(filename, \"wb\");\n   if (!f) { STBIW_FREE(png); return 0; }\n   fwrite(png, 1, len, f);\n   fclose(f);\n   STBIW_FREE(png);\n   return 1;\n}\n#endif\n\nSTBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes)\n{\n   int len;\n   unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);\n   if (png == NULL) return 0;\n   func(context, png, len);\n   STBIW_FREE(png);\n   return 1;\n}\n\n\n/* ***************************************************************************\n *\n * JPEG writer\n *\n * This is based on Jon Olick's jo_jpeg.cpp:\n * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html\n */\n\nstatic const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,\n      24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };\n\nstatic void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) {\n   int bitBuf = *bitBufP, bitCnt = *bitCntP;\n   bitCnt += bs[1];\n   bitBuf |= bs[0] << (24 - bitCnt);\n   while(bitCnt >= 8) {\n      unsigned char c = (bitBuf >> 16) & 255;\n      stbiw__putc(s, c);\n      if(c == 255) {\n         stbiw__putc(s, 0);\n      }\n      bitBuf <<= 8;\n      bitCnt -= 8;\n   }\n   *bitBufP = bitBuf;\n   *bitCntP = bitCnt;\n}\n\nstatic void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) {\n   float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p;\n   float z1, z2, z3, z4, z5, z11, z13;\n\n   float tmp0 = d0 + d7;\n   float tmp7 = d0 - d7;\n   float tmp1 = d1 + d6;\n   float tmp6 = d1 - d6;\n   float tmp2 = d2 + d5;\n   float tmp5 = d2 - d5;\n   float tmp3 = d3 + d4;\n   float tmp4 = d3 - d4;\n\n   // Even part\n   float tmp10 = tmp0 + tmp3;   // phase 2\n   float tmp13 = tmp0 - tmp3;\n   float tmp11 = tmp1 + tmp2;\n   float tmp12 = tmp1 - tmp2;\n\n   d0 = tmp10 + tmp11;       // phase 3\n   d4 = tmp10 - tmp11;\n\n   z1 = (tmp12 + tmp13) * 0.707106781f; // c4\n   d2 = tmp13 + z1;       // phase 5\n   d6 = tmp13 - z1;\n\n   // Odd part\n   tmp10 = tmp4 + tmp5;       // phase 2\n   tmp11 = tmp5 + tmp6;\n   tmp12 = tmp6 + tmp7;\n\n   // The rotator is modified from fig 4-8 to avoid extra negations.\n   z5 = (tmp10 - tmp12) * 0.382683433f; // c6\n   z2 = tmp10 * 0.541196100f + z5; // c2-c6\n   z4 = tmp12 * 1.306562965f + z5; // c2+c6\n   z3 = tmp11 * 0.707106781f; // c4\n\n   z11 = tmp7 + z3;      // phase 5\n   z13 = tmp7 - z3;\n\n   *d5p = z13 + z2;         // phase 6\n   *d3p = z13 - z2;\n   *d1p = z11 + z4;\n   *d7p = z11 - z4;\n\n   *d0p = d0;  *d2p = d2;  *d4p = d4;  *d6p = d6;\n}\n\nstatic void stbiw__jpg_calcBits(int val, unsigned short bits[2]) {\n   int tmp1 = val < 0 ? -val : val;\n   val = val < 0 ? val-1 : val;\n   bits[1] = 1;\n   while(tmp1 >>= 1) {\n      ++bits[1];\n   }\n   bits[0] = val & ((1<<bits[1])-1);\n}\n\nstatic int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) {\n   const unsigned short EOB[2] = { HTAC[0x00][0], HTAC[0x00][1] };\n   const unsigned short M16zeroes[2] = { HTAC[0xF0][0], HTAC[0xF0][1] };\n   int dataOff, i, diff, end0pos;\n   int DU[64];\n\n   // DCT rows\n   for(dataOff=0; dataOff<64; dataOff+=8) {\n      stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]);\n   }\n   // DCT columns\n   for(dataOff=0; dataOff<8; ++dataOff) {\n      stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+8], &CDU[dataOff+16], &CDU[dataOff+24], &CDU[dataOff+32], &CDU[dataOff+40], &CDU[dataOff+48], &CDU[dataOff+56]);\n   }\n   // Quantize/descale/zigzag the coefficients\n   for(i=0; i<64; ++i) {\n      float v = CDU[i]*fdtbl[i];\n      // DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));\n      // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?\n      DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? v - 0.5f : v + 0.5f);\n   }\n\n   // Encode DC\n   diff = DU[0] - DC;\n   if (diff == 0) {\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]);\n   } else {\n      unsigned short bits[2];\n      stbiw__jpg_calcBits(diff, bits);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);\n   }\n   // Encode ACs\n   end0pos = 63;\n   for(; (end0pos>0)&&(DU[end0pos]==0); --end0pos) {\n   }\n   // end0pos = first element in reverse order !=0\n   if(end0pos == 0) {\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);\n      return DU[0];\n   }\n   for(i = 1; i <= end0pos; ++i) {\n      int startpos = i;\n      int nrzeroes;\n      unsigned short bits[2];\n      for (; DU[i]==0 && i<=end0pos; ++i) {\n      }\n      nrzeroes = i-startpos;\n      if ( nrzeroes >= 16 ) {\n         int lng = nrzeroes>>4;\n         int nrmarker;\n         for (nrmarker=1; nrmarker <= lng; ++nrmarker)\n            stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes);\n         nrzeroes &= 15;\n      }\n      stbiw__jpg_calcBits(DU[i], bits);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);\n   }\n   if(end0pos != 63) {\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);\n   }\n   return DU[0];\n}\n\nstatic int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) {\n   // Constants that don't pollute global namespace\n   static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0};\n   static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};\n   static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d};\n   static const unsigned char std_ac_luminance_values[] = {\n      0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,\n      0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,\n      0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,\n      0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,\n      0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,\n      0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,\n      0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa\n   };\n   static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0};\n   static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};\n   static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77};\n   static const unsigned char std_ac_chrominance_values[] = {\n      0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,\n      0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,\n      0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,\n      0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,\n      0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,\n      0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,\n      0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa\n   };\n   // Huffman tables\n   static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}};\n   static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}};\n   static const unsigned short YAC_HT[256][2] = {\n      {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}\n   };\n   static const unsigned short UVAC_HT[256][2] = {\n      {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}\n   };\n   static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22,\n                             37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99};\n   static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99,\n                              99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99};\n   static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, \n                                 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f };\n\n   int row, col, i, k;\n   float fdtbl_Y[64], fdtbl_UV[64];\n   unsigned char YTable[64], UVTable[64];\n\n   if(!data || !width || !height || comp > 4 || comp < 1) {\n      return 0;\n   }\n\n   quality = quality ? quality : 90;\n   quality = quality < 1 ? 1 : quality > 100 ? 100 : quality;\n   quality = quality < 50 ? 5000 / quality : 200 - quality * 2;\n\n   for(i = 0; i < 64; ++i) {\n      int uvti, yti = (YQT[i]*quality+50)/100;\n      YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti);\n      uvti = (UVQT[i]*quality+50)/100;\n      UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti);\n   }\n\n   for(row = 0, k = 0; row < 8; ++row) {\n      for(col = 0; col < 8; ++col, ++k) {\n         fdtbl_Y[k]  = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);\n         fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);\n      }\n   }\n\n   // Write Headers\n   {\n      static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 };\n      static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 };\n      const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width),\n                                      3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 };\n      s->func(s->context, (void*)head0, sizeof(head0));\n      s->func(s->context, (void*)YTable, sizeof(YTable));\n      stbiw__putc(s, 1);\n      s->func(s->context, UVTable, sizeof(UVTable));\n      s->func(s->context, (void*)head1, sizeof(head1));\n      s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1);\n      s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values));\n      stbiw__putc(s, 0x10); // HTYACinfo\n      s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1);\n      s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values));\n      stbiw__putc(s, 1); // HTUDCinfo\n      s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1);\n      s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values));\n      stbiw__putc(s, 0x11); // HTUACinfo\n      s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1);\n      s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values));\n      s->func(s->context, (void*)head2, sizeof(head2));\n   }\n\n   // Encode 8x8 macroblocks\n   {\n      static const unsigned short fillBits[] = {0x7F, 7};\n      const unsigned char *imageData = (const unsigned char *)data;\n      int DCY=0, DCU=0, DCV=0;\n      int bitBuf=0, bitCnt=0;\n      // comp == 2 is grey+alpha (alpha is ignored)\n      int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;\n      int x, y, pos;\n      for(y = 0; y < height; y += 8) {\n         for(x = 0; x < width; x += 8) {\n            float YDU[64], UDU[64], VDU[64];\n            for(row = y, pos = 0; row < y+8; ++row) {\n               // row >= height => use last input row\n               int clamped_row = (row < height) ? row : height - 1;\n               int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;\n               for(col = x; col < x+8; ++col, ++pos) {\n                  float r, g, b;\n                  // if col >= width => use pixel from last input column\n                  int p = base_p + ((col < width) ? col : (width-1))*comp;\n\n                  r = imageData[p+0];\n                  g = imageData[p+ofsG];\n                  b = imageData[p+ofsB];\n                  YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128;\n                  UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b;\n                  VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b;\n               }\n            }\n\n            DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n            DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);\n            DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);\n         }\n      }\n\n      // Do the bit alignment of the EOI marker\n      stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits);\n   }\n\n   // EOI\n   stbiw__putc(s, 0xFF);\n   stbiw__putc(s, 0xD9);\n\n   return 1;\n}\n\nSTBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality)\n{\n   stbi__write_context s;\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality);\n}\n\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)\n{\n   stbi__write_context s;\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif\n\n#endif // STB_IMAGE_WRITE_IMPLEMENTATION\n\n/* Revision history\n      1.10  (2019-02-07)\n             support utf8 filenames in Windows; fix warnings and platform ifdefs \n      1.09  (2018-02-11)\n             fix typo in zlib quality API, improve STB_I_W_STATIC in C++\n      1.08  (2018-01-29)\n             add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter\n      1.07  (2017-07-24)\n             doc fix\n      1.06 (2017-07-23)\n             writing JPEG (using Jon Olick's code)\n      1.05   ???\n      1.04 (2017-03-03)\n             monochrome BMP expansion\n      1.03   ???\n      1.02 (2016-04-02)\n             avoid allocating large structures on the stack\n      1.01 (2016-01-16)\n             STBIW_REALLOC_SIZED: support allocators with no realloc support\n             avoid race-condition in crc initialization\n             minor compile issues\n      1.00 (2015-09-14)\n             installable file IO function\n      0.99 (2015-09-13)\n             warning fixes; TGA rle support\n      0.98 (2015-04-08)\n             added STBIW_MALLOC, STBIW_ASSERT etc\n      0.97 (2015-01-18)\n             fixed HDR asserts, rewrote HDR rle logic\n      0.96 (2015-01-17)\n             add HDR output\n             fix monochrome BMP\n      0.95 (2014-08-17)\n\t\t       add monochrome TGA output\n      0.94 (2014-05-31)\n             rename private functions to avoid conflicts with stb_image.h\n      0.93 (2014-05-27)\n             warning fixes\n      0.92 (2010-08-01)\n             casts to unsigned char to fix warnings\n      0.91 (2010-07-17)\n             first public release\n      0.90   first internal release\n*/\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of \nthis software and associated documentation files (the \"Software\"), to deal in \nthe Software without restriction, including without limitation the rights to \nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies \nof the Software, and to permit persons to whom the Software is furnished to do \nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all \ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR \nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE \nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER \nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE \nSOFTWARE.\n------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this \nsoftware, either in source code form or as a compiled binary, for any purpose, \ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this \nsoftware dedicate any and all copyright interest in the software to the public \ndomain. We make this dedication for the benefit of the public at large and to \nthe detriment of our heirs and successors. We intend this dedication to be an \novert act of relinquishment in perpetuity of all present and future rights to \nthis software under copyright law.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR \nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE \nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN \nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION \nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/source/imgui_capture_tool.cpp",
    "content": "// dear imgui test engine\n// (screen/video capture tool)\n// This is usable as a standalone applet or controlled by the test engine.\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n// Two mode of operation:\n// - Interactive: call ImGuiCaptureToolUI::ShowCaptureToolWindow()\n// - Programmatic: generally via ImGuiTestContext::CaptureXXX functions\n\n// FIXME: This probably needs a rewrite, it's a bit too complicated.\n\n/*\n\nIndex of this file:\n\n// [SECTION] Includes\n// [SECTION] ImGuiCaptureImageBuf\n// [SECTION] ImGuiCaptureContext\n// [SECTION] ImGuiCaptureToolUI\n\n*/\n\n//-----------------------------------------------------------------------------\n// [SECTION] Includes\n//-----------------------------------------------------------------------------\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#define IMGUI_DEFINE_MATH_OPERATORS\n#include \"imgui.h\"\n#include \"imgui_internal.h\"\n#include \"imgui_te_engine.h\"\n#include \"imgui_capture_tool.h\"\n#include \"imgui_te_utils.h\"         // ImPathFindFilename, ImPathFindExtension, ImPathFixSeparatorsForCurrentOS, ImFileCreateDirectoryChain, ImOsOpenInShell\n#include \"thirdparty/Str/Str.h\"\n\n//-----------------------------------------------------------------------------\n// [SECTION] Link stb_image_write.h\n//-----------------------------------------------------------------------------\n\n#if IMGUI_TEST_ENGINE_ENABLE_CAPTURE\n\n// Compile time options:\n//#define IMGUI_STB_NAMESPACE            ImStb\n//#define IMGUI_STB_IMAGE_WRITE_FILENAME \"my_folder/stb_image_write.h\"\n//#define IMGUI_DISABLE_STB_IMAGE_WRITE_IMPLEMENTATION\n\n// stb_image_write\n#ifdef _MSC_VER\n#pragma warning (push)\n#pragma warning (disable: 4456)                             // declaration of 'xx' hides previous local declaration\n#pragma warning (disable: 4457)                             // declaration of 'xx' hides function parameter\n#elif defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"// warning: 'sprintf' has been explicitly marked deprecated here\n#elif defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"\n#endif\n\n#ifdef IMGUI_STB_NAMESPACE\nnamespace IMGUI_STB_NAMESPACE\n{\n#endif\n#ifndef STB_IMAGE_WRITE_IMPLEMENTATION                       // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)\n#ifndef IMGUI_DISABLE_STB_IMAGE_WRITE_IMPLEMENTATION         // in case the user already have an implementation in another compilation unit\n#define STB_IMAGE_WRITE_IMPLEMENTATION\n#endif\n#ifdef IMGUI_STB_IMAGE_WRITE_FILENAME\n#include IMGUI_STB_IMAGE_WRITE_FILENAME\n#else\n#include \"thirdparty/stb/imstb_image_write.h\"\n#endif  // #ifdef IMGUI_STB_IMAGE_WRITE_FILENAME\n#endif  // #ifndef STB_IMAGE_WRITE_IMPLEMENTATION\n#ifdef IMGUI_STB_NAMESPACE\n} // namespace ImStb\nusing namespace IMGUI_STB_NAMESPACE;\n#endif\n\n#ifdef _MSC_VER\n#pragma warning (pop)\n#elif defined(__clang__)\n#pragma clang diagnostic pop\n#elif defined(__GNUC__)\n#pragma GCC diagnostic pop\n#endif\n\n#endif // #if IMGUI_TEST_ENGINE_ENABLE_CAPTURE\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiCaptureImageBuf\n// Helper class for simple bitmap manipulation (not particularly efficient!)\n//-----------------------------------------------------------------------------\n\nvoid ImGuiCaptureImageBuf::Clear()\n{\n    if (Data)\n        IM_FREE(Data);\n    Data = nullptr;\n}\n\nvoid ImGuiCaptureImageBuf::CreateEmpty(int w, int h)\n{\n    Clear();\n    Width = w;\n    Height = h;\n    Data = (unsigned int*)IM_ALLOC((size_t)(Width * Height * 4));\n    memset(Data, 0, (size_t)(Width * Height * 4));\n}\n\nbool ImGuiCaptureImageBuf::SaveFile(const char* filename)\n{\n#if IMGUI_TEST_ENGINE_ENABLE_CAPTURE\n    IM_ASSERT(Data != nullptr);\n    ImFileCreateDirectoryChain(filename, ImPathFindFilename(filename));\n    int ret = stbi_write_png(filename, Width, Height, 4, Data, Width * 4);\n    return ret != 0;\n#else\n    IM_UNUSED(filename);\n    return false;\n#endif\n}\n\nvoid ImGuiCaptureImageBuf::RemoveAlpha()\n{\n    unsigned int* p = Data;\n    int n = Width * Height;\n    while (n-- > 0)\n    {\n        *p |= IM_COL32_A_MASK;\n        p++;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiCaptureContext\n//-----------------------------------------------------------------------------\n\n#if IMGUI_TEST_ENGINE_ENABLE_CAPTURE\nstatic void HideOtherWindows(const ImGuiCaptureArgs* args)\n{\n    ImGuiContext& g = *GImGui;\n    for (ImGuiWindow* window : g.Windows)\n    {\n        if (window->Flags & ImGuiWindowFlags_ChildWindow)\n            continue;\n        if (window->Flags & ImGuiWindowFlags_Tooltip)\n            continue;\n        if ((window->Flags & ImGuiWindowFlags_Popup) && (args->InFlags & ImGuiCaptureFlags_IncludePopups) != 0)\n            continue;\n        if (args->InCaptureWindows.contains(window))\n            continue;\n\n#ifdef IMGUI_HAS_DOCK\n        bool should_hide_window = true;\n        for (ImGuiWindow* capture_window : args->InCaptureWindows)\n        {\n            if (capture_window->DockNode != nullptr && capture_window->DockNode->HostWindow->RootWindow == window)\n            {\n                should_hide_window = false;\n                break;\n            }\n        }\n        if (!should_hide_window)\n            continue;\n#endif  // IMGUI_HAS_DOCK\n\n        // Not overwriting HiddenFramesCanSkipItems or HiddenFramesCannotSkipItems since they have side-effects (e.g. preserving ContentsSize)\n        if (window->WasActive || window->Active)\n            window->HiddenFramesForRenderOnly = 2;\n    }\n}\n#endif  // IMGUI_TEST_ENGINE_ENABLE_CAPTURE\n\nstatic ImRect GetMainViewportRect()\n{\n    ImGuiViewport* viewport = ImGui::GetMainViewport();\n    return ImRect(viewport->Pos, viewport->Pos + viewport->Size);\n}\n\nvoid ImGuiCaptureContext::PreNewFrame()\n{\n    const ImGuiCaptureArgs* args = _CaptureArgs;\n    if (args == nullptr)\n        return;\n\n    ImGuiContext& g = *GImGui;\n\n    // Force mouse position. Hovered window is reset in ImGui::NewFrame() based on mouse real mouse position.\n    if (_FrameNo > 2 && (args->InFlags & ImGuiCaptureFlags_StitchAll) != 0)\n    {\n        IM_ASSERT(args->InCaptureWindows.Size == 1);\n        g.IO.MousePos = args->InCaptureWindows[0]->Pos + _MouseRelativeToWindowPos;\n        g.HoveredWindow = _HoveredWindow;\n    }\n}\n\nvoid ImGuiCaptureContext::PreRender()\n{\n    ImGuiContext& g = *GImGui;\n    _BackupMouseDrawCursor = g.IO.MouseDrawCursor;\n    if (IsCapturing())\n    {\n        const ImGuiCaptureArgs* args = _CaptureArgs;\n        IM_ASSERT(args != NULL);\n        g.IO.MouseDrawCursor = !(args->InFlags & ImGuiCaptureFlags_HideMouseCursor);\n    }\n}\n\nvoid ImGuiCaptureContext::PostRender()\n{\n    ImGuiContext& g = *GImGui;\n    g.IO.MouseDrawCursor = _BackupMouseDrawCursor;\n}\n\nvoid ImGuiCaptureContext::RestoreBackedUpData()\n{\n    // Restore window positions unconditionally. We may have moved them ourselves during capture.\n    ImGuiContext& g = *GImGui;\n    for (int n = 0; n < _WindowsData.Size; n++)\n    {\n        ImGuiWindow* window = _WindowsData[n].Window;\n        if (window->Hidden)\n            continue;\n        ImGui::SetWindowPos(window, _WindowsData[n].BackupRect.Min, ImGuiCond_Always);\n        ImGui::SetWindowSize(window, _WindowsData[n].BackupRect.GetSize(), ImGuiCond_Always);\n    }\n    g.Style.DisplayWindowPadding = _BackupDisplayWindowPadding;\n    g.Style.DisplaySafeAreaPadding = _BackupDisplaySafeAreaPadding;\n}\n\nvoid ImGuiCaptureContext::ClearState()\n{\n    _FrameNo = _ChunkNo = 0;\n    _VideoLastFrameTime = 0;\n    _MouseRelativeToWindowPos = ImVec2(-FLT_MAX, -FLT_MAX);\n    _HoveredWindow = nullptr;\n    _CaptureArgs = nullptr;\n}\n\n// Returns true when capture is in progress.\nImGuiCaptureStatus ImGuiCaptureContext::CaptureUpdate(ImGuiCaptureArgs* args)\n{\n#if IMGUI_TEST_ENGINE_ENABLE_CAPTURE\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    // Sanity checks\n    IM_ASSERT(args != nullptr);\n    IM_ASSERT(ScreenCaptureFunc != nullptr);\n    IM_ASSERT(args->InOutputImageBuf != nullptr || args->InOutputFile[0]);\n    IM_ASSERT(args->InRecordFPSTarget != 0);\n    if (_VideoRecording)\n    {\n        IM_ASSERT(args->InOutputFile[0] && \"Output filename must be specified when recording videos.\");\n        IM_ASSERT(args->InOutputImageBuf == nullptr && \"Output buffer cannot be specified when recording videos.\");\n        IM_ASSERT((args->InFlags & ImGuiCaptureFlags_StitchAll) == 0 && \"Image stitching is not supported when recording videos.\");\n        if (!ImFileExist(VideoCaptureEncoderPath))\n        {\n            fprintf(stderr, \"Video encoder not found at \\\"%s\\\", video capturing failed.\\n\", VideoCaptureEncoderPath);\n            return ImGuiCaptureStatus_Error;\n        }\n    }\n\n    ImGuiCaptureImageBuf* output = args->InOutputImageBuf ? args->InOutputImageBuf : &_CaptureBuf;\n    const ImRect viewport_rect = GetMainViewportRect();\n\n    // Hide other windows so they can't be seen visible behind captured window\n    if ((args->InFlags & ImGuiCaptureFlags_IncludeOtherWindows) == 0 && !args->InCaptureWindows.empty())\n        HideOtherWindows(args);\n\n    // Recording will be set to false when we are stopping video capture.\n    const bool is_recording_video = IsCapturingVideo();\n    const double current_time_sec = ImGui::GetTime();\n    if (is_recording_video && _VideoLastFrameTime > 0)\n    {\n        double delta_sec = current_time_sec - _VideoLastFrameTime;\n        if (delta_sec < 1.0 / args->InRecordFPSTarget)\n            return ImGuiCaptureStatus_InProgress;\n    }\n\n    // Capture can be performed in single frame if we are capturing a rect.\n    const bool instant_capture = (args->InFlags & ImGuiCaptureFlags_Instant) != 0;\n    const bool is_capturing_explicit_rect = args->InCaptureRect.GetWidth() > 0 && args->InCaptureRect.GetHeight() > 0;\n    if (instant_capture)\n    {\n        IM_ASSERT(args->InCaptureWindows.empty());\n        IM_ASSERT(is_capturing_explicit_rect);\n        IM_ASSERT(is_recording_video == false);\n        IM_ASSERT((args->InFlags & ImGuiCaptureFlags_StitchAll) == 0);\n    }\n\n    // Do not start a capture process while mouse button is pressed. In case mouse cursor is hovering a captured window,\n    // pressed button may cause window to be repositioned unexpectedly. This is only important in stitched mode, because\n    // this is the only time we move mouse cursor.\n    if ((args->InFlags & ImGuiCaptureFlags_StitchAll) != 0)\n        if (g.IO.MouseDown[0] && _FrameNo == 0)\n            return ImGuiCaptureStatus_InProgress;\n\n    //-----------------------------------------------------------------\n    // Frame 0: Initialize capture state\n    //-----------------------------------------------------------------\n    if (_FrameNo == 0)\n    {\n        if (is_recording_video)\n        {\n            // Determinate size alignment\n            const char* extension = (char*)ImPathFindExtension(args->InOutputFile);\n            if (args->InSizeAlign == 0)\n            {\n                if (strcmp(extension, \".gif\") == 0)\n                    args->InSizeAlign = 1;\n                else\n                    args->InSizeAlign = 2; // mp4 wants >= 2\n            }\n            IM_ASSERT(args->InSizeAlign > 0);\n        }\n\n        // When recording, same args should have been passed to BeginVideoCapture().\n        IM_ASSERT(!_VideoRecording || _CaptureArgs == args);\n\n        _CaptureArgs = args;\n        _ChunkNo = 0;\n        _CaptureRect = _CapturedWindowRect = ImRect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);\n        _WindowsData.clear();\n        _BackupDisplayWindowPadding = g.Style.DisplayWindowPadding;\n        _BackupDisplaySafeAreaPadding = g.Style.DisplaySafeAreaPadding;\n        g.Style.DisplayWindowPadding = ImVec2(0, 0);    // Allow windows to be positioned fully outside of visible viewport.\n        g.Style.DisplaySafeAreaPadding = ImVec2(0, 0);\n\n        if (is_capturing_explicit_rect)\n        {\n            // Capture arbitrary rectangle. If any windows are specified in this mode only they will appear in captured region.\n            _CaptureRect = args->InCaptureRect;\n            if (args->InCaptureWindows.empty() && !instant_capture)\n            {\n                // Gather all top level windows. We will need to move them in order to capture regions larger than viewport.\n                for (ImGuiWindow* window : g.Windows)\n                {\n                    // Child windows will be included by their parents.\n                    if (window->ParentWindow != nullptr)\n                        continue;\n                    if ((window->Flags & ImGuiWindowFlags_Popup) && !(args->InFlags & ImGuiCaptureFlags_IncludePopups))\n                        continue;\n                    args->InCaptureWindows.push_back(window);\n                }\n            }\n        }\n\n        // Save rectangle covering all windows and find top-left corner of combined rect which will be used to\n        // translate this group of windows to top-left corner of the screen.\n        for (ImGuiWindow* window : args->InCaptureWindows)\n        {\n            _CapturedWindowRect.Add(window->Rect());\n            ImGuiCaptureWindowData window_data;\n            window_data.BackupRect = window->Rect();\n            window_data.Window = window;\n            _WindowsData.push_back(window_data);\n        }\n\n        if (args->InFlags & ImGuiCaptureFlags_StitchAll)\n        {\n            IM_ASSERT(is_capturing_explicit_rect == false && \"ImGuiCaptureContext: capture of full window contents is not possible when capturing specified rect.\");\n            IM_ASSERT(args->InCaptureWindows.Size == 1 && \"ImGuiCaptureContext: capture of full window contents is not possible when capturing more than one window.\");\n\n            // Resize window to it's contents and capture it's entire width/height. However if window is bigger than it's contents - keep original size.\n            ImGuiWindow* window = args->InCaptureWindows[0];\n            ImVec2 full_size = window->SizeFull;\n\n            // Mouse cursor is relative to captured window even if it is not hovered, in which case cursor is kept off the window to prevent appearing in screenshot multiple times by accident.\n            _MouseRelativeToWindowPos = io.MousePos - window->Pos + window->Scroll;\n\n            // FIXME-CAPTURE: Window width change may affect vertical content size if window contains text that wraps. To accurately position mouse cursor for capture we avoid horizontal resize.\n            // Instead window width should be set manually before capture, as it is simple to do and most of the time we already have a window of desired width.\n            //full_size.x = ImMax(window->SizeFull.x, window->ContentSize.x + (window->WindowPadding.x + window->WindowBorderSize) * 2);\n            full_size.y = ImMax(window->SizeFull.y, window->ContentSize.y + (window->WindowPadding.y + window->WindowBorderSize) * 2 + window->DecoOuterSizeY1);\n            ImGui::SetWindowSize(window, full_size);\n            _HoveredWindow = g.HoveredWindow;\n        }\n        else\n        {\n            _MouseRelativeToWindowPos = ImVec2(-FLT_MAX, -FLT_MAX);\n            _HoveredWindow = nullptr;\n        }\n    }\n    else\n    {\n        IM_ASSERT(args == _CaptureArgs); // Capture args can not change mid-capture.\n    }\n\n    //-----------------------------------------------------------------\n    // Frame 1: Skipped to allow window size to update fully\n    //-----------------------------------------------------------------\n\n    //-----------------------------------------------------------------\n    // Frame 2: Position windows, lock rectangle, create capture buffer\n    //-----------------------------------------------------------------\n    if (_FrameNo == 2 || instant_capture)\n    {\n        // Move group of windows so combined rectangle position is at the top-left corner + padding and create combined\n        // capture rect of entire area that will be saved to screenshot. Doing this on the second frame because when\n        // ImGuiCaptureFlags_StitchAll flag is used we need to allow window to reposition.\n        // Repositioning of a window may take multiple frames, depending on whether window was already rendered or not.\n        if (args->InFlags & ImGuiCaptureFlags_StitchAll)\n        {\n            ImVec2 move_offset = ImVec2(args->InPadding, args->InPadding) - _CapturedWindowRect.Min + viewport_rect.Min;\n            IM_ASSERT(args->InCaptureWindows.Size == _WindowsData.Size);\n            for (int n = 0; n < _WindowsData.Size; n++)\n            {\n                ImGuiWindow* window = _WindowsData[n].Window;\n                _WindowsData[n].PosDuringCapture = window->Pos + move_offset;\n                ImGui::SetWindowPos(window, _WindowsData[n].PosDuringCapture);\n            }\n        }\n\n        // Determine capture rectangle if not provided by user\n        if (!is_capturing_explicit_rect)\n        {\n            if (args->InCaptureWindows.Size > 0)\n            {\n                for (ImGuiWindow* window : args->InCaptureWindows)\n                    _CaptureRect.Add(window->Rect());\n                _CaptureRect.Expand(args->InPadding);\n            }\n            else\n            {\n                _CaptureRect = viewport_rect;\n            }\n        }\n        if ((args->InFlags & ImGuiCaptureFlags_StitchAll) == 0)\n        {\n            // Can not capture area outside of screen. Clip capture rect, since we are capturing only visible rect anyway.\n            _CaptureRect.ClipWith(viewport_rect);\n\n            // Align size\n            // FIXME: ffmpeg + codec can possibly handle that better on their side.\n            ImVec2 capture_size_aligned = _CaptureRect.GetSize();\n            if (args->InSizeAlign > 1)\n            {\n                // Round up\n                IM_ASSERT(ImIsPowerOfTwo(args->InSizeAlign));\n                capture_size_aligned.x = (float)IM_MEMALIGN((int)capture_size_aligned.x, args->InSizeAlign);\n                capture_size_aligned.y = (float)IM_MEMALIGN((int)capture_size_aligned.y, args->InSizeAlign);\n\n                // Unless will stray off viewport, then round down\n                if (_CaptureRect.Min.x + capture_size_aligned.x >= viewport_rect.Max.x)\n                    capture_size_aligned.x -= args->InSizeAlign;\n                if (_CaptureRect.Min.y + capture_size_aligned.y >= viewport_rect.Max.y)\n                    capture_size_aligned.y -= args->InSizeAlign;\n\n                IM_ASSERT(capture_size_aligned.x > 0);\n                IM_ASSERT(capture_size_aligned.y > 0);\n                _CaptureRect.Max = _CaptureRect.Min + capture_size_aligned;\n            }\n        }\n\n        // Initialize capture buffer.\n        IM_ASSERT(!_CaptureRect.IsInverted());\n        args->OutImageSize = _CaptureRect.GetSize();\n        output->CreateEmpty((int)_CaptureRect.GetWidth(), (int)_CaptureRect.GetHeight());\n    }\n\n    //-----------------------------------------------------------------\n    // Frame 4+N*4: Capture a frame\n    //-----------------------------------------------------------------\n\n    const ImRect clip_rect = viewport_rect;\n    ImRect capture_rect = _CaptureRect;\n    capture_rect.ClipWith(clip_rect);\n    const int capture_height = ImMin((int)io.DisplaySize.y, (int)_CaptureRect.GetHeight());\n    const int x1 = (int)(capture_rect.Min.x - clip_rect.Min.x);\n    const int y1 = (int)(capture_rect.Min.y - clip_rect.Min.y);\n    const int w = (int)capture_rect.GetWidth();\n    const int h = (int)ImMin(output->Height - _ChunkNo * capture_height, capture_height);\n\n    // Position windows\n    if ((_FrameNo > 2) && (args->InFlags & ImGuiCaptureFlags_StitchAll))\n    {\n        // Unlike SetNextWindowPos(), SetWindowPos() will still perform viewport clamping, affecting support for io.ConfigWindowsMoveFromTitleBarOnly.\n        IM_ASSERT(args->InCaptureWindows.Size == _WindowsData.Size);\n        for (int n = 0; n < _WindowsData.Size; n++)\n            ImGui::SetWindowPos(_WindowsData[n].Window, _WindowsData[n].PosDuringCapture - ImVec2(0, (float)capture_height * _ChunkNo));\n    }\n\n    if (((_FrameNo > 2) && (_FrameNo % 4) == 0) || (is_recording_video && _FrameNo > 2) || instant_capture)\n    {\n        // FIXME: Implement capture of regions wider than viewport.\n        // Capture a portion of image. Capturing of windows wider than viewport is not implemented yet.\n        if (h > 0)\n        {\n            IM_ASSERT(w == output->Width);\n            if (args->InFlags & ImGuiCaptureFlags_StitchAll)\n                IM_ASSERT(h <= output->Height);     // When stitching, image can be taller than captured viewport.\n            else\n                IM_ASSERT(h == output->Height);\n\n            ImGuiID viewport_id = 0;\n#ifdef IMGUI_HAS_VIEWPORT\n            if (args->InFlags & ImGuiCaptureFlags_StitchAll)\n                viewport_id = _WindowsData[0].Window->ViewportId;\n            else\n                viewport_id = ImGui::GetMainViewport()->ID;\n#endif\n\n            //printf(\"ScreenCaptureFunc x1: %d, y1: %d, w: %d, h: %d\\n\", x1, y1, w, h);\n            if (!ScreenCaptureFunc(viewport_id, x1, y1, w, h, &output->Data[_ChunkNo * w * capture_height], ScreenCaptureUserData))\n            {\n                fprintf(stderr, \"Screen capture function failed.\\n\");\n                RestoreBackedUpData();\n                ClearState();\n                return ImGuiCaptureStatus_Error;\n            }\n\n            if (args->InFlags & ImGuiCaptureFlags_StitchAll)\n            {\n                // Window moves up in order to expose it's lower part.\n                _ChunkNo++;\n                _CaptureRect.TranslateY(-(float)h);\n            }\n\n            if (is_recording_video && (args->InFlags & ImGuiCaptureFlags_NoSave) == 0)\n            {\n                // _VideoEncoderPipe is nullptr when recording just started. Initialize recording state.\n                if (_VideoEncoderPipe == nullptr)\n                {\n                    // First video frame, initialize now that dimensions are known.\n                    const unsigned int width = (unsigned int)capture_rect.GetWidth();\n                    const unsigned int height = (unsigned int)capture_rect.GetHeight();\n                    IM_ASSERT(VideoCaptureEncoderPath != nullptr && VideoCaptureEncoderPath[0]);\n                    Str256f encoder_exe(VideoCaptureEncoderPath), cmd(\"\");\n                    ImPathFixSeparatorsForCurrentOS(encoder_exe.c_str());\n                    ImFileCreateDirectoryChain(args->InOutputFile, ImPathFindFilename(args->InOutputFile));\n#if _WIN32\n                    cmd.append(\"\\\"\");   // On windows, entire command wrapped in quotes allows use of quotes for parameters.\n#endif\n                    const char* extension = (char*)ImPathFindExtension(args->InOutputFile);\n                    if (strcmp(extension, \".gif\") == 0)\n                    {\n                        IM_ASSERT(GifCaptureEncoderParams != nullptr && GifCaptureEncoderParams[0]);\n                        cmd.appendf(\"\\\"%s\\\" %s\", encoder_exe.c_str(), GifCaptureEncoderParams);\n                    }\n                    else\n                    {\n                        IM_ASSERT(VideoCaptureEncoderParams != nullptr && VideoCaptureEncoderParams[0]);\n                        cmd.appendf(\"\\\"%s\\\" %s\", encoder_exe.c_str(), VideoCaptureEncoderParams);\n                    }\n#if _WIN32\n                    cmd.append(\"\\\"\");\n#endif\n                    ImStrReplace(&cmd, \"$FPS\", Str16f(\"%d\", args->InRecordFPSTarget).c_str());\n                    ImStrReplace(&cmd, \"$WIDTH\", Str16f(\"%d\", width).c_str());\n                    ImStrReplace(&cmd, \"$HEIGHT\", Str16f(\"%d\", height).c_str());\n                    ImStrReplace(&cmd, \"$OUTPUT\", args->InOutputFile);\n                    fprintf(stdout, \"# %s\\n\", cmd.c_str());\n                    _VideoEncoderPipe = ImOsPOpen(cmd.c_str(), \"w\");\n                    IM_ASSERT(_VideoEncoderPipe != nullptr);\n                }\n\n                // Save new video frame\n                fwrite(output->Data, 1, output->Width * output->Height * 4, _VideoEncoderPipe);\n            }\n            if (is_recording_video)\n                _VideoLastFrameTime = current_time_sec;\n        }\n\n        // Image is finalized immediately when we are not stitching. Otherwise, image is finalized when we have captured and stitched all frames.\n        if (!_VideoRecording && (!(args->InFlags & ImGuiCaptureFlags_StitchAll) || h <= 0))\n        {\n            output->RemoveAlpha();\n\n            if (_VideoEncoderPipe != nullptr)\n            {\n                // At this point _Recording is false, but we know we were recording because _VideoEncoderPipe is not nullptr. Finalize video here.\n                ImOsPClose(_VideoEncoderPipe);\n                _VideoEncoderPipe = nullptr;\n            }\n            else if (args->InOutputImageBuf == nullptr)\n            {\n                // Save single frame.\n                if ((args->InFlags & ImGuiCaptureFlags_NoSave) == 0)\n                    output->SaveFile(args->InOutputFile);\n                output->Clear();\n            }\n\n            RestoreBackedUpData();\n            ClearState();\n            return ImGuiCaptureStatus_Done;\n        }\n    }\n\n    // Keep going\n    _FrameNo++;\n    return ImGuiCaptureStatus_InProgress;\n#else\n    IM_UNUSED(args);\n    return ImGuiCaptureStatus_Done;\n#endif\n}\n\nvoid ImGuiCaptureContext::BeginVideoCapture(ImGuiCaptureArgs* args)\n{\n    IM_ASSERT(args != nullptr);\n    IM_ASSERT(_VideoRecording == false);\n    IM_ASSERT(_VideoEncoderPipe == nullptr);\n    IM_ASSERT(args->InRecordFPSTarget >= 1 && args->InRecordFPSTarget <= 100);\n\n    ImFileCreateDirectoryChain(args->InOutputFile, ImPathFindFilename(args->InOutputFile));\n    _VideoRecording = true;\n    _CaptureArgs = args;\n}\n\nvoid ImGuiCaptureContext::EndVideoCapture()\n{\n    IM_ASSERT(_CaptureArgs != nullptr);\n    IM_ASSERT(_VideoRecording == true);\n\n    _VideoRecording = false;\n    _CaptureArgs = nullptr;\n}\n\nbool ImGuiCaptureContext::IsCapturingVideo()\n{\n    return _VideoRecording;\n}\n\nbool ImGuiCaptureContext::IsCapturing()\n{\n    return _CaptureArgs != nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// ImGuiCaptureToolUI\n//-----------------------------------------------------------------------------\n\nImGuiCaptureToolUI::ImGuiCaptureToolUI()\n{\n    // Filename template for where screenshots will be saved. May contain directories or variation of %d format.\n    ImStrncpy(_OutputFileTemplate, \"output/captures/imgui_capture_%04d.png\", IM_ARRAYSIZE(_OutputFileTemplate));\n}\n\n// Interactively pick a single window\nvoid ImGuiCaptureToolUI::_CaptureWindowPicker(ImGuiCaptureArgs* args)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    const float TEXT_BASE_WIDTH = ImGui::CalcTextSize(\"A\").x;\n    const ImVec2 button_sz = ImVec2(TEXT_BASE_WIDTH * 30, 0.0f);\n    const ImGuiID picking_id = ImGui::GetID(\"##picking\");\n\n    if (ImGui::Button(\"Capture Single Window..\", button_sz))\n        _StateIsPickingWindow = true;\n\n    if (_StateIsPickingWindow)\n    {\n        // Picking a window\n        ImGuiWindow* capture_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : nullptr;\n        ImDrawList* fg_draw_list = ImGui::GetForegroundDrawList();\n        ImGui::SetActiveID(picking_id, g.CurrentWindow);    // Steal active ID so our click won't interact with something else.\n        ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);\n        ImGui::SetTooltip(\"Capture window: '%s'\\nPress ESC to cancel.\", capture_window ? capture_window->Name : \"<None>\");\n\n        // FIXME: Would be nice to have a way to enforce front-most windows. Perhaps make this Render() feature more generic.\n        //if (capture_window)\n        //    g.NavWindowingTarget = capture_window;\n\n        // Draw rect that is about to be captured\n        const ImRect viewport_rect = GetMainViewportRect();\n        const ImU32 col_dim_overlay = IM_COL32(0, 0, 0, 40);\n        if (capture_window)\n        {\n            ImRect r = capture_window->Rect();\n            r.Expand(args->InPadding);\n            r.ClipWith(ImRect(ImVec2(0, 0), io.DisplaySize));\n            r.Expand(1.0f);\n            fg_draw_list->AddRect(r.Min, r.Max, IM_COL32_WHITE, 0.0f, 0, 2.0f);\n            ImGui::RenderRectFilledWithHole(fg_draw_list, viewport_rect, r, col_dim_overlay, 0.0f);\n        }\n        else\n        {\n            fg_draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col_dim_overlay);\n        }\n\n        if (ImGui::IsMouseClicked(0) && capture_window && _InitializeOutputFile())\n        {\n            ImGui::FocusWindow(capture_window);\n            _SelectedWindows.resize(0);\n            _StateIsPickingWindow = false;\n            _StateIsCapturing = true;\n            args->InCaptureWindows.clear();\n            args->InCaptureWindows.push_back(capture_window);\n        }\n        if (ImGui::IsKeyPressed(ImGuiKey_Escape))\n            _StateIsPickingWindow = _StateIsCapturing = false;\n    }\n    else\n    {\n        if (ImGui::GetActiveID() == picking_id)\n            ImGui::ClearActiveID();\n    }\n}\n\nvoid ImGuiCaptureToolUI::_CaptureWindowsSelector(ImGuiCaptureContext* context, ImGuiCaptureArgs* args)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    // Gather selected windows\n    ImRect capture_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);\n    for (ImGuiWindow* window : g.Windows)\n    {\n        if (window->WasActive == false)\n            continue;\n        if (window->Flags & ImGuiWindowFlags_ChildWindow)\n            continue;\n        const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;\n        if ((args->InFlags & ImGuiCaptureFlags_IncludePopups) && is_popup)\n        {\n            capture_rect.Add(window->Rect());\n            args->InCaptureWindows.push_back(window);\n            continue;\n        }\n        if (is_popup)\n            continue;\n        if (_SelectedWindows.contains(window->RootWindow->ID))\n        {\n            capture_rect.Add(window->Rect());\n            args->InCaptureWindows.push_back(window);\n        }\n    }\n    const bool allow_capture = !capture_rect.IsInverted() && args->InCaptureWindows.Size > 0 && _OutputFileTemplate[0];\n\n    const float TEXT_BASE_WIDTH = ImGui::CalcTextSize(\"A\").x;\n    const ImVec2 button_sz = ImVec2(TEXT_BASE_WIDTH * 30, 0.0f);\n\n    // Capture Multiple Button\n    {\n        char label[64];\n        ImFormatString(label, 64, \"Capture Multiple (%d)###CaptureMultiple\", args->InCaptureWindows.Size);\n\n        if (!allow_capture)\n            ImGui::BeginDisabled();\n        bool do_capture = ImGui::Button(label, button_sz);\n        do_capture |= io.KeyAlt && ImGui::IsKeyPressed(ImGuiKey_C);\n        if (!allow_capture)\n            ImGui::EndDisabled();\n        ImGui::SetItemTooltip(\"Alternatively press Alt+C to capture selection.\");\n        if (do_capture && _InitializeOutputFile())\n            _StateIsCapturing = true;\n    }\n\n    // Record video button\n    // (Prefer 100/FPS to be an integer)\n    {\n        const bool is_capturing_video = context->IsCapturingVideo();\n        if (is_capturing_video)\n        {\n            if (ImGui::Button(\"Stop capturing video###CaptureVideo\", button_sz))\n                context->EndVideoCapture();\n        }\n        else\n        {\n            char label[64];\n            ImFormatString(label, 64, \"Capture video (%d)###CaptureVideo\", args->InCaptureWindows.Size);\n            if (!allow_capture)\n                ImGui::BeginDisabled();\n            if (ImGui::Button(label, button_sz) && _InitializeOutputFile())\n            {\n                // File template will most likely end with .png, but we need a different extension for videos.\n                IM_ASSERT(VideoCaptureExtension != nullptr && VideoCaptureExtension[0]);\n                char* ext = (char*)ImPathFindExtension(args->InOutputFile);\n                ImStrncpy(ext, VideoCaptureExtension, (size_t)(ext - args->InOutputFile));\n                _StateIsCapturing = true;\n                context->BeginVideoCapture(args);\n            }\n            if (!allow_capture)\n                ImGui::EndDisabled();\n        }\n    }\n\n    // Draw capture rectangle\n    ImDrawList* draw_list = ImGui::GetForegroundDrawList();\n    if (allow_capture && !_StateIsPickingWindow && !_StateIsCapturing)\n    {\n        IM_ASSERT(capture_rect.GetWidth() > 0);\n        IM_ASSERT(capture_rect.GetHeight() > 0);\n        const ImRect viewport_rect = GetMainViewportRect();\n        capture_rect.Expand(args->InPadding);\n        capture_rect.ClipWith(viewport_rect);\n        draw_list->AddRect(capture_rect.Min - ImVec2(1.0f, 1.0f), capture_rect.Max + ImVec2(1.0f, 1.0f), IM_COL32_WHITE);\n    }\n\n    ImGui::Separator();\n\n    // Show window list and update rectangles\n    ImGui::Text(\"Windows:\");\n    if (ImGui::BeginTable(\"split\", 2))\n    {\n        ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed);\n        ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch);\n        for (ImGuiWindow* window : g.Windows)\n        {\n            if (!window->WasActive)\n                continue;\n\n            const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) || (window->Flags & ImGuiWindowFlags_Tooltip);\n            if (is_popup)\n                continue;\n\n            if (window->Flags & ImGuiWindowFlags_ChildWindow)\n                continue;\n\n            ImGui::TableNextRow();\n            ImGui::TableSetColumnIndex(0);\n            ImGui::PushID(window);\n\n            // Ensure that text after the ## is actually displayed to the user (FIXME: won't be able to check/uncheck from that portion of the text)\n            bool is_selected = _SelectedWindows.contains(window->RootWindow->ID);\n            if (ImGui::Checkbox(window->Name, &is_selected))\n            {\n                if (is_selected)\n                    _SelectedWindows.push_back(window->RootWindow->ID);\n                else\n                    _SelectedWindows.find_erase_unsorted(window->RootWindow->ID);\n            }\n\n            if (const char* remaining_text = ImGui::FindRenderedTextEnd(window->Name))\n                if (remaining_text[0] != 0)\n                {\n                    if (remaining_text > window->Name)\n                        ImGui::SameLine(0, 1);\n                    else\n                        ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);\n                    ImGui::TextUnformatted(remaining_text);\n                }\n\n            ImGui::TableSetColumnIndex(1);\n            ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 9.0f);\n            ImGui::DragFloat2(\"Pos\", &window->Pos.x, 0.05f, 0.0f, 0.0f, \"%.0f\");\n            ImGui::SameLine();\n            ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 9.0f);\n            ImGui::DragFloat2(\"Size\", &window->SizeFull.x, 0.05f, 0.0f, 0.0f, \"%.0f\");\n            ImGui::PopID();\n        }\n        ImGui::EndTable();\n    }\n}\n\nvoid ImGuiCaptureToolUI::ShowCaptureToolWindow(ImGuiCaptureContext* context, bool* p_open)\n{\n    // Update capturing\n    if (_StateIsCapturing)\n    {\n        ImGuiCaptureArgs* args = &_CaptureArgs;\n        if (context->IsCapturingVideo() || args->InCaptureWindows.Size > 1)\n            args->InFlags &= ~ImGuiCaptureFlags_StitchAll;\n\n        if (context->_VideoRecording && ImGui::IsKeyPressed(ImGuiKey_Escape))\n            context->EndVideoCapture();\n\n        ImGuiCaptureStatus status = context->CaptureUpdate(args);\n        if (status != ImGuiCaptureStatus_InProgress)\n        {\n            if (status == ImGuiCaptureStatus_Done)\n                ImStrncpy(OutputLastFilename, args->InOutputFile, IM_ARRAYSIZE(OutputLastFilename));\n            _StateIsCapturing = false;\n            _FileCounter++;\n        }\n    }\n\n    // Update UI\n    if (!ImGui::Begin(\"Dear ImGui Capture Tool\", p_open))\n    {\n        ImGui::End();\n        return;\n    }\n    if (context->ScreenCaptureFunc == nullptr)\n    {\n        ImGui::TextColored(ImVec4(1, 0, 0, 1), \"Backend is missing ScreenCaptureFunc!\");\n        ImGui::End();\n        return;\n    }\n\n    ImGuiIO& io = ImGui::GetIO();\n    ImGuiStyle& style = ImGui::GetStyle();\n\n    // Options\n    ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n    if (ImGui::TreeNode(\"Options\"))\n    {\n        // Open Last\n        {\n            const bool has_last_file_name = (OutputLastFilename[0] != 0);\n            if (!has_last_file_name)\n                ImGui::BeginDisabled();\n            if (ImGui::Button(\"Open Last\"))\n                ImOsOpenInShell(OutputLastFilename);\n            if (!has_last_file_name)\n                ImGui::EndDisabled();\n            if (has_last_file_name)\n                ImGui::SetItemTooltip(\"Open %s\", OutputLastFilename);\n            ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);\n        }\n\n        // Open Directory\n        {\n            char output_dir[256];\n            strcpy(output_dir, _OutputFileTemplate);\n            char* output_filename = (char*)ImPathFindFilename(output_dir);\n            if (output_filename > output_dir)\n                output_filename[-1] = 0;\n            else\n                strcpy(output_dir, \".\");\n            if (ImGui::Button(\"Open Directory\"))\n            {\n                ImPathFixSeparatorsForCurrentOS(output_dir);\n                ImOsOpenInShell(output_dir);\n            }\n            ImGui::SetItemTooltip(\"Open %s/\", output_dir);\n        }\n\n        const float TEXT_BASE_WIDTH = ImGui::CalcTextSize(\"A\").x;\n        const float BUTTON_WIDTH = (float)(int)-(TEXT_BASE_WIDTH * 26);\n\n        ImGui::PushItemWidth(BUTTON_WIDTH);\n        ImGui::InputText(\"Output template\", _OutputFileTemplate, IM_ARRAYSIZE(_OutputFileTemplate));\n        ImGui::SetItemTooltip(\n            \"Output template should contain one %%d (or variation of it) format variable. \"\n            \"Multiple captures will be saved with an increasing number to avoid overwriting same file.\");\n\n        _ShowEncoderConfigFields(context);\n\n        ImGui::DragFloat(\"Padding\", &_CaptureArgs.InPadding, 0.1f, 0, 32, \"%.0f\");\n        ImGui::SetItemTooltip(\"Extra padding around captured area.\");\n        ImGui::DragInt(\"Video FPS\", &_CaptureArgs.InRecordFPSTarget, 0.1f, 10, 100, \"%d fps\");\n        ImGui::SetItemTooltip(\"Target FPS for video captures.\");\n\n        if (ImGui::Button(\"Snap Windows To Grid\", ImVec2(BUTTON_WIDTH, 0)))\n            _SnapWindowsToGrid(SnapGridSize);\n        ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);\n        ImGui::SetNextItemWidth((float)(int)-(TEXT_BASE_WIDTH * 5));\n        ImGui::DragFloat(\"##SnapGridSize\", &SnapGridSize, 1.0f, 1.0f, 128.0f, \"%.0f\");\n\n        ImGui::Checkbox(\"Software Mouse Cursor\", &io.MouseDrawCursor);\n\n        bool content_stitching_available = _CaptureArgs.InCaptureWindows.Size <= 1;\n#ifdef IMGUI_HAS_VIEWPORT\n        content_stitching_available &= !(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable);\n#endif\n        ImGui::BeginDisabled(!content_stitching_available);\n        ImGui::CheckboxFlags(\"Stitch full contents height\", &_CaptureArgs.InFlags, ImGuiCaptureFlags_StitchAll);\n        ImGui::EndDisabled();\n        if (!content_stitching_available)\n            ImGui::SetItemTooltip(\"Content stitching is not possible when using viewports.\");\n\n        ImGui::CheckboxFlags(\"Include other windows\", &_CaptureArgs.InFlags, ImGuiCaptureFlags_IncludeOtherWindows);\n        ImGui::CheckboxFlags(\"Include popups\", &_CaptureArgs.InFlags, ImGuiCaptureFlags_IncludePopups);\n        ImGui::SetItemTooltip(\"Capture area will be expanded to include visible tooltips.\");\n\n        ImGui::PopItemWidth();\n        ImGui::TreePop();\n    }\n\n    ImGui::Separator();\n\n    if (!_StateIsCapturing)\n        _CaptureArgs.InCaptureWindows.clear();\n    _CaptureWindowPicker(&_CaptureArgs);\n    _CaptureWindowsSelector(context, &_CaptureArgs);\n\n    ImGui::Separator();\n\n    ImGui::End();\n}\n\n// Move/resize all windows so they are neatly aligned on a grid\n// This is an easy way of ensuring some form of alignment without specifying detailed constraints.\nvoid ImGuiCaptureToolUI::_SnapWindowsToGrid(float cell_size)\n{\n    ImGuiContext& g = *GImGui;\n    for (ImGuiWindow* window : g.Windows)\n    {\n        if (!window->WasActive)\n            continue;\n\n        if (window->Flags & ImGuiWindowFlags_ChildWindow)\n            continue;\n\n        if ((window->Flags & ImGuiWindowFlags_Popup) || (window->Flags & ImGuiWindowFlags_Tooltip))\n            continue;\n\n        ImRect rect = window->Rect();\n        rect.Min.x = ImFloor(rect.Min.x / cell_size) * cell_size;\n        rect.Min.y = ImFloor(rect.Min.y / cell_size) * cell_size;\n        rect.Max.x = ImFloor(rect.Max.x / cell_size) * cell_size;\n        rect.Max.y = ImFloor(rect.Max.y / cell_size) * cell_size;\n        ImGui::SetWindowPos(window, rect.Min);\n        ImGui::SetWindowSize(window, rect.GetSize());\n    }\n}\n\nbool ImGuiCaptureToolUI::_InitializeOutputFile()\n{\n    // Create output folder and decide of output filename\n    ImFormatString(_CaptureArgs.InOutputFile, IM_ARRAYSIZE(_CaptureArgs.InOutputFile), _OutputFileTemplate,\n                   _FileCounter + 1);\n    ImPathFixSeparatorsForCurrentOS(_CaptureArgs.InOutputFile);\n    if (!ImFileCreateDirectoryChain(_CaptureArgs.InOutputFile, ImPathFindFilename(_CaptureArgs.InOutputFile)))\n    {\n        fprintf(stderr, \"ImGuiCaptureContext: unable to create directory for file '%s'.\\n\",\n                _CaptureArgs.InOutputFile);\n        return false;\n    }\n    return true;\n}\n\nbool ImGuiCaptureToolUI::_ShowEncoderConfigFields(ImGuiCaptureContext* context)\n{\n    ImGuiContext& g = *GImGui;\n    const float TEXT_BASE_WIDTH = ImGui::CalcTextSize(\"A\").x;\n    const float BUTTON_WIDTH = (float)(int)-(TEXT_BASE_WIDTH * 26);\n\n    bool modified = false;\n    if (context->VideoCaptureEncoderPathSize)\n    {\n        ImGui::PushItemWidth(BUTTON_WIDTH);\n        modified |= ImGui::InputText(\"Video Encoder Path\", context->VideoCaptureEncoderPath, context->VideoCaptureEncoderPathSize);\n        const bool encoder_exe_missing = !ImFileExist(context->VideoCaptureEncoderPath);\n        if (encoder_exe_missing)\n            ImGui::ItemErrorFrame(IM_COL32(255, 0, 0, 255));\n        ImGui::SetItemTooltip(\"Absolute or relative path to video encoder executable (e.g. \\\"path/to/ffmpeg.exe\\\"). Required for video recording.%s\", encoder_exe_missing ? \"\\nFile does not exist!\" : \"\");\n    }\n\n    struct CmdLineParamsInfo\n    {\n        const char* Title = nullptr;\n        char*       Params = nullptr;\n        int         ParamsSize = 0;\n        const char* DefaultCmdLineParams = nullptr;\n        const char* VideoFileExt = nullptr;\n        CmdLineParamsInfo(const char* title, char* params, int params_size, const char* default_cmd, const char* ext) { Title = title; Params = params; ParamsSize = params_size; DefaultCmdLineParams = default_cmd; VideoFileExt = ext; }\n    };\n    CmdLineParamsInfo params_info[] =\n    {\n        { \"Video Encoder params\", context->VideoCaptureEncoderParams, context->VideoCaptureEncoderParamsSize, IMGUI_CAPTURE_DEFAULT_VIDEO_PARAMS_FOR_FFMPEG, \".mp4\" },\n        { \"Gif Encoder params\", context->GifCaptureEncoderParams, context->GifCaptureEncoderParamsSize, IMGUI_CAPTURE_DEFAULT_GIF_PARAMS_FOR_FFMPEG, \".gif\" },\n    };\n    for (CmdLineParamsInfo& info : params_info)\n    {\n        if (info.ParamsSize == 0)\n            continue;   // Can not be edited.\n        IM_ASSERT(info.Params != nullptr);\n        ImGui::PushID(&info);\n        float small_button_width = ImGui::CalcTextSize(\"..\").x + ImGui::GetStyle().FramePadding.x * 2.0f;\n        ImGui::PushItemWidth(BUTTON_WIDTH - small_button_width);\n        modified |= ImGui::InputText(\"###Params\", info.Params, info.ParamsSize);\n        ImGui::SameLine(0.0f, 0.0f);\n        ImRect input_rect = g.LastItemData.Rect;\n        if (ImGui::Button(\"..\"))\n            ImGui::OpenPopup(\"CmdParamsPopup\");\n        input_rect.Add(g.LastItemData.Rect);\n        ImGui::SetNextWindowSize(ImVec2(input_rect.GetWidth(), 0.0f));\n        ImGui::SetNextWindowPos(input_rect.GetBL());\n        if (ImGui::BeginPopup(\"CmdParamsPopup\"))\n        {\n            ImGui::Text(\"Reset to default params for FFMPEG and %s file format:\", info.VideoFileExt);\n            ImGui::Indent();\n            float wrap_width = ImGui::GetContentRegionAvail().x - g.Style.FramePadding.x * 2;\n            ImVec2 text_size = ImGui::CalcTextSize(info.DefaultCmdLineParams, nullptr, false, wrap_width);\n            if (ImGui::Selectable(\"###Reset\", false, 0, text_size + g.Style.FramePadding * 2))\n            {\n                ImStrncpy(info.Params, info.DefaultCmdLineParams, info.ParamsSize);\n                ImGui::CloseCurrentPopup();\n            }\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n            draw_list->AddText(nullptr, 0, g.LastItemData.Rect.GetTL() + g.Style.FramePadding, ImGui::GetColorU32(ImGuiCol_Text), info.DefaultCmdLineParams, nullptr, wrap_width);\n            ImGui::Unindent();\n\n            ImGui::Separator();\n            ImGui::TextUnformatted(\n                \"Command line parameters passed to video encoder executable.\\n\"\n                \"Following variables may be used:\\n\"\n                \"$FPS     - target FPS\\n\"\n                \"$WIDTH   - width of captured frame\\n\"\n                \"$HEIGHT  - height of captured frame\\n\"\n                \"$OUTPUT  - video output file\");\n            ImGui::EndPopup();\n        }\n        ImGui::SameLine(0, g.Style.ItemInnerSpacing.x);\n        ImGui::TextUnformatted(info.Title);\n        if (!info.Params[0])\n            ImGui::ItemErrorFrame(IM_COL32(255, 0, 0, 255));\n        ImGui::PopID();\n    }\n\n    if (VideoCaptureExtensionSize)\n    {\n        IM_ASSERT(VideoCaptureExtension != nullptr);\n        ImGui::PushItemWidth(BUTTON_WIDTH);\n        if (ImGui::BeginCombo(\"Video format (default)\", VideoCaptureExtension))\n        {\n            const char* supported_exts[] = { \".gif\", \".mp4\" };\n            for (auto& ext: supported_exts)\n                if (ImGui::Selectable(ext, strcmp(VideoCaptureExtension, ext) == 0))\n                {\n                    ImStrncpy(VideoCaptureExtension, ext, VideoCaptureExtensionSize);\n                    modified = true;\n                }\n            ImGui::EndCombo();\n        }\n        ImGui::SetItemTooltip(\"File extension for captured video file.\");\n    }\n    return modified;\n}\n\n//-----------------------------------------------------------------------------\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/source/imgui_te_context.cpp",
    "content": "// dear imgui\n// (context when a running test + end user automation API)\n// This is the main (if not only) interface that your Tests will be using.\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#define IMGUI_DEFINE_MATH_OPERATORS\n#include \"imgui_te_context.h\"\n#include \"imgui.h\"\n#include \"imgui_internal.h\"\n#include \"imgui_te_engine.h\"\n#include \"imgui_te_internal.h\"\n#include \"imgui_te_perftool.h\"\n#include \"imgui_te_utils.h\"\n#include \"thirdparty/Str/Str.h\"\n\n//-------------------------------------------------------------------------\n// [SECTION] ImGuiTestRefDesc\n//-------------------------------------------------------------------------\n\nImGuiTestRefDesc::ImGuiTestRefDesc(const ImGuiTestRef& ref)\n{\n    if (ref.Path && ref.ID != 0)\n        ImFormatString(Buf, IM_ARRAYSIZE(Buf), \"'%s' (id 0x%08X)\", ref.Path, ref.ID);\n    else if (ref.Path)\n        ImFormatString(Buf, IM_ARRAYSIZE(Buf), \"'%s'\", ref.Path);\n    else\n        ImFormatString(Buf, IM_ARRAYSIZE(Buf), \"0x%08X\", ref.ID);\n}\n\nImGuiTestRefDesc::ImGuiTestRefDesc(const ImGuiTestRef& ref, const ImGuiTestItemInfo& item)\n{\n    if (ref.Path && item.ID != 0)\n        ImFormatString(Buf, IM_ARRAYSIZE(Buf), \"'%s' (id 0x%08X)\", ref.Path, item.ID);\n    else if (ref.Path)\n        ImFormatString(Buf, IM_ARRAYSIZE(Buf), \"'%s'\", ref.Path);\n    else\n        ImFormatString(Buf, IM_ARRAYSIZE(Buf), \"0x%08X (label \\\"%s\\\")\", ref.ID, item.DebugLabel);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] ImGuiTestContextDepthScope\n//-------------------------------------------------------------------------\n\n// Helper to increment/decrement the function depth (so our log entry can be padded accordingly)\n#define IM_TOKENCONCAT_INTERNAL(x, y)                   x ## y\n#define IM_TOKENCONCAT(x, y)                            IM_TOKENCONCAT_INTERNAL(x, y)\n#define IMGUI_TEST_CONTEXT_REGISTER_DEPTH(_THIS)        ImGuiTestContextDepthScope IM_TOKENCONCAT(depth_register, __LINE__)(_THIS)\n\nstruct ImGuiTestContextDepthScope\n{\n    ImGuiTestContext* TestContext;\n    ImGuiTestContextDepthScope(ImGuiTestContext* ctx) { TestContext = ctx; TestContext->ActionDepth++; }\n    ~ImGuiTestContextDepthScope() { TestContext->ActionDepth--; }\n};\n\n//-------------------------------------------------------------------------\n// [SECTION] Enum names helpers\n//-------------------------------------------------------------------------\n\ninline const char* GetActionName(ImGuiTestAction action)\n{\n    switch (action)\n    {\n    case ImGuiTestAction_Unknown:       return \"Unknown\";\n    case ImGuiTestAction_Hover:         return \"Hover\";\n    case ImGuiTestAction_Click:         return \"Click\";\n    case ImGuiTestAction_DoubleClick:   return \"DoubleClick\";\n    case ImGuiTestAction_Check:         return \"Check\";\n    case ImGuiTestAction_Uncheck:       return \"Uncheck\";\n    case ImGuiTestAction_Open:          return \"Open\";\n    case ImGuiTestAction_Close:         return \"Close\";\n    case ImGuiTestAction_Input:         return \"Input\";\n    case ImGuiTestAction_NavActivate:   return \"NavActivate\";\n    case ImGuiTestAction_COUNT:\n    default:                            return \"N/A\";\n    }\n}\n\ninline const char* GetActionVerb(ImGuiTestAction action)\n{\n    switch (action)\n    {\n    case ImGuiTestAction_Unknown:       return \"Unknown\";\n    case ImGuiTestAction_Hover:         return \"Hovered\";\n    case ImGuiTestAction_Click:         return \"Clicked\";\n    case ImGuiTestAction_DoubleClick:   return \"DoubleClicked\";\n    case ImGuiTestAction_Check:         return \"Checked\";\n    case ImGuiTestAction_Uncheck:       return \"Unchecked\";\n    case ImGuiTestAction_Open:          return \"Opened\";\n    case ImGuiTestAction_Close:         return \"Closed\";\n    case ImGuiTestAction_Input:         return \"Input\";\n    case ImGuiTestAction_NavActivate:   return \"NavActivated\";\n    case ImGuiTestAction_COUNT:\n    default:                            return \"N/A\";\n    }\n}\n\n\n//-------------------------------------------------------------------------\n// [SECTION] ImGuiTestContext\n// This is the interface that most tests will interact with.\n//-------------------------------------------------------------------------\n\nvoid    ImGuiTestContext::LogEx(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    LogExV(level, flags, fmt, args);\n    va_end(args);\n}\n\nvoid    ImGuiTestContext::LogExV(ImGuiTestVerboseLevel level, ImGuiTestLogFlags flags, const char* fmt, va_list args)\n{\n    ImGuiTestContext* ctx = this;\n    //ImGuiTest* test = ctx->Test;\n\n    IM_ASSERT(level > ImGuiTestVerboseLevel_Silent && level < ImGuiTestVerboseLevel_COUNT);\n\n    if (level == ImGuiTestVerboseLevel_Debug && ctx->ActionDepth > 1)\n        level = ImGuiTestVerboseLevel_Trace;\n\n    // Log all messages that we may want to print in future.\n    if (EngineIO->ConfigVerboseLevelOnError < level)\n        return;\n\n    ImGuiTestLog* log = &ctx->TestOutput->Log;\n    const int prev_size = log->Buffer.size();\n\n    //const char verbose_level_char = ImGuiTestEngine_GetVerboseLevelName(level)[0];\n    //if (flags & ImGuiTestLogFlags_NoHeader)\n    //    log->Buffer.appendf(\"[%c] \", verbose_level_char);\n    //else\n    //    log->Buffer.appendf(\"[%c] [%04d] \", verbose_level_char, ctx->FrameCount);\n    if ((flags & ImGuiTestLogFlags_NoHeader) == 0)\n        log->Buffer.appendf(\"[%04d] \", ctx->FrameCount);\n\n    if (level >= ImGuiTestVerboseLevel_Debug)\n        log->Buffer.appendf(\"-- %*s\", ImMax(0, (ctx->ActionDepth - 1) * 2), \"\");\n    log->Buffer.appendfv(fmt, args);\n    log->Buffer.append(\"\\n\");\n\n    log->UpdateLineOffsets(EngineIO, level, log->Buffer.begin() + prev_size);\n    LogToTTY(level, log->Buffer.c_str() + prev_size);\n    LogToDebugger(level, log->Buffer.c_str() + prev_size);\n}\n\nvoid    ImGuiTestContext::LogDebug(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    LogExV(ImGuiTestVerboseLevel_Debug, ImGuiTestLogFlags_None, fmt, args);\n    va_end(args);\n}\n\nvoid ImGuiTestContext::LogInfo(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    LogExV(ImGuiTestVerboseLevel_Info, ImGuiTestLogFlags_None, fmt, args);\n    va_end(args);\n}\n\nvoid ImGuiTestContext::LogWarning(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    LogExV(ImGuiTestVerboseLevel_Warning, ImGuiTestLogFlags_None, fmt, args);\n    va_end(args);\n}\n\nvoid ImGuiTestContext::LogError(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    LogExV(ImGuiTestVerboseLevel_Error, ImGuiTestLogFlags_None, fmt, args);\n    va_end(args);\n}\n\nvoid    ImGuiTestContext::LogToTTY(ImGuiTestVerboseLevel level, const char* message, const char* message_end)\n{\n    IM_ASSERT(level > ImGuiTestVerboseLevel_Silent && level < ImGuiTestVerboseLevel_COUNT);\n\n    if (!EngineIO->ConfigLogToTTY)\n        return;\n\n    ImGuiTestContext* ctx = this;\n    ImGuiTestOutput* test_output = ctx->TestOutput;\n    ImGuiTestLog* log = &test_output->Log;\n\n    if (test_output->Status == ImGuiTestStatus_Error)\n    {\n        // Current test failed.\n        if (!CachedLinesPrintedToTTY)\n        {\n            // Print all previous logged messages first\n            // FIXME: Can't use ExtractLinesAboveVerboseLevel() because we want to keep error level...\n            CachedLinesPrintedToTTY = true;\n            for (int i = 0; i < log->LineInfo.Size; i++)\n            {\n                ImGuiTestLogLineInfo& line_info = log->LineInfo[i];\n                if (line_info.Level > EngineIO->ConfigVerboseLevelOnError)\n                    continue;\n                char* line_begin = log->Buffer.Buf.Data + line_info.LineOffset;\n                char* line_end = strchr(line_begin, '\\n');\n                LogToTTY(line_info.Level, line_begin, line_end + 1);\n            }\n            // We already printed current line as well, so return now.\n            return;\n        }\n        // Otherwise print only current message. If we are executing here log level already is within range of\n        // ConfigVerboseLevelOnError setting.\n    }\n    else if (EngineIO->ConfigVerboseLevel < level)\n    {\n        // Skip printing messages of lower level than configured.\n        return;\n    }\n\n    switch (level)\n    {\n    case ImGuiTestVerboseLevel_Warning:\n        ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_BrightYellow);\n        break;\n    case ImGuiTestVerboseLevel_Error:\n        ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_BrightRed);\n        break;\n    default:\n        ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_White);\n        break;\n    }\n    if (message_end)\n        fprintf(stdout, \"%.*s\", (int)(message_end - message), message);\n    else\n        fprintf(stdout, \"%s\", message);\n    ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_White);\n    fflush(stdout);\n}\n\nvoid        ImGuiTestContext::LogToDebugger(ImGuiTestVerboseLevel level, const char* message)\n{\n    IM_ASSERT(level > ImGuiTestVerboseLevel_Silent && level < ImGuiTestVerboseLevel_COUNT);\n\n    if (!EngineIO->ConfigLogToDebugger)\n        return;\n\n    if (EngineIO->ConfigVerboseLevel < level)\n        return;\n\n    switch (level)\n    {\n    default:\n        break;\n    case ImGuiTestVerboseLevel_Error:\n        ImOsOutputDebugString(\"[error] \");\n        break;\n    case ImGuiTestVerboseLevel_Warning:\n        ImOsOutputDebugString(\"[warn.] \");\n        break;\n    case ImGuiTestVerboseLevel_Info:\n        ImOsOutputDebugString(\"[info ] \");\n        break;\n    case ImGuiTestVerboseLevel_Debug:\n        ImOsOutputDebugString(\"[debug] \");\n        break;\n    case ImGuiTestVerboseLevel_Trace:\n        ImOsOutputDebugString(\"[trace] \");\n        break;\n    }\n\n    ImOsOutputDebugString(message);\n}\n\nvoid    ImGuiTestContext::LogBasicUiState()\n{\n    ImGuiID item_hovered_id = UiContext->HoveredIdPreviousFrame;\n    ImGuiID item_active_id = UiContext->ActiveId;\n    ImGuiTestItemInfo* item_hovered_info = item_hovered_id ? ImGuiTestEngine_FindItemInfo(Engine, item_hovered_id, \"\") : nullptr;\n    ImGuiTestItemInfo* item_active_info = item_active_id ? ImGuiTestEngine_FindItemInfo(Engine, item_active_id, \"\") : nullptr;\n    LogDebug(\"Hovered: 0x%08X (\\\"%s\\\"), Active:  0x%08X(\\\"%s\\\")\",\n        item_hovered_id, item_hovered_info->ID != 0 ? item_hovered_info->DebugLabel : \"\",\n        item_active_id, item_active_info->ID != 0 ? item_active_info->DebugLabel : \"\");\n}\n\nvoid    ImGuiTestContext::LogItemList(ImGuiTestItemList* items)\n{\n    for (const ImGuiTestItemInfo& info : *items)\n        LogDebug(\"- 0x%08X: depth %d: '%s' in window '%s'\\n\", info.ID, info.Depth, info.DebugLabel, info.Window->Name);\n}\n\nvoid    ImGuiTestContext::Finish(ImGuiTestStatus status)\n{\n    if (ActiveFunc == ImGuiTestActiveFunc_GuiFunc)\n    {\n        IM_ASSERT(status == ImGuiTestStatus_Success || status == ImGuiTestStatus_Unknown);\n        if (RunFlags & ImGuiTestRunFlags_GuiFuncOnly)\n            return;\n        if (TestOutput->Status == ImGuiTestStatus_Running)\n            TestOutput->Status = status;\n    }\n    else if (ActiveFunc == ImGuiTestActiveFunc_TestFunc)\n    {\n        IM_ASSERT(status == ImGuiTestStatus_Unknown); // To set Success from a TestFunc() you can 'return' from it.\n        if (TestOutput->Status == ImGuiTestStatus_Running)\n            TestOutput->Status = status;\n    }\n}\n\nvoid    ImGuiTestContext::Yield(int count)\n{\n    IM_ASSERT(count > 0);\n    while (count > 0)\n    {\n        ImGuiTestEngine_Yield(Engine);\n        count--;\n    }\n}\n\n// Supported values for ImGuiTestRunFlags:\n// - ImGuiTestRunFlags_NoError: if child test fails, return false and do not mark parent test as failed.\n// - ImGuiTestRunFlags_ShareVars: share generic vars and custom vars between child and parent tests.\n// - ImGuiTestRunFlags_ShareTestContext\nImGuiTestStatus ImGuiTestContext::RunChildTest(const char* child_test_name, ImGuiTestRunFlags run_flags)\n{\n    if (IsError())\n        return ImGuiTestStatus_Error;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"RunChildTest %s\", child_test_name);\n\n    ImGuiTest* child_test = ImGuiTestEngine_FindTestByName(Engine, nullptr, child_test_name);\n    IM_CHECK_SILENT_RETV(child_test != nullptr, ImGuiTestStatus_Error);\n    IM_CHECK_SILENT_RETV(child_test != Test, ImGuiTestStatus_Error); // Can't recursively run same test.\n\n    ImGuiTestStatus parent_status = TestOutput->Status;\n    TestOutput->Status = ImGuiTestStatus_Running;\n    ImGuiTestEngine_RunTest(Engine, this, child_test, run_flags);\n    ImGuiTestStatus child_status = TestOutput->Status;\n\n    // Restore parent status\n    TestOutput->Status = parent_status;\n    if (child_status == ImGuiTestStatus_Error && (run_flags & ImGuiTestRunFlags_NoError) == 0)\n        TestOutput->Status = ImGuiTestStatus_Error;\n\n    // Return child status\n    LogDebug(\"(returning to parent test)\");\n    return child_status;\n}\n\n// Return true to request aborting TestFunc\n// Called via IM_SUSPEND_TESTFUNC()\nbool    ImGuiTestContext::SuspendTestFunc(const char* file, int line)\n{\n    if (IsError())\n        return false;\n\n    if (file != nullptr)\n    {\n        file = ImPathFindFilename(file);\n        LogError(\"SuspendTestFunc() at %s:%d\", file, line);\n    }\n    else\n    {\n        LogError(\"SuspendTestFunc()\");\n    }\n\n    // Save relevant state.\n    // FIXME-TESTS: Saving/restoring window z-order could be desirable.\n    ImVec2 mouse_pos = Inputs->MousePosValue;\n    ImGuiTestRunFlags run_flags = RunFlags;\n#if IMGUI_VERSION_NUM >= 18992\n    ImGui::TeleportMousePos(mouse_pos);\n#endif\n\n    RunFlags |= ImGuiTestRunFlags_GuiFuncOnly;\n    TestOutput->Status = ImGuiTestStatus_Suspended;\n    while (TestOutput->Status == ImGuiTestStatus_Suspended && !Abort)\n        Yield();\n    TestOutput->Status = ImGuiTestStatus_Running;\n\n    // Restore relevant state.\n    RunFlags = run_flags;\n    Inputs->MousePosValue = mouse_pos;\n\n    // Terminate TestFunc on abort, continue otherwise.\n    return Abort;\n}\n\n// Sleep a given amount of time (unless running in Fast mode: there it will Yield once)\nvoid    ImGuiTestContext::Sleep(float time)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Fast)\n    {\n        LogEx(ImGuiTestVerboseLevel_Trace, ImGuiTestLogFlags_None, \"Sleep(%.2f) -> Yield() in fast mode\", time);\n        //ImGuiTestEngine_AddExtraTime(Engine, time); // We could add time, for now we have no use for it...\n        ImGuiTestEngine_Yield(Engine);\n    }\n    else\n    {\n        LogEx(ImGuiTestVerboseLevel_Trace, ImGuiTestLogFlags_None, \"Sleep(%.2f)\", time);\n        while (time > 0.0f && !Abort)\n        {\n            ImGuiTestEngine_Yield(Engine);\n            time -= UiContext->IO.DeltaTime;\n        }\n    }\n}\n\n// This is useful when you need to wait a certain amount of time (even in Fast mode)\n// Sleep for a given clock time from the point of view of the Dear ImGui context, without affecting wall clock time of the running application.\n// FIXME: This makes sense for apps only relying on io.DeltaTime.\nvoid    ImGuiTestContext::SleepNoSkip(float time, float framestep_in_second)\n{\n    if (IsError())\n        return;\n\n    LogDebug(\"SleepNoSkip %f seconds (in %f increments)\", time, framestep_in_second);\n    while (time > 0.0f && !Abort)\n    {\n        ImGuiTestEngine_SetDeltaTime(Engine, framestep_in_second);\n        ImGuiTestEngine_Yield(Engine);\n        time -= UiContext->IO.DeltaTime;\n    }\n}\n\nvoid    ImGuiTestContext::SleepShort()\n{\n    if (EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast)\n        Sleep(EngineIO->ActionDelayShort);\n}\n\nvoid    ImGuiTestContext::SleepStandard()\n{\n    if (EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast)\n        Sleep(EngineIO->ActionDelayStandard);\n}\n\nvoid ImGuiTestContext::SetInputMode(ImGuiInputSource input_mode)\n{\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"SetInputMode %d\", input_mode);\n\n    IM_ASSERT(input_mode == ImGuiInputSource_Mouse || input_mode == ImGuiInputSource_Keyboard || input_mode == ImGuiInputSource_Gamepad);\n    InputMode = input_mode;\n\n    if (InputMode == ImGuiInputSource_Keyboard || InputMode == ImGuiInputSource_Gamepad)\n    {\n#if IMGUI_VERSION_NUM >= 19136\n        ImGui::SetNavCursorVisible(true);\n        UiContext->NavHighlightItemUnderNav = true;\n#else\n        UiContext->NavDisableHighlight = false;\n        UiContext->NavDisableMouseHover = true;\n#endif\n    }\n    else\n    {\n#if IMGUI_VERSION_NUM >= 19136\n        ImGui::SetNavCursorVisible(false);\n        UiContext->NavHighlightItemUnderNav = false;\n#else\n        UiContext->NavDisableHighlight = true;\n        UiContext->NavDisableMouseHover = false;\n#endif\n    }\n}\n\n// Shortcut for when we have a window pointer, avoid mistakes with slashes in child names.\nvoid ImGuiTestContext::SetRef(ImGuiWindow* window)\n{\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    IM_CHECK_SILENT(window != nullptr);\n    LogDebug(\"SetRef '%s' 0x%08X\", window->Name, window->ID);\n\n    // We grab the ID directly and avoid ImHashDecoratedPath so \"/\" in window names are not ignored.\n    size_t len = strlen(window->Name);\n    IM_ASSERT(len < IM_ARRAYSIZE(RefStr) - 1);\n    strcpy(RefStr, window->Name);\n    RefID = RefWindowID = window->ID;\n\n    MouseSetViewport(window);\n\n    // Automatically uncollapse by default\n    if (!(OpFlags & ImGuiTestOpFlags_NoAutoUncollapse))\n        WindowCollapse(window->ID, false);\n}\n\n// It is exceptionally OK to call SetRef() in GuiFunc, as a facility to call seeded ctx->GetId() in GuiFunc.\n// FIXME-TESTS: May be good to focus window when docked? Otherwise locate request won't even see an item?\nvoid ImGuiTestContext::SetRef(ImGuiTestRef ref)\n{\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    if (ActiveFunc == ImGuiTestActiveFunc_TestFunc)\n        LogDebug(\"SetRef '%s' 0x%08X\", ref.Path ? ref.Path : \"nullptr\", ref.ID);\n\n    if (ref.Path)\n    {\n        size_t len = strlen(ref.Path);\n        IM_ASSERT(len < IM_ARRAYSIZE(RefStr) - 1);\n\n        strcpy(RefStr, ref.Path);\n        RefID = GetID(ref.Path, ImGuiTestRef());\n    }\n    else\n    {\n        RefStr[0] = 0;\n        RefID = ref.ID;\n    }\n    RefWindowID = 0;\n\n    // Early out\n    if (ref.IsEmpty())\n        return;\n\n    // Try to infer window\n    // (This is in order to set viewport, uncollapse window, and store its base id for leading \"/\" operator)\n\n    // (0) Windows is fully specified in path?\n    ImGuiWindow* window = GetWindowByRef(\"\");\n\n    // (1) Try first element of ref path, it is most likely a window name and item lookup won't be necessary.\n    if (window == nullptr && ref.Path != nullptr)\n    {\n        // \"Window/SomeItem\" -> search for \"Window\"\n        const char* name_begin = ref.Path;\n        while (*name_begin == '/') // Skip leading slashes\n            name_begin++;\n        const char* name_end = name_begin - 1;\n        do\n        {\n            name_end = strchr(name_end + 1, '/');\n        } while (name_end != nullptr && name_end > name_begin && name_end[-1] == '\\\\');\n        window = GetWindowByRef(ImHashDecoratedPath(name_begin, name_end));\n    }\n\n    if (ActiveFunc == ImGuiTestActiveFunc_GuiFunc)\n        return;\n\n    // (2) Ref was specified as an ID and points to an item therefore item lookup is unavoidable.\n    // FIXME: Maybe display something in log when that happens?\n    if (window == nullptr)\n    {\n        ImGuiTestItemInfo item_info = ItemInfo(RefID, ImGuiTestOpFlags_NoError);\n        if (item_info.ID != 0)\n            window = item_info.Window;\n    }\n\n    // Set viewport and base ID for single \"/\" operator.\n    if (window)\n    {\n        RefWindowID = window->ID;\n        MouseSetViewport(window);\n    }\n\n    // Automatically uncollapse by default\n    if (window && !(OpFlags & ImGuiTestOpFlags_NoAutoUncollapse))\n        WindowCollapse(window->ID, false);\n}\n\nImGuiTestRef ImGuiTestContext::GetRef()\n{\n    return RefID;\n}\n\nImGuiWindow* ImGuiTestContext::GetWindowByRef(ImGuiTestRef ref)\n{\n    ImGuiID window_id = GetID(ref);\n    ImGuiWindow* window = ImGui::FindWindowByID(window_id);\n    return window;\n}\n\nImGuiID ImGuiTestContext::GetID(ImGuiTestRef ref)\n{\n    if (ref.ID)\n        return ref.ID;\n\n    return GetID(ref, RefID);\n}\n\n// Refer to Wiki to read details\n// https://github.com/ocornut/imgui_test_engine/wiki/Named-References\n// - Meaning of leading \"//\" ................. \"//rootnode\" : ignore SetRef\n// - Meaning of leading \"//$FOCUSED\" ......... \"//$FOCUSED/node\" : \"node\" in currently focused window\n// - Meaning of leading \"/\" .................. \"/node\" : move to root of window pointed by SetRef() when SetRef() uses a path\n// - Meaning of $$xxxx literal encoding ...... \"list/$$1\" : hash of \"list\" + hash if (int)1, equivalent of PushID(\"hello\"); PushID(1);\n//// - Meaning of leading \"../\" .............. \"../node\" : move back 1 level from SetRef path() when SetRef() uses a path // Unimplemented\n// FIXME: \"//$FOCUSED/..\" is currently not usable.\nImGuiID ImGuiTestContext::GetID(ImGuiTestRef ref, ImGuiTestRef seed_ref)\n{\n    ImGuiContext& g = *UiContext;\n\n    if (ref.ID)\n        return ref.ID; // FIXME: What if seed_ref != 0\n\n    // Handle special $FOCUSED variable.\n    // (Note that we don't and can't really support a \"$HOVERED\" equivalent for the hovered window.\n    //  Why? Because it is extremely fragile to use: with late translation of variable held in string,\n    //  it is extremely common that the \"expected\" hovered window at the time of passing the string has\n    //  changed in later uses of the same reference.)\n    // You can however easily use:\n    //   SetRef(g.HoveredWindow->ID);\n    const char* FOCUSED_PREFIX = \"//$FOCUSED\";\n    const size_t FOCUSED_PREFIX_LEN = 10;\n\n    const char* path = ref.Path ? ref.Path : \"\";\n    if (strncmp(path, FOCUSED_PREFIX, FOCUSED_PREFIX_LEN) == 0)\n        if (path[FOCUSED_PREFIX_LEN] == '/' || path[FOCUSED_PREFIX_LEN] == 0)\n        {\n            path += FOCUSED_PREFIX_LEN;\n            if (path[0] == '/')\n                path++;\n            if (g.NavWindow)\n                seed_ref = g.NavWindow->ID;\n            else\n                LogError(\"\\\"//$FOCUSED\\\" was used with no focused window!\");\n        }\n\n    if (path[0] == '/')\n    {\n        path++;\n        if (path[0] == '/')\n        {\n            // \"//\" : Double-slash prefix resets ID seed to 0.\n            seed_ref = ImGuiTestRef();\n        }\n        else\n        {\n            // \"/\" : Single-slash prefix sets seed to the \"current window\", which a parent window containing an item with RefID id.\n            if (ActiveFunc == ImGuiTestActiveFunc_GuiFunc)\n                seed_ref = ImGuiTestRef(g.CurrentWindow->ID);\n            else\n                seed_ref = RefWindowID;\n        }\n    }\n\n    return ImHashDecoratedPath(path, nullptr, seed_ref.Path ? GetID(seed_ref) : seed_ref.ID);\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\nImGuiID ImGuiTestContext::GetIDByInt(int n)\n{\n    return ImHashData(&n, sizeof(n), GetID(RefID));\n}\n\nImGuiID ImGuiTestContext::GetIDByInt(int n, ImGuiTestRef seed_ref)\n{\n    return ImHashData(&n, sizeof(n), GetID(seed_ref));\n}\n\nImGuiID ImGuiTestContext::GetIDByPtr(void* p)\n{\n    return ImHashData(&p, sizeof(p), GetID(RefID));\n}\n\nImGuiID ImGuiTestContext::GetIDByPtr(void* p, ImGuiTestRef seed_ref)\n{\n    return ImHashData(&p, sizeof(p), GetID(seed_ref));\n}\n#endif\n\nImVec2 ImGuiTestContext::GetMainMonitorWorkPos()\n{\n#ifdef IMGUI_HAS_VIEWPORT\n    if (UiContext->IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)\n    {\n        const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ImGui::GetMainViewport());\n        return monitor->WorkPos;\n    }\n#endif\n    return ImGui::GetMainViewport()->WorkPos;\n}\n\nImVec2 ImGuiTestContext::GetMainMonitorWorkSize()\n{\n#ifdef IMGUI_HAS_VIEWPORT\n    if (UiContext->IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)\n    {\n        const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ImGui::GetMainViewport());\n        return monitor->WorkSize;\n    }\n#endif\n    return ImGui::GetMainViewport()->WorkSize;\n}\n\n#if IMGUI_TEST_ENGINE_ENABLE_CAPTURE\nstatic bool ImGuiTestContext_CanCaptureScreenshot(ImGuiTestContext* ctx)\n{\n    ImGuiTestEngineIO* io = ctx->EngineIO;\n    return io->ConfigCaptureEnabled;\n}\n\nstatic bool ImGuiTestContext_CanCaptureVideo(ImGuiTestContext* ctx)\n{\n    ImGuiTestEngineIO* io = ctx->EngineIO;\n    return io->ConfigCaptureEnabled && ImFileExist(io->VideoCaptureEncoderPath);\n}\n#endif\n\nbool ImGuiTestContext::CaptureAddWindow(ImGuiTestRef ref)\n{\n    ImGuiWindow* window = GetWindowByRef(ref);\n    IM_CHECK_SILENT_RETV(window != nullptr, false);\n    CaptureArgs->InCaptureWindows.push_back(window);\n    return true;\n}\n\nstatic void CaptureInitAutoFilename(ImGuiTestContext* ctx, const char* ext)\n{\n    IM_ASSERT(ext != nullptr && ext[0] == '.');\n\n    if (ctx->CaptureArgs->InOutputFile[0] == 0)\n        ctx->CaptureSetExtension(ext); // Reset extension of specified filename or auto-generate a new filename.\n}\n\nbool ImGuiTestContext::CaptureScreenshot(int capture_flags)\n{\n    if (IsError())\n        return false;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogInfo(\"CaptureScreenshot()\");\n    ImGuiCaptureArgs* args = CaptureArgs;\n    args->InFlags = capture_flags;\n\n    // Auto filename\n    CaptureInitAutoFilename(this, \".png\");\n\n#if IMGUI_TEST_ENGINE_ENABLE_CAPTURE\n    // Way capture tool is implemented doesn't prevent ClampWindowPos() from running,\n    // so we disable that feature at the moment. (imgui_test_engine/#33)\n    ImGuiIO& io = ImGui::GetIO();\n    bool backup_io_config_move_window_from_title_bar_only = io.ConfigWindowsMoveFromTitleBarOnly;\n    if (capture_flags & ImGuiCaptureFlags_StitchAll)\n        io.ConfigWindowsMoveFromTitleBarOnly = false;\n\n    bool can_capture = ImGuiTestContext_CanCaptureScreenshot(this);\n    if (!can_capture)\n        args->InFlags |= ImGuiCaptureFlags_NoSave;\n\n    bool ret = ImGuiTestEngine_CaptureScreenshot(Engine, args);\n    if (can_capture)\n        LogInfo(\"Saved '%s' (%d*%d pixels)\", args->InOutputFile, (int)args->OutImageSize.x, (int)args->OutImageSize.y);\n    else\n        LogWarning(\"Skipped saving '%s' (%d*%d pixels) (enable in 'Misc->Options')\", args->InOutputFile, (int)args->OutImageSize.x, (int)args->OutImageSize.y);\n\n    if (capture_flags & ImGuiCaptureFlags_StitchAll)\n        io.ConfigWindowsMoveFromTitleBarOnly = backup_io_config_move_window_from_title_bar_only;\n\n    return ret;\n#else\n    IM_UNUSED(args);\n    LogWarning(\"Skipped screenshot capture: disabled by IMGUI_TEST_ENGINE_ENABLE_CAPTURE=0.\");\n    return false;\n#endif\n}\n\nvoid ImGuiTestContext::CaptureReset()\n{\n    *CaptureArgs = ImGuiCaptureArgs();\n}\n\n// FIXME-TESTS: Add ImGuiCaptureFlags_NoHideOtherWindows\nvoid ImGuiTestContext::CaptureScreenshotWindow(ImGuiTestRef ref, int capture_flags)\n{\n    CaptureReset();\n    CaptureAddWindow(ref);\n    CaptureScreenshot(capture_flags);\n}\n\nvoid ImGuiTestContext::CaptureSetExtension(const char* ext)\n{\n    IM_ASSERT(ext && ext[0] == '.');\n    ImGuiCaptureArgs* args = CaptureArgs;\n    if (args->InOutputFile[0] == 0)\n    {\n        ImFormatString(args->InOutputFile, IM_ARRAYSIZE(args->InOutputFile), \"output/captures/%s_%04d%s\", Test->Name, CaptureCounter, ext);\n        CaptureCounter++;\n    }\n    else\n    {\n        char* filename_ext = (char*)ImPathFindExtension(args->InOutputFile);\n        ImStrncpy(filename_ext, ext, (size_t)(filename_ext - args->InOutputFile));\n    }\n}\n\nbool ImGuiTestContext::CaptureBeginVideo()\n{\n    if (IsError())\n        return false;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogInfo(\"CaptureBeginVideo()\");\n    ImGuiCaptureArgs* args = CaptureArgs;\n\n    // Auto filename\n    CaptureInitAutoFilename(this, EngineIO->VideoCaptureExtension);\n\n#if IMGUI_TEST_ENGINE_ENABLE_CAPTURE\n    bool can_capture = ImGuiTestContext_CanCaptureVideo(this);\n    if (!can_capture)\n        args->InFlags |= ImGuiCaptureFlags_NoSave;\n    return ImGuiTestEngine_CaptureBeginVideo(Engine, args);\n#else\n    IM_UNUSED(args);\n    LogWarning(\"Skipped recording GIF: disabled by IMGUI_TEST_ENGINE_ENABLE_CAPTURE=0.\");\n    return false;\n#endif\n}\n\nbool ImGuiTestContext::CaptureEndVideo()\n{\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogInfo(\"CaptureEndVideo()\");\n    ImGuiCaptureArgs* args = CaptureArgs;\n\n    bool ret = Engine->CaptureContext.IsCapturingVideo() && ImGuiTestEngine_CaptureEndVideo(Engine, args);\n    if (!ret)\n        return false;\n\n#if IMGUI_TEST_ENGINE_ENABLE_CAPTURE\n    // In-progress capture was canceled by user. Delete incomplete file.\n    if (IsError())\n    {\n        //ImFileDelete(args->OutSavedFileName);\n        return false;\n    }\n    bool can_capture = ImGuiTestContext_CanCaptureVideo(this);\n    if (can_capture)\n    {\n        LogInfo(\"Saved '%s' (%d*%d pixels)\", args->InOutputFile, (int)args->OutImageSize.x, (int)args->OutImageSize.y);\n    }\n    else\n    {\n        if (!EngineIO->ConfigCaptureEnabled)\n            LogWarning(\"Skipped saving '%s' video because: io.ConfigCaptureEnabled == false (enable in Misc->Options)\", args->InOutputFile);\n        else\n            LogWarning(\"Skipped saving '%s' video because: Video Encoder not found.\", args->InOutputFile);\n    }\n#endif\n\n    return ret;\n}\n\n// Handle wildcard search on the TestFunc side.\n// Results will be resolved on the Gui side via the following call-chain:\n//   IMGUI_TEST_ENGINE_ITEM_INFO() -> ImGuiTestEngineHook_ItemInfo() -> ImGuiTestEngineHook_ItemInfo_ResolveFindByLabel()\nImGuiID ImGuiTestContext::ItemInfoHandleWildcardSearch(const char* wildcard_prefix_start, const char* wildcard_prefix_end, const char* wildcard_suffix_start)\n{\n    LogDebug(\"Wildcard matching..\");\n\n    // Wildcard matching\n    // Note that task->InPrefixId may be 0 as well (= we don't know the window)\n    ImGuiTestFindByLabelTask* task = &Engine->FindByLabelTask;\n    if (wildcard_prefix_start < wildcard_prefix_end)\n        task->InPrefixId = ImHashDecoratedPath(wildcard_prefix_start, wildcard_prefix_end, RefID);\n    else\n        task->InPrefixId = RefID;\n    task->OutItemId = 0;\n\n    // Advance pointer to point it to the last label\n    task->InSuffix = task->InSuffixLastItem = wildcard_suffix_start;\n    for (const char* c = task->InSuffix; *c; c++)\n        if (*c == '/')\n            task->InSuffixLastItem = c + 1;\n    task->InSuffixLastItemHash = ImHashStr(task->InSuffixLastItem, 0, 0);\n\n    // Count number of labels\n    task->InSuffixDepth = 1;\n    for (const char* c = wildcard_suffix_start; *c; c++)\n        if (*c == '/')\n            task->InSuffixDepth++;\n\n    int retries = 0;\n    while (retries < 2 && task->OutItemId == 0)\n    {\n        ImGuiTestEngine_Yield(Engine);\n        retries++;\n    }\n\n    // Wildcard matching requires item to be visible, because clipped items are unaware of their labels. Try panning through entire window, searching for target item.\n    // (Scrollbar position restoration in theory may be desirable, however it interferes with typical use of found item)\n    // FIXME-TESTS: This doesn't recurse properly into each child..\n    // FIXME: Down the line if we refactor ItemAdd() return value to distinguish render-clipping vs logic-clipping etc, we should instead temporarily enable a \"no clip\"\n    // mode without the need for scrolling.\n    if (task->OutItemId == 0)\n    {\n        ImGuiTestItemInfo base_item = ItemInfo(task->InPrefixId, ImGuiTestOpFlags_NoError);\n        ImGuiWindow* window = (base_item.ID != 0) ? base_item.Window : GetWindowByRef(task->InPrefixId);\n        if (window)\n        {\n            ImVec2 rect_size = window->InnerRect.GetSize();\n            for (float scroll_x = 0.0f; task->OutItemId == 0; scroll_x += rect_size.x)\n            {\n                for (float scroll_y = 0.0f; task->OutItemId == 0; scroll_y += rect_size.y)\n                {\n                    window->Scroll.x = scroll_x;\n                    window->Scroll.y = scroll_y;\n\n                    retries = 0;\n                    while (retries < 2 && task->OutItemId == 0)\n                    {\n                        ImGuiTestEngine_Yield(Engine);\n                        retries++;\n                    }\n                    if (window->Scroll.y >= window->ScrollMax.y)\n                        break;\n                }\n                if (window->Scroll.x >= window->ScrollMax.x)\n                    break;\n            }\n        }\n    }\n    ImGuiID full_id = task->OutItemId;\n\n    // FIXME: InFilterItemStatusFlags is intentionally not cleared here, because it is set in ItemAction() and reused in later calls to ItemInfo() to resolve ambiguities.\n    task->InPrefixId = 0;\n    task->InSuffix = task->InSuffixLastItem = nullptr;\n    task->InSuffixLastItemHash = 0;\n    task->InSuffixDepth = 0;\n    task->OutItemId = 0;    // -V1048   // Variable 'OutItemId' was assigned the same value. False-positive, because value of OutItemId could be modified from other thread during ImGuiTestEngine_Yield() call.\n\n    return full_id;\n}\n\nstatic void ItemInfoErrorLog(ImGuiTestContext* ctx, ImGuiTestRef ref, ImGuiID full_id, ImGuiTestOpFlags flags)\n{\n    if (flags & ImGuiTestOpFlags_NoError)\n        return;\n\n    if (ctx->Engine->UiContextHasHooks == false)\n        IM_ERRORF_NOHDR(\"%s\", \"IMGUI DOES NOT SEEM COMPILED WITH '#define IMGUI_ENABLE_TEST_ENGINE'!\\nMAKE SURE THAT BOTH 'imgui' AND 'imgui_test_engine' ARE USING THE SAME 'imconfig' FILE.\");\n\n    // Prefixing the string with / ignore the reference/current ID\n    Str256 msg;\n    if (ref.Path && ref.Path[0] == '/' && ctx->RefStr[0] != 0)\n        msg.setf(\"Unable to locate item: '%s' (0x%08X)\", ref.Path, full_id);\n    else if (ref.Path && full_id != 0)\n        msg.setf(\"Unable to locate item: '%s/%s' (0x%08X)\", ctx->RefStr, ref.Path, full_id);\n    else if (ref.Path)\n        msg.setf(\"Unable to locate item: '%s/%s' (0x%08X)\", ctx->RefStr, ref.Path, full_id);\n    else\n        msg.setf(\"Unable to locate item: 0x%08X\", ref.ID);\n\n    //if (flags & ImGuiTestOpFlags_NoError)\n    //    ctx->LogInfo(\"Ignored: %s\", msg.c_str()); // FIXME\n    //else\n    IM_ERRORF_NOHDR(\"%s\", msg.c_str());\n}\n\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoError\nImGuiTestItemInfo ImGuiTestContext::ItemInfo(ImGuiTestRef ref, ImGuiTestOpFlags flags)\n{\n    if (IsError())\n        return ItemInfoNull();\n\n    const ImGuiTestOpFlags SUPPORTED_FLAGS = ImGuiTestOpFlags_NoError;\n    IM_ASSERT((flags & ~SUPPORTED_FLAGS) == 0);\n\n    ImGuiID full_id = 0;\n\n    if (const char* p = ref.Path ? strstr(ref.Path, \"**/\") : nullptr)\n    {\n        // Wildcard matching\n        // FIXME-TESTS: Need to verify that this is not inhibited by a \\, so \\**/ should not pass, but \\\\**/ should :)\n        // We could add a simple helpers that would iterate the strings, handling inhibitors, and let you check if a given characters is inhibited or not.\n        const char* wildcard_prefix_start = ref.Path;\n        const char* wildcard_prefix_end = p;\n        const char* wildcard_suffix_start = wildcard_prefix_end + 3;\n        full_id = ItemInfoHandleWildcardSearch(wildcard_prefix_start, wildcard_prefix_end, wildcard_suffix_start);\n    }\n    else\n    {\n        // Regular matching\n        full_id = GetID(ref);\n    }\n\n    // If ui_ctx->TestEngineHooksEnabled is not already on (first ItemInfo() task in a while) we'll probably need an extra frame to warmup\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestItemInfo* item = nullptr;\n    int retries = 0;\n    int max_retries = 2;\n    int extra_retries_for_appearing = 0;\n    while (full_id && retries < max_retries)\n    {\n        item = ImGuiTestEngine_FindItemInfo(Engine, full_id, ref.Path);\n\n        // While a window is appearing it is likely to be resizing and items moving. Wait an extra frame for things to settle. (FIXME: Could use another source e.g. Hidden? AutoFitFramesX?)\n        if (item && item->Window && item->Window->Appearing && extra_retries_for_appearing == 0)\n        {\n            item = nullptr;\n            max_retries++;\n            extra_retries_for_appearing++;\n        }\n\n        if (item)\n            return *item;\n        ImGuiTestEngine_Yield(Engine);\n        retries++;\n    }\n\n    ItemInfoErrorLog(this, ref, full_id, flags);\n\n    return ItemInfoNull();\n}\n\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoError\nImGuiTestItemInfo ImGuiTestContext::ItemInfoOpenFullPath(ImGuiTestRef ref, ImGuiTestOpFlags flags)\n{\n    // First query\n    bool can_open_full_path = (ref.Path != nullptr);\n    ImGuiTestItemInfo item = ItemInfo(ref, (can_open_full_path ? ImGuiTestOpFlags_NoError : ImGuiTestOpFlags_None) | (flags & ImGuiTestOpFlags_NoError));\n    if (item.ID != 0)\n        return item;\n    if (!can_open_full_path)\n        return ItemInfoNull();\n\n    // Tries to auto open intermediaries leading to final path.\n    // Note that openables cannot be part of the **/ (else it means we would have to open everything).\n    // - Openables can be before the wildcard    \"Node2/Node3/**/Button\"\n    // - Openables can be after the wildcard     \"**/Node2/Node3/Lv4/Button\"\n    int opened_parents = 0;\n    for (const char* parent_end = strstr(ref.Path, \"/\"); parent_end != nullptr; parent_end = strstr(parent_end + 1, \"/\"))\n    {\n        // Skip \"**/* sections\n        if (strncmp(ref.Path, \"**/\", parent_end - ref.Path) == 0)\n            continue;\n\n        Str128 parent_id;\n        parent_id.set(ref.Path, parent_end);\n        ImGuiTestItemInfo parent_item = ItemInfo(parent_id.c_str(), ImGuiTestOpFlags_NoError);\n        if (parent_item.ID != 0)\n        {\n#ifdef IMGUI_HAS_DOCK\n            ImGuiWindow* parent_window = parent_item.Window;\n#endif\n            if ((parent_item.StatusFlags & ImGuiItemStatusFlags_Openable) != 0 && (parent_item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0)\n            {\n                // Open intermediary item\n                if ((parent_item.ItemFlags & ImGuiItemFlags_Disabled) == 0) // FIXME: Report disabled state in log?\n                {\n                    ItemAction(ImGuiTestAction_Open, parent_item.ID, ImGuiTestOpFlags_NoAutoOpenFullPath);\n                    opened_parents++;\n                }\n            }\n#ifdef IMGUI_HAS_DOCK\n            else if (parent_window->ID == parent_item.ID && parent_window->DockIsActive && parent_window->DockTabIsVisible == false)\n            {\n                // Make tab visible\n                ItemClick(parent_item.ID);\n                opened_parents++;\n            }\n#endif\n        }\n    }\n    if (opened_parents > 0)\n        item = ItemInfo(ref, (flags & ImGuiTestOpFlags_NoError));\n\n    if (item.ID == 0)\n        ItemInfoErrorLog(this, ref, 0, flags);\n\n    return item;\n}\n\n// Find a window given a path or an ID.\n// In the case of when a path is passed, this handle finding child windows as well.\n// e.g.\n//   ctx->WindowInfo(\"//Test Window\");                          // OK\n//   ctx->WindowInfo(\"//Test Window/Child/SubChild\");           // OK\n//   ctx->WindowInfo(\"//$FOCUSED/Child\");                       // OK\n//   ctx->SetRef(\"Test Window); ctx->WindowInfo(\"Child\");       // OK\n//   ctx->WindowInfo(GetID(\"//Test Window\"));                   // OK (find by raw ID without a path)\n//   ctx->WindowInfo(GetID(\"//Test Window/Child/SubChild));     // *INCORRECT* GetID() doesn't unmangle child names.\n//   ctx->WindowInfo(\"//Test Window/Button\");                   // *INCORRECT* Only finds windows, not items.\n// Return:\n// - Return pointer is always valid.\n// - Valid fields are:\n//   - item->ID     : window ID      (may be == 0, if the window doesn't exist)\n//   - item->Window : window pointer (may be == nullptr, if the window doesn't exist)\n//   - Other fields correspond to the title-bar/tab item of a window, so likely not what you want (same as using IsItemXXX after Begin)\n//   - If you want other fields simply get them via the window-> pointer.\n// - Likely you may want to feed the return value into SetRef(): e.g. 'ctx->SetRef(item->ID)' or 'ctx->SetRef(WindowInfo(\"//Window/Child\")->ID);'\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoError\n// Todos:\n// - FIXME: Missing support for wildcards.\nImGuiTestItemInfo ImGuiTestContext::WindowInfo(ImGuiTestRef ref, ImGuiTestOpFlags flags)\n{\n    if (IsError())\n        return ItemInfoNull();\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n\n    // Query by ID (not very useful but supported)\n    if (ref.ID != 0)\n    {\n        LogDebug(\"WindowInfo: by id: 0x%08X\", ref.ID);\n        IM_ASSERT(ref.Path == nullptr);\n        ImGuiWindow* window = GetWindowByRef(ref);\n        if (window == nullptr)\n        {\n            if ((flags & ImGuiTestOpFlags_NoError) == 0)\n                LogError(\"WindowInfo: cannot find window by ID!\"); // FIXME: What if we want to query a not-yet-existing window by ID?\n            return ItemInfoNull();\n        }\n        return ItemInfo(window->ID, flags & ImGuiTestOpFlags_NoError);\n    }\n\n    // Query by Path: this is where the meat of our work is.\n    LogDebug(\"WindowInfo: by path: '%s'\", ref.Path ? ref.Path : \"nullptr\");\n    ImGuiWindow* window = nullptr;\n    ImGuiID window_idstack_back = 0;\n    const char* current = ref.Path;\n    while (*current || window == nullptr)\n    {\n        // Handle SetRef(), if any (this will also handle \"//$FOCUSED\" syntax)\n        Str128 part_name;\n        if (window == nullptr && RefID != 0 && strncmp(ref.Path, \"//\", 2) != 0)\n        {\n            window = GetWindowByRef(\"\");\n            window_idstack_back = window ? window->ID : 0;\n        }\n        else\n        {\n            // Find next part of the path + create a zero-terminated copy for convenience\n            const char* part_start = current;\n            const char* part_end = ImFindNextDecoratedPartInPath(current);\n            if (part_end == nullptr)\n            {\n                current = part_end = part_start + strlen(part_start);\n            }\n            else if (part_end > part_start)\n            {\n                current = part_end;\n                part_end--;\n                IM_ASSERT(part_end[0] == '/');\n            }\n            part_name.setf(\"%.*s\", (int)(part_end - part_start), part_start);\n\n            // Find root window or child window\n            if (window == nullptr)\n            {\n                // Root: defer first element to GetID(), this will handle SetRef(), \"//\" and \"//$FOCUSED\" syntax.\n                ImGuiID window_id = GetID(part_name.c_str());\n                window = GetWindowByRef(window_id);\n                window_idstack_back = window ? window->ID : 0;\n            }\n            else\n            {\n                ImGuiID child_window_id = 0;\n                ImGuiWindow* child_window = nullptr;\n                {\n                    // Child: Attempt 1: Try to BeginChild(const char*) variant and mimic its logic.\n                    Str128 child_window_full_name;\n#if (IMGUI_VERSION_NUM >= 18996) && (IMGUI_VERSION_NUM < 18999)\n                    if (window_idstack_back == window->ID)\n                    {\n                        child_window_full_name.setf(\"%s/%s\", window->Name, part_name.c_str());\n                    }\n                    else\n#endif\n                    {\n                        ImGuiID child_item_id = GetID(part_name.c_str(), window_idstack_back);\n                        child_window_full_name.setf(\"%s/%s_%08X\", window->Name, part_name.c_str(), child_item_id);\n                    }\n                    child_window_id = ImHashStr(child_window_full_name.c_str()); // We do NOT use ImHashDecoratedPath()\n                    child_window = GetWindowByRef(child_window_id);\n                }\n                if (child_window == nullptr)\n                {\n                    // Child: Attempt 2: Try for BeginChild(ImGuiID id) variant and mimic its logic.\n                    // FIXME: This only really works when ID passed to BeginChild() was derived from a string.\n                    // We could support $$xxxx syntax to encode ID in parameter?\n                    ImGuiID child_item_id = GetID(part_name.c_str(), window_idstack_back);\n                    Str128f child_window_full_name(\"%s/%08X\", window->Name, child_item_id);\n                    child_window_id = ImHashStr(child_window_full_name.c_str()); // We do NOT use ImHashDecoratedPath()\n                    child_window = GetWindowByRef(child_window_id);\n                }\n                if (child_window == nullptr)\n                {\n                    // Assume that part is an arbitrary PushID(const char*)\n                    window_idstack_back = GetID(part_name.c_str(), window_idstack_back);\n                }\n                else\n                {\n                    window = child_window;\n                    window_idstack_back = window ? window->ID : 0;\n                }\n            }\n        }\n\n        // Process result\n        // FIXME: What if we want to query a not-yet-existing window by ID?\n        if (window == nullptr)\n        {\n            if ((flags & ImGuiTestOpFlags_NoError) == 0)\n                LogError(\"WindowInfo: element \\\"%s\\\" doesn't seem to exist.\", part_name.c_str());\n            return ItemInfoNull();\n        }\n    }\n\n    IM_ASSERT(window != nullptr);\n    IM_ASSERT(window_idstack_back != 0);\n\n    // Stopped on \"window/node/\"\n    if (window_idstack_back != 0 && window_idstack_back != window->ID)\n    {\n        if ((flags & ImGuiTestOpFlags_NoError) == 0)\n            LogError(\"WindowInfo: element doesn't seem to exist or isn't a window.\");\n        return ItemInfoNull();\n    }\n\n    return ItemInfo(window->ID, flags & ImGuiTestOpFlags_NoError);\n}\n\nvoid    ImGuiTestContext::ScrollToTop(ImGuiTestRef ref)\n{\n    if (IsError())\n        return;\n\n    ImGuiWindow* window = GetWindowByRef(ref);\n    IM_CHECK_SILENT(window != nullptr);\n    if (window->Scroll.y == 0.0f)\n        return;\n    ScrollToY(ref, 0.0f);\n    Yield();\n}\n\nvoid    ImGuiTestContext::ScrollToBottom(ImGuiTestRef ref)\n{\n    if (IsError())\n        return;\n\n    ImGuiWindow* window = GetWindowByRef(ref);\n    IM_CHECK_SILENT(window != nullptr);\n    if (window->Scroll.y == window->ScrollMax.y)\n        return;\n    ScrollToY(ref, window->ScrollMax.y);\n    Yield();\n}\n\nbool    ImGuiTestContext::ScrollErrorCheck(ImGuiAxis axis, float expected, float actual, int* remaining_attempts)\n{\n    if (IsError())\n    {\n        (*remaining_attempts)--;\n        return false;\n    }\n\n    float THRESHOLD = 1.0f;\n    if (ImFabs(actual - expected) < THRESHOLD)\n        return true;\n\n    (*remaining_attempts)--;\n    if (*remaining_attempts > 0)\n    {\n        LogInfo(\"Failed to set Scroll%c. Requested %.2f, got %.2f. Will try again.\", 'X' + axis, expected, actual);\n        return true;\n    }\n    else\n    {\n        IM_ERRORF(\"Failed to set Scroll%c. Requested %.2f, got %.2f. Aborting.\", 'X' + axis, expected, actual);\n        return false;\n    }\n}\n\n// FIXME-TESTS: Mostly the same code as ScrollbarEx()\nstatic ImVec2 GetWindowScrollbarMousePositionForScroll(ImGuiWindow* window, ImGuiAxis axis, float scroll_v)\n{\n    ImGuiContext& g = *GImGui;\n    ImRect bb = ImGui::GetWindowScrollbarRect(window, axis);\n\n    // From Scrollbar():\n    //float* scroll_v = &window->Scroll[axis];\n    const float size_avail_v = window->InnerRect.Max[axis] - window->InnerRect.Min[axis];\n    const float size_contents_v = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f;\n\n    // From ScrollbarEx() onward:\n\n    // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)\n    const float scrollbar_size_v = bb.Max[axis] - bb.Min[axis];\n\n    // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)\n    // But we maintain a minimum size in pixel to allow for the user to still aim inside.\n    const float win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), 1.0f);\n    const float grab_h_pixels = ImClamp(scrollbar_size_v * (size_avail_v / win_size_v), g.Style.GrabMinSize, scrollbar_size_v);\n\n    const float scroll_max = ImMax(1.0f, size_contents_v - size_avail_v);\n    const float scroll_ratio = ImSaturate(scroll_v / scroll_max);\n    const float grab_v = scroll_ratio * (scrollbar_size_v - grab_h_pixels); // Grab position\n\n    ImVec2 position;\n    position[axis] = bb.Min[axis] + grab_v + grab_h_pixels * 0.5f;\n    position[axis ^ 1] = bb.GetCenter()[axis ^ 1];\n\n    return position;\n}\n\n#if IMGUI_VERSION_NUM < 18993\n#define ImTrunc ImFloor\n#endif\n\nstatic bool ScrollToWithScrollbar(ImGuiTestContext* ctx, ImGuiWindow* window, ImGuiAxis axis, float scroll_target)\n{\n    ImGuiContext& g = *ctx->UiContext;\n    ctx->Yield();\n    ctx->WindowFocus(window->ID);\n    if (window->ScrollbarSizes[axis ^ 1] <= 0.0f) // Verify if still exists after yield\n        return false;\n\n    const ImRect scrollbar_rect = ImGui::GetWindowScrollbarRect(window, axis);\n    const float scrollbar_size_v = scrollbar_rect.Max[axis] - scrollbar_rect.Min[axis];\n    const float window_resize_grip_size = ImTrunc(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));\n\n    // In case of a very small window, directly use SetScrollX/Y function to prevent resizing it\n    // FIXME-TESTS: GetWindowScrollbarMousePositionForScroll() doesn't return the exact value when scrollbar grip is too small\n    if (scrollbar_size_v < window_resize_grip_size)\n        return false;\n\n    ctx->MouseSetViewport(window);\n\n    const float scroll_src = window->Scroll[axis];\n    ImVec2 scrollbar_src_pos = GetWindowScrollbarMousePositionForScroll(window, axis, scroll_src);\n    scrollbar_src_pos[axis] = ImMin(scrollbar_src_pos[axis], scrollbar_rect.Min[axis] + scrollbar_size_v - window_resize_grip_size);\n    ctx->MouseMoveToPos(scrollbar_src_pos);\n    ctx->MouseDown(0);\n    ctx->SleepStandard();\n\n    ImVec2 scrollbar_dst_pos = GetWindowScrollbarMousePositionForScroll(window, axis, scroll_target);\n    ctx->MouseMoveToPos(scrollbar_dst_pos);\n    ctx->MouseUp(0);\n    ctx->SleepStandard();\n\n    // Verify that things worked\n    const float scroll_result = window->Scroll[axis];\n    if (ImFabs(scroll_result - scroll_target) < 1.0f)\n        return true;\n\n    // FIXME-TESTS: Investigate\n    ctx->LogWarning(\"Failed to set Scroll%c. Requested %.2f, got %.2f.\", 'X' + axis, scroll_target, scroll_result);\n    return true;\n}\n\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoFocusWindow\nvoid    ImGuiTestContext::ScrollTo(ImGuiTestRef ref, ImGuiAxis axis, float scroll_target, ImGuiTestOpFlags flags)\n{\n    ImGuiContext& g = *UiContext;\n    if (IsError())\n        return;\n\n    ImGuiWindow* window = GetWindowByRef(ref);\n    IM_CHECK_SILENT(window != nullptr);\n\n    // Early out\n    const float scroll_target_clamp = ImClamp(scroll_target, 0.0f, window->ScrollMax[axis]);\n    if (ImFabs(window->Scroll[axis] - scroll_target_clamp) < 1.0f)\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    const char axis_c = (char)('X' + axis);\n    LogDebug(\"ScrollTo %c %.1f/%.1f\", axis_c, scroll_target, window->ScrollMax[axis]);\n\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        SleepStandard();\n\n    // Try to use Scrollbar if available\n    const ImGuiTestItemInfo scrollbar_item = ItemInfo(ImGui::GetWindowScrollbarID(window, axis), ImGuiTestOpFlags_NoError);\n    if (scrollbar_item.ID != 0 && /*EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast && */ !(flags & ImGuiTestOpFlags_NoFocusWindow))\n    {\n        if (ScrollToWithScrollbar(this, window, axis, scroll_target_clamp))\n        {\n            // Verify that things worked\n            const float scroll_result = window->Scroll[axis];\n            if (ImFabs(scroll_result - scroll_target_clamp) < 1.0f)\n                return;\n\n            // FIXME-TESTS: Investigate\n            LogWarning(\"Failed to set Scroll%c. Requested %.2f, got %.2f.\", 'X' + axis, scroll_target_clamp, scroll_result);\n        }\n    }\n\n    // Fallback: manual slow scroll\n    // FIXME-TESTS: Consider using mouse wheel, since it can work without taking focus\n    int remaining_failures = 3;\n    while (!Abort)\n    {\n        if (ImFabs(window->Scroll[axis] - scroll_target_clamp) < 1.0f)\n            break;\n\n        const float scroll_speed = (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Fast) ? FLT_MAX : ImFloor(EngineIO->ScrollSpeed * g.IO.DeltaTime + 0.99f);\n        const float scroll_next = ImLinearSweep(window->Scroll[axis], scroll_target, scroll_speed);\n        if (axis == ImGuiAxis_X)\n            ImGui::SetScrollX(window, scroll_next);\n        else\n            ImGui::SetScrollY(window, scroll_next);\n\n        // Error handling to avoid getting stuck in this function.\n        Yield();\n        if (!ScrollErrorCheck(axis, scroll_next, window->Scroll[axis], &remaining_failures))\n            break;\n    }\n\n    // Need another frame for the result->Rect to stabilize\n    Yield();\n}\n\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoFocusWindow\nvoid    ImGuiTestContext::ScrollToPos(ImGuiTestRef window_ref, float pos_v, ImGuiAxis axis, ImGuiTestOpFlags flags)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"ScrollToPos %c %.2f\", 'X' + axis, pos_v);\n\n    // Ensure window size and ScrollMax are up-to-date\n    Yield();\n\n    ImGuiWindow* window = GetWindowByRef(window_ref);\n    IM_CHECK_SILENT(window != NULL);\n    float item_curr = pos_v;\n    float item_target = ImFloor(window->InnerClipRect.GetCenter()[axis]);\n    float scroll_delta = item_target - item_curr;\n    float scroll_target = ImClamp(window->Scroll[axis] - scroll_delta, 0.0f, window->ScrollMax[axis]);\n\n    ScrollTo(window->ID, axis, scroll_target, (flags & ImGuiTestOpFlags_NoFocusWindow));\n}\n\nvoid    ImGuiTestContext::ScrollToPosX(ImGuiTestRef window_ref, float pos_x)\n{\n    ScrollToPos(window_ref, pos_x, ImGuiAxis_X);\n}\n\nvoid    ImGuiTestContext::ScrollToPosY(ImGuiTestRef window_ref, float pos_y)\n{\n    ScrollToPos(window_ref, pos_y, ImGuiAxis_Y);\n}\n\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoFocusWindow\nvoid    ImGuiTestContext::ScrollToItem(ImGuiTestRef ref, ImGuiAxis axis, ImGuiTestOpFlags flags)\n{\n    if (IsError())\n        return;\n\n    // If the item is not currently visible, scroll to get it in the center of our window\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestItemInfo item = ItemInfo(ref);\n    ImGuiTestRefDesc desc(ref, item);\n    LogDebug(\"ScrollToItem %c %s\", 'X' + axis, desc.c_str());\n\n    if (item.ID == 0)\n        return;\n\n    // Ensure window size and ScrollMax are up-to-date\n    Yield();\n\n    // TabBar are a special case because they have no scrollbar and rely on ScrollButton \"<\" and \">\"\n    // FIXME-TESTS: Consider moving to its own function.\n    ImGuiContext& g = *UiContext;\n    if (axis == ImGuiAxis_X)\n        if (ImGuiTabBar* tab_bar = g.TabBars.GetByKey(item.ParentID))\n            if (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll)\n            {\n                ScrollToTabItem(tab_bar, item.ID);\n                return;\n            }\n\n    ImGuiWindow* window = item.Window;\n    float item_curr = ImFloor(item.RectFull.GetCenter()[axis]);\n    float item_target = ImFloor(window->InnerClipRect.GetCenter()[axis]);\n    float scroll_delta = item_target - item_curr;\n    float scroll_target = ImClamp(window->Scroll[axis] - scroll_delta, 0.0f, window->ScrollMax[axis]);\n\n    ScrollTo(window->ID, axis, scroll_target, (flags & ImGuiTestOpFlags_NoFocusWindow));\n}\n\nvoid    ImGuiTestContext::ScrollToItemX(ImGuiTestRef ref)\n{\n    ScrollToItem(ref, ImGuiAxis_X);\n}\n\nvoid    ImGuiTestContext::ScrollToItemY(ImGuiTestRef ref)\n{\n    ScrollToItem(ref, ImGuiAxis_Y);\n}\n\nvoid    ImGuiTestContext::ScrollToTabItem(ImGuiTabBar* tab_bar, ImGuiID tab_id)\n{\n    if (IsError())\n        return;\n\n    // Cancel if \"##v\", because it's outside the tab_bar rect, and will be considered as \"not visible\" even if it is!\n    //if (GetID(\"##v\") == item->ID)\n    //    return;\n\n    IM_CHECK_SILENT(tab_bar != nullptr);\n    const ImGuiTabItem* selected_tab_item = ImGui::TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId);\n    const ImGuiTabItem* target_tab_item = ImGui::TabBarFindTabByID(tab_bar, tab_id);\n    if (target_tab_item == nullptr)\n        return;\n\n    int selected_tab_index = tab_bar->Tabs.index_from_ptr(selected_tab_item);\n    int target_tab_index = tab_bar->Tabs.index_from_ptr(target_tab_item);\n\n    ImGuiTestRef backup_ref = GetRef();\n    SetRef(tab_bar->ID);\n\n    if (selected_tab_index > target_tab_index)\n    {\n        MouseMove(\"##<\");\n        for (int i = 0; i < selected_tab_index - target_tab_index; ++i)\n            MouseClick(0);\n    }\n    else\n    {\n        MouseMove(\"##>\");\n        for (int i = 0; i < target_tab_index - selected_tab_index; ++i)\n            MouseClick(0);\n    }\n\n    // Skip the scroll animation\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Fast)\n    {\n        tab_bar->ScrollingAnim = tab_bar->ScrollingTarget;\n        Yield();\n    }\n\n    SetRef(backup_ref);\n}\n\n// Verify that ScrollMax is stable regardless of scrolling position\n// - This can break when the layout of clipped items doesn't match layout of unclipped items\n// - This can break with non-rounded calls to ItemSize(), namely when the starting position is negative (above visible area)\n//   We should ideally be more tolerant of non-rounded sizes passed by the users.\n// - One of the net visible effect of an unstable ScrollMax is that the End key would put you at a spot that's not exactly the lowest spot,\n//   and so a second press to End would you move again by a few pixels.\n// FIXME-TESTS: Make this an iterative, smooth scroll.\nvoid    ImGuiTestContext::ScrollVerifyScrollMax(ImGuiTestRef ref)\n{\n    ImGuiWindow* window = GetWindowByRef(ref);\n    ImGui::SetScrollY(window, 0.0f);\n    Yield();\n    float scroll_max_0 = window->ScrollMax.y;\n    ImGui::SetScrollY(window, window->ScrollMax.y);\n    Yield();\n    float scroll_max_1 = window->ScrollMax.y;\n    IM_CHECK_EQ(scroll_max_0, scroll_max_1);\n}\n\nvoid    ImGuiTestContext::NavMoveTo(ImGuiTestRef ref)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiContext& g = *UiContext;\n    ImGuiTestItemInfo item = ItemInfo(ref);\n    ImGuiTestRefDesc desc(ref, item);\n    LogDebug(\"NavMove to %s\", desc.c_str());\n\n    if (item.ID == 0)\n        return;\n\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        SleepStandard();\n\n    // Focus window before scrolling/moving so things are nicely visible\n    WindowFocus(item.Window->ID);\n\n    // Teleport\n    // FIXME-NAV: We should have a nav request feature that does this,\n    // except it'll have to queue the request to find rect, then set scrolling, which would incur a 2 frame delay :/\n    // FIXME-TESTS-NOT_SAME_AS_END_USER\n    IM_ASSERT(g.NavMoveSubmitted == false);\n    ImRect rect_rel = item.RectFull;\n    rect_rel.Translate(ImVec2(-item.Window->Pos.x, -item.Window->Pos.y));\n    ImGui::SetNavID(item.ID, (ImGuiNavLayer)item.NavLayer, 0, rect_rel);\n#if IMGUI_VERSION_NUM >= 19136\n    ImGui::SetNavCursorVisible(true);\n    g.NavHighlightItemUnderNav = true;\n#else\n    g.NavDisableHighlight = false;\n    g.NavDisableMouseHover = true;\n#endif\n    g.NavMousePosDirty = true;\n    ImGui::ScrollToBringRectIntoView(item.Window, item.RectFull);\n    while (g.NavMoveSubmitted)\n        Yield();\n    Yield();\n\n    if (!Abort)\n        if (g.NavId != item.ID)\n            IM_ERRORF_NOHDR(\"Unable to set NavId to %s\", desc.c_str());\n}\n\nvoid    ImGuiTestContext::NavActivate()\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"NavActivate\");\n    Yield(); // ?\n    KeyPress(ImGuiKey_Space);\n}\n\nvoid    ImGuiTestContext::NavInput()\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"NavInput\");\n    KeyPress(ImGuiKey_Enter);\n}\n\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_MoveToEdgeL\n// - ImGuiTestOpFlags_MoveToEdgeR\n// - ImGuiTestOpFlags_MoveToEdgeU\n// - ImGuiTestOpFlags_MoveToEdgeD\nstatic ImVec2 GetMouseAimingPos(const ImGuiTestItemInfo& item, ImGuiTestOpFlags flags)\n{\n    ImRect r = item.RectClipped;\n    ImVec2 pos;\n    if (flags & ImGuiTestOpFlags_MoveToEdgeL)\n        pos.x = (r.Min.x + 1.0f);\n    else if (flags & ImGuiTestOpFlags_MoveToEdgeR)\n        pos.x = (r.Max.x - 1.0f);\n    else\n        pos.x = (r.Min.x + r.Max.x) * 0.5f;\n    if (flags & ImGuiTestOpFlags_MoveToEdgeU)\n        pos.y = (r.Min.y + 1.0f);\n    else if (flags & ImGuiTestOpFlags_MoveToEdgeD)\n        pos.y = (r.Max.y - 1.0f);\n    else\n        pos.y = (r.Min.y + r.Max.y) * 0.5f;\n    return pos;\n}\n\nvoid ImGuiTestContext::_MakeAimingSpaceOverPos(ImGuiViewport* viewport, ImGuiWindow* over_window, const ImVec2& over_pos)\n{\n    ImGuiContext& g = *UiContext;\n\n    // if window == nullptr : make space to reach void\n    // if window != nullptr : make space to reach window\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"_MakeAimingSpaceOverPos(over_window = '%s', over_pos = %.2f,%.2f)\", over_window ? over_window->Name : \"N/A\", over_pos.x, over_pos.y);\n\n    const int over_window_n = (over_window != nullptr) ? ImGui::FindWindowDisplayIndex(over_window) : -1;\n#if IMGUI_VERSION_NUM < 19183\n    const ImVec2 hover_padding = g.WindowsHoverPadding;\n#else\n    const ImVec2 hover_padding = ImVec2(g.WindowsBorderHoverPadding, g.WindowsBorderHoverPadding);\n#endif\n\n    const ImVec2 window_min_pos = over_pos + hover_padding + ImVec2(1.0f, 1.0f);\n    for (int window_n = g.Windows.Size - 1; window_n > over_window_n; window_n--)\n    {\n        ImGuiWindow* window = g.Windows[window_n];\n        if (window->WasActive == false)\n            continue;\n#ifdef IMGUI_HAS_DOCK\n        if (window->Viewport != viewport)\n            continue;\n        if (window->RootWindowDockTree != window)\n            continue;\n#else\n        IM_UNUSED(viewport);\n        if (window->RootWindow != window)\n            continue;\n        if (window->Flags & ImGuiWindowFlags_NoMove)\n            continue;\n#endif\n        if (window->Rect().Contains(window_min_pos))\n        {\n            WindowMove(window->ID, window_min_pos);\n\n            // Verify that we have managed to move the window..\n            if (ImLengthSqr(window->Pos - window_min_pos) >= 1.0f)\n            {\n                LogWarning(\"Failed to move window '%s'! While trying to make space to click at (%.2f,%.2f) over window '%s'.\",\n                    window->Name, over_pos.x, over_pos.y, over_window ? over_window->Name : \"N/A\");\n                //IM_CHECK_EQ(window->Pos, window_min_pos); // Failed to move window to make space\n            }\n        }\n    }\n}\n\nstatic void FocusOrMakeClickableAtPos(ImGuiTestContext* ctx, ImGuiWindow* window, const ImVec2& pos)\n{\n    IM_ASSERT(window != nullptr);\n\n    // Avoid unnecessary focus\n    // While this is generally desirable and much more consistent with user behavior,\n    // it make test-engine behavior a little less deterministic.\n    // incorrectly written tests could possibly succeed or fail based on position of other windows.\n    bool is_covered = ctx->FindHoveredWindowAtPos(pos) != window;\n#if IMGUI_VERSION_NUM >= 18944\n    bool is_inhibited = ImGui::IsWindowContentHoverable(window) == false;\n#else\n    bool is_inhibited = false;\n#endif\n    // FIXME-TESTS-NOT_SAME_AS_END_USER: This has too many side effect, could we do without?\n    // - e.g. This can close a modal.\n    if (is_covered || is_inhibited)\n    {\n        // Testing ImGuiWindowFlags_NoBringToFrontOnFocus is similar to what FocusWindow() does\n        ImGuiWindow* focus_front_window = window ? window->RootWindow : nullptr;\n#ifdef IMGUI_HAS_DOCK\n        ImGuiWindow* display_front_window = window ? window->RootWindowDockTree : nullptr;\n#else\n        ImGuiWindow* display_front_window = window ? window->RootWindow : nullptr;\n#endif\n        if ((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus)\n        {\n            // FIXME-TESTS: Aim to make it the common/default path, and WindowBringToFront() the exceptional path!\n            ctx->_MakeAimingSpaceOverPos(window->Viewport, window, pos);\n        }\n        else\n        {\n            ctx->WindowBringToFront(window->ID);\n        }\n    }\n}\n\n// Conceptucally this could be called ItemHover()\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoFocusWindow\n// - ImGuiTestOpFlags_NoCheckHoveredId\n// - ImGuiTestOpFlags_IsSecondAttempt [used when recursively calling ourself)\n// - ImGuiTestOpFlags_MoveToEdgeXXX flags\n// FIXME-TESTS: This is too eagerly trying to scroll everything even if already visible.\n// FIXME: Maybe ImGuiTestOpFlags_NoCheckHoveredId could be automatic if we detect that another item is active as intended?\nvoid    ImGuiTestContext::MouseMove(ImGuiTestRef ref, ImGuiTestOpFlags flags)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiContext& g = *UiContext;\n\n    ImGuiTestItemInfo item;\n    if (flags & ImGuiTestOpFlags_NoAutoOpenFullPath)\n        item = ItemInfo(ref);\n    else\n        item = ItemInfoOpenFullPath(ref);\n\n    ImGuiTestRefDesc desc(ref, item);\n    LogDebug(\"MouseMove to %s\", desc.c_str());\n    if (item.ID == 0)\n        return;\n\n    ImGuiWindow* window = item.Window;\n    if (!window->WasActive)\n    {\n        LogError(\"Window '%s' is not active!\", window->Name);\n        return;\n    }\n\n    // FIXME-TESTS: If window was not brought to front (because of either ImGuiWindowFlags_NoBringToFrontOnFocus or ImGuiTestOpFlags_NoFocusWindow)\n    // then we need to make space by moving other windows away.\n    // An easy to reproduce this bug is to run \"docking_dockspace_tab_amend\" with Test Engine UI over top-left corner, covering the Tools menu.\n\n    // Check visibility and scroll if necessary\n    {\n#if IMGUI_VERSION_NUM < 19183\n        const ImVec2 hover_padding = g.WindowsHoverPadding;\n#else\n        const ImVec2 hover_padding = ImVec2(g.WindowsBorderHoverPadding, g.WindowsBorderHoverPadding);\n#endif\n        if (item.NavLayer == ImGuiNavLayer_Main)\n        {\n            float min_visible_size = 10.0f;\n            float min_window_size_x = window->DecoInnerSizeX1 + window->DecoOuterSizeX1 + window->DecoOuterSizeX2 + min_visible_size + hover_padding.x * 2.0f;\n            float min_window_size_y = window->DecoInnerSizeY1 + window->DecoOuterSizeY1 + window->DecoOuterSizeY2 + min_visible_size + hover_padding.y * 2.0f;\n            if ((window->Size.x < min_window_size_x || window->Size.y < min_window_size_y) && (window->Flags & ImGuiWindowFlags_NoResize) == 0 && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0)\n            {\n                LogDebug(\"MouseMove: Will attempt to resize window to make item in main scrolling layer visible.\");\n                if (window->Size.x < min_window_size_x)\n                    WindowResize(window->ID, ImVec2(min_window_size_x, window->Size.y));\n                if (window->Size.y < min_window_size_y)\n                    WindowResize(window->ID, ImVec2(window->Size.x, min_window_size_y));\n                item = ItemInfo(item.ID);\n            }\n        }\n\n        ImRect window_r = window->InnerClipRect;\n        window_r.Expand(ImVec2(-hover_padding.x, -hover_padding.y));\n\n        ImRect item_r_clipped;\n        item_r_clipped.Min.x = ImClamp(item.RectFull.Min.x, window_r.Min.x, window_r.Max.x);\n        item_r_clipped.Min.y = ImClamp(item.RectFull.Min.y, window_r.Min.y, window_r.Max.y);\n        item_r_clipped.Max.x = ImClamp(item.RectFull.Max.x, window_r.Min.x, window_r.Max.x);\n        item_r_clipped.Max.y = ImClamp(item.RectFull.Max.y, window_r.Min.y, window_r.Max.y);\n\n        // In theory all we need is one visible point, but it is generally nicer if we scroll toward visibility.\n        // Bias toward reducing amount of horizontal scroll.\n        float visibility_ratio_x = (item_r_clipped.GetWidth() + 1.0f) / (item.RectFull.GetWidth() + 1.0f);\n        float visibility_ratio_y = (item_r_clipped.GetHeight() + 1.0f) / (item.RectFull.GetHeight() + 1.0f);\n\n        if (item.NavLayer == ImGuiNavLayer_Main)\n        {\n            if (visibility_ratio_x < 0.70f)\n                ScrollToItem(ref, ImGuiAxis_X, ImGuiTestOpFlags_NoFocusWindow);\n            if (visibility_ratio_y < 0.90f)\n                ScrollToItem(ref, ImGuiAxis_Y, ImGuiTestOpFlags_NoFocusWindow);\n        }\n        // FIXME: Scroll parent window\n    }\n\n    // Menu layer is not scrollable: attempt to resize window.\n    if (item.NavLayer == ImGuiNavLayer_Menu)\n    {\n        // FIXME-TESTS: We designed RectClipped as being within RectFull which is not what we want here. Approximate using window's Max.x\n        ImRect window_r = window->Rect();\n        if (item.RectFull.Min.x > window_r.Max.x)\n        {\n            float extra_width_desired = item.RectFull.Max.x - window_r.Max.x; // item->RectClipped.Max.x;\n            if (extra_width_desired > 0.0f && (flags & ImGuiTestOpFlags_IsSecondAttempt) == 0)\n            {\n                LogDebug(\"MouseMove: Will attempt to resize window to make item in menu layer visible.\");\n                WindowResize(window->ID, window->Size + ImVec2(extra_width_desired, 0.0f));\n            }\n        }\n    }\n\n    // Update item\n    item = ItemInfo(item.ID);\n\n    // FIXME-TESTS-NOT_SAME_AS_END_USER\n    ImVec2 pos = item.RectFull.GetCenter();\n    if (WindowTeleportToMakePosVisible(window->ID, pos))\n        item = ItemInfo(item.ID);\n\n    // Handle the off-chance that e.g. item/window stops being submitted while scrolling (easy to repro by pressing Esc during a long scroll)\n    if (item.ID == 0)\n    {\n        LogError(\"MouseMove: item doesn't exist anymore (after scrolling)\");\n        return;\n    }\n\n    // Keep a copy of item info\n    const ImGuiTestItemInfo item_initial_state = item;\n\n    // Target point\n    pos = GetMouseAimingPos(item, flags);\n\n    // Focus window\n    if (!(flags & ImGuiTestOpFlags_NoFocusWindow) && item.Window != nullptr)\n        FocusOrMakeClickableAtPos(this, item.Window, pos);\n\n    // Another is window active test (in the case focus change has a side effect but also as we have yield an extra frame)\n    if (!item.Window->WasActive)\n    {\n        LogError(\"MouseMove: Window '%s' is not active (after aiming)\", item.Window->Name);\n        return;\n    }\n\n    MouseSetViewport(item.Window);\n    MouseMoveToPos(pos);\n\n    // Focus again in case something made us lost focus (which could happen on a simple hover)\n    if (!(flags & ImGuiTestOpFlags_NoFocusWindow))\n        FocusOrMakeClickableAtPos(this, item.Window, pos);\n\n    // Check hovering target: may be an item (common) or a window (rare)\n    if (!Abort && !(flags & ImGuiTestOpFlags_NoCheckHoveredId))\n    {\n        ImGuiID hovered_id;\n        bool is_hovered_item;\n\n        // Give a few extra frames to validate hovering.\n        // In the vast majority of case this will be set on the first attempt,\n        // but e.g. blocking popups may need to close based on external logic.\n        for (int remaining_attempts = 3; remaining_attempts > 0; remaining_attempts--)\n        {\n            hovered_id = g.HoveredIdPreviousFrame;\n            is_hovered_item = (hovered_id == item.ID);\n            if (is_hovered_item)\n                break;\n            Yield();\n        }\n\n        bool is_hovered_window = is_hovered_item ? true : false;\n        if (!is_hovered_item)\n            for (ImGuiWindow* hovered_window = g.HoveredWindow; hovered_window != nullptr && !is_hovered_window; hovered_window = hovered_window->ParentWindow)\n                if (hovered_window->ID == item.ID && hovered_window == item.Window)\n                    is_hovered_window = true;\n\n        if (!is_hovered_item && !is_hovered_window)\n        {\n            // Check if we are accidentally hovering resize grip (which uses ImGuiButtonFlags_FlattenChildren)\n            if (!(window->Flags & ImGuiWindowFlags_NoResize) && !(flags & ImGuiTestOpFlags_IsSecondAttempt))\n            {\n                bool is_hovering_resize_corner = false;\n                for (int n = 0; n < 2; n++)\n                    is_hovering_resize_corner |= (hovered_id == ImGui::GetWindowResizeCornerID(window, n));\n                if (is_hovering_resize_corner)\n                {\n                    LogDebug(\"MouseMove: Child obstructed by parent's ResizeGrip, trying to resize window and trying again..\");\n#if IMGUI_VERSION_NUM < 19172\n                    float extra_size = window->CalcFontSize() * 3.0f;\n#else\n                    float extra_size = window->FontRefSize * 3.0f;\n#endif\n                    WindowResize(window->ID, window->Size + ImVec2(extra_size, extra_size));\n                    MouseMove(ref, flags | ImGuiTestOpFlags_IsSecondAttempt);\n                    return;\n                }\n            }\n\n            // Update item\n            item = ItemInfo(item.ID);\n\n            // Log message\n            // FIXME: Consider a second attempt?\n            ImVec2 pos_old = item_initial_state.RectFull.Min;\n            ImVec2 pos_new = item.RectFull.Min;\n            ImVec2 size_old = item_initial_state.RectFull.GetSize();\n            ImVec2 size_new = item.RectFull.GetSize();\n            Str256f error_message(\n                \"MouseMove: Unable to Hover %s:\\n\"\n                \"- Expected item 0x%08X in window '%s', targeted position: (%.1f,%.1f)'\\n\"\n                \"- Hovered id was 0x%08X in '%s'.\\n\"\n                \"- Before mouse move: Item Pos (%6.1f,%6.1f) Size (%6.1f,%6.1f)\\n\"\n                \"- After  mouse move: Item Pos (%6.1f,%6.1f) Size (%6.1f,%6.1f)\",\n                desc.c_str(),\n                item.ID, item.Window ? item.Window->Name : \"<nullptr>\", pos.x, pos.y,\n                hovered_id, g.HoveredWindow ? g.HoveredWindow->Name : \"\",\n                pos_old.x, pos_old.y, size_old.x, size_old.y,\n                pos_new.x, pos_new.y, size_new.x, size_new.y);\n            IM_ERRORF_NOHDR(\"%s\", error_message.c_str());\n        }\n    }\n}\n\nvoid    ImGuiTestContext::MouseSetViewport(ImGuiWindow* window)\n{\n    IM_CHECK_SILENT(window != nullptr);\n#ifdef IMGUI_HAS_VIEWPORT\n    ImGuiViewportP* viewport = window ? window->Viewport : nullptr;\n    ImGuiID viewport_id = viewport ? viewport->ID : 0;\n    if (window->Viewport == nullptr)\n        IM_CHECK(window->WasActive == false); // only time this is allowed is an inactive window (where the viewport was destroyed)\n    if (Inputs->MouseHoveredViewport != viewport_id)\n    {\n        IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n        LogDebug(\"MouseSetViewport changing to 0x%08X (window '%s')\", viewport_id, window->Name);\n        Inputs->MouseHoveredViewport = viewport_id;\n        Yield(2);\n    }\n#else\n    IM_UNUSED(window);\n#endif\n}\n\n// May be 0 to specify \"automatic\" (based on platform stack, rarely used)\nvoid    ImGuiTestContext::MouseSetViewportID(ImGuiID viewport_id)\n{\n    if (IsError())\n        return;\n\n    if (Inputs->MouseHoveredViewport != viewport_id)\n    {\n        IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n        LogDebug(\"MouseSetViewportID changing to 0x%08X\", viewport_id);\n        Inputs->MouseHoveredViewport = viewport_id;\n        ImGuiTestEngine_Yield(Engine);\n    }\n}\n\n// Make the point at 'pos' (generally expected to be within window's boundaries) visible in the viewport,\n// so it can be later focused then clicked.\nbool    ImGuiTestContext::WindowTeleportToMakePosVisible(ImGuiTestRef ref, ImVec2 pos)\n{\n    ImGuiContext& g = *UiContext;\n    if (IsError())\n        return false;\n    ImGuiWindow* window = GetWindowByRef(ref);\n    IM_CHECK_SILENT_RETV(window != nullptr, false);\n\n#ifdef IMGUI_HAS_DOCK\n    // This is particularly useful for docked windows, as we have to move root dockspace window instead of docket window\n    // itself. As a side effect this also adds support for child windows.\n    window = window->RootWindowDockTree;\n#endif\n\n    ImRect visible_r;\n    visible_r.Min = GetMainMonitorWorkPos();\n    visible_r.Max = visible_r.Min + GetMainMonitorWorkSize();\n    if (!visible_r.Contains(pos))\n    {\n        // Fallback move window directly to make our item reachable with the mouse.\n        // FIXME-TESTS-NOT_SAME_AS_END_USER\n        float pad = g.FontSize;\n        ImVec2 delta;\n        delta.x = (pos.x < visible_r.Min.x) ? (visible_r.Min.x - pos.x + pad) : (pos.x > visible_r.Max.x) ? (visible_r.Max.x - pos.x - pad) : 0.0f;\n        delta.y = (pos.y < visible_r.Min.y) ? (visible_r.Min.y - pos.y + pad) : (pos.y > visible_r.Max.y) ? (visible_r.Max.y - pos.y - pad) : 0.0f;\n        ImGui::SetWindowPos(window, window->Pos + delta, ImGuiCond_Always);\n        LogDebug(\"WindowTeleportToMakePosVisible '%s' delta (%.1f,%.1f)\", window->Name, delta.x, delta.y);\n        Yield();\n        return true;\n    }\n    return false;\n}\n\n// ignore_list is a nullptr-terminated list of pointers\n// Windows that are below all of ignore_list windows are not hidden.\n// FIXME-TESTS-NOT_SAME_AS_END_USER: Aim to get rid of this.\nvoid ImGuiTestContext::_ForeignWindowsHideOverPos(const ImVec2& pos, ImGuiWindow** ignore_list)\n{\n    ImGuiContext& g = *UiContext;\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"ForeignWindowsHideOverPos (%.0f,%.0f)\", pos.x, pos.y);\n    IM_CHECK_SILENT(ignore_list != nullptr); // It makes little sense to call this function with an empty list.\n    IM_CHECK_SILENT(ignore_list[0] != nullptr);\n    //auto& ctx = this;  IM_SUSPEND_TESTFUNC();\n\n    // Find lowest ignored window index. All windows rendering above this index will be hidden. All windows rendering\n    // below this index do not prevent interactions with these windows already, and they can be ignored.\n    int min_window_index = g.Windows.Size;\n    for (int i = 0; ignore_list[i]; i++)\n        min_window_index = ImMin(min_window_index, ImGui::FindWindowDisplayIndex(ignore_list[i]));\n\n#if IMGUI_VERSION_NUM < 19183\n    const ImVec2 hover_padding = g.WindowsHoverPadding;\n#else\n    const ImVec2 hover_padding = ImVec2(g.WindowsBorderHoverPadding, g.WindowsBorderHoverPadding);\n#endif\n    bool hidden_windows = false;\n    for (int i = 0; i < g.Windows.Size; i++)\n    {\n        ImGuiWindow* other_window = g.Windows[i];\n        if (other_window->RootWindow == other_window && other_window->WasActive)\n        {\n            ImRect r = other_window->Rect();\n            r.Expand(hover_padding);\n            if (r.Contains(pos))\n            {\n                for (int j = 0; ignore_list[j]; j++)\n#ifdef IMGUI_HAS_DOCK\n                    if (ignore_list[j]->RootWindowDockTree == other_window->RootWindowDockTree)\n#else\n                    if (ignore_list[j] == other_window)\n#endif\n                    {\n                        other_window = nullptr;\n                        break;\n                    }\n\n                if (other_window && ImGui::FindWindowDisplayIndex(other_window) < min_window_index)\n                    other_window = nullptr;\n\n                if (other_window)\n                {\n                    ForeignWindowsToHide.push_back(other_window);\n                    hidden_windows = true;\n                }\n            }\n        }\n    }\n    if (hidden_windows)\n        Yield();\n}\n\nvoid    ImGuiTestContext::_ForeignWindowsUnhideAll()\n{\n    ForeignWindowsToHide.clear();\n    Yield();\n}\n\nvoid    ImGuiTestContext::MouseMoveToPos(ImVec2 target)\n{\n    ImGuiContext& g = *UiContext;\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"MouseMoveToPos from (%.0f,%.0f) to (%.0f,%.0f)\", Inputs->MousePosValue.x, Inputs->MousePosValue.y, target.x, target.y);\n\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        SleepStandard();\n\n    // Enforce a mouse move if we are already at destination, to enforce g.NavHighlightItemUnderNav gets cleared.\n#if IMGUI_VERSION_NUM >= 19136\n    if (g.NavHighlightItemUnderNav && ImLengthSqr(Inputs->MousePosValue - target) < 1.0f)\n#else\n    if (g.NavDisableMouseHover && ImLengthSqr(Inputs->MousePosValue - target) < 1.0f)\n#endif\n    {\n        Inputs->MousePosValue = target + ImVec2(1.0f, 0.0f);\n        ImGuiTestEngine_Yield(Engine);\n    }\n\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Fast)\n    {\n        Inputs->MousePosValue = target;\n        ImGuiTestEngine_Yield(Engine);\n        ImGuiTestEngine_Yield(Engine);\n        return;\n    }\n\n    // Simulate slower movements. We use a slightly curved movement to make the movement look less robotic.\n\n    // Calculate some basic parameters\n    const ImVec2 start_pos = Inputs->MousePosValue;\n    const ImVec2 delta = target - start_pos;\n    const float length2 = ImLengthSqr(delta);\n    const float length = (length2 > 0.0001f) ? ImSqrt(length2) : 1.0f;\n    const float inv_length = 1.0f / length;\n\n    // Short distance alter speed and wobble\n    float base_speed = EngineIO->MouseSpeed;\n    float base_wobble = EngineIO->MouseWobble;\n    if (length < base_speed * 1.0f)\n    {\n        // Time = 1.0f -> wobble max, Time = 0.0f -> no wobble\n        base_wobble *= length / base_speed;\n\n        // Slow down for short movements(all movement in the 0.0f..1.0f range are remapped to a 0.5f..1.0f seconds)\n        if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        {\n            float approx_time = length / base_speed;\n            approx_time = 0.5f + ImSaturate(approx_time * 0.5f);\n            base_speed = length / approx_time;\n        }\n    }\n\n    // Calculate a vector perpendicular to the motion delta\n    const ImVec2 perp = ImVec2(delta.y, -delta.x) * inv_length;\n\n    // Calculate how much wobble we want, clamped to max out when the delta is 100 pixels (shorter movements get less wobble)\n    const float position_offset_magnitude = ImClamp(length, 1.0f, 100.0f) * base_wobble;\n\n    // Wobble positions, using a sine wave based on position as a cheap way to get a deterministic offset\n    ImVec2 intermediate_pos_a = start_pos + (delta * 0.3f);\n    ImVec2 intermediate_pos_b = start_pos + (delta * 0.6f);\n    intermediate_pos_a += perp * ImSin(intermediate_pos_a.y * 0.1f) * position_offset_magnitude;\n    intermediate_pos_b += perp * ImCos(intermediate_pos_b.y * 0.1f) * position_offset_magnitude;\n\n    // We manipulate Inputs->MousePosValue without reading back from g.IO.MousePos because the later is rounded.\n    // To handle high framerate it is easier to bypass this rounding.\n    float current_dist = 0.0f; // Our current distance along the line (in pixels)\n    while (true)\n    {\n        float move_speed = base_speed * g.IO.DeltaTime;\n\n        //if (g.IO.KeyShift)\n        //    move_speed *= 0.1f;\n\n        current_dist += move_speed; // Move along the line\n\n        // Calculate a parametric position on the direct line that we will use for the curve\n        float t = current_dist * inv_length;\n        t = ImClamp(t, 0.0f, 1.0f);\n        t = 1.0f - ((ImCos(t * IM_PI) + 1.0f) * 0.5f); // Generate a smooth curve with acceleration/deceleration\n\n        //ImGui::GetOverlayDrawList()->AddCircle(target, 10.0f, IM_COL32(255, 255, 0, 255));\n\n        if (t >= 1.0f)\n        {\n            Inputs->MousePosValue = target;\n            ImGuiTestEngine_Yield(Engine);\n            ImGuiTestEngine_Yield(Engine);\n            return;\n        }\n        else\n        {\n            // Use a bezier curve through the wobble points\n            Inputs->MousePosValue = ImBezierCubicCalc(start_pos, intermediate_pos_a, intermediate_pos_b, target, t);\n            //ImGui::GetOverlayDrawList()->AddBezierCurve(start_pos, intermediate_pos_a, intermediate_pos_b, target, IM_COL32(255,0,0,255), 1.0f);\n            ImGuiTestEngine_Yield(Engine);\n        }\n    }\n}\n\n// This always teleport the mouse regardless of fast/slow mode. Useful e.g. to set initial mouse position for a GIF recording.\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoYield\nvoid\tImGuiTestContext::MouseTeleportToPos(ImVec2 target, ImGuiTestOpFlags flags)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"MouseTeleportToPos from (%.0f,%.0f) to (%.0f,%.0f)\", Inputs->MousePosValue.x, Inputs->MousePosValue.y, target.x, target.y);\n\n    Inputs->MousePosValue = target;\n    if ((flags & ImGuiTestOpFlags_NoYield) == 0)\n    {\n        ImGuiTestEngine_Yield(Engine);\n        ImGuiTestEngine_Yield(Engine);\n    }\n}\n\nvoid    ImGuiTestContext::MouseDown(ImGuiMouseButton button)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"MouseDown %d\", button);\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        SleepStandard();\n\n    UiContext->IO.MouseClickedTime[button] = -FLT_MAX; // Prevent accidental double-click from happening ever\n    Inputs->MouseButtonsValue |= (1 << button);\n    Yield();\n}\n\nvoid    ImGuiTestContext::MouseUp(ImGuiMouseButton button)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"MouseUp %d\", button);\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        SleepShort();\n\n    Inputs->MouseButtonsValue &= ~(1 << button);\n    Yield();\n}\n\n// TODO: click time argument (seconds and/or frames)\nvoid    ImGuiTestContext::MouseClick(ImGuiMouseButton button)\n{\n    if (IsError())\n        return;\n    MouseClickMulti(button, 1);\n}\n\n// TODO: click time argument (seconds and/or frames)\nvoid    ImGuiTestContext::MouseClickMulti(ImGuiMouseButton button, int count)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    if (count > 1)\n        LogDebug(\"MouseClickMulti %d x%d\", button, count);\n    else\n        LogDebug(\"MouseClick %d\", button);\n\n    // Make sure mouse buttons are released\n    IM_ASSERT(count >= 1);\n    IM_ASSERT(Inputs->MouseButtonsValue == 0);\n    Yield();\n\n    // Press\n    UiContext->IO.MouseClickedTime[button] = -FLT_MAX; // Prevent accidental double-click from happening ever\n\n    for (int n = 0; n < count; n++)\n    {\n        Inputs->MouseButtonsValue = (1 << button);\n        if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n            SleepShort();\n        else if (EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast)\n            Yield(2); // Leave enough time for non-alive IDs to expire. (#5325)\n        else\n            Yield();\n        Inputs->MouseButtonsValue = 0;\n\n        if (EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast)\n            Yield(2); // Not strictly necessary but covers more variant.\n        else\n            Yield();\n    }\n\n    // Now NewFrame() has seen the mouse release.\n    // Let the imgui frame finish, now e.g. Button() function will return true. Start a new frame.\n    Yield();\n}\n\n// TODO: click time argument (seconds and/or frames)\nvoid    ImGuiTestContext::MouseDoubleClick(ImGuiMouseButton button)\n{\n    MouseClickMulti(button, 2);\n}\n\nvoid    ImGuiTestContext::MouseLiftDragThreshold(ImGuiMouseButton button)\n{\n    if (IsError())\n        return;\n\n    ImGuiContext& g = *UiContext;\n    g.IO.MouseDragMaxDistanceSqr[button] = (g.IO.MouseDragThreshold * g.IO.MouseDragThreshold) + (g.IO.MouseDragThreshold * g.IO.MouseDragThreshold);\n}\n\nImGuiWindow* ImGuiTestContext::FindHoveredWindowAtPos(const ImVec2& pos)\n{\n#if IMGUI_VERSION_NUM >= 19062\n    ImGuiWindow* hovered_window = nullptr;\n    ImGui::FindHoveredWindowEx(pos, true, &hovered_window, nullptr);\n    return hovered_window;\n#else\n    // Modeled on FindHoveredWindow() in imgui.cpp.\n    // Ideally that core function would be refactored to avoid this copy.\n    // - Need to take account of MovingWindow specificities and early out.\n    // - Need to be able to skip viewport compare.\n    // So for now we use a custom function.\n    ImGuiContext& g = *UiContext;\n    ImVec2 padding_regular = g.Style.TouchExtraPadding;\n    ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular;\n    for (int i = g.Windows.Size - 1; i >= 0; i--)\n    {\n        ImGuiWindow* window = g.Windows[i];\n        if (!window->Active || window->Hidden)\n            continue;\n        if (window->Flags & ImGuiWindowFlags_NoMouseInputs)\n            continue;\n\n        // Using the clipped AABB, a child window will typically be clipped by its parent (not always)\n        ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;\n        ImRect r = window->OuterRectClipped;\n        r.Expand(hit_padding);\n        if (!r.Contains(pos))\n            continue;\n\n        // Support for one rectangular hole in any given window\n        // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)\n        if (window->HitTestHoleSize.x != 0)\n        {\n            ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);\n            ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);\n            if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos))\n                continue;\n        }\n\n        return window;\n    }\n    return nullptr;\n#endif\n}\n\nstatic bool IsPosOnVoid(ImGuiContext& g, const ImVec2& pos)\n{\n#if IMGUI_VERSION_NUM < 19183\n    const ImVec2 hover_padding = g.WindowsHoverPadding;\n#else\n    const ImVec2 hover_padding = ImVec2(g.WindowsBorderHoverPadding, g.WindowsBorderHoverPadding);\n#endif\n    for (ImGuiWindow* window : g.Windows)\n#ifdef IMGUI_HAS_DOCK\n        if (window->RootWindowDockTree == window && window->WasActive)\n#else\n        if (window->RootWindow == window && window->WasActive)\n#endif\n        {\n            ImRect r = window->Rect();\n            r.Expand(hover_padding);\n            if (r.Contains(pos))\n                return false;\n        }\n    return true;\n}\n\n// Sample viewport for an easy location with nothing on it.\n// FIXME-OPT: If ever any problematic:\n// - (1) could iterate g.WindowsFocusOrder[] now that we made the switch of it only containing root windows\n// - (2) increase steps iteratively\n// - (3) remember last answer and tries it first.\n// - (4) shortpath to failure negative if a window covers the whole viewport?\nbool    ImGuiTestContext::FindExistingVoidPosOnViewport(ImGuiViewport* viewport, ImVec2* out)\n{\n    ImGuiContext& g = *UiContext;\n    if (IsError())\n        return false;\n\n    for (int yn = 0; yn < 20; yn++)\n        for (int xn = 0; xn < 20; xn++)\n        {\n            ImVec2 pos = viewport->Pos + viewport->Size * ImVec2(xn / 20.0f, yn / 20.0f);\n            if (!IsPosOnVoid(g, pos))\n                continue;\n            *out = pos;\n            return true;\n        }\n    return false;\n}\n\nImVec2   ImGuiTestContext::GetPosOnVoid(ImGuiViewport* viewport)\n{\n    if (IsError())\n        return ImVec2();\n\n    ImVec2 void_pos;\n    bool found_existing_void_pos = FindExistingVoidPosOnViewport(viewport, &void_pos);\n    if (found_existing_void_pos)\n        return void_pos;\n\n    // Move windows away\n    // FIXME: Should be optional and otherwise error.\n    void_pos = viewport->Pos + ImVec2(1, 1);\n    _MakeAimingSpaceOverPos(viewport, nullptr, void_pos);\n\n    return void_pos;\n}\n\nImVec2  ImGuiTestContext::GetWindowTitlebarPoint(ImGuiTestRef window_ref)\n{\n    // FIXME-TESTS: Need to find a -visible- click point. drag_pos may end up being outside of main viewport.\n    if (IsError())\n        return ImVec2();\n\n    ImGuiWindow* window = GetWindowByRef(window_ref);\n    if (window == nullptr)\n    {\n        IM_ERRORF_NOHDR(\"Unable to locate ref window: '%s'\", window_ref.Path);\n        return ImVec2();\n    }\n\n    ImVec2 drag_pos;\n    for (int n = 0; n < 2; n++)\n    {\n#ifdef IMGUI_HAS_DOCK\n        if (window->DockNode != nullptr && window->DockNode->TabBar != nullptr)\n        {\n            ImGuiTabBar* tab_bar = window->DockNode->TabBar;\n            ImGuiTabItem* tab = ImGui::TabBarFindTabByID(tab_bar, window->TabId);\n            IM_ASSERT(tab != nullptr);\n            drag_pos = tab_bar->BarRect.Min + ImVec2(tab->Offset + tab->Width * 0.5f, tab_bar->BarRect.GetHeight() * 0.5f);\n        }\n        else\n#endif\n        {\n#if IMGUI_VERSION_NUM >= 19071\n            const float h = window->TitleBarHeight;\n#else\n            const float h = window->TitleBarHeight();\n#endif\n            drag_pos = ImFloor(window->Pos + ImVec2(window->Size.x, h) * 0.5f);\n        }\n\n        // If we didn't have to teleport it means we can reach the position already\n        if (!WindowTeleportToMakePosVisible(window->ID, drag_pos))\n            break;\n    }\n    return drag_pos;\n}\n\n// Click position which should have no windows.\n// Default to last mouse viewport if viewport not specified.\nvoid    ImGuiTestContext::MouseMoveToVoid(ImGuiViewport* viewport)\n{\n    ImGuiContext& g = *UiContext;\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"MouseMoveToVoid\");\n\n#ifdef IMGUI_HAS_VIEWPORT\n    if (viewport == nullptr && g.MouseViewport && (g.MouseViewport->Flags & ImGuiViewportFlags_CanHostOtherWindows))\n        viewport = g.MouseViewport;\n#endif\n    if (viewport == nullptr)\n        viewport = ImGui::GetMainViewport();\n\n    ImVec2 pos = GetPosOnVoid(viewport); // This may call WindowMove and alter mouse viewport.\n#ifdef IMGUI_HAS_VIEWPORT\n    MouseSetViewportID(viewport->ID);\n#endif\n    MouseMoveToPos(pos);\n    IM_CHECK(g.HoveredWindow == nullptr);\n}\n\nvoid    ImGuiTestContext::MouseClickOnVoid(int mouse_button, ImGuiViewport* viewport)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"MouseClickOnVoid %d\", mouse_button);\n    MouseMoveToVoid(viewport);\n    MouseClick(mouse_button);\n}\n\nvoid    ImGuiTestContext::MouseDragWithDelta(ImVec2 delta, ImGuiMouseButton button)\n{\n    ImGuiContext& g = *UiContext;\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"MouseDragWithDelta %d (%.1f, %.1f)\", button, delta.x, delta.y);\n\n    MouseDown(button);\n    MouseMoveToPos(g.IO.MousePos + delta);\n    MouseUp(button);\n}\n\n// Important: always call MouseWheelX()/MouseWheelY() with an understand that holding Shift will swap axises.\n// - On Windows/Linux, this swap is done in ImGui::NewFrame()\n// - On OSX, this swap is generally done by the backends\n// - In simulated test engine, always assume Windows/Linux behavior as we will swap in ImGuiTestEngine_ApplyInputToImGuiContext()\nvoid    ImGuiTestContext::MouseWheel(ImVec2 delta)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n\n    LogDebug(\"MouseWheel(%g, %g)\", delta.x, delta.y);\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        SleepStandard();\n\n    float td = 0.0f;\n    const float scroll_speed = 15.0f; // Units per second.\n    while (delta.x != 0.0f || delta.y != 0.0f)\n    {\n        ImVec2 scroll;\n        if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Fast)\n        {\n            scroll = delta;\n        }\n        else\n        {\n            td += UiContext->IO.DeltaTime;\n            scroll = ImFloor(delta * ImVec2(td, td) * scroll_speed);\n        }\n\n        if (scroll.x != 0.0f || scroll.y != 0.0f)\n        {\n            scroll = ImClamp(scroll, ImVec2(ImMin(delta.x, 0.0f), ImMin(delta.y, 0.0f)), ImVec2(ImMax(delta.x, 0.0f), ImMax(delta.y, 0.0f)));\n            Inputs->MouseWheel = scroll;\n            delta -= scroll;\n            td = 0;\n        }\n        Yield();\n    }\n}\n\nvoid    ImGuiTestContext::KeyDown(ImGuiKeyChord key_chord)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n#if IMGUI_VERSION_NUM >= 19012\n    const char* chord_desc = ImGui::GetKeyChordName(key_chord);\n#else\n    char chord_desc[32];\n    ImGui::GetKeyChordName(key_chord, chord_desc, IM_ARRAYSIZE(chord_desc));\n#endif\n    LogDebug(\"KeyDown(%s)\", chord_desc);\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        SleepShort();\n\n    Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, true));\n    Yield();\n    Yield();\n}\n\nvoid    ImGuiTestContext::KeyUp(ImGuiKeyChord key_chord)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n#if IMGUI_VERSION_NUM >= 19012\n    const char* chord_desc = ImGui::GetKeyChordName(key_chord);\n#else\n    char chord_desc[32];\n    ImGui::GetKeyChordName(key_chord, chord_desc, IM_ARRAYSIZE(chord_desc));\n#endif\n    LogDebug(\"KeyUp(%s)\", chord_desc);\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        SleepShort();\n\n    Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, false));\n    Yield();\n    Yield();\n}\n\nvoid    ImGuiTestContext::KeyPress(ImGuiKeyChord key_chord, int count)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n#if IMGUI_VERSION_NUM >= 19012\n    const char* chord_desc = ImGui::GetKeyChordName(key_chord);\n#else\n    char chord_desc[32];\n    ImGui::GetKeyChordName(key_chord, chord_desc, IM_ARRAYSIZE(chord_desc));\n#endif\n    LogDebug(\"KeyPress(%s, %d)\", chord_desc, count);\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        SleepShort();\n\n    while (count > 0)\n    {\n        count--;\n        Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, true));\n        if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n            SleepShort();\n        else\n            Yield();\n        Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, false));\n        Yield();\n\n        // Give a frame for items to react\n        Yield();\n    }\n}\n\nvoid    ImGuiTestContext::KeyHold(ImGuiKeyChord key_chord, float time)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n#if IMGUI_VERSION_NUM >= 19012\n    const char* chord_desc = ImGui::GetKeyChordName(key_chord);\n#else\n    char chord_desc[32];\n    ImGui::GetKeyChordName(key_chord, chord_desc, IM_ARRAYSIZE(chord_desc));\n#endif\n    LogDebug(\"KeyHold(%s, %.2f sec)\", chord_desc, time);\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        SleepStandard();\n\n    Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, true));\n    SleepNoSkip(time, 1 / 100.0f);\n    Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, false));\n    Yield(); // Give a frame for items to react\n}\n\n// No extra yield\nvoid    ImGuiTestContext::KeySetEx(ImGuiKeyChord key_chord, bool is_down, float time)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n#if IMGUI_VERSION_NUM >= 19012\n    const char* chord_desc = ImGui::GetKeyChordName(key_chord);\n#else\n    char chord_desc[32];\n    ImGui::GetKeyChordName(key_chord, chord_desc, IM_ARRAYSIZE(chord_desc));\n#endif\n    LogDebug(\"KeySetEx(%s, is_down=%d, time=%.f)\", chord_desc, is_down, time);\n    Inputs->Queue.push_back(ImGuiTestInput::ForKeyChord(key_chord, is_down));\n    if (time > 0.0f)\n        SleepNoSkip(time, 1.0f / 100.0f);\n}\n\nvoid    ImGuiTestContext::KeyChars(const char* chars)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"KeyChars('%s')\", chars);\n    if (EngineIO->ConfigRunSpeed == ImGuiTestRunSpeed_Cinematic)\n        SleepStandard();\n\n    while (*chars)\n    {\n        unsigned int c = 0;\n        int bytes_count = ImTextCharFromUtf8(&c, chars, nullptr);\n        chars += bytes_count;\n        if (c > 0 && c <= 0xFFFF)\n            Inputs->Queue.push_back(ImGuiTestInput::ForChar((ImWchar)c));\n\n        if (EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast)\n            Sleep(1.0f / EngineIO->TypingSpeed);\n    }\n    Yield();\n}\n\nvoid    ImGuiTestContext::KeyCharsAppend(const char* chars)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"KeyCharsAppend('%s')\", chars);\n    KeyPress(ImGuiKey_End);\n    KeyChars(chars);\n}\n\nvoid    ImGuiTestContext::KeyCharsAppendEnter(const char* chars)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"KeyCharsAppendEnter('%s')\", chars);\n    KeyPress(ImGuiKey_End);\n    KeyChars(chars);\n    KeyPress(ImGuiKey_Enter);\n}\n\nvoid    ImGuiTestContext::KeyCharsReplace(const char* chars)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"KeyCharsReplace('%s')\", chars);\n#if IMGUI_VERSION_NUM < 19063\n    KeyPress(ImGuiKey_A | ImGuiMod_Shortcut);\n#else\n    KeyPress(ImGuiKey_A | ImGuiMod_Ctrl);\n#endif\n    if (chars[0])\n        KeyChars(chars);\n    else\n        KeyPress(ImGuiKey_Delete);\n}\n\nvoid    ImGuiTestContext::KeyCharsReplaceEnter(const char* chars)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"KeyCharsReplaceEnter('%s')\", chars);\n#if IMGUI_VERSION_NUM < 19063\n    KeyPress(ImGuiKey_A | ImGuiMod_Shortcut);\n#else\n    KeyPress(ImGuiKey_A | ImGuiMod_Ctrl);\n#endif\n    if (chars[0])\n        KeyChars(chars);\n    else\n        KeyPress(ImGuiKey_Delete);\n    KeyPress(ImGuiKey_Enter);\n}\n\n// depth = 1 -> immediate child of 'parent' in ID Stack\n // FIXME: Configurable filter for InLayerMask. Perhaps we can expose a GatherItemEx() that takes a ImGuiTestGatherTask struct as input.\nvoid    ImGuiTestContext::GatherItems(ImGuiTestItemList* out_list, ImGuiTestRef parent, int depth)\n{\n    IM_ASSERT(out_list != nullptr);\n    IM_ASSERT(depth > 0 || depth == -1);\n\n    if (IsError())\n        return;\n\n    ImGuiTestGatherTask* task = &Engine->GatherTask;\n    IM_ASSERT(task->InParentID == 0);\n    IM_ASSERT(task->LastItemInfo == nullptr);\n\n    // Register gather tasks\n    if (depth == -1)\n        depth = 99;\n    if (parent.ID == 0)\n        parent.ID = GetID(parent);\n    task->InParentID = parent.ID;\n    task->InMaxDepth = depth;\n    task->InLayerMask = (1 << ImGuiNavLayer_Main);\n    task->OutList = out_list;\n\n    // Keep running while gathering\n    // The corresponding hook is ItemAdd() -> ImGuiTestEngineHook_ItemAdd() -> ImGuiTestEngineHook_ItemAdd_GatherTask()\n    const int begin_gather_size = out_list->GetSize();\n    while (true)\n    {\n        const int begin_gather_size_for_frame = out_list->GetSize();\n        Yield();\n        const int end_gather_size_for_frame = out_list->GetSize();\n        if (begin_gather_size_for_frame == end_gather_size_for_frame)\n            break;\n    }\n    const int end_gather_size = out_list->GetSize();\n\n    // FIXME-TESTS: To support filter we'd need to process the list here,\n    // Because ImGuiTestItemList is a pool (ImVector + map ID->index) we'll need to filter, rewrite, rebuild map\n\n    ImGuiTestItemInfo parent_item = ItemInfo(parent, ImGuiTestOpFlags_NoError);\n    LogDebug(\"GatherItems from %s, %d deep: found %d items.\", ImGuiTestRefDesc(parent, parent_item).c_str(), depth, end_gather_size - begin_gather_size);\n\n    task->Clear();\n}\n\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoAutoOpenFullPath\n// - ImGuiTestOpFlags_NoError\nvoid    ImGuiTestContext::ItemAction(ImGuiTestAction action, ImGuiTestRef ref, ImGuiTestOpFlags flags, void* action_arg)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n\n    // [DEBUG] Breakpoint\n    //if (ref.ID == 0x0d4af068)\n    //    printf(\"\");\n\n    // FIXME-TESTS: Fix that stuff\n    const bool is_wildcard = ref.Path != nullptr && strstr(ref.Path, \"**/\") != 0;\n    if (is_wildcard)\n    {\n        // This is a fragile way to avoid some ambiguities, we're relying on expected action to further filter by status flags.\n        // These flags are not cleared by ItemInfo() because ItemAction() may call ItemInfo() again to get same item and thus it\n        // needs these flags to remain in place.\n        if (action == ImGuiTestAction_Check || action == ImGuiTestAction_Uncheck)\n            Engine->FindByLabelTask.InFilterItemStatusFlags = ImGuiItemStatusFlags_Checkable;\n        else if (action == ImGuiTestAction_Open || action == ImGuiTestAction_Close)\n            Engine->FindByLabelTask.InFilterItemStatusFlags = ImGuiItemStatusFlags_Openable;\n    }\n\n    // Find item\n    ImGuiTestItemInfo item;\n    if (flags & ImGuiTestOpFlags_NoAutoOpenFullPath)\n        item = ItemInfo(ref, (flags & ImGuiTestOpFlags_NoError));\n    else\n        item = ItemInfoOpenFullPath(ref, (flags & ImGuiTestOpFlags_NoError));\n\n    ImGuiTestRefDesc desc(ref, item);\n    LogDebug(\"Item%s %s%s\", GetActionName(action), desc.c_str(), (InputMode == ImGuiInputSource_Mouse) ? \"\" : \" (w/ Nav)\");\n    if (item.ID == 0)\n    {\n        if (flags & ImGuiTestOpFlags_NoError)\n            LogDebug(\"Action skipped: Item doesn't exist + used ImGuiTestOpFlags_NoError.\");\n        return;\n    }\n\n    // Automatically uncollapse by default\n    if (item.Window && !(OpFlags & ImGuiTestOpFlags_NoAutoUncollapse))\n        WindowCollapse(item.Window->ID, false);\n\n    if (action == ImGuiTestAction_Hover)\n    {\n        MouseMove(ref, flags);\n    }\n    if (action == ImGuiTestAction_Click || action == ImGuiTestAction_DoubleClick)\n    {\n        if (InputMode == ImGuiInputSource_Mouse)\n        {\n            const int mouse_button = (int)(intptr_t)action_arg;\n            IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);\n            MouseMove(ref, flags);\n            if (action == ImGuiTestAction_DoubleClick)\n                MouseDoubleClick(mouse_button);\n            else\n                MouseClick(mouse_button);\n        }\n        else\n        {\n            action = ImGuiTestAction_NavActivate;\n        }\n    }\n\n    if (action == ImGuiTestAction_NavActivate)\n    {\n        IM_ASSERT(action_arg == nullptr); // Unused\n        NavMoveTo(ref);\n        NavActivate();\n        if (action == ImGuiTestAction_DoubleClick)\n            IM_ASSERT(0);\n    }\n    else if (action == ImGuiTestAction_Input)\n    {\n        IM_ASSERT(action_arg == nullptr); // Unused\n        if (InputMode == ImGuiInputSource_Mouse)\n        {\n            MouseMove(ref, flags);\n            KeyDown(ImGuiMod_Ctrl);\n            MouseClick(0);\n            KeyUp(ImGuiMod_Ctrl);\n        }\n        else\n        {\n            NavMoveTo(ref);\n            NavInput();\n        }\n    }\n    else if (action == ImGuiTestAction_Open)\n    {\n        IM_ASSERT(action_arg == nullptr); // Unused\n        if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0)\n        {\n            MouseMove(ref, flags);\n\n            // Some item may open just by hovering, give them that chance\n            item = ItemInfo(item.ID);\n            if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0)\n            {\n                MouseClick(0);\n                item = ItemInfo(item.ID);\n                if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0)\n                {\n                    MouseDoubleClick(0); // Attempt a double-click // FIXME-TESTS: let's not start doing those fuzzy things..\n                    item = ItemInfo(item.ID);\n                    if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0)\n                        IM_ERRORF_NOHDR(\"Unable to Open item: '%s' in '%s'\", desc.c_str(), item.Window ? item.Window->Name : \"N/A\");\n                }\n            }\n            //Yield();\n        }\n    }\n    else if (action == ImGuiTestAction_Close)\n    {\n        IM_ASSERT(action_arg == nullptr); // Unused\n        if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) != 0)\n        {\n            ItemClick(ref, 0, flags);\n            item = ItemInfo(item.ID);\n            if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) != 0)\n            {\n                ItemDoubleClick(ref, flags); // Attempt a double-click\n                // FIXME-TESTS: let's not start doing those fuzzy things.. widget should give direction of how to close/open... e.g. do you we close a TabItem?\n                item = ItemInfo(item.ID);\n                if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) != 0)\n                    IM_ERRORF_NOHDR(\"Unable to Close item: %s\", ImGuiTestRefDesc(ref, item).c_str());\n            }\n            Yield();\n        }\n    }\n    else if (action == ImGuiTestAction_Check)\n    {\n        IM_ASSERT(action_arg == nullptr); // Unused\n        if ((item.StatusFlags & ImGuiItemStatusFlags_Checkable) && !(item.StatusFlags & ImGuiItemStatusFlags_Checked))\n        {\n            ItemClick(ref, 0, flags);\n        }\n        ItemVerifyCheckedIfAlive(ref, true); // We can't just IM_ASSERT(ItemIsChecked()) because the item may disappear and never update its StatusFlags any more!\n    }\n    else if (action == ImGuiTestAction_Uncheck)\n    {\n        IM_ASSERT(action_arg == nullptr); // Unused\n        if ((item.StatusFlags & ImGuiItemStatusFlags_Checkable) && (item.StatusFlags & ImGuiItemStatusFlags_Checked))\n        {\n            ItemClick(ref, 0, flags);\n        }\n        ItemVerifyCheckedIfAlive(ref, false); // We can't just IM_ASSERT(ItemIsChecked()) because the item may disappear and never update its StatusFlags any more!\n    }\n\n    //if (is_wildcard)\n        Engine->FindByLabelTask.InFilterItemStatusFlags = ImGuiItemStatusFlags_None;\n}\n\nvoid    ImGuiTestContext::ItemActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent, const ImGuiTestActionFilter* filter)\n{\n    int max_depth = filter ? filter->MaxDepth : -1;\n    if (max_depth == -1)\n        max_depth = 99;\n    int max_passes = filter ? filter->MaxPasses : -1;\n    if (max_passes == -1)\n        max_passes = 99;\n    IM_ASSERT(max_depth > 0 && max_passes > 0);\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"ItemActionAll() %s\", GetActionName(action));\n\n    if (!ref_parent.IsEmpty())\n    {\n        // Open parent's parents\n        ImGuiTestItemInfo parent_info = ItemInfoOpenFullPath(ref_parent);\n        if (parent_info.ID != 0)\n        {\n            // Open parent\n            if (action == ImGuiTestAction_Open)\n                if ((parent_info.StatusFlags & ImGuiItemStatusFlags_Openable) && (parent_info.ItemFlags & ImGuiItemFlags_Disabled) == 0)\n                    ItemOpen(ref_parent, ImGuiTestOpFlags_NoError);\n        }\n    }\n\n    // Find child items\n    int actioned_total = 0;\n    for (int pass = 0; pass < max_passes; pass++)\n    {\n        ImGuiTestItemList items;\n        GatherItems(&items, ref_parent, max_depth);\n        //LogItemList(&items);\n\n        // Find deep most items\n        int highest_depth = -1;\n        if (action == ImGuiTestAction_Close)\n            for (auto& item : items)\n                if ((item.StatusFlags & ImGuiItemStatusFlags_Openable) && (item.StatusFlags & ImGuiItemStatusFlags_Opened)) // Not checking Disabled state here\n                    highest_depth = ImMax(highest_depth, item.Depth);\n\n        const int actioned_total_at_beginning_of_pass = actioned_total;\n\n        // Process top-to-bottom in most cases\n        int scan_start = 0;\n        int scan_end = items.GetSize();\n        int scan_dir = +1;\n        if (action == ImGuiTestAction_Close)\n        {\n            // Close bottom-to-top because\n            // 1) it is more likely to handle same-depth parent/child relationship better (e.g. CollapsingHeader)\n            // 2) it gives a nicer sense of symmetry with the corresponding open operation.\n            scan_start = items.GetSize() - 1;\n            scan_end = -1;\n            scan_dir = -1;\n        }\n\n        int processed_count_per_depth[8];\n        memset(processed_count_per_depth, 0, sizeof(processed_count_per_depth));\n\n        for (int n = scan_start; n != scan_end; n += scan_dir)\n        {\n            if (IsError())\n                break;\n\n            const ImGuiTestItemInfo& item = *items[n];\n\n            if (filter && filter->RequireAllStatusFlags != 0)\n                if ((item.StatusFlags & filter->RequireAllStatusFlags) != filter->RequireAllStatusFlags)\n                    continue;\n\n            if (filter && filter->RequireAnyStatusFlags != 0)\n                if ((item.StatusFlags & filter->RequireAnyStatusFlags) != 0)\n                    continue;\n\n            if (filter && filter->MaxItemCountPerDepth != nullptr)\n            {\n                if (item.Depth < IM_ARRAYSIZE(processed_count_per_depth))\n                {\n                    if (processed_count_per_depth[item.Depth] >= filter->MaxItemCountPerDepth[item.Depth])\n                        continue;\n                    processed_count_per_depth[item.Depth]++;\n                }\n            }\n\n            switch (action)\n            {\n            case ImGuiTestAction_Hover:\n            case ImGuiTestAction_Click:\n                ItemAction(action, item.ID);\n                actioned_total++;\n                break;\n            case ImGuiTestAction_Check:\n                if ((item.StatusFlags & ImGuiItemStatusFlags_Checkable) && !(item.StatusFlags & ImGuiItemStatusFlags_Checked))\n                    if ((item.ItemFlags & ImGuiItemFlags_Disabled) == 0)\n                    {\n                        ItemAction(action, item.ID);\n                        actioned_total++;\n                    }\n                break;\n            case ImGuiTestAction_Uncheck:\n                if ((item.StatusFlags & ImGuiItemStatusFlags_Checkable) && (item.StatusFlags & ImGuiItemStatusFlags_Checked))\n                    if ((item.ItemFlags & ImGuiItemFlags_Disabled) == 0)\n                    {\n                        ItemAction(action, item.ID);\n                        actioned_total++;\n                    }\n                break;\n            case ImGuiTestAction_Open:\n                if ((item.StatusFlags & ImGuiItemStatusFlags_Openable) && !(item.StatusFlags & ImGuiItemStatusFlags_Opened))\n                    if ((item.ItemFlags & ImGuiItemFlags_Disabled) == 0)\n                    {\n                        ItemAction(action, item.ID);\n                        actioned_total++;\n                    }\n                break;\n            case ImGuiTestAction_Close:\n                if (item.Depth == highest_depth && (item.StatusFlags & ImGuiItemStatusFlags_Openable) && (item.StatusFlags & ImGuiItemStatusFlags_Opened))\n                    if ((item.ItemFlags & ImGuiItemFlags_Disabled) == 0)\n                    {\n                        ItemClose(item.ID);\n                        actioned_total++;\n                    }\n                break;\n            default:\n                IM_ASSERT(0);\n            }\n        }\n\n        if (IsError())\n            break;\n\n        if (action == ImGuiTestAction_Hover)\n            break;\n        if (actioned_total_at_beginning_of_pass == actioned_total)\n            break;\n    }\n    LogDebug(\"%s %d items in total!\", GetActionVerb(action), actioned_total);\n}\n\nvoid    ImGuiTestContext::ItemOpenAll(ImGuiTestRef ref_parent, int max_depth, int max_passes)\n{\n    ImGuiTestActionFilter filter;\n    filter.MaxDepth = max_depth;\n    filter.MaxPasses = max_passes;\n    ItemActionAll(ImGuiTestAction_Open, ref_parent, &filter);\n}\n\nvoid    ImGuiTestContext::ItemCloseAll(ImGuiTestRef ref_parent, int max_depth, int max_passes)\n{\n    ImGuiTestActionFilter filter;\n    filter.MaxDepth = max_depth;\n    filter.MaxPasses = max_passes;\n    ItemActionAll(ImGuiTestAction_Close, ref_parent, &filter);\n}\n\nvoid    ImGuiTestContext::ItemInputValue(ImGuiTestRef ref, int value)\n{\n    char buf[32];\n    ImFormatString(buf, IM_ARRAYSIZE(buf), \"%d\", value);\n    ItemInput(ref);\n    KeyCharsReplaceEnter(buf);\n}\n\nvoid    ImGuiTestContext::ItemInputValue(ImGuiTestRef ref, float value)\n{\n    char buf[32];\n    ImFormatString(buf, IM_ARRAYSIZE(buf), \"%f\", value);\n    ItemInput(ref);\n    KeyCharsReplaceEnter(buf);\n}\n\nvoid    ImGuiTestContext::ItemInputValue(ImGuiTestRef ref, const char* value)\n{\n    ItemInput(ref);\n    KeyCharsReplaceEnter(value);\n}\n\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoError\nbool    ImGuiTestContext::ItemReadAsScalar(ImGuiTestRef ref, ImGuiDataType data_type, void* out_data, ImGuiTestOpFlags flags)\n{\n    if (IsError())\n        return false;\n\n    const ImGuiDataTypeInfo* data_type_info = ImGui::DataTypeGetInfo(data_type);\n    const ImGuiTestOpFlags SUPPORTED_FLAGS = ImGuiTestOpFlags_NoError;\n    IM_ASSERT((flags & ~SUPPORTED_FLAGS) == 0);\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"ItemSelectReadValue '%s' 0x%08X as %s\", ref.Path ? ref.Path : \"nullptr\", ref.ID, data_type_info->Name);\n    IM_CHECK_SILENT_RETV(out_data != nullptr, false);\n\n    Str256 backup_clipboard = ImGui::GetClipboardText();\n\n    ItemInput(ref, flags);\n#if IMGUI_VERSION_NUM < 19063\n    KeyPress(ImGuiKey_A | ImGuiMod_Shortcut);\n    KeyPress(ImGuiKey_C | ImGuiMod_Shortcut);   // Copy to clipboard\n#else\n    KeyPress(ImGuiKey_A | ImGuiMod_Ctrl);\n    KeyPress(ImGuiKey_C | ImGuiMod_Ctrl);       // Copy to clipboard\n#endif\n    KeyPress(ImGuiKey_Enter);\n\n    const char* clipboard = ImGui::GetClipboardText();\n    bool ret = ImGui::DataTypeApplyFromText(clipboard, data_type, out_data, data_type_info->ScanFmt);\n    if (ret == false)\n    {\n        if ((flags & ImGuiTestOpFlags_NoError) == 0)\n        {\n            LogError(\"Unable to parse buffer '%s' as %s\", clipboard, data_type_info->Name);\n            IM_CHECK_RETV(ret, false);\n        }\n    }\n    ImGui::SetClipboardText(backup_clipboard.c_str());\n\n    return ret;\n}\n\nint     ImGuiTestContext::ItemReadAsInt(ImGuiTestRef ref)\n{\n    int v = 0;\n    ItemReadAsScalar(ref, ImGuiDataType_S32, (void*)&v);\n    return v;\n}\n\nfloat   ImGuiTestContext::ItemReadAsFloat(ImGuiTestRef ref)\n{\n    float v = 0.0f;\n    ItemReadAsScalar(ref, ImGuiDataType_Float, (void*)&v);\n    return v;\n}\n\n// Convenient wrapper for ItemSelectAndReadString using our own storage\n// Returned pointer is only valid until next call to same function.\nconst char* ImGuiTestContext::ItemReadAsString(ImGuiTestRef ref)\n{\n    if (IsError())\n        return \"\";\n\n    size_t required_1 = ItemReadAsString(ref, TempString.Data, TempString.capacity());\n    if ((int)required_1 > TempString.capacity())\n    {\n        TempString.reserve((int)required_1);\n        size_t required_2 = ItemReadAsString(ref, TempString.Data, TempString.capacity());\n        IM_CHECK_SILENT_RETV(required_1 == required_2, \"\");\n    }\n    return TempString.Data;\n}\n\n// return required buffer size to store output value (#26, #66)\n// write up to out_buf_size to out_buf, always zero-terminated.\n// if (out_buf == nulltr) || (out_buf_size < return value), then you want to.\n// You'd probably want to wrap this in a helper for your preferred string type.\nsize_t  ImGuiTestContext::ItemReadAsString(ImGuiTestRef ref, char* out_buf, size_t out_buf_size)\n{\n    if (IsError())\n    {\n        if (out_buf_size > 0)\n            out_buf[0] = 0;\n        return 0;\n    }\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"ItemSelectAndReadString '%s' 0x%08X as string\", ref.Path ? ref.Path : \"nullptr\", ref.ID);\n    IM_CHECK_SILENT_RETV(out_buf != nullptr || out_buf_size == 0, false);\n\n    Str256 backup_clipboard = ImGui::GetClipboardText();\n\n    ItemInput(ref);\n#if IMGUI_VERSION_NUM < 19063\n    KeyPress(ImGuiKey_A | ImGuiMod_Shortcut);\n    KeyPress(ImGuiKey_C | ImGuiMod_Shortcut);   // Copy to clipboard\n#else\n    KeyPress(ImGuiKey_A | ImGuiMod_Ctrl);\n    KeyPress(ImGuiKey_C | ImGuiMod_Ctrl);       // Copy to clipboard\n#endif\n    KeyPress(ImGuiKey_Enter);\n\n    const char* value_str = ImGui::GetClipboardText();\n    size_t value_required_buf_size = strlen(value_str) + 1;\n\n    if (out_buf_size > 0)\n        ImFormatString(out_buf, out_buf_size, \"%.*s\", (int)ImMax(value_required_buf_size, out_buf_size), value_str);\n\n    ImGui::SetClipboardText(backup_clipboard.c_str());\n\n    return value_required_buf_size;\n}\n\nvoid    ImGuiTestContext::ItemHold(ImGuiTestRef ref, float time)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestRefDesc desc(ref);\n    LogDebug(\"ItemHold %s\", desc.c_str());\n\n    MouseMove(ref);\n\n    Yield();\n    Inputs->MouseButtonsValue = (1 << 0);\n    Sleep(time);\n    Inputs->MouseButtonsValue = 0;\n    Yield();\n}\n\nvoid    ImGuiTestContext::ItemHoldForFrames(ImGuiTestRef ref, int frames)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestRefDesc desc(ref);\n    LogDebug(\"ItemHoldForFrames %s\", desc.c_str());\n\n    MouseMove(ref);\n    Yield();\n    Inputs->MouseButtonsValue = (1 << 0);\n    Yield(frames);\n    Inputs->MouseButtonsValue = 0;\n    Yield();\n}\n\n// Used to test opening containers (TreeNode, Tabs) while dragging a payload.\n// Hold for 1 second and then release mouse button.\nvoid    ImGuiTestContext::ItemDragOverAndHold(ImGuiTestRef ref_src, ImGuiTestRef ref_dst)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestItemInfo item_src = ItemInfo(ref_src);\n    ImGuiTestItemInfo item_dst = ItemInfo(ref_dst);\n    ImGuiTestRefDesc desc_src(ref_src, item_src);\n    ImGuiTestRefDesc desc_dst(ref_dst, item_dst);\n    LogDebug(\"ItemDragOverAndHold %s to %s\", desc_src.c_str(), desc_dst.c_str());\n\n    MouseMove(ref_src, ImGuiTestOpFlags_NoCheckHoveredId);\n    SleepStandard();\n    MouseDown(0);\n\n    // Enforce lifting drag threshold even if both item are exactly at the same location.\n    // Don't lift the threshold in the same frame as calling MouseDown() as it can trigger two actions.\n    Yield();\n    MouseLiftDragThreshold();\n    MouseMove(ref_dst, ImGuiTestOpFlags_NoCheckHoveredId);\n\n    SleepNoSkip(1.0f, 1.0f / 10.0f);\n    MouseUp(0);\n}\n\nvoid    ImGuiTestContext::ItemDragAndDrop(ImGuiTestRef ref_src, ImGuiTestRef ref_dst, ImGuiMouseButton button)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestItemInfo item_src = ItemInfo(ref_src);\n    ImGuiTestItemInfo item_dst = ItemInfo(ref_dst);\n    ImGuiTestRefDesc desc_src(ref_src, item_src);\n    ImGuiTestRefDesc desc_dst(ref_dst, item_dst);\n    LogDebug(\"ItemDragAndDrop %s to %s\", desc_src.c_str(), desc_dst.c_str());\n\n    // Try to keep destination window above other windows. MouseMove() operation will avoid focusing destination window\n    // as that may steal ActiveID and break operation.\n    // FIXME-TESTS: This does not handle a case where source and destination windows overlap.\n    if (item_dst.Window != nullptr)\n        WindowBringToFront(item_dst.Window->ID);\n\n    // Use item_src/item_dst instead of ref_src/ref_dst so references with e.g. //$FOCUSED are latched once in the ItemInfo() call.\n    MouseMove(item_src.ID, ImGuiTestOpFlags_NoCheckHoveredId);\n    SleepStandard();\n    MouseDown(button);\n\n    // Enforce lifting drag threshold even if both item are exactly at the same location.\n    // Don't lift the threshold in the same frame as calling MouseDown() as it can trigger two actions.\n    Yield();\n    MouseLiftDragThreshold();\n    MouseMove(item_dst.ID, ImGuiTestOpFlags_NoCheckHoveredId | ImGuiTestOpFlags_NoFocusWindow);\n\n    SleepStandard();\n    MouseUp(button);\n}\n\nvoid    ImGuiTestContext::ItemDragWithDelta(ImGuiTestRef ref_src, ImVec2 pos_delta)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestItemInfo item_src = ItemInfo(ref_src);\n    ImGuiTestRefDesc desc_src(ref_src, item_src);\n    LogDebug(\"ItemDragWithDelta %s to (%f, %f)\", desc_src.c_str(), pos_delta.x, pos_delta.y);\n\n    MouseMove(ref_src, ImGuiTestOpFlags_NoCheckHoveredId);\n    SleepStandard();\n    MouseDown(0);\n\n    MouseMoveToPos(UiContext->IO.MousePos + pos_delta);\n    SleepStandard();\n    MouseUp(0);\n}\n\nbool    ImGuiTestContext::ItemExists(ImGuiTestRef ref)\n{\n    ImGuiTestItemInfo item = ItemInfo(ref, ImGuiTestOpFlags_NoError);\n    return item.ID != 0;\n}\n\n// May want to add support for ImGuiTestOpFlags_NoError if item does not exist?\nbool    ImGuiTestContext::ItemIsChecked(ImGuiTestRef ref)\n{\n    ImGuiTestItemInfo item = ItemInfo(ref);\n    return (item.StatusFlags & ImGuiItemStatusFlags_Checked) != 0;\n}\n\n// May want to add support for ImGuiTestOpFlags_NoError if item does not exist?\nbool    ImGuiTestContext::ItemIsOpened(ImGuiTestRef ref)\n{\n    ImGuiTestItemInfo item = ItemInfo(ref);\n    return (item.StatusFlags & ImGuiItemStatusFlags_Opened) != 0;\n}\n\nvoid    ImGuiTestContext::ItemVerifyCheckedIfAlive(ImGuiTestRef ref, bool checked)\n{\n    // This is designed to deal with disappearing items which will not update their state,\n    // e.g. a checkable menu item in a popup which closes when checked.\n    // Otherwise ItemInfo() data is preserved for an additional frame.\n    Yield();\n    ImGuiTestItemInfo item = ItemInfo(ref, ImGuiTestOpFlags_NoError);\n    if (item.ID == 0)\n        return;\n    if (item.TimestampMain + 1 >= ImGuiTestEngine_GetFrameCount(Engine) && item.TimestampStatus == item.TimestampMain)\n        IM_CHECK_SILENT(((item.StatusFlags & ImGuiItemStatusFlags_Checked) != 0) == checked);\n}\n\n// FIXME-TESTS: Could this be handled by ItemClose()?\nvoid    ImGuiTestContext::TabClose(ImGuiTestRef ref)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestRefDesc desc(ref);\n    LogDebug(\"TabClose %s\", desc.c_str());\n\n    // Move into first, then click close button as it appears\n    MouseMove(ref);\n    ImGuiTestRef backup_ref = GetRef();\n    SetRef(GetID(ref));\n    ItemClick(\"#CLOSE\");\n    SetRef(backup_ref);\n}\n\nbool    ImGuiTestContext::TabBarCompareOrder(ImGuiTabBar* tab_bar, const char** tab_order)\n{\n    if (IsError())\n        return false;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"TabBarCompareOrder\");\n    IM_CHECK_SILENT_RETV(tab_bar != nullptr, false);\n\n    // Display\n    char buf[256];\n    char* buf_end = buf + IM_ARRAYSIZE(buf);\n\n    char* p = buf;\n    for (int i = 0; i < tab_bar->Tabs.Size; i++)\n        p += ImFormatString(p, buf_end - p, \"%s\\\"%s\\\"\", i ? \", \" : \" \", ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[i]));\n    LogDebug(\"  Current  {%s }\", buf);\n\n    p = buf;\n    for (int i = 0; tab_order[i] != nullptr; i++)\n        p += ImFormatString(p, buf_end - p, \"%s\\\"%s\\\"\", i ? \", \" : \" \", tab_order[i]);\n    LogDebug(\"  Expected {%s }\", buf);\n\n    // Compare\n    for (int i = 0; tab_order[i] != nullptr; i++)\n    {\n        if (i >= tab_bar->Tabs.Size)\n            return false;\n        const char* current = ImGui::TabBarGetTabName(tab_bar, &tab_bar->Tabs[i]);\n        const char* expected = tab_order[i];\n        if (strcmp(current, expected) != 0)\n            return false;\n    }\n    return true;\n}\n\n// Automatically insert \"##MenuBar\" between window and menus.\n// Automatically open and navigate sub-menus\n// FIXME: Currently assume that any path after the window are sub-menus.\nvoid    ImGuiTestContext::MenuAction(ImGuiTestAction action, ImGuiTestRef ref)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestRefDesc desc(ref);\n    LogDebug(\"MenuAction %s\", desc.c_str());\n\n    IM_ASSERT(ref.Path != nullptr);\n\n    // MenuAction() doesn't support **/ in most case it would be equivalent to opening all menus to \"search\".\n    // [01] Works:\n    //   MenuClick(\"File/New\"):\n    // [02] Works:\n    //   MenuClick(\"File\");\n    //   MenuClick(\"File/New\");\n    // [03] Works:\n    //   MenuClick(\"File\");\n    //   ItemClick(\"**/New\");\n    // [04] Doesn't work: (may work in the future)\n    //   MenuClick(\"File\");\n    //   MenuClick(\"**/New\");\n    // [05] Doesn't work: (unlikely to ever work)\n    //   MenuClick(\"**/New\");\n    if (strncmp(ref.Path, \"**/\", 3) == 0)\n    {\n        LogError(\"\\\"**/\\\" is not yet supported by MenuAction().\");\n        return;\n    }\n\n    int depth = 0;\n    const char* path = ref.Path;\n    const char* path_end = path + strlen(path);\n\n    ImGuiWindow* ref_window = nullptr;\n    if ((path[0] == '/' && path[1] == '/') || (RefID == 0))\n    {\n        const char* end = strstr(path + 2, \"/\");\n        IM_CHECK_SILENT(end != nullptr); // Menu interaction without any menus specified in ref.\n        Str64 window_name;\n        window_name.append(path, end);\n        ref_window = GetWindowByRef(GetID(window_name.c_str()));\n        path = end + 1;\n        if (ref_window == nullptr)\n            LogError(\"MenuAction: missing ref window (invalid name \\\"//%s\\\" ?\", window_name.c_str());\n    }\n    else\n    {\n        ref_window = GetWindowByRef(RefID);\n        if (ref_window == nullptr)\n            LogError(\"MenuAction: missing ref window (invalid SetRef value?)\");\n    }\n    IM_CHECK_SILENT(ref_window != nullptr);  // A ref window must always be set\n\n    ImGuiWindow* current_window = ref_window;\n    Str128 buf;\n    while (path < path_end && !IsError())\n    {\n        const char* p = ImStrchrRangeWithEscaping(path, path_end, '/');\n        if (p == nullptr)\n            p = path_end;\n\n        const bool is_target_item = (p == path_end);\n#if IMGUI_VERSION_NUM >= 19174\n        if (current_window->Flags & ImGuiWindowFlags_MenuBar)\n            buf.setf(\"//%s/##MenuBar/%.*s\", current_window->Name, (int)(p - path), path);    // Click menu in menu bar\n#else\n        if (current_window->Flags & ImGuiWindowFlags_MenuBar)\n            buf.setf(\"//%s/##menubar/%.*s\", current_window->Name, (int)(p - path), path);    // Click menu in menu bar\n#endif\n        else\n            buf.setf(\"//%s/%.*s\", current_window->Name, (int)(p - path), path);              // Click sub menu in its own window\n\n#if IMGUI_VERSION_NUM < 18520\n        if (depth == 0 && (current_window->Flags & ImGuiWindowFlags_Popup))\n            depth++;\n#endif\n\n        // Timestamps updated in hooks submitted in ui code.\n        ImGuiTestItemInfo item = ItemInfo(buf.c_str());\n        IM_CHECK_SILENT(item.ID != 0);\n        if (item.TimestampStatus < UiContext->FrameCount)\n        {\n            Yield();\n            item = ItemInfo(buf.c_str());\n            IM_CHECK_SILENT(item.ID != 0);\n        }\n\n        if ((item.StatusFlags & ImGuiItemStatusFlags_Opened) == 0) // Open menus can be ignored completely.\n        {\n            // We cannot move diagonally to a menu item because depending on the angle and other items we cross on our path we could close our target menu.\n            // First move horizontally into the menu, then vertically!\n            if (depth > 0)\n            {\n                MouseSetViewport(item.Window);\n                if (Inputs->MousePosValue.x <= item.RectFull.Min.x || Inputs->MousePosValue.x >= item.RectFull.Max.x)\n                    MouseMoveToPos(ImVec2(item.RectFull.GetCenter().x, Inputs->MousePosValue.y));\n                if (Inputs->MousePosValue.y <= item.RectFull.Min.y || Inputs->MousePosValue.y >= item.RectFull.Max.y)\n                    MouseMoveToPos(ImVec2(Inputs->MousePosValue.x, item.RectFull.GetCenter().y));\n            }\n\n            if (is_target_item)\n            {\n                // Final item\n                ItemAction(action, buf.c_str());\n                break;\n            }\n            else\n            {\n                // Then aim at the menu item. Menus may be navigated by holding mouse button down by hovering a menu.\n                ItemAction(Inputs->MouseButtonsValue ? ImGuiTestAction_Hover : ImGuiTestAction_Click, buf.c_str());\n            }\n        }\n#if IMGUI_VERSION_NUM < 19187\n        current_window = GetWindowByRef(Str16f(\"//##Menu_%02d\", depth).c_str());\n#else\n        current_window = GetWindowByRef(Str16f(\"//###Menu_%02d\", depth).c_str());\n#endif\n        IM_CHECK_SILENT(current_window != nullptr);\n\n        path = p + 1;\n        depth++;\n    }\n}\n\nvoid    ImGuiTestContext::MenuActionAll(ImGuiTestAction action, ImGuiTestRef ref_parent)\n{\n    ImGuiTestItemList items;\n    MenuAction(ImGuiTestAction_Open, ref_parent);\n    GatherItems(&items, \"//$FOCUSED\", 1);\n    //LogItemList(&items);\n\n    for (auto item : items)\n    {\n        MenuAction(ImGuiTestAction_Open, ref_parent); // We assume that every interaction will close the menu again\n\n        if (action == ImGuiTestAction_Check || action == ImGuiTestAction_Uncheck)\n        {\n            ImGuiTestItemInfo info2 = ItemInfo(item.ID); // refresh info\n            if ((info2.ItemFlags & ImGuiItemFlags_Disabled) != 0) // FIXME: Report disabled state in log? Make that optional?\n                continue;\n            if ((info2.StatusFlags & ImGuiItemStatusFlags_Checkable) == 0)\n                continue;\n        }\n\n        ItemAction(action, item.ID);\n    }\n}\n\nstatic bool IsWindowACombo(ImGuiWindow* window)\n{\n    if ((window->Flags & ImGuiWindowFlags_Popup) == 0)\n        return false;\n    if (strncmp(window->Name, \"##Combo_\", strlen(\"##Combo_\")) != 0)\n        return false;\n    return true;\n}\n\n// Usage: ComboClick(\"ComboName/ItemName\");\nvoid    ImGuiTestContext::ComboClick(ImGuiTestRef ref)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestRefDesc desc(ref);\n    LogDebug(\"ComboClick %s\", desc.c_str());\n\n    IM_ASSERT(ref.Path != nullptr); // Should always pass an actual path, not an ID.\n\n    const char* path = ref.Path;\n    const char* path_end = path + strlen(path);\n    const char* p = ImStrchrRangeWithEscaping(path, path_end, '/');\n    if (p == nullptr)\n    {\n        LogError(\"Error: path should contains a / separator, e.g. ComboClick(\\\"mycombo/myitem\\\")\");\n        IM_CHECK(p != nullptr);\n    }\n\n    Str128f combo_popup_buf = Str128f(\"%.*s\", (int)(p-path), path);\n    ItemClick(combo_popup_buf.c_str());\n\n    ImGuiWindow* popup = GetWindowByRef(\"//$FOCUSED\");\n    IM_CHECK_SILENT(popup && IsWindowACombo(popup));\n\n    Str128f combo_item_buf = Str128f(\"//%s/**/%s\", popup->Name, p + 1);\n    ItemClick(combo_item_buf.c_str());\n\n    // For if Combo Selectables uses ImGuiSelectableFlags_NoAutoClosePopups\n    if (GetWindowByRef(\"//$FOCUSED\") == popup)\n        KeyPress(ImGuiKey_Enter);\n}\n\nvoid    ImGuiTestContext::ComboClickAll(ImGuiTestRef ref_parent)\n{\n    ItemClick(ref_parent);\n\n    ImGuiWindow* popup = GetWindowByRef(\"//$FOCUSED\");\n    IM_CHECK_SILENT(popup && IsWindowACombo(popup));\n\n    ImGuiTestItemList items;\n    GatherItems(&items, \"//$FOCUSED\");\n    for (auto item : items)\n    {\n        // Reopen popup when closed\n        if (GetWindowByRef(\"//$FOCUSED\") != popup)\n            ItemClick(ref_parent);\n        ItemClick(item.ID);\n    }\n\n    // For if Combo Selectables uses ImGuiSelectableFlags_NoAutoClosePopups\n    if (GetWindowByRef(\"//$FOCUSED\") == popup)\n        KeyPress(ImGuiKey_Enter);\n}\n\nstatic ImGuiTableColumn* HelperTableFindColumnByName(ImGuiTable* table, const char* name)\n{\n    for (int i = 0; i < table->Columns.size(); i++)\n        if (strcmp(ImGui::TableGetColumnName(table, i), name) == 0)\n            return &table->Columns[i];\n    return nullptr;\n}\n\nvoid ImGuiTestContext::TableOpenContextMenu(ImGuiTestRef ref, int column_n)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestRefDesc desc(ref);\n    LogDebug(\"TableOpenContextMenu %s\", desc.c_str());\n\n    ImGuiTable* table = ImGui::TableFindByID(GetID(ref));\n    IM_CHECK_SILENT(table != nullptr);\n\n    if (column_n == -1)\n        column_n = table->RightMostEnabledColumn;\n\n    IM_CHECK(column_n >= 0 && column_n <= table->ColumnsCount);\n    ImGuiTableColumn* column = &table->Columns[column_n];\n    IM_CHECK_SILENT(column->IsEnabled);\n\n    ImGuiID header_id = TableGetHeaderID(table, column_n);\n\n    // Make visible\n    if (!ItemExists(header_id))\n        ScrollToPosX(table->InnerWindow->ID, (column->MinX + column->MaxX) * 0.5f);\n\n    ItemClick(header_id, ImGuiMouseButton_Right);\n    Yield();\n}\n\nImGuiSortDirection ImGuiTestContext::TableClickHeader(ImGuiTestRef ref, const char* label, ImGuiKeyChord key_mods)\n{\n    IM_ASSERT((key_mods & ~ImGuiMod_Mask_) == 0); // Cannot pass keys only mods\n\n    ImGuiTable* table = ImGui::TableFindByID(GetID(ref));\n    IM_CHECK_SILENT_RETV(table != nullptr, ImGuiSortDirection_None);\n\n    ImGuiTableColumn* column = HelperTableFindColumnByName(table, label);\n    IM_CHECK_SILENT_RETV(column != nullptr, ImGuiSortDirection_None);\n\n    if (key_mods != ImGuiMod_None)\n        KeyDown(key_mods);\n\n    ImGuiID header_id = TableGetHeaderID(table, label);\n\n    // Make visible\n    if (!ItemExists(header_id))\n        ScrollToPosX(table->InnerWindow->ID, (column->MinX + column->MaxX) * 0.5f);\n\n    ItemClick(header_id, ImGuiMouseButton_Left);\n\n    if (key_mods != ImGuiMod_None)\n        KeyUp(key_mods);\n    return (ImGuiSortDirection)column->SortDirection;\n}\n\nvoid ImGuiTestContext::TableSetColumnEnabled(ImGuiTestRef ref, const char* label, bool enabled)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestRefDesc desc(ref);\n    LogDebug(\"TableSetColumnEnabled %s label '%s' enabled = %d\", desc.c_str(), label, enabled);\n\n    ImGuiTable* table = ImGui::TableFindByID(GetID(ref));\n    IM_CHECK_SILENT(table != NULL);\n    ImGuiTableColumn* column = HelperTableFindColumnByName(table, label);\n    int column_n = column->IsEnabled ? table->Columns.index_from_ptr(column) : -1;\n    TableOpenContextMenu(ref, column_n);\n\n    ImGuiTestRef backup_ref = GetRef();\n    SetRef(\"//$FOCUSED\");\n    if (enabled)\n        ItemCheck(label);\n    else\n        ItemUncheck(label);\n    PopupCloseOne();\n    SetRef(backup_ref);\n}\n\nvoid ImGuiTestContext::TableResizeColumn(ImGuiTestRef ref, int column_n, float width)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestRefDesc desc(ref);\n    LogDebug(\"TableResizeColumn %s column %d width %.2f\", desc.c_str(), column_n, width);\n\n    ImGuiTable* table = ImGui::TableFindByID(GetID(ref));\n    IM_CHECK_SILENT(table != nullptr);\n\n    ImGuiID resize_id = ImGui::TableGetColumnResizeID(table, column_n);\n    float old_width = table->Columns[column_n].WidthGiven;\n    ItemDragWithDelta(resize_id, ImVec2(width - old_width, 0));\n\n    IM_CHECK_EQ(table->Columns[column_n].WidthRequest, width);\n}\n\nconst ImGuiTableSortSpecs* ImGuiTestContext::TableGetSortSpecs(ImGuiTestRef ref)\n{\n    ImGuiTable* table = ImGui::TableFindByID(GetID(ref));\n    IM_CHECK_SILENT_RETV(table != nullptr, nullptr);\n\n    ImGuiContext& g = *UiContext;\n    ImSwap(table, g.CurrentTable);\n    const ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs();\n    ImSwap(table, g.CurrentTable);\n    return sort_specs;\n}\n\nvoid    ImGuiTestContext::WindowClose(ImGuiTestRef ref)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"WindowClose\");\n    ImGuiTestRef backup_ref = GetRef();\n    SetRef(GetID(ref));\n\n#ifdef IMGUI_HAS_DOCK\n    // When docked: first move to Tab to make Close Button appear.\n    if (ImGuiWindow* window = GetWindowByRef(\"\"))\n        if (window->DockIsActive)\n            MouseMove(window->TabId);\n#endif\n\n    ItemClick(\"#CLOSE\");\n    SetRef(backup_ref);\n}\n\nvoid    ImGuiTestContext::WindowCollapse(ImGuiTestRef window_ref, bool collapsed)\n{\n    if (IsError())\n        return;\n    ImGuiWindow* window = GetWindowByRef(window_ref);\n    if (window == nullptr)\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    if (window->Collapsed != collapsed)\n    {\n        LogDebug(\"WindowCollapse %d\", collapsed);\n        ImGuiTestOpFlags backup_op_flags = OpFlags;\n        OpFlags |= ImGuiTestOpFlags_NoAutoUncollapse;\n        ImGuiTestRef backup_ref = GetRef();\n        SetRef(window->ID);\n        ItemClick(\"#COLLAPSE\");\n        SetRef(backup_ref);\n        OpFlags = backup_op_flags;\n        Yield();\n        IM_CHECK(window->Collapsed == collapsed);\n    }\n}\n\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoError\nvoid    ImGuiTestContext::WindowFocus(ImGuiTestRef ref, ImGuiTestOpFlags flags)\n{\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    ImGuiTestRefDesc desc(ref);\n    LogDebug(\"WindowFocus('%s')\", desc.c_str());\n\n    ImGuiWindow* window = GetWindowByRef(ref);\n    IM_CHECK_SILENT(window != nullptr);\n    if (window)\n    {\n        ImGui::FocusWindow(window); // FIXME-TESTS-NOT_SAME_AS_END_USER: In theory should be replaced by click on title-bar or tab?\n        Yield();\n    }\n\n    // We cannot guarantee this will work 100%\n    // - Some modal inhibition may kick-in.\n    // - Because merely hovering an item may e.g. open a window or change focus.\n    //   In particular this can be the case with MenuItem. So trying to Open a MenuItem may lead to its child opening while hovering,\n    //   causing this function to seemingly fail (even if the end goal was reached).\n    ImGuiContext& g = *UiContext;\n    if ((window != g.NavWindow) && !(flags & ImGuiTestOpFlags_NoError))\n        LogDebug(\"-- Expected focused window '%s', but '%s' got focus back.\", window->Name, g.NavWindow ? g.NavWindow->Name : \"<nullptr>\");\n}\n\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoError\n// - ImGuiTestOpFlags_NoFocusWindow\n// FIXME: In principle most calls to this could be replaced by WindowFocus()?\nvoid    ImGuiTestContext::WindowBringToFront(ImGuiTestRef ref, ImGuiTestOpFlags flags)\n{\n    ImGuiContext& g = *UiContext;\n    if (IsError())\n        return;\n\n    ImGuiWindow* window = GetWindowByRef(ref);\n    IM_CHECK_SILENT(window != nullptr);\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    if (window != g.NavWindow && !(flags & ImGuiTestOpFlags_NoFocusWindow))\n    {\n        LogDebug(\"WindowBringToFront()->FocusWindow('%s')\", window->Name);\n        ImGui::FocusWindow(window); // FIXME-TESTS-NOT_SAME_AS_END_USER: In theory should be replaced by click on title-bar or tab?\n        Yield(2);\n    }\n    else if (window->RootWindow != g.Windows.back()->RootWindow)\n    {\n        LogDebug(\"BringWindowToDisplayFront('%s') (window.back=%s)\", window->Name, g.Windows.back()->Name);\n        ImGui::BringWindowToDisplayFront(window); // FIXME-TESTS-NOT_SAME_AS_END_USER: This is not an actually possible action for end-user.\n        Yield(2);\n    }\n\n    // Same as WindowFocus()\n    if ((window != g.NavWindow) && !(flags & ImGuiTestOpFlags_NoError))\n        LogDebug(\"-- Expected focused window '%s', but '%s' got focus back.\", window->Name, g.NavWindow ? g.NavWindow->Name : \"<nullptr>\");\n}\n\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoFocusWindow\nvoid    ImGuiTestContext::WindowMove(ImGuiTestRef ref, ImVec2 input_pos, ImVec2 pivot, ImGuiTestOpFlags flags)\n{\n    if (IsError())\n        return;\n\n    ImGuiWindow* window = GetWindowByRef(ref);\n    IM_CHECK_SILENT(window != nullptr);\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"WindowMove '%s' (%.1f,%.1f) \", window->Name, input_pos.x, input_pos.y);\n    ImVec2 target_pos = ImFloor(input_pos - pivot * window->Size);\n    if (ImLengthSqr(target_pos - window->Pos) < 0.001f)\n    {\n        //MouseMoveToPos(window->Pos); //??\n        return;\n    }\n\n    if ((flags & ImGuiTestOpFlags_NoFocusWindow) == 0)\n        WindowFocus(window->ID);\n    WindowCollapse(window->ID, false);\n\n    MouseSetViewport(window);\n    MouseMoveToPos(GetWindowTitlebarPoint(ref));\n    //IM_CHECK_SILENT(UiContext->HoveredWindow == window);\n    MouseDown(0);\n\n    // Disable docking\n#ifdef IMGUI_HAS_DOCK\n    if (UiContext->IO.ConfigDockingWithShift)\n        KeyUp(ImGuiMod_Shift);\n    else\n        KeyDown(ImGuiMod_Shift);\n#endif\n\n    ImVec2 delta = target_pos - window->Pos;\n    MouseMoveToPos(Inputs->MousePosValue + delta);\n    Yield();\n\n    MouseUp();\n#ifdef IMGUI_HAS_DOCK\n    KeyUp(ImGuiMod_Shift);\n#endif\n    MouseSetViewport(window); // Update in case window has changed viewport\n}\n\nvoid    ImGuiTestContext::WindowResize(ImGuiTestRef ref, ImVec2 size)\n{\n    if (IsError())\n        return;\n\n    ImGuiWindow* window = GetWindowByRef(ref);\n    IM_CHECK_SILENT(window != nullptr);\n    size = ImFloor(size);\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"WindowResize '%s' (%.1f,%.1f)\", window->Name, size.x, size.y);\n    if (ImLengthSqr(size - window->Size) < 0.001f)\n        return;\n\n    WindowFocus(window->ID);\n    WindowCollapse(window->ID, false);\n\n    // Extra yield as newly created window that have AutoFitFramesX/AutoFitFramesY set are temporarily not submitting their resize widgets. Give them a bit of slack.\n    Yield();\n\n    // Aim at resize border or resize corner\n    ImGuiID border_x2 = ImGui::GetWindowResizeBorderID(window, ImGuiDir_Right);\n    ImGuiID border_y2 = ImGui::GetWindowResizeBorderID(window, ImGuiDir_Down);\n    ImGuiID resize_br = ImGui::GetWindowResizeCornerID(window, 0);\n    ImGuiID id;\n    if (ImAbs(size.x - window->Size.x) < 0.0001f && ItemExists(border_y2))\n        id = border_y2;\n    else if (ImAbs(size.y - window->Size.y) < 0.0001f && ItemExists(border_x2))\n        id = border_x2;\n    else\n        id = resize_br;\n    MouseMove(id, ImGuiTestOpFlags_IsSecondAttempt);\n\n    if (size.x <= 0.0f || size.y <= 0.0f)\n    {\n        IM_ASSERT(size.x <= 0.0f && size.y <= 0.0f);\n        MouseDoubleClick(0);\n        Yield();\n    }\n    else\n    {\n        MouseDown(0);\n        ImVec2 delta = size - window->Size;\n        MouseMoveToPos(Inputs->MousePosValue + delta);\n        Yield(); // At this point we don't guarantee the final size!\n        MouseUp();\n    }\n    MouseSetViewport(window); // Update in case window has changed viewport\n}\n\nvoid    ImGuiTestContext::PopupCloseOne()\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"PopupCloseOne\");\n    ImGuiContext& g = *UiContext;\n    if (g.OpenPopupStack.Size > 0)\n        ImGui::ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);    // FIXME-TESTS-NOT_SAME_AS_END_USER\n    Yield();\n}\n\nvoid    ImGuiTestContext::PopupCloseAll()\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"PopupCloseAll\");\n    ImGuiContext& g = *UiContext;\n    if (g.OpenPopupStack.Size > 0)\n        ImGui::ClosePopupToLevel(0, true);    // FIXME-TESTS-NOT_SAME_AS_END_USER\n    Yield();\n}\n\n// Match code in BeginPopupEx()\nImGuiID ImGuiTestContext::PopupGetWindowID(ImGuiTestRef ref)\n{\n    Str30f popup_name(\"//##Popup_%08x\", GetID(ref));\n    return GetID(popup_name.c_str());\n}\n\n#ifdef IMGUI_HAS_VIEWPORT\nvoid    ImGuiTestContext::ViewportPlatform_SetWindowPos(ImGuiViewport* viewport, const ImVec2& pos)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"ViewportPlatform_SetWindowPos(0x%08X, {%.2f,%.2f)\", viewport->ID, pos.x, pos.y);\n    Inputs->Queue.push_back(ImGuiTestInput::ForViewportSetPos(viewport->ID, pos)); // Queued since this will poke into backend, best to do in main thread.\n    Yield(); // Submit to Platform\n    Yield(); // Let Dear ImGui next frame see it\n}\n\nvoid    ImGuiTestContext::ViewportPlatform_SetWindowSize(ImGuiViewport* viewport, const ImVec2& size)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"ViewportPlatform_SetWindowSize(0x%08X, {%.2f,%.2f)\", viewport->ID, size.x, size.y);\n    Inputs->Queue.push_back(ImGuiTestInput::ForViewportSetSize(viewport->ID, size)); // Queued since this will poke into backend, best to do in main thread.\n    Yield(); // Submit to Platform\n    Yield(); // Let Dear ImGui next frame see it\n}\n\n// Simulate a platform focus WITHOUT a click perceived by dear imgui. Similar to clicking on Platform title bar.\nvoid    ImGuiTestContext::ViewportPlatform_SetWindowFocus(ImGuiViewport* viewport)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"ViewportPlatform_SetWindowFocus(0x%08X)\", viewport->ID);\n    Inputs->Queue.push_back(ImGuiTestInput::ForViewportFocus(viewport->ID)); // Queued since this will poke into backend, best to do in main thread.\n    Yield(); // Submit to Platform\n    Yield(); // Let Dear ImGui next frame see it\n}\n\n// Simulate a platform window closure.\nvoid    ImGuiTestContext::ViewportPlatform_CloseWindow(ImGuiViewport* viewport)\n{\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"ViewportPlatform_CloseWindow(0x%08X)\", viewport->ID);\n    Inputs->Queue.push_back(ImGuiTestInput::ForViewportClose(viewport->ID)); // Queued since this will poke into backend, best to do in main thread.\n    Yield(); // Submit to Platform\n    Yield(3); // Let Dear ImGui next frame see it\n}\n\n#endif\n\n#ifdef IMGUI_HAS_DOCK\n// Note: unlike DockBuilder functions, for _nodes_ this require the node to be visible.\n// Supported values for ImGuiTestOpFlags:\n// - ImGuiTestOpFlags_NoFocusWindow\n// FIXME-TESTS: USING ImGuiTestOpFlags_NoFocusWindow leads to increase of ForeignWindowsHideOverPos(), best to avoid\nvoid    ImGuiTestContext::DockInto(ImGuiTestRef src_id, ImGuiTestRef dst_id, ImGuiDir split_dir, bool split_outer, ImGuiTestOpFlags flags)\n{\n    ImGuiContext& g = *UiContext;\n    if (IsError())\n        return;\n\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n\n    ImGuiWindow* window_src = GetWindowByRef(src_id);\n    ImGuiWindow* window_dst = GetWindowByRef(dst_id);\n    ImGuiDockNode* node_src = ImGui::DockBuilderGetNode(GetID(src_id));\n    ImGuiDockNode* node_dst = ImGui::DockBuilderGetNode(GetID(dst_id));\n    IM_CHECK_SILENT((window_src != nullptr) != (node_src != nullptr)); // Src must be either a window either a node\n    IM_CHECK_SILENT((window_dst != nullptr) != (node_dst != nullptr)); // Dst must be either a window either a node\n\n    // Infer node from window. Not the opposite as docking a node would imply docking all of it.\n    if (node_src)\n        window_src = node_src->HostWindow;\n    if (node_dst)\n        window_dst = node_dst->HostWindow;\n\n    Str128f log(\"DockInto() Src: %s '%s' (0x%08X), Dst: %s '%s' (0x%08X), SplitDir = %d\",\n        node_src ? \"node\" : \"window\", node_src ? \"\" : window_src->Name, node_src ? node_src->ID : window_src->ID,\n        node_dst ? \"node\" : \"window\", node_dst ? \"\" : window_dst->Name, node_dst ? node_dst->ID : window_dst->ID, split_dir);\n    LogDebug(\"%s\", log.c_str());\n\n    IM_CHECK_SILENT(window_src != nullptr);\n    IM_CHECK_SILENT(window_dst != nullptr);\n    IM_CHECK_SILENT(window_src->WasActive);\n    IM_CHECK_SILENT(window_dst->WasActive);\n\n    // Avoid focusing if we don't need it (this facilitate avoiding focus flashing when recording animated gifs)\n    if (!(flags & ImGuiTestOpFlags_NoFocusWindow))\n    {\n        if (g.Windows[g.Windows.Size - 2] != window_dst)\n            WindowFocus(window_dst->ID);\n        if (g.Windows[g.Windows.Size - 1] != window_src)\n            WindowFocus(window_src->ID);\n    }\n\n    // Aim at title bar or tab or node grab\n    ImGuiTestRef ref_src;\n    if (node_src)\n        ref_src = ImGui::DockNodeGetWindowMenuButtonId(node_src); // Whole node grab\n    else\n        ref_src = (window_src->DockIsActive ? window_src->TabId : window_src->MoveId); // FIXME-TESTS FIXME-DOCKING: Identify tab\n    MouseMove(ref_src, ImGuiTestOpFlags_NoCheckHoveredId);\n    SleepStandard();\n\n    // Start dragging source, so it gets undocked already, because we calculate target position\n    // (Consider the possibility that dragging this out will move target position)\n    MouseDown(0);\n    if (g.IO.ConfigDockingWithShift)\n        KeyDown(ImGuiMod_Shift);\n    Yield();\n    MouseLiftDragThreshold();\n    if (window_src->DockIsActive)\n        MouseMoveToPos(g.IO.MousePos + ImVec2(0, ImGui::GetFrameHeight() * 2.0f));\n    else\n        Yield(); // A yield is necessary to start the moving, otherwise if by the time we call MouseSetViewport() no frame has elapsed and the viewports differs, dragging will fail.\n    // (Button still held)\n\n    // Locate target\n    ImVec2 drop_pos;\n    bool drop_is_valid = ImGui::DockContextCalcDropPosForDocking(window_dst, node_dst, window_src, node_src, split_dir, split_outer, &drop_pos);\n    IM_CHECK_SILENT(drop_is_valid);\n    if (!drop_is_valid)\n    {\n        if (g.IO.ConfigDockingWithShift)\n            KeyUp(ImGuiMod_Shift);\n        return;\n    }\n\n    // Ensure we can reach target\n    WindowTeleportToMakePosVisible(window_dst->ID, drop_pos);\n    ImGuiWindow* friend_windows[] = { window_src, window_dst, nullptr };\n    _ForeignWindowsHideOverPos(drop_pos, friend_windows);\n\n    // Drag\n    drop_is_valid = ImGui::DockContextCalcDropPosForDocking(window_dst, node_dst, window_src, node_src, split_dir, split_outer, &drop_pos);\n    IM_CHECK_SILENT(drop_is_valid);\n    MouseSetViewport(window_dst);\n    MouseMoveToPos(drop_pos);\n    if (node_src)\n        window_src = node_src->HostWindow;  // Dragging a menu button may detach a node and create a new window.\n    IM_CHECK_SILENT(g.MovingWindow == window_src);\n\n    Yield(2);    // Docking to dockspace over viewport (needs extra frame) or moving a dock node to another node (needs two extra frames) fails in fast mode without this.\n    IM_CHECK_SILENT(g.HoveredWindowUnderMovingWindow && g.HoveredWindowUnderMovingWindow->RootWindowDockTree == window_dst->RootWindowDockTree);\n\n    // Docking will happen on the mouse-up\n    const ImGuiID prev_dock_id = window_src->DockId;\n    const ImGuiID prev_dock_parent_id = (window_src->DockNode && window_src->DockNode->ParentNode) ? window_src->DockNode->ParentNode->ID : 0;\n    const ImGuiID prev_dock_node_as_host_id = window_src->DockNodeAsHost ? window_src->DockNodeAsHost->ID : 0;\n\n    MouseUp(0);\n\n    // Cool down\n    if (g.IO.ConfigDockingWithShift)\n        KeyUp(ImGuiMod_Shift);\n    _ForeignWindowsUnhideAll();\n    Yield();\n    Yield();\n\n    // Verify docking has succeeded! It's not easy to write a full fledged test, let's go for a simple one.\n    if (!(flags & ImGuiTestOpFlags_NoError))\n    {\n        const ImGuiID curr_dock_id = window_src->DockId;\n        const ImGuiID curr_dock_parent_id = (window_src->DockNode && window_src->DockNode->ParentNode) ? window_src->DockNode->ParentNode->ID : 0;\n        const ImGuiID curr_dock_node_as_host_id = window_src->DockNodeAsHost ? window_src->DockNodeAsHost->ID : 0;\n        IM_CHECK_SILENT((prev_dock_id != curr_dock_id) || (prev_dock_parent_id != curr_dock_parent_id) || (prev_dock_node_as_host_id != curr_dock_node_as_host_id));\n    }\n}\n\nvoid    ImGuiTestContext::DockClear(const char* window_name, ...)\n{\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"DockClear\");\n\n    va_list args;\n    va_start(args, window_name);\n    while (window_name != nullptr)\n    {\n        ImGui::DockBuilderDockWindow(window_name, 0);\n        window_name = va_arg(args, const char*);\n    }\n    va_end(args);\n\n    if (ActiveFunc == ImGuiTestActiveFunc_TestFunc)\n        Yield(2); // Give time to rebuild dock in case io.ConfigDockingAlwaysTabBar is set\n}\n\nbool    ImGuiTestContext::WindowIsUndockedOrStandalone(ImGuiWindow* window)\n{\n    if (window->DockNode == nullptr)\n        return true;\n    return DockIdIsUndockedOrStandalone(window->DockId);\n}\n\nbool    ImGuiTestContext::DockIdIsUndockedOrStandalone(ImGuiID dock_id)\n{\n    if (dock_id == 0)\n        return true;\n    if (ImGuiDockNode* node = ImGui::DockBuilderGetNode(dock_id))\n        if (node->IsFloatingNode() && node->IsLeafNode() && node->Windows.Size == 1)\n            return true;\n    return false;\n}\n\nvoid    ImGuiTestContext::DockNodeHideTabBar(ImGuiDockNode* node, bool hidden)\n{\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"DockNodeHideTabBar %d\", hidden);\n\n    ImGuiTestRef backup_ref = GetRef();\n    if (hidden)\n    {\n        SetRef(node->HostWindow);\n        ItemClick(ImGui::DockNodeGetWindowMenuButtonId(node));\n        ImGuiID popup_id = PopupGetWindowID(GetID(\"#WindowMenu\", node->ID));\n        SetRef(popup_id);\n#if IMGUI_VERSION_NUM >= 18910\n        ItemClick(\"###HideTabBar\");\n#else\n        ItemClick(\"Hide tab bar\");\n#endif\n        IM_CHECK_SILENT(node->IsHiddenTabBar());\n    }\n    else\n    {\n        IM_CHECK_SILENT(node->VisibleWindow != nullptr);\n        SetRef(node->VisibleWindow);\n        ItemClick(\"#UNHIDE\", 0, ImGuiTestOpFlags_MoveToEdgeD | ImGuiTestOpFlags_MoveToEdgeR);\n        IM_CHECK_SILENT(!node->IsHiddenTabBar());\n    }\n    SetRef(backup_ref);\n}\n\nvoid    ImGuiTestContext::UndockNode(ImGuiID dock_id)\n{\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"UndockNode 0x%08X\", dock_id);\n\n    ImGuiDockNode* node = ImGui::DockBuilderGetNode(dock_id);\n    if (node == nullptr)\n        return;\n    if (node->IsFloatingNode())\n        return;\n    if (node->Windows.empty())\n        return;\n\n#if IMGUI_VERSION_NUM >= 19071\n    const float h = node->Windows[0]->TitleBarHeight;\n#else\n    const float h = node->Windows[0]->TitleBarHeight();\n#endif\n    if (!UiContext->IO.ConfigDockingWithShift)\n        KeyDown(ImGuiMod_Shift); // Disable docking\n    ItemDragWithDelta(ImGui::DockNodeGetWindowMenuButtonId(node), ImVec2(h, h) * -2);\n    if (!UiContext->IO.ConfigDockingWithShift)\n        KeyUp(ImGuiMod_Shift);\n    MouseUp();\n}\n\nvoid    ImGuiTestContext::UndockWindow(const char* window_name)\n{\n    IM_ASSERT(window_name != nullptr);\n    IMGUI_TEST_CONTEXT_REGISTER_DEPTH(this);\n    LogDebug(\"UndockWindow \\\"%s\\\"\", window_name);\n\n    ImGuiWindow* window = GetWindowByRef(window_name);\n    if (!window->DockIsActive)\n        return;\n\n#if IMGUI_VERSION_NUM >= 19071\n    const float h = window->TitleBarHeight;\n#else\n    const float h = window->TitleBarHeight();\n#endif\n    if (!UiContext->IO.ConfigDockingWithShift)\n        KeyDown(ImGuiMod_Shift);\n    ItemDragWithDelta(window->TabId, ImVec2(h, h) * -2);\n    if (!UiContext->IO.ConfigDockingWithShift)\n        KeyUp(ImGuiMod_Shift);\n    Yield();\n}\n\n#endif // #ifdef IMGUI_HAS_DOCK\n\n//-------------------------------------------------------------------------\n// ImGuiTestContext - Performance Tools\n//-------------------------------------------------------------------------\n\n// Calculate the reference DeltaTime, averaged over PerfIterations/500 frames, with GuiFunc disabled.\nvoid    ImGuiTestContext::PerfCalcRef()\n{\n    LogDebug(\"Measuring ref dt...\");\n    RunFlags |= ImGuiTestRunFlags_GuiFuncDisable;\n\n    ImMovingAverage<double> delta_times;\n    delta_times.Init(PerfIterations);\n    for (int n = 0; n < PerfIterations && !Abort; n++)\n    {\n        Yield();\n        delta_times.AddSample(UiContext->IO.DeltaTime);\n    }\n\n    PerfRefDt = delta_times.GetAverage();\n    RunFlags &= ~ImGuiTestRunFlags_GuiFuncDisable;\n}\n\nvoid    ImGuiTestContext::PerfCapture(const char* category, const char* test_name, const char* csv_file)\n{\n    if (IsError())\n        return;\n\n    // Calculate reference average DeltaTime if it wasn't explicitly called by TestFunc\n    if (PerfRefDt < 0.0)\n        PerfCalcRef();\n    IM_ASSERT(PerfRefDt >= 0.0);\n\n    // Yield for the average to stabilize\n    LogDebug(\"Measuring GUI dt...\");\n    ImMovingAverage<double> delta_times;\n    delta_times.Init(PerfIterations);\n    for (int n = 0; n < PerfIterations && !Abort; n++)\n    {\n        Yield();\n        delta_times.AddSample(UiContext->IO.DeltaTime);\n    }\n    if (Abort)\n        return;\n\n    double dt_curr = delta_times.GetAverage();\n    double dt_ref_ms = PerfRefDt * 1000;\n    double dt_delta_ms = (dt_curr - PerfRefDt) * 1000;\n\n    const ImBuildInfo* build_info = ImBuildGetCompilationInfo();\n\n    // Display results\n    // FIXME-TESTS: Would be nice if we could submit a custom marker (e.g. branch/feature name)\n    LogInfo(\"[PERF] Conditions: Stress x%d, %s, %s, %s, %s, %s\",\n        PerfStressAmount, build_info->Type, build_info->Cpu, build_info->OS, build_info->Compiler, build_info->Date);\n    LogInfo(\"[PERF] Result: %+6.3f ms (from ref %+6.3f)\", dt_delta_ms, dt_ref_ms);\n\n    ImGuiPerfToolEntry entry;\n    entry.Timestamp = Engine->BatchStartTime;\n    entry.Category = category ? category : Test->Category;\n    entry.TestName = test_name ? test_name : Test->Name;\n    entry.DtDeltaMs = dt_delta_ms;\n    entry.PerfStressAmount = PerfStressAmount;\n    entry.GitBranchName = EngineIO->GitBranchName;\n    entry.BuildType = build_info->Type;\n    entry.Cpu = build_info->Cpu;\n    entry.OS = build_info->OS;\n    entry.Compiler = build_info->Compiler;\n    entry.Date = build_info->Date;\n    ImGuiTestEngine_PerfToolAppendToCSV(Engine->PerfTool, &entry, csv_file);\n\n    // Disable the \"Success\" message\n    RunFlags |= ImGuiTestRunFlags_NoSuccessMsg;\n}\n\n//-------------------------------------------------------------------------\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/source/imgui_te_coroutine.cpp",
    "content": "// dear imgui test engine\n// (coroutine interface + optional implementation)\n// Read https://github.com/ocornut/imgui_test_engine/wiki/Setting-Up\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#include \"imgui_te_coroutine.h\"\n#include \"imgui.h\"\n\n#ifdef _MSC_VER\n#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#endif\n\n//------------------------------------------------------------------------\n// Coroutine implementation using std::thread\n// This implements a coroutine using std::thread, with a helper thread for each coroutine (with serialised execution, so threads never actually run concurrently)\n//------------------------------------------------------------------------\n\n#if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL\n\n#include \"imgui_te_utils.h\"\n#include \"thirdparty/Str/Str.h\"\n#include <thread>\n#include <mutex>\n#include <condition_variable>\n\nstruct Coroutine_ImplStdThreadData\n{\n    std::thread*            Thread;                 // The thread this coroutine is using\n    std::condition_variable StateChange;            // Condition variable notified when the coroutine state changes\n    std::mutex              StateMutex;             // Mutex to protect coroutine state\n    bool                    CoroutineRunning;       // Is the coroutine currently running? Lock StateMutex before access and notify StateChange on change\n    bool                    CoroutineTerminated;    // Has the coroutine terminated? Lock StateMutex before access and notify StateChange on change\n    Str64                   Name;                   // The name of this coroutine\n};\n\n// The coroutine executing on the current thread (if it is a coroutine thread)\nstatic thread_local Coroutine_ImplStdThreadData* GThreadCoroutine = nullptr;\n\n// The main function for a coroutine thread\nstatic void CoroutineThreadMain(Coroutine_ImplStdThreadData* data, ImGuiTestCoroutineMainFunc func, void* ctx)\n{\n    // Set our thread name\n    ImThreadSetCurrentThreadDescription(data->Name.c_str());\n\n    // Set the thread coroutine\n    GThreadCoroutine = data;\n\n    // Wait for initial Run()\n    while (1)\n    {\n        std::unique_lock<std::mutex> lock(data->StateMutex);\n        if (data->CoroutineRunning)\n            break;\n        data->StateChange.wait(lock);\n    }\n\n    // Run user code, which will then call Yield() when it wants to yield control\n    func(ctx);\n\n    // Mark as terminated\n    {\n        std::lock_guard<std::mutex> lock(data->StateMutex);\n\n        data->CoroutineTerminated = true;\n        data->CoroutineRunning = false;\n        data->StateChange.notify_all();\n    }\n}\n\n\nstatic ImGuiTestCoroutineHandle Coroutine_ImplStdThread_Create(ImGuiTestCoroutineMainFunc* func, const char* name, void* ctx)\n{\n    Coroutine_ImplStdThreadData* data = new Coroutine_ImplStdThreadData();\n\n    data->Name = name;\n    data->CoroutineRunning = false;\n    data->CoroutineTerminated = false;\n    data->Thread = new std::thread(CoroutineThreadMain, data, func, ctx);\n\n    return (ImGuiTestCoroutineHandle)data;\n}\n\nstatic void Coroutine_ImplStdThread_Destroy(ImGuiTestCoroutineHandle handle)\n{\n    Coroutine_ImplStdThreadData* data = (Coroutine_ImplStdThreadData*)handle;\n\n    IM_ASSERT(data->CoroutineTerminated); // The coroutine needs to run to termination otherwise it may leak all sorts of things and this will deadlock\n    if (data->Thread)\n    {\n        data->Thread->join();\n\n        delete data->Thread;\n        data->Thread = nullptr;\n    }\n\n    delete data;\n    data = nullptr;\n}\n\n// Run the coroutine until the next call to Yield(). Returns TRUE if the coroutine yielded, FALSE if it terminated (or had previously terminated)\nstatic bool Coroutine_ImplStdThread_Run(ImGuiTestCoroutineHandle handle)\n{\n    Coroutine_ImplStdThreadData* data = (Coroutine_ImplStdThreadData*)handle;\n\n    // Wake up coroutine thread\n    {\n        std::lock_guard<std::mutex> lock(data->StateMutex);\n\n        if (data->CoroutineTerminated)\n            return false; // Coroutine has already finished\n\n        data->CoroutineRunning = true;\n        data->StateChange.notify_all();\n    }\n\n    // Wait for coroutine to stop\n    while (1)\n    {\n        std::unique_lock<std::mutex> lock(data->StateMutex);\n        if (!data->CoroutineRunning)\n        {\n            // Breakpoint here to catch the point where we return from the coroutine\n            if (data->CoroutineTerminated)\n                return false; // Coroutine finished\n            break;\n        }\n        data->StateChange.wait(lock);\n    }\n\n    return true;\n}\n\n// Yield the current coroutine (can only be called from a coroutine)\nstatic void Coroutine_ImplStdThread_Yield()\n{\n    IM_ASSERT(GThreadCoroutine); // This can only be called from a coroutine thread\n\n    Coroutine_ImplStdThreadData* data = GThreadCoroutine;\n\n    // Flag that we are not running any more\n    {\n        std::lock_guard<std::mutex> lock(data->StateMutex);\n        data->CoroutineRunning = false;\n        data->StateChange.notify_all();\n    }\n\n    // At this point the thread that called RunCoroutine() will leave the \"Wait for coroutine to stop\" loop\n    // Wait until we get started up again\n    while (1)\n    {\n        std::unique_lock<std::mutex> lock(data->StateMutex);\n        if (data->CoroutineRunning)\n            break; // Breakpoint here if you want to catch the point where execution of this coroutine resumes\n        data->StateChange.wait(lock);\n    }\n}\n\nImGuiTestCoroutineInterface* Coroutine_ImplStdThread_GetInterface()\n{\n    static ImGuiTestCoroutineInterface intf;\n    intf.CreateFunc = Coroutine_ImplStdThread_Create;\n    intf.DestroyFunc = Coroutine_ImplStdThread_Destroy;\n    intf.RunFunc = Coroutine_ImplStdThread_Run;\n    intf.YieldFunc = Coroutine_ImplStdThread_Yield;\n    return &intf;\n}\n\n#endif // #if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/source/imgui_te_engine.cpp",
    "content": "﻿// dear imgui test engine\n// (core)\n// This is the interface that your initial setup (app init, main loop) will mostly be using.\n// Actual tests will mostly use the interface of imgui_te_context.h\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#define IMGUI_DEFINE_MATH_OPERATORS\n#include \"imgui_te_engine.h\"\n#include \"imgui.h\"\n#include \"imgui_internal.h\"\n#include \"imgui_te_utils.h\"\n#include \"imgui_te_context.h\"\n#include \"imgui_te_internal.h\"\n#include \"imgui_te_perftool.h\"\n#include \"imgui_te_exporters.h\"\n#include \"thirdparty/Str/Str.h\"\n#if _WIN32\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>    // SetUnhandledExceptionFilter()\n#undef Yield            // Undo some of the damage done by <windows.h>\n#else\n#if !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n#include <signal.h>     // signal()\n#endif\n#include <unistd.h>     // sleep()\n#endif\n\n// Warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4127) // conditional expression is constant\n#endif\n\n/*\n\nIndex of this file:\n\n// [SECTION] TODO\n// [SECTION] FORWARD DECLARATIONS\n// [SECTION] DATA STRUCTURES\n// [SECTION] TEST ENGINE FUNCTIONS\n// [SECTION] CRASH HANDLING\n// [SECTION] HOOKS FOR CORE LIBRARY\n// [SECTION] CHECK/ERROR FUNCTIONS FOR TESTS\n// [SECTION] SETTINGS\n// [SECTION] ImGuiTestLog\n// [SECTION] ImGuiTest\n\n*/\n\n//-------------------------------------------------------------------------\n// [SECTION] DATA\n//-------------------------------------------------------------------------\n\nstatic ImGuiTestEngine* GImGuiTestEngine = nullptr;\n\n//-------------------------------------------------------------------------\n// [SECTION] FORWARD DECLARATIONS\n//-------------------------------------------------------------------------\n\n// Private functions\nstatic void ImGuiTestEngine_CoroutineStopAndJoin(ImGuiTestEngine* engine);\nstatic void ImGuiTestEngine_ClearInput(ImGuiTestEngine* engine);\nstatic void ImGuiTestEngine_ApplyInputToImGuiContext(ImGuiTestEngine* engine);\nstatic void ImGuiTestEngine_ProcessTestQueue(ImGuiTestEngine* engine);\nstatic void ImGuiTestEngine_ClearTests(ImGuiTestEngine* engine);\nstatic void ImGuiTestEngine_PreNewFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx);\nstatic void ImGuiTestEngine_PostNewFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx);\nstatic void ImGuiTestEngine_PreEndFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx);\nstatic void ImGuiTestEngine_PreRender(ImGuiTestEngine* engine, ImGuiContext* ui_ctx);\nstatic void ImGuiTestEngine_PostRender(ImGuiTestEngine* engine, ImGuiContext* ui_ctx);\nstatic void ImGuiTestEngine_UpdateHooks(ImGuiTestEngine* engine);\nstatic void ImGuiTestEngine_RunGuiFunc(ImGuiTestEngine* engine);\nstatic void ImGuiTestEngine_RunTestFunc(ImGuiTestEngine* engine);\nstatic void ImGuiTestEngine_ErrorRecoverySetup(ImGuiTestEngine* engine);\nstatic void ImGuiTestEngine_ErrorRecoveryRun(ImGuiTestEngine* engine);\nstatic void ImGuiTestEngine_TestQueueCoroutineMain(void* engine_opaque);\n\n// Settings\nstatic void* ImGuiTestEngine_SettingsReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);\nstatic void  ImGuiTestEngine_SettingsReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);\nstatic void  ImGuiTestEngine_SettingsWriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);\n\n//-------------------------------------------------------------------------\n// [SECTION] TEST ENGINE FUNCTIONS\n//-------------------------------------------------------------------------\n// Public\n// - ImGuiTestEngine_CreateContext()\n// - ImGuiTestEngine_DestroyContext()\n// - ImGuiTestEngine_BindImGuiContext()\n// - ImGuiTestEngine_UnbindImGuiContext()\n// - ImGuiTestEngine_GetIO()\n// - ImGuiTestEngine_Abort()\n// - ImGuiTestEngine_QueueAllTests()\n//-------------------------------------------------------------------------\n// - ImGuiTestEngine_FindItemInfo()\n// - ImGuiTestEngine_ClearTests()\n// - ImGuiTestEngine_ApplyInputToImGuiContext()\n// - ImGuiTestEngine_PreNewFrame()\n// - ImGuiTestEngine_PostNewFrame()\n// - ImGuiTestEngine_Yield()\n// - ImGuiTestEngine_ProcessTestQueue()\n// - ImGuiTestEngine_QueueTest()\n// - ImGuiTestEngine_RunTest()\n//-------------------------------------------------------------------------\n\nImGuiTestEngine::ImGuiTestEngine()\n{\n    PerfRefDeltaTime = 0.0f;\n    PerfDeltaTime100.Init(100);\n    PerfDeltaTime500.Init(500);\n    PerfTool = IM_NEW(ImGuiPerfTool);\n    UiFilterTests = IM_NEW(Str256); // We bite the bullet of adding an extra alloc/indirection in order to avoid including Str.h in our header\n    UiFilterPerfs = IM_NEW(Str256);\n\n    // Initialize std::thread based coroutine implementation if requested\n#if IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL\n    IM_ASSERT(IO.CoroutineFuncs == nullptr && \"IO.CoroutineFuncs already setup elsewhere!\");\n    IO.CoroutineFuncs = Coroutine_ImplStdThread_GetInterface();\n#endif\n}\n\nImGuiTestEngine::~ImGuiTestEngine()\n{\n    IM_ASSERT(TestQueueCoroutine == nullptr);\n    IM_DELETE(PerfTool);\n    IM_DELETE(UiFilterTests);\n    IM_DELETE(UiFilterPerfs);\n}\n\n// Using named functions here instead of lambda gives nicer call-stacks (mostly because we frequently step in PostNewFrame)\nstatic void ImGuiTestEngine_ShutdownHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook)      { ImGuiTestEngine_UnbindImGuiContext((ImGuiTestEngine*)hook->UserData, ui_ctx); }\nstatic void ImGuiTestEngine_PreNewFrameHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook)   { ImGuiTestEngine_PreNewFrame((ImGuiTestEngine*)hook->UserData, ui_ctx); }\nstatic void ImGuiTestEngine_PostNewFrameHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook)  { ImGuiTestEngine_PostNewFrame((ImGuiTestEngine*)hook->UserData, ui_ctx); }\nstatic void ImGuiTestEngine_PreEndFrameHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook)   { ImGuiTestEngine_PreEndFrame((ImGuiTestEngine*)hook->UserData, ui_ctx); }\nstatic void ImGuiTestEngine_PreRenderHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook)     { ImGuiTestEngine_PreRender((ImGuiTestEngine*)hook->UserData, ui_ctx); }\nstatic void ImGuiTestEngine_PostRenderHook(ImGuiContext* ui_ctx, ImGuiContextHook* hook)    { ImGuiTestEngine_PostRender((ImGuiTestEngine*)hook->UserData, ui_ctx); }\n\nvoid ImGuiTestEngine_BindImGuiContext(ImGuiTestEngine* engine, ImGuiContext* ui_ctx)\n{\n    IM_ASSERT(engine->UiContextTarget == ui_ctx);\n\n    // Add .ini handle for ImGuiWindow type\n    if (engine->IO.ConfigSavedSettings)\n    {\n        ImGuiSettingsHandler ini_handler;\n        ini_handler.TypeName = \"TestEngine\";\n        ini_handler.TypeHash = ImHashStr(\"TestEngine\");\n        ini_handler.ReadOpenFn = ImGuiTestEngine_SettingsReadOpen;\n        ini_handler.ReadLineFn = ImGuiTestEngine_SettingsReadLine;\n        ini_handler.WriteAllFn = ImGuiTestEngine_SettingsWriteAll;\n        ui_ctx->SettingsHandlers.push_back(ini_handler);\n        engine->PerfTool->_AddSettingsHandler();\n    }\n\n    // Install generic context hooks facility\n    ImGuiContextHook hook;\n    hook.Type = ImGuiContextHookType_Shutdown;\n    hook.Callback = ImGuiTestEngine_ShutdownHook;\n    hook.UserData = (void*)engine;\n    ImGui::AddContextHook(ui_ctx, &hook);\n\n    hook.Type = ImGuiContextHookType_NewFramePre;\n    hook.Callback = ImGuiTestEngine_PreNewFrameHook;\n    hook.UserData = (void*)engine;\n    ImGui::AddContextHook(ui_ctx, &hook);\n\n    hook.Type = ImGuiContextHookType_NewFramePost;\n    hook.Callback = ImGuiTestEngine_PostNewFrameHook;\n    hook.UserData = (void*)engine;\n    ImGui::AddContextHook(ui_ctx, &hook);\n\n    hook.Type = ImGuiContextHookType_EndFramePre;\n    hook.Callback = ImGuiTestEngine_PreEndFrameHook;\n    hook.UserData = (void*)engine;\n    ImGui::AddContextHook(ui_ctx, &hook);\n\n    hook.Type = ImGuiContextHookType_RenderPre;\n    hook.Callback = ImGuiTestEngine_PreRenderHook;\n    hook.UserData = (void*)engine;\n    ImGui::AddContextHook(ui_ctx, &hook);\n\n    hook.Type = ImGuiContextHookType_RenderPost;\n    hook.Callback = ImGuiTestEngine_PostRenderHook;\n    hook.UserData = (void*)engine;\n    ImGui::AddContextHook(ui_ctx, &hook);\n\n    // Install custom test engine hook data\n    if (GImGuiTestEngine == nullptr)\n        GImGuiTestEngine = engine;\n    IM_ASSERT(ui_ctx->TestEngine == nullptr);\n    ui_ctx->TestEngine = engine;\n    engine->UiContextHasHooks = false;\n}\n\nvoid    ImGuiTestEngine_UnbindImGuiContext(ImGuiTestEngine* engine, ImGuiContext* ui_ctx)\n{\n    IM_ASSERT(engine->UiContextTarget == ui_ctx);\n\n    // FIXME: Could use ImGui::RemoveContextHook() if we stored our hook ids\n    for (int hook_n = 0; hook_n < ui_ctx->Hooks.Size; hook_n++)\n        if (ui_ctx->Hooks[hook_n].UserData == engine)\n            ImGui::RemoveContextHook(ui_ctx, ui_ctx->Hooks[hook_n].HookId);\n\n    ImGuiTestEngine_CoroutineStopAndJoin(engine);\n\n    IM_ASSERT(ui_ctx->TestEngine == engine);\n    ui_ctx->TestEngine = nullptr;\n\n    // Remove .ini handler\n    IM_ASSERT(GImGui == ui_ctx);\n    if (engine->IO.ConfigSavedSettings)\n    {\n        ImGui::RemoveSettingsHandler(\"TestEngine\");\n        ImGui::RemoveSettingsHandler(\"TestEnginePerfTool\");\n    }\n\n    // Remove hook\n    if (GImGuiTestEngine == engine)\n        GImGuiTestEngine = nullptr;\n    engine->UiContextTarget = engine->UiContextActive = nullptr;\n}\n\n// Create test context (not bound to any dear imgui context yet)\nImGuiTestEngine*    ImGuiTestEngine_CreateContext()\n{\n    IMGUI_CHECKVERSION(); // <--- If you get a crash here: mismatching config, check that both imgui and imgui_test_engine are using same defines (e.g. using the same imconfig file)\n    ImGuiTestEngine* engine = IM_NEW(ImGuiTestEngine)();\n    return engine;\n}\n\nvoid    ImGuiTestEngine_DestroyContext(ImGuiTestEngine* engine)\n{\n    // We require user to call DestroyContext() before ImGuiTestEngine_DestroyContext() in order to preserve ini data...\n    // In case of e.g. dynamically creating a TestEngine as runtime and not caring about its settings, you may set io.ConfigSavedSettings to false\n    // in order to allow earlier destruction of the context.\n    if (engine->IO.ConfigSavedSettings)\n        IM_ASSERT(engine->UiContextTarget == nullptr && \"You need to call ImGui::DestroyContext() BEFORE ImGuiTestEngine_DestroyContext()\");\n\n    // Shutdown coroutine\n    ImGuiTestEngine_CoroutineStopAndJoin(engine);\n    if (engine->UiContextTarget != nullptr)\n        ImGuiTestEngine_UnbindImGuiContext(engine, engine->UiContextTarget);\n\n    ImGuiTestEngine_ClearTests(engine);\n\n    for (int n = 0; n < engine->InfoTasks.Size; n++)\n        IM_DELETE(engine->InfoTasks[n]);\n    engine->InfoTasks.clear();\n\n    IM_DELETE(engine);\n\n    // Release hook\n    if (GImGuiTestEngine == engine)\n        GImGuiTestEngine = nullptr;\n}\n\nvoid    ImGuiTestEngine_Start(ImGuiTestEngine* engine, ImGuiContext* ui_ctx)\n{\n    IM_ASSERT(engine->Started == false);\n    IM_ASSERT(engine->UiContextTarget == nullptr);\n\n    engine->UiContextTarget = ui_ctx;\n    ImGuiTestEngine_BindImGuiContext(engine, engine->UiContextTarget);\n\n    // Create our coroutine\n    // (we include the word \"Main\" in the name to facilitate filtering for both this thread and the \"Main Thread\" in debuggers)\n    if (!engine->TestQueueCoroutine)\n    {\n        IM_ASSERT(engine->IO.CoroutineFuncs && \"Missing CoroutineFuncs! Use '#define IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL 1' or define your own implementation!\");\n        engine->TestQueueCoroutine = engine->IO.CoroutineFuncs->CreateFunc(ImGuiTestEngine_TestQueueCoroutineMain, \"Main Dear ImGui Test Thread\", engine);\n    }\n    engine->TestQueueCoroutineShouldExit = false;\n    engine->Started = true;\n}\n\nvoid    ImGuiTestEngine_Stop(ImGuiTestEngine* engine)\n{\n    IM_ASSERT(engine->Started);\n    IM_ASSERT(engine->UiContextTarget != NULL);\n\n    engine->Abort = true;\n    ImGuiTestEngine_CoroutineStopAndJoin(engine);\n    //ImGuiTestEngine_UnbindImGuiContext(engine, engine->UiContextTarget);\n    ImGuiTestEngine_Export(engine);\n    engine->Started = false;\n}\n\nstatic void    ImGuiTestEngine_CoroutineStopRequest(ImGuiTestEngine* engine)\n{\n    if (engine->TestQueueCoroutine != nullptr)\n        engine->TestQueueCoroutineShouldExit = true;\n}\n\nstatic void    ImGuiTestEngine_CoroutineStopAndJoin(ImGuiTestEngine* engine)\n{\n    if (engine->TestQueueCoroutine != nullptr)\n    {\n        // Run until the coroutine exits\n        engine->TestQueueCoroutineShouldExit = true;\n        while (true)\n        {\n            if (!engine->IO.CoroutineFuncs->RunFunc(engine->TestQueueCoroutine))\n                break;\n        }\n        engine->IO.CoroutineFuncs->DestroyFunc(engine->TestQueueCoroutine);\n        engine->TestQueueCoroutine = nullptr;\n    }\n}\n\n// [EXPERIMENTAL] Destroy and recreate ImGui context\n// This potentially allow us to test issues related to handling new windows, restoring settings etc.\n// This also gets us once inch closer to more dynamic management of context (e.g. jail tests in their own context)\n// FIXME: This is currently called by ImGuiTestEngine_PreNewFrame() in hook but may end up needing to be called\n// by main application loop in order to facilitate letting app know of the new pointers. For now none of our backends\n// preserve the pointer so may be fine.\nvoid    ImGuiTestEngine_RebootUiContext(ImGuiTestEngine* engine)\n{\n    IM_ASSERT(engine->Started);\n    ImGuiContext* ctx = engine->UiContextTarget;\n    ImGuiTestEngine_Stop(engine);\n    ImGuiTestEngine_UnbindImGuiContext(engine, ctx);\n\n    // Backup\n#ifdef IMGUI_HAS_TEXTURES\n    ImGuiContext* backup_atlas_owner = ctx->IO.Fonts->OwnerContext;\n#else\n    bool backup_atlas_owned_by_context = ctx->FontAtlasOwnedByContext;\n#endif\n    ImFontAtlas* backup_atlas = ctx->IO.Fonts;\n    ImGuiIO backup_io = ctx->IO;\n#ifdef IMGUI_HAS_VIEWPORT\n    // FIXME: Break with multi-viewports as we don't preserve user windowing data properly.\n    // Backend tend to store e.g. HWND data in viewport 0.\n    if (ctx->IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)\n        IM_ASSERT(0);\n    //ImGuiViewport backup_viewport0 = *(ImGuiViewport*)ctx->Viewports[0];\n    //ImGuiPlatformIO backup_platform_io = ctx->PlatformIO;\n    //ImGui::DestroyPlatformWindows();\n#endif\n\n    // Recreate\n#ifdef IMGUI_HAS_TEXTURES\n    ctx->IO.Fonts->OwnerContext = backup_atlas_owner;\n#else\n    ctx->FontAtlasOwnedByContext = false;\n#endif\n#if 1\n    ImGui::DestroyContext();\n    ImGui::CreateContext(backup_atlas);\n#else\n    // Preserve same context pointer, which is probably misleading and not even necessary.\n    ImGui::Shutdown(ctx);\n    ctx->~ImGuiContext();\n    IM_PLACEMENT_NEW(ctx) ImGuiContext(backup_atlas);\n    ImGui::Initialize(ctx);\n#endif\n\n    // Restore\n#ifdef IMGUI_HAS_TEXTURES\n    ctx->IO.Fonts->OwnerContext = ctx;\n#else\n    ctx->FontAtlasOwnedByContext = backup_atlas_owned_by_context;\n#endif\n    ctx->IO = backup_io;\n#ifdef IMGUI_HAS_VIEWPORT\n    //backup_platform_io.Viewports.swap(ctx->PlatformIO.Viewports);\n    //ctx->PlatformIO = backup_platform_io;\n    //ctx->Viewports[0]->RendererUserData = backup_viewport0.RendererUserData;\n    //ctx->Viewports[0]->PlatformUserData = backup_viewport0.PlatformUserData;\n    //ctx->Viewports[0]->PlatformHandle = backup_viewport0.PlatformHandle;\n    //ctx->Viewports[0]->PlatformHandleRaw = backup_viewport0.PlatformHandleRaw;\n    //memset(&backup_viewport0, 0, sizeof(backup_viewport0));\n#endif\n\n    ImGuiTestEngine_Start(engine, ctx);\n}\n\nvoid    ImGuiTestEngine_PostSwap(ImGuiTestEngine* engine)\n{\n    engine->PostSwapCalled = true;\n\n    if (engine->IO.ConfigFixedDeltaTime != 0.0f)\n        ImGuiTestEngine_SetDeltaTime(engine, engine->IO.ConfigFixedDeltaTime);\n\n    // Sync capture tool configurations from engine IO.\n    engine->CaptureContext.ScreenCaptureFunc = engine->IO.ScreenCaptureFunc;\n    engine->CaptureContext.ScreenCaptureUserData = engine->IO.ScreenCaptureUserData;\n    engine->CaptureContext.VideoCaptureEncoderPath = engine->IO.VideoCaptureEncoderPath;\n    engine->CaptureContext.VideoCaptureEncoderPathSize = IM_ARRAYSIZE(engine->IO.VideoCaptureEncoderPath);\n    engine->CaptureContext.VideoCaptureEncoderParams = engine->IO.VideoCaptureEncoderParams;\n    engine->CaptureContext.VideoCaptureEncoderParamsSize = IM_ARRAYSIZE(engine->IO.VideoCaptureEncoderParams);\n    engine->CaptureContext.GifCaptureEncoderParams = engine->IO.GifCaptureEncoderParams;\n    engine->CaptureContext.GifCaptureEncoderParamsSize = IM_ARRAYSIZE(engine->IO.GifCaptureEncoderParams);\n    engine->CaptureTool.VideoCaptureExtension = engine->IO.VideoCaptureExtension;\n    engine->CaptureTool.VideoCaptureExtensionSize = IM_ARRAYSIZE(engine->IO.VideoCaptureExtension);\n\n    // Capture a screenshot from main thread while coroutine waits\n    if (engine->CaptureCurrentArgs != nullptr)\n    {\n        ImGuiCaptureStatus status = engine->CaptureContext.CaptureUpdate(engine->CaptureCurrentArgs);\n        if (status != ImGuiCaptureStatus_InProgress)\n        {\n            if (status == ImGuiCaptureStatus_Done)\n                ImStrncpy(engine->CaptureTool.OutputLastFilename, engine->CaptureCurrentArgs->InOutputFile, IM_ARRAYSIZE(engine->CaptureTool.OutputLastFilename));\n            engine->CaptureCurrentArgs = nullptr;\n        }\n    }\n}\n\nImGuiTestEngineIO&  ImGuiTestEngine_GetIO(ImGuiTestEngine* engine)\n{\n    return engine->IO;\n}\n\nvoid    ImGuiTestEngine_AbortCurrentTest(ImGuiTestEngine* engine)\n{\n    engine->Abort = true;\n    if (ImGuiTestContext* test_context = engine->TestContext)\n        test_context->Abort = true;\n}\n\nbool    ImGuiTestEngine_TryAbortEngine(ImGuiTestEngine* engine)\n{\n    ImGuiTestEngine_AbortCurrentTest(engine);\n    ImGuiTestEngine_CoroutineStopRequest(engine);\n    if (ImGuiTestEngine_IsTestQueueEmpty(engine))\n        return true;\n    return false; // Still running coroutine\n}\n\n// FIXME-OPT\nImGuiTest* ImGuiTestEngine_FindTestByName(ImGuiTestEngine* engine, const char* category, const char* name)\n{\n    IM_ASSERT(category != nullptr || name != nullptr);\n    for (int n = 0; n < engine->TestsAll.Size; n++)\n    {\n        ImGuiTest* test = engine->TestsAll[n];\n        if (name != nullptr && strcmp(test->Name, name) != 0)\n            continue;\n        if (category != nullptr && strcmp(test->Category, category) != 0)\n            continue;\n        return test;\n    }\n    return nullptr;\n}\n\n// FIXME-OPT\nstatic ImGuiTestInfoTask* ImGuiTestEngine_FindInfoTask(ImGuiTestEngine* engine, ImGuiID id)\n{\n    for (int task_n = 0; task_n < engine->InfoTasks.Size; task_n++)\n    {\n        ImGuiTestInfoTask* task = engine->InfoTasks[task_n];\n        if (task->ID == id)\n            return task;\n    }\n    return nullptr;\n}\n\n// Request information about one item.\n// Will push a request for the test engine to process.\n// Will return nullptr when results are not ready (or not available).\nImGuiTestItemInfo* ImGuiTestEngine_FindItemInfo(ImGuiTestEngine* engine, ImGuiID id, const char* debug_id)\n{\n    IM_ASSERT(id != 0);\n\n    if (ImGuiTestInfoTask* task = ImGuiTestEngine_FindInfoTask(engine, id))\n    {\n        if (task->Result.TimestampMain + 2 >= engine->FrameCount)\n        {\n            task->FrameCount = engine->FrameCount; // Renew task\n            return &task->Result;\n        }\n        return nullptr;\n    }\n\n    // Create task\n    ImGuiTestInfoTask* task = IM_NEW(ImGuiTestInfoTask)();\n    task->ID = id;\n    task->FrameCount = engine->FrameCount;\n    if (debug_id)\n    {\n        size_t debug_id_sz = strlen(debug_id);\n        if (debug_id_sz < IM_ARRAYSIZE(task->DebugName) - 1)\n        {\n            memcpy(task->DebugName, debug_id, debug_id_sz + 1);\n        }\n        else\n        {\n            size_t header_sz = (size_t)(IM_ARRAYSIZE(task->DebugName) * 0.30f);\n            size_t footer_sz = IM_ARRAYSIZE(task->DebugName) - 2 - header_sz;\n            IM_ASSERT(header_sz > 0 && footer_sz > 0);\n            ImFormatString(task->DebugName, IM_ARRAYSIZE(task->DebugName), \"%.*s..%.*s\", (int)header_sz, debug_id, (int)footer_sz, debug_id + debug_id_sz - footer_sz);\n        }\n    }\n    engine->InfoTasks.push_back(task);\n\n    return nullptr;\n}\n\nstatic void ImGuiTestEngine_ClearTests(ImGuiTestEngine* engine)\n{\n    for (int n = 0; n < engine->TestsAll.Size; n++)\n        IM_DELETE(engine->TestsAll[n]);\n    engine->TestsAll.clear();\n    engine->TestsQueue.clear();\n}\n\n// Called at the beginning of a test to ensure no previous inputs leak into the new test\n// FIXME-TESTS: Would make sense to reset mouse position as well?\nvoid ImGuiTestEngine_ClearInput(ImGuiTestEngine* engine)\n{\n    IM_ASSERT(engine->UiContextTarget != nullptr);\n    ImGuiContext& g = *engine->UiContextTarget;\n\n    engine->Inputs.MouseButtonsValue = 0;\n    engine->Inputs.Queue.clear();\n    engine->Inputs.MouseWheel = ImVec2(0, 0);\n\n    // FIXME: Necessary?\n#if IMGUI_VERSION_NUM >= 18972\n    g.IO.ClearEventsQueue();\n#else\n    g.InputEventsQueue.resize(0);\n    g.IO.ClearInputCharacters();\n#endif\n    g.IO.ClearInputKeys();\n\n    ImGuiTestEngine_ApplyInputToImGuiContext(engine);\n}\n\nbool ImGuiTestEngine_IsUsingSimulatedInputs(ImGuiTestEngine* engine)\n{\n    if (engine->UiContextActive)\n        if (!ImGuiTestEngine_IsTestQueueEmpty(engine))\n            if (!(engine->TestContext->RunFlags & ImGuiTestRunFlags_GuiFuncOnly))\n                return true;\n    return false;\n}\n\n// Setup inputs in the tested Dear ImGui context. Essentially we override the work of the backend here.\nvoid ImGuiTestEngine_ApplyInputToImGuiContext(ImGuiTestEngine* engine)\n{\n    IM_ASSERT(engine->UiContextTarget != nullptr);\n    ImGuiContext& g = *engine->UiContextTarget;\n    ImGuiIO& io = g.IO;\n\n    const bool use_simulated_inputs = ImGuiTestEngine_IsUsingSimulatedInputs(engine);\n    if (!use_simulated_inputs)\n        return;\n\n    // Erase events submitted by backend\n    for (int n = 0; n < g.InputEventsQueue.Size; n++)\n        if (g.InputEventsQueue[n].AddedByTestEngine == false)\n            g.InputEventsQueue.erase(&g.InputEventsQueue[n--]);\n\n    // To support using ImGuiKey_NavXXXX shortcuts pointing to gamepad actions\n    // FIXME-TEST-ENGINE: Should restore\n    g.IO.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;\n    g.IO.BackendFlags |= ImGuiBackendFlags_HasGamepad;\n\n    // Special flags to stop submitting events\n    if (engine->TestContext->RunFlags & ImGuiTestRunFlags_EnableRawInputs)\n        return;\n\n    const int input_event_count_prev = g.InputEventsQueue.Size;\n\n    // Apply mouse viewport\n#ifdef IMGUI_HAS_VIEWPORT\n    ImGuiPlatformIO& platform_io = g.PlatformIO;\n    ImGuiViewport* mouse_hovered_viewport;\n    if (engine->Inputs.MouseHoveredViewport != 0)\n        mouse_hovered_viewport = ImGui::FindViewportByID(engine->Inputs.MouseHoveredViewport); // Common case\n    else\n        mouse_hovered_viewport = ImGui::FindHoveredViewportFromPlatformWindowStack(engine->Inputs.MousePosValue); // Rarely used, some tests rely on this (e.g. \"docking_dockspace_passthru_hover\") may make it a opt-in feature instead?\n    if (mouse_hovered_viewport && (mouse_hovered_viewport->Flags & ImGuiViewportFlags_NoInputs))\n        mouse_hovered_viewport = nullptr;\n    //if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)\n        if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)\n            io.AddMouseViewportEvent(mouse_hovered_viewport ? mouse_hovered_viewport->ID : 0);\n    bool mouse_hovered_viewport_focused = mouse_hovered_viewport && (mouse_hovered_viewport->Flags & ImGuiViewportFlags_IsFocused) != 0;\n#endif\n\n    // Apply mouse\n    io.AddMousePosEvent(engine->Inputs.MousePosValue.x, engine->Inputs.MousePosValue.y);\n    for (int n = 0; n < ImGuiMouseButton_COUNT; n++)\n    {\n        bool down = (engine->Inputs.MouseButtonsValue & (1 << n)) != 0;\n        io.AddMouseButtonEvent(n, down);\n\n        // A click simulate platform focus on the viewport.\n#ifdef IMGUI_HAS_VIEWPORT\n        if (down && mouse_hovered_viewport && !mouse_hovered_viewport_focused)\n            if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)\n            {\n                mouse_hovered_viewport_focused = true;\n                engine->Inputs.Queue.push_back(ImGuiTestInput::ForViewportFocus(mouse_hovered_viewport->ID));\n            }\n#endif\n    }\n\n    // Apply mouse wheel\n    // [OSX] Simulate OSX behavior of automatically swapping mouse wheel axis when SHIFT is held.\n    // This is working in conjonction with the fact that ImGuiTestContext::MouseWheel() assume Windows-style behavior.\n    ImVec2 wheel = engine->Inputs.MouseWheel;\n    if (io.ConfigMacOSXBehaviors && (io.KeyMods & ImGuiMod_Shift)) // FIXME!!\n        ImSwap(wheel.x, wheel.y);\n    if (wheel.x != 0.0f || wheel.y != 0.0f)\n        io.AddMouseWheelEvent(wheel.x, wheel.y);\n    engine->Inputs.MouseWheel = ImVec2(0, 0);\n\n    // Process input requests/queues\n    if (engine->Inputs.Queue.Size > 0)\n    {\n        for (int n = 0; n < engine->Inputs.Queue.Size; n++)\n        {\n            const ImGuiTestInput& input = engine->Inputs.Queue[n];\n            switch (input.Type)\n            {\n            case ImGuiTestInputType_Key:\n            {\n                ImGuiKeyChord key_chord = input.KeyChord;\n#if IMGUI_VERSION_NUM >= 19016 && IMGUI_VERSION_NUM < 19063\n                key_chord = ImGui::FixupKeyChord(&g, key_chord); // This will add ImGuiMod_Alt when pressing ImGuiKey_LeftAlt or ImGuiKey_LeftRight\n#endif\n#if IMGUI_VERSION_NUM >= 19063\n                key_chord = ImGui::FixupKeyChord(key_chord);     // This will add ImGuiMod_Alt when pressing ImGuiKey_LeftAlt or ImGuiKey_LeftRight\n#endif\n                ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);\n                ImGuiKeyChord mods = (key_chord & ImGuiMod_Mask_);\n                if (mods != 0x00)\n                {\n                    // OSX conversion\n#if IMGUI_VERSION_NUM >= 18912 && IMGUI_VERSION_NUM < 19063\n                    if (mods & ImGuiMod_Shortcut)\n                        mods = (mods & ~ImGuiMod_Shortcut) | (g.IO.ConfigMacOSXBehaviors ? ImGuiMod_Super : ImGuiMod_Ctrl);\n#endif\n#if IMGUI_VERSION_NUM >= 19063\n                    // MacOS: swap Cmd(Super) and Ctrl WILL BE SWAPPED BACK BY io.AddKeyEvent()\n                    if (g.IO.ConfigMacOSXBehaviors)\n                    {\n                        if ((mods & (ImGuiMod_Ctrl | ImGuiMod_Super)) == ImGuiMod_Super)\n                            mods = (mods & ~ImGuiMod_Super) | ImGuiMod_Ctrl;\n                        else if ((mods & (ImGuiMod_Ctrl | ImGuiMod_Super)) == ImGuiMod_Ctrl)\n                            mods = (mods & ~ImGuiMod_Ctrl) | ImGuiMod_Super;\n                        if (key == ImGuiKey_LeftSuper)      { key = ImGuiKey_LeftCtrl; }\n                        else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_RightCtrl; }\n                        else if (key == ImGuiKey_LeftCtrl)  { key = ImGuiKey_LeftSuper; }\n                        else if (key == ImGuiKey_LeftCtrl)  { key = ImGuiKey_RightSuper; }\n                    }\n#endif\n                    // Submitting a ImGuiMod_XXX without associated key needs to add at least one of the key.\n                    if (mods & ImGuiMod_Ctrl)\n                    {\n                        io.AddKeyEvent(ImGuiMod_Ctrl, input.Down);\n                        if (key != ImGuiKey_LeftCtrl && key != ImGuiKey_RightCtrl)\n                            io.AddKeyEvent(ImGuiKey_LeftCtrl, input.Down);\n                    }\n                    if (mods & ImGuiMod_Shift)\n                    {\n                        io.AddKeyEvent(ImGuiMod_Shift, input.Down);\n                        if (key != ImGuiKey_LeftShift && key != ImGuiKey_RightShift)\n                            io.AddKeyEvent(ImGuiKey_LeftShift, input.Down);\n                    }\n                    if (mods & ImGuiMod_Alt)\n                    {\n                        io.AddKeyEvent(ImGuiMod_Alt, input.Down);\n                        if (key != ImGuiKey_LeftAlt && key != ImGuiKey_RightAlt)\n                            io.AddKeyEvent(ImGuiKey_LeftAlt, input.Down);\n                    }\n                    if (mods & ImGuiMod_Super)\n                    {\n                        io.AddKeyEvent(ImGuiMod_Super, input.Down);\n                        if (key != ImGuiKey_LeftSuper && key != ImGuiKey_RightSuper)\n                            io.AddKeyEvent(ImGuiKey_LeftSuper, input.Down);\n                    }\n                }\n\n                if (key != ImGuiKey_None)\n                    io.AddKeyEvent(key, input.Down);\n                break;\n            }\n            case ImGuiTestInputType_Char:\n            {\n                IM_ASSERT(input.Char != 0);\n                io.AddInputCharacter(input.Char);\n                break;\n            }\n#ifdef IMGUI_HAS_VIEWPORT\n            case ImGuiTestInputType_ViewportFocus:\n            {\n                if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0)\n                    break;\n                IM_ASSERT(engine->TestContext != nullptr);\n                ImGuiViewport* viewport = ImGui::FindViewportByID(input.ViewportId);\n                if (viewport == nullptr)\n                    engine->TestContext->LogError(\"ViewportPlatform_SetWindowFocus(%08X): cannot find viewport anymore!\", input.ViewportId);\n                else if (platform_io.Platform_SetWindowFocus == nullptr)\n                    engine->TestContext->LogError(\"ViewportPlatform_SetWindowFocus(%08X): backend's Platform_SetWindowFocus() is not set\", input.ViewportId);\n                else\n                    platform_io.Platform_SetWindowFocus(viewport);\n                break;\n            }\n            case ImGuiTestInputType_ViewportSetPos:\n            {\n                if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0)\n                    break;\n                IM_ASSERT(engine->TestContext != nullptr);\n                ImGuiViewport* viewport = ImGui::FindViewportByID(input.ViewportId);\n                if (viewport == nullptr)\n                    engine->TestContext->LogError(\"ViewportPlatform_SetWindowPos(%08X): cannot find viewport anymore!\", input.ViewportId);\n                else if (platform_io.Platform_SetWindowPos == nullptr)\n                    engine->TestContext->LogError(\"ViewportPlatform_SetWindowPos(%08X): backend's Platform_SetWindowPos() is not set\", input.ViewportId);\n                else\n                    platform_io.Platform_SetWindowPos(viewport, input.ViewportPosSize);\n                break;\n            }\n            case ImGuiTestInputType_ViewportSetSize:\n            {\n                if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0)\n                    break;\n                IM_ASSERT(engine->TestContext != nullptr);\n                ImGuiViewport* viewport = ImGui::FindViewportByID(input.ViewportId);\n                if (viewport == nullptr)\n                    engine->TestContext->LogError(\"ViewportPlatform_SetWindowSize(%08X): cannot find viewport anymore!\", input.ViewportId);\n                else if (platform_io.Platform_SetWindowPos == nullptr)\n                    engine->TestContext->LogError(\"ViewportPlatform_SetWindowSize(%08X): backend's Platform_SetWindowSize() is not set\", input.ViewportId);\n                else\n                    platform_io.Platform_SetWindowSize(viewport, input.ViewportPosSize);\n                break;\n            }\n            case ImGuiTestInputType_ViewportClose:\n            {\n                if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0)\n                    break;\n                IM_ASSERT(engine->TestContext != nullptr);\n                ImGuiViewport* viewport = ImGui::FindViewportByID(input.ViewportId);\n                if (viewport == nullptr)\n                    engine->TestContext->LogError(\"ViewportPlatform_CloseWindow(%08X): cannot find viewport anymore!\", input.ViewportId);\n                else\n                    viewport->PlatformRequestClose = true;\n                // FIXME: doesn't apply to actual backend\n                break;\n            }\n#else\n            case ImGuiTestInputType_ViewportFocus:\n            case ImGuiTestInputType_ViewportSetPos:\n            case ImGuiTestInputType_ViewportSetSize:\n            case ImGuiTestInputType_ViewportClose:\n                break;\n#endif\n            case ImGuiTestInputType_None:\n            default:\n                break;\n            }\n        }\n\n        engine->Inputs.Queue.resize(0);\n    }\n\n    const int input_event_count_curr = g.InputEventsQueue.Size;\n    for (int n = input_event_count_prev; n < input_event_count_curr; n++)\n        g.InputEventsQueue[n].AddedByTestEngine = true;\n}\n\n// FIXME: Trying to abort a running GUI test won't kill the app immediately.\nstatic void ImGuiTestEngine_UpdateWatchdog(ImGuiTestEngine* engine, ImGuiContext* ui_ctx, double t0, double t1)\n{\n    IM_UNUSED(ui_ctx);\n    ImGuiTestContext* test_ctx = engine->TestContext;\n\n    if (engine->IO.ConfigRunSpeed != ImGuiTestRunSpeed_Fast || ImOsIsDebuggerPresent())\n        return;\n\n    if (test_ctx->RunFlags & ImGuiTestRunFlags_RunFromGui)\n        return;\n\n    const float timer_warn = engine->IO.ConfigWatchdogWarning;\n    const float timer_kill_test = engine->IO.ConfigWatchdogKillTest;\n    const float timer_kill_app = engine->IO.ConfigWatchdogKillApp;\n\n    // Emit a warning and then fail the test after a given time.\n    if (t0 < timer_warn && t1 >= timer_warn)\n    {\n        test_ctx->LogWarning(\"[Watchdog] Running time for '%s' is >%.f seconds, may be excessive.\", test_ctx->Test->Name, timer_warn);\n    }\n    if (t0 < timer_kill_test && t1 >= timer_kill_test)\n    {\n        test_ctx->LogError(\"[Watchdog] Running time for '%s' is >%.f seconds, aborting.\", test_ctx->Test->Name, timer_kill_test);\n        IM_CHECK(false);\n    }\n\n    // Final safety watchdog in case the TestFunc is calling Yield() but never returning.\n    // Note that we are not catching infinite loop cases where the TestFunc may be running but not yielding..\n    if (t0 < timer_kill_app + 5.0f && t1 >= timer_kill_app + 5.0f)\n    {\n        test_ctx->LogError(\"[Watchdog] Emergency process exit as the test didn't return.\");\n        exit(1);\n    }\n}\n\nstatic void ImGuiTestEngine_PreNewFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx)\n{\n    if (engine->UiContextTarget != ui_ctx)\n        return;\n    IM_ASSERT(ui_ctx == GImGui);\n    ImGuiContext& g = *ui_ctx;\n\n    engine->CaptureContext.PreNewFrame();\n\n    if (engine->ToolDebugRebootUiContext)\n    {\n        ImGuiTestEngine_RebootUiContext(engine);\n        ui_ctx = engine->UiContextTarget;\n        engine->ToolDebugRebootUiContext = false;\n    }\n\n    // Inject extra time into the Dear ImGui context\n    if (engine->OverrideDeltaTime >= 0.0f)\n    {\n        ui_ctx->IO.DeltaTime = engine->OverrideDeltaTime;\n        engine->OverrideDeltaTime = -1.0f;\n    }\n\n    // NewFrame() will increase this so we are +1 ahead at the time of calling this\n    engine->FrameCount = g.FrameCount + 1;\n    if (ImGuiTestContext* test_ctx = engine->TestContext)\n    {\n        double t0 = test_ctx->RunningTime;\n        double t1 = t0 + ui_ctx->IO.DeltaTime;\n        test_ctx->FrameCount++;\n        test_ctx->RunningTime = t1;\n        ImGuiTestEngine_UpdateWatchdog(engine, ui_ctx, t0, t1);\n    }\n\n    engine->PerfDeltaTime100.AddSample(g.IO.DeltaTime);\n    engine->PerfDeltaTime500.AddSample(g.IO.DeltaTime);\n\n    if (!ImGuiTestEngine_IsTestQueueEmpty(engine) && !engine->Abort)\n    {\n        // Abort testing by holding ESC\n        // When running GuiFunc only main_io == simulated_io we test for a long hold.\n        ImGuiIO& main_io = g.IO;\n        for (auto& e : g.InputEventsQueue)\n            if (e.Type == ImGuiInputEventType_Key && e.Key.Key == ImGuiKey_Escape)\n                engine->Inputs.HostEscDown = e.Key.Down;\n        engine->Inputs.HostEscDownDuration = engine->Inputs.HostEscDown ? (ImMax(engine->Inputs.HostEscDownDuration, 0.0f) + main_io.DeltaTime) : -1.0f;\n        const bool abort = engine->Inputs.HostEscDownDuration >= 0.20f;\n        if (abort)\n        {\n            if (engine->TestContext)\n                engine->TestContext->LogWarning(\"User aborted (pressed ESC)\");\n            ImGuiTestEngine_AbortCurrentTest(engine);\n        }\n    }\n    else\n    {\n        engine->Inputs.HostEscDown = false;\n        engine->Inputs.HostEscDownDuration = -1.0f;\n    }\n\n    ImGuiTestEngine_ApplyInputToImGuiContext(engine);\n    ImGuiTestEngine_UpdateHooks(engine);\n}\n\nstatic void ImGuiTestEngine_PostNewFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx)\n{\n    if (engine->UiContextTarget != ui_ctx)\n        return;\n    IM_ASSERT(ui_ctx == GImGui);\n\n    // Set initial mouse position to a decent value on startup\n    if (engine->FrameCount == 1)\n        engine->Inputs.MousePosValue = ImGui::GetMainViewport()->Pos;\n\n    engine->IO.IsCapturing = engine->CaptureContext.IsCapturing();\n\n    // Garbage collect unused tasks\n    const int LOCATION_TASK_ELAPSE_FRAMES = 20;\n    for (int task_n = 0; task_n < engine->InfoTasks.Size; task_n++)\n    {\n        ImGuiTestInfoTask* task = engine->InfoTasks[task_n];\n        if (task->FrameCount < engine->FrameCount - LOCATION_TASK_ELAPSE_FRAMES)\n        {\n            IM_DELETE(task);\n            engine->InfoTasks.erase(engine->InfoTasks.Data + task_n);\n            task_n--;\n        }\n    }\n\n    // Slow down whole app\n    if (engine->ToolSlowDown)\n        ImThreadSleepInMilliseconds(engine->ToolSlowDownMs);\n\n    // Call user GUI function\n    ImGuiTestEngine_RunGuiFunc(engine);\n}\n\nstatic void ImGuiTestEngine_PreEndFrame(ImGuiTestEngine* engine, ImGuiContext* ui_ctx)\n{\n    IM_UNUSED(ui_ctx);\n\n    // Call user Test Function\n    // (process on-going queues in a coroutine)\n    ImGuiTestEngine_RunTestFunc(engine);\n\n    // Update hooks and output flags\n    ImGuiTestEngine_UpdateHooks(engine);\n\n    // Disable vsync\n    engine->IO.IsRequestingMaxAppSpeed = engine->IO.ConfigNoThrottle;\n    if (engine->IO.ConfigRunSpeed == ImGuiTestRunSpeed_Fast && engine->IO.IsRunningTests)\n        if (engine->TestContext && (engine->TestContext->RunFlags & ImGuiTestRunFlags_GuiFuncOnly) == 0)\n            engine->IO.IsRequestingMaxAppSpeed = true;\n}\n\nstatic void ImGuiTestEngine_PreRender(ImGuiTestEngine* engine, ImGuiContext* ui_ctx)\n{\n    if (engine->UiContextTarget != ui_ctx)\n        return;\n    IM_ASSERT(ui_ctx == GImGui);\n\n    engine->CaptureContext.PreRender();\n}\n\nstatic void ImGuiTestEngine_PostRender(ImGuiTestEngine* engine, ImGuiContext* ui_ctx)\n{\n    if (engine->UiContextTarget != ui_ctx)\n        return;\n    IM_ASSERT(ui_ctx == GImGui);\n\n    // When test are running make sure real backend doesn't pick mouse cursor shape from tests.\n    // (If were to instead set io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange in ImGuiTestEngine_RunTest() that would get us 99% of the way,\n    // but unfortunately backend wouldn't restore normal shape after modified by OS decoration such as resize, so not enough..)\n    ImGuiContext& g = *ui_ctx;\n    if (!engine->IO.ConfigMouseDrawCursor && !g.IO.MouseDrawCursor && ImGuiTestEngine_IsUsingSimulatedInputs(engine))\n        g.MouseCursor = ImGuiMouseCursor_Arrow;\n\n\n    // Check ImDrawData integrity\n    // This is currently a very cheap operation but may later become slower we if e.g. check idx boundaries.\n#ifdef IMGUI_HAS_DOCK\n    if (engine->IO.CheckDrawDataIntegrity)\n        for (ImGuiViewport* viewport : ImGui::GetPlatformIO().Viewports)\n            DrawDataVerifyMatchingBufferCount(viewport->DrawData);\n#else\n    if (engine->IO.CheckDrawDataIntegrity)\n        DrawDataVerifyMatchingBufferCount(ImGui::GetDrawData());\n#endif\n\n    engine->CaptureContext.PostRender();\n}\n\nstatic void ImGuiTestEngine_RunGuiFunc(ImGuiTestEngine* engine)\n{\n    ImGuiTestContext* ctx = engine->TestContext;\n    if (ctx && ctx->Test->GuiFunc)\n    {\n        if (!(ctx->RunFlags & ImGuiTestRunFlags_GuiFuncDisable))\n        {\n            ImGuiTestActiveFunc backup_active_func = ctx->ActiveFunc;\n            ctx->ActiveFunc = ImGuiTestActiveFunc_GuiFunc;\n            engine->TestContext->Test->GuiFunc(engine->TestContext);\n            ctx->ActiveFunc = backup_active_func;\n        }\n\n        ImGuiTestEngine_ErrorRecoveryRun(engine);\n    }\n    if (ctx)\n        ctx->FirstGuiFrame = false;\n}\n\nstatic void ImGuiTestEngine_RunTestFunc(ImGuiTestEngine* engine)\n{\n    ImGuiContext* ui_ctx = engine->UiContextTarget;\n\n    // Process on-going queues in a coroutine\n    // Run the test coroutine. This will resume the test queue from either the last point the test called YieldFromCoroutine(),\n    // or the loop in ImGuiTestEngine_TestQueueCoroutineMain that does so if no test is running.\n    // If you want to breakpoint the point execution continues in the test code, breakpoint the exit condition in YieldFromCoroutine()\n    const int input_queue_size_before = ui_ctx->InputEventsQueue.Size;\n    engine->IO.CoroutineFuncs->RunFunc(engine->TestQueueCoroutine);\n\n    // Events added by TestFunc() marked automaticaly to not be deleted\n    if (engine->TestContext && (engine->TestContext->RunFlags & ImGuiTestRunFlags_EnableRawInputs))\n        for (int n = input_queue_size_before; n < ui_ctx->InputEventsQueue.Size; n++)\n            ui_ctx->InputEventsQueue[n].AddedByTestEngine = true;\n}\n\n// Main function for the test coroutine\nstatic void ImGuiTestEngine_TestQueueCoroutineMain(void* engine_opaque)\n{\n    ImGuiTestEngine* engine = (ImGuiTestEngine*)engine_opaque;\n    while (!engine->TestQueueCoroutineShouldExit)\n    {\n        ImGuiTestEngine_ProcessTestQueue(engine);\n        engine->IO.CoroutineFuncs->YieldFunc();\n    }\n}\n\nstatic void ImGuiTestEngine_DisableWindowInputs(ImGuiWindow* window)\n{\n    window->DisableInputsFrames = 1;\n    for (ImGuiWindow* child_window : window->DC.ChildWindows)\n        ImGuiTestEngine_DisableWindowInputs(child_window);\n}\n\n// Yield control back from the TestFunc to the main update + GuiFunc, for one frame.\nvoid ImGuiTestEngine_Yield(ImGuiTestEngine* engine)\n{\n    ImGuiTestContext* ctx = engine->TestContext;\n\n    // Can only yield in the test func!\n    if (ctx)\n    {\n        IM_ASSERT(ctx->ActiveFunc == ImGuiTestActiveFunc_TestFunc && \"Can only yield inside TestFunc()!\");\n        for (ImGuiWindow* window : ctx->ForeignWindowsToHide)\n        {\n            window->HiddenFramesForRenderOnly = 2;          // Hide root window\n            ImGuiTestEngine_DisableWindowInputs(window);    // Disable inputs for root window and all it's children recursively\n        }\n    }\n\n    engine->IO.CoroutineFuncs->YieldFunc();\n}\n\nvoid ImGuiTestEngine_SetDeltaTime(ImGuiTestEngine* engine, float delta_time)\n{\n    IM_ASSERT(delta_time >= 0.0f);\n    engine->OverrideDeltaTime = delta_time;\n}\n\nint ImGuiTestEngine_GetFrameCount(ImGuiTestEngine* engine)\n{\n    return engine->FrameCount;\n}\n\nconst char* ImGuiTestEngine_GetStatusName(ImGuiTestStatus v)\n{\n    static const char* names[ImGuiTestStatus_COUNT] = { \"Unknown\", \"Success\", \"Queued\", \"Running\", \"Error\", \"Suspended\" };\n    IM_STATIC_ASSERT(IM_ARRAYSIZE(names) == ImGuiTestStatus_COUNT);\n    if (v >= 0 && v < IM_ARRAYSIZE(names))\n        return names[v];\n    return \"N/A\";\n}\n\nconst char* ImGuiTestEngine_GetRunSpeedName(ImGuiTestRunSpeed v)\n{\n    static const char* names[ImGuiTestRunSpeed_COUNT] = { \"Fast\", \"Normal\", \"Cinematic\" };\n    IM_STATIC_ASSERT(IM_ARRAYSIZE(names) == ImGuiTestRunSpeed_COUNT);\n    if (v >= 0 && v < IM_ARRAYSIZE(names))\n        return names[v];\n    return \"N/A\";\n}\n\nconst char* ImGuiTestEngine_GetVerboseLevelName(ImGuiTestVerboseLevel v)\n{\n    static const char* names[ImGuiTestVerboseLevel_COUNT] = { \"Silent\", \"Error\", \"Warning\", \"Info\", \"Debug\", \"Trace\" };\n    IM_STATIC_ASSERT(IM_ARRAYSIZE(names) == ImGuiTestVerboseLevel_COUNT);\n    if (v >= 0 && v < IM_ARRAYSIZE(names))\n        return names[v];\n    return \"N/A\";\n}\n\nbool ImGuiTestEngine_CaptureScreenshot(ImGuiTestEngine* engine, ImGuiCaptureArgs* args)\n{\n    if (engine->IO.ScreenCaptureFunc == nullptr)\n    {\n        IM_ASSERT(0);\n        return false;\n    }\n\n    IM_ASSERT(engine->CaptureCurrentArgs == nullptr && \"Nested captures are not supported.\");\n\n    // Graphics API must render a window so it can be captured\n    // FIXME: This should work without this, as long as Present vs Vsync are separated (we need a Present, we don't need Vsync)\n    const ImGuiTestRunSpeed backup_run_speed = engine->IO.ConfigRunSpeed;\n    engine->IO.ConfigRunSpeed = ImGuiTestRunSpeed_Fast;\n\n    const int frame_count = engine->FrameCount;\n\n    // Because we rely on window->ContentSize for stitching, let 1 extra frame elapse to make sure any\n    // windows which contents have changed in the last frame get a correct window->ContentSize value.\n    // FIXME: Can remove this yield if not stitching\n    if ((args->InFlags & ImGuiCaptureFlags_Instant) == 0)\n        ImGuiTestEngine_Yield(engine);\n\n    // This will yield until ImGuiTestEngine_PostSwap() -> ImGuiCaptureContext::CaptureUpdate() return false.\n    // - CaptureUpdate() will call user provided test_io.ScreenCaptureFunc() function\n    // - Capturing is likely to take multiple frames depending on settings.\n    int frames_yielded = 0;\n    engine->CaptureCurrentArgs = args;\n    engine->PostSwapCalled = false;\n    while (engine->CaptureCurrentArgs != nullptr)\n    {\n        ImGuiTestEngine_Yield(engine);\n        frames_yielded++;\n        if (frames_yielded > 4)\n            IM_ASSERT(engine->PostSwapCalled && \"ImGuiTestEngine_PostSwap() is not being called by application! Must be called in order.\");\n    }\n\n    // Verify that the ImGuiCaptureFlags_Instant flag got honored\n    if (args->InFlags & ImGuiCaptureFlags_Instant)\n        IM_ASSERT(frame_count + 1 == engine->FrameCount);\n\n    engine->IO.ConfigRunSpeed = backup_run_speed;\n    return true;\n}\n\nbool ImGuiTestEngine_CaptureBeginVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args)\n{\n    if (engine->IO.ScreenCaptureFunc == nullptr)\n    {\n        IM_ASSERT(0);\n        return false;\n    }\n\n    IM_ASSERT(engine->CaptureCurrentArgs == nullptr && \"Nested captures are not supported.\");\n\n    // RunSpeed set to Fast      -> Switch to Cinematic, no throttle\n    // RunSpeed set to Normal    -> No change\n    // RunSpeed set to Cinematic -> No change\n    engine->BackupConfigRunSpeed = engine->IO.ConfigRunSpeed;\n    engine->BackupConfigNoThrottle = engine->IO.ConfigNoThrottle;\n    if (engine->IO.ConfigRunSpeed == ImGuiTestRunSpeed_Fast)\n    {\n        engine->IO.ConfigRunSpeed = ImGuiTestRunSpeed_Cinematic;\n        engine->IO.ConfigNoThrottle = true;\n        engine->IO.ConfigFixedDeltaTime = 1.0f / 60.0f;\n    }\n    engine->CaptureCurrentArgs = args;\n    engine->CaptureContext.BeginVideoCapture(args);\n    return true;\n}\n\nbool ImGuiTestEngine_CaptureEndVideo(ImGuiTestEngine* engine, ImGuiCaptureArgs* args)\n{\n    IM_UNUSED(args);\n    IM_ASSERT(engine->CaptureContext.IsCapturingVideo() && \"No video capture is in progress.\");\n\n    engine->CaptureContext.EndVideoCapture();\n    while (engine->CaptureCurrentArgs != nullptr)   // Wait until last frame is captured and gif is saved.\n        ImGuiTestEngine_Yield(engine);\n    engine->IO.ConfigRunSpeed = engine->BackupConfigRunSpeed;\n    engine->IO.ConfigNoThrottle = engine->BackupConfigNoThrottle;\n    engine->IO.ConfigFixedDeltaTime = 0;\n    engine->CaptureCurrentArgs = nullptr;\n    return true;\n}\n\nstatic void ImGuiTestEngine_ProcessTestQueue(ImGuiTestEngine* engine)\n{\n    // Avoid tracking scrolling in UI when running a single test\n    const bool track_scrolling = (engine->TestsQueue.Size > 1) || (engine->TestsQueue.Size == 1 && (engine->TestsQueue[0].RunFlags & ImGuiTestRunFlags_RunFromCommandLine));\n\n    // Backup some state\n    ImGuiIO& io = ImGui::GetIO();\n    const char* backup_ini_filename = io.IniFilename;\n    ImGuiWindow* backup_nav_window = engine->UiContextTarget->NavWindow;\n    io.IniFilename = nullptr;\n\n    int ran_tests = 0;\n    engine->BatchStartTime = ImTimeGetInMicroseconds();\n    engine->IO.IsRunningTests = true;\n    for (int n = 0; n < engine->TestsQueue.Size; n++)\n    {\n        ImGuiTestRunTask* run_task = &engine->TestsQueue[n];\n        IM_ASSERT(run_task->Test->Output.Status == ImGuiTestStatus_Queued);\n\n        // FIXME-TESTS: Blind mode not supported\n        IM_ASSERT(engine->UiContextTarget != nullptr);\n        IM_ASSERT(engine->UiContextActive == nullptr);\n        engine->UiContextActive = engine->UiContextTarget;\n        engine->UiSelectedTest = run_task->Test;\n        if (track_scrolling)\n            engine->UiSelectAndScrollToTest = run_task->Test;\n\n        // Run test\n        ImGuiTestEngine_RunTest(engine, nullptr, run_task->Test, run_task->RunFlags);\n\n        // Cleanup\n        IM_ASSERT(engine->TestContext == nullptr);\n        IM_ASSERT(engine->UiContextActive == engine->UiContextTarget);\n        engine->UiContextActive = nullptr;\n\n        // Auto select the first error test\n        //if (test->Status == ImGuiTestStatus_Error)\n        //    if (engine->UiSelectedTest == nullptr || engine->UiSelectedTest->Status != ImGuiTestStatus_Error)\n        //        engine->UiSelectedTest = test;\n\n        ran_tests++;\n    }\n    engine->IO.IsRunningTests = false;\n    engine->BatchEndTime = ImTimeGetInMicroseconds();\n\n    engine->Abort = false;\n    engine->TestsQueue.clear();\n\n    // Restore UI state (done after all ImGuiTestEngine_RunTest() are done)\n    if (ran_tests)\n    {\n        if (engine->IO.ConfigRestoreFocusAfterTests)\n            ImGui::FocusWindow(backup_nav_window);\n    }\n    io.IniFilename = backup_ini_filename;\n}\n\nbool ImGuiTestEngine_IsTestQueueEmpty(ImGuiTestEngine* engine)\n{\n    return engine->TestsQueue.Size == 0;\n}\n\nstatic bool ImGuiTestEngine_IsRunningTest(ImGuiTestEngine* engine, ImGuiTest* test)\n{\n    for (ImGuiTestRunTask& t : engine->TestsQueue)\n        if (t.Test == test)\n            return true;\n    return false;\n}\n\nvoid ImGuiTestEngine_QueueTest(ImGuiTestEngine* engine, ImGuiTest* test, ImGuiTestRunFlags run_flags)\n{\n    if (ImGuiTestEngine_IsRunningTest(engine, test))\n        return;\n\n    // Detect lack of signal from imgui context, most likely not compiled with IMGUI_ENABLE_TEST_ENGINE=1\n    // FIXME: Why is in this function?\n    if (engine->UiContextTarget && engine->FrameCount < engine->UiContextTarget->FrameCount - 2)\n    {\n        ImGuiTestEngine_AbortCurrentTest(engine);\n        IM_ASSERT(0 && \"Not receiving signal from core library. Did you call ImGuiTestEngine_CreateContext() with the correct context? Did you compile imgui/ with IMGUI_ENABLE_TEST_ENGINE=1?\");\n        test->Output.Status = ImGuiTestStatus_Error;\n        return;\n    }\n\n    test->Output.Status = ImGuiTestStatus_Queued;\n\n    ImGuiTestRunTask run_task;\n    run_task.Test = test;\n    run_task.RunFlags = run_flags;\n    engine->TestsQueue.push_back(run_task);\n}\n\n// Called by IM_REGISTER_TEST(). Prefer calling IM_REGISTER_TEST() in your code so src_file/src_line are automatically passed.\nImGuiTest* ImGuiTestEngine_RegisterTest(ImGuiTestEngine* engine, const char* category, const char* name, const char* src_file, int src_line)\n{\n    ImGuiTestGroup group = ImGuiTestGroup_Tests;\n    if (strcmp(category, \"perf\") == 0)\n        group = ImGuiTestGroup_Perfs;\n\n    ImGuiTest* t = IM_NEW(ImGuiTest)();\n    t->Group = group;\n    t->Category = category;\n    t->Name = name;\n    t->SourceFile = src_file;\n    t->SourceLine = t->SourceLineEnd = src_line;\n    engine->TestsAll.push_back(t);\n    engine->TestsSourceLinesDirty = true;\n\n    return t;\n}\n\nvoid ImGuiTestEngine_UnregisterTest(ImGuiTestEngine* engine, ImGuiTest* test)\n{\n    // Cannot unregister a running test. Please contact us if you need this.\n    if (engine->TestContext != nullptr)\n        IM_ASSERT(engine->TestContext->Test != test);\n\n    // Remove from lists\n    bool found = engine->TestsAll.find_erase(test);\n    IM_ASSERT(found); // Calling ImGuiTestEngine_UnregisterTest() on an unknown test.\n    for (int n = 0; n < engine->TestsQueue.Size; n++)\n    {\n        ImGuiTestRunTask& task = engine->TestsQueue[n];\n        if (task.Test == test)\n        {\n            engine->TestsQueue.erase(&task);\n            n--;\n        }\n    }\n    if (engine->UiSelectAndScrollToTest == test)\n        engine->UiSelectAndScrollToTest = nullptr;\n    if (engine->UiSelectedTest == test)\n        engine->UiSelectedTest = nullptr;\n    engine->TestsSourceLinesDirty = true;\n\n    IM_DELETE(test);\n}\n\nvoid ImGuiTestEngine_UnregisterAllTests(ImGuiTestEngine* engine)\n{\n    // Cannot unregister a running test. Please contact us if you need this.\n    IM_ASSERT(engine->TestContext == nullptr);\n\n    engine->TestsAll.clear_delete();\n    engine->TestsQueue.clear();\n    engine->UiSelectAndScrollToTest = nullptr;\n    engine->UiSelectedTest = nullptr;\n    engine->TestsSourceLinesDirty = true;\n}\n\nImGuiPerfTool* ImGuiTestEngine_GetPerfTool(ImGuiTestEngine* engine)\n{\n    return engine->PerfTool;\n}\n\n// Filter tests by a specified query. Query is composed of one or more comma-separated filter terms optionally prefixed/suffixed with modifiers.\n// Available modifiers:\n// - '-' prefix excludes tests matched by the term.\n// - '^' prefix anchors term matching to the start of the string.\n// - '$' suffix anchors term matching to the end of the string.\n// Special keywords:\n// - \"all\"   : all tests, no matter what group they are in.\n// - \"tests\" : tests in ImGuiTestGroup_Tests group.\n// - \"perfs\" : tests in ImGuiTestGroup_Perfs group.\n// Example queries:\n// - \"\"      : empty query matches no tests.\n// - \"^nav_\" : all tests with name starting with \"nav_\".\n// - \"_nav$\" : all tests with name ending with \"_nav\".\n// - \"-xxx\"  : all tests and perfs that do not contain \"xxx\".\n// - \"tests,-scroll,-^nav_\" : all tests (but no perfs) that do not contain \"scroll\" in their name and does not start with \"nav_\".\n// Note: while we borrowed ^ and $ from regex conventions, we do not support actual regex syntax except for behavior of these two modifiers.\nbool ImGuiTestEngine_PassFilter(ImGuiTest* test, const char* filter_specs)\n{\n    IM_ASSERT(filter_specs != nullptr);\n    auto str_iequal = [](const char* s1, const char* s2, const char* s2_end)\n    {\n        size_t s2_len = (size_t)(s2_end - s2);\n        if (strlen(s1) != s2_len) return false;\n        return ImStrnicmp(s1, s2, s2_len) == 0;\n    };\n\n    auto str_iendswith = [&str_iequal](const char* s1, const char* s2, const char* s2_end)\n    {\n        size_t s1_len = strlen(s1);\n        size_t s2_len = (size_t)(s2_end - s2);\n        if (s1_len < s2_len) return false;\n        s1 = s1 + s1_len - s2_len;\n        return str_iequal(s1, s2, s2_end);\n    };\n\n    bool include = false;\n    const char* prefixes = \"^-\";\n\n    // When filter starts with exclude condition, we assume we have included all tests from the start. This enables\n    // writing \"-window\" instead of \"all,-window\".\n    for (int i = 0; filter_specs[i]; i++)\n        if (filter_specs[i] == '-')\n            include = true; // First filter is exclusion\n        else if (strchr(prefixes, filter_specs[i]) == nullptr)\n            break;          // End of prefixes\n\n    for (const char* filter_start = filter_specs; filter_start[0];)\n    {\n        // Filter modifiers\n        bool is_exclude = false;\n        bool is_anchor_to_start = false;\n        bool is_anchor_to_end = false;\n        for (;;)\n        {\n            if (filter_start[0] == '-')\n                is_exclude = true;\n            else if (filter_start[0] == '^')\n                is_anchor_to_start = true;\n            else\n                break;\n            filter_start++;\n        }\n\n        const char* filter_end = strstr(filter_start, \",\");\n        filter_end = filter_end ? filter_end : filter_start + strlen(filter_start);\n        is_anchor_to_end = filter_end[-1] == '$';\n        if (is_anchor_to_end)\n            filter_end--;\n\n        if (str_iequal(\"all\", filter_start, filter_end))\n            include = !is_exclude;\n        else if (str_iequal(\"tests\", filter_start, filter_end))\n            include = (test->Group == ImGuiTestGroup_Tests) ? !is_exclude : include;\n        else if (str_iequal(\"perfs\", filter_start, filter_end))\n            include = (test->Group == ImGuiTestGroup_Perfs) ? !is_exclude : include;\n        else\n        {\n            // General filtering\n            for (int n = 0; n < 2; n++)\n            {\n                const char* name = (n == 0) ? test->Name : test->Category;\n\n                bool match = true;\n\n                // \"foo\" - match a substring.\n                if (!is_anchor_to_start && !is_anchor_to_end)\n                    match = ImStristr(name, nullptr, filter_start, filter_end) != nullptr;\n\n                // \"^foo\" - match start of the string.\n                // \"foo$\" - match end of the string.\n                // FIXME: (minor) '^aaa$' will incorrectly match 'aaabbbaaa'.\n                if (is_anchor_to_start)\n                    match &= ImStrnicmp(name, filter_start, filter_end - filter_start) == 0;\n                if (is_anchor_to_end)\n                    match &= str_iendswith(name, filter_start, filter_end);\n\n                if (match)\n                {\n                    include = is_exclude ? false : true;\n                    break;\n                }\n            }\n        }\n\n        while (filter_end[0] == ',' || filter_end[0] == '$')\n            filter_end++;\n        filter_start = filter_end;\n    }\n    return include;\n}\n\nvoid ImGuiTestEngine_QueueTests(ImGuiTestEngine* engine, ImGuiTestGroup group, const char* filter_str, ImGuiTestRunFlags run_flags)\n{\n    IM_ASSERT(group >= ImGuiTestGroup_Unknown && group < ImGuiTestGroup_COUNT);\n    for (int n = 0; n < engine->TestsAll.Size; n++)\n    {\n        ImGuiTest* test = engine->TestsAll[n];\n        if (group != ImGuiTestGroup_Unknown && test->Group != group)\n            continue;\n\n        if (filter_str != nullptr)\n            if (!ImGuiTestEngine_PassFilter(test, filter_str))\n                continue;\n\n        ImGuiTestEngine_QueueTest(engine, test, run_flags);\n    }\n}\n\nvoid ImGuiTestEngine_UpdateTestsSourceLines(ImGuiTestEngine* engine)\n{\n    engine->TestsSourceLinesDirty = false;\n    if (engine->TestsAll.empty())\n        return;\n\n    struct TestAndSourceLine { ImGuiTest* Test; int SourceLine; };\n\n    ImPool<ImVector<TestAndSourceLine>> db;\n    for (ImGuiTest* test : engine->TestsAll)\n    {\n        if (test->SourceFile == nullptr)\n            continue;\n        ImGuiID srcfile_hash = ImHashStr(test->SourceFile);\n        ImVector<TestAndSourceLine>* srcfile_tests = db.GetOrAddByKey(srcfile_hash);\n        srcfile_tests->push_back({ test, test->SourceLine });\n    }\n\n    int pool_size = db.GetMapSize();\n    for (int map_n = 0; map_n < pool_size; map_n++)\n        if (ImVector<TestAndSourceLine>* srcfile_tests = db.TryGetMapData(map_n))\n        {\n            ImQsort(srcfile_tests->Data, (size_t)srcfile_tests->Size, sizeof(TestAndSourceLine), [](const void* lhs, const void* rhs)\n            { return ((const TestAndSourceLine*)lhs)->SourceLine - ((const TestAndSourceLine*)rhs)->SourceLine; });\n            for (int test_n = 0; test_n < srcfile_tests->Size - 1; test_n++)\n            {\n                TestAndSourceLine& tasl = (*srcfile_tests)[test_n];\n                IM_ASSERT(tasl.Test->SourceLine == tasl.SourceLine);\n                tasl.Test->SourceLineEnd = (*srcfile_tests)[test_n + 1].SourceLine - 1;\n            }\n        }\n}\n\n// count_remaining could be >0 if e.g. called during a crash handler or aborting a run.\nvoid ImGuiTestEngine_GetResultSummary(ImGuiTestEngine* engine, ImGuiTestEngineResultSummary* out_results)\n{\n    int count_tested = 0;\n    int count_success = 0;\n    int count_remaining = 0;\n    for (int n = 0; n < engine->TestsAll.Size; n++)\n    {\n        ImGuiTest* test = engine->TestsAll[n];\n        if (test->Output.Status == ImGuiTestStatus_Unknown)\n            continue;\n        if (test->Output.Status == ImGuiTestStatus_Queued)\n        {\n            count_remaining++;\n            continue;\n        }\n        IM_ASSERT(test->Output.Status != ImGuiTestStatus_Running);\n        count_tested++;\n        if (test->Output.Status == ImGuiTestStatus_Success)\n            count_success++;\n    }\n    out_results->CountTested = count_tested;\n    out_results->CountSuccess = count_success;\n    out_results->CountInQueue = count_remaining;\n}\n\n// Get a copy of the test list\nvoid ImGuiTestEngine_GetTestList(ImGuiTestEngine* engine, ImVector<ImGuiTest*>* out_tests)\n{\n    *out_tests = engine->TestsAll;\n}\n\n// Get a copy of the test queue\nvoid ImGuiTestEngine_GetTestQueue(ImGuiTestEngine* engine, ImVector<ImGuiTestRunTask>* out_tests)\n{\n    *out_tests = engine->TestsQueue;\n}\n\nstatic void ImGuiTestEngine_UpdateHooks(ImGuiTestEngine* engine)\n{\n    ImGuiContext* ui_ctx = engine->UiContextTarget;\n    IM_ASSERT(ui_ctx->TestEngine == engine);\n    bool want_hooking = false;\n\n    //if (engine->TestContext != nullptr)\n    //    want_hooking = true;\n\n    if (engine->InfoTasks.Size > 0)\n        want_hooking = true;\n    if (engine->FindByLabelTask.InSuffix != nullptr)\n        want_hooking = true;\n    if (engine->GatherTask.InParentID != 0)\n        want_hooking = true;\n\n    // Update test engine specific hooks\n    ui_ctx->TestEngineHookItems = want_hooking;\n}\n\nstruct ImGuiTestContextUiContextBackup\n{\n    ImGuiIO             IO;\n#if IMGUI_VERSION_NUM >= 19103\n    ImGuiPlatformIO     PlatformIO;\n#endif\n    ImGuiStyle          Style;\n    ImGuiDebugLogFlags  DebugLogFlags;\n    ImGuiKeyChord       ConfigNavWindowingKeyNext;\n    ImGuiKeyChord       ConfigNavWindowingKeyPrev;\n#if IMGUI_VERSION_NUM >= 19123\n    ImGuiErrorCallback  ErrorCallback;\n    void*               ErrorCallbackUserData;\n#endif\n\n    void Backup(ImGuiContext& g)\n    {\n        IO = g.IO;\n#if IMGUI_VERSION_NUM >= 19103\n        PlatformIO = g.PlatformIO;\n#endif\n        Style = g.Style;\n        DebugLogFlags = g.DebugLogFlags;\n#if IMGUI_VERSION_NUM >= 18837\n        ConfigNavWindowingKeyNext = g.ConfigNavWindowingKeyNext;\n        ConfigNavWindowingKeyPrev = g.ConfigNavWindowingKeyPrev;\n#endif\n#if IMGUI_VERSION_NUM >= 19123\n        ErrorCallback = g.ErrorCallback;\n        ErrorCallbackUserData = g.ErrorCallbackUserData;\n#endif\n        memset(IO.MouseDown, 0, sizeof(IO.MouseDown));\n        for (int n = 0; n < IM_ARRAYSIZE(IO.KeysData); n++)\n            IO.KeysData[n].Down = false;\n    }\n\n    void Restore(ImGuiContext& g)\n    {\n#if IMGUI_VERSION_NUM < 18993\n        IO.MetricsActiveAllocations = g.IO.MetricsActiveAllocations;\n#endif\n        g.IO = IO;\n#if IMGUI_VERSION_NUM >= 19103\n        // FIXME: This will invalidate pointers platform_io.Monitors[].\n        // User is not expected to point to monitor ever, but some may do that....\n        //g.PlatformIO = PlatformIO;\n        RestoreClipboardFuncs(g); // We only need to restore this for now. We'll find if we need more.\n#endif\n        g.Style = Style;\n        g.DebugLogFlags = DebugLogFlags;\n#if IMGUI_VERSION_NUM >= 18837\n        g.ConfigNavWindowingKeyNext = ConfigNavWindowingKeyNext;\n        g.ConfigNavWindowingKeyPrev = ConfigNavWindowingKeyPrev;\n#endif\n#if IMGUI_VERSION_NUM >= 19123\n        g.ErrorCallback = ErrorCallback;\n        g.ErrorCallbackUserData = ErrorCallbackUserData;\n#endif\n    }\n    void RestoreClipboardFuncs(ImGuiContext& g)\n    {\n#if IMGUI_VERSION_NUM >= 19103\n        g.PlatformIO.Platform_GetClipboardTextFn = PlatformIO.Platform_GetClipboardTextFn;\n        g.PlatformIO.Platform_SetClipboardTextFn = PlatformIO.Platform_SetClipboardTextFn;\n        g.PlatformIO.Platform_ClipboardUserData = PlatformIO.Platform_ClipboardUserData;\n#else\n        g.IO.GetClipboardTextFn = IO.GetClipboardTextFn;\n        g.IO.SetClipboardTextFn = IO.SetClipboardTextFn;\n        g.IO.ClipboardUserData = IO.ClipboardUserData;\n#endif\n    }\n};\n\n// FIXME: Work toward simplifying this function?\nvoid ImGuiTestEngine_RunTest(ImGuiTestEngine* engine, ImGuiTestContext* parent_ctx, ImGuiTest* test, ImGuiTestRunFlags run_flags)\n{\n    ImGuiTestContext stack_ctx;\n    ImGuiCaptureArgs stack_capture_args;\n    ImGuiTestContext* ctx;\n\n    if (run_flags & ImGuiTestRunFlags_ShareTestContext)\n    {\n        // Reuse existing test context\n        IM_ASSERT(parent_ctx != nullptr);\n        ctx = parent_ctx;\n    }\n    else\n    {\n        // Create a test context\n        ctx = &stack_ctx;\n        ctx->Engine = engine;\n        ctx->EngineIO = &engine->IO;\n        ctx->Inputs = &engine->Inputs;\n        ctx->CaptureArgs = &stack_capture_args;\n        ctx->UserVars = nullptr;\n        ctx->PerfStressAmount = engine->IO.PerfStressAmount;\n#ifdef IMGUI_HAS_DOCK\n        ctx->HasDock = true;\n#else\n        ctx->HasDock = false;\n#endif\n    }\n\n    ImGuiTestOutput* test_output;\n    if (parent_ctx == nullptr)\n    {\n        ctx->Test = test;\n        test_output = ctx->TestOutput = &test->Output;\n        test_output->StartTime = ImTimeGetInMicroseconds();\n    }\n    else\n    {\n        ctx->Test = parent_ctx->Test;\n        test_output = ctx->TestOutput = parent_ctx->TestOutput;\n    }\n\n    if (engine->Abort)\n    {\n        test_output->Status = ImGuiTestStatus_Unknown;\n        if (parent_ctx == nullptr)\n            test_output->EndTime = test_output->StartTime;\n        ctx->Test = nullptr;\n        ctx->TestOutput = nullptr;\n        ctx->CaptureArgs = nullptr;\n        return;\n    }\n\n    test_output->Status = ImGuiTestStatus_Running;\n\n    ctx->RunFlags = run_flags;\n    ctx->UiContext = engine->UiContextActive;\n\n    engine->TestContext = ctx;\n    ImGuiTestEngine_UpdateHooks(engine);\n\n    void* backup_user_vars = nullptr;\n    ImGuiTestGenericVars backup_generic_vars;\n    if (run_flags & ImGuiTestRunFlags_ShareVars)\n    {\n        // Share user vars and generic vars\n        IM_CHECK_SILENT(parent_ctx != nullptr);\n        IM_CHECK_SILENT(test->VarsSize == parent_ctx->Test->VarsSize);\n        IM_CHECK_SILENT(test->VarsConstructor == parent_ctx->Test->VarsConstructor);\n        IM_CHECK_SILENT(test->VarsPostConstructor == parent_ctx->Test->VarsPostConstructor);\n        IM_CHECK_SILENT(test->VarsPostConstructorUserFn == parent_ctx->Test->VarsPostConstructorUserFn);\n        IM_CHECK_SILENT(test->VarsDestructor == parent_ctx->Test->VarsDestructor);\n        if ((run_flags & ImGuiTestRunFlags_ShareTestContext) == 0)\n        {\n            ctx->GenericVars = parent_ctx->GenericVars;\n            ctx->UserVars = parent_ctx->UserVars;\n        }\n    }\n    else\n    {\n        // Create user vars\n        if (run_flags & ImGuiTestRunFlags_ShareTestContext)\n        {\n            backup_user_vars = parent_ctx->UserVars;\n            backup_generic_vars = parent_ctx->GenericVars;\n        }\n        ctx->GenericVars.Clear();\n        if (test->VarsConstructor != nullptr)\n        {\n            ctx->UserVars = IM_ALLOC(test->VarsSize);\n            memset(ctx->UserVars, 0, test->VarsSize);\n            test->VarsConstructor(ctx->UserVars);\n            if (test->VarsPostConstructor != nullptr && test->VarsPostConstructorUserFn != nullptr)\n                test->VarsPostConstructor(ctx, ctx->UserVars, test->VarsPostConstructorUserFn);\n        }\n    }\n\n    // Log header\n    if (parent_ctx == nullptr)\n    {\n        ctx->LogEx(ImGuiTestVerboseLevel_Info, ImGuiTestLogFlags_NoHeader, \"----------------------------------------------------------------------\"); // Intentionally TTY only (just before clear: make it a flag?)\n        test_output->Log.Clear();\n        ctx->LogWarning(\"Test: '%s' '%s'..\", test->Category, test->Name);\n    }\n    else\n    {\n        ctx->LogWarning(\"Child Test: '%s' '%s'..\", test->Category, test->Name);\n        ctx->LogDebug(\"(ShareVars=%d ShareTestContext=%d)\", (run_flags & ImGuiTestRunFlags_ShareVars) ? 1 : 0, (run_flags & ImGuiTestRunFlags_ShareTestContext) ? 1 : 0);\n    }\n\n    // Clear ImGui inputs to avoid key/mouse leaks from one test to another\n    ImGuiTestEngine_ClearInput(engine);\n\n    // Backup entire IO and style. Allows tests modifying them and not caring about restoring state.\n    ImGuiTestContextUiContextBackup backup_ui_context;\n    backup_ui_context.Backup(*ctx->UiContext);\n\n    // Setup IO: software mouse cursor, viewport support\n    ImGuiIO& io = ctx->UiContext->IO;\n    if (engine->IO.ConfigMouseDrawCursor)\n        io.MouseDrawCursor = true;\n#ifdef IMGUI_HAS_VIEWPORT\n    // We always fill io.MouseHoveredViewport manually (maintained in ImGuiTestInputs::SimulatedIO)\n    // so ensure we don't leave a chance to Dear ImGui to interpret things differently.\n    // FIXME: As written, this would prevent tests from toggling ImGuiConfigFlags_ViewportsEnable and have correct value for ImGuiBackendFlags_HasMouseHoveredViewport\n    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)\n        io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;\n    else\n        io.BackendFlags &= ~ImGuiBackendFlags_HasMouseHoveredViewport;\n#endif\n\n    // Setup IO: override clipboard\n    if ((ctx->RunFlags & ImGuiTestRunFlags_GuiFuncOnly) == 0)\n    {\n#if IMGUI_VERSION_NUM >= 19103\n        ImGuiPlatformIO& platform_io = ctx->UiContext->PlatformIO;\n        platform_io.Platform_GetClipboardTextFn = [](ImGuiContext* ui_ctx) -> const char*\n        {\n            ImGuiTestContext* ctx = (ImGuiTestContext*)ui_ctx->PlatformIO.Platform_ClipboardUserData;\n            return ctx->Clipboard.empty() ? \"\" : ctx->Clipboard.Data;\n        };\n        platform_io.Platform_SetClipboardTextFn = [](ImGuiContext* ui_ctx, const char* text)\n        {\n            ImGuiTestContext* ctx = (ImGuiTestContext*)ui_ctx->PlatformIO.Platform_ClipboardUserData;\n            ctx->Clipboard.resize((int)strlen(text) + 1);\n            strcpy(ctx->Clipboard.Data, text);\n        };\n        platform_io.Platform_ClipboardUserData = ctx;\n#else\n        io.GetClipboardTextFn = [](void* user_data) -> const char*\n        {\n            ImGuiTestContext* ctx = (ImGuiTestContext*)user_data;\n            return ctx->Clipboard.empty() ? \"\" : ctx->Clipboard.Data;\n        };\n        io.SetClipboardTextFn = [](void* user_data, const char* text)\n        {\n            ImGuiTestContext* ctx = (ImGuiTestContext*)user_data;\n            ctx->Clipboard.resize((int)strlen(text) + 1);\n            strcpy(ctx->Clipboard.Data, text);\n        };\n        io.ClipboardUserData = ctx;\n#endif\n    }\n\n    // Setup IO: error handling\n    ImGuiTestEngine_ErrorRecoverySetup(engine);\n\n    // Mark as currently running the TestFunc (this is the only time when we are allowed to yield)\n    IM_ASSERT(ctx->ActiveFunc == ImGuiTestActiveFunc_None || ctx->ActiveFunc == ImGuiTestActiveFunc_TestFunc);\n    ImGuiTestActiveFunc backup_active_func = ctx->ActiveFunc;\n    ctx->ActiveFunc = ImGuiTestActiveFunc_TestFunc;\n    ctx->FirstGuiFrame = (test->GuiFunc != nullptr) ? true : false;\n    ctx->FrameCount = parent_ctx ? parent_ctx->FrameCount : 0;\n    ctx->ErrorCounter = 0;\n    ctx->SetRef(\"\");\n    ctx->SetInputMode(ImGuiInputSource_Mouse);\n    ctx->UiContext->NavInputSource = ImGuiInputSource_Keyboard;\n    ctx->Clipboard.clear();\n\n    // Warm up GUI\n    // - We need one mandatory frame running GuiFunc before running TestFunc\n    // - We add a second frame, to avoid running tests while e.g. windows are typically appearing for the first time, hidden,\n    // measuring their initial size. Most tests are going to be more meaningful with this stabilized base.\n    if (!(test->Flags & ImGuiTestFlags_NoGuiWarmUp))\n    {\n        ctx->FrameCount -= 2;\n        ctx->Yield();\n        if (test_output->Status == ImGuiTestStatus_Running) // To allow GuiFunc calling Finish() in first frame\n            ctx->Yield();\n    }\n    ctx->FirstTestFrameCount = ctx->FrameCount;\n\n    // Call user test function (optional)\n    if (ctx->RunFlags & ImGuiTestRunFlags_GuiFuncOnly)\n    {\n        // No test function\n        while (!engine->Abort && test_output->Status == ImGuiTestStatus_Running)\n            ctx->Yield();\n    }\n    else\n    {\n        if (test->TestFunc)\n        {\n            // Test function\n            test->TestFunc(ctx);\n\n            // In case test failed without finishing gif capture - finish it here. This may trigger due to user error or\n            // due to IM_SUSPEND_TESTFUNC() terminating TestFunc() early.\n            if (engine->CaptureContext.IsCapturingVideo())\n            {\n                ImGuiCaptureArgs* args = engine->CaptureCurrentArgs;\n                ImGuiTestEngine_CaptureEndVideo(engine, args);\n                //ImFileDelete(args->OutSavedFileName);\n                ctx->LogWarning(\"Recovered from missing CaptureEndVideo()\");\n            }\n        }\n        else\n        {\n            // No test function\n            if (test->Flags & ImGuiTestFlags_NoAutoFinish)\n                while (!engine->Abort && test_output->Status == ImGuiTestStatus_Running)\n                    ctx->Yield();\n        }\n\n        // Capture failure screenshot.\n        if (ctx->IsError() && engine->IO.ConfigCaptureOnError)\n        {\n            // FIXME-VIEWPORT: Tested windows may be in their own viewport. This only captures everything in main viewport. Capture tool may be extended to capture viewport windows as well. This would leave out OS windows which may be a cause of failure.\n            ImGuiCaptureArgs args;\n            args.InFlags = ImGuiCaptureFlags_Instant;\n            args.InCaptureRect.Min = ImGui::GetMainViewport()->Pos;\n            args.InCaptureRect.Max = args.InCaptureRect.Min + ImGui::GetMainViewport()->Size;\n            ImFormatString(args.InOutputFile, IM_ARRAYSIZE(args.InOutputFile), \"output/failures/%s_%04d.png\", ctx->Test->Name, ctx->ErrorCounter);\n            if (ImGuiTestEngine_CaptureScreenshot(engine, &args))\n                ctx->LogDebug(\"Saved '%s' (%d*%d pixels)\", args.InOutputFile, (int)args.OutImageSize.x, (int)args.OutImageSize.y);\n        }\n\n        // Recover missing End*/Pop* calls.\n        ImGuiTestEngine_ErrorRecoveryRun(engine);\n\n        if (engine->IO.ConfigRunSpeed != ImGuiTestRunSpeed_Fast)\n            ctx->SleepStandard();\n\n        // Stop in GuiFunc mode\n        if (engine->IO.ConfigKeepGuiFunc && ctx->IsError())\n        {\n            // Position mouse cursor\n            ctx->UiContext->IO.WantSetMousePos = true;\n            ctx->UiContext->IO.MousePos = engine->Inputs.MousePosValue;\n\n            // Restore backend clipboard functions\n            backup_ui_context.RestoreClipboardFuncs(*ctx->UiContext);\n\n            // Unhide foreign windows (may be useful sometimes to inspect GuiFunc state... sometimes not)\n            //ctx->ForeignWindowsUnhideAll();\n        }\n\n        // Keep GuiFunc spinning\n        // FIXME-TESTS: after an error, this is not visible in the UI because status is not _Running anymore...\n        if (engine->IO.ConfigKeepGuiFunc)\n        {\n            if (engine->TestsQueue.Size == 1 || test_output->Status == ImGuiTestStatus_Error)\n            {\n#if IMGUI_VERSION_NUM >= 18992\n                ImGui::TeleportMousePos(engine->Inputs.MousePosValue);\n#endif\n                while (engine->IO.ConfigKeepGuiFunc && !engine->Abort)\n                {\n                    ctx->RunFlags |= ImGuiTestRunFlags_GuiFuncOnly;\n                    ctx->Yield();\n                }\n            }\n        }\n    }\n\n    IM_ASSERT(engine->CaptureCurrentArgs == nullptr && \"Active capture was not terminated in the test code.\");\n\n    // Process and display result/status\n    test_output->EndTime = ImTimeGetInMicroseconds();\n    if (test_output->Status == ImGuiTestStatus_Running)\n        test_output->Status = ImGuiTestStatus_Success;\n    if (engine->Abort && test_output->Status != ImGuiTestStatus_Error)\n        test_output->Status = ImGuiTestStatus_Unknown;\n\n    // Log result\n    if (test_output->Status == ImGuiTestStatus_Success)\n    {\n        if ((ctx->RunFlags & ImGuiTestRunFlags_NoSuccessMsg) == 0)\n            ctx->LogInfo(\"Success.\");\n    }\n    else if (engine->Abort)\n        ctx->LogWarning(\"Aborted.\");\n    else if (test_output->Status == ImGuiTestStatus_Error)\n        ctx->LogError(\"%s test failed.\", test->Name);\n    else\n        ctx->LogWarning(\"Unknown status.\");\n\n    // Additional yields to avoid consecutive tests who may share identifiers from missing their window/item activation.\n    ctx->RunFlags |= ImGuiTestRunFlags_GuiFuncDisable;\n    ctx->Yield(3);\n\n    // Restore active func\n    ctx->ActiveFunc = backup_active_func;\n    if (parent_ctx)\n        parent_ctx->FrameCount = ctx->FrameCount;\n\n    // Restore backed up IO and style\n    backup_ui_context.Restore(*ctx->UiContext);\n\n    if (run_flags & ImGuiTestRunFlags_ShareVars)\n    {\n        // Share generic vars?\n        if ((run_flags & ImGuiTestRunFlags_ShareTestContext) == 0)\n            parent_ctx->GenericVars = ctx->GenericVars;\n    }\n    else\n    {\n        // Destruct user vars\n        if (test->VarsConstructor != nullptr)\n        {\n            test->VarsDestructor(ctx->UserVars);\n            if (ctx->UserVars)\n                IM_FREE(ctx->UserVars);\n            ctx->UserVars = nullptr;\n        }\n        if (run_flags & ImGuiTestRunFlags_ShareTestContext)\n        {\n            parent_ctx->UserVars = backup_user_vars;\n            parent_ctx->GenericVars = backup_generic_vars;\n        }\n    }\n\n    // 'ctx' at this point is either a local variable or shared with parent.\n    //ctx->Test = nullptr;\n    //ctx->TestOutput = nullptr;\n    //ctx->CaptureArgs = nullptr;\n\n    IM_ASSERT(engine->TestContext == ctx);\n    engine->TestContext = parent_ctx;\n}\n\n#if IMGUI_VERSION_NUM < 19123\nstatic void LogAsWarningFunc(void* user_data, const char* fmt, ...)\n{\n    ImGuiTestContext* ctx = (ImGuiTestContext*)user_data;\n    va_list args;\n    va_start(args, fmt);\n    ctx->LogExV(ImGuiTestVerboseLevel_Warning, ImGuiTestLogFlags_None, fmt, args);\n    va_end(args);\n}\n\nstatic void LogAsDebugFunc(void* user_data, const char* fmt, ...)\n{\n    ImGuiTestContext* ctx = (ImGuiTestContext*)user_data;\n    va_list args;\n    va_start(args, fmt);\n    ctx->LogExV(ImGuiTestVerboseLevel_Debug, ImGuiTestLogFlags_None, fmt, args);\n    va_end(args);\n}\n#else\nstatic void LogAsWarningFunc(ImGuiContext*, void* user_data, const char* msg)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTestContext* ctx = (ImGuiTestContext*)user_data;\n    ImGuiWindow* window = g.CurrentWindow;\n    ctx->LogEx(ImGuiTestVerboseLevel_Warning, ImGuiTestLogFlags_None, \"In '%s': %s\", window ? window->Name : \"nullptr\", msg);\n}\nstatic void LogAsDebugFunc(ImGuiContext*, void* user_data, const char* msg)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTestContext* ctx = (ImGuiTestContext*)user_data;\n    ImGuiWindow* window = g.CurrentWindow;\n    ctx->LogEx(ImGuiTestVerboseLevel_Debug, ImGuiTestLogFlags_None, \"In '%s': %s\", window ? window->Name : \"nullptr\", msg);\n}\n#endif\n\nvoid ImGuiTestEngine_ErrorRecoverySetup(ImGuiTestEngine* engine)\n{\n    ImGuiTestContext* ctx = engine->TestContext;\n    IM_ASSERT(ctx != nullptr);\n    IM_ASSERT(ctx->Test != nullptr);\n#if IMGUI_VERSION_NUM >= 19123\n    if ((ctx->Test->Flags & ImGuiTestFlags_NoRecoveryWarnings) == 0)\n    {\n        ctx->UiContext->ErrorCallback = LogAsWarningFunc;\n        ctx->UiContext->ErrorCallbackUserData = ctx;\n    }\n    else\n    {\n        ctx->UiContext->ErrorCallback = LogAsDebugFunc;\n        ctx->UiContext->ErrorCallbackUserData = ctx;\n    }\n    ctx->UiContext->IO.ConfigErrorRecoveryEnableAssert = ((ctx->Test->Flags & ImGuiTestFlags_NoRecoveryWarnings) == 0 && ctx->TestOutput->Status != ImGuiTestStatus_Error);\n#else\n    IM_UNUSED(ctx);\n#endif\n}\n\nvoid ImGuiTestEngine_ErrorRecoveryRun(ImGuiTestEngine* engine)\n{\n    ImGuiTestContext* ctx = engine->TestContext;\n    IM_ASSERT(ctx != nullptr);\n    IM_ASSERT(ctx->Test != nullptr);\n    ImGuiTestEngine_ErrorRecoverySetup(engine);\n\n#if IMGUI_VERSION_NUM < 19123\n    // If we are _already_ in a test error state, recovering is normal so we'll hide the log.\n    const bool verbose = (ctx->TestOutput->Status != ImGuiTestStatus_Error) || (engine->IO.ConfigVerboseLevel >= ImGuiTestVerboseLevel_Debug);\n    if (verbose && (ctx->Test->Flags & ImGuiTestFlags_NoRecoveryWarnings) == 0)\n        ImGui::ErrorCheckEndFrameRecover(LogAsWarningFunc, ctx);\n    else\n        ImGui::ErrorCheckEndFrameRecover(LogAsDebugFunc, ctx);\n#else\n    // This would automatically be done in EndFrame() but doing it here means we get a report earlier and in the right co-routine.\n    // And the state we entered in happens to be the NewFrame() state (hence using g.StackSizesInNewFrame)\n    ImGuiContext& g = *GImGui;\n    ImGui::ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame);\n#endif\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] CRASH HANDLING\n//-------------------------------------------------------------------------\n// - ImGuiTestEngine_CrashHandler()\n// - ImGuiTestEngine_InstallDefaultCrashHandler()\n//-------------------------------------------------------------------------\n\nvoid ImGuiTestEngine_CrashHandler()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTestEngine* engine = (ImGuiTestEngine*)g.TestEngine;\n    ImGuiTest* crashed_test = (engine->TestContext && engine->TestContext->Test) ? engine->TestContext->Test : nullptr;\n\n    ImOsConsoleSetTextColor(ImOsConsoleStream_StandardError, ImOsConsoleTextColor_BrightRed);\n    if (crashed_test != nullptr)\n        fprintf(stderr, \"**ImGuiTestEngine_CrashHandler()** Crashed while running \\\"%s\\\" :(\\n\", crashed_test->Name);\n    else\n        fprintf(stderr, \"**ImGuiTestEngine_CrashHandler()** Crashed :(\\n\");\n\n    static bool handled = false;\n    if (handled)\n        return;\n    handled = true;\n\n    // Write stop times, because thread executing tests will no longer run.\n    engine->BatchEndTime = ImTimeGetInMicroseconds();\n    if (crashed_test && crashed_test->Output.Status == ImGuiTestStatus_Running)\n    {\n        crashed_test->Output.Status = ImGuiTestStatus_Error;\n        crashed_test->Output.EndTime = engine->BatchEndTime;\n    }\n\n    // Export test run results.\n    ImGuiTestEngine_Export(engine);\n    ImGuiTestEngine_PrintResultSummary(engine);\n}\n\n#ifdef _WIN32\nstatic LONG WINAPI ImGuiTestEngine_CrashHandlerWin32(LPEXCEPTION_POINTERS)\n{\n    ImGuiTestEngine_CrashHandler();\n    return EXCEPTION_EXECUTE_HANDLER;\n}\n#else\nstatic void ImGuiTestEngine_CrashHandlerUnix(int signal)\n{\n    IM_UNUSED(signal);\n    ImGuiTestEngine_CrashHandler();\n    abort();\n}\n#endif\n\nvoid ImGuiTestEngine_InstallDefaultCrashHandler()\n{\n#ifdef _WIN32\n    SetUnhandledExceptionFilter(&ImGuiTestEngine_CrashHandlerWin32);\n#elif !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n    // Install a crash handler to relevant signals.\n    struct sigaction action = {};\n    action.sa_handler = ImGuiTestEngine_CrashHandlerUnix;\n    action.sa_flags = SA_SIGINFO;\n    sigaction(SIGILL, &action, nullptr);\n    sigaction(SIGABRT, &action, nullptr);\n    sigaction(SIGFPE, &action, nullptr);\n    sigaction(SIGSEGV, &action, nullptr);\n    sigaction(SIGPIPE, &action, nullptr);\n    sigaction(SIGBUS, &action, nullptr);\n#endif\n}\n\n\n//-------------------------------------------------------------------------\n// [SECTION] HOOKS FOR CORE LIBRARY\n//-------------------------------------------------------------------------\n// - ImGuiTestEngineHook_ItemAdd()\n// - ImGuiTestEngineHook_ItemAdd_GatherTask()\n// - ImGuiTestEngineHook_ItemInfo()\n// - ImGuiTestEngineHook_ItemInfo_ResolveFindByLabel()\n// - ImGuiTestEngineHook_Log()\n// - ImGuiTestEngineHook_AssertFunc()\n//-------------------------------------------------------------------------\n\n// This is rather slow at it runs on all items but only during a GatherItems() operations.\nstatic void ImGuiTestEngineHook_ItemAdd_GatherTask(ImGuiContext* ui_ctx, ImGuiTestEngine* engine, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data)\n{\n    ImGuiContext& g = *ui_ctx;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiTestGatherTask* task = &engine->GatherTask;\n\n    if ((task->InLayerMask & (1 << window->DC.NavLayerCurrent)) == 0)\n        return;\n\n    const ImGuiID parent_id = window->IDStack.Size ? window->IDStack.back() : 0;\n    const ImGuiID gather_parent_id = task->InParentID;\n    int result_depth = -1;\n    if (gather_parent_id == parent_id)\n    {\n        result_depth = 0;\n    }\n    else\n    {\n        const int max_depth = task->InMaxDepth;\n\n        // When using a 'PushID(label); Widget(\"\"); PopID();` pattern flatten as 1 deep instead of 2 for simplicity.\n        // We do this by offsetting our depth level.\n        int curr_depth = (id == parent_id) ? -1 : 0;\n\n        ImGuiWindow* curr_window = window;\n        while (result_depth == -1 && curr_window != nullptr)\n        {\n            const int id_stack_size = curr_window->IDStack.Size;\n            for (ImGuiID* p_id_stack = curr_window->IDStack.Data + id_stack_size - 1; p_id_stack >= curr_window->IDStack.Data; p_id_stack--, curr_depth++)\n            {\n                if (curr_depth >= max_depth)\n                    break;\n                if (*p_id_stack == gather_parent_id)\n                {\n                    result_depth = curr_depth;\n                    break;\n                }\n            }\n\n            // Recurse in child (could be policy/option in GatherTask)\n            if (curr_window->Flags & ImGuiWindowFlags_ChildWindow)\n                curr_window = curr_window->ParentWindow;\n            else\n                curr_window = nullptr;\n        }\n    }\n\n    if (result_depth != -1)\n    {\n        ImGuiTestItemInfo* item = task->OutList->Pool.GetOrAddByKey(id); // Add\n        item->TimestampMain = engine->FrameCount;\n        item->ID = id;\n        item->ParentID = parent_id;\n        item->Window = window;\n        item->RectFull = item->RectClipped = bb;\n        item->RectClipped.ClipWithFull(window->ClipRect);      // This two step clipping is important, we want RectClipped to stays within RectFull\n        item->RectClipped.ClipWithFull(item->RectFull);\n        item->NavLayer = window->DC.NavLayerCurrent;\n        item->Depth = result_depth;\n#if IMGUI_VERSION_NUM >= 19135\n        item->ItemFlags = item_data ? item_data->ItemFlags : ImGuiItemFlags_None;\n#else\n        item->ItemFlags = item_data ? item_data->InFlags : ImGuiItemFlags_None;\n#endif\n        item->StatusFlags = item_data ? item_data->StatusFlags : ImGuiItemStatusFlags_None;\n        task->LastItemInfo = item;\n    }\n}\n\nvoid ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data)\n{\n    ImGuiTestEngine* engine = (ImGuiTestEngine*)ui_ctx->TestEngine;\n    engine->UiContextHasHooks = true;\n\n    IM_ASSERT(id != 0);\n    ImGuiContext& g = *ui_ctx;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // FIXME-OPT: Early out if there are no active Info/Gather tasks.\n\n    // Info Tasks\n    if (ImGuiTestInfoTask* task = ImGuiTestEngine_FindInfoTask(engine, id))\n    {\n        ImGuiTestItemInfo* item = &task->Result;\n        item->TimestampMain = engine->FrameCount;\n        item->ID = id;\n        item->ParentID = window->IDStack.Size ? window->IDStack.back() : 0;\n        item->Window = window;\n        item->RectFull = item->RectClipped = bb;\n        item->RectClipped.ClipWithFull(window->ClipRect);      // This two step clipping is important, we want RectClipped to stays within RectFull\n        item->RectClipped.ClipWithFull(item->RectFull);\n        item->NavLayer = window->DC.NavLayerCurrent;\n        item->Depth = 0;\n#if IMGUI_VERSION_NUM >= 19135\n        item->ItemFlags = item_data ? item_data->ItemFlags : ImGuiItemFlags_None;\n#else\n        item->ItemFlags = item_data ? item_data->InFlags : ImGuiItemFlags_None;\n#endif\n        item->StatusFlags = item_data ? item_data->StatusFlags : ImGuiItemStatusFlags_None;\n    }\n\n    // Gather Task (only 1 can be active)\n    if (engine->GatherTask.InParentID != 0)\n        ImGuiTestEngineHook_ItemAdd_GatherTask(ui_ctx, engine, id, bb, item_data);\n}\n\n#if IMGUI_VERSION_NUM < 18934\nvoid    ImGuiTestEngineHook_ItemAdd(ImGuiContext* ui_ctx, const ImRect& bb, ImGuiID id)\n{\n    ImGuiTestEngineHook_ItemAdd(ui_ctx, id, bb, nullptr);\n}\n#endif\n\n// Task is submitted in TestFunc by ItemInfo() -> ItemInfoHandleWildcardSearch()\n#ifdef IMGUI_HAS_IMSTR\nstatic void ImGuiTestEngineHook_ItemInfo_ResolveFindByLabel(ImGuiContext* ui_ctx, ImGuiID id, const ImStrv label, ImGuiItemStatusFlags flags)\n#else\nstatic void ImGuiTestEngineHook_ItemInfo_ResolveFindByLabel(ImGuiContext* ui_ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags)\n#endif\n{\n    // At this point \"label\" is a match for the right-most name in user wildcard (e.g. the \"bar\" of \"**/foo/bar\"\n    ImGuiContext& g = *ui_ctx;\n    ImGuiTestEngine* engine = (ImGuiTestEngine*)ui_ctx->TestEngine;\n    IM_UNUSED(label); // Match ABI of caller function (faster call)\n\n    // Test for matching status flags\n    ImGuiTestFindByLabelTask* label_task = &engine->FindByLabelTask;\n    if (ImGuiItemStatusFlags filter_flags = label_task->InFilterItemStatusFlags)\n        if (!(filter_flags & flags))\n            return;\n\n    // Test for matching PREFIX (the \"window\" of \"window/**/foo/bar\" or the \"\" of \"/**/foo/bar\")\n    // FIXME-TESTS: Stack depth limit?\n    // FIXME-TESTS: Recurse back into parent window limit?\n    bool match_prefix = false;\n    if (label_task->InPrefixId == 0)\n    {\n        match_prefix = true;\n    }\n    else\n    {\n        // Recurse back into parent, so from \"WindowA\" with SetRef(\"WindowA\") it is possible to use \"**/Button\" to reach \"WindowA/ChildXXXX/Button\"\n        for (ImGuiWindow* window = g.CurrentWindow; window != nullptr && !match_prefix; window = window->ParentWindow)\n        {\n            const int id_stack_size = window->IDStack.Size;\n            for (ImGuiID* p_id_stack = window->IDStack.Data + id_stack_size - 1; p_id_stack >= window->IDStack.Data; p_id_stack--)\n                if (*p_id_stack == label_task->InPrefixId)\n                {\n                    match_prefix = true;\n                    break;\n                }\n        }\n    }\n    if (!match_prefix)\n        return;\n\n    // Test for full matching SUFFIX (the \"foo/bar\" or \"window/**/foo/bar\")\n    // Because at this point we have only compared the prefix and the right-most label (the \"window\" and \"bar\" or \"window/**/foo/bar\")\n    // FIXME-TESTS: The entire suffix must be inside the final window:\n    // - In theory, someone could craft a suffix that contains sub-window, e.g. \"SomeWindow/**/SomeChild_XXXX/SomeItem\" and this will fail.\n    // - Once we make child path easier to access we can fix that.\n    if (label_task->InSuffixDepth > 1) // This is merely an early out: for Depth==1 the compare has already been done in ImGuiTestEngineHook_ItemInfo()\n    {\n        ImGuiWindow* window = g.CurrentWindow;\n        const int id_stack_size = window->IDStack.Size;\n        int id_stack_pos = id_stack_size - label_task->InSuffixDepth;\n\n        // At this point, IN MOST CASES (BUT NOT ALL) this should be the case:\n        //    ImHashStr(label, 0, g.CurrentWindow->IDStack.back()) == id\n        // It's not always the case as we have situations where we call IMGUI_TEST_ENGINE_ITEM_INFO() outside of the right stack location:\n        //    e.g. Begin(), or items using the PushID(label); SubItem(\"\"); PopID(); idiom.\n        // If you are curious or need to understand this more in depth, uncomment this assert to detect them:\n        //    ImGuiID tmp_id = ImHashStr(label, 0, g.CurrentWindow->IDStack.back());\n        //    IM_ASSERT(tmp_id == id);\n        // The \"Try with parent\" case is designed to handle that. May need further tuning.\n\n        ImGuiID base_id = id_stack_pos >= 0 ? window->IDStack.Data[id_stack_pos] : 0;   // base_id correspond to the \"**\"\n        ImGuiID find_id = ImHashDecoratedPath(label_task->InSuffix, nullptr, base_id);     // hash the whole suffix e.g. \"foo/bar\" over our base\n        if (id != find_id)\n        {\n            // Try with parent\n            base_id = id_stack_pos > 0 ? window->IDStack.Data[id_stack_pos - 1] : 0;\n            find_id = ImHashDecoratedPath(label_task->InSuffix, nullptr, base_id);\n            if (id != find_id)\n                return;\n        }\n    }\n\n    // Success\n    label_task->OutItemId = id;\n}\n\n// label is optional\n#ifdef IMGUI_HAS_IMSTR\nvoid ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, ImStrv label, ImGuiItemStatusFlags flags)\n#else\nvoid ImGuiTestEngineHook_ItemInfo(ImGuiContext* ui_ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags)\n#endif\n{\n    ImGuiTestEngine* engine = (ImGuiTestEngine*)ui_ctx->TestEngine;\n\n    IM_ASSERT(id != 0);\n    ImGuiContext& g = *ui_ctx;\n    //ImGuiWindow* window = g.CurrentWindow;\n    //IM_ASSERT(window->DC.LastItemId == id || window->DC.LastItemId == 0); // Need _ItemAdd() to be submitted before _ItemInfo()\n\n    // Update Info Task status flags\n    if (ImGuiTestInfoTask* task = ImGuiTestEngine_FindInfoTask(engine, id))\n    {\n        ImGuiTestItemInfo* item = &task->Result;\n        item->TimestampStatus = g.FrameCount;\n        item->StatusFlags = flags;\n        if (label)\n            ImStrncpy(item->DebugLabel, label, IM_ARRAYSIZE(item->DebugLabel));\n    }\n\n    // Update Gather Task status flags\n    if (engine->GatherTask.LastItemInfo && engine->GatherTask.LastItemInfo->ID == id)\n    {\n        ImGuiTestItemInfo* item = engine->GatherTask.LastItemInfo;\n        item->TimestampStatus = g.FrameCount;\n        item->StatusFlags = flags;\n        if (label)\n            ImStrncpy(item->DebugLabel, label, IM_ARRAYSIZE(item->DebugLabel));\n    }\n\n    // Update Find by Label Task\n    // FIXME-TESTS FIXME-OPT: Compare by hashes instead of strcmp to support \"###\" operator.\n    // Perhaps we could use strcmp() if we detect that ### is not used, that would be faster.\n    ImGuiTestFindByLabelTask* label_task = &engine->FindByLabelTask;\n    if (label && label_task->InSuffixLastItem && label_task->OutItemId == 0)\n#ifdef IMGUI_HAS_IMSTR\n        if (label_task->InSuffixLastItemHash == ImHashStr(label))\n#else\n        if (label_task->InSuffixLastItemHash == ImHashStr(label, 0))\n#endif\n            ImGuiTestEngineHook_ItemInfo_ResolveFindByLabel(ui_ctx, id, label, flags);\n}\n\n// Forward core/user-land text to test log\n// This is called via the user-land IMGUI_TEST_ENGINE_LOG() macro.\nvoid ImGuiTestEngineHook_Log(ImGuiContext* ui_ctx, const char* fmt, ...)\n{\n    ImGuiTestEngine* engine = (ImGuiTestEngine*)ui_ctx->TestEngine;\n    if (engine == nullptr || engine->TestContext == nullptr)\n        return;\n\n    va_list args;\n    va_start(args, fmt);\n    engine->TestContext->LogExV(ImGuiTestVerboseLevel_Debug, ImGuiTestLogFlags_None, fmt, args);\n    va_end(args);\n}\n\n// Helper to output extra information (e.g. current test) during an assert.\n// Your custom assert code may optionally want to call this.\nvoid ImGuiTestEngine_AssertLog(const char* expr, const char* file, const char* function, int line)\n{\n    if (ImGuiTestEngine* engine = GImGuiTestEngine)\n        if (ImGuiTestContext* ctx = engine->TestContext)\n        {\n            ctx->LogError(\"Assert: '%s'\", expr);\n            ctx->LogWarning(\"In %s:%d, function %s()\", file, line, function);\n            if (ImGuiTest* test = ctx->Test)\n                ctx->LogWarning(\"While running test: %s %s\", test->Category, test->Name);\n        }\n}\n\n// Used by IM_CHECK_OP() macros\nImGuiTextBuffer* ImGuiTestEngine_GetTempStringBuilder()\n{\n    static ImGuiTextBuffer builder;\n    builder.Buf.resize(1);\n    builder.Buf[0] = 0;\n    return &builder;\n}\n\n// Out of convenience for main library we allow this to be called before TestEngine is initialized.\nconst char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ui_ctx, ImGuiID id)\n{\n    if (ui_ctx->TestEngine == nullptr || id == 0)\n        return nullptr;\n    if (ImGuiTestItemInfo* id_info = ImGuiTestEngine_FindItemInfo((ImGuiTestEngine*)ui_ctx->TestEngine, id, \"\"))\n        return id_info->DebugLabel;\n    return nullptr;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] CHECK/ERROR FUNCTIONS FOR TESTS\n//-------------------------------------------------------------------------\n// - ImGuiTestEngine_Check()\n// - ImGuiTestEngine_Error()\n//-------------------------------------------------------------------------\n\n// Return true to request a debugger break\nbool ImGuiTestEngine_Check(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, bool result, const char* expr)\n{\n    ImGuiTestEngine* engine = GImGuiTestEngine;\n    (void)func;\n\n    // Removed absolute path from output so we have deterministic output (otherwise __FILE__ gives us machine dending output)\n    const char* file_without_path = file ? ImPathFindFilename(file) : \"\";\n\n    if (ImGuiTestContext* ctx = engine->TestContext)\n    {\n        ImGuiTest* test = ctx->Test;\n        //ctx->LogDebug(\"IM_CHECK(%s)\", expr);\n        if (!result)\n        {\n            if (!(ctx->RunFlags & ImGuiTestRunFlags_GuiFuncOnly))\n                test->Output.Status = ImGuiTestStatus_Error;\n\n            if (file)\n                ctx->LogError(\"Error %s:%d '%s'\", file_without_path, line, expr);\n            else\n                ctx->LogError(\"Error '%s'\", expr);\n            ctx->ErrorCounter++;\n        }\n        else if (!(flags & ImGuiTestCheckFlags_SilentSuccess))\n        {\n            if (file)\n                ctx->LogInfo(\"OK %s:%d '%s'\", file_without_path, line, expr);\n            else\n                ctx->LogInfo(\"OK '%s'\", expr);\n        }\n    }\n    else\n    {\n        IM_ASSERT(0 && \"No active tests!\");\n    }\n\n    if (result == false && engine->IO.ConfigStopOnError && !engine->Abort)\n        engine->Abort = true; //ImGuiTestEngine_Abort(engine);\n    if (result == false && engine->IO.ConfigBreakOnError && !engine->Abort)\n        return true;\n\n    return false;\n}\n\nbool ImGuiTestEngine_CheckStrOp(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* op, const char* lhs_var, const char* lhs_value, const char* rhs_var, const char* rhs_value, bool* out_res)\n{\n    int res_strcmp = strcmp(lhs_value, rhs_value);\n    bool res = 0;\n    if (strcmp(op, \"==\") == 0)\n        res = (res_strcmp == 0);\n    else if (strcmp(op, \"!=\") == 0)\n        res = (res_strcmp != 0);\n    else\n        IM_ASSERT(0);\n    *out_res = res;\n\n    ImGuiTextBuffer buf; // FIXME-OPT: Now we can probably remove that allocation\n\n    bool lhs_is_literal = lhs_var[0] == '\\\"';\n    bool rhs_is_literal = rhs_var[0] == '\\\"';\n    if (strchr(lhs_value, '\\n') != nullptr || strchr(rhs_value, '\\n') != nullptr)\n    {\n        // Multi line strings\n        size_t lhs_value_len = strlen(lhs_value);\n        size_t rhs_value_len = strlen(rhs_value);\n        if (lhs_value_len > 0 && lhs_value[lhs_value_len - 1] == '\\n') // Strip trailing carriage return as we are adding one ourselves\n            lhs_value_len--;\n        if (rhs_value_len > 0 && rhs_value[rhs_value_len - 1] == '\\n')\n            rhs_value_len--;\n        buf.appendf(\n            \"\\n\"\n            \"---------------------------------------- // lhs: %s\\n\"\n            \"%.*s\\n\"\n            \"---------------------------------------- // rhs: %s, compare op: %s\\n\"\n            \"%.*s\\n\"\n            \"----------------------------------------\\n\",\n            lhs_is_literal ? \"literal\" : lhs_var,\n            (int)lhs_value_len, lhs_value,\n            rhs_is_literal ? \"literal\" : rhs_var,\n            op,\n            (int)rhs_value_len, rhs_value);\n    }\n    else\n    {\n        // Single line strings\n        buf.appendf(\n            \"%s [\\\"%s\\\"] %s %s [\\\"%s\\\"]\",\n            lhs_is_literal ? \"\" : lhs_var, lhs_value,\n            op,\n            rhs_is_literal ? \"\" : rhs_var, rhs_value);\n    }\n\n\n    return ImGuiTestEngine_Check(file, func, line, flags, res, buf.c_str());\n}\n\nbool ImGuiTestEngine_Error(const char* file, const char* func, int line, ImGuiTestCheckFlags flags, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    Str256 buf;\n    buf.setfv(fmt, args);\n    bool ret = ImGuiTestEngine_Check(file, func, line, flags, false, buf.c_str());\n    va_end(args);\n\n    ImGuiTestEngine* engine = GImGuiTestEngine;\n    if (engine && engine->Abort)\n        return false;\n    return ret;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] SETTINGS\n//-------------------------------------------------------------------------\n// FIXME: In our wildest dreams we could provide a imgui_club/ serialization helper that would be\n// easy to use in both the ReadLine and WriteAll functions.\n//-------------------------------------------------------------------------\n\nstatic void*    ImGuiTestEngine_SettingsReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)\n{\n    if (strcmp(name, \"Data\") != 0)\n        return nullptr;\n    return (void*)1;\n}\n\nstatic bool     SettingsTryReadString(const char* line, const char* prefix, char* out_buf, size_t out_buf_size)\n{\n    // Could also use scanf() with \"%[^\\n]\" but it won't bound check.\n    size_t prefix_len = strlen(prefix);\n    if (strncmp(line, prefix, prefix_len) != 0)\n        return false;\n    line += prefix_len;\n    IM_ASSERT(out_buf_size >= strlen(line) + 1);\n    ImFormatString(out_buf, out_buf_size, \"%s\", line);\n    return true;\n}\n\nstatic bool     SettingsTryReadString(const char* line, const char* prefix, Str* out_str)\n{\n    // Could also use scanf() with \"%[^\\n]\" but it won't bound check.\n    size_t prefix_len = strlen(prefix);\n    if (strncmp(line, prefix, prefix_len) != 0)\n        return false;\n    line += prefix_len;\n    out_str->set(line);\n    return true;\n}\n\nstatic void     ImGuiTestEngine_SettingsReadLine(ImGuiContext* ui_ctx, ImGuiSettingsHandler*, void* entry, const char* line)\n{\n    ImGuiTestEngine* e = (ImGuiTestEngine*)ui_ctx->TestEngine;\n    IM_ASSERT(e != nullptr);\n    IM_ASSERT(e->UiContextTarget == ui_ctx);\n    IM_UNUSED(entry);\n\n    int n = 0;\n    /**/ if (SettingsTryReadString(line, \"FilterTests=\", e->UiFilterTests))                                                         { }\n    else if (SettingsTryReadString(line, \"FilterPerfs=\", e->UiFilterPerfs))                                                         { }\n    else if (sscanf(line, \"LogHeight=%f\", &e->UiLogHeight) == 1)                                                                    { }\n    else if (sscanf(line, \"CaptureTool=%d\", &n) == 1)                                                                               { e->UiCaptureToolOpen = (n != 0); }\n    else if (sscanf(line, \"PerfTool=%d\", &n) == 1)                                                                                  { e->UiPerfToolOpen = (n != 0); }\n    else if (sscanf(line, \"StackTool=%d\", &n) == 1)                                                                                 { e->UiStackToolOpen = (n != 0); }\n    else if (sscanf(line, \"CaptureEnabled=%d\", &n) == 1)                                                                            { e->IO.ConfigCaptureEnabled = (n != 0); }\n    else if (sscanf(line, \"CaptureOnError=%d\", &n) == 1)                                                                            { e->IO.ConfigCaptureOnError = (n != 0); }\n    else if (SettingsTryReadString(line, \"VideoCapturePathToEncoder=\", e->IO.VideoCaptureEncoderPath, IM_ARRAYSIZE(e->IO.VideoCaptureEncoderPath))) { }\n    else if (SettingsTryReadString(line, \"VideoCaptureParamsToEncoder=\", e->IO.VideoCaptureEncoderParams, IM_ARRAYSIZE(e->IO.VideoCaptureEncoderParams))) { }\n    else if (SettingsTryReadString(line, \"GifCaptureParamsToEncoder=\", e->IO.GifCaptureEncoderParams, IM_ARRAYSIZE(e->IO.GifCaptureEncoderParams))) { }\n    else if (SettingsTryReadString(line, \"VideoCaptureExtension=\", e->IO.VideoCaptureExtension, IM_ARRAYSIZE(e->IO.VideoCaptureExtension))) { }\n}\n\nstatic void     ImGuiTestEngine_SettingsWriteAll(ImGuiContext* ui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)\n{\n    ImGuiTestEngine* engine = (ImGuiTestEngine*)ui_ctx->TestEngine;\n    IM_ASSERT(engine != nullptr);\n    IM_ASSERT(engine->UiContextTarget == ui_ctx);\n\n    buf->appendf(\"[%s][Data]\\n\", handler->TypeName);\n    buf->appendf(\"FilterTests=%s\\n\", engine->UiFilterTests->c_str());\n    buf->appendf(\"FilterPerfs=%s\\n\", engine->UiFilterPerfs->c_str());\n    buf->appendf(\"LogHeight=%.0f\\n\", engine->UiLogHeight);\n    buf->appendf(\"CaptureTool=%d\\n\", engine->UiCaptureToolOpen);\n    buf->appendf(\"PerfTool=%d\\n\", engine->UiPerfToolOpen);\n    buf->appendf(\"StackTool=%d\\n\", engine->UiStackToolOpen);\n    buf->appendf(\"CaptureEnabled=%d\\n\", engine->IO.ConfigCaptureEnabled);\n    buf->appendf(\"CaptureOnError=%d\\n\", engine->IO.ConfigCaptureOnError);\n    buf->appendf(\"VideoCapturePathToEncoder=%s\\n\", engine->IO.VideoCaptureEncoderPath);\n    buf->appendf(\"VideoCaptureParamsToEncoder=%s\\n\", engine->IO.VideoCaptureEncoderParams);\n    buf->appendf(\"GifCaptureParamsToEncoder=%s\\n\", engine->IO.GifCaptureEncoderParams);\n    buf->appendf(\"VideoCaptureExtension=%s\\n\", engine->IO.VideoCaptureExtension);\n    buf->appendf(\"\\n\");\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] ImGuiTestLog\n//-------------------------------------------------------------------------\n\nvoid ImGuiTestLog::Clear()\n{\n    Buffer.clear();\n    LineInfo.clear();\n    memset(&CountPerLevel, 0, sizeof(CountPerLevel));\n}\n\n// Output:\n// - If 'buffer != nullptr': all extracted lines are appended to 'buffer'. Use 'buffer->c_str()' on your side to obtain the text.\n// - Return value: number of lines extracted (should be equivalent to number of '\\n' inside buffer->c_str()).\n// - You may call the function with buffer == nullptr to only obtain a count without getting the data.\n// Verbose levels are inclusive:\n// - To get ONLY Error:                     Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Error\n// - To get ONLY Error and Warnings:        Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Warning\n// - To get All Errors, Warnings, Debug...  Use level_min == ImGuiTestVerboseLevel_Error, level_max = ImGuiTestVerboseLevel_Trace\nint ImGuiTestLog::ExtractLinesForVerboseLevels(ImGuiTestVerboseLevel level_min, ImGuiTestVerboseLevel level_max, ImGuiTextBuffer* out_buffer)\n{\n    IM_ASSERT(level_min <= level_max);\n\n    // Return count\n    int count = 0;\n    if (out_buffer == nullptr)\n    {\n        for (int n = level_min; n <= level_max; n++)\n            count += CountPerLevel[n];\n        return count;\n    }\n\n    // Extract lines and return count\n    for (auto& line_info : LineInfo)\n        if (line_info.Level >= level_min && line_info.Level <= level_max)\n        {\n            const char* line_begin = Buffer.c_str() + line_info.LineOffset;\n            const char* line_end = strchr(line_begin, '\\n');\n            out_buffer->append(line_begin, line_end[0] == '\\n' ? line_end + 1 : line_end);\n            count++;\n        }\n    return count;\n}\n\nvoid ImGuiTestLog::UpdateLineOffsets(ImGuiTestEngineIO* engine_io, ImGuiTestVerboseLevel level, const char* start)\n{\n    IM_UNUSED(engine_io);\n    IM_ASSERT(Buffer.begin() <= start && start < Buffer.end());\n    const char* p_begin = start;\n    const char* p_end = Buffer.end();\n    const char* p = p_begin;\n    while (p < p_end)\n    {\n        const char* p_bol = p;\n        const char* p_eol = strchr(p, '\\n');\n\n        bool last_empty_line = (p_bol + 1 == p_end);\n        if (!last_empty_line)\n        {\n            int offset = (int)(p_bol - Buffer.c_str());\n            LineInfo.push_back({level, offset});\n            CountPerLevel[level] += 1;\n        }\n        p = p_eol ? p_eol + 1 : nullptr;\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] ImGuiTest\n//-------------------------------------------------------------------------\n\nImGuiTest::~ImGuiTest()\n{\n    if (NameOwned)\n        ImGui::MemFree((char*)Name);\n}\n\nvoid ImGuiTest::SetOwnedName(const char* name)\n{\n    IM_ASSERT(!NameOwned);\n    NameOwned = true;\n    Name = ImStrdup(name);\n}\n\n//-------------------------------------------------------------------------\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/source/imgui_te_exporters.cpp",
    "content": "// dear imgui test engine\n// (result exporters)\n// Read https://github.com/ocornut/imgui_test_engine/wiki/Exporting-Results\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n#include \"imgui_te_exporters.h\"\n#include \"imgui_te_engine.h\"\n#include \"imgui_te_internal.h\"\n#include \"thirdparty/Str/Str.h\"\n\n//-------------------------------------------------------------------------\n// [SECTION] FORWARD DECLARATIONS\n//-------------------------------------------------------------------------\n\nstatic void ImGuiTestEngine_ExportJUnitXml(ImGuiTestEngine* engine, const char* output_file);\n\n//-------------------------------------------------------------------------\n// [SECTION] TEST ENGINE EXPORTER FUNCTIONS\n//-------------------------------------------------------------------------\n// - ImGuiTestEngine_PrintResultSummary()\n// - ImGuiTestEngine_Export()\n// - ImGuiTestEngine_ExportEx()\n// - ImGuiTestEngine_ExportJUnitXml()\n//-------------------------------------------------------------------------\n\nvoid ImGuiTestEngine_PrintResultSummary(ImGuiTestEngine* engine)\n{\n    ImGuiTestEngineResultSummary summary;\n    ImGuiTestEngine_GetResultSummary(engine, &summary);\n\n    if (summary.CountSuccess < summary.CountTested)\n    {\n        printf(\"\\nFailing tests:\\n\");\n        for (ImGuiTest* test : engine->TestsAll)\n            if (test->Output.Status == ImGuiTestStatus_Error)\n                printf(\"- %s\\n\", test->Name);\n    }\n\n    bool success = (summary.CountSuccess == summary.CountTested);\n    ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, success ? ImOsConsoleTextColor_BrightGreen : ImOsConsoleTextColor_BrightRed);\n    printf(\"\\nTests Result: %s\\n\", success ? \"OK\" : \"Errors\");\n    printf(\"(%d/%d tests passed)\\n\", summary.CountSuccess, summary.CountTested);\n    if (summary.CountInQueue > 0)\n        printf(\"(%d queued tests remaining)\\n\", summary.CountInQueue);\n    ImOsConsoleSetTextColor(ImOsConsoleStream_StandardOutput, ImOsConsoleTextColor_White);\n}\n\n// This is mostly a copy of ImGuiTestEngine_PrintResultSummary with few additions.\nstatic void ImGuiTestEngine_ExportResultSummary(ImGuiTestEngine* engine, FILE* fp, int indent_count, ImGuiTestGroup group)\n{\n    int count_tested = 0;\n    int count_success = 0;\n\n    for (ImGuiTest* test : engine->TestsAll)\n    {\n        if (test->Group != group)\n            continue;\n        if (test->Output.Status != ImGuiTestStatus_Unknown)\n            count_tested++;\n        if (test->Output.Status == ImGuiTestStatus_Success)\n            count_success++;\n    }\n\n    Str64 indent_str;\n    indent_str.reserve(indent_count + 1);\n    memset(indent_str.c_str(), ' ', indent_count);\n    indent_str[indent_count] = 0;\n    const char* indent = indent_str.c_str();\n\n    if (count_success < count_tested)\n    {\n        fprintf(fp, \"\\n%sFailing tests:\\n\", indent);\n        for (ImGuiTest* test : engine->TestsAll)\n        {\n            if (test->Group != group)\n                continue;\n            if (test->Output.Status == ImGuiTestStatus_Error)\n                fprintf(fp, \"%s- %s\\n\", indent, test->Name);\n        }\n        fprintf(fp, \"\\n\");\n    }\n\n    fprintf(fp, \"%sTests Result: %s\\n\", indent, (count_success == count_tested) ? \"OK\" : \"Errors\");\n    fprintf(fp, \"%s(%d/%d tests passed)\\n\", indent, count_success, count_tested);\n}\n\nstatic bool ImGuiTestEngine_HasAnyLogLines(ImGuiTestLog* test_log, ImGuiTestVerboseLevel level)\n{\n    for (auto& line_info : test_log->LineInfo)\n        if (line_info.Level <= level)\n            return true;\n    return false;\n}\n\nstatic void ImGuiTestEngine_PrintLogLines(FILE* fp, ImGuiTestLog* test_log, int indent, ImGuiTestVerboseLevel level)\n{\n    Str128 log_line;\n    for (auto& line_info : test_log->LineInfo)\n    {\n        if (line_info.Level > level)\n            continue;\n        const char* line_start = test_log->Buffer.c_str() + line_info.LineOffset;\n        const char* line_end = strstr(line_start, \"\\n\"); // FIXME: Incorrect.\n        log_line.set(line_start, line_end);\n        ImStrXmlEscape(&log_line); // FIXME: Should not be here considering the function name.\n\n        // Some users may want to disable indenting?\n        fprintf(fp, \"%*s%s\\n\", indent, \"\", log_line.c_str());\n    }\n}\n\n// Export using settings stored in ImGuiTestEngineIO\n// This is called by ImGuiTestEngine_CrashHandler().\nvoid ImGuiTestEngine_Export(ImGuiTestEngine* engine)\n{\n    ImGuiTestEngineIO& io = engine->IO;\n    ImGuiTestEngine_ExportEx(engine, io.ExportResultsFormat, io.ExportResultsFilename);\n}\n\n// Export using custom settings.\nvoid ImGuiTestEngine_ExportEx(ImGuiTestEngine* engine, ImGuiTestEngineExportFormat format, const char* filename)\n{\n    if (format == ImGuiTestEngineExportFormat_None)\n        return;\n    IM_ASSERT(filename != nullptr);\n\n    if (format == ImGuiTestEngineExportFormat_JUnitXml)\n        ImGuiTestEngine_ExportJUnitXml(engine, filename);\n    else\n        IM_ASSERT(0);\n}\n\nvoid ImGuiTestEngine_ExportJUnitXml(ImGuiTestEngine* engine, const char* output_file)\n{\n    IM_ASSERT(engine != nullptr);\n    IM_ASSERT(output_file != nullptr);\n\n    FILE* fp = fopen(output_file, \"w+b\");\n    if (fp == nullptr)\n    {\n        fprintf(stderr, \"Writing '%s' failed.\\n\", output_file);\n        return;\n    }\n\n    // Per-testsuite test statistics.\n    struct\n    {\n        const char* Name     = nullptr;\n        int         Tests    = 0;\n        int         Failures = 0;\n        int         Disabled = 0;\n    } testsuites[ImGuiTestGroup_COUNT];\n    testsuites[ImGuiTestGroup_Tests].Name = \"tests\";\n    testsuites[ImGuiTestGroup_Perfs].Name = \"perfs\";\n\n    for (int n = 0; n < engine->TestsAll.Size; n++)\n    {\n        ImGuiTest* test = engine->TestsAll[n];\n        auto* stats = &testsuites[test->Group];\n        stats->Tests += 1;\n        if (test->Output.Status == ImGuiTestStatus_Error)\n            stats->Failures += 1;\n        else if (test->Output.Status == ImGuiTestStatus_Unknown)\n            stats->Disabled += 1;\n    }\n\n    // Attributes for <testsuites> tag.\n    const char* testsuites_name = \"Dear ImGui\";\n    int testsuites_failures = 0;\n    int testsuites_tests = 0;\n    int testsuites_disabled = 0;\n    float testsuites_time = (float)((double)(engine->BatchEndTime - engine->BatchStartTime) / 1000000.0);\n    for (int testsuite_id = ImGuiTestGroup_Tests; testsuite_id < ImGuiTestGroup_COUNT; testsuite_id++)\n    {\n        testsuites_tests += testsuites[testsuite_id].Tests;\n        testsuites_failures += testsuites[testsuite_id].Failures;\n        testsuites_disabled += testsuites[testsuite_id].Disabled;\n    }\n\n    // FIXME: \"errors\" attribute and <error> tag in <testcase> may be supported if we have means to catch unexpected errors like assertions.\n    fprintf(fp, \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\"\n        \"<testsuites disabled=\\\"%d\\\" errors=\\\"0\\\" failures=\\\"%d\\\" name=\\\"%s\\\" tests=\\\"%d\\\" time=\\\"%.3f\\\">\\n\",\n        testsuites_disabled, testsuites_failures, testsuites_name, testsuites_tests, testsuites_time);\n\n    for (int testsuite_id = ImGuiTestGroup_Tests; testsuite_id < ImGuiTestGroup_COUNT; testsuite_id++)\n    {\n        // Attributes for <testsuite> tag.\n        auto* testsuite = &testsuites[testsuite_id];\n        float testsuite_time = testsuites_time;         // FIXME: We do not differentiate between tests and perfs, they are executed in one big batch.\n        Str30 testsuite_timestamp = \"\";\n        ImTimestampToISO8601(engine->BatchStartTime, &testsuite_timestamp);\n        fprintf(fp, \"  <testsuite name=\\\"%s\\\" tests=\\\"%d\\\" disabled=\\\"%d\\\" errors=\\\"0\\\" failures=\\\"%d\\\" hostname=\\\"\\\" id=\\\"%d\\\" package=\\\"\\\" skipped=\\\"0\\\" time=\\\"%.3f\\\" timestamp=\\\"%s\\\">\\n\",\n            testsuite->Name, testsuite->Tests, testsuite->Disabled, testsuite->Failures, testsuite_id, testsuite_time, testsuite_timestamp.c_str());\n\n        for (int n = 0; n < engine->TestsAll.Size; n++)\n        {\n            ImGuiTest* test = engine->TestsAll[n];\n            if (test->Group != testsuite_id)\n                continue;\n\n            ImGuiTestOutput* test_output = &test->Output;\n            ImGuiTestLog* test_log = &test_output->Log;\n\n            // Attributes for <testcase> tag.\n            const char* testcase_name = test->Name;\n            const char* testcase_classname = test->Category;\n            const char* testcase_status = ImGuiTestEngine_GetStatusName(test_output->Status);\n            const float testcase_time = (float)((double)(test_output->EndTime - test_output->StartTime) / 1000000.0);\n\n            fprintf(fp, \"    <testcase name=\\\"%s\\\" assertions=\\\"0\\\" classname=\\\"%s\\\" status=\\\"%s\\\" time=\\\"%.3f\\\">\\n\",\n                testcase_name, testcase_classname, testcase_status, testcase_time);\n\n            if (test_output->Status == ImGuiTestStatus_Error)\n            {\n                // Skip last error message because it is generic information that test failed.\n                Str128 log_line;\n                for (int i = test_log->LineInfo.Size - 2; i >= 0; i--)\n                {\n                    ImGuiTestLogLineInfo* line_info = &test_log->LineInfo[i];\n                    if (line_info->Level > engine->IO.ConfigVerboseLevelOnError)\n                        continue;\n                    if (line_info->Level == ImGuiTestVerboseLevel_Error)\n                    {\n                        const char* line_start = test_log->Buffer.c_str() + line_info->LineOffset;\n                        const char* line_end = strstr(line_start, \"\\n\");\n                        log_line.set(line_start, line_end);\n                        ImStrXmlEscape(&log_line);\n                        break;\n                    }\n                }\n\n                // Failing tests save their \"on error\" log output in text element of <failure> tag.\n                fprintf(fp, \"      <failure message=\\\"%s\\\" type=\\\"error\\\">\\n\", log_line.c_str());\n                ImGuiTestEngine_PrintLogLines(fp, test_log, 8, engine->IO.ConfigVerboseLevelOnError);\n                fprintf(fp, \"      </failure>\\n\");\n            }\n\n            if (test_output->Status == ImGuiTestStatus_Unknown)\n            {\n                fprintf(fp, \"      <skipped message=\\\"Skipped\\\" />\\n\");\n            }\n            else\n            {\n                // Succeeding tests save their default log output output as \"stdout\".\n                if (ImGuiTestEngine_HasAnyLogLines(test_log, engine->IO.ConfigVerboseLevel))\n                {\n                    fprintf(fp, \"      <system-out>\\n\");\n                    ImGuiTestEngine_PrintLogLines(fp, test_log, 8, engine->IO.ConfigVerboseLevel);\n                    fprintf(fp, \"      </system-out>\\n\");\n                }\n\n                // Save error messages as \"stderr\".\n                if (ImGuiTestEngine_HasAnyLogLines(test_log, ImGuiTestVerboseLevel_Error))\n                {\n                    fprintf(fp, \"      <system-err>\\n\");\n                    ImGuiTestEngine_PrintLogLines(fp, test_log, 8, ImGuiTestVerboseLevel_Error);\n                    fprintf(fp, \"      </system-err>\\n\");\n                }\n            }\n            fprintf(fp, \"    </testcase>\\n\");\n        }\n\n        if (testsuites[testsuite_id].Disabled < testsuites[testsuite_id].Tests) // Any tests executed\n        {\n            // Log all log messages as \"stdout\".\n            fprintf(fp, \"    <system-out>\\n\");\n            for (int n = 0; n < engine->TestsAll.Size; n++)\n            {\n                ImGuiTest* test = engine->TestsAll[n];\n                ImGuiTestOutput* test_output = &test->Output;\n                if (test->Group != testsuite_id)\n                    continue;\n                if (test_output->Status == ImGuiTestStatus_Unknown)\n                    continue;\n                fprintf(fp, \"      [0000] Test: '%s' '%s'..\\n\", test->Category, test->Name);\n                ImGuiTestVerboseLevel level = test_output->Status == ImGuiTestStatus_Error ? engine->IO.ConfigVerboseLevelOnError : engine->IO.ConfigVerboseLevel;\n                ImGuiTestEngine_PrintLogLines(fp, &test_output->Log, 6, level);\n            }\n            ImGuiTestEngine_ExportResultSummary(engine, fp, 6, (ImGuiTestGroup)testsuite_id);\n            fprintf(fp, \"    </system-out>\\n\");\n\n            // Log all warning and error messages as \"stderr\".\n            fprintf(fp, \"    <system-err>\\n\");\n            for (int n = 0; n < engine->TestsAll.Size; n++)\n            {\n                ImGuiTest* test = engine->TestsAll[n];\n                ImGuiTestOutput* test_output = &test->Output;\n                if (test->Group != testsuite_id)\n                    continue;\n                if (test_output->Status == ImGuiTestStatus_Unknown)\n                    continue;\n                fprintf(fp, \"      [0000] Test: '%s' '%s'..\\n\", test->Category, test->Name);\n                ImGuiTestEngine_PrintLogLines(fp, &test_output->Log, 6, ImGuiTestVerboseLevel_Warning);\n            }\n            ImGuiTestEngine_ExportResultSummary(engine, fp, 6, (ImGuiTestGroup)testsuite_id);\n            fprintf(fp, \"    </system-err>\\n\");\n        }\n        fprintf(fp, \"  </testsuite>\\n\");\n    }\n    fprintf(fp, \"</testsuites>\\n\");\n    fclose(fp);\n    fprintf(stdout, \"Saved test results to '%s' successfully.\\n\", output_file);\n}\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/source/imgui_te_perftool.cpp",
    "content": "// dear imgui test engine\n// (performance tool)\n// Browse and visualize samples recorded by ctx->PerfCapture() calls.\n// User access via 'Test Engine UI -> Tools -> Perf Tool'\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n/*\n\nIndex of this file:\n// [SECTION] Header mess\n// [SECTION] ImGuiPerflogEntry\n// [SECTION] Types & everything else\n// [SECTION] USER INTERFACE\n// [SECTION] SETTINGS\n// [SECTION] TESTS\n\n*/\n\n// Terminology:\n// * Entry: information about execution of a single perf test. This corresponds to one line in CSV file.\n// * Batch: a group of entries that were created together during a single execution. A new batch is created each time\n//   one or more perf tests are executed. All entries in a single batch will have a matching ImGuiPerflogEntry::Timestamp.\n// * Build: A group of batches that have matching BuildType, OS, Cpu, Compiler, GitBranchName.\n// * Baseline: A batch that we are comparing against. Baselines are identified by batch timestamp and build id.\n\n//-------------------------------------------------------------------------\n// [SECTION] Header mess\n//-------------------------------------------------------------------------\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#define IMGUI_DEFINE_MATH_OPERATORS\n#include \"imgui_te_perftool.h\"\n#include \"imgui.h\"\n#include \"imgui_internal.h\"\n#include \"imgui_te_utils.h\"\n#include \"thirdparty/Str/Str.h\"\n#include <time.h>               // time(), localtime()\n#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT\n#include \"implot.h\"\n#include \"implot_internal.h\"\n#endif\n\n// For tests\n#include \"imgui_te_engine.h\"\n#include \"imgui_te_context.h\"\n#include \"imgui_te_internal.h\"  // ImGuiTestEngine_GetPerfTool()\n#include \"imgui_capture_tool.h\"\n\n//-------------------------------------------------------------------------\n// [SECTION] ImGuiPerflogEntry\n//-------------------------------------------------------------------------\n\nvoid ImGuiPerfToolEntry::Set(const ImGuiPerfToolEntry& other)\n{\n    Timestamp = other.Timestamp;\n    Category = other.Category;\n    TestName = other.TestName;\n    DtDeltaMs = other.DtDeltaMs;\n    DtDeltaMsMin = other.DtDeltaMsMin;\n    DtDeltaMsMax = other.DtDeltaMsMax;\n    NumSamples = other.NumSamples;\n    PerfStressAmount = other.PerfStressAmount;\n    GitBranchName = other.GitBranchName;\n    BuildType = other.BuildType;\n    Cpu = other.Cpu;\n    OS = other.OS;\n    Compiler = other.Compiler;\n    Date = other.Date;\n    //DateMax = ...\n    VsBaseline = other.VsBaseline;\n    LabelIndex = other.LabelIndex;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Types & everything else\n//-------------------------------------------------------------------------\n\ntypedef ImGuiID(*HashEntryFn)(ImGuiPerfToolEntry* entry);\ntypedef void(*FormatEntryLabelFn)(ImGuiPerfTool* perftool, Str* result, ImGuiPerfToolEntry* entry);\n\nstruct ImGuiPerfToolColumnInfo\n{\n    const char*     Title;\n    int             Offset;\n    ImGuiDataType   Type;\n    bool            ShowAlways;\n    ImGuiTableFlags Flags;\n\n    template<typename T>\n    T GetValue(const ImGuiPerfToolEntry* entry) const { return *(T*)((const char*)entry + Offset); }\n};\n\n// Update _ShowEntriesTable() and SaveHtmlReport() when adding new entries.\nstatic const ImGuiPerfToolColumnInfo PerfToolColumnInfo[] =\n{\n    { /* 00 */ \"Date\",        offsetof(ImGuiPerfToolEntry, Timestamp),        ImGuiDataType_U64,    true,  ImGuiTableColumnFlags_DefaultHide },\n    { /* 01 */ \"Test Name\",   offsetof(ImGuiPerfToolEntry, TestName),         ImGuiDataType_COUNT,  true,  0 },\n    { /* 02 */ \"Branch\",      offsetof(ImGuiPerfToolEntry, GitBranchName),    ImGuiDataType_COUNT,  true,  0 },\n    { /* 03 */ \"Compiler\",    offsetof(ImGuiPerfToolEntry, Compiler),         ImGuiDataType_COUNT,  true,  0 },\n    { /* 04 */ \"OS\",          offsetof(ImGuiPerfToolEntry, OS),               ImGuiDataType_COUNT,  true,  0 },\n    { /* 05 */ \"CPU\",         offsetof(ImGuiPerfToolEntry, Cpu),              ImGuiDataType_COUNT,  true,  0 },\n    { /* 06 */ \"Build\",       offsetof(ImGuiPerfToolEntry, BuildType),        ImGuiDataType_COUNT,  true,  0 },\n    { /* 07 */ \"Stress\",      offsetof(ImGuiPerfToolEntry, PerfStressAmount), ImGuiDataType_S32,    true,  0 },\n    { /* 08 */ \"Avg ms\",      offsetof(ImGuiPerfToolEntry, DtDeltaMs),        ImGuiDataType_Double, true,  0 },\n    { /* 09 */ \"Min ms\",      offsetof(ImGuiPerfToolEntry, DtDeltaMsMin),     ImGuiDataType_Double, false, 0 },\n    { /* 00 */ \"Max ms\",      offsetof(ImGuiPerfToolEntry, DtDeltaMsMax),     ImGuiDataType_Double, false, 0 },\n    { /* 11 */ \"Samples\",     offsetof(ImGuiPerfToolEntry, NumSamples),       ImGuiDataType_S32,    false, 0 },\n    { /* 12 */ \"VS Baseline\", offsetof(ImGuiPerfToolEntry, VsBaseline),       ImGuiDataType_Float,  true,  0 },\n};\n\nstatic const char* PerfToolReportDefaultOutputPath = \"./output/capture_perf_report.html\";\n\n// This is declared as a standalone function in order to run without a PerfTool instance\nvoid ImGuiTestEngine_PerfToolAppendToCSV(ImGuiPerfTool* perf_log, ImGuiPerfToolEntry* entry, const char* filename)\n{\n    if (filename == nullptr)\n        filename = IMGUI_PERFLOG_DEFAULT_FILENAME;\n\n    if (!ImFileCreateDirectoryChain(filename, ImPathFindFilename(filename)))\n    {\n        fprintf(stderr, \"Unable to create missing directory '%*s', perftool entry was not saved.\\n\", (int)(ImPathFindFilename(filename) - filename), filename);\n        return;\n    }\n\n    // Appends to .csv\n    FILE* f = fopen(filename, \"a+b\");\n    if (f == nullptr)\n    {\n        fprintf(stderr, \"Unable to open '%s', perftool entry was not saved.\\n\", filename);\n        return;\n    }\n    fprintf(f, \"%llu,%s,%s,%.3f,x%d,%s,%s,%s,%s,%s,%s\\n\", entry->Timestamp, entry->Category, entry->TestName,\n            entry->DtDeltaMs, entry->PerfStressAmount, entry->GitBranchName, entry->BuildType, entry->Cpu, entry->OS,\n            entry->Compiler, entry->Date);\n    fflush(f);\n    fclose(f);\n\n    // Register to runtime perf tool if any\n    if (perf_log != nullptr)\n        perf_log->AddEntry(entry);\n}\n\n// Tri-state button. Copied and modified ButtonEx().\nstatic bool Button3(const char* label, int* value)\n{\n    ImGuiWindow* window = ImGui::GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    const ImVec2 label_size = ImGui::CalcTextSize(label, nullptr, true);\n    float dot_radius2 = g.FontSize;\n    ImVec2 btn_size(dot_radius2 * 2, dot_radius2);\n\n    ImVec2 pos = window->DC.CursorPos;\n    ImVec2 size = ImGui::CalcItemSize(ImVec2(), btn_size.x + label_size.x + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x, label_size.y + style.FramePadding.y * 2.0f);\n\n    const ImRect bb(pos, pos + size);\n    ImGui::ItemSize(size, style.FramePadding.y);\n    if (!ImGui::ItemAdd(bb, id))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ImGui::ButtonBehavior(ImRect(pos, pos + style.FramePadding + btn_size), id, &hovered, &held, 0);\n\n    // Render\n    const ImU32 col = ImGui::GetColorU32(ImGuiCol_FrameBg);\n#if IMGUI_VERSION_NUM >= 19136\n    ImGui::RenderNavCursor(bb, id);\n#else\n    ImGui::RenderNavHighlight(bb, id);\n#endif\n    ImGui::RenderFrame(bb.Min + style.FramePadding, bb.Min + style.FramePadding + btn_size, col, true, /*style.FrameRounding*/ 5.0f);\n\n    ImColor btn_col;\n    if (held)\n        btn_col = style.Colors[ImGuiCol_SliderGrabActive];\n    else if (hovered)\n        btn_col = style.Colors[ImGuiCol_ButtonHovered];\n    else\n        btn_col = style.Colors[ImGuiCol_SliderGrab];\n    ImVec2 center = bb.Min + ImVec2(dot_radius2 + (dot_radius2 * (float)*value), dot_radius2) * 0.5f + style.FramePadding;\n    window->DrawList->AddCircleFilled(center, dot_radius2 * 0.5f, btn_col);\n\n    ImRect text_bb;\n    text_bb.Min = bb.Min + style.FramePadding + ImVec2(btn_size.x + style.ItemInnerSpacing.x, 0);\n    text_bb.Max = text_bb.Min + label_size;\n    ImGui::RenderTextClipped(text_bb.Min, text_bb.Max, label, nullptr, &label_size, style.ButtonTextAlign, &bb);\n\n    *value = (*value + pressed) % 3;\n    return pressed;\n}\n\nstatic ImGuiID GetBuildID(const ImGuiPerfToolEntry* entry)\n{\n    IM_ASSERT(entry != nullptr);\n    ImGuiID build_id = ImHashStr(entry->BuildType);\n    build_id = ImHashStr(entry->OS, 0, build_id);\n    build_id = ImHashStr(entry->Cpu, 0, build_id);\n    build_id = ImHashStr(entry->Compiler, 0, build_id);\n    build_id = ImHashStr(entry->GitBranchName, 0, build_id);\n    return build_id;\n}\n\nstatic ImGuiID GetBuildID(const ImGuiPerfToolBatch* batch)\n{\n    IM_ASSERT(batch != nullptr);\n    IM_ASSERT(!batch->Entries.empty());\n    return GetBuildID(&batch->Entries.Data[0]);\n}\n\n// Batch ID depends on display type. It is either a build ID (when combinding by build type) or batch timestamp otherwise.\nstatic ImGuiID GetBatchID(const ImGuiPerfTool* perftool, const ImGuiPerfToolEntry* entry)\n{\n    IM_ASSERT(perftool != nullptr);\n    IM_ASSERT(entry != nullptr);\n    if (perftool->_DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo)\n        return GetBuildID(entry);\n    else\n        return (ImU32)entry->Timestamp;\n}\n\nstatic int PerfToolComparerStr(const void* a, const void* b)\n{\n    return strcmp(*(const char**)b, *(const char**)a);\n}\n\nstatic int IMGUI_CDECL PerfToolComparerByEntryInfo(const void* lhs, const void* rhs)\n{\n    const ImGuiPerfToolEntry* a = (const ImGuiPerfToolEntry*)lhs;\n    const ImGuiPerfToolEntry* b = (const ImGuiPerfToolEntry*)rhs;\n\n    // While build ID does include git branch it wont ensure branches are grouped together, therefore we do branch\n    // sorting manually.\n    int result = strcmp(a->GitBranchName, b->GitBranchName);\n\n    // Now that we have groups of branches - sort individual builds within those groups.\n    if (result == 0)\n        result = ImClamp<int>((int)((ImS64)GetBuildID(a) - (ImS64)GetBuildID(b)), -1, +1);\n\n    // Group individual runs together within build groups.\n    if (result == 0)\n        result = (int)ImClamp<ImS64>((ImS64)b->Timestamp - (ImS64)a->Timestamp, -1, +1);\n\n    // And finally sort individual runs by perf name so we can have a predictable order (used to optimize in _Rebuild()).\n    if (result == 0)\n        result = (int)strcmp(a->TestName, b->TestName);\n\n    return result;\n}\n\nstatic ImGuiPerfTool* PerfToolInstance = nullptr;\nstatic int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)\n{\n    IM_ASSERT(PerfToolInstance != nullptr);\n    ImGuiPerfTool* tool = PerfToolInstance;\n    const ImGuiTableSortSpecs* sort_specs = PerfToolInstance->_InfoTableSortSpecs;\n    int batch_index_a, entry_index_a, mono_index_a, batch_index_b, entry_index_b, mono_index_b;\n    tool->_UnpackSortedKey(*(ImU64*)lhs, &batch_index_a, &entry_index_a, &mono_index_a);\n    tool->_UnpackSortedKey(*(ImU64*)rhs, &batch_index_b, &entry_index_b, &mono_index_b);\n    for (int i = 0; i < sort_specs->SpecsCount; i++)\n    {\n        const ImGuiTableColumnSortSpecs* specs = &sort_specs->Specs[i];\n        const ImGuiPerfToolColumnInfo& col_info = PerfToolColumnInfo[specs->ColumnIndex];\n        const ImGuiPerfToolBatch* batch_a = &tool->_Batches[batch_index_a];\n        const ImGuiPerfToolBatch* batch_b = &tool->_Batches[batch_index_b];\n        ImGuiPerfToolEntry* a = &batch_a->Entries.Data[entry_index_a];\n        ImGuiPerfToolEntry* b = &batch_b->Entries.Data[entry_index_b];\n        if (specs->SortDirection == ImGuiSortDirection_Ascending)\n            ImSwap(a, b);\n\n        int result = 0;\n        switch (col_info.Type)\n        {\n        case ImGuiDataType_S32:\n            result = col_info.GetValue<int>(a) - col_info.GetValue<int>(b);\n            break;\n        case ImGuiDataType_U64:\n            result = (int)(col_info.GetValue<ImU64>(a) - col_info.GetValue<ImU64>(b));\n            break;\n        case ImGuiDataType_Float:\n            result = (int)((col_info.GetValue<float>(a) - col_info.GetValue<float>(b)) * 1000.0f);\n            break;\n        case ImGuiDataType_Double:\n            result = (int)((col_info.GetValue<double>(a) - col_info.GetValue<double>(b)) * 1000.0);\n            break;\n        case ImGuiDataType_COUNT:\n            result = strcmp(col_info.GetValue<const char*>(a), col_info.GetValue<const char*>(b));\n            break;\n        default:\n            IM_ASSERT(false);\n        }\n        if (result != 0)\n            return result;\n    }\n    return mono_index_a - mono_index_b;\n}\n\n// Dates are in format \"YYYY-MM-DD\"\nstatic bool IsDateValid(const char* date)\n{\n    if (date[4] != '-' || date[7] != '-')\n        return false;\n    for (int i = 0; i < 10; i++)\n    {\n        if (i == 4 || i == 7)\n            continue;\n        if (date[i] < '0' || date[i] > '9')\n            return false;\n    }\n    return true;\n}\n\nstatic float FormatVsBaseline(ImGuiPerfToolEntry* entry, ImGuiPerfToolEntry* baseline_entry, Str& out_label)\n{\n    if (baseline_entry == nullptr)\n    {\n        out_label.appendf(\"--\");\n        return FLT_MAX;\n    }\n\n    if (entry == baseline_entry)\n    {\n        out_label.append(\"baseline\");\n        return FLT_MAX;\n    }\n\n    double percent_vs_first = 100.0 / baseline_entry->DtDeltaMs * entry->DtDeltaMs;\n    double dt_change = -(100.0 - percent_vs_first);\n    if (dt_change == INFINITY)\n        out_label.appendf(\"--\");\n    else if (ImAbs(dt_change) > 0.001f)\n        out_label.appendf(\"%+.2lf%% (%s)\", dt_change, dt_change < 0.0f ? \"faster\" : \"slower\");\n    else\n        out_label.appendf(\"==\");\n    return (float)dt_change;\n}\n\n#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT\nstatic void PerfToolFormatBuildInfo(ImGuiPerfTool* perftool, Str* result, ImGuiPerfToolBatch* batch)\n{\n    IM_ASSERT(perftool != nullptr);\n    IM_ASSERT(result != nullptr);\n    IM_ASSERT(batch != nullptr);\n    IM_ASSERT(batch->Entries.Size > 0);\n    ImGuiPerfToolEntry* entry = &batch->Entries.Data[0];\n    Str64f legend_format(\"x%%-%dd %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%s%%s%%s%%s(%%-%dd sample%%s)%%s\",\n        perftool->_AlignStress, perftool->_AlignType, perftool->_AlignCpu, perftool->_AlignOs, perftool->_AlignCompiler,\n        perftool->_AlignBranch, perftool->_AlignSamples);\n    result->appendf(legend_format.c_str(), entry->PerfStressAmount, entry->BuildType, entry->Cpu, entry->OS,\n        entry->Compiler, entry->GitBranchName, entry->Date,\n#if 0\n        // Show min-max dates.\n        perftool->_CombineByBuildInfo ? \" - \" : \"\",\n        entry->DateMax ? entry->DateMax : \"\",\n#else\n        \"\", \"\",\n#endif\n        *entry->Date ? \" \" : \"\",\n        batch->NumSamples,\n        batch->NumSamples > 1 ? \"s\" : \"\",                               // Singular/plural form of \"sample(s)\"\n        batch->NumSamples > 1 || perftool->_AlignSamples == 1 ? \"\" : \" \" // Space after legend entry to separate * marking baseline\n    );\n}\n#endif\n\nstatic int PerfToolCountBuilds(ImGuiPerfTool* perftool, bool only_visible)\n{\n    int num_builds = 0;\n    ImU64 build_id = 0;\n    for (ImGuiPerfToolEntry& entry : perftool->_SrcData)\n    {\n        if (build_id != GetBuildID(&entry))\n        {\n            if (!only_visible || perftool->_IsVisibleBuild(&entry))\n                num_builds++;\n            build_id = GetBuildID(&entry);\n        }\n    }\n    return num_builds;\n}\n\nstatic bool InputDate(const char* label, char* date, int date_len, bool valid)\n{\n    ImGui::SetNextItemWidth(ImGui::CalcTextSize(\"YYYY-MM-DD\").x + ImGui::GetStyle().FramePadding.x * 2.0f);\n    const bool date_valid = date[0] == 0 || (IsDateValid(date) && valid);\n    if (!date_valid)\n    {\n        ImGui::PushStyleColor(ImGuiCol_Border, IM_COL32(255, 0, 0, 255));\n        ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);\n    }\n    bool date_changed = ImGui::InputTextWithHint(label, \"YYYY-MM-DD\", date, date_len);\n    if (!date_valid)\n    {\n        ImGui::PopStyleVar();\n        ImGui::PopStyleColor();\n    }\n    return date_changed;\n}\n\nstatic void FormatDate(ImU64 microseconds, char* buf, size_t buf_size)\n{\n    time_t timestamp = (time_t)(microseconds / 1000000);\n    tm* time = localtime(&timestamp);\n    ImFormatString(buf, buf_size, \"%04d-%02d-%02d\", time->tm_year + 1900, time->tm_mon + 1, time->tm_mday);\n}\n\nstatic void FormatDateAndTime(ImU64 microseconds, char* buf, size_t buf_size)\n{\n    time_t timestamp = (time_t)(microseconds / 1000000);\n    tm* time = localtime(&timestamp);\n    ImFormatString(buf, buf_size, \"%04d-%02d-%02d %02d:%02d:%02d\", time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec);\n}\n\nstatic void RenderFilterInput(ImGuiPerfTool* perf, const char* hint, float width = -FLT_MIN)\n{\n    if (ImGui::IsWindowAppearing())\n        strcpy(perf->_Filter, \"\");\n    ImGui::SetNextItemWidth(width);\n    ImGui::InputTextWithHint(\"##filter\", hint, perf->_Filter, IM_ARRAYSIZE(perf->_Filter));\n    if (ImGui::IsWindowAppearing())\n        ImGui::SetKeyboardFocusHere();\n}\n\nstatic bool RenderMultiSelectFilter(ImGuiPerfTool* perf, const char* filter_hint, ImVector<const char*>* labels)\n{\n    ImGuiContext& g = *ImGui::GetCurrentContext();\n    ImGuiIO& io = ImGui::GetIO();\n    ImGuiStorage& visibility = perf->_Visibility;\n    bool modified = false;\n    RenderFilterInput(perf, filter_hint, -(ImGui::CalcTextSize(\"(?)\").x + g.Style.ItemSpacing.x));\n    ImGui::SameLine();\n    ImGui::TextDisabled(\"(?)\");\n    if (ImGui::IsItemHovered())\n        ImGui::SetTooltip(\"Hold CTRL to invert other items.\\nHold SHIFT to close popup instantly.\");\n\n    // Keep popup open for multiple actions if SHIFT is pressed.\n#if IMGUI_VERSION_NUM >= 19094\n    if (!io.KeyShift)\n        ImGui::PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);\n#else\n    if (!io.KeyShift)\n        ImGui::PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);\n#endif\n\n    if (ImGui::MenuItem(\"Show All\"))\n    {\n        for (const char* label : *labels)\n            if (strstr(label, perf->_Filter) != nullptr)\n                visibility.SetBool(ImHashStr(label), true);\n        modified = true;\n    }\n\n    if (ImGui::MenuItem(\"Hide All\"))\n    {\n        for (const char* label : *labels)\n            if (strstr(label, perf->_Filter) != nullptr)\n                visibility.SetBool(ImHashStr(label), false);\n        modified = true;\n    }\n\n    // Render perf labels in reversed order. Labels are sorted, but stored in reversed order to render them on the plot\n    // from top down (ImPlot renders stuff from bottom up).\n    int filtered_entries = 0;\n    for (int i = labels->Size - 1; i >= 0; i--)\n    {\n        const char* label = (*labels)[i];\n        if (strstr(label, perf->_Filter) == nullptr)   // Filter out entries not matching a filter query\n            continue;\n\n        if (filtered_entries == 0)\n            ImGui::Separator();\n\n        ImGuiID build_id = ImHashStr(label);\n        bool visible = visibility.GetBool(build_id, true);\n        if (ImGui::MenuItem(label, nullptr, &visible))\n        {\n            modified = true;\n            if (io.KeyCtrl)\n            {\n                for (const char* label2 : *labels)\n                {\n                    ImGuiID build_id2 = ImHashStr(label2);\n                    visibility.SetBool(build_id2, !visibility.GetBool(build_id2, true));\n                }\n            }\n            else\n            {\n                visibility.SetBool(build_id, !visibility.GetBool(build_id, true));\n            }\n        }\n        filtered_entries++;\n    }\n\n    if (!io.KeyShift)\n        ImGui::PopItemFlag();\n\n    return modified;\n}\n\n// Based on ImPlot::SetupFinish().\n#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT\nstatic ImRect ImPlotGetYTickRect(int t, int y = 0)\n{\n    ImPlotContext& gp = *GImPlot;\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& ax = plot.YAxis(y);\n    const ImPlotTicker& tkc = ax.Ticker;\n    const bool opp = ax.IsOpposite();\n    ImRect result(1.0f, 1.0f, -1.0f, -1.0f);\n    if (ax.HasTickLabels())\n    {\n        const ImPlotTick& tk = tkc.Ticks[t];\n        const float datum = ax.Datum1 + (opp ? gp.Style.LabelPadding.x : (-gp.Style.LabelPadding.x - tk.LabelSize.x));\n        if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.y - 1 && tk.PixelPos <= plot.PlotRect.Max.y + 1)\n        {\n            ImVec2 start(datum, tk.PixelPos - 0.5f * tk.LabelSize.y);\n            result.Min = start;\n            result.Max = start + tk.LabelSize;\n        }\n    }\n    return result;\n}\n#endif // #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT\n\nImGuiPerfTool::ImGuiPerfTool()\n{\n    _CsvParser = IM_NEW(ImGuiCsvParser)();\n    Clear();\n}\n\nImGuiPerfTool::~ImGuiPerfTool()\n{\n    _SrcData.clear_destruct();\n    _Batches.clear_destruct();\n    IM_DELETE(_CsvParser);\n}\n\nvoid ImGuiPerfTool::AddEntry(ImGuiPerfToolEntry* entry)\n{\n    if (strcmp(_FilterDateFrom, entry->Date) > 0)\n        ImStrncpy(_FilterDateFrom, entry->Date, IM_ARRAYSIZE(_FilterDateFrom));\n    if (strcmp(_FilterDateTo, entry->Date) < 0)\n        ImStrncpy(_FilterDateTo, entry->Date, IM_ARRAYSIZE(_FilterDateTo));\n\n    _SrcData.push_back(*entry);\n    _Batches.clear_destruct();\n}\n\nvoid ImGuiPerfTool::_Rebuild()\n{\n    if (_SrcData.empty())\n        return;\n\n    ImGuiStorage& temp_set = _TempSet;\n    _Labels.resize(0);\n    _LabelsVisible.resize(0);\n    _InfoTableSort.resize(0);\n    _Batches.clear_destruct();\n    _InfoTableSortDirty = true;\n\n    // Gather all visible labels. Legend batches will store data in this order.\n    temp_set.Data.resize(0);    // name_id:IsLabelSeen\n    for (ImGuiPerfToolEntry& entry : _SrcData)\n    {\n        ImGuiID name_id = ImHashStr(entry.TestName);\n        if (!temp_set.GetBool(name_id))\n        {\n            temp_set.SetBool(name_id, true);\n            _Labels.push_back(entry.TestName);\n            if (_IsVisibleTest(entry.TestName))\n                _LabelsVisible.push_front(entry.TestName);\n        }\n    }\n    int num_visible_labels = _LabelsVisible.Size;\n\n    // Labels are sorted in reverse order so they appear to be oredered from top down.\n    ImQsort(_Labels.Data, _Labels.Size, sizeof(const char*), &PerfToolComparerStr);\n    ImQsort(_LabelsVisible.Data, num_visible_labels, sizeof(const char*), &PerfToolComparerStr);\n\n    // _SrcData vector stores sorted raw entries of imgui_perflog.csv. Sorting is very important,\n    // algorithm depends on data being correctly sorted. Sorting _SrcData is OK, because it is only\n    // ever appended to and never written out to disk. Entries are sorted by multiple criteria,\n    // in specified order:\n    // 1. By branch name\n    // 2. By build ID\n    // 3. By run timestamp\n    // 4. By test name\n    // This results in a neatly partitioned dataset where similar data is grouped together and where perf test order\n    // is consistent in all batches. Sorting by build ID _before_ timestamp is also important as we will be aggregating\n    // entries by build ID instead of timestamp, when appropriate display mode is enabled.\n    ImQsort(_SrcData.Data, _SrcData.Size, sizeof(ImGuiPerfToolEntry), &PerfToolComparerByEntryInfo);\n\n    // Sort groups of entries into batches.\n    const bool combine_by_build_info = _DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo;\n    _LabelBarCounts.Data.resize(0);\n\n    // Process all batches. `entry` is always a first batch element (guaranteed by _SrcData being sorted by timestamp).\n    // At the end of this loop we fast-forward until next batch (first entry having different batch id (which is a\n    // timestamp or build info)).\n    for (ImGuiPerfToolEntry* entry = _SrcData.begin(); entry < _SrcData.end();)\n    {\n        // Filtered out entries can be safely ignored. Note that entry++ does not follow logic of fast-forwarding to the\n        // next batch, as found at the end of this loop. This is OK, because all entries belonging to a same batch will\n        // also have same date.\n        if ((_FilterDateFrom[0] && strcmp(entry->Date, _FilterDateFrom) < 0) || (_FilterDateTo[0] && strcmp(entry->Date, _FilterDateTo) > 0))\n        {\n            entry++;\n            continue;\n        }\n\n        _Batches.push_back(ImGuiPerfToolBatch());\n        ImGuiPerfToolBatch& batch = _Batches.back();\n        batch.BatchID = GetBatchID(this, entry);\n        batch.Entries.resize(num_visible_labels);\n\n        // Fill in defaults. Done once before data aggregation loop, because same entry may be touched multiple times in\n        // the following loop when entries are being combined by build info.\n        for (int i = 0; i < num_visible_labels; i++)\n        {\n            ImGuiPerfToolEntry* e = &batch.Entries.Data[i];\n            *e = *entry;\n            e->DtDeltaMs = 0;\n            e->NumSamples = 0;\n            e->LabelIndex = i;\n            e->TestName = _LabelsVisible.Data[i];\n        }\n\n        // Find perf test runs for this particular batch and accumulate them.\n        for (int i = 0; i < num_visible_labels; i++)\n        {\n            // This inner loop walks all entries that belong to current batch. Due to sorting we are sure that batch\n            // always starts with `entry`, and all entries that belong to a batch (whether we combine by build info or not)\n            // will be grouped in _SrcData.\n            ImGuiPerfToolEntry* aggregate = &batch.Entries.Data[i];\n            for (ImGuiPerfToolEntry* e = entry; e < _SrcData.end() && GetBatchID(this, e) == batch.BatchID; e++)\n            {\n                if (strcmp(e->TestName, aggregate->TestName) != 0)\n                    continue;\n                aggregate->DtDeltaMs += e->DtDeltaMs;\n                aggregate->NumSamples++;\n                aggregate->DtDeltaMsMin = ImMin(aggregate->DtDeltaMsMin, e->DtDeltaMs);\n                aggregate->DtDeltaMsMax = ImMax(aggregate->DtDeltaMsMax, e->DtDeltaMs);\n            }\n        }\n\n        // In case data is combined by build info, DtDeltaMs will be a sum of all combined entries. Average it out.\n        if (combine_by_build_info)\n            for (int i = 0; i < num_visible_labels; i++)\n            {\n                ImGuiPerfToolEntry* aggregate = &batch.Entries.Data[i];\n                if (aggregate->NumSamples > 0)\n                    aggregate->DtDeltaMs /= aggregate->NumSamples;\n            }\n\n        // Advance to the next batch.\n        batch.NumSamples = 1;\n        if (combine_by_build_info)\n        {\n            ImU64 last_timestamp = entry->Timestamp;\n            for (ImGuiID build_id = GetBuildID(entry); entry < _SrcData.end() && build_id == GetBuildID(entry);)\n            {\n                // Also count how many unique batches participate in this aggregated batch.\n                if (entry->Timestamp != last_timestamp)\n                {\n                    batch.NumSamples++;\n                    last_timestamp = entry->Timestamp;\n                }\n                entry++;\n            }\n        }\n        else\n        {\n            for (ImU64 timestamp = entry->Timestamp; entry < _SrcData.end() && timestamp == entry->Timestamp;)\n                entry++;\n        }\n    }\n\n    // Create man entries for every batch.\n    // Pushed after sorting so they are always at the start of the chart.\n    const char* mean_labels[] = { \"harmonic mean\", \"arithmetic mean\", \"geometric mean\" };\n    int num_visible_mean_labels = 0;\n    for (const char* label : mean_labels)\n    {\n        _Labels.push_back(label);\n        if (_IsVisibleTest(label))\n        {\n           _LabelsVisible.push_back(label);\n           num_visible_mean_labels++;\n        }\n    }\n    for (ImGuiPerfToolBatch& batch : _Batches)\n    {\n        double delta_sum = 0.0;\n        double delta_prd = 1.0;\n        double delta_rec = 0.0;\n        for (int i = 0; i < batch.Entries.Size; i++)\n        {\n            ImGuiPerfToolEntry* entry = &batch.Entries.Data[i];\n            delta_sum += entry->DtDeltaMs;\n            delta_prd *= entry->DtDeltaMs;\n            delta_rec += 1 / entry->DtDeltaMs;\n        }\n\n        int visible_label_i = 0;\n        for (int i = 0; i < IM_ARRAYSIZE(mean_labels); i++)\n        {\n            if (!_IsVisibleTest(mean_labels[i]))\n                continue;\n\n            batch.Entries.push_back(ImGuiPerfToolEntry());\n            ImGuiPerfToolEntry* mean_entry = &batch.Entries.back();\n            *mean_entry = batch.Entries.Data[0];\n            mean_entry->LabelIndex = _LabelsVisible.Size - num_visible_mean_labels + visible_label_i;\n            mean_entry->TestName = _LabelsVisible.Data[mean_entry->LabelIndex];\n            mean_entry->GitBranchName = \"\";\n            mean_entry->BuildType = \"\";\n            mean_entry->Compiler = \"\";\n            mean_entry->OS = \"\";\n            mean_entry->Cpu = \"\";\n            mean_entry->Date = \"\";\n            visible_label_i++;\n            if (i == 0)\n                mean_entry->DtDeltaMs = num_visible_labels / delta_rec;\n            else if (i == 1)\n                mean_entry->DtDeltaMs = delta_sum / num_visible_labels;\n            else if (i == 2)\n                mean_entry->DtDeltaMs = pow(delta_prd, 1.0 / num_visible_labels);\n            else\n                IM_ASSERT(0);\n        }\n        IM_ASSERT(batch.Entries.Size == _LabelsVisible.Size);\n    }\n\n    // Find number of bars (batches) each label will render.\n    for (ImGuiPerfToolBatch& batch : _Batches)\n    {\n        if (!_IsVisibleBuild(&batch))\n            continue;\n\n        for (ImGuiPerfToolEntry& entry : batch.Entries)\n        {\n            ImGuiID label_id = ImHashStr(entry.TestName);\n            int num_bars = _LabelBarCounts.GetInt(label_id) + 1;\n            _LabelBarCounts.SetInt(label_id, num_bars);\n        }\n    }\n\n    // Index branches, used for per-branch colors.\n    temp_set.Data.resize(0);    // ImHashStr(branch_name):linear_index\n    int branch_index_last = 0;\n    _BaselineBatchIndex = -1;\n    for (ImGuiPerfToolBatch& batch : _Batches)\n    {\n        if (batch.Entries.empty())\n            continue;\n        ImGuiPerfToolEntry* entry = &batch.Entries.Data[0];\n        ImGuiID branch_hash = ImHashStr(entry->GitBranchName);\n        batch.BranchIndex = temp_set.GetInt(branch_hash, -1);\n        if (batch.BranchIndex < 0)\n        {\n            batch.BranchIndex = branch_index_last++;\n            temp_set.SetInt(branch_hash, batch.BranchIndex);\n        }\n\n        if (_BaselineBatchIndex < 0)\n            if ((combine_by_build_info && GetBuildID(entry) == _BaselineBuildId) || _BaselineTimestamp == entry->Timestamp)\n                _BaselineBatchIndex = _Batches.index_from_ptr(&batch);\n    }\n\n    // When per-branch colors are enabled we aggregate sample counts and set them to all batches with identical build info.\n    temp_set.Data.resize(0);    // build_id:TotalSamples\n    if (_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors)\n    {\n        // Aggregate totals to temp_set.\n        for (ImGuiPerfToolBatch& batch : _Batches)\n        {\n            ImGuiID build_id = GetBuildID(&batch);\n            temp_set.SetInt(build_id, temp_set.GetInt(build_id, 0) + batch.NumSamples);\n        }\n\n        // Fill in batch sample counts.\n        for (ImGuiPerfToolBatch& batch : _Batches)\n        {\n            ImGuiID build_id = GetBuildID(&batch);\n            batch.NumSamples = temp_set.GetInt(build_id, 1);\n        }\n    }\n\n    _NumVisibleBuilds = PerfToolCountBuilds(this, true);\n    _NumUniqueBuilds = PerfToolCountBuilds(this, false);\n\n    _CalculateLegendAlignment();\n    temp_set.Data.resize(0);\n}\n\nvoid ImGuiPerfTool::Clear()\n{\n    _Labels.clear();\n    _LabelsVisible.clear();\n    _Batches.clear_destruct();\n    _Visibility.Clear();\n    _SrcData.clear_destruct();\n    _CsvParser->Clear();\n\n    ImStrncpy(_FilterDateFrom, \"9999-99-99\", IM_ARRAYSIZE(_FilterDateFrom));\n    ImStrncpy(_FilterDateTo, \"0000-00-00\", IM_ARRAYSIZE(_FilterDateFrom));\n}\n\nbool ImGuiPerfTool::LoadCSV(const char* filename)\n{\n    if (filename == nullptr)\n        filename = IMGUI_PERFLOG_DEFAULT_FILENAME;\n\n    Clear();\n\n    ImGuiCsvParser* parser = _CsvParser;\n    parser->Columns = 11;\n    if (!parser->Load(filename))\n        return false;\n\n    // Read perf test entries from CSV\n    for (int row = 0; row < parser->Rows; row++)\n    {\n        ImGuiPerfToolEntry entry;\n        int col = 0;\n        sscanf(parser->GetCell(row, col++), \"%llu\", &entry.Timestamp);\n        entry.Category = parser->GetCell(row, col++);\n        entry.TestName = parser->GetCell(row, col++);\n        sscanf(parser->GetCell(row, col++), \"%lf\", &entry.DtDeltaMs);\n        sscanf(parser->GetCell(row, col++), \"x%d\", &entry.PerfStressAmount);\n        entry.GitBranchName = parser->GetCell(row, col++);\n        entry.BuildType = parser->GetCell(row, col++);\n        entry.Cpu = parser->GetCell(row, col++);\n        entry.OS = parser->GetCell(row, col++);\n        entry.Compiler = parser->GetCell(row, col++);\n        entry.Date = parser->GetCell(row, col++);\n        AddEntry(&entry);\n    }\n\n    return true;\n}\n\nvoid ImGuiPerfTool::ViewOnly(const char** perf_names)\n{\n    // Data would not be built if we tried to view perftool of a particular test without first opening perftool via button. We need data to be built to hide perf tests.\n    if (_Batches.empty())\n        _Rebuild();\n\n    // Hide other perf tests.\n    for (const char* label : _Labels)\n    {\n        bool visible = false;\n        for (const char** p_name = perf_names; !visible && *p_name; p_name++)\n            visible |= strcmp(label, *p_name) == 0;\n        _Visibility.SetBool(ImHashStr(label), visible);\n    }\n}\n\nvoid ImGuiPerfTool::ViewOnly(const char* perf_name)\n{\n    const char* names[] = { perf_name, nullptr };\n    ViewOnly(names);\n}\n\nImGuiPerfToolEntry* ImGuiPerfTool::GetEntryByBatchIdx(int idx, const char* perf_name)\n{\n    if (idx < 0)\n        return nullptr;\n    IM_ASSERT(idx < _Batches.Size);\n    ImGuiPerfToolBatch& batch = _Batches.Data[idx];\n    for (int i = 0; i < batch.Entries.Size; i++)\n        if (ImGuiPerfToolEntry* entry = &batch.Entries.Data[i])\n            if (strcmp(entry->TestName, perf_name) == 0)\n                return entry;\n    return nullptr;\n}\n\nbool ImGuiPerfTool::_IsVisibleBuild(ImGuiPerfToolBatch* batch)\n{\n    IM_ASSERT(batch != nullptr);\n    if (batch->Entries.empty())\n        return false;   // All entries are hidden.\n    return _IsVisibleBuild(&batch->Entries.Data[0]);\n}\n\nbool ImGuiPerfTool::_IsVisibleBuild(ImGuiPerfToolEntry* entry)\n{\n    return _Visibility.GetBool(ImHashStr(entry->GitBranchName), true) &&\n        _Visibility.GetBool(ImHashStr(entry->Compiler), true) &&\n        _Visibility.GetBool(ImHashStr(entry->Cpu), true) &&\n        _Visibility.GetBool(ImHashStr(entry->OS), true) &&\n        _Visibility.GetBool(ImHashStr(entry->BuildType), true);\n}\n\nbool ImGuiPerfTool::_IsVisibleTest(const char* test_name)\n{\n    return _Visibility.GetBool(ImHashStr(test_name), true);\n}\n\nvoid ImGuiPerfTool::_CalculateLegendAlignment()\n{\n    // Estimate paddings for legend format so it looks nice and aligned\n    // FIXME: Rely on font being monospace. May need to recalculate every frame on a per-need basis based on font?\n    _AlignStress = _AlignType = _AlignCpu = _AlignOs = _AlignCompiler = _AlignBranch = _AlignSamples = 0;\n    for (ImGuiPerfToolBatch& batch : _Batches)\n    {\n        if (batch.Entries.empty())\n            continue;\n        ImGuiPerfToolEntry* entry = &batch.Entries.Data[0];\n        if (!_IsVisibleBuild(entry))\n            continue;\n        _AlignStress = ImMax(_AlignStress, (int)ceil(log10(entry->PerfStressAmount)));\n        _AlignType = ImMax(_AlignType, (int)strlen(entry->BuildType));\n        _AlignCpu = ImMax(_AlignCpu, (int)strlen(entry->Cpu));\n        _AlignOs = ImMax(_AlignOs, (int)strlen(entry->OS));\n        _AlignCompiler = ImMax(_AlignCompiler, (int)strlen(entry->Compiler));\n        _AlignBranch = ImMax(_AlignBranch, (int)strlen(entry->GitBranchName));\n        _AlignSamples = ImMax(_AlignSamples, (int)Str16f(\"%d\", entry->NumSamples).length());\n    }\n}\n\nbool ImGuiPerfTool::SaveHtmlReport(const char* file_name, const char* image_file)\n{\n    if (!ImFileCreateDirectoryChain(file_name, ImPathFindFilename(file_name)))\n        return false;\n\n    FILE* fp = fopen(file_name, \"w+\");\n    if (fp == nullptr)\n        return false;\n\n    fprintf(fp, \"<!doctype html>\\n\"\n                \"<html>\\n\"\n                \"<head>\\n\"\n                \"  <meta charset=\\\"utf-8\\\"/>\\n\"\n                \"  <title>Dear ImGui perf report</title>\\n\"\n                \"</head>\\n\"\n                \"<body>\\n\"\n                \"  <pre id=\\\"content\\\">\\n\");\n\n    // Embed performance chart.\n    fprintf(fp, \"## Dear ImGui perf report\\n\\n\");\n\n    if (image_file != nullptr)\n    {\n        FILE* fp_img = fopen(image_file, \"rb\");\n        if (fp_img != nullptr)\n        {\n            ImVector<char> image_buffer;\n            ImVector<char> base64_buffer;\n            fseek(fp_img, 0, SEEK_END);\n            image_buffer.resize((int)ftell(fp_img));\n            base64_buffer.resize(((image_buffer.Size / 3) + 1) * 4 + 1);\n            rewind(fp_img);\n            fread(image_buffer.Data, 1, image_buffer.Size, fp_img);\n            fclose(fp_img);\n            int len = ImStrBase64Encode((unsigned char*)image_buffer.Data, base64_buffer.Data, image_buffer.Size);\n            base64_buffer.Data[len] = 0;\n            fprintf(fp, \"![](data:image/png;base64,%s)\\n\\n\", base64_buffer.Data);\n        }\n    }\n\n    // Print info table.\n    const bool combine_by_build_info = _DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo;\n    for (const auto& column_info : PerfToolColumnInfo)\n        if (column_info.ShowAlways || combine_by_build_info)\n            fprintf(fp, \"| %s \", column_info.Title);\n    fprintf(fp, \"|\\n\");\n    for (const auto& column_info : PerfToolColumnInfo)\n        if (column_info.ShowAlways || combine_by_build_info)\n            fprintf(fp, \"| -- \");\n    fprintf(fp, \"|\\n\");\n\n    for (int row_index = _InfoTableSort.Size - 1; row_index >= 0; row_index--)\n    {\n        int batch_index_sorted, entry_index_sorted;\n        _UnpackSortedKey(_InfoTableSort[row_index], &batch_index_sorted, &entry_index_sorted);\n        ImGuiPerfToolBatch* batch = &_Batches[batch_index_sorted];\n        ImGuiPerfToolEntry* entry = &batch->Entries[entry_index_sorted];\n        const char* test_name = entry->TestName;\n        if (!_IsVisibleBuild(entry) || entry->NumSamples == 0)\n            continue;\n\n        ImGuiPerfToolEntry* baseline_entry = GetEntryByBatchIdx(_BaselineBatchIndex, test_name);\n        for (int i = 0; i < IM_ARRAYSIZE(PerfToolColumnInfo); i++)\n        {\n            Str30f label(\"\");\n            const ImGuiPerfToolColumnInfo& column_info = PerfToolColumnInfo[i];\n            if (column_info.ShowAlways || combine_by_build_info)\n            {\n                switch (i)\n                {\n                case 0:\n                {\n                    char date[64];\n                    FormatDateAndTime(entry->Timestamp, date, IM_ARRAYSIZE(date));\n                    fprintf(fp, \"| %s \", date);\n                    break;\n                }\n                case 1:  fprintf(fp, \"| %s \", entry->TestName);             break;\n                case 2:  fprintf(fp, \"| %s \", entry->GitBranchName);        break;\n                case 3:  fprintf(fp, \"| %s \", entry->Compiler);             break;\n                case 4:  fprintf(fp, \"| %s \", entry->OS);                   break;\n                case 5:  fprintf(fp, \"| %s \", entry->Cpu);                  break;\n                case 6:  fprintf(fp, \"| %s \", entry->BuildType);            break;\n                case 7:  fprintf(fp, \"| x%d \", entry->PerfStressAmount);    break;\n                case 8:  fprintf(fp, \"| %.2f \", entry->DtDeltaMs);          break;\n                case 9:  fprintf(fp, \"| %.2f \", entry->DtDeltaMsMin);       break;\n                case 10: fprintf(fp, \"| %.2f \", entry->DtDeltaMsMax);       break;\n                case 11: fprintf(fp, \"| %d \", entry->NumSamples);           break;\n                case 12: FormatVsBaseline(entry, baseline_entry, label); fprintf(fp, \"| %s \", label.c_str()); break;\n                default: IM_ASSERT(0); break;\n                }\n            }\n        }\n        fprintf(fp, \"|\\n\");\n    }\n\n    fprintf(fp, \"</pre>\\n\"\n                \"  <script src=\\\"https://cdn.jsdelivr.net/npm/marked@4.0.0/marked.min.js\\\"></script>\\n\"\n                \"  <script>\\n\"\n                \"    var content = document.getElementById('content');\\n\"\n                \"    content.innerHTML = marked.parse(content.innerText);\\n\"\n                \"  </script>\\n\"\n                \"</body>\\n\"\n                \"</html>\\n\");\n\n    fclose(fp);\n    return true;\n}\n\nvoid ImGuiPerfTool::_SetBaseline(int batch_index)\n{\n    IM_ASSERT(batch_index < _Batches.Size);\n    _BaselineBatchIndex = batch_index;\n    if (batch_index >= 0)\n    {\n        _BaselineTimestamp = _Batches.Data[batch_index].Entries.Data[0].Timestamp;\n        _BaselineBuildId = GetBuildID(&_Batches.Data[batch_index]);\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] USER INTERFACE\n//-------------------------------------------------------------------------\n\nvoid ImGuiPerfTool::ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open)\n{\n    if (!ImGui::Begin(\"Dear ImGui Perf Tool\", p_open))\n    {\n        ImGui::End();\n        return;\n    }\n\n    if (ImGui::IsWindowAppearing() && Empty())\n        LoadCSV();\n\n    // -----------------------------------------------------------------------------------------------------------------\n    // Render utility buttons\n    // -----------------------------------------------------------------------------------------------------------------\n\n    // Date filter\n    ImGui::AlignTextToFramePadding();\n    ImGui::TextUnformatted(\"Date Range:\");\n    ImGui::SameLine();\n\n    bool dirty = _Batches.empty();\n    bool date_changed = InputDate(\"##date-from\", _FilterDateFrom, IM_ARRAYSIZE(_FilterDateFrom),\n                                  (strcmp(_FilterDateFrom, _FilterDateTo) <= 0 || !*_FilterDateTo));\n    if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right))\n        ImGui::OpenPopup(\"InputDate From Menu\");\n    ImGui::SameLine(0, 0.0f);\n    ImGui::TextUnformatted(\"..\");\n    ImGui::SameLine(0, 0.0f);\n    date_changed |= InputDate(\"##date-to\", _FilterDateTo, IM_ARRAYSIZE(_FilterDateTo),\n                              (strcmp(_FilterDateFrom, _FilterDateTo) <= 0 || !*_FilterDateFrom));\n    if (date_changed)\n    {\n        dirty = (!_FilterDateFrom[0] || IsDateValid(_FilterDateFrom)) && (!_FilterDateTo[0] || IsDateValid(_FilterDateTo));\n        if (_FilterDateFrom[0] && _FilterDateTo[0])\n            dirty &= strcmp(_FilterDateFrom, _FilterDateTo) <= 0;\n    }\n    if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right))\n        ImGui::OpenPopup(\"InputDate To Menu\");\n    ImGui::SameLine();\n\n    for (int i = 0; i < 2; i++)\n    {\n        if (ImGui::BeginPopup(i == 0 ? \"InputDate From Menu\" : \"InputDate To Menu\"))\n        {\n            char* date = i == 0 ? _FilterDateFrom : _FilterDateTo;\n            int date_size = i == 0 ? IM_ARRAYSIZE(_FilterDateFrom) : IM_ARRAYSIZE(_FilterDateTo);\n            if (i == 0 && ImGui::MenuItem(\"Set Min\"))\n            {\n                for (ImGuiPerfToolEntry& entry : _SrcData)\n                    if (strcmp(date, entry.Date) > 0)\n                    {\n                        ImStrncpy(date, entry.Date, date_size);\n                        dirty = true;\n                    }\n            }\n            if (ImGui::MenuItem(\"Set Max\"))\n            {\n                for (ImGuiPerfToolEntry& entry : _SrcData)\n                    if (strcmp(date, entry.Date) < 0)\n                    {\n                        ImStrncpy(date, entry.Date, date_size);\n                        dirty = true;\n                    }\n            }\n            if (ImGui::MenuItem(\"Set Today\"))\n            {\n                time_t now = time(nullptr);\n                FormatDate((ImU64)now * 1000000, date, date_size);\n                dirty = true;\n            }\n            ImGui::EndPopup();\n        }\n    }\n\n    if (ImGui::Button(Str64f(\"Filter builds (%d/%d)###Filter builds\", _NumVisibleBuilds, _NumUniqueBuilds).c_str()))\n        ImGui::OpenPopup(\"Filter builds\");\n    if (ImGui::IsItemHovered())\n        ImGui::SetTooltip(\"Hide or show individual builds.\");\n    ImGui::SameLine();\n    if (ImGui::Button(Str64f(\"Filter tests (%d/%d)###Filter tests\", _LabelsVisible.Size, _Labels.Size).c_str()))\n        ImGui::OpenPopup(\"Filter perfs\");\n    if (ImGui::IsItemHovered())\n        ImGui::SetTooltip(\"Hide or show individual tests.\");\n    ImGui::SameLine();\n\n    dirty |= Button3(\"Combine\", (int*)&_DisplayType);\n    if (ImGui::IsItemHovered())\n    {\n        ImGui::BeginTooltip();\n        ImGui::RadioButton(\"Display each run separately\", _DisplayType == ImGuiPerfToolDisplayType_Simple);\n        ImGui::RadioButton(\"Use one color per branch. Disables baseline comparisons!\", _DisplayType == ImGuiPerfToolDisplayType_PerBranchColors);\n        ImGui::RadioButton(\"Combine multiple runs with same build info into one averaged build entry.\", _DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo);\n        ImGui::EndTooltip();\n    }\n\n    ImGui::SameLine();\n    if (_ReportGenerating && ImGuiTestEngine_IsTestQueueEmpty(engine))\n    {\n        _ReportGenerating = false;\n        ImOsOpenInShell(PerfToolReportDefaultOutputPath);\n    }\n    if (_Batches.empty())\n        ImGui::BeginDisabled();\n    if (ImGui::Button(\"Html Export\"))\n    {\n        // In order to capture a screenshot Report is saved by executing a \"capture_perf_report\" test.\n        _ReportGenerating = true;\n        ImGuiTestEngine_QueueTests(engine, ImGuiTestGroup_Tests, \"capture_perf_report\");\n    }\n    if (_Batches.empty())\n        ImGui::EndDisabled();\n    ImGui::SameLine();\n    if (ImGui::IsItemHovered())\n        ImGui::SetTooltip(\"Generate a report and open it in the browser.\");\n\n    // Align help button to the right.\n    ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImMax(0.0f, ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(\"(?)\").x));\n    ImGui::TextDisabled(\"(?)\");\n    if (ImGui::IsItemHovered())\n    {\n        ImGui::BeginTooltip();\n        ImGui::BulletText(\"To change baseline build, double-click desired build in the legend.\");\n        ImGui::BulletText(\"Extra information is displayed when hovering bars of a particular perf test and holding SHIFT.\");\n        ImGui::BulletText(\"Double-click plot to fit plot into available area.\");\n        ImGui::EndTooltip();\n    }\n\n    if (ImGui::BeginPopup(\"Filter builds\"))\n    {\n        ImGuiStorage& temp_set = _TempSet;\n        temp_set.Data.resize(0);    // ImHashStr(BuildProperty):seen\n\n        static const char* columns[] = { \"Branch\", \"Build\", \"CPU\", \"OS\", \"Compiler\" };\n        bool show_all = ImGui::Button(\"Show All\");\n        ImGui::SameLine();\n        bool hide_all = ImGui::Button(\"Hide All\");\n        if (ImGui::BeginTable(\"Builds\", IM_ARRAYSIZE(columns), ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit))\n        {\n            for (int i = 0; i < IM_ARRAYSIZE(columns); i++)\n                ImGui::TableSetupColumn(columns[i]);\n            ImGui::TableHeadersRow();\n\n            // Find columns with nothing checked.\n            bool checked_any[] = { false, false, false, false, false };\n            for (ImGuiPerfToolEntry& entry : _SrcData)\n            {\n                const char* properties[] = { entry.GitBranchName, entry.BuildType, entry.Cpu, entry.OS, entry.Compiler };\n                for (int i = 0; i < IM_ARRAYSIZE(properties); i++)\n                {\n                    ImGuiID hash = ImHashStr(properties[i]);\n                    checked_any[i] |= _Visibility.GetBool(hash, true);\n                }\n            }\n\n            int property_offsets[] =\n            {\n                offsetof(ImGuiPerfToolEntry, GitBranchName),\n                offsetof(ImGuiPerfToolEntry, BuildType),\n                offsetof(ImGuiPerfToolEntry, Cpu),\n                offsetof(ImGuiPerfToolEntry, OS),\n                offsetof(ImGuiPerfToolEntry, Compiler),\n            };\n\n            ImGui::TableNextRow();\n            for (int i = 0; i < IM_ARRAYSIZE(property_offsets); i++)\n            {\n                ImGui::TableSetColumnIndex(i);\n                for (ImGuiPerfToolEntry& entry : _SrcData)\n                {\n                    const char* property = *(const char**)((const char*)&entry + property_offsets[i]);\n                    ImGuiID hash = ImHashStr(property);\n                    if (temp_set.GetBool(hash))\n                        continue;\n                    temp_set.SetBool(hash, true);\n\n                    bool visible = _Visibility.GetBool(hash, true) || show_all;\n                    if (hide_all)\n                        visible = false;\n                    bool modified = ImGui::Checkbox(property, &visible) || show_all || hide_all;\n                    _Visibility.SetBool(hash, visible);\n                    if (modified)\n                    {\n                        _CalculateLegendAlignment();\n                        _NumVisibleBuilds = PerfToolCountBuilds(this, true);\n                        dirty = true;\n                    }\n                    if (!checked_any[i])\n                    {\n                        ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImColor(1.0f, 0.0f, 0.0f, 0.2f));\n                        if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered)\n                            ImGui::SetTooltip(\"Check at least one item in each column to see any data.\");\n                    }\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::EndPopup();\n    }\n\n    if (ImGui::BeginPopup(\"Filter perfs\"))\n    {\n        dirty |= RenderMultiSelectFilter(this, \"Filter by perf test\", &_Labels);\n        if (ImGui::IsKeyPressed(ImGuiKey_Escape))\n            ImGui::CloseCurrentPopup();\n        ImGui::EndPopup();\n    }\n\n    if (dirty)\n        _Rebuild();\n\n    // Rendering a plot of empty dataset is not possible.\n    if (_Batches.empty() || _LabelsVisible.Size == 0 || _NumVisibleBuilds == 0)\n    {\n        ImGui::TextUnformatted(\"No data is available. Run some perf tests or adjust filter settings.\");\n    }\n    else\n    {\n#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT\n        // Splitter between two following child windows is rendered first.\n        ImGuiStyle& style = ImGui::GetStyle();\n        float plot_height = 0.0f;\n        float& table_height = _InfoTableHeight;\n        ImGui::Splitter(\"splitter\", &plot_height, &table_height, ImGuiAxis_Y, +1);\n\n        // Double-click to move splitter to bottom\n        if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))\n        {\n            table_height = 0;\n            plot_height = ImGui::GetContentRegionAvail().y - style.ItemSpacing.y;\n            ImGui::ClearActiveID();\n        }\n\n        // Render entries plot\n        if (ImGui::BeginChild(ImGui::GetID(\"plot\"), ImVec2(0, plot_height)))\n            _ShowEntriesPlot();\n        ImGui::EndChild();\n\n        // Render entries tables\n        if (table_height > 0.0f)\n        {\n            if (ImGui::BeginChild(ImGui::GetID(\"info-table\"), ImVec2(0, table_height)))\n                _ShowEntriesTable();\n            ImGui::EndChild();\n        }\n#else\n        _ShowEntriesTable();\n#endif\n    }\n    ImGui::End();\n}\n\n#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT\nstatic double GetLabelVerticalOffset(double occupy_h, int max_visible_builds, int now_visible_builds)\n{\n    const double h = occupy_h / (float)max_visible_builds;\n    double offset = -h * ((max_visible_builds - 1) * 0.5);\n    return (double)now_visible_builds * h + offset;\n}\n#endif\n\nvoid ImGuiPerfTool::_ShowEntriesPlot()\n{\n#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT\n    ImGuiIO& io = ImGui::GetIO();\n    ImGuiStyle& style = ImGui::GetStyle();\n    Str256 label;\n    Str256 display_label;\n\n    ImPlot::PushStyleColor(ImPlotCol_AxisBgHovered, IM_COL32(0, 0, 0, 0));\n    ImPlot::PushStyleColor(ImPlotCol_AxisBgActive, IM_COL32(0, 0, 0, 0));\n    if (!ImPlot::BeginPlot(\"PerfTool\", ImVec2(-1, -1), ImPlotFlags_NoTitle))\n        return;\n\n    ImPlot::SetupAxis(ImAxis_X1, nullptr, ImPlotAxisFlags_NoTickLabels);\n    if (_LabelsVisible.Size > 1)\n    {\n        ImPlot::SetupAxisTicks(ImAxis_Y1, 0, _LabelsVisible.Size, _LabelsVisible.Size, _LabelsVisible.Data);\n    }\n    else if (_LabelsVisible.Size == 1)\n    {\n        const char* labels[] = { _LabelsVisible[0], \"\" };\n        ImPlot::SetupAxisTicks(ImAxis_Y1, 0, _LabelsVisible.Size, 2, labels);\n    }\n    ImPlot::SetupLegend(ImPlotLocation_NorthEast);\n\n    // Amount of vertical space bars of one label will occupy. 1.0 would leave no space between bars of adjacent labels.\n    const float occupy_h = 0.8f;\n\n    // Plot bars\n    bool legend_hovered = false;\n    ImGuiStorage& temp_set = _TempSet;\n    temp_set.Data.resize(0);    // ImHashStr(TestName):now_visible_builds_i\n    int current_baseline_batch_index = _BaselineBatchIndex; // Cache this value before loop, so toggling it does not create flicker.\n    for (int batch_index = 0; batch_index < _Batches.Size; batch_index++)\n    {\n        ImGuiPerfToolBatch& batch = _Batches[batch_index];\n        if (!_IsVisibleBuild(&batch.Entries.Data[0]))\n            continue;\n\n        // Plot bars.\n        label.clear();\n        display_label.clear();\n        PerfToolFormatBuildInfo(this, &label, &batch);\n        display_label.append(label.c_str());\n        ImGuiID batch_label_id;\n        bool baseline_match = false;\n        if (_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors)\n        {\n            // No \"vs baseline\" comparison for per-branch colors, because runs are combined in the legend, but not in the info table.\n            batch_label_id = GetBuildID(&batch);\n        }\n        else\n        {\n            batch_label_id = ImHashData(&batch.BatchID, sizeof(batch.BatchID));\n            baseline_match = current_baseline_batch_index == batch_index;\n        }\n        display_label.appendf(\"%s###%08X\", baseline_match ? \" *\" : \"\", batch_label_id);\n\n        // Plot all bars one by one, so batches with varying number of bars would not contain empty holes.\n        for (ImGuiPerfToolEntry& entry : batch.Entries)\n        {\n            if (entry.NumSamples == 0)\n                continue;   // Dummy entry, perf did not run for this test in this batch.\n            ImGuiID label_id = ImHashStr(entry.TestName);\n            const int max_visible_builds = _LabelBarCounts.GetInt(label_id);\n            const int now_visible_builds = temp_set.GetInt(label_id);\n            temp_set.SetInt(label_id, now_visible_builds + 1);\n            double y_pos = (double)entry.LabelIndex + GetLabelVerticalOffset(occupy_h, max_visible_builds, now_visible_builds);\n            ImPlot::SetNextFillStyle(ImPlot::GetColormapColor(_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors ? batch.BranchIndex : batch_index));\n            ImPlot::PlotBars<double>(display_label.c_str(), &entry.DtDeltaMs, &y_pos, 1, occupy_h / (double)max_visible_builds, ImPlotBarsFlags_Horizontal);\n        }\n        legend_hovered |= ImPlot::IsLegendEntryHovered(display_label.c_str());\n\n        // Set baseline.\n        if (ImPlot::IsLegendEntryHovered(display_label.c_str()))\n        {\n            if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))\n                _SetBaseline(batch_index);\n        }\n    }\n\n    // Plot highlights.\n    ImPlotContext& gp = *GImPlot;\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    _PlotHoverTest = -1;\n    _PlotHoverBatch = -1;\n    _PlotHoverTestLabel = false;\n    bool can_highlight = !legend_hovered && (ImPlot::IsPlotHovered() || ImPlot::IsAxisHovered(ImAxis_Y1));\n    ImDrawList* plot_draw_list = ImPlot::GetPlotDrawList();\n\n    // Highlight bars when hovering a label.\n    int hovered_label_index = -1;\n    for (int i = 0; i < _LabelsVisible.Size && can_highlight; i++)\n    {\n        ImRect label_rect_loose = ImPlotGetYTickRect(i);                // Rect around test label\n        ImRect label_rect_tight;                                        // Rect around test label, covering bar height and label area width\n        label_rect_tight.Min.y = ImPlot::PlotToPixels(0, (float)i + 0.5f).y;\n        label_rect_tight.Max.y = ImPlot::PlotToPixels(0, (float)i - 0.5f).y;\n        label_rect_tight.Min.x = plot.CanvasRect.Min.x;\n        label_rect_tight.Max.x = plot.PlotRect.Min.x;\n\n        ImRect rect_bars;                                               // Rect around bars only\n        rect_bars.Min.x = plot.PlotRect.Min.x;\n        rect_bars.Max.x = plot.PlotRect.Max.x;\n        rect_bars.Min.y = ImPlot::PlotToPixels(0, (float)i + 0.5f).y;\n        rect_bars.Max.y = ImPlot::PlotToPixels(0, (float)i - 0.5f).y;\n\n        // Render underline signaling it is clickable. Clicks are handled when rendering info table.\n        if (label_rect_loose.Contains(io.MousePos))\n        {\n            ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);\n            plot_draw_list->AddLine(ImFloor(label_rect_loose.GetBL()), ImFloor(label_rect_loose.GetBR()),\n                ImColor(style.Colors[ImGuiCol_Text]));\n        }\n\n        // Highlight bars belonging to hovered label.\n        if (label_rect_tight.Contains(io.MousePos))\n        {\n            plot_draw_list->AddRectFilled(rect_bars.Min, rect_bars.Max, ImColor(style.Colors[ImGuiCol_TextSelectedBg]));\n            _PlotHoverTestLabel = true;\n            _PlotHoverTest = i;\n        }\n\n        if (rect_bars.Contains(io.MousePos))\n            hovered_label_index = i;\n    }\n\n    // Highlight individual bars when hovering them on the plot or info table.\n    temp_set.Data.resize(0);    // ImHashStr(hovered_label):now_visible_builds_i\n    if (hovered_label_index < 0)\n        hovered_label_index = _TableHoveredTest;\n    if (hovered_label_index >= 0)\n    {\n        const char* hovered_label = _LabelsVisible.Data[hovered_label_index];\n        ImGuiID label_id = ImHashStr(hovered_label);\n        for (ImGuiPerfToolBatch& batch : _Batches)\n        {\n            int batch_index = _Batches.index_from_ptr(&batch);\n            if (!_IsVisibleBuild(&batch))\n                continue;\n\n            ImGuiPerfToolEntry* entry = &batch.Entries.Data[hovered_label_index];\n            if (entry->NumSamples == 0)\n                continue;   // Dummy entry, perf did not run for this test in this batch.\n\n            int max_visible_builds = _LabelBarCounts.GetInt(label_id);\n            const int now_visible_builds = temp_set.GetInt(label_id);\n            temp_set.SetInt(label_id, now_visible_builds + 1);\n            float h = occupy_h / (float)max_visible_builds;\n            float y_pos = (float)entry->LabelIndex;\n            y_pos += (float)GetLabelVerticalOffset(occupy_h, max_visible_builds, now_visible_builds);\n            ImRect rect_bar;                                                    // Rect around hovered bar only\n            rect_bar.Min.x = plot.PlotRect.Min.x;\n            rect_bar.Max.x = plot.PlotRect.Max.x;\n            rect_bar.Min.y = ImPlot::PlotToPixels(0, y_pos - h * 0.5f + h).y;   // ImPlot y_pos is for bar center, therefore we adjust positions by half-height to get a bounding box.\n            rect_bar.Max.y = ImPlot::PlotToPixels(0, y_pos - h * 0.5f).y;\n\n            // Mouse is hovering label or bars of a perf test - highlight them in info table.\n            if (_PlotHoverTest < 0 && rect_bar.Min.y <= io.MousePos.y && io.MousePos.y < rect_bar.Max.y && io.MousePos.x > plot.PlotRect.Min.x)\n            {\n                // _LabelsVisible is inverted to make perf test order match info table order. Revert it back.\n                _PlotHoverTest = hovered_label_index;\n                _PlotHoverBatch = batch_index;\n                plot_draw_list->AddRectFilled(rect_bar.Min, rect_bar.Max, ImColor(style.Colors[ImGuiCol_TextSelectedBg]));\n            }\n\n            // Mouse is hovering a row in info table - highlight relevant bars on the plot.\n            if (_TableHoveredBatch == batch_index && _TableHoveredTest == hovered_label_index)\n                plot_draw_list->AddRectFilled(rect_bar.Min, rect_bar.Max, ImColor(style.Colors[ImGuiCol_TextSelectedBg]));\n        }\n    }\n\n    if (io.KeyShift && _PlotHoverTest >= 0)\n    {\n        // Info tooltip with delta times of each batch for a hovered test.\n        const char* test_name = _LabelsVisible.Data[_PlotHoverTest];\n        ImGui::BeginTooltip();\n        float w = ImGui::CalcTextSize(test_name).x;\n        float total_w = ImGui::GetContentRegionAvail().x;\n        if (total_w > w)\n            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (total_w - w) * 0.5f);\n        ImGui::TextUnformatted(test_name);\n\n        for (int i = 0; i < _Batches.Size; i++)\n        {\n            if (ImGuiPerfToolEntry* hovered_entry = GetEntryByBatchIdx(i, test_name))\n                ImGui::Text(\"%s %.3fms\", label.c_str(), hovered_entry->DtDeltaMs);\n            else\n                ImGui::Text(\"%s --\", label.c_str());\n        }\n        ImGui::EndTooltip();\n    }\n\n    ImPlot::EndPlot();\n    ImPlot::PopStyleColor(2);\n#else\n    ImGui::TextUnformatted(\"Not enabled because ImPlot is not available (IMGUI_TEST_ENGINE_ENABLE_IMPLOT=0).\");\n#endif\n}\n\nvoid ImGuiPerfTool::_ShowEntriesTable()\n{\n    ImGuiTableFlags table_flags = ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_Sortable |\n        ImGuiTableFlags_SortMulti | ImGuiTableFlags_SortTristate | ImGuiTableFlags_Resizable |\n        ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY;\n    if (!ImGui::BeginTable(\"PerfInfo\", IM_ARRAYSIZE(PerfToolColumnInfo), table_flags))\n        return;\n\n    ImGuiStyle& style = ImGui::GetStyle();\n    int num_visible_labels = _LabelsVisible.Size;\n\n    // Test name column is not sorted because we do sorting only within perf runs of a particular tests,\n    // so as far as sorting function is concerned all items in first column are identical.\n    for (int i = 0; i < IM_ARRAYSIZE(PerfToolColumnInfo); i++)\n    {\n        const ImGuiPerfToolColumnInfo& info = PerfToolColumnInfo[i];\n        ImGuiTableColumnFlags column_flags = info.Flags;\n        if (i == 0 && _DisplayType != ImGuiPerfToolDisplayType_Simple)\n            column_flags |= ImGuiTableColumnFlags_Disabled; // Date only visible in non-combining mode.\n        if (!info.ShowAlways && _DisplayType != ImGuiPerfToolDisplayType_CombineByBuildInfo)\n            column_flags |= ImGuiTableColumnFlags_Disabled;\n        ImGui::TableSetupColumn(info.Title, column_flags);\n    }\n    ImGui::TableSetupScrollFreeze(0, 1);\n\n    if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs())\n        if (sorts_specs->SpecsDirty || _InfoTableSortDirty)\n        {\n            // Fill sort table with unsorted indices.\n            sorts_specs->SpecsDirty = _InfoTableSortDirty = false;\n\n            // Reinitialize sorting table to unsorted state.\n            _InfoTableSort.resize(num_visible_labels * _Batches.Size);\n            for (int entry_index = 0, i = 0; entry_index < num_visible_labels; entry_index++)\n                for (int batch_index = 0; batch_index < _Batches.Size; batch_index++, i++)\n                    _InfoTableSort.Data[i] = (((ImU64)batch_index * num_visible_labels + entry_index) << 24) | i;\n\n            // Sort batches of each label.\n            if (sorts_specs->SpecsCount > 0)\n            {\n                _InfoTableSortSpecs = sorts_specs;\n                PerfToolInstance = this;\n                ImQsort(_InfoTableSort.Data, (size_t)_InfoTableSort.Size, sizeof(_InfoTableSort.Data[0]), CompareWithSortSpecs);\n                _InfoTableSortSpecs = nullptr;\n                PerfToolInstance = nullptr;\n            }\n        }\n\n    ImGui::TableHeadersRow();\n\n    // ImPlot renders bars from bottom to the top. We want bars to render from top to the bottom, therefore we loop\n    // labels and batches in reverse order.\n    _TableHoveredTest = -1;\n    _TableHoveredBatch = -1;\n    const bool scroll_into_view = _PlotHoverTestLabel && ImGui::IsMouseClicked(ImGuiMouseButton_Left);\n    const float header_row_height = ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 0).GetHeight();\n    ImRect scroll_into_view_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);\n\n    for (int row_index = _InfoTableSort.Size - 1; row_index >= 0; row_index--)\n    {\n        int batch_index_sorted, entry_index_sorted;\n        _UnpackSortedKey(_InfoTableSort[row_index], &batch_index_sorted, &entry_index_sorted);\n        ImGuiPerfToolBatch* batch = &_Batches[batch_index_sorted];\n        ImGuiPerfToolEntry* entry = &batch->Entries[entry_index_sorted];\n        const char* test_name = entry->TestName;\n\n        if (!_IsVisibleBuild(entry) || !_IsVisibleTest(entry->TestName) || entry->NumSamples == 0)\n            continue;\n\n        ImGui::PushID(entry);\n        ImGui::TableNextRow();\n        if (row_index & 1)\n            ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32(ImGuiCol_TableRowBgAlt, 0.5f));\n        else\n            ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32(ImGuiCol_TableRowBg, 0.5f));\n\n        if (_PlotHoverTest == entry_index_sorted)\n        {\n            // Highlight a row that corresponds to hovered bar, or all rows that correspond to hovered perf test label.\n            if (_PlotHoverBatch == batch_index_sorted || _PlotHoverTestLabel)\n                ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImColor(style.Colors[ImGuiCol_TextSelectedBg]));\n        }\n\n        ImGuiPerfToolEntry* baseline_entry = GetEntryByBatchIdx(_BaselineBatchIndex, test_name);\n\n        // Date\n        if (ImGui::TableNextColumn())\n        {\n            char date[64];\n            FormatDateAndTime(entry->Timestamp, date, IM_ARRAYSIZE(date));\n            ImGui::TextUnformatted(date);\n        }\n\n        // Build info\n        if (ImGui::TableNextColumn())\n        {\n            // ImGuiSelectableFlags_Disabled + changing ImGuiCol_TextDisabled color prevents selectable from overriding table highlight behavior.\n            ImGui::PushStyleColor(ImGuiCol_Header, style.Colors[ImGuiCol_Text]);\n            ImGui::PushStyleColor(ImGuiCol_HeaderHovered, style.Colors[ImGuiCol_TextSelectedBg]);\n            ImGui::PushStyleColor(ImGuiCol_HeaderActive, style.Colors[ImGuiCol_TextSelectedBg]);\n            ImGui::Selectable(entry->TestName, false, ImGuiSelectableFlags_SpanAllColumns);\n            ImGui::PopStyleColor(3);\n            if (ImGui::IsItemHovered())\n            {\n                _TableHoveredTest = entry_index_sorted;\n                _TableHoveredBatch = batch_index_sorted;\n            }\n\n            if (ImGui::BeginPopupContextItem())\n            {\n                if (entry == baseline_entry)\n                    ImGui::BeginDisabled();\n                if (ImGui::MenuItem(\"Set as baseline\"))\n                    _SetBaseline(batch_index_sorted);\n                if (entry == baseline_entry)\n                    ImGui::EndDisabled();\n                ImGui::EndPopup();\n            }\n        }\n        if (ImGui::TableNextColumn())\n            ImGui::TextUnformatted(entry->GitBranchName);\n        if (ImGui::TableNextColumn())\n            ImGui::TextUnformatted(entry->Compiler);\n        if (ImGui::TableNextColumn())\n            ImGui::TextUnformatted(entry->OS);\n        if (ImGui::TableNextColumn())\n            ImGui::TextUnformatted(entry->Cpu);\n        if (ImGui::TableNextColumn())\n            ImGui::TextUnformatted(entry->BuildType);\n        if (ImGui::TableNextColumn())\n            ImGui::Text(\"x%d\", entry->PerfStressAmount);\n\n        // Avg ms\n        if (ImGui::TableNextColumn())\n            ImGui::Text(\"%.3lf\", entry->DtDeltaMs);\n\n        // Min ms\n        if (ImGui::TableNextColumn())\n            ImGui::Text(\"%.3lf\", entry->DtDeltaMsMin);\n\n        // Max ms\n        if (ImGui::TableNextColumn())\n            ImGui::Text(\"%.3lf\", entry->DtDeltaMsMax);\n\n        // Num samples\n        if (ImGui::TableNextColumn())\n            ImGui::Text(\"%d\", entry->NumSamples);\n\n        // VS Baseline\n        if (ImGui::TableNextColumn())\n        {\n            float dt_change = (float)entry->VsBaseline;\n            if (_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors)\n            {\n                ImGui::TextUnformatted(\"--\");\n            }\n            else\n            {\n                Str30 label;\n                dt_change = FormatVsBaseline(entry, baseline_entry, label);\n                ImGui::TextUnformatted(label.c_str());\n                if (dt_change != entry->VsBaseline)\n                {\n                    entry->VsBaseline = dt_change;\n                    _InfoTableSortDirty = true;             // Force re-sorting.\n                }\n            }\n        }\n\n        if (_PlotHoverTest == entry_index_sorted && scroll_into_view)\n        {\n            ImGuiTable* table = ImGui::GetCurrentTable();\n            scroll_into_view_rect.Add(ImGui::TableGetCellBgRect(table, 0));\n        }\n\n        ImGui::PopID();\n    }\n\n    if (scroll_into_view)\n    {\n        scroll_into_view_rect.Min.y -= header_row_height;   // FIXME-TABLE: Compensate for frozen header row covering a first content row scrolled into view.\n        ImGui::ScrollToRect(ImGui::GetCurrentWindow(), scroll_into_view_rect, ImGuiScrollFlags_NoScrollParent);\n    }\n\n    ImGui::EndTable();\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] SETTINGS\n//-------------------------------------------------------------------------\n\nstatic void PerflogSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler* ini_handler)\n{\n    ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData;\n    perftool->_Visibility.Clear();\n}\n\nstatic void* PerflogSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char*)\n{\n    return (void*)1;\n}\n\nstatic void PerflogSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler* ini_handler, void*, const char* line)\n{\n    ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData;\n    char buf[128];\n    int visible = -1, display_type = -1;\n    /**/ if (sscanf(line, \"DateFrom=%10s\", perftool->_FilterDateFrom)) {}\n    else if (sscanf(line, \"DateTo=%10s\", perftool->_FilterDateTo)) {}\n    else if (sscanf(line, \"DisplayType=%d\", &display_type)) { perftool->_DisplayType = (ImGuiPerfToolDisplayType)display_type; }\n    else if (sscanf(line, \"BaselineBuildId=%llu\", &perftool->_BaselineBuildId)) {}\n    else if (sscanf(line, \"BaselineTimestamp=%llu\", &perftool->_BaselineTimestamp)) {}\n    else if (sscanf(line, \"TestVisibility=%[^,],%d\", buf, &visible) == 2) { perftool->_Visibility.SetBool(ImHashStr(buf), !!visible); }\n    else if (sscanf(line, \"BuildVisibility=%[^,],%d\", buf, &visible) == 2) { perftool->_Visibility.SetBool(ImHashStr(buf), !!visible); }\n}\n\nstatic void PerflogSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler* ini_handler)\n{\n    ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData;\n    perftool->_Batches.clear_destruct();\n    perftool->_SetBaseline(-1);\n}\n\nstatic void PerflogSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler* ini_handler, ImGuiTextBuffer* buf)\n{\n    ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData;\n    if (perftool->_Batches.empty())\n        return;\n    buf->appendf(\"[%s][Data]\\n\", ini_handler->TypeName);\n    buf->appendf(\"DateFrom=%s\\n\", perftool->_FilterDateFrom);\n    buf->appendf(\"DateTo=%s\\n\", perftool->_FilterDateTo);\n    buf->appendf(\"DisplayType=%d\\n\", perftool->_DisplayType);\n    buf->appendf(\"BaselineBuildId=%llu\\n\", perftool->_BaselineBuildId);\n    buf->appendf(\"BaselineTimestamp=%llu\\n\", perftool->_BaselineTimestamp);\n    for (const char* label : perftool->_Labels)\n        buf->appendf(\"TestVisibility=%s,%d\\n\", label, perftool->_Visibility.GetBool(ImHashStr(label), true));\n\n    ImGuiStorage& temp_set = perftool->_TempSet;\n    temp_set.Data.clear();\n    for (ImGuiPerfToolEntry& entry : perftool->_SrcData)\n    {\n        const char* properties[] = { entry.GitBranchName, entry.BuildType, entry.Cpu, entry.OS, entry.Compiler };\n        for (int i = 0; i < IM_ARRAYSIZE(properties); i++)\n        {\n            ImGuiID hash = ImHashStr(properties[i]);\n            if (!temp_set.GetBool(hash))\n            {\n                temp_set.SetBool(hash, true);\n                buf->appendf(\"BuildVisibility=%s,%d\\n\", properties[i], perftool->_Visibility.GetBool(hash, true));\n            }\n        }\n    }\n    buf->append(\"\\n\");\n}\n\nvoid ImGuiPerfTool::_AddSettingsHandler()\n{\n    ImGuiSettingsHandler ini_handler;\n    ini_handler.TypeName = \"TestEnginePerfTool\";\n    ini_handler.TypeHash = ImHashStr(\"TestEnginePerfTool\");\n    ini_handler.ClearAllFn = PerflogSettingsHandler_ClearAll;\n    ini_handler.ReadOpenFn = PerflogSettingsHandler_ReadOpen;\n    ini_handler.ReadLineFn = PerflogSettingsHandler_ReadLine;\n    ini_handler.ApplyAllFn = PerflogSettingsHandler_ApplyAll;\n    ini_handler.WriteAllFn = PerflogSettingsHandler_WriteAll;\n    ini_handler.UserData = this;\n    ImGui::AddSettingsHandler(&ini_handler);\n}\n\nvoid ImGuiPerfTool::_UnpackSortedKey(ImU64 key, int* batch_index, int* entry_index, int* monotonic_index)\n{\n    IM_ASSERT(batch_index != nullptr);\n    IM_ASSERT(entry_index != nullptr);\n    const int num_visible_labels = _LabelsVisible.Size;\n    *batch_index = (int)((key >> 24) / num_visible_labels);\n    *entry_index = (int)((key >> 24) % num_visible_labels);\n    if (monotonic_index)\n        *monotonic_index = (int)(key & 0xFFFFFF);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] TESTS\n//-------------------------------------------------------------------------\n\nstatic bool SetPerfToolWindowOpen(ImGuiTestContext* ctx, bool is_open)\n{\n    ctx->MenuClick(\"//Dear ImGui Test Engine/Tools\");\n    bool was_open = ctx->ItemIsChecked(\"//$FOCUSED/Perf Tool\");\n    ctx->MenuAction(is_open ? ImGuiTestAction_Check : ImGuiTestAction_Uncheck, \"//Dear ImGui Test Engine/Tools/Perf Tool\");\n    return was_open;\n}\n\nvoid RegisterTests_TestEnginePerfTool(ImGuiTestEngine* e)\n{\n    ImGuiTest* t = nullptr;\n\n    // ## Flex perf tool code.\n    t = IM_REGISTER_TEST(e, \"testengine\", \"testengine_cov_perftool\");\n    t->GuiFunc = [](ImGuiTestContext* ctx)\n    {\n        IM_UNUSED(ctx);\n        ImGui::Begin(\"Test Func\", nullptr, ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);\n        int loop_count = 1000;\n        bool v1 = false, v2 = true;\n        for (int n = 0; n < loop_count / 2; n++)\n        {\n            ImGui::PushID(n);\n            ImGui::Checkbox(\"Hello, world\", &v1);\n            ImGui::Checkbox(\"Hello, world\", &v2);\n            ImGui::PopID();\n        }\n        ImGui::End();\n    };\n    t->TestFunc = [](ImGuiTestContext* ctx)\n    {\n        ImGuiPerfTool* perftool = ImGuiTestEngine_GetPerfTool(ctx->Engine);\n        const char* temp_perf_csv = \"output/misc_cov_perf_tool.csv\";\n\n        Str16f min_date_bkp = perftool->_FilterDateFrom;\n        Str16f max_date_bkp = perftool->_FilterDateTo;\n\n        // Execute few perf tests, serialize them to temporary csv file.\n        ctx->PerfIterations = 50; // Make faster\n        ctx->PerfCapture(\"perf\", \"misc_cov_perf_tool_1\", temp_perf_csv);\n        ctx->PerfCapture(\"perf\", \"misc_cov_perf_tool_2\", temp_perf_csv);\n\n        // Load perf data from csv file and open perf tool.\n        perftool->Clear();\n        perftool->LoadCSV(temp_perf_csv);\n        bool perf_was_open = SetPerfToolWindowOpen(ctx, true);\n        ctx->Yield();\n\n        ImGuiWindow* window = ctx->GetWindowByRef(\"Dear ImGui Perf Tool\");\n        IM_CHECK(window != nullptr);\n        ImVec2 pos_bkp = window->Pos;\n        ImVec2 size_bkp = window->Size;\n        ctx->SetRef(window);\n        ctx->WindowMove(\"\", ImVec2(50, 50));\n        ctx->WindowResize(\"\", ImVec2(1400, 900));\n#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT\n        ImGuiWindow* plot_child = ctx->WindowInfo(\"plot\").Window;  // \"plot/PerfTool\" prior to implot 2023/08/21\n        IM_CHECK(plot_child != nullptr);\n\n        // Move legend to right side.\n        ctx->MouseMoveToPos(plot_child->Rect().GetCenter());\n        ctx->MouseDoubleClick(ImGuiMouseButton_Left);               // Auto-size plots while at it\n        ctx->MouseClick(ImGuiMouseButton_Right);\n        ctx->MenuClick(\"//$FOCUSED/Legend/NE\");\n\n        // Click some stuff for more coverage.\n        ctx->MouseMoveToPos(plot_child->Rect().GetCenter());\n        ctx->KeyPress(ImGuiMod_Shift);\n#endif\n        ctx->ItemClick(\"##date-from\", ImGuiMouseButton_Right);\n        ctx->ItemClick(ctx->GetID(\"//$FOCUSED/Set Min\"));\n        ctx->ItemClick(\"##date-to\", ImGuiMouseButton_Right);\n        ctx->ItemClick(ctx->GetID(\"//$FOCUSED/Set Max\"));\n        ctx->ItemClick(\"###Filter builds\");\n        ctx->ItemClick(\"###Filter tests\");\n        ctx->ItemClick(\"Combine\", 0, ImGuiTestOpFlags_MoveToEdgeL); // Toggle thrice to leave state unchanged\n        ctx->ItemClick(\"Combine\", 0, ImGuiTestOpFlags_MoveToEdgeL);\n        ctx->ItemClick(\"Combine\", 0, ImGuiTestOpFlags_MoveToEdgeL);\n\n        // Restore original state.\n        perftool->Clear();                                           // Clear test data and load original data\n        ImFileDelete(temp_perf_csv);\n        perftool->LoadCSV();\n        ctx->Yield();\n#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT\n        ctx->MouseMoveToPos(plot_child->Rect().GetCenter());\n        ctx->MouseDoubleClick(ImGuiMouseButton_Left);               // Fit plot to original data\n#endif\n        ImStrncpy(perftool->_FilterDateFrom, min_date_bkp.c_str(), IM_ARRAYSIZE(perftool->_FilterDateFrom));\n        ImStrncpy(perftool->_FilterDateTo, max_date_bkp.c_str(), IM_ARRAYSIZE(perftool->_FilterDateTo));\n        ImGui::SetWindowPos(window, pos_bkp);\n        ImGui::SetWindowSize(window, size_bkp);\n        SetPerfToolWindowOpen(ctx, perf_was_open);                   // Restore window visibility\n    };\n\n    // ## Capture perf tool graph.\n    t = IM_REGISTER_TEST(e, \"capture\", \"capture_perf_report\");\n    t->TestFunc = [](ImGuiTestContext* ctx)\n    {\n        ImGuiPerfTool* perftool = ImGuiTestEngine_GetPerfTool(ctx->Engine);\n        const char* perf_report_image = nullptr;\n        if (!ImFileExist(IMGUI_PERFLOG_DEFAULT_FILENAME))\n        {\n            ctx->LogWarning(\"Perf tool has no data. Perf report generation was aborted.\");\n            return;\n        }\n\n        char min_date_bkp[sizeof(perftool->_FilterDateFrom)], max_date_bkp[sizeof(perftool->_FilterDateTo)];\n        ImStrncpy(min_date_bkp, perftool->_FilterDateFrom, IM_ARRAYSIZE(min_date_bkp));\n        ImStrncpy(max_date_bkp, perftool->_FilterDateTo, IM_ARRAYSIZE(max_date_bkp));\n        bool perf_was_open = SetPerfToolWindowOpen(ctx, true);\n        ctx->Yield();\n\n        ImGuiWindow* window = ctx->GetWindowByRef(\"Dear ImGui Perf Tool\");\n        IM_CHECK_SILENT(window != nullptr);\n        ImVec2 pos_bkp = window->Pos;\n        ImVec2 size_bkp = window->Size;\n        ctx->SetRef(window);\n        ctx->WindowMove(\"\", ImVec2(50, 50));\n        ctx->WindowResize(\"\", ImVec2(1400, 900));\n#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT\n        ctx->ItemDoubleClick(\"splitter\");   // Hide info table\n\n        ImGuiWindow* plot_child = ctx->WindowInfo(\"plot\").Window;  // \"plot/PerfTool\" prior to implot 2023/08/21\n        IM_CHECK(plot_child != nullptr);\n\n        // Move legend to right side.\n        ctx->MouseMoveToPos(plot_child->Rect().GetCenter());\n        ctx->MouseDoubleClick(ImGuiMouseButton_Left);               // Auto-size plots while at it\n        ctx->MouseClick(ImGuiMouseButton_Right);\n        ctx->MenuClick(\"//$FOCUSED/Legend/NE\");\n#endif\n        // Click some stuff for more coverage.\n        ctx->ItemClick(\"##date-from\", ImGuiMouseButton_Right);\n        ctx->ItemClick(ctx->GetID(\"//$FOCUSED/Set Min\"));\n        ctx->ItemClick(\"##date-to\", ImGuiMouseButton_Right);\n        ctx->ItemClick(ctx->GetID(\"//$FOCUSED/Set Max\"));\n#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT\n        // Take a screenshot.\n        ImGuiCaptureArgs* args = ctx->CaptureArgs;\n        args->InCaptureRect = plot_child->Rect();\n        ctx->CaptureAddWindow(window->ID);\n        ctx->CaptureScreenshot(ImGuiCaptureFlags_HideMouseCursor);\n        ctx->ItemDragWithDelta(\"splitter\", ImVec2(0, -180));        // Show info table\n        perf_report_image = args->InOutputFile;\n#endif\n        ImStrncpy(perftool->_FilterDateFrom, min_date_bkp, IM_ARRAYSIZE(min_date_bkp));\n        ImStrncpy(perftool->_FilterDateTo, max_date_bkp, IM_ARRAYSIZE(max_date_bkp));\n        ImGui::SetWindowPos(window, pos_bkp);\n        ImGui::SetWindowSize(window, size_bkp);\n        SetPerfToolWindowOpen(ctx, perf_was_open);                   // Restore window visibility\n\n#if !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n        const char* perf_report_output = getenv(\"CAPTURE_PERF_REPORT_OUTPUT\");\n#else\n        const char* perf_report_output = nullptr;\n#endif\n        if (perf_report_output == nullptr)\n            perf_report_output = PerfToolReportDefaultOutputPath;\n        perftool->SaveHtmlReport(perf_report_output, perf_report_image);\n    };\n}\n\n//-------------------------------------------------------------------------\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/source/imgui_te_ui.cpp",
    "content": "// dear imgui test engine\n// (ui)\n// If you run tests in an interactive or visible application, you may want to call ImGuiTestEngine_ShowTestEngineWindows()\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#define IMGUI_DEFINE_MATH_OPERATORS\n#include \"imgui_te_ui.h\"\n#include \"imgui.h\"\n#include \"imgui_internal.h\"\n#include \"imgui_te_engine.h\"\n#include \"imgui_te_context.h\"\n#include \"imgui_te_internal.h\"\n#include \"imgui_te_perftool.h\"\n#include \"thirdparty/Str/Str.h\"\n\n//-------------------------------------------------------------------------\n// TEST ENGINE: USER INTERFACE\n//-------------------------------------------------------------------------\n// - DrawTestLog() [internal]\n// - GetVerboseLevelName() [internal]\n// - ShowTestGroup() [internal]\n// - ImGuiTestEngine_ShowTestEngineWindows()\n//-------------------------------------------------------------------------\n\n// Look for \" filename:number \" in the string and add menu option to open source.\nstatic bool ParseLineAndDrawFileOpenItemForSourceFile(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end)\n{\n    const char* separator = ImStrchrRange(line_start, line_end, ':');\n    if (separator == nullptr)\n        return false;\n\n    const char* path_end = separator;\n    const char* path_begin = separator - 1;\n    while (path_begin > line_start&& path_begin[-1] != ' ')\n        path_begin--;\n    if (path_begin == path_end)\n        return false;\n\n    int line_no = -1;\n    sscanf(separator + 1, \"%d \", &line_no);\n    if (line_no == -1)\n        return false;\n\n    Str256f buf(\"Open '%.*s' at line %d\", (int)(path_end - path_begin), path_begin, line_no);\n    if (ImGui::MenuItem(buf.c_str()))\n    {\n        // FIXME-TESTS: Assume folder is same as folder of test->SourceFile!\n        const char* src_path = test->SourceFile;\n        const char* src_name = ImPathFindFilename(src_path);\n        buf.setf(\"%.*s%.*s\", (int)(src_name - src_path), src_path, (int)(path_end - path_begin), path_begin);\n\n        ImGuiTestEngine_OpenSourceFile(e, buf.c_str(), line_no);\n    }\n\n    return true;\n}\n\n// Look for \"[ ,\"]filename.png\" in the string and add menu option to open image.\nstatic bool ParseLineAndDrawFileOpenItemForImageFile(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end, const char* file_ext)\n{\n    IM_UNUSED(e);\n    IM_UNUSED(test);\n\n    const char* extension = ImStristr(line_start, line_end, file_ext, nullptr);\n    if (extension == nullptr)\n        return false;\n\n    const char* path_end = extension + strlen(file_ext);\n    const char* path_begin = extension - 1;\n    while (path_begin > line_start && path_begin[-1] != ' ' && path_begin[-1] != '\\'' && path_begin[-1] != '\\\"')\n        path_begin--;\n    if (path_begin == path_end)\n        return false;\n\n    Str256 buf;\n\n    // Open file\n    buf.setf(\"Open file: %.*s\", (int)(path_end - path_begin), path_begin);\n    if (ImGui::MenuItem(buf.c_str()))\n    {\n        buf.setf(\"%.*s\", (int)(path_end - path_begin), path_begin);\n        ImPathFixSeparatorsForCurrentOS(buf.c_str());\n        ImOsOpenInShell(buf.c_str());\n    }\n\n    // Open folder\n    const char* folder_begin = path_begin;\n    const char* folder_end = ImPathFindFilename(path_begin, path_end);\n    buf.setf(\"Open folder: %.*s\", (int)(folder_end - folder_begin), path_begin);\n    if (ImGui::MenuItem(buf.c_str()))\n    {\n        buf.setf(\"%.*s\", (int)(folder_end - folder_begin), folder_begin);\n        ImPathFixSeparatorsForCurrentOS(buf.c_str());\n        ImOsOpenInShell(buf.c_str());\n    }\n\n    return true;\n}\n\nstatic bool ParseLineAndDrawFileOpenItem(ImGuiTestEngine* e, ImGuiTest* test, const char* line_start, const char* line_end)\n{\n    if (ParseLineAndDrawFileOpenItemForSourceFile(e, test, line_start, line_end))\n        return true;\n    if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, \".png\"))\n        return true;\n    if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, \".gif\"))\n        return true;\n    if (ParseLineAndDrawFileOpenItemForImageFile(e, test, line_start, line_end, \".mp4\"))\n        return true;\n    return false;\n}\n\nstatic float GetDpiScale()\n{\n#ifdef IMGUI_HAS_VIEWPORT\n    return ImGui::GetWindowViewport()->DpiScale;\n#else\n    return 1.0f;\n#endif\n}\n\nstatic void DrawTestLog(ImGuiTestEngine* e, ImGuiTest* test)\n{\n    const ImU32 error_col = IM_COL32(255, 150, 150, 255);\n    const ImU32 warning_col = IM_COL32(240, 240, 150, 255);\n    const ImU32 unimportant_col = IM_COL32(190, 190, 190, 255);\n    const float dpi_scale = GetDpiScale();\n\n    ImGuiTestOutput* test_output = &test->Output;\n\n    ImGuiTestLog* log = &test_output->Log;\n    const char* text = log->Buffer.begin();\n    const char* text_end = log->Buffer.end();\n    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 2.0f) * dpi_scale);\n    ImGuiListClipper clipper;\n    ImGuiTestVerboseLevel max_log_level = test_output->Status == ImGuiTestStatus_Error ? e->IO.ConfigVerboseLevelOnError : e->IO.ConfigVerboseLevel;\n    int line_count = log->ExtractLinesForVerboseLevels(ImGuiTestVerboseLevel_Silent, max_log_level, nullptr);\n    int current_index_clipped = -1;\n    int current_index_abs = 0;\n    clipper.Begin(line_count);\n    while (clipper.Step())\n    {\n        for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)\n        {\n            // Advance index_by_log_level to find log entry indicated by line_no.\n            ImGuiTestLogLineInfo* line_info = nullptr;\n            while (current_index_clipped < line_no)\n            {\n                line_info = &log->LineInfo[current_index_abs];\n                if (line_info->Level <= max_log_level)\n                    current_index_clipped++;\n                current_index_abs++;\n            }\n\n            const char* line_start = text + line_info->LineOffset;\n            const char* line_end = strchr(line_start, '\\n');\n            if (line_end == nullptr)\n                line_end = text_end;\n\n            switch (line_info->Level)\n            {\n            case ImGuiTestVerboseLevel_Error:\n                ImGui::PushStyleColor(ImGuiCol_Text, error_col);\n                break;\n            case ImGuiTestVerboseLevel_Warning:\n                ImGui::PushStyleColor(ImGuiCol_Text, warning_col);\n                break;\n            case ImGuiTestVerboseLevel_Debug:\n            case ImGuiTestVerboseLevel_Trace:\n                ImGui::PushStyleColor(ImGuiCol_Text, unimportant_col);\n                break;\n            default:\n                ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32_WHITE);\n                break;\n            }\n#if IMGUI_VERSION_NUM >= 19072\n            ImGui::DebugTextUnformattedWithLocateItem(line_start, line_end);\n#else\n            ImGui::TextUnformatted(line_start, line_end);\n#endif\n            ImGui::PopStyleColor();\n\n            ImGui::PushID(line_no);\n            if (ImGui::BeginPopupContextItem(\"Context\", 1))\n            {\n                if (!ParseLineAndDrawFileOpenItem(e, test, line_start, line_end))\n                    ImGui::MenuItem(\"No options\", nullptr, false, false);\n                ImGui::EndPopup();\n            }\n            ImGui::PopID();\n        }\n    }\n    ImGui::PopStyleVar();\n}\n\n#if IMGUI_VERSION_NUM <= 18963\nnamespace ImGui\n{\n    void SetItemTooltip(const char* fmt, ...)\n    {\n        if (ImGui::IsItemHovered())\n        {\n            va_list args;\n            va_start(args, fmt);\n            ImGui::SetTooltipV(fmt, args);\n            va_end(args);\n        }\n    }\n} // namespace ImGui\n#endif\n\nstatic bool ShowTestGroupFilterTest(ImGuiTestEngine* e, ImGuiTestGroup group, const char* filter, ImGuiTest* test)\n{\n    if (test->Group != group)\n        return false;\n    if (!ImGuiTestEngine_PassFilter(test, *filter ? filter : \"all\"))\n        return false;\n    if ((e->UiFilterByStatusMask & (1 << test->Output.Status)) == 0)\n        return false;\n    return true;\n}\n\nstatic void GetFailingTestsAsString(ImGuiTestEngine* e, ImGuiTestGroup group, char separator, Str* out_string)\n{\n    IM_ASSERT(out_string != nullptr);\n    bool first = true;\n    for (int i = 0; i < e->TestsAll.Size; i++)\n    {\n        ImGuiTest* failing_test = e->TestsAll[i];\n        Str* filter = (group == ImGuiTestGroup_Tests) ? e->UiFilterTests : e->UiFilterPerfs;\n        if (failing_test->Group != group)\n            continue;\n        if (failing_test->Output.Status != ImGuiTestStatus_Error)\n            continue;\n        if (!ImGuiTestEngine_PassFilter(failing_test, filter->empty() ? \"all\" : filter->c_str()))\n            continue;\n        if (!first)\n            out_string->append(separator);\n        out_string->append(failing_test->Name);\n        first = false;\n    }\n}\n\nstatic void TestStatusButton(const char* id, const ImVec4& color, bool running, int display_counter)\n{\n    ImGuiContext& g = *GImGui;\n    ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop | ImGuiItemFlags_NoNav, true);\n    ImGui::ColorButton(id, color, ImGuiColorEditFlags_NoTooltip);\n    ImGui::PopItemFlag();\n    if (running)\n    {\n        //ImRect r = g.LastItemData.Rect;\n        ImVec2 center = g.LastItemData.Rect.GetCenter();\n        float radius = ImFloor(ImMin(g.LastItemData.Rect.GetWidth(), g.LastItemData.Rect.GetHeight()) * 0.40f);\n        float t = (float)(ImGui::GetTime() * 20.0f);\n        ImVec2 off(ImCos(t) * radius, ImSin(t) * radius);\n        ImGui::GetWindowDrawList()->AddLine(center - off, center + off, ImGui::GetColorU32(ImGuiCol_Text), 1.5f);\n        //ImGui::RenderText(r.Min + style.FramePadding + ImVec2(0, 0), &\"|\\0/\\0-\\0\\\\\"[(((ImGui::GetFrameCount() / 5) & 3) << 1)], nullptr);\n    }\n    else if (display_counter >= 0)\n    {\n        ImVec2 center = g.LastItemData.Rect.GetCenter();\n        Str30f buf(\"%d\", display_counter);\n        ImGui::GetWindowDrawList()->AddText(center - ImGui::CalcTextSize(buf.c_str()) * 0.5f, ImGui::GetColorU32(ImGuiCol_Text), buf.c_str());\n    }\n}\n\nstatic void ShowTestGroup(ImGuiTestEngine* e, ImGuiTestGroup group, Str* filter, bool run)\n{\n    ImGuiStyle& style = ImGui::GetStyle();\n    ImGuiIO& io = ImGui::GetIO();\n    const float dpi_scale = GetDpiScale();\n\n    // Colored Status button: will be displayed later below\n    // - Save position of test run status button and make space for it.\n    const ImVec2 status_button_pos = ImGui::GetCursorPos();\n    ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetFrameHeight() + style.ItemInnerSpacing.x);\n\n    //ImGui::Text(\"TESTS (%d)\", engine->TestsAll.Size);\n#if IMGUI_VERSION_NUM >= 19066\n    ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_R, ImGuiInputFlags_Tooltip | ImGuiInputFlags_RouteFromRootWindow);\n    run |= ImGui::Button(\"Run\");\n#elif IMGUI_VERSION_NUM >= 18837\n    run |= ImGui::Button(\"Run\") || ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_R);\n#if IMGUI_VERSION_NUM > 18963\n    ImGui::SetItemTooltip(\"Ctrl+R\");\n#endif\n#else\n    run |= ImGui::Button(\"Run\");\n#endif\n    if (run)\n    {\n        for (int n = 0; n < e->TestsAll.Size; n++)\n        {\n            ImGuiTest* test = e->TestsAll[n];\n            if (!ShowTestGroupFilterTest(e, group, filter->c_str(), test))\n                continue;\n            ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_None);\n        }\n    }\n    ImGui::SameLine();\n\n    {\n        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 6.0f);\n        const char* filter_by_status_desc = \"\";\n        if (e->UiFilterByStatusMask == ~0u)\n            filter_by_status_desc = \"All\";\n        else if (e->UiFilterByStatusMask == ~(1u << ImGuiTestStatus_Success))\n            filter_by_status_desc = \"Not OK\";\n        else if (e->UiFilterByStatusMask == (1u << ImGuiTestStatus_Error))\n            filter_by_status_desc = \"Errors\";\n        if (ImGui::BeginCombo(\"##filterbystatus\", filter_by_status_desc))\n        {\n            if (ImGui::Selectable(\"All\", e->UiFilterByStatusMask == ~0u))\n                e->UiFilterByStatusMask = (ImU32)~0u;\n            if (ImGui::Selectable(\"Not OK\", e->UiFilterByStatusMask == ~(1u << ImGuiTestStatus_Success)))\n                e->UiFilterByStatusMask = (ImU32)~(1u << ImGuiTestStatus_Success);\n            if (ImGui::Selectable(\"Errors\", e->UiFilterByStatusMask == (1u << ImGuiTestStatus_Error)))\n                e->UiFilterByStatusMask = (ImU32)(1u << ImGuiTestStatus_Error);\n            ImGui::EndCombo();\n        }\n    }\n\n    ImGui::SameLine();\n    const char* perflog_label = \"Perf Tool\";\n    float filter_width = ImGui::GetContentRegionAvail().x;\n    float perf_stress_factor_width = (30 * dpi_scale);\n    if (group == ImGuiTestGroup_Perfs)\n    {\n        filter_width -= style.ItemSpacing.x + perf_stress_factor_width;\n        filter_width -= style.ItemSpacing.x + style.FramePadding.x * 2 + ImGui::CalcTextSize(perflog_label).x;\n    }\n    filter_width -= ImGui::CalcTextSize(\"(?)\").x + style.ItemSpacing.x;\n    ImGui::SetNextItemWidth(ImMax(20.0f, filter_width));\n#if IMGUI_VERSION_NUM >= 19066\n    ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip | ImGuiInputFlags_RouteFromRootWindow);\n#endif\n    ImGui::InputText(\"##filter\", filter);\n    ImGui::SameLine();\n    ImGui::TextDisabled(\"(?)\");\n    ImGui::SetItemTooltip(\"Query is composed of one or more comma-separated filter terms with optional modifiers.\\n\"\n        \"Available modifiers:\\n\"\n        \"- '-' prefix excludes tests matched by the term.\\n\"\n        \"- '^' prefix anchors term matching to the start of the string.\\n\"\n        \"- '$' suffix anchors term matching to the end of the string.\");\n    if (group == ImGuiTestGroup_Perfs)\n    {\n        ImGui::SameLine();\n        ImGui::SetNextItemWidth(perf_stress_factor_width);\n        ImGui::DragInt(\"##PerfStress\", &e->IO.PerfStressAmount, 0.1f, 1, 20, \"x%d\");\n        ImGui::SetItemTooltip(\"Increase workload of performance tests (higher means longer run).\"); // FIXME: Move?\n        ImGui::SameLine();\n        if (ImGui::Button(perflog_label))\n        {\n            e->UiPerfToolOpen = true;\n            ImGui::FocusWindow(ImGui::FindWindowByName(\"Dear ImGui Perf Tool\"));\n        }\n    }\n\n    int tests_completed = 0;\n    int tests_succeeded = 0;\n    int tests_failed = 0;\n    ImVector<ImGuiTest*> tests_to_remove;\n    if (ImGui::BeginTable(\"Tests\", 3, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_SizingFixedFit))\n    {\n        ImGui::TableSetupScrollFreeze(0, 1);\n        ImGui::TableSetupColumn(\"Status\");\n        ImGui::TableSetupColumn(\"Category\");\n        ImGui::TableSetupColumn(\"Test\", ImGuiTableColumnFlags_WidthStretch);\n        ImGui::TableHeadersRow();\n\n        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6, 4) * dpi_scale);\n        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 0) * dpi_scale);\n        //ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(100, 10) * dpi_scale);\n        for (int test_n = 0; test_n < e->TestsAll.Size; test_n++)\n        {\n            ImGuiTest* test = e->TestsAll[test_n];\n            if (!ShowTestGroupFilterTest(e, group, filter->c_str(), test))\n                continue;\n\n            ImGuiTestOutput* test_output = &test->Output;\n            ImGuiTestContext* test_context = (e->TestContext && e->TestContext->Test == test) ? e->TestContext : nullptr; // Running context, if any\n\n            ImGui::TableNextRow();\n            ImGui::PushID(test_n);\n\n            // Colors match general test status colors defined below.\n            ImVec4 status_color;\n            switch (test_output->Status)\n            {\n            case ImGuiTestStatus_Error:\n                status_color = ImVec4(0.9f, 0.1f, 0.1f, 1.0f);\n                tests_completed++;\n                tests_failed++;\n                break;\n            case ImGuiTestStatus_Success:\n                status_color = ImVec4(0.1f, 0.9f, 0.1f, 1.0f);\n                tests_completed++;\n                tests_succeeded++;\n                break;\n            case ImGuiTestStatus_Queued:\n            case ImGuiTestStatus_Running:\n            case ImGuiTestStatus_Suspended:\n                if (test_context && (test_context->RunFlags & ImGuiTestRunFlags_GuiFuncOnly))\n                    status_color = ImVec4(0.8f, 0.0f, 0.8f, 1.0f);\n                else\n                    status_color = ImVec4(0.8f, 0.4f, 0.1f, 1.0f);\n                break;\n            default:\n                status_color = ImVec4(0.4f, 0.4f, 0.4f, 1.0f);\n                break;\n            }\n\n            ImGui::TableNextColumn();\n            TestStatusButton(\"status\", status_color, test_output->Status == ImGuiTestStatus_Running || test_output->Status == ImGuiTestStatus_Suspended, -1);\n            ImGui::SameLine();\n\n            bool queue_test = false;\n            bool queue_gui_func_toggle = false;\n            bool select_test = false;\n\n            if (test_output->Status == ImGuiTestStatus_Suspended)\n            {\n                // Resume IM_SUSPEND_TESTFUNC\n                // FIXME: Terrible user experience to have this here.\n                if (ImGui::Button(\"Con###Run\"))\n                    test_output->Status = ImGuiTestStatus_Running;\n                ImGui::SetItemTooltip(\"CTRL+Space to continue.\");\n                if (ImGui::IsKeyPressed(ImGuiKey_Space) && io.KeyCtrl)\n                    test_output->Status = ImGuiTestStatus_Running;\n            }\n            else\n            {\n                if (ImGui::Button(\"Run###Run\"))\n                   queue_test = select_test = true;\n            }\n\n            ImGui::TableNextColumn();\n            if (ImGui::Selectable(test->Category, test == e->UiSelectedTest, ImGuiSelectableFlags_SpanAllColumns | (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnNav))\n                select_test = true;\n\n            // Double-click to run test, CTRL+Double-click to run GUI function\n            const bool is_running_gui_func = (test_context && (test_context->RunFlags & ImGuiTestRunFlags_GuiFuncOnly));\n            const bool has_gui_func = (test->GuiFunc != nullptr);\n            if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0))\n            {\n                if (ImGui::GetIO().KeyCtrl)\n                    queue_gui_func_toggle = true;\n                else\n                    queue_test = true;\n            }\n\n            /*if (ImGui::IsItemHovered() && test->TestLog.size() > 0)\n            {\n            ImGui::BeginTooltip();\n            DrawTestLog(engine, test, false);\n            ImGui::EndTooltip();\n            }*/\n\n            if (e->UiSelectAndScrollToTest == test)\n                ImGui::SetScrollHereY();\n\n            bool view_source = false;\n            if (ImGui::BeginPopupContextItem())\n            {\n                select_test = true;\n\n                if (ImGui::MenuItem(\"Run test\"))\n                    queue_test = true;\n                if (ImGui::MenuItem(\"Run GUI func\", \"Ctrl+DblClick\", is_running_gui_func, has_gui_func))\n                    queue_gui_func_toggle = true;\n\n                ImGui::Separator();\n\n                const bool open_source_available = (test->SourceFile != nullptr) && (e->IO.SrcFileOpenFunc != nullptr);\n\n                Str128 buf;\n                if (test->SourceFile != nullptr) // This is normally set by IM_REGISTER_TEST() but custom registration may omit it.\n                    buf.setf(\"Open source (%s:%d)\", ImPathFindFilename(test->SourceFile), test->SourceLine);\n                else\n                    buf.set(\"Open source\");\n                if (ImGui::MenuItem(buf.c_str(), nullptr, false, open_source_available))\n                    ImGuiTestEngine_OpenSourceFile(e, test->SourceFile, test->SourceLine);\n                if (ImGui::MenuItem(\"View source...\", nullptr, false, test->SourceFile != nullptr))\n                    view_source = true;\n\n                if (group == ImGuiTestGroup_Perfs && ImGui::MenuItem(\"View perflog\"))\n                {\n                    e->PerfTool->ViewOnly(test->Name);\n                    e->UiPerfToolOpen = true;\n                }\n\n                ImGui::Separator();\n                if (ImGui::MenuItem(\"Copy name\", nullptr, false))\n                    ImGui::SetClipboardText(test->Name);\n\n                if (test_output->Status == ImGuiTestStatus_Error)\n                    if (ImGui::MenuItem(\"Copy names of all failing tests\"))\n                    {\n                        Str256 failing_tests;\n                        GetFailingTestsAsString(e, group, ',', &failing_tests);\n                        ImGui::SetClipboardText(failing_tests.c_str());\n                    }\n\n                ImGuiTestLog* test_log = &test_output->Log;\n                if (ImGui::BeginMenu(\"Copy log\", !test_log->IsEmpty()))\n                {\n                    for (int level_n = ImGuiTestVerboseLevel_Error; level_n < ImGuiTestVerboseLevel_COUNT; level_n++)\n                    {\n                        ImGuiTestVerboseLevel level = (ImGuiTestVerboseLevel)level_n;\n                        int count = test_log->ExtractLinesForVerboseLevels((ImGuiTestVerboseLevel)0, level, nullptr);\n                        if (ImGui::MenuItem(Str64f(\"%s (%d lines)\", ImGuiTestEngine_GetVerboseLevelName(level), count).c_str(), nullptr, false, count > 0))\n                        {\n                            ImGuiTextBuffer buffer;\n                            test_log->ExtractLinesForVerboseLevels((ImGuiTestVerboseLevel)0, level, &buffer);\n                            ImGui::SetClipboardText(buffer.c_str());\n                        }\n                    }\n                    ImGui::EndMenu();\n                }\n\n                if (ImGui::MenuItem(\"Clear log\", nullptr, false, !test_log->IsEmpty()))\n                    test_log->Clear();\n\n                // [DEBUG] Simple way to exercise ImGuiTestEngine_UnregisterTest()\n                //ImGui::Separator();\n                //if (ImGui::MenuItem(\"Remove test\"))\n                //    tests_to_remove.push_back(test);\n\n                ImGui::EndPopup();\n            }\n\n            // Process source popup\n            static ImGuiTextBuffer source_blurb;\n            static int goto_line = -1;\n            if (view_source)\n            {\n                source_blurb.clear();\n                size_t file_size = 0;\n                char* file_data = (char*)ImFileLoadToMemory(test->SourceFile, \"rb\", &file_size);\n                if (file_data)\n                    source_blurb.append(file_data, file_data + file_size);\n                else\n                    source_blurb.append(\"<Error loading sources>\");\n                goto_line = test->SourceLine;\n                ImGui::OpenPopup(\"Source\");\n            }\n            if (ImGui::BeginPopup(\"Source\"))\n            {\n                const ImVec2 start_pos = ImGui::GetCursorScreenPos();\n                const float line_height = ImGui::GetTextLineHeight();\n                if (goto_line != -1)\n                    ImGui::SetScrollY(ImMax((goto_line - 5) * line_height, 0.0f));\n                goto_line = -1;\n\n                ImRect r(0.0f, (test->SourceLine - 1) * line_height, ImGui::GetWindowWidth(), (test->SourceLineEnd - 1) * line_height);\n                ImGui::GetWindowDrawList()->AddRectFilled(start_pos + r.Min, start_pos + r.Max, IM_COL32(80, 80, 150, 100));\n\n                ImGui::TextUnformatted(source_blurb.c_str(), source_blurb.end());\n                ImGui::EndPopup();\n            }\n\n            ImGui::TableNextColumn();\n            ImGui::TextUnformatted(test->Name);\n\n            // Process selection\n            if (select_test)\n                e->UiSelectedTest = test;\n\n            // Process queuing\n            if (queue_gui_func_toggle && is_running_gui_func)\n                ImGuiTestEngine_AbortCurrentTest(e);\n            else if (queue_gui_func_toggle && !e->IO.IsRunningTests)\n                ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_RunFromGui | ImGuiTestRunFlags_GuiFuncOnly);\n            if (queue_test && !e->IO.IsRunningTests)\n                ImGuiTestEngine_QueueTest(e, test, ImGuiTestRunFlags_RunFromGui);\n\n            ImGui::PopID();\n        }\n        ImGui::Spacing();\n        ImGui::PopStyleVar(2);\n        ImGui::EndTable();\n    }\n\n    // Process removal\n    for (ImGuiTest* test : tests_to_remove)\n        ImGuiTestEngine_UnregisterTest(e, test);\n\n    // Display test status recap (colors match per-test run button colors defined above)\n    {\n        ImVec4 status_color;\n        if (tests_failed > 0)\n            status_color = ImVec4(0.9f, 0.1f, 0.1f, 1.0f); // Red\n        else if (e->IO.IsRunningTests)\n            status_color = ImVec4(0.8f, 0.4f, 0.1f, 1.0f);\n        else if (tests_succeeded > 0 && tests_completed == tests_succeeded)\n            status_color = ImVec4(0.1f, 0.9f, 0.1f, 1.0f);\n        else\n            status_color = ImVec4(0.4f, 0.4f, 0.4f, 1.0f);\n        //ImVec2 cursor_pos_bkp = ImGui::GetCursorPos();\n        ImGui::SetCursorPos(status_button_pos);\n        TestStatusButton(\"status\", status_color, false, tests_failed > 0 ? tests_failed : -1);// e->IO.IsRunningTests);\n        ImGui::SetItemTooltip(\"Filtered: %d\\n- OK: %d\\n- Errors: %d\", tests_completed, tests_succeeded, tests_failed);\n        //ImGui::SetCursorPos(cursor_pos_bkp);  // Restore cursor position for rendering further widgets\n    }\n}\n\nstatic void ImGuiTestEngine_ShowLogAndTools(ImGuiTestEngine* engine)\n{\n    ImGuiContext& g = *GImGui;\n    const float dpi_scale = GetDpiScale();\n\n    if (!ImGui::BeginTabBar(\"##tools\"))\n        return;\n\n    if (ImGui::BeginTabItem(\"LOG\"))\n    {\n        ImGuiTest* selected_test = engine->UiSelectedTest;\n\n        if (selected_test != nullptr)\n            ImGui::Text(\"Log for '%s' '%s'\", selected_test->Category, selected_test->Name);\n        else\n            ImGui::Text(\"N/A\");\n        if (ImGui::SmallButton(\"Clear\"))\n            if (selected_test)\n                selected_test->Output.Log.Clear();\n        ImGui::SameLine();\n        if (ImGui::SmallButton(\"Copy to clipboard\"))\n            if (engine->UiSelectedTest)\n                ImGui::SetClipboardText(selected_test->Output.Log.Buffer.c_str());\n        ImGui::Separator();\n\n        ImGui::BeginChild(\"Log\");\n        if (engine->UiSelectedTest)\n        {\n            DrawTestLog(engine, engine->UiSelectedTest);\n            if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())\n                ImGui::SetScrollHereY();\n        }\n        ImGui::EndChild();\n        ImGui::EndTabItem();\n    }\n\n    // Options\n    if (ImGui::BeginTabItem(\"OPTIONS\"))\n    {\n        ImGuiIO& io = ImGui::GetIO();\n        ImGui::Text(\"%.3f ms/frame (%.1f FPS)\", 1000.0f / io.Framerate, io.Framerate);\n        ImGui::Text(\"TestEngine: HookItems: %d, HookPushId: %d, InfoTasks: %d\", g.TestEngineHookItems,\n#if IMGUI_VERSION_NUM < 19229\n            g.DebugHookIdInfo != 0,\n#else\n            g.DebugHookIdInfoId != 0,\n#endif\n            engine->InfoTasks.Size);\n        ImGui::Separator();\n\n        if (ImGui::Button(\"Reboot UI context\"))\n            engine->ToolDebugRebootUiContext = true;\n\n        const ImGuiInputTextCallback filter_callback = [](ImGuiInputTextCallbackData* data) { return (data->EventChar == ',' || data->EventChar == ';') ? 1 : 0; };\n        ImGui::InputText(\"Branch/Annotation\", engine->IO.GitBranchName, IM_ARRAYSIZE(engine->IO.GitBranchName), ImGuiInputTextFlags_CallbackCharFilter, filter_callback, nullptr);\n        ImGui::SetItemTooltip(\"This will be stored in the CSV file for performance tools.\");\n\n        ImGui::Separator();\n\n        if (ImGui::TreeNode(\"Screen/video capture\"))\n        {\n            ImGui::Checkbox(\"Capture when requested by API\", &engine->IO.ConfigCaptureEnabled);\n            ImGui::SetItemTooltip(\"Enable or disable screen capture API completely.\");\n            ImGui::Checkbox(\"Capture screen on error\", &engine->IO.ConfigCaptureOnError);\n            ImGui::SetItemTooltip(\"Capture a screenshot on test failure.\");\n\n            // Fields modified by in this call will be synced to engine->CaptureContext.\n            engine->CaptureTool._ShowEncoderConfigFields(&engine->CaptureContext);\n\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Performances\"))\n        {\n            ImGui::Checkbox(\"Slow down whole app\", &engine->ToolSlowDown);\n            ImGui::SameLine(); ImGui::SetNextItemWidth(70 * dpi_scale);\n            ImGui::SliderInt(\"##ms\", &engine->ToolSlowDownMs, 0, 400, \"%d ms\");\n\n            // FIXME-TESTS: Need to be visualizing the samples/spikes.\n            double dt_1 = 1.0 / ImGui::GetIO().Framerate;\n            double fps_now = 1.0 / dt_1;\n            double dt_100 = engine->PerfDeltaTime100.GetAverage();\n            double dt_500 = engine->PerfDeltaTime500.GetAverage();\n\n            //if (engine->PerfRefDeltaTime <= 0.0 && engine->PerfRefDeltaTime.IsFull())\n            //    engine->PerfRefDeltaTime = dt_2000;\n\n            ImGui::Checkbox(\"Unthrolled\", &engine->IO.ConfigNoThrottle);\n            ImGui::SameLine();\n            if (ImGui::Button(\"Pick ref dt\"))\n                engine->PerfRefDeltaTime = dt_500;\n\n            double dt_ref = engine->PerfRefDeltaTime;\n            ImGui::Text(\"[ref dt]    %6.3f ms\", engine->PerfRefDeltaTime * 1000);\n            ImGui::Text(\"[last 001] %6.3f ms (%.1f FPS) ++ %6.3f ms\", dt_1 * 1000.0, 1.0 / dt_1, (dt_1 - dt_ref) * 1000);\n            ImGui::Text(\"[last 100] %6.3f ms (%.1f FPS) ++ %6.3f ms ~ converging in %.1f secs\", dt_100 * 1000.0, 1.0 / dt_100, (dt_1 - dt_ref) * 1000, 100.0 / fps_now);\n            ImGui::Text(\"[last 500] %6.3f ms (%.1f FPS) ++ %6.3f ms ~ converging in %.1f secs\", dt_500 * 1000.0, 1.0 / dt_500, (dt_1 - dt_ref) * 1000, 500.0 / fps_now);\n\n            //ImGui::PlotLines(\"Last 100\", &engine->PerfDeltaTime100.Samples.Data, engine->PerfDeltaTime100.Samples.Size, engine->PerfDeltaTime100.Idx, nullptr, 0.0f, dt_1000 * 1.10f, ImVec2(0.0f, ImGui::GetFontSize()));\n            ImVec2 plot_size(0.0f, ImGui::GetFrameHeight() * 3);\n            ImMovingAverage<double>* ma = &engine->PerfDeltaTime500;\n            ImGui::PlotLines(\"Last 500\",\n                [](void* data, int n) { ImMovingAverage<double>* ma = (ImMovingAverage<double>*)data; return (float)(ma->Samples[n] * 1000); },\n                ma, ma->Samples.Size, 0 * ma->Idx, nullptr, 0.0f, (float)(ImMax(dt_100, dt_500) * 1000.0 * 1.2f), plot_size);\n\n            ImGui::TreePop();\n        }\n\n        if (ImGui::TreeNode(\"Dear ImGui Configuration Flags\"))\n        {\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NavEnableKeyboard\", &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NavEnableGamepad\", &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);\n#ifdef IMGUI_HAS_DOCK\n            ImGui::Checkbox(\"io.ConfigDockingAlwaysTabBar\", &io.ConfigDockingAlwaysTabBar);\n#endif\n            ImGui::TreePop();\n        }\n\n        ImGui::EndTabItem();\n    }\n    ImGui::EndTabBar();\n}\n\nstatic void ImGuiTestEngine_ShowTestTool(ImGuiTestEngine* engine, bool* p_open)\n{\n    const float dpi_scale = GetDpiScale();\n\n    ImGui::SetNextWindowSize(ImVec2(ImGui::GetFontSize() * 50, ImGui::GetFontSize() * 40), ImGuiCond_FirstUseEver);\n    if (!ImGui::Begin(\"Dear ImGui Test Engine\", p_open, ImGuiWindowFlags_MenuBar))\n    {\n        ImGui::End();\n        return;\n    }\n\n    bool run = false;\n    if (ImGui::BeginMenuBar())\n    {\n        if (ImGui::BeginMenu(\"Tests\"))\n        {\n            // FIXME: This idiom showcases an issue with menus vs shortcuts. Would be nice if e.g. we could activate a shortcut?\n            run = ImGui::MenuItem(\"Run Visible\", \"Ctrl+R\");\n            ImGui::MenuItem(\"Filter\", \"Ctrl+F\");\n            if (p_open != NULL && ImGui::MenuItem(\"Close\"))\n                *p_open = false;\n            ImGui::EndMenu();\n        }\n        if (ImGui::BeginMenu(\"Tools\"))\n        {\n            ImGuiContext& g = *GImGui;\n            ImGui::MenuItem(\"Metrics/Debugger\", \"\", &engine->UiMetricsOpen);\n            ImGui::MenuItem(\"Debug Log\", \"\", &engine->UiDebugLogOpen);\n            ImGui::MenuItem(\"Stack Tool\", \"\", &engine->UiStackToolOpen);\n            ImGui::MenuItem(\"Item Picker\", \"\", &g.DebugItemPickerActive);\n            ImGui::Separator();\n            ImGui::MenuItem(\"Capture Tool\", \"\", &engine->UiCaptureToolOpen);\n            ImGui::MenuItem(\"Perf Tool\", \"\", &engine->UiPerfToolOpen);\n            ImGui::EndMenu();\n        }\n        ImGui::EndMenuBar();\n    }\n\n    ImGui::SetNextItemWidth(90 * dpi_scale);\n    if (ImGui::BeginCombo(\"##RunSpeed\", ImGuiTestEngine_GetRunSpeedName(engine->IO.ConfigRunSpeed), ImGuiComboFlags_None))\n    {\n        for (ImGuiTestRunSpeed level = (ImGuiTestRunSpeed)0; level < ImGuiTestRunSpeed_COUNT; level = (ImGuiTestRunSpeed)(level + 1))\n            if (ImGui::Selectable(ImGuiTestEngine_GetRunSpeedName(level), engine->IO.ConfigRunSpeed == level))\n                engine->IO.ConfigRunSpeed = level;\n        ImGui::EndCombo();\n    }\n    ImGui::SetItemTooltip(\n        \"Running speed\\n\"\n        \"- Fast: Run tests as fast as possible (no delay/vsync, teleport mouse, etc.).\\n\"\n        \"- Normal: Run tests at human watchable speed (for debugging).\\n\"\n        \"- Cinematic: Run tests with pauses between actions (for e.g. tutorials).\"\n    );\n    ImGui::SameLine();\n    ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);\n    ImGui::SameLine();\n\n    // (Would be good if we exposed horizontal layout mode..)\n    ImGui::Checkbox(\"Stop\", &engine->IO.ConfigStopOnError);\n    ImGui::SetItemTooltip(\"When hitting an error:\\n- Stop running other tests.\");\n    ImGui::SameLine();\n    ImGui::Checkbox(\"DbgBrk\", &engine->IO.ConfigBreakOnError);\n    ImGui::SetItemTooltip(\"When hitting an error:\\n- Break in debugger.\");\n    ImGui::SameLine();\n    ImGui::Checkbox(\"Capture\", &engine->IO.ConfigCaptureOnError);\n    ImGui::SetItemTooltip(\"When hitting an error:\\n- Capture screen to PNG. Right-click filename in Test Log to open.\");\n    ImGui::SameLine();\n    ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);\n    ImGui::SameLine();\n\n    ImGui::Checkbox(\"KeepGUI\", &engine->IO.ConfigKeepGuiFunc);\n    ImGui::SetItemTooltip(\"After running single test or hitting an error:\\n- Keep GUI function visible and interactive.\\n- Hold ESC to abort a running GUI function.\");\n    ImGui::SameLine();\n    bool keep_focus = !engine->IO.ConfigRestoreFocusAfterTests;\n    if (ImGui::Checkbox(\"KeepFocus\", &keep_focus))\n        engine->IO.ConfigRestoreFocusAfterTests = !keep_focus;\n    ImGui::SetItemTooltip(\"After running tests:\\n- Keep GUI current focus, instead of restoring focus to this window.\");\n\n    ImGui::SameLine();\n    ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);\n    ImGui::SameLine();\n\n    ImGui::SetNextItemWidth(70 * dpi_scale);\n    if (ImGui::BeginCombo(\"##Verbose\", ImGuiTestEngine_GetVerboseLevelName(engine->IO.ConfigVerboseLevel), ImGuiComboFlags_None))\n    {\n        for (ImGuiTestVerboseLevel level = (ImGuiTestVerboseLevel)0; level < ImGuiTestVerboseLevel_COUNT; level = (ImGuiTestVerboseLevel)(level + 1))\n            if (ImGui::Selectable(ImGuiTestEngine_GetVerboseLevelName(level), engine->IO.ConfigVerboseLevel == level))\n                engine->IO.ConfigVerboseLevel = engine->IO.ConfigVerboseLevelOnError = level;\n        ImGui::EndCombo();\n    }\n    ImGui::SetItemTooltip(\"Verbose level.\");\n\n    //ImGui::PopStyleVar();\n    ImGui::Separator();\n\n    // SPLITTER\n    // FIXME-OPT: A better splitter API supporting arbitrary number of splits would be useful.\n    float list_height = 0.0f;\n    float& log_height = engine->UiLogHeight;\n    ImGui::Splitter(\"splitter\", &list_height, &log_height, ImGuiAxis_Y, +1);\n\n    // TESTS\n    ImGui::BeginChild(\"List\", ImVec2(0, list_height), false, ImGuiWindowFlags_NoScrollbar);\n    if (ImGui::BeginTabBar(\"##Tests\", ImGuiTabBarFlags_NoTooltip))  // Add _NoPushId flag in TabBar?\n    {\n        if (ImGui::BeginTabItem(\"TESTS\", nullptr, ImGuiTabItemFlags_NoPushId))\n        {\n            ShowTestGroup(engine, ImGuiTestGroup_Tests, engine->UiFilterTests, run);\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"PERFS\", nullptr, ImGuiTabItemFlags_NoPushId))\n        {\n            ShowTestGroup(engine, ImGuiTestGroup_Perfs, engine->UiFilterPerfs, run);\n            ImGui::EndTabItem();\n        }\n        ImGui::EndTabBar();\n    }\n    ImGui::EndChild();\n    engine->UiSelectAndScrollToTest = nullptr;\n\n    // LOG & TOOLS\n    ImGui::BeginChild(\"Log\", ImVec2(0, log_height));\n    ImGuiTestEngine_ShowLogAndTools(engine);\n    ImGui::EndChild();\n\n    ImGui::End();\n}\n\nvoid    ImGuiTestEngine_ShowTestEngineWindows(ImGuiTestEngine* e, bool* p_open)\n{\n    if (e->TestsSourceLinesDirty)\n        ImGuiTestEngine_UpdateTestsSourceLines(e);\n\n    // Test Tool\n    ImGuiTestEngine_ShowTestTool(e, p_open);\n\n    // Stack Tool\n#if IMGUI_VERSION_NUM < 18993\n    if (e->UiStackToolOpen)\n        ImGui::ShowStackToolWindow(&e->UiStackToolOpen);\n#else\n    if (e->UiStackToolOpen)\n        ImGui::ShowIDStackToolWindow(&e->UiStackToolOpen);\n#endif\n\n    // Capture Tool\n    if (e->UiCaptureToolOpen)\n        e->CaptureTool.ShowCaptureToolWindow(&e->CaptureContext, &e->UiCaptureToolOpen);\n\n    // Performance tool\n    if (e->UiPerfToolOpen)\n        e->PerfTool->ShowPerfToolWindow(e, &e->UiPerfToolOpen);;\n\n    // Show Dear ImGui windows\n    // (we cannot show demo window here because it could lead to duplicate display, which demo windows isn't guarded for)\n    if (e->UiMetricsOpen)\n        ImGui::ShowMetricsWindow(&e->UiMetricsOpen);\n    if (e->UiDebugLogOpen)\n        ImGui::ShowDebugLogWindow(&e->UiDebugLogOpen);\n}\n\nvoid    ImGuiTestEngine_OpenSourceFile(ImGuiTestEngine* e, const char* source_filename, int source_line_no)\n{\n    ImGuiTestEngineIO& e_io = ImGuiTestEngine_GetIO(e);\n    if (e_io.SrcFileOpenFunc == nullptr)\n        ImOsOpenInShell(source_filename); // This is never used by imgui_test_suite but we provide it as a second layer of convenience for test engine users.\n    else\n        e_io.SrcFileOpenFunc(source_filename, source_line_no, e_io.SrcFileOpenUserData);\n\n    // Debugger output which may be double-clicked\n    // Print after opener so it appears in a neat place below e.g. DLL loading.\n    if (ImGui::GetIO().ConfigDebugIsDebuggerPresent)\n        ImOsOutputDebugString(Str256f(\"%s(%d): opening from user action.\\n\", source_filename, source_line_no).c_str());\n}\n"
  },
  {
    "path": "lib/third_party/imgui/imgui_test_engine/source/imgui_te_utils.cpp",
    "content": "// dear imgui test engine\n// (helpers/utilities. do NOT use this as a general purpose library)\n\n// This file is governed by the \"Dear ImGui Test Engine License\".\n// Details of the license are provided in the LICENSE.txt file in the same directory.\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"imgui_te_utils.h\"\n#include \"imgui.h\"\n#include \"imgui_internal.h\"\n#define STR_IMPLEMENTATION\n#include \"thirdparty/Str/Str.h\"\n\n#if defined(_WIN32)\n#if !defined(_WINDOWS_)\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n#endif\n#include <shellapi.h>   // ShellExecuteA()\n#include <stdio.h>\n#else\n#include <errno.h>\n#include <unistd.h>\n#endif\n#ifndef _MSC_VER\n#include <sys/types.h>\n#include <sys/stat.h>   // stat()\n#endif\n#ifdef __APPLE__\n#include <sys/sysctl.h>\n#endif\n\n#if defined(__linux) || defined(__linux__) || defined(__MACH__) || defined(__MSL__) || defined(__MINGW32__)\n#include <pthread.h>    // pthread_setname_np()\n#endif\n#include <chrono>       // high_resolution_clock::now()\n#include <thread>       // this_thread::sleep_for()\n\n//-----------------------------------------------------------------------------\n// Hashing Helpers\n//-----------------------------------------------------------------------------\n// - ImHashDecoratedPathParseLiteral() [internal]\n// - ImHashDecoratedPath()\n// - ImFindNextDecoratedPartInPath()\n//-----------------------------------------------------------------------------\n\n// - Parse literals encoded as \"$$xxxx/\" and incorporate into our hash based on type.\n// - $$ not passed by caller.\nstatic ImGuiID ImHashDecoratedPathParseLiteral(ImGuiID crc, const unsigned char* str, const unsigned char* str_end, const unsigned char** out_str_remaining)\n{\n    // Parse type (default to int)\n    ImGuiDataType type = ImGuiDataType_S32;\n    if (*str == '(')\n    {\n        // \"$$(int)????\" where ???? is s32 or u32\n        if (str + 5 < str_end && memcmp(str, \"(int)\", 5) == 0)\n        {\n            type = ImGuiDataType_S32;\n            str += 5;\n        }\n        // \"$$(ptr)0x????\" where ???? is ptr size\n        else if (str + 7 < str_end && memcmp(str, \"(ptr)0x\", 7) == 0)\n        {\n            type = ImGuiDataType_Pointer;\n            str += 7;\n        }\n    }\n\n    // Parse value\n    switch (type)\n    {\n    case ImGuiDataType_S32:\n    {\n        // e.g. \"$$(int)123\" for s32/u32/ImGuiID, same as PushID(int)\n        int v = 0;\n        {\n            int negative = 0;\n            if (str < str_end && *str == '-') { negative = 1; str++; }\n            if (str < str_end && *str == '+') { str++; }\n            for (char c = *str; str < str_end; c = *(++str))\n            {\n                if (c >= '0' && c <= '9') { v = (v * 10) + (c - '0'); }\n                else break;\n            }\n            if (negative)\n                v = -v;\n        }\n        crc = ~ImHashData(&v, sizeof(int), ~crc);\n        break;\n    }\n    case ImGuiDataType_Pointer:\n    {\n        // e.g. \"$$(ptr)0x1234FFFF\" for pointers, same as PushID(void*)\n        intptr_t v = 0;\n        {\n            for (char c = *str; str < str_end; c = *(++str))\n            {\n                if (c >= '0' && c <= '9')       { v = (v << 4) + (c - '0'); }\n                else if (c >= 'A' && c <= 'F')  { v = (v << 4) + 10 + (c - 'A'); }\n                else if (c >= 'a' && c <= 'f')  { v = (v << 4) + 10 + (c - 'a'); }\n                else break;\n            }\n        }\n        crc = ~ImHashData(&v, sizeof(void*), ~crc);\n        break;\n    }\n    }\n\n    // \"$$xxxx\" must always be either end of string, either leading to a next section e.g. \"$$xxxx/\"\n    IM_ASSERT(str == str_end || *str == '/');\n\n    *out_str_remaining = str;\n    return crc;\n}\n\n// Hash \"hello/world\" as if it was \"helloworld\"\n// To hash a forward slash we need to use \"hello\\\\/world\"\n//   IM_ASSERT(ImHashDecoratedPath(\"Hello/world\")   == ImHashStr(\"Helloworld\", 0));\n//   IM_ASSERT(ImHashDecoratedPath(\"Hello\\\\/world\") == ImHashStr(\"Hello/world\", 0));\n//   IM_ASSERT(ImHashDecoratedPath(\"$$1\")           == (n = 1, ImHashData(&n, sizeof(int))));\n// Adapted from ImHash(). Not particularly fast!\nstatic const ImU32 GImGuiTestEngineCrc32LookupTable[256] =\n{\n#if (IMGUI_VERSION_NUM < 19152) || defined(IMGUI_USE_LEGACY_CRC32_ADLER)\n    0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,\n    0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,\n    0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,\n    0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,\n    0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,\n    0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,\n    0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,\n    0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,\n    0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,\n    0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,\n    0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,\n    0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,\n    0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,\n    0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,\n    0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,\n    0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,\n#else\n    0x00000000,0xF26B8303,0xE13B70F7,0x1350F3F4,0xC79A971F,0x35F1141C,0x26A1E7E8,0xD4CA64EB,0x8AD958CF,0x78B2DBCC,0x6BE22838,0x9989AB3B,0x4D43CFD0,0xBF284CD3,0xAC78BF27,0x5E133C24,\n    0x105EC76F,0xE235446C,0xF165B798,0x030E349B,0xD7C45070,0x25AFD373,0x36FF2087,0xC494A384,0x9A879FA0,0x68EC1CA3,0x7BBCEF57,0x89D76C54,0x5D1D08BF,0xAF768BBC,0xBC267848,0x4E4DFB4B,\n    0x20BD8EDE,0xD2D60DDD,0xC186FE29,0x33ED7D2A,0xE72719C1,0x154C9AC2,0x061C6936,0xF477EA35,0xAA64D611,0x580F5512,0x4B5FA6E6,0xB93425E5,0x6DFE410E,0x9F95C20D,0x8CC531F9,0x7EAEB2FA,\n    0x30E349B1,0xC288CAB2,0xD1D83946,0x23B3BA45,0xF779DEAE,0x05125DAD,0x1642AE59,0xE4292D5A,0xBA3A117E,0x4851927D,0x5B016189,0xA96AE28A,0x7DA08661,0x8FCB0562,0x9C9BF696,0x6EF07595,\n    0x417B1DBC,0xB3109EBF,0xA0406D4B,0x522BEE48,0x86E18AA3,0x748A09A0,0x67DAFA54,0x95B17957,0xCBA24573,0x39C9C670,0x2A993584,0xD8F2B687,0x0C38D26C,0xFE53516F,0xED03A29B,0x1F682198,\n    0x5125DAD3,0xA34E59D0,0xB01EAA24,0x42752927,0x96BF4DCC,0x64D4CECF,0x77843D3B,0x85EFBE38,0xDBFC821C,0x2997011F,0x3AC7F2EB,0xC8AC71E8,0x1C661503,0xEE0D9600,0xFD5D65F4,0x0F36E6F7,\n    0x61C69362,0x93AD1061,0x80FDE395,0x72966096,0xA65C047D,0x5437877E,0x4767748A,0xB50CF789,0xEB1FCBAD,0x197448AE,0x0A24BB5A,0xF84F3859,0x2C855CB2,0xDEEEDFB1,0xCDBE2C45,0x3FD5AF46,\n    0x7198540D,0x83F3D70E,0x90A324FA,0x62C8A7F9,0xB602C312,0x44694011,0x5739B3E5,0xA55230E6,0xFB410CC2,0x092A8FC1,0x1A7A7C35,0xE811FF36,0x3CDB9BDD,0xCEB018DE,0xDDE0EB2A,0x2F8B6829,\n    0x82F63B78,0x709DB87B,0x63CD4B8F,0x91A6C88C,0x456CAC67,0xB7072F64,0xA457DC90,0x563C5F93,0x082F63B7,0xFA44E0B4,0xE9141340,0x1B7F9043,0xCFB5F4A8,0x3DDE77AB,0x2E8E845F,0xDCE5075C,\n    0x92A8FC17,0x60C37F14,0x73938CE0,0x81F80FE3,0x55326B08,0xA759E80B,0xB4091BFF,0x466298FC,0x1871A4D8,0xEA1A27DB,0xF94AD42F,0x0B21572C,0xDFEB33C7,0x2D80B0C4,0x3ED04330,0xCCBBC033,\n    0xA24BB5A6,0x502036A5,0x4370C551,0xB11B4652,0x65D122B9,0x97BAA1BA,0x84EA524E,0x7681D14D,0x2892ED69,0xDAF96E6A,0xC9A99D9E,0x3BC21E9D,0xEF087A76,0x1D63F975,0x0E330A81,0xFC588982,\n    0xB21572C9,0x407EF1CA,0x532E023E,0xA145813D,0x758FE5D6,0x87E466D5,0x94B49521,0x66DF1622,0x38CC2A06,0xCAA7A905,0xD9F75AF1,0x2B9CD9F2,0xFF56BD19,0x0D3D3E1A,0x1E6DCDEE,0xEC064EED,\n    0xC38D26C4,0x31E6A5C7,0x22B65633,0xD0DDD530,0x0417B1DB,0xF67C32D8,0xE52CC12C,0x1747422F,0x49547E0B,0xBB3FFD08,0xA86F0EFC,0x5A048DFF,0x8ECEE914,0x7CA56A17,0x6FF599E3,0x9D9E1AE0,\n    0xD3D3E1AB,0x21B862A8,0x32E8915C,0xC083125F,0x144976B4,0xE622F5B7,0xF5720643,0x07198540,0x590AB964,0xAB613A67,0xB831C993,0x4A5A4A90,0x9E902E7B,0x6CFBAD78,0x7FAB5E8C,0x8DC0DD8F,\n    0xE330A81A,0x115B2B19,0x020BD8ED,0xF0605BEE,0x24AA3F05,0xD6C1BC06,0xC5914FF2,0x37FACCF1,0x69E9F0D5,0x9B8273D6,0x88D28022,0x7AB90321,0xAE7367CA,0x5C18E4C9,0x4F48173D,0xBD23943E,\n    0xF36E6F75,0x0105EC76,0x12551F82,0xE03E9C81,0x34F4F86A,0xC69F7B69,0xD5CF889D,0x27A40B9E,0x79B737BA,0x8BDCB4B9,0x988C474D,0x6AE7C44E,0xBE2DA0A5,0x4C4623A6,0x5F16D052,0xAD7D5351\n#endif\n};\n\nImGuiID ImHashDecoratedPath(const char* str, const char* str_end, ImGuiID seed)\n{\n    const ImU32* crc32_lut = GImGuiTestEngineCrc32LookupTable;\n\n    // Prefixing the string with / ignore the seed\n    if (str != str_end && str[0] == '/')\n        seed = 0;\n\n    seed = ~seed;\n    ImU32 crc = seed;\n\n    // Focus for non-zero terminated string for consistency\n    if (str_end == nullptr)\n        str_end = str + strlen(str);\n\n    bool inhibit_one = false;\n    bool new_section = true;\n    const unsigned char* current = (const unsigned char*)str;\n    while (current < (const unsigned char*)str_end)\n    {\n        const unsigned char c = *current++;\n\n        // Backslash to inhibit special behavior of following character\n        if (c == '\\\\' && !inhibit_one)\n        {\n            inhibit_one = true;\n            continue;\n        }\n\n        // Forward slashes are ignored unless prefixed with a backward slash\n        if (c == '/' && !inhibit_one)\n        {\n            inhibit_one = false;\n            new_section = true;\n            seed = crc; // Set seed to the new path\n            continue;\n        }\n\n        // $$ at the beginning of a section to encode literals.\n        // - Currently: \"$$????\" = hash of 1 as int\n        // - May add pointers and other types.\n        if (c == '$' && current[0] == '$' && !inhibit_one && new_section)\n        {\n            crc = ImHashDecoratedPathParseLiteral(crc, current + 1, (const unsigned char*)str_end, &current);\n            continue;\n        }\n\n        // Reset the hash when encountering ###\n        if (c == '#' && current[0] == '#' && current[1] == '#')\n            crc = seed;\n\n        // Hash byte\n        crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];\n\n        inhibit_one = new_section = false;\n    }\n    return ~crc;\n}\n\n// Returns a next element of decorated hash path.\n//    \"//hello/world/child\" --> \"world/child\"\n//    \"world/child\"         --> \"child\"\n// This is a helper for code needing to do some parsing of individual nodes in a path.\n// Note: we need the (unsigned char*) stuff in order to keep code similar to ImHashDecoratedPath(). They are not really necessary in this function tho.\nconst char* ImFindNextDecoratedPartInPath(const char* str, const char* str_end)\n{\n    const unsigned char* current = (const unsigned char*)str;\n    while (*current == '/')\n        current++;\n\n    bool inhibit_one = false;\n    while (true)\n    {\n        if (str_end != nullptr && current == (const unsigned char*)str_end)\n            break;\n\n        const unsigned char c = *current++;\n        if (c == 0)\n            break;\n        if (c == '\\\\' && !inhibit_one)\n        {\n            inhibit_one = true;\n            continue;\n        }\n\n        // Forward slashes are ignored unless prefixed with a backward slash\n        if (c == '/' && !inhibit_one)\n            return (const char*)current;\n\n        inhibit_one = false;\n    }\n    return nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// File/Directory Helpers\n//-----------------------------------------------------------------------------\n// - ImFileExist()\n// - ImFileCreateDirectoryChain()\n// - ImFileFindInParents()\n// - ImFileLoadSourceBlurb()\n//-----------------------------------------------------------------------------\n\n#if _WIN32\nstatic const char IM_DIR_SEPARATOR = '\\\\';\n\nstatic void ImUtf8ToWideChar(const char* multi_byte, ImVector<wchar_t>* buf)\n{\n    const int wsize = ::MultiByteToWideChar(CP_UTF8, 0, multi_byte, -1, nullptr, 0);\n    buf->resize(wsize);\n    ::MultiByteToWideChar(CP_UTF8, 0, multi_byte, -1, (wchar_t*)buf->Data, wsize);\n}\n#else\nstatic const char IM_DIR_SEPARATOR = '/';\n#endif\n\nbool ImFileExist(const char* filename)\n{\n    struct stat dir_stat;\n    int ret = stat(filename, &dir_stat);\n    return (ret == 0);\n}\n\nbool ImFileDelete(const char* filename)\n{\n#if _WIN32\n    ImVector<wchar_t> buf;\n    ImUtf8ToWideChar(filename, &buf);\n    return ::DeleteFileW(&buf[0]) == TRUE;\n#else\n    unlink(filename);\n#endif\n    return false;\n}\n\n// Create directories for specified path. Slashes will be replaced with platform directory separators.\n// e.g. ImFileCreateDirectoryChain(\"aaaa/bbbb/cccc.png\")\n// will try to create \"aaaa/\" then \"aaaa/bbbb/\".\nbool ImFileCreateDirectoryChain(const char* path, const char* path_end)\n{\n    IM_ASSERT(path != nullptr);\n    IM_ASSERT(path[0] != 0);\n\n    if (path_end == nullptr)\n        path_end = path + strlen(path);\n\n    // Copy in a local, zero-terminated buffer\n    size_t path_len = (size_t)(path_end - path);\n    char* path_local = (char*)IM_ALLOC(path_len + 1);\n    memcpy(path_local, path, path_len);\n    path_local[path_len] = 0;\n\n#if defined(_WIN32)\n    ImVector<wchar_t> buf;\n#endif\n    // Modification of passed file_name allows us to avoid extra temporary memory allocation.\n    // strtok() pokes \\0 into places where slashes are, we create a directory using directory_name and restore slash.\n    for (char* token = strtok(path_local, \"\\\\/\"); token != nullptr; token = strtok(nullptr, \"\\\\/\"))\n    {\n        // strtok() replaces slashes with NULLs. Overwrite removed slashes here with the type of slashes the OS needs (win32 functions need backslashes).\n        if (token != path_local)\n            *(token - 1) = IM_DIR_SEPARATOR;\n\n#if defined(_WIN32)\n        // Use ::CreateDirectoryW() because ::CreateDirectoryA() treat filenames in the local code-page instead of UTF-8\n        // We cannot use ImWchar, which can be 32bits if IMGUI_USE_WCHAR32 (and CreateDirectoryW require 16bits wchar)\n        int filename_wsize = MultiByteToWideChar(CP_UTF8, 0, path_local, -1, nullptr, 0);\n        buf.resize(filename_wsize);\n        MultiByteToWideChar(CP_UTF8, 0, path_local, -1, &buf[0], filename_wsize);\n        if (!::CreateDirectoryW((wchar_t*)&buf[0], nullptr) && GetLastError() != ERROR_ALREADY_EXISTS)\n#else\n        if (mkdir(path_local, S_IRWXU) != 0 && errno != EEXIST)\n#endif\n        {\n            IM_FREE(path_local);\n            return false;\n        }\n    }\n    IM_FREE(path_local);\n    return true;\n}\n\nbool ImFileFindInParents(const char* sub_path, int max_parent_count, Str* output)\n{\n    IM_ASSERT(sub_path != nullptr);\n    IM_ASSERT(output != nullptr);\n    for (int parent_level = 0; parent_level < max_parent_count; parent_level++)\n    {\n        output->clear();\n        for (int j = 0; j < parent_level; j++)\n            output->append(\"../\");\n        output->append(sub_path);\n        if (ImFileExist(output->c_str()))\n            return true;\n    }\n    output->clear();\n    return false;\n}\n\nbool ImFileLoadSourceBlurb(const char* file_name, int line_no_start, int line_no_end, ImGuiTextBuffer* out_buf)\n{\n    size_t file_size = 0;\n    char* file_begin = (char*)ImFileLoadToMemory(file_name, \"rb\", &file_size, 1);\n    if (file_begin == nullptr)\n        return false;\n\n    char* file_end = file_begin + file_size;\n    int line_no = 0;\n    const char* test_src_begin = nullptr;\n    const char* test_src_end = nullptr;\n    for (const char* p = file_begin; p < file_end; )\n    {\n        line_no++;\n        const char* line_begin = p;\n        const char* line_end = ImStrchrRange(line_begin + 1, file_end, '\\n');\n        if (line_end == nullptr)\n            line_end = file_end;\n        if (line_no >= line_no_start && line_no <= line_no_end)\n        {\n            if (test_src_begin == nullptr)\n                test_src_begin = line_begin;\n            test_src_end = ImMax(test_src_end, line_end);\n        }\n        p = line_end + 1;\n    }\n\n    if (test_src_begin != nullptr)\n        out_buf->append(test_src_begin, test_src_end);\n    else\n        out_buf->clear();\n\n    ImGui::MemFree(file_begin);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Path Helpers\n//-----------------------------------------------------------------------------\n// - ImPathFindFilename()\n// - ImPathFindFileExt()\n// - ImPathFixSeparatorsForCurrentOS()\n//-----------------------------------------------------------------------------\n\nconst char* ImPathFindFilename(const char* path, const char* path_end)\n{\n    IM_ASSERT(path != nullptr);\n    if (!path_end)\n        path_end = path + strlen(path);\n    const char* p = path_end;\n    while (p > path)\n    {\n        if (p[-1] == '/' || p[-1] == '\\\\')\n            break;\n        p--;\n    }\n    return p;\n}\n\n// \"folder/filename\" -> return pointer to \"\" (end of string)\n// \"folder/filename.png\" -> return pointer to \".png\"\n// \"folder/filename.png.bak\" -> return pointer to \".png.bak\"\nconst char* ImPathFindExtension(const char* path, const char* path_end)\n{\n    if (!path_end)\n        path_end = path + strlen(path);\n    const char* filename = ImPathFindFilename(path, path_end);\n    const char* p = filename;\n    while (p < path_end)\n    {\n        if (p[0] == '.')\n            break;\n        p++;\n    }\n    return p;\n}\n\nvoid ImPathFixSeparatorsForCurrentOS(char* buf)\n{\n#ifdef _WIN32\n    for (char* p = buf; *p != 0; p++)\n        if (*p == '/')\n            *p = '\\\\';\n#else\n    for (char* p = buf; *p != 0; p++)\n        if (*p == '\\\\')\n            *p = '/';\n#endif\n}\n\n//-----------------------------------------------------------------------------\n// String Helpers\n//-----------------------------------------------------------------------------\n\nstatic const char* ImStrStr(const char* haystack, size_t hlen, const char* needle, int nlen)\n{\n    const char* end = haystack + hlen;\n    const char* p = haystack;\n    while ((p = (const char*)memchr(p, *needle, end - p)) != nullptr)\n    {\n        if (end - p < nlen)\n            return nullptr;\n        if (memcmp(p, needle, nlen) == 0)\n            return p;\n        p++;\n    }\n    return nullptr;\n}\n\nvoid ImStrReplace(Str* s, const char* find, const char* repl)\n{\n    IM_ASSERT(find != nullptr && *find);\n    IM_ASSERT(repl != nullptr);\n    int find_len = (int)strlen(find);\n    int repl_len = (int)strlen(repl);\n    int repl_diff = repl_len - find_len;\n\n    // Estimate required length of new buffer if string size increases.\n    int need_capacity = s->capacity();\n    int num_matches = INT_MAX;\n    if (repl_diff > 0)\n    {\n        num_matches = 0;\n        need_capacity = s->length();\n        for (char* p = s->c_str(), *end = s->c_str() + s->length(); p != nullptr && p < end;)\n        {\n            p = (char*)ImStrStr(p, end - p, find, find_len);\n            if (p)\n            {\n                need_capacity += repl_diff;\n                p += find_len;\n                num_matches++;\n            }\n        }\n    }\n\n    if (num_matches == 0)\n        return;\n\n    const char* not_owned_data = s->owned() ? nullptr : s->c_str();\n    if (!s->owned() || need_capacity > s->capacity())\n        s->reserve(need_capacity);\n    if (not_owned_data != nullptr)\n        s->set(not_owned_data);\n\n    // Replace data.\n    for (char* p = s->c_str(), *end = s->c_str() + s->length(); p != nullptr && p < end && num_matches--;)\n    {\n        p = (char*)ImStrStr(p, end - p, find, find_len);\n        if (p)\n        {\n            memmove(p + repl_len, p + find_len, end - p - find_len + 1);\n            memcpy(p, repl, repl_len);\n            p += repl_len;\n            end += repl_diff;\n        }\n    }\n}\n\nconst char* ImStrchrRangeWithEscaping(const char* str, const char* str_end, char find_c)\n{\n    while (str < str_end)\n    {\n        const char c = *str;\n        if (c == '\\\\')\n        {\n            str += 2;\n            continue;\n        }\n        if (c == find_c)\n            return str;\n        str++;\n    }\n    return nullptr;\n}\n\n// Suboptimal but ok for the data size we are dealing with (see commit on 2022/08/22 for a faster and more complicated version)\nvoid ImStrXmlEscape(Str* s)\n{\n    ImStrReplace(s, \"&\", \"&amp;\");\n    ImStrReplace(s, \"<\", \"&lt;\");\n    ImStrReplace(s, \">\", \"&gt;\");\n    ImStrReplace(s, \"\\\"\", \"&quot;\");\n    ImStrReplace(s, \"\\'\", \"&apos;\");\n}\n\n// Based on code from https://github.com/EddieBreeg/C_b64 by @EddieBreeg.\nint ImStrBase64Encode(const unsigned char* src, char* dst, int length)\n{\n    static const char* b64Table = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\";\n    int i, j, k, l, encoded_len = 0;\n\n    while (length > 0)\n    {\n        switch (length)\n        {\n        case 1:\n            i = src[0] >> 2;\n            j = (src[0] & 3) << 4;\n            k = 64;\n            l = 64;\n            break;\n        case 2:\n            i = src[0] >> 2;\n            j = ((src[0] & 3) << 4) | (src[1] >> 4);\n            k = (src[1] & 15) << 2;\n            l = 64;\n            break;\n        default:\n            i = src[0] >> 2;\n            j = ((src[0] & 3) << 4) | (src[1] >> 4);\n            k = ((src[1] & 0xf) << 2) | (src[2] >> 6 & 3);\n            l = src[2] & 0x3f;\n            break;\n        }\n        dst[0] = b64Table[i];\n        dst[1] = b64Table[j];\n        dst[2] = b64Table[k];\n        dst[3] = b64Table[l];\n        src += 3;\n        dst += 4;\n        length -= 3;\n        encoded_len += 4;\n    }\n    return encoded_len;\n}\n\n//-----------------------------------------------------------------------------\n// Parsing Helpers\n//-----------------------------------------------------------------------------\n// - ImParseSplitCommandLine()\n// - ImParseFindIniSection()\n//-----------------------------------------------------------------------------\n\nvoid    ImParseExtractArgcArgvFromCommandLine(int* out_argc, char const*** out_argv, const char* cmd_line)\n{\n    size_t cmd_line_len = strlen(cmd_line);\n\n    int n = 1;\n    {\n        const char* p = cmd_line;\n        while (*p != 0)\n        {\n            const char* arg = p;\n            while (*arg == ' ')\n                arg++;\n            const char* arg_end = strchr(arg, ' ');\n            if (arg_end == nullptr)\n                p = arg_end = cmd_line + cmd_line_len;\n            else\n                p = arg_end + 1;\n            n++;\n        }\n    }\n\n    int argc = n;\n    char const** argv = (char const**)malloc(sizeof(char*) * ((size_t)argc + 1) + (cmd_line_len + 1));\n    IM_ASSERT(argv != nullptr);\n    char* cmd_line_dup = (char*)argv + sizeof(char*) * ((size_t)argc + 1);\n    strcpy(cmd_line_dup, cmd_line);\n\n    {\n        argv[0] = \"main.exe\";\n        argv[argc] = nullptr;\n\n        char* p = cmd_line_dup;\n        for (n = 1; n < argc; n++)\n        {\n            char* arg = p;\n            char* arg_end = strchr(arg, ' ');\n            if (arg_end == nullptr)\n                p = arg_end = cmd_line_dup + cmd_line_len;\n            else\n                p = arg_end + 1;\n            argv[n] = arg;\n            arg_end[0] = 0;\n        }\n    }\n\n    *out_argc = argc;\n    *out_argv = argv;\n}\n\nbool    ImParseFindIniSection(const char* ini_config, const char* header, ImVector<char>* result)\n{\n    IM_ASSERT(ini_config != nullptr);\n    IM_ASSERT(header != nullptr);\n    IM_ASSERT(result != nullptr);\n\n    size_t ini_len = strlen(ini_config);\n    size_t header_len = strlen(header);\n\n    IM_ASSERT(header_len > 0);\n\n    if (ini_len == 0)\n        return false;\n\n    const char* section_start = strstr(ini_config, header);\n    if (section_start == nullptr)\n        return false;\n\n    const char* section_end = strstr(section_start + header_len, \"\\n[\");\n    if (section_end == nullptr)\n        section_end = section_start + ini_len;\n\n    // \"\\n[\" matches next header start on all platforms, but it cuts new line marker in half on windows.\n    if (*(section_end - 1) == '\\r')\n        --section_end;\n\n    size_t section_len = (size_t)(section_end - section_start);\n    result->resize((int)section_len + 1);\n    ImStrncpy(result->Data, section_start, section_len);\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Time Helpers\n//-----------------------------------------------------------------------------\n// - ImTimeGetInMicroseconds()\n// - ImTimestampToISO8601()\n//-----------------------------------------------------------------------------\n\nuint64_t ImTimeGetInMicroseconds()\n{\n    // Trying std::chrono out of unfettered optimism that it may actually work..\n    using namespace std;\n    chrono::microseconds ms = chrono::duration_cast<chrono::microseconds>(chrono::high_resolution_clock::now().time_since_epoch());\n    return (uint64_t)ms.count();\n}\n\nvoid ImTimestampToISO8601(uint64_t timestamp, Str* out_date)\n{\n    time_t unix_time = (time_t)(timestamp / 1000000); // Convert to seconds.\n    tm* time = gmtime(&unix_time);\n    const char* time_format = \"%Y-%m-%dT%H:%M:%S\";\n\n    // max_size \"maximum number of characters to be copied to ptr, including the terminating null-character.\"\n    // return   \"returns the total number of characters copied to ptr (not including the terminating null-character)\"\n    size_t size_req = strftime(out_date->c_str(), out_date->capacity() + 1, time_format, time);\n    if (size_req > (size_t)out_date->capacity())\n    {\n        out_date->reserve((int)size_req);\n        strftime(out_date->c_str(), out_date->capacity() + 1, time_format, time);\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Threading Helpers\n//-----------------------------------------------------------------------------\n// - ImThreadSleepInMilliseconds()\n// - ImThreadSetCurrentThreadDescription()\n//-----------------------------------------------------------------------------\n\nvoid ImThreadSleepInMilliseconds(int ms)\n{\n    using namespace std;\n    this_thread::sleep_for(chrono::milliseconds(ms));\n}\n\n#if defined(_MSC_VER)\n// Helper function for setting thread name on Win32\n// This is a separate function because __try cannot coexist with local objects that need destructors called on stack unwind\nstatic void ImThreadSetCurrentThreadDescriptionWin32OldStyle(const char* description)\n{\n    // Old-style Win32 thread name setting method\n    // See https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code\n    const DWORD MS_VC_EXCEPTION = 0x406D1388;\n#pragma pack(push,8)\n    typedef struct tagTHREADNAME_INFO\n    {\n        DWORD dwType; // Must be 0x1000.\n        LPCSTR szName; // Pointer to name (in user addr space).\n        DWORD dwThreadID; // Thread ID (-1=caller thread).\n        DWORD dwFlags; // Reserved for future use, must be zero.\n    } THREADNAME_INFO;\n#pragma pack(pop)\n\n    THREADNAME_INFO info;\n    info.dwType = 0x1000;\n    info.szName = description;\n    info.dwThreadID = (DWORD)-1;\n    info.dwFlags = 0;\n#pragma warning(push)\n#pragma warning(disable: 6320 6322)\n    __try\n    {\n        RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);\n    }\n    __except (EXCEPTION_EXECUTE_HANDLER)\n    {\n    }\n#pragma warning(pop)\n}\n#endif // #ifdef _WIN32\n\n// Set the description (name) of the current thread for debugging purposes\nvoid ImThreadSetCurrentThreadDescription(const char* description)\n{\n#if defined(_MSC_VER) // Windows + Visual Studio\n    // New-style thread name setting\n    // Only supported from Win 10 version 1607/Server 2016 onwards, hence the need for dynamic linking\n\n    typedef HRESULT(WINAPI* SetThreadDescriptionFunc)(HANDLE hThread, PCWSTR lpThreadDescription);\n\n    SetThreadDescriptionFunc set_thread_description = (SetThreadDescriptionFunc)::GetProcAddress(GetModuleHandleA(\"Kernel32.dll\"), \"SetThreadDescription\");\n    if (set_thread_description)\n    {\n        ImVector<ImWchar> buf;\n        const int description_wsize = ImTextCountCharsFromUtf8(description, nullptr) + 1;\n        buf.resize(description_wsize);\n        ImTextStrFromUtf8(&buf[0], description_wsize, description, nullptr);\n        set_thread_description(::GetCurrentThread(), (wchar_t*)&buf[0]);\n    }\n\n    // Also do the old-style method too even if the new-style one worked, as the two work in slightly different sets of circumstances\n    ImThreadSetCurrentThreadDescriptionWin32OldStyle(description);\n#elif defined(__linux) || defined(__linux__) || defined(__MINGW32__) // Linux or MingW\n    pthread_setname_np(pthread_self(), description);\n#elif defined(__MACH__) || defined(__MSL__) // OSX\n    pthread_setname_np(description);\n#else\n    // This is a nice-to-have rather than critical functionality, so fail silently if we don't support this platform\n#endif\n}\n\n//-----------------------------------------------------------------------------\n// Build info helpers\n//-----------------------------------------------------------------------------\n// - ImBuildGetCompilationInfo()\n// - ImBuildGetGitBranchName()\n//-----------------------------------------------------------------------------\n\n// Turn __DATE__ \"Jan 10 2019\" into \"2019-01-10\"\nstatic void ImBuildParseDateFromCompilerIntoYMD(const char* in_date, char* out_buf, size_t out_buf_size)\n{\n    char month_str[5];\n    int year, month, day;\n    sscanf(in_date, \"%3s %d %d\", month_str, &day, &year);\n    const char month_names[] = \"JanFebMarAprMayJunJulAugSepOctNovDec\";\n    const char* p = strstr(month_names, month_str);\n    month = p ? (int)(1 + (p - month_names) / 3) : 0;\n    ImFormatString(out_buf, out_buf_size, \"%04d-%02d-%02d\", year, month, day);\n}\n\n// Those strings are used to output easily identifiable markers in compare logs. We only need to support what we use for testing.\n// We can probably grab info in eaplatform.h/eacompiler.h etc. in EASTL\nconst ImBuildInfo* ImBuildGetCompilationInfo()\n{\n    static ImBuildInfo build_info;\n\n    if (build_info.Type[0] == '\\0')\n    {\n        // Build Type\n#if defined(DEBUG) || defined(_DEBUG)\n        build_info.Type = \"Debug\";\n#else\n        build_info.Type = \"Release\";\n#endif\n\n        // CPU\n#if defined(_M_X86) || defined(_M_IX86) || defined(__i386) || defined(__i386__) || defined(_X86_) || defined(_M_AMD64) || defined(_AMD64_) || defined(__x86_64__)\n        build_info.Cpu = (sizeof(size_t) == 4) ? \"X86\" : \"X64\";\n#elif defined(__aarch64__) || (defined(_M_ARM64) && defined(_WIN64))\n        build_info.Cpu = \"ARM64\";\n#elif defined(__EMSCRIPTEN__)\n        build_info.Cpu = \"WebAsm\";\n#else\n        build_info.Cpu = (sizeof(size_t) == 4) ? \"Unknown32\" : \"Unknown64\";\n#endif\n\n        // Platform/OS\n#if defined(_WIN32)\n        build_info.OS = \"Windows\";\n#elif defined(__linux) || defined(__linux__)\n        build_info.OS = \"Linux\";\n#elif defined(__MACH__) || defined(__MSL__)\n        build_info.OS = \"OSX\";\n#elif defined(__ORBIS__)\n        build_info.OS = \"PS4\";\n#elif defined(__PROSPERO__)\n        build_info.OS = \"PS5\";\n#elif defined(_DURANGO)\n        build_info.OS = \"XboxOne\";\n#else\n        build_info.OS = \"Unknown\";\n#endif\n\n        // Compiler\n#if defined(_MSC_VER)\n        build_info.Compiler = \"MSVC\";\n#elif defined(__clang__)\n        build_info.Compiler = \"Clang\";\n#elif defined(__GNUC__)\n        build_info.Compiler = \"GCC\";\n#else\n        build_info.Compiler = \"Unknown\";\n#endif\n\n        // Date/Time\n        ImBuildParseDateFromCompilerIntoYMD(__DATE__, build_info.Date, IM_ARRAYSIZE(build_info.Date));\n        build_info.Time = __TIME__;\n    }\n\n    return &build_info;\n}\n\nbool ImBuildFindGitBranchName(const char* git_repo_path, Str* branch_name)\n{\n    IM_ASSERT(git_repo_path != nullptr);\n    IM_ASSERT(branch_name != nullptr);\n    Str256f head_path(\"%s/.git/HEAD\", git_repo_path);\n    size_t head_size = 0;\n    bool result = false;\n    if (char* git_head = (char*)ImFileLoadToMemory(head_path.c_str(), \"r\", &head_size, 1))\n    {\n        const char prefix[] = \"ref: refs/heads/\";       // Branch name is prefixed with this in HEAD file.\n        const int prefix_length = IM_ARRAYSIZE(prefix) - 1;\n        strtok(git_head, \"\\r\\n\");                       // Trim new line\n        if (head_size > prefix_length && strncmp(git_head, prefix, prefix_length) == 0)\n        {\n            // \"ref: refs/heads/master\" -> \"master\"\n            branch_name->set(git_head + prefix_length);\n        }\n        else\n        {\n            // Should be git hash, keep first 8 characters (see #42)\n            branch_name->setf(\"%.8s\", git_head);\n        }\n        result = true;\n        IM_FREE(git_head);\n    }\n    return result;\n}\n\n//-----------------------------------------------------------------------------\n// Operating System Helpers\n//-----------------------------------------------------------------------------\n// - ImOsCreateProcess()\n// - ImOsPOpen()\n// - ImOsPClose()\n// - ImOsOpenInShell()\n// - ImOsConsoleSetTextColor()\n// - ImOsIsDebuggerPresent()\n//-----------------------------------------------------------------------------\n\nbool    ImOsCreateProcess(const char* cmd_line)\n{\n#if defined(_WIN32) && !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n    STARTUPINFOA siStartInfo;\n    PROCESS_INFORMATION piProcInfo;\n    ZeroMemory(&siStartInfo, sizeof(STARTUPINFOA));\n    char* cmd_line_copy = ImStrdup(cmd_line);\n    BOOL ret = ::CreateProcessA(nullptr, cmd_line_copy, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &siStartInfo, &piProcInfo);\n    free(cmd_line_copy);\n    ::CloseHandle(siStartInfo.hStdInput);\n    ::CloseHandle(siStartInfo.hStdOutput);\n    ::CloseHandle(siStartInfo.hStdError);\n    ::CloseHandle(piProcInfo.hProcess);\n    ::CloseHandle(piProcInfo.hThread);\n    return ret != 0;\n#else\n    IM_UNUSED(cmd_line);\n    IM_ASSERT(0);\n    return false;\n#endif\n}\n\nFILE*       ImOsPOpen(const char* cmd_line, const char* mode)\n{\n    IM_ASSERT(cmd_line != nullptr && *cmd_line);\n    IM_ASSERT(mode != nullptr && *mode);\n#if defined(_WIN32) && !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n    ImVector<wchar_t> w_cmd_line;\n    ImVector<wchar_t> w_mode;\n    ImUtf8ToWideChar(cmd_line, &w_cmd_line);\n    ImUtf8ToWideChar(mode, &w_mode);\n    w_mode.resize(w_mode.Size + 1);\n    wcscat(w_mode.Data, L\"b\");   // Windows requires 'b' mode while unixes do not support it and default to binary.\n    return _wpopen(w_cmd_line.Data, w_mode.Data);\n#elif !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n    return popen(cmd_line, mode);\n#else\n    IM_ASSERT(0);\n    return NULL;\n#endif\n}\n\nvoid        ImOsPClose(FILE* fp)\n{\n    IM_ASSERT(fp != nullptr);\n#if defined(_WIN32) && !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n    _pclose(fp);\n#elif !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n    pclose(fp);\n#else\n    IM_ASSERT(0);\n#endif\n}\n\nvoid    ImOsOpenInShell(const char* path)\n{\n    Str256 command(path);\n#if defined(_WIN32) && !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n    ImPathFixSeparatorsForCurrentOS(command.c_str());\n    ::ShellExecuteA(nullptr, \"open\", command.c_str(), nullptr, nullptr, SW_SHOWDEFAULT);\n#elif !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n#if __APPLE__\n    const char* open_executable = \"open\";\n#else\n    const char* open_executable = \"xdg-open\";\n#endif\n    command.setf(\"%s \\\"%s\\\"\", open_executable, path);\n    ImPathFixSeparatorsForCurrentOS(command.c_str());\n    system(command.c_str());\n#else\n    IM_UNUSED(path);\n    IM_ASSERT(0);\n#endif\n}\n\nvoid    ImOsConsoleSetTextColor(ImOsConsoleStream stream, ImOsConsoleTextColor color)\n{\n#if defined(_WIN32) && !IMGUI_TEST_ENGINE_IS_GAME_CONSOLE\n    HANDLE hConsole = 0;\n    switch (stream)\n    {\n    case ImOsConsoleStream_StandardOutput: hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE); break;\n    case ImOsConsoleStream_StandardError:  hConsole = ::GetStdHandle(STD_ERROR_HANDLE);  break;\n    }\n    WORD wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;\n    switch (color)\n    {\n    case ImOsConsoleTextColor_Black:        wAttributes = 0x00; break;\n    case ImOsConsoleTextColor_White:        wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break;\n    case ImOsConsoleTextColor_BrightWhite:  wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;\n    case ImOsConsoleTextColor_BrightRed:    wAttributes = FOREGROUND_RED | FOREGROUND_INTENSITY; break;\n    case ImOsConsoleTextColor_BrightGreen:  wAttributes = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;\n    case ImOsConsoleTextColor_BrightBlue:   wAttributes = FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;\n    case ImOsConsoleTextColor_BrightYellow: wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;\n    default: IM_ASSERT(0);\n    }\n    ::SetConsoleTextAttribute(hConsole, wAttributes);\n#elif defined(__linux) || defined(__linux__) || defined(__MACH__) || defined(__MSL__)\n    // FIXME: check system capabilities (with environment variable TERM)\n    FILE* handle = 0;\n    switch (stream)\n    {\n    case ImOsConsoleStream_StandardOutput: handle = stdout; break;\n    case ImOsConsoleStream_StandardError:  handle = stderr; break;\n    }\n\n    const char* modifier = \"\";\n    switch (color)\n    {\n    case ImOsConsoleTextColor_Black:        modifier = \"\\033[30m\";   break;\n    case ImOsConsoleTextColor_White:        modifier = \"\\033[0m\";    break;\n    case ImOsConsoleTextColor_BrightWhite:  modifier = \"\\033[1;37m\"; break;\n    case ImOsConsoleTextColor_BrightRed:    modifier = \"\\033[1;31m\"; break;\n    case ImOsConsoleTextColor_BrightGreen:  modifier = \"\\033[1;32m\"; break;\n    case ImOsConsoleTextColor_BrightBlue:   modifier = \"\\033[1;34m\"; break;\n    case ImOsConsoleTextColor_BrightYellow: modifier = \"\\033[1;33m\"; break;\n    default: IM_ASSERT(0);\n    }\n\n    fprintf(handle, \"%s\", modifier);\n#else\n    IM_UNUSED(stream);\n    IM_UNUSED(color);\n#endif\n}\n\nbool    ImOsIsDebuggerPresent()\n{\n#ifdef _WIN32\n    return ::IsDebuggerPresent() != 0;\n#elif defined(__linux__)\n    int debugger_pid = 0;\n    char buf[2048];                                 // TracerPid is located near the start of the file. If end of the buffer gets cut off thats fine.\n    FILE* fp = fopen(\"/proc/self/status\", \"rb\");    // Can not use ImFileLoadToMemory because size detection of /proc/self/status would fail.\n    if (fp == nullptr)\n        return false;\n    fread(buf, 1, IM_ARRAYSIZE(buf), fp);\n    fclose(fp);\n    buf[IM_ARRAYSIZE(buf) - 1] = 0;\n    if (char* tracer_pid = strstr(buf, \"TracerPid:\"))\n    {\n        tracer_pid += 10;   // Skip label\n        while (isspace(*tracer_pid))\n            tracer_pid++;\n        debugger_pid = atoi(tracer_pid);\n    }\n    return debugger_pid != 0;\n#elif defined(__APPLE__)\n    // https://stackoverflow.com/questions/2200277/detecting-debugger-on-mac-os-x\n    int                 junk;\n    int                 mib[4];\n    struct kinfo_proc   info;\n    size_t              size;\n\n    // Initialize mib, which tells sysctl the info we want, in this case\n    // we're looking for information about a specific process ID.\n    info.kp_proc.p_flag = 0;\n    mib[0] = CTL_KERN;\n    mib[1] = KERN_PROC;\n    mib[2] = KERN_PROC_PID;\n    mib[3] = getpid();\n\n    size = sizeof(info);\n    junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0);\n    IM_ASSERT(junk == 0);\n\n    // We're being debugged if the P_TRACED flag is set.\n    return (info.kp_proc.p_flag & P_TRACED) != 0;\n#else\n    // FIXME\n    return false;\n#endif\n}\n\nvoid    ImOsOutputDebugString(const char* message)\n{\n#ifdef _WIN32\n    OutputDebugStringA(message);\n#else\n    IM_UNUSED(message);\n#endif\n}\n\n//-----------------------------------------------------------------------------\n// Str.h + InputText bindings\n//-----------------------------------------------------------------------------\n\nstruct InputTextCallbackStr_UserData\n{\n    Str*                    StrObj;\n    ImGuiInputTextCallback  ChainCallback;\n    void*                   ChainCallbackUserData;\n};\n\nstatic int InputTextCallbackStr(ImGuiInputTextCallbackData* data)\n{\n    InputTextCallbackStr_UserData* user_data = (InputTextCallbackStr_UserData*)data->UserData;\n    if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)\n    {\n        // Resize string callback\n        // If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want.\n        Str* str = user_data->StrObj;\n        IM_ASSERT(data->Buf == str->c_str());\n        str->reserve(data->BufTextLen);\n        data->Buf = (char*)str->c_str();\n    }\n    else if (user_data->ChainCallback)\n    {\n        // Forward to user callback, if any\n        data->UserData = user_data->ChainCallbackUserData;\n        return user_data->ChainCallback(data);\n    }\n    return 0;\n}\n\n// Draw an extra colored frame over the previous item\n// Similar to DebugDrawItemRect() but use Max(1.0f, FrameBorderSize)\nvoid ImGui::ItemErrorFrame(ImU32 col)\n{\n    ImGuiContext& g = *GetCurrentContext();\n    ImDrawList* drawlist = GetWindowDrawList();\n    ImGuiStyle& style = GetStyle();\n    // FIXME: GetItemRectMin() / GetItemRectMax() will include label. NavRect is not probably defined :(\n    drawlist->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, GetColorU32(col), style.FrameRounding, ImDrawFlags_None, ImMax(1.0f, style.FrameBorderSize));\n}\n\nbool ImGui::InputText(const char* label, Str* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)\n{\n    IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);\n    flags |= ImGuiInputTextFlags_CallbackResize;\n\n    InputTextCallbackStr_UserData cb_user_data;\n    cb_user_data.StrObj = str;\n    cb_user_data.ChainCallback = callback;\n    cb_user_data.ChainCallbackUserData = user_data;\n    return InputText(label, (char*)str->c_str(), (size_t)str->capacity() + 1, flags, InputTextCallbackStr, &cb_user_data);\n}\n\nbool ImGui::InputTextWithHint(const char* label, const char* hint, Str* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)\n{\n    IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);\n    flags |= ImGuiInputTextFlags_CallbackResize;\n\n    InputTextCallbackStr_UserData cb_user_data;\n    cb_user_data.StrObj = str;\n    cb_user_data.ChainCallback = callback;\n    cb_user_data.ChainCallbackUserData = user_data;\n    return InputTextWithHint(label, hint, (char*)str->c_str(), (size_t)str->capacity() + 1, flags, InputTextCallbackStr, &cb_user_data);\n}\n\nbool ImGui::InputTextMultiline(const char* label, Str* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)\n{\n    IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);\n    flags |= ImGuiInputTextFlags_CallbackResize;\n\n    InputTextCallbackStr_UserData cb_user_data;\n    cb_user_data.StrObj = str;\n    cb_user_data.ChainCallback = callback;\n    cb_user_data.ChainCallbackUserData = user_data;\n    return InputTextMultiline(label, (char*)str->c_str(), (size_t)str->capacity() + 1, size, flags, InputTextCallbackStr, &cb_user_data);\n}\n\n// anchor parameter indicates which split would retain it's constant size.\n// anchor = 0 - both splits resize when parent container size changes. Both value_1 and value_2 should be persistent.\n// anchor = -1 - top/left split would have a constant size. bottom/right split would resize when parent container size changes. value_1 should be persistent, value_2 will always be recalculated from value_1.\n// anchor = +1 - bottom/right split would have a constant size. top/left split would resize when parent container size changes. value_2 should be persistent, value_1 will always be recalculated from value_2.\nbool ImGui::Splitter(const char* id, float* value_1, float* value_2, int axis, int anchor, float min_size_0, float min_size_1)\n{\n    // FIXME-DOGFOODING: This needs further refining.\n    // FIXME-SCROLL: When resizing either we'd like to keep scroll focus on something (e.g. last clicked item for list, bottom for log)\n    // See https://github.com/ocornut/imgui/issues/319\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n    ImGuiWindow* window = ImGui::GetCurrentWindow();\n    if (min_size_0 < 0)\n        min_size_0 = ImGui::GetFrameHeight();\n    if (min_size_1)\n        min_size_1 = ImGui::GetFrameHeight();\n\n    IM_ASSERT(axis == ImGuiAxis_X || axis == ImGuiAxis_Y);\n\n    float& v_1 = *value_1;\n    float& v_2 = *value_2;\n    ImRect splitter_bb;\n    const float avail = axis == ImGuiAxis_X ? ImGui::GetContentRegionAvail().x - style.ItemSpacing.x : ImGui::GetContentRegionAvail().y - style.ItemSpacing.y;\n    if (anchor < 0)\n    {\n        v_2 = ImMax(avail - v_1, min_size_1);   // First split is constant size.\n    }\n    else if (anchor > 0)\n    {\n        v_1 = ImMax(avail - v_2, min_size_0);   // Second split is constant size.\n    }\n    else\n    {\n        float r = v_1 / (v_1 + v_2);            // Both splits maintain same relative size to parent.\n        v_1 = IM_ROUND(avail * r) - 1;\n        v_2 = IM_ROUND(avail * (1.0f - r)) - 1;\n    }\n    if (axis == ImGuiAxis_X)\n    {\n        float x = window->DC.CursorPos.x + v_1 + IM_ROUND(style.ItemSpacing.x * 0.5f);\n        splitter_bb = ImRect(x - 1, window->WorkRect.Min.y, x + 1, window->WorkRect.Max.y);\n    }\n    else if (axis == ImGuiAxis_Y)\n    {\n        float y = window->DC.CursorPos.y + v_1 + IM_ROUND(style.ItemSpacing.y * 0.5f);\n        splitter_bb = ImRect(window->WorkRect.Min.x, y - 1, window->WorkRect.Max.x, y + 1);\n    }\n    return ImGui::SplitterBehavior(splitter_bb, ImGui::GetID(id), (ImGuiAxis)axis, &v_1, &v_2, min_size_0, min_size_1, 3.0f);\n}\n\n// FIXME-TESTS: Should eventually remove.\nImFont* ImGui::FindFontByPrefix(const char* prefix)\n{\n    ImGuiContext& g = *GImGui;\n    for (ImFont* font : g.IO.Fonts->Fonts)\n#if IMGUI_VERSION_NUM < 19184\n        if (strncmp(font->ConfigData->Name, prefix, strlen(prefix)) == 0)\n#elif defined(IMGUI_HAS_TEXTURES)\n        if (strncmp(font->Sources[0]->Name, prefix, strlen(prefix)) == 0)\n#else\n        if (strncmp(font->Sources[0].Name, prefix, strlen(prefix)) == 0)\n#endif\n            return font;\n    return nullptr;\n}\n\n// Legacy version support\n#if IMGUI_VERSION_NUM < 18924\nconst char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)\n{\n    return tab_bar->GetTabName(tab);\n}\n#endif\n\n#if IMGUI_VERSION_NUM < 18927\nImGuiID ImGui::TableGetInstanceID(ImGuiTable* table, int instance_no)\n{\n    // Changed in #6140\n    return table->ID + instance_no;\n}\n#endif\n\nImGuiID TableGetHeaderID(ImGuiTable* table, const char* column, int instance_no)\n{\n    IM_ASSERT(table != nullptr);\n    int column_n = -1;\n    for (int n = 0; n < table->Columns.size() && column_n < 0; n++)\n        if (strcmp(ImGui::TableGetColumnName(table, n), column) == 0)\n            column_n = n;\n    IM_ASSERT(column_n != -1);\n    return TableGetHeaderID(table, column_n, instance_no);\n}\n\nImGuiID TableGetHeaderID(ImGuiTable* table, int column_n, int instance_no)\n{\n    IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);\n    const ImGuiID table_instance_id = ImGui::TableGetInstanceID(table, instance_no);\n    const char* column_name = ImGui::TableGetColumnName(table, column_n);\n#if IMGUI_VERSION_NUM >= 18927\n    const int column_id_differencier = column_n;\n#else\n    const int column_id_differencier = instance_no * table->ColumnsCount + column_n;\n#endif\n    const int column_id = ImHashData(&column_id_differencier, sizeof(column_id_differencier), table_instance_id);\n    return ImHashData(column_name, strlen(column_name), column_id);\n}\n\n// FIXME: Could be moved to core as an internal function?\nvoid TableDiscardInstanceAndSettings(ImGuiID table_id)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.CurrentTable == nullptr);\n    if (ImGuiTableSettings* settings = ImGui::TableSettingsFindByID(table_id))\n        settings->ID = 0;\n\n    if (ImGuiTable* table = ImGui::TableFindByID(table_id))\n        ImGui::TableRemove(table);\n    // FIXME-TABLE: We should be able to use TableResetSettings() instead of TableRemove()! Maybe less of a clean slate but would be good to check that it does the job\n    //ImGui::TableResetSettings(table);\n}\n\n// Helper to verify ImDrawData integrity of buffer count (broke before e.g. #6716)\nvoid DrawDataVerifyMatchingBufferCount(ImDrawData* draw_data)\n{\n#if IMGUI_VERSION_NUM >= 18973\n    int total_vtx_count = 0;\n    int total_idx_count = 0;\n    for (ImDrawList* draw_list : draw_data->CmdLists)\n    {\n        total_vtx_count += draw_list->VtxBuffer.Size;\n        total_idx_count += draw_list->IdxBuffer.Size;\n    }\n    IM_UNUSED(total_vtx_count);\n    IM_UNUSED(total_idx_count);\n    IM_ASSERT(total_vtx_count == draw_data->TotalVtxCount);\n    IM_ASSERT(total_idx_count == draw_data->TotalIdxCount);\n#else\n    IM_UNUSED(draw_data);\n#endif\n}\n\n//-----------------------------------------------------------------------------\n// Simple CSV parser\n//-----------------------------------------------------------------------------\n\nvoid ImGuiCsvParser::Clear()\n{\n    Rows = Columns = 0;\n    if (_Data != nullptr)\n        IM_FREE(_Data);\n    _Data = nullptr;\n    _Index.clear();\n}\n\nbool ImGuiCsvParser::Load(const char* filename)\n{\n    size_t len = 0;\n    _Data = (char*)ImFileLoadToMemory(filename, \"rb\", &len, 1);\n    if (_Data == nullptr)\n        return false;\n\n    int columns = 1;\n    if (Columns > 0)\n    {\n        columns = Columns;                                          // User-provided expected column count.\n    }\n    else\n    {\n        for (const char* c = _Data; *c != '\\n' && *c != '\\0'; c++)  // Count columns. Quoted columns with commas are not supported.\n            if (*c == ',')\n                columns++;\n    }\n\n    // Count rows. Extra new lines anywhere in the file are ignored.\n    int max_rows = 0;\n    for (const char* c = _Data, *end = c + len; c < end; c++)\n        if ((*c == '\\n' && c[1] != '\\r' && c[1] != '\\n') || *c == '\\0')\n            max_rows++;\n\n    if (columns == 0 || max_rows == 0)\n        return false;\n\n    // Create index\n    _Index.resize(columns * max_rows);\n\n    int col = 0;\n    char* col_data = _Data;\n    for (char* c = _Data; *c != '\\0'; c++)\n    {\n        const bool is_comma = (*c == ',');\n        const bool is_eol = (*c == '\\n' || *c == '\\r');\n        const bool is_eof = (*c == '\\0');\n        if (is_comma || is_eol || is_eof)\n        {\n            _Index[Rows * columns + col] = col_data;\n            col_data = c + 1;\n            if (is_comma)\n            {\n                col++;\n            }\n            else\n            {\n                if (col + 1 == columns)\n                    Rows++;\n                else\n                    fprintf(stderr, \"%s: Unexpected number of columns on line %d, ignoring.\\n\", filename, Rows + 1); // FIXME\n                col = 0;\n            }\n            *c = 0;\n            if (is_eol)\n                while (c[1] == '\\r' || c[1] == '\\n')\n                    c++;\n        }\n    }\n\n    Columns = columns;\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n"
  },
  {
    "path": "lib/third_party/imgui/imnodes/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\n# https://github.com/Nelarius/imnodes\nproject(imgui_imnodes)\n\nset(CMAKE_CXX_STANDARD 23)\n\nif (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)\n    add_library(imgui_imnodes OBJECT\n        source/imnodes.cpp\n    )\n\n    target_include_directories(imgui_imnodes PUBLIC\n        include\n    )\n\n    target_link_libraries(imgui_imnodes PRIVATE imgui_includes)\nendif()\n\ntarget_include_directories(imgui_all_includes INTERFACE include)\n"
  },
  {
    "path": "lib/third_party/imgui/imnodes/LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2019 Johann Muszynski\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "lib/third_party/imgui/imnodes/README.md",
    "content": "<h1 align=\"center\">imnodes</h1>\n\n<p align=\"center\">A small, dependency-free node editor extension for <a href=\"https://github.com/ocornut/imgui\">dear imgui</a>.</p>\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/Nelarius/imnodes/master/img/imnodes.gif?token=ADH_jEpqbBrw0nH-BUmOip490dyO2CnRks5cVZllwA%3D%3D\">\n</p>\n\n[![Build Status](https://github.com/nelarius/imnodes/workflows/Build/badge.svg)](https://github.com/nelarius/imnodes/actions?workflow=Build)\n\nImnodes aims to provide a simple, immediate-mode interface for creating a node editor within an ImGui window. Imnodes provides simple, customizable building blocks that a user needs to build their node editor.\n\nFeatures:\n\n* Create nodes, links, and pins in an immediate-mode style. The user controls all the state.\n* Nest ImGui widgets inside nodes\n* Simple distribution, just copy-paste `imnodes.h`, `imnodes_internal.h`, and `imnodes.cpp` into your project along side ImGui.\n\n## Examples\n\nThis repository includes a few example files, under `example/`. They are intended as simple examples giving you an idea of what you can build with imnodes.\n\nIf you need to build the examples, you can use the provided CMake script to do so.\n\n```bash\n# Initialize the vcpkg submodule\n$ git submodule update --init\n# Run the generation step and build\n$ cmake -B build-release/ -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake\n$ cmake --build build-release -- -j\n```\n\nNote that this has not been tested on Linux and is likely to fail on the platform.\n\n## A brief tour\n\nHere is a small overview of how the extension is used. For more information on example usage, scroll to the bottom of the README.\n\nBefore anything can be done, the library must be initialized. This can be done at the same time as `dear imgui` initialization.\n\n```cpp\nImGui::CreateContext();\nImNodes::CreateContext();\n\n// elsewhere in the code...\nImNodes::DestroyContext();\nImGui::DestroyContext();\n```\n\nThe node editor is a workspace which contains nodes. The node editor must be instantiated within a window, like any other UI element.\n\n```cpp\nImGui::Begin(\"node editor\");\n\nImNodes::BeginNodeEditor();\nImNodes::EndNodeEditor();\n\nImGui::End();\n```\n\nNow you should have a workspace with a grid visible in the window. An empty node can now be instantiated:\n\n```cpp\nconst int hardcoded_node_id = 1;\n\nImNodes::BeginNodeEditor();\n\nImNodes::BeginNode(hardcoded_node_id);\nImGui::Dummy(ImVec2(80.0f, 45.0f));\nImNodes::EndNode();\n\nImNodes::EndNodeEditor();\n```\n\nNodes, like windows in `dear imgui` must be uniquely identified. But we can't use the node titles for identification, because it should be possible to have many nodes of the same name in the workspace. Instead, you just use integers for identification.\n\nAttributes are the UI content of the node. An attribute will have a pin (the little circle) on either side of the node. There are two types of attributes: input, and output attributes. Input attribute pins are on the left side of the node, and output attribute pins are on the right. Like nodes, pins must be uniquely identified.\n\n```cpp\nImNodes::BeginNode(hardcoded_node_id);\n\nconst int output_attr_id = 2;\nImNodes::BeginOutputAttribute(output_attr_id);\n// in between Begin|EndAttribute calls, you can call ImGui\n// UI functions\nImGui::Text(\"output pin\");\nImNodes::EndOutputAttribute();\n\nImNodes::EndNode();\n```\n\nThe extension doesn't really care what is in the attribute. It just renders the pin for the attribute, and allows the user to create links between pins.\n\nA title bar can be added to the node using `BeginNodeTitleBar` and `EndNodeTitleBar`. Like attributes, you place your title bar's content between the function calls. Note that these functions have to be called before adding attributes or other `dear imgui` UI elements to the node, since the node's layout is built in order, top-to-bottom.\n\n```cpp\nImNodes::BeginNode(hardcoded_node_id);\n\nImNodes::BeginNodeTitleBar();\nImGui::TextUnformatted(\"output node\");\nImNodes::EndNodeTitleBar();\n\n// pins and other node UI content omitted...\n\nImNodes::EndNode();\n```\n\nThe user has to render their own links between nodes as well. A link is a curve which connects two attributes. A link is just a pair of attribute ids. And like nodes and attributes, links too have to be identified by unique integer values:\n\n```cpp\nstd::vector<std::pair<int, int>> links;\n// elsewhere in the code...\nfor (int i = 0; i < links.size(); ++i)\n{\n  const std::pair<int, int> p = links[i];\n  // in this case, we just use the array index of the link\n  // as the unique identifier\n  ImNodes::Link(i, p.first, p.second);\n}\n```\n\nAfter `EndNodeEditor` has been called, you can check if a link was created during the frame with the function call `IsLinkCreated`:\n\n```cpp\nint start_attr, end_attr;\nif (ImNodes::IsLinkCreated(&start_attr, &end_attr))\n{\n  links.push_back(std::make_pair(start_attr, end_attr));\n}\n```\n\nIn addition to checking for new links, you can also check whether UI elements are being hovered over by the mouse cursor:\n\n```cpp\nint node_id;\nif (ImNodes::IsNodeHovered(&node_id))\n{\n  node_hovered = node_id;\n}\n```\n\nYou can also check to see if any node has been selected. Nodes can be clicked on, or they can be selected by clicking and dragging the box selector over them.\n\n```cpp\n// Note that since many nodes can be selected at once, we first need to query the number of\n// selected nodes before getting them.\nconst int num_selected_nodes = ImNodes::NumSelectedNodes();\nif (num_selected_nodes > 0)\n{\n  std::vector<int> selected_nodes;\n  selected_nodes.resize(num_selected_nodes);\n  ImNodes::GetSelectedNodes(selected_nodes.data());\n}\n```\n\nSee `imnodes.h` for more UI event-related functions.\n\nLike `dear imgui`, the style of the UI can be changed. You can set the color style of individual nodes, pins, and links mid-frame by calling `ImNodes::PushColorStyle` and `ImNodes::PopColorStyle`.\n\n```cpp\n// set the titlebar color of an individual node\nImNodes::PushColorStyle(\n  ImNodesCol_TitleBar, IM_COL32(11, 109, 191, 255));\nImNodes::PushColorStyle(\n  ImNodesCol_TitleBarSelected, IM_COL32(81, 148, 204, 255));\n\nImNodes::BeginNode(hardcoded_node_id);\n// node internals here...\nImNodes::EndNode();\n\nImNodes::PopColorStyle();\nImNodes::PopColorStyle();\n```\n\nIf the style is not being set mid-frame, `ImNodes::GetStyle` can be called instead, and the values can be set into the style array directly.\n\n```cpp\n// set the titlebar color for all nodes\nImNodesStyle& style = ImNodes::GetStyle();\nstyle.colors[ImNodesCol_TitleBar] = IM_COL32(232, 27, 86, 255);\nstyle.colors[ImNodesCol_TitleBarSelected] = IM_COL32(241, 108, 146, 255);\n```\n\nTo handle quicker navigation of large graphs you can use an interactive mini-map overlay. The mini-map can be zoomed and scrolled. Editor nodes will track the panning of the mini-map accordingly.\n\n```cpp\nImGui::Begin(\"node editor\");\n\nImNodes::BeginNodeEditor();\n\n// add nodes...\n\n// must be called right before EndNodeEditor\nImNodes::MiniMap();\nImNodes::EndNodeEditor();\n\nImGui::End();\n```\n\nThe relative sizing and corner location of the mini-map in the editor space can be specified like so:\n\n```cpp\n// MiniMap is a square region with a side length that is 20% the largest editor canvas dimension\n// See ImNodesMiniMapLocation_ for other corner locations\nImNodes::MiniMap(0.2f, ImNodesMiniMapLocation_TopRight);\n```\n\nThe mini-map also supports limited node hovering customization through a user-defined callback.\n```cpp\n// User callback\nvoid mini_map_node_hovering_callback(int node_id, void* user_data)\n{\n  ImGui::SetTooltip(\"This is node %d\", node_id);\n}\n\n// Later on...\nImNodes::MiniMap(0.2f, ImNodesMiniMapLocation_TopRight, mini_map_node_hovering_callback, custom_user_data);\n\n// 'custom_user_data' can be used to supply extra information needed for drawing within the callback\n```\n\n## Customizing ImNodes\n\nImNodes can be customized by providing an `imnodes_config.h` header and specifying defining `IMNODES_USER_CONFIG=imnodes_config.h` when compiling.\n\nIt is currently possible to override the type of the minimap hovering callback function. This is useful when generating bindings for another language.\n\nHere's an example imnodes_config.h, which generates a pybind wrapper for the callback.\n```cpp\n#pragma once\n\n#include <pybind11/functional.h>\n\nnamespace pybind11 {\n\ninline bool PyWrapper_Check(PyObject *o) { return true; }\n\nclass wrapper : public object {\npublic:\n    PYBIND11_OBJECT_DEFAULT(wrapper, object, PyWrapper_Check)\n    wrapper(void* x) { m_ptr = (PyObject*)x; }\n    explicit operator bool() const { return m_ptr != nullptr && m_ptr != Py_None; }\n};\n\n} //namespace pybind11\n\nnamespace py = pybind11;\n\n#define ImNodesMiniMapNodeHoveringCallback py::wrapper\n\n#define ImNodesMiniMapNodeHoveringCallbackUserData py::wrapper\n```\n\n## Known issues\n\n* `ImGui::Separator()` spans the current window span. As a result, using a separator inside a node will result in the separator spilling out of the node into the node editor grid.\n\n## Further information\n\nSee the `examples/` directory to see library usage in greater detail.\n\n* simple.cpp is a simple hello-world style program which displays two nodes\n* save_load.cpp is enables you to add and remove nodes and links, and serializes/deserializes them, so that the program state is retained between restarting the program\n* color_node_editor.cpp is a more complete example, which shows how a simple node editor is implemented with a graph.\n"
  },
  {
    "path": "lib/third_party/imgui/imnodes/include/imnodes.h",
    "content": "#pragma once\n\n#include <stddef.h>\n#include <imgui.h>\n\n#ifdef IMNODES_USER_CONFIG\n#include IMNODES_USER_CONFIG\n#endif\n\n#ifndef IMNODES_NAMESPACE\n#define IMNODES_NAMESPACE ImNodes\n#endif\n\ntypedef int ImNodesCol;             // -> enum ImNodesCol_\ntypedef int ImNodesStyleVar;        // -> enum ImNodesStyleVar_\ntypedef int ImNodesStyleFlags;      // -> enum ImNodesStyleFlags_\ntypedef int ImNodesPinShape;        // -> enum ImNodesPinShape_\ntypedef int ImNodesAttributeFlags;  // -> enum ImNodesAttributeFlags_\ntypedef int ImNodesMiniMapLocation; // -> enum ImNodesMiniMapLocation_\n\nenum ImNodesCol_\n{\n    ImNodesCol_NodeBackground = 0,\n    ImNodesCol_NodeBackgroundHovered,\n    ImNodesCol_NodeBackgroundSelected,\n    ImNodesCol_NodeOutline,\n    ImNodesCol_TitleBar,\n    ImNodesCol_TitleBarHovered,\n    ImNodesCol_TitleBarSelected,\n    ImNodesCol_Link,\n    ImNodesCol_LinkHovered,\n    ImNodesCol_LinkSelected,\n    ImNodesCol_Pin,\n    ImNodesCol_PinHovered,\n    ImNodesCol_BoxSelector,\n    ImNodesCol_BoxSelectorOutline,\n    ImNodesCol_GridBackground,\n    ImNodesCol_GridLine,\n    ImNodesCol_GridLinePrimary,\n    ImNodesCol_MiniMapBackground,\n    ImNodesCol_MiniMapBackgroundHovered,\n    ImNodesCol_MiniMapOutline,\n    ImNodesCol_MiniMapOutlineHovered,\n    ImNodesCol_MiniMapNodeBackground,\n    ImNodesCol_MiniMapNodeBackgroundHovered,\n    ImNodesCol_MiniMapNodeBackgroundSelected,\n    ImNodesCol_MiniMapNodeOutline,\n    ImNodesCol_MiniMapLink,\n    ImNodesCol_MiniMapLinkSelected,\n    ImNodesCol_MiniMapCanvas,\n    ImNodesCol_MiniMapCanvasOutline,\n    ImNodesCol_COUNT\n};\n\nenum ImNodesStyleVar_\n{\n    ImNodesStyleVar_GridSpacing = 0,\n    ImNodesStyleVar_NodeCornerRounding,\n    ImNodesStyleVar_NodePadding,\n    ImNodesStyleVar_NodeBorderThickness,\n    ImNodesStyleVar_LinkThickness,\n    ImNodesStyleVar_LinkLineSegmentsPerLength,\n    ImNodesStyleVar_LinkHoverDistance,\n    ImNodesStyleVar_PinCircleRadius,\n    ImNodesStyleVar_PinQuadSideLength,\n    ImNodesStyleVar_PinTriangleSideLength,\n    ImNodesStyleVar_PinLineThickness,\n    ImNodesStyleVar_PinHoverRadius,\n    ImNodesStyleVar_PinOffset,\n    ImNodesStyleVar_MiniMapPadding,\n    ImNodesStyleVar_MiniMapOffset,\n    ImNodesStyleVar_COUNT\n};\n\nenum ImNodesStyleFlags_\n{\n    ImNodesStyleFlags_None = 0,\n    ImNodesStyleFlags_NodeOutline = 1 << 0,\n    ImNodesStyleFlags_GridLines = 1 << 2,\n    ImNodesStyleFlags_GridLinesPrimary = 1 << 3,\n    ImNodesStyleFlags_GridSnapping = 1 << 4\n};\n\nenum ImNodesPinShape_\n{\n    ImNodesPinShape_Circle,\n    ImNodesPinShape_CircleFilled,\n    ImNodesPinShape_Triangle,\n    ImNodesPinShape_TriangleFilled,\n    ImNodesPinShape_Quad,\n    ImNodesPinShape_QuadFilled\n};\n\n// This enum controls the way the attribute pins behave.\nenum ImNodesAttributeFlags_\n{\n    ImNodesAttributeFlags_None = 0,\n    // Allow detaching a link by left-clicking and dragging the link at a pin it is connected to.\n    // NOTE: the user has to actually delete the link for this to work. A deleted link can be\n    // detected by calling IsLinkDestroyed() after EndNodeEditor().\n    ImNodesAttributeFlags_EnableLinkDetachWithDragClick = 1 << 0,\n    // Visual snapping of an in progress link will trigger IsLink Created/Destroyed events. Allows\n    // for previewing the creation of a link while dragging it across attributes. See here for demo:\n    // https://github.com/Nelarius/imnodes/issues/41#issuecomment-647132113 NOTE: the user has to\n    // actually delete the link for this to work. A deleted link can be detected by calling\n    // IsLinkDestroyed() after EndNodeEditor().\n    ImNodesAttributeFlags_EnableLinkCreationOnSnap = 1 << 1\n};\n\nstruct ImNodesIO\n{\n    struct EmulateThreeButtonMouse\n    {\n        EmulateThreeButtonMouse();\n\n        // The keyboard modifier to use in combination with mouse left click to pan the editor view.\n        // Set to NULL by default. To enable this feature, set the modifier to point to a boolean\n        // indicating the state of a modifier. For example,\n        //\n        // ImNodes::GetIO().EmulateThreeButtonMouse.Modifier = &ImGui::GetIO().KeyAlt;\n        const bool* Modifier;\n    } EmulateThreeButtonMouse;\n\n    struct LinkDetachWithModifierClick\n    {\n        LinkDetachWithModifierClick();\n\n        // Pointer to a boolean value indicating when the desired modifier is pressed. Set to NULL\n        // by default. To enable the feature, set the modifier to point to a boolean indicating the\n        // state of a modifier. For example,\n        //\n        // ImNodes::GetIO().LinkDetachWithModifierClick.Modifier = &ImGui::GetIO().KeyCtrl;\n        //\n        // Left-clicking a link with this modifier pressed will detach that link. NOTE: the user has\n        // to actually delete the link for this to work. A deleted link can be detected by calling\n        // IsLinkDestroyed() after EndNodeEditor().\n        const bool* Modifier;\n    } LinkDetachWithModifierClick;\n\n    struct MultipleSelectModifier\n    {\n        MultipleSelectModifier();\n\n        // Pointer to a boolean value indicating when the desired modifier is pressed. Set to NULL\n        // by default. To enable the feature, set the modifier to point to a boolean indicating the\n        // state of a modifier. For example,\n        //\n        // ImNodes::GetIO().MultipleSelectModifier.Modifier = &ImGui::GetIO().KeyCtrl;\n        //\n        // Left-clicking a node with this modifier pressed will add the node to the list of\n        // currently selected nodes. If this value is NULL, the Ctrl key will be used.\n        const bool* Modifier;\n    } MultipleSelectModifier;\n\n    // Holding alt mouse button pans the node area, by default middle mouse button will be used\n    // Set based on ImGuiMouseButton values\n    int AltMouseButton;\n\n    // Panning speed when dragging an element and mouse is outside the main editor view.\n    float AutoPanningSpeed;\n\n    ImNodesIO();\n};\n\nstruct ImNodesStyle\n{\n    float GridSpacing;\n\n    float  NodeCornerRounding;\n    ImVec2 NodePadding;\n    float  NodeBorderThickness;\n\n    float LinkThickness;\n    float LinkLineSegmentsPerLength;\n    float LinkHoverDistance;\n\n    // The following variables control the look and behavior of the pins. The default size of each\n    // pin shape is balanced to occupy approximately the same surface area on the screen.\n\n    // The circle radius used when the pin shape is either ImNodesPinShape_Circle or\n    // ImNodesPinShape_CircleFilled.\n    float PinCircleRadius;\n    // The quad side length used when the shape is either ImNodesPinShape_Quad or\n    // ImNodesPinShape_QuadFilled.\n    float PinQuadSideLength;\n    // The equilateral triangle side length used when the pin shape is either\n    // ImNodesPinShape_Triangle or ImNodesPinShape_TriangleFilled.\n    float PinTriangleSideLength;\n    // The thickness of the line used when the pin shape is not filled.\n    float PinLineThickness;\n    // The radius from the pin's center position inside of which it is detected as being hovered\n    // over.\n    float PinHoverRadius;\n    // Offsets the pins' positions from the edge of the node to the outside of the node.\n    float PinOffset;\n\n    // Mini-map padding size between mini-map edge and mini-map content.\n    ImVec2 MiniMapPadding;\n    // Mini-map offset from the screen side.\n    ImVec2 MiniMapOffset;\n\n    // By default, ImNodesStyleFlags_NodeOutline and ImNodesStyleFlags_Gridlines are enabled.\n    ImNodesStyleFlags Flags;\n    // Set these mid-frame using Push/PopColorStyle. You can index this color array with with a\n    // ImNodesCol value.\n    unsigned int Colors[ImNodesCol_COUNT];\n\n    ImNodesStyle();\n};\n\nenum ImNodesMiniMapLocation_\n{\n    ImNodesMiniMapLocation_BottomLeft,\n    ImNodesMiniMapLocation_BottomRight,\n    ImNodesMiniMapLocation_TopLeft,\n    ImNodesMiniMapLocation_TopRight,\n};\n\nstruct ImGuiContext;\nstruct ImVec2;\n\nstruct ImNodesContext;\n\n// An editor context corresponds to a set of nodes in a single workspace (created with a single\n// Begin/EndNodeEditor pair)\n//\n// By default, the library creates an editor context behind the scenes, so using any of the imnodes\n// functions doesn't require you to explicitly create a context.\nstruct ImNodesEditorContext;\n\n// Callback type used to specify special behavior when hovering a node in the minimap\n#ifndef ImNodesMiniMapNodeHoveringCallback\ntypedef void (*ImNodesMiniMapNodeHoveringCallback)(int, void*);\n#endif\n\n#ifndef ImNodesMiniMapNodeHoveringCallbackUserData\ntypedef void* ImNodesMiniMapNodeHoveringCallbackUserData;\n#endif\n\nnamespace IMNODES_NAMESPACE\n{\n// Call this function if you are compiling imnodes in to a dll, separate from ImGui. Calling this\n// function sets the GImGui global variable, which is not shared across dll boundaries.\nvoid SetImGuiContext(ImGuiContext* ctx);\n\nImNodesContext* CreateContext();\nvoid            DestroyContext(ImNodesContext* ctx = NULL); // NULL = destroy current context\nImNodesContext* GetCurrentContext();\nvoid            SetCurrentContext(ImNodesContext* ctx);\n\nImNodesEditorContext* EditorContextCreate();\nvoid                  EditorContextFree(ImNodesEditorContext*);\nvoid                  EditorContextSet(ImNodesEditorContext*);\nImVec2                EditorContextGetPanning();\nvoid                  EditorContextResetPanning(const ImVec2& pos);\nvoid                  EditorContextMoveToNode(const int node_id);\n\nImNodesIO& GetIO();\n\n// Returns the global style struct. See the struct declaration for default values.\nImNodesStyle& GetStyle();\n// Style presets matching the dear imgui styles of the same name. If dest is NULL, the active\n// context's ImNodesStyle instance will be used as the destination.\nvoid StyleColorsDark(ImNodesStyle* dest = NULL); // on by default\nvoid StyleColorsClassic(ImNodesStyle* dest = NULL);\nvoid StyleColorsLight(ImNodesStyle* dest = NULL);\n\n// The top-level function call. Call this before calling BeginNode/EndNode. Calling this function\n// will result the node editor grid workspace being rendered.\nvoid BeginNodeEditor();\nvoid EndNodeEditor();\n\n// Add a navigable minimap to the editor; call before EndNodeEditor after all\n// nodes and links have been specified\nvoid MiniMap(\n    const float                                      minimap_size_fraction = 0.2f,\n    const ImNodesMiniMapLocation                     location = ImNodesMiniMapLocation_TopLeft,\n    const ImNodesMiniMapNodeHoveringCallback         node_hovering_callback = NULL,\n    const ImNodesMiniMapNodeHoveringCallbackUserData node_hovering_callback_data = NULL);\n\n// Use PushColorStyle and PopColorStyle to modify ImNodesStyle::Colors mid-frame.\nvoid PushColorStyle(ImNodesCol item, unsigned int color);\nvoid PopColorStyle();\nvoid PushStyleVar(ImNodesStyleVar style_item, float value);\nvoid PushStyleVar(ImNodesStyleVar style_item, const ImVec2& value);\nvoid PopStyleVar(int count = 1);\n\n// id can be any positive or negative integer, but INT_MIN is currently reserved for internal use.\nvoid BeginNode(int id);\nvoid EndNode();\n\nImVec2 GetNodeDimensions(int id);\n\n// Place your node title bar content (such as the node title, using ImGui::Text) between the\n// following function calls. These functions have to be called before adding any attributes, or the\n// layout of the node will be incorrect.\nvoid BeginNodeTitleBar();\nvoid EndNodeTitleBar();\n\n// Attributes are ImGui UI elements embedded within the node. Attributes can have pin shapes\n// rendered next to them. Links are created between pins.\n//\n// The activity status of an attribute can be checked via the IsAttributeActive() and\n// IsAnyAttributeActive() function calls. This is one easy way of checking for any changes made to\n// an attribute's drag float UI, for instance.\n//\n// Each attribute id must be unique.\n\n// Create an input attribute block. The pin is rendered on left side.\nvoid BeginInputAttribute(int id, ImNodesPinShape shape = ImNodesPinShape_CircleFilled);\nvoid EndInputAttribute();\n// Create an output attribute block. The pin is rendered on the right side.\nvoid BeginOutputAttribute(int id, ImNodesPinShape shape = ImNodesPinShape_CircleFilled);\nvoid EndOutputAttribute();\n// Create a static attribute block. A static attribute has no pin, and therefore can't be linked to\n// anything. However, you can still use IsAttributeActive() and IsAnyAttributeActive() to check for\n// attribute activity.\nvoid BeginStaticAttribute(int id);\nvoid EndStaticAttribute();\n\n// Push a single AttributeFlags value. By default, only AttributeFlags_None is set.\nvoid PushAttributeFlag(ImNodesAttributeFlags flag);\nvoid PopAttributeFlag();\n\n// Render a link between attributes.\n// The attributes ids used here must match the ids used in Begin(Input|Output)Attribute function\n// calls. The order of start_attr and end_attr doesn't make a difference for rendering the link.\nvoid Link(int id, int start_attribute_id, int end_attribute_id);\n\n// Enable or disable the ability to click and drag a specific node.\nvoid SetNodeDraggable(int node_id, const bool draggable);\n\n// The node's position can be expressed in three coordinate systems:\n// * screen space coordinates, -- the origin is the upper left corner of the window.\n// * editor space coordinates -- the origin is the upper left corner of the node editor window\n// * grid space coordinates, -- the origin is the upper left corner of the node editor window,\n// translated by the current editor panning vector (see EditorContextGetPanning() and\n// EditorContextResetPanning())\n\n// Use the following functions to get and set the node's coordinates in these coordinate systems.\n\nvoid SetNodeScreenSpacePos(int node_id, const ImVec2& screen_space_pos);\nvoid SetNodeEditorSpacePos(int node_id, const ImVec2& editor_space_pos);\nvoid SetNodeGridSpacePos(int node_id, const ImVec2& grid_pos);\n\nImVec2 GetNodeScreenSpacePos(const int node_id);\nImVec2 GetNodeEditorSpacePos(const int node_id);\nImVec2 GetNodeGridSpacePos(const int node_id);\n\n// If ImNodesStyleFlags_GridSnapping is enabled, snap the specified node's origin to the grid.\nvoid SnapNodeToGrid(int node_id);\n\n// Returns true if the current node editor canvas is being hovered over by the mouse, and is not\n// blocked by any other windows.\nbool IsEditorHovered();\n// The following functions return true if a UI element is being hovered over by the mouse cursor.\n// Assigns the id of the UI element being hovered over to the function argument. Use these functions\n// after EndNodeEditor() has been called.\nbool IsNodeHovered(int* node_id);\nbool IsLinkHovered(int* link_id);\nbool IsPinHovered(int* attribute_id);\n\n// Use The following two functions to query the number of selected nodes or links in the current\n// editor. Use after calling EndNodeEditor().\nint NumSelectedNodes();\nint NumSelectedLinks();\n// Get the selected node/link ids. The pointer argument should point to an integer array with at\n// least as many elements as the respective NumSelectedNodes/NumSelectedLinks function call\n// returned.\nvoid GetSelectedNodes(int* node_ids);\nvoid GetSelectedLinks(int* link_ids);\n// Clears the list of selected nodes/links. Useful if you want to delete a selected node or link.\nvoid ClearNodeSelection();\nvoid ClearLinkSelection();\n// Use the following functions to add or remove individual nodes or links from the current editors\n// selection. Note that all functions require the id to be an existing valid id for this editor.\n// Select-functions has the precondition that the object is currently considered unselected.\n// Clear-functions has the precondition that the object is currently considered selected.\n// Preconditions listed above can be checked via IsNodeSelected/IsLinkSelected if not already\n// known.\nvoid SelectNode(int node_id);\nvoid ClearNodeSelection(int node_id);\nbool IsNodeSelected(int node_id);\nvoid SelectLink(int link_id);\nvoid ClearLinkSelection(int link_id);\nbool IsLinkSelected(int link_id);\n\n// Was the previous attribute active? This will continuously return true while the left mouse button\n// is being pressed over the UI content of the attribute.\nbool IsAttributeActive();\n// Was any attribute active? If so, sets the active attribute id to the output function argument.\nbool IsAnyAttributeActive(int* attribute_id = NULL);\n\n// Use the following functions to query a change of state for an existing link, or new link. Call\n// these after EndNodeEditor().\n\n// Did the user start dragging a new link from a pin?\nbool IsLinkStarted(int* started_at_attribute_id);\n// Did the user drop the dragged link before attaching it to a pin?\n// There are two different kinds of situations to consider when handling this event:\n// 1) a link which is created at a pin and then dropped\n// 2) an existing link which is detached from a pin and then dropped\n// Use the including_detached_links flag to control whether this function triggers when the user\n// detaches a link and drops it.\nbool IsLinkDropped(int* started_at_attribute_id = NULL, bool including_detached_links = true);\n// Did the user finish creating a new link?\nbool IsLinkCreated(\n    int*  started_at_attribute_id,\n    int*  ended_at_attribute_id,\n    bool* created_from_snap = NULL);\nbool IsLinkCreated(\n    int*  started_at_node_id,\n    int*  started_at_attribute_id,\n    int*  ended_at_node_id,\n    int*  ended_at_attribute_id,\n    bool* created_from_snap = NULL);\n\n// Was an existing link detached from a pin by the user? The detached link's id is assigned to the\n// output argument link_id.\nbool IsLinkDestroyed(int* link_id);\n\n// Use the following functions to write the editor context's state to a string, or directly to a\n// file. The editor context is serialized in the INI file format.\n\nconst char* SaveCurrentEditorStateToIniString(size_t* data_size = NULL);\nconst char* SaveEditorStateToIniString(\n    const ImNodesEditorContext* editor,\n    size_t*                     data_size = NULL);\n\nvoid LoadCurrentEditorStateFromIniString(const char* data, size_t data_size);\nvoid LoadEditorStateFromIniString(ImNodesEditorContext* editor, const char* data, size_t data_size);\n\nvoid SaveCurrentEditorStateToIniFile(const char* file_name);\nvoid SaveEditorStateToIniFile(const ImNodesEditorContext* editor, const char* file_name);\n\nvoid LoadCurrentEditorStateFromIniFile(const char* file_name);\nvoid LoadEditorStateFromIniFile(ImNodesEditorContext* editor, const char* file_name);\n} // namespace IMNODES_NAMESPACE\n"
  },
  {
    "path": "lib/third_party/imgui/imnodes/include/imnodes_internal.h",
    "content": "#pragma once\n\n#include \"imnodes.h\"\n\n#include <imgui.h>\n#define IMGUI_DEFINE_MATH_OPERATORS\n#include <imgui_internal.h>\n\n#include <limits.h>\n\n// the structure of this file:\n//\n// [SECTION] internal enums\n// [SECTION] internal data structures\n// [SECTION] global and editor context structs\n// [SECTION] object pool implementation\n\nstruct ImNodesContext;\n\nIMGUI_API extern ImNodesContext* GImNodes;\n\n// [SECTION] internal enums\n\ntypedef int ImNodesScope;\ntypedef int ImNodesAttributeType;\ntypedef int ImNodesUIState;\ntypedef int ImNodesClickInteractionType;\ntypedef int ImNodesLinkCreationType;\n\nenum ImNodesScope_\n{\n    ImNodesScope_None = 1,\n    ImNodesScope_Editor = 1 << 1,\n    ImNodesScope_Node = 1 << 2,\n    ImNodesScope_Attribute = 1 << 3\n};\n\nenum ImNodesAttributeType_\n{\n    ImNodesAttributeType_None,\n    ImNodesAttributeType_Input,\n    ImNodesAttributeType_Output\n};\n\nenum ImNodesUIState_\n{\n    ImNodesUIState_None = 0,\n    ImNodesUIState_LinkStarted = 1 << 0,\n    ImNodesUIState_LinkDropped = 1 << 1,\n    ImNodesUIState_LinkCreated = 1 << 2\n};\n\nenum ImNodesClickInteractionType_\n{\n    ImNodesClickInteractionType_Node,\n    ImNodesClickInteractionType_Link,\n    ImNodesClickInteractionType_LinkCreation,\n    ImNodesClickInteractionType_Panning,\n    ImNodesClickInteractionType_BoxSelection,\n    ImNodesClickInteractionType_ImGuiItem,\n    ImNodesClickInteractionType_None\n};\n\nenum ImNodesLinkCreationType_\n{\n    ImNodesLinkCreationType_Standard,\n    ImNodesLinkCreationType_FromDetach\n};\n\n// [SECTION] internal data structures\n\n// The object T must have the following interface:\n//\n// struct T\n// {\n//     T();\n//\n//     int id;\n// };\ntemplate<typename T>\nstruct ImObjectPool\n{\n    ImVector<T>    Pool;\n    ImVector<bool> InUse;\n    ImVector<int>  FreeList;\n    ImGuiStorage   IdMap;\n\n    ImObjectPool() : Pool(), InUse(), FreeList(), IdMap() {}\n};\n\n// Emulates std::optional<int> using the sentinel value `INVALID_INDEX`.\nstruct ImOptionalIndex\n{\n    ImOptionalIndex() : _Index(INVALID_INDEX) {}\n    ImOptionalIndex(const int value) : _Index(value) {}\n\n    // Observers\n\n    inline bool HasValue() const { return _Index != INVALID_INDEX; }\n\n    inline int Value() const\n    {\n        IM_ASSERT(HasValue());\n        return _Index;\n    }\n\n    // Modifiers\n\n    inline ImOptionalIndex& operator=(const int value)\n    {\n        _Index = value;\n        return *this;\n    }\n\n    inline void Reset() { _Index = INVALID_INDEX; }\n\n    inline bool operator==(const ImOptionalIndex& rhs) const { return _Index == rhs._Index; }\n\n    inline bool operator==(const int rhs) const { return _Index == rhs; }\n\n    inline bool operator!=(const ImOptionalIndex& rhs) const { return _Index != rhs._Index; }\n\n    inline bool operator!=(const int rhs) const { return _Index != rhs; }\n\n    static const int INVALID_INDEX = -1;\n\nprivate:\n    int _Index;\n};\n\nstruct ImNodeData\n{\n    int    Id;\n    ImVec2 Origin; // The node origin is in editor space\n    ImRect TitleBarContentRect;\n    ImRect Rect;\n\n    struct\n    {\n        ImU32 Background, BackgroundHovered, BackgroundSelected, Outline, Titlebar, TitlebarHovered,\n            TitlebarSelected;\n    } ColorStyle;\n\n    struct\n    {\n        float  CornerRounding;\n        ImVec2 Padding;\n        float  BorderThickness;\n    } LayoutStyle;\n\n    ImVector<int> PinIndices;\n    bool          Draggable;\n\n    ImNodeData(const int node_id)\n        : Id(node_id), Origin(0.0f, 0.0f), TitleBarContentRect(),\n          Rect(ImVec2(0.0f, 0.0f), ImVec2(0.0f, 0.0f)), ColorStyle(), LayoutStyle(), PinIndices(),\n          Draggable(true)\n    {\n    }\n\n    ~ImNodeData() { Id = INT_MIN; }\n};\n\nstruct ImPinData\n{\n    int                  Id;\n    int                  ParentNodeIdx;\n    ImRect               AttributeRect;\n    ImNodesAttributeType Type;\n    ImNodesPinShape      Shape;\n    ImVec2               Pos; // screen-space coordinates\n    int                  Flags;\n\n    struct\n    {\n        ImU32 Background, Hovered;\n    } ColorStyle;\n\n    ImPinData(const int pin_id)\n        : Id(pin_id), ParentNodeIdx(), AttributeRect(), Type(ImNodesAttributeType_None),\n          Shape(ImNodesPinShape_CircleFilled), Pos(), Flags(ImNodesAttributeFlags_None),\n          ColorStyle()\n    {\n    }\n};\n\nstruct ImLinkData\n{\n    int Id;\n    int StartPinIdx, EndPinIdx;\n\n    struct\n    {\n        ImU32 Base, Hovered, Selected;\n    } ColorStyle;\n\n    ImLinkData(const int link_id) : Id(link_id), StartPinIdx(), EndPinIdx(), ColorStyle() {}\n};\n\nstruct ImClickInteractionState\n{\n    ImNodesClickInteractionType Type;\n\n    struct\n    {\n        int                     StartPinIdx;\n        ImOptionalIndex         EndPinIdx;\n        ImNodesLinkCreationType Type;\n    } LinkCreation;\n\n    struct\n    {\n        ImRect Rect; // Coordinates in grid space\n    } BoxSelector;\n\n    ImClickInteractionState() : Type(ImNodesClickInteractionType_None) {}\n};\n\nstruct ImNodesColElement\n{\n    ImU32      Color;\n    ImNodesCol Item;\n\n    ImNodesColElement(const ImU32 c, const ImNodesCol s) : Color(c), Item(s) {}\n};\n\nstruct ImNodesStyleVarElement\n{\n    ImNodesStyleVar Item;\n    float           FloatValue[2];\n\n    ImNodesStyleVarElement(const ImNodesStyleVar variable, const float value) : Item(variable)\n    {\n        FloatValue[0] = value;\n    }\n\n    ImNodesStyleVarElement(const ImNodesStyleVar variable, const ImVec2 value) : Item(variable)\n    {\n        FloatValue[0] = value.x;\n        FloatValue[1] = value.y;\n    }\n};\n\n// [SECTION] global and editor context structs\n\nstruct ImNodesEditorContext\n{\n    ImObjectPool<ImNodeData> Nodes;\n    ImObjectPool<ImPinData>  Pins;\n    ImObjectPool<ImLinkData> Links;\n\n    ImVector<int> NodeDepthOrder;\n\n    // ui related fields\n    ImVec2 Panning;\n    ImVec2 AutoPanningDelta;\n    // Minimum and maximum extents of all content in grid space. Valid after final\n    // ImNodes::EndNode() call.\n    ImRect GridContentBounds;\n\n    ImVector<int> SelectedNodeIndices;\n    ImVector<int> SelectedLinkIndices;\n\n    // Relative origins of selected nodes for snapping of dragged nodes\n    ImVector<ImVec2> SelectedNodeOffsets;\n    // Offset of the primary node origin relative to the mouse cursor.\n    ImVec2           PrimaryNodeOffset;\n\n    ImClickInteractionState ClickInteraction;\n\n    // Mini-map state set by MiniMap()\n\n    bool                                       MiniMapEnabled;\n    ImNodesMiniMapLocation                     MiniMapLocation;\n    float                                      MiniMapSizeFraction;\n    ImNodesMiniMapNodeHoveringCallback         MiniMapNodeHoveringCallback;\n    ImNodesMiniMapNodeHoveringCallbackUserData MiniMapNodeHoveringCallbackUserData;\n\n    // Mini-map state set during EndNodeEditor() call\n\n    ImRect MiniMapRectScreenSpace;\n    ImRect MiniMapContentScreenSpace;\n    float  MiniMapScaling;\n\n    ImNodesEditorContext()\n        : Nodes(), Pins(), Links(), Panning(0.f, 0.f), SelectedNodeIndices(), SelectedLinkIndices(),\n          SelectedNodeOffsets(), PrimaryNodeOffset(0.f, 0.f), ClickInteraction(),\n          MiniMapEnabled(false), MiniMapSizeFraction(0.0f),\n          MiniMapNodeHoveringCallback(NULL), MiniMapNodeHoveringCallbackUserData(NULL),\n          MiniMapScaling(0.0f)\n    {\n    }\n};\n\nstruct ImNodesContext\n{\n    ImNodesEditorContext* DefaultEditorCtx;\n    ImNodesEditorContext* EditorCtx;\n\n    // Canvas draw list and helper state\n    ImDrawList*   CanvasDrawList;\n    ImGuiStorage  NodeIdxToSubmissionIdx;\n    ImVector<int> NodeIdxSubmissionOrder;\n    ImVector<int> NodeIndicesOverlappingWithMouse;\n    ImVector<int> OccludedPinIndices;\n\n    // Canvas extents\n    ImVec2 CanvasOriginScreenSpace;\n    ImRect CanvasRectScreenSpace;\n\n    // Debug helpers\n    ImNodesScope CurrentScope;\n\n    // Configuration state\n    ImNodesIO                        Io;\n    ImNodesStyle                     Style;\n    ImVector<ImNodesColElement>      ColorModifierStack;\n    ImVector<ImNodesStyleVarElement> StyleModifierStack;\n    ImGuiTextBuffer                  TextBuffer;\n\n    int           CurrentAttributeFlags;\n    ImVector<int> AttributeFlagStack;\n\n    // UI element state\n    int CurrentNodeIdx;\n    int CurrentPinIdx;\n    int CurrentAttributeId;\n\n    ImOptionalIndex HoveredNodeIdx;\n    ImOptionalIndex HoveredLinkIdx;\n    ImOptionalIndex HoveredPinIdx;\n\n    ImOptionalIndex DeletedLinkIdx;\n    ImOptionalIndex SnapLinkIdx;\n\n    // Event helper state\n    // TODO: this should be a part of a state machine, and not a member of the global struct.\n    // Unclear what parts of the code this relates to.\n    int ImNodesUIState;\n\n    int  ActiveAttributeId;\n    bool ActiveAttribute;\n\n    // ImGui::IO cache\n\n    ImVec2 MousePos;\n\n    bool  LeftMouseClicked;\n    bool  LeftMouseReleased;\n    bool  AltMouseClicked;\n    bool  LeftMouseDragging;\n    bool  AltMouseDragging;\n    float AltMouseScrollDelta;\n    bool  MultipleSelectModifier;\n};\n\nnamespace IMNODES_NAMESPACE\n{\nstatic inline ImNodesEditorContext& EditorContextGet()\n{\n    // No editor context was set! Did you forget to call ImNodes::CreateContext()?\n    IM_ASSERT(GImNodes->EditorCtx != NULL);\n    return *GImNodes->EditorCtx;\n}\n\n// [SECTION] ObjectPool implementation\n\ntemplate<typename T>\nstatic inline int ObjectPoolFind(const ImObjectPool<T>& objects, const int id)\n{\n    const int index = objects.IdMap.GetInt(static_cast<ImGuiID>(id), -1);\n    return index;\n}\n\ntemplate<typename T>\nstatic inline void ObjectPoolUpdate(ImObjectPool<T>& objects)\n{\n    for (int i = 0; i < objects.InUse.size(); ++i)\n    {\n        const int id = objects.Pool[i].Id;\n\n        if (!objects.InUse[i] && objects.IdMap.GetInt(id, -1) == i)\n        {\n            objects.IdMap.SetInt(id, -1);\n            objects.FreeList.push_back(i);\n            (objects.Pool.Data + i)->~T();\n        }\n    }\n}\n\ntemplate<>\ninline void ObjectPoolUpdate(ImObjectPool<ImNodeData>& nodes)\n{\n    for (int i = 0; i < nodes.InUse.size(); ++i)\n    {\n        if (nodes.InUse[i])\n        {\n            nodes.Pool[i].PinIndices.clear();\n        }\n        else\n        {\n            const int id = nodes.Pool[i].Id;\n\n            if (nodes.IdMap.GetInt(id, -1) == i)\n            {\n                // Remove node idx form depth stack the first time we detect that this idx slot is\n                // unused\n                ImVector<int>&   depth_stack = EditorContextGet().NodeDepthOrder;\n                const int* const elem = depth_stack.find(i);\n                IM_ASSERT(elem != depth_stack.end());\n                depth_stack.erase(elem);\n\n                nodes.IdMap.SetInt(id, -1);\n                nodes.FreeList.push_back(i);\n                (nodes.Pool.Data + i)->~ImNodeData();\n            }\n        }\n    }\n}\n\ntemplate<typename T>\nstatic inline void ObjectPoolReset(ImObjectPool<T>& objects)\n{\n    if (!objects.InUse.empty())\n    {\n        memset(objects.InUse.Data, 0, objects.InUse.size_in_bytes());\n    }\n}\n\ntemplate<typename T>\nstatic inline int ObjectPoolFindOrCreateIndex(ImObjectPool<T>& objects, const int id)\n{\n    int index = objects.IdMap.GetInt(static_cast<ImGuiID>(id), -1);\n\n    // Construct new object\n    if (index == -1)\n    {\n        if (objects.FreeList.empty())\n        {\n            index = objects.Pool.size();\n            IM_ASSERT(objects.Pool.size() == objects.InUse.size());\n            const int new_size = objects.Pool.size() + 1;\n            objects.Pool.resize(new_size);\n            objects.InUse.resize(new_size);\n        }\n        else\n        {\n            index = objects.FreeList.back();\n            objects.FreeList.pop_back();\n        }\n        IM_PLACEMENT_NEW(objects.Pool.Data + index) T(id);\n        objects.IdMap.SetInt(static_cast<ImGuiID>(id), index);\n    }\n\n    // Flag it as used\n    objects.InUse[index] = true;\n\n    return index;\n}\n\ntemplate<>\ninline int ObjectPoolFindOrCreateIndex(ImObjectPool<ImNodeData>& nodes, const int node_id)\n{\n    int node_idx = nodes.IdMap.GetInt(static_cast<ImGuiID>(node_id), -1);\n\n    // Construct new node\n    if (node_idx == -1)\n    {\n        if (nodes.FreeList.empty())\n        {\n            node_idx = nodes.Pool.size();\n            IM_ASSERT(nodes.Pool.size() == nodes.InUse.size());\n            const int new_size = nodes.Pool.size() + 1;\n            nodes.Pool.resize(new_size);\n            nodes.InUse.resize(new_size);\n        }\n        else\n        {\n            node_idx = nodes.FreeList.back();\n            nodes.FreeList.pop_back();\n        }\n        IM_PLACEMENT_NEW(nodes.Pool.Data + node_idx) ImNodeData(node_id);\n        nodes.IdMap.SetInt(static_cast<ImGuiID>(node_id), node_idx);\n\n        ImNodesEditorContext& editor = EditorContextGet();\n        editor.NodeDepthOrder.push_back(node_idx);\n    }\n\n    // Flag node as used\n    nodes.InUse[node_idx] = true;\n\n    return node_idx;\n}\n\ntemplate<typename T>\nstatic inline T& ObjectPoolFindOrCreateObject(ImObjectPool<T>& objects, const int id)\n{\n    const int index = ObjectPoolFindOrCreateIndex(objects, id);\n    return objects.Pool[index];\n}\n} // namespace IMNODES_NAMESPACE\n"
  },
  {
    "path": "lib/third_party/imgui/imnodes/source/imnodes.cpp",
    "content": "// the structure of this file:\n//\n// [SECTION] bezier curve helpers\n// [SECTION] draw list helper\n// [SECTION] ui state logic\n// [SECTION] render helpers\n// [SECTION] API implementation\n\n#include \"imnodes.h\"\n#include \"imnodes_internal.h\"\n\n#define IMGUI_DEFINE_MATH_OPERATORS\n#include <imgui_internal.h>\n\n// Check minimum ImGui version\n#define MINIMUM_COMPATIBLE_IMGUI_VERSION 17400\n#if IMGUI_VERSION_NUM < MINIMUM_COMPATIBLE_IMGUI_VERSION\n#error \"Minimum ImGui version requirement not met -- please use a newer version!\"\n#endif\n\n#include <limits.h>\n#include <math.h>\n#include <new>\n#include <stdint.h>\n#include <stdio.h> // for fwrite, ssprintf, sscanf\n#include <stdlib.h>\n#include <string.h> // strlen, strncmp\n\n// Use secure CRT function variants to avoid MSVC compiler errors\n#ifdef _MSC_VER\n#define sscanf sscanf_s\n#endif\n\nIMGUI_API ImNodesContext* GImNodes = NULL;\n\nnamespace IMNODES_NAMESPACE\n{\nnamespace\n{\n// [SECTION] bezier curve helpers\n\nstruct CubicBezier\n{\n    ImVec2 P0, P1, P2, P3;\n    int    NumSegments;\n};\n\ninline ImVec2 EvalCubicBezier(\n    const float   t,\n    const ImVec2& P0,\n    const ImVec2& P1,\n    const ImVec2& P2,\n    const ImVec2& P3)\n{\n    // B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3\n\n    const float u = 1.0f - t;\n    const float b0 = u * u * u;\n    const float b1 = 3 * u * u * t;\n    const float b2 = 3 * u * t * t;\n    const float b3 = t * t * t;\n    return ImVec2(\n        b0 * P0.x + b1 * P1.x + b2 * P2.x + b3 * P3.x,\n        b0 * P0.y + b1 * P1.y + b2 * P2.y + b3 * P3.y);\n}\n\n// Calculates the closest point along each bezier curve segment.\nImVec2 GetClosestPointOnCubicBezier(const int num_segments, const ImVec2& p, const CubicBezier& cb)\n{\n    IM_ASSERT(num_segments > 0);\n    ImVec2 p_last = cb.P0;\n    ImVec2 p_closest;\n    float  p_closest_dist = FLT_MAX;\n    float  t_step = 1.0f / (float)num_segments;\n    for (int i = 1; i <= num_segments; ++i)\n    {\n        ImVec2 p_current = EvalCubicBezier(t_step * i, cb.P0, cb.P1, cb.P2, cb.P3);\n        ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);\n        float  dist = ImLengthSqr(p - p_line);\n        if (dist < p_closest_dist)\n        {\n            p_closest = p_line;\n            p_closest_dist = dist;\n        }\n        p_last = p_current;\n    }\n    return p_closest;\n}\n\ninline float GetDistanceToCubicBezier(\n    const ImVec2&      pos,\n    const CubicBezier& cubic_bezier,\n    const int          num_segments)\n{\n    const ImVec2 point_on_curve = GetClosestPointOnCubicBezier(num_segments, pos, cubic_bezier);\n\n    const ImVec2 to_curve = point_on_curve - pos;\n    return ImSqrt(ImLengthSqr(to_curve));\n}\n\ninline ImRect GetContainingRectForCubicBezier(const CubicBezier& cb)\n{\n    const ImVec2 min = ImVec2(ImMin(cb.P0.x, cb.P3.x), ImMin(cb.P0.y, cb.P3.y));\n    const ImVec2 max = ImVec2(ImMax(cb.P0.x, cb.P3.x), ImMax(cb.P0.y, cb.P3.y));\n\n    const float hover_distance = GImNodes->Style.LinkHoverDistance;\n\n    ImRect rect(min, max);\n    rect.Add(cb.P1);\n    rect.Add(cb.P2);\n    rect.Expand(ImVec2(hover_distance, hover_distance));\n\n    return rect;\n}\n\ninline CubicBezier GetCubicBezier(\n    ImVec2                     start,\n    ImVec2                     end,\n    const ImNodesAttributeType start_type,\n    const float                line_segments_per_length)\n{\n    IM_ASSERT(\n        (start_type == ImNodesAttributeType_Input) || (start_type == ImNodesAttributeType_Output));\n    if (start_type == ImNodesAttributeType_Input)\n    {\n        ImSwap(start, end);\n    }\n\n    const float  link_length = ImSqrt(ImLengthSqr(end - start));\n    const ImVec2 offset = ImVec2(0.25f * link_length, 0.f);\n    CubicBezier  cubic_bezier;\n    cubic_bezier.P0 = start;\n    cubic_bezier.P1 = start + offset;\n    cubic_bezier.P2 = end - offset;\n    cubic_bezier.P3 = end;\n    cubic_bezier.NumSegments = ImMax(static_cast<int>(link_length * line_segments_per_length), 1);\n    return cubic_bezier;\n}\n\ninline float EvalImplicitLineEq(const ImVec2& p1, const ImVec2& p2, const ImVec2& p)\n{\n    return (p2.y - p1.y) * p.x + (p1.x - p2.x) * p.y + (p2.x * p1.y - p1.x * p2.y);\n}\n\ninline int Sign(float val) { return int(val > 0.0f) - int(val < 0.0f); }\n\ninline bool RectangleOverlapsLineSegment(const ImRect& rect, const ImVec2& p1, const ImVec2& p2)\n{\n    // Trivial case: rectangle contains an endpoint\n    if (rect.Contains(p1) || rect.Contains(p2))\n    {\n        return true;\n    }\n\n    // Flip rectangle if necessary\n    ImRect flip_rect = rect;\n\n    if (flip_rect.Min.x > flip_rect.Max.x)\n    {\n        ImSwap(flip_rect.Min.x, flip_rect.Max.x);\n    }\n\n    if (flip_rect.Min.y > flip_rect.Max.y)\n    {\n        ImSwap(flip_rect.Min.y, flip_rect.Max.y);\n    }\n\n    // Trivial case: line segment lies to one particular side of rectangle\n    if ((p1.x < flip_rect.Min.x && p2.x < flip_rect.Min.x) ||\n        (p1.x > flip_rect.Max.x && p2.x > flip_rect.Max.x) ||\n        (p1.y < flip_rect.Min.y && p2.y < flip_rect.Min.y) ||\n        (p1.y > flip_rect.Max.y && p2.y > flip_rect.Max.y))\n    {\n        return false;\n    }\n\n    const int corner_signs[4] = {\n        Sign(EvalImplicitLineEq(p1, p2, flip_rect.Min)),\n        Sign(EvalImplicitLineEq(p1, p2, ImVec2(flip_rect.Max.x, flip_rect.Min.y))),\n        Sign(EvalImplicitLineEq(p1, p2, ImVec2(flip_rect.Min.x, flip_rect.Max.y))),\n        Sign(EvalImplicitLineEq(p1, p2, flip_rect.Max))};\n\n    int sum = 0;\n    int sum_abs = 0;\n\n    for (int i = 0; i < 4; ++i)\n    {\n        sum += corner_signs[i];\n        sum_abs += abs(corner_signs[i]);\n    }\n\n    // At least one corner of rectangle lies on a different side of line segment\n    return abs(sum) != sum_abs;\n}\n\ninline bool RectangleOverlapsBezier(const ImRect& rectangle, const CubicBezier& cubic_bezier)\n{\n    ImVec2 current =\n        EvalCubicBezier(0.f, cubic_bezier.P0, cubic_bezier.P1, cubic_bezier.P2, cubic_bezier.P3);\n    const float dt = 1.0f / cubic_bezier.NumSegments;\n    for (int s = 0; s < cubic_bezier.NumSegments; ++s)\n    {\n        ImVec2 next = EvalCubicBezier(\n            static_cast<float>((s + 1) * dt),\n            cubic_bezier.P0,\n            cubic_bezier.P1,\n            cubic_bezier.P2,\n            cubic_bezier.P3);\n        if (RectangleOverlapsLineSegment(rectangle, current, next))\n        {\n            return true;\n        }\n        current = next;\n    }\n    return false;\n}\n\ninline bool RectangleOverlapsLink(\n    const ImRect&              rectangle,\n    const ImVec2&              start,\n    const ImVec2&              end,\n    const ImNodesAttributeType start_type)\n{\n    // First level: simple rejection test via rectangle overlap:\n\n    ImRect lrect = ImRect(start, end);\n    if (lrect.Min.x > lrect.Max.x)\n    {\n        ImSwap(lrect.Min.x, lrect.Max.x);\n    }\n\n    if (lrect.Min.y > lrect.Max.y)\n    {\n        ImSwap(lrect.Min.y, lrect.Max.y);\n    }\n\n    if (rectangle.Overlaps(lrect))\n    {\n        // First, check if either one or both endpoinds are trivially contained\n        // in the rectangle\n\n        if (rectangle.Contains(start) || rectangle.Contains(end))\n        {\n            return true;\n        }\n\n        // Second level of refinement: do a more expensive test against the\n        // link\n\n        const CubicBezier cubic_bezier =\n            GetCubicBezier(start, end, start_type, GImNodes->Style.LinkLineSegmentsPerLength);\n        return RectangleOverlapsBezier(rectangle, cubic_bezier);\n    }\n\n    return false;\n}\n\n// [SECTION] coordinate space conversion helpers\n\ninline ImVec2 ScreenSpaceToGridSpace(const ImNodesEditorContext& editor, const ImVec2& v)\n{\n    return v - GImNodes->CanvasOriginScreenSpace - editor.Panning;\n}\n\ninline ImRect ScreenSpaceToGridSpace(const ImNodesEditorContext& editor, const ImRect& r)\n{\n    return ImRect(ScreenSpaceToGridSpace(editor, r.Min), ScreenSpaceToGridSpace(editor, r.Max));\n}\n\ninline ImVec2 GridSpaceToScreenSpace(const ImNodesEditorContext& editor, const ImVec2& v)\n{\n    return v + GImNodes->CanvasOriginScreenSpace + editor.Panning;\n}\n\ninline ImVec2 GridSpaceToEditorSpace(const ImNodesEditorContext& editor, const ImVec2& v)\n{\n    return v + editor.Panning;\n}\n\ninline ImVec2 EditorSpaceToGridSpace(const ImNodesEditorContext& editor, const ImVec2& v)\n{\n    return v - editor.Panning;\n}\n\ninline ImVec2 EditorSpaceToScreenSpace(const ImVec2& v)\n{\n    return GImNodes->CanvasOriginScreenSpace + v;\n}\n\ninline ImVec2 MiniMapSpaceToGridSpace(const ImNodesEditorContext& editor, const ImVec2& v)\n{\n    return (v - editor.MiniMapContentScreenSpace.Min) / editor.MiniMapScaling +\n           editor.GridContentBounds.Min;\n}\n\ninline ImVec2 ScreenSpaceToMiniMapSpace(const ImNodesEditorContext& editor, const ImVec2& v)\n{\n    return (ScreenSpaceToGridSpace(editor, v) - editor.GridContentBounds.Min) *\n               editor.MiniMapScaling +\n           editor.MiniMapContentScreenSpace.Min;\n}\n\ninline ImRect ScreenSpaceToMiniMapSpace(const ImNodesEditorContext& editor, const ImRect& r)\n{\n    return ImRect(\n        ScreenSpaceToMiniMapSpace(editor, r.Min), ScreenSpaceToMiniMapSpace(editor, r.Max));\n}\n\n// [SECTION] draw list helper\n\nvoid ImDrawListGrowChannels(ImDrawList* draw_list, const int num_channels)\n{\n    ImDrawListSplitter& splitter = draw_list->_Splitter;\n\n    if (splitter._Count == 1)\n    {\n        splitter.Split(draw_list, num_channels + 1);\n        return;\n    }\n\n    // NOTE: this logic has been lifted from ImDrawListSplitter::Split with slight modifications\n    // to allow nested splits. The main modification is that we only create new ImDrawChannel\n    // instances after splitter._Count, instead of over the whole splitter._Channels array like\n    // the regular ImDrawListSplitter::Split method does.\n\n    const int old_channel_capacity = splitter._Channels.Size;\n    // NOTE: _Channels is not resized down, and therefore _Count <= _Channels.size()!\n    const int old_channel_count = splitter._Count;\n    const int requested_channel_count = old_channel_count + num_channels;\n    if (old_channel_capacity < old_channel_count + num_channels)\n    {\n        splitter._Channels.resize(requested_channel_count);\n    }\n\n    splitter._Count = requested_channel_count;\n\n    for (int i = old_channel_count; i < requested_channel_count; ++i)\n    {\n        ImDrawChannel& channel = splitter._Channels[i];\n\n        // If we're inside the old capacity region of the array, we need to reuse the existing\n        // memory of the command and index buffers.\n        if (i < old_channel_capacity)\n        {\n            channel._CmdBuffer.resize(0);\n            channel._IdxBuffer.resize(0);\n        }\n        // Else, we need to construct new draw channels.\n        else\n        {\n            IM_PLACEMENT_NEW(&channel) ImDrawChannel();\n        }\n\n        {\n            ImDrawCmd draw_cmd;\n            draw_cmd.ClipRect = draw_list->_ClipRectStack.back();\n            draw_cmd.TexRef = draw_list->_TextureStack.back();\n            channel._CmdBuffer.push_back(draw_cmd);\n        }\n    }\n}\n\nvoid ImDrawListSplitterSwapChannels(\n    ImDrawListSplitter& splitter,\n    const int           lhs_idx,\n    const int           rhs_idx)\n{\n    if (lhs_idx == rhs_idx)\n    {\n        return;\n    }\n\n    IM_ASSERT(lhs_idx >= 0 && lhs_idx < splitter._Count);\n    IM_ASSERT(rhs_idx >= 0 && rhs_idx < splitter._Count);\n\n    ImDrawChannel& lhs_channel = splitter._Channels[lhs_idx];\n    ImDrawChannel& rhs_channel = splitter._Channels[rhs_idx];\n    lhs_channel._CmdBuffer.swap(rhs_channel._CmdBuffer);\n    lhs_channel._IdxBuffer.swap(rhs_channel._IdxBuffer);\n\n    const int current_channel = splitter._Current;\n\n    if (current_channel == lhs_idx)\n    {\n        splitter._Current = rhs_idx;\n    }\n    else if (current_channel == rhs_idx)\n    {\n        splitter._Current = lhs_idx;\n    }\n}\n\nvoid DrawListSet(ImDrawList* window_draw_list)\n{\n    GImNodes->CanvasDrawList = window_draw_list;\n    GImNodes->NodeIdxToSubmissionIdx.Clear();\n    GImNodes->NodeIdxSubmissionOrder.clear();\n}\n\n// The draw list channels are structured as follows. First we have our base channel, the canvas grid\n// on which we render the grid lines in BeginNodeEditor(). The base channel is the reason\n// draw_list_submission_idx_to_background_channel_idx offsets the index by one. Each BeginNode()\n// call appends two new draw channels, for the node background and foreground. The node foreground\n// is the channel into which the node's ImGui content is rendered. Finally, in EndNodeEditor() we\n// append one last draw channel for rendering the selection box and the incomplete link on top of\n// everything else.\n//\n// +----------+----------+----------+----------+----------+----------+\n// |          |          |          |          |          |          |\n// |canvas    |node      |node      |...       |...       |click     |\n// |grid      |background|foreground|          |          |interaction\n// |          |          |          |          |          |          |\n// +----------+----------+----------+----------+----------+----------+\n//            |                     |\n//            |   submission idx    |\n//            |                     |\n//            -----------------------\n\nvoid DrawListAddNode(const int node_idx)\n{\n    GImNodes->NodeIdxToSubmissionIdx.SetInt(\n        static_cast<ImGuiID>(node_idx), GImNodes->NodeIdxSubmissionOrder.Size);\n    GImNodes->NodeIdxSubmissionOrder.push_back(node_idx);\n    ImDrawListGrowChannels(GImNodes->CanvasDrawList, 2);\n}\n\nvoid DrawListAppendClickInteractionChannel()\n{\n    // NOTE: don't use this function outside of EndNodeEditor. Using this before all nodes have been\n    // added will screw up the node draw order.\n    ImDrawListGrowChannels(GImNodes->CanvasDrawList, 1);\n}\n\nint DrawListSubmissionIdxToBackgroundChannelIdx(const int submission_idx)\n{\n    // NOTE: the first channel is the canvas background, i.e. the grid\n    return 1 + 2 * submission_idx;\n}\n\nint DrawListSubmissionIdxToForegroundChannelIdx(const int submission_idx)\n{\n    return DrawListSubmissionIdxToBackgroundChannelIdx(submission_idx) + 1;\n}\n\nvoid DrawListActivateClickInteractionChannel()\n{\n    GImNodes->CanvasDrawList->_Splitter.SetCurrentChannel(\n        GImNodes->CanvasDrawList, GImNodes->CanvasDrawList->_Splitter._Count - 1);\n}\n\nvoid DrawListActivateCurrentNodeForeground()\n{\n    const int foreground_channel_idx =\n        DrawListSubmissionIdxToForegroundChannelIdx(GImNodes->NodeIdxSubmissionOrder.Size - 1);\n    GImNodes->CanvasDrawList->_Splitter.SetCurrentChannel(\n        GImNodes->CanvasDrawList, foreground_channel_idx);\n}\n\nvoid DrawListActivateNodeBackground(const int node_idx)\n{\n    const int submission_idx =\n        GImNodes->NodeIdxToSubmissionIdx.GetInt(static_cast<ImGuiID>(node_idx), -1);\n    // There is a discrepancy in the submitted node count and the rendered node count! Did you call\n    // one of the following functions\n    // * EditorContextMoveToNode\n    // * SetNodeScreenSpacePos\n    // * SetNodeGridSpacePos\n    // * SetNodeDraggable\n    // after the BeginNode/EndNode function calls?\n    IM_ASSERT(submission_idx != -1);\n    const int background_channel_idx = DrawListSubmissionIdxToBackgroundChannelIdx(submission_idx);\n    GImNodes->CanvasDrawList->_Splitter.SetCurrentChannel(\n        GImNodes->CanvasDrawList, background_channel_idx);\n}\n\nvoid DrawListSwapSubmissionIndices(const int lhs_idx, const int rhs_idx)\n{\n    IM_ASSERT(lhs_idx != rhs_idx);\n\n    const int lhs_foreground_channel_idx = DrawListSubmissionIdxToForegroundChannelIdx(lhs_idx);\n    const int lhs_background_channel_idx = DrawListSubmissionIdxToBackgroundChannelIdx(lhs_idx);\n    const int rhs_foreground_channel_idx = DrawListSubmissionIdxToForegroundChannelIdx(rhs_idx);\n    const int rhs_background_channel_idx = DrawListSubmissionIdxToBackgroundChannelIdx(rhs_idx);\n\n    ImDrawListSplitterSwapChannels(\n        GImNodes->CanvasDrawList->_Splitter,\n        lhs_background_channel_idx,\n        rhs_background_channel_idx);\n    ImDrawListSplitterSwapChannels(\n        GImNodes->CanvasDrawList->_Splitter,\n        lhs_foreground_channel_idx,\n        rhs_foreground_channel_idx);\n}\n\nvoid DrawListSortChannelsByDepth(const ImVector<int>& node_idx_depth_order)\n{\n    if (GImNodes->NodeIdxToSubmissionIdx.Data.Size < 2)\n    {\n        return;\n    }\n\n    IM_ASSERT(node_idx_depth_order.Size == GImNodes->NodeIdxSubmissionOrder.Size);\n\n    int start_idx = node_idx_depth_order.Size - 1;\n\n    while (node_idx_depth_order[start_idx] == GImNodes->NodeIdxSubmissionOrder[start_idx])\n    {\n        if (--start_idx == 0)\n        {\n            // early out if submission order and depth order are the same\n            return;\n        }\n    }\n\n    // TODO: this is an O(N^2) algorithm. It might be worthwhile revisiting this to see if the time\n    // complexity can be reduced.\n\n    for (int depth_idx = start_idx; depth_idx > 0; --depth_idx)\n    {\n        const int node_idx = node_idx_depth_order[depth_idx];\n\n        // Find the current index of the node_idx in the submission order array\n        int submission_idx = -1;\n        for (int i = 0; i < GImNodes->NodeIdxSubmissionOrder.Size; ++i)\n        {\n            if (GImNodes->NodeIdxSubmissionOrder[i] == node_idx)\n            {\n                submission_idx = i;\n                break;\n            }\n        }\n        IM_ASSERT(submission_idx >= 0);\n\n        if (submission_idx == depth_idx)\n        {\n            continue;\n        }\n\n        for (int j = submission_idx; j < depth_idx; ++j)\n        {\n            DrawListSwapSubmissionIndices(j, j + 1);\n            ImSwap(GImNodes->NodeIdxSubmissionOrder[j], GImNodes->NodeIdxSubmissionOrder[j + 1]);\n        }\n    }\n}\n\n// [SECTION] ui state logic\n\nImVec2 GetScreenSpacePinCoordinates(\n    const ImRect&              node_rect,\n    const ImRect&              attribute_rect,\n    const ImNodesAttributeType type)\n{\n    IM_ASSERT(type == ImNodesAttributeType_Input || type == ImNodesAttributeType_Output);\n    const float x = type == ImNodesAttributeType_Input\n                        ? (node_rect.Min.x - GImNodes->Style.PinOffset)\n                        : (node_rect.Max.x + GImNodes->Style.PinOffset);\n    return ImVec2(x, 0.5f * (attribute_rect.Min.y + attribute_rect.Max.y));\n}\n\nImVec2 GetScreenSpacePinCoordinates(const ImNodesEditorContext& editor, const ImPinData& pin)\n{\n    const ImRect& parent_node_rect = editor.Nodes.Pool[pin.ParentNodeIdx].Rect;\n    return GetScreenSpacePinCoordinates(parent_node_rect, pin.AttributeRect, pin.Type);\n}\n\nbool MouseInCanvas()\n{\n    // This flag should be true either when hovering or clicking something in the canvas.\n    const bool is_window_hovered_or_focused = ImGui::IsWindowHovered() || ImGui::IsWindowFocused();\n\n    return is_window_hovered_or_focused &&\n           GImNodes->CanvasRectScreenSpace.Contains(ImGui::GetMousePos());\n}\n\nvoid BeginNodeSelection(ImNodesEditorContext& editor, const int node_idx)\n{\n    // Don't start selecting a node if we are e.g. already creating and dragging\n    // a new link! New link creation can happen when the mouse is clicked over\n    // a node, but within the hover radius of a pin.\n    if (editor.ClickInteraction.Type != ImNodesClickInteractionType_None)\n    {\n        return;\n    }\n\n    editor.ClickInteraction.Type = ImNodesClickInteractionType_Node;\n    // If the node is not already contained in the selection, then we want only\n    // the interaction node to be selected, effective immediately.\n    //\n    // If the multiple selection modifier is active, we want to add this node\n    // to the current list of selected nodes.\n    //\n    // Otherwise, we want to allow for the possibility of multiple nodes to be\n    // moved at once.\n    if (!editor.SelectedNodeIndices.contains(node_idx))\n    {\n        editor.SelectedLinkIndices.clear();\n        if (!GImNodes->MultipleSelectModifier)\n        {\n            editor.SelectedNodeIndices.clear();\n        }\n        editor.SelectedNodeIndices.push_back(node_idx);\n\n        // Ensure that individually selected nodes get rendered on top\n        ImVector<int>&   depth_stack = editor.NodeDepthOrder;\n        const int* const elem = depth_stack.find(node_idx);\n        IM_ASSERT(elem != depth_stack.end());\n        depth_stack.erase(elem);\n        depth_stack.push_back(node_idx);\n    }\n    // Deselect a previously-selected node\n    else if (GImNodes->MultipleSelectModifier)\n    {\n        const int* const node_ptr = editor.SelectedNodeIndices.find(node_idx);\n        editor.SelectedNodeIndices.erase(node_ptr);\n\n        // Don't allow dragging after deselecting\n        editor.ClickInteraction.Type = ImNodesClickInteractionType_None;\n    }\n\n    // To support snapping of multiple nodes, we need to store the offset of\n    // each node in the selection to the origin of the dragged node.\n    const ImVec2 ref_origin = editor.Nodes.Pool[node_idx].Origin;\n    editor.PrimaryNodeOffset =\n        ref_origin + GImNodes->CanvasOriginScreenSpace + editor.Panning - GImNodes->MousePos;\n\n    editor.SelectedNodeOffsets.clear();\n    for (int idx = 0; idx < editor.SelectedNodeIndices.Size; idx++)\n    {\n        const int    node = editor.SelectedNodeIndices[idx];\n        const ImVec2 node_origin = editor.Nodes.Pool[node].Origin - ref_origin;\n        editor.SelectedNodeOffsets.push_back(node_origin);\n    }\n}\n\nvoid BeginLinkSelection(ImNodesEditorContext& editor, const int link_idx)\n{\n    editor.ClickInteraction.Type = ImNodesClickInteractionType_Link;\n    // When a link is selected, clear all other selections, and insert the link\n    // as the sole selection.\n    editor.SelectedNodeIndices.clear();\n    editor.SelectedLinkIndices.clear();\n    editor.SelectedLinkIndices.push_back(link_idx);\n}\n\nvoid BeginLinkDetach(ImNodesEditorContext& editor, const int link_idx, const int detach_pin_idx)\n{\n    const ImLinkData&        link = editor.Links.Pool[link_idx];\n    ImClickInteractionState& state = editor.ClickInteraction;\n    state.Type = ImNodesClickInteractionType_LinkCreation;\n    state.LinkCreation.EndPinIdx.Reset();\n    state.LinkCreation.StartPinIdx =\n        detach_pin_idx == link.StartPinIdx ? link.EndPinIdx : link.StartPinIdx;\n    GImNodes->DeletedLinkIdx = link_idx;\n}\n\nvoid BeginLinkCreation(ImNodesEditorContext& editor, const int hovered_pin_idx)\n{\n    editor.ClickInteraction.Type = ImNodesClickInteractionType_LinkCreation;\n    editor.ClickInteraction.LinkCreation.StartPinIdx = hovered_pin_idx;\n    editor.ClickInteraction.LinkCreation.EndPinIdx.Reset();\n    editor.ClickInteraction.LinkCreation.Type = ImNodesLinkCreationType_Standard;\n    GImNodes->ImNodesUIState |= ImNodesUIState_LinkStarted;\n}\n\nvoid BeginLinkInteraction(\n    ImNodesEditorContext& editor,\n    const int             link_idx,\n    const ImOptionalIndex pin_idx = ImOptionalIndex())\n{\n    // Check if we are clicking the link with the modifier pressed.\n    // This will in a link detach via clicking.\n\n    const bool modifier_pressed = GImNodes->Io.LinkDetachWithModifierClick.Modifier == NULL\n                                      ? false\n                                      : *GImNodes->Io.LinkDetachWithModifierClick.Modifier;\n\n    if (modifier_pressed)\n    {\n        const ImLinkData& link = editor.Links.Pool[link_idx];\n        const ImPinData&  start_pin = editor.Pins.Pool[link.StartPinIdx];\n        const ImPinData&  end_pin = editor.Pins.Pool[link.EndPinIdx];\n        const ImVec2&     mouse_pos = GImNodes->MousePos;\n        const float       dist_to_start = ImLengthSqr(start_pin.Pos - mouse_pos);\n        const float       dist_to_end = ImLengthSqr(end_pin.Pos - mouse_pos);\n        const int closest_pin_idx = dist_to_start < dist_to_end ? link.StartPinIdx : link.EndPinIdx;\n\n        editor.ClickInteraction.Type = ImNodesClickInteractionType_LinkCreation;\n        BeginLinkDetach(editor, link_idx, closest_pin_idx);\n        editor.ClickInteraction.LinkCreation.Type = ImNodesLinkCreationType_FromDetach;\n    }\n    else\n    {\n        if (pin_idx.HasValue())\n        {\n            const int hovered_pin_flags = editor.Pins.Pool[pin_idx.Value()].Flags;\n\n            // Check the 'click and drag to detach' case.\n            if (hovered_pin_flags & ImNodesAttributeFlags_EnableLinkDetachWithDragClick)\n            {\n                BeginLinkDetach(editor, link_idx, pin_idx.Value());\n                editor.ClickInteraction.LinkCreation.Type = ImNodesLinkCreationType_FromDetach;\n            }\n            else\n            {\n                BeginLinkCreation(editor, pin_idx.Value());\n            }\n        }\n        else\n        {\n            BeginLinkSelection(editor, link_idx);\n        }\n    }\n}\n\nstatic inline bool IsMiniMapHovered();\n\nvoid BeginCanvasInteraction(ImNodesEditorContext& editor)\n{\n    const bool any_ui_element_hovered =\n        GImNodes->HoveredNodeIdx.HasValue() || GImNodes->HoveredLinkIdx.HasValue() ||\n        GImNodes->HoveredPinIdx.HasValue() || ImGui::IsAnyItemHovered();\n\n    const bool mouse_not_in_canvas = !MouseInCanvas();\n\n    if (editor.ClickInteraction.Type != ImNodesClickInteractionType_None ||\n        any_ui_element_hovered || mouse_not_in_canvas)\n    {\n        return;\n    }\n\n    const bool started_panning = GImNodes->AltMouseClicked;\n\n    if (started_panning)\n    {\n        editor.ClickInteraction.Type = ImNodesClickInteractionType_Panning;\n    }\n    else if (GImNodes->LeftMouseClicked)\n    {\n        editor.ClickInteraction.Type = ImNodesClickInteractionType_BoxSelection;\n        editor.ClickInteraction.BoxSelector.Rect.Min =\n            ScreenSpaceToGridSpace(editor, GImNodes->MousePos);\n    }\n}\n\nvoid BoxSelectorUpdateSelection(ImNodesEditorContext& editor, ImRect box_rect)\n{\n    // Invert box selector coordinates as needed\n\n    if (box_rect.Min.x > box_rect.Max.x)\n    {\n        ImSwap(box_rect.Min.x, box_rect.Max.x);\n    }\n\n    if (box_rect.Min.y > box_rect.Max.y)\n    {\n        ImSwap(box_rect.Min.y, box_rect.Max.y);\n    }\n\n    // Update node selection\n\n    editor.SelectedNodeIndices.clear();\n\n    // Test for overlap against node rectangles\n\n    for (int node_idx = 0; node_idx < editor.Nodes.Pool.size(); ++node_idx)\n    {\n        if (editor.Nodes.InUse[node_idx])\n        {\n            ImNodeData& node = editor.Nodes.Pool[node_idx];\n            if (box_rect.Overlaps(node.Rect))\n            {\n                editor.SelectedNodeIndices.push_back(node_idx);\n            }\n        }\n    }\n\n    // Update link selection\n\n    editor.SelectedLinkIndices.clear();\n\n    // Test for overlap against links\n\n    for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx)\n    {\n        if (editor.Links.InUse[link_idx])\n        {\n            const ImLinkData& link = editor.Links.Pool[link_idx];\n\n            const ImPinData& pin_start = editor.Pins.Pool[link.StartPinIdx];\n            const ImPinData& pin_end = editor.Pins.Pool[link.EndPinIdx];\n            const ImRect&    node_start_rect = editor.Nodes.Pool[pin_start.ParentNodeIdx].Rect;\n            const ImRect&    node_end_rect = editor.Nodes.Pool[pin_end.ParentNodeIdx].Rect;\n\n            const ImVec2 start = GetScreenSpacePinCoordinates(\n                node_start_rect, pin_start.AttributeRect, pin_start.Type);\n            const ImVec2 end =\n                GetScreenSpacePinCoordinates(node_end_rect, pin_end.AttributeRect, pin_end.Type);\n\n            // Test\n            if (RectangleOverlapsLink(box_rect, start, end, pin_start.Type))\n            {\n                editor.SelectedLinkIndices.push_back(link_idx);\n            }\n        }\n    }\n}\n\nImVec2 SnapOriginToGrid(ImVec2 origin)\n{\n    if (GImNodes->Style.Flags & ImNodesStyleFlags_GridSnapping)\n    {\n        const float spacing = GImNodes->Style.GridSpacing;\n        const float spacing2 = spacing * 0.5f;\n\n        // Snap the origin to the nearest grid point in any direction\n        float modx = fmodf(fabsf(origin.x) + spacing2, spacing) - spacing2;\n        float mody = fmodf(fabsf(origin.y) + spacing2, spacing) - spacing2;\n        origin.x += (origin.x < 0.f) ? modx : -modx;\n        origin.y += (origin.y < 0.f) ? mody : -mody;\n    }\n\n    return origin;\n}\n\nvoid TranslateSelectedNodes(ImNodesEditorContext& editor)\n{\n    if (GImNodes->LeftMouseDragging)\n    {\n        // If we have grid snap enabled, don't start moving nodes until we've moved the mouse\n        // slightly\n        const bool shouldTranslate = (GImNodes->Style.Flags & ImNodesStyleFlags_GridSnapping)\n                                         ? ImGui::GetIO().MouseDragMaxDistanceSqr[0] > 5.0\n                                         : true;\n\n        const ImVec2 origin = SnapOriginToGrid(\n            GImNodes->MousePos - GImNodes->CanvasOriginScreenSpace - editor.Panning +\n            editor.PrimaryNodeOffset);\n        for (int i = 0; i < editor.SelectedNodeIndices.size(); ++i)\n        {\n            const ImVec2 node_rel = editor.SelectedNodeOffsets[i];\n            const int    node_idx = editor.SelectedNodeIndices[i];\n            ImNodeData&  node = editor.Nodes.Pool[node_idx];\n            if (node.Draggable && shouldTranslate)\n            {\n                node.Origin = origin + node_rel + editor.AutoPanningDelta;\n            }\n        }\n    }\n}\n\nstruct LinkPredicate\n{\n    bool operator()(const ImLinkData& lhs, const ImLinkData& rhs) const\n    {\n        // Do a unique compare by sorting the pins' addresses.\n        // This catches duplicate links, whether they are in the\n        // same direction or not.\n        // Sorting by pin index should have the uniqueness guarantees as sorting\n        // by id -- each unique id will get one slot in the link pool array.\n\n        int lhs_start = lhs.StartPinIdx;\n        int lhs_end = lhs.EndPinIdx;\n        int rhs_start = rhs.StartPinIdx;\n        int rhs_end = rhs.EndPinIdx;\n\n        if (lhs_start > lhs_end)\n        {\n            ImSwap(lhs_start, lhs_end);\n        }\n\n        if (rhs_start > rhs_end)\n        {\n            ImSwap(rhs_start, rhs_end);\n        }\n\n        return lhs_start == rhs_start && lhs_end == rhs_end;\n    }\n};\n\nImOptionalIndex FindDuplicateLink(\n    const ImNodesEditorContext& editor,\n    const int                   start_pin_idx,\n    const int                   end_pin_idx)\n{\n    ImLinkData test_link(0);\n    test_link.StartPinIdx = start_pin_idx;\n    test_link.EndPinIdx = end_pin_idx;\n    for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx)\n    {\n        const ImLinkData& link = editor.Links.Pool[link_idx];\n        if (LinkPredicate()(test_link, link) && editor.Links.InUse[link_idx])\n        {\n            return ImOptionalIndex(link_idx);\n        }\n    }\n\n    return ImOptionalIndex();\n}\n\nbool ShouldLinkSnapToPin(\n    const ImNodesEditorContext& editor,\n    const ImPinData&            start_pin,\n    const int                   hovered_pin_idx,\n    const ImOptionalIndex       duplicate_link)\n{\n    const ImPinData& end_pin = editor.Pins.Pool[hovered_pin_idx];\n\n    // The end pin must be in a different node\n    if (start_pin.ParentNodeIdx == end_pin.ParentNodeIdx)\n    {\n        return false;\n    }\n\n    // The end pin must be of a different type\n    if (start_pin.Type == end_pin.Type)\n    {\n        return false;\n    }\n\n    // The link to be created must not be a duplicate, unless it is the link which was created on\n    // snap. In that case we want to snap, since we want it to appear visually as if the created\n    // link remains snapped to the pin.\n    if (duplicate_link.HasValue() && !(duplicate_link == GImNodes->SnapLinkIdx))\n    {\n        return false;\n    }\n\n    return true;\n}\n\nvoid ClickInteractionUpdate(ImNodesEditorContext& editor)\n{\n    switch (editor.ClickInteraction.Type)\n    {\n    case ImNodesClickInteractionType_BoxSelection:\n    {\n        editor.ClickInteraction.BoxSelector.Rect.Max =\n            ScreenSpaceToGridSpace(editor, GImNodes->MousePos);\n\n        ImRect box_rect = editor.ClickInteraction.BoxSelector.Rect;\n        box_rect.Min = GridSpaceToScreenSpace(editor, box_rect.Min);\n        box_rect.Max = GridSpaceToScreenSpace(editor, box_rect.Max);\n\n        BoxSelectorUpdateSelection(editor, box_rect);\n\n        const ImU32 box_selector_color = GImNodes->Style.Colors[ImNodesCol_BoxSelector];\n        const ImU32 box_selector_outline = GImNodes->Style.Colors[ImNodesCol_BoxSelectorOutline];\n        GImNodes->CanvasDrawList->AddRectFilled(box_rect.Min, box_rect.Max, box_selector_color);\n        GImNodes->CanvasDrawList->AddRect(box_rect.Min, box_rect.Max, box_selector_outline);\n\n        if (GImNodes->LeftMouseReleased)\n        {\n            ImVector<int>&       depth_stack = editor.NodeDepthOrder;\n            const ImVector<int>& selected_idxs = editor.SelectedNodeIndices;\n\n            // Bump the selected node indices, in order, to the top of the depth stack.\n            // NOTE: this algorithm has worst case time complexity of O(N^2), if the node selection\n            // is ~ N (due to selected_idxs.contains()).\n\n            if ((selected_idxs.Size > 0) && (selected_idxs.Size < depth_stack.Size))\n            {\n                int num_moved = 0; // The number of indices moved. Stop after selected_idxs.Size\n                for (int i = 0; i < depth_stack.Size - selected_idxs.Size; ++i)\n                {\n                    for (int node_idx = depth_stack[i]; selected_idxs.contains(node_idx);\n                         node_idx = depth_stack[i])\n                    {\n                        depth_stack.erase(depth_stack.begin() + static_cast<size_t>(i));\n                        depth_stack.push_back(node_idx);\n                        ++num_moved;\n                    }\n\n                    if (num_moved == selected_idxs.Size)\n                    {\n                        break;\n                    }\n                }\n            }\n\n            editor.ClickInteraction.Type = ImNodesClickInteractionType_None;\n        }\n    }\n    break;\n    case ImNodesClickInteractionType_Node:\n    {\n        TranslateSelectedNodes(editor);\n\n        if (GImNodes->LeftMouseReleased)\n        {\n            editor.ClickInteraction.Type = ImNodesClickInteractionType_None;\n        }\n    }\n    break;\n    case ImNodesClickInteractionType_Link:\n    {\n        if (GImNodes->LeftMouseReleased)\n        {\n            editor.ClickInteraction.Type = ImNodesClickInteractionType_None;\n        }\n    }\n    break;\n    case ImNodesClickInteractionType_LinkCreation:\n    {\n        const ImPinData& start_pin =\n            editor.Pins.Pool[editor.ClickInteraction.LinkCreation.StartPinIdx];\n\n        const ImOptionalIndex maybe_duplicate_link_idx =\n            GImNodes->HoveredPinIdx.HasValue()\n                ? FindDuplicateLink(\n                      editor,\n                      editor.ClickInteraction.LinkCreation.StartPinIdx,\n                      GImNodes->HoveredPinIdx.Value())\n                : ImOptionalIndex();\n\n        const bool should_snap =\n            GImNodes->HoveredPinIdx.HasValue() &&\n            ShouldLinkSnapToPin(\n                editor, start_pin, GImNodes->HoveredPinIdx.Value(), maybe_duplicate_link_idx);\n\n        // If we created on snap and the hovered pin is empty or changed, then we need signal that\n        // the link's state has changed.\n        const bool snapping_pin_changed =\n            editor.ClickInteraction.LinkCreation.EndPinIdx.HasValue() &&\n            !(GImNodes->HoveredPinIdx == editor.ClickInteraction.LinkCreation.EndPinIdx);\n\n        // Detach the link that was created by this link event if it's no longer in snap range\n        if (snapping_pin_changed && GImNodes->SnapLinkIdx.HasValue())\n        {\n            BeginLinkDetach(\n                editor,\n                GImNodes->SnapLinkIdx.Value(),\n                editor.ClickInteraction.LinkCreation.EndPinIdx.Value());\n        }\n\n        const ImVec2 start_pos = GetScreenSpacePinCoordinates(editor, start_pin);\n        // If we are within the hover radius of a receiving pin, snap the link\n        // endpoint to it\n        const ImVec2 end_pos = should_snap\n                                   ? GetScreenSpacePinCoordinates(\n                                         editor, editor.Pins.Pool[GImNodes->HoveredPinIdx.Value()])\n                                   : GImNodes->MousePos;\n\n        const CubicBezier cubic_bezier = GetCubicBezier(\n            start_pos, end_pos, start_pin.Type, GImNodes->Style.LinkLineSegmentsPerLength);\n#if IMGUI_VERSION_NUM < 18000\n        GImNodes->CanvasDrawList->AddBezierCurve(\n#else\n        GImNodes->CanvasDrawList->AddBezierCubic(\n#endif\n            cubic_bezier.P0,\n            cubic_bezier.P1,\n            cubic_bezier.P2,\n            cubic_bezier.P3,\n            GImNodes->Style.Colors[ImNodesCol_Link],\n            GImNodes->Style.LinkThickness,\n            cubic_bezier.NumSegments);\n\n        const bool link_creation_on_snap =\n            GImNodes->HoveredPinIdx.HasValue() &&\n            (editor.Pins.Pool[GImNodes->HoveredPinIdx.Value()].Flags &\n             ImNodesAttributeFlags_EnableLinkCreationOnSnap);\n\n        if (!should_snap)\n        {\n            editor.ClickInteraction.LinkCreation.EndPinIdx.Reset();\n        }\n\n        const bool create_link =\n            should_snap && (GImNodes->LeftMouseReleased || link_creation_on_snap);\n\n        if (create_link && !maybe_duplicate_link_idx.HasValue())\n        {\n            // Avoid send OnLinkCreated() events every frame if the snap link is not saved\n            // (only applies for EnableLinkCreationOnSnap)\n            if (!GImNodes->LeftMouseReleased &&\n                editor.ClickInteraction.LinkCreation.EndPinIdx == GImNodes->HoveredPinIdx)\n            {\n                break;\n            }\n\n            GImNodes->ImNodesUIState |= ImNodesUIState_LinkCreated;\n            editor.ClickInteraction.LinkCreation.EndPinIdx = GImNodes->HoveredPinIdx.Value();\n        }\n\n        if (GImNodes->LeftMouseReleased)\n        {\n            editor.ClickInteraction.Type = ImNodesClickInteractionType_None;\n            if (!create_link)\n            {\n                GImNodes->ImNodesUIState |= ImNodesUIState_LinkDropped;\n            }\n        }\n    }\n    break;\n    case ImNodesClickInteractionType_Panning:\n    {\n        const bool dragging = GImNodes->AltMouseDragging;\n\n        if (dragging)\n        {\n            editor.Panning += ImGui::GetIO().MouseDelta;\n        }\n        else\n        {\n            editor.ClickInteraction.Type = ImNodesClickInteractionType_None;\n        }\n    }\n    break;\n    case ImNodesClickInteractionType_ImGuiItem:\n    {\n        if (GImNodes->LeftMouseReleased)\n        {\n            editor.ClickInteraction.Type = ImNodesClickInteractionType_None;\n        }\n    }\n    case ImNodesClickInteractionType_None:\n        break;\n    default:\n        IM_ASSERT(!\"Unreachable code!\");\n        break;\n    }\n}\n\nvoid ResolveOccludedPins(const ImNodesEditorContext& editor, ImVector<int>& occluded_pin_indices)\n{\n    const ImVector<int>& depth_stack = editor.NodeDepthOrder;\n\n    occluded_pin_indices.resize(0);\n\n    if (depth_stack.Size < 2)\n    {\n        return;\n    }\n\n    // For each node in the depth stack\n    for (int depth_idx = 0; depth_idx < (depth_stack.Size - 1); ++depth_idx)\n    {\n        const ImNodeData& node_below = editor.Nodes.Pool[depth_stack[depth_idx]];\n\n        // Iterate over the rest of the depth stack to find nodes overlapping the pins\n        for (int next_depth_idx = depth_idx + 1; next_depth_idx < depth_stack.Size;\n             ++next_depth_idx)\n        {\n            const ImRect& rect_above = editor.Nodes.Pool[depth_stack[next_depth_idx]].Rect;\n\n            // Iterate over each pin\n            for (int idx = 0; idx < node_below.PinIndices.Size; ++idx)\n            {\n                const int     pin_idx = node_below.PinIndices[idx];\n                const ImVec2& pin_pos = editor.Pins.Pool[pin_idx].Pos;\n\n                if (rect_above.Contains(pin_pos))\n                {\n                    occluded_pin_indices.push_back(pin_idx);\n                }\n            }\n        }\n    }\n}\n\nImOptionalIndex ResolveHoveredPin(\n    const ImObjectPool<ImPinData>& pins,\n    const ImVector<int>&           occluded_pin_indices)\n{\n    float           smallest_distance = FLT_MAX;\n    ImOptionalIndex pin_idx_with_smallest_distance;\n\n    const float hover_radius_sqr = GImNodes->Style.PinHoverRadius * GImNodes->Style.PinHoverRadius;\n\n    for (int idx = 0; idx < pins.Pool.Size; ++idx)\n    {\n        if (!pins.InUse[idx])\n        {\n            continue;\n        }\n\n        if (occluded_pin_indices.contains(idx))\n        {\n            continue;\n        }\n\n        const ImVec2& pin_pos = pins.Pool[idx].Pos;\n        const float   distance_sqr = ImLengthSqr(pin_pos - GImNodes->MousePos);\n\n        // TODO: GImNodes->Style.PinHoverRadius needs to be copied into pin data and the pin-local\n        // value used here. This is no longer called in BeginAttribute/EndAttribute scope and the\n        // detected pin might have a different hover radius than what the user had when calling\n        // BeginAttribute/EndAttribute.\n        if (distance_sqr < hover_radius_sqr && distance_sqr < smallest_distance)\n        {\n            smallest_distance = distance_sqr;\n            pin_idx_with_smallest_distance = idx;\n        }\n    }\n\n    return pin_idx_with_smallest_distance;\n}\n\nImOptionalIndex ResolveHoveredNode(const ImVector<int>& depth_stack)\n{\n    if (GImNodes->NodeIndicesOverlappingWithMouse.size() == 0)\n    {\n        return ImOptionalIndex();\n    }\n\n    if (GImNodes->NodeIndicesOverlappingWithMouse.size() == 1)\n    {\n        return ImOptionalIndex(GImNodes->NodeIndicesOverlappingWithMouse[0]);\n    }\n\n    int largest_depth_idx = -1;\n    int node_idx_on_top = -1;\n\n    for (int i = 0; i < GImNodes->NodeIndicesOverlappingWithMouse.size(); ++i)\n    {\n        const int node_idx = GImNodes->NodeIndicesOverlappingWithMouse[i];\n        for (int depth_idx = 0; depth_idx < depth_stack.size(); ++depth_idx)\n        {\n            if (depth_stack[depth_idx] == node_idx && (depth_idx > largest_depth_idx))\n            {\n                largest_depth_idx = depth_idx;\n                node_idx_on_top = node_idx;\n            }\n        }\n    }\n\n    IM_ASSERT(node_idx_on_top != -1);\n    return ImOptionalIndex(node_idx_on_top);\n}\n\nImOptionalIndex ResolveHoveredLink(\n    const ImObjectPool<ImLinkData>& links,\n    const ImObjectPool<ImPinData>&  pins)\n{\n    float           smallest_distance = FLT_MAX;\n    ImOptionalIndex link_idx_with_smallest_distance;\n\n    // There are two ways a link can be detected as \"hovered\".\n    // 1. The link is within hover distance to the mouse. The closest such link is selected as being\n    // hovered over.\n    // 2. If the link is connected to the currently hovered pin.\n    //\n    // The latter is a requirement for link detaching with drag click to work, as both a link and\n    // pin are required to be hovered over for the feature to work.\n\n    for (int idx = 0; idx < links.Pool.Size; ++idx)\n    {\n        if (!links.InUse[idx])\n        {\n            continue;\n        }\n\n        const ImLinkData& link = links.Pool[idx];\n        const ImPinData&  start_pin = pins.Pool[link.StartPinIdx];\n        const ImPinData&  end_pin = pins.Pool[link.EndPinIdx];\n\n        // If there is a hovered pin links can only be considered hovered if they use that pin\n        if (GImNodes->HoveredPinIdx.HasValue())\n        {\n            if (GImNodes->HoveredPinIdx == link.StartPinIdx ||\n                GImNodes->HoveredPinIdx == link.EndPinIdx)\n            {\n                return idx;\n            }\n            continue;\n        }\n\n        // TODO: the calculated CubicBeziers could be cached since we generate them again when\n        // rendering the links\n\n        const CubicBezier cubic_bezier = GetCubicBezier(\n            start_pin.Pos, end_pin.Pos, start_pin.Type, GImNodes->Style.LinkLineSegmentsPerLength);\n\n        // The distance test\n        {\n            const ImRect link_rect = GetContainingRectForCubicBezier(cubic_bezier);\n\n            // First, do a simple bounding box test against the box containing the link\n            // to see whether calculating the distance to the link is worth doing.\n            if (link_rect.Contains(GImNodes->MousePos))\n            {\n                const float distance = GetDistanceToCubicBezier(\n                    GImNodes->MousePos, cubic_bezier, cubic_bezier.NumSegments);\n\n                // TODO: GImNodes->Style.LinkHoverDistance could be also copied into ImLinkData,\n                // since we're not calling this function in the same scope as ImNodes::Link(). The\n                // rendered/detected link might have a different hover distance than what the user\n                // had specified when calling Link()\n                if (distance < GImNodes->Style.LinkHoverDistance && distance < smallest_distance)\n                {\n                    smallest_distance = distance;\n                    link_idx_with_smallest_distance = idx;\n                }\n            }\n        }\n    }\n\n    return link_idx_with_smallest_distance;\n}\n\n// [SECTION] render helpers\n\ninline ImRect GetItemRect() { return ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); }\n\ninline ImVec2 GetNodeTitleBarOrigin(const ImNodeData& node)\n{\n    return node.Origin + node.LayoutStyle.Padding;\n}\n\ninline ImVec2 GetNodeContentOrigin(const ImNodeData& node)\n{\n    const ImVec2 title_bar_height =\n        ImVec2(0.f, node.TitleBarContentRect.GetHeight() + 2.0f * node.LayoutStyle.Padding.y);\n    return node.Origin + title_bar_height + node.LayoutStyle.Padding;\n}\n\ninline ImRect GetNodeTitleRect(const ImNodeData& node)\n{\n    ImRect expanded_title_rect = node.TitleBarContentRect;\n    expanded_title_rect.Expand(node.LayoutStyle.Padding);\n\n    return ImRect(\n        expanded_title_rect.Min,\n        expanded_title_rect.Min + ImVec2(node.Rect.GetWidth(), 0.f) +\n            ImVec2(0.f, expanded_title_rect.GetHeight()));\n}\n\nvoid DrawGrid(ImNodesEditorContext& editor, const ImVec2& canvas_size)\n{\n    const ImVec2 offset = editor.Panning;\n    ImU32        line_color = GImNodes->Style.Colors[ImNodesCol_GridLine];\n    ImU32        line_color_prim = GImNodes->Style.Colors[ImNodesCol_GridLinePrimary];\n    bool         draw_primary = GImNodes->Style.Flags & ImNodesStyleFlags_GridLinesPrimary;\n\n    for (float x = fmodf(offset.x, GImNodes->Style.GridSpacing); x < canvas_size.x;\n         x += GImNodes->Style.GridSpacing)\n    {\n        GImNodes->CanvasDrawList->AddLine(\n            EditorSpaceToScreenSpace(ImVec2(x, 0.0f)),\n            EditorSpaceToScreenSpace(ImVec2(x, canvas_size.y)),\n            offset.x - x == 0.f && draw_primary ? line_color_prim : line_color);\n    }\n\n    for (float y = fmodf(offset.y, GImNodes->Style.GridSpacing); y < canvas_size.y;\n         y += GImNodes->Style.GridSpacing)\n    {\n        GImNodes->CanvasDrawList->AddLine(\n            EditorSpaceToScreenSpace(ImVec2(0.0f, y)),\n            EditorSpaceToScreenSpace(ImVec2(canvas_size.x, y)),\n            offset.y - y == 0.f && draw_primary ? line_color_prim : line_color);\n    }\n}\n\nstruct QuadOffsets\n{\n    ImVec2 TopLeft, BottomLeft, BottomRight, TopRight;\n};\n\nQuadOffsets CalculateQuadOffsets(const float side_length)\n{\n    const float half_side = 0.5f * side_length;\n\n    QuadOffsets offset;\n\n    offset.TopLeft = ImVec2(-half_side, half_side);\n    offset.BottomLeft = ImVec2(-half_side, -half_side);\n    offset.BottomRight = ImVec2(half_side, -half_side);\n    offset.TopRight = ImVec2(half_side, half_side);\n\n    return offset;\n}\n\nstruct TriangleOffsets\n{\n    ImVec2 TopLeft, BottomLeft, Right;\n};\n\nTriangleOffsets CalculateTriangleOffsets(const float side_length)\n{\n    // Calculates the Vec2 offsets from an equilateral triangle's midpoint to\n    // its vertices. Here is how the left_offset and right_offset are\n    // calculated.\n    //\n    // For an equilateral triangle of side length s, the\n    // triangle's height, h, is h = s * sqrt(3) / 2.\n    //\n    // The length from the base to the midpoint is (1 / 3) * h. The length from\n    // the midpoint to the triangle vertex is (2 / 3) * h.\n    const float sqrt_3 = sqrtf(3.0f);\n    const float left_offset = -0.1666666666667f * sqrt_3 * side_length;\n    const float right_offset = 0.333333333333f * sqrt_3 * side_length;\n    const float vertical_offset = 0.5f * side_length;\n\n    TriangleOffsets offset;\n    offset.TopLeft = ImVec2(left_offset, vertical_offset);\n    offset.BottomLeft = ImVec2(left_offset, -vertical_offset);\n    offset.Right = ImVec2(right_offset, 0.f);\n\n    return offset;\n}\n\nvoid DrawPinShape(const ImVec2& pin_pos, const ImPinData& pin, const ImU32 pin_color)\n{\n    static const int CIRCLE_NUM_SEGMENTS = 8;\n\n    switch (pin.Shape)\n    {\n    case ImNodesPinShape_Circle:\n    {\n        GImNodes->CanvasDrawList->AddCircle(\n            pin_pos,\n            GImNodes->Style.PinCircleRadius,\n            pin_color,\n            CIRCLE_NUM_SEGMENTS,\n            GImNodes->Style.PinLineThickness);\n    }\n    break;\n    case ImNodesPinShape_CircleFilled:\n    {\n        GImNodes->CanvasDrawList->AddCircleFilled(\n            pin_pos, GImNodes->Style.PinCircleRadius, pin_color, CIRCLE_NUM_SEGMENTS);\n    }\n    break;\n    case ImNodesPinShape_Quad:\n    {\n        const QuadOffsets offset = CalculateQuadOffsets(GImNodes->Style.PinQuadSideLength);\n        GImNodes->CanvasDrawList->AddQuad(\n            pin_pos + offset.TopLeft,\n            pin_pos + offset.BottomLeft,\n            pin_pos + offset.BottomRight,\n            pin_pos + offset.TopRight,\n            pin_color,\n            GImNodes->Style.PinLineThickness);\n    }\n    break;\n    case ImNodesPinShape_QuadFilled:\n    {\n        const QuadOffsets offset = CalculateQuadOffsets(GImNodes->Style.PinQuadSideLength);\n        GImNodes->CanvasDrawList->AddQuadFilled(\n            pin_pos + offset.TopLeft,\n            pin_pos + offset.BottomLeft,\n            pin_pos + offset.BottomRight,\n            pin_pos + offset.TopRight,\n            pin_color);\n    }\n    break;\n    case ImNodesPinShape_Triangle:\n    {\n        const TriangleOffsets offset =\n            CalculateTriangleOffsets(GImNodes->Style.PinTriangleSideLength);\n        GImNodes->CanvasDrawList->AddTriangle(\n            pin_pos + offset.TopLeft,\n            pin_pos + offset.BottomLeft,\n            pin_pos + offset.Right,\n            pin_color,\n            // NOTE: for some weird reason, the line drawn by AddTriangle is\n            // much thinner than the lines drawn by AddCircle or AddQuad.\n            // Multiplying the line thickness by two seemed to solve the\n            // problem at a few different thickness values.\n            2.f * GImNodes->Style.PinLineThickness);\n    }\n    break;\n    case ImNodesPinShape_TriangleFilled:\n    {\n        const TriangleOffsets offset =\n            CalculateTriangleOffsets(GImNodes->Style.PinTriangleSideLength);\n        GImNodes->CanvasDrawList->AddTriangleFilled(\n            pin_pos + offset.TopLeft,\n            pin_pos + offset.BottomLeft,\n            pin_pos + offset.Right,\n            pin_color);\n    }\n    break;\n    default:\n        IM_ASSERT(!\"Invalid PinShape value!\");\n        break;\n    }\n}\n\nvoid DrawPin(ImNodesEditorContext& editor, const int pin_idx)\n{\n    ImPinData&    pin = editor.Pins.Pool[pin_idx];\n    const ImRect& parent_node_rect = editor.Nodes.Pool[pin.ParentNodeIdx].Rect;\n\n    pin.Pos = GetScreenSpacePinCoordinates(parent_node_rect, pin.AttributeRect, pin.Type);\n\n    ImU32 pin_color = pin.ColorStyle.Background;\n\n    if (GImNodes->HoveredPinIdx == pin_idx)\n    {\n        pin_color = pin.ColorStyle.Hovered;\n    }\n\n    DrawPinShape(pin.Pos, pin, pin_color);\n}\n\nvoid DrawNode(ImNodesEditorContext& editor, const int node_idx)\n{\n    const ImNodeData& node = editor.Nodes.Pool[node_idx];\n    ImGui::SetCursorPos(node.Origin + editor.Panning);\n\n    const bool node_hovered =\n        GImNodes->HoveredNodeIdx == node_idx &&\n        editor.ClickInteraction.Type != ImNodesClickInteractionType_BoxSelection;\n\n    ImU32 node_background = node.ColorStyle.Background;\n    ImU32 titlebar_background = node.ColorStyle.Titlebar;\n\n    if (editor.SelectedNodeIndices.contains(node_idx))\n    {\n        node_background = node.ColorStyle.BackgroundSelected;\n        titlebar_background = node.ColorStyle.TitlebarSelected;\n    }\n    else if (node_hovered)\n    {\n        node_background = node.ColorStyle.BackgroundHovered;\n        titlebar_background = node.ColorStyle.TitlebarHovered;\n    }\n\n    {\n        // node base\n        GImNodes->CanvasDrawList->AddRectFilled(\n            node.Rect.Min, node.Rect.Max, node_background, node.LayoutStyle.CornerRounding);\n\n        // title bar:\n        if (node.TitleBarContentRect.GetHeight() > 0.f)\n        {\n            ImRect title_bar_rect = GetNodeTitleRect(node);\n\n#if IMGUI_VERSION_NUM < 18200\n            GImNodes->CanvasDrawList->AddRectFilled(\n                title_bar_rect.Min,\n                title_bar_rect.Max,\n                titlebar_background,\n                node.LayoutStyle.CornerRounding,\n                ImDrawCornerFlags_Top);\n#else\n            GImNodes->CanvasDrawList->AddRectFilled(\n                title_bar_rect.Min,\n                title_bar_rect.Max,\n                titlebar_background,\n                node.LayoutStyle.CornerRounding,\n                ImDrawFlags_RoundCornersTop);\n\n#endif\n        }\n\n        if ((GImNodes->Style.Flags & ImNodesStyleFlags_NodeOutline) != 0)\n        {\n#if IMGUI_VERSION_NUM < 18200\n            GImNodes->CanvasDrawList->AddRect(\n                node.Rect.Min,\n                node.Rect.Max,\n                node.ColorStyle.Outline,\n                node.LayoutStyle.CornerRounding,\n                ImDrawCornerFlags_All,\n                node.LayoutStyle.BorderThickness);\n#else\n            GImNodes->CanvasDrawList->AddRect(\n                node.Rect.Min,\n                node.Rect.Max,\n                node.ColorStyle.Outline,\n                node.LayoutStyle.CornerRounding,\n                ImDrawFlags_RoundCornersAll,\n                node.LayoutStyle.BorderThickness);\n#endif\n        }\n    }\n\n    for (int i = 0; i < node.PinIndices.size(); ++i)\n    {\n        DrawPin(editor, node.PinIndices[i]);\n    }\n\n    if (node_hovered)\n    {\n        GImNodes->HoveredNodeIdx = node_idx;\n    }\n}\n\nvoid DrawLink(ImNodesEditorContext& editor, const int link_idx)\n{\n    const ImLinkData& link = editor.Links.Pool[link_idx];\n    const ImPinData&  start_pin = editor.Pins.Pool[link.StartPinIdx];\n    const ImPinData&  end_pin = editor.Pins.Pool[link.EndPinIdx];\n\n    const CubicBezier cubic_bezier = GetCubicBezier(\n        start_pin.Pos, end_pin.Pos, start_pin.Type, GImNodes->Style.LinkLineSegmentsPerLength);\n\n    const bool link_hovered =\n        GImNodes->HoveredLinkIdx == link_idx &&\n        editor.ClickInteraction.Type != ImNodesClickInteractionType_BoxSelection;\n\n    if (link_hovered)\n    {\n        GImNodes->HoveredLinkIdx = link_idx;\n    }\n\n    // It's possible for a link to be deleted in begin_link_interaction. A user\n    // may detach a link, resulting in the link wire snapping to the mouse\n    // position.\n    //\n    // In other words, skip rendering the link if it was deleted.\n    if (GImNodes->DeletedLinkIdx == link_idx)\n    {\n        return;\n    }\n\n    ImU32 link_color = link.ColorStyle.Base;\n    if (editor.SelectedLinkIndices.contains(link_idx))\n    {\n        link_color = link.ColorStyle.Selected;\n    }\n    else if (link_hovered)\n    {\n        link_color = link.ColorStyle.Hovered;\n    }\n\n#if IMGUI_VERSION_NUM < 18000\n    GImNodes->CanvasDrawList->AddBezierCurve(\n#else\n    GImNodes->CanvasDrawList->AddBezierCubic(\n#endif\n        cubic_bezier.P0,\n        cubic_bezier.P1,\n        cubic_bezier.P2,\n        cubic_bezier.P3,\n        link_color,\n        GImNodes->Style.LinkThickness,\n        cubic_bezier.NumSegments);\n}\n\nvoid BeginPinAttribute(\n    const int                  id,\n    const ImNodesAttributeType type,\n    const ImNodesPinShape      shape,\n    const int                  node_idx)\n{\n    // Make sure to call BeginNode() before calling\n    // BeginAttribute()\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node);\n    GImNodes->CurrentScope = ImNodesScope_Attribute;\n\n    ImGui::BeginGroup();\n    ImGui::PushID(id);\n\n    ImNodesEditorContext& editor = EditorContextGet();\n\n    GImNodes->CurrentAttributeId = id;\n\n    const int pin_idx = ObjectPoolFindOrCreateIndex(editor.Pins, id);\n    GImNodes->CurrentPinIdx = pin_idx;\n    ImPinData& pin = editor.Pins.Pool[pin_idx];\n    pin.Id = id;\n    pin.ParentNodeIdx = node_idx;\n    pin.Type = type;\n    pin.Shape = shape;\n    pin.Flags = GImNodes->CurrentAttributeFlags;\n    pin.ColorStyle.Background = GImNodes->Style.Colors[ImNodesCol_Pin];\n    pin.ColorStyle.Hovered = GImNodes->Style.Colors[ImNodesCol_PinHovered];\n}\n\nvoid EndPinAttribute()\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Attribute);\n    GImNodes->CurrentScope = ImNodesScope_Node;\n\n    ImGui::PopID();\n    ImGui::EndGroup();\n\n    if (ImGui::IsItemActive())\n    {\n        GImNodes->ActiveAttribute = true;\n        GImNodes->ActiveAttributeId = GImNodes->CurrentAttributeId;\n    }\n\n    ImNodesEditorContext& editor = EditorContextGet();\n    ImPinData&            pin = editor.Pins.Pool[GImNodes->CurrentPinIdx];\n    ImNodeData&           node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx];\n    pin.AttributeRect = GetItemRect();\n    node.PinIndices.push_back(GImNodes->CurrentPinIdx);\n}\n\nvoid Initialize(ImNodesContext* context)\n{\n    context->CanvasOriginScreenSpace = ImVec2(0.0f, 0.0f);\n    context->CanvasRectScreenSpace = ImRect(ImVec2(0.f, 0.f), ImVec2(0.f, 0.f));\n    context->CurrentScope = ImNodesScope_None;\n\n    context->CurrentPinIdx = INT_MAX;\n    context->CurrentNodeIdx = INT_MAX;\n\n    context->DefaultEditorCtx = EditorContextCreate();\n    context->EditorCtx = context->DefaultEditorCtx;\n\n    context->CurrentAttributeFlags = ImNodesAttributeFlags_None;\n    context->AttributeFlagStack.push_back(GImNodes->CurrentAttributeFlags);\n\n    StyleColorsDark(&context->Style);\n}\n\nvoid Shutdown(ImNodesContext* ctx) { EditorContextFree(ctx->DefaultEditorCtx); }\n\n// [SECTION] minimap\n\nstatic inline bool IsMiniMapActive()\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    return editor.MiniMapEnabled && editor.MiniMapSizeFraction > 0.0f;\n}\n\nstatic inline bool IsMiniMapHovered()\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    return IsMiniMapActive() &&\n           ImGui::IsMouseHoveringRect(\n               editor.MiniMapRectScreenSpace.Min, editor.MiniMapRectScreenSpace.Max);\n}\n\nstatic inline void CalcMiniMapLayout()\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    const ImVec2          offset = GImNodes->Style.MiniMapOffset;\n    const ImVec2          border = GImNodes->Style.MiniMapPadding;\n    const ImRect          editor_rect = GImNodes->CanvasRectScreenSpace;\n\n    // Compute the size of the mini-map area\n    ImVec2 mini_map_size;\n    float  mini_map_scaling;\n    {\n        const ImVec2 max_size =\n            ImFloor(editor_rect.GetSize() * editor.MiniMapSizeFraction - border * 2.0f);\n        const float  max_size_aspect_ratio = max_size.x / max_size.y;\n        const ImVec2 grid_content_size = editor.GridContentBounds.IsInverted()\n                                             ? max_size\n                                             : ImFloor(editor.GridContentBounds.GetSize());\n        const float grid_content_aspect_ratio = grid_content_size.x / grid_content_size.y;\n        mini_map_size = ImFloor(\n            grid_content_aspect_ratio > max_size_aspect_ratio\n                ? ImVec2(max_size.x, max_size.x / grid_content_aspect_ratio)\n                : ImVec2(max_size.y * grid_content_aspect_ratio, max_size.y));\n        mini_map_scaling = mini_map_size.x / grid_content_size.x;\n    }\n\n    // Compute location of the mini-map\n    ImVec2 mini_map_pos;\n    {\n        ImVec2 align;\n\n        switch (editor.MiniMapLocation)\n        {\n        case ImNodesMiniMapLocation_BottomRight:\n            align.x = 1.0f;\n            align.y = 1.0f;\n            break;\n        case ImNodesMiniMapLocation_BottomLeft:\n            align.x = 0.0f;\n            align.y = 1.0f;\n            break;\n        case ImNodesMiniMapLocation_TopRight:\n            align.x = 1.0f;\n            align.y = 0.0f;\n            break;\n        case ImNodesMiniMapLocation_TopLeft: // [[fallthrough]]\n        default:\n            align.x = 0.0f;\n            align.y = 0.0f;\n            break;\n        }\n\n        const ImVec2 top_left_pos = editor_rect.Min + offset + border;\n        const ImVec2 bottom_right_pos = editor_rect.Max - offset - border - mini_map_size;\n        mini_map_pos = ImFloor(ImLerp(top_left_pos, bottom_right_pos, align));\n    }\n\n    editor.MiniMapRectScreenSpace =\n        ImRect(mini_map_pos - border, mini_map_pos + mini_map_size + border);\n    editor.MiniMapContentScreenSpace = ImRect(mini_map_pos, mini_map_pos + mini_map_size);\n    editor.MiniMapScaling = mini_map_scaling;\n}\n\nstatic void MiniMapDrawNode(ImNodesEditorContext& editor, const int node_idx)\n{\n    const ImNodeData& node = editor.Nodes.Pool[node_idx];\n\n    const ImRect node_rect = ScreenSpaceToMiniMapSpace(editor, node.Rect);\n\n    // Round to near whole pixel value for corner-rounding to prevent visual glitches\n    const float mini_map_node_rounding =\n        floorf(node.LayoutStyle.CornerRounding * editor.MiniMapScaling);\n\n    ImU32 mini_map_node_background;\n\n    if (editor.ClickInteraction.Type == ImNodesClickInteractionType_None &&\n        ImGui::IsMouseHoveringRect(node_rect.Min, node_rect.Max))\n    {\n        mini_map_node_background = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeBackgroundHovered];\n\n        // Run user callback when hovering a mini-map node\n        if (editor.MiniMapNodeHoveringCallback)\n        {\n            editor.MiniMapNodeHoveringCallback(node.Id, editor.MiniMapNodeHoveringCallbackUserData);\n        }\n    }\n    else if (editor.SelectedNodeIndices.contains(node_idx))\n    {\n        mini_map_node_background = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeBackgroundSelected];\n    }\n    else\n    {\n        mini_map_node_background = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeBackground];\n    }\n\n    const ImU32 mini_map_node_outline = GImNodes->Style.Colors[ImNodesCol_MiniMapNodeOutline];\n\n    GImNodes->CanvasDrawList->AddRectFilled(\n        node_rect.Min, node_rect.Max, mini_map_node_background, mini_map_node_rounding);\n\n    GImNodes->CanvasDrawList->AddRect(\n        node_rect.Min, node_rect.Max, mini_map_node_outline, mini_map_node_rounding);\n}\n\nstatic void MiniMapDrawLink(ImNodesEditorContext& editor, const int link_idx)\n{\n    const ImLinkData& link = editor.Links.Pool[link_idx];\n    const ImPinData&  start_pin = editor.Pins.Pool[link.StartPinIdx];\n    const ImPinData&  end_pin = editor.Pins.Pool[link.EndPinIdx];\n\n    const CubicBezier cubic_bezier = GetCubicBezier(\n        ScreenSpaceToMiniMapSpace(editor, start_pin.Pos),\n        ScreenSpaceToMiniMapSpace(editor, end_pin.Pos),\n        start_pin.Type,\n        GImNodes->Style.LinkLineSegmentsPerLength / editor.MiniMapScaling);\n\n    // It's possible for a link to be deleted in begin_link_interaction. A user\n    // may detach a link, resulting in the link wire snapping to the mouse\n    // position.\n    //\n    // In other words, skip rendering the link if it was deleted.\n    if (GImNodes->DeletedLinkIdx == link_idx)\n    {\n        return;\n    }\n\n    const ImU32 link_color =\n        GImNodes->Style.Colors\n            [editor.SelectedLinkIndices.contains(link_idx) ? ImNodesCol_MiniMapLinkSelected\n                                                           : ImNodesCol_MiniMapLink];\n\n#if IMGUI_VERSION_NUM < 18000\n    GImNodes->CanvasDrawList->AddBezierCurve(\n#else\n    GImNodes->CanvasDrawList->AddBezierCubic(\n#endif\n        cubic_bezier.P0,\n        cubic_bezier.P1,\n        cubic_bezier.P2,\n        cubic_bezier.P3,\n        link_color,\n        GImNodes->Style.LinkThickness * editor.MiniMapScaling,\n        cubic_bezier.NumSegments);\n}\n\nstatic void MiniMapUpdate()\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n\n    ImU32 mini_map_background;\n\n    if (IsMiniMapHovered())\n    {\n        mini_map_background = GImNodes->Style.Colors[ImNodesCol_MiniMapBackgroundHovered];\n    }\n    else\n    {\n        mini_map_background = GImNodes->Style.Colors[ImNodesCol_MiniMapBackground];\n    }\n\n    // Create a child window bellow mini-map, so it blocks all mouse interaction on canvas.\n    int flags = ImGuiWindowFlags_NoBackground;\n    ImGui::SetCursorScreenPos(editor.MiniMapRectScreenSpace.Min);\n    ImGui::BeginChild(\"minimap\", editor.MiniMapRectScreenSpace.GetSize(), false, flags);\n\n    const ImRect& mini_map_rect = editor.MiniMapRectScreenSpace;\n\n    // Draw minimap background and border\n    GImNodes->CanvasDrawList->AddRectFilled(\n        mini_map_rect.Min, mini_map_rect.Max, mini_map_background);\n\n    GImNodes->CanvasDrawList->AddRect(\n        mini_map_rect.Min, mini_map_rect.Max, GImNodes->Style.Colors[ImNodesCol_MiniMapOutline]);\n\n    // Clip draw list items to mini-map rect (after drawing background/outline)\n    GImNodes->CanvasDrawList->PushClipRect(\n        mini_map_rect.Min, mini_map_rect.Max, true /* intersect with editor clip-rect */);\n\n    // Draw links first so they appear under nodes, and we can use the same draw channel\n    for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx)\n    {\n        if (editor.Links.InUse[link_idx])\n        {\n            MiniMapDrawLink(editor, link_idx);\n        }\n    }\n\n    for (int node_idx = 0; node_idx < editor.Nodes.Pool.size(); ++node_idx)\n    {\n        if (editor.Nodes.InUse[node_idx])\n        {\n            MiniMapDrawNode(editor, node_idx);\n        }\n    }\n\n    // Draw editor canvas rect inside mini-map\n    {\n        const ImU32  canvas_color = GImNodes->Style.Colors[ImNodesCol_MiniMapCanvas];\n        const ImU32  outline_color = GImNodes->Style.Colors[ImNodesCol_MiniMapCanvasOutline];\n        const ImRect rect = ScreenSpaceToMiniMapSpace(editor, GImNodes->CanvasRectScreenSpace);\n\n        GImNodes->CanvasDrawList->AddRectFilled(rect.Min, rect.Max, canvas_color);\n        GImNodes->CanvasDrawList->AddRect(rect.Min, rect.Max, outline_color);\n    }\n\n    // Have to pop mini-map clip rect\n    GImNodes->CanvasDrawList->PopClipRect();\n\n    bool mini_map_is_hovered = ImGui::IsWindowHovered();\n\n    ImGui::EndChild();\n\n    bool center_on_click = mini_map_is_hovered && ImGui::IsMouseDown(ImGuiMouseButton_Left) &&\n                           editor.ClickInteraction.Type == ImNodesClickInteractionType_None &&\n                           !GImNodes->NodeIdxSubmissionOrder.empty();\n    if (center_on_click)\n    {\n        ImVec2 target = MiniMapSpaceToGridSpace(editor, ImGui::GetMousePos());\n        ImVec2 center = GImNodes->CanvasRectScreenSpace.GetSize() * 0.5f;\n        editor.Panning = ImFloor(center - target);\n    }\n\n    // Reset callback info after use\n    editor.MiniMapNodeHoveringCallback = NULL;\n    editor.MiniMapNodeHoveringCallbackUserData = NULL;\n}\n\n// [SECTION] selection helpers\n\ntemplate<typename T>\nvoid SelectObject(const ImObjectPool<T>& objects, ImVector<int>& selected_indices, const int id)\n{\n    const int idx = ObjectPoolFind(objects, id);\n    IM_ASSERT(idx >= 0);\n    IM_ASSERT(selected_indices.find(idx) == selected_indices.end());\n    selected_indices.push_back(idx);\n}\n\ntemplate<typename T>\nvoid ClearObjectSelection(\n    const ImObjectPool<T>& objects,\n    ImVector<int>&         selected_indices,\n    const int              id)\n{\n    const int idx = ObjectPoolFind(objects, id);\n    IM_ASSERT(idx >= 0);\n    IM_ASSERT(selected_indices.find(idx) != selected_indices.end());\n    selected_indices.find_erase_unsorted(idx);\n}\n\ntemplate<typename T>\nbool IsObjectSelected(const ImObjectPool<T>& objects, ImVector<int>& selected_indices, const int id)\n{\n    const int idx = ObjectPoolFind(objects, id);\n    return selected_indices.find(idx) != selected_indices.end();\n}\n\n} // namespace\n} // namespace IMNODES_NAMESPACE\n\n// [SECTION] API implementation\n\nImNodesIO::EmulateThreeButtonMouse::EmulateThreeButtonMouse() : Modifier(NULL) {}\n\nImNodesIO::LinkDetachWithModifierClick::LinkDetachWithModifierClick() : Modifier(NULL) {}\n\nImNodesIO::MultipleSelectModifier::MultipleSelectModifier() : Modifier(NULL) {}\n\nImNodesIO::ImNodesIO()\n    : EmulateThreeButtonMouse(), LinkDetachWithModifierClick(),\n      AltMouseButton(ImGuiMouseButton_Middle), AutoPanningSpeed(1000.0f)\n{\n}\n\nImNodesStyle::ImNodesStyle()\n    : GridSpacing(24.f), NodeCornerRounding(4.f), NodePadding(8.f, 8.f), NodeBorderThickness(1.f),\n      LinkThickness(3.f), LinkLineSegmentsPerLength(0.1f), LinkHoverDistance(10.f),\n      PinCircleRadius(4.f), PinQuadSideLength(7.f), PinTriangleSideLength(9.5),\n      PinLineThickness(1.f), PinHoverRadius(10.f), PinOffset(0.f), MiniMapPadding(8.0f, 8.0f),\n      MiniMapOffset(4.0f, 4.0f), Flags(ImNodesStyleFlags_NodeOutline | ImNodesStyleFlags_GridLines),\n      Colors()\n{\n}\n\nnamespace IMNODES_NAMESPACE\n{\nImNodesContext* CreateContext()\n{\n    ImNodesContext* ctx = IM_NEW(ImNodesContext)();\n    if (GImNodes == NULL)\n        SetCurrentContext(ctx);\n    Initialize(ctx);\n    return ctx;\n}\n\nvoid DestroyContext(ImNodesContext* ctx)\n{\n    if (ctx == NULL)\n        ctx = GImNodes;\n    Shutdown(ctx);\n    if (GImNodes == ctx)\n        SetCurrentContext(NULL);\n    IM_DELETE(ctx);\n}\n\nImNodesContext* GetCurrentContext() { return GImNodes; }\n\nvoid SetCurrentContext(ImNodesContext* ctx) { GImNodes = ctx; }\n\nImNodesEditorContext* EditorContextCreate()\n{\n    void* mem = ImGui::MemAlloc(sizeof(ImNodesEditorContext));\n    new (mem) ImNodesEditorContext();\n    return (ImNodesEditorContext*)mem;\n}\n\nvoid EditorContextFree(ImNodesEditorContext* ctx)\n{\n    ctx->~ImNodesEditorContext();\n    ImGui::MemFree(ctx);\n}\n\nvoid EditorContextSet(ImNodesEditorContext* ctx) { GImNodes->EditorCtx = ctx; }\n\nImVec2 EditorContextGetPanning()\n{\n    const ImNodesEditorContext& editor = EditorContextGet();\n    return editor.Panning;\n}\n\nvoid EditorContextResetPanning(const ImVec2& pos)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    editor.Panning = pos;\n}\n\nvoid EditorContextMoveToNode(const int node_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    ImNodeData&           node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id);\n\n    editor.Panning.x = -node.Origin.x;\n    editor.Panning.y = -node.Origin.y;\n}\n\nvoid SetImGuiContext(ImGuiContext* ctx) { ImGui::SetCurrentContext(ctx); }\n\nImNodesIO& GetIO() { return GImNodes->Io; }\n\nImNodesStyle& GetStyle() { return GImNodes->Style; }\n\nvoid StyleColorsDark(ImNodesStyle* dest)\n{\n    if (dest == nullptr)\n    {\n        dest = &GImNodes->Style;\n    }\n\n    dest->Colors[ImNodesCol_NodeBackground] = IM_COL32(50, 50, 50, 255);\n    dest->Colors[ImNodesCol_NodeBackgroundHovered] = IM_COL32(75, 75, 75, 255);\n    dest->Colors[ImNodesCol_NodeBackgroundSelected] = IM_COL32(75, 75, 75, 255);\n    dest->Colors[ImNodesCol_NodeOutline] = IM_COL32(100, 100, 100, 255);\n    // title bar colors match ImGui's titlebg colors\n    dest->Colors[ImNodesCol_TitleBar] = IM_COL32(41, 74, 122, 255);\n    dest->Colors[ImNodesCol_TitleBarHovered] = IM_COL32(66, 150, 250, 255);\n    dest->Colors[ImNodesCol_TitleBarSelected] = IM_COL32(66, 150, 250, 255);\n    // link colors match ImGui's slider grab colors\n    dest->Colors[ImNodesCol_Link] = IM_COL32(61, 133, 224, 200);\n    dest->Colors[ImNodesCol_LinkHovered] = IM_COL32(66, 150, 250, 255);\n    dest->Colors[ImNodesCol_LinkSelected] = IM_COL32(66, 150, 250, 255);\n    // pin colors match ImGui's button colors\n    dest->Colors[ImNodesCol_Pin] = IM_COL32(53, 150, 250, 180);\n    dest->Colors[ImNodesCol_PinHovered] = IM_COL32(53, 150, 250, 255);\n\n    dest->Colors[ImNodesCol_BoxSelector] = IM_COL32(61, 133, 224, 30);\n    dest->Colors[ImNodesCol_BoxSelectorOutline] = IM_COL32(61, 133, 224, 150);\n\n    dest->Colors[ImNodesCol_GridBackground] = IM_COL32(40, 40, 50, 200);\n    dest->Colors[ImNodesCol_GridLine] = IM_COL32(200, 200, 200, 40);\n    dest->Colors[ImNodesCol_GridLinePrimary] = IM_COL32(240, 240, 240, 60);\n\n    // minimap colors\n    dest->Colors[ImNodesCol_MiniMapBackground] = IM_COL32(25, 25, 25, 150);\n    dest->Colors[ImNodesCol_MiniMapBackgroundHovered] = IM_COL32(25, 25, 25, 200);\n    dest->Colors[ImNodesCol_MiniMapOutline] = IM_COL32(150, 150, 150, 100);\n    dest->Colors[ImNodesCol_MiniMapOutlineHovered] = IM_COL32(150, 150, 150, 200);\n    dest->Colors[ImNodesCol_MiniMapNodeBackground] = IM_COL32(200, 200, 200, 100);\n    dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered] = IM_COL32(200, 200, 200, 255);\n    dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] =\n        dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered];\n    dest->Colors[ImNodesCol_MiniMapNodeOutline] = IM_COL32(200, 200, 200, 100);\n    dest->Colors[ImNodesCol_MiniMapLink] = dest->Colors[ImNodesCol_Link];\n    dest->Colors[ImNodesCol_MiniMapLinkSelected] = dest->Colors[ImNodesCol_LinkSelected];\n    dest->Colors[ImNodesCol_MiniMapCanvas] = IM_COL32(200, 200, 200, 25);\n    dest->Colors[ImNodesCol_MiniMapCanvasOutline] = IM_COL32(200, 200, 200, 200);\n}\n\nvoid StyleColorsClassic(ImNodesStyle* dest)\n{\n    if (dest == nullptr)\n    {\n        dest = &GImNodes->Style;\n    }\n\n    dest->Colors[ImNodesCol_NodeBackground] = IM_COL32(50, 50, 50, 255);\n    dest->Colors[ImNodesCol_NodeBackgroundHovered] = IM_COL32(75, 75, 75, 255);\n    dest->Colors[ImNodesCol_NodeBackgroundSelected] = IM_COL32(75, 75, 75, 255);\n    dest->Colors[ImNodesCol_NodeOutline] = IM_COL32(100, 100, 100, 255);\n    dest->Colors[ImNodesCol_TitleBar] = IM_COL32(69, 69, 138, 255);\n    dest->Colors[ImNodesCol_TitleBarHovered] = IM_COL32(82, 82, 161, 255);\n    dest->Colors[ImNodesCol_TitleBarSelected] = IM_COL32(82, 82, 161, 255);\n    dest->Colors[ImNodesCol_Link] = IM_COL32(255, 255, 255, 100);\n    dest->Colors[ImNodesCol_LinkHovered] = IM_COL32(105, 99, 204, 153);\n    dest->Colors[ImNodesCol_LinkSelected] = IM_COL32(105, 99, 204, 153);\n    dest->Colors[ImNodesCol_Pin] = IM_COL32(89, 102, 156, 170);\n    dest->Colors[ImNodesCol_PinHovered] = IM_COL32(102, 122, 179, 200);\n    dest->Colors[ImNodesCol_BoxSelector] = IM_COL32(82, 82, 161, 100);\n    dest->Colors[ImNodesCol_BoxSelectorOutline] = IM_COL32(82, 82, 161, 255);\n    dest->Colors[ImNodesCol_GridBackground] = IM_COL32(40, 40, 50, 200);\n    dest->Colors[ImNodesCol_GridLine] = IM_COL32(200, 200, 200, 40);\n    dest->Colors[ImNodesCol_GridLinePrimary] = IM_COL32(240, 240, 240, 60);\n\n    // minimap colors\n    dest->Colors[ImNodesCol_MiniMapBackground] = IM_COL32(25, 25, 25, 100);\n    dest->Colors[ImNodesCol_MiniMapBackgroundHovered] = IM_COL32(25, 25, 25, 200);\n    dest->Colors[ImNodesCol_MiniMapOutline] = IM_COL32(150, 150, 150, 100);\n    dest->Colors[ImNodesCol_MiniMapOutlineHovered] = IM_COL32(150, 150, 150, 200);\n    dest->Colors[ImNodesCol_MiniMapNodeBackground] = IM_COL32(200, 200, 200, 100);\n    dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] =\n        dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered];\n    dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = IM_COL32(200, 200, 240, 255);\n    dest->Colors[ImNodesCol_MiniMapNodeOutline] = IM_COL32(200, 200, 200, 100);\n    dest->Colors[ImNodesCol_MiniMapLink] = dest->Colors[ImNodesCol_Link];\n    dest->Colors[ImNodesCol_MiniMapLinkSelected] = dest->Colors[ImNodesCol_LinkSelected];\n    dest->Colors[ImNodesCol_MiniMapCanvas] = IM_COL32(200, 200, 200, 25);\n    dest->Colors[ImNodesCol_MiniMapCanvasOutline] = IM_COL32(200, 200, 200, 200);\n}\n\nvoid StyleColorsLight(ImNodesStyle* dest)\n{\n    if (dest == nullptr)\n    {\n        dest = &GImNodes->Style;\n    }\n\n    dest->Colors[ImNodesCol_NodeBackground] = IM_COL32(240, 240, 240, 255);\n    dest->Colors[ImNodesCol_NodeBackgroundHovered] = IM_COL32(240, 240, 240, 255);\n    dest->Colors[ImNodesCol_NodeBackgroundSelected] = IM_COL32(240, 240, 240, 255);\n    dest->Colors[ImNodesCol_NodeOutline] = IM_COL32(100, 100, 100, 255);\n    dest->Colors[ImNodesCol_TitleBar] = IM_COL32(248, 248, 248, 255);\n    dest->Colors[ImNodesCol_TitleBarHovered] = IM_COL32(209, 209, 209, 255);\n    dest->Colors[ImNodesCol_TitleBarSelected] = IM_COL32(209, 209, 209, 255);\n    // original imgui values: 66, 150, 250\n    dest->Colors[ImNodesCol_Link] = IM_COL32(66, 150, 250, 100);\n    // original imgui values: 117, 138, 204\n    dest->Colors[ImNodesCol_LinkHovered] = IM_COL32(66, 150, 250, 242);\n    dest->Colors[ImNodesCol_LinkSelected] = IM_COL32(66, 150, 250, 242);\n    // original imgui values: 66, 150, 250\n    dest->Colors[ImNodesCol_Pin] = IM_COL32(66, 150, 250, 160);\n    dest->Colors[ImNodesCol_PinHovered] = IM_COL32(66, 150, 250, 255);\n    dest->Colors[ImNodesCol_BoxSelector] = IM_COL32(90, 170, 250, 30);\n    dest->Colors[ImNodesCol_BoxSelectorOutline] = IM_COL32(90, 170, 250, 150);\n    dest->Colors[ImNodesCol_GridBackground] = IM_COL32(225, 225, 225, 255);\n    dest->Colors[ImNodesCol_GridLine] = IM_COL32(180, 180, 180, 100);\n    dest->Colors[ImNodesCol_GridLinePrimary] = IM_COL32(120, 120, 120, 100);\n\n    // minimap colors\n    dest->Colors[ImNodesCol_MiniMapBackground] = IM_COL32(25, 25, 25, 100);\n    dest->Colors[ImNodesCol_MiniMapBackgroundHovered] = IM_COL32(25, 25, 25, 200);\n    dest->Colors[ImNodesCol_MiniMapOutline] = IM_COL32(150, 150, 150, 100);\n    dest->Colors[ImNodesCol_MiniMapOutlineHovered] = IM_COL32(150, 150, 150, 200);\n    dest->Colors[ImNodesCol_MiniMapNodeBackground] = IM_COL32(200, 200, 200, 100);\n    dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] =\n        dest->Colors[ImNodesCol_MiniMapNodeBackgroundHovered];\n    dest->Colors[ImNodesCol_MiniMapNodeBackgroundSelected] = IM_COL32(200, 200, 240, 255);\n    dest->Colors[ImNodesCol_MiniMapNodeOutline] = IM_COL32(200, 200, 200, 100);\n    dest->Colors[ImNodesCol_MiniMapLink] = dest->Colors[ImNodesCol_Link];\n    dest->Colors[ImNodesCol_MiniMapLinkSelected] = dest->Colors[ImNodesCol_LinkSelected];\n    dest->Colors[ImNodesCol_MiniMapCanvas] = IM_COL32(200, 200, 200, 25);\n    dest->Colors[ImNodesCol_MiniMapCanvasOutline] = IM_COL32(200, 200, 200, 200);\n}\n\nvoid BeginNodeEditor()\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);\n    GImNodes->CurrentScope = ImNodesScope_Editor;\n\n    // Reset state from previous pass\n\n    ImNodesEditorContext& editor = EditorContextGet();\n    editor.AutoPanningDelta = ImVec2(0, 0);\n    editor.GridContentBounds = ImRect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);\n    editor.MiniMapEnabled = false;\n    ObjectPoolReset(editor.Nodes);\n    ObjectPoolReset(editor.Pins);\n    ObjectPoolReset(editor.Links);\n\n    GImNodes->HoveredNodeIdx.Reset();\n    GImNodes->HoveredLinkIdx.Reset();\n    GImNodes->HoveredPinIdx.Reset();\n    GImNodes->DeletedLinkIdx.Reset();\n    GImNodes->SnapLinkIdx.Reset();\n\n    GImNodes->NodeIndicesOverlappingWithMouse.clear();\n\n    GImNodes->ImNodesUIState = ImNodesUIState_None;\n\n    GImNodes->MousePos = ImGui::GetIO().MousePos;\n    GImNodes->LeftMouseClicked = ImGui::IsMouseClicked(0);\n    GImNodes->LeftMouseReleased = ImGui::IsMouseReleased(0);\n    GImNodes->LeftMouseDragging = ImGui::IsMouseDragging(0, 0.0f);\n    GImNodes->AltMouseClicked =\n        (GImNodes->Io.EmulateThreeButtonMouse.Modifier != NULL &&\n         *GImNodes->Io.EmulateThreeButtonMouse.Modifier && GImNodes->LeftMouseClicked) ||\n        ImGui::IsMouseClicked(GImNodes->Io.AltMouseButton);\n    GImNodes->AltMouseDragging =\n        (GImNodes->Io.EmulateThreeButtonMouse.Modifier != NULL && GImNodes->LeftMouseDragging &&\n         (*GImNodes->Io.EmulateThreeButtonMouse.Modifier)) ||\n        ImGui::IsMouseDragging(GImNodes->Io.AltMouseButton, 0.0f);\n    GImNodes->AltMouseScrollDelta = ImGui::GetIO().MouseWheel;\n    GImNodes->MultipleSelectModifier =\n        (GImNodes->Io.MultipleSelectModifier.Modifier != NULL\n             ? *GImNodes->Io.MultipleSelectModifier.Modifier\n             : ImGui::GetIO().KeyCtrl);\n\n    GImNodes->ActiveAttribute = false;\n\n    ImGui::BeginGroup();\n    {\n        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1.f, 1.f));\n        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.f, 0.f));\n        ImGui::PushStyleColor(ImGuiCol_ChildBg, GImNodes->Style.Colors[ImNodesCol_GridBackground]);\n        ImGui::BeginChild(\n            \"scrolling_region\",\n            ImVec2(0.f, 0.f),\n            true,\n            ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove |\n                ImGuiWindowFlags_NoScrollWithMouse);\n        GImNodes->CanvasOriginScreenSpace = ImGui::GetCursorScreenPos();\n\n        // NOTE: we have to fetch the canvas draw list *after* we call\n        // BeginChild(), otherwise the ImGui UI elements are going to be\n        // rendered into the parent window draw list.\n        DrawListSet(ImGui::GetWindowDrawList());\n\n        {\n            const ImVec2 canvas_size = ImGui::GetWindowSize();\n            GImNodes->CanvasRectScreenSpace = ImRect(\n                EditorSpaceToScreenSpace(ImVec2(0.f, 0.f)), EditorSpaceToScreenSpace(canvas_size));\n\n            if (GImNodes->Style.Flags & ImNodesStyleFlags_GridLines)\n            {\n                DrawGrid(editor, canvas_size);\n            }\n        }\n    }\n}\n\nvoid EndNodeEditor()\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor);\n    GImNodes->CurrentScope = ImNodesScope_None;\n\n    ImNodesEditorContext& editor = EditorContextGet();\n\n    bool no_grid_content = editor.GridContentBounds.IsInverted();\n    if (no_grid_content)\n    {\n        editor.GridContentBounds = ScreenSpaceToGridSpace(editor, GImNodes->CanvasRectScreenSpace);\n    }\n\n    // Detect ImGui interaction first, because it blocks interaction with the rest of the UI\n\n    if (GImNodes->LeftMouseClicked && ImGui::IsAnyItemActive())\n    {\n        editor.ClickInteraction.Type = ImNodesClickInteractionType_ImGuiItem;\n    }\n\n    // Detect which UI element is being hovered over. Detection is done in a hierarchical fashion,\n    // because a UI element being hovered excludes any other as being hovered over.\n\n    // Don't do hovering detection for nodes/links/pins when interacting with the mini-map, since\n    // its an *overlay* with its own interaction behavior and must have precedence during mouse\n    // interaction.\n\n    if ((editor.ClickInteraction.Type == ImNodesClickInteractionType_None ||\n         editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation) &&\n        MouseInCanvas() && !IsMiniMapHovered())\n    {\n        // Pins needs some special care. We need to check the depth stack to see which pins are\n        // being occluded by other nodes.\n        ResolveOccludedPins(editor, GImNodes->OccludedPinIndices);\n\n        GImNodes->HoveredPinIdx = ResolveHoveredPin(editor.Pins, GImNodes->OccludedPinIndices);\n\n        if (!GImNodes->HoveredPinIdx.HasValue())\n        {\n            // Resolve which node is actually on top and being hovered using the depth stack.\n            GImNodes->HoveredNodeIdx = ResolveHoveredNode(editor.NodeDepthOrder);\n        }\n\n        // We don't check for hovered pins here, because if we want to detach a link by clicking and\n        // dragging, we need to have both a link and pin hovered.\n        if (!GImNodes->HoveredNodeIdx.HasValue())\n        {\n            GImNodes->HoveredLinkIdx = ResolveHoveredLink(editor.Links, editor.Pins);\n        }\n    }\n\n    for (int node_idx = 0; node_idx < editor.Nodes.Pool.size(); ++node_idx)\n    {\n        if (editor.Nodes.InUse[node_idx])\n        {\n            DrawListActivateNodeBackground(node_idx);\n            DrawNode(editor, node_idx);\n        }\n    }\n\n    // In order to render the links underneath the nodes, we want to first select the bottom draw\n    // channel.\n    GImNodes->CanvasDrawList->ChannelsSetCurrent(0);\n\n    for (int link_idx = 0; link_idx < editor.Links.Pool.size(); ++link_idx)\n    {\n        if (editor.Links.InUse[link_idx])\n        {\n            DrawLink(editor, link_idx);\n        }\n    }\n\n    // Render the click interaction UI elements (partial links, box selector) on top of everything\n    // else.\n\n    DrawListAppendClickInteractionChannel();\n    DrawListActivateClickInteractionChannel();\n\n    if (IsMiniMapActive())\n    {\n        CalcMiniMapLayout();\n        MiniMapUpdate();\n    }\n\n    // Handle node graph interaction\n\n    if (!IsMiniMapHovered())\n    {\n        if (GImNodes->LeftMouseClicked && GImNodes->HoveredLinkIdx.HasValue())\n        {\n            BeginLinkInteraction(editor, GImNodes->HoveredLinkIdx.Value(), GImNodes->HoveredPinIdx);\n        }\n\n        else if (GImNodes->LeftMouseClicked && GImNodes->HoveredPinIdx.HasValue())\n        {\n            BeginLinkCreation(editor, GImNodes->HoveredPinIdx.Value());\n        }\n\n        else if (GImNodes->LeftMouseClicked && GImNodes->HoveredNodeIdx.HasValue())\n        {\n            BeginNodeSelection(editor, GImNodes->HoveredNodeIdx.Value());\n        }\n\n        else if (\n            GImNodes->LeftMouseClicked || GImNodes->LeftMouseReleased ||\n            GImNodes->AltMouseClicked || GImNodes->AltMouseScrollDelta != 0.f)\n        {\n            BeginCanvasInteraction(editor);\n        }\n\n        bool should_auto_pan =\n            editor.ClickInteraction.Type == ImNodesClickInteractionType_BoxSelection ||\n            editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation ||\n            editor.ClickInteraction.Type == ImNodesClickInteractionType_Node;\n        if (should_auto_pan && !MouseInCanvas())\n        {\n            ImVec2 mouse = ImGui::GetMousePos();\n            ImVec2 center = GImNodes->CanvasRectScreenSpace.GetCenter();\n            ImVec2 direction = (center - mouse);\n            direction = direction * ImInvLength(direction, 0.0);\n\n            editor.AutoPanningDelta =\n                direction * ImGui::GetIO().DeltaTime * GImNodes->Io.AutoPanningSpeed;\n            editor.Panning += editor.AutoPanningDelta;\n        }\n    }\n    ClickInteractionUpdate(editor);\n\n    // At this point, draw commands have been issued for all nodes (and pins). Update the node pool\n    // to detect unused node slots and remove those indices from the depth stack before sorting the\n    // node draw commands by depth.\n    ObjectPoolUpdate(editor.Nodes);\n    ObjectPoolUpdate(editor.Pins);\n\n    DrawListSortChannelsByDepth(editor.NodeDepthOrder);\n\n    // After the links have been rendered, the link pool can be updated as well.\n    ObjectPoolUpdate(editor.Links);\n\n    // Finally, merge the draw channels\n    GImNodes->CanvasDrawList->ChannelsMerge();\n\n    // pop style\n    ImGui::EndChild();      // end scrolling region\n    ImGui::PopStyleColor(); // pop child window background color\n    ImGui::PopStyleVar();   // pop window padding\n    ImGui::PopStyleVar();   // pop frame padding\n    ImGui::EndGroup();\n}\n\nvoid MiniMap(\n    const float                                      minimap_size_fraction,\n    const ImNodesMiniMapLocation                     location,\n    const ImNodesMiniMapNodeHoveringCallback         node_hovering_callback,\n    const ImNodesMiniMapNodeHoveringCallbackUserData node_hovering_callback_data)\n{\n    // Check that editor size fraction is sane; must be in the range (0, 1]\n    IM_ASSERT(minimap_size_fraction > 0.f && minimap_size_fraction <= 1.f);\n\n    // Remember to call before EndNodeEditor\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor);\n\n    ImNodesEditorContext& editor = EditorContextGet();\n\n    editor.MiniMapEnabled = true;\n    editor.MiniMapSizeFraction = minimap_size_fraction;\n    editor.MiniMapLocation = location;\n\n    // Set node hovering callback information\n    editor.MiniMapNodeHoveringCallback = node_hovering_callback;\n    editor.MiniMapNodeHoveringCallbackUserData = node_hovering_callback_data;\n\n    // Actual drawing/updating of the MiniMap is done in EndNodeEditor so that\n    // mini map is draw over everything and all pin/link positions are updated\n    // correctly relative to their respective nodes. Hence, we must store some of\n    // of the state for the mini map in GImNodes for the actual drawing/updating\n}\n\nvoid BeginNode(const int node_id)\n{\n    // Remember to call BeginNodeEditor before calling BeginNode\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor);\n    GImNodes->CurrentScope = ImNodesScope_Node;\n\n    ImNodesEditorContext& editor = EditorContextGet();\n\n    const int node_idx = ObjectPoolFindOrCreateIndex(editor.Nodes, node_id);\n    GImNodes->CurrentNodeIdx = node_idx;\n\n    ImNodeData& node = editor.Nodes.Pool[node_idx];\n    node.ColorStyle.Background = GImNodes->Style.Colors[ImNodesCol_NodeBackground];\n    node.ColorStyle.BackgroundHovered = GImNodes->Style.Colors[ImNodesCol_NodeBackgroundHovered];\n    node.ColorStyle.BackgroundSelected = GImNodes->Style.Colors[ImNodesCol_NodeBackgroundSelected];\n    node.ColorStyle.Outline = GImNodes->Style.Colors[ImNodesCol_NodeOutline];\n    node.ColorStyle.Titlebar = GImNodes->Style.Colors[ImNodesCol_TitleBar];\n    node.ColorStyle.TitlebarHovered = GImNodes->Style.Colors[ImNodesCol_TitleBarHovered];\n    node.ColorStyle.TitlebarSelected = GImNodes->Style.Colors[ImNodesCol_TitleBarSelected];\n    node.LayoutStyle.CornerRounding = GImNodes->Style.NodeCornerRounding;\n    node.LayoutStyle.Padding = GImNodes->Style.NodePadding;\n    node.LayoutStyle.BorderThickness = GImNodes->Style.NodeBorderThickness;\n\n    // ImGui::SetCursorPos sets the cursor position, local to the current widget\n    // (in this case, the child object started in BeginNodeEditor). Use\n    // ImGui::SetCursorScreenPos to set the screen space coordinates directly.\n    ImGui::SetCursorPos(GridSpaceToEditorSpace(editor, GetNodeTitleBarOrigin(node)));\n\n    DrawListAddNode(node_idx);\n    DrawListActivateCurrentNodeForeground();\n\n    ImGui::PushID(node.Id);\n    ImGui::BeginGroup();\n}\n\nvoid EndNode()\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node);\n    GImNodes->CurrentScope = ImNodesScope_Editor;\n\n    ImNodesEditorContext& editor = EditorContextGet();\n\n    // The node's rectangle depends on the ImGui UI group size.\n    ImGui::EndGroup();\n    ImGui::PopID();\n\n    ImNodeData& node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx];\n    node.Rect = GetItemRect();\n    node.Rect.Expand(node.LayoutStyle.Padding);\n\n    editor.GridContentBounds.Add(node.Origin);\n    editor.GridContentBounds.Add(node.Origin + node.Rect.GetSize());\n\n    if (node.Rect.Contains(GImNodes->MousePos))\n    {\n        GImNodes->NodeIndicesOverlappingWithMouse.push_back(GImNodes->CurrentNodeIdx);\n    }\n}\n\nImVec2 GetNodeDimensions(int node_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    const int             node_idx = ObjectPoolFind(editor.Nodes, node_id);\n    IM_ASSERT(node_idx != -1); // invalid node_id\n    const ImNodeData& node = editor.Nodes.Pool[node_idx];\n    return node.Rect.GetSize();\n}\n\nvoid BeginNodeTitleBar()\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node);\n    ImGui::BeginGroup();\n}\n\nvoid EndNodeTitleBar()\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node);\n    ImGui::EndGroup();\n\n    ImNodesEditorContext& editor = EditorContextGet();\n    ImNodeData&           node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx];\n    node.TitleBarContentRect = GetItemRect();\n\n    ImGui::ItemAdd(GetNodeTitleRect(node), ImGui::GetID(\"title_bar\"));\n\n    ImGui::SetCursorPos(GridSpaceToEditorSpace(editor, GetNodeContentOrigin(node)));\n}\n\nvoid BeginInputAttribute(const int id, const ImNodesPinShape shape)\n{\n    BeginPinAttribute(id, ImNodesAttributeType_Input, shape, GImNodes->CurrentNodeIdx);\n}\n\nvoid EndInputAttribute() { EndPinAttribute(); }\n\nvoid BeginOutputAttribute(const int id, const ImNodesPinShape shape)\n{\n    BeginPinAttribute(id, ImNodesAttributeType_Output, shape, GImNodes->CurrentNodeIdx);\n}\n\nvoid EndOutputAttribute() { EndPinAttribute(); }\n\nvoid BeginStaticAttribute(const int id)\n{\n    // Make sure to call BeginNode() before calling BeginAttribute()\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Node);\n    GImNodes->CurrentScope = ImNodesScope_Attribute;\n\n    GImNodes->CurrentAttributeId = id;\n\n    ImGui::BeginGroup();\n    ImGui::PushID(id);\n}\n\nvoid EndStaticAttribute()\n{\n    // Make sure to call BeginNode() before calling BeginAttribute()\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Attribute);\n    GImNodes->CurrentScope = ImNodesScope_Node;\n\n    ImGui::PopID();\n    ImGui::EndGroup();\n\n    if (ImGui::IsItemActive())\n    {\n        GImNodes->ActiveAttribute = true;\n        GImNodes->ActiveAttributeId = GImNodes->CurrentAttributeId;\n    }\n}\n\nvoid PushAttributeFlag(const ImNodesAttributeFlags flag)\n{\n    GImNodes->CurrentAttributeFlags |= flag;\n    GImNodes->AttributeFlagStack.push_back(GImNodes->CurrentAttributeFlags);\n}\n\nvoid PopAttributeFlag()\n{\n    // PopAttributeFlag called without a matching PushAttributeFlag!\n    // The bottom value is always the default value, pushed in Initialize().\n    IM_ASSERT(GImNodes->AttributeFlagStack.size() > 1);\n\n    GImNodes->AttributeFlagStack.pop_back();\n    GImNodes->CurrentAttributeFlags = GImNodes->AttributeFlagStack.back();\n}\n\nvoid Link(const int id, const int start_attr_id, const int end_attr_id)\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_Editor);\n\n    ImNodesEditorContext& editor = EditorContextGet();\n    ImLinkData&           link = ObjectPoolFindOrCreateObject(editor.Links, id);\n    link.Id = id;\n    link.StartPinIdx = ObjectPoolFindOrCreateIndex(editor.Pins, start_attr_id);\n    link.EndPinIdx = ObjectPoolFindOrCreateIndex(editor.Pins, end_attr_id);\n    link.ColorStyle.Base = GImNodes->Style.Colors[ImNodesCol_Link];\n    link.ColorStyle.Hovered = GImNodes->Style.Colors[ImNodesCol_LinkHovered];\n    link.ColorStyle.Selected = GImNodes->Style.Colors[ImNodesCol_LinkSelected];\n\n    // Check if this link was created by the current link event\n    if ((editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation &&\n         editor.Pins.Pool[link.EndPinIdx].Flags & ImNodesAttributeFlags_EnableLinkCreationOnSnap &&\n         editor.ClickInteraction.LinkCreation.StartPinIdx == link.StartPinIdx &&\n         editor.ClickInteraction.LinkCreation.EndPinIdx == link.EndPinIdx) ||\n        (editor.ClickInteraction.LinkCreation.StartPinIdx == link.EndPinIdx &&\n         editor.ClickInteraction.LinkCreation.EndPinIdx == link.StartPinIdx))\n    {\n        GImNodes->SnapLinkIdx = ObjectPoolFindOrCreateIndex(editor.Links, id);\n    }\n}\n\nvoid PushColorStyle(const ImNodesCol item, unsigned int color)\n{\n    GImNodes->ColorModifierStack.push_back(ImNodesColElement(GImNodes->Style.Colors[item], item));\n    GImNodes->Style.Colors[item] = color;\n}\n\nvoid PopColorStyle()\n{\n    IM_ASSERT(GImNodes->ColorModifierStack.size() > 0);\n    const ImNodesColElement elem = GImNodes->ColorModifierStack.back();\n    GImNodes->Style.Colors[elem.Item] = elem.Color;\n    GImNodes->ColorModifierStack.pop_back();\n}\n\nstruct ImNodesStyleVarInfo\n{\n    ImGuiDataType Type;\n    ImU32         Count;\n    ImU32         Offset;\n    void* GetVarPtr(ImNodesStyle* style) const { return (void*)((unsigned char*)style + Offset); }\n};\n\nstatic const ImNodesStyleVarInfo GStyleVarInfo[] = {\n    // ImNodesStyleVar_GridSpacing\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, GridSpacing)},\n    // ImNodesStyleVar_NodeCornerRounding\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, NodeCornerRounding)},\n    // ImNodesStyleVar_NodePadding\n    {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImNodesStyle, NodePadding)},\n    // ImNodesStyleVar_NodeBorderThickness\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, NodeBorderThickness)},\n    // ImNodesStyleVar_LinkThickness\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, LinkThickness)},\n    // ImNodesStyleVar_LinkLineSegmentsPerLength\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, LinkLineSegmentsPerLength)},\n    // ImNodesStyleVar_LinkHoverDistance\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, LinkHoverDistance)},\n    // ImNodesStyleVar_PinCircleRadius\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinCircleRadius)},\n    // ImNodesStyleVar_PinQuadSideLength\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinQuadSideLength)},\n    // ImNodesStyleVar_PinTriangleSideLength\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinTriangleSideLength)},\n    // ImNodesStyleVar_PinLineThickness\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinLineThickness)},\n    // ImNodesStyleVar_PinHoverRadius\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinHoverRadius)},\n    // ImNodesStyleVar_PinOffset\n    {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImNodesStyle, PinOffset)},\n    // ImNodesStyleVar_MiniMapPadding\n    {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImNodesStyle, MiniMapPadding)},\n    // ImNodesStyleVar_MiniMapOffset\n    {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImNodesStyle, MiniMapOffset)},\n};\n\nstatic const ImNodesStyleVarInfo* GetStyleVarInfo(ImNodesStyleVar idx)\n{\n    IM_ASSERT(idx >= 0 && idx < ImNodesStyleVar_COUNT);\n    IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImNodesStyleVar_COUNT);\n    return &GStyleVarInfo[idx];\n}\n\nvoid PushStyleVar(const ImNodesStyleVar item, const float value)\n{\n    const ImNodesStyleVarInfo* var_info = GetStyleVarInfo(item);\n    if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)\n    {\n        float& style_var = *(float*)var_info->GetVarPtr(&GImNodes->Style);\n        GImNodes->StyleModifierStack.push_back(ImNodesStyleVarElement(item, style_var));\n        style_var = value;\n        return;\n    }\n    IM_ASSERT(0 && \"Called PushStyleVar() float variant but variable is not a float!\");\n}\n\nvoid PushStyleVar(const ImNodesStyleVar item, const ImVec2& value)\n{\n    const ImNodesStyleVarInfo* var_info = GetStyleVarInfo(item);\n    if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)\n    {\n        ImVec2& style_var = *(ImVec2*)var_info->GetVarPtr(&GImNodes->Style);\n        GImNodes->StyleModifierStack.push_back(ImNodesStyleVarElement(item, style_var));\n        style_var = value;\n        return;\n    }\n    IM_ASSERT(0 && \"Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!\");\n}\n\nvoid PopStyleVar(int count)\n{\n    while (count > 0)\n    {\n        IM_ASSERT(GImNodes->StyleModifierStack.size() > 0);\n        const ImNodesStyleVarElement style_backup = GImNodes->StyleModifierStack.back();\n        GImNodes->StyleModifierStack.pop_back();\n        const ImNodesStyleVarInfo* var_info = GetStyleVarInfo(style_backup.Item);\n        void*                      style_var = var_info->GetVarPtr(&GImNodes->Style);\n        if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)\n        {\n            ((float*)style_var)[0] = style_backup.FloatValue[0];\n        }\n        else if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)\n        {\n            ((float*)style_var)[0] = style_backup.FloatValue[0];\n            ((float*)style_var)[1] = style_backup.FloatValue[1];\n        }\n        count--;\n    }\n}\n\nvoid SetNodeScreenSpacePos(const int node_id, const ImVec2& screen_space_pos)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    ImNodeData&           node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id);\n    node.Origin = ScreenSpaceToGridSpace(editor, screen_space_pos);\n}\n\nvoid SetNodeEditorSpacePos(const int node_id, const ImVec2& editor_space_pos)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    ImNodeData&           node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id);\n    node.Origin = EditorSpaceToGridSpace(editor, editor_space_pos);\n}\n\nvoid SetNodeGridSpacePos(const int node_id, const ImVec2& grid_pos)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    ImNodeData&           node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id);\n    node.Origin = grid_pos;\n}\n\nvoid SetNodeDraggable(const int node_id, const bool draggable)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    ImNodeData&           node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id);\n    node.Draggable = draggable;\n}\n\nImVec2 GetNodeScreenSpacePos(const int node_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    const int             node_idx = ObjectPoolFind(editor.Nodes, node_id);\n    IM_ASSERT(node_idx != -1);\n    ImNodeData& node = editor.Nodes.Pool[node_idx];\n    return GridSpaceToScreenSpace(editor, node.Origin);\n}\n\nImVec2 GetNodeEditorSpacePos(const int node_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    const int             node_idx = ObjectPoolFind(editor.Nodes, node_id);\n    IM_ASSERT(node_idx != -1);\n    ImNodeData& node = editor.Nodes.Pool[node_idx];\n    return GridSpaceToEditorSpace(editor, node.Origin);\n}\n\nImVec2 GetNodeGridSpacePos(const int node_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    const int             node_idx = ObjectPoolFind(editor.Nodes, node_id);\n    IM_ASSERT(node_idx != -1);\n    ImNodeData& node = editor.Nodes.Pool[node_idx];\n    return node.Origin;\n}\n\nvoid SnapNodeToGrid(int node_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    ImNodeData&           node = ObjectPoolFindOrCreateObject(editor.Nodes, node_id);\n    node.Origin = SnapOriginToGrid(node.Origin);\n}\n\nbool IsEditorHovered() { return MouseInCanvas(); }\n\nbool IsNodeHovered(int* const node_id)\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);\n    IM_ASSERT(node_id != NULL);\n\n    const bool is_hovered = GImNodes->HoveredNodeIdx.HasValue();\n    if (is_hovered)\n    {\n        const ImNodesEditorContext& editor = EditorContextGet();\n        *node_id = editor.Nodes.Pool[GImNodes->HoveredNodeIdx.Value()].Id;\n    }\n    return is_hovered;\n}\n\nbool IsLinkHovered(int* const link_id)\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);\n    IM_ASSERT(link_id != NULL);\n\n    const bool is_hovered = GImNodes->HoveredLinkIdx.HasValue();\n    if (is_hovered)\n    {\n        const ImNodesEditorContext& editor = EditorContextGet();\n        *link_id = editor.Links.Pool[GImNodes->HoveredLinkIdx.Value()].Id;\n    }\n    return is_hovered;\n}\n\nbool IsPinHovered(int* const attr)\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);\n    IM_ASSERT(attr != NULL);\n\n    const bool is_hovered = GImNodes->HoveredPinIdx.HasValue();\n    if (is_hovered)\n    {\n        const ImNodesEditorContext& editor = EditorContextGet();\n        *attr = editor.Pins.Pool[GImNodes->HoveredPinIdx.Value()].Id;\n    }\n    return is_hovered;\n}\n\nint NumSelectedNodes()\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);\n    const ImNodesEditorContext& editor = EditorContextGet();\n    return editor.SelectedNodeIndices.size();\n}\n\nint NumSelectedLinks()\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);\n    const ImNodesEditorContext& editor = EditorContextGet();\n    return editor.SelectedLinkIndices.size();\n}\n\nvoid GetSelectedNodes(int* node_ids)\n{\n    IM_ASSERT(node_ids != NULL);\n\n    const ImNodesEditorContext& editor = EditorContextGet();\n    for (int i = 0; i < editor.SelectedNodeIndices.size(); ++i)\n    {\n        const int node_idx = editor.SelectedNodeIndices[i];\n        node_ids[i] = editor.Nodes.Pool[node_idx].Id;\n    }\n}\n\nvoid GetSelectedLinks(int* link_ids)\n{\n    IM_ASSERT(link_ids != NULL);\n\n    const ImNodesEditorContext& editor = EditorContextGet();\n    for (int i = 0; i < editor.SelectedLinkIndices.size(); ++i)\n    {\n        const int link_idx = editor.SelectedLinkIndices[i];\n        link_ids[i] = editor.Links.Pool[link_idx].Id;\n    }\n}\n\nvoid ClearNodeSelection()\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    editor.SelectedNodeIndices.clear();\n}\n\nvoid ClearNodeSelection(int node_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    ClearObjectSelection(editor.Nodes, editor.SelectedNodeIndices, node_id);\n}\n\nvoid ClearLinkSelection()\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    editor.SelectedLinkIndices.clear();\n}\n\nvoid ClearLinkSelection(int link_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    ClearObjectSelection(editor.Links, editor.SelectedLinkIndices, link_id);\n}\n\nvoid SelectNode(int node_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    SelectObject(editor.Nodes, editor.SelectedNodeIndices, node_id);\n}\n\nvoid SelectLink(int link_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    SelectObject(editor.Links, editor.SelectedLinkIndices, link_id);\n}\n\nbool IsNodeSelected(int node_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    return IsObjectSelected(editor.Nodes, editor.SelectedNodeIndices, node_id);\n}\n\nbool IsLinkSelected(int link_id)\n{\n    ImNodesEditorContext& editor = EditorContextGet();\n    return IsObjectSelected(editor.Links, editor.SelectedLinkIndices, link_id);\n}\n\nbool IsAttributeActive()\n{\n    IM_ASSERT((GImNodes->CurrentScope & ImNodesScope_Node) != 0);\n\n    if (!GImNodes->ActiveAttribute)\n    {\n        return false;\n    }\n\n    return GImNodes->ActiveAttributeId == GImNodes->CurrentAttributeId;\n}\n\nbool IsAnyAttributeActive(int* const attribute_id)\n{\n    IM_ASSERT((GImNodes->CurrentScope & (ImNodesScope_Node | ImNodesScope_Attribute)) == 0);\n\n    if (!GImNodes->ActiveAttribute)\n    {\n        return false;\n    }\n\n    if (attribute_id != NULL)\n    {\n        *attribute_id = GImNodes->ActiveAttributeId;\n    }\n\n    return true;\n}\n\nbool IsLinkStarted(int* const started_at_id)\n{\n    // Call this function after EndNodeEditor()!\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);\n    IM_ASSERT(started_at_id != NULL);\n\n    const bool is_started = (GImNodes->ImNodesUIState & ImNodesUIState_LinkStarted) != 0;\n    if (is_started)\n    {\n        const ImNodesEditorContext& editor = EditorContextGet();\n        const int                   pin_idx = editor.ClickInteraction.LinkCreation.StartPinIdx;\n        *started_at_id = editor.Pins.Pool[pin_idx].Id;\n    }\n\n    return is_started;\n}\n\nbool IsLinkDropped(int* const started_at_id, const bool including_detached_links)\n{\n    // Call this function after EndNodeEditor()!\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);\n\n    const ImNodesEditorContext& editor = EditorContextGet();\n\n    const bool link_dropped =\n        (GImNodes->ImNodesUIState & ImNodesUIState_LinkDropped) != 0 &&\n        (including_detached_links ||\n         editor.ClickInteraction.LinkCreation.Type != ImNodesLinkCreationType_FromDetach);\n\n    if (link_dropped && started_at_id)\n    {\n        const int pin_idx = editor.ClickInteraction.LinkCreation.StartPinIdx;\n        *started_at_id = editor.Pins.Pool[pin_idx].Id;\n    }\n\n    return link_dropped;\n}\n\nbool IsLinkCreated(\n    int* const  started_at_pin_id,\n    int* const  ended_at_pin_id,\n    bool* const created_from_snap)\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);\n    IM_ASSERT(started_at_pin_id != NULL);\n    IM_ASSERT(ended_at_pin_id != NULL);\n\n    const bool is_created = (GImNodes->ImNodesUIState & ImNodesUIState_LinkCreated) != 0;\n\n    if (is_created)\n    {\n        const ImNodesEditorContext& editor = EditorContextGet();\n        const int                   start_idx = editor.ClickInteraction.LinkCreation.StartPinIdx;\n        const int        end_idx = editor.ClickInteraction.LinkCreation.EndPinIdx.Value();\n        const ImPinData& start_pin = editor.Pins.Pool[start_idx];\n        const ImPinData& end_pin = editor.Pins.Pool[end_idx];\n\n        if (start_pin.Type == ImNodesAttributeType_Output)\n        {\n            *started_at_pin_id = start_pin.Id;\n            *ended_at_pin_id = end_pin.Id;\n        }\n        else\n        {\n            *started_at_pin_id = end_pin.Id;\n            *ended_at_pin_id = start_pin.Id;\n        }\n\n        if (created_from_snap)\n        {\n            *created_from_snap =\n                editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation;\n        }\n    }\n\n    return is_created;\n}\n\nbool IsLinkCreated(\n    int*  started_at_node_id,\n    int*  started_at_pin_id,\n    int*  ended_at_node_id,\n    int*  ended_at_pin_id,\n    bool* created_from_snap)\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);\n    IM_ASSERT(started_at_node_id != NULL);\n    IM_ASSERT(started_at_pin_id != NULL);\n    IM_ASSERT(ended_at_node_id != NULL);\n    IM_ASSERT(ended_at_pin_id != NULL);\n\n    const bool is_created = (GImNodes->ImNodesUIState & ImNodesUIState_LinkCreated) != 0;\n\n    if (is_created)\n    {\n        const ImNodesEditorContext& editor = EditorContextGet();\n        const int                   start_idx = editor.ClickInteraction.LinkCreation.StartPinIdx;\n        const int         end_idx = editor.ClickInteraction.LinkCreation.EndPinIdx.Value();\n        const ImPinData&  start_pin = editor.Pins.Pool[start_idx];\n        const ImNodeData& start_node = editor.Nodes.Pool[start_pin.ParentNodeIdx];\n        const ImPinData&  end_pin = editor.Pins.Pool[end_idx];\n        const ImNodeData& end_node = editor.Nodes.Pool[end_pin.ParentNodeIdx];\n\n        if (start_pin.Type == ImNodesAttributeType_Output)\n        {\n            *started_at_pin_id = start_pin.Id;\n            *started_at_node_id = start_node.Id;\n            *ended_at_pin_id = end_pin.Id;\n            *ended_at_node_id = end_node.Id;\n        }\n        else\n        {\n            *started_at_pin_id = end_pin.Id;\n            *started_at_node_id = end_node.Id;\n            *ended_at_pin_id = start_pin.Id;\n            *ended_at_node_id = start_node.Id;\n        }\n\n        if (created_from_snap)\n        {\n            *created_from_snap =\n                editor.ClickInteraction.Type == ImNodesClickInteractionType_LinkCreation;\n        }\n    }\n\n    return is_created;\n}\n\nbool IsLinkDestroyed(int* const link_id)\n{\n    IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);\n\n    const bool link_destroyed = GImNodes->DeletedLinkIdx.HasValue();\n    if (link_destroyed)\n    {\n        const ImNodesEditorContext& editor = EditorContextGet();\n        const int                   link_idx = GImNodes->DeletedLinkIdx.Value();\n        *link_id = editor.Links.Pool[link_idx].Id;\n    }\n\n    return link_destroyed;\n}\n\nnamespace\n{\nvoid NodeLineHandler(ImNodesEditorContext& editor, const char* const line)\n{\n    int id;\n    int x, y;\n    if (sscanf(line, \"[node.%i\", &id) == 1)\n    {\n        const int node_idx = ObjectPoolFindOrCreateIndex(editor.Nodes, id);\n        GImNodes->CurrentNodeIdx = node_idx;\n        ImNodeData& node = editor.Nodes.Pool[node_idx];\n        node.Id = id;\n    }\n    else if (sscanf(line, \"origin=%i,%i\", &x, &y) == 2)\n    {\n        ImNodeData& node = editor.Nodes.Pool[GImNodes->CurrentNodeIdx];\n        node.Origin = SnapOriginToGrid(ImVec2((float)x, (float)y));\n    }\n}\n\nvoid EditorLineHandler(ImNodesEditorContext& editor, const char* const line)\n{\n    (void)sscanf(line, \"panning=%f,%f\", &editor.Panning.x, &editor.Panning.y);\n}\n} // namespace\n\nconst char* SaveCurrentEditorStateToIniString(size_t* const data_size)\n{\n    return SaveEditorStateToIniString(&EditorContextGet(), data_size);\n}\n\nconst char* SaveEditorStateToIniString(\n    const ImNodesEditorContext* const editor_ptr,\n    size_t* const                     data_size)\n{\n    IM_ASSERT(editor_ptr != NULL);\n    const ImNodesEditorContext& editor = *editor_ptr;\n\n    GImNodes->TextBuffer.clear();\n    // TODO: check to make sure that the estimate is the upper bound of element\n    GImNodes->TextBuffer.reserve(64 * editor.Nodes.Pool.size());\n\n    GImNodes->TextBuffer.appendf(\n        \"[editor]\\npanning=%i,%i\\n\", (int)editor.Panning.x, (int)editor.Panning.y);\n\n    for (int i = 0; i < editor.Nodes.Pool.size(); i++)\n    {\n        if (editor.Nodes.InUse[i])\n        {\n            const ImNodeData& node = editor.Nodes.Pool[i];\n            GImNodes->TextBuffer.appendf(\"\\n[node.%d]\\n\", node.Id);\n            GImNodes->TextBuffer.appendf(\"origin=%i,%i\\n\", (int)node.Origin.x, (int)node.Origin.y);\n        }\n    }\n\n    if (data_size != NULL)\n    {\n        *data_size = GImNodes->TextBuffer.size();\n    }\n\n    return GImNodes->TextBuffer.c_str();\n}\n\nvoid LoadCurrentEditorStateFromIniString(const char* const data, const size_t data_size)\n{\n    LoadEditorStateFromIniString(&EditorContextGet(), data, data_size);\n}\n\nvoid LoadEditorStateFromIniString(\n    ImNodesEditorContext* const editor_ptr,\n    const char* const           data,\n    const size_t                data_size)\n{\n    if (data_size == 0u)\n    {\n        return;\n    }\n\n    ImNodesEditorContext& editor = editor_ptr == NULL ? EditorContextGet() : *editor_ptr;\n\n    char*       buf = (char*)ImGui::MemAlloc(data_size + 1);\n    const char* buf_end = buf + data_size;\n    memcpy(buf, data, data_size);\n    buf[data_size] = 0;\n\n    void (*line_handler)(ImNodesEditorContext&, const char*);\n    line_handler = NULL;\n    char* line_end = NULL;\n    for (char* line = buf; line < buf_end; line = line_end + 1)\n    {\n        while (*line == '\\n' || *line == '\\r')\n        {\n            line++;\n        }\n        line_end = line;\n        while (line_end < buf_end && *line_end != '\\n' && *line_end != '\\r')\n        {\n            line_end++;\n        }\n        line_end[0] = 0;\n\n        if (*line == ';' || *line == '\\0')\n        {\n            continue;\n        }\n\n        if (line[0] == '[' && line_end[-1] == ']')\n        {\n            line_end[-1] = 0;\n            if (strncmp(line + 1, \"node\", 4) == 0)\n            {\n                line_handler = NodeLineHandler;\n            }\n            else if (strcmp(line + 1, \"editor\") == 0)\n            {\n                line_handler = EditorLineHandler;\n            }\n        }\n\n        if (line_handler != NULL)\n        {\n            line_handler(editor, line);\n        }\n    }\n    ImGui::MemFree(buf);\n}\n\nvoid SaveCurrentEditorStateToIniFile(const char* const file_name)\n{\n    SaveEditorStateToIniFile(&EditorContextGet(), file_name);\n}\n\nvoid SaveEditorStateToIniFile(const ImNodesEditorContext* const editor, const char* const file_name)\n{\n    size_t      data_size = 0u;\n    const char* data = SaveEditorStateToIniString(editor, &data_size);\n    FILE*       file = ImFileOpen(file_name, \"wt\");\n    if (!file)\n    {\n        return;\n    }\n\n    fwrite(data, sizeof(char), data_size, file);\n    fclose(file);\n}\n\nvoid LoadCurrentEditorStateFromIniFile(const char* const file_name)\n{\n    LoadEditorStateFromIniFile(&EditorContextGet(), file_name);\n}\n\nvoid LoadEditorStateFromIniFile(ImNodesEditorContext* const editor, const char* const file_name)\n{\n    size_t data_size = 0u;\n    char*  file_data = (char*)ImFileLoadToMemory(file_name, \"rb\", &data_size);\n\n    if (!file_data)\n    {\n        return;\n    }\n\n    LoadEditorStateFromIniString(editor, file_data, data_size);\n    ImGui::MemFree(file_data);\n}\n} // namespace IMNODES_NAMESPACE\n"
  },
  {
    "path": "lib/third_party/imgui/implot/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\n# https://github.com/epezent/implot\nproject(imgui_implot)\n\nset(CMAKE_CXX_STANDARD 23)\n\nif (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)\n    add_library(imgui_implot OBJECT\n        source/implot.cpp\n        source/implot_items.cpp\n        source/implot_demo.cpp\n    )\n\n    target_include_directories(imgui_implot PUBLIC\n        include\n    )\n\n    target_link_libraries(imgui_implot PRIVATE imgui_includes)\nendif()\n\ntarget_include_directories(imgui_all_includes INTERFACE include)\n"
  },
  {
    "path": "lib/third_party/imgui/implot/LICENSE.txt",
    "content": "MIT License\n\nCopyright (c) 2020 Evan Pezent\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "lib/third_party/imgui/implot/README.md",
    "content": "# ImPlot\nImPlot is an immediate mode, GPU accelerated plotting library for [Dear ImGui](https://github.com/ocornut/imgui). It aims to provide a first-class API that ImGui fans will love. ImPlot is well suited for visualizing program data in real-time or creating interactive plots, and requires minimal code to integrate. Just like ImGui, it does not burden the end user with GUI state management, avoids STL containers and C++ headers, and has no external dependencies except for ImGui itself.\n\n<img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/controls.gif\" width=\"270\"> <img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/dnd.gif\" width=\"270\"> <img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/pie.gif\" width=\"270\">\n\n<img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/query.gif\" width=\"270\"> <img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/bars.gif\" width=\"270\">\n<img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/rt.gif\" width=\"270\">\n\n<img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/stem.gif\" width=\"270\"> <img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/markers.gif\" width=\"270\">\n<img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/shaded.gif\" width=\"270\">\n\n<img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/candle.gif\" width=\"270\"> <img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/heat.gif\" width=\"270\">\n<img src=\"https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/tables.gif\" width=\"270\">\n\n## Features\n\n- GPU accelerated rendering\n- multiple plot types:\n    - line plots\n    - shaded plots\n    - scatter plots\n    - vertical/horizontal/stacked bars graphs\n    - vertical/horizontal error bars\n    - stem plots\n    - stair plots\n    - pie charts\n    - heatmap charts\n    - 1D/2D histograms\n    - images\n    - and more likely to come\n- mix/match multiple plot items on a single plot\n- configurable axes ranges and scaling (linear/log)\n- subplots\n- time formatted x-axes (US formatted or ISO 8601)\n- reversible and lockable axes\n- multiple x-axes and y-axes\n- controls for zooming, panning, box selection, and auto-fitting data\n- controls for creating persistent query ranges (see demo)\n- several plot styling options: 10 marker types, adjustable marker sizes, line weights, outline colors, fill colors, etc.\n- 16 built-in colormaps and support for and user-added colormaps\n- optional plot titles, axis labels, and grid labels\n- optional and configurable legends with toggle buttons to quickly show/hide plot items\n- default styling based on current ImGui theme, or completely custom plot styles\n- customizable data getters and data striding (just like ImGui:PlotLine)\n- accepts data as float, double, and 8, 16, 32, and 64-bit signed/unsigned integral types\n- and more! (see Announcements [2022](https://github.com/epezent/implot/discussions/370)/[2021](https://github.com/epezent/implot/issues/168)/[2020](https://github.com/epezent/implot/issues/48))\n\n## Usage\n\nThe API is used just like any other ImGui `BeginX`/`EndX` pair. First, start a new plot with `ImPlot::BeginPlot()`. Next, plot as many items as you want with the provided `PlotX` functions (e.g. `PlotLine()`, `PlotBars()`, `PlotScatter()`, etc). Finally, wrap things up with a call to `ImPlot::EndPlot()`. That's it!\n\n```cpp\nint   bar_data[11] = ...;\nfloat x_data[1000] = ...;\nfloat y_data[1000] = ...;\n\nImGui::Begin(\"My Window\");\nif (ImPlot::BeginPlot(\"My Plot\")) {\n    ImPlot::PlotBars(\"My Bar Plot\", bar_data, 11);\n    ImPlot::PlotLine(\"My Line Plot\", x_data, y_data, 1000);\n    ...\n    ImPlot::EndPlot();\n}\nImGui::End();\n```\n\n![Usage](https://raw.githubusercontent.com/wiki/epezent/implot/screenshots3/example.PNG)\n\n\nOf course, there's much more you can do with ImPlot...\n\n## Demos\n\nA comprehensive example of ImPlot's features can be found in `implot_demo.cpp`. Add this file to your sources and call `ImPlot::ShowDemoWindow()` somewhere in your update loop. You are encouraged to use this file as a reference when needing to implement various plot types. The demo is always updated to show new plot types and features as they are added, so check back with each release!\n\nAn online version of the demo is hosted [here](https://traineq.org/implot_demo/src/implot_demo.html). You can view the plots and the source code that generated them. Note that this demo may not always be up to date and is not as performant as a desktop implementation, but it should give you a general taste of what's possible with ImPlot. Special thanks to [pthom](https://github.com/pthom) for creating and hosting this!\n\nMore sophisticated demos requiring lengthier code and/or third-party libraries can be found in a separate repository: [implot_demos](https://github.com/epezent/implot_demos). Here, you will find advanced signal processing and ImPlot usage in action. Please read the `Contributing` section of that repository if you have an idea for a new demo!\n\n## Integration\n\n0) Set up an [ImGui](https://github.com/ocornut/imgui) environment if you don't already have one.\n1) Add `implot.h`, `implot_internal.h`, `implot.cpp`, `implot_items.cpp` and optionally `implot_demo.cpp` to your sources. Alternatively, you can get ImPlot using [vcpkg](https://github.com/microsoft/vcpkg/tree/master/ports/implot).\n2) Create and destroy an `ImPlotContext` wherever you do so for your `ImGuiContext`:\n\n```cpp\nImGui::CreateContext();\nImPlot::CreateContext();\n...\nImPlot::DestroyContext();\nImGui::DestroyContext();\n```\n\nYou should be good to go!\n\n## Installing ImPlot using vcpkg\n\nYou can download and install ImPlot using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:\n\n```bash\ngit clone https://github.com/Microsoft/vcpkg.git\ncd vcpkg\n./bootstrap-vcpkg.sh\n./vcpkg integrate install\n./vcpkg install implot\n```\n\nThe ImPlot port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.\n\n## Extremely Important Note\n\nDear ImGui uses **16-bit indexing by default**, so high-density ImPlot widgets like `ImPlot::PlotHeatmap()` may produce too many vertices into `ImDrawList`, which causes an assertion failure and will result in data truncation and/or visual glitches. Therefore, it is **HIGHLY** recommended that you EITHER:\n\n- **Option 1:** Enable 32-bit indices by uncommenting `#define ImDrawIdx unsigned int` in your ImGui [`imconfig.h`](https://github.com/ocornut/imgui/blob/master/imconfig.h#L89) file.\n- **Option 2:** Handle the `ImGuiBackendFlags_RendererHasVtxOffset` flag in your renderer if you must use 16-bit indices. Many of the default ImGui rendering backends already support `ImGuiBackendFlags_RendererHasVtxOffset`. Refer to [this issue](https://github.com/ocornut/imgui/issues/2591) for more information.\n\n## FAQ\n\n**Q: Why?**\n\nA: ImGui is an incredibly powerful tool for rapid prototyping and development, but provides only limited mechanisms for data visualization. Two dimensional plots are ubiquitous and useful to almost any application. Being able to visualize your data in real-time will give you insight and better understanding of your application.\n\n**Q: Is ImPlot the right plotting library for me?**\n\nA: If you're looking to generate publication quality plots and/or export plots to a file, ImPlot is NOT the library for you! ImPlot is geared toward plotting application data at realtime speeds with high levels of interactivity. ImPlot does its best to create pretty plots (indeed, there are quite a few styling options available), but it will always favor function over form.\n\n**Q: Where is the documentation?**\n\nA: The API is thoroughly commented in `implot.h`, and the demo in `implot_demo.cpp` should be more than enough to get you started. Also take a look at the [implot_demos](https://github.com/epezent/implot_demos) repository.\n\n**Q: Is ImPlot suitable for plotting large datasets?**\n\nA: Yes, within reason. You can plot tens to hundreds of thousands of points without issue, but don't expect millions to be a buttery smooth experience. That said, you can always downsample extremely large datasets by telling ImPlot to stride your data at larger intervals if needed. Also try the experimental `backends` branch which aims to provide GPU acceleration support.\n\n**Q: What data types can I plot?**\n\nA: ImPlot plotting functions accept most scalar types:\n`float`, `double`, `int8`, `uint8`, `int16`, `uint16`, `int32`, `uint32`, `int64`, `uint64`. Arrays of custom structs or classes (e.g. `Vector2f` or similar) are easily passed to ImPlot functions using the built-in striding features (see `implot.h` for documentation), and many plotters provide a \"getter\" overload which accepts data generating callbacks. You can fully customize the list of accepted types by defining `IMPLOT_CUSTOM_NUMERIC_TYPES` at compile time: see doc in `implot_items.cpp`.\n\n**Q: Can plot styles be modified?**\n\nA: Yes. Data colormaps and various styling colors and variables can be pushed/popped or modified permanently on startup. Three default styles are available, as well as an automatic style that attempts to match you ImGui style.\n\n**Q: Does ImPlot support logarithmic scaling or time formatting?**\n\nA: Yep! Both logscale and timescale are supported.\n\n**Q: Does ImPlot support multiple y-axes? x-axes?**\n\nA: Yes. Up to three x-axes and three y-axes can be enabled.\n\n**Q: Does ImPlot support [insert plot type]?**\n\nA: Maybe. Check the demo, gallery, or Announcements ([2020](https://github.com/epezent/implot/issues/48)/[2021](https://github.com/epezent/implot/issues/168))to see if your desired plot type is shown. If not, consider submitting an issue or better yet, a PR!\n\n**Q: Does ImPlot support 3D plots?**\n\nA: No, and likely never will since ImGui only deals in 2D rendering.\n\n**Q: Does ImPlot provide analytic tools?**\n\nA: Not exactly, but it does give you the ability to query plot sub-ranges, with which you can process your data however you like.\n\n**Q: Can plots be exported/saved to image?**\n\nA: Not currently. Use your OS's screen capturing mechanisms if you need to capture a plot. ImPlot is not suitable for rendering publication quality plots; it is only intended to be used as a visualization tool. Post-process your data with MATLAB or matplotlib for these purposes.\n\n**Q: Why are my plot lines showing aliasing?**\n\nA: You probably need to enable `ImGuiStyle::AntiAliasedLinesUseTex` (or possibly `ImGuiStyle:AntiAliasedLines`). If those settings are already enabled, then you must ensure your backend supports texture based anti-aliasing (i.e. uses bilinear sampling). Most of the default ImGui backends support this feature out of the box. Learn more [here](https://github.com/ocornut/imgui/issues/3245). Alternatively, you can enable MSAA at the application level if your hardware supports it (4x should do).\n\n**Q: Can I compile ImPlot as a dynamic library?**\n\nA: Like ImGui, it is recommended that you compile and link ImPlot as a *static* library or directly as a part of your sources. However, if you must and are compiling ImPlot and ImGui as separate DLLs, make sure you set the current *ImGui* context with `ImPlot::SetImGuiContext(ImGuiContext* ctx)`. This ensures that global ImGui variables are correctly shared across the DLL boundary.\n\n**Q: Can ImPlot be used with other languages/bindings?**\n\nA: Yes, you can use the generated C binding, [cimplot](https://github.com/cimgui/cimplot) with most high level languages. [DearPyGui](https://github.com/hoffstadt/DearPyGui) provides a Python wrapper, among other things. [DearImGui/DearImPlot](https://github.com/aybe/DearImGui) provides bindings for .NET. [imgui-java](https://github.com/SpaiR/imgui-java) provides bindings for Java. [ImPlot.jl](https://github.com/wsphillips/ImPlot.jl) provides bindings for Julia. A Rust binding, [implot-rs](https://github.com/4bb4/implot-rs), is currently in the works. An example using Emscripten can be found [here](https://github.com/pthom/implot_demo).\n"
  },
  {
    "path": "lib/third_party/imgui/implot/include/implot.h",
    "content": "// MIT License\n\n// Copyright (c) 2023 Evan Pezent\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n// ImPlot v0.17\n\n// Table of Contents:\n//\n// [SECTION] Macros and Defines\n// [SECTION] Enums and Types\n// [SECTION] Callbacks\n// [SECTION] Contexts\n// [SECTION] Begin/End Plot\n// [SECTION] Begin/End Subplot\n// [SECTION] Setup\n// [SECTION] SetNext\n// [SECTION] Plot Items\n// [SECTION] Plot Tools\n// [SECTION] Plot Utils\n// [SECTION] Legend Utils\n// [SECTION] Drag and Drop\n// [SECTION] Styling\n// [SECTION] Colormaps\n// [SECTION] Input Mapping\n// [SECTION] Miscellaneous\n// [SECTION] Demo\n// [SECTION] Obsolete API\n\n#pragma once\n#include \"imgui.h\"\n\n//-----------------------------------------------------------------------------\n// [SECTION] Macros and Defines\n//-----------------------------------------------------------------------------\n\n// Define attributes of all API symbols declarations (e.g. for DLL under Windows)\n// Using ImPlot via a shared library is not recommended, because we don't guarantee\n// backward nor forward ABI compatibility and also function call overhead. If you\n// do use ImPlot as a DLL, be sure to call SetImGuiContext (see Miscellanous section).\n#ifndef IMPLOT_API\n#define IMPLOT_API\n#endif\n\n// ImPlot version string.\n#define IMPLOT_VERSION \"0.17\"\n// Indicates variable should deduced automatically.\n#define IMPLOT_AUTO -1\n// Special color used to indicate that a color should be deduced automatically.\n#define IMPLOT_AUTO_COL ImVec4(0,0,0,-1)\n// Macro for templated plotting functions; keeps header clean.\n#define IMPLOT_TMP template <typename T> IMPLOT_API\n\n//-----------------------------------------------------------------------------\n// [SECTION] Enums and Types\n//-----------------------------------------------------------------------------\n\n// Forward declarations\nstruct ImPlotContext;             // ImPlot context (opaque struct, see implot_internal.h)\n\n// Enums/Flags\ntypedef int ImAxis;                   // -> enum ImAxis_\ntypedef int ImPlotFlags;              // -> enum ImPlotFlags_\ntypedef int ImPlotAxisFlags;          // -> enum ImPlotAxisFlags_\ntypedef int ImPlotSubplotFlags;       // -> enum ImPlotSubplotFlags_\ntypedef int ImPlotLegendFlags;        // -> enum ImPlotLegendFlags_\ntypedef int ImPlotMouseTextFlags;     // -> enum ImPlotMouseTextFlags_\ntypedef int ImPlotDragToolFlags;      // -> ImPlotDragToolFlags_\ntypedef int ImPlotColormapScaleFlags; // -> ImPlotColormapScaleFlags_\n\ntypedef int ImPlotItemFlags;          // -> ImPlotItemFlags_\ntypedef int ImPlotLineFlags;          // -> ImPlotLineFlags_\ntypedef int ImPlotScatterFlags;       // -> ImPlotScatterFlags\ntypedef int ImPlotStairsFlags;        // -> ImPlotStairsFlags_\ntypedef int ImPlotShadedFlags;        // -> ImPlotShadedFlags_\ntypedef int ImPlotBarsFlags;          // -> ImPlotBarsFlags_\ntypedef int ImPlotBarGroupsFlags;     // -> ImPlotBarGroupsFlags_\ntypedef int ImPlotErrorBarsFlags;     // -> ImPlotErrorBarsFlags_\ntypedef int ImPlotStemsFlags;         // -> ImPlotStemsFlags_\ntypedef int ImPlotInfLinesFlags;      // -> ImPlotInfLinesFlags_\ntypedef int ImPlotPieChartFlags;      // -> ImPlotPieChartFlags_\ntypedef int ImPlotHeatmapFlags;       // -> ImPlotHeatmapFlags_\ntypedef int ImPlotHistogramFlags;     // -> ImPlotHistogramFlags_\ntypedef int ImPlotDigitalFlags;       // -> ImPlotDigitalFlags_\ntypedef int ImPlotImageFlags;         // -> ImPlotImageFlags_\ntypedef int ImPlotTextFlags;          // -> ImPlotTextFlags_\ntypedef int ImPlotDummyFlags;         // -> ImPlotDummyFlags_\n\ntypedef int ImPlotCond;               // -> enum ImPlotCond_\ntypedef int ImPlotCol;                // -> enum ImPlotCol_\ntypedef int ImPlotStyleVar;           // -> enum ImPlotStyleVar_\ntypedef int ImPlotScale;              // -> enum ImPlotScale_\ntypedef int ImPlotMarker;             // -> enum ImPlotMarker_\ntypedef int ImPlotColormap;           // -> enum ImPlotColormap_\ntypedef int ImPlotLocation;           // -> enum ImPlotLocation_\ntypedef int ImPlotBin;                // -> enum ImPlotBin_\n\n// Axis indices. The values assigned may change; NEVER hardcode these.\nenum ImAxis_ {\n    // horizontal axes\n    ImAxis_X1 = 0, // enabled by default\n    ImAxis_X2,     // disabled by default\n    ImAxis_X3,     // disabled by default\n    // vertical axes\n    ImAxis_Y1,     // enabled by default\n    ImAxis_Y2,     // disabled by default\n    ImAxis_Y3,     // disabled by default\n    // bookeeping\n    ImAxis_COUNT\n};\n\n// Options for plots (see BeginPlot).\nenum ImPlotFlags_ {\n    ImPlotFlags_None          = 0,       // default\n    ImPlotFlags_NoTitle       = 1 << 0,  // the plot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. \"##MyPlot\")\n    ImPlotFlags_NoLegend      = 1 << 1,  // the legend will not be displayed\n    ImPlotFlags_NoMouseText   = 1 << 2,  // the mouse position, in plot coordinates, will not be displayed inside of the plot\n    ImPlotFlags_NoInputs      = 1 << 3,  // the user will not be able to interact with the plot\n    ImPlotFlags_NoMenus       = 1 << 4,  // the user will not be able to open context menus\n    ImPlotFlags_NoBoxSelect   = 1 << 5,  // the user will not be able to box-select\n    ImPlotFlags_NoFrame       = 1 << 6,  // the ImGui frame will not be rendered\n    ImPlotFlags_Equal         = 1 << 7,  // x and y axes pairs will be constrained to have the same units/pixel\n    ImPlotFlags_Crosshairs    = 1 << 8,  // the default mouse cursor will be replaced with a crosshair when hovered\n    ImPlotFlags_CanvasOnly    = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText\n};\n\n// Options for plot axes (see SetupAxis).\nenum ImPlotAxisFlags_ {\n    ImPlotAxisFlags_None          = 0,       // default\n    ImPlotAxisFlags_NoLabel       = 1 << 0,  // the axis label will not be displayed (axis labels are also hidden if the supplied string name is nullptr)\n    ImPlotAxisFlags_NoGridLines   = 1 << 1,  // no grid lines will be displayed\n    ImPlotAxisFlags_NoTickMarks   = 1 << 2,  // no tick marks will be displayed\n    ImPlotAxisFlags_NoTickLabels  = 1 << 3,  // no text labels will be displayed\n    ImPlotAxisFlags_NoInitialFit  = 1 << 4,  // axis will not be initially fit to data extents on the first rendered frame\n    ImPlotAxisFlags_NoMenus       = 1 << 5,  // the user will not be able to open context menus with right-click\n    ImPlotAxisFlags_NoSideSwitch  = 1 << 6,  // the user will not be able to switch the axis side by dragging it\n    ImPlotAxisFlags_NoHighlight   = 1 << 7,  // the axis will not have its background highlighted when hovered or held\n    ImPlotAxisFlags_Opposite      = 1 << 8,  // axis ticks and labels will be rendered on the conventionally opposite side (i.e, right or top)\n    ImPlotAxisFlags_Foreground    = 1 << 9,  // grid lines will be displayed in the foreground (i.e. on top of data) instead of the background\n    ImPlotAxisFlags_Invert        = 1 << 10, // the axis will be inverted\n    ImPlotAxisFlags_AutoFit       = 1 << 11, // axis will be auto-fitting to data extents\n    ImPlotAxisFlags_RangeFit      = 1 << 12, // axis will only fit points if the point is in the visible range of the **orthogonal** axis\n    ImPlotAxisFlags_PanStretch    = 1 << 13, // panning in a locked or constrained state will cause the axis to stretch if possible\n    ImPlotAxisFlags_LockMin       = 1 << 14, // the axis minimum value will be locked when panning/zooming\n    ImPlotAxisFlags_LockMax       = 1 << 15, // the axis maximum value will be locked when panning/zooming\n    ImPlotAxisFlags_Lock          = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax,\n    ImPlotAxisFlags_NoDecorations = ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels,\n    ImPlotAxisFlags_AuxDefault    = ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_Opposite\n};\n\n// Options for subplots (see BeginSubplot)\nenum ImPlotSubplotFlags_ {\n    ImPlotSubplotFlags_None        = 0,       // default\n    ImPlotSubplotFlags_NoTitle     = 1 << 0,  // the subplot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. \"##MySubplot\")\n    ImPlotSubplotFlags_NoLegend    = 1 << 1,  // the legend will not be displayed (only applicable if ImPlotSubplotFlags_ShareItems is enabled)\n    ImPlotSubplotFlags_NoMenus     = 1 << 2,  // the user will not be able to open context menus with right-click\n    ImPlotSubplotFlags_NoResize    = 1 << 3,  // resize splitters between subplot cells will be not be provided\n    ImPlotSubplotFlags_NoAlign     = 1 << 4,  // subplot edges will not be aligned vertically or horizontally\n    ImPlotSubplotFlags_ShareItems  = 1 << 5,  // items across all subplots will be shared and rendered into a single legend entry\n    ImPlotSubplotFlags_LinkRows    = 1 << 6,  // link the y-axis limits of all plots in each row (does not apply to auxiliary axes)\n    ImPlotSubplotFlags_LinkCols    = 1 << 7,  // link the x-axis limits of all plots in each column (does not apply to auxiliary axes)\n    ImPlotSubplotFlags_LinkAllX    = 1 << 8,  // link the x-axis limits in every plot in the subplot (does not apply to auxiliary axes)\n    ImPlotSubplotFlags_LinkAllY    = 1 << 9,  // link the y-axis limits in every plot in the subplot (does not apply to auxiliary axes)\n    ImPlotSubplotFlags_ColMajor    = 1 << 10  // subplots are added in column major order instead of the default row major order\n};\n\n// Options for legends (see SetupLegend)\nenum ImPlotLegendFlags_ {\n    ImPlotLegendFlags_None            = 0,      // default\n    ImPlotLegendFlags_NoButtons       = 1 << 0, // legend icons will not function as hide/show buttons\n    ImPlotLegendFlags_NoHighlightItem = 1 << 1, // plot items will not be highlighted when their legend entry is hovered\n    ImPlotLegendFlags_NoHighlightAxis = 1 << 2, // axes will not be highlighted when legend entries are hovered (only relevant if x/y-axis count > 1)\n    ImPlotLegendFlags_NoMenus         = 1 << 3, // the user will not be able to open context menus with right-click\n    ImPlotLegendFlags_Outside         = 1 << 4, // legend will be rendered outside of the plot area\n    ImPlotLegendFlags_Horizontal      = 1 << 5, // legend entries will be displayed horizontally\n    ImPlotLegendFlags_Sort            = 1 << 6, // legend entries will be displayed in alphabetical order\n};\n\n// Options for mouse hover text (see SetupMouseText)\nenum ImPlotMouseTextFlags_ {\n    ImPlotMouseTextFlags_None        = 0,      // default\n    ImPlotMouseTextFlags_NoAuxAxes   = 1 << 0, // only show the mouse position for primary axes\n    ImPlotMouseTextFlags_NoFormat    = 1 << 1, // axes label formatters won't be used to render text\n    ImPlotMouseTextFlags_ShowAlways  = 1 << 2, // always display mouse position even if plot not hovered\n};\n\n// Options for DragPoint, DragLine, DragRect\nenum ImPlotDragToolFlags_ {\n    ImPlotDragToolFlags_None      = 0,      // default\n    ImPlotDragToolFlags_NoCursors = 1 << 0, // drag tools won't change cursor icons when hovered or held\n    ImPlotDragToolFlags_NoFit     = 1 << 1, // the drag tool won't be considered for plot fits\n    ImPlotDragToolFlags_NoInputs  = 1 << 2, // lock the tool from user inputs\n    ImPlotDragToolFlags_Delayed   = 1 << 3, // tool rendering will be delayed one frame; useful when applying position-constraints\n};\n\n// Flags for ColormapScale\nenum ImPlotColormapScaleFlags_ {\n    ImPlotColormapScaleFlags_None     = 0,      // default\n    ImPlotColormapScaleFlags_NoLabel  = 1 << 0, // the colormap axis label will not be displayed\n    ImPlotColormapScaleFlags_Opposite = 1 << 1, // render the colormap label and tick labels on the opposite side\n    ImPlotColormapScaleFlags_Invert   = 1 << 2, // invert the colormap bar and axis scale (this only affects rendering; if you only want to reverse the scale mapping, make scale_min > scale_max)\n};\n\n// Flags for ANY PlotX function\nenum ImPlotItemFlags_ {\n    ImPlotItemFlags_None     = 0,\n    ImPlotItemFlags_NoLegend = 1 << 0, // the item won't have a legend entry displayed\n    ImPlotItemFlags_NoFit    = 1 << 1, // the item won't be considered for plot fits\n};\n\n// Flags for PlotLine\nenum ImPlotLineFlags_ {\n    ImPlotLineFlags_None        = 0,       // default\n    ImPlotLineFlags_Segments    = 1 << 10, // a line segment will be rendered from every two consecutive points\n    ImPlotLineFlags_Loop        = 1 << 11, // the last and first point will be connected to form a closed loop\n    ImPlotLineFlags_SkipNaN     = 1 << 12, // NaNs values will be skipped instead of rendered as missing data\n    ImPlotLineFlags_NoClip      = 1 << 13, // markers (if displayed) on the edge of a plot will not be clipped\n    ImPlotLineFlags_Shaded      = 1 << 14, // a filled region between the line and horizontal origin will be rendered; use PlotShaded for more advanced cases\n};\n\n// Flags for PlotScatter\nenum ImPlotScatterFlags_ {\n    ImPlotScatterFlags_None   = 0,       // default\n    ImPlotScatterFlags_NoClip = 1 << 10, // markers on the edge of a plot will not be clipped\n};\n\n// Flags for PlotStairs\nenum ImPlotStairsFlags_ {\n    ImPlotStairsFlags_None     = 0,       // default\n    ImPlotStairsFlags_PreStep  = 1 << 10, // the y value is continued constantly to the left from every x position, i.e. the interval (x[i-1], x[i]] has the value y[i]\n    ImPlotStairsFlags_Shaded   = 1 << 11  // a filled region between the stairs and horizontal origin will be rendered; use PlotShaded for more advanced cases\n};\n\n// Flags for PlotShaded (placeholder)\nenum ImPlotShadedFlags_ {\n    ImPlotShadedFlags_None  = 0 // default\n};\n\n// Flags for PlotBars\nenum ImPlotBarsFlags_ {\n    ImPlotBarsFlags_None         = 0,       // default\n    ImPlotBarsFlags_Horizontal   = 1 << 10, // bars will be rendered horizontally on the current y-axis\n};\n\n// Flags for PlotBarGroups\nenum ImPlotBarGroupsFlags_ {\n    ImPlotBarGroupsFlags_None        = 0,       // default\n    ImPlotBarGroupsFlags_Horizontal  = 1 << 10, // bar groups will be rendered horizontally on the current y-axis\n    ImPlotBarGroupsFlags_Stacked     = 1 << 11, // items in a group will be stacked on top of each other\n};\n\n// Flags for PlotErrorBars\nenum ImPlotErrorBarsFlags_ {\n    ImPlotErrorBarsFlags_None       = 0,       // default\n    ImPlotErrorBarsFlags_Horizontal = 1 << 10, // error bars will be rendered horizontally on the current y-axis\n};\n\n// Flags for PlotStems\nenum ImPlotStemsFlags_ {\n    ImPlotStemsFlags_None       = 0,       // default\n    ImPlotStemsFlags_Horizontal = 1 << 10, // stems will be rendered horizontally on the current y-axis\n};\n\n// Flags for PlotInfLines\nenum ImPlotInfLinesFlags_ {\n    ImPlotInfLinesFlags_None       = 0,      // default\n    ImPlotInfLinesFlags_Horizontal = 1 << 10 // lines will be rendered horizontally on the current y-axis\n};\n\n// Flags for PlotPieChart\nenum ImPlotPieChartFlags_ {\n    ImPlotPieChartFlags_None         = 0,       // default\n    ImPlotPieChartFlags_Normalize    = 1 << 10, // force normalization of pie chart values (i.e. always make a full circle if sum < 0)\n    ImPlotPieChartFlags_IgnoreHidden = 1 << 11  // ignore hidden slices when drawing the pie chart (as if they were not there)\n};\n\n// Flags for PlotHeatmap\nenum ImPlotHeatmapFlags_ {\n    ImPlotHeatmapFlags_None     = 0,       // default\n    ImPlotHeatmapFlags_ColMajor = 1 << 10, // data will be read in column major order\n};\n\n// Flags for PlotHistogram and PlotHistogram2D\nenum ImPlotHistogramFlags_ {\n    ImPlotHistogramFlags_None       = 0,       // default\n    ImPlotHistogramFlags_Horizontal = 1 << 10, // histogram bars will be rendered horizontally (not supported by PlotHistogram2D)\n    ImPlotHistogramFlags_Cumulative = 1 << 11, // each bin will contain its count plus the counts of all previous bins (not supported by PlotHistogram2D)\n    ImPlotHistogramFlags_Density    = 1 << 12, // counts will be normalized, i.e. the PDF will be visualized, or the CDF will be visualized if Cumulative is also set\n    ImPlotHistogramFlags_NoOutliers = 1 << 13, // exclude values outside the specifed histogram range from the count toward normalizing and cumulative counts\n    ImPlotHistogramFlags_ColMajor   = 1 << 14  // data will be read in column major order (not supported by PlotHistogram)\n};\n\n// Flags for PlotDigital (placeholder)\nenum ImPlotDigitalFlags_ {\n    ImPlotDigitalFlags_None = 0 // default\n};\n\n// Flags for PlotImage (placeholder)\nenum ImPlotImageFlags_ {\n    ImPlotImageFlags_None = 0 // default\n};\n\n// Flags for PlotText\nenum ImPlotTextFlags_ {\n    ImPlotTextFlags_None     = 0,       // default\n    ImPlotTextFlags_Vertical = 1 << 10  // text will be rendered vertically\n};\n\n// Flags for PlotDummy (placeholder)\nenum ImPlotDummyFlags_ {\n    ImPlotDummyFlags_None = 0 // default\n};\n\n// Represents a condition for SetupAxisLimits etc. (same as ImGuiCond, but we only support a subset of those enums)\nenum ImPlotCond_\n{\n    ImPlotCond_None   = ImGuiCond_None,    // No condition (always set the variable), same as _Always\n    ImPlotCond_Always = ImGuiCond_Always,  // No condition (always set the variable)\n    ImPlotCond_Once   = ImGuiCond_Once,    // Set the variable once per runtime session (only the first call will succeed)\n};\n\n// Plot styling colors.\nenum ImPlotCol_ {\n    // item styling colors\n    ImPlotCol_Line,          // plot line/outline color (defaults to next unused color in current colormap)\n    ImPlotCol_Fill,          // plot fill color for bars (defaults to the current line color)\n    ImPlotCol_MarkerOutline, // marker outline color (defaults to the current line color)\n    ImPlotCol_MarkerFill,    // marker fill color (defaults to the current line color)\n    ImPlotCol_ErrorBar,      // error bar color (defaults to ImGuiCol_Text)\n    // plot styling colors\n    ImPlotCol_FrameBg,       // plot frame background color (defaults to ImGuiCol_FrameBg)\n    ImPlotCol_PlotBg,        // plot area background color (defaults to ImGuiCol_WindowBg)\n    ImPlotCol_PlotBorder,    // plot area border color (defaults to ImGuiCol_Border)\n    ImPlotCol_LegendBg,      // legend background color (defaults to ImGuiCol_PopupBg)\n    ImPlotCol_LegendBorder,  // legend border color (defaults to ImPlotCol_PlotBorder)\n    ImPlotCol_LegendText,    // legend text color (defaults to ImPlotCol_InlayText)\n    ImPlotCol_TitleText,     // plot title text color (defaults to ImGuiCol_Text)\n    ImPlotCol_InlayText,     // color of text appearing inside of plots (defaults to ImGuiCol_Text)\n    ImPlotCol_AxisText,      // axis label and tick lables color (defaults to ImGuiCol_Text)\n    ImPlotCol_AxisGrid,      // axis grid color (defaults to 25% ImPlotCol_AxisText)\n    ImPlotCol_AxisTick,      // axis tick color (defaults to AxisGrid)\n    ImPlotCol_AxisBg,        // background color of axis hover region (defaults to transparent)\n    ImPlotCol_AxisBgHovered, // axis hover color (defaults to ImGuiCol_ButtonHovered)\n    ImPlotCol_AxisBgActive,  // axis active color (defaults to ImGuiCol_ButtonActive)\n    ImPlotCol_Selection,     // box-selection color (defaults to yellow)\n    ImPlotCol_Crosshairs,    // crosshairs color (defaults to ImPlotCol_PlotBorder)\n    ImPlotCol_COUNT\n};\n\n// Plot styling variables.\nenum ImPlotStyleVar_ {\n    // item styling variables\n    ImPlotStyleVar_LineWeight,         // float,  plot item line weight in pixels\n    ImPlotStyleVar_Marker,             // int,    marker specification\n    ImPlotStyleVar_MarkerSize,         // float,  marker size in pixels (roughly the marker's \"radius\")\n    ImPlotStyleVar_MarkerWeight,       // float,  plot outline weight of markers in pixels\n    ImPlotStyleVar_FillAlpha,          // float,  alpha modifier applied to all plot item fills\n    ImPlotStyleVar_ErrorBarSize,       // float,  error bar whisker width in pixels\n    ImPlotStyleVar_ErrorBarWeight,     // float,  error bar whisker weight in pixels\n    ImPlotStyleVar_DigitalBitHeight,   // float,  digital channels bit height (at 1) in pixels\n    ImPlotStyleVar_DigitalBitGap,      // float,  digital channels bit padding gap in pixels\n    // plot styling variables\n    ImPlotStyleVar_PlotBorderSize,     // float,  thickness of border around plot area\n    ImPlotStyleVar_MinorAlpha,         // float,  alpha multiplier applied to minor axis grid lines\n    ImPlotStyleVar_MajorTickLen,       // ImVec2, major tick lengths for X and Y axes\n    ImPlotStyleVar_MinorTickLen,       // ImVec2, minor tick lengths for X and Y axes\n    ImPlotStyleVar_MajorTickSize,      // ImVec2, line thickness of major ticks\n    ImPlotStyleVar_MinorTickSize,      // ImVec2, line thickness of minor ticks\n    ImPlotStyleVar_MajorGridSize,      // ImVec2, line thickness of major grid lines\n    ImPlotStyleVar_MinorGridSize,      // ImVec2, line thickness of minor grid lines\n    ImPlotStyleVar_PlotPadding,        // ImVec2, padding between widget frame and plot area, labels, or outside legends (i.e. main padding)\n    ImPlotStyleVar_LabelPadding,       // ImVec2, padding between axes labels, tick labels, and plot edge\n    ImPlotStyleVar_LegendPadding,      // ImVec2, legend padding from plot edges\n    ImPlotStyleVar_LegendInnerPadding, // ImVec2, legend inner padding from legend edges\n    ImPlotStyleVar_LegendSpacing,      // ImVec2, spacing between legend entries\n    ImPlotStyleVar_MousePosPadding,    // ImVec2, padding between plot edge and interior info text\n    ImPlotStyleVar_AnnotationPadding,  // ImVec2, text padding around annotation labels\n    ImPlotStyleVar_FitPadding,         // ImVec2, additional fit padding as a percentage of the fit extents (e.g. ImVec2(0.1f,0.1f) adds 10% to the fit extents of X and Y)\n    ImPlotStyleVar_PlotDefaultSize,    // ImVec2, default size used when ImVec2(0,0) is passed to BeginPlot\n    ImPlotStyleVar_PlotMinSize,        // ImVec2, minimum size plot frame can be when shrunk\n    ImPlotStyleVar_COUNT\n};\n\n// Axis scale\nenum ImPlotScale_ {\n    ImPlotScale_Linear = 0, // default linear scale\n    ImPlotScale_Time,       // date/time scale\n    ImPlotScale_Log10,      // base 10 logartithmic scale\n    ImPlotScale_SymLog,     // symmetric log scale\n};\n\n// Marker specifications.\nenum ImPlotMarker_ {\n    ImPlotMarker_None = -1, // no marker\n    ImPlotMarker_Circle,    // a circle marker (default)\n    ImPlotMarker_Square,    // a square maker\n    ImPlotMarker_Diamond,   // a diamond marker\n    ImPlotMarker_Up,        // an upward-pointing triangle marker\n    ImPlotMarker_Down,      // an downward-pointing triangle marker\n    ImPlotMarker_Left,      // an leftward-pointing triangle marker\n    ImPlotMarker_Right,     // an rightward-pointing triangle marker\n    ImPlotMarker_Cross,     // a cross marker (not fillable)\n    ImPlotMarker_Plus,      // a plus marker (not fillable)\n    ImPlotMarker_Asterisk,  // a asterisk marker (not fillable)\n    ImPlotMarker_COUNT\n};\n\n// Built-in colormaps\nenum ImPlotColormap_ {\n    ImPlotColormap_Deep     = 0,   // a.k.a. seaborn deep             (qual=true,  n=10) (default)\n    ImPlotColormap_Dark     = 1,   // a.k.a. matplotlib \"Set1\"        (qual=true,  n=9 )\n    ImPlotColormap_Pastel   = 2,   // a.k.a. matplotlib \"Pastel1\"     (qual=true,  n=9 )\n    ImPlotColormap_Paired   = 3,   // a.k.a. matplotlib \"Paired\"      (qual=true,  n=12)\n    ImPlotColormap_Viridis  = 4,   // a.k.a. matplotlib \"viridis\"     (qual=false, n=11)\n    ImPlotColormap_Plasma   = 5,   // a.k.a. matplotlib \"plasma\"      (qual=false, n=11)\n    ImPlotColormap_Hot      = 6,   // a.k.a. matplotlib/MATLAB \"hot\"  (qual=false, n=11)\n    ImPlotColormap_Cool     = 7,   // a.k.a. matplotlib/MATLAB \"cool\" (qual=false, n=11)\n    ImPlotColormap_Pink     = 8,   // a.k.a. matplotlib/MATLAB \"pink\" (qual=false, n=11)\n    ImPlotColormap_Jet      = 9,   // a.k.a. MATLAB \"jet\"             (qual=false, n=11)\n    ImPlotColormap_Twilight = 10,  // a.k.a. matplotlib \"twilight\"    (qual=false, n=11)\n    ImPlotColormap_RdBu     = 11,  // red/blue, Color Brewer          (qual=false, n=11)\n    ImPlotColormap_BrBG     = 12,  // brown/blue-green, Color Brewer  (qual=false, n=11)\n    ImPlotColormap_PiYG     = 13,  // pink/yellow-green, Color Brewer (qual=false, n=11)\n    ImPlotColormap_Spectral = 14,  // color spectrum, Color Brewer    (qual=false, n=11)\n    ImPlotColormap_Greys    = 15,  // white/black                     (qual=false, n=2 )\n};\n\n// Used to position items on a plot (e.g. legends, labels, etc.)\nenum ImPlotLocation_ {\n    ImPlotLocation_Center    = 0,                                          // center-center\n    ImPlotLocation_North     = 1 << 0,                                     // top-center\n    ImPlotLocation_South     = 1 << 1,                                     // bottom-center\n    ImPlotLocation_West      = 1 << 2,                                     // center-left\n    ImPlotLocation_East      = 1 << 3,                                     // center-right\n    ImPlotLocation_NorthWest = ImPlotLocation_North | ImPlotLocation_West, // top-left\n    ImPlotLocation_NorthEast = ImPlotLocation_North | ImPlotLocation_East, // top-right\n    ImPlotLocation_SouthWest = ImPlotLocation_South | ImPlotLocation_West, // bottom-left\n    ImPlotLocation_SouthEast = ImPlotLocation_South | ImPlotLocation_East  // bottom-right\n};\n\n// Enums for different automatic histogram binning methods (k = bin count or w = bin width)\nenum ImPlotBin_ {\n    ImPlotBin_Sqrt    = -1, // k = sqrt(n)\n    ImPlotBin_Sturges = -2, // k = 1 + log2(n)\n    ImPlotBin_Rice    = -3, // k = 2 * cbrt(n)\n    ImPlotBin_Scott   = -4, // w = 3.49 * sigma / cbrt(n)\n};\n\n// Double precision version of ImVec2 used by ImPlot. Extensible by end users.\nIM_MSVC_RUNTIME_CHECKS_OFF\nstruct ImPlotPoint {\n    double x, y;\n    constexpr ImPlotPoint()                     : x(0.0), y(0.0) { }\n    constexpr ImPlotPoint(double _x, double _y) : x(_x), y(_y) { }\n    constexpr ImPlotPoint(const ImVec2& p)      : x((double)p.x), y((double)p.y) { }\n    double& operator[] (size_t idx)             { IM_ASSERT(idx == 0 || idx == 1); return ((double*)(void*)(char*)this)[idx]; }\n    double  operator[] (size_t idx) const       { IM_ASSERT(idx == 0 || idx == 1); return ((const double*)(const void*)(const char*)this)[idx]; }\n#ifdef IMPLOT_POINT_CLASS_EXTRA\n    IMPLOT_POINT_CLASS_EXTRA     // Define additional constructors and implicit cast operators in imconfig.h\n                                 // to convert back and forth between your math types and ImPlotPoint.\n#endif\n};\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n// Range defined by a min/max value.\nstruct ImPlotRange {\n    double Min, Max;\n    constexpr ImPlotRange()                         : Min(0.0), Max(0.0) { }\n    constexpr ImPlotRange(double _min, double _max) : Min(_min), Max(_max) { }\n    bool Contains(double value) const               { return value >= Min && value <= Max;                      }\n    double Size() const                             { return Max - Min;                                         }\n    double Clamp(double value) const                { return (value < Min) ? Min : (value > Max) ? Max : value; }\n};\n\n// Combination of two range limits for X and Y axes. Also an AABB defined by Min()/Max().\nstruct ImPlotRect {\n    ImPlotRange X, Y;\n    constexpr ImPlotRect()                                                       : X(0.0,0.0), Y(0.0,0.0) { }\n    constexpr ImPlotRect(double x_min, double x_max, double y_min, double y_max) : X(x_min, x_max), Y(y_min, y_max) { }\n    bool Contains(const ImPlotPoint& p) const                                    { return Contains(p.x, p.y);                 }\n    bool Contains(double x, double y) const                                      { return X.Contains(x) && Y.Contains(y);     }\n    ImPlotPoint Size() const                                                     { return ImPlotPoint(X.Size(), Y.Size());    }\n    ImPlotPoint Clamp(const ImPlotPoint& p)                                      { return Clamp(p.x, p.y);                    }\n    ImPlotPoint Clamp(double x, double y)                                        { return ImPlotPoint(X.Clamp(x),Y.Clamp(y)); }\n    ImPlotPoint Min() const                                                      { return ImPlotPoint(X.Min, Y.Min);          }\n    ImPlotPoint Max() const                                                      { return ImPlotPoint(X.Max, Y.Max);          }\n};\n\n// Plot style structure\nstruct ImPlotStyle {\n    // item styling variables\n    float   LineWeight;              // = 1,      item line weight in pixels\n    int     Marker;                  // = ImPlotMarker_None, marker specification\n    float   MarkerSize;              // = 4,      marker size in pixels (roughly the marker's \"radius\")\n    float   MarkerWeight;            // = 1,      outline weight of markers in pixels\n    float   FillAlpha;               // = 1,      alpha modifier applied to plot fills\n    float   ErrorBarSize;            // = 5,      error bar whisker width in pixels\n    float   ErrorBarWeight;          // = 1.5,    error bar whisker weight in pixels\n    float   DigitalBitHeight;        // = 8,      digital channels bit height (at y = 1.0f) in pixels\n    float   DigitalBitGap;           // = 4,      digital channels bit padding gap in pixels\n    // plot styling variables\n    float   PlotBorderSize;          // = 1,      line thickness of border around plot area\n    float   MinorAlpha;              // = 0.25    alpha multiplier applied to minor axis grid lines\n    ImVec2  MajorTickLen;            // = 10,10   major tick lengths for X and Y axes\n    ImVec2  MinorTickLen;            // = 5,5     minor tick lengths for X and Y axes\n    ImVec2  MajorTickSize;           // = 1,1     line thickness of major ticks\n    ImVec2  MinorTickSize;           // = 1,1     line thickness of minor ticks\n    ImVec2  MajorGridSize;           // = 1,1     line thickness of major grid lines\n    ImVec2  MinorGridSize;           // = 1,1     line thickness of minor grid lines\n    ImVec2  PlotPadding;             // = 10,10   padding between widget frame and plot area, labels, or outside legends (i.e. main padding)\n    ImVec2  LabelPadding;            // = 5,5     padding between axes labels, tick labels, and plot edge\n    ImVec2  LegendPadding;           // = 10,10   legend padding from plot edges\n    ImVec2  LegendInnerPadding;      // = 5,5     legend inner padding from legend edges\n    ImVec2  LegendSpacing;           // = 5,0     spacing between legend entries\n    ImVec2  MousePosPadding;         // = 10,10   padding between plot edge and interior mouse location text\n    ImVec2  AnnotationPadding;       // = 2,2     text padding around annotation labels\n    ImVec2  FitPadding;              // = 0,0     additional fit padding as a percentage of the fit extents (e.g. ImVec2(0.1f,0.1f) adds 10% to the fit extents of X and Y)\n    ImVec2  PlotDefaultSize;         // = 400,300 default size used when ImVec2(0,0) is passed to BeginPlot\n    ImVec2  PlotMinSize;             // = 200,150 minimum size plot frame can be when shrunk\n    // style colors\n    ImVec4  Colors[ImPlotCol_COUNT]; // Array of styling colors. Indexable with ImPlotCol_ enums.\n    // colormap\n    ImPlotColormap Colormap;         // The current colormap. Set this to either an ImPlotColormap_ enum or an index returned by AddColormap.\n    // settings/flags\n    bool    UseLocalTime;            // = false,  axis labels will be formatted for your timezone when ImPlotAxisFlag_Time is enabled\n    bool    UseISO8601;              // = false,  dates will be formatted according to ISO 8601 where applicable (e.g. YYYY-MM-DD, YYYY-MM, --MM-DD, etc.)\n    bool    Use24HourClock;          // = false,  times will be formatted using a 24 hour clock\n    IMPLOT_API ImPlotStyle();\n};\n\n// Support for legacy versions\n#if (IMGUI_VERSION_NUM < 18716) // Renamed in 1.88\n#define ImGuiMod_None       0\n#define ImGuiMod_Ctrl       ImGuiKeyModFlags_Ctrl\n#define ImGuiMod_Shift      ImGuiKeyModFlags_Shift\n#define ImGuiMod_Alt        ImGuiKeyModFlags_Alt\n#define ImGuiMod_Super      ImGuiKeyModFlags_Super\n#elif (IMGUI_VERSION_NUM < 18823) // Renamed in 1.89, sorry\n#define ImGuiMod_None       0\n#define ImGuiMod_Ctrl       ImGuiModFlags_Ctrl\n#define ImGuiMod_Shift      ImGuiModFlags_Shift\n#define ImGuiMod_Alt        ImGuiModFlags_Alt\n#define ImGuiMod_Super      ImGuiModFlags_Super\n#endif\n\n// Input mapping structure. Default values listed. See also MapInputDefault, MapInputReverse.\nstruct ImPlotInputMap {\n    ImGuiMouseButton Pan;           // LMB    enables panning when held,\n    int              PanMod;        // none   optional modifier that must be held for panning/fitting\n    ImGuiMouseButton Fit;           // LMB    initiates fit when double clicked\n    ImGuiMouseButton Select;        // RMB    begins box selection when pressed and confirms selection when released\n    ImGuiMouseButton SelectCancel;  // LMB    cancels active box selection when pressed; cannot be same as Select\n    int              SelectMod;     // none   optional modifier that must be held for box selection\n    int              SelectHorzMod; // Alt    expands active box selection horizontally to plot edge when held\n    int              SelectVertMod; // Shift  expands active box selection vertically to plot edge when held\n    ImGuiMouseButton Menu;          // RMB    opens context menus (if enabled) when clicked\n    int              OverrideMod;   // Ctrl   when held, all input is ignored; used to enable axis/plots as DND sources\n    int              ZoomMod;       // none   optional modifier that must be held for scroll wheel zooming\n    float            ZoomRate;      // 0.1f   zoom rate for scroll (e.g. 0.1f = 10% plot range every scroll click); make negative to invert\n    IMPLOT_API ImPlotInputMap();\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Callbacks\n//-----------------------------------------------------------------------------\n\n// Callback signature for axis tick label formatter.\ntypedef int (*ImPlotFormatter)(double value, char* buff, int size, void* user_data);\n\n// Callback signature for data getter.\ntypedef ImPlotPoint (*ImPlotGetter)(int idx, void* user_data);\n\n// Callback signature for axis transform.\ntypedef double (*ImPlotTransform)(double value, void* user_data);\n\nnamespace ImPlot {\n\n//-----------------------------------------------------------------------------\n// [SECTION] Contexts\n//-----------------------------------------------------------------------------\n\n// Creates a new ImPlot context. Call this after ImGui::CreateContext.\nIMPLOT_API ImPlotContext* CreateContext();\n// Destroys an ImPlot context. Call this before ImGui::DestroyContext. nullptr = destroy current context.\nIMPLOT_API void DestroyContext(ImPlotContext* ctx = nullptr);\n// Returns the current ImPlot context. nullptr if no context has ben set.\nIMPLOT_API ImPlotContext* GetCurrentContext();\n// Sets the current ImPlot context.\nIMPLOT_API void SetCurrentContext(ImPlotContext* ctx);\n\n// Sets the current **ImGui** context. This is ONLY necessary if you are compiling\n// ImPlot as a DLL (not recommended) separate from your ImGui compilation. It\n// sets the global variable GImGui, which is not shared across DLL boundaries.\n// See GImGui documentation in imgui.cpp for more details.\nIMPLOT_API void SetImGuiContext(ImGuiContext* ctx);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Begin/End Plot\n//-----------------------------------------------------------------------------\n\n// Starts a 2D plotting context. If this function returns true, EndPlot() MUST\n// be called! You are encouraged to use the following convention:\n//\n// if (BeginPlot(...)) {\n//     PlotLine(...);\n//     ...\n//     EndPlot();\n// }\n//\n// Important notes:\n//\n// - #title_id must be unique to the current ImGui ID scope. If you need to avoid ID\n//   collisions or don't want to display a title in the plot, use double hashes\n//   (e.g. \"MyPlot##HiddenIdText\" or \"##NoTitle\").\n// - #size is the **frame** size of the plot widget, not the plot area. The default\n//   size of plots (i.e. when ImVec2(0,0)) can be modified in your ImPlotStyle.\nIMPLOT_API bool BeginPlot(const char* title_id, const ImVec2& size=ImVec2(-1,0), ImPlotFlags flags=0);\n\n// Only call EndPlot() if BeginPlot() returns true! Typically called at the end\n// of an if statement conditioned on BeginPlot(). See example above.\nIMPLOT_API void EndPlot();\n\n//-----------------------------------------------------------------------------\n// [SECTION] Begin/End Subplots\n//-----------------------------------------------------------------------------\n\n// Starts a subdivided plotting context. If the function returns true,\n// EndSubplots() MUST be called! Call BeginPlot/EndPlot AT MOST [rows*cols]\n// times in  between the begining and end of the subplot context. Plots are\n// added in row major order.\n//\n// Example:\n//\n// if (BeginSubplots(\"My Subplot\",2,3,ImVec2(800,400)) {\n//     for (int i = 0; i < 6; ++i) {\n//         if (BeginPlot(...)) {\n//             ImPlot::PlotLine(...);\n//             ...\n//             EndPlot();\n//         }\n//     }\n//     EndSubplots();\n// }\n//\n// Produces:\n//\n// [0] | [1] | [2]\n// ----|-----|----\n// [3] | [4] | [5]\n//\n// Important notes:\n//\n// - #title_id must be unique to the current ImGui ID scope. If you need to avoid ID\n//   collisions or don't want to display a title in the plot, use double hashes\n//   (e.g. \"MySubplot##HiddenIdText\" or \"##NoTitle\").\n// - #rows and #cols must be greater than 0.\n// - #size is the size of the entire grid of subplots, not the individual plots\n// - #row_ratios and #col_ratios must have AT LEAST #rows and #cols elements,\n//   respectively. These are the sizes of the rows and columns expressed in ratios.\n//   If the user adjusts the dimensions, the arrays are updated with new ratios.\n//\n// Important notes regarding BeginPlot from inside of BeginSubplots:\n//\n// - The #title_id parameter of _BeginPlot_ (see above) does NOT have to be\n//   unique when called inside of a subplot context. Subplot IDs are hashed\n//   for your convenience so you don't have call PushID or generate unique title\n//   strings. Simply pass an empty string to BeginPlot unless you want to title\n//   each subplot.\n// - The #size parameter of _BeginPlot_ (see above) is ignored when inside of a\n//   subplot context. The actual size of the subplot will be based on the\n//   #size value you pass to _BeginSubplots_ and #row/#col_ratios if provided.\n\nIMPLOT_API bool BeginSubplots(const char* title_id,\n                             int rows,\n                             int cols,\n                             const ImVec2& size,\n                             ImPlotSubplotFlags flags = 0,\n                             float* row_ratios        = nullptr,\n                             float* col_ratios        = nullptr);\n\n// Only call EndSubplots() if BeginSubplots() returns true! Typically called at the end\n// of an if statement conditioned on BeginSublots(). See example above.\nIMPLOT_API void EndSubplots();\n\n//-----------------------------------------------------------------------------\n// [SECTION] Setup\n//-----------------------------------------------------------------------------\n\n// The following API allows you to setup and customize various aspects of the\n// current plot. The functions should be called immediately after BeginPlot\n// and before any other API calls. Typical usage is as follows:\n\n// if (BeginPlot(...)) {                     1) begin a new plot\n//     SetupAxis(ImAxis_X1, \"My X-Axis\");    2) make Setup calls\n//     SetupAxis(ImAxis_Y1, \"My Y-Axis\");\n//     SetupLegend(ImPlotLocation_North);\n//     ...\n//     SetupFinish();                        3) [optional] explicitly finish setup\n//     PlotLine(...);                        4) plot items\n//     ...\n//     EndPlot();                            5) end the plot\n// }\n//\n// Important notes:\n//\n// - Always call Setup code at the top of your BeginPlot conditional statement.\n// - Setup is locked once you start plotting or explicitly call SetupFinish.\n//   Do NOT call Setup code after you begin plotting or after you make\n//   any non-Setup API calls (e.g. utils like PlotToPixels also lock Setup)\n// - Calling SetupFinish is OPTIONAL, but probably good practice. If you do not\n//   call it yourself, then the first subsequent plotting or utility function will\n//   call it for you.\n\n// Enables an axis or sets the label and/or flags for an existing axis. Leave #label = nullptr for no label.\nIMPLOT_API void SetupAxis(ImAxis axis, const char* label=nullptr, ImPlotAxisFlags flags=0);\n// Sets an axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. Inversion with v_min > v_max is not supported; use SetupAxisLimits instead.\nIMPLOT_API void SetupAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once);\n// Links an axis range limits to external values. Set to nullptr for no linkage. The pointer data must remain valid until EndPlot.\nIMPLOT_API void SetupAxisLinks(ImAxis axis, double* link_min, double* link_max);\n// Sets the format of numeric axis labels via formater specifier (default=\"%g\"). Formated values will be double (i.e. use %f).\nIMPLOT_API void SetupAxisFormat(ImAxis axis, const char* fmt);\n// Sets the format of numeric axis labels via formatter callback. Given #value, write a label into #buff. Optionally pass user data.\nIMPLOT_API void SetupAxisFormat(ImAxis axis, ImPlotFormatter formatter, void* data=nullptr);\n// Sets an axis' ticks and optionally the labels. To keep the default ticks, set #keep_default=true.\nIMPLOT_API void SetupAxisTicks(ImAxis axis, const double* values, int n_ticks, const char* const labels[]=nullptr, bool keep_default=false);\n// Sets an axis' ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true.\nIMPLOT_API void SetupAxisTicks(ImAxis axis, double v_min, double v_max, int n_ticks, const char* const labels[]=nullptr, bool keep_default=false);\n// Sets an axis' scale using built-in options.\nIMPLOT_API void SetupAxisScale(ImAxis axis, ImPlotScale scale);\n// Sets an axis' scale using user supplied forward and inverse transfroms.\nIMPLOT_API void SetupAxisScale(ImAxis axis, ImPlotTransform forward, ImPlotTransform inverse, void* data=nullptr);\n// Sets an axis' limits constraints.\nIMPLOT_API void SetupAxisLimitsConstraints(ImAxis axis, double v_min, double v_max);\n// Sets an axis' zoom constraints.\nIMPLOT_API void SetupAxisZoomConstraints(ImAxis axis, double z_min, double z_max);\n\n// Sets the label and/or flags for primary X and Y axes (shorthand for two calls to SetupAxis).\nIMPLOT_API void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags=0, ImPlotAxisFlags y_flags=0);\n// Sets the primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits).\nIMPLOT_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once);\n\n// Sets up the plot legend. This can also be called immediately after BeginSubplots when using ImPlotSubplotFlags_ShareItems.\nIMPLOT_API void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags=0);\n// Set the location of the current plot's mouse position text (default = South|East).\nIMPLOT_API void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags=0);\n\n// Explicitly finalize plot setup. Once you call this, you cannot make anymore Setup calls for the current plot!\n// Note that calling this function is OPTIONAL; it will be called by the first subsequent setup-locking API call.\nIMPLOT_API void SetupFinish();\n\n//-----------------------------------------------------------------------------\n// [SECTION] SetNext\n//-----------------------------------------------------------------------------\n\n// Though you should default to the `Setup` API above, there are some scenarios\n// where (re)configuring a plot or axis before `BeginPlot` is needed (e.g. if\n// using a preceding button or slider widget to change the plot limits). In\n// this case, you can use the `SetNext` API below. While this is not as feature\n// rich as the Setup API, most common needs are provided. These functions can be\n// called anwhere except for inside of `Begin/EndPlot`. For example:\n\n// if (ImGui::Button(\"Center Plot\"))\n//     ImPlot::SetNextPlotLimits(-1,1,-1,1);\n// if (ImPlot::BeginPlot(...)) {\n//     ...\n//     ImPlot::EndPlot();\n// }\n//\n// Important notes:\n//\n// - You must still enable non-default axes with SetupAxis for these functions\n//   to work properly.\n\n// Sets an upcoming axis range limits. If ImPlotCond_Always is used, the axes limits will be locked.\nIMPLOT_API void SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once);\n// Links an upcoming axis range limits to external values. Set to nullptr for no linkage. The pointer data must remain valid until EndPlot!\nIMPLOT_API void SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max);\n// Set an upcoming axis to auto fit to its data.\nIMPLOT_API void SetNextAxisToFit(ImAxis axis);\n\n// Sets the upcoming primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits).\nIMPLOT_API void SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once);\n// Sets all upcoming axes to auto fit to their data.\nIMPLOT_API void SetNextAxesToFit();\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot Items\n//-----------------------------------------------------------------------------\n\n// The main plotting API is provied below. Call these functions between\n// Begin/EndPlot and after any Setup API calls. Each plots data on the current\n// x and y axes, which can be changed with `SetAxis/Axes`.\n//\n// The templated functions are explicitly instantiated in implot_items.cpp.\n// They are not intended to be used generically with custom types. You will get\n// a linker error if you try! All functions support the following scalar types:\n//\n// float, double, ImS8, ImU8, ImS16, ImU16, ImS32, ImU32, ImS64, ImU64\n//\n//\n// If you need to plot custom or non-homogenous data you have a few options:\n//\n// 1. If your data is a simple struct/class (e.g. Vector2f), you can use striding.\n//    This is the most performant option if applicable.\n//\n//    struct Vector2f { float X, Y; };\n//    ...\n//    Vector2f data[42];\n//    ImPlot::PlotLine(\"line\", &data[0].x, &data[0].y, 42, 0, 0, sizeof(Vector2f));\n//\n// 2. Write a custom getter C function or C++ lambda and pass it and optionally your data to\n//    an ImPlot function post-fixed with a G (e.g. PlotScatterG). This has a slight performance\n//    cost, but probably not enough to worry about unless your data is very large. Examples:\n//\n//    ImPlotPoint MyDataGetter(void* data, int idx) {\n//        MyData* my_data = (MyData*)data;\n//        ImPlotPoint p;\n//        p.x = my_data->GetTime(idx);\n//        p.y = my_data->GetValue(idx);\n//        return p\n//    }\n//    ...\n//    auto my_lambda = [](int idx, void*) {\n//        double t = idx / 999.0;\n//        return ImPlotPoint(t, 0.5+0.5*std::sin(2*PI*10*t));\n//    };\n//    ...\n//    if (ImPlot::BeginPlot(\"MyPlot\")) {\n//        MyData my_data;\n//        ImPlot::PlotScatterG(\"scatter\", MyDataGetter, &my_data, my_data.Size());\n//        ImPlot::PlotLineG(\"line\", my_lambda, nullptr, 1000);\n//        ImPlot::EndPlot();\n//    }\n//\n// NB: All types are converted to double before plotting. You may lose information\n// if you try plotting extremely large 64-bit integral types. Proceed with caution!\n\n// Plots a standard 2D line plot.\nIMPLOT_TMP void PlotLine(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, int count, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_API void PlotLineG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotLineFlags flags=0);\n\n// Plots a standard 2D scatter plot. Default marker is ImPlotMarker_Circle.\nIMPLOT_TMP void PlotScatter(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_API void PlotScatterG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotScatterFlags flags=0);\n\n// Plots a a stairstep graph. The y value is continued constantly to the right from every x position, i.e. the interval [x[i], x[i+1]) has the value y[i]\nIMPLOT_TMP void PlotStairs(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_TMP void PlotStairs(const char* label_id, const T* xs, const T* ys, int count, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_API void PlotStairsG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotStairsFlags flags=0);\n\n// Plots a shaded (filled) region between two lines, or a line and a horizontal reference. Set yref to +/-INFINITY for infinite fill extents.\nIMPLOT_TMP void PlotShaded(const char* label_id, const T* values, int count, double yref=0, double xscale=1, double xstart=0, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys, int count, double yref=0, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys1, const T* ys2, int count, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_API void PlotShadedG(const char* label_id, ImPlotGetter getter1, void* data1, ImPlotGetter getter2, void* data2, int count, ImPlotShadedFlags flags=0);\n\n// Plots a bar graph. Vertical by default. #bar_size and #shift are in plot units.\nIMPLOT_TMP void PlotBars(const char* label_id, const T* values, int count, double bar_size=0.67, double shift=0, ImPlotBarsFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_TMP void PlotBars(const char* label_id, const T* xs, const T* ys, int count, double bar_size, ImPlotBarsFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_API void PlotBarsG(const char* label_id, ImPlotGetter getter, void* data, int count, double bar_size, ImPlotBarsFlags flags=0);\n\n// Plots a group of bars. #values is a row-major matrix with #item_count rows and #group_count cols. #label_ids should have #item_count elements.\nIMPLOT_TMP void PlotBarGroups(const char* const label_ids[], const T* values, int item_count, int group_count, double group_size=0.67, double shift=0, ImPlotBarGroupsFlags flags=0);\n\n// Plots vertical error bar. The label_id should be the same as the label_id of the associated line or bar plot.\nIMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, ImPlotErrorBarsFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, ImPlotErrorBarsFlags flags=0, int offset=0, int stride=sizeof(T));\n\n// Plots stems. Vertical by default.\nIMPLOT_TMP void PlotStems(const char* label_id, const T* values, int count, double ref=0, double scale=1, double start=0, ImPlotStemsFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_TMP void PlotStems(const char* label_id, const T* xs, const T* ys, int count, double ref=0, ImPlotStemsFlags flags=0, int offset=0, int stride=sizeof(T));\n\n// Plots infinite vertical or horizontal lines (e.g. for references or asymptotes).\nIMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags=0, int offset=0, int stride=sizeof(T));\n\n// Plots a pie chart. Center and radius are in plot units. #label_fmt can be set to nullptr for no labels.\nIMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data=nullptr, double angle0=90, ImPlotPieChartFlags flags=0);\nIMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt=\"%.1f\", double angle0=90, ImPlotPieChartFlags flags=0);\n\n// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels.\nIMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt=\"%.1f\", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0);\n\n// Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range.\n// Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned.\nIMPLOT_TMP double PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBin_Sturges, double bar_scale=1.0, ImPlotRange range=ImPlotRange(), ImPlotHistogramFlags flags=0);\n\n// Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBin. If #range is left unspecified, the min/max of\n// #xs an #ys will be used as the ranges. Otherwise, outlier values outside of range are not binned. The largest bin count or density is returned.\nIMPLOT_TMP double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBin_Sturges, int y_bins=ImPlotBin_Sturges, ImPlotRect range=ImPlotRect(), ImPlotHistogramFlags flags=0);\n\n// Plots digital data. Digital plots do not respond to y drag or zoom, and are always referenced to the bottom of the plot.\nIMPLOT_TMP void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, ImPlotDigitalFlags flags=0, int offset=0, int stride=sizeof(T));\nIMPLOT_API void PlotDigitalG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotDigitalFlags flags=0);\n\n// Plots an axis-aligned image. #bounds_min/bounds_max are in plot coordinates (y-up) and #uv0/uv1 are in texture coordinates (y-down).\n#ifdef IMGUI_HAS_TEXTURES\n    IMPLOT_API void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), ImPlotImageFlags flags = 0);\n#else\n    IMPLOT_API void PlotImage(const char* label_id, ImTextureID tex_ref, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0=ImVec2(0,0), const ImVec2& uv1=ImVec2(1,1), const ImVec4& tint_col=ImVec4(1,1,1,1), ImPlotImageFlags flags=0);\n#endif\n\n// Plots a centered text label at point x,y with an optional pixel offset. Text color can be changed with ImPlot::PushStyleColor(ImPlotCol_InlayText, ...).\nIMPLOT_API void PlotText(const char* text, double x, double y, const ImVec2& pix_offset=ImVec2(0,0), ImPlotTextFlags flags=0);\n\n// Plots a dummy item (i.e. adds a legend entry colored by ImPlotCol_Line)\nIMPLOT_API void PlotDummy(const char* label_id, ImPlotDummyFlags flags=0);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot Tools\n//-----------------------------------------------------------------------------\n\n// The following can be used to render interactive elements and/or annotations.\n// Like the item plotting functions above, they apply to the current x and y\n// axes, which can be changed with `SetAxis/SetAxes`. These functions return true\n// when user interaction causes the provided coordinates to change. Additional\n// user interactions can be retrieved through the optional output parameters.\n\n// Shows a draggable point at x,y. #col defaults to ImGuiCol_Text.\nIMPLOT_API bool DragPoint(int id, double* x, double* y, const ImVec4& col, float size = 4, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);\n// Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text.\nIMPLOT_API bool DragLineX(int id, double* x, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);\n// Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text.\nIMPLOT_API bool DragLineY(int id, double* y, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);\n// Shows a draggable and resizeable rectangle.\nIMPLOT_API bool DragRect(int id, double* x1, double* y1, double* x2, double* y2, const ImVec4& col, ImPlotDragToolFlags flags = 0, bool* out_clicked = nullptr, bool* out_hovered = nullptr, bool* held = nullptr);\n\n// Shows an annotation callout at a chosen point. Clamping keeps annotations in the plot area. Annotations are always rendered on top.\nIMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, bool round = false);\nIMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, const char* fmt, ...)           IM_FMTARGS(6);\nIMPLOT_API void AnnotationV(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, const char* fmt, va_list args) IM_FMTLIST(6);\n\n// Shows a x-axis tag at the specified coordinate value.\nIMPLOT_API void TagX(double x, const ImVec4& col, bool round = false);\nIMPLOT_API void TagX(double x, const ImVec4& col, const char* fmt, ...)           IM_FMTARGS(3);\nIMPLOT_API void TagXV(double x, const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(3);\n\n// Shows a y-axis tag at the specified coordinate value.\nIMPLOT_API void TagY(double y, const ImVec4& col, bool round = false);\nIMPLOT_API void TagY(double y, const ImVec4& col, const char* fmt, ...)           IM_FMTARGS(3);\nIMPLOT_API void TagYV(double y, const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(3);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot Utils\n//-----------------------------------------------------------------------------\n\n// Select which axis/axes will be used for subsequent plot elements.\nIMPLOT_API void SetAxis(ImAxis axis);\nIMPLOT_API void SetAxes(ImAxis x_axis, ImAxis y_axis);\n\n// Convert pixels to a position in the current plot's coordinate system. Passing IMPLOT_AUTO uses the current axes.\nIMPLOT_API ImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO);\nIMPLOT_API ImPlotPoint PixelsToPlot(float x, float y, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO);\n\n// Convert a position in the current plot's coordinate system to pixels. Passing IMPLOT_AUTO uses the current axes.\nIMPLOT_API ImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO);\nIMPLOT_API ImVec2 PlotToPixels(double x, double y, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO);\n\n// Get the current Plot position (top-left) in pixels.\nIMPLOT_API ImVec2 GetPlotPos();\n// Get the curent Plot size in pixels.\nIMPLOT_API ImVec2 GetPlotSize();\n\n// Returns the mouse position in x,y coordinates of the current plot. Passing IMPLOT_AUTO uses the current axes.\nIMPLOT_API ImPlotPoint GetPlotMousePos(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO);\n// Returns the current plot axis range.\nIMPLOT_API ImPlotRect GetPlotLimits(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO);\n\n// Returns true if the plot area in the current plot is hovered.\nIMPLOT_API bool IsPlotHovered();\n// Returns true if the axis label area in the current plot is hovered.\nIMPLOT_API bool IsAxisHovered(ImAxis axis);\n// Returns true if the bounding frame of a subplot is hovered.\nIMPLOT_API bool IsSubplotsHovered();\n\n// Returns true if the current plot is being box selected.\nIMPLOT_API bool IsPlotSelected();\n// Returns the current plot box selection bounds. Passing IMPLOT_AUTO uses the current axes.\nIMPLOT_API ImPlotRect GetPlotSelection(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO);\n// Cancels a the current plot box selection.\nIMPLOT_API void CancelPlotSelection();\n\n// Hides or shows the next plot item (i.e. as if it were toggled from the legend).\n// Use ImPlotCond_Always if you need to forcefully set this every frame.\nIMPLOT_API void HideNextItem(bool hidden = true, ImPlotCond cond = ImPlotCond_Once);\n\n// Use the following around calls to Begin/EndPlot to align l/r/t/b padding.\n// Consider using Begin/EndSubplots first. They are more feature rich and\n// accomplish the same behaviour by default. The functions below offer lower\n// level control of plot alignment.\n\n// Align axis padding over multiple plots in a single row or column. #group_id must\n// be unique. If this function returns true, EndAlignedPlots() must be called.\nIMPLOT_API bool BeginAlignedPlots(const char* group_id, bool vertical = true);\n// Only call EndAlignedPlots() if BeginAlignedPlots() returns true!\nIMPLOT_API void EndAlignedPlots();\n\n//-----------------------------------------------------------------------------\n// [SECTION] Legend Utils\n//-----------------------------------------------------------------------------\n\n// Begin a popup for a legend entry.\nIMPLOT_API bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button=1);\n// End a popup for a legend entry.\nIMPLOT_API void EndLegendPopup();\n// Returns true if a plot item legend entry is hovered.\nIMPLOT_API bool IsLegendEntryHovered(const char* label_id);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Drag and Drop\n//-----------------------------------------------------------------------------\n\n// Turns the current plot's plotting area into a drag and drop target. Don't forget to call EndDragDropTarget!\nIMPLOT_API bool BeginDragDropTargetPlot();\n// Turns the current plot's X-axis into a drag and drop target. Don't forget to call EndDragDropTarget!\nIMPLOT_API bool BeginDragDropTargetAxis(ImAxis axis);\n// Turns the current plot's legend into a drag and drop target. Don't forget to call EndDragDropTarget!\nIMPLOT_API bool BeginDragDropTargetLegend();\n// Ends a drag and drop target (currently just an alias for ImGui::EndDragDropTarget).\nIMPLOT_API void EndDragDropTarget();\n\n// NB: By default, plot and axes drag and drop *sources* require holding the Ctrl modifier to initiate the drag.\n// You can change the modifier if desired. If ImGuiMod_None is provided, the axes will be locked from panning.\n\n// Turns the current plot's plotting area into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource!\nIMPLOT_API bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags=0);\n// Turns the current plot's X-axis into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource!\nIMPLOT_API bool BeginDragDropSourceAxis(ImAxis axis, ImGuiDragDropFlags flags=0);\n// Turns an item in the current plot's legend into drag and drop source. Don't forget to call EndDragDropSource!\nIMPLOT_API bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags=0);\n// Ends a drag and drop source (currently just an alias for ImGui::EndDragDropSource).\nIMPLOT_API void EndDragDropSource();\n\n//-----------------------------------------------------------------------------\n// [SECTION] Styling\n//-----------------------------------------------------------------------------\n\n// Styling colors in ImPlot works similarly to styling colors in ImGui, but\n// with one important difference. Like ImGui, all style colors are stored in an\n// indexable array in ImPlotStyle. You can permanently modify these values through\n// GetStyle().Colors, or temporarily modify them with Push/Pop functions below.\n// However, by default all style colors in ImPlot default to a special color\n// IMPLOT_AUTO_COL. The behavior of this color depends upon the style color to\n// which it as applied:\n//\n//     1) For style colors associated with plot items (e.g. ImPlotCol_Line),\n//        IMPLOT_AUTO_COL tells ImPlot to color the item with the next unused\n//        color in the current colormap. Thus, every item will have a different\n//        color up to the number of colors in the colormap, at which point the\n//        colormap will roll over. For most use cases, you should not need to\n//        set these style colors to anything but IMPLOT_COL_AUTO; you are\n//        probably better off changing the current colormap. However, if you\n//        need to explicitly color a particular item you may either Push/Pop\n//        the style color around the item in question, or use the SetNextXXXStyle\n//        API below. If you permanently set one of these style colors to a specific\n//        color, or forget to call Pop, then all subsequent items will be styled\n//        with the color you set.\n//\n//     2) For style colors associated with plot styling (e.g. ImPlotCol_PlotBg),\n//        IMPLOT_AUTO_COL tells ImPlot to set that color from color data in your\n//        **ImGuiStyle**. The ImGuiCol_ that these style colors default to are\n//        detailed above, and in general have been mapped to produce plots visually\n//        consistent with your current ImGui style. Of course, you are free to\n//        manually set these colors to whatever you like, and further can Push/Pop\n//        them around individual plots for plot-specific styling (e.g. coloring axes).\n\n// Provides access to plot style structure for permanant modifications to colors, sizes, etc.\nIMPLOT_API ImPlotStyle& GetStyle();\n\n// Style plot colors for current ImGui style (default).\nIMPLOT_API void StyleColorsAuto(ImPlotStyle* dst = nullptr);\n// Style plot colors for ImGui \"Classic\".\nIMPLOT_API void StyleColorsClassic(ImPlotStyle* dst = nullptr);\n// Style plot colors for ImGui \"Dark\".\nIMPLOT_API void StyleColorsDark(ImPlotStyle* dst = nullptr);\n// Style plot colors for ImGui \"Light\".\nIMPLOT_API void StyleColorsLight(ImPlotStyle* dst = nullptr);\n\n// Use PushStyleX to temporarily modify your ImPlotStyle. The modification\n// will last until the matching call to PopStyleX. You MUST call a pop for\n// every push, otherwise you will leak memory! This behaves just like ImGui.\n\n// Temporarily modify a style color. Don't forget to call PopStyleColor!\nIMPLOT_API void PushStyleColor(ImPlotCol idx, ImU32 col);\nIMPLOT_API void PushStyleColor(ImPlotCol idx, const ImVec4& col);\n// Undo temporary style color modification(s). Undo multiple pushes at once by increasing count.\nIMPLOT_API void PopStyleColor(int count = 1);\n\n// Temporarily modify a style variable of float type. Don't forget to call PopStyleVar!\nIMPLOT_API void PushStyleVar(ImPlotStyleVar idx, float val);\n// Temporarily modify a style variable of int type. Don't forget to call PopStyleVar!\nIMPLOT_API void PushStyleVar(ImPlotStyleVar idx, int val);\n// Temporarily modify a style variable of ImVec2 type. Don't forget to call PopStyleVar!\nIMPLOT_API void PushStyleVar(ImPlotStyleVar idx, const ImVec2& val);\n// Undo temporary style variable modification(s). Undo multiple pushes at once by increasing count.\nIMPLOT_API void PopStyleVar(int count = 1);\n\n// The following can be used to modify the style of the next plot item ONLY. They do\n// NOT require calls to PopStyleX. Leave style attributes you don't want modified to\n// IMPLOT_AUTO or IMPLOT_AUTO_COL. Automatic styles will be deduced from the current\n// values in your ImPlotStyle or from Colormap data.\n\n// Set the line color and weight for the next item only.\nIMPLOT_API void SetNextLineStyle(const ImVec4& col = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO);\n// Set the fill color for the next item only.\nIMPLOT_API void SetNextFillStyle(const ImVec4& col = IMPLOT_AUTO_COL, float alpha_mod = IMPLOT_AUTO);\n// Set the marker style for the next item only.\nIMPLOT_API void SetNextMarkerStyle(ImPlotMarker marker = IMPLOT_AUTO, float size = IMPLOT_AUTO, const ImVec4& fill = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO, const ImVec4& outline = IMPLOT_AUTO_COL);\n// Set the error bar style for the next item only.\nIMPLOT_API void SetNextErrorBarStyle(const ImVec4& col = IMPLOT_AUTO_COL, float size = IMPLOT_AUTO, float weight = IMPLOT_AUTO);\n\n// Gets the last item primary color (i.e. its legend icon color)\nIMPLOT_API ImVec4 GetLastItemColor();\n\n// Returns the null terminated string name for an ImPlotCol.\nIMPLOT_API const char* GetStyleColorName(ImPlotCol idx);\n// Returns the null terminated string name for an ImPlotMarker.\nIMPLOT_API const char* GetMarkerName(ImPlotMarker idx);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Colormaps\n//-----------------------------------------------------------------------------\n\n// Item styling is based on colormaps when the relevant ImPlotCol_XXX is set to\n// IMPLOT_AUTO_COL (default). Several built-in colormaps are available. You can\n// add and then push/pop your own colormaps as well. To permanently set a colormap,\n// modify the Colormap index member of your ImPlotStyle.\n\n// Colormap data will be ignored and a custom color will be used if you have done one of the following:\n//     1) Modified an item style color in your ImPlotStyle to anything other than IMPLOT_AUTO_COL.\n//     2) Pushed an item style color using PushStyleColor().\n//     3) Set the next item style with a SetNextXXXStyle function.\n\n// Add a new colormap. The color data will be copied. The colormap can be used by pushing either the returned index or the\n// string name with PushColormap. The colormap name must be unique and the size must be greater than 1. You will receive\n// an assert otherwise! By default colormaps are considered to be qualitative (i.e. discrete). If you want to create a\n// continuous colormap, set #qual=false. This will treat the colors you provide as keys, and ImPlot will build a linearly\n// interpolated lookup table. The memory footprint of this table will be exactly ((size-1)*255+1)*4 bytes.\nIMPLOT_API ImPlotColormap AddColormap(const char* name, const ImVec4* cols, int size, bool qual=true);\nIMPLOT_API ImPlotColormap AddColormap(const char* name, const ImU32*  cols, int size, bool qual=true);\n\n// Returns the number of available colormaps (i.e. the built-in + user-added count).\nIMPLOT_API int GetColormapCount();\n// Returns a null terminated string name for a colormap given an index. Returns nullptr if index is invalid.\nIMPLOT_API const char* GetColormapName(ImPlotColormap cmap);\n// Returns an index number for a colormap given a valid string name. Returns -1 if name is invalid.\nIMPLOT_API ImPlotColormap GetColormapIndex(const char* name);\n\n// Temporarily switch to one of the built-in (i.e. ImPlotColormap_XXX) or user-added colormaps (i.e. a return value of AddColormap). Don't forget to call PopColormap!\nIMPLOT_API void PushColormap(ImPlotColormap cmap);\n// Push a colormap by string name. Use built-in names such as \"Default\", \"Deep\", \"Jet\", etc. or a string you provided to AddColormap. Don't forget to call PopColormap!\nIMPLOT_API void PushColormap(const char* name);\n// Undo temporary colormap modification(s). Undo multiple pushes at once by increasing count.\nIMPLOT_API void PopColormap(int count = 1);\n\n// Returns the next color from the current colormap and advances the colormap for the current plot.\n// Can also be used with no return value to skip colors if desired. You need to call this between Begin/EndPlot!\nIMPLOT_API ImVec4 NextColormapColor();\n\n// Colormap utils. If cmap = IMPLOT_AUTO (default), the current colormap is assumed.\n// Pass an explicit colormap index (built-in or user-added) to specify otherwise.\n\n// Returns the size of a colormap.\nIMPLOT_API int GetColormapSize(ImPlotColormap cmap = IMPLOT_AUTO);\n// Returns a color from a colormap given an index >= 0 (modulo will be performed).\nIMPLOT_API ImVec4 GetColormapColor(int idx, ImPlotColormap cmap = IMPLOT_AUTO);\n// Sample a color from the current colormap given t between 0 and 1.\nIMPLOT_API ImVec4 SampleColormap(float t, ImPlotColormap cmap = IMPLOT_AUTO);\n\n// Shows a vertical color scale with linear spaced ticks using the specified color map. Use double hashes to hide label (e.g. \"##NoLabel\"). If scale_min > scale_max, the scale to color mapping will be reversed.\nIMPLOT_API void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0), const char* format = \"%g\", ImPlotColormapScaleFlags flags = 0, ImPlotColormap cmap = IMPLOT_AUTO);\n// Shows a horizontal slider with a colormap gradient background. Optionally returns the color sampled at t in [0 1].\nIMPLOT_API bool ColormapSlider(const char* label, float* t, ImVec4* out = nullptr, const char* format = \"\", ImPlotColormap cmap = IMPLOT_AUTO);\n// Shows a button with a colormap gradient brackground.\nIMPLOT_API bool ColormapButton(const char* label, const ImVec2& size = ImVec2(0,0), ImPlotColormap cmap = IMPLOT_AUTO);\n\n// When items in a plot sample their color from a colormap, the color is cached and does not change\n// unless explicitly overriden. Therefore, if you change the colormap after the item has already been plotted,\n// item colors will NOT update. If you need item colors to resample the new colormap, then use this\n// function to bust the cached colors. If #plot_title_id is nullptr, then every item in EVERY existing plot\n// will be cache busted. Otherwise only the plot specified by #plot_title_id will be busted. For the\n// latter, this function must be called in the same ImGui ID scope that the plot is in. You should rarely if ever\n// need this function, but it is available for applications that require runtime colormap swaps (e.g. Heatmaps demo).\nIMPLOT_API void BustColorCache(const char* plot_title_id = nullptr);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Input Mapping\n//-----------------------------------------------------------------------------\n\n// Provides access to input mapping structure for permanant modifications to controls for pan, select, etc.\nIMPLOT_API ImPlotInputMap& GetInputMap();\n\n// Default input mapping: pan = LMB drag, box select = RMB drag, fit = LMB double click, context menu = RMB click, zoom = scroll.\nIMPLOT_API void MapInputDefault(ImPlotInputMap* dst = nullptr);\n// Reverse input mapping: pan = RMB drag, box select = LMB drag, fit = LMB double click, context menu = RMB click, zoom = scroll.\nIMPLOT_API void MapInputReverse(ImPlotInputMap* dst = nullptr);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Miscellaneous\n//-----------------------------------------------------------------------------\n\n// Render icons similar to those that appear in legends (nifty for data lists).\nIMPLOT_API void ItemIcon(const ImVec4& col);\nIMPLOT_API void ItemIcon(ImU32 col);\nIMPLOT_API void ColormapIcon(ImPlotColormap cmap);\n\n// Get the plot draw list for custom rendering to the current plot area. Call between Begin/EndPlot.\nIMPLOT_API ImDrawList* GetPlotDrawList();\n// Push clip rect for rendering to current plot area. The rect can be expanded or contracted by #expand pixels. Call between Begin/EndPlot.\nIMPLOT_API void PushPlotClipRect(float expand=0);\n// Pop plot clip rect. Call between Begin/EndPlot.\nIMPLOT_API void PopPlotClipRect();\n\n// Shows ImPlot style selector dropdown menu.\nIMPLOT_API bool ShowStyleSelector(const char* label);\n// Shows ImPlot colormap selector dropdown menu.\nIMPLOT_API bool ShowColormapSelector(const char* label);\n// Shows ImPlot input map selector dropdown menu.\nIMPLOT_API bool ShowInputMapSelector(const char* label);\n// Shows ImPlot style editor block (not a window).\nIMPLOT_API void ShowStyleEditor(ImPlotStyle* ref = nullptr);\n// Add basic help/info block for end users (not a window).\nIMPLOT_API void ShowUserGuide();\n// Shows ImPlot metrics/debug information window.\nIMPLOT_API void ShowMetricsWindow(bool* p_popen = nullptr);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Demo\n//-----------------------------------------------------------------------------\n\n// Shows the ImPlot demo window (add implot_demo.cpp to your sources!)\nIMPLOT_API void ShowDemoWindow(bool* p_open = nullptr);\n\n}  // namespace ImPlot\n\n//-----------------------------------------------------------------------------\n// [SECTION] Obsolete API\n//-----------------------------------------------------------------------------\n\n// The following functions will be removed! Keep your copy of implot up to date!\n// Occasionally set '#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS' to stay ahead.\n// If you absolutely must use these functions and do not want to receive compiler\n// warnings, set '#define IMPLOT_DISABLE_OBSOLETE_WARNINGS'.\n\n#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS\n\n#ifndef IMPLOT_DISABLE_DEPRECATED_WARNINGS\n#if __cplusplus > 201402L\n#define IMPLOT_DEPRECATED(method) [[deprecated]] method\n#elif defined( __GNUC__ ) && !defined( __INTEL_COMPILER ) && ( __GNUC__ > 3 || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) )\n#define IMPLOT_DEPRECATED(method) method __attribute__( ( deprecated ) )\n#elif defined( _MSC_VER )\n#define IMPLOT_DEPRECATED(method) __declspec(deprecated) method\n#else\n#define IMPLOT_DEPRECATED(method) method\n#endif\n#else\n#define IMPLOT_DEPRECATED(method) method\n#endif\n\nenum ImPlotFlagsObsolete_ {\n    ImPlotFlags_YAxis2 = 1 << 20,\n    ImPlotFlags_YAxis3 = 1 << 21,\n};\n\nnamespace ImPlot {\n\n// OBSOLETED in v0.13 -> PLANNED REMOVAL in v1.0\nIMPLOT_DEPRECATED( IMPLOT_API bool BeginPlot(const char* title_id,\n                                             const char* x_label,  // = nullptr,\n                                             const char* y_label,  // = nullptr,\n                                             const ImVec2& size       = ImVec2(-1,0),\n                                             ImPlotFlags flags        = ImPlotFlags_None,\n                                             ImPlotAxisFlags x_flags  = 0,\n                                             ImPlotAxisFlags y_flags  = 0,\n                                             ImPlotAxisFlags y2_flags = ImPlotAxisFlags_AuxDefault,\n                                             ImPlotAxisFlags y3_flags = ImPlotAxisFlags_AuxDefault,\n                                             const char* y2_label     = nullptr,\n                                             const char* y3_label     = nullptr) );\n\n} // namespace ImPlot\n\n#endif\n"
  },
  {
    "path": "lib/third_party/imgui/implot/include/implot_internal.h",
    "content": "// MIT License\n\n// Copyright (c) 2023 Evan Pezent\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n// ImPlot v0.17\n\n// You may use this file to debug, understand or extend ImPlot features but we\n// don't provide any guarantee of forward compatibility!\n\n//-----------------------------------------------------------------------------\n// [SECTION] Header Mess\n//-----------------------------------------------------------------------------\n\n#pragma once\n\n#include <time.h>\n#include \"imgui_internal.h\"\n#include <cmath>\n\n#ifndef IMPLOT_VERSION\n#error Must include implot.h before implot_internal.h\n#endif\n\n\n// Support for pre-1.84 versions. ImPool's GetSize() -> GetBufSize()\n#if (IMGUI_VERSION_NUM < 18303)\n#define GetBufSize GetSize\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Constants\n//-----------------------------------------------------------------------------\n\n// Constants can be changed unless stated otherwise. We may move some of these\n// to ImPlotStyleVar_ over time.\n\n// Mimimum allowable timestamp value 01/01/1970 @ 12:00am (UTC) (DO NOT DECREASE THIS)\n#define IMPLOT_MIN_TIME  0\n// Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) (DO NOT INCREASE THIS)\n#define IMPLOT_MAX_TIME  32503680000\n// Default label format for axis labels\n#define IMPLOT_LABEL_FORMAT \"%g\"\n// Max character size for tick labels\n#define IMPLOT_LABEL_MAX_SIZE 32\n\n//-----------------------------------------------------------------------------\n// [SECTION] Macros\n//-----------------------------------------------------------------------------\n\n#define IMPLOT_NUM_X_AXES ImAxis_Y1\n#define IMPLOT_NUM_Y_AXES (ImAxis_COUNT - IMPLOT_NUM_X_AXES)\n\n// Split ImU32 color into RGB components [0 255]\n#define IM_COL32_SPLIT_RGB(col,r,g,b) \\\n    ImU32 r = ((col >> IM_COL32_R_SHIFT) & 0xFF); \\\n    ImU32 g = ((col >> IM_COL32_G_SHIFT) & 0xFF); \\\n    ImU32 b = ((col >> IM_COL32_B_SHIFT) & 0xFF);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Forward Declarations\n//-----------------------------------------------------------------------------\n\nstruct ImPlotTick;\nstruct ImPlotAxis;\nstruct ImPlotAxisColor;\nstruct ImPlotItem;\nstruct ImPlotLegend;\nstruct ImPlotPlot;\nstruct ImPlotNextPlotData;\nstruct ImPlotTicker;\n\n//-----------------------------------------------------------------------------\n// [SECTION] Context Pointer\n//-----------------------------------------------------------------------------\n\n#ifndef GImPlot\nextern IMPLOT_API ImPlotContext* GImPlot; // Current implicit context pointer\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Generic Helpers\n//-----------------------------------------------------------------------------\n\n// Computes the common (base-10) logarithm\nstatic inline float  ImLog10(float x)  { return log10f(x); }\nstatic inline double ImLog10(double x) { return log10(x);  }\nstatic inline float  ImSinh(float x)   { return sinhf(x);  }\nstatic inline double ImSinh(double x)  { return sinh(x);   }\nstatic inline float  ImAsinh(float x)  { return asinhf(x); }\nstatic inline double ImAsinh(double x) { return asinh(x);  }\n// Returns true if a flag is set\ntemplate <typename TSet, typename TFlag>\nstatic inline bool ImHasFlag(TSet set, TFlag flag) { return (set & flag) == flag; }\n// Flips a flag in a flagset\ntemplate <typename TSet, typename TFlag>\nstatic inline void ImFlipFlag(TSet& set, TFlag flag) { ImHasFlag(set, flag) ? set &= ~flag : set |= flag; }\n// Linearly remaps x from [x0 x1] to [y0 y1].\ntemplate <typename T>\nstatic inline T ImRemap(T x, T x0, T x1, T y0, T y1) { return y0 + (x - x0) * (y1 - y0) / (x1 - x0); }\n// Linear rempas x from [x0 x1] to [0 1]\ntemplate <typename T>\nstatic inline T ImRemap01(T x, T x0, T x1) { return (x - x0) / (x1 - x0); }\n// Returns always positive modulo (assumes r != 0)\nstatic inline int ImPosMod(int l, int r) { return (l % r + r) % r; }\n// Returns true if val is NAN\nstatic inline bool ImNan(double val) { return std::isnan(val); }\n// Returns true if val is NAN or INFINITY\nstatic inline bool ImNanOrInf(double val) { return !(val >= -DBL_MAX && val <= DBL_MAX) || ImNan(val); }\n// Turns NANs to 0s\nstatic inline double ImConstrainNan(double val) { return ImNan(val) ? 0 : val; }\n// Turns infinity to floating point maximums\nstatic inline double ImConstrainInf(double val) { return val >= DBL_MAX ?  DBL_MAX : val <= -DBL_MAX ? - DBL_MAX : val; }\n// Turns numbers less than or equal to 0 to 0.001 (sort of arbitrary, is there a better way?)\nstatic inline double ImConstrainLog(double val) { return val <= 0 ? 0.001f : val; }\n// Turns numbers less than 0 to zero\nstatic inline double ImConstrainTime(double val) { return val < IMPLOT_MIN_TIME ? IMPLOT_MIN_TIME : (val > IMPLOT_MAX_TIME ? IMPLOT_MAX_TIME : val); }\n// True if two numbers are approximately equal using units in the last place.\nstatic inline bool ImAlmostEqual(double v1, double v2, int ulp = 2) { return ImAbs(v1-v2) < DBL_EPSILON * ImAbs(v1+v2) * ulp || ImAbs(v1-v2) < DBL_MIN; }\n// Finds min value in an unsorted array\ntemplate <typename T>\nstatic inline T ImMinArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] < m) { m = values[i]; } } return m; }\n// Finds the max value in an unsorted array\ntemplate <typename T>\nstatic inline T ImMaxArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] > m) { m = values[i]; } } return m; }\n// Finds the min and max value in an unsorted array\ntemplate <typename T>\nstatic inline void ImMinMaxArray(const T* values, int count, T* min_out, T* max_out) {\n    T Min = values[0]; T Max = values[0];\n    for (int i = 1; i < count; ++i) {\n        if (values[i] < Min) { Min = values[i]; }\n        if (values[i] > Max) { Max = values[i]; }\n    }\n    *min_out = Min; *max_out = Max;\n}\n// Finds the sim of an array\ntemplate <typename T>\nstatic inline T ImSum(const T* values, int count) {\n    T sum  = 0;\n    for (int i = 0; i < count; ++i)\n        sum += values[i];\n    return sum;\n}\n// Finds the mean of an array\ntemplate <typename T>\nstatic inline double ImMean(const T* values, int count) {\n    double den = 1.0 / count;\n    double mu  = 0;\n    for (int i = 0; i < count; ++i)\n        mu += (double)values[i] * den;\n    return mu;\n}\n// Finds the sample standard deviation of an array\ntemplate <typename T>\nstatic inline double ImStdDev(const T* values, int count) {\n    double den = 1.0 / (count - 1.0);\n    double mu  = ImMean(values, count);\n    double x   = 0;\n    for (int i = 0; i < count; ++i)\n        x += ((double)values[i] - mu) * ((double)values[i] - mu) * den;\n    return sqrt(x);\n}\n// Mix color a and b by factor s in [0 256]\nstatic inline ImU32 ImMixU32(ImU32 a, ImU32 b, ImU32 s) {\n#ifdef IMPLOT_MIX64\n    const ImU32 af = 256-s;\n    const ImU32 bf = s;\n    const ImU64 al = (a & 0x00ff00ff) | (((ImU64)(a & 0xff00ff00)) << 24);\n    const ImU64 bl = (b & 0x00ff00ff) | (((ImU64)(b & 0xff00ff00)) << 24);\n    const ImU64 mix = (al * af + bl * bf);\n    return ((mix >> 32) & 0xff00ff00) | ((mix & 0xff00ff00) >> 8);\n#else\n    const ImU32 af = 256-s;\n    const ImU32 bf = s;\n    const ImU32 al = (a & 0x00ff00ff);\n    const ImU32 ah = (a & 0xff00ff00) >> 8;\n    const ImU32 bl = (b & 0x00ff00ff);\n    const ImU32 bh = (b & 0xff00ff00) >> 8;\n    const ImU32 ml = (al * af + bl * bf);\n    const ImU32 mh = (ah * af + bh * bf);\n    return (mh & 0xff00ff00) | ((ml & 0xff00ff00) >> 8);\n#endif\n}\n\n// Lerp across an array of 32-bit collors given t in [0.0 1.0]\nstatic inline ImU32 ImLerpU32(const ImU32* colors, int size, float t) {\n    int i1 = (int)((size - 1 ) * t);\n    int i2 = i1 + 1;\n    if (i2 == size || size == 1)\n        return colors[i1];\n    float den = 1.0f / (size - 1);\n    float t1 = i1 * den;\n    float t2 = i2 * den;\n    float tr = ImRemap01(t, t1, t2);\n    return ImMixU32(colors[i1], colors[i2], (ImU32)(tr*256));\n}\n\n// Set alpha channel of 32-bit color from float in range [0.0 1.0]\nstatic inline ImU32 ImAlphaU32(ImU32 col, float alpha) {\n    return col & ~((ImU32)((1.0f-alpha)*255)<<IM_COL32_A_SHIFT);\n}\n\n// Returns true of two ranges overlap\ntemplate <typename T>\nstatic inline bool ImOverlaps(T min_a, T max_a, T min_b, T max_b) {\n    return min_a <= max_b && min_b <= max_a;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot Enums\n//-----------------------------------------------------------------------------\n\ntypedef int ImPlotTimeUnit;    // -> enum ImPlotTimeUnit_\ntypedef int ImPlotDateFmt;     // -> enum ImPlotDateFmt_\ntypedef int ImPlotTimeFmt;     // -> enum ImPlotTimeFmt_\n\nenum ImPlotTimeUnit_ {\n    ImPlotTimeUnit_Us,  // microsecond\n    ImPlotTimeUnit_Ms,  // millisecond\n    ImPlotTimeUnit_S,   // second\n    ImPlotTimeUnit_Min, // minute\n    ImPlotTimeUnit_Hr,  // hour\n    ImPlotTimeUnit_Day, // day\n    ImPlotTimeUnit_Mo,  // month\n    ImPlotTimeUnit_Yr,  // year\n    ImPlotTimeUnit_COUNT\n};\n\nenum ImPlotDateFmt_ {              // default        [ ISO 8601     ]\n    ImPlotDateFmt_None = 0,\n    ImPlotDateFmt_DayMo,           // 10/3           [ --10-03      ]\n    ImPlotDateFmt_DayMoYr,         // 10/3/91        [ 1991-10-03   ]\n    ImPlotDateFmt_MoYr,            // Oct 1991       [ 1991-10      ]\n    ImPlotDateFmt_Mo,              // Oct            [ --10         ]\n    ImPlotDateFmt_Yr               // 1991           [ 1991         ]\n};\n\nenum ImPlotTimeFmt_ {              // default        [ 24 Hour Clock ]\n    ImPlotTimeFmt_None = 0,\n    ImPlotTimeFmt_Us,              // .428 552       [ .428 552     ]\n    ImPlotTimeFmt_SUs,             // :29.428 552    [ :29.428 552  ]\n    ImPlotTimeFmt_SMs,             // :29.428        [ :29.428      ]\n    ImPlotTimeFmt_S,               // :29            [ :29          ]\n    ImPlotTimeFmt_MinSMs,          // 21:29.428      [ 21:29.428    ]\n    ImPlotTimeFmt_HrMinSMs,        // 7:21:29.428pm  [ 19:21:29.428 ]\n    ImPlotTimeFmt_HrMinS,          // 7:21:29pm      [ 19:21:29     ]\n    ImPlotTimeFmt_HrMin,           // 7:21pm         [ 19:21        ]\n    ImPlotTimeFmt_Hr               // 7pm            [ 19:00        ]\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Callbacks\n//-----------------------------------------------------------------------------\n\ntypedef void (*ImPlotLocator)(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Structs\n//-----------------------------------------------------------------------------\n\n// Combined date/time format spec\nstruct ImPlotDateTimeSpec {\n    ImPlotDateTimeSpec() {}\n    ImPlotDateTimeSpec(ImPlotDateFmt date_fmt, ImPlotTimeFmt time_fmt, bool use_24_hr_clk = false, bool use_iso_8601 = false) {\n        Date           = date_fmt;\n        Time           = time_fmt;\n        UseISO8601     = use_iso_8601;\n        Use24HourClock = use_24_hr_clk;\n    }\n    ImPlotDateFmt Date;\n    ImPlotTimeFmt Time;\n    bool UseISO8601;\n    bool Use24HourClock;\n};\n\n// Two part timestamp struct.\nstruct ImPlotTime {\n    time_t S;  // second part\n    int    Us; // microsecond part\n    ImPlotTime() { S = 0; Us = 0; }\n    ImPlotTime(time_t s, int us = 0) { S  = s + us / 1000000; Us = us % 1000000; }\n    void RollOver() { S  = S + Us / 1000000;  Us = Us % 1000000; }\n    double ToDouble() const { return (double)S + (double)Us / 1000000.0; }\n    static ImPlotTime FromDouble(double t) { return ImPlotTime((time_t)t, (int)(t * 1000000 - floor(t) * 1000000)); }\n};\n\nstatic inline ImPlotTime operator+(const ImPlotTime& lhs, const ImPlotTime& rhs)\n{ return ImPlotTime(lhs.S + rhs.S, lhs.Us + rhs.Us); }\nstatic inline ImPlotTime operator-(const ImPlotTime& lhs, const ImPlotTime& rhs)\n{ return ImPlotTime(lhs.S - rhs.S, lhs.Us - rhs.Us); }\nstatic inline bool operator==(const ImPlotTime& lhs, const ImPlotTime& rhs)\n{ return lhs.S == rhs.S && lhs.Us == rhs.Us; }\nstatic inline bool operator<(const ImPlotTime& lhs, const ImPlotTime& rhs)\n{ return lhs.S == rhs.S ? lhs.Us < rhs.Us : lhs.S < rhs.S; }\nstatic inline bool operator>(const ImPlotTime& lhs, const ImPlotTime& rhs)\n{ return rhs < lhs; }\nstatic inline bool operator<=(const ImPlotTime& lhs, const ImPlotTime& rhs)\n{ return lhs < rhs || lhs == rhs; }\nstatic inline bool operator>=(const ImPlotTime& lhs, const ImPlotTime& rhs)\n{ return lhs > rhs || lhs == rhs; }\n\n// Colormap data storage\nstruct ImPlotColormapData {\n    ImVector<ImU32> Keys;\n    ImVector<int>   KeyCounts;\n    ImVector<int>   KeyOffsets;\n    ImVector<ImU32> Tables;\n    ImVector<int>   TableSizes;\n    ImVector<int>   TableOffsets;\n    ImGuiTextBuffer Text;\n    ImVector<int>   TextOffsets;\n    ImVector<bool>  Quals;\n    ImGuiStorage    Map;\n    int             Count;\n\n    ImPlotColormapData() { Count = 0; }\n\n    int Append(const char* name, const ImU32* keys, int count, bool qual) {\n        if (GetIndex(name) != -1)\n            return -1;\n        KeyOffsets.push_back(Keys.size());\n        KeyCounts.push_back(count);\n        Keys.reserve(Keys.size()+count);\n        for (int i = 0; i < count; ++i)\n            Keys.push_back(keys[i]);\n        TextOffsets.push_back(Text.size());\n        Text.append(name, name + strlen(name) + 1);\n        Quals.push_back(qual);\n        ImGuiID id = ImHashStr(name);\n        int idx = Count++;\n        Map.SetInt(id,idx);\n        _AppendTable(idx);\n        return idx;\n    }\n\n    void _AppendTable(ImPlotColormap cmap) {\n        int key_count     = GetKeyCount(cmap);\n        const ImU32* keys = GetKeys(cmap);\n        int off = Tables.size();\n        TableOffsets.push_back(off);\n        if (IsQual(cmap)) {\n            Tables.reserve(key_count);\n            for (int i = 0; i < key_count; ++i)\n                Tables.push_back(keys[i]);\n            TableSizes.push_back(key_count);\n        }\n        else {\n            int max_size = 255 * (key_count-1) + 1;\n            Tables.reserve(off + max_size);\n            // ImU32 last = keys[0];\n            // Tables.push_back(last);\n            // int n = 1;\n            for (int i = 0; i < key_count-1; ++i) {\n                for (int s = 0; s < 255; ++s) {\n                    ImU32 a = keys[i];\n                    ImU32 b = keys[i+1];\n                    ImU32 c = ImMixU32(a,b,s);\n                    // if (c != last) {\n                        Tables.push_back(c);\n                        // last = c;\n                        // n++;\n                    // }\n                }\n            }\n            ImU32 c = keys[key_count-1];\n            // if (c != last) {\n                Tables.push_back(c);\n                // n++;\n            // }\n            // TableSizes.push_back(n);\n            TableSizes.push_back(max_size);\n        }\n    }\n\n    void RebuildTables() {\n        Tables.resize(0);\n        TableSizes.resize(0);\n        TableOffsets.resize(0);\n        for (int i = 0; i < Count; ++i)\n            _AppendTable(i);\n    }\n\n    inline bool           IsQual(ImPlotColormap cmap) const                      { return Quals[cmap];                                                }\n    inline const char*    GetName(ImPlotColormap cmap) const                     { return cmap < Count ? Text.Buf.Data + TextOffsets[cmap] : nullptr; }\n    inline ImPlotColormap GetIndex(const char* name) const                       { ImGuiID key = ImHashStr(name); return Map.GetInt(key,-1);          }\n\n    inline const ImU32*   GetKeys(ImPlotColormap cmap) const                     { return &Keys[KeyOffsets[cmap]];                                    }\n    inline int            GetKeyCount(ImPlotColormap cmap) const                 { return KeyCounts[cmap];                                            }\n    inline ImU32          GetKeyColor(ImPlotColormap cmap, int idx) const        { return Keys[KeyOffsets[cmap]+idx];                                 }\n    inline void           SetKeyColor(ImPlotColormap cmap, int idx, ImU32 value) { Keys[KeyOffsets[cmap]+idx] = value; RebuildTables();               }\n\n    inline const ImU32*   GetTable(ImPlotColormap cmap) const                    { return &Tables[TableOffsets[cmap]];                                }\n    inline int            GetTableSize(ImPlotColormap cmap) const                { return TableSizes[cmap];                                           }\n    inline ImU32          GetTableColor(ImPlotColormap cmap, int idx) const      { return Tables[TableOffsets[cmap]+idx];                             }\n\n    inline ImU32 LerpTable(ImPlotColormap cmap, float t) const {\n        int off = TableOffsets[cmap];\n        int siz = TableSizes[cmap];\n        int idx = Quals[cmap] ? ImClamp((int)(siz*t),0,siz-1) : (int)((siz - 1) * t + 0.5f);\n        return Tables[off + idx];\n    }\n};\n\n// ImPlotPoint with positive/negative error values\nstruct ImPlotPointError {\n    double X, Y, Neg, Pos;\n    ImPlotPointError(double x, double y, double neg, double pos) {\n        X = x; Y = y; Neg = neg; Pos = pos;\n    }\n};\n\n// Interior plot label/annotation\nstruct ImPlotAnnotation {\n    ImVec2 Pos;\n    ImVec2 Offset;\n    ImU32  ColorBg;\n    ImU32  ColorFg;\n    int    TextOffset;\n    bool   Clamp;\n    ImPlotAnnotation() {\n        ColorBg = ColorFg = 0;\n        TextOffset = 0;\n        Clamp = false;\n    }\n};\n\n// Collection of plot labels\nstruct ImPlotAnnotationCollection {\n\n    ImVector<ImPlotAnnotation> Annotations;\n    ImGuiTextBuffer            TextBuffer;\n    int                        Size;\n\n    ImPlotAnnotationCollection() { Reset(); }\n\n    void AppendV(const ImVec2& pos, const ImVec2& off, ImU32 bg, ImU32 fg, bool clamp, const char* fmt,  va_list args) IM_FMTLIST(7) {\n        ImPlotAnnotation an;\n        an.Pos = pos; an.Offset = off;\n        an.ColorBg = bg; an.ColorFg = fg;\n        an.TextOffset = TextBuffer.size();\n        an.Clamp = clamp;\n        Annotations.push_back(an);\n        TextBuffer.appendfv(fmt, args);\n        const char nul[] = \"\";\n        TextBuffer.append(nul,nul+1);\n        Size++;\n    }\n\n    void Append(const ImVec2& pos, const ImVec2& off, ImU32 bg, ImU32 fg, bool clamp, const char* fmt,  ...) IM_FMTARGS(7) {\n        va_list args;\n        va_start(args, fmt);\n        AppendV(pos, off, bg, fg, clamp, fmt, args);\n        va_end(args);\n    }\n\n    const char* GetText(int idx) {\n        return TextBuffer.Buf.Data + Annotations[idx].TextOffset;\n    }\n\n    void Reset() {\n        Annotations.shrink(0);\n        TextBuffer.Buf.shrink(0);\n        Size = 0;\n    }\n};\n\nstruct ImPlotTag {\n    ImAxis Axis;\n    double Value;\n    ImU32  ColorBg;\n    ImU32  ColorFg;\n    int    TextOffset;\n};\n\nstruct ImPlotTagCollection {\n\n    ImVector<ImPlotTag> Tags;\n    ImGuiTextBuffer     TextBuffer;\n    int                 Size;\n\n    ImPlotTagCollection() { Reset(); }\n\n    void AppendV(ImAxis axis, double value, ImU32 bg, ImU32 fg, const char* fmt, va_list args) IM_FMTLIST(6) {\n        ImPlotTag tag;\n        tag.Axis = axis;\n        tag.Value = value;\n        tag.ColorBg = bg;\n        tag.ColorFg = fg;\n        tag.TextOffset = TextBuffer.size();\n        Tags.push_back(tag);\n        TextBuffer.appendfv(fmt, args);\n        const char nul[] = \"\";\n        TextBuffer.append(nul,nul+1);\n        Size++;\n    }\n\n    void Append(ImAxis axis, double value, ImU32 bg, ImU32 fg, const char* fmt, ...) IM_FMTARGS(6) {\n        va_list args;\n        va_start(args, fmt);\n        AppendV(axis, value, bg, fg, fmt, args);\n        va_end(args);\n    }\n\n    const char* GetText(int idx) {\n        return TextBuffer.Buf.Data + Tags[idx].TextOffset;\n    }\n\n    void Reset() {\n        Tags.shrink(0);\n        TextBuffer.Buf.shrink(0);\n        Size = 0;\n    }\n};\n\n// Tick mark info\nstruct ImPlotTick\n{\n    double PlotPos;\n    float  PixelPos;\n    ImVec2 LabelSize;\n    int    TextOffset;\n    bool   Major;\n    bool   ShowLabel;\n    int    Level;\n    int    Idx;\n\n    ImPlotTick(double value, bool major, int level, bool show_label) {\n        PixelPos     = 0;\n        PlotPos      = value;\n        Major        = major;\n        ShowLabel    = show_label;\n        Level        = level;\n        TextOffset   = -1;\n    }\n};\n\n// Collection of ticks\nstruct ImPlotTicker {\n    ImVector<ImPlotTick> Ticks;\n    ImGuiTextBuffer      TextBuffer;\n    ImVec2               MaxSize;\n    ImVec2               LateSize;\n    int                  Levels;\n\n    ImPlotTicker() {\n        Reset();\n    }\n\n    ImPlotTick& AddTick(double value, bool major, int level, bool show_label, const char* label) {\n        ImPlotTick tick(value, major, level, show_label);\n        if (show_label && label != nullptr) {\n            tick.TextOffset = TextBuffer.size();\n            TextBuffer.append(label, label + strlen(label) + 1);\n            tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset);\n        }\n        return AddTick(tick);\n    }\n\n    ImPlotTick& AddTick(double value, bool major, int level, bool show_label, ImPlotFormatter formatter, void* data) {\n        ImPlotTick tick(value, major, level, show_label);\n        if (show_label && formatter != nullptr) {\n            char buff[IMPLOT_LABEL_MAX_SIZE];\n            tick.TextOffset = TextBuffer.size();\n            formatter(tick.PlotPos, buff, sizeof(buff), data);\n            TextBuffer.append(buff, buff + strlen(buff) + 1);\n            tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset);\n        }\n        return AddTick(tick);\n    }\n\n    inline ImPlotTick& AddTick(ImPlotTick tick) {\n        if (tick.ShowLabel) {\n            MaxSize.x     =  tick.LabelSize.x > MaxSize.x ? tick.LabelSize.x : MaxSize.x;\n            MaxSize.y     =  tick.LabelSize.y > MaxSize.y ? tick.LabelSize.y : MaxSize.y;\n        }\n        tick.Idx = Ticks.size();\n        Ticks.push_back(tick);\n        return Ticks.back();\n    }\n\n    const char* GetText(int idx) const {\n        return TextBuffer.Buf.Data + Ticks[idx].TextOffset;\n    }\n\n    const char* GetText(const ImPlotTick& tick) {\n        return GetText(tick.Idx);\n    }\n\n    void OverrideSizeLate(const ImVec2& size) {\n        LateSize.x = size.x > LateSize.x ? size.x : LateSize.x;\n        LateSize.y = size.y > LateSize.y ? size.y : LateSize.y;\n    }\n\n    void Reset() {\n        Ticks.shrink(0);\n        TextBuffer.Buf.shrink(0);\n        MaxSize = LateSize;\n        LateSize = ImVec2(0,0);\n        Levels = 1;\n    }\n\n    int TickCount() const {\n        return Ticks.Size;\n    }\n};\n\n// Axis state information that must persist after EndPlot\nstruct ImPlotAxis\n{\n    ImGuiID              ID;\n    ImPlotAxisFlags      Flags;\n    ImPlotAxisFlags      PreviousFlags;\n    ImPlotRange          Range;\n    ImPlotCond           RangeCond;\n    ImPlotScale          Scale;\n    ImPlotRange          FitExtents;\n    ImPlotAxis*          OrthoAxis;\n    ImPlotRange          ConstraintRange;\n    ImPlotRange          ConstraintZoom;\n\n    ImPlotTicker         Ticker;\n    ImPlotFormatter      Formatter;\n    void*                FormatterData;\n    char                 FormatSpec[16];\n    ImPlotLocator        Locator;\n\n    double*              LinkedMin;\n    double*              LinkedMax;\n\n    int                  PickerLevel;\n    ImPlotTime           PickerTimeMin, PickerTimeMax;\n\n    ImPlotTransform      TransformForward;\n    ImPlotTransform      TransformInverse;\n    void*                TransformData;\n    float                PixelMin, PixelMax;\n    double               ScaleMin, ScaleMax;\n    double               ScaleToPixel;\n    float                Datum1, Datum2;\n\n    ImRect               HoverRect;\n    int                  LabelOffset;\n    ImU32                ColorMaj, ColorMin, ColorTick, ColorTxt, ColorBg, ColorHov, ColorAct, ColorHiLi;\n\n    bool                 Enabled;\n    bool                 Vertical;\n    bool                 FitThisFrame;\n    bool                 HasRange;\n    bool                 HasFormatSpec;\n    bool                 ShowDefaultTicks;\n    bool                 Hovered;\n    bool                 Held;\n\n    ImPlotAxis() {\n        ID               = 0;\n        Flags            = PreviousFlags = ImPlotAxisFlags_None;\n        Range.Min        = 0;\n        Range.Max        = 1;\n        Scale            = ImPlotScale_Linear;\n        TransformForward = TransformInverse = nullptr;\n        TransformData    = nullptr;\n        FitExtents.Min   = HUGE_VAL;\n        FitExtents.Max   = -HUGE_VAL;\n        OrthoAxis        = nullptr;\n        ConstraintRange  = ImPlotRange(-INFINITY,INFINITY);\n        ConstraintZoom   = ImPlotRange(DBL_MIN,INFINITY);\n        LinkedMin        = LinkedMax = nullptr;\n        PickerLevel      = 0;\n        Datum1           = Datum2 = 0;\n        PixelMin         = PixelMax = 0;\n        LabelOffset      = -1;\n        ColorMaj         = ColorMin = ColorTick = ColorTxt = ColorBg = ColorHov = ColorAct = 0;\n        ColorHiLi        = IM_COL32_BLACK_TRANS;\n        Formatter        = nullptr;\n        FormatterData    = nullptr;\n        Locator          = nullptr;\n        Enabled          = Hovered = Held = FitThisFrame = HasRange = HasFormatSpec = false;\n        ShowDefaultTicks = true;\n    }\n\n    inline void Reset() {\n        Enabled          = false;\n        Scale            = ImPlotScale_Linear;\n        TransformForward = TransformInverse = nullptr;\n        TransformData    = nullptr;\n        LabelOffset      = -1;\n        HasFormatSpec    = false;\n        Formatter        = nullptr;\n        FormatterData    = nullptr;\n        Locator          = nullptr;\n        ShowDefaultTicks = true;\n        FitThisFrame     = false;\n        FitExtents.Min   = HUGE_VAL;\n        FitExtents.Max   = -HUGE_VAL;\n        OrthoAxis        = nullptr;\n        ConstraintRange  = ImPlotRange(-INFINITY,INFINITY);\n        ConstraintZoom   = ImPlotRange(DBL_MIN,INFINITY);\n        Ticker.Reset();\n    }\n\n    inline bool SetMin(double _min, bool force=false) {\n        if (!force && IsLockedMin())\n            return false;\n        _min = ImConstrainNan(ImConstrainInf(_min));\n        if (_min < ConstraintRange.Min)\n            _min = ConstraintRange.Min;\n        double z = Range.Max - _min;\n        if (z < ConstraintZoom.Min)\n            _min = Range.Max - ConstraintZoom.Min;\n        if (z > ConstraintZoom.Max)\n            _min = Range.Max - ConstraintZoom.Max;\n        if (_min >= Range.Max)\n            return false;\n        Range.Min = _min;\n        PickerTimeMin = ImPlotTime::FromDouble(Range.Min);\n        UpdateTransformCache();\n        return true;\n    };\n\n    inline bool SetMax(double _max, bool force=false) {\n        if (!force && IsLockedMax())\n            return false;\n        _max = ImConstrainNan(ImConstrainInf(_max));\n        if (_max > ConstraintRange.Max)\n            _max = ConstraintRange.Max;\n        double z = _max - Range.Min;\n        if (z < ConstraintZoom.Min)\n            _max = Range.Min + ConstraintZoom.Min;\n        if (z > ConstraintZoom.Max)\n            _max = Range.Min + ConstraintZoom.Max;\n        if (_max <= Range.Min)\n            return false;\n        Range.Max = _max;\n        PickerTimeMax = ImPlotTime::FromDouble(Range.Max);\n        UpdateTransformCache();\n        return true;\n    };\n\n    inline void SetRange(double v1, double v2) {\n        Range.Min = ImMin(v1,v2);\n        Range.Max = ImMax(v1,v2);\n        Constrain();\n        PickerTimeMin = ImPlotTime::FromDouble(Range.Min);\n        PickerTimeMax = ImPlotTime::FromDouble(Range.Max);\n        UpdateTransformCache();\n    }\n\n    inline void SetRange(const ImPlotRange& range) {\n        SetRange(range.Min, range.Max);\n    }\n\n    inline void SetAspect(double unit_per_pix) {\n        double new_size = unit_per_pix * PixelSize();\n        double delta    = (new_size - Range.Size()) * 0.5;\n        if (IsLocked())\n            return;\n        else if (IsLockedMin() && !IsLockedMax())\n            SetRange(Range.Min, Range.Max  + 2*delta);\n        else if (!IsLockedMin() && IsLockedMax())\n            SetRange(Range.Min - 2*delta, Range.Max);\n        else\n            SetRange(Range.Min - delta, Range.Max + delta);\n    }\n\n    inline float PixelSize() const { return ImAbs(PixelMax - PixelMin); }\n\n    inline double GetAspect() const { return Range.Size() / PixelSize(); }\n\n    inline void Constrain() {\n        Range.Min = ImConstrainNan(ImConstrainInf(Range.Min));\n        Range.Max = ImConstrainNan(ImConstrainInf(Range.Max));\n        if (Range.Min < ConstraintRange.Min)\n            Range.Min = ConstraintRange.Min;\n        if (Range.Max > ConstraintRange.Max)\n            Range.Max = ConstraintRange.Max;\n        double z = Range.Size();\n        if (z < ConstraintZoom.Min) {\n            double delta = (ConstraintZoom.Min - z) * 0.5;\n            Range.Min -= delta;\n            Range.Max += delta;\n        }\n        if (z > ConstraintZoom.Max) {\n            double delta = (z - ConstraintZoom.Max) * 0.5;\n            Range.Min += delta;\n            Range.Max -= delta;\n        }\n        if (Range.Max <= Range.Min)\n            Range.Max = Range.Min + DBL_EPSILON;\n    }\n\n    inline void UpdateTransformCache() {\n        ScaleToPixel = (PixelMax - PixelMin) / Range.Size();\n        if (TransformForward != nullptr) {\n            ScaleMin = TransformForward(Range.Min, TransformData);\n            ScaleMax = TransformForward(Range.Max, TransformData);\n        }\n        else {\n            ScaleMin = Range.Min;\n            ScaleMax = Range.Max;\n        }\n    }\n\n    inline float PlotToPixels(double plt) const {\n        if (TransformForward != nullptr) {\n            double s = TransformForward(plt, TransformData);\n            double t = (s - ScaleMin) / (ScaleMax - ScaleMin);\n            plt      = Range.Min + Range.Size() * t;\n        }\n        return (float)(PixelMin + ScaleToPixel * (plt - Range.Min));\n    }\n\n\n    inline double PixelsToPlot(float pix) const {\n        double plt = (pix - PixelMin) / ScaleToPixel + Range.Min;\n        if (TransformInverse != nullptr) {\n            double t = (plt - Range.Min) / Range.Size();\n            double s = t * (ScaleMax - ScaleMin) + ScaleMin;\n            plt = TransformInverse(s, TransformData);\n        }\n        return plt;\n    }\n\n    inline void ExtendFit(double v) {\n        if (!ImNanOrInf(v) && v >= ConstraintRange.Min && v <= ConstraintRange.Max) {\n            FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min;\n            FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max;\n        }\n    }\n\n    inline void ExtendFitWith(ImPlotAxis& alt, double v, double v_alt) {\n        if (ImHasFlag(Flags, ImPlotAxisFlags_RangeFit) && !alt.Range.Contains(v_alt))\n            return;\n        if (!ImNanOrInf(v) && v >= ConstraintRange.Min && v <= ConstraintRange.Max) {\n            FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min;\n            FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max;\n        }\n    }\n\n    inline void ApplyFit(float padding) {\n        const double ext_size = FitExtents.Size() * 0.5;\n        FitExtents.Min -= ext_size * padding;\n        FitExtents.Max += ext_size * padding;\n        if (!IsLockedMin() && !ImNanOrInf(FitExtents.Min))\n            Range.Min = FitExtents.Min;\n        if (!IsLockedMax() && !ImNanOrInf(FitExtents.Max))\n            Range.Max = FitExtents.Max;\n        if (ImAlmostEqual(Range.Min, Range.Max))  {\n            Range.Max += 0.5;\n            Range.Min -= 0.5;\n        }\n        Constrain();\n        UpdateTransformCache();\n    }\n\n    inline bool HasLabel()          const { return LabelOffset != -1 && !ImHasFlag(Flags, ImPlotAxisFlags_NoLabel);                          }\n    inline bool HasGridLines()      const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoGridLines);                                           }\n    inline bool HasTickLabels()     const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickLabels);                                          }\n    inline bool HasTickMarks()      const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickMarks);                                           }\n    inline bool WillRender()        const { return Enabled && (HasGridLines() || HasTickLabels() || HasTickMarks());                         }\n    inline bool IsOpposite()        const { return ImHasFlag(Flags, ImPlotAxisFlags_Opposite);                                               }\n    inline bool IsInverted()        const { return ImHasFlag(Flags, ImPlotAxisFlags_Invert);                                                 }\n    inline bool IsForeground()      const { return ImHasFlag(Flags, ImPlotAxisFlags_Foreground);                                             }\n    inline bool IsAutoFitting()     const { return ImHasFlag(Flags, ImPlotAxisFlags_AutoFit);                                                }\n    inline bool CanInitFit()        const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoInitialFit) && !HasRange && !LinkedMin && !LinkedMax; }\n    inline bool IsRangeLocked()     const { return HasRange && RangeCond == ImPlotCond_Always;                                               }\n    inline bool IsLockedMin()       const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMin);                 }\n    inline bool IsLockedMax()       const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMax);                 }\n    inline bool IsLocked()          const { return IsLockedMin() && IsLockedMax();                                                           }\n    inline bool IsInputLockedMin()  const { return IsLockedMin() || IsAutoFitting();                                                         }\n    inline bool IsInputLockedMax()  const { return IsLockedMax() || IsAutoFitting();                                                         }\n    inline bool IsInputLocked()     const { return IsLocked()    || IsAutoFitting();                                                         }\n    inline bool HasMenus()          const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoMenus);                                               }\n\n    inline bool IsPanLocked(bool increasing) {\n        if (ImHasFlag(Flags, ImPlotAxisFlags_PanStretch)) {\n            return IsInputLocked();\n        }\n        else {\n            if (IsLockedMin() || IsLockedMax() || IsAutoFitting())\n                return false;\n            if (increasing)\n                return Range.Max == ConstraintRange.Max;\n            else\n                return Range.Min == ConstraintRange.Min;\n        }\n    }\n\n    void PushLinks() {\n        if (LinkedMin) { *LinkedMin = Range.Min; }\n        if (LinkedMax) { *LinkedMax = Range.Max; }\n    }\n\n    void PullLinks() {\n        if (LinkedMin && LinkedMax) { SetRange(*LinkedMin, *LinkedMax); }\n        else if (LinkedMin) { SetMin(*LinkedMin,true); }\n        else if (LinkedMax) { SetMax(*LinkedMax,true); }\n    }\n};\n\n// Align plots group data\nstruct ImPlotAlignmentData {\n    bool  Vertical;\n    float PadA;\n    float PadB;\n    float PadAMax;\n    float PadBMax;\n    ImPlotAlignmentData() {\n        Vertical    = true;\n        PadA = PadB = PadAMax = PadBMax = 0;\n    }\n    void Begin() { PadAMax = PadBMax = 0; }\n    void Update(float& pad_a, float& pad_b, float& delta_a, float& delta_b) {\n        float bak_a = pad_a; float bak_b = pad_b;\n        if (PadAMax < pad_a) { PadAMax = pad_a; }\n        if (PadBMax < pad_b) { PadBMax = pad_b; }\n        if (pad_a < PadA)    { pad_a = PadA; delta_a = pad_a - bak_a; } else { delta_a = 0; }\n        if (pad_b < PadB)    { pad_b = PadB; delta_b = pad_b - bak_b; } else { delta_b = 0; }\n    }\n    void End()   { PadA = PadAMax; PadB = PadBMax;      }\n    void Reset() { PadA = PadB = PadAMax = PadBMax = 0; }\n};\n\n// State information for Plot items\nstruct ImPlotItem\n{\n    ImGuiID      ID;\n    ImU32        Color;\n    ImRect       LegendHoverRect;\n    int          NameOffset;\n    bool         Show;\n    bool         LegendHovered;\n    bool         SeenThisFrame;\n\n    ImPlotItem() {\n        ID            = 0;\n        Color         = IM_COL32_WHITE;\n        NameOffset    = -1;\n        Show          = true;\n        SeenThisFrame = false;\n        LegendHovered = false;\n    }\n\n    ~ImPlotItem() { ID = 0; }\n};\n\n// Holds Legend state\nstruct ImPlotLegend\n{\n    ImPlotLegendFlags Flags;\n    ImPlotLegendFlags PreviousFlags;\n    ImPlotLocation    Location;\n    ImPlotLocation    PreviousLocation;\n    ImVec2            Scroll;\n    ImVector<int>     Indices;\n    ImGuiTextBuffer   Labels;\n    ImRect            Rect;\n    ImRect            RectClamped;\n    bool              Hovered;\n    bool              Held;\n    bool              CanGoInside;\n\n    ImPlotLegend() {\n        Flags        = PreviousFlags = ImPlotLegendFlags_None;\n        CanGoInside  = true;\n        Hovered      = Held = false;\n        Location     = PreviousLocation = ImPlotLocation_NorthWest;\n        Scroll       = ImVec2(0,0);\n    }\n\n    void Reset() { Indices.shrink(0); Labels.Buf.shrink(0); }\n};\n\n// Holds Items and Legend data\nstruct ImPlotItemGroup\n{\n    ImGuiID            ID;\n    ImPlotLegend       Legend;\n    ImPool<ImPlotItem> ItemPool;\n    int                ColormapIdx;\n\n    ImPlotItemGroup() { ID = 0; ColormapIdx = 0; }\n\n    int         GetItemCount() const             { return ItemPool.GetBufSize();                                 }\n    ImGuiID     GetItemID(const char*  label_id) { return ImGui::GetID(label_id); /* GetIDWithSeed */            }\n    ImPlotItem* GetItem(ImGuiID id)              { return ItemPool.GetByKey(id);                                 }\n    ImPlotItem* GetItem(const char* label_id)    { return GetItem(GetItemID(label_id));                          }\n    ImPlotItem* GetOrAddItem(ImGuiID id)         { return ItemPool.GetOrAddByKey(id);                            }\n    ImPlotItem* GetItemByIndex(int i)            { return ItemPool.GetByIndex(i);                                }\n    int         GetItemIndex(ImPlotItem* item)   { return ItemPool.GetIndex(item);                               }\n    int         GetLegendCount() const           { return Legend.Indices.size();                                 }\n    ImPlotItem* GetLegendItem(int i)             { return ItemPool.GetByIndex(Legend.Indices[i]);                }\n    const char* GetLegendLabel(int i)            { return Legend.Labels.Buf.Data + GetLegendItem(i)->NameOffset; }\n    void        Reset()                          { ItemPool.Clear(); Legend.Reset(); ColormapIdx = 0;            }\n};\n\n// Holds Plot state information that must persist after EndPlot\nstruct ImPlotPlot\n{\n    ImGuiID              ID;\n    ImPlotFlags          Flags;\n    ImPlotFlags          PreviousFlags;\n    ImPlotLocation       MouseTextLocation;\n    ImPlotMouseTextFlags MouseTextFlags;\n    ImPlotAxis           Axes[ImAxis_COUNT];\n    ImGuiTextBuffer      TextBuffer;\n    ImPlotItemGroup      Items;\n    ImAxis               CurrentX;\n    ImAxis               CurrentY;\n    ImRect               FrameRect;\n    ImRect               CanvasRect;\n    ImRect               PlotRect;\n    ImRect               AxesRect;\n    ImRect               SelectRect;\n    ImVec2               SelectStart;\n    int                  TitleOffset;\n    bool                 JustCreated;\n    bool                 Initialized;\n    bool                 SetupLocked;\n    bool                 FitThisFrame;\n    bool                 Hovered;\n    bool                 Held;\n    bool                 Selecting;\n    bool                 Selected;\n    bool                 ContextLocked;\n\n    ImPlotPlot() {\n        Flags             = PreviousFlags = ImPlotFlags_None;\n        for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i)\n            XAxis(i).Vertical = false;\n        for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i)\n            YAxis(i).Vertical = true;\n        SelectStart       = ImVec2(0,0);\n        CurrentX          = ImAxis_X1;\n        CurrentY          = ImAxis_Y1;\n        MouseTextLocation  = ImPlotLocation_South | ImPlotLocation_East;\n        MouseTextFlags     = ImPlotMouseTextFlags_None;\n        TitleOffset       = -1;\n        JustCreated       = true;\n        Initialized = SetupLocked = FitThisFrame = false;\n        Hovered = Held = Selected = Selecting = ContextLocked = false;\n    }\n\n    inline bool IsInputLocked() const {\n        for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {\n            if (!XAxis(i).IsInputLocked())\n                return false;\n        }\n        for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) {\n            if (!YAxis(i).IsInputLocked())\n                return false;\n        }\n        return true;\n    }\n\n    inline void ClearTextBuffer() { TextBuffer.Buf.shrink(0); }\n\n    inline void SetTitle(const char* title) {\n        if (title && ImGui::FindRenderedTextEnd(title, nullptr) != title) {\n            TitleOffset = TextBuffer.size();\n            TextBuffer.append(title, title + strlen(title) + 1);\n        }\n        else {\n            TitleOffset = -1;\n        }\n    }\n    inline bool HasTitle() const { return TitleOffset != -1 && !ImHasFlag(Flags, ImPlotFlags_NoTitle); }\n    inline const char* GetTitle() const { return TextBuffer.Buf.Data + TitleOffset; }\n\n    inline       ImPlotAxis& XAxis(int i)       { return Axes[ImAxis_X1 + i]; }\n    inline const ImPlotAxis& XAxis(int i) const { return Axes[ImAxis_X1 + i]; }\n    inline       ImPlotAxis& YAxis(int i)       { return Axes[ImAxis_Y1 + i]; }\n    inline const ImPlotAxis& YAxis(int i) const { return Axes[ImAxis_Y1 + i]; }\n\n    inline int EnabledAxesX() {\n        int cnt = 0;\n        for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i)\n            cnt += XAxis(i).Enabled;\n        return cnt;\n    }\n\n    inline int EnabledAxesY() {\n        int cnt = 0;\n        for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i)\n            cnt += YAxis(i).Enabled;\n        return cnt;\n    }\n\n    inline void SetAxisLabel(ImPlotAxis& axis, const char* label) {\n        if (label && ImGui::FindRenderedTextEnd(label, nullptr) != label) {\n            axis.LabelOffset = TextBuffer.size();\n            TextBuffer.append(label, label + strlen(label) + 1);\n        }\n        else {\n            axis.LabelOffset = -1;\n        }\n    }\n\n    inline const char* GetAxisLabel(const ImPlotAxis& axis) const { return TextBuffer.Buf.Data + axis.LabelOffset; }\n};\n\n// Holds subplot data that must persist after EndSubplot\nstruct ImPlotSubplot {\n    ImGuiID                       ID;\n    ImPlotSubplotFlags            Flags;\n    ImPlotSubplotFlags            PreviousFlags;\n    ImPlotItemGroup               Items;\n    int                           Rows;\n    int                           Cols;\n    int                           CurrentIdx;\n    ImRect                        FrameRect;\n    ImRect                        GridRect;\n    ImVec2                        CellSize;\n    ImVector<ImPlotAlignmentData> RowAlignmentData;\n    ImVector<ImPlotAlignmentData> ColAlignmentData;\n    ImVector<float>               RowRatios;\n    ImVector<float>               ColRatios;\n    ImVector<ImPlotRange>         RowLinkData;\n    ImVector<ImPlotRange>         ColLinkData;\n    float                         TempSizes[2];\n    bool                          FrameHovered;\n    bool                          HasTitle;\n\n    ImPlotSubplot() {\n        ID                          = 0;\n        Flags = PreviousFlags       = ImPlotSubplotFlags_None;\n        Rows = Cols = CurrentIdx    = 0;\n        Items.Legend.Location       = ImPlotLocation_North;\n        Items.Legend.Flags          = ImPlotLegendFlags_Horizontal|ImPlotLegendFlags_Outside;\n        Items.Legend.CanGoInside    = false;\n        TempSizes[0] = TempSizes[1] = 0;\n        FrameHovered                = false;\n        HasTitle                    = false;\n    }\n};\n\n// Temporary data storage for upcoming plot\nstruct ImPlotNextPlotData\n{\n    ImPlotCond  RangeCond[ImAxis_COUNT];\n    ImPlotRange Range[ImAxis_COUNT];\n    bool        HasRange[ImAxis_COUNT];\n    bool        Fit[ImAxis_COUNT];\n    double*     LinkedMin[ImAxis_COUNT];\n    double*     LinkedMax[ImAxis_COUNT];\n\n    ImPlotNextPlotData() { Reset(); }\n\n    void Reset() {\n        for (int i = 0; i < ImAxis_COUNT; ++i) {\n            HasRange[i]                 = false;\n            Fit[i]                      = false;\n            LinkedMin[i] = LinkedMax[i] = nullptr;\n        }\n    }\n\n};\n\n// Temporary data storage for upcoming item\nstruct ImPlotNextItemData {\n    ImVec4          Colors[5]; // ImPlotCol_Line, ImPlotCol_Fill, ImPlotCol_MarkerOutline, ImPlotCol_MarkerFill, ImPlotCol_ErrorBar\n    float           LineWeight;\n    ImPlotMarker    Marker;\n    float           MarkerSize;\n    float           MarkerWeight;\n    float           FillAlpha;\n    float           ErrorBarSize;\n    float           ErrorBarWeight;\n    float           DigitalBitHeight;\n    float           DigitalBitGap;\n    bool            RenderLine;\n    bool            RenderFill;\n    bool            RenderMarkerLine;\n    bool            RenderMarkerFill;\n    bool            HasHidden;\n    bool            Hidden;\n    ImPlotCond      HiddenCond;\n    ImPlotNextItemData() { Reset(); }\n    void Reset() {\n        for (int i = 0; i < 5; ++i)\n            Colors[i] = IMPLOT_AUTO_COL;\n        LineWeight    = MarkerSize = MarkerWeight = FillAlpha = ErrorBarSize = ErrorBarWeight = DigitalBitHeight = DigitalBitGap = IMPLOT_AUTO;\n        Marker        = IMPLOT_AUTO;\n        HasHidden     = Hidden = false;\n    }\n};\n\n// Holds state information that must persist between calls to BeginPlot()/EndPlot()\nstruct ImPlotContext {\n    // Plot States\n    ImPool<ImPlotPlot>    Plots;\n    ImPool<ImPlotSubplot> Subplots;\n    ImPlotPlot*           CurrentPlot;\n    ImPlotSubplot*        CurrentSubplot;\n    ImPlotItemGroup*      CurrentItems;\n    ImPlotItem*           CurrentItem;\n    ImPlotItem*           PreviousItem;\n\n    // Tick Marks and Labels\n    ImPlotTicker CTicker;\n\n    // Annotation and Tabs\n    ImPlotAnnotationCollection Annotations;\n    ImPlotTagCollection        Tags;\n\n    // Style and Colormaps\n    ImPlotStyle                 Style;\n    ImVector<ImGuiColorMod>     ColorModifiers;\n    ImVector<ImGuiStyleMod>     StyleModifiers;\n    ImPlotColormapData          ColormapData;\n    ImVector<ImPlotColormap>    ColormapModifiers;\n\n    // Time\n    tm Tm;\n\n    // Temp data for general use\n    ImVector<double>   TempDouble1, TempDouble2;\n    ImVector<int>      TempInt1;\n\n    // Misc\n    int                DigitalPlotItemCnt;\n    int                DigitalPlotOffset;\n    ImPlotNextPlotData NextPlotData;\n    ImPlotNextItemData NextItemData;\n    ImPlotInputMap     InputMap;\n    bool               OpenContextThisFrame;\n    ImGuiTextBuffer    MousePosStringBuilder;\n    ImPlotItemGroup*   SortItems;\n\n    // Align plots\n    ImPool<ImPlotAlignmentData> AlignmentData;\n    ImPlotAlignmentData*        CurrentAlignmentH;\n    ImPlotAlignmentData*        CurrentAlignmentV;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Internal API\n// No guarantee of forward compatibility here!\n//-----------------------------------------------------------------------------\n\nnamespace ImPlot {\n\n//-----------------------------------------------------------------------------\n// [SECTION] Context Utils\n//-----------------------------------------------------------------------------\n\n// Initializes an ImPlotContext\nIMPLOT_API void Initialize(ImPlotContext* ctx);\n// Resets an ImPlot context for the next call to BeginPlot\nIMPLOT_API void ResetCtxForNextPlot(ImPlotContext* ctx);\n// Resets an ImPlot context for the next call to BeginAlignedPlots\nIMPLOT_API void ResetCtxForNextAlignedPlots(ImPlotContext* ctx);\n// Resets an ImPlot context for the next call to BeginSubplot\nIMPLOT_API void ResetCtxForNextSubplot(ImPlotContext* ctx);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot Utils\n//-----------------------------------------------------------------------------\n\n// Gets a plot from the current ImPlotContext\nIMPLOT_API ImPlotPlot* GetPlot(const char* title);\n// Gets the current plot from the current ImPlotContext\nIMPLOT_API ImPlotPlot* GetCurrentPlot();\n// Busts the cache for every plot in the current context\nIMPLOT_API void BustPlotCache();\n\n// Shows a plot's context menu.\nIMPLOT_API void ShowPlotContextMenu(ImPlotPlot& plot);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Setup Utils\n//-----------------------------------------------------------------------------\n\n// Lock Setup and call SetupFinish if necessary.\nstatic inline void SetupLock() {\n    ImPlotContext& gp = *GImPlot;\n    if (!gp.CurrentPlot->SetupLocked)\n        SetupFinish();\n    gp.CurrentPlot->SetupLocked = true;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Subplot Utils\n//-----------------------------------------------------------------------------\n\n// Advances to next subplot\nIMPLOT_API void SubplotNextCell();\n\n// Shows a subplot's context menu.\nIMPLOT_API void ShowSubplotsContextMenu(ImPlotSubplot& subplot);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Item Utils\n//-----------------------------------------------------------------------------\n\n// Begins a new item. Returns false if the item should not be plotted. Pushes PlotClipRect.\nIMPLOT_API bool BeginItem(const char* label_id, ImPlotItemFlags flags=0, ImPlotCol recolor_from=IMPLOT_AUTO);\n\n// Same as above but with fitting functionality.\ntemplate <typename _Fitter>\nbool BeginItemEx(const char* label_id, const _Fitter& fitter, ImPlotItemFlags flags=0, ImPlotCol recolor_from=IMPLOT_AUTO) {\n    if (BeginItem(label_id, flags, recolor_from)) {\n        ImPlotPlot& plot = *GetCurrentPlot();\n        if (plot.FitThisFrame && !ImHasFlag(flags, ImPlotItemFlags_NoFit))\n            fitter.Fit(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]);\n        return true;\n    }\n    return false;\n}\n\n// Ends an item (call only if BeginItem returns true). Pops PlotClipRect.\nIMPLOT_API void EndItem();\n\n// Register or get an existing item from the current plot.\nIMPLOT_API ImPlotItem* RegisterOrGetItem(const char* label_id, ImPlotItemFlags flags, bool* just_created = nullptr);\n// Get a plot item from the current plot.\nIMPLOT_API ImPlotItem* GetItem(const char* label_id);\n// Gets the current item.\nIMPLOT_API ImPlotItem* GetCurrentItem();\n// Busts the cache for every item for every plot in the current context.\nIMPLOT_API void BustItemCache();\n\n//-----------------------------------------------------------------------------\n// [SECTION] Axis Utils\n//-----------------------------------------------------------------------------\n\n// Returns true if any enabled axis is locked from user input.\nstatic inline bool AnyAxesInputLocked(ImPlotAxis* axes, int count) {\n    for (int i = 0; i < count; ++i) {\n        if (axes[i].Enabled && axes[i].IsInputLocked())\n            return true;\n    }\n    return false;\n}\n\n// Returns true if all enabled axes are locked from user input.\nstatic inline bool AllAxesInputLocked(ImPlotAxis* axes, int count) {\n    for (int i = 0; i < count; ++i) {\n        if (axes[i].Enabled && !axes[i].IsInputLocked())\n            return false;\n    }\n    return true;\n}\n\nstatic inline bool AnyAxesHeld(ImPlotAxis* axes, int count) {\n    for (int i = 0; i < count; ++i) {\n        if (axes[i].Enabled && axes[i].Held)\n            return true;\n    }\n    return false;\n}\n\nstatic inline bool AnyAxesHovered(ImPlotAxis* axes, int count) {\n    for (int i = 0; i < count; ++i) {\n        if (axes[i].Enabled && axes[i].Hovered)\n            return true;\n    }\n    return false;\n}\n\n// Returns true if the user has requested data to be fit.\nstatic inline bool FitThisFrame() {\n    return GImPlot->CurrentPlot->FitThisFrame;\n}\n\n// Extends the current plot's axes so that it encompasses a vertical line at x\nstatic inline void FitPointX(double x) {\n    ImPlotPlot& plot   = *GetCurrentPlot();\n    ImPlotAxis& x_axis = plot.Axes[plot.CurrentX];\n    x_axis.ExtendFit(x);\n}\n\n// Extends the current plot's axes so that it encompasses a horizontal line at y\nstatic inline void FitPointY(double y) {\n    ImPlotPlot& plot   = *GetCurrentPlot();\n    ImPlotAxis& y_axis = plot.Axes[plot.CurrentY];\n    y_axis.ExtendFit(y);\n}\n\n// Extends the current plot's axes so that it encompasses point p\nstatic inline void FitPoint(const ImPlotPoint& p) {\n    ImPlotPlot& plot   = *GetCurrentPlot();\n    ImPlotAxis& x_axis = plot.Axes[plot.CurrentX];\n    ImPlotAxis& y_axis = plot.Axes[plot.CurrentY];\n    x_axis.ExtendFitWith(y_axis, p.x, p.y);\n    y_axis.ExtendFitWith(x_axis, p.y, p.x);\n}\n\n// Returns true if two ranges overlap\nstatic inline bool RangesOverlap(const ImPlotRange& r1, const ImPlotRange& r2)\n{ return r1.Min <= r2.Max && r2.Min <= r1.Max; }\n\n// Shows an axis's context menu.\nIMPLOT_API void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_allowed = false);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Legend Utils\n//-----------------------------------------------------------------------------\n\n// Gets the position of an inner rect that is located inside of an outer rect according to an ImPlotLocation and padding amount.\nIMPLOT_API ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation location, const ImVec2& pad = ImVec2(0,0));\n// Calculates the bounding box size of a legend _before_ clipping.\nIMPLOT_API ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical);\n// Clips calculated legend size\nIMPLOT_API bool ClampLegendRect(ImRect& legend_rect, const ImRect& outer_rect, const ImVec2& pad);      \n// Renders legend entries into a bounding box\nIMPLOT_API bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList);\n// Shows an alternate legend for the plot identified by #title_id, outside of the plot frame (can be called before or after of Begin/EndPlot but must occur in the same ImGui window! This is not thoroughly tested nor scrollable!).\nIMPLOT_API void ShowAltLegend(const char* title_id, bool vertical = true, const ImVec2 size = ImVec2(0,0), bool interactable = true);\n// Shows a legend's context menu.\nIMPLOT_API bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Label Utils\n//-----------------------------------------------------------------------------\n\n// Create a a string label for a an axis value\nIMPLOT_API void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round = false);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Styling Utils\n//-----------------------------------------------------------------------------\n\n// Get styling data for next item (call between Begin/EndItem)\nstatic inline const ImPlotNextItemData& GetItemData() { return GImPlot->NextItemData; }\n\n// Returns true if a color is set to be automatically determined\nstatic inline bool IsColorAuto(const ImVec4& col) { return col.w == -1; }\n// Returns true if a style color is set to be automatically determined\nstatic inline bool IsColorAuto(ImPlotCol idx) { return IsColorAuto(GImPlot->Style.Colors[idx]); }\n// Returns the automatically deduced style color\nIMPLOT_API ImVec4 GetAutoColor(ImPlotCol idx);\n\n// Returns the style color whether it is automatic or custom set\nstatic inline ImVec4 GetStyleColorVec4(ImPlotCol idx) { return IsColorAuto(idx) ? GetAutoColor(idx) : GImPlot->Style.Colors[idx]; }\nstatic inline ImU32  GetStyleColorU32(ImPlotCol idx)  { return ImGui::ColorConvertFloat4ToU32(GetStyleColorVec4(idx)); }\n\n// Draws vertical text. The position is the bottom left of the text rect.\nIMPLOT_API void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char* text_begin, const char* text_end = nullptr);\n// Draws multiline horizontal text centered.\nIMPLOT_API void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end = nullptr);\n// Calculates the size of vertical text\nstatic inline ImVec2 CalcTextSizeVertical(const char *text) {\n    ImVec2 sz = ImGui::CalcTextSize(text);\n    return ImVec2(sz.y, sz.x);\n}\n// Returns white or black text given background color\nstatic inline ImU32 CalcTextColor(const ImVec4& bg) { return (bg.x * 0.299f + bg.y * 0.587f + bg.z * 0.114f) > 0.5f ? IM_COL32_BLACK : IM_COL32_WHITE; }\nstatic inline ImU32 CalcTextColor(ImU32 bg)         { return CalcTextColor(ImGui::ColorConvertU32ToFloat4(bg)); }\n// Lightens or darkens a color for hover\nstatic inline ImU32 CalcHoverColor(ImU32 col)       {  return ImMixU32(col, CalcTextColor(col), 32); }\n\n// Clamps a label position so that it fits a rect defined by Min/Max\nstatic inline ImVec2 ClampLabelPos(ImVec2 pos, const ImVec2& size, const ImVec2& Min, const ImVec2& Max) {\n    if (pos.x < Min.x)              pos.x = Min.x;\n    if (pos.y < Min.y)              pos.y = Min.y;\n    if ((pos.x + size.x) > Max.x)   pos.x = Max.x - size.x;\n    if ((pos.y + size.y) > Max.y)   pos.y = Max.y - size.y;\n    return pos;\n}\n\n// Returns a color from the Color map given an index >= 0 (modulo will be performed).\nIMPLOT_API ImU32  GetColormapColorU32(int idx, ImPlotColormap cmap);\n// Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired.\nIMPLOT_API ImU32  NextColormapColorU32();\n// Linearly interpolates a color from the current colormap given t between 0 and 1.\nIMPLOT_API ImU32  SampleColormapU32(float t, ImPlotColormap cmap);\n\n// Render a colormap bar\nIMPLOT_API void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Math and Misc Utils\n//-----------------------------------------------------------------------------\n\n// Rounds x to powers of 2,5 and 10 for generating axis labels (from Graphics Gems 1 Chapter 11.2)\nIMPLOT_API double NiceNum(double x, bool round);\n// Computes order of magnitude of double.\nstatic inline int OrderOfMagnitude(double val) { return val == 0 ? 0 : (int)(floor(log10(fabs(val)))); }\n// Returns the precision required for a order of magnitude.\nstatic inline int OrderToPrecision(int order) { return order > 0 ? 0 : 1 - order; }\n// Returns a floating point precision to use given a value\nstatic inline int Precision(double val) { return OrderToPrecision(OrderOfMagnitude(val)); }\n// Round a value to a given precision\nstatic inline double RoundTo(double val, int prec) { double p = pow(10,(double)prec); return floor(val*p+0.5)/p; }\n\n// Returns the intersection point of two lines A and B (assumes they are not parallel!)\nstatic inline ImVec2 Intersection(const ImVec2& a1, const ImVec2& a2, const ImVec2& b1, const ImVec2& b2) {\n    float v1 = (a1.x * a2.y - a1.y * a2.x);  float v2 = (b1.x * b2.y - b1.y * b2.x);\n    float v3 = ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x));\n    return ImVec2((v1 * (b1.x - b2.x) - v2 * (a1.x - a2.x)) / v3, (v1 * (b1.y - b2.y) - v2 * (a1.y - a2.y)) / v3);\n}\n\n// Fills a buffer with n samples linear interpolated from vmin to vmax\ntemplate <typename T>\nvoid FillRange(ImVector<T>& buffer, int n, T vmin, T vmax) {\n    buffer.resize(n);\n    T step = (vmax - vmin) / (n - 1);\n    for (int i = 0; i < n; ++i) {\n        buffer[i] = vmin + i * step;\n    }\n}\n\n// Calculate histogram bin counts and widths\ntemplate <typename T>\nstatic inline void CalculateBins(const T* values, int count, ImPlotBin meth, const ImPlotRange& range, int& bins_out, double& width_out) {\n    switch (meth) {\n        case ImPlotBin_Sqrt:\n            bins_out  = (int)ceil(sqrt(count));\n            break;\n        case ImPlotBin_Sturges:\n            bins_out  = (int)ceil(1.0 + log2(count));\n            break;\n        case ImPlotBin_Rice:\n            bins_out  = (int)ceil(2 * cbrt(count));\n            break;\n        case ImPlotBin_Scott:\n            width_out = 3.49 * ImStdDev(values, count) / cbrt(count);\n            bins_out  = (int)round(range.Size() / width_out);\n            break;\n    }\n    width_out = range.Size() / bins_out;\n}\n\n//-----------------------------------------------------------------------------\n// Time Utils\n//-----------------------------------------------------------------------------\n\n// Returns true if year is leap year (366 days long)\nstatic inline bool IsLeapYear(int year) {\n    return  year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);\n}\n// Returns the number of days in a month, accounting for Feb. leap years. #month is zero indexed.\nstatic inline int GetDaysInMonth(int year, int month) {\n    static const int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};\n    return  days[month] + (int)(month == 1 && IsLeapYear(year));\n}\n\n// Make a UNIX timestamp from a tm struct expressed in UTC time (i.e. GMT timezone).\nIMPLOT_API ImPlotTime MkGmtTime(struct tm *ptm);\n// Make a tm struct expressed in UTC time (i.e. GMT timezone) from a UNIX timestamp.\nIMPLOT_API tm* GetGmtTime(const ImPlotTime& t, tm* ptm);\n\n// Make a UNIX timestamp from a tm struct expressed in local time.\nIMPLOT_API ImPlotTime MkLocTime(struct tm *ptm);\n// Make a tm struct expressed in local time from a UNIX timestamp.\nIMPLOT_API tm* GetLocTime(const ImPlotTime& t, tm* ptm);\n\n// NB: The following functions only work if there is a current ImPlotContext because the\n// internal tm struct is owned by the context! They are aware of ImPlotStyle.UseLocalTime.\n\n// Make a timestamp from time components.\n// year[1970-3000], month[0-11], day[1-31], hour[0-23], min[0-59], sec[0-59], us[0,999999]\nIMPLOT_API ImPlotTime MakeTime(int year, int month = 0, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0);\n// Get year component from timestamp [1970-3000]\nIMPLOT_API int GetYear(const ImPlotTime& t);\n\n// Adds or subtracts time from a timestamp. #count > 0 to add, < 0 to subtract.\nIMPLOT_API ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count);\n// Rounds a timestamp down to nearest unit.\nIMPLOT_API ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit);\n// Rounds a timestamp up to the nearest unit.\nIMPLOT_API ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit);\n// Rounds a timestamp up or down to the nearest unit.\nIMPLOT_API ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit);\n// Combines the date of one timestamp with the time-of-day of another timestamp.\nIMPLOT_API ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& time_part);\n\n// Formats the time part of timestamp t into a buffer according to #fmt\nIMPLOT_API int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, bool use_24_hr_clk);\n// Formats the date part of timestamp t into a buffer according to #fmt\nIMPLOT_API int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, bool use_iso_8601);\n// Formats the time and/or date parts of a timestamp t into a buffer according to #fmt\nIMPLOT_API int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt);\n\n// Shows a date picker widget block (year/month/day).\n// #level = 0 for day, 1 for month, 2 for year. Modified by user interaction.\n// #t will be set when a day is clicked and the function will return true.\n// #t1 and #t2 are optional dates to highlight.\nIMPLOT_API bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1 = nullptr, const ImPlotTime* t2 = nullptr);\n// Shows a time picker widget block (hour/min/sec).\n// #t will be set when a new hour, minute, or sec is selected or am/pm is toggled, and the function will return true.\nIMPLOT_API bool ShowTimePicker(const char* id, ImPlotTime* t);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Transforms\n//-----------------------------------------------------------------------------\n\nstatic inline double TransformForward_Log10(double v, void*) {\n    v = v <= 0.0 ? DBL_MIN : v;\n    return ImLog10(v);\n}\n\nstatic inline double TransformInverse_Log10(double v, void*) {\n    return ImPow(10, v);\n}\n\nstatic inline double TransformForward_SymLog(double v, void*) {\n    return 2.0 * ImAsinh(v / 2.0);\n}\n\nstatic inline double TransformInverse_SymLog(double v, void*) {\n    return 2.0 * ImSinh(v / 2.0);\n}\n\nstatic inline double TransformForward_Logit(double v, void*) {\n    v = ImClamp(v, DBL_MIN, 1.0 - DBL_EPSILON);\n    return ImLog10(v / (1 - v));\n}\n\nstatic inline double TransformInverse_Logit(double v, void*) {\n    return 1.0 / (1.0 + ImPow(10,-v));\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Formatters\n//-----------------------------------------------------------------------------\n\nstatic inline int Formatter_Default(double value, char* buff, int size, void* data) {\n    char* fmt = (char*)data;\n    return ImFormatString(buff, size, fmt, value);\n}\n\nstatic inline int Formatter_Logit(double value, char* buff, int size, void*) {\n    if (value == 0.5)\n        return ImFormatString(buff,size,\"1/2\");\n    else if (value < 0.5)\n        return ImFormatString(buff,size,\"%g\", value);\n    else\n        return ImFormatString(buff,size,\"1 - %g\", 1 - value);\n}\n\nstruct Formatter_Time_Data {\n    ImPlotTime Time;\n    ImPlotDateTimeSpec Spec;\n    ImPlotFormatter UserFormatter;\n    void* UserFormatterData;\n};\n\nstatic inline int Formatter_Time(double, char* buff, int size, void* data) {\n    Formatter_Time_Data* ftd = (Formatter_Time_Data*)data;\n    return FormatDateTime(ftd->Time, buff, size, ftd->Spec);\n}\n\n//------------------------------------------------------------------------------\n// [SECTION] Locator\n//------------------------------------------------------------------------------\n\nvoid Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data);\nvoid Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data);\nvoid Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data);\nvoid Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data);\n\n} // namespace ImPlot\n"
  },
  {
    "path": "lib/third_party/imgui/implot/source/implot.cpp",
    "content": "// MIT License\n\n// Copyright (c) 2023 Evan Pezent\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n// ImPlot v0.17\n\n/*\n\nAPI BREAKING CHANGES\n====================\nOccasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.\nBelow is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code.\nWhen you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files.\nYou can read releases logs https://github.com/epezent/implot/releases for more details.\n\n- 2023/08/20 (0.17) - ImPlotFlags_NoChild was removed as child windows are no longer needed to capture scroll. You can safely remove this flag if you were using it. \n- 2023/06/26 (0.15) - Various build fixes related to updates in Dear ImGui internals.\n- 2022/11/25 (0.15) - Make PlotText honor ImPlotItemFlags_NoFit.\n- 2022/06/19 (0.14) - The signature of ColormapScale has changed to accommodate a new ImPlotColormapScaleFlags parameter\n- 2022/06/17 (0.14) - **IMPORTANT** All PlotX functions now take an ImPlotX_Flags `flags` parameter. Where applicable, it is located before the existing `offset` and `stride` parameters.\n                      If you were providing offset and stride values, you will need to update your function call to include a `flags` value. If you fail to do this, you will likely see\n                      unexpected results or crashes without a compiler warning since these three are all default args. We apologize for the inconvenience, but this was a necessary evil.\n                    - PlotBarsH has been removed; use PlotBars + ImPlotBarsFlags_Horizontal instead\n                    - PlotErrorBarsH has been removed; use PlotErrorBars + ImPlotErrorBarsFlags_Horizontal\n                    - PlotHistogram/PlotHistogram2D signatures changed; `cumulative`, `density`, and `outliers` options now specified via ImPlotHistogramFlags\n                    - PlotPieChart signature changed; `normalize` option now specified via ImPlotPieChartFlags\n                    - PlotText signature changes; `vertical` option now specified via `ImPlotTextFlags_Vertical`\n                    - `PlotVLines` and `PlotHLines` replaced with `PlotInfLines` (+ ImPlotInfLinesFlags_Horizontal )\n                    - arguments of ImPlotGetter have been reversed to be consistent with other API callbacks\n                    - SetupAxisScale + ImPlotScale have replaced ImPlotAxisFlags_LogScale and ImPlotAxisFlags_Time flags\n                    - ImPlotFormatters should now return an int indicating the size written\n                    - the signature of ImPlotGetter has been reversed so that void* user_data is the last argument and consistent with other callbacks\n- 2021/10/19 (0.13) - MAJOR API OVERHAUL! See #168 and #272\n                    - TRIVIAL RENAME:\n                      - ImPlotLimits                              -> ImPlotRect\n                      - ImPlotYAxis_                              -> ImAxis_\n                      - SetPlotYAxis                              -> SetAxis\n                      - BeginDragDropTarget                       -> BeginDragDropTargetPlot\n                      - BeginDragDropSource                       -> BeginDragDropSourcePlot\n                      - ImPlotFlags_NoMousePos                    -> ImPlotFlags_NoMouseText\n                      - SetNextPlotLimits                         -> SetNextAxesLimits\n                      - SetMouseTextLocation                      -> SetupMouseText\n                    - SIGNATURE MODIFIED:\n                      - PixelsToPlot/PlotToPixels                 -> added optional X-Axis arg\n                      - GetPlotMousePos                           -> added optional X-Axis arg\n                      - GetPlotLimits                             -> added optional X-Axis arg\n                      - GetPlotSelection                          -> added optional X-Axis arg\n                      - DragLineX/Y/DragPoint                     -> now takes int id; removed labels (render with Annotation/Tag instead)\n                    - REPLACED:\n                      - IsPlotXAxisHovered/IsPlotXYAxisHovered    -> IsAxisHovered(ImAxis)\n                      - BeginDragDropTargetX/BeginDragDropTargetY -> BeginDragDropTargetAxis(ImAxis)\n                      - BeginDragDropSourceX/BeginDragDropSourceY -> BeginDragDropSourceAxis(ImAxis)\n                      - ImPlotCol_XAxis, ImPlotCol_YAxis1, etc.   -> ImPlotCol_AxisText (push/pop this around SetupAxis to style individual axes)\n                      - ImPlotCol_XAxisGrid, ImPlotCol_Y1AxisGrid -> ImPlotCol_AxisGrid (push/pop this around SetupAxis to style individual axes)\n                      - SetNextPlotLimitsX/Y                      -> SetNextAxisLimits(ImAxis)\n                      - LinkNextPlotLimits                        -> SetNextAxisLinks(ImAxis)\n                      - FitNextPlotAxes                           -> SetNextAxisToFit(ImAxis)/SetNextAxesToFit\n                      - SetLegendLocation                         -> SetupLegend\n                      - ImPlotFlags_NoHighlight                   -> ImPlotLegendFlags_NoHighlight\n                      - ImPlotOrientation                         -> ImPlotLegendFlags_Horizontal\n                      - Annotate                                  -> Annotation\n                    - REMOVED:\n                      - GetPlotQuery, SetPlotQuery, IsPlotQueried -> use DragRect\n                      - SetNextPlotTicksX, SetNextPlotTicksY      -> use SetupAxisTicks\n                      - SetNextPlotFormatX, SetNextPlotFormatY    -> use SetupAxisFormat\n                      - AnnotateClamped                           -> use Annotation(bool clamp = true)\n                    - OBSOLETED:\n                      - BeginPlot (original signature)            -> use simplified signature + Setup API\n- 2021/07/30 (0.12) - The offset argument of `PlotXG` functions was been removed. Implement offsetting in your getter callback instead.\n- 2021/03/08 (0.9)  - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. LerpColormap was changed to SampleColormap.\n                      ShowColormapScale was changed to ColormapScale and requires additional arguments.\n- 2021/03/07 (0.9)  - The signature of ShowColormapScale was modified to accept a ImVec2 size.\n- 2021/02/28 (0.9)  - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements.\n- 2021/01/18 (0.9)  - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved\n                      to implot_internal.h due to its immaturity.\n- 2020/10/16 (0.8)  - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding\n- 2020/09/10 (0.8)  - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0.\n- 2020/09/07 (0.8)  - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG)\n- 2020/09/06 (0.7)  - Several flags under ImPlotFlags and ImPlotAxisFlags were inverted (e.g. ImPlotFlags_Legend -> ImPlotFlags_NoLegend) so that the default flagset\n                      is simply 0. This more closely matches ImGui's style and makes it easier to enable non-default but commonly used flags (e.g. ImPlotAxisFlags_Time).\n- 2020/08/28 (0.5)  - ImPlotMarker_ can no longer be combined with bitwise OR, |. This features caused unecessary slow-down, and almost no one used it.\n- 2020/08/25 (0.5)  - ImPlotAxisFlags_Scientific was removed. Logarithmic axes automatically uses scientific notation.\n- 2020/08/17 (0.5)  - PlotText was changed so that text is centered horizontally and vertically about the desired point.\n- 2020/08/16 (0.5)  - An ImPlotContext must be explicitly created and destroyed now with `CreateContext` and `DestroyContext`. Previously, the context was statically initialized in this source file.\n- 2020/06/13 (0.4)  - The flags `ImPlotAxisFlag_Adaptive` and `ImPlotFlags_Cull` were removed. Both are now done internally by default.\n- 2020/06/03 (0.3)  - The signature and behavior of PlotPieChart was changed so that data with sum less than 1 can optionally be normalized. The label format can now be specified as well.\n- 2020/06/01 (0.3)  - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`.\n- 2020/05/31 (0.3)  - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead.\n- 2020/05/29 (0.3)  - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2\n- 2020/05/16 (0.2)  - All plotting functions were reverted to being prefixed with \"Plot\" to maintain a consistent VerbNoun style. `Plot` was split into `PlotLine`\n                      and `PlotScatter` (however, `PlotLine` can still be used to plot scatter points as `Plot` did before.). `Bar` is not `PlotBars`, to indicate\n                      that multiple bars will be plotted.\n- 2020/05/13 (0.2)  - `ImMarker` was change to `ImPlotMarker` and `ImAxisFlags` was changed to `ImPlotAxisFlags`.\n- 2020/05/11 (0.2)  - `ImPlotFlags_Selection` was changed to `ImPlotFlags_BoxSelect`\n- 2020/05/11 (0.2)  - The namespace ImGui:: was replaced with ImPlot::. As a result, the following additional changes were made:\n                      - Functions that were prefixed or decorated with the word \"Plot\" have been truncated. E.g., `ImGui::PlotBars` is now just `ImPlot::Bar`.\n                        It should be fairly obvious what was what.\n                      - Some functions have been given names that would have otherwise collided with the ImGui namespace. This has been done to maintain a consistent\n                        style with ImGui. E.g., 'ImGui::PushPlotStyleVar` is now 'ImPlot::PushStyleVar'.\n- 2020/05/10 (0.2)  - The following function/struct names were changes:\n                     - ImPlotRange       -> ImPlotLimits\n                     - GetPlotRange()    -> GetPlotLimits()\n                     - SetNextPlotRange  -> SetNextPlotLimits\n                     - SetNextPlotRangeX -> SetNextPlotLimitsX\n                     - SetNextPlotRangeY -> SetNextPlotLimitsY\n- 2020/05/10 (0.2)  - Plot queries are pixel based by default. Query rects that maintain relative plot position have been removed. This was done to support multi-y-axis.\n\n*/\n\n#define IMGUI_DEFINE_MATH_OPERATORS\n#include \"implot.h\"\n#include \"implot_internal.h\"\n\n#include <stdlib.h>\n\n// Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean \"all corners\" but in order to support older versions we are more explicit.\n#if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll)\n#define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All\n#endif\n\n// Support for pre-1.89.7 versions.\n#if (IMGUI_VERSION_NUM < 18966)\n#define ImGuiButtonFlags_AllowOverlap ImGuiButtonFlags_AllowItemOverlap\n#endif\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#pragma clang diagnostic ignored \"-Wformat-nonliteral\"  // warning: format string is not a string literal\n#elif defined(__GNUC__)\n#pragma GCC diagnostic ignored \"-Wformat-nonliteral\"    // warning: format not a string literal, format string not checked\n#endif\n\n// Global plot context\n#ifndef GImPlot\nImPlotContext* GImPlot = nullptr;\n#endif\n\n//-----------------------------------------------------------------------------\n// Struct Implementations\n//-----------------------------------------------------------------------------\n\nImPlotInputMap::ImPlotInputMap() {\n    ImPlot::MapInputDefault(this);\n}\n\nImPlotStyle::ImPlotStyle() {\n\n    LineWeight         = 1;\n    Marker             = ImPlotMarker_None;\n    MarkerSize         = 4;\n    MarkerWeight       = 1;\n    FillAlpha          = 1;\n    ErrorBarSize       = 5;\n    ErrorBarWeight     = 1.5f;\n    DigitalBitHeight   = 8;\n    DigitalBitGap      = 4;\n\n    PlotBorderSize     = 1;\n    MinorAlpha         = 0.25f;\n    MajorTickLen       = ImVec2(10,10);\n    MinorTickLen       = ImVec2(5,5);\n    MajorTickSize      = ImVec2(1,1);\n    MinorTickSize      = ImVec2(1,1);\n    MajorGridSize      = ImVec2(1,1);\n    MinorGridSize      = ImVec2(1,1);\n    PlotPadding        = ImVec2(10,10);\n    LabelPadding       = ImVec2(5,5);\n    LegendPadding      = ImVec2(10,10);\n    LegendInnerPadding = ImVec2(5,5);\n    LegendSpacing      = ImVec2(5,0);\n    MousePosPadding    = ImVec2(10,10);\n    AnnotationPadding  = ImVec2(2,2);\n    FitPadding         = ImVec2(0,0);\n    PlotDefaultSize    = ImVec2(400,300);\n    PlotMinSize        = ImVec2(200,150);\n\n    ImPlot::StyleColorsAuto(this);\n\n    Colormap = ImPlotColormap_Deep;\n\n    UseLocalTime     = false;\n    Use24HourClock   = false;\n    UseISO8601       = false;\n}\n\n//-----------------------------------------------------------------------------\n// Style\n//-----------------------------------------------------------------------------\n\nnamespace ImPlot {\n\nconst char* GetStyleColorName(ImPlotCol col) {\n    static const char* col_names[ImPlotCol_COUNT] = {\n        \"Line\",\n        \"Fill\",\n        \"MarkerOutline\",\n        \"MarkerFill\",\n        \"ErrorBar\",\n        \"FrameBg\",\n        \"PlotBg\",\n        \"PlotBorder\",\n        \"LegendBg\",\n        \"LegendBorder\",\n        \"LegendText\",\n        \"TitleText\",\n        \"InlayText\",\n        \"AxisText\",\n        \"AxisGrid\",\n        \"AxisTick\",\n        \"AxisBg\",\n        \"AxisBgHovered\",\n        \"AxisBgActive\",\n        \"Selection\",\n        \"Crosshairs\"\n    };\n    return col_names[col];\n}\n\nconst char* GetMarkerName(ImPlotMarker marker) {\n    switch (marker) {\n        case ImPlotMarker_None:     return \"None\";\n        case ImPlotMarker_Circle:   return \"Circle\";\n        case ImPlotMarker_Square:   return \"Square\";\n        case ImPlotMarker_Diamond:  return \"Diamond\";\n        case ImPlotMarker_Up:       return \"Up\";\n        case ImPlotMarker_Down:     return \"Down\";\n        case ImPlotMarker_Left:     return \"Left\";\n        case ImPlotMarker_Right:    return \"Right\";\n        case ImPlotMarker_Cross:    return \"Cross\";\n        case ImPlotMarker_Plus:     return \"Plus\";\n        case ImPlotMarker_Asterisk: return \"Asterisk\";\n        default:                    return \"\";\n    }\n}\n\nImVec4 GetAutoColor(ImPlotCol idx) {\n    ImVec4 col(0,0,0,1);\n    switch(idx) {\n        case ImPlotCol_Line:          return col; // these are plot dependent!\n        case ImPlotCol_Fill:          return col; // these are plot dependent!\n        case ImPlotCol_MarkerOutline: return col; // these are plot dependent!\n        case ImPlotCol_MarkerFill:    return col; // these are plot dependent!\n        case ImPlotCol_ErrorBar:      return ImGui::GetStyleColorVec4(ImGuiCol_Text);\n        case ImPlotCol_FrameBg:       return ImGui::GetStyleColorVec4(ImGuiCol_FrameBg);\n        case ImPlotCol_PlotBg:        return ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);\n        case ImPlotCol_PlotBorder:    return ImGui::GetStyleColorVec4(ImGuiCol_Border);\n        case ImPlotCol_LegendBg:      return ImGui::GetStyleColorVec4(ImGuiCol_PopupBg);\n        case ImPlotCol_LegendBorder:  return GetStyleColorVec4(ImPlotCol_PlotBorder);\n        case ImPlotCol_LegendText:    return GetStyleColorVec4(ImPlotCol_InlayText);\n        case ImPlotCol_TitleText:     return ImGui::GetStyleColorVec4(ImGuiCol_Text);\n        case ImPlotCol_InlayText:     return ImGui::GetStyleColorVec4(ImGuiCol_Text);\n        case ImPlotCol_AxisText:      return ImGui::GetStyleColorVec4(ImGuiCol_Text);\n        case ImPlotCol_AxisGrid:      return GetStyleColorVec4(ImPlotCol_AxisText) * ImVec4(1,1,1,0.25f);\n        case ImPlotCol_AxisTick:      return GetStyleColorVec4(ImPlotCol_AxisGrid);\n        case ImPlotCol_AxisBg:        return ImVec4(0,0,0,0);\n        case ImPlotCol_AxisBgHovered: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);\n        case ImPlotCol_AxisBgActive:  return ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);\n        case ImPlotCol_Selection:     return ImVec4(1,1,0,1);\n        case ImPlotCol_Crosshairs:    return GetStyleColorVec4(ImPlotCol_PlotBorder);\n        default: return col;\n    }\n}\n\nstruct ImPlotStyleVarInfo {\n    ImGuiDataType   Type;\n    ImU32           Count;\n    ImU32           Offset;\n    void*           GetVarPtr(ImPlotStyle* style) const { return (void*)((unsigned char*)style + Offset); }\n};\n\nstatic const ImPlotStyleVarInfo GPlotStyleVarInfo[] =\n{\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, LineWeight)         }, // ImPlotStyleVar_LineWeight\n    { ImGuiDataType_S32,   1, (ImU32)offsetof(ImPlotStyle, Marker)             }, // ImPlotStyleVar_Marker\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerSize)         }, // ImPlotStyleVar_MarkerSize\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerWeight)       }, // ImPlotStyleVar_MarkerWeight\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, FillAlpha)          }, // ImPlotStyleVar_FillAlpha\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarSize)       }, // ImPlotStyleVar_ErrorBarSize\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarWeight)     }, // ImPlotStyleVar_ErrorBarWeight\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitHeight)   }, // ImPlotStyleVar_DigitalBitHeight\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitGap)      }, // ImPlotStyleVar_DigitalBitGap\n\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, PlotBorderSize)     }, // ImPlotStyleVar_PlotBorderSize\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MinorAlpha)         }, // ImPlotStyleVar_MinorAlpha\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickLen)       }, // ImPlotStyleVar_MajorTickLen\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickLen)       }, // ImPlotStyleVar_MinorTickLen\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickSize)      }, // ImPlotStyleVar_MajorTickSize\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickSize)      }, // ImPlotStyleVar_MinorTickSize\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorGridSize)      }, // ImPlotStyleVar_MajorGridSize\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorGridSize)      }, // ImPlotStyleVar_MinorGridSize\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotPadding)        }, // ImPlotStyleVar_PlotPadding\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LabelPadding)       }, // ImPlotStyleVar_LabelPaddine\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendPadding)      }, // ImPlotStyleVar_LegendPadding\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendSpacing)      }, // ImPlotStyleVar_LegendSpacing\n\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MousePosPadding)    }, // ImPlotStyleVar_MousePosPadding\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, AnnotationPadding)  }, // ImPlotStyleVar_AnnotationPadding\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, FitPadding)         }, // ImPlotStyleVar_FitPadding\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotDefaultSize)    }, // ImPlotStyleVar_PlotDefaultSize\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotMinSize)        }  // ImPlotStyleVar_PlotMinSize\n};\n\nstatic const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) {\n    IM_ASSERT(idx >= 0 && idx < ImPlotStyleVar_COUNT);\n    IM_ASSERT(IM_ARRAYSIZE(GPlotStyleVarInfo) == ImPlotStyleVar_COUNT);\n    return &GPlotStyleVarInfo[idx];\n}\n\n//-----------------------------------------------------------------------------\n// Generic Helpers\n//-----------------------------------------------------------------------------\n\nvoid AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char *text_begin, const char* text_end) {\n    // the code below is based loosely on ImFont::RenderText\n    if (!text_end)\n        text_end = text_begin + strlen(text_begin);\n    ImGuiContext& g = *GImGui;\n\n#ifdef IMGUI_HAS_TEXTURES\n    ImFontBaked* font = g.Font->GetFontBaked(g.FontSize);\n    const float scale = g.FontSize / font->Size;\n#else\n    ImFont* font = g.Font;\n    const float scale = g.FontSize / font->FontSize;\n#endif\n\n    // Align to be pixel perfect\n    pos.x = ImFloor(pos.x);\n    pos.y = ImFloor(pos.y);\n    const char* s = text_begin;\n    int chars_exp = (int)(text_end - s);\n    int chars_rnd = 0;\n    const int vtx_count_max = chars_exp * 4;\n    const int idx_count_max = chars_exp * 6;\n    DrawList->PrimReserve(idx_count_max, vtx_count_max);\n    while (s < text_end) {\n        unsigned int c = (unsigned int)*s;\n        if (c < 0x80) {\n            s += 1;\n        }\n        else {\n            s += ImTextCharFromUtf8(&c, s, text_end);\n            if (c == 0) // Malformed UTF-8?\n                break;\n        }\n        const ImFontGlyph * glyph = font->FindGlyph((ImWchar)c);\n        if (glyph == nullptr) {\n            continue;\n        }\n        DrawList->PrimQuadUV(pos + ImVec2(glyph->Y0, -glyph->X0) * scale, pos + ImVec2(glyph->Y0, -glyph->X1) * scale,\n                             pos + ImVec2(glyph->Y1, -glyph->X1) * scale, pos + ImVec2(glyph->Y1, -glyph->X0) * scale,\n                             ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V0),\n                             ImVec2(glyph->U1, glyph->V1), ImVec2(glyph->U0, glyph->V1),\n                             col);\n        pos.y -= glyph->AdvanceX * scale;\n        chars_rnd++;\n    }\n    // Give back unused vertices\n    int chars_skp = chars_exp-chars_rnd;\n    DrawList->PrimUnreserve(chars_skp*6, chars_skp*4);\n}\n\nvoid AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end) {\n    float txt_ht = ImGui::GetTextLineHeight();\n    const char* title_end = ImGui::FindRenderedTextEnd(text_begin, text_end);\n    ImVec2 text_size;\n    float  y = 0;\n    while (const char* tmp = (const char*)memchr(text_begin, '\\n', title_end-text_begin)) {\n        text_size = ImGui::CalcTextSize(text_begin,tmp,true);\n        DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,tmp);\n        text_begin = tmp + 1;\n        y += txt_ht;\n    }\n    text_size = ImGui::CalcTextSize(text_begin,title_end,true);\n    DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,title_end);\n}\n\ndouble NiceNum(double x, bool round) {\n    double f;\n    double nf;\n    int expv = (int)floor(ImLog10(x));\n    f = x / ImPow(10.0, (double)expv);\n    if (round)\n        if (f < 1.5)\n            nf = 1;\n        else if (f < 3)\n            nf = 2;\n        else if (f < 7)\n            nf = 5;\n        else\n            nf = 10;\n    else if (f <= 1)\n        nf = 1;\n    else if (f <= 2)\n        nf = 2;\n    else if (f <= 5)\n        nf = 5;\n    else\n        nf = 10;\n    return nf * ImPow(10.0, expv);\n}\n\n//-----------------------------------------------------------------------------\n// Context Utils\n//-----------------------------------------------------------------------------\n\nvoid SetImGuiContext(ImGuiContext* ctx) {\n    ImGui::SetCurrentContext(ctx);\n}\n\nImPlotContext* CreateContext() {\n    ImPlotContext* ctx = IM_NEW(ImPlotContext)();\n    Initialize(ctx);\n    if (GImPlot == nullptr)\n        SetCurrentContext(ctx);\n    return ctx;\n}\n\nvoid DestroyContext(ImPlotContext* ctx) {\n    if (ctx == nullptr)\n        ctx = GImPlot;\n    if (GImPlot == ctx)\n        SetCurrentContext(nullptr);\n    IM_DELETE(ctx);\n}\n\nImPlotContext* GetCurrentContext() {\n    return GImPlot;\n}\n\nvoid SetCurrentContext(ImPlotContext* ctx) {\n    GImPlot = ctx;\n}\n\n#define IMPLOT_APPEND_CMAP(name, qual) ctx->ColormapData.Append(#name, name, sizeof(name)/sizeof(ImU32), qual)\n#define IM_RGB(r,g,b) IM_COL32(r,g,b,255)\n\nvoid Initialize(ImPlotContext* ctx) {\n    ResetCtxForNextPlot(ctx);\n    ResetCtxForNextAlignedPlots(ctx);\n    ResetCtxForNextSubplot(ctx);\n\n    const ImU32 Deep[]     = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396                        };\n    const ImU32 Dark[]     = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409                                    };\n    const ImU32 Pastel[]   = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986                                    };\n    const ImU32 Paired[]   = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481};\n    const ImU32 Viridis[]  = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301            };\n    const ImU32 Plasma[]   = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752            };\n    const ImU32 Hot[]      = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295            };\n    const ImU32 Cool[]     = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015            };\n    const ImU32 Pink[]     = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295            };\n    const ImU32 Jet[]      = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335            };\n    const ImU32 Twilight[] = {IM_RGB(226,217,226),IM_RGB(166,191,202),IM_RGB(109,144,192),IM_RGB(95,88,176),IM_RGB(83,30,124),IM_RGB(47,20,54),IM_RGB(100,25,75),IM_RGB(159,60,80),IM_RGB(192,117,94),IM_RGB(208,179,158),IM_RGB(226,217,226)};\n    const ImU32 RdBu[]     = {IM_RGB(103,0,31),IM_RGB(178,24,43),IM_RGB(214,96,77),IM_RGB(244,165,130),IM_RGB(253,219,199),IM_RGB(247,247,247),IM_RGB(209,229,240),IM_RGB(146,197,222),IM_RGB(67,147,195),IM_RGB(33,102,172),IM_RGB(5,48,97)};\n    const ImU32 BrBG[]     = {IM_RGB(84,48,5),IM_RGB(140,81,10),IM_RGB(191,129,45),IM_RGB(223,194,125),IM_RGB(246,232,195),IM_RGB(245,245,245),IM_RGB(199,234,229),IM_RGB(128,205,193),IM_RGB(53,151,143),IM_RGB(1,102,94),IM_RGB(0,60,48)};\n    const ImU32 PiYG[]     = {IM_RGB(142,1,82),IM_RGB(197,27,125),IM_RGB(222,119,174),IM_RGB(241,182,218),IM_RGB(253,224,239),IM_RGB(247,247,247),IM_RGB(230,245,208),IM_RGB(184,225,134),IM_RGB(127,188,65),IM_RGB(77,146,33),IM_RGB(39,100,25)};\n    const ImU32 Spectral[] = {IM_RGB(158,1,66),IM_RGB(213,62,79),IM_RGB(244,109,67),IM_RGB(253,174,97),IM_RGB(254,224,139),IM_RGB(255,255,191),IM_RGB(230,245,152),IM_RGB(171,221,164),IM_RGB(102,194,165),IM_RGB(50,136,189),IM_RGB(94,79,162)};\n    const ImU32 Greys[]    = {IM_COL32_WHITE, IM_COL32_BLACK                                                                                                                };\n\n    IMPLOT_APPEND_CMAP(Deep, true);\n    IMPLOT_APPEND_CMAP(Dark, true);\n    IMPLOT_APPEND_CMAP(Pastel, true);\n    IMPLOT_APPEND_CMAP(Paired, true);\n    IMPLOT_APPEND_CMAP(Viridis, false);\n    IMPLOT_APPEND_CMAP(Plasma, false);\n    IMPLOT_APPEND_CMAP(Hot, false);\n    IMPLOT_APPEND_CMAP(Cool, false);\n    IMPLOT_APPEND_CMAP(Pink, false);\n    IMPLOT_APPEND_CMAP(Jet, false);\n    IMPLOT_APPEND_CMAP(Twilight, false);\n    IMPLOT_APPEND_CMAP(RdBu, false);\n    IMPLOT_APPEND_CMAP(BrBG, false);\n    IMPLOT_APPEND_CMAP(PiYG, false);\n    IMPLOT_APPEND_CMAP(Spectral, false);\n    IMPLOT_APPEND_CMAP(Greys, false);\n}\n\nvoid ResetCtxForNextPlot(ImPlotContext* ctx) {\n    // reset the next plot/item data\n    ctx->NextPlotData.Reset();\n    ctx->NextItemData.Reset();\n    // reset labels\n    ctx->Annotations.Reset();\n    ctx->Tags.Reset();\n    // reset extents/fit\n    ctx->OpenContextThisFrame = false;\n    // reset digital plot items count\n    ctx->DigitalPlotItemCnt = 0;\n    ctx->DigitalPlotOffset = 0;\n    // nullify plot\n    ctx->CurrentPlot  = nullptr;\n    ctx->CurrentItem  = nullptr;\n    ctx->PreviousItem = nullptr;\n}\n\nvoid ResetCtxForNextAlignedPlots(ImPlotContext* ctx) {\n    ctx->CurrentAlignmentH = nullptr;\n    ctx->CurrentAlignmentV = nullptr;\n}\n\nvoid ResetCtxForNextSubplot(ImPlotContext* ctx) {\n    ctx->CurrentSubplot      = nullptr;\n    ctx->CurrentAlignmentH   = nullptr;\n    ctx->CurrentAlignmentV   = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// Plot Utils\n//-----------------------------------------------------------------------------\n\nImPlotPlot* GetPlot(const char* title) {\n    ImGuiWindow*   Window = GImGui->CurrentWindow;\n    const ImGuiID  ID     = Window->GetID(title);\n    return GImPlot->Plots.GetByKey(ID);\n}\n\nImPlotPlot* GetCurrentPlot() {\n    return GImPlot->CurrentPlot;\n}\n\nvoid BustPlotCache() {\n    ImPlotContext& gp = *GImPlot;\n    gp.Plots.Clear();\n    gp.Subplots.Clear();\n}\n\n//-----------------------------------------------------------------------------\n// Legend Utils\n//-----------------------------------------------------------------------------\n\nImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation loc, const ImVec2& pad) {\n    ImVec2 pos;\n    if (ImHasFlag(loc, ImPlotLocation_West) && !ImHasFlag(loc, ImPlotLocation_East))\n        pos.x = outer_rect.Min.x + pad.x;\n    else if (!ImHasFlag(loc, ImPlotLocation_West) && ImHasFlag(loc, ImPlotLocation_East))\n        pos.x = outer_rect.Max.x - pad.x - inner_size.x;\n    else\n        pos.x = outer_rect.GetCenter().x - inner_size.x * 0.5f;\n    // legend reference point y\n    if (ImHasFlag(loc, ImPlotLocation_North) && !ImHasFlag(loc, ImPlotLocation_South))\n        pos.y = outer_rect.Min.y + pad.y;\n    else if (!ImHasFlag(loc, ImPlotLocation_North) && ImHasFlag(loc, ImPlotLocation_South))\n        pos.y = outer_rect.Max.y - pad.y - inner_size.y;\n    else\n        pos.y = outer_rect.GetCenter().y - inner_size.y * 0.5f;\n    pos.x = IM_ROUND(pos.x);\n    pos.y = IM_ROUND(pos.y);\n    return pos;\n}\n\nImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical) {\n    // vars\n    const int   nItems      = items.GetLegendCount();\n    const float txt_ht      = ImGui::GetTextLineHeight();\n    const float icon_size   = txt_ht;\n    // get label max width\n    float max_label_width = 0;\n    float sum_label_width = 0;\n    for (int i = 0; i < nItems; ++i) {\n        const char* label       = items.GetLegendLabel(i);\n        const float label_width = ImGui::CalcTextSize(label, nullptr, true).x;\n        max_label_width         = label_width > max_label_width ? label_width : max_label_width;\n        sum_label_width        += label_width;\n    }\n    // calc legend size\n    const ImVec2 legend_size = vertical ?\n                               ImVec2(pad.x * 2 + icon_size + max_label_width, pad.y * 2 + nItems * txt_ht + (nItems - 1) * spacing.y) :\n                               ImVec2(pad.x * 2 + icon_size * nItems + sum_label_width + (nItems - 1) * spacing.x, pad.y * 2 + txt_ht);\n    return legend_size;\n}\n\nbool ClampLegendRect(ImRect& legend_rect, const ImRect& outer_rect, const ImVec2& pad) {\n    bool clamped = false;\n    ImRect outer_rect_pad(outer_rect.Min + pad, outer_rect.Max - pad);\n    if (legend_rect.Min.x < outer_rect_pad.Min.x) {\n        legend_rect.Min.x = outer_rect_pad.Min.x;\n        clamped = true;\n    }\n    if (legend_rect.Min.y < outer_rect_pad.Min.y) {\n        legend_rect.Min.y = outer_rect_pad.Min.y;\n        clamped = true;\n    }\n    if (legend_rect.Max.x > outer_rect_pad.Max.x) {\n        legend_rect.Max.x = outer_rect_pad.Max.x;\n        clamped = true;\n    }\n    if (legend_rect.Max.y > outer_rect_pad.Max.y) {\n        legend_rect.Max.y = outer_rect_pad.Max.y;\n        clamped = true;\n    }    \n    return clamped;\n}\n    \nint LegendSortingComp(const void* _a, const void* _b) {\n    ImPlotItemGroup* items = GImPlot->SortItems;\n    const int a = *(const int*)_a;\n    const int b = *(const int*)_b;\n    const char* label_a = items->GetLegendLabel(a);\n    const char* label_b = items->GetLegendLabel(b);\n    return strcmp(label_a,label_b);\n}\n\nbool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool hovered, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList) {\n    // vars\n    const float txt_ht      = ImGui::GetTextLineHeight();\n    const float icon_size   = txt_ht;\n    const float icon_shrink = 2;\n    ImU32 col_txt           = GetStyleColorU32(ImPlotCol_LegendText);\n    ImU32 col_txt_dis       = ImAlphaU32(col_txt, 0.25f);\n    // render each legend item\n    float sum_label_width = 0;\n    bool any_item_hovered = false;\n\n    const int num_items = items.GetLegendCount();\n    if (num_items < 1)\n        return hovered;\n    // build render order\n    ImPlotContext& gp = *GImPlot;\n    ImVector<int>& indices = gp.TempInt1;\n    indices.resize(num_items);\n    for (int i = 0; i < num_items; ++i)\n        indices[i] = i;\n    if (ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_Sort) && num_items > 1) {\n        gp.SortItems = &items;\n        qsort(indices.Data, num_items, sizeof(int), LegendSortingComp);\n    }\n    // render\n    for (int i = 0; i < num_items; ++i) {\n        const int idx           = indices[i];\n        ImPlotItem* item        = items.GetLegendItem(idx);\n        const char* label       = items.GetLegendLabel(idx);\n        const float label_width = ImGui::CalcTextSize(label, nullptr, true).x;\n        const ImVec2 top_left   = vertical ?\n                                  legend_bb.Min + pad + ImVec2(0, i * (txt_ht + spacing.y)) :\n                                  legend_bb.Min + pad + ImVec2(i * (icon_size + spacing.x) + sum_label_width, 0);\n        sum_label_width        += label_width;\n        ImRect icon_bb;\n        icon_bb.Min = top_left + ImVec2(icon_shrink,icon_shrink);\n        icon_bb.Max = top_left + ImVec2(icon_size - icon_shrink, icon_size - icon_shrink);\n        ImRect label_bb;\n        label_bb.Min = top_left;\n        label_bb.Max = top_left + ImVec2(label_width + icon_size, icon_size);\n        ImU32 col_txt_hl;\n        ImU32 col_item = ImAlphaU32(item->Color,1);\n\n        ImRect button_bb(icon_bb.Min, label_bb.Max);\n\n        ImGui::KeepAliveID(item->ID);\n\n        bool item_hov = false;\n        bool item_hld = false;\n        bool item_clk = ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoButtons)\n                      ? false\n                      : ImGui::ButtonBehavior(button_bb, item->ID, &item_hov, &item_hld);\n\n        if (item_clk)\n            item->Show = !item->Show;\n\n\n        const bool can_hover = (item_hov)\n                             && (!ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightItem)\n                             || !ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightAxis));\n\n        if (can_hover) {\n            item->LegendHoverRect.Min = icon_bb.Min;\n            item->LegendHoverRect.Max = label_bb.Max;\n            item->LegendHovered = true;\n            col_txt_hl = ImMixU32(col_txt, col_item, 64);\n            any_item_hovered = true;\n        }\n        else {\n            col_txt_hl = ImGui::GetColorU32(col_txt);\n        }\n        ImU32 col_icon;\n        if (item_hld)\n            col_icon = item->Show ? ImAlphaU32(col_item,0.5f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f);\n        else if (item_hov)\n            col_icon = item->Show ? ImAlphaU32(col_item,0.75f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.75f);\n        else\n            col_icon = item->Show ? col_item : col_txt_dis;\n\n        DrawList.AddRectFilled(icon_bb.Min, icon_bb.Max, col_icon);\n        const char* text_display_end = ImGui::FindRenderedTextEnd(label, nullptr);\n        if (label != text_display_end)\n            DrawList.AddText(top_left + ImVec2(icon_size, 0), item->Show ? col_txt_hl  : col_txt_dis, label, text_display_end);\n    }\n    return hovered && !any_item_hovered;\n}\n\n//-----------------------------------------------------------------------------\n// Locators\n//-----------------------------------------------------------------------------\n\nstatic const float TICK_FILL_X = 0.8f;\nstatic const float TICK_FILL_Y = 1.0f;\n\nvoid Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) {\n    if (range.Min == range.Max)\n        return;\n    const int nMinor        = 10;\n    const int nMajor        = ImMax(2, (int)IM_ROUND(pixels / (vertical ? 300.0f : 400.0f)));\n    const double nice_range = NiceNum(range.Size() * 0.99, false);\n    const double interval   = NiceNum(nice_range / (nMajor - 1), true);\n    const double graphmin   = floor(range.Min / interval) * interval;\n    const double graphmax   = ceil(range.Max / interval) * interval;\n    bool first_major_set    = false;\n    int  first_major_idx    = 0;\n    const int idx0 = ticker.TickCount(); // ticker may have user custom ticks\n    ImVec2 total_size(0,0);\n    for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) {\n        // is this zero? combat zero formatting issues\n        if (major-interval < 0 && major+interval > 0)\n            major = 0;\n        if (range.Contains(major)) {\n            if (!first_major_set) {\n                first_major_idx = ticker.TickCount();\n                first_major_set = true;\n            }\n            total_size += ticker.AddTick(major, true, 0, true, formatter, formatter_data).LabelSize;\n        }\n        for (int i = 1; i < nMinor; ++i) {\n            double minor = major + i * interval / nMinor;\n            if (range.Contains(minor)) {\n                total_size += ticker.AddTick(minor, false, 0, true, formatter, formatter_data).LabelSize;\n            }\n        }\n    }\n    // prune if necessary\n    if ((!vertical && total_size.x > pixels*TICK_FILL_X) || (vertical && total_size.y > pixels*TICK_FILL_Y)) {\n        for (int i = first_major_idx-1; i >= idx0; i -= 2)\n            ticker.Ticks[i].ShowLabel = false;\n        for (int i = first_major_idx+1; i < ticker.TickCount(); i += 2)\n            ticker.Ticks[i].ShowLabel = false;\n    }\n}\n\nbool CalcLogarithmicExponents(const ImPlotRange& range, float pix, bool vertical, int& exp_min, int& exp_max, int& exp_step) {\n    if (range.Min * range.Max > 0) {\n        const int nMajor = vertical ? ImMax(2, (int)IM_ROUND(pix * 0.02f)) : ImMax(2, (int)IM_ROUND(pix * 0.01f)); // TODO: magic numbers\n        double log_min = ImLog10(ImAbs(range.Min));\n        double log_max = ImLog10(ImAbs(range.Max));\n        double log_a = ImMin(log_min,log_max);\n        double log_b = ImMax(log_min,log_max);\n        exp_step  = ImMax(1,(int)(log_b - log_a) / nMajor);\n        exp_min   = (int)log_a;\n        exp_max   = (int)log_b;\n        if (exp_step != 1) {\n            while(exp_step % 3 != 0)       exp_step++; // make step size multiple of three\n            while(exp_min % exp_step != 0) exp_min--;  // decrease exp_min until exp_min + N * exp_step will be 0\n        }\n        return true;\n    }\n    return false;\n}\n\nvoid AddTicksLogarithmic(const ImPlotRange& range, int exp_min, int exp_max, int exp_step, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) {\n    const double sign = ImSign(range.Max);\n    for (int e = exp_min - exp_step; e < (exp_max + exp_step); e += exp_step) {\n        double major1 = sign*ImPow(10, (double)(e));\n        double major2 = sign*ImPow(10, (double)(e + 1));\n        double interval = (major2 - major1) / 9;\n        if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON))\n            ticker.AddTick(major1, true, 0, true, formatter, data);\n        for (int j = 0; j < exp_step; ++j) {\n            major1 = sign*ImPow(10, (double)(e+j));\n            major2 = sign*ImPow(10, (double)(e+j+1));\n            interval = (major2 - major1) / 9;\n            for (int i = 1; i < (9 + (int)(j < (exp_step - 1))); ++i) {\n                double minor = major1 + i * interval;\n                if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON))\n                    ticker.AddTick(minor, false, 0, false, formatter, data);\n            }\n        }\n    }\n}\n\nvoid Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) {\n    int exp_min, exp_max, exp_step;\n    if (CalcLogarithmicExponents(range, pixels, vertical, exp_min, exp_max, exp_step))\n        AddTicksLogarithmic(range, exp_min, exp_max, exp_step, ticker, formatter, formatter_data);\n}\n\nfloat CalcSymLogPixel(double plt, const ImPlotRange& range, float pixels) {\n    double scaleToPixels = pixels / range.Size();\n    double scaleMin      = TransformForward_SymLog(range.Min,nullptr);\n    double scaleMax      = TransformForward_SymLog(range.Max,nullptr);\n    double s             = TransformForward_SymLog(plt, nullptr);\n    double t             = (s - scaleMin) / (scaleMax - scaleMin);\n    plt                  = range.Min + range.Size() * t;\n\n    return (float)(0 + scaleToPixels * (plt - range.Min));\n}\n\nvoid Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) {\n    if (range.Min >= -1 && range.Max <= 1) {\n        Locator_Default(ticker, range, pixels, vertical, formatter, formatter_data);\n    }\n    else if (range.Min * range.Max < 0) { // cross zero\n        const float pix_min = 0;\n        const float pix_max = pixels;\n        const float pix_p1  = CalcSymLogPixel(1, range, pixels);\n        const float pix_n1  = CalcSymLogPixel(-1, range, pixels);\n        int exp_min_p, exp_max_p, exp_step_p;\n        int exp_min_n, exp_max_n, exp_step_n;\n        CalcLogarithmicExponents(ImPlotRange(1,range.Max), ImAbs(pix_max-pix_p1),vertical,exp_min_p,exp_max_p,exp_step_p);\n        CalcLogarithmicExponents(ImPlotRange(range.Min,-1),ImAbs(pix_n1-pix_min),vertical,exp_min_n,exp_max_n,exp_step_n);\n        int exp_step = ImMax(exp_step_n, exp_step_p);\n        ticker.AddTick(0,true,0,true,formatter,formatter_data);\n        AddTicksLogarithmic(ImPlotRange(1,range.Max), exp_min_p,exp_max_p,exp_step,ticker,formatter,formatter_data);\n        AddTicksLogarithmic(ImPlotRange(range.Min,-1),exp_min_n,exp_max_n,exp_step,ticker,formatter,formatter_data);\n    }\n    else {\n        Locator_Log10(ticker, range, pixels, vertical, formatter, formatter_data);\n    }\n}\n\nvoid AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) {\n    for (int i = 0; i < n; ++i) {\n        if (labels != nullptr)\n            ticker.AddTick(values[i], false, 0, true, labels[i]);\n        else\n            ticker.AddTick(values[i], false, 0, true, formatter, data);\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Time Ticks and Utils\n//-----------------------------------------------------------------------------\n\n// this may not be thread safe?\nstatic const double TimeUnitSpans[ImPlotTimeUnit_COUNT] = {\n    0.000001,\n    0.001,\n    1,\n    60,\n    3600,\n    86400,\n    2629800,\n    31557600\n};\n\ninline ImPlotTimeUnit GetUnitForRange(double range) {\n    static double cutoffs[ImPlotTimeUnit_COUNT] = {0.001, 1, 60, 3600, 86400, 2629800, 31557600, IMPLOT_MAX_TIME};\n    for (int i = 0; i < ImPlotTimeUnit_COUNT; ++i) {\n        if (range <= cutoffs[i])\n            return (ImPlotTimeUnit)i;\n    }\n    return ImPlotTimeUnit_Yr;\n}\n\ninline int LowerBoundStep(int max_divs, const int* divs, const int* step, int size) {\n    if (max_divs < divs[0])\n        return 0;\n    for (int i = 1; i < size; ++i) {\n        if (max_divs < divs[i])\n            return step[i-1];\n    }\n    return step[size-1];\n}\n\ninline int GetTimeStep(int max_divs, ImPlotTimeUnit unit) {\n    if (unit == ImPlotTimeUnit_Ms || unit == ImPlotTimeUnit_Us) {\n        static const int step[] = {500,250,200,100,50,25,20,10,5,2,1};\n        static const int divs[] = {2,4,5,10,20,40,50,100,200,500,1000};\n        return LowerBoundStep(max_divs, divs, step, 11);\n    }\n    if (unit == ImPlotTimeUnit_S || unit == ImPlotTimeUnit_Min) {\n        static const int step[] = {30,15,10,5,1};\n        static const int divs[] = {2,4,6,12,60};\n        return LowerBoundStep(max_divs, divs, step, 5);\n    }\n    else if (unit == ImPlotTimeUnit_Hr) {\n        static const int step[] = {12,6,3,2,1};\n        static const int divs[] = {2,4,8,12,24};\n        return LowerBoundStep(max_divs, divs, step, 5);\n    }\n    else if (unit == ImPlotTimeUnit_Day) {\n        static const int step[] = {14,7,2,1};\n        static const int divs[] = {2,4,14,28};\n        return LowerBoundStep(max_divs, divs, step, 4);\n    }\n    else if (unit == ImPlotTimeUnit_Mo) {\n        static const int step[] = {6,3,2,1};\n        static const int divs[] = {2,4,6,12};\n        return LowerBoundStep(max_divs, divs, step, 4);\n    }\n    return 0;\n}\n\nImPlotTime MkGmtTime(struct tm *ptm) {\n    ImPlotTime t;\n#ifdef _WIN32\n    t.S = _mkgmtime(ptm);\n#else\n    t.S = timegm(ptm);\n#endif\n    if (t.S < 0)\n        t.S = 0;\n    return t;\n}\n\ntm* GetGmtTime(const ImPlotTime& t, tm* ptm)\n{\n#ifdef _WIN32\n  if (gmtime_s(ptm, &t.S) == 0)\n    return ptm;\n  else\n    return nullptr;\n#else\n  return gmtime_r(&t.S, ptm);\n#endif\n}\n\nImPlotTime MkLocTime(struct tm *ptm) {\n    ImPlotTime t;\n    t.S = mktime(ptm);\n    if (t.S < 0)\n        t.S = 0;\n    return t;\n}\n\ntm* GetLocTime(const ImPlotTime& t, tm* ptm) {\n#ifdef _WIN32\n  if (localtime_s(ptm, &t.S) == 0)\n    return ptm;\n  else\n    return nullptr;\n#else\n    return localtime_r(&t.S, ptm);\n#endif\n}\n\ninline ImPlotTime MkTime(struct tm *ptm) {\n    if (GetStyle().UseLocalTime)\n        return MkLocTime(ptm);\n    else\n        return MkGmtTime(ptm);\n}\n\ninline tm* GetTime(const ImPlotTime& t, tm* ptm) {\n    if (GetStyle().UseLocalTime)\n        return GetLocTime(t,ptm);\n    else\n        return GetGmtTime(t,ptm);\n}\n\nImPlotTime MakeTime(int year, int month, int day, int hour, int min, int sec, int us) {\n    tm& Tm = GImPlot->Tm;\n\n    int yr = year - 1900;\n    if (yr < 0)\n        yr = 0;\n\n    sec  = sec + us / 1000000;\n    us   = us % 1000000;\n\n    Tm.tm_sec  = sec;\n    Tm.tm_min  = min;\n    Tm.tm_hour = hour;\n    Tm.tm_mday = day;\n    Tm.tm_mon  = month;\n    Tm.tm_year = yr;\n\n    ImPlotTime t = MkTime(&Tm);\n\n    t.Us = us;\n    return t;\n}\n\nint GetYear(const ImPlotTime& t) {\n    tm& Tm = GImPlot->Tm;\n    GetTime(t, &Tm);\n    return Tm.tm_year + 1900;\n}\n\nImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) {\n    tm& Tm = GImPlot->Tm;\n    ImPlotTime t_out = t;\n    switch(unit) {\n        case ImPlotTimeUnit_Us:  t_out.Us += count;         break;\n        case ImPlotTimeUnit_Ms:  t_out.Us += count * 1000;  break;\n        case ImPlotTimeUnit_S:   t_out.S  += count;         break;\n        case ImPlotTimeUnit_Min: t_out.S  += count * 60;    break;\n        case ImPlotTimeUnit_Hr:  t_out.S  += count * 3600;  break;\n        case ImPlotTimeUnit_Day: t_out.S  += count * 86400; break;\n        case ImPlotTimeUnit_Mo:  for (int i = 0; i < abs(count); ++i) {\n                                     GetTime(t_out, &Tm);\n                                     if (count > 0)\n                                        t_out.S += 86400 * GetDaysInMonth(Tm.tm_year + 1900, Tm.tm_mon);\n                                     else if (count < 0)\n                                        t_out.S -= 86400 * GetDaysInMonth(Tm.tm_year + 1900 - (Tm.tm_mon == 0 ? 1 : 0), Tm.tm_mon == 0 ? 11 : Tm.tm_mon - 1); // NOT WORKING\n                                 }\n                                 break;\n        case ImPlotTimeUnit_Yr:  for (int i = 0; i < abs(count); ++i) {\n                                    if (count > 0)\n                                        t_out.S += 86400 * (365 + (int)IsLeapYear(GetYear(t_out)));\n                                    else if (count < 0)\n                                        t_out.S -= 86400 * (365 + (int)IsLeapYear(GetYear(t_out) - 1));\n                                    // this is incorrect if leap year and we are past Feb 28\n                                 }\n                                 break;\n        default:                 break;\n    }\n    t_out.RollOver();\n    return t_out;\n}\n\nImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit) {\n    ImPlotContext& gp = *GImPlot;\n    GetTime(t, &gp.Tm);\n    switch (unit) {\n        case ImPlotTimeUnit_S:   return ImPlotTime(t.S, 0);\n        case ImPlotTimeUnit_Ms:  return ImPlotTime(t.S, (t.Us / 1000) * 1000);\n        case ImPlotTimeUnit_Us:  return t;\n        case ImPlotTimeUnit_Yr:  gp.Tm.tm_mon  = 0; // fall-through\n        case ImPlotTimeUnit_Mo:  gp.Tm.tm_mday = 1; // fall-through\n        case ImPlotTimeUnit_Day: gp.Tm.tm_hour = 0; // fall-through\n        case ImPlotTimeUnit_Hr:  gp.Tm.tm_min  = 0; // fall-through\n        case ImPlotTimeUnit_Min: gp.Tm.tm_sec  = 0; break;\n        default:                 return t;\n    }\n    return MkTime(&gp.Tm);\n}\n\nImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit) {\n    return AddTime(FloorTime(t, unit), unit, 1);\n}\n\nImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit) {\n    ImPlotTime t1 = FloorTime(t, unit);\n    ImPlotTime t2 = AddTime(t1,unit,1);\n    if (t1.S == t2.S)\n        return t.Us - t1.Us < t2.Us - t.Us ? t1 : t2;\n    return t.S - t1.S < t2.S - t.S ? t1 : t2;\n}\n\nImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& tod_part) {\n    ImPlotContext& gp = *GImPlot;\n    tm& Tm = gp.Tm;\n    GetTime(date_part, &gp.Tm);\n    int y = Tm.tm_year;\n    int m = Tm.tm_mon;\n    int d = Tm.tm_mday;\n    GetTime(tod_part, &gp.Tm);\n    Tm.tm_year = y;\n    Tm.tm_mon  = m;\n    Tm.tm_mday = d;\n    ImPlotTime t = MkTime(&Tm);\n    t.Us = tod_part.Us;\n    return t;\n}\n\n// TODO: allow users to define these\nstatic const char* MONTH_NAMES[]  = {\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"};\nstatic const char* WD_ABRVS[]     = {\"Su\",\"Mo\",\"Tu\",\"We\",\"Th\",\"Fr\",\"Sa\"};\nstatic const char* MONTH_ABRVS[]  = {\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"};\n\nint FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, bool use_24_hr_clk) {\n    tm& Tm = GImPlot->Tm;\n    GetTime(t, &Tm);\n    const int us   = t.Us % 1000;\n    const int ms   = t.Us / 1000;\n    const int sec  = Tm.tm_sec;\n    const int min  = Tm.tm_min;\n    if (use_24_hr_clk) {\n        const int hr   = Tm.tm_hour;\n        switch(fmt) {\n            case ImPlotTimeFmt_Us:        return ImFormatString(buffer, size, \".%03d %03d\", ms, us);\n            case ImPlotTimeFmt_SUs:       return ImFormatString(buffer, size, \":%02d.%03d %03d\", sec, ms, us);\n            case ImPlotTimeFmt_SMs:       return ImFormatString(buffer, size, \":%02d.%03d\", sec, ms);\n            case ImPlotTimeFmt_S:         return ImFormatString(buffer, size, \":%02d\", sec);\n            case ImPlotTimeFmt_MinSMs:    return ImFormatString(buffer, size, \":%02d:%02d.%03d\", min, sec, ms);\n            case ImPlotTimeFmt_HrMinSMs:  return ImFormatString(buffer, size, \"%02d:%02d:%02d.%03d\", hr, min, sec, ms);\n            case ImPlotTimeFmt_HrMinS:    return ImFormatString(buffer, size, \"%02d:%02d:%02d\", hr, min, sec);\n            case ImPlotTimeFmt_HrMin:     return ImFormatString(buffer, size, \"%02d:%02d\", hr, min);\n            case ImPlotTimeFmt_Hr:        return ImFormatString(buffer, size, \"%02d:00\", hr);\n            default:                      return 0;\n        }\n    }\n    else {\n        const char* ap = Tm.tm_hour < 12 ? \"am\" : \"pm\";\n        const int hr   = (Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12;\n        switch(fmt) {\n            case ImPlotTimeFmt_Us:        return ImFormatString(buffer, size, \".%03d %03d\", ms, us);\n            case ImPlotTimeFmt_SUs:       return ImFormatString(buffer, size, \":%02d.%03d %03d\", sec, ms, us);\n            case ImPlotTimeFmt_SMs:       return ImFormatString(buffer, size, \":%02d.%03d\", sec, ms);\n            case ImPlotTimeFmt_S:         return ImFormatString(buffer, size, \":%02d\", sec);\n            case ImPlotTimeFmt_MinSMs:    return ImFormatString(buffer, size, \":%02d:%02d.%03d\", min, sec, ms);\n            case ImPlotTimeFmt_HrMinSMs:  return ImFormatString(buffer, size, \"%d:%02d:%02d.%03d%s\", hr, min, sec, ms, ap);\n            case ImPlotTimeFmt_HrMinS:    return ImFormatString(buffer, size, \"%d:%02d:%02d%s\", hr, min, sec, ap);\n            case ImPlotTimeFmt_HrMin:     return ImFormatString(buffer, size, \"%d:%02d%s\", hr, min, ap);\n            case ImPlotTimeFmt_Hr:        return ImFormatString(buffer, size, \"%d%s\", hr, ap);\n            default:                      return 0;\n        }\n    }\n}\n\nint FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, bool use_iso_8601) {\n    tm& Tm = GImPlot->Tm;\n    GetTime(t, &Tm);\n    const int day  = Tm.tm_mday;\n    const int mon  = Tm.tm_mon + 1;\n    const int year = Tm.tm_year + 1900;\n    const int yr   = year % 100;\n    if (use_iso_8601) {\n        switch (fmt) {\n            case ImPlotDateFmt_DayMo:   return ImFormatString(buffer, size, \"--%02d-%02d\", mon, day);\n            case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, \"%d-%02d-%02d\", year, mon, day);\n            case ImPlotDateFmt_MoYr:    return ImFormatString(buffer, size, \"%d-%02d\", year, mon);\n            case ImPlotDateFmt_Mo:      return ImFormatString(buffer, size, \"--%02d\", mon);\n            case ImPlotDateFmt_Yr:      return ImFormatString(buffer, size, \"%d\", year);\n            default:                    return 0;\n        }\n    }\n    else {\n        switch (fmt) {\n            case ImPlotDateFmt_DayMo:   return ImFormatString(buffer, size, \"%d/%d\", mon, day);\n            case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, \"%d/%d/%02d\", mon, day, yr);\n            case ImPlotDateFmt_MoYr:    return ImFormatString(buffer, size, \"%s %d\", MONTH_ABRVS[Tm.tm_mon], year);\n            case ImPlotDateFmt_Mo:      return ImFormatString(buffer, size, \"%s\", MONTH_ABRVS[Tm.tm_mon]);\n            case ImPlotDateFmt_Yr:      return ImFormatString(buffer, size, \"%d\", year);\n            default:                    return 0;\n        }\n    }\n }\n\nint FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt) {\n    int written = 0;\n    if (fmt.Date != ImPlotDateFmt_None)\n        written += FormatDate(t, buffer, size, fmt.Date, fmt.UseISO8601);\n    if (fmt.Time != ImPlotTimeFmt_None) {\n        if (fmt.Date != ImPlotDateFmt_None)\n            buffer[written++] = ' ';\n        written += FormatTime(t, &buffer[written], size - written, fmt.Time, fmt.Use24HourClock);\n    }\n    return written;\n}\n\ninline float GetDateTimeWidth(ImPlotDateTimeSpec fmt) {\n    static const ImPlotTime t_max_width = MakeTime(2888, 12, 22, 12, 58, 58, 888888); // best guess at time that maximizes pixel width\n    char buffer[32];\n    FormatDateTime(t_max_width, buffer, 32, fmt);\n    return ImGui::CalcTextSize(buffer).x;\n}\n\ninline bool TimeLabelSame(const char* l1, const char* l2) {\n    size_t len1 = strlen(l1);\n    size_t len2 = strlen(l2);\n    size_t n  = len1 < len2 ? len1 : len2;\n    return strcmp(l1 + len1 - n, l2 + len2 - n) == 0;\n}\n\nstatic const ImPlotDateTimeSpec TimeFormatLevel0[ImPlotTimeUnit_COUNT] = {\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,  ImPlotTimeFmt_Us),\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,  ImPlotTimeFmt_SMs),\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,  ImPlotTimeFmt_S),\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,  ImPlotTimeFmt_HrMin),\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,  ImPlotTimeFmt_Hr),\n    ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_None),\n    ImPlotDateTimeSpec(ImPlotDateFmt_Mo,    ImPlotTimeFmt_None),\n    ImPlotDateTimeSpec(ImPlotDateFmt_Yr,    ImPlotTimeFmt_None)\n};\n\nstatic const ImPlotDateTimeSpec TimeFormatLevel1[ImPlotTimeUnit_COUNT] = {\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,    ImPlotTimeFmt_HrMin),\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,    ImPlotTimeFmt_HrMinS),\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,    ImPlotTimeFmt_HrMin),\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,    ImPlotTimeFmt_HrMin),\n    ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None),\n    ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None),\n    ImPlotDateTimeSpec(ImPlotDateFmt_Yr,      ImPlotTimeFmt_None),\n    ImPlotDateTimeSpec(ImPlotDateFmt_Yr,      ImPlotTimeFmt_None)\n};\n\nstatic const ImPlotDateTimeSpec TimeFormatLevel1First[ImPlotTimeUnit_COUNT] = {\n    ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS),\n    ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS),\n    ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin),\n    ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin),\n    ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None),\n    ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None),\n    ImPlotDateTimeSpec(ImPlotDateFmt_Yr,      ImPlotTimeFmt_None),\n    ImPlotDateTimeSpec(ImPlotDateFmt_Yr,      ImPlotTimeFmt_None)\n};\n\nstatic const ImPlotDateTimeSpec TimeFormatMouseCursor[ImPlotTimeUnit_COUNT] = {\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,     ImPlotTimeFmt_Us),\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,     ImPlotTimeFmt_SUs),\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,     ImPlotTimeFmt_SMs),\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,     ImPlotTimeFmt_HrMinS),\n    ImPlotDateTimeSpec(ImPlotDateFmt_None,     ImPlotTimeFmt_HrMin),\n    ImPlotDateTimeSpec(ImPlotDateFmt_DayMo,    ImPlotTimeFmt_Hr),\n    ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr,  ImPlotTimeFmt_None),\n    ImPlotDateTimeSpec(ImPlotDateFmt_MoYr,     ImPlotTimeFmt_None)\n};\n\ninline ImPlotDateTimeSpec GetDateTimeFmt(const ImPlotDateTimeSpec* ctx, ImPlotTimeUnit idx) {\n    ImPlotStyle& style     = GetStyle();\n    ImPlotDateTimeSpec fmt  = ctx[idx];\n    fmt.UseISO8601         = style.UseISO8601;\n    fmt.Use24HourClock     = style.Use24HourClock;\n    return fmt;\n}\n\nvoid Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) {\n    IM_ASSERT_USER_ERROR(vertical == false, \"Cannot locate Time ticks on vertical axis!\");\n    (void)vertical;\n    // get units for level 0 and level 1 labels\n    const ImPlotTimeUnit unit0 = GetUnitForRange(range.Size() / (pixels / 100)); // level = 0 (top)\n    const ImPlotTimeUnit unit1 = ImClamp(unit0 + 1, 0, ImPlotTimeUnit_COUNT-1);  // level = 1 (bottom)\n    // get time format specs\n    const ImPlotDateTimeSpec fmt0 = GetDateTimeFmt(TimeFormatLevel0, unit0);\n    const ImPlotDateTimeSpec fmt1 = GetDateTimeFmt(TimeFormatLevel1, unit1);\n    const ImPlotDateTimeSpec fmtf = GetDateTimeFmt(TimeFormatLevel1First, unit1);\n    // min max times\n    const ImPlotTime t_min = ImPlotTime::FromDouble(range.Min);\n    const ImPlotTime t_max = ImPlotTime::FromDouble(range.Max);\n    // maximum allowable density of labels\n    const float max_density = 0.5f;\n    // book keeping\n    int last_major_offset = -1;\n    // formatter data\n    Formatter_Time_Data ftd;\n    ftd.UserFormatter = formatter;\n    ftd.UserFormatterData = formatter_data;\n    if (unit0 != ImPlotTimeUnit_Yr) {\n        // pixels per major (level 1) division\n        const float pix_per_major_div = pixels / (float)(range.Size() / TimeUnitSpans[unit1]);\n        // nominal pixels taken up by labels\n        const float fmt0_width = GetDateTimeWidth(fmt0);\n        const float fmt1_width = GetDateTimeWidth(fmt1);\n        const float fmtf_width = GetDateTimeWidth(fmtf);\n        // the maximum number of minor (level 0) labels that can fit between major (level 1) divisions\n        const int   minor_per_major   = (int)(max_density * pix_per_major_div / fmt0_width);\n        // the minor step size (level 0)\n        const int step = GetTimeStep(minor_per_major, unit0);\n        // generate ticks\n        ImPlotTime t1 = FloorTime(ImPlotTime::FromDouble(range.Min), unit1);\n        while (t1 < t_max) {\n            // get next major\n            const ImPlotTime t2 = AddTime(t1, unit1, 1);\n            // add major tick\n            if (t1 >= t_min && t1 <= t_max) {\n                // minor level 0 tick\n                ftd.Time = t1; ftd.Spec = fmt0;\n                ticker.AddTick(t1.ToDouble(), true, 0, true, Formatter_Time, &ftd);\n                // major level 1 tick\n                ftd.Time = t1; ftd.Spec = last_major_offset < 0 ? fmtf : fmt1;\n                ImPlotTick& tick_maj = ticker.AddTick(t1.ToDouble(), true, 1, true, Formatter_Time, &ftd);\n                const char* this_major = ticker.GetText(tick_maj);\n                if (last_major_offset >= 0 && TimeLabelSame(ticker.TextBuffer.Buf.Data + last_major_offset, this_major))\n                    tick_maj.ShowLabel = false;\n                last_major_offset = tick_maj.TextOffset;\n            }\n            // add minor ticks up until next major\n            if (minor_per_major > 1 && (t_min <= t2 && t1 <= t_max)) {\n                ImPlotTime t12 = AddTime(t1, unit0, step);\n                while (t12 < t2) {\n                    float px_to_t2 = (float)((t2 - t12).ToDouble()/range.Size()) * pixels;\n                    if (t12 >= t_min && t12 <= t_max) {\n                        ftd.Time = t12; ftd.Spec = fmt0;\n                        ticker.AddTick(t12.ToDouble(), false, 0, px_to_t2 >= fmt0_width, Formatter_Time, &ftd);\n                        if (last_major_offset < 0 && px_to_t2 >= fmt0_width && px_to_t2 >= (fmt1_width + fmtf_width) / 2) {\n                            ftd.Time = t12; ftd.Spec = fmtf;\n                            ImPlotTick& tick_maj = ticker.AddTick(t12.ToDouble(), true, 1, true, Formatter_Time, &ftd);\n                            last_major_offset = tick_maj.TextOffset;\n                        }\n                    }\n                    t12 = AddTime(t12, unit0, step);\n                }\n            }\n            t1 = t2;\n        }\n    }\n    else {\n        const ImPlotDateTimeSpec fmty = GetDateTimeFmt(TimeFormatLevel0, ImPlotTimeUnit_Yr);\n        const float label_width = GetDateTimeWidth(fmty);\n        const int   max_labels  = (int)(max_density * pixels / label_width);\n        const int year_min      = GetYear(t_min);\n        const int year_max      = GetYear(CeilTime(t_max, ImPlotTimeUnit_Yr));\n        const double nice_range = NiceNum((year_max - year_min)*0.99,false);\n        const double interval   = NiceNum(nice_range / (max_labels - 1), true);\n        const int graphmin      = (int)(floor(year_min / interval) * interval);\n        const int graphmax      = (int)(ceil(year_max  / interval) * interval);\n        const int step          = (int)interval <= 0 ? 1 : (int)interval;\n\n        for (int y = graphmin; y < graphmax; y += step) {\n            ImPlotTime t = MakeTime(y);\n            if (t >= t_min && t <= t_max) {\n                ftd.Time = t; ftd.Spec = fmty;\n                ticker.AddTick(t.ToDouble(), true, 0, true, Formatter_Time, &ftd);\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Context Menu\n//-----------------------------------------------------------------------------\n\ntemplate <typename F>\nbool DragFloat(const char*, F*, float, F, F) {\n    return false;\n}\n\ntemplate <>\nbool DragFloat<double>(const char* label, double* v, float v_speed, double v_min, double v_max) {\n    return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, \"%.3g\", 1);\n}\n\ntemplate <>\nbool DragFloat<float>(const char* label, float* v, float v_speed, float v_min, float v_max) {\n    return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, \"%.3g\", 1);\n}\n\ninline void BeginDisabledControls(bool cond) {\n    if (cond) {\n        ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);\n        ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f);\n    }\n}\n\ninline void EndDisabledControls(bool cond) {\n    if (cond) {\n        ImGui::PopItemFlag();\n        ImGui::PopStyleVar();\n    }\n}\n\nvoid ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool /*time_allowed*/) {\n\n    ImGui::PushItemWidth(75);\n    bool always_locked   = axis.IsRangeLocked() || axis.IsAutoFitting();\n    bool label           = axis.HasLabel();\n    bool grid            = axis.HasGridLines();\n    bool ticks           = axis.HasTickMarks();\n    bool labels          = axis.HasTickLabels();\n    double drag_speed    = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits.\n\n    if (axis.Scale == ImPlotScale_Time) {\n        ImPlotTime tmin = ImPlotTime::FromDouble(axis.Range.Min);\n        ImPlotTime tmax = ImPlotTime::FromDouble(axis.Range.Max);\n\n        BeginDisabledControls(always_locked);\n        ImGui::CheckboxFlags(\"##LockMin\", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin);\n        EndDisabledControls(always_locked);\n        ImGui::SameLine();\n        BeginDisabledControls(axis.IsLockedMin() || always_locked);\n        if (ImGui::BeginMenu(\"Min Time\")) {\n            if (ShowTimePicker(\"mintime\", &tmin)) {\n                if (tmin >= tmax)\n                    tmax = AddTime(tmin, ImPlotTimeUnit_S, 1);\n                axis.SetRange(tmin.ToDouble(),tmax.ToDouble());\n            }\n            ImGui::Separator();\n            if (ShowDatePicker(\"mindate\",&axis.PickerLevel,&axis.PickerTimeMin,&tmin,&tmax)) {\n                tmin = CombineDateTime(axis.PickerTimeMin, tmin);\n                if (tmin >= tmax)\n                    tmax = AddTime(tmin, ImPlotTimeUnit_S, 1);\n                axis.SetRange(tmin.ToDouble(), tmax.ToDouble());\n            }\n            ImGui::EndMenu();\n        }\n        EndDisabledControls(axis.IsLockedMin() || always_locked);\n\n        BeginDisabledControls(always_locked);\n        ImGui::CheckboxFlags(\"##LockMax\", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax);\n        EndDisabledControls(always_locked);\n        ImGui::SameLine();\n        BeginDisabledControls(axis.IsLockedMax() || always_locked);\n        if (ImGui::BeginMenu(\"Max Time\")) {\n            if (ShowTimePicker(\"maxtime\", &tmax)) {\n                if (tmax <= tmin)\n                    tmin = AddTime(tmax, ImPlotTimeUnit_S, -1);\n                axis.SetRange(tmin.ToDouble(),tmax.ToDouble());\n            }\n            ImGui::Separator();\n            if (ShowDatePicker(\"maxdate\",&axis.PickerLevel,&axis.PickerTimeMax,&tmin,&tmax)) {\n                tmax = CombineDateTime(axis.PickerTimeMax, tmax);\n                if (tmax <= tmin)\n                    tmin = AddTime(tmax, ImPlotTimeUnit_S, -1);\n                axis.SetRange(tmin.ToDouble(), tmax.ToDouble());\n            }\n            ImGui::EndMenu();\n        }\n        EndDisabledControls(axis.IsLockedMax() || always_locked);\n    }\n    else {\n        BeginDisabledControls(always_locked);\n        ImGui::CheckboxFlags(\"##LockMin\", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin);\n        EndDisabledControls(always_locked);\n        ImGui::SameLine();\n        BeginDisabledControls(axis.IsLockedMin() || always_locked);\n        double temp_min = axis.Range.Min;\n        if (DragFloat(\"Min\", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - DBL_EPSILON)) {\n            axis.SetMin(temp_min,true);\n            if (equal_axis != nullptr)\n                equal_axis->SetAspect(axis.GetAspect());\n        }\n        EndDisabledControls(axis.IsLockedMin() || always_locked);\n\n        BeginDisabledControls(always_locked);\n        ImGui::CheckboxFlags(\"##LockMax\", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax);\n        EndDisabledControls(always_locked);\n        ImGui::SameLine();\n        BeginDisabledControls(axis.IsLockedMax() || always_locked);\n        double temp_max = axis.Range.Max;\n        if (DragFloat(\"Max\", &temp_max, (float)drag_speed, axis.Range.Min + DBL_EPSILON, HUGE_VAL)) {\n            axis.SetMax(temp_max,true);\n            if (equal_axis != nullptr)\n                equal_axis->SetAspect(axis.GetAspect());\n        }\n        EndDisabledControls(axis.IsLockedMax() || always_locked);\n    }\n\n    ImGui::Separator();\n\n    ImGui::CheckboxFlags(\"Auto-Fit\",(unsigned int*)&axis.Flags, ImPlotAxisFlags_AutoFit);\n    // TODO\n    // BeginDisabledControls(axis.IsTime() && time_allowed);\n    // ImGui::CheckboxFlags(\"Log Scale\",(unsigned int*)&axis.Flags, ImPlotAxisFlags_LogScale);\n    // EndDisabledControls(axis.IsTime() && time_allowed);\n    // if (time_allowed) {\n    //     BeginDisabledControls(axis.IsLog() || axis.IsSymLog());\n    //     ImGui::CheckboxFlags(\"Time\",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Time);\n    //     EndDisabledControls(axis.IsLog() || axis.IsSymLog());\n    // }\n    ImGui::Separator();\n    ImGui::CheckboxFlags(\"Invert\",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Invert);\n    ImGui::CheckboxFlags(\"Opposite\",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Opposite);\n    ImGui::Separator();\n    BeginDisabledControls(axis.LabelOffset == -1);\n    if (ImGui::Checkbox(\"Label\", &label))\n        ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoLabel);\n    EndDisabledControls(axis.LabelOffset == -1);\n    if (ImGui::Checkbox(\"Grid Lines\", &grid))\n        ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoGridLines);\n    if (ImGui::Checkbox(\"Tick Marks\", &ticks))\n        ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks);\n    if (ImGui::Checkbox(\"Tick Labels\", &labels))\n        ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels);\n\n}\n\nbool ShowLegendContextMenu(ImPlotLegend& legend, bool visible) {\n    const float s = ImGui::GetFrameHeight();\n    bool ret = false;\n    if (ImGui::Checkbox(\"Show\",&visible))\n        ret = true;\n    if (legend.CanGoInside)\n        ImGui::CheckboxFlags(\"Outside\",(unsigned int*)&legend.Flags, ImPlotLegendFlags_Outside);\n    if (ImGui::RadioButton(\"H\", ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal)))\n        legend.Flags |= ImPlotLegendFlags_Horizontal;\n    ImGui::SameLine();\n    if (ImGui::RadioButton(\"V\", !ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal)))\n        legend.Flags &= ~ImPlotLegendFlags_Horizontal;\n    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2,2));\n    if (ImGui::Button(\"NW\",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthWest; } ImGui::SameLine();\n    if (ImGui::Button(\"N\", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_North;     } ImGui::SameLine();\n    if (ImGui::Button(\"NE\",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthEast; }\n    if (ImGui::Button(\"W\", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_West;      } ImGui::SameLine();\n    if (ImGui::InvisibleButton(\"C\", ImVec2(1.5f*s,s))) {     } ImGui::SameLine();\n    if (ImGui::Button(\"E\", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_East;      }\n    if (ImGui::Button(\"SW\",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthWest; } ImGui::SameLine();\n    if (ImGui::Button(\"S\", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_South;     } ImGui::SameLine();\n    if (ImGui::Button(\"SE\",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthEast; }\n    ImGui::PopStyleVar();\n    return ret;\n}\n\nvoid ShowSubplotsContextMenu(ImPlotSubplot& subplot) {\n    if ((ImGui::BeginMenu(\"Linking\"))) {\n        if (ImGui::MenuItem(\"Link Rows\",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows)))\n            ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows);\n        if (ImGui::MenuItem(\"Link Cols\",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols)))\n            ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols);\n        if (ImGui::MenuItem(\"Link All X\",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX)))\n            ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX);\n        if (ImGui::MenuItem(\"Link All Y\",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY)))\n            ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY);\n        ImGui::EndMenu();\n    }\n    if ((ImGui::BeginMenu(\"Settings\"))) {\n        BeginDisabledControls(!subplot.HasTitle);\n        if (ImGui::MenuItem(\"Title\",nullptr,subplot.HasTitle && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle)))\n            ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle);\n        EndDisabledControls(!subplot.HasTitle);\n        if (ImGui::MenuItem(\"Resizable\",nullptr,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize)))\n            ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoResize);\n        if (ImGui::MenuItem(\"Align\",nullptr,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign)))\n            ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign);\n        if (ImGui::MenuItem(\"Share Items\",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems)))\n            ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);\n        ImGui::EndMenu();\n    }\n}\n\nvoid ShowPlotContextMenu(ImPlotPlot& plot) {\n    ImPlotContext& gp = *GImPlot;\n    const bool owns_legend = gp.CurrentItems == &plot.Items;\n    const bool equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal);\n\n    char buf[16] = {};\n\n    for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {\n        ImPlotAxis& x_axis = plot.XAxis(i);\n        if (!x_axis.Enabled || !x_axis.HasMenus())\n            continue;\n        ImGui::PushID(i);\n        ImFormatString(buf, sizeof(buf) - 1, i == 0 ? \"X-Axis\" : \"X-Axis %d\", i + 1);\n        if (ImGui::BeginMenu(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : buf)) {\n            ShowAxisContextMenu(x_axis, equal ? x_axis.OrthoAxis : nullptr, false);\n            ImGui::EndMenu();\n        }\n        ImGui::PopID();\n    }\n\n    for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {\n        ImPlotAxis& y_axis = plot.YAxis(i);\n        if (!y_axis.Enabled || !y_axis.HasMenus())\n            continue;\n        ImGui::PushID(i);\n        ImFormatString(buf, sizeof(buf) - 1, i == 0 ? \"Y-Axis\" : \"Y-Axis %d\", i + 1);\n        if (ImGui::BeginMenu(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : buf)) {\n            ShowAxisContextMenu(y_axis, equal ? y_axis.OrthoAxis : nullptr, false);\n            ImGui::EndMenu();\n        }\n        ImGui::PopID();\n    }\n\n    ImGui::Separator();\n    if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoMenus)) {\n        if ((ImGui::BeginMenu(\"Legend\"))) {\n            if (owns_legend) {\n                if (ShowLegendContextMenu(plot.Items.Legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend)))\n                    ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend);\n            }\n            else if (gp.CurrentSubplot != nullptr) {\n                if (ShowLegendContextMenu(gp.CurrentSubplot->Items.Legend, !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend)))\n                    ImFlipFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend);\n            }\n            ImGui::EndMenu();\n        }\n    }\n    if ((ImGui::BeginMenu(\"Settings\"))) {\n        if (ImGui::MenuItem(\"Equal\", nullptr, ImHasFlag(plot.Flags, ImPlotFlags_Equal)))\n            ImFlipFlag(plot.Flags, ImPlotFlags_Equal);\n        if (ImGui::MenuItem(\"Box Select\",nullptr,!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect)))\n            ImFlipFlag(plot.Flags, ImPlotFlags_NoBoxSelect);\n        BeginDisabledControls(plot.TitleOffset == -1);\n        if (ImGui::MenuItem(\"Title\",nullptr,plot.HasTitle()))\n            ImFlipFlag(plot.Flags, ImPlotFlags_NoTitle);\n        EndDisabledControls(plot.TitleOffset == -1);\n        if (ImGui::MenuItem(\"Mouse Position\",nullptr,!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText)))\n            ImFlipFlag(plot.Flags, ImPlotFlags_NoMouseText);\n        if (ImGui::MenuItem(\"Crosshairs\",nullptr,ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs)))\n            ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs);\n        ImGui::EndMenu();\n    }\n    if (gp.CurrentSubplot != nullptr && !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoMenus)) {\n        ImGui::Separator();\n        if ((ImGui::BeginMenu(\"Subplots\"))) {\n            ShowSubplotsContextMenu(*gp.CurrentSubplot);\n            ImGui::EndMenu();\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Axis Utils\n//-----------------------------------------------------------------------------\n\nstatic inline int AxisPrecision(const ImPlotAxis& axis) {\n    const double range = axis.Ticker.TickCount() > 1 ? (axis.Ticker.Ticks[1].PlotPos - axis.Ticker.Ticks[0].PlotPos) : axis.Range.Size();\n    return Precision(range);\n}\n\nstatic inline double RoundAxisValue(const ImPlotAxis& axis, double value) {\n    return RoundTo(value, AxisPrecision(axis));\n}\n\nvoid LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round) {\n    ImPlotContext& gp = *GImPlot;\n    // TODO: We shouldn't explicitly check that the axis is Time here. Ideally,\n    // Formatter_Time would handle the formatting for us, but the code below\n    // needs additional arguments which are not currently available in ImPlotFormatter\n    if (axis.Locator == Locator_Time) {\n        ImPlotTimeUnit unit = axis.Vertical\n                            ? GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetHeight() / 100)) // TODO: magic value!\n                            : GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetWidth() / 100)); // TODO: magic value!\n        FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit));\n    }\n    else {\n        if (round)\n            value = RoundAxisValue(axis, value);\n        axis.Formatter(value, buff, size, axis.FormatterData);\n    }\n}\n\nvoid UpdateAxisColors(ImPlotAxis& axis) {\n    const ImVec4 col_grid = GetStyleColorVec4(ImPlotCol_AxisGrid);\n    axis.ColorMaj         = ImGui::GetColorU32(col_grid);\n    axis.ColorMin         = ImGui::GetColorU32(col_grid*ImVec4(1,1,1,GImPlot->Style.MinorAlpha));\n    axis.ColorTick        = GetStyleColorU32(ImPlotCol_AxisTick);\n    axis.ColorTxt         = GetStyleColorU32(ImPlotCol_AxisText);\n    axis.ColorBg          = GetStyleColorU32(ImPlotCol_AxisBg);\n    axis.ColorHov         = GetStyleColorU32(ImPlotCol_AxisBgHovered);\n    axis.ColorAct         = GetStyleColorU32(ImPlotCol_AxisBgActive);\n    // axis.ColorHiLi     = IM_COL32_BLACK_TRANS;\n}\n\nvoid PadAndDatumAxesX(ImPlotPlot& plot, float& pad_T, float& pad_B, ImPlotAlignmentData* align) {\n\n    ImPlotContext& gp = *GImPlot;\n\n    const float T = ImGui::GetTextLineHeight();\n    const float P = gp.Style.LabelPadding.y;\n    const float K = gp.Style.MinorTickLen.x;\n\n    int   count_T = 0;\n    int   count_B = 0;\n    float last_T  = plot.AxesRect.Min.y;\n    float last_B  = plot.AxesRect.Max.y;\n\n    for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { // FYI: can iterate forward\n        ImPlotAxis& axis = plot.XAxis(i);\n        if (!axis.Enabled)\n            continue;\n        const bool label = axis.HasLabel();\n        const bool ticks = axis.HasTickLabels();\n        const bool opp   = axis.IsOpposite();\n        const bool time  = axis.Scale == ImPlotScale_Time;\n        if (opp) {\n            if (count_T++ > 0)\n                pad_T += K + P;\n            if (label)\n                pad_T += T + P;\n            if (ticks)\n                pad_T += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0);\n            axis.Datum1 = plot.CanvasRect.Min.y + pad_T;\n            axis.Datum2 = last_T;\n            last_T = axis.Datum1;\n        }\n        else {\n            if (count_B++ > 0)\n                pad_B += K + P;\n            if (label)\n                pad_B += T + P;\n            if (ticks)\n                pad_B += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0);\n            axis.Datum1 = plot.CanvasRect.Max.y - pad_B;\n            axis.Datum2 = last_B;\n            last_B = axis.Datum1;\n        }\n    }\n\n    if (align) {\n        count_T = count_B = 0;\n        float delta_T, delta_B;\n        align->Update(pad_T,pad_B,delta_T,delta_B);\n        for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) {\n            ImPlotAxis& axis = plot.XAxis(i);\n            if (!axis.Enabled)\n                continue;\n            if (axis.IsOpposite()) {\n                axis.Datum1 += delta_T;\n                axis.Datum2 += count_T++ > 1 ? delta_T : 0;\n            }\n            else {\n                axis.Datum1 -= delta_B;\n                axis.Datum2 -= count_B++ > 1 ? delta_B : 0;\n            }\n        }\n    }\n}\n\nvoid PadAndDatumAxesY(ImPlotPlot& plot, float& pad_L, float& pad_R, ImPlotAlignmentData* align) {\n\n    //   [   pad_L   ]                 [   pad_R   ]\n    //   .................CanvasRect................\n    //   :TPWPK.PTPWP _____PlotRect____ PWPTP.KPWPT:\n    //   :A # |- A # |-               -| # A -| # A:\n    //   :X   |  X   |                 |   X  |   x:\n    //   :I # |- I # |-               -| # I -| # I:\n    //   :S   |  S   |                 |   S  |   S:\n    //   :3 # |- 0 # |-_______________-| # 1 -| # 2:\n    //   :.........................................:\n    //\n    //   T = text height\n    //   P = label padding\n    //   K = minor tick length\n    //   W = label width\n\n    ImPlotContext& gp = *GImPlot;\n\n    const float T = ImGui::GetTextLineHeight();\n    const float P = gp.Style.LabelPadding.x;\n    const float K = gp.Style.MinorTickLen.y;\n\n    int   count_L = 0;\n    int   count_R = 0;\n    float last_L  = plot.AxesRect.Min.x;\n    float last_R  = plot.AxesRect.Max.x;\n\n    for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { // FYI: can iterate forward\n        ImPlotAxis& axis = plot.YAxis(i);\n        if (!axis.Enabled)\n            continue;\n        const bool label = axis.HasLabel();\n        const bool ticks = axis.HasTickLabels();\n        const bool opp   = axis.IsOpposite();\n        if (opp) {\n            if (count_R++ > 0)\n                pad_R += K + P;\n            if (label)\n                pad_R += T + P;\n            if (ticks)\n                pad_R += axis.Ticker.MaxSize.x + P;\n            axis.Datum1 = plot.CanvasRect.Max.x - pad_R;\n            axis.Datum2 = last_R;\n            last_R = axis.Datum1;\n        }\n        else {\n            if (count_L++ > 0)\n                pad_L += K + P;\n            if (label)\n                pad_L += T + P;\n            if (ticks)\n                pad_L += axis.Ticker.MaxSize.x + P;\n            axis.Datum1 = plot.CanvasRect.Min.x + pad_L;\n            axis.Datum2 = last_L;\n            last_L = axis.Datum1;\n        }\n    }\n\n    plot.PlotRect.Min.x = plot.CanvasRect.Min.x + pad_L;\n    plot.PlotRect.Max.x = plot.CanvasRect.Max.x - pad_R;\n\n    if (align) {\n        count_L = count_R = 0;\n        float delta_L, delta_R;\n        align->Update(pad_L,pad_R,delta_L,delta_R);\n        for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) {\n            ImPlotAxis& axis = plot.YAxis(i);\n            if (!axis.Enabled)\n                continue;\n            if (axis.IsOpposite()) {\n                axis.Datum1 -= delta_R;\n                axis.Datum2 -= count_R++ > 1 ? delta_R : 0;\n            }\n            else {\n                axis.Datum1 += delta_L;\n                axis.Datum2 += count_L++ > 1 ? delta_L : 0;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// RENDERING\n//-----------------------------------------------------------------------------\n\nstatic inline void RenderGridLinesX(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) {\n    const float density   = ticker.TickCount() / rect.GetWidth();\n    ImVec4 col_min4  = ImGui::ColorConvertU32ToFloat4(col_min);\n    col_min4.w      *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f);\n    col_min = ImGui::ColorConvertFloat4ToU32(col_min4);\n    for (int t = 0; t < ticker.TickCount(); t++) {\n        const ImPlotTick& xt = ticker.Ticks[t];\n        if (xt.PixelPos < rect.Min.x || xt.PixelPos > rect.Max.x)\n            continue;\n        if (xt.Level == 0) {\n            if (xt.Major)\n                DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_maj, size_maj);\n            else if (density < 0.2f)\n                DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_min, size_min);\n        }\n    }\n}\n\nstatic inline void RenderGridLinesY(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) {\n    const float density   = ticker.TickCount() / rect.GetHeight();\n    ImVec4 col_min4  = ImGui::ColorConvertU32ToFloat4(col_min);\n    col_min4.w      *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f);\n    col_min = ImGui::ColorConvertFloat4ToU32(col_min4);\n    for (int t = 0; t < ticker.TickCount(); t++) {\n        const ImPlotTick& yt = ticker.Ticks[t];\n        if (yt.PixelPos < rect.Min.y || yt.PixelPos > rect.Max.y)\n            continue;\n        if (yt.Major)\n            DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_maj, size_maj);\n        else if (density < 0.2f)\n            DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_min, size_min);\n    }\n}\n\nstatic inline void RenderSelectionRect(ImDrawList& DrawList, const ImVec2& p_min, const ImVec2& p_max, const ImVec4& col) {\n    const ImU32 col_bg = ImGui::GetColorU32(col * ImVec4(1,1,1,0.25f));\n    const ImU32 col_bd = ImGui::GetColorU32(col);\n    DrawList.AddRectFilled(p_min, p_max, col_bg);\n    DrawList.AddRect(p_min, p_max, col_bd);\n}\n\n//-----------------------------------------------------------------------------\n// Input Handling\n//-----------------------------------------------------------------------------\n\nstatic const float MOUSE_CURSOR_DRAG_THRESHOLD = 5.0f;\nstatic const float BOX_SELECT_DRAG_THRESHOLD   = 4.0f;\n\nbool UpdateInput(ImPlotPlot& plot) {\n\n    bool changed = false;\n\n    ImPlotContext& gp = *GImPlot;\n    ImGuiIO& IO = ImGui::GetIO();\n\n    // BUTTON STATE -----------------------------------------------------------\n\n    const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowOverlap\n                                             | ImGuiButtonFlags_PressedOnClick\n                                             | ImGuiButtonFlags_PressedOnDoubleClick\n                                             | ImGuiButtonFlags_MouseButtonLeft\n                                             | ImGuiButtonFlags_MouseButtonRight\n                                             | ImGuiButtonFlags_MouseButtonMiddle;\n    const ImGuiButtonFlags axis_button_flags = ImGuiButtonFlags_FlattenChildren\n                                             | plot_button_flags;\n\n    const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect,plot.ID,&plot.Hovered,&plot.Held,plot_button_flags);\n#if (IMGUI_VERSION_NUM < 18966)\n    ImGui::SetItemAllowOverlap(); // Handled by ButtonBehavior()\n#endif\n\n    if (plot_clicked) {\n        if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && IO.MouseClicked[gp.InputMap.Select] && ImHasFlag(IO.KeyMods, gp.InputMap.SelectMod)) {\n            plot.Selecting   = true;\n            plot.SelectStart = IO.MousePos;\n            plot.SelectRect  = ImRect(0,0,0,0);\n        }\n        if (IO.MouseDoubleClicked[gp.InputMap.Fit]) {\n            plot.FitThisFrame = true;\n            for (int i = 0; i < ImAxis_COUNT; ++i)\n                plot.Axes[i].FitThisFrame = true;\n        }\n    }\n\n    const bool can_pan = IO.MouseDown[gp.InputMap.Pan] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod);\n\n    plot.Held = plot.Held && can_pan;\n\n    bool x_click[IMPLOT_NUM_X_AXES] = {false};\n    bool x_held[IMPLOT_NUM_X_AXES]  = {false};\n    bool x_hov[IMPLOT_NUM_X_AXES]   = {false};\n\n    bool y_click[IMPLOT_NUM_Y_AXES] = {false};\n    bool y_held[IMPLOT_NUM_Y_AXES]  = {false};\n    bool y_hov[IMPLOT_NUM_Y_AXES]   = {false};\n\n    for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {\n        ImPlotAxis& xax = plot.XAxis(i);\n        if (xax.Enabled) {\n            ImGui::KeepAliveID(xax.ID);\n            x_click[i]  = ImGui::ButtonBehavior(xax.HoverRect,xax.ID,&xax.Hovered,&xax.Held,axis_button_flags);\n            if (x_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit])\n                plot.FitThisFrame = xax.FitThisFrame = true;\n            xax.Held  = xax.Held && can_pan;\n            x_hov[i]  = xax.Hovered || plot.Hovered;\n            x_held[i] = xax.Held    || plot.Held;\n        }\n    }\n\n    for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) {\n        ImPlotAxis& yax = plot.YAxis(i);\n        if (yax.Enabled) {\n            ImGui::KeepAliveID(yax.ID);\n            y_click[i]  = ImGui::ButtonBehavior(yax.HoverRect,yax.ID,&yax.Hovered,&yax.Held,axis_button_flags);\n            if (y_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit])\n                plot.FitThisFrame = yax.FitThisFrame = true;\n            yax.Held  = yax.Held && can_pan;\n            y_hov[i]  = yax.Hovered || plot.Hovered;\n            y_held[i] = yax.Held    || plot.Held;\n        }\n    }\n\n    // cancel due to DND activity\n    if (GImGui->DragDropActive || (IO.KeyMods == gp.InputMap.OverrideMod && gp.InputMap.OverrideMod != 0))\n        return false;\n\n    // STATE -------------------------------------------------------------------\n\n    const bool axis_equal      = ImHasFlag(plot.Flags, ImPlotFlags_Equal);\n\n    const bool any_x_hov       = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES);\n    const bool any_x_held      = plot.Held    || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES);\n    const bool any_y_hov       = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);\n    const bool any_y_held      = plot.Held    || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);\n    const bool any_hov         = any_x_hov    || any_y_hov;\n    const bool any_held        = any_x_held   || any_y_held;\n\n    const ImVec2 select_drag   = ImGui::GetMouseDragDelta(gp.InputMap.Select);\n    const ImVec2 pan_drag      = ImGui::GetMouseDragDelta(gp.InputMap.Pan);\n    const float select_drag_sq = ImLengthSqr(select_drag);\n    const float pan_drag_sq    = ImLengthSqr(pan_drag);\n    const bool selecting       = plot.Selecting && select_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD;\n    const bool panning         = any_held       && pan_drag_sq    > MOUSE_CURSOR_DRAG_THRESHOLD;\n\n    // CONTEXT MENU -----------------------------------------------------------\n\n    if (IO.MouseReleased[gp.InputMap.Menu] && !plot.ContextLocked)\n        gp.OpenContextThisFrame = true;\n\n    if (selecting || panning)\n        plot.ContextLocked = true;\n    else if (!(IO.MouseDown[gp.InputMap.Menu] || IO.MouseReleased[gp.InputMap.Menu]))\n        plot.ContextLocked = false;\n\n    // DRAG INPUT -------------------------------------------------------------\n\n    if (any_held && !plot.Selecting) {\n        int drag_direction = 0;\n        for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {\n            ImPlotAxis& x_axis = plot.XAxis(i);\n            if (x_held[i] && !x_axis.IsInputLocked()) {\n                drag_direction |= (1 << 1);\n                bool increasing = x_axis.IsInverted() ? IO.MouseDelta.x > 0 : IO.MouseDelta.x < 0;\n                if (IO.MouseDelta.x != 0 && !x_axis.IsPanLocked(increasing)) {\n                    const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - IO.MouseDelta.x);\n                    const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x - IO.MouseDelta.x);\n                    x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l);\n                    x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r);\n                    if (axis_equal && x_axis.OrthoAxis != nullptr)\n                        x_axis.OrthoAxis->SetAspect(x_axis.GetAspect());\n                    changed = true;\n                }\n            }\n        }\n        for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {\n            ImPlotAxis& y_axis = plot.YAxis(i);\n            if (y_held[i] && !y_axis.IsInputLocked()) {\n                drag_direction |= (1 << 2);\n                bool increasing = y_axis.IsInverted() ? IO.MouseDelta.y < 0 : IO.MouseDelta.y > 0;\n                if (IO.MouseDelta.y != 0 && !y_axis.IsPanLocked(increasing)) {\n                    const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - IO.MouseDelta.y);\n                    const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y - IO.MouseDelta.y);\n                    y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b);\n                    y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t);\n                    if (axis_equal && y_axis.OrthoAxis != nullptr)\n                        y_axis.OrthoAxis->SetAspect(y_axis.GetAspect());\n                    changed = true;\n                }\n            }\n        }\n        if (IO.MouseDragMaxDistanceSqr[gp.InputMap.Pan] > MOUSE_CURSOR_DRAG_THRESHOLD) {\n            switch (drag_direction) {\n                case 0        : ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); break;\n                case (1 << 1) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);   break;\n                case (1 << 2) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);   break;\n                default       : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);  break;\n            }\n        }\n    }\n\n    // SCROLL INPUT -----------------------------------------------------------\n\n    if (any_hov && ImHasFlag(IO.KeyMods, gp.InputMap.ZoomMod)) {\n\n        float zoom_rate = gp.InputMap.ZoomRate;\n        if (IO.MouseWheel == 0.0f) \n            zoom_rate = 0;        \n        else if (IO.MouseWheel > 0) \n            zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate));\n        ImVec2 rect_size = plot.PlotRect.GetSize();\n        float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f);\n        float ty = ImRemap(IO.MousePos.y, plot.PlotRect.Min.y, plot.PlotRect.Max.y, 0.0f, 1.0f);\n\n        for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {\n            ImPlotAxis& x_axis = plot.XAxis(i);\n            const bool equal_zoom   = axis_equal && x_axis.OrthoAxis != nullptr;\n            const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked();\n            if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) {\n                ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);\n                if (zoom_rate != 0.0f) {\n                    float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;\n                    const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction);\n                    const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction);\n                    x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l);\n                    x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r);\n                    if (axis_equal && x_axis.OrthoAxis != nullptr)\n                        x_axis.OrthoAxis->SetAspect(x_axis.GetAspect());\n                    changed = true;\n                }\n            }\n        }\n        for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {\n            ImPlotAxis& y_axis = plot.YAxis(i);\n            const bool equal_zoom   = axis_equal && y_axis.OrthoAxis != nullptr;\n            const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked();\n            if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) {\n                ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);\n                if (zoom_rate != 0.0f) {\n                    float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;\n                    const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction);\n                    const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction);\n                    y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b);\n                    y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t);\n                    if (axis_equal && y_axis.OrthoAxis != nullptr)\n                        y_axis.OrthoAxis->SetAspect(y_axis.GetAspect());\n                    changed = true;\n                }\n            }\n        }\n    }\n\n    // BOX-SELECTION ----------------------------------------------------------\n\n    if (plot.Selecting) {\n        const ImVec2 d = plot.SelectStart - IO.MousePos;\n        const bool x_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImFabs(d.x) > 2;\n        const bool y_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod) && ImFabs(d.y) > 2;\n        // confirm\n        if (IO.MouseReleased[gp.InputMap.Select]) {\n            for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {\n                ImPlotAxis& x_axis = plot.XAxis(i);\n                if (!x_axis.IsInputLocked() && x_can_change) {\n                    const double p1 = x_axis.PixelsToPlot(plot.SelectStart.x);\n                    const double p2 = x_axis.PixelsToPlot(IO.MousePos.x);\n                    x_axis.SetMin(ImMin(p1, p2));\n                    x_axis.SetMax(ImMax(p1, p2));\n                    changed = true;\n                }\n            }\n            for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {\n                ImPlotAxis& y_axis = plot.YAxis(i);\n                if (!y_axis.IsInputLocked() && y_can_change) {\n                    const double p1 = y_axis.PixelsToPlot(plot.SelectStart.y);\n                    const double p2 = y_axis.PixelsToPlot(IO.MousePos.y);\n                    y_axis.SetMin(ImMin(p1, p2));\n                    y_axis.SetMax(ImMax(p1, p2));\n                    changed = true;\n                }\n            }\n            if (x_can_change || y_can_change || (ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod)))\n                gp.OpenContextThisFrame = false;\n            plot.Selected = plot.Selecting = false;\n        }\n        // cancel\n        else if (IO.MouseReleased[gp.InputMap.SelectCancel]) {\n            plot.Selected = plot.Selecting = false;\n            gp.OpenContextThisFrame = false;\n        }\n        else if (ImLengthSqr(d) > BOX_SELECT_DRAG_THRESHOLD) {\n            // bad selection\n            if (plot.IsInputLocked()) {\n                ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);\n                gp.OpenContextThisFrame = false;\n                plot.Selected      = false;\n            }\n            else {\n                // TODO: Handle only min or max locked cases\n                const bool full_width  = ImHasFlag(IO.KeyMods, gp.InputMap.SelectHorzMod) || AllAxesInputLocked(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES);\n                const bool full_height = ImHasFlag(IO.KeyMods, gp.InputMap.SelectVertMod) || AllAxesInputLocked(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);\n                plot.SelectRect.Min.x = full_width  ? plot.PlotRect.Min.x : ImMin(plot.SelectStart.x, IO.MousePos.x);\n                plot.SelectRect.Max.x = full_width  ? plot.PlotRect.Max.x : ImMax(plot.SelectStart.x, IO.MousePos.x);\n                plot.SelectRect.Min.y = full_height ? plot.PlotRect.Min.y : ImMin(plot.SelectStart.y, IO.MousePos.y);\n                plot.SelectRect.Max.y = full_height ? plot.PlotRect.Max.y : ImMax(plot.SelectStart.y, IO.MousePos.y);\n                plot.SelectRect.Min  -= plot.PlotRect.Min;\n                plot.SelectRect.Max  -= plot.PlotRect.Min;\n                plot.Selected = true;\n            }\n        }\n        else {\n            plot.Selected = false;\n        }\n    }\n    return changed;\n}\n\n//-----------------------------------------------------------------------------\n// Next Plot Data (Legacy)\n//-----------------------------------------------------------------------------\n\nvoid ApplyNextPlotData(ImAxis idx) {\n    ImPlotContext& gp = *GImPlot;\n    ImPlotPlot& plot  = *gp.CurrentPlot;\n    ImPlotAxis& axis  = plot.Axes[idx];\n    if (!axis.Enabled)\n        return;\n    double*     npd_lmin = gp.NextPlotData.LinkedMin[idx];\n    double*     npd_lmax = gp.NextPlotData.LinkedMax[idx];\n    bool        npd_rngh = gp.NextPlotData.HasRange[idx];\n    ImPlotCond  npd_rngc = gp.NextPlotData.RangeCond[idx];\n    ImPlotRange     npd_rngv = gp.NextPlotData.Range[idx];\n    axis.LinkedMin = npd_lmin;\n    axis.LinkedMax = npd_lmax;\n    axis.PullLinks();\n    if (npd_rngh) {\n        if (!plot.Initialized || npd_rngc == ImPlotCond_Always)\n            axis.SetRange(npd_rngv);\n    }\n    axis.HasRange         = npd_rngh;\n    axis.RangeCond        = npd_rngc;\n}\n\n//-----------------------------------------------------------------------------\n// Setup\n//-----------------------------------------------------------------------------\n\nvoid SetupAxis(ImAxis idx, const char* label, ImPlotAxisFlags flags) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    // get plot and axis\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& axis = plot.Axes[idx];\n    // set ID\n    axis.ID = plot.ID + idx + 1;\n    // check and set flags\n    if (plot.JustCreated || flags != axis.PreviousFlags)\n        axis.Flags = flags;\n    axis.PreviousFlags = flags;\n    // enable axis\n    axis.Enabled = true;\n    // set label\n    plot.SetAxisLabel(axis,label);\n    // cache colors\n    UpdateAxisColors(axis);\n}\n\nvoid SetupAxisLimits(ImAxis idx, double min_lim, double max_lim, ImPlotCond cond) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");    // get plot and axis\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& axis = plot.Axes[idx];\n    IM_ASSERT_USER_ERROR(axis.Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    if (!plot.Initialized || cond == ImPlotCond_Always)\n        axis.SetRange(min_lim, max_lim);\n    axis.HasRange  = true;\n    axis.RangeCond = cond;\n}\n\nvoid SetupAxisFormat(ImAxis idx, const char* fmt) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& axis = plot.Axes[idx];\n    IM_ASSERT_USER_ERROR(axis.Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    axis.HasFormatSpec = fmt != nullptr;\n    if (fmt != nullptr)\n        ImStrncpy(axis.FormatSpec,fmt,sizeof(axis.FormatSpec));\n}\n\nvoid SetupAxisLinks(ImAxis idx, double* min_lnk, double* max_lnk) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& axis = plot.Axes[idx];\n    IM_ASSERT_USER_ERROR(axis.Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    axis.LinkedMin = min_lnk;\n    axis.LinkedMax = max_lnk;\n    axis.PullLinks();\n}\n\nvoid SetupAxisFormat(ImAxis idx, ImPlotFormatter formatter, void* data) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& axis = plot.Axes[idx];\n    IM_ASSERT_USER_ERROR(axis.Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    axis.Formatter = formatter;\n    axis.FormatterData = data;\n}\n\nvoid SetupAxisTicks(ImAxis idx, const double* values, int n_ticks, const char* const labels[], bool show_default) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                        \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& axis = plot.Axes[idx];\n    IM_ASSERT_USER_ERROR(axis.Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    axis.ShowDefaultTicks = show_default;\n    AddTicksCustom(values,\n                  labels,\n                  n_ticks,\n                  axis.Ticker,\n                  axis.Formatter ? axis.Formatter : Formatter_Default,\n                  (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT);\n}\n\nvoid SetupAxisTicks(ImAxis idx, double v_min, double v_max, int n_ticks, const char* const labels[], bool show_default) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    n_ticks = n_ticks < 2 ? 2 : n_ticks;\n    FillRange(gp.TempDouble1, n_ticks, v_min, v_max);\n    SetupAxisTicks(idx, gp.TempDouble1.Data, n_ticks, labels, show_default);\n}\n\nvoid SetupAxisScale(ImAxis idx, ImPlotScale scale) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                        \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& axis = plot.Axes[idx];\n    IM_ASSERT_USER_ERROR(axis.Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    axis.Scale = scale;\n    switch (scale)\n    {\n    case ImPlotScale_Time:\n        axis.TransformForward = nullptr;\n        axis.TransformInverse = nullptr;\n        axis.TransformData    = nullptr;\n        axis.Locator          = Locator_Time;\n        axis.ConstraintRange  = ImPlotRange(IMPLOT_MIN_TIME, IMPLOT_MAX_TIME);\n        axis.Ticker.Levels    = 2;\n        break;\n    case ImPlotScale_Log10:\n        axis.TransformForward = TransformForward_Log10;\n        axis.TransformInverse = TransformInverse_Log10;\n        axis.TransformData    = nullptr;\n        axis.Locator          = Locator_Log10;\n        axis.ConstraintRange  = ImPlotRange(DBL_MIN, INFINITY);\n        break;\n    case ImPlotScale_SymLog:\n        axis.TransformForward = TransformForward_SymLog;\n        axis.TransformInverse = TransformInverse_SymLog;\n        axis.TransformData    = nullptr;\n        axis.Locator          = Locator_SymLog;\n        axis.ConstraintRange  = ImPlotRange(-INFINITY, INFINITY);\n        break;\n    default:\n        axis.TransformForward = nullptr;\n        axis.TransformInverse = nullptr;\n        axis.TransformData    = nullptr;\n        axis.Locator          = nullptr;\n        axis.ConstraintRange  = ImPlotRange(-INFINITY, INFINITY);\n        break;\n    }\n}\n\nvoid SetupAxisScale(ImAxis idx, ImPlotTransform fwd, ImPlotTransform inv, void* data) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                        \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& axis = plot.Axes[idx];\n    IM_ASSERT_USER_ERROR(axis.Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    axis.Scale = IMPLOT_AUTO;\n    axis.TransformForward = fwd;\n    axis.TransformInverse = inv;\n    axis.TransformData = data;\n}\n\nvoid SetupAxisLimitsConstraints(ImAxis idx, double v_min, double v_max) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                        \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& axis = plot.Axes[idx];\n    IM_ASSERT_USER_ERROR(axis.Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    axis.ConstraintRange.Min = v_min;\n    axis.ConstraintRange.Max = v_max;\n}\n\nvoid SetupAxisZoomConstraints(ImAxis idx, double z_min, double z_max) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                        \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& axis = plot.Axes[idx];\n    IM_ASSERT_USER_ERROR(axis.Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    axis.ConstraintZoom.Min = z_min;\n    axis.ConstraintZoom.Max = z_max;\n}\n\nvoid SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags) {\n    SetupAxis(ImAxis_X1, x_label, x_flags);\n    SetupAxis(ImAxis_Y1, y_label, y_flags);\n}\n\nvoid SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) {\n    SetupAxisLimits(ImAxis_X1, x_min, x_max, cond);\n    SetupAxisLimits(ImAxis_Y1, y_min, y_max, cond);\n}\n\nvoid SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR((gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked) || (gp.CurrentSubplot != nullptr && gp.CurrentPlot == nullptr),\n                         \"Setup needs to be called after BeginPlot or BeginSubplots and before any setup locking functions (e.g. PlotX)!\");\n    if (gp.CurrentItems) {\n        ImPlotLegend& legend = gp.CurrentItems->Legend;\n        // check and set location\n        if (location != legend.PreviousLocation)\n            legend.Location = location;\n        legend.PreviousLocation = location;\n        // check and set flags\n        if (flags != legend.PreviousFlags)\n            legend.Flags = flags;\n        legend.PreviousFlags = flags;\n    }\n}\n\nvoid SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    gp.CurrentPlot->MouseTextLocation = location;\n    gp.CurrentPlot->MouseTextFlags = flags;\n}\n\n//-----------------------------------------------------------------------------\n// SetNext\n//-----------------------------------------------------------------------------\n\nvoid SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, \"SetNextAxisLimits() needs to be called before BeginPlot()!\");\n    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.\n    gp.NextPlotData.HasRange[axis]  = true;\n    gp.NextPlotData.RangeCond[axis] = cond;\n    gp.NextPlotData.Range[axis].Min = v_min;\n    gp.NextPlotData.Range[axis].Max = v_max;\n}\n\nvoid SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, \"SetNextAxisLinks() needs to be called before BeginPlot()!\");\n    gp.NextPlotData.LinkedMin[axis] = link_min;\n    gp.NextPlotData.LinkedMax[axis] = link_max;\n}\n\nvoid SetNextAxisToFit(ImAxis axis) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, \"SetNextAxisToFit() needs to be called before BeginPlot()!\");\n    gp.NextPlotData.Fit[axis] = true;\n}\n\nvoid SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) {\n    SetNextAxisLimits(ImAxis_X1, x_min, x_max, cond);\n    SetNextAxisLimits(ImAxis_Y1, y_min, y_max, cond);\n}\n\nvoid SetNextAxesToFit() {\n    for (int i = 0; i < ImAxis_COUNT; ++i)\n        SetNextAxisToFit(i);\n}\n\n//-----------------------------------------------------------------------------\n// BeginPlot\n//-----------------------------------------------------------------------------\n\nbool BeginPlot(const char* title_id, const ImVec2& size, ImPlotFlags flags) {\n    IM_ASSERT_USER_ERROR(GImPlot != nullptr, \"No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?\");\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, \"Mismatched BeginPlot()/EndPlot()!\");\n\n    // FRONT MATTER -----------------------------------------------------------\n\n    if (gp.CurrentSubplot != nullptr)\n        ImGui::PushID(gp.CurrentSubplot->CurrentIdx);\n\n    // get globals\n    ImGuiContext &G          = *GImGui;\n    ImGuiWindow* Window      = G.CurrentWindow;\n\n    // skip if needed\n    if (Window->SkipItems && !gp.CurrentSubplot) {\n        ResetCtxForNextPlot(GImPlot);\n        return false;\n    }\n\n    // ID and age (TODO: keep track of plot age in frames)\n    const ImGuiID ID         = Window->GetID(title_id);\n    const bool just_created  = gp.Plots.GetByKey(ID) == nullptr;\n    gp.CurrentPlot           = gp.Plots.GetOrAddByKey(ID);\n\n    ImPlotPlot &plot         = *gp.CurrentPlot;\n    plot.ID                  = ID;\n    plot.Items.ID            = ID - 1;\n    plot.JustCreated         = just_created;\n    plot.SetupLocked         = false;\n\n    // check flags\n    if (plot.JustCreated)\n        plot.Flags = flags;\n    else if (flags != plot.PreviousFlags)\n        plot.Flags = flags;\n    plot.PreviousFlags = flags;\n\n    // setup default axes\n    if (plot.JustCreated) {\n        SetupAxis(ImAxis_X1);\n        SetupAxis(ImAxis_Y1);\n    }\n\n    // reset axes\n    for (int i = 0; i < ImAxis_COUNT; ++i) {\n        plot.Axes[i].Reset();\n        UpdateAxisColors(plot.Axes[i]);\n    }\n    // ensure first axes enabled\n    plot.Axes[ImAxis_X1].Enabled = true;\n    plot.Axes[ImAxis_Y1].Enabled = true;\n    // set initial axes\n    plot.CurrentX = ImAxis_X1;\n    plot.CurrentY = ImAxis_Y1;\n\n    // process next plot data (legacy)\n    for (int i = 0; i < ImAxis_COUNT; ++i)\n        ApplyNextPlotData(i);\n\n    // clear text buffers\n    plot.ClearTextBuffer();\n    plot.SetTitle(title_id);\n\n    // set frame size\n    ImVec2 frame_size;\n    if (gp.CurrentSubplot != nullptr)\n        frame_size = gp.CurrentSubplot->CellSize;\n    else\n        frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y);\n\n    if (frame_size.x < gp.Style.PlotMinSize.x && (size.x < 0.0f || gp.CurrentSubplot != nullptr))\n        frame_size.x = gp.Style.PlotMinSize.x;\n    if (frame_size.y < gp.Style.PlotMinSize.y && (size.y < 0.0f || gp.CurrentSubplot != nullptr))\n        frame_size.y = gp.Style.PlotMinSize.y;\n\n    plot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);\n    ImGui::ItemSize(plot.FrameRect);\n    if (!ImGui::ItemAdd(plot.FrameRect, plot.ID, &plot.FrameRect) && !gp.CurrentSubplot) {\n        ResetCtxForNextPlot(GImPlot);\n        return false;\n    }\n    \n    // setup items (or dont)\n    if (gp.CurrentItems == nullptr)\n        gp.CurrentItems = &plot.Items;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// SetupFinish\n//-----------------------------------------------------------------------------\n\nvoid SetupFinish() {\n    IM_ASSERT_USER_ERROR(GImPlot != nullptr, \"No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?\");\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"SetupFinish needs to be called after BeginPlot!\");\n\n    ImGuiContext& G         = *GImGui;\n    ImDrawList& DrawList    = *G.CurrentWindow->DrawList;\n    const ImGuiStyle& Style = G.Style;\n\n    ImPlotPlot &plot  = *gp.CurrentPlot;\n\n    // lock setup\n    plot.SetupLocked = true;\n\n    // finalize axes and set default formatter/locator\n    for (int i = 0; i < ImAxis_COUNT; ++i) {\n        ImPlotAxis& axis = plot.Axes[i];\n        if (axis.Enabled) {\n            axis.Constrain();\n            if (!plot.Initialized && axis.CanInitFit())\n                plot.FitThisFrame = axis.FitThisFrame = true;\n        }\n        if (axis.Formatter == nullptr) {\n            axis.Formatter = Formatter_Default;\n            if (axis.HasFormatSpec)\n                axis.FormatterData = axis.FormatSpec;\n            else\n                axis.FormatterData = (void*)IMPLOT_LABEL_FORMAT;\n        }\n        if (axis.Locator == nullptr) {\n            axis.Locator = Locator_Default;\n        }\n    }\n\n    // setup nullptr orthogonal axes\n    const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal);\n    for (int ix = ImAxis_X1, iy = ImAxis_Y1; ix < ImAxis_Y1 || iy < ImAxis_COUNT; ++ix, ++iy) {\n        ImPlotAxis& x_axis = plot.Axes[ix];\n        ImPlotAxis& y_axis = plot.Axes[iy];\n        if (x_axis.Enabled && y_axis.Enabled) {\n            if (x_axis.OrthoAxis == nullptr)\n                x_axis.OrthoAxis = &y_axis;\n            if (y_axis.OrthoAxis == nullptr)\n                y_axis.OrthoAxis = &x_axis;\n        }\n        else if (x_axis.Enabled)\n        {\n            if (x_axis.OrthoAxis == nullptr && !axis_equal)\n                x_axis.OrthoAxis = &plot.Axes[ImAxis_Y1];\n        }\n        else if (y_axis.Enabled) {\n            if (y_axis.OrthoAxis == nullptr && !axis_equal)\n                y_axis.OrthoAxis = &plot.Axes[ImAxis_X1];\n        }\n    }\n\n    // canvas/axes bb\n    plot.CanvasRect = ImRect(plot.FrameRect.Min + gp.Style.PlotPadding, plot.FrameRect.Max - gp.Style.PlotPadding);\n    plot.AxesRect   = plot.FrameRect;\n\n    // outside legend adjustments\n    if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0 && ImHasFlag(plot.Items.Legend.Flags, ImPlotLegendFlags_Outside)) {\n        ImPlotLegend& legend = plot.Items.Legend;\n        const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal);\n        const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz);\n        const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East);\n        const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West);\n        const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South);\n        const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North);\n        if ((west && !horz) || (west && horz && !north && !south)) {\n            plot.CanvasRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x);\n            plot.AxesRect.Min.x   += (legend_size.x + gp.Style.PlotPadding.x);\n        }\n        if ((east && !horz) || (east && horz && !north && !south)) {\n            plot.CanvasRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x);\n            plot.AxesRect.Max.x   -= (legend_size.x + gp.Style.PlotPadding.x);\n        }\n        if ((north && horz) || (north && !horz && !west && !east)) {\n            plot.CanvasRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y);\n            plot.AxesRect.Min.y   += (legend_size.y + gp.Style.PlotPadding.y);\n        }\n        if ((south && horz) || (south && !horz && !west && !east)) {\n            plot.CanvasRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y);\n            plot.AxesRect.Max.y   -= (legend_size.y + gp.Style.PlotPadding.y);\n        }\n    }\n\n    // plot bb\n    float pad_top = 0, pad_bot = 0, pad_left = 0, pad_right = 0;\n\n    // (0) calc top padding form title\n    ImVec2 title_size(0.0f, 0.0f);\n    if (plot.HasTitle())\n         title_size = ImGui::CalcTextSize(plot.GetTitle(), nullptr, true);\n    if (title_size.x > 0) {\n        pad_top += title_size.y + gp.Style.LabelPadding.y;\n        plot.AxesRect.Min.y += gp.Style.PlotPadding.y + pad_top;\n    }\n\n    // (1) calc addition top padding and bot padding\n    PadAndDatumAxesX(plot,pad_top,pad_bot,gp.CurrentAlignmentH);\n\n    const float plot_height = plot.CanvasRect.GetHeight() - pad_top - pad_bot;\n\n    // (2) get y tick labels (needed for left/right pad)\n    for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {\n        ImPlotAxis& axis = plot.YAxis(i);\n        if (axis.WillRender() && axis.ShowDefaultTicks && plot_height > 0) {\n            axis.Locator(axis.Ticker, axis.Range, plot_height, true, axis.Formatter, axis.FormatterData);\n        }\n    }\n\n    // (3) calc left/right pad\n    PadAndDatumAxesY(plot,pad_left,pad_right,gp.CurrentAlignmentV);\n\n    const float plot_width = plot.CanvasRect.GetWidth() - pad_left - pad_right;\n\n    // (4) get x ticks\n    for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {\n        ImPlotAxis& axis = plot.XAxis(i);\n        if (axis.WillRender() && axis.ShowDefaultTicks && plot_width > 0) {\n            axis.Locator(axis.Ticker, axis.Range, plot_width, false, axis.Formatter, axis.FormatterData);\n        }\n    }\n\n    // (5) calc plot bb\n    plot.PlotRect = ImRect(plot.CanvasRect.Min + ImVec2(pad_left, pad_top), plot.CanvasRect.Max - ImVec2(pad_right, pad_bot));\n\n    // HOVER------------------------------------------------------------\n\n    // axes hover rect, pixel ranges\n    for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {\n        ImPlotAxis& xax = plot.XAxis(i);\n        xax.HoverRect   = ImRect(ImVec2(plot.PlotRect.Min.x, ImMin(xax.Datum1,xax.Datum2)),\n                                 ImVec2(plot.PlotRect.Max.x, ImMax(xax.Datum1,xax.Datum2)));\n        xax.PixelMin    = xax.IsInverted() ? plot.PlotRect.Max.x : plot.PlotRect.Min.x;\n        xax.PixelMax    = xax.IsInverted() ? plot.PlotRect.Min.x : plot.PlotRect.Max.x;\n        xax.UpdateTransformCache();\n    }\n\n    for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) {\n        ImPlotAxis& yax = plot.YAxis(i);\n        yax.HoverRect   = ImRect(ImVec2(ImMin(yax.Datum1,yax.Datum2),plot.PlotRect.Min.y),\n                                 ImVec2(ImMax(yax.Datum1,yax.Datum2),plot.PlotRect.Max.y));\n        yax.PixelMin    = yax.IsInverted() ? plot.PlotRect.Min.y : plot.PlotRect.Max.y;\n        yax.PixelMax    = yax.IsInverted() ? plot.PlotRect.Max.y : plot.PlotRect.Min.y;\n        yax.UpdateTransformCache();\n    }\n    // Equal axis constraint. Must happen after we set Pixels\n    // constrain equal axes for primary x and y if not approximately equal\n    // constrains x to y since x pixel size depends on y labels width, and causes feedback loops in opposite case\n    if (axis_equal) {\n        for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {\n            ImPlotAxis& x_axis = plot.XAxis(i);\n            if (x_axis.OrthoAxis == nullptr)\n                continue;\n            double xar = x_axis.GetAspect();\n            double yar = x_axis.OrthoAxis->GetAspect();\n            // edge case: user has set x range this frame, so fit y to x so that we honor their request for x range\n            // NB: because of feedback across several frames, the user's x request may not be perfectly honored\n            if (x_axis.HasRange)\n                x_axis.OrthoAxis->SetAspect(xar);\n            else if (!ImAlmostEqual(xar,yar) && !x_axis.OrthoAxis->IsInputLocked())\n                 x_axis.SetAspect(yar);\n        }\n    }\n\n    // INPUT ------------------------------------------------------------------\n    if (!ImHasFlag(plot.Flags, ImPlotFlags_NoInputs))\n        UpdateInput(plot);\n\n    // fit from FitNextPlotAxes or auto fit\n    for (int i = 0; i < ImAxis_COUNT; ++i) {\n        if (gp.NextPlotData.Fit[i] || plot.Axes[i].IsAutoFitting()) {\n            plot.FitThisFrame = true;\n            plot.Axes[i].FitThisFrame = true;\n        }\n    }\n\n    // RENDER -----------------------------------------------------------------\n\n    const float txt_height = ImGui::GetTextLineHeight();\n\n    // render frame\n    if (!ImHasFlag(plot.Flags, ImPlotFlags_NoFrame))\n        ImGui::RenderFrame(plot.FrameRect.Min, plot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding);\n\n    // grid bg\n    DrawList.AddRectFilled(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBg));\n\n    // transform ticks\n    for (int i = 0; i < ImAxis_COUNT; i++) {\n        ImPlotAxis& axis = plot.Axes[i];\n        if (axis.WillRender()) {\n            for (int t = 0; t < axis.Ticker.TickCount(); t++) {\n                ImPlotTick& tk = axis.Ticker.Ticks[t];\n                tk.PixelPos = IM_ROUND(axis.PlotToPixels(tk.PlotPos));\n            }\n        }\n    }\n\n    // render grid (background)\n    for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {\n        ImPlotAxis& x_axis = plot.XAxis(i);\n        if (x_axis.Enabled && x_axis.HasGridLines() && !x_axis.IsForeground())\n            RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x);\n    }\n    for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {\n        ImPlotAxis& y_axis = plot.YAxis(i);\n        if (y_axis.Enabled && y_axis.HasGridLines() && !y_axis.IsForeground())\n            RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect,  y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y);\n    }\n\n    // render x axis button, label, tick labels\n    for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {\n        ImPlotAxis& ax = plot.XAxis(i);\n        if (!ax.Enabled)\n            continue;\n        if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight))\n            DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov);\n        else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) {\n            DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi);\n            ax.ColorHiLi = IM_COL32_BLACK_TRANS;\n        }\n        else if (ax.ColorBg != IM_COL32_BLACK_TRANS) {\n            DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg);\n        }\n        const ImPlotTicker& tkr = ax.Ticker;\n        const bool opp = ax.IsOpposite();\n        if (ax.HasLabel()) {\n            const char* label        = plot.GetAxisLabel(ax);\n            const ImVec2 label_size  = ImGui::CalcTextSize(label);\n            const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.y + gp.Style.LabelPadding.y : 0.0f)\n                                     + (tkr.Levels - 1) * (txt_height + gp.Style.LabelPadding.y)\n                                     + gp.Style.LabelPadding.y;\n            const ImVec2 label_pos(plot.PlotRect.GetCenter().x - label_size.x * 0.5f,\n                                   opp ? ax.Datum1 - label_offset - label_size.y : ax.Datum1 + label_offset);\n            DrawList.AddText(label_pos, ax.ColorTxt, label);\n        }\n        if (ax.HasTickLabels()) {\n            for (int j = 0; j < tkr.TickCount(); ++j) {\n                const ImPlotTick& tk = tkr.Ticks[j];\n                const float datum = ax.Datum1 + (opp ? (-gp.Style.LabelPadding.y -txt_height -tk.Level * (txt_height + gp.Style.LabelPadding.y))\n                                                     : gp.Style.LabelPadding.y + tk.Level * (txt_height + gp.Style.LabelPadding.y));\n                if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.x - 1 && tk.PixelPos <= plot.PlotRect.Max.x + 1) {\n                    ImVec2 start(tk.PixelPos - 0.5f * tk.LabelSize.x, datum);\n                    DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j));\n                }\n            }\n        }\n    }\n\n    // render y axis button, label, tick labels\n    for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {\n        ImPlotAxis& ax = plot.YAxis(i);\n        if (!ax.Enabled)\n            continue;\n        if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight))\n            DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov);\n        else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) {\n            DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi);\n            ax.ColorHiLi = IM_COL32_BLACK_TRANS;\n        }\n        else if (ax.ColorBg != IM_COL32_BLACK_TRANS) {\n            DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg);\n        }\n        const ImPlotTicker& tkr = ax.Ticker;\n        const bool opp = ax.IsOpposite();\n        if (ax.HasLabel()) {\n            const char* label        = plot.GetAxisLabel(ax);\n            const ImVec2 label_size  = CalcTextSizeVertical(label);\n            const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.x + gp.Style.LabelPadding.x : 0.0f)\n                                     + gp.Style.LabelPadding.x;\n            const ImVec2 label_pos(opp ? ax.Datum1 + label_offset : ax.Datum1 - label_offset - label_size.x,\n                                   plot.PlotRect.GetCenter().y + label_size.y * 0.5f);\n            AddTextVertical(&DrawList, label_pos, ax.ColorTxt, label);\n        }\n        if (ax.HasTickLabels()) {\n            for (int j = 0; j < tkr.TickCount(); ++j) {\n                const ImPlotTick& tk = tkr.Ticks[j];\n                const float datum = ax.Datum1 + (opp ? gp.Style.LabelPadding.x : (-gp.Style.LabelPadding.x - tk.LabelSize.x));\n                if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.y - 1 && tk.PixelPos <= plot.PlotRect.Max.y + 1) {\n                    ImVec2 start(datum, tk.PixelPos - 0.5f * tk.LabelSize.y);\n                    DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j));\n                }\n            }\n        }\n    }\n\n\n    // clear legend (TODO: put elsewhere)\n    plot.Items.Legend.Reset();\n    // push ID to set item hashes (NB: !!!THIS PROBABLY NEEDS TO BE IN BEGIN PLOT!!!!)\n    ImGui::PushOverrideID(gp.CurrentItems->ID);\n}\n\n//-----------------------------------------------------------------------------\n// EndPlot()\n//-----------------------------------------------------------------------------\n\nvoid EndPlot() {\n    IM_ASSERT_USER_ERROR(GImPlot != nullptr, \"No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?\");\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"Mismatched BeginPlot()/EndPlot()!\");\n\n    SetupLock();\n\n    ImGuiContext &G       = *GImGui;\n    ImPlotPlot &plot      = *gp.CurrentPlot;\n    ImGuiWindow * Window  = G.CurrentWindow;\n    ImDrawList & DrawList = *Window->DrawList;\n    const ImGuiIO &   IO  = ImGui::GetIO();\n\n    // FINAL RENDER -----------------------------------------------------------\n\n    const bool render_border  = gp.Style.PlotBorderSize > 0 && GetStyleColorVec4(ImPlotCol_PlotBorder).w > 0;\n    const bool any_x_held = plot.Held    || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES);\n    const bool any_y_held = plot.Held    || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES);\n\n    ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true);\n\n    // render grid (foreground)\n    for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {\n        ImPlotAxis& x_axis = plot.XAxis(i);\n        if (x_axis.Enabled && x_axis.HasGridLines() && x_axis.IsForeground())\n            RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x);\n    }\n    for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {\n        ImPlotAxis& y_axis = plot.YAxis(i);\n        if (y_axis.Enabled && y_axis.HasGridLines() && y_axis.IsForeground())\n            RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect,  y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y);\n    }\n\n\n    // render title\n    if (plot.HasTitle()) {\n        ImU32 col = GetStyleColorU32(ImPlotCol_TitleText);\n        AddTextCentered(&DrawList,ImVec2(plot.PlotRect.GetCenter().x, plot.CanvasRect.Min.y),col,plot.GetTitle());\n    }\n\n    // render x ticks\n    int count_B = 0, count_T = 0;\n    for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {\n        const ImPlotAxis& ax = plot.XAxis(i);\n        if (!ax.Enabled)\n            continue;\n        const ImPlotTicker& tkr = ax.Ticker;\n        const bool opp = ax.IsOpposite();\n        const bool aux = ((opp && count_T > 0)||(!opp && count_B > 0));\n        if (ax.HasTickMarks()) {\n            const float direction = opp ? 1.0f : -1.0f;\n            for (int j = 0; j < tkr.TickCount(); ++j) {\n                const ImPlotTick& tk = tkr.Ticks[j];\n                if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.x || tk.PixelPos > plot.PlotRect.Max.x)\n                    continue;\n                const ImVec2 start(tk.PixelPos, ax.Datum1);\n                const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.x  : gp.Style.MinorTickLen.x;\n                const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.x : gp.Style.MinorTickSize.x;\n                DrawList.AddLine(start, start + ImVec2(0,direction*len), ax.ColorTick, thk);\n            }\n            if (aux || !render_border)\n                DrawList.AddLine(ImVec2(plot.PlotRect.Min.x,ax.Datum1), ImVec2(plot.PlotRect.Max.x,ax.Datum1), ax.ColorTick, gp.Style.MinorTickSize.x);\n        }\n        count_B += !opp;\n        count_T +=  opp;\n    }\n\n    // render y ticks\n    int count_L = 0, count_R = 0;\n    for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {\n        const ImPlotAxis& ax = plot.YAxis(i);\n        if (!ax.Enabled)\n            continue;\n        const ImPlotTicker& tkr = ax.Ticker;\n        const bool opp = ax.IsOpposite();\n        const bool aux = ((opp && count_R > 0)||(!opp && count_L > 0));\n        if (ax.HasTickMarks()) {\n            const float direction = opp ? -1.0f : 1.0f;\n            for (int j = 0; j < tkr.TickCount(); ++j) {\n                const ImPlotTick& tk = tkr.Ticks[j];\n                if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.y || tk.PixelPos > plot.PlotRect.Max.y)\n                    continue;\n                const ImVec2 start(ax.Datum1, tk.PixelPos);\n                const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.y  : gp.Style.MinorTickLen.y;\n                const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y;\n                DrawList.AddLine(start, start + ImVec2(direction*len,0), ax.ColorTick, thk);\n            }\n            if (aux || !render_border)\n                DrawList.AddLine(ImVec2(ax.Datum1, plot.PlotRect.Min.y), ImVec2(ax.Datum1, plot.PlotRect.Max.y), ax.ColorTick, gp.Style.MinorTickSize.y);\n        }\n        count_L += !opp;\n        count_R +=  opp;\n    }\n    ImGui::PopClipRect();\n\n    // render annotations\n    PushPlotClipRect();\n    for (int i = 0; i < gp.Annotations.Size; ++i) {\n        const char* txt       = gp.Annotations.GetText(i);\n        ImPlotAnnotation& an  = gp.Annotations.Annotations[i];\n        const ImVec2 txt_size = ImGui::CalcTextSize(txt);\n        const ImVec2 size     = txt_size + gp.Style.AnnotationPadding * 2;\n        ImVec2 pos            = an.Pos;\n        if (an.Offset.x == 0)\n            pos.x -= size.x / 2;\n        else if (an.Offset.x > 0)\n            pos.x += an.Offset.x;\n        else\n            pos.x -= size.x - an.Offset.x;\n        if (an.Offset.y == 0)\n            pos.y -= size.y / 2;\n        else if (an.Offset.y > 0)\n            pos.y += an.Offset.y;\n        else\n            pos.y -= size.y - an.Offset.y;\n        if (an.Clamp)\n            pos = ClampLabelPos(pos, size, plot.PlotRect.Min, plot.PlotRect.Max);\n        ImRect rect(pos,pos+size);\n        if (an.Offset.x != 0 || an.Offset.y != 0) {\n            ImVec2 corners[4] = {rect.GetTL(), rect.GetTR(), rect.GetBR(), rect.GetBL()};\n            int min_corner = 0;\n            float min_len = FLT_MAX;\n            for (int c = 0; c < 4; ++c) {\n                float len = ImLengthSqr(an.Pos - corners[c]);\n                if (len < min_len) {\n                    min_corner = c;\n                    min_len = len;\n                }\n            }\n            DrawList.AddLine(an.Pos, corners[min_corner], an.ColorBg);\n        }\n        DrawList.AddRectFilled(rect.Min, rect.Max, an.ColorBg);\n        DrawList.AddText(pos + gp.Style.AnnotationPadding, an.ColorFg, txt);\n    }\n\n    // render selection\n    if (plot.Selected)\n        RenderSelectionRect(DrawList, plot.SelectRect.Min + plot.PlotRect.Min, plot.SelectRect.Max + plot.PlotRect.Min, GetStyleColorVec4(ImPlotCol_Selection));\n\n    // render crosshairs\n    if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && plot.Hovered && !(any_x_held || any_y_held) && !plot.Selecting && !plot.Items.Legend.Hovered) {\n        ImGui::SetMouseCursor(ImGuiMouseCursor_None);\n        ImVec2 xy = IO.MousePos;\n        ImVec2 h1(plot.PlotRect.Min.x, xy.y);\n        ImVec2 h2(xy.x - 5, xy.y);\n        ImVec2 h3(xy.x + 5, xy.y);\n        ImVec2 h4(plot.PlotRect.Max.x, xy.y);\n        ImVec2 v1(xy.x, plot.PlotRect.Min.y);\n        ImVec2 v2(xy.x, xy.y - 5);\n        ImVec2 v3(xy.x, xy.y + 5);\n        ImVec2 v4(xy.x, plot.PlotRect.Max.y);\n        ImU32 col = GetStyleColorU32(ImPlotCol_Crosshairs);\n        DrawList.AddLine(h1, h2, col);\n        DrawList.AddLine(h3, h4, col);\n        DrawList.AddLine(v1, v2, col);\n        DrawList.AddLine(v3, v4, col);\n    }\n\n    // render mouse pos\n    if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText) && (plot.Hovered || ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_ShowAlways))) {\n\n        const bool no_aux = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoAuxAxes);\n        const bool no_fmt = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoFormat);\n\n        ImGuiTextBuffer& builder = gp.MousePosStringBuilder;\n        builder.Buf.shrink(0);\n        char buff[IMPLOT_LABEL_MAX_SIZE];\n\n        const int num_x = no_aux ? 1 : IMPLOT_NUM_X_AXES;\n        for (int i = 0; i < num_x; ++i) {\n            ImPlotAxis& x_axis = plot.XAxis(i);\n            if (!x_axis.Enabled)\n                continue;\n            if (i > 0)\n                builder.append(\", (\");\n            double v = x_axis.PixelsToPlot(IO.MousePos.x);\n            if (no_fmt)\n                Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT);\n            else\n                LabelAxisValue(x_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true);\n            builder.append(buff);\n            if (i > 0)\n                builder.append(\")\");\n        }\n        builder.append(\", \");\n        const int num_y = no_aux ? 1 : IMPLOT_NUM_Y_AXES;\n        for (int i = 0; i < num_y; ++i) {\n            ImPlotAxis& y_axis = plot.YAxis(i);\n            if (!y_axis.Enabled)\n                continue;\n            if (i > 0)\n                builder.append(\", (\");\n            double v = y_axis.PixelsToPlot(IO.MousePos.y);\n            if (no_fmt)\n                Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT);\n            else\n                LabelAxisValue(y_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true);\n            builder.append(buff);\n            if (i > 0)\n                builder.append(\")\");\n        }\n\n        if (!builder.empty()) {\n            const ImVec2 size = ImGui::CalcTextSize(builder.c_str());\n            const ImVec2 pos = GetLocationPos(plot.PlotRect, size, plot.MouseTextLocation, gp.Style.MousePosPadding);\n            DrawList.AddText(pos, GetStyleColorU32(ImPlotCol_InlayText), builder.c_str());\n        }\n    }\n    PopPlotClipRect();\n\n    // axis side switch\n    if (!plot.Held) {\n        ImVec2 mouse_pos = ImGui::GetIO().MousePos;\n        ImRect trigger_rect = plot.PlotRect;\n        trigger_rect.Expand(-10);\n        for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {\n            ImPlotAxis& x_axis = plot.XAxis(i);\n            if (ImHasFlag(x_axis.Flags, ImPlotAxisFlags_NoSideSwitch))\n                continue;\n            if (x_axis.Held && plot.PlotRect.Contains(mouse_pos)) {\n                const bool opp = ImHasFlag(x_axis.Flags, ImPlotAxisFlags_Opposite);\n                if (!opp) {\n                    ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5,\n                                plot.PlotRect.Max.x + 5, plot.PlotRect.Min.y + 5);\n                    if (mouse_pos.y < plot.PlotRect.Max.y - 10)\n                        DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov);\n                    if (rect.Contains(mouse_pos))\n                        x_axis.Flags |= ImPlotAxisFlags_Opposite;\n                }\n                else {\n                    ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Max.y - 5,\n                                plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5);\n                    if (mouse_pos.y > plot.PlotRect.Min.y + 10)\n                        DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov);\n                    if (rect.Contains(mouse_pos))\n                        x_axis.Flags &= ~ImPlotAxisFlags_Opposite;\n                }\n            }\n        }\n        for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) {\n            ImPlotAxis& y_axis = plot.YAxis(i);\n            if (ImHasFlag(y_axis.Flags, ImPlotAxisFlags_NoSideSwitch))\n                continue;\n            if (y_axis.Held && plot.PlotRect.Contains(mouse_pos)) {\n                const bool opp = ImHasFlag(y_axis.Flags, ImPlotAxisFlags_Opposite);\n                if (!opp) {\n                    ImRect rect(plot.PlotRect.Max.x - 5, plot.PlotRect.Min.y - 5,\n                                plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5);\n                    if (mouse_pos.x > plot.PlotRect.Min.x + 10)\n                        DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov);\n                    if (rect.Contains(mouse_pos))\n                        y_axis.Flags |= ImPlotAxisFlags_Opposite;\n                }\n                else {\n                    ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5,\n                                plot.PlotRect.Min.x + 5, plot.PlotRect.Max.y + 5);\n                    if (mouse_pos.x < plot.PlotRect.Max.x - 10)\n                        DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov);\n                    if (rect.Contains(mouse_pos))\n                        y_axis.Flags &= ~ImPlotAxisFlags_Opposite;\n                }\n            }\n        }\n    }\n\n    // reset legend hovers\n    plot.Items.Legend.Hovered = false;\n    for (int i = 0; i < plot.Items.GetItemCount(); ++i)\n        plot.Items.GetItemByIndex(i)->LegendHovered = false;\n    // render legend\n    if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0) {\n        ImPlotLegend& legend = plot.Items.Legend;\n        const bool   legend_out  = ImHasFlag(legend.Flags, ImPlotLegendFlags_Outside);\n        const bool   legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal);\n        const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz);\n        const ImVec2 legend_pos  = GetLocationPos(legend_out ? plot.FrameRect : plot.PlotRect,\n                                                  legend_size,\n                                                  legend.Location,\n                                                  legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding);\n        legend.Rect = ImRect(legend_pos, legend_pos + legend_size);\n        legend.RectClamped = legend.Rect;\n        const bool legend_scrollable = ClampLegendRect(legend.RectClamped,\n                                                        legend_out ? plot.FrameRect : plot.PlotRect,\n                                                        legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding\n                                                        );       \n        const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap\n                                                    | ImGuiButtonFlags_PressedOnClick\n                                                    | ImGuiButtonFlags_PressedOnDoubleClick\n                                                    | ImGuiButtonFlags_MouseButtonLeft\n                                                    | ImGuiButtonFlags_MouseButtonRight\n                                                    | ImGuiButtonFlags_MouseButtonMiddle\n                                                    | ImGuiButtonFlags_FlattenChildren;\n        ImGui::KeepAliveID(plot.Items.ID);\n        ImGui::ButtonBehavior(legend.RectClamped, plot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags);\n        legend.Hovered = legend.Hovered || (ImGui::IsWindowHovered() && legend.RectClamped.Contains(IO.MousePos));\n       \n        if (legend_scrollable) {\n            if (legend.Hovered) {\n                ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.Items.ID);\n                if (IO.MouseWheel != 0.0f) {\n                    ImVec2 max_step = legend.Rect.GetSize() * 0.67f;\n#if IMGUI_VERSION_NUM < 19172\n                    float font_size = ImGui::GetCurrentWindow()->CalcFontSize();\n#else\n                    float font_size = ImGui::GetCurrentWindow()->FontRefSize;\n#endif\n                    float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x));\n                    legend.Scroll.x += scroll_step * IO.MouseWheel;\n                    legend.Scroll.y += scroll_step * IO.MouseWheel;\n                }\n            }            \n            const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize();\n            legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f);\n            legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f);            \n            const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y);\n            ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset;\n            legend.Rect.Min += legend_offset;\n            legend.Rect.Max += legend_offset;\n        } else {\n            legend.Scroll = ImVec2(0,0);\n        }\n\n        const ImU32 col_bg  = GetStyleColorU32(ImPlotCol_LegendBg);\n        const ImU32 col_bd  = GetStyleColorU32(ImPlotCol_LegendBorder);        \n        ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true);       \n        DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg);        \n        bool legend_contextable = ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList)\n                                && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus);        \n        DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd);\n        ImGui::PopClipRect();\n\n        // main ctx menu\n        if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus))\n            ImGui::OpenPopup(\"##LegendContext\");        \n        \n        if (ImGui::BeginPopup(\"##LegendContext\")) {\n            ImGui::Text(\"Legend\"); ImGui::Separator();\n            if (ShowLegendContextMenu(legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend)))\n                ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend);\n            ImGui::EndPopup();\n        }\n    }\n    else {\n        plot.Items.Legend.Rect = ImRect();\n    }\n\n    // render border\n    if (render_border)\n        DrawList.AddRect(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBorder), 0, ImDrawFlags_RoundCornersAll, gp.Style.PlotBorderSize);\n\n    // render tags\n    for (int i = 0; i < gp.Tags.Size; ++i) {\n        ImPlotTag& tag  = gp.Tags.Tags[i];\n        ImPlotAxis& axis = plot.Axes[tag.Axis];\n        if (!axis.Enabled || !axis.Range.Contains(tag.Value))\n            continue;\n        const char* txt = gp.Tags.GetText(i);\n        ImVec2 text_size = ImGui::CalcTextSize(txt);\n        ImVec2 size = text_size + gp.Style.AnnotationPadding * 2;\n        ImVec2 pos;\n        axis.Ticker.OverrideSizeLate(size);\n        float pix = IM_ROUND(axis.PlotToPixels(tag.Value));\n        if (axis.Vertical) {\n            if (axis.IsOpposite()) {\n                pos = ImVec2(axis.Datum1 + gp.Style.LabelPadding.x, pix - size.y * 0.5f);\n                DrawList.AddTriangleFilled(ImVec2(axis.Datum1,pix), pos, pos + ImVec2(0,size.y), tag.ColorBg);\n            }\n            else {\n                pos = ImVec2(axis.Datum1 - size.x - gp.Style.LabelPadding.x, pix - size.y * 0.5f);\n                DrawList.AddTriangleFilled(pos + ImVec2(size.x,0), ImVec2(axis.Datum1,pix), pos+size, tag.ColorBg);\n            }\n        }\n        else {\n            if (axis.IsOpposite()) {\n                pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 - size.y - gp.Style.LabelPadding.y );\n                DrawList.AddTriangleFilled(pos + ImVec2(0,size.y), pos + size, ImVec2(pix,axis.Datum1), tag.ColorBg);\n            }\n            else {\n                pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 + gp.Style.LabelPadding.y);\n                DrawList.AddTriangleFilled(pos, ImVec2(pix,axis.Datum1), pos + ImVec2(size.x, 0), tag.ColorBg);\n            }\n        }\n        DrawList.AddRectFilled(pos,pos+size,tag.ColorBg);\n        DrawList.AddText(pos+gp.Style.AnnotationPadding,tag.ColorFg,txt);\n    }\n\n    // FIT DATA --------------------------------------------------------------\n    const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal);\n    if (plot.FitThisFrame) {\n        for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) {\n            ImPlotAxis& x_axis = plot.XAxis(i);\n            if (x_axis.FitThisFrame) {\n                x_axis.ApplyFit(gp.Style.FitPadding.x);\n                if (axis_equal && x_axis.OrthoAxis != nullptr) {\n                    double aspect = x_axis.GetAspect();\n                    ImPlotAxis& y_axis = *x_axis.OrthoAxis;\n                    if (y_axis.FitThisFrame) {\n                        y_axis.ApplyFit(gp.Style.FitPadding.y);\n                        y_axis.FitThisFrame = false;\n                        aspect = ImMax(aspect, y_axis.GetAspect());\n                    }\n                    x_axis.SetAspect(aspect);\n                    y_axis.SetAspect(aspect);\n                }\n            }\n        }\n        for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {\n            ImPlotAxis& y_axis = plot.YAxis(i);\n            if (y_axis.FitThisFrame) {\n                y_axis.ApplyFit(gp.Style.FitPadding.y);\n                if (axis_equal && y_axis.OrthoAxis != nullptr) {\n                    double aspect = y_axis.GetAspect();\n                    ImPlotAxis& x_axis = *y_axis.OrthoAxis;\n                    if (x_axis.FitThisFrame) {\n                        x_axis.ApplyFit(gp.Style.FitPadding.x);\n                        x_axis.FitThisFrame = false;\n                        aspect = ImMax(x_axis.GetAspect(), aspect);\n                    }\n                    x_axis.SetAspect(aspect);\n                    y_axis.SetAspect(aspect);\n                }\n            }\n        }\n        plot.FitThisFrame = false;\n    }\n\n    // CONTEXT MENUS -----------------------------------------------------------\n\n    ImGui::PushOverrideID(plot.ID);\n\n    const bool can_ctx = gp.OpenContextThisFrame &&\n                         !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) &&\n                         !plot.Items.Legend.Hovered;\n\n\n\n    // main ctx menu\n    if (can_ctx && plot.Hovered)\n        ImGui::OpenPopup(\"##PlotContext\");\n    if (ImGui::BeginPopup(\"##PlotContext\")) {\n        ShowPlotContextMenu(plot);\n        ImGui::EndPopup();\n    }\n\n    // axes ctx menus\n    for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {\n        ImGui::PushID(i);\n        ImPlotAxis& x_axis = plot.XAxis(i);\n        if (can_ctx && x_axis.Hovered && x_axis.HasMenus())\n            ImGui::OpenPopup(\"##XContext\");\n        if (ImGui::BeginPopup(\"##XContext\")) {\n            ImGui::Text(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) :  i == 0 ? \"X-Axis\" : \"X-Axis %d\", i + 1);\n            ImGui::Separator();\n            ShowAxisContextMenu(x_axis, axis_equal ? x_axis.OrthoAxis : nullptr, true);\n            ImGui::EndPopup();\n        }\n        ImGui::PopID();\n    }\n    for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) {\n        ImGui::PushID(i);\n        ImPlotAxis& y_axis = plot.YAxis(i);\n        if (can_ctx && y_axis.Hovered && y_axis.HasMenus())\n            ImGui::OpenPopup(\"##YContext\");\n        if (ImGui::BeginPopup(\"##YContext\")) {\n            ImGui::Text(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : i == 0 ? \"Y-Axis\" : \"Y-Axis %d\", i + 1);\n            ImGui::Separator();\n            ShowAxisContextMenu(y_axis, axis_equal ? y_axis.OrthoAxis : nullptr, false);\n            ImGui::EndPopup();\n        }\n        ImGui::PopID();\n    }\n    ImGui::PopID();\n\n    // LINKED AXES ------------------------------------------------------------\n\n    for (int i = 0; i < ImAxis_COUNT; ++i)\n        plot.Axes[i].PushLinks();\n\n\n    // CLEANUP ----------------------------------------------------------------\n\n    // remove items\n    if (gp.CurrentItems == &plot.Items)\n        gp.CurrentItems = nullptr;\n    // reset the plot items for the next frame\n    for (int i = 0; i < plot.Items.GetItemCount(); ++i) {\n        plot.Items.GetItemByIndex(i)->SeenThisFrame = false;\n    }\n\n    // mark the plot as initialized, i.e. having made it through one frame completely\n    plot.Initialized = true;\n    // Pop ImGui::PushID at the end of BeginPlot\n    ImGui::PopID();\n    // Reset context for next plot\n    ResetCtxForNextPlot(GImPlot);\n\n    // setup next subplot\n    if (gp.CurrentSubplot != nullptr) {\n        ImGui::PopID();\n        SubplotNextCell();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// BEGIN/END SUBPLOT\n//-----------------------------------------------------------------------------\n\nstatic const float SUBPLOT_BORDER_SIZE             = 1.0f;\nstatic const float SUBPLOT_SPLITTER_HALF_THICKNESS = 4.0f;\nstatic const float SUBPLOT_SPLITTER_FEEDBACK_TIMER = 0.06f;\n\nvoid SubplotSetCell(int row, int col) {\n    ImPlotContext& gp      = *GImPlot;\n    ImPlotSubplot& subplot = *gp.CurrentSubplot;\n    if (row >= subplot.Rows || col >= subplot.Cols)\n        return;\n    float xoff = 0;\n    float yoff = 0;\n    for (int c = 0; c < col; ++c)\n        xoff += subplot.ColRatios[c];\n    for (int r = 0; r < row; ++r)\n        yoff += subplot.RowRatios[r];\n    const ImVec2 grid_size = subplot.GridRect.GetSize();\n    ImVec2 cpos            = subplot.GridRect.Min + ImVec2(xoff*grid_size.x,yoff*grid_size.y);\n    cpos.x = IM_ROUND(cpos.x);\n    cpos.y = IM_ROUND(cpos.y);\n    ImGui::GetCurrentWindow()->DC.CursorPos =  cpos;\n    // set cell size\n    subplot.CellSize.x = IM_ROUND(subplot.GridRect.GetWidth()  * subplot.ColRatios[col]);\n    subplot.CellSize.y = IM_ROUND(subplot.GridRect.GetHeight() * subplot.RowRatios[row]);\n    // setup links\n    const bool lx = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX);\n    const bool ly = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY);\n    const bool lr = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows);\n    const bool lc = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols);\n\n    SetNextAxisLinks(ImAxis_X1, lx ? &subplot.ColLinkData[0].Min : lc ? &subplot.ColLinkData[col].Min : nullptr,\n                                lx ? &subplot.ColLinkData[0].Max : lc ? &subplot.ColLinkData[col].Max : nullptr);\n    SetNextAxisLinks(ImAxis_Y1, ly ? &subplot.RowLinkData[0].Min : lr ? &subplot.RowLinkData[row].Min : nullptr,\n                                ly ? &subplot.RowLinkData[0].Max : lr ? &subplot.RowLinkData[row].Max : nullptr);\n    // setup alignment\n    if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign)) {\n        gp.CurrentAlignmentH = &subplot.RowAlignmentData[row];\n        gp.CurrentAlignmentV = &subplot.ColAlignmentData[col];\n    }\n    // set idx\n    if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor))\n        subplot.CurrentIdx = col * subplot.Rows + row;\n    else\n        subplot.CurrentIdx = row * subplot.Cols + col;\n}\n\nvoid SubplotSetCell(int idx) {\n    ImPlotContext& gp      = *GImPlot;\n    ImPlotSubplot& subplot = *gp.CurrentSubplot;\n    if (idx >= subplot.Rows * subplot.Cols)\n        return;\n    int row = 0, col = 0;\n    if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) {\n        row = idx % subplot.Rows;\n        col = idx / subplot.Rows;\n    }\n    else {\n        row = idx / subplot.Cols;\n        col = idx % subplot.Cols;\n    }\n    return SubplotSetCell(row, col);\n}\n\nvoid SubplotNextCell() {\n    ImPlotContext& gp      = *GImPlot;\n    ImPlotSubplot& subplot = *gp.CurrentSubplot;\n    SubplotSetCell(++subplot.CurrentIdx);\n}\n\nbool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, ImPlotSubplotFlags flags, float* row_sizes, float* col_sizes) {\n    IM_ASSERT_USER_ERROR(rows > 0 && cols > 0, \"Invalid sizing arguments!\");\n    IM_ASSERT_USER_ERROR(GImPlot != nullptr, \"No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?\");\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentSubplot == nullptr, \"Mismatched BeginSubplots()/EndSubplots()!\");\n    ImGuiContext &G = *GImGui;\n    ImGuiWindow * Window = G.CurrentWindow;\n    if (Window->SkipItems)\n        return false;\n    const ImGuiID ID = Window->GetID(title);\n    bool just_created = gp.Subplots.GetByKey(ID) == nullptr;\n    gp.CurrentSubplot = gp.Subplots.GetOrAddByKey(ID);\n    ImPlotSubplot& subplot = *gp.CurrentSubplot;\n    subplot.ID       = ID;\n    subplot.Items.ID = ID - 1;\n    subplot.HasTitle = ImGui::FindRenderedTextEnd(title, nullptr) != title;\n    // push ID\n    ImGui::PushID(ID);\n\n    if (just_created)\n        subplot.Flags = flags;\n    else if (flags != subplot.PreviousFlags)\n        subplot.Flags = flags;\n    subplot.PreviousFlags = flags;\n\n    // check for change in rows and cols\n    if (subplot.Rows != rows || subplot.Cols != cols) {\n        subplot.RowAlignmentData.resize(rows);\n        subplot.RowLinkData.resize(rows);\n        subplot.RowRatios.resize(rows);\n        for (int r = 0; r < rows; ++r) {\n            subplot.RowAlignmentData[r].Reset();\n            subplot.RowLinkData[r] = ImPlotRange(0,1);\n            subplot.RowRatios[r] = 1.0f / rows;\n        }\n        subplot.ColAlignmentData.resize(cols);\n        subplot.ColLinkData.resize(cols);\n        subplot.ColRatios.resize(cols);\n        for (int c = 0; c < cols; ++c) {\n            subplot.ColAlignmentData[c].Reset();\n            subplot.ColLinkData[c] = ImPlotRange(0,1);\n            subplot.ColRatios[c] = 1.0f / cols;\n        }\n    }\n    // check incoming size requests\n    float row_sum = 0, col_sum = 0;\n    if (row_sizes != nullptr) {\n        row_sum = ImSum(row_sizes, rows);\n        for (int r = 0; r < rows; ++r)\n            subplot.RowRatios[r] = row_sizes[r] / row_sum;\n    }\n    if (col_sizes != nullptr) {\n        col_sum = ImSum(col_sizes, cols);\n        for (int c = 0; c < cols; ++c)\n            subplot.ColRatios[c] = col_sizes[c] / col_sum;\n    }\n    subplot.Rows = rows;\n    subplot.Cols = cols;\n\n    // calc plot frame sizes\n    ImVec2 title_size(0.0f, 0.0f);\n    if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle))\n         title_size = ImGui::CalcTextSize(title, nullptr, true);\n    const float pad_top = title_size.x > 0.0f ? title_size.y + gp.Style.LabelPadding.y : 0;\n    const ImVec2 half_pad = gp.Style.PlotPadding/2;\n    const ImVec2 frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y);\n    subplot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);\n    subplot.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top);\n    subplot.GridRect.Max = subplot.FrameRect.Max - half_pad;\n    subplot.FrameHovered = subplot.FrameRect.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows|ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);\n\n    // outside legend adjustments (TODO: make function)\n    const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);\n    if (share_items)\n        gp.CurrentItems = &subplot.Items;\n    if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) {\n        ImPlotLegend& legend = subplot.Items.Legend;\n        const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal);\n        const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz);\n        const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East);\n        const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West);\n        const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South);\n        const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North);\n        if ((west && !horz) || (west && horz && !north && !south))\n            subplot.GridRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x);\n        if ((east && !horz) || (east && horz && !north && !south))\n            subplot.GridRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x);\n        if ((north && horz) || (north && !horz && !west && !east))\n            subplot.GridRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y);\n        if ((south && horz) || (south && !horz && !west && !east))\n            subplot.GridRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y);\n    }\n\n    // render single background frame\n    ImGui::RenderFrame(subplot.FrameRect.Min, subplot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, ImGui::GetStyle().FrameRounding);\n    // render title\n    if (title_size.x > 0.0f && !ImHasFlag(subplot.Flags, ImPlotFlags_NoTitle)) {\n        const ImU32 col = GetStyleColorU32(ImPlotCol_TitleText);\n        AddTextCentered(ImGui::GetWindowDrawList(),ImVec2(subplot.GridRect.GetCenter().x, subplot.GridRect.Min.y - pad_top + half_pad.y),col,title);\n    }\n\n    // render splitters\n    if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize)) {\n        ImDrawList& DrawList = *ImGui::GetWindowDrawList();\n        const ImU32 hov_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorHovered]);\n        const ImU32 act_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorActive]);\n        float xpos = subplot.GridRect.Min.x;\n        float ypos = subplot.GridRect.Min.y;\n        int separator = 1;\n        // bool pass = false;\n        for (int r = 0; r < subplot.Rows-1; ++r) {\n            ypos += subplot.RowRatios[r] * subplot.GridRect.GetHeight();\n            const ImGuiID sep_id = subplot.ID + separator;\n            ImGui::KeepAliveID(sep_id);\n            const ImRect sep_bb = ImRect(subplot.GridRect.Min.x, ypos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.x, ypos+SUBPLOT_SPLITTER_HALF_THICKNESS);\n            bool sep_hov = false, sep_hld = false;\n            const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick);\n            if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) {\n                if (sep_clk && ImGui::IsMouseDoubleClicked(0)) {\n                    float p = (subplot.RowRatios[r] + subplot.RowRatios[r+1])/2;\n                    subplot.RowRatios[r] = subplot.RowRatios[r+1] = p;\n                }\n                if (sep_clk) {\n                    subplot.TempSizes[0] = subplot.RowRatios[r];\n                    subplot.TempSizes[1] = subplot.RowRatios[r+1];\n                }\n                if (sep_hld) {\n                    float dp = ImGui::GetMouseDragDelta(0).y  / subplot.GridRect.GetHeight();\n                    if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) {\n                        subplot.RowRatios[r]   = subplot.TempSizes[0] + dp;\n                        subplot.RowRatios[r+1] = subplot.TempSizes[1] - dp;\n                    }\n                }\n                DrawList.AddLine(ImVec2(IM_ROUND(subplot.GridRect.Min.x),IM_ROUND(ypos)),\n                                 ImVec2(IM_ROUND(subplot.GridRect.Max.x),IM_ROUND(ypos)),\n                                 sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE);\n                ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);\n            }\n            separator++;\n        }\n        for (int c = 0; c < subplot.Cols-1; ++c) {\n            xpos += subplot.ColRatios[c] * subplot.GridRect.GetWidth();\n            const ImGuiID sep_id = subplot.ID + separator;\n            ImGui::KeepAliveID(sep_id);\n            const ImRect sep_bb = ImRect(xpos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Min.y, xpos+SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.y);\n            bool sep_hov = false, sep_hld = false;\n            const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick);\n            if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) {\n                if (sep_clk && ImGui::IsMouseDoubleClicked(0)) {\n                    float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2;\n                    subplot.ColRatios[c] = subplot.ColRatios[c+1] = p;\n                }\n                if (sep_clk) {\n                    subplot.TempSizes[0] = subplot.ColRatios[c];\n                    subplot.TempSizes[1] = subplot.ColRatios[c+1];\n                }\n                if (sep_hld) {\n                    float dp = ImGui::GetMouseDragDelta(0).x / subplot.GridRect.GetWidth();\n                    if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) {\n                        subplot.ColRatios[c]   = subplot.TempSizes[0] + dp;\n                        subplot.ColRatios[c+1] = subplot.TempSizes[1] - dp;\n                    }\n                }\n                DrawList.AddLine(ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Min.y)),\n                                 ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Max.y)),\n                                 sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE);\n                ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);\n            }\n            separator++;\n        }\n    }\n\n    // set outgoing sizes\n    if (row_sizes != nullptr) {\n        for (int r = 0; r < rows; ++r)\n            row_sizes[r] = subplot.RowRatios[r] * row_sum;\n    }\n    if (col_sizes != nullptr) {\n        for (int c = 0; c < cols; ++c)\n            col_sizes[c] = subplot.ColRatios[c] * col_sum;\n    }\n\n    // push styling\n    PushStyleColor(ImPlotCol_FrameBg, IM_COL32_BLACK_TRANS);\n    PushStyleVar(ImPlotStyleVar_PlotPadding, half_pad);\n    PushStyleVar(ImPlotStyleVar_PlotMinSize, ImVec2(0,0));\n    ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize,0);\n\n    // set initial cursor pos\n    Window->DC.CursorPos = subplot.GridRect.Min;\n    // begin alignments\n    for (int r = 0; r < subplot.Rows; ++r)\n        subplot.RowAlignmentData[r].Begin();\n    for (int c = 0; c < subplot.Cols; ++c)\n        subplot.ColAlignmentData[c].Begin();\n    // clear legend data\n    subplot.Items.Legend.Reset();\n    // Setup first subplot\n    SubplotSetCell(0,0);\n    return true;\n}\n\nvoid EndSubplots() {\n    IM_ASSERT_USER_ERROR(GImPlot != nullptr, \"No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?\");\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, \"Mismatched BeginSubplots()/EndSubplots()!\");\n    ImPlotSubplot& subplot = *gp.CurrentSubplot;\n    const ImGuiIO& IO  = ImGui::GetIO();\n    // set alignments\n    for (int r = 0; r < subplot.Rows; ++r)\n        subplot.RowAlignmentData[r].End();\n    for (int c = 0; c < subplot.Cols; ++c)\n        subplot.ColAlignmentData[c].End();\n    // pop styling\n    PopStyleColor();\n    PopStyleVar();\n    PopStyleVar();\n    ImGui::PopStyleVar();\n    // legend\n    subplot.Items.Legend.Hovered = false;\n    for (int i = 0; i < subplot.Items.GetItemCount(); ++i)\n        subplot.Items.GetItemByIndex(i)->LegendHovered = false;\n    // render legend\n    const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems);\n    ImDrawList& DrawList = *ImGui::GetWindowDrawList();\n    if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) {\n        ImPlotLegend& legend = subplot.Items.Legend;\n        const bool   legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal);\n        const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz);\n        const ImVec2 legend_pos  = GetLocationPos(subplot.FrameRect, legend_size, legend.Location, gp.Style.PlotPadding);\n        legend.Rect = ImRect(legend_pos, legend_pos + legend_size);\n        legend.RectClamped = legend.Rect;\n        const bool legend_scrollable = ClampLegendRect(legend.RectClamped,subplot.FrameRect, gp.Style.PlotPadding);    \n        const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap\n                                                    | ImGuiButtonFlags_PressedOnClick\n                                                    | ImGuiButtonFlags_PressedOnDoubleClick\n                                                    | ImGuiButtonFlags_MouseButtonLeft\n                                                    | ImGuiButtonFlags_MouseButtonRight\n                                                    | ImGuiButtonFlags_MouseButtonMiddle\n                                                    | ImGuiButtonFlags_FlattenChildren;\n        ImGui::KeepAliveID(subplot.Items.ID);\n        ImGui::ButtonBehavior(legend.RectClamped, subplot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags);\n        legend.Hovered = legend.Hovered || (subplot.FrameHovered && legend.RectClamped.Contains(ImGui::GetIO().MousePos));\n        \n        if (legend_scrollable) {\n            if (legend.Hovered) {\n                ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, subplot.Items.ID);\n                if (IO.MouseWheel != 0.0f) {\n                    ImVec2 max_step = legend.Rect.GetSize() * 0.67f;\n#if IMGUI_VERSION_NUM < 19172\n                    float font_size = ImGui::GetCurrentWindow()->CalcFontSize();\n#else\n                    float font_size = ImGui::GetCurrentWindow()->FontRefSize;\n#endif\n                    float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x));\n                    legend.Scroll.x += scroll_step * IO.MouseWheel;\n                    legend.Scroll.y += scroll_step * IO.MouseWheel;\n                }\n            }                    \n            const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize();\n            legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f);\n            legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f);            \n            const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y);\n            ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset;\n            legend.Rect.Min += legend_offset;\n            legend.Rect.Max += legend_offset;\n        } else {\n            legend.Scroll = ImVec2(0,0);\n        }\n\n        const ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);\n        const ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);\n        ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true);       \n        DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg);        \n        bool legend_contextable = ShowLegendEntries(subplot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList)\n                                && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus);\n        DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd);\n        ImGui::PopClipRect();\n        \n        if (legend_contextable && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoMenus) && ImGui::GetIO().MouseReleased[gp.InputMap.Menu])\n            ImGui::OpenPopup(\"##LegendContext\");\n        if (ImGui::BeginPopup(\"##LegendContext\")) {\n            ImGui::Text(\"Legend\"); ImGui::Separator();\n            if (ShowLegendContextMenu(legend, !ImHasFlag(subplot.Flags, ImPlotFlags_NoLegend)))\n                ImFlipFlag(subplot.Flags, ImPlotFlags_NoLegend);\n            ImGui::EndPopup();\n        }\n    }\n    else {\n        subplot.Items.Legend.Rect = ImRect();\n    }\n    // remove items\n    if (gp.CurrentItems == &subplot.Items)\n        gp.CurrentItems = nullptr;\n    // reset the plot items for the next frame (TODO: put this elswhere)\n    for (int i = 0; i < subplot.Items.GetItemCount(); ++i) {\n        subplot.Items.GetItemByIndex(i)->SeenThisFrame = false;\n    }\n    // pop id\n    ImGui::PopID();\n    // set DC back correctly\n    GImGui->CurrentWindow->DC.CursorPos = subplot.FrameRect.Min;\n    ImGui::Dummy(subplot.FrameRect.GetSize());\n    ResetCtxForNextSubplot(GImPlot);\n\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot Utils\n//-----------------------------------------------------------------------------\n\nvoid SetAxis(ImAxis axis) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"SetAxis() needs to be called between BeginPlot() and EndPlot()!\");\n    IM_ASSERT_USER_ERROR(axis >= ImAxis_X1 && axis < ImAxis_COUNT, \"Axis index out of bounds!\");\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[axis].Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    SetupLock();\n    if (axis < ImAxis_Y1)\n        gp.CurrentPlot->CurrentX = axis;\n    else\n        gp.CurrentPlot->CurrentY = axis;\n}\n\nvoid SetAxes(ImAxis x_idx, ImAxis y_idx) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"SetAxes() needs to be called between BeginPlot() and EndPlot()!\");\n    IM_ASSERT_USER_ERROR(x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1, \"X-Axis index out of bounds!\");\n    IM_ASSERT_USER_ERROR(y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT, \"Y-Axis index out of bounds!\");\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[x_idx].Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[y_idx].Enabled, \"Axis is not enabled! Did you forget to call SetupAxis()?\");\n    SetupLock();\n    gp.CurrentPlot->CurrentX = x_idx;\n    gp.CurrentPlot->CurrentY = y_idx;\n}\n\nImPlotPoint PixelsToPlot(float x, float y, ImAxis x_idx, ImAxis y_idx) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!\");\n    IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1),    \"X-Axis index out of bounds!\");\n    IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), \"Y-Axis index out of bounds!\");\n    SetupLock();\n    ImPlotPlot& plot   = *gp.CurrentPlot;\n    ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx];\n    ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx];\n    return ImPlotPoint( x_axis.PixelsToPlot(x), y_axis.PixelsToPlot(y) );\n}\n\nImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_idx, ImAxis y_idx) {\n    return PixelsToPlot(pix.x, pix.y, x_idx, y_idx);\n}\n\nImVec2 PlotToPixels(double x, double y, ImAxis x_idx, ImAxis y_idx) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PlotToPixels() needs to be called between BeginPlot() and EndPlot()!\");\n    IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1),    \"X-Axis index out of bounds!\");\n    IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), \"Y-Axis index out of bounds!\");\n    SetupLock();\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx];\n    ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx];\n    return ImVec2( x_axis.PlotToPixels(x), y_axis.PlotToPixels(y) );\n}\n\nImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_idx, ImAxis y_idx) {\n    return PlotToPixels(plt.x, plt.y, x_idx, y_idx);\n}\n\nImVec2 GetPlotPos() {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"GetPlotPos() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    return gp.CurrentPlot->PlotRect.Min;\n}\n\nImVec2 GetPlotSize() {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"GetPlotSize() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    return gp.CurrentPlot->PlotRect.GetSize();\n}\n\nImPlotPoint GetPlotMousePos(ImAxis x_idx, ImAxis y_idx) {\n    IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, \"GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    return PixelsToPlot(ImGui::GetMousePos(), x_idx, y_idx);\n}\n\nImPlotRect GetPlotLimits(ImAxis x_idx, ImAxis y_idx) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!\");\n    IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1),    \"X-Axis index out of bounds!\");\n    IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), \"Y-Axis index out of bounds!\");\n    SetupLock();\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx];\n    ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx];\n    ImPlotRect limits;\n    limits.X = x_axis.Range;\n    limits.Y = y_axis.Range;\n    return limits;\n}\n\nbool IsPlotHovered() {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"IsPlotHovered() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    return gp.CurrentPlot->Hovered;\n}\n\nbool IsAxisHovered(ImAxis axis) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"IsPlotXAxisHovered() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    return gp.CurrentPlot->Axes[axis].Hovered;\n}\n\nbool IsSubplotsHovered() {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, \"IsSubplotsHovered() needs to be called between BeginSubplots() and EndSubplots()!\");\n    return gp.CurrentSubplot->FrameHovered;\n}\n\nbool IsPlotSelected() {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"IsPlotSelected() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    return gp.CurrentPlot->Selected;\n}\n\nImPlotRect GetPlotSelection(ImAxis x_idx, ImAxis y_idx) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"GetPlotSelection() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    if (!plot.Selected)\n        return ImPlotRect(0,0,0,0);\n    ImPlotPoint p1 = PixelsToPlot(plot.SelectRect.Min + plot.PlotRect.Min, x_idx, y_idx);\n    ImPlotPoint p2 = PixelsToPlot(plot.SelectRect.Max + plot.PlotRect.Min, x_idx, y_idx);\n    ImPlotRect result;\n    result.X.Min = ImMin(p1.x, p2.x);\n    result.X.Max = ImMax(p1.x, p2.x);\n    result.Y.Min = ImMin(p1.y, p2.y);\n    result.Y.Max = ImMax(p1.y, p2.y);\n    return result;\n}\n\nvoid CancelPlotSelection() {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"CancelPlotSelection() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    ImPlotPlot& plot = *gp.CurrentPlot;\n    if (plot.Selected)\n        plot.Selected = plot.Selecting = false;\n}\n\nvoid HideNextItem(bool hidden, ImPlotCond cond) {\n    ImPlotContext& gp = *GImPlot;\n    gp.NextItemData.HasHidden  = true;\n    gp.NextItemData.Hidden     = hidden;\n    gp.NextItemData.HiddenCond = cond;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot Tools\n//-----------------------------------------------------------------------------\n\nvoid Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, bool round) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"Annotation() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    char x_buff[IMPLOT_LABEL_MAX_SIZE];\n    char y_buff[IMPLOT_LABEL_MAX_SIZE];\n    ImPlotAxis& x_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX];\n    ImPlotAxis& y_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentY];\n    LabelAxisValue(x_axis, x, x_buff, sizeof(x_buff), round);\n    LabelAxisValue(y_axis, y, y_buff, sizeof(y_buff), round);\n    Annotation(x,y,col,offset,clamp,\"%s, %s\",x_buff,y_buff);\n}\n\nvoid AnnotationV(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, va_list args) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"Annotation() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    ImVec2 pos = PlotToPixels(x,y,IMPLOT_AUTO,IMPLOT_AUTO);\n    ImU32  bg  = ImGui::GetColorU32(col);\n    ImU32  fg  = col.w == 0 ? GetStyleColorU32(ImPlotCol_InlayText) : CalcTextColor(col);\n    gp.Annotations.AppendV(pos, offset, bg, fg, clamp, fmt, args);\n}\n\nvoid Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, ...) {\n    va_list args;\n    va_start(args, fmt);\n    AnnotationV(x,y,col,offset,clamp,fmt,args);\n    va_end(args);\n}\n\nvoid TagV(ImAxis axis, double v, const ImVec4& col, const char* fmt, va_list args) {\n    ImPlotContext& gp = *GImPlot;\n    SetupLock();\n    ImU32 bg = ImGui::GetColorU32(col);\n    ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_AxisText) : CalcTextColor(col);\n    gp.Tags.AppendV(axis,v,bg,fg,fmt,args);\n}\n\nvoid Tag(ImAxis axis, double v, const ImVec4& col, const char* fmt, ...) {\n    va_list args;\n    va_start(args, fmt);\n    TagV(axis,v,col,fmt,args);\n    va_end(args);\n}\n\nvoid Tag(ImAxis axis, double v, const ImVec4& color, bool round) {\n    ImPlotContext& gp = *GImPlot;\n    SetupLock();\n    char buff[IMPLOT_LABEL_MAX_SIZE];\n    ImPlotAxis& ax = gp.CurrentPlot->Axes[axis];\n    LabelAxisValue(ax, v, buff, sizeof(buff), round);\n    Tag(axis,v,color,\"%s\",buff);\n}\n\nIMPLOT_API void TagX(double x, const ImVec4& color, bool round) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"TagX() needs to be called between BeginPlot() and EndPlot()!\");\n    Tag(gp.CurrentPlot->CurrentX, x, color, round);\n}\n\nIMPLOT_API void TagX(double x, const ImVec4& color, const char* fmt, ...) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"TagX() needs to be called between BeginPlot() and EndPlot()!\");\n    va_list args;\n    va_start(args, fmt);\n    TagV(gp.CurrentPlot->CurrentX,x,color,fmt,args);\n    va_end(args);\n}\n\nIMPLOT_API void TagXV(double x, const ImVec4& color, const char* fmt, va_list args) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"TagX() needs to be called between BeginPlot() and EndPlot()!\");\n    TagV(gp.CurrentPlot->CurrentX, x, color, fmt, args);\n}\n\nIMPLOT_API void TagY(double y, const ImVec4& color, bool round) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"TagY() needs to be called between BeginPlot() and EndPlot()!\");\n    Tag(gp.CurrentPlot->CurrentY, y, color, round);\n}\n\nIMPLOT_API void TagY(double y, const ImVec4& color, const char* fmt, ...) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"TagY() needs to be called between BeginPlot() and EndPlot()!\");\n    va_list args;\n    va_start(args, fmt);\n    TagV(gp.CurrentPlot->CurrentY,y,color,fmt,args);\n    va_end(args);\n}\n\nIMPLOT_API void TagYV(double y, const ImVec4& color, const char* fmt, va_list args) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"TagY() needs to be called between BeginPlot() and EndPlot()!\");\n    TagV(gp.CurrentPlot->CurrentY, y, color, fmt, args);\n}\n\nstatic const float DRAG_GRAB_HALF_SIZE = 4.0f;\n\nbool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {\n    ImGui::PushID(\"#IMPLOT_DRAG_POINT\");\n    IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, \"DragPoint() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n\n    if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) {\n        FitPoint(ImPlotPoint(*x,*y));\n    }\n\n    const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs);\n    const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors);\n    const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed);\n    const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, radius);\n    const ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;\n    const ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);\n\n    ImVec2 pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO);\n    const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);\n    ImRect rect(pos.x-grab_half_size,pos.y-grab_half_size,pos.x+grab_half_size,pos.y+grab_half_size);\n    bool hovered = false, held = false;\n\n    ImGui::KeepAliveID(id);\n    if (input) {\n        bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);\n        if (out_clicked) *out_clicked = clicked;\n        if (out_hovered) *out_hovered = hovered;\n        if (out_held)    *out_held    = held;\n    }\n\n    bool modified = false;\n    if (held && ImGui::IsMouseDragging(0)) {\n        *x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;\n        *y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;\n        modified = true;\n    }\n\n    PushPlotClipRect();\n    ImDrawList& DrawList = *GetPlotDrawList();\n    if ((hovered || held) && show_curs)\n        ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);\n    if (modified && no_delay)\n        pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO);\n    DrawList.AddCircleFilled(pos, radius, col32);\n    PopPlotClipRect();\n\n    ImGui::PopID();\n    return modified;\n}\n\nbool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {\n    // ImGui::PushID(\"#IMPLOT_DRAG_LINE_X\");\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"DragLineX() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n\n    if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) {\n        FitPointX(*value);\n    }\n\n    const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs);\n    const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors);\n    const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed);\n    const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2);\n    float yt = gp.CurrentPlot->PlotRect.Min.y;\n    float yb = gp.CurrentPlot->PlotRect.Max.y;\n    float x  = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x);\n    const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);\n    ImRect rect(x-grab_half_size,yt,x+grab_half_size,yb);\n    bool hovered = false, held = false;\n\n    ImGui::KeepAliveID(id);\n    if (input) {\n        bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);\n        if (out_clicked) *out_clicked = clicked;\n        if (out_hovered) *out_hovered = hovered;\n        if (out_held)    *out_held    = held;\n    }\n\n    if ((hovered || held) && show_curs)\n        ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);\n\n    float len = gp.Style.MajorTickLen.x;\n    ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;\n    ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);\n\n    bool modified = false;\n    if (held && ImGui::IsMouseDragging(0)) {\n        *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;\n        modified = true;\n    }\n\n    PushPlotClipRect();\n    ImDrawList& DrawList = *GetPlotDrawList();\n    if (modified && no_delay)\n        x  = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x);\n    DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb),     col32,   thickness);\n    DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness);\n    DrawList.AddLine(ImVec2(x,yb), ImVec2(x,yb-len), col32, 3*thickness);\n    PopPlotClipRect();\n\n    // ImGui::PopID();\n    return modified;\n}\n\nbool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {\n    ImGui::PushID(\"#IMPLOT_DRAG_LINE_Y\");\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"DragLineY() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n\n    if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) {\n        FitPointY(*value);\n    }\n\n    const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs);\n    const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors);\n    const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed);\n    const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2);\n    float xl = gp.CurrentPlot->PlotRect.Min.x;\n    float xr = gp.CurrentPlot->PlotRect.Max.x;\n    float y  = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y);\n\n    const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);\n    ImRect rect(xl,y-grab_half_size,xr,y+grab_half_size);\n    bool hovered = false, held = false;\n\n    ImGui::KeepAliveID(id);\n    if (input) {\n        bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held);\n        if (out_clicked) *out_clicked = clicked;\n        if (out_hovered) *out_hovered = hovered;\n        if (out_held)    *out_held    = held;\n    }\n\n    if ((hovered || held) && show_curs)\n        ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);\n\n    float len = gp.Style.MajorTickLen.y;\n    ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;\n    ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);\n\n    bool modified = false;\n    if (held && ImGui::IsMouseDragging(0)) {\n        *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;\n        modified = true;\n    }\n\n    PushPlotClipRect();\n    ImDrawList& DrawList = *GetPlotDrawList();\n    if (modified && no_delay)\n        y  = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y);\n    DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y),     col32,   thickness);\n    DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness);\n    DrawList.AddLine(ImVec2(xr,y), ImVec2(xr-len,y), col32, 3*thickness);\n    PopPlotClipRect();\n\n    ImGui::PopID();\n    return modified;\n}\n\nbool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {\n    ImGui::PushID(\"#IMPLOT_DRAG_RECT\");\n    IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, \"DragRect() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n\n    if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) {\n        FitPoint(ImPlotPoint(*x_min,*y_min));\n        FitPoint(ImPlotPoint(*x_max,*y_max));\n    }\n\n    const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs);\n    const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors);\n    const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed);\n    bool    h[] = {true,false,true,false};\n    double* x[] = {x_min,x_max,x_max,x_min};\n    double* y[] = {y_min,y_min,y_max,y_max};\n    ImVec2 p[4];\n    for (int i = 0; i < 4; ++i)\n        p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO);\n    ImVec2 pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO);\n    ImRect rect(ImMin(p[0],p[2]),ImMax(p[0],p[2]));\n    ImRect rect_grab = rect; rect_grab.Expand(DRAG_GRAB_HALF_SIZE);\n\n    ImGuiMouseCursor cur[4];\n    if (show_curs) {\n        cur[0] = (rect.Min.x == p[0].x && rect.Min.y == p[0].y) || (rect.Max.x == p[0].x && rect.Max.y == p[0].y) ? ImGuiMouseCursor_ResizeNWSE : ImGuiMouseCursor_ResizeNESW;\n        cur[1] = cur[0] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;\n        cur[2] = cur[1] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;\n        cur[3] = cur[2] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;\n    }\n\n    ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col;\n    ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color);\n    color.w *= 0.25f;\n    ImU32 col32_a = ImGui::ColorConvertFloat4ToU32(color);\n    const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id);\n\n    bool modified = false;\n    bool clicked = false, hovered = false, held = false;\n    ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE);\n\n    ImGui::KeepAliveID(id);\n    if (input) {\n        // middle point\n        clicked = ImGui::ButtonBehavior(b_rect,id,&hovered,&held);\n        if (out_clicked) *out_clicked = clicked;\n        if (out_hovered) *out_hovered = hovered;\n        if (out_held)    *out_held    = held;\n    }\n\n    if ((hovered || held) && show_curs)\n        ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);\n    if (held && ImGui::IsMouseDragging(0)) {\n        for (int i = 0; i < 4; ++i) {\n            ImPlotPoint pp = PixelsToPlot(p[i] + ImGui::GetIO().MouseDelta,IMPLOT_AUTO,IMPLOT_AUTO);\n            *y[i] = pp.y;\n            *x[i] = pp.x;\n        }\n        modified = true;\n    }\n\n    for (int i = 0; i < 4; ++i) {\n        // points\n        b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE);\n        ImGuiID p_id = id + i + 1;\n        ImGui::KeepAliveID(p_id);\n        if (input) {\n            clicked = ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held);\n            if (out_clicked) *out_clicked = *out_clicked || clicked;\n            if (out_hovered) *out_hovered = *out_hovered || hovered;\n            if (out_held)    *out_held    = *out_held    || held;\n        }\n        if ((hovered || held) && show_curs)\n            ImGui::SetMouseCursor(cur[i]);\n\n        if (held && ImGui::IsMouseDragging(0)) {\n            *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;\n            *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;\n            modified = true;\n        }\n\n        // edges\n        ImVec2 e_min = ImMin(p[i],p[(i+1)%4]);\n        ImVec2 e_max = ImMax(p[i],p[(i+1)%4]);\n        b_rect = h[i] ? ImRect(e_min.x + DRAG_GRAB_HALF_SIZE, e_min.y - DRAG_GRAB_HALF_SIZE, e_max.x - DRAG_GRAB_HALF_SIZE, e_max.y + DRAG_GRAB_HALF_SIZE)\n                    : ImRect(e_min.x - DRAG_GRAB_HALF_SIZE, e_min.y + DRAG_GRAB_HALF_SIZE, e_max.x + DRAG_GRAB_HALF_SIZE, e_max.y - DRAG_GRAB_HALF_SIZE);\n        ImGuiID e_id = id + i + 5;\n        ImGui::KeepAliveID(e_id);\n        if (input) {\n            clicked = ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held);\n            if (out_clicked) *out_clicked = *out_clicked || clicked;\n            if (out_hovered) *out_hovered = *out_hovered || hovered;\n            if (out_held)    *out_held    = *out_held    || held;\n        }\n        if ((hovered || held) && show_curs)\n            h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);\n        if (held && ImGui::IsMouseDragging(0)) {\n            if (h[i])\n                *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y;\n            else\n                *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x;\n            modified = true;\n        }\n        if (hovered && ImGui::IsMouseDoubleClicked(0))\n        {\n            ImPlotRect b = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO);\n            if (h[i])\n                *y[i] = ((y[i] == y_min && *y_min < *y_max) || (y[i] == y_max && *y_max < *y_min)) ? b.Y.Min : b.Y.Max;\n            else\n                *x[i] = ((x[i] == x_min && *x_min < *x_max) || (x[i] == x_max && *x_max < *x_min)) ? b.X.Min : b.X.Max;\n            modified = true;\n        }\n    }\n\n    const bool mouse_inside = rect_grab.Contains(ImGui::GetMousePos());\n    const bool mouse_clicked = ImGui::IsMouseClicked(0);\n    const bool mouse_down = ImGui::IsMouseDown(0);\n    if (input && mouse_inside) {\n        if (out_clicked) *out_clicked = *out_clicked || mouse_clicked;\n        if (out_hovered) *out_hovered = true;\n        if (out_held)    *out_held    = *out_held    || mouse_down;\n    }\n\n    PushPlotClipRect();\n    ImDrawList& DrawList = *GetPlotDrawList();\n    if (modified && no_delay) {\n        for (int i = 0; i < 4; ++i)\n            p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO);\n        pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO);\n        rect = ImRect(ImMin(p[0],p[2]),ImMax(p[0],p[2]));\n    }\n    DrawList.AddRectFilled(rect.Min, rect.Max, col32_a);\n    DrawList.AddRect(rect.Min, rect.Max, col32);\n    if (input && (modified || mouse_inside)) {\n        DrawList.AddCircleFilled(pc,DRAG_GRAB_HALF_SIZE,col32);\n        for (int i = 0; i < 4; ++i)\n            DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32);\n    }\n    PopPlotClipRect();\n    ImGui::PopID();\n    return modified;\n}\n\nbool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) {\n    return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, flags, out_clicked, out_hovered, out_held);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Legend Utils and Tools\n//-----------------------------------------------------------------------------\n\nbool IsLegendEntryHovered(const char* label_id) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, \"IsPlotItemHighlight() needs to be called within an itemized context!\");\n    SetupLock();\n    ImGuiID id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID);\n    ImPlotItem* item = gp.CurrentItems->GetItem(id);\n    return item && item->LegendHovered;\n}\n\nbool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, \"BeginLegendPopup() needs to be called within an itemized context!\");\n    SetupLock();\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    if (window->SkipItems)\n        return false;\n    ImGuiID id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID);\n    if (ImGui::IsMouseReleased(mouse_button)) {\n        ImPlotItem* item = gp.CurrentItems->GetItem(id);\n        if (item && item->LegendHovered)\n            ImGui::OpenPopupEx(id);\n    }\n    return ImGui::BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);\n}\n\nvoid EndLegendPopup() {\n    SetupLock();\n    ImGui::EndPopup();\n}\n\nvoid ShowAltLegend(const char* title_id, bool vertical, const ImVec2 size, bool interactable) {\n    ImPlotContext& gp    = *GImPlot;\n    ImGuiContext &G      = *GImGui;\n    ImGuiWindow * Window = G.CurrentWindow;\n    if (Window->SkipItems)\n        return;\n    ImDrawList &DrawList = *Window->DrawList;\n    ImPlotPlot* plot = GetPlot(title_id);\n    ImVec2 legend_size;\n    ImVec2 default_size = gp.Style.LegendPadding * 2;\n    if (plot != nullptr) {\n        legend_size  = CalcLegendSize(plot->Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical);\n        default_size = legend_size + gp.Style.LegendPadding * 2;\n    }\n    ImVec2 frame_size = ImGui::CalcItemSize(size, default_size.x, default_size.y);\n    ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);\n    ImGui::ItemSize(bb_frame);\n    if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame))\n        return;\n    ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding);\n    DrawList.PushClipRect(bb_frame.Min, bb_frame.Max, true);\n    if (plot != nullptr) {\n        const ImVec2 legend_pos  = GetLocationPos(bb_frame, legend_size, 0, gp.Style.LegendPadding);\n        const ImRect legend_bb(legend_pos, legend_pos + legend_size);\n        interactable = interactable && bb_frame.Contains(ImGui::GetIO().MousePos);\n        // render legend box\n        ImU32  col_bg      = GetStyleColorU32(ImPlotCol_LegendBg);\n        ImU32  col_bd      = GetStyleColorU32(ImPlotCol_LegendBorder);\n        DrawList.AddRectFilled(legend_bb.Min, legend_bb.Max, col_bg);\n        DrawList.AddRect(legend_bb.Min, legend_bb.Max, col_bd);\n        // render entries\n        ShowLegendEntries(plot->Items, legend_bb, interactable, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical, DrawList);\n    }\n    DrawList.PopClipRect();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Drag and Drop Utils\n//-----------------------------------------------------------------------------\n\nbool BeginDragDropTargetPlot() {\n    SetupLock();\n    ImPlotContext& gp = *GImPlot;\n    ImRect rect = gp.CurrentPlot->PlotRect;\n    return ImGui::BeginDragDropTargetCustom(rect, gp.CurrentPlot->ID);\n}\n\nbool BeginDragDropTargetAxis(ImAxis axis) {\n    SetupLock();\n    ImPlotPlot& plot = *GImPlot->CurrentPlot;\n    ImPlotAxis& ax = plot.Axes[axis];\n    ImRect rect = ax.HoverRect;\n    rect.Expand(-3.5f);\n    return ImGui::BeginDragDropTargetCustom(rect, ax.ID);\n}\n\nbool BeginDragDropTargetLegend() {\n    SetupLock();\n    ImPlotItemGroup& items = *GImPlot->CurrentItems;\n    ImRect rect = items.Legend.RectClamped;\n    return ImGui::BeginDragDropTargetCustom(rect, items.ID);\n}\n\nvoid EndDragDropTarget() {\n    SetupLock();\n\tImGui::EndDragDropTarget();\n}\n\nbool BeginDragDropSourcePlot(ImGuiDragDropFlags flags) {\n    SetupLock();\n    ImPlotContext& gp = *GImPlot;\n    ImPlotPlot* plot = gp.CurrentPlot;\n    if (GImGui->IO.KeyMods == gp.InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == plot->ID)\n        return ImGui::ItemAdd(plot->PlotRect, plot->ID) && ImGui::BeginDragDropSource(flags);\n    return false;\n}\n\nbool BeginDragDropSourceAxis(ImAxis idx, ImGuiDragDropFlags flags) {\n    SetupLock();\n    ImPlotContext& gp = *GImPlot;\n    ImPlotAxis& axis = gp.CurrentPlot->Axes[idx];\n    if (GImGui->IO.KeyMods == gp.InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == axis.ID)\n        return ImGui::ItemAdd(axis.HoverRect, axis.ID) && ImGui::BeginDragDropSource(flags);\n    return false;\n}\n\nbool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags) {\n    SetupLock();\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, \"BeginDragDropSourceItem() needs to be called within an itemized context!\");\n    ImGuiID item_id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID);\n    ImPlotItem* item = gp.CurrentItems->GetItem(item_id);\n    if (item != nullptr) {\n        return ImGui::ItemAdd(item->LegendHoverRect, item->ID) && ImGui::BeginDragDropSource(flags);\n    }\n    return false;\n}\n\nvoid EndDragDropSource() {\n    SetupLock();\n    ImGui::EndDragDropSource();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Aligned Plots\n//-----------------------------------------------------------------------------\n\nbool BeginAlignedPlots(const char* group_id, bool vertical) {\n    IM_ASSERT_USER_ERROR(GImPlot != nullptr, \"No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?\");\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentAlignmentH == nullptr && gp.CurrentAlignmentV == nullptr, \"Mismatched BeginAlignedPlots()/EndAlignedPlots()!\");\n    ImGuiContext &G = *GImGui;\n    ImGuiWindow * Window = G.CurrentWindow;\n    if (Window->SkipItems)\n        return false;\n    const ImGuiID ID = Window->GetID(group_id);\n    ImPlotAlignmentData* alignment = gp.AlignmentData.GetOrAddByKey(ID);\n    if (vertical)\n        gp.CurrentAlignmentV = alignment;\n    else\n        gp.CurrentAlignmentH = alignment;\n    if (alignment->Vertical != vertical)\n        alignment->Reset();\n    alignment->Vertical = vertical;\n    alignment->Begin();\n    return true;\n}\n\nvoid EndAlignedPlots() {\n    IM_ASSERT_USER_ERROR(GImPlot != nullptr, \"No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?\");\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentAlignmentH != nullptr || gp.CurrentAlignmentV != nullptr, \"Mismatched BeginAlignedPlots()/EndAlignedPlots()!\");\n    ImPlotAlignmentData* alignment = gp.CurrentAlignmentH != nullptr ? gp.CurrentAlignmentH : (gp.CurrentAlignmentV != nullptr ? gp.CurrentAlignmentV : nullptr);\n    if (alignment)\n        alignment->End();\n    ResetCtxForNextAlignedPlots(GImPlot);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot and Item Styling\n//-----------------------------------------------------------------------------\n\nImPlotStyle& GetStyle() {\n    IM_ASSERT_USER_ERROR(GImPlot != nullptr, \"No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?\");\n    ImPlotContext& gp = *GImPlot;\n    return gp.Style;\n}\n\nvoid PushStyleColor(ImPlotCol idx, ImU32 col) {\n    ImPlotContext& gp = *GImPlot;\n    ImGuiColorMod backup;\n    backup.Col = (ImGuiCol)idx;\n    backup.BackupValue = gp.Style.Colors[idx];\n    gp.ColorModifiers.push_back(backup);\n    gp.Style.Colors[idx] = ImGui::ColorConvertU32ToFloat4(col);\n}\n\nvoid PushStyleColor(ImPlotCol idx, const ImVec4& col) {\n    ImPlotContext& gp = *GImPlot;\n    ImGuiColorMod backup;\n    backup.Col = (ImGuiCol)idx;\n    backup.BackupValue = gp.Style.Colors[idx];\n    gp.ColorModifiers.push_back(backup);\n    gp.Style.Colors[idx] = col;\n}\n\nvoid PopStyleColor(int count) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(count <= gp.ColorModifiers.Size, \"You can't pop more modifiers than have been pushed!\");\n    while (count > 0)\n    {\n        ImGuiColorMod& backup = gp.ColorModifiers.back();\n        gp.Style.Colors[backup.Col] = backup.BackupValue;\n        gp.ColorModifiers.pop_back();\n        count--;\n    }\n}\n\nvoid PushStyleVar(ImPlotStyleVar idx, float val) {\n    ImPlotContext& gp = *GImPlot;\n    const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx);\n    if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) {\n        float* pvar = (float*)var_info->GetVarPtr(&gp.Style);\n        gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));\n        *pvar = val;\n        return;\n    }\n    IM_ASSERT(0 && \"Called PushStyleVar() float variant but variable is not a float!\");\n}\n\nvoid PushStyleVar(ImPlotStyleVar idx, int val) {\n    ImPlotContext& gp = *GImPlot;\n    const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx);\n    if (var_info->Type == ImGuiDataType_S32 && var_info->Count == 1) {\n        int* pvar = (int*)var_info->GetVarPtr(&gp.Style);\n        gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));\n        *pvar = val;\n        return;\n    }\n    else if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) {\n        float* pvar = (float*)var_info->GetVarPtr(&gp.Style);\n        gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));\n        *pvar = (float)val;\n        return;\n    }\n    IM_ASSERT(0 && \"Called PushStyleVar() int variant but variable is not a int!\");\n}\n\nvoid PushStyleVar(ImPlotStyleVar idx, const ImVec2& val)\n{\n    ImPlotContext& gp = *GImPlot;\n    const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx);\n    if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)\n    {\n        ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&gp.Style);\n        gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));\n        *pvar = val;\n        return;\n    }\n    IM_ASSERT(0 && \"Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!\");\n}\n\nvoid PopStyleVar(int count) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(count <= gp.StyleModifiers.Size, \"You can't pop more modifiers than have been pushed!\");\n    while (count > 0) {\n        ImGuiStyleMod& backup = gp.StyleModifiers.back();\n        const ImPlotStyleVarInfo* info = GetPlotStyleVarInfo(backup.VarIdx);\n        void* data = info->GetVarPtr(&gp.Style);\n        if (info->Type == ImGuiDataType_Float && info->Count == 1) {\n            ((float*)data)[0] = backup.BackupFloat[0];\n        }\n        else if (info->Type == ImGuiDataType_Float && info->Count == 2) {\n             ((float*)data)[0] = backup.BackupFloat[0];\n             ((float*)data)[1] = backup.BackupFloat[1];\n        }\n        else if (info->Type == ImGuiDataType_S32 && info->Count == 1) {\n            ((int*)data)[0] = backup.BackupInt[0];\n        }\n        gp.StyleModifiers.pop_back();\n        count--;\n    }\n}\n\n//------------------------------------------------------------------------------\n// [Section] Colormaps\n//------------------------------------------------------------------------------\n\nImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size, bool qual) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(size > 1, \"The colormap size must be greater than 1!\");\n    IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, \"The colormap name has already been used!\");\n    ImVector<ImU32> buffer;\n    buffer.resize(size);\n    for (int i = 0; i < size; ++i)\n        buffer[i] = ImGui::ColorConvertFloat4ToU32(colormap[i]);\n    return gp.ColormapData.Append(name, buffer.Data, size, qual);\n}\n\nImPlotColormap AddColormap(const char* name, const ImU32*  colormap, int size, bool qual) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(size > 1, \"The colormap size must be greater than 1!\");\n    IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, \"The colormap name has already be used!\");\n    return gp.ColormapData.Append(name, colormap, size, qual);\n}\n\nint GetColormapCount() {\n    ImPlotContext& gp = *GImPlot;\n    return gp.ColormapData.Count;\n}\n\nconst char* GetColormapName(ImPlotColormap colormap) {\n    ImPlotContext& gp = *GImPlot;\n    return gp.ColormapData.GetName(colormap);\n}\n\nImPlotColormap GetColormapIndex(const char* name) {\n    ImPlotContext& gp = *GImPlot;\n    return gp.ColormapData.GetIndex(name);\n}\n\nvoid PushColormap(ImPlotColormap colormap) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(colormap >= 0 && colormap < gp.ColormapData.Count, \"The colormap index is invalid!\");\n    gp.ColormapModifiers.push_back(gp.Style.Colormap);\n    gp.Style.Colormap = colormap;\n}\n\nvoid PushColormap(const char* name) {\n    ImPlotContext& gp = *GImPlot;\n    ImPlotColormap idx = gp.ColormapData.GetIndex(name);\n    IM_ASSERT_USER_ERROR(idx != -1, \"The colormap name is invalid!\");\n    PushColormap(idx);\n}\n\nvoid PopColormap(int count) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(count <= gp.ColormapModifiers.Size, \"You can't pop more modifiers than have been pushed!\");\n    while (count > 0) {\n        const ImPlotColormap& backup = gp.ColormapModifiers.back();\n        gp.Style.Colormap     = backup;\n        gp.ColormapModifiers.pop_back();\n        count--;\n    }\n}\n\nImU32 NextColormapColorU32() {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, \"NextColormapColor() needs to be called between BeginPlot() and EndPlot()!\");\n    int idx = gp.CurrentItems->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap);\n    ImU32 col  = gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx);\n    gp.CurrentItems->ColormapIdx++;\n    return col;\n}\n\nImVec4 NextColormapColor() {\n    return ImGui::ColorConvertU32ToFloat4(NextColormapColorU32());\n}\n\nint GetColormapSize(ImPlotColormap cmap) {\n    ImPlotContext& gp = *GImPlot;\n    cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;\n    IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, \"Invalid colormap index!\");\n    return gp.ColormapData.GetKeyCount(cmap);\n}\n\nImU32 GetColormapColorU32(int idx, ImPlotColormap cmap) {\n    ImPlotContext& gp = *GImPlot;\n    cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;\n    IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, \"Invalid colormap index!\");\n    idx = idx % gp.ColormapData.GetKeyCount(cmap);\n    return gp.ColormapData.GetKeyColor(cmap, idx);\n}\n\nImVec4 GetColormapColor(int idx, ImPlotColormap cmap) {\n    return ImGui::ColorConvertU32ToFloat4(GetColormapColorU32(idx,cmap));\n}\n\nImU32  SampleColormapU32(float t, ImPlotColormap cmap) {\n    ImPlotContext& gp = *GImPlot;\n    cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;\n    IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, \"Invalid colormap index!\");\n    return gp.ColormapData.LerpTable(cmap, t);\n}\n\nImVec4 SampleColormap(float t, ImPlotColormap cmap) {\n    return ImGui::ColorConvertU32ToFloat4(SampleColormapU32(t,cmap));\n}\n\nvoid RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous) {\n    const int n = continuous ? size - 1 : size;\n    ImU32 col1, col2;\n    if (vert) {\n        const float step = bounds.GetHeight() / n;\n        ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Max.x, bounds.Min.y + step);\n        for (int i = 0; i < n; ++i) {\n            if (reversed) {\n                col1 = colors[size-i-1];\n                col2 = continuous ? colors[size-i-2] : col1;\n            }\n            else {\n                col1 = colors[i];\n                col2 = continuous ? colors[i+1] : col1;\n            }\n            DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2);\n            rect.TranslateY(step);\n        }\n    }\n    else {\n        const float step = bounds.GetWidth() / n;\n        ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Min.x + step, bounds.Max.y);\n        for (int i = 0; i < n; ++i) {\n            if (reversed) {\n                col1 = colors[size-i-1];\n                col2 = continuous ? colors[size-i-2] : col1;\n            }\n            else {\n                col1 = colors[i];\n                col2 = continuous ? colors[i+1] : col1;\n            }\n            DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col2, col2, col1);\n            rect.TranslateX(step);\n        }\n    }\n}\n\nvoid ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size, const char* format, ImPlotColormapScaleFlags flags, ImPlotColormap cmap) {\n    ImGuiContext &G      = *GImGui;\n    ImGuiWindow * Window = G.CurrentWindow;\n    if (Window->SkipItems)\n        return;\n\n    const ImGuiID ID = Window->GetID(label);\n    ImVec2 label_size(0,0);\n    if (!ImHasFlag(flags, ImPlotColormapScaleFlags_NoLabel)) {\n        label_size = ImGui::CalcTextSize(label,nullptr,true);\n    }\n\n    ImPlotContext& gp = *GImPlot;\n    cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;\n    IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, \"Invalid colormap index!\");\n\n    ImVec2 frame_size  = ImGui::CalcItemSize(size, 0, gp.Style.PlotDefaultSize.y);\n    if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f)\n        frame_size.y = gp.Style.PlotMinSize.y;\n\n    ImPlotRange range(ImMin(scale_min,scale_max), ImMax(scale_min,scale_max));\n    gp.CTicker.Reset();\n    Locator_Default(gp.CTicker, range, frame_size.y, true, Formatter_Default, (void*)format);\n\n    const bool rend_label = label_size.x > 0;\n    const float txt_off   = gp.Style.LabelPadding.x;\n    const float pad       = txt_off + gp.CTicker.MaxSize.x + (rend_label ? txt_off + label_size.y : 0);\n    float bar_w           = 20;\n    if (frame_size.x == 0)\n        frame_size.x = bar_w + pad + 2 * gp.Style.PlotPadding.x;\n    else {\n        bar_w = frame_size.x - (pad + 2 * gp.Style.PlotPadding.x);\n        if (bar_w < gp.Style.MajorTickLen.y)\n            bar_w = gp.Style.MajorTickLen.y;\n    }\n\n    ImDrawList &DrawList = *Window->DrawList;\n    ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);\n    ImGui::ItemSize(bb_frame);\n    if (!ImGui::ItemAdd(bb_frame, ID, &bb_frame))\n        return;\n\n    ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding);\n\n    const bool opposite = ImHasFlag(flags, ImPlotColormapScaleFlags_Opposite);\n    const bool inverted = ImHasFlag(flags, ImPlotColormapScaleFlags_Invert);\n    const bool reversed = scale_min > scale_max;\n\n    float bb_grad_shift = opposite ? pad : 0;\n    ImRect bb_grad(bb_frame.Min + gp.Style.PlotPadding + ImVec2(bb_grad_shift, 0),\n                   bb_frame.Min + ImVec2(bar_w + gp.Style.PlotPadding.x + bb_grad_shift,\n                                         frame_size.y - gp.Style.PlotPadding.y));\n\n    ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true);\n    const ImU32 col_text = ImGui::GetColorU32(ImGuiCol_Text);\n\n    const bool invert_scale = inverted ? (reversed ? false : true) : (reversed ? true : false);\n    const float y_min = invert_scale ? bb_grad.Max.y : bb_grad.Min.y;\n    const float y_max = invert_scale ? bb_grad.Min.y : bb_grad.Max.y;\n\n    RenderColorBar(gp.ColormapData.GetKeys(cmap), gp.ColormapData.GetKeyCount(cmap), DrawList, bb_grad, true, !inverted, !gp.ColormapData.IsQual(cmap));\n    for (int i = 0; i < gp.CTicker.TickCount(); ++i) {\n        const double y_pos_plt = gp.CTicker.Ticks[i].PlotPos;\n        const float y_pos = ImRemap((float)y_pos_plt, (float)range.Max, (float)range.Min, y_min, y_max);\n        const float tick_width = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y;\n        const float tick_thick = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y;\n        const float tick_t     = (float)((y_pos_plt - scale_min) / (scale_max - scale_min));\n        const ImU32 tick_col = CalcTextColor(gp.ColormapData.LerpTable(cmap,tick_t));\n        if (y_pos < bb_grad.Max.y - 2 && y_pos > bb_grad.Min.y + 2) {\n            DrawList.AddLine(opposite ? ImVec2(bb_grad.Min.x+1, y_pos) : ImVec2(bb_grad.Max.x-1, y_pos),\n                             opposite ? ImVec2(bb_grad.Min.x + tick_width, y_pos) : ImVec2(bb_grad.Max.x - tick_width, y_pos),\n                             tick_col,\n                             tick_thick);\n        }\n        const float txt_x = opposite ? bb_grad.Min.x - txt_off - gp.CTicker.Ticks[i].LabelSize.x : bb_grad.Max.x + txt_off;\n        const float txt_y = y_pos - gp.CTicker.Ticks[i].LabelSize.y * 0.5f;\n        DrawList.AddText(ImVec2(txt_x, txt_y), col_text, gp.CTicker.GetText(i));\n    }\n\n    if (rend_label) {\n        const float pos_x = opposite ? bb_frame.Min.x + gp.Style.PlotPadding.x : bb_grad.Max.x + 2 * txt_off + gp.CTicker.MaxSize.x;\n        const float pos_y = bb_grad.GetCenter().y + label_size.x * 0.5f;\n        const char* label_end = ImGui::FindRenderedTextEnd(label);\n        AddTextVertical(&DrawList,ImVec2(pos_x,pos_y),col_text,label,label_end);\n    }\n    DrawList.AddRect(bb_grad.Min, bb_grad.Max, GetStyleColorU32(ImPlotCol_PlotBorder));\n    ImGui::PopClipRect();\n}\n\nbool ColormapSlider(const char* label, float* t, ImVec4* out, const char* format, ImPlotColormap cmap) {\n    *t = ImClamp(*t,0.0f,1.0f);\n    ImGuiContext &G      = *GImGui;\n    ImGuiWindow * Window = G.CurrentWindow;\n    if (Window->SkipItems)\n        return false;\n    ImPlotContext& gp = *GImPlot;\n    cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;\n    IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, \"Invalid colormap index!\");\n    const ImU32* keys  = gp.ColormapData.GetKeys(cmap);\n    const int    count = gp.ColormapData.GetKeyCount(cmap);\n    const bool   qual  = gp.ColormapData.IsQual(cmap);\n    const ImVec2 pos  = ImGui::GetCurrentWindow()->DC.CursorPos;\n    const float w     = ImGui::CalcItemWidth();\n    const float h     = ImGui::GetFrameHeight();\n    const ImRect rect = ImRect(pos.x,pos.y,pos.x+w,pos.y+h);\n    RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual);\n    const ImU32 grab = CalcTextColor(gp.ColormapData.LerpTable(cmap,*t));\n    // const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap,0.5f));\n    ImGui::PushStyleColor(ImGuiCol_FrameBg,IM_COL32_BLACK_TRANS);\n    ImGui::PushStyleColor(ImGuiCol_FrameBgActive,IM_COL32_BLACK_TRANS);\n    ImGui::PushStyleColor(ImGuiCol_FrameBgHovered,ImVec4(1,1,1,0.1f));\n    ImGui::PushStyleColor(ImGuiCol_SliderGrab,grab);\n    ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, grab);\n    ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize,2);\n    ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0);\n    const bool changed = ImGui::SliderFloat(label,t,0,1,format);\n    ImGui::PopStyleColor(5);\n    ImGui::PopStyleVar(2);\n    if (out != nullptr)\n        *out = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.LerpTable(cmap,*t));\n    return changed;\n}\n\nbool ColormapButton(const char* label, const ImVec2& size_arg, ImPlotColormap cmap) {\n    ImGuiContext &G      = *GImGui;\n    const ImGuiStyle& style = G.Style;\n    ImGuiWindow * Window = G.CurrentWindow;\n    if (Window->SkipItems)\n        return false;\n    ImPlotContext& gp = *GImPlot;\n    cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap;\n    IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, \"Invalid colormap index!\");\n    const ImU32* keys  = gp.ColormapData.GetKeys(cmap);\n    const int    count = gp.ColormapData.GetKeyCount(cmap);\n    const bool   qual  = gp.ColormapData.IsQual(cmap);\n    const ImVec2 pos  = ImGui::GetCurrentWindow()->DC.CursorPos;\n    const ImVec2 label_size = ImGui::CalcTextSize(label, nullptr, true);\n    ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);\n    const ImRect rect = ImRect(pos.x,pos.y,pos.x+size.x,pos.y+size.y);\n    RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual);\n    const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap,G.Style.ButtonTextAlign.x));\n    ImGui::PushStyleColor(ImGuiCol_Button,IM_COL32_BLACK_TRANS);\n    ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ImVec4(1,1,1,0.1f));\n    ImGui::PushStyleColor(ImGuiCol_ButtonActive,ImVec4(1,1,1,0.2f));\n    ImGui::PushStyleColor(ImGuiCol_Text,text);\n    ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0);\n    const bool pressed = ImGui::Button(label,size);\n    ImGui::PopStyleColor(4);\n    ImGui::PopStyleVar(1);\n    return pressed;\n}\n\n//-----------------------------------------------------------------------------\n// [Section] Miscellaneous\n//-----------------------------------------------------------------------------\n\nImPlotInputMap& GetInputMap() {\n    IM_ASSERT_USER_ERROR(GImPlot != nullptr, \"No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?\");\n    ImPlotContext& gp = *GImPlot;\n    return gp.InputMap;\n}\n\nvoid MapInputDefault(ImPlotInputMap* dst) {\n    ImPlotInputMap& map = dst ? *dst : GetInputMap();\n    map.Pan             = ImGuiMouseButton_Left;\n    map.PanMod          = ImGuiMod_None;\n    map.Fit             = ImGuiMouseButton_Left;\n    map.Menu            = ImGuiMouseButton_Right;\n    map.Select          = ImGuiMouseButton_Right;\n    map.SelectMod       = ImGuiMod_None;\n    map.SelectCancel    = ImGuiMouseButton_Left;\n    map.SelectHorzMod   = ImGuiMod_Alt;\n    map.SelectVertMod   = ImGuiMod_Shift;\n    map.OverrideMod     = ImGuiMod_Ctrl;\n    map.ZoomMod         = ImGuiMod_None;\n    map.ZoomRate        = 0.1f;\n}\n\nvoid MapInputReverse(ImPlotInputMap* dst) {\n    ImPlotInputMap& map = dst ? *dst : GetInputMap();\n    map.Pan             = ImGuiMouseButton_Right;\n    map.PanMod          = ImGuiMod_None;\n    map.Fit             = ImGuiMouseButton_Left;\n    map.Menu            = ImGuiMouseButton_Right;\n    map.Select          = ImGuiMouseButton_Left;\n    map.SelectMod       = ImGuiMod_None;\n    map.SelectCancel    = ImGuiMouseButton_Right;\n    map.SelectHorzMod   = ImGuiMod_Alt;\n    map.SelectVertMod   = ImGuiMod_Shift;\n    map.OverrideMod     = ImGuiMod_Ctrl;\n    map.ZoomMod         = ImGuiMod_None;\n    map.ZoomRate        = 0.1f;\n}\n\n//-----------------------------------------------------------------------------\n// [Section] Miscellaneous\n//-----------------------------------------------------------------------------\n\nvoid ItemIcon(const ImVec4& col) {\n    ItemIcon(ImGui::ColorConvertFloat4ToU32(col));\n}\n\nvoid ItemIcon(ImU32 col) {\n    const float txt_size = ImGui::GetTextLineHeight();\n    ImVec2 size(txt_size-4,txt_size);\n    ImGuiWindow* window = ImGui::GetCurrentWindow();\n    ImVec2 pos = window->DC.CursorPos;\n    ImGui::GetWindowDrawList()->AddRectFilled(pos + ImVec2(0,2), pos + size - ImVec2(0,2), col);\n    ImGui::Dummy(size);\n}\n\nvoid ColormapIcon(ImPlotColormap cmap) {\n    ImPlotContext& gp = *GImPlot;\n    const float txt_size = ImGui::GetTextLineHeight();\n    ImVec2 size(txt_size-4,txt_size);\n    ImGuiWindow* window = ImGui::GetCurrentWindow();\n    ImVec2 pos = window->DC.CursorPos;\n    ImRect rect(pos+ImVec2(0,2),pos+size-ImVec2(0,2));\n    ImDrawList& DrawList = *ImGui::GetWindowDrawList();\n    RenderColorBar(gp.ColormapData.GetKeys(cmap),gp.ColormapData.GetKeyCount(cmap),DrawList,rect,false,false,!gp.ColormapData.IsQual(cmap));\n    ImGui::Dummy(size);\n}\n\nImDrawList* GetPlotDrawList() {\n    return ImGui::GetWindowDrawList();\n}\n\nvoid PushPlotClipRect(float expand) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    ImRect rect = gp.CurrentPlot->PlotRect;\n    rect.Expand(expand);\n    ImGui::PushClipRect(rect.Min, rect.Max, true);\n}\n\nvoid PopPlotClipRect() {\n    SetupLock();\n    ImGui::PopClipRect();\n}\n\nstatic void HelpMarker(const char* desc) {\n    ImGui::TextDisabled(\"(?)\");\n    if (ImGui::IsItemHovered()) {\n        ImGui::BeginTooltip();\n        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);\n        ImGui::TextUnformatted(desc);\n        ImGui::PopTextWrapPos();\n        ImGui::EndTooltip();\n    }\n}\n\nbool ShowStyleSelector(const char* label)\n{\n    static int style_idx = -1;\n    if (ImGui::Combo(label, &style_idx, \"Auto\\0Classic\\0Dark\\0Light\\0\"))\n    {\n        switch (style_idx)\n        {\n        case 0: StyleColorsAuto(); break;\n        case 1: StyleColorsClassic(); break;\n        case 2: StyleColorsDark(); break;\n        case 3: StyleColorsLight(); break;\n        }\n        return true;\n    }\n    return false;\n}\n\nbool ShowColormapSelector(const char* label) {\n    ImPlotContext& gp = *GImPlot;\n    bool set = false;\n    if (ImGui::BeginCombo(label, gp.ColormapData.GetName(gp.Style.Colormap))) {\n        for (int i = 0; i < gp.ColormapData.Count; ++i) {\n            const char* name = gp.ColormapData.GetName(i);\n            if (ImGui::Selectable(name, gp.Style.Colormap == i)) {\n                gp.Style.Colormap = i;\n                ImPlot::BustItemCache();\n                set = true;\n            }\n        }\n        ImGui::EndCombo();\n    }\n    return set;\n}\n\nbool ShowInputMapSelector(const char* label) {\n    static int map_idx = -1;\n    if (ImGui::Combo(label, &map_idx, \"Default\\0Reversed\\0\"))\n    {\n        switch (map_idx)\n        {\n        case 0: MapInputDefault(); break;\n        case 1: MapInputReverse(); break;\n        }\n        return true;\n    }\n    return false;\n}\n\n\nvoid ShowStyleEditor(ImPlotStyle* ref) {\n    ImPlotContext& gp = *GImPlot;\n    ImPlotStyle& style = GetStyle();\n    static ImPlotStyle ref_saved_style;\n    // Default to using internal storage as reference\n    static bool init = true;\n    if (init && ref == nullptr)\n        ref_saved_style = style;\n    init = false;\n    if (ref == nullptr)\n        ref = &ref_saved_style;\n\n    if (ImPlot::ShowStyleSelector(\"Colors##Selector\"))\n        ref_saved_style = style;\n\n    // Save/Revert button\n    if (ImGui::Button(\"Save Ref\"))\n        *ref = ref_saved_style = style;\n    ImGui::SameLine();\n    if (ImGui::Button(\"Revert Ref\"))\n        style = *ref;\n    ImGui::SameLine();\n    HelpMarker(\"Save/Revert in local non-persistent storage. Default Colors definition are not affected. \"\n               \"Use \\\"Export\\\" below to save them somewhere.\");\n    if (ImGui::BeginTabBar(\"##StyleEditor\")) {\n        if (ImGui::BeginTabItem(\"Variables\")) {\n            ImGui::Text(\"Item Styling\");\n            ImGui::SliderFloat(\"LineWeight\", &style.LineWeight, 0.0f, 5.0f, \"%.1f\");\n            ImGui::SliderFloat(\"MarkerSize\", &style.MarkerSize, 2.0f, 10.0f, \"%.1f\");\n            ImGui::SliderFloat(\"MarkerWeight\", &style.MarkerWeight, 0.0f, 5.0f, \"%.1f\");\n            ImGui::SliderFloat(\"FillAlpha\", &style.FillAlpha, 0.0f, 1.0f, \"%.2f\");\n            ImGui::SliderFloat(\"ErrorBarSize\", &style.ErrorBarSize, 0.0f, 10.0f, \"%.1f\");\n            ImGui::SliderFloat(\"ErrorBarWeight\", &style.ErrorBarWeight, 0.0f, 5.0f, \"%.1f\");\n            ImGui::SliderFloat(\"DigitalBitHeight\", &style.DigitalBitHeight, 0.0f, 20.0f, \"%.1f\");\n            ImGui::SliderFloat(\"DigitalBitGap\", &style.DigitalBitGap, 0.0f, 20.0f, \"%.1f\");\n            ImGui::Text(\"Plot Styling\");\n            ImGui::SliderFloat(\"PlotBorderSize\", &style.PlotBorderSize, 0.0f, 2.0f, \"%.0f\");\n            ImGui::SliderFloat(\"MinorAlpha\", &style.MinorAlpha, 0.0f, 1.0f, \"%.2f\");\n            ImGui::SliderFloat2(\"MajorTickLen\", (float*)&style.MajorTickLen, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"MinorTickLen\", (float*)&style.MinorTickLen, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"MajorTickSize\",  (float*)&style.MajorTickSize, 0.0f, 2.0f, \"%.1f\");\n            ImGui::SliderFloat2(\"MinorTickSize\", (float*)&style.MinorTickSize, 0.0f, 2.0f, \"%.1f\");\n            ImGui::SliderFloat2(\"MajorGridSize\", (float*)&style.MajorGridSize, 0.0f, 2.0f, \"%.1f\");\n            ImGui::SliderFloat2(\"MinorGridSize\", (float*)&style.MinorGridSize, 0.0f, 2.0f, \"%.1f\");\n            ImGui::SliderFloat2(\"PlotDefaultSize\", (float*)&style.PlotDefaultSize, 0.0f, 1000, \"%.0f\");\n            ImGui::SliderFloat2(\"PlotMinSize\", (float*)&style.PlotMinSize, 0.0f, 300, \"%.0f\");\n            ImGui::Text(\"Plot Padding\");\n            ImGui::SliderFloat2(\"PlotPadding\", (float*)&style.PlotPadding, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"LabelPadding\", (float*)&style.LabelPadding, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"LegendPadding\", (float*)&style.LegendPadding, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"LegendInnerPadding\", (float*)&style.LegendInnerPadding, 0.0f, 10.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"LegendSpacing\", (float*)&style.LegendSpacing, 0.0f, 5.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"MousePosPadding\", (float*)&style.MousePosPadding, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"AnnotationPadding\", (float*)&style.AnnotationPadding, 0.0f, 5.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"FitPadding\", (float*)&style.FitPadding, 0, 0.2f, \"%.2f\");\n\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"Colors\")) {\n            static int output_dest = 0;\n            static bool output_only_modified = false;\n\n            if (ImGui::Button(\"Export\", ImVec2(75,0))) {\n                if (output_dest == 0)\n                    ImGui::LogToClipboard();\n                else\n                    ImGui::LogToTTY();\n                ImGui::LogText(\"ImVec4* colors = ImPlot::GetStyle().Colors;\\n\");\n                for (int i = 0; i < ImPlotCol_COUNT; i++) {\n                    const ImVec4& col = style.Colors[i];\n                    const char* name = ImPlot::GetStyleColorName(i);\n                    if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) {\n                        if (IsColorAuto(i))\n                            ImGui::LogText(\"colors[ImPlotCol_%s]%*s= IMPLOT_AUTO_COL;\\n\",name,14 - (int)strlen(name), \"\");\n                        else\n                            ImGui::LogText(\"colors[ImPlotCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\\n\",\n                                        name, 14 - (int)strlen(name), \"\", col.x, col.y, col.z, col.w);\n                    }\n                }\n                ImGui::LogFinish();\n            }\n            ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo(\"##output_type\", &output_dest, \"To Clipboard\\0To TTY\\0\");\n            ImGui::SameLine(); ImGui::Checkbox(\"Only Modified Colors\", &output_only_modified);\n\n            static ImGuiTextFilter filter;\n            filter.Draw(\"Filter colors\", ImGui::GetFontSize() * 16);\n\n            static ImGuiColorEditFlags alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf;\n#if IMGUI_VERSION_NUM < 19173\n            if (ImGui::RadioButton(\"Opaque\", alpha_flags == ImGuiColorEditFlags_None))             { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine();\n            if (ImGui::RadioButton(\"Alpha\",  alpha_flags == ImGuiColorEditFlags_AlphaPreview))     { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine();\n            if (ImGui::RadioButton(\"Both\",   alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine();\n#else\n            if (ImGui::RadioButton(\"Opaque\", alpha_flags == ImGuiColorEditFlags_AlphaOpaque))      { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } ImGui::SameLine();\n            if (ImGui::RadioButton(\"Alpha\",  alpha_flags == ImGuiColorEditFlags_None))             { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine();\n            if (ImGui::RadioButton(\"Both\",   alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine();\n#endif\n            HelpMarker(\n                \"In the color list:\\n\"\n                \"Left-click on colored square to open color picker,\\n\"\n                \"Right-click to open edit options menu.\");\n            ImGui::Separator();\n            ImGui::PushItemWidth(-160);\n            for (int i = 0; i < ImPlotCol_COUNT; i++) {\n                const char* name = ImPlot::GetStyleColorName(i);\n                if (!filter.PassFilter(name))\n                    continue;\n                ImGui::PushID(i);\n                ImVec4 temp = GetStyleColorVec4(i);\n                const bool is_auto = IsColorAuto(i);\n                if (!is_auto)\n                    ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f);\n                if (ImGui::Button(\"Auto\")) {\n                    if (is_auto)\n                        style.Colors[i] = temp;\n                    else\n                        style.Colors[i] = IMPLOT_AUTO_COL;\n                    BustItemCache();\n                }\n                if (!is_auto)\n                    ImGui::PopStyleVar();\n                ImGui::SameLine();\n                if (ImGui::ColorEdit4(name, &temp.x, ImGuiColorEditFlags_NoInputs | alpha_flags)) {\n                    style.Colors[i] = temp;\n                    BustItemCache();\n                }\n                if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) {\n                    ImGui::SameLine(175); if (ImGui::Button(\"Save\")) { ref->Colors[i] = style.Colors[i]; }\n                    ImGui::SameLine(); if (ImGui::Button(\"Revert\")) {\n                        style.Colors[i] = ref->Colors[i];\n                        BustItemCache();\n                    }\n                }\n                ImGui::PopID();\n            }\n            ImGui::PopItemWidth();\n            ImGui::Separator();\n            ImGui::Text(\"Colors that are set to Auto (i.e. IMPLOT_AUTO_COL) will\\n\"\n                        \"be automatically deduced from your ImGui style or the\\n\"\n                        \"current ImPlot Colormap. If you want to style individual\\n\"\n                        \"plot items, use Push/PopStyleColor around its function.\");\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"Colormaps\")) {\n            static int output_dest = 0;\n            if (ImGui::Button(\"Export\", ImVec2(75,0))) {\n                if (output_dest == 0)\n                    ImGui::LogToClipboard();\n                else\n                    ImGui::LogToTTY();\n                int size = GetColormapSize();\n                const char* name = GetColormapName(gp.Style.Colormap);\n                ImGui::LogText(\"static const ImU32 %s_Data[%d] = {\\n\", name, size);\n                for (int i = 0; i < size; ++i) {\n                    ImU32 col = GetColormapColorU32(i,gp.Style.Colormap);\n                    ImGui::LogText(\"    %u%s\\n\", col, i == size - 1 ? \"\" : \",\");\n                }\n                ImGui::LogText(\"};\\nImPlotColormap %s = ImPlot::AddColormap(\\\"%s\\\", %s_Data, %d);\", name, name, name, size);\n                ImGui::LogFinish();\n            }\n            ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo(\"##output_type\", &output_dest, \"To Clipboard\\0To TTY\\0\");\n            ImGui::SameLine();\n            static bool edit = false;\n            ImGui::Checkbox(\"Edit Mode\",&edit);\n\n            // built-in/added\n            ImGui::Separator();\n            for (int i = 0; i < gp.ColormapData.Count; ++i) {\n                ImGui::PushID(i);\n                int size = gp.ColormapData.GetKeyCount(i);\n                bool selected = i == gp.Style.Colormap;\n\n                const char* name = GetColormapName(i);\n                if (!selected)\n                    ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f);\n                if (ImGui::Button(name, ImVec2(100,0))) {\n                    gp.Style.Colormap = i;\n                    BustItemCache();\n                }\n                if (!selected)\n                    ImGui::PopStyleVar();\n                ImGui::SameLine();\n                ImGui::BeginGroup();\n                if (edit) {\n                    for (int c = 0; c < size; ++c) {\n                        ImGui::PushID(c);\n                        ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(i,c));\n                        if (ImGui::ColorEdit4(\"\",&col4.x,ImGuiColorEditFlags_NoInputs)) {\n                            ImU32 col32 = ImGui::ColorConvertFloat4ToU32(col4);\n                            gp.ColormapData.SetKeyColor(i,c,col32);\n                            BustItemCache();\n                        }\n                        if ((c + 1) % 12 != 0 && c != size -1)\n                            ImGui::SameLine();\n                        ImGui::PopID();\n                    }\n                }\n                else {\n                    if (ImPlot::ColormapButton(\"##\",ImVec2(-1,0),i))\n                        edit = true;\n                }\n                ImGui::EndGroup();\n                ImGui::PopID();\n            }\n\n\n            static ImVector<ImVec4> custom;\n            if (custom.Size == 0) {\n                custom.push_back(ImVec4(1,0,0,1));\n                custom.push_back(ImVec4(0,1,0,1));\n                custom.push_back(ImVec4(0,0,1,1));\n            }\n            ImGui::Separator();\n            ImGui::BeginGroup();\n            static char name[16] = \"MyColormap\";\n\n\n            if (ImGui::Button(\"+\", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0)))\n                custom.push_back(ImVec4(0,0,0,1));\n            ImGui::SameLine();\n            if (ImGui::Button(\"-\", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 2)\n                custom.pop_back();\n            ImGui::SetNextItemWidth(100);\n            ImGui::InputText(\"##Name\",name,16,ImGuiInputTextFlags_CharsNoBlank);\n            static bool qual = true;\n            ImGui::Checkbox(\"Qualitative\",&qual);\n            if (ImGui::Button(\"Add\", ImVec2(100, 0)) && gp.ColormapData.GetIndex(name)==-1)\n                AddColormap(name,custom.Data,custom.Size,qual);\n\n            ImGui::EndGroup();\n            ImGui::SameLine();\n            ImGui::BeginGroup();\n            for (int c = 0; c < custom.Size; ++c) {\n                ImGui::PushID(c);\n                if (ImGui::ColorEdit4(\"##Col1\", &custom[c].x, ImGuiColorEditFlags_NoInputs)) {\n\n                }\n                if ((c + 1) % 12 != 0)\n                    ImGui::SameLine();\n                ImGui::PopID();\n            }\n            ImGui::EndGroup();\n\n\n            ImGui::EndTabItem();\n        }\n        ImGui::EndTabBar();\n    }\n}\n\nvoid ShowUserGuide() {\n        ImGui::BulletText(\"Left-click drag within the plot area to pan X and Y axes.\");\n    ImGui::Indent();\n        ImGui::BulletText(\"Left-click drag on axis labels to pan an individual axis.\");\n    ImGui::Unindent();\n    ImGui::BulletText(\"Scroll in the plot area to zoom both X any Y axes.\");\n    ImGui::Indent();\n        ImGui::BulletText(\"Scroll on axis labels to zoom an individual axis.\");\n    ImGui::Unindent();\n    ImGui::BulletText(\"Right-click drag to box select data.\");\n    ImGui::Indent();\n        ImGui::BulletText(\"Hold Alt to expand box selection horizontally.\");\n        ImGui::BulletText(\"Hold Shift to expand box selection vertically.\");\n        ImGui::BulletText(\"Left-click while box selecting to cancel the selection.\");\n    ImGui::Unindent();\n    ImGui::BulletText(\"Double left-click to fit all visible data.\");\n    ImGui::Indent();\n        ImGui::BulletText(\"Double left-click axis labels to fit the individual axis.\");\n    ImGui::Unindent();\n    ImGui::BulletText(\"Right-click open the full plot context menu.\");\n    ImGui::Indent();\n        ImGui::BulletText(\"Right-click axis labels to open an individual axis context menu.\");\n    ImGui::Unindent();\n    ImGui::BulletText(\"Click legend label icons to show/hide plot items.\");\n}\n\nvoid ShowTicksMetrics(const ImPlotTicker& ticker) {\n    ImGui::BulletText(\"Size: %d\", ticker.TickCount());\n    ImGui::BulletText(\"MaxSize: [%f,%f]\", ticker.MaxSize.x, ticker.MaxSize.y);\n}\n\nvoid ShowAxisMetrics(const ImPlotPlot& plot, const ImPlotAxis& axis) {\n    ImGui::BulletText(\"Label: %s\", axis.LabelOffset == -1 ? \"[none]\" : plot.GetAxisLabel(axis));\n    ImGui::BulletText(\"Flags: 0x%08X\", axis.Flags);\n    ImGui::BulletText(\"Range: [%f,%f]\",axis.Range.Min, axis.Range.Max);\n    ImGui::BulletText(\"Pixels: %f\", axis.PixelSize());\n    ImGui::BulletText(\"Aspect: %f\", axis.GetAspect());\n    ImGui::BulletText(axis.OrthoAxis == nullptr ? \"OrtherAxis: NULL\" : \"OrthoAxis: 0x%08X\", axis.OrthoAxis->ID);\n    ImGui::BulletText(\"LinkedMin: %p\", (void*)axis.LinkedMin);\n    ImGui::BulletText(\"LinkedMax: %p\", (void*)axis.LinkedMax);\n    ImGui::BulletText(\"HasRange: %s\", axis.HasRange ? \"true\" : \"false\");\n    ImGui::BulletText(\"Hovered: %s\", axis.Hovered ? \"true\" : \"false\");\n    ImGui::BulletText(\"Held: %s\", axis.Held ? \"true\" : \"false\");\n\n    if (ImGui::TreeNode(\"Transform\")) {\n        ImGui::BulletText(\"PixelMin: %f\", axis.PixelMin);\n        ImGui::BulletText(\"PixelMax: %f\", axis.PixelMax);\n        ImGui::BulletText(\"ScaleToPixel: %f\", axis.ScaleToPixel);\n        ImGui::BulletText(\"ScaleMax: %f\", axis.ScaleMax);\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Ticks\")) {\n        ShowTicksMetrics(axis.Ticker);\n        ImGui::TreePop();\n    }\n}\n\nvoid ShowMetricsWindow(bool* p_popen) {\n\n    static bool show_plot_rects = false;\n    static bool show_axes_rects = false;\n    static bool show_axis_rects = false;\n    static bool show_canvas_rects = false;\n    static bool show_frame_rects = false;\n    static bool show_subplot_frame_rects = false;\n    static bool show_subplot_grid_rects = false;\n    static bool show_legend_rects = false;\n\n    ImDrawList& fg = *ImGui::GetForegroundDrawList();\n\n    ImPlotContext& gp = *GImPlot;\n    // ImGuiContext& g = *GImGui;\n    ImGuiIO& io = ImGui::GetIO();\n    ImGui::Begin(\"ImPlot Metrics\", p_popen);\n    ImGui::Text(\"ImPlot \" IMPLOT_VERSION);\n    ImGui::Text(\"Application average %.3f ms/frame (%.1f FPS)\", 1000.0f / io.Framerate, io.Framerate);\n    ImGui::Text(\"Mouse Position: [%.0f,%.0f]\", io.MousePos.x, io.MousePos.y);\n    ImGui::Separator();\n    if (ImGui::TreeNode(\"Tools\")) {\n        if (ImGui::Button(\"Bust Plot Cache\"))\n            BustPlotCache();\n        ImGui::SameLine();\n        if (ImGui::Button(\"Bust Item Cache\"))\n            BustItemCache();\n        ImGui::Checkbox(\"Show Frame Rects\", &show_frame_rects);\n        ImGui::Checkbox(\"Show Canvas Rects\",&show_canvas_rects);\n        ImGui::Checkbox(\"Show Plot Rects\",  &show_plot_rects);\n        ImGui::Checkbox(\"Show Axes Rects\",  &show_axes_rects);\n        ImGui::Checkbox(\"Show Axis Rects\",  &show_axis_rects);\n        ImGui::Checkbox(\"Show Subplot Frame Rects\",  &show_subplot_frame_rects);\n        ImGui::Checkbox(\"Show Subplot Grid Rects\",  &show_subplot_grid_rects);\n        ImGui::Checkbox(\"Show Legend Rects\",  &show_legend_rects);\n        ImGui::TreePop();\n    }\n    const int n_plots = gp.Plots.GetBufSize();\n    const int n_subplots = gp.Subplots.GetBufSize();\n    // render rects\n    for (int p = 0; p < n_plots; ++p) {\n        ImPlotPlot* plot = gp.Plots.GetByIndex(p);\n        if (show_frame_rects)\n            fg.AddRect(plot->FrameRect.Min, plot->FrameRect.Max, IM_COL32(255,0,255,255));\n        if (show_canvas_rects)\n            fg.AddRect(plot->CanvasRect.Min, plot->CanvasRect.Max, IM_COL32(0,255,255,255));\n        if (show_plot_rects)\n            fg.AddRect(plot->PlotRect.Min, plot->PlotRect.Max, IM_COL32(255,255,0,255));\n        if (show_axes_rects)\n            fg.AddRect(plot->AxesRect.Min, plot->AxesRect.Max, IM_COL32(0,255,128,255));\n        if (show_axis_rects) {\n            for (int i = 0; i < ImAxis_COUNT; ++i) {\n                if (plot->Axes[i].Enabled)\n                    fg.AddRect(plot->Axes[i].HoverRect.Min, plot->Axes[i].HoverRect.Max, IM_COL32(0,255,0,255));\n            }\n        }\n        if (show_legend_rects && plot->Items.GetLegendCount() > 0) {\n            fg.AddRect(plot->Items.Legend.Rect.Min, plot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255));\n            fg.AddRect(plot->Items.Legend.RectClamped.Min, plot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255));\n        }\n    }\n    for (int p = 0; p < n_subplots; ++p) {\n        ImPlotSubplot* subplot = gp.Subplots.GetByIndex(p);\n        if (show_subplot_frame_rects)\n            fg.AddRect(subplot->FrameRect.Min, subplot->FrameRect.Max, IM_COL32(255,0,0,255));\n        if (show_subplot_grid_rects)\n            fg.AddRect(subplot->GridRect.Min, subplot->GridRect.Max, IM_COL32(0,0,255,255));\n        if (show_legend_rects && subplot->Items.GetLegendCount() > 0) {\n            fg.AddRect(subplot->Items.Legend.Rect.Min, subplot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255));\n            fg.AddRect(subplot->Items.Legend.RectClamped.Min, subplot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255));\n        }\n    }\n    if (ImGui::TreeNode(\"Plots\",\"Plots (%d)\", n_plots)) {\n        for (int p = 0; p < n_plots; ++p) {\n            // plot\n            ImPlotPlot& plot = *gp.Plots.GetByIndex(p);\n            ImGui::PushID(p);\n            if (ImGui::TreeNode(\"Plot\", \"Plot [0x%08X]\", plot.ID)) {\n                int n_items = plot.Items.GetItemCount();\n                if (ImGui::TreeNode(\"Items\", \"Items (%d)\", n_items)) {\n                    for (int i = 0; i < n_items; ++i) {\n                        ImPlotItem* item = plot.Items.GetItemByIndex(i);\n                        ImGui::PushID(i);\n                        if (ImGui::TreeNode(\"Item\", \"Item [0x%08X]\", item->ID)) {\n                            ImGui::Bullet(); ImGui::Checkbox(\"Show\", &item->Show);\n                            ImGui::Bullet();\n                            ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color);\n                            if (ImGui::ColorEdit4(\"Color\",&temp.x, ImGuiColorEditFlags_NoInputs))\n                                item->Color = ImGui::ColorConvertFloat4ToU32(temp);\n\n                            ImGui::BulletText(\"NameOffset: %d\",item->NameOffset);\n                            ImGui::BulletText(\"Name: %s\", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : \"N/A\");\n                            ImGui::BulletText(\"Hovered: %s\",item->LegendHovered ? \"true\" : \"false\");\n                            ImGui::TreePop();\n                        }\n                        ImGui::PopID();\n                    }\n                    ImGui::TreePop();\n                }\n                char buff[16];\n                for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) {\n                    ImFormatString(buff,16,\"X-Axis %d\", i+1);\n                    if (plot.XAxis(i).Enabled && ImGui::TreeNode(buff, \"X-Axis %d [0x%08X]\", i+1, plot.XAxis(i).ID)) {\n                        ShowAxisMetrics(plot, plot.XAxis(i));\n                        ImGui::TreePop();\n                    }\n                }\n                for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) {\n                    ImFormatString(buff,16,\"Y-Axis %d\", i+1);\n                    if (plot.YAxis(i).Enabled && ImGui::TreeNode(buff, \"Y-Axis %d [0x%08X]\", i+1, plot.YAxis(i).ID)) {\n                        ShowAxisMetrics(plot, plot.YAxis(i));\n                        ImGui::TreePop();\n                    }\n                }\n                ImGui::BulletText(\"Title: %s\", plot.HasTitle() ? plot.GetTitle() : \"none\");\n                ImGui::BulletText(\"Flags: 0x%08X\", plot.Flags);\n                ImGui::BulletText(\"Initialized: %s\", plot.Initialized ? \"true\" : \"false\");\n                ImGui::BulletText(\"Selecting: %s\", plot.Selecting ? \"true\" : \"false\");\n                ImGui::BulletText(\"Selected: %s\", plot.Selected ? \"true\" : \"false\");\n                ImGui::BulletText(\"Hovered: %s\", plot.Hovered ? \"true\" : \"false\");\n                ImGui::BulletText(\"Held: %s\", plot.Held ? \"true\" : \"false\");\n                ImGui::BulletText(\"LegendHovered: %s\", plot.Items.Legend.Hovered ? \"true\" : \"false\");\n                ImGui::BulletText(\"ContextLocked: %s\", plot.ContextLocked ? \"true\" : \"false\");\n                ImGui::TreePop();\n            }\n            ImGui::PopID();\n        }\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Subplots\",\"Subplots (%d)\", n_subplots)) {\n        for (int p = 0; p < n_subplots; ++p) {\n            // plot\n            ImPlotSubplot& plot = *gp.Subplots.GetByIndex(p);\n            ImGui::PushID(p);\n            if (ImGui::TreeNode(\"Subplot\", \"Subplot [0x%08X]\", plot.ID)) {\n                int n_items = plot.Items.GetItemCount();\n                if (ImGui::TreeNode(\"Items\", \"Items (%d)\", n_items)) {\n                    for (int i = 0; i < n_items; ++i) {\n                        ImPlotItem* item = plot.Items.GetItemByIndex(i);\n                        ImGui::PushID(i);\n                        if (ImGui::TreeNode(\"Item\", \"Item [0x%08X]\", item->ID)) {\n                            ImGui::Bullet(); ImGui::Checkbox(\"Show\", &item->Show);\n                            ImGui::Bullet();\n                            ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color);\n                            if (ImGui::ColorEdit4(\"Color\",&temp.x, ImGuiColorEditFlags_NoInputs))\n                                item->Color = ImGui::ColorConvertFloat4ToU32(temp);\n\n                            ImGui::BulletText(\"NameOffset: %d\",item->NameOffset);\n                            ImGui::BulletText(\"Name: %s\", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : \"N/A\");\n                            ImGui::BulletText(\"Hovered: %s\",item->LegendHovered ? \"true\" : \"false\");\n                            ImGui::TreePop();\n                        }\n                        ImGui::PopID();\n                    }\n                    ImGui::TreePop();\n                }\n                ImGui::BulletText(\"Flags: 0x%08X\", plot.Flags);\n                ImGui::BulletText(\"FrameHovered: %s\", plot.FrameHovered ? \"true\" : \"false\");\n                ImGui::BulletText(\"LegendHovered: %s\", plot.Items.Legend.Hovered ? \"true\" : \"false\");\n                ImGui::TreePop();\n            }\n            ImGui::PopID();\n        }\n        ImGui::TreePop();\n    }\n    if (ImGui::TreeNode(\"Colormaps\")) {\n        ImGui::BulletText(\"Colormaps:  %d\", gp.ColormapData.Count);\n        ImGui::BulletText(\"Memory: %d bytes\", gp.ColormapData.Tables.Size * 4);\n        if (ImGui::TreeNode(\"Data\")) {\n            for (int m = 0; m < gp.ColormapData.Count; ++m) {\n                if (ImGui::TreeNode(gp.ColormapData.GetName(m))) {\n                    int count = gp.ColormapData.GetKeyCount(m);\n                    int size = gp.ColormapData.GetTableSize(m);\n                    bool qual = gp.ColormapData.IsQual(m);\n                    ImGui::BulletText(\"Qualitative: %s\", qual ? \"true\" : \"false\");\n                    ImGui::BulletText(\"Key Count: %d\", count);\n                    ImGui::BulletText(\"Table Size: %d\", size);\n                    ImGui::Indent();\n\n                    static float t = 0.5;\n                    ImVec4 samp;\n                    float wid = 32 * 10 - ImGui::GetFrameHeight() - ImGui::GetStyle().ItemSpacing.x;\n                    ImGui::SetNextItemWidth(wid);\n                    ImPlot::ColormapSlider(\"##Sample\",&t,&samp,\"%.3f\",m);\n                    ImGui::SameLine();\n                    ImGui::ColorButton(\"Sampler\",samp);\n                    ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0));\n                    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0));\n                    for (int c = 0; c < size; ++c) {\n                        ImVec4 col = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetTableColor(m,c));\n                        ImGui::PushID(m*1000+c);\n                        ImGui::ColorButton(\"\",col,0,ImVec2(10,10));\n                        ImGui::PopID();\n                        if ((c + 1) % 32 != 0 && c != size - 1)\n                            ImGui::SameLine();\n                    }\n                    ImGui::PopStyleVar();\n                    ImGui::PopStyleColor();\n                    ImGui::Unindent();\n                    ImGui::TreePop();\n                }\n            }\n            ImGui::TreePop();\n        }\n        ImGui::TreePop();\n    }\n    ImGui::End();\n}\n\nbool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1, const ImPlotTime* t2) {\n\n    ImGui::PushID(id);\n    ImGui::BeginGroup();\n\n    ImGuiStyle& style = ImGui::GetStyle();\n    ImVec4 col_txt    = style.Colors[ImGuiCol_Text];\n    ImVec4 col_dis    = style.Colors[ImGuiCol_TextDisabled];\n    ImVec4 col_btn    = style.Colors[ImGuiCol_Button];\n    ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));\n    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0));\n\n    const float ht    = ImGui::GetFrameHeight();\n    ImVec2 cell_size(ht*1.25f,ht);\n    char buff[32];\n    bool clk = false;\n    tm& Tm = GImPlot->Tm;\n\n    const int min_yr = 1970;\n    const int max_yr = 2999;\n\n    // t1 parts\n    int t1_mo = 0; int t1_md = 0; int t1_yr = 0;\n    if (t1 != nullptr) {\n        GetTime(*t1,&Tm);\n        t1_mo = Tm.tm_mon;\n        t1_md = Tm.tm_mday;\n        t1_yr = Tm.tm_year + 1900;\n    }\n\n     // t2 parts\n    int t2_mo = 0; int t2_md = 0; int t2_yr = 0;\n    if (t2 != nullptr) {\n        GetTime(*t2,&Tm);\n        t2_mo = Tm.tm_mon;\n        t2_md = Tm.tm_mday;\n        t2_yr = Tm.tm_year + 1900;\n    }\n\n    // day widget\n    if (*level == 0) {\n        *t = FloorTime(*t, ImPlotTimeUnit_Day);\n        GetTime(*t, &Tm);\n        const int this_year = Tm.tm_year + 1900;\n        const int last_year = this_year - 1;\n        const int next_year = this_year + 1;\n        const int this_mon  = Tm.tm_mon;\n        const int last_mon  = this_mon == 0 ? 11 : this_mon - 1;\n        const int next_mon  = this_mon == 11 ? 0 : this_mon + 1;\n        const int days_this_mo = GetDaysInMonth(this_year, this_mon);\n        const int days_last_mo = GetDaysInMonth(this_mon == 0 ? last_year : this_year, last_mon);\n        ImPlotTime t_first_mo = FloorTime(*t,ImPlotTimeUnit_Mo);\n        GetTime(t_first_mo,&Tm);\n        const int first_wd = Tm.tm_wday;\n        // month year\n        ImFormatString(buff, 32, \"%s %d\", MONTH_NAMES[this_mon], this_year);\n        if (ImGui::Button(buff))\n            *level = 1;\n        ImGui::SameLine(5*cell_size.x);\n        BeginDisabledControls(this_year <= min_yr && this_mon == 0);\n        if (ImGui::ArrowButtonEx(\"##Up\",ImGuiDir_Up,cell_size))\n            *t = AddTime(*t, ImPlotTimeUnit_Mo, -1);\n        EndDisabledControls(this_year <= min_yr && this_mon == 0);\n        ImGui::SameLine();\n        BeginDisabledControls(this_year >= max_yr && this_mon == 11);\n        if (ImGui::ArrowButtonEx(\"##Down\",ImGuiDir_Down,cell_size))\n            *t = AddTime(*t, ImPlotTimeUnit_Mo, 1);\n        EndDisabledControls(this_year >= max_yr && this_mon == 11);\n        // render weekday abbreviations\n        ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);\n        for (int i = 0; i < 7; ++i) {\n            ImGui::Button(WD_ABRVS[i],cell_size);\n            if (i != 6) { ImGui::SameLine(); }\n        }\n        ImGui::PopItemFlag();\n        // 0 = last mo, 1 = this mo, 2 = next mo\n        int mo = first_wd > 0 ? 0 : 1;\n        int day = mo == 1 ? 1 : days_last_mo - first_wd + 1;\n        for (int i = 0; i < 6; ++i) {\n            for (int j = 0; j < 7; ++j) {\n                if (mo == 0 && day > days_last_mo) {\n                    mo = 1;\n                    day = 1;\n                }\n                else if (mo == 1 && day > days_this_mo) {\n                    mo = 2;\n                    day = 1;\n                }\n                const int now_yr = (mo == 0 && this_mon == 0) ? last_year : ((mo == 2 && this_mon == 11) ? next_year : this_year);\n                const int now_mo = mo == 0 ? last_mon : (mo == 1 ? this_mon : next_mon);\n                const int now_md = day;\n\n                const bool off_mo   = mo == 0 || mo == 2;\n                const bool t1_or_t2 = (t1 != nullptr && t1_mo == now_mo && t1_yr == now_yr && t1_md == now_md) ||\n                                      (t2 != nullptr && t2_mo == now_mo && t2_yr == now_yr && t2_md == now_md);\n\n                if (off_mo)\n                    ImGui::PushStyleColor(ImGuiCol_Text, col_dis);\n                if (t1_or_t2) {\n                    ImGui::PushStyleColor(ImGuiCol_Button, col_btn);\n                    ImGui::PushStyleColor(ImGuiCol_Text, col_txt);\n                }\n                ImGui::PushID(i*7+j);\n                ImFormatString(buff,32,\"%d\",day);\n                if (now_yr == min_yr-1 || now_yr == max_yr+1) {\n                    ImGui::Dummy(cell_size);\n                }\n                else if (ImGui::Button(buff,cell_size) && !clk) {\n                    *t = MakeTime(now_yr, now_mo, now_md);\n                    clk = true;\n                }\n                ImGui::PopID();\n                if (t1_or_t2)\n                    ImGui::PopStyleColor(2);\n                if (off_mo)\n                    ImGui::PopStyleColor();\n                if (j != 6)\n                    ImGui::SameLine();\n                day++;\n            }\n        }\n    }\n    // month widget\n    else if (*level == 1) {\n        *t = FloorTime(*t, ImPlotTimeUnit_Mo);\n        GetTime(*t, &Tm);\n        int this_yr  = Tm.tm_year + 1900;\n        ImFormatString(buff, 32, \"%d\", this_yr);\n        if (ImGui::Button(buff))\n            *level = 2;\n        BeginDisabledControls(this_yr <= min_yr);\n        ImGui::SameLine(5*cell_size.x);\n        if (ImGui::ArrowButtonEx(\"##Up\",ImGuiDir_Up,cell_size))\n            *t = AddTime(*t, ImPlotTimeUnit_Yr, -1);\n        EndDisabledControls(this_yr <= min_yr);\n        ImGui::SameLine();\n        BeginDisabledControls(this_yr >= max_yr);\n        if (ImGui::ArrowButtonEx(\"##Down\",ImGuiDir_Down,cell_size))\n            *t = AddTime(*t, ImPlotTimeUnit_Yr, 1);\n        EndDisabledControls(this_yr >= max_yr);\n        // ImGui::Dummy(cell_size);\n        cell_size.x *= 7.0f/4.0f;\n        cell_size.y *= 7.0f/3.0f;\n        int mo = 0;\n        for (int i = 0; i < 3; ++i) {\n            for (int j = 0; j < 4; ++j) {\n                const bool t1_or_t2 = (t1 != nullptr && t1_yr == this_yr && t1_mo == mo) ||\n                                      (t2 != nullptr && t2_yr == this_yr && t2_mo == mo);\n                if (t1_or_t2)\n                    ImGui::PushStyleColor(ImGuiCol_Button, col_btn);\n                if (ImGui::Button(MONTH_ABRVS[mo],cell_size) && !clk) {\n                    *t = MakeTime(this_yr, mo);\n                    *level = 0;\n                }\n                if (t1_or_t2)\n                    ImGui::PopStyleColor();\n                if (j != 3)\n                    ImGui::SameLine();\n                mo++;\n            }\n        }\n    }\n    else if (*level == 2) {\n        *t = FloorTime(*t, ImPlotTimeUnit_Yr);\n        int this_yr = GetYear(*t);\n        int yr = this_yr  - this_yr % 20;\n        ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);\n        ImFormatString(buff,32,\"%d-%d\",yr,yr+19);\n        ImGui::Button(buff);\n        ImGui::PopItemFlag();\n        ImGui::SameLine(5*cell_size.x);\n        BeginDisabledControls(yr <= min_yr);\n        if (ImGui::ArrowButtonEx(\"##Up\",ImGuiDir_Up,cell_size))\n            *t = MakeTime(yr-20);\n        EndDisabledControls(yr <= min_yr);\n        ImGui::SameLine();\n        BeginDisabledControls(yr + 20 >= max_yr);\n        if (ImGui::ArrowButtonEx(\"##Down\",ImGuiDir_Down,cell_size))\n            *t = MakeTime(yr+20);\n        EndDisabledControls(yr+ 20 >= max_yr);\n        // ImGui::Dummy(cell_size);\n        cell_size.x *= 7.0f/4.0f;\n        cell_size.y *= 7.0f/5.0f;\n        for (int i = 0; i < 5; ++i) {\n            for (int j = 0; j < 4; ++j) {\n                const bool t1_or_t2 = (t1 != nullptr && t1_yr == yr) || (t2 != nullptr && t2_yr == yr);\n                if (t1_or_t2)\n                    ImGui::PushStyleColor(ImGuiCol_Button, col_btn);\n                ImFormatString(buff,32,\"%d\",yr);\n                if (yr<1970||yr>3000) {\n                    ImGui::Dummy(cell_size);\n                }\n                else if (ImGui::Button(buff,cell_size)) {\n                    *t = MakeTime(yr);\n                    *level = 1;\n                }\n                if (t1_or_t2)\n                    ImGui::PopStyleColor();\n                if (j != 3)\n                    ImGui::SameLine();\n                yr++;\n            }\n        }\n    }\n    ImGui::PopStyleVar();\n    ImGui::PopStyleColor();\n    ImGui::EndGroup();\n    ImGui::PopID();\n    return clk;\n}\n\nbool ShowTimePicker(const char* id, ImPlotTime* t) {\n    ImPlotContext& gp = *GImPlot;\n    ImGui::PushID(id);\n    tm& Tm = gp.Tm;\n    GetTime(*t,&Tm);\n\n    static const char* nums[] = { \"00\",\"01\",\"02\",\"03\",\"04\",\"05\",\"06\",\"07\",\"08\",\"09\",\n                                  \"10\",\"11\",\"12\",\"13\",\"14\",\"15\",\"16\",\"17\",\"18\",\"19\",\n                                  \"20\",\"21\",\"22\",\"23\",\"24\",\"25\",\"26\",\"27\",\"28\",\"29\",\n                                  \"30\",\"31\",\"32\",\"33\",\"34\",\"35\",\"36\",\"37\",\"38\",\"39\",\n                                  \"40\",\"41\",\"42\",\"43\",\"44\",\"45\",\"46\",\"47\",\"48\",\"49\",\n                                  \"50\",\"51\",\"52\",\"53\",\"54\",\"55\",\"56\",\"57\",\"58\",\"59\"};\n\n    static const char* am_pm[] = {\"am\",\"pm\"};\n\n    bool hour24 = gp.Style.Use24HourClock;\n\n    int hr  = hour24 ? Tm.tm_hour : ((Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12);\n    int min = Tm.tm_min;\n    int sec = Tm.tm_sec;\n    int ap  = Tm.tm_hour < 12 ? 0 : 1;\n\n    bool changed = false;\n\n    ImVec2 spacing = ImGui::GetStyle().ItemSpacing;\n    spacing.x = 0;\n    float width    = ImGui::CalcTextSize(\"888\").x;\n    float height   = ImGui::GetFrameHeight();\n\n    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, spacing);\n    ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize,2.0f);\n    ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0));\n    ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));\n    ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered));\n\n    ImGui::SetNextItemWidth(width);\n    if (ImGui::BeginCombo(\"##hr\",nums[hr],ImGuiComboFlags_NoArrowButton)) {\n        const int ia = hour24 ? 0 : 1;\n        const int ib = hour24 ? 24 : 13;\n        for (int i = ia; i < ib; ++i) {\n            if (ImGui::Selectable(nums[i],i==hr)) {\n                hr = i;\n                changed = true;\n            }\n        }\n        ImGui::EndCombo();\n    }\n    ImGui::SameLine();\n    ImGui::Text(\":\");\n    ImGui::SameLine();\n    ImGui::SetNextItemWidth(width);\n    if (ImGui::BeginCombo(\"##min\",nums[min],ImGuiComboFlags_NoArrowButton)) {\n        for (int i = 0; i < 60; ++i) {\n            if (ImGui::Selectable(nums[i],i==min)) {\n                min = i;\n                changed = true;\n            }\n        }\n        ImGui::EndCombo();\n    }\n    ImGui::SameLine();\n    ImGui::Text(\":\");\n    ImGui::SameLine();\n    ImGui::SetNextItemWidth(width);\n    if (ImGui::BeginCombo(\"##sec\",nums[sec],ImGuiComboFlags_NoArrowButton)) {\n        for (int i = 0; i < 60; ++i) {\n            if (ImGui::Selectable(nums[i],i==sec)) {\n                sec = i;\n                changed = true;\n            }\n        }\n        ImGui::EndCombo();\n    }\n    if (!hour24) {\n        ImGui::SameLine();\n        if (ImGui::Button(am_pm[ap],ImVec2(0,height))) {\n            ap = 1 - ap;\n            changed = true;\n        }\n    }\n\n    ImGui::PopStyleColor(3);\n    ImGui::PopStyleVar(2);\n    ImGui::PopID();\n\n    if (changed) {\n        if (!hour24)\n            hr = hr % 12 + ap * 12;\n        Tm.tm_hour = hr;\n        Tm.tm_min  = min;\n        Tm.tm_sec  = sec;\n        *t = MkTime(&Tm);\n    }\n\n    return changed;\n}\n\nvoid StyleColorsAuto(ImPlotStyle* dst) {\n    ImPlotStyle* style              = dst ? dst : &ImPlot::GetStyle();\n    ImVec4* colors                  = style->Colors;\n\n    style->MinorAlpha               = 0.25f;\n\n    colors[ImPlotCol_Line]          = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_Fill]          = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_MarkerFill]    = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_ErrorBar]      = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_FrameBg]       = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_PlotBg]        = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_PlotBorder]    = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_LegendBg]      = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_LegendBorder]  = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_LegendText]    = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_TitleText]     = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_InlayText]     = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_PlotBorder]    = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_AxisText]      = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_AxisGrid]      = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_AxisTick]      = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_AxisBg]        = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_AxisBgActive]  = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_Selection]     = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_Crosshairs]    = IMPLOT_AUTO_COL;\n}\n\nvoid StyleColorsClassic(ImPlotStyle* dst) {\n    ImPlotStyle* style              = dst ? dst : &ImPlot::GetStyle();\n    ImVec4* colors                  = style->Colors;\n\n    style->MinorAlpha               = 0.5f;\n\n    colors[ImPlotCol_Line]          = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_Fill]          = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_MarkerFill]    = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_ErrorBar]      = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImPlotCol_FrameBg]       = ImVec4(0.43f, 0.43f, 0.43f, 0.39f);\n    colors[ImPlotCol_PlotBg]        = ImVec4(0.00f, 0.00f, 0.00f, 0.35f);\n    colors[ImPlotCol_PlotBorder]    = ImVec4(0.50f, 0.50f, 0.50f, 0.50f);\n    colors[ImPlotCol_LegendBg]      = ImVec4(0.11f, 0.11f, 0.14f, 0.92f);\n    colors[ImPlotCol_LegendBorder]  = ImVec4(0.50f, 0.50f, 0.50f, 0.50f);\n    colors[ImPlotCol_LegendText]    = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImPlotCol_TitleText]     = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImPlotCol_InlayText]     = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImPlotCol_AxisText]      = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImPlotCol_AxisGrid]      = ImVec4(0.90f, 0.90f, 0.90f, 0.25f);\n    colors[ImPlotCol_AxisTick]      = IMPLOT_AUTO_COL; // TODO\n    colors[ImPlotCol_AxisBg]        = IMPLOT_AUTO_COL; // TODO\n    colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO\n    colors[ImPlotCol_AxisBgActive]  = IMPLOT_AUTO_COL; // TODO\n    colors[ImPlotCol_Selection]     = ImVec4(0.97f, 0.97f, 0.39f, 1.00f);\n    colors[ImPlotCol_Crosshairs]    = ImVec4(0.50f, 0.50f, 0.50f, 0.75f);\n}\n\nvoid StyleColorsDark(ImPlotStyle* dst) {\n    ImPlotStyle* style              = dst ? dst : &ImPlot::GetStyle();\n    ImVec4* colors                  = style->Colors;\n\n    style->MinorAlpha               = 0.25f;\n\n    colors[ImPlotCol_Line]          = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_Fill]          = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_MarkerFill]    = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_ErrorBar]      = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_FrameBg]       = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);\n    colors[ImPlotCol_PlotBg]        = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);\n    colors[ImPlotCol_PlotBorder]    = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);\n    colors[ImPlotCol_LegendBg]      = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);\n    colors[ImPlotCol_LegendBorder]  = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);\n    colors[ImPlotCol_LegendText]    = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlotCol_TitleText]     = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlotCol_InlayText]     = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlotCol_AxisText]      = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlotCol_AxisGrid]      = ImVec4(1.00f, 1.00f, 1.00f, 0.25f);\n    colors[ImPlotCol_AxisTick]      = IMPLOT_AUTO_COL; // TODO\n    colors[ImPlotCol_AxisBg]        = IMPLOT_AUTO_COL; // TODO\n    colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO\n    colors[ImPlotCol_AxisBgActive]  = IMPLOT_AUTO_COL; // TODO\n    colors[ImPlotCol_Selection]     = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);\n    colors[ImPlotCol_Crosshairs]    = ImVec4(1.00f, 1.00f, 1.00f, 0.50f);\n}\n\nvoid StyleColorsLight(ImPlotStyle* dst) {\n    ImPlotStyle* style              = dst ? dst : &ImPlot::GetStyle();\n    ImVec4* colors                  = style->Colors;\n\n    style->MinorAlpha               = 1.0f;\n\n    colors[ImPlotCol_Line]          = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_Fill]          = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_MarkerFill]    = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_ErrorBar]      = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_FrameBg]       = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlotCol_PlotBg]        = ImVec4(0.42f, 0.57f, 1.00f, 0.13f);\n    colors[ImPlotCol_PlotBorder]    = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImPlotCol_LegendBg]      = ImVec4(1.00f, 1.00f, 1.00f, 0.98f);\n    colors[ImPlotCol_LegendBorder]  = ImVec4(0.82f, 0.82f, 0.82f, 0.80f);\n    colors[ImPlotCol_LegendText]    = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlotCol_TitleText]     = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlotCol_InlayText]     = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlotCol_AxisText]      = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlotCol_AxisGrid]      = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlotCol_AxisTick]      = ImVec4(0.00f, 0.00f, 0.00f, 0.25f);\n    colors[ImPlotCol_AxisBg]        = IMPLOT_AUTO_COL; // TODO\n    colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO\n    colors[ImPlotCol_AxisBgActive]  = IMPLOT_AUTO_COL; // TODO\n    colors[ImPlotCol_Selection]     = ImVec4(0.82f, 0.64f, 0.03f, 1.00f);\n    colors[ImPlotCol_Crosshairs]    = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Obsolete Functions/Types\n//-----------------------------------------------------------------------------\n\n#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS\n\nbool BeginPlot(const char* title, const char* x_label, const char* y1_label, const ImVec2& size,\n               ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y1_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags,\n               const char* y2_label, const char* y3_label)\n{\n    if (!BeginPlot(title, size, flags))\n        return false;\n    SetupAxis(ImAxis_X1, x_label, x_flags);\n    SetupAxis(ImAxis_Y1, y1_label, y1_flags);\n    if (ImHasFlag(flags, ImPlotFlags_YAxis2))\n        SetupAxis(ImAxis_Y2, y2_label, y2_flags);\n    if (ImHasFlag(flags, ImPlotFlags_YAxis3))\n        SetupAxis(ImAxis_Y3, y3_label, y3_flags);\n    return true;\n}\n\n#endif\n\n}  // namespace ImPlot\n"
  },
  {
    "path": "lib/third_party/imgui/implot/source/implot_demo.cpp",
    "content": "// MIT License\n\n// Copyright (c) 2023 Evan Pezent\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n// ImPlot v0.17\n\n// We define this so that the demo does not accidentally use deprecated API\n#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS\n#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS\n#endif\n\n#include \"implot.h\"\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n\n#ifdef _MSC_VER\n#define sprintf sprintf_s\n#endif\n\n#ifndef PI\n#define PI 3.14159265358979323846\n#endif\n\n#define CHECKBOX_FLAG(flags, flag) ImGui::CheckboxFlags(#flag, (unsigned int*)&flags, flag)\n\n// Encapsulates examples for customizing ImPlot.\nnamespace MyImPlot {\n\n// Example for Custom Data and Getters section.\nstruct Vector2f {\n    Vector2f(float _x, float _y) { x = _x; y = _y; }\n    float x, y;\n};\n\n// Example for Custom Data and Getters section.\nstruct WaveData {\n    double X, Amp, Freq, Offset;\n    WaveData(double x, double amp, double freq, double offset) { X = x; Amp = amp; Freq = freq; Offset = offset; }\n};\nImPlotPoint SineWave(int idx, void* wave_data);\nImPlotPoint SawWave(int idx, void* wave_data);\nImPlotPoint Spiral(int idx, void* wave_data);\n\n// Example for Tables section.\nvoid Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size);\n\n// Example for Custom Plotters and Tooltips section.\nvoid PlotCandlestick(const char* label_id, const double* xs, const double* opens, const double* closes, const double* lows, const double* highs, int count, bool tooltip = true, float width_percent = 0.25f, ImVec4 bullCol = ImVec4(0,1,0,1), ImVec4 bearCol = ImVec4(1,0,0,1));\n\n// Example for Custom Styles section.\nvoid StyleSeaborn();\n\n} // namespace MyImPlot\n\nnamespace ImPlot {\n\ntemplate <typename T>\ninline T RandomRange(T min, T max) {\n    T scale = rand() / (T) RAND_MAX;\n    return min + scale * ( max - min );\n}\n\nImVec4 RandomColor() {\n    ImVec4 col;\n    col.x = RandomRange(0.0f,1.0f);\n    col.y = RandomRange(0.0f,1.0f);\n    col.z = RandomRange(0.0f,1.0f);\n    col.w = 1.0f;\n    return col;\n}\n\ndouble RandomGauss() {\n\tstatic double V1, V2, S;\n\tstatic int phase = 0;\n\tdouble X;\n\tif(phase == 0) {\n\t\tdo {\n\t\t\tdouble U1 = (double)rand() / RAND_MAX;\n\t\t\tdouble U2 = (double)rand() / RAND_MAX;\n\t\t\tV1 = 2 * U1 - 1;\n\t\t\tV2 = 2 * U2 - 1;\n\t\t\tS = V1 * V1 + V2 * V2;\n\t\t\t} while(S >= 1 || S == 0);\n\n\t\tX = V1 * sqrt(-2 * log(S) / S);\n\t} else\n\t\tX = V2 * sqrt(-2 * log(S) / S);\n\tphase = 1 - phase;\n\treturn X;\n}\n\ntemplate <int N>\nstruct NormalDistribution {\n    NormalDistribution(double mean, double sd) {\n        for (int i = 0; i < N; ++i)\n            Data[i] = RandomGauss()*sd + mean;\n    }\n    double Data[N];\n};\n\n// utility structure for realtime plot\nstruct ScrollingBuffer {\n    int MaxSize;\n    int Offset;\n    ImVector<ImVec2> Data;\n    ScrollingBuffer(int max_size = 2000) {\n        MaxSize = max_size;\n        Offset  = 0;\n        Data.reserve(MaxSize);\n    }\n    void AddPoint(float x, float y) {\n        if (Data.size() < MaxSize)\n            Data.push_back(ImVec2(x,y));\n        else {\n            Data[Offset] = ImVec2(x,y);\n            Offset =  (Offset + 1) % MaxSize;\n        }\n    }\n    void Erase() {\n        if (Data.size() > 0) {\n            Data.shrink(0);\n            Offset  = 0;\n        }\n    }\n};\n\n// utility structure for realtime plot\nstruct RollingBuffer {\n    float Span;\n    ImVector<ImVec2> Data;\n    RollingBuffer() {\n        Span = 10.0f;\n        Data.reserve(2000);\n    }\n    void AddPoint(float x, float y) {\n        float xmod = fmodf(x, Span);\n        if (!Data.empty() && xmod < Data.back().x)\n            Data.shrink(0);\n        Data.push_back(ImVec2(xmod, y));\n    }\n};\n\n// Huge data used by Time Formatting example (~500 MB allocation!)\nstruct HugeTimeData {\n    HugeTimeData(double min) {\n        Ts = new double[Size];\n        Ys = new double[Size];\n        for (int i = 0; i < Size; ++i) {\n            Ts[i] = min + i;\n            Ys[i] = GetY(Ts[i]);\n        }\n    }\n    ~HugeTimeData() { delete[] Ts;  delete[] Ys; }\n    static double GetY(double t) {\n        return 0.5 + 0.25 * sin(t/86400/12) +  0.005 * sin(t/3600);\n    }\n    double* Ts;\n    double* Ys;\n    static const int Size = 60*60*24*366;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Demo Functions\n//-----------------------------------------------------------------------------\n\nvoid Demo_Help() {\n    ImGui::Text(\"ABOUT THIS DEMO:\");\n    ImGui::BulletText(\"Sections below are demonstrating many aspects of the library.\");\n    ImGui::BulletText(\"The \\\"Tools\\\" menu above gives access to: Style Editors (ImPlot/ImGui)\\n\"\n                        \"and Metrics (general purpose Dear ImGui debugging tool).\");\n    ImGui::Separator();\n    ImGui::Text(\"PROGRAMMER GUIDE:\");\n    ImGui::BulletText(\"See the ShowDemoWindow() code in implot_demo.cpp. <- you are here!\");\n    ImGui::BulletText(\"If you see visual artifacts, do one of the following:\");\n    ImGui::Indent();\n    ImGui::BulletText(\"Handle ImGuiBackendFlags_RendererHasVtxOffset for 16-bit indices in your backend.\");\n    ImGui::BulletText(\"Or, enable 32-bit indices in imconfig.h.\");\n    ImGui::BulletText(\"Your current configuration is:\");\n    ImGui::Indent();\n    ImGui::BulletText(\"ImDrawIdx: %d-bit\", (int)(sizeof(ImDrawIdx) * 8));\n    ImGui::BulletText(\"ImGuiBackendFlags_RendererHasVtxOffset: %s\", (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ? \"True\" : \"False\");\n    ImGui::Unindent();\n    ImGui::Unindent();\n    ImGui::Separator();\n    ImGui::Text(\"USER GUIDE:\");\n    ShowUserGuide();\n}\n\n//-----------------------------------------------------------------------------\n\nvoid ButtonSelector(const char* label, ImGuiMouseButton* b) {\n    ImGui::PushID(label);\n    if (ImGui::RadioButton(\"LMB\",*b == ImGuiMouseButton_Left))\n        *b = ImGuiMouseButton_Left;\n    ImGui::SameLine();\n    if (ImGui::RadioButton(\"RMB\",*b == ImGuiMouseButton_Right))\n        *b = ImGuiMouseButton_Right;\n    ImGui::SameLine();\n    if (ImGui::RadioButton(\"MMB\",*b == ImGuiMouseButton_Middle))\n        *b = ImGuiMouseButton_Middle;\n    ImGui::PopID();\n}\n\nvoid ModSelector(const char* label, int* k) {\n    ImGui::PushID(label);\n    ImGui::CheckboxFlags(\"Ctrl\", (unsigned int*)k, ImGuiMod_Ctrl); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"Shift\", (unsigned int*)k, ImGuiMod_Shift); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"Alt\", (unsigned int*)k, ImGuiMod_Alt); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"Super\", (unsigned int*)k, ImGuiMod_Super);\n    ImGui::PopID();\n}\n\nvoid InputMapping(const char* label, ImGuiMouseButton* b, int* k) {\n    ImGui::LabelText(\"##\",\"%s\",label);\n    if (b != nullptr) {\n        ImGui::SameLine(100);\n        ButtonSelector(label,b);\n    }\n    if (k != nullptr) {\n        ImGui::SameLine(300);\n        ModSelector(label,k);\n    }\n}\n\nvoid ShowInputMapping() {\n    ImPlotInputMap& map = ImPlot::GetInputMap();\n    InputMapping(\"Pan\",&map.Pan,&map.PanMod);\n    InputMapping(\"Fit\",&map.Fit,nullptr);\n    InputMapping(\"Select\",&map.Select,&map.SelectMod);\n    InputMapping(\"SelectHorzMod\",nullptr,&map.SelectHorzMod);\n    InputMapping(\"SelectVertMod\",nullptr,&map.SelectVertMod);\n    InputMapping(\"SelectCancel\",&map.SelectCancel,nullptr);\n    InputMapping(\"Menu\",&map.Menu,nullptr);\n    InputMapping(\"OverrideMod\",nullptr,&map.OverrideMod);\n    InputMapping(\"ZoomMod\",nullptr,&map.ZoomMod);\n    ImGui::SliderFloat(\"ZoomRate\",&map.ZoomRate,-1,1);\n}\n\nvoid Demo_Config() {\n    ImGui::ShowFontSelector(\"Font\");\n    ImGui::ShowStyleSelector(\"ImGui Style\");\n    ImPlot::ShowStyleSelector(\"ImPlot Style\");\n    ImPlot::ShowColormapSelector(\"ImPlot Colormap\");\n    ImPlot::ShowInputMapSelector(\"Input Map\");\n    ImGui::Separator();\n    ImGui::Checkbox(\"Use Local Time\", &ImPlot::GetStyle().UseLocalTime);\n    ImGui::Checkbox(\"Use ISO 8601\", &ImPlot::GetStyle().UseISO8601);\n    ImGui::Checkbox(\"Use 24 Hour Clock\", &ImPlot::GetStyle().Use24HourClock);\n    ImGui::Separator();\n    if (ImPlot::BeginPlot(\"Preview\")) {\n        static double now = (double)time(nullptr);\n        ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time);\n        ImPlot::SetupAxisLimits(ImAxis_X1, now, now + 24*3600);\n        for (int i = 0; i < 10; ++i) {\n            double x[2] = {now, now + 24*3600};\n            double y[2] = {0,i/9.0};\n            ImGui::PushID(i);\n            ImPlot::PlotLine(\"##Line\",x,y,2);\n            ImGui::PopID();\n        }\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_LinePlots() {\n    static float xs1[1001], ys1[1001];\n    for (int i = 0; i < 1001; ++i) {\n        xs1[i] = i * 0.001f;\n        ys1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)ImGui::GetTime() / 10));\n    }\n    static double xs2[20], ys2[20];\n    for (int i = 0; i < 20; ++i) {\n        xs2[i] = i * 1/19.0f;\n        ys2[i] = xs2[i] * xs2[i];\n    }\n    if (ImPlot::BeginPlot(\"Line Plots\")) {\n        ImPlot::SetupAxes(\"x\",\"y\");\n        ImPlot::PlotLine(\"f(x)\", xs1, ys1, 1001);\n        ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle);\n        ImPlot::PlotLine(\"g(x)\", xs2, ys2, 20,ImPlotLineFlags_Segments);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_FilledLinePlots() {\n    static double xs1[101], ys1[101], ys2[101], ys3[101];\n    srand(0);\n    for (int i = 0; i < 101; ++i) {\n        xs1[i] = (float)i;\n        ys1[i] = RandomRange(400.0,450.0);\n        ys2[i] = RandomRange(275.0,350.0);\n        ys3[i] = RandomRange(150.0,225.0);\n    }\n    static bool show_lines = true;\n    static bool show_fills = true;\n    static float fill_ref = 0;\n    static int shade_mode = 0;\n    static ImPlotShadedFlags flags = 0;\n    ImGui::Checkbox(\"Lines\",&show_lines); ImGui::SameLine();\n    ImGui::Checkbox(\"Fills\",&show_fills);\n    if (show_fills) {\n        ImGui::SameLine();\n        if (ImGui::RadioButton(\"To -INF\",shade_mode == 0))\n            shade_mode = 0;\n        ImGui::SameLine();\n        if (ImGui::RadioButton(\"To +INF\",shade_mode == 1))\n            shade_mode = 1;\n        ImGui::SameLine();\n        if (ImGui::RadioButton(\"To Ref\",shade_mode == 2))\n            shade_mode = 2;\n        if (shade_mode == 2) {\n            ImGui::SameLine();\n            ImGui::SetNextItemWidth(100);\n            ImGui::DragFloat(\"##Ref\",&fill_ref, 1, -100, 500);\n        }\n    }\n\n    if (ImPlot::BeginPlot(\"Stock Prices\")) {\n        ImPlot::SetupAxes(\"Days\",\"Price\");\n        ImPlot::SetupAxesLimits(0,100,0,500);\n        if (show_fills) {\n            ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f);\n            ImPlot::PlotShaded(\"Stock 1\", xs1, ys1, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags);\n            ImPlot::PlotShaded(\"Stock 2\", xs1, ys2, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags);\n            ImPlot::PlotShaded(\"Stock 3\", xs1, ys3, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags);\n            ImPlot::PopStyleVar();\n        }\n        if (show_lines) {\n            ImPlot::PlotLine(\"Stock 1\", xs1, ys1, 101);\n            ImPlot::PlotLine(\"Stock 2\", xs1, ys2, 101);\n            ImPlot::PlotLine(\"Stock 3\", xs1, ys3, 101);\n        }\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_ShadedPlots() {\n    static float xs[1001], ys[1001], ys1[1001], ys2[1001], ys3[1001], ys4[1001];\n    srand(0);\n    for (int i = 0; i < 1001; ++i) {\n        xs[i]  = i * 0.001f;\n        ys[i]  = 0.25f + 0.25f * sinf(25 * xs[i]) * sinf(5 * xs[i]) + RandomRange(-0.01f, 0.01f);\n        ys1[i] = ys[i] + RandomRange(0.1f, 0.12f);\n        ys2[i] = ys[i] - RandomRange(0.1f, 0.12f);\n        ys3[i] = 0.75f + 0.2f * sinf(25 * xs[i]);\n        ys4[i] = 0.75f + 0.1f * cosf(25 * xs[i]);\n    }\n    static float alpha = 0.25f;\n    ImGui::DragFloat(\"Alpha\",&alpha,0.01f,0,1);\n\n    if (ImPlot::BeginPlot(\"Shaded Plots\")) {\n        ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha);\n        ImPlot::PlotShaded(\"Uncertain Data\",xs,ys1,ys2,1001);\n        ImPlot::PlotLine(\"Uncertain Data\", xs, ys, 1001);\n        ImPlot::PlotShaded(\"Overlapping\",xs,ys3,ys4,1001);\n        ImPlot::PlotLine(\"Overlapping\",xs,ys3,1001);\n        ImPlot::PlotLine(\"Overlapping\",xs,ys4,1001);\n        ImPlot::PopStyleVar();\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_ScatterPlots() {\n    srand(0);\n    static float xs1[100], ys1[100];\n    for (int i = 0; i < 100; ++i) {\n        xs1[i] = i * 0.01f;\n        ys1[i] = xs1[i] + 0.1f * ((float)rand() / (float)RAND_MAX);\n    }\n    static float xs2[50], ys2[50];\n    for (int i = 0; i < 50; i++) {\n        xs2[i] = 0.25f + 0.2f * ((float)rand() / (float)RAND_MAX);\n        ys2[i] = 0.75f + 0.2f * ((float)rand() / (float)RAND_MAX);\n    }\n\n    if (ImPlot::BeginPlot(\"Scatter Plot\")) {\n        ImPlot::PlotScatter(\"Data 1\", xs1, ys1, 100);\n        ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f);\n        ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6, ImPlot::GetColormapColor(1), IMPLOT_AUTO, ImPlot::GetColormapColor(1));\n        ImPlot::PlotScatter(\"Data 2\", xs2, ys2, 50);\n        ImPlot::PopStyleVar();\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_StairstepPlots() {\n    static float ys1[21], ys2[21];\n    for (int i = 0; i < 21; ++i) {\n        ys1[i] = 0.75f + 0.2f * sinf(10 * i * 0.05f);\n        ys2[i] = 0.25f + 0.2f * sinf(10 * i * 0.05f);\n    }\n    static ImPlotStairsFlags flags = 0;\n    CHECKBOX_FLAG(flags, ImPlotStairsFlags_Shaded);\n    if (ImPlot::BeginPlot(\"Stairstep Plot\")) {\n        ImPlot::SetupAxes(\"x\",\"f(x)\");\n        ImPlot::SetupAxesLimits(0,1,0,1);\n\n        ImPlot::PushStyleColor(ImPlotCol_Line, ImVec4(0.5f,0.5f,0.5f,1.0f));\n        ImPlot::PlotLine(\"##1\",ys1,21,0.05f);\n        ImPlot::PlotLine(\"##2\",ys2,21,0.05f);\n        ImPlot::PopStyleColor();\n\n        ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle);\n        ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.25f);\n        ImPlot::PlotStairs(\"Post Step (default)\", ys1, 21, 0.05f, 0, flags);\n        ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle);\n        ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.25f);\n        ImPlot::PlotStairs(\"Pre Step\", ys2, 21, 0.05f, 0, flags|ImPlotStairsFlags_PreStep);\n\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_BarPlots() {\n    static ImS8  data[10] = {1,2,3,4,5,6,7,8,9,10};\n    if (ImPlot::BeginPlot(\"Bar Plot\")) {\n        ImPlot::PlotBars(\"Vertical\",data,10,0.7,1);\n        ImPlot::PlotBars(\"Horizontal\",data,10,0.4,1,ImPlotBarsFlags_Horizontal);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_BarGroups() {\n    static ImS8  data[30] = {83, 67, 23, 89, 83, 78, 91, 82, 85, 90,  // midterm\n                             80, 62, 56, 99, 55, 78, 88, 78, 90, 100, // final\n                             80, 69, 52, 92, 72, 78, 75, 76, 89, 95}; // course\n\n    static const char*  ilabels[]   = {\"Midterm Exam\",\"Final Exam\",\"Course Grade\"};\n    static const char*  glabels[]   = {\"S1\",\"S2\",\"S3\",\"S4\",\"S5\",\"S6\",\"S7\",\"S8\",\"S9\",\"S10\"};\n    static const double positions[] = {0,1,2,3,4,5,6,7,8,9};\n\n    static int items  = 3;\n    static int groups = 10;\n    static float size = 0.67f;\n\n    static ImPlotBarGroupsFlags flags = 0;\n    static bool horz = false;\n\n    ImGui::CheckboxFlags(\"Stacked\", (unsigned int*)&flags, ImPlotBarGroupsFlags_Stacked);\n    ImGui::SameLine();\n    ImGui::Checkbox(\"Horizontal\",&horz);\n\n    ImGui::SliderInt(\"Items\",&items,1,3);\n    ImGui::SliderFloat(\"Size\",&size,0,1);\n\n    if (ImPlot::BeginPlot(\"Bar Group\")) {\n        ImPlot::SetupLegend(ImPlotLocation_East, ImPlotLegendFlags_Outside);\n        if (horz) {\n            ImPlot::SetupAxes(\"Score\",\"Student\",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit);\n            ImPlot::SetupAxisTicks(ImAxis_Y1,positions, groups, glabels);\n            ImPlot::PlotBarGroups(ilabels,data,items,groups,size,0,flags|ImPlotBarGroupsFlags_Horizontal);\n        }\n        else {\n            ImPlot::SetupAxes(\"Student\",\"Score\",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit);\n            ImPlot::SetupAxisTicks(ImAxis_X1,positions, groups, glabels);\n            ImPlot::PlotBarGroups(ilabels,data,items,groups,size,0,flags);\n        }\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_BarStacks() {\n\n    static ImPlotColormap Liars = -1;\n    if (Liars == -1) {\n        static const ImU32 Liars_Data[6] = { 4282515870, 4282609140, 4287357182, 4294630301, 4294945280, 4294921472 };\n        Liars = ImPlot::AddColormap(\"Liars\", Liars_Data, 6);\n    }\n\n    static bool diverging = true;\n    ImGui::Checkbox(\"Diverging\",&diverging);\n\n    static const char* politicians[] = {\"Trump\",\"Bachman\",\"Cruz\",\"Gingrich\",\"Palin\",\"Santorum\",\"Walker\",\"Perry\",\"Ryan\",\"McCain\",\"Rubio\",\"Romney\",\"Rand Paul\",\"Christie\",\"Biden\",\"Kasich\",\"Sanders\",\"J Bush\",\"H Clinton\",\"Obama\"};\n    static int data_reg[] = {18,26,7,14,10,8,6,11,4,4,3,8,6,8,6,5,0,3,1,2,                // Pants on Fire\n                             43,36,30,21,30,27,25,17,11,22,15,16,16,17,12,12,14,6,13,12,  // False\n                             16,13,28,22,15,21,15,18,30,17,24,18,13,10,14,15,17,22,14,12, // Mostly False\n                             17,10,13,25,12,22,19,26,23,17,22,27,20,26,29,17,18,22,21,27, // Half True\n                             5,7,16,10,10,12,23,13,17,20,22,16,23,19,20,26,36,29,27,26,   // Mostly True\n                             1,8,6,8,23,10,12,15,15,20,14,15,22,20,19,25,15,18,24,21};    // True\n    static const char* labels_reg[] = {\"Pants on Fire\",\"False\",\"Mostly False\",\"Half True\",\"Mostly True\",\"True\"};\n\n\n    static int data_div[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                                         // Pants on Fire (dummy, to order legend logically)\n                             0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                                         // False         (dummy, to order legend logically)\n                             0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                                         // Mostly False  (dummy, to order legend logically)\n                             -16,-13,-28,-22,-15,-21,-15,-18,-30,-17,-24,-18,-13,-10,-14,-15,-17,-22,-14,-12, // Mostly False\n                             -43,-36,-30,-21,-30,-27,-25,-17,-11,-22,-15,-16,-16,-17,-12,-12,-14,-6,-13,-12,  // False\n                             -18,-26,-7,-14,-10,-8,-6,-11,-4,-4,-3,-8,-6,-8,-6,-5,0,-3,-1,-2,                 // Pants on Fire\n                             17,10,13,25,12,22,19,26,23,17,22,27,20,26,29,17,18,22,21,27,                     // Half True\n                             5,7,16,10,10,12,23,13,17,20,22,16,23,19,20,26,36,29,27,26,                       // Mostly True\n                             1,8,6,8,23,10,12,15,15,20,14,15,22,20,19,25,15,18,24,21};                        // True\n    static const char* labels_div[] = {\"Pants on Fire\",\"False\",\"Mostly False\",\"Mostly False\",\"False\",\"Pants on Fire\",\"Half True\",\"Mostly True\",\"True\"};\n\n    ImPlot::PushColormap(Liars);\n    if (ImPlot::BeginPlot(\"PolitiFact: Who Lies More?\",ImVec2(-1,400),ImPlotFlags_NoMouseText)) {\n        ImPlot::SetupLegend(ImPlotLocation_South, ImPlotLegendFlags_Outside|ImPlotLegendFlags_Horizontal);\n        ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Invert);\n        ImPlot::SetupAxisTicks(ImAxis_Y1,0,19,20,politicians,false);\n        if (diverging)\n            ImPlot::PlotBarGroups(labels_div,data_div,9,20,0.75,0,ImPlotBarGroupsFlags_Stacked|ImPlotBarGroupsFlags_Horizontal);\n        else\n            ImPlot::PlotBarGroups(labels_reg,data_reg,6,20,0.75,0,ImPlotBarGroupsFlags_Stacked|ImPlotBarGroupsFlags_Horizontal);\n        ImPlot::EndPlot();\n    }\n    ImPlot::PopColormap();\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_ErrorBars() {\n    static float xs[5]    = {1,2,3,4,5};\n    static float bar[5]   = {1,2,5,3,4};\n    static float lin1[5]  = {8,8,9,7,8};\n    static float lin2[5]  = {6,7,6,9,6};\n    static float err1[5]  = {0.2f, 0.4f, 0.2f, 0.6f, 0.4f};\n    static float err2[5]  = {0.4f, 0.2f, 0.4f, 0.8f, 0.6f};\n    static float err3[5]  = {0.09f, 0.14f, 0.09f, 0.12f, 0.16f};\n    static float err4[5]  = {0.02f, 0.08f, 0.15f, 0.05f, 0.2f};\n\n\n    if (ImPlot::BeginPlot(\"##ErrorBars\")) {\n        ImPlot::SetupAxesLimits(0, 6, 0, 10);\n        ImPlot::PlotBars(\"Bar\", xs, bar, 5, 0.5f);\n        ImPlot::PlotErrorBars(\"Bar\", xs, bar, err1, 5);\n        ImPlot::SetNextErrorBarStyle(ImPlot::GetColormapColor(1), 0);\n        ImPlot::PlotErrorBars(\"Line\", xs, lin1, err1, err2, 5);\n        ImPlot::SetNextMarkerStyle(ImPlotMarker_Square);\n        ImPlot::PlotLine(\"Line\", xs, lin1, 5);\n        ImPlot::PushStyleColor(ImPlotCol_ErrorBar, ImPlot::GetColormapColor(2));\n        ImPlot::PlotErrorBars(\"Scatter\", xs, lin2, err2, 5);\n        ImPlot::PlotErrorBars(\"Scatter\", xs, lin2,  err3, err4, 5, ImPlotErrorBarsFlags_Horizontal);\n        ImPlot::PopStyleColor();\n        ImPlot::PlotScatter(\"Scatter\", xs, lin2, 5);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_StemPlots() {\n    static double xs[51], ys1[51], ys2[51];\n    for (int i = 0; i < 51; ++i) {\n        xs[i] = i * 0.02;\n        ys1[i] = 1.0 + 0.5 * sin(25*xs[i])*cos(2*xs[i]);\n        ys2[i] = 0.5 + 0.25  * sin(10*xs[i]) * sin(xs[i]);\n    }\n    if (ImPlot::BeginPlot(\"Stem Plots\")) {\n        ImPlot::SetupAxisLimits(ImAxis_X1,0,1.0);\n        ImPlot::SetupAxisLimits(ImAxis_Y1,0,1.6);\n        ImPlot::PlotStems(\"Stems 1\",xs,ys1,51);\n        ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle);\n        ImPlot::PlotStems(\"Stems 2\", xs, ys2,51);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_InfiniteLines() {\n    static double vals[] = {0.25, 0.5, 0.75};\n    if (ImPlot::BeginPlot(\"##Infinite\")) {\n        ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoInitialFit,ImPlotAxisFlags_NoInitialFit);\n        ImPlot::PlotInfLines(\"Vertical\",vals,3);\n        ImPlot::PlotInfLines(\"Horizontal\",vals,3,ImPlotInfLinesFlags_Horizontal);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_PieCharts() {\n    static const char* labels1[]    = {\"Frogs\",\"Hogs\",\"Dogs\",\"Logs\"};\n    static float data1[]            = {0.15f,  0.30f,  0.2f, 0.05f};\n    static ImPlotPieChartFlags flags = 0;\n    ImGui::SetNextItemWidth(250);\n    ImGui::DragFloat4(\"Values\", data1, 0.01f, 0, 1);\n    CHECKBOX_FLAG(flags, ImPlotPieChartFlags_Normalize);\n    CHECKBOX_FLAG(flags, ImPlotPieChartFlags_IgnoreHidden);\n\n    if (ImPlot::BeginPlot(\"##Pie1\", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) {\n        ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);\n        ImPlot::SetupAxesLimits(0, 1, 0, 1);\n        ImPlot::PlotPieChart(labels1, data1, 4, 0.5, 0.5, 0.4, \"%.2f\", 90, flags);\n        ImPlot::EndPlot();\n    }\n\n    ImGui::SameLine();\n\n    static const char* labels2[]   = {\"A\",\"B\",\"C\",\"D\",\"E\"};\n    static int data2[]             = {1,1,2,3,5};\n\n    ImPlot::PushColormap(ImPlotColormap_Pastel);\n    if (ImPlot::BeginPlot(\"##Pie2\", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) {\n        ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);\n        ImPlot::SetupAxesLimits(0, 1, 0, 1);\n        ImPlot::PlotPieChart(labels2, data2, 5, 0.5, 0.5, 0.4, \"%.0f\", 180, flags);\n        ImPlot::EndPlot();\n    }\n    ImPlot::PopColormap();\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_Heatmaps() {\n    static float values1[7][7]  = {{0.8f, 2.4f, 2.5f, 3.9f, 0.0f, 4.0f, 0.0f},\n                                    {2.4f, 0.0f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f},\n                                    {1.1f, 2.4f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f},\n                                    {0.6f, 0.0f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f},\n                                    {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f},\n                                    {1.3f, 1.2f, 0.0f, 0.0f, 0.0f, 3.2f, 5.1f},\n                                    {0.1f, 2.0f, 0.0f, 1.4f, 0.0f, 1.9f, 6.3f}};\n    static float scale_min       = 0;\n    static float scale_max       = 6.3f;\n    static const char* xlabels[] = {\"C1\",\"C2\",\"C3\",\"C4\",\"C5\",\"C6\",\"C7\"};\n    static const char* ylabels[] = {\"R1\",\"R2\",\"R3\",\"R4\",\"R5\",\"R6\",\"R7\"};\n\n    static ImPlotColormap map = ImPlotColormap_Viridis;\n    if (ImPlot::ColormapButton(ImPlot::GetColormapName(map),ImVec2(225,0),map)) {\n        map = (map + 1) % ImPlot::GetColormapCount();\n        // We bust the color cache of our plots so that item colors will\n        // resample the new colormap in the event that they have already\n        // been created. See documentation in implot.h.\n        BustColorCache(\"##Heatmap1\");\n        BustColorCache(\"##Heatmap2\");\n    }\n\n    ImGui::SameLine();\n    ImGui::LabelText(\"##Colormap Index\", \"%s\", \"Change Colormap\");\n    ImGui::SetNextItemWidth(225);\n    ImGui::DragFloatRange2(\"Min / Max\",&scale_min, &scale_max, 0.01f, -20, 20);\n\n    static ImPlotHeatmapFlags hm_flags = 0;\n\n    ImGui::CheckboxFlags(\"Column Major\", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor);\n\n    static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks;\n\n    ImPlot::PushColormap(map);\n\n    if (ImPlot::BeginPlot(\"##Heatmap1\",ImVec2(225,225),ImPlotFlags_NoLegend|ImPlotFlags_NoMouseText)) {\n        ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags);\n        ImPlot::SetupAxisTicks(ImAxis_X1,0 + 1.0/14.0, 1 - 1.0/14.0, 7, xlabels);\n        ImPlot::SetupAxisTicks(ImAxis_Y1,1 - 1.0/14.0, 0 + 1.0/14.0, 7, ylabels);\n        ImPlot::PlotHeatmap(\"heat\",values1[0],7,7,scale_min,scale_max,\"%g\",ImPlotPoint(0,0),ImPlotPoint(1,1),hm_flags);\n        ImPlot::EndPlot();\n    }\n    ImGui::SameLine();\n    ImPlot::ColormapScale(\"##HeatScale\",scale_min, scale_max, ImVec2(60,225));\n\n    ImGui::SameLine();\n\n    const int size = 80;\n    static double values2[size*size];\n    srand((unsigned int)(ImGui::GetTime()*1000000));\n    for (int i = 0; i < size*size; ++i)\n        values2[i] = RandomRange(0.0,1.0);\n\n    if (ImPlot::BeginPlot(\"##Heatmap2\",ImVec2(225,225))) {\n        ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);\n        ImPlot::SetupAxesLimits(-1,1,-1,1);\n        ImPlot::PlotHeatmap(\"heat1\",values2,size,size,0,1,nullptr);\n        ImPlot::PlotHeatmap(\"heat2\",values2,size,size,0,1,nullptr, ImPlotPoint(-1,-1), ImPlotPoint(0,0));\n        ImPlot::EndPlot();\n    }\n    ImPlot::PopColormap();\n\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_Histogram() {\n    static ImPlotHistogramFlags hist_flags = ImPlotHistogramFlags_Density;\n    static int  bins       = 50;\n    static double mu       = 5;\n    static double sigma    = 2;\n    ImGui::SetNextItemWidth(200);\n    if (ImGui::RadioButton(\"Sqrt\",bins==ImPlotBin_Sqrt))       { bins = ImPlotBin_Sqrt;    } ImGui::SameLine();\n    if (ImGui::RadioButton(\"Sturges\",bins==ImPlotBin_Sturges)) { bins = ImPlotBin_Sturges; } ImGui::SameLine();\n    if (ImGui::RadioButton(\"Rice\",bins==ImPlotBin_Rice))       { bins = ImPlotBin_Rice;    } ImGui::SameLine();\n    if (ImGui::RadioButton(\"Scott\",bins==ImPlotBin_Scott))     { bins = ImPlotBin_Scott;   } ImGui::SameLine();\n    if (ImGui::RadioButton(\"N Bins\",bins>=0))                  { bins = 50;                }\n    if (bins>=0) {\n        ImGui::SameLine();\n        ImGui::SetNextItemWidth(200);\n        ImGui::SliderInt(\"##Bins\", &bins, 1, 100);\n    }\n    ImGui::CheckboxFlags(\"Horizontal\", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Horizontal);\n    ImGui::SameLine();\n    ImGui::CheckboxFlags(\"Density\", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Density);\n    ImGui::SameLine();\n    ImGui::CheckboxFlags(\"Cumulative\", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Cumulative);\n\n    static bool range = false;\n    ImGui::Checkbox(\"Range\", &range);\n    static float rmin = -3;\n    static float rmax = 13;\n    if (range) {\n        ImGui::SameLine();\n        ImGui::SetNextItemWidth(200);\n        ImGui::DragFloat2(\"##Range\",&rmin,0.1f,-3,13);\n        ImGui::SameLine();\n        ImGui::CheckboxFlags(\"Exclude Outliers\", (unsigned int*)&hist_flags, ImPlotHistogramFlags_NoOutliers);\n    }\n    static NormalDistribution<10000> dist(mu, sigma);\n    static double x[100];\n    static double y[100];\n    if (hist_flags & ImPlotHistogramFlags_Density) {\n        for (int i = 0; i < 100; ++i) {\n            x[i] = -3 + 16 * (double)i/99.0;\n            y[i] = exp( - (x[i]-mu)*(x[i]-mu) / (2*sigma*sigma)) / (sigma * sqrt(2*3.141592653589793238));\n        }\n        if (hist_flags & ImPlotHistogramFlags_Cumulative) {\n            for (int i = 1; i < 100; ++i)\n                y[i] += y[i-1];\n            for (int i = 0; i < 100; ++i)\n                y[i] /= y[99];\n        }\n    }\n\n    if (ImPlot::BeginPlot(\"##Histograms\")) {\n        ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit);\n        ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f);\n        ImPlot::PlotHistogram(\"Empirical\", dist.Data, 10000, bins, 1.0, range ? ImPlotRange(rmin,rmax) : ImPlotRange(), hist_flags);\n        if ((hist_flags & ImPlotHistogramFlags_Density) && !(hist_flags & ImPlotHistogramFlags_NoOutliers)) {\n            if (hist_flags & ImPlotHistogramFlags_Horizontal)\n                ImPlot::PlotLine(\"Theoretical\",y,x,100);\n            else\n                ImPlot::PlotLine(\"Theoretical\",x,y,100);\n        }\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_Histogram2D() {\n    static int count     = 50000;\n    static int xybins[2] = {100,100};\n\n    static ImPlotHistogramFlags hist_flags = 0;\n\n    ImGui::SliderInt(\"Count\",&count,100,100000);\n    ImGui::SliderInt2(\"Bins\",xybins,1,500);\n    ImGui::SameLine();\n    ImGui::CheckboxFlags(\"Density\", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Density);\n\n    static NormalDistribution<100000> dist1(1, 2);\n    static NormalDistribution<100000> dist2(1, 1);\n    double max_count = 0;\n    ImPlotAxisFlags flags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Foreground;\n    ImPlot::PushColormap(\"Hot\");\n    if (ImPlot::BeginPlot(\"##Hist2D\",ImVec2(ImGui::GetContentRegionAvail().x-100-ImGui::GetStyle().ItemSpacing.x,0))) {\n        ImPlot::SetupAxes(nullptr, nullptr, flags, flags);\n        ImPlot::SetupAxesLimits(-6,6,-6,6);\n        max_count = ImPlot::PlotHistogram2D(\"Hist2D\",dist1.Data,dist2.Data,count,xybins[0],xybins[1],ImPlotRect(-6,6,-6,6), hist_flags);\n        ImPlot::EndPlot();\n    }\n    ImGui::SameLine();\n    ImPlot::ColormapScale(hist_flags & ImPlotHistogramFlags_Density ? \"Density\" : \"Count\",0,max_count,ImVec2(100,0));\n    ImPlot::PopColormap();\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_DigitalPlots() {\n    ImGui::BulletText(\"Digital plots do not respond to Y drag and zoom, so that\");\n    ImGui::Indent();\n    ImGui::Text(\"you can drag analog plots over the rising/falling digital edge.\");\n    ImGui::Unindent();\n\n    static bool paused = false;\n    static ScrollingBuffer dataDigital[2];\n    static ScrollingBuffer dataAnalog[2];\n    static bool showDigital[2] = {true, false};\n    static bool showAnalog[2] = {true, false};\n\n    char label[32];\n    ImGui::Checkbox(\"digital_0\", &showDigital[0]); ImGui::SameLine();\n    ImGui::Checkbox(\"digital_1\", &showDigital[1]); ImGui::SameLine();\n    ImGui::Checkbox(\"analog_0\",  &showAnalog[0]);  ImGui::SameLine();\n    ImGui::Checkbox(\"analog_1\",  &showAnalog[1]);\n\n    static float t = 0;\n    if (!paused) {\n        t += ImGui::GetIO().DeltaTime;\n        //digital signal values\n        if (showDigital[0])\n            dataDigital[0].AddPoint(t, sinf(2*t) > 0.45);\n        if (showDigital[1])\n            dataDigital[1].AddPoint(t, sinf(2*t) < 0.45);\n        //Analog signal values\n        if (showAnalog[0])\n            dataAnalog[0].AddPoint(t, sinf(2*t));\n        if (showAnalog[1])\n            dataAnalog[1].AddPoint(t, cosf(2*t));\n    }\n    if (ImPlot::BeginPlot(\"##Digital\")) {\n        ImPlot::SetupAxisLimits(ImAxis_X1, t - 10.0, t, paused ? ImGuiCond_Once : ImGuiCond_Always);\n        ImPlot::SetupAxisLimits(ImAxis_Y1, -1, 1);\n        for (int i = 0; i < 2; ++i) {\n            if (showDigital[i] && dataDigital[i].Data.size() > 0) {\n                snprintf(label, sizeof(label), \"digital_%d\", i);\n                ImPlot::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), 0, dataDigital[i].Offset, 2 * sizeof(float));\n            }\n        }\n        for (int i = 0; i < 2; ++i) {\n            if (showAnalog[i]) {\n                snprintf(label, sizeof(label), \"analog_%d\", i);\n                if (dataAnalog[i].Data.size() > 0)\n                    ImPlot::PlotLine(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), 0, dataAnalog[i].Offset, 2 * sizeof(float));\n            }\n        }\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_Images() {\n    ImGui::BulletText(\"Below we are displaying the font texture, which is the only texture we have\\naccess to in this demo.\");\n    ImGui::BulletText(\"Use the 'ImTextureID' type as storage to pass pointers or identifiers to your\\nown texture data.\");\n    ImGui::BulletText(\"See ImGui Wiki page 'Image Loading and Displaying Examples'.\");\n    static ImVec2 bmin(0,0);\n    static ImVec2 bmax(1,1);\n    static ImVec2 uv0(0,0);\n    static ImVec2 uv1(1,1);\n    static ImVec4 tint(1,1,1,1);\n    ImGui::SliderFloat2(\"Min\", &bmin.x, -2, 2, \"%.1f\");\n    ImGui::SliderFloat2(\"Max\", &bmax.x, -2, 2, \"%.1f\");\n    ImGui::SliderFloat2(\"UV0\", &uv0.x, -2, 2, \"%.1f\");\n    ImGui::SliderFloat2(\"UV1\", &uv1.x, -2, 2, \"%.1f\");\n    ImGui::ColorEdit4(\"Tint\",&tint.x);\n    if (ImPlot::BeginPlot(\"##image\")) {\n#ifdef IMGUI_HAS_TEXTURES\n        // We use the font atlas ImTextureRef for this demo, but in your real code when you submit\n        // an image that you have loaded yourself, you would normally have a ImTextureID which works\n        // just as well (as ImTextureRef can be constructed from ImTextureID).\n        ImPlot::PlotImage(\"my image\", ImGui::GetIO().Fonts->TexRef, bmin, bmax, uv0, uv1, tint);\n#else\n        ImPlot::PlotImage(\"my image\", ImGui::GetIO().Fonts->TexID, bmin, bmax, uv0, uv1, tint);\n#endif\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_RealtimePlots() {\n    ImGui::BulletText(\"Move your mouse to change the data!\");\n    ImGui::BulletText(\"This example assumes 60 FPS. Higher FPS requires larger buffer size.\");\n    static ScrollingBuffer sdata1, sdata2;\n    static RollingBuffer   rdata1, rdata2;\n    ImVec2 mouse = ImGui::GetMousePos();\n    static float t = 0;\n    t += ImGui::GetIO().DeltaTime;\n    sdata1.AddPoint(t, mouse.x * 0.0005f);\n    rdata1.AddPoint(t, mouse.x * 0.0005f);\n    sdata2.AddPoint(t, mouse.y * 0.0005f);\n    rdata2.AddPoint(t, mouse.y * 0.0005f);\n\n    static float history = 10.0f;\n    ImGui::SliderFloat(\"History\",&history,1,30,\"%.1f s\");\n    rdata1.Span = history;\n    rdata2.Span = history;\n\n    static ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels;\n\n    if (ImPlot::BeginPlot(\"##Scrolling\", ImVec2(-1,150))) {\n        ImPlot::SetupAxes(nullptr, nullptr, flags, flags);\n        ImPlot::SetupAxisLimits(ImAxis_X1,t - history, t, ImGuiCond_Always);\n        ImPlot::SetupAxisLimits(ImAxis_Y1,0,1);\n        ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f);\n        ImPlot::PlotShaded(\"Mouse X\", &sdata1.Data[0].x, &sdata1.Data[0].y, sdata1.Data.size(), -INFINITY, 0, sdata1.Offset, 2 * sizeof(float));\n        ImPlot::PlotLine(\"Mouse Y\", &sdata2.Data[0].x, &sdata2.Data[0].y, sdata2.Data.size(), 0, sdata2.Offset, 2*sizeof(float));\n        ImPlot::EndPlot();\n    }\n    if (ImPlot::BeginPlot(\"##Rolling\", ImVec2(-1,150))) {\n        ImPlot::SetupAxes(nullptr, nullptr, flags, flags);\n        ImPlot::SetupAxisLimits(ImAxis_X1,0,history, ImGuiCond_Always);\n        ImPlot::SetupAxisLimits(ImAxis_Y1,0,1);\n        ImPlot::PlotLine(\"Mouse X\", &rdata1.Data[0].x, &rdata1.Data[0].y, rdata1.Data.size(), 0, 0, 2 * sizeof(float));\n        ImPlot::PlotLine(\"Mouse Y\", &rdata2.Data[0].x, &rdata2.Data[0].y, rdata2.Data.size(), 0, 0, 2 * sizeof(float));\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_MarkersAndText() {\n    static float mk_size = ImPlot::GetStyle().MarkerSize;\n    static float mk_weight = ImPlot::GetStyle().MarkerWeight;\n    ImGui::DragFloat(\"Marker Size\",&mk_size,0.1f,2.0f,10.0f,\"%.2f px\");\n    ImGui::DragFloat(\"Marker Weight\", &mk_weight,0.05f,0.5f,3.0f,\"%.2f px\");\n\n    if (ImPlot::BeginPlot(\"##MarkerStyles\", ImVec2(-1,0), ImPlotFlags_CanvasOnly)) {\n\n        ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);\n        ImPlot::SetupAxesLimits(0, 10, 0, 12);\n\n        ImS8 xs[2] = {1,4};\n        ImS8 ys[2] = {10,11};\n\n        // filled markers\n        for (int m = 0; m < ImPlotMarker_COUNT; ++m) {\n            ImGui::PushID(m);\n            ImPlot::SetNextMarkerStyle(m, mk_size, IMPLOT_AUTO_COL, mk_weight);\n            ImPlot::PlotLine(\"##Filled\", xs, ys, 2);\n            ImGui::PopID();\n            ys[0]--; ys[1]--;\n        }\n        xs[0] = 6; xs[1] = 9; ys[0] = 10; ys[1] = 11;\n        // open markers\n        for (int m = 0; m < ImPlotMarker_COUNT; ++m) {\n            ImGui::PushID(m);\n            ImPlot::SetNextMarkerStyle(m, mk_size, ImVec4(0,0,0,0), mk_weight);\n            ImPlot::PlotLine(\"##Open\", xs, ys, 2);\n            ImGui::PopID();\n            ys[0]--; ys[1]--;\n        }\n\n        ImPlot::PlotText(\"Filled Markers\", 2.5f, 6.0f);\n        ImPlot::PlotText(\"Open Markers\",   7.5f, 6.0f);\n\n        ImPlot::PushStyleColor(ImPlotCol_InlayText, ImVec4(1,0,1,1));\n        ImPlot::PlotText(\"Vertical Text\", 5.0f, 6.0f, ImVec2(0,0), ImPlotTextFlags_Vertical);\n        ImPlot::PopStyleColor();\n\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_NaNValues() {\n\n    static bool include_nan = true;\n    static ImPlotLineFlags flags = 0;\n\n    float data1[5] = {0.0f,0.25f,0.5f,0.75f,1.0f};\n    float data2[5] = {0.0f,0.25f,0.5f,0.75f,1.0f};\n\n    if (include_nan)\n        data1[2] = NAN;\n\n    ImGui::Checkbox(\"Include NaN\",&include_nan);\n    ImGui::SameLine();\n    ImGui::CheckboxFlags(\"Skip NaN\", (unsigned int*)&flags, ImPlotLineFlags_SkipNaN);\n\n    if (ImPlot::BeginPlot(\"##NaNValues\")) {\n        ImPlot::SetNextMarkerStyle(ImPlotMarker_Square);\n        ImPlot::PlotLine(\"line\", data1, data2, 5, flags);\n        ImPlot::PlotBars(\"bars\", data1, 5);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_LogScale() {\n    static double xs[1001], ys1[1001], ys2[1001], ys3[1001];\n    for (int i = 0; i < 1001; ++i) {\n        xs[i]  = i*0.1f;\n        ys1[i] = sin(xs[i]) + 1;\n        ys2[i] = log(xs[i]);\n        ys3[i] = pow(10.0, xs[i]);\n    }\n    if (ImPlot::BeginPlot(\"Log Plot\", ImVec2(-1,0))) {\n        ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Log10);\n        ImPlot::SetupAxesLimits(0.1, 100, 0, 10);\n        ImPlot::PlotLine(\"f(x) = x\",        xs, xs,  1001);\n        ImPlot::PlotLine(\"f(x) = sin(x)+1\", xs, ys1, 1001);\n        ImPlot::PlotLine(\"f(x) = log(x)\",   xs, ys2, 1001);\n        ImPlot::PlotLine(\"f(x) = 10^x\",     xs, ys3, 21);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_SymmetricLogScale() {\n    static double xs[1001], ys1[1001], ys2[1001];\n    for (int i = 0; i < 1001; ++i) {\n        xs[i]  = i*0.1f-50;\n        ys1[i] = sin(xs[i]);\n        ys2[i] = i*0.002 - 1;\n    }\n    if (ImPlot::BeginPlot(\"SymLog Plot\", ImVec2(-1,0))) {\n        ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_SymLog);\n        ImPlot::PlotLine(\"f(x) = a*x+b\",xs,ys2,1001);\n        ImPlot::PlotLine(\"f(x) = sin(x)\",xs,ys1,1001);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_TimeScale() {\n\n    static double t_min = 1609459200; // 01/01/2021 @ 12:00:00am (UTC)\n    static double t_max = 1640995200; // 01/01/2022 @ 12:00:00am (UTC)\n\n    ImGui::BulletText(\"When ImPlotAxisFlags_Time is enabled on the X-Axis, values are interpreted as\\n\"\n                        \"UNIX timestamps in seconds and axis labels are formated as date/time.\");\n    ImGui::BulletText(\"By default, labels are in UTC time but can be set to use local time instead.\");\n\n    ImGui::Checkbox(\"Local Time\",&ImPlot::GetStyle().UseLocalTime);\n    ImGui::SameLine();\n    ImGui::Checkbox(\"ISO 8601\",&ImPlot::GetStyle().UseISO8601);\n    ImGui::SameLine();\n    ImGui::Checkbox(\"24 Hour Clock\",&ImPlot::GetStyle().Use24HourClock);\n\n    static HugeTimeData* data = nullptr;\n    if (data == nullptr) {\n        ImGui::SameLine();\n        if (ImGui::Button(\"Generate Huge Data (~500MB!)\")) {\n            static HugeTimeData sdata(t_min);\n            data = &sdata;\n        }\n    }\n\n    if (ImPlot::BeginPlot(\"##Time\", ImVec2(-1,0))) {\n        ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time);\n        ImPlot::SetupAxesLimits(t_min,t_max,0,1);\n        if (data != nullptr) {\n            // downsample our data\n            int downsample = (int)ImPlot::GetPlotLimits().X.Size() / 1000 + 1;\n            int start = (int)(ImPlot::GetPlotLimits().X.Min - t_min);\n            start = start < 0 ? 0 : start > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : start;\n            int end = (int)(ImPlot::GetPlotLimits().X.Max - t_min) + 1000;\n            end = end < 0 ? 0 : end > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : end;\n            int size = (end - start)/downsample;\n            // plot it\n            ImPlot::PlotLine(\"Time Series\", &data->Ts[start], &data->Ys[start], size, 0, 0, sizeof(double)*downsample);\n        }\n        // plot time now\n        double t_now = (double)time(nullptr);\n        double y_now = HugeTimeData::GetY(t_now);\n        ImPlot::PlotScatter(\"Now\",&t_now,&y_now,1);\n        ImPlot::Annotation(t_now,y_now,ImPlot::GetLastItemColor(),ImVec2(10,10),false,\"Now\");\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nstatic inline double TransformForward_Sqrt(double v, void*) {\n    return sqrt(v);\n}\n\nstatic inline double TransformInverse_Sqrt(double v, void*) {\n    return v*v;\n}\n\nvoid Demo_CustomScale() {\n    static float v[100];\n    for (int i = 0; i < 100; ++i) {\n        v[i] = i*0.01f;\n    }\n    if (ImPlot::BeginPlot(\"Sqrt\")) {\n        ImPlot::SetupAxis(ImAxis_X1, \"Linear\");\n        ImPlot::SetupAxis(ImAxis_Y1, \"Sqrt\");\n        ImPlot::SetupAxisScale(ImAxis_Y1, TransformForward_Sqrt, TransformInverse_Sqrt);\n        ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1, 0, INFINITY);\n        ImPlot::PlotLine(\"##data\",v,v,100);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_MultipleAxes() {\n    static float xs[1001], xs2[1001], ys1[1001], ys2[1001], ys3[1001];\n    for (int i = 0; i < 1001; ++i) {\n        xs[i]  = (i*0.1f);\n        xs2[i] = xs[i] + 10.0f;\n        ys1[i] = sinf(xs[i]) * 3 + 1;\n        ys2[i] = cosf(xs[i]) * 0.2f + 0.5f;\n        ys3[i] = sinf(xs[i]+0.5f) * 100 + 200;\n    }\n\n    static bool x2_axis = true;\n    static bool y2_axis = true;\n    static bool y3_axis = true;\n\n    ImGui::Checkbox(\"X-Axis 2\", &x2_axis);\n    ImGui::SameLine();\n    ImGui::Checkbox(\"Y-Axis 2\", &y2_axis);\n    ImGui::SameLine();\n    ImGui::Checkbox(\"Y-Axis 3\", &y3_axis);\n\n    ImGui::BulletText(\"You can drag axes to the opposite side of the plot.\");\n    ImGui::BulletText(\"Hover over legend items to see which axis they are plotted on.\");\n\n    if (ImPlot::BeginPlot(\"Multi-Axis Plot\", ImVec2(-1,0))) {\n        ImPlot::SetupAxes(\"X-Axis 1\", \"Y-Axis 1\");\n        ImPlot::SetupAxesLimits(0, 100, 0, 10);\n        if (x2_axis) {\n            ImPlot::SetupAxis(ImAxis_X2, \"X-Axis 2\",ImPlotAxisFlags_AuxDefault);\n            ImPlot::SetupAxisLimits(ImAxis_X2, 0, 100);\n        }\n        if (y2_axis) {\n            ImPlot::SetupAxis(ImAxis_Y2, \"Y-Axis 2\",ImPlotAxisFlags_AuxDefault);\n            ImPlot::SetupAxisLimits(ImAxis_Y2, 0, 1);\n        }\n        if (y3_axis) {\n            ImPlot::SetupAxis(ImAxis_Y3, \"Y-Axis 3\",ImPlotAxisFlags_AuxDefault);\n            ImPlot::SetupAxisLimits(ImAxis_Y3, 0, 300);\n        }\n\n        ImPlot::PlotLine(\"f(x) = x\", xs, xs, 1001);\n        if (x2_axis) {\n            ImPlot::SetAxes(ImAxis_X2, ImAxis_Y1);\n            ImPlot::PlotLine(\"f(x) = sin(x)*3+1\", xs2, ys1, 1001);\n        }\n        if (y2_axis) {\n            ImPlot::SetAxes(ImAxis_X1, ImAxis_Y2);\n            ImPlot::PlotLine(\"f(x) = cos(x)*.2+.5\", xs, ys2, 1001);\n        }\n        if (x2_axis && y3_axis) {\n            ImPlot::SetAxes(ImAxis_X2, ImAxis_Y3);\n            ImPlot::PlotLine(\"f(x) = sin(x+.5)*100+200 \", xs2, ys3, 1001);\n        }\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_LinkedAxes() {\n    static ImPlotRect lims(0,1,0,1);\n    static bool linkx = true, linky = true;\n    int data[2] = {0,1};\n    ImGui::Checkbox(\"Link X\", &linkx);\n    ImGui::SameLine();\n    ImGui::Checkbox(\"Link Y\", &linky);\n\n    ImGui::DragScalarN(\"Limits\",ImGuiDataType_Double,&lims.X.Min,4,0.01f);\n\n    if (BeginAlignedPlots(\"AlignedGroup\")) {\n        if (ImPlot::BeginPlot(\"Plot A\")) {\n            ImPlot::SetupAxisLinks(ImAxis_X1, linkx ? &lims.X.Min : nullptr, linkx ? &lims.X.Max : nullptr);\n            ImPlot::SetupAxisLinks(ImAxis_Y1, linky ? &lims.Y.Min : nullptr, linky ? &lims.Y.Max : nullptr);\n            ImPlot::PlotLine(\"Line\",data,2);\n            ImPlot::EndPlot();\n        }\n        if (ImPlot::BeginPlot(\"Plot B\")) {\n            ImPlot::SetupAxisLinks(ImAxis_X1, linkx ? &lims.X.Min : nullptr, linkx ? &lims.X.Max : nullptr);\n            ImPlot::SetupAxisLinks(ImAxis_Y1, linky ? &lims.Y.Min : nullptr, linky ? &lims.Y.Max : nullptr);\n            ImPlot::PlotLine(\"Line\",data,2);\n            ImPlot::EndPlot();\n        }\n        ImPlot::EndAlignedPlots();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_AxisConstraints() {\n    static float constraints[4] = {-10,10,1,20};\n    static ImPlotAxisFlags flags;\n    ImGui::DragFloat2(\"Limits Constraints\", &constraints[0], 0.01f);\n    ImGui::DragFloat2(\"Zoom Constraints\", &constraints[2], 0.01f);\n    CHECKBOX_FLAG(flags, ImPlotAxisFlags_PanStretch);\n    if (ImPlot::BeginPlot(\"##AxisConstraints\",ImVec2(-1,0))) {\n        ImPlot::SetupAxes(\"X\",\"Y\",flags,flags);\n        ImPlot::SetupAxesLimits(-1,1,-1,1);\n        ImPlot::SetupAxisLimitsConstraints(ImAxis_X1,constraints[0], constraints[1]);\n        ImPlot::SetupAxisZoomConstraints(ImAxis_X1,constraints[2], constraints[3]);\n        ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1,constraints[0], constraints[1]);\n        ImPlot::SetupAxisZoomConstraints(ImAxis_Y1,constraints[2], constraints[3]);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_EqualAxes() {\n    ImGui::BulletText(\"Equal constraint applies to axis pairs (e.g ImAxis_X1/Y1, ImAxis_X2/Y2)\");\n    static double xs1[360], ys1[360];\n    for (int i = 0; i < 360; ++i) {\n        double angle = i * 2 * PI / 359.0;\n        xs1[i] = cos(angle); ys1[i] = sin(angle);\n    }\n    float xs2[] = {-1,0,1,0,-1};\n    float ys2[] = {0,1,0,-1,0};\n    if (ImPlot::BeginPlot(\"##EqualAxes\",ImVec2(-1,0),ImPlotFlags_Equal)) {\n        ImPlot::SetupAxis(ImAxis_X2, nullptr, ImPlotAxisFlags_AuxDefault);\n        ImPlot::SetupAxis(ImAxis_Y2, nullptr, ImPlotAxisFlags_AuxDefault);\n        ImPlot::PlotLine(\"Circle\",xs1,ys1,360);\n        ImPlot::SetAxes(ImAxis_X2, ImAxis_Y2);\n        ImPlot::PlotLine(\"Diamond\",xs2,ys2,5);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_AutoFittingData() {\n    ImGui::BulletText(\"The Y-axis has been configured to auto-fit to only the data visible in X-axis range.\");\n    ImGui::BulletText(\"Zoom and pan the X-axis. Disable Stems to see a difference in fit.\");\n    ImGui::BulletText(\"If ImPlotAxisFlags_RangeFit is disabled, the axis will fit ALL data.\");\n\n    static ImPlotAxisFlags xflags = ImPlotAxisFlags_None;\n    static ImPlotAxisFlags yflags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit;\n\n    ImGui::TextUnformatted(\"X: \"); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"ImPlotAxisFlags_AutoFit##X\", (unsigned int*)&xflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"ImPlotAxisFlags_RangeFit##X\", (unsigned int*)&xflags, ImPlotAxisFlags_RangeFit);\n\n    ImGui::TextUnformatted(\"Y: \"); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"ImPlotAxisFlags_AutoFit##Y\", (unsigned int*)&yflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"ImPlotAxisFlags_RangeFit##Y\", (unsigned int*)&yflags, ImPlotAxisFlags_RangeFit);\n\n    static double data[101];\n    srand(0);\n    for (int i = 0; i < 101; ++i)\n        data[i] = 1 + sin(i/10.0f);\n\n    if (ImPlot::BeginPlot(\"##DataFitting\")) {\n        ImPlot::SetupAxes(\"X\",\"Y\",xflags,yflags);\n        ImPlot::PlotLine(\"Line\",data,101);\n        ImPlot::PlotStems(\"Stems\",data,101);\n        ImPlot::EndPlot();\n    };\n}\n\n//-----------------------------------------------------------------------------\n\nImPlotPoint SinewaveGetter(int i, void* data) {\n    float f = *(float*)data;\n    return ImPlotPoint(i,sinf(f*i));\n}\n\nvoid Demo_SubplotsSizing() {\n\n    static ImPlotSubplotFlags flags = ImPlotSubplotFlags_ShareItems|ImPlotSubplotFlags_NoLegend;\n    ImGui::CheckboxFlags(\"ImPlotSubplotFlags_NoResize\", (unsigned int*)&flags, ImPlotSubplotFlags_NoResize);\n    ImGui::CheckboxFlags(\"ImPlotSubplotFlags_NoTitle\", (unsigned int*)&flags, ImPlotSubplotFlags_NoTitle);\n\n    static int rows = 3;\n    static int cols = 3;\n    ImGui::SliderInt(\"Rows\",&rows,1,5);\n    ImGui::SliderInt(\"Cols\",&cols,1,5);\n    if (rows < 1 || cols < 1) {\n        ImGui::TextColored(ImVec4(1,0,0,1), \"Nice try, but the number of rows and columns must be greater than 0!\");\n        return;\n    }\n    static float rratios[] = {5,1,1,1,1,1};\n    static float cratios[] = {5,1,1,1,1,1};\n    ImGui::DragScalarN(\"Row Ratios\",ImGuiDataType_Float,rratios,rows,0.01f,nullptr);\n    ImGui::DragScalarN(\"Col Ratios\",ImGuiDataType_Float,cratios,cols,0.01f,nullptr);\n    if (ImPlot::BeginSubplots(\"My Subplots\", rows, cols, ImVec2(-1,400), flags, rratios, cratios)) {\n        int id = 0;\n        for (int i = 0; i < rows*cols; ++i) {\n            if (ImPlot::BeginPlot(\"\",ImVec2(),ImPlotFlags_NoLegend)) {\n                ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);\n                float fi = 0.01f * (i+1);\n                if (rows*cols > 1) {\n                    ImPlot::SetNextLineStyle(SampleColormap((float)i/(float)(rows*cols-1),ImPlotColormap_Jet));\n                }\n                char label[16];\n                snprintf(label, sizeof(label), \"data%d\", id++);\n                ImPlot::PlotLineG(label,SinewaveGetter,&fi,1000);\n                ImPlot::EndPlot();\n            }\n        }\n        ImPlot::EndSubplots();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_SubplotItemSharing() {\n    static ImPlotSubplotFlags flags = ImPlotSubplotFlags_ShareItems;\n    ImGui::CheckboxFlags(\"ImPlotSubplotFlags_ShareItems\", (unsigned int*)&flags, ImPlotSubplotFlags_ShareItems);\n    ImGui::CheckboxFlags(\"ImPlotSubplotFlags_ColMajor\", (unsigned int*)&flags, ImPlotSubplotFlags_ColMajor);\n    ImGui::BulletText(\"Drag and drop items from the legend onto plots (except for 'common')\");\n    static int rows = 2;\n    static int cols = 3;\n    static int id[] = {0,1,2,3,4,5};\n    static int curj = -1;\n    if (ImPlot::BeginSubplots(\"##ItemSharing\", rows, cols, ImVec2(-1,400), flags)) {\n        ImPlot::SetupLegend(ImPlotLocation_South, ImPlotLegendFlags_Sort|ImPlotLegendFlags_Horizontal);\n        for (int i = 0; i < rows*cols; ++i) {\n            if (ImPlot::BeginPlot(\"\")) {\n                float fc = 0.01f;\n                ImPlot::PlotLineG(\"common\",SinewaveGetter,&fc,1000);\n                for (int j = 0; j < 6; ++j) {\n                    if (id[j] == i) {\n                        char label[8];\n                        float fj = 0.01f * (j+2);\n                        snprintf(label, sizeof(label), \"data%d\", j);\n                        ImPlot::PlotLineG(label,SinewaveGetter,&fj,1000);\n                        if (ImPlot::BeginDragDropSourceItem(label)) {\n                            curj = j;\n                            ImGui::SetDragDropPayload(\"MY_DND\",nullptr,0);\n                            ImPlot::ItemIcon(GetLastItemColor()); ImGui::SameLine();\n                            ImGui::TextUnformatted(label);\n                            ImPlot::EndDragDropSource();\n                        }\n                    }\n                }\n                if (ImPlot::BeginDragDropTargetPlot()) {\n                    if (ImGui::AcceptDragDropPayload(\"MY_DND\"))\n                        id[curj] = i;\n                    ImPlot::EndDragDropTarget();\n                }\n                ImPlot::EndPlot();\n            }\n        }\n        ImPlot::EndSubplots();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_SubplotAxisLinking() {\n    static ImPlotSubplotFlags flags = ImPlotSubplotFlags_LinkRows | ImPlotSubplotFlags_LinkCols;\n    ImGui::CheckboxFlags(\"ImPlotSubplotFlags_LinkRows\", (unsigned int*)&flags, ImPlotSubplotFlags_LinkRows);\n    ImGui::CheckboxFlags(\"ImPlotSubplotFlags_LinkCols\", (unsigned int*)&flags, ImPlotSubplotFlags_LinkCols);\n    ImGui::CheckboxFlags(\"ImPlotSubplotFlags_LinkAllX\", (unsigned int*)&flags, ImPlotSubplotFlags_LinkAllX);\n    ImGui::CheckboxFlags(\"ImPlotSubplotFlags_LinkAllY\", (unsigned int*)&flags, ImPlotSubplotFlags_LinkAllY);\n\n    static int rows = 2;\n    static int cols = 2;\n    if (ImPlot::BeginSubplots(\"##AxisLinking\", rows, cols, ImVec2(-1,400), flags)) {\n        for (int i = 0; i < rows*cols; ++i) {\n            if (ImPlot::BeginPlot(\"\")) {\n                ImPlot::SetupAxesLimits(0,1000,-1,1);\n                float fc = 0.01f;\n                ImPlot::PlotLineG(\"common\",SinewaveGetter,&fc,1000);\n                ImPlot::EndPlot();\n            }\n        }\n        ImPlot::EndSubplots();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_LegendOptions() {\n    static ImPlotLocation loc = ImPlotLocation_East;\n    ImGui::CheckboxFlags(\"North\", (unsigned int*)&loc, ImPlotLocation_North); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"South\", (unsigned int*)&loc, ImPlotLocation_South); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"West\",  (unsigned int*)&loc, ImPlotLocation_West);  ImGui::SameLine();\n    ImGui::CheckboxFlags(\"East\",  (unsigned int*)&loc, ImPlotLocation_East);\n\n    static ImPlotLegendFlags flags = 0;\n\n    CHECKBOX_FLAG(flags, ImPlotLegendFlags_Horizontal);\n    CHECKBOX_FLAG(flags, ImPlotLegendFlags_Outside);\n    CHECKBOX_FLAG(flags, ImPlotLegendFlags_Sort);\n\n    ImGui::SliderFloat2(\"LegendPadding\", (float*)&GetStyle().LegendPadding, 0.0f, 20.0f, \"%.0f\");\n    ImGui::SliderFloat2(\"LegendInnerPadding\", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.0f, \"%.0f\");\n    ImGui::SliderFloat2(\"LegendSpacing\", (float*)&GetStyle().LegendSpacing, 0.0f, 5.0f, \"%.0f\");\n\n    static int num_dummy_items = 25;\n    ImGui::SliderInt(\"Num Dummy Items (Demo Scrolling)\", &num_dummy_items, 0, 100);\n    \n    if (ImPlot::BeginPlot(\"##Legend\",ImVec2(-1,0))) {\n        ImPlot::SetupLegend(loc, flags);\n        static MyImPlot::WaveData data1(0.001, 0.2, 4, 0.2);\n        static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.4);\n        static MyImPlot::WaveData data3(0.001, 0.2, 4, 0.6);\n        static MyImPlot::WaveData data4(0.001, 0.2, 4, 0.8);\n        static MyImPlot::WaveData data5(0.001, 0.2, 4, 1.0);\n\n        ImPlot::PlotLineG(\"Item 002\", MyImPlot::SawWave, &data1, 1000);         // \"Item B\" added to legend\n        ImPlot::PlotLineG(\"Item 001##IDText\", MyImPlot::SawWave, &data2, 1000);  // \"Item A\" added to legend, text after ## used for ID only\n        ImPlot::PlotLineG(\"##NotListed\", MyImPlot::SawWave, &data3, 1000);     // plotted, but not added to legend\n        ImPlot::PlotLineG(\"Item 003\", MyImPlot::SawWave, &data4, 1000);         // \"Item C\" added to legend\n        ImPlot::PlotLineG(\"Item 003\", MyImPlot::SawWave,  &data5, 1000);         // combined with previous \"Item C\"\n\n        for (int i = 0; i < num_dummy_items; ++i) {\n            char label[16];\n            snprintf(label, sizeof(label), \"Item %03d\", i+4);\n            ImPlot::PlotDummy(label);\n        }        \n        ImPlot::EndPlot();\n    }\n} \n    \n//-----------------------------------------------------------------------------\n\nvoid Demo_DragPoints() {\n    ImGui::BulletText(\"Click and drag each point.\");\n    static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None;\n    ImGui::CheckboxFlags(\"NoCursors\", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"NoFit\", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"NoInput\", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs);\n    ImPlotAxisFlags ax_flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks;\n    bool clicked[4] = {false, false, false, false};\n    bool hovered[4] = {false, false, false, false};\n    bool held[4]    = {false, false, false, false};\n    if (ImPlot::BeginPlot(\"##Bezier\",ImVec2(-1,0),ImPlotFlags_CanvasOnly)) {\n        ImPlot::SetupAxes(nullptr,nullptr,ax_flags,ax_flags);\n        ImPlot::SetupAxesLimits(0,1,0,1);\n        static ImPlotPoint P[] = {ImPlotPoint(.05f,.05f), ImPlotPoint(0.2,0.4),  ImPlotPoint(0.8,0.6),  ImPlotPoint(.95f,.95f)};\n\n        ImPlot::DragPoint(0,&P[0].x,&P[0].y, ImVec4(0,0.9f,0,1),4,flags, &clicked[0], &hovered[0], &held[0]);\n        ImPlot::DragPoint(1,&P[1].x,&P[1].y, ImVec4(1,0.5f,1,1),4,flags, &clicked[1], &hovered[1], &held[1]);\n        ImPlot::DragPoint(2,&P[2].x,&P[2].y, ImVec4(0,0.5f,1,1),4,flags, &clicked[2], &hovered[2], &held[2]);\n        ImPlot::DragPoint(3,&P[3].x,&P[3].y, ImVec4(0,0.9f,0,1),4,flags, &clicked[3], &hovered[3], &held[3]);\n\n        static ImPlotPoint B[100];\n        for (int i = 0; i < 100; ++i) {\n            double t  = i / 99.0;\n            double u  = 1 - t;\n            double w1 = u*u*u;\n            double w2 = 3*u*u*t;\n            double w3 = 3*u*t*t;\n            double w4 = t*t*t;\n            B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y);\n        }\n\n        ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1),hovered[1]||held[1] ? 2.0f : 1.0f);\n        ImPlot::PlotLine(\"##h1\",&P[0].x, &P[0].y, 2, 0, 0, sizeof(ImPlotPoint));\n        ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1), hovered[2]||held[2] ? 2.0f : 1.0f);\n        ImPlot::PlotLine(\"##h2\",&P[2].x, &P[2].y, 2, 0, 0, sizeof(ImPlotPoint));\n        ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), hovered[0]||held[0]||hovered[3]||held[3] ? 3.0f : 2.0f);\n        ImPlot::PlotLine(\"##bez\",&B[0].x, &B[0].y, 100, 0, 0, sizeof(ImPlotPoint));\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_DragLines() {\n    ImGui::BulletText(\"Click and drag the horizontal and vertical lines.\");\n    static double x1 = 0.2;\n    static double x2 = 0.8;\n    static double y1 = 0.25;\n    static double y2 = 0.75;\n    static double f = 0.1;\n    bool clicked = false;\n    bool hovered = false;\n    bool held = false;\n    static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None;\n    ImGui::CheckboxFlags(\"NoCursors\", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"NoFit\", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"NoInput\", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs);\n    if (ImPlot::BeginPlot(\"##lines\",ImVec2(-1,0))) {\n        ImPlot::SetupAxesLimits(0,1,0,1);\n        ImPlot::DragLineX(0,&x1,ImVec4(1,1,1,1),1,flags);\n        ImPlot::DragLineX(1,&x2,ImVec4(1,1,1,1),1,flags);\n        ImPlot::DragLineY(2,&y1,ImVec4(1,1,1,1),1,flags);\n        ImPlot::DragLineY(3,&y2,ImVec4(1,1,1,1),1,flags);\n        double xs[1000], ys[1000];\n        for (int i = 0; i < 1000; ++i) {\n            xs[i] = (x2+x1)/2+fabs(x2-x1)*(i/1000.0f - 0.5f);\n            ys[i] = (y1+y2)/2+fabs(y2-y1)/2*sin(f*i/10);\n        }\n        ImPlot::DragLineY(120482,&f,ImVec4(1,0.5f,1,1),1,flags, &clicked, &hovered, &held);\n        ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL, hovered||held ? 2.0f : 1.0f);\n        ImPlot::PlotLine(\"Interactive Data\", xs, ys, 1000);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_DragRects() {\n\n    static float x_data[512];\n    static float y_data1[512];\n    static float y_data2[512];\n    static float y_data3[512];\n    static float sampling_freq = 44100;\n    static float freq = 500;\n    bool clicked = false;\n    bool hovered = false;\n    bool held = false;\n    for (size_t i = 0; i < 512; ++i) {\n        const float t = i / sampling_freq;\n        x_data[i] = t;\n        const float arg = 2 * 3.14f * freq * t;\n        y_data1[i] = sinf(arg);\n        y_data2[i] = y_data1[i] * -0.6f + sinf(2 * arg) * 0.4f;\n        y_data3[i] = y_data2[i] * -0.6f + sinf(3 * arg) * 0.4f;\n    }\n    ImGui::BulletText(\"Click and drag the edges, corners, and center of the rect.\");\n    ImGui::BulletText(\"Double click edges to expand rect to plot extents.\");\n    static ImPlotRect rect(0.0025,0.0045,0,0.5);\n    static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None;\n    ImGui::CheckboxFlags(\"NoCursors\", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"NoFit\", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine();\n    ImGui::CheckboxFlags(\"NoInput\", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs);\n\n    if (ImPlot::BeginPlot(\"##Main\",ImVec2(-1,150))) {\n        ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoTickLabels,ImPlotAxisFlags_NoTickLabels);\n        ImPlot::SetupAxesLimits(0,0.01,-1,1);\n        ImPlot::PlotLine(\"Signal 1\", x_data, y_data1, 512);\n        ImPlot::PlotLine(\"Signal 2\", x_data, y_data2, 512);\n        ImPlot::PlotLine(\"Signal 3\", x_data, y_data3, 512);\n        ImPlot::DragRect(0,&rect.X.Min,&rect.Y.Min,&rect.X.Max,&rect.Y.Max,ImVec4(1,0,1,1),flags, &clicked, &hovered, &held);\n        ImPlot::EndPlot();\n    }\n    ImVec4 bg_col = held ? ImVec4(0.5f,0,0.5f,1) : (hovered ? ImVec4(0.25f,0,0.25f,1) : ImPlot::GetStyle().Colors[ImPlotCol_PlotBg]);\n    ImPlot::PushStyleColor(ImPlotCol_PlotBg, bg_col);\n    if (ImPlot::BeginPlot(\"##rect\",ImVec2(-1,150), ImPlotFlags_CanvasOnly)) {\n        ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);\n        ImPlot::SetupAxesLimits(rect.X.Min, rect.X.Max, rect.Y.Min, rect.Y.Max, ImGuiCond_Always);\n        ImPlot::PlotLine(\"Signal 1\", x_data, y_data1, 512);\n        ImPlot::PlotLine(\"Signal 2\", x_data, y_data2, 512);\n        ImPlot::PlotLine(\"Signal 3\", x_data, y_data3, 512);\n        ImPlot::EndPlot();\n    }\n    ImPlot::PopStyleColor();\n    ImGui::Text(\"Rect is %sclicked, %shovered, %sheld\", clicked ? \"\" : \"not \", hovered ? \"\" : \"not \", held ? \"\" : \"not \");\n}\n\n//-----------------------------------------------------------------------------\n\nImPlotPoint FindCentroid(const ImVector<ImPlotPoint>& data, const ImPlotRect& bounds, int& cnt) {\n    cnt = 0;\n    ImPlotPoint avg;\n    ImPlotRect bounds_fixed;\n    bounds_fixed.X.Min = bounds.X.Min < bounds.X.Max ? bounds.X.Min : bounds.X.Max;\n    bounds_fixed.X.Max = bounds.X.Min < bounds.X.Max ? bounds.X.Max : bounds.X.Min;\n    bounds_fixed.Y.Min = bounds.Y.Min < bounds.Y.Max ? bounds.Y.Min : bounds.Y.Max;\n    bounds_fixed.Y.Max = bounds.Y.Min < bounds.Y.Max ? bounds.Y.Max : bounds.Y.Min;\n    for (int i = 0; i < data.size(); ++i) {\n        if (bounds_fixed.Contains(data[i].x, data[i].y)) {\n            avg.x += data[i].x;\n            avg.y += data[i].y;\n            cnt++;\n        }\n    }\n    if (cnt > 0) {\n        avg.x = avg.x / cnt;\n        avg.y = avg.y / cnt;\n    }\n    return avg;\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_Querying() {\n    static ImVector<ImPlotPoint> data;\n    static ImVector<ImPlotRect> rects;\n    static ImPlotRect limits, select;\n    static bool init = true;\n    if (init) {\n        for (int i = 0; i < 50; ++i)\n        {\n            double x = RandomRange(0.1, 0.9);\n            double y = RandomRange(0.1, 0.9);\n            data.push_back(ImPlotPoint(x,y));\n        }\n        init = false;\n    }\n\n    ImGui::BulletText(\"Box select and left click mouse to create a new query rect.\");\n    ImGui::BulletText(\"Ctrl + click in the plot area to draw points.\");\n\n    if (ImGui::Button(\"Clear Queries\"))\n        rects.shrink(0);\n\n    if (ImPlot::BeginPlot(\"##Centroid\")) {\n        ImPlot::SetupAxesLimits(0,1,0,1);\n        if (ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyCtrl) {\n            ImPlotPoint pt = ImPlot::GetPlotMousePos();\n            data.push_back(pt);\n        }\n        ImPlot::PlotScatter(\"Points\", &data[0].x, &data[0].y, data.size(), 0, 0, 2 * sizeof(double));\n        if (ImPlot::IsPlotSelected()) {\n            select = ImPlot::GetPlotSelection();\n            int cnt;\n            ImPlotPoint centroid = FindCentroid(data,select,cnt);\n            if (cnt > 0) {\n                ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,6);\n                ImPlot::PlotScatter(\"Centroid\", &centroid.x, &centroid.y, 1);\n            }\n            if (ImGui::IsMouseClicked(ImPlot::GetInputMap().SelectCancel)) {\n                CancelPlotSelection();\n                rects.push_back(select);\n            }\n        }\n        for (int i = 0; i < rects.size(); ++i) {\n            int cnt;\n            ImPlotPoint centroid = FindCentroid(data,rects[i],cnt);\n            if (cnt > 0) {\n                ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,6);\n                ImPlot::PlotScatter(\"Centroid\", &centroid.x, &centroid.y, 1);\n            }\n            ImPlot::DragRect(i,&rects[i].X.Min,&rects[i].Y.Min,&rects[i].X.Max,&rects[i].Y.Max,ImVec4(1,0,1,1));\n        }\n        limits  = ImPlot::GetPlotLimits();\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_Annotations() {\n    static bool clamp = false;\n    ImGui::Checkbox(\"Clamp\",&clamp);\n    if (ImPlot::BeginPlot(\"##Annotations\")) {\n        ImPlot::SetupAxesLimits(0,2,0,1);\n        static float p[] = {0.25f, 0.25f, 0.75f, 0.75f, 0.25f};\n        ImPlot::PlotScatter(\"##Points\",&p[0],&p[1],4);\n        ImVec4 col = GetLastItemColor();\n        ImPlot::Annotation(0.25,0.25,col,ImVec2(-15,15),clamp,\"BL\");\n        ImPlot::Annotation(0.75,0.25,col,ImVec2(15,15),clamp,\"BR\");\n        ImPlot::Annotation(0.75,0.75,col,ImVec2(15,-15),clamp,\"TR\");\n        ImPlot::Annotation(0.25,0.75,col,ImVec2(-15,-15),clamp,\"TL\");\n        ImPlot::Annotation(0.5,0.5,col,ImVec2(0,0),clamp,\"Center\");\n\n        ImPlot::Annotation(1.25,0.75,ImVec4(0,1,0,1),ImVec2(0,0),clamp);\n\n        float bx[] = {1.2f,1.5f,1.8f};\n        float by[] = {0.25f, 0.5f, 0.75f};\n        ImPlot::PlotBars(\"##Bars\",bx,by,3,0.2);\n        for (int i = 0; i < 3; ++i)\n            ImPlot::Annotation(bx[i],by[i],ImVec4(0,0,0,0),ImVec2(0,-5),clamp,\"B[%d]=%.2f\",i,by[i]);\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_Tags() {\n    static bool show = true;\n    ImGui::Checkbox(\"Show Tags\",&show);\n    if (ImPlot::BeginPlot(\"##Tags\")) {\n        ImPlot::SetupAxis(ImAxis_X2);\n        ImPlot::SetupAxis(ImAxis_Y2);\n        if (show) {\n            ImPlot::TagX(0.25, ImVec4(1,1,0,1));\n            ImPlot::TagY(0.75, ImVec4(1,1,0,1));\n            static double drag_tag = 0.25;\n            ImPlot::DragLineY(0,&drag_tag,ImVec4(1,0,0,1),1,ImPlotDragToolFlags_NoFit);\n            ImPlot::TagY(drag_tag, ImVec4(1,0,0,1), \"Drag\");\n            SetAxes(ImAxis_X2, ImAxis_Y2);\n            ImPlot::TagX(0.5, ImVec4(0,1,1,1), \"%s\", \"MyTag\");\n            ImPlot::TagY(0.5, ImVec4(0,1,1,1), \"Tag: %d\", 42);\n        }\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_DragAndDrop() {\n    ImGui::BulletText(\"Drag/drop items from the left column.\");\n    ImGui::BulletText(\"Drag/drop items between plots.\");\n    ImGui::Indent();\n    ImGui::BulletText(\"Plot 1 Targets: Plot, Y-Axes, Legend\");\n    ImGui::BulletText(\"Plot 1 Sources: Legend Item Labels\");\n    ImGui::BulletText(\"Plot 2 Targets: Plot, X-Axis, Y-Axis\");\n    ImGui::BulletText(\"Plot 2 Sources: Plot, X-Axis, Y-Axis (hold Ctrl)\");\n    ImGui::Unindent();\n\n    // convenience struct to manage DND items; do this however you like\n    struct MyDndItem {\n        int              Idx;\n        int              Plt;\n        ImAxis           Yax;\n        char             Label[16];\n        ImVector<ImVec2> Data;\n        ImVec4           Color;\n        MyDndItem()        {\n            static int i = 0;\n            Idx = i++;\n            Plt = 0;\n            Yax = ImAxis_Y1;\n            snprintf(Label, sizeof(Label), \"%02d Hz\", Idx+1);\n            Color = RandomColor();\n            Data.reserve(1001);\n            for (int k = 0; k < 1001; ++k) {\n                float t = k * 1.0f / 999;\n                Data.push_back(ImVec2(t, 0.5f + 0.5f * sinf(2*3.14f*t*(Idx+1))));\n            }\n        }\n        void Reset() { Plt = 0; Yax = ImAxis_Y1; }\n    };\n\n    const int         k_dnd = 20;\n    static MyDndItem  dnd[k_dnd];\n    static MyDndItem* dndx = nullptr; // for plot 2\n    static MyDndItem* dndy = nullptr; // for plot 2\n\n    // child window to serve as initial source for our DND items\n    ImGui::BeginChild(\"DND_LEFT\",ImVec2(100,400));\n    if (ImGui::Button(\"Reset Data\")) {\n        for (int k = 0; k < k_dnd; ++k)\n            dnd[k].Reset();\n        dndx = dndy = nullptr;\n    }\n    for (int k = 0; k < k_dnd; ++k) {\n        if (dnd[k].Plt > 0)\n            continue;\n        ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine();\n        ImGui::Selectable(dnd[k].Label, false, 0, ImVec2(100, 0));\n        if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {\n            ImGui::SetDragDropPayload(\"MY_DND\", &k, sizeof(int));\n            ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine();\n            ImGui::TextUnformatted(dnd[k].Label);\n            ImGui::EndDragDropSource();\n        }\n    }\n    ImGui::EndChild();\n    if (ImGui::BeginDragDropTarget()) {\n        if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(\"MY_DND\")) {\n            int i = *(int*)payload->Data; dnd[i].Reset();\n        }\n        ImGui::EndDragDropTarget();\n    }\n\n    ImGui::SameLine();\n    ImGui::BeginChild(\"DND_RIGHT\",ImVec2(-1,400));\n    // plot 1 (time series)\n    ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoHighlight;\n    if (ImPlot::BeginPlot(\"##DND1\", ImVec2(-1,195))) {\n        ImPlot::SetupAxis(ImAxis_X1, nullptr, flags|ImPlotAxisFlags_Lock);\n        ImPlot::SetupAxis(ImAxis_Y1, \"[drop here]\", flags);\n        ImPlot::SetupAxis(ImAxis_Y2, \"[drop here]\", flags|ImPlotAxisFlags_Opposite);\n        ImPlot::SetupAxis(ImAxis_Y3, \"[drop here]\", flags|ImPlotAxisFlags_Opposite);\n\n        for (int k = 0; k < k_dnd; ++k) {\n            if (dnd[k].Plt == 1 && dnd[k].Data.size() > 0) {\n                ImPlot::SetAxis(dnd[k].Yax);\n                ImPlot::SetNextLineStyle(dnd[k].Color);\n                ImPlot::PlotLine(dnd[k].Label, &dnd[k].Data[0].x, &dnd[k].Data[0].y, dnd[k].Data.size(), 0, 0, 2 * sizeof(float));\n                // allow legend item labels to be DND sources\n                if (ImPlot::BeginDragDropSourceItem(dnd[k].Label)) {\n                    ImGui::SetDragDropPayload(\"MY_DND\", &k, sizeof(int));\n                    ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine();\n                    ImGui::TextUnformatted(dnd[k].Label);\n                    ImPlot::EndDragDropSource();\n                }\n            }\n        }\n        // allow the main plot area to be a DND target\n        if (ImPlot::BeginDragDropTargetPlot()) {\n            if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(\"MY_DND\")) {\n                int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = ImAxis_Y1;\n            }\n            ImPlot::EndDragDropTarget();\n        }\n        // allow each y-axis to be a DND target\n        for (int y = ImAxis_Y1; y <= ImAxis_Y3; ++y) {\n            if (ImPlot::BeginDragDropTargetAxis(y)) {\n                if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(\"MY_DND\")) {\n                    int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = y;\n                }\n                ImPlot::EndDragDropTarget();\n            }\n        }\n        // allow the legend to be a DND target\n        if (ImPlot::BeginDragDropTargetLegend()) {\n            if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(\"MY_DND\")) {\n                int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = ImAxis_Y1;\n            }\n            ImPlot::EndDragDropTarget();\n        }\n        ImPlot::EndPlot();\n    }\n    // plot 2 (Lissajous)\n    if (ImPlot::BeginPlot(\"##DND2\", ImVec2(-1,195))) {\n        ImPlot::PushStyleColor(ImPlotCol_AxisBg, dndx != nullptr ? dndx->Color : ImPlot::GetStyle().Colors[ImPlotCol_AxisBg]);\n        ImPlot::SetupAxis(ImAxis_X1, dndx == nullptr ? \"[drop here]\" : dndx->Label, flags);\n        ImPlot::PushStyleColor(ImPlotCol_AxisBg, dndy != nullptr ? dndy->Color : ImPlot::GetStyle().Colors[ImPlotCol_AxisBg]);\n        ImPlot::SetupAxis(ImAxis_Y1, dndy == nullptr ? \"[drop here]\" : dndy->Label, flags);\n        ImPlot::PopStyleColor(2);\n        if (dndx != nullptr && dndy != nullptr) {\n            ImVec4 mixed((dndx->Color.x + dndy->Color.x)/2,(dndx->Color.y + dndy->Color.y)/2,(dndx->Color.z + dndy->Color.z)/2,(dndx->Color.w + dndy->Color.w)/2);\n            ImPlot::SetNextLineStyle(mixed);\n            ImPlot::PlotLine(\"##dndxy\", &dndx->Data[0].y, &dndy->Data[0].y, dndx->Data.size(), 0, 0, 2 * sizeof(float));\n        }\n        // allow the x-axis to be a DND target\n        if (ImPlot::BeginDragDropTargetAxis(ImAxis_X1)) {\n            if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(\"MY_DND\")) {\n                int i = *(int*)payload->Data; dndx = &dnd[i];\n            }\n            ImPlot::EndDragDropTarget();\n        }\n        // allow the x-axis to be a DND source\n        if (dndx != nullptr && ImPlot::BeginDragDropSourceAxis(ImAxis_X1)) {\n            ImGui::SetDragDropPayload(\"MY_DND\", &dndx->Idx, sizeof(int));\n            ImPlot::ItemIcon(dndx->Color); ImGui::SameLine();\n            ImGui::TextUnformatted(dndx->Label);\n            ImPlot::EndDragDropSource();\n        }\n        // allow the y-axis to be a DND target\n        if (ImPlot::BeginDragDropTargetAxis(ImAxis_Y1)) {\n            if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(\"MY_DND\")) {\n                int i = *(int*)payload->Data; dndy = &dnd[i];\n            }\n            ImPlot::EndDragDropTarget();\n        }\n        // allow the y-axis to be a DND source\n        if (dndy != nullptr && ImPlot::BeginDragDropSourceAxis(ImAxis_Y1)) {\n            ImGui::SetDragDropPayload(\"MY_DND\", &dndy->Idx, sizeof(int));\n            ImPlot::ItemIcon(dndy->Color); ImGui::SameLine();\n            ImGui::TextUnformatted(dndy->Label);\n            ImPlot::EndDragDropSource();\n        }\n        // allow the plot area to be a DND target\n        if (ImPlot::BeginDragDropTargetPlot()) {\n            if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(\"MY_DND\")) {\n                int i = *(int*)payload->Data; dndx = dndy = &dnd[i];\n            }\n        }\n        // allow the plot area to be a DND source\n        if (ImPlot::BeginDragDropSourcePlot()) {\n            ImGui::TextUnformatted(\"Yes, you can\\ndrag this!\");\n            ImPlot::EndDragDropSource();\n        }\n        ImPlot::EndPlot();\n    }\n    ImGui::EndChild();\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_Tables() {\n#ifdef IMGUI_HAS_TABLE\n    static ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |\n                                   ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable;\n    static bool anim = true;\n    static int offset = 0;\n    ImGui::BulletText(\"Plots can be used inside of ImGui tables as another means of creating subplots.\");\n    ImGui::Checkbox(\"Animate\",&anim);\n    if (anim)\n        offset = (offset + 1) % 100;\n    if (ImGui::BeginTable(\"##table\", 3, flags, ImVec2(-1,0))) {\n        ImGui::TableSetupColumn(\"Electrode\", ImGuiTableColumnFlags_WidthFixed, 75.0f);\n        ImGui::TableSetupColumn(\"Voltage\", ImGuiTableColumnFlags_WidthFixed, 75.0f);\n        ImGui::TableSetupColumn(\"EMG Signal\");\n        ImGui::TableHeadersRow();\n        ImPlot::PushColormap(ImPlotColormap_Cool);\n        for (int row = 0; row < 10; row++) {\n            ImGui::TableNextRow();\n            static float data[100];\n            srand(row);\n            for (int i = 0; i < 100; ++i)\n                data[i] = RandomRange(0.0f,10.0f);\n            ImGui::TableSetColumnIndex(0);\n            ImGui::Text(\"EMG %d\", row);\n            ImGui::TableSetColumnIndex(1);\n            ImGui::Text(\"%.3f V\", data[offset]);\n            ImGui::TableSetColumnIndex(2);\n            ImGui::PushID(row);\n            MyImPlot::Sparkline(\"##spark\",data,100,0,11.0f,offset,ImPlot::GetColormapColor(row),ImVec2(-1, 35));\n            ImGui::PopID();\n        }\n        ImPlot::PopColormap();\n        ImGui::EndTable();\n    }\n#else\n    ImGui::BulletText(\"You need to merge the ImGui 'tables' branch for this section.\");\n#endif\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_OffsetAndStride() {\n    static const int k_circles    = 11;\n    static const int k_points_per = 50;\n    static const int k_size       = 2 * k_points_per * k_circles;\n    static double interleaved_data[k_size];\n    for (int p = 0; p < k_points_per; ++p) {\n        for (int c = 0; c < k_circles; ++c) {\n            double r = (double)c / (k_circles - 1) * 0.2 + 0.2;\n            interleaved_data[p*2*k_circles + 2*c + 0] = 0.5 + r * cos((double)p/k_points_per * 6.28);\n            interleaved_data[p*2*k_circles + 2*c + 1] = 0.5 + r * sin((double)p/k_points_per * 6.28);\n        }\n    }\n    static int offset = 0;\n    ImGui::BulletText(\"Offsetting is useful for realtime plots (see above) and circular buffers.\");\n    ImGui::BulletText(\"Striding is useful for interleaved data (e.g. audio) or plotting structs.\");\n    ImGui::BulletText(\"Here, all circle data is stored in a single interleaved buffer:\");\n    ImGui::BulletText(\"[c0.x0 c0.y0 ... cn.x0 cn.y0 c0.x1 c0.y1 ... cn.x1 cn.y1 ... cn.xm cn.ym]\");\n    ImGui::BulletText(\"The offset value indicates which circle point index is considered the first.\");\n    ImGui::BulletText(\"Offsets can be negative and/or larger than the actual data count.\");\n    ImGui::SliderInt(\"Offset\", &offset, -2*k_points_per, 2*k_points_per);\n    if (ImPlot::BeginPlot(\"##strideoffset\",ImVec2(-1,0),ImPlotFlags_Equal)) {\n        ImPlot::PushColormap(ImPlotColormap_Jet);\n        char buff[32];\n        for (int c = 0; c < k_circles; ++c) {\n            snprintf(buff, sizeof(buff), \"Circle %d\", c);\n            ImPlot::PlotLine(buff, &interleaved_data[c*2 + 0], &interleaved_data[c*2 + 1], k_points_per, 0, offset, 2*k_circles*sizeof(double));\n        }\n        ImPlot::EndPlot();\n        ImPlot::PopColormap();\n    }\n    // offset++; uncomment for animation!\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_CustomDataAndGetters() {\n    ImGui::BulletText(\"You can plot custom structs using the stride feature.\");\n    ImGui::BulletText(\"Most plotters can also be passed a function pointer for getting data.\");\n    ImGui::Indent();\n        ImGui::BulletText(\"You can optionally pass user data to be given to your getter function.\");\n        ImGui::BulletText(\"C++ lambdas can be passed as function pointers as well!\");\n    ImGui::Unindent();\n\n    MyImPlot::Vector2f vec2_data[2] = { MyImPlot::Vector2f(0,0), MyImPlot::Vector2f(1,1) };\n\n    if (ImPlot::BeginPlot(\"##Custom Data\")) {\n\n        // custom structs using stride example:\n        ImPlot::PlotLine(\"Vector2f\", &vec2_data[0].x, &vec2_data[0].y, 2, 0, 0, sizeof(MyImPlot::Vector2f) /* or sizeof(float) * 2 */);\n\n        // custom getter example 1:\n        ImPlot::PlotLineG(\"Spiral\", MyImPlot::Spiral, nullptr, 1000);\n\n        // custom getter example 2:\n        static MyImPlot::WaveData data1(0.001, 0.2, 2, 0.75);\n        static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.25);\n        ImPlot::PlotLineG(\"Waves\", MyImPlot::SineWave, &data1, 1000);\n        ImPlot::PlotLineG(\"Waves\", MyImPlot::SawWave, &data2, 1000);\n        ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f);\n        ImPlot::PlotShadedG(\"Waves\", MyImPlot::SineWave, &data1, MyImPlot::SawWave, &data2, 1000);\n        ImPlot::PopStyleVar();\n\n        // you can also pass C++ lambdas:\n        // auto lamda = [](void* data, int idx) { ... return ImPlotPoint(x,y); };\n        // ImPlot::PlotLine(\"My Lambda\", lambda, data, 1000);\n\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nint MetricFormatter(double value, char* buff, int size, void* data) {\n    const char* unit = (const char*)data;\n    static double v[]      = {1000000000,1000000,1000,1,0.001,0.000001,0.000000001};\n    static const char* p[] = {\"G\",\"M\",\"k\",\"\",\"m\",\"u\",\"n\"};\n    if (value == 0) {\n        return snprintf(buff,size,\"0 %s\", unit);\n    }\n    for (int i = 0; i < 7; ++i) {\n        if (fabs(value) >= v[i]) {\n            return snprintf(buff,size,\"%g %s%s\",value/v[i],p[i],unit);\n        }\n    }\n    return snprintf(buff,size,\"%g %s%s\",value/v[6],p[6],unit);\n}\n\nvoid Demo_TickLabels()  {\n    static bool custom_fmt    = true;\n    static bool custom_ticks  = false;\n    static bool custom_labels = true;\n    ImGui::Checkbox(\"Show Custom Format\", &custom_fmt);\n    ImGui::SameLine();\n    ImGui::Checkbox(\"Show Custom Ticks\", &custom_ticks);\n    if (custom_ticks) {\n        ImGui::SameLine();\n        ImGui::Checkbox(\"Show Custom Labels\", &custom_labels);\n    }\n    const double pi = 3.14;\n    const char* pi_str[] = {\"PI\"};\n    static double yticks[] = {100,300,700,900};\n    static const char*  ylabels[] = {\"One\",\"Three\",\"Seven\",\"Nine\"};\n    static double yticks_aux[] = {0.2,0.4,0.6};\n    static const char* ylabels_aux[] = {\"A\",\"B\",\"C\",\"D\",\"E\",\"F\"};\n    if (ImPlot::BeginPlot(\"##Ticks\")) {\n        ImPlot::SetupAxesLimits(2.5,5,0,1000);\n        ImPlot::SetupAxis(ImAxis_Y2, nullptr, ImPlotAxisFlags_AuxDefault);\n        ImPlot::SetupAxis(ImAxis_Y3, nullptr, ImPlotAxisFlags_AuxDefault);\n        if (custom_fmt) {\n            ImPlot::SetupAxisFormat(ImAxis_X1, \"%g ms\");\n            ImPlot::SetupAxisFormat(ImAxis_Y1, MetricFormatter, (void*)\"Hz\");\n            ImPlot::SetupAxisFormat(ImAxis_Y2, \"%g dB\");\n            ImPlot::SetupAxisFormat(ImAxis_Y3, MetricFormatter, (void*)\"m\");\n        }\n        if (custom_ticks) {\n            ImPlot::SetupAxisTicks(ImAxis_X1, &pi,1,custom_labels ? pi_str : nullptr, true);\n            ImPlot::SetupAxisTicks(ImAxis_Y1, yticks, 4, custom_labels ? ylabels : nullptr, false);\n            ImPlot::SetupAxisTicks(ImAxis_Y2, yticks_aux, 3, custom_labels ? ylabels_aux : nullptr, false);\n            ImPlot::SetupAxisTicks(ImAxis_Y3, 0, 1, 6, custom_labels ? ylabels_aux : nullptr, false);\n        }\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_CustomStyles() {\n    ImPlot::PushColormap(ImPlotColormap_Deep);\n    // normally you wouldn't change the entire style each frame\n    ImPlotStyle backup = ImPlot::GetStyle();\n    MyImPlot::StyleSeaborn();\n    if (ImPlot::BeginPlot(\"seaborn style\")) {\n        ImPlot::SetupAxes( \"x-axis\", \"y-axis\");\n        ImPlot::SetupAxesLimits(-0.5f, 9.5f, 0, 10);\n        unsigned int lin[10] = {8,8,9,7,8,8,8,9,7,8};\n        unsigned int bar[10] = {1,2,5,3,4,1,2,5,3,4};\n        unsigned int dot[10] = {7,6,6,7,8,5,6,5,8,7};\n        ImPlot::PlotBars(\"Bars\", bar, 10, 0.5f);\n        ImPlot::PlotLine(\"Line\", lin, 10);\n        ImPlot::NextColormapColor(); // skip green\n        ImPlot::PlotScatter(\"Scatter\", dot, 10);\n        ImPlot::EndPlot();\n    }\n    ImPlot::GetStyle() = backup;\n    ImPlot::PopColormap();\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_CustomRendering() {\n    if (ImPlot::BeginPlot(\"##CustomRend\")) {\n        ImVec2 cntr = ImPlot::PlotToPixels(ImPlotPoint(0.5f,  0.5f));\n        ImVec2 rmin = ImPlot::PlotToPixels(ImPlotPoint(0.25f, 0.75f));\n        ImVec2 rmax = ImPlot::PlotToPixels(ImPlotPoint(0.75f, 0.25f));\n        ImPlot::PushPlotClipRect();\n        ImPlot::GetPlotDrawList()->AddCircleFilled(cntr,20,IM_COL32(255,255,0,255),20);\n        ImPlot::GetPlotDrawList()->AddRect(rmin, rmax, IM_COL32(128,0,255,255));\n        ImPlot::PopPlotClipRect();\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_LegendPopups() {\n    ImGui::BulletText(\"You can implement legend context menus to inject per-item controls and widgets.\");\n    ImGui::BulletText(\"Right click the legend label/icon to edit custom item attributes.\");\n\n    static float  frequency = 0.1f;\n    static float  amplitude = 0.5f;\n    static ImVec4 color     = ImVec4(1,1,0,1);\n    static float  alpha     = 1.0f;\n    static bool   line      = false;\n    static float  thickness = 1;\n    static bool   markers   = false;\n    static bool   shaded    = false;\n\n    static float vals[101];\n    for (int i = 0; i < 101; ++i)\n        vals[i] = amplitude * sinf(frequency * i);\n\n    if (ImPlot::BeginPlot(\"Right Click the Legend\")) {\n        ImPlot::SetupAxesLimits(0,100,-1,1);\n        // rendering logic\n        ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha);\n        if (!line) {\n            ImPlot::SetNextFillStyle(color);\n            ImPlot::PlotBars(\"Right Click Me\", vals, 101);\n        }\n        else {\n            if (markers) ImPlot::SetNextMarkerStyle(ImPlotMarker_Square);\n            ImPlot::SetNextLineStyle(color, thickness);\n            ImPlot::PlotLine(\"Right Click Me\", vals, 101);\n            if (shaded) ImPlot::PlotShaded(\"Right Click Me\",vals,101);\n        }\n        ImPlot::PopStyleVar();\n        // custom legend context menu\n        if (ImPlot::BeginLegendPopup(\"Right Click Me\")) {\n            ImGui::SliderFloat(\"Frequency\",&frequency,0,1,\"%0.2f\");\n            ImGui::SliderFloat(\"Amplitude\",&amplitude,0,1,\"%0.2f\");\n            ImGui::Separator();\n            ImGui::ColorEdit3(\"Color\",&color.x);\n            ImGui::SliderFloat(\"Transparency\",&alpha,0,1,\"%.2f\");\n            ImGui::Checkbox(\"Line Plot\", &line);\n            if (line) {\n                ImGui::SliderFloat(\"Thickness\", &thickness, 0, 5);\n                ImGui::Checkbox(\"Markers\", &markers);\n                ImGui::Checkbox(\"Shaded\",&shaded);\n            }\n            ImPlot::EndLegendPopup();\n        }\n        ImPlot::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_ColormapWidgets() {\n    static int cmap = ImPlotColormap_Viridis;\n\n    if (ImPlot::ColormapButton(\"Button\",ImVec2(0,0),cmap)) {\n        cmap = (cmap + 1) % ImPlot::GetColormapCount();\n    }\n\n    static float t = 0.5f;\n    static ImVec4 col;\n    ImGui::ColorButton(\"##Display\",col,ImGuiColorEditFlags_NoInputs);\n    ImGui::SameLine();\n    ImPlot::ColormapSlider(\"Slider\", &t, &col, \"%.3f\", cmap);\n\n    ImPlot::ColormapIcon(cmap); ImGui::SameLine(); ImGui::Text(\"Icon\");\n\n    static ImPlotColormapScaleFlags flags = 0;\n    static float scale[2] = {0, 100};\n    ImPlot::ColormapScale(\"Scale\",scale[0],scale[1],ImVec2(0,0),\"%g dB\",flags,cmap);\n    ImGui::InputFloat2(\"Scale\",scale);\n    CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_NoLabel);\n    CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_Opposite);\n    CHECKBOX_FLAG(flags, ImPlotColormapScaleFlags_Invert);\n}\n\n//-----------------------------------------------------------------------------\n\nvoid Demo_CustomPlottersAndTooltips()  {\n    ImGui::BulletText(\"You can create custom plotters or extend ImPlot using implot_internal.h.\");\n    double dates[]  = {1546300800,1546387200,1546473600,1546560000,1546819200,1546905600,1546992000,1547078400,1547164800,1547424000,1547510400,1547596800,1547683200,1547769600,1547942400,1548028800,1548115200,1548201600,1548288000,1548374400,1548633600,1548720000,1548806400,1548892800,1548979200,1549238400,1549324800,1549411200,1549497600,1549584000,1549843200,1549929600,1550016000,1550102400,1550188800,1550361600,1550448000,1550534400,1550620800,1550707200,1550793600,1551052800,1551139200,1551225600,1551312000,1551398400,1551657600,1551744000,1551830400,1551916800,1552003200,1552262400,1552348800,1552435200,1552521600,1552608000,1552867200,1552953600,1553040000,1553126400,1553212800,1553472000,1553558400,1553644800,1553731200,1553817600,1554076800,1554163200,1554249600,1554336000,1554422400,1554681600,1554768000,1554854400,1554940800,1555027200,1555286400,1555372800,1555459200,1555545600,1555632000,1555891200,1555977600,1556064000,1556150400,1556236800,1556496000,1556582400,1556668800,1556755200,1556841600,1557100800,1557187200,1557273600,1557360000,1557446400,1557705600,1557792000,1557878400,1557964800,1558051200,1558310400,1558396800,1558483200,1558569600,1558656000,1558828800,1558915200,1559001600,1559088000,1559174400,1559260800,1559520000,1559606400,1559692800,1559779200,1559865600,1560124800,1560211200,1560297600,1560384000,1560470400,1560729600,1560816000,1560902400,1560988800,1561075200,1561334400,1561420800,1561507200,1561593600,1561680000,1561939200,1562025600,1562112000,1562198400,1562284800,1562544000,1562630400,1562716800,1562803200,1562889600,1563148800,1563235200,1563321600,1563408000,1563494400,1563753600,1563840000,1563926400,1564012800,1564099200,1564358400,1564444800,1564531200,1564617600,1564704000,1564963200,1565049600,1565136000,1565222400,1565308800,1565568000,1565654400,1565740800,1565827200,1565913600,1566172800,1566259200,1566345600,1566432000,1566518400,1566777600,1566864000,1566950400,1567036800,1567123200,1567296000,1567382400,1567468800,1567555200,1567641600,1567728000,1567987200,1568073600,1568160000,1568246400,1568332800,1568592000,1568678400,1568764800,1568851200,1568937600,1569196800,1569283200,1569369600,1569456000,1569542400,1569801600,1569888000,1569974400,1570060800,1570147200,1570406400,1570492800,1570579200,1570665600,1570752000,1571011200,1571097600,1571184000,1571270400,1571356800,1571616000,1571702400,1571788800,1571875200,1571961600};\n    double opens[]  = {1284.7,1319.9,1318.7,1328,1317.6,1321.6,1314.3,1325,1319.3,1323.1,1324.7,1321.3,1323.5,1322,1281.3,1281.95,1311.1,1315,1314,1313.1,1331.9,1334.2,1341.3,1350.6,1349.8,1346.4,1343.4,1344.9,1335.6,1337.9,1342.5,1337,1338.6,1337,1340.4,1324.65,1324.35,1349.5,1371.3,1367.9,1351.3,1357.8,1356.1,1356,1347.6,1339.1,1320.6,1311.8,1314,1312.4,1312.3,1323.5,1319.1,1327.2,1332.1,1320.3,1323.1,1328,1330.9,1338,1333,1335.3,1345.2,1341.1,1332.5,1314,1314.4,1310.7,1314,1313.1,1315,1313.7,1320,1326.5,1329.2,1314.2,1312.3,1309.5,1297.4,1293.7,1277.9,1295.8,1295.2,1290.3,1294.2,1298,1306.4,1299.8,1302.3,1297,1289.6,1302,1300.7,1303.5,1300.5,1303.2,1306,1318.7,1315,1314.5,1304.1,1294.7,1293.7,1291.2,1290.2,1300.4,1284.2,1284.25,1301.8,1295.9,1296.2,1304.4,1323.1,1340.9,1341,1348,1351.4,1351.4,1343.5,1342.3,1349,1357.6,1357.1,1354.7,1361.4,1375.2,1403.5,1414.7,1433.2,1438,1423.6,1424.4,1418,1399.5,1435.5,1421.25,1434.1,1412.4,1409.8,1412.2,1433.4,1418.4,1429,1428.8,1420.6,1441,1460.4,1441.7,1438.4,1431,1439.3,1427.4,1431.9,1439.5,1443.7,1425.6,1457.5,1451.2,1481.1,1486.7,1512.1,1515.9,1509.2,1522.3,1513,1526.6,1533.9,1523,1506.3,1518.4,1512.4,1508.8,1545.4,1537.3,1551.8,1549.4,1536.9,1535.25,1537.95,1535.2,1556,1561.4,1525.6,1516.4,1507,1493.9,1504.9,1506.5,1513.1,1506.5,1509.7,1502,1506.8,1521.5,1529.8,1539.8,1510.9,1511.8,1501.7,1478,1485.4,1505.6,1511.6,1518.6,1498.7,1510.9,1510.8,1498.3,1492,1497.7,1484.8,1494.2,1495.6,1495.6,1487.5,1491.1,1495.1,1506.4};\n    double highs[]  = {1284.75,1320.6,1327,1330.8,1326.8,1321.6,1326,1328,1325.8,1327.1,1326,1326,1323.5,1322.1,1282.7,1282.95,1315.8,1316.3,1314,1333.2,1334.7,1341.7,1353.2,1354.6,1352.2,1346.4,1345.7,1344.9,1340.7,1344.2,1342.7,1342.1,1345.2,1342,1350,1324.95,1330.75,1369.6,1374.3,1368.4,1359.8,1359,1357,1356,1353.4,1340.6,1322.3,1314.1,1316.1,1312.9,1325.7,1323.5,1326.3,1336,1332.1,1330.1,1330.4,1334.7,1341.1,1344.2,1338.8,1348.4,1345.6,1342.8,1334.7,1322.3,1319.3,1314.7,1316.6,1316.4,1315,1325.4,1328.3,1332.2,1329.2,1316.9,1312.3,1309.5,1299.6,1296.9,1277.9,1299.5,1296.2,1298.4,1302.5,1308.7,1306.4,1305.9,1307,1297.2,1301.7,1305,1305.3,1310.2,1307,1308,1319.8,1321.7,1318.7,1316.2,1305.9,1295.8,1293.8,1293.7,1304.2,1302,1285.15,1286.85,1304,1302,1305.2,1323,1344.1,1345.2,1360.1,1355.3,1363.8,1353,1344.7,1353.6,1358,1373.6,1358.2,1369.6,1377.6,1408.9,1425.5,1435.9,1453.7,1438,1426,1439.1,1418,1435,1452.6,1426.65,1437.5,1421.5,1414.1,1433.3,1441.3,1431.4,1433.9,1432.4,1440.8,1462.3,1467,1443.5,1444,1442.9,1447,1437.6,1440.8,1445.7,1447.8,1458.2,1461.9,1481.8,1486.8,1522.7,1521.3,1521.1,1531.5,1546.1,1534.9,1537.7,1538.6,1523.6,1518.8,1518.4,1514.6,1540.3,1565,1554.5,1556.6,1559.8,1541.9,1542.9,1540.05,1558.9,1566.2,1561.9,1536.2,1523.8,1509.1,1506.2,1532.2,1516.6,1519.7,1515,1519.5,1512.1,1524.5,1534.4,1543.3,1543.3,1542.8,1519.5,1507.2,1493.5,1511.4,1525.8,1522.2,1518.8,1515.3,1518,1522.3,1508,1501.5,1503,1495.5,1501.1,1497.9,1498.7,1492.1,1499.4,1506.9,1520.9};\n    double lows[]   = {1282.85,1315,1318.7,1309.6,1317.6,1312.9,1312.4,1319.1,1319,1321,1318.1,1321.3,1319.9,1312,1280.5,1276.15,1308,1309.9,1308.5,1312.3,1329.3,1333.1,1340.2,1347,1345.9,1338,1340.8,1335,1332,1337.9,1333,1336.8,1333.2,1329.9,1340.4,1323.85,1324.05,1349,1366.3,1351.2,1349.1,1352.4,1350.7,1344.3,1338.9,1316.3,1308.4,1306.9,1309.6,1306.7,1312.3,1315.4,1319,1327.2,1317.2,1320,1323,1328,1323,1327.8,1331.7,1335.3,1336.6,1331.8,1311.4,1310,1309.5,1308,1310.6,1302.8,1306.6,1313.7,1320,1322.8,1311,1312.1,1303.6,1293.9,1293.5,1291,1277.9,1294.1,1286,1289.1,1293.5,1296.9,1298,1299.6,1292.9,1285.1,1288.5,1296.3,1297.2,1298.4,1298.6,1302,1300.3,1312,1310.8,1301.9,1292,1291.1,1286.3,1289.2,1289.9,1297.4,1283.65,1283.25,1292.9,1295.9,1290.8,1304.2,1322.7,1336.1,1341,1343.5,1345.8,1340.3,1335.1,1341.5,1347.6,1352.8,1348.2,1353.7,1356.5,1373.3,1398,1414.7,1427,1416.4,1412.7,1420.1,1396.4,1398.8,1426.6,1412.85,1400.7,1406,1399.8,1404.4,1415.5,1417.2,1421.9,1415,1413.7,1428.1,1434,1435.7,1427.5,1429.4,1423.9,1425.6,1427.5,1434.8,1422.3,1412.1,1442.5,1448.8,1468.2,1484.3,1501.6,1506.2,1498.6,1488.9,1504.5,1518.3,1513.9,1503.3,1503,1506.5,1502.1,1503,1534.8,1535.3,1541.4,1528.6,1525.6,1535.25,1528.15,1528,1542.6,1514.3,1510.7,1505.5,1492.1,1492.9,1496.8,1493.1,1503.4,1500.9,1490.7,1496.3,1505.3,1505.3,1517.9,1507.4,1507.1,1493.3,1470.5,1465,1480.5,1501.7,1501.4,1493.3,1492.1,1505.1,1495.7,1478,1487.1,1480.8,1480.6,1487,1488.3,1484.8,1484,1490.7,1490.4,1503.1};\n    double closes[] = {1283.35,1315.3,1326.1,1317.4,1321.5,1317.4,1323.5,1319.2,1321.3,1323.3,1319.7,1325.1,1323.6,1313.8,1282.05,1279.05,1314.2,1315.2,1310.8,1329.1,1334.5,1340.2,1340.5,1350,1347.1,1344.3,1344.6,1339.7,1339.4,1343.7,1337,1338.9,1340.1,1338.7,1346.8,1324.25,1329.55,1369.6,1372.5,1352.4,1357.6,1354.2,1353.4,1346,1341,1323.8,1311.9,1309.1,1312.2,1310.7,1324.3,1315.7,1322.4,1333.8,1319.4,1327.1,1325.8,1330.9,1325.8,1331.6,1336.5,1346.7,1339.2,1334.7,1313.3,1316.5,1312.4,1313.4,1313.3,1312.2,1313.7,1319.9,1326.3,1331.9,1311.3,1313.4,1309.4,1295.2,1294.7,1294.1,1277.9,1295.8,1291.2,1297.4,1297.7,1306.8,1299.4,1303.6,1302.2,1289.9,1299.2,1301.8,1303.6,1299.5,1303.2,1305.3,1319.5,1313.6,1315.1,1303.5,1293,1294.6,1290.4,1291.4,1302.7,1301,1284.15,1284.95,1294.3,1297.9,1304.1,1322.6,1339.3,1340.1,1344.9,1354,1357.4,1340.7,1342.7,1348.2,1355.1,1355.9,1354.2,1362.1,1360.1,1408.3,1411.2,1429.5,1430.1,1426.8,1423.4,1425.1,1400.8,1419.8,1432.9,1423.55,1412.1,1412.2,1412.8,1424.9,1419.3,1424.8,1426.1,1423.6,1435.9,1440.8,1439.4,1439.7,1434.5,1436.5,1427.5,1432.2,1433.3,1441.8,1437.8,1432.4,1457.5,1476.5,1484.2,1519.6,1509.5,1508.5,1517.2,1514.1,1527.8,1531.2,1523.6,1511.6,1515.7,1515.7,1508.5,1537.6,1537.2,1551.8,1549.1,1536.9,1529.4,1538.05,1535.15,1555.9,1560.4,1525.5,1515.5,1511.1,1499.2,1503.2,1507.4,1499.5,1511.5,1513.4,1515.8,1506.2,1515.1,1531.5,1540.2,1512.3,1515.2,1506.4,1472.9,1489,1507.9,1513.8,1512.9,1504.4,1503.9,1512.8,1500.9,1488.7,1497.6,1483.5,1494,1498.3,1494.1,1488.1,1487.5,1495.7,1504.7,1505.3};\n    static bool tooltip = true;\n    ImGui::Checkbox(\"Show Tooltip\", &tooltip);\n    ImGui::SameLine();\n    static ImVec4 bullCol = ImVec4(0.000f, 1.000f, 0.441f, 1.000f);\n    static ImVec4 bearCol = ImVec4(0.853f, 0.050f, 0.310f, 1.000f);\n    ImGui::SameLine(); ImGui::ColorEdit4(\"##Bull\", &bullCol.x, ImGuiColorEditFlags_NoInputs);\n    ImGui::SameLine(); ImGui::ColorEdit4(\"##Bear\", &bearCol.x, ImGuiColorEditFlags_NoInputs);\n    ImPlot::GetStyle().UseLocalTime = false;\n\n    if (ImPlot::BeginPlot(\"Candlestick Chart\",ImVec2(-1,0))) {\n        ImPlot::SetupAxes(nullptr,nullptr,0,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit);\n        ImPlot::SetupAxesLimits(1546300800, 1571961600, 1250, 1600);\n        ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time);\n        ImPlot::SetupAxisLimitsConstraints(ImAxis_X1, 1546300800, 1571961600);\n        ImPlot::SetupAxisZoomConstraints(ImAxis_X1, 60*60*24*14, 1571961600-1546300800);\n        ImPlot::SetupAxisFormat(ImAxis_Y1, \"$%.0f\");\n        MyImPlot::PlotCandlestick(\"GOOGL\",dates, opens, closes, lows, highs, 218, tooltip, 0.25f, bullCol, bearCol);\n        ImPlot::EndPlot();\n    }\n    }\n\n//-----------------------------------------------------------------------------\n// DEMO WINDOW\n//-----------------------------------------------------------------------------\n\nvoid DemoHeader(const char* label, void(*demo)()) {\n    if (ImGui::TreeNodeEx(label)) {\n        demo();\n        ImGui::TreePop();\n    }\n}\n\nvoid ShowDemoWindow(bool* p_open) {\n    static bool show_implot_metrics      = false;\n    static bool show_implot_style_editor = false;\n    static bool show_imgui_metrics       = false;\n    static bool show_imgui_style_editor  = false;\n    static bool show_imgui_demo          = false;\n\n    if (show_implot_metrics) {\n        ImPlot::ShowMetricsWindow(&show_implot_metrics);\n    }\n    if (show_implot_style_editor) {\n        ImGui::SetNextWindowSize(ImVec2(415,762), ImGuiCond_Appearing);\n        ImGui::Begin(\"Style Editor (ImPlot)\", &show_implot_style_editor);\n        ImPlot::ShowStyleEditor();\n        ImGui::End();\n    }\n    if (show_imgui_style_editor) {\n        ImGui::Begin(\"Style Editor (ImGui)\", &show_imgui_style_editor);\n        ImGui::ShowStyleEditor();\n        ImGui::End();\n    }\n    if (show_imgui_metrics) {\n        ImGui::ShowMetricsWindow(&show_imgui_metrics);\n    }\n    if (show_imgui_demo) {\n        ImGui::ShowDemoWindow(&show_imgui_demo);\n    }\n    ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver);\n    ImGui::SetNextWindowSize(ImVec2(600, 750), ImGuiCond_FirstUseEver);\n    ImGui::Begin(\"ImPlot Demo\", p_open, ImGuiWindowFlags_MenuBar);\n    if (ImGui::BeginMenuBar()) {\n        if (ImGui::BeginMenu(\"Tools\")) {\n            ImGui::MenuItem(\"Metrics\",      nullptr, &show_implot_metrics);\n            ImGui::MenuItem(\"Style Editor\", nullptr, &show_implot_style_editor);\n            ImGui::Separator();\n            ImGui::MenuItem(\"ImGui Metrics\",       nullptr, &show_imgui_metrics);\n            ImGui::MenuItem(\"ImGui Style Editor\",  nullptr, &show_imgui_style_editor);\n            ImGui::MenuItem(\"ImGui Demo\",          nullptr, &show_imgui_demo);\n            ImGui::EndMenu();\n        }\n        ImGui::EndMenuBar();\n    }\n    //-------------------------------------------------------------------------\n    ImGui::Text(\"ImPlot says hello. (%s)\", IMPLOT_VERSION);\n    // display warning about 16-bit indices\n    static bool showWarning = sizeof(ImDrawIdx)*8 == 16 && (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) == false;\n    if (showWarning) {\n        ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1));\n        ImGui::TextWrapped(\"WARNING: ImDrawIdx is 16-bit and ImGuiBackendFlags_RendererHasVtxOffset is false. Expect visual glitches and artifacts! See README for more information.\");\n        ImGui::PopStyleColor();\n    }\n\n    ImGui::Spacing();\n\n    if (ImGui::BeginTabBar(\"ImPlotDemoTabs\")) {\n        if (ImGui::BeginTabItem(\"Plots\")) {\n            DemoHeader(\"Line Plots\", Demo_LinePlots);\n            DemoHeader(\"Filled Line Plots\", Demo_FilledLinePlots);\n            DemoHeader(\"Shaded Plots##\", Demo_ShadedPlots);\n            DemoHeader(\"Scatter Plots\", Demo_ScatterPlots);\n            DemoHeader(\"Realtime Plots\", Demo_RealtimePlots);\n            DemoHeader(\"Stairstep Plots\", Demo_StairstepPlots);\n            DemoHeader(\"Bar Plots\", Demo_BarPlots);\n            DemoHeader(\"Bar Groups\", Demo_BarGroups);\n            DemoHeader(\"Bar Stacks\", Demo_BarStacks);\n            DemoHeader(\"Error Bars\", Demo_ErrorBars);\n            DemoHeader(\"Stem Plots##\", Demo_StemPlots);\n            DemoHeader(\"Infinite Lines\", Demo_InfiniteLines);\n            DemoHeader(\"Pie Charts\", Demo_PieCharts);\n            DemoHeader(\"Heatmaps\", Demo_Heatmaps);\n            DemoHeader(\"Histogram\", Demo_Histogram);\n            DemoHeader(\"Histogram 2D\", Demo_Histogram2D);\n            DemoHeader(\"Digital Plots\", Demo_DigitalPlots);\n            DemoHeader(\"Images\", Demo_Images);\n            DemoHeader(\"Markers and Text\", Demo_MarkersAndText);\n            DemoHeader(\"NaN Values\", Demo_NaNValues);\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"Subplots\")) {\n            DemoHeader(\"Sizing\", Demo_SubplotsSizing);\n            DemoHeader(\"Item Sharing\", Demo_SubplotItemSharing);\n            DemoHeader(\"Axis Linking\", Demo_SubplotAxisLinking);\n            DemoHeader(\"Tables\", Demo_Tables);\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"Axes\")) {\n            DemoHeader(\"Log Scale\", Demo_LogScale);\n            DemoHeader(\"Symmetric Log Scale\", Demo_SymmetricLogScale);\n            DemoHeader(\"Time Scale\", Demo_TimeScale);\n            DemoHeader(\"Custom Scale\", Demo_CustomScale);\n            DemoHeader(\"Multiple Axes\", Demo_MultipleAxes);\n            DemoHeader(\"Tick Labels\", Demo_TickLabels);\n            DemoHeader(\"Linked Axes\", Demo_LinkedAxes);\n            DemoHeader(\"Axis Constraints\", Demo_AxisConstraints);\n            DemoHeader(\"Equal Axes\", Demo_EqualAxes);\n            DemoHeader(\"Auto-Fitting Data\", Demo_AutoFittingData);\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"Tools\")) {\n            DemoHeader(\"Offset and Stride\", Demo_OffsetAndStride);\n            DemoHeader(\"Drag Points\", Demo_DragPoints);\n            DemoHeader(\"Drag Lines\", Demo_DragLines);\n            DemoHeader(\"Drag Rects\", Demo_DragRects);\n            DemoHeader(\"Querying\", Demo_Querying);\n            DemoHeader(\"Annotations\", Demo_Annotations);\n            DemoHeader(\"Tags\", Demo_Tags);\n            DemoHeader(\"Drag and Drop\", Demo_DragAndDrop);\n            DemoHeader(\"Legend Options\", Demo_LegendOptions);\n            DemoHeader(\"Legend Popups\", Demo_LegendPopups);\n            DemoHeader(\"Colormap Widgets\", Demo_ColormapWidgets);\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"Custom\")) {\n            DemoHeader(\"Custom Styles\", Demo_CustomStyles);\n            DemoHeader(\"Custom Data and Getters\", Demo_CustomDataAndGetters);\n            DemoHeader(\"Custom Rendering\", Demo_CustomRendering);\n            DemoHeader(\"Custom Plotters and Tooltips\", Demo_CustomPlottersAndTooltips);\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"Config\")) {\n            Demo_Config();\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"Help\")) {\n            Demo_Help();\n            ImGui::EndTabItem();\n        }\n        ImGui::EndTabBar();\n    }\n    ImGui::End();\n}\n\n} // namespace ImPlot\n\nnamespace MyImPlot {\n\nImPlotPoint SineWave(int idx, void* data) {\n    WaveData* wd = (WaveData*)data;\n    double x = idx * wd->X;\n    return ImPlotPoint(x, wd->Offset + wd->Amp * sin(2 * 3.14 * wd->Freq * x));\n}\n\nImPlotPoint SawWave(int idx, void* data) {\n    WaveData* wd = (WaveData*)data;\n    double x = idx * wd->X;\n    return ImPlotPoint(x, wd->Offset + wd->Amp * (-2 / 3.14 * atan(cos(3.14 * wd->Freq * x) / sin(3.14 * wd->Freq * x))));\n}\n\nImPlotPoint Spiral(int idx, void*) {\n    float r = 0.9f;            // outer radius\n    float a = 0;               // inner radius\n    float b = 0.05f;           // increment per rev\n    float n = (r - a) / b;     // number  of revolutions\n    double th = 2 * n * 3.14;  // angle\n    float Th = float(th * idx / (1000 - 1));\n    return ImPlotPoint(0.5f+(a + b*Th / (2.0f * (float) 3.14))*cos(Th),\n                       0.5f + (a + b*Th / (2.0f * (float)3.14))*sin(Th));\n}\n\nvoid Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size) {\n    ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0,0));\n    if (ImPlot::BeginPlot(id,size,ImPlotFlags_CanvasOnly)) {\n        ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);\n        ImPlot::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always);\n        ImPlot::SetNextLineStyle(col);\n        ImPlot::SetNextFillStyle(col, 0.25);\n        ImPlot::PlotLine(id, values, count, 1, 0, ImPlotLineFlags_Shaded, offset);\n        ImPlot::EndPlot();\n    }\n    ImPlot::PopStyleVar();\n}\n\nvoid StyleSeaborn() {\n\n    ImPlotStyle& style              = ImPlot::GetStyle();\n\n    ImVec4* colors                  = style.Colors;\n    colors[ImPlotCol_Line]          = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_Fill]          = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_MarkerFill]    = IMPLOT_AUTO_COL;\n    colors[ImPlotCol_ErrorBar]      = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlotCol_FrameBg]       = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlotCol_PlotBg]        = ImVec4(0.92f, 0.92f, 0.95f, 1.00f);\n    colors[ImPlotCol_PlotBorder]    = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImPlotCol_LegendBg]      = ImVec4(0.92f, 0.92f, 0.95f, 1.00f);\n    colors[ImPlotCol_LegendBorder]  = ImVec4(0.80f, 0.81f, 0.85f, 1.00f);\n    colors[ImPlotCol_LegendText]    = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlotCol_TitleText]     = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlotCol_InlayText]     = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlotCol_AxisText]      = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlotCol_AxisGrid]      = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlotCol_AxisBgHovered]   = ImVec4(0.92f, 0.92f, 0.95f, 1.00f);\n    colors[ImPlotCol_AxisBgActive]    = ImVec4(0.92f, 0.92f, 0.95f, 0.75f);\n    colors[ImPlotCol_Selection]     = ImVec4(1.00f, 0.65f, 0.00f, 1.00f);\n    colors[ImPlotCol_Crosshairs]    = ImVec4(0.23f, 0.10f, 0.64f, 0.50f);\n\n    style.LineWeight       = 1.5;\n    style.Marker           = ImPlotMarker_None;\n    style.MarkerSize       = 4;\n    style.MarkerWeight     = 1;\n    style.FillAlpha        = 1.0f;\n    style.ErrorBarSize     = 5;\n    style.ErrorBarWeight   = 1.5f;\n    style.DigitalBitHeight = 8;\n    style.DigitalBitGap    = 4;\n    style.PlotBorderSize   = 0;\n    style.MinorAlpha       = 1.0f;\n    style.MajorTickLen     = ImVec2(0,0);\n    style.MinorTickLen     = ImVec2(0,0);\n    style.MajorTickSize    = ImVec2(0,0);\n    style.MinorTickSize    = ImVec2(0,0);\n    style.MajorGridSize    = ImVec2(1.2f,1.2f);\n    style.MinorGridSize    = ImVec2(1.2f,1.2f);\n    style.PlotPadding      = ImVec2(12,12);\n    style.LabelPadding     = ImVec2(5,5);\n    style.LegendPadding    = ImVec2(5,5);\n    style.MousePosPadding  = ImVec2(5,5);\n    style.PlotMinSize      = ImVec2(300,225);\n}\n\n} // namespaece MyImPlot\n\n// WARNING:\n//\n// You can use \"implot_internal.h\" to build custom plotting fuctions or extend ImPlot.\n// However, note that forward compatibility of this file is not guaranteed and the\n// internal API is subject to change. At some point we hope to bring more of this\n// into the public API and expose the necessary building blocks to fully support\n// custom plotters. For now, proceed at your own risk!\n\n#include \"implot_internal.h\"\n\nnamespace MyImPlot {\n\ntemplate <typename T>\nint BinarySearch(const T* arr, int l, int r, T x) {\n    if (r >= l) {\n        int mid = l + (r - l) / 2;\n        if (arr[mid] == x)\n            return mid;\n        if (arr[mid] > x)\n            return BinarySearch(arr, l, mid - 1, x);\n        return BinarySearch(arr, mid + 1, r, x);\n    }\n    return -1;\n}\n\nvoid PlotCandlestick(const char* label_id, const double* xs, const double* opens, const double* closes, const double* lows, const double* highs, int count, bool tooltip, float width_percent, ImVec4 bullCol, ImVec4 bearCol) {\n\n    // get ImGui window DrawList\n    ImDrawList* draw_list = ImPlot::GetPlotDrawList();\n    // calc real value width\n    double half_width = count > 1 ? (xs[1] - xs[0]) * width_percent : width_percent;\n\n    // custom tool\n    if (ImPlot::IsPlotHovered() && tooltip) {\n        ImPlotPoint mouse   = ImPlot::GetPlotMousePos();\n        mouse.x             = ImPlot::RoundTime(ImPlotTime::FromDouble(mouse.x), ImPlotTimeUnit_Day).ToDouble();\n        float  tool_l       = ImPlot::PlotToPixels(mouse.x - half_width * 1.5, mouse.y).x;\n        float  tool_r       = ImPlot::PlotToPixels(mouse.x + half_width * 1.5, mouse.y).x;\n        float  tool_t       = ImPlot::GetPlotPos().y;\n        float  tool_b       = tool_t + ImPlot::GetPlotSize().y;\n        ImPlot::PushPlotClipRect();\n        draw_list->AddRectFilled(ImVec2(tool_l, tool_t), ImVec2(tool_r, tool_b), IM_COL32(128,128,128,64));\n        ImPlot::PopPlotClipRect();\n        // find mouse location index\n        int idx = BinarySearch(xs, 0, count - 1, mouse.x);\n        // render tool tip (won't be affected by plot clip rect)\n        if (idx != -1) {\n            ImGui::BeginTooltip();\n            char buff[32];\n            ImPlot::FormatDate(ImPlotTime::FromDouble(xs[idx]),buff,32,ImPlotDateFmt_DayMoYr,ImPlot::GetStyle().UseISO8601);\n            ImGui::Text(\"Day:   %s\",  buff);\n            ImGui::Text(\"Open:  $%.2f\", opens[idx]);\n            ImGui::Text(\"Close: $%.2f\", closes[idx]);\n            ImGui::Text(\"Low:   $%.2f\", lows[idx]);\n            ImGui::Text(\"High:  $%.2f\", highs[idx]);\n            ImGui::EndTooltip();\n        }\n    }\n\n    // begin plot item\n    if (ImPlot::BeginItem(label_id)) {\n        // override legend icon color\n        ImPlot::GetCurrentItem()->Color = IM_COL32(64,64,64,255);\n        // fit data if requested\n        if (ImPlot::FitThisFrame()) {\n            for (int i = 0; i < count; ++i) {\n                ImPlot::FitPoint(ImPlotPoint(xs[i], lows[i]));\n                ImPlot::FitPoint(ImPlotPoint(xs[i], highs[i]));\n            }\n        }\n        // render data\n        for (int i = 0; i < count; ++i) {\n            ImVec2 open_pos  = ImPlot::PlotToPixels(xs[i] - half_width, opens[i]);\n            ImVec2 close_pos = ImPlot::PlotToPixels(xs[i] + half_width, closes[i]);\n            ImVec2 low_pos   = ImPlot::PlotToPixels(xs[i], lows[i]);\n            ImVec2 high_pos  = ImPlot::PlotToPixels(xs[i], highs[i]);\n            ImU32 color      = ImGui::GetColorU32(opens[i] > closes[i] ? bearCol : bullCol);\n            draw_list->AddLine(low_pos, high_pos, color);\n            draw_list->AddRectFilled(open_pos, close_pos, color);\n        }\n\n        // end plot item\n        ImPlot::EndItem();\n    }\n}\n\n} // namespace MyImplot\n"
  },
  {
    "path": "lib/third_party/imgui/implot/source/implot_items.cpp",
    "content": "// MIT License\n\n// Copyright (c) 2023 Evan Pezent\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n// ImPlot v0.17\n\n#define IMGUI_DEFINE_MATH_OPERATORS\n#include \"implot.h\"\n#include \"implot_internal.h\"\n\n//-----------------------------------------------------------------------------\n// [SECTION] Macros and Defines\n//-----------------------------------------------------------------------------\n\n#define SQRT_1_2 0.70710678118f\n#define SQRT_3_2 0.86602540378f\n\n#ifndef IMPLOT_NO_FORCE_INLINE\n    #ifdef _MSC_VER\n        #define IMPLOT_INLINE __forceinline\n    #elif defined(__GNUC__)\n        #define IMPLOT_INLINE inline __attribute__((__always_inline__))\n    #elif defined(__CLANG__)\n        #if __has_attribute(__always_inline__)\n            #define IMPLOT_INLINE inline __attribute__((__always_inline__))\n        #else\n            #define IMPLOT_INLINE inline\n        #endif\n    #else\n        #define IMPLOT_INLINE inline\n    #endif\n#else\n    #define IMPLOT_INLINE inline\n#endif\n\n#if defined __SSE__ || defined __x86_64__ || defined _M_X64\n#ifndef IMGUI_ENABLE_SSE\n#include <immintrin.h>\n#endif\nstatic IMPLOT_INLINE float  ImInvSqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); }\n#else\nstatic IMPLOT_INLINE float  ImInvSqrt(float x) { return 1.0f / sqrtf(x); }\n#endif\n\n#define IMPLOT_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImInvSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0)\n\n// Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean \"all corners\" but in order to support older versions we are more explicit.\n#if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll)\n#define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Template instantiation utility\n//-----------------------------------------------------------------------------\n\n// By default, templates are instantiated for `float`, `double`, and for the following integer types, which are defined in imgui.h:\n//     signed char         ImS8;   // 8-bit signed integer\n//     unsigned char       ImU8;   // 8-bit unsigned integer\n//     signed short        ImS16;  // 16-bit signed integer\n//     unsigned short      ImU16;  // 16-bit unsigned integer\n//     signed int          ImS32;  // 32-bit signed integer == int\n//     unsigned int        ImU32;  // 32-bit unsigned integer\n//     signed   long long  ImS64;  // 64-bit signed integer\n//     unsigned long long  ImU64;  // 64-bit unsigned integer\n// (note: this list does *not* include `long`, `unsigned long` and `long double`)\n//\n// You can customize the supported types by defining IMPLOT_CUSTOM_NUMERIC_TYPES at compile time to define your own type list.\n//    As an example, you could use the compile time define given by the line below in order to support only float and double.\n//        -DIMPLOT_CUSTOM_NUMERIC_TYPES=\"(float)(double)\"\n//    In order to support all known C++ types, use:\n//        -DIMPLOT_CUSTOM_NUMERIC_TYPES=\"(signed char)(unsigned char)(signed short)(unsigned short)(signed int)(unsigned int)(signed long)(unsigned long)(signed long long)(unsigned long long)(float)(double)(long double)\"\n\n#ifdef IMPLOT_CUSTOM_NUMERIC_TYPES\n    #define IMPLOT_NUMERIC_TYPES IMPLOT_CUSTOM_NUMERIC_TYPES\n#else\n    #define IMPLOT_NUMERIC_TYPES (ImS8)(ImU8)(ImS16)(ImU16)(ImS32)(ImU32)(ImS64)(ImU64)(float)(double)\n#endif\n\n// CALL_INSTANTIATE_FOR_NUMERIC_TYPES will duplicate the template instantion code `INSTANTIATE_MACRO(T)` on supported types.\n#define _CAT(x, y) _CAT_(x, y)\n#define _CAT_(x,y) x ## y\n#define _INSTANTIATE_FOR_NUMERIC_TYPES(chain) _CAT(_INSTANTIATE_FOR_NUMERIC_TYPES_1 chain, _END)\n#define _INSTANTIATE_FOR_NUMERIC_TYPES_1(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_2\n#define _INSTANTIATE_FOR_NUMERIC_TYPES_2(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_1\n#define _INSTANTIATE_FOR_NUMERIC_TYPES_1_END\n#define _INSTANTIATE_FOR_NUMERIC_TYPES_2_END\n#define CALL_INSTANTIATE_FOR_NUMERIC_TYPES() _INSTANTIATE_FOR_NUMERIC_TYPES(IMPLOT_NUMERIC_TYPES)\n\nnamespace ImPlot {\n\n//-----------------------------------------------------------------------------\n// [SECTION] Utils\n//-----------------------------------------------------------------------------\n\n// Calc maximum index size of ImDrawIdx\ntemplate <typename T>\nstruct MaxIdx { static const unsigned int Value; };\ntemplate <> const unsigned int MaxIdx<unsigned short>::Value = 65535;\ntemplate <> const unsigned int MaxIdx<unsigned int>::Value   = 4294967295;\n\nIMPLOT_INLINE void GetLineRenderProps(const ImDrawList& draw_list, float& half_weight, ImVec2& tex_uv0, ImVec2& tex_uv1) {\n    const bool aa = ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLines) &&\n                    ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLinesUseTex);\n    if (aa) {\n        ImVec4 tex_uvs = draw_list._Data->TexUvLines[(int)(half_weight*2)];\n        tex_uv0 = ImVec2(tex_uvs.x, tex_uvs.y);\n        tex_uv1 = ImVec2(tex_uvs.z, tex_uvs.w);\n        half_weight += 1;\n    }\n    else {\n        tex_uv0 = tex_uv1 = draw_list._Data->TexUvWhitePixel;\n    }\n}\n\nIMPLOT_INLINE void PrimLine(ImDrawList& draw_list, const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, const ImVec2& tex_uv0, const ImVec2 tex_uv1) {\n    float dx = P2.x - P1.x;\n    float dy = P2.y - P1.y;\n    IMPLOT_NORMALIZE2F_OVER_ZERO(dx, dy);\n    dx *= half_weight;\n    dy *= half_weight;\n    draw_list._VtxWritePtr[0].pos.x = P1.x + dy;\n    draw_list._VtxWritePtr[0].pos.y = P1.y - dx;\n    draw_list._VtxWritePtr[0].uv    = tex_uv0;\n    draw_list._VtxWritePtr[0].col   = col;\n    draw_list._VtxWritePtr[1].pos.x = P2.x + dy;\n    draw_list._VtxWritePtr[1].pos.y = P2.y - dx;\n    draw_list._VtxWritePtr[1].uv    = tex_uv0;\n    draw_list._VtxWritePtr[1].col   = col;\n    draw_list._VtxWritePtr[2].pos.x = P2.x - dy;\n    draw_list._VtxWritePtr[2].pos.y = P2.y + dx;\n    draw_list._VtxWritePtr[2].uv    = tex_uv1;\n    draw_list._VtxWritePtr[2].col   = col;\n    draw_list._VtxWritePtr[3].pos.x = P1.x - dy;\n    draw_list._VtxWritePtr[3].pos.y = P1.y + dx;\n    draw_list._VtxWritePtr[3].uv    = tex_uv1;\n    draw_list._VtxWritePtr[3].col   = col;\n    draw_list._VtxWritePtr += 4;\n    draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx);\n    draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1);\n    draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2);\n    draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx);\n    draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2);\n    draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3);\n    draw_list._IdxWritePtr += 6;\n    draw_list._VtxCurrentIdx += 4;\n}\n\nIMPLOT_INLINE void PrimRectFill(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, ImU32 col, const ImVec2& uv) {\n    draw_list._VtxWritePtr[0].pos   = Pmin;\n    draw_list._VtxWritePtr[0].uv    = uv;\n    draw_list._VtxWritePtr[0].col   = col;\n    draw_list._VtxWritePtr[1].pos   = Pmax;\n    draw_list._VtxWritePtr[1].uv    = uv;\n    draw_list._VtxWritePtr[1].col   = col;\n    draw_list._VtxWritePtr[2].pos.x = Pmin.x;\n    draw_list._VtxWritePtr[2].pos.y = Pmax.y;\n    draw_list._VtxWritePtr[2].uv    = uv;\n    draw_list._VtxWritePtr[2].col   = col;\n    draw_list._VtxWritePtr[3].pos.x = Pmax.x;\n    draw_list._VtxWritePtr[3].pos.y = Pmin.y;\n    draw_list._VtxWritePtr[3].uv    = uv;\n    draw_list._VtxWritePtr[3].col   = col;\n    draw_list._VtxWritePtr += 4;\n    draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx);\n    draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1);\n    draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2);\n    draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx);\n    draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1);\n    draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3);\n    draw_list._IdxWritePtr += 6;\n    draw_list._VtxCurrentIdx += 4;\n}\n\nIMPLOT_INLINE void PrimRectLine(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, float weight, ImU32 col, const ImVec2& uv) {\n\n    draw_list._VtxWritePtr[0].pos.x = Pmin.x;\n    draw_list._VtxWritePtr[0].pos.y = Pmin.y;\n    draw_list._VtxWritePtr[0].uv    = uv;\n    draw_list._VtxWritePtr[0].col   = col;\n\n    draw_list._VtxWritePtr[1].pos.x = Pmin.x;\n    draw_list._VtxWritePtr[1].pos.y = Pmax.y;\n    draw_list._VtxWritePtr[1].uv    = uv;\n    draw_list._VtxWritePtr[1].col   = col;\n\n    draw_list._VtxWritePtr[2].pos.x = Pmax.x;\n    draw_list._VtxWritePtr[2].pos.y = Pmax.y;\n    draw_list._VtxWritePtr[2].uv    = uv;\n    draw_list._VtxWritePtr[2].col   = col;\n\n    draw_list._VtxWritePtr[3].pos.x = Pmax.x;\n    draw_list._VtxWritePtr[3].pos.y = Pmin.y;\n    draw_list._VtxWritePtr[3].uv    = uv;\n    draw_list._VtxWritePtr[3].col   = col;\n\n    draw_list._VtxWritePtr[4].pos.x = Pmin.x + weight;\n    draw_list._VtxWritePtr[4].pos.y = Pmin.y + weight;\n    draw_list._VtxWritePtr[4].uv    = uv;\n    draw_list._VtxWritePtr[4].col   = col;\n\n    draw_list._VtxWritePtr[5].pos.x = Pmin.x + weight;\n    draw_list._VtxWritePtr[5].pos.y = Pmax.y - weight;\n    draw_list._VtxWritePtr[5].uv    = uv;\n    draw_list._VtxWritePtr[5].col   = col;\n\n    draw_list._VtxWritePtr[6].pos.x = Pmax.x - weight;\n    draw_list._VtxWritePtr[6].pos.y = Pmax.y - weight;\n    draw_list._VtxWritePtr[6].uv    = uv;\n    draw_list._VtxWritePtr[6].col   = col;\n\n    draw_list._VtxWritePtr[7].pos.x = Pmax.x - weight;\n    draw_list._VtxWritePtr[7].pos.y = Pmin.y + weight;\n    draw_list._VtxWritePtr[7].uv    = uv;\n    draw_list._VtxWritePtr[7].col   = col;\n\n    draw_list._VtxWritePtr += 8;\n\n    draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0);\n    draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1);\n    draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5);\n    draw_list._IdxWritePtr += 3;\n\n    draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0);\n    draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5);\n    draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4);\n    draw_list._IdxWritePtr += 3;\n\n    draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1);\n    draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2);\n    draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6);\n    draw_list._IdxWritePtr += 3;\n\n    draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1);\n    draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6);\n    draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5);\n    draw_list._IdxWritePtr += 3;\n\n    draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2);\n    draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3);\n    draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7);\n    draw_list._IdxWritePtr += 3;\n\n    draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2);\n    draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7);\n    draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6);\n    draw_list._IdxWritePtr += 3;\n\n    draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3);\n    draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0);\n    draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4);\n    draw_list._IdxWritePtr += 3;\n\n    draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3);\n    draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4);\n    draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7);\n    draw_list._IdxWritePtr += 3;\n\n    draw_list._VtxCurrentIdx += 8;\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] Item Utils\n//-----------------------------------------------------------------------------\n\nImPlotItem* RegisterOrGetItem(const char* label_id, ImPlotItemFlags flags, bool* just_created) {\n    ImPlotContext& gp = *GImPlot;\n    ImPlotItemGroup& Items = *gp.CurrentItems;\n    ImGuiID id = Items.GetItemID(label_id);\n    if (just_created != nullptr)\n        *just_created = Items.GetItem(id) == nullptr;\n    ImPlotItem* item = Items.GetOrAddItem(id);\n    if (item->SeenThisFrame)\n        return item;\n    item->SeenThisFrame = true;\n    int idx = Items.GetItemIndex(item);\n    item->ID = id;\n    if (!ImHasFlag(flags, ImPlotItemFlags_NoLegend) && ImGui::FindRenderedTextEnd(label_id, nullptr) != label_id) {\n        Items.Legend.Indices.push_back(idx);\n        item->NameOffset = Items.Legend.Labels.size();\n        Items.Legend.Labels.append(label_id, label_id + strlen(label_id) + 1);\n    }\n    else {\n        item->Show = true;\n    }\n    return item;\n}\n\nImPlotItem* GetItem(const char* label_id) {\n    ImPlotContext& gp = *GImPlot;\n    return gp.CurrentItems->GetItem(label_id);\n}\n\nbool IsItemHidden(const char* label_id) {\n    ImPlotItem* item = GetItem(label_id);\n    return item != nullptr && !item->Show;\n}\n\nImPlotItem* GetCurrentItem() {\n    ImPlotContext& gp = *GImPlot;\n    return gp.CurrentItem;\n}\n\nvoid SetNextLineStyle(const ImVec4& col, float weight) {\n    ImPlotContext& gp = *GImPlot;\n    gp.NextItemData.Colors[ImPlotCol_Line] = col;\n    gp.NextItemData.LineWeight             = weight;\n}\n\nvoid SetNextFillStyle(const ImVec4& col, float alpha) {\n    ImPlotContext& gp = *GImPlot;\n    gp.NextItemData.Colors[ImPlotCol_Fill] = col;\n    gp.NextItemData.FillAlpha              = alpha;\n}\n\nvoid SetNextMarkerStyle(ImPlotMarker marker, float size, const ImVec4& fill, float weight, const ImVec4& outline) {\n    ImPlotContext& gp = *GImPlot;\n    gp.NextItemData.Marker                          = marker;\n    gp.NextItemData.Colors[ImPlotCol_MarkerFill]    = fill;\n    gp.NextItemData.MarkerSize                      = size;\n    gp.NextItemData.Colors[ImPlotCol_MarkerOutline] = outline;\n    gp.NextItemData.MarkerWeight                    = weight;\n}\n\nvoid SetNextErrorBarStyle(const ImVec4& col, float size, float weight) {\n    ImPlotContext& gp = *GImPlot;\n    gp.NextItemData.Colors[ImPlotCol_ErrorBar] = col;\n    gp.NextItemData.ErrorBarSize               = size;\n    gp.NextItemData.ErrorBarWeight             = weight;\n}\n\nImVec4 GetLastItemColor() {\n    ImPlotContext& gp = *GImPlot;\n    if (gp.PreviousItem)\n        return ImGui::ColorConvertU32ToFloat4(gp.PreviousItem->Color);\n    return ImVec4();\n}\n\nvoid BustItemCache() {\n    ImPlotContext& gp = *GImPlot;\n    for (int p = 0; p < gp.Plots.GetBufSize(); ++p) {\n        ImPlotPlot& plot = *gp.Plots.GetByIndex(p);\n        plot.Items.Reset();\n    }\n    for (int p = 0; p < gp.Subplots.GetBufSize(); ++p) {\n        ImPlotSubplot& subplot = *gp.Subplots.GetByIndex(p);\n        subplot.Items.Reset();\n    }\n}\n\nvoid BustColorCache(const char* plot_title_id) {\n    ImPlotContext& gp = *GImPlot;\n    if (plot_title_id == nullptr) {\n        BustItemCache();\n    }\n    else {\n        ImGuiID id = ImGui::GetCurrentWindow()->GetID(plot_title_id);\n        ImPlotPlot* plot = gp.Plots.GetByKey(id);\n        if (plot != nullptr)\n            plot->Items.Reset();\n        else {\n            ImPlotSubplot* subplot = gp.Subplots.GetByKey(id);\n            if (subplot != nullptr)\n                subplot->Items.Reset();\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] BeginItem / EndItem\n//-----------------------------------------------------------------------------\n\nstatic const float ITEM_HIGHLIGHT_LINE_SCALE = 2.0f;\nstatic const float ITEM_HIGHLIGHT_MARK_SCALE = 1.25f;\n\n// Begins a new item. Returns false if the item should not be plotted.\nbool BeginItem(const char* label_id, ImPlotItemFlags flags, ImPlotCol recolor_from) {\n    ImPlotContext& gp = *GImPlot;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PlotX() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    bool just_created;\n    ImPlotItem* item = RegisterOrGetItem(label_id, flags, &just_created);\n    // set current item\n    gp.CurrentItem = item;\n    ImPlotNextItemData& s = gp.NextItemData;\n    // set/override item color\n    if (recolor_from != -1) {\n        if (!IsColorAuto(s.Colors[recolor_from]))\n            item->Color = ImGui::ColorConvertFloat4ToU32(s.Colors[recolor_from]);\n        else if (!IsColorAuto(gp.Style.Colors[recolor_from]))\n            item->Color = ImGui::ColorConvertFloat4ToU32(gp.Style.Colors[recolor_from]);\n        else if (just_created)\n            item->Color = NextColormapColorU32();\n    }\n    else if (just_created) {\n        item->Color = NextColormapColorU32();\n    }\n    // hide/show item\n    if (gp.NextItemData.HasHidden) {\n        if (just_created || gp.NextItemData.HiddenCond == ImGuiCond_Always)\n            item->Show = !gp.NextItemData.Hidden;\n    }\n    if (!item->Show) {\n        // reset next item data\n        gp.NextItemData.Reset();\n        gp.PreviousItem = item;\n        gp.CurrentItem  = nullptr;\n        return false;\n    }\n    else {\n        ImVec4 item_color = ImGui::ColorConvertU32ToFloat4(item->Color);\n        // stage next item colors\n        s.Colors[ImPlotCol_Line]           = IsColorAuto(s.Colors[ImPlotCol_Line])          ? ( IsColorAuto(ImPlotCol_Line)           ? item_color                 : gp.Style.Colors[ImPlotCol_Line]          ) : s.Colors[ImPlotCol_Line];\n        s.Colors[ImPlotCol_Fill]           = IsColorAuto(s.Colors[ImPlotCol_Fill])          ? ( IsColorAuto(ImPlotCol_Fill)           ? item_color                 : gp.Style.Colors[ImPlotCol_Fill]          ) : s.Colors[ImPlotCol_Fill];\n        s.Colors[ImPlotCol_MarkerOutline]  = IsColorAuto(s.Colors[ImPlotCol_MarkerOutline]) ? ( IsColorAuto(ImPlotCol_MarkerOutline)  ? s.Colors[ImPlotCol_Line]   : gp.Style.Colors[ImPlotCol_MarkerOutline] ) : s.Colors[ImPlotCol_MarkerOutline];\n        s.Colors[ImPlotCol_MarkerFill]     = IsColorAuto(s.Colors[ImPlotCol_MarkerFill])    ? ( IsColorAuto(ImPlotCol_MarkerFill)     ? s.Colors[ImPlotCol_Line]   : gp.Style.Colors[ImPlotCol_MarkerFill]    ) : s.Colors[ImPlotCol_MarkerFill];\n        s.Colors[ImPlotCol_ErrorBar]       = IsColorAuto(s.Colors[ImPlotCol_ErrorBar])      ? ( GetStyleColorVec4(ImPlotCol_ErrorBar)                                                                         ) : s.Colors[ImPlotCol_ErrorBar];\n        // stage next item style vars\n        s.LineWeight         = s.LineWeight       < 0 ? gp.Style.LineWeight       : s.LineWeight;\n        s.Marker             = s.Marker           < 0 ? gp.Style.Marker           : s.Marker;\n        s.MarkerSize         = s.MarkerSize       < 0 ? gp.Style.MarkerSize       : s.MarkerSize;\n        s.MarkerWeight       = s.MarkerWeight     < 0 ? gp.Style.MarkerWeight     : s.MarkerWeight;\n        s.FillAlpha          = s.FillAlpha        < 0 ? gp.Style.FillAlpha        : s.FillAlpha;\n        s.ErrorBarSize       = s.ErrorBarSize     < 0 ? gp.Style.ErrorBarSize     : s.ErrorBarSize;\n        s.ErrorBarWeight     = s.ErrorBarWeight   < 0 ? gp.Style.ErrorBarWeight   : s.ErrorBarWeight;\n        s.DigitalBitHeight   = s.DigitalBitHeight < 0 ? gp.Style.DigitalBitHeight : s.DigitalBitHeight;\n        s.DigitalBitGap      = s.DigitalBitGap    < 0 ? gp.Style.DigitalBitGap    : s.DigitalBitGap;\n        // apply alpha modifier(s)\n        s.Colors[ImPlotCol_Fill].w       *= s.FillAlpha;\n        s.Colors[ImPlotCol_MarkerFill].w *= s.FillAlpha; // TODO: this should be separate, if it at all\n        // apply highlight mods\n        if (item->LegendHovered) {\n            if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightItem)) {\n                s.LineWeight   *= ITEM_HIGHLIGHT_LINE_SCALE;\n                s.MarkerSize   *= ITEM_HIGHLIGHT_MARK_SCALE;\n                s.MarkerWeight *= ITEM_HIGHLIGHT_LINE_SCALE;\n                // TODO: how to highlight fills?\n            }\n            if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightAxis)) {\n                if (gp.CurrentPlot->EnabledAxesX() > 1)\n                    gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX].ColorHiLi = item->Color;\n                if (gp.CurrentPlot->EnabledAxesY() > 1)\n                    gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentY].ColorHiLi = item->Color;\n            }\n        }\n        // set render flags\n        s.RenderLine       = s.Colors[ImPlotCol_Line].w          > 0 && s.LineWeight > 0;\n        s.RenderFill       = s.Colors[ImPlotCol_Fill].w          > 0;\n        s.RenderMarkerFill = s.Colors[ImPlotCol_MarkerFill].w    > 0;\n        s.RenderMarkerLine = s.Colors[ImPlotCol_MarkerOutline].w > 0 && s.MarkerWeight > 0;\n        // push rendering clip rect\n        PushPlotClipRect();\n        return true;\n    }\n}\n\n// Ends an item (call only if BeginItem returns true)\nvoid EndItem() {\n    ImPlotContext& gp = *GImPlot;\n    // pop rendering clip rect\n    PopPlotClipRect();\n    // reset next item data\n    gp.NextItemData.Reset();\n    // set current item\n    gp.PreviousItem = gp.CurrentItem;\n    gp.CurrentItem  = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Indexers\n//-----------------------------------------------------------------------------\n\ntemplate <typename T>\nIMPLOT_INLINE T IndexData(const T* data, int idx, int count, int offset, int stride) {\n    const int s = ((offset == 0) << 0) | ((stride == sizeof(T)) << 1);\n    switch (s) {\n        case 3 : return data[idx];\n        case 2 : return data[(offset + idx) % count];\n        case 1 : return *(const T*)(const void*)((const unsigned char*)data + (size_t)((idx) ) * stride);\n        case 0 : return *(const T*)(const void*)((const unsigned char*)data + (size_t)((offset + idx) % count) * stride);\n        default: return T(0);\n    }\n}\n\ntemplate <typename T>\nstruct IndexerIdx {\n    IndexerIdx(const T* data, int count, int offset = 0, int stride = sizeof(T)) :\n        Data(data),\n        Count(count),\n        Offset(count ? ImPosMod(offset, count) : 0),\n        Stride(stride)\n    { }\n    template <typename I> IMPLOT_INLINE double operator()(I idx) const {\n        return (double)IndexData(Data, idx, Count, Offset, Stride);\n    }\n    const T* Data;\n    int Count;\n    int Offset;\n    int Stride;\n};\n\ntemplate <typename _Indexer1, typename _Indexer2>\nstruct IndexerAdd {\n    IndexerAdd(const _Indexer1& indexer1, const _Indexer2& indexer2, double scale1 = 1, double scale2 = 1)\n        : Indexer1(indexer1),\n          Indexer2(indexer2),\n          Scale1(scale1),\n          Scale2(scale2),\n          Count(ImMin(Indexer1.Count, Indexer2.Count))\n    { }\n    template <typename I> IMPLOT_INLINE double operator()(I idx) const {\n        return Scale1 * Indexer1(idx) + Scale2 * Indexer2(idx);\n    }\n    const _Indexer1& Indexer1;\n    const _Indexer2& Indexer2;\n    double Scale1;\n    double Scale2;\n    int Count;\n};\n\nstruct IndexerLin {\n    IndexerLin(double m, double b) : M(m), B(b) { }\n    template <typename I> IMPLOT_INLINE double operator()(I idx) const {\n        return M * idx + B;\n    }\n    const double M;\n    const double B;\n};\n\nstruct IndexerConst {\n    IndexerConst(double ref) : Ref(ref) { }\n    template <typename I> IMPLOT_INLINE double operator()(I) const { return Ref; }\n    const double Ref;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Getters\n//-----------------------------------------------------------------------------\n\ntemplate <typename _IndexerX, typename _IndexerY>\nstruct GetterXY {\n    GetterXY(_IndexerX x, _IndexerY y, int count) : IndxerX(x), IndxerY(y), Count(count) { }\n    template <typename I> IMPLOT_INLINE ImPlotPoint operator()(I idx) const {\n        return ImPlotPoint(IndxerX(idx),IndxerY(idx));\n    }\n    const _IndexerX IndxerX;\n    const _IndexerY IndxerY;\n    const int Count;\n};\n\n/// Interprets a user's function pointer as ImPlotPoints\nstruct GetterFuncPtr {\n    GetterFuncPtr(ImPlotGetter getter, void* data, int count) :\n        Getter(getter),\n        Data(data),\n        Count(count)\n    { }\n    template <typename I> IMPLOT_INLINE ImPlotPoint operator()(I idx) const {\n        return Getter(idx, Data);\n    }\n    ImPlotGetter Getter;\n    void* const Data;\n    const int Count;\n};\n\ntemplate <typename _Getter>\nstruct GetterOverrideX {\n    GetterOverrideX(_Getter getter, double x) : Getter(getter), X(x), Count(getter.Count) { }\n    template <typename I> IMPLOT_INLINE ImPlotPoint operator()(I idx) const {\n        ImPlotPoint p = Getter(idx);\n        p.x = X;\n        return p;\n    }\n    const _Getter Getter;\n    const double X;\n    const int Count;\n};\n\ntemplate <typename _Getter>\nstruct GetterOverrideY {\n    GetterOverrideY(_Getter getter, double y) : Getter(getter), Y(y), Count(getter.Count) { }\n    template <typename I> IMPLOT_INLINE ImPlotPoint operator()(I idx) const {\n        ImPlotPoint p = Getter(idx);\n        p.y = Y;\n        return p;\n    }\n    const _Getter Getter;\n    const double Y;\n    const int Count;\n};\n\ntemplate <typename _Getter>\nstruct GetterLoop {\n    GetterLoop(_Getter getter) : Getter(getter), Count(getter.Count + 1) { }\n    template <typename I> IMPLOT_INLINE ImPlotPoint operator()(I idx) const {\n        idx = idx % (Count - 1);\n        return Getter(idx);\n    }\n    const _Getter Getter;\n    const int Count;\n};\n\ntemplate <typename T>\nstruct GetterError {\n    GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) :\n        Xs(xs),\n        Ys(ys),\n        Neg(neg),\n        Pos(pos),\n        Count(count),\n        Offset(count ? ImPosMod(offset, count) : 0),\n        Stride(stride)\n    { }\n    template <typename I> IMPLOT_INLINE ImPlotPointError operator()(I idx) const {\n        return ImPlotPointError((double)IndexData(Xs,  idx, Count, Offset, Stride),\n                                (double)IndexData(Ys,  idx, Count, Offset, Stride),\n                                (double)IndexData(Neg, idx, Count, Offset, Stride),\n                                (double)IndexData(Pos, idx, Count, Offset, Stride));\n    }\n    const T* const Xs;\n    const T* const Ys;\n    const T* const Neg;\n    const T* const Pos;\n    const int Count;\n    const int Offset;\n    const int Stride;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Fitters\n//-----------------------------------------------------------------------------\n\ntemplate <typename _Getter1>\nstruct Fitter1 {\n    Fitter1(const _Getter1& getter) : Getter(getter) { }\n    void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const {\n        for (int i = 0; i < Getter.Count; ++i) {\n            ImPlotPoint p = Getter(i);\n            x_axis.ExtendFitWith(y_axis, p.x, p.y);\n            y_axis.ExtendFitWith(x_axis, p.y, p.x);\n        }\n    }\n    const _Getter1& Getter;\n};\n\ntemplate <typename _Getter1>\nstruct FitterX {\n    FitterX(const _Getter1& getter) : Getter(getter) { }\n    void Fit(ImPlotAxis& x_axis, ImPlotAxis&) const {\n        for (int i = 0; i < Getter.Count; ++i) {\n            ImPlotPoint p = Getter(i);\n            x_axis.ExtendFit(p.x);\n        }\n    }\n    const _Getter1& Getter;\n};\n\ntemplate <typename _Getter1>\nstruct FitterY {\n    FitterY(const _Getter1& getter) : Getter(getter) { }\n    void Fit(ImPlotAxis&, ImPlotAxis& y_axis) const {\n        for (int i = 0; i < Getter.Count; ++i) {\n            ImPlotPoint p = Getter(i);\n            y_axis.ExtendFit(p.y);\n        }\n    }\n    const _Getter1& Getter;\n};\n\ntemplate <typename _Getter1, typename _Getter2>\nstruct Fitter2 {\n    Fitter2(const _Getter1& getter1, const _Getter2& getter2) : Getter1(getter1), Getter2(getter2) { }\n    void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const {\n        for (int i = 0; i < Getter1.Count; ++i) {\n            ImPlotPoint p = Getter1(i);\n            x_axis.ExtendFitWith(y_axis, p.x, p.y);\n            y_axis.ExtendFitWith(x_axis, p.y, p.x);\n        }\n        for (int i = 0; i < Getter2.Count; ++i) {\n            ImPlotPoint p = Getter2(i);\n            x_axis.ExtendFitWith(y_axis, p.x, p.y);\n            y_axis.ExtendFitWith(x_axis, p.y, p.x);\n        }\n    }\n    const _Getter1& Getter1;\n    const _Getter2& Getter2;\n};\n\ntemplate <typename _Getter1, typename _Getter2>\nstruct FitterBarV {\n    FitterBarV(const _Getter1& getter1, const _Getter2& getter2, double width) :\n        Getter1(getter1),\n        Getter2(getter2),\n        HalfWidth(width*0.5)\n    { }\n    void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const {\n        int count = ImMin(Getter1.Count, Getter2.Count);\n        for (int i = 0; i < count; ++i) {\n            ImPlotPoint p1 = Getter1(i); p1.x -= HalfWidth;\n            ImPlotPoint p2 = Getter2(i); p2.x += HalfWidth;\n            x_axis.ExtendFitWith(y_axis, p1.x, p1.y);\n            y_axis.ExtendFitWith(x_axis, p1.y, p1.x);\n            x_axis.ExtendFitWith(y_axis, p2.x, p2.y);\n            y_axis.ExtendFitWith(x_axis, p2.y, p2.x);\n        }\n    }\n    const _Getter1& Getter1;\n    const _Getter2& Getter2;\n    const double    HalfWidth;\n};\n\ntemplate <typename _Getter1, typename _Getter2>\nstruct FitterBarH {\n    FitterBarH(const _Getter1& getter1, const _Getter2& getter2, double height) :\n        Getter1(getter1),\n        Getter2(getter2),\n        HalfHeight(height*0.5)\n    { }\n    void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const {\n        int count = ImMin(Getter1.Count, Getter2.Count);\n        for (int i = 0; i < count; ++i) {\n            ImPlotPoint p1 = Getter1(i); p1.y -= HalfHeight;\n            ImPlotPoint p2 = Getter2(i); p2.y += HalfHeight;\n            x_axis.ExtendFitWith(y_axis, p1.x, p1.y);\n            y_axis.ExtendFitWith(x_axis, p1.y, p1.x);\n            x_axis.ExtendFitWith(y_axis, p2.x, p2.y);\n            y_axis.ExtendFitWith(x_axis, p2.y, p2.x);\n        }\n    }\n    const _Getter1& Getter1;\n    const _Getter2& Getter2;\n    const double    HalfHeight;\n};\n\nstruct FitterRect {\n    FitterRect(const ImPlotPoint& pmin, const ImPlotPoint& pmax) :\n        Pmin(pmin),\n        Pmax(pmax)\n    { }\n    FitterRect(const ImPlotRect& rect) :\n        FitterRect(rect.Min(), rect.Max())\n    { }\n    void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const {\n        x_axis.ExtendFitWith(y_axis, Pmin.x, Pmin.y);\n        y_axis.ExtendFitWith(x_axis, Pmin.y, Pmin.x);\n        x_axis.ExtendFitWith(y_axis, Pmax.x, Pmax.y);\n        y_axis.ExtendFitWith(x_axis, Pmax.y, Pmax.x);\n    }\n    const ImPlotPoint Pmin;\n    const ImPlotPoint Pmax;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Transformers\n//-----------------------------------------------------------------------------\n\nstruct Transformer1 {\n    Transformer1(double pixMin, double pltMin, double pltMax, double m, double scaMin, double scaMax, ImPlotTransform fwd, void* data) :\n        ScaMin(scaMin),\n        ScaMax(scaMax),\n        PltMin(pltMin),\n        PltMax(pltMax),\n        PixMin(pixMin),\n        M(m),\n        TransformFwd(fwd),\n        TransformData(data)\n    { }\n\n    template <typename T> IMPLOT_INLINE float operator()(T p) const {\n        if (TransformFwd != nullptr) {\n            double s = TransformFwd(p, TransformData);\n            double t = (s - ScaMin) / (ScaMax - ScaMin);\n            p = PltMin + (PltMax - PltMin) * t;\n        }\n        return (float)(PixMin + M * (p - PltMin));\n    }\n\n    double ScaMin, ScaMax, PltMin, PltMax, PixMin, M;\n    ImPlotTransform TransformFwd;\n    void*           TransformData;\n};\n\nstruct Transformer2 {\n    Transformer2(const ImPlotAxis& x_axis, const ImPlotAxis& y_axis) :\n        Tx(x_axis.PixelMin,\n           x_axis.Range.Min,\n           x_axis.Range.Max,\n           x_axis.ScaleToPixel,\n           x_axis.ScaleMin,\n           x_axis.ScaleMax,\n           x_axis.TransformForward,\n           x_axis.TransformData),\n        Ty(y_axis.PixelMin,\n           y_axis.Range.Min,\n           y_axis.Range.Max,\n           y_axis.ScaleToPixel,\n           y_axis.ScaleMin,\n           y_axis.ScaleMax,\n           y_axis.TransformForward,\n           y_axis.TransformData)\n    { }\n\n    Transformer2(const ImPlotPlot& plot) :\n        Transformer2(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY])\n    { }\n\n    Transformer2() :\n        Transformer2(*GImPlot->CurrentPlot)\n    { }\n\n    template <typename P> IMPLOT_INLINE ImVec2 operator()(const P& plt) const {\n        ImVec2 out;\n        out.x = Tx(plt.x);\n        out.y = Ty(plt.y);\n        return out;\n    }\n\n    template <typename T> IMPLOT_INLINE ImVec2 operator()(T x, T y) const {\n        ImVec2 out;\n        out.x = Tx(x);\n        out.y = Ty(y);\n        return out;\n    }\n\n    Transformer1 Tx;\n    Transformer1 Ty;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Renderers\n//-----------------------------------------------------------------------------\n\nstruct RendererBase {\n    RendererBase(int prims, int idx_consumed, int vtx_consumed) :\n        Prims(prims),\n        IdxConsumed(idx_consumed),\n        VtxConsumed(vtx_consumed)\n    { }\n    const int Prims;\n    Transformer2 Transformer;\n    const int IdxConsumed;\n    const int VtxConsumed;\n};\n\ntemplate <class _Getter>\nstruct RendererLineStrip : RendererBase {\n    RendererLineStrip(const _Getter& getter, ImU32 col, float weight) :\n        RendererBase(getter.Count - 1, 6, 4),\n        Getter(getter),\n        Col(col),\n        HalfWeight(ImMax(1.0f,weight)*0.5f)\n    {\n        P1 = this->Transformer(Getter(0));\n    }\n    void Init(ImDrawList& draw_list) const {\n        GetLineRenderProps(draw_list, HalfWeight, UV0, UV1);\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImVec2 P2 = this->Transformer(Getter(prim + 1));\n        if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) {\n            P1 = P2;\n            return false;\n        }\n        PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1);\n        P1 = P2;\n        return true;\n    }\n    const _Getter& Getter;\n    const ImU32 Col;\n    mutable float HalfWeight;\n    mutable ImVec2 P1;\n    mutable ImVec2 UV0;\n    mutable ImVec2 UV1;\n};\n\ntemplate <class _Getter>\nstruct RendererLineStripSkip : RendererBase {\n    RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight) :\n        RendererBase(getter.Count - 1, 6, 4),\n        Getter(getter),\n        Col(col),\n        HalfWeight(ImMax(1.0f,weight)*0.5f)\n    {\n        P1 = this->Transformer(Getter(0));\n    }\n    void Init(ImDrawList& draw_list) const {\n        GetLineRenderProps(draw_list, HalfWeight, UV0, UV1);\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImVec2 P2 = this->Transformer(Getter(prim + 1));\n        if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) {\n            if (!ImNan(P2.x) && !ImNan(P2.y))\n                P1 = P2;\n            return false;\n        }\n        PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1);\n        if (!ImNan(P2.x) && !ImNan(P2.y))\n            P1 = P2;\n        return true;\n    }\n    const _Getter& Getter;\n    const ImU32 Col;\n    mutable float HalfWeight;\n    mutable ImVec2 P1;\n    mutable ImVec2 UV0;\n    mutable ImVec2 UV1;\n};\n\ntemplate <class _Getter>\nstruct RendererLineSegments1 : RendererBase {\n    RendererLineSegments1(const _Getter& getter, ImU32 col, float weight) :\n        RendererBase(getter.Count / 2, 6, 4),\n        Getter(getter),\n        Col(col),\n        HalfWeight(ImMax(1.0f,weight)*0.5f)\n    { }\n    void Init(ImDrawList& draw_list) const {\n        GetLineRenderProps(draw_list, HalfWeight, UV0, UV1);\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImVec2 P1 = this->Transformer(Getter(prim*2+0));\n        ImVec2 P2 = this->Transformer(Getter(prim*2+1));\n        if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2))))\n            return false;\n        PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1);\n        return true;\n    }\n    const _Getter& Getter;\n    const ImU32 Col;\n    mutable float HalfWeight;\n    mutable ImVec2 UV0;\n    mutable ImVec2 UV1;\n};\n\ntemplate <class _Getter1, class _Getter2>\nstruct RendererLineSegments2 : RendererBase {\n    RendererLineSegments2(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, float weight) :\n        RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4),\n        Getter1(getter1),\n        Getter2(getter2),\n        Col(col),\n        HalfWeight(ImMax(1.0f,weight)*0.5f)\n    {}\n    void Init(ImDrawList& draw_list) const {\n        GetLineRenderProps(draw_list, HalfWeight, UV0, UV1);\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImVec2 P1 = this->Transformer(Getter1(prim));\n        ImVec2 P2 = this->Transformer(Getter2(prim));\n        if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2))))\n            return false;\n        PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1);\n        return true;\n    }\n    const _Getter1& Getter1;\n    const _Getter2& Getter2;\n    const ImU32 Col;\n    mutable float HalfWeight;\n    mutable ImVec2 UV0;\n    mutable ImVec2 UV1;\n};\n\ntemplate <class _Getter1, class _Getter2>\nstruct RendererBarsFillV : RendererBase {\n    RendererBarsFillV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width) :\n        RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4),\n        Getter1(getter1),\n        Getter2(getter2),\n        Col(col),\n        HalfWidth(width/2)\n    {}\n    void Init(ImDrawList& draw_list) const {\n        UV = draw_list._Data->TexUvWhitePixel;\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImPlotPoint p1 = Getter1(prim);\n        ImPlotPoint p2 = Getter2(prim);\n        p1.x += HalfWidth;\n        p2.x -= HalfWidth;\n        ImVec2 P1 = this->Transformer(p1);\n        ImVec2 P2 = this->Transformer(p2);\n        float width_px = ImAbs(P1.x-P2.x);\n        if (width_px < 1.0f) {\n            P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2;\n            P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2;\n        }\n        ImVec2 PMin = ImMin(P1, P2);\n        ImVec2 PMax = ImMax(P1, P2);\n        if (!cull_rect.Overlaps(ImRect(PMin, PMax)))\n            return false;\n        PrimRectFill(draw_list,PMin,PMax,Col,UV);\n        return true;\n    }\n    const _Getter1& Getter1;\n    const _Getter2& Getter2;\n    const ImU32 Col;\n    const double HalfWidth;\n    mutable ImVec2 UV;\n};\n\ntemplate <class _Getter1, class _Getter2>\nstruct RendererBarsFillH : RendererBase {\n    RendererBarsFillH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height) :\n        RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4),\n        Getter1(getter1),\n        Getter2(getter2),\n        Col(col),\n        HalfHeight(height/2)\n    {}\n    void Init(ImDrawList& draw_list) const {\n        UV = draw_list._Data->TexUvWhitePixel;\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImPlotPoint p1 = Getter1(prim);\n        ImPlotPoint p2 = Getter2(prim);\n        p1.y += HalfHeight;\n        p2.y -= HalfHeight;\n        ImVec2 P1 = this->Transformer(p1);\n        ImVec2 P2 = this->Transformer(p2);\n        float height_px = ImAbs(P1.y-P2.y);\n        if (height_px < 1.0f) {\n            P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2;\n            P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2;\n        }\n        ImVec2 PMin = ImMin(P1, P2);\n        ImVec2 PMax = ImMax(P1, P2);\n        if (!cull_rect.Overlaps(ImRect(PMin, PMax)))\n            return false;\n        PrimRectFill(draw_list,PMin,PMax,Col,UV);\n        return true;\n    }\n    const _Getter1& Getter1;\n    const _Getter2& Getter2;\n    const ImU32 Col;\n    const double HalfHeight;\n    mutable ImVec2 UV;\n};\n\ntemplate <class _Getter1, class _Getter2>\nstruct RendererBarsLineV : RendererBase {\n    RendererBarsLineV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width, float weight) :\n        RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8),\n        Getter1(getter1),\n        Getter2(getter2),\n        Col(col),\n        HalfWidth(width/2),\n        Weight(weight)\n    {}\n    void Init(ImDrawList& draw_list) const {\n        UV = draw_list._Data->TexUvWhitePixel;\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImPlotPoint p1 = Getter1(prim);\n        ImPlotPoint p2 = Getter2(prim);\n        p1.x += HalfWidth;\n        p2.x -= HalfWidth;\n        ImVec2 P1 = this->Transformer(p1);\n        ImVec2 P2 = this->Transformer(p2);\n        float width_px = ImAbs(P1.x-P2.x);\n        if (width_px < 1.0f) {\n            P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2;\n            P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2;\n        }\n        ImVec2 PMin = ImMin(P1, P2);\n        ImVec2 PMax = ImMax(P1, P2);\n        if (!cull_rect.Overlaps(ImRect(PMin, PMax)))\n            return false;\n        PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV);\n        return true;\n    }\n    const _Getter1& Getter1;\n    const _Getter2& Getter2;\n    const ImU32 Col;\n    const double HalfWidth;\n    const float Weight;\n    mutable ImVec2 UV;\n};\n\ntemplate <class _Getter1, class _Getter2>\nstruct RendererBarsLineH : RendererBase {\n    RendererBarsLineH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height, float weight) :\n        RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8),\n        Getter1(getter1),\n        Getter2(getter2),\n        Col(col),\n        HalfHeight(height/2),\n        Weight(weight)\n    {}\n    void Init(ImDrawList& draw_list) const {\n        UV = draw_list._Data->TexUvWhitePixel;\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImPlotPoint p1 = Getter1(prim);\n        ImPlotPoint p2 = Getter2(prim);\n        p1.y += HalfHeight;\n        p2.y -= HalfHeight;\n        ImVec2 P1 = this->Transformer(p1);\n        ImVec2 P2 = this->Transformer(p2);\n        float height_px = ImAbs(P1.y-P2.y);\n        if (height_px < 1.0f) {\n            P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2;\n            P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2;\n        }\n        ImVec2 PMin = ImMin(P1, P2);\n        ImVec2 PMax = ImMax(P1, P2);\n        if (!cull_rect.Overlaps(ImRect(PMin, PMax)))\n            return false;\n        PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV);\n        return true;\n    }\n    const _Getter1& Getter1;\n    const _Getter2& Getter2;\n    const ImU32 Col;\n    const double HalfHeight;\n    const float Weight;\n    mutable ImVec2 UV;\n};\n\n\ntemplate <class _Getter>\nstruct RendererStairsPre : RendererBase {\n    RendererStairsPre(const _Getter& getter, ImU32 col, float weight) :\n        RendererBase(getter.Count - 1, 12, 8),\n        Getter(getter),\n        Col(col),\n        HalfWeight(ImMax(1.0f,weight)*0.5f)\n    {\n        P1 = this->Transformer(Getter(0));\n    }\n    void Init(ImDrawList& draw_list) const {\n        UV = draw_list._Data->TexUvWhitePixel;\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImVec2 P2 = this->Transformer(Getter(prim + 1));\n        if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) {\n            P1 = P2;\n            return false;\n        }\n        PrimRectFill(draw_list, ImVec2(P1.x - HalfWeight, P1.y), ImVec2(P1.x + HalfWeight, P2.y), Col, UV);\n        PrimRectFill(draw_list, ImVec2(P1.x, P2.y + HalfWeight), ImVec2(P2.x, P2.y - HalfWeight), Col, UV);\n        P1 = P2;\n        return true;\n    }\n    const _Getter& Getter;\n    const ImU32 Col;\n    mutable float HalfWeight;\n    mutable ImVec2 P1;\n    mutable ImVec2 UV;\n};\n\ntemplate <class _Getter>\nstruct RendererStairsPost : RendererBase {\n    RendererStairsPost(const _Getter& getter, ImU32 col, float weight) :\n        RendererBase(getter.Count - 1, 12, 8),\n        Getter(getter),\n        Col(col),\n        HalfWeight(ImMax(1.0f,weight) * 0.5f)\n    {\n        P1 = this->Transformer(Getter(0));\n    }\n    void Init(ImDrawList& draw_list) const {\n        UV = draw_list._Data->TexUvWhitePixel;\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImVec2 P2 = this->Transformer(Getter(prim + 1));\n        if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) {\n            P1 = P2;\n            return false;\n        }\n        PrimRectFill(draw_list, ImVec2(P1.x, P1.y + HalfWeight), ImVec2(P2.x, P1.y - HalfWeight), Col, UV);\n        PrimRectFill(draw_list, ImVec2(P2.x - HalfWeight, P2.y), ImVec2(P2.x + HalfWeight, P1.y), Col, UV);\n        P1 = P2;\n        return true;\n    }\n    const _Getter& Getter;\n    const ImU32 Col;\n    mutable float HalfWeight;\n    mutable ImVec2 P1;\n    mutable ImVec2 UV;\n};\n\ntemplate <class _Getter>\nstruct RendererStairsPreShaded : RendererBase {\n    RendererStairsPreShaded(const _Getter& getter, ImU32 col) :\n        RendererBase(getter.Count - 1, 6, 4),\n        Getter(getter),\n        Col(col)\n    {\n        P1 = this->Transformer(Getter(0));\n        Y0 = this->Transformer(ImPlotPoint(0,0)).y;\n    }\n    void Init(ImDrawList& draw_list) const {\n        UV = draw_list._Data->TexUvWhitePixel;\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImVec2 P2 = this->Transformer(Getter(prim + 1));\n        ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(Y0, P2.y));\n        ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(Y0, P2.y));\n        if (!cull_rect.Overlaps(ImRect(PMin, PMax))) {\n            P1 = P2;\n            return false;\n        }\n        PrimRectFill(draw_list, PMin, PMax, Col, UV);\n        P1 = P2;\n        return true;\n    }\n    const _Getter& Getter;\n    const ImU32 Col;\n    float Y0;\n    mutable ImVec2 P1;\n    mutable ImVec2 UV;\n};\n\ntemplate <class _Getter>\nstruct RendererStairsPostShaded : RendererBase {\n    RendererStairsPostShaded(const _Getter& getter, ImU32 col) :\n        RendererBase(getter.Count - 1, 6, 4),\n        Getter(getter),\n        Col(col)\n    {\n        P1 = this->Transformer(Getter(0));\n        Y0 = this->Transformer(ImPlotPoint(0,0)).y;\n    }\n    void Init(ImDrawList& draw_list) const {\n        UV = draw_list._Data->TexUvWhitePixel;\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImVec2 P2 = this->Transformer(Getter(prim + 1));\n        ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(P1.y, Y0));\n        ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(P1.y, Y0));\n        if (!cull_rect.Overlaps(ImRect(PMin, PMax))) {\n            P1 = P2;\n            return false;\n        }\n        PrimRectFill(draw_list, PMin, PMax, Col, UV);\n        P1 = P2;\n        return true;\n    }\n    const _Getter& Getter;\n    const ImU32 Col;\n    float Y0;\n    mutable ImVec2 P1;\n    mutable ImVec2 UV;\n};\n\n\n\ntemplate <class _Getter1, class _Getter2>\nstruct RendererShaded : RendererBase {\n    RendererShaded(const _Getter1& getter1, const _Getter2& getter2, ImU32 col) :\n        RendererBase(ImMin(getter1.Count, getter2.Count) - 1, 6, 5),\n        Getter1(getter1),\n        Getter2(getter2),\n        Col(col)\n    {\n        P11 = this->Transformer(Getter1(0));\n        P12 = this->Transformer(Getter2(0));\n    }\n    void Init(ImDrawList& draw_list) const {\n        UV = draw_list._Data->TexUvWhitePixel;\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImVec2 P21 = this->Transformer(Getter1(prim+1));\n        ImVec2 P22 = this->Transformer(Getter2(prim+1));\n        ImRect rect(ImMin(ImMin(ImMin(P11,P12),P21),P22), ImMax(ImMax(ImMax(P11,P12),P21),P22));\n        if (!cull_rect.Overlaps(rect)) {\n            P11 = P21;\n            P12 = P22;\n            return false;\n        }\n        const int intersect = (P11.y > P12.y && P22.y > P21.y) || (P12.y > P11.y && P21.y > P22.y);\n        const ImVec2 intersection = intersect == 0 ? ImVec2(0,0) : Intersection(P11,P21,P12,P22);\n        draw_list._VtxWritePtr[0].pos = P11;\n        draw_list._VtxWritePtr[0].uv  = UV;\n        draw_list._VtxWritePtr[0].col = Col;\n        draw_list._VtxWritePtr[1].pos = P21;\n        draw_list._VtxWritePtr[1].uv  = UV;\n        draw_list._VtxWritePtr[1].col = Col;\n        draw_list._VtxWritePtr[2].pos = intersection;\n        draw_list._VtxWritePtr[2].uv  = UV;\n        draw_list._VtxWritePtr[2].col = Col;\n        draw_list._VtxWritePtr[3].pos = P12;\n        draw_list._VtxWritePtr[3].uv  = UV;\n        draw_list._VtxWritePtr[3].col = Col;\n        draw_list._VtxWritePtr[4].pos = P22;\n        draw_list._VtxWritePtr[4].uv  = UV;\n        draw_list._VtxWritePtr[4].col = Col;\n        draw_list._VtxWritePtr += 5;\n        draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx);\n        draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1 + intersect);\n        draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3);\n        draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1);\n        draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4);\n        draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3 - intersect);\n        draw_list._IdxWritePtr += 6;\n        draw_list._VtxCurrentIdx += 5;\n        P11 = P21;\n        P12 = P22;\n        return true;\n    }\n    const _Getter1& Getter1;\n    const _Getter2& Getter2;\n    const ImU32 Col;\n    mutable ImVec2 P11;\n    mutable ImVec2 P12;\n    mutable ImVec2 UV;\n};\n\nstruct RectC {\n    ImPlotPoint Pos;\n    ImPlotPoint HalfSize;\n    ImU32 Color;\n};\n\ntemplate <typename _Getter>\nstruct RendererRectC : RendererBase {\n    RendererRectC(const _Getter& getter) :\n        RendererBase(getter.Count, 6, 4),\n        Getter(getter)\n    {}\n    void Init(ImDrawList& draw_list) const {\n        UV = draw_list._Data->TexUvWhitePixel;\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        RectC rect = Getter(prim);\n        ImVec2 P1 = this->Transformer(rect.Pos.x - rect.HalfSize.x , rect.Pos.y - rect.HalfSize.y);\n        ImVec2 P2 = this->Transformer(rect.Pos.x + rect.HalfSize.x , rect.Pos.y + rect.HalfSize.y);\n        if ((rect.Color & IM_COL32_A_MASK) == 0 || !cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2))))\n            return false;\n        PrimRectFill(draw_list,P1,P2,rect.Color,UV);\n        return true;\n    }\n    const _Getter& Getter;\n    mutable ImVec2 UV;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] RenderPrimitives\n//-----------------------------------------------------------------------------\n\n/// Renders primitive shapes in bulk as efficiently as possible.\ntemplate <class _Renderer>\nvoid RenderPrimitivesEx(const _Renderer& renderer, ImDrawList& draw_list, const ImRect& cull_rect) {\n    unsigned int prims        = renderer.Prims;\n    unsigned int prims_culled = 0;\n    unsigned int idx          = 0;\n    renderer.Init(draw_list);\n    while (prims) {\n        // find how many can be reserved up to end of current draw command's limit\n        unsigned int cnt = ImMin(prims, (MaxIdx<ImDrawIdx>::Value - draw_list._VtxCurrentIdx) / renderer.VtxConsumed);\n        // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time\n        if (cnt >= ImMin(64u, prims)) {\n            if (prims_culled >= cnt)\n                prims_culled -= cnt; // reuse previous reservation\n            else {\n                // add more elements to previous reservation\n                draw_list.PrimReserve((cnt - prims_culled) * renderer.IdxConsumed, (cnt - prims_culled) * renderer.VtxConsumed);\n                prims_culled = 0;\n            }\n        }\n        else\n        {\n            if (prims_culled > 0) {\n                draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed);\n                prims_culled = 0;\n            }\n            cnt = ImMin(prims, (MaxIdx<ImDrawIdx>::Value - 0/*draw_list._VtxCurrentIdx*/) / renderer.VtxConsumed);\n            // reserve new draw command\n            draw_list.PrimReserve(cnt * renderer.IdxConsumed, cnt * renderer.VtxConsumed);\n        }\n        prims -= cnt;\n        for (unsigned int ie = idx + cnt; idx != ie; ++idx) {\n            if (!renderer.Render(draw_list, cull_rect, idx))\n                prims_culled++;\n        }\n    }\n    if (prims_culled > 0)\n        draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed);\n}\n\ntemplate <template <class> class _Renderer, class _Getter, typename ...Args>\nvoid RenderPrimitives1(const _Getter& getter, Args... args) {\n    ImDrawList& draw_list = *GetPlotDrawList();\n    const ImRect& cull_rect = GetCurrentPlot()->PlotRect;\n    RenderPrimitivesEx(_Renderer<_Getter>(getter,args...), draw_list, cull_rect);\n}\n\ntemplate <template <class,class> class _Renderer, class _Getter1, class _Getter2, typename ...Args>\nvoid RenderPrimitives2(const _Getter1& getter1, const _Getter2& getter2, Args... args) {\n    ImDrawList& draw_list = *GetPlotDrawList();\n    const ImRect& cull_rect = GetCurrentPlot()->PlotRect;\n    RenderPrimitivesEx(_Renderer<_Getter1,_Getter2>(getter1,getter2,args...), draw_list, cull_rect);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Markers\n//-----------------------------------------------------------------------------\n\ntemplate <class _Getter>\nstruct RendererMarkersFill : RendererBase {\n    RendererMarkersFill(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col) :\n        RendererBase(getter.Count, (count-2)*3, count),\n        Getter(getter),\n        Marker(marker),\n        Count(count),\n        Size(size),\n        Col(col)\n    { }\n    void Init(ImDrawList& draw_list) const {\n        UV = draw_list._Data->TexUvWhitePixel;\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImVec2 p = this->Transformer(Getter(prim));\n        if (p.x >= cull_rect.Min.x && p.y >= cull_rect.Min.y && p.x <= cull_rect.Max.x && p.y <= cull_rect.Max.y) {\n            for (int i = 0; i < Count; i++) {\n                draw_list._VtxWritePtr[0].pos.x = p.x + Marker[i].x * Size;\n                draw_list._VtxWritePtr[0].pos.y = p.y + Marker[i].y * Size;\n                draw_list._VtxWritePtr[0].uv = UV;\n                draw_list._VtxWritePtr[0].col = Col;\n                draw_list._VtxWritePtr++;\n            }\n            for (int i = 2; i < Count; i++) {\n                draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx);\n                draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + i - 1);\n                draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + i);\n                draw_list._IdxWritePtr += 3;\n            }\n            draw_list._VtxCurrentIdx += (ImDrawIdx)Count;\n            return true;\n        }\n        return false;\n    }\n    const _Getter& Getter;\n    const ImVec2* Marker;\n    const int Count;\n    const float Size;\n    const ImU32 Col;\n    mutable ImVec2 UV;\n};\n\n\ntemplate <class _Getter>\nstruct RendererMarkersLine : RendererBase {\n    RendererMarkersLine(const _Getter& getter, const ImVec2* marker, int count, float size, float weight, ImU32 col) :\n        RendererBase(getter.Count, count/2*6, count/2*4),\n        Getter(getter),\n        Marker(marker),\n        Count(count),\n        HalfWeight(ImMax(1.0f,weight)*0.5f),\n        Size(size),\n        Col(col)\n    { }\n    void Init(ImDrawList& draw_list) const {\n        GetLineRenderProps(draw_list, HalfWeight, UV0, UV1);\n    }\n    IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const {\n        ImVec2 p = this->Transformer(Getter(prim));\n        if (p.x >= cull_rect.Min.x && p.y >= cull_rect.Min.y && p.x <= cull_rect.Max.x && p.y <= cull_rect.Max.y) {\n            for (int i = 0; i < Count; i = i + 2) {\n                ImVec2 p1(p.x + Marker[i].x * Size, p.y + Marker[i].y * Size);\n                ImVec2 p2(p.x + Marker[i+1].x * Size, p.y + Marker[i+1].y * Size);\n                PrimLine(draw_list, p1, p2, HalfWeight, Col, UV0, UV1);\n            }\n            return true;\n        }\n        return false;\n    }\n    const _Getter& Getter;\n    const ImVec2* Marker;\n    const int Count;\n    mutable float HalfWeight;\n    const float Size;\n    const ImU32 Col;\n    mutable ImVec2 UV0;\n    mutable ImVec2 UV1;\n};\n\nstatic const ImVec2 MARKER_FILL_CIRCLE[10]  = {ImVec2(1.0f, 0.0f), ImVec2(0.809017f, 0.58778524f),ImVec2(0.30901697f, 0.95105654f),ImVec2(-0.30901703f, 0.9510565f),ImVec2(-0.80901706f, 0.5877852f),ImVec2(-1.0f, 0.0f),ImVec2(-0.80901694f, -0.58778536f),ImVec2(-0.3090171f, -0.9510565f),ImVec2(0.30901712f, -0.9510565f),ImVec2(0.80901694f, -0.5877853f)};\nstatic const ImVec2 MARKER_FILL_SQUARE[4]   = {ImVec2(SQRT_1_2,SQRT_1_2), ImVec2(SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,SQRT_1_2)};\nstatic const ImVec2 MARKER_FILL_DIAMOND[4]  = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(0, 1)};\nstatic const ImVec2 MARKER_FILL_UP[3]       = {ImVec2(SQRT_3_2,0.5f),ImVec2(0,-1),ImVec2(-SQRT_3_2,0.5f)};\nstatic const ImVec2 MARKER_FILL_DOWN[3]     = {ImVec2(SQRT_3_2,-0.5f),ImVec2(0,1),ImVec2(-SQRT_3_2,-0.5f)};\nstatic const ImVec2 MARKER_FILL_LEFT[3]     = {ImVec2(-1,0), ImVec2(0.5, SQRT_3_2), ImVec2(0.5, -SQRT_3_2)};\nstatic const ImVec2 MARKER_FILL_RIGHT[3]    = {ImVec2(1,0), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, -SQRT_3_2)};\n\nstatic const ImVec2 MARKER_LINE_CIRCLE[20]  = {\n    ImVec2(1.0f, 0.0f),\n    ImVec2(0.809017f, 0.58778524f),\n    ImVec2(0.809017f, 0.58778524f),\n    ImVec2(0.30901697f, 0.95105654f),\n    ImVec2(0.30901697f, 0.95105654f),\n    ImVec2(-0.30901703f, 0.9510565f),\n    ImVec2(-0.30901703f, 0.9510565f),\n    ImVec2(-0.80901706f, 0.5877852f),\n    ImVec2(-0.80901706f, 0.5877852f),\n    ImVec2(-1.0f, 0.0f),\n    ImVec2(-1.0f, 0.0f),\n    ImVec2(-0.80901694f, -0.58778536f),\n    ImVec2(-0.80901694f, -0.58778536f),\n    ImVec2(-0.3090171f, -0.9510565f),\n    ImVec2(-0.3090171f, -0.9510565f),\n    ImVec2(0.30901712f, -0.9510565f),\n    ImVec2(0.30901712f, -0.9510565f),\n    ImVec2(0.80901694f, -0.5877853f),\n    ImVec2(0.80901694f, -0.5877853f),\n    ImVec2(1.0f, 0.0f)\n};\nstatic const ImVec2 MARKER_LINE_SQUARE[8]   = {ImVec2(SQRT_1_2,SQRT_1_2), ImVec2(SQRT_1_2,-SQRT_1_2), ImVec2(SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,SQRT_1_2), ImVec2(-SQRT_1_2,SQRT_1_2), ImVec2(SQRT_1_2,SQRT_1_2)};\nstatic const ImVec2 MARKER_LINE_DIAMOND[8]  = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(-1, 0), ImVec2(0, 1), ImVec2(0, 1), ImVec2(1, 0)};\nstatic const ImVec2 MARKER_LINE_UP[6]       = {ImVec2(SQRT_3_2,0.5f), ImVec2(0,-1),ImVec2(0,-1),ImVec2(-SQRT_3_2,0.5f),ImVec2(-SQRT_3_2,0.5f),ImVec2(SQRT_3_2,0.5f)};\nstatic const ImVec2 MARKER_LINE_DOWN[6]     = {ImVec2(SQRT_3_2,-0.5f),ImVec2(0,1),ImVec2(0,1),ImVec2(-SQRT_3_2,-0.5f), ImVec2(-SQRT_3_2,-0.5f), ImVec2(SQRT_3_2,-0.5f)};\nstatic const ImVec2 MARKER_LINE_LEFT[6]     = {ImVec2(-1,0), ImVec2(0.5, SQRT_3_2),  ImVec2(0.5, SQRT_3_2),  ImVec2(0.5, -SQRT_3_2) , ImVec2(0.5, -SQRT_3_2) , ImVec2(-1,0) };\nstatic const ImVec2 MARKER_LINE_RIGHT[6]    = {ImVec2(1,0),  ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, -SQRT_3_2), ImVec2(-0.5, -SQRT_3_2), ImVec2(1,0) };\nstatic const ImVec2 MARKER_LINE_ASTERISK[6] = {ImVec2(-SQRT_3_2, -0.5f), ImVec2(SQRT_3_2, 0.5f),  ImVec2(-SQRT_3_2, 0.5f), ImVec2(SQRT_3_2, -0.5f), ImVec2(0, -1), ImVec2(0, 1)};\nstatic const ImVec2 MARKER_LINE_PLUS[4]     = {ImVec2(-1, 0), ImVec2(1, 0), ImVec2(0, -1), ImVec2(0, 1)};\nstatic const ImVec2 MARKER_LINE_CROSS[4]    = {ImVec2(-SQRT_1_2,-SQRT_1_2),ImVec2(SQRT_1_2,SQRT_1_2),ImVec2(SQRT_1_2,-SQRT_1_2),ImVec2(-SQRT_1_2,SQRT_1_2)};\n\ntemplate <typename _Getter>\nvoid RenderMarkers(const _Getter& getter, ImPlotMarker marker, float size, bool rend_fill, ImU32 col_fill, bool rend_line, ImU32 col_line, float weight) {\n    if (rend_fill) {\n        switch (marker) {\n            case ImPlotMarker_Circle  : RenderPrimitives1<RendererMarkersFill>(getter,MARKER_FILL_CIRCLE,10,size,col_fill); break;\n            case ImPlotMarker_Square  : RenderPrimitives1<RendererMarkersFill>(getter,MARKER_FILL_SQUARE, 4,size,col_fill); break;\n            case ImPlotMarker_Diamond : RenderPrimitives1<RendererMarkersFill>(getter,MARKER_FILL_DIAMOND,4,size,col_fill); break;\n            case ImPlotMarker_Up      : RenderPrimitives1<RendererMarkersFill>(getter,MARKER_FILL_UP,     3,size,col_fill); break;\n            case ImPlotMarker_Down    : RenderPrimitives1<RendererMarkersFill>(getter,MARKER_FILL_DOWN,   3,size,col_fill); break;\n            case ImPlotMarker_Left    : RenderPrimitives1<RendererMarkersFill>(getter,MARKER_FILL_LEFT,   3,size,col_fill); break;\n            case ImPlotMarker_Right   : RenderPrimitives1<RendererMarkersFill>(getter,MARKER_FILL_RIGHT,  3,size,col_fill); break;\n        }\n    }\n    if (rend_line) {\n        switch (marker) {\n            case ImPlotMarker_Circle    : RenderPrimitives1<RendererMarkersLine>(getter,MARKER_LINE_CIRCLE, 20,size,weight,col_line); break;\n            case ImPlotMarker_Square    : RenderPrimitives1<RendererMarkersLine>(getter,MARKER_LINE_SQUARE,  8,size,weight,col_line); break;\n            case ImPlotMarker_Diamond   : RenderPrimitives1<RendererMarkersLine>(getter,MARKER_LINE_DIAMOND, 8,size,weight,col_line); break;\n            case ImPlotMarker_Up        : RenderPrimitives1<RendererMarkersLine>(getter,MARKER_LINE_UP,      6,size,weight,col_line); break;\n            case ImPlotMarker_Down      : RenderPrimitives1<RendererMarkersLine>(getter,MARKER_LINE_DOWN,    6,size,weight,col_line); break;\n            case ImPlotMarker_Left      : RenderPrimitives1<RendererMarkersLine>(getter,MARKER_LINE_LEFT,    6,size,weight,col_line); break;\n            case ImPlotMarker_Right     : RenderPrimitives1<RendererMarkersLine>(getter,MARKER_LINE_RIGHT,   6,size,weight,col_line); break;\n            case ImPlotMarker_Asterisk  : RenderPrimitives1<RendererMarkersLine>(getter,MARKER_LINE_ASTERISK,6,size,weight,col_line); break;\n            case ImPlotMarker_Plus      : RenderPrimitives1<RendererMarkersLine>(getter,MARKER_LINE_PLUS,    4,size,weight,col_line); break;\n            case ImPlotMarker_Cross     : RenderPrimitives1<RendererMarkersLine>(getter,MARKER_LINE_CROSS,   4,size,weight,col_line); break;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotLine\n//-----------------------------------------------------------------------------\n\ntemplate <typename _Getter>\nvoid PlotLineEx(const char* label_id, const _Getter& getter, ImPlotLineFlags flags) {\n    if (BeginItemEx(label_id, Fitter1<_Getter>(getter), flags, ImPlotCol_Line)) {\n        if (getter.Count <= 0) {\n            EndItem();\n            return;\n        }\n        const ImPlotNextItemData& s = GetItemData();\n        if (getter.Count > 1) {\n            if (ImHasFlag(flags, ImPlotLineFlags_Shaded) && s.RenderFill) {\n                const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);\n                GetterOverrideY<_Getter> getter2(getter, 0);\n                RenderPrimitives2<RendererShaded>(getter,getter2,col_fill);\n            }\n            if (s.RenderLine) {\n                const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);\n                if (ImHasFlag(flags,ImPlotLineFlags_Segments)) {\n                    RenderPrimitives1<RendererLineSegments1>(getter,col_line,s.LineWeight);\n                }\n                else if (ImHasFlag(flags, ImPlotLineFlags_Loop)) {\n                    if (ImHasFlag(flags, ImPlotLineFlags_SkipNaN))\n                        RenderPrimitives1<RendererLineStripSkip>(GetterLoop<_Getter>(getter),col_line,s.LineWeight);\n                    else\n                        RenderPrimitives1<RendererLineStrip>(GetterLoop<_Getter>(getter),col_line,s.LineWeight);\n                }\n                else {\n                    if (ImHasFlag(flags, ImPlotLineFlags_SkipNaN))\n                        RenderPrimitives1<RendererLineStripSkip>(getter,col_line,s.LineWeight);\n                    else\n                        RenderPrimitives1<RendererLineStrip>(getter,col_line,s.LineWeight);\n                }\n            }\n        }\n        // render markers\n        if (s.Marker != ImPlotMarker_None) {\n            if (ImHasFlag(flags, ImPlotLineFlags_NoClip)) {\n                PopPlotClipRect();\n                PushPlotClipRect(s.MarkerSize);\n            }\n            const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);\n            const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]);\n            RenderMarkers<_Getter>(getter, s.Marker, s.MarkerSize, s.RenderMarkerFill, col_fill, s.RenderMarkerLine, col_line, s.MarkerWeight);\n        }\n        EndItem();\n    }\n}\n\ntemplate <typename T>\nvoid PlotLine(const char* label_id, const T* values, int count, double xscale, double x0, ImPlotLineFlags flags, int offset, int stride) {\n    GetterXY<IndexerLin,IndexerIdx<T>> getter(IndexerLin(xscale,x0),IndexerIdx<T>(values,count,offset,stride),count);\n    PlotLineEx(label_id, getter, flags);\n}\n\ntemplate <typename T>\nvoid PlotLine(const char* label_id, const T* xs, const T* ys, int count, ImPlotLineFlags flags, int offset, int stride) {\n    GetterXY<IndexerIdx<T>,IndexerIdx<T>> getter(IndexerIdx<T>(xs,count,offset,stride),IndexerIdx<T>(ys,count,offset,stride),count);\n    PlotLineEx(label_id, getter, flags);\n}\n\n#define INSTANTIATE_MACRO(T) \\\n    template IMPLOT_API void PlotLine<T> (const char* label_id, const T* values, int count, double xscale, double x0, ImPlotLineFlags flags, int offset, int stride); \\\n    template IMPLOT_API void PlotLine<T>(const char* label_id, const T* xs, const T* ys, int count, ImPlotLineFlags flags, int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n// custom\nvoid PlotLineG(const char* label_id, ImPlotGetter getter_func, void* data, int count, ImPlotLineFlags flags) {\n    GetterFuncPtr getter(getter_func,data, count);\n    PlotLineEx(label_id, getter, flags);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotScatter\n//-----------------------------------------------------------------------------\n\ntemplate <typename Getter>\nvoid PlotScatterEx(const char* label_id, const Getter& getter, ImPlotScatterFlags flags) {\n    if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_MarkerOutline)) {\n        if (getter.Count <= 0) {\n            EndItem();\n            return;\n        }\n        const ImPlotNextItemData& s = GetItemData();\n        ImPlotMarker marker = s.Marker == ImPlotMarker_None ? ImPlotMarker_Circle: s.Marker;\n        if (marker != ImPlotMarker_None) {\n            if (ImHasFlag(flags,ImPlotScatterFlags_NoClip)) {\n                PopPlotClipRect();\n                PushPlotClipRect(s.MarkerSize);\n            }\n            const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);\n            const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]);\n            RenderMarkers<Getter>(getter, marker, s.MarkerSize, s.RenderMarkerFill, col_fill, s.RenderMarkerLine, col_line, s.MarkerWeight);\n        }\n        EndItem();\n    }\n}\n\ntemplate <typename T>\nvoid PlotScatter(const char* label_id, const T* values, int count, double xscale, double x0, ImPlotScatterFlags flags, int offset, int stride) {\n    GetterXY<IndexerLin,IndexerIdx<T>> getter(IndexerLin(xscale,x0),IndexerIdx<T>(values,count,offset,stride),count);\n    PlotScatterEx(label_id, getter, flags);\n}\n\ntemplate <typename T>\nvoid PlotScatter(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags, int offset, int stride) {\n    GetterXY<IndexerIdx<T>,IndexerIdx<T>> getter(IndexerIdx<T>(xs,count,offset,stride),IndexerIdx<T>(ys,count,offset,stride),count);\n    return PlotScatterEx(label_id, getter, flags);\n}\n\n#define INSTANTIATE_MACRO(T) \\\n    template IMPLOT_API void PlotScatter<T>(const char* label_id, const T* values, int count, double xscale, double x0, ImPlotScatterFlags flags, int offset, int stride); \\\n    template IMPLOT_API void PlotScatter<T>(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags, int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n// custom\nvoid PlotScatterG(const char* label_id, ImPlotGetter getter_func, void* data, int count, ImPlotScatterFlags flags) {\n    GetterFuncPtr getter(getter_func,data, count);\n    return PlotScatterEx(label_id, getter, flags);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotStairs\n//-----------------------------------------------------------------------------\n\ntemplate <typename Getter>\nvoid PlotStairsEx(const char* label_id, const Getter& getter, ImPlotStairsFlags flags) {\n    if (BeginItemEx(label_id, Fitter1<Getter>(getter), flags, ImPlotCol_Line)) {\n        if (getter.Count <= 0) {\n            EndItem();\n            return;\n        }\n        const ImPlotNextItemData& s = GetItemData();\n        if (getter.Count > 1) {\n            if (s.RenderFill && ImHasFlag(flags,ImPlotStairsFlags_Shaded)) {\n                const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);\n                if (ImHasFlag(flags, ImPlotStairsFlags_PreStep))\n                    RenderPrimitives1<RendererStairsPreShaded>(getter,col_fill);\n                else\n                    RenderPrimitives1<RendererStairsPostShaded>(getter,col_fill);\n            }\n            if (s.RenderLine) {\n                const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);\n                if (ImHasFlag(flags, ImPlotStairsFlags_PreStep))\n                    RenderPrimitives1<RendererStairsPre>(getter,col_line,s.LineWeight);\n                else\n                    RenderPrimitives1<RendererStairsPost>(getter,col_line,s.LineWeight);\n            }\n        }\n        // render markers\n        if (s.Marker != ImPlotMarker_None) {\n            PopPlotClipRect();\n            PushPlotClipRect(s.MarkerSize);\n            const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);\n            const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]);\n            RenderMarkers<Getter>(getter, s.Marker, s.MarkerSize, s.RenderMarkerFill, col_fill, s.RenderMarkerLine, col_line, s.MarkerWeight);\n        }\n        EndItem();\n    }\n}\n\ntemplate <typename T>\nvoid PlotStairs(const char* label_id, const T* values, int count, double xscale, double x0, ImPlotStairsFlags flags, int offset, int stride) {\n    GetterXY<IndexerLin,IndexerIdx<T>> getter(IndexerLin(xscale,x0),IndexerIdx<T>(values,count,offset,stride),count);\n    PlotStairsEx(label_id, getter, flags);\n}\n\ntemplate <typename T>\nvoid PlotStairs(const char* label_id, const T* xs, const T* ys, int count, ImPlotStairsFlags flags, int offset, int stride) {\n    GetterXY<IndexerIdx<T>,IndexerIdx<T>> getter(IndexerIdx<T>(xs,count,offset,stride),IndexerIdx<T>(ys,count,offset,stride),count);\n    return PlotStairsEx(label_id, getter, flags);\n}\n\n#define INSTANTIATE_MACRO(T) \\\n    template IMPLOT_API void PlotStairs<T> (const char* label_id, const T* values, int count, double xscale, double x0, ImPlotStairsFlags flags, int offset, int stride); \\\n    template IMPLOT_API void PlotStairs<T>(const char* label_id, const T* xs, const T* ys, int count, ImPlotStairsFlags flags, int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n// custom\nvoid PlotStairsG(const char* label_id, ImPlotGetter getter_func, void* data, int count, ImPlotStairsFlags flags) {\n    GetterFuncPtr getter(getter_func,data, count);\n    return PlotStairsEx(label_id, getter, flags);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotShaded\n//-----------------------------------------------------------------------------\n\ntemplate <typename Getter1, typename Getter2>\nvoid PlotShadedEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, ImPlotShadedFlags flags) {\n    if (BeginItemEx(label_id, Fitter2<Getter1,Getter2>(getter1,getter2), flags, ImPlotCol_Fill)) {\n        if (getter1.Count <= 0 || getter2.Count <= 0) {\n            EndItem();\n            return;\n        }\n        const ImPlotNextItemData& s = GetItemData();\n        if (s.RenderFill) {\n            const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);\n            RenderPrimitives2<RendererShaded>(getter1,getter2,col);\n        }\n        EndItem();\n    }\n}\n\ntemplate <typename T>\nvoid PlotShaded(const char* label_id, const T* values, int count, double y_ref, double xscale, double x0, ImPlotShadedFlags flags, int offset, int stride) {\n    if (!(y_ref > -DBL_MAX))\n        y_ref = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO).Y.Min;\n    if (!(y_ref < DBL_MAX))\n        y_ref = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO).Y.Max;\n    GetterXY<IndexerLin,IndexerIdx<T>> getter1(IndexerLin(xscale,x0),IndexerIdx<T>(values,count,offset,stride),count);\n    GetterXY<IndexerLin,IndexerConst>  getter2(IndexerLin(xscale,x0),IndexerConst(y_ref),count);\n    PlotShadedEx(label_id, getter1, getter2, flags);\n}\n\ntemplate <typename T>\nvoid PlotShaded(const char* label_id, const T* xs, const T* ys, int count, double y_ref, ImPlotShadedFlags flags, int offset, int stride) {\n    if (y_ref == -HUGE_VAL)\n        y_ref = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO).Y.Min;\n    if (y_ref == HUGE_VAL)\n        y_ref = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO).Y.Max;\n    GetterXY<IndexerIdx<T>,IndexerIdx<T>> getter1(IndexerIdx<T>(xs,count,offset,stride),IndexerIdx<T>(ys,count,offset,stride),count);\n    GetterXY<IndexerIdx<T>,IndexerConst>  getter2(IndexerIdx<T>(xs,count,offset,stride),IndexerConst(y_ref),count);\n    PlotShadedEx(label_id, getter1, getter2, flags);\n}\n\n\ntemplate <typename T>\nvoid PlotShaded(const char* label_id, const T* xs, const T* ys1, const T* ys2, int count, ImPlotShadedFlags flags, int offset, int stride) {\n    GetterXY<IndexerIdx<T>,IndexerIdx<T>> getter1(IndexerIdx<T>(xs,count,offset,stride),IndexerIdx<T>(ys1,count,offset,stride),count);\n    GetterXY<IndexerIdx<T>,IndexerIdx<T>> getter2(IndexerIdx<T>(xs,count,offset,stride),IndexerIdx<T>(ys2,count,offset,stride),count);\n    PlotShadedEx(label_id, getter1, getter2, flags);\n}\n\n#define INSTANTIATE_MACRO(T) \\\n    template IMPLOT_API void PlotShaded<T>(const char* label_id, const T* values, int count, double y_ref, double xscale, double x0, ImPlotShadedFlags flags, int offset, int stride); \\\n    template IMPLOT_API void PlotShaded<T>(const char* label_id, const T* xs, const T* ys, int count, double y_ref, ImPlotShadedFlags flags, int offset, int stride); \\\n    template IMPLOT_API void PlotShaded<T>(const char* label_id, const T* xs, const T* ys1, const T* ys2, int count, ImPlotShadedFlags flags, int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n// custom\nvoid PlotShadedG(const char* label_id, ImPlotGetter getter_func1, void* data1, ImPlotGetter getter_func2, void* data2, int count, ImPlotShadedFlags flags) {\n    GetterFuncPtr getter1(getter_func1, data1, count);\n    GetterFuncPtr getter2(getter_func2, data2, count);\n    PlotShadedEx(label_id, getter1, getter2, flags);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotBars\n//-----------------------------------------------------------------------------\n\ntemplate <typename Getter1, typename Getter2>\nvoid PlotBarsVEx(const char* label_id, const Getter1& getter1, const Getter2 getter2, double width, ImPlotBarsFlags flags) {\n    if (BeginItemEx(label_id, FitterBarV<Getter1,Getter2>(getter1,getter2,width), flags, ImPlotCol_Fill)) {\n        if (getter1.Count <= 0 || getter2.Count <= 0) {\n            EndItem();\n            return;\n        }\n        const ImPlotNextItemData& s = GetItemData();\n        const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);\n        const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);\n        bool rend_fill = s.RenderFill;\n        bool rend_line = s.RenderLine;\n        if (rend_fill) {\n            RenderPrimitives2<RendererBarsFillV>(getter1,getter2,col_fill,width);\n            if (rend_line && col_fill == col_line)\n                rend_line = false;\n        }\n        if (rend_line) {\n            RenderPrimitives2<RendererBarsLineV>(getter1,getter2,col_line,width,s.LineWeight);\n        }\n        EndItem();\n    }\n}\n\ntemplate <typename Getter1, typename Getter2>\nvoid PlotBarsHEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, double height, ImPlotBarsFlags flags) {\n    if (BeginItemEx(label_id, FitterBarH<Getter1,Getter2>(getter1,getter2,height), flags, ImPlotCol_Fill)) {\n        if (getter1.Count <= 0 || getter2.Count <= 0) {\n            EndItem();\n            return;\n        }\n        const ImPlotNextItemData& s = GetItemData();\n        const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]);\n        const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);\n        bool rend_fill = s.RenderFill;\n        bool rend_line = s.RenderLine;\n        if (rend_fill) {\n            RenderPrimitives2<RendererBarsFillH>(getter1,getter2,col_fill,height);\n            if (rend_line && col_fill == col_line)\n                rend_line = false;\n        }\n        if (rend_line) {\n            RenderPrimitives2<RendererBarsLineH>(getter1,getter2,col_line,height,s.LineWeight);\n        }\n        EndItem();\n    }\n}\n\ntemplate <typename T>\nvoid PlotBars(const char* label_id, const T* values, int count, double bar_size, double shift, ImPlotBarsFlags flags, int offset, int stride) {\n    if (ImHasFlag(flags, ImPlotBarsFlags_Horizontal)) {\n        GetterXY<IndexerIdx<T>,IndexerLin> getter1(IndexerIdx<T>(values,count,offset,stride),IndexerLin(1.0,shift),count);\n        GetterXY<IndexerConst,IndexerLin>  getter2(IndexerConst(0),IndexerLin(1.0,shift),count);\n        PlotBarsHEx(label_id, getter1, getter2, bar_size, flags);\n    }\n    else {\n        GetterXY<IndexerLin,IndexerIdx<T>> getter1(IndexerLin(1.0,shift),IndexerIdx<T>(values,count,offset,stride),count);\n        GetterXY<IndexerLin,IndexerConst>  getter2(IndexerLin(1.0,shift),IndexerConst(0),count);\n        PlotBarsVEx(label_id, getter1, getter2, bar_size, flags);\n    }\n}\n\ntemplate <typename T>\nvoid PlotBars(const char* label_id, const T* xs, const T* ys, int count, double bar_size, ImPlotBarsFlags flags, int offset, int stride) {\n    if (ImHasFlag(flags, ImPlotBarsFlags_Horizontal)) {\n        GetterXY<IndexerIdx<T>,IndexerIdx<T>> getter1(IndexerIdx<T>(xs,count,offset,stride),IndexerIdx<T>(ys,count,offset,stride),count);\n        GetterXY<IndexerConst, IndexerIdx<T>> getter2(IndexerConst(0),IndexerIdx<T>(ys,count,offset,stride),count);\n        PlotBarsHEx(label_id, getter1, getter2, bar_size, flags);\n    }\n    else {\n        GetterXY<IndexerIdx<T>,IndexerIdx<T>> getter1(IndexerIdx<T>(xs,count,offset,stride),IndexerIdx<T>(ys,count,offset,stride),count);\n        GetterXY<IndexerIdx<T>,IndexerConst>  getter2(IndexerIdx<T>(xs,count,offset,stride),IndexerConst(0),count);\n        PlotBarsVEx(label_id, getter1, getter2, bar_size, flags);\n    }\n}\n\n#define INSTANTIATE_MACRO(T) \\\n    template IMPLOT_API void PlotBars<T>(const char* label_id, const T* values, int count, double bar_size, double shift, ImPlotBarsFlags flags, int offset, int stride); \\\n    template IMPLOT_API void PlotBars<T>(const char* label_id, const T* xs, const T* ys, int count, double bar_size, ImPlotBarsFlags flags, int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\nvoid PlotBarsG(const char* label_id, ImPlotGetter getter_func, void* data, int count, double bar_size, ImPlotBarsFlags flags) {\n    if (ImHasFlag(flags, ImPlotBarsFlags_Horizontal)) {\n        GetterFuncPtr getter1(getter_func, data, count);\n        GetterOverrideX<GetterFuncPtr> getter2(getter1,0);\n        PlotBarsHEx(label_id, getter1, getter2, bar_size, flags);\n    }\n    else {\n        GetterFuncPtr getter1(getter_func, data, count);\n        GetterOverrideY<GetterFuncPtr> getter2(getter1,0);\n        PlotBarsVEx(label_id, getter1, getter2, bar_size, flags);\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotBarGroups\n//-----------------------------------------------------------------------------\n\ntemplate <typename T>\nvoid PlotBarGroups(const char* const label_ids[], const T* values, int item_count, int group_count, double group_size, double shift, ImPlotBarGroupsFlags flags) {\n    const bool horz = ImHasFlag(flags, ImPlotBarGroupsFlags_Horizontal);\n    const bool stack = ImHasFlag(flags, ImPlotBarGroupsFlags_Stacked);\n    if (stack) {\n        SetupLock();\n        ImPlotContext& gp = *GImPlot;\n        gp.TempDouble1.resize(4*group_count);\n        double* temp = gp.TempDouble1.Data;\n        double* neg =      &temp[0];\n        double* pos =      &temp[group_count];\n        double* curr_min = &temp[group_count*2];\n        double* curr_max = &temp[group_count*3];\n        for (int g = 0; g < group_count*2; ++g)\n            temp[g] = 0;\n        if (horz) {\n            for (int i = 0; i < item_count; ++i) {\n                if (!IsItemHidden(label_ids[i])) {\n                    for (int g = 0; g < group_count; ++g) {\n                        double v = (double)values[i*group_count+g];\n                        if (v > 0) {\n                            curr_min[g] = pos[g];\n                            curr_max[g] = curr_min[g] + v;\n                            pos[g]      += v;\n                        }\n                        else {\n                            curr_max[g] = neg[g];\n                            curr_min[g] = curr_max[g] + v;\n                            neg[g]      += v;\n                        }\n                    }\n                }\n                GetterXY<IndexerIdx<double>,IndexerLin> getter1(IndexerIdx<double>(curr_min,group_count),IndexerLin(1.0,shift),group_count);\n                GetterXY<IndexerIdx<double>,IndexerLin> getter2(IndexerIdx<double>(curr_max,group_count),IndexerLin(1.0,shift),group_count);\n                PlotBarsHEx(label_ids[i],getter1,getter2,group_size,0);\n            }\n        }\n        else {\n            for (int i = 0; i < item_count; ++i) {\n                if (!IsItemHidden(label_ids[i])) {\n                    for (int g = 0; g < group_count; ++g) {\n                        double v = (double)values[i*group_count+g];\n                        if (v > 0) {\n                            curr_min[g] = pos[g];\n                            curr_max[g] = curr_min[g] + v;\n                            pos[g]      += v;\n                        }\n                        else {\n                            curr_max[g] = neg[g];\n                            curr_min[g] = curr_max[g] + v;\n                            neg[g]      += v;\n                        }\n                    }\n                }\n                GetterXY<IndexerLin,IndexerIdx<double>> getter1(IndexerLin(1.0,shift),IndexerIdx<double>(curr_min,group_count),group_count);\n                GetterXY<IndexerLin,IndexerIdx<double>> getter2(IndexerLin(1.0,shift),IndexerIdx<double>(curr_max,group_count),group_count);\n                PlotBarsVEx(label_ids[i],getter1,getter2,group_size,0);\n            }\n        }\n    }\n    else {\n        const double subsize = group_size / item_count;\n        if (horz) {\n            for (int i = 0; i < item_count; ++i) {\n                const double subshift = (i+0.5)*subsize - group_size/2;\n                PlotBars(label_ids[i],&values[i*group_count],group_count,subsize,subshift+shift,ImPlotBarsFlags_Horizontal);\n            }\n        }\n        else {\n            for (int i = 0; i < item_count; ++i) {\n                const double subshift = (i+0.5)*subsize - group_size/2;\n                PlotBars(label_ids[i],&values[i*group_count],group_count,subsize,subshift+shift);\n            }\n        }\n    }\n}\n\n#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotBarGroups<T>(const char* const label_ids[], const T* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotErrorBars\n//-----------------------------------------------------------------------------\n\ntemplate <typename _GetterPos, typename _GetterNeg>\nvoid PlotErrorBarsVEx(const char* label_id, const _GetterPos& getter_pos, const _GetterNeg& getter_neg, ImPlotErrorBarsFlags flags) {\n    if (BeginItemEx(label_id, Fitter2<_GetterPos,_GetterNeg>(getter_pos, getter_neg), flags, IMPLOT_AUTO)) {\n        if (getter_pos.Count <= 0 || getter_neg.Count <= 0) {\n            EndItem();\n            return;\n        }\n        const ImPlotNextItemData& s = GetItemData();\n        ImDrawList& draw_list = *GetPlotDrawList();\n        const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_ErrorBar]);\n        const bool rend_whisker  = s.ErrorBarSize > 0;\n        const float half_whisker = s.ErrorBarSize * 0.5f;\n        for (int i = 0; i < getter_pos.Count; ++i) {\n            ImVec2 p1 = PlotToPixels(getter_neg(i),IMPLOT_AUTO,IMPLOT_AUTO);\n            ImVec2 p2 = PlotToPixels(getter_pos(i),IMPLOT_AUTO,IMPLOT_AUTO);\n            draw_list.AddLine(p1,p2,col, s.ErrorBarWeight);\n            if (rend_whisker) {\n                draw_list.AddLine(p1 - ImVec2(half_whisker, 0), p1 + ImVec2(half_whisker, 0), col, s.ErrorBarWeight);\n                draw_list.AddLine(p2 - ImVec2(half_whisker, 0), p2 + ImVec2(half_whisker, 0), col, s.ErrorBarWeight);\n            }\n        }\n        EndItem();\n    }\n}\n\ntemplate <typename _GetterPos, typename _GetterNeg>\nvoid PlotErrorBarsHEx(const char* label_id, const _GetterPos& getter_pos, const _GetterNeg& getter_neg, ImPlotErrorBarsFlags flags) {\n    if (BeginItemEx(label_id, Fitter2<_GetterPos,_GetterNeg>(getter_pos, getter_neg), flags, IMPLOT_AUTO)) {\n        if (getter_pos.Count <= 0 || getter_neg.Count <= 0) {\n            EndItem();\n            return;\n        }\n        const ImPlotNextItemData& s = GetItemData();\n        ImDrawList& draw_list = *GetPlotDrawList();\n        const ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_ErrorBar]);\n        const bool rend_whisker  = s.ErrorBarSize > 0;\n        const float half_whisker = s.ErrorBarSize * 0.5f;\n        for (int i = 0; i < getter_pos.Count; ++i) {\n            ImVec2 p1 = PlotToPixels(getter_neg(i),IMPLOT_AUTO,IMPLOT_AUTO);\n            ImVec2 p2 = PlotToPixels(getter_pos(i),IMPLOT_AUTO,IMPLOT_AUTO);\n            draw_list.AddLine(p1, p2, col, s.ErrorBarWeight);\n            if (rend_whisker) {\n                draw_list.AddLine(p1 - ImVec2(0, half_whisker), p1 + ImVec2(0, half_whisker), col, s.ErrorBarWeight);\n                draw_list.AddLine(p2 - ImVec2(0, half_whisker), p2 + ImVec2(0, half_whisker), col, s.ErrorBarWeight);\n            }\n        }\n        EndItem();\n    }\n}\n\ntemplate <typename T>\nvoid PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, ImPlotErrorBarsFlags flags, int offset, int stride) {\n    PlotErrorBars(label_id, xs, ys, err, err, count, flags, offset, stride);\n}\n\ntemplate <typename T>\nvoid PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, ImPlotErrorBarsFlags flags, int offset, int stride) {\n    IndexerIdx<T> indexer_x(xs, count,offset,stride);\n    IndexerIdx<T> indexer_y(ys, count,offset,stride);\n    IndexerIdx<T> indexer_n(neg,count,offset,stride);\n    IndexerIdx<T> indexer_p(pos,count,offset,stride);\n    GetterError<T> getter(xs, ys, neg, pos, count, offset, stride);\n    if (ImHasFlag(flags, ImPlotErrorBarsFlags_Horizontal)) {\n        IndexerAdd<IndexerIdx<T>,IndexerIdx<T>> indexer_xp(indexer_x, indexer_p, 1,  1);\n        IndexerAdd<IndexerIdx<T>,IndexerIdx<T>> indexer_xn(indexer_x, indexer_n, 1, -1);\n        GetterXY<IndexerAdd<IndexerIdx<T>,IndexerIdx<T>>,IndexerIdx<T>> getter_p(indexer_xp, indexer_y, count);\n        GetterXY<IndexerAdd<IndexerIdx<T>,IndexerIdx<T>>,IndexerIdx<T>> getter_n(indexer_xn, indexer_y, count);\n        PlotErrorBarsHEx(label_id, getter_p, getter_n, flags);\n    }\n    else {\n        IndexerAdd<IndexerIdx<T>,IndexerIdx<T>> indexer_yp(indexer_y, indexer_p, 1,  1);\n        IndexerAdd<IndexerIdx<T>,IndexerIdx<T>> indexer_yn(indexer_y, indexer_n, 1, -1);\n        GetterXY<IndexerIdx<T>,IndexerAdd<IndexerIdx<T>,IndexerIdx<T>>> getter_p(indexer_x, indexer_yp, count);\n        GetterXY<IndexerIdx<T>,IndexerAdd<IndexerIdx<T>,IndexerIdx<T>>> getter_n(indexer_x, indexer_yn, count);\n        PlotErrorBarsVEx(label_id, getter_p, getter_n, flags);\n    }\n}\n\n#define INSTANTIATE_MACRO(T) \\\n    template IMPLOT_API void PlotErrorBars<T>(const char* label_id, const T* xs, const T* ys, const T* err, int count, ImPlotErrorBarsFlags flags, int offset, int stride); \\\n    template IMPLOT_API void PlotErrorBars<T>(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, ImPlotErrorBarsFlags flags, int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotStems\n//-----------------------------------------------------------------------------\n\ntemplate <typename _GetterM, typename _GetterB>\nvoid PlotStemsEx(const char* label_id, const _GetterM& getter_mark, const _GetterB& getter_base, ImPlotStemsFlags flags) {\n    if (BeginItemEx(label_id, Fitter2<_GetterM,_GetterB>(getter_mark,getter_base), flags, ImPlotCol_Line)) {\n        if (getter_mark.Count <= 0 || getter_base.Count <= 0) {\n            EndItem();\n            return;\n        }\n        const ImPlotNextItemData& s = GetItemData();\n        // render stems\n        if (s.RenderLine) {\n            const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);\n            RenderPrimitives2<RendererLineSegments2>(getter_mark, getter_base, col_line, s.LineWeight);\n        }\n        // render markers\n        if (s.Marker != ImPlotMarker_None) {\n            PopPlotClipRect();\n            PushPlotClipRect(s.MarkerSize);\n            const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]);\n            const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]);\n            RenderMarkers<_GetterM>(getter_mark, s.Marker, s.MarkerSize, s.RenderMarkerFill, col_fill, s.RenderMarkerLine, col_line, s.MarkerWeight);\n        }\n        EndItem();\n    }\n}\n\ntemplate <typename T>\nvoid PlotStems(const char* label_id, const T* values, int count, double ref, double scale, double start, ImPlotStemsFlags flags, int offset, int stride) {\n    if (ImHasFlag(flags, ImPlotStemsFlags_Horizontal)) {\n        GetterXY<IndexerIdx<T>,IndexerLin> get_mark(IndexerIdx<T>(values,count,offset,stride),IndexerLin(scale,start),count);\n        GetterXY<IndexerConst,IndexerLin>  get_base(IndexerConst(ref),IndexerLin(scale,start),count);\n        PlotStemsEx(label_id, get_mark, get_base, flags);\n    }\n    else {\n        GetterXY<IndexerLin,IndexerIdx<T>> get_mark(IndexerLin(scale,start),IndexerIdx<T>(values,count,offset,stride),count);\n        GetterXY<IndexerLin,IndexerConst>  get_base(IndexerLin(scale,start),IndexerConst(ref),count);\n        PlotStemsEx(label_id, get_mark, get_base, flags);\n    }\n}\n\ntemplate <typename T>\nvoid PlotStems(const char* label_id, const T* xs, const T* ys, int count, double ref, ImPlotStemsFlags flags, int offset, int stride) {\n    if (ImHasFlag(flags, ImPlotStemsFlags_Horizontal)) {\n        GetterXY<IndexerIdx<T>,IndexerIdx<T>> get_mark(IndexerIdx<T>(xs,count,offset,stride),IndexerIdx<T>(ys,count,offset,stride),count);\n        GetterXY<IndexerConst,IndexerIdx<T>>  get_base(IndexerConst(ref),IndexerIdx<T>(ys,count,offset,stride),count);\n        PlotStemsEx(label_id, get_mark, get_base, flags);\n    }\n    else {\n        GetterXY<IndexerIdx<T>,IndexerIdx<T>> get_mark(IndexerIdx<T>(xs,count,offset,stride),IndexerIdx<T>(ys,count,offset,stride),count);\n        GetterXY<IndexerIdx<T>,IndexerConst>  get_base(IndexerIdx<T>(xs,count,offset,stride),IndexerConst(ref),count);\n        PlotStemsEx(label_id, get_mark, get_base, flags);\n    }\n}\n\n#define INSTANTIATE_MACRO(T) \\\n    template IMPLOT_API void PlotStems<T>(const char* label_id, const T* values, int count, double ref, double scale, double start, ImPlotStemsFlags flags, int offset, int stride); \\\n    template IMPLOT_API void PlotStems<T>(const char* label_id, const T* xs, const T* ys, int count, double ref, ImPlotStemsFlags flags, int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotInfLines\n//-----------------------------------------------------------------------------\n\ntemplate <typename T>\nvoid PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags, int offset, int stride) {\n    const ImPlotRect lims = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO);\n    if (ImHasFlag(flags, ImPlotInfLinesFlags_Horizontal)) {\n        GetterXY<IndexerConst,IndexerIdx<T>> getter_min(IndexerConst(lims.X.Min),IndexerIdx<T>(values,count,offset,stride),count);\n        GetterXY<IndexerConst,IndexerIdx<T>> getter_max(IndexerConst(lims.X.Max),IndexerIdx<T>(values,count,offset,stride),count);\n        if (BeginItemEx(label_id, FitterY<GetterXY<IndexerConst,IndexerIdx<T>>>(getter_min), flags, ImPlotCol_Line)) {\n            if (count <= 0) {\n                EndItem();\n                return;\n            }\n            const ImPlotNextItemData& s = GetItemData();\n            const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);\n            if (s.RenderLine)\n                RenderPrimitives2<RendererLineSegments2>(getter_min, getter_max, col_line, s.LineWeight);\n            EndItem();\n        }\n    }\n    else {\n        GetterXY<IndexerIdx<T>,IndexerConst> get_min(IndexerIdx<T>(values,count,offset,stride),IndexerConst(lims.Y.Min),count);\n        GetterXY<IndexerIdx<T>,IndexerConst> get_max(IndexerIdx<T>(values,count,offset,stride),IndexerConst(lims.Y.Max),count);\n        if (BeginItemEx(label_id, FitterX<GetterXY<IndexerIdx<T>,IndexerConst>>(get_min), flags, ImPlotCol_Line)) {\n            if (count <= 0) {\n                EndItem();\n                return;\n            }\n            const ImPlotNextItemData& s = GetItemData();\n            const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]);\n            if (s.RenderLine)\n                RenderPrimitives2<RendererLineSegments2>(get_min, get_max, col_line, s.LineWeight);\n            EndItem();\n        }\n    }\n}\n#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotInfLines<T>(const char* label_id, const T* xs, int count, ImPlotInfLinesFlags flags, int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotPieChart\n//-----------------------------------------------------------------------------\n\nIMPLOT_INLINE void RenderPieSlice(ImDrawList& draw_list, const ImPlotPoint& center, double radius, double a0, double a1, ImU32 col) {\n    const float resolution = 50 / (2 * IM_PI);\n    ImVec2 buffer[52];\n    buffer[0] = PlotToPixels(center,IMPLOT_AUTO,IMPLOT_AUTO);\n    int n = ImMax(3, (int)((a1 - a0) * resolution));\n    double da = (a1 - a0) / (n - 1);\n    int i = 0;\n    for (; i < n; ++i) {\n        double a = a0 + i * da;\n        buffer[i + 1] = PlotToPixels(center.x + radius * cos(a), center.y + radius * sin(a),IMPLOT_AUTO,IMPLOT_AUTO);\n    }\n    buffer[i+1] = buffer[0];\n    // fill\n    draw_list.AddConvexPolyFilled(buffer, n + 1, col);\n    // border (for AA)\n    draw_list.AddPolyline(buffer, n + 2, col, 0, 2.0f);\n}\n\ntemplate <typename T>\ndouble PieChartSum(const T* values, int count, bool ignore_hidden) {\n    double sum = 0;\n    if (ignore_hidden) {\n        ImPlotContext& gp = *GImPlot;\n        ImPlotItemGroup& Items = *gp.CurrentItems;\n        for (int i = 0; i < count; ++i) {\n            if (i >= Items.GetItemCount())\n                break;\n\n            ImPlotItem* item = Items.GetItemByIndex(i);\n            IM_ASSERT(item != nullptr);\n            if (item->Show) {\n                sum += (double)values[i];\n            }\n        }\n    }\n    else {\n        for (int i = 0; i < count; ++i) {\n            sum += (double)values[i];\n        }\n    }\n    return sum;\n}\n\ntemplate <typename T>\nvoid PlotPieChartEx(const char* const label_ids[], const T* values, int count, ImPlotPoint center, double radius, double angle0, ImPlotPieChartFlags flags) {\n    ImDrawList& draw_list  = *GetPlotDrawList();\n    \n    const bool ignore_hidden = ImHasFlag(flags, ImPlotPieChartFlags_IgnoreHidden);\n    const double sum         = PieChartSum(values, count, ignore_hidden);\n    const bool normalize     = ImHasFlag(flags, ImPlotPieChartFlags_Normalize) || sum > 1.0;\n    \n    double a0 = angle0 * 2 * IM_PI / 360.0;\n    double a1 = angle0 * 2 * IM_PI / 360.0;\n    ImPlotPoint Pmin = ImPlotPoint(center.x - radius, center.y - radius);\n    ImPlotPoint Pmax = ImPlotPoint(center.x + radius, center.y + radius);\n    for (int i = 0; i < count; ++i) {\n        ImPlotItem* item = GetItem(label_ids[i]);\n\n        const double percent = normalize ? (double)values[i] / sum : (double)values[i];\n        const bool skip      = sum <= 0.0 || (ignore_hidden && item != nullptr && !item->Show);\n        if (!skip)\n            a1 = a0 + 2 * IM_PI * percent;\n\n        if (BeginItemEx(label_ids[i], FitterRect(Pmin, Pmax))) {\n            if (sum > 0.0) {\n                ImU32 col = GetCurrentItem()->Color;\n                if (percent < 0.5) {\n                    RenderPieSlice(draw_list, center, radius, a0, a1, col);\n                }\n                else {\n                    RenderPieSlice(draw_list, center, radius, a0, a0 + (a1 - a0) * 0.5, col);\n                    RenderPieSlice(draw_list, center, radius, a0 + (a1 - a0) * 0.5, a1, col);\n                }\n            }\n            EndItem();\n        }\n        if (!skip)\n            a0 = a1;\n    }\n}\n\nint PieChartFormatter(double value, char* buff, int size, void* data) {\n    const char* fmt = (const char*)data;\n    return snprintf(buff, size, fmt, value);\n};\n\ntemplate <typename T>\nvoid PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* fmt, double angle0, ImPlotPieChartFlags flags) {\n    PlotPieChart<T>(label_ids, values, count, x, y, radius, PieChartFormatter, (void*)fmt, angle0, flags);\n}\n#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotPieChart<T>(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* fmt, double angle0, ImPlotPieChartFlags flags);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\ntemplate <typename T>\nvoid PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data, double angle0, ImPlotPieChartFlags flags) {\n    IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, \"PlotPieChart() needs to be called between BeginPlot() and EndPlot()!\");\n    ImDrawList& draw_list = *GetPlotDrawList();\n\n    const bool ignore_hidden = ImHasFlag(flags, ImPlotPieChartFlags_IgnoreHidden);\n    const double sum = PieChartSum(values, count, ignore_hidden);\n    const bool normalize = ImHasFlag(flags, ImPlotPieChartFlags_Normalize) || sum > 1.0;\n    ImPlotPoint center(x, y);\n\n    PushPlotClipRect();\n    PlotPieChartEx(label_ids, values, count, center, radius, angle0, flags);\n    if (fmt != nullptr) {\n        double a0 = angle0 * 2 * IM_PI / 360.0;\n        double a1 = angle0 * 2 * IM_PI / 360.0;\n        char buffer[32];\n        for (int i = 0; i < count; ++i) {\n            ImPlotItem* item = GetItem(label_ids[i]);\n            IM_ASSERT(item != nullptr);\n\n            const double percent = normalize ? (double)values[i] / sum : (double)values[i];\n            const bool skip = ignore_hidden && item != nullptr && !item->Show;\n\n            if (!skip) {\n                a1 = a0 + 2 * IM_PI * percent;\n                if (item->Show) {\n                    fmt((double)values[i], buffer, 32, fmt_data);\n                    ImVec2 size = ImGui::CalcTextSize(buffer);\n                    double angle = a0 + (a1 - a0) * 0.5;\n                    ImVec2 pos = PlotToPixels(center.x + 0.5 * radius * cos(angle), center.y + 0.5 * radius * sin(angle), IMPLOT_AUTO, IMPLOT_AUTO);\n                    ImU32 col = CalcTextColor(ImGui::ColorConvertU32ToFloat4(item->Color));\n                    draw_list.AddText(pos - size * 0.5f, col, buffer);\n                }\n                a0 = a1;\n            }\n        }\n    }\n    PopPlotClipRect();\n}\n#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data, double angle0, ImPlotPieChartFlags flags);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotHeatmap\n//-----------------------------------------------------------------------------\n\ntemplate <typename T>\nstruct GetterHeatmapRowMaj {\n    GetterHeatmapRowMaj(const T* values, int rows, int cols, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) :\n        Values(values),\n        Count(rows*cols),\n        Rows(rows),\n        Cols(cols),\n        ScaleMin(scale_min),\n        ScaleMax(scale_max),\n        Width(width),\n        Height(height),\n        XRef(xref),\n        YRef(yref),\n        YDir(ydir),\n        HalfSize(Width*0.5, Height*0.5)\n    { }\n    template <typename I> IMPLOT_INLINE RectC operator()(I idx) const {\n        double val = (double)Values[idx];\n        const int r = idx / Cols;\n        const int c = idx % Cols;\n        const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height));\n        RectC rect;\n        rect.Pos = p;\n        rect.HalfSize = HalfSize;\n        const float t = ImClamp((float)ImRemap01(val, ScaleMin, ScaleMax),0.0f,1.0f);\n        ImPlotContext& gp = *GImPlot;\n        rect.Color = gp.ColormapData.LerpTable(gp.Style.Colormap, t);\n        return rect;\n    }\n    const T* const Values;\n    const int Count, Rows, Cols;\n    const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir;\n    const ImPlotPoint HalfSize;\n};\n\ntemplate <typename T>\nstruct GetterHeatmapColMaj {\n    GetterHeatmapColMaj(const T* values, int rows, int cols, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) :\n        Values(values),\n        Count(rows*cols),\n        Rows(rows),\n        Cols(cols),\n        ScaleMin(scale_min),\n        ScaleMax(scale_max),\n        Width(width),\n        Height(height),\n        XRef(xref),\n        YRef(yref),\n        YDir(ydir),\n        HalfSize(Width*0.5, Height*0.5)\n    { }\n    template <typename I> IMPLOT_INLINE RectC operator()(I idx) const {\n        double val = (double)Values[idx];\n        const int r = idx % Rows;\n        const int c = idx / Rows;\n        const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height));\n        RectC rect;\n        rect.Pos = p;\n        rect.HalfSize = HalfSize;\n        const float t = ImClamp((float)ImRemap01(val, ScaleMin, ScaleMax),0.0f,1.0f);\n        ImPlotContext& gp = *GImPlot;\n        rect.Color = gp.ColormapData.LerpTable(gp.Style.Colormap, t);\n        return rect;\n    }\n    const T* const Values;\n    const int Count, Rows, Cols;\n    const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir;\n    const ImPlotPoint HalfSize;\n};\n\ntemplate <typename T>\nvoid RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, bool reverse_y, bool col_maj) {\n    ImPlotContext& gp = *GImPlot;\n    Transformer2 transformer;\n    if (scale_min == 0 && scale_max == 0) {\n        T temp_min, temp_max;\n        ImMinMaxArray(values,rows*cols,&temp_min,&temp_max);\n        scale_min = (double)temp_min;\n        scale_max = (double)temp_max;\n    }\n    if (scale_min == scale_max) {\n        ImVec2 a = transformer(bounds_min);\n        ImVec2 b = transformer(bounds_max);\n        ImU32  col = GetColormapColorU32(0,gp.Style.Colormap);\n        draw_list.AddRectFilled(a, b, col);\n        return;\n    }\n    const double yref = reverse_y ? bounds_max.y : bounds_min.y;\n    const double ydir = reverse_y ? -1 : 1;\n    if (col_maj) {\n        GetterHeatmapColMaj<T> getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir);\n        RenderPrimitives1<RendererRectC>(getter);\n    }\n    else {\n        GetterHeatmapRowMaj<T> getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir);\n        RenderPrimitives1<RendererRectC>(getter);\n    }\n    // labels\n    if (fmt != nullptr) {\n        const double w = (bounds_max.x - bounds_min.x) / cols;\n        const double h = (bounds_max.y - bounds_min.y) / rows;\n        const ImPlotPoint half_size(w*0.5,h*0.5);\n        int i = 0;\n        if (col_maj) {\n            for (int c = 0; c < cols; ++c) {\n                for (int r = 0; r < rows; ++r) {\n                    ImPlotPoint p;\n                    p.x = bounds_min.x + 0.5*w + c*w;\n                    p.y = yref + ydir * (0.5*h + r*h);\n                    ImVec2 px = transformer(p);\n                    char buff[32];\n                    ImFormatString(buff, 32, fmt, values[i]);\n                    ImVec2 size = ImGui::CalcTextSize(buff);\n                    double t = ImClamp(ImRemap01((double)values[i], scale_min, scale_max),0.0,1.0);\n                    ImVec4 color = SampleColormap((float)t);\n                    ImU32 col = CalcTextColor(color);\n                    draw_list.AddText(px - size * 0.5f, col, buff);\n                    i++;\n                }\n            }\n        }\n        else {\n            for (int r = 0; r < rows; ++r) {\n                for (int c = 0; c < cols; ++c) {\n                    ImPlotPoint p;\n                    p.x = bounds_min.x + 0.5*w + c*w;\n                    p.y = yref + ydir * (0.5*h + r*h);\n                    ImVec2 px = transformer(p);\n                    char buff[32];\n                    ImFormatString(buff, 32, fmt, values[i]);\n                    ImVec2 size = ImGui::CalcTextSize(buff);\n                    double t = ImClamp(ImRemap01((double)values[i], scale_min, scale_max),0.0,1.0);\n                    ImVec4 color = SampleColormap((float)t);\n                    ImU32 col = CalcTextColor(color);\n                    draw_list.AddText(px - size * 0.5f, col, buff);\n                    i++;\n                }\n            }\n        }\n    }\n}\n\ntemplate <typename T>\nvoid PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags) {\n    if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) {\n        if (rows <= 0 || cols <= 0) {\n            EndItem();\n            return;\n        }\n        ImDrawList& draw_list = *GetPlotDrawList();\n        const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor);\n        RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj);\n        EndItem();\n    }\n}\n#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotHeatmap<T>(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotHistogram\n//-----------------------------------------------------------------------------\n\ntemplate <typename T>\ndouble PlotHistogram(const char* label_id, const T* values, int count, int bins, double bar_scale, ImPlotRange range, ImPlotHistogramFlags flags) {\n\n    const bool cumulative = ImHasFlag(flags, ImPlotHistogramFlags_Cumulative);\n    const bool density    = ImHasFlag(flags, ImPlotHistogramFlags_Density);\n    const bool outliers   = !ImHasFlag(flags, ImPlotHistogramFlags_NoOutliers);\n\n    if (count <= 0 || bins == 0)\n        return 0;\n\n    if (range.Min == 0 && range.Max == 0) {\n        T Min, Max;\n        ImMinMaxArray(values, count, &Min, &Max);\n        range.Min = (double)Min;\n        range.Max = (double)Max;\n    }\n\n    double width;\n    if (bins < 0)\n        CalculateBins(values, count, bins, range, bins, width);\n    else\n        width = range.Size() / bins;\n\n    ImPlotContext& gp = *GImPlot;\n    ImVector<double>& bin_centers = gp.TempDouble1;\n    ImVector<double>& bin_counts  = gp.TempDouble2;\n    bin_centers.resize(bins);\n    bin_counts.resize(bins);\n    int below = 0;\n\n    for (int b = 0; b < bins; ++b) {\n        bin_centers[b] = range.Min + b * width + width * 0.5;\n        bin_counts[b] = 0;\n    }\n    int counted = 0;\n    double max_count = 0;\n    for (int i = 0; i < count; ++i) {\n        double val = (double)values[i];\n        if (range.Contains(val)) {\n            const int b = ImClamp((int)((val - range.Min) / width), 0, bins - 1);\n            bin_counts[b] += 1.0;\n            if (bin_counts[b] > max_count)\n                max_count = bin_counts[b];\n            counted++;\n        }\n        else if (val < range.Min) {\n            below++;\n        }\n    }\n    if (cumulative && density) {\n        if (outliers)\n            bin_counts[0] += below;\n        for (int b = 1; b < bins; ++b)\n            bin_counts[b] += bin_counts[b-1];\n        double scale = 1.0 / (outliers ? count : counted);\n        for (int b = 0; b < bins; ++b)\n            bin_counts[b] *= scale;\n        max_count = bin_counts[bins-1];\n    }\n    else if (cumulative) {\n        if (outliers)\n            bin_counts[0] += below;\n        for (int b = 1; b < bins; ++b)\n            bin_counts[b] += bin_counts[b-1];\n        max_count = bin_counts[bins-1];\n    }\n    else if (density) {\n        double scale = 1.0 / ((outliers ? count : counted) * width);\n        for (int b = 0; b < bins; ++b)\n            bin_counts[b] *= scale;\n        max_count *= scale;\n    }\n    if (ImHasFlag(flags, ImPlotHistogramFlags_Horizontal))\n        PlotBars(label_id, &bin_counts.Data[0], &bin_centers.Data[0], bins, bar_scale*width, ImPlotBarsFlags_Horizontal);\n    else\n        PlotBars(label_id, &bin_centers.Data[0], &bin_counts.Data[0], bins, bar_scale*width);\n    return max_count;\n}\n#define INSTANTIATE_MACRO(T) template IMPLOT_API double PlotHistogram<T>(const char* label_id, const T* values, int count, int bins, double bar_scale, ImPlotRange range, ImPlotHistogramFlags flags);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotHistogram2D\n//-----------------------------------------------------------------------------\n\ntemplate <typename T>\ndouble PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, ImPlotRect range, ImPlotHistogramFlags flags) {\n\n    // const bool cumulative = ImHasFlag(flags, ImPlotHistogramFlags_Cumulative); NOT SUPPORTED\n    const bool density  = ImHasFlag(flags, ImPlotHistogramFlags_Density);\n    const bool outliers = !ImHasFlag(flags, ImPlotHistogramFlags_NoOutliers);\n    const bool col_maj  = ImHasFlag(flags, ImPlotHistogramFlags_ColMajor);\n\n    if (count <= 0 || x_bins == 0 || y_bins == 0)\n        return 0;\n\n    if (range.X.Min == 0 && range.X.Max == 0) {\n        T Min, Max;\n        ImMinMaxArray(xs, count, &Min, &Max);\n        range.X.Min = (double)Min;\n        range.X.Max = (double)Max;\n    }\n    if (range.Y.Min == 0 && range.Y.Max == 0) {\n        T Min, Max;\n        ImMinMaxArray(ys, count, &Min, &Max);\n        range.Y.Min = (double)Min;\n        range.Y.Max = (double)Max;\n    }\n\n    double width, height;\n    if (x_bins < 0)\n        CalculateBins(xs, count, x_bins, range.X, x_bins, width);\n    else\n        width = range.X.Size() / x_bins;\n    if (y_bins < 0)\n        CalculateBins(ys, count, y_bins, range.Y, y_bins, height);\n    else\n        height = range.Y.Size() / y_bins;\n\n    const int bins = x_bins * y_bins;\n\n    ImPlotContext& gp = *GImPlot;\n    ImVector<double>& bin_counts = gp.TempDouble1;\n    bin_counts.resize(bins);\n\n    for (int b = 0; b < bins; ++b)\n        bin_counts[b] = 0;\n\n    int counted = 0;\n    double max_count = 0;\n    for (int i = 0; i < count; ++i) {\n        if (range.Contains((double)xs[i], (double)ys[i])) {\n            const int xb = ImClamp( (int)((double)(xs[i] - range.X.Min) / width)  , 0, x_bins - 1);\n            const int yb = ImClamp( (int)((double)(ys[i] - range.Y.Min) / height) , 0, y_bins - 1);\n            const int b  = yb * x_bins + xb;\n            bin_counts[b] += 1.0;\n            if (bin_counts[b] > max_count)\n                max_count = bin_counts[b];\n            counted++;\n        }\n    }\n    if (density) {\n        double scale = 1.0 / ((outliers ? count : counted) * width * height);\n        for (int b = 0; b < bins; ++b)\n            bin_counts[b] *= scale;\n        max_count *= scale;\n    }\n\n    if (BeginItemEx(label_id, FitterRect(range))) {\n        if (y_bins <= 0 || x_bins <= 0) {\n            EndItem();\n            return max_count;\n        }\n        ImDrawList& draw_list = *GetPlotDrawList();\n        RenderHeatmap(draw_list, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, nullptr, range.Min(), range.Max(), false, col_maj);\n        EndItem();\n    }\n    return max_count;\n}\n#define INSTANTIATE_MACRO(T) template IMPLOT_API double PlotHistogram2D<T>(const char* label_id,   const T*   xs, const T*   ys, int count, int x_bins, int y_bins, ImPlotRect range, ImPlotHistogramFlags flags);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotDigital\n//-----------------------------------------------------------------------------\n\n// TODO: Make this behave like all the other plot types (.e. not fixed in y axis)\n\ntemplate <typename Getter>\nvoid PlotDigitalEx(const char* label_id, Getter getter, ImPlotDigitalFlags flags) {\n    if (BeginItem(label_id, flags, ImPlotCol_Fill)) {\n        ImPlotContext& gp = *GImPlot;\n        ImDrawList& draw_list = *GetPlotDrawList();\n        const ImPlotNextItemData& s = GetItemData();\n        if (getter.Count > 1 && s.RenderFill) {\n            ImPlotPlot& plot   = *gp.CurrentPlot;\n            ImPlotAxis& x_axis = plot.Axes[plot.CurrentX];\n            ImPlotAxis& y_axis = plot.Axes[plot.CurrentY];\n\n            int pixYMax = 0;\n            ImPlotPoint itemData1 = getter(0);\n            for (int i = 0; i < getter.Count; ++i) {\n                ImPlotPoint itemData2 = getter(i);\n                if (ImNanOrInf(itemData1.y)) {\n                    itemData1 = itemData2;\n                    continue;\n                }\n                if (ImNanOrInf(itemData2.y)) itemData2.y = ImConstrainNan(ImConstrainInf(itemData2.y));\n                int pixY_0 = (int)(s.LineWeight);\n                itemData1.y = ImMax(0.0, itemData1.y);\n                float pixY_1_float = s.DigitalBitHeight * (float)itemData1.y;\n                int pixY_1 = (int)(pixY_1_float); //allow only positive values\n                int pixY_chPosOffset = (int)(ImMax(s.DigitalBitHeight, pixY_1_float) + s.DigitalBitGap);\n                pixYMax = ImMax(pixYMax, pixY_chPosOffset);\n                ImVec2 pMin = PlotToPixels(itemData1,IMPLOT_AUTO,IMPLOT_AUTO);\n                ImVec2 pMax = PlotToPixels(itemData2,IMPLOT_AUTO,IMPLOT_AUTO);\n                int pixY_Offset = 0; //20 pixel from bottom due to mouse cursor label\n                pMin.y = (y_axis.PixelMin) + ((-gp.DigitalPlotOffset)                   - pixY_Offset);\n                pMax.y = (y_axis.PixelMin) + ((-gp.DigitalPlotOffset) - pixY_0 - pixY_1 - pixY_Offset);\n                //plot only one rectangle for same digital state\n                while (((i+2) < getter.Count) && (itemData1.y == itemData2.y)) {\n                    const int in = (i + 1);\n                    itemData2 = getter(in);\n                    if (ImNanOrInf(itemData2.y)) break;\n                    pMax.x = PlotToPixels(itemData2,IMPLOT_AUTO,IMPLOT_AUTO).x;\n                    i++;\n                }\n                //do not extend plot outside plot range\n                if (pMin.x < x_axis.PixelMin) pMin.x = x_axis.PixelMin;\n                if (pMax.x < x_axis.PixelMin) pMax.x = x_axis.PixelMin;\n                if (pMin.x > x_axis.PixelMax) pMin.x = x_axis.PixelMax - 1; //fix issue related to https://github.com/ocornut/imgui/issues/3976\n                if (pMax.x > x_axis.PixelMax) pMax.x = x_axis.PixelMax - 1; //fix issue related to https://github.com/ocornut/imgui/issues/3976\n                //plot a rectangle that extends up to x2 with y1 height\n                if ((pMax.x > pMin.x) && (gp.CurrentPlot->PlotRect.Contains(pMin) || gp.CurrentPlot->PlotRect.Contains(pMax))) {\n                    // ImVec4 colAlpha = item->Color;\n                    // colAlpha.w = item->Highlight ? 1.0f : 0.9f;\n                    draw_list.AddRectFilled(pMin, pMax, ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]));\n                }\n                itemData1 = itemData2;\n            }\n            gp.DigitalPlotItemCnt++;\n            gp.DigitalPlotOffset += pixYMax;\n        }\n        EndItem();\n    }\n}\n\n\ntemplate <typename T>\nvoid PlotDigital(const char* label_id, const T* xs, const T* ys, int count, ImPlotDigitalFlags flags, int offset, int stride) {\n    GetterXY<IndexerIdx<T>,IndexerIdx<T>> getter(IndexerIdx<T>(xs,count,offset,stride),IndexerIdx<T>(ys,count,offset,stride),count);\n    return PlotDigitalEx(label_id, getter, flags);\n}\n#define INSTANTIATE_MACRO(T) template IMPLOT_API void PlotDigital<T>(const char* label_id, const T* xs, const T* ys, int count, ImPlotDigitalFlags flags, int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n// custom\nvoid PlotDigitalG(const char* label_id, ImPlotGetter getter_func, void* data, int count, ImPlotDigitalFlags flags) {\n    GetterFuncPtr getter(getter_func,data,count);\n    return PlotDigitalEx(label_id, getter, flags);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotImage\n//-----------------------------------------------------------------------------\n\n#ifdef IMGUI_HAS_TEXTURES\n    void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlotPoint& bmin, const ImPlotPoint& bmax, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, ImPlotImageFlags) {\n#else\n    void PlotImage(const char* label_id, ImTextureID tex_ref, const ImPlotPoint& bmin, const ImPlotPoint& bmax, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, ImPlotImageFlags) {\n#endif\n        if (BeginItemEx(label_id, FitterRect(bmin,bmax))) {\n        ImU32 tint_col32 = ImGui::ColorConvertFloat4ToU32(tint_col);\n        GetCurrentItem()->Color = tint_col32;\n        ImDrawList& draw_list = *GetPlotDrawList();\n        ImVec2 p1 = PlotToPixels(bmin.x, bmax.y,IMPLOT_AUTO,IMPLOT_AUTO);\n        ImVec2 p2 = PlotToPixels(bmax.x, bmin.y,IMPLOT_AUTO,IMPLOT_AUTO);\n        PushPlotClipRect();\n        draw_list.AddImage(tex_ref, p1, p2, uv0, uv1, tint_col32);\n        PopPlotClipRect();\n        EndItem();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotText\n//-----------------------------------------------------------------------------\n\nvoid PlotText(const char* text, double x, double y, const ImVec2& pixel_offset, ImPlotTextFlags flags) {\n    IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, \"PlotText() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    ImDrawList & draw_list = *GetPlotDrawList();\n    PushPlotClipRect();\n    ImU32 colTxt = GetStyleColorU32(ImPlotCol_InlayText);\n    if (ImHasFlag(flags,ImPlotTextFlags_Vertical)) {\n        ImVec2 siz = CalcTextSizeVertical(text) * 0.5f;\n        ImVec2 ctr = siz * 0.5f;\n        ImVec2 pos = PlotToPixels(ImPlotPoint(x,y),IMPLOT_AUTO,IMPLOT_AUTO) + ImVec2(-ctr.x, ctr.y) + pixel_offset;\n        if (FitThisFrame() && !ImHasFlag(flags, ImPlotItemFlags_NoFit)) {\n            FitPoint(PixelsToPlot(pos));\n            FitPoint(PixelsToPlot(pos.x + siz.x, pos.y - siz.y));\n        }\n        AddTextVertical(&draw_list, pos, colTxt, text);\n    }\n    else {\n        ImVec2 siz = ImGui::CalcTextSize(text);\n        ImVec2 pos = PlotToPixels(ImPlotPoint(x,y),IMPLOT_AUTO,IMPLOT_AUTO) - siz * 0.5f + pixel_offset;\n        if (FitThisFrame() && !ImHasFlag(flags, ImPlotItemFlags_NoFit)) {\n            FitPoint(PixelsToPlot(pos));\n            FitPoint(PixelsToPlot(pos+siz));\n        }\n        draw_list.AddText(pos, colTxt, text);\n    }\n    PopPlotClipRect();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotDummy\n//-----------------------------------------------------------------------------\n\nvoid PlotDummy(const char* label_id, ImPlotDummyFlags flags) {\n    if (BeginItem(label_id, flags, ImPlotCol_Line))\n        EndItem();\n}\n\n} // namespace ImPlot\n"
  },
  {
    "path": "lib/third_party/imgui/implot3d/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\r\n# https://github.com/brenocq/implot3d\r\nproject(imgui_implot3d)\r\n\r\nset(CMAKE_CXX_STANDARD 23)\r\n\r\nif (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)\r\n    add_library(imgui_implot3d OBJECT\r\n        source/implot3d.cpp\r\n        source/implot3d_items.cpp\r\n        source/implot3d_demo.cpp\r\n        source/implot3d_meshes.cpp\r\n    )\r\n\r\n    target_include_directories(imgui_implot3d PUBLIC\r\n        include\r\n    )\r\n\r\n    target_link_libraries(imgui_implot3d PRIVATE imgui_includes)\r\nendif()\r\n\r\ntarget_include_directories(imgui_all_includes INTERFACE include)\r\n"
  },
  {
    "path": "lib/third_party/imgui/implot3d/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 Breno Cunha Queiroz\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "lib/third_party/imgui/implot3d/README.md",
    "content": "# ImPlot3D\n\n<p align=\"center\">\n<img src=\"https://github.com/user-attachments/assets/359473d2-73a9-452c-a5f3-cb96e3785dc2\" width=\"270\"> <img src=\"https://github.com/user-attachments/assets/97ec8be4-50f9-428b-b357-25e2479409b8\" width=\"270\"> <img src=\"https://github.com/user-attachments/assets/c212039b-4853-4d26-95a5-5470bf97555e\" width=\"270\">\n</p>\n\n<p align=\"center\">\n<img src=\"https://github.com/user-attachments/assets/ec7ec42a-3c62-44bf-9275-f735f0304c95\" width=\"270\"> <img src=\"https://github.com/user-attachments/assets/e6bd03fa-6d76-4f3e-8d15-c24a05a5f714\" width=\"270\"> <img src=\"https://github.com/user-attachments/assets/b66ff296-7fbf-4644-9129-37daecca0b62\" width=\"270\">\n</p>\n\nImPlot3D is an extension of [Dear ImGui](https://github.com/ocornut/imgui) that provides easy-to-use, high-performance 3D plotting functionality. Inspired by [ImPlot](https://github.com/epezent/implot), it brings a familiar and intuitive API for developers already acquainted with ImPlot. ImPlot3D is designed for rendering 3D plots with customizable markers, lines, surfaces, and meshes, providing an ideal solution for applications requiring visual representation of 3D data.\n\n## 🚀 Features\n- GPU-accelerated rendering\n- Multiple plot types:\n  - Line plots\n  - Scatter plots\n  - Surface plots\n  - Quad plots\n  - Triangle plots\n  - Mesh plots\n  - Text plots\n- Rotate, pan, and zoom 3D plots interactively\n- Several plot styling options: 10 marker types, adjustable marker sizes, line weights, outline colors, fill colors, etc.\n- 16 built-in colormaps and support for and user-added colormaps\n- Optional plot titles, axis labels, and grid labels\n- Optional and configurable legends with toggle buttons to quickly show/hide plot items\n- Default styling based on the current ImGui theme, or completely custom plot styles\n\n## 🛠️ Usage\nThe ImPlot3D API is designed to feel very similar to Dear ImGui and ImPlot. You start by calling `ImPlot3D::BeginPlot()` to initialize a 3D plot, followed by plotting various data using the `PlotX` functions (e.g., `PlotLine()` , `PlotScatter()` , `PlotSurface()` ). Finally, you end the plot with ` ImPlot3D::EndPlot()` .\n\n```cpp\nfloat x_data[1000] = ...;\nfloat y_data[1000] = ...;\nfloat z_data[1000] = ...;\n\nImGui::Begin(\"My Window\");\nif (ImPlot3D::BeginPlot(\"My Plot\")) {\n    ImPlot3D::PlotLine(\"My Line Plot\", x_data, y_data, z_data, 1000);\n    ImPlot3D::PlotScatter(\"My Scatter Plot\", x_data, y_data, z_data, 1000);\n    ...\n    ImPlot3D::EndPlot();\n}\nImGui::End();\n```\n\n## 🎨 Demos\nA comprehensive example showcasing ImPlot3D features can be found in `implot3d_demo.cpp`. Add this file to your project and call `ImPlot3D::ShowDemoWindow()` in your update loop. This demo provides a wide variety of 3D plotting examples, serving as a reference for creating different types of 3D plots. The demo is regularly updated to reflect new features and plot types, so be sure to revisit it with each release!\n\n## ⚙️ Integration\nTo integrate ImPlot3D into your application, follow these steps:\n\n1. Ensure you have a working Dear ImGui environment. ImPlot3D requires only Dear ImGui to function and does not depend on ImPlot.\n2. Add the following source files to your project: `implot3d.h`, `implot3d.cpp`, `implot3d_internal.h`, `implot3d_items.cpp`. Optionally, include `implot3d_demo.cpp` for examples and `implot3d_meshes.cpp` to support pre-loaded meshes.\n3. Create and destroy an ImPlot3DContext alongside your ImGuiContext:\n  ```cpp\n  ImGui::CreateContext();\n  ImPlot3D::CreateContext();\n  ...\n  ImPlot3D::DestroyContext();\n  ImGui::DestroyContext();\n  ```\n\nYou're now ready to start plotting in 3D!\n\n## ⚠️ Extremely Important Note\nDear ImGui, by default, uses 16-bit indexing, which might cause issues with high-density 3D visualizations such as complex surfaces or meshes. This can lead to assertion failures, data truncation, or visual glitches. To avoid these problems, it's recommended to:\n\n- Option 1: Enable 32-bit indices by uncommenting `#define ImDrawIdx unsigned int` in your ImGui imconfig.h file.\n- Option 2: Ensure your renderer supports the `ImGuiBackendFlags_RendererHasVtxOffset` flag. Many official ImGui backends already support this functionality.\n\n## 💬 FAQ\n#### Why ImPlot3D?\nWhile ImGui excels at building UI, it lacks tools for 3D data visualization. ImPlot3D fills this gap, offering a lightweight, real-time library for 3D plotting, designed with interactivity and ease of use in mind.\n\nInspired by ImPlot, ImPlot3D provides a similar API, making it easy for existing ImPlot users to adopt. It focuses on real-time, application-level 3D visualizations for debugging, simulations, and data analysis, with performance as a priority.\n\nImPlot is great for 2D visualizations; ImPlot3D extends this power to 3D, offering the same simplicity and speed.\n\n#### Where can I find documentation?\nThe API for ImPlot3D is thoroughly commented in `implot3d.h`, and a comprehensive demo file, `implot3d_demo.cpp`, showcases all the features. You are encouraged to explore the demo file as it is regularly updated to reflect new functionality. Additionally, if you're familiar with ImPlot, you'll notice many similarities in usage patterns.\n\n#### How is ImPlot3D different from ImPlot?\nImPlot3D is highly inspired by ImPlot, so if you're already familiar with ImPlot, you'll feel right at home. However, ImPlot3D is specifically built for 3D visualizations, offering interactive 3D rotations, panning, and scaling.\n\n### Do I need ImPlot to use ImPlot3D?\nNo. ImPlot3D is a standalone library and does not depend on ImPlot. You only need Dear ImGui to get started.\n\n#### Does ImPlot3D support 2D plots?\nWhile you can rotate the 3D view to align with a 2D plane, ImPlot is far better suited for visualizing 2D data. ImPlot3D is specifically designed for 3D plotting and interaction, so we recommend using ImPlot for all your 2D visualization needs.\n\n#### Can I customize the appearance of plots?\nAbsolutely. ImPlot3D allows you to modify plot styles, including line colors, thickness, fill opacity, and marker sizes. You can also use colormaps for surfaces and customize axis labels, grid styles, and background colors.\n\n#### Can I export 3D plots to an image?\nNot currently. You can use your OS's screen capturing tools to save a plot. ImPlot3D is designed for real-time visualization and interaction, not for creating publication-quality renders. For publication-quality output, consider exporting your data to a dedicated 3D rendering tool.\n\n#### Is ImPlot3D suitable for publication-quality visuals?\nImPlot3D prioritizes interactivity and real-time performance. If you need high-quality visualizations, use ImPlot3D for initial exploration and then switch to tools like [MATLAB](https://www.mathworks.com/products/matlab.html), [matplotlib](https://matplotlib.org/), or [ParaView](https://www.paraview.org/) for the final output.\n\n## License\nThis project is licensed under the MIT License - check [LICENSE](LICENSE) for details.\n"
  },
  {
    "path": "lib/third_party/imgui/implot3d/include/implot3d.h",
    "content": "//--------------------------------------------------\n// ImPlot3D v0.3 WIP\n// implot3d.h\n// Date: 2024-11-16\n// Author: Breno Cunha Queiroz (brenocq.com)\n//\n// Acknowledgments:\n//  ImPlot3D is heavily inspired by ImPlot\n//  (https://github.com/epezent/implot) by Evan Pezent,\n//  and follows a similar code style and structure to\n//  maintain consistency with ImPlot's API.\n//--------------------------------------------------\n\n// Table of Contents:\n// [SECTION] Macros and Defines\n// [SECTION] Forward declarations and basic types\n// [SECTION] Flags & Enumerations\n// [SECTION] Callbacks\n// [SECTION] Context\n// [SECTION] Begin/End Plot\n// [SECTION] Setup\n// [SECTION] Plot Items\n// [SECTION] Plot Utils\n// [SECTION] Miscellaneous\n// [SECTION] Styles\n// [SECTION] Demo\n// [SECTION] Debugging\n// [SECTION] ImPlot3DPoint\n// [SECTION] ImPlot3DRay\n// [SECTION] ImPlot3DPlane\n// [SECTION] ImPlot3DBox\n// [SECTION] ImPlot3DQuat\n// [SECTION] ImPlot3DStyle\n// [SECTION] Meshes\n\n#pragma once\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n\n//-----------------------------------------------------------------------------\n// [SECTION] Macros and Defines\n//-----------------------------------------------------------------------------\n\n#ifndef IMPLOT3D_API\n#define IMPLOT3D_API\n#endif\n\n#define IMPLOT3D_VERSION \"0.3 WIP\"            // ImPlot3D version\n#define IMPLOT3D_VERSION_NUM 300              // Integer encoded version\n#define IMPLOT3D_AUTO -1                      // Deduce variable automatically\n#define IMPLOT3D_AUTO_COL ImVec4(0, 0, 0, -1) // Deduce color automatically\n#define IMPLOT3D_TMP template <typename T> IMPLOT3D_API\n\n//-----------------------------------------------------------------------------\n// [SECTION] Forward declarations and basic types\n//-----------------------------------------------------------------------------\n\n// Forward declarations\nstruct ImPlot3DContext;\nstruct ImPlot3DStyle;\nstruct ImPlot3DPoint;\nstruct ImPlot3DRay;\nstruct ImPlot3DPlane;\nstruct ImPlot3DBox;\nstruct ImPlot3DRange;\nstruct ImPlot3DQuat;\n\n// Enums\ntypedef int ImPlot3DCond;     // -> ImPlot3DCond_              // Enum: Condition for flags\ntypedef int ImPlot3DCol;      // -> ImPlot3DCol_               // Enum: Styling colors\ntypedef int ImPlot3DStyleVar; // -> ImPlot3DStyleVar_          // Enum: Style variables\ntypedef int ImPlot3DMarker;   // -> ImPlot3DMarker_            // Enum: Marker styles\ntypedef int ImPlot3DLocation; // -> ImPlot3DLocation_          // Enum: Locations\ntypedef int ImAxis3D;         // -> ImAxis3D_                  // Enum: Axis indices\ntypedef int ImPlane3D;        // -> ImPlane3D_                  // Enum: Plane indices\ntypedef int ImPlot3DColormap; // -> ImPlot3DColormap_          // Enum: Colormaps\n\n// Flags\ntypedef int ImPlot3DFlags;         // -> ImPlot3DFlags_         // Flags: for BeginPlot()\ntypedef int ImPlot3DItemFlags;     // -> ImPlot3DItemFlags_     // Flags: Item flags\ntypedef int ImPlot3DScatterFlags;  // -> ImPlot3DScatterFlags_  // Flags: Scatter plot flags\ntypedef int ImPlot3DLineFlags;     // -> ImPlot3DLineFlags_     // Flags: Line plot flags\ntypedef int ImPlot3DTriangleFlags; // -> ImPlot3DTriangleFlags_ // Flags: Triangle plot flags\ntypedef int ImPlot3DQuadFlags;     // -> ImPlot3DQuadFlags_     // Flags: QuadFplot flags\ntypedef int ImPlot3DSurfaceFlags;  // -> ImPlot3DSurfaceFlags_  // Flags: Surface plot flags\ntypedef int ImPlot3DMeshFlags;     // -> ImPlot3DMeshFlags_     // Flags: Mesh plot flags\ntypedef int ImPlot3DImageFlags;    // -> ImPlot3DImageFlags_    // Flags: Image plot flags\ntypedef int ImPlot3DLegendFlags;   // -> ImPlot3DLegendFlags_   // Flags: Legend flags\ntypedef int ImPlot3DAxisFlags;     // -> ImPlot3DAxisFlags_     // Flags: Axis flags\n\n// Fallback for ImGui versions before v1.92: define ImTextureRef as ImTextureID\n// You can `#define IMPLOT3D_NO_IMTEXTUREREF` to avoid this fallback\n#if !defined(IMGUI_HAS_TEXTURES) && !defined(IMPLOT3D_NO_IMTEXTUREREF)\ntypedef ImTextureID ImTextureRef;\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Flags & Enumerations\n//-----------------------------------------------------------------------------\n\n// Flags for ImPlot3D::BeginPlot()\nenum ImPlot3DFlags_ {\n    ImPlot3DFlags_None = 0,             // Default\n    ImPlot3DFlags_NoTitle = 1 << 0,     // Hide plot title\n    ImPlot3DFlags_NoLegend = 1 << 1,    // Hide plot legend\n    ImPlot3DFlags_NoMouseText = 1 << 2, // Hide mouse position in plot coordinates\n    ImPlot3DFlags_NoClip = 1 << 3,      // Disable 3D box clipping\n    ImPlot3DFlags_NoMenus = 1 << 4,     // The user will not be able to open context menus\n    ImPlot3DFlags_CanvasOnly = ImPlot3DFlags_NoTitle | ImPlot3DFlags_NoLegend | ImPlot3DFlags_NoMouseText,\n};\n\n// Represents a condition for SetupAxisLimits etc. (same as ImGuiCond, but we only support a subset of those enums)\nenum ImPlot3DCond_ {\n    ImPlot3DCond_None = ImGuiCond_None,     // No condition (always set the variable), same as _Always\n    ImPlot3DCond_Always = ImGuiCond_Always, // No condition (always set the variable)\n    ImPlot3DCond_Once = ImGuiCond_Once,     // Set the variable once per runtime session (only the first call will succeed)\n};\n\nenum ImPlot3DCol_ {\n    // Item colors\n    ImPlot3DCol_Line = 0,      // Line color\n    ImPlot3DCol_Fill,          // Fill color\n    ImPlot3DCol_MarkerOutline, // Marker outline color\n    ImPlot3DCol_MarkerFill,    // Marker fill color\n    // Plot colors\n    ImPlot3DCol_TitleText,  // Title color\n    ImPlot3DCol_InlayText,  // Color for texts appearing inside of plots\n    ImPlot3DCol_FrameBg,    // Frame background color\n    ImPlot3DCol_PlotBg,     // Plot area background color\n    ImPlot3DCol_PlotBorder, // Plot area border color\n    // Legend colors\n    ImPlot3DCol_LegendBg,     // Legend background color\n    ImPlot3DCol_LegendBorder, // Legend border color\n    ImPlot3DCol_LegendText,   // Legend text color\n    // Axis colors\n    ImPlot3DCol_AxisText, // Axis label and tick lables color\n    ImPlot3DCol_AxisGrid, // Axis grid color\n    ImPlot3DCol_AxisTick, // Axis tick color (defaults to AxisGrid)\n    ImPlot3DCol_COUNT,\n};\n\n// Plot styling variables\nenum ImPlot3DStyleVar_ {\n    // Item style\n    ImPlot3DStyleVar_LineWeight,   // float, plot item line weight in pixels\n    ImPlot3DStyleVar_Marker,       // int,   marker specification\n    ImPlot3DStyleVar_MarkerSize,   // float, marker size in pixels (roughly the marker's \"radius\")\n    ImPlot3DStyleVar_MarkerWeight, // float, plot outline weight of markers in pixels\n    ImPlot3DStyleVar_FillAlpha,    // float, alpha modifier applied to all plot item fills\n    // Plot style\n    ImPlot3DStyleVar_PlotDefaultSize, // ImVec2, default size used when ImVec2(0,0) is passed to BeginPlot\n    ImPlot3DStyleVar_PlotMinSize,     // ImVec2, minimum size plot frame can be when shrunk\n    ImPlot3DStyleVar_PlotPadding,     // ImVec2, padding between widget frame and plot area, labels, or outside legends (i.e. main padding)\n    ImPlot3DStyleVar_LabelPadding,    // ImVec2, padding between axes labels, tick labels, and plot edge\n    // Legend style\n    ImPlot3DStyleVar_LegendPadding,      // ImVec2, legend padding from plot edges\n    ImPlot3DStyleVar_LegendInnerPadding, // ImVec2, legend inner padding from legend edges\n    ImPlot3DStyleVar_LegendSpacing,      // ImVec2, spacing between legend entries\n    ImPlot3DStyleVar_COUNT\n};\n\nenum ImPlot3DMarker_ {\n    ImPlot3DMarker_None = -1, // No marker\n    ImPlot3DMarker_Circle,    // Circle marker (default)\n    ImPlot3DMarker_Square,    // Square maker\n    ImPlot3DMarker_Diamond,   // Diamond marker\n    ImPlot3DMarker_Up,        // Upward-pointing triangle marker\n    ImPlot3DMarker_Down,      // Downward-pointing triangle marker\n    ImPlot3DMarker_Left,      // Leftward-pointing triangle marker\n    ImPlot3DMarker_Right,     // Rightward-pointing triangle marker\n    ImPlot3DMarker_Cross,     // Cross marker (not fillable)\n    ImPlot3DMarker_Plus,      // Plus marker (not fillable)\n    ImPlot3DMarker_Asterisk,  // Asterisk marker (not fillable)\n    ImPlot3DMarker_COUNT\n};\n\n// Flags for items\nenum ImPlot3DItemFlags_ {\n    ImPlot3DItemFlags_None = 0,          // Default\n    ImPlot3DItemFlags_NoLegend = 1 << 0, // The item won't have a legend entry displayed\n    ImPlot3DItemFlags_NoFit = 1 << 1,    // The item won't be considered for plot fits\n};\n\n// Flags for PlotScatter\nenum ImPlot3DScatterFlags_ {\n    ImPlot3DScatterFlags_None = 0, // Default\n    ImPlot3DScatterFlags_NoLegend = ImPlot3DItemFlags_NoLegend,\n    ImPlot3DScatterFlags_NoFit = ImPlot3DItemFlags_NoFit,\n};\n\n// Flags for PlotLine\nenum ImPlot3DLineFlags_ {\n    ImPlot3DLineFlags_None = 0, // Default\n    ImPlot3DLineFlags_NoLegend = ImPlot3DItemFlags_NoLegend,\n    ImPlot3DLineFlags_NoFit = ImPlot3DItemFlags_NoFit,\n    ImPlot3DLineFlags_Segments = 1 << 10, // A line segment will be rendered from every two consecutive points\n    ImPlot3DLineFlags_Loop = 1 << 11,     // The last and first point will be connected to form a closed loop\n    ImPlot3DLineFlags_SkipNaN = 1 << 12,  // NaNs values will be skipped instead of rendered as missing data\n};\n\n// Flags for PlotTriangle\nenum ImPlot3DTriangleFlags_ {\n    ImPlot3DTriangleFlags_None = 0, // Default\n    ImPlot3DTriangleFlags_NoLegend = ImPlot3DItemFlags_NoLegend,\n    ImPlot3DTriangleFlags_NoFit = ImPlot3DItemFlags_NoFit,\n    ImPlot3DTriangleFlags_NoLines = 1 << 10,   // No lines will be rendered\n    ImPlot3DTriangleFlags_NoFill = 1 << 11,    // No fill will be rendered\n    ImPlot3DTriangleFlags_NoMarkers = 1 << 12, // No markers will be rendered\n};\n\n// Flags for PlotQuad\nenum ImPlot3DQuadFlags_ {\n    ImPlot3DQuadFlags_None = 0, // Default\n    ImPlot3DQuadFlags_NoLegend = ImPlot3DItemFlags_NoLegend,\n    ImPlot3DQuadFlags_NoFit = ImPlot3DItemFlags_NoFit,\n    ImPlot3DQuadFlags_NoLines = 1 << 10,   // No lines will be rendered\n    ImPlot3DQuadFlags_NoFill = 1 << 11,    // No fill will be rendered\n    ImPlot3DQuadFlags_NoMarkers = 1 << 12, // No markers will be rendered\n};\n\n// Flags for PlotSurface\nenum ImPlot3DSurfaceFlags_ {\n    ImPlot3DSurfaceFlags_None = 0, // Default\n    ImPlot3DSurfaceFlags_NoLegend = ImPlot3DItemFlags_NoLegend,\n    ImPlot3DSurfaceFlags_NoFit = ImPlot3DItemFlags_NoFit,\n    ImPlot3DSurfaceFlags_NoLines = 1 << 10,   // No lines will be rendered\n    ImPlot3DSurfaceFlags_NoFill = 1 << 11,    // No fill will be rendered\n    ImPlot3DSurfaceFlags_NoMarkers = 1 << 12, // No markers will be rendered\n};\n\n// Flags for PlotMesh\nenum ImPlot3DMeshFlags_ {\n    ImPlot3DMeshFlags_None = 0, // Default\n    ImPlot3DMeshFlags_NoLegend = ImPlot3DItemFlags_NoLegend,\n    ImPlot3DMeshFlags_NoFit = ImPlot3DItemFlags_NoFit,\n    ImPlot3DMeshFlags_NoLines = 1 << 10,   // No lines will be rendered\n    ImPlot3DMeshFlags_NoFill = 1 << 11,    // No fill will be rendered\n    ImPlot3DMeshFlags_NoMarkers = 1 << 12, // No markers will be rendered\n};\n\n// Flags for PlotImage\nenum ImPlot3DImageFlags_ {\n    ImPlot3DImageFlags_None = 0, // Default\n    ImPlot3DImageFlags_NoLegend = ImPlot3DItemFlags_NoLegend,\n    ImPlot3DImageFlags_NoFit = ImPlot3DItemFlags_NoFit,\n};\n\n// Flags for legends\nenum ImPlot3DLegendFlags_ {\n    ImPlot3DLegendFlags_None = 0,                 // Default\n    ImPlot3DLegendFlags_NoButtons = 1 << 0,       // Legend icons will not function as hide/show buttons\n    ImPlot3DLegendFlags_NoHighlightItem = 1 << 1, // Plot items will not be highlighted when their legend entry is hovered\n    ImPlot3DLegendFlags_Horizontal = 1 << 2,      // Legend entries will be displayed horizontally\n};\n\n// Used to position legend on a plot\nenum ImPlot3DLocation_ {\n    ImPlot3DLocation_Center = 0,                                                 // Center-center\n    ImPlot3DLocation_North = 1 << 0,                                             // Top-center\n    ImPlot3DLocation_South = 1 << 1,                                             // Bottom-center\n    ImPlot3DLocation_West = 1 << 2,                                              // Center-left\n    ImPlot3DLocation_East = 1 << 3,                                              // Center-right\n    ImPlot3DLocation_NorthWest = ImPlot3DLocation_North | ImPlot3DLocation_West, // Top-left\n    ImPlot3DLocation_NorthEast = ImPlot3DLocation_North | ImPlot3DLocation_East, // Top-right\n    ImPlot3DLocation_SouthWest = ImPlot3DLocation_South | ImPlot3DLocation_West, // Bottom-left\n    ImPlot3DLocation_SouthEast = ImPlot3DLocation_South | ImPlot3DLocation_East  // Bottom-right\n};\n\n// Flags for axis\nenum ImPlot3DAxisFlags_ {\n    ImPlot3DAxisFlags_None = 0,              // Default\n    ImPlot3DAxisFlags_NoLabel = 1 << 0,      // No axis label will be displayed\n    ImPlot3DAxisFlags_NoGridLines = 1 << 1,  // No grid lines will be displayed\n    ImPlot3DAxisFlags_NoTickMarks = 1 << 2,  // No tick marks will be displayed\n    ImPlot3DAxisFlags_NoTickLabels = 1 << 3, // No tick labels will be displayed\n    ImPlot3DAxisFlags_LockMin = 1 << 4,      // The axis minimum value will be locked when panning/zooming\n    ImPlot3DAxisFlags_LockMax = 1 << 5,      // The axis maximum value will be locked when panning/zooming\n    ImPlot3DAxisFlags_AutoFit = 1 << 6,      // Axis will be auto-fitting to data extents\n    ImPlot3DAxisFlags_Invert = 1 << 7,       // The axis will be inverted\n    ImPlot3DAxisFlags_PanStretch = 1 << 8,   // Panning in a locked or constrained state will cause the axis to stretch if possible\n    ImPlot3DAxisFlags_Lock = ImPlot3DAxisFlags_LockMin | ImPlot3DAxisFlags_LockMax,\n    ImPlot3DAxisFlags_NoDecorations = ImPlot3DAxisFlags_NoLabel | ImPlot3DAxisFlags_NoGridLines | ImPlot3DAxisFlags_NoTickLabels,\n};\n\n// Axis indices\nenum ImAxis3D_ {\n    ImAxis3D_X = 0,\n    ImAxis3D_Y,\n    ImAxis3D_Z,\n    ImAxis3D_COUNT,\n};\n\n// Plane indices\nenum ImPlane3D_ {\n    ImPlane3D_YZ = 0,\n    ImPlane3D_XZ,\n    ImPlane3D_XY,\n    ImPlane3D_COUNT,\n};\n\n// Colormaps\nenum ImPlot3DColormap_ {\n    ImPlot3DColormap_Deep = 0,      // Same as seaborn \"deep\"\n    ImPlot3DColormap_Dark = 1,      // Same as matplotlib \"Set1\"\n    ImPlot3DColormap_Pastel = 2,    // Same as matplotlib \"Pastel1\"\n    ImPlot3DColormap_Paired = 3,    // Same as matplotlib \"Paired\"\n    ImPlot3DColormap_Viridis = 4,   // Same as matplotlib \"viridis\"\n    ImPlot3DColormap_Plasma = 5,    // Same as matplotlib \"plasma\"\n    ImPlot3DColormap_Hot = 6,       // Same as matplotlib/MATLAB \"hot\"\n    ImPlot3DColormap_Cool = 7,      // Same as matplotlib/MATLAB \"cool\"\n    ImPlot3DColormap_Pink = 8,      // Same as matplotlib/MATLAB \"pink\"\n    ImPlot3DColormap_Jet = 9,       // Same as matplotlib/MATLAB \"jet\"\n    ImPlot3DColormap_Twilight = 10, // Same as matplotlib \"twilight\"\n    ImPlot3DColormap_RdBu = 11,     // Same as matplotlib \"RdBu\"\n    ImPlot3DColormap_BrBG = 12,     // Same as matplotlib \"BrGB\"\n    ImPlot3DColormap_PiYG = 13,     // Same as matplotlib \"PiYG\"\n    ImPlot3DColormap_Spectral = 14, // Same as matplotlib \"Spectral\"\n    ImPlot3DColormap_Greys = 15,    // White/black\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Callbacks\n//-----------------------------------------------------------------------------\n\n// Callback signature for axis tick label formatter\ntypedef int (*ImPlot3DFormatter)(float value, char* buff, int size, void* user_data);\n\nnamespace ImPlot3D {\n\n//-----------------------------------------------------------------------------\n// [SECTION] Context\n//-----------------------------------------------------------------------------\nIMPLOT3D_API ImPlot3DContext* CreateContext();\nIMPLOT3D_API void DestroyContext(ImPlot3DContext* ctx = nullptr);\nIMPLOT3D_API ImPlot3DContext* GetCurrentContext();\nIMPLOT3D_API void SetCurrentContext(ImPlot3DContext* ctx);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Begin/End Plot\n//-----------------------------------------------------------------------------\n\n// Starts a 3D plotting context. If this function returns true, EndPlot() MUST\n// be called! You are encouraged to use the following convention:\n//\n// if (ImPlot3D::BeginPlot(...)) {\n//     ImPlot3D::PlotLine(...);\n//     ...\n//     ImPlot3D::EndPlot();\n// }\n//\n// Important notes:\n// - #title_id must be unique to the current ImGui ID scope. If you need to avoid ID\n//   collisions or don't want to display a title in the plot, use double hashes\n//   (e.g. \"MyPlot##HiddenIdText\" or \"##NoTitle\").\n// - #size is the **frame** size of the plot widget, not the plot area.\nIMPLOT3D_API bool BeginPlot(const char* title_id, const ImVec2& size = ImVec2(-1, 0), ImPlot3DFlags flags = 0);\nIMPLOT3D_API void EndPlot(); // Only call if BeginPlot() returns true!\n\n//-----------------------------------------------------------------------------\n// [SECTION] Setup\n//-----------------------------------------------------------------------------\n\n// The following API allows you to setup and customize various aspects of the\n// current plot. The functions should be called immediately after BeginPlot()\n// and before any other API calls. Typical usage is as follows:\n\n// if (ImPlot3D::BeginPlot(...)) {                     1) Begin a new plot\n//     ImPlot3D::SetupAxis(ImAxis3D_X, \"My X-Axis\");    2) Make Setup calls\n//     ImPlot3D::SetupAxis(ImAxis3D_Y, \"My Y-Axis\");\n//     ImPlot3D::SetupLegend(ImPlotLocation_North);\n//     ...\n//     ImPlot3D::SetupFinish();                        3) [Optional] Explicitly finish setup\n//     ImPlot3D::PlotLine(...);                        4) Plot items\n//     ...\n//     ImPlot3D::EndPlot();                            5) End the plot\n// }\n//\n// Important notes:\n//\n// - Always call Setup code at the top of your BeginPlot conditional statement.\n// - Setup is locked once you start plotting or explicitly call SetupFinish.\n//   Do NOT call Setup code after you begin plotting or after you make\n//   any non-Setup API calls (e.g. utils like PlotToPixels also lock Setup).\n// - Calling SetupFinish is OPTIONAL, but probably good practice. If you do not\n//   call it yourself, then the first subsequent plotting or utility function will\n//   call it for you.\n\n// Enables an axis or sets the label and/or flags for an existing axis. Leave #label = nullptr for no label\nIMPLOT3D_API void SetupAxis(ImAxis3D axis, const char* label = nullptr, ImPlot3DAxisFlags flags = 0);\n\nIMPLOT3D_API void SetupAxisLimits(ImAxis3D axis, double v_min, double v_max, ImPlot3DCond cond = ImPlot3DCond_Once);\n\nIMPLOT3D_API void SetupAxisFormat(ImAxis3D axis, ImPlot3DFormatter formatter, void* data = nullptr);\n\n// Sets an axis' ticks and optionally the labels. To keep the default ticks, set #keep_default=true\nIMPLOT3D_API void SetupAxisTicks(ImAxis3D axis, const double* values, int n_ticks, const char* const labels[] = nullptr, bool keep_default = false);\n\n// Sets an axis' ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true\nIMPLOT3D_API void SetupAxisTicks(ImAxis3D axis, double v_min, double v_max, int n_ticks, const char* const labels[] = nullptr,\n                                 bool keep_default = false);\n\n// Sets an axis' limits constraints\nIMPLOT3D_API void SetupAxisLimitsConstraints(ImAxis3D axis, double v_min, double v_max);\n\n// Sets an axis' zoom constraints\nIMPLOT3D_API void SetupAxisZoomConstraints(ImAxis3D axis, double z_min, double z_max);\n\n// Sets the label and/or flags for primary X/Y/Z axes (shorthand for three calls to SetupAxis)\nIMPLOT3D_API void SetupAxes(const char* x_label, const char* y_label, const char* z_label, ImPlot3DAxisFlags x_flags = 0,\n                            ImPlot3DAxisFlags y_flags = 0, ImPlot3DAxisFlags z_flags = 0);\n\n// Sets the X/Y/Z axes range limits. If ImPlot3DCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits)\nIMPLOT3D_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, double z_min, double z_max,\n                                  ImPlot3DCond cond = ImPlot3DCond_Once);\n\n// Sets the plot box rotation given the elevation and azimuth angles in degrees. If ImPlot3DCond_Always is used, the rotation will be locked\nIMPLOT3D_API void SetupBoxRotation(float elevation, float azimuth, bool animate = false, ImPlot3DCond cond = ImPlot3DCond_Once);\n\n// Sets the plot box rotation given a quaternion. If ImPlot3DCond_Always is used, the rotation will be locked\nIMPLOT3D_API void SetupBoxRotation(ImPlot3DQuat rotation, bool animate = false, ImPlot3DCond cond = ImPlot3DCond_Once);\n\n// Sets the plot box initial rotation given the elevation and azimuth angles in degrees. The initial rotation is the rotation the plot goes back to\n// when a left mouse button double click happens\nIMPLOT3D_API void SetupBoxInitialRotation(float elevation, float azimuth);\n\n// Sets the plot box initial rotation given a quaternion. The initial rotation is the rotation the plot goes back to when a left mouse button double\n// click happens\nIMPLOT3D_API void SetupBoxInitialRotation(ImPlot3DQuat rotation);\n\n// Sets the plot box X/Y/Z scale. A scale of 1.0 is the default. Values greater than 1.0 enlarge the plot, while values between 0.0 and 1.0 shrink it\nIMPLOT3D_API void SetupBoxScale(float x, float y, float z);\n\nIMPLOT3D_API void SetupLegend(ImPlot3DLocation location, ImPlot3DLegendFlags flags = 0);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot Items\n//-----------------------------------------------------------------------------\n\nIMPLOT3D_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DScatterFlags flags = 0, int offset = 0,\n                              int stride = sizeof(T));\n\nIMPLOT3D_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DLineFlags flags = 0, int offset = 0,\n                           int stride = sizeof(T));\n\nIMPLOT3D_TMP void PlotTriangle(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DTriangleFlags flags = 0,\n                               int offset = 0, int stride = sizeof(T));\n\nIMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DQuadFlags flags = 0, int offset = 0,\n                           int stride = sizeof(T));\n\n// Plot the surface defined by a grid of vertices. The grid is defined by the x and y arrays, and the z array contains the height of each vertex. A\n// total of x_count * y_count vertices are expected for each array. Leave #scale_min and #scale_max both at 0 for automatic color scaling, or set them\n// to a predefined range\nIMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min = 0.0,\n                              double scale_max = 0.0, ImPlot3DSurfaceFlags flags = 0, int offset = 0, int stride = sizeof(T));\n\nIMPLOT3D_API void PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int* idx, int vtx_count, int idx_count,\n                           ImPlot3DMeshFlags flags = 0);\n\n// Plots a rectangular image in 3D defined by its center and two direction vectors (axes).\n// #center is the center of the rectangle in plot coordinates.\n// #axis_u and #axis_v define the local axes and half-extents of the rectangle in 3D space.\n// The rectangle is formed by moving from the center along ±axis_u and ±axis_v.\n// #uv0 and #uv1 define the texture mapping.\n// #tint_col can be used to tint the image.\nIMPLOT3D_API void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlot3DPoint& center, const ImPlot3DPoint& axis_u,\n                            const ImPlot3DPoint& axis_v, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1),\n                            const ImVec4& tint_col = ImVec4(1, 1, 1, 1), ImPlot3DImageFlags flags = 0);\n\n// Plots an image using four arbitrary 3D points that define a quad in space.\n// Each corner (p0 to p3) corresponds to a corner in the image, and #uv0 to #uv3 are the texture coordinates for each.\n// This overload allows full control over orientation, shape, and distortion.\n// Note: The quad is internally split into two triangles, so non-rectangular quads may produce rendering artifacts\n// since distortion is interpolated per triangle rather than over the full quad.\nIMPLOT3D_API void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlot3DPoint& p0, const ImPlot3DPoint& p1, const ImPlot3DPoint& p2,\n                            const ImPlot3DPoint& p3, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 0),\n                            const ImVec2& uv2 = ImVec2(1, 1), const ImVec2& uv3 = ImVec2(0, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1),\n                            ImPlot3DImageFlags flags = 0);\n\n// Plots a centered text label at point x,y,z. It is possible to set the text angle in radians and offset in pixels\nIMPLOT3D_API void PlotText(const char* text, float x, float y, float z, float angle = 0.0f, const ImVec2& pix_offset = ImVec2(0, 0));\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot Utils\n//-----------------------------------------------------------------------------\n\n// Convert a position in the current plot's coordinate system to pixels\nIMPLOT3D_API ImVec2 PlotToPixels(const ImPlot3DPoint& point);\nIMPLOT3D_API ImVec2 PlotToPixels(double x, double y, double z);\n// Convert a pixel coordinate to a ray in the current plot's coordinate system\nIMPLOT3D_API ImPlot3DRay PixelsToPlotRay(const ImVec2& pix);\nIMPLOT3D_API ImPlot3DRay PixelsToPlotRay(double x, double y);\n// Convert a pixel coordinate to a point in an axis plane in the current plot's coordinate system\nIMPLOT3D_API ImPlot3DPoint PixelsToPlotPlane(const ImVec2& pix, ImPlane3D plane, bool mask = true);\nIMPLOT3D_API ImPlot3DPoint PixelsToPlotPlane(double x, double y, ImPlane3D plane, bool mask = true);\n\nIMPLOT3D_API ImVec2 GetPlotPos();  // Get the current plot position (top-left) in pixels\nIMPLOT3D_API ImVec2 GetPlotSize(); // Get the current plot size in pixels\n\n//-----------------------------------------------------------------------------\n// [SECTION] Miscellaneous\n//-----------------------------------------------------------------------------\n\nIMPLOT3D_API ImDrawList* GetPlotDrawList();\n\n//-----------------------------------------------------------------------------\n// [SECTION] Styles\n//-----------------------------------------------------------------------------\n\n// Get current style\nIMPLOT3D_API ImPlot3DStyle& GetStyle();\nIMPLOT3D_API void SetStyle(const ImPlot3DStyle& style);\n\n// Set color styles\nIMPLOT3D_API void StyleColorsAuto(ImPlot3DStyle* dst = nullptr);    // Set colors with ImGui style\nIMPLOT3D_API void StyleColorsDark(ImPlot3DStyle* dst = nullptr);    // Set colors with dark style\nIMPLOT3D_API void StyleColorsLight(ImPlot3DStyle* dst = nullptr);   // Set colors with light style\nIMPLOT3D_API void StyleColorsClassic(ImPlot3DStyle* dst = nullptr); // Set colors with classic style\n\n// Temporarily modify a style color. Don't forget to call PopStyleColor!\nIMPLOT3D_API void PushStyleColor(ImPlot3DCol idx, ImU32 col);\nIMPLOT3D_API void PushStyleColor(ImPlot3DCol idx, const ImVec4& col);\n// Undo temporary style color modification(s). Undo multiple pushes at once by increasing count\nIMPLOT3D_API void PopStyleColor(int count = 1);\n\n// Temporarily modify a style variable of float type. Don't forget to call PopStyleVar!\nIMPLOT3D_API void PushStyleVar(ImPlot3DStyleVar idx, float val);\n// Temporarily modify a style variable of int type. Don't forget to call PopStyleVar!\nIMPLOT3D_API void PushStyleVar(ImPlot3DStyleVar idx, int val);\n// Temporarily modify a style variable of ImVec2 type. Don't forget to call PopStyleVar!\nIMPLOT3D_API void PushStyleVar(ImPlot3DStyleVar idx, const ImVec2& val);\n// Undo temporary style variable modification(s). Undo multiple pushes at once by increasing count\nIMPLOT3D_API void PopStyleVar(int count = 1);\n\n// Set the line color and weight for the next item only\nIMPLOT3D_API void SetNextLineStyle(const ImVec4& col = IMPLOT3D_AUTO_COL, float weight = IMPLOT3D_AUTO);\n// Set the fill color for the next item only\nIMPLOT3D_API void SetNextFillStyle(const ImVec4& col = IMPLOT3D_AUTO_COL, float alpha_mod = IMPLOT3D_AUTO);\n// Set the marker style for the next item only\nIMPLOT3D_API void SetNextMarkerStyle(ImPlot3DMarker marker = IMPLOT3D_AUTO, float size = IMPLOT3D_AUTO, const ImVec4& fill = IMPLOT3D_AUTO_COL,\n                                     float weight = IMPLOT3D_AUTO, const ImVec4& outline = IMPLOT3D_AUTO_COL);\n\n// Get color\nIMPLOT3D_API ImVec4 GetStyleColorVec4(ImPlot3DCol idx);\nIMPLOT3D_API ImU32 GetStyleColorU32(ImPlot3DCol idx);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Colormaps\n//-----------------------------------------------------------------------------\n\n// Item styling is based on colormaps when the relevant ImPlot3DCol_XXX is set to\n// IMPLOT3D_AUTO_COL (default). Several built-in colormaps are available. You can\n// add and then push/pop your own colormaps as well. To permanently set a colormap,\n// modify the Colormap index member of your ImPlot3DStyle.\n\n// Colormap data will be ignored and a custom color will be used if you have done one of the following:\n//     1) Modified an item style color in your ImPlot3DStyle to anything other than IMPLOT3D_AUTO_COL.\n//     3) Set the next item style with a SetNextXXXStyle function.\n\n// Add a new colormap. The color data will be copied. The colormap can be used by pushing either the returned index or the\n// string name with PushColormap. The colormap name must be unique and the size must be greater than 1. You will receive\n// an assert otherwise! By default colormaps are considered to be qualitative (i.e. discrete). If you want to create a\n// continuous colormap, set #qual=false. This will treat the colors you provide as keys, and ImPlot3D will build a linearly\n// interpolated lookup table. The memory footprint of this table will be exactly ((size-1)*255+1)*4 bytes.\n\nIMPLOT3D_API ImPlot3DColormap AddColormap(const char* name, const ImVec4* cols, int size, bool qual = true);\nIMPLOT3D_API ImPlot3DColormap AddColormap(const char* name, const ImU32* cols, int size, bool qual = true);\n\n// Returns the number of available colormaps (i.e. the built-in + user-added count)\nIMPLOT3D_API int GetColormapCount();\n// Returns a null terminated string name for a colormap given an index. Returns nullptr if index is invalid\nIMPLOT3D_API const char* GetColormapName(ImPlot3DColormap cmap);\n// Returns an index number for a colormap given a valid string name. Returns -1 if name is invalid\nIMPLOT3D_API ImPlot3DColormap GetColormapIndex(const char* name);\n\n// Temporarily switch to one of the built-in (i.e. ImPlot3DColormap_XXX) or user-added colormaps (i.e. a return value of AddColormap). Don't forget to\n// call PopColormap!\nIMPLOT3D_API void PushColormap(ImPlot3DColormap cmap);\n// Push a colormap by string name. Use built-in names such as \"Default\", \"Deep\", \"Jet\", etc. or a string you provided to AddColormap. Don't forget to\n// call PopColormap!\nIMPLOT3D_API void PushColormap(const char* name);\n// Undo temporary colormap modification(s). Undo multiple pushes at once by increasing count\nIMPLOT3D_API void PopColormap(int count = 1);\n\n// Returns the next color from the current colormap and advances the colormap for the current plot\n// Can also be used with no return value to skip colors if desired. You need to call it between Begin/EndPlot!\nIMPLOT3D_API ImVec4 NextColormapColor();\n\n// Returns the size of a colormap\nIMPLOT3D_API int GetColormapSize(ImPlot3DColormap cmap = IMPLOT3D_AUTO);\n// Returns a color from a colormap given an index >= 0 (modulo will be performed)\nIMPLOT3D_API ImVec4 GetColormapColor(int idx, ImPlot3DColormap cmap = IMPLOT3D_AUTO);\n// Sample a color from the current colormap given t between 0 and 1\nIMPLOT3D_API ImVec4 SampleColormap(float t, ImPlot3DColormap cmap = IMPLOT3D_AUTO);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Demo\n//-----------------------------------------------------------------------------\n// Add implot3d_demo.cpp to your sources to use methods in this section\n\n// Shows the ImPlot3D demo window\nIMPLOT3D_API void ShowDemoWindow(bool* p_open = nullptr);\n// Shows all ImPlot3D demos, without enclosing window\nIMPLOT3D_API void ShowAllDemos();\n\n// Shows ImPlot3D style editor block (not a window)\nIMPLOT3D_API void ShowStyleEditor(ImPlot3DStyle* ref = nullptr);\n\n// Shows ImPlot3D metrics/debug information window.\nIMPLOT3D_API void ShowMetricsWindow(bool* p_popen = nullptr);\n\n} // namespace ImPlot3D\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DPoint\n//-----------------------------------------------------------------------------\n\n// ImPlot3DPoint: 3D vector to store points in 3D\nstruct ImPlot3DPoint {\n    float x, y, z;\n    constexpr ImPlot3DPoint() : x(0.0f), y(0.0f), z(0.0f) {}\n    constexpr ImPlot3DPoint(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}\n\n    // Accessors\n    float& operator[](size_t idx) {\n        IM_ASSERT(idx == 0 || idx == 1 || idx == 2);\n        return ((float*)(void*)(char*)this)[idx];\n    }\n    float operator[](size_t idx) const {\n        IM_ASSERT(idx == 0 || idx == 1 || idx == 2);\n        return ((const float*)(const void*)(const char*)this)[idx];\n    }\n\n    // Binary operators\n    IMPLOT3D_API ImPlot3DPoint operator*(float rhs) const;\n    IMPLOT3D_API ImPlot3DPoint operator/(float rhs) const;\n    IMPLOT3D_API ImPlot3DPoint operator+(const ImPlot3DPoint& rhs) const;\n    IMPLOT3D_API ImPlot3DPoint operator-(const ImPlot3DPoint& rhs) const;\n    IMPLOT3D_API ImPlot3DPoint operator*(const ImPlot3DPoint& rhs) const;\n    IMPLOT3D_API ImPlot3DPoint operator/(const ImPlot3DPoint& rhs) const;\n\n    // Unary operator\n    IMPLOT3D_API ImPlot3DPoint operator-() const;\n\n    // Compound assignment operators\n    IMPLOT3D_API ImPlot3DPoint& operator*=(float rhs);\n    IMPLOT3D_API ImPlot3DPoint& operator/=(float rhs);\n    IMPLOT3D_API ImPlot3DPoint& operator+=(const ImPlot3DPoint& rhs);\n    IMPLOT3D_API ImPlot3DPoint& operator-=(const ImPlot3DPoint& rhs);\n    IMPLOT3D_API ImPlot3DPoint& operator*=(const ImPlot3DPoint& rhs);\n    IMPLOT3D_API ImPlot3DPoint& operator/=(const ImPlot3DPoint& rhs);\n\n    // Comparison operators\n    IMPLOT3D_API bool operator==(const ImPlot3DPoint& rhs) const;\n    IMPLOT3D_API bool operator!=(const ImPlot3DPoint& rhs) const;\n\n    // Dot product\n    IMPLOT3D_API float Dot(const ImPlot3DPoint& rhs) const;\n\n    // Cross product\n    IMPLOT3D_API ImPlot3DPoint Cross(const ImPlot3DPoint& rhs) const;\n\n    // Get vector length\n    IMPLOT3D_API float Length() const;\n\n    // Get vector squared length\n    IMPLOT3D_API float LengthSquared() const;\n\n    // Normalize to unit length\n    IMPLOT3D_API void Normalize();\n\n    // Return vector normalized to unit length\n    IMPLOT3D_API ImPlot3DPoint Normalized() const;\n\n    // Friend binary operators to allow commutative behavior\n    IMPLOT3D_API friend ImPlot3DPoint operator*(float lhs, const ImPlot3DPoint& rhs);\n\n    // Check if the point is NaN\n    IMPLOT3D_API bool IsNaN() const;\n\n#ifdef IMPLOT3D_POINT_CLASS_EXTRA\n    IMPLOT3D_POINT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math\n                               // types and ImPlot3DPoint\n#endif\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DRay\n//-----------------------------------------------------------------------------\n\nstruct ImPlot3DRay {\n    ImPlot3DPoint Origin;\n    ImPlot3DPoint Direction;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DPlane\n//-----------------------------------------------------------------------------\n\nstruct ImPlot3DPlane {\n    ImPlot3DPoint Point;\n    ImPlot3DPoint Normal;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DBox\n//-----------------------------------------------------------------------------\n\nstruct ImPlot3DBox {\n    ImPlot3DPoint Min;\n    ImPlot3DPoint Max;\n\n    // Default constructor\n    constexpr ImPlot3DBox() : Min(ImPlot3DPoint()), Max(ImPlot3DPoint()) {}\n\n    // Constructor with two points\n    constexpr ImPlot3DBox(const ImPlot3DPoint& min, const ImPlot3DPoint& max) : Min(min), Max(max) {}\n\n    // Method to expand the box to include a point\n    IMPLOT3D_API void Expand(const ImPlot3DPoint& point);\n\n    // Method to check if a point is inside the box\n    IMPLOT3D_API bool Contains(const ImPlot3DPoint& point) const;\n\n    // Method to clip a line segment against the box\n    IMPLOT3D_API bool ClipLineSegment(const ImPlot3DPoint& p0, const ImPlot3DPoint& p1, ImPlot3DPoint& p0_clipped, ImPlot3DPoint& p1_clipped) const;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DRange\n//-----------------------------------------------------------------------------\n\nstruct ImPlot3DRange {\n    float Min;\n    float Max;\n\n    constexpr ImPlot3DRange() : Min(0.0f), Max(0.0f) {}\n    constexpr ImPlot3DRange(float min, float max) : Min(min), Max(max) {}\n\n    IMPLOT3D_API void Expand(float value);\n    IMPLOT3D_API bool Contains(float value) const;\n    float Size() const { return Max - Min; }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DQuat\n//-----------------------------------------------------------------------------\n\nstruct ImPlot3DQuat {\n    float x, y, z, w;\n\n    // Constructors\n    constexpr ImPlot3DQuat() : x(0.0f), y(0.0f), z(0.0f), w(1.0f) {}\n    constexpr ImPlot3DQuat(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) {}\n\n    IMPLOT3D_API ImPlot3DQuat(float _angle, const ImPlot3DPoint& _axis);\n\n    // Set quaternion from two vectors\n    IMPLOT3D_API static ImPlot3DQuat FromTwoVectors(const ImPlot3DPoint& v0, const ImPlot3DPoint& v1);\n\n    // Set quaternion given elevation and azimuth angles in radians\n    IMPLOT3D_API static ImPlot3DQuat FromElAz(float elevation, float azimuth);\n\n    // Get quaternion length\n    IMPLOT3D_API float Length() const;\n\n    // Get normalized quaternion\n    IMPLOT3D_API ImPlot3DQuat Normalized() const;\n\n    // Conjugate of the quaternion\n    IMPLOT3D_API ImPlot3DQuat Conjugate() const;\n\n    // Inverse of the quaternion\n    IMPLOT3D_API ImPlot3DQuat Inverse() const;\n\n    // Binary operators\n    IMPLOT3D_API ImPlot3DQuat operator*(const ImPlot3DQuat& rhs) const;\n\n    // Normalize the quaternion in place\n    IMPLOT3D_API ImPlot3DQuat& Normalize();\n\n    // Rotate a 3D point using the quaternion\n    IMPLOT3D_API ImPlot3DPoint operator*(const ImPlot3DPoint& point) const;\n\n    // Comparison operators\n    IMPLOT3D_API bool operator==(const ImPlot3DQuat& rhs) const;\n    IMPLOT3D_API bool operator!=(const ImPlot3DQuat& rhs) const;\n\n    // Interpolate between two quaternions\n    IMPLOT3D_API static ImPlot3DQuat Slerp(const ImPlot3DQuat& q1, const ImPlot3DQuat& q2, float t);\n\n    // Get quaternion dot product\n    IMPLOT3D_API float Dot(const ImPlot3DQuat& rhs) const;\n\n#ifdef IMPLOT3D_QUAT_CLASS_EXTRA\n    IMPLOT3D_QUAT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math\n                              // types and ImPlot3DQuat\n#endif\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DStyle\n//-----------------------------------------------------------------------------\n\nstruct ImPlot3DStyle {\n    // Item style\n    float LineWeight;   // Line weight in pixels\n    int Marker;         // Default marker type (ImPlot3DMarker_None)\n    float MarkerSize;   // Marker size in pixels (roughly the marker's \"radius\")\n    float MarkerWeight; // Marker outline weight in pixels\n    float FillAlpha;    // Alpha modifier applied to plot fills\n    // Plot style\n    ImVec2 PlotDefaultSize;\n    ImVec2 PlotMinSize;\n    ImVec2 PlotPadding;\n    ImVec2 LabelPadding;\n    // Legend style\n    ImVec2 LegendPadding;      // Legend padding from plot edges\n    ImVec2 LegendInnerPadding; // Legend inner padding from legend edges\n    ImVec2 LegendSpacing;      // Spacing between legend entries\n    // Colors\n    ImVec4 Colors[ImPlot3DCol_COUNT];\n    inline ImVec4 GetColor(ImPlot3DCol idx) const { return Colors[idx]; }\n    inline void SetColor(ImPlot3DCol idx, const ImVec4& col) { Colors[idx] = col; }\n    // Colormap\n    ImPlot3DColormap Colormap; // The current colormap. Set this to either an ImPlot3DColormap_ enum or an index returned by AddColormap\n    // Constructor\n    IMPLOT3D_API ImPlot3DStyle();\n    ImPlot3DStyle(const ImPlot3DStyle& other) = default;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Meshes\n//-----------------------------------------------------------------------------\n\nnamespace ImPlot3D {\n\n// Cube\nconstexpr int CUBE_VTX_COUNT = 8;              // Number of cube vertices\nconstexpr int CUBE_IDX_COUNT = 36;             // Number of cube indices (12 triangles)\nextern ImPlot3DPoint cube_vtx[CUBE_VTX_COUNT]; // Cube vertices\nextern unsigned int cube_idx[CUBE_IDX_COUNT];  // Cube indices\n\n// Sphere\nconstexpr int SPHERE_VTX_COUNT = 162;              // Number of sphere vertices for 128 triangles\nconstexpr int SPHERE_IDX_COUNT = 960;              // Number of sphere indices (128 triangles)\nextern ImPlot3DPoint sphere_vtx[SPHERE_VTX_COUNT]; // Sphere vertices\nextern unsigned int sphere_idx[SPHERE_IDX_COUNT];  // Sphere indices\n\n// Duck (Rubber Duck by Poly by Google [CC-BY] via Poly Pizza)\nconstexpr int DUCK_VTX_COUNT = 254;            // Number of duck vertices\nconstexpr int DUCK_IDX_COUNT = 1428;           // Number of duck indices\nextern ImPlot3DPoint duck_vtx[DUCK_VTX_COUNT]; // Duck vertices\nextern unsigned int duck_idx[DUCK_IDX_COUNT];  // Duck indices\n\n} // namespace ImPlot3D\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/implot3d/include/implot3d_internal.h",
    "content": "//--------------------------------------------------\n// ImPlot3D v0.3 WIP\n// implot3d_internal.h\n// Date: 2024-11-17\n// Author: Breno Cunha Queiroz (brenocq.com)\n//\n// Acknowledgments:\n//  ImPlot3D is heavily inspired by ImPlot\n//  (https://github.com/epezent/implot) by Evan Pezent,\n//  and follows a similar code style and structure to\n//  maintain consistency with ImPlot's API.\n//--------------------------------------------------\n\n// Table of Contents:\n// [SECTION] Constants\n// [SECTION] Generic Helpers\n// [SECTION] Forward Declarations\n// [SECTION] Callbacks\n// [SECTION] Structs\n// [SECTION] Context Pointer\n// [SECTION] Context Utils\n// [SECTION] Style Utils\n// [SECTION] Item Utils\n// [SECTION] Plot Utils\n// [SECTION] Setup Utils\n// [SECTION] Formatter\n// [SECTION] Locator\n\n#pragma once\n\n#include <cmath>\n\n#ifndef IMPLOT3D_VERSION\n#include \"implot3d.h\"\n#endif\n\n#ifndef IMGUI_DISABLE\n#include \"imgui_internal.h\"\n\n//-----------------------------------------------------------------------------\n// [SECTION] Constants\n//-----------------------------------------------------------------------------\n\n// Default label format for axis labels\n#define IMPLOT3D_LABEL_FORMAT \"%g\"\n// Max character size for tick labels\n#define IMPLOT3D_LABEL_MAX_SIZE 32\n\n//-----------------------------------------------------------------------------\n// [SECTION] Generic Helpers\n//-----------------------------------------------------------------------------\n\nnamespace ImPlot3D {\n\n// Computes the common (base-10) logarithm\nstatic inline float ImLog10(float x) { return log10f(x); }\n// Returns true if flag is set\ntemplate <typename TSet, typename TFlag> static inline bool ImHasFlag(TSet set, TFlag flag) { return (set & flag) == flag; }\n// Flips a flag in a flagset\ntemplate <typename TSet, typename TFlag> static inline void ImFlipFlag(TSet& set, TFlag flag) { ImHasFlag(set, flag) ? set &= ~flag : set |= flag; }\ntemplate <typename T> static inline T ImRemap01(T x, T x0, T x1) { return (x1 - x0) ? ((x - x0) / (x1 - x0)) : 0; }\n// Returns true if val is NAN\nstatic inline bool ImNan(float val) { return std::isnan(val); }\n// Returns true if val is NAN or INFINITY\nstatic inline bool ImNanOrInf(float val) { return !(val >= -FLT_MAX && val <= FLT_MAX) || ImNan(val); }\n// Turns NANs to 0s\nstatic inline double ImConstrainNan(float val) { return ImNan(val) ? 0 : val; }\n// Turns infinity to floating point maximums\nstatic inline double ImConstrainInf(double val) { return val >= FLT_MAX ? FLT_MAX : val <= -FLT_MAX ? -FLT_MAX : val; }\n// True if two numbers are approximately equal using units in the last place.\nstatic inline bool ImAlmostEqual(double v1, double v2, int ulp = 2) {\n    return ImAbs(v1 - v2) < FLT_EPSILON * ImAbs(v1 + v2) * ulp || ImAbs(v1 - v2) < FLT_MIN;\n}\n// Set alpha channel of 32-bit color from float in range [0.0 1.0]\nstatic inline ImU32 ImAlphaU32(ImU32 col, float alpha) { return col & ~((ImU32)((1.0f - alpha) * 255) << IM_COL32_A_SHIFT); }\n// Mix color a and b by factor s in [0 256]\nstatic inline ImU32 ImMixU32(ImU32 a, ImU32 b, ImU32 s) {\n#ifdef IMPLOT3D_MIX64\n    const ImU32 af = 256 - s;\n    const ImU32 bf = s;\n    const ImU64 al = (a & 0x00ff00ff) | (((ImU64)(a & 0xff00ff00)) << 24);\n    const ImU64 bl = (b & 0x00ff00ff) | (((ImU64)(b & 0xff00ff00)) << 24);\n    const ImU64 mix = (al * af + bl * bf);\n    return ((mix >> 32) & 0xff00ff00) | ((mix & 0xff00ff00) >> 8);\n#else\n    const ImU32 af = 256 - s;\n    const ImU32 bf = s;\n    const ImU32 al = (a & 0x00ff00ff);\n    const ImU32 ah = (a & 0xff00ff00) >> 8;\n    const ImU32 bl = (b & 0x00ff00ff);\n    const ImU32 bh = (b & 0xff00ff00) >> 8;\n    const ImU32 ml = (al * af + bl * bf);\n    const ImU32 mh = (ah * af + bh * bf);\n    return (mh & 0xff00ff00) | ((ml & 0xff00ff00) >> 8);\n#endif\n}\n\n// Fills a buffer with n samples linear interpolated from vmin to vmax\ntemplate <typename T> void FillRange(ImVector<T>& buffer, int n, T vmin, T vmax) {\n    buffer.resize(n);\n    T step = (vmax - vmin) / (n - 1);\n    for (int i = 0; i < n; ++i) {\n        buffer[i] = vmin + i * step;\n    }\n}\n\n} // namespace ImPlot3D\n\n//-----------------------------------------------------------------------------\n// [SECTION] Forward Declarations\n//-----------------------------------------------------------------------------\n\nstruct ImPlot3DTicker;\n\n//------------------------------------------------------------------------------\n// [SECTION] Callbacks\n//------------------------------------------------------------------------------\n\ntypedef void (*ImPlot3DLocator)(ImPlot3DTicker& ticker, const ImPlot3DRange& range, float pixels, ImPlot3DFormatter formatter, void* formatter_data);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Structs\n//-----------------------------------------------------------------------------\n\nstruct ImDrawList3D {\n    // [Internal] Define which texture should be used when rendering triangles.\n    struct ImTextureBufferItem {\n        ImTextureRef TexRef;\n        unsigned int VtxIdx;\n    };\n\n    ImVector<ImDrawIdx> IdxBuffer;  // Index buffer\n    ImVector<ImDrawVert> VtxBuffer; // Vertex buffer\n    ImVector<float> ZBuffer;        // Z buffer. Depth value for each triangle\n    unsigned int _VtxCurrentIdx;    // [Internal] current vertex index\n    ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)\n    ImDrawIdx* _IdxWritePtr;  // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)\n    float* _ZWritePtr;        // [Internal] point within ZBuffer.Data after each add command (to avoid using the ImVector<> operators too much)\n    ImDrawListFlags _Flags;   // [Internal] draw list flags\n    ImVector<ImTextureBufferItem> _TextureBuffer; // [Internal] buffer for SetTexture/ResetTexture\n    ImDrawListSharedData* _SharedData;            // [Internal] shared draw list data\n\n    ImDrawList3D() {\n        _Flags = ImDrawListFlags_None;\n        _SharedData = nullptr;\n        ResetBuffers();\n    }\n\n    void PrimReserve(int idx_count, int vtx_count);\n    void PrimUnreserve(int idx_count, int vtx_count);\n\n    void SetTexture(ImTextureRef tex_ref);\n    void ResetTexture();\n\n    void SortedMoveToImGuiDrawList();\n\n    void ResetBuffers() {\n        IdxBuffer.clear();\n        VtxBuffer.clear();\n        ZBuffer.clear();\n        _VtxCurrentIdx = 0;\n        _VtxWritePtr = VtxBuffer.Data;\n        _IdxWritePtr = IdxBuffer.Data;\n        _ZWritePtr = ZBuffer.Data;\n        _TextureBuffer.clear();\n        ResetTexture();\n    }\n\n    constexpr static unsigned int MaxIdx() { return sizeof(ImDrawIdx) == 2 ? 65535 : 4294967295; }\n};\n\nstruct ImPlot3DNextItemData {\n    ImVec4 Colors[4]; // ImPlot3DCol_Line, ImPlot3DCol_Fill, ImPlot3DCol_MarkerOutline, ImPlot3DCol_MarkerFill,\n    float LineWeight;\n    ImPlot3DMarker Marker;\n    float MarkerSize;\n    float MarkerWeight;\n    float FillAlpha;\n    bool RenderLine;\n    bool RenderFill;\n    bool RenderMarkerLine;\n    bool RenderMarkerFill;\n    bool IsAutoFill;\n    bool IsAutoLine;\n    bool Hidden;\n\n    ImPlot3DNextItemData() { Reset(); }\n\n    void Reset() {\n        for (int i = 0; i < 4; i++)\n            Colors[i] = IMPLOT3D_AUTO_COL;\n        LineWeight = IMPLOT3D_AUTO;\n        Marker = IMPLOT3D_AUTO;\n        MarkerSize = IMPLOT3D_AUTO;\n        MarkerWeight = IMPLOT3D_AUTO;\n        FillAlpha = IMPLOT3D_AUTO;\n        RenderLine = false;\n        RenderFill = false;\n        RenderMarkerLine = true;\n        RenderMarkerFill = true;\n        IsAutoFill = true;\n        IsAutoLine = true;\n        Hidden = false;\n    }\n};\n\n// Colormap data storage\nstruct ImPlot3DColormapData {\n    ImVector<ImU32> Keys;\n    ImVector<int> KeyCounts;\n    ImVector<int> KeyOffsets;\n    ImVector<ImU32> Tables;\n    ImVector<int> TableSizes;\n    ImVector<int> TableOffsets;\n    ImGuiTextBuffer Text;\n    ImVector<int> TextOffsets;\n    ImVector<bool> Quals;\n    ImGuiStorage Map;\n    int Count;\n\n    ImPlot3DColormapData() { Count = 0; }\n\n    int Append(const char* name, const ImU32* keys, int count, bool qual) {\n        if (GetIndex(name) != -1)\n            return -1;\n        KeyOffsets.push_back(Keys.size());\n        KeyCounts.push_back(count);\n        Keys.reserve(Keys.size() + count);\n        for (int i = 0; i < count; ++i)\n            Keys.push_back(keys[i]);\n        TextOffsets.push_back(Text.size());\n        Text.append(name, name + strlen(name) + 1);\n        Quals.push_back(qual);\n        ImGuiID id = ImHashStr(name);\n        int idx = Count++;\n        Map.SetInt(id, idx);\n        _AppendTable(idx);\n        return idx;\n    }\n\n    void _AppendTable(ImPlot3DColormap cmap) {\n        int key_count = GetKeyCount(cmap);\n        const ImU32* keys = GetKeys(cmap);\n        int off = Tables.size();\n        TableOffsets.push_back(off);\n        if (IsQual(cmap)) {\n            Tables.reserve(key_count);\n            for (int i = 0; i < key_count; ++i)\n                Tables.push_back(keys[i]);\n            TableSizes.push_back(key_count);\n        } else {\n            int max_size = 255 * (key_count - 1) + 1;\n            Tables.reserve(off + max_size);\n            // ImU32 last = keys[0];\n            // Tables.push_back(last);\n            // int n = 1;\n            for (int i = 0; i < key_count - 1; ++i) {\n                for (int s = 0; s < 255; ++s) {\n                    ImU32 a = keys[i];\n                    ImU32 b = keys[i + 1];\n                    ImU32 c = ImPlot3D::ImMixU32(a, b, s);\n                    // if (c != last) {\n                    Tables.push_back(c);\n                    // last = c;\n                    // n++;\n                    // }\n                }\n            }\n            ImU32 c = keys[key_count - 1];\n            // if (c != last) {\n            Tables.push_back(c);\n            // n++;\n            // }\n            // TableSizes.push_back(n);\n            TableSizes.push_back(max_size);\n        }\n    }\n\n    void RebuildTables() {\n        Tables.resize(0);\n        TableSizes.resize(0);\n        TableOffsets.resize(0);\n        for (int i = 0; i < Count; ++i)\n            _AppendTable(i);\n    }\n\n    inline bool IsQual(ImPlot3DColormap cmap) const { return Quals[cmap]; }\n    inline const char* GetName(ImPlot3DColormap cmap) const { return cmap < Count ? Text.Buf.Data + TextOffsets[cmap] : nullptr; }\n    inline ImPlot3DColormap GetIndex(const char* name) const {\n        ImGuiID key = ImHashStr(name);\n        return Map.GetInt(key, -1);\n    }\n\n    inline const ImU32* GetKeys(ImPlot3DColormap cmap) const { return &Keys[KeyOffsets[cmap]]; }\n    inline int GetKeyCount(ImPlot3DColormap cmap) const { return KeyCounts[cmap]; }\n    inline ImU32 GetKeyColor(ImPlot3DColormap cmap, int idx) const { return Keys[KeyOffsets[cmap] + idx]; }\n    inline void SetKeyColor(ImPlot3DColormap cmap, int idx, ImU32 value) {\n        Keys[KeyOffsets[cmap] + idx] = value;\n        RebuildTables();\n    }\n\n    inline const ImU32* GetTable(ImPlot3DColormap cmap) const { return &Tables[TableOffsets[cmap]]; }\n    inline int GetTableSize(ImPlot3DColormap cmap) const { return TableSizes[cmap]; }\n    inline ImU32 GetTableColor(ImPlot3DColormap cmap, int idx) const { return Tables[TableOffsets[cmap] + idx]; }\n\n    inline ImU32 LerpTable(ImPlot3DColormap cmap, float t) const {\n        int off = TableOffsets[cmap];\n        int siz = TableSizes[cmap];\n        int idx = Quals[cmap] ? ImClamp((int)(siz * t), 0, siz - 1) : (int)((siz - 1) * t + 0.5f);\n        return Tables[off + idx];\n    }\n};\n\n// State information for plot items\nstruct ImPlot3DItem {\n    ImGuiID ID;\n    ImU32 Color;\n    int NameOffset;\n    bool Show;\n    bool LegendHovered;\n    bool SeenThisFrame;\n\n    ImPlot3DItem() {\n        ID = 0;\n        Color = IM_COL32_WHITE;\n        NameOffset = -1;\n        Show = true;\n        LegendHovered = false;\n        SeenThisFrame = false;\n    }\n    ~ImPlot3DItem() { ID = 0; }\n};\n\n// Holds legend state\nstruct ImPlot3DLegend {\n    ImPlot3DLegendFlags Flags;\n    ImPlot3DLegendFlags PreviousFlags;\n    ImPlot3DLocation Location;\n    ImPlot3DLocation PreviousLocation;\n    ImVector<int> Indices;\n    ImGuiTextBuffer Labels;\n    ImRect Rect;\n    bool Hovered;\n    bool Held;\n\n    ImPlot3DLegend() {\n        PreviousFlags = Flags = ImPlot3DLegendFlags_None;\n        Hovered = Held = false;\n        PreviousLocation = Location = ImPlot3DLocation_NorthWest;\n    }\n\n    void Reset() {\n        Indices.shrink(0);\n        Labels.Buf.shrink(0);\n    }\n};\n\n// Holds items\nstruct ImPlot3DItemGroup {\n    ImPool<ImPlot3DItem> ItemPool;\n    ImPlot3DLegend Legend;\n    int ColormapIdx;\n\n    ImPlot3DItemGroup() { ColormapIdx = 0; }\n\n    int GetItemCount() const { return ItemPool.GetBufSize(); }\n    ImGuiID GetItemID(const char* label_id) { return ImGui::GetID(label_id); }\n    ImPlot3DItem* GetItem(ImGuiID id) { return ItemPool.GetByKey(id); }\n    ImPlot3DItem* GetItem(const char* label_id) { return GetItem(GetItemID(label_id)); }\n    ImPlot3DItem* GetOrAddItem(ImGuiID id) { return ItemPool.GetOrAddByKey(id); }\n    ImPlot3DItem* GetItemByIndex(int i) { return ItemPool.GetByIndex(i); }\n    int GetItemIndex(ImPlot3DItem* item) { return ItemPool.GetIndex(item); }\n    int GetLegendCount() const { return Legend.Indices.size(); }\n    ImPlot3DItem* GetLegendItem(int i) { return ItemPool.GetByIndex(Legend.Indices[i]); }\n    const char* GetLegendLabel(int i) { return Legend.Labels.Buf.Data + GetLegendItem(i)->NameOffset; }\n    void Reset() {\n        ItemPool.Clear();\n        Legend.Reset();\n        ColormapIdx = 0;\n    }\n};\n\n// Tick mark info\nstruct ImPlot3DTick {\n    float PlotPos;\n    bool Major;\n    bool ShowLabel;\n    ImVec2 LabelSize;\n    int TextOffset;\n    int Idx;\n\n    ImPlot3DTick(double value, bool major, bool show_label) {\n        PlotPos = (float)value;\n        Major = major;\n        ShowLabel = show_label;\n        TextOffset = -1;\n    }\n};\n\n// Collection of ticks\nstruct ImPlot3DTicker {\n    ImVector<ImPlot3DTick> Ticks;\n    ImGuiTextBuffer TextBuffer;\n\n    ImPlot3DTicker() { Reset(); }\n\n    ImPlot3DTick& AddTick(double value, bool major, bool show_label, const char* label) {\n        ImPlot3DTick tick(value, major, show_label);\n        if (show_label && label != nullptr) {\n            tick.TextOffset = TextBuffer.size();\n            TextBuffer.append(label, label + strlen(label) + 1);\n            tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset);\n        }\n        return AddTick(tick);\n    }\n\n    ImPlot3DTick& AddTick(double value, bool major, bool show_label, ImPlot3DFormatter formatter, void* data) {\n        ImPlot3DTick tick(value, major, show_label);\n        if (show_label && formatter != nullptr) {\n            char buff[IMPLOT3D_LABEL_MAX_SIZE];\n            tick.TextOffset = TextBuffer.size();\n            formatter(tick.PlotPos, buff, sizeof(buff), data);\n            TextBuffer.append(buff, buff + strlen(buff) + 1);\n            tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset);\n        }\n        return AddTick(tick);\n    }\n\n    inline ImPlot3DTick& AddTick(ImPlot3DTick tick) {\n        tick.Idx = Ticks.size();\n        Ticks.push_back(tick);\n        return Ticks.back();\n    }\n\n    const char* GetText(int idx) const { return TextBuffer.Buf.Data + Ticks[idx].TextOffset; }\n\n    const char* GetText(const ImPlot3DTick& tick) const { return GetText(tick.Idx); }\n\n    void Reset() {\n        Ticks.shrink(0);\n        TextBuffer.Buf.shrink(0);\n    }\n\n    int TickCount() const { return Ticks.Size; }\n};\n\n// Holds axis information\nstruct ImPlot3DAxis {\n    ImPlot3DAxisFlags Flags;\n    ImPlot3DAxisFlags PreviousFlags;\n    ImPlot3DRange Range;\n    ImPlot3DCond RangeCond;\n    ImGuiTextBuffer Label;\n    // Ticks\n    ImPlot3DTicker Ticker;\n    ImPlot3DFormatter Formatter;\n    void* FormatterData;\n    ImPlot3DLocator Locator;\n    bool ShowDefaultTicks;\n    // Fit data\n    bool FitThisFrame;\n    ImPlot3DRange FitExtents;\n    // Constraints\n    ImPlot3DRange ConstraintRange;\n    ImPlot3DRange ConstraintZoom;\n    // User input\n    bool Hovered;\n    bool Held;\n\n    // Constructor\n    ImPlot3DAxis() {\n        PreviousFlags = Flags = ImPlot3DAxisFlags_None;\n        // Range\n        Range.Min = 0.0f;\n        Range.Max = 1.0f;\n        RangeCond = ImPlot3DCond_None;\n        // Ticks\n        Formatter = nullptr;\n        FormatterData = nullptr;\n        Locator = nullptr;\n        ShowDefaultTicks = true;\n        // Fit data\n        FitThisFrame = true;\n        FitExtents = ImPlot3DRange(HUGE_VAL, -HUGE_VAL);\n        // Constraints\n        ConstraintRange = ImPlot3DRange(-INFINITY, INFINITY);\n        ConstraintZoom = ImPlot3DRange(FLT_MIN, INFINITY);\n        // User input\n        Hovered = false;\n        Held = false;\n    }\n\n    inline void Reset() {\n        RangeCond = ImPlot3DCond_None;\n        // Ticks\n        Ticker.Reset();\n        Formatter = nullptr;\n        FormatterData = nullptr;\n        Locator = nullptr;\n        ShowDefaultTicks = true;\n        // Fit data\n        FitExtents = ImPlot3DRange(HUGE_VAL, -HUGE_VAL);\n        // Constraints\n        ConstraintRange = ImPlot3DRange(-INFINITY, INFINITY);\n        ConstraintZoom = ImPlot3DRange(FLT_MIN, INFINITY);\n    }\n\n    inline void SetRange(double v1, double v2) {\n        Range.Min = (float)ImMin(v1, v2);\n        Range.Max = (float)ImMax(v1, v2);\n        Constrain();\n    }\n\n    inline bool SetMin(double _min, bool force = false) {\n        if (!force && IsLockedMin())\n            return false;\n        _min = ImPlot3D::ImConstrainNan((float)ImPlot3D::ImConstrainInf(_min));\n\n        // Constraints\n        if (_min < ConstraintRange.Min)\n            _min = ConstraintRange.Min;\n        double zoom = Range.Max - _min;\n        if (zoom < ConstraintZoom.Min)\n            _min = Range.Max - ConstraintZoom.Min;\n        if (zoom > ConstraintZoom.Max)\n            _min = Range.Max - ConstraintZoom.Max;\n\n        // Ensure min is less than max\n        if (_min >= Range.Max)\n            return false;\n\n        Range.Min = (float)_min;\n        return true;\n    }\n\n    inline bool SetMax(double _max, bool force = false) {\n        if (!force && IsLockedMax())\n            return false;\n        _max = ImPlot3D::ImConstrainNan((float)ImPlot3D::ImConstrainInf(_max));\n\n        // Constraints\n        if (_max > ConstraintRange.Max)\n            _max = ConstraintRange.Max;\n        double zoom = _max - Range.Min;\n        if (zoom < ConstraintZoom.Min)\n            _max = Range.Min + ConstraintZoom.Min;\n        if (zoom > ConstraintZoom.Max)\n            _max = Range.Min + ConstraintZoom.Max;\n\n        // Ensure max is greater than min\n        if (_max <= Range.Min)\n            return false;\n        Range.Max = (float)_max;\n        return true;\n    }\n\n    inline void Constrain() {\n        Range.Min = (float)ImPlot3D::ImConstrainNan((float)ImPlot3D::ImConstrainInf((double)Range.Min));\n        Range.Max = (float)ImPlot3D::ImConstrainNan((float)ImPlot3D::ImConstrainInf((double)Range.Max));\n        if (Range.Min < ConstraintRange.Min)\n            Range.Min = ConstraintRange.Min;\n        if (Range.Max > ConstraintRange.Max)\n            Range.Max = ConstraintRange.Max;\n        float zoom = Range.Size();\n        if (zoom < ConstraintZoom.Min) {\n            float delta = (ConstraintZoom.Min - zoom) * 0.5f;\n            Range.Min -= delta;\n            Range.Max += delta;\n        }\n        if (zoom > ConstraintZoom.Max) {\n            float delta = (zoom - ConstraintZoom.Max) * 0.5f;\n            Range.Min += delta;\n            Range.Max -= delta;\n        }\n        if (Range.Max <= Range.Min)\n            Range.Max = Range.Min + FLT_EPSILON;\n    }\n\n    inline bool IsRangeLocked() const { return RangeCond == ImPlot3DCond_Always; }\n    inline bool IsLockedMin() const { return IsRangeLocked() || ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_LockMin); }\n    inline bool IsLockedMax() const { return IsRangeLocked() || ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_LockMax); }\n    inline bool IsLocked() const { return IsLockedMin() && IsLockedMax(); }\n    inline bool IsInputLockedMin() const { return IsLockedMin() || IsAutoFitting(); }\n    inline bool IsInputLockedMax() const { return IsLockedMax() || IsAutoFitting(); }\n    inline bool IsInputLocked() const { return IsLocked() || IsAutoFitting(); }\n\n    inline bool IsPanLocked(bool increasing) {\n        if (ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_PanStretch)) {\n            return IsInputLocked();\n        } else {\n            if (IsLockedMin() || IsLockedMax() || IsAutoFitting())\n                return false;\n            if (increasing)\n                return Range.Max == ConstraintRange.Max;\n            else\n                return Range.Min == ConstraintRange.Min;\n        }\n    }\n\n    inline void SetLabel(const char* label) {\n        Label.Buf.shrink(0);\n        if (label && ImGui::FindRenderedTextEnd(label, nullptr) != label)\n            Label.append(label, label + strlen(label) + 1);\n    }\n\n    inline const char* GetLabel() const { return Label.Buf.Data; }\n\n    bool HasLabel() const;\n    bool HasGridLines() const;\n    bool HasTickLabels() const;\n    bool HasTickMarks() const;\n    bool IsAutoFitting() const;\n    void ExtendFit(float value);\n    void ApplyFit();\n};\n\n// Holds plot state information that must persist after EndPlot\nstruct ImPlot3DPlot {\n    ImGuiID ID;\n    ImPlot3DFlags Flags;\n    ImPlot3DFlags PreviousFlags;\n    ImGuiTextBuffer Title;\n    bool JustCreated;\n    bool Initialized;\n    // Bounding rectangles\n    ImRect FrameRect;  // Outermost bounding rectangle that encapsulates whole the plot/title/padding/etc\n    ImRect CanvasRect; // Frame rectangle reduced by padding\n    ImRect PlotRect;   // Bounding rectangle for the actual plot area\n    // Rotation & axes & box\n    ImPlot3DQuat InitialRotation; // Initial rotation quaternion\n    ImPlot3DQuat Rotation;        // Current rotation quaternion\n    ImPlot3DCond RotationCond;\n    ImPlot3DAxis Axes[3];   // X, Y, Z axes\n    ImPlot3DPoint BoxScale; // Scale factor for plot box X, Y, Z axes\n    // Animation\n    float AnimationTime;               // Remaining animation time\n    ImPlot3DQuat RotationAnimationEnd; // End rotation for animation\n    // User input\n    bool SetupLocked;\n    bool Hovered;\n    bool Held;\n    int HeldEdgeIdx;  // Index of the edge being held\n    int HeldPlaneIdx; // Index of the plane being held\n    // Fit data\n    bool FitThisFrame;\n    // Items\n    ImPlot3DItemGroup Items;\n    // 3D draw list\n    ImDrawList3D DrawList;\n    // Misc\n    bool ContextClick; // True if context button was clicked (to distinguish from double click)\n    bool OpenContextThisFrame;\n\n    ImPlot3DPlot() {\n        PreviousFlags = Flags = ImPlot3DFlags_None;\n        JustCreated = true;\n        Initialized = false;\n        InitialRotation = ImPlot3DQuat(-0.513269f, -0.212596f, -0.318184f, 0.76819f);\n        Rotation = ImPlot3DQuat(0.0f, 0.0f, 0.0f, 1.0f);\n        RotationCond = ImPlot3DCond_None;\n        for (int i = 0; i < 3; i++)\n            Axes[i] = ImPlot3DAxis();\n        BoxScale = ImPlot3DPoint(1.0f, 1.0f, 1.0f);\n        AnimationTime = 0.0f;\n        RotationAnimationEnd = Rotation;\n        SetupLocked = false;\n        Hovered = Held = false;\n        HeldEdgeIdx = -1;\n        HeldPlaneIdx = -1;\n        FitThisFrame = true;\n        ContextClick = false;\n        OpenContextThisFrame = false;\n    }\n\n    inline void SetTitle(const char* title) {\n        Title.Buf.shrink(0);\n        if (title && ImGui::FindRenderedTextEnd(title, nullptr) != title)\n            Title.append(title, title + strlen(title) + 1);\n    }\n    inline bool HasTitle() const { return !Title.empty() && !ImPlot3D::ImHasFlag(Flags, ImPlot3DFlags_NoTitle); }\n    inline const char* GetTitle() const { return Title.Buf.Data; }\n    inline bool IsRotationLocked() const { return RotationCond == ImPlot3DCond_Always; }\n\n    void ExtendFit(const ImPlot3DPoint& point);\n    ImPlot3DPoint RangeMin() const;\n    ImPlot3DPoint RangeMax() const;\n    ImPlot3DPoint RangeCenter() const;\n    void SetRange(const ImPlot3DPoint& min, const ImPlot3DPoint& max);\n    float GetBoxZoom() const;\n};\n\nstruct ImPlot3DContext {\n    ImPool<ImPlot3DPlot> Plots;\n    ImPlot3DPlot* CurrentPlot;\n    ImPlot3DItemGroup* CurrentItems;\n    ImPlot3DItem* CurrentItem;\n    ImPlot3DNextItemData NextItemData;\n    ImPlot3DStyle Style;\n    ImVector<ImGuiColorMod> ColorModifiers;\n    ImVector<ImGuiStyleMod> StyleModifiers;\n    ImVector<ImPlot3DColormap> ColormapModifiers;\n    ImPlot3DColormapData ColormapData;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Context Pointer\n//-----------------------------------------------------------------------------\n\nnamespace ImPlot3D {\n\n#ifndef GImPlot3D\nextern IMPLOT3D_API ImPlot3DContext* GImPlot3D; // Current context pointer\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Context Utils\n//-----------------------------------------------------------------------------\n\nIMPLOT3D_API void InitializeContext(ImPlot3DContext* ctx); // Initialize ImPlot3DContext\nIMPLOT3D_API void ResetContext(ImPlot3DContext* ctx);      // Reset ImPlot3DContext\n\n//-----------------------------------------------------------------------------\n// [SECTION] Style Utils\n//-----------------------------------------------------------------------------\n\nIMPLOT3D_API bool IsColorAuto(const ImVec4& col);\nIMPLOT3D_API bool IsColorAuto(ImPlot3DCol idx);\nIMPLOT3D_API ImVec4 GetAutoColor(ImPlot3DCol idx);\nIMPLOT3D_API const char* GetStyleColorName(ImPlot3DCol idx);\n\n// Returns white or black text given background color\nstatic inline ImU32 CalcTextColor(const ImVec4& bg) {\n    return (bg.x * 0.299f + bg.y * 0.587f + bg.z * 0.114f) > 0.5f ? IM_COL32_BLACK : IM_COL32_WHITE;\n}\nstatic inline ImU32 CalcTextColor(ImU32 bg) { return CalcTextColor(ImGui::ColorConvertU32ToFloat4(bg)); }\n\n// Get styling data for next item (call between BeginItem/EndItem)\nIMPLOT3D_API const ImPlot3DNextItemData& GetItemData();\n\n// Returns a color from the Color map given an index >= 0 (modulo will be performed)\nIMPLOT3D_API ImU32 GetColormapColorU32(int idx, ImPlot3DColormap cmap);\n\n// Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired\nIMPLOT3D_API ImU32 NextColormapColorU32();\n\n// Render a colormap bar\nIMPLOT3D_API void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed,\n                                 bool continuous);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Item Utils\n//-----------------------------------------------------------------------------\n\nIMPLOT3D_API bool BeginItem(const char* label_id, ImPlot3DItemFlags flags = 0, ImPlot3DCol recolor_from = IMPLOT3D_AUTO);\nIMPLOT3D_API void EndItem();\n\n// Register or get an existing item from the current plot\nIMPLOT3D_API ImPlot3DItem* RegisterOrGetItem(const char* label_id, ImPlot3DItemFlags flags, bool* just_created = nullptr);\n\n// Gets the current item from ImPlot3DContext\nIMPLOT3D_API ImPlot3DItem* GetCurrentItem();\n\n// Busts the cache for every item for every plot in the current context\nIMPLOT3D_API void BustItemCache();\n\n// TODO move to another place\nIMPLOT3D_API void AddTextRotated(ImDrawList* draw_list, ImVec2 pos, float angle, ImU32 col, const char* text_begin, const char* text_end = nullptr);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot Utils\n//-----------------------------------------------------------------------------\n\n// Gets the current plot from ImPlot3DContext\nIMPLOT3D_API ImPlot3DPlot* GetCurrentPlot();\n\n// Busts the cache for every plot in the current context\nIMPLOT3D_API void BustPlotCache();\n\nIMPLOT3D_API ImVec2 GetFramePos();  // Get the current frame position (top-left) in pixels\nIMPLOT3D_API ImVec2 GetFrameSize(); // Get the current frame size in pixels\n\n// Convert a position in the current plot's coordinate system to the current plot's normalized device coordinate system (NDC)\n// When the cube aspect ratio is [1,1,1], the NDC varies from [-0.5, 0.5] in each axis\nIMPLOT3D_API ImPlot3DPoint PlotToNDC(const ImPlot3DPoint& point);\nIMPLOT3D_API ImPlot3DPoint NDCToPlot(const ImPlot3DPoint& point);\n// Convert a position in the current plot's NDC to pixels\nIMPLOT3D_API ImVec2 NDCToPixels(const ImPlot3DPoint& point);\n// Convert a pixel coordinate to a ray in the NDC\nIMPLOT3D_API ImPlot3DRay PixelsToNDCRay(const ImVec2& pix);\n// Convert a ray in the NDC to a ray in the current plot's coordinate system\nIMPLOT3D_API ImPlot3DRay NDCRayToPlotRay(const ImPlot3DRay& ray);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Setup Utils\n//-----------------------------------------------------------------------------\n\nIMPLOT3D_API void SetupLock();\n\n//-----------------------------------------------------------------------------\n// [SECTION] Formatter\n//-----------------------------------------------------------------------------\n\nint Formatter_Default(float value, char* buff, int size, void* data);\n\n//------------------------------------------------------------------------------\n// [SECTION] Locator\n//------------------------------------------------------------------------------\n\nvoid Locator_Default(ImPlot3DTicker& ticker, const ImPlot3DRange& range, float pixels, ImPlot3DFormatter formatter, void* formatter_data);\n\n} // namespace ImPlot3D\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/implot3d/source/implot3d.cpp",
    "content": "//--------------------------------------------------\n// ImPlot3D v0.3 WIP\n// implot3d.cpp\n// Date: 2024-11-16\n// Author: Breno Cunha Queiroz (brenocq.com)\n//\n// Acknowledgments:\n//  ImPlot3D is heavily inspired by ImPlot\n//  (https://github.com/epezent/implot) by Evan Pezent,\n//  and follows a similar code style and structure to\n//  maintain consistency with ImPlot's API.\n//--------------------------------------------------\n\n// Table of Contents:\n// [SECTION] Includes\n// [SECTION] Macros\n// [SECTION] Context\n// [SECTION] Text Utils\n// [SECTION] Legend Utils\n// [SECTION] Mouse Position Utils\n// [SECTION] Plot Box Utils\n// [SECTION] Formatter\n// [SECTION] Locator\n// [SECTION] Context Menus\n// [SECTION] Begin/End Plot\n// [SECTION] Setup\n// [SECTION] Plot Utils\n// [SECTION] Setup Utils\n// [SECTION] Miscellaneous\n// [SECTION] Styles\n// [SECTION] Colormaps\n// [SECTION] Context Utils\n// [SECTION] Style Utils\n// [SECTION] ImPlot3DPoint\n// [SECTION] ImPlot3DBox\n// [SECTION] ImPlot3DRange\n// [SECTION] ImPlot3DQuat\n// [SECTION] ImDrawList3D\n// [SECTION] ImPlot3DAxis\n// [SECTION] ImPlot3DPlot\n// [SECTION] ImPlot3DStyle\n// [SECTION] Metrics\n\n//-----------------------------------------------------------------------------\n// [SECTION] Includes\n//-----------------------------------------------------------------------------\n\n#ifndef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS\n#endif\n\n// We define this to avoid accidentally using the deprecated API\n#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS\n#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS\n#endif\n\n#include \"implot3d.h\"\n#include \"implot3d_internal.h\"\n\n#ifndef IMGUI_DISABLE\n\n//-----------------------------------------------------------------------------\n// [SECTION] Macros\n//-----------------------------------------------------------------------------\n\n#define IMPLOT3D_CHECK_CTX()                                                                                                                         \\\n    IM_ASSERT_USER_ERROR(GImPlot3D != nullptr, \"No current context. Did you call ImPlot3D::CreateContext() or ImPlot3D::SetCurrentContext()?\")\n#define IMPLOT3D_CHECK_PLOT() IM_ASSERT_USER_ERROR(GImPlot3D->CurrentPlot != nullptr, \"No active plot. Did you call ImPlot3D::BeginPlot()?\")\n\n//-----------------------------------------------------------------------------\n// [SECTION] Context\n//-----------------------------------------------------------------------------\n\nnamespace ImPlot3D {\n\n// Global ImPlot3D context\n#ifndef GImPlot3D\nImPlot3DContext* GImPlot3D = nullptr;\n#endif\n\nImPlot3DContext* CreateContext() {\n    ImPlot3DContext* ctx = IM_NEW(ImPlot3DContext)();\n    if (GImPlot3D == nullptr)\n        SetCurrentContext(ctx);\n    InitializeContext(ctx);\n    return ctx;\n}\n\nvoid DestroyContext(ImPlot3DContext* ctx) {\n    if (ctx == nullptr)\n        ctx = GImPlot3D;\n    if (GImPlot3D == ctx)\n        SetCurrentContext(nullptr);\n    IM_DELETE(ctx);\n}\n\nImPlot3DContext* GetCurrentContext() { return GImPlot3D; }\n\nvoid SetCurrentContext(ImPlot3DContext* ctx) { GImPlot3D = ctx; }\n\n//-----------------------------------------------------------------------------\n// [SECTION] Text Utils\n//-----------------------------------------------------------------------------\n\nvoid AddTextRotated(ImDrawList* draw_list, ImVec2 pos, float angle, ImU32 col, const char* text_begin, const char* text_end) {\n    if (!text_end)\n        text_end = text_begin + strlen(text_begin);\n\n    ImGuiContext& g = *GImGui;\n    ImFont* font = g.Font;\n\n#ifdef IMGUI_HAS_TEXTURES\n    ImFontBaked* fontBaked = g.Font->GetFontBaked(g.FontSize);\n    const float scale = g.FontSize / fontBaked->Size;\n#else\n    const float scale = g.FontSize / font->FontSize;\n#endif\n\n    // Align to be pixel perfect\n    pos = ImFloor(pos);\n\n    // Measure the size of the text in unrotated coordinates\n    ImVec2 text_size = font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, text_begin, text_end, nullptr);\n\n    // Precompute sine and cosine of the angle (note: angle should be positive for rotation in ImGui)\n    float cos_a = cosf(-angle);\n    float sin_a = sinf(-angle);\n\n    const char* s = text_begin;\n    int chars_total = (int)(text_end - s);\n    int chars_rendered = 0;\n    const int vtx_count_max = chars_total * 4;\n    const int idx_count_max = chars_total * 6;\n    draw_list->PrimReserve(idx_count_max, vtx_count_max);\n\n    // Adjust pen position to center the text\n    ImVec2 pen = ImVec2(-text_size.x * 0.5f, -text_size.y * 0.5f);\n\n    while (s < text_end) {\n        unsigned int c = (unsigned int)*s;\n        if (c < 0x80) {\n            s += 1;\n        } else {\n            s += ImTextCharFromUtf8(&c, s, text_end);\n            if (c == 0) // Malformed UTF-8?\n                break;\n        }\n\n#ifdef IMGUI_HAS_TEXTURES\n        const ImFontGlyph* glyph = fontBaked->FindGlyph((ImWchar)c);\n#else\n        const ImFontGlyph* glyph = font->FindGlyph((ImWchar)c);\n#endif\n        if (glyph == nullptr) {\n            continue;\n        }\n\n        // Glyph dimensions and positions\n        ImVec2 glyph_offset = ImVec2(glyph->X0, glyph->Y0) * scale;\n        ImVec2 glyph_size = ImVec2(glyph->X1 - glyph->X0, glyph->Y1 - glyph->Y0) * scale;\n\n        // Corners of the glyph quad in unrotated space\n        ImVec2 corners[4];\n        corners[0] = pen + glyph_offset;\n        corners[1] = pen + glyph_offset + ImVec2(glyph_size.x, 0);\n        corners[2] = pen + glyph_offset + glyph_size;\n        corners[3] = pen + glyph_offset + ImVec2(0, glyph_size.y);\n\n        // Rotate and translate the corners\n        for (int i = 0; i < 4; i++) {\n            float x = corners[i].x;\n            float y = corners[i].y;\n            corners[i].x = x * cos_a - y * sin_a + pos.x;\n            corners[i].y = x * sin_a + y * cos_a + pos.y;\n        }\n\n        // Texture coordinates\n        ImVec2 uv0 = ImVec2(glyph->U0, glyph->V0);\n        ImVec2 uv1 = ImVec2(glyph->U1, glyph->V1);\n\n        // Render the glyph quad\n        draw_list->PrimQuadUV(corners[0], corners[1], corners[2], corners[3], uv0, ImVec2(glyph->U1, glyph->V0), uv1, ImVec2(glyph->U0, glyph->V1),\n                              col);\n\n        // Advance the pen position\n        pen.x += glyph->AdvanceX * scale;\n\n        chars_rendered++;\n    }\n\n    // Return unused vertices\n    int chars_skipped = chars_total - chars_rendered;\n    draw_list->PrimUnreserve(chars_skipped * 6, chars_skipped * 4);\n}\n\nvoid AddTextCentered(ImDrawList* draw_list, ImVec2 top_center, ImU32 col, const char* text_begin) {\n    const char* text_end = ImGui::FindRenderedTextEnd(text_begin);\n    ImVec2 text_size = ImGui::CalcTextSize(text_begin, text_end, true);\n    draw_list->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y), col, text_begin, text_end);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Legend Utils\n//-----------------------------------------------------------------------------\n\nstatic const char* short_legend_location[9] = {\"C\", \"N\", \"S\", \"W\", \"E\", \"NW\", \"NE\", \"SW\", \"SE\"};\n\nstatic const char* GetShortLegendLocationName(ImPlot3DLocation loc) {\n    if (loc == ImPlot3DLocation_Center)\n        return short_legend_location[0];\n    if (loc == ImPlot3DLocation_North)\n        return short_legend_location[1];\n    if (loc == ImPlot3DLocation_South)\n        return short_legend_location[2];\n    if (loc == ImPlot3DLocation_West)\n        return short_legend_location[3];\n    if (loc == ImPlot3DLocation_East)\n        return short_legend_location[4];\n    if (loc == ImPlot3DLocation_NorthWest)\n        return short_legend_location[5];\n    if (loc == ImPlot3DLocation_NorthEast)\n        return short_legend_location[6];\n    if (loc == ImPlot3DLocation_SouthWest)\n        return short_legend_location[7];\n    if (loc == ImPlot3DLocation_SouthEast)\n        return short_legend_location[8];\n    return nullptr;\n}\n\nImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlot3DLocation loc, const ImVec2& pad) {\n    ImVec2 pos;\n    // Legend x coordinate\n    if (ImPlot3D::ImHasFlag(loc, ImPlot3DLocation_West) && !ImPlot3D::ImHasFlag(loc, ImPlot3DLocation_East))\n        pos.x = outer_rect.Min.x + pad.x;\n    else if (!ImPlot3D::ImHasFlag(loc, ImPlot3DLocation_West) && ImPlot3D::ImHasFlag(loc, ImPlot3DLocation_East))\n        pos.x = outer_rect.Max.x - pad.x - inner_size.x;\n    else\n        pos.x = outer_rect.GetCenter().x - inner_size.x * 0.5f;\n    // Legend y coordinate\n    if (ImPlot3D::ImHasFlag(loc, ImPlot3DLocation_North) && !ImPlot3D::ImHasFlag(loc, ImPlot3DLocation_South))\n        pos.y = outer_rect.Min.y + pad.y;\n    else if (!ImPlot3D::ImHasFlag(loc, ImPlot3DLocation_North) && ImPlot3D::ImHasFlag(loc, ImPlot3DLocation_South))\n        pos.y = outer_rect.Max.y - pad.y - inner_size.y;\n    else\n        pos.y = outer_rect.GetCenter().y - inner_size.y * 0.5f;\n    pos.x = IM_ROUND(pos.x);\n    pos.y = IM_ROUND(pos.y);\n    return pos;\n}\n\nImVec2 CalcLegendSize(ImPlot3DItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical) {\n    const int nItems = items.GetLegendCount();\n    const float txt_ht = ImGui::GetTextLineHeight();\n    const float icon_size = txt_ht;\n    // Get label max width\n    float max_label_width = 0;\n    float sum_label_width = 0;\n    for (int i = 0; i < nItems; i++) {\n        const char* label = items.GetLegendLabel(i);\n        const float label_width = ImGui::CalcTextSize(label, nullptr, true).x;\n        max_label_width = label_width > max_label_width ? label_width : max_label_width;\n        sum_label_width += label_width;\n    }\n    // Compute legend size\n    const ImVec2 legend_size = vertical ? ImVec2(pad.x * 2 + icon_size + max_label_width, pad.y * 2 + nItems * txt_ht + (nItems - 1) * spacing.y)\n                                        : ImVec2(pad.x * 2 + icon_size * nItems + sum_label_width + (nItems - 1) * spacing.x, pad.y * 2 + txt_ht);\n    return legend_size;\n}\n\nvoid ShowLegendEntries(ImPlot3DItemGroup& items, const ImRect& legend_bb, bool hovered, const ImVec2& pad, const ImVec2& spacing, bool vertical,\n                       ImDrawList& draw_list) {\n    const float txt_ht = ImGui::GetTextLineHeight();\n    const float icon_size = txt_ht;\n    const float icon_shrink = 2;\n    ImU32 col_txt = GetStyleColorU32(ImPlot3DCol_LegendText);\n    ImU32 col_txt_dis = ImAlphaU32(col_txt, 0.25f);\n    float sum_label_width = 0;\n\n    const int num_items = items.GetLegendCount();\n    if (num_items == 0)\n        return;\n\n    // Render legend items\n    for (int i = 0; i < num_items; i++) {\n        const int idx = i;\n        ImPlot3DItem* item = items.GetLegendItem(idx);\n        const char* label = items.GetLegendLabel(idx);\n        const float label_width = ImGui::CalcTextSize(label, nullptr, true).x;\n        const ImVec2 top_left = vertical ? legend_bb.Min + pad + ImVec2(0, i * (txt_ht + spacing.y))\n                                         : legend_bb.Min + pad + ImVec2(i * (icon_size + spacing.x) + sum_label_width, 0);\n        sum_label_width += label_width;\n        ImRect icon_bb;\n        icon_bb.Min = top_left + ImVec2(icon_shrink, icon_shrink);\n        icon_bb.Max = top_left + ImVec2(icon_size - icon_shrink, icon_size - icon_shrink);\n        ImRect label_bb;\n        label_bb.Min = top_left;\n        label_bb.Max = top_left + ImVec2(label_width + icon_size, icon_size);\n        ImU32 col_txt_hl;\n        ImU32 col_item = ImAlphaU32(item->Color, 1);\n\n        ImRect button_bb(icon_bb.Min, label_bb.Max);\n\n        ImGui::KeepAliveID(item->ID);\n\n        bool item_hov = false;\n        bool item_hld = false;\n        bool item_clk = ImPlot3D::ImHasFlag(items.Legend.Flags, ImPlot3DLegendFlags_NoButtons)\n                            ? false\n                            : ImGui::ButtonBehavior(button_bb, item->ID, &item_hov, &item_hld);\n\n        if (item_clk)\n            item->Show = !item->Show;\n\n        const bool hovering = item_hov && !ImPlot3D::ImHasFlag(items.Legend.Flags, ImPlot3DLegendFlags_NoHighlightItem);\n\n        if (hovering) {\n            item->LegendHovered = true;\n            col_txt_hl = ImPlot3D::ImMixU32(col_txt, col_item, 64);\n        } else {\n            item->LegendHovered = false;\n            col_txt_hl = ImGui::GetColorU32(col_txt);\n        }\n\n        ImU32 col_icon;\n        if (item_hld)\n            col_icon = item->Show ? ImAlphaU32(col_item, 0.5f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f);\n        else if (item_hov)\n            col_icon = item->Show ? ImAlphaU32(col_item, 0.75f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.75f);\n        else\n            col_icon = item->Show ? col_item : col_txt_dis;\n\n        draw_list.AddRectFilled(icon_bb.Min, icon_bb.Max, col_icon);\n        const char* text_display_end = ImGui::FindRenderedTextEnd(label, nullptr);\n        if (label != text_display_end)\n            draw_list.AddText(top_left + ImVec2(icon_size, 0), item->Show ? col_txt_hl : col_txt_dis, label, text_display_end);\n    }\n}\n\nvoid RenderLegend() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    if (ImPlot3D::ImHasFlag(plot.Flags, ImPlot3DFlags_NoLegend) || plot.Items.GetLegendCount() == 0)\n        return;\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImDrawList* draw_list = window->DrawList;\n    const ImGuiIO& IO = ImGui::GetIO();\n\n    ImPlot3DLegend& legend = plot.Items.Legend;\n    const bool legend_horz = ImPlot3D::ImHasFlag(legend.Flags, ImPlot3DLegendFlags_Horizontal);\n    const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz);\n    const ImVec2 legend_pos = GetLocationPos(plot.PlotRect, legend_size, legend.Location, gp.Style.LegendPadding);\n    legend.Rect = ImRect(legend_pos, legend_pos + legend_size);\n\n    // Test hover\n    legend.Hovered = legend.Rect.Contains(IO.MousePos);\n\n    // Render background\n    ImU32 col_bg = GetStyleColorU32(ImPlot3DCol_LegendBg);\n    ImU32 col_bd = GetStyleColorU32(ImPlot3DCol_LegendBorder);\n    draw_list->AddRectFilled(legend.Rect.Min, legend.Rect.Max, col_bg);\n    draw_list->AddRect(legend.Rect.Min, legend.Rect.Max, col_bd);\n\n    // Render legends\n    ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, *draw_list);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Mouse Position Utils\n//-----------------------------------------------------------------------------\n\nvoid RenderMousePos() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    if (ImPlot3D::ImHasFlag(plot.Flags, ImPlot3DFlags_NoMouseText))\n        return;\n\n    ImVec2 mouse_pos = ImGui::GetMousePos();\n    ImPlot3DPoint mouse_plot_pos = PixelsToPlotPlane(mouse_pos, ImPlane3D_YZ, true);\n    if (mouse_plot_pos.IsNaN())\n        mouse_plot_pos = PixelsToPlotPlane(mouse_pos, ImPlane3D_XZ, true);\n    if (mouse_plot_pos.IsNaN())\n        mouse_plot_pos = PixelsToPlotPlane(mouse_pos, ImPlane3D_XY, true);\n\n    char buff[IMPLOT3D_LABEL_MAX_SIZE];\n    if (!mouse_plot_pos.IsNaN()) {\n        ImGuiTextBuffer builder;\n        builder.append(\"(\");\n        for (int i = 0; i < 3; i++) {\n            ImPlot3DAxis& axis = plot.Axes[i];\n            if (i > 0)\n                builder.append(\", \");\n            axis.Formatter(mouse_plot_pos[i], buff, IMPLOT3D_LABEL_MAX_SIZE, axis.FormatterData);\n            builder.append(buff);\n        }\n        builder.append(\")\");\n\n        const ImVec2 size = ImGui::CalcTextSize(builder.c_str());\n        // TODO custom location/padding\n        const ImVec2 pos = GetLocationPos(plot.PlotRect, size, ImPlot3DLocation_SouthEast, ImVec2(10, 10));\n        ImDrawList& draw_list = *ImGui::GetWindowDrawList();\n        draw_list.AddText(pos, GetStyleColorU32(ImPlot3DCol_InlayText), builder.c_str());\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot Box Utils\n//-----------------------------------------------------------------------------\n\n// Faces of the box (defined by 4 corner indices)\nstatic const int faces[6][4] = {\n    {0, 3, 7, 4}, // X-min face\n    {0, 4, 5, 1}, // Y-min face\n    {0, 1, 2, 3}, // Z-min face\n    {1, 2, 6, 5}, // X-max face\n    {3, 7, 6, 2}, // Y-max face\n    {4, 5, 6, 7}, // Z-max face\n};\n\n// Edges of the box (defined by 2 corner indices)\nstatic const int edges[12][2] = {\n    // Bottom face edges\n    {0, 1},\n    {1, 2},\n    {2, 3},\n    {3, 0},\n    // Top face edges\n    {4, 5},\n    {5, 6},\n    {6, 7},\n    {7, 4},\n    // Vertical edges\n    {0, 4},\n    {1, 5},\n    {2, 6},\n    {3, 7},\n};\n\n// Face edges (4 edge indices for each face)\nstatic const int face_edges[6][4] = {\n    {3, 11, 8, 7},  // X-min face\n    {0, 8, 4, 9},   // Y-min face\n    {0, 1, 2, 3},   // Z-min face\n    {1, 9, 5, 10},  // X-max face\n    {2, 10, 6, 11}, // Y-max face\n    {4, 5, 6, 7},   // Z-max face\n};\n\n// Lookup table for axis_corners based on active_faces (3D plot)\nstatic const int axis_corners_lookup_3d[8][3][2] = {\n    // Index 0: active_faces = {0, 0, 0}\n    {{3, 2}, {1, 2}, {1, 5}},\n    // Index 1: active_faces = {0, 0, 1}\n    {{7, 6}, {5, 6}, {1, 5}},\n    // Index 2: active_faces = {0, 1, 0}\n    {{0, 1}, {1, 2}, {2, 6}},\n    // Index 3: active_faces = {0, 1, 1}\n    {{4, 5}, {5, 6}, {2, 6}},\n    // Index 4: active_faces = {1, 0, 0}\n    {{3, 2}, {0, 3}, {0, 4}},\n    // Index 5: active_faces = {1, 0, 1}\n    {{7, 6}, {4, 7}, {0, 4}},\n    // Index 6: active_faces = {1, 1, 0}\n    {{0, 1}, {0, 3}, {3, 7}},\n    // Index 7: active_faces = {1, 1, 1}\n    {{4, 5}, {4, 7}, {3, 7}},\n};\n\n// Lookup table for axis_edges based on active_faces (3D plot)\nstatic const int axis_edges_lookup_3d[8][3] = {\n    // Index 0: active_faces = {0, 0, 0}\n    {2, 1, 9},\n    // Index 1: active_faces = {0, 0, 1}\n    {6, 5, 9},\n    // Index 2: active_faces = {0, 1, 0}\n    {0, 1, 10},\n    // Index 3: active_faces = {0, 1, 1}\n    {4, 5, 10},\n    // Index 4: active_faces = {1, 0, 0}\n    {2, 3, 8},\n    // Index 5: active_faces = {1, 0, 1}\n    {6, 7, 8},\n    // Index 6: active_faces = {1, 1, 0}\n    {0, 3, 11},\n    // Index 7: active_faces = {1, 1, 1}\n    {4, 7, 11},\n};\n\n// Convert the X, Y and Z active_faces array to a integer value representation that can be looked up in axis_corners_lookup_3d\nint Active3DFacesToAxisLookupIndex(const bool* active_faces) {\n    return ((int)active_faces[0] << 2) | ((int)active_faces[1] << 1) | ((int)active_faces[2]);\n}\n\nint GetMouseOverPlane(const ImPlot3DPlot& plot, const bool* active_faces, const ImVec2* corners_pix, int* plane_out = nullptr) {\n    ImGuiIO& io = ImGui::GetIO();\n    ImVec2 mouse_pos = io.MousePos;\n    if (plane_out)\n        *plane_out = -1;\n\n    // Check each active face\n    for (int a = 0; a < 3; a++) {\n        int face_idx = a + 3 * active_faces[a];\n        ImVec2 p0 = corners_pix[faces[face_idx][0]];\n        ImVec2 p1 = corners_pix[faces[face_idx][1]];\n        ImVec2 p2 = corners_pix[faces[face_idx][2]];\n        ImVec2 p3 = corners_pix[faces[face_idx][3]];\n\n        // Check if the mouse is inside the face's quad (using a triangle check)\n        if (ImTriangleContainsPoint(p0, p1, p2, mouse_pos) || ImTriangleContainsPoint(p2, p3, p0, mouse_pos)) {\n            if (plane_out)\n                *plane_out = a;\n            return a; // Return the plane index: 0 -> YZ, 1 -> XZ, 2 -> XY\n        }\n    }\n\n    return -1; // Not over any active plane\n}\n\nint GetMouseOverAxis(const ImPlot3DPlot& plot, const bool* active_faces, const ImVec2* corners_pix, const int plane_2d, int* edge_out = nullptr) {\n    const float axis_proximity_threshold = 15.0f; // Distance in pixels to consider the mouse \"close\" to an axis\n\n    ImGuiIO& io = ImGui::GetIO();\n    ImVec2 mouse_pos = io.MousePos;\n    if (edge_out)\n        *edge_out = -1;\n\n    bool visible_edges[12];\n    for (int i = 0; i < 12; i++)\n        visible_edges[i] = false;\n    for (int a = 0; a < 3; a++) {\n        int face_idx = a + 3 * active_faces[a];\n        if (plane_2d != -1 && a != plane_2d)\n            continue;\n        for (size_t i = 0; i < 4; i++)\n            visible_edges[face_edges[face_idx][i]] = true;\n    }\n\n    // Check each edge for proximity to the mouse\n    for (int edge = 0; edge < 12; edge++) {\n        if (!visible_edges[edge])\n            continue;\n\n        ImVec2 p0 = corners_pix[edges[edge][0]];\n        ImVec2 p1 = corners_pix[edges[edge][1]];\n\n        // Check distance to the edge\n        ImVec2 closest_point = ImLineClosestPoint(p0, p1, mouse_pos);\n        float dist = ImLengthSqr(mouse_pos - closest_point);\n        if (dist <= axis_proximity_threshold) {\n            if (edge_out)\n                *edge_out = edge;\n\n            // Determine which axis the edge belongs to\n            if (edge == 0 || edge == 2 || edge == 4 || edge == 6)\n                return 0; // X-axis\n            else if (edge == 1 || edge == 3 || edge == 5 || edge == 7)\n                return 1; // Y-axis\n            else\n                return 2; // Z-axis\n        }\n    }\n\n    return -1; // Not over any axis\n}\n\nvoid RenderPlotBackground(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImVec2* corners_pix, const bool* active_faces, const int plane_2d) {\n    const ImVec4 col_bg = GetStyleColorVec4(ImPlot3DCol_PlotBg);\n    const ImVec4 col_bg_hov = col_bg + ImVec4(0.03f, 0.03f, 0.03f, 0.0f);\n\n    int hovered_plane = -1;\n    if (!plot.Held) {\n        // If the mouse is not held, highlight plane hovering when mouse over it\n        hovered_plane = GetMouseOverPlane(plot, active_faces, corners_pix);\n        if (GetMouseOverAxis(plot, active_faces, corners_pix, plane_2d) != -1)\n            hovered_plane = -1;\n    } else {\n        // If the mouse is held, highlight the held plane\n        hovered_plane = plot.HeldPlaneIdx;\n    }\n\n    for (int a = 0; a < 3; a++) {\n        int idx[4]; // Corner indices\n        for (int i = 0; i < 4; i++)\n            idx[i] = faces[a + 3 * active_faces[a]][i];\n        const ImU32 col = ImGui::ColorConvertFloat4ToU32((hovered_plane == a) ? col_bg_hov : col_bg);\n        draw_list->AddQuadFilled(corners_pix[idx[0]], corners_pix[idx[1]], corners_pix[idx[2]], corners_pix[idx[3]], col);\n    }\n}\n\nvoid RenderPlotBorder(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImVec2* corners_pix, const bool* active_faces, const int plane_2d) {\n    int hovered_edge = -1;\n    if (!plot.Held)\n        GetMouseOverAxis(plot, active_faces, corners_pix, plane_2d, &hovered_edge);\n    else\n        hovered_edge = plot.HeldEdgeIdx;\n\n    bool render_edge[12];\n    for (int i = 0; i < 12; i++)\n        render_edge[i] = false;\n    for (int a = 0; a < 3; a++) {\n        int face_idx = a + 3 * active_faces[a];\n        if (plane_2d != -1 && a != plane_2d)\n            continue;\n        for (size_t i = 0; i < 4; i++)\n            render_edge[face_edges[face_idx][i]] = true;\n    }\n\n    ImU32 col_bd = GetStyleColorU32(ImPlot3DCol_PlotBorder);\n    for (int i = 0; i < 12; i++) {\n        if (render_edge[i]) {\n            int idx0 = edges[i][0];\n            int idx1 = edges[i][1];\n            float thickness = i == hovered_edge ? 3.0f : 1.0f;\n            draw_list->AddLine(corners_pix[idx0], corners_pix[idx1], col_bd, thickness);\n        }\n    }\n}\n\nvoid RenderGrid(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImPlot3DPoint* corners, const bool* active_faces, const int plane_2d) {\n    ImVec4 col_grid = GetStyleColorVec4(ImPlot3DCol_AxisGrid);\n    ImU32 col_grid_minor = ImGui::GetColorU32(col_grid * ImVec4(1, 1, 1, 0.3f));\n    ImU32 col_grid_major = ImGui::GetColorU32(col_grid * ImVec4(1, 1, 1, 0.6f));\n    for (int face = 0; face < 3; face++) {\n        if (plane_2d != -1 && face != plane_2d)\n            continue;\n        int face_idx = face + 3 * active_faces[face];\n        const ImPlot3DAxis& axis_u = plot.Axes[(face + 1) % 3];\n        const ImPlot3DAxis& axis_v = plot.Axes[(face + 2) % 3];\n\n        // Get the two axes (u and v) that define the face plane\n        int idx0 = faces[face_idx][0];\n        int idx1 = faces[face_idx][1];\n        int idx3 = faces[face_idx][3];\n\n        // Corners of the face in plot space\n        ImPlot3DPoint p0 = corners[idx0];\n        ImPlot3DPoint p1 = corners[idx1];\n        ImPlot3DPoint p3 = corners[idx3];\n\n        // Vectors along the edges\n        ImPlot3DPoint u_vec = p1 - p0;\n        ImPlot3DPoint v_vec = p3 - p0;\n\n        // Render grid lines along u axis (axis_u)\n        if (!ImPlot3D::ImHasFlag(axis_u.Flags, ImPlot3DAxisFlags_NoGridLines))\n            for (int t = 0; t < axis_u.Ticker.TickCount(); ++t) {\n                const ImPlot3DTick& tick = axis_u.Ticker.Ticks[t];\n\n                // Compute position along u\n                float t_u = (tick.PlotPos - axis_u.Range.Min) / (axis_u.Range.Max - axis_u.Range.Min);\n\n                // Skip ticks that are out of range\n                if (t_u < 0.0f || t_u > 1.0f)\n                    continue;\n\n                ImPlot3DPoint p_start = p0 + u_vec * t_u;\n                ImPlot3DPoint p_end = p3 + u_vec * t_u;\n\n                // Convert to pixel coordinates\n                ImVec2 p_start_pix = PlotToPixels(p_start);\n                ImVec2 p_end_pix = PlotToPixels(p_end);\n\n                // Get color\n                ImU32 col_line = tick.Major ? col_grid_major : col_grid_minor;\n\n                // Draw the grid line\n                draw_list->AddLine(p_start_pix, p_end_pix, col_line);\n            }\n\n        // Render grid lines along v axis (axis_v)\n        if (!ImPlot3D::ImHasFlag(axis_v.Flags, ImPlot3DAxisFlags_NoGridLines))\n            for (int t = 0; t < axis_v.Ticker.TickCount(); ++t) {\n                const ImPlot3DTick& tick = axis_v.Ticker.Ticks[t];\n\n                // Compute position along v\n                float t_v = (tick.PlotPos - axis_v.Range.Min) / (axis_v.Range.Max - axis_v.Range.Min);\n\n                // Skip ticks that are out of range\n                if (t_v < 0.0f || t_v > 1.0f)\n                    continue;\n\n                ImPlot3DPoint p_start = p0 + v_vec * t_v;\n                ImPlot3DPoint p_end = p1 + v_vec * t_v;\n\n                // Convert to pixel coordinates\n                ImVec2 p_start_pix = PlotToPixels(p_start);\n                ImVec2 p_end_pix = PlotToPixels(p_end);\n\n                // Get color\n                ImU32 col_line = tick.Major ? col_grid_major : col_grid_minor;\n\n                // Draw the grid line\n                draw_list->AddLine(p_start_pix, p_end_pix, col_line);\n            }\n    }\n}\n\nvoid RenderTickMarks(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImPlot3DPoint* corners, const ImVec2* corners_pix,\n                     const int axis_corners[3][2], const int plane_2d) {\n    ImU32 col_tick = GetStyleColorU32(ImPlot3DCol_AxisTick);\n\n    auto DeterminePlaneForAxis = [&](int axis_idx) {\n        if (plane_2d != -1)\n            return plane_2d;\n        // If no plane chosen (-1), use:\n        // X or Y axis -> XY plane (2)\n        // Z axis -> YZ plane (0)\n        if (axis_idx == 2)\n            return 1; // Z-axis use XZ plane\n        else\n            return 2; // X or Y-axis use XY plane\n    };\n\n    for (int a = 0; a < 3; a++) {\n        const ImPlot3DAxis& axis = plot.Axes[a];\n        if (ImPlot3D::ImHasFlag(axis.Flags, ImPlot3DAxisFlags_NoTickMarks))\n            continue;\n\n        int idx0 = axis_corners[a][0];\n        int idx1 = axis_corners[a][1];\n        if (idx0 == idx1) // axis not visible or invalid\n            continue;\n\n        ImPlot3DPoint axis_start = corners[idx0];\n        ImPlot3DPoint axis_end = corners[idx1];\n        ImPlot3DPoint axis_dir = axis_end - axis_start;\n        float axis_len = axis_dir.Length();\n        if (axis_len < 1e-12f)\n            continue;\n        axis_dir /= axis_len;\n\n        // Draw axis line\n        ImVec2 axis_start_pix = corners_pix[idx0];\n        ImVec2 axis_end_pix = corners_pix[idx1];\n        draw_list->AddLine(axis_start_pix, axis_end_pix, col_tick);\n\n        // Choose plane\n        int chosen_plane = DeterminePlaneForAxis(a);\n\n        // Project axis_dir onto chosen plane\n        ImPlot3DPoint proj_dir = axis_dir;\n        if (chosen_plane == 0) {\n            // YZ plane: zero out x\n            proj_dir.x = 0.0f;\n        } else if (chosen_plane == 1) {\n            // XZ plane: zero out y\n            proj_dir.y = 0.0f;\n        } else if (chosen_plane == 2) {\n            // XY plane: zero out z\n            proj_dir.z = 0.0f;\n        }\n\n        float proj_len = proj_dir.Length();\n        if (proj_len < 1e-12f) {\n            // Axis is parallel to plane normal or something degenerate, skip ticks\n            continue;\n        }\n        proj_dir /= proj_len;\n\n        // Rotate 90 degrees in chosen plane\n        ImPlot3DPoint tick_dir;\n        if (chosen_plane == 0) {\n            // YZ plane\n            // proj_dir=(0,py,pz), rotate 90°: (py,pz) -> (-pz,py)\n            tick_dir = ImPlot3DPoint(0, -proj_dir.z, proj_dir.y);\n        } else if (chosen_plane == 1) {\n            // XZ plane (plane=1)\n            // proj_dir=(px,0,pz), rotate 90°: (px,pz) -> (-pz,px)\n            tick_dir = ImPlot3DPoint(-proj_dir.z, 0, proj_dir.x);\n        } else {\n            // XY plane\n            // proj_dir=(px,py,0), rotate by 90°: (px,py) -> (-py,px)\n            tick_dir = ImPlot3DPoint(-proj_dir.y, proj_dir.x, 0);\n        }\n        tick_dir.Normalize();\n\n        // Tick lengths in NDC units\n        const float major_size_ndc = 0.06f;\n        const float minor_size_ndc = 0.03f;\n\n        for (int t = 0; t < axis.Ticker.TickCount(); ++t) {\n            const ImPlot3DTick& tick = axis.Ticker.Ticks[t];\n            float v = (tick.PlotPos - axis.Range.Min) / (axis.Range.Max - axis.Range.Min);\n\n            // Skip ticks that are out of range\n            if (v < 0.0f || v > 1.0f)\n                continue;\n            ImPlot3DPoint tick_pos_ndc = PlotToNDC(axis_start + axis_dir * (v * axis_len));\n\n            // Half tick on each side of the axis line\n            float size_tick_ndc = tick.Major ? major_size_ndc : minor_size_ndc;\n            ImPlot3DPoint half_tick_ndc = tick_dir * (size_tick_ndc * 0.5f);\n\n            ImPlot3DPoint T1_ndc = tick_pos_ndc - half_tick_ndc;\n            ImPlot3DPoint T2_ndc = tick_pos_ndc + half_tick_ndc;\n\n            ImVec2 T1_screen = NDCToPixels(T1_ndc);\n            ImVec2 T2_screen = NDCToPixels(T2_ndc);\n\n            draw_list->AddLine(T1_screen, T2_screen, col_tick);\n        }\n    }\n}\n\nvoid RenderTickLabels(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImPlot3DPoint* corners, const ImVec2* corners_pix,\n                      const int axis_corners[3][2]) {\n    ImU32 col_tick_txt = GetStyleColorU32(ImPlot3DCol_AxisText);\n\n    for (int a = 0; a < 3; a++) {\n        const ImPlot3DAxis& axis = plot.Axes[a];\n        if (ImPlot3D::ImHasFlag(axis.Flags, ImPlot3DAxisFlags_NoTickLabels))\n            continue;\n\n        // Corner indices for this axis\n        int idx0 = axis_corners[a][0];\n        int idx1 = axis_corners[a][1];\n\n        // If normal to the 2D plot, ignore the ticks\n        if (idx0 == idx1)\n            continue;\n\n        // Start and end points of the axis in plot space\n        ImPlot3DPoint axis_start = corners[idx0];\n        ImPlot3DPoint axis_end = corners[idx1];\n\n        // Direction vector along the axis\n        ImPlot3DPoint axis_dir = axis_end - axis_start;\n\n        // Convert axis start and end to screen space\n        ImVec2 axis_start_pix = corners_pix[idx0];\n        ImVec2 axis_end_pix = corners_pix[idx1];\n\n        // Screen space axis direction\n        ImVec2 axis_screen_dir = axis_end_pix - axis_start_pix;\n        float axis_length = ImSqrt(ImLengthSqr(axis_screen_dir));\n        if (axis_length != 0.0f)\n            axis_screen_dir /= axis_length;\n        else\n            axis_screen_dir = ImVec2(1.0f, 0.0f); // Default direction if length is zero\n\n        // Perpendicular direction in screen space\n        ImVec2 offset_dir_pix = ImVec2(-axis_screen_dir.y, axis_screen_dir.x);\n\n        // Make sure direction points away from cube center\n        ImVec2 box_center_pix = PlotToPixels(plot.RangeCenter());\n        ImVec2 axis_center_pix = (axis_start_pix + axis_end_pix) * 0.5f;\n        ImVec2 center_to_axis_pix = axis_center_pix - box_center_pix;\n        center_to_axis_pix /= ImSqrt(ImLengthSqr(center_to_axis_pix));\n        if (ImDot(offset_dir_pix, center_to_axis_pix) < 0.0f)\n            offset_dir_pix = -offset_dir_pix;\n\n        // Adjust the offset magnitude\n        float offset_magnitude = 20.0f; // TODO Calculate based on label size\n        ImVec2 offset_pix = offset_dir_pix * offset_magnitude;\n\n        // Compute angle perpendicular to axis in screen space\n        float angle = atan2f(-axis_screen_dir.y, axis_screen_dir.x) + IM_PI * 0.5f;\n\n        // Normalize angle to be between -π and π\n        if (angle > IM_PI)\n            angle -= 2 * IM_PI;\n        if (angle < -IM_PI)\n            angle += 2 * IM_PI;\n\n        // Adjust angle to keep labels upright\n        if (angle > IM_PI * 0.5f)\n            angle -= IM_PI;\n        if (angle < -IM_PI * 0.5f)\n            angle += IM_PI;\n\n        // Loop over ticks\n        for (int t = 0; t < axis.Ticker.TickCount(); ++t) {\n            const ImPlot3DTick& tick = axis.Ticker.Ticks[t];\n            if (!tick.ShowLabel)\n                continue;\n\n            // Compute position along the axis\n            float t_axis = (tick.PlotPos - axis.Range.Min) / (axis.Range.Max - axis.Range.Min);\n\n            // Skip ticks that are out of range\n            if (t_axis < 0.0f || t_axis > 1.0f)\n                continue;\n            ImPlot3DPoint tick_pos = axis_start + axis_dir * t_axis;\n\n            // Convert to pixel coordinates\n            ImVec2 tick_pos_pix = PlotToPixels(tick_pos);\n\n            // Get the tick label text\n            const char* label = axis.Ticker.GetText(tick);\n\n            // Adjust label position by offset\n            ImVec2 label_pos_pix = tick_pos_pix + offset_pix;\n\n            // Render the tick label\n            AddTextRotated(draw_list, label_pos_pix, angle, col_tick_txt, label);\n        }\n    }\n}\n\nvoid RenderAxisLabels(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImPlot3DPoint* corners, const ImVec2* corners_pix,\n                      const int axis_corners[3][2]) {\n    for (int a = 0; a < 3; a++) {\n        const ImPlot3DAxis& axis = plot.Axes[a];\n        if (!axis.HasLabel())\n            continue;\n\n        const char* label = axis.GetLabel();\n\n        // Corner indices\n        int idx0 = axis_corners[a][0];\n        int idx1 = axis_corners[a][1];\n\n        // If normal to the 2D plot, ignore axis label\n        if (idx0 == idx1)\n            continue;\n\n        // Position at the end of the axis\n        ImPlot3DPoint label_pos = (PlotToNDC(corners[idx0]) + PlotToNDC(corners[idx1])) * 0.5f;\n        ImPlot3DPoint center_dir = label_pos.Normalized();\n        // Add offset\n        label_pos += center_dir * 0.3f;\n\n        // Convert to pixel coordinates\n        ImVec2 label_pos_pix = NDCToPixels(label_pos);\n\n        // Adjust label position and angle\n        ImU32 col_ax_txt = GetStyleColorU32(ImPlot3DCol_AxisText);\n\n        // Compute text angle\n        ImVec2 screen_delta = corners_pix[idx1] - corners_pix[idx0];\n        float angle = atan2f(-screen_delta.y, screen_delta.x);\n        if (angle > IM_PI * 0.5f)\n            angle -= IM_PI;\n        if (angle < -IM_PI * 0.5f)\n            angle += IM_PI;\n\n        AddTextRotated(draw_list, label_pos_pix, angle, col_ax_txt, label);\n    }\n}\n\n// Function to compute active faces based on the rotation\n// If the plot is close to 2D, plane_2d is set to the plane index (0 -> YZ, 1 -> XZ, 2 -> XY)\n// plane_2d is set to -1 otherwise\nvoid ComputeActiveFaces(bool* active_faces, const ImPlot3DQuat& rotation, const ImPlot3DAxis* axes, int* plane_2d = nullptr) {\n    if (plane_2d)\n        *plane_2d = -1;\n\n    ImPlot3DPoint rot_face_n[3] = {\n        rotation * ImPlot3DPoint(1.0f, 0.0f, 0.0f),\n        rotation * ImPlot3DPoint(0.0f, 1.0f, 0.0f),\n        rotation * ImPlot3DPoint(0.0f, 0.0f, 1.0f),\n    };\n\n    int num_deg = 0; // Check number of planes that are degenerate (seen as a line)\n    for (int i = 0; i < 3; i++) {\n        // Determine the active face based on the Z component\n        if (fabs(rot_face_n[i].z) < 0.025) {\n            // If aligned with the plane, choose the min face for bottom/left\n            active_faces[i] = rot_face_n[i].x + rot_face_n[i].y < 0.0f;\n            num_deg++;\n        } else {\n            // Otherwise, determine based on the Z component\n            bool is_inverted = ImHasFlag(axes[i].Flags, ImPlot3DAxisFlags_Invert);\n            active_faces[i] = is_inverted ? (rot_face_n[i].z > 0.0f) : (rot_face_n[i].z < 0.0f);\n            // Set this plane as possible 2d plane\n            if (plane_2d)\n                *plane_2d = i;\n        }\n    }\n    // Only return 2d plane if there are exactly 2 degenerate planes\n    if (num_deg != 2 && plane_2d)\n        *plane_2d = -1;\n}\n\n// Function to compute the box corners in plot space\nvoid ComputeBoxCorners(ImPlot3DPoint* corners, const ImPlot3DPoint& range_min, const ImPlot3DPoint& range_max) {\n    corners[0] = ImPlot3DPoint(range_min.x, range_min.y, range_min.z); // 0\n    corners[1] = ImPlot3DPoint(range_max.x, range_min.y, range_min.z); // 1\n    corners[2] = ImPlot3DPoint(range_max.x, range_max.y, range_min.z); // 2\n    corners[3] = ImPlot3DPoint(range_min.x, range_max.y, range_min.z); // 3\n    corners[4] = ImPlot3DPoint(range_min.x, range_min.y, range_max.z); // 4\n    corners[5] = ImPlot3DPoint(range_max.x, range_min.y, range_max.z); // 5\n    corners[6] = ImPlot3DPoint(range_max.x, range_max.y, range_max.z); // 6\n    corners[7] = ImPlot3DPoint(range_min.x, range_max.y, range_max.z); // 7\n}\n\n// Convert a position in the plot's NDC to pixels\nImVec2 NDCToPixels(const ImPlot3DPlot& plot, const ImPlot3DPoint& point) {\n    ImVec2 center = plot.PlotRect.GetCenter();\n    ImPlot3DPoint point_pix = plot.GetBoxZoom() * (plot.Rotation * point);\n    point_pix.y *= -1.0f; // Invert y-axis\n    point_pix.x += center.x;\n    point_pix.y += center.y;\n\n    return {point_pix.x, point_pix.y};\n}\n\n// Convert a position in the plot's coordinate system to the plot's normalized device coordinate system (NDC)\nImPlot3DPoint PlotToNDC(const ImPlot3DPlot& plot, const ImPlot3DPoint& point) {\n    ImPlot3DPoint ndc_point;\n    for (int i = 0; i < 3; i++) {\n        const ImPlot3DAxis& axis = plot.Axes[i];\n        float ndc_range = 0.5f * plot.BoxScale[i];\n        float t = (point[i] - axis.Range.Min) / (axis.Range.Max - axis.Range.Min);\n        t *= plot.BoxScale[i];\n        ndc_point[i] = ImPlot3D::ImHasFlag(axis.Flags, ImPlot3DAxisFlags_Invert) ? (ndc_range - t) : (t - ndc_range);\n    }\n    return ndc_point;\n}\n\n// Convert a position in the plot's coordinate system to pixels\nImVec2 PlotToPixels(const ImPlot3DPlot& plot, const ImPlot3DPoint& point) { return NDCToPixels(plot, PlotToNDC(plot, point)); }\n\n// Function to compute the box corners in pixel space\nvoid ComputeBoxCornersPix(const ImPlot3DPlot& plot, ImVec2* corners_pix, const ImPlot3DPoint* corners) {\n    for (int i = 0; i < 8; i++) {\n        corners_pix[i] = PlotToPixels(plot, corners[i]);\n    }\n}\n\n// Function to get axes parameters which can then be used later down the line\nvoid GetAxesParameters(const ImPlot3DPlot& plot, bool* active_faces, ImVec2* corners_pix, ImPlot3DPoint* corners, int& plane_2d,\n                       int axis_corners[3][2]) {\n    // Get plot parameters\n    const ImPlot3DQuat& rotation = plot.Rotation;\n    ImPlot3DPoint range_min = plot.RangeMin();\n    ImPlot3DPoint range_max = plot.RangeMax();\n\n    // Compute active faces\n    ComputeActiveFaces(active_faces, rotation, plot.Axes, &plane_2d);\n    bool is_2d = plane_2d != -1;\n\n    // Compute box corners in plot space\n    ComputeBoxCorners(corners, range_min, range_max);\n\n    // Compute box corners in pixel space\n    ComputeBoxCornersPix(plot, corners_pix, corners);\n\n    // Compute axes start and end corners (given current rotation)\n    if (is_2d) {\n        int face = plane_2d + 3 * active_faces[plane_2d]; // Face of the 2D plot\n        int common_edges[2] = {-1, -1};                   // Edges shared by the 3 faces\n\n        // Find the common edges between the 3 faces\n        for (int i = 0; i < 4; i++) {\n            int edge = face_edges[face][i];\n            for (int j = 0; j < 2; j++) {\n                int axis = (plane_2d + 1 + j) % 3;\n                int face_idx = axis + active_faces[axis] * 3;\n                for (int k = 0; k < 4; k++) {\n                    if (face_edges[face_idx][k] == edge) {\n                        common_edges[j] = edge;\n                        break;\n                    }\n                }\n            }\n        }\n\n        // Get corners from 2 edges (origin is the corner in common)\n        int origin_corner = -1;\n        int x_corner = -1;\n        int y_corner = -1;\n        for (int i = 0; i < 2; i++)\n            for (int j = 0; j < 2; j++)\n                if (edges[common_edges[0]][i] == edges[common_edges[1]][j]) {\n                    origin_corner = edges[common_edges[0]][i];\n                    x_corner = edges[common_edges[0]][!i];\n                    y_corner = edges[common_edges[1]][!j];\n                }\n\n        // Swap x and y if they are flipped\n        ImVec2 x_vec = corners_pix[x_corner] - corners_pix[origin_corner];\n        ImVec2 y_vec = corners_pix[y_corner] - corners_pix[origin_corner];\n        if (y_vec.x > x_vec.x)\n            ImSwap(x_corner, y_corner);\n\n        // Check which 3d axis the 2d axis refers to\n        ImPlot3DPoint origin_3d = corners[origin_corner];\n        ImPlot3DPoint x_3d = (corners[x_corner] - origin_3d).Normalized();\n        ImPlot3DPoint y_3d = (corners[y_corner] - origin_3d).Normalized();\n        int x_axis = -1;\n        bool x_inverted = false;\n        int y_axis = -1;\n        bool y_inverted = false;\n        for (int i = 0; i < 2; i++) {\n            int axis_i = (plane_2d + 1 + i) % 3;\n            if (y_axis != -1 || (ImAbs(x_3d[axis_i]) > 1e-8f && x_axis == -1)) {\n                x_axis = axis_i;\n                x_inverted = x_3d[axis_i] < 0.0f;\n            } else {\n                y_axis = axis_i;\n                y_inverted = y_3d[axis_i] < 0.0f;\n            }\n        }\n\n        // Set the 3d axis corners based on the 2d axis corners\n        axis_corners[plane_2d][0] = -1;\n        axis_corners[plane_2d][1] = -1;\n        if (x_inverted) {\n            axis_corners[x_axis][0] = x_corner;\n            axis_corners[x_axis][1] = origin_corner;\n        } else {\n            axis_corners[x_axis][0] = origin_corner;\n            axis_corners[x_axis][1] = x_corner;\n        }\n        if (y_inverted) {\n            axis_corners[y_axis][0] = y_corner;\n            axis_corners[y_axis][1] = origin_corner;\n        } else {\n            axis_corners[y_axis][0] = origin_corner;\n            axis_corners[y_axis][1] = y_corner;\n        }\n    } else {\n        int index = Active3DFacesToAxisLookupIndex(active_faces);\n        for (int a = 0; a < 3; a++) {\n            axis_corners[a][0] = axis_corners_lookup_3d[index][a][0];\n            axis_corners[a][1] = axis_corners_lookup_3d[index][a][1];\n        }\n    }\n}\n\nvoid RenderPlotBox(ImDrawList* draw_list, const ImPlot3DPlot& plot) {\n    bool active_faces[3];\n    ImVec2 corners_pix[8];\n    ImPlot3DPoint corners[8];\n    int plane_2d;\n    int axis_corners[3][2];\n\n    GetAxesParameters(plot, active_faces, corners_pix, corners, plane_2d, axis_corners);\n\n    // Render components\n    RenderPlotBackground(draw_list, plot, corners_pix, active_faces, plane_2d);\n    RenderPlotBorder(draw_list, plot, corners_pix, active_faces, plane_2d);\n    RenderGrid(draw_list, plot, corners, active_faces, plane_2d);\n    RenderTickMarks(draw_list, plot, corners, corners_pix, axis_corners, plane_2d);\n    RenderTickLabels(draw_list, plot, corners, corners_pix, axis_corners);\n    RenderAxisLabels(draw_list, plot, corners, corners_pix, axis_corners);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Formatter\n//-----------------------------------------------------------------------------\n\nint Formatter_Default(float value, char* buff, int size, void* data) {\n    char* fmt = (char*)data;\n    return ImFormatString(buff, size, fmt, value);\n}\n\n//------------------------------------------------------------------------------\n// [SECTION] Locator\n//------------------------------------------------------------------------------\n\ndouble NiceNum(double x, bool round) {\n    double f;\n    double nf;\n    int expv = (int)floor(ImLog10((float)x));\n    f = x / ImPow(10.0, (double)expv);\n    if (round)\n        if (f < 1.5)\n            nf = 1;\n        else if (f < 3)\n            nf = 2;\n        else if (f < 7)\n            nf = 5;\n        else\n            nf = 10;\n    else if (f <= 1)\n        nf = 1;\n    else if (f <= 2)\n        nf = 2;\n    else if (f <= 5)\n        nf = 5;\n    else\n        nf = 10;\n    return nf * ImPow(10.0, expv);\n}\n\nvoid Locator_Default(ImPlot3DTicker& ticker, const ImPlot3DRange& range, float pixels, ImPlot3DFormatter formatter, void* formatter_data) {\n    if (range.Min == range.Max)\n        return;\n    const int nMinor = ImMin(ImMax(1, (int)IM_ROUND(pixels / 30.0f)), 5);\n    const int nMajor = ImMax(2, (int)IM_ROUND(pixels / 80.0f));\n    const int max_ticks_labels = 7;\n    const double nice_range = NiceNum(range.Size() * 0.99, false);\n    const double interval = NiceNum(nice_range / (nMajor - 1), true);\n    const double graphmin = floor(range.Min / interval) * interval;\n    const double graphmax = ceil(range.Max / interval) * interval;\n    bool first_major_set = false;\n    int first_major_idx = 0;\n    const int idx0 = ticker.TickCount(); // Ticker may have user custom ticks\n    for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) {\n        // Is this zero? combat zero formatting issues\n        if (major - interval < 0 && major + interval > 0)\n            major = 0;\n        if (range.Contains((float)major)) {\n            if (!first_major_set) {\n                first_major_idx = ticker.TickCount();\n                first_major_set = true;\n            }\n            ticker.AddTick(major, true, true, formatter, formatter_data);\n        }\n        for (int i = 1; i < nMinor; i++) {\n            double minor = major + i * interval / nMinor;\n            if (range.Contains((float)minor)) {\n                ticker.AddTick(minor, false, true, formatter, formatter_data);\n            }\n        }\n    }\n\n    // Prune tick labels\n    if (ticker.TickCount() > max_ticks_labels) {\n        for (int i = first_major_idx - 1; i >= idx0; i -= 2)\n            ticker.Ticks[i].ShowLabel = false;\n        for (int i = first_major_idx + 1; i < ticker.TickCount(); i += 2)\n            ticker.Ticks[i].ShowLabel = false;\n    }\n}\n\nvoid AddTicksCustom(const double* values, const char* const labels[], int n, ImPlot3DTicker& ticker, ImPlot3DFormatter formatter, void* data) {\n    for (int i = 0; i < n; i++) {\n        if (labels != nullptr)\n            ticker.AddTick(values[i], false, true, labels[i]);\n        else\n            ticker.AddTick(values[i], false, true, formatter, data);\n    }\n}\n\n//------------------------------------------------------------------------------\n// [SECTION] Context Menus\n//------------------------------------------------------------------------------\n\nstatic const char* axis_contexts[3] = {\"##XAxisContext\", \"##YAxisContext\", \"##ZAxisContext\"};\nstatic const char* axis_labels[3] = {\"X-Axis\", \"Y-Axis\", \"Z-Axis\"};\nstatic const char* plane_contexts[3] = {\"##YZPlaneContext\", \"##XZPlaneContext\", \"##XYPlaneContext\"};\nstatic const char* plane_labels[3] = {\"YZ-Plane\", \"XZ-Plane\", \"XY-Plane\"};\n\nbool ShowLegendContextMenu(ImPlot3DLegend& legend, bool visible) {\n    const float s = ImGui::GetFrameHeight();\n    bool ret = false;\n    if (ImGui::Checkbox(\"Show\", &visible))\n        ret = true;\n    if (ImGui::RadioButton(\"H\", ImPlot3D::ImHasFlag(legend.Flags, ImPlot3DLegendFlags_Horizontal)))\n        legend.Flags |= ImPlot3DLegendFlags_Horizontal;\n    ImGui::SameLine();\n    if (ImGui::RadioButton(\"V\", !ImPlot3D::ImHasFlag(legend.Flags, ImPlot3DLegendFlags_Horizontal)))\n        legend.Flags &= ~ImPlot3DLegendFlags_Horizontal;\n    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 2));\n    // clang-format off\n    if (ImGui::Button(GetShortLegendLocationName(ImPlot3DLocation_NorthWest),ImVec2(1.5f*s,s))) { legend.Location = ImPlot3DLocation_NorthWest; } ImGui::SameLine();\n    if (ImGui::Button(GetShortLegendLocationName(ImPlot3DLocation_North) , ImVec2(1.5f*s,s))) { legend.Location = ImPlot3DLocation_North;     } ImGui::SameLine();\n    if (ImGui::Button(GetShortLegendLocationName(ImPlot3DLocation_NorthEast), ImVec2(1.5f*s,s))) { legend.Location = ImPlot3DLocation_NorthEast; }\n    if (ImGui::Button(GetShortLegendLocationName(ImPlot3DLocation_West), ImVec2(1.5f*s,s))) { legend.Location = ImPlot3DLocation_West;      } ImGui::SameLine();\n    if (ImGui::InvisibleButton(GetShortLegendLocationName(ImPlot3DLocation_Center), ImVec2(1.5f*s,s))) {     } ImGui::SameLine();\n    if (ImGui::Button(GetShortLegendLocationName(ImPlot3DLocation_East), ImVec2(1.5f*s,s))) { legend.Location = ImPlot3DLocation_East;      }\n    if (ImGui::Button(GetShortLegendLocationName(ImPlot3DLocation_SouthWest),ImVec2(1.5f*s,s))) { legend.Location = ImPlot3DLocation_SouthWest; } ImGui::SameLine();\n    if (ImGui::Button(GetShortLegendLocationName(ImPlot3DLocation_South), ImVec2(1.5f*s,s))) { legend.Location = ImPlot3DLocation_South;     } ImGui::SameLine();\n    if (ImGui::Button(GetShortLegendLocationName(ImPlot3DLocation_SouthEast),ImVec2(1.5f*s,s))) { legend.Location = ImPlot3DLocation_SouthEast; }\n    // clang-format on\n    ImGui::PopStyleVar();\n    return ret;\n}\n\nvoid ShowAxisContextMenu(ImPlot3DAxis& axis) {\n    ImGui::PushItemWidth(75);\n    bool always_locked = axis.IsRangeLocked() || axis.IsAutoFitting();\n    bool label = axis.HasLabel();\n    bool grid = axis.HasGridLines();\n    bool ticks = axis.HasTickMarks();\n    bool labels = axis.HasTickLabels();\n    double drag_speed =\n        (axis.Range.Size() <= FLT_EPSILON) ? FLT_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits.\n\n    ImGui::BeginDisabled(always_locked);\n    ImGui::CheckboxFlags(\"##LockMin\", (unsigned int*)&axis.Flags, ImPlot3DAxisFlags_LockMin);\n    ImGui::EndDisabled();\n    ImGui::SameLine();\n\n    ImGui::BeginDisabled(axis.IsLockedMin() || always_locked);\n    float temp_min = axis.Range.Min;\n    if (ImGui::DragFloat(\"Min\", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - FLT_EPSILON)) {\n        axis.SetMin(temp_min, true);\n    }\n    ImGui::EndDisabled();\n\n    ImGui::BeginDisabled(always_locked);\n    ImGui::CheckboxFlags(\"##LockMax\", (unsigned int*)&axis.Flags, ImPlot3DAxisFlags_LockMax);\n    ImGui::EndDisabled();\n    ImGui::SameLine();\n    ImGui::BeginDisabled(axis.IsLockedMax() || always_locked);\n    float temp_max = axis.Range.Max;\n    if (ImGui::DragFloat(\"Max\", &temp_max, (float)drag_speed, axis.Range.Min + FLT_EPSILON, HUGE_VAL)) {\n        axis.SetMax(temp_max, true);\n    }\n    ImGui::EndDisabled();\n\n    ImGui::Separator();\n\n    // Flags\n    ImGui::CheckboxFlags(\"Auto-Fit\", (unsigned int*)&axis.Flags, ImPlot3DAxisFlags_AutoFit);\n    ImGui::Separator();\n\n    bool inverted = ImPlot3D::ImHasFlag(axis.Flags, ImPlot3DAxisFlags_Invert);\n    if (ImGui::Checkbox(\"Invert\", &inverted))\n        ImFlipFlag(axis.Flags, ImPlot3DAxisFlags_Invert);\n\n    ImGui::Separator();\n\n    ImGui::BeginDisabled(axis.Label.empty());\n    if (ImGui::Checkbox(\"Label\", &label))\n        ImFlipFlag(axis.Flags, ImPlot3DAxisFlags_NoLabel);\n    ImGui::EndDisabled();\n\n    if (ImGui::Checkbox(\"Grid Lines\", &grid))\n        ImFlipFlag(axis.Flags, ImPlot3DAxisFlags_NoGridLines);\n    if (ImGui::Checkbox(\"Tick Marks\", &ticks))\n        ImFlipFlag(axis.Flags, ImPlot3DAxisFlags_NoTickMarks);\n    if (ImGui::Checkbox(\"Tick Labels\", &labels))\n        ImFlipFlag(axis.Flags, ImPlot3DAxisFlags_NoTickLabels);\n}\n\nvoid ShowPlaneContextMenu(ImPlot3DPlot& plot, int plane_idx) {\n    for (int i = 0; i < 3; i++) {\n        if (i == plane_idx)\n            continue;\n        ImPlot3DAxis& axis = plot.Axes[i];\n        ImGui::PushID(i);\n        if (ImGui::BeginMenu(axis.HasLabel() ? axis.GetLabel() : axis_labels[i])) {\n            ShowAxisContextMenu(axis);\n            ImGui::EndMenu();\n        }\n        ImGui::PopID();\n    }\n}\n\nvoid ShowPlotContextMenu(ImPlot3DPlot& plot) {\n    char buf[16] = {};\n    for (int i = 0; i < 3; i++) {\n        ImPlot3DAxis& axis = plot.Axes[i];\n        ImGui::PushID(i);\n        ImFormatString(buf, sizeof(buf) - 1, i == 0 ? \"X-Axis\" : \"X-Axis %d\", i + 1);\n        if (ImGui::BeginMenu(axis.HasLabel() ? axis.GetLabel() : axis_labels[i])) {\n            ShowAxisContextMenu(axis);\n            ImGui::EndMenu();\n        }\n        ImGui::PopID();\n    }\n\n    ImGui::Separator();\n\n    if ((ImGui::BeginMenu(\"Box\"))) {\n        ImGui::PushItemWidth(75);\n        float temp_scale[3] = {plot.BoxScale[0], plot.BoxScale[1], plot.BoxScale[2]};\n        if (ImGui::DragFloat(\"Scale X\", &temp_scale[0], 0.01f, 0.1f, 3.0f))\n            plot.BoxScale[0] = ImMax(temp_scale[0], 0.01f);\n        if (ImGui::DragFloat(\"Scale Y\", &temp_scale[1], 0.01f, 0.1f, 3.0f))\n            plot.BoxScale[1] = ImMax(temp_scale[1], 0.01f);\n        if (ImGui::DragFloat(\"Scale Z\", &temp_scale[2], 0.01f, 0.1f, 3.0f))\n            plot.BoxScale[2] = ImMax(temp_scale[2], 0.01f);\n        ImGui::PopItemWidth();\n        ImGui::EndMenu();\n    }\n\n    ImGui::Separator();\n\n    if ((ImGui::BeginMenu(\"Legend\"))) {\n        if (ShowLegendContextMenu(plot.Items.Legend, !ImPlot3D::ImHasFlag(plot.Flags, ImPlot3DFlags_NoLegend)))\n            ImFlipFlag(plot.Flags, ImPlot3DFlags_NoLegend);\n        ImGui::EndMenu();\n    }\n\n    if ((ImGui::BeginMenu(\"Settings\"))) {\n        ImGui::BeginDisabled(plot.Title.empty());\n        if (ImGui::MenuItem(\"Title\", nullptr, plot.HasTitle()))\n            ImFlipFlag(plot.Flags, ImPlot3DFlags_NoTitle);\n        ImGui::EndDisabled();\n        if (ImGui::MenuItem(\"Clip\", nullptr, !ImHasFlag(plot.Flags, ImPlot3DFlags_NoClip)))\n            ImFlipFlag(plot.Flags, ImPlot3DFlags_NoClip);\n        if (ImGui::MenuItem(\"Mouse Position\", nullptr, !ImHasFlag(plot.Flags, ImPlot3DFlags_NoMouseText)))\n            ImFlipFlag(plot.Flags, ImPlot3DFlags_NoMouseText);\n        ImGui::EndMenu();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Begin/End Plot\n//-----------------------------------------------------------------------------\n\nbool BeginPlot(const char* title_id, const ImVec2& size, ImPlot3DFlags flags) {\n    IMPLOT3D_CHECK_CTX();\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, \"Mismatched BeginPlot()/EndPlot()!\");\n\n    // Get window\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Skip if needed\n    if (window->SkipItems)\n        return false;\n\n    // Get or create plot\n    const ImGuiID ID = window->GetID(title_id);\n    const bool just_created = gp.Plots.GetByKey(ID) == nullptr;\n    gp.CurrentPlot = gp.Plots.GetOrAddByKey(ID);\n    gp.CurrentItems = &gp.CurrentPlot->Items;\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n\n    // Populate plot\n    plot.ID = ID;\n    plot.JustCreated = just_created;\n    if (just_created) {\n        plot.Rotation = plot.InitialRotation;\n        plot.FitThisFrame = true;\n        for (int i = 0; i < 3; i++) {\n            plot.Axes[i] = ImPlot3DAxis();\n            plot.Axes[i].FitThisFrame = true;\n        }\n    }\n    if (plot.PreviousFlags != flags)\n        plot.Flags = flags;\n    plot.PreviousFlags = flags;\n    plot.SetupLocked = false;\n    plot.OpenContextThisFrame = false;\n    plot.RotationCond = ImPlot3DCond_None;\n\n    // Populate title\n    plot.SetTitle(title_id);\n\n    // Calculate frame size\n    ImVec2 frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y);\n    if (frame_size.x < gp.Style.PlotMinSize.x && size.x < 0.0f)\n        frame_size.x = gp.Style.PlotMinSize.x;\n    if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f)\n        frame_size.y = gp.Style.PlotMinSize.y;\n\n    // Create plot ImGui item\n    plot.FrameRect = ImRect(window->DC.CursorPos, window->DC.CursorPos + frame_size);\n    ImGui::ItemSize(plot.FrameRect);\n    if (!ImGui::ItemAdd(plot.FrameRect, plot.ID, &plot.FrameRect)) {\n        gp.CurrentPlot = nullptr;\n        gp.CurrentItems = nullptr;\n        gp.CurrentItem = nullptr;\n        return false;\n    }\n\n    // Reset legend\n    plot.Items.Legend.Reset();\n\n    // Reset axes\n    for (int i = 0; i < ImAxis3D_COUNT; i++)\n        plot.Axes[i].Reset();\n\n    // Push frame rect clipping\n    ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true);\n    plot.DrawList._Flags = window->DrawList->Flags;\n    plot.DrawList._SharedData = ImGui::GetDrawListSharedData();\n\n    return true;\n}\n\nvoid EndPlot() {\n    IMPLOT3D_CHECK_CTX();\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"Mismatched BeginPlot()/EndPlot()!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n\n    // Move triangles from 3D draw list to ImGui draw list\n    plot.DrawList.SortedMoveToImGuiDrawList();\n\n    // Handle data fitting\n    if (plot.FitThisFrame) {\n        plot.FitThisFrame = false;\n        for (int i = 0; i < 3; i++) {\n            if (plot.Axes[i].FitThisFrame) {\n                plot.Axes[i].FitThisFrame = false;\n                plot.Axes[i].ApplyFit();\n            }\n        }\n    }\n\n    // Lock setup if not already done\n    SetupLock();\n\n    // Reset legend hover\n    plot.Items.Legend.Hovered = false;\n\n    // Render legend\n    RenderLegend();\n\n    // Render mouse position\n    RenderMousePos();\n\n    // Legend context menu\n    if (ImGui::BeginPopup(\"##LegendContext\")) {\n        ImGui::Text(\"Legend\");\n        ImGui::Separator();\n        if (ShowLegendContextMenu(plot.Items.Legend, !ImPlot3D::ImHasFlag(plot.Flags, ImPlot3DFlags_NoLegend)))\n            ImFlipFlag(plot.Flags, ImPlot3DFlags_NoLegend);\n        ImGui::EndPopup();\n    }\n\n    // Axis context menus\n    for (int i = 0; i < 3; i++) {\n        ImPlot3DAxis& axis = plot.Axes[i];\n        if (ImGui::BeginPopup(axis_contexts[i])) {\n            ImGui::Text(axis.HasLabel() ? axis.GetLabel() : \"%c-Axis\", 'X' + i);\n            ImGui::Separator();\n            ShowAxisContextMenu(axis);\n            ImGui::EndPopup();\n        }\n    }\n\n    // Plane context menus\n    for (int i = 0; i < 3; i++) {\n        if (ImGui::BeginPopup(plane_contexts[i])) {\n            ImGui::Text(\"%s\", plane_labels[i]);\n            ImGui::Separator();\n            ShowPlaneContextMenu(plot, i);\n            ImGui::EndPopup();\n        }\n    }\n\n    // Plot context menu\n    if (ImGui::BeginPopup(\"##PlotContext\")) {\n        ShowPlotContextMenu(plot);\n        ImGui::EndPopup();\n    }\n\n    // Pop frame rect clipping\n    ImGui::PopClipRect();\n\n    // Reset current plot\n    gp.CurrentPlot = nullptr;\n    gp.CurrentItems = nullptr;\n    gp.CurrentItem = nullptr;\n\n    // Reset the plot items for the next frame\n    for (int i = 0; i < plot.Items.GetItemCount(); i++)\n        plot.Items.GetItemByIndex(i)->SeenThisFrame = false;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Setup\n//-----------------------------------------------------------------------------\n\nstatic const float ANIMATION_ANGULAR_VELOCITY = 2 * 3.1415f;\n\nfloat CalcAnimationTime(ImPlot3DQuat q0, ImPlot3DQuat q1) {\n    // Compute the angular distance between orientations\n    float dot_product = ImClamp(q0.Dot(q1), -1.0f, 1.0f);\n    float angle = 2.0f * acosf(fabsf(dot_product));\n\n    // Calculate animation time for constant the angular velocity\n    return angle / ANIMATION_ANGULAR_VELOCITY;\n}\n\nvoid SetupAxis(ImAxis3D idx, const char* label, ImPlot3DAxisFlags flags) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"SetupAxis() needs to be called after BeginPlot() and before any setup locking functions (e.g. PlotX)!\");\n\n    // Get plot and axis\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    ImPlot3DAxis& axis = plot.Axes[idx];\n    if (axis.PreviousFlags != flags)\n        axis.Flags = flags;\n    axis.PreviousFlags = flags;\n    axis.SetLabel(label);\n}\n\nvoid SetupAxisLimits(ImAxis3D idx, double min_lim, double max_lim, ImPlot3DCond cond) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"SetupAxisLimits() needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    ImPlot3DAxis& axis = plot.Axes[idx];\n    if (!plot.Initialized || cond == ImPlot3DCond_Always) {\n        axis.SetRange(min_lim, max_lim);\n        axis.RangeCond = cond;\n        axis.FitThisFrame = false;\n    }\n}\n\nvoid SetupAxisFormat(ImAxis3D idx, ImPlot3DFormatter formatter, void* data) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    ImPlot3DAxis& axis = plot.Axes[idx];\n    axis.Formatter = formatter;\n    axis.FormatterData = data;\n}\n\nvoid SetupAxisTicks(ImAxis3D idx, const double* values, int n_ticks, const char* const labels[], bool keep_default) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    ImPlot3DAxis& axis = plot.Axes[idx];\n    axis.ShowDefaultTicks = keep_default;\n    AddTicksCustom(values, labels, n_ticks, axis.Ticker, axis.Formatter ? axis.Formatter : Formatter_Default,\n                   (axis.Formatter && axis.FormatterData) ? axis.FormatterData : (void*)IMPLOT3D_LABEL_FORMAT);\n}\n\nvoid SetupAxisTicks(ImAxis3D idx, double v_min, double v_max, int n_ticks, const char* const labels[], bool keep_default) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    n_ticks = n_ticks < 2 ? 2 : n_ticks;\n    ImVector<double> temp;\n    FillRange(temp, n_ticks, v_min, v_max);\n    SetupAxisTicks(idx, temp.Data, n_ticks, labels, keep_default);\n}\n\nvoid SetupAxisLimitsConstraints(ImAxis3D idx, double v_min, double v_max) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    ImPlot3DAxis& axis = plot.Axes[idx];\n    axis.ConstraintRange.Min = (float)v_min;\n    axis.ConstraintRange.Max = (float)v_max;\n}\n\nvoid SetupAxisZoomConstraints(ImAxis3D idx, double z_min, double z_max) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    ImPlot3DAxis& axis = plot.Axes[idx];\n    axis.ConstraintZoom.Min = (float)z_min;\n    axis.ConstraintZoom.Max = (float)z_max;\n}\n\nvoid SetupAxes(const char* x_label, const char* y_label, const char* z_label, ImPlot3DAxisFlags x_flags, ImPlot3DAxisFlags y_flags,\n               ImPlot3DAxisFlags z_flags) {\n    SetupAxis(ImAxis3D_X, x_label, x_flags);\n    SetupAxis(ImAxis3D_Y, y_label, y_flags);\n    SetupAxis(ImAxis3D_Z, z_label, z_flags);\n}\n\nvoid SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, double z_min, double z_max, ImPlot3DCond cond) {\n    SetupAxisLimits(ImAxis3D_X, x_min, x_max, cond);\n    SetupAxisLimits(ImAxis3D_Y, y_min, y_max, cond);\n    SetupAxisLimits(ImAxis3D_Z, z_min, z_max, cond);\n    if (cond == ImPlot3DCond_Once)\n        GImPlot3D->CurrentPlot->FitThisFrame = false;\n}\n\nvoid SetupBoxRotation(float elevation, float azimuth, bool animate, ImPlot3DCond cond) {\n    // Convert angles from degrees to radians\n    float elev_rad = elevation * IM_PI / 180.0f;\n    float azim_rad = azimuth * IM_PI / 180.0f;\n\n    // Call quaternion SetupBoxRotation\n    SetupBoxRotation(ImPlot3DQuat::FromElAz(elev_rad, azim_rad), animate, cond);\n}\n\nvoid SetupBoxRotation(ImPlot3DQuat rotation, bool animate, ImPlot3DCond cond) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"SetupBoxRotation() needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n\n    if (!plot.Initialized || cond == ImPlot3DCond_Always) {\n        if (!animate) {\n            plot.Rotation = rotation;\n            plot.AnimationTime = 0.0f; // Force any running rotation animation to stop\n        } else {\n            plot.RotationAnimationEnd = rotation;\n            plot.AnimationTime = CalcAnimationTime(plot.Rotation, plot.RotationAnimationEnd);\n        }\n        plot.RotationCond = cond;\n    }\n}\n\nvoid SetupBoxInitialRotation(float elevation, float azimuth) {\n    // Convert angles from degrees to radians\n    float elev_rad = elevation * IM_PI / 180.0f;\n    float azim_rad = azimuth * IM_PI / 180.0f;\n\n    // Call quaternion SetupBoxInitialRotation\n    SetupBoxInitialRotation(ImPlot3DQuat::FromElAz(elev_rad, azim_rad));\n}\n\nvoid SetupBoxInitialRotation(ImPlot3DQuat rotation) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"SetupBoxInitialRotation() needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n\n    plot.InitialRotation = rotation;\n}\n\nvoid SetupBoxScale(float x, float y, float z) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"SetupBoxScale() needs to be called after BeginPlot() and before any setup locking functions (e.g. PlotX)!\");\n    IM_ASSERT_USER_ERROR(x > 0.0f && y > 0.0f && z > 0.0f, \"SetupBoxScale() requires all aspect ratios to be greater than 0!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    plot.BoxScale = ImPlot3DPoint(x, y, z);\n}\n\nvoid SetupLegend(ImPlot3DLocation location, ImPlot3DLegendFlags flags) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked,\n                         \"SetupLegend() needs to be called after BeginPlot() and before any setup locking functions (e.g. PlotX)!\");\n    IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, \"SetupLegend() needs to be called within an itemized context!\");\n    ImPlot3DLegend& legend = gp.CurrentItems->Legend;\n    if (legend.PreviousLocation != location)\n        legend.Location = location;\n    legend.PreviousLocation = location;\n    if (legend.PreviousFlags != flags)\n        legend.Flags = flags;\n    legend.PreviousFlags = flags;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plot Utils\n//-----------------------------------------------------------------------------\n\nImPlot3DPlot* GetCurrentPlot() { return GImPlot3D->CurrentPlot; }\n\nvoid BustPlotCache() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    gp.Plots.Clear();\n}\n\nImVec2 PlotToPixels(const ImPlot3DPoint& point) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PlotToPixels() needs to be called between BeginPlot() and EndPlot()!\");\n    return NDCToPixels(PlotToNDC(point));\n}\n\nImVec2 PlotToPixels(double x, double y, double z) { return PlotToPixels(ImPlot3DPoint((float)x, (float)y, (float)z)); }\n\nImPlot3DRay PixelsToPlotRay(const ImVec2& pix) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PixelsToPlotRay() needs to be called between BeginPlot() and EndPlot()!\");\n    return NDCRayToPlotRay(PixelsToNDCRay(pix));\n}\n\nImPlot3DRay PixelsToPlotRay(double x, double y) { return PixelsToPlotRay(ImVec2((float)x, (float)y)); }\n\nImPlot3DPoint PixelsToPlotPlane(const ImVec2& pix, ImPlane3D plane, bool mask) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PixelsToPlotPlane() needs to be called between BeginPlot() and EndPlot()!\");\n\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    ImPlot3DRay ray = PixelsToNDCRay(pix);\n    const ImPlot3DPoint& O = ray.Origin;\n    const ImPlot3DPoint& D = ray.Direction;\n\n    // Helper lambda to check intersection with a given coordinate and return intersection point if valid.\n    auto IntersectPlane = [&](float coord) -> ImPlot3DPoint {\n        // Solve for t in O[axis] + D[axis]*t = coord\n        float denom = 0.0f;\n        float numer = 0.0f;\n        if (plane == ImPlane3D_YZ) {\n            denom = D.x;\n            numer = coord - O.x;\n        } else if (plane == ImPlane3D_XZ) {\n            denom = D.y;\n            numer = coord - O.y;\n        } else if (plane == ImPlane3D_XY) {\n            denom = D.z;\n            numer = coord - O.z;\n        }\n\n        if (ImAbs(denom) < 1e-12f) {\n            // Ray is parallel or nearly parallel to the plane\n            return ImPlot3DPoint(NAN, NAN, NAN);\n        }\n\n        float t = numer / denom;\n        if (t < 0.0f) {\n            // Intersection behind the ray origin\n            return ImPlot3DPoint(NAN, NAN, NAN);\n        }\n\n        return O + D * t;\n    };\n\n    // Compute which plane to intersect with\n    bool active_faces[3];\n    ComputeActiveFaces(active_faces, plot.Rotation, plot.Axes);\n\n    // Calculate intersection point with the planes\n    ImPlot3DPoint P = IntersectPlane(active_faces[plane] ? 0.5f * plot.BoxScale[plane] : -0.5f * plot.BoxScale[plane]);\n    if (P.IsNaN())\n        return P;\n\n    // Helper lambda to check if point P is within the plot box\n    auto InRange = [&](const ImPlot3DPoint& P) {\n        return P.x >= -0.5f * plot.BoxScale.x && P.x <= 0.5f * plot.BoxScale.x && P.y >= -0.5f * plot.BoxScale.y && P.y <= 0.5f * plot.BoxScale.y &&\n               P.z >= -0.5f * plot.BoxScale.z && P.z <= 0.5f * plot.BoxScale.z;\n    };\n\n    // Handle mask (if one of the intersections is out of range, set it to NAN)\n    if (mask) {\n        switch (plane) {\n            case ImPlane3D_YZ:\n                if (!InRange(ImPlot3DPoint(0.0, P.y, P.z)))\n                    return ImPlot3DPoint(NAN, NAN, NAN);\n                break;\n            case ImPlane3D_XZ:\n                if (!InRange(ImPlot3DPoint(P.x, 0.0, P.z)))\n                    return ImPlot3DPoint(NAN, NAN, NAN);\n                break;\n            case ImPlane3D_XY:\n                if (!InRange(ImPlot3DPoint(P.x, P.y, 0.0)))\n                    return ImPlot3DPoint(NAN, NAN, NAN);\n                break;\n        }\n    }\n\n    return NDCToPlot(P);\n}\n\nImPlot3DPoint PixelsToPlotPlane(double x, double y, ImPlane3D plane, bool mask) { return PixelsToPlotPlane(ImVec2((float)x, (float)y), plane, mask); }\n\nImVec2 GetPlotPos() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"GetPlotPos() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    return gp.CurrentPlot->PlotRect.Min;\n}\n\nImVec2 GetPlotSize() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"GetPlotSize() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    return gp.CurrentPlot->PlotRect.GetSize();\n}\n\nImVec2 GetFramePos() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"GetFramePos() needs to be called between BeginPlot() and EndPlot()!\");\n    return gp.CurrentPlot->FrameRect.Min;\n}\n\nImVec2 GetFrameSize() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"GetFrameSize() needs to be called between BeginPlot() and EndPlot()!\");\n    return gp.CurrentPlot->FrameRect.GetSize();\n}\n\nImPlot3DPoint PlotToNDC(const ImPlot3DPoint& point) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PlotToNDC() needs to be called between BeginPlot() and EndPlot()!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    SetupLock();\n\n    return PlotToNDC(plot, point);\n}\n\nImPlot3DPoint NDCToPlot(const ImPlot3DPoint& point) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"NDCToPlot() needs to be called between BeginPlot() and EndPlot()!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    SetupLock();\n\n    ImPlot3DPoint plot_point;\n    for (int i = 0; i < 3; i++) {\n        ImPlot3DAxis& axis = plot.Axes[i];\n        float ndc_range = 0.5f * plot.BoxScale[i];\n        float t = ImPlot3D::ImHasFlag(axis.Flags, ImPlot3DAxisFlags_Invert) ? (ndc_range - point[i]) : (point[i] + ndc_range);\n        t /= plot.BoxScale[i];\n        plot_point[i] = axis.Range.Min + t * (axis.Range.Max - axis.Range.Min);\n    }\n    return plot_point;\n}\n\nImVec2 NDCToPixels(const ImPlot3DPoint& point) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"NDCToPixels() needs to be called between BeginPlot() and EndPlot()!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    SetupLock();\n\n    return NDCToPixels(plot, point);\n}\n\nImPlot3DRay PixelsToNDCRay(const ImVec2& pix) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PixelsToNDCRay() needs to be called between BeginPlot() and EndPlot()!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    SetupLock();\n\n    // Calculate zoom factor and plot center\n    float zoom = plot.GetBoxZoom();\n    ImVec2 center = plot.PlotRect.GetCenter();\n\n    // Undo screen transformations to get back to NDC space\n    float x = (pix.x - center.x) / zoom;\n    float y = -(pix.y - center.y) / zoom; // Invert y-axis\n\n    // Define near and far points in NDC space along the z-axis\n    ImPlot3DPoint ndc_near = plot.Rotation.Inverse() * ImPlot3DPoint(x, y, -10.0f);\n    ImPlot3DPoint ndc_far = plot.Rotation.Inverse() * ImPlot3DPoint(x, y, 10.0f);\n\n    // Create the ray in NDC space\n    ImPlot3DRay ndc_ray;\n    ndc_ray.Origin = ndc_near;\n    ndc_ray.Direction = (ndc_far - ndc_near).Normalized();\n\n    return ndc_ray;\n}\n\nImPlot3DRay NDCRayToPlotRay(const ImPlot3DRay& ray) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"NDCRayToPlotRay() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n\n    // Convert NDC origin and a point along the ray to plot coordinates\n    ImPlot3DPoint plot_origin = NDCToPlot(ray.Origin);\n    ImPlot3DPoint ndc_point_along_ray = ray.Origin + ray.Direction;\n    ImPlot3DPoint plot_point_along_ray = NDCToPlot(ndc_point_along_ray);\n\n    // Compute the direction in plot coordinates\n    ImPlot3DPoint plot_direction = (plot_point_along_ray - plot_origin).Normalized();\n\n    // Create the ray in plot coordinates\n    ImPlot3DRay plot_ray;\n    plot_ray.Origin = plot_origin;\n    plot_ray.Direction = plot_direction;\n\n    return plot_ray;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Setup Utils\n//-----------------------------------------------------------------------------\n\nstatic const float MOUSE_CURSOR_DRAG_THRESHOLD = 5.0f;\n\nvoid HandleInput(ImPlot3DPlot& plot) {\n    ImGuiIO& IO = ImGui::GetIO();\n\n    // clang-format off\n    const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowOverlap\n                                             | ImGuiButtonFlags_PressedOnClick\n                                             | ImGuiButtonFlags_PressedOnDoubleClick\n                                             | ImGuiButtonFlags_MouseButtonLeft\n                                             | ImGuiButtonFlags_MouseButtonRight\n                                             | ImGuiButtonFlags_MouseButtonMiddle;\n    // clang-format on\n    const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect, plot.ID, &plot.Hovered, &plot.Held, plot_button_flags);\n#if (IMGUI_VERSION_NUM < 18966)\n    ImGui::SetItemAllowOverlap(); // Handled by ButtonBehavior()\n#endif\n\n    // State\n    const ImVec2 rot_drag = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);\n    const bool rotating = ImLengthSqr(rot_drag) > MOUSE_CURSOR_DRAG_THRESHOLD;\n\n    // Check if any axis/plane is hovered\n    const ImPlot3DQuat& rotation = plot.Rotation;\n    ImPlot3DPoint range_min = plot.RangeMin();\n    ImPlot3DPoint range_max = plot.RangeMax();\n    bool active_faces[3];\n    int plane_2d = -1;\n    ComputeActiveFaces(active_faces, rotation, plot.Axes, &plane_2d);\n    ImPlot3DPoint corners[8];\n    ComputeBoxCorners(corners, range_min, range_max);\n    ImVec2 corners_pix[8];\n    ComputeBoxCornersPix(plot, corners_pix, corners);\n    int hovered_plane_idx = -1;\n    int hovered_plane = GetMouseOverPlane(plot, active_faces, corners_pix, &hovered_plane_idx);\n    int hovered_edge_idx = -1;\n    int hovered_axis = GetMouseOverAxis(plot, active_faces, corners_pix, plane_2d, &hovered_edge_idx);\n    if (hovered_axis != -1) {\n        hovered_plane_idx = -1;\n        hovered_plane = -1;\n    }\n\n    // If the user is no longer pressing the translation/zoom buttons, set axes as not held\n    if (!ImGui::IsMouseDown(ImGuiMouseButton_Left) && !ImGui::IsMouseDown(ImGuiMouseButton_Middle)) {\n        for (int i = 0; i < 3; i++)\n            plot.Axes[i].Held = false;\n    }\n\n    // Reset held edge/plane indices (it will be set if mouse button is down)\n    if (!plot.Held) {\n        plot.HeldEdgeIdx = -1;\n        plot.HeldPlaneIdx = -1;\n    }\n\n    // Check which axes should be transformed (fit/zoom/translate)\n    bool any_axis_held = plot.Axes[0].Held || plot.Axes[1].Held || plot.Axes[2].Held;\n    if (!any_axis_held) {\n        // Only update the transformation axes if the user is not already performing a transformation\n        plot.Axes[0].Hovered = plot.Axes[1].Hovered = plot.Axes[2].Hovered = false;\n        if (hovered_axis != -1) {\n            plot.Axes[hovered_axis].Hovered = true;\n        } else if (hovered_plane != -1) {\n            plot.Axes[(hovered_plane + 1) % 3].Hovered = true;\n            plot.Axes[(hovered_plane + 2) % 3].Hovered = true;\n        } else {\n            plot.Axes[0].Hovered = plot.Axes[1].Hovered = plot.Axes[2].Hovered = true;\n        }\n    }\n\n    // Compute plane that is being hovered by mouse\n    ImPlane3D mouse_plane = ImPlane3D_XY;\n    if (plane_2d != -1)\n        mouse_plane = plane_2d;\n    else if (plot.Axes[1].Hovered && plot.Axes[2].Hovered)\n        mouse_plane = ImPlane3D_YZ;\n    else if (plot.Axes[0].Hovered && plot.Axes[2].Hovered)\n        mouse_plane = ImPlane3D_XZ;\n    else if (plot.Axes[2].Hovered)\n        mouse_plane = ImPlane3D_YZ;\n    ImVec2 mouse_pos = ImGui::GetMousePos();\n    ImPlot3DPoint mouse_pos_plot = PixelsToPlotPlane(mouse_pos, mouse_plane, false);\n\n    // Handle translation/zoom fit with double click\n    if (plot_clicked && (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) || ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Middle))) {\n        plot.FitThisFrame = true;\n        for (int i = 0; i < 3; i++)\n            plot.Axes[i].FitThisFrame = plot.Axes[i].Hovered;\n    }\n\n    // Handle auto fit\n    for (int i = 0; i < 3; i++)\n        if (plot.Axes[i].IsAutoFitting()) {\n            plot.FitThisFrame = true;\n            plot.Axes[i].FitThisFrame = true;\n        }\n\n    // Handle translation with right mouse button\n    if (plot.Held && ImGui::IsMouseDown(ImGuiMouseButton_Left)) {\n        ImVec2 delta(IO.MouseDelta.x, IO.MouseDelta.y);\n\n        if (plot.Axes[0].Hovered && plot.Axes[1].Hovered && plot.Axes[2].Hovered) {\n            // Perform unconstrained translation (translate on the viewer plane)\n\n            // Compute delta_pixels in 3D (invert y-axis)\n            ImPlot3DPoint delta_pixels(delta.x, -delta.y, 0.0f);\n\n            // Convert delta to NDC space\n            float zoom = plot.GetBoxZoom();\n            ImPlot3DPoint delta_NDC = plot.Rotation.Inverse() * (delta_pixels / zoom);\n\n            // Convert delta to plot space\n            ImPlot3DPoint delta_plot = delta_NDC * (plot.RangeMax() - plot.RangeMin()) / plot.BoxScale;\n\n            // Adjust delta for inverted axes\n            for (int i = 0; i < 3; i++) {\n                if (ImHasFlag(plot.Axes[i].Flags, ImPlot3DAxisFlags_Invert))\n                    delta_plot[i] *= -1;\n            }\n\n            // Adjust plot range to translate the plot\n            for (int i = 0; i < 3; i++) {\n                if (plot.Axes[i].Hovered) {\n                    bool increasing = delta_plot[i] < 0.0f;\n                    if (delta_plot[i] != 0.0f && !plot.Axes[i].IsPanLocked(increasing)) {\n                        plot.Axes[i].SetMin(plot.Axes[i].Range.Min - delta_plot[i]);\n                        plot.Axes[i].SetMax(plot.Axes[i].Range.Max - delta_plot[i]);\n                    }\n                    plot.Axes[i].Held = true;\n                }\n                // If no axis was held before (user started translating in this frame), set the held edge/plane indices\n                if (!any_axis_held) {\n                    plot.HeldEdgeIdx = hovered_edge_idx;\n                    plot.HeldPlaneIdx = hovered_plane_idx;\n                }\n            }\n        } else if (plot.Axes[0].Hovered || plot.Axes[1].Hovered || plot.Axes[2].Hovered) {\n            // Translate along plane/axis\n\n            // Mouse delta in pixels\n            ImVec2 mouse_delta(IO.MouseDelta.x, IO.MouseDelta.y);\n\n            ImPlot3DPoint mouse_delta_plot = PixelsToPlotPlane(mouse_pos + mouse_delta, mouse_plane, false);\n            ImPlot3DPoint delta_plot = mouse_delta_plot - mouse_pos_plot;\n\n            // Apply translation to the selected axes\n            for (int i = 0; i < 3; i++) {\n                if (plot.Axes[i].Hovered) {\n                    bool increasing = delta_plot[i] < 0.0f;\n                    if (delta_plot[i] != 0.0f && !plot.Axes[i].IsPanLocked(increasing)) {\n                        plot.Axes[i].SetMin(plot.Axes[i].Range.Min - delta_plot[i]);\n                        plot.Axes[i].SetMax(plot.Axes[i].Range.Max - delta_plot[i]);\n                    }\n                    plot.Axes[i].Held = true;\n                }\n                if (!any_axis_held) {\n                    plot.HeldEdgeIdx = hovered_edge_idx;\n                    plot.HeldPlaneIdx = hovered_plane_idx;\n                }\n            }\n        }\n    }\n\n    // Handle context click with right mouse button\n    if (plot.Held && ImGui::IsMouseClicked(ImGuiMouseButton_Right) && !ImPlot3D::ImHasFlag(plot.Flags, ImPlot3DFlags_NoMenus))\n        plot.ContextClick = true;\n    if (rotating || ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Right))\n        plot.ContextClick = false;\n\n    // Handle reset rotation with left mouse double click\n    if (plot.Held && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Right) && !plot.IsRotationLocked()) {\n        plot.RotationAnimationEnd = plot.Rotation;\n\n        // Calculate rotation to align the z-axis with the camera direction\n        if (hovered_plane == -1) {\n            plot.RotationAnimationEnd = plot.InitialRotation;\n        } else {\n            // Compute plane normal\n            ImPlot3DPoint axis_normal = ImPlot3DPoint(0.0f, 0.0f, 0.0f);\n            axis_normal[hovered_plane] = active_faces[hovered_plane] ? -1.0f : 1.0f;\n            if (ImPlot3D::ImHasFlag(plot.Axes[hovered_plane].Flags, ImPlot3DAxisFlags_Invert))\n                axis_normal[hovered_plane] *= -1;\n\n            // Compute rotation to align the plane normal with the z-axis\n            ImPlot3DQuat align_normal = ImPlot3DQuat::FromTwoVectors(plot.RotationAnimationEnd * axis_normal, ImPlot3DPoint(0.0f, 0.0f, 1.0f));\n            plot.RotationAnimationEnd = align_normal * plot.RotationAnimationEnd;\n\n            if (hovered_plane != 2) {\n                // Compute rotation to point z-axis up\n                ImPlot3DQuat align_up =\n                    ImPlot3DQuat::FromTwoVectors(plot.RotationAnimationEnd * ImPlot3DPoint(0.0f, 0.0f, 1.0f), ImPlot3DPoint(0.0f, 1.0f, 0.0f));\n                plot.RotationAnimationEnd = align_up * plot.RotationAnimationEnd;\n            } else {\n                // Find the axis most aligned with the up direction\n                ImPlot3DPoint up(0.0f, 1.0f, 0.0f);\n                ImPlot3DPoint x_axis = plot.RotationAnimationEnd * ImPlot3DPoint(1.0f, 0.0f, 0.0f);\n                ImPlot3DPoint y_axis = plot.RotationAnimationEnd * ImPlot3DPoint(0.0f, 1.0f, 0.0f);\n                ImPlot3DPoint neg_x_axis = plot.RotationAnimationEnd * ImPlot3DPoint(-1.0f, 0.0f, 0.0f);\n                ImPlot3DPoint neg_y_axis = plot.RotationAnimationEnd * ImPlot3DPoint(0.0f, -1.0f, 0.0f);\n\n                struct AxisAlignment {\n                    ImPlot3DPoint axis;\n                    float dot;\n                };\n\n                AxisAlignment candidates[] = {\n                    {x_axis, x_axis.Dot(up)},\n                    {y_axis, y_axis.Dot(up)},\n                    {neg_x_axis, neg_x_axis.Dot(up)},\n                    {neg_y_axis, neg_y_axis.Dot(up)},\n                };\n\n                // Find the candidate with the maximum dot product\n                AxisAlignment* best_candidate = &candidates[0];\n                for (int i = 1; i < 4; i++) {\n                    if (candidates[i].dot > best_candidate->dot) {\n                        best_candidate = &candidates[i];\n                    }\n                }\n\n                // Compute the rotation to align the best candidate with the up direction\n                ImPlot3DQuat align_up = ImPlot3DQuat::FromTwoVectors(best_candidate->axis, up);\n                plot.RotationAnimationEnd = align_up * plot.RotationAnimationEnd;\n            }\n        }\n\n        // Calculate animation time\n        plot.AnimationTime = CalcAnimationTime(plot.Rotation, plot.RotationAnimationEnd);\n    }\n\n    // Handle rotation with left mouse dragging\n    if (plot.Held && ImGui::IsMouseDown(ImGuiMouseButton_Right) && !plot.IsRotationLocked()) {\n        ImVec2 delta(IO.MouseDelta.x, IO.MouseDelta.y);\n\n        // Map delta to rotation angles (in radians)\n        float angle_x = delta.x * (3.1415f / 180.0f);\n        float angle_y = delta.y * (3.1415f / 180.0f);\n\n        // Create quaternions for the rotations\n        ImPlot3DQuat quat_x(angle_y, ImPlot3DPoint(1.0f, 0.0f, 0.0f));\n        ImPlot3DQuat quat_z(angle_x, ImPlot3DPoint(0.0f, 0.0f, 1.0f));\n\n        // Combine the new rotations with the current rotation\n        plot.Rotation = quat_x * plot.Rotation * quat_z;\n        plot.Rotation.Normalize();\n    }\n\n    // Handle zoom with mouse wheel\n    if (plot.Hovered) {\n        ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);\n        if (ImGui::IsMouseDown(ImGuiMouseButton_Middle) || IO.MouseWheel != 0.0f) {\n            float delta = ImGui::IsMouseDown(ImGuiMouseButton_Middle) ? (-0.01f * IO.MouseDelta.y) : (-0.1f * IO.MouseWheel);\n            float zoom = 1.0f + delta;\n            for (int i = 0; i < 3; i++) {\n                ImPlot3DAxis& axis = plot.Axes[i];\n                float size = axis.Range.Max - axis.Range.Min;\n                float new_min, new_max;\n                if (hovered_axis != -1 || hovered_plane != -1) {\n                    // If mouse over the plot box, zoom around the mouse plot position\n                    float new_size = size * zoom;\n\n                    // Calculate offset ratio of the mouse position relative to the axis range\n                    float offset = mouse_pos_plot[i] - axis.Range.Min;\n                    float ratio = offset / size;\n\n                    // Adjust the axis range to zoom around the mouse position\n                    new_min = mouse_pos_plot[i] - new_size * ratio;\n                    new_max = mouse_pos_plot[i] + new_size * (1.0f - ratio);\n                } else {\n                    // If mouse is not over the plot box, zoom around the plot center\n                    float center = (axis.Range.Min + axis.Range.Max) * 0.5f;\n\n                    // Adjust the axis range to zoom around plot center\n                    new_min = center - zoom * size * 0.5f;\n                    new_max = center + zoom * size * 0.5f;\n                }\n\n                // Set new range after zoom\n                if (plot.Axes[i].Hovered) {\n                    if (!plot.Axes[i].IsInputLocked()) {\n                        plot.Axes[i].SetMin(new_min);\n                        plot.Axes[i].SetMax(new_max);\n                    }\n                    plot.Axes[i].Held = true;\n                }\n\n                // If no axis was held before (user started zoom in this frame), set the held edge/plane indices\n                if (!any_axis_held) {\n                    plot.HeldEdgeIdx = hovered_edge_idx;\n                    plot.HeldPlaneIdx = hovered_plane_idx;\n                }\n            }\n        }\n    }\n\n    // Handle context menu (should not happen if it is not a double click action)\n    bool not_double_click = (float)(ImGui::GetTime() - IO.MouseClickedTime[ImGuiMouseButton_Right]) > IO.MouseDoubleClickTime;\n    if (plot.Hovered && plot.ContextClick && not_double_click && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) {\n        plot.ContextClick = false;\n        plot.OpenContextThisFrame = true;\n    }\n\n    if (plot.OpenContextThisFrame) {\n        if (plot.Items.Legend.Hovered)\n            ImGui::OpenPopup(\"##LegendContext\");\n        else if (hovered_axis != -1) {\n            ImGui::OpenPopup(axis_contexts[hovered_axis]);\n        } else if (hovered_plane != -1) {\n            ImGui::OpenPopup(plane_contexts[hovered_plane]);\n        } else if (plot.Hovered) {\n            ImGui::OpenPopup(\"##PlotContext\");\n        }\n    }\n}\n\nvoid SetupLock() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"SetupLock() needs to be called between BeginPlot() and EndPlot()!\");\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n    if (plot.SetupLocked)\n        return;\n    // Lock setup\n    plot.SetupLocked = true;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImDrawList* draw_list = window->DrawList;\n\n    ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true);\n\n    // Set default formatter/locator\n    for (int i = 0; i < 3; i++) {\n        ImPlot3DAxis& axis = plot.Axes[i];\n\n        // Set formatter\n        if (axis.Formatter == nullptr) {\n            axis.Formatter = Formatter_Default;\n            if (axis.FormatterData == nullptr)\n                axis.FormatterData = (void*)IMPLOT3D_LABEL_FORMAT;\n        }\n\n        // Set locator\n        if (axis.Locator == nullptr)\n            axis.Locator = Locator_Default;\n    }\n\n    // Draw frame background\n    ImU32 f_bg_color = GetStyleColorU32(ImPlot3DCol_FrameBg);\n    draw_list->AddRectFilled(plot.FrameRect.Min, plot.FrameRect.Max, f_bg_color);\n\n    // Compute canvas/canvas rectangle\n    plot.CanvasRect = ImRect(plot.FrameRect.Min + gp.Style.PlotPadding, plot.FrameRect.Max - gp.Style.PlotPadding);\n    plot.PlotRect = plot.CanvasRect;\n\n    // Compute ticks\n    for (int i = 0; i < 3; i++) {\n        ImPlot3DAxis& axis = plot.Axes[i];\n        if (axis.ShowDefaultTicks) {\n            float pixels = plot.GetBoxZoom() * plot.BoxScale[i];\n            axis.Locator(axis.Ticker, axis.Range, pixels, axis.Formatter, axis.FormatterData);\n        }\n    }\n\n    // Render title\n    if (plot.HasTitle()) {\n        ImU32 col = GetStyleColorU32(ImPlot3DCol_TitleText);\n        ImVec2 top_center = ImVec2(plot.FrameRect.GetCenter().x, plot.CanvasRect.Min.y);\n        AddTextCentered(draw_list, top_center, col, plot.GetTitle());\n        plot.PlotRect.Min.y += ImGui::GetTextLineHeight() + gp.Style.LabelPadding.y;\n    }\n\n    // Handle animation\n    if (plot.AnimationTime > 0.0f) {\n        float dt = ImGui::GetIO().DeltaTime;\n        float t = ImClamp(dt / plot.AnimationTime, 0.0f, 1.0f);\n        plot.AnimationTime -= dt;\n        if (plot.AnimationTime < 0.0f)\n            plot.AnimationTime = 0.0f;\n        plot.Rotation = ImPlot3DQuat::Slerp(plot.Rotation, plot.RotationAnimationEnd, t);\n    }\n\n    plot.Initialized = true;\n\n    // Handle user input\n    HandleInput(plot);\n\n    // Render plot box\n    RenderPlotBox(draw_list, plot);\n\n    ImGui::PopClipRect();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Miscellaneous\n//-----------------------------------------------------------------------------\n\nImDrawList* GetPlotDrawList() { return ImGui::GetWindowDrawList(); }\n\n//-----------------------------------------------------------------------------\n// [SECTION] Styles\n//-----------------------------------------------------------------------------\n\nstruct ImPlot3DStyleVarInfo {\n    ImGuiDataType Type;\n    ImU32 Count;\n    ImU32 Offset;\n    void* GetVarPtr(ImPlot3DStyle* style) const { return (void*)((unsigned char*)style + Offset); }\n};\n\nstatic const ImPlot3DStyleVarInfo GPlot3DStyleVarInfo[] = {\n    // Item style\n    {ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlot3DStyle, LineWeight)},   // ImPlot3DStyleVar_LineWeight\n    {ImGuiDataType_S32, 1, (ImU32)offsetof(ImPlot3DStyle, Marker)},         // ImPlot3DStyleVar_Marker\n    {ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlot3DStyle, MarkerSize)},   // ImPlot3DStyleVar_MarkerSize\n    {ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlot3DStyle, MarkerWeight)}, // ImPlot3DStyleVar_MarkerWeight\n    {ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlot3DStyle, FillAlpha)},    // ImPlot3DStyleVar_FillAlpha\n\n    // Plot style\n    {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, PlotDefaultSize)}, // ImPlot3DStyleVar_Plot3DDefaultSize\n    {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, PlotMinSize)},     // ImPlot3DStyleVar_Plot3DMinSize\n    {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, PlotPadding)},     // ImPlot3DStyleVar_Plot3DPadding\n\n    // Label style\n    {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, LabelPadding)},       // ImPlot3DStyleVar_LabelPaddine\n    {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, LegendPadding)},      // ImPlot3DStyleVar_LegendPadding\n    {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, LegendInnerPadding)}, // ImPlot3DStyleVar_LegendInnerPadding\n    {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, LegendSpacing)},      // ImPlot3DStyleVar_LegendSpacing\n};\n\nstatic const ImPlot3DStyleVarInfo* GetPlotStyleVarInfo(ImPlot3DStyleVar idx) {\n    IM_ASSERT(idx >= 0 && idx < ImPlot3DStyleVar_COUNT);\n    IM_ASSERT(IM_ARRAYSIZE(GPlot3DStyleVarInfo) == ImPlot3DStyleVar_COUNT);\n    return &GPlot3DStyleVarInfo[idx];\n}\n\nImPlot3DStyle& GetStyle() { return GImPlot3D->Style; }\nvoid SetStyle(const ImPlot3DStyle& style) { GImPlot3D->Style = style; }\n\nvoid StyleColorsAuto(ImPlot3DStyle* dst) {\n    ImPlot3DStyle* style = dst ? dst : &ImPlot3D::GetStyle();\n    ImVec4* colors = style->Colors;\n\n    colors[ImPlot3DCol_Line] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_Fill] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_MarkerOutline] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_MarkerFill] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_TitleText] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_InlayText] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_FrameBg] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_PlotBg] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_PlotBorder] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_LegendBg] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_LegendBorder] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_LegendText] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_AxisText] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_AxisGrid] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_AxisTick] = IMPLOT3D_AUTO_COL;\n}\n\nvoid StyleColorsDark(ImPlot3DStyle* dst) {\n    ImPlot3DStyle* style = dst ? dst : &ImPlot3D::GetStyle();\n    ImVec4* colors = style->Colors;\n\n    colors[ImPlot3DCol_Line] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_Fill] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_MarkerOutline] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_MarkerFill] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_TitleText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlot3DCol_InlayText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlot3DCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);\n    colors[ImPlot3DCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);\n    colors[ImPlot3DCol_PlotBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);\n    colors[ImPlot3DCol_LegendBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);\n    colors[ImPlot3DCol_LegendBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);\n    colors[ImPlot3DCol_LegendText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlot3DCol_AxisText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlot3DCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f);\n    colors[ImPlot3DCol_AxisTick] = IMPLOT3D_AUTO_COL;\n}\n\nvoid StyleColorsLight(ImPlot3DStyle* dst) {\n    ImPlot3DStyle* style = dst ? dst : &ImPlot3D::GetStyle();\n    ImVec4* colors = style->Colors;\n\n    colors[ImPlot3DCol_Line] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_Fill] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_MarkerOutline] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_MarkerFill] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlot3DCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlot3DCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlot3DCol_PlotBg] = ImVec4(0.42f, 0.57f, 1.00f, 0.13f);\n    colors[ImPlot3DCol_PlotBorder] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImPlot3DCol_LegendBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.98f);\n    colors[ImPlot3DCol_LegendBorder] = ImVec4(0.82f, 0.82f, 0.82f, 0.80f);\n    colors[ImPlot3DCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlot3DCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlot3DCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlot3DCol_AxisTick] = IMPLOT3D_AUTO_COL;\n}\n\nvoid StyleColorsClassic(ImPlot3DStyle* dst) {\n    ImPlot3DStyle* style = dst ? dst : &ImPlot3D::GetStyle();\n    ImVec4* colors = style->Colors;\n\n    colors[ImPlot3DCol_Line] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_Fill] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_MarkerOutline] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_MarkerFill] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_TitleText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImPlot3DCol_InlayText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImPlot3DCol_FrameBg] = ImVec4(0.43f, 0.43f, 0.43f, 0.39f);\n    colors[ImPlot3DCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.35f);\n    colors[ImPlot3DCol_PlotBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f);\n    colors[ImPlot3DCol_LegendBg] = ImVec4(0.11f, 0.11f, 0.14f, 0.92f);\n    colors[ImPlot3DCol_LegendBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f);\n    colors[ImPlot3DCol_LegendText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImPlot3DCol_AxisText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImPlot3DCol_AxisGrid] = ImVec4(0.90f, 0.90f, 0.90f, 0.25f);\n    colors[ImPlot3DCol_AxisTick] = IMPLOT3D_AUTO_COL;\n}\n\nvoid PushStyleColor(ImPlot3DCol idx, ImU32 col) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    ImGuiColorMod backup;\n    backup.Col = (ImGuiCol)idx;\n    backup.BackupValue = gp.Style.Colors[idx];\n    gp.ColorModifiers.push_back(backup);\n    gp.Style.Colors[idx] = ImGui::ColorConvertU32ToFloat4(col);\n}\n\nvoid PushStyleColor(ImPlot3DCol idx, const ImVec4& col) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    ImGuiColorMod backup;\n    backup.Col = (ImGuiCol)idx;\n    backup.BackupValue = gp.Style.Colors[idx];\n    gp.ColorModifiers.push_back(backup);\n    gp.Style.Colors[idx] = col;\n}\n\nvoid PopStyleColor(int count) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(count <= gp.ColorModifiers.Size, \"You can't pop more modifiers than have been pushed!\");\n    while (count > 0) {\n        ImGuiColorMod& backup = gp.ColorModifiers.back();\n        gp.Style.Colors[backup.Col] = backup.BackupValue;\n        gp.ColorModifiers.pop_back();\n        count--;\n    }\n}\n\nvoid PushStyleVar(ImPlot3DStyleVar idx, float val) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    const ImPlot3DStyleVarInfo* var_info = GetPlotStyleVarInfo(idx);\n    if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) {\n        float* pvar = (float*)var_info->GetVarPtr(&gp.Style);\n        gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));\n        *pvar = val;\n        return;\n    }\n    IM_ASSERT(0 && \"Called PushStyleVar() float variant but variable is not a float!\");\n}\n\nvoid PushStyleVar(ImPlot3DStyleVar idx, int val) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    const ImPlot3DStyleVarInfo* var_info = GetPlotStyleVarInfo(idx);\n    if (var_info->Type == ImGuiDataType_S32 && var_info->Count == 1) {\n        int* pvar = (int*)var_info->GetVarPtr(&gp.Style);\n        gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));\n        *pvar = val;\n        return;\n    } else if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) {\n        float* pvar = (float*)var_info->GetVarPtr(&gp.Style);\n        gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));\n        *pvar = (float)val;\n        return;\n    }\n    IM_ASSERT(0 && \"Called PushStyleVar() int variant but variable is not a int!\");\n}\n\nvoid PushStyleVar(ImPlot3DStyleVar idx, const ImVec2& val) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    const ImPlot3DStyleVarInfo* var_info = GetPlotStyleVarInfo(idx);\n    if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) {\n        ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&gp.Style);\n        gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar));\n        *pvar = val;\n        return;\n    }\n    IM_ASSERT(0 && \"Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!\");\n}\n\nvoid PopStyleVar(int count) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(count <= gp.StyleModifiers.Size, \"You can't pop more modifiers than have been pushed!\");\n    while (count > 0) {\n        ImGuiStyleMod& backup = gp.StyleModifiers.back();\n        const ImPlot3DStyleVarInfo* info = GetPlotStyleVarInfo(backup.VarIdx);\n        void* data = info->GetVarPtr(&gp.Style);\n        if (info->Type == ImGuiDataType_Float && info->Count == 1) {\n            ((float*)data)[0] = backup.BackupFloat[0];\n        } else if (info->Type == ImGuiDataType_Float && info->Count == 2) {\n            ((float*)data)[0] = backup.BackupFloat[0];\n            ((float*)data)[1] = backup.BackupFloat[1];\n        } else if (info->Type == ImGuiDataType_S32 && info->Count == 1) {\n            ((int*)data)[0] = backup.BackupInt[0];\n        }\n        gp.StyleModifiers.pop_back();\n        count--;\n    }\n}\n\nImVec4 GetStyleColorVec4(ImPlot3DCol idx) { return IsColorAuto(idx) ? GetAutoColor(idx) : GImPlot3D->Style.Colors[idx]; }\n\nImU32 GetStyleColorU32(ImPlot3DCol idx) { return ImGui::ColorConvertFloat4ToU32(ImPlot3D::GetStyleColorVec4(idx)); }\n\n//------------------------------------------------------------------------------\n// [SECTION] Colormaps\n//------------------------------------------------------------------------------\n\nImPlot3DColormap AddColormap(const char* name, const ImVec4* colormap, int size, bool qual) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(size > 1, \"The colormap size must be greater than 1!\");\n    IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, \"The colormap name has already been used!\");\n    ImVector<ImU32> buffer;\n    buffer.resize(size);\n    for (int i = 0; i < size; i++)\n        buffer[i] = ImGui::ColorConvertFloat4ToU32(colormap[i]);\n    return gp.ColormapData.Append(name, buffer.Data, size, qual);\n}\n\nImPlot3DColormap AddColormap(const char* name, const ImU32* colormap, int size, bool qual) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(size > 1, \"The colormap size must be greater than 1!\");\n    IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, \"The colormap name has already be used!\");\n    return gp.ColormapData.Append(name, colormap, size, qual);\n}\n\nint GetColormapCount() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    return gp.ColormapData.Count;\n}\n\nconst char* GetColormapName(ImPlot3DColormap colormap) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    return gp.ColormapData.GetName(colormap);\n}\n\nImPlot3DColormap GetColormapIndex(const char* name) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    return gp.ColormapData.GetIndex(name);\n}\n\nvoid PushColormap(ImPlot3DColormap colormap) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(colormap >= 0 && colormap < gp.ColormapData.Count, \"The colormap index is invalid!\");\n    gp.ColormapModifiers.push_back(gp.Style.Colormap);\n    gp.Style.Colormap = colormap;\n}\n\nvoid PushColormap(const char* name) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    ImPlot3DColormap idx = gp.ColormapData.GetIndex(name);\n    IM_ASSERT_USER_ERROR(idx != -1, \"The colormap name is invalid!\");\n    PushColormap(idx);\n}\n\nvoid PopColormap(int count) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(count <= gp.ColormapModifiers.Size, \"You can't pop more modifiers than have been pushed!\");\n    while (count > 0) {\n        const ImPlot3DColormap& backup = gp.ColormapModifiers.back();\n        gp.Style.Colormap = backup;\n        gp.ColormapModifiers.pop_back();\n        count--;\n    }\n}\n\nImU32 NextColormapColorU32() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, \"NextColormapColor() needs to be called between BeginPlot() and EndPlot()!\");\n    int idx = gp.CurrentItems->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap);\n    ImU32 col = gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx);\n    gp.CurrentItems->ColormapIdx++;\n    return col;\n}\n\nImVec4 NextColormapColor() { return ImGui::ColorConvertU32ToFloat4(NextColormapColorU32()); }\n\nint GetColormapSize(ImPlot3DColormap cmap) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    cmap = cmap == IMPLOT3D_AUTO ? gp.Style.Colormap : cmap;\n    IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, \"Invalid colormap index!\");\n    return gp.ColormapData.GetKeyCount(cmap);\n}\n\nImU32 GetColormapColorU32(int idx, ImPlot3DColormap cmap) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    cmap = cmap == IMPLOT3D_AUTO ? gp.Style.Colormap : cmap;\n    IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, \"Invalid colormap index!\");\n    idx = idx % gp.ColormapData.GetKeyCount(cmap);\n    return gp.ColormapData.GetKeyColor(cmap, idx);\n}\n\nImVec4 GetColormapColor(int idx, ImPlot3DColormap cmap) { return ImGui::ColorConvertU32ToFloat4(GetColormapColorU32(idx, cmap)); }\n\nImU32 SampleColormapU32(float t, ImPlot3DColormap cmap) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    cmap = cmap == IMPLOT3D_AUTO ? gp.Style.Colormap : cmap;\n    IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, \"Invalid colormap index!\");\n    return gp.ColormapData.LerpTable(cmap, t);\n}\n\nImVec4 SampleColormap(float t, ImPlot3DColormap cmap) { return ImGui::ColorConvertU32ToFloat4(SampleColormapU32(t, cmap)); }\n\nvoid RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous) {\n    const int n = continuous ? size - 1 : size;\n    ImU32 col1, col2;\n    if (vert) {\n        const float step = bounds.GetHeight() / n;\n        ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Max.x, bounds.Min.y + step);\n        for (int i = 0; i < n; ++i) {\n            if (reversed) {\n                col1 = colors[size - i - 1];\n                col2 = continuous ? colors[size - i - 2] : col1;\n            } else {\n                col1 = colors[i];\n                col2 = continuous ? colors[i + 1] : col1;\n            }\n            DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2);\n            rect.TranslateY(step);\n        }\n    } else {\n        const float step = bounds.GetWidth() / n;\n        ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Min.x + step, bounds.Max.y);\n        for (int i = 0; i < n; ++i) {\n            if (reversed) {\n                col1 = colors[size - i - 1];\n                col2 = continuous ? colors[size - i - 2] : col1;\n            } else {\n                col1 = colors[i];\n                col2 = continuous ? colors[i + 1] : col1;\n            }\n            DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col2, col2, col1);\n            rect.TranslateX(step);\n        }\n    }\n}\n\nbool ColormapSlider(const char* label, float* t, ImVec4* out, const char* format, ImPlot3DColormap cmap) {\n    *t = ImClamp(*t, 0.0f, 1.0f);\n    ImGuiContext& G = *GImGui;\n    ImGuiWindow* Window = G.CurrentWindow;\n    if (Window->SkipItems)\n        return false;\n    ImPlot3DContext& gp = *GImPlot3D;\n    cmap = cmap == IMPLOT3D_AUTO ? gp.Style.Colormap : cmap;\n    IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, \"Invalid colormap index!\");\n    const ImU32* keys = gp.ColormapData.GetKeys(cmap);\n    const int count = gp.ColormapData.GetKeyCount(cmap);\n    const bool qual = gp.ColormapData.IsQual(cmap);\n    const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos;\n    const float w = ImGui::CalcItemWidth();\n    const float h = ImGui::GetFrameHeight();\n    const ImRect rect = ImRect(pos.x, pos.y, pos.x + w, pos.y + h);\n    RenderColorBar(keys, count, *ImGui::GetWindowDrawList(), rect, false, false, !qual);\n    const ImU32 grab = CalcTextColor(gp.ColormapData.LerpTable(cmap, *t));\n    // const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap,0.5f));\n    ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32_BLACK_TRANS);\n    ImGui::PushStyleColor(ImGuiCol_FrameBgActive, IM_COL32_BLACK_TRANS);\n    ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(1, 1, 1, 0.1f));\n    ImGui::PushStyleColor(ImGuiCol_SliderGrab, grab);\n    ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, grab);\n    ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 2);\n    ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0);\n    const bool changed = ImGui::SliderFloat(label, t, 0, 1, format);\n    ImGui::PopStyleColor(5);\n    ImGui::PopStyleVar(2);\n    if (out != nullptr)\n        *out = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.LerpTable(cmap, *t));\n    return changed;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Context Utils\n//-----------------------------------------------------------------------------\n\n#define IMPLOT3D_APPEND_CMAP(name, qual) ctx->ColormapData.Append(#name, name, sizeof(name) / sizeof(ImU32), qual)\n#define IM_RGB(r, g, b) IM_COL32(r, g, b, 255)\n\nvoid InitializeContext(ImPlot3DContext* ctx) {\n    ResetContext(ctx);\n\n    const ImU32 Deep[] = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396};\n    const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409};\n    const ImU32 Pastel[] = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986};\n    const ImU32 Paired[] = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971,\n                            4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481};\n    const ImU32 Viridis[] = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273,\n                             4286883874, 4285579076, 4283552122, 4280737725, 4280674301};\n    const ImU32 Plasma[] = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780,\n                            4284638433, 4283139314, 4281771772, 4280667900, 4280416752};\n    const ImU32 Hot[] = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103,\n                         4278239231, 4278255615, 4283826175, 4289396735, 4294967295};\n    const ImU32 Cool[] = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656,\n                          4294928025, 4294921651, 4294915020, 4294908646, 4294902015};\n    const ImU32 Pink[] = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160,\n                          4288794330, 4289651940, 4291685869, 4293392118, 4294967295};\n    const ImU32 Jet[] = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565,\n                         4283826090, 4278255615, 4278233855, 4278212095, 4278190335};\n    const ImU32 Twilight[] = {IM_RGB(226, 217, 226), IM_RGB(166, 191, 202), IM_RGB(109, 144, 192), IM_RGB(95, 88, 176),\n                              IM_RGB(83, 30, 124),   IM_RGB(47, 20, 54),    IM_RGB(100, 25, 75),   IM_RGB(159, 60, 80),\n                              IM_RGB(192, 117, 94),  IM_RGB(208, 179, 158), IM_RGB(226, 217, 226)};\n    const ImU32 RdBu[] = {IM_RGB(103, 0, 31),    IM_RGB(178, 24, 43),   IM_RGB(214, 96, 77),   IM_RGB(244, 165, 130),\n                          IM_RGB(253, 219, 199), IM_RGB(247, 247, 247), IM_RGB(209, 229, 240), IM_RGB(146, 197, 222),\n                          IM_RGB(67, 147, 195),  IM_RGB(33, 102, 172),  IM_RGB(5, 48, 97)};\n    const ImU32 BrBG[] = {IM_RGB(84, 48, 5),     IM_RGB(140, 81, 10),   IM_RGB(191, 129, 45),  IM_RGB(223, 194, 125),\n                          IM_RGB(246, 232, 195), IM_RGB(245, 245, 245), IM_RGB(199, 234, 229), IM_RGB(128, 205, 193),\n                          IM_RGB(53, 151, 143),  IM_RGB(1, 102, 94),    IM_RGB(0, 60, 48)};\n    const ImU32 PiYG[] = {IM_RGB(142, 1, 82),    IM_RGB(197, 27, 125),  IM_RGB(222, 119, 174), IM_RGB(241, 182, 218),\n                          IM_RGB(253, 224, 239), IM_RGB(247, 247, 247), IM_RGB(230, 245, 208), IM_RGB(184, 225, 134),\n                          IM_RGB(127, 188, 65),  IM_RGB(77, 146, 33),   IM_RGB(39, 100, 25)};\n    const ImU32 Spectral[] = {IM_RGB(158, 1, 66),    IM_RGB(213, 62, 79),   IM_RGB(244, 109, 67),  IM_RGB(253, 174, 97),\n                              IM_RGB(254, 224, 139), IM_RGB(255, 255, 191), IM_RGB(230, 245, 152), IM_RGB(171, 221, 164),\n                              IM_RGB(102, 194, 165), IM_RGB(50, 136, 189),  IM_RGB(94, 79, 162)};\n    const ImU32 Greys[] = {IM_COL32_WHITE, IM_COL32_BLACK};\n\n    IMPLOT3D_APPEND_CMAP(Deep, true);\n    IMPLOT3D_APPEND_CMAP(Dark, true);\n    IMPLOT3D_APPEND_CMAP(Pastel, true);\n    IMPLOT3D_APPEND_CMAP(Paired, true);\n    IMPLOT3D_APPEND_CMAP(Viridis, false);\n    IMPLOT3D_APPEND_CMAP(Plasma, false);\n    IMPLOT3D_APPEND_CMAP(Hot, false);\n    IMPLOT3D_APPEND_CMAP(Cool, false);\n    IMPLOT3D_APPEND_CMAP(Pink, false);\n    IMPLOT3D_APPEND_CMAP(Jet, false);\n    IMPLOT3D_APPEND_CMAP(Twilight, false);\n    IMPLOT3D_APPEND_CMAP(RdBu, false);\n    IMPLOT3D_APPEND_CMAP(BrBG, false);\n    IMPLOT3D_APPEND_CMAP(PiYG, false);\n    IMPLOT3D_APPEND_CMAP(Spectral, false);\n    IMPLOT3D_APPEND_CMAP(Greys, false);\n}\n\nvoid ResetContext(ImPlot3DContext* ctx) {\n    ctx->Plots.Clear();\n    ctx->CurrentPlot = nullptr;\n    ctx->CurrentItems = nullptr;\n    ctx->CurrentItem = nullptr;\n    ctx->NextItemData.Reset();\n    ctx->Style = ImPlot3DStyle();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Style Utils\n//-----------------------------------------------------------------------------\n\nbool IsColorAuto(const ImVec4& col) { return col.w == -1.0f; }\n\nbool IsColorAuto(ImPlot3DCol idx) { return IsColorAuto(GImPlot3D->Style.Colors[idx]); }\n\nImVec4 GetAutoColor(ImPlot3DCol idx) {\n    switch (idx) {\n        case ImPlot3DCol_Line: return IMPLOT3D_AUTO_COL;          // Plot dependent\n        case ImPlot3DCol_Fill: return IMPLOT3D_AUTO_COL;          // Plot dependent\n        case ImPlot3DCol_MarkerOutline: return IMPLOT3D_AUTO_COL; // Plot dependent\n        case ImPlot3DCol_MarkerFill: return IMPLOT3D_AUTO_COL;    // Plot dependent\n        case ImPlot3DCol_TitleText: return ImGui::GetStyleColorVec4(ImGuiCol_Text);\n        case ImPlot3DCol_InlayText: return ImGui::GetStyleColorVec4(ImGuiCol_Text);\n        case ImPlot3DCol_FrameBg: return ImGui::GetStyleColorVec4(ImGuiCol_FrameBg);\n        case ImPlot3DCol_PlotBg: return ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);\n        case ImPlot3DCol_PlotBorder: return ImGui::GetStyleColorVec4(ImGuiCol_Border);\n        case ImPlot3DCol_LegendBg: return ImGui::GetStyleColorVec4(ImGuiCol_PopupBg);\n        case ImPlot3DCol_LegendBorder: return ImGui::GetStyleColorVec4(ImGuiCol_Border);\n        case ImPlot3DCol_LegendText: return ImGui::GetStyleColorVec4(ImGuiCol_Text);\n        case ImPlot3DCol_AxisText: return ImGui::GetStyleColorVec4(ImGuiCol_Text);\n        case ImPlot3DCol_AxisGrid: return ImGui::GetStyleColorVec4(ImGuiCol_Text) * ImVec4(1, 1, 1, 0.25f);\n        case ImPlot3DCol_AxisTick: return GetStyleColorVec4(ImPlot3DCol_AxisGrid);\n        default: return IMPLOT3D_AUTO_COL;\n    }\n}\n\nconst char* GetStyleColorName(ImPlot3DCol idx) {\n    static const char* color_names[ImPlot3DCol_COUNT] = {\n        \"Line\",       \"Fill\",     \"MarkerOutline\", \"MarkerFill\", \"TitleText\", \"InlayText\", \"FrameBg\",  \"PlotBg\",\n        \"PlotBorder\", \"LegendBg\", \"LegendBorder\",  \"LegendText\", \"AxisText\",  \"AxisGrid\",  \"AxisTick\",\n    };\n    return color_names[idx];\n}\n\nconst ImPlot3DNextItemData& GetItemData() { return GImPlot3D->NextItemData; }\n\n} // namespace ImPlot3D\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DPoint\n//-----------------------------------------------------------------------------\n\nImPlot3DPoint ImPlot3DPoint::operator*(float rhs) const { return ImPlot3DPoint(x * rhs, y * rhs, z * rhs); }\nImPlot3DPoint ImPlot3DPoint::operator/(float rhs) const { return ImPlot3DPoint(x / rhs, y / rhs, z / rhs); }\nImPlot3DPoint ImPlot3DPoint::operator+(const ImPlot3DPoint& rhs) const { return ImPlot3DPoint(x + rhs.x, y + rhs.y, z + rhs.z); }\nImPlot3DPoint ImPlot3DPoint::operator-(const ImPlot3DPoint& rhs) const { return ImPlot3DPoint(x - rhs.x, y - rhs.y, z - rhs.z); }\nImPlot3DPoint ImPlot3DPoint::operator*(const ImPlot3DPoint& rhs) const { return ImPlot3DPoint(x * rhs.x, y * rhs.y, z * rhs.z); }\nImPlot3DPoint ImPlot3DPoint::operator/(const ImPlot3DPoint& rhs) const { return ImPlot3DPoint(x / rhs.x, y / rhs.y, z / rhs.z); }\nImPlot3DPoint ImPlot3DPoint::operator-() const { return ImPlot3DPoint(-x, -y, -z); }\n\nImPlot3DPoint& ImPlot3DPoint::operator*=(float rhs) {\n    x *= rhs;\n    y *= rhs;\n    z *= rhs;\n    return *this;\n}\nImPlot3DPoint& ImPlot3DPoint::operator/=(float rhs) {\n    x /= rhs;\n    y /= rhs;\n    z /= rhs;\n    return *this;\n}\nImPlot3DPoint& ImPlot3DPoint::operator+=(const ImPlot3DPoint& rhs) {\n    x += rhs.x;\n    y += rhs.y;\n    z += rhs.z;\n    return *this;\n}\nImPlot3DPoint& ImPlot3DPoint::operator-=(const ImPlot3DPoint& rhs) {\n    x -= rhs.x;\n    y -= rhs.y;\n    z -= rhs.z;\n    return *this;\n}\nImPlot3DPoint& ImPlot3DPoint::operator*=(const ImPlot3DPoint& rhs) {\n    x *= rhs.x;\n    y *= rhs.y;\n    z *= rhs.z;\n    return *this;\n}\nImPlot3DPoint& ImPlot3DPoint::operator/=(const ImPlot3DPoint& rhs) {\n    x /= rhs.x;\n    y /= rhs.y;\n    z /= rhs.z;\n    return *this;\n}\n\nbool ImPlot3DPoint::operator==(const ImPlot3DPoint& rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z; }\nbool ImPlot3DPoint::operator!=(const ImPlot3DPoint& rhs) const { return !(*this == rhs); }\n\nfloat ImPlot3DPoint::Dot(const ImPlot3DPoint& rhs) const { return x * rhs.x + y * rhs.y + z * rhs.z; }\n\nImPlot3DPoint ImPlot3DPoint::Cross(const ImPlot3DPoint& rhs) const {\n    return ImPlot3DPoint(y * rhs.z - z * rhs.y, z * rhs.x - x * rhs.z, x * rhs.y - y * rhs.x);\n}\n\nfloat ImPlot3DPoint::Length() const { return ImSqrt(x * x + y * y + z * z); }\n\nfloat ImPlot3DPoint::LengthSquared() const { return x * x + y * y + z * z; }\n\nvoid ImPlot3DPoint::Normalize() {\n    float l = Length();\n    x /= l;\n    y /= l;\n    z /= l;\n}\n\nImPlot3DPoint ImPlot3DPoint::Normalized() const {\n    float l = Length();\n    return ImPlot3DPoint(x / l, y / l, z / l);\n}\n\nImPlot3DPoint operator*(float lhs, const ImPlot3DPoint& rhs) { return ImPlot3DPoint(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z); }\n\nbool ImPlot3DPoint::IsNaN() const { return ImPlot3D::ImNan(x) || ImPlot3D::ImNan(y) || ImPlot3D::ImNan(z); }\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DBox\n//-----------------------------------------------------------------------------\n\nvoid ImPlot3DBox::Expand(const ImPlot3DPoint& point) {\n    Min.x = ImMin(Min.x, point.x);\n    Min.y = ImMin(Min.y, point.y);\n    Min.z = ImMin(Min.z, point.z);\n    Max.x = ImMax(Max.x, point.x);\n    Max.y = ImMax(Max.y, point.y);\n    Max.z = ImMax(Max.z, point.z);\n}\n\nbool ImPlot3DBox::Contains(const ImPlot3DPoint& point) const {\n    return (point.x >= Min.x && point.x <= Max.x) && (point.y >= Min.y && point.y <= Max.y) && (point.z >= Min.z && point.z <= Max.z);\n}\n\nbool ImPlot3DBox::ClipLineSegment(const ImPlot3DPoint& p0, const ImPlot3DPoint& p1, ImPlot3DPoint& p0_clipped, ImPlot3DPoint& p1_clipped) const {\n    // Check if the line segment is completely inside the box\n    if (Contains(p0) && Contains(p1)) {\n        p0_clipped = p0;\n        p1_clipped = p1;\n        return true;\n    }\n\n    // Perform Liang-Barsky 3D clipping\n    double t0 = 0.0;\n    double t1 = 1.0;\n    ImPlot3DPoint d = p1 - p0;\n\n    // Define the clipping boundaries\n    const double xmin = Min.x, xmax = Max.x;\n    const double ymin = Min.y, ymax = Max.y;\n    const double zmin = Min.z, zmax = Max.z;\n\n    // Lambda function to update t0 and t1\n    auto update = [&](double p, double q) -> bool {\n        if (p == 0.0) {\n            if (q < 0.0)\n                return false; // Line is parallel and outside the boundary\n            else\n                return true; // Line is parallel and inside or coincident with boundary\n        }\n        double r = q / p;\n        if (p < 0.0) {\n            if (r > t1)\n                return false; // Line is outside\n            if (r > t0)\n                t0 = r; // Move up t0\n        } else {\n            if (r < t0)\n                return false; // Line is outside\n            if (r < t1)\n                t1 = r; // Move down t1\n        }\n        return true;\n    };\n\n    // Clip against each boundary\n    if (!update(-d.x, p0.x - xmin))\n        return false; // Left\n    if (!update(d.x, xmax - p0.x))\n        return false; // Right\n    if (!update(-d.y, p0.y - ymin))\n        return false; // Bottom\n    if (!update(d.y, ymax - p0.y))\n        return false; // Top\n    if (!update(-d.z, p0.z - zmin))\n        return false; // Near\n    if (!update(d.z, zmax - p0.z))\n        return false; // Far\n\n    // Compute clipped points\n    p0_clipped = p0 + d * (float)t0;\n    p1_clipped = p0 + d * (float)t1;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DRange\n//-----------------------------------------------------------------------------\n\nvoid ImPlot3DRange::Expand(float value) {\n    Min = ImMin(Min, value);\n    Max = ImMax(Max, value);\n}\n\nbool ImPlot3DRange::Contains(float value) const { return value >= Min && value <= Max; }\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DQuat\n//-----------------------------------------------------------------------------\n\nImPlot3DQuat::ImPlot3DQuat(float _angle, const ImPlot3DPoint& _axis) {\n    float half_angle = _angle * 0.5f;\n    float s = ImSin(half_angle);\n    x = s * _axis.x;\n    y = s * _axis.y;\n    z = s * _axis.z;\n    w = ImCos(half_angle);\n}\n\nImPlot3DQuat ImPlot3DQuat::FromTwoVectors(const ImPlot3DPoint& v0, const ImPlot3DPoint& v1) {\n    ImPlot3DQuat q;\n\n    // Compute the dot product and lengths of the vectors\n    float dot = v0.Dot(v1);\n    float length_v0 = v0.Length();\n    float length_v1 = v1.Length();\n\n    // Normalize the dot product\n    float normalized_dot = dot / (length_v0 * length_v1);\n\n    // Handle edge cases: if vectors are very close or identical\n    const float epsilon = 1e-6f;\n    if (ImFabs(normalized_dot - 1.0f) < epsilon) {\n        // v0 and v1 are nearly identical; return an identity quaternion\n        q.x = 0.0f;\n        q.y = 0.0f;\n        q.z = 0.0f;\n        q.w = 1.0f;\n        return q;\n    }\n\n    // Handle edge case: if vectors are opposite\n    if (ImFabs(normalized_dot + 1.0f) < epsilon) {\n        // v0 and v1 are opposite; choose an arbitrary orthogonal axis\n        ImPlot3DPoint arbitrary_axis = ImFabs(v0.x) > ImFabs(v0.z) ? ImPlot3DPoint(-v0.y, v0.x, 0.0f) : ImPlot3DPoint(0.0f, -v0.z, v0.y);\n        arbitrary_axis.Normalize();\n        q.x = arbitrary_axis.x;\n        q.y = arbitrary_axis.y;\n        q.z = arbitrary_axis.z;\n        q.w = 0.0f;\n        return q;\n    }\n\n    // General case\n    ImPlot3DPoint axis = v0.Cross(v1);\n    axis.Normalize();\n    float angle = ImAcos(normalized_dot);\n    float half_angle = angle * 0.5f;\n    float s = ImSin(half_angle);\n    q.x = s * axis.x;\n    q.y = s * axis.y;\n    q.z = s * axis.z;\n    q.w = ImCos(half_angle);\n\n    return q;\n}\n\nImPlot3DQuat ImPlot3DQuat::FromElAz(float elevation, float azimuth) {\n    // Create quaternions for azimuth and elevation\n    ImPlot3DQuat azimuth_quat(azimuth, ImPlot3DPoint(0.0f, 0.0f, 1.0f));     // Rotate around Z-axis\n    ImPlot3DQuat elevation_quat(elevation, ImPlot3DPoint(1.0f, 0.0f, 0.0f)); // Rotate around X-axis\n    ImPlot3DQuat zero_quat(-IM_PI / 2, ImPlot3DPoint(1.0f, 0.0f, 0.0f));     // Rotate to zero azimuth/elevation orientation\n\n    // Combine rotations\n    return elevation_quat * zero_quat * azimuth_quat;\n}\n\nfloat ImPlot3DQuat::Length() const { return ImSqrt(x * x + y * y + z * z + w * w); }\n\nImPlot3DQuat ImPlot3DQuat::Normalized() const {\n    float l = Length();\n    return ImPlot3DQuat(x / l, y / l, z / l, w / l);\n}\n\nImPlot3DQuat ImPlot3DQuat::Conjugate() const { return ImPlot3DQuat(-x, -y, -z, w); }\n\nImPlot3DQuat ImPlot3DQuat::Inverse() const {\n    float l_squared = x * x + y * y + z * z + w * w;\n    return ImPlot3DQuat(-x / l_squared, -y / l_squared, -z / l_squared, w / l_squared);\n}\n\nImPlot3DQuat ImPlot3DQuat::operator*(const ImPlot3DQuat& rhs) const {\n    return ImPlot3DQuat(w * rhs.x + x * rhs.w + y * rhs.z - z * rhs.y, w * rhs.y - x * rhs.z + y * rhs.w + z * rhs.x,\n                        w * rhs.z + x * rhs.y - y * rhs.x + z * rhs.w, w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z);\n}\n\nImPlot3DQuat& ImPlot3DQuat::Normalize() {\n    float l = Length();\n    x /= l;\n    y /= l;\n    z /= l;\n    w /= l;\n    return *this;\n}\n\nImPlot3DPoint ImPlot3DQuat::operator*(const ImPlot3DPoint& point) const {\n    // Extract vector part of the quaternion\n    ImPlot3DPoint qv(x, y, z);\n\n    // Compute the cross products needed for rotation\n    ImPlot3DPoint uv = qv.Cross(point); // uv = qv x point\n    ImPlot3DPoint uuv = qv.Cross(uv);   // uuv = qv x uv\n\n    // Compute the rotated vector\n    return point + (uv * w * 2.0f) + (uuv * 2.0f);\n}\n\nbool ImPlot3DQuat::operator==(const ImPlot3DQuat& rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z && w == rhs.w; }\n\nbool ImPlot3DQuat::operator!=(const ImPlot3DQuat& rhs) const { return !(*this == rhs); }\n\nImPlot3DQuat ImPlot3DQuat::Slerp(const ImPlot3DQuat& q1, const ImPlot3DQuat& q2, float t) {\n    // Clamp t to [0, 1]\n    t = ImClamp(t, 0.0f, 1.0f);\n\n    // Compute the dot product (cosine of the angle between quaternions)\n    float dot = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;\n\n    // If the dot product is negative, negate one quaternion to take the shorter path\n    ImPlot3DQuat q2_ = q2;\n    if (dot < 0.0f) {\n        q2_ = ImPlot3DQuat(-q2.x, -q2.y, -q2.z, -q2.w);\n        dot = -dot;\n    }\n\n    // If the quaternions are very close, use linear interpolation to avoid numerical instability\n    if (dot > 0.9995f) {\n        return ImPlot3DQuat(q1.x + t * (q2_.x - q1.x), q1.y + t * (q2_.y - q1.y), q1.z + t * (q2_.z - q1.z), q1.w + t * (q2_.w - q1.w)).Normalized();\n    }\n\n    // Compute the angle and the interpolation factors\n    float theta_0 = ImAcos(dot);        // Angle between input quaternions\n    float theta = theta_0 * t;          // Interpolated angle\n    float sin_theta = ImSin(theta);     // Sine of interpolated angle\n    float sin_theta_0 = ImSin(theta_0); // Sine of original angle\n\n    float s1 = ImCos(theta) - dot * sin_theta / sin_theta_0;\n    float s2 = sin_theta / sin_theta_0;\n\n    // Interpolate and return the result\n    return ImPlot3DQuat(s1 * q1.x + s2 * q2_.x, s1 * q1.y + s2 * q2_.y, s1 * q1.z + s2 * q2_.z, s1 * q1.w + s2 * q2_.w);\n}\n\nfloat ImPlot3DQuat::Dot(const ImPlot3DQuat& rhs) const { return x * rhs.x + y * rhs.y + z * rhs.z + w * rhs.w; }\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImDrawList3D\n//-----------------------------------------------------------------------------\n\nvoid ImDrawList3D::PrimReserve(int idx_count, int vtx_count) {\n    IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0 && idx_count % 3 == 0);\n\n    int vtx_buffer_old_size = VtxBuffer.Size;\n    VtxBuffer.resize(vtx_buffer_old_size + vtx_count);\n    _VtxWritePtr = VtxBuffer.Data + vtx_buffer_old_size;\n\n    int idx_buffer_old_size = IdxBuffer.Size;\n    IdxBuffer.resize(idx_buffer_old_size + idx_count);\n    _IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size;\n\n    int z_buffer_old_size = ZBuffer.Size;\n    ZBuffer.resize(z_buffer_old_size + idx_count / 3);\n    _ZWritePtr = ZBuffer.Data + z_buffer_old_size;\n}\n\nvoid ImDrawList3D::PrimUnreserve(int idx_count, int vtx_count) {\n    IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0 && idx_count % 3 == 0);\n\n    VtxBuffer.shrink(VtxBuffer.Size - vtx_count);\n    IdxBuffer.shrink(IdxBuffer.Size - idx_count);\n    ZBuffer.shrink(ZBuffer.Size - idx_count / 3);\n}\n\nvoid ImDrawList3D::SetTexture(ImTextureRef tex_ref) {\n    if (_TextureBuffer.empty()) {\n        // First texture assignment\n        _TextureBuffer.push_back({tex_ref, _VtxCurrentIdx});\n        return;\n    }\n\n    ImTextureBufferItem& prev_item = _TextureBuffer.back();\n    if (prev_item.VtxIdx == _VtxCurrentIdx) {\n        // Same vertex index: update existing texture ID\n        prev_item.TexRef = tex_ref;\n\n        // If the previous texture was the same, remove current texture\n        if (_TextureBuffer.Size >= 2) {\n            if (_TextureBuffer[_TextureBuffer.Size - 2].TexRef == tex_ref) {\n                _TextureBuffer.pop_back();\n            }\n        }\n    } else if (prev_item.TexRef != tex_ref) {\n        // New vertex index and different texture: insert new item\n        _TextureBuffer.push_back({tex_ref, _VtxCurrentIdx});\n    }\n}\n\nvoid ImDrawList3D::ResetTexture() { SetTexture(ImTextureID(0)); }\n\n#ifdef IMGUI_HAS_TEXTURES\n#define SET_TEX_REF(cmd, tex_ref) (cmd).TexRef = (tex_ref)\n#define GET_TEX_REF(cmd) (cmd).TexRef\n#else\n#define SET_TEX_REF(cmd, tex_ref) (cmd).TextureId = (tex_ref)\n#define GET_TEX_REF(cmd) (cmd).TextureId\n#endif\n\nvoid ImDrawList3D::SortedMoveToImGuiDrawList() {\n    ImDrawList& draw_list = *ImGui::GetWindowDrawList();\n\n    const int tri_count = ZBuffer.Size;\n    if (tri_count == 0) {\n        // No triangles, just reset buffers and return\n        ResetBuffers();\n        return;\n    }\n\n    // Build an array of (z, tri_idx)\n    struct TriRef {\n        float z;\n        int tri_idx;\n    };\n    TriRef* tris = (TriRef*)IM_ALLOC(sizeof(TriRef) * tri_count);\n    for (int i = 0; i < tri_count; i++) {\n        tris[i].z = ZBuffer[i];\n        tris[i].tri_idx = i;\n    }\n\n    // Sort by z (distance from viewer)\n    ImQsort(tris, (size_t)tri_count, sizeof(TriRef), [](const void* a, const void* b) {\n        float za = ((const TriRef*)a)->z;\n        float zb = ((const TriRef*)b)->z;\n        return (za < zb) ? -1 : (za > zb) ? 1 : 0;\n    });\n\n    // Reserve space in the ImGui draw list\n    draw_list.PrimReserve(IdxBuffer.Size, VtxBuffer.Size);\n\n    // Copy vertices (no reordering needed)\n    memcpy(draw_list._VtxWritePtr, VtxBuffer.Data, VtxBuffer.Size * sizeof(ImDrawVert));\n    unsigned int idx_offset = draw_list._VtxCurrentIdx;\n    draw_list._VtxWritePtr += VtxBuffer.Size;\n    draw_list._VtxCurrentIdx += (unsigned int)VtxBuffer.Size;\n\n    // Maximum index allowed to not overflow ImDrawIdx\n    unsigned int max_index_allowed = MaxIdx() - idx_offset;\n\n    // Copy indices with triangle sorting based on distance from viewer\n    ImDrawIdx* idx_out_begin = draw_list._IdxWritePtr;\n    ImDrawIdx* idx_out = idx_out_begin;\n    ImDrawIdx* idx_in = IdxBuffer.Data;\n    for (int i = 0; i < tri_count; i++) {\n        int tri_i = tris[i].tri_idx;\n        int base_idx = tri_i * 3;\n        unsigned int i0 = (unsigned int)idx_in[base_idx + 0];\n        unsigned int i1 = (unsigned int)idx_in[base_idx + 1];\n        unsigned int i2 = (unsigned int)idx_in[base_idx + 2];\n\n        // Check if after adding offset any of these indices exceed max_index_allowed\n        if (i0 > max_index_allowed || i1 > max_index_allowed || i2 > max_index_allowed)\n            continue;\n\n        idx_out[0] = (ImDrawIdx)(i0 + idx_offset);\n        idx_out[1] = (ImDrawIdx)(i1 + idx_offset);\n        idx_out[2] = (ImDrawIdx)(i2 + idx_offset);\n\n        idx_out += 3;\n    }\n    ImDrawIdx* idx_out_end = idx_out;\n    draw_list._IdxWritePtr = idx_out_end;\n\n    // If multiple textures were used (e.g. PlotImage was called), generate multiple ImDrawCmd\n    if (_TextureBuffer.Size > 1) {\n        ImTextureRef default_tex = GET_TEX_REF(draw_list._CmdHeader);\n        ImTextureRef curr_tex = default_tex;\n\n        // Remove elements reserved from PrimReserve\n        draw_list.CmdBuffer.back().ElemCount -= IdxBuffer.Size;\n        ImDrawIdx* last_cmd_buffer_idx = idx_out_begin;\n\n        // For each triangle added to the draw_list\n        for (idx_out = idx_out_begin; idx_out < idx_out_end; idx_out += 3) {\n            // Get index of first vertex in the triangle\n            unsigned int idx_in = (unsigned int)(*idx_out - idx_offset);\n\n            // Get the texture for this triangle\n            const ImTextureRef invalid_tex = ImTextureID(0);\n            ImTextureRef tri_tex = invalid_tex;\n            for (int j = 0; j < _TextureBuffer.Size; j++)\n                if (idx_in >= _TextureBuffer[j].VtxIdx)\n                    tri_tex = _TextureBuffer[j].TexRef;\n\n            // If tri_tex is invalid, the default texture should be used\n            if (tri_tex == invalid_tex)\n                tri_tex = default_tex;\n\n            if (tri_tex != curr_tex) {\n                // Update element count of previous draw cmd\n                draw_list.CmdBuffer.back().ElemCount += (unsigned int)(idx_out - last_cmd_buffer_idx);\n                last_cmd_buffer_idx = idx_out;\n\n                // Set custom texture\n                curr_tex = tri_tex;\n                SET_TEX_REF(draw_list._CmdHeader, curr_tex);\n\n                // Add new draw cmd for the new texture\n                ImDrawCmd draw_cmd;\n                draw_cmd.ClipRect = draw_list._CmdHeader.ClipRect;\n                SET_TEX_REF(draw_cmd, GET_TEX_REF(draw_list._CmdHeader));\n                draw_cmd.VtxOffset = draw_list._CmdHeader.VtxOffset;\n                draw_cmd.IdxOffset = (unsigned int)(idx_out - draw_list.IdxBuffer.Data);\n                draw_list.CmdBuffer.push_back(draw_cmd);\n            }\n        }\n        // Flush last elements to cmd buffer\n        draw_list.CmdBuffer.back().ElemCount += (unsigned int)(&draw_list.IdxBuffer.back() - last_cmd_buffer_idx + 1);\n\n        // Check if the last texture was not the default texture\n        if (curr_tex != default_tex) {\n            // Restore default texture\n            SET_TEX_REF(draw_list._CmdHeader, default_tex);\n\n            // Flush last draw cmd with custom texture\n            draw_list.AddDrawCmd();\n        }\n    }\n\n    // Reset buffers since we've moved them\n    ResetBuffers();\n\n    IM_FREE(tris);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DAxis\n//-----------------------------------------------------------------------------\n\nbool ImPlot3DAxis::HasLabel() const { return !Label.empty() && !ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_NoLabel); }\nbool ImPlot3DAxis::HasGridLines() const { return !ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_NoGridLines); }\nbool ImPlot3DAxis::HasTickLabels() const { return !ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_NoTickLabels); }\nbool ImPlot3DAxis::HasTickMarks() const { return !ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_NoTickMarks); }\nbool ImPlot3DAxis::IsAutoFitting() const { return ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_AutoFit); }\n\nvoid ImPlot3DAxis::ExtendFit(float value) {\n    FitExtents.Min = ImMin(FitExtents.Min, value);\n    FitExtents.Max = ImMax(FitExtents.Max, value);\n}\n\nvoid ImPlot3DAxis::ApplyFit() {\n    if (!IsLockedMin() && !ImPlot3D::ImNanOrInf(FitExtents.Min))\n        Range.Min = FitExtents.Min;\n    if (!IsLockedMax() && !ImPlot3D::ImNanOrInf(FitExtents.Max))\n        Range.Max = FitExtents.Max;\n    if (ImPlot3D::ImAlmostEqual(Range.Min, Range.Max)) {\n        Range.Max += 0.5;\n        Range.Min -= 0.5;\n    }\n    Constrain();\n    FitExtents.Min = HUGE_VAL;\n    FitExtents.Max = -HUGE_VAL;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DPlot\n//-----------------------------------------------------------------------------\n\nvoid ImPlot3DPlot::ExtendFit(const ImPlot3DPoint& point) {\n    for (int i = 0; i < 3; i++) {\n        if (!ImPlot3D::ImNanOrInf(point[i]) && Axes[i].FitThisFrame)\n            Axes[i].ExtendFit(point[i]);\n    }\n}\n\nImPlot3DPoint ImPlot3DPlot::RangeMin() const { return ImPlot3DPoint(Axes[0].Range.Min, Axes[1].Range.Min, Axes[2].Range.Min); }\n\nImPlot3DPoint ImPlot3DPlot::RangeMax() const { return ImPlot3DPoint(Axes[0].Range.Max, Axes[1].Range.Max, Axes[2].Range.Max); }\n\nImPlot3DPoint ImPlot3DPlot::RangeCenter() const {\n    return ImPlot3DPoint((Axes[0].Range.Min + Axes[0].Range.Max) * 0.5f, (Axes[1].Range.Min + Axes[1].Range.Max) * 0.5f,\n                         (Axes[2].Range.Min + Axes[2].Range.Max) * 0.5f);\n}\n\nvoid ImPlot3DPlot::SetRange(const ImPlot3DPoint& min, const ImPlot3DPoint& max) {\n    Axes[0].SetRange(min.x, max.x);\n    Axes[1].SetRange(min.y, max.y);\n    Axes[2].SetRange(min.z, max.z);\n}\n\nfloat ImPlot3DPlot::GetBoxZoom() const { return ImMin(PlotRect.GetWidth(), PlotRect.GetHeight()) / 1.8f; }\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImPlot3DStyle\n//-----------------------------------------------------------------------------\n\nImPlot3DStyle::ImPlot3DStyle() {\n    // Item style\n    LineWeight = 1.0f;\n    Marker = ImPlot3DMarker_None;\n    MarkerSize = 4.0f;\n    MarkerWeight = 1.0f;\n    FillAlpha = 1.0f;\n    // Plot style\n    PlotDefaultSize = ImVec2(400, 400);\n    PlotMinSize = ImVec2(200, 200);\n    PlotPadding = ImVec2(10, 10);\n    LabelPadding = ImVec2(5, 5);\n    // Legend style\n    LegendPadding = ImVec2(10, 10);\n    LegendInnerPadding = ImVec2(5, 5);\n    LegendSpacing = ImVec2(5, 0);\n    // Colors\n    ImPlot3D::StyleColorsAuto(this);\n    Colormap = ImPlot3DColormap_Deep;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Metrics\n//-----------------------------------------------------------------------------\n\nvoid ShowTicksMetrics(const ImPlot3DTicker& ticker) { ImGui::BulletText(\"Size: %d\", ticker.TickCount()); }\n\nvoid ShowAxisMetrics(const ImPlot3DAxis& axis) {\n    ImGui::BulletText(\"Label: %s\", axis.GetLabel());\n    ImGui::BulletText(\"Flags: 0x%08X\", axis.Flags);\n    ImGui::BulletText(\"Range: [%f,%f]\", axis.Range.Min, axis.Range.Max);\n\n    ImGui::BulletText(\"ShowDefaultTicks: %s\", axis.ShowDefaultTicks ? \"true\" : \"false\");\n    ImGui::BulletText(\"FitThisFrame: %s\", axis.FitThisFrame ? \"true\" : \"false\");\n    ImGui::BulletText(\"FitExtents: [%f,%f]\", axis.FitExtents.Min, axis.FitExtents.Min);\n    ImGui::BulletText(\"ConstraintRange: [%f,%f]\", axis.ConstraintRange.Min, axis.ConstraintRange.Min);\n    ImGui::BulletText(\"ConstraintZoom: [%f,%f]\", axis.ConstraintZoom.Min, axis.ConstraintZoom.Min);\n    ImGui::BulletText(\"Hovered: %s\", axis.Hovered ? \"true\" : \"false\");\n    ImGui::BulletText(\"Held: %s\", axis.Held ? \"true\" : \"false\");\n\n    if (ImGui::TreeNode(\"Ticks\")) {\n        ShowTicksMetrics(axis.Ticker);\n        ImGui::TreePop();\n    }\n}\n\nvoid ImPlot3D::ShowMetricsWindow(bool* p_popen) {\n    static bool show_frame_rects = false;\n    static bool show_canvas_rects = false;\n    static bool show_plot_rects = false;\n    static bool show_plot_box = false;\n    static bool show_axis_line = false;\n    static bool show_axis_corner_indexes = false;\n    static bool show_axis_face_indexes = false;\n    static bool show_axis_edge_indexes = false;\n    static bool show_legend_rects = false;\n\n    ImDrawList& fg = *ImGui::GetForegroundDrawList();\n\n    ImPlot3DContext& gp = *GImPlot3D;\n    ImGuiIO& io = ImGui::GetIO();\n    ImGui::Begin(\"Metrics (ImPlot3D)\", p_popen);\n    ImGui::Text(\"ImPlot3D \" IMPLOT3D_VERSION);\n    ImGui::Text(\"Application average %.3f ms/frame (%.1f FPS)\", 1000.0f / io.Framerate, io.Framerate);\n    ImGui::Text(\"Mouse Position: [%.0f,%.0f]\", io.MousePos.x, io.MousePos.y);\n    ImGui::Separator();\n    if (ImGui::TreeNode(\"Tools\")) {\n        if (ImGui::Button(\"Bust Plot Cache\"))\n            BustPlotCache();\n        ImGui::SameLine();\n        if (ImGui::Button(\"Bust Item Cache\"))\n            BustItemCache();\n        ImGui::Checkbox(\"Show Frame Rects\", &show_frame_rects);\n        ImGui::Checkbox(\"Show Canvas Rects\", &show_canvas_rects);\n        ImGui::Checkbox(\"Show Plot Rects\", &show_plot_rects);\n        ImGui::Checkbox(\"Show Plot Box\", &show_plot_box);\n        ImGui::Checkbox(\"Show Axis Line\", &show_axis_line);\n        ImGui::Checkbox(\"Show Axis Corner Indexes\", &show_axis_corner_indexes);\n        ImGui::Checkbox(\"Show Axis Face Indexes\", &show_axis_face_indexes);\n        ImGui::Checkbox(\"Show Axis Edge Indexes\", &show_axis_edge_indexes);\n        ImGui::Checkbox(\"Show Legend Rects\", &show_legend_rects);\n        ImGui::TreePop();\n    }\n    const int n_plots = gp.Plots.GetBufSize();\n    bool active_faces[3];\n    ImVec2 corners_pix[8];\n    ImPlot3DPoint corners[8];\n    int plane_2d;\n    int axis_corners[3][2];\n    char buff[16];\n    // Enum used to indicate how a certain type will be displayed\n    enum class DisplayedType {\n        Hidden,   // Hidden type that is not displayed. Used when the object should not be displayed. Will be used when:\n                  // 1. 2D plot and this type should not be used,\n                  // 2. The flag show_plot_box is disabled\n        Active,   // Part of the faces of the box that are active\n        NotActive // Part of the faces of the box that are not active when show_plot_box is active\n    };\n\n    // Render rectangles\n    for (int p = 0; p < n_plots; ++p) {\n        ImPlot3DPlot& plot = *gp.Plots.GetByIndex(p);\n        if (show_frame_rects)\n            fg.AddRect(plot.FrameRect.Min, plot.FrameRect.Max, IM_COL32(255, 0, 255, 255));\n        if (show_canvas_rects)\n            fg.AddRect(plot.CanvasRect.Min, plot.CanvasRect.Max, IM_COL32(0, 255, 255, 255));\n        if (show_plot_rects)\n            fg.AddRect(plot.PlotRect.Min, plot.PlotRect.Max, IM_COL32(255, 255, 0, 255));\n        if (show_plot_box || show_axis_line || show_axis_corner_indexes || show_axis_face_indexes || show_axis_edge_indexes)\n            GetAxesParameters(plot, active_faces, corners_pix, corners, plane_2d, axis_corners);\n        if (show_plot_box || show_axis_edge_indexes) {\n            // Determine which display type each of the edges are\n            DisplayedType edge_types[12];\n            for (int e = 0; e < 12; e++) {\n                if (plane_2d == -1 && show_plot_box)\n                    edge_types[e] = DisplayedType::NotActive;\n                else\n                    edge_types[e] = DisplayedType::Hidden;\n            }\n            for (int a = 0; a < 3; a++) {\n                int face_idx = a + 3 * active_faces[a];\n                for (size_t i = 0; (plane_2d == -1 || a == plane_2d) && i < 4; i++)\n                    edge_types[face_edges[face_idx][i]] = DisplayedType::Active;\n            }\n\n            // Go through each edge and use the display type\n            for (int e = 0; e < 12; e++) {\n                int idx0 = edges[e][0];\n                int idx1 = edges[e][1];\n\n                if (show_plot_box) {\n                    // Draw different lines depending on the type that the renderer thinks it is\n                    if (edge_types[e] == DisplayedType::Active)\n                        fg.AddLine(corners_pix[idx0], corners_pix[idx1], IM_COL32(255, 200, 0, 255));\n                    else if (edge_types[e] == DisplayedType::NotActive)\n                        fg.AddLine(corners_pix[idx0], corners_pix[idx1], IM_COL32(100, 255, 0, 125));\n                    // If the render edge type is Hidden then there this edge does not get rendered\n                }\n                if (show_axis_edge_indexes) {\n                    if (edge_types[e] == DisplayedType::Active || edge_types[e] == DisplayedType::NotActive) {\n                        // Display the text in the middle between the points and at the same angle between the two points\n                        ImVec2 edge_middle = (corners_pix[idx0] + corners_pix[idx1]) / 2;\n                        ImVec2 point_difference = (corners_pix[idx1] - corners_pix[idx0]);\n                        float angle = ImAtan2(-point_difference.y, point_difference.x);\n                        // Adjust angle to keep labels upright\n                        if (angle > IM_PI * 0.5f)\n                            angle -= IM_PI;\n                        if (angle < -IM_PI * 0.5f)\n                            angle += IM_PI;\n\n                        ImFormatString(buff, IM_ARRAYSIZE(buff), \"E%d\", e);\n                        AddTextRotated(&fg, edge_middle, angle, IM_COL32(200, 0, 0, 200), buff);\n                    }\n                }\n            }\n        }\n        if (show_axis_line) {\n            // For each axis plot a line between the axis corners\n            for (int a = 0; a < 3; a++) {\n                if (plane_2d != -1 && a == plane_2d)\n                    continue;\n                int idx0 = axis_corners[a][0];\n                int idx1 = axis_corners[a][1];\n\n                // Draw axis line\n                ImVec2 axis_start_pix = corners_pix[idx0];\n                ImVec2 axis_end_pix = corners_pix[idx1];\n                fg.AddLine(axis_start_pix, axis_end_pix, IM_COL32(0, 255, 255, 255));\n            }\n        }\n        if (show_axis_corner_indexes) {\n            // Determine which display type each of the corners are\n            DisplayedType corner_types[8];\n            for (int c = 0; c < 8; c++) {\n                if (plane_2d == -1 && show_plot_box)\n                    corner_types[c] = DisplayedType::NotActive;\n                else\n                    corner_types[c] = DisplayedType::Hidden;\n            }\n            for (int a = 0; a < 3; a++) {\n                int face_idx = a + 3 * active_faces[a];\n                for (size_t i = 0; (plane_2d == -1 || a == plane_2d) && i < 4; i++)\n                    corner_types[faces[face_idx][i]] = DisplayedType::Active;\n            }\n            // Go through each corner and use the display type\n            for (int c = 0; c < 8; c++) {\n                if (corner_types[c] == DisplayedType::Active || corner_types[c] == DisplayedType::NotActive) {\n                    ImVec2 corner = corners_pix[c];\n                    ImFormatString(buff, IM_ARRAYSIZE(buff), \"C%d\", c);\n                    AddTextRotated(&fg, corner, 0, IM_COL32(0, 200, 0, 200), buff);\n                }\n            }\n        }\n        if (show_axis_face_indexes) {\n            // Go through each axis and then face to display the face index\n            for (int a = 0; a < 3; a++) {\n                for (int f = 0; f < 2; f++) {\n                    if ((plane_2d == -1 || a == plane_2d) && (f == int(active_faces[a]) || show_plot_box)) {\n                        const int face_idx = a + 3 * f;\n                        const int* face_indexes = faces[face_idx];\n                        // Display the text in the middle of the square by determining the point between two of opposite corner pixels\n                        ImVec2 middle_of_face_sqaure = (corners_pix[face_indexes[0]] + corners_pix[face_indexes[2]]) / 2;\n                        ImFormatString(buff, IM_ARRAYSIZE(buff), \"F%d\", face_idx);\n                        AddTextRotated(&fg, middle_of_face_sqaure, 0, IM_COL32(0, 200, 200, 200), buff);\n                    }\n                }\n            }\n        }\n        if (show_legend_rects && plot.Items.GetLegendCount() > 0)\n            fg.AddRect(plot.Items.Legend.Rect.Min, plot.Items.Legend.Rect.Max, IM_COL32(255, 192, 0, 255));\n    }\n    if (ImGui::TreeNode(\"Plots\", \"Plots (%d)\", n_plots)) {\n        for (int p = 0; p < n_plots; ++p) {\n            ImPlot3DPlot& plot = *gp.Plots.GetByIndex(p);\n            ImGui::PushID(p);\n            if (ImGui::TreeNode(\"Plot\", \"Plot [0x%08X]\", plot.ID)) {\n                int n_items = plot.Items.GetItemCount();\n                if (ImGui::TreeNode(\"Items\", \"Items (%d)\", n_items)) {\n                    for (int i = 0; i < n_items; ++i) {\n                        ImPlot3DItem* item = plot.Items.GetItemByIndex(i);\n                        ImGui::PushID(i);\n                        if (ImGui::TreeNode(\"Item\", \"Item [0x%08X]\", item->ID)) {\n                            ImGui::Bullet();\n                            ImGui::Checkbox(\"Show\", &item->Show);\n                            ImGui::Bullet();\n                            ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color);\n                            if (ImGui::ColorEdit4(\"Color\", &temp.x, ImGuiColorEditFlags_NoInputs))\n                                item->Color = ImGui::ColorConvertFloat4ToU32(temp);\n\n                            ImGui::BulletText(\"NameOffset: %d\", item->NameOffset);\n                            ImGui::BulletText(\"Name: %s\", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : \"N/A\");\n                            ImGui::BulletText(\"Hovered: %s\", item->LegendHovered ? \"true\" : \"false\");\n                            ImGui::TreePop();\n                        }\n                        ImGui::PopID();\n                    }\n                    ImGui::TreePop();\n                }\n\n                if (ImGui::TreeNode(\"Axes\")) {\n                    // Get the axes parameters of this plot and use it to plot\n                    GetAxesParameters(plot, active_faces, corners_pix, corners, plane_2d, axis_corners);\n\n                    for (int a = 0; a < 3; ++a) {\n                        if (plane_2d != -1 && plane_2d == a)\n                            continue;\n                        if (ImGui::TreeNode(axis_labels[a])) {\n                            ShowAxisMetrics(plot.Axes[a]);\n                            ImGui::TreePop();\n                        }\n                    }\n\n                    if (plane_2d != -1)\n                        ImGui::BulletText(\"Plane2D: %d %s\", plane_2d, plane_labels[plane_2d]);\n                    else {\n                        int active_corner_index = Active3DFacesToAxisLookupIndex(active_faces);\n                        ImGui::BulletText(\"3D Active Faces: [%s,%s,%s]=%d\", active_faces[0] ? \"X-max\" : \"X-min\", active_faces[1] ? \"Y-max\" : \"Y-min\",\n                                          active_faces[2] ? \"Z-max\" : \"Z-min\", active_corner_index);\n                        ImGui::BulletText(\"3D Active Corner Lookup: [[%d,%d],[%d, %d],[%d, %d]]\", axis_corners_lookup_3d[active_corner_index][0][0],\n                                          axis_corners_lookup_3d[active_corner_index][0][1], axis_corners_lookup_3d[active_corner_index][1][0],\n                                          axis_corners_lookup_3d[active_corner_index][1][1], axis_corners_lookup_3d[active_corner_index][2][0],\n                                          axis_corners_lookup_3d[active_corner_index][2][1]);\n                        ImGui::BulletText(\"3D Active Edges Lookup: [%d,%d,%d]\", axis_edges_lookup_3d[active_corner_index][0],\n                                          axis_edges_lookup_3d[active_corner_index][1], axis_edges_lookup_3d[active_corner_index][2]);\n                    }\n\n                    for (int a = 0; a < 3; a++)\n                        ImGui::BulletText(\"%s Label Corners: [%d,%d]\", axis_labels[a], axis_corners[a][0], axis_corners[a][1]);\n                    for (int c = 0; c < 8; c++)\n                        ImGui::BulletText(\"Corner %d: [%.2f,%.2f,%.2f] [%.2f,%.2f]\", c, corners[c].x, corners[c].y, corners[c].z, corners_pix[c].x,\n                                          corners_pix[c].y);\n\n                    ImGui::TreePop();\n                }\n\n                ImGui::BulletText(\"Title: %s\", plot.HasTitle() ? plot.GetTitle() : \"none\");\n                ImGui::BulletText(\"Flags: 0x%08X\", plot.Flags);\n                ImGui::BulletText(\"Initialized: %s\", plot.Initialized ? \"true\" : \"false\");\n                ImGui::BulletText(\"Hovered: %s\", plot.Hovered ? \"true\" : \"false\");\n                ImGui::BulletText(\"Held: %s\", plot.Held ? \"true\" : \"false\");\n                ImGui::BulletText(\"HeldEdgeIdx: %d\", plot.HeldEdgeIdx);\n                ImGui::BulletText(\"HeldPlaneIdx: %d\", plot.HeldPlaneIdx);\n                ImGui::BulletText(\"LegendLocation: %s\", GetShortLegendLocationName(plot.Items.Legend.Location));\n                ImGui::BulletText(\"LegendHovered: %s\", plot.Items.Legend.Hovered ? \"true\" : \"false\");\n                ImGui::BulletText(\"LegendHeld: %s\", plot.Items.Legend.Held ? \"true\" : \"false\");\n                ImGui::BulletText(\"RotationCond: 0x%08X\", plot.RotationCond);\n                ImGui::BulletText(\"InitialRotation: [%.2f,%.2f,%.2f,%.2f]\", plot.InitialRotation.x, plot.InitialRotation.y, plot.InitialRotation.z,\n                                  plot.InitialRotation.w);\n                ImGui::BulletText(\"Rotation: [%.2f,%.2f,%.2f,%.2f]\", plot.Rotation.x, plot.Rotation.y, plot.Rotation.z, plot.Rotation.w);\n                ImGui::BulletText(\"Animation: Time=%.4f RotationEnd=[%.2f,%.2f,%.2f,%.2f]\", plot.AnimationTime, plot.RotationAnimationEnd.x,\n                                  plot.RotationAnimationEnd.y, plot.Rotation.z, plot.RotationAnimationEnd.w);\n                ImGui::BulletText(\"BoxScale: [%.2f,%.2f,%.2f]\", plot.BoxScale.x, plot.BoxScale.y, plot.BoxScale.z);\n                ImGui::BulletText(\"BoxZoom: %.2f\", plot.GetBoxZoom());\n\n                ImGui::TreePop();\n            }\n            ImGui::PopID();\n        }\n        ImGui::TreePop();\n    }\n\n    if (ImGui::TreeNode(\"Colormaps\")) {\n        ImGui::BulletText(\"Colormaps:  %d\", gp.ColormapData.Count);\n        ImGui::BulletText(\"Memory: %d bytes\", gp.ColormapData.Tables.Size * (int)(sizeof(gp.ColormapData.Tables.Data[0])));\n        if (ImGui::TreeNode(\"Data\")) {\n            for (int m = 0; m < gp.ColormapData.Count; ++m) {\n                if (ImGui::TreeNode(gp.ColormapData.GetName(m))) {\n                    int count = gp.ColormapData.GetKeyCount(m);\n                    int size = gp.ColormapData.GetTableSize(m);\n                    bool qual = gp.ColormapData.IsQual(m);\n                    ImGui::BulletText(\"Qualitative: %s\", qual ? \"true\" : \"false\");\n                    ImGui::BulletText(\"Key Count: %d\", count);\n                    ImGui::BulletText(\"Table Size: %d\", size);\n                    ImGui::Indent();\n\n                    static float t = 0.5;\n                    ImVec4 samp;\n                    float wid = 32 * 10 - ImGui::GetFrameHeight() - ImGui::GetStyle().ItemSpacing.x;\n                    ImGui::SetNextItemWidth(wid);\n                    ColormapSlider(\"##Sample\", &t, &samp, \"%.3f\", m);\n                    ImGui::SameLine();\n                    ImGui::ColorButton(\"Sampler\", samp);\n                    ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0, 0, 0, 0));\n                    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));\n                    for (int c = 0; c < size; ++c) {\n                        ImVec4 col = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetTableColor(m, c));\n                        ImGui::PushID(m * 1000 + c);\n                        ImGui::ColorButton(\"\", col, 0, ImVec2(10, 10));\n                        ImGui::PopID();\n                        if ((c + 1) % 32 != 0 && c != size - 1)\n                            ImGui::SameLine();\n                    }\n                    ImGui::PopStyleVar();\n                    ImGui::PopStyleColor();\n                    ImGui::Unindent();\n                    ImGui::TreePop();\n                }\n            }\n            ImGui::TreePop();\n        }\n        ImGui::TreePop();\n    }\n    ImGui::End();\n}\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/implot3d/source/implot3d_demo.cpp",
    "content": "//--------------------------------------------------\n// ImPlot3D v0.3 WIP\n// implot3d_demo.cpp\n// Date: 2024-11-17\n// Author: Breno Cunha Queiroz (brenocq.com)\n//\n// Acknowledgments:\n//  ImPlot3D is heavily inspired by ImPlot\n//  (https://github.com/epezent/implot) by Evan Pezent,\n//  and follows a similar code style and structure to\n//  maintain consistency with ImPlot's API.\n//--------------------------------------------------\n\n// Table of Contents:\n// [SECTION] User Namespace\n// [SECTION] Helpers\n// [SECTION] Plots\n// [SECTION] Axes\n// [SECTION] Custom\n// [SECTION] Demo Window\n// [SECTION] Style Editor\n// [SECTION] User Namespace Implementation\n\n// We define this to avoid accidentally using the deprecated API\n#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS\n#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS\n#endif\n\n#include \"implot3d.h\"\n#include \"implot3d_internal.h\"\n\n//-----------------------------------------------------------------------------\n// [SECTION] User Namespace\n//-----------------------------------------------------------------------------\n\n// Encapsulates examples for customizing ImPlot3D\nnamespace MyImPlot3D {\n\n// Example for Custom Styles section\nvoid StyleSeaborn();\n\n} // namespace MyImPlot3D\n\nnamespace ImPlot3D {\n\n//-----------------------------------------------------------------------------\n// [SECTION] Helpers\n//-----------------------------------------------------------------------------\n\n#define CHECKBOX_FLAG(flags, flag) ImGui::CheckboxFlags(#flag, (unsigned int*)&flags, flag)\n\nstatic void HelpMarker(const char* desc) {\n    ImGui::TextDisabled(\"(?)\");\n    if (ImGui::BeginItemTooltip()) {\n        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);\n        ImGui::TextUnformatted(desc);\n        ImGui::PopTextWrapPos();\n        ImGui::EndTooltip();\n    }\n}\n\n// Utility structure for realtime plot\nstruct ScrollingBuffer {\n    int MaxSize;\n    int Offset;\n    ImVector<float> Data;\n    ScrollingBuffer(int max_size = 2000) {\n        MaxSize = max_size;\n        Offset = 0;\n        Data.reserve(MaxSize);\n    }\n    void AddPoint(float x) {\n        if (Data.size() < MaxSize)\n            Data.push_back(x);\n        else {\n            Data[Offset] = x;\n            Offset = (Offset + 1) % MaxSize;\n        }\n    }\n    void Erase() {\n        if (Data.size() > 0) {\n            Data.shrink(0);\n            Offset = 0;\n        }\n    }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Plots\n//-----------------------------------------------------------------------------\n\nvoid DemoLinePlots() {\n    static float xs1[1001], ys1[1001], zs1[1001];\n    for (int i = 0; i < 1001; i++) {\n        xs1[i] = i * 0.001f;\n        ys1[i] = 0.5f + 0.5f * cosf(50 * (xs1[i] + (float)ImGui::GetTime() / 10));\n        zs1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)ImGui::GetTime() / 10));\n    }\n    static double xs2[20], ys2[20], zs2[20];\n    for (int i = 0; i < 20; i++) {\n        xs2[i] = i * 1 / 19.0f;\n        ys2[i] = xs2[i] * xs2[i];\n        zs2[i] = xs2[i] * ys2[i];\n    }\n    if (ImPlot3D::BeginPlot(\"Line Plots\")) {\n        ImPlot3D::SetupAxes(\"x\", \"y\", \"z\");\n        ImPlot3D::PlotLine(\"f(x)\", xs1, ys1, zs1, 1001);\n        ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Circle);\n        ImPlot3D::PlotLine(\"g(x)\", xs2, ys2, zs2, 20, ImPlot3DLineFlags_Segments);\n        ImPlot3D::EndPlot();\n    }\n}\n\nvoid DemoScatterPlots() {\n    srand(0);\n    static float xs1[100], ys1[100], zs1[100];\n    for (int i = 0; i < 100; i++) {\n        xs1[i] = i * 0.01f;\n        ys1[i] = xs1[i] + 0.1f * ((float)rand() / (float)RAND_MAX);\n        zs1[i] = xs1[i] + 0.1f * ((float)rand() / (float)RAND_MAX);\n    }\n    static float xs2[50], ys2[50], zs2[50];\n    for (int i = 0; i < 50; i++) {\n        xs2[i] = 0.25f + 0.2f * ((float)rand() / (float)RAND_MAX);\n        ys2[i] = 0.50f + 0.2f * ((float)rand() / (float)RAND_MAX);\n        zs2[i] = 0.75f + 0.2f * ((float)rand() / (float)RAND_MAX);\n    }\n\n    if (ImPlot3D::BeginPlot(\"Scatter Plots\")) {\n        ImPlot3D::PlotScatter(\"Data 1\", xs1, ys1, zs1, 100);\n        ImPlot3D::PushStyleVar(ImPlot3DStyleVar_FillAlpha, 0.25f);\n        ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 6, ImPlot3D::GetColormapColor(1), IMPLOT3D_AUTO, ImPlot3D::GetColormapColor(1));\n        ImPlot3D::PlotScatter(\"Data 2\", xs2, ys2, zs2, 50);\n        ImPlot3D::PopStyleVar();\n        ImPlot3D::EndPlot();\n    }\n}\n\nvoid DemoTrianglePlots() {\n    // Pyramid coordinates\n    // Apex\n    float ax = 0.0f, ay = 0.0f, az = 1.0f;\n    // Square base corners\n    float cx[4] = {-0.5f, 0.5f, 0.5f, -0.5f};\n    float cy[4] = {-0.5f, -0.5f, 0.5f, 0.5f};\n    float cz[4] = {0.0f, 0.0f, 0.0f, 0.0f};\n\n    // We have 6 triangles (18 vertices) total:\n    // Sides:\n    // T1: apex, corner0, corner1\n    // T2: apex, corner1, corner2\n    // T3: apex, corner2, corner3\n    // T4: apex, corner3, corner0\n    // Base (two triangles form a square):\n    // T5: corner0, corner1, corner2\n    // T6: corner0, corner2, corner3\n\n    static float xs[18], ys[18], zs[18];\n    int i = 0;\n\n    // Helper lambda to append a vertex\n    auto AddVertex = [&](float X, float Y, float Z) {\n        xs[i] = X;\n        ys[i] = Y;\n        zs[i] = Z;\n        i++;\n    };\n\n    // Triangle 1\n    AddVertex(ax, ay, az);\n    AddVertex(cx[0], cy[0], cz[0]);\n    AddVertex(cx[1], cy[1], cz[1]);\n\n    // Triangle 2\n    AddVertex(ax, ay, az);\n    AddVertex(cx[1], cy[1], cz[1]);\n    AddVertex(cx[2], cy[2], cz[2]);\n\n    // Triangle 3\n    AddVertex(ax, ay, az);\n    AddVertex(cx[2], cy[2], cz[2]);\n    AddVertex(cx[3], cy[3], cz[3]);\n\n    // Triangle 4\n    AddVertex(ax, ay, az);\n    AddVertex(cx[3], cy[3], cz[3]);\n    AddVertex(cx[0], cy[0], cz[0]);\n\n    // Triangle 5 (base)\n    AddVertex(cx[0], cy[0], cz[0]);\n    AddVertex(cx[1], cy[1], cz[1]);\n    AddVertex(cx[2], cy[2], cz[2]);\n\n    // Triangle 6 (base)\n    AddVertex(cx[0], cy[0], cz[0]);\n    AddVertex(cx[2], cy[2], cz[2]);\n    AddVertex(cx[3], cy[3], cz[3]);\n\n    // Now we have 18 vertices in xs, ys, zs forming the pyramid\n\n    // Triangle flags\n    static ImPlot3DTriangleFlags flags = ImPlot3DTriangleFlags_None;\n    CHECKBOX_FLAG(flags, ImPlot3DTriangleFlags_NoLines);\n    CHECKBOX_FLAG(flags, ImPlot3DTriangleFlags_NoFill);\n    CHECKBOX_FLAG(flags, ImPlot3DTriangleFlags_NoMarkers);\n\n    if (ImPlot3D::BeginPlot(\"Triangle Plots\")) {\n        ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -0.5, 1.5);\n\n        // Setup pyramid colors\n        ImPlot3D::SetNextFillStyle(ImPlot3D::GetColormapColor(0));\n        ImPlot3D::SetNextLineStyle(ImPlot3D::GetColormapColor(1), 2);\n        ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 3, ImPlot3D::GetColormapColor(2), IMPLOT3D_AUTO, ImPlot3D::GetColormapColor(2));\n\n        // Plot pyramid\n        ImPlot3D::PlotTriangle(\"Pyramid\", xs, ys, zs, 6 * 3, flags); // 6 triangles, 3 vertices each = 18\n        ImPlot3D::EndPlot();\n    }\n}\n\nvoid DemoQuadPlots() {\n    static float xs[6 * 4], ys[6 * 4], zs[6 * 4];\n\n    // clang-format off\n    // Initialize the cube vertices for +x and -x faces\n    // +x face\n    xs[0] = 1; ys[0] = -1; zs[0] = -1;\n    xs[1] = 1; ys[1] =  1; zs[1] = -1;\n    xs[2] = 1; ys[2] =  1; zs[2] =  1;\n    xs[3] = 1; ys[3] = -1; zs[3] =  1;\n\n    // -x face\n    xs[4] = -1; ys[4] = -1; zs[4] = -1;\n    xs[5] = -1; ys[5] =  1; zs[5] = -1;\n    xs[6] = -1; ys[6] =  1; zs[6] =  1;\n    xs[7] = -1; ys[7] = -1; zs[7] =  1;\n\n    // Initialize the cube vertices for +y and -y faces\n    // +y face\n    xs[8] = -1; ys[8] = 1; zs[8] = -1;\n    xs[9] =  1; ys[9] = 1; zs[9] = -1;\n    xs[10] =  1; ys[10] = 1; zs[10] =  1;\n    xs[11] = -1; ys[11] = 1; zs[11] =  1;\n\n    // -y face\n    xs[12] = -1; ys[12] = -1; zs[12] = -1;\n    xs[13] =  1; ys[13] = -1; zs[13] = -1;\n    xs[14] =  1; ys[14] = -1; zs[14] =  1;\n    xs[15] = -1; ys[15] = -1; zs[15] =  1;\n\n    // Initialize the cube vertices for +z and -z faces\n    // +z face\n    xs[16] = -1; ys[16] = -1; zs[16] = 1;\n    xs[17] =  1; ys[17] = -1; zs[17] = 1;\n    xs[18] =  1; ys[18] =  1; zs[18] = 1;\n    xs[19] = -1; ys[19] =  1; zs[19] = 1;\n\n    // -z face\n    xs[20] = -1; ys[20] = -1; zs[20] = -1;\n    xs[21] =  1; ys[21] = -1; zs[21] = -1;\n    xs[22] =  1; ys[22] =  1; zs[22] = -1;\n    xs[23] = -1; ys[23] =  1; zs[23] = -1;\n    // clang-format on\n\n    // Quad flags\n    static ImPlot3DQuadFlags flags = ImPlot3DQuadFlags_None;\n    CHECKBOX_FLAG(flags, ImPlot3DQuadFlags_NoLines);\n    CHECKBOX_FLAG(flags, ImPlot3DQuadFlags_NoFill);\n    CHECKBOX_FLAG(flags, ImPlot3DQuadFlags_NoMarkers);\n\n    if (ImPlot3D::BeginPlot(\"Quad Plots\")) {\n        ImPlot3D::SetupAxesLimits(-1.5f, 1.5f, -1.5f, 1.5f, -1.5f, 1.5f);\n\n        // Render +x and -x faces\n        static ImVec4 colorX(0.8f, 0.2f, 0.2f, 0.8f); // Red\n        ImPlot3D::SetNextFillStyle(colorX);\n        ImPlot3D::SetNextLineStyle(colorX, 2);\n        ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 3, colorX, IMPLOT3D_AUTO, colorX);\n        ImPlot3D::PlotQuad(\"X\", &xs[0], &ys[0], &zs[0], 8, flags);\n\n        // Render +y and -y faces\n        static ImVec4 colorY(0.2f, 0.8f, 0.2f, 0.8f); // Green\n        ImPlot3D::SetNextFillStyle(colorY);\n        ImPlot3D::SetNextLineStyle(colorY, 2);\n        ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 3, colorY, IMPLOT3D_AUTO, colorY);\n        ImPlot3D::PlotQuad(\"Y\", &xs[8], &ys[8], &zs[8], 8, flags);\n\n        // Render +z and -z faces\n        static ImVec4 colorZ(0.2f, 0.2f, 0.8f, 0.8f); // Blue\n        ImPlot3D::SetNextFillStyle(colorZ);\n        ImPlot3D::SetNextLineStyle(colorZ, 2);\n        ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 3, colorZ, IMPLOT3D_AUTO, colorZ);\n        ImPlot3D::PlotQuad(\"Z\", &xs[16], &ys[16], &zs[16], 8, flags);\n\n        ImPlot3D::EndPlot();\n    }\n}\n\nvoid DemoSurfacePlots() {\n    constexpr int N = 20;\n    static float xs[N * N], ys[N * N], zs[N * N];\n    static float t = 0.0f;\n    t += ImGui::GetIO().DeltaTime;\n\n    // Define the range for X and Y\n    constexpr float min_val = -1.0f;\n    constexpr float max_val = 1.0f;\n    constexpr float step = (max_val - min_val) / (N - 1);\n\n    // Populate the xs, ys, and zs arrays\n    for (int i = 0; i < N; i++) {\n        for (int j = 0; j < N; j++) {\n            int idx = i * N + j;\n            xs[idx] = min_val + j * step;                                             // X values are constant along rows\n            ys[idx] = min_val + i * step;                                             // Y values are constant along columns\n            zs[idx] = ImSin(2 * t + ImSqrt((xs[idx] * xs[idx] + ys[idx] * ys[idx]))); // z = sin(2t + sqrt(x^2 + y^2))\n        }\n    }\n\n    // Choose fill color\n    ImGui::Text(\"Fill color\");\n    static int selected_fill = 1; // Colormap by default\n    static ImVec4 solid_color = ImVec4(0.8f, 0.8f, 0.2f, 0.6f);\n    const char* colormaps[] = {\"Viridis\", \"Plasma\", \"Hot\", \"Cool\", \"Pink\", \"Jet\", \"Twilight\", \"RdBu\", \"BrBG\", \"PiYG\", \"Spectral\", \"Greys\"};\n    static int sel_colormap = 5; // Jet by default\n    {\n        ImGui::Indent();\n\n        // Choose solid color\n        ImGui::RadioButton(\"Solid\", &selected_fill, 0);\n        if (selected_fill == 0) {\n            ImGui::SameLine();\n            ImGui::ColorEdit4(\"##SurfaceSolidColor\", (float*)&solid_color);\n        }\n\n        // Choose colormap\n        ImGui::RadioButton(\"Colormap\", &selected_fill, 1);\n        if (selected_fill == 1) {\n            ImGui::SameLine();\n            ImGui::Combo(\"##SurfaceColormap\", &sel_colormap, colormaps, IM_ARRAYSIZE(colormaps));\n        }\n        ImGui::Unindent();\n    }\n\n    // Choose range\n    static bool custom_range = false;\n    static float range_min = -1.0f;\n    static float range_max = 1.0f;\n    ImGui::Checkbox(\"Custom range\", &custom_range);\n    {\n        ImGui::Indent();\n\n        if (!custom_range)\n            ImGui::BeginDisabled();\n        ImGui::SliderFloat(\"Range min\", &range_min, -1.0f, range_max - 0.01f);\n        ImGui::SliderFloat(\"Range max\", &range_max, range_min + 0.01f, 1.0f);\n        if (!custom_range)\n            ImGui::EndDisabled();\n\n        ImGui::Unindent();\n    }\n\n    // Select flags\n    static ImPlot3DSurfaceFlags flags = ImPlot3DSurfaceFlags_NoMarkers;\n    CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoLines);\n    CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoFill);\n    CHECKBOX_FLAG(flags, ImPlot3DSurfaceFlags_NoMarkers);\n\n    // Begin the plot\n    if (selected_fill == 1)\n        ImPlot3D::PushColormap(colormaps[sel_colormap]);\n    if (ImPlot3D::BeginPlot(\"Surface Plots\", ImVec2(-1, 400), ImPlot3DFlags_NoClip)) {\n        ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1.5, 1.5);\n\n        // Set fill style\n        ImPlot3D::PushStyleVar(ImPlot3DStyleVar_FillAlpha, 0.8f);\n        if (selected_fill == 0)\n            ImPlot3D::SetNextFillStyle(solid_color);\n\n        // Set line style\n        ImPlot3D::SetNextLineStyle(ImPlot3D::GetColormapColor(1));\n\n        // Set marker style\n        ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, IMPLOT3D_AUTO, ImPlot3D::GetColormapColor(2));\n\n        // Plot the surface\n        if (custom_range)\n            ImPlot3D::PlotSurface(\"Wave Surface\", xs, ys, zs, N, N, (double)range_min, (double)range_max, flags);\n        else\n            ImPlot3D::PlotSurface(\"Wave Surface\", xs, ys, zs, N, N, 0.0, 0.0, flags);\n\n        // End the plot\n        ImPlot3D::PopStyleVar();\n        ImPlot3D::EndPlot();\n    }\n    if (selected_fill == 1)\n        ImPlot3D::PopColormap();\n}\n\nvoid DemoMeshPlots() {\n    static int mesh_id = 0;\n    ImGui::Combo(\"Mesh\", &mesh_id, \"Duck\\0Sphere\\0Cube\\0\\0\");\n\n    // Choose line color\n    static ImVec4 line_color = ImVec4(0.5f, 0.5f, 0.2f, 0.6f);\n    ImGui::ColorEdit4(\"Line Color##Mesh\", (float*)&line_color);\n\n    // Choose fill color\n    static ImVec4 fill_color = ImVec4(0.8f, 0.8f, 0.2f, 0.6f);\n    ImGui::ColorEdit4(\"Fill Color##Mesh\", (float*)&fill_color);\n\n    // Choose marker color\n    static ImVec4 marker_color = ImVec4(0.5f, 0.5f, 0.2f, 0.6f);\n    ImGui::ColorEdit4(\"Marker Color##Mesh\", (float*)&marker_color);\n\n    // Mesh flags\n    static ImPlot3DMeshFlags flags = ImPlot3DMeshFlags_NoMarkers;\n    CHECKBOX_FLAG(flags, ImPlot3DMeshFlags_NoLines);\n    CHECKBOX_FLAG(flags, ImPlot3DMeshFlags_NoFill);\n    CHECKBOX_FLAG(flags, ImPlot3DMeshFlags_NoMarkers);\n\n    if (ImPlot3D::BeginPlot(\"Mesh Plots\")) {\n        ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1, 1);\n\n        // Set fill style\n        ImPlot3D::SetNextFillStyle(fill_color);\n\n        // Set line style\n        ImPlot3D::SetNextLineStyle(line_color);\n\n        // Set marker style\n        ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 3, marker_color, IMPLOT3D_AUTO, marker_color);\n\n        // Plot mesh\n        if (mesh_id == 0)\n            ImPlot3D::PlotMesh(\"Duck\", duck_vtx, duck_idx, DUCK_VTX_COUNT, DUCK_IDX_COUNT, flags);\n        else if (mesh_id == 1)\n            ImPlot3D::PlotMesh(\"Sphere\", sphere_vtx, sphere_idx, SPHERE_VTX_COUNT, SPHERE_IDX_COUNT, flags);\n        else if (mesh_id == 2)\n            ImPlot3D::PlotMesh(\"Cube\", cube_vtx, cube_idx, CUBE_VTX_COUNT, CUBE_IDX_COUNT, flags);\n\n        ImPlot3D::EndPlot();\n    }\n}\n\nvoid DemoImagePlots() {\n    ImGui::BulletText(\"Below we are displaying the font texture, which is the only texture we have\\naccess to in this demo.\");\n    ImGui::BulletText(\"Use the 'ImTextureID' type as storage to pass pointers or identifiers to your\\nown texture data.\");\n    ImGui::BulletText(\"See ImGui Wiki page 'Image Loading and Displaying Examples'.\");\n\n    static ImVec4 tint1(1, 1, 1, 1);\n    static ImVec4 tint2(1, 1, 1, 1);\n\n    static ImPlot3DPoint center1(0, 0, 1);\n    static ImPlot3DPoint axis_u1(1, 0, 0);\n    static ImPlot3DPoint axis_v1(0, 1, 0);\n    static ImVec2 uv0_1(0, 0), uv1_1(1, 1);\n\n    static ImPlot3DPoint p0(-1, -1, 0);\n    static ImPlot3DPoint p1(1, -1, 0);\n    static ImPlot3DPoint p2(1, 1, 0);\n    static ImPlot3DPoint p3(-1, 1, 0);\n    static ImVec2 uv0(0, 0), uv1(1, 0), uv2(1, 1), uv3(0, 1);\n\n    // Spacing\n    ImGui::Dummy(ImVec2(0, 10));\n\n    // Image 1 Controls\n    if (ImGui::TreeNodeEx(\"Image 1 Controls: Center + Axes\")) {\n        ImGui::SliderFloat3(\"Center\", &center1.x, -2, 2, \"%.1f\");\n        ImGui::SliderFloat3(\"Axis U\", &axis_u1.x, -2, 2, \"%.1f\");\n        ImGui::SliderFloat3(\"Axis V\", &axis_v1.x, -2, 2, \"%.1f\");\n        ImGui::SliderFloat2(\"UV0\", &uv0_1.x, 0, 1, \"%.2f\");\n        ImGui::SliderFloat2(\"UV1\", &uv1_1.x, 0, 1, \"%.2f\");\n        ImGui::ColorEdit4(\"Tint\", &tint1.x);\n\n        ImGui::TreePop();\n    }\n\n    // Image 2 Controls\n    if (ImGui::TreeNodeEx(\"Image 2 Controls: Full Quad\")) {\n        ImGui::SliderFloat3(\"P0\", &p0.x, -2, 2, \"%.1f\");\n        ImGui::SliderFloat3(\"P1\", &p1.x, -2, 2, \"%.1f\");\n        ImGui::SliderFloat3(\"P2\", &p2.x, -2, 2, \"%.1f\");\n        ImGui::SliderFloat3(\"P3\", &p3.x, -2, 2, \"%.1f\");\n\n        ImGui::SliderFloat2(\"UV0\", &uv0.x, 0, 1, \"%.2f\");\n        ImGui::SliderFloat2(\"UV1\", &uv1.x, 0, 1, \"%.2f\");\n        ImGui::SliderFloat2(\"UV2\", &uv2.x, 0, 1, \"%.2f\");\n        ImGui::SliderFloat2(\"UV3\", &uv3.x, 0, 1, \"%.2f\");\n\n        ImGui::ColorEdit4(\"Tint##2\", &tint2.x);\n\n        ImGui::TreePop();\n    }\n\n    // Plot\n    if (ImPlot3D::BeginPlot(\"Image Plot\", ImVec2(-1, 0), ImPlot3DFlags_NoClip)) {\n#ifdef IMGUI_HAS_TEXTURES\n        // We use the font atlas ImTextureRef for this demo, but in your real code when you submit\n        // an image that you have loaded yourself, you would normally have a ImTextureID which works\n        // just as well (as ImTextureRef can be constructed from ImTextureID).\n        ImTextureRef tex = ImGui::GetIO().Fonts->TexRef;\n#else\n        ImTextureID tex = ImGui::GetIO().Fonts->TexID;\n#endif\n\n        ImPlot3D::PlotImage(\"Image 1\", tex, center1, axis_u1, axis_v1, uv0_1, uv1_1, tint1);\n        ImPlot3D::PlotImage(\"Image 2\", tex, p0, p1, p2, p3, uv0, uv1, uv2, uv3, tint2);\n\n        ImPlot3D::EndPlot();\n    }\n}\n\nvoid DemoRealtimePlots() {\n    ImGui::BulletText(\"Move your mouse to change the data!\");\n    static ScrollingBuffer sdata1, sdata2, sdata3;\n    static ImPlot3DAxisFlags flags = ImPlot3DAxisFlags_NoTickLabels;\n    static float t = 0.0f;\n    static float last_t = -1.0f;\n\n    if (ImPlot3D::BeginPlot(\"Scrolling Plot\", ImVec2(-1, 400))) {\n        // Pool mouse data every 10 ms\n        t += ImGui::GetIO().DeltaTime;\n        if (t - last_t > 0.01f) {\n            last_t = t;\n            ImVec2 mouse = ImGui::GetMousePos();\n            if (ImAbs(mouse.x) < 1e4f && ImAbs(mouse.y) < 1e4f) {\n                ImVec2 plot_center = ImPlot3D::GetFramePos();\n                plot_center.x += ImPlot3D::GetFrameSize().x / 2;\n                plot_center.y += ImPlot3D::GetFrameSize().y / 2;\n                sdata1.AddPoint(t);\n                sdata2.AddPoint(mouse.x - plot_center.x);\n                sdata3.AddPoint(mouse.y - plot_center.y);\n            }\n        }\n\n        ImPlot3D::SetupAxes(\"Time\", \"Mouse X\", \"Mouse Y\", flags, flags, flags);\n        ImPlot3D::SetupAxisLimits(ImAxis3D_X, t - 10.0f, t, ImPlot3DCond_Always);\n        ImPlot3D::SetupAxisLimits(ImAxis3D_Y, -400, 400, ImPlot3DCond_Once);\n        ImPlot3D::SetupAxisLimits(ImAxis3D_Z, -400, 400, ImPlot3DCond_Once);\n        ImPlot3D::PlotLine(\"Mouse\", &sdata1.Data[0], &sdata2.Data[0], &sdata3.Data[0], sdata1.Data.size(), 0, sdata1.Offset, sizeof(float));\n        ImPlot3D::EndPlot();\n    }\n}\n\nvoid DemoMarkersAndText() {\n    static float mk_size = ImPlot3D::GetStyle().MarkerSize;\n    static float mk_weight = ImPlot3D::GetStyle().MarkerWeight;\n    ImGui::DragFloat(\"Marker Size\", &mk_size, 0.1f, 2.0f, 10.0f, \"%.2f px\");\n    ImGui::DragFloat(\"Marker Weight\", &mk_weight, 0.05f, 0.5f, 3.0f, \"%.2f px\");\n\n    if (ImPlot3D::BeginPlot(\"##MarkerStyles\", ImVec2(-1, 0), ImPlot3DFlags_CanvasOnly)) {\n\n        ImPlot3D::SetupAxes(nullptr, nullptr, nullptr, ImPlot3DAxisFlags_NoDecorations, ImPlot3DAxisFlags_NoDecorations,\n                            ImPlot3DAxisFlags_NoDecorations);\n        ImPlot3D::SetupAxesLimits(-0.5, 1.5, -0.5, 1.5, 0, ImPlot3DMarker_COUNT + 1);\n\n        float xs[2] = {0, 0};\n        float ys[2] = {0, 0};\n        float zs[2] = {ImPlot3DMarker_COUNT, ImPlot3DMarker_COUNT + 1};\n\n        // Filled markers\n        for (int m = 0; m < ImPlot3DMarker_COUNT; ++m) {\n            xs[1] = xs[0] + ImCos(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5f;\n            ys[1] = ys[0] + ImSin(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5f;\n\n            ImGui::PushID(m);\n            ImPlot3D::SetNextMarkerStyle(m, mk_size, IMPLOT3D_AUTO_COL, mk_weight);\n            ImPlot3D::PlotLine(\"##Filled\", xs, ys, zs, 2);\n            ImGui::PopID();\n            zs[0]--;\n            zs[1]--;\n        }\n\n        xs[0] = 1;\n        ys[0] = 1;\n        zs[0] = ImPlot3DMarker_COUNT;\n        zs[1] = zs[0] + 1;\n\n        // Open markers\n        for (int m = 0; m < ImPlot3DMarker_COUNT; ++m) {\n            xs[1] = xs[0] + ImCos(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5f;\n            ys[1] = ys[0] - ImSin(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5f;\n\n            ImGui::PushID(m);\n            ImPlot3D::SetNextMarkerStyle(m, mk_size, ImVec4(0, 0, 0, 0), mk_weight);\n            ImPlot3D::PlotLine(\"##Open\", xs, ys, zs, 2);\n            ImGui::PopID();\n            zs[0]--;\n            zs[1]--;\n        }\n\n        ImPlot3D::PlotText(\"Filled Markers\", 0.0f, 0.0f, 6.0f);\n        ImPlot3D::PlotText(\"Open Markers\", 1.0f, 1.0f, 6.0f);\n\n        ImPlot3D::PushStyleColor(ImPlot3DCol_InlayText, ImVec4(1, 0, 1, 1));\n        ImPlot3D::PlotText(\"Rotated Text\", 0.5f, 0.5f, 6.0f, IM_PI / 4, ImVec2(0, 0));\n        ImPlot3D::PopStyleColor();\n\n        ImPlot3D::EndPlot();\n    }\n}\n\nvoid DemoNaNValues() {\n    static bool include_nan = true;\n    static ImPlot3DLineFlags flags = 0;\n\n    float data1[5] = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f};\n    float data2[5] = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f};\n    float data3[5] = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f};\n\n    if (include_nan)\n        data1[2] = NAN;\n\n    ImGui::Checkbox(\"Include NaN\", &include_nan);\n    ImGui::SameLine();\n    ImGui::CheckboxFlags(\"Skip NaN\", (unsigned int*)&flags, ImPlot3DLineFlags_SkipNaN);\n\n    if (ImPlot3D::BeginPlot(\"##NaNValues\")) {\n        ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square);\n        ImPlot3D::PlotLine(\"Line\", data1, data2, data3, 5, flags);\n        ImPlot3D::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Axes\n//-----------------------------------------------------------------------------\n\nvoid DemoBoxScale() {\n    constexpr int N = 100;\n    float xs[N], ys[N], zs[N];\n    for (int i = 0; i < N; ++i) {\n        float t = i / (float)(N - 1);\n        xs[i] = sinf(t * 2.0f * IM_PI);\n        ys[i] = cosf(t * 4.0f * IM_PI);\n        zs[i] = t * 2.0f - 1.0f;\n    }\n\n    static float scale[3] = {1.0f, 1.0f, 1.0f};\n    ImGui::SliderFloat3(\"Box Scale\", scale, 0.1f, 2.0f, \"%.2f\");\n\n    if (ImPlot3D::BeginPlot(\"##BoxScale\")) {\n        ImPlot3D::SetupBoxScale(scale[0], scale[1], scale[2]);\n        ImPlot3D::PlotLine(\"3D Curve\", xs, ys, zs, N);\n        ImPlot3D::EndPlot();\n    }\n}\n\nvoid DemoBoxRotation() {\n    float origin[2] = {0.0f, 0.0f};\n    float axis[2] = {0.0f, 1.0f};\n\n    // Sliders for rotation angles\n    static float elevation = 45.0f;\n    static float azimuth = -135.0f;\n    static bool animate = false;\n    ImGui::Text(\"Rotation\");\n    bool changed = false;\n    if (ImGui::SliderFloat(\"Elevation\", &elevation, -90.0f, 90.0f, \"%.1f degrees\"))\n        changed = true;\n    if (ImGui::SliderFloat(\"Azimuth\", &azimuth, -180.0f, 180.0f, \"%.1f degrees\"))\n        changed = true;\n    ImGui::Checkbox(\"Animate\", &animate);\n\n    ImGui::Text(\"Initial Rotation\");\n    ImGui::SameLine();\n    HelpMarker(\"The rotation will be reset to the initial rotation when you double right-click\");\n    static float init_elevation = 45.0f;\n    static float init_azimuth = -135.0f;\n    ImGui::SliderFloat(\"Initial Elevation\", &init_elevation, -90.0f, 90.0f, \"%.1f degrees\");\n    ImGui::SliderFloat(\"Initial Azimuth\", &init_azimuth, -180.0f, 180.0f, \"%.1f degrees\");\n\n    if (ImPlot3D::BeginPlot(\"##BoxRotation\")) {\n        ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1, 1, ImPlot3DCond_Always);\n\n        // Set initial rotation\n        ImPlot3D::SetupBoxInitialRotation(init_elevation, init_azimuth);\n\n        // Set the rotation using the specified elevation and azimuth\n        if (changed)\n            ImPlot3D::SetupBoxRotation(elevation, azimuth, animate, ImPlot3DCond_Always);\n\n        // Plot axis lines\n        ImPlot3D::SetNextLineStyle(ImVec4(0.8f, 0.2f, 0.2f, 1));\n        ImPlot3D::PlotLine(\"X-Axis\", axis, origin, origin, 2);\n        ImPlot3D::SetNextLineStyle(ImVec4(0.2f, 0.8f, 0.2f, 1));\n        ImPlot3D::PlotLine(\"Y-Axis\", origin, axis, origin, 2);\n        ImPlot3D::SetNextLineStyle(ImVec4(0.2f, 0.2f, 0.8f, 1));\n        ImPlot3D::PlotLine(\"Z-Axis\", origin, origin, axis, 2);\n\n        ImPlot3D::EndPlot();\n    }\n}\n\nvoid DemoTickLabels() {\n    static bool custom_ticks = true;\n    static bool custom_labels = true;\n    ImGui::Checkbox(\"Show Custom Ticks\", &custom_ticks);\n    if (custom_ticks) {\n        ImGui::SameLine();\n        ImGui::Checkbox(\"Show Custom Labels\", &custom_labels);\n    }\n    const double pi = 3.14;\n    const char* pi_str[] = {\"PI\"};\n    static double letters_ticks[] = {0.0, 0.2, 0.4, 0.6, 0.8, 1.0};\n    static const char* letters_labels[] = {\"A\", \"B\", \"C\", \"D\", \"E\", \"F\"};\n    if (ImPlot3D::BeginPlot(\"##Ticks\")) {\n        ImPlot3D::SetupAxesLimits(2, 5, 0, 1, 0, 1);\n        if (custom_ticks) {\n            ImPlot3D::SetupAxisTicks(ImAxis3D_X, &pi, 1, custom_labels ? pi_str : nullptr, true);\n            ImPlot3D::SetupAxisTicks(ImAxis3D_Y, letters_ticks, 6, custom_labels ? letters_labels : nullptr, false);\n            ImPlot3D::SetupAxisTicks(ImAxis3D_Z, 0, 1, 6, custom_labels ? letters_labels : nullptr, false);\n        }\n        ImPlot3D::EndPlot();\n    }\n}\n\nvoid DemoAxisConstraints() {\n    static float limit_constraints[2] = {-10, 10};\n    static float zoom_constraints[2] = {1, 20};\n    static ImPlot3DAxisFlags flags;\n    ImGui::DragFloat2(\"Limits Constraints\", limit_constraints, 0.01f);\n    ImGui::DragFloat2(\"Zoom Constraints\", zoom_constraints, 0.01f);\n    CHECKBOX_FLAG(flags, ImPlot3DAxisFlags_PanStretch);\n    if (ImPlot3D::BeginPlot(\"##AxisConstraints\", ImVec2(-1, 0))) {\n        ImPlot3D::SetupAxes(\"X\", \"Y\", \"Z\", flags, flags, flags);\n        ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1, 1);\n        ImPlot3D::SetupAxisLimitsConstraints(ImAxis3D_X, limit_constraints[0], limit_constraints[1]);\n        ImPlot3D::SetupAxisLimitsConstraints(ImAxis3D_Y, limit_constraints[0], limit_constraints[1]);\n        ImPlot3D::SetupAxisLimitsConstraints(ImAxis3D_Z, limit_constraints[0], limit_constraints[1]);\n        ImPlot3D::SetupAxisZoomConstraints(ImAxis3D_X, zoom_constraints[0], zoom_constraints[1]);\n        ImPlot3D::SetupAxisZoomConstraints(ImAxis3D_Y, zoom_constraints[0], zoom_constraints[1]);\n        ImPlot3D::SetupAxisZoomConstraints(ImAxis3D_Z, zoom_constraints[0], zoom_constraints[1]);\n        ImPlot3D::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Custom\n//-----------------------------------------------------------------------------\n\nvoid DemoCustomStyles() {\n    ImPlot3D::PushColormap(ImPlot3DColormap_Deep);\n    // normally you wouldn't change the entire style each frame\n    ImPlot3DStyle backup = ImPlot3D::GetStyle();\n    MyImPlot3D::StyleSeaborn();\n    if (ImPlot3D::BeginPlot(\"Seaborn Style\")) {\n        ImPlot3D::SetupAxes(\"X-axis\", \"Y-axis\", \"Z-axis\");\n        ImPlot3D::SetupAxesLimits(-0.5f, 9.5f, -0.5f, 0.5f, 0, 10);\n        unsigned int xs[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};\n        unsigned int ys[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\n        unsigned int lin[10] = {8, 8, 9, 7, 8, 8, 8, 9, 7, 8};\n        unsigned int dot[10] = {7, 6, 6, 7, 8, 5, 6, 5, 8, 7};\n        ImPlot3D::NextColormapColor(); // Skip blue\n        ImPlot3D::PlotLine(\"Line\", xs, ys, lin, 10);\n        ImPlot3D::NextColormapColor(); // Skip green\n        ImPlot3D::PlotScatter(\"Scatter\", xs, ys, dot, 10);\n        ImPlot3D::EndPlot();\n    }\n    ImPlot3D::GetStyle() = backup;\n    ImPlot3D::PopColormap();\n}\n\nvoid DemoCustomRendering() {\n    if (ImPlot3D::BeginPlot(\"##CustomRend\")) {\n        ImPlot3D::SetupAxesLimits(-0.1f, 1.1f, -0.1f, 1.1f, -0.1f, 1.1f);\n\n        // Draw circle\n        ImVec2 cntr = ImPlot3D::PlotToPixels(ImPlot3DPoint(0.5f, 0.5f, 0.5f));\n        ImPlot3D::GetPlotDrawList()->AddCircleFilled(cntr, 20, IM_COL32(255, 255, 0, 255), 20);\n\n        // Draw box\n        ImPlot3DPoint corners[8] = {\n            ImPlot3DPoint(0, 0, 0), ImPlot3DPoint(1, 0, 0), ImPlot3DPoint(1, 1, 0), ImPlot3DPoint(0, 1, 0),\n            ImPlot3DPoint(0, 0, 1), ImPlot3DPoint(1, 0, 1), ImPlot3DPoint(1, 1, 1), ImPlot3DPoint(0, 1, 1),\n        };\n        ImVec2 corners_px[8];\n        for (int i = 0; i < 8; i++)\n            corners_px[i] = ImPlot3D::PlotToPixels(corners[i]);\n\n        ImU32 col = IM_COL32(128, 0, 255, 255);\n        for (int i = 0; i < 4; i++) {\n            ImPlot3D::GetPlotDrawList()->AddLine(corners_px[i], corners_px[(i + 1) % 4], col);\n            ImPlot3D::GetPlotDrawList()->AddLine(corners_px[i + 4], corners_px[(i + 1) % 4 + 4], col);\n            ImPlot3D::GetPlotDrawList()->AddLine(corners_px[i], corners_px[i + 4], col);\n        }\n        ImPlot3D::EndPlot();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Demo Window\n//-----------------------------------------------------------------------------\n\nvoid DemoHelp() {\n    ImGui::SeparatorText(\"ABOUT THIS DEMO:\");\n    ImGui::BulletText(\"The other tabs are demonstrating many aspects of the library.\");\n\n    ImGui::SeparatorText(\"PROGRAMMER GUIDE:\");\n    ImGui::BulletText(\"See the ShowDemoWindow() code in implot3d_demo.cpp. <- you are here!\");\n    ImGui::BulletText(\"See comments in implot3d_demo.cpp.\");\n    ImGui::BulletText(\"See example application in example/ folder.\");\n\n    ImGui::SeparatorText(\"USER GUIDE:\");\n    ImGui::BulletText(\"Translation\");\n    {\n        ImGui::Indent();\n        ImGui::BulletText(\"Left-click drag to translate.\");\n        ImGui::BulletText(\"If over axis, only that axis will translate.\");\n        ImGui::BulletText(\"If over plane, only that plane will translate.\");\n        ImGui::BulletText(\"If outside plot area, translate in the view plane.\");\n        ImGui::Unindent();\n    }\n\n    ImGui::BulletText(\"Zoom\");\n    {\n        ImGui::Indent();\n        ImGui::BulletText(\"Scroll or middle-click drag to zoom.\");\n        ImGui::BulletText(\"If over axis, only that axis will zoom.\");\n        ImGui::BulletText(\"If over plane, only that plane will zoom.\");\n        ImGui::BulletText(\"If outside plot area, zoom the entire plot.\");\n        ImGui::Unindent();\n    }\n\n    ImGui::BulletText(\"Rotation\");\n    {\n        ImGui::Indent();\n        ImGui::BulletText(\"Right-click drag to rotate.\");\n        ImGui::BulletText(\"To reset rotation, double right-click outside plot area.\");\n        ImGui::BulletText(\"To rotate to plane, double right-click when over the plane.\");\n        ImGui::Unindent();\n    }\n\n    ImGui::BulletText(\"Fit data\");\n    {\n        ImGui::Indent();\n        ImGui::BulletText(\"Double left-click to fit.\");\n        ImGui::BulletText(\"If over axis, fit data to axis.\");\n        ImGui::BulletText(\"If over plane, fit data to plane.\");\n        ImGui::BulletText(\"If outside plot area, fit data to plot.\");\n        ImGui::Unindent();\n    }\n\n    ImGui::BulletText(\"Context Menus\");\n    {\n        ImGui::Indent();\n        ImGui::BulletText(\"Right-click outside plot area to show full context menu.\");\n        ImGui::BulletText(\"Right-click over legend to show legend context menu.\");\n        ImGui::BulletText(\"Right-click over axis to show axis context menu.\");\n        ImGui::BulletText(\"Right-click over plane to show plane context menu.\");\n        ImGui::Unindent();\n    }\n\n    ImGui::BulletText(\"Click legend label icons to show/hide plot items.\");\n}\n\nvoid DemoHeader(const char* label, void (*demo)()) {\n    if (ImGui::TreeNodeEx(label)) {\n        demo();\n        ImGui::TreePop();\n    }\n}\n\nvoid ShowAllDemos() {\n    ImGui::Text(\"ImPlot3D says olá! (%s)\", IMPLOT3D_VERSION);\n    ImGui::Spacing();\n    if (ImGui::BeginTabBar(\"ImPlot3DDemoTabs\")) {\n        if (ImGui::BeginTabItem(\"Plots\")) {\n            DemoHeader(\"Line Plots\", DemoLinePlots);\n            DemoHeader(\"Scatter Plots\", DemoScatterPlots);\n            DemoHeader(\"Triangle Plots\", DemoTrianglePlots);\n            DemoHeader(\"Quad Plots\", DemoQuadPlots);\n            DemoHeader(\"Surface Plots\", DemoSurfacePlots);\n            DemoHeader(\"Mesh Plots\", DemoMeshPlots);\n            DemoHeader(\"Realtime Plots\", DemoRealtimePlots);\n            DemoHeader(\"Image Plots\", DemoImagePlots);\n            DemoHeader(\"Markers and Text\", DemoMarkersAndText);\n            DemoHeader(\"NaN Values\", DemoNaNValues);\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"Axes\")) {\n            DemoHeader(\"Box Scale\", DemoBoxScale);\n            DemoHeader(\"Box Rotation\", DemoBoxRotation);\n            DemoHeader(\"Tick Labels\", DemoTickLabels);\n            DemoHeader(\"Axis Constraints\", DemoAxisConstraints);\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"Custom\")) {\n            DemoHeader(\"Custom Styles\", DemoCustomStyles);\n            DemoHeader(\"Custom Rendering\", DemoCustomRendering);\n            ImGui::EndTabItem();\n        }\n        if (ImGui::BeginTabItem(\"Help\")) {\n            DemoHelp();\n            ImGui::EndTabItem();\n        }\n        ImGui::EndTabBar();\n    }\n}\n\nvoid ShowDemoWindow(bool* p_open) {\n    static bool show_implot3d_metrics = false;\n    static bool show_implot3d_style_editor = false;\n    static bool show_imgui_metrics = false;\n    static bool show_imgui_style_editor = false;\n    static bool show_imgui_demo = false;\n\n    if (show_implot3d_metrics)\n        ImPlot3D::ShowMetricsWindow(&show_implot3d_metrics);\n    if (show_implot3d_style_editor) {\n        ImGui::Begin(\"Style Editor (ImPlot3D)\", &show_implot3d_style_editor);\n        ImPlot3D::ShowStyleEditor();\n        ImGui::End();\n    }\n    if (show_imgui_style_editor) {\n        ImGui::Begin(\"Style Editor (ImGui)\", &show_imgui_style_editor);\n        ImGui::ShowStyleEditor();\n        ImGui::End();\n    }\n    if (show_imgui_metrics)\n        ImGui::ShowMetricsWindow(&show_imgui_metrics);\n    if (show_imgui_demo)\n        ImGui::ShowDemoWindow(&show_imgui_demo);\n\n    ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver);\n    ImGui::SetNextWindowSize(ImVec2(600, 750), ImGuiCond_FirstUseEver);\n    ImGui::Begin(\"ImPlot3D Demo\", p_open, ImGuiWindowFlags_MenuBar);\n    if (ImGui::BeginMenuBar()) {\n        if (ImGui::BeginMenu(\"Tools\")) {\n            ImGui::MenuItem(\"Metrics\", nullptr, &show_implot3d_metrics);\n            ImGui::MenuItem(\"Style Editor\", nullptr, &show_implot3d_style_editor);\n            ImGui::Separator();\n            ImGui::MenuItem(\"ImGui Metrics\", nullptr, &show_imgui_metrics);\n            ImGui::MenuItem(\"ImGui Style Editor\", nullptr, &show_imgui_style_editor);\n            ImGui::MenuItem(\"ImGui Demo\", nullptr, &show_imgui_demo);\n            ImGui::EndMenu();\n        }\n        ImGui::EndMenuBar();\n    }\n    ShowAllDemos();\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Style Editor\n//-----------------------------------------------------------------------------\n\nbool ShowStyleSelector(const char* label) {\n    static int style_idx = -1;\n    if (ImGui::Combo(label, &style_idx, \"Auto\\0Classic\\0Dark\\0Light\\0\")) {\n        switch (style_idx) {\n            case 0: StyleColorsAuto(); break;\n            case 1: StyleColorsClassic(); break;\n            case 2: StyleColorsDark(); break;\n            case 3: StyleColorsLight(); break;\n        }\n        return true;\n    }\n    return false;\n}\n\nbool ColormapButton(const char* label, const ImVec2& size_arg, ImPlot3DColormap cmap) {\n    ImGuiContext& G = *GImGui;\n    const ImGuiStyle& style = G.Style;\n    ImGuiWindow* Window = G.CurrentWindow;\n    if (Window->SkipItems)\n        return false;\n    ImPlot3DContext& gp = *GImPlot3D;\n    cmap = cmap == IMPLOT3D_AUTO ? gp.Style.Colormap : cmap;\n    IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, \"Invalid colormap index!\");\n    const ImU32* keys = gp.ColormapData.GetKeys(cmap);\n    const int count = gp.ColormapData.GetKeyCount(cmap);\n    const bool qual = gp.ColormapData.IsQual(cmap);\n    const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos;\n    const ImVec2 label_size = ImGui::CalcTextSize(label, nullptr, true);\n    ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);\n    const ImRect rect = ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y);\n    RenderColorBar(keys, count, *ImGui::GetWindowDrawList(), rect, false, false, !qual);\n    const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap, G.Style.ButtonTextAlign.x));\n    ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32_BLACK_TRANS);\n    ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1, 1, 1, 0.1f));\n    ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1, 1, 1, 0.2f));\n    ImGui::PushStyleColor(ImGuiCol_Text, text);\n    ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0);\n    const bool pressed = ImGui::Button(label, size);\n    ImGui::PopStyleColor(4);\n    ImGui::PopStyleVar(1);\n    return pressed;\n}\n\nvoid ShowStyleEditor(ImPlot3DStyle* ref) {\n    ImPlot3DContext& gp = *GImPlot3D;\n\n    // Handle style internal storage\n    ImPlot3DStyle& style = GetStyle();\n    static ImPlot3DStyle ref_saved_style;\n    static bool init = true;\n    if (init && ref == nullptr)\n        ref_saved_style = style;\n    init = false;\n    if (ref == nullptr)\n        ref = &ref_saved_style;\n\n    // Handle flash style color\n    static float flash_color_time = 0.5f;\n    static ImPlot3DCol flash_color_idx = ImPlot3DCol_COUNT;\n    static ImVec4 flash_color_backup = ImVec4(0, 0, 0, 0);\n    if (flash_color_idx != ImPlot3DCol_COUNT) {\n        // Flash color\n        ImVec4& color = style.Colors[flash_color_idx];\n        ImGui::ColorConvertHSVtoRGB(ImCos(flash_color_time * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, color.x, color.y, color.z);\n        color.w = 1.0f;\n\n        // Decrease timer until zero\n        if ((flash_color_time -= ImGui::GetIO().DeltaTime) <= 0.0f) {\n            // When timer reaches zero, restore the backup color\n            style.Colors[flash_color_idx] = flash_color_backup;\n            flash_color_idx = ImPlot3DCol_COUNT;\n            flash_color_time = 0.5f;\n        }\n    }\n\n    // Style selector\n    if (ImPlot3D::ShowStyleSelector(\"Colors##Selector\"))\n        ref_saved_style = style;\n\n    // Save/Revert button\n    if (ImGui::Button(\"Save Ref\"))\n        *ref = ref_saved_style = style;\n    ImGui::SameLine();\n    if (ImGui::Button(\"Revert Ref\"))\n        style = *ref;\n    ImGui::SameLine();\n    HelpMarker(\"Save/Revert in local non-persistent storage. Default Colors definition are not affected. \"\n               \"Use \\\"Export\\\" below to save them somewhere.\");\n\n    ImGui::Separator();\n\n    if (ImGui::BeginTabBar(\"##Tabs\", ImGuiTabBarFlags_None)) {\n        if (ImGui::BeginTabItem(\"Variables\")) {\n            ImGui::Text(\"Item Styling\");\n            ImGui::SliderFloat(\"LineWeight\", &style.LineWeight, 0.0f, 5.0f, \"%.1f\");\n            ImGui::SliderFloat(\"MarkerSize\", &style.MarkerSize, 2.0f, 10.0f, \"%.1f\");\n            ImGui::SliderFloat(\"MarkerWeight\", &style.MarkerWeight, 0.0f, 5.0f, \"%.1f\");\n            ImGui::SliderFloat(\"FillAlpha\", &style.FillAlpha, 0.0f, 1.0f, \"%.2f\");\n            ImGui::Text(\"Plot Styling\");\n            ImGui::SliderFloat2(\"PlotDefaultSize\", (float*)&style.PlotDefaultSize, 0.0f, 1000, \"%.0f\");\n            ImGui::SliderFloat2(\"PlotMinSize\", (float*)&style.PlotMinSize, 0.0f, 300, \"%.0f\");\n            ImGui::SliderFloat2(\"PlotPadding\", (float*)&style.PlotPadding, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"LabelPadding\", (float*)&style.LabelPadding, 0.0f, 20.0f, \"%.0f\");\n            ImGui::Text(\"Legend Styling\");\n            ImGui::SliderFloat2(\"LegendPadding\", (float*)&style.LegendPadding, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"LegendInnerPadding\", (float*)&style.LegendInnerPadding, 0.0f, 10.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"LegendSpacing\", (float*)&style.LegendSpacing, 0.0f, 5.0f, \"%.0f\");\n            ImGui::EndTabItem();\n        }\n\n        if (ImGui::BeginTabItem(\"Colors\")) {\n            static int output_dest = 0;\n            static bool output_only_modified = true;\n            if (ImGui::Button(\"Export\")) {\n                if (output_dest == 0)\n                    ImGui::LogToClipboard();\n                else\n                    ImGui::LogToTTY();\n                ImGui::LogText(\"ImVec4* colors = ImPlot3D::GetStyle().Colors;\\n\");\n                for (int i = 0; i < ImPlot3DCol_COUNT; i++) {\n                    const ImVec4& col = style.Colors[i];\n                    const char* name = ImPlot3D::GetStyleColorName(i);\n                    if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)\n                        ImGui::LogText(\"colors[ImPlot3DCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\\n\", name, 15 - (int)strlen(name), \"\", col.x,\n                                       col.y, col.z, col.w);\n                }\n                ImGui::LogFinish();\n            }\n            ImGui::SameLine();\n            ImGui::SetNextItemWidth(120);\n            ImGui::Combo(\"##output_type\", &output_dest, \"To Clipboard\\0To TTY\\0\");\n            ImGui::SameLine();\n            ImGui::Checkbox(\"Only Modified Colors\", &output_only_modified);\n\n            static ImGuiTextFilter filter;\n            filter.Draw(\"Filter colors\", ImGui::GetFontSize() * 16);\n\n            static ImGuiColorEditFlags alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf;\n#if IMGUI_VERSION_NUM < 19173\n            if (ImGui::RadioButton(\"Opaque\", alpha_flags == ImGuiColorEditFlags_None))\n                alpha_flags = ImGuiColorEditFlags_None;\n            ImGui::SameLine();\n            if (ImGui::RadioButton(\"Alpha\", alpha_flags == ImGuiColorEditFlags_AlphaPreview))\n                alpha_flags = ImGuiColorEditFlags_AlphaPreview;\n            ImGui::SameLine();\n            if (ImGui::RadioButton(\"Both\", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf))\n                alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf;\n            ImGui::SameLine();\n#else\n            if (ImGui::RadioButton(\"Opaque\", alpha_flags == ImGuiColorEditFlags_AlphaOpaque))\n                alpha_flags = ImGuiColorEditFlags_AlphaOpaque;\n            ImGui::SameLine();\n            if (ImGui::RadioButton(\"Alpha\", alpha_flags == ImGuiColorEditFlags_None))\n                alpha_flags = ImGuiColorEditFlags_None;\n            ImGui::SameLine();\n            if (ImGui::RadioButton(\"Both\", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf))\n                alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf;\n            ImGui::SameLine();\n#endif\n            HelpMarker(\"In the color list:\\n\"\n                       \"Left-click on color square to open color picker,\\n\"\n                       \"Right-click to open edit options menu.\");\n\n            ImGui::Separator();\n\n            for (int i = 0; i < ImPlot3DCol_COUNT; i++) {\n                const char* name = ImPlot3D::GetStyleColorName(i);\n                if (!filter.PassFilter(name))\n                    continue;\n                ImGui::PushID(i);\n\n                // Flash color\n                if (ImGui::Button(\"?\")) {\n                    if (flash_color_idx != ImPlot3DCol_COUNT)\n                        style.Colors[flash_color_idx] = flash_color_backup;\n                    flash_color_time = 0.5f;\n                    flash_color_idx = (ImPlot3DCol)i;\n                    flash_color_backup = style.Colors[i];\n                }\n                ImGui::SetItemTooltip(\"Flash given color to identify places where it is used.\");\n                ImGui::SameLine();\n\n                // Handle auto color selection\n                const bool is_auto = IsColorAuto(style.Colors[i]);\n                if (is_auto)\n                    ImGui::BeginDisabled();\n                if (ImGui::Button(\"Auto\"))\n                    style.Colors[i] = IMPLOT3D_AUTO_COL;\n                if (is_auto)\n                    ImGui::EndDisabled();\n\n                // Color selection\n                ImGui::SameLine();\n                if (ImGui::ColorEdit4(\"##Color\", (float*)&style.Colors[i], ImGuiColorEditFlags_NoInputs | alpha_flags)) {\n                    if (style.Colors[i].w == -1)\n                        style.Colors[i].w = 1;\n                }\n\n                // Save/Revert buttons if color changed\n                if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) {\n                    ImGui::SameLine();\n                    if (ImGui::Button(\"Save\"))\n                        ref->Colors[i] = style.Colors[i];\n                    ImGui::SameLine();\n                    if (ImGui::Button(\"Revert\"))\n                        style.Colors[i] = ref->Colors[i];\n                }\n                ImGui::SameLine();\n                ImGui::TextUnformatted(name);\n                ImGui::PopID();\n            }\n            ImGui::EndTabItem();\n        }\n\n        if (ImGui::BeginTabItem(\"Colormaps\")) {\n            static int output_dest = 0;\n            if (ImGui::Button(\"Export\", ImVec2(75, 0))) {\n                if (output_dest == 0)\n                    ImGui::LogToClipboard();\n                else\n                    ImGui::LogToTTY();\n                int size = GetColormapSize();\n                const char* name = GetColormapName(gp.Style.Colormap);\n                ImGui::LogText(\"static const ImU32 %s_Data[%d] = {\\n\", name, size);\n                for (int i = 0; i < size; ++i) {\n                    ImU32 col = GetColormapColorU32(i, gp.Style.Colormap);\n                    ImGui::LogText(\"    %u%s\\n\", col, i == size - 1 ? \"\" : \",\");\n                }\n                ImGui::LogText(\"};\\nImPlotColormap %s = ImPlot::AddColormap(\\\"%s\\\", %s_Data, %d);\", name, name, name, size);\n                ImGui::LogFinish();\n            }\n            ImGui::SameLine();\n            ImGui::SetNextItemWidth(120);\n            ImGui::Combo(\"##output_type\", &output_dest, \"To Clipboard\\0To TTY\\0\");\n            ImGui::SameLine();\n            static bool edit = false;\n            ImGui::Checkbox(\"Edit Mode\", &edit);\n\n            // built-in/added\n            ImGui::Separator();\n            for (int i = 0; i < gp.ColormapData.Count; ++i) {\n                ImGui::PushID(i);\n                int size = gp.ColormapData.GetKeyCount(i);\n                bool selected = i == gp.Style.Colormap;\n\n                const char* name = GetColormapName(i);\n                if (!selected)\n                    ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f);\n                if (ImGui::Button(name, ImVec2(100, 0))) {\n                    gp.Style.Colormap = i;\n                    BustItemCache();\n                }\n                if (!selected)\n                    ImGui::PopStyleVar();\n                ImGui::SameLine();\n                ImGui::BeginGroup();\n                if (edit) {\n                    for (int c = 0; c < size; ++c) {\n                        ImGui::PushID(c);\n                        ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(i, c));\n                        if (ImGui::ColorEdit4(\"\", &col4.x, ImGuiColorEditFlags_NoInputs)) {\n                            ImU32 col32 = ImGui::ColorConvertFloat4ToU32(col4);\n                            gp.ColormapData.SetKeyColor(i, c, col32);\n                            BustItemCache();\n                        }\n                        if ((c + 1) % 12 != 0 && c != size - 1)\n                            ImGui::SameLine();\n                        ImGui::PopID();\n                    }\n                } else {\n                    if (ColormapButton(\"##\", ImVec2(-1, 0), i))\n                        edit = true;\n                }\n                ImGui::EndGroup();\n                ImGui::PopID();\n            }\n\n            static ImVector<ImVec4> custom;\n            if (custom.Size == 0) {\n                custom.push_back(ImVec4(1, 0, 0, 1));\n                custom.push_back(ImVec4(0, 1, 0, 1));\n                custom.push_back(ImVec4(0, 0, 1, 1));\n            }\n            ImGui::Separator();\n            ImGui::BeginGroup();\n            static char name[16] = \"MyColormap\";\n\n            if (ImGui::Button(\"+\", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x) / 2, 0)))\n                custom.push_back(ImVec4(0, 0, 0, 1));\n            ImGui::SameLine();\n            if (ImGui::Button(\"-\", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x) / 2, 0)) && custom.Size > 2)\n                custom.pop_back();\n            ImGui::SetNextItemWidth(100);\n            ImGui::InputText(\"##Name\", name, 16, ImGuiInputTextFlags_CharsNoBlank);\n            static bool qual = true;\n            ImGui::Checkbox(\"Qualitative\", &qual);\n            if (ImGui::Button(\"Add\", ImVec2(100, 0)) && gp.ColormapData.GetIndex(name) == -1)\n                AddColormap(name, custom.Data, custom.Size, qual);\n\n            ImGui::EndGroup();\n            ImGui::SameLine();\n            ImGui::BeginGroup();\n            for (int c = 0; c < custom.Size; ++c) {\n                ImGui::PushID(c);\n                if (ImGui::ColorEdit4(\"##Col1\", &custom[c].x, ImGuiColorEditFlags_NoInputs)) {\n                }\n                if ((c + 1) % 12 != 0)\n                    ImGui::SameLine();\n                ImGui::PopID();\n            }\n            ImGui::EndGroup();\n\n            ImGui::EndTabItem();\n        }\n\n        ImGui::EndTabBar();\n    }\n}\n\n} // namespace ImPlot3D\n\n//-----------------------------------------------------------------------------\n// [SECTION] User Namespace Implementation\n//-----------------------------------------------------------------------------\n\nnamespace MyImPlot3D {\n\nvoid StyleSeaborn() {\n\n    ImPlot3DStyle& style = ImPlot3D::GetStyle();\n\n    ImVec4* colors = style.Colors;\n    colors[ImPlot3DCol_Line] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_Fill] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_MarkerOutline] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_MarkerFill] = IMPLOT3D_AUTO_COL;\n    colors[ImPlot3DCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImPlot3DCol_PlotBg] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f);\n    colors[ImPlot3DCol_PlotBorder] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImPlot3DCol_LegendBg] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f);\n    colors[ImPlot3DCol_LegendBorder] = ImVec4(0.80f, 0.81f, 0.85f, 1.00f);\n    colors[ImPlot3DCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlot3DCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlot3DCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlot3DCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImPlot3DCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n\n    style.LineWeight = 1.5;\n    style.Marker = ImPlot3DMarker_None;\n    style.MarkerSize = 4;\n    style.MarkerWeight = 1;\n    style.FillAlpha = 1.0f;\n    style.PlotPadding = ImVec2(12, 12);\n    style.LabelPadding = ImVec2(5, 5);\n    style.LegendPadding = ImVec2(5, 5);\n    style.PlotMinSize = ImVec2(300, 225);\n}\n\n} // namespace MyImPlot3D\n"
  },
  {
    "path": "lib/third_party/imgui/implot3d/source/implot3d_items.cpp",
    "content": "//--------------------------------------------------\n// ImPlot3D v0.3 WIP\n// implot3d_items.cpp\n// Date: 2024-11-26\n// Author: Breno Cunha Queiroz (brenocq.com)\n//\n// Acknowledgments:\n//  ImPlot3D is heavily inspired by ImPlot\n//  (https://github.com/epezent/implot) by Evan Pezent,\n//  and follows a similar code style and structure to\n//  maintain consistency with ImPlot's API.\n//--------------------------------------------------\n\n// Table of Contents:\n// [SECTION] Includes\n// [SECTION] Macros & Defines\n// [SECTION] Template instantiation utility\n// [SECTION] Item Utils\n// [SECTION] Draw Utils\n// [SECTION] Renderers\n// [SECTION] Indexers\n// [SECTION] Getters\n// [SECTION] RenderPrimitives\n// [SECTION] Markers\n// [SECTION] PlotScatter\n// [SECTION] PlotLine\n// [SECTION] PlotTriangle\n// [SECTION] PlotQuad\n// [SECTION] PlotSurface\n// [SECTION] PlotMesh\n// [SECTION] PlotImage\n// [SECTION] PlotText\n\n//-----------------------------------------------------------------------------\n// [SECTION] Includes\n//-----------------------------------------------------------------------------\n\n#ifndef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS\n#endif\n\n#include \"implot3d.h\"\n#include \"implot3d_internal.h\"\n\n#ifndef IMGUI_DISABLE\n\n//-----------------------------------------------------------------------------\n// [SECTION] Macros & Defines\n//-----------------------------------------------------------------------------\n\n#define SQRT_1_2 0.70710678118f\n#define SQRT_3_2 0.86602540378f\n\n// clang-format off\n#ifndef IMPLOT3D_NO_FORCE_INLINE\n    #ifdef _MSC_VER\n        #define IMPLOT3D_INLINE __forceinline\n    #elif defined(__GNUC__)\n        #define IMPLOT3D_INLINE inline __attribute__((__always_inline__))\n    #elif defined(__CLANG__)\n        #if __has_attribute(__always_inline__)\n            #define IMPLOT3D_INLINE inline __attribute__((__always_inline__))\n        #else\n            #define IMPLOT3D_INLINE inline\n        #endif\n    #else\n        #define IMPLOT3D_INLINE inline\n    #endif\n#else\n    #define IMPLOT3D_INLINE inline\n#endif\n// clang-format on\n\n#define IMPLOT3D_NORMALIZE2F(VX, VY)                                                                                                                 \\\n    do {                                                                                                                                             \\\n        float d2 = VX * VX + VY * VY;                                                                                                                \\\n        if (d2 > 0.0f) {                                                                                                                             \\\n            float inv_len = ImRsqrt(d2);                                                                                                             \\\n            VX *= inv_len;                                                                                                                           \\\n            VY *= inv_len;                                                                                                                           \\\n        }                                                                                                                                            \\\n    } while (0)\n\nIMPLOT3D_INLINE void GetLineRenderProps(const ImDrawList3D& draw_list_3d, float& half_weight, ImVec2& tex_uv0, ImVec2& tex_uv1) {\n    const bool aa = ImPlot3D::ImHasFlag(draw_list_3d._Flags, ImDrawListFlags_AntiAliasedLines) &&\n                    ImPlot3D::ImHasFlag(draw_list_3d._Flags, ImDrawListFlags_AntiAliasedLinesUseTex);\n    if (aa) {\n        ImVec4 tex_uvs = draw_list_3d._SharedData->TexUvLines[(int)(half_weight * 2)];\n        tex_uv0 = ImVec2(tex_uvs.x, tex_uvs.y);\n        tex_uv1 = ImVec2(tex_uvs.z, tex_uvs.w);\n        half_weight += 1;\n    } else {\n        tex_uv0 = tex_uv1 = draw_list_3d._SharedData->TexUvWhitePixel;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Template instantiation utility\n//-----------------------------------------------------------------------------\n\n// By default, templates are instantiated for `float`, `double`, and for the following integer types, which are defined in imgui.h:\n//     signed char         ImS8;   // 8-bit signed integer\n//     unsigned char       ImU8;   // 8-bit unsigned integer\n//     signed short        ImS16;  // 16-bit signed integer\n//     unsigned short      ImU16;  // 16-bit unsigned integer\n//     signed int          ImS32;  // 32-bit signed integer == int\n//     unsigned int        ImU32;  // 32-bit unsigned integer\n//     signed   long long  ImS64;  // 64-bit signed integer\n//     unsigned long long  ImU64;  // 64-bit unsigned integer\n// (note: this list does *not* include `long`, `unsigned long` and `long double`)\n//\n// You can customize the supported types by defining IMPLOT3D_CUSTOM_NUMERIC_TYPES at compile time to define your own type list.\n//    As an example, you could use the compile time define given by the line below in order to support only float and double.\n//        -DIMPLOT3D_CUSTOM_NUMERIC_TYPES=\"(float)(double)\"\n//    In order to support all known C++ types, use:\n//        -DIMPLOT3D_CUSTOM_NUMERIC_TYPES=\"(signed char)(unsigned char)(signed short)(unsigned short)(signed int)(unsigned int)(signed long)(unsigned\n//        long)(signed long long)(unsigned long long)(float)(double)(long double)\"\n\n#ifdef IMPLOT3D_CUSTOM_NUMERIC_TYPES\n#define IMPLOT3D_NUMERIC_TYPES IMPLOT3D_CUSTOM_NUMERIC_TYPES\n#else\n#define IMPLOT3D_NUMERIC_TYPES (ImS8)(ImU8)(ImS16)(ImU16)(ImS32)(ImU32)(ImS64)(ImU64)(float)(double)\n#endif\n\n// CALL_INSTANTIATE_FOR_NUMERIC_TYPES will duplicate the template instantiation code `INSTANTIATE_MACRO(T)` on supported types.\n#define _CAT(x, y) _CAT_(x, y)\n#define _CAT_(x, y) x##y\n#define _INSTANTIATE_FOR_NUMERIC_TYPES(chain) _CAT(_INSTANTIATE_FOR_NUMERIC_TYPES_1 chain, _END)\n#define _INSTANTIATE_FOR_NUMERIC_TYPES_1(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_2\n#define _INSTANTIATE_FOR_NUMERIC_TYPES_2(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_1\n#define _INSTANTIATE_FOR_NUMERIC_TYPES_1_END\n#define _INSTANTIATE_FOR_NUMERIC_TYPES_2_END\n#define CALL_INSTANTIATE_FOR_NUMERIC_TYPES() _INSTANTIATE_FOR_NUMERIC_TYPES(IMPLOT3D_NUMERIC_TYPES)\n\n//-----------------------------------------------------------------------------\n// [SECTION] Item Utils\n//-----------------------------------------------------------------------------\nnamespace ImPlot3D {\n\nstatic const float ITEM_HIGHLIGHT_LINE_SCALE = 2.0f;\nstatic const float ITEM_HIGHLIGHT_MARK_SCALE = 1.25f;\n\nbool BeginItem(const char* label_id, ImPlot3DItemFlags flags, ImPlot3DCol recolor_from) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PlotX() needs to be called between BeginPlot() and EndPlot()!\");\n\n    // Lock setup\n    SetupLock();\n\n    ImPlot3DStyle& style = gp.Style;\n    ImPlot3DNextItemData& n = gp.NextItemData;\n\n    // Register item\n    bool just_created;\n    ImPlot3DItem* item = RegisterOrGetItem(label_id, flags, &just_created);\n    // Set current item\n    gp.CurrentItem = item;\n\n    // Set/override item color\n    if (recolor_from != -1) {\n        if (!IsColorAuto(n.Colors[recolor_from]))\n            item->Color = ImGui::ColorConvertFloat4ToU32(n.Colors[recolor_from]);\n        else if (!IsColorAuto(gp.Style.Colors[recolor_from]))\n            item->Color = ImGui::ColorConvertFloat4ToU32(gp.Style.Colors[recolor_from]);\n        else if (just_created)\n            item->Color = NextColormapColorU32();\n    } else if (just_created) {\n        item->Color = NextColormapColorU32();\n    }\n\n    // Set next item color\n    ImVec4 item_color = ImGui::ColorConvertU32ToFloat4(item->Color);\n    n.IsAutoLine = IsColorAuto(n.Colors[ImPlot3DCol_Line]) && IsColorAuto(ImPlot3DCol_Line);\n    n.IsAutoFill = IsColorAuto(n.Colors[ImPlot3DCol_Fill]) && IsColorAuto(ImPlot3DCol_Fill);\n    n.Colors[ImPlot3DCol_Line] = IsColorAuto(n.Colors[ImPlot3DCol_Line])\n                                     ? (IsColorAuto(ImPlot3DCol_Line) ? item_color : gp.Style.Colors[ImPlot3DCol_Line])\n                                     : n.Colors[ImPlot3DCol_Line];\n    n.Colors[ImPlot3DCol_Fill] = IsColorAuto(n.Colors[ImPlot3DCol_Fill])\n                                     ? (IsColorAuto(ImPlot3DCol_Fill) ? item_color : gp.Style.Colors[ImPlot3DCol_Fill])\n                                     : n.Colors[ImPlot3DCol_Fill];\n    n.Colors[ImPlot3DCol_MarkerOutline] =\n        IsColorAuto(n.Colors[ImPlot3DCol_MarkerOutline])\n            ? (IsColorAuto(ImPlot3DCol_MarkerOutline) ? n.Colors[ImPlot3DCol_Line] : gp.Style.Colors[ImPlot3DCol_MarkerOutline])\n            : n.Colors[ImPlot3DCol_MarkerOutline];\n    n.Colors[ImPlot3DCol_MarkerFill] =\n        IsColorAuto(n.Colors[ImPlot3DCol_MarkerFill])\n            ? (IsColorAuto(ImPlot3DCol_MarkerFill) ? n.Colors[ImPlot3DCol_Line] : gp.Style.Colors[ImPlot3DCol_MarkerFill])\n            : n.Colors[ImPlot3DCol_MarkerFill];\n\n    // Set size & weight\n    n.LineWeight = n.LineWeight < 0.0f ? style.LineWeight : n.LineWeight;\n    n.Marker = n.Marker < 0 ? style.Marker : n.Marker;\n    n.MarkerSize = n.MarkerSize < 0.0f ? style.MarkerSize : n.MarkerSize;\n    n.MarkerWeight = n.MarkerWeight < 0.0f ? style.MarkerWeight : n.MarkerWeight;\n    n.FillAlpha = n.FillAlpha < 0 ? gp.Style.FillAlpha : n.FillAlpha;\n\n    // Apply alpha modifiers\n    n.Colors[ImPlot3DCol_Fill].w *= n.FillAlpha;\n    n.Colors[ImPlot3DCol_MarkerFill].w *= n.FillAlpha;\n\n    // Set render flags\n    n.RenderLine = n.Colors[ImPlot3DCol_Line].w > 0 && n.LineWeight > 0;\n    n.RenderFill = n.Colors[ImPlot3DCol_Fill].w > 0;\n    n.RenderMarkerFill = n.Colors[ImPlot3DCol_MarkerFill].w > 0;\n    n.RenderMarkerLine = n.Colors[ImPlot3DCol_MarkerOutline].w > 0 && n.MarkerWeight > 0;\n\n    // Don't render if item is hidden\n    if (!item->Show) {\n        EndItem();\n        return false;\n    } else {\n        // Legend hover highlight\n        if (item->LegendHovered) {\n            if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlot3DLegendFlags_NoHighlightItem)) {\n                n.LineWeight *= ITEM_HIGHLIGHT_LINE_SCALE;\n                n.MarkerSize *= ITEM_HIGHLIGHT_MARK_SCALE;\n                n.MarkerWeight *= ITEM_HIGHLIGHT_LINE_SCALE;\n            }\n        }\n    }\n\n    return true;\n}\n\ntemplate <typename _Getter>\nbool BeginItemEx(const char* label_id, const _Getter& getter, ImPlot3DItemFlags flags = 0, ImPlot3DCol recolor_from = IMPLOT3D_AUTO) {\n    if (BeginItem(label_id, flags, recolor_from)) {\n        ImPlot3DContext& gp = *GImPlot3D;\n        ImPlot3DPlot& plot = *gp.CurrentPlot;\n        if (plot.FitThisFrame && !ImHasFlag(flags, ImPlot3DItemFlags_NoFit)) {\n            for (int i = 0; i < getter.Count; i++)\n                plot.ExtendFit(getter(i));\n        }\n        return true;\n    }\n    return false;\n}\n\nvoid EndItem() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    gp.NextItemData.Reset();\n    gp.CurrentItem = nullptr;\n}\n\nImPlot3DItem* RegisterOrGetItem(const char* label_id, ImPlot3DItemFlags flags, bool* just_created) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    ImPlot3DItemGroup& Items = *gp.CurrentItems;\n    ImGuiID id = Items.GetItemID(label_id);\n    if (just_created != nullptr)\n        *just_created = Items.GetItem(id) == nullptr;\n    ImPlot3DItem* item = Items.GetOrAddItem(id);\n\n    // Avoid re-adding the same item to the legend (the legend is reset every frame)\n    if (item->SeenThisFrame)\n        return item;\n    item->SeenThisFrame = true;\n\n    // Add item to the legend\n    int idx = Items.GetItemIndex(item);\n    item->ID = id;\n    if (!ImHasFlag(flags, ImPlot3DItemFlags_NoLegend) && ImGui::FindRenderedTextEnd(label_id, nullptr) != label_id) {\n        Items.Legend.Indices.push_back(idx);\n        item->NameOffset = Items.Legend.Labels.size();\n        Items.Legend.Labels.append(label_id, label_id + strlen(label_id) + 1);\n    }\n    return item;\n}\n\nImPlot3DItem* GetCurrentItem() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    return gp.CurrentItem;\n}\n\nvoid BustItemCache() {\n    ImPlot3DContext& gp = *GImPlot3D;\n    for (int p = 0; p < gp.Plots.GetBufSize(); ++p) {\n        ImPlot3DPlot& plot = *gp.Plots.GetByIndex(p);\n        plot.Items.Reset();\n    }\n}\n\nvoid SetNextLineStyle(const ImVec4& col, float weight) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    ImPlot3DNextItemData& n = gp.NextItemData;\n    n.Colors[ImPlot3DCol_Line] = col;\n    n.LineWeight = weight;\n}\n\nvoid SetNextFillStyle(const ImVec4& col, float alpha) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    ImPlot3DNextItemData& n = gp.NextItemData;\n    n.Colors[ImPlot3DCol_Fill] = col;\n    n.FillAlpha = alpha;\n}\n\nvoid SetNextMarkerStyle(ImPlot3DMarker marker, float size, const ImVec4& fill, float weight, const ImVec4& outline) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    ImPlot3DNextItemData& n = gp.NextItemData;\n    n.Marker = marker;\n    n.Colors[ImPlot3DCol_MarkerFill] = fill;\n    n.MarkerSize = size;\n    n.Colors[ImPlot3DCol_MarkerOutline] = outline;\n    n.MarkerWeight = weight;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Draw Utils\n//-----------------------------------------------------------------------------\n\nIMPLOT3D_INLINE void PrimLine(ImDrawList3D& draw_list_3d, const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, const ImVec2& tex_uv0,\n                              const ImVec2& tex_uv1, float z) {\n    float dx = P2.x - P1.x;\n    float dy = P2.y - P1.y;\n    IMPLOT3D_NORMALIZE2F(dx, dy);\n    dx *= half_weight;\n    dy *= half_weight;\n    draw_list_3d._VtxWritePtr[0].pos.x = P1.x + dy;\n    draw_list_3d._VtxWritePtr[0].pos.y = P1.y - dx;\n    draw_list_3d._VtxWritePtr[0].uv = tex_uv0;\n    draw_list_3d._VtxWritePtr[0].col = col;\n    draw_list_3d._VtxWritePtr[1].pos.x = P2.x + dy;\n    draw_list_3d._VtxWritePtr[1].pos.y = P2.y - dx;\n    draw_list_3d._VtxWritePtr[1].uv = tex_uv0;\n    draw_list_3d._VtxWritePtr[1].col = col;\n    draw_list_3d._VtxWritePtr[2].pos.x = P2.x - dy;\n    draw_list_3d._VtxWritePtr[2].pos.y = P2.y + dx;\n    draw_list_3d._VtxWritePtr[2].uv = tex_uv1;\n    draw_list_3d._VtxWritePtr[2].col = col;\n    draw_list_3d._VtxWritePtr[3].pos.x = P1.x - dy;\n    draw_list_3d._VtxWritePtr[3].pos.y = P1.y + dx;\n    draw_list_3d._VtxWritePtr[3].uv = tex_uv1;\n    draw_list_3d._VtxWritePtr[3].col = col;\n    draw_list_3d._VtxWritePtr += 4;\n    draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);\n    draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 1);\n    draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2);\n    draw_list_3d._IdxWritePtr[3] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);\n    draw_list_3d._IdxWritePtr[4] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2);\n    draw_list_3d._IdxWritePtr[5] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 3);\n    draw_list_3d._IdxWritePtr += 6;\n    draw_list_3d._VtxCurrentIdx += 4;\n    draw_list_3d._ZWritePtr[0] = z;\n    draw_list_3d._ZWritePtr[1] = z;\n    draw_list_3d._ZWritePtr += 2;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Renderers\n//-----------------------------------------------------------------------------\n\nfloat GetPointDepth(ImPlot3DPoint p) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n\n    // Adjust for inverted axes before rotation\n    if (ImHasFlag(plot.Axes[0].Flags, ImPlot3DAxisFlags_Invert))\n        p.x = -p.x;\n    if (ImHasFlag(plot.Axes[1].Flags, ImPlot3DAxisFlags_Invert))\n        p.y = -p.y;\n    if (ImHasFlag(plot.Axes[2].Flags, ImPlot3DAxisFlags_Invert))\n        p.z = -p.z;\n\n    ImPlot3DPoint p_rot = plot.Rotation * p;\n    return p_rot.z;\n}\n\nstruct RendererBase {\n    RendererBase(int prims, int idx_consumed, int vtx_consumed) : Prims(prims), IdxConsumed(idx_consumed), VtxConsumed(vtx_consumed) {}\n    const unsigned int Prims;       // Number of primitives to render\n    const unsigned int IdxConsumed; // Number of indices consumed per primitive\n    const unsigned int VtxConsumed; // Number of vertices consumed per primitive\n};\n\ntemplate <class _Getter> struct RendererMarkersFill : RendererBase {\n    RendererMarkersFill(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col)\n        : RendererBase(getter.Count, (count - 2) * 3, count), Getter(getter), Marker(marker), Count(count), Size(size), Col(col) {}\n\n    void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; }\n\n    IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {\n        ImPlot3DPoint p_plot = Getter(prim);\n        if (!cull_box.Contains(p_plot))\n            return false;\n        ImVec2 p = PlotToPixels(p_plot);\n        // 3 vertices per triangle\n        for (int i = 0; i < Count; i++) {\n            draw_list_3d._VtxWritePtr[0].pos.x = p.x + Marker[i].x * Size;\n            draw_list_3d._VtxWritePtr[0].pos.y = p.y + Marker[i].y * Size;\n            draw_list_3d._VtxWritePtr[0].uv = UV;\n            draw_list_3d._VtxWritePtr[0].col = Col;\n            draw_list_3d._VtxWritePtr++;\n        }\n        // 3 indices per triangle\n        for (int i = 2; i < Count; i++) {\n            // Indices\n            draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);\n            draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + i - 1);\n            draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + i);\n            draw_list_3d._IdxWritePtr += 3;\n            // Z\n            draw_list_3d._ZWritePtr[0] = GetPointDepth(p_plot);\n            draw_list_3d._ZWritePtr++;\n        }\n        // Update vertex count\n        draw_list_3d._VtxCurrentIdx += (ImDrawIdx)Count;\n        return true;\n    }\n    const _Getter& Getter;\n    const ImVec2* Marker;\n    const int Count;\n    const float Size;\n    const ImU32 Col;\n    mutable ImVec2 UV;\n};\n\ntemplate <class _Getter> struct RendererMarkersLine : RendererBase {\n    RendererMarkersLine(const _Getter& getter, const ImVec2* marker, int count, float size, float weight, ImU32 col)\n        : RendererBase(getter.Count, count / 2 * 6, count / 2 * 4), Getter(getter), Marker(marker), Count(count),\n          HalfWeight(ImMax(1.0f, weight) * 0.5f), Size(size), Col(col) {}\n\n    void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); }\n\n    IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {\n        ImPlot3DPoint p_plot = Getter(prim);\n        if (!cull_box.Contains(p_plot))\n            return false;\n        ImVec2 p = PlotToPixels(p_plot);\n        for (int i = 0; i < Count; i = i + 2) {\n            ImVec2 p1(p.x + Marker[i].x * Size, p.y + Marker[i].y * Size);\n            ImVec2 p2(p.x + Marker[i + 1].x * Size, p.y + Marker[i + 1].y * Size);\n            PrimLine(draw_list_3d, p1, p2, HalfWeight, Col, UV0, UV1, GetPointDepth(p_plot));\n        }\n        return true;\n    }\n\n    const _Getter& Getter;\n    const ImVec2* Marker;\n    const int Count;\n    mutable float HalfWeight;\n    const float Size;\n    const ImU32 Col;\n    mutable ImVec2 UV0;\n    mutable ImVec2 UV1;\n};\n\ntemplate <class _Getter> struct RendererLineStrip : RendererBase {\n    RendererLineStrip(const _Getter& getter, ImU32 col, float weight)\n        : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) {\n        // Initialize the first point in plot coordinates\n        P1_plot = Getter(0);\n    }\n\n    void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); }\n\n    IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {\n        ImPlot3DPoint P2_plot = Getter(prim + 1);\n\n        // Clip the line segment to the culling box using Liang-Barsky algorithm\n        ImPlot3DPoint P1_clipped, P2_clipped;\n        bool visible = cull_box.ClipLineSegment(P1_plot, P2_plot, P1_clipped, P2_clipped);\n\n        if (visible) {\n            // Convert clipped points to pixel coordinates\n            ImVec2 P1_screen = PlotToPixels(P1_clipped);\n            ImVec2 P2_screen = PlotToPixels(P2_clipped);\n            // Render the line segment\n            PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, Col, UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5f));\n        }\n\n        // Update for next segment\n        P1_plot = P2_plot;\n\n        return visible;\n    }\n\n    const _Getter& Getter;\n    const ImU32 Col;\n    mutable float HalfWeight;\n    mutable ImPlot3DPoint P1_plot;\n    mutable ImVec2 UV0;\n    mutable ImVec2 UV1;\n};\n\ntemplate <class _Getter> struct RendererLineStripSkip : RendererBase {\n    RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight)\n        : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) {\n        // Initialize the first point in plot coordinates\n        P1_plot = Getter(0);\n    }\n\n    void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); }\n\n    IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {\n        // Get the next point in plot coordinates\n        ImPlot3DPoint P2_plot = Getter(prim + 1);\n        bool visible = false;\n\n        // Check for NaNs in P1_plot and P2_plot\n        if (!ImNan(P1_plot.x) && !ImNan(P1_plot.y) && !ImNan(P1_plot.z) && !ImNan(P2_plot.x) && !ImNan(P2_plot.y) && !ImNan(P2_plot.z)) {\n\n            // Clip the line segment to the culling box\n            ImPlot3DPoint P1_clipped, P2_clipped;\n            visible = cull_box.ClipLineSegment(P1_plot, P2_plot, P1_clipped, P2_clipped);\n\n            if (visible) {\n                // Convert clipped points to pixel coordinates\n                ImVec2 P1_screen = PlotToPixels(P1_clipped);\n                ImVec2 P2_screen = PlotToPixels(P2_clipped);\n                // Render the line segment\n                PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, Col, UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5f));\n            }\n        }\n\n        // Update P1_plot if P2_plot is valid\n        if (!ImNan(P2_plot.x) && !ImNan(P2_plot.y) && !ImNan(P2_plot.z))\n            P1_plot = P2_plot;\n\n        return visible;\n    }\n\n    const _Getter& Getter;\n    const ImU32 Col;\n    mutable float HalfWeight;\n    mutable ImPlot3DPoint P1_plot;\n    mutable ImVec2 UV0;\n    mutable ImVec2 UV1;\n};\n\ntemplate <class _Getter> struct RendererLineSegments : RendererBase {\n    RendererLineSegments(const _Getter& getter, ImU32 col, float weight)\n        : RendererBase(getter.Count / 2, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) {}\n\n    void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); }\n\n    IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {\n        // Get the segment's endpoints in plot coordinates\n        ImPlot3DPoint P1_plot = Getter(prim * 2 + 0);\n        ImPlot3DPoint P2_plot = Getter(prim * 2 + 1);\n\n        // Check for NaNs in P1_plot and P2_plot\n        if (!ImNan(P1_plot.x) && !ImNan(P1_plot.y) && !ImNan(P1_plot.z) && !ImNan(P2_plot.x) && !ImNan(P2_plot.y) && !ImNan(P2_plot.z)) {\n\n            // Clip the line segment to the culling box\n            ImPlot3DPoint P1_clipped, P2_clipped;\n            bool visible = cull_box.ClipLineSegment(P1_plot, P2_plot, P1_clipped, P2_clipped);\n\n            if (visible) {\n                // Convert clipped points to pixel coordinates\n                ImVec2 P1_screen = PlotToPixels(P1_clipped);\n                ImVec2 P2_screen = PlotToPixels(P2_clipped);\n                // Render the line segment\n                PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, Col, UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5f));\n            }\n            return visible;\n        }\n\n        return false;\n    }\n\n    const _Getter& Getter;\n    const ImU32 Col;\n    mutable float HalfWeight;\n    mutable ImVec2 UV0;\n    mutable ImVec2 UV1;\n};\n\ntemplate <class _Getter> struct RendererTriangleFill : RendererBase {\n    RendererTriangleFill(const _Getter& getter, ImU32 col) : RendererBase(getter.Count / 3, 3, 3), Getter(getter), Col(col) {}\n\n    void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; }\n\n    IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {\n        ImPlot3DPoint p_plot[3];\n        p_plot[0] = Getter(3 * prim);\n        p_plot[1] = Getter(3 * prim + 1);\n        p_plot[2] = Getter(3 * prim + 2);\n\n        // Check if the triangle is outside the culling box\n        if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) && !cull_box.Contains(p_plot[2]))\n            return false;\n\n        // Project the triangle vertices to screen space\n        ImVec2 p[3];\n        p[0] = PlotToPixels(p_plot[0]);\n        p[1] = PlotToPixels(p_plot[1]);\n        p[2] = PlotToPixels(p_plot[2]);\n\n        // 3 vertices per triangle\n        draw_list_3d._VtxWritePtr[0].pos.x = p[0].x;\n        draw_list_3d._VtxWritePtr[0].pos.y = p[0].y;\n        draw_list_3d._VtxWritePtr[0].uv = UV;\n        draw_list_3d._VtxWritePtr[0].col = Col;\n        draw_list_3d._VtxWritePtr[1].pos.x = p[1].x;\n        draw_list_3d._VtxWritePtr[1].pos.y = p[1].y;\n        draw_list_3d._VtxWritePtr[1].uv = UV;\n        draw_list_3d._VtxWritePtr[1].col = Col;\n        draw_list_3d._VtxWritePtr[2].pos.x = p[2].x;\n        draw_list_3d._VtxWritePtr[2].pos.y = p[2].y;\n        draw_list_3d._VtxWritePtr[2].uv = UV;\n        draw_list_3d._VtxWritePtr[2].col = Col;\n        draw_list_3d._VtxWritePtr += 3;\n\n        // 3 indices per triangle\n        draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);\n        draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 1);\n        draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2);\n        draw_list_3d._IdxWritePtr += 3;\n        // 1 Z per vertex\n        draw_list_3d._ZWritePtr[0] = GetPointDepth((p_plot[0] + p_plot[1] + p_plot[2]) / 3);\n        draw_list_3d._ZWritePtr++;\n\n        // Update vertex count\n        draw_list_3d._VtxCurrentIdx += 3;\n\n        return true;\n    }\n\n    const _Getter& Getter;\n    mutable ImVec2 UV;\n    const ImU32 Col;\n};\n\ntemplate <class _Getter> struct RendererQuadFill : RendererBase {\n    RendererQuadFill(const _Getter& getter, ImU32 col) : RendererBase(getter.Count / 4, 6, 4), Getter(getter), Col(col) {}\n\n    void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; }\n\n    IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {\n        ImPlot3DPoint p_plot[4];\n        p_plot[0] = Getter(4 * prim);\n        p_plot[1] = Getter(4 * prim + 1);\n        p_plot[2] = Getter(4 * prim + 2);\n        p_plot[3] = Getter(4 * prim + 3);\n\n        // Check if the quad is outside the culling box\n        if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) && !cull_box.Contains(p_plot[2]) && !cull_box.Contains(p_plot[3]))\n            return false;\n\n        // Project the quad vertices to screen space\n        ImVec2 p[4];\n        p[0] = PlotToPixels(p_plot[0]);\n        p[1] = PlotToPixels(p_plot[1]);\n        p[2] = PlotToPixels(p_plot[2]);\n        p[3] = PlotToPixels(p_plot[3]);\n\n        // Add vertices for two triangles\n        draw_list_3d._VtxWritePtr[0].pos.x = p[0].x;\n        draw_list_3d._VtxWritePtr[0].pos.y = p[0].y;\n        draw_list_3d._VtxWritePtr[0].uv = UV;\n        draw_list_3d._VtxWritePtr[0].col = Col;\n\n        draw_list_3d._VtxWritePtr[1].pos.x = p[1].x;\n        draw_list_3d._VtxWritePtr[1].pos.y = p[1].y;\n        draw_list_3d._VtxWritePtr[1].uv = UV;\n        draw_list_3d._VtxWritePtr[1].col = Col;\n\n        draw_list_3d._VtxWritePtr[2].pos.x = p[2].x;\n        draw_list_3d._VtxWritePtr[2].pos.y = p[2].y;\n        draw_list_3d._VtxWritePtr[2].uv = UV;\n        draw_list_3d._VtxWritePtr[2].col = Col;\n\n        draw_list_3d._VtxWritePtr[3].pos.x = p[3].x;\n        draw_list_3d._VtxWritePtr[3].pos.y = p[3].y;\n        draw_list_3d._VtxWritePtr[3].uv = UV;\n        draw_list_3d._VtxWritePtr[3].col = Col;\n\n        draw_list_3d._VtxWritePtr += 4;\n\n        // Add indices for two triangles\n        draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);\n        draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 1);\n        draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2);\n\n        draw_list_3d._IdxWritePtr[3] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);\n        draw_list_3d._IdxWritePtr[4] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2);\n        draw_list_3d._IdxWritePtr[5] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 3);\n\n        draw_list_3d._IdxWritePtr += 6;\n\n        // Add depth value for the quad\n        float z = GetPointDepth((p_plot[0] + p_plot[1] + p_plot[2] + p_plot[3]) / 4.0f);\n        draw_list_3d._ZWritePtr[0] = z;\n        draw_list_3d._ZWritePtr[1] = z;\n        draw_list_3d._ZWritePtr += 2;\n\n        // Update vertex count\n        draw_list_3d._VtxCurrentIdx += 4;\n\n        return true;\n    }\n\n    const _Getter& Getter;\n    mutable ImVec2 UV;\n    const ImU32 Col;\n};\n\ntemplate <class _Getter> struct RendererQuadImage : RendererBase {\n    RendererQuadImage(const _Getter& getter, ImTextureRef tex_ref, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3,\n                      ImU32 col)\n        : RendererBase(getter.Count / 4, 6, 4), Getter(getter), TexRef(tex_ref), UV0(uv0), UV1(uv1), UV2(uv2), UV3(uv3), Col(col) {}\n\n    void Init(ImDrawList3D& draw_list_3d) const {}\n\n    IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {\n        ImPlot3DPoint p_plot[4];\n        p_plot[0] = Getter(4 * prim);\n        p_plot[1] = Getter(4 * prim + 1);\n        p_plot[2] = Getter(4 * prim + 2);\n        p_plot[3] = Getter(4 * prim + 3);\n\n        // Check if the quad is outside the culling box\n        if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) && !cull_box.Contains(p_plot[2]) && !cull_box.Contains(p_plot[3]))\n            return false;\n\n        // Set texture ID to be used when rendering this quad\n        draw_list_3d.SetTexture(TexRef);\n\n        // Project the quad vertices to screen space\n        ImVec2 p[4];\n        p[0] = PlotToPixels(p_plot[0]);\n        p[1] = PlotToPixels(p_plot[1]);\n        p[2] = PlotToPixels(p_plot[2]);\n        p[3] = PlotToPixels(p_plot[3]);\n\n        // Add vertices for two triangles\n        draw_list_3d._VtxWritePtr[0].pos.x = p[0].x;\n        draw_list_3d._VtxWritePtr[0].pos.y = p[0].y;\n        draw_list_3d._VtxWritePtr[0].uv = UV0;\n        draw_list_3d._VtxWritePtr[0].col = Col;\n\n        draw_list_3d._VtxWritePtr[1].pos.x = p[1].x;\n        draw_list_3d._VtxWritePtr[1].pos.y = p[1].y;\n        draw_list_3d._VtxWritePtr[1].uv = UV1;\n        draw_list_3d._VtxWritePtr[1].col = Col;\n\n        draw_list_3d._VtxWritePtr[2].pos.x = p[2].x;\n        draw_list_3d._VtxWritePtr[2].pos.y = p[2].y;\n        draw_list_3d._VtxWritePtr[2].uv = UV2;\n        draw_list_3d._VtxWritePtr[2].col = Col;\n\n        draw_list_3d._VtxWritePtr[3].pos.x = p[3].x;\n        draw_list_3d._VtxWritePtr[3].pos.y = p[3].y;\n        draw_list_3d._VtxWritePtr[3].uv = UV3;\n        draw_list_3d._VtxWritePtr[3].col = Col;\n\n        draw_list_3d._VtxWritePtr += 4;\n\n        // Add indices for two triangles\n        draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);\n        draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 1);\n        draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2);\n\n        draw_list_3d._IdxWritePtr[3] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);\n        draw_list_3d._IdxWritePtr[4] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2);\n        draw_list_3d._IdxWritePtr[5] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 3);\n\n        draw_list_3d._IdxWritePtr += 6;\n\n        // Add depth value for the quad\n        float z = GetPointDepth((p_plot[0] + p_plot[1] + p_plot[2] + p_plot[3]) / 4.0f);\n        draw_list_3d._ZWritePtr[0] = z;\n        draw_list_3d._ZWritePtr[1] = z;\n        draw_list_3d._ZWritePtr += 2;\n\n        // Update vertex count\n        draw_list_3d._VtxCurrentIdx += 4;\n\n        // Reset texture ID\n        draw_list_3d.ResetTexture();\n\n        return true;\n    }\n\n    const _Getter& Getter;\n    const ImTextureRef TexRef;\n    const ImVec2 UV0, UV1, UV2, UV3;\n    const ImU32 Col;\n};\n\ntemplate <class _Getter> struct RendererSurfaceFill : RendererBase {\n    RendererSurfaceFill(const _Getter& getter, int x_count, int y_count, ImU32 col, double scale_min, double scale_max)\n        : RendererBase((x_count - 1) * (y_count - 1), 6, 4), Getter(getter), XCount(x_count), YCount(y_count), Col(col), ScaleMin(scale_min),\n          ScaleMax(scale_max) {}\n\n    void Init(ImDrawList3D& draw_list_3d) const {\n        UV = draw_list_3d._SharedData->TexUvWhitePixel;\n\n        // Compute min and max values for the colormap (if not solid fill)\n        const ImPlot3DNextItemData& n = GetItemData();\n        if (n.IsAutoFill) {\n            Min = FLT_MAX;\n            Max = -FLT_MAX;\n            for (int i = 0; i < Getter.Count; i++) {\n                float z = Getter(i).z;\n                Min = ImMin(Min, z);\n                Max = ImMax(Max, z);\n            }\n        }\n    }\n\n    IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const {\n        int x = prim % (XCount - 1);\n        int y = prim / (XCount - 1);\n\n        ImPlot3DPoint p_plot[4];\n        p_plot[0] = Getter(x + y * XCount);\n        p_plot[1] = Getter(x + 1 + y * XCount);\n        p_plot[2] = Getter(x + 1 + (y + 1) * XCount);\n        p_plot[3] = Getter(x + (y + 1) * XCount);\n\n        // Check if the quad is outside the culling box\n        if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) && !cull_box.Contains(p_plot[2]) && !cull_box.Contains(p_plot[3]))\n            return false;\n\n        // Compute colors\n        ImU32 cols[4] = {Col, Col, Col, Col};\n        const ImPlot3DNextItemData& n = GetItemData();\n        if (n.IsAutoFill) {\n            float alpha = GImPlot3D->NextItemData.FillAlpha;\n            float min = Min;\n            float max = Max;\n            if (ScaleMin != 0.0 || ScaleMax != 0.0) {\n                min = (float)ScaleMin;\n                max = (float)ScaleMax;\n            }\n            for (int i = 0; i < 4; i++) {\n                ImVec4 col = SampleColormap(ImClamp(ImRemap01(p_plot[i].z, min, max), 0.0f, 1.0f));\n                col.w *= alpha;\n                cols[i] = ImGui::ColorConvertFloat4ToU32(col);\n            }\n        }\n\n        // Project the quad vertices to screen space\n        ImVec2 p[4];\n        p[0] = PlotToPixels(p_plot[0]);\n        p[1] = PlotToPixels(p_plot[1]);\n        p[2] = PlotToPixels(p_plot[2]);\n        p[3] = PlotToPixels(p_plot[3]);\n\n        // Add vertices for two triangles\n        draw_list_3d._VtxWritePtr[0].pos.x = p[0].x;\n        draw_list_3d._VtxWritePtr[0].pos.y = p[0].y;\n        draw_list_3d._VtxWritePtr[0].uv = UV;\n        draw_list_3d._VtxWritePtr[0].col = cols[0];\n\n        draw_list_3d._VtxWritePtr[1].pos.x = p[1].x;\n        draw_list_3d._VtxWritePtr[1].pos.y = p[1].y;\n        draw_list_3d._VtxWritePtr[1].uv = UV;\n        draw_list_3d._VtxWritePtr[1].col = cols[1];\n\n        draw_list_3d._VtxWritePtr[2].pos.x = p[2].x;\n        draw_list_3d._VtxWritePtr[2].pos.y = p[2].y;\n        draw_list_3d._VtxWritePtr[2].uv = UV;\n        draw_list_3d._VtxWritePtr[2].col = cols[2];\n\n        draw_list_3d._VtxWritePtr[3].pos.x = p[3].x;\n        draw_list_3d._VtxWritePtr[3].pos.y = p[3].y;\n        draw_list_3d._VtxWritePtr[3].uv = UV;\n        draw_list_3d._VtxWritePtr[3].col = cols[3];\n\n        draw_list_3d._VtxWritePtr += 4;\n\n        // Add indices for two triangles\n        draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);\n        draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 1);\n        draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2);\n\n        draw_list_3d._IdxWritePtr[3] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx);\n        draw_list_3d._IdxWritePtr[4] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2);\n        draw_list_3d._IdxWritePtr[5] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 3);\n\n        draw_list_3d._IdxWritePtr += 6;\n\n        // Add depth values for the two triangles\n        draw_list_3d._ZWritePtr[0] = GetPointDepth((p_plot[0] + p_plot[1] + p_plot[2]) / 3.0f);\n        draw_list_3d._ZWritePtr[1] = GetPointDepth((p_plot[0] + p_plot[2] + p_plot[3]) / 3.0f);\n        draw_list_3d._ZWritePtr += 2;\n\n        // Update vertex count\n        draw_list_3d._VtxCurrentIdx += 4;\n\n        return true;\n    }\n\n    const _Getter& Getter;\n    mutable ImVec2 UV;\n    mutable float Min; // Minimum value for the colormap\n    mutable float Max; // Minimum value for the colormap\n    const int XCount;\n    const int YCount;\n    const ImU32 Col;\n    const double ScaleMin;\n    const double ScaleMax;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Indexers\n//-----------------------------------------------------------------------------\n\ntemplate <typename T> IMPLOT3D_INLINE T IndexData(const T* data, int idx, int count, int offset, int stride) {\n    const int s = ((offset == 0) << 0) | ((stride == sizeof(T)) << 1);\n    switch (s) {\n        case 3: return data[idx];\n        case 2: return data[(offset + idx) % count];\n        case 1: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((idx))*stride);\n        case 0: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((offset + idx) % count) * stride);\n        default: return T(0);\n    }\n}\n\ntemplate <typename T> struct IndexerIdx {\n    IndexerIdx(const T* data, int count, int offset = 0, int stride = sizeof(T)) : Data(data), Count(count), Offset(offset), Stride(stride) {}\n    template <typename I> IMPLOT3D_INLINE double operator()(I idx) const { return (double)IndexData(Data, idx, Count, Offset, Stride); }\n    const T* Data;\n    int Count;\n    int Offset;\n    int Stride;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Getters\n//-----------------------------------------------------------------------------\n\ntemplate <typename _IndexerX, typename _IndexerY, typename _IndexerZ> struct GetterXYZ {\n    GetterXYZ(_IndexerX x, _IndexerY y, _IndexerZ z, int count) : IndexerX(x), IndexerY(y), IndexerZ(z), Count(count) {}\n    template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const {\n        return ImPlot3DPoint((float)IndexerX(idx), (float)IndexerY(idx), (float)IndexerZ(idx));\n    }\n    const _IndexerX IndexerX;\n    const _IndexerY IndexerY;\n    const _IndexerZ IndexerZ;\n    const int Count;\n};\n\ntemplate <typename _Getter> struct GetterLoop {\n    GetterLoop(_Getter getter) : Getter(getter), Count(getter.Count + 1) {}\n    template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const {\n        idx = idx % (Count - 1);\n        return Getter(idx);\n    }\n    const _Getter Getter;\n    const int Count;\n};\n\ntemplate <typename _Getter> struct GetterTriangleLines {\n    GetterTriangleLines(_Getter getter) : Getter(getter), Count(getter.Count * 2) {}\n    template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const {\n        idx = ((idx % 6 + 1) / 2) % 3 + idx / 6 * 3;\n        return Getter(idx);\n    }\n    const _Getter Getter;\n    const int Count;\n};\n\ntemplate <typename _Getter> struct GetterQuadLines {\n    GetterQuadLines(_Getter getter) : Getter(getter), Count(getter.Count * 2) {}\n    template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const {\n        idx = ((idx % 8 + 1) / 2) % 4 + idx / 8 * 4;\n        return Getter(idx);\n    }\n    const _Getter Getter;\n    const int Count;\n};\n\ntemplate <typename _Getter> struct GetterSurfaceLines {\n    GetterSurfaceLines(_Getter getter, int x_count, int y_count) : Getter(getter), XCount(x_count), YCount(y_count) {\n        int horizontal_segments = (XCount - 1) * YCount;\n        int vertical_segments = (YCount - 1) * XCount;\n        int segments = horizontal_segments + vertical_segments;\n        Count = segments * 2; // Each segment has 2 endpoints\n    }\n\n    template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const {\n        // idx is an endpoint index\n        int endpoint_i = (int)(idx % 2);\n        int segment_i = (int)(idx / 2);\n\n        int horizontal_segments = (XCount - 1) * YCount;\n\n        int px, py;\n        if (segment_i < horizontal_segments) {\n            // Horizontal segment\n            int row = segment_i / (XCount - 1);\n            int col = segment_i % (XCount - 1);\n            // Endpoint 0 is (col, row), endpoint 1 is (col+1, row)\n            px = endpoint_i == 0 ? col : col + 1;\n            py = row;\n        } else {\n            // Vertical segment\n            int seg_v = segment_i - horizontal_segments;\n            int col = seg_v / (YCount - 1);\n            int row = seg_v % (YCount - 1);\n            // Endpoint 0 is (col, row), endpoint 1 is (col, row+1)\n            px = col;\n            py = row + endpoint_i;\n        }\n\n        return Getter(py * XCount + px);\n    }\n\n    const _Getter Getter;\n    int Count;\n    const int XCount;\n    const int YCount;\n};\n\nstruct Getter3DPoints {\n    Getter3DPoints(const ImPlot3DPoint* points, int count) : Points(points), Count(count) {}\n    template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { return Points[idx]; }\n    const ImPlot3DPoint* Points;\n    const int Count;\n};\n\nstruct GetterMeshTriangles {\n    GetterMeshTriangles(const ImPlot3DPoint* vtx, const unsigned int* idx, int idx_count)\n        : Vtx(vtx), Idx(idx), IdxCount(idx_count), TriCount(idx_count / 3), Count(idx_count) {}\n\n    template <typename I> IMPLOT3D_INLINE ImPlot3DPoint operator()(I i) const {\n        unsigned int vi = Idx[i];\n        return Vtx[vi];\n    }\n\n    const ImPlot3DPoint* Vtx;\n    const unsigned int* Idx;\n    int IdxCount;\n    int TriCount;\n    int Count;\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] RenderPrimitives\n//-----------------------------------------------------------------------------\n\n/// Renders primitive shapes\ntemplate <template <class> class _Renderer, class _Getter, typename... Args> void RenderPrimitives(const _Getter& getter, Args... args) {\n    _Renderer<_Getter> renderer(getter, args...);\n    ImPlot3DPlot& plot = *GetCurrentPlot();\n    ImDrawList3D& draw_list_3d = plot.DrawList;\n    ImPlot3DBox cull_box;\n    if (ImHasFlag(plot.Flags, ImPlot3DFlags_NoClip)) {\n        cull_box.Min = ImPlot3DPoint(-HUGE_VAL, -HUGE_VAL, -HUGE_VAL);\n        cull_box.Max = ImPlot3DPoint(HUGE_VAL, HUGE_VAL, HUGE_VAL);\n    } else {\n        cull_box.Min = plot.RangeMin();\n        cull_box.Max = plot.RangeMax();\n    }\n\n    // Find how many can be reserved up to end of current draw command's limit\n    unsigned int prims_to_render = ImMin(renderer.Prims, (ImDrawList3D::MaxIdx() - draw_list_3d._VtxCurrentIdx) / renderer.VtxConsumed);\n\n    // Reserve vertices and indices to render the primitives\n    draw_list_3d.PrimReserve(prims_to_render * renderer.IdxConsumed, prims_to_render * renderer.VtxConsumed);\n\n    // Initialize renderer\n    renderer.Init(draw_list_3d);\n\n    // Render primitives\n    int num_culled = 0;\n    for (unsigned int i = 0; i < prims_to_render; i++)\n        if (!renderer.Render(draw_list_3d, cull_box, i))\n            num_culled++;\n    // Unreserve unused vertices and indices\n    draw_list_3d.PrimUnreserve(num_culled * renderer.IdxConsumed, num_culled * renderer.VtxConsumed);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Markers\n//-----------------------------------------------------------------------------\n\nstatic const ImVec2 MARKER_FILL_CIRCLE[10] = {ImVec2(1.0f, 0.0f),\n                                              ImVec2(0.809017f, 0.58778524f),\n                                              ImVec2(0.30901697f, 0.95105654f),\n                                              ImVec2(-0.30901703f, 0.9510565f),\n                                              ImVec2(-0.80901706f, 0.5877852f),\n                                              ImVec2(-1.0f, 0.0f),\n                                              ImVec2(-0.80901694f, -0.58778536f),\n                                              ImVec2(-0.3090171f, -0.9510565f),\n                                              ImVec2(0.30901712f, -0.9510565f),\n                                              ImVec2(0.80901694f, -0.5877853f)};\nstatic const ImVec2 MARKER_FILL_SQUARE[4] = {ImVec2(SQRT_1_2, SQRT_1_2), ImVec2(SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, -SQRT_1_2),\n                                             ImVec2(-SQRT_1_2, SQRT_1_2)};\nstatic const ImVec2 MARKER_FILL_DIAMOND[4] = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(0, 1)};\nstatic const ImVec2 MARKER_FILL_UP[3] = {ImVec2(SQRT_3_2, 0.5f), ImVec2(0, -1), ImVec2(-SQRT_3_2, 0.5f)};\nstatic const ImVec2 MARKER_FILL_DOWN[3] = {ImVec2(SQRT_3_2, -0.5f), ImVec2(0, 1), ImVec2(-SQRT_3_2, -0.5f)};\nstatic const ImVec2 MARKER_FILL_LEFT[3] = {ImVec2(-1, 0), ImVec2(0.5, SQRT_3_2), ImVec2(0.5, -SQRT_3_2)};\nstatic const ImVec2 MARKER_FILL_RIGHT[3] = {ImVec2(1, 0), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, -SQRT_3_2)};\nstatic const ImVec2 MARKER_LINE_CIRCLE[20] = {ImVec2(1.0f, 0.0f),\n                                              ImVec2(0.809017f, 0.58778524f),\n                                              ImVec2(0.809017f, 0.58778524f),\n                                              ImVec2(0.30901697f, 0.95105654f),\n                                              ImVec2(0.30901697f, 0.95105654f),\n                                              ImVec2(-0.30901703f, 0.9510565f),\n                                              ImVec2(-0.30901703f, 0.9510565f),\n                                              ImVec2(-0.80901706f, 0.5877852f),\n                                              ImVec2(-0.80901706f, 0.5877852f),\n                                              ImVec2(-1.0f, 0.0f),\n                                              ImVec2(-1.0f, 0.0f),\n                                              ImVec2(-0.80901694f, -0.58778536f),\n                                              ImVec2(-0.80901694f, -0.58778536f),\n                                              ImVec2(-0.3090171f, -0.9510565f),\n                                              ImVec2(-0.3090171f, -0.9510565f),\n                                              ImVec2(0.30901712f, -0.9510565f),\n                                              ImVec2(0.30901712f, -0.9510565f),\n                                              ImVec2(0.80901694f, -0.5877853f),\n                                              ImVec2(0.80901694f, -0.5877853f),\n                                              ImVec2(1.0f, 0.0f)};\nstatic const ImVec2 MARKER_LINE_SQUARE[8] = {ImVec2(SQRT_1_2, SQRT_1_2),   ImVec2(SQRT_1_2, -SQRT_1_2),  ImVec2(SQRT_1_2, -SQRT_1_2),\n                                             ImVec2(-SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, -SQRT_1_2), ImVec2(-SQRT_1_2, SQRT_1_2),\n                                             ImVec2(-SQRT_1_2, SQRT_1_2),  ImVec2(SQRT_1_2, SQRT_1_2)};\nstatic const ImVec2 MARKER_LINE_DIAMOND[8] = {ImVec2(1, 0),  ImVec2(0, -1), ImVec2(0, -1), ImVec2(-1, 0),\n                                              ImVec2(-1, 0), ImVec2(0, 1),  ImVec2(0, 1),  ImVec2(1, 0)};\nstatic const ImVec2 MARKER_LINE_UP[6] = {ImVec2(SQRT_3_2, 0.5f),  ImVec2(0, -1),           ImVec2(0, -1),\n                                         ImVec2(-SQRT_3_2, 0.5f), ImVec2(-SQRT_3_2, 0.5f), ImVec2(SQRT_3_2, 0.5f)};\nstatic const ImVec2 MARKER_LINE_DOWN[6] = {ImVec2(SQRT_3_2, -0.5f),  ImVec2(0, 1),           ImVec2(0, 1), ImVec2(-SQRT_3_2, -0.5f),\n                                           ImVec2(-SQRT_3_2, -0.5f), ImVec2(SQRT_3_2, -0.5f)};\nstatic const ImVec2 MARKER_LINE_LEFT[6] = {ImVec2(-1, 0),          ImVec2(0.5, SQRT_3_2),  ImVec2(0.5, SQRT_3_2),\n                                           ImVec2(0.5, -SQRT_3_2), ImVec2(0.5, -SQRT_3_2), ImVec2(-1, 0)};\nstatic const ImVec2 MARKER_LINE_RIGHT[6] = {\n    ImVec2(1, 0), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, -SQRT_3_2), ImVec2(-0.5, -SQRT_3_2), ImVec2(1, 0)};\nstatic const ImVec2 MARKER_LINE_ASTERISK[6] = {ImVec2(-SQRT_3_2, -0.5f), ImVec2(SQRT_3_2, 0.5f), ImVec2(-SQRT_3_2, 0.5f),\n                                               ImVec2(SQRT_3_2, -0.5f),  ImVec2(0, -1),          ImVec2(0, 1)};\nstatic const ImVec2 MARKER_LINE_PLUS[4] = {ImVec2(-1, 0), ImVec2(1, 0), ImVec2(0, -1), ImVec2(0, 1)};\nstatic const ImVec2 MARKER_LINE_CROSS[4] = {ImVec2(-SQRT_1_2, -SQRT_1_2), ImVec2(SQRT_1_2, SQRT_1_2), ImVec2(SQRT_1_2, -SQRT_1_2),\n                                            ImVec2(-SQRT_1_2, SQRT_1_2)};\n\ntemplate <typename _Getter> void RenderMarkers(const _Getter& getter, ImPlot3DMarker marker, float size, bool rend_fill, ImU32 col_fill,\n                                               bool rend_line, ImU32 col_line, float weight) {\n    if (rend_fill) {\n        switch (marker) {\n            case ImPlot3DMarker_Circle: RenderPrimitives<RendererMarkersFill>(getter, MARKER_FILL_CIRCLE, 10, size, col_fill); break;\n            case ImPlot3DMarker_Square: RenderPrimitives<RendererMarkersFill>(getter, MARKER_FILL_SQUARE, 4, size, col_fill); break;\n            case ImPlot3DMarker_Diamond: RenderPrimitives<RendererMarkersFill>(getter, MARKER_FILL_DIAMOND, 4, size, col_fill); break;\n            case ImPlot3DMarker_Up: RenderPrimitives<RendererMarkersFill>(getter, MARKER_FILL_UP, 3, size, col_fill); break;\n            case ImPlot3DMarker_Down: RenderPrimitives<RendererMarkersFill>(getter, MARKER_FILL_DOWN, 3, size, col_fill); break;\n            case ImPlot3DMarker_Left: RenderPrimitives<RendererMarkersFill>(getter, MARKER_FILL_LEFT, 3, size, col_fill); break;\n            case ImPlot3DMarker_Right: RenderPrimitives<RendererMarkersFill>(getter, MARKER_FILL_RIGHT, 3, size, col_fill); break;\n        }\n    }\n    if (rend_line) {\n        switch (marker) {\n            case ImPlot3DMarker_Circle: RenderPrimitives<RendererMarkersLine>(getter, MARKER_LINE_CIRCLE, 20, size, weight, col_line); break;\n            case ImPlot3DMarker_Square: RenderPrimitives<RendererMarkersLine>(getter, MARKER_LINE_SQUARE, 8, size, weight, col_line); break;\n            case ImPlot3DMarker_Diamond: RenderPrimitives<RendererMarkersLine>(getter, MARKER_LINE_DIAMOND, 8, size, weight, col_line); break;\n            case ImPlot3DMarker_Up: RenderPrimitives<RendererMarkersLine>(getter, MARKER_LINE_UP, 6, size, weight, col_line); break;\n            case ImPlot3DMarker_Down: RenderPrimitives<RendererMarkersLine>(getter, MARKER_LINE_DOWN, 6, size, weight, col_line); break;\n            case ImPlot3DMarker_Left: RenderPrimitives<RendererMarkersLine>(getter, MARKER_LINE_LEFT, 6, size, weight, col_line); break;\n            case ImPlot3DMarker_Right: RenderPrimitives<RendererMarkersLine>(getter, MARKER_LINE_RIGHT, 6, size, weight, col_line); break;\n            case ImPlot3DMarker_Asterisk: RenderPrimitives<RendererMarkersLine>(getter, MARKER_LINE_ASTERISK, 6, size, weight, col_line); break;\n            case ImPlot3DMarker_Plus: RenderPrimitives<RendererMarkersLine>(getter, MARKER_LINE_PLUS, 4, size, weight, col_line); break;\n            case ImPlot3DMarker_Cross: RenderPrimitives<RendererMarkersLine>(getter, MARKER_LINE_CROSS, 4, size, weight, col_line); break;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotScatter\n//-----------------------------------------------------------------------------\n\ntemplate <typename Getter> void PlotScatterEx(const char* label_id, const Getter& getter, ImPlot3DScatterFlags flags) {\n    if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_MarkerOutline)) {\n        const ImPlot3DNextItemData& n = GetItemData();\n        ImPlot3DMarker marker = n.Marker == ImPlot3DMarker_None ? ImPlot3DMarker_Circle : n.Marker;\n        const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerOutline]);\n        const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerFill]);\n        if (marker != ImPlot3DMarker_None)\n            RenderMarkers<Getter>(getter, marker, n.MarkerSize, n.RenderMarkerFill, col_fill, n.RenderMarkerLine, col_line, n.MarkerWeight);\n        EndItem();\n    }\n}\n\ntemplate <typename T>\nvoid PlotScatter(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DScatterFlags flags, int offset, int stride) {\n    if (count < 1)\n        return;\n    GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride),\n                                                                  IndexerIdx<T>(zs, count, offset, stride), count);\n    return PlotScatterEx(label_id, getter, flags);\n}\n\n#define INSTANTIATE_MACRO(T)                                                                                                                         \\\n    template IMPLOT3D_API void PlotScatter<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DScatterFlags flags,    \\\n                                              int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotLine\n//-----------------------------------------------------------------------------\n\ntemplate <typename _Getter> void PlotLineEx(const char* label_id, const _Getter& getter, ImPlot3DLineFlags flags) {\n    if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_Line)) {\n        const ImPlot3DNextItemData& n = GetItemData();\n        if (getter.Count >= 2 && n.RenderLine) {\n            const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Line]);\n            if (ImHasFlag(flags, ImPlot3DLineFlags_Segments)) {\n                RenderPrimitives<RendererLineSegments>(getter, col_line, n.LineWeight);\n            } else if (ImHasFlag(flags, ImPlot3DLineFlags_Loop)) {\n                if (ImHasFlag(flags, ImPlot3DLineFlags_SkipNaN))\n                    RenderPrimitives<RendererLineStripSkip>(GetterLoop<_Getter>(getter), col_line, n.LineWeight);\n                else\n                    RenderPrimitives<RendererLineStrip>(GetterLoop<_Getter>(getter), col_line, n.LineWeight);\n            } else {\n                if (ImHasFlag(flags, ImPlot3DLineFlags_SkipNaN))\n                    RenderPrimitives<RendererLineStripSkip>(getter, col_line, n.LineWeight);\n                else\n                    RenderPrimitives<RendererLineStrip>(getter, col_line, n.LineWeight);\n            }\n        }\n\n        // Render markers\n        if (n.Marker != ImPlot3DMarker_None) {\n            const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerOutline]);\n            const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerFill]);\n            RenderMarkers<_Getter>(getter, n.Marker, n.MarkerSize, n.RenderMarkerFill, col_fill, n.RenderMarkerLine, col_line, n.MarkerWeight);\n        }\n        EndItem();\n    }\n}\n\nIMPLOT3D_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DLineFlags flags, int offset, int stride) {\n    if (count < 2)\n        return;\n    GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride),\n                                                                  IndexerIdx<T>(zs, count, offset, stride), count);\n    return PlotLineEx(label_id, getter, flags);\n}\n\n#define INSTANTIATE_MACRO(T)                                                                                                                         \\\n    template IMPLOT3D_API void PlotLine<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DLineFlags flags,          \\\n                                           int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotTriangle\n//-----------------------------------------------------------------------------\n\ntemplate <typename _Getter> void PlotTriangleEx(const char* label_id, const _Getter& getter, ImPlot3DTriangleFlags flags) {\n    if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_Fill)) {\n        const ImPlot3DNextItemData& n = GetItemData();\n\n        // Render fill\n        if (getter.Count >= 3 && n.RenderFill && !ImHasFlag(flags, ImPlot3DTriangleFlags_NoFill)) {\n            const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Fill]);\n            RenderPrimitives<RendererTriangleFill>(getter, col_fill);\n        }\n\n        // Render lines\n        if (getter.Count >= 2 && n.RenderLine && !ImHasFlag(flags, ImPlot3DTriangleFlags_NoLines)) {\n            const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Line]);\n            RenderPrimitives<RendererLineSegments>(GetterTriangleLines<_Getter>(getter), col_line, n.LineWeight);\n        }\n\n        // Render markers\n        if (n.Marker != ImPlot3DMarker_None && !ImHasFlag(flags, ImPlot3DTriangleFlags_NoMarkers)) {\n            const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerOutline]);\n            const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerFill]);\n            RenderMarkers<_Getter>(getter, n.Marker, n.MarkerSize, n.RenderMarkerFill, col_fill, n.RenderMarkerLine, col_line, n.MarkerWeight);\n        }\n\n        EndItem();\n    }\n}\n\nIMPLOT3D_TMP void PlotTriangle(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DTriangleFlags flags, int offset,\n                               int stride) {\n    if (count < 3)\n        return;\n    GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride),\n                                                                  IndexerIdx<T>(zs, count, offset, stride), count);\n    return PlotTriangleEx(label_id, getter, flags);\n}\n\n#define INSTANTIATE_MACRO(T)                                                                                                                         \\\n    template IMPLOT3D_API void PlotTriangle<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DTriangleFlags flags,  \\\n                                               int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotQuad\n//-----------------------------------------------------------------------------\n\ntemplate <typename _Getter> void PlotQuadEx(const char* label_id, const _Getter& getter, ImPlot3DQuadFlags flags) {\n    if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_Fill)) {\n        const ImPlot3DNextItemData& n = GetItemData();\n\n        // Render fill\n        if (getter.Count >= 4 && n.RenderFill && !ImHasFlag(flags, ImPlot3DQuadFlags_NoFill)) {\n            const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Fill]);\n            RenderPrimitives<RendererQuadFill>(getter, col_fill);\n        }\n\n        // Render lines\n        if (getter.Count >= 2 && n.RenderLine && !ImHasFlag(flags, ImPlot3DQuadFlags_NoLines)) {\n            const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Line]);\n            RenderPrimitives<RendererLineSegments>(GetterQuadLines<_Getter>(getter), col_line, n.LineWeight);\n        }\n\n        // Render markers\n        if (n.Marker != ImPlot3DMarker_None && !ImHasFlag(flags, ImPlot3DQuadFlags_NoMarkers)) {\n            const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerOutline]);\n            const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerFill]);\n            RenderMarkers<_Getter>(getter, n.Marker, n.MarkerSize, n.RenderMarkerFill, col_fill, n.RenderMarkerLine, col_line, n.MarkerWeight);\n        }\n\n        EndItem();\n    }\n}\n\nIMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DQuadFlags flags, int offset, int stride) {\n    if (count < 3)\n        return;\n    GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride),\n                                                                  IndexerIdx<T>(zs, count, offset, stride), count);\n    return PlotQuadEx(label_id, getter, flags);\n}\n\n#define INSTANTIATE_MACRO(T)                                                                                                                         \\\n    template IMPLOT3D_API void PlotQuad<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DQuadFlags flags,          \\\n                                           int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotSurface\n//-----------------------------------------------------------------------------\n\ntemplate <typename _Getter> void PlotSurfaceEx(const char* label_id, const _Getter& getter, int x_count, int y_count, double scale_min,\n                                               double scale_max, ImPlot3DSurfaceFlags flags) {\n    if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_Fill)) {\n        const ImPlot3DNextItemData& n = GetItemData();\n\n        // Render fill\n        if (getter.Count >= 4 && n.RenderFill && !ImHasFlag(flags, ImPlot3DSurfaceFlags_NoFill)) {\n            const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Fill]);\n            RenderPrimitives<RendererSurfaceFill>(getter, x_count, y_count, col_fill, scale_min, scale_max);\n        }\n\n        // Render lines\n        if (getter.Count >= 2 && n.RenderLine && !ImHasFlag(flags, ImPlot3DSurfaceFlags_NoLines)) {\n            const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Line]);\n            RenderPrimitives<RendererLineSegments>(GetterSurfaceLines<_Getter>(getter, x_count, y_count), col_line, n.LineWeight);\n        }\n\n        // Render markers\n        if (n.Marker != ImPlot3DMarker_None && !ImHasFlag(flags, ImPlot3DSurfaceFlags_NoMarkers)) {\n            const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerOutline]);\n            const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerFill]);\n            RenderMarkers<_Getter>(getter, n.Marker, n.MarkerSize, n.RenderMarkerFill, col_fill, n.RenderMarkerLine, col_line, n.MarkerWeight);\n        }\n\n        EndItem();\n    }\n}\n\nIMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min,\n                              double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride) {\n    int count = x_count * y_count;\n    if (count < 4)\n        return;\n    GetterXYZ<IndexerIdx<T>, IndexerIdx<T>, IndexerIdx<T>> getter(IndexerIdx<T>(xs, count, offset, stride), IndexerIdx<T>(ys, count, offset, stride),\n                                                                  IndexerIdx<T>(zs, count, offset, stride), count);\n    return PlotSurfaceEx(label_id, getter, x_count, y_count, scale_min, scale_max, flags);\n}\n\n#define INSTANTIATE_MACRO(T)                                                                                                                         \\\n    template IMPLOT3D_API void PlotSurface<T>(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count,                 \\\n                                              double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride);\nCALL_INSTANTIATE_FOR_NUMERIC_TYPES()\n#undef INSTANTIATE_MACRO\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotMesh\n//-----------------------------------------------------------------------------\n\nvoid PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int* idx, int vtx_count, int idx_count, ImPlot3DMeshFlags flags) {\n    Getter3DPoints getter(vtx, vtx_count);                     // Get vertices\n    GetterMeshTriangles getter_triangles(vtx, idx, idx_count); // Get triangle vertices\n    if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_Fill)) {\n        const ImPlot3DNextItemData& n = GetItemData();\n\n        // Render fill\n        if (getter.Count >= 3 && n.RenderFill && !ImHasFlag(flags, ImPlot3DMeshFlags_NoFill)) {\n            const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Fill]);\n            RenderPrimitives<RendererTriangleFill>(getter_triangles, col_fill);\n        }\n\n        // Render lines\n        if (getter.Count >= 2 && n.RenderLine && !n.IsAutoLine && !ImHasFlag(flags, ImPlot3DMeshFlags_NoLines)) {\n            const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Line]);\n            RenderPrimitives<RendererLineSegments>(GetterTriangleLines<GetterMeshTriangles>(getter_triangles), col_line, n.LineWeight);\n        }\n\n        // Render markers\n        if (n.Marker != ImPlot3DMarker_None && !ImHasFlag(flags, ImPlot3DMeshFlags_NoMarkers)) {\n            const ImU32 col_line = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerOutline]);\n            const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_MarkerFill]);\n            RenderMarkers(getter, n.Marker, n.MarkerSize, n.RenderMarkerFill, col_fill, n.RenderMarkerLine, col_line, n.MarkerWeight);\n        }\n\n        EndItem();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotImage\n//-----------------------------------------------------------------------------\n\nIMPLOT3D_API void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlot3DPoint& center, const ImPlot3DPoint& axis_u,\n                            const ImPlot3DPoint& axis_v, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, ImPlot3DImageFlags flags) {\n    // Compute corners from center and axes\n    ImPlot3DPoint p0 = center - axis_u - axis_v; // Bottom-left\n    ImPlot3DPoint p1 = center + axis_u - axis_v; // Bottom-right\n    ImPlot3DPoint p2 = center + axis_u + axis_v; // Top-right\n    ImPlot3DPoint p3 = center - axis_u + axis_v; // Top-left\n\n    // Map ImPlot-style 2-point UVs into full 4-corner UVs\n    ImVec2 uv_0 = uv0;\n    ImVec2 uv_1 = ImVec2(uv1.x, uv0.y);\n    ImVec2 uv_2 = uv1;\n    ImVec2 uv_3 = ImVec2(uv0.x, uv1.y);\n\n    // Delegate to full quad version\n    PlotImage(label_id, tex_ref, p0, p1, p2, p3, uv_0, uv_1, uv_2, uv_3, tint_col, flags);\n}\n\nIMPLOT3D_API void PlotImage(const char* label_id, ImTextureRef tex_ref, const ImPlot3DPoint& p0, const ImPlot3DPoint& p1, const ImPlot3DPoint& p2,\n                            const ImPlot3DPoint& p3, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3,\n                            const ImVec4& tint_col, ImPlot3DImageFlags flags) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PlotImage() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n\n    ImPlot3DPoint corners[4] = {p0, p1, p2, p3};\n    Getter3DPoints getter(corners, 4);\n\n    // Invert Y from UVs\n    ImVec2 uv_0 = ImVec2(uv0.x, 1 - uv0.y);\n    ImVec2 uv_1 = ImVec2(uv1.x, 1 - uv1.y);\n    ImVec2 uv_2 = ImVec2(uv2.x, 1 - uv2.y);\n    ImVec2 uv_3 = ImVec2(uv3.x, 1 - uv3.y);\n\n    if (BeginItemEx(label_id, getter, flags)) {\n        ImU32 tint_col32 = ImGui::ColorConvertFloat4ToU32(tint_col);\n        GetCurrentItem()->Color = tint_col32;\n\n        // Render image\n        bool is_transparent = (tint_col32 & IM_COL32_A_MASK) == 0;\n        if (!is_transparent)\n            RenderPrimitives<RendererQuadImage>(getter, tex_ref, uv_0, uv_1, uv_2, uv_3, tint_col32);\n\n        EndItem();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] PlotText\n//-----------------------------------------------------------------------------\n\nvoid PlotText(const char* text, float x, float y, float z, float angle, const ImVec2& pix_offset) {\n    ImPlot3DContext& gp = *GImPlot3D;\n    IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, \"PlotText() needs to be called between BeginPlot() and EndPlot()!\");\n    SetupLock();\n    ImPlot3DPlot& plot = *gp.CurrentPlot;\n\n    ImPlot3DBox cull_box;\n    if (ImHasFlag(plot.Flags, ImPlot3DFlags_NoClip)) {\n        cull_box.Min = ImPlot3DPoint(-HUGE_VAL, -HUGE_VAL, -HUGE_VAL);\n        cull_box.Max = ImPlot3DPoint(HUGE_VAL, HUGE_VAL, HUGE_VAL);\n    } else {\n        cull_box.Min = plot.RangeMin();\n        cull_box.Max = plot.RangeMax();\n    }\n    if (!cull_box.Contains(ImPlot3DPoint(x, y, z)))\n        return;\n\n    ImVec2 p = PlotToPixels(ImPlot3DPoint(x, y, z));\n    p.x += pix_offset.x;\n    p.y += pix_offset.y;\n    AddTextRotated(GetPlotDrawList(), p, angle, GetStyleColorU32(ImPlot3DCol_InlayText), text);\n}\n\n} // namespace ImPlot3D\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "lib/third_party/imgui/implot3d/source/implot3d_meshes.cpp",
    "content": "//--------------------------------------------------\n// ImPlot3D v0.3 WIP\n// implot3d_meshes.cpp\n// Date: 2024-12-07\n// Author: Breno Cunha Queiroz (brenocq.com)\n//\n// Acknowledgments:\n//  ImPlot3D is heavily inspired by ImPlot\n//  (https://github.com/epezent/implot) by Evan Pezent,\n//  and follows a similar code style and structure to\n//  maintain consistency with ImPlot's API.\n//--------------------------------------------------\n\n// Table of Contents:\n// [SECTION] Includes\n// [SECTION] Cube\n// [SECTION] Sphere\n// [SECTION] Duck\n\n//-----------------------------------------------------------------------------\n// [SECTION] Includes\n//-----------------------------------------------------------------------------\n\n#include \"implot3d.h\"\n\n//-----------------------------------------------------------------------------\n// [SECTION] Cube\n//-----------------------------------------------------------------------------\n\nnamespace ImPlot3D {\n\n// clang-format off\nImPlot3DPoint cube_vtx[CUBE_VTX_COUNT] = {\n    {-1.0f, -1.0f, -1.0f}, // 0: Bottom-back-left\n    { 1.0f, -1.0f, -1.0f}, // 1: Bottom-back-right\n    { 1.0f,  1.0f, -1.0f}, // 2: Top-back-right\n    {-1.0f,  1.0f, -1.0f}, // 3: Top-back-left\n    {-1.0f, -1.0f,  1.0f}, // 4: Bottom-front-left\n    { 1.0f, -1.0f,  1.0f}, // 5: Bottom-front-right\n    { 1.0f,  1.0f,  1.0f}, // 6: Top-front-right\n    {-1.0f,  1.0f,  1.0f}, // 7: Top-front-left\n};\n\nunsigned int cube_idx[CUBE_IDX_COUNT] = {\n    // Back face\n    0, 1, 2,\n    0, 2, 3,\n    // Front face\n    4, 5, 6,\n    4, 6, 7,\n    // Left face\n    0, 3, 7,\n    0, 7, 4,\n    // Right face\n    1, 5, 6,\n    1, 6, 2,\n    // Top face\n    3, 2, 6,\n    3, 6, 7,\n    // Bottom face\n    0, 1, 5,\n    0, 5, 4,\n};\n// clang-format on\n\n//-----------------------------------------------------------------------------\n// [SECTION] Sphere\n//-----------------------------------------------------------------------------\n\nImPlot3DPoint sphere_vtx[SPHERE_VTX_COUNT] = {\n    {-0.525731f, 0.850651f, 0.000000f},   {0.525731f, 0.850651f, 0.000000f},    {-0.525731f, -0.850651f, 0.000000f},\n    {0.525731f, -0.850651f, 0.000000f},   {0.000000f, -0.525731f, 0.850651f},   {0.000000f, 0.525731f, 0.850651f},\n    {0.000000f, -0.525731f, -0.850651f},  {0.000000f, 0.525731f, -0.850651f},   {0.850651f, 0.000000f, -0.525731f},\n    {0.850651f, 0.000000f, 0.525731f},    {-0.850651f, 0.000000f, -0.525731f},  {-0.850651f, 0.000000f, 0.525731f},\n    {-0.809017f, 0.500000f, 0.309017f},   {-0.500000f, 0.309017f, 0.809017f},   {-0.309017f, 0.809017f, 0.500000f},\n    {0.309017f, 0.809017f, 0.500000f},    {0.000000f, 1.000000f, 0.000000f},    {0.309017f, 0.809017f, -0.500000f},\n    {-0.309017f, 0.809017f, -0.500000f},  {-0.500000f, 0.309017f, -0.809017f},  {-0.809017f, 0.500000f, -0.309017f},\n    {-1.000000f, 0.000000f, 0.000000f},   {0.500000f, 0.309017f, 0.809017f},    {0.809017f, 0.500000f, 0.309017f},\n    {-0.500000f, -0.309017f, 0.809017f},  {0.000000f, 0.000000f, 1.000000f},    {-0.809017f, -0.500000f, -0.309017f},\n    {-0.809017f, -0.500000f, 0.309017f},  {0.000000f, 0.000000f, -1.000000f},   {-0.500000f, -0.309017f, -0.809017f},\n    {0.809017f, 0.500000f, -0.309017f},   {0.500000f, 0.309017f, -0.809017f},   {0.809017f, -0.500000f, 0.309017f},\n    {0.500000f, -0.309017f, 0.809017f},   {0.309017f, -0.809017f, 0.500000f},   {-0.309017f, -0.809017f, 0.500000f},\n    {0.000000f, -1.000000f, 0.000000f},   {-0.309017f, -0.809017f, -0.500000f}, {0.309017f, -0.809017f, -0.500000f},\n    {0.500000f, -0.309017f, -0.809017f},  {0.809017f, -0.500000f, -0.309017f},  {1.000000f, 0.000000f, 0.000000f},\n    {-0.693780f, 0.702046f, 0.160622f},   {-0.587785f, 0.688191f, 0.425325f},   {-0.433889f, 0.862668f, 0.259892f},\n    {-0.862668f, 0.259892f, 0.433889f},   {-0.702046f, 0.160622f, 0.693780f},   {-0.688191f, 0.425325f, 0.587785f},\n    {-0.425325f, 0.587785f, 0.688191f},   {-0.259892f, 0.433889f, 0.862668f},   {-0.160622f, 0.693780f, 0.702046f},\n    {-0.162460f, 0.951057f, 0.262866f},   {-0.273267f, 0.961938f, 0.000000f},   {0.160622f, 0.693780f, 0.702046f},\n    {0.000000f, 0.850651f, 0.525731f},    {0.162460f, 0.951057f, 0.262866f},    {0.433889f, 0.862668f, 0.259892f},\n    {0.273267f, 0.961938f, 0.000000f},    {-0.162460f, 0.951057f, -0.262866f},  {-0.433889f, 0.862668f, -0.259892f},\n    {0.433889f, 0.862668f, -0.259892f},   {0.162460f, 0.951057f, -0.262866f},   {0.000000f, 0.850651f, -0.525731f},\n    {0.160622f, 0.693780f, -0.702046f},   {-0.160622f, 0.693780f, -0.702046f},  {-0.587785f, 0.688191f, -0.425325f},\n    {-0.693780f, 0.702046f, -0.160622f},  {-0.259892f, 0.433889f, -0.862668f},  {-0.425325f, 0.587785f, -0.688191f},\n    {-0.688191f, 0.425325f, -0.587785f},  {-0.702046f, 0.160622f, -0.693780f},  {-0.862668f, 0.259892f, -0.433889f},\n    {-0.850651f, 0.525731f, 0.000000f},   {-0.961938f, 0.000000f, -0.273267f},  {-0.951057f, 0.262866f, -0.162460f},\n    {-0.951057f, 0.262866f, 0.162460f},   {-0.961938f, 0.000000f, 0.273267f},   {0.587785f, 0.688191f, 0.425325f},\n    {0.693780f, 0.702046f, 0.160622f},    {0.259892f, 0.433889f, 0.862668f},    {0.425325f, 0.587785f, 0.688191f},\n    {0.688191f, 0.425325f, 0.587785f},    {0.702046f, 0.160622f, 0.693780f},    {0.862668f, 0.259892f, 0.433889f},\n    {-0.262866f, 0.162460f, 0.951057f},   {0.000000f, 0.273267f, 0.961938f},    {-0.702046f, -0.160622f, 0.693780f},\n    {-0.525731f, 0.000000f, 0.850651f},   {-0.262866f, -0.162460f, 0.951057f},  {-0.259892f, -0.433889f, 0.862668f},\n    {0.000000f, -0.273267f, 0.961938f},   {-0.951057f, -0.262866f, 0.162460f},  {-0.862668f, -0.259892f, 0.433889f},\n    {-0.862668f, -0.259892f, -0.433889f}, {-0.951057f, -0.262866f, -0.162460f}, {-0.850651f, -0.525731f, 0.000000f},\n    {-0.693780f, -0.702046f, -0.160622f}, {-0.693780f, -0.702046f, 0.160622f},  {-0.525731f, 0.000000f, -0.850651f},\n    {-0.702046f, -0.160622f, -0.693780f}, {0.000000f, 0.273267f, -0.961938f},   {-0.262866f, 0.162460f, -0.951057f},\n    {-0.262866f, -0.162460f, -0.951057f}, {0.000000f, -0.273267f, -0.961938f},  {-0.259892f, -0.433889f, -0.862668f},\n    {0.425325f, 0.587785f, -0.688191f},   {0.259892f, 0.433889f, -0.862668f},   {0.693780f, 0.702046f, -0.160622f},\n    {0.587785f, 0.688191f, -0.425325f},   {0.688191f, 0.425325f, -0.587785f},   {0.862668f, 0.259892f, -0.433889f},\n    {0.702046f, 0.160622f, -0.693780f},   {0.693780f, -0.702046f, 0.160622f},   {0.587785f, -0.688191f, 0.425325f},\n    {0.433889f, -0.862668f, 0.259892f},   {0.862668f, -0.259892f, 0.433889f},   {0.702046f, -0.160622f, 0.693780f},\n    {0.688191f, -0.425325f, 0.587785f},   {0.425325f, -0.587785f, 0.688191f},   {0.259892f, -0.433889f, 0.862668f},\n    {0.160622f, -0.693780f, 0.702046f},   {0.162460f, -0.951057f, 0.262866f},   {0.273267f, -0.961938f, 0.000000f},\n    {-0.160622f, -0.693780f, 0.702046f},  {0.000000f, -0.850651f, 0.525731f},   {-0.162460f, -0.951057f, 0.262866f},\n    {-0.433889f, -0.862668f, 0.259892f},  {-0.273267f, -0.961938f, 0.000000f},  {0.162460f, -0.951057f, -0.262866f},\n    {0.433889f, -0.862668f, -0.259892f},  {-0.433889f, -0.862668f, -0.259892f}, {-0.162460f, -0.951057f, -0.262866f},\n    {0.000000f, -0.850651f, -0.525731f},  {-0.160622f, -0.693780f, -0.702046f}, {0.160622f, -0.693780f, -0.702046f},\n    {0.587785f, -0.688191f, -0.425325f},  {0.693780f, -0.702046f, -0.160622f},  {0.259892f, -0.433889f, -0.862668f},\n    {0.425325f, -0.587785f, -0.688191f},  {0.688191f, -0.425325f, -0.587785f},  {0.702046f, -0.160622f, -0.693780f},\n    {0.862668f, -0.259892f, -0.433889f},  {0.850651f, -0.525731f, 0.000000f},   {0.961938f, 0.000000f, -0.273267f},\n    {0.951057f, -0.262866f, -0.162460f},  {0.951057f, -0.262866f, 0.162460f},   {0.961938f, 0.000000f, 0.273267f},\n    {0.262866f, -0.162460f, 0.951057f},   {0.525731f, 0.000000f, 0.850651f},    {0.262866f, 0.162460f, 0.951057f},\n    {-0.587785f, -0.688191f, 0.425325f},  {-0.425325f, -0.587785f, 0.688191f},  {-0.688191f, -0.425325f, 0.587785f},\n    {-0.425325f, -0.587785f, -0.688191f}, {-0.587785f, -0.688191f, -0.425325f}, {-0.688191f, -0.425325f, -0.587785f},\n    {0.525731f, 0.000000f, -0.850651f},   {0.262866f, -0.162460f, -0.951057f},  {0.262866f, 0.162460f, -0.951057f},\n    {0.951057f, 0.262866f, 0.162460f},    {0.951057f, 0.262866f, -0.162460f},   {0.850651f, 0.525731f, 0.000000f},\n};\n\nunsigned int sphere_idx[SPHERE_IDX_COUNT] = {\n    0,   42,  44,  42,  12,  43,  44,  43,  14,  42,  43,  44,  12,  45,  47,  45,  11,  46,  47,  46,  13,  45,  46,  47,  14,  48,  50,  48,  13,\n    49,  50,  49,  5,   48,  49,  50,  12,  47,  43,  47,  13,  48,  43,  48,  14,  47,  48,  43,  0,   44,  52,  44,  14,  51,  52,  51,  16,  44,\n    51,  52,  14,  50,  54,  50,  5,   53,  54,  53,  15,  50,  53,  54,  16,  55,  57,  55,  15,  56,  57,  56,  1,   55,  56,  57,  14,  54,  51,\n    54,  15,  55,  51,  55,  16,  54,  55,  51,  0,   52,  59,  52,  16,  58,  59,  58,  18,  52,  58,  59,  16,  57,  61,  57,  1,   60,  61,  60,\n    17,  57,  60,  61,  18,  62,  64,  62,  17,  63,  64,  63,  7,   62,  63,  64,  16,  61,  58,  61,  17,  62,  58,  62,  18,  61,  62,  58,  0,\n    59,  66,  59,  18,  65,  66,  65,  20,  59,  65,  66,  18,  64,  68,  64,  7,   67,  68,  67,  19,  64,  67,  68,  20,  69,  71,  69,  19,  70,\n    71,  70,  10,  69,  70,  71,  18,  68,  65,  68,  19,  69,  65,  69,  20,  68,  69,  65,  0,   66,  42,  66,  20,  72,  42,  72,  12,  66,  72,\n    42,  20,  71,  74,  71,  10,  73,  74,  73,  21,  71,  73,  74,  12,  75,  45,  75,  21,  76,  45,  76,  11,  75,  76,  45,  20,  74,  72,  74,\n    21,  75,  72,  75,  12,  74,  75,  72,  1,   56,  78,  56,  15,  77,  78,  77,  23,  56,  77,  78,  15,  53,  80,  53,  5,   79,  80,  79,  22,\n    53,  79,  80,  23,  81,  83,  81,  22,  82,  83,  82,  9,   81,  82,  83,  15,  80,  77,  80,  22,  81,  77,  81,  23,  80,  81,  77,  5,   49,\n    85,  49,  13,  84,  85,  84,  25,  49,  84,  85,  13,  46,  87,  46,  11,  86,  87,  86,  24,  46,  86,  87,  25,  88,  90,  88,  24,  89,  90,\n    89,  4,   88,  89,  90,  13,  87,  84,  87,  24,  88,  84,  88,  25,  87,  88,  84,  11,  76,  92,  76,  21,  91,  92,  91,  27,  76,  91,  92,\n    21,  73,  94,  73,  10,  93,  94,  93,  26,  73,  93,  94,  27,  95,  97,  95,  26,  96,  97,  96,  2,   95,  96,  97,  21,  94,  91,  94,  26,\n    95,  91,  95,  27,  94,  95,  91,  10,  70,  99,  70,  19,  98,  99,  98,  29,  70,  98,  99,  19,  67,  101, 67,  7,   100, 101, 100, 28,  67,\n    100, 101, 29,  102, 104, 102, 28,  103, 104, 103, 6,   102, 103, 104, 19,  101, 98,  101, 28,  102, 98,  102, 29,  101, 102, 98,  7,   63,  106,\n    63,  17,  105, 106, 105, 31,  63,  105, 106, 17,  60,  108, 60,  1,   107, 108, 107, 30,  60,  107, 108, 31,  109, 111, 109, 30,  110, 111, 110,\n    8,   109, 110, 111, 17,  108, 105, 108, 30,  109, 105, 109, 31,  108, 109, 105, 3,   112, 114, 112, 32,  113, 114, 113, 34,  112, 113, 114, 32,\n    115, 117, 115, 9,   116, 117, 116, 33,  115, 116, 117, 34,  118, 120, 118, 33,  119, 120, 119, 4,   118, 119, 120, 32,  117, 113, 117, 33,  118,\n    113, 118, 34,  117, 118, 113, 3,   114, 122, 114, 34,  121, 122, 121, 36,  114, 121, 122, 34,  120, 124, 120, 4,   123, 124, 123, 35,  120, 123,\n    124, 36,  125, 127, 125, 35,  126, 127, 126, 2,   125, 126, 127, 34,  124, 121, 124, 35,  125, 121, 125, 36,  124, 125, 121, 3,   122, 129, 122,\n    36,  128, 129, 128, 38,  122, 128, 129, 36,  127, 131, 127, 2,   130, 131, 130, 37,  127, 130, 131, 38,  132, 134, 132, 37,  133, 134, 133, 6,\n    132, 133, 134, 36,  131, 128, 131, 37,  132, 128, 132, 38,  131, 132, 128, 3,   129, 136, 129, 38,  135, 136, 135, 40,  129, 135, 136, 38,  134,\n    138, 134, 6,   137, 138, 137, 39,  134, 137, 138, 40,  139, 141, 139, 39,  140, 141, 140, 8,   139, 140, 141, 38,  138, 135, 138, 39,  139, 135,\n    139, 40,  138, 139, 135, 3,   136, 112, 136, 40,  142, 112, 142, 32,  136, 142, 112, 40,  141, 144, 141, 8,   143, 144, 143, 41,  141, 143, 144,\n    32,  145, 115, 145, 41,  146, 115, 146, 9,   145, 146, 115, 40,  144, 142, 144, 41,  145, 142, 145, 32,  144, 145, 142, 4,   119, 90,  119, 33,\n    147, 90,  147, 25,  119, 147, 90,  33,  116, 148, 116, 9,   82,  148, 82,  22,  116, 82,  148, 25,  149, 85,  149, 22,  79,  85,  79,  5,   149,\n    79,  85,  33,  148, 147, 148, 22,  149, 147, 149, 25,  148, 149, 147, 2,   126, 97,  126, 35,  150, 97,  150, 27,  126, 150, 97,  35,  123, 151,\n    123, 4,   89,  151, 89,  24,  123, 89,  151, 27,  152, 92,  152, 24,  86,  92,  86,  11,  152, 86,  92,  35,  151, 150, 151, 24,  152, 150, 152,\n    27,  151, 152, 150, 6,   133, 104, 133, 37,  153, 104, 153, 29,  133, 153, 104, 37,  130, 154, 130, 2,   96,  154, 96,  26,  130, 96,  154, 29,\n    155, 99,  155, 26,  93,  99,  93,  10,  155, 93,  99,  37,  154, 153, 154, 26,  155, 153, 155, 29,  154, 155, 153, 8,   140, 111, 140, 39,  156,\n    111, 156, 31,  140, 156, 111, 39,  137, 157, 137, 6,   103, 157, 103, 28,  137, 103, 157, 31,  158, 106, 158, 28,  100, 106, 100, 7,   158, 100,\n    106, 39,  157, 156, 157, 28,  158, 156, 158, 31,  157, 158, 156, 9,   146, 83,  146, 41,  159, 83,  159, 23,  146, 159, 83,  41,  143, 160, 143,\n    8,   110, 160, 110, 30,  143, 110, 160, 23,  161, 78,  161, 30,  107, 78,  107, 1,   161, 107, 78,  41,  160, 159, 160, 30,  161, 159, 161, 23,\n    160, 161, 159};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Duck\n//-----------------------------------------------------------------------------\n// Rubber Duck by Poly by Google [CC-BY] via Poly Pizza\n\nImPlot3DPoint duck_vtx[DUCK_VTX_COUNT] = {\n    {0.263370f, 0.782383f, -0.488235f},   {0.358677f, -0.624396f, -0.546179f},  {0.292714f, 0.782558f, -0.125229f},\n    {0.349165f, -0.579430f, -0.151545f},  {0.000000f, 0.985995f, 0.055503f},    {0.000000f, -0.771795f, -0.060755f},\n    {0.000000f, 0.884807f, -0.562217f},   {0.000000f, -0.793724f, -0.609042f},  {0.000000f, -0.299071f, -0.806689f},\n    {0.416629f, -0.234865f, -0.718575f},  {0.386268f, -0.264259f, -0.087117f},  {0.000000f, -0.154521f, 0.030419f},\n    {0.170659f, -0.255441f, 0.007066f},   {0.180454f, -0.704124f, -0.061920f},  {0.155948f, 0.918253f, -0.001556f},\n    {0.155050f, -0.228032f, 0.751753f},   {0.151757f, -0.513087f, 0.744284f},   {0.000000f, -0.182899f, 0.779477f},\n    {0.000000f, -0.578330f, 0.764610f},   {0.000000f, 0.685169f, -0.739686f},   {0.371476f, 0.629238f, -0.627667f},\n    {0.331315f, 0.641641f, -0.175468f},   {0.206943f, 0.597766f, -0.089039f},   {0.000000f, 0.614709f, -0.054922f},\n    {0.000000f, -0.737692f, 0.299441f},   {0.283809f, -0.102660f, 0.299441f},   {0.000000f, 0.014558f, 0.299441f},\n    {0.000000f, -0.736425f, 0.474106f},   {0.278490f, -0.087547f, 0.474106f},   {0.000000f, 0.025780f, 0.474106f},\n    {0.164839f, 0.864781f, -0.528560f},   {0.230536f, 0.696343f, -0.704629f},   {0.220279f, -0.294500f, -0.788924f},\n    {0.216836f, -0.741049f, -0.579700f},  {0.238591f, -0.660227f, 0.299441f},   {0.244763f, -0.660211f, 0.474106f},\n    {0.000000f, -0.363444f, 0.812572f},   {0.163341f, -0.354552f, 0.773703f},   {0.374463f, -0.372350f, 0.474106f},\n    {0.379897f, -0.380938f, 0.299441f},   {0.289253f, -0.469584f, -0.048850f},  {0.399568f, -0.463659f, -0.141744f},\n    {0.416824f, -0.506064f, -0.619739f},  {0.226948f, -0.580280f, -0.719311f},  {0.000000f, -0.596101f, -0.755098f},\n    {0.199617f, 0.050031f, -0.023613f},   {0.000000f, 0.056331f, -0.812572f},   {0.221169f, 0.056088f, -0.800430f},\n    {0.414907f, 0.057214f, -0.741319f},   {0.383278f, 0.079881f, -0.106452f},   {0.000000f, 0.066068f, 0.011448f},\n    {0.485071f, -0.225034f, -0.615697f},  {0.474322f, -0.235931f, -0.130497f},  {0.451926f, -0.378909f, -0.182230f},\n    {0.464485f, -0.423127f, -0.533935f},  {0.376105f, 0.621725f, -0.201967f},   {0.458732f, 0.078611f, -0.144688f},\n    {0.414064f, 0.587520f, -0.543056f},   {0.482586f, 0.065612f, -0.653211f},   {0.000000f, -0.672915f, 0.096026f},\n    {0.146952f, -0.607627f, 0.096026f},   {0.261607f, -0.401487f, 0.096026f},   {0.193018f, -0.190607f, 0.096026f},\n    {0.000000f, -0.095407f, 0.096026f},   {0.000000f, -0.044418f, 0.665235f},   {0.241792f, -0.132120f, 0.640095f},\n    {0.312765f, -0.375473f, 0.640095f},   {0.214627f, -0.620202f, 0.640095f},   {0.000000f, -0.694664f, 0.658020f},\n    {0.000000f, -0.682358f, 0.030419f},   {0.138512f, -0.610831f, 0.030419f},   {0.234026f, -0.438399f, 0.030419f},\n    {0.000000f, 0.914951f, 0.077360f},    {0.160964f, 0.841809f, 0.024144f},    {0.193354f, 0.741696f, -0.037907f},\n    {0.000000f, 0.800313f, 0.001882f},    {0.000000f, 0.405613f, -0.811128f},   {0.229117f, 0.399656f, -0.791482f},\n    {0.409787f, 0.383452f, -0.704163f},   {0.458770f, 0.376363f, -0.605461f},   {0.385168f, 0.580561f, -0.185704f},\n    {0.344420f, 0.600331f, -0.166386f},   {0.437609f, 0.339612f, -0.230592f},   {0.386172f, 0.365055f, -0.180691f},\n    {0.203357f, 0.342793f, -0.082669f},   {0.000000f, 0.367574f, -0.043515f},   {0.368402f, 0.509624f, -0.189577f},\n    {0.408311f, 0.494489f, -0.224777f},   {0.317982f, -0.276765f, -0.036125f},  {0.000000f, -0.705078f, 0.171934f},\n    {0.194647f, -0.634647f, 0.171934f},   {0.332827f, -0.390756f, 0.171934f},   {0.252044f, -0.153800f, 0.171934f},\n    {0.000000f, -0.052325f, 0.171934f},   {0.000000f, -0.845971f, -0.319676f},  {0.204859f, -0.774310f, -0.326206f},\n    {0.382866f, -0.635409f, -0.351147f},  {0.437191f, -0.532057f, -0.362802f},  {0.277419f, 0.820517f, -0.362410f},\n    {0.165994f, 0.946464f, -0.306175f},   {0.366520f, 0.694046f, -0.383403f},   {0.000000f, 1.000000f, -0.305615f},\n    {0.554754f, 0.081200f, -0.400087f},   {0.548457f, -0.240710f, -0.371820f},  {0.496187f, -0.443697f, -0.357878f},\n    {0.410754f, 0.655281f, -0.382353f},   {0.501407f, 0.340701f, -0.406753f},   {-0.263370f, 0.782383f, -0.488235f},\n    {-0.358677f, -0.624396f, -0.546179f}, {-0.292714f, 0.782558f, -0.125229f},  {-0.349165f, -0.579430f, -0.151545f},\n    {-0.416629f, -0.234865f, -0.718575f}, {-0.386268f, -0.264259f, -0.087117f}, {-0.170659f, -0.255441f, 0.007066f},\n    {-0.180454f, -0.704124f, -0.061920f}, {-0.155948f, 0.918253f, -0.001556f},  {-0.155050f, -0.228032f, 0.751753f},\n    {-0.151757f, -0.513087f, 0.744284f},  {-0.371476f, 0.629238f, -0.627667f},  {-0.331315f, 0.641641f, -0.175468f},\n    {-0.206943f, 0.597766f, -0.089039f},  {-0.283809f, -0.102660f, 0.299441f},  {-0.278490f, -0.087547f, 0.474106f},\n    {-0.164839f, 0.864781f, -0.528560f},  {-0.230536f, 0.696343f, -0.704629f},  {-0.220279f, -0.294500f, -0.788924f},\n    {-0.216836f, -0.741049f, -0.579700f}, {-0.238591f, -0.660227f, 0.299441f},  {-0.244763f, -0.660211f, 0.474106f},\n    {-0.163341f, -0.354552f, 0.773703f},  {-0.374463f, -0.372350f, 0.474106f},  {-0.379897f, -0.380938f, 0.299441f},\n    {-0.289253f, -0.469584f, -0.048850f}, {-0.399568f, -0.463659f, -0.141744f}, {-0.416824f, -0.506064f, -0.619739f},\n    {-0.226948f, -0.580280f, -0.719311f}, {-0.199617f, 0.050031f, -0.023613f},  {-0.221169f, 0.056088f, -0.800430f},\n    {-0.414907f, 0.057214f, -0.741319f},  {-0.383278f, 0.079881f, -0.106452f},  {-0.485071f, -0.225034f, -0.615697f},\n    {-0.474322f, -0.235931f, -0.130497f}, {-0.451926f, -0.378909f, -0.182230f}, {-0.464485f, -0.423127f, -0.533935f},\n    {-0.376105f, 0.621725f, -0.201967f},  {-0.458732f, 0.078611f, -0.144688f},  {-0.414064f, 0.587520f, -0.543056f},\n    {-0.482586f, 0.065612f, -0.653211f},  {-0.146952f, -0.607627f, 0.096026f},  {-0.261606f, -0.401487f, 0.096026f},\n    {-0.193018f, -0.190607f, 0.096026f},  {-0.241792f, -0.132120f, 0.640095f},  {-0.312765f, -0.375473f, 0.640095f},\n    {-0.214627f, -0.620202f, 0.640095f},  {-0.138512f, -0.610831f, 0.030419f},  {-0.234026f, -0.438399f, 0.030419f},\n    {-0.160964f, 0.841809f, 0.024144f},   {-0.193354f, 0.741696f, -0.037907f},  {-0.229117f, 0.399656f, -0.791482f},\n    {-0.409787f, 0.383452f, -0.704163f},  {-0.458770f, 0.376363f, -0.605461f},  {-0.385168f, 0.580561f, -0.185704f},\n    {-0.344420f, 0.600331f, -0.166386f},  {-0.437609f, 0.339612f, -0.230592f},  {-0.386172f, 0.365055f, -0.180691f},\n    {-0.203357f, 0.342793f, -0.082669f},  {-0.368402f, 0.509624f, -0.189577f},  {-0.408311f, 0.494489f, -0.224777f},\n    {-0.317982f, -0.276765f, -0.036125f}, {-0.194647f, -0.634647f, 0.171934f},  {-0.332827f, -0.390756f, 0.171934f},\n    {-0.252044f, -0.153800f, 0.171934f},  {-0.204859f, -0.774310f, -0.326206f}, {-0.382866f, -0.635409f, -0.351147f},\n    {-0.437191f, -0.532057f, -0.362802f}, {-0.277419f, 0.820517f, -0.362410f},  {-0.165994f, 0.946464f, -0.306175f},\n    {-0.366520f, 0.694046f, -0.383403f},  {-0.554754f, 0.081200f, -0.400087f},  {-0.548457f, -0.240710f, -0.371820f},\n    {-0.496187f, -0.443697f, -0.357878f}, {-0.410754f, 0.655281f, -0.382353f},  {-0.501407f, 0.340701f, -0.406753f},\n    {-0.147251f, -0.668523f, 0.330326f},  {-0.047291f, -0.930817f, 0.472716f},  {-0.224222f, -0.665336f, 0.415767f},\n    {-0.075450f, -0.918607f, 0.533818f},  {-0.075797f, -0.674119f, 0.541030f},  {-0.096675f, -0.818748f, 0.395991f},\n    {-0.159031f, -0.798414f, 0.462927f},  {-0.223648f, -0.656683f, 0.331961f},  {-0.072544f, -0.935279f, 0.500884f},\n    {0.000000f, -0.961081f, 0.579077f},   {0.000000f, -0.726138f, 0.328549f},   {0.000000f, -0.969221f, 0.509585f},\n    {-0.176269f, -0.669386f, 0.471817f},  {-0.051675f, -0.862571f, 0.538304f},  {0.000000f, -0.680106f, 0.554423f},\n    {0.000000f, -0.974061f, 0.546160f},   {-0.152228f, -0.819158f, 0.418117f},  {0.000000f, -0.867770f, 0.561794f},\n    {0.000000f, -0.827887f, 0.404080f},   {-0.123466f, -0.788030f, 0.489190f},  {0.147251f, -0.668523f, 0.330326f},\n    {0.047291f, -0.930817f, 0.472716f},   {0.224222f, -0.665336f, 0.415767f},   {0.075450f, -0.918607f, 0.533818f},\n    {0.075797f, -0.674119f, 0.541030f},   {0.096675f, -0.818748f, 0.395991f},   {0.159031f, -0.798414f, 0.462927f},\n    {0.223648f, -0.656683f, 0.331961f},   {0.072544f, -0.935279f, 0.500884f},   {0.176269f, -0.669386f, 0.471817f},\n    {0.051675f, -0.862571f, 0.538304f},   {0.152228f, -0.819158f, 0.418117f},   {0.123466f, -0.788030f, 0.489190f},\n    {0.000000f, -0.994773f, 0.345730f},   {0.000000f, -0.647443f, 0.199203f},   {0.000000f, -0.986165f, 0.291566f},\n    {0.000000f, -0.703971f, 0.341994f},   {0.000000f, -1.000000f, 0.315210f},   {0.000000f, -0.787658f, 0.226787f},\n    {0.000000f, -0.961085f, 0.352303f},   {-0.133409f, -0.653443f, 0.222877f},  {-0.060090f, -0.929363f, 0.267589f},\n    {-0.224222f, -0.658853f, 0.342353f},  {-0.094843f, -0.932646f, 0.343060f},  {-0.151330f, -0.675024f, 0.346911f},\n    {0.000000f, -0.932532f, 0.323403f},   {-0.120579f, -0.791706f, 0.239478f},  {-0.187682f, -0.791724f, 0.341367f},\n    {-0.213355f, -0.651997f, 0.277443f},  {-0.092940f, -0.939719f, 0.301090f},  {-0.176269f, -0.666860f, 0.352303f},\n    {-0.130777f, -0.793601f, 0.328226f},  {-0.075297f, -0.913539f, 0.352303f},  {-0.182747f, -0.797038f, 0.284545f},\n    {-0.150873f, -0.786911f, 0.352303f},  {-0.055104f, -0.898766f, 0.325273f},  {0.133409f, -0.653443f, 0.222877f},\n    {0.060090f, -0.929363f, 0.267589f},   {0.224222f, -0.658853f, 0.342353f},   {0.094843f, -0.932646f, 0.343060f},\n    {0.151330f, -0.675024f, 0.346911f},   {0.120579f, -0.791706f, 0.239478f},   {0.187682f, -0.791724f, 0.341367f},\n    {0.213355f, -0.651997f, 0.277443f},   {0.092940f, -0.939719f, 0.301090f},   {0.176269f, -0.666860f, 0.352303f},\n    {0.130777f, -0.793601f, 0.328226f},   {0.075297f, -0.913539f, 0.352303f},   {0.182747f, -0.797038f, 0.284545f},\n    {0.150873f, -0.786911f, 0.352303f},   {0.055104f, -0.898766f, 0.325273f}};\n\nunsigned int duck_idx[DUCK_IDX_COUNT] = {\n    2,   73,  74,  2,   74,  21,  30,  0,   20,  30,  20,  31,  43,  42,  1,   43,  1,   33,  41,  40,  13,  41,  13,  3,   40,  71,  70,  40,  70,\n    13,  48,  9,   32,  48,  32,  47,  45,  88,  10,  45,  10,  49,  39,  38,  35,  39,  35,  34,  66,  37,  16,  66,  16,  67,  6,   30,  31,  6,\n    31,  19,  46,  47,  32,  46,  32,  8,   44,  43,  33,  44,  33,  7,   70,  69,  5,   70,  5,   13,  35,  27,  24,  35,  24,  34,  16,  18,  68,\n    16,  68,  67,  36,  18,  16,  36,  16,  37,  64,  17,  15,  64,  15,  65,  26,  29,  28,  26,  28,  25,  50,  11,  12,  50,  12,  45,  72,  75,\n    74,  72,  74,  73,  17,  36,  37,  17,  37,  15,  65,  15,  37,  65,  37,  66,  25,  28,  38,  25,  38,  39,  40,  88,  12,  40,  12,  71,  10,\n    88,  40,  10,  40,  41,  32,  9,   42,  32,  42,  43,  8,   32,  43,  8,   43,  44,  85,  84,  22,  85,  22,  23,  20,  78,  77,  20,  77,  31,\n    19,  31,  77,  19,  77,  76,  48,  58,  51,  48,  51,  9,   10,  52,  56,  10,  56,  49,  41,  53,  52,  41,  52,  10,  9,   51,  54,  9,   54,\n    42,  81,  80,  55,  81,  55,  21,  20,  57,  79,  20,  79,  78,  90,  89,  59,  90,  59,  60,  61,  91,  90,  61,  90,  60,  62,  92,  91,  62,\n    91,  61,  63,  93,  92,  63,  92,  62,  29,  64,  65,  29,  65,  28,  28,  65,  66,  28,  66,  38,  38,  66,  67,  38,  67,  35,  68,  27,  35,\n    68,  35,  67,  60,  59,  69,  60,  69,  70,  71,  61,  60,  71,  60,  70,  12,  62,  61,  12,  61,  71,  11,  63,  62,  11,  62,  12,  14,  4,\n    72,  14,  72,  73,  2,   14,  73,  22,  81,  21,  22,  21,  74,  75,  23,  22,  75,  22,  74,  83,  82,  87,  83,  87,  86,  76,  77,  47,  76,\n    47,  46,  78,  48,  47,  78,  47,  77,  79,  58,  48,  79,  48,  78,  49,  56,  82,  49,  82,  83,  84,  45,  49,  84,  49,  83,  85,  50,  45,\n    85,  45,  84,  87,  80,  81,  87,  81,  86,  22,  84,  83,  22,  83,  86,  45,  12,  88,  34,  24,  89,  34,  89,  90,  91,  39,  34,  91,  34,\n    90,  92,  25,  39,  92,  39,  91,  93,  26,  25,  93,  25,  92,  0,   98,  100, 0,   100, 20,  1,   96,  95,  1,   95,  33,  30,  99,  98,  30,\n    98,  0,   97,  96,  1,   97,  1,   42,  96,  3,   13,  96,  13,  95,  41,  3,   96,  41,  96,  97,  98,  2,   21,  98,  21,  100, 99,  14,  2,\n    99,  2,   98,  102, 103, 51,  102, 51,  58,  56,  52,  103, 56,  103, 102, 101, 4,   14,  101, 14,  99,  6,   101, 99,  6,   99,  30,  95,  94,\n    7,   95,  7,   33,  13,  5,   94,  13,  94,  95,  52,  53,  104, 52,  104, 103, 103, 104, 54,  103, 54,  51,  105, 106, 79,  105, 79,  57,  97,\n    104, 53,  97,  53,  41,  42,  54,  104, 42,  104, 97,  21,  55,  105, 21,  105, 100, 100, 105, 57,  100, 57,  20,  106, 105, 87,  106, 87,  82,\n    106, 102, 58,  106, 58,  79,  82,  56,  102, 82,  102, 106, 22,  86,  81,  80,  87,  105, 80,  105, 55,  109, 119, 157, 109, 157, 156, 123, 124,\n    118, 123, 118, 107, 135, 126, 108, 135, 108, 134, 133, 110, 114, 133, 114, 132, 132, 114, 154, 132, 154, 155, 138, 137, 125, 138, 125, 111, 136,\n    139, 112, 136, 112, 168, 131, 127, 128, 131, 128, 130, 152, 153, 117, 152, 117, 129, 6,   19,  124, 6,   124, 123, 46,  8,   125, 46,  125, 137,\n    44,  7,   126, 44,  126, 135, 154, 114, 5,   154, 5,   69,  128, 127, 24,  128, 24,  27,  117, 153, 68,  117, 68,  18,  36,  129, 117, 36,  117,\n    18,  64,  151, 116, 64,  116, 17,  26,  121, 122, 26,  122, 29,  50,  136, 113, 50,  113, 11,  72,  156, 157, 72,  157, 75,  17,  116, 129, 17,\n    129, 36,  151, 152, 129, 151, 129, 116, 121, 131, 130, 121, 130, 122, 132, 155, 113, 132, 113, 168, 112, 133, 132, 112, 132, 168, 125, 135, 134,\n    125, 134, 111, 8,   44,  135, 8,   135, 125, 85,  23,  120, 85,  120, 165, 118, 124, 158, 118, 158, 159, 19,  76,  158, 19,  158, 124, 138, 111,\n    140, 138, 140, 147, 112, 139, 145, 112, 145, 141, 133, 112, 141, 133, 141, 142, 111, 134, 143, 111, 143, 140, 162, 119, 144, 162, 144, 161, 118,\n    159, 160, 118, 160, 146, 169, 148, 59,  169, 59,  89,  149, 148, 169, 149, 169, 170, 150, 149, 170, 150, 170, 171, 63,  150, 171, 63,  171, 93,\n    29,  122, 151, 29,  151, 64,  122, 130, 152, 122, 152, 151, 130, 128, 153, 130, 153, 152, 68,  153, 128, 68,  128, 27,  148, 154, 69,  148, 69,\n    59,  155, 154, 148, 155, 148, 149, 113, 155, 149, 113, 149, 150, 11,  113, 150, 11,  150, 63,  115, 156, 72,  115, 72,  4,   109, 156, 115, 120,\n    157, 119, 120, 119, 162, 75,  157, 120, 75,  120, 23,  164, 166, 167, 164, 167, 163, 76,  46,  137, 76,  137, 158, 159, 158, 137, 159, 137, 138,\n    160, 159, 138, 160, 138, 147, 139, 164, 163, 139, 163, 145, 165, 164, 139, 165, 139, 136, 85,  165, 136, 85,  136, 50,  167, 166, 162, 167, 162,\n    161, 120, 166, 164, 120, 164, 165, 136, 168, 113, 127, 169, 89,  127, 89,  24,  170, 169, 127, 170, 127, 131, 171, 170, 131, 171, 131, 121, 93,\n    171, 121, 93,  121, 26,  107, 118, 177, 107, 177, 175, 108, 126, 172, 108, 172, 173, 123, 107, 175, 123, 175, 176, 174, 134, 108, 174, 108, 173,\n    173, 172, 114, 173, 114, 110, 133, 174, 173, 133, 173, 110, 175, 177, 119, 175, 119, 109, 176, 175, 109, 176, 109, 115, 178, 147, 140, 178, 140,\n    179, 145, 178, 179, 145, 179, 141, 101, 176, 115, 101, 115, 4,   6,   123, 176, 6,   176, 101, 172, 126, 7,   172, 7,   94,  114, 172, 94,  114,\n    94,  5,   141, 179, 180, 141, 180, 142, 179, 140, 143, 179, 143, 180, 181, 146, 160, 181, 160, 182, 174, 133, 142, 174, 142, 180, 134, 174, 180,\n    134, 180, 143, 119, 177, 181, 119, 181, 144, 177, 118, 146, 177, 146, 181, 182, 163, 167, 182, 167, 181, 182, 160, 147, 182, 147, 178, 163, 182,\n    178, 163, 178, 145, 120, 162, 166, 161, 144, 181, 161, 181, 167, 189, 185, 190, 189, 190, 199, 190, 183, 188, 190, 188, 199, 197, 187, 196, 197,\n    196, 200, 188, 183, 193, 188, 193, 201, 192, 186, 191, 192, 191, 198, 191, 184, 194, 191, 194, 198, 188, 184, 191, 188, 191, 199, 191, 186, 189,\n    191, 189, 199, 194, 184, 188, 194, 188, 201, 196, 187, 195, 196, 195, 202, 195, 185, 189, 195, 189, 202, 189, 186, 196, 189, 196, 202, 196, 186,\n    192, 196, 192, 200, 209, 214, 210, 209, 210, 205, 210, 214, 208, 210, 208, 203, 197, 200, 213, 197, 213, 207, 208, 201, 193, 208, 193, 203, 192,\n    198, 211, 192, 211, 206, 211, 198, 194, 211, 194, 204, 208, 214, 211, 208, 211, 204, 211, 214, 209, 211, 209, 206, 194, 201, 208, 194, 208, 204,\n    213, 215, 212, 213, 212, 207, 212, 215, 209, 212, 209, 205, 209, 215, 213, 209, 213, 206, 213, 200, 192, 213, 192, 206, 230, 225, 231, 230, 231,\n    236, 231, 223, 229, 231, 229, 236, 219, 227, 234, 229, 223, 217, 229, 217, 221, 216, 226, 232, 216, 232, 220, 232, 224, 218, 232, 218, 220, 229,\n    224, 232, 229, 232, 236, 232, 226, 230, 232, 230, 236, 234, 238, 228, 234, 228, 219, 218, 224, 229, 218, 229, 221, 234, 227, 233, 234, 233, 237,\n    233, 225, 230, 233, 230, 237, 230, 226, 235, 230, 235, 237, 235, 238, 234, 235, 234, 237, 228, 238, 235, 228, 235, 222, 235, 226, 216, 235, 216,\n    222, 245, 251, 246, 245, 246, 241, 246, 251, 244, 246, 244, 239, 219, 249, 243, 244, 221, 217, 244, 217, 239, 216, 220, 247, 216, 247, 242, 247,\n    220, 218, 247, 218, 240, 244, 251, 247, 244, 247, 240, 247, 251, 245, 247, 245, 242, 249, 219, 228, 249, 228, 253, 218, 221, 244, 218, 244, 240,\n    249, 252, 248, 249, 248, 243, 248, 252, 245, 248, 245, 241, 245, 252, 250, 245, 250, 242, 250, 252, 249, 250, 249, 253, 228, 222, 250, 228, 250,\n    253, 250, 222, 216, 250, 216, 242};\n\n} // namespace ImPlot3D\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\nproject(LLVMDemangle)\n\nset(CMAKE_CXX_STANDARD 17)\n\nadd_library(LLVMDemangle STATIC\n    source/Demangle.cpp\n    source/DLangDemangle.cpp\n    source/ItaniumDemangle.cpp\n    source/MicrosoftDemangle.cpp\n    source/MicrosoftDemangleNodes.cpp\n    source/RustDemangle.cpp\n)\n\ntarget_include_directories(LLVMDemangle PUBLIC include)\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/LICENSE.TXT",
    "content": "==============================================================================\nThe LLVM Project is under the Apache License v2.0 with LLVM Exceptions:\n==============================================================================\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n    1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n    2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n    3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n    4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n    5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n    6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n    7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n    8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n    9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n    END OF TERMS AND CONDITIONS\n\n    APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n    Copyright [yyyy] [name of copyright owner]\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n\n\n---- LLVM Exceptions to the Apache 2.0 License ----\n\nAs an exception, if, as a result of your compiling your source code, portions\nof this Software are embedded into an Object form of such source code, you\nmay redistribute such embedded portions in such Object form without complying\nwith the conditions of Sections 4(a), 4(b) and 4(d) of the License.\n\nIn addition, if you combine or link compiled forms of this Software with\nsoftware that is licensed under the GPLv2 (\"Combined Software\") and if a\ncourt of competent jurisdiction determines that the patent provision (Section\n3), the indemnity provision (Section 9) or other Section of the License\nconflicts with the conditions of the GPLv2, you may retroactively and\nprospectively choose to deem waived or otherwise exclude such Section(s) of\nthe License, but only in their entirety and only with respect to the Combined\nSoftware.\n\n==============================================================================\nSoftware from third parties included in the LLVM Project:\n==============================================================================\nThe LLVM Project contains third party software which is under different license\nterms. All such code will be identified clearly using at least one of two\nmechanisms:\n1) It will be in a separate directory tree with its own `LICENSE.txt` or\n   `LICENSE` file at the top containing the specific license and restrictions\n   which apply to that software, or\n2) It will contain specific license and restriction terms at the top of every\n   file.\n\n==============================================================================\nLegacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):\n==============================================================================\nUniversity of Illinois/NCSA\nOpen Source License\n\nCopyright (c) 2003-2019 University of Illinois at Urbana-Champaign.\nAll rights reserved.\n\nDeveloped by:\n\n    LLVM Team\n\n    University of Illinois at Urbana-Champaign\n\n    http://llvm.org\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal with\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\n    * Redistributions of source code must retain the above copyright notice,\n      this list of conditions and the following disclaimers.\n\n    * Redistributions in binary form must reproduce the above copyright notice,\n      this list of conditions and the following disclaimers in the\n      documentation and/or other materials provided with the distribution.\n\n    * Neither the names of the LLVM Team, University of Illinois at\n      Urbana-Champaign, nor the names of its contributors may be used to\n      endorse or promote products derived from this Software without specific\n      prior written permission.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nCONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE\nSOFTWARE.\n\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/include/llvm/Demangle/Demangle.h",
    "content": "//===--- Demangle.h ---------------------------------------------*- C++ -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef LLVM_DEMANGLE_DEMANGLE_H\n#define LLVM_DEMANGLE_DEMANGLE_H\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <string_view>\n\nnamespace llvm {\n/// This is a llvm local version of __cxa_demangle. Other than the name and\n/// being in the llvm namespace it is identical.\n///\n/// The mangled_name is demangled into buf and returned. If the buffer is not\n/// large enough, realloc is used to expand it.\n///\n/// The *status will be set to a value from the following enumeration\nenum : int {\n  demangle_unknown_error = -4,\n  demangle_invalid_args = -3,\n  demangle_invalid_mangled_name = -2,\n  demangle_memory_alloc_failure = -1,\n  demangle_success = 0,\n};\n\n/// Returns a non-NULL pointer to a NUL-terminated C style string\n/// that should be explicitly freed, if successful. Otherwise, may return\n/// nullptr if mangled_name is not a valid mangling or is nullptr.\nchar *itaniumDemangle(std::string_view mangled_name, bool ParseParams = true);\n\nenum MSDemangleFlags {\n  MSDF_None = 0,\n  MSDF_DumpBackrefs = 1 << 0,\n  MSDF_NoAccessSpecifier = 1 << 1,\n  MSDF_NoCallingConvention = 1 << 2,\n  MSDF_NoReturnType = 1 << 3,\n  MSDF_NoMemberType = 1 << 4,\n  MSDF_NoVariableType = 1 << 5,\n};\n\n/// Demangles the Microsoft symbol pointed at by mangled_name and returns it.\n/// Returns a pointer to the start of a null-terminated demangled string on\n/// success, or nullptr on error.\n/// If n_read is non-null and demangling was successful, it receives how many\n/// bytes of the input string were consumed.\n/// status receives one of the demangle_ enum entries above if it's not nullptr.\n/// Flags controls various details of the demangled representation.\nchar *microsoftDemangle(std::string_view mangled_name, size_t *n_read,\n                        int *status, MSDemangleFlags Flags = MSDF_None);\n\nstd::optional<size_t>\ngetArm64ECInsertionPointInMangledName(std::string_view MangledName);\n\n// Demangles a Rust v0 mangled symbol.\nchar *rustDemangle(std::string_view MangledName);\n\n// Demangles a D mangled symbol.\nchar *dlangDemangle(std::string_view MangledName);\n\n/// Attempt to demangle a string using different demangling schemes.\n/// The function uses heuristics to determine which demangling scheme to use.\n/// \\param MangledName - reference to string to demangle.\n/// \\returns - the demangled string, or a copy of the input string if no\n/// demangling occurred.\nstd::string demangle(std::string_view MangledName);\n\nbool nonMicrosoftDemangle(std::string_view MangledName, std::string &Result,\n                          bool CanHaveLeadingDot = true,\n                          bool ParseParams = true);\n\n/// \"Partial\" demangler. This supports demangling a string into an AST\n/// (typically an intermediate stage in itaniumDemangle) and querying certain\n/// properties or partially printing the demangled name.\nstruct ItaniumPartialDemangler {\n  ItaniumPartialDemangler();\n\n  ItaniumPartialDemangler(ItaniumPartialDemangler &&Other);\n  ItaniumPartialDemangler &operator=(ItaniumPartialDemangler &&Other);\n\n  /// Demangle into an AST. Subsequent calls to the rest of the member functions\n  /// implicitly operate on the AST this produces.\n  /// \\return true on error, false otherwise\n  bool partialDemangle(const char *MangledName);\n\n  /// Just print the entire mangled name into Buf. Buf and N behave like the\n  /// second and third parameters to __cxa_demangle.\n  char *finishDemangle(char *Buf, size_t *N) const;\n\n  /// Get the base name of a function. This doesn't include trailing template\n  /// arguments, ie for \"a::b<int>\" this function returns \"b\".\n  char *getFunctionBaseName(char *Buf, size_t *N) const;\n\n  /// Get the context name for a function. For \"a::b::c\", this function returns\n  /// \"a::b\".\n  char *getFunctionDeclContextName(char *Buf, size_t *N) const;\n\n  /// Get the entire name of this function.\n  char *getFunctionName(char *Buf, size_t *N) const;\n\n  /// Get the parameters for this function.\n  char *getFunctionParameters(char *Buf, size_t *N) const;\n  char *getFunctionReturnType(char *Buf, size_t *N) const;\n\n  /// If this function has any cv or reference qualifiers. These imply that\n  /// the function is a non-static member function.\n  bool hasFunctionQualifiers() const;\n\n  /// If this symbol describes a constructor or destructor.\n  bool isCtorOrDtor() const;\n\n  /// If this symbol describes a function.\n  bool isFunction() const;\n\n  /// If this symbol describes a variable.\n  bool isData() const;\n\n  /// If this symbol is a <special-name>. These are generally implicitly\n  /// generated by the implementation, such as vtables and typeinfo names.\n  bool isSpecialName() const;\n\n  ~ItaniumPartialDemangler();\n\nprivate:\n  void *RootNode;\n  void *Context;\n};\n} // namespace llvm\n\n#endif\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/include/llvm/Demangle/DemangleConfig.h",
    "content": "//===--- DemangleConfig.h ---------------------------------------*- C++ -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file contains a variety of feature test macros copied from\n// include/llvm/Support/Compiler.h so that LLVMDemangle does not need to take\n// a dependency on LLVMSupport.\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef LLVM_DEMANGLE_DEMANGLECONFIG_H\n#define LLVM_DEMANGLE_DEMANGLECONFIG_H\n\n#ifndef __has_feature\n#define __has_feature(x) 0\n#endif\n\n#ifndef __has_cpp_attribute\n#define __has_cpp_attribute(x) 0\n#endif\n\n#ifndef __has_attribute\n#define __has_attribute(x) 0\n#endif\n\n#ifndef __has_builtin\n#define __has_builtin(x) 0\n#endif\n\n#ifndef DEMANGLE_GNUC_PREREQ\n#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)\n#define DEMANGLE_GNUC_PREREQ(maj, min, patch)                           \\\n  ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >=          \\\n   ((maj) << 20) + ((min) << 10) + (patch))\n#elif defined(__GNUC__) && defined(__GNUC_MINOR__)\n#define DEMANGLE_GNUC_PREREQ(maj, min, patch)                           \\\n  ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10))\n#else\n#define DEMANGLE_GNUC_PREREQ(maj, min, patch) 0\n#endif\n#endif\n\n#if __has_attribute(used) || DEMANGLE_GNUC_PREREQ(3, 1, 0)\n#define DEMANGLE_ATTRIBUTE_USED __attribute__((__used__))\n#else\n#define DEMANGLE_ATTRIBUTE_USED\n#endif\n\n#if __has_builtin(__builtin_unreachable) || DEMANGLE_GNUC_PREREQ(4, 5, 0)\n#define DEMANGLE_UNREACHABLE __builtin_unreachable()\n#elif defined(_MSC_VER)\n#define DEMANGLE_UNREACHABLE __assume(false)\n#else\n#define DEMANGLE_UNREACHABLE\n#endif\n\n#if __has_attribute(noinline) || DEMANGLE_GNUC_PREREQ(3, 4, 0)\n#define DEMANGLE_ATTRIBUTE_NOINLINE __attribute__((noinline))\n#elif defined(_MSC_VER)\n#define DEMANGLE_ATTRIBUTE_NOINLINE __declspec(noinline)\n#else\n#define DEMANGLE_ATTRIBUTE_NOINLINE\n#endif\n\n#if !defined(NDEBUG)\n#define DEMANGLE_DUMP_METHOD DEMANGLE_ATTRIBUTE_NOINLINE DEMANGLE_ATTRIBUTE_USED\n#else\n#define DEMANGLE_DUMP_METHOD DEMANGLE_ATTRIBUTE_NOINLINE\n#endif\n\n#if __cplusplus > 201402L && __has_cpp_attribute(fallthrough)\n#define DEMANGLE_FALLTHROUGH [[fallthrough]]\n#elif __has_cpp_attribute(gnu::fallthrough)\n#define DEMANGLE_FALLTHROUGH [[gnu::fallthrough]]\n#elif !__cplusplus\n// Workaround for llvm.org/PR23435, since clang 3.6 and below emit a spurious\n// error when __has_cpp_attribute is given a scoped attribute in C mode.\n#define DEMANGLE_FALLTHROUGH\n#elif __has_cpp_attribute(clang::fallthrough)\n#define DEMANGLE_FALLTHROUGH [[clang::fallthrough]]\n#else\n#define DEMANGLE_FALLTHROUGH\n#endif\n\n#ifndef DEMANGLE_ASSERT\n#include <cassert>\n#define DEMANGLE_ASSERT(__expr, __msg) assert((__expr) && (__msg))\n#endif\n\n#define DEMANGLE_NAMESPACE_BEGIN namespace llvm { namespace itanium_demangle {\n#define DEMANGLE_NAMESPACE_END } }\n\n#endif\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/include/llvm/Demangle/ItaniumDemangle.h",
    "content": "//===--- ItaniumDemangle.h -----------*- mode:c++;eval:(read-only-mode) -*-===//\n//       Do not edit! See README.txt.\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// Generic itanium demangler library.\n// There are two copies of this file in the source tree.  The one under\n// libcxxabi is the original and the one under llvm is the copy.  Use\n// cp-to-llvm.sh to update the copy.  See README.txt for more details.\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef DEMANGLE_ITANIUMDEMANGLE_H\n#define DEMANGLE_ITANIUMDEMANGLE_H\n\n#include \"DemangleConfig.h\"\n#include \"StringViewExtras.h\"\n#include \"Utility.h\"\n#include <algorithm>\n#include <cctype>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <limits>\n#include <new>\n#include <string_view>\n#include <type_traits>\n#include <utility>\n\n#ifdef _LIBCXXABI_COMPILER_CLANG\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunused-template\"\n#endif\n\nDEMANGLE_NAMESPACE_BEGIN\n\ntemplate <class T, size_t N> class PODSmallVector {\n  static_assert(std::is_trivial<T>::value,\n                \"T is required to be a trivial type\");\n  T *First = nullptr;\n  T *Last = nullptr;\n  T *Cap = nullptr;\n  T Inline[N] = {};\n\n  bool isInline() const { return First == Inline; }\n\n  void clearInline() {\n    First = Inline;\n    Last = Inline;\n    Cap = Inline + N;\n  }\n\n  void reserve(size_t NewCap) {\n    size_t S = size();\n    if (isInline()) {\n      auto *Tmp = static_cast<T *>(std::malloc(NewCap * sizeof(T)));\n      if (Tmp == nullptr)\n        std::abort();\n      std::copy(First, Last, Tmp);\n      First = Tmp;\n    } else {\n      First = static_cast<T *>(std::realloc(First, NewCap * sizeof(T)));\n      if (First == nullptr)\n        std::abort();\n    }\n    Last = First + S;\n    Cap = First + NewCap;\n  }\n\npublic:\n  PODSmallVector() : First(Inline), Last(First), Cap(Inline + N) {}\n\n  PODSmallVector(const PODSmallVector &) = delete;\n  PODSmallVector &operator=(const PODSmallVector &) = delete;\n\n  PODSmallVector(PODSmallVector &&Other) : PODSmallVector() {\n    if (Other.isInline()) {\n      std::copy(Other.begin(), Other.end(), First);\n      Last = First + Other.size();\n      Other.clear();\n      return;\n    }\n\n    First = Other.First;\n    Last = Other.Last;\n    Cap = Other.Cap;\n    Other.clearInline();\n  }\n\n  PODSmallVector &operator=(PODSmallVector &&Other) {\n    if (Other.isInline()) {\n      if (!isInline()) {\n        std::free(First);\n        clearInline();\n      }\n      std::copy(Other.begin(), Other.end(), First);\n      Last = First + Other.size();\n      Other.clear();\n      return *this;\n    }\n\n    if (isInline()) {\n      First = Other.First;\n      Last = Other.Last;\n      Cap = Other.Cap;\n      Other.clearInline();\n      return *this;\n    }\n\n    std::swap(First, Other.First);\n    std::swap(Last, Other.Last);\n    std::swap(Cap, Other.Cap);\n    Other.clear();\n    return *this;\n  }\n\n  // NOLINTNEXTLINE(readability-identifier-naming)\n  void push_back(const T &Elem) {\n    if (Last == Cap)\n      reserve(size() * 2);\n    *Last++ = Elem;\n  }\n\n  // NOLINTNEXTLINE(readability-identifier-naming)\n  void pop_back() {\n    DEMANGLE_ASSERT(Last != First, \"Popping empty vector!\");\n    --Last;\n  }\n\n  void shrinkToSize(size_t Index) {\n    DEMANGLE_ASSERT(Index <= size(), \"shrinkToSize() can't expand!\");\n    Last = First + Index;\n  }\n\n  T *begin() { return First; }\n  T *end() { return Last; }\n\n  bool empty() const { return First == Last; }\n  size_t size() const { return static_cast<size_t>(Last - First); }\n  T &back() {\n    DEMANGLE_ASSERT(Last != First, \"Calling back() on empty vector!\");\n    return *(Last - 1);\n  }\n  T &operator[](size_t Index) {\n    DEMANGLE_ASSERT(Index < size(), \"Invalid access!\");\n    return *(begin() + Index);\n  }\n  void clear() { Last = First; }\n\n  ~PODSmallVector() {\n    if (!isInline())\n      std::free(First);\n  }\n};\n\n// Base class of all AST nodes. The AST is built by the parser, then is\n// traversed by the printLeft/Right functions to produce a demangled string.\nclass Node {\npublic:\n  enum Kind : unsigned char {\n#define NODE(NodeKind) K##NodeKind,\n#include \"ItaniumNodes.def\"\n  };\n\n  /// Three-way bool to track a cached value. Unknown is possible if this node\n  /// has an unexpanded parameter pack below it that may affect this cache.\n  enum class Cache : unsigned char { Yes, No, Unknown, };\n\n  /// Operator precedence for expression nodes. Used to determine required\n  /// parens in expression emission.\n  enum class Prec {\n    Primary,\n    Postfix,\n    Unary,\n    Cast,\n    PtrMem,\n    Multiplicative,\n    Additive,\n    Shift,\n    Spaceship,\n    Relational,\n    Equality,\n    And,\n    Xor,\n    Ior,\n    AndIf,\n    OrIf,\n    Conditional,\n    Assign,\n    Comma,\n    Default,\n  };\n\nprivate:\n  Kind K;\n\n  Prec Precedence : 6;\n\n  // FIXME: Make these protected.\npublic:\n  /// Tracks if this node has a component on its right side, in which case we\n  /// need to call printRight.\n  Cache RHSComponentCache : 2;\n\n  /// Track if this node is a (possibly qualified) array type. This can affect\n  /// how we format the output string.\n  Cache ArrayCache : 2;\n\n  /// Track if this node is a (possibly qualified) function type. This can\n  /// affect how we format the output string.\n  Cache FunctionCache : 2;\n\npublic:\n  Node(Kind K_, Prec Precedence_ = Prec::Primary,\n       Cache RHSComponentCache_ = Cache::No, Cache ArrayCache_ = Cache::No,\n       Cache FunctionCache_ = Cache::No)\n      : K(K_), Precedence(Precedence_), RHSComponentCache(RHSComponentCache_),\n        ArrayCache(ArrayCache_), FunctionCache(FunctionCache_) {}\n  Node(Kind K_, Cache RHSComponentCache_, Cache ArrayCache_ = Cache::No,\n       Cache FunctionCache_ = Cache::No)\n      : Node(K_, Prec::Primary, RHSComponentCache_, ArrayCache_,\n             FunctionCache_) {}\n\n  /// Visit the most-derived object corresponding to this object.\n  template<typename Fn> void visit(Fn F) const;\n\n  // The following function is provided by all derived classes:\n  //\n  // Call F with arguments that, when passed to the constructor of this node,\n  // would construct an equivalent node.\n  //template<typename Fn> void match(Fn F) const;\n\n  bool hasRHSComponent(OutputBuffer &OB) const {\n    if (RHSComponentCache != Cache::Unknown)\n      return RHSComponentCache == Cache::Yes;\n    return hasRHSComponentSlow(OB);\n  }\n\n  bool hasArray(OutputBuffer &OB) const {\n    if (ArrayCache != Cache::Unknown)\n      return ArrayCache == Cache::Yes;\n    return hasArraySlow(OB);\n  }\n\n  bool hasFunction(OutputBuffer &OB) const {\n    if (FunctionCache != Cache::Unknown)\n      return FunctionCache == Cache::Yes;\n    return hasFunctionSlow(OB);\n  }\n\n  Kind getKind() const { return K; }\n\n  Prec getPrecedence() const { return Precedence; }\n\n  virtual bool hasRHSComponentSlow(OutputBuffer &) const { return false; }\n  virtual bool hasArraySlow(OutputBuffer &) const { return false; }\n  virtual bool hasFunctionSlow(OutputBuffer &) const { return false; }\n\n  // Dig through \"glue\" nodes like ParameterPack and ForwardTemplateReference to\n  // get at a node that actually represents some concrete syntax.\n  virtual const Node *getSyntaxNode(OutputBuffer &) const { return this; }\n\n  // Print this node as an expression operand, surrounding it in parentheses if\n  // its precedence is [Strictly] weaker than P.\n  void printAsOperand(OutputBuffer &OB, Prec P = Prec::Default,\n                      bool StrictlyWorse = false) const {\n    bool Paren =\n        unsigned(getPrecedence()) >= unsigned(P) + unsigned(StrictlyWorse);\n    if (Paren)\n      OB.printOpen();\n    print(OB);\n    if (Paren)\n      OB.printClose();\n  }\n\n  void print(OutputBuffer &OB) const {\n    printLeft(OB);\n    if (RHSComponentCache != Cache::No)\n      printRight(OB);\n  }\n\n  // Print the \"left\" side of this Node into OutputBuffer.\n  virtual void printLeft(OutputBuffer &) const = 0;\n\n  // Print the \"right\". This distinction is necessary to represent C++ types\n  // that appear on the RHS of their subtype, such as arrays or functions.\n  // Since most types don't have such a component, provide a default\n  // implementation.\n  virtual void printRight(OutputBuffer &) const {}\n\n  virtual std::string_view getBaseName() const { return {}; }\n\n  // Silence compiler warnings, this dtor will never be called.\n  virtual ~Node() = default;\n\n#ifndef NDEBUG\n  DEMANGLE_DUMP_METHOD void dump() const;\n#endif\n};\n\nclass NodeArray {\n  Node **Elements;\n  size_t NumElements;\n\npublic:\n  NodeArray() : Elements(nullptr), NumElements(0) {}\n  NodeArray(Node **Elements_, size_t NumElements_)\n      : Elements(Elements_), NumElements(NumElements_) {}\n\n  bool empty() const { return NumElements == 0; }\n  size_t size() const { return NumElements; }\n\n  Node **begin() const { return Elements; }\n  Node **end() const { return Elements + NumElements; }\n\n  Node *operator[](size_t Idx) const { return Elements[Idx]; }\n\n  void printWithComma(OutputBuffer &OB) const {\n    bool FirstElement = true;\n    for (size_t Idx = 0; Idx != NumElements; ++Idx) {\n      size_t BeforeComma = OB.getCurrentPosition();\n      if (!FirstElement)\n        OB += \", \";\n      size_t AfterComma = OB.getCurrentPosition();\n      Elements[Idx]->printAsOperand(OB, Node::Prec::Comma);\n\n      // Elements[Idx] is an empty parameter pack expansion, we should erase the\n      // comma we just printed.\n      if (AfterComma == OB.getCurrentPosition()) {\n        OB.setCurrentPosition(BeforeComma);\n        continue;\n      }\n\n      FirstElement = false;\n    }\n  }\n};\n\nstruct NodeArrayNode : Node {\n  NodeArray Array;\n  NodeArrayNode(NodeArray Array_) : Node(KNodeArrayNode), Array(Array_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Array); }\n\n  void printLeft(OutputBuffer &OB) const override { Array.printWithComma(OB); }\n};\n\nclass DotSuffix final : public Node {\n  const Node *Prefix;\n  const std::string_view Suffix;\n\npublic:\n  DotSuffix(const Node *Prefix_, std::string_view Suffix_)\n      : Node(KDotSuffix), Prefix(Prefix_), Suffix(Suffix_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Prefix, Suffix); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Prefix->print(OB);\n    OB += \" (\";\n    OB += Suffix;\n    OB += \")\";\n  }\n};\n\nclass VendorExtQualType final : public Node {\n  const Node *Ty;\n  std::string_view Ext;\n  const Node *TA;\n\npublic:\n  VendorExtQualType(const Node *Ty_, std::string_view Ext_, const Node *TA_)\n      : Node(KVendorExtQualType), Ty(Ty_), Ext(Ext_), TA(TA_) {}\n\n  const Node *getTy() const { return Ty; }\n  std::string_view getExt() const { return Ext; }\n  const Node *getTA() const { return TA; }\n\n  template <typename Fn> void match(Fn F) const { F(Ty, Ext, TA); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Ty->print(OB);\n    OB += \" \";\n    OB += Ext;\n    if (TA != nullptr)\n      TA->print(OB);\n  }\n};\n\nenum FunctionRefQual : unsigned char {\n  FrefQualNone,\n  FrefQualLValue,\n  FrefQualRValue,\n};\n\nenum Qualifiers {\n  QualNone = 0,\n  QualConst = 0x1,\n  QualVolatile = 0x2,\n  QualRestrict = 0x4,\n};\n\ninline Qualifiers operator|=(Qualifiers &Q1, Qualifiers Q2) {\n  return Q1 = static_cast<Qualifiers>(Q1 | Q2);\n}\n\nclass QualType final : public Node {\nprotected:\n  const Qualifiers Quals;\n  const Node *Child;\n\n  void printQuals(OutputBuffer &OB) const {\n    if (Quals & QualConst)\n      OB += \" const\";\n    if (Quals & QualVolatile)\n      OB += \" volatile\";\n    if (Quals & QualRestrict)\n      OB += \" restrict\";\n  }\n\npublic:\n  QualType(const Node *Child_, Qualifiers Quals_)\n      : Node(KQualType, Child_->RHSComponentCache,\n             Child_->ArrayCache, Child_->FunctionCache),\n        Quals(Quals_), Child(Child_) {}\n\n  Qualifiers getQuals() const { return Quals; }\n  const Node *getChild() const { return Child; }\n\n  template<typename Fn> void match(Fn F) const { F(Child, Quals); }\n\n  bool hasRHSComponentSlow(OutputBuffer &OB) const override {\n    return Child->hasRHSComponent(OB);\n  }\n  bool hasArraySlow(OutputBuffer &OB) const override {\n    return Child->hasArray(OB);\n  }\n  bool hasFunctionSlow(OutputBuffer &OB) const override {\n    return Child->hasFunction(OB);\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Child->printLeft(OB);\n    printQuals(OB);\n  }\n\n  void printRight(OutputBuffer &OB) const override { Child->printRight(OB); }\n};\n\nclass ConversionOperatorType final : public Node {\n  const Node *Ty;\n\npublic:\n  ConversionOperatorType(const Node *Ty_)\n      : Node(KConversionOperatorType), Ty(Ty_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Ty); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"operator \";\n    Ty->print(OB);\n  }\n};\n\nclass PostfixQualifiedType final : public Node {\n  const Node *Ty;\n  const std::string_view Postfix;\n\npublic:\n  PostfixQualifiedType(const Node *Ty_, std::string_view Postfix_)\n      : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Ty, Postfix); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Ty->printLeft(OB);\n    OB += Postfix;\n  }\n};\n\nclass NameType final : public Node {\n  const std::string_view Name;\n\npublic:\n  NameType(std::string_view Name_) : Node(KNameType), Name(Name_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Name); }\n\n  std::string_view getName() const { return Name; }\n  std::string_view getBaseName() const override { return Name; }\n\n  void printLeft(OutputBuffer &OB) const override { OB += Name; }\n};\n\nclass BitIntType final : public Node {\n  const Node *Size;\n  bool Signed;\n\npublic:\n  BitIntType(const Node *Size_, bool Signed_)\n      : Node(KBitIntType), Size(Size_), Signed(Signed_) {}\n\n  template <typename Fn> void match(Fn F) const { F(Size, Signed); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    if (!Signed)\n      OB += \"unsigned \";\n    OB += \"_BitInt\";\n    OB.printOpen();\n    Size->printAsOperand(OB);\n    OB.printClose();\n  }\n};\n\nclass ElaboratedTypeSpefType : public Node {\n  std::string_view Kind;\n  Node *Child;\npublic:\n  ElaboratedTypeSpefType(std::string_view Kind_, Node *Child_)\n      : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Kind, Child); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += Kind;\n    OB += ' ';\n    Child->print(OB);\n  }\n};\n\nclass TransformedType : public Node {\n  std::string_view Transform;\n  Node *BaseType;\npublic:\n  TransformedType(std::string_view Transform_, Node *BaseType_)\n      : Node(KTransformedType), Transform(Transform_), BaseType(BaseType_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Transform, BaseType); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += Transform;\n    OB += '(';\n    BaseType->print(OB);\n    OB += ')';\n  }\n};\n\nstruct AbiTagAttr : Node {\n  Node *Base;\n  std::string_view Tag;\n\n  AbiTagAttr(Node *Base_, std::string_view Tag_)\n      : Node(KAbiTagAttr, Base_->RHSComponentCache, Base_->ArrayCache,\n             Base_->FunctionCache),\n        Base(Base_), Tag(Tag_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Base, Tag); }\n\n  std::string_view getBaseName() const override { return Base->getBaseName(); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Base->printLeft(OB);\n    OB += \"[abi:\";\n    OB += Tag;\n    OB += \"]\";\n  }\n};\n\nclass EnableIfAttr : public Node {\n  NodeArray Conditions;\npublic:\n  EnableIfAttr(NodeArray Conditions_)\n      : Node(KEnableIfAttr), Conditions(Conditions_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Conditions); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \" [enable_if:\";\n    Conditions.printWithComma(OB);\n    OB += ']';\n  }\n};\n\nclass ObjCProtoName : public Node {\n  const Node *Ty;\n  std::string_view Protocol;\n\n  friend class PointerType;\n\npublic:\n  ObjCProtoName(const Node *Ty_, std::string_view Protocol_)\n      : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Ty, Protocol); }\n\n  bool isObjCObject() const {\n    return Ty->getKind() == KNameType &&\n           static_cast<const NameType *>(Ty)->getName() == \"objc_object\";\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Ty->print(OB);\n    OB += \"<\";\n    OB += Protocol;\n    OB += \">\";\n  }\n};\n\nclass PointerType final : public Node {\n  const Node *Pointee;\n\npublic:\n  PointerType(const Node *Pointee_)\n      : Node(KPointerType, Pointee_->RHSComponentCache),\n        Pointee(Pointee_) {}\n\n  const Node *getPointee() const { return Pointee; }\n\n  template<typename Fn> void match(Fn F) const { F(Pointee); }\n\n  bool hasRHSComponentSlow(OutputBuffer &OB) const override {\n    return Pointee->hasRHSComponent(OB);\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    // We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>.\n    if (Pointee->getKind() != KObjCProtoName ||\n        !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {\n      Pointee->printLeft(OB);\n      if (Pointee->hasArray(OB))\n        OB += \" \";\n      if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))\n        OB += \"(\";\n      OB += \"*\";\n    } else {\n      const auto *objcProto = static_cast<const ObjCProtoName *>(Pointee);\n      OB += \"id<\";\n      OB += objcProto->Protocol;\n      OB += \">\";\n    }\n  }\n\n  void printRight(OutputBuffer &OB) const override {\n    if (Pointee->getKind() != KObjCProtoName ||\n        !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {\n      if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))\n        OB += \")\";\n      Pointee->printRight(OB);\n    }\n  }\n};\n\nenum class ReferenceKind {\n  LValue,\n  RValue,\n};\n\n// Represents either a LValue or an RValue reference type.\nclass ReferenceType : public Node {\n  const Node *Pointee;\n  ReferenceKind RK;\n\n  mutable bool Printing = false;\n\n  // Dig through any refs to refs, collapsing the ReferenceTypes as we go. The\n  // rule here is rvalue ref to rvalue ref collapses to a rvalue ref, and any\n  // other combination collapses to a lvalue ref.\n  //\n  // A combination of a TemplateForwardReference and a back-ref Substitution\n  // from an ill-formed string may have created a cycle; use cycle detection to\n  // avoid looping forever.\n  std::pair<ReferenceKind, const Node *> collapse(OutputBuffer &OB) const {\n    auto SoFar = std::make_pair(RK, Pointee);\n    // Track the chain of nodes for the Floyd's 'tortoise and hare'\n    // cycle-detection algorithm, since getSyntaxNode(S) is impure\n    PODSmallVector<const Node *, 8> Prev;\n    for (;;) {\n      const Node *SN = SoFar.second->getSyntaxNode(OB);\n      if (SN->getKind() != KReferenceType)\n        break;\n      auto *RT = static_cast<const ReferenceType *>(SN);\n      SoFar.second = RT->Pointee;\n      SoFar.first = std::min(SoFar.first, RT->RK);\n\n      // The middle of Prev is the 'slow' pointer moving at half speed\n      Prev.push_back(SoFar.second);\n      if (Prev.size() > 1 && SoFar.second == Prev[(Prev.size() - 1) / 2]) {\n        // Cycle detected\n        SoFar.second = nullptr;\n        break;\n      }\n    }\n    return SoFar;\n  }\n\npublic:\n  ReferenceType(const Node *Pointee_, ReferenceKind RK_)\n      : Node(KReferenceType, Pointee_->RHSComponentCache),\n        Pointee(Pointee_), RK(RK_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Pointee, RK); }\n\n  bool hasRHSComponentSlow(OutputBuffer &OB) const override {\n    return Pointee->hasRHSComponent(OB);\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    if (Printing)\n      return;\n    ScopedOverride<bool> SavePrinting(Printing, true);\n    std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB);\n    if (!Collapsed.second)\n      return;\n    Collapsed.second->printLeft(OB);\n    if (Collapsed.second->hasArray(OB))\n      OB += \" \";\n    if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))\n      OB += \"(\";\n\n    OB += (Collapsed.first == ReferenceKind::LValue ? \"&\" : \"&&\");\n  }\n  void printRight(OutputBuffer &OB) const override {\n    if (Printing)\n      return;\n    ScopedOverride<bool> SavePrinting(Printing, true);\n    std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB);\n    if (!Collapsed.second)\n      return;\n    if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))\n      OB += \")\";\n    Collapsed.second->printRight(OB);\n  }\n};\n\nclass PointerToMemberType final : public Node {\n  const Node *ClassType;\n  const Node *MemberType;\n\npublic:\n  PointerToMemberType(const Node *ClassType_, const Node *MemberType_)\n      : Node(KPointerToMemberType, MemberType_->RHSComponentCache),\n        ClassType(ClassType_), MemberType(MemberType_) {}\n\n  template<typename Fn> void match(Fn F) const { F(ClassType, MemberType); }\n\n  bool hasRHSComponentSlow(OutputBuffer &OB) const override {\n    return MemberType->hasRHSComponent(OB);\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    MemberType->printLeft(OB);\n    if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))\n      OB += \"(\";\n    else\n      OB += \" \";\n    ClassType->print(OB);\n    OB += \"::*\";\n  }\n\n  void printRight(OutputBuffer &OB) const override {\n    if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))\n      OB += \")\";\n    MemberType->printRight(OB);\n  }\n};\n\nclass ArrayType final : public Node {\n  const Node *Base;\n  Node *Dimension;\n\npublic:\n  ArrayType(const Node *Base_, Node *Dimension_)\n      : Node(KArrayType,\n             /*RHSComponentCache=*/Cache::Yes,\n             /*ArrayCache=*/Cache::Yes),\n        Base(Base_), Dimension(Dimension_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Base, Dimension); }\n\n  bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }\n  bool hasArraySlow(OutputBuffer &) const override { return true; }\n\n  void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); }\n\n  void printRight(OutputBuffer &OB) const override {\n    if (OB.back() != ']')\n      OB += \" \";\n    OB += \"[\";\n    if (Dimension)\n      Dimension->print(OB);\n    OB += \"]\";\n    Base->printRight(OB);\n  }\n};\n\nclass FunctionType final : public Node {\n  const Node *Ret;\n  NodeArray Params;\n  Qualifiers CVQuals;\n  FunctionRefQual RefQual;\n  const Node *ExceptionSpec;\n\npublic:\n  FunctionType(const Node *Ret_, NodeArray Params_, Qualifiers CVQuals_,\n               FunctionRefQual RefQual_, const Node *ExceptionSpec_)\n      : Node(KFunctionType,\n             /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No,\n             /*FunctionCache=*/Cache::Yes),\n        Ret(Ret_), Params(Params_), CVQuals(CVQuals_), RefQual(RefQual_),\n        ExceptionSpec(ExceptionSpec_) {}\n\n  template<typename Fn> void match(Fn F) const {\n    F(Ret, Params, CVQuals, RefQual, ExceptionSpec);\n  }\n\n  bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }\n  bool hasFunctionSlow(OutputBuffer &) const override { return true; }\n\n  // Handle C++'s ... quirky decl grammar by using the left & right\n  // distinction. Consider:\n  //   int (*f(float))(char) {}\n  // f is a function that takes a float and returns a pointer to a function\n  // that takes a char and returns an int. If we're trying to print f, start\n  // by printing out the return types's left, then print our parameters, then\n  // finally print right of the return type.\n  void printLeft(OutputBuffer &OB) const override {\n    Ret->printLeft(OB);\n    OB += \" \";\n  }\n\n  void printRight(OutputBuffer &OB) const override {\n    OB.printOpen();\n    Params.printWithComma(OB);\n    OB.printClose();\n    Ret->printRight(OB);\n\n    if (CVQuals & QualConst)\n      OB += \" const\";\n    if (CVQuals & QualVolatile)\n      OB += \" volatile\";\n    if (CVQuals & QualRestrict)\n      OB += \" restrict\";\n\n    if (RefQual == FrefQualLValue)\n      OB += \" &\";\n    else if (RefQual == FrefQualRValue)\n      OB += \" &&\";\n\n    if (ExceptionSpec != nullptr) {\n      OB += ' ';\n      ExceptionSpec->print(OB);\n    }\n  }\n};\n\nclass NoexceptSpec : public Node {\n  const Node *E;\npublic:\n  NoexceptSpec(const Node *E_) : Node(KNoexceptSpec), E(E_) {}\n\n  template<typename Fn> void match(Fn F) const { F(E); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"noexcept\";\n    OB.printOpen();\n    E->printAsOperand(OB);\n    OB.printClose();\n  }\n};\n\nclass DynamicExceptionSpec : public Node {\n  NodeArray Types;\npublic:\n  DynamicExceptionSpec(NodeArray Types_)\n      : Node(KDynamicExceptionSpec), Types(Types_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Types); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"throw\";\n    OB.printOpen();\n    Types.printWithComma(OB);\n    OB.printClose();\n  }\n};\n\n/// Represents the explicitly named object parameter.\n/// E.g.,\n/// \\code{.cpp}\n///   struct Foo {\n///     void bar(this Foo && self);\n///   };\n/// \\endcode\nclass ExplicitObjectParameter final : public Node {\n  Node *Base;\n\npublic:\n  ExplicitObjectParameter(Node *Base_)\n      : Node(KExplicitObjectParameter), Base(Base_) {\n    DEMANGLE_ASSERT(\n        Base != nullptr,\n        \"Creating an ExplicitObjectParameter without a valid Base Node.\");\n  }\n\n  template <typename Fn> void match(Fn F) const { F(Base); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"this \";\n    Base->print(OB);\n  }\n};\n\nclass FunctionEncoding final : public Node {\n  const Node *Ret;\n  const Node *Name;\n  NodeArray Params;\n  const Node *Attrs;\n  const Node *Requires;\n  Qualifiers CVQuals;\n  FunctionRefQual RefQual;\n\npublic:\n  FunctionEncoding(const Node *Ret_, const Node *Name_, NodeArray Params_,\n                   const Node *Attrs_, const Node *Requires_,\n                   Qualifiers CVQuals_, FunctionRefQual RefQual_)\n      : Node(KFunctionEncoding,\n             /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No,\n             /*FunctionCache=*/Cache::Yes),\n        Ret(Ret_), Name(Name_), Params(Params_), Attrs(Attrs_),\n        Requires(Requires_), CVQuals(CVQuals_), RefQual(RefQual_) {}\n\n  template<typename Fn> void match(Fn F) const {\n    F(Ret, Name, Params, Attrs, Requires, CVQuals, RefQual);\n  }\n\n  Qualifiers getCVQuals() const { return CVQuals; }\n  FunctionRefQual getRefQual() const { return RefQual; }\n  NodeArray getParams() const { return Params; }\n  const Node *getReturnType() const { return Ret; }\n\n  bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }\n  bool hasFunctionSlow(OutputBuffer &) const override { return true; }\n\n  const Node *getName() const { return Name; }\n\n  void printLeft(OutputBuffer &OB) const override {\n    if (Ret) {\n      Ret->printLeft(OB);\n      if (!Ret->hasRHSComponent(OB))\n        OB += \" \";\n    }\n    Name->print(OB);\n  }\n\n  void printRight(OutputBuffer &OB) const override {\n    OB.printOpen();\n    Params.printWithComma(OB);\n    OB.printClose();\n    if (Ret)\n      Ret->printRight(OB);\n\n    if (CVQuals & QualConst)\n      OB += \" const\";\n    if (CVQuals & QualVolatile)\n      OB += \" volatile\";\n    if (CVQuals & QualRestrict)\n      OB += \" restrict\";\n\n    if (RefQual == FrefQualLValue)\n      OB += \" &\";\n    else if (RefQual == FrefQualRValue)\n      OB += \" &&\";\n\n    if (Attrs != nullptr)\n      Attrs->print(OB);\n\n    if (Requires != nullptr) {\n      OB += \" requires \";\n      Requires->print(OB);\n    }\n  }\n};\n\nclass LiteralOperator : public Node {\n  const Node *OpName;\n\npublic:\n  LiteralOperator(const Node *OpName_)\n      : Node(KLiteralOperator), OpName(OpName_) {}\n\n  template<typename Fn> void match(Fn F) const { F(OpName); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"operator\\\"\\\" \";\n    OpName->print(OB);\n  }\n};\n\nclass SpecialName final : public Node {\n  const std::string_view Special;\n  const Node *Child;\n\npublic:\n  SpecialName(std::string_view Special_, const Node *Child_)\n      : Node(KSpecialName), Special(Special_), Child(Child_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Special, Child); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += Special;\n    Child->print(OB);\n  }\n};\n\nclass CtorVtableSpecialName final : public Node {\n  const Node *FirstType;\n  const Node *SecondType;\n\npublic:\n  CtorVtableSpecialName(const Node *FirstType_, const Node *SecondType_)\n      : Node(KCtorVtableSpecialName),\n        FirstType(FirstType_), SecondType(SecondType_) {}\n\n  template<typename Fn> void match(Fn F) const { F(FirstType, SecondType); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"construction vtable for \";\n    FirstType->print(OB);\n    OB += \"-in-\";\n    SecondType->print(OB);\n  }\n};\n\nstruct NestedName : Node {\n  Node *Qual;\n  Node *Name;\n\n  NestedName(Node *Qual_, Node *Name_)\n      : Node(KNestedName), Qual(Qual_), Name(Name_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Qual, Name); }\n\n  std::string_view getBaseName() const override { return Name->getBaseName(); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Qual->print(OB);\n    OB += \"::\";\n    Name->print(OB);\n  }\n};\n\nstruct MemberLikeFriendName : Node {\n  Node *Qual;\n  Node *Name;\n\n  MemberLikeFriendName(Node *Qual_, Node *Name_)\n      : Node(KMemberLikeFriendName), Qual(Qual_), Name(Name_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Qual, Name); }\n\n  std::string_view getBaseName() const override { return Name->getBaseName(); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Qual->print(OB);\n    OB += \"::friend \";\n    Name->print(OB);\n  }\n};\n\nstruct ModuleName : Node {\n  ModuleName *Parent;\n  Node *Name;\n  bool IsPartition;\n\n  ModuleName(ModuleName *Parent_, Node *Name_, bool IsPartition_ = false)\n      : Node(KModuleName), Parent(Parent_), Name(Name_),\n        IsPartition(IsPartition_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(Parent, Name, IsPartition);\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    if (Parent)\n      Parent->print(OB);\n    if (Parent || IsPartition)\n      OB += IsPartition ? ':' : '.';\n    Name->print(OB);\n  }\n};\n\nstruct ModuleEntity : Node {\n  ModuleName *Module;\n  Node *Name;\n\n  ModuleEntity(ModuleName *Module_, Node *Name_)\n      : Node(KModuleEntity), Module(Module_), Name(Name_) {}\n\n  template <typename Fn> void match(Fn F) const { F(Module, Name); }\n\n  std::string_view getBaseName() const override { return Name->getBaseName(); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Name->print(OB);\n    OB += '@';\n    Module->print(OB);\n  }\n};\n\nstruct LocalName : Node {\n  Node *Encoding;\n  Node *Entity;\n\n  LocalName(Node *Encoding_, Node *Entity_)\n      : Node(KLocalName), Encoding(Encoding_), Entity(Entity_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Encoding, Entity); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Encoding->print(OB);\n    OB += \"::\";\n    Entity->print(OB);\n  }\n};\n\nclass QualifiedName final : public Node {\n  // qualifier::name\n  const Node *Qualifier;\n  const Node *Name;\n\npublic:\n  QualifiedName(const Node *Qualifier_, const Node *Name_)\n      : Node(KQualifiedName), Qualifier(Qualifier_), Name(Name_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Qualifier, Name); }\n\n  std::string_view getBaseName() const override { return Name->getBaseName(); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Qualifier->print(OB);\n    OB += \"::\";\n    Name->print(OB);\n  }\n};\n\nclass VectorType final : public Node {\n  const Node *BaseType;\n  const Node *Dimension;\n\npublic:\n  VectorType(const Node *BaseType_, const Node *Dimension_)\n      : Node(KVectorType), BaseType(BaseType_), Dimension(Dimension_) {}\n\n  const Node *getBaseType() const { return BaseType; }\n  const Node *getDimension() const { return Dimension; }\n\n  template<typename Fn> void match(Fn F) const { F(BaseType, Dimension); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    BaseType->print(OB);\n    OB += \" vector[\";\n    if (Dimension)\n      Dimension->print(OB);\n    OB += \"]\";\n  }\n};\n\nclass PixelVectorType final : public Node {\n  const Node *Dimension;\n\npublic:\n  PixelVectorType(const Node *Dimension_)\n      : Node(KPixelVectorType), Dimension(Dimension_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Dimension); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    // FIXME: This should demangle as \"vector pixel\".\n    OB += \"pixel vector[\";\n    Dimension->print(OB);\n    OB += \"]\";\n  }\n};\n\nclass BinaryFPType final : public Node {\n  const Node *Dimension;\n\npublic:\n  BinaryFPType(const Node *Dimension_)\n      : Node(KBinaryFPType), Dimension(Dimension_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Dimension); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"_Float\";\n    Dimension->print(OB);\n  }\n};\n\nenum class TemplateParamKind { Type, NonType, Template };\n\n/// An invented name for a template parameter for which we don't have a\n/// corresponding template argument.\n///\n/// This node is created when parsing the <lambda-sig> for a lambda with\n/// explicit template arguments, which might be referenced in the parameter\n/// types appearing later in the <lambda-sig>.\nclass SyntheticTemplateParamName final : public Node {\n  TemplateParamKind Kind;\n  unsigned Index;\n\npublic:\n  SyntheticTemplateParamName(TemplateParamKind Kind_, unsigned Index_)\n      : Node(KSyntheticTemplateParamName), Kind(Kind_), Index(Index_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Kind, Index); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    switch (Kind) {\n    case TemplateParamKind::Type:\n      OB += \"$T\";\n      break;\n    case TemplateParamKind::NonType:\n      OB += \"$N\";\n      break;\n    case TemplateParamKind::Template:\n      OB += \"$TT\";\n      break;\n    }\n    if (Index > 0)\n      OB << Index - 1;\n  }\n};\n\nclass TemplateParamQualifiedArg final : public Node {\n  Node *Param;\n  Node *Arg;\n\npublic:\n  TemplateParamQualifiedArg(Node *Param_, Node *Arg_)\n      : Node(KTemplateParamQualifiedArg), Param(Param_), Arg(Arg_) {}\n\n  template <typename Fn> void match(Fn F) const { F(Param, Arg); }\n\n  Node *getArg() { return Arg; }\n\n  void printLeft(OutputBuffer &OB) const override {\n    // Don't print Param to keep the output consistent.\n    Arg->print(OB);\n  }\n};\n\n/// A template type parameter declaration, 'typename T'.\nclass TypeTemplateParamDecl final : public Node {\n  Node *Name;\n\npublic:\n  TypeTemplateParamDecl(Node *Name_)\n      : Node(KTypeTemplateParamDecl, Cache::Yes), Name(Name_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Name); }\n\n  void printLeft(OutputBuffer &OB) const override { OB += \"typename \"; }\n\n  void printRight(OutputBuffer &OB) const override { Name->print(OB); }\n};\n\n/// A constrained template type parameter declaration, 'C<U> T'.\nclass ConstrainedTypeTemplateParamDecl final : public Node {\n  Node *Constraint;\n  Node *Name;\n\npublic:\n  ConstrainedTypeTemplateParamDecl(Node *Constraint_, Node *Name_)\n      : Node(KConstrainedTypeTemplateParamDecl, Cache::Yes),\n        Constraint(Constraint_), Name(Name_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Constraint, Name); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Constraint->print(OB);\n    OB += \" \";\n  }\n\n  void printRight(OutputBuffer &OB) const override { Name->print(OB); }\n};\n\n/// A non-type template parameter declaration, 'int N'.\nclass NonTypeTemplateParamDecl final : public Node {\n  Node *Name;\n  Node *Type;\n\npublic:\n  NonTypeTemplateParamDecl(Node *Name_, Node *Type_)\n      : Node(KNonTypeTemplateParamDecl, Cache::Yes), Name(Name_), Type(Type_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Name, Type); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Type->printLeft(OB);\n    if (!Type->hasRHSComponent(OB))\n      OB += \" \";\n  }\n\n  void printRight(OutputBuffer &OB) const override {\n    Name->print(OB);\n    Type->printRight(OB);\n  }\n};\n\n/// A template template parameter declaration,\n/// 'template<typename T> typename N'.\nclass TemplateTemplateParamDecl final : public Node {\n  Node *Name;\n  NodeArray Params;\n  Node *Requires;\n\npublic:\n  TemplateTemplateParamDecl(Node *Name_, NodeArray Params_, Node *Requires_)\n      : Node(KTemplateTemplateParamDecl, Cache::Yes), Name(Name_),\n        Params(Params_), Requires(Requires_) {}\n\n  template <typename Fn> void match(Fn F) const { F(Name, Params, Requires); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    ScopedOverride<unsigned> LT(OB.GtIsGt, 0);\n    OB += \"template<\";\n    Params.printWithComma(OB);\n    OB += \"> typename \";\n  }\n\n  void printRight(OutputBuffer &OB) const override {\n    Name->print(OB);\n    if (Requires != nullptr) {\n      OB += \" requires \";\n      Requires->print(OB);\n    }\n  }\n};\n\n/// A template parameter pack declaration, 'typename ...T'.\nclass TemplateParamPackDecl final : public Node {\n  Node *Param;\n\npublic:\n  TemplateParamPackDecl(Node *Param_)\n      : Node(KTemplateParamPackDecl, Cache::Yes), Param(Param_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Param); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Param->printLeft(OB);\n    OB += \"...\";\n  }\n\n  void printRight(OutputBuffer &OB) const override { Param->printRight(OB); }\n};\n\n/// An unexpanded parameter pack (either in the expression or type context). If\n/// this AST is correct, this node will have a ParameterPackExpansion node above\n/// it.\n///\n/// This node is created when some <template-args> are found that apply to an\n/// <encoding>, and is stored in the TemplateParams table. In order for this to\n/// appear in the final AST, it has to referenced via a <template-param> (ie,\n/// T_).\nclass ParameterPack final : public Node {\n  NodeArray Data;\n\n  // Setup OutputBuffer for a pack expansion, unless we're already expanding\n  // one.\n  void initializePackExpansion(OutputBuffer &OB) const {\n    if (OB.CurrentPackMax == std::numeric_limits<unsigned>::max()) {\n      OB.CurrentPackMax = static_cast<unsigned>(Data.size());\n      OB.CurrentPackIndex = 0;\n    }\n  }\n\npublic:\n  ParameterPack(NodeArray Data_) : Node(KParameterPack), Data(Data_) {\n    ArrayCache = FunctionCache = RHSComponentCache = Cache::Unknown;\n    if (std::all_of(Data.begin(), Data.end(), [](Node* P) {\n          return P->ArrayCache == Cache::No;\n        }))\n      ArrayCache = Cache::No;\n    if (std::all_of(Data.begin(), Data.end(), [](Node* P) {\n          return P->FunctionCache == Cache::No;\n        }))\n      FunctionCache = Cache::No;\n    if (std::all_of(Data.begin(), Data.end(), [](Node* P) {\n          return P->RHSComponentCache == Cache::No;\n        }))\n      RHSComponentCache = Cache::No;\n  }\n\n  template<typename Fn> void match(Fn F) const { F(Data); }\n\n  bool hasRHSComponentSlow(OutputBuffer &OB) const override {\n    initializePackExpansion(OB);\n    size_t Idx = OB.CurrentPackIndex;\n    return Idx < Data.size() && Data[Idx]->hasRHSComponent(OB);\n  }\n  bool hasArraySlow(OutputBuffer &OB) const override {\n    initializePackExpansion(OB);\n    size_t Idx = OB.CurrentPackIndex;\n    return Idx < Data.size() && Data[Idx]->hasArray(OB);\n  }\n  bool hasFunctionSlow(OutputBuffer &OB) const override {\n    initializePackExpansion(OB);\n    size_t Idx = OB.CurrentPackIndex;\n    return Idx < Data.size() && Data[Idx]->hasFunction(OB);\n  }\n  const Node *getSyntaxNode(OutputBuffer &OB) const override {\n    initializePackExpansion(OB);\n    size_t Idx = OB.CurrentPackIndex;\n    return Idx < Data.size() ? Data[Idx]->getSyntaxNode(OB) : this;\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    initializePackExpansion(OB);\n    size_t Idx = OB.CurrentPackIndex;\n    if (Idx < Data.size())\n      Data[Idx]->printLeft(OB);\n  }\n  void printRight(OutputBuffer &OB) const override {\n    initializePackExpansion(OB);\n    size_t Idx = OB.CurrentPackIndex;\n    if (Idx < Data.size())\n      Data[Idx]->printRight(OB);\n  }\n};\n\n/// A variadic template argument. This node represents an occurrence of\n/// J<something>E in some <template-args>. It isn't itself unexpanded, unless\n/// one of its Elements is. The parser inserts a ParameterPack into the\n/// TemplateParams table if the <template-args> this pack belongs to apply to an\n/// <encoding>.\nclass TemplateArgumentPack final : public Node {\n  NodeArray Elements;\npublic:\n  TemplateArgumentPack(NodeArray Elements_)\n      : Node(KTemplateArgumentPack), Elements(Elements_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Elements); }\n\n  NodeArray getElements() const { return Elements; }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Elements.printWithComma(OB);\n  }\n};\n\n/// A pack expansion. Below this node, there are some unexpanded ParameterPacks\n/// which each have Child->ParameterPackSize elements.\nclass ParameterPackExpansion final : public Node {\n  const Node *Child;\n\npublic:\n  ParameterPackExpansion(const Node *Child_)\n      : Node(KParameterPackExpansion), Child(Child_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Child); }\n\n  const Node *getChild() const { return Child; }\n\n  void printLeft(OutputBuffer &OB) const override {\n    constexpr unsigned Max = std::numeric_limits<unsigned>::max();\n    ScopedOverride<unsigned> SavePackIdx(OB.CurrentPackIndex, Max);\n    ScopedOverride<unsigned> SavePackMax(OB.CurrentPackMax, Max);\n    size_t StreamPos = OB.getCurrentPosition();\n\n    // Print the first element in the pack. If Child contains a ParameterPack,\n    // it will set up S.CurrentPackMax and print the first element.\n    Child->print(OB);\n\n    // No ParameterPack was found in Child. This can occur if we've found a pack\n    // expansion on a <function-param>.\n    if (OB.CurrentPackMax == Max) {\n      OB += \"...\";\n      return;\n    }\n\n    // We found a ParameterPack, but it has no elements. Erase whatever we may\n    // of printed.\n    if (OB.CurrentPackMax == 0) {\n      OB.setCurrentPosition(StreamPos);\n      return;\n    }\n\n    // Else, iterate through the rest of the elements in the pack.\n    for (unsigned I = 1, E = OB.CurrentPackMax; I < E; ++I) {\n      OB += \", \";\n      OB.CurrentPackIndex = I;\n      Child->print(OB);\n    }\n  }\n};\n\nclass TemplateArgs final : public Node {\n  NodeArray Params;\n  Node *Requires;\n\npublic:\n  TemplateArgs(NodeArray Params_, Node *Requires_)\n      : Node(KTemplateArgs), Params(Params_), Requires(Requires_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Params, Requires); }\n\n  NodeArray getParams() { return Params; }\n\n  void printLeft(OutputBuffer &OB) const override {\n    ScopedOverride<unsigned> LT(OB.GtIsGt, 0);\n    OB += \"<\";\n    Params.printWithComma(OB);\n    OB += \">\";\n    // Don't print the requires clause to keep the output simple.\n  }\n};\n\n/// A forward-reference to a template argument that was not known at the point\n/// where the template parameter name was parsed in a mangling.\n///\n/// This is created when demangling the name of a specialization of a\n/// conversion function template:\n///\n/// \\code\n/// struct A {\n///   template<typename T> operator T*();\n/// };\n/// \\endcode\n///\n/// When demangling a specialization of the conversion function template, we\n/// encounter the name of the template (including the \\c T) before we reach\n/// the template argument list, so we cannot substitute the parameter name\n/// for the corresponding argument while parsing. Instead, we create a\n/// \\c ForwardTemplateReference node that is resolved after we parse the\n/// template arguments.\nstruct ForwardTemplateReference : Node {\n  size_t Index;\n  Node *Ref = nullptr;\n\n  // If we're currently printing this node. It is possible (though invalid) for\n  // a forward template reference to refer to itself via a substitution. This\n  // creates a cyclic AST, which will stack overflow printing. To fix this, bail\n  // out if more than one print* function is active.\n  mutable bool Printing = false;\n\n  ForwardTemplateReference(size_t Index_)\n      : Node(KForwardTemplateReference, Cache::Unknown, Cache::Unknown,\n             Cache::Unknown),\n        Index(Index_) {}\n\n  // We don't provide a matcher for these, because the value of the node is\n  // not determined by its construction parameters, and it generally needs\n  // special handling.\n  template<typename Fn> void match(Fn F) const = delete;\n\n  bool hasRHSComponentSlow(OutputBuffer &OB) const override {\n    if (Printing)\n      return false;\n    ScopedOverride<bool> SavePrinting(Printing, true);\n    return Ref->hasRHSComponent(OB);\n  }\n  bool hasArraySlow(OutputBuffer &OB) const override {\n    if (Printing)\n      return false;\n    ScopedOverride<bool> SavePrinting(Printing, true);\n    return Ref->hasArray(OB);\n  }\n  bool hasFunctionSlow(OutputBuffer &OB) const override {\n    if (Printing)\n      return false;\n    ScopedOverride<bool> SavePrinting(Printing, true);\n    return Ref->hasFunction(OB);\n  }\n  const Node *getSyntaxNode(OutputBuffer &OB) const override {\n    if (Printing)\n      return this;\n    ScopedOverride<bool> SavePrinting(Printing, true);\n    return Ref->getSyntaxNode(OB);\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    if (Printing)\n      return;\n    ScopedOverride<bool> SavePrinting(Printing, true);\n    Ref->printLeft(OB);\n  }\n  void printRight(OutputBuffer &OB) const override {\n    if (Printing)\n      return;\n    ScopedOverride<bool> SavePrinting(Printing, true);\n    Ref->printRight(OB);\n  }\n};\n\nstruct NameWithTemplateArgs : Node {\n  // name<template_args>\n  Node *Name;\n  Node *TemplateArgs;\n\n  NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_)\n      : Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Name, TemplateArgs); }\n\n  std::string_view getBaseName() const override { return Name->getBaseName(); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Name->print(OB);\n    TemplateArgs->print(OB);\n  }\n};\n\nclass GlobalQualifiedName final : public Node {\n  Node *Child;\n\npublic:\n  GlobalQualifiedName(Node* Child_)\n      : Node(KGlobalQualifiedName), Child(Child_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Child); }\n\n  std::string_view getBaseName() const override { return Child->getBaseName(); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"::\";\n    Child->print(OB);\n  }\n};\n\nenum class SpecialSubKind {\n  allocator,\n  basic_string,\n  string,\n  istream,\n  ostream,\n  iostream,\n};\n\nclass SpecialSubstitution;\nclass ExpandedSpecialSubstitution : public Node {\nprotected:\n  SpecialSubKind SSK;\n\n  ExpandedSpecialSubstitution(SpecialSubKind SSK_, Kind K_)\n      : Node(K_), SSK(SSK_) {}\npublic:\n  ExpandedSpecialSubstitution(SpecialSubKind SSK_)\n      : ExpandedSpecialSubstitution(SSK_, KExpandedSpecialSubstitution) {}\n  inline ExpandedSpecialSubstitution(SpecialSubstitution const *);\n\n  template<typename Fn> void match(Fn F) const { F(SSK); }\n\nprotected:\n  bool isInstantiation() const {\n    return unsigned(SSK) >= unsigned(SpecialSubKind::string);\n  }\n\n  std::string_view getBaseName() const override {\n    switch (SSK) {\n    case SpecialSubKind::allocator:\n      return {\"allocator\"};\n    case SpecialSubKind::basic_string:\n      return {\"basic_string\"};\n    case SpecialSubKind::string:\n      return {\"basic_string\"};\n    case SpecialSubKind::istream:\n      return {\"basic_istream\"};\n    case SpecialSubKind::ostream:\n      return {\"basic_ostream\"};\n    case SpecialSubKind::iostream:\n      return {\"basic_iostream\"};\n    }\n    DEMANGLE_UNREACHABLE;\n  }\n\nprivate:\n  void printLeft(OutputBuffer &OB) const override {\n    OB << \"std::\" << getBaseName();\n    if (isInstantiation()) {\n      OB << \"<char, std::char_traits<char>\";\n      if (SSK == SpecialSubKind::string)\n        OB << \", std::allocator<char>\";\n      OB << \">\";\n    }\n  }\n};\n\nclass SpecialSubstitution final : public ExpandedSpecialSubstitution {\npublic:\n  SpecialSubstitution(SpecialSubKind SSK_)\n      : ExpandedSpecialSubstitution(SSK_, KSpecialSubstitution) {}\n\n  template<typename Fn> void match(Fn F) const { F(SSK); }\n\n  std::string_view getBaseName() const override {\n    std::string_view SV = ExpandedSpecialSubstitution::getBaseName();\n    if (isInstantiation()) {\n      // The instantiations are typedefs that drop the \"basic_\" prefix.\n      DEMANGLE_ASSERT(starts_with(SV, \"basic_\"), \"\");\n      SV.remove_prefix(sizeof(\"basic_\") - 1);\n    }\n    return SV;\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB << \"std::\" << getBaseName();\n  }\n};\n\ninline ExpandedSpecialSubstitution::ExpandedSpecialSubstitution(\n    SpecialSubstitution const *SS)\n    : ExpandedSpecialSubstitution(SS->SSK) {}\n\nclass CtorDtorName final : public Node {\n  const Node *Basename;\n  const bool IsDtor;\n  const int Variant;\n\npublic:\n  CtorDtorName(const Node *Basename_, bool IsDtor_, int Variant_)\n      : Node(KCtorDtorName), Basename(Basename_), IsDtor(IsDtor_),\n        Variant(Variant_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    if (IsDtor)\n      OB += \"~\";\n    OB += Basename->getBaseName();\n  }\n};\n\nclass DtorName : public Node {\n  const Node *Base;\n\npublic:\n  DtorName(const Node *Base_) : Node(KDtorName), Base(Base_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Base); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"~\";\n    Base->printLeft(OB);\n  }\n};\n\nclass UnnamedTypeName : public Node {\n  const std::string_view Count;\n\npublic:\n  UnnamedTypeName(std::string_view Count_)\n      : Node(KUnnamedTypeName), Count(Count_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Count); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"'unnamed\";\n    OB += Count;\n    OB += \"\\'\";\n  }\n};\n\nclass ClosureTypeName : public Node {\n  NodeArray TemplateParams;\n  const Node *Requires1;\n  NodeArray Params;\n  const Node *Requires2;\n  std::string_view Count;\n\npublic:\n  ClosureTypeName(NodeArray TemplateParams_, const Node *Requires1_,\n                  NodeArray Params_, const Node *Requires2_,\n                  std::string_view Count_)\n      : Node(KClosureTypeName), TemplateParams(TemplateParams_),\n        Requires1(Requires1_), Params(Params_), Requires2(Requires2_),\n        Count(Count_) {}\n\n  template<typename Fn> void match(Fn F) const {\n    F(TemplateParams, Requires1, Params, Requires2, Count);\n  }\n\n  void printDeclarator(OutputBuffer &OB) const {\n    if (!TemplateParams.empty()) {\n      ScopedOverride<unsigned> LT(OB.GtIsGt, 0);\n      OB += \"<\";\n      TemplateParams.printWithComma(OB);\n      OB += \">\";\n    }\n    if (Requires1 != nullptr) {\n      OB += \" requires \";\n      Requires1->print(OB);\n      OB += \" \";\n    }\n    OB.printOpen();\n    Params.printWithComma(OB);\n    OB.printClose();\n    if (Requires2 != nullptr) {\n      OB += \" requires \";\n      Requires2->print(OB);\n    }\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    // FIXME: This demangling is not particularly readable.\n    OB += \"\\'lambda\";\n    OB += Count;\n    OB += \"\\'\";\n    printDeclarator(OB);\n  }\n};\n\nclass StructuredBindingName : public Node {\n  NodeArray Bindings;\npublic:\n  StructuredBindingName(NodeArray Bindings_)\n      : Node(KStructuredBindingName), Bindings(Bindings_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Bindings); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB.printOpen('[');\n    Bindings.printWithComma(OB);\n    OB.printClose(']');\n  }\n};\n\n// -- Expression Nodes --\n\nclass BinaryExpr : public Node {\n  const Node *LHS;\n  const std::string_view InfixOperator;\n  const Node *RHS;\n\npublic:\n  BinaryExpr(const Node *LHS_, std::string_view InfixOperator_,\n             const Node *RHS_, Prec Prec_)\n      : Node(KBinaryExpr, Prec_), LHS(LHS_), InfixOperator(InfixOperator_),\n        RHS(RHS_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(LHS, InfixOperator, RHS, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    bool ParenAll = OB.isGtInsideTemplateArgs() &&\n                    (InfixOperator == \">\" || InfixOperator == \">>\");\n    if (ParenAll)\n      OB.printOpen();\n    // Assignment is right associative, with special LHS precedence.\n    bool IsAssign = getPrecedence() == Prec::Assign;\n    LHS->printAsOperand(OB, IsAssign ? Prec::OrIf : getPrecedence(), !IsAssign);\n    // No space before comma operator\n    if (!(InfixOperator == \",\"))\n      OB += \" \";\n    OB += InfixOperator;\n    OB += \" \";\n    RHS->printAsOperand(OB, getPrecedence(), IsAssign);\n    if (ParenAll)\n      OB.printClose();\n  }\n};\n\nclass ArraySubscriptExpr : public Node {\n  const Node *Op1;\n  const Node *Op2;\n\npublic:\n  ArraySubscriptExpr(const Node *Op1_, const Node *Op2_, Prec Prec_)\n      : Node(KArraySubscriptExpr, Prec_), Op1(Op1_), Op2(Op2_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(Op1, Op2, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Op1->printAsOperand(OB, getPrecedence());\n    OB.printOpen('[');\n    Op2->printAsOperand(OB);\n    OB.printClose(']');\n  }\n};\n\nclass PostfixExpr : public Node {\n  const Node *Child;\n  const std::string_view Operator;\n\npublic:\n  PostfixExpr(const Node *Child_, std::string_view Operator_, Prec Prec_)\n      : Node(KPostfixExpr, Prec_), Child(Child_), Operator(Operator_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(Child, Operator, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Child->printAsOperand(OB, getPrecedence(), true);\n    OB += Operator;\n  }\n};\n\nclass ConditionalExpr : public Node {\n  const Node *Cond;\n  const Node *Then;\n  const Node *Else;\n\npublic:\n  ConditionalExpr(const Node *Cond_, const Node *Then_, const Node *Else_,\n                  Prec Prec_)\n      : Node(KConditionalExpr, Prec_), Cond(Cond_), Then(Then_), Else(Else_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(Cond, Then, Else, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Cond->printAsOperand(OB, getPrecedence());\n    OB += \" ? \";\n    Then->printAsOperand(OB);\n    OB += \" : \";\n    Else->printAsOperand(OB, Prec::Assign, true);\n  }\n};\n\nclass MemberExpr : public Node {\n  const Node *LHS;\n  const std::string_view Kind;\n  const Node *RHS;\n\npublic:\n  MemberExpr(const Node *LHS_, std::string_view Kind_, const Node *RHS_,\n             Prec Prec_)\n      : Node(KMemberExpr, Prec_), LHS(LHS_), Kind(Kind_), RHS(RHS_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(LHS, Kind, RHS, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    LHS->printAsOperand(OB, getPrecedence(), true);\n    OB += Kind;\n    RHS->printAsOperand(OB, getPrecedence(), false);\n  }\n};\n\nclass SubobjectExpr : public Node {\n  const Node *Type;\n  const Node *SubExpr;\n  std::string_view Offset;\n  NodeArray UnionSelectors;\n  bool OnePastTheEnd;\n\npublic:\n  SubobjectExpr(const Node *Type_, const Node *SubExpr_,\n                std::string_view Offset_, NodeArray UnionSelectors_,\n                bool OnePastTheEnd_)\n      : Node(KSubobjectExpr), Type(Type_), SubExpr(SubExpr_), Offset(Offset_),\n        UnionSelectors(UnionSelectors_), OnePastTheEnd(OnePastTheEnd_) {}\n\n  template<typename Fn> void match(Fn F) const {\n    F(Type, SubExpr, Offset, UnionSelectors, OnePastTheEnd);\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    SubExpr->print(OB);\n    OB += \".<\";\n    Type->print(OB);\n    OB += \" at offset \";\n    if (Offset.empty()) {\n      OB += \"0\";\n    } else if (Offset[0] == 'n') {\n      OB += \"-\";\n      OB += std::string_view(Offset.data() + 1, Offset.size() - 1);\n    } else {\n      OB += Offset;\n    }\n    OB += \">\";\n  }\n};\n\nclass EnclosingExpr : public Node {\n  const std::string_view Prefix;\n  const Node *Infix;\n  const std::string_view Postfix;\n\npublic:\n  EnclosingExpr(std::string_view Prefix_, const Node *Infix_,\n                Prec Prec_ = Prec::Primary)\n      : Node(KEnclosingExpr, Prec_), Prefix(Prefix_), Infix(Infix_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(Prefix, Infix, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += Prefix;\n    OB.printOpen();\n    Infix->print(OB);\n    OB.printClose();\n    OB += Postfix;\n  }\n};\n\nclass CastExpr : public Node {\n  // cast_kind<to>(from)\n  const std::string_view CastKind;\n  const Node *To;\n  const Node *From;\n\npublic:\n  CastExpr(std::string_view CastKind_, const Node *To_, const Node *From_,\n           Prec Prec_)\n      : Node(KCastExpr, Prec_), CastKind(CastKind_), To(To_), From(From_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(CastKind, To, From, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += CastKind;\n    {\n      ScopedOverride<unsigned> LT(OB.GtIsGt, 0);\n      OB += \"<\";\n      To->printLeft(OB);\n      OB += \">\";\n    }\n    OB.printOpen();\n    From->printAsOperand(OB);\n    OB.printClose();\n  }\n};\n\nclass SizeofParamPackExpr : public Node {\n  const Node *Pack;\n\npublic:\n  SizeofParamPackExpr(const Node *Pack_)\n      : Node(KSizeofParamPackExpr), Pack(Pack_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Pack); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"sizeof...\";\n    OB.printOpen();\n    ParameterPackExpansion PPE(Pack);\n    PPE.printLeft(OB);\n    OB.printClose();\n  }\n};\n\nclass CallExpr : public Node {\n  const Node *Callee;\n  NodeArray Args;\n\npublic:\n  CallExpr(const Node *Callee_, NodeArray Args_, Prec Prec_)\n      : Node(KCallExpr, Prec_), Callee(Callee_), Args(Args_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(Callee, Args, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    Callee->print(OB);\n    OB.printOpen();\n    Args.printWithComma(OB);\n    OB.printClose();\n  }\n};\n\nclass NewExpr : public Node {\n  // new (expr_list) type(init_list)\n  NodeArray ExprList;\n  Node *Type;\n  NodeArray InitList;\n  bool IsGlobal; // ::operator new ?\n  bool IsArray;  // new[] ?\npublic:\n  NewExpr(NodeArray ExprList_, Node *Type_, NodeArray InitList_, bool IsGlobal_,\n          bool IsArray_, Prec Prec_)\n      : Node(KNewExpr, Prec_), ExprList(ExprList_), Type(Type_),\n        InitList(InitList_), IsGlobal(IsGlobal_), IsArray(IsArray_) {}\n\n  template<typename Fn> void match(Fn F) const {\n    F(ExprList, Type, InitList, IsGlobal, IsArray, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    if (IsGlobal)\n      OB += \"::\";\n    OB += \"new\";\n    if (IsArray)\n      OB += \"[]\";\n    if (!ExprList.empty()) {\n      OB.printOpen();\n      ExprList.printWithComma(OB);\n      OB.printClose();\n    }\n    OB += \" \";\n    Type->print(OB);\n    if (!InitList.empty()) {\n      OB.printOpen();\n      InitList.printWithComma(OB);\n      OB.printClose();\n    }\n  }\n};\n\nclass DeleteExpr : public Node {\n  Node *Op;\n  bool IsGlobal;\n  bool IsArray;\n\npublic:\n  DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_, Prec Prec_)\n      : Node(KDeleteExpr, Prec_), Op(Op_), IsGlobal(IsGlobal_),\n        IsArray(IsArray_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(Op, IsGlobal, IsArray, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    if (IsGlobal)\n      OB += \"::\";\n    OB += \"delete\";\n    if (IsArray)\n      OB += \"[]\";\n    OB += ' ';\n    Op->print(OB);\n  }\n};\n\nclass PrefixExpr : public Node {\n  std::string_view Prefix;\n  Node *Child;\n\npublic:\n  PrefixExpr(std::string_view Prefix_, Node *Child_, Prec Prec_)\n      : Node(KPrefixExpr, Prec_), Prefix(Prefix_), Child(Child_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(Prefix, Child, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += Prefix;\n    Child->printAsOperand(OB, getPrecedence());\n  }\n};\n\nclass FunctionParam : public Node {\n  std::string_view Number;\n\npublic:\n  FunctionParam(std::string_view Number_)\n      : Node(KFunctionParam), Number(Number_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Number); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"fp\";\n    OB += Number;\n  }\n};\n\nclass ConversionExpr : public Node {\n  const Node *Type;\n  NodeArray Expressions;\n\npublic:\n  ConversionExpr(const Node *Type_, NodeArray Expressions_, Prec Prec_)\n      : Node(KConversionExpr, Prec_), Type(Type_), Expressions(Expressions_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(Type, Expressions, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB.printOpen();\n    Type->print(OB);\n    OB.printClose();\n    OB.printOpen();\n    Expressions.printWithComma(OB);\n    OB.printClose();\n  }\n};\n\nclass PointerToMemberConversionExpr : public Node {\n  const Node *Type;\n  const Node *SubExpr;\n  std::string_view Offset;\n\npublic:\n  PointerToMemberConversionExpr(const Node *Type_, const Node *SubExpr_,\n                                std::string_view Offset_, Prec Prec_)\n      : Node(KPointerToMemberConversionExpr, Prec_), Type(Type_),\n        SubExpr(SubExpr_), Offset(Offset_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(Type, SubExpr, Offset, getPrecedence());\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB.printOpen();\n    Type->print(OB);\n    OB.printClose();\n    OB.printOpen();\n    SubExpr->print(OB);\n    OB.printClose();\n  }\n};\n\nclass InitListExpr : public Node {\n  const Node *Ty;\n  NodeArray Inits;\npublic:\n  InitListExpr(const Node *Ty_, NodeArray Inits_)\n      : Node(KInitListExpr), Ty(Ty_), Inits(Inits_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Ty, Inits); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    if (Ty)\n      Ty->print(OB);\n    OB += '{';\n    Inits.printWithComma(OB);\n    OB += '}';\n  }\n};\n\nclass BracedExpr : public Node {\n  const Node *Elem;\n  const Node *Init;\n  bool IsArray;\npublic:\n  BracedExpr(const Node *Elem_, const Node *Init_, bool IsArray_)\n      : Node(KBracedExpr), Elem(Elem_), Init(Init_), IsArray(IsArray_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Elem, Init, IsArray); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    if (IsArray) {\n      OB += '[';\n      Elem->print(OB);\n      OB += ']';\n    } else {\n      OB += '.';\n      Elem->print(OB);\n    }\n    if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr)\n      OB += \" = \";\n    Init->print(OB);\n  }\n};\n\nclass BracedRangeExpr : public Node {\n  const Node *First;\n  const Node *Last;\n  const Node *Init;\npublic:\n  BracedRangeExpr(const Node *First_, const Node *Last_, const Node *Init_)\n      : Node(KBracedRangeExpr), First(First_), Last(Last_), Init(Init_) {}\n\n  template<typename Fn> void match(Fn F) const { F(First, Last, Init); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += '[';\n    First->print(OB);\n    OB += \" ... \";\n    Last->print(OB);\n    OB += ']';\n    if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr)\n      OB += \" = \";\n    Init->print(OB);\n  }\n};\n\nclass FoldExpr : public Node {\n  const Node *Pack, *Init;\n  std::string_view OperatorName;\n  bool IsLeftFold;\n\npublic:\n  FoldExpr(bool IsLeftFold_, std::string_view OperatorName_, const Node *Pack_,\n           const Node *Init_)\n      : Node(KFoldExpr), Pack(Pack_), Init(Init_), OperatorName(OperatorName_),\n        IsLeftFold(IsLeftFold_) {}\n\n  template<typename Fn> void match(Fn F) const {\n    F(IsLeftFold, OperatorName, Pack, Init);\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    auto PrintPack = [&] {\n      OB.printOpen();\n      ParameterPackExpansion(Pack).print(OB);\n      OB.printClose();\n    };\n\n    OB.printOpen();\n    // Either '[init op ]... op pack' or 'pack op ...[ op init]'\n    // Refactored to '[(init|pack) op ]...[ op (pack|init)]'\n    // Fold expr operands are cast-expressions\n    if (!IsLeftFold || Init != nullptr) {\n      // '(init|pack) op '\n      if (IsLeftFold)\n        Init->printAsOperand(OB, Prec::Cast, true);\n      else\n        PrintPack();\n      OB << \" \" << OperatorName << \" \";\n    }\n    OB << \"...\";\n    if (IsLeftFold || Init != nullptr) {\n      // ' op (init|pack)'\n      OB << \" \" << OperatorName << \" \";\n      if (IsLeftFold)\n        PrintPack();\n      else\n        Init->printAsOperand(OB, Prec::Cast, true);\n    }\n    OB.printClose();\n  }\n};\n\nclass ThrowExpr : public Node {\n  const Node *Op;\n\npublic:\n  ThrowExpr(const Node *Op_) : Node(KThrowExpr), Op(Op_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Op); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"throw \";\n    Op->print(OB);\n  }\n};\n\nclass BoolExpr : public Node {\n  bool Value;\n\npublic:\n  BoolExpr(bool Value_) : Node(KBoolExpr), Value(Value_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Value); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += Value ? std::string_view(\"true\") : std::string_view(\"false\");\n  }\n};\n\nclass StringLiteral : public Node {\n  const Node *Type;\n\npublic:\n  StringLiteral(const Node *Type_) : Node(KStringLiteral), Type(Type_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Type); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"\\\"<\";\n    Type->print(OB);\n    OB += \">\\\"\";\n  }\n};\n\nclass LambdaExpr : public Node {\n  const Node *Type;\n\npublic:\n  LambdaExpr(const Node *Type_) : Node(KLambdaExpr), Type(Type_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Type); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"[]\";\n    if (Type->getKind() == KClosureTypeName)\n      static_cast<const ClosureTypeName *>(Type)->printDeclarator(OB);\n    OB += \"{...}\";\n  }\n};\n\nclass EnumLiteral : public Node {\n  // ty(integer)\n  const Node *Ty;\n  std::string_view Integer;\n\npublic:\n  EnumLiteral(const Node *Ty_, std::string_view Integer_)\n      : Node(KEnumLiteral), Ty(Ty_), Integer(Integer_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Ty, Integer); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB.printOpen();\n    Ty->print(OB);\n    OB.printClose();\n\n    if (Integer[0] == 'n')\n      OB << '-' << std::string_view(Integer.data() + 1, Integer.size() - 1);\n    else\n      OB << Integer;\n  }\n};\n\nclass IntegerLiteral : public Node {\n  std::string_view Type;\n  std::string_view Value;\n\npublic:\n  IntegerLiteral(std::string_view Type_, std::string_view Value_)\n      : Node(KIntegerLiteral), Type(Type_), Value(Value_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Type, Value); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    if (Type.size() > 3) {\n      OB.printOpen();\n      OB += Type;\n      OB.printClose();\n    }\n\n    if (Value[0] == 'n')\n      OB << '-' << std::string_view(Value.data() + 1, Value.size() - 1);\n    else\n      OB += Value;\n\n    if (Type.size() <= 3)\n      OB += Type;\n  }\n};\n\nclass RequiresExpr : public Node {\n  NodeArray Parameters;\n  NodeArray Requirements;\npublic:\n  RequiresExpr(NodeArray Parameters_, NodeArray Requirements_)\n      : Node(KRequiresExpr), Parameters(Parameters_),\n        Requirements(Requirements_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Parameters, Requirements); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \"requires\";\n    if (!Parameters.empty()) {\n      OB += ' ';\n      OB.printOpen();\n      Parameters.printWithComma(OB);\n      OB.printClose();\n    }\n    OB += ' ';\n    OB.printOpen('{');\n    for (const Node *Req : Requirements) {\n      Req->print(OB);\n    }\n    OB += ' ';\n    OB.printClose('}');\n  }\n};\n\nclass ExprRequirement : public Node {\n  const Node *Expr;\n  bool IsNoexcept;\n  const Node *TypeConstraint;\npublic:\n  ExprRequirement(const Node *Expr_, bool IsNoexcept_,\n                  const Node *TypeConstraint_)\n      : Node(KExprRequirement), Expr(Expr_), IsNoexcept(IsNoexcept_),\n        TypeConstraint(TypeConstraint_) {}\n\n  template <typename Fn> void match(Fn F) const {\n    F(Expr, IsNoexcept, TypeConstraint);\n  }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \" \";\n    if (IsNoexcept || TypeConstraint)\n      OB.printOpen('{');\n    Expr->print(OB);\n    if (IsNoexcept || TypeConstraint)\n      OB.printClose('}');\n    if (IsNoexcept)\n      OB += \" noexcept\";\n    if (TypeConstraint) {\n      OB += \" -> \";\n      TypeConstraint->print(OB);\n    }\n    OB += ';';\n  }\n};\n\nclass TypeRequirement : public Node {\n  const Node *Type;\npublic:\n  TypeRequirement(const Node *Type_)\n      : Node(KTypeRequirement), Type(Type_) {}\n\n  template <typename Fn> void match(Fn F) const { F(Type); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \" typename \";\n    Type->print(OB);\n    OB += ';';\n  }\n};\n\nclass NestedRequirement : public Node {\n  const Node *Constraint;\npublic:\n  NestedRequirement(const Node *Constraint_)\n      : Node(KNestedRequirement), Constraint(Constraint_) {}\n\n  template <typename Fn> void match(Fn F) const { F(Constraint); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    OB += \" requires \";\n    Constraint->print(OB);\n    OB += ';';\n  }\n};\n\ntemplate <class Float> struct FloatData;\n\nnamespace float_literal_impl {\nconstexpr Node::Kind getFloatLiteralKind(float *) {\n  return Node::KFloatLiteral;\n}\nconstexpr Node::Kind getFloatLiteralKind(double *) {\n  return Node::KDoubleLiteral;\n}\nconstexpr Node::Kind getFloatLiteralKind(long double *) {\n  return Node::KLongDoubleLiteral;\n}\n}\n\ntemplate <class Float> class FloatLiteralImpl : public Node {\n  const std::string_view Contents;\n\n  static constexpr Kind KindForClass =\n      float_literal_impl::getFloatLiteralKind((Float *)nullptr);\n\npublic:\n  FloatLiteralImpl(std::string_view Contents_)\n      : Node(KindForClass), Contents(Contents_) {}\n\n  template<typename Fn> void match(Fn F) const { F(Contents); }\n\n  void printLeft(OutputBuffer &OB) const override {\n    const size_t N = FloatData<Float>::mangled_size;\n    if (Contents.size() >= N) {\n      union {\n        Float value;\n        char buf[sizeof(Float)];\n      };\n      const char *t = Contents.data();\n      const char *last = t + N;\n      char *e = buf;\n      for (; t != last; ++t, ++e) {\n        unsigned d1 = isdigit(*t) ? static_cast<unsigned>(*t - '0')\n                                  : static_cast<unsigned>(*t - 'a' + 10);\n        ++t;\n        unsigned d0 = isdigit(*t) ? static_cast<unsigned>(*t - '0')\n                                  : static_cast<unsigned>(*t - 'a' + 10);\n        *e = static_cast<char>((d1 << 4) + d0);\n      }\n#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n      std::reverse(buf, e);\n#endif\n      char num[FloatData<Float>::max_demangled_size] = {0};\n      int n = snprintf(num, sizeof(num), FloatData<Float>::spec, value);\n      OB += std::string_view(num, n);\n    }\n  }\n};\n\nusing FloatLiteral = FloatLiteralImpl<float>;\nusing DoubleLiteral = FloatLiteralImpl<double>;\nusing LongDoubleLiteral = FloatLiteralImpl<long double>;\n\n/// Visit the node. Calls \\c F(P), where \\c P is the node cast to the\n/// appropriate derived class.\ntemplate<typename Fn>\nvoid Node::visit(Fn F) const {\n  switch (K) {\n#define NODE(X)                                                                \\\n  case K##X:                                                                   \\\n    return F(static_cast<const X *>(this));\n#include \"ItaniumNodes.def\"\n  }\n  DEMANGLE_ASSERT(0, \"unknown mangling node kind\");\n}\n\n/// Determine the kind of a node from its type.\ntemplate<typename NodeT> struct NodeKind;\n#define NODE(X)                                                                \\\n  template <> struct NodeKind<X> {                                             \\\n    static constexpr Node::Kind Kind = Node::K##X;                             \\\n    static constexpr const char *name() { return #X; }                         \\\n  };\n#include \"ItaniumNodes.def\"\n\ntemplate <typename Derived, typename Alloc> struct AbstractManglingParser {\n  const char *First;\n  const char *Last;\n\n  // Name stack, this is used by the parser to hold temporary names that were\n  // parsed. The parser collapses multiple names into new nodes to construct\n  // the AST. Once the parser is finished, names.size() == 1.\n  PODSmallVector<Node *, 32> Names;\n\n  // Substitution table. Itanium supports name substitutions as a means of\n  // compression. The string \"S42_\" refers to the 44nd entry (base-36) in this\n  // table.\n  PODSmallVector<Node *, 32> Subs;\n\n  // A list of template argument values corresponding to a template parameter\n  // list.\n  using TemplateParamList = PODSmallVector<Node *, 8>;\n\n  class ScopedTemplateParamList {\n    AbstractManglingParser *Parser;\n    size_t OldNumTemplateParamLists;\n    TemplateParamList Params;\n\n  public:\n    ScopedTemplateParamList(AbstractManglingParser *TheParser)\n        : Parser(TheParser),\n          OldNumTemplateParamLists(TheParser->TemplateParams.size()) {\n      Parser->TemplateParams.push_back(&Params);\n    }\n    ~ScopedTemplateParamList() {\n      DEMANGLE_ASSERT(Parser->TemplateParams.size() >= OldNumTemplateParamLists,\n                      \"\");\n      Parser->TemplateParams.shrinkToSize(OldNumTemplateParamLists);\n    }\n    TemplateParamList *params() { return &Params; }\n  };\n\n  // Template parameter table. Like the above, but referenced like \"T42_\".\n  // This has a smaller size compared to Subs and Names because it can be\n  // stored on the stack.\n  TemplateParamList OuterTemplateParams;\n\n  // Lists of template parameters indexed by template parameter depth,\n  // referenced like \"TL2_4_\". If nonempty, element 0 is always\n  // OuterTemplateParams; inner elements are always template parameter lists of\n  // lambda expressions. For a generic lambda with no explicit template\n  // parameter list, the corresponding parameter list pointer will be null.\n  PODSmallVector<TemplateParamList *, 4> TemplateParams;\n\n  class SaveTemplateParams {\n    AbstractManglingParser *Parser;\n    decltype(TemplateParams) OldParams;\n    decltype(OuterTemplateParams) OldOuterParams;\n\n  public:\n    SaveTemplateParams(AbstractManglingParser *TheParser) : Parser(TheParser) {\n      OldParams = std::move(Parser->TemplateParams);\n      OldOuterParams = std::move(Parser->OuterTemplateParams);\n      Parser->TemplateParams.clear();\n      Parser->OuterTemplateParams.clear();\n    }\n    ~SaveTemplateParams() {\n      Parser->TemplateParams = std::move(OldParams);\n      Parser->OuterTemplateParams = std::move(OldOuterParams);\n    }\n  };\n\n  // Set of unresolved forward <template-param> references. These can occur in a\n  // conversion operator's type, and are resolved in the enclosing <encoding>.\n  PODSmallVector<ForwardTemplateReference *, 4> ForwardTemplateRefs;\n\n  bool TryToParseTemplateArgs = true;\n  bool PermitForwardTemplateReferences = false;\n  bool InConstraintExpr = false;\n  size_t ParsingLambdaParamsAtLevel = (size_t)-1;\n\n  unsigned NumSyntheticTemplateParameters[3] = {};\n\n  Alloc ASTAllocator;\n\n  AbstractManglingParser(const char *First_, const char *Last_)\n      : First(First_), Last(Last_) {}\n\n  Derived &getDerived() { return static_cast<Derived &>(*this); }\n\n  void reset(const char *First_, const char *Last_) {\n    First = First_;\n    Last = Last_;\n    Names.clear();\n    Subs.clear();\n    TemplateParams.clear();\n    ParsingLambdaParamsAtLevel = (size_t)-1;\n    TryToParseTemplateArgs = true;\n    PermitForwardTemplateReferences = false;\n    for (int I = 0; I != 3; ++I)\n      NumSyntheticTemplateParameters[I] = 0;\n    ASTAllocator.reset();\n  }\n\n  template <class T, class... Args> Node *make(Args &&... args) {\n    return ASTAllocator.template makeNode<T>(std::forward<Args>(args)...);\n  }\n\n  template <class It> NodeArray makeNodeArray(It begin, It end) {\n    size_t sz = static_cast<size_t>(end - begin);\n    void *mem = ASTAllocator.allocateNodeArray(sz);\n    Node **data = new (mem) Node *[sz];\n    std::copy(begin, end, data);\n    return NodeArray(data, sz);\n  }\n\n  NodeArray popTrailingNodeArray(size_t FromPosition) {\n    DEMANGLE_ASSERT(FromPosition <= Names.size(), \"\");\n    NodeArray res =\n        makeNodeArray(Names.begin() + (long)FromPosition, Names.end());\n    Names.shrinkToSize(FromPosition);\n    return res;\n  }\n\n  bool consumeIf(std::string_view S) {\n    if (starts_with(std::string_view(First, Last - First), S)) {\n      First += S.size();\n      return true;\n    }\n    return false;\n  }\n\n  bool consumeIf(char C) {\n    if (First != Last && *First == C) {\n      ++First;\n      return true;\n    }\n    return false;\n  }\n\n  char consume() { return First != Last ? *First++ : '\\0'; }\n\n  char look(unsigned Lookahead = 0) const {\n    if (static_cast<size_t>(Last - First) <= Lookahead)\n      return '\\0';\n    return First[Lookahead];\n  }\n\n  size_t numLeft() const { return static_cast<size_t>(Last - First); }\n\n  std::string_view parseNumber(bool AllowNegative = false);\n  Qualifiers parseCVQualifiers();\n  bool parsePositiveInteger(size_t *Out);\n  std::string_view parseBareSourceName();\n\n  bool parseSeqId(size_t *Out);\n  Node *parseSubstitution();\n  Node *parseTemplateParam();\n  Node *parseTemplateParamDecl(TemplateParamList *Params);\n  Node *parseTemplateArgs(bool TagTemplates = false);\n  Node *parseTemplateArg();\n\n  bool isTemplateParamDecl() {\n    return look() == 'T' &&\n           std::string_view(\"yptnk\").find(look(1)) != std::string_view::npos;\n  }\n\n  /// Parse the <expression> production.\n  Node *parseExpr();\n  Node *parsePrefixExpr(std::string_view Kind, Node::Prec Prec);\n  Node *parseBinaryExpr(std::string_view Kind, Node::Prec Prec);\n  Node *parseIntegerLiteral(std::string_view Lit);\n  Node *parseExprPrimary();\n  template <class Float> Node *parseFloatingLiteral();\n  Node *parseFunctionParam();\n  Node *parseConversionExpr();\n  Node *parseBracedExpr();\n  Node *parseFoldExpr();\n  Node *parsePointerToMemberConversionExpr(Node::Prec Prec);\n  Node *parseSubobjectExpr();\n  Node *parseConstraintExpr();\n  Node *parseRequiresExpr();\n\n  /// Parse the <type> production.\n  Node *parseType();\n  Node *parseFunctionType();\n  Node *parseVectorType();\n  Node *parseDecltype();\n  Node *parseArrayType();\n  Node *parsePointerToMemberType();\n  Node *parseClassEnumType();\n  Node *parseQualifiedType();\n\n  Node *parseEncoding(bool ParseParams = true);\n  bool parseCallOffset();\n  Node *parseSpecialName();\n\n  /// Holds some extra information about a <name> that is being parsed. This\n  /// information is only pertinent if the <name> refers to an <encoding>.\n  struct NameState {\n    bool CtorDtorConversion = false;\n    bool EndsWithTemplateArgs = false;\n    Qualifiers CVQualifiers = QualNone;\n    FunctionRefQual ReferenceQualifier = FrefQualNone;\n    size_t ForwardTemplateRefsBegin;\n    bool HasExplicitObjectParameter = false;\n\n    NameState(AbstractManglingParser *Enclosing)\n        : ForwardTemplateRefsBegin(Enclosing->ForwardTemplateRefs.size()) {}\n  };\n\n  bool resolveForwardTemplateRefs(NameState &State) {\n    size_t I = State.ForwardTemplateRefsBegin;\n    size_t E = ForwardTemplateRefs.size();\n    for (; I < E; ++I) {\n      size_t Idx = ForwardTemplateRefs[I]->Index;\n      if (TemplateParams.empty() || !TemplateParams[0] ||\n          Idx >= TemplateParams[0]->size())\n        return true;\n      ForwardTemplateRefs[I]->Ref = (*TemplateParams[0])[Idx];\n    }\n    ForwardTemplateRefs.shrinkToSize(State.ForwardTemplateRefsBegin);\n    return false;\n  }\n\n  /// Parse the <name> production>\n  Node *parseName(NameState *State = nullptr);\n  Node *parseLocalName(NameState *State);\n  Node *parseOperatorName(NameState *State);\n  bool parseModuleNameOpt(ModuleName *&Module);\n  Node *parseUnqualifiedName(NameState *State, Node *Scope, ModuleName *Module);\n  Node *parseUnnamedTypeName(NameState *State);\n  Node *parseSourceName(NameState *State);\n  Node *parseUnscopedName(NameState *State, bool *isSubstName);\n  Node *parseNestedName(NameState *State);\n  Node *parseCtorDtorName(Node *&SoFar, NameState *State);\n\n  Node *parseAbiTags(Node *N);\n\n  struct OperatorInfo {\n    enum OIKind : unsigned char {\n      Prefix,      // Prefix unary: @ expr\n      Postfix,     // Postfix unary: expr @\n      Binary,      // Binary: lhs @ rhs\n      Array,       // Array index:  lhs [ rhs ]\n      Member,      // Member access: lhs @ rhs\n      New,         // New\n      Del,         // Delete\n      Call,        // Function call: expr (expr*)\n      CCast,       // C cast: (type)expr\n      Conditional, // Conditional: expr ? expr : expr\n      NameOnly,    // Overload only, not allowed in expression.\n      // Below do not have operator names\n      NamedCast, // Named cast, @<type>(expr)\n      OfIdOp,    // alignof, sizeof, typeid\n\n      Unnameable = NamedCast,\n    };\n    char Enc[2];      // Encoding\n    OIKind Kind;      // Kind of operator\n    bool Flag : 1;    // Entry-specific flag\n    Node::Prec Prec : 7; // Precedence\n    const char *Name; // Spelling\n\n  public:\n    constexpr OperatorInfo(const char (&E)[3], OIKind K, bool F, Node::Prec P,\n                           const char *N)\n        : Enc{E[0], E[1]}, Kind{K}, Flag{F}, Prec{P}, Name{N} {}\n\n  public:\n    bool operator<(const OperatorInfo &Other) const {\n      return *this < Other.Enc;\n    }\n    bool operator<(const char *Peek) const {\n      return Enc[0] < Peek[0] || (Enc[0] == Peek[0] && Enc[1] < Peek[1]);\n    }\n    bool operator==(const char *Peek) const {\n      return Enc[0] == Peek[0] && Enc[1] == Peek[1];\n    }\n    bool operator!=(const char *Peek) const { return !this->operator==(Peek); }\n\n  public:\n    std::string_view getSymbol() const {\n      std::string_view Res = Name;\n      if (Kind < Unnameable) {\n        DEMANGLE_ASSERT(starts_with(Res, \"operator\"),\n                        \"operator name does not start with 'operator'\");\n        Res.remove_prefix(sizeof(\"operator\") - 1);\n        if (starts_with(Res, ' '))\n          Res.remove_prefix(1);\n      }\n      return Res;\n    }\n    std::string_view getName() const { return Name; }\n    OIKind getKind() const { return Kind; }\n    bool getFlag() const { return Flag; }\n    Node::Prec getPrecedence() const { return Prec; }\n  };\n  static const OperatorInfo Ops[];\n  static const size_t NumOps;\n  const OperatorInfo *parseOperatorEncoding();\n\n  /// Parse the <unresolved-name> production.\n  Node *parseUnresolvedName(bool Global);\n  Node *parseSimpleId();\n  Node *parseBaseUnresolvedName();\n  Node *parseUnresolvedType();\n  Node *parseDestructorName();\n\n  /// Top-level entry point into the parser.\n  Node *parse(bool ParseParams = true);\n};\n\nconst char* parse_discriminator(const char* first, const char* last);\n\n// <name> ::= <nested-name> // N\n//        ::= <local-name> # See Scope Encoding below  // Z\n//        ::= <unscoped-template-name> <template-args>\n//        ::= <unscoped-name>\n//\n// <unscoped-template-name> ::= <unscoped-name>\n//                          ::= <substitution>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseName(NameState *State) {\n  if (look() == 'N')\n    return getDerived().parseNestedName(State);\n  if (look() == 'Z')\n    return getDerived().parseLocalName(State);\n\n  Node *Result = nullptr;\n  bool IsSubst = false;\n\n  Result = getDerived().parseUnscopedName(State, &IsSubst);\n  if (!Result)\n    return nullptr;\n\n  if (look() == 'I') {\n    //        ::= <unscoped-template-name> <template-args>\n    if (!IsSubst)\n      // An unscoped-template-name is substitutable.\n      Subs.push_back(Result);\n    Node *TA = getDerived().parseTemplateArgs(State != nullptr);\n    if (TA == nullptr)\n      return nullptr;\n    if (State)\n      State->EndsWithTemplateArgs = true;\n    Result = make<NameWithTemplateArgs>(Result, TA);\n  } else if (IsSubst) {\n    // The substitution case must be followed by <template-args>.\n    return nullptr;\n  }\n\n  return Result;\n}\n\n// <local-name> := Z <function encoding> E <entity name> [<discriminator>]\n//              := Z <function encoding> E s [<discriminator>]\n//              := Z <function encoding> Ed [ <parameter number> ] _ <entity name>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseLocalName(NameState *State) {\n  if (!consumeIf('Z'))\n    return nullptr;\n  Node *Encoding = getDerived().parseEncoding();\n  if (Encoding == nullptr || !consumeIf('E'))\n    return nullptr;\n\n  if (consumeIf('s')) {\n    First = parse_discriminator(First, Last);\n    auto *StringLitName = make<NameType>(\"string literal\");\n    if (!StringLitName)\n      return nullptr;\n    return make<LocalName>(Encoding, StringLitName);\n  }\n\n  // The template parameters of the inner name are unrelated to those of the\n  // enclosing context.\n  SaveTemplateParams SaveTemplateParamsScope(this);\n\n  if (consumeIf('d')) {\n    parseNumber(true);\n    if (!consumeIf('_'))\n      return nullptr;\n    Node *N = getDerived().parseName(State);\n    if (N == nullptr)\n      return nullptr;\n    return make<LocalName>(Encoding, N);\n  }\n\n  Node *Entity = getDerived().parseName(State);\n  if (Entity == nullptr)\n    return nullptr;\n  First = parse_discriminator(First, Last);\n  return make<LocalName>(Encoding, Entity);\n}\n\n// <unscoped-name> ::= <unqualified-name>\n//                 ::= St <unqualified-name>   # ::std::\n// [*] extension\ntemplate <typename Derived, typename Alloc>\nNode *\nAbstractManglingParser<Derived, Alloc>::parseUnscopedName(NameState *State,\n                                                          bool *IsSubst) {\n\n  Node *Std = nullptr;\n  if (consumeIf(\"St\")) {\n    Std = make<NameType>(\"std\");\n    if (Std == nullptr)\n      return nullptr;\n  }\n\n  Node *Res = nullptr;\n  ModuleName *Module = nullptr;\n  if (look() == 'S') {\n    Node *S = getDerived().parseSubstitution();\n    if (!S)\n      return nullptr;\n    if (S->getKind() == Node::KModuleName)\n      Module = static_cast<ModuleName *>(S);\n    else if (IsSubst && Std == nullptr) {\n      Res = S;\n      *IsSubst = true;\n    } else {\n      return nullptr;\n    }\n  }\n\n  if (Res == nullptr || Std != nullptr) {\n    Res = getDerived().parseUnqualifiedName(State, Std, Module);\n  }\n\n  return Res;\n}\n\n// <unqualified-name> ::= [<module-name>] F? L? <operator-name> [<abi-tags>]\n//                    ::= [<module-name>] <ctor-dtor-name> [<abi-tags>]\n//                    ::= [<module-name>] F? L? <source-name> [<abi-tags>]\n//                    ::= [<module-name>] L? <unnamed-type-name> [<abi-tags>]\n//\t\t\t# structured binding declaration\n//                    ::= [<module-name>] L? DC <source-name>+ E\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseUnqualifiedName(\n    NameState *State, Node *Scope, ModuleName *Module) {\n  if (getDerived().parseModuleNameOpt(Module))\n    return nullptr;\n\n  bool IsMemberLikeFriend = Scope && consumeIf('F');\n\n  consumeIf('L');\n\n  Node *Result;\n  if (look() >= '1' && look() <= '9') {\n    Result = getDerived().parseSourceName(State);\n  } else if (look() == 'U') {\n    Result = getDerived().parseUnnamedTypeName(State);\n  } else if (consumeIf(\"DC\")) {\n    // Structured binding\n    size_t BindingsBegin = Names.size();\n    do {\n      Node *Binding = getDerived().parseSourceName(State);\n      if (Binding == nullptr)\n        return nullptr;\n      Names.push_back(Binding);\n    } while (!consumeIf('E'));\n    Result = make<StructuredBindingName>(popTrailingNodeArray(BindingsBegin));\n  } else if (look() == 'C' || look() == 'D') {\n    // A <ctor-dtor-name>.\n    if (Scope == nullptr || Module != nullptr)\n      return nullptr;\n    Result = getDerived().parseCtorDtorName(Scope, State);\n  } else {\n    Result = getDerived().parseOperatorName(State);\n  }\n\n  if (Result != nullptr && Module != nullptr)\n    Result = make<ModuleEntity>(Module, Result);\n  if (Result != nullptr)\n    Result = getDerived().parseAbiTags(Result);\n  if (Result != nullptr && IsMemberLikeFriend)\n    Result = make<MemberLikeFriendName>(Scope, Result);\n  else if (Result != nullptr && Scope != nullptr)\n    Result = make<NestedName>(Scope, Result);\n\n  return Result;\n}\n\n// <module-name> ::= <module-subname>\n// \t \t ::= <module-name> <module-subname>\n//\t\t ::= <substitution>  # passed in by caller\n// <module-subname> ::= W <source-name>\n//\t\t    ::= W P <source-name>\ntemplate <typename Derived, typename Alloc>\nbool AbstractManglingParser<Derived, Alloc>::parseModuleNameOpt(\n    ModuleName *&Module) {\n  while (consumeIf('W')) {\n    bool IsPartition = consumeIf('P');\n    Node *Sub = getDerived().parseSourceName(nullptr);\n    if (!Sub)\n      return true;\n    Module =\n        static_cast<ModuleName *>(make<ModuleName>(Module, Sub, IsPartition));\n    Subs.push_back(Module);\n  }\n\n  return false;\n}\n\n// <unnamed-type-name> ::= Ut [<nonnegative number>] _\n//                     ::= <closure-type-name>\n//\n// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _\n//\n// <lambda-sig> ::= <template-param-decl>* [Q <requires-clause expression>]\n//                  <parameter type>+  # or \"v\" if the lambda has no parameters\ntemplate <typename Derived, typename Alloc>\nNode *\nAbstractManglingParser<Derived, Alloc>::parseUnnamedTypeName(NameState *State) {\n  // <template-params> refer to the innermost <template-args>. Clear out any\n  // outer args that we may have inserted into TemplateParams.\n  if (State != nullptr)\n    TemplateParams.clear();\n\n  if (consumeIf(\"Ut\")) {\n    std::string_view Count = parseNumber();\n    if (!consumeIf('_'))\n      return nullptr;\n    return make<UnnamedTypeName>(Count);\n  }\n  if (consumeIf(\"Ul\")) {\n    ScopedOverride<size_t> SwapParams(ParsingLambdaParamsAtLevel,\n                                      TemplateParams.size());\n    ScopedTemplateParamList LambdaTemplateParams(this);\n\n    size_t ParamsBegin = Names.size();\n    while (getDerived().isTemplateParamDecl()) {\n      Node *T =\n          getDerived().parseTemplateParamDecl(LambdaTemplateParams.params());\n      if (T == nullptr)\n        return nullptr;\n      Names.push_back(T);\n    }\n    NodeArray TempParams = popTrailingNodeArray(ParamsBegin);\n\n    // FIXME: If TempParams is empty and none of the function parameters\n    // includes 'auto', we should remove LambdaTemplateParams from the\n    // TemplateParams list. Unfortunately, we don't find out whether there are\n    // any 'auto' parameters until too late in an example such as:\n    //\n    //   template<typename T> void f(\n    //       decltype([](decltype([]<typename T>(T v) {}),\n    //                   auto) {})) {}\n    //   template<typename T> void f(\n    //       decltype([](decltype([]<typename T>(T w) {}),\n    //                   int) {})) {}\n    //\n    // Here, the type of v is at level 2 but the type of w is at level 1. We\n    // don't find this out until we encounter the type of the next parameter.\n    //\n    // However, compilers can't actually cope with the former example in\n    // practice, and it's likely to be made ill-formed in future, so we don't\n    // need to support it here.\n    //\n    // If we encounter an 'auto' in the function parameter types, we will\n    // recreate a template parameter scope for it, but any intervening lambdas\n    // will be parsed in the 'wrong' template parameter depth.\n    if (TempParams.empty())\n      TemplateParams.pop_back();\n\n    Node *Requires1 = nullptr;\n    if (consumeIf('Q')) {\n      Requires1 = getDerived().parseConstraintExpr();\n      if (Requires1 == nullptr)\n        return nullptr;\n    }\n\n    if (!consumeIf(\"v\")) {\n      do {\n        Node *P = getDerived().parseType();\n        if (P == nullptr)\n          return nullptr;\n        Names.push_back(P);\n      } while (look() != 'E' && look() != 'Q');\n    }\n    NodeArray Params = popTrailingNodeArray(ParamsBegin);\n\n    Node *Requires2 = nullptr;\n    if (consumeIf('Q')) {\n      Requires2 = getDerived().parseConstraintExpr();\n      if (Requires2 == nullptr)\n        return nullptr;\n    }\n\n    if (!consumeIf('E'))\n      return nullptr;\n\n    std::string_view Count = parseNumber();\n    if (!consumeIf('_'))\n      return nullptr;\n    return make<ClosureTypeName>(TempParams, Requires1, Params, Requires2,\n                                 Count);\n  }\n  if (consumeIf(\"Ub\")) {\n    (void)parseNumber();\n    if (!consumeIf('_'))\n      return nullptr;\n    return make<NameType>(\"'block-literal'\");\n  }\n  return nullptr;\n}\n\n// <source-name> ::= <positive length number> <identifier>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseSourceName(NameState *) {\n  size_t Length = 0;\n  if (parsePositiveInteger(&Length))\n    return nullptr;\n  if (numLeft() < Length || Length == 0)\n    return nullptr;\n  std::string_view Name(First, Length);\n  First += Length;\n  if (starts_with(Name, \"_GLOBAL__N\"))\n    return make<NameType>(\"(anonymous namespace)\");\n  return make<NameType>(Name);\n}\n\n// Operator encodings\ntemplate <typename Derived, typename Alloc>\nconst typename AbstractManglingParser<\n    Derived, Alloc>::OperatorInfo AbstractManglingParser<Derived,\n                                                         Alloc>::Ops[] = {\n    // Keep ordered by encoding\n    {\"aN\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator&=\"},\n    {\"aS\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator=\"},\n    {\"aa\", OperatorInfo::Binary, false, Node::Prec::AndIf, \"operator&&\"},\n    {\"ad\", OperatorInfo::Prefix, false, Node::Prec::Unary, \"operator&\"},\n    {\"an\", OperatorInfo::Binary, false, Node::Prec::And, \"operator&\"},\n    {\"at\", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Unary, \"alignof \"},\n    {\"aw\", OperatorInfo::NameOnly, false, Node::Prec::Primary,\n     \"operator co_await\"},\n    {\"az\", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Unary, \"alignof \"},\n    {\"cc\", OperatorInfo::NamedCast, false, Node::Prec::Postfix, \"const_cast\"},\n    {\"cl\", OperatorInfo::Call, false, Node::Prec::Postfix, \"operator()\"},\n    {\"cm\", OperatorInfo::Binary, false, Node::Prec::Comma, \"operator,\"},\n    {\"co\", OperatorInfo::Prefix, false, Node::Prec::Unary, \"operator~\"},\n    {\"cv\", OperatorInfo::CCast, false, Node::Prec::Cast, \"operator\"}, // C Cast\n    {\"dV\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator/=\"},\n    {\"da\", OperatorInfo::Del, /*Ary*/ true, Node::Prec::Unary,\n     \"operator delete[]\"},\n    {\"dc\", OperatorInfo::NamedCast, false, Node::Prec::Postfix, \"dynamic_cast\"},\n    {\"de\", OperatorInfo::Prefix, false, Node::Prec::Unary, \"operator*\"},\n    {\"dl\", OperatorInfo::Del, /*Ary*/ false, Node::Prec::Unary,\n     \"operator delete\"},\n    {\"ds\", OperatorInfo::Member, /*Named*/ false, Node::Prec::PtrMem,\n     \"operator.*\"},\n    {\"dt\", OperatorInfo::Member, /*Named*/ false, Node::Prec::Postfix,\n     \"operator.\"},\n    {\"dv\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator/\"},\n    {\"eO\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator^=\"},\n    {\"eo\", OperatorInfo::Binary, false, Node::Prec::Xor, \"operator^\"},\n    {\"eq\", OperatorInfo::Binary, false, Node::Prec::Equality, \"operator==\"},\n    {\"ge\", OperatorInfo::Binary, false, Node::Prec::Relational, \"operator>=\"},\n    {\"gt\", OperatorInfo::Binary, false, Node::Prec::Relational, \"operator>\"},\n    {\"ix\", OperatorInfo::Array, false, Node::Prec::Postfix, \"operator[]\"},\n    {\"lS\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator<<=\"},\n    {\"le\", OperatorInfo::Binary, false, Node::Prec::Relational, \"operator<=\"},\n    {\"ls\", OperatorInfo::Binary, false, Node::Prec::Shift, \"operator<<\"},\n    {\"lt\", OperatorInfo::Binary, false, Node::Prec::Relational, \"operator<\"},\n    {\"mI\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator-=\"},\n    {\"mL\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator*=\"},\n    {\"mi\", OperatorInfo::Binary, false, Node::Prec::Additive, \"operator-\"},\n    {\"ml\", OperatorInfo::Binary, false, Node::Prec::Multiplicative,\n     \"operator*\"},\n    {\"mm\", OperatorInfo::Postfix, false, Node::Prec::Postfix, \"operator--\"},\n    {\"na\", OperatorInfo::New, /*Ary*/ true, Node::Prec::Unary,\n     \"operator new[]\"},\n    {\"ne\", OperatorInfo::Binary, false, Node::Prec::Equality, \"operator!=\"},\n    {\"ng\", OperatorInfo::Prefix, false, Node::Prec::Unary, \"operator-\"},\n    {\"nt\", OperatorInfo::Prefix, false, Node::Prec::Unary, \"operator!\"},\n    {\"nw\", OperatorInfo::New, /*Ary*/ false, Node::Prec::Unary, \"operator new\"},\n    {\"oR\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator|=\"},\n    {\"oo\", OperatorInfo::Binary, false, Node::Prec::OrIf, \"operator||\"},\n    {\"or\", OperatorInfo::Binary, false, Node::Prec::Ior, \"operator|\"},\n    {\"pL\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator+=\"},\n    {\"pl\", OperatorInfo::Binary, false, Node::Prec::Additive, \"operator+\"},\n    {\"pm\", OperatorInfo::Member, /*Named*/ false, Node::Prec::PtrMem,\n     \"operator->*\"},\n    {\"pp\", OperatorInfo::Postfix, false, Node::Prec::Postfix, \"operator++\"},\n    {\"ps\", OperatorInfo::Prefix, false, Node::Prec::Unary, \"operator+\"},\n    {\"pt\", OperatorInfo::Member, /*Named*/ true, Node::Prec::Postfix,\n     \"operator->\"},\n    {\"qu\", OperatorInfo::Conditional, false, Node::Prec::Conditional,\n     \"operator?\"},\n    {\"rM\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator%=\"},\n    {\"rS\", OperatorInfo::Binary, false, Node::Prec::Assign, \"operator>>=\"},\n    {\"rc\", OperatorInfo::NamedCast, false, Node::Prec::Postfix,\n     \"reinterpret_cast\"},\n    {\"rm\", OperatorInfo::Binary, false, Node::Prec::Multiplicative,\n     \"operator%\"},\n    {\"rs\", OperatorInfo::Binary, false, Node::Prec::Shift, \"operator>>\"},\n    {\"sc\", OperatorInfo::NamedCast, false, Node::Prec::Postfix, \"static_cast\"},\n    {\"ss\", OperatorInfo::Binary, false, Node::Prec::Spaceship, \"operator<=>\"},\n    {\"st\", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Unary, \"sizeof \"},\n    {\"sz\", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Unary, \"sizeof \"},\n    {\"te\", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Postfix,\n     \"typeid \"},\n    {\"ti\", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Postfix, \"typeid \"},\n};\ntemplate <typename Derived, typename Alloc>\nconst size_t AbstractManglingParser<Derived, Alloc>::NumOps = sizeof(Ops) /\n                                                              sizeof(Ops[0]);\n\n// If the next 2 chars are an operator encoding, consume them and return their\n// OperatorInfo.  Otherwise return nullptr.\ntemplate <typename Derived, typename Alloc>\nconst typename AbstractManglingParser<Derived, Alloc>::OperatorInfo *\nAbstractManglingParser<Derived, Alloc>::parseOperatorEncoding() {\n  if (numLeft() < 2)\n    return nullptr;\n\n  // We can't use lower_bound as that can link to symbols in the C++ library,\n  // and this must remain independant of that.\n  size_t lower = 0u, upper = NumOps - 1; // Inclusive bounds.\n  while (upper != lower) {\n    size_t middle = (upper + lower) / 2;\n    if (Ops[middle] < First)\n      lower = middle + 1;\n    else\n      upper = middle;\n  }\n  if (Ops[lower] != First)\n    return nullptr;\n\n  First += 2;\n  return &Ops[lower];\n}\n\n//   <operator-name> ::= See parseOperatorEncoding()\n//                   ::= li <source-name>  # operator \"\"\n//                   ::= v <digit> <source-name>  # vendor extended operator\ntemplate <typename Derived, typename Alloc>\nNode *\nAbstractManglingParser<Derived, Alloc>::parseOperatorName(NameState *State) {\n  if (const auto *Op = parseOperatorEncoding()) {\n    if (Op->getKind() == OperatorInfo::CCast) {\n      //              ::= cv <type>    # (cast)\n      ScopedOverride<bool> SaveTemplate(TryToParseTemplateArgs, false);\n      // If we're parsing an encoding, State != nullptr and the conversion\n      // operators' <type> could have a <template-param> that refers to some\n      // <template-arg>s further ahead in the mangled name.\n      ScopedOverride<bool> SavePermit(PermitForwardTemplateReferences,\n                                      PermitForwardTemplateReferences ||\n                                          State != nullptr);\n      Node *Ty = getDerived().parseType();\n      if (Ty == nullptr)\n        return nullptr;\n      if (State) State->CtorDtorConversion = true;\n      return make<ConversionOperatorType>(Ty);\n    }\n\n    if (Op->getKind() >= OperatorInfo::Unnameable)\n      /* Not a nameable operator.  */\n      return nullptr;\n    if (Op->getKind() == OperatorInfo::Member && !Op->getFlag())\n      /* Not a nameable MemberExpr */\n      return nullptr;\n\n    return make<NameType>(Op->getName());\n  }\n\n  if (consumeIf(\"li\")) {\n    //                   ::= li <source-name>  # operator \"\"\n    Node *SN = getDerived().parseSourceName(State);\n    if (SN == nullptr)\n      return nullptr;\n    return make<LiteralOperator>(SN);\n  }\n\n  if (consumeIf('v')) {\n    // ::= v <digit> <source-name>        # vendor extended operator\n    if (look() >= '0' && look() <= '9') {\n      First++;\n      Node *SN = getDerived().parseSourceName(State);\n      if (SN == nullptr)\n        return nullptr;\n      return make<ConversionOperatorType>(SN);\n    }\n    return nullptr;\n  }\n\n  return nullptr;\n}\n\n// <ctor-dtor-name> ::= C1  # complete object constructor\n//                  ::= C2  # base object constructor\n//                  ::= C3  # complete object allocating constructor\n//   extension      ::= C4  # gcc old-style \"[unified]\" constructor\n//   extension      ::= C5  # the COMDAT used for ctors\n//                  ::= D0  # deleting destructor\n//                  ::= D1  # complete object destructor\n//                  ::= D2  # base object destructor\n//   extension      ::= D4  # gcc old-style \"[unified]\" destructor\n//   extension      ::= D5  # the COMDAT used for dtors\ntemplate <typename Derived, typename Alloc>\nNode *\nAbstractManglingParser<Derived, Alloc>::parseCtorDtorName(Node *&SoFar,\n                                                          NameState *State) {\n  if (SoFar->getKind() == Node::KSpecialSubstitution) {\n    // Expand the special substitution.\n    SoFar = make<ExpandedSpecialSubstitution>(\n        static_cast<SpecialSubstitution *>(SoFar));\n    if (!SoFar)\n      return nullptr;\n  }\n\n  if (consumeIf('C')) {\n    bool IsInherited = consumeIf('I');\n    if (look() != '1' && look() != '2' && look() != '3' && look() != '4' &&\n        look() != '5')\n      return nullptr;\n    int Variant = look() - '0';\n    ++First;\n    if (State) State->CtorDtorConversion = true;\n    if (IsInherited) {\n      if (getDerived().parseName(State) == nullptr)\n        return nullptr;\n    }\n    return make<CtorDtorName>(SoFar, /*IsDtor=*/false, Variant);\n  }\n\n  if (look() == 'D' && (look(1) == '0' || look(1) == '1' || look(1) == '2' ||\n                        look(1) == '4' || look(1) == '5')) {\n    int Variant = look(1) - '0';\n    First += 2;\n    if (State) State->CtorDtorConversion = true;\n    return make<CtorDtorName>(SoFar, /*IsDtor=*/true, Variant);\n  }\n\n  return nullptr;\n}\n\n// <nested-name> ::= N [<CV-Qualifiers>] [<ref-qualifier>] <prefix>\n// \t\t\t<unqualified-name> E\n//               ::= N [<CV-Qualifiers>] [<ref-qualifier>] <template-prefix>\n//               \t<template-args> E\n//\n// <prefix> ::= <prefix> <unqualified-name>\n//          ::= <template-prefix> <template-args>\n//          ::= <template-param>\n//          ::= <decltype>\n//          ::= # empty\n//          ::= <substitution>\n//          ::= <prefix> <data-member-prefix>\n// [*] extension\n//\n// <data-member-prefix> := <member source-name> [<template-args>] M\n//\n// <template-prefix> ::= <prefix> <template unqualified-name>\n//                   ::= <template-param>\n//                   ::= <substitution>\ntemplate <typename Derived, typename Alloc>\nNode *\nAbstractManglingParser<Derived, Alloc>::parseNestedName(NameState *State) {\n  if (!consumeIf('N'))\n    return nullptr;\n\n  // 'H' specifies that the encoding that follows\n  // has an explicit object parameter.\n  if (!consumeIf('H')) {\n    Qualifiers CVTmp = parseCVQualifiers();\n    if (State)\n      State->CVQualifiers = CVTmp;\n\n    if (consumeIf('O')) {\n      if (State)\n        State->ReferenceQualifier = FrefQualRValue;\n    } else if (consumeIf('R')) {\n      if (State)\n        State->ReferenceQualifier = FrefQualLValue;\n    } else {\n      if (State)\n        State->ReferenceQualifier = FrefQualNone;\n    }\n  } else if (State) {\n    State->HasExplicitObjectParameter = true;\n  }\n\n  Node *SoFar = nullptr;\n  while (!consumeIf('E')) {\n    if (State)\n      // Only set end-with-template on the case that does that.\n      State->EndsWithTemplateArgs = false;\n\n    if (look() == 'T') {\n      //          ::= <template-param>\n      if (SoFar != nullptr)\n        return nullptr; // Cannot have a prefix.\n      SoFar = getDerived().parseTemplateParam();\n    } else if (look() == 'I') {\n      //          ::= <template-prefix> <template-args>\n      if (SoFar == nullptr)\n        return nullptr; // Must have a prefix.\n      Node *TA = getDerived().parseTemplateArgs(State != nullptr);\n      if (TA == nullptr)\n        return nullptr;\n      if (SoFar->getKind() == Node::KNameWithTemplateArgs)\n        // Semantically <template-args> <template-args> cannot be generated by a\n        // C++ entity.  There will always be [something like] a name between\n        // them.\n        return nullptr;\n      if (State)\n        State->EndsWithTemplateArgs = true;\n      SoFar = make<NameWithTemplateArgs>(SoFar, TA);\n    } else if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) {\n      //          ::= <decltype>\n      if (SoFar != nullptr)\n        return nullptr; // Cannot have a prefix.\n      SoFar = getDerived().parseDecltype();\n    } else {\n      ModuleName *Module = nullptr;\n\n      if (look() == 'S') {\n        //          ::= <substitution>\n        Node *S = nullptr;\n        if (look(1) == 't') {\n          First += 2;\n          S = make<NameType>(\"std\");\n        } else {\n          S = getDerived().parseSubstitution();\n        }\n        if (!S)\n          return nullptr;\n        if (S->getKind() == Node::KModuleName) {\n          Module = static_cast<ModuleName *>(S);\n        } else if (SoFar != nullptr) {\n          return nullptr; // Cannot have a prefix.\n        } else {\n          SoFar = S;\n          continue; // Do not push a new substitution.\n        }\n      }\n\n      //          ::= [<prefix>] <unqualified-name>\n      SoFar = getDerived().parseUnqualifiedName(State, SoFar, Module);\n    }\n\n    if (SoFar == nullptr)\n      return nullptr;\n    Subs.push_back(SoFar);\n\n    // No longer used.\n    // <data-member-prefix> := <member source-name> [<template-args>] M\n    consumeIf('M');\n  }\n\n  if (SoFar == nullptr || Subs.empty())\n    return nullptr;\n\n  Subs.pop_back();\n  return SoFar;\n}\n\n// <simple-id> ::= <source-name> [ <template-args> ]\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseSimpleId() {\n  Node *SN = getDerived().parseSourceName(/*NameState=*/nullptr);\n  if (SN == nullptr)\n    return nullptr;\n  if (look() == 'I') {\n    Node *TA = getDerived().parseTemplateArgs();\n    if (TA == nullptr)\n      return nullptr;\n    return make<NameWithTemplateArgs>(SN, TA);\n  }\n  return SN;\n}\n\n// <destructor-name> ::= <unresolved-type>  # e.g., ~T or ~decltype(f())\n//                   ::= <simple-id>        # e.g., ~A<2*N>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseDestructorName() {\n  Node *Result;\n  if (std::isdigit(look()))\n    Result = getDerived().parseSimpleId();\n  else\n    Result = getDerived().parseUnresolvedType();\n  if (Result == nullptr)\n    return nullptr;\n  return make<DtorName>(Result);\n}\n\n// <unresolved-type> ::= <template-param>\n//                   ::= <decltype>\n//                   ::= <substitution>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseUnresolvedType() {\n  if (look() == 'T') {\n    Node *TP = getDerived().parseTemplateParam();\n    if (TP == nullptr)\n      return nullptr;\n    Subs.push_back(TP);\n    return TP;\n  }\n  if (look() == 'D') {\n    Node *DT = getDerived().parseDecltype();\n    if (DT == nullptr)\n      return nullptr;\n    Subs.push_back(DT);\n    return DT;\n  }\n  return getDerived().parseSubstitution();\n}\n\n// <base-unresolved-name> ::= <simple-id>                                # unresolved name\n//          extension     ::= <operator-name>                            # unresolved operator-function-id\n//          extension     ::= <operator-name> <template-args>            # unresolved operator template-id\n//                        ::= on <operator-name>                         # unresolved operator-function-id\n//                        ::= on <operator-name> <template-args>         # unresolved operator template-id\n//                        ::= dn <destructor-name>                       # destructor or pseudo-destructor;\n//                                                                         # e.g. ~X or ~X<N-1>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseBaseUnresolvedName() {\n  if (std::isdigit(look()))\n    return getDerived().parseSimpleId();\n\n  if (consumeIf(\"dn\"))\n    return getDerived().parseDestructorName();\n\n  consumeIf(\"on\");\n\n  Node *Oper = getDerived().parseOperatorName(/*NameState=*/nullptr);\n  if (Oper == nullptr)\n    return nullptr;\n  if (look() == 'I') {\n    Node *TA = getDerived().parseTemplateArgs();\n    if (TA == nullptr)\n      return nullptr;\n    return make<NameWithTemplateArgs>(Oper, TA);\n  }\n  return Oper;\n}\n\n// <unresolved-name>\n//  extension        ::= srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name>\n//                   ::= [gs] <base-unresolved-name>                     # x or (with \"gs\") ::x\n//                   ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>\n//                                                                       # A::x, N::y, A<T>::z; \"gs\" means leading \"::\"\n// [gs] has been parsed by caller.\n//                   ::= sr <unresolved-type> <base-unresolved-name>     # T::x / decltype(p)::x\n//  extension        ::= sr <unresolved-type> <template-args> <base-unresolved-name>\n//                                                                       # T::N::x /decltype(p)::N::x\n//  (ignored)        ::= srN <unresolved-type>  <unresolved-qualifier-level>+ E <base-unresolved-name>\n//\n// <unresolved-qualifier-level> ::= <simple-id>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseUnresolvedName(bool Global) {\n  Node *SoFar = nullptr;\n\n  // srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name>\n  // srN <unresolved-type>                   <unresolved-qualifier-level>+ E <base-unresolved-name>\n  if (consumeIf(\"srN\")) {\n    SoFar = getDerived().parseUnresolvedType();\n    if (SoFar == nullptr)\n      return nullptr;\n\n    if (look() == 'I') {\n      Node *TA = getDerived().parseTemplateArgs();\n      if (TA == nullptr)\n        return nullptr;\n      SoFar = make<NameWithTemplateArgs>(SoFar, TA);\n      if (!SoFar)\n        return nullptr;\n    }\n\n    while (!consumeIf('E')) {\n      Node *Qual = getDerived().parseSimpleId();\n      if (Qual == nullptr)\n        return nullptr;\n      SoFar = make<QualifiedName>(SoFar, Qual);\n      if (!SoFar)\n        return nullptr;\n    }\n\n    Node *Base = getDerived().parseBaseUnresolvedName();\n    if (Base == nullptr)\n      return nullptr;\n    return make<QualifiedName>(SoFar, Base);\n  }\n\n  // [gs] <base-unresolved-name>                     # x or (with \"gs\") ::x\n  if (!consumeIf(\"sr\")) {\n    SoFar = getDerived().parseBaseUnresolvedName();\n    if (SoFar == nullptr)\n      return nullptr;\n    if (Global)\n      SoFar = make<GlobalQualifiedName>(SoFar);\n    return SoFar;\n  }\n\n  // [gs] sr <unresolved-qualifier-level>+ E   <base-unresolved-name>\n  if (std::isdigit(look())) {\n    do {\n      Node *Qual = getDerived().parseSimpleId();\n      if (Qual == nullptr)\n        return nullptr;\n      if (SoFar)\n        SoFar = make<QualifiedName>(SoFar, Qual);\n      else if (Global)\n        SoFar = make<GlobalQualifiedName>(Qual);\n      else\n        SoFar = Qual;\n      if (!SoFar)\n        return nullptr;\n    } while (!consumeIf('E'));\n  }\n  //      sr <unresolved-type>                 <base-unresolved-name>\n  //      sr <unresolved-type> <template-args> <base-unresolved-name>\n  else {\n    SoFar = getDerived().parseUnresolvedType();\n    if (SoFar == nullptr)\n      return nullptr;\n\n    if (look() == 'I') {\n      Node *TA = getDerived().parseTemplateArgs();\n      if (TA == nullptr)\n        return nullptr;\n      SoFar = make<NameWithTemplateArgs>(SoFar, TA);\n      if (!SoFar)\n        return nullptr;\n    }\n  }\n\n  DEMANGLE_ASSERT(SoFar != nullptr, \"\");\n\n  Node *Base = getDerived().parseBaseUnresolvedName();\n  if (Base == nullptr)\n    return nullptr;\n  return make<QualifiedName>(SoFar, Base);\n}\n\n// <abi-tags> ::= <abi-tag> [<abi-tags>]\n// <abi-tag> ::= B <source-name>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseAbiTags(Node *N) {\n  while (consumeIf('B')) {\n    std::string_view SN = parseBareSourceName();\n    if (SN.empty())\n      return nullptr;\n    N = make<AbiTagAttr>(N, SN);\n    if (!N)\n      return nullptr;\n  }\n  return N;\n}\n\n// <number> ::= [n] <non-negative decimal integer>\ntemplate <typename Alloc, typename Derived>\nstd::string_view\nAbstractManglingParser<Alloc, Derived>::parseNumber(bool AllowNegative) {\n  const char *Tmp = First;\n  if (AllowNegative)\n    consumeIf('n');\n  if (numLeft() == 0 || !std::isdigit(*First))\n    return std::string_view();\n  while (numLeft() != 0 && std::isdigit(*First))\n    ++First;\n  return std::string_view(Tmp, First - Tmp);\n}\n\n// <positive length number> ::= [0-9]*\ntemplate <typename Alloc, typename Derived>\nbool AbstractManglingParser<Alloc, Derived>::parsePositiveInteger(size_t *Out) {\n  *Out = 0;\n  if (look() < '0' || look() > '9')\n    return true;\n  while (look() >= '0' && look() <= '9') {\n    *Out *= 10;\n    *Out += static_cast<size_t>(consume() - '0');\n  }\n  return false;\n}\n\ntemplate <typename Alloc, typename Derived>\nstd::string_view AbstractManglingParser<Alloc, Derived>::parseBareSourceName() {\n  size_t Int = 0;\n  if (parsePositiveInteger(&Int) || numLeft() < Int)\n    return {};\n  std::string_view R(First, Int);\n  First += Int;\n  return R;\n}\n\n// <function-type> ::= [<CV-qualifiers>] [<exception-spec>] [Dx] F [Y] <bare-function-type> [<ref-qualifier>] E\n//\n// <exception-spec> ::= Do                # non-throwing exception-specification (e.g., noexcept, throw())\n//                  ::= DO <expression> E # computed (instantiation-dependent) noexcept\n//                  ::= Dw <type>+ E      # dynamic exception specification with instantiation-dependent types\n//\n// <ref-qualifier> ::= R                   # & ref-qualifier\n// <ref-qualifier> ::= O                   # && ref-qualifier\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseFunctionType() {\n  Qualifiers CVQuals = parseCVQualifiers();\n\n  Node *ExceptionSpec = nullptr;\n  if (consumeIf(\"Do\")) {\n    ExceptionSpec = make<NameType>(\"noexcept\");\n    if (!ExceptionSpec)\n      return nullptr;\n  } else if (consumeIf(\"DO\")) {\n    Node *E = getDerived().parseExpr();\n    if (E == nullptr || !consumeIf('E'))\n      return nullptr;\n    ExceptionSpec = make<NoexceptSpec>(E);\n    if (!ExceptionSpec)\n      return nullptr;\n  } else if (consumeIf(\"Dw\")) {\n    size_t SpecsBegin = Names.size();\n    while (!consumeIf('E')) {\n      Node *T = getDerived().parseType();\n      if (T == nullptr)\n        return nullptr;\n      Names.push_back(T);\n    }\n    ExceptionSpec =\n      make<DynamicExceptionSpec>(popTrailingNodeArray(SpecsBegin));\n    if (!ExceptionSpec)\n      return nullptr;\n  }\n\n  consumeIf(\"Dx\"); // transaction safe\n\n  if (!consumeIf('F'))\n    return nullptr;\n  consumeIf('Y'); // extern \"C\"\n  Node *ReturnType = getDerived().parseType();\n  if (ReturnType == nullptr)\n    return nullptr;\n\n  FunctionRefQual ReferenceQualifier = FrefQualNone;\n  size_t ParamsBegin = Names.size();\n  while (true) {\n    if (consumeIf('E'))\n      break;\n    if (consumeIf('v'))\n      continue;\n    if (consumeIf(\"RE\")) {\n      ReferenceQualifier = FrefQualLValue;\n      break;\n    }\n    if (consumeIf(\"OE\")) {\n      ReferenceQualifier = FrefQualRValue;\n      break;\n    }\n    Node *T = getDerived().parseType();\n    if (T == nullptr)\n      return nullptr;\n    Names.push_back(T);\n  }\n\n  NodeArray Params = popTrailingNodeArray(ParamsBegin);\n  return make<FunctionType>(ReturnType, Params, CVQuals,\n                            ReferenceQualifier, ExceptionSpec);\n}\n\n// extension:\n// <vector-type>           ::= Dv <positive dimension number> _ <extended element type>\n//                         ::= Dv [<dimension expression>] _ <element type>\n// <extended element type> ::= <element type>\n//                         ::= p # AltiVec vector pixel\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseVectorType() {\n  if (!consumeIf(\"Dv\"))\n    return nullptr;\n  if (look() >= '1' && look() <= '9') {\n    Node *DimensionNumber = make<NameType>(parseNumber());\n    if (!DimensionNumber)\n      return nullptr;\n    if (!consumeIf('_'))\n      return nullptr;\n    if (consumeIf('p'))\n      return make<PixelVectorType>(DimensionNumber);\n    Node *ElemType = getDerived().parseType();\n    if (ElemType == nullptr)\n      return nullptr;\n    return make<VectorType>(ElemType, DimensionNumber);\n  }\n\n  if (!consumeIf('_')) {\n    Node *DimExpr = getDerived().parseExpr();\n    if (!DimExpr)\n      return nullptr;\n    if (!consumeIf('_'))\n      return nullptr;\n    Node *ElemType = getDerived().parseType();\n    if (!ElemType)\n      return nullptr;\n    return make<VectorType>(ElemType, DimExpr);\n  }\n  Node *ElemType = getDerived().parseType();\n  if (!ElemType)\n    return nullptr;\n  return make<VectorType>(ElemType, /*Dimension=*/nullptr);\n}\n\n// <decltype>  ::= Dt <expression> E  # decltype of an id-expression or class member access (C++0x)\n//             ::= DT <expression> E  # decltype of an expression (C++0x)\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseDecltype() {\n  if (!consumeIf('D'))\n    return nullptr;\n  if (!consumeIf('t') && !consumeIf('T'))\n    return nullptr;\n  Node *E = getDerived().parseExpr();\n  if (E == nullptr)\n    return nullptr;\n  if (!consumeIf('E'))\n    return nullptr;\n  return make<EnclosingExpr>(\"decltype\", E);\n}\n\n// <array-type> ::= A <positive dimension number> _ <element type>\n//              ::= A [<dimension expression>] _ <element type>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseArrayType() {\n  if (!consumeIf('A'))\n    return nullptr;\n\n  Node *Dimension = nullptr;\n\n  if (std::isdigit(look())) {\n    Dimension = make<NameType>(parseNumber());\n    if (!Dimension)\n      return nullptr;\n    if (!consumeIf('_'))\n      return nullptr;\n  } else if (!consumeIf('_')) {\n    Node *DimExpr = getDerived().parseExpr();\n    if (DimExpr == nullptr)\n      return nullptr;\n    if (!consumeIf('_'))\n      return nullptr;\n    Dimension = DimExpr;\n  }\n\n  Node *Ty = getDerived().parseType();\n  if (Ty == nullptr)\n    return nullptr;\n  return make<ArrayType>(Ty, Dimension);\n}\n\n// <pointer-to-member-type> ::= M <class type> <member type>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parsePointerToMemberType() {\n  if (!consumeIf('M'))\n    return nullptr;\n  Node *ClassType = getDerived().parseType();\n  if (ClassType == nullptr)\n    return nullptr;\n  Node *MemberType = getDerived().parseType();\n  if (MemberType == nullptr)\n    return nullptr;\n  return make<PointerToMemberType>(ClassType, MemberType);\n}\n\n// <class-enum-type> ::= <name>     # non-dependent type name, dependent type name, or dependent typename-specifier\n//                   ::= Ts <name>  # dependent elaborated type specifier using 'struct' or 'class'\n//                   ::= Tu <name>  # dependent elaborated type specifier using 'union'\n//                   ::= Te <name>  # dependent elaborated type specifier using 'enum'\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseClassEnumType() {\n  std::string_view ElabSpef;\n  if (consumeIf(\"Ts\"))\n    ElabSpef = \"struct\";\n  else if (consumeIf(\"Tu\"))\n    ElabSpef = \"union\";\n  else if (consumeIf(\"Te\"))\n    ElabSpef = \"enum\";\n\n  Node *Name = getDerived().parseName();\n  if (Name == nullptr)\n    return nullptr;\n\n  if (!ElabSpef.empty())\n    return make<ElaboratedTypeSpefType>(ElabSpef, Name);\n\n  return Name;\n}\n\n// <qualified-type>     ::= <qualifiers> <type>\n// <qualifiers> ::= <extended-qualifier>* <CV-qualifiers>\n// <extended-qualifier> ::= U <source-name> [<template-args>] # vendor extended type qualifier\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseQualifiedType() {\n  if (consumeIf('U')) {\n    std::string_view Qual = parseBareSourceName();\n    if (Qual.empty())\n      return nullptr;\n\n    // extension            ::= U <objc-name> <objc-type>  # objc-type<identifier>\n    if (starts_with(Qual, \"objcproto\")) {\n      constexpr size_t Len = sizeof(\"objcproto\") - 1;\n      std::string_view ProtoSourceName(Qual.data() + Len, Qual.size() - Len);\n      std::string_view Proto;\n      {\n        ScopedOverride<const char *> SaveFirst(First, ProtoSourceName.data()),\n            SaveLast(Last, &*ProtoSourceName.rbegin() + 1);\n        Proto = parseBareSourceName();\n      }\n      if (Proto.empty())\n        return nullptr;\n      Node *Child = getDerived().parseQualifiedType();\n      if (Child == nullptr)\n        return nullptr;\n      return make<ObjCProtoName>(Child, Proto);\n    }\n\n    Node *TA = nullptr;\n    if (look() == 'I') {\n      TA = getDerived().parseTemplateArgs();\n      if (TA == nullptr)\n        return nullptr;\n    }\n\n    Node *Child = getDerived().parseQualifiedType();\n    if (Child == nullptr)\n      return nullptr;\n    return make<VendorExtQualType>(Child, Qual, TA);\n  }\n\n  Qualifiers Quals = parseCVQualifiers();\n  Node *Ty = getDerived().parseType();\n  if (Ty == nullptr)\n    return nullptr;\n  if (Quals != QualNone)\n    Ty = make<QualType>(Ty, Quals);\n  return Ty;\n}\n\n// <type>      ::= <builtin-type>\n//             ::= <qualified-type>\n//             ::= <function-type>\n//             ::= <class-enum-type>\n//             ::= <array-type>\n//             ::= <pointer-to-member-type>\n//             ::= <template-param>\n//             ::= <template-template-param> <template-args>\n//             ::= <decltype>\n//             ::= P <type>        # pointer\n//             ::= R <type>        # l-value reference\n//             ::= O <type>        # r-value reference (C++11)\n//             ::= C <type>        # complex pair (C99)\n//             ::= G <type>        # imaginary (C99)\n//             ::= <substitution>  # See Compression below\n// extension   ::= U <objc-name> <objc-type>  # objc-type<identifier>\n// extension   ::= <vector-type> # <vector-type> starts with Dv\n//\n// <objc-name> ::= <k0 number> objcproto <k1 number> <identifier>  # k0 = 9 + <number of digits in k1> + k1\n// <objc-type> ::= <source-name>  # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseType() {\n  Node *Result = nullptr;\n\n  switch (look()) {\n  //             ::= <qualified-type>\n  case 'r':\n  case 'V':\n  case 'K': {\n    unsigned AfterQuals = 0;\n    if (look(AfterQuals) == 'r') ++AfterQuals;\n    if (look(AfterQuals) == 'V') ++AfterQuals;\n    if (look(AfterQuals) == 'K') ++AfterQuals;\n\n    if (look(AfterQuals) == 'F' ||\n        (look(AfterQuals) == 'D' &&\n         (look(AfterQuals + 1) == 'o' || look(AfterQuals + 1) == 'O' ||\n          look(AfterQuals + 1) == 'w' || look(AfterQuals + 1) == 'x'))) {\n      Result = getDerived().parseFunctionType();\n      break;\n    }\n    DEMANGLE_FALLTHROUGH;\n  }\n  case 'U': {\n    Result = getDerived().parseQualifiedType();\n    break;\n  }\n  // <builtin-type> ::= v    # void\n  case 'v':\n    ++First;\n    return make<NameType>(\"void\");\n  //                ::= w    # wchar_t\n  case 'w':\n    ++First;\n    return make<NameType>(\"wchar_t\");\n  //                ::= b    # bool\n  case 'b':\n    ++First;\n    return make<NameType>(\"bool\");\n  //                ::= c    # char\n  case 'c':\n    ++First;\n    return make<NameType>(\"char\");\n  //                ::= a    # signed char\n  case 'a':\n    ++First;\n    return make<NameType>(\"signed char\");\n  //                ::= h    # unsigned char\n  case 'h':\n    ++First;\n    return make<NameType>(\"unsigned char\");\n  //                ::= s    # short\n  case 's':\n    ++First;\n    return make<NameType>(\"short\");\n  //                ::= t    # unsigned short\n  case 't':\n    ++First;\n    return make<NameType>(\"unsigned short\");\n  //                ::= i    # int\n  case 'i':\n    ++First;\n    return make<NameType>(\"int\");\n  //                ::= j    # unsigned int\n  case 'j':\n    ++First;\n    return make<NameType>(\"unsigned int\");\n  //                ::= l    # long\n  case 'l':\n    ++First;\n    return make<NameType>(\"long\");\n  //                ::= m    # unsigned long\n  case 'm':\n    ++First;\n    return make<NameType>(\"unsigned long\");\n  //                ::= x    # long long, __int64\n  case 'x':\n    ++First;\n    return make<NameType>(\"long long\");\n  //                ::= y    # unsigned long long, __int64\n  case 'y':\n    ++First;\n    return make<NameType>(\"unsigned long long\");\n  //                ::= n    # __int128\n  case 'n':\n    ++First;\n    return make<NameType>(\"__int128\");\n  //                ::= o    # unsigned __int128\n  case 'o':\n    ++First;\n    return make<NameType>(\"unsigned __int128\");\n  //                ::= f    # float\n  case 'f':\n    ++First;\n    return make<NameType>(\"float\");\n  //                ::= d    # double\n  case 'd':\n    ++First;\n    return make<NameType>(\"double\");\n  //                ::= e    # long double, __float80\n  case 'e':\n    ++First;\n    return make<NameType>(\"long double\");\n  //                ::= g    # __float128\n  case 'g':\n    ++First;\n    return make<NameType>(\"__float128\");\n  //                ::= z    # ellipsis\n  case 'z':\n    ++First;\n    return make<NameType>(\"...\");\n\n  // <builtin-type> ::= u <source-name>    # vendor extended type\n  case 'u': {\n    ++First;\n    std::string_view Res = parseBareSourceName();\n    if (Res.empty())\n      return nullptr;\n    // Typically, <builtin-type>s are not considered substitution candidates,\n    // but the exception to that exception is vendor extended types (Itanium C++\n    // ABI 5.9.1).\n    if (consumeIf('I')) {\n      Node *BaseType = parseType();\n      if (BaseType == nullptr)\n        return nullptr;\n      if (!consumeIf('E'))\n        return nullptr;\n      Result = make<TransformedType>(Res, BaseType);\n    } else\n      Result = make<NameType>(Res);\n    break;\n  }\n  case 'D':\n    switch (look(1)) {\n    //                ::= Dd   # IEEE 754r decimal floating point (64 bits)\n    case 'd':\n      First += 2;\n      return make<NameType>(\"decimal64\");\n    //                ::= De   # IEEE 754r decimal floating point (128 bits)\n    case 'e':\n      First += 2;\n      return make<NameType>(\"decimal128\");\n    //                ::= Df   # IEEE 754r decimal floating point (32 bits)\n    case 'f':\n      First += 2;\n      return make<NameType>(\"decimal32\");\n    //                ::= Dh   # IEEE 754r half-precision floating point (16 bits)\n    case 'h':\n      First += 2;\n      return make<NameType>(\"half\");\n    //                ::= DF <number> _ # ISO/IEC TS 18661 binary floating point (N bits)\n    case 'F': {\n      First += 2;\n      Node *DimensionNumber = make<NameType>(parseNumber());\n      if (!DimensionNumber)\n        return nullptr;\n      if (!consumeIf('_'))\n        return nullptr;\n      return make<BinaryFPType>(DimensionNumber);\n    }\n    //                ::= DB <number> _                             # C23 signed _BitInt(N)\n    //                ::= DB <instantiation-dependent expression> _ # C23 signed _BitInt(N)\n    //                ::= DU <number> _                             # C23 unsigned _BitInt(N)\n    //                ::= DU <instantiation-dependent expression> _ # C23 unsigned _BitInt(N)\n    case 'B':\n    case 'U': {\n      bool Signed = look(1) == 'B';\n      First += 2;\n      Node *Size = std::isdigit(look()) ? make<NameType>(parseNumber())\n                                        : getDerived().parseExpr();\n      if (!Size)\n        return nullptr;\n      if (!consumeIf('_'))\n        return nullptr;\n      return make<BitIntType>(Size, Signed);\n    }\n    //                ::= Di   # char32_t\n    case 'i':\n      First += 2;\n      return make<NameType>(\"char32_t\");\n    //                ::= Ds   # char16_t\n    case 's':\n      First += 2;\n      return make<NameType>(\"char16_t\");\n    //                ::= Du   # char8_t (C++2a, not yet in the Itanium spec)\n    case 'u':\n      First += 2;\n      return make<NameType>(\"char8_t\");\n    //                ::= Da   # auto (in dependent new-expressions)\n    case 'a':\n      First += 2;\n      return make<NameType>(\"auto\");\n    //                ::= Dc   # decltype(auto)\n    case 'c':\n      First += 2;\n      return make<NameType>(\"decltype(auto)\");\n    //                ::= Dk <type-constraint> # constrained auto\n    //                ::= DK <type-constraint> # constrained decltype(auto)\n    case 'k':\n    case 'K': {\n      std::string_view Kind = look(1) == 'k' ? \" auto\" : \" decltype(auto)\";\n      First += 2;\n      Node *Constraint = getDerived().parseName();\n      if (!Constraint)\n        return nullptr;\n      return make<PostfixQualifiedType>(Constraint, Kind);\n    }\n    //                ::= Dn   # std::nullptr_t (i.e., decltype(nullptr))\n    case 'n':\n      First += 2;\n      return make<NameType>(\"std::nullptr_t\");\n\n    //             ::= <decltype>\n    case 't':\n    case 'T': {\n      Result = getDerived().parseDecltype();\n      break;\n    }\n    // extension   ::= <vector-type> # <vector-type> starts with Dv\n    case 'v': {\n      Result = getDerived().parseVectorType();\n      break;\n    }\n    //           ::= Dp <type>       # pack expansion (C++0x)\n    case 'p': {\n      First += 2;\n      Node *Child = getDerived().parseType();\n      if (!Child)\n        return nullptr;\n      Result = make<ParameterPackExpansion>(Child);\n      break;\n    }\n    // Exception specifier on a function type.\n    case 'o':\n    case 'O':\n    case 'w':\n    // Transaction safe function type.\n    case 'x':\n      Result = getDerived().parseFunctionType();\n      break;\n    }\n    break;\n  //             ::= <function-type>\n  case 'F': {\n    Result = getDerived().parseFunctionType();\n    break;\n  }\n  //             ::= <array-type>\n  case 'A': {\n    Result = getDerived().parseArrayType();\n    break;\n  }\n  //             ::= <pointer-to-member-type>\n  case 'M': {\n    Result = getDerived().parsePointerToMemberType();\n    break;\n  }\n  //             ::= <template-param>\n  case 'T': {\n    // This could be an elaborate type specifier on a <class-enum-type>.\n    if (look(1) == 's' || look(1) == 'u' || look(1) == 'e') {\n      Result = getDerived().parseClassEnumType();\n      break;\n    }\n\n    Result = getDerived().parseTemplateParam();\n    if (Result == nullptr)\n      return nullptr;\n\n    // Result could be either of:\n    //   <type>        ::= <template-param>\n    //   <type>        ::= <template-template-param> <template-args>\n    //\n    //   <template-template-param> ::= <template-param>\n    //                             ::= <substitution>\n    //\n    // If this is followed by some <template-args>, and we're permitted to\n    // parse them, take the second production.\n\n    if (TryToParseTemplateArgs && look() == 'I') {\n      Node *TA = getDerived().parseTemplateArgs();\n      if (TA == nullptr)\n        return nullptr;\n      Result = make<NameWithTemplateArgs>(Result, TA);\n    }\n    break;\n  }\n  //             ::= P <type>        # pointer\n  case 'P': {\n    ++First;\n    Node *Ptr = getDerived().parseType();\n    if (Ptr == nullptr)\n      return nullptr;\n    Result = make<PointerType>(Ptr);\n    break;\n  }\n  //             ::= R <type>        # l-value reference\n  case 'R': {\n    ++First;\n    Node *Ref = getDerived().parseType();\n    if (Ref == nullptr)\n      return nullptr;\n    Result = make<ReferenceType>(Ref, ReferenceKind::LValue);\n    break;\n  }\n  //             ::= O <type>        # r-value reference (C++11)\n  case 'O': {\n    ++First;\n    Node *Ref = getDerived().parseType();\n    if (Ref == nullptr)\n      return nullptr;\n    Result = make<ReferenceType>(Ref, ReferenceKind::RValue);\n    break;\n  }\n  //             ::= C <type>        # complex pair (C99)\n  case 'C': {\n    ++First;\n    Node *P = getDerived().parseType();\n    if (P == nullptr)\n      return nullptr;\n    Result = make<PostfixQualifiedType>(P, \" complex\");\n    break;\n  }\n  //             ::= G <type>        # imaginary (C99)\n  case 'G': {\n    ++First;\n    Node *P = getDerived().parseType();\n    if (P == nullptr)\n      return P;\n    Result = make<PostfixQualifiedType>(P, \" imaginary\");\n    break;\n  }\n  //             ::= <substitution>  # See Compression below\n  case 'S': {\n    if (look(1) != 't') {\n      bool IsSubst = false;\n      Result = getDerived().parseUnscopedName(nullptr, &IsSubst);\n      if (!Result)\n        return nullptr;\n\n      // Sub could be either of:\n      //   <type>        ::= <substitution>\n      //   <type>        ::= <template-template-param> <template-args>\n      //\n      //   <template-template-param> ::= <template-param>\n      //                             ::= <substitution>\n      //\n      // If this is followed by some <template-args>, and we're permitted to\n      // parse them, take the second production.\n\n      if (look() == 'I' && (!IsSubst || TryToParseTemplateArgs)) {\n        if (!IsSubst)\n          Subs.push_back(Result);\n        Node *TA = getDerived().parseTemplateArgs();\n        if (TA == nullptr)\n          return nullptr;\n        Result = make<NameWithTemplateArgs>(Result, TA);\n      } else if (IsSubst) {\n        // If all we parsed was a substitution, don't re-insert into the\n        // substitution table.\n        return Result;\n      }\n      break;\n    }\n    DEMANGLE_FALLTHROUGH;\n  }\n  //        ::= <class-enum-type>\n  default: {\n    Result = getDerived().parseClassEnumType();\n    break;\n  }\n  }\n\n  // If we parsed a type, insert it into the substitution table. Note that all\n  // <builtin-type>s and <substitution>s have already bailed out, because they\n  // don't get substitutions.\n  if (Result != nullptr)\n    Subs.push_back(Result);\n  return Result;\n}\n\ntemplate <typename Derived, typename Alloc>\nNode *\nAbstractManglingParser<Derived, Alloc>::parsePrefixExpr(std::string_view Kind,\n                                                        Node::Prec Prec) {\n  Node *E = getDerived().parseExpr();\n  if (E == nullptr)\n    return nullptr;\n  return make<PrefixExpr>(Kind, E, Prec);\n}\n\ntemplate <typename Derived, typename Alloc>\nNode *\nAbstractManglingParser<Derived, Alloc>::parseBinaryExpr(std::string_view Kind,\n                                                        Node::Prec Prec) {\n  Node *LHS = getDerived().parseExpr();\n  if (LHS == nullptr)\n    return nullptr;\n  Node *RHS = getDerived().parseExpr();\n  if (RHS == nullptr)\n    return nullptr;\n  return make<BinaryExpr>(LHS, Kind, RHS, Prec);\n}\n\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseIntegerLiteral(\n    std::string_view Lit) {\n  std::string_view Tmp = parseNumber(true);\n  if (!Tmp.empty() && consumeIf('E'))\n    return make<IntegerLiteral>(Lit, Tmp);\n  return nullptr;\n}\n\n// <CV-Qualifiers> ::= [r] [V] [K]\ntemplate <typename Alloc, typename Derived>\nQualifiers AbstractManglingParser<Alloc, Derived>::parseCVQualifiers() {\n  Qualifiers CVR = QualNone;\n  if (consumeIf('r'))\n    CVR |= QualRestrict;\n  if (consumeIf('V'))\n    CVR |= QualVolatile;\n  if (consumeIf('K'))\n    CVR |= QualConst;\n  return CVR;\n}\n\n// <function-param> ::= fp <top-level CV-Qualifiers> _                                     # L == 0, first parameter\n//                  ::= fp <top-level CV-Qualifiers> <parameter-2 non-negative number> _   # L == 0, second and later parameters\n//                  ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> _         # L > 0, first parameter\n//                  ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> <parameter-2 non-negative number> _   # L > 0, second and later parameters\n//                  ::= fpT      # 'this' expression (not part of standard?)\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseFunctionParam() {\n  if (consumeIf(\"fpT\"))\n    return make<NameType>(\"this\");\n  if (consumeIf(\"fp\")) {\n    parseCVQualifiers();\n    std::string_view Num = parseNumber();\n    if (!consumeIf('_'))\n      return nullptr;\n    return make<FunctionParam>(Num);\n  }\n  if (consumeIf(\"fL\")) {\n    if (parseNumber().empty())\n      return nullptr;\n    if (!consumeIf('p'))\n      return nullptr;\n    parseCVQualifiers();\n    std::string_view Num = parseNumber();\n    if (!consumeIf('_'))\n      return nullptr;\n    return make<FunctionParam>(Num);\n  }\n  return nullptr;\n}\n\n// cv <type> <expression>                               # conversion with one argument\n// cv <type> _ <expression>* E                          # conversion with a different number of arguments\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseConversionExpr() {\n  if (!consumeIf(\"cv\"))\n    return nullptr;\n  Node *Ty;\n  {\n    ScopedOverride<bool> SaveTemp(TryToParseTemplateArgs, false);\n    Ty = getDerived().parseType();\n  }\n\n  if (Ty == nullptr)\n    return nullptr;\n\n  if (consumeIf('_')) {\n    size_t ExprsBegin = Names.size();\n    while (!consumeIf('E')) {\n      Node *E = getDerived().parseExpr();\n      if (E == nullptr)\n        return E;\n      Names.push_back(E);\n    }\n    NodeArray Exprs = popTrailingNodeArray(ExprsBegin);\n    return make<ConversionExpr>(Ty, Exprs);\n  }\n\n  Node *E[1] = {getDerived().parseExpr()};\n  if (E[0] == nullptr)\n    return nullptr;\n  return make<ConversionExpr>(Ty, makeNodeArray(E, E + 1));\n}\n\n// <expr-primary> ::= L <type> <value number> E                          # integer literal\n//                ::= L <type> <value float> E                           # floating literal\n//                ::= L <string type> E                                  # string literal\n//                ::= L <nullptr type> E                                 # nullptr literal (i.e., \"LDnE\")\n//                ::= L <lambda type> E                                  # lambda expression\n// FIXME:         ::= L <type> <real-part float> _ <imag-part float> E   # complex floating point literal (C 2000)\n//                ::= L <mangled-name> E                                 # external name\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseExprPrimary() {\n  if (!consumeIf('L'))\n    return nullptr;\n  switch (look()) {\n  case 'w':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"wchar_t\");\n  case 'b':\n    if (consumeIf(\"b0E\"))\n      return make<BoolExpr>(0);\n    if (consumeIf(\"b1E\"))\n      return make<BoolExpr>(1);\n    return nullptr;\n  case 'c':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"char\");\n  case 'a':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"signed char\");\n  case 'h':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"unsigned char\");\n  case 's':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"short\");\n  case 't':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"unsigned short\");\n  case 'i':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"\");\n  case 'j':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"u\");\n  case 'l':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"l\");\n  case 'm':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"ul\");\n  case 'x':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"ll\");\n  case 'y':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"ull\");\n  case 'n':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"__int128\");\n  case 'o':\n    ++First;\n    return getDerived().parseIntegerLiteral(\"unsigned __int128\");\n  case 'f':\n    ++First;\n    return getDerived().template parseFloatingLiteral<float>();\n  case 'd':\n    ++First;\n    return getDerived().template parseFloatingLiteral<double>();\n  case 'e':\n    ++First;\n#if defined(__powerpc__) || defined(__s390__)\n    // Handle cases where long doubles encoded with e have the same size\n    // and representation as doubles.\n    return getDerived().template parseFloatingLiteral<double>();\n#else\n    return getDerived().template parseFloatingLiteral<long double>();\n#endif\n  case '_':\n    if (consumeIf(\"_Z\")) {\n      Node *R = getDerived().parseEncoding();\n      if (R != nullptr && consumeIf('E'))\n        return R;\n    }\n    return nullptr;\n  case 'A': {\n    Node *T = getDerived().parseType();\n    if (T == nullptr)\n      return nullptr;\n    // FIXME: We need to include the string contents in the mangling.\n    if (consumeIf('E'))\n      return make<StringLiteral>(T);\n    return nullptr;\n  }\n  case 'D':\n    if (consumeIf(\"Dn\") && (consumeIf('0'), consumeIf('E')))\n      return make<NameType>(\"nullptr\");\n    return nullptr;\n  case 'T':\n    // Invalid mangled name per\n    //   http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html\n    return nullptr;\n  case 'U': {\n    // FIXME: Should we support LUb... for block literals?\n    if (look(1) != 'l')\n      return nullptr;\n    Node *T = parseUnnamedTypeName(nullptr);\n    if (!T || !consumeIf('E'))\n      return nullptr;\n    return make<LambdaExpr>(T);\n  }\n  default: {\n    // might be named type\n    Node *T = getDerived().parseType();\n    if (T == nullptr)\n      return nullptr;\n    std::string_view N = parseNumber(/*AllowNegative=*/true);\n    if (N.empty())\n      return nullptr;\n    if (!consumeIf('E'))\n      return nullptr;\n    return make<EnumLiteral>(T, N);\n  }\n  }\n}\n\n// <braced-expression> ::= <expression>\n//                     ::= di <field source-name> <braced-expression>    # .name = expr\n//                     ::= dx <index expression> <braced-expression>     # [expr] = expr\n//                     ::= dX <range begin expression> <range end expression> <braced-expression>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseBracedExpr() {\n  if (look() == 'd') {\n    switch (look(1)) {\n    case 'i': {\n      First += 2;\n      Node *Field = getDerived().parseSourceName(/*NameState=*/nullptr);\n      if (Field == nullptr)\n        return nullptr;\n      Node *Init = getDerived().parseBracedExpr();\n      if (Init == nullptr)\n        return nullptr;\n      return make<BracedExpr>(Field, Init, /*isArray=*/false);\n    }\n    case 'x': {\n      First += 2;\n      Node *Index = getDerived().parseExpr();\n      if (Index == nullptr)\n        return nullptr;\n      Node *Init = getDerived().parseBracedExpr();\n      if (Init == nullptr)\n        return nullptr;\n      return make<BracedExpr>(Index, Init, /*isArray=*/true);\n    }\n    case 'X': {\n      First += 2;\n      Node *RangeBegin = getDerived().parseExpr();\n      if (RangeBegin == nullptr)\n        return nullptr;\n      Node *RangeEnd = getDerived().parseExpr();\n      if (RangeEnd == nullptr)\n        return nullptr;\n      Node *Init = getDerived().parseBracedExpr();\n      if (Init == nullptr)\n        return nullptr;\n      return make<BracedRangeExpr>(RangeBegin, RangeEnd, Init);\n    }\n    }\n  }\n  return getDerived().parseExpr();\n}\n\n// (not yet in the spec)\n// <fold-expr> ::= fL <binary-operator-name> <expression> <expression>\n//             ::= fR <binary-operator-name> <expression> <expression>\n//             ::= fl <binary-operator-name> <expression>\n//             ::= fr <binary-operator-name> <expression>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseFoldExpr() {\n  if (!consumeIf('f'))\n    return nullptr;\n\n  bool IsLeftFold = false, HasInitializer = false;\n  switch (look()) {\n  default:\n    return nullptr;\n  case 'L':\n    IsLeftFold = true;\n    HasInitializer = true;\n    break;\n  case 'R':\n    HasInitializer = true;\n    break;\n  case 'l':\n    IsLeftFold = true;\n    break;\n  case 'r':\n    break;\n  }\n  ++First;\n\n  const auto *Op = parseOperatorEncoding();\n  if (!Op)\n    return nullptr;\n  if (!(Op->getKind() == OperatorInfo::Binary\n        || (Op->getKind() == OperatorInfo::Member\n            && Op->getName().back() == '*')))\n    return nullptr;\n\n  Node *Pack = getDerived().parseExpr();\n  if (Pack == nullptr)\n    return nullptr;\n\n  Node *Init = nullptr;\n  if (HasInitializer) {\n    Init = getDerived().parseExpr();\n    if (Init == nullptr)\n      return nullptr;\n  }\n\n  if (IsLeftFold && Init)\n    std::swap(Pack, Init);\n\n  return make<FoldExpr>(IsLeftFold, Op->getSymbol(), Pack, Init);\n}\n\n// <expression> ::= mc <parameter type> <expr> [<offset number>] E\n//\n// Not yet in the spec: https://github.com/itanium-cxx-abi/cxx-abi/issues/47\ntemplate <typename Derived, typename Alloc>\nNode *\nAbstractManglingParser<Derived, Alloc>::parsePointerToMemberConversionExpr(\n    Node::Prec Prec) {\n  Node *Ty = getDerived().parseType();\n  if (!Ty)\n    return nullptr;\n  Node *Expr = getDerived().parseExpr();\n  if (!Expr)\n    return nullptr;\n  std::string_view Offset = getDerived().parseNumber(true);\n  if (!consumeIf('E'))\n    return nullptr;\n  return make<PointerToMemberConversionExpr>(Ty, Expr, Offset, Prec);\n}\n\n// <expression> ::= so <referent type> <expr> [<offset number>] <union-selector>* [p] E\n// <union-selector> ::= _ [<number>]\n//\n// Not yet in the spec: https://github.com/itanium-cxx-abi/cxx-abi/issues/47\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseSubobjectExpr() {\n  Node *Ty = getDerived().parseType();\n  if (!Ty)\n    return nullptr;\n  Node *Expr = getDerived().parseExpr();\n  if (!Expr)\n    return nullptr;\n  std::string_view Offset = getDerived().parseNumber(true);\n  size_t SelectorsBegin = Names.size();\n  while (consumeIf('_')) {\n    Node *Selector = make<NameType>(parseNumber());\n    if (!Selector)\n      return nullptr;\n    Names.push_back(Selector);\n  }\n  bool OnePastTheEnd = consumeIf('p');\n  if (!consumeIf('E'))\n    return nullptr;\n  return make<SubobjectExpr>(\n      Ty, Expr, Offset, popTrailingNodeArray(SelectorsBegin), OnePastTheEnd);\n}\n\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseConstraintExpr() {\n  // Within this expression, all enclosing template parameter lists are in\n  // scope.\n  ScopedOverride<bool> SaveInConstraintExpr(InConstraintExpr, true);\n  return getDerived().parseExpr();\n}\n\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseRequiresExpr() {\n  NodeArray Params;\n  if (consumeIf(\"rQ\")) {\n    // <expression> ::= rQ <bare-function-type> _ <requirement>+ E\n    size_t ParamsBegin = Names.size();\n    while (!consumeIf('_')) {\n      Node *Type = getDerived().parseType();\n      if (Type == nullptr)\n        return nullptr;\n      Names.push_back(Type);\n    }\n    Params = popTrailingNodeArray(ParamsBegin);\n  } else if (!consumeIf(\"rq\")) {\n    // <expression> ::= rq <requirement>+ E\n    return nullptr;\n  }\n\n  size_t ReqsBegin = Names.size();\n  do {\n    Node *Constraint = nullptr;\n    if (consumeIf('X')) {\n      // <requirement> ::= X <expression> [N] [R <type-constraint>]\n      Node *Expr = getDerived().parseExpr();\n      if (Expr == nullptr)\n        return nullptr;\n      bool Noexcept = consumeIf('N');\n      Node *TypeReq = nullptr;\n      if (consumeIf('R')) {\n        TypeReq = getDerived().parseName();\n        if (TypeReq == nullptr)\n          return nullptr;\n      }\n      Constraint = make<ExprRequirement>(Expr, Noexcept, TypeReq);\n    } else if (consumeIf('T')) {\n      // <requirement> ::= T <type>\n      Node *Type = getDerived().parseType();\n      if (Type == nullptr)\n        return nullptr;\n      Constraint = make<TypeRequirement>(Type);\n    } else if (consumeIf('Q')) {\n      // <requirement> ::= Q <constraint-expression>\n      //\n      // FIXME: We use <expression> instead of <constraint-expression>. Either\n      // the requires expression is already inside a constraint expression, in\n      // which case it makes no difference, or we're in a requires-expression\n      // that might be partially-substituted, where the language behavior is\n      // not yet settled and clang mangles after substitution.\n      Node *NestedReq = getDerived().parseExpr();\n      if (NestedReq == nullptr)\n        return nullptr;\n      Constraint = make<NestedRequirement>(NestedReq);\n    }\n    if (Constraint == nullptr)\n      return nullptr;\n    Names.push_back(Constraint);\n  } while (!consumeIf('E'));\n\n  return make<RequiresExpr>(Params, popTrailingNodeArray(ReqsBegin));\n}\n\n// <expression> ::= <unary operator-name> <expression>\n//              ::= <binary operator-name> <expression> <expression>\n//              ::= <ternary operator-name> <expression> <expression> <expression>\n//              ::= cl <expression>+ E                                   # call\n//              ::= cv <type> <expression>                               # conversion with one argument\n//              ::= cv <type> _ <expression>* E                          # conversion with a different number of arguments\n//              ::= [gs] nw <expression>* _ <type> E                     # new (expr-list) type\n//              ::= [gs] nw <expression>* _ <type> <initializer>         # new (expr-list) type (init)\n//              ::= [gs] na <expression>* _ <type> E                     # new[] (expr-list) type\n//              ::= [gs] na <expression>* _ <type> <initializer>         # new[] (expr-list) type (init)\n//              ::= [gs] dl <expression>                                 # delete expression\n//              ::= [gs] da <expression>                                 # delete[] expression\n//              ::= pp_ <expression>                                     # prefix ++\n//              ::= mm_ <expression>                                     # prefix --\n//              ::= ti <type>                                            # typeid (type)\n//              ::= te <expression>                                      # typeid (expression)\n//              ::= dc <type> <expression>                               # dynamic_cast<type> (expression)\n//              ::= sc <type> <expression>                               # static_cast<type> (expression)\n//              ::= cc <type> <expression>                               # const_cast<type> (expression)\n//              ::= rc <type> <expression>                               # reinterpret_cast<type> (expression)\n//              ::= st <type>                                            # sizeof (a type)\n//              ::= sz <expression>                                      # sizeof (an expression)\n//              ::= at <type>                                            # alignof (a type)\n//              ::= az <expression>                                      # alignof (an expression)\n//              ::= nx <expression>                                      # noexcept (expression)\n//              ::= <template-param>\n//              ::= <function-param>\n//              ::= dt <expression> <unresolved-name>                    # expr.name\n//              ::= pt <expression> <unresolved-name>                    # expr->name\n//              ::= ds <expression> <expression>                         # expr.*expr\n//              ::= sZ <template-param>                                  # size of a parameter pack\n//              ::= sZ <function-param>                                  # size of a function parameter pack\n//              ::= sP <template-arg>* E                                 # sizeof...(T), size of a captured template parameter pack from an alias template\n//              ::= sp <expression>                                      # pack expansion\n//              ::= tw <expression>                                      # throw expression\n//              ::= tr                                                   # throw with no operand (rethrow)\n//              ::= <unresolved-name>                                    # f(p), N::f(p), ::f(p),\n//                                                                       # freestanding dependent name (e.g., T::x),\n//                                                                       # objectless nonstatic member reference\n//              ::= fL <binary-operator-name> <expression> <expression>\n//              ::= fR <binary-operator-name> <expression> <expression>\n//              ::= fl <binary-operator-name> <expression>\n//              ::= fr <binary-operator-name> <expression>\n//              ::= <expr-primary>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseExpr() {\n  bool Global = consumeIf(\"gs\");\n\n  const auto *Op = parseOperatorEncoding();\n  if (Op) {\n    auto Sym = Op->getSymbol();\n    switch (Op->getKind()) {\n    case OperatorInfo::Binary:\n      // Binary operator: lhs @ rhs\n      return getDerived().parseBinaryExpr(Sym, Op->getPrecedence());\n    case OperatorInfo::Prefix:\n      // Prefix unary operator: @ expr\n      return getDerived().parsePrefixExpr(Sym, Op->getPrecedence());\n    case OperatorInfo::Postfix: {\n      // Postfix unary operator: expr @\n      if (consumeIf('_'))\n        return getDerived().parsePrefixExpr(Sym, Op->getPrecedence());\n      Node *Ex = getDerived().parseExpr();\n      if (Ex == nullptr)\n        return nullptr;\n      return make<PostfixExpr>(Ex, Sym, Op->getPrecedence());\n    }\n    case OperatorInfo::Array: {\n      // Array Index:  lhs [ rhs ]\n      Node *Base = getDerived().parseExpr();\n      if (Base == nullptr)\n        return nullptr;\n      Node *Index = getDerived().parseExpr();\n      if (Index == nullptr)\n        return nullptr;\n      return make<ArraySubscriptExpr>(Base, Index, Op->getPrecedence());\n    }\n    case OperatorInfo::Member: {\n      // Member access lhs @ rhs\n      Node *LHS = getDerived().parseExpr();\n      if (LHS == nullptr)\n        return nullptr;\n      Node *RHS = getDerived().parseExpr();\n      if (RHS == nullptr)\n        return nullptr;\n      return make<MemberExpr>(LHS, Sym, RHS, Op->getPrecedence());\n    }\n    case OperatorInfo::New: {\n      // New\n      // # new (expr-list) type [(init)]\n      // [gs] nw <expression>* _ <type> [pi <expression>*] E\n      // # new[] (expr-list) type [(init)]\n      // [gs] na <expression>* _ <type> [pi <expression>*] E\n      size_t Exprs = Names.size();\n      while (!consumeIf('_')) {\n        Node *Ex = getDerived().parseExpr();\n        if (Ex == nullptr)\n          return nullptr;\n        Names.push_back(Ex);\n      }\n      NodeArray ExprList = popTrailingNodeArray(Exprs);\n      Node *Ty = getDerived().parseType();\n      if (Ty == nullptr)\n        return nullptr;\n      bool HaveInits = consumeIf(\"pi\");\n      size_t InitsBegin = Names.size();\n      while (!consumeIf('E')) {\n        if (!HaveInits)\n          return nullptr;\n        Node *Init = getDerived().parseExpr();\n        if (Init == nullptr)\n          return Init;\n        Names.push_back(Init);\n      }\n      NodeArray Inits = popTrailingNodeArray(InitsBegin);\n      return make<NewExpr>(ExprList, Ty, Inits, Global,\n                           /*IsArray=*/Op->getFlag(), Op->getPrecedence());\n    }\n    case OperatorInfo::Del: {\n      // Delete\n      Node *Ex = getDerived().parseExpr();\n      if (Ex == nullptr)\n        return nullptr;\n      return make<DeleteExpr>(Ex, Global, /*IsArray=*/Op->getFlag(),\n                              Op->getPrecedence());\n    }\n    case OperatorInfo::Call: {\n      // Function Call\n      Node *Callee = getDerived().parseExpr();\n      if (Callee == nullptr)\n        return nullptr;\n      size_t ExprsBegin = Names.size();\n      while (!consumeIf('E')) {\n        Node *E = getDerived().parseExpr();\n        if (E == nullptr)\n          return nullptr;\n        Names.push_back(E);\n      }\n      return make<CallExpr>(Callee, popTrailingNodeArray(ExprsBegin),\n                            Op->getPrecedence());\n    }\n    case OperatorInfo::CCast: {\n      // C Cast: (type)expr\n      Node *Ty;\n      {\n        ScopedOverride<bool> SaveTemp(TryToParseTemplateArgs, false);\n        Ty = getDerived().parseType();\n      }\n      if (Ty == nullptr)\n        return nullptr;\n\n      size_t ExprsBegin = Names.size();\n      bool IsMany = consumeIf('_');\n      while (!consumeIf('E')) {\n        Node *E = getDerived().parseExpr();\n        if (E == nullptr)\n          return E;\n        Names.push_back(E);\n        if (!IsMany)\n          break;\n      }\n      NodeArray Exprs = popTrailingNodeArray(ExprsBegin);\n      if (!IsMany && Exprs.size() != 1)\n        return nullptr;\n      return make<ConversionExpr>(Ty, Exprs, Op->getPrecedence());\n    }\n    case OperatorInfo::Conditional: {\n      // Conditional operator: expr ? expr : expr\n      Node *Cond = getDerived().parseExpr();\n      if (Cond == nullptr)\n        return nullptr;\n      Node *LHS = getDerived().parseExpr();\n      if (LHS == nullptr)\n        return nullptr;\n      Node *RHS = getDerived().parseExpr();\n      if (RHS == nullptr)\n        return nullptr;\n      return make<ConditionalExpr>(Cond, LHS, RHS, Op->getPrecedence());\n    }\n    case OperatorInfo::NamedCast: {\n      // Named cast operation, @<type>(expr)\n      Node *Ty = getDerived().parseType();\n      if (Ty == nullptr)\n        return nullptr;\n      Node *Ex = getDerived().parseExpr();\n      if (Ex == nullptr)\n        return nullptr;\n      return make<CastExpr>(Sym, Ty, Ex, Op->getPrecedence());\n    }\n    case OperatorInfo::OfIdOp: {\n      // [sizeof/alignof/typeid] ( <type>|<expr> )\n      Node *Arg =\n          Op->getFlag() ? getDerived().parseType() : getDerived().parseExpr();\n      if (!Arg)\n        return nullptr;\n      return make<EnclosingExpr>(Sym, Arg, Op->getPrecedence());\n    }\n    case OperatorInfo::NameOnly: {\n      // Not valid as an expression operand.\n      return nullptr;\n    }\n    }\n    DEMANGLE_UNREACHABLE;\n  }\n\n  if (numLeft() < 2)\n    return nullptr;\n\n  if (look() == 'L')\n    return getDerived().parseExprPrimary();\n  if (look() == 'T')\n    return getDerived().parseTemplateParam();\n  if (look() == 'f') {\n    // Disambiguate a fold expression from a <function-param>.\n    if (look(1) == 'p' || (look(1) == 'L' && std::isdigit(look(2))))\n      return getDerived().parseFunctionParam();\n    return getDerived().parseFoldExpr();\n  }\n  if (consumeIf(\"il\")) {\n    size_t InitsBegin = Names.size();\n    while (!consumeIf('E')) {\n      Node *E = getDerived().parseBracedExpr();\n      if (E == nullptr)\n        return nullptr;\n      Names.push_back(E);\n    }\n    return make<InitListExpr>(nullptr, popTrailingNodeArray(InitsBegin));\n  }\n  if (consumeIf(\"mc\"))\n    return parsePointerToMemberConversionExpr(Node::Prec::Unary);\n  if (consumeIf(\"nx\")) {\n    Node *Ex = getDerived().parseExpr();\n    if (Ex == nullptr)\n      return Ex;\n    return make<EnclosingExpr>(\"noexcept \", Ex, Node::Prec::Unary);\n  }\n  if (look() == 'r' && (look(1) == 'q' || look(1) == 'Q'))\n    return parseRequiresExpr();\n  if (consumeIf(\"so\"))\n    return parseSubobjectExpr();\n  if (consumeIf(\"sp\")) {\n    Node *Child = getDerived().parseExpr();\n    if (Child == nullptr)\n      return nullptr;\n    return make<ParameterPackExpansion>(Child);\n  }\n  if (consumeIf(\"sZ\")) {\n    if (look() == 'T') {\n      Node *R = getDerived().parseTemplateParam();\n      if (R == nullptr)\n        return nullptr;\n      return make<SizeofParamPackExpr>(R);\n    }\n    Node *FP = getDerived().parseFunctionParam();\n    if (FP == nullptr)\n      return nullptr;\n    return make<EnclosingExpr>(\"sizeof... \", FP);\n  }\n  if (consumeIf(\"sP\")) {\n    size_t ArgsBegin = Names.size();\n    while (!consumeIf('E')) {\n      Node *Arg = getDerived().parseTemplateArg();\n      if (Arg == nullptr)\n        return nullptr;\n      Names.push_back(Arg);\n    }\n    auto *Pack = make<NodeArrayNode>(popTrailingNodeArray(ArgsBegin));\n    if (!Pack)\n      return nullptr;\n    return make<EnclosingExpr>(\"sizeof... \", Pack);\n  }\n  if (consumeIf(\"tl\")) {\n    Node *Ty = getDerived().parseType();\n    if (Ty == nullptr)\n      return nullptr;\n    size_t InitsBegin = Names.size();\n    while (!consumeIf('E')) {\n      Node *E = getDerived().parseBracedExpr();\n      if (E == nullptr)\n        return nullptr;\n      Names.push_back(E);\n    }\n    return make<InitListExpr>(Ty, popTrailingNodeArray(InitsBegin));\n  }\n  if (consumeIf(\"tr\"))\n    return make<NameType>(\"throw\");\n  if (consumeIf(\"tw\")) {\n    Node *Ex = getDerived().parseExpr();\n    if (Ex == nullptr)\n      return nullptr;\n    return make<ThrowExpr>(Ex);\n  }\n  if (consumeIf('u')) {\n    Node *Name = getDerived().parseSourceName(/*NameState=*/nullptr);\n    if (!Name)\n      return nullptr;\n    // Special case legacy __uuidof mangling. The 't' and 'z' appear where the\n    // standard encoding expects a <template-arg>, and would be otherwise be\n    // interpreted as <type> node 'short' or 'ellipsis'. However, neither\n    // __uuidof(short) nor __uuidof(...) can actually appear, so there is no\n    // actual conflict here.\n    bool IsUUID = false;\n    Node *UUID = nullptr;\n    if (Name->getBaseName() == \"__uuidof\") {\n      if (consumeIf('t')) {\n        UUID = getDerived().parseType();\n        IsUUID = true;\n      } else if (consumeIf('z')) {\n        UUID = getDerived().parseExpr();\n        IsUUID = true;\n      }\n    }\n    size_t ExprsBegin = Names.size();\n    if (IsUUID) {\n      if (UUID == nullptr)\n        return nullptr;\n      Names.push_back(UUID);\n    } else {\n      while (!consumeIf('E')) {\n        Node *E = getDerived().parseTemplateArg();\n        if (E == nullptr)\n          return E;\n        Names.push_back(E);\n      }\n    }\n    return make<CallExpr>(Name, popTrailingNodeArray(ExprsBegin),\n                          Node::Prec::Postfix);\n  }\n\n  // Only unresolved names remain.\n  return getDerived().parseUnresolvedName(Global);\n}\n\n// <call-offset> ::= h <nv-offset> _\n//               ::= v <v-offset> _\n//\n// <nv-offset> ::= <offset number>\n//               # non-virtual base override\n//\n// <v-offset>  ::= <offset number> _ <virtual offset number>\n//               # virtual base override, with vcall offset\ntemplate <typename Alloc, typename Derived>\nbool AbstractManglingParser<Alloc, Derived>::parseCallOffset() {\n  // Just scan through the call offset, we never add this information into the\n  // output.\n  if (consumeIf('h'))\n    return parseNumber(true).empty() || !consumeIf('_');\n  if (consumeIf('v'))\n    return parseNumber(true).empty() || !consumeIf('_') ||\n           parseNumber(true).empty() || !consumeIf('_');\n  return true;\n}\n\n// <special-name> ::= TV <type>    # virtual table\n//                ::= TT <type>    # VTT structure (construction vtable index)\n//                ::= TI <type>    # typeinfo structure\n//                ::= TS <type>    # typeinfo name (null-terminated byte string)\n//                ::= Tc <call-offset> <call-offset> <base encoding>\n//                    # base is the nominal target function of thunk\n//                    # first call-offset is 'this' adjustment\n//                    # second call-offset is result adjustment\n//                ::= T <call-offset> <base encoding>\n//                    # base is the nominal target function of thunk\n//                # Guard variable for one-time initialization\n//                ::= GV <object name>\n//                                     # No <type>\n//                ::= TW <object name> # Thread-local wrapper\n//                ::= TH <object name> # Thread-local initialization\n//                ::= GR <object name> _             # First temporary\n//                ::= GR <object name> <seq-id> _    # Subsequent temporaries\n//                # construction vtable for second-in-first\n//      extension ::= TC <first type> <number> _ <second type>\n//      extension ::= GR <object name> # reference temporary for object\n//      extension ::= GI <module name> # module global initializer\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseSpecialName() {\n  switch (look()) {\n  case 'T':\n    switch (look(1)) {\n    // TA <template-arg>    # template parameter object\n    //\n    // Not yet in the spec: https://github.com/itanium-cxx-abi/cxx-abi/issues/63\n    case 'A': {\n      First += 2;\n      Node *Arg = getDerived().parseTemplateArg();\n      if (Arg == nullptr)\n        return nullptr;\n      return make<SpecialName>(\"template parameter object for \", Arg);\n    }\n    // TV <type>    # virtual table\n    case 'V': {\n      First += 2;\n      Node *Ty = getDerived().parseType();\n      if (Ty == nullptr)\n        return nullptr;\n      return make<SpecialName>(\"vtable for \", Ty);\n    }\n    // TT <type>    # VTT structure (construction vtable index)\n    case 'T': {\n      First += 2;\n      Node *Ty = getDerived().parseType();\n      if (Ty == nullptr)\n        return nullptr;\n      return make<SpecialName>(\"VTT for \", Ty);\n    }\n    // TI <type>    # typeinfo structure\n    case 'I': {\n      First += 2;\n      Node *Ty = getDerived().parseType();\n      if (Ty == nullptr)\n        return nullptr;\n      return make<SpecialName>(\"typeinfo for \", Ty);\n    }\n    // TS <type>    # typeinfo name (null-terminated byte string)\n    case 'S': {\n      First += 2;\n      Node *Ty = getDerived().parseType();\n      if (Ty == nullptr)\n        return nullptr;\n      return make<SpecialName>(\"typeinfo name for \", Ty);\n    }\n    // Tc <call-offset> <call-offset> <base encoding>\n    case 'c': {\n      First += 2;\n      if (parseCallOffset() || parseCallOffset())\n        return nullptr;\n      Node *Encoding = getDerived().parseEncoding();\n      if (Encoding == nullptr)\n        return nullptr;\n      return make<SpecialName>(\"covariant return thunk to \", Encoding);\n    }\n    // extension ::= TC <first type> <number> _ <second type>\n    //               # construction vtable for second-in-first\n    case 'C': {\n      First += 2;\n      Node *FirstType = getDerived().parseType();\n      if (FirstType == nullptr)\n        return nullptr;\n      if (parseNumber(true).empty() || !consumeIf('_'))\n        return nullptr;\n      Node *SecondType = getDerived().parseType();\n      if (SecondType == nullptr)\n        return nullptr;\n      return make<CtorVtableSpecialName>(SecondType, FirstType);\n    }\n    // TW <object name> # Thread-local wrapper\n    case 'W': {\n      First += 2;\n      Node *Name = getDerived().parseName();\n      if (Name == nullptr)\n        return nullptr;\n      return make<SpecialName>(\"thread-local wrapper routine for \", Name);\n    }\n    // TH <object name> # Thread-local initialization\n    case 'H': {\n      First += 2;\n      Node *Name = getDerived().parseName();\n      if (Name == nullptr)\n        return nullptr;\n      return make<SpecialName>(\"thread-local initialization routine for \", Name);\n    }\n    // T <call-offset> <base encoding>\n    default: {\n      ++First;\n      bool IsVirt = look() == 'v';\n      if (parseCallOffset())\n        return nullptr;\n      Node *BaseEncoding = getDerived().parseEncoding();\n      if (BaseEncoding == nullptr)\n        return nullptr;\n      if (IsVirt)\n        return make<SpecialName>(\"virtual thunk to \", BaseEncoding);\n      else\n        return make<SpecialName>(\"non-virtual thunk to \", BaseEncoding);\n    }\n    }\n  case 'G':\n    switch (look(1)) {\n    // GV <object name> # Guard variable for one-time initialization\n    case 'V': {\n      First += 2;\n      Node *Name = getDerived().parseName();\n      if (Name == nullptr)\n        return nullptr;\n      return make<SpecialName>(\"guard variable for \", Name);\n    }\n    // GR <object name> # reference temporary for object\n    // GR <object name> _             # First temporary\n    // GR <object name> <seq-id> _    # Subsequent temporaries\n    case 'R': {\n      First += 2;\n      Node *Name = getDerived().parseName();\n      if (Name == nullptr)\n        return nullptr;\n      size_t Count;\n      bool ParsedSeqId = !parseSeqId(&Count);\n      if (!consumeIf('_') && ParsedSeqId)\n        return nullptr;\n      return make<SpecialName>(\"reference temporary for \", Name);\n    }\n    // GI <module-name> v\n    case 'I': {\n      First += 2;\n      ModuleName *Module = nullptr;\n      if (getDerived().parseModuleNameOpt(Module))\n        return nullptr;\n      if (Module == nullptr)\n        return nullptr;\n      return make<SpecialName>(\"initializer for module \", Module);\n    }\n    }\n  }\n  return nullptr;\n}\n\n// <encoding> ::= <function name> <bare-function-type>\n//                    [`Q` <requires-clause expr>]\n//            ::= <data name>\n//            ::= <special-name>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseEncoding(bool ParseParams) {\n  // The template parameters of an encoding are unrelated to those of the\n  // enclosing context.\n  SaveTemplateParams SaveTemplateParamsScope(this);\n\n  if (look() == 'G' || look() == 'T')\n    return getDerived().parseSpecialName();\n\n  auto IsEndOfEncoding = [&] {\n    // The set of chars that can potentially follow an <encoding> (none of which\n    // can start a <type>). Enumerating these allows us to avoid speculative\n    // parsing.\n    return numLeft() == 0 || look() == 'E' || look() == '.' || look() == '_';\n  };\n\n  NameState NameInfo(this);\n  Node *Name = getDerived().parseName(&NameInfo);\n  if (Name == nullptr)\n    return nullptr;\n\n  if (resolveForwardTemplateRefs(NameInfo))\n    return nullptr;\n\n  if (IsEndOfEncoding())\n    return Name;\n\n  // ParseParams may be false at the top level only, when called from parse().\n  // For example in the mangled name _Z3fooILZ3BarEET_f, ParseParams may be\n  // false when demangling 3fooILZ3BarEET_f but is always true when demangling\n  // 3Bar.\n  if (!ParseParams) {\n    while (consume())\n      ;\n    return Name;\n  }\n\n  Node *Attrs = nullptr;\n  if (consumeIf(\"Ua9enable_ifI\")) {\n    size_t BeforeArgs = Names.size();\n    while (!consumeIf('E')) {\n      Node *Arg = getDerived().parseTemplateArg();\n      if (Arg == nullptr)\n        return nullptr;\n      Names.push_back(Arg);\n    }\n    Attrs = make<EnableIfAttr>(popTrailingNodeArray(BeforeArgs));\n    if (!Attrs)\n      return nullptr;\n  }\n\n  Node *ReturnType = nullptr;\n  if (!NameInfo.CtorDtorConversion && NameInfo.EndsWithTemplateArgs) {\n    ReturnType = getDerived().parseType();\n    if (ReturnType == nullptr)\n      return nullptr;\n  }\n\n  NodeArray Params;\n  if (!consumeIf('v')) {\n    size_t ParamsBegin = Names.size();\n    do {\n      Node *Ty = getDerived().parseType();\n      if (Ty == nullptr)\n        return nullptr;\n\n      const bool IsFirstParam = ParamsBegin == Names.size();\n      if (NameInfo.HasExplicitObjectParameter && IsFirstParam)\n        Ty = make<ExplicitObjectParameter>(Ty);\n\n      if (Ty == nullptr)\n        return nullptr;\n\n      Names.push_back(Ty);\n    } while (!IsEndOfEncoding() && look() != 'Q');\n    Params = popTrailingNodeArray(ParamsBegin);\n  }\n\n  Node *Requires = nullptr;\n  if (consumeIf('Q')) {\n    Requires = getDerived().parseConstraintExpr();\n    if (!Requires)\n      return nullptr;\n  }\n\n  return make<FunctionEncoding>(ReturnType, Name, Params, Attrs, Requires,\n                                NameInfo.CVQualifiers,\n                                NameInfo.ReferenceQualifier);\n}\n\ntemplate <class Float>\nstruct FloatData;\n\ntemplate <>\nstruct FloatData<float>\n{\n    static const size_t mangled_size = 8;\n    static const size_t max_demangled_size = 24;\n    static constexpr const char* spec = \"%af\";\n};\n\ntemplate <>\nstruct FloatData<double>\n{\n    static const size_t mangled_size = 16;\n    static const size_t max_demangled_size = 32;\n    static constexpr const char* spec = \"%a\";\n};\n\ntemplate <>\nstruct FloatData<long double>\n{\n#if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \\\n    defined(__wasm__) || defined(__riscv) || defined(__loongarch__) || \\\n    defined(__ve__)\n    static const size_t mangled_size = 32;\n#elif defined(__arm__) || defined(__mips__) || defined(__hexagon__)\n    static const size_t mangled_size = 16;\n#else\n    static const size_t mangled_size = 20;  // May need to be adjusted to 16 or 24 on other platforms\n#endif\n    // `-0x1.ffffffffffffffffffffffffffffp+16383` + 'L' + '\\0' == 42 bytes.\n    // 28 'f's * 4 bits == 112 bits, which is the number of mantissa bits.\n    // Negatives are one character longer than positives.\n    // `0x1.` and `p` are constant, and exponents `+16383` and `-16382` are the\n    // same length. 1 sign bit, 112 mantissa bits, and 15 exponent bits == 128.\n    static const size_t max_demangled_size = 42;\n    static constexpr const char *spec = \"%LaL\";\n};\n\ntemplate <typename Alloc, typename Derived>\ntemplate <class Float>\nNode *AbstractManglingParser<Alloc, Derived>::parseFloatingLiteral() {\n  const size_t N = FloatData<Float>::mangled_size;\n  if (numLeft() <= N)\n    return nullptr;\n  std::string_view Data(First, N);\n  for (char C : Data)\n    if (!(C >= '0' && C <= '9') && !(C >= 'a' && C <= 'f'))\n      return nullptr;\n  First += N;\n  if (!consumeIf('E'))\n    return nullptr;\n  return make<FloatLiteralImpl<Float>>(Data);\n}\n\n// <seq-id> ::= <0-9A-Z>+\ntemplate <typename Alloc, typename Derived>\nbool AbstractManglingParser<Alloc, Derived>::parseSeqId(size_t *Out) {\n  if (!(look() >= '0' && look() <= '9') &&\n      !(look() >= 'A' && look() <= 'Z'))\n    return true;\n\n  size_t Id = 0;\n  while (true) {\n    if (look() >= '0' && look() <= '9') {\n      Id *= 36;\n      Id += static_cast<size_t>(look() - '0');\n    } else if (look() >= 'A' && look() <= 'Z') {\n      Id *= 36;\n      Id += static_cast<size_t>(look() - 'A') + 10;\n    } else {\n      *Out = Id;\n      return false;\n    }\n    ++First;\n  }\n}\n\n// <substitution> ::= S <seq-id> _\n//                ::= S_\n// <substitution> ::= Sa # ::std::allocator\n// <substitution> ::= Sb # ::std::basic_string\n// <substitution> ::= Ss # ::std::basic_string < char,\n//                                               ::std::char_traits<char>,\n//                                               ::std::allocator<char> >\n// <substitution> ::= Si # ::std::basic_istream<char,  std::char_traits<char> >\n// <substitution> ::= So # ::std::basic_ostream<char,  std::char_traits<char> >\n// <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> >\n// The St case is handled specially in parseNestedName.\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseSubstitution() {\n  if (!consumeIf('S'))\n    return nullptr;\n\n  if (look() >= 'a' && look() <= 'z') {\n    SpecialSubKind Kind;\n    switch (look()) {\n    case 'a':\n      Kind = SpecialSubKind::allocator;\n      break;\n    case 'b':\n      Kind = SpecialSubKind::basic_string;\n      break;\n    case 'd':\n      Kind = SpecialSubKind::iostream;\n      break;\n    case 'i':\n      Kind = SpecialSubKind::istream;\n      break;\n    case 'o':\n      Kind = SpecialSubKind::ostream;\n      break;\n    case 's':\n      Kind = SpecialSubKind::string;\n      break;\n    default:\n      return nullptr;\n    }\n    ++First;\n    auto *SpecialSub = make<SpecialSubstitution>(Kind);\n    if (!SpecialSub)\n      return nullptr;\n\n    // Itanium C++ ABI 5.1.2: If a name that would use a built-in <substitution>\n    // has ABI tags, the tags are appended to the substitution; the result is a\n    // substitutable component.\n    Node *WithTags = getDerived().parseAbiTags(SpecialSub);\n    if (WithTags != SpecialSub) {\n      Subs.push_back(WithTags);\n      SpecialSub = WithTags;\n    }\n    return SpecialSub;\n  }\n\n  //                ::= S_\n  if (consumeIf('_')) {\n    if (Subs.empty())\n      return nullptr;\n    return Subs[0];\n  }\n\n  //                ::= S <seq-id> _\n  size_t Index = 0;\n  if (parseSeqId(&Index))\n    return nullptr;\n  ++Index;\n  if (!consumeIf('_') || Index >= Subs.size())\n    return nullptr;\n  return Subs[Index];\n}\n\n// <template-param> ::= T_    # first template parameter\n//                  ::= T <parameter-2 non-negative number> _\n//                  ::= TL <level-1> __\n//                  ::= TL <level-1> _ <parameter-2 non-negative number> _\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseTemplateParam() {\n  const char *Begin = First;\n  if (!consumeIf('T'))\n    return nullptr;\n\n  size_t Level = 0;\n  if (consumeIf('L')) {\n    if (parsePositiveInteger(&Level))\n      return nullptr;\n    ++Level;\n    if (!consumeIf('_'))\n      return nullptr;\n  }\n\n  size_t Index = 0;\n  if (!consumeIf('_')) {\n    if (parsePositiveInteger(&Index))\n      return nullptr;\n    ++Index;\n    if (!consumeIf('_'))\n      return nullptr;\n  }\n\n  // We don't track enclosing template parameter levels well enough to reliably\n  // substitute them all within a <constraint-expression>, so print the\n  // parameter numbering instead for now.\n  // TODO: Track all enclosing template parameters and substitute them here.\n  if (InConstraintExpr) {\n    return make<NameType>(std::string_view(Begin, First - 1 - Begin));\n  }\n\n  // If we're in a context where this <template-param> refers to a\n  // <template-arg> further ahead in the mangled name (currently just conversion\n  // operator types), then we should only look it up in the right context.\n  // This can only happen at the outermost level.\n  if (PermitForwardTemplateReferences && Level == 0) {\n    Node *ForwardRef = make<ForwardTemplateReference>(Index);\n    if (!ForwardRef)\n      return nullptr;\n    DEMANGLE_ASSERT(ForwardRef->getKind() == Node::KForwardTemplateReference,\n                    \"\");\n    ForwardTemplateRefs.push_back(\n        static_cast<ForwardTemplateReference *>(ForwardRef));\n    return ForwardRef;\n  }\n\n  if (Level >= TemplateParams.size() || !TemplateParams[Level] ||\n      Index >= TemplateParams[Level]->size()) {\n    // Itanium ABI 5.1.8: In a generic lambda, uses of auto in the parameter\n    // list are mangled as the corresponding artificial template type parameter.\n    if (ParsingLambdaParamsAtLevel == Level && Level <= TemplateParams.size()) {\n      // This will be popped by the ScopedTemplateParamList in\n      // parseUnnamedTypeName.\n      if (Level == TemplateParams.size())\n        TemplateParams.push_back(nullptr);\n      return make<NameType>(\"auto\");\n    }\n\n    return nullptr;\n  }\n\n  return (*TemplateParams[Level])[Index];\n}\n\n// <template-param-decl> ::= Ty                          # type parameter\n//                       ::= Tk <concept name> [<template-args>] # constrained type parameter\n//                       ::= Tn <type>                   # non-type parameter\n//                       ::= Tt <template-param-decl>* E # template parameter\n//                       ::= Tp <template-param-decl>    # parameter pack\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseTemplateParamDecl(\n    TemplateParamList *Params) {\n  auto InventTemplateParamName = [&](TemplateParamKind Kind) {\n    unsigned Index = NumSyntheticTemplateParameters[(int)Kind]++;\n    Node *N = make<SyntheticTemplateParamName>(Kind, Index);\n    if (N && Params)\n      Params->push_back(N);\n    return N;\n  };\n\n  if (consumeIf(\"Ty\")) {\n    Node *Name = InventTemplateParamName(TemplateParamKind::Type);\n    if (!Name)\n      return nullptr;\n    return make<TypeTemplateParamDecl>(Name);\n  }\n\n  if (consumeIf(\"Tk\")) {\n    Node *Constraint = getDerived().parseName();\n    if (!Constraint)\n      return nullptr;\n    Node *Name = InventTemplateParamName(TemplateParamKind::Type);\n    if (!Name)\n      return nullptr;\n    return make<ConstrainedTypeTemplateParamDecl>(Constraint, Name);\n  }\n\n  if (consumeIf(\"Tn\")) {\n    Node *Name = InventTemplateParamName(TemplateParamKind::NonType);\n    if (!Name)\n      return nullptr;\n    Node *Type = parseType();\n    if (!Type)\n      return nullptr;\n    return make<NonTypeTemplateParamDecl>(Name, Type);\n  }\n\n  if (consumeIf(\"Tt\")) {\n    Node *Name = InventTemplateParamName(TemplateParamKind::Template);\n    if (!Name)\n      return nullptr;\n    size_t ParamsBegin = Names.size();\n    ScopedTemplateParamList TemplateTemplateParamParams(this);\n    Node *Requires = nullptr;\n    while (!consumeIf('E')) {\n      Node *P = parseTemplateParamDecl(TemplateTemplateParamParams.params());\n      if (!P)\n        return nullptr;\n      Names.push_back(P);\n      if (consumeIf('Q')) {\n        Requires = getDerived().parseConstraintExpr();\n        if (Requires == nullptr || !consumeIf('E'))\n          return nullptr;\n        break;\n      }\n    }\n    NodeArray InnerParams = popTrailingNodeArray(ParamsBegin);\n    return make<TemplateTemplateParamDecl>(Name, InnerParams, Requires);\n  }\n\n  if (consumeIf(\"Tp\")) {\n    Node *P = parseTemplateParamDecl(Params);\n    if (!P)\n      return nullptr;\n    return make<TemplateParamPackDecl>(P);\n  }\n\n  return nullptr;\n}\n\n// <template-arg> ::= <type>                    # type or template\n//                ::= X <expression> E          # expression\n//                ::= <expr-primary>            # simple expressions\n//                ::= J <template-arg>* E       # argument pack\n//                ::= LZ <encoding> E           # extension\n//                ::= <template-param-decl> <template-arg>\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parseTemplateArg() {\n  switch (look()) {\n  case 'X': {\n    ++First;\n    Node *Arg = getDerived().parseExpr();\n    if (Arg == nullptr || !consumeIf('E'))\n      return nullptr;\n    return Arg;\n  }\n  case 'J': {\n    ++First;\n    size_t ArgsBegin = Names.size();\n    while (!consumeIf('E')) {\n      Node *Arg = getDerived().parseTemplateArg();\n      if (Arg == nullptr)\n        return nullptr;\n      Names.push_back(Arg);\n    }\n    NodeArray Args = popTrailingNodeArray(ArgsBegin);\n    return make<TemplateArgumentPack>(Args);\n  }\n  case 'L': {\n    //                ::= LZ <encoding> E           # extension\n    if (look(1) == 'Z') {\n      First += 2;\n      Node *Arg = getDerived().parseEncoding();\n      if (Arg == nullptr || !consumeIf('E'))\n        return nullptr;\n      return Arg;\n    }\n    //                ::= <expr-primary>            # simple expressions\n    return getDerived().parseExprPrimary();\n  }\n  case 'T': {\n    // Either <template-param> or a <template-param-decl> <template-arg>.\n    if (!getDerived().isTemplateParamDecl())\n      return getDerived().parseType();\n    Node *Param = getDerived().parseTemplateParamDecl(nullptr);\n    if (!Param)\n      return nullptr;\n    Node *Arg = getDerived().parseTemplateArg();\n    if (!Arg)\n      return nullptr;\n    return make<TemplateParamQualifiedArg>(Param, Arg);\n  }\n  default:\n    return getDerived().parseType();\n  }\n}\n\n// <template-args> ::= I <template-arg>* [Q <requires-clause expr>] E\n//     extension, the abi says <template-arg>+\ntemplate <typename Derived, typename Alloc>\nNode *\nAbstractManglingParser<Derived, Alloc>::parseTemplateArgs(bool TagTemplates) {\n  if (!consumeIf('I'))\n    return nullptr;\n\n  // <template-params> refer to the innermost <template-args>. Clear out any\n  // outer args that we may have inserted into TemplateParams.\n  if (TagTemplates) {\n    TemplateParams.clear();\n    TemplateParams.push_back(&OuterTemplateParams);\n    OuterTemplateParams.clear();\n  }\n\n  size_t ArgsBegin = Names.size();\n  Node *Requires = nullptr;\n  while (!consumeIf('E')) {\n    if (TagTemplates) {\n      Node *Arg = getDerived().parseTemplateArg();\n      if (Arg == nullptr)\n        return nullptr;\n      Names.push_back(Arg);\n      Node *TableEntry = Arg;\n      if (Arg->getKind() == Node::KTemplateParamQualifiedArg) {\n        TableEntry =\n            static_cast<TemplateParamQualifiedArg *>(TableEntry)->getArg();\n      }\n      if (Arg->getKind() == Node::KTemplateArgumentPack) {\n        TableEntry = make<ParameterPack>(\n            static_cast<TemplateArgumentPack*>(TableEntry)->getElements());\n        if (!TableEntry)\n          return nullptr;\n      }\n      OuterTemplateParams.push_back(TableEntry);\n    } else {\n      Node *Arg = getDerived().parseTemplateArg();\n      if (Arg == nullptr)\n        return nullptr;\n      Names.push_back(Arg);\n    }\n    if (consumeIf('Q')) {\n      Requires = getDerived().parseConstraintExpr();\n      if (!Requires || !consumeIf('E'))\n        return nullptr;\n      break;\n    }\n  }\n  return make<TemplateArgs>(popTrailingNodeArray(ArgsBegin), Requires);\n}\n\n// <mangled-name> ::= _Z <encoding>\n//                ::= <type>\n// extension      ::= ___Z <encoding> _block_invoke\n// extension      ::= ___Z <encoding> _block_invoke<decimal-digit>+\n// extension      ::= ___Z <encoding> _block_invoke_<decimal-digit>+\ntemplate <typename Derived, typename Alloc>\nNode *AbstractManglingParser<Derived, Alloc>::parse(bool ParseParams) {\n  if (consumeIf(\"_Z\") || consumeIf(\"__Z\")) {\n    Node *Encoding = getDerived().parseEncoding(ParseParams);\n    if (Encoding == nullptr)\n      return nullptr;\n    if (look() == '.') {\n      Encoding =\n          make<DotSuffix>(Encoding, std::string_view(First, Last - First));\n      First = Last;\n    }\n    if (numLeft() != 0)\n      return nullptr;\n    return Encoding;\n  }\n\n  if (consumeIf(\"___Z\") || consumeIf(\"____Z\")) {\n    Node *Encoding = getDerived().parseEncoding(ParseParams);\n    if (Encoding == nullptr || !consumeIf(\"_block_invoke\"))\n      return nullptr;\n    bool RequireNumber = consumeIf('_');\n    if (parseNumber().empty() && RequireNumber)\n      return nullptr;\n    if (look() == '.')\n      First = Last;\n    if (numLeft() != 0)\n      return nullptr;\n    return make<SpecialName>(\"invocation function for block in \", Encoding);\n  }\n\n  Node *Ty = getDerived().parseType();\n  if (numLeft() != 0)\n    return nullptr;\n  return Ty;\n}\n\ntemplate <typename Alloc>\nstruct ManglingParser : AbstractManglingParser<ManglingParser<Alloc>, Alloc> {\n  using AbstractManglingParser<ManglingParser<Alloc>,\n                               Alloc>::AbstractManglingParser;\n};\n\nDEMANGLE_NAMESPACE_END\n\n#ifdef _LIBCXXABI_COMPILER_CLANG\n#pragma clang diagnostic pop\n#endif\n\n#endif // DEMANGLE_ITANIUMDEMANGLE_H\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/include/llvm/Demangle/ItaniumNodes.def",
    "content": "//===--- ItaniumNodes.def ------------*- mode:c++;eval:(read-only-mode) -*-===//\n//       Do not edit! See README.txt.\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// Define the demangler's node names\n\n#ifndef NODE\n#error Define NODE to handle nodes\n#endif\n\nNODE(NodeArrayNode)\nNODE(DotSuffix)\nNODE(VendorExtQualType)\nNODE(QualType)\nNODE(ConversionOperatorType)\nNODE(PostfixQualifiedType)\nNODE(ElaboratedTypeSpefType)\nNODE(TransformedType)\nNODE(NameType)\nNODE(AbiTagAttr)\nNODE(EnableIfAttr)\nNODE(ObjCProtoName)\nNODE(PointerType)\nNODE(ReferenceType)\nNODE(PointerToMemberType)\nNODE(ArrayType)\nNODE(FunctionType)\nNODE(NoexceptSpec)\nNODE(DynamicExceptionSpec)\nNODE(FunctionEncoding)\nNODE(LiteralOperator)\nNODE(SpecialName)\nNODE(CtorVtableSpecialName)\nNODE(QualifiedName)\nNODE(NestedName)\nNODE(MemberLikeFriendName)\nNODE(LocalName)\nNODE(ModuleName)\nNODE(ModuleEntity)\nNODE(VectorType)\nNODE(PixelVectorType)\nNODE(BinaryFPType)\nNODE(BitIntType)\nNODE(SyntheticTemplateParamName)\nNODE(TemplateParamQualifiedArg)\nNODE(TypeTemplateParamDecl)\nNODE(ConstrainedTypeTemplateParamDecl)\nNODE(NonTypeTemplateParamDecl)\nNODE(TemplateTemplateParamDecl)\nNODE(TemplateParamPackDecl)\nNODE(ParameterPack)\nNODE(TemplateArgumentPack)\nNODE(ParameterPackExpansion)\nNODE(TemplateArgs)\nNODE(ForwardTemplateReference)\nNODE(NameWithTemplateArgs)\nNODE(GlobalQualifiedName)\nNODE(ExpandedSpecialSubstitution)\nNODE(SpecialSubstitution)\nNODE(CtorDtorName)\nNODE(DtorName)\nNODE(UnnamedTypeName)\nNODE(ClosureTypeName)\nNODE(StructuredBindingName)\nNODE(BinaryExpr)\nNODE(ArraySubscriptExpr)\nNODE(PostfixExpr)\nNODE(ConditionalExpr)\nNODE(MemberExpr)\nNODE(SubobjectExpr)\nNODE(EnclosingExpr)\nNODE(CastExpr)\nNODE(SizeofParamPackExpr)\nNODE(CallExpr)\nNODE(NewExpr)\nNODE(DeleteExpr)\nNODE(PrefixExpr)\nNODE(FunctionParam)\nNODE(ConversionExpr)\nNODE(PointerToMemberConversionExpr)\nNODE(InitListExpr)\nNODE(FoldExpr)\nNODE(ThrowExpr)\nNODE(BoolExpr)\nNODE(StringLiteral)\nNODE(LambdaExpr)\nNODE(EnumLiteral)\nNODE(IntegerLiteral)\nNODE(FloatLiteral)\nNODE(DoubleLiteral)\nNODE(LongDoubleLiteral)\nNODE(BracedExpr)\nNODE(BracedRangeExpr)\nNODE(RequiresExpr)\nNODE(ExprRequirement)\nNODE(TypeRequirement)\nNODE(NestedRequirement)\nNODE(ExplicitObjectParameter)\n\n#undef NODE\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/include/llvm/Demangle/MicrosoftDemangle.h",
    "content": "//===------------------------- MicrosoftDemangle.h --------------*- C++ -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef LLVM_DEMANGLE_MICROSOFTDEMANGLE_H\n#define LLVM_DEMANGLE_MICROSOFTDEMANGLE_H\n\n#include \"llvm/Demangle/Demangle.h\"\n#include \"llvm/Demangle/MicrosoftDemangleNodes.h\"\n\n#include <cassert>\n#include <string_view>\n#include <utility>\n\nnamespace llvm {\nnamespace ms_demangle {\n// This memory allocator is extremely fast, but it doesn't call dtors\n// for allocated objects. That means you can't use STL containers\n// (such as std::vector) with this allocator. But it pays off --\n// the demangler is 3x faster with this allocator compared to one with\n// STL containers.\nconstexpr size_t AllocUnit = 4096;\n\nclass ArenaAllocator {\n  struct AllocatorNode {\n    uint8_t *Buf = nullptr;\n    size_t Used = 0;\n    size_t Capacity = 0;\n    AllocatorNode *Next = nullptr;\n  };\n\n  void addNode(size_t Capacity) {\n    AllocatorNode *NewHead = new AllocatorNode;\n    NewHead->Buf = new uint8_t[Capacity];\n    NewHead->Next = Head;\n    NewHead->Capacity = Capacity;\n    Head = NewHead;\n    NewHead->Used = 0;\n  }\n\npublic:\n  ArenaAllocator() { addNode(AllocUnit); }\n\n  ~ArenaAllocator() {\n    while (Head) {\n      assert(Head->Buf);\n      delete[] Head->Buf;\n      AllocatorNode *Next = Head->Next;\n      delete Head;\n      Head = Next;\n    }\n  }\n\n  // Delete the copy constructor and the copy assignment operator.\n  ArenaAllocator(const ArenaAllocator &) = delete;\n  ArenaAllocator &operator=(const ArenaAllocator &) = delete;\n\n  char *allocUnalignedBuffer(size_t Size) {\n    assert(Head && Head->Buf);\n\n    uint8_t *P = Head->Buf + Head->Used;\n\n    Head->Used += Size;\n    if (Head->Used <= Head->Capacity)\n      return reinterpret_cast<char *>(P);\n\n    addNode(std::max(AllocUnit, Size));\n    Head->Used = Size;\n    return reinterpret_cast<char *>(Head->Buf);\n  }\n\n  template <typename T, typename... Args> T *allocArray(size_t Count) {\n    size_t Size = Count * sizeof(T);\n    assert(Head && Head->Buf);\n\n    size_t P = (size_t)Head->Buf + Head->Used;\n    uintptr_t AlignedP =\n        (((size_t)P + alignof(T) - 1) & ~(size_t)(alignof(T) - 1));\n    uint8_t *PP = (uint8_t *)AlignedP;\n    size_t Adjustment = AlignedP - P;\n\n    Head->Used += Size + Adjustment;\n    if (Head->Used <= Head->Capacity)\n      return new (PP) T[Count]();\n\n    addNode(std::max(AllocUnit, Size));\n    Head->Used = Size;\n    return new (Head->Buf) T[Count]();\n  }\n\n  template <typename T, typename... Args> T *alloc(Args &&... ConstructorArgs) {\n    constexpr size_t Size = sizeof(T);\n    assert(Head && Head->Buf);\n\n    size_t P = (size_t)Head->Buf + Head->Used;\n    uintptr_t AlignedP =\n        (((size_t)P + alignof(T) - 1) & ~(size_t)(alignof(T) - 1));\n    uint8_t *PP = (uint8_t *)AlignedP;\n    size_t Adjustment = AlignedP - P;\n\n    Head->Used += Size + Adjustment;\n    if (Head->Used <= Head->Capacity)\n      return new (PP) T(std::forward<Args>(ConstructorArgs)...);\n\n    static_assert(Size < AllocUnit);\n    addNode(AllocUnit);\n    Head->Used = Size;\n    return new (Head->Buf) T(std::forward<Args>(ConstructorArgs)...);\n  }\n\nprivate:\n  AllocatorNode *Head = nullptr;\n};\n\nstruct BackrefContext {\n  static constexpr size_t Max = 10;\n\n  TypeNode *FunctionParams[Max];\n  size_t FunctionParamCount = 0;\n\n  // The first 10 BackReferences in a mangled name can be back-referenced by\n  // special name @[0-9]. This is a storage for the first 10 BackReferences.\n  NamedIdentifierNode *Names[Max];\n  size_t NamesCount = 0;\n};\n\nenum class QualifierMangleMode { Drop, Mangle, Result };\n\nenum NameBackrefBehavior : uint8_t {\n  NBB_None = 0,          // don't save any names as backrefs.\n  NBB_Template = 1 << 0, // save template instanations.\n  NBB_Simple = 1 << 1,   // save simple names.\n};\n\nenum class FunctionIdentifierCodeGroup { Basic, Under, DoubleUnder };\n\n// Demangler class takes the main role in demangling symbols.\n// It has a set of functions to parse mangled symbols into Type instances.\n// It also has a set of functions to convert Type instances to strings.\nclass Demangler {\n  friend std::optional<size_t>\n  llvm::getArm64ECInsertionPointInMangledName(std::string_view MangledName);\n\npublic:\n  Demangler() = default;\n  virtual ~Demangler() = default;\n\n  // You are supposed to call parse() first and then check if error is true.  If\n  // it is false, call output() to write the formatted name to the given stream.\n  SymbolNode *parse(std::string_view &MangledName);\n\n  TagTypeNode *parseTagUniqueName(std::string_view &MangledName);\n\n  // True if an error occurred.\n  bool Error = false;\n\n  void dumpBackReferences();\n\nprivate:\n  SymbolNode *demangleEncodedSymbol(std::string_view &MangledName,\n                                    QualifiedNameNode *QN);\n  SymbolNode *demangleDeclarator(std::string_view &MangledName);\n  SymbolNode *demangleMD5Name(std::string_view &MangledName);\n  SymbolNode *demangleTypeinfoName(std::string_view &MangledName);\n\n  VariableSymbolNode *demangleVariableEncoding(std::string_view &MangledName,\n                                               StorageClass SC);\n  FunctionSymbolNode *demangleFunctionEncoding(std::string_view &MangledName);\n\n  Qualifiers demanglePointerExtQualifiers(std::string_view &MangledName);\n\n  // Parser functions. This is a recursive-descent parser.\n  TypeNode *demangleType(std::string_view &MangledName,\n                         QualifierMangleMode QMM);\n  PrimitiveTypeNode *demanglePrimitiveType(std::string_view &MangledName);\n  CustomTypeNode *demangleCustomType(std::string_view &MangledName);\n  TagTypeNode *demangleClassType(std::string_view &MangledName);\n  PointerTypeNode *demanglePointerType(std::string_view &MangledName);\n  PointerTypeNode *demangleMemberPointerType(std::string_view &MangledName);\n  FunctionSignatureNode *demangleFunctionType(std::string_view &MangledName,\n                                              bool HasThisQuals);\n\n  ArrayTypeNode *demangleArrayType(std::string_view &MangledName);\n\n  NodeArrayNode *demangleFunctionParameterList(std::string_view &MangledName,\n                                               bool &IsVariadic);\n  NodeArrayNode *demangleTemplateParameterList(std::string_view &MangledName);\n\n  std::pair<uint64_t, bool> demangleNumber(std::string_view &MangledName);\n  uint64_t demangleUnsigned(std::string_view &MangledName);\n  int64_t demangleSigned(std::string_view &MangledName);\n\n  void memorizeString(std::string_view s);\n  void memorizeIdentifier(IdentifierNode *Identifier);\n\n  /// Allocate a copy of \\p Borrowed into memory that we own.\n  std::string_view copyString(std::string_view Borrowed);\n\n  QualifiedNameNode *\n  demangleFullyQualifiedTypeName(std::string_view &MangledName);\n  QualifiedNameNode *\n  demangleFullyQualifiedSymbolName(std::string_view &MangledName);\n\n  IdentifierNode *demangleUnqualifiedTypeName(std::string_view &MangledName,\n                                              bool Memorize);\n  IdentifierNode *demangleUnqualifiedSymbolName(std::string_view &MangledName,\n                                                NameBackrefBehavior NBB);\n\n  QualifiedNameNode *demangleNameScopeChain(std::string_view &MangledName,\n                                            IdentifierNode *UnqualifiedName);\n  IdentifierNode *demangleNameScopePiece(std::string_view &MangledName);\n\n  NamedIdentifierNode *demangleBackRefName(std::string_view &MangledName);\n  IdentifierNode *\n  demangleTemplateInstantiationName(std::string_view &MangledName,\n                                    NameBackrefBehavior NBB);\n  IntrinsicFunctionKind\n  translateIntrinsicFunctionCode(char CH, FunctionIdentifierCodeGroup Group);\n  IdentifierNode *demangleFunctionIdentifierCode(std::string_view &MangledName);\n  IdentifierNode *\n  demangleFunctionIdentifierCode(std::string_view &MangledName,\n                                 FunctionIdentifierCodeGroup Group);\n  StructorIdentifierNode *\n  demangleStructorIdentifier(std::string_view &MangledName, bool IsDestructor);\n  ConversionOperatorIdentifierNode *\n  demangleConversionOperatorIdentifier(std::string_view &MangledName);\n  LiteralOperatorIdentifierNode *\n  demangleLiteralOperatorIdentifier(std::string_view &MangledName);\n\n  SymbolNode *demangleSpecialIntrinsic(std::string_view &MangledName);\n  SpecialTableSymbolNode *\n  demangleSpecialTableSymbolNode(std::string_view &MangledName,\n                                 SpecialIntrinsicKind SIK);\n  LocalStaticGuardVariableNode *\n  demangleLocalStaticGuard(std::string_view &MangledName, bool IsThread);\n  VariableSymbolNode *demangleUntypedVariable(ArenaAllocator &Arena,\n                                              std::string_view &MangledName,\n                                              std::string_view VariableName);\n  VariableSymbolNode *\n  demangleRttiBaseClassDescriptorNode(ArenaAllocator &Arena,\n                                      std::string_view &MangledName);\n  FunctionSymbolNode *demangleInitFiniStub(std::string_view &MangledName,\n                                           bool IsDestructor);\n\n  NamedIdentifierNode *demangleSimpleName(std::string_view &MangledName,\n                                          bool Memorize);\n  NamedIdentifierNode *\n  demangleAnonymousNamespaceName(std::string_view &MangledName);\n  NamedIdentifierNode *\n  demangleLocallyScopedNamePiece(std::string_view &MangledName);\n  EncodedStringLiteralNode *\n  demangleStringLiteral(std::string_view &MangledName);\n  FunctionSymbolNode *demangleVcallThunkNode(std::string_view &MangledName);\n\n  std::string_view demangleSimpleString(std::string_view &MangledName,\n                                        bool Memorize);\n\n  FuncClass demangleFunctionClass(std::string_view &MangledName);\n  CallingConv demangleCallingConvention(std::string_view &MangledName);\n  StorageClass demangleVariableStorageClass(std::string_view &MangledName);\n  bool demangleThrowSpecification(std::string_view &MangledName);\n  wchar_t demangleWcharLiteral(std::string_view &MangledName);\n  uint8_t demangleCharLiteral(std::string_view &MangledName);\n\n  std::pair<Qualifiers, bool> demangleQualifiers(std::string_view &MangledName);\n\n  // Memory allocator.\n  ArenaAllocator Arena;\n\n  // A single type uses one global back-ref table for all function params.\n  // This means back-refs can even go \"into\" other types.  Examples:\n  //\n  //  // Second int* is a back-ref to first.\n  //  void foo(int *, int*);\n  //\n  //  // Second int* is not a back-ref to first (first is not a function param).\n  //  int* foo(int*);\n  //\n  //  // Second int* is a back-ref to first (ALL function types share the same\n  //  // back-ref map.\n  //  using F = void(*)(int*);\n  //  F G(int *);\n  BackrefContext Backrefs;\n};\n\n} // namespace ms_demangle\n} // namespace llvm\n\n#endif // LLVM_DEMANGLE_MICROSOFTDEMANGLE_H\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/include/llvm/Demangle/MicrosoftDemangleNodes.h",
    "content": "//===- MicrosoftDemangleNodes.h ---------------------------------*- C++ -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file defines the AST nodes used in the MSVC demangler.\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef LLVM_DEMANGLE_MICROSOFTDEMANGLENODES_H\n#define LLVM_DEMANGLE_MICROSOFTDEMANGLENODES_H\n\n#include <array>\n#include <cstdint>\n#include <string>\n#include <string_view>\n\nnamespace llvm {\nnamespace itanium_demangle {\nclass OutputBuffer;\n}\n}\n\nusing llvm::itanium_demangle::OutputBuffer;\n\nnamespace llvm {\nnamespace ms_demangle {\n\n// Storage classes\nenum Qualifiers : uint8_t {\n  Q_None = 0,\n  Q_Const = 1 << 0,\n  Q_Volatile = 1 << 1,\n  Q_Far = 1 << 2,\n  Q_Huge = 1 << 3,\n  Q_Unaligned = 1 << 4,\n  Q_Restrict = 1 << 5,\n  Q_Pointer64 = 1 << 6\n};\n\nenum class StorageClass : uint8_t {\n  None,\n  PrivateStatic,\n  ProtectedStatic,\n  PublicStatic,\n  Global,\n  FunctionLocalStatic,\n};\n\nenum class PointerAffinity { None, Pointer, Reference, RValueReference };\nenum class FunctionRefQualifier { None, Reference, RValueReference };\n\n// Calling conventions\nenum class CallingConv : uint8_t {\n  None,\n  Cdecl,\n  Pascal,\n  Thiscall,\n  Stdcall,\n  Fastcall,\n  Clrcall,\n  Eabi,\n  Vectorcall,\n  Regcall,\n  Swift,      // Clang-only\n  SwiftAsync, // Clang-only\n};\n\nenum class ReferenceKind : uint8_t { None, LValueRef, RValueRef };\n\nenum OutputFlags {\n  OF_Default = 0,\n  OF_NoCallingConvention = 1,\n  OF_NoTagSpecifier = 2,\n  OF_NoAccessSpecifier = 4,\n  OF_NoMemberType = 8,\n  OF_NoReturnType = 16,\n  OF_NoVariableType = 32,\n};\n\n// Types\nenum class PrimitiveKind {\n  Void,\n  Bool,\n  Char,\n  Schar,\n  Uchar,\n  Char8,\n  Char16,\n  Char32,\n  Short,\n  Ushort,\n  Int,\n  Uint,\n  Long,\n  Ulong,\n  Int64,\n  Uint64,\n  Wchar,\n  Float,\n  Double,\n  Ldouble,\n  Nullptr,\n};\n\nenum class CharKind {\n  Char,\n  Char16,\n  Char32,\n  Wchar,\n};\n\nenum class IntrinsicFunctionKind : uint8_t {\n  None,\n  New,                        // ?2 # operator new\n  Delete,                     // ?3 # operator delete\n  Assign,                     // ?4 # operator=\n  RightShift,                 // ?5 # operator>>\n  LeftShift,                  // ?6 # operator<<\n  LogicalNot,                 // ?7 # operator!\n  Equals,                     // ?8 # operator==\n  NotEquals,                  // ?9 # operator!=\n  ArraySubscript,             // ?A # operator[]\n  Pointer,                    // ?C # operator->\n  Dereference,                // ?D # operator*\n  Increment,                  // ?E # operator++\n  Decrement,                  // ?F # operator--\n  Minus,                      // ?G # operator-\n  Plus,                       // ?H # operator+\n  BitwiseAnd,                 // ?I # operator&\n  MemberPointer,              // ?J # operator->*\n  Divide,                     // ?K # operator/\n  Modulus,                    // ?L # operator%\n  LessThan,                   // ?M operator<\n  LessThanEqual,              // ?N operator<=\n  GreaterThan,                // ?O operator>\n  GreaterThanEqual,           // ?P operator>=\n  Comma,                      // ?Q operator,\n  Parens,                     // ?R operator()\n  BitwiseNot,                 // ?S operator~\n  BitwiseXor,                 // ?T operator^\n  BitwiseOr,                  // ?U operator|\n  LogicalAnd,                 // ?V operator&&\n  LogicalOr,                  // ?W operator||\n  TimesEqual,                 // ?X operator*=\n  PlusEqual,                  // ?Y operator+=\n  MinusEqual,                 // ?Z operator-=\n  DivEqual,                   // ?_0 operator/=\n  ModEqual,                   // ?_1 operator%=\n  RshEqual,                   // ?_2 operator>>=\n  LshEqual,                   // ?_3 operator<<=\n  BitwiseAndEqual,            // ?_4 operator&=\n  BitwiseOrEqual,             // ?_5 operator|=\n  BitwiseXorEqual,            // ?_6 operator^=\n  VbaseDtor,                  // ?_D # vbase destructor\n  VecDelDtor,                 // ?_E # vector deleting destructor\n  DefaultCtorClosure,         // ?_F # default constructor closure\n  ScalarDelDtor,              // ?_G # scalar deleting destructor\n  VecCtorIter,                // ?_H # vector constructor iterator\n  VecDtorIter,                // ?_I # vector destructor iterator\n  VecVbaseCtorIter,           // ?_J # vector vbase constructor iterator\n  VdispMap,                   // ?_K # virtual displacement map\n  EHVecCtorIter,              // ?_L # eh vector constructor iterator\n  EHVecDtorIter,              // ?_M # eh vector destructor iterator\n  EHVecVbaseCtorIter,         // ?_N # eh vector vbase constructor iterator\n  CopyCtorClosure,            // ?_O # copy constructor closure\n  LocalVftableCtorClosure,    // ?_T # local vftable constructor closure\n  ArrayNew,                   // ?_U operator new[]\n  ArrayDelete,                // ?_V operator delete[]\n  ManVectorCtorIter,          // ?__A managed vector ctor iterator\n  ManVectorDtorIter,          // ?__B managed vector dtor iterator\n  EHVectorCopyCtorIter,       // ?__C EH vector copy ctor iterator\n  EHVectorVbaseCopyCtorIter,  // ?__D EH vector vbase copy ctor iterator\n  VectorCopyCtorIter,         // ?__G vector copy constructor iterator\n  VectorVbaseCopyCtorIter,    // ?__H vector vbase copy constructor iterator\n  ManVectorVbaseCopyCtorIter, // ?__I managed vector vbase copy constructor\n  CoAwait,                    // ?__L operator co_await\n  Spaceship,                  // ?__M operator<=>\n  MaxIntrinsic\n};\n\nenum class SpecialIntrinsicKind {\n  None,\n  Vftable,\n  Vbtable,\n  Typeof,\n  VcallThunk,\n  LocalStaticGuard,\n  StringLiteralSymbol,\n  UdtReturning,\n  Unknown,\n  DynamicInitializer,\n  DynamicAtexitDestructor,\n  RttiTypeDescriptor,\n  RttiBaseClassDescriptor,\n  RttiBaseClassArray,\n  RttiClassHierarchyDescriptor,\n  RttiCompleteObjLocator,\n  LocalVftable,\n  LocalStaticThreadGuard,\n};\n\n// Function classes\nenum FuncClass : uint16_t {\n  FC_None = 0,\n  FC_Public = 1 << 0,\n  FC_Protected = 1 << 1,\n  FC_Private = 1 << 2,\n  FC_Global = 1 << 3,\n  FC_Static = 1 << 4,\n  FC_Virtual = 1 << 5,\n  FC_Far = 1 << 6,\n  FC_ExternC = 1 << 7,\n  FC_NoParameterList = 1 << 8,\n  FC_VirtualThisAdjust = 1 << 9,\n  FC_VirtualThisAdjustEx = 1 << 10,\n  FC_StaticThisAdjust = 1 << 11,\n};\n\nenum class TagKind { Class, Struct, Union, Enum };\n\nenum class NodeKind {\n  Unknown,\n  Md5Symbol,\n  PrimitiveType,\n  FunctionSignature,\n  Identifier,\n  NamedIdentifier,\n  VcallThunkIdentifier,\n  LocalStaticGuardIdentifier,\n  IntrinsicFunctionIdentifier,\n  ConversionOperatorIdentifier,\n  DynamicStructorIdentifier,\n  StructorIdentifier,\n  LiteralOperatorIdentifier,\n  ThunkSignature,\n  PointerType,\n  TagType,\n  ArrayType,\n  Custom,\n  IntrinsicType,\n  NodeArray,\n  QualifiedName,\n  TemplateParameterReference,\n  EncodedStringLiteral,\n  IntegerLiteral,\n  RttiBaseClassDescriptor,\n  LocalStaticGuardVariable,\n  FunctionSymbol,\n  VariableSymbol,\n  SpecialTableSymbol\n};\n\nstruct Node {\n  explicit Node(NodeKind K) : Kind(K) {}\n  virtual ~Node() = default;\n\n  NodeKind kind() const { return Kind; }\n\n  virtual void output(OutputBuffer &OB, OutputFlags Flags) const = 0;\n\n  std::string toString(OutputFlags Flags = OF_Default) const;\n\nprivate:\n  NodeKind Kind;\n};\n\nstruct TypeNode;\nstruct PrimitiveTypeNode;\nstruct FunctionSignatureNode;\nstruct IdentifierNode;\nstruct NamedIdentifierNode;\nstruct VcallThunkIdentifierNode;\nstruct IntrinsicFunctionIdentifierNode;\nstruct LiteralOperatorIdentifierNode;\nstruct ConversionOperatorIdentifierNode;\nstruct StructorIdentifierNode;\nstruct ThunkSignatureNode;\nstruct PointerTypeNode;\nstruct ArrayTypeNode;\nstruct TagTypeNode;\nstruct NodeArrayNode;\nstruct QualifiedNameNode;\nstruct TemplateParameterReferenceNode;\nstruct EncodedStringLiteralNode;\nstruct IntegerLiteralNode;\nstruct RttiBaseClassDescriptorNode;\nstruct LocalStaticGuardVariableNode;\nstruct SymbolNode;\nstruct FunctionSymbolNode;\nstruct VariableSymbolNode;\nstruct SpecialTableSymbolNode;\n\nstruct TypeNode : public Node {\n  explicit TypeNode(NodeKind K) : Node(K) {}\n\n  virtual void outputPre(OutputBuffer &OB, OutputFlags Flags) const = 0;\n  virtual void outputPost(OutputBuffer &OB, OutputFlags Flags) const = 0;\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override {\n    outputPre(OB, Flags);\n    outputPost(OB, Flags);\n  }\n\n  Qualifiers Quals = Q_None;\n};\n\nstruct PrimitiveTypeNode : public TypeNode {\n  explicit PrimitiveTypeNode(PrimitiveKind K)\n      : TypeNode(NodeKind::PrimitiveType), PrimKind(K) {}\n\n  void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;\n  void outputPost(OutputBuffer &OB, OutputFlags Flags) const override {}\n\n  PrimitiveKind PrimKind;\n};\n\nstruct FunctionSignatureNode : public TypeNode {\n  explicit FunctionSignatureNode(NodeKind K) : TypeNode(K) {}\n  FunctionSignatureNode() : TypeNode(NodeKind::FunctionSignature) {}\n\n  void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;\n  void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  // Valid if this FunctionTypeNode is the Pointee of a PointerType or\n  // MemberPointerType.\n  PointerAffinity Affinity = PointerAffinity::None;\n\n  // The function's calling convention.\n  CallingConv CallConvention = CallingConv::None;\n\n  // Function flags (gloabl, public, etc)\n  FuncClass FunctionClass = FC_Global;\n\n  FunctionRefQualifier RefQualifier = FunctionRefQualifier::None;\n\n  // The return type of the function.\n  TypeNode *ReturnType = nullptr;\n\n  // True if this is a C-style ... varargs function.\n  bool IsVariadic = false;\n\n  // Function parameters\n  NodeArrayNode *Params = nullptr;\n\n  // True if the function type is noexcept.\n  bool IsNoexcept = false;\n};\n\nstruct IdentifierNode : public Node {\n  explicit IdentifierNode(NodeKind K) : Node(K) {}\n\n  NodeArrayNode *TemplateParams = nullptr;\n\nprotected:\n  void outputTemplateParameters(OutputBuffer &OB, OutputFlags Flags) const;\n};\n\nstruct VcallThunkIdentifierNode : public IdentifierNode {\n  VcallThunkIdentifierNode() : IdentifierNode(NodeKind::VcallThunkIdentifier) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  uint64_t OffsetInVTable = 0;\n};\n\nstruct DynamicStructorIdentifierNode : public IdentifierNode {\n  DynamicStructorIdentifierNode()\n      : IdentifierNode(NodeKind::DynamicStructorIdentifier) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  VariableSymbolNode *Variable = nullptr;\n  QualifiedNameNode *Name = nullptr;\n  bool IsDestructor = false;\n};\n\nstruct NamedIdentifierNode : public IdentifierNode {\n  NamedIdentifierNode() : IdentifierNode(NodeKind::NamedIdentifier) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  std::string_view Name;\n};\n\nstruct IntrinsicFunctionIdentifierNode : public IdentifierNode {\n  explicit IntrinsicFunctionIdentifierNode(IntrinsicFunctionKind Operator)\n      : IdentifierNode(NodeKind::IntrinsicFunctionIdentifier),\n        Operator(Operator) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  IntrinsicFunctionKind Operator;\n};\n\nstruct LiteralOperatorIdentifierNode : public IdentifierNode {\n  LiteralOperatorIdentifierNode()\n      : IdentifierNode(NodeKind::LiteralOperatorIdentifier) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  std::string_view Name;\n};\n\nstruct LocalStaticGuardIdentifierNode : public IdentifierNode {\n  LocalStaticGuardIdentifierNode()\n      : IdentifierNode(NodeKind::LocalStaticGuardIdentifier) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  bool IsThread = false;\n  uint32_t ScopeIndex = 0;\n};\n\nstruct ConversionOperatorIdentifierNode : public IdentifierNode {\n  ConversionOperatorIdentifierNode()\n      : IdentifierNode(NodeKind::ConversionOperatorIdentifier) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  // The type that this operator converts too.\n  TypeNode *TargetType = nullptr;\n};\n\nstruct StructorIdentifierNode : public IdentifierNode {\n  StructorIdentifierNode() : IdentifierNode(NodeKind::StructorIdentifier) {}\n  explicit StructorIdentifierNode(bool IsDestructor)\n      : IdentifierNode(NodeKind::StructorIdentifier),\n        IsDestructor(IsDestructor) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  // The name of the class that this is a structor of.\n  IdentifierNode *Class = nullptr;\n  bool IsDestructor = false;\n};\n\nstruct ThunkSignatureNode : public FunctionSignatureNode {\n  ThunkSignatureNode() : FunctionSignatureNode(NodeKind::ThunkSignature) {}\n\n  void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;\n  void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  struct ThisAdjustor {\n    uint32_t StaticOffset = 0;\n    int32_t VBPtrOffset = 0;\n    int32_t VBOffsetOffset = 0;\n    int32_t VtordispOffset = 0;\n  };\n\n  ThisAdjustor ThisAdjust;\n};\n\nstruct PointerTypeNode : public TypeNode {\n  PointerTypeNode() : TypeNode(NodeKind::PointerType) {}\n  void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;\n  void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  // Is this a pointer, reference, or rvalue-reference?\n  PointerAffinity Affinity = PointerAffinity::None;\n\n  // If this is a member pointer, this is the class that the member is in.\n  QualifiedNameNode *ClassParent = nullptr;\n\n  // Represents a type X in \"a pointer to X\", \"a reference to X\", or\n  // \"rvalue-reference to X\"\n  TypeNode *Pointee = nullptr;\n};\n\nstruct TagTypeNode : public TypeNode {\n  explicit TagTypeNode(TagKind Tag) : TypeNode(NodeKind::TagType), Tag(Tag) {}\n\n  void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;\n  void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  QualifiedNameNode *QualifiedName = nullptr;\n  TagKind Tag;\n};\n\nstruct ArrayTypeNode : public TypeNode {\n  ArrayTypeNode() : TypeNode(NodeKind::ArrayType) {}\n\n  void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;\n  void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  void outputDimensionsImpl(OutputBuffer &OB, OutputFlags Flags) const;\n  void outputOneDimension(OutputBuffer &OB, OutputFlags Flags, Node *N) const;\n\n  // A list of array dimensions.  e.g. [3,4,5] in `int Foo[3][4][5]`\n  NodeArrayNode *Dimensions = nullptr;\n\n  // The type of array element.\n  TypeNode *ElementType = nullptr;\n};\n\nstruct IntrinsicNode : public TypeNode {\n  IntrinsicNode() : TypeNode(NodeKind::IntrinsicType) {}\n  void output(OutputBuffer &OB, OutputFlags Flags) const override {}\n};\n\nstruct CustomTypeNode : public TypeNode {\n  CustomTypeNode() : TypeNode(NodeKind::Custom) {}\n\n  void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;\n  void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  IdentifierNode *Identifier = nullptr;\n};\n\nstruct NodeArrayNode : public Node {\n  NodeArrayNode() : Node(NodeKind::NodeArray) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  void output(OutputBuffer &OB, OutputFlags Flags,\n              std::string_view Separator) const;\n\n  Node **Nodes = nullptr;\n  size_t Count = 0;\n};\n\nstruct QualifiedNameNode : public Node {\n  QualifiedNameNode() : Node(NodeKind::QualifiedName) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  NodeArrayNode *Components = nullptr;\n\n  IdentifierNode *getUnqualifiedIdentifier() {\n    Node *LastComponent = Components->Nodes[Components->Count - 1];\n    return static_cast<IdentifierNode *>(LastComponent);\n  }\n};\n\nstruct TemplateParameterReferenceNode : public Node {\n  TemplateParameterReferenceNode()\n      : Node(NodeKind::TemplateParameterReference) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  SymbolNode *Symbol = nullptr;\n\n  int ThunkOffsetCount = 0;\n  std::array<int64_t, 3> ThunkOffsets;\n  PointerAffinity Affinity = PointerAffinity::None;\n  bool IsMemberPointer = false;\n};\n\nstruct IntegerLiteralNode : public Node {\n  IntegerLiteralNode() : Node(NodeKind::IntegerLiteral) {}\n  IntegerLiteralNode(uint64_t Value, bool IsNegative)\n      : Node(NodeKind::IntegerLiteral), Value(Value), IsNegative(IsNegative) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  uint64_t Value = 0;\n  bool IsNegative = false;\n};\n\nstruct RttiBaseClassDescriptorNode : public IdentifierNode {\n  RttiBaseClassDescriptorNode()\n      : IdentifierNode(NodeKind::RttiBaseClassDescriptor) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  uint32_t NVOffset = 0;\n  int32_t VBPtrOffset = 0;\n  uint32_t VBTableOffset = 0;\n  uint32_t Flags = 0;\n};\n\nstruct SymbolNode : public Node {\n  explicit SymbolNode(NodeKind K) : Node(K) {}\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n  QualifiedNameNode *Name = nullptr;\n};\n\nstruct SpecialTableSymbolNode : public SymbolNode {\n  explicit SpecialTableSymbolNode()\n      : SymbolNode(NodeKind::SpecialTableSymbol) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n  QualifiedNameNode *TargetName = nullptr;\n  Qualifiers Quals = Qualifiers::Q_None;\n};\n\nstruct LocalStaticGuardVariableNode : public SymbolNode {\n  LocalStaticGuardVariableNode()\n      : SymbolNode(NodeKind::LocalStaticGuardVariable) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  bool IsVisible = false;\n};\n\nstruct EncodedStringLiteralNode : public SymbolNode {\n  EncodedStringLiteralNode() : SymbolNode(NodeKind::EncodedStringLiteral) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  std::string_view DecodedString;\n  bool IsTruncated = false;\n  CharKind Char = CharKind::Char;\n};\n\nstruct VariableSymbolNode : public SymbolNode {\n  VariableSymbolNode() : SymbolNode(NodeKind::VariableSymbol) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  StorageClass SC = StorageClass::None;\n  TypeNode *Type = nullptr;\n};\n\nstruct FunctionSymbolNode : public SymbolNode {\n  FunctionSymbolNode() : SymbolNode(NodeKind::FunctionSymbol) {}\n\n  void output(OutputBuffer &OB, OutputFlags Flags) const override;\n\n  FunctionSignatureNode *Signature = nullptr;\n};\n\n} // namespace ms_demangle\n} // namespace llvm\n\n#endif\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/include/llvm/Demangle/README.txt",
    "content": "Itanium Name Demangler Library\n==============================\n\nIntroduction\n------------\n\nThis directory contains the generic itanium name demangler\nlibrary. The main purpose of the library is to demangle C++ symbols,\ni.e. convert the string \"_Z1fv\" into \"f()\". You can also use the CRTP\nbase ManglingParser to perform some simple analysis on the mangled\nname, or (in LLVM) use the opaque ItaniumPartialDemangler to query the\ndemangled AST.\n\nWhy are there multiple copies of the this library in the source tree?\n---------------------------------------------------------------------\n\nThe canonical sources are in libcxxabi/src/demangle and some of the\nfiles are copied to llvm/include/llvm/Demangle.  The simple reason for\nthis comes from before the monorepo, and both [sub]projects need to\ndemangle symbols, but neither can depend on each other.\n\n* libcxxabi needs the demangler to implement __cxa_demangle, which is\n  part of the itanium ABI spec.\n\n* LLVM needs a copy for a bunch of places, and cannot rely on the\n  system's __cxa_demangle because it a) might not be available (i.e.,\n  on Windows), and b) may not be up-to-date on the latest language\n  features.\n\nThe copy of the demangler in LLVM has some extra stuff that aren't\nneeded in libcxxabi (ie, the MSVC demangler, ItaniumPartialDemangler),\nwhich depend on the shared generic components. Despite these\ndifferences, we want to keep the \"core\" generic demangling library\nidentical between both copies to simplify development and testing.\n\nIf you're working on the generic library, then do the work first in\nlibcxxabi, then run libcxxabi/src/demangle/cp-to-llvm.sh. This\nscript takes as an optional argument the path to llvm, and copies the\nchanges you made to libcxxabi over.  Note that this script just\nblindly overwrites all changes to the generic library in llvm, so be\ncareful.\n\nBecause the core demangler needs to work in libcxxabi, everything\nneeds to be declared in an anonymous namespace (see\nDEMANGLE_NAMESPACE_BEGIN), and you can't introduce any code that\ndepends on the libcxx dylib.\n\nFIXME: Now that LLVM is a monorepo, it should be possible to\nde-duplicate this code, and have both LLVM and libcxxabi depend on a\nshared demangler library.\n\nTesting\n-------\n\nThe tests are split up between libcxxabi/test/{unit,}test_demangle.cpp, and\nllvm/unittest/Demangle. The llvm directory should only get tests for stuff not\nincluded in the core library. In the future though, we should probably move all\nthe tests to LLVM.\n\nIt is also a really good idea to run libFuzzer after non-trivial changes, see\nlibcxxabi/fuzz/cxa_demangle_fuzzer.cpp and https://llvm.org/docs/LibFuzzer.html.\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/include/llvm/Demangle/StringViewExtras.h",
    "content": "//===--- StringViewExtras.h ----------*- mode:c++;eval:(read-only-mode) -*-===//\n//       Do not edit! See README.txt.\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// There are two copies of this file in the source tree.  The one under\n// libcxxabi is the original and the one under llvm is the copy.  Use\n// cp-to-llvm.sh to update the copy.  See README.txt for more details.\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef DEMANGLE_STRINGVIEW_H\n#define DEMANGLE_STRINGVIEW_H\n\n#include \"DemangleConfig.h\"\n\n#include <string_view>\n\nDEMANGLE_NAMESPACE_BEGIN\n\ninline bool starts_with(std::string_view self, char C) noexcept {\n  return !self.empty() && *self.begin() == C;\n}\n\ninline bool starts_with(std::string_view haystack,\n                        std::string_view needle) noexcept {\n  if (needle.size() > haystack.size())\n    return false;\n  haystack.remove_suffix(haystack.size() - needle.size());\n  return haystack == needle;\n}\n\nDEMANGLE_NAMESPACE_END\n\n#endif\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/include/llvm/Demangle/Utility.h",
    "content": "//===--- Utility.h -------------------*- mode:c++;eval:(read-only-mode) -*-===//\n//       Do not edit! See README.txt.\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// Provide some utility classes for use in the demangler.\n// There are two copies of this file in the source tree.  The one in libcxxabi\n// is the original and the one in llvm is the copy.  Use cp-to-llvm.sh to update\n// the copy.  See README.txt for more details.\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef DEMANGLE_UTILITY_H\n#define DEMANGLE_UTILITY_H\n\n#include \"DemangleConfig.h\"\n\n#include <array>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <limits>\n#include <string_view>\n\nDEMANGLE_NAMESPACE_BEGIN\n\n// Stream that AST nodes write their string representation into after the AST\n// has been parsed.\nclass OutputBuffer {\n  char *Buffer = nullptr;\n  size_t CurrentPosition = 0;\n  size_t BufferCapacity = 0;\n\n  // Ensure there are at least N more positions in the buffer.\n  void grow(size_t N) {\n    size_t Need = N + CurrentPosition;\n    if (Need > BufferCapacity) {\n      // Reduce the number of reallocations, with a bit of hysteresis. The\n      // number here is chosen so the first allocation will more-than-likely not\n      // allocate more than 1K.\n      Need += 1024 - 32;\n      BufferCapacity *= 2;\n      if (BufferCapacity < Need)\n        BufferCapacity = Need;\n      Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity));\n      if (Buffer == nullptr)\n        std::abort();\n    }\n  }\n\n  OutputBuffer &writeUnsigned(uint64_t N, bool isNeg = false) {\n    std::array<char, 21> Temp;\n    char *TempPtr = Temp.data() + Temp.size();\n\n    // Output at least one character.\n    do {\n      *--TempPtr = char('0' + N % 10);\n      N /= 10;\n    } while (N);\n\n    // Add negative sign.\n    if (isNeg)\n      *--TempPtr = '-';\n\n    return operator+=(\n        std::string_view(TempPtr, Temp.data() + Temp.size() - TempPtr));\n  }\n\npublic:\n  OutputBuffer(char *StartBuf, size_t Size)\n      : Buffer(StartBuf), BufferCapacity(Size) {}\n  OutputBuffer(char *StartBuf, size_t *SizePtr)\n      : OutputBuffer(StartBuf, StartBuf ? *SizePtr : 0) {}\n  OutputBuffer() = default;\n  // Non-copyable\n  OutputBuffer(const OutputBuffer &) = delete;\n  OutputBuffer &operator=(const OutputBuffer &) = delete;\n\n  operator std::string_view() const {\n    return std::string_view(Buffer, CurrentPosition);\n  }\n\n  /// If a ParameterPackExpansion (or similar type) is encountered, the offset\n  /// into the pack that we're currently printing.\n  unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();\n  unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();\n\n  /// When zero, we're printing template args and '>' needs to be parenthesized.\n  /// Use a counter so we can simply increment inside parentheses.\n  unsigned GtIsGt = 1;\n\n  bool isGtInsideTemplateArgs() const { return GtIsGt == 0; }\n\n  void printOpen(char Open = '(') {\n    GtIsGt++;\n    *this += Open;\n  }\n  void printClose(char Close = ')') {\n    GtIsGt--;\n    *this += Close;\n  }\n\n  OutputBuffer &operator+=(std::string_view R) {\n    if (size_t Size = R.size()) {\n      grow(Size);\n      std::memcpy(Buffer + CurrentPosition, &*R.begin(), Size);\n      CurrentPosition += Size;\n    }\n    return *this;\n  }\n\n  OutputBuffer &operator+=(char C) {\n    grow(1);\n    Buffer[CurrentPosition++] = C;\n    return *this;\n  }\n\n  OutputBuffer &prepend(std::string_view R) {\n    size_t Size = R.size();\n\n    grow(Size);\n    std::memmove(Buffer + Size, Buffer, CurrentPosition);\n    std::memcpy(Buffer, &*R.begin(), Size);\n    CurrentPosition += Size;\n\n    return *this;\n  }\n\n  OutputBuffer &operator<<(std::string_view R) { return (*this += R); }\n\n  OutputBuffer &operator<<(char C) { return (*this += C); }\n\n  OutputBuffer &operator<<(long long N) {\n    return writeUnsigned(static_cast<unsigned long long>(std::abs(N)), N < 0);\n  }\n\n  OutputBuffer &operator<<(unsigned long long N) {\n    return writeUnsigned(N, false);\n  }\n\n  OutputBuffer &operator<<(long N) {\n    return this->operator<<(static_cast<long long>(N));\n  }\n\n  OutputBuffer &operator<<(unsigned long N) {\n    return this->operator<<(static_cast<unsigned long long>(N));\n  }\n\n  OutputBuffer &operator<<(int N) {\n    return this->operator<<(static_cast<long long>(N));\n  }\n\n  OutputBuffer &operator<<(unsigned int N) {\n    return this->operator<<(static_cast<unsigned long long>(N));\n  }\n\n  void insert(size_t Pos, const char *S, size_t N) {\n    DEMANGLE_ASSERT(Pos <= CurrentPosition, \"\");\n    if (N == 0)\n      return;\n    grow(N);\n    std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos);\n    std::memcpy(Buffer + Pos, S, N);\n    CurrentPosition += N;\n  }\n\n  size_t getCurrentPosition() const { return CurrentPosition; }\n  void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; }\n\n  char back() const {\n    DEMANGLE_ASSERT(CurrentPosition, \"\");\n    return Buffer[CurrentPosition - 1];\n  }\n\n  bool empty() const { return CurrentPosition == 0; }\n\n  char *getBuffer() { return Buffer; }\n  char *getBufferEnd() { return Buffer + CurrentPosition - 1; }\n  size_t getBufferCapacity() const { return BufferCapacity; }\n};\n\ntemplate <class T> class ScopedOverride {\n  T &Loc;\n  T Original;\n\npublic:\n  ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {}\n\n  ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) {\n    Loc_ = std::move(NewVal);\n  }\n  ~ScopedOverride() { Loc = std::move(Original); }\n\n  ScopedOverride(const ScopedOverride &) = delete;\n  ScopedOverride &operator=(const ScopedOverride &) = delete;\n};\n\nDEMANGLE_NAMESPACE_END\n\n#endif\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/source/DLangDemangle.cpp",
    "content": "//===--- DLangDemangle.cpp ------------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n///\n/// \\file\n/// This file defines a demangler for the D programming language as specified\n/// in the ABI specification, available at:\n/// https://dlang.org/spec/abi.html#name_mangling\n///\n//===----------------------------------------------------------------------===//\n\n#include \"llvm/Demangle/Demangle.h\"\n#include \"llvm/Demangle/StringViewExtras.h\"\n#include \"llvm/Demangle/Utility.h\"\n\n#include <cctype>\n#include <cstring>\n#include <limits>\n#include <string_view>\n\nusing namespace llvm;\nusing llvm::itanium_demangle::OutputBuffer;\nusing llvm::itanium_demangle::starts_with;\n\nnamespace {\n\n/// Demangle information structure.\nstruct Demangler {\n  /// Initialize the information structure we use to pass around information.\n  ///\n  /// \\param Mangled String to demangle.\n  Demangler(std::string_view Mangled);\n\n  /// Extract and demangle the mangled symbol and append it to the output\n  /// string.\n  ///\n  /// \\param Demangled Output buffer to write the demangled name.\n  ///\n  /// \\return The remaining string on success or nullptr on failure.\n  ///\n  /// \\see https://dlang.org/spec/abi.html#name_mangling .\n  /// \\see https://dlang.org/spec/abi.html#MangledName .\n  const char *parseMangle(OutputBuffer *Demangled);\n\nprivate:\n  /// Extract and demangle a given mangled symbol and append it to the output\n  /// string.\n  ///\n  /// \\param Demangled output buffer to write the demangled name.\n  /// \\param Mangled mangled symbol to be demangled.\n  ///\n  /// \\see https://dlang.org/spec/abi.html#name_mangling .\n  /// \\see https://dlang.org/spec/abi.html#MangledName .\n  void parseMangle(OutputBuffer *Demangled, std::string_view &Mangled);\n\n  /// Extract the number from a given string.\n  ///\n  /// \\param Mangled string to extract the number.\n  /// \\param Ret assigned result value.\n  ///\n  /// \\note Ret larger than UINT_MAX is considered a failure.\n  ///\n  /// \\see https://dlang.org/spec/abi.html#Number .\n  void decodeNumber(std::string_view &Mangled, unsigned long &Ret);\n\n  /// Extract the back reference position from a given string.\n  ///\n  /// \\param Mangled string to extract the back reference position.\n  /// \\param Ret assigned result value.\n  ///\n  /// \\return true on success, false on error.\n  ///\n  /// \\note Ret is always >= 0 on success, and unspecified on failure\n  ///\n  /// \\see https://dlang.org/spec/abi.html#back_ref .\n  /// \\see https://dlang.org/spec/abi.html#NumberBackRef .\n  bool decodeBackrefPos(std::string_view &Mangled, long &Ret);\n\n  /// Extract the symbol pointed by the back reference form a given string.\n  ///\n  /// \\param Mangled string to extract the back reference position.\n  /// \\param Ret assigned result value.\n  ///\n  /// \\return true on success, false on error.\n  ///\n  /// \\see https://dlang.org/spec/abi.html#back_ref .\n  bool decodeBackref(std::string_view &Mangled, std::string_view &Ret);\n\n  /// Extract and demangle backreferenced symbol from a given mangled symbol\n  /// and append it to the output string.\n  ///\n  /// \\param Demangled output buffer to write the demangled name.\n  /// \\param Mangled mangled symbol to be demangled.\n  ///\n  /// \\see https://dlang.org/spec/abi.html#back_ref .\n  /// \\see https://dlang.org/spec/abi.html#IdentifierBackRef .\n  void parseSymbolBackref(OutputBuffer *Demangled, std::string_view &Mangled);\n\n  /// Extract and demangle backreferenced type from a given mangled symbol\n  /// and append it to the output string.\n  ///\n  /// \\param Mangled mangled symbol to be demangled.\n  ///\n  /// \\see https://dlang.org/spec/abi.html#back_ref .\n  /// \\see https://dlang.org/spec/abi.html#TypeBackRef .\n  void parseTypeBackref(std::string_view &Mangled);\n\n  /// Check whether it is the beginning of a symbol name.\n  ///\n  /// \\param Mangled string to extract the symbol name.\n  ///\n  /// \\return true on success, false otherwise.\n  ///\n  /// \\see https://dlang.org/spec/abi.html#SymbolName .\n  bool isSymbolName(std::string_view Mangled);\n\n  /// Extract and demangle an identifier from a given mangled symbol append it\n  /// to the output string.\n  ///\n  /// \\param Demangled Output buffer to write the demangled name.\n  /// \\param Mangled Mangled symbol to be demangled.\n  ///\n  /// \\see https://dlang.org/spec/abi.html#SymbolName .\n  void parseIdentifier(OutputBuffer *Demangled, std::string_view &Mangled);\n\n  /// Extract and demangle the plain identifier from a given mangled symbol and\n  /// prepend/append it to the output string, with a special treatment for some\n  /// magic compiler generated symbols.\n  ///\n  /// \\param Demangled Output buffer to write the demangled name.\n  /// \\param Mangled Mangled symbol to be demangled.\n  /// \\param Len Length of the mangled symbol name.\n  ///\n  /// \\see https://dlang.org/spec/abi.html#LName .\n  void parseLName(OutputBuffer *Demangled, std::string_view &Mangled,\n                  unsigned long Len);\n\n  /// Extract and demangle the qualified symbol from a given mangled symbol\n  /// append it to the output string.\n  ///\n  /// \\param Demangled Output buffer to write the demangled name.\n  /// \\param Mangled Mangled symbol to be demangled.\n  ///\n  /// \\see https://dlang.org/spec/abi.html#QualifiedName .\n  void parseQualified(OutputBuffer *Demangled, std::string_view &Mangled);\n\n  /// Extract and demangle a type from a given mangled symbol append it to\n  /// the output string.\n  ///\n  /// \\param Mangled mangled symbol to be demangled.\n  ///\n  /// \\return true on success, false on error.\n  ///\n  /// \\see https://dlang.org/spec/abi.html#Type .\n  bool parseType(std::string_view &Mangled);\n\n  /// An immutable view of the string we are demangling.\n  const std::string_view Str;\n  /// The index of the last back reference.\n  int LastBackref;\n};\n\n} // namespace\n\nvoid Demangler::decodeNumber(std::string_view &Mangled, unsigned long &Ret) {\n  // Clear Mangled if trying to extract something that isn't a digit.\n  if (Mangled.empty()) {\n    Mangled = {};\n    return;\n  }\n\n  if (!std::isdigit(Mangled.front())) {\n    Mangled = {};\n    return;\n  }\n\n  unsigned long Val = 0;\n\n  do {\n    unsigned long Digit = Mangled[0] - '0';\n\n    // Check for overflow.\n    if (Val > (std::numeric_limits<unsigned int>::max() - Digit) / 10) {\n      Mangled = {};\n      return;\n    }\n\n    Val = Val * 10 + Digit;\n    Mangled.remove_prefix(1);\n  } while (!Mangled.empty() && std::isdigit(Mangled.front()));\n\n  if (Mangled.empty()) {\n    Mangled = {};\n    return;\n  }\n\n  Ret = Val;\n}\n\nbool Demangler::decodeBackrefPos(std::string_view &Mangled, long &Ret) {\n  // Return nullptr if trying to extract something that isn't a digit\n  if (Mangled.empty()) {\n    Mangled = {};\n    return false;\n  }\n  // Any identifier or non-basic type that has been emitted to the mangled\n  // symbol before will not be emitted again, but is referenced by a special\n  // sequence encoding the relative position of the original occurrence in the\n  // mangled symbol name.\n  // Numbers in back references are encoded with base 26 by upper case letters\n  // A-Z for higher digits but lower case letters a-z for the last digit.\n  //    NumberBackRef:\n  //        [a-z]\n  //        [A-Z] NumberBackRef\n  //        ^\n  unsigned long Val = 0;\n\n  while (!Mangled.empty() && std::isalpha(Mangled.front())) {\n    // Check for overflow\n    if (Val > (std::numeric_limits<unsigned long>::max() - 25) / 26)\n      break;\n\n    Val *= 26;\n\n    if (Mangled[0] >= 'a' && Mangled[0] <= 'z') {\n      Val += Mangled[0] - 'a';\n      if ((long)Val <= 0)\n        break;\n      Ret = Val;\n      Mangled.remove_prefix(1);\n      return true;\n    }\n\n    Val += Mangled[0] - 'A';\n    Mangled.remove_prefix(1);\n  }\n\n  Mangled = {};\n  return false;\n}\n\nbool Demangler::decodeBackref(std::string_view &Mangled,\n                              std::string_view &Ret) {\n  assert(!Mangled.empty() && Mangled.front() == 'Q' &&\n         \"Invalid back reference!\");\n  Ret = {};\n\n  // Position of 'Q'\n  const char *Qpos = Mangled.data();\n  long RefPos;\n  Mangled.remove_prefix(1);\n\n  if (!decodeBackrefPos(Mangled, RefPos)) {\n    Mangled = {};\n    return false;\n  }\n\n  if (RefPos > Qpos - Str.data()) {\n    Mangled = {};\n    return false;\n  }\n\n  // Set the position of the back reference.\n  Ret = Qpos - RefPos;\n\n  return true;\n}\n\nvoid Demangler::parseSymbolBackref(OutputBuffer *Demangled,\n                                   std::string_view &Mangled) {\n  // An identifier back reference always points to a digit 0 to 9.\n  //    IdentifierBackRef:\n  //        Q NumberBackRef\n  //        ^\n  unsigned long Len;\n\n  // Get position of the back reference\n  std::string_view Backref;\n  if (!decodeBackref(Mangled, Backref)) {\n    Mangled = {};\n    return;\n  }\n\n  // Must point to a simple identifier\n  decodeNumber(Backref, Len);\n  if (Backref.empty() || Backref.length() < Len) {\n    Mangled = {};\n    return;\n  }\n\n  parseLName(Demangled, Backref, Len);\n  if (Backref.empty())\n    Mangled = {};\n}\n\nvoid Demangler::parseTypeBackref(std::string_view &Mangled) {\n  // A type back reference always points to a letter.\n  //    TypeBackRef:\n  //        Q NumberBackRef\n  //        ^\n\n  // If we appear to be moving backwards through the mangle string, then\n  // bail as this may be a recursive back reference.\n  if (Mangled.data() - Str.data() >= LastBackref) {\n    Mangled = {};\n    return;\n  }\n\n  int SaveRefPos = LastBackref;\n  LastBackref = Mangled.data() - Str.data();\n\n  // Get position of the back reference.\n  std::string_view Backref;\n  if (!decodeBackref(Mangled, Backref)) {\n    Mangled = {};\n    return;\n  }\n\n  // Can't decode back reference.\n  if (Backref.empty()) {\n    Mangled = {};\n    return;\n  }\n\n  // TODO: Add support for function type back references.\n  if (!parseType(Backref))\n    Mangled = {};\n\n  LastBackref = SaveRefPos;\n\n  if (Backref.empty())\n    Mangled = {};\n}\n\nbool Demangler::isSymbolName(std::string_view Mangled) {\n  long Ret;\n  const char *Qref = Mangled.data();\n\n  if (std::isdigit(Mangled.front()))\n    return true;\n\n  // TODO: Handle template instances.\n\n  if (Mangled.front() != 'Q')\n    return false;\n\n  Mangled.remove_prefix(1);\n  bool Valid = decodeBackrefPos(Mangled, Ret);\n  if (!Valid || Ret > Qref - Str.data())\n    return false;\n\n  return std::isdigit(Qref[-Ret]);\n}\n\nvoid Demangler::parseMangle(OutputBuffer *Demangled,\n                            std::string_view &Mangled) {\n  // A D mangled symbol is comprised of both scope and type information.\n  //    MangleName:\n  //        _D QualifiedName Type\n  //        _D QualifiedName Z\n  //        ^\n  // The caller should have guaranteed that the start pointer is at the\n  // above location.\n  // Note that type is never a function type, but only the return type of\n  // a function or the type of a variable.\n  Mangled.remove_prefix(2);\n\n  parseQualified(Demangled, Mangled);\n\n  if (Mangled.empty()) {\n    Mangled = {};\n    return;\n  }\n\n  // Artificial symbols end with 'Z' and have no type.\n  if (Mangled.front() == 'Z') {\n    Mangled.remove_prefix(1);\n  } else if (!parseType(Mangled))\n    Mangled = {};\n}\n\nvoid Demangler::parseQualified(OutputBuffer *Demangled,\n                               std::string_view &Mangled) {\n  // Qualified names are identifiers separated by their encoded length.\n  // Nested functions also encode their argument types without specifying\n  // what they return.\n  //    QualifiedName:\n  //        SymbolFunctionName\n  //        SymbolFunctionName QualifiedName\n  //        ^\n  //    SymbolFunctionName:\n  //        SymbolName\n  //        SymbolName TypeFunctionNoReturn\n  //        SymbolName M TypeFunctionNoReturn\n  //        SymbolName M TypeModifiers TypeFunctionNoReturn\n  // The start pointer should be at the above location.\n\n  // Whether it has more than one symbol\n  size_t NotFirst = false;\n  do {\n    // Skip over anonymous symbols.\n    if (!Mangled.empty() && Mangled.front() == '0') {\n      do\n        Mangled.remove_prefix(1);\n      while (!Mangled.empty() && Mangled.front() == '0');\n\n      continue;\n    }\n\n    if (NotFirst)\n      *Demangled << '.';\n    NotFirst = true;\n\n    parseIdentifier(Demangled, Mangled);\n  } while (!Mangled.empty() && isSymbolName(Mangled));\n}\n\nvoid Demangler::parseIdentifier(OutputBuffer *Demangled,\n                                std::string_view &Mangled) {\n  if (Mangled.empty()) {\n    Mangled = {};\n    return;\n  }\n\n  if (Mangled.front() == 'Q')\n    return parseSymbolBackref(Demangled, Mangled);\n\n  // TODO: Parse lengthless template instances.\n\n  unsigned long Len;\n  decodeNumber(Mangled, Len);\n\n  if (Mangled.empty()) {\n    Mangled = {};\n    return;\n  }\n  if (!Len || Mangled.length() < Len) {\n    Mangled = {};\n    return;\n  }\n\n  // TODO: Parse template instances with a length prefix.\n\n  // There can be multiple different declarations in the same function that\n  // have the same mangled name.  To make the mangled names unique, a fake\n  // parent in the form `__Sddd' is added to the symbol.\n  if (Len >= 4 && starts_with(Mangled, \"__S\")) {\n    const size_t SuffixLen = Mangled.length() - Len;\n    std::string_view P = Mangled.substr(3);\n    while (P.length() > SuffixLen && std::isdigit(P.front()))\n      P.remove_prefix(1);\n    if (P.length() == SuffixLen) {\n      // Skip over the fake parent.\n      Mangled.remove_prefix(Len);\n      return parseIdentifier(Demangled, Mangled);\n    }\n\n    // Else demangle it as a plain identifier.\n  }\n\n  parseLName(Demangled, Mangled, Len);\n}\n\nbool Demangler::parseType(std::string_view &Mangled) {\n  if (Mangled.empty()) {\n    Mangled = {};\n    return false;\n  }\n\n  switch (Mangled.front()) {\n  // TODO: Parse type qualifiers.\n  // TODO: Parse function types.\n  // TODO: Parse compound types.\n  // TODO: Parse delegate types.\n  // TODO: Parse tuple types.\n\n  // Basic types.\n  case 'i':\n    Mangled.remove_prefix(1);\n    // TODO: Add type name dumping\n    return true;\n\n    // TODO: Add support for the rest of the basic types.\n\n  // Back referenced type.\n  case 'Q': {\n    parseTypeBackref(Mangled);\n    return true;\n  }\n\n  default: // unhandled.\n    Mangled = {};\n    return false;\n  }\n}\n\nvoid Demangler::parseLName(OutputBuffer *Demangled, std::string_view &Mangled,\n                           unsigned long Len) {\n  switch (Len) {\n  case 6:\n    if (starts_with(Mangled, \"__initZ\")) {\n      // The static initializer for a given symbol.\n      Demangled->prepend(\"initializer for \");\n      Demangled->setCurrentPosition(Demangled->getCurrentPosition() - 1);\n      Mangled.remove_prefix(Len);\n      return;\n    }\n    if (starts_with(Mangled, \"__vtblZ\")) {\n      // The vtable symbol for a given class.\n      Demangled->prepend(\"vtable for \");\n      Demangled->setCurrentPosition(Demangled->getCurrentPosition() - 1);\n      Mangled.remove_prefix(Len);\n      return;\n    }\n    break;\n\n  case 7:\n    if (starts_with(Mangled, \"__ClassZ\")) {\n      // The classinfo symbol for a given class.\n      Demangled->prepend(\"ClassInfo for \");\n      Demangled->setCurrentPosition(Demangled->getCurrentPosition() - 1);\n      Mangled.remove_prefix(Len);\n      return;\n    }\n    break;\n\n  case 11:\n    if (starts_with(Mangled, \"__InterfaceZ\")) {\n      // The interface symbol for a given class.\n      Demangled->prepend(\"Interface for \");\n      Demangled->setCurrentPosition(Demangled->getCurrentPosition() - 1);\n      Mangled.remove_prefix(Len);\n      return;\n    }\n    break;\n\n  case 12:\n    if (starts_with(Mangled, \"__ModuleInfoZ\")) {\n      // The ModuleInfo symbol for a given module.\n      Demangled->prepend(\"ModuleInfo for \");\n      Demangled->setCurrentPosition(Demangled->getCurrentPosition() - 1);\n      Mangled.remove_prefix(Len);\n      return;\n    }\n    break;\n  }\n\n  *Demangled << Mangled.substr(0, Len);\n  Mangled.remove_prefix(Len);\n}\n\nDemangler::Demangler(std::string_view Mangled)\n    : Str(Mangled), LastBackref(Mangled.length()) {}\n\nconst char *Demangler::parseMangle(OutputBuffer *Demangled) {\n  std::string_view M(this->Str);\n  parseMangle(Demangled, M);\n  return M.data();\n}\n\nchar *llvm::dlangDemangle(std::string_view MangledName) {\n  if (MangledName.empty() || !starts_with(MangledName, \"_D\"))\n    return nullptr;\n\n  OutputBuffer Demangled;\n  if (MangledName == \"_Dmain\") {\n    Demangled << \"D main\";\n  } else {\n\n    Demangler D(MangledName);\n    const char *M = D.parseMangle(&Demangled);\n\n    // Check that the entire symbol was successfully demangled.\n    if (M == nullptr || *M != '\\0') {\n      std::free(Demangled.getBuffer());\n      return nullptr;\n    }\n  }\n\n  // OutputBuffer's internal buffer is not null terminated and therefore we need\n  // to add it to comply with C null terminated strings.\n  if (Demangled.getCurrentPosition() > 0) {\n    Demangled << '\\0';\n    Demangled.setCurrentPosition(Demangled.getCurrentPosition() - 1);\n    return Demangled.getBuffer();\n  }\n\n  std::free(Demangled.getBuffer());\n  return nullptr;\n}\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/source/Demangle.cpp",
    "content": "//===-- Demangle.cpp - Common demangling functions ------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n///\n/// \\file This file contains definitions of common demangling functions.\n///\n//===----------------------------------------------------------------------===//\n\n#include \"llvm/Demangle/Demangle.h\"\n#include \"llvm/Demangle/StringViewExtras.h\"\n#include <cstdlib>\n#include <string_view>\n\nusing llvm::itanium_demangle::starts_with;\n\nstd::string llvm::demangle(std::string_view MangledName) {\n  std::string Result;\n\n  if (nonMicrosoftDemangle(MangledName, Result))\n    return Result;\n\n  if (starts_with(MangledName, '_') &&\n      nonMicrosoftDemangle(MangledName.substr(1), Result,\n                           /*CanHaveLeadingDot=*/false))\n    return Result;\n\n  if (char *Demangled = microsoftDemangle(MangledName, nullptr, nullptr)) {\n    Result = Demangled;\n    std::free(Demangled);\n  } else {\n    Result = MangledName;\n  }\n  return Result;\n}\n\nstatic bool isItaniumEncoding(std::string_view S) {\n  // Itanium encoding requires 1 or 3 leading underscores, followed by 'Z'.\n  return starts_with(S, \"_Z\") || starts_with(S, \"___Z\");\n}\n\nstatic bool isRustEncoding(std::string_view S) { return starts_with(S, \"_R\"); }\n\nstatic bool isDLangEncoding(std::string_view S) { return starts_with(S, \"_D\"); }\n\nbool llvm::nonMicrosoftDemangle(std::string_view MangledName,\n                                std::string &Result, bool CanHaveLeadingDot,\n                                bool ParseParams) {\n  char *Demangled = nullptr;\n\n  // Do not consider the dot prefix as part of the demangled symbol name.\n  if (CanHaveLeadingDot && MangledName.size() > 0 && MangledName[0] == '.') {\n    MangledName.remove_prefix(1);\n    Result = \".\";\n  }\n\n  if (isItaniumEncoding(MangledName))\n    Demangled = itaniumDemangle(MangledName, ParseParams);\n  else if (isRustEncoding(MangledName))\n    Demangled = rustDemangle(MangledName);\n  else if (isDLangEncoding(MangledName))\n    Demangled = dlangDemangle(MangledName);\n\n  if (!Demangled)\n    return false;\n\n  Result += Demangled;\n  std::free(Demangled);\n  return true;\n}\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/source/ItaniumDemangle.cpp",
    "content": "//===------------------------- ItaniumDemangle.cpp ------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n// FIXME: (possibly) incomplete list of features that clang mangles that this\n// file does not yet support:\n//   - C++ modules TS\n\n#include \"llvm/Demangle/Demangle.h\"\n#include \"llvm/Demangle/ItaniumDemangle.h\"\n\n#include <cassert>\n#include <cctype>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <exception>\n#include <functional>\n#include <utility>\n\nusing namespace llvm;\nusing namespace llvm::itanium_demangle;\n\nconstexpr const char *itanium_demangle::FloatData<float>::spec;\nconstexpr const char *itanium_demangle::FloatData<double>::spec;\nconstexpr const char *itanium_demangle::FloatData<long double>::spec;\n\n// <discriminator> := _ <non-negative number>      # when number < 10\n//                 := __ <non-negative number> _   # when number >= 10\n//  extension      := decimal-digit+               # at the end of string\nconst char *itanium_demangle::parse_discriminator(const char *first,\n                                                  const char *last) {\n  // parse but ignore discriminator\n  if (first != last) {\n    if (*first == '_') {\n      const char *t1 = first + 1;\n      if (t1 != last) {\n        if (std::isdigit(*t1))\n          first = t1 + 1;\n        else if (*t1 == '_') {\n          for (++t1; t1 != last && std::isdigit(*t1); ++t1)\n            ;\n          if (t1 != last && *t1 == '_')\n            first = t1 + 1;\n        }\n      }\n    } else if (std::isdigit(*first)) {\n      const char *t1 = first + 1;\n      for (; t1 != last && std::isdigit(*t1); ++t1)\n        ;\n      if (t1 == last)\n        first = last;\n    }\n  }\n  return first;\n}\n\n#ifndef NDEBUG\nnamespace {\nstruct DumpVisitor {\n  unsigned Depth = 0;\n  bool PendingNewline = false;\n\n  template<typename NodeT> static constexpr bool wantsNewline(const NodeT *) {\n    return true;\n  }\n  static bool wantsNewline(NodeArray A) { return !A.empty(); }\n  static constexpr bool wantsNewline(...) { return false; }\n\n  template<typename ...Ts> static bool anyWantNewline(Ts ...Vs) {\n    for (bool B : {wantsNewline(Vs)...})\n      if (B)\n        return true;\n    return false;\n  }\n\n  void printStr(const char *S) { fprintf(stderr, \"%s\", S); }\n  void print(std::string_view SV) {\n    fprintf(stderr, \"\\\"%.*s\\\"\", (int)SV.size(), SV.data());\n  }\n  void print(const Node *N) {\n    if (N)\n      N->visit(std::ref(*this));\n    else\n      printStr(\"<null>\");\n  }\n  void print(NodeArray A) {\n    ++Depth;\n    printStr(\"{\");\n    bool First = true;\n    for (const Node *N : A) {\n      if (First)\n        print(N);\n      else\n        printWithComma(N);\n      First = false;\n    }\n    printStr(\"}\");\n    --Depth;\n  }\n\n  // Overload used when T is exactly 'bool', not merely convertible to 'bool'.\n  void print(bool B) { printStr(B ? \"true\" : \"false\"); }\n\n  template <class T> std::enable_if_t<std::is_unsigned<T>::value> print(T N) {\n    fprintf(stderr, \"%llu\", (unsigned long long)N);\n  }\n\n  template <class T> std::enable_if_t<std::is_signed<T>::value> print(T N) {\n    fprintf(stderr, \"%lld\", (long long)N);\n  }\n\n  void print(ReferenceKind RK) {\n    switch (RK) {\n    case ReferenceKind::LValue:\n      return printStr(\"ReferenceKind::LValue\");\n    case ReferenceKind::RValue:\n      return printStr(\"ReferenceKind::RValue\");\n    }\n  }\n  void print(FunctionRefQual RQ) {\n    switch (RQ) {\n    case FunctionRefQual::FrefQualNone:\n      return printStr(\"FunctionRefQual::FrefQualNone\");\n    case FunctionRefQual::FrefQualLValue:\n      return printStr(\"FunctionRefQual::FrefQualLValue\");\n    case FunctionRefQual::FrefQualRValue:\n      return printStr(\"FunctionRefQual::FrefQualRValue\");\n    }\n  }\n  void print(Qualifiers Qs) {\n    if (!Qs) return printStr(\"QualNone\");\n    struct QualName { Qualifiers Q; const char *Name; } Names[] = {\n      {QualConst, \"QualConst\"},\n      {QualVolatile, \"QualVolatile\"},\n      {QualRestrict, \"QualRestrict\"},\n    };\n    for (QualName Name : Names) {\n      if (Qs & Name.Q) {\n        printStr(Name.Name);\n        Qs = Qualifiers(Qs & ~Name.Q);\n        if (Qs) printStr(\" | \");\n      }\n    }\n  }\n  void print(SpecialSubKind SSK) {\n    switch (SSK) {\n    case SpecialSubKind::allocator:\n      return printStr(\"SpecialSubKind::allocator\");\n    case SpecialSubKind::basic_string:\n      return printStr(\"SpecialSubKind::basic_string\");\n    case SpecialSubKind::string:\n      return printStr(\"SpecialSubKind::string\");\n    case SpecialSubKind::istream:\n      return printStr(\"SpecialSubKind::istream\");\n    case SpecialSubKind::ostream:\n      return printStr(\"SpecialSubKind::ostream\");\n    case SpecialSubKind::iostream:\n      return printStr(\"SpecialSubKind::iostream\");\n    }\n  }\n  void print(TemplateParamKind TPK) {\n    switch (TPK) {\n    case TemplateParamKind::Type:\n      return printStr(\"TemplateParamKind::Type\");\n    case TemplateParamKind::NonType:\n      return printStr(\"TemplateParamKind::NonType\");\n    case TemplateParamKind::Template:\n      return printStr(\"TemplateParamKind::Template\");\n    }\n  }\n  void print(Node::Prec P) {\n    switch (P) {\n    case Node::Prec::Primary:\n      return printStr(\"Node::Prec::Primary\");\n    case Node::Prec::Postfix:\n      return printStr(\"Node::Prec::Postfix\");\n    case Node::Prec::Unary:\n      return printStr(\"Node::Prec::Unary\");\n    case Node::Prec::Cast:\n      return printStr(\"Node::Prec::Cast\");\n    case Node::Prec::PtrMem:\n      return printStr(\"Node::Prec::PtrMem\");\n    case Node::Prec::Multiplicative:\n      return printStr(\"Node::Prec::Multiplicative\");\n    case Node::Prec::Additive:\n      return printStr(\"Node::Prec::Additive\");\n    case Node::Prec::Shift:\n      return printStr(\"Node::Prec::Shift\");\n    case Node::Prec::Spaceship:\n      return printStr(\"Node::Prec::Spaceship\");\n    case Node::Prec::Relational:\n      return printStr(\"Node::Prec::Relational\");\n    case Node::Prec::Equality:\n      return printStr(\"Node::Prec::Equality\");\n    case Node::Prec::And:\n      return printStr(\"Node::Prec::And\");\n    case Node::Prec::Xor:\n      return printStr(\"Node::Prec::Xor\");\n    case Node::Prec::Ior:\n      return printStr(\"Node::Prec::Ior\");\n    case Node::Prec::AndIf:\n      return printStr(\"Node::Prec::AndIf\");\n    case Node::Prec::OrIf:\n      return printStr(\"Node::Prec::OrIf\");\n    case Node::Prec::Conditional:\n      return printStr(\"Node::Prec::Conditional\");\n    case Node::Prec::Assign:\n      return printStr(\"Node::Prec::Assign\");\n    case Node::Prec::Comma:\n      return printStr(\"Node::Prec::Comma\");\n    case Node::Prec::Default:\n      return printStr(\"Node::Prec::Default\");\n    }\n  }\n\n  void newLine() {\n    printStr(\"\\n\");\n    for (unsigned I = 0; I != Depth; ++I)\n      printStr(\" \");\n    PendingNewline = false;\n  }\n\n  template<typename T> void printWithPendingNewline(T V) {\n    print(V);\n    if (wantsNewline(V))\n      PendingNewline = true;\n  }\n\n  template<typename T> void printWithComma(T V) {\n    if (PendingNewline || wantsNewline(V)) {\n      printStr(\",\");\n      newLine();\n    } else {\n      printStr(\", \");\n    }\n\n    printWithPendingNewline(V);\n  }\n\n  struct CtorArgPrinter {\n    DumpVisitor &Visitor;\n\n    template<typename T, typename ...Rest> void operator()(T V, Rest ...Vs) {\n      if (Visitor.anyWantNewline(V, Vs...))\n        Visitor.newLine();\n      Visitor.printWithPendingNewline(V);\n      int PrintInOrder[] = { (Visitor.printWithComma(Vs), 0)..., 0 };\n      (void)PrintInOrder;\n    }\n  };\n\n  template<typename NodeT> void operator()(const NodeT *Node) {\n    Depth += 2;\n    fprintf(stderr, \"%s(\", itanium_demangle::NodeKind<NodeT>::name());\n    Node->match(CtorArgPrinter{*this});\n    fprintf(stderr, \")\");\n    Depth -= 2;\n  }\n\n  void operator()(const ForwardTemplateReference *Node) {\n    Depth += 2;\n    fprintf(stderr, \"ForwardTemplateReference(\");\n    if (Node->Ref && !Node->Printing) {\n      Node->Printing = true;\n      CtorArgPrinter{*this}(Node->Ref);\n      Node->Printing = false;\n    } else {\n      CtorArgPrinter{*this}(Node->Index);\n    }\n    fprintf(stderr, \")\");\n    Depth -= 2;\n  }\n};\n}\n\nvoid itanium_demangle::Node::dump() const {\n  DumpVisitor V;\n  visit(std::ref(V));\n  V.newLine();\n}\n#endif\n\nnamespace {\nclass BumpPointerAllocator {\n  struct BlockMeta {\n    BlockMeta* Next;\n    size_t Current;\n  };\n\n  static constexpr size_t AllocSize = 4096;\n  static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta);\n\n  alignas(long double) char InitialBuffer[AllocSize];\n  BlockMeta* BlockList = nullptr;\n\n  void grow() {\n    char* NewMeta = static_cast<char *>(std::malloc(AllocSize));\n    if (NewMeta == nullptr)\n      std::terminate();\n    BlockList = new (NewMeta) BlockMeta{BlockList, 0};\n  }\n\n  void* allocateMassive(size_t NBytes) {\n    NBytes += sizeof(BlockMeta);\n    BlockMeta* NewMeta = reinterpret_cast<BlockMeta*>(std::malloc(NBytes));\n    if (NewMeta == nullptr)\n      std::terminate();\n    BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0};\n    return static_cast<void*>(NewMeta + 1);\n  }\n\npublic:\n  BumpPointerAllocator()\n      : BlockList(new (InitialBuffer) BlockMeta{nullptr, 0}) {}\n\n  void* allocate(size_t N) {\n    N = (N + 15u) & ~15u;\n    if (N + BlockList->Current >= UsableAllocSize) {\n      if (N > UsableAllocSize)\n        return allocateMassive(N);\n      grow();\n    }\n    BlockList->Current += N;\n    return static_cast<void*>(reinterpret_cast<char*>(BlockList + 1) +\n                              BlockList->Current - N);\n  }\n\n  void reset() {\n    while (BlockList) {\n      BlockMeta* Tmp = BlockList;\n      BlockList = BlockList->Next;\n      if (reinterpret_cast<char*>(Tmp) != InitialBuffer)\n        std::free(Tmp);\n    }\n    BlockList = new (InitialBuffer) BlockMeta{nullptr, 0};\n  }\n\n  ~BumpPointerAllocator() { reset(); }\n};\n\nclass DefaultAllocator {\n  BumpPointerAllocator Alloc;\n\npublic:\n  void reset() { Alloc.reset(); }\n\n  template<typename T, typename ...Args> T *makeNode(Args &&...args) {\n    return new (Alloc.allocate(sizeof(T)))\n        T(std::forward<Args>(args)...);\n  }\n\n  void *allocateNodeArray(size_t sz) {\n    return Alloc.allocate(sizeof(Node *) * sz);\n  }\n};\n}  // unnamed namespace\n\n//===----------------------------------------------------------------------===//\n// Code beyond this point should not be synchronized with libc++abi.\n//===----------------------------------------------------------------------===//\n\nusing Demangler = itanium_demangle::ManglingParser<DefaultAllocator>;\n\nchar *llvm::itaniumDemangle(std::string_view MangledName, bool ParseParams) {\n  if (MangledName.empty())\n    return nullptr;\n\n  Demangler Parser(MangledName.data(),\n                   MangledName.data() + MangledName.length());\n  Node *AST = Parser.parse(ParseParams);\n  if (!AST)\n    return nullptr;\n\n  OutputBuffer OB;\n  assert(Parser.ForwardTemplateRefs.empty());\n  AST->print(OB);\n  OB += '\\0';\n  return OB.getBuffer();\n}\n\nItaniumPartialDemangler::ItaniumPartialDemangler()\n    : RootNode(nullptr), Context(new Demangler{nullptr, nullptr}) {}\n\nItaniumPartialDemangler::~ItaniumPartialDemangler() {\n  delete static_cast<Demangler *>(Context);\n}\n\nItaniumPartialDemangler::ItaniumPartialDemangler(\n    ItaniumPartialDemangler &&Other)\n    : RootNode(Other.RootNode), Context(Other.Context) {\n  Other.Context = Other.RootNode = nullptr;\n}\n\nItaniumPartialDemangler &ItaniumPartialDemangler::\noperator=(ItaniumPartialDemangler &&Other) {\n  std::swap(RootNode, Other.RootNode);\n  std::swap(Context, Other.Context);\n  return *this;\n}\n\n// Demangle MangledName into an AST, storing it into this->RootNode.\nbool ItaniumPartialDemangler::partialDemangle(const char *MangledName) {\n  Demangler *Parser = static_cast<Demangler *>(Context);\n  size_t Len = std::strlen(MangledName);\n  Parser->reset(MangledName, MangledName + Len);\n  RootNode = Parser->parse();\n  return RootNode == nullptr;\n}\n\nstatic char *printNode(const Node *RootNode, char *Buf, size_t *N) {\n  OutputBuffer OB(Buf, N);\n  RootNode->print(OB);\n  OB += '\\0';\n  if (N != nullptr)\n    *N = OB.getCurrentPosition();\n  return OB.getBuffer();\n}\n\nchar *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const {\n  if (!isFunction())\n    return nullptr;\n\n  const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName();\n\n  while (true) {\n    switch (Name->getKind()) {\n    case Node::KAbiTagAttr:\n      Name = static_cast<const AbiTagAttr *>(Name)->Base;\n      continue;\n    case Node::KModuleEntity:\n      Name = static_cast<const ModuleEntity *>(Name)->Name;\n      continue;\n    case Node::KNestedName:\n      Name = static_cast<const NestedName *>(Name)->Name;\n      continue;\n    case Node::KLocalName:\n      Name = static_cast<const LocalName *>(Name)->Entity;\n      continue;\n    case Node::KNameWithTemplateArgs:\n      Name = static_cast<const NameWithTemplateArgs *>(Name)->Name;\n      continue;\n    default:\n      return printNode(Name, Buf, N);\n    }\n  }\n}\n\nchar *ItaniumPartialDemangler::getFunctionDeclContextName(char *Buf,\n                                                          size_t *N) const {\n  if (!isFunction())\n    return nullptr;\n  const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName();\n\n  OutputBuffer OB(Buf, N);\n\n KeepGoingLocalFunction:\n  while (true) {\n    if (Name->getKind() == Node::KAbiTagAttr) {\n      Name = static_cast<const AbiTagAttr *>(Name)->Base;\n      continue;\n    }\n    if (Name->getKind() == Node::KNameWithTemplateArgs) {\n      Name = static_cast<const NameWithTemplateArgs *>(Name)->Name;\n      continue;\n    }\n    break;\n  }\n\n  if (Name->getKind() == Node::KModuleEntity)\n    Name = static_cast<const ModuleEntity *>(Name)->Name;\n\n  switch (Name->getKind()) {\n  case Node::KNestedName:\n    static_cast<const NestedName *>(Name)->Qual->print(OB);\n    break;\n  case Node::KLocalName: {\n    auto *LN = static_cast<const LocalName *>(Name);\n    LN->Encoding->print(OB);\n    OB += \"::\";\n    Name = LN->Entity;\n    goto KeepGoingLocalFunction;\n  }\n  default:\n    break;\n  }\n  OB += '\\0';\n  if (N != nullptr)\n    *N = OB.getCurrentPosition();\n  return OB.getBuffer();\n}\n\nchar *ItaniumPartialDemangler::getFunctionName(char *Buf, size_t *N) const {\n  if (!isFunction())\n    return nullptr;\n  auto *Name = static_cast<FunctionEncoding *>(RootNode)->getName();\n  return printNode(Name, Buf, N);\n}\n\nchar *ItaniumPartialDemangler::getFunctionParameters(char *Buf,\n                                                     size_t *N) const {\n  if (!isFunction())\n    return nullptr;\n  NodeArray Params = static_cast<FunctionEncoding *>(RootNode)->getParams();\n\n  OutputBuffer OB(Buf, N);\n\n  OB += '(';\n  Params.printWithComma(OB);\n  OB += ')';\n  OB += '\\0';\n  if (N != nullptr)\n    *N = OB.getCurrentPosition();\n  return OB.getBuffer();\n}\n\nchar *ItaniumPartialDemangler::getFunctionReturnType(\n    char *Buf, size_t *N) const {\n  if (!isFunction())\n    return nullptr;\n\n  OutputBuffer OB(Buf, N);\n\n  if (const Node *Ret =\n          static_cast<const FunctionEncoding *>(RootNode)->getReturnType())\n    Ret->print(OB);\n\n  OB += '\\0';\n  if (N != nullptr)\n    *N = OB.getCurrentPosition();\n  return OB.getBuffer();\n}\n\nchar *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const {\n  assert(RootNode != nullptr && \"must call partialDemangle()\");\n  return printNode(static_cast<Node *>(RootNode), Buf, N);\n}\n\nbool ItaniumPartialDemangler::hasFunctionQualifiers() const {\n  assert(RootNode != nullptr && \"must call partialDemangle()\");\n  if (!isFunction())\n    return false;\n  auto *E = static_cast<const FunctionEncoding *>(RootNode);\n  return E->getCVQuals() != QualNone || E->getRefQual() != FrefQualNone;\n}\n\nbool ItaniumPartialDemangler::isCtorOrDtor() const {\n  const Node *N = static_cast<const Node *>(RootNode);\n  while (N) {\n    switch (N->getKind()) {\n    default:\n      return false;\n    case Node::KCtorDtorName:\n      return true;\n\n    case Node::KAbiTagAttr:\n      N = static_cast<const AbiTagAttr *>(N)->Base;\n      break;\n    case Node::KFunctionEncoding:\n      N = static_cast<const FunctionEncoding *>(N)->getName();\n      break;\n    case Node::KLocalName:\n      N = static_cast<const LocalName *>(N)->Entity;\n      break;\n    case Node::KNameWithTemplateArgs:\n      N = static_cast<const NameWithTemplateArgs *>(N)->Name;\n      break;\n    case Node::KNestedName:\n      N = static_cast<const NestedName *>(N)->Name;\n      break;\n    case Node::KModuleEntity:\n      N = static_cast<const ModuleEntity *>(N)->Name;\n      break;\n    }\n  }\n  return false;\n}\n\nbool ItaniumPartialDemangler::isFunction() const {\n  assert(RootNode != nullptr && \"must call partialDemangle()\");\n  return static_cast<const Node *>(RootNode)->getKind() ==\n         Node::KFunctionEncoding;\n}\n\nbool ItaniumPartialDemangler::isSpecialName() const {\n  assert(RootNode != nullptr && \"must call partialDemangle()\");\n  auto K = static_cast<const Node *>(RootNode)->getKind();\n  return K == Node::KSpecialName || K == Node::KCtorVtableSpecialName;\n}\n\nbool ItaniumPartialDemangler::isData() const {\n  return !isFunction() && !isSpecialName();\n}\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/source/MicrosoftDemangle.cpp",
    "content": "//===- MicrosoftDemangle.cpp ----------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file defines a demangler for MSVC-style mangled symbols.\n//\n// This file has no dependencies on the rest of LLVM so that it can be\n// easily reused in other programs such as libcxxabi.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"llvm/Demangle/MicrosoftDemangle.h\"\n\n#include \"llvm/Demangle/Demangle.h\"\n#include \"llvm/Demangle/DemangleConfig.h\"\n#include \"llvm/Demangle/MicrosoftDemangleNodes.h\"\n#include \"llvm/Demangle/StringViewExtras.h\"\n#include \"llvm/Demangle/Utility.h\"\n\n#include <array>\n#include <cctype>\n#include <cstdio>\n#include <optional>\n#include <string_view>\n#include <tuple>\n\nusing namespace llvm;\nusing namespace ms_demangle;\n\nstatic bool startsWithDigit(std::string_view S) {\n  return !S.empty() && std::isdigit(S.front());\n}\n\nstruct NodeList {\n  Node *N = nullptr;\n  NodeList *Next = nullptr;\n};\n\nstatic bool consumeFront(std::string_view &S, char C) {\n  if (!llvm::itanium_demangle::starts_with(S, C))\n    return false;\n  S.remove_prefix(1);\n  return true;\n}\n\nstatic bool consumeFront(std::string_view &S, std::string_view C) {\n  if (!llvm::itanium_demangle::starts_with(S, C))\n    return false;\n  S.remove_prefix(C.size());\n  return true;\n}\n\nstatic bool consumeFront(std::string_view &S, std::string_view PrefixA,\n                         std::string_view PrefixB, bool A) {\n  const std::string_view &Prefix = A ? PrefixA : PrefixB;\n  return consumeFront(S, Prefix);\n}\n\nstatic bool startsWith(std::string_view S, std::string_view PrefixA,\n                       std::string_view PrefixB, bool A) {\n  const std::string_view &Prefix = A ? PrefixA : PrefixB;\n  return llvm::itanium_demangle::starts_with(S, Prefix);\n}\n\nstatic bool isMemberPointer(std::string_view MangledName, bool &Error) {\n  Error = false;\n  const char F = MangledName.front();\n  MangledName.remove_prefix(1);\n  switch (F) {\n  case '$':\n    // This is probably an rvalue reference (e.g. $$Q), and you cannot have an\n    // rvalue reference to a member.\n    return false;\n  case 'A':\n    // 'A' indicates a reference, and you cannot have a reference to a member\n    // function or member.\n    return false;\n  case 'P':\n  case 'Q':\n  case 'R':\n  case 'S':\n    // These 4 values indicate some kind of pointer, but we still don't know\n    // what.\n    break;\n  default:\n    // isMemberPointer() is called only if isPointerType() returns true,\n    // and it rejects other prefixes.\n    DEMANGLE_UNREACHABLE;\n  }\n\n  // If it starts with a number, then 6 indicates a non-member function\n  // pointer, and 8 indicates a member function pointer.\n  if (startsWithDigit(MangledName)) {\n    if (MangledName[0] != '6' && MangledName[0] != '8') {\n      Error = true;\n      return false;\n    }\n    return (MangledName[0] == '8');\n  }\n\n  // Remove ext qualifiers since those can appear on either type and are\n  // therefore not indicative.\n  consumeFront(MangledName, 'E'); // 64-bit\n  consumeFront(MangledName, 'I'); // restrict\n  consumeFront(MangledName, 'F'); // unaligned\n\n  if (MangledName.empty()) {\n    Error = true;\n    return false;\n  }\n\n  // The next value should be either ABCD (non-member) or QRST (member).\n  switch (MangledName.front()) {\n  case 'A':\n  case 'B':\n  case 'C':\n  case 'D':\n    return false;\n  case 'Q':\n  case 'R':\n  case 'S':\n  case 'T':\n    return true;\n  default:\n    Error = true;\n    return false;\n  }\n}\n\nstatic SpecialIntrinsicKind\nconsumeSpecialIntrinsicKind(std::string_view &MangledName) {\n  if (consumeFront(MangledName, \"?_7\"))\n    return SpecialIntrinsicKind::Vftable;\n  if (consumeFront(MangledName, \"?_8\"))\n    return SpecialIntrinsicKind::Vbtable;\n  if (consumeFront(MangledName, \"?_9\"))\n    return SpecialIntrinsicKind::VcallThunk;\n  if (consumeFront(MangledName, \"?_A\"))\n    return SpecialIntrinsicKind::Typeof;\n  if (consumeFront(MangledName, \"?_B\"))\n    return SpecialIntrinsicKind::LocalStaticGuard;\n  if (consumeFront(MangledName, \"?_C\"))\n    return SpecialIntrinsicKind::StringLiteralSymbol;\n  if (consumeFront(MangledName, \"?_P\"))\n    return SpecialIntrinsicKind::UdtReturning;\n  if (consumeFront(MangledName, \"?_R0\"))\n    return SpecialIntrinsicKind::RttiTypeDescriptor;\n  if (consumeFront(MangledName, \"?_R1\"))\n    return SpecialIntrinsicKind::RttiBaseClassDescriptor;\n  if (consumeFront(MangledName, \"?_R2\"))\n    return SpecialIntrinsicKind::RttiBaseClassArray;\n  if (consumeFront(MangledName, \"?_R3\"))\n    return SpecialIntrinsicKind::RttiClassHierarchyDescriptor;\n  if (consumeFront(MangledName, \"?_R4\"))\n    return SpecialIntrinsicKind::RttiCompleteObjLocator;\n  if (consumeFront(MangledName, \"?_S\"))\n    return SpecialIntrinsicKind::LocalVftable;\n  if (consumeFront(MangledName, \"?__E\"))\n    return SpecialIntrinsicKind::DynamicInitializer;\n  if (consumeFront(MangledName, \"?__F\"))\n    return SpecialIntrinsicKind::DynamicAtexitDestructor;\n  if (consumeFront(MangledName, \"?__J\"))\n    return SpecialIntrinsicKind::LocalStaticThreadGuard;\n  return SpecialIntrinsicKind::None;\n}\n\nstatic bool startsWithLocalScopePattern(std::string_view S) {\n  if (!consumeFront(S, '?'))\n    return false;\n\n  size_t End = S.find('?');\n  if (End == std::string_view::npos)\n    return false;\n  std::string_view Candidate = S.substr(0, End);\n  if (Candidate.empty())\n    return false;\n\n  // \\?[0-9]\\?\n  // ?@? is the discriminator 0.\n  if (Candidate.size() == 1)\n    return Candidate[0] == '@' || (Candidate[0] >= '0' && Candidate[0] <= '9');\n\n  // If it's not 0-9, then it's an encoded number terminated with an @\n  if (Candidate.back() != '@')\n    return false;\n  Candidate.remove_suffix(1);\n\n  // An encoded number starts with B-P and all subsequent digits are in A-P.\n  // Note that the reason the first digit cannot be A is two fold.  First, it\n  // would create an ambiguity with ?A which delimits the beginning of an\n  // anonymous namespace.  Second, A represents 0, and you don't start a multi\n  // digit number with a leading 0.  Presumably the anonymous namespace\n  // ambiguity is also why single digit encoded numbers use 0-9 rather than A-J.\n  if (Candidate[0] < 'B' || Candidate[0] > 'P')\n    return false;\n  Candidate.remove_prefix(1);\n  while (!Candidate.empty()) {\n    if (Candidate[0] < 'A' || Candidate[0] > 'P')\n      return false;\n    Candidate.remove_prefix(1);\n  }\n\n  return true;\n}\n\nstatic bool isTagType(std::string_view S) {\n  switch (S.front()) {\n  case 'T': // union\n  case 'U': // struct\n  case 'V': // class\n  case 'W': // enum\n    return true;\n  }\n  return false;\n}\n\nstatic bool isCustomType(std::string_view S) { return S[0] == '?'; }\n\nstatic bool isPointerType(std::string_view S) {\n  if (llvm::itanium_demangle::starts_with(S, \"$$Q\")) // foo &&\n    return true;\n\n  switch (S.front()) {\n  case 'A': // foo &\n  case 'P': // foo *\n  case 'Q': // foo *const\n  case 'R': // foo *volatile\n  case 'S': // foo *const volatile\n    return true;\n  }\n  return false;\n}\n\nstatic bool isArrayType(std::string_view S) { return S[0] == 'Y'; }\n\nstatic bool isFunctionType(std::string_view S) {\n  return llvm::itanium_demangle::starts_with(S, \"$$A8@@\") ||\n         llvm::itanium_demangle::starts_with(S, \"$$A6\");\n}\n\nstatic FunctionRefQualifier\ndemangleFunctionRefQualifier(std::string_view &MangledName) {\n  if (consumeFront(MangledName, 'G'))\n    return FunctionRefQualifier::Reference;\n  else if (consumeFront(MangledName, 'H'))\n    return FunctionRefQualifier::RValueReference;\n  return FunctionRefQualifier::None;\n}\n\nstatic std::pair<Qualifiers, PointerAffinity>\ndemanglePointerCVQualifiers(std::string_view &MangledName) {\n  if (consumeFront(MangledName, \"$$Q\"))\n    return std::make_pair(Q_None, PointerAffinity::RValueReference);\n\n  const char F = MangledName.front();\n  MangledName.remove_prefix(1);\n  switch (F) {\n  case 'A':\n    return std::make_pair(Q_None, PointerAffinity::Reference);\n  case 'P':\n    return std::make_pair(Q_None, PointerAffinity::Pointer);\n  case 'Q':\n    return std::make_pair(Q_Const, PointerAffinity::Pointer);\n  case 'R':\n    return std::make_pair(Q_Volatile, PointerAffinity::Pointer);\n  case 'S':\n    return std::make_pair(Qualifiers(Q_Const | Q_Volatile),\n                          PointerAffinity::Pointer);\n  }\n  // This function is only called if isPointerType() returns true,\n  // and it only returns true for the six cases listed above.\n  DEMANGLE_UNREACHABLE;\n}\n\nstd::string_view Demangler::copyString(std::string_view Borrowed) {\n  char *Stable = Arena.allocUnalignedBuffer(Borrowed.size());\n  // This is not a micro-optimization, it avoids UB, should Borrowed be an null\n  // buffer.\n  if (Borrowed.size())\n    std::memcpy(Stable, Borrowed.data(), Borrowed.size());\n\n  return {Stable, Borrowed.size()};\n}\n\nSpecialTableSymbolNode *\nDemangler::demangleSpecialTableSymbolNode(std::string_view &MangledName,\n                                          SpecialIntrinsicKind K) {\n  NamedIdentifierNode *NI = Arena.alloc<NamedIdentifierNode>();\n  switch (K) {\n  case SpecialIntrinsicKind::Vftable:\n    NI->Name = \"`vftable'\";\n    break;\n  case SpecialIntrinsicKind::Vbtable:\n    NI->Name = \"`vbtable'\";\n    break;\n  case SpecialIntrinsicKind::LocalVftable:\n    NI->Name = \"`local vftable'\";\n    break;\n  case SpecialIntrinsicKind::RttiCompleteObjLocator:\n    NI->Name = \"`RTTI Complete Object Locator'\";\n    break;\n  default:\n    DEMANGLE_UNREACHABLE;\n  }\n  QualifiedNameNode *QN = demangleNameScopeChain(MangledName, NI);\n  SpecialTableSymbolNode *STSN = Arena.alloc<SpecialTableSymbolNode>();\n  STSN->Name = QN;\n  bool IsMember = false;\n  if (MangledName.empty()) {\n    Error = true;\n    return nullptr;\n  }\n  char Front = MangledName.front();\n  MangledName.remove_prefix(1);\n  if (Front != '6' && Front != '7') {\n    Error = true;\n    return nullptr;\n  }\n\n  std::tie(STSN->Quals, IsMember) = demangleQualifiers(MangledName);\n  if (!consumeFront(MangledName, '@'))\n    STSN->TargetName = demangleFullyQualifiedTypeName(MangledName);\n  return STSN;\n}\n\nLocalStaticGuardVariableNode *\nDemangler::demangleLocalStaticGuard(std::string_view &MangledName,\n                                    bool IsThread) {\n  LocalStaticGuardIdentifierNode *LSGI =\n      Arena.alloc<LocalStaticGuardIdentifierNode>();\n  LSGI->IsThread = IsThread;\n  QualifiedNameNode *QN = demangleNameScopeChain(MangledName, LSGI);\n  LocalStaticGuardVariableNode *LSGVN =\n      Arena.alloc<LocalStaticGuardVariableNode>();\n  LSGVN->Name = QN;\n\n  if (consumeFront(MangledName, \"4IA\"))\n    LSGVN->IsVisible = false;\n  else if (consumeFront(MangledName, \"5\"))\n    LSGVN->IsVisible = true;\n  else {\n    Error = true;\n    return nullptr;\n  }\n\n  if (!MangledName.empty())\n    LSGI->ScopeIndex = demangleUnsigned(MangledName);\n  return LSGVN;\n}\n\nstatic NamedIdentifierNode *synthesizeNamedIdentifier(ArenaAllocator &Arena,\n                                                      std::string_view Name) {\n  NamedIdentifierNode *Id = Arena.alloc<NamedIdentifierNode>();\n  Id->Name = Name;\n  return Id;\n}\n\nstatic QualifiedNameNode *synthesizeQualifiedName(ArenaAllocator &Arena,\n                                                  IdentifierNode *Identifier) {\n  QualifiedNameNode *QN = Arena.alloc<QualifiedNameNode>();\n  QN->Components = Arena.alloc<NodeArrayNode>();\n  QN->Components->Count = 1;\n  QN->Components->Nodes = Arena.allocArray<Node *>(1);\n  QN->Components->Nodes[0] = Identifier;\n  return QN;\n}\n\nstatic QualifiedNameNode *synthesizeQualifiedName(ArenaAllocator &Arena,\n                                                  std::string_view Name) {\n  NamedIdentifierNode *Id = synthesizeNamedIdentifier(Arena, Name);\n  return synthesizeQualifiedName(Arena, Id);\n}\n\nstatic VariableSymbolNode *synthesizeVariable(ArenaAllocator &Arena,\n                                              TypeNode *Type,\n                                              std::string_view VariableName) {\n  VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();\n  VSN->Type = Type;\n  VSN->Name = synthesizeQualifiedName(Arena, VariableName);\n  return VSN;\n}\n\nVariableSymbolNode *\nDemangler::demangleUntypedVariable(ArenaAllocator &Arena,\n                                   std::string_view &MangledName,\n                                   std::string_view VariableName) {\n  NamedIdentifierNode *NI = synthesizeNamedIdentifier(Arena, VariableName);\n  QualifiedNameNode *QN = demangleNameScopeChain(MangledName, NI);\n  VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();\n  VSN->Name = QN;\n  if (consumeFront(MangledName, \"8\"))\n    return VSN;\n\n  Error = true;\n  return nullptr;\n}\n\nVariableSymbolNode *\nDemangler::demangleRttiBaseClassDescriptorNode(ArenaAllocator &Arena,\n                                               std::string_view &MangledName) {\n  RttiBaseClassDescriptorNode *RBCDN =\n      Arena.alloc<RttiBaseClassDescriptorNode>();\n  RBCDN->NVOffset = demangleUnsigned(MangledName);\n  RBCDN->VBPtrOffset = demangleSigned(MangledName);\n  RBCDN->VBTableOffset = demangleUnsigned(MangledName);\n  RBCDN->Flags = demangleUnsigned(MangledName);\n  if (Error)\n    return nullptr;\n\n  VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();\n  VSN->Name = demangleNameScopeChain(MangledName, RBCDN);\n  consumeFront(MangledName, '8');\n  return VSN;\n}\n\nFunctionSymbolNode *\nDemangler::demangleInitFiniStub(std::string_view &MangledName,\n                                bool IsDestructor) {\n  DynamicStructorIdentifierNode *DSIN =\n      Arena.alloc<DynamicStructorIdentifierNode>();\n  DSIN->IsDestructor = IsDestructor;\n\n  bool IsKnownStaticDataMember = false;\n  if (consumeFront(MangledName, '?'))\n    IsKnownStaticDataMember = true;\n\n  SymbolNode *Symbol = demangleDeclarator(MangledName);\n  if (Error)\n    return nullptr;\n\n  FunctionSymbolNode *FSN = nullptr;\n\n  if (Symbol->kind() == NodeKind::VariableSymbol) {\n    DSIN->Variable = static_cast<VariableSymbolNode *>(Symbol);\n\n    // Older versions of clang mangled this type of symbol incorrectly.  They\n    // would omit the leading ? and they would only emit a single @ at the end.\n    // The correct mangling is a leading ? and 2 trailing @ signs.  Handle\n    // both cases.\n    int AtCount = IsKnownStaticDataMember ? 2 : 1;\n    for (int I = 0; I < AtCount; ++I) {\n      if (consumeFront(MangledName, '@'))\n        continue;\n      Error = true;\n      return nullptr;\n    }\n\n    FSN = demangleFunctionEncoding(MangledName);\n    if (FSN)\n      FSN->Name = synthesizeQualifiedName(Arena, DSIN);\n  } else {\n    if (IsKnownStaticDataMember) {\n      // This was supposed to be a static data member, but we got a function.\n      Error = true;\n      return nullptr;\n    }\n\n    FSN = static_cast<FunctionSymbolNode *>(Symbol);\n    DSIN->Name = Symbol->Name;\n    FSN->Name = synthesizeQualifiedName(Arena, DSIN);\n  }\n\n  return FSN;\n}\n\nSymbolNode *Demangler::demangleSpecialIntrinsic(std::string_view &MangledName) {\n  SpecialIntrinsicKind SIK = consumeSpecialIntrinsicKind(MangledName);\n\n  switch (SIK) {\n  case SpecialIntrinsicKind::None:\n    return nullptr;\n  case SpecialIntrinsicKind::StringLiteralSymbol:\n    return demangleStringLiteral(MangledName);\n  case SpecialIntrinsicKind::Vftable:\n  case SpecialIntrinsicKind::Vbtable:\n  case SpecialIntrinsicKind::LocalVftable:\n  case SpecialIntrinsicKind::RttiCompleteObjLocator:\n    return demangleSpecialTableSymbolNode(MangledName, SIK);\n  case SpecialIntrinsicKind::VcallThunk:\n    return demangleVcallThunkNode(MangledName);\n  case SpecialIntrinsicKind::LocalStaticGuard:\n    return demangleLocalStaticGuard(MangledName, /*IsThread=*/false);\n  case SpecialIntrinsicKind::LocalStaticThreadGuard:\n    return demangleLocalStaticGuard(MangledName, /*IsThread=*/true);\n  case SpecialIntrinsicKind::RttiTypeDescriptor: {\n    TypeNode *T = demangleType(MangledName, QualifierMangleMode::Result);\n    if (Error)\n      break;\n    if (!consumeFront(MangledName, \"@8\"))\n      break;\n    if (!MangledName.empty())\n      break;\n    return synthesizeVariable(Arena, T, \"`RTTI Type Descriptor'\");\n  }\n  case SpecialIntrinsicKind::RttiBaseClassArray:\n    return demangleUntypedVariable(Arena, MangledName,\n                                   \"`RTTI Base Class Array'\");\n  case SpecialIntrinsicKind::RttiClassHierarchyDescriptor:\n    return demangleUntypedVariable(Arena, MangledName,\n                                   \"`RTTI Class Hierarchy Descriptor'\");\n  case SpecialIntrinsicKind::RttiBaseClassDescriptor:\n    return demangleRttiBaseClassDescriptorNode(Arena, MangledName);\n  case SpecialIntrinsicKind::DynamicInitializer:\n    return demangleInitFiniStub(MangledName, /*IsDestructor=*/false);\n  case SpecialIntrinsicKind::DynamicAtexitDestructor:\n    return demangleInitFiniStub(MangledName, /*IsDestructor=*/true);\n  case SpecialIntrinsicKind::Typeof:\n  case SpecialIntrinsicKind::UdtReturning:\n    // It's unclear which tools produces these manglings, so demangling\n    // support is not (yet?) implemented.\n    break;\n  case SpecialIntrinsicKind::Unknown:\n    DEMANGLE_UNREACHABLE; // Never returned by consumeSpecialIntrinsicKind.\n  }\n  Error = true;\n  return nullptr;\n}\n\nIdentifierNode *\nDemangler::demangleFunctionIdentifierCode(std::string_view &MangledName) {\n  assert(llvm::itanium_demangle::starts_with(MangledName, '?'));\n  MangledName.remove_prefix(1);\n  if (MangledName.empty()) {\n    Error = true;\n    return nullptr;\n  }\n\n  if (consumeFront(MangledName, \"__\"))\n    return demangleFunctionIdentifierCode(\n        MangledName, FunctionIdentifierCodeGroup::DoubleUnder);\n  if (consumeFront(MangledName, \"_\"))\n    return demangleFunctionIdentifierCode(MangledName,\n                                          FunctionIdentifierCodeGroup::Under);\n  return demangleFunctionIdentifierCode(MangledName,\n                                        FunctionIdentifierCodeGroup::Basic);\n}\n\nStructorIdentifierNode *\nDemangler::demangleStructorIdentifier(std::string_view &MangledName,\n                                      bool IsDestructor) {\n  StructorIdentifierNode *N = Arena.alloc<StructorIdentifierNode>();\n  N->IsDestructor = IsDestructor;\n  return N;\n}\n\nConversionOperatorIdentifierNode *\nDemangler::demangleConversionOperatorIdentifier(std::string_view &MangledName) {\n  ConversionOperatorIdentifierNode *N =\n      Arena.alloc<ConversionOperatorIdentifierNode>();\n  return N;\n}\n\nLiteralOperatorIdentifierNode *\nDemangler::demangleLiteralOperatorIdentifier(std::string_view &MangledName) {\n  LiteralOperatorIdentifierNode *N =\n      Arena.alloc<LiteralOperatorIdentifierNode>();\n  N->Name = demangleSimpleString(MangledName, /*Memorize=*/false);\n  return N;\n}\n\nIntrinsicFunctionKind\nDemangler::translateIntrinsicFunctionCode(char CH,\n                                          FunctionIdentifierCodeGroup Group) {\n  using IFK = IntrinsicFunctionKind;\n  if (!(CH >= '0' && CH <= '9') && !(CH >= 'A' && CH <= 'Z')) {\n    Error = true;\n    return IFK::None;\n  }\n\n  // Not all ? identifiers are intrinsics *functions*.  This function only maps\n  // operator codes for the special functions, all others are handled elsewhere,\n  // hence the IFK::None entries in the table.\n  static IFK Basic[36] = {\n      IFK::None,             // ?0 # Foo::Foo()\n      IFK::None,             // ?1 # Foo::~Foo()\n      IFK::New,              // ?2 # operator new\n      IFK::Delete,           // ?3 # operator delete\n      IFK::Assign,           // ?4 # operator=\n      IFK::RightShift,       // ?5 # operator>>\n      IFK::LeftShift,        // ?6 # operator<<\n      IFK::LogicalNot,       // ?7 # operator!\n      IFK::Equals,           // ?8 # operator==\n      IFK::NotEquals,        // ?9 # operator!=\n      IFK::ArraySubscript,   // ?A # operator[]\n      IFK::None,             // ?B # Foo::operator <type>()\n      IFK::Pointer,          // ?C # operator->\n      IFK::Dereference,      // ?D # operator*\n      IFK::Increment,        // ?E # operator++\n      IFK::Decrement,        // ?F # operator--\n      IFK::Minus,            // ?G # operator-\n      IFK::Plus,             // ?H # operator+\n      IFK::BitwiseAnd,       // ?I # operator&\n      IFK::MemberPointer,    // ?J # operator->*\n      IFK::Divide,           // ?K # operator/\n      IFK::Modulus,          // ?L # operator%\n      IFK::LessThan,         // ?M operator<\n      IFK::LessThanEqual,    // ?N operator<=\n      IFK::GreaterThan,      // ?O operator>\n      IFK::GreaterThanEqual, // ?P operator>=\n      IFK::Comma,            // ?Q operator,\n      IFK::Parens,           // ?R operator()\n      IFK::BitwiseNot,       // ?S operator~\n      IFK::BitwiseXor,       // ?T operator^\n      IFK::BitwiseOr,        // ?U operator|\n      IFK::LogicalAnd,       // ?V operator&&\n      IFK::LogicalOr,        // ?W operator||\n      IFK::TimesEqual,       // ?X operator*=\n      IFK::PlusEqual,        // ?Y operator+=\n      IFK::MinusEqual,       // ?Z operator-=\n  };\n  static IFK Under[36] = {\n      IFK::DivEqual,           // ?_0 operator/=\n      IFK::ModEqual,           // ?_1 operator%=\n      IFK::RshEqual,           // ?_2 operator>>=\n      IFK::LshEqual,           // ?_3 operator<<=\n      IFK::BitwiseAndEqual,    // ?_4 operator&=\n      IFK::BitwiseOrEqual,     // ?_5 operator|=\n      IFK::BitwiseXorEqual,    // ?_6 operator^=\n      IFK::None,               // ?_7 # vftable\n      IFK::None,               // ?_8 # vbtable\n      IFK::None,               // ?_9 # vcall\n      IFK::None,               // ?_A # typeof\n      IFK::None,               // ?_B # local static guard\n      IFK::None,               // ?_C # string literal\n      IFK::VbaseDtor,          // ?_D # vbase destructor\n      IFK::VecDelDtor,         // ?_E # vector deleting destructor\n      IFK::DefaultCtorClosure, // ?_F # default constructor closure\n      IFK::ScalarDelDtor,      // ?_G # scalar deleting destructor\n      IFK::VecCtorIter,        // ?_H # vector constructor iterator\n      IFK::VecDtorIter,        // ?_I # vector destructor iterator\n      IFK::VecVbaseCtorIter,   // ?_J # vector vbase constructor iterator\n      IFK::VdispMap,           // ?_K # virtual displacement map\n      IFK::EHVecCtorIter,      // ?_L # eh vector constructor iterator\n      IFK::EHVecDtorIter,      // ?_M # eh vector destructor iterator\n      IFK::EHVecVbaseCtorIter, // ?_N # eh vector vbase constructor iterator\n      IFK::CopyCtorClosure,    // ?_O # copy constructor closure\n      IFK::None,               // ?_P<name> # udt returning <name>\n      IFK::None,               // ?_Q # <unknown>\n      IFK::None,               // ?_R0 - ?_R4 # RTTI Codes\n      IFK::None,               // ?_S # local vftable\n      IFK::LocalVftableCtorClosure, // ?_T # local vftable constructor closure\n      IFK::ArrayNew,                // ?_U operator new[]\n      IFK::ArrayDelete,             // ?_V operator delete[]\n      IFK::None,                    // ?_W <unused>\n      IFK::None,                    // ?_X <unused>\n      IFK::None,                    // ?_Y <unused>\n      IFK::None,                    // ?_Z <unused>\n  };\n  static IFK DoubleUnder[36] = {\n      IFK::None,                       // ?__0 <unused>\n      IFK::None,                       // ?__1 <unused>\n      IFK::None,                       // ?__2 <unused>\n      IFK::None,                       // ?__3 <unused>\n      IFK::None,                       // ?__4 <unused>\n      IFK::None,                       // ?__5 <unused>\n      IFK::None,                       // ?__6 <unused>\n      IFK::None,                       // ?__7 <unused>\n      IFK::None,                       // ?__8 <unused>\n      IFK::None,                       // ?__9 <unused>\n      IFK::ManVectorCtorIter,          // ?__A managed vector ctor iterator\n      IFK::ManVectorDtorIter,          // ?__B managed vector dtor iterator\n      IFK::EHVectorCopyCtorIter,       // ?__C EH vector copy ctor iterator\n      IFK::EHVectorVbaseCopyCtorIter,  // ?__D EH vector vbase copy ctor iter\n      IFK::None,                       // ?__E dynamic initializer for `T'\n      IFK::None,                       // ?__F dynamic atexit destructor for `T'\n      IFK::VectorCopyCtorIter,         // ?__G vector copy constructor iter\n      IFK::VectorVbaseCopyCtorIter,    // ?__H vector vbase copy ctor iter\n      IFK::ManVectorVbaseCopyCtorIter, // ?__I managed vector vbase copy ctor\n                                       // iter\n      IFK::None,                       // ?__J local static thread guard\n      IFK::None,                       // ?__K operator \"\"_name\n      IFK::CoAwait,                    // ?__L operator co_await\n      IFK::Spaceship,                  // ?__M operator<=>\n      IFK::None,                       // ?__N <unused>\n      IFK::None,                       // ?__O <unused>\n      IFK::None,                       // ?__P <unused>\n      IFK::None,                       // ?__Q <unused>\n      IFK::None,                       // ?__R <unused>\n      IFK::None,                       // ?__S <unused>\n      IFK::None,                       // ?__T <unused>\n      IFK::None,                       // ?__U <unused>\n      IFK::None,                       // ?__V <unused>\n      IFK::None,                       // ?__W <unused>\n      IFK::None,                       // ?__X <unused>\n      IFK::None,                       // ?__Y <unused>\n      IFK::None,                       // ?__Z <unused>\n  };\n\n  int Index = (CH >= '0' && CH <= '9') ? (CH - '0') : (CH - 'A' + 10);\n  switch (Group) {\n  case FunctionIdentifierCodeGroup::Basic:\n    return Basic[Index];\n  case FunctionIdentifierCodeGroup::Under:\n    return Under[Index];\n  case FunctionIdentifierCodeGroup::DoubleUnder:\n    return DoubleUnder[Index];\n  }\n  DEMANGLE_UNREACHABLE;\n}\n\nIdentifierNode *\nDemangler::demangleFunctionIdentifierCode(std::string_view &MangledName,\n                                          FunctionIdentifierCodeGroup Group) {\n  if (MangledName.empty()) {\n    Error = true;\n    return nullptr;\n  }\n  const char CH = MangledName.front();\n  switch (Group) {\n  case FunctionIdentifierCodeGroup::Basic:\n    MangledName.remove_prefix(1);\n    switch (CH) {\n    case '0':\n    case '1':\n      return demangleStructorIdentifier(MangledName, CH == '1');\n    case 'B':\n      return demangleConversionOperatorIdentifier(MangledName);\n    default:\n      return Arena.alloc<IntrinsicFunctionIdentifierNode>(\n          translateIntrinsicFunctionCode(CH, Group));\n    }\n  case FunctionIdentifierCodeGroup::Under:\n    MangledName.remove_prefix(1);\n    return Arena.alloc<IntrinsicFunctionIdentifierNode>(\n        translateIntrinsicFunctionCode(CH, Group));\n  case FunctionIdentifierCodeGroup::DoubleUnder:\n    MangledName.remove_prefix(1);\n    switch (CH) {\n    case 'K':\n      return demangleLiteralOperatorIdentifier(MangledName);\n    default:\n      return Arena.alloc<IntrinsicFunctionIdentifierNode>(\n          translateIntrinsicFunctionCode(CH, Group));\n    }\n  }\n\n  DEMANGLE_UNREACHABLE;\n}\n\nSymbolNode *Demangler::demangleEncodedSymbol(std::string_view &MangledName,\n                                             QualifiedNameNode *Name) {\n  if (MangledName.empty()) {\n    Error = true;\n    return nullptr;\n  }\n\n  // Read a variable.\n  switch (MangledName.front()) {\n  case '0':\n  case '1':\n  case '2':\n  case '3':\n  case '4': {\n    StorageClass SC = demangleVariableStorageClass(MangledName);\n    return demangleVariableEncoding(MangledName, SC);\n  }\n  }\n  FunctionSymbolNode *FSN = demangleFunctionEncoding(MangledName);\n\n  IdentifierNode *UQN = Name->getUnqualifiedIdentifier();\n  if (UQN->kind() == NodeKind::ConversionOperatorIdentifier) {\n    ConversionOperatorIdentifierNode *COIN =\n        static_cast<ConversionOperatorIdentifierNode *>(UQN);\n    if (FSN)\n      COIN->TargetType = FSN->Signature->ReturnType;\n  }\n  return FSN;\n}\n\nSymbolNode *Demangler::demangleDeclarator(std::string_view &MangledName) {\n  // What follows is a main symbol name. This may include namespaces or class\n  // back references.\n  QualifiedNameNode *QN = demangleFullyQualifiedSymbolName(MangledName);\n  if (Error)\n    return nullptr;\n\n  SymbolNode *Symbol = demangleEncodedSymbol(MangledName, QN);\n  if (Error)\n    return nullptr;\n  Symbol->Name = QN;\n\n  IdentifierNode *UQN = QN->getUnqualifiedIdentifier();\n  if (UQN->kind() == NodeKind::ConversionOperatorIdentifier) {\n    ConversionOperatorIdentifierNode *COIN =\n        static_cast<ConversionOperatorIdentifierNode *>(UQN);\n    if (!COIN->TargetType) {\n      Error = true;\n      return nullptr;\n    }\n  }\n  return Symbol;\n}\n\nSymbolNode *Demangler::demangleMD5Name(std::string_view &MangledName) {\n  assert(llvm::itanium_demangle::starts_with(MangledName, \"??@\"));\n  // This is an MD5 mangled name.  We can't demangle it, just return the\n  // mangled name.\n  // An MD5 mangled name is ??@ followed by 32 characters and a terminating @.\n  size_t MD5Last = MangledName.find('@', strlen(\"??@\"));\n  if (MD5Last == std::string_view::npos) {\n    Error = true;\n    return nullptr;\n  }\n  const char *Start = MangledName.data();\n  const size_t StartSize = MangledName.size();\n  MangledName.remove_prefix(MD5Last + 1);\n\n  // There are two additional special cases for MD5 names:\n  // 1. For complete object locators where the object name is long enough\n  //    for the object to have an MD5 name, the complete object locator is\n  //    called ??@...@??_R4@ (with a trailing \"??_R4@\" instead of the usual\n  //    leading \"??_R4\". This is handled here.\n  // 2. For catchable types, in versions of MSVC before 2015 (<1900) or after\n  //    2017.2 (>= 1914), the catchable type mangling is _CT??@...@??@...@8\n  //    instead of_CT??@...@8 with just one MD5 name. Since we don't yet\n  //    demangle catchable types anywhere, this isn't handled for MD5 names\n  //    either.\n  consumeFront(MangledName, \"??_R4@\");\n\n  assert(MangledName.size() < StartSize);\n  const size_t Count = StartSize - MangledName.size();\n  std::string_view MD5(Start, Count);\n  SymbolNode *S = Arena.alloc<SymbolNode>(NodeKind::Md5Symbol);\n  S->Name = synthesizeQualifiedName(Arena, MD5);\n\n  return S;\n}\n\nSymbolNode *Demangler::demangleTypeinfoName(std::string_view &MangledName) {\n  assert(llvm::itanium_demangle::starts_with(MangledName, '.'));\n  consumeFront(MangledName, '.');\n\n  TypeNode *T = demangleType(MangledName, QualifierMangleMode::Result);\n  if (Error || !MangledName.empty()) {\n    Error = true;\n    return nullptr;\n  }\n  return synthesizeVariable(Arena, T, \"`RTTI Type Descriptor Name'\");\n}\n\n// Parser entry point.\nSymbolNode *Demangler::parse(std::string_view &MangledName) {\n  // Typeinfo names are strings stored in RTTI data. They're not symbol names.\n  // It's still useful to demangle them. They're the only demangled entity\n  // that doesn't start with a \"?\" but a \".\".\n  if (llvm::itanium_demangle::starts_with(MangledName, '.'))\n    return demangleTypeinfoName(MangledName);\n\n  if (llvm::itanium_demangle::starts_with(MangledName, \"??@\"))\n    return demangleMD5Name(MangledName);\n\n  // MSVC-style mangled symbols must start with '?'.\n  if (!llvm::itanium_demangle::starts_with(MangledName, '?')) {\n    Error = true;\n    return nullptr;\n  }\n\n  consumeFront(MangledName, '?');\n\n  // ?$ is a template instantiation, but all other names that start with ? are\n  // operators / special names.\n  if (SymbolNode *SI = demangleSpecialIntrinsic(MangledName))\n    return SI;\n\n  return demangleDeclarator(MangledName);\n}\n\nTagTypeNode *Demangler::parseTagUniqueName(std::string_view &MangledName) {\n  if (!consumeFront(MangledName, \".?A\")) {\n    Error = true;\n    return nullptr;\n  }\n  consumeFront(MangledName, \".?A\");\n  if (MangledName.empty()) {\n    Error = true;\n    return nullptr;\n  }\n\n  return demangleClassType(MangledName);\n}\n\n// <type-encoding> ::= <storage-class> <variable-type>\n// <storage-class> ::= 0  # private static member\n//                 ::= 1  # protected static member\n//                 ::= 2  # public static member\n//                 ::= 3  # global\n//                 ::= 4  # static local\n\nVariableSymbolNode *\nDemangler::demangleVariableEncoding(std::string_view &MangledName,\n                                    StorageClass SC) {\n  VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();\n\n  VSN->Type = demangleType(MangledName, QualifierMangleMode::Drop);\n  VSN->SC = SC;\n\n  if (Error)\n    return nullptr;\n\n  // <variable-type> ::= <type> <cvr-qualifiers>\n  //                 ::= <type> <pointee-cvr-qualifiers> # pointers, references\n  switch (VSN->Type->kind()) {\n  case NodeKind::PointerType: {\n    PointerTypeNode *PTN = static_cast<PointerTypeNode *>(VSN->Type);\n\n    Qualifiers ExtraChildQuals = Q_None;\n    PTN->Quals = Qualifiers(VSN->Type->Quals |\n                            demanglePointerExtQualifiers(MangledName));\n\n    bool IsMember = false;\n    std::tie(ExtraChildQuals, IsMember) = demangleQualifiers(MangledName);\n\n    if (PTN->ClassParent) {\n      QualifiedNameNode *BackRefName =\n          demangleFullyQualifiedTypeName(MangledName);\n      (void)BackRefName;\n    }\n    PTN->Pointee->Quals = Qualifiers(PTN->Pointee->Quals | ExtraChildQuals);\n\n    break;\n  }\n  default:\n    VSN->Type->Quals = demangleQualifiers(MangledName).first;\n    break;\n  }\n\n  return VSN;\n}\n\n// Sometimes numbers are encoded in mangled symbols. For example,\n// \"int (*x)[20]\" is a valid C type (x is a pointer to an array of\n// length 20), so we need some way to embed numbers as part of symbols.\n// This function parses it.\n//\n// <number>               ::= [?] <non-negative integer>\n//\n// <non-negative integer> ::= <decimal digit> # when 1 <= Number <= 10\n//                        ::= <hex digit>+ @  # when Number == 0 or >= 10\n//\n// <hex-digit>            ::= [A-P]           # A = 0, B = 1, ...\nstd::pair<uint64_t, bool>\nDemangler::demangleNumber(std::string_view &MangledName) {\n  bool IsNegative = consumeFront(MangledName, '?');\n\n  if (startsWithDigit(MangledName)) {\n    uint64_t Ret = MangledName[0] - '0' + 1;\n    MangledName.remove_prefix(1);\n    return {Ret, IsNegative};\n  }\n\n  uint64_t Ret = 0;\n  for (size_t i = 0; i < MangledName.size(); ++i) {\n    char C = MangledName[i];\n    if (C == '@') {\n      MangledName.remove_prefix(i + 1);\n      return {Ret, IsNegative};\n    }\n    if ('A' <= C && C <= 'P') {\n      Ret = (Ret << 4) + (C - 'A');\n      continue;\n    }\n    break;\n  }\n\n  Error = true;\n  return {0ULL, false};\n}\n\nuint64_t Demangler::demangleUnsigned(std::string_view &MangledName) {\n  bool IsNegative = false;\n  uint64_t Number = 0;\n  std::tie(Number, IsNegative) = demangleNumber(MangledName);\n  if (IsNegative)\n    Error = true;\n  return Number;\n}\n\nint64_t Demangler::demangleSigned(std::string_view &MangledName) {\n  bool IsNegative = false;\n  uint64_t Number = 0;\n  std::tie(Number, IsNegative) = demangleNumber(MangledName);\n  if (Number > INT64_MAX)\n    Error = true;\n  int64_t I = static_cast<int64_t>(Number);\n  return IsNegative ? -I : I;\n}\n\n// First 10 strings can be referenced by special BackReferences ?0, ?1, ..., ?9.\n// Memorize it.\nvoid Demangler::memorizeString(std::string_view S) {\n  if (Backrefs.NamesCount >= BackrefContext::Max)\n    return;\n  for (size_t i = 0; i < Backrefs.NamesCount; ++i)\n    if (S == Backrefs.Names[i]->Name)\n      return;\n  NamedIdentifierNode *N = Arena.alloc<NamedIdentifierNode>();\n  N->Name = S;\n  Backrefs.Names[Backrefs.NamesCount++] = N;\n}\n\nNamedIdentifierNode *\nDemangler::demangleBackRefName(std::string_view &MangledName) {\n  assert(startsWithDigit(MangledName));\n\n  size_t I = MangledName[0] - '0';\n  if (I >= Backrefs.NamesCount) {\n    Error = true;\n    return nullptr;\n  }\n\n  MangledName.remove_prefix(1);\n  return Backrefs.Names[I];\n}\n\nvoid Demangler::memorizeIdentifier(IdentifierNode *Identifier) {\n  // Render this class template name into a string buffer so that we can\n  // memorize it for the purpose of back-referencing.\n  OutputBuffer OB;\n  Identifier->output(OB, OF_Default);\n  std::string_view Owned = copyString(OB);\n  memorizeString(Owned);\n  std::free(OB.getBuffer());\n}\n\nIdentifierNode *\nDemangler::demangleTemplateInstantiationName(std::string_view &MangledName,\n                                             NameBackrefBehavior NBB) {\n  assert(llvm::itanium_demangle::starts_with(MangledName, \"?$\"));\n  consumeFront(MangledName, \"?$\");\n\n  BackrefContext OuterContext;\n  std::swap(OuterContext, Backrefs);\n\n  IdentifierNode *Identifier =\n      demangleUnqualifiedSymbolName(MangledName, NBB_Simple);\n  if (!Error)\n    Identifier->TemplateParams = demangleTemplateParameterList(MangledName);\n\n  std::swap(OuterContext, Backrefs);\n  if (Error)\n    return nullptr;\n\n  if (NBB & NBB_Template) {\n    // NBB_Template is only set for types and non-leaf names (\"a::\" in \"a::b\").\n    // Structors and conversion operators only makes sense in a leaf name, so\n    // reject them in NBB_Template contexts.\n    if (Identifier->kind() == NodeKind::ConversionOperatorIdentifier ||\n        Identifier->kind() == NodeKind::StructorIdentifier) {\n      Error = true;\n      return nullptr;\n    }\n\n    memorizeIdentifier(Identifier);\n  }\n\n  return Identifier;\n}\n\nNamedIdentifierNode *\nDemangler::demangleSimpleName(std::string_view &MangledName, bool Memorize) {\n  std::string_view S = demangleSimpleString(MangledName, Memorize);\n  if (Error)\n    return nullptr;\n\n  NamedIdentifierNode *Name = Arena.alloc<NamedIdentifierNode>();\n  Name->Name = S;\n  return Name;\n}\n\nstatic bool isRebasedHexDigit(char C) { return (C >= 'A' && C <= 'P'); }\n\nstatic uint8_t rebasedHexDigitToNumber(char C) {\n  assert(isRebasedHexDigit(C));\n  return (C <= 'J') ? (C - 'A') : (10 + C - 'K');\n}\n\nuint8_t Demangler::demangleCharLiteral(std::string_view &MangledName) {\n  assert(!MangledName.empty());\n  if (!llvm::itanium_demangle::starts_with(MangledName, '?')) {\n    const uint8_t F = MangledName.front();\n    MangledName.remove_prefix(1);\n    return F;\n  }\n\n  MangledName.remove_prefix(1);\n  if (MangledName.empty())\n    goto CharLiteralError;\n\n  if (consumeFront(MangledName, '$')) {\n    // Two hex digits\n    if (MangledName.size() < 2)\n      goto CharLiteralError;\n    std::string_view Nibbles = MangledName.substr(0, 2);\n    if (!isRebasedHexDigit(Nibbles[0]) || !isRebasedHexDigit(Nibbles[1]))\n      goto CharLiteralError;\n    // Don't append the null terminator.\n    uint8_t C1 = rebasedHexDigitToNumber(Nibbles[0]);\n    uint8_t C2 = rebasedHexDigitToNumber(Nibbles[1]);\n    MangledName.remove_prefix(2);\n    return (C1 << 4) | C2;\n  }\n\n  if (startsWithDigit(MangledName)) {\n    const char *Lookup = \",/\\\\:. \\n\\t'-\";\n    char C = Lookup[MangledName[0] - '0'];\n    MangledName.remove_prefix(1);\n    return C;\n  }\n\n  if (MangledName[0] >= 'a' && MangledName[0] <= 'z') {\n    char Lookup[26] = {'\\xE1', '\\xE2', '\\xE3', '\\xE4', '\\xE5', '\\xE6', '\\xE7',\n                       '\\xE8', '\\xE9', '\\xEA', '\\xEB', '\\xEC', '\\xED', '\\xEE',\n                       '\\xEF', '\\xF0', '\\xF1', '\\xF2', '\\xF3', '\\xF4', '\\xF5',\n                       '\\xF6', '\\xF7', '\\xF8', '\\xF9', '\\xFA'};\n    char C = Lookup[MangledName[0] - 'a'];\n    MangledName.remove_prefix(1);\n    return C;\n  }\n\n  if (MangledName[0] >= 'A' && MangledName[0] <= 'Z') {\n    char Lookup[26] = {'\\xC1', '\\xC2', '\\xC3', '\\xC4', '\\xC5', '\\xC6', '\\xC7',\n                       '\\xC8', '\\xC9', '\\xCA', '\\xCB', '\\xCC', '\\xCD', '\\xCE',\n                       '\\xCF', '\\xD0', '\\xD1', '\\xD2', '\\xD3', '\\xD4', '\\xD5',\n                       '\\xD6', '\\xD7', '\\xD8', '\\xD9', '\\xDA'};\n    char C = Lookup[MangledName[0] - 'A'];\n    MangledName.remove_prefix(1);\n    return C;\n  }\n\nCharLiteralError:\n  Error = true;\n  return '\\0';\n}\n\nwchar_t Demangler::demangleWcharLiteral(std::string_view &MangledName) {\n  uint8_t C1, C2;\n\n  C1 = demangleCharLiteral(MangledName);\n  if (Error || MangledName.empty())\n    goto WCharLiteralError;\n  C2 = demangleCharLiteral(MangledName);\n  if (Error)\n    goto WCharLiteralError;\n\n  return ((wchar_t)C1 << 8) | (wchar_t)C2;\n\nWCharLiteralError:\n  Error = true;\n  return L'\\0';\n}\n\nstatic void writeHexDigit(char *Buffer, uint8_t Digit) {\n  assert(Digit <= 15);\n  *Buffer = (Digit < 10) ? ('0' + Digit) : ('A' + Digit - 10);\n}\n\nstatic void outputHex(OutputBuffer &OB, unsigned C) {\n  assert (C != 0);\n\n  // It's easier to do the math if we can work from right to left, but we need\n  // to print the numbers from left to right.  So render this into a temporary\n  // buffer first, then output the temporary buffer.  Each byte is of the form\n  // \\xAB, which means that each byte needs 4 characters.  Since there are at\n  // most 4 bytes, we need a 4*4+1 = 17 character temporary buffer.\n  char TempBuffer[17];\n\n  ::memset(TempBuffer, 0, sizeof(TempBuffer));\n  constexpr int MaxPos = sizeof(TempBuffer) - 1;\n\n  int Pos = MaxPos - 1; // TempBuffer[MaxPos] is the terminating \\0.\n  while (C != 0) {\n    for (int I = 0; I < 2; ++I) {\n      writeHexDigit(&TempBuffer[Pos--], C % 16);\n      C /= 16;\n    }\n  }\n  TempBuffer[Pos--] = 'x';\n  assert(Pos >= 0);\n  TempBuffer[Pos--] = '\\\\';\n  OB << std::string_view(&TempBuffer[Pos + 1]);\n}\n\nstatic void outputEscapedChar(OutputBuffer &OB, unsigned C) {\n  switch (C) {\n  case '\\0': // nul\n    OB << \"\\\\0\";\n    return;\n  case '\\'': // single quote\n    OB << \"\\\\\\'\";\n    return;\n  case '\\\"': // double quote\n    OB << \"\\\\\\\"\";\n    return;\n  case '\\\\': // backslash\n    OB << \"\\\\\\\\\";\n    return;\n  case '\\a': // bell\n    OB << \"\\\\a\";\n    return;\n  case '\\b': // backspace\n    OB << \"\\\\b\";\n    return;\n  case '\\f': // form feed\n    OB << \"\\\\f\";\n    return;\n  case '\\n': // new line\n    OB << \"\\\\n\";\n    return;\n  case '\\r': // carriage return\n    OB << \"\\\\r\";\n    return;\n  case '\\t': // tab\n    OB << \"\\\\t\";\n    return;\n  case '\\v': // vertical tab\n    OB << \"\\\\v\";\n    return;\n  default:\n    break;\n  }\n\n  if (C > 0x1F && C < 0x7F) {\n    // Standard ascii char.\n    OB << (char)C;\n    return;\n  }\n\n  outputHex(OB, C);\n}\n\nstatic unsigned countTrailingNullBytes(const uint8_t *StringBytes, int Length) {\n  const uint8_t *End = StringBytes + Length - 1;\n  unsigned Count = 0;\n  while (Length > 0 && *End == 0) {\n    --Length;\n    --End;\n    ++Count;\n  }\n  return Count;\n}\n\nstatic unsigned countEmbeddedNulls(const uint8_t *StringBytes,\n                                   unsigned Length) {\n  unsigned Result = 0;\n  for (unsigned I = 0; I < Length; ++I) {\n    if (*StringBytes++ == 0)\n      ++Result;\n  }\n  return Result;\n}\n\n// A mangled (non-wide) string literal stores the total length of the string it\n// refers to (passed in NumBytes), and it contains up to 32 bytes of actual text\n// (passed in StringBytes, NumChars).\nstatic unsigned guessCharByteSize(const uint8_t *StringBytes, unsigned NumChars,\n                                  uint64_t NumBytes) {\n  assert(NumBytes > 0);\n\n  // If the number of bytes is odd, this is guaranteed to be a char string.\n  if (NumBytes % 2 == 1)\n    return 1;\n\n  // All strings can encode at most 32 bytes of data.  If it's less than that,\n  // then we encoded the entire string.  In this case we check for a 1-byte,\n  // 2-byte, or 4-byte null terminator.\n  if (NumBytes < 32) {\n    unsigned TrailingNulls = countTrailingNullBytes(StringBytes, NumChars);\n    if (TrailingNulls >= 4 && NumBytes % 4 == 0)\n      return 4;\n    if (TrailingNulls >= 2)\n      return 2;\n    return 1;\n  }\n\n  // The whole string was not able to be encoded.  Try to look at embedded null\n  // terminators to guess.  The heuristic is that we count all embedded null\n  // terminators.  If more than 2/3 are null, it's a char32.  If more than 1/3\n  // are null, it's a char16.  Otherwise it's a char8.  This obviously isn't\n  // perfect and is biased towards languages that have ascii alphabets, but this\n  // was always going to be best effort since the encoding is lossy.\n  unsigned Nulls = countEmbeddedNulls(StringBytes, NumChars);\n  if (Nulls >= 2 * NumChars / 3 && NumBytes % 4 == 0)\n    return 4;\n  if (Nulls >= NumChars / 3)\n    return 2;\n  return 1;\n}\n\nstatic unsigned decodeMultiByteChar(const uint8_t *StringBytes,\n                                    unsigned CharIndex, unsigned CharBytes) {\n  assert(CharBytes == 1 || CharBytes == 2 || CharBytes == 4);\n  unsigned Offset = CharIndex * CharBytes;\n  unsigned Result = 0;\n  StringBytes = StringBytes + Offset;\n  for (unsigned I = 0; I < CharBytes; ++I) {\n    unsigned C = static_cast<unsigned>(StringBytes[I]);\n    Result |= C << (8 * I);\n  }\n  return Result;\n}\n\nFunctionSymbolNode *\nDemangler::demangleVcallThunkNode(std::string_view &MangledName) {\n  FunctionSymbolNode *FSN = Arena.alloc<FunctionSymbolNode>();\n  VcallThunkIdentifierNode *VTIN = Arena.alloc<VcallThunkIdentifierNode>();\n  FSN->Signature = Arena.alloc<ThunkSignatureNode>();\n  FSN->Signature->FunctionClass = FC_NoParameterList;\n\n  FSN->Name = demangleNameScopeChain(MangledName, VTIN);\n  if (!Error)\n    Error = !consumeFront(MangledName, \"$B\");\n  if (!Error)\n    VTIN->OffsetInVTable = demangleUnsigned(MangledName);\n  if (!Error)\n    Error = !consumeFront(MangledName, 'A');\n  if (!Error)\n    FSN->Signature->CallConvention = demangleCallingConvention(MangledName);\n  return (Error) ? nullptr : FSN;\n}\n\nEncodedStringLiteralNode *\nDemangler::demangleStringLiteral(std::string_view &MangledName) {\n  // This function uses goto, so declare all variables up front.\n  OutputBuffer OB;\n  std::string_view CRC;\n  uint64_t StringByteSize;\n  bool IsWcharT = false;\n  bool IsNegative = false;\n  size_t CrcEndPos = 0;\n  char F;\n\n  EncodedStringLiteralNode *Result = Arena.alloc<EncodedStringLiteralNode>();\n\n  // Prefix indicating the beginning of a string literal\n  if (!consumeFront(MangledName, \"@_\"))\n    goto StringLiteralError;\n  if (MangledName.empty())\n    goto StringLiteralError;\n\n  // Char Type (regular or wchar_t)\n  F = MangledName.front();\n  MangledName.remove_prefix(1);\n  switch (F) {\n  case '1':\n    IsWcharT = true;\n    DEMANGLE_FALLTHROUGH;\n  case '0':\n    break;\n  default:\n    goto StringLiteralError;\n  }\n\n  // Encoded Length\n  std::tie(StringByteSize, IsNegative) = demangleNumber(MangledName);\n  if (Error || IsNegative || StringByteSize < (IsWcharT ? 2 : 1))\n    goto StringLiteralError;\n\n  // CRC 32 (always 8 characters plus a terminator)\n  CrcEndPos = MangledName.find('@');\n  if (CrcEndPos == std::string_view::npos)\n    goto StringLiteralError;\n  CRC = MangledName.substr(0, CrcEndPos);\n  MangledName.remove_prefix(CrcEndPos + 1);\n  if (MangledName.empty())\n    goto StringLiteralError;\n\n  if (IsWcharT) {\n    Result->Char = CharKind::Wchar;\n    if (StringByteSize > 64)\n      Result->IsTruncated = true;\n\n    while (!consumeFront(MangledName, '@')) {\n      if (MangledName.size() < 2)\n        goto StringLiteralError;\n      wchar_t W = demangleWcharLiteral(MangledName);\n      if (StringByteSize != 2 || Result->IsTruncated)\n        outputEscapedChar(OB, W);\n      StringByteSize -= 2;\n      if (Error)\n        goto StringLiteralError;\n    }\n  } else {\n    // The max byte length is actually 32, but some compilers mangled strings\n    // incorrectly, so we have to assume it can go higher.\n    constexpr unsigned MaxStringByteLength = 32 * 4;\n    uint8_t StringBytes[MaxStringByteLength];\n\n    unsigned BytesDecoded = 0;\n    while (!consumeFront(MangledName, '@')) {\n      if (MangledName.size() < 1 || BytesDecoded >= MaxStringByteLength)\n        goto StringLiteralError;\n      StringBytes[BytesDecoded++] = demangleCharLiteral(MangledName);\n    }\n\n    if (StringByteSize > BytesDecoded)\n      Result->IsTruncated = true;\n\n    unsigned CharBytes =\n        guessCharByteSize(StringBytes, BytesDecoded, StringByteSize);\n    assert(StringByteSize % CharBytes == 0);\n    switch (CharBytes) {\n    case 1:\n      Result->Char = CharKind::Char;\n      break;\n    case 2:\n      Result->Char = CharKind::Char16;\n      break;\n    case 4:\n      Result->Char = CharKind::Char32;\n      break;\n    default:\n      DEMANGLE_UNREACHABLE;\n    }\n    const unsigned NumChars = BytesDecoded / CharBytes;\n    for (unsigned CharIndex = 0; CharIndex < NumChars; ++CharIndex) {\n      unsigned NextChar =\n          decodeMultiByteChar(StringBytes, CharIndex, CharBytes);\n      if (CharIndex + 1 < NumChars || Result->IsTruncated)\n        outputEscapedChar(OB, NextChar);\n    }\n  }\n\n  Result->DecodedString = copyString(OB);\n  std::free(OB.getBuffer());\n  return Result;\n\nStringLiteralError:\n  Error = true;\n  std::free(OB.getBuffer());\n  return nullptr;\n}\n\n// Returns MangledName's prefix before the first '@', or an error if\n// MangledName contains no '@' or the prefix has length 0.\nstd::string_view Demangler::demangleSimpleString(std::string_view &MangledName,\n                                                 bool Memorize) {\n  std::string_view S;\n  for (size_t i = 0; i < MangledName.size(); ++i) {\n    if (MangledName[i] != '@')\n      continue;\n    if (i == 0)\n      break;\n    S = MangledName.substr(0, i);\n    MangledName.remove_prefix(i + 1);\n\n    if (Memorize)\n      memorizeString(S);\n    return S;\n  }\n\n  Error = true;\n  return {};\n}\n\nNamedIdentifierNode *\nDemangler::demangleAnonymousNamespaceName(std::string_view &MangledName) {\n  assert(llvm::itanium_demangle::starts_with(MangledName, \"?A\"));\n  consumeFront(MangledName, \"?A\");\n\n  NamedIdentifierNode *Node = Arena.alloc<NamedIdentifierNode>();\n  Node->Name = \"`anonymous namespace'\";\n  size_t EndPos = MangledName.find('@');\n  if (EndPos == std::string_view::npos) {\n    Error = true;\n    return nullptr;\n  }\n  std::string_view NamespaceKey = MangledName.substr(0, EndPos);\n  memorizeString(NamespaceKey);\n  MangledName = MangledName.substr(EndPos + 1);\n  return Node;\n}\n\nNamedIdentifierNode *\nDemangler::demangleLocallyScopedNamePiece(std::string_view &MangledName) {\n  assert(startsWithLocalScopePattern(MangledName));\n\n  NamedIdentifierNode *Identifier = Arena.alloc<NamedIdentifierNode>();\n  consumeFront(MangledName, '?');\n  uint64_t Number = 0;\n  bool IsNegative = false;\n  std::tie(Number, IsNegative) = demangleNumber(MangledName);\n  assert(!IsNegative);\n\n  // One ? to terminate the number\n  consumeFront(MangledName, '?');\n\n  assert(!Error);\n  Node *Scope = parse(MangledName);\n  if (Error)\n    return nullptr;\n\n  // Render the parent symbol's name into a buffer.\n  OutputBuffer OB;\n  OB << '`';\n  Scope->output(OB, OF_Default);\n  OB << '\\'';\n  OB << \"::`\" << Number << \"'\";\n\n  Identifier->Name = copyString(OB);\n  std::free(OB.getBuffer());\n  return Identifier;\n}\n\n// Parses a type name in the form of A@B@C@@ which represents C::B::A.\nQualifiedNameNode *\nDemangler::demangleFullyQualifiedTypeName(std::string_view &MangledName) {\n  IdentifierNode *Identifier =\n      demangleUnqualifiedTypeName(MangledName, /*Memorize=*/true);\n  if (Error)\n    return nullptr;\n  assert(Identifier);\n\n  QualifiedNameNode *QN = demangleNameScopeChain(MangledName, Identifier);\n  if (Error)\n    return nullptr;\n  assert(QN);\n  return QN;\n}\n\n// Parses a symbol name in the form of A@B@C@@ which represents C::B::A.\n// Symbol names have slightly different rules regarding what can appear\n// so we separate out the implementations for flexibility.\nQualifiedNameNode *\nDemangler::demangleFullyQualifiedSymbolName(std::string_view &MangledName) {\n  // This is the final component of a symbol name (i.e. the leftmost component\n  // of a mangled name.  Since the only possible template instantiation that\n  // can appear in this context is a function template, and since those are\n  // not saved for the purposes of name backreferences, only backref simple\n  // names.\n  IdentifierNode *Identifier =\n      demangleUnqualifiedSymbolName(MangledName, NBB_Simple);\n  if (Error)\n    return nullptr;\n\n  QualifiedNameNode *QN = demangleNameScopeChain(MangledName, Identifier);\n  if (Error)\n    return nullptr;\n\n  if (Identifier->kind() == NodeKind::StructorIdentifier) {\n    if (QN->Components->Count < 2) {\n      Error = true;\n      return nullptr;\n    }\n    StructorIdentifierNode *SIN =\n        static_cast<StructorIdentifierNode *>(Identifier);\n    Node *ClassNode = QN->Components->Nodes[QN->Components->Count - 2];\n    SIN->Class = static_cast<IdentifierNode *>(ClassNode);\n  }\n  assert(QN);\n  return QN;\n}\n\nIdentifierNode *\nDemangler::demangleUnqualifiedTypeName(std::string_view &MangledName,\n                                       bool Memorize) {\n  // An inner-most name can be a back-reference, because a fully-qualified name\n  // (e.g. Scope + Inner) can contain other fully qualified names inside of\n  // them (for example template parameters), and these nested parameters can\n  // refer to previously mangled types.\n  if (startsWithDigit(MangledName))\n    return demangleBackRefName(MangledName);\n\n  if (llvm::itanium_demangle::starts_with(MangledName, \"?$\"))\n    return demangleTemplateInstantiationName(MangledName, NBB_Template);\n\n  return demangleSimpleName(MangledName, Memorize);\n}\n\nIdentifierNode *\nDemangler::demangleUnqualifiedSymbolName(std::string_view &MangledName,\n                                         NameBackrefBehavior NBB) {\n  if (startsWithDigit(MangledName))\n    return demangleBackRefName(MangledName);\n  if (llvm::itanium_demangle::starts_with(MangledName, \"?$\"))\n    return demangleTemplateInstantiationName(MangledName, NBB);\n  if (llvm::itanium_demangle::starts_with(MangledName, '?'))\n    return demangleFunctionIdentifierCode(MangledName);\n  return demangleSimpleName(MangledName, /*Memorize=*/(NBB & NBB_Simple) != 0);\n}\n\nIdentifierNode *\nDemangler::demangleNameScopePiece(std::string_view &MangledName) {\n  if (startsWithDigit(MangledName))\n    return demangleBackRefName(MangledName);\n\n  if (llvm::itanium_demangle::starts_with(MangledName, \"?$\"))\n    return demangleTemplateInstantiationName(MangledName, NBB_Template);\n\n  if (llvm::itanium_demangle::starts_with(MangledName, \"?A\"))\n    return demangleAnonymousNamespaceName(MangledName);\n\n  if (startsWithLocalScopePattern(MangledName))\n    return demangleLocallyScopedNamePiece(MangledName);\n\n  return demangleSimpleName(MangledName, /*Memorize=*/true);\n}\n\nstatic NodeArrayNode *nodeListToNodeArray(ArenaAllocator &Arena, NodeList *Head,\n                                          size_t Count) {\n  NodeArrayNode *N = Arena.alloc<NodeArrayNode>();\n  N->Count = Count;\n  N->Nodes = Arena.allocArray<Node *>(Count);\n  for (size_t I = 0; I < Count; ++I) {\n    N->Nodes[I] = Head->N;\n    Head = Head->Next;\n  }\n  return N;\n}\n\nQualifiedNameNode *\nDemangler::demangleNameScopeChain(std::string_view &MangledName,\n                                  IdentifierNode *UnqualifiedName) {\n  NodeList *Head = Arena.alloc<NodeList>();\n\n  Head->N = UnqualifiedName;\n\n  size_t Count = 1;\n  while (!consumeFront(MangledName, \"@\")) {\n    ++Count;\n    NodeList *NewHead = Arena.alloc<NodeList>();\n    NewHead->Next = Head;\n    Head = NewHead;\n\n    if (MangledName.empty()) {\n      Error = true;\n      return nullptr;\n    }\n\n    assert(!Error);\n    IdentifierNode *Elem = demangleNameScopePiece(MangledName);\n    if (Error)\n      return nullptr;\n\n    Head->N = Elem;\n  }\n\n  QualifiedNameNode *QN = Arena.alloc<QualifiedNameNode>();\n  QN->Components = nodeListToNodeArray(Arena, Head, Count);\n  return QN;\n}\n\nFuncClass Demangler::demangleFunctionClass(std::string_view &MangledName) {\n  const char F = MangledName.front();\n  MangledName.remove_prefix(1);\n  switch (F) {\n  case '9':\n    return FuncClass(FC_ExternC | FC_NoParameterList);\n  case 'A':\n    return FC_Private;\n  case 'B':\n    return FuncClass(FC_Private | FC_Far);\n  case 'C':\n    return FuncClass(FC_Private | FC_Static);\n  case 'D':\n    return FuncClass(FC_Private | FC_Static | FC_Far);\n  case 'E':\n    return FuncClass(FC_Private | FC_Virtual);\n  case 'F':\n    return FuncClass(FC_Private | FC_Virtual | FC_Far);\n  case 'G':\n    return FuncClass(FC_Private | FC_StaticThisAdjust);\n  case 'H':\n    return FuncClass(FC_Private | FC_StaticThisAdjust | FC_Far);\n  case 'I':\n    return FuncClass(FC_Protected);\n  case 'J':\n    return FuncClass(FC_Protected | FC_Far);\n  case 'K':\n    return FuncClass(FC_Protected | FC_Static);\n  case 'L':\n    return FuncClass(FC_Protected | FC_Static | FC_Far);\n  case 'M':\n    return FuncClass(FC_Protected | FC_Virtual);\n  case 'N':\n    return FuncClass(FC_Protected | FC_Virtual | FC_Far);\n  case 'O':\n    return FuncClass(FC_Protected | FC_Virtual | FC_StaticThisAdjust);\n  case 'P':\n    return FuncClass(FC_Protected | FC_Virtual | FC_StaticThisAdjust | FC_Far);\n  case 'Q':\n    return FuncClass(FC_Public);\n  case 'R':\n    return FuncClass(FC_Public | FC_Far);\n  case 'S':\n    return FuncClass(FC_Public | FC_Static);\n  case 'T':\n    return FuncClass(FC_Public | FC_Static | FC_Far);\n  case 'U':\n    return FuncClass(FC_Public | FC_Virtual);\n  case 'V':\n    return FuncClass(FC_Public | FC_Virtual | FC_Far);\n  case 'W':\n    return FuncClass(FC_Public | FC_Virtual | FC_StaticThisAdjust);\n  case 'X':\n    return FuncClass(FC_Public | FC_Virtual | FC_StaticThisAdjust | FC_Far);\n  case 'Y':\n    return FuncClass(FC_Global);\n  case 'Z':\n    return FuncClass(FC_Global | FC_Far);\n  case '$': {\n    FuncClass VFlag = FC_VirtualThisAdjust;\n    if (consumeFront(MangledName, 'R'))\n      VFlag = FuncClass(VFlag | FC_VirtualThisAdjustEx);\n    if (MangledName.empty())\n      break;\n    const char F = MangledName.front();\n    MangledName.remove_prefix(1);\n    switch (F) {\n    case '0':\n      return FuncClass(FC_Private | FC_Virtual | VFlag);\n    case '1':\n      return FuncClass(FC_Private | FC_Virtual | VFlag | FC_Far);\n    case '2':\n      return FuncClass(FC_Protected | FC_Virtual | VFlag);\n    case '3':\n      return FuncClass(FC_Protected | FC_Virtual | VFlag | FC_Far);\n    case '4':\n      return FuncClass(FC_Public | FC_Virtual | VFlag);\n    case '5':\n      return FuncClass(FC_Public | FC_Virtual | VFlag | FC_Far);\n    }\n  }\n  }\n\n  Error = true;\n  return FC_Public;\n}\n\nCallingConv\nDemangler::demangleCallingConvention(std::string_view &MangledName) {\n  if (MangledName.empty()) {\n    Error = true;\n    return CallingConv::None;\n  }\n\n  const char F = MangledName.front();\n  MangledName.remove_prefix(1);\n  switch (F) {\n  case 'A':\n  case 'B':\n    return CallingConv::Cdecl;\n  case 'C':\n  case 'D':\n    return CallingConv::Pascal;\n  case 'E':\n  case 'F':\n    return CallingConv::Thiscall;\n  case 'G':\n  case 'H':\n    return CallingConv::Stdcall;\n  case 'I':\n  case 'J':\n    return CallingConv::Fastcall;\n  case 'M':\n  case 'N':\n    return CallingConv::Clrcall;\n  case 'O':\n  case 'P':\n    return CallingConv::Eabi;\n  case 'Q':\n    return CallingConv::Vectorcall;\n  case 'S':\n    return CallingConv::Swift;\n  case 'W':\n    return CallingConv::SwiftAsync;\n  }\n\n  return CallingConv::None;\n}\n\nStorageClass\nDemangler::demangleVariableStorageClass(std::string_view &MangledName) {\n  assert(MangledName.front() >= '0' && MangledName.front() <= '4');\n\n  const char F = MangledName.front();\n  MangledName.remove_prefix(1);\n  switch (F) {\n  case '0':\n    return StorageClass::PrivateStatic;\n  case '1':\n    return StorageClass::ProtectedStatic;\n  case '2':\n    return StorageClass::PublicStatic;\n  case '3':\n    return StorageClass::Global;\n  case '4':\n    return StorageClass::FunctionLocalStatic;\n  }\n  DEMANGLE_UNREACHABLE;\n}\n\nstd::pair<Qualifiers, bool>\nDemangler::demangleQualifiers(std::string_view &MangledName) {\n  if (MangledName.empty()) {\n    Error = true;\n    return std::make_pair(Q_None, false);\n  }\n\n  const char F = MangledName.front();\n  MangledName.remove_prefix(1);\n  switch (F) {\n  // Member qualifiers\n  case 'Q':\n    return std::make_pair(Q_None, true);\n  case 'R':\n    return std::make_pair(Q_Const, true);\n  case 'S':\n    return std::make_pair(Q_Volatile, true);\n  case 'T':\n    return std::make_pair(Qualifiers(Q_Const | Q_Volatile), true);\n  // Non-Member qualifiers\n  case 'A':\n    return std::make_pair(Q_None, false);\n  case 'B':\n    return std::make_pair(Q_Const, false);\n  case 'C':\n    return std::make_pair(Q_Volatile, false);\n  case 'D':\n    return std::make_pair(Qualifiers(Q_Const | Q_Volatile), false);\n  }\n  Error = true;\n  return std::make_pair(Q_None, false);\n}\n\n// <variable-type> ::= <type> <cvr-qualifiers>\n//                 ::= <type> <pointee-cvr-qualifiers> # pointers, references\nTypeNode *Demangler::demangleType(std::string_view &MangledName,\n                                  QualifierMangleMode QMM) {\n  Qualifiers Quals = Q_None;\n  bool IsMember = false;\n  if (QMM == QualifierMangleMode::Mangle) {\n    std::tie(Quals, IsMember) = demangleQualifiers(MangledName);\n  } else if (QMM == QualifierMangleMode::Result) {\n    if (consumeFront(MangledName, '?'))\n      std::tie(Quals, IsMember) = demangleQualifiers(MangledName);\n  }\n\n  if (MangledName.empty()) {\n    Error = true;\n    return nullptr;\n  }\n\n  TypeNode *Ty = nullptr;\n  if (isTagType(MangledName))\n    Ty = demangleClassType(MangledName);\n  else if (isPointerType(MangledName)) {\n    if (isMemberPointer(MangledName, Error))\n      Ty = demangleMemberPointerType(MangledName);\n    else if (!Error)\n      Ty = demanglePointerType(MangledName);\n    else\n      return nullptr;\n  } else if (isArrayType(MangledName))\n    Ty = demangleArrayType(MangledName);\n  else if (isFunctionType(MangledName)) {\n    if (consumeFront(MangledName, \"$$A8@@\"))\n      Ty = demangleFunctionType(MangledName, true);\n    else {\n      assert(llvm::itanium_demangle::starts_with(MangledName, \"$$A6\"));\n      consumeFront(MangledName, \"$$A6\");\n      Ty = demangleFunctionType(MangledName, false);\n    }\n  } else if (isCustomType(MangledName)) {\n    Ty = demangleCustomType(MangledName);\n  } else {\n    Ty = demanglePrimitiveType(MangledName);\n  }\n\n  if (!Ty || Error)\n    return Ty;\n  Ty->Quals = Qualifiers(Ty->Quals | Quals);\n  return Ty;\n}\n\nbool Demangler::demangleThrowSpecification(std::string_view &MangledName) {\n  if (consumeFront(MangledName, \"_E\"))\n    return true;\n  if (consumeFront(MangledName, 'Z'))\n    return false;\n\n  Error = true;\n  return false;\n}\n\nFunctionSignatureNode *\nDemangler::demangleFunctionType(std::string_view &MangledName,\n                                bool HasThisQuals) {\n  FunctionSignatureNode *FTy = Arena.alloc<FunctionSignatureNode>();\n\n  if (HasThisQuals) {\n    FTy->Quals = demanglePointerExtQualifiers(MangledName);\n    FTy->RefQualifier = demangleFunctionRefQualifier(MangledName);\n    FTy->Quals = Qualifiers(FTy->Quals | demangleQualifiers(MangledName).first);\n  }\n\n  // Fields that appear on both member and non-member functions.\n  FTy->CallConvention = demangleCallingConvention(MangledName);\n\n  // <return-type> ::= <type>\n  //               ::= @ # structors (they have no declared return type)\n  bool IsStructor = consumeFront(MangledName, '@');\n  if (!IsStructor)\n    FTy->ReturnType = demangleType(MangledName, QualifierMangleMode::Result);\n\n  FTy->Params = demangleFunctionParameterList(MangledName, FTy->IsVariadic);\n\n  FTy->IsNoexcept = demangleThrowSpecification(MangledName);\n\n  return FTy;\n}\n\nFunctionSymbolNode *\nDemangler::demangleFunctionEncoding(std::string_view &MangledName) {\n  FuncClass ExtraFlags = FC_None;\n  if (consumeFront(MangledName, \"$$J0\"))\n    ExtraFlags = FC_ExternC;\n\n  if (MangledName.empty()) {\n    Error = true;\n    return nullptr;\n  }\n\n  FuncClass FC = demangleFunctionClass(MangledName);\n  FC = FuncClass(ExtraFlags | FC);\n\n  FunctionSignatureNode *FSN = nullptr;\n  ThunkSignatureNode *TTN = nullptr;\n  if (FC & FC_StaticThisAdjust) {\n    TTN = Arena.alloc<ThunkSignatureNode>();\n    TTN->ThisAdjust.StaticOffset = demangleSigned(MangledName);\n  } else if (FC & FC_VirtualThisAdjust) {\n    TTN = Arena.alloc<ThunkSignatureNode>();\n    if (FC & FC_VirtualThisAdjustEx) {\n      TTN->ThisAdjust.VBPtrOffset = demangleSigned(MangledName);\n      TTN->ThisAdjust.VBOffsetOffset = demangleSigned(MangledName);\n    }\n    TTN->ThisAdjust.VtordispOffset = demangleSigned(MangledName);\n    TTN->ThisAdjust.StaticOffset = demangleSigned(MangledName);\n  }\n\n  if (FC & FC_NoParameterList) {\n    // This is an extern \"C\" function whose full signature hasn't been mangled.\n    // This happens when we need to mangle a local symbol inside of an extern\n    // \"C\" function.\n    FSN = Arena.alloc<FunctionSignatureNode>();\n  } else {\n    bool HasThisQuals = !(FC & (FC_Global | FC_Static));\n    FSN = demangleFunctionType(MangledName, HasThisQuals);\n  }\n\n  if (Error)\n    return nullptr;\n\n  if (TTN) {\n    *static_cast<FunctionSignatureNode *>(TTN) = *FSN;\n    FSN = TTN;\n  }\n  FSN->FunctionClass = FC;\n\n  FunctionSymbolNode *Symbol = Arena.alloc<FunctionSymbolNode>();\n  Symbol->Signature = FSN;\n  return Symbol;\n}\n\nCustomTypeNode *Demangler::demangleCustomType(std::string_view &MangledName) {\n  assert(llvm::itanium_demangle::starts_with(MangledName, '?'));\n  MangledName.remove_prefix(1);\n\n  CustomTypeNode *CTN = Arena.alloc<CustomTypeNode>();\n  CTN->Identifier = demangleUnqualifiedTypeName(MangledName, /*Memorize=*/true);\n  if (!consumeFront(MangledName, '@'))\n    Error = true;\n  if (Error)\n    return nullptr;\n  return CTN;\n}\n\n// Reads a primitive type.\nPrimitiveTypeNode *\nDemangler::demanglePrimitiveType(std::string_view &MangledName) {\n  if (consumeFront(MangledName, \"$$T\"))\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Nullptr);\n\n  const char F = MangledName.front();\n  MangledName.remove_prefix(1);\n  switch (F) {\n  case 'X':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Void);\n  case 'D':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char);\n  case 'C':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Schar);\n  case 'E':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uchar);\n  case 'F':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Short);\n  case 'G':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ushort);\n  case 'H':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Int);\n  case 'I':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uint);\n  case 'J':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Long);\n  case 'K':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ulong);\n  case 'M':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Float);\n  case 'N':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Double);\n  case 'O':\n    return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ldouble);\n  case '_': {\n    if (MangledName.empty()) {\n      Error = true;\n      return nullptr;\n    }\n    const char F = MangledName.front();\n    MangledName.remove_prefix(1);\n    switch (F) {\n    case 'N':\n      return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Bool);\n    case 'J':\n      return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Int64);\n    case 'K':\n      return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uint64);\n    case 'W':\n      return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Wchar);\n    case 'Q':\n      return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char8);\n    case 'S':\n      return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char16);\n    case 'U':\n      return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char32);\n    }\n    break;\n  }\n  }\n  Error = true;\n  return nullptr;\n}\n\nTagTypeNode *Demangler::demangleClassType(std::string_view &MangledName) {\n  TagTypeNode *TT = nullptr;\n\n  const char F = MangledName.front();\n  MangledName.remove_prefix(1);\n  switch (F) {\n  case 'T':\n    TT = Arena.alloc<TagTypeNode>(TagKind::Union);\n    break;\n  case 'U':\n    TT = Arena.alloc<TagTypeNode>(TagKind::Struct);\n    break;\n  case 'V':\n    TT = Arena.alloc<TagTypeNode>(TagKind::Class);\n    break;\n  case 'W':\n    if (!consumeFront(MangledName, '4')) {\n      Error = true;\n      return nullptr;\n    }\n    TT = Arena.alloc<TagTypeNode>(TagKind::Enum);\n    break;\n  default:\n    assert(false);\n  }\n\n  TT->QualifiedName = demangleFullyQualifiedTypeName(MangledName);\n  return TT;\n}\n\n// <pointer-type> ::= E? <pointer-cvr-qualifiers> <ext-qualifiers> <type>\n//                       # the E is required for 64-bit non-static pointers\nPointerTypeNode *Demangler::demanglePointerType(std::string_view &MangledName) {\n  PointerTypeNode *Pointer = Arena.alloc<PointerTypeNode>();\n\n  std::tie(Pointer->Quals, Pointer->Affinity) =\n      demanglePointerCVQualifiers(MangledName);\n\n  if (consumeFront(MangledName, \"6\")) {\n    Pointer->Pointee = demangleFunctionType(MangledName, false);\n    return Pointer;\n  }\n\n  Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName);\n  Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);\n\n  Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Mangle);\n  return Pointer;\n}\n\nPointerTypeNode *\nDemangler::demangleMemberPointerType(std::string_view &MangledName) {\n  PointerTypeNode *Pointer = Arena.alloc<PointerTypeNode>();\n\n  std::tie(Pointer->Quals, Pointer->Affinity) =\n      demanglePointerCVQualifiers(MangledName);\n  assert(Pointer->Affinity == PointerAffinity::Pointer);\n\n  Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName);\n  Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);\n\n  // isMemberPointer() only returns true if there is at least one character\n  // after the qualifiers.\n  if (consumeFront(MangledName, \"8\")) {\n    Pointer->ClassParent = demangleFullyQualifiedTypeName(MangledName);\n    Pointer->Pointee = demangleFunctionType(MangledName, true);\n  } else {\n    Qualifiers PointeeQuals = Q_None;\n    bool IsMember = false;\n    std::tie(PointeeQuals, IsMember) = demangleQualifiers(MangledName);\n    assert(IsMember || Error);\n    Pointer->ClassParent = demangleFullyQualifiedTypeName(MangledName);\n\n    Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Drop);\n    if (Pointer->Pointee)\n      Pointer->Pointee->Quals = PointeeQuals;\n  }\n\n  return Pointer;\n}\n\nQualifiers\nDemangler::demanglePointerExtQualifiers(std::string_view &MangledName) {\n  Qualifiers Quals = Q_None;\n  if (consumeFront(MangledName, 'E'))\n    Quals = Qualifiers(Quals | Q_Pointer64);\n  if (consumeFront(MangledName, 'I'))\n    Quals = Qualifiers(Quals | Q_Restrict);\n  if (consumeFront(MangledName, 'F'))\n    Quals = Qualifiers(Quals | Q_Unaligned);\n\n  return Quals;\n}\n\nArrayTypeNode *Demangler::demangleArrayType(std::string_view &MangledName) {\n  assert(MangledName.front() == 'Y');\n  MangledName.remove_prefix(1);\n\n  uint64_t Rank = 0;\n  bool IsNegative = false;\n  std::tie(Rank, IsNegative) = demangleNumber(MangledName);\n  if (IsNegative || Rank == 0) {\n    Error = true;\n    return nullptr;\n  }\n\n  ArrayTypeNode *ATy = Arena.alloc<ArrayTypeNode>();\n  NodeList *Head = Arena.alloc<NodeList>();\n  NodeList *Tail = Head;\n\n  for (uint64_t I = 0; I < Rank; ++I) {\n    uint64_t D = 0;\n    std::tie(D, IsNegative) = demangleNumber(MangledName);\n    if (Error || IsNegative) {\n      Error = true;\n      return nullptr;\n    }\n    Tail->N = Arena.alloc<IntegerLiteralNode>(D, IsNegative);\n    if (I + 1 < Rank) {\n      Tail->Next = Arena.alloc<NodeList>();\n      Tail = Tail->Next;\n    }\n  }\n  ATy->Dimensions = nodeListToNodeArray(Arena, Head, Rank);\n\n  if (consumeFront(MangledName, \"$$C\")) {\n    bool IsMember = false;\n    std::tie(ATy->Quals, IsMember) = demangleQualifiers(MangledName);\n    if (IsMember) {\n      Error = true;\n      return nullptr;\n    }\n  }\n\n  ATy->ElementType = demangleType(MangledName, QualifierMangleMode::Drop);\n  return ATy;\n}\n\n// Reads a function's parameters.\nNodeArrayNode *\nDemangler::demangleFunctionParameterList(std::string_view &MangledName,\n                                         bool &IsVariadic) {\n  // Empty parameter list.\n  if (consumeFront(MangledName, 'X'))\n    return nullptr;\n\n  NodeList *Head = Arena.alloc<NodeList>();\n  NodeList **Current = &Head;\n  size_t Count = 0;\n  while (!Error && !llvm::itanium_demangle::starts_with(MangledName, '@') &&\n         !llvm::itanium_demangle::starts_with(MangledName, 'Z')) {\n    ++Count;\n\n    if (startsWithDigit(MangledName)) {\n      size_t N = MangledName[0] - '0';\n      if (N >= Backrefs.FunctionParamCount) {\n        Error = true;\n        return nullptr;\n      }\n      MangledName.remove_prefix(1);\n\n      *Current = Arena.alloc<NodeList>();\n      (*Current)->N = Backrefs.FunctionParams[N];\n      Current = &(*Current)->Next;\n      continue;\n    }\n\n    size_t OldSize = MangledName.size();\n\n    *Current = Arena.alloc<NodeList>();\n    TypeNode *TN = demangleType(MangledName, QualifierMangleMode::Drop);\n    if (!TN || Error)\n      return nullptr;\n\n    (*Current)->N = TN;\n\n    size_t CharsConsumed = OldSize - MangledName.size();\n    assert(CharsConsumed != 0);\n\n    // Single-letter types are ignored for backreferences because memorizing\n    // them doesn't save anything.\n    if (Backrefs.FunctionParamCount <= 9 && CharsConsumed > 1)\n      Backrefs.FunctionParams[Backrefs.FunctionParamCount++] = TN;\n\n    Current = &(*Current)->Next;\n  }\n\n  if (Error)\n    return nullptr;\n\n  NodeArrayNode *NA = nodeListToNodeArray(Arena, Head, Count);\n  // A non-empty parameter list is terminated by either 'Z' (variadic) parameter\n  // list or '@' (non variadic).  Careful not to consume \"@Z\", as in that case\n  // the following Z could be a throw specifier.\n  if (consumeFront(MangledName, '@'))\n    return NA;\n\n  if (consumeFront(MangledName, 'Z')) {\n    IsVariadic = true;\n    return NA;\n  }\n\n  DEMANGLE_UNREACHABLE;\n}\n\nNodeArrayNode *\nDemangler::demangleTemplateParameterList(std::string_view &MangledName) {\n  NodeList *Head = nullptr;\n  NodeList **Current = &Head;\n  size_t Count = 0;\n\n  while (!llvm::itanium_demangle::starts_with(MangledName, '@')) {\n    if (consumeFront(MangledName, \"$S\") || consumeFront(MangledName, \"$$V\") ||\n        consumeFront(MangledName, \"$$$V\") || consumeFront(MangledName, \"$$Z\")) {\n      // parameter pack separator\n      continue;\n    }\n\n    ++Count;\n\n    // Template parameter lists don't participate in back-referencing.\n    *Current = Arena.alloc<NodeList>();\n\n    NodeList &TP = **Current;\n\n    // <auto-nttp> ::= $ M <type> <nttp>\n    const bool IsAutoNTTP = consumeFront(MangledName, \"$M\");\n    if (IsAutoNTTP) {\n      // The deduced type of the auto NTTP parameter isn't printed so\n      // we want to ignore the AST created from demangling the type.\n      //\n      // TODO: Avoid the extra allocations to the bump allocator in this case.\n      (void)demangleType(MangledName, QualifierMangleMode::Drop);\n      if (Error)\n        return nullptr;\n    }\n\n    TemplateParameterReferenceNode *TPRN = nullptr;\n    if (consumeFront(MangledName, \"$$Y\")) {\n      // Template alias\n      TP.N = demangleFullyQualifiedTypeName(MangledName);\n    } else if (consumeFront(MangledName, \"$$B\")) {\n      // Array\n      TP.N = demangleType(MangledName, QualifierMangleMode::Drop);\n    } else if (consumeFront(MangledName, \"$$C\")) {\n      // Type has qualifiers.\n      TP.N = demangleType(MangledName, QualifierMangleMode::Mangle);\n    } else if (startsWith(MangledName, \"$1\", \"1\", !IsAutoNTTP) ||\n               startsWith(MangledName, \"$H\", \"H\", !IsAutoNTTP) ||\n               startsWith(MangledName, \"$I\", \"I\", !IsAutoNTTP) ||\n               startsWith(MangledName, \"$J\", \"J\", !IsAutoNTTP)) {\n      // Pointer to member\n      TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();\n      TPRN->IsMemberPointer = true;\n\n      if (!IsAutoNTTP)\n        MangledName.remove_prefix(1); // Remove leading '$'\n\n      // 1 - single inheritance       <name>\n      // H - multiple inheritance     <name> <number>\n      // I - virtual inheritance      <name> <number> <number>\n      // J - unspecified inheritance  <name> <number> <number> <number>\n      char InheritanceSpecifier = MangledName.front();\n      MangledName.remove_prefix(1);\n      SymbolNode *S = nullptr;\n      if (llvm::itanium_demangle::starts_with(MangledName, '?')) {\n        S = parse(MangledName);\n        if (Error || !S->Name) {\n          Error = true;\n          return nullptr;\n        }\n        memorizeIdentifier(S->Name->getUnqualifiedIdentifier());\n      }\n\n      switch (InheritanceSpecifier) {\n      case 'J':\n        TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =\n            demangleSigned(MangledName);\n        DEMANGLE_FALLTHROUGH;\n      case 'I':\n        TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =\n            demangleSigned(MangledName);\n        DEMANGLE_FALLTHROUGH;\n      case 'H':\n        TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =\n            demangleSigned(MangledName);\n        DEMANGLE_FALLTHROUGH;\n      case '1':\n        break;\n      default:\n        DEMANGLE_UNREACHABLE;\n      }\n      TPRN->Affinity = PointerAffinity::Pointer;\n      TPRN->Symbol = S;\n    } else if (llvm::itanium_demangle::starts_with(MangledName, \"$E?\")) {\n      consumeFront(MangledName, \"$E\");\n      // Reference to symbol\n      TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();\n      TPRN->Symbol = parse(MangledName);\n      TPRN->Affinity = PointerAffinity::Reference;\n    } else if (startsWith(MangledName, \"$F\", \"F\", !IsAutoNTTP) ||\n               startsWith(MangledName, \"$G\", \"G\", !IsAutoNTTP)) {\n      TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();\n\n      // Data member pointer.\n      if (!IsAutoNTTP)\n        MangledName.remove_prefix(1); // Remove leading '$'\n      char InheritanceSpecifier = MangledName.front();\n      MangledName.remove_prefix(1);\n\n      switch (InheritanceSpecifier) {\n      case 'G':\n        TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =\n            demangleSigned(MangledName);\n        DEMANGLE_FALLTHROUGH;\n      case 'F':\n        TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =\n            demangleSigned(MangledName);\n        TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =\n            demangleSigned(MangledName);\n        break;\n      default:\n        DEMANGLE_UNREACHABLE;\n      }\n      TPRN->IsMemberPointer = true;\n\n    } else if (consumeFront(MangledName, \"$0\", \"0\", !IsAutoNTTP)) {\n      // Integral non-type template parameter\n      bool IsNegative = false;\n      uint64_t Value = 0;\n      std::tie(Value, IsNegative) = demangleNumber(MangledName);\n\n      TP.N = Arena.alloc<IntegerLiteralNode>(Value, IsNegative);\n    } else {\n      TP.N = demangleType(MangledName, QualifierMangleMode::Drop);\n    }\n    if (Error)\n      return nullptr;\n\n    Current = &TP.Next;\n  }\n\n  // The loop above returns nullptr on Error.\n  assert(!Error);\n\n  // Template parameter lists cannot be variadic, so it can only be terminated\n  // by @ (as opposed to 'Z' in the function parameter case).\n  assert(llvm::itanium_demangle::starts_with(\n      MangledName, '@')); // The above loop exits only on '@'.\n  consumeFront(MangledName, '@');\n  return nodeListToNodeArray(Arena, Head, Count);\n}\n\nvoid Demangler::dumpBackReferences() {\n  std::printf(\"%d function parameter backreferences\\n\",\n              (int)Backrefs.FunctionParamCount);\n\n  // Create an output stream so we can render each type.\n  OutputBuffer OB;\n  for (size_t I = 0; I < Backrefs.FunctionParamCount; ++I) {\n    OB.setCurrentPosition(0);\n\n    TypeNode *T = Backrefs.FunctionParams[I];\n    T->output(OB, OF_Default);\n\n    std::string_view B = OB;\n    std::printf(\"  [%d] - %.*s\\n\", (int)I, (int)B.size(), B.data());\n  }\n  std::free(OB.getBuffer());\n\n  if (Backrefs.FunctionParamCount > 0)\n    std::printf(\"\\n\");\n  std::printf(\"%d name backreferences\\n\", (int)Backrefs.NamesCount);\n  for (size_t I = 0; I < Backrefs.NamesCount; ++I) {\n    std::printf(\"  [%d] - %.*s\\n\", (int)I, (int)Backrefs.Names[I]->Name.size(),\n                Backrefs.Names[I]->Name.data());\n  }\n  if (Backrefs.NamesCount > 0)\n    std::printf(\"\\n\");\n}\n\nstd::optional<size_t>\nllvm::getArm64ECInsertionPointInMangledName(std::string_view MangledName) {\n  std::string_view ProcessedName{MangledName};\n\n  // We only support this for MSVC-style C++ symbols.\n  if (!consumeFront(ProcessedName, '?'))\n    return std::nullopt;\n\n  // The insertion point is just after the name of the symbol, so parse that to\n  // remove it from the processed name.\n  Demangler D;\n  D.demangleFullyQualifiedSymbolName(ProcessedName);\n  if (D.Error)\n    return std::nullopt;\n\n  return MangledName.length() - ProcessedName.length();\n}\n\nchar *llvm::microsoftDemangle(std::string_view MangledName, size_t *NMangled,\n                              int *Status, MSDemangleFlags Flags) {\n  Demangler D;\n\n  std::string_view Name{MangledName};\n  SymbolNode *AST = D.parse(Name);\n  if (!D.Error && NMangled)\n    *NMangled = MangledName.size() - Name.size();\n\n  if (Flags & MSDF_DumpBackrefs)\n    D.dumpBackReferences();\n\n  OutputFlags OF = OF_Default;\n  if (Flags & MSDF_NoCallingConvention)\n    OF = OutputFlags(OF | OF_NoCallingConvention);\n  if (Flags & MSDF_NoAccessSpecifier)\n    OF = OutputFlags(OF | OF_NoAccessSpecifier);\n  if (Flags & MSDF_NoReturnType)\n    OF = OutputFlags(OF | OF_NoReturnType);\n  if (Flags & MSDF_NoMemberType)\n    OF = OutputFlags(OF | OF_NoMemberType);\n  if (Flags & MSDF_NoVariableType)\n    OF = OutputFlags(OF | OF_NoVariableType);\n\n  int InternalStatus = demangle_success;\n  char *Buf;\n  if (D.Error)\n    InternalStatus = demangle_invalid_mangled_name;\n  else {\n    OutputBuffer OB;\n    AST->output(OB, OF);\n    OB += '\\0';\n    Buf = OB.getBuffer();\n  }\n\n  if (Status)\n    *Status = InternalStatus;\n  return InternalStatus == demangle_success ? Buf : nullptr;\n}\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/source/MicrosoftDemangleNodes.cpp",
    "content": "//===- MicrosoftDemangle.cpp ----------------------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file defines a demangler for MSVC-style mangled symbols.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"llvm/Demangle/MicrosoftDemangleNodes.h\"\n#include \"llvm/Demangle/Utility.h\"\n#include <cctype>\n#include <string>\n\nusing namespace llvm;\nusing namespace ms_demangle;\n\n#define OUTPUT_ENUM_CLASS_VALUE(Enum, Value, Desc)                             \\\n  case Enum::Value:                                                            \\\n    OB << Desc;                                                                \\\n    break;\n\n// Writes a space if the last token does not end with a punctuation.\nstatic void outputSpaceIfNecessary(OutputBuffer &OB) {\n  if (OB.empty())\n    return;\n\n  char C = OB.back();\n  if (std::isalnum(C) || C == '>')\n    OB << \" \";\n}\n\nstatic void outputSingleQualifier(OutputBuffer &OB, Qualifiers Q) {\n  switch (Q) {\n  case Q_Const:\n    OB << \"const\";\n    break;\n  case Q_Volatile:\n    OB << \"volatile\";\n    break;\n  case Q_Restrict:\n    OB << \"__restrict\";\n    break;\n  default:\n    break;\n  }\n}\n\nstatic bool outputQualifierIfPresent(OutputBuffer &OB, Qualifiers Q,\n                                     Qualifiers Mask, bool NeedSpace) {\n  if (!(Q & Mask))\n    return NeedSpace;\n\n  if (NeedSpace)\n    OB << \" \";\n\n  outputSingleQualifier(OB, Mask);\n  return true;\n}\n\nstatic void outputQualifiers(OutputBuffer &OB, Qualifiers Q, bool SpaceBefore,\n                             bool SpaceAfter) {\n  if (Q == Q_None)\n    return;\n\n  size_t Pos1 = OB.getCurrentPosition();\n  SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Const, SpaceBefore);\n  SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Volatile, SpaceBefore);\n  SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Restrict, SpaceBefore);\n  size_t Pos2 = OB.getCurrentPosition();\n  if (SpaceAfter && Pos2 > Pos1)\n    OB << \" \";\n}\n\nstatic void outputCallingConvention(OutputBuffer &OB, CallingConv CC) {\n  outputSpaceIfNecessary(OB);\n\n  switch (CC) {\n  case CallingConv::Cdecl:\n    OB << \"__cdecl\";\n    break;\n  case CallingConv::Fastcall:\n    OB << \"__fastcall\";\n    break;\n  case CallingConv::Pascal:\n    OB << \"__pascal\";\n    break;\n  case CallingConv::Regcall:\n    OB << \"__regcall\";\n    break;\n  case CallingConv::Stdcall:\n    OB << \"__stdcall\";\n    break;\n  case CallingConv::Thiscall:\n    OB << \"__thiscall\";\n    break;\n  case CallingConv::Eabi:\n    OB << \"__eabi\";\n    break;\n  case CallingConv::Vectorcall:\n    OB << \"__vectorcall\";\n    break;\n  case CallingConv::Clrcall:\n    OB << \"__clrcall\";\n    break;\n  case CallingConv::Swift:\n    OB << \"__attribute__((__swiftcall__)) \";\n    break;\n  case CallingConv::SwiftAsync:\n    OB << \"__attribute__((__swiftasynccall__)) \";\n    break;\n  default:\n    break;\n  }\n}\n\nstd::string Node::toString(OutputFlags Flags) const {\n  OutputBuffer OB;\n  this->output(OB, Flags);\n  std::string_view SV = OB;\n  std::string Owned(SV.begin(), SV.end());\n  std::free(OB.getBuffer());\n  return Owned;\n}\n\nvoid PrimitiveTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {\n  switch (PrimKind) {\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Void, \"void\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Bool, \"bool\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char, \"char\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Schar, \"signed char\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uchar, \"unsigned char\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char8, \"char8_t\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char16, \"char16_t\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Char32, \"char32_t\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Short, \"short\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ushort, \"unsigned short\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int, \"int\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint, \"unsigned int\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Long, \"long\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ulong, \"unsigned long\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Int64, \"__int64\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Uint64, \"unsigned __int64\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Wchar, \"wchar_t\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Float, \"float\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Double, \"double\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ldouble, \"long double\");\n    OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Nullptr, \"std::nullptr_t\");\n  }\n  outputQualifiers(OB, Quals, true, false);\n}\n\nvoid NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags) const {\n  output(OB, Flags, \", \");\n}\n\nvoid NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags,\n                           std::string_view Separator) const {\n  if (Count == 0)\n    return;\n  if (Nodes[0])\n    Nodes[0]->output(OB, Flags);\n  for (size_t I = 1; I < Count; ++I) {\n    OB << Separator;\n    Nodes[I]->output(OB, Flags);\n  }\n}\n\nvoid EncodedStringLiteralNode::output(OutputBuffer &OB,\n                                      OutputFlags Flags) const {\n  switch (Char) {\n  case CharKind::Wchar:\n    OB << \"L\\\"\";\n    break;\n  case CharKind::Char:\n    OB << \"\\\"\";\n    break;\n  case CharKind::Char16:\n    OB << \"u\\\"\";\n    break;\n  case CharKind::Char32:\n    OB << \"U\\\"\";\n    break;\n  }\n  OB << DecodedString << \"\\\"\";\n  if (IsTruncated)\n    OB << \"...\";\n}\n\nvoid IntegerLiteralNode::output(OutputBuffer &OB, OutputFlags Flags) const {\n  if (IsNegative)\n    OB << '-';\n  OB << Value;\n}\n\nvoid TemplateParameterReferenceNode::output(OutputBuffer &OB,\n                                            OutputFlags Flags) const {\n  if (ThunkOffsetCount > 0)\n    OB << \"{\";\n  else if (Affinity == PointerAffinity::Pointer)\n    OB << \"&\";\n\n  if (Symbol) {\n    Symbol->output(OB, Flags);\n    if (ThunkOffsetCount > 0)\n      OB << \", \";\n  }\n\n  if (ThunkOffsetCount > 0)\n    OB << ThunkOffsets[0];\n  for (int I = 1; I < ThunkOffsetCount; ++I) {\n    OB << \", \" << ThunkOffsets[I];\n  }\n  if (ThunkOffsetCount > 0)\n    OB << \"}\";\n}\n\nvoid IdentifierNode::outputTemplateParameters(OutputBuffer &OB,\n                                              OutputFlags Flags) const {\n  if (!TemplateParams)\n    return;\n  OB << \"<\";\n  TemplateParams->output(OB, Flags);\n  OB << \">\";\n}\n\nvoid DynamicStructorIdentifierNode::output(OutputBuffer &OB,\n                                           OutputFlags Flags) const {\n  if (IsDestructor)\n    OB << \"`dynamic atexit destructor for \";\n  else\n    OB << \"`dynamic initializer for \";\n\n  if (Variable) {\n    OB << \"`\";\n    Variable->output(OB, Flags);\n    OB << \"''\";\n  } else {\n    OB << \"'\";\n    Name->output(OB, Flags);\n    OB << \"''\";\n  }\n}\n\nvoid NamedIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const {\n  OB << Name;\n  outputTemplateParameters(OB, Flags);\n}\n\nvoid IntrinsicFunctionIdentifierNode::output(OutputBuffer &OB,\n                                             OutputFlags Flags) const {\n  switch (Operator) {\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, New, \"operator new\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Delete, \"operator delete\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Assign, \"operator=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RightShift, \"operator>>\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LeftShift, \"operator<<\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalNot, \"operator!\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Equals, \"operator==\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, NotEquals, \"operator!=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArraySubscript,\n                            \"operator[]\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Pointer, \"operator->\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Increment, \"operator++\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Decrement, \"operator--\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Minus, \"operator-\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Plus, \"operator+\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Dereference, \"operator*\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAnd, \"operator&\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MemberPointer,\n                            \"operator->*\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Divide, \"operator/\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Modulus, \"operator%\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThan, \"operator<\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LessThanEqual, \"operator<=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThan, \"operator>\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, GreaterThanEqual,\n                            \"operator>=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Comma, \"operator,\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Parens, \"operator()\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseNot, \"operator~\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXor, \"operator^\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOr, \"operator|\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalAnd, \"operator&&\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LogicalOr, \"operator||\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, TimesEqual, \"operator*=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, PlusEqual, \"operator+=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, MinusEqual, \"operator-=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DivEqual, \"operator/=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ModEqual, \"operator%=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, RshEqual, \"operator>>=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LshEqual, \"operator<<=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseAndEqual,\n                            \"operator&=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseOrEqual,\n                            \"operator|=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, BitwiseXorEqual,\n                            \"operator^=\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VbaseDtor, \"`vbase dtor'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDelDtor,\n                            \"`vector deleting dtor'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, DefaultCtorClosure,\n                            \"`default ctor closure'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ScalarDelDtor,\n                            \"`scalar deleting dtor'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecCtorIter,\n                            \"`vector ctor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecDtorIter,\n                            \"`vector dtor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VecVbaseCtorIter,\n                            \"`vector vbase ctor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VdispMap,\n                            \"`virtual displacement map'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecCtorIter,\n                            \"`eh vector ctor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecDtorIter,\n                            \"`eh vector dtor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVecVbaseCtorIter,\n                            \"`eh vector vbase ctor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CopyCtorClosure,\n                            \"`copy ctor closure'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, LocalVftableCtorClosure,\n                            \"`local vftable ctor closure'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayNew, \"operator new[]\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ArrayDelete,\n                            \"operator delete[]\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorCtorIter,\n                            \"`managed vector ctor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorDtorIter,\n                            \"`managed vector dtor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorCopyCtorIter,\n                            \"`EH vector copy ctor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, EHVectorVbaseCopyCtorIter,\n                            \"`EH vector vbase copy ctor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorCopyCtorIter,\n                            \"`vector copy ctor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, VectorVbaseCopyCtorIter,\n                            \"`vector vbase copy constructor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, ManVectorVbaseCopyCtorIter,\n                            \"`managed vector vbase copy constructor iterator'\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, CoAwait,\n                            \"operator co_await\");\n    OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, Spaceship, \"operator<=>\");\n  case IntrinsicFunctionKind::MaxIntrinsic:\n  case IntrinsicFunctionKind::None:\n    break;\n  }\n  outputTemplateParameters(OB, Flags);\n}\n\nvoid LocalStaticGuardIdentifierNode::output(OutputBuffer &OB,\n                                            OutputFlags Flags) const {\n  if (IsThread)\n    OB << \"`local static thread guard'\";\n  else\n    OB << \"`local static guard'\";\n  if (ScopeIndex > 0)\n    OB << \"{\" << ScopeIndex << \"}\";\n}\n\nvoid ConversionOperatorIdentifierNode::output(OutputBuffer &OB,\n                                              OutputFlags Flags) const {\n  OB << \"operator\";\n  outputTemplateParameters(OB, Flags);\n  OB << \" \";\n  TargetType->output(OB, Flags);\n}\n\nvoid StructorIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const {\n  if (IsDestructor)\n    OB << \"~\";\n  Class->output(OB, Flags);\n  outputTemplateParameters(OB, Flags);\n}\n\nvoid LiteralOperatorIdentifierNode::output(OutputBuffer &OB,\n                                           OutputFlags Flags) const {\n  OB << \"operator \\\"\\\"\" << Name;\n  outputTemplateParameters(OB, Flags);\n}\n\nvoid FunctionSignatureNode::outputPre(OutputBuffer &OB,\n                                      OutputFlags Flags) const {\n  if (!(Flags & OF_NoAccessSpecifier)) {\n    if (FunctionClass & FC_Public)\n      OB << \"public: \";\n    if (FunctionClass & FC_Protected)\n      OB << \"protected: \";\n    if (FunctionClass & FC_Private)\n      OB << \"private: \";\n  }\n\n  if (!(Flags & OF_NoMemberType)) {\n    if (!(FunctionClass & FC_Global)) {\n      if (FunctionClass & FC_Static)\n        OB << \"static \";\n    }\n    if (FunctionClass & FC_Virtual)\n      OB << \"virtual \";\n\n    if (FunctionClass & FC_ExternC)\n      OB << \"extern \\\"C\\\" \";\n  }\n\n  if (!(Flags & OF_NoReturnType) && ReturnType) {\n    ReturnType->outputPre(OB, Flags);\n    OB << \" \";\n  }\n\n  if (!(Flags & OF_NoCallingConvention))\n    outputCallingConvention(OB, CallConvention);\n}\n\nvoid FunctionSignatureNode::outputPost(OutputBuffer &OB,\n                                       OutputFlags Flags) const {\n  if (!(FunctionClass & FC_NoParameterList)) {\n    OB << \"(\";\n    if (Params)\n      Params->output(OB, Flags);\n    else\n      OB << \"void\";\n\n    if (IsVariadic) {\n      if (OB.back() != '(')\n        OB << \", \";\n      OB << \"...\";\n    }\n    OB << \")\";\n  }\n\n  if (Quals & Q_Const)\n    OB << \" const\";\n  if (Quals & Q_Volatile)\n    OB << \" volatile\";\n  if (Quals & Q_Restrict)\n    OB << \" __restrict\";\n  if (Quals & Q_Unaligned)\n    OB << \" __unaligned\";\n\n  if (IsNoexcept)\n    OB << \" noexcept\";\n\n  if (RefQualifier == FunctionRefQualifier::Reference)\n    OB << \" &\";\n  else if (RefQualifier == FunctionRefQualifier::RValueReference)\n    OB << \" &&\";\n\n  if (!(Flags & OF_NoReturnType) && ReturnType)\n    ReturnType->outputPost(OB, Flags);\n}\n\nvoid ThunkSignatureNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {\n  OB << \"[thunk]: \";\n\n  FunctionSignatureNode::outputPre(OB, Flags);\n}\n\nvoid ThunkSignatureNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {\n  if (FunctionClass & FC_StaticThisAdjust) {\n    OB << \"`adjustor{\" << ThisAdjust.StaticOffset << \"}'\";\n  } else if (FunctionClass & FC_VirtualThisAdjust) {\n    if (FunctionClass & FC_VirtualThisAdjustEx) {\n      OB << \"`vtordispex{\" << ThisAdjust.VBPtrOffset << \", \"\n         << ThisAdjust.VBOffsetOffset << \", \" << ThisAdjust.VtordispOffset\n         << \", \" << ThisAdjust.StaticOffset << \"}'\";\n    } else {\n      OB << \"`vtordisp{\" << ThisAdjust.VtordispOffset << \", \"\n         << ThisAdjust.StaticOffset << \"}'\";\n    }\n  }\n\n  FunctionSignatureNode::outputPost(OB, Flags);\n}\n\nvoid PointerTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {\n  if (Pointee->kind() == NodeKind::FunctionSignature) {\n    // If this is a pointer to a function, don't output the calling convention.\n    // It needs to go inside the parentheses.\n    const FunctionSignatureNode *Sig =\n        static_cast<const FunctionSignatureNode *>(Pointee);\n    Sig->outputPre(OB, OF_NoCallingConvention);\n  } else\n    Pointee->outputPre(OB, Flags);\n\n  outputSpaceIfNecessary(OB);\n\n  if (Quals & Q_Unaligned)\n    OB << \"__unaligned \";\n\n  if (Pointee->kind() == NodeKind::ArrayType) {\n    OB << \"(\";\n  } else if (Pointee->kind() == NodeKind::FunctionSignature) {\n    OB << \"(\";\n    const FunctionSignatureNode *Sig =\n        static_cast<const FunctionSignatureNode *>(Pointee);\n    outputCallingConvention(OB, Sig->CallConvention);\n    OB << \" \";\n  }\n\n  if (ClassParent) {\n    ClassParent->output(OB, Flags);\n    OB << \"::\";\n  }\n\n  switch (Affinity) {\n  case PointerAffinity::Pointer:\n    OB << \"*\";\n    break;\n  case PointerAffinity::Reference:\n    OB << \"&\";\n    break;\n  case PointerAffinity::RValueReference:\n    OB << \"&&\";\n    break;\n  default:\n    assert(false);\n  }\n  outputQualifiers(OB, Quals, false, false);\n}\n\nvoid PointerTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {\n  if (Pointee->kind() == NodeKind::ArrayType ||\n      Pointee->kind() == NodeKind::FunctionSignature)\n    OB << \")\";\n\n  Pointee->outputPost(OB, Flags);\n}\n\nvoid TagTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {\n  if (!(Flags & OF_NoTagSpecifier)) {\n    switch (Tag) {\n      OUTPUT_ENUM_CLASS_VALUE(TagKind, Class, \"class\");\n      OUTPUT_ENUM_CLASS_VALUE(TagKind, Struct, \"struct\");\n      OUTPUT_ENUM_CLASS_VALUE(TagKind, Union, \"union\");\n      OUTPUT_ENUM_CLASS_VALUE(TagKind, Enum, \"enum\");\n    }\n    OB << \" \";\n  }\n  QualifiedName->output(OB, Flags);\n  outputQualifiers(OB, Quals, true, false);\n}\n\nvoid TagTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {}\n\nvoid ArrayTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {\n  ElementType->outputPre(OB, Flags);\n  outputQualifiers(OB, Quals, true, false);\n}\n\nvoid ArrayTypeNode::outputOneDimension(OutputBuffer &OB, OutputFlags Flags,\n                                       Node *N) const {\n  assert(N->kind() == NodeKind::IntegerLiteral);\n  IntegerLiteralNode *ILN = static_cast<IntegerLiteralNode *>(N);\n  if (ILN->Value != 0)\n    ILN->output(OB, Flags);\n}\n\nvoid ArrayTypeNode::outputDimensionsImpl(OutputBuffer &OB,\n                                         OutputFlags Flags) const {\n  if (Dimensions->Count == 0)\n    return;\n\n  outputOneDimension(OB, Flags, Dimensions->Nodes[0]);\n  for (size_t I = 1; I < Dimensions->Count; ++I) {\n    OB << \"][\";\n    outputOneDimension(OB, Flags, Dimensions->Nodes[I]);\n  }\n}\n\nvoid ArrayTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {\n  OB << \"[\";\n  outputDimensionsImpl(OB, Flags);\n  OB << \"]\";\n\n  ElementType->outputPost(OB, Flags);\n}\n\nvoid SymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {\n  Name->output(OB, Flags);\n}\n\nvoid FunctionSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {\n  Signature->outputPre(OB, Flags);\n  outputSpaceIfNecessary(OB);\n  Name->output(OB, Flags);\n  Signature->outputPost(OB, Flags);\n}\n\nvoid VariableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {\n  const char *AccessSpec = nullptr;\n  bool IsStatic = true;\n  switch (SC) {\n  case StorageClass::PrivateStatic:\n    AccessSpec = \"private\";\n    break;\n  case StorageClass::PublicStatic:\n    AccessSpec = \"public\";\n    break;\n  case StorageClass::ProtectedStatic:\n    AccessSpec = \"protected\";\n    break;\n  default:\n    IsStatic = false;\n    break;\n  }\n  if (!(Flags & OF_NoAccessSpecifier) && AccessSpec)\n    OB << AccessSpec << \": \";\n  if (!(Flags & OF_NoMemberType) && IsStatic)\n    OB << \"static \";\n\n  if (!(Flags & OF_NoVariableType) && Type) {\n    Type->outputPre(OB, Flags);\n    outputSpaceIfNecessary(OB);\n  }\n  Name->output(OB, Flags);\n  if (!(Flags & OF_NoVariableType) && Type)\n    Type->outputPost(OB, Flags);\n}\n\nvoid CustomTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {\n  Identifier->output(OB, Flags);\n}\nvoid CustomTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {}\n\nvoid QualifiedNameNode::output(OutputBuffer &OB, OutputFlags Flags) const {\n  Components->output(OB, Flags, \"::\");\n}\n\nvoid RttiBaseClassDescriptorNode::output(OutputBuffer &OB,\n                                         OutputFlags Flags) const {\n  OB << \"`RTTI Base Class Descriptor at (\";\n  OB << NVOffset << \", \" << VBPtrOffset << \", \" << VBTableOffset << \", \"\n     << this->Flags;\n  OB << \")'\";\n}\n\nvoid LocalStaticGuardVariableNode::output(OutputBuffer &OB,\n                                          OutputFlags Flags) const {\n  Name->output(OB, Flags);\n}\n\nvoid VcallThunkIdentifierNode::output(OutputBuffer &OB,\n                                      OutputFlags Flags) const {\n  OB << \"`vcall'{\" << OffsetInVTable << \", {flat}}\";\n}\n\nvoid SpecialTableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {\n  outputQualifiers(OB, Quals, false, true);\n  Name->output(OB, Flags);\n  if (TargetName) {\n    OB << \"{for `\";\n    TargetName->output(OB, Flags);\n    OB << \"'}\";\n  }\n}\n"
  },
  {
    "path": "lib/third_party/llvm-demangle/source/RustDemangle.cpp",
    "content": "//===--- RustDemangle.cpp ---------------------------------------*- C++ -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file defines a demangler for Rust v0 mangled symbols as specified in\n// https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html\n//\n//===----------------------------------------------------------------------===//\n\n#include \"llvm/Demangle/Demangle.h\"\n#include \"llvm/Demangle/StringViewExtras.h\"\n#include \"llvm/Demangle/Utility.h\"\n\n#include <algorithm>\n#include <cassert>\n#include <cstdint>\n#include <cstring>\n#include <limits>\n#include <string_view>\n\nusing namespace llvm;\n\nusing llvm::itanium_demangle::OutputBuffer;\nusing llvm::itanium_demangle::ScopedOverride;\nusing llvm::itanium_demangle::starts_with;\n\nnamespace {\n\nstruct Identifier {\n  std::string_view Name;\n  bool Punycode;\n\n  bool empty() const { return Name.empty(); }\n};\n\nenum class BasicType {\n  Bool,\n  Char,\n  I8,\n  I16,\n  I32,\n  I64,\n  I128,\n  ISize,\n  U8,\n  U16,\n  U32,\n  U64,\n  U128,\n  USize,\n  F32,\n  F64,\n  Str,\n  Placeholder,\n  Unit,\n  Variadic,\n  Never,\n};\n\nenum class IsInType {\n  No,\n  Yes,\n};\n\nenum class LeaveGenericsOpen {\n  No,\n  Yes,\n};\n\nclass Demangler {\n  // Maximum recursion level. Used to avoid stack overflow.\n  size_t MaxRecursionLevel;\n  // Current recursion level.\n  size_t RecursionLevel;\n  size_t BoundLifetimes;\n  // Input string that is being demangled with \"_R\" prefix removed.\n  std::string_view Input;\n  // Position in the input string.\n  size_t Position;\n  // When true, print methods append the output to the stream.\n  // When false, the output is suppressed.\n  bool Print;\n  // True if an error occurred.\n  bool Error;\n\npublic:\n  // Demangled output.\n  OutputBuffer Output;\n\n  Demangler(size_t MaxRecursionLevel = 500);\n\n  bool demangle(std::string_view MangledName);\n\nprivate:\n  bool demanglePath(IsInType Type,\n                    LeaveGenericsOpen LeaveOpen = LeaveGenericsOpen::No);\n  void demangleImplPath(IsInType InType);\n  void demangleGenericArg();\n  void demangleType();\n  void demangleFnSig();\n  void demangleDynBounds();\n  void demangleDynTrait();\n  void demangleOptionalBinder();\n  void demangleConst();\n  void demangleConstInt();\n  void demangleConstBool();\n  void demangleConstChar();\n\n  template <typename Callable> void demangleBackref(Callable Demangler) {\n    uint64_t Backref = parseBase62Number();\n    if (Error || Backref >= Position) {\n      Error = true;\n      return;\n    }\n\n    if (!Print)\n      return;\n\n    ScopedOverride<size_t> SavePosition(Position, Position);\n    Position = Backref;\n    Demangler();\n  }\n\n  Identifier parseIdentifier();\n  uint64_t parseOptionalBase62Number(char Tag);\n  uint64_t parseBase62Number();\n  uint64_t parseDecimalNumber();\n  uint64_t parseHexNumber(std::string_view &HexDigits);\n\n  void print(char C);\n  void print(std::string_view S);\n  void printDecimalNumber(uint64_t N);\n  void printBasicType(BasicType);\n  void printLifetime(uint64_t Index);\n  void printIdentifier(Identifier Ident);\n\n  char look() const;\n  char consume();\n  bool consumeIf(char Prefix);\n\n  bool addAssign(uint64_t &A, uint64_t B);\n  bool mulAssign(uint64_t &A, uint64_t B);\n};\n\n} // namespace\n\nchar *llvm::rustDemangle(std::string_view MangledName) {\n  // Return early if mangled name doesn't look like a Rust symbol.\n  if (MangledName.empty() || !starts_with(MangledName, \"_R\"))\n    return nullptr;\n\n  Demangler D;\n  if (!D.demangle(MangledName)) {\n    std::free(D.Output.getBuffer());\n    return nullptr;\n  }\n\n  D.Output += '\\0';\n\n  return D.Output.getBuffer();\n}\n\nDemangler::Demangler(size_t MaxRecursionLevel)\n    : MaxRecursionLevel(MaxRecursionLevel) {}\n\nstatic inline bool isDigit(const char C) { return '0' <= C && C <= '9'; }\n\nstatic inline bool isHexDigit(const char C) {\n  return ('0' <= C && C <= '9') || ('a' <= C && C <= 'f');\n}\n\nstatic inline bool isLower(const char C) { return 'a' <= C && C <= 'z'; }\n\nstatic inline bool isUpper(const char C) { return 'A' <= C && C <= 'Z'; }\n\n/// Returns true if C is a valid mangled character: <0-9a-zA-Z_>.\nstatic inline bool isValid(const char C) {\n  return isDigit(C) || isLower(C) || isUpper(C) || C == '_';\n}\n\n// Demangles Rust v0 mangled symbol. Returns true when successful, and false\n// otherwise. The demangled symbol is stored in Output field. It is\n// responsibility of the caller to free the memory behind the output stream.\n//\n// <symbol-name> = \"_R\" <path> [<instantiating-crate>]\nbool Demangler::demangle(std::string_view Mangled) {\n  Position = 0;\n  Error = false;\n  Print = true;\n  RecursionLevel = 0;\n  BoundLifetimes = 0;\n\n  if (!starts_with(Mangled, \"_R\")) {\n    Error = true;\n    return false;\n  }\n  Mangled.remove_prefix(2);\n  size_t Dot = Mangled.find('.');\n  Input = Dot == std::string_view::npos ? Mangled : Mangled.substr(0, Dot);\n\n  demanglePath(IsInType::No);\n\n  if (Position != Input.size()) {\n    ScopedOverride<bool> SavePrint(Print, false);\n    demanglePath(IsInType::No);\n  }\n\n  if (Position != Input.size())\n    Error = true;\n\n  if (Dot != std::string_view::npos) {\n    print(\" (\");\n    print(Mangled.substr(Dot));\n    print(\")\");\n  }\n\n  return !Error;\n}\n\n// Demangles a path. InType indicates whether a path is inside a type. When\n// LeaveOpen is true, a closing `>` after generic arguments is omitted from the\n// output. Return value indicates whether generics arguments have been left\n// open.\n//\n// <path> = \"C\" <identifier>               // crate root\n//        | \"M\" <impl-path> <type>         // <T> (inherent impl)\n//        | \"X\" <impl-path> <type> <path>  // <T as Trait> (trait impl)\n//        | \"Y\" <type> <path>              // <T as Trait> (trait definition)\n//        | \"N\" <ns> <path> <identifier>   // ...::ident (nested path)\n//        | \"I\" <path> {<generic-arg>} \"E\" // ...<T, U> (generic args)\n//        | <backref>\n// <identifier> = [<disambiguator>] <undisambiguated-identifier>\n// <ns> = \"C\"      // closure\n//      | \"S\"      // shim\n//      | <A-Z>    // other special namespaces\n//      | <a-z>    // internal namespaces\nbool Demangler::demanglePath(IsInType InType, LeaveGenericsOpen LeaveOpen) {\n  if (Error || RecursionLevel >= MaxRecursionLevel) {\n    Error = true;\n    return false;\n  }\n  ScopedOverride<size_t> SaveRecursionLevel(RecursionLevel, RecursionLevel + 1);\n\n  switch (consume()) {\n  case 'C': {\n    parseOptionalBase62Number('s');\n    printIdentifier(parseIdentifier());\n    break;\n  }\n  case 'M': {\n    demangleImplPath(InType);\n    print(\"<\");\n    demangleType();\n    print(\">\");\n    break;\n  }\n  case 'X': {\n    demangleImplPath(InType);\n    print(\"<\");\n    demangleType();\n    print(\" as \");\n    demanglePath(IsInType::Yes);\n    print(\">\");\n    break;\n  }\n  case 'Y': {\n    print(\"<\");\n    demangleType();\n    print(\" as \");\n    demanglePath(IsInType::Yes);\n    print(\">\");\n    break;\n  }\n  case 'N': {\n    char NS = consume();\n    if (!isLower(NS) && !isUpper(NS)) {\n      Error = true;\n      break;\n    }\n    demanglePath(InType);\n\n    uint64_t Disambiguator = parseOptionalBase62Number('s');\n    Identifier Ident = parseIdentifier();\n\n    if (isUpper(NS)) {\n      // Special namespaces\n      print(\"::{\");\n      if (NS == 'C')\n        print(\"closure\");\n      else if (NS == 'S')\n        print(\"shim\");\n      else\n        print(NS);\n      if (!Ident.empty()) {\n        print(\":\");\n        printIdentifier(Ident);\n      }\n      print('#');\n      printDecimalNumber(Disambiguator);\n      print('}');\n    } else {\n      // Implementation internal namespaces.\n      if (!Ident.empty()) {\n        print(\"::\");\n        printIdentifier(Ident);\n      }\n    }\n    break;\n  }\n  case 'I': {\n    demanglePath(InType);\n    // Omit \"::\" when in a type, where it is optional.\n    if (InType == IsInType::No)\n      print(\"::\");\n    print(\"<\");\n    for (size_t I = 0; !Error && !consumeIf('E'); ++I) {\n      if (I > 0)\n        print(\", \");\n      demangleGenericArg();\n    }\n    if (LeaveOpen == LeaveGenericsOpen::Yes)\n      return true;\n    else\n      print(\">\");\n    break;\n  }\n  case 'B': {\n    bool IsOpen = false;\n    demangleBackref([&] { IsOpen = demanglePath(InType, LeaveOpen); });\n    return IsOpen;\n  }\n  default:\n    Error = true;\n    break;\n  }\n\n  return false;\n}\n\n// <impl-path> = [<disambiguator>] <path>\n// <disambiguator> = \"s\" <base-62-number>\nvoid Demangler::demangleImplPath(IsInType InType) {\n  ScopedOverride<bool> SavePrint(Print, false);\n  parseOptionalBase62Number('s');\n  demanglePath(InType);\n}\n\n// <generic-arg> = <lifetime>\n//               | <type>\n//               | \"K\" <const>\n// <lifetime> = \"L\" <base-62-number>\nvoid Demangler::demangleGenericArg() {\n  if (consumeIf('L'))\n    printLifetime(parseBase62Number());\n  else if (consumeIf('K'))\n    demangleConst();\n  else\n    demangleType();\n}\n\n// <basic-type> = \"a\"      // i8\n//              | \"b\"      // bool\n//              | \"c\"      // char\n//              | \"d\"      // f64\n//              | \"e\"      // str\n//              | \"f\"      // f32\n//              | \"h\"      // u8\n//              | \"i\"      // isize\n//              | \"j\"      // usize\n//              | \"l\"      // i32\n//              | \"m\"      // u32\n//              | \"n\"      // i128\n//              | \"o\"      // u128\n//              | \"s\"      // i16\n//              | \"t\"      // u16\n//              | \"u\"      // ()\n//              | \"v\"      // ...\n//              | \"x\"      // i64\n//              | \"y\"      // u64\n//              | \"z\"      // !\n//              | \"p\"      // placeholder (e.g. for generic params), shown as _\nstatic bool parseBasicType(char C, BasicType &Type) {\n  switch (C) {\n  case 'a':\n    Type = BasicType::I8;\n    return true;\n  case 'b':\n    Type = BasicType::Bool;\n    return true;\n  case 'c':\n    Type = BasicType::Char;\n    return true;\n  case 'd':\n    Type = BasicType::F64;\n    return true;\n  case 'e':\n    Type = BasicType::Str;\n    return true;\n  case 'f':\n    Type = BasicType::F32;\n    return true;\n  case 'h':\n    Type = BasicType::U8;\n    return true;\n  case 'i':\n    Type = BasicType::ISize;\n    return true;\n  case 'j':\n    Type = BasicType::USize;\n    return true;\n  case 'l':\n    Type = BasicType::I32;\n    return true;\n  case 'm':\n    Type = BasicType::U32;\n    return true;\n  case 'n':\n    Type = BasicType::I128;\n    return true;\n  case 'o':\n    Type = BasicType::U128;\n    return true;\n  case 'p':\n    Type = BasicType::Placeholder;\n    return true;\n  case 's':\n    Type = BasicType::I16;\n    return true;\n  case 't':\n    Type = BasicType::U16;\n    return true;\n  case 'u':\n    Type = BasicType::Unit;\n    return true;\n  case 'v':\n    Type = BasicType::Variadic;\n    return true;\n  case 'x':\n    Type = BasicType::I64;\n    return true;\n  case 'y':\n    Type = BasicType::U64;\n    return true;\n  case 'z':\n    Type = BasicType::Never;\n    return true;\n  default:\n    return false;\n  }\n}\n\nvoid Demangler::printBasicType(BasicType Type) {\n  switch (Type) {\n  case BasicType::Bool:\n    print(\"bool\");\n    break;\n  case BasicType::Char:\n    print(\"char\");\n    break;\n  case BasicType::I8:\n    print(\"i8\");\n    break;\n  case BasicType::I16:\n    print(\"i16\");\n    break;\n  case BasicType::I32:\n    print(\"i32\");\n    break;\n  case BasicType::I64:\n    print(\"i64\");\n    break;\n  case BasicType::I128:\n    print(\"i128\");\n    break;\n  case BasicType::ISize:\n    print(\"isize\");\n    break;\n  case BasicType::U8:\n    print(\"u8\");\n    break;\n  case BasicType::U16:\n    print(\"u16\");\n    break;\n  case BasicType::U32:\n    print(\"u32\");\n    break;\n  case BasicType::U64:\n    print(\"u64\");\n    break;\n  case BasicType::U128:\n    print(\"u128\");\n    break;\n  case BasicType::USize:\n    print(\"usize\");\n    break;\n  case BasicType::F32:\n    print(\"f32\");\n    break;\n  case BasicType::F64:\n    print(\"f64\");\n    break;\n  case BasicType::Str:\n    print(\"str\");\n    break;\n  case BasicType::Placeholder:\n    print(\"_\");\n    break;\n  case BasicType::Unit:\n    print(\"()\");\n    break;\n  case BasicType::Variadic:\n    print(\"...\");\n    break;\n  case BasicType::Never:\n    print(\"!\");\n    break;\n  }\n}\n\n// <type> = | <basic-type>\n//          | <path>                      // named type\n//          | \"A\" <type> <const>          // [T; N]\n//          | \"S\" <type>                  // [T]\n//          | \"T\" {<type>} \"E\"            // (T1, T2, T3, ...)\n//          | \"R\" [<lifetime>] <type>     // &T\n//          | \"Q\" [<lifetime>] <type>     // &mut T\n//          | \"P\" <type>                  // *const T\n//          | \"O\" <type>                  // *mut T\n//          | \"F\" <fn-sig>                // fn(...) -> ...\n//          | \"D\" <dyn-bounds> <lifetime> // dyn Trait<Assoc = X> + Send + 'a\n//          | <backref>                   // backref\nvoid Demangler::demangleType() {\n  if (Error || RecursionLevel >= MaxRecursionLevel) {\n    Error = true;\n    return;\n  }\n  ScopedOverride<size_t> SaveRecursionLevel(RecursionLevel, RecursionLevel + 1);\n\n  size_t Start = Position;\n  char C = consume();\n  BasicType Type;\n  if (parseBasicType(C, Type))\n    return printBasicType(Type);\n\n  switch (C) {\n  case 'A':\n    print(\"[\");\n    demangleType();\n    print(\"; \");\n    demangleConst();\n    print(\"]\");\n    break;\n  case 'S':\n    print(\"[\");\n    demangleType();\n    print(\"]\");\n    break;\n  case 'T': {\n    print(\"(\");\n    size_t I = 0;\n    for (; !Error && !consumeIf('E'); ++I) {\n      if (I > 0)\n        print(\", \");\n      demangleType();\n    }\n    if (I == 1)\n      print(\",\");\n    print(\")\");\n    break;\n  }\n  case 'R':\n  case 'Q':\n    print('&');\n    if (consumeIf('L')) {\n      if (auto Lifetime = parseBase62Number()) {\n        printLifetime(Lifetime);\n        print(' ');\n      }\n    }\n    if (C == 'Q')\n      print(\"mut \");\n    demangleType();\n    break;\n  case 'P':\n    print(\"*const \");\n    demangleType();\n    break;\n  case 'O':\n    print(\"*mut \");\n    demangleType();\n    break;\n  case 'F':\n    demangleFnSig();\n    break;\n  case 'D':\n    demangleDynBounds();\n    if (consumeIf('L')) {\n      if (auto Lifetime = parseBase62Number()) {\n        print(\" + \");\n        printLifetime(Lifetime);\n      }\n    } else {\n      Error = true;\n    }\n    break;\n  case 'B':\n    demangleBackref([&] { demangleType(); });\n    break;\n  default:\n    Position = Start;\n    demanglePath(IsInType::Yes);\n    break;\n  }\n}\n\n// <fn-sig> := [<binder>] [\"U\"] [\"K\" <abi>] {<type>} \"E\" <type>\n// <abi> = \"C\"\n//       | <undisambiguated-identifier>\nvoid Demangler::demangleFnSig() {\n  ScopedOverride<size_t> SaveBoundLifetimes(BoundLifetimes, BoundLifetimes);\n  demangleOptionalBinder();\n\n  if (consumeIf('U'))\n    print(\"unsafe \");\n\n  if (consumeIf('K')) {\n    print(\"extern \\\"\");\n    if (consumeIf('C')) {\n      print(\"C\");\n    } else {\n      Identifier Ident = parseIdentifier();\n      if (Ident.Punycode)\n        Error = true;\n      for (char C : Ident.Name) {\n        // When mangling ABI string, the \"-\" is replaced with \"_\".\n        if (C == '_')\n          C = '-';\n        print(C);\n      }\n    }\n    print(\"\\\" \");\n  }\n\n  print(\"fn(\");\n  for (size_t I = 0; !Error && !consumeIf('E'); ++I) {\n    if (I > 0)\n      print(\", \");\n    demangleType();\n  }\n  print(\")\");\n\n  if (consumeIf('u')) {\n    // Skip the unit type from the output.\n  } else {\n    print(\" -> \");\n    demangleType();\n  }\n}\n\n// <dyn-bounds> = [<binder>] {<dyn-trait>} \"E\"\nvoid Demangler::demangleDynBounds() {\n  ScopedOverride<size_t> SaveBoundLifetimes(BoundLifetimes, BoundLifetimes);\n  print(\"dyn \");\n  demangleOptionalBinder();\n  for (size_t I = 0; !Error && !consumeIf('E'); ++I) {\n    if (I > 0)\n      print(\" + \");\n    demangleDynTrait();\n  }\n}\n\n// <dyn-trait> = <path> {<dyn-trait-assoc-binding>}\n// <dyn-trait-assoc-binding> = \"p\" <undisambiguated-identifier> <type>\nvoid Demangler::demangleDynTrait() {\n  bool IsOpen = demanglePath(IsInType::Yes, LeaveGenericsOpen::Yes);\n  while (!Error && consumeIf('p')) {\n    if (!IsOpen) {\n      IsOpen = true;\n      print('<');\n    } else {\n      print(\", \");\n    }\n    print(parseIdentifier().Name);\n    print(\" = \");\n    demangleType();\n  }\n  if (IsOpen)\n    print(\">\");\n}\n\n// Demangles optional binder and updates the number of bound lifetimes.\n//\n// <binder> = \"G\" <base-62-number>\nvoid Demangler::demangleOptionalBinder() {\n  uint64_t Binder = parseOptionalBase62Number('G');\n  if (Error || Binder == 0)\n    return;\n\n  // In valid inputs each bound lifetime is referenced later. Referencing a\n  // lifetime requires at least one byte of input. Reject inputs that are too\n  // short to reference all bound lifetimes. Otherwise demangling of invalid\n  // binders could generate excessive amounts of output.\n  if (Binder >= Input.size() - BoundLifetimes) {\n    Error = true;\n    return;\n  }\n\n  print(\"for<\");\n  for (size_t I = 0; I != Binder; ++I) {\n    BoundLifetimes += 1;\n    if (I > 0)\n      print(\", \");\n    printLifetime(1);\n  }\n  print(\"> \");\n}\n\n// <const> = <basic-type> <const-data>\n//         | \"p\"                          // placeholder\n//         | <backref>\nvoid Demangler::demangleConst() {\n  if (Error || RecursionLevel >= MaxRecursionLevel) {\n    Error = true;\n    return;\n  }\n  ScopedOverride<size_t> SaveRecursionLevel(RecursionLevel, RecursionLevel + 1);\n\n  char C = consume();\n  BasicType Type;\n  if (parseBasicType(C, Type)) {\n    switch (Type) {\n    case BasicType::I8:\n    case BasicType::I16:\n    case BasicType::I32:\n    case BasicType::I64:\n    case BasicType::I128:\n    case BasicType::ISize:\n    case BasicType::U8:\n    case BasicType::U16:\n    case BasicType::U32:\n    case BasicType::U64:\n    case BasicType::U128:\n    case BasicType::USize:\n      demangleConstInt();\n      break;\n    case BasicType::Bool:\n      demangleConstBool();\n      break;\n    case BasicType::Char:\n      demangleConstChar();\n      break;\n    case BasicType::Placeholder:\n      print('_');\n      break;\n    default:\n      Error = true;\n      break;\n    }\n  } else if (C == 'B') {\n    demangleBackref([&] { demangleConst(); });\n  } else {\n    Error = true;\n  }\n}\n\n// <const-data> = [\"n\"] <hex-number>\nvoid Demangler::demangleConstInt() {\n  if (consumeIf('n'))\n    print('-');\n\n  std::string_view HexDigits;\n  uint64_t Value = parseHexNumber(HexDigits);\n  if (HexDigits.size() <= 16) {\n    printDecimalNumber(Value);\n  } else {\n    print(\"0x\");\n    print(HexDigits);\n  }\n}\n\n// <const-data> = \"0_\" // false\n//              | \"1_\" // true\nvoid Demangler::demangleConstBool() {\n  std::string_view HexDigits;\n  parseHexNumber(HexDigits);\n  if (HexDigits == \"0\")\n    print(\"false\");\n  else if (HexDigits == \"1\")\n    print(\"true\");\n  else\n    Error = true;\n}\n\n/// Returns true if CodePoint represents a printable ASCII character.\nstatic bool isAsciiPrintable(uint64_t CodePoint) {\n  return 0x20 <= CodePoint && CodePoint <= 0x7e;\n}\n\n// <const-data> = <hex-number>\nvoid Demangler::demangleConstChar() {\n  std::string_view HexDigits;\n  uint64_t CodePoint = parseHexNumber(HexDigits);\n  if (Error || HexDigits.size() > 6) {\n    Error = true;\n    return;\n  }\n\n  print(\"'\");\n  switch (CodePoint) {\n  case '\\t':\n    print(R\"(\\t)\");\n    break;\n  case '\\r':\n    print(R\"(\\r)\");\n    break;\n  case '\\n':\n    print(R\"(\\n)\");\n    break;\n  case '\\\\':\n    print(R\"(\\\\)\");\n    break;\n  case '\"':\n    print(R\"(\")\");\n    break;\n  case '\\'':\n    print(R\"(\\')\");\n    break;\n  default:\n    if (isAsciiPrintable(CodePoint)) {\n      char C = CodePoint;\n      print(C);\n    } else {\n      print(R\"(\\u{)\");\n      print(HexDigits);\n      print('}');\n    }\n    break;\n  }\n  print('\\'');\n}\n\n// <undisambiguated-identifier> = [\"u\"] <decimal-number> [\"_\"] <bytes>\nIdentifier Demangler::parseIdentifier() {\n  bool Punycode = consumeIf('u');\n  uint64_t Bytes = parseDecimalNumber();\n\n  // Underscore resolves the ambiguity when identifier starts with a decimal\n  // digit or another underscore.\n  consumeIf('_');\n\n  if (Error || Bytes > Input.size() - Position) {\n    Error = true;\n    return {};\n  }\n  std::string_view S = Input.substr(Position, Bytes);\n  Position += Bytes;\n\n  if (!std::all_of(S.begin(), S.end(), isValid)) {\n    Error = true;\n    return {};\n  }\n\n  return {S, Punycode};\n}\n\n// Parses optional base 62 number. The presence of a number is determined using\n// Tag. Returns 0 when tag is absent and parsed value + 1 otherwise\n//\n// This function is indended for parsing disambiguators and binders which when\n// not present have their value interpreted as 0, and otherwise as decoded\n// value + 1. For example for binders, value for \"G_\" is 1, for \"G0_\" value is\n// 2. When \"G\" is absent value is 0.\nuint64_t Demangler::parseOptionalBase62Number(char Tag) {\n  if (!consumeIf(Tag))\n    return 0;\n\n  uint64_t N = parseBase62Number();\n  if (Error || !addAssign(N, 1))\n    return 0;\n\n  return N;\n}\n\n// Parses base 62 number with <0-9a-zA-Z> as digits. Number is terminated by\n// \"_\". All values are offset by 1, so that \"_\" encodes 0, \"0_\" encodes 1,\n// \"1_\" encodes 2, etc.\n//\n// <base-62-number> = {<0-9a-zA-Z>} \"_\"\nuint64_t Demangler::parseBase62Number() {\n  if (consumeIf('_'))\n    return 0;\n\n  uint64_t Value = 0;\n\n  while (true) {\n    uint64_t Digit;\n    char C = consume();\n\n    if (C == '_') {\n      break;\n    } else if (isDigit(C)) {\n      Digit = C - '0';\n    } else if (isLower(C)) {\n      Digit = 10 + (C - 'a');\n    } else if (isUpper(C)) {\n      Digit = 10 + 26 + (C - 'A');\n    } else {\n      Error = true;\n      return 0;\n    }\n\n    if (!mulAssign(Value, 62))\n      return 0;\n\n    if (!addAssign(Value, Digit))\n      return 0;\n  }\n\n  if (!addAssign(Value, 1))\n    return 0;\n\n  return Value;\n}\n\n// Parses a decimal number that had been encoded without any leading zeros.\n//\n// <decimal-number> = \"0\"\n//                  | <1-9> {<0-9>}\nuint64_t Demangler::parseDecimalNumber() {\n  char C = look();\n  if (!isDigit(C)) {\n    Error = true;\n    return 0;\n  }\n\n  if (C == '0') {\n    consume();\n    return 0;\n  }\n\n  uint64_t Value = 0;\n\n  while (isDigit(look())) {\n    if (!mulAssign(Value, 10)) {\n      Error = true;\n      return 0;\n    }\n\n    uint64_t D = consume() - '0';\n    if (!addAssign(Value, D))\n      return 0;\n  }\n\n  return Value;\n}\n\n// Parses a hexadecimal number with <0-9a-f> as a digits. Returns the parsed\n// value and stores hex digits in HexDigits. The return value is unspecified if\n// HexDigits.size() > 16.\n//\n// <hex-number> = \"0_\"\n//              | <1-9a-f> {<0-9a-f>} \"_\"\nuint64_t Demangler::parseHexNumber(std::string_view &HexDigits) {\n  size_t Start = Position;\n  uint64_t Value = 0;\n\n  if (!isHexDigit(look()))\n    Error = true;\n\n  if (consumeIf('0')) {\n    if (!consumeIf('_'))\n      Error = true;\n  } else {\n    while (!Error && !consumeIf('_')) {\n      char C = consume();\n      Value *= 16;\n      if (isDigit(C))\n        Value += C - '0';\n      else if ('a' <= C && C <= 'f')\n        Value += 10 + (C - 'a');\n      else\n        Error = true;\n    }\n  }\n\n  if (Error) {\n    HexDigits = std::string_view();\n    return 0;\n  }\n\n  size_t End = Position - 1;\n  assert(Start < End);\n  HexDigits = Input.substr(Start, End - Start);\n  return Value;\n}\n\nvoid Demangler::print(char C) {\n  if (Error || !Print)\n    return;\n\n  Output += C;\n}\n\nvoid Demangler::print(std::string_view S) {\n  if (Error || !Print)\n    return;\n\n  Output += S;\n}\n\nvoid Demangler::printDecimalNumber(uint64_t N) {\n  if (Error || !Print)\n    return;\n\n  Output << N;\n}\n\n// Prints a lifetime. An index 0 always represents an erased lifetime. Indices\n// starting from 1, are De Bruijn indices, referring to higher-ranked lifetimes\n// bound by one of the enclosing binders.\nvoid Demangler::printLifetime(uint64_t Index) {\n  if (Index == 0) {\n    print(\"'_\");\n    return;\n  }\n\n  if (Index - 1 >= BoundLifetimes) {\n    Error = true;\n    return;\n  }\n\n  uint64_t Depth = BoundLifetimes - Index;\n  print('\\'');\n  if (Depth < 26) {\n    char C = 'a' + Depth;\n    print(C);\n  } else {\n    print('z');\n    printDecimalNumber(Depth - 26 + 1);\n  }\n}\n\nstatic inline bool decodePunycodeDigit(char C, size_t &Value) {\n  if (isLower(C)) {\n    Value = C - 'a';\n    return true;\n  }\n\n  if (isDigit(C)) {\n    Value = 26 + (C - '0');\n    return true;\n  }\n\n  return false;\n}\n\nstatic void removeNullBytes(OutputBuffer &Output, size_t StartIdx) {\n  char *Buffer = Output.getBuffer();\n  char *Start = Buffer + StartIdx;\n  char *End = Buffer + Output.getCurrentPosition();\n  Output.setCurrentPosition(std::remove(Start, End, '\\0') - Buffer);\n}\n\n// Encodes code point as UTF-8 and stores results in Output. Returns false if\n// CodePoint is not a valid unicode scalar value.\nstatic inline bool encodeUTF8(size_t CodePoint, char *Output) {\n  if (0xD800 <= CodePoint && CodePoint <= 0xDFFF)\n    return false;\n\n  if (CodePoint <= 0x7F) {\n    Output[0] = CodePoint;\n    return true;\n  }\n\n  if (CodePoint <= 0x7FF) {\n    Output[0] = 0xC0 | ((CodePoint >> 6) & 0x3F);\n    Output[1] = 0x80 | (CodePoint & 0x3F);\n    return true;\n  }\n\n  if (CodePoint <= 0xFFFF) {\n    Output[0] = 0xE0 | (CodePoint >> 12);\n    Output[1] = 0x80 | ((CodePoint >> 6) & 0x3F);\n    Output[2] = 0x80 | (CodePoint & 0x3F);\n    return true;\n  }\n\n  if (CodePoint <= 0x10FFFF) {\n    Output[0] = 0xF0 | (CodePoint >> 18);\n    Output[1] = 0x80 | ((CodePoint >> 12) & 0x3F);\n    Output[2] = 0x80 | ((CodePoint >> 6) & 0x3F);\n    Output[3] = 0x80 | (CodePoint & 0x3F);\n    return true;\n  }\n\n  return false;\n}\n\n// Decodes string encoded using punycode and appends results to Output.\n// Returns true if decoding was successful.\nstatic bool decodePunycode(std::string_view Input, OutputBuffer &Output) {\n  size_t OutputSize = Output.getCurrentPosition();\n  size_t InputIdx = 0;\n\n  // Rust uses an underscore as a delimiter.\n  size_t DelimiterPos = std::string_view::npos;\n  for (size_t I = 0; I != Input.size(); ++I)\n    if (Input[I] == '_')\n      DelimiterPos = I;\n\n  if (DelimiterPos != std::string_view::npos) {\n    // Copy basic code points before the last delimiter to the output.\n    for (; InputIdx != DelimiterPos; ++InputIdx) {\n      char C = Input[InputIdx];\n      if (!isValid(C))\n        return false;\n      // Code points are padded with zeros while decoding is in progress.\n      char UTF8[4] = {C};\n      Output += std::string_view(UTF8, 4);\n    }\n    // Skip over the delimiter.\n    ++InputIdx;\n  }\n\n  size_t Base = 36;\n  size_t Skew = 38;\n  size_t Bias = 72;\n  size_t N = 0x80;\n  size_t TMin = 1;\n  size_t TMax = 26;\n  size_t Damp = 700;\n\n  auto Adapt = [&](size_t Delta, size_t NumPoints) {\n    Delta /= Damp;\n    Delta += Delta / NumPoints;\n    Damp = 2;\n\n    size_t K = 0;\n    while (Delta > (Base - TMin) * TMax / 2) {\n      Delta /= Base - TMin;\n      K += Base;\n    }\n    return K + (((Base - TMin + 1) * Delta) / (Delta + Skew));\n  };\n\n  // Main decoding loop.\n  for (size_t I = 0; InputIdx != Input.size(); I += 1) {\n    size_t OldI = I;\n    size_t W = 1;\n    size_t Max = std::numeric_limits<size_t>::max();\n    for (size_t K = Base; true; K += Base) {\n      if (InputIdx == Input.size())\n        return false;\n      char C = Input[InputIdx++];\n      size_t Digit = 0;\n      if (!decodePunycodeDigit(C, Digit))\n        return false;\n\n      if (Digit > (Max - I) / W)\n        return false;\n      I += Digit * W;\n\n      size_t T;\n      if (K <= Bias)\n        T = TMin;\n      else if (K >= Bias + TMax)\n        T = TMax;\n      else\n        T = K - Bias;\n\n      if (Digit < T)\n        break;\n\n      if (W > Max / (Base - T))\n        return false;\n      W *= (Base - T);\n    }\n    size_t NumPoints = (Output.getCurrentPosition() - OutputSize) / 4 + 1;\n    Bias = Adapt(I - OldI, NumPoints);\n\n    if (I / NumPoints > Max - N)\n      return false;\n    N += I / NumPoints;\n    I = I % NumPoints;\n\n    // Insert N at position I in the output.\n    char UTF8[4] = {};\n    if (!encodeUTF8(N, UTF8))\n      return false;\n    Output.insert(OutputSize + I * 4, UTF8, 4);\n  }\n\n  removeNullBytes(Output, OutputSize);\n  return true;\n}\n\nvoid Demangler::printIdentifier(Identifier Ident) {\n  if (Error || !Print)\n    return;\n\n  if (Ident.Punycode) {\n    if (!decodePunycode(Ident.Name, Output))\n      Error = true;\n  } else {\n    print(Ident.Name);\n  }\n}\n\nchar Demangler::look() const {\n  if (Error || Position >= Input.size())\n    return 0;\n\n  return Input[Position];\n}\n\nchar Demangler::consume() {\n  if (Error || Position >= Input.size()) {\n    Error = true;\n    return 0;\n  }\n\n  return Input[Position++];\n}\n\nbool Demangler::consumeIf(char Prefix) {\n  if (Error || Position >= Input.size() || Input[Position] != Prefix)\n    return false;\n\n  Position += 1;\n  return true;\n}\n\n/// Computes A + B. When computation wraps around sets the error and returns\n/// false. Otherwise assigns the result to A and returns true.\nbool Demangler::addAssign(uint64_t &A, uint64_t B) {\n  if (A > std::numeric_limits<uint64_t>::max() - B) {\n    Error = true;\n    return false;\n  }\n\n  A += B;\n  return true;\n}\n\n/// Computes A * B. When computation wraps around sets the error and returns\n/// false. Otherwise assigns the result to A and returns true.\nbool Demangler::mulAssign(uint64_t &A, uint64_t B) {\n  if (B != 0 && A > std::numeric_limits<uint64_t>::max() / B) {\n    Error = true;\n    return false;\n  }\n\n  A *= B;\n  return true;\n}\n"
  },
  {
    "path": "lib/third_party/microtar/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\nproject(microtar)\n\nadd_library(microtar STATIC\n        source/microtar.c\n)\n\ntarget_include_directories(microtar PUBLIC include)\n"
  },
  {
    "path": "lib/third_party/microtar/LICENSE",
    "content": "Copyright (c) 2017 rxi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "lib/third_party/microtar/README.md",
    "content": "# microtar\nA lightweight tar library written in ANSI C\n\n\n## Basic Usage\nThe library consists of `microtar.c` and `microtar.h`. These two files can be\ndropped into an existing project and compiled along with it.\n\n\n#### Reading\n```c\nmtar_t tar;\nmtar_header_t h;\nchar *p;\n\n/* Open archive for reading */\nmtar_open(&tar, \"test.tar\", \"r\");\n\n/* Print all file names and sizes */\nwhile ( (mtar_read_header(&tar, &h)) != MTAR_ENULLRECORD ) {\n  printf(\"%s (%d bytes)\\n\", h.name, h.size);\n  mtar_next(&tar);\n}\n\n/* Load and print contents of file \"test.txt\" */\nmtar_find(&tar, \"test.txt\", &h);\np = calloc(1, h.size + 1);\nmtar_read_data(&tar, p, h.size);\nprintf(\"%s\", p);\nfree(p);\n\n/* Close archive */\nmtar_close(&tar);\n```\n\n#### Writing\n```c\nmtar_t tar;\nconst char *str1 = \"Hello world\";\nconst char *str2 = \"Goodbye world\";\n\n/* Open archive for writing */\nmtar_open(&tar, \"test.tar\", \"w\");\n\n/* Write strings to files `test1.txt` and `test2.txt` */\nmtar_write_file_header(&tar, \"test1.txt\", strlen(str1));\nmtar_write_data(&tar, str1, strlen(str1));\nmtar_write_file_header(&tar, \"test2.txt\", strlen(str2));\nmtar_write_data(&tar, str2, strlen(str2));\n\n/* Finalize -- this needs to be the last thing done before closing */\nmtar_finalize(&tar);\n\n/* Close archive */\nmtar_close(&tar);\n```\n\n\n## Error handling\nAll functions which return an `int` will return `MTAR_ESUCCESS` if the operation\nis successful. If an error occurs an error value less-than-zero will be\nreturned; this value can be passed to the function `mtar_strerror()` to get its\ncorresponding error string.\n\n\n## Wrapping a stream\nIf you want to read or write from something other than a file, the `mtar_t`\nstruct can be manually initialized with your own callback functions and a\n`stream` pointer.\n\nAll callback functions are passed a pointer to the `mtar_t` struct as their\nfirst argument. They should return `MTAR_ESUCCESS` if the operation succeeds\nwithout an error, or an integer below zero if an error occurs.\n\nAfter the `stream` field has been set, all required callbacks have been set and\nall unused fields have been zeroset the `mtar_t` struct can be safely used with\nthe microtar functions. `mtar_open` *should not* be called if the `mtar_t`\nstruct was initialized manually.\n\n#### Reading\nThe following callbacks should be set for reading an archive from a stream:\n\nName    | Arguments                                | Description\n--------|------------------------------------------|---------------------------\n`read`  | `mtar_t *tar, void *data, unsigned size` | Read data from the stream\n`seek`  | `mtar_t *tar, unsigned pos`              | Set the position indicator\n`close` | `mtar_t *tar`                            | Close the stream\n\n#### Writing\nThe following callbacks should be set for writing an archive to a stream:\n\nName    | Arguments                                      | Description\n--------|------------------------------------------------|---------------------\n`write` | `mtar_t *tar, const void *data, unsigned size` | Write data to the stream\n\n\n## License\nThis library is free software; you can redistribute it and/or modify it under\nthe terms of the MIT license. See [LICENSE](LICENSE) for details.\n"
  },
  {
    "path": "lib/third_party/microtar/include/microtar.h",
    "content": "/**\n * Copyright (c) 2017 rxi\n *\n * This library is free software; you can redistribute it and/or modify it\n * under the terms of the MIT license. See `microtar.c` for details.\n */\n\n#ifndef MICROTAR_H\n#define MICROTAR_H\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\n#define MTAR_VERSION \"0.1.0\"\n\nenum {\n    MTAR_ESUCCESS = 0,\n    MTAR_EFAILURE = -1,\n    MTAR_EOPENFAIL = -2,\n    MTAR_EREADFAIL = -3,\n    MTAR_EWRITEFAIL = -4,\n    MTAR_ESEEKFAIL = -5,\n    MTAR_EBADCHKSUM = -6,\n    MTAR_ENULLRECORD = -7,\n    MTAR_ENOTFOUND = -8\n};\n\nenum {\n    MTAR_TREG = '0',\n    MTAR_TLNK = '1',\n    MTAR_TSYM = '2',\n    MTAR_TCHR = '3',\n    MTAR_TBLK = '4',\n    MTAR_TDIR = '5',\n    MTAR_TFIFO = '6'\n};\n\ntypedef struct {\n    unsigned mode;\n    unsigned owner;\n    unsigned size;\n    unsigned mtime;\n    unsigned type;\n    char name[100];\n    char linkname[100];\n} mtar_header_t;\n\n\ntypedef struct mtar_t mtar_t;\n\nstruct mtar_t {\n    int (*read)(mtar_t *tar, void *data, unsigned size);\n\n    int (*write)(mtar_t *tar, const void *data, unsigned size);\n\n    int (*seek)(mtar_t *tar, unsigned pos);\n\n    int (*close)(mtar_t *tar);\n\n    void *stream;\n    unsigned pos;\n    unsigned remaining_data;\n    unsigned last_header;\n};\n\n\nconst char *mtar_strerror(int err);\n\nint mtar_open(mtar_t *tar, const char *filename, const char *mode);\nint mtar_close(mtar_t *tar);\n\nint mtar_seek(mtar_t *tar, unsigned pos);\nint mtar_rewind(mtar_t *tar);\nint mtar_next(mtar_t *tar);\nint mtar_find(mtar_t *tar, const char *name, mtar_header_t *h);\nint mtar_read_header(mtar_t *tar, mtar_header_t *h);\nint mtar_read_data(mtar_t *tar, void *ptr, unsigned size);\n\nint mtar_write_header(mtar_t *tar, const mtar_header_t *h);\nint mtar_write_file_header(mtar_t *tar, const char *name, unsigned size);\nint mtar_write_dir_header(mtar_t *tar, const char *name);\nint mtar_write_data(mtar_t *tar, const void *data, unsigned size);\nint mtar_finalize(mtar_t *tar);\n\n#if defined(__cplusplus)\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/third_party/microtar/source/microtar.c",
    "content": "/*\n * Copyright (c) 2017 rxi\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stddef.h>\n#include <string.h>\n\n#include <microtar.h>\n\ntypedef struct {\n  char name[100];\n  char mode[8];\n  char owner[8];\n  char group[8];\n  char size[12];\n  char mtime[12];\n  char checksum[8];\n  char type;\n  char linkname[100];\n  char _padding[255];\n} mtar_raw_header_t;\n\n\nstatic unsigned round_up(unsigned n, unsigned incr) {\n  return n + (incr - n % incr) % incr;\n}\n\n\nstatic unsigned checksum(const mtar_raw_header_t* rh) {\n  unsigned i;\n  unsigned char *p = (unsigned char*) rh;\n  unsigned res = 256;\n  for (i = 0; i < offsetof(mtar_raw_header_t, checksum); i++) {\n    res += p[i];\n  }\n  for (i = offsetof(mtar_raw_header_t, type); i < sizeof(*rh); i++) {\n    res += p[i];\n  }\n  return res;\n}\n\n\nstatic int tread(mtar_t *tar, void *data, unsigned size) {\n  int err = tar->read(tar, data, size);\n  tar->pos += size;\n  return err;\n}\n\n\nstatic int twrite(mtar_t *tar, const void *data, unsigned size) {\n  int err = tar->write(tar, data, size);\n  tar->pos += size;\n  return err;\n}\n\n\nstatic int write_null_bytes(mtar_t *tar, int n) {\n  int i, err;\n  char nul = '\\0';\n  for (i = 0; i < n; i++) {\n    err = twrite(tar, &nul, 1);\n    if (err) {\n      return err;\n    }\n  }\n  return MTAR_ESUCCESS;\n}\n\n\nstatic int raw_to_header(mtar_header_t *h, const mtar_raw_header_t *rh) {\n  unsigned chksum1, chksum2;\n\n  /* If the checksum starts with a null byte we assume the record is NULL */\n  if (*rh->checksum == '\\0') {\n    return MTAR_ENULLRECORD;\n  }\n\n  /* Build and compare checksum */\n  chksum1 = checksum(rh);\n  sscanf(rh->checksum, \"%o\", &chksum2);\n  if (chksum1 != chksum2) {\n    return MTAR_EBADCHKSUM;\n  }\n\n  /* Load raw header into header */\n  sscanf(rh->mode, \"%o\", &h->mode);\n  sscanf(rh->owner, \"%o\", &h->owner);\n  sscanf(rh->size, \"%o\", &h->size);\n  sscanf(rh->mtime, \"%o\", &h->mtime);\n  h->type = rh->type;\n  strcpy(h->name, rh->name);\n  strcpy(h->linkname, rh->linkname);\n\n  return MTAR_ESUCCESS;\n}\n\n\nstatic int header_to_raw(mtar_raw_header_t *rh, const mtar_header_t *h) {\n  unsigned chksum;\n\n  /* Load header into raw header */\n  memset(rh, 0, sizeof(*rh));\n  sprintf(rh->mode, \"%o\", h->mode);\n  sprintf(rh->owner, \"%o\", h->owner);\n  sprintf(rh->size, \"%o\", h->size);\n  sprintf(rh->mtime, \"%o\", h->mtime);\n  rh->type = h->type ? h->type : MTAR_TREG;\n  strcpy(rh->name, h->name);\n  strcpy(rh->linkname, h->linkname);\n\n  /* Calculate and write checksum */\n  chksum = checksum(rh);\n  sprintf(rh->checksum, \"%06o\", chksum);\n  rh->checksum[7] = ' ';\n\n  return MTAR_ESUCCESS;\n}\n\n\nconst char* mtar_strerror(int err) {\n  switch (err) {\n    case MTAR_ESUCCESS     : return \"success\";\n    case MTAR_EFAILURE     : return \"failure\";\n    case MTAR_EOPENFAIL    : return \"could not open\";\n    case MTAR_EREADFAIL    : return \"could not read\";\n    case MTAR_EWRITEFAIL   : return \"could not write\";\n    case MTAR_ESEEKFAIL    : return \"could not seek\";\n    case MTAR_EBADCHKSUM   : return \"bad checksum\";\n    case MTAR_ENULLRECORD  : return \"null record\";\n    case MTAR_ENOTFOUND    : return \"file not found\";\n  }\n  return \"unknown error\";\n}\n\n\nstatic int file_write(mtar_t *tar, const void *data, unsigned size) {\n  unsigned res = fwrite(data, 1, size, tar->stream);\n  return (res == size) ? MTAR_ESUCCESS : MTAR_EWRITEFAIL;\n}\n\nstatic int file_read(mtar_t *tar, void *data, unsigned size) {\n  unsigned res = fread(data, 1, size, tar->stream);\n  return (res == size) ? MTAR_ESUCCESS : MTAR_EREADFAIL;\n}\n\nstatic int file_seek(mtar_t *tar, unsigned offset) {\n  int res = fseek(tar->stream, offset, SEEK_SET);\n  return (res == 0) ? MTAR_ESUCCESS : MTAR_ESEEKFAIL;\n}\n\nstatic int file_close(mtar_t *tar) {\n  fclose(tar->stream);\n  return MTAR_ESUCCESS;\n}\n\n\nint mtar_open(mtar_t *tar, const char *filename, const char *mode) {\n  int err;\n  mtar_header_t h;\n\n  /* Init tar struct and functions */\n  memset(tar, 0, sizeof(*tar));\n  tar->write = file_write;\n  tar->read = file_read;\n  tar->seek = file_seek;\n  tar->close = file_close;\n\n  /* Assure mode is always binary */\n  if ( strchr(mode, 'r') ) mode = \"rb\";\n  if ( strchr(mode, 'w') ) mode = \"wb\";\n  if ( strchr(mode, 'a') ) mode = \"ab\";\n  /* Open file */\n  tar->stream = fopen(filename, mode);\n  if (!tar->stream) {\n    return MTAR_EOPENFAIL;\n  }\n  /* Read first header to check it is valid if mode is `r` */\n  if (*mode == 'r') {\n    err = mtar_read_header(tar, &h);\n    if (err != MTAR_ESUCCESS) {\n      mtar_close(tar);\n      return err;\n    }\n  }\n\n  /* Return ok */\n  return MTAR_ESUCCESS;\n}\n\n\nint mtar_close(mtar_t *tar) {\n  return tar->close(tar);\n}\n\n\nint mtar_seek(mtar_t *tar, unsigned pos) {\n  int err = tar->seek(tar, pos);\n  tar->pos = pos;\n  return err;\n}\n\n\nint mtar_rewind(mtar_t *tar) {\n  tar->remaining_data = 0;\n  tar->last_header = 0;\n  return mtar_seek(tar, 0);\n}\n\n\nint mtar_next(mtar_t *tar) {\n  int err, n;\n  mtar_header_t h;\n  /* Load header */\n  err = mtar_read_header(tar, &h);\n  if (err) {\n    return err;\n  }\n  /* Seek to next record */\n  n = round_up(h.size, 512) + sizeof(mtar_raw_header_t);\n  return mtar_seek(tar, tar->pos + n);\n}\n\n\nint mtar_find(mtar_t *tar, const char *name, mtar_header_t *h) {\n  int err;\n  mtar_header_t header;\n  /* Start at beginning */\n  err = mtar_rewind(tar);\n  if (err) {\n    return err;\n  }\n  /* Iterate all files until we hit an error or find the file */\n  while ( (err = mtar_read_header(tar, &header)) == MTAR_ESUCCESS ) {\n    if ( !strcmp(header.name, name) ) {\n      if (h) {\n        *h = header;\n      }\n      return MTAR_ESUCCESS;\n    }\n    mtar_next(tar);\n  }\n  /* Return error */\n  if (err == MTAR_ENULLRECORD) {\n    err = MTAR_ENOTFOUND;\n  }\n  return err;\n}\n\n\nint mtar_read_header(mtar_t *tar, mtar_header_t *h) {\n  int err;\n  mtar_raw_header_t rh;\n  /* Save header position */\n  tar->last_header = tar->pos;\n  /* Read raw header */\n  err = tread(tar, &rh, sizeof(rh));\n  if (err) {\n    return err;\n  }\n  /* Seek back to start of header */\n  err = mtar_seek(tar, tar->last_header);\n  if (err) {\n    return err;\n  }\n  /* Load raw header into header struct and return */\n  return raw_to_header(h, &rh);\n}\n\n\nint mtar_read_data(mtar_t *tar, void *ptr, unsigned size) {\n  int err;\n  /* If we have no remaining data then this is the first read, we get the size,\n   * set the remaining data and seek to the beginning of the data */\n  if (tar->remaining_data == 0) {\n    mtar_header_t h;\n    /* Read header */\n    err = mtar_read_header(tar, &h);\n    if (err) {\n      return err;\n    }\n    /* Seek past header and init remaining data */\n    err = mtar_seek(tar, tar->pos + sizeof(mtar_raw_header_t));\n    if (err) {\n      return err;\n    }\n    tar->remaining_data = h.size;\n  }\n  /* Read data */\n  err = tread(tar, ptr, size);\n  if (err) {\n    return err;\n  }\n  tar->remaining_data -= size;\n  /* If there is no remaining data we've finished reading and seek back to the\n   * header */\n  if (tar->remaining_data == 0) {\n    return mtar_seek(tar, tar->last_header);\n  }\n  return MTAR_ESUCCESS;\n}\n\n\nint mtar_write_header(mtar_t *tar, const mtar_header_t *h) {\n  mtar_raw_header_t rh;\n  /* Build raw header and write */\n  header_to_raw(&rh, h);\n  tar->remaining_data = h->size;\n  return twrite(tar, &rh, sizeof(rh));\n}\n\n\nint mtar_write_file_header(mtar_t *tar, const char *name, unsigned size) {\n  mtar_header_t h;\n  /* Build header */\n  memset(&h, 0, sizeof(h));\n  strcpy(h.name, name);\n  h.size = size;\n  h.type = MTAR_TREG;\n  h.mode = 0664;\n  /* Write header */\n  return mtar_write_header(tar, &h);\n}\n\n\nint mtar_write_dir_header(mtar_t *tar, const char *name) {\n  mtar_header_t h;\n  /* Build header */\n  memset(&h, 0, sizeof(h));\n  strcpy(h.name, name);\n  h.type = MTAR_TDIR;\n  h.mode = 0775;\n  /* Write header */\n  return mtar_write_header(tar, &h);\n}\n\n\nint mtar_write_data(mtar_t *tar, const void *data, unsigned size) {\n  int err;\n  /* Write data */\n  err = twrite(tar, data, size);\n  if (err) {\n    return err;\n  }\n  tar->remaining_data -= size;\n  /* Write padding if we've written all the data for this file */\n  if (tar->remaining_data == 0) {\n    return write_null_bytes(tar, round_up(tar->pos, 512) - tar->pos);\n  }\n  return MTAR_ESUCCESS;\n}\n\n\nint mtar_finalize(mtar_t *tar) {\n  /* Write two NULL records */\n  return write_null_bytes(tar, sizeof(mtar_raw_header_t) * 2);\n}\n"
  },
  {
    "path": "lib/third_party/miniaudio/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\nproject(miniaudio)\n\nadd_library(miniaudio STATIC\n        source/miniaudio.c\n)\n\ntarget_include_directories(miniaudio PUBLIC include)\nif (APPLE)\n    set_source_files_properties(source/miniaudio.c PROPERTIES LANGUAGE OBJC)\nelseif (NOT MSVC)\n    target_compile_options(miniaudio PRIVATE -Wno-unused-result)\nendif ()\n"
  },
  {
    "path": "lib/third_party/miniaudio/LICENSE",
    "content": "This software is available as a choice of the following licenses. Choose\nwhichever you prefer.\n\n===============================================================================\nALTERNATIVE 1 - Public Domain (www.unlicense.org)\n===============================================================================\nThis is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\n\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <http://unlicense.org/>\n\n===============================================================================\nALTERNATIVE 2 - MIT No Attribution\n===============================================================================\nCopyright 2020 David Reid\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "lib/third_party/miniaudio/include/miniaudio.h",
    "content": "/*\nAudio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.\nminiaudio - v0.11.21 - 2023-11-15\n\nDavid Reid - mackron@gmail.com\n\nWebsite:       https://miniaud.io\nDocumentation: https://miniaud.io/docs\nGitHub:        https://github.com/mackron/miniaudio\n*/\n\n/*\n1. Introduction\n===============\nminiaudio is a single file library for audio playback and capture. To use it, do the following in\none .c file:\n\n    ```c\n    #define MINIAUDIO_IMPLEMENTATION\n    #include \"miniaudio.h\"\n    ```\n\nYou can do `#include \"miniaudio.h\"` in other parts of the program just like any other header.\n\nminiaudio includes both low level and high level APIs. The low level API is good for those who want\nto do all of their mixing themselves and only require a light weight interface to the underlying\naudio device. The high level API is good for those who have complex mixing and effect requirements.\n\nIn miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles\nto opaque objects which means you need to allocate memory for objects yourself. In the examples\npresented in this documentation you will often see objects declared on the stack. You need to be\ncareful when translating these examples to your own code so that you don't accidentally declare\nyour objects on the stack and then cause them to become invalid once the function returns. In\naddition, you must ensure the memory address of your objects remain the same throughout their\nlifetime. You therefore cannot be making copies of your objects.\n\nA config/init pattern is used throughout the entire library. The idea is that you set up a config\nobject and pass that into the initialization routine. The advantage to this system is that the\nconfig object can be initialized with logical defaults and new properties added to it without\nbreaking the API. The config object can be allocated on the stack and does not need to be\nmaintained after initialization of the corresponding object.\n\n\n1.1. Low Level API\n------------------\nThe low level API gives you access to the raw audio data of an audio device. It supports playback,\ncapture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which\nphysical device(s) you want to connect to.\n\nThe low level API uses the concept of a \"device\" as the abstraction for physical devices. The idea\nis that you choose a physical device to emit or capture audio from, and then move data to/from the\ndevice when miniaudio tells you to. Data is delivered to and from devices asynchronously via a\ncallback which you specify when initializing the device.\n\nWhen initializing the device you first need to configure it. The device configuration allows you to\nspecify things like the format of the data delivered via the callback, the size of the internal\nbuffer and the ID of the device you want to emit or capture audio from.\n\nOnce you have the device configuration set up you can initialize the device. When initializing a\ndevice you need to allocate memory for the device object beforehand. This gives the application\ncomplete control over how the memory is allocated. In the example below we initialize a playback\ndevice on the stack, but you could allocate it on the heap if that suits your situation better.\n\n    ```c\n    void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)\n    {\n        // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both\n        // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than\n        // frameCount frames.\n    }\n\n    int main()\n    {\n        ma_device_config config = ma_device_config_init(ma_device_type_playback);\n        config.playback.format   = ma_format_f32;   // Set to ma_format_unknown to use the device's native format.\n        config.playback.channels = 2;               // Set to 0 to use the device's native channel count.\n        config.sampleRate        = 48000;           // Set to 0 to use the device's native sample rate.\n        config.dataCallback      = data_callback;   // This function will be called when miniaudio needs more data.\n        config.pUserData         = pMyCustomData;   // Can be accessed from the device object (device.pUserData).\n\n        ma_device device;\n        if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {\n            return -1;  // Failed to initialize the device.\n        }\n\n        ma_device_start(&device);     // The device is sleeping by default so you'll need to start it manually.\n\n        // Do something here. Probably your program's main loop.\n\n        ma_device_uninit(&device);\n        return 0;\n    }\n    ```\n\nIn the example above, `data_callback()` is where audio data is written and read from the device.\nThe idea is in playback mode you cause sound to be emitted from the speakers by writing audio data\nto the output buffer (`pOutput` in the example). In capture mode you read data from the input\nbuffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you\nhow many frames can be written to the output buffer and read from the input buffer. A \"frame\" is\none sample for each channel. For example, in a stereo stream (2 channels), one frame is 2\nsamples: one for the left, one for the right. The channel count is defined by the device config.\nThe size in bytes of an individual sample is defined by the sample format which is also specified\nin the device config. Multi-channel audio data is always interleaved, which means the samples for\neach frame are stored next to each other in memory. For example, in a stereo stream the first pair\nof samples will be the left and right samples for the first frame, the second pair of samples will\nbe the left and right samples for the second frame, etc.\n\nThe configuration of the device is defined by the `ma_device_config` structure. The config object\nis always initialized with `ma_device_config_init()`. It's important to always initialize the\nconfig with this function as it initializes it with logical defaults and ensures your program\ndoesn't break when new members are added to the `ma_device_config` structure. The example above\nuses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes\na single parameter, which is whether or not the device is a playback, capture, duplex or loopback\ndevice (loopback devices are not supported on all backends). The `config.playback.format` member\nsets the sample format which can be one of the following (all formats are native-endian):\n\n    +---------------+----------------------------------------+---------------------------+\n    | Symbol        | Description                            | Range                     |\n    +---------------+----------------------------------------+---------------------------+\n    | ma_format_f32 | 32-bit floating point                  | [-1, 1]                   |\n    | ma_format_s16 | 16-bit signed integer                  | [-32768, 32767]           |\n    | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607]       |\n    | ma_format_s32 | 32-bit signed integer                  | [-2147483648, 2147483647] |\n    | ma_format_u8  | 8-bit unsigned integer                 | [0, 255]                  |\n    +---------------+----------------------------------------+---------------------------+\n\nThe `config.playback.channels` member sets the number of channels to use with the device. The\nchannel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate\n(which must be the same for both playback and capture in full-duplex configurations). This is\nusually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between\n8000 and 384000, however.\n\nNote that leaving the format, channel count and/or sample rate at their default values will result\nin the internal device's native configuration being used which is useful if you want to avoid the\noverhead of miniaudio's automatic data conversion.\n\nIn addition to the sample format, channel count and sample rate, the data callback and user data\npointer are also set via the config. The user data pointer is not passed into the callback as a\nparameter, but is instead set to the `pUserData` member of `ma_device` which you can access\ndirectly since all miniaudio structures are transparent.\n\nInitializing the device is done with `ma_device_init()`. This will return a result code telling you\nwhat went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is\ncomplete the device will be in a stopped state. To start it, use `ma_device_start()`.\nUninitializing the device will stop it, which is what the example above does, but you can also stop\nthe device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again.\nNote that it's important to never stop or start the device from inside the callback. This will\nresult in a deadlock. Instead you set a variable or signal an event indicating that the device\nneeds to stop and handle it in a different thread. The following APIs must never be called inside\nthe callback:\n\n    ```c\n    ma_device_init()\n    ma_device_init_ex()\n    ma_device_uninit()\n    ma_device_start()\n    ma_device_stop()\n    ```\n\nYou must never try uninitializing and reinitializing a device inside the callback. You must also\nnever try to stop and start it from inside the callback. There are a few other things you shouldn't\ndo in the callback depending on your requirements, however this isn't so much a thread-safety\nthing, but rather a real-time processing thing which is beyond the scope of this introduction.\n\nThe example above demonstrates the initialization of a playback device, but it works exactly the\nsame for capture. All you need to do is change the device type from `ma_device_type_playback` to\n`ma_device_type_capture` when setting up the config, like so:\n\n    ```c\n    ma_device_config config = ma_device_config_init(ma_device_type_capture);\n    config.capture.format   = MY_FORMAT;\n    config.capture.channels = MY_CHANNEL_COUNT;\n    ```\n\nIn the data callback you just read from the input buffer (`pInput` in the example above) and leave\nthe output buffer alone (it will be set to NULL when the device type is set to\n`ma_device_type_capture`).\n\nThese are the available device types and how you should handle the buffers in the callback:\n\n    +-------------------------+--------------------------------------------------------+\n    | Device Type             | Callback Behavior                                      |\n    +-------------------------+--------------------------------------------------------+\n    | ma_device_type_playback | Write to output buffer, leave input buffer untouched.  |\n    | ma_device_type_capture  | Read from input buffer, leave output buffer untouched. |\n    | ma_device_type_duplex   | Read from input buffer, write to output buffer.        |\n    | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. |\n    +-------------------------+--------------------------------------------------------+\n\nYou will notice in the example above that the sample format and channel count is specified\nseparately for playback and capture. This is to support different data formats between the playback\nand capture devices in a full-duplex system. An example may be that you want to capture audio data\nas a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you\nuse different formats between playback and capture in a full-duplex configuration you will need to\nconvert the data yourself. There are functions available to help you do this which will be\nexplained later.\n\nThe example above did not specify a physical device to connect to which means it will use the\noperating system's default device. If you have multiple physical devices connected and you want to\nuse a specific one you will need to specify the device ID in the configuration, like so:\n\n    ```c\n    config.playback.pDeviceID = pMyPlaybackDeviceID;    // Only if requesting a playback or duplex device.\n    config.capture.pDeviceID = pMyCaptureDeviceID;      // Only if requesting a capture, duplex or loopback device.\n    ```\n\nTo retrieve the device ID you will need to perform device enumeration, however this requires the\nuse of a new concept called the \"context\". Conceptually speaking the context sits above the device.\nThere is one context to many devices. The purpose of the context is to represent the backend at a\nmore global level and to perform operations outside the scope of an individual device. Mainly it is\nused for performing run-time linking against backend libraries, initializing backends and\nenumerating devices. The example below shows how to enumerate devices.\n\n    ```c\n    ma_context context;\n    if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) {\n        // Error.\n    }\n\n    ma_device_info* pPlaybackInfos;\n    ma_uint32 playbackCount;\n    ma_device_info* pCaptureInfos;\n    ma_uint32 captureCount;\n    if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) {\n        // Error.\n    }\n\n    // Loop over each device info and do something with it. Here we just print the name with their index. You may want\n    // to give the user the opportunity to choose which device they'd prefer.\n    for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) {\n        printf(\"%d - %s\\n\", iDevice, pPlaybackInfos[iDevice].name);\n    }\n\n    ma_device_config config = ma_device_config_init(ma_device_type_playback);\n    config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id;\n    config.playback.format    = MY_FORMAT;\n    config.playback.channels  = MY_CHANNEL_COUNT;\n    config.sampleRate         = MY_SAMPLE_RATE;\n    config.dataCallback       = data_callback;\n    config.pUserData          = pMyCustomData;\n\n    ma_device device;\n    if (ma_device_init(&context, &config, &device) != MA_SUCCESS) {\n        // Error\n    }\n\n    ...\n\n    ma_device_uninit(&device);\n    ma_context_uninit(&context);\n    ```\n\nThe first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`.\nThe first parameter is a pointer to a list of `ma_backend` values which are used to override the\ndefault backend priorities. When this is NULL, as in this example, miniaudio's default priorities\nare used. The second parameter is the number of backends listed in the array pointed to by the\nfirst parameter. The third parameter is a pointer to a `ma_context_config` object which can be\nNULL, in which case defaults are used. The context configuration is used for setting the logging\ncallback, custom memory allocation callbacks, user-defined data and some backend-specific\nconfigurations.\n\nOnce the context has been initialized you can enumerate devices. In the example above we use the\nsimpler `ma_context_get_devices()`, however you can also use a callback for handling devices by\nusing `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer\nto a pointer that will, upon output, be set to a pointer to a buffer containing a list of\n`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive\nthe number of items in the returned buffer. Do not free the returned buffers as their memory is\nmanaged internally by miniaudio.\n\nThe `ma_device_info` structure contains an `id` member which is the ID you pass to the device\nconfig. It also contains the name of the device which is useful for presenting a list of devices\nto the user via the UI.\n\nWhen creating your own context you will want to pass it to `ma_device_init()` when initializing the\ndevice. Passing in NULL, like we do in the first example, will result in miniaudio creating the\ncontext for you, which you don't want to do since you've already created a context. Note that\ninternally the context is only tracked by it's pointer which means you must not change the location\nof the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for\nthe context.\n\n\n1.2. High Level API\n-------------------\nThe high level API consists of three main parts:\n\n  * Resource management for loading and streaming sounds.\n  * A node graph for advanced mixing and effect processing.\n  * A high level \"engine\" that wraps around the resource manager and node graph.\n\nThe resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds\nfully into memory and also streaming. It will also deal with reference counting for you which\navoids the same sound being loaded multiple times.\n\nThe node graph is used for mixing and effect processing. The idea is that you connect a number of\nnodes into the graph by connecting each node's outputs to another node's inputs. Each node can\nimplement it's own effect. By chaining nodes together, advanced mixing and effect processing can\nbe achieved.\n\nThe engine encapsulates both the resource manager and the node graph to create a simple, easy to\nuse high level API. The resource manager and node graph APIs are covered in more later sections of\nthis manual.\n\nThe code below shows how you can initialize an engine using it's default configuration.\n\n    ```c\n    ma_result result;\n    ma_engine engine;\n\n    result = ma_engine_init(NULL, &engine);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to initialize the engine.\n    }\n    ```\n\nThis creates an engine instance which will initialize a device internally which you can access with\n`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed\nwith `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which\nmeans you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a\ncast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast.\n\nNote that all objects in miniaudio, including the `ma_engine` object in the example above, are\ntransparent structures. There are no handles to opaque structures in miniaudio which means you need\nto be mindful of how you declare them. In the example above we are declaring it on the stack, but\nthis will result in the struct being invalidated once the function encapsulating it returns. If\nallocating the engine on the heap is more appropriate, you can easily do so with a standard call\nto `malloc()` or whatever heap allocation routine you like:\n\n    ```c\n    ma_engine* pEngine = malloc(sizeof(*pEngine));\n    ```\n\nThe `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure\nan engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of\n`ma_engine_init()`:\n\n    ```c\n    ma_result result;\n    ma_engine engine;\n    ma_engine_config engineConfig;\n\n    engineConfig = ma_engine_config_init();\n    engineConfig.pResourceManager = &myCustomResourceManager;   // <-- Initialized as some earlier stage.\n\n    result = ma_engine_init(&engineConfig, &engine);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n    ```\n\nThis creates an engine instance using a custom config. In this particular example it's showing how\nyou can specify a custom resource manager rather than having the engine initialize one internally.\nThis is particularly useful if you want to have multiple engine's share the same resource manager.\n\nThe engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed.\n\nBy default the engine will be started, but nothing will be playing because no sounds have been\ninitialized. The easiest but least flexible way of playing a sound is like so:\n\n    ```c\n    ma_engine_play_sound(&engine, \"my_sound.wav\", NULL);\n    ```\n\nThis plays what miniaudio calls an \"inline\" sound. It plays the sound once, and then puts the\ninternal sound up for recycling. The last parameter is used to specify which sound group the sound\nshould be associated with which will be explained later. This particular way of playing a sound is\nsimple, but lacks flexibility and features. A more flexible way of playing a sound is to first\ninitialize a sound:\n\n    ```c\n    ma_result result;\n    ma_sound sound;\n\n    result = ma_sound_init_from_file(&engine, \"my_sound.wav\", 0, NULL, NULL, &sound);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    ma_sound_start(&sound);\n    ```\n\nThis returns a `ma_sound` object which represents a single instance of the specified sound file. If\nyou want to play the same file multiple times simultaneously, you need to create one sound for each\ninstance.\n\nSounds should be uninitialized with `ma_sound_uninit()`.\n\nSounds are not started by default. Start a sound with `ma_sound_start()` and stop it with\n`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use\n`ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting\nand stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound\nthe be started and/or stopped at a specific time. This can be done with the following functions:\n\n    ```c\n    ma_sound_set_start_time_in_pcm_frames()\n    ma_sound_set_start_time_in_milliseconds()\n    ma_sound_set_stop_time_in_pcm_frames()\n    ma_sound_set_stop_time_in_milliseconds()\n    ```\n\nThe start/stop time needs to be specified based on the absolute timer which is controlled by the\nengine. The current global time time in PCM frames can be retrieved with\n`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with\n`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling\na start time still requires an explicit call to `ma_sound_start()` before anything will play:\n\n    ```c\n    ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2);\n    ma_sound_start(&sound);\n    ```\n\nThe third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be\nloaded and a few options on which features should be enabled for that sound. By default, the sound\nis synchronously loaded fully into memory straight from the file system without any kind of\ndecoding. If you want to decode the sound before storing it in memory, you need to specify the\n`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier\nstage, such as a loading stage. Without this option, decoding will happen dynamically at mixing\ntime which might be too expensive on the audio thread.\n\nIf you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This\nwill result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing\nuntil the sound has had some audio decoded.\n\nThe fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise\nsounds into groups which have their own effect processing and volume control. An example is a game\nwhich might have separate groups for sfx, voice and music. Each of these groups have their own\nindependent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize\na sound group.\n\nSounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node`\nAPI. This makes it possible to connect sounds and sound groups to effect nodes to produce complex\neffect chains.\n\nA sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume\ncontrol you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear.\n\nPanning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know\na sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect,\nyou can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization.\n\nBy default, sounds and sound groups have spatialization enabled. If you don't ever want to\nspatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The\nspatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and\nenvironmental occlusion are not currently supported, but planned for the future. The supported\nfeatures include:\n\n  * Sound and listener positioning and orientation with cones\n  * Attenuation models: none, inverse, linear and exponential\n  * Doppler effect\n\nSounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`.\n\nTo check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound\nis at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with\n`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping.\n\n\n\n2. Building\n===========\nminiaudio should work cleanly out of the box without the need to download or install any\ndependencies. See below for platform-specific details.\n\nNote that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations.\n\nIf you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`,\netc. you need to link with `-latomic`.\n\n\n2.1. Windows\n------------\nThe Windows build should compile cleanly on all popular compilers without the need to configure any\ninclude paths nor link to any libraries.\n\nThe UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external\nsymbol for `ActivateAudioInterfaceAsync()`.\n\n\n2.2. macOS and iOS\n------------------\nThe macOS build should compile cleanly without the need to download any dependencies nor link to\nany libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to\nlink the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling\nthrough the command line requires linking to `-lpthread` and `-lm`.\n\nDue to the way miniaudio links to frameworks at runtime, your application may not pass Apple's\nnotarization process. To fix this there are two options. The first is to use the\n`MA_NO_RUNTIME_LINKING` option, like so:\n\n    ```c\n    #ifdef __APPLE__\n        #define MA_NO_RUNTIME_LINKING\n    #endif\n    #define MINIAUDIO_IMPLEMENTATION\n    #include \"miniaudio.h\"\n    ```\n\nThis will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`.\nIf you get errors about AudioToolbox, try with `-framework AudioUnit` instead. You may get this when\nusing older versions of iOS. Alternatively, if you would rather keep using runtime linking you can\nadd the following to your entitlements.xcent file:\n\n    ```\n    <key>com.apple.security.cs.allow-dyld-environment-variables</key>\n    <true/>\n    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>\n    <true/>\n    ```\n\nSee this discussion for more info: https://github.com/mackron/miniaudio/issues/203.\n\n\n2.3. Linux\n----------\nThe Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any\ndevelopment packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM.\n\n\n2.4. BSD\n--------\nThe BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses\nsndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit\nARM.\n\n\n2.5. Android\n------------\nAAudio is the highest priority backend on Android. This should work out of the box without needing\nany kind of compiler configuration. Support for AAudio starts with Android 8 which means older\nversions will fall back to OpenSL|ES which requires API level 16+.\n\nThere have been reports that the OpenSL|ES backend fails to initialize on some Android based\ndevices due to `dlopen()` failing to open \"libOpenSLES.so\". If this happens on your platform\nyou'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES.\n\n\n2.6. Emscripten\n---------------\nThe Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box.\nYou cannot use `-std=c*` compiler flags, nor `-ansi`.\n\nYou can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling\nwith the following options:\n\n    -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY\n\nAn example for compiling with AudioWorklet support might look like this:\n\n    emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY\n\nTo run locally, you'll need to use emrun:\n\n    emrun bin/program.html\n\n\n\n2.7. Build Options\n------------------\n`#define` these options before including miniaudio.h.\n\n    +----------------------------------+--------------------------------------------------------------------+\n    | Option                           | Description                                                        |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_WASAPI                     | Disables the WASAPI backend.                                       |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_DSOUND                     | Disables the DirectSound backend.                                  |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_WINMM                      | Disables the WinMM backend.                                        |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_ALSA                       | Disables the ALSA backend.                                         |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_PULSEAUDIO                 | Disables the PulseAudio backend.                                   |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_JACK                       | Disables the JACK backend.                                         |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_COREAUDIO                  | Disables the Core Audio backend.                                   |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_SNDIO                      | Disables the sndio backend.                                        |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_AUDIO4                     | Disables the audio(4) backend.                                     |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_OSS                        | Disables the OSS backend.                                          |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_AAUDIO                     | Disables the AAudio backend.                                       |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_OPENSL                     | Disables the OpenSL|ES backend.                                    |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_WEBAUDIO                   | Disables the Web Audio backend.                                    |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_NULL                       | Disables the null backend.                                         |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to     |\n    |                                  | enable specific backends.                                          |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_WASAPI                 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the WASAPI backend.                                         |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_DSOUND                 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the DirectSound backend.                                    |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_WINMM                  | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the WinMM backend.                                          |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_ALSA                   | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the ALSA backend.                                           |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_PULSEAUDIO             | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the PulseAudio backend.                                     |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_JACK                   | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the JACK backend.                                           |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_COREAUDIO              | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the Core Audio backend.                                     |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_SNDIO                  | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the sndio backend.                                          |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_AUDIO4                 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the audio(4) backend.                                       |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_OSS                    | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the OSS backend.                                            |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_AAUDIO                 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the AAudio backend.                                         |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_OPENSL                 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the OpenSL|ES backend.                                      |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_WEBAUDIO               | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the Web Audio backend.                                      |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_ENABLE_NULL                   | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |\n    |                                  | enable the null backend.                                           |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_DECODING                   | Disables decoding APIs.                                            |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_ENCODING                   | Disables encoding APIs.                                            |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_WAV                        | Disables the built-in WAV decoder and encoder.                     |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_FLAC                       | Disables the built-in FLAC decoder.                                |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_MP3                        | Disables the built-in MP3 decoder.                                 |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_DEVICE_IO                  | Disables playback and recording. This will disable `ma_context`    |\n    |                                  | and `ma_device` APIs. This is useful if you only want to use       |\n    |                                  | miniaudio's data conversion and/or decoding APIs.                  |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_RESOURCE_MANAGER           | Disables the resource manager. When using the engine this will     |\n    |                                  | also disable the following functions:                              |\n    |                                  |                                                                    |\n    |                                  | ```                                                                |\n    |                                  | ma_sound_init_from_file()                                          |\n    |                                  | ma_sound_init_from_file_w()                                        |\n    |                                  | ma_sound_init_copy()                                               |\n    |                                  | ma_engine_play_sound_ex()                                          |\n    |                                  | ma_engine_play_sound()                                             |\n    |                                  | ```                                                                |\n    |                                  |                                                                    |\n    |                                  | The only way to initialize a `ma_sound` object is to initialize it |\n    |                                  | from a data source.                                                |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_NODE_GRAPH                 | Disables the node graph API. This will also disable the engine API |\n    |                                  | because it depends on the node graph.                              |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_ENGINE                     | Disables the engine API.                                           |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_THREADING                  | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and           |\n    |                                  | `ma_event` APIs. This option is useful if you only need to use     |\n    |                                  | miniaudio for data conversion, decoding and/or encoding. Some      |\n    |                                  | families of APIs require threading which means the following       |\n    |                                  | options must also be set:                                          |\n    |                                  |                                                                    |\n    |                                  |     ```                                                            |\n    |                                  |     MA_NO_DEVICE_IO                                                |\n    |                                  |     ```                                                            |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_GENERATION                 | Disables generation APIs such a `ma_waveform` and `ma_noise`.      |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_SSE2                       | Disables SSE2 optimizations.                                       |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_AVX2                       | Disables AVX2 optimizations.                                       |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_NEON                       | Disables NEON optimizations.                                       |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_NO_RUNTIME_LINKING            | Disables runtime linking. This is useful for passing Apple's       |\n    |                                  | notarization process. When enabling this, you may need to avoid    |\n    |                                  | using `-std=c89` or `-std=c99` on Linux builds or else you may end |\n    |                                  | up with compilation errors due to conflicts with `timespec` and    |\n    |                                  | `timeval` data types.                                              |\n    |                                  |                                                                    |\n    |                                  | You may need to enable this if your target platform does not allow |\n    |                                  | runtime linking via `dlopen()`.                                    |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_DEBUG_OUTPUT                  | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`).     |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_COINIT_VALUE                  | Windows only. The value to pass to internal calls to               |\n    |                                  | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`.            |\n    +----------------------------------+--------------------------------------------------------------------+\n    | MA_API                           | Controls how public APIs should be decorated. Default is `extern`. |\n    +----------------------------------+--------------------------------------------------------------------+\n\n\n3. Definitions\n==============\nThis section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity\nin the use of terms throughout the audio space, so this section is intended to clarify how miniaudio\nuses each term.\n\n3.1. Sample\n-----------\nA sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit\nfloating point number.\n\n3.2. Frame / PCM Frame\n----------------------\nA frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2\nsamples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms \"frame\"\nand \"PCM frame\" are the same thing in miniaudio. Note that this is different to a compressed frame.\nIf ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always\nclarify what it's referring to with something like \"FLAC frame\".\n\n3.3. Channel\n------------\nA stream of monaural audio that is emitted from an individual speaker in a speaker system, or\nreceived from an individual microphone in a microphone system. A stereo stream has two channels (a\nleft channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio\nsystems refer to a channel as a complex audio stream that's mixed with other channels to produce\nthe final mix - this is completely different to miniaudio's use of the term \"channel\" and should\nnot be confused.\n\n3.4. Sample Rate\n----------------\nThe sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number\nof PCM frames that are processed per second.\n\n3.5. Formats\n------------\nThroughout miniaudio you will see references to different sample formats:\n\n    +---------------+----------------------------------------+---------------------------+\n    | Symbol        | Description                            | Range                     |\n    +---------------+----------------------------------------+---------------------------+\n    | ma_format_f32 | 32-bit floating point                  | [-1, 1]                   |\n    | ma_format_s16 | 16-bit signed integer                  | [-32768, 32767]           |\n    | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607]       |\n    | ma_format_s32 | 32-bit signed integer                  | [-2147483648, 2147483647] |\n    | ma_format_u8  | 8-bit unsigned integer                 | [0, 255]                  |\n    +---------------+----------------------------------------+---------------------------+\n\nAll formats are native-endian.\n\n\n\n4. Data Sources\n===============\nThe data source abstraction in miniaudio is used for retrieving audio data from some source. A few\nexamples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data\nsources in order to make sense of some of the higher level concepts in miniaudio.\n\nThe `ma_data_source` API is a generic interface for reading from a data source. Any object that\nimplements the data source interface can be plugged into any `ma_data_source` function.\n\nTo read data from a data source:\n\n    ```c\n    ma_result result;\n    ma_uint64 framesRead;\n\n    result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to read data from the data source.\n    }\n    ```\n\nIf you don't need the number of frames that were successfully read you can pass in `NULL` to the\n`pFramesRead` parameter. If this returns a value less than the number of frames requested it means\nthe end of the file has been reached. `MA_AT_END` will be returned only when the number of frames\nread is 0.\n\nWhen calling any data source function, with the exception of `ma_data_source_init()` and\n`ma_data_source_uninit()`, you can pass in any object that implements a data source. For example,\nyou could plug in a decoder like so:\n\n    ```c\n    ma_result result;\n    ma_uint64 framesRead;\n    ma_decoder decoder;   // <-- This would be initialized with `ma_decoder_init_*()`.\n\n    result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to read data from the decoder.\n    }\n    ```\n\nIf you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you\ncan use `ma_data_source_seek_pcm_frames()`.\n\nTo seek to a specific PCM frame:\n\n    ```c\n    result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to seek to PCM frame.\n    }\n    ```\n\nYou can retrieve the total length of a data source in PCM frames, but note that some data sources\nmay not have the notion of a length, such as noise and waveforms, and others may just not have a\nway of determining the length such as some decoders. To retrieve the length:\n\n    ```c\n    ma_uint64 length;\n\n    result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to retrieve the length.\n    }\n    ```\n\nCare should be taken when retrieving the length of a data source where the underlying decoder is\npulling data from a data stream with an undefined length, such as internet radio or some kind of\nbroadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return.\n\nThe current position of the cursor in PCM frames can also be retrieved:\n\n    ```c\n    ma_uint64 cursor;\n\n    result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to retrieve the cursor.\n    }\n    ```\n\nYou will often need to know the data format that will be returned after reading. This can be\nretrieved like so:\n\n    ```c\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_channel channelMap[MA_MAX_CHANNELS];\n\n    result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to retrieve data format.\n    }\n    ```\n\nIf you do not need a specific data format property, just pass in NULL to the respective parameter.\n\nThere may be cases where you want to implement something like a sound bank where you only want to\nread data within a certain range of the underlying data. To do this you can use a range:\n\n    ```c\n    result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to set the range.\n    }\n    ```\n\nThis is useful if you have a sound bank where many sounds are stored in the same file and you want\nthe data source to only play one of those sub-sounds. Note that once the range is set, everything\nthat takes a position, such as cursors and loop points, should always be relatvie to the start of\nthe range. When the range is set, any previously defined loop point will be reset.\n\nCustom loop points can also be used with data sources. By default, data sources will loop after\nthey reach the end of the data source, but if you need to loop at a specific location, you can do\nthe following:\n\n    ```c\n    result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to set the loop point.\n    }\n    ```\n\nThe loop point is relative to the current range.\n\nIt's sometimes useful to chain data sources together so that a seamless transition can be achieved.\nTo do this, you can use chaining:\n\n    ```c\n    ma_decoder decoder1;\n    ma_decoder decoder2;\n\n    // ... initialize decoders with ma_decoder_init_*() ...\n\n    result = ma_data_source_set_next(&decoder1, &decoder2);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to set the next data source.\n    }\n\n    result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to read from the decoder.\n    }\n    ```\n\nIn the example above we're using decoders. When reading from a chain, you always want to read from\nthe top level data source in the chain. In the example above, `decoder1` is the top level data\nsource in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any\ngaps.\n\nNote that when looping is enabled, only the current data source will be looped. You can loop the\nentire chain by linking in a loop like so:\n\n    ```c\n    ma_data_source_set_next(&decoder1, &decoder2);  // decoder1 -> decoder2\n    ma_data_source_set_next(&decoder2, &decoder1);  // decoder2 -> decoder1 (loop back to the start).\n    ```\n\nNote that setting up chaining is not thread safe, so care needs to be taken if you're dynamically\nchanging links while the audio thread is in the middle of reading.\n\nDo not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple\ninstances of the same sound simultaneously. This can be extremely inefficient depending on the type\nof data source and can result in glitching due to subtle changes to the state of internal filters.\nInstead, initialize multiple data sources for each instance.\n\n\n4.1. Custom Data Sources\n------------------------\nYou can implement a custom data source by implementing the functions in `ma_data_source_vtable`.\nYour custom object must have `ma_data_source_base` as it's first member:\n\n    ```c\n    struct my_data_source\n    {\n        ma_data_source_base base;\n        ...\n    };\n    ```\n\nIn your initialization routine, you need to call `ma_data_source_init()` in order to set up the\nbase object (`ma_data_source_base`):\n\n    ```c\n    static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n    {\n        // Read data here. Output in the same format returned by my_data_source_get_data_format().\n    }\n\n    static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)\n    {\n        // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported.\n    }\n\n    static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n    {\n        // Return the format of the data here.\n    }\n\n    static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)\n    {\n        // Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor.\n    }\n\n    static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength)\n    {\n        // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown.\n    }\n\n    static ma_data_source_vtable g_my_data_source_vtable =\n    {\n        my_data_source_read,\n        my_data_source_seek,\n        my_data_source_get_data_format,\n        my_data_source_get_cursor,\n        my_data_source_get_length\n    };\n\n    ma_result my_data_source_init(my_data_source* pMyDataSource)\n    {\n        ma_result result;\n        ma_data_source_config baseConfig;\n\n        baseConfig = ma_data_source_config_init();\n        baseConfig.vtable = &g_my_data_source_vtable;\n\n        result = ma_data_source_init(&baseConfig, &pMyDataSource->base);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        // ... do the initialization of your custom data source here ...\n\n        return MA_SUCCESS;\n    }\n\n    void my_data_source_uninit(my_data_source* pMyDataSource)\n    {\n        // ... do the uninitialization of your custom data source here ...\n\n        // You must uninitialize the base data source.\n        ma_data_source_uninit(&pMyDataSource->base);\n    }\n    ```\n\nNote that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside\nof the custom data source. It's up to the custom data source itself to call these within their own\ninit/uninit functions.\n\n\n\n5. Engine\n=========\nThe `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The\n`ma_engine` object encapsulates a resource manager and a node graph, both of which will be\nexplained in more detail later.\n\nSounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing\ngroup called `ma_sound_group` which are also created from the engine. Both `ma_sound` and\n`ma_sound_group` objects are nodes within the engine's node graph.\n\nWhen the engine is initialized, it will normally create a device internally. If you would rather\nmanage the device yourself, you can do so and just pass a pointer to it via the engine config when\nyou initialize the engine. You can also just use the engine without a device, which again can be\nconfigured via the engine config.\n\nThe most basic way to initialize the engine is with a default config, like so:\n\n    ```c\n    ma_result result;\n    ma_engine engine;\n\n    result = ma_engine_init(NULL, &engine);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to initialize the engine.\n    }\n    ```\n\nThis will result in the engine initializing a playback device using the operating system's default\ndevice. This will be sufficient for many use cases, but if you need more flexibility you'll want to\nconfigure the engine with an engine config:\n\n    ```c\n    ma_result result;\n    ma_engine engine;\n    ma_engine_config engineConfig;\n\n    engineConfig = ma_engine_config_init();\n    engineConfig.pDevice = &myDevice;\n\n    result = ma_engine_init(&engineConfig, &engine);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to initialize the engine.\n    }\n    ```\n\nIn the example above we're passing in a pre-initialized device. Since the caller is the one in\ncontrol of the device's data callback, it's their responsibility to manually call\n`ma_engine_read_pcm_frames()` from inside their data callback:\n\n    ```c\n    void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)\n    {\n        ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL);\n    }\n    ```\n\nYou can also use the engine independent of a device entirely:\n\n    ```c\n    ma_result result;\n    ma_engine engine;\n    ma_engine_config engineConfig;\n\n    engineConfig = ma_engine_config_init();\n    engineConfig.noDevice   = MA_TRUE;\n    engineConfig.channels   = 2;        // Must be set when not using a device.\n    engineConfig.sampleRate = 48000;    // Must be set when not using a device.\n\n    result = ma_engine_init(&engineConfig, &engine);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to initialize the engine.\n    }\n    ```\n\nNote that when you're not using a device, you must set the channel count and sample rate in the\nconfig or else miniaudio won't know what to use (miniaudio will use the device to determine this\nnormally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio\ndata from the engine. This kind of setup is useful if you want to do something like offline\nprocessing or want to use a different audio system for playback such as SDL.\n\nWhen a sound is loaded it goes through a resource manager. By default the engine will initialize a\nresource manager internally, but you can also specify a pre-initialized resource manager:\n\n    ```c\n    ma_result result;\n    ma_engine engine1;\n    ma_engine engine2;\n    ma_engine_config engineConfig;\n\n    engineConfig = ma_engine_config_init();\n    engineConfig.pResourceManager = &myResourceManager;\n\n    ma_engine_init(&engineConfig, &engine1);\n    ma_engine_init(&engineConfig, &engine2);\n    ```\n\nIn this example we are initializing two engines, both of which are sharing the same resource\nmanager. This is especially useful for saving memory when loading the same file across multiple\nengines. If you were not to use a shared resource manager, each engine instance would use their own\nwhich would result in any sounds that are used between both engine's being loaded twice. By using\na shared resource manager, it would only be loaded once. Using multiple engine's is useful when you\nneed to output to multiple playback devices, such as in a local multiplayer game where each player\nis using their own set of headphones.\n\nBy default an engine will be in a started state. To make it so the engine is not automatically\nstarted you can configure it as such:\n\n    ```c\n    engineConfig.noAutoStart = MA_TRUE;\n\n    // The engine will need to be started manually.\n    ma_engine_start(&engine);\n\n    // Later on the engine can be stopped with ma_engine_stop().\n    ma_engine_stop(&engine);\n    ```\n\nThe concept of starting or stopping an engine is only relevant when using the engine with a\ndevice. Attempting to start or stop an engine that is not associated with a device will result in\n`MA_INVALID_OPERATION`.\n\nThe master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a\nlinear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you\nprefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear.\n\nWhen a sound is spatialized, it is done so relative to a listener. An engine can be configured to\nhave multiple listeners which can be configured via the config:\n\n    ```c\n    engineConfig.listenerCount = 2;\n    ```\n\nThe maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a\nsound is spatialized, it will be done so relative to the closest listener. You can also pin a sound\nto a specific listener which will be explained later. Listener's have a position, direction, cone,\nand velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up\nto the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The\nposition, direction and velocity are all specified in absolute terms:\n\n    ```c\n    ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ);\n    ```\n\nThe direction of the listener represents it's forward vector. The listener's up vector can also be\nspecified and defaults to +1 on the Y axis.\n\n    ```c\n    ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ);\n    ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0);\n    ```\n\nThe engine supports directional attenuation. The listener can have a cone the controls how sound is\nattenuated based on the listener's direction. When a sound is between the inner and outer cones, it\nwill be attenuated between 1 and the cone's outer gain:\n\n    ```c\n    ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain);\n    ```\n\nWhen a sound is inside the inner code, no directional attenuation is applied. When the sound is\noutside of the outer cone, the attenuation will be set to `outerGain` in the example above. When\nthe sound is in between the inner and outer cones, the attenuation will be interpolated between 1\nand the outer gain.\n\nThe engine's coordinate system follows the OpenGL coordinate system where positive X points right,\npositive Y points up and negative Z points forward.\n\nThe simplest and least flexible way to play a sound is like so:\n\n    ```c\n    ma_engine_play_sound(&engine, \"my_sound.wav\", pGroup);\n    ```\n\nThis is a \"fire and forget\" style of function. The engine will manage the `ma_sound` object\ninternally. When the sound finishes playing, it'll be put up for recycling. For more flexibility\nyou'll want to initialize a sound object:\n\n    ```c\n    ma_sound sound;\n\n    result = ma_sound_init_from_file(&engine, \"my_sound.wav\", flags, pGroup, NULL, &sound);\n    if (result != MA_SUCCESS) {\n        return result;  // Failed to load sound.\n    }\n    ```\n\nSounds need to be uninitialized with `ma_sound_uninit()`.\n\nThe example above loads a sound from a file. If the resource manager has been disabled you will not\nbe able to use this function and instead you'll need to initialize a sound directly from a data\nsource:\n\n    ```c\n    ma_sound sound;\n\n    result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n    ```\n\nEach `ma_sound` object represents a single instance of the sound. If you want to play the same\nsound multiple times at the same time, you need to initialize a separate `ma_sound` object.\n\nFor the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's\nstandard config/init pattern:\n\n    ```c\n    ma_sound sound;\n    ma_sound_config soundConfig;\n\n    soundConfig = ma_sound_config_init();\n    soundConfig.pFilePath   = NULL; // Set this to load from a file path.\n    soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source.\n    soundConfig.pInitialAttachment = &someNodeInTheNodeGraph;\n    soundConfig.initialAttachmentInputBusIndex = 0;\n    soundConfig.channelsIn  = 1;\n    soundConfig.channelsOut = 0;    // Set to 0 to use the engine's native channel count.\n\n    result = ma_sound_init_ex(&soundConfig, &sound);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n    ```\n\nIn the example above, the sound is being initialized without a file nor a data source. This is\nvalid, in which case the sound acts as a node in the middle of the node graph. This means you can\nconnect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly\nwhat a `ma_sound_group` is.\n\nWhen loading a sound, you specify a set of flags that control how the sound is loaded and what\nfeatures are enabled for that sound. When no flags are set, the sound will be fully loaded into\nmemory in exactly the same format as how it's stored on the file system. The resource manager will\nallocate a block of memory and then load the file directly into it. When reading audio data, it\nwill be decoded dynamically on the fly. In order to save processing time on the audio thread, it\nmight be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag:\n\n    ```c\n    ma_sound_init_from_file(&engine, \"my_sound.wav\", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound);\n    ```\n\nBy default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until\nthe sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously\nby specifying the `MA_SOUND_FLAG_ASYNC` flag:\n\n    ```c\n    ma_sound_init_from_file(&engine, \"my_sound.wav\", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound);\n    ```\n\nThis will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully\nloaded. When you start the sound, it won't output anything until some sound is available. The sound\nwill start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE`\nis specified.\n\nIf you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A\nfence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal\ncounter hit's zero. You can specify a fence like so:\n\n    ```c\n    ma_result result;\n    ma_fence fence;\n    ma_sound sounds[4];\n\n    result = ma_fence_init(&fence);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    // Load some sounds asynchronously.\n    for (int iSound = 0; iSound < 4; iSound += 1) {\n        ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]);\n    }\n\n    // ... do some other stuff here in the mean time ...\n\n    // Wait for all sounds to finish loading.\n    ma_fence_wait(&fence);\n    ```\n\nIf loading the entire sound into memory is prohibitive, you can also configure the engine to stream\nthe audio data:\n\n    ```c\n    ma_sound_init_from_file(&engine, \"my_sound.wav\", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound);\n    ```\n\nWhen streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work\nfine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music\ntracks in games.\n\nWhen loading a sound from a file path, the engine will reference count the file to prevent it from\nbeing loaded if it's already in memory. When you uninitialize a sound, the reference count will be\ndecremented, and if it hits zero, the sound will be unloaded from memory. This reference counting\nsystem is not used for streams. The engine will use a 64-bit hash of the file name when comparing\nfile paths which means there's a small chance you might encounter a name collision. If this is an\nissue, you'll need to use a different name for one of the colliding file paths, or just not load\nfrom files and instead load from a data source.\n\nYou can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this\nonly works for sounds that were initialized with `ma_sound_init_from_file()` and without the\n`MA_SOUND_FLAG_STREAM` flag.\n\nWhen you initialize a sound, if you specify a sound group the sound will be attached to that group\nautomatically. If you set it to NULL, it will be automatically attached to the engine's endpoint.\nIf you would instead rather leave the sound unattached by default, you can can specify the\n`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node\ngraph.\n\nSounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with\n`ma_sound_stop()`.\n\nSounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the\nengine's master volume.\n\nSounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan\nto 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas\n+1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger\nvalue will result in a higher pitch. The pitch must be greater than 0.\n\nThe engine supports 3D spatialization of sounds. By default sounds will have spatialization\nenabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways\nto disable spatialization of a sound:\n\n    ```c\n    // Disable spatialization at initialization time via a flag:\n    ma_sound_init_from_file(&engine, \"my_sound.wav\", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound);\n\n    // Dynamically disable or enable spatialization post-initialization:\n    ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled);\n    ```\n\nBy default sounds will be spatialized based on the closest listener. If a sound should always be\nspatialized relative to a specific listener it can be pinned to one:\n\n    ```c\n    ma_sound_set_pinned_listener_index(&sound, listenerIndex);\n    ```\n\nLike listeners, sounds have a position. By default, the position of a sound is in absolute space,\nbut it can be changed to be relative to a listener:\n\n    ```c\n    ma_sound_set_positioning(&sound, ma_positioning_relative);\n    ```\n\nNote that relative positioning of a sound only makes sense if there is either only one listener, or\nthe sound is pinned to a specific listener. To set the position of a sound:\n\n    ```c\n    ma_sound_set_position(&sound, posX, posY, posZ);\n    ```\n\nThe direction works the same way as a listener and represents the sound's forward direction:\n\n    ```c\n    ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ);\n    ```\n\nSound's also have a cone for controlling directional attenuation. This works exactly the same as\nlisteners:\n\n    ```c\n    ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain);\n    ```\n\nThe velocity of a sound is used for doppler effect and can be set as such:\n\n    ```c\n    ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ);\n    ```\n\nThe engine supports different attenuation models which can be configured on a per-sound basis. By\ndefault the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to\nOpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so:\n\n    ```c\n    ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse);\n    ```\n\nThe supported attenuation models include the following:\n\n    +----------------------------------+----------------------------------------------+\n    | ma_attenuation_model_none        | No distance attenuation.                     |\n    +----------------------------------+----------------------------------------------+\n    | ma_attenuation_model_inverse     | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. |\n    +----------------------------------+----------------------------------------------+\n    | ma_attenuation_model_linear      | Linear attenuation.                          |\n    +----------------------------------+----------------------------------------------+\n    | ma_attenuation_model_exponential | Exponential attenuation.                     |\n    +----------------------------------+----------------------------------------------+\n\nTo control how quickly a sound rolls off as it moves away from the listener, you need to configure\nthe rolloff:\n\n    ```c\n    ma_sound_set_rolloff(&sound, rolloff);\n    ```\n\nYou can control the minimum and maximum gain to apply from spatialization:\n\n    ```c\n    ma_sound_set_min_gain(&sound, minGain);\n    ma_sound_set_max_gain(&sound, maxGain);\n    ```\n\nLikewise, in the calculation of attenuation, you can control the minimum and maximum distances for\nthe attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain\nvolume after the listener moves further away and to have sounds play a maximum volume when the\nlistener is within a certain distance:\n\n    ```c\n    ma_sound_set_min_distance(&sound, minDistance);\n    ma_sound_set_max_distance(&sound, maxDistance);\n    ```\n\nThe engine's spatialization system supports doppler effect. The doppler factor can be configure on\na per-sound basis like so:\n\n    ```c\n    ma_sound_set_doppler_factor(&sound, dopplerFactor);\n    ```\n\nYou can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and\n`ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the\nstarting volume:\n\n    ```c\n    // Fade in over 1 second.\n    ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000);\n\n    // ... sometime later ...\n\n    // Fade out over 1 second, starting from the current volume.\n    ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000);\n    ```\n\nBy default sounds will start immediately, but sometimes for timing and synchronization purposes it\ncan be useful to schedule a sound to start or stop:\n\n    ```c\n    // Start the sound in 1 second from now.\n    ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1));\n\n    // Stop the sound in 2 seconds from now.\n    ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2));\n    ```\n\nNote that scheduling a start time still requires an explicit call to `ma_sound_start()` before\nanything will play.\n\nThe time is specified in global time which is controlled by the engine. You can get the engine's\ncurrent time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented\nautomatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()`\nin case it needs to be resynchronized for some reason.\n\nTo determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will\ntake the scheduled start and stop times into account.\n\nWhether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not\nbe looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping.\n\nUse `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping\nsound this should never return true. Alternatively, you can configure a callback that will be fired\nwhen the sound reaches the end. Note that the callback is fired from the audio thread which means\nyou cannot be uninitializing sound from the callback. To set the callback you can use\n`ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it\ninto the config like so:\n\n    ```c\n    soundConfig.endCallback = my_end_callback;\n    soundConfig.pEndCallbackUserData = pMyEndCallbackUserData;\n    ```\n\nThe end callback is declared like so:\n\n    ```c\n    void my_end_callback(void* pUserData, ma_sound* pSound)\n    {\n        ...\n    }\n    ```\n\nInternally a sound wraps around a data source. Some APIs exist to control the underlying data\nsource, mainly for convenience:\n\n    ```c\n    ma_sound_seek_to_pcm_frame(&sound, frameIndex);\n    ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity);\n    ma_sound_get_cursor_in_pcm_frames(&sound, &cursor);\n    ma_sound_get_length_in_pcm_frames(&sound, &length);\n    ```\n\nSound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do\nnot have any notion of a data source, anything relating to a data source is unavailable.\n\nInternally, sound data is loaded via the `ma_decoder` API which means by default it only supports\nfile formats that have built-in support in miniaudio. You can extend this to support any kind of\nfile format through the use of custom decoders. To do this you'll need to use a self-managed\nresource manager and configure it appropriately. See the \"Resource Management\" section below for\ndetails on how to set this up.\n\n\n6. Resource Management\n======================\nMany programs will want to manage sound resources for things such as reference counting and\nstreaming. This is supported by miniaudio via the `ma_resource_manager` API.\n\nThe resource manager is mainly responsible for the following:\n\n  * Loading of sound files into memory with reference counting.\n  * Streaming of sound data.\n\nWhen loading a sound file, the resource manager will give you back a `ma_data_source` compatible\nobject called `ma_resource_manager_data_source`. This object can be passed into any\n`ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you\nspecify whether or not you want the sound to be fully loaded into memory (and optionally\npre-decoded) or streamed. When loading into memory, you can also specify whether or not you want\nthe data to be loaded asynchronously.\n\nThe example below is how you can initialize a resource manager using it's default configuration:\n\n    ```c\n    ma_resource_manager_config config;\n    ma_resource_manager resourceManager;\n\n    config = ma_resource_manager_config_init();\n    result = ma_resource_manager_init(&config, &resourceManager);\n    if (result != MA_SUCCESS) {\n        ma_device_uninit(&device);\n        printf(\"Failed to initialize the resource manager.\");\n        return -1;\n    }\n    ```\n\nYou can configure the format, channels and sample rate of the decoded audio data. By default it\nwill use the file's native data format, but you can configure it to use a consistent format. This\nis useful for offloading the cost of data conversion to load time rather than dynamically\nconverting at mixing time. To do this, you configure the decoded format, channels and sample rate\nlike the code below:\n\n    ```c\n    config = ma_resource_manager_config_init();\n    config.decodedFormat     = device.playback.format;\n    config.decodedChannels   = device.playback.channels;\n    config.decodedSampleRate = device.sampleRate;\n    ```\n\nIn the code above, the resource manager will be configured so that any decoded audio data will be\npre-converted at load time to the device's native data format. If instead you used defaults and\nthe data format of the file did not match the device's data format, you would need to convert the\ndata at mixing time which may be prohibitive in high-performance and large scale scenarios like\ngames.\n\nInternally the resource manager uses the `ma_decoder` API to load sounds. This means by default it\nonly supports decoders that are built into miniaudio. It's possible to support additional encoding\nformats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable`\nvtables into the resource manager config:\n\n    ```c\n    ma_decoding_backend_vtable* pCustomBackendVTables[] =\n    {\n        &g_ma_decoding_backend_vtable_libvorbis,\n        &g_ma_decoding_backend_vtable_libopus\n    };\n\n    ...\n\n    resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables;\n    resourceManagerConfig.customDecodingBackendCount     = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);\n    resourceManagerConfig.pCustomDecodingBackendUserData = NULL;\n    ```\n\nThis system can allow you to support any kind of file format. See the \"Decoding\" section for\ndetails on how to implement custom decoders. The miniaudio repository includes examples for Opus\nvia libopus and libopusfile and Vorbis via libvorbis and libvorbisfile.\n\nAsynchronicity is achieved via a job system. When an operation needs to be performed, such as the\ndecoding of a page, a job will be posted to a queue which will then be processed by a job thread.\nBy default there will be only one job thread running, but this can be configured, like so:\n\n    ```c\n    config = ma_resource_manager_config_init();\n    config.jobThreadCount = MY_JOB_THREAD_COUNT;\n    ```\n\nBy default job threads are managed internally by the resource manager, however you can also self\nmanage your job threads if, for example, you want to integrate the job processing into your\nexisting job infrastructure, or if you simply don't like the way the resource manager does it. To\ndo this, just set the job thread count to 0 and process jobs manually. To process jobs, you first\nneed to retrieve a job using `ma_resource_manager_next_job()` and then process it using\n`ma_job_process()`:\n\n    ```c\n    config = ma_resource_manager_config_init();\n    config.jobThreadCount = 0;                            // Don't manage any job threads internally.\n    config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking.\n\n    // ... Initialize your custom job threads ...\n\n    void my_custom_job_thread(...)\n    {\n        for (;;) {\n            ma_job job;\n            ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job);\n            if (result != MA_SUCCESS) {\n                if (result == MA_NO_DATA_AVAILABLE) {\n                    // No jobs are available. Keep going. Will only get this if the resource manager was initialized\n                    // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING.\n                    continue;\n                } else if (result == MA_CANCELLED) {\n                    // MA_JOB_TYPE_QUIT was posted. Exit.\n                    break;\n                } else {\n                    // Some other error occurred.\n                    break;\n                }\n            }\n\n            ma_job_process(&job);\n        }\n    }\n    ```\n\nIn the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination\nindicator, but you can use whatever you would like to terminate the thread. The call to\n`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking\nby initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration\nflag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This\nis to give every thread the opportunity to catch the event and terminate naturally.\n\nWhen loading a file, it's sometimes convenient to be able to customize how files are opened and\nread instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by\ndefault. This can be done by setting `pVFS` member of the resource manager's config:\n\n    ```c\n    // Initialize your custom VFS object. See documentation for VFS for information on how to do this.\n    my_custom_vfs vfs = my_custom_vfs_init();\n\n    config = ma_resource_manager_config_init();\n    config.pVFS = &vfs;\n    ```\n\nThis is particularly useful in programs like games where you want to read straight from an archive\nrather than the normal file system. If you do not specify a custom VFS, the resource manager will\nuse the operating system's normal file operations.\n\nTo load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When\nloading a sound you need to specify the file path and options for how the sounds should be loaded.\nBy default a sound will be loaded synchronously. The returned data source is owned by the caller\nwhich means the caller is responsible for the allocation and freeing of the data source. Below is\nan example for initializing a data source:\n\n    ```c\n    ma_resource_manager_data_source dataSource;\n    ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource);\n    if (result != MA_SUCCESS) {\n        // Error.\n    }\n\n    // ...\n\n    // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call\n    // the `ma_data_source_read_pcm_frames()` like you would with any normal data source.\n    result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead);\n    if (result != MA_SUCCESS) {\n        // Failed to read PCM frames.\n    }\n\n    // ...\n\n    ma_resource_manager_data_source_uninit(&dataSource);\n    ```\n\nThe `flags` parameter specifies how you want to perform loading of the sound file. It can be a\ncombination of the following flags:\n\n    ```\n    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM\n    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE\n    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC\n    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT\n    ```\n\nWhen no flags are specified (set to 0), the sound will be fully loaded into memory, but not\ndecoded, meaning the raw file data will be stored in memory, and then dynamically decoded when\n`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in\nmemory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will\nbe loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after\nthe entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You\ncan instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag.\nThis will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be\nreturned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is\navailable because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by\n`ma_data_source_read_pcm_frames()`.\n\nFor large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you\ncan instead stream audio data which you can do by specifying the\n`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1\nsecond pages. When a new page needs to be decoded, a job will be posted to the job queue and then\nsubsequently processed in a job thread.\n\nFor in-memory sounds, reference counting is used to ensure the data is loaded only once. This means\nmultiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in\nthe file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be\nmatched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful\nfor a program to register self-managed raw audio data and associate it with a file path. Use the\n`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this.\n`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed\ndecoded audio data in the specified data format with the specified name. Likewise,\n`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed\nencoded audio data (the raw file data) with the specified name. Note that these names need not be\nactual file paths. When `ma_resource_manager_data_source_init()` is called (without the\n`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these\nexplicitly registered data buffers and, if found, will use it as the backing data for the data\nsource. Note that the resource manager does *not* make a copy of this data so it is up to the\ncaller to ensure the pointer stays valid for it's lifetime. Use\n`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use\n`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and\nunregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM`\nflag with a self-managed data pointer.\n\n\n6.1. Asynchronous Loading and Synchronization\n---------------------------------------------\nWhen loading asynchronously, it can be useful to poll whether or not loading has finished. Use\n`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will\nreturn `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded,\n`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed\nto load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been\ndecoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY`\nwill be returned. Otherwise, some other error code will be returned if the sound failed to load.\n\nIn addition to polling, you can also use a simple synchronization object called a \"fence\" to wait\nfor asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a\nfence is that it can be used to wait for a group of sounds to finish loading rather than waiting\nfor sounds on an individual basis. There are two stages to loading a sound:\n\n  * Initialization of the internal decoder; and\n  * Completion of decoding of the file (the file is fully decoded)\n\nYou can specify separate fences for each of the different stages. Waiting for the initialization\nof the internal decoder is important for when you need to know the sample format, channels and\nsample rate of the file.\n\nThe example below shows how you could use a fence when loading a number of sounds:\n\n    ```c\n    // This fence will be released when all sounds are finished loading entirely.\n    ma_fence fence;\n    ma_fence_init(&fence);\n\n    // This will be passed into the initialization routine for each sound.\n    ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();\n    notifications.done.pFence = &fence;\n\n    // Now load a bunch of sounds:\n    for (iSound = 0; iSound < soundCount; iSound += 1) {\n        ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, &notifications, &pSoundSources[iSound]);\n    }\n\n    // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ...\n\n    // Wait for loading of sounds to finish.\n    ma_fence_wait(&fence);\n    ```\n\nIn the example above we used a fence for waiting until the entire file has been fully decoded. If\nyou only need to wait for the initialization of the internal decoder to complete, you can use the\n`init` member of the `ma_resource_manager_pipeline_notifications` object:\n\n    ```c\n    notifications.init.pFence = &fence;\n    ```\n\nIf a fence is not appropriate for your situation, you can instead use a callback that is fired on\nan individual sound basis. This is done in a very similar way to fences:\n\n    ```c\n    typedef struct\n    {\n        ma_async_notification_callbacks cb;\n        void* pMyData;\n    } my_notification;\n\n    void my_notification_callback(ma_async_notification* pNotification)\n    {\n        my_notification* pMyNotification = (my_notification*)pNotification;\n\n        // Do something in response to the sound finishing loading.\n    }\n\n    ...\n\n    my_notification myCallback;\n    myCallback.cb.onSignal = my_notification_callback;\n    myCallback.pMyData     = pMyData;\n\n    ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();\n    notifications.done.pNotification = &myCallback;\n\n    ma_resource_manager_data_source_init(pResourceManager, \"my_sound.wav\", flags, &notifications, &mySound);\n    ```\n\nIn the example above we just extend the `ma_async_notification_callbacks` object and pass an\ninstantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with\nthe fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same\ntime and they should both work as expected. If using the `pNotification` system, you need to ensure\nyour `ma_async_notification_callbacks` object stays valid.\n\n\n\n6.2. Resource Manager Implementation Details\n--------------------------------------------\nResources are managed in two main ways:\n\n  * By storing the entire sound inside an in-memory buffer (referred to as a data buffer)\n  * By streaming audio data on the fly (referred to as a data stream)\n\nA resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or\ndata stream, depending on whether or not the data source was initialized with the\n`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a\n`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer`\nobject. Both of these objects are data sources which means they can be used with any\n`ma_data_source_*()` API.\n\nAnother major feature of the resource manager is the ability to asynchronously decode audio files.\nThis relieves the audio thread of time-consuming decoding which can negatively affect scalability\ndue to the audio thread needing to complete it's work extremely quickly to avoid glitching.\nAsynchronous decoding is achieved through a job system. There is a central multi-producer,\nmulti-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is\nposted to the queue which is then read by a job thread. The number of job threads can be\nconfigured for improved scalability, and job threads can all run in parallel without needing to\nworry about the order of execution (how this is achieved is explained below).\n\nWhen a sound is being loaded asynchronously, playback can begin before the sound has been fully\ndecoded. This enables the application to start playback of the sound quickly, while at the same\ntime allowing to resource manager to keep loading in the background. Since there may be less\nthreads than the number of sounds being loaded at a given time, a simple scheduling system is used\nto keep decoding time balanced and fair. The resource manager solves this by splitting decoding\ninto chunks called pages. By default, each page is 1 second long. When a page has been decoded, a\nnew job will be posted to start decoding the next page. By dividing up decoding into pages, an\nindividual sound shouldn't ever delay every other sound from having their first page decoded. Of\ncourse, when loading many sounds at the same time, there will always be an amount of time required\nto process jobs in the queue so in heavy load situations there will still be some delay. To\ndetermine if a data source is ready to have some frames read, use\n`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames\navailable starting from the current position.\n\n\n6.2.1. Job Queue\n----------------\nThe resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity.\nThis job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety.\nOnly a fixed number of jobs can be allocated and inserted into the queue which is done through a\nlock-free data structure for allocating an index into a fixed sized array, with reference counting\nfor mitigation of the ABA problem. The reference count is 32-bit.\n\nFor many types of jobs it's important that they execute in a specific order. In these cases, jobs\nare executed serially. For the resource manager, serial execution of jobs is only required on a\nper-object basis (per data buffer or per data stream). Each of these objects stores an execution\ncounter. When a job is posted it is associated with an execution counter. When the job is\nprocessed, it checks if the execution counter of the job equals the execution counter of the\nowning object and if so, processes the job. If the counters are not equal, the job will be posted\nback onto the job queue for later processing. When the job finishes processing the execution order\nof the main object is incremented. This system means the no matter how many job threads are\nexecuting, decoding of an individual sound will always get processed serially. The advantage to\nhaving multiple threads comes into play when loading multiple sounds at the same time.\n\nThe resource manager's job queue is not 100% lock-free and will use a spinlock to achieve\nthread-safety for a very small section of code. This is only relevant when the resource manager\nuses more than one job thread. If only using a single job thread, which is the default, the\nlock should never actually wait in practice. The amount of time spent locking should be quite\nshort, but it's something to be aware of for those who have pedantic lock-free requirements and\nneed to use more than one job thread. There are plans to remove this lock in a future version.\n\nIn addition, posting a job will release a semaphore, which on Win32 is implemented with\n`ReleaseSemaphore` and on POSIX platforms via a condition variable:\n\n    ```c\n    pthread_mutex_lock(&pSemaphore->lock);\n    {\n        pSemaphore->value += 1;\n        pthread_cond_signal(&pSemaphore->cond);\n    }\n    pthread_mutex_unlock(&pSemaphore->lock);\n    ```\n\nAgain, this is relevant for those with strict lock-free requirements in the audio thread. To avoid\nthis, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING`\nflag) and implement your own job processing routine (see the \"Resource Manager\" section above for\ndetails on how to do this).\n\n\n\n6.2.2. Data Buffers\n-------------------\nWhen the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the\nresource manager will try to load the data into an in-memory data buffer. Before doing so, however,\nit will first check if the specified file is already loaded. If so, it will increment a reference\ncounter and just use the already loaded data. This saves both time and memory. When the data buffer\nis uninitialized, the reference counter will be decremented. If the counter hits zero, the file\nwill be unloaded. This is a detail to keep in mind because it could result in excessive loading and\nunloading of a sound. For example, the following sequence will result in a file be loaded twice,\nonce after the other:\n\n    ```c\n    ma_resource_manager_data_source_init(pResourceManager, \"my_file\", ..., &myDataBuffer0); // Refcount = 1. Initial load.\n    ma_resource_manager_data_source_uninit(&myDataBuffer0);                                 // Refcount = 0. Unloaded.\n\n    ma_resource_manager_data_source_init(pResourceManager, \"my_file\", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it.\n    ma_resource_manager_data_source_uninit(&myDataBuffer1);                                 // Refcount = 0. Unloaded.\n    ```\n\nA binary search tree (BST) is used for storing data buffers as it has good balance between\nefficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed\ninto `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves\nmemory over storing the entire path, has faster comparisons, and results in a mostly balanced BST\ndue to the random nature of the hash. The disadvantages are that file names are case-sensitive and\nthere's a small chance of name collisions. If case-sensitivity is an issue, you should normalize\nyour file names to upper- or lower-case before initializing your data sources. If name collisions\nbecome an issue, you'll need to change the name of one of the colliding names or just not use the\nresource manager.\n\nWhen a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC`\nflag is excluded, the file will be decoded synchronously by the calling thread. There are two\noptions for controlling how the audio is stored in the data buffer - encoded or decoded. When the\n`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored\nin memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is\na very simple and standard process of simply adding an item to the BST, allocating a block of\nmemory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified).\n\nWhen the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer\nis done asynchronously. In this case, a job is posted to the queue to start loading and then the\nfunction immediately returns, setting an internal result code to `MA_BUSY`. This result code is\nreturned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully\ncompleted `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed.\n\nWhen loading asynchronously, a single job is posted to the queue of the type\n`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and\nassociating it with job. When the job is processed by the job thread, it will first load the file\nusing the VFS associated with the resource manager. When using a custom VFS, it's important that it\nbe completely thread-safe because it will be used from one or more job threads at the same time.\nIndividual files should only ever be accessed by one thread at a time, however. After opening the\nfile via the VFS, the job will determine whether or not the file is being decoded. If not, it\nsimply allocates a block of memory and loads the raw file contents into it and returns. On the\nother hand, when the file is being decoded, it will first allocate a decoder on the heap and\ninitialize it. Then it will check if the length of the file is known. If so it will allocate a\nblock of memory to store the decoded output and initialize it to silence. If the size is unknown,\nit will allocate room for one page. After memory has been allocated, the first page will be\ndecoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the\ncompletion event will be signalled and loading is now complete. If, however, there is more to\ndecode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job\nwill decode the next page and perform the same process if it reaches the end. If there is more to\ndecode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will\nkeep on happening until the sound has been fully decoded. For sounds of an unknown length, each\npage will be linked together as a linked list. Internally this is implemented via the\n`ma_paged_audio_buffer` object.\n\n\n6.2.3. Data Streams\n-------------------\nData streams only ever store two pages worth of data for each instance. They are most useful for\nlarge sounds like music tracks in games that would consume too much memory if fully decoded in\nmemory. After every frame from a page has been read, a job will be posted to load the next page\nwhich is done from the VFS.\n\nFor data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or\nnot initialization of the data source waits until the two pages have been decoded. When unset,\n`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise\nit will return immediately.\n\nWhen frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`,\n`MA_BUSY` will be returned if there are no frames available. If there are some frames available,\nbut less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames\nread will be less than the number requested. Due to the asynchronous nature of data streams,\nseeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be\nreturned when trying to read frames.\n\nWhen `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed\na job is posted to load the next page. This will be posted from the same thread that called\n`ma_resource_manager_data_source_read_pcm_frames()`.\n\nData streams are uninitialized by posting a job to the queue, but the function won't return until\nthat job has been processed. The reason for this is that the caller owns the data stream object and\ntherefore miniaudio needs to ensure everything completes before handing back control to the caller.\nAlso, if the data stream is uninitialized while pages are in the middle of decoding, they must\ncomplete before destroying any underlying object and the job system handles this cleanly.\n\nNote that when a new page needs to be loaded, a job will be posted to the resource manager's job\nthread from the audio thread. You must keep in mind the details mentioned in the \"Job Queue\"\nsection above regarding locking when posting an event if you require a strictly lock-free audio\nthread.\n\n\n\n7. Node Graph\n=============\nminiaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a\nnode whose outputs are attached to inputs of another node, thereby creating a graph. There are\ndifferent types of nodes, with each node in the graph processing input data to produce output,\nwhich is then fed through the chain. Each node in the graph can apply their own custom effects. At\nthe start of the graph will usually be one or more data source nodes which have no inputs and\ninstead pull their data from a data source. At the end of the graph is an endpoint which represents\nthe end of the chain and is where the final output is ultimately extracted from.\n\nEach node has a number of input buses and a number of output buses. An output bus from a node is\nattached to an input bus of another. Multiple nodes can connect their output buses to another\nnode's input bus, in which case their outputs will be mixed before processing by the node. Below is\na diagram that illustrates a hypothetical node graph setup:\n\n    ```\n    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n    +---------------+                              +-----------------+\n    | Data Source 1 =----+    +----------+    +----= Low Pass Filter =----+\n    +---------------+    |    |          =----+    +-----------------+    |    +----------+\n                         +----= Splitter |                                +----= ENDPOINT |\n    +---------------+    |    |          =----+    +-----------------+    |    +----------+\n    | Data Source 2 =----+    +----------+    +----=  Echo / Delay   =----+\n    +---------------+                              +-----------------+\n    ```\n\nIn the above graph, it starts with two data sources whose outputs are attached to the input of a\nsplitter node. It's at this point that the two data sources are mixed. After mixing, the splitter\nperforms it's processing routine and produces two outputs which is simply a duplication of the\ninput stream. One output is attached to a low pass filter, whereas the other output is attached to\na echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and\nsince they're both connected to the same input bus, they'll be mixed.\n\nEach input bus must be configured to accept the same number of channels, but the number of channels\nused by input buses can be different to the number of channels for output buses in which case\nminiaudio will automatically convert the input data to the output channel count before processing.\nThe number of channels of an output bus of one node must match the channel count of the input bus\nit's attached to. The channel counts cannot be changed after the node has been initialized. If you\nattempt to attach an output bus to an input bus with a different channel count, attachment will\nfail.\n\nTo use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a\ncontainer around the entire graph. The `ma_node_graph` object is required for some thread-safety\nissues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's\nstandard config/init system:\n\n    ```c\n    ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount);\n\n    result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph);    // Second parameter is a pointer to allocation callbacks.\n    if (result != MA_SUCCESS) {\n        // Failed to initialize node graph.\n    }\n    ```\n\nWhen you initialize the node graph, you're specifying the channel count of the endpoint. The\nendpoint is a special node which has one input bus and one output bus, both of which have the\nsame channel count, which is specified in the config. Any nodes that connect directly to the\nendpoint must be configured such that their output buses have the same channel count. When you read\naudio data from the node graph, it'll have the channel count you specified in the config. To read\ndata from the graph:\n\n    ```c\n    ma_uint32 framesRead;\n    result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead);\n    if (result != MA_SUCCESS) {\n        // Failed to read data from the node graph.\n    }\n    ```\n\nWhen you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in\ndata from it's input attachments, which in turn recursively pull in data from their inputs, and so\non. At the start of the graph there will be some kind of data source node which will have zero\ninputs and will instead read directly from a data source. The base nodes don't literally need to\nread from a `ma_data_source` object, but they will always have some kind of underlying object that\nsources some kind of audio. The `ma_data_source_node` node can be used to read from a\n`ma_data_source`. Data is always in floating-point format and in the number of channels you\nspecified when the graph was initialized. The sample rate is defined by the underlying data sources.\nIt's up to you to ensure they use a consistent and appropriate sample rate.\n\nThe `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but\nminiaudio includes a few stock nodes for common functionality. This is how you would initialize a\nnode which reads directly from a data source (`ma_data_source_node`) which is an example of one\nof the stock nodes that comes with miniaudio:\n\n    ```c\n    ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource);\n\n    ma_data_source_node dataSourceNode;\n    result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode);\n    if (result != MA_SUCCESS) {\n        // Failed to create data source node.\n    }\n    ```\n\nThe data source node will use the output channel count to determine the channel count of the output\nbus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data\nsource). The data source must output to floating-point (`ma_format_f32`) or else an error will be\nreturned from `ma_data_source_node_init()`.\n\nBy default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`:\n\n    ```c\n    result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0);\n    if (result != MA_SUCCESS) {\n        // Failed to attach node.\n    }\n    ```\n\nThe code above connects the data source node directly to the endpoint. Since the data source node\nhas only a single output bus, the index will always be 0. Likewise, the endpoint only has a single\ninput bus which means the input bus index will also always be 0.\n\nTo detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use\n`ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to\nanother, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll\ndeal with it for you.\n\nLess frequently you may want to create a specialized node. This will be a node where you implement\nyour own processing callback to apply a custom effect of some kind. This is similar to initializing\none of the stock node types, only this time you need to specify a pointer to a vtable containing a\npointer to the processing function and the number of input and output buses. Example:\n\n    ```c\n    static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n    {\n        // Do some processing of ppFramesIn (one stream of audio data per input bus)\n        const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0.\n        const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1.\n        float* pFramesOut_0 = ppFramesOut[0];     // Output bus @ index 0.\n\n        // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each\n        // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers\n        // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames\n        // your node consumed and `pFrameCountOut` should be set the number of output frames that\n        // were produced.\n        //\n        // You should process as many frames as you can. If your effect consumes input frames at the\n        // same rate as output frames (always the case, unless you're doing resampling), you need\n        // only look at `ppFramesOut` and process that exact number of frames. If you're doing\n        // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut`\n        // properly.\n    }\n\n    static ma_node_vtable my_custom_node_vtable =\n    {\n        my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing.\n        NULL,   // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.\n        2,      // 2 input buses.\n        1,      // 1 output bus.\n        0       // Default flags.\n    };\n\n    ...\n\n    // Each bus needs to have a channel count specified. To do this you need to specify the channel\n    // counts in an array and then pass that into the node config.\n    ma_uint32 inputChannels[2];     // Equal in size to the number of input channels specified in the vtable.\n    ma_uint32 outputChannels[1];    // Equal in size to the number of output channels specified in the vtable.\n\n    inputChannels[0]  = channelsIn;\n    inputChannels[1]  = channelsIn;\n    outputChannels[0] = channelsOut;\n\n    ma_node_config nodeConfig = ma_node_config_init();\n    nodeConfig.vtable          = &my_custom_node_vtable;\n    nodeConfig.pInputChannels  = inputChannels;\n    nodeConfig.pOutputChannels = outputChannels;\n\n    ma_node_base node;\n    result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node);\n    if (result != MA_SUCCESS) {\n        // Failed to initialize node.\n    }\n    ```\n\nWhen initializing a custom node, as in the code above, you'll normally just place your vtable in\nstatic space. The number of input and output buses are specified as part of the vtable. If you need\na variable number of buses on a per-node bases, the vtable should have the relevant bus count set\nto `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config:\n\n    ```c\n    static ma_node_vtable my_custom_node_vtable =\n    {\n        my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing.\n        NULL,   // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.\n        MA_NODE_BUS_COUNT_UNKNOWN,  // The number of input buses is determined on a per-node basis.\n        1,      // 1 output bus.\n        0       // Default flags.\n    };\n\n    ...\n\n    ma_node_config nodeConfig = ma_node_config_init();\n    nodeConfig.vtable          = &my_custom_node_vtable;\n    nodeConfig.inputBusCount   = myBusCount;        // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here.\n    nodeConfig.pInputChannels  = inputChannels;     // <-- Make sure there are nodeConfig.inputBusCount elements in this array.\n    nodeConfig.pOutputChannels = outputChannels;    // <-- The vtable specifies 1 output bus, so there must be 1 element in this array.\n    ```\n\nIn the above example it's important to never set the `inputBusCount` and `outputBusCount` members\nto anything other than their defaults if the vtable specifies an explicit count. They can only be\nset if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count.\n\nMost often you'll want to create a structure to encapsulate your node with some extra data. You\nneed to make sure the `ma_node_base` object is your first member of the structure:\n\n    ```c\n    typedef struct\n    {\n        ma_node_base base; // <-- Make sure this is always the first member.\n        float someCustomData;\n    } my_custom_node;\n    ```\n\nBy doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the\ngraph just like any other node.\n\nIn the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the\nnumber of channels for each bus is what was specified by the config when the node was initialized\nwith `ma_node_init()`. In addition, all attachments to each of the input buses will have been\npre-mixed by miniaudio. The config allows you to specify different channel counts for each\nindividual input and output bus. It's up to the effect to handle it appropriate, and if it can't,\nreturn an error in it's initialization routine.\n\nCustom nodes can be assigned some flags to describe their behaviour. These are set via the vtable\nand include the following:\n\n    +-----------------------------------------+---------------------------------------------------+\n    | Flag Name                               | Description                                       |\n    +-----------------------------------------+---------------------------------------------------+\n    | MA_NODE_FLAG_PASSTHROUGH                | Useful for nodes that do not do any kind of audio |\n    |                                         | processing, but are instead used for tracking     |\n    |                                         | time, handling events, etc. Also used by the      |\n    |                                         | internal endpoint node. It reads directly from    |\n    |                                         | the input bus to the output bus. Nodes with this  |\n    |                                         | flag must have exactly 1 input bus and 1 output   |\n    |                                         | bus, and both buses must have the same channel    |\n    |                                         | counts.                                           |\n    +-----------------------------------------+---------------------------------------------------+\n    | MA_NODE_FLAG_CONTINUOUS_PROCESSING      | Causes the processing callback to be called even  |\n    |                                         | when no data is available to be read from input   |\n    |                                         | attachments. When a node has at least one input   |\n    |                                         | bus, but there are no inputs attached or the      |\n    |                                         | inputs do not deliver any data, the node's        |\n    |                                         | processing callback will not get fired. This flag |\n    |                                         | will make it so the callback is always fired      |\n    |                                         | regardless of whether or not any input data is    |\n    |                                         | received. This is useful for effects like         |\n    |                                         | echos where there will be a tail of audio data    |\n    |                                         | that still needs to be processed even when the    |\n    |                                         | original data sources have reached their ends. It |\n    |                                         | may also be useful for nodes that must always     |\n    |                                         | have their processing callback fired when there   |\n    |                                         | are no inputs attached.                           |\n    +-----------------------------------------+---------------------------------------------------+\n    | MA_NODE_FLAG_ALLOW_NULL_INPUT           | Used in conjunction with                          |\n    |                                         | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this   |\n    |                                         | is set, the `ppFramesIn` parameter of the         |\n    |                                         | processing callback will be set to NULL when      |\n    |                                         | there are no input frames are available. When     |\n    |                                         | this is unset, silence will be posted to the      |\n    |                                         | processing callback.                              |\n    +-----------------------------------------+---------------------------------------------------+\n    | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output      |\n    |                                         | frames are processed at different rates. You      |\n    |                                         | should set this for any nodes that perform        |\n    |                                         | resampling.                                       |\n    +-----------------------------------------+---------------------------------------------------+\n    | MA_NODE_FLAG_SILENT_OUTPUT              | Used to tell miniaudio that a node produces only  |\n    |                                         | silent output. This is useful for nodes where you |\n    |                                         | don't want the output to contribute to the final  |\n    |                                         | mix. An example might be if you want split your   |\n    |                                         | stream and have one branch be output to a file.   |\n    |                                         | When using this flag, you should avoid writing to |\n    |                                         | the output buffer of the node's processing        |\n    |                                         | callback because miniaudio will ignore it anyway. |\n    +-----------------------------------------+---------------------------------------------------+\n\n\nIf you need to make a copy of an audio stream for effect processing you can use a splitter node\ncalled `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses.\nYou can use it like this:\n\n    ```c\n    ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels);\n\n    ma_splitter_node splitterNode;\n    result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode);\n    if (result != MA_SUCCESS) {\n        // Failed to create node.\n    }\n\n    // Attach your output buses to two different input buses (can be on two different nodes).\n    ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint.\n    ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode,                          0); // Attach to input bus 0 of some effect node.\n    ```\n\nThe volume of an output bus can be configured on a per-bus basis:\n\n    ```c\n    ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f);\n    ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f);\n    ```\n\nIn the code above we're using the splitter node from before and changing the volume of each of the\ncopied streams.\n\nYou can start and stop a node with the following:\n\n    ```c\n    ma_node_set_state(&splitterNode, ma_node_state_started);    // The default state.\n    ma_node_set_state(&splitterNode, ma_node_state_stopped);\n    ```\n\nBy default the node is in a started state, but since it won't be connected to anything won't\nactually be invoked by the node graph until it's connected. When you stop a node, data will not be\nread from any of it's input connections. You can use this property to stop a group of sounds\natomically.\n\nYou can configure the initial state of a node in it's config:\n\n    ```c\n    nodeConfig.initialState = ma_node_state_stopped;\n    ```\n\nNote that for the stock specialized nodes, all of their configs will have a `nodeConfig` member\nwhich is the config to use with the base node. This is where the initial state can be configured\nfor specialized nodes:\n\n    ```c\n    dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped;\n    ```\n\nWhen using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not\nmodify the `vtable` member of the `nodeConfig` object.\n\n\n7.1. Timing\n-----------\nThe node graph supports starting and stopping nodes at scheduled times. This is especially useful\nfor data source nodes where you want to get the node set up, but only start playback at a specific\ntime. There are two clocks: local and global.\n\nA local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can\nonly be done based on the global clock because the local clock will not be running while the node\nis stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the\nother hand, the local clock only advances when the node's processing callback is fired, and is\nadvanced based on the output frame count.\n\nTo retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with\n`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline.\nGetting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time,\nand `ma_node_set_time()` to set the local time. The global and local times will be advanced by the\naudio thread, so care should be taken to avoid data races. Ideally you should avoid calling these\noutside of the node processing callbacks which are always run on the audio thread.\n\nThere is basic support for scheduling the starting and stopping of nodes. You can only schedule one\nstart and one stop at a time. This is mainly intended for putting nodes into a started or stopped\nstate in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited\nto the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks\nof several milliseconds. The following APIs can be used for scheduling node states:\n\n    ```c\n    ma_node_set_state_time()\n    ma_node_get_state_time()\n    ```\n\nThe time is absolute and must be based on the global clock. An example is below:\n\n    ```c\n    ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1);   // Delay starting to 1 second.\n    ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5);   // Delay stopping to 5 seconds.\n    ```\n\nAn example for changing the state using a relative time.\n\n    ```c\n    ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph));\n    ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph));\n    ```\n\nNote that due to the nature of multi-threading the times may not be 100% exact. If this is an\nissue, consider scheduling state changes from within a processing callback. An idea might be to\nhave some kind of passthrough trigger node that is used specifically for tracking time and handling\nevents.\n\n\n\n7.2. Thread Safety and Locking\n------------------------------\nWhen processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's\nexpected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so\nwithout the use of any locks. This section discusses the implementation used by miniaudio and goes\nover some of the compromises employed by miniaudio to achieve this goal. Note that the current\nimplementation may not be ideal - feedback and critiques are most welcome.\n\nThe node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected\nto be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the\nimplementation, but are crafted in a way such that such locking is not required when reading audio\ndata from the graph. Locking in these areas are achieved by means of spinlocks.\n\nThe main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact\nthat a node can be uninitialized, and it's memory potentially freed, while in the middle of being\nprocessed on the audio thread. There are times when the audio thread will be referencing a node,\nwhich means the uninitialization process of a node needs to make sure it delays returning until the\naudio thread is finished so that control is not handed back to the caller thereby giving them a\nchance to free the node's memory.\n\nWhen the audio thread is processing a node, it does so by reading from each of the output buses of\nthe node. In order for a node to process data for one of it's output buses, it needs to read from\neach of it's input buses, and so on an so forth. It follows that once all output buses of a node\nare detached, the node as a whole will be disconnected and no further processing will occur unless\nit's output buses are reattached, which won't be happening when the node is being uninitialized.\nBy having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can\nsimplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By\ndoing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output\nnodes, followed by each of the attachments to each of it's input nodes, and then do any final clean\nup.\n\nWith the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as\nit takes to process the output bus being detached. This will happen if it's called at just the\nwrong moment where the audio thread has just iterated it and has just started processing. The\ncaller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which\nincludes the cost of recursively processing it's inputs. This is the biggest compromise made with\nthe approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes\nearlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching\nhigher level nodes, such as some kind of final post-processing endpoint. If you need to do mass\ndetachments, detach starting from the lowest level nodes and work your way towards the final\nendpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not\nrunning, detachment will be fast and detachment in any order will be the same. The reason nodes\nneed to wait for their input attachments to complete is due to the potential for desyncs between\ndata sources. If the node was to terminate processing mid way through processing it's inputs,\nthere's a chance that some of the underlying data sources will have been read, but then others not.\nThat will then result in a potential desynchronization when detaching and reattaching higher-level\nnodes. A possible solution to this is to have an option when detaching to terminate processing\nbefore processing all input attachments which should be fairly simple.\n\nAnother compromise, albeit less significant, is locking when attaching and detaching nodes. This\nlocking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present\nfor each input bus and output bus. When an output bus is connected to an input bus, both the output\nbus and input bus is locked. This locking is specifically for attaching and detaching across\ndifferent threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and\nunlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when\nconsidering that iterating over attachments must not break as a result of attaching or detaching a\nnode while iteration is occurring.\n\nAttaching and detaching are both quite simple. When an output bus of a node is attached to an input\nbus of another node, it's added to a linked list. Basically, an input bus is a linked list, where\neach item in the list is and output bus. We have some intentional (and convenient) restrictions on\nwhat can done with the linked list in order to simplify the implementation. First of all, whenever\nsomething needs to iterate over the list, it must do so in a forward direction. Backwards iteration\nis not supported. Also, items can only be added to the start of the list.\n\nThe linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer\nto the next item in the list, and another to the previous item. A pointer to the previous item is\nonly required for fast detachment of the node - it is never used in iteration. This is an\nimportant property because it means from the perspective of iteration, attaching and detaching of\nan item can be done with a single atomic assignment. This is exploited by both the attachment and\ndetachment process. When attaching the node, the first thing that is done is the setting of the\nlocal \"next\" and \"previous\" pointers of the node. After that, the item is \"attached\" to the list\nby simply performing an atomic exchange with the head pointer. After that, the node is \"attached\"\nto the list from the perspective of iteration. Even though the \"previous\" pointer of the next item\nhasn't yet been set, from the perspective of iteration it's been attached because iteration will\nonly be happening in a forward direction which means the \"previous\" pointer won't actually ever get\nused. The same general process applies to detachment. See `ma_node_attach_output_bus()` and\n`ma_node_detach_output_bus()` for the implementation of this mechanism.\n\n\n\n8. Decoding\n===========\nThe `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from\ndevices and can be used independently. Built-in support is included for the following formats:\n\n    +---------+\n    | Format  |\n    +---------+\n    | WAV     |\n    | MP3     |\n    | FLAC    |\n    +---------+\n\nYou can disable the built-in decoders by specifying one or more of the following options before the\nminiaudio implementation:\n\n    ```c\n    #define MA_NO_WAV\n    #define MA_NO_MP3\n    #define MA_NO_FLAC\n    ```\n\nminiaudio supports the ability to plug in custom decoders. See the section below for details on how\nto use custom decoders.\n\nA decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with\n`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is\nan example for loading a decoder from a file:\n\n    ```c\n    ma_decoder decoder;\n    ma_result result = ma_decoder_init_file(\"MySong.mp3\", NULL, &decoder);\n    if (result != MA_SUCCESS) {\n        return false;   // An error occurred.\n    }\n\n    ...\n\n    ma_decoder_uninit(&decoder);\n    ```\n\nWhen initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object\n(the `NULL` argument in the example above) which allows you to configure the output format, channel\ncount, sample rate and channel map:\n\n    ```c\n    ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000);\n    ```\n\nWhen passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the\nsame as that defined by the decoding backend.\n\nData is read from the decoder as PCM frames. This will output the number of PCM frames actually\nread. If this is less than the requested number of PCM frames it means you've reached the end. The\nreturn value will be `MA_AT_END` if no samples have been read and the end has been reached.\n\n    ```c\n    ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead);\n    if (framesRead < framesToRead) {\n        // Reached the end.\n    }\n    ```\n\nYou can also seek to a specific frame like so:\n\n    ```c\n    ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame);\n    if (result != MA_SUCCESS) {\n        return false;   // An error occurred.\n    }\n    ```\n\nIf you want to loop back to the start, you can simply seek back to the first PCM frame:\n\n    ```c\n    ma_decoder_seek_to_pcm_frame(pDecoder, 0);\n    ```\n\nWhen loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding\nbackend. This can be unnecessarily inefficient if the type is already known. In this case you can\nuse `encodingFormat` variable in the device config to specify a specific encoding format you want\nto decode:\n\n    ```c\n    decoderConfig.encodingFormat = ma_encoding_format_wav;\n    ```\n\nSee the `ma_encoding_format` enum for possible encoding formats.\n\nThe `ma_decoder_init_file()` API will try using the file extension to determine which decoding\nbackend to prefer.\n\n\n8.1. Custom Decoders\n--------------------\nIt's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful\nwhen you want to use the `ma_decoder` API, but need to support an encoding format that's not one of\nthe stock formats supported by miniaudio. This can be put to particularly good use when using the\n`ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for\nexample, you wanted to support Opus, you can do so with a custom decoder (there if a reference\nOpus decoder in the \"extras\" folder of the miniaudio repository which uses libopus + libopusfile).\n\nA custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs\nto be implemented which is then passed into the decoder config:\n\n    ```c\n    ma_decoding_backend_vtable* pCustomBackendVTables[] =\n    {\n        &g_ma_decoding_backend_vtable_libvorbis,\n        &g_ma_decoding_backend_vtable_libopus\n    };\n\n    ...\n\n    decoderConfig = ma_decoder_config_init_default();\n    decoderConfig.pCustomBackendUserData = NULL;\n    decoderConfig.ppCustomBackendVTables = pCustomBackendVTables;\n    decoderConfig.customBackendCount     = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);\n    ```\n\nThe `ma_decoding_backend_vtable` vtable has the following functions:\n\n    ```\n    onInit\n    onInitFile\n    onInitFileW\n    onInitMemory\n    onUninit\n    ```\n\nThere are only two functions that must be implemented - `onInit` and `onUninit`. The other\nfunctions can be implemented for a small optimization for loading from a file path or memory. If\nthese are not specified, miniaudio will deal with it for you via a generic implementation.\n\nWhen you initialize a custom data source (by implementing the `onInit` function in the vtable) you\nwill need to output a pointer to a `ma_data_source` which implements your custom decoder. See the\nsection about data sources for details on how to implement this. Alternatively, see the\n\"custom_decoders\" example in the miniaudio repository.\n\nThe `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data\nfrom some arbitrary source. You'll use these functions to read from the raw data and perform the\ndecoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant\nparameter.\n\nThe `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only\nused as a hint and can be ignored. However, if any of the properties are relevant to your decoder,\nan optimal implementation will handle the relevant properties appropriately.\n\nIf memory allocation is required, it should be done so via the specified allocation callbacks if\npossible (the `pAllocationCallbacks` parameter).\n\nIf an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to\nNULL, and make sure everything is cleaned up appropriately and an appropriate result code returned.\nWhen multiple custom backends are specified, miniaudio will cycle through the vtables in the order\nthey're listed in the array that's passed into the decoder config so it's important that your\ninitialization routine is clean.\n\nWhen a decoder is uninitialized, the `onUninit` callback will be fired which will give you an\nopportunity to clean up and internal data.\n\n\n\n9. Encoding\n===========\nThe `ma_encoding` API is used for writing audio files. The only supported output format is WAV.\nThis can be disabled by specifying the following option before the implementation of miniaudio:\n\n    ```c\n    #define MA_NO_WAV\n    ```\n\nAn encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data\ndelivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder\nto output to a file.\n\n    ```c\n    ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE);\n    ma_encoder encoder;\n    ma_result result = ma_encoder_init_file(\"my_file.wav\", &config, &encoder);\n    if (result != MA_SUCCESS) {\n        // Error\n    }\n\n    ...\n\n    ma_encoder_uninit(&encoder);\n    ```\n\nWhen initializing an encoder you must specify a config which is initialized with\n`ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output\nchannel count and output sample rate. The following file types are supported:\n\n    +------------------------+-------------+\n    | Enum                   | Description |\n    +------------------------+-------------+\n    | ma_encoding_format_wav | WAV         |\n    +------------------------+-------------+\n\nIf the format, channel count or sample rate is not supported by the output file type an error will\nbe returned. The encoder will not perform data conversion so you will need to convert it before\noutputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the\nexample below:\n\n    ```c\n    ma_uint64 framesWritten;\n    result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten);\n    if (result != MA_SUCCESS) {\n        ... handle error ...\n    }\n    ```\n\nThe `framesWritten` variable will contain the number of PCM frames that were actually written. This\nis optionally and you can pass in `NULL` if you need this.\n\nEncoders must be uninitialized with `ma_encoder_uninit()`.\n\n\n\n10. Data Conversion\n===================\nA data conversion API is included with miniaudio which supports the majority of data conversion\nrequirements. This supports conversion between sample formats, channel counts (with channel\nmapping) and sample rates.\n\n\n10.1. Sample Format Conversion\n------------------------------\nConversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and\n`ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific\nformats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use\n`ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count\nand channel count as a variable instead of the total sample count.\n\n\n10.1.1. Dithering\n-----------------\nDithering can be set using the ditherMode parameter.\n\nThe different dithering modes include the following, in order of efficiency:\n\n    +-----------+--------------------------+\n    | Type      | Enum Token               |\n    +-----------+--------------------------+\n    | None      | ma_dither_mode_none      |\n    | Rectangle | ma_dither_mode_rectangle |\n    | Triangle  | ma_dither_mode_triangle  |\n    +-----------+--------------------------+\n\nNote that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be\nignored for conversions where dithering is not needed. Dithering is available for the following\nconversions:\n\n    ```\n    s16 -> u8\n    s24 -> u8\n    s32 -> u8\n    f32 -> u8\n    s24 -> s16\n    s32 -> s16\n    f32 -> s16\n    ```\n\nNote that it is not an error to pass something other than ma_dither_mode_none for conversions where\ndither is not used. It will just be ignored.\n\n\n\n10.2. Channel Conversion\n------------------------\nChannel conversion is used for channel rearrangement and conversion from one channel count to\nanother. The `ma_channel_converter` API is used for channel conversion. Below is an example of\ninitializing a simple channel converter which converts from mono to stereo.\n\n    ```c\n    ma_channel_converter_config config = ma_channel_converter_config_init(\n        ma_format,                      // Sample format\n        1,                              // Input channels\n        NULL,                           // Input channel map\n        2,                              // Output channels\n        NULL,                           // Output channel map\n        ma_channel_mix_mode_default);   // The mixing algorithm to use when combining channels.\n\n    result = ma_channel_converter_init(&config, NULL, &converter);\n    if (result != MA_SUCCESS) {\n        // Error.\n    }\n    ```\n\nTo perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so:\n\n    ```c\n    ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount);\n    if (result != MA_SUCCESS) {\n        // Error.\n    }\n    ```\n\nIt is up to the caller to ensure the output buffer is large enough to accommodate the new PCM\nframes.\n\nInput and output PCM frames are always interleaved. Deinterleaved layouts are not supported.\n\n\n10.2.1. Channel Mapping\n-----------------------\nIn addition to converting from one channel count to another, like the example above, the channel\nconverter can also be used to rearrange channels. When initializing the channel converter, you can\noptionally pass in channel maps for both the input and output frames. If the channel counts are the\nsame, and each channel map contains the same channel positions with the exception that they're in\na different order, a simple shuffling of the channels will be performed. If, however, there is not\na 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed\nbased on a mixing mode which is specified when initializing the `ma_channel_converter_config`\nobject.\n\nWhen converting from mono to multi-channel, the mono channel is simply copied to each output\nchannel. When going the other way around, the audio of each output channel is simply averaged and\ncopied to the mono channel.\n\nIn more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess\nchannels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th\nchannels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and\n4th channels.\n\nThe `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a\nsimple distribution between input and output. Imagine sitting in the middle of a room, with\nspeakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be\nthought of as being in the corner of the front and left walls.\n\nFinally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined\nweights. Custom weights can be passed in as the last parameter of\n`ma_channel_converter_config_init()`.\n\nPredefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a\n`ma_standard_channel_map` enum as it's first parameter, which can be one of the following:\n\n    +-----------------------------------+-----------------------------------------------------------+\n    | Name                              | Description                                               |\n    +-----------------------------------+-----------------------------------------------------------+\n    | ma_standard_channel_map_default   | Default channel map used by miniaudio. See below.         |\n    | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps.    |\n    | ma_standard_channel_map_alsa      | Default ALSA channel map.                                 |\n    | ma_standard_channel_map_rfc3551   | RFC 3551. Based on AIFF.                                  |\n    | ma_standard_channel_map_flac      | FLAC channel map.                                         |\n    | ma_standard_channel_map_vorbis    | Vorbis channel map.                                       |\n    | ma_standard_channel_map_sound4    | FreeBSD's sound(4).                                       |\n    | ma_standard_channel_map_sndio     | sndio channel map. http://www.sndio.org/tips.html.        |\n    | ma_standard_channel_map_webaudio  | https://webaudio.github.io/web-audio-api/#ChannelOrdering |\n    +-----------------------------------+-----------------------------------------------------------+\n\nBelow are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`):\n\n    +---------------+---------------------------------+\n    | Channel Count | Mapping                         |\n    +---------------+---------------------------------+\n    | 1 (Mono)      | 0: MA_CHANNEL_MONO              |\n    +---------------+---------------------------------+\n    | 2 (Stereo)    | 0: MA_CHANNEL_FRONT_LEFT   <br> |\n    |               | 1: MA_CHANNEL_FRONT_RIGHT       |\n    +---------------+---------------------------------+\n    | 3             | 0: MA_CHANNEL_FRONT_LEFT   <br> |\n    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |\n    |               | 2: MA_CHANNEL_FRONT_CENTER      |\n    +---------------+---------------------------------+\n    | 4 (Surround)  | 0: MA_CHANNEL_FRONT_LEFT   <br> |\n    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |\n    |               | 2: MA_CHANNEL_FRONT_CENTER <br> |\n    |               | 3: MA_CHANNEL_BACK_CENTER       |\n    +---------------+---------------------------------+\n    | 5             | 0: MA_CHANNEL_FRONT_LEFT   <br> |\n    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |\n    |               | 2: MA_CHANNEL_FRONT_CENTER <br> |\n    |               | 3: MA_CHANNEL_BACK_LEFT    <br> |\n    |               | 4: MA_CHANNEL_BACK_RIGHT        |\n    +---------------+---------------------------------+\n    | 6 (5.1)       | 0: MA_CHANNEL_FRONT_LEFT   <br> |\n    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |\n    |               | 2: MA_CHANNEL_FRONT_CENTER <br> |\n    |               | 3: MA_CHANNEL_LFE          <br> |\n    |               | 4: MA_CHANNEL_SIDE_LEFT    <br> |\n    |               | 5: MA_CHANNEL_SIDE_RIGHT        |\n    +---------------+---------------------------------+\n    | 7             | 0: MA_CHANNEL_FRONT_LEFT   <br> |\n    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |\n    |               | 2: MA_CHANNEL_FRONT_CENTER <br> |\n    |               | 3: MA_CHANNEL_LFE          <br> |\n    |               | 4: MA_CHANNEL_BACK_CENTER  <br> |\n    |               | 4: MA_CHANNEL_SIDE_LEFT    <br> |\n    |               | 5: MA_CHANNEL_SIDE_RIGHT        |\n    +---------------+---------------------------------+\n    | 8 (7.1)       | 0: MA_CHANNEL_FRONT_LEFT   <br> |\n    |               | 1: MA_CHANNEL_FRONT_RIGHT  <br> |\n    |               | 2: MA_CHANNEL_FRONT_CENTER <br> |\n    |               | 3: MA_CHANNEL_LFE          <br> |\n    |               | 4: MA_CHANNEL_BACK_LEFT    <br> |\n    |               | 5: MA_CHANNEL_BACK_RIGHT   <br> |\n    |               | 6: MA_CHANNEL_SIDE_LEFT    <br> |\n    |               | 7: MA_CHANNEL_SIDE_RIGHT        |\n    +---------------+---------------------------------+\n    | Other         | All channels set to 0. This     |\n    |               | is equivalent to the same       |\n    |               | mapping as the device.          |\n    +---------------+---------------------------------+\n\n\n\n10.3. Resampling\n----------------\nResampling is achieved with the `ma_resampler` object. To create a resampler object, do something\nlike the following:\n\n    ```c\n    ma_resampler_config config = ma_resampler_config_init(\n        ma_format_s16,\n        channels,\n        sampleRateIn,\n        sampleRateOut,\n        ma_resample_algorithm_linear);\n\n    ma_resampler resampler;\n    ma_result result = ma_resampler_init(&config, &resampler);\n    if (result != MA_SUCCESS) {\n        // An error occurred...\n    }\n    ```\n\nDo the following to uninitialize the resampler:\n\n    ```c\n    ma_resampler_uninit(&resampler);\n    ```\n\nThe following example shows how data can be processed\n\n    ```c\n    ma_uint64 frameCountIn  = 1000;\n    ma_uint64 frameCountOut = 2000;\n    ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);\n    if (result != MA_SUCCESS) {\n        // An error occurred...\n    }\n\n    // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the\n    // number of output frames written.\n    ```\n\nTo initialize the resampler you first need to set up a config (`ma_resampler_config`) with\n`ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of\nchannels, the input and output sample rate, and the algorithm.\n\nThe sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format\nyou will need to perform pre- and post-conversions yourself where necessary. Note that the format\nis the same for both input and output. The format cannot be changed after initialization.\n\nThe resampler supports multiple channels and is always interleaved (both input and output). The\nchannel count cannot be changed after initialization.\n\nThe sample rates can be anything other than zero, and are always specified in hertz. They should be\nset to something like 44100, etc. The sample rate is the only configuration property that can be\nchanged after initialization.\n\nThe miniaudio resampler has built-in support for the following algorithms:\n\n    +-----------+------------------------------+\n    | Algorithm | Enum Token                   |\n    +-----------+------------------------------+\n    | Linear    | ma_resample_algorithm_linear |\n    | Custom    | ma_resample_algorithm_custom |\n    +-----------+------------------------------+\n\nThe algorithm cannot be changed after initialization.\n\nProcessing always happens on a per PCM frame basis and always assumes interleaved input and output.\nDe-interleaved processing is not supported. To process frames, use\n`ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you\ncan fit in the output buffer and the number of input frames contained in the input buffer. On\noutput these variables contain the number of output frames that were written to the output buffer\nand the number of input frames that were consumed in the process. You can pass in NULL for the\ninput buffer in which case it will be treated as an infinitely large buffer of zeros. The output\nbuffer can also be NULL, in which case the processing will be treated as seek.\n\nThe sample rate can be changed dynamically on the fly. You can change this with explicit sample\nrates with `ma_resampler_set_rate()` and also with a decimal ratio with\n`ma_resampler_set_rate_ratio()`. The ratio is in/out.\n\nSometimes it's useful to know exactly how many input frames will be required to output a specific\nnumber of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`.\nLikewise, it's sometimes useful to know exactly how many frames would be output given a certain\nnumber of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`.\n\nDue to the nature of how resampling works, the resampler introduces some latency. This can be\nretrieved in terms of both the input rate and the output rate with\n`ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`.\n\n\n10.3.1. Resampling Algorithms\n-----------------------------\nThe choice of resampling algorithm depends on your situation and requirements.\n\n\n10.3.1.1. Linear Resampling\n---------------------------\nThe linear resampler is the fastest, but comes at the expense of poorer quality. There is, however,\nsome control over the quality of the linear resampler which may make it a suitable option depending\non your requirements.\n\nThe linear resampler performs low-pass filtering before or after downsampling or upsampling,\ndepending on the sample rates you're converting between. When decreasing the sample rate, the\nlow-pass filter will be applied before downsampling. When increasing the rate it will be performed\nafter upsampling. By default a fourth order low-pass filter will be applied. This can be configured\nvia the `lpfOrder` configuration variable. Setting this to 0 will disable filtering.\n\nThe low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of\nthe input and output sample rates (Nyquist Frequency).\n\nThe API for the linear resampler is the same as the main resampler API, only it's called\n`ma_linear_resampler`.\n\n\n10.3.2. Custom Resamplers\n-------------------------\nYou can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling\nalgorithm and setting a vtable in the resampler config:\n\n    ```c\n    ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom);\n    config.pBackendVTable = &g_customResamplerVTable;\n    ```\n\nCustom resamplers are useful if the stock algorithms are not appropriate for your use case. You\nneed to implement the required functions in `ma_resampling_backend_vtable`. Note that not all\nfunctions in the vtable need to be implemented, but if it's possible to implement, they should be.\n\nYou can use the `ma_linear_resampler` object for an example on how to implement the vtable. The\n`onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom\nresampler will need to make given the supplied config. When you initialize the resampler via the\n`onInit` callback, you'll be given a pointer to a heap allocation which is where you should store\nthe heap allocated data. You should not free this data in `onUninit` because miniaudio will manage\nit for you.\n\nThe `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn`\npoints to a variable containing the number of frames in the `pFramesIn` buffer and\n`pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer.\nOn output, `pFrameCountIn` should be set to the number of input frames that were fully consumed,\nwhereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`.\n\nThe `onSetRate` callback is optional and is used for dynamically changing the sample rate. If\ndynamic rate changes are not supported, you can set this callback to NULL.\n\nThe `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in\ninput and output rates respectively. These can be NULL in which case latency calculations will be\nassumed to be NULL.\n\nThe `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input\nframes are required to be available to produce the given number of output frames. Likewise, the\n`onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be\nproduced given the specified number of input frames. miniaudio will use these as a hint, but they\nare optional and can be set to NULL if you're unable to implement them.\n\n\n\n10.4. General Data Conversion\n-----------------------------\nThe `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and\nresampling into one operation. This is what miniaudio uses internally to convert between the format\nrequested when the device was initialized and the format of the backend's native device. The API\nfor general data conversion is very similar to the resampling API. Create a `ma_data_converter`\nobject like this:\n\n    ```c\n    ma_data_converter_config config = ma_data_converter_config_init(\n        inputFormat,\n        outputFormat,\n        inputChannels,\n        outputChannels,\n        inputSampleRate,\n        outputSampleRate\n    );\n\n    ma_data_converter converter;\n    ma_result result = ma_data_converter_init(&config, NULL, &converter);\n    if (result != MA_SUCCESS) {\n        // An error occurred...\n    }\n    ```\n\nIn the example above we use `ma_data_converter_config_init()` to initialize the config, however\nthere's many more properties that can be configured, such as channel maps and resampling quality.\nSomething like the following may be more suitable depending on your requirements:\n\n    ```c\n    ma_data_converter_config config = ma_data_converter_config_init_default();\n    config.formatIn = inputFormat;\n    config.formatOut = outputFormat;\n    config.channelsIn = inputChannels;\n    config.channelsOut = outputChannels;\n    config.sampleRateIn = inputSampleRate;\n    config.sampleRateOut = outputSampleRate;\n    ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn);\n    config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER;\n    ```\n\nDo the following to uninitialize the data converter:\n\n    ```c\n    ma_data_converter_uninit(&converter, NULL);\n    ```\n\nThe following example shows how data can be processed\n\n    ```c\n    ma_uint64 frameCountIn  = 1000;\n    ma_uint64 frameCountOut = 2000;\n    ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);\n    if (result != MA_SUCCESS) {\n        // An error occurred...\n    }\n\n    // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number\n    // of output frames written.\n    ```\n\nThe data converter supports multiple channels and is always interleaved (both input and output).\nThe channel count cannot be changed after initialization.\n\nSample rates can be anything other than zero, and are always specified in hertz. They should be set\nto something like 44100, etc. The sample rate is the only configuration property that can be\nchanged after initialization, but only if the `resampling.allowDynamicSampleRate` member of\n`ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use\n`ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out.\nThe resampling algorithm cannot be changed after initialization.\n\nProcessing always happens on a per PCM frame basis and always assumes interleaved input and output.\nDe-interleaved processing is not supported. To process frames, use\n`ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames\nyou can fit in the output buffer and the number of input frames contained in the input buffer. On\noutput these variables contain the number of output frames that were written to the output buffer\nand the number of input frames that were consumed in the process. You can pass in NULL for the\ninput buffer in which case it will be treated as an infinitely large\nbuffer of zeros. The output buffer can also be NULL, in which case the processing will be treated\nas seek.\n\nSometimes it's useful to know exactly how many input frames will be required to output a specific\nnumber of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`.\nLikewise, it's sometimes useful to know exactly how many frames would be output given a certain\nnumber of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`.\n\nDue to the nature of how resampling works, the data converter introduces some latency if resampling\nis required. This can be retrieved in terms of both the input rate and the output rate with\n`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`.\n\n\n\n11. Filtering\n=============\n\n11.1. Biquad Filtering\n----------------------\nBiquad filtering is achieved with the `ma_biquad` API. Example:\n\n    ```c\n    ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);\n    ma_result result = ma_biquad_init(&config, &biquad);\n    if (result != MA_SUCCESS) {\n        // Error.\n    }\n\n    ...\n\n    ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount);\n    ```\n\nBiquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0,\nb1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and\ncoefficients must not be pre-normalized.\n\nSupported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format\nyou need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use\nfixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used.\n\nInput and output frames are always interleaved.\n\nFiltering can be applied in-place by passing in the same pointer for both the input and output\nbuffers, like so:\n\n    ```c\n    ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount);\n    ```\n\nIf you need to change the values of the coefficients, but maintain the values in the registers you\ncan do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the\nfilter while keeping the values of registers valid to avoid glitching. Do not use\n`ma_biquad_init()` for this as it will do a full initialization which involves clearing the\nregisters to 0. Note that changing the format or channel count after initialization is invalid and\nwill result in an error.\n\n\n11.2. Low-Pass Filtering\n------------------------\nLow-pass filtering is achieved with the following APIs:\n\n    +---------+------------------------------------------+\n    | API     | Description                              |\n    +---------+------------------------------------------+\n    | ma_lpf1 | First order low-pass filter              |\n    | ma_lpf2 | Second order low-pass filter             |\n    | ma_lpf  | High order low-pass filter (Butterworth) |\n    +---------+------------------------------------------+\n\nLow-pass filter example:\n\n    ```c\n    ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);\n    ma_result result = ma_lpf_init(&config, &lpf);\n    if (result != MA_SUCCESS) {\n        // Error.\n    }\n\n    ...\n\n    ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount);\n    ```\n\nSupported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format\nyou need to convert it yourself beforehand. Input and output frames are always interleaved.\n\nFiltering can be applied in-place by passing in the same pointer for both the input and output\nbuffers, like so:\n\n    ```c\n    ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount);\n    ```\n\nThe maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more,\nyou can chain first and second order filters together.\n\n    ```c\n    for (iFilter = 0; iFilter < filterCount; iFilter += 1) {\n        ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount);\n    }\n    ```\n\nIf you need to change the configuration of the filter, but need to maintain the state of internal\nregisters you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample\nrate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the\nformat or channel count after initialization is invalid and will result in an error.\n\nThe `ma_lpf` object supports a configurable order, but if you only need a first order filter you\nmay want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use\n`ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient.\n\nIf an even filter order is specified, a series of second order filters will be processed in a\nchain. If an odd filter order is specified, a first order filter will be applied, followed by a\nseries of second order filters in a chain.\n\n\n11.3. High-Pass Filtering\n-------------------------\nHigh-pass filtering is achieved with the following APIs:\n\n    +---------+-------------------------------------------+\n    | API     | Description                               |\n    +---------+-------------------------------------------+\n    | ma_hpf1 | First order high-pass filter              |\n    | ma_hpf2 | Second order high-pass filter             |\n    | ma_hpf  | High order high-pass filter (Butterworth) |\n    +---------+-------------------------------------------+\n\nHigh-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`,\n`ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage.\n\n\n11.4. Band-Pass Filtering\n-------------------------\nBand-pass filtering is achieved with the following APIs:\n\n    +---------+-------------------------------+\n    | API     | Description                   |\n    +---------+-------------------------------+\n    | ma_bpf2 | Second order band-pass filter |\n    | ma_bpf  | High order band-pass filter   |\n    +---------+-------------------------------+\n\nBand-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and\n`ma_hpf`. See example code for low-pass filters for example usage. Note that the order for\nband-pass filters must be an even number which means there is no first order band-pass filter,\nunlike low-pass and high-pass filters.\n\n\n11.5. Notch Filtering\n---------------------\nNotch filtering is achieved with the following APIs:\n\n    +-----------+------------------------------------------+\n    | API       | Description                              |\n    +-----------+------------------------------------------+\n    | ma_notch2 | Second order notching filter             |\n    +-----------+------------------------------------------+\n\n\n11.6. Peaking EQ Filtering\n-------------------------\nPeaking filtering is achieved with the following APIs:\n\n    +----------+------------------------------------------+\n    | API      | Description                              |\n    +----------+------------------------------------------+\n    | ma_peak2 | Second order peaking filter              |\n    +----------+------------------------------------------+\n\n\n11.7. Low Shelf Filtering\n-------------------------\nLow shelf filtering is achieved with the following APIs:\n\n    +-------------+------------------------------------------+\n    | API         | Description                              |\n    +-------------+------------------------------------------+\n    | ma_loshelf2 | Second order low shelf filter            |\n    +-------------+------------------------------------------+\n\nWhere a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to\njust turn them down rather than eliminate them entirely.\n\n\n11.8. High Shelf Filtering\n--------------------------\nHigh shelf filtering is achieved with the following APIs:\n\n    +-------------+------------------------------------------+\n    | API         | Description                              |\n    +-------------+------------------------------------------+\n    | ma_hishelf2 | Second order high shelf filter           |\n    +-------------+------------------------------------------+\n\nThe high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf`\ninstead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies,\nthe high shelf filter does the same thing for high frequencies.\n\n\n\n\n12. Waveform and Noise Generation\n=================================\n\n12.1. Waveforms\n---------------\nminiaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved\nwith the `ma_waveform` API. Example:\n\n    ```c\n    ma_waveform_config config = ma_waveform_config_init(\n        FORMAT,\n        CHANNELS,\n        SAMPLE_RATE,\n        ma_waveform_type_sine,\n        amplitude,\n        frequency);\n\n    ma_waveform waveform;\n    ma_result result = ma_waveform_init(&config, &waveform);\n    if (result != MA_SUCCESS) {\n        // Error.\n    }\n\n    ...\n\n    ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount);\n    ```\n\nThe amplitude, frequency, type, and sample rate can be changed dynamically with\n`ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and\n`ma_waveform_set_sample_rate()` respectively.\n\nYou can invert the waveform by setting the amplitude to a negative value. You can use this to\ncontrol whether or not a sawtooth has a positive or negative ramp, for example.\n\nBelow are the supported waveform types:\n\n    +---------------------------+\n    | Enum Name                 |\n    +---------------------------+\n    | ma_waveform_type_sine     |\n    | ma_waveform_type_square   |\n    | ma_waveform_type_triangle |\n    | ma_waveform_type_sawtooth |\n    +---------------------------+\n\n\n\n12.2. Noise\n-----------\nminiaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example:\n\n    ```c\n    ma_noise_config config = ma_noise_config_init(\n        FORMAT,\n        CHANNELS,\n        ma_noise_type_white,\n        SEED,\n        amplitude);\n\n    ma_noise noise;\n    ma_result result = ma_noise_init(&config, &noise);\n    if (result != MA_SUCCESS) {\n        // Error.\n    }\n\n    ...\n\n    ma_noise_read_pcm_frames(&noise, pOutput, frameCount);\n    ```\n\nThe noise API uses simple LCG random number generation. It supports a custom seed which is useful\nfor things like automated testing requiring reproducibility. Setting the seed to zero will default\nto `MA_DEFAULT_LCG_SEED`.\n\nThe amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and\n`ma_noise_set_seed()` respectively.\n\nBy default, the noise API will use different values for different channels. So, for example, the\nleft side in a stereo stream will be different to the right side. To instead have each channel use\nthe same random value, set the `duplicateChannels` member of the noise config to true, like so:\n\n    ```c\n    config.duplicateChannels = MA_TRUE;\n    ```\n\nBelow are the supported noise types.\n\n    +------------------------+\n    | Enum Name              |\n    +------------------------+\n    | ma_noise_type_white    |\n    | ma_noise_type_pink     |\n    | ma_noise_type_brownian |\n    +------------------------+\n\n\n\n13. Audio Buffers\n=================\nminiaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can\nread from memory that's managed by the application, but can also handle the memory management for\nyou internally. Memory management is flexible and should support most use cases.\n\nAudio buffers are initialized using the standard configuration system used everywhere in miniaudio:\n\n    ```c\n    ma_audio_buffer_config config = ma_audio_buffer_config_init(\n        format,\n        channels,\n        sizeInFrames,\n        pExistingData,\n        &allocationCallbacks);\n\n    ma_audio_buffer buffer;\n    result = ma_audio_buffer_init(&config, &buffer);\n    if (result != MA_SUCCESS) {\n        // Error.\n    }\n\n    ...\n\n    ma_audio_buffer_uninit(&buffer);\n    ```\n\nIn the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an\napplication can do self-managed memory allocation. If you would rather make a copy of the data, use\n`ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`.\n\nSometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the\nraw audio data in a contiguous block of memory. That is, the raw audio data will be located\nimmediately after the `ma_audio_buffer` structure. To do this, use\n`ma_audio_buffer_alloc_and_init()`:\n\n    ```c\n    ma_audio_buffer_config config = ma_audio_buffer_config_init(\n        format,\n        channels,\n        sizeInFrames,\n        pExistingData,\n        &allocationCallbacks);\n\n    ma_audio_buffer* pBuffer\n    result = ma_audio_buffer_alloc_and_init(&config, &pBuffer);\n    if (result != MA_SUCCESS) {\n        // Error\n    }\n\n    ...\n\n    ma_audio_buffer_uninit_and_free(&buffer);\n    ```\n\nIf you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it\nwith `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by\n`pExistingData` will be copied into the buffer, which is contrary to the behavior of\n`ma_audio_buffer_init()`.\n\nAn audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the\ncursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should\nloop. The return value is the number of frames actually read. If this is less than the number of\nframes requested it means the end has been reached. This should never happen if the `loop`\nparameter is set to true. If you want to manually loop back to the start, you can do so with with\n`ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an\naudio buffer.\n\n    ```c\n    ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping);\n    if (framesRead < desiredFrameCount) {\n        // If not looping, this means the end has been reached. This should never happen in looping mode with valid input.\n    }\n    ```\n\nSometimes you may want to avoid the cost of data movement between the internal buffer and the\noutput buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data:\n\n    ```c\n    void* pMappedFrames;\n    ma_uint64 frameCount = frameCountToTryMapping;\n    ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount);\n    if (result == MA_SUCCESS) {\n        // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be\n        // less due to the end of the buffer being reached.\n        ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels);\n\n        // You must unmap the buffer.\n        ma_audio_buffer_unmap(pAudioBuffer, frameCount);\n    }\n    ```\n\nWhen you use memory mapping, the read cursor is increment by the frame count passed in to\n`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller\nthan the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is\nthat it does not handle looping for you. You can determine if the buffer is at the end for the\npurpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of\n`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END`\nas an error when returned by `ma_audio_buffer_unmap()`.\n\n\n\n14. Ring Buffers\n================\nminiaudio supports lock free (single producer, single consumer) ring buffers which are exposed via\nthe `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb`\noperates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around\n`ma_rb`.\n\nUnlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved\nstreams. The caller can also allocate their own backing memory for the ring buffer to use\ninternally for added flexibility. Otherwise the ring buffer will manage it's internal memory for\nyou.\n\nThe examples below use the PCM frame variant of the ring buffer since that's most likely the one\nyou will want to use. To initialize a ring buffer, do something like the following:\n\n    ```c\n    ma_pcm_rb rb;\n    ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb);\n    if (result != MA_SUCCESS) {\n        // Error\n    }\n    ```\n\nThe `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because\nit's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you\nwould call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes\ninstead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter\nis a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines.\nPassing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used.\n\nUse `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is\noffset from each other based on the stride. To manage your sub-buffers you can use\n`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and\n`ma_pcm_rb_get_subbuffer_ptr()`.\n\nUse `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section\nof the ring buffer. You specify the number of frames you need, and on output it will set to what\nwas actually acquired. If the read or write pointer is positioned such that the number of frames\nrequested will require a loop, it will be clamped to the end of the buffer. Therefore, the number\nof frames you're given may be less than the number you requested.\n\nAfter calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the\nbuffer and then \"commit\" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is\nwhere the read/write pointers are updated. When you commit you need to pass in the buffer that was\nreturned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is\nonly used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and\n`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was\noriginally requested.\n\nIf you want to correct for drift between the write pointer and the read pointer you can use a\ncombination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and\n`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only\nmove the read pointer forward via the consumer thread, and the write pointer forward by the\nproducer thread. If there is too much space between the pointers, move the read pointer forward. If\nthere is too little space between the pointers, move the write pointer forward.\n\nYou can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb`\nAPI. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and\ninstead of frame counts you will pass around byte counts.\n\nThe maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most\nsignificant bit being used to encode a loop flag and the internally managed buffers always being\naligned to `MA_SIMD_ALIGNMENT`.\n\nNote that the ring buffer is only thread safe when used by a single consumer thread and single\nproducer thread.\n\n\n\n15. Backends\n============\nThe following backends are supported by miniaudio. These are listed in order of default priority.\nWhen no backend is specified when initializing a context or device, miniaudio will attempt to use\neach of these backends in the order listed in the table below.\n\nNote that backends that are not usable by the build target will not be included in the build. For\nexample, ALSA, which is specific to Linux, will not be included in the Windows build.\n\n    +-------------+-----------------------+--------------------------------------------------------+\n    | Name        | Enum Name             | Supported Operating Systems                            |\n    +-------------+-----------------------+--------------------------------------------------------+\n    | WASAPI      | ma_backend_wasapi     | Windows Vista+                                         |\n    | DirectSound | ma_backend_dsound     | Windows XP+                                            |\n    | WinMM       | ma_backend_winmm      | Windows 95+                                            |\n    | Core Audio  | ma_backend_coreaudio  | macOS, iOS                                             |\n    | sndio       | ma_backend_sndio      | OpenBSD                                                |\n    | audio(4)    | ma_backend_audio4     | NetBSD, OpenBSD                                        |\n    | OSS         | ma_backend_oss        | FreeBSD                                                |\n    | PulseAudio  | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android)  |\n    | ALSA        | ma_backend_alsa       | Linux                                                  |\n    | JACK        | ma_backend_jack       | Cross Platform (disabled on BSD and Android)           |\n    | AAudio      | ma_backend_aaudio     | Android 8+                                             |\n    | OpenSL ES   | ma_backend_opensl     | Android (API level 16+)                                |\n    | Web Audio   | ma_backend_webaudio   | Web (via Emscripten)                                   |\n    | Custom      | ma_backend_custom     | Cross Platform                                         |\n    | Null        | ma_backend_null       | Cross Platform (not used on Web)                       |\n    +-------------+-----------------------+--------------------------------------------------------+\n\nSome backends have some nuance details you may want to be aware of.\n\n15.1. WASAPI\n------------\n- Low-latency shared mode will be disabled when using an application-defined sample rate which is\n  different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC`\n  to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing\n  when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC\n  will result in miniaudio's internal resampler being used instead which will in turn enable the\n  use of low-latency shared mode.\n\n15.2. PulseAudio\n----------------\n- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:\n  https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling.\n  Alternatively, consider using a different backend such as ALSA.\n\n15.3. Android\n-------------\n- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:\n  `<uses-permission android:name=\"android.permission.RECORD_AUDIO\" />`\n- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a\n  limitation with OpenSL|ES.\n- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration\n  API (devices are enumerated through Java). You can however perform your own device enumeration\n  through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it\n  to ma_device_init().\n- The backend API will perform resampling where possible. The reason for this as opposed to using\n  miniaudio's built-in resampler is to take advantage of any potential device-specific\n  optimizations the driver may implement.\n\nBSD\n---\n- The sndio backend is currently only enabled on OpenBSD builds.\n- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can\n  use it.\n\n15.4. UWP\n---------\n- UWP only supports default playback and capture devices.\n- UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):\n\n    ```\n    <Package ...>\n        ...\n        <Capabilities>\n            <DeviceCapability Name=\"microphone\" />\n        </Capabilities>\n    </Package>\n    ```\n\n15.5. Web Audio / Emscripten\n----------------------------\n- You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build.\n- The first time a context is initialized it will create a global object called \"miniaudio\" whose\n  primary purpose is to act as a factory for device objects.\n- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as\n  they've been deprecated.\n- Google has implemented a policy in their browsers that prevent automatic media output without\n  first receiving some kind of user input. The following web page has additional details:\n  https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device\n  may fail if you try to start playback without first handling some kind of user input.\n\n\n\n16. Optimization Tips\n=====================\nSee below for some tips on improving performance.\n\n16.1. Low Level API\n-------------------\n- In the data callback, if your data is already clipped prior to copying it into the output buffer,\n  set the `noClip` config option in the device config to true. This will disable miniaudio's built\n  in clipping function.\n- By default, miniaudio will pre-silence the data callback's output buffer. If you know that you\n  will always write valid data to the output buffer you can disable pre-silencing by setting the\n  `noPreSilence` config option in the device config to true.\n\n16.2. High Level API\n--------------------\n- If a sound does not require doppler or pitch shifting, consider disabling pitching by\n  initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag.\n- If a sound does not require spatialization, disable it by initializing the sound with the\n  `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with\n  `ma_sound_set_spatialization_enabled()`.\n- If you know all of your sounds will always be the same sample rate, set the engine's sample\n  rate to match that of the sounds. Likewise, if you're using a self-managed resource manager,\n  consider setting the decoded sample rate to match your sounds. By configuring everything to\n  use a consistent sample rate, sample rate conversion can be avoided.\n\n\n\n17. Miscellaneous Notes\n=======================\n- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for\n  WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though\n  not all have been tested.\n- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This\n  is due to 64-bit file APIs not being available.\n*/\n\n#ifndef miniaudio_h\n#define miniaudio_h\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MA_STRINGIFY(x)     #x\n#define MA_XSTRINGIFY(x)    MA_STRINGIFY(x)\n\n#define MA_VERSION_MAJOR    0\n#define MA_VERSION_MINOR    11\n#define MA_VERSION_REVISION 21\n#define MA_VERSION_STRING   MA_XSTRINGIFY(MA_VERSION_MAJOR) \".\" MA_XSTRINGIFY(MA_VERSION_MINOR) \".\" MA_XSTRINGIFY(MA_VERSION_REVISION)\n\n#if defined(_MSC_VER) && !defined(__clang__)\n    #pragma warning(push)\n    #pragma warning(disable:4201)   /* nonstandard extension used: nameless struct/union */\n    #pragma warning(disable:4214)   /* nonstandard extension used: bit field types other than int */\n    #pragma warning(disable:4324)   /* structure was padded due to alignment specifier */\n#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wpedantic\" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */\n    #if defined(__clang__)\n        #pragma GCC diagnostic ignored \"-Wc11-extensions\"   /* anonymous unions are a C11 extension */\n    #endif\n#endif\n\n\n\n#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__)\n    #define MA_SIZEOF_PTR   8\n#else\n    #define MA_SIZEOF_PTR   4\n#endif\n\n#include <stddef.h> /* For size_t. */\n\n/* Sized types. */\n#if defined(MA_USE_STDINT)\n    #include <stdint.h>\n    typedef int8_t   ma_int8;\n    typedef uint8_t  ma_uint8;\n    typedef int16_t  ma_int16;\n    typedef uint16_t ma_uint16;\n    typedef int32_t  ma_int32;\n    typedef uint32_t ma_uint32;\n    typedef int64_t  ma_int64;\n    typedef uint64_t ma_uint64;\n#else\n    typedef   signed char           ma_int8;\n    typedef unsigned char           ma_uint8;\n    typedef   signed short          ma_int16;\n    typedef unsigned short          ma_uint16;\n    typedef   signed int            ma_int32;\n    typedef unsigned int            ma_uint32;\n    #if defined(_MSC_VER) && !defined(__clang__)\n        typedef   signed __int64    ma_int64;\n        typedef unsigned __int64    ma_uint64;\n    #else\n        #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n            #pragma GCC diagnostic push\n            #pragma GCC diagnostic ignored \"-Wlong-long\"\n            #if defined(__clang__)\n                #pragma GCC diagnostic ignored \"-Wc++11-long-long\"\n            #endif\n        #endif\n        typedef   signed long long  ma_int64;\n        typedef unsigned long long  ma_uint64;\n        #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n            #pragma GCC diagnostic pop\n        #endif\n    #endif\n#endif  /* MA_USE_STDINT */\n\n#if MA_SIZEOF_PTR == 8\n    typedef ma_uint64           ma_uintptr;\n#else\n    typedef ma_uint32           ma_uintptr;\n#endif\n\ntypedef ma_uint8    ma_bool8;\ntypedef ma_uint32   ma_bool32;\n#define MA_TRUE     1\n#define MA_FALSE    0\n\n/* These float types are not used universally by miniaudio. It's to simplify some macro expansion for atomic types. */\ntypedef float       ma_float;\ntypedef double      ma_double;\n\ntypedef void* ma_handle;\ntypedef void* ma_ptr;\n\n/*\nma_proc is annoying because when compiling with GCC we get pendantic warnings about converting\nbetween `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get\nwarning C4191 about \"type cast between incompatible function types\". To work around this I'm going\nto use a different data type depending on the compiler.\n*/\n#if defined(__GNUC__)\ntypedef void (*ma_proc)(void);\n#else\ntypedef void* ma_proc;\n#endif\n\n#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)\ntypedef ma_uint16 wchar_t;\n#endif\n\n/* Define NULL for some compilers. */\n#ifndef NULL\n#define NULL 0\n#endif\n\n#if defined(SIZE_MAX)\n    #define MA_SIZE_MAX    SIZE_MAX\n#else\n    #define MA_SIZE_MAX    0xFFFFFFFF  /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */\n#endif\n\n\n/* Platform/backend detection. */\n#if defined(_WIN32) || defined(__COSMOPOLITAN__)\n    #define MA_WIN32\n    #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)))\n        #define MA_WIN32_UWP\n    #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES)\n        #define MA_WIN32_GDK\n    #else\n        #define MA_WIN32_DESKTOP\n    #endif\n#endif\n#if !defined(_WIN32)    /* If it's not Win32, assume POSIX. */\n    #define MA_POSIX\n\n    /*\n    Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented.\n    You can use this to avoid including pthread.h in the header section. The downside is that it\n    results in some fixed sized structures being declared for the various types that are used in\n    miniaudio. The risk here is that these types might be too small for a given platform. This\n    risk is yours to take and no support will be offered if you enable this option.\n    */\n    #ifndef MA_NO_PTHREAD_IN_HEADER\n        #include <pthread.h>    /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */\n        typedef pthread_t       ma_pthread_t;\n        typedef pthread_mutex_t ma_pthread_mutex_t;\n        typedef pthread_cond_t  ma_pthread_cond_t;\n    #else\n        typedef ma_uintptr      ma_pthread_t;\n        typedef union           ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t;\n        typedef union           ma_pthread_cond_t  { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t;\n    #endif\n\n    #if defined(__unix__)\n        #define MA_UNIX\n    #endif\n    #if defined(__linux__)\n        #define MA_LINUX\n    #endif\n    #if defined(__APPLE__)\n        #define MA_APPLE\n    #endif\n    #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)\n        #define MA_BSD\n    #endif\n    #if defined(__ANDROID__)\n        #define MA_ANDROID\n    #endif\n    #if defined(__EMSCRIPTEN__)\n        #define MA_EMSCRIPTEN\n    #endif\n    #if defined(__ORBIS__)\n        #define MA_ORBIS\n    #endif\n    #if defined(__PROSPERO__)\n        #define MA_PROSPERO\n    #endif\n    #if defined(__NX__)\n        #define MA_NX\n    #endif\n    #if defined(__BEOS__) || defined(__HAIKU__)\n        #define MA_BEOS\n    #endif\n    #if defined(__HAIKU__)\n        #define MA_HAIKU\n    #endif\n#endif\n\n#if defined(__has_c_attribute)\n    #if __has_c_attribute(fallthrough)\n        #define MA_FALLTHROUGH [[fallthrough]]\n    #endif\n#endif\n#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__))\n    #if __has_attribute(fallthrough)\n        #define MA_FALLTHROUGH __attribute__((fallthrough))\n    #endif\n#endif\n#if !defined(MA_FALLTHROUGH)\n    #define MA_FALLTHROUGH ((void)0)\n#endif\n\n#ifdef _MSC_VER\n    #define MA_INLINE __forceinline\n\n    /* noinline was introduced in Visual Studio 2005. */\n    #if _MSC_VER >= 1400\n        #define MA_NO_INLINE __declspec(noinline)\n    #else\n        #define MA_NO_INLINE\n    #endif\n#elif defined(__GNUC__)\n    /*\n    I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when\n    the __attribute__((always_inline)) attribute is defined without an \"inline\" statement. I think therefore there must be some\n    case where \"__inline__\" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the\n    command line, we cannot use the \"inline\" keyword and instead need to use \"__inline__\". In an attempt to work around this issue\n    I am using \"__inline__\" only when we're compiling in strict ANSI mode.\n    */\n    #if defined(__STRICT_ANSI__)\n        #define MA_GNUC_INLINE_HINT __inline__\n    #else\n        #define MA_GNUC_INLINE_HINT inline\n    #endif\n\n    #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)\n        #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline))\n        #define MA_NO_INLINE __attribute__((noinline))\n    #else\n        #define MA_INLINE MA_GNUC_INLINE_HINT\n        #define MA_NO_INLINE __attribute__((noinline))\n    #endif\n#elif defined(__WATCOMC__)\n    #define MA_INLINE __inline\n    #define MA_NO_INLINE\n#else\n    #define MA_INLINE\n    #define MA_NO_INLINE\n#endif\n\n/* MA_DLL is not officially supported. You're on your own if you want to use this. */\n#if defined(MA_DLL)\n    #if defined(_WIN32)\n        #define MA_DLL_IMPORT  __declspec(dllimport)\n        #define MA_DLL_EXPORT  __declspec(dllexport)\n        #define MA_DLL_PRIVATE static\n    #else\n        #if defined(__GNUC__) && __GNUC__ >= 4\n            #define MA_DLL_IMPORT  __attribute__((visibility(\"default\")))\n            #define MA_DLL_EXPORT  __attribute__((visibility(\"default\")))\n            #define MA_DLL_PRIVATE __attribute__((visibility(\"hidden\")))\n        #else\n            #define MA_DLL_IMPORT\n            #define MA_DLL_EXPORT\n            #define MA_DLL_PRIVATE static\n        #endif\n    #endif\n#endif\n\n#if !defined(MA_API)\n    #if defined(MA_DLL)\n        #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)\n            #define MA_API  MA_DLL_EXPORT\n        #else\n            #define MA_API  MA_DLL_IMPORT\n        #endif\n    #else\n        #define MA_API extern\n    #endif\n#endif\n\n#if !defined(MA_STATIC)\n    #if defined(MA_DLL)\n        #define MA_PRIVATE MA_DLL_PRIVATE\n    #else\n        #define MA_PRIVATE static\n    #endif\n#endif\n\n\n/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */\n#define MA_SIMD_ALIGNMENT  32\n\n/*\nSpecial wchar_t type to ensure any structures in the public sections that reference it have a\nconsistent size across all platforms.\n\nOn Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use\nwchar_t for it's IDs, we need a special explicitly sized wchar type that is always 2 bytes on all\nplatforms.\n*/\n#if !defined(MA_POSIX) && defined(MA_WIN32)\ntypedef wchar_t     ma_wchar_win32;\n#else\ntypedef ma_uint16   ma_wchar_win32;\n#endif\n\n\n\n/*\nLogging Levels\n==============\nLog levels are only used to give logging callbacks some context as to the severity of a log message\nso they can do filtering. All log levels will be posted to registered logging callbacks. If you\ndon't want to output a certain log level you can discriminate against the log level in the callback.\n\nMA_LOG_LEVEL_DEBUG\n    Used for debugging. Useful for debug and test builds, but should be disabled in release builds.\n\nMA_LOG_LEVEL_INFO\n    Informational logging. Useful for debugging. This will never be called from within the data\n    callback.\n\nMA_LOG_LEVEL_WARNING\n    Warnings. You should enable this in you development builds and action them when encounted. These\n    logs usually indicate a potential problem or misconfiguration, but still allow you to keep\n    running. This will never be called from within the data callback.\n\nMA_LOG_LEVEL_ERROR\n    Error logging. This will be fired when an operation fails and is subsequently aborted. This can\n    be fired from within the data callback, in which case the device will be stopped. You should\n    always have this log level enabled.\n*/\ntypedef enum\n{\n    MA_LOG_LEVEL_DEBUG   = 4,\n    MA_LOG_LEVEL_INFO    = 3,\n    MA_LOG_LEVEL_WARNING = 2,\n    MA_LOG_LEVEL_ERROR   = 1\n} ma_log_level;\n\n/*\nVariables needing to be accessed atomically should be declared with this macro for two reasons:\n\n    1) It allows people who read the code to identify a variable as such; and\n    2) It forces alignment on platforms where it's required or optimal.\n\nNote that for x86/64, alignment is not strictly necessary, but does have some performance\nimplications. Where supported by the compiler, alignment will be used, but otherwise if the CPU\narchitecture does not require it, it will simply leave it unaligned. This is the case with old\nversions of Visual Studio, which I've confirmed with at least VC6.\n*/\n#if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)\n    #include <stdalign.h>\n    #define MA_ATOMIC(alignment, type)            _Alignas(alignment) type\n#else\n    #if defined(__GNUC__)\n        /* GCC-style compilers. */\n        #define MA_ATOMIC(alignment, type)        type __attribute__((aligned(alignment)))\n    #elif defined(_MSC_VER) && _MSC_VER > 1200  /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */\n        /* MSVC. */\n        #define MA_ATOMIC(alignment, type)        __declspec(align(alignment)) type\n    #else\n        /* Other compilers. */\n        #define MA_ATOMIC(alignment, type)        type\n    #endif\n#endif\n\ntypedef struct ma_context ma_context;\ntypedef struct ma_device ma_device;\n\ntypedef ma_uint8 ma_channel;\ntypedef enum\n{\n    MA_CHANNEL_NONE               = 0,\n    MA_CHANNEL_MONO               = 1,\n    MA_CHANNEL_FRONT_LEFT         = 2,\n    MA_CHANNEL_FRONT_RIGHT        = 3,\n    MA_CHANNEL_FRONT_CENTER       = 4,\n    MA_CHANNEL_LFE                = 5,\n    MA_CHANNEL_BACK_LEFT          = 6,\n    MA_CHANNEL_BACK_RIGHT         = 7,\n    MA_CHANNEL_FRONT_LEFT_CENTER  = 8,\n    MA_CHANNEL_FRONT_RIGHT_CENTER = 9,\n    MA_CHANNEL_BACK_CENTER        = 10,\n    MA_CHANNEL_SIDE_LEFT          = 11,\n    MA_CHANNEL_SIDE_RIGHT         = 12,\n    MA_CHANNEL_TOP_CENTER         = 13,\n    MA_CHANNEL_TOP_FRONT_LEFT     = 14,\n    MA_CHANNEL_TOP_FRONT_CENTER   = 15,\n    MA_CHANNEL_TOP_FRONT_RIGHT    = 16,\n    MA_CHANNEL_TOP_BACK_LEFT      = 17,\n    MA_CHANNEL_TOP_BACK_CENTER    = 18,\n    MA_CHANNEL_TOP_BACK_RIGHT     = 19,\n    MA_CHANNEL_AUX_0              = 20,\n    MA_CHANNEL_AUX_1              = 21,\n    MA_CHANNEL_AUX_2              = 22,\n    MA_CHANNEL_AUX_3              = 23,\n    MA_CHANNEL_AUX_4              = 24,\n    MA_CHANNEL_AUX_5              = 25,\n    MA_CHANNEL_AUX_6              = 26,\n    MA_CHANNEL_AUX_7              = 27,\n    MA_CHANNEL_AUX_8              = 28,\n    MA_CHANNEL_AUX_9              = 29,\n    MA_CHANNEL_AUX_10             = 30,\n    MA_CHANNEL_AUX_11             = 31,\n    MA_CHANNEL_AUX_12             = 32,\n    MA_CHANNEL_AUX_13             = 33,\n    MA_CHANNEL_AUX_14             = 34,\n    MA_CHANNEL_AUX_15             = 35,\n    MA_CHANNEL_AUX_16             = 36,\n    MA_CHANNEL_AUX_17             = 37,\n    MA_CHANNEL_AUX_18             = 38,\n    MA_CHANNEL_AUX_19             = 39,\n    MA_CHANNEL_AUX_20             = 40,\n    MA_CHANNEL_AUX_21             = 41,\n    MA_CHANNEL_AUX_22             = 42,\n    MA_CHANNEL_AUX_23             = 43,\n    MA_CHANNEL_AUX_24             = 44,\n    MA_CHANNEL_AUX_25             = 45,\n    MA_CHANNEL_AUX_26             = 46,\n    MA_CHANNEL_AUX_27             = 47,\n    MA_CHANNEL_AUX_28             = 48,\n    MA_CHANNEL_AUX_29             = 49,\n    MA_CHANNEL_AUX_30             = 50,\n    MA_CHANNEL_AUX_31             = 51,\n    MA_CHANNEL_LEFT               = MA_CHANNEL_FRONT_LEFT,\n    MA_CHANNEL_RIGHT              = MA_CHANNEL_FRONT_RIGHT,\n    MA_CHANNEL_POSITION_COUNT     = (MA_CHANNEL_AUX_31 + 1)\n} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */\n\ntypedef enum\n{\n    MA_SUCCESS                        =  0,\n    MA_ERROR                          = -1,  /* A generic error. */\n    MA_INVALID_ARGS                   = -2,\n    MA_INVALID_OPERATION              = -3,\n    MA_OUT_OF_MEMORY                  = -4,\n    MA_OUT_OF_RANGE                   = -5,\n    MA_ACCESS_DENIED                  = -6,\n    MA_DOES_NOT_EXIST                 = -7,\n    MA_ALREADY_EXISTS                 = -8,\n    MA_TOO_MANY_OPEN_FILES            = -9,\n    MA_INVALID_FILE                   = -10,\n    MA_TOO_BIG                        = -11,\n    MA_PATH_TOO_LONG                  = -12,\n    MA_NAME_TOO_LONG                  = -13,\n    MA_NOT_DIRECTORY                  = -14,\n    MA_IS_DIRECTORY                   = -15,\n    MA_DIRECTORY_NOT_EMPTY            = -16,\n    MA_AT_END                         = -17,\n    MA_NO_SPACE                       = -18,\n    MA_BUSY                           = -19,\n    MA_IO_ERROR                       = -20,\n    MA_INTERRUPT                      = -21,\n    MA_UNAVAILABLE                    = -22,\n    MA_ALREADY_IN_USE                 = -23,\n    MA_BAD_ADDRESS                    = -24,\n    MA_BAD_SEEK                       = -25,\n    MA_BAD_PIPE                       = -26,\n    MA_DEADLOCK                       = -27,\n    MA_TOO_MANY_LINKS                 = -28,\n    MA_NOT_IMPLEMENTED                = -29,\n    MA_NO_MESSAGE                     = -30,\n    MA_BAD_MESSAGE                    = -31,\n    MA_NO_DATA_AVAILABLE              = -32,\n    MA_INVALID_DATA                   = -33,\n    MA_TIMEOUT                        = -34,\n    MA_NO_NETWORK                     = -35,\n    MA_NOT_UNIQUE                     = -36,\n    MA_NOT_SOCKET                     = -37,\n    MA_NO_ADDRESS                     = -38,\n    MA_BAD_PROTOCOL                   = -39,\n    MA_PROTOCOL_UNAVAILABLE           = -40,\n    MA_PROTOCOL_NOT_SUPPORTED         = -41,\n    MA_PROTOCOL_FAMILY_NOT_SUPPORTED  = -42,\n    MA_ADDRESS_FAMILY_NOT_SUPPORTED   = -43,\n    MA_SOCKET_NOT_SUPPORTED           = -44,\n    MA_CONNECTION_RESET               = -45,\n    MA_ALREADY_CONNECTED              = -46,\n    MA_NOT_CONNECTED                  = -47,\n    MA_CONNECTION_REFUSED             = -48,\n    MA_NO_HOST                        = -49,\n    MA_IN_PROGRESS                    = -50,\n    MA_CANCELLED                      = -51,\n    MA_MEMORY_ALREADY_MAPPED          = -52,\n\n    /* General non-standard errors. */\n    MA_CRC_MISMATCH                   = -100,\n\n    /* General miniaudio-specific errors. */\n    MA_FORMAT_NOT_SUPPORTED           = -200,\n    MA_DEVICE_TYPE_NOT_SUPPORTED      = -201,\n    MA_SHARE_MODE_NOT_SUPPORTED       = -202,\n    MA_NO_BACKEND                     = -203,\n    MA_NO_DEVICE                      = -204,\n    MA_API_NOT_FOUND                  = -205,\n    MA_INVALID_DEVICE_CONFIG          = -206,\n    MA_LOOP                           = -207,\n    MA_BACKEND_NOT_ENABLED            = -208,\n\n    /* State errors. */\n    MA_DEVICE_NOT_INITIALIZED         = -300,\n    MA_DEVICE_ALREADY_INITIALIZED     = -301,\n    MA_DEVICE_NOT_STARTED             = -302,\n    MA_DEVICE_NOT_STOPPED             = -303,\n\n    /* Operation errors. */\n    MA_FAILED_TO_INIT_BACKEND         = -400,\n    MA_FAILED_TO_OPEN_BACKEND_DEVICE  = -401,\n    MA_FAILED_TO_START_BACKEND_DEVICE = -402,\n    MA_FAILED_TO_STOP_BACKEND_DEVICE  = -403\n} ma_result;\n\n\n#define MA_MIN_CHANNELS                 1\n#ifndef MA_MAX_CHANNELS\n#define MA_MAX_CHANNELS                 254\n#endif\n\n#ifndef MA_MAX_FILTER_ORDER\n#define MA_MAX_FILTER_ORDER             8\n#endif\n\ntypedef enum\n{\n    ma_stream_format_pcm = 0\n} ma_stream_format;\n\ntypedef enum\n{\n    ma_stream_layout_interleaved = 0,\n    ma_stream_layout_deinterleaved\n} ma_stream_layout;\n\ntypedef enum\n{\n    ma_dither_mode_none = 0,\n    ma_dither_mode_rectangle,\n    ma_dither_mode_triangle\n} ma_dither_mode;\n\ntypedef enum\n{\n    /*\n    I like to keep these explicitly defined because they're used as a key into a lookup table. When items are\n    added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample().\n    */\n    ma_format_unknown = 0,     /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */\n    ma_format_u8      = 1,\n    ma_format_s16     = 2,     /* Seems to be the most widely supported format. */\n    ma_format_s24     = 3,     /* Tightly packed. 3 bytes per sample. */\n    ma_format_s32     = 4,\n    ma_format_f32     = 5,\n    ma_format_count\n} ma_format;\n\ntypedef enum\n{\n    /* Standard rates need to be in priority order. */\n    ma_standard_sample_rate_48000  = 48000,     /* Most common */\n    ma_standard_sample_rate_44100  = 44100,\n\n    ma_standard_sample_rate_32000  = 32000,     /* Lows */\n    ma_standard_sample_rate_24000  = 24000,\n    ma_standard_sample_rate_22050  = 22050,\n\n    ma_standard_sample_rate_88200  = 88200,     /* Highs */\n    ma_standard_sample_rate_96000  = 96000,\n    ma_standard_sample_rate_176400 = 176400,\n    ma_standard_sample_rate_192000 = 192000,\n\n    ma_standard_sample_rate_16000  = 16000,     /* Extreme lows */\n    ma_standard_sample_rate_11025  = 11025,\n    ma_standard_sample_rate_8000   = 8000,\n\n    ma_standard_sample_rate_352800 = 352800,    /* Extreme highs */\n    ma_standard_sample_rate_384000 = 384000,\n\n    ma_standard_sample_rate_min    = ma_standard_sample_rate_8000,\n    ma_standard_sample_rate_max    = ma_standard_sample_rate_384000,\n    ma_standard_sample_rate_count  = 14         /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */\n} ma_standard_sample_rate;\n\n\ntypedef enum\n{\n    ma_channel_mix_mode_rectangular = 0,   /* Simple averaging based on the plane(s) the channel is sitting on. */\n    ma_channel_mix_mode_simple,            /* Drop excess channels; zeroed out extra channels. */\n    ma_channel_mix_mode_custom_weights,    /* Use custom weights specified in ma_channel_converter_config. */\n    ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular\n} ma_channel_mix_mode;\n\ntypedef enum\n{\n    ma_standard_channel_map_microsoft,\n    ma_standard_channel_map_alsa,\n    ma_standard_channel_map_rfc3551,   /* Based off AIFF. */\n    ma_standard_channel_map_flac,\n    ma_standard_channel_map_vorbis,\n    ma_standard_channel_map_sound4,    /* FreeBSD's sound(4). */\n    ma_standard_channel_map_sndio,     /* www.sndio.org/tips.html */\n    ma_standard_channel_map_webaudio = ma_standard_channel_map_flac, /* https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. */\n    ma_standard_channel_map_default = ma_standard_channel_map_microsoft\n} ma_standard_channel_map;\n\ntypedef enum\n{\n    ma_performance_profile_low_latency = 0,\n    ma_performance_profile_conservative\n} ma_performance_profile;\n\n\ntypedef struct\n{\n    void* pUserData;\n    void* (* onMalloc)(size_t sz, void* pUserData);\n    void* (* onRealloc)(void* p, size_t sz, void* pUserData);\n    void  (* onFree)(void* p, void* pUserData);\n} ma_allocation_callbacks;\n\ntypedef struct\n{\n    ma_int32 state;\n} ma_lcg;\n\n\n/*\nAtomics.\n\nThese are typesafe structures to prevent errors as a result of forgetting to reference variables atomically. It's too\neasy to introduce subtle bugs where you accidentally do a regular assignment instead of an atomic load/store, etc. By\nusing a struct we can enforce the use of atomics at compile time.\n\nThese types are declared in the header section because we need to reference them in structs below, but functions for\nusing them are only exposed in the implementation section. I do not want these to be part of the public API.\n\nThere's a few downsides to this system. The first is that you need to declare a new struct for each type. Below are\nsome macros to help with the declarations. They will be named like so:\n\n    ma_atomic_uint32 - atomic ma_uint32\n    ma_atomic_int32  - atomic ma_int32\n    ma_atomic_uint64 - atomic ma_uint64\n    ma_atomic_float  - atomic float\n    ma_atomic_bool32 - atomic ma_bool32\n\nThe other downside is that atomic pointers are extremely messy. You need to declare a new struct for each specific\ntype of pointer you need to make atomic. For example, an atomic ma_node* will look like this:\n\n    MA_ATOMIC_SAFE_TYPE_IMPL_PTR(node)\n\nWhich will declare a type struct that's named like so:\n\n    ma_atomic_ptr_node\n\nFunctions to use the atomic types are declared in the implementation section. All atomic functions are prefixed with\nthe name of the struct. For example:\n\n    ma_atomic_uint32_set() - Atomic store of ma_uint32\n    ma_atomic_uint32_get() - Atomic load of ma_uint32\n    etc.\n\nFor pointer types it's the same, which makes them a bit messy to use due to the length of each function name, but in\nreturn you get type safety and enforcement of atomic operations.\n*/\n#define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \\\n    typedef struct \\\n    { \\\n        MA_ATOMIC(typeSize, ma_##type) value; \\\n    } ma_atomic_##type; \\\n\n#define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \\\n    typedef struct \\\n    { \\\n        MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \\\n    } ma_atomic_ptr_##type; \\\n\nMA_ATOMIC_SAFE_TYPE_DECL(32,  4, uint32)\nMA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32)\nMA_ATOMIC_SAFE_TYPE_DECL(64,  8, uint64)\nMA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float)\nMA_ATOMIC_SAFE_TYPE_DECL(32,  4, bool32)\n\n\n/* Spinlocks are 32-bit for compatibility reasons. */\ntypedef ma_uint32 ma_spinlock;\n\n#ifndef MA_NO_THREADING\n    /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */\n    typedef enum\n    {\n        ma_thread_priority_idle     = -5,\n        ma_thread_priority_lowest   = -4,\n        ma_thread_priority_low      = -3,\n        ma_thread_priority_normal   = -2,\n        ma_thread_priority_high     = -1,\n        ma_thread_priority_highest  =  0,\n        ma_thread_priority_realtime =  1,\n        ma_thread_priority_default  =  0\n    } ma_thread_priority;\n\n    #if defined(MA_POSIX)\n        typedef ma_pthread_t ma_thread;\n    #elif defined(MA_WIN32)\n        typedef ma_handle ma_thread;\n    #endif\n\n    #if defined(MA_POSIX)\n        typedef ma_pthread_mutex_t ma_mutex;\n    #elif defined(MA_WIN32)\n        typedef ma_handle ma_mutex;\n    #endif\n\n    #if defined(MA_POSIX)\n        typedef struct\n        {\n            ma_uint32 value;\n            ma_pthread_mutex_t lock;\n            ma_pthread_cond_t cond;\n        } ma_event;\n    #elif defined(MA_WIN32)\n        typedef ma_handle ma_event;\n    #endif\n\n    #if defined(MA_POSIX)\n        typedef struct\n        {\n            int value;\n            ma_pthread_mutex_t lock;\n            ma_pthread_cond_t cond;\n        } ma_semaphore;\n    #elif defined(MA_WIN32)\n        typedef ma_handle ma_semaphore;\n    #endif\n#else\n    /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */\n    #ifndef MA_NO_DEVICE_IO\n        #error \"MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO\";\n    #endif\n#endif  /* MA_NO_THREADING */\n\n\n/*\nRetrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required.\n*/\nMA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);\n\n/*\nRetrieves the version of miniaudio as a string which can be useful for logging purposes.\n*/\nMA_API const char* ma_version_string(void);\n\n\n/**************************************************************************************************************************************************************\n\nLogging\n\n**************************************************************************************************************************************************************/\n#include <stdarg.h> /* For va_list. */\n\n#if defined(__has_attribute)\n    #if __has_attribute(format)\n        #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va)))\n    #endif\n#endif\n#ifndef MA_ATTRIBUTE_FORMAT\n#define MA_ATTRIBUTE_FORMAT(fmt, va)\n#endif\n\n#ifndef MA_MAX_LOG_CALLBACKS\n#define MA_MAX_LOG_CALLBACKS    4\n#endif\n\n\n/*\nThe callback for handling log messages.\n\n\nParameters\n----------\npUserData (in)\n    The user data pointer that was passed into ma_log_register_callback().\n\nlogLevel (in)\n    The log level. This can be one of the following:\n\n    +----------------------+\n    | Log Level            |\n    +----------------------+\n    | MA_LOG_LEVEL_DEBUG   |\n    | MA_LOG_LEVEL_INFO    |\n    | MA_LOG_LEVEL_WARNING |\n    | MA_LOG_LEVEL_ERROR   |\n    +----------------------+\n\npMessage (in)\n    The log message.\n*/\ntypedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage);\n\ntypedef struct\n{\n    ma_log_callback_proc onLog;\n    void* pUserData;\n} ma_log_callback;\n\nMA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData);\n\n\ntypedef struct\n{\n    ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS];\n    ma_uint32 callbackCount;\n    ma_allocation_callbacks allocationCallbacks;    /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */\n#ifndef MA_NO_THREADING\n    ma_mutex lock;  /* For thread safety just to make it easier and safer for the logging implementation. */\n#endif\n} ma_log;\n\nMA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog);\nMA_API void ma_log_uninit(ma_log* pLog);\nMA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback);\nMA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback);\nMA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage);\nMA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args);\nMA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4);\n\n\n/**************************************************************************************************************************************************************\n\nBiquad Filtering\n\n**************************************************************************************************************************************************************/\ntypedef union\n{\n    float    f32;\n    ma_int32 s32;\n} ma_biquad_coefficient;\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    double b0;\n    double b1;\n    double b2;\n    double a0;\n    double a1;\n    double a2;\n} ma_biquad_config;\n\nMA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2);\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_biquad_coefficient b0;\n    ma_biquad_coefficient b1;\n    ma_biquad_coefficient b2;\n    ma_biquad_coefficient a1;\n    ma_biquad_coefficient a2;\n    ma_biquad_coefficient* pR1;\n    ma_biquad_coefficient* pR2;\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_biquad;\n\nMA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ);\nMA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ);\nMA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ);\nMA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ);\nMA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ);\n\n\n/**************************************************************************************************************************************************************\n\nLow-Pass Filtering\n\n**************************************************************************************************************************************************************/\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    double cutoffFrequency;\n    double q;\n} ma_lpf1_config, ma_lpf2_config;\n\nMA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);\nMA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_biquad_coefficient a;\n    ma_biquad_coefficient* pR1;\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_lpf1;\n\nMA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF);\nMA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF);\nMA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF);\nMA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF);\nMA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF);\n\ntypedef struct\n{\n    ma_biquad bq;   /* The second order low-pass filter is implemented as a biquad filter. */\n} ma_lpf2;\n\nMA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF);\nMA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF);\nMA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF);\nMA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF);\nMA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF);\n\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    double cutoffFrequency;\n    ma_uint32 order;    /* If set to 0, will be treated as a passthrough (no filtering will be applied). */\n} ma_lpf_config;\n\nMA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_uint32 lpf1Count;\n    ma_uint32 lpf2Count;\n    ma_lpf1* pLPF1;\n    ma_lpf2* pLPF2;\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_lpf;\n\nMA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF);\nMA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF);\nMA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF);\nMA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF);\nMA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF);\n\n\n/**************************************************************************************************************************************************************\n\nHigh-Pass Filtering\n\n**************************************************************************************************************************************************************/\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    double cutoffFrequency;\n    double q;\n} ma_hpf1_config, ma_hpf2_config;\n\nMA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);\nMA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_biquad_coefficient a;\n    ma_biquad_coefficient* pR1;\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_hpf1;\n\nMA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF);\nMA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF);\nMA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF);\nMA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF);\n\ntypedef struct\n{\n    ma_biquad bq;   /* The second order high-pass filter is implemented as a biquad filter. */\n} ma_hpf2;\n\nMA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF);\nMA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF);\nMA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF);\nMA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF);\n\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    double cutoffFrequency;\n    ma_uint32 order;    /* If set to 0, will be treated as a passthrough (no filtering will be applied). */\n} ma_hpf_config;\n\nMA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_uint32 hpf1Count;\n    ma_uint32 hpf2Count;\n    ma_hpf1* pHPF1;\n    ma_hpf2* pHPF2;\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_hpf;\n\nMA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF);\nMA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF);\nMA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF);\nMA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF);\n\n\n/**************************************************************************************************************************************************************\n\nBand-Pass Filtering\n\n**************************************************************************************************************************************************************/\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    double cutoffFrequency;\n    double q;\n} ma_bpf2_config;\n\nMA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);\n\ntypedef struct\n{\n    ma_biquad bq;   /* The second order band-pass filter is implemented as a biquad filter. */\n} ma_bpf2;\n\nMA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF);\nMA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF);\nMA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF);\nMA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF);\n\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    double cutoffFrequency;\n    ma_uint32 order;    /* If set to 0, will be treated as a passthrough (no filtering will be applied). */\n} ma_bpf_config;\n\nMA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 bpf2Count;\n    ma_bpf2* pBPF2;\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_bpf;\n\nMA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF);\nMA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF);\nMA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF);\nMA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF);\n\n\n/**************************************************************************************************************************************************************\n\nNotching Filter\n\n**************************************************************************************************************************************************************/\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    double q;\n    double frequency;\n} ma_notch2_config, ma_notch_config;\n\nMA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);\n\ntypedef struct\n{\n    ma_biquad bq;\n} ma_notch2;\n\nMA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter);\nMA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter);\nMA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter);\nMA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter);\n\n\n/**************************************************************************************************************************************************************\n\nPeaking EQ Filter\n\n**************************************************************************************************************************************************************/\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    double gainDB;\n    double q;\n    double frequency;\n} ma_peak2_config, ma_peak_config;\n\nMA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);\n\ntypedef struct\n{\n    ma_biquad bq;\n} ma_peak2;\n\nMA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter);\nMA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter);\nMA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter);\nMA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter);\n\n\n/**************************************************************************************************************************************************************\n\nLow Shelf Filter\n\n**************************************************************************************************************************************************************/\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    double gainDB;\n    double shelfSlope;\n    double frequency;\n} ma_loshelf2_config, ma_loshelf_config;\n\nMA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);\n\ntypedef struct\n{\n    ma_biquad bq;\n} ma_loshelf2;\n\nMA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter);\nMA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter);\nMA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter);\nMA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter);\n\n\n/**************************************************************************************************************************************************************\n\nHigh Shelf Filter\n\n**************************************************************************************************************************************************************/\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    double gainDB;\n    double shelfSlope;\n    double frequency;\n} ma_hishelf2_config, ma_hishelf_config;\n\nMA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);\n\ntypedef struct\n{\n    ma_biquad bq;\n} ma_hishelf2;\n\nMA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter);\nMA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter);\nMA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter);\nMA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter);\n\n\n\n/*\nDelay\n*/\ntypedef struct\n{\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_uint32 delayInFrames;\n    ma_bool32 delayStart;       /* Set to true to delay the start of the output; false otherwise. */\n    float wet;                  /* 0..1. Default = 1. */\n    float dry;                  /* 0..1. Default = 1. */\n    float decay;                /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */\n} ma_delay_config;\n\nMA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay);\n\n\ntypedef struct\n{\n    ma_delay_config config;\n    ma_uint32 cursor;               /* Feedback is written to this cursor. Always equal or in front of the read cursor. */\n    ma_uint32 bufferSizeInFrames;\n    float* pBuffer;\n} ma_delay;\n\nMA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay);\nMA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount);\nMA_API void ma_delay_set_wet(ma_delay* pDelay, float value);\nMA_API float ma_delay_get_wet(const ma_delay* pDelay);\nMA_API void ma_delay_set_dry(ma_delay* pDelay, float value);\nMA_API float ma_delay_get_dry(const ma_delay* pDelay);\nMA_API void ma_delay_set_decay(ma_delay* pDelay, float value);\nMA_API float ma_delay_get_decay(const ma_delay* pDelay);\n\n\n/* Gainer for smooth volume changes. */\ntypedef struct\n{\n    ma_uint32 channels;\n    ma_uint32 smoothTimeInFrames;\n} ma_gainer_config;\n\nMA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames);\n\n\ntypedef struct\n{\n    ma_gainer_config config;\n    ma_uint32 t;\n    float masterVolume;\n    float* pOldGains;\n    float* pNewGains;\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_gainer;\n\nMA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer);\nMA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer);\nMA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain);\nMA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains);\nMA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume);\nMA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume);\n\n\n\n/* Stereo panner. */\ntypedef enum\n{\n    ma_pan_mode_balance = 0,    /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */\n    ma_pan_mode_pan             /* A true pan. The sound from one side will \"move\" to the other side and blend with it. */\n} ma_pan_mode;\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_pan_mode mode;\n    float pan;\n} ma_panner_config;\n\nMA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels);\n\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_pan_mode mode;\n    float pan;  /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */\n} ma_panner;\n\nMA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner);\nMA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode);\nMA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner);\nMA_API void ma_panner_set_pan(ma_panner* pPanner, float pan);\nMA_API float ma_panner_get_pan(const ma_panner* pPanner);\n\n\n\n/* Fader. */\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n} ma_fader_config;\n\nMA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate);\n\ntypedef struct\n{\n    ma_fader_config config;\n    float volumeBeg;            /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */\n    float volumeEnd;\n    ma_uint64 lengthInFrames;   /* The total length of the fade. */\n    ma_int64  cursorInFrames;   /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */\n} ma_fader;\n\nMA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader);\nMA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);\nMA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames);\nMA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames);\nMA_API float ma_fader_get_current_volume(const ma_fader* pFader);\n\n\n\n/* Spatializer. */\ntypedef struct\n{\n    float x;\n    float y;\n    float z;\n} ma_vec3f;\n\ntypedef struct\n{\n    ma_vec3f v;\n    ma_spinlock lock;\n} ma_atomic_vec3f;\n\ntypedef enum\n{\n    ma_attenuation_model_none,          /* No distance attenuation and no spatialization. */\n    ma_attenuation_model_inverse,       /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */\n    ma_attenuation_model_linear,        /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */\n    ma_attenuation_model_exponential    /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */\n} ma_attenuation_model;\n\ntypedef enum\n{\n    ma_positioning_absolute,\n    ma_positioning_relative\n} ma_positioning;\n\ntypedef enum\n{\n    ma_handedness_right,\n    ma_handedness_left\n} ma_handedness;\n\n\ntypedef struct\n{\n    ma_uint32 channelsOut;\n    ma_channel* pChannelMapOut;\n    ma_handedness handedness;   /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */\n    float coneInnerAngleInRadians;\n    float coneOuterAngleInRadians;\n    float coneOuterGain;\n    float speedOfSound;\n    ma_vec3f worldUp;\n} ma_spatializer_listener_config;\n\nMA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut);\n\n\ntypedef struct\n{\n    ma_spatializer_listener_config config;\n    ma_atomic_vec3f position;  /* The absolute position of the listener. */\n    ma_atomic_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */\n    ma_atomic_vec3f velocity;\n    ma_bool32 isEnabled;\n\n    /* Memory management. */\n    ma_bool32 _ownsHeap;\n    void* _pHeap;\n} ma_spatializer_listener;\n\nMA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener);\nMA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener);\nMA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener);\nMA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain);\nMA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);\nMA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z);\nMA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener);\nMA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z);\nMA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener);\nMA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z);\nMA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener);\nMA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound);\nMA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener);\nMA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z);\nMA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener);\nMA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled);\nMA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener);\n\n\ntypedef struct\n{\n    ma_uint32 channelsIn;\n    ma_uint32 channelsOut;\n    ma_channel* pChannelMapIn;\n    ma_attenuation_model attenuationModel;\n    ma_positioning positioning;\n    ma_handedness handedness;           /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */\n    float minGain;\n    float maxGain;\n    float minDistance;\n    float maxDistance;\n    float rolloff;\n    float coneInnerAngleInRadians;\n    float coneOuterAngleInRadians;\n    float coneOuterGain;\n    float dopplerFactor;                /* Set to 0 to disable doppler effect. */\n    float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */\n    float minSpatializationChannelGain; /* The minimal scaling factor to apply to channel gains when accounting for the direction of the sound relative to the listener. Must be in the range of 0..1. Smaller values means more aggressive directional panning, larger values means more subtle directional panning. */\n    ma_uint32 gainSmoothTimeInFrames;   /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */\n} ma_spatializer_config;\n\nMA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut);\n\n\ntypedef struct\n{\n    ma_uint32 channelsIn;\n    ma_uint32 channelsOut;\n    ma_channel* pChannelMapIn;\n    ma_attenuation_model attenuationModel;\n    ma_positioning positioning;\n    ma_handedness handedness;           /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */\n    float minGain;\n    float maxGain;\n    float minDistance;\n    float maxDistance;\n    float rolloff;\n    float coneInnerAngleInRadians;\n    float coneOuterAngleInRadians;\n    float coneOuterGain;\n    float dopplerFactor;                /* Set to 0 to disable doppler effect. */\n    float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */\n    ma_uint32 gainSmoothTimeInFrames;   /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */\n    ma_atomic_vec3f position;\n    ma_atomic_vec3f direction;\n    ma_atomic_vec3f velocity;  /* For doppler effect. */\n    float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */\n    float minSpatializationChannelGain;\n    ma_gainer gainer;   /* For smooth gain transitions. */\n    float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_spatializer;\n\nMA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer);\nMA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume);\nMA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume);\nMA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer);\nMA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel);\nMA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning);\nMA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff);\nMA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain);\nMA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain);\nMA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance);\nMA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance);\nMA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain);\nMA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);\nMA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor);\nMA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor);\nMA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z);\nMA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z);\nMA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z);\nMA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer);\nMA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir);\n\n\n\n/************************************************************************************************************************************************************\n*************************************************************************************************************************************************************\n\nDATA CONVERSION\n===============\n\nThis section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc.\n\n*************************************************************************************************************************************************************\n************************************************************************************************************************************************************/\n\n/**************************************************************************************************************************************************************\n\nResampling\n\n**************************************************************************************************************************************************************/\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRateIn;\n    ma_uint32 sampleRateOut;\n    ma_uint32 lpfOrder;         /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */\n    double    lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */\n} ma_linear_resampler_config;\n\nMA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);\n\ntypedef struct\n{\n    ma_linear_resampler_config config;\n    ma_uint32 inAdvanceInt;\n    ma_uint32 inAdvanceFrac;\n    ma_uint32 inTimeInt;\n    ma_uint32 inTimeFrac;\n    union\n    {\n        float* f32;\n        ma_int16* s16;\n    } x0; /* The previous input frame. */\n    union\n    {\n        float* f32;\n        ma_int16* s16;\n    } x1; /* The next input frame. */\n    ma_lpf lpf;\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_linear_resampler;\n\nMA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler);\nMA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler);\nMA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);\nMA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);\nMA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut);\nMA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler);\nMA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler);\nMA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);\nMA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);\nMA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler);\n\n\ntypedef struct ma_resampler_config ma_resampler_config;\n\ntypedef void ma_resampling_backend;\ntypedef struct\n{\n    ma_result (* onGetHeapSize                )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes);\n    ma_result (* onInit                       )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend);\n    void      (* onUninit                     )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);\n    ma_result (* onProcess                    )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);\n    ma_result (* onSetRate                    )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);                 /* Optional. Rate changes will be disabled. */\n    ma_uint64 (* onGetInputLatency            )(void* pUserData, const ma_resampling_backend* pBackend);                                                            /* Optional. Latency will be reported as 0. */\n    ma_uint64 (* onGetOutputLatency           )(void* pUserData, const ma_resampling_backend* pBackend);                                                            /* Optional. Latency will be reported as 0. */\n    ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);   /* Optional. Latency mitigation will be disabled. */\n    ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);   /* Optional. Latency mitigation will be disabled. */\n    ma_result (* onReset                      )(void* pUserData, ma_resampling_backend* pBackend);\n} ma_resampling_backend_vtable;\n\ntypedef enum\n{\n    ma_resample_algorithm_linear = 0,    /* Fastest, lowest quality. Optional low-pass filtering. Default. */\n    ma_resample_algorithm_custom,\n} ma_resample_algorithm;\n\nstruct ma_resampler_config\n{\n    ma_format format;   /* Must be either ma_format_f32 or ma_format_s16. */\n    ma_uint32 channels;\n    ma_uint32 sampleRateIn;\n    ma_uint32 sampleRateOut;\n    ma_resample_algorithm algorithm;    /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */\n    ma_resampling_backend_vtable* pBackendVTable;\n    void* pBackendUserData;\n    struct\n    {\n        ma_uint32 lpfOrder;\n    } linear;\n};\n\nMA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm);\n\ntypedef struct\n{\n    ma_resampling_backend* pBackend;\n    ma_resampling_backend_vtable* pBackendVTable;\n    void* pBackendUserData;\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRateIn;\n    ma_uint32 sampleRateOut;\n    union\n    {\n        ma_linear_resampler linear;\n    } state;    /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_resampler;\n\nMA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler);\n\n/*\nInitializes a new resampler object from a config.\n*/\nMA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler);\n\n/*\nUninitializes a resampler.\n*/\nMA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks);\n\n/*\nConverts the given input data.\n\nBoth the input and output frames must be in the format specified in the config when the resampler was initialized.\n\nOn input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that\nwere actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use\nma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames.\n\nOn input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole\ninput frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames\nyou should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead.\n\nIf [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of\noutput frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input\nframes. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be\nprocessed. In this case, any internal filter state will be updated as if zeroes were passed in.\n\nIt is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL.\n\nIt is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.\n*/\nMA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);\n\n\n/*\nSets the input and output sample rate.\n*/\nMA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);\n\n/*\nSets the input and output sample rate as a ratio.\n\nThe ration is in/out.\n*/\nMA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio);\n\n/*\nRetrieves the latency introduced by the resampler in input frames.\n*/\nMA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler);\n\n/*\nRetrieves the latency introduced by the resampler in output frames.\n*/\nMA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler);\n\n/*\nCalculates the number of whole input frames that would need to be read from the client in order to output the specified\nnumber of output frames.\n\nThe returned value does not include cached input frames. It only returns the number of extra frames that would need to be\nread from the input buffer in order to output the specified number of output frames.\n*/\nMA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);\n\n/*\nCalculates the number of whole output frames that would be output after fully reading and consuming the specified number of\ninput frames.\n*/\nMA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);\n\n/*\nResets the resampler's timer and clears it's internal cache.\n*/\nMA_API ma_result ma_resampler_reset(ma_resampler* pResampler);\n\n\n/**************************************************************************************************************************************************************\n\nChannel Conversion\n\n**************************************************************************************************************************************************************/\ntypedef enum\n{\n    ma_channel_conversion_path_unknown,\n    ma_channel_conversion_path_passthrough,\n    ma_channel_conversion_path_mono_out,    /* Converting to mono. */\n    ma_channel_conversion_path_mono_in,     /* Converting from mono. */\n    ma_channel_conversion_path_shuffle,     /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */\n    ma_channel_conversion_path_weights      /* Blended based on weights. */\n} ma_channel_conversion_path;\n\ntypedef enum\n{\n    ma_mono_expansion_mode_duplicate = 0,   /* The default. */\n    ma_mono_expansion_mode_average,         /* Average the mono channel across all channels. */\n    ma_mono_expansion_mode_stereo_only,     /* Duplicate to the left and right channels only and ignore the others. */\n    ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate\n} ma_mono_expansion_mode;\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channelsIn;\n    ma_uint32 channelsOut;\n    const ma_channel* pChannelMapIn;\n    const ma_channel* pChannelMapOut;\n    ma_channel_mix_mode mixingMode;\n    ma_bool32 calculateLFEFromSpatialChannels;  /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */\n    float** ppWeights;  /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */\n} ma_channel_converter_config;\n\nMA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode);\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channelsIn;\n    ma_uint32 channelsOut;\n    ma_channel_mix_mode mixingMode;\n    ma_channel_conversion_path conversionPath;\n    ma_channel* pChannelMapIn;\n    ma_channel* pChannelMapOut;\n    ma_uint8* pShuffleTable;    /* Indexed by output channel index. */\n    union\n    {\n        float**    f32;\n        ma_int32** s16;\n    } weights;  /* [in][out] */\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_channel_converter;\n\nMA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter);\nMA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter);\nMA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);\nMA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);\n\n\n/**************************************************************************************************************************************************************\n\nData Conversion\n\n**************************************************************************************************************************************************************/\ntypedef struct\n{\n    ma_format formatIn;\n    ma_format formatOut;\n    ma_uint32 channelsIn;\n    ma_uint32 channelsOut;\n    ma_uint32 sampleRateIn;\n    ma_uint32 sampleRateOut;\n    ma_channel* pChannelMapIn;\n    ma_channel* pChannelMapOut;\n    ma_dither_mode ditherMode;\n    ma_channel_mix_mode channelMixMode;\n    ma_bool32 calculateLFEFromSpatialChannels;  /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */\n    float** ppChannelWeights;  /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */\n    ma_bool32 allowDynamicSampleRate;\n    ma_resampler_config resampling;\n} ma_data_converter_config;\n\nMA_API ma_data_converter_config ma_data_converter_config_init_default(void);\nMA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);\n\n\ntypedef enum\n{\n    ma_data_converter_execution_path_passthrough,       /* No conversion. */\n    ma_data_converter_execution_path_format_only,       /* Only format conversion. */\n    ma_data_converter_execution_path_channels_only,     /* Only channel conversion. */\n    ma_data_converter_execution_path_resample_only,     /* Only resampling. */\n    ma_data_converter_execution_path_resample_first,    /* All conversions, but resample as the first step. */\n    ma_data_converter_execution_path_channels_first     /* All conversions, but channels as the first step. */\n} ma_data_converter_execution_path;\n\ntypedef struct\n{\n    ma_format formatIn;\n    ma_format formatOut;\n    ma_uint32 channelsIn;\n    ma_uint32 channelsOut;\n    ma_uint32 sampleRateIn;\n    ma_uint32 sampleRateOut;\n    ma_dither_mode ditherMode;\n    ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */\n    ma_channel_converter channelConverter;\n    ma_resampler resampler;\n    ma_bool8 hasPreFormatConversion;\n    ma_bool8 hasPostFormatConversion;\n    ma_bool8 hasChannelConverter;\n    ma_bool8 hasResampler;\n    ma_bool8 isPassthrough;\n\n    /* Memory management. */\n    ma_bool8 _ownsHeap;\n    void* _pHeap;\n} ma_data_converter;\n\nMA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter);\nMA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter);\nMA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);\nMA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);\nMA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut);\nMA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter);\nMA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter);\nMA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);\nMA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);\nMA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter);\n\n\n/************************************************************************************************************************************************************\n\nFormat Conversion\n\n************************************************************************************************************************************************************/\nMA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);\nMA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode);\nMA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode);\n\n/*\nDeinterleaves an interleaved buffer.\n*/\nMA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);\n\n/*\nInterleaves a group of deinterleaved buffers.\n*/\nMA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);\n\n\n/************************************************************************************************************************************************************\n\nChannel Maps\n\n************************************************************************************************************************************************************/\n/*\nThis is used in the shuffle table to indicate that the channel index is undefined and should be ignored.\n*/\n#define MA_CHANNEL_INDEX_NULL   255\n\n/*\nRetrieves the channel position of the specified channel in the given channel map.\n\nThe pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed.\n*/\nMA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);\n\n/*\nInitializes a blank channel map.\n\nWhen a blank channel map is specified anywhere it indicates that the native channel map should be used.\n*/\nMA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels);\n\n/*\nHelper for retrieving a standard channel map.\n\nThe output channel map buffer must have a capacity of at least `channelMapCap`.\n*/\nMA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels);\n\n/*\nCopies a channel map.\n\nBoth input and output channel map buffers must have a capacity of at at least `channels`.\n*/\nMA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);\n\n/*\nCopies a channel map if one is specified, otherwise copies the default channel map.\n\nThe output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`.\n*/\nMA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels);\n\n\n/*\nDetermines whether or not a channel map is valid.\n\nA blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but\nis usually treated as a passthrough.\n\nInvalid channel maps:\n  - A channel map with no channels\n  - A channel map with more than one channel and a mono channel\n\nThe channel map buffer must have a capacity of at least `channels`.\n*/\nMA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels);\n\n/*\nHelper for comparing two channel maps for equality.\n\nThis assumes the channel count is the same between the two.\n\nBoth channels map buffers must have a capacity of at least `channels`.\n*/\nMA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels);\n\n/*\nHelper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).\n\nThe channel map buffer must have a capacity of at least `channels`.\n*/\nMA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels);\n\n/*\nHelper for determining whether or not a channel is present in the given channel map.\n\nThe channel map buffer must have a capacity of at least `channels`.\n*/\nMA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition);\n\n/*\nFind a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The\nindex of the channel is output to `pChannelIndex`.\n\nThe channel map buffer must have a capacity of at least `channels`.\n*/\nMA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex);\n\n/*\nGenerates a string representing the given channel map.\n\nThis is for printing and debugging purposes, not serialization/deserialization.\n\nReturns the length of the string, not including the null terminator.\n*/\nMA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap);\n\n/*\nRetrieves a human readable version of a channel position.\n*/\nMA_API const char* ma_channel_position_to_string(ma_channel channel);\n\n\n/************************************************************************************************************************************************************\n\nConversion Helpers\n\n************************************************************************************************************************************************************/\n\n/*\nHigh-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to\ndetermine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is\nignored.\n\nA return value of 0 indicates an error.\n\nThis function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead.\n*/\nMA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn);\nMA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig);\n\n\n/************************************************************************************************************************************************************\n\nData Source\n\n************************************************************************************************************************************************************/\ntypedef void ma_data_source;\n\n#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT    0x00000001\n\ntypedef struct\n{\n    ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\n    ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex);\n    ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);\n    ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor);\n    ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength);\n    ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping);\n    ma_uint32 flags;\n} ma_data_source_vtable;\n\ntypedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource);\n\ntypedef struct\n{\n    const ma_data_source_vtable* vtable;\n} ma_data_source_config;\n\nMA_API ma_data_source_config ma_data_source_config_init(void);\n\n\ntypedef struct\n{\n    const ma_data_source_vtable* vtable;\n    ma_uint64 rangeBegInFrames;\n    ma_uint64 rangeEndInFrames;             /* Set to -1 for unranged (default). */\n    ma_uint64 loopBegInFrames;              /* Relative to rangeBegInFrames. */\n    ma_uint64 loopEndInFrames;              /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */\n    ma_data_source* pCurrent;               /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */\n    ma_data_source* pNext;                  /* When set to NULL, onGetNext will be used. */\n    ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */\n    MA_ATOMIC(4, ma_bool32) isLooping;\n} ma_data_source_base;\n\nMA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource);\nMA_API void ma_data_source_uninit(ma_data_source* pDataSource);\nMA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);   /* Must support pFramesOut = NULL in which case a forward seek should be performed. */\nMA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */\nMA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex);\nMA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor);\nMA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength);    /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */\nMA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor);\nMA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength);\nMA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping);\nMA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource);\nMA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames);\nMA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames);\nMA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames);\nMA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames);\nMA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource);\nMA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource);\nMA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource);\nMA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource);\nMA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext);\nMA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource);\n\n\ntypedef struct\n{\n    ma_data_source_base ds;\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_uint64 cursor;\n    ma_uint64 sizeInFrames;\n    const void* pData;\n} ma_audio_buffer_ref;\n\nMA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef);\nMA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef);\nMA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames);\nMA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);\nMA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex);\nMA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount);\nMA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount);    /* Returns MA_AT_END if the end has been reached. This should be considered successful. */\nMA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef);\nMA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor);\nMA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength);\nMA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames);\n\n\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_uint64 sizeInFrames;\n    const void* pData;  /* If set to NULL, will allocate a block of memory for you. */\n    ma_allocation_callbacks allocationCallbacks;\n} ma_audio_buffer_config;\n\nMA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks);\n\ntypedef struct\n{\n    ma_audio_buffer_ref ref;\n    ma_allocation_callbacks allocationCallbacks;\n    ma_bool32 ownsData;             /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */\n    ma_uint8 _pExtraData[1];        /* For allocating a buffer with the memory located directly after the other memory of the structure. */\n} ma_audio_buffer;\n\nMA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);\nMA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);\nMA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer);  /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */\nMA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer);\nMA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer);\nMA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);\nMA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex);\nMA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount);\nMA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount);    /* Returns MA_AT_END if the end has been reached. This should be considered successful. */\nMA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer);\nMA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor);\nMA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength);\nMA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames);\n\n\n/*\nPaged Audio Buffer\n==================\nA paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It\ncan be used for cases where audio data is streamed in asynchronously while allowing data to be read\nat the same time.\n\nThis is lock-free, but not 100% thread safe. You can append a page and read from the buffer across\nsimultaneously across different threads, however only one thread at a time can append, and only one\nthread at a time can read and seek.\n*/\ntypedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page;\nstruct ma_paged_audio_buffer_page\n{\n    MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext;\n    ma_uint64 sizeInFrames;\n    ma_uint8 pAudioData[1];\n};\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_paged_audio_buffer_page head;                                /* Dummy head for the lock-free algorithm. Always has a size of 0. */\n    MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail;    /* Never null. Initially set to &head. */\n} ma_paged_audio_buffer_data;\n\nMA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData);\nMA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData);\nMA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData);\nMA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength);\nMA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage);\nMA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage);\nMA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\ntypedef struct\n{\n    ma_paged_audio_buffer_data* pData;  /* Must not be null. */\n} ma_paged_audio_buffer_config;\n\nMA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData);\n\n\ntypedef struct\n{\n    ma_data_source_base ds;\n    ma_paged_audio_buffer_data* pData;              /* Audio data is read from here. Cannot be null. */\n    ma_paged_audio_buffer_page* pCurrent;\n    ma_uint64 relativeCursor;                       /* Relative to the current page. */\n    ma_uint64 absoluteCursor;\n} ma_paged_audio_buffer;\n\nMA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer);\nMA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer);\nMA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);   /* Returns MA_AT_END if no more pages available. */\nMA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex);\nMA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor);\nMA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength);\n\n\n\n/************************************************************************************************************************************************************\n\nRing Buffer\n\n************************************************************************************************************************************************************/\ntypedef struct\n{\n    void* pBuffer;\n    ma_uint32 subbufferSizeInBytes;\n    ma_uint32 subbufferCount;\n    ma_uint32 subbufferStrideInBytes;\n    MA_ATOMIC(4, ma_uint32) encodedReadOffset;  /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */\n    MA_ATOMIC(4, ma_uint32) encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */\n    ma_bool8 ownsBuffer;                        /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */\n    ma_bool8 clearOnWriteAcquire;               /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */\n    ma_allocation_callbacks allocationCallbacks;\n} ma_rb;\n\nMA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);\nMA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);\nMA_API void ma_rb_uninit(ma_rb* pRB);\nMA_API void ma_rb_reset(ma_rb* pRB);\nMA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);\nMA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes);\nMA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);\nMA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes);\nMA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes);\nMA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes);\nMA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB);    /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */\nMA_API ma_uint32 ma_rb_available_read(ma_rb* pRB);\nMA_API ma_uint32 ma_rb_available_write(ma_rb* pRB);\nMA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB);\nMA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB);\nMA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex);\nMA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer);\n\n\ntypedef struct\n{\n    ma_data_source_base ds;\n    ma_rb rb;\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */\n} ma_pcm_rb;\n\nMA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB);\nMA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB);\nMA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB);\nMA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB);\nMA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);\nMA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames);\nMA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);\nMA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames);\nMA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);\nMA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);\nMA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */\nMA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB);\nMA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB);\nMA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB);\nMA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB);\nMA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex);\nMA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer);\nMA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB);\nMA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB);\nMA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB);\nMA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate);\n\n\n/*\nThe idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The\ncapture device writes to it, and then a playback device reads from it.\n\nAt the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly\nhandle desyncs. Note that the API is work in progress and may change at any time in any version.\n\nThe size of the buffer is based on the capture side since that's what'll be written to the buffer. It is based on the capture period size\nin frames. The internal sample rate of the capture device is also needed in order to calculate the size.\n*/\ntypedef struct\n{\n    ma_pcm_rb rb;\n} ma_duplex_rb;\n\nMA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB);\nMA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB);\n\n\n/************************************************************************************************************************************************************\n\nMiscellaneous Helpers\n\n************************************************************************************************************************************************************/\n/*\nRetrieves a human readable description of the given result code.\n*/\nMA_API const char* ma_result_description(ma_result result);\n\n/*\nmalloc()\n*/\nMA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);\n\n/*\ncalloc()\n*/\nMA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);\n\n/*\nrealloc()\n*/\nMA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);\n\n/*\nfree()\n*/\nMA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);\n\n/*\nPerforms an aligned malloc, with the assumption that the alignment is a power of 2.\n*/\nMA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks);\n\n/*\nFree's an aligned malloc'd buffer.\n*/\nMA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);\n\n/*\nRetrieves a friendly name for a format.\n*/\nMA_API const char* ma_get_format_name(ma_format format);\n\n/*\nBlends two frames in floating point format.\n*/\nMA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels);\n\n/*\nRetrieves the size of a sample in bytes for the given format.\n\nThis API is efficient and is implemented using a lookup table.\n\nThread Safety: SAFE\n  This API is pure.\n*/\nMA_API ma_uint32 ma_get_bytes_per_sample(ma_format format);\nstatic MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; }\n\n/*\nConverts a log level to a string.\n*/\nMA_API const char* ma_log_level_to_string(ma_uint32 logLevel);\n\n\n\n\n/************************************************************************************************************************************************************\n\nSynchronization\n\n************************************************************************************************************************************************************/\n/*\nLocks a spinlock.\n*/\nMA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock);\n\n/*\nLocks a spinlock, but does not yield() when looping.\n*/\nMA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock);\n\n/*\nUnlocks a spinlock.\n*/\nMA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock);\n\n\n#ifndef MA_NO_THREADING\n\n/*\nCreates a mutex.\n\nA mutex must be created from a valid context. A mutex is initially unlocked.\n*/\nMA_API ma_result ma_mutex_init(ma_mutex* pMutex);\n\n/*\nDeletes a mutex.\n*/\nMA_API void ma_mutex_uninit(ma_mutex* pMutex);\n\n/*\nLocks a mutex with an infinite timeout.\n*/\nMA_API void ma_mutex_lock(ma_mutex* pMutex);\n\n/*\nUnlocks a mutex.\n*/\nMA_API void ma_mutex_unlock(ma_mutex* pMutex);\n\n\n/*\nInitializes an auto-reset event.\n*/\nMA_API ma_result ma_event_init(ma_event* pEvent);\n\n/*\nUninitializes an auto-reset event.\n*/\nMA_API void ma_event_uninit(ma_event* pEvent);\n\n/*\nWaits for the specified auto-reset event to become signalled.\n*/\nMA_API ma_result ma_event_wait(ma_event* pEvent);\n\n/*\nSignals the specified auto-reset event.\n*/\nMA_API ma_result ma_event_signal(ma_event* pEvent);\n#endif  /* MA_NO_THREADING */\n\n\n/*\nFence\n=====\nThis locks while the counter is larger than 0. Counter can be incremented and decremented by any\nthread, but care needs to be taken when waiting. It is possible for one thread to acquire the\nfence just as another thread returns from ma_fence_wait().\n\nThe idea behind a fence is to allow you to wait for a group of operations to complete. When an\noperation starts, the counter is incremented which locks the fence. When the operation completes,\nthe fence will be released which decrements the counter. ma_fence_wait() will block until the\ncounter hits zero.\n\nIf threading is disabled, ma_fence_wait() will spin on the counter.\n*/\ntypedef struct\n{\n#ifndef MA_NO_THREADING\n    ma_event e;\n#endif\n    ma_uint32 counter;\n} ma_fence;\n\nMA_API ma_result ma_fence_init(ma_fence* pFence);\nMA_API void ma_fence_uninit(ma_fence* pFence);\nMA_API ma_result ma_fence_acquire(ma_fence* pFence);    /* Increment counter. */\nMA_API ma_result ma_fence_release(ma_fence* pFence);    /* Decrement counter. */\nMA_API ma_result ma_fence_wait(ma_fence* pFence);       /* Wait for counter to reach 0. */\n\n\n\n/*\nNotification callback for asynchronous operations.\n*/\ntypedef void ma_async_notification;\n\ntypedef struct\n{\n    void (* onSignal)(ma_async_notification* pNotification);\n} ma_async_notification_callbacks;\n\nMA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification);\n\n\n/*\nSimple polling notification.\n\nThis just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled()\n*/\ntypedef struct\n{\n    ma_async_notification_callbacks cb;\n    ma_bool32 signalled;\n} ma_async_notification_poll;\n\nMA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll);\nMA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll);\n\n\n/*\nEvent Notification\n\nThis uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail.\n*/\ntypedef struct\n{\n    ma_async_notification_callbacks cb;\n#ifndef MA_NO_THREADING\n    ma_event e;\n#endif\n} ma_async_notification_event;\n\nMA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent);\nMA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent);\nMA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent);\nMA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent);\n\n\n\n\n/************************************************************************************************************************************************************\n\nJob Queue\n\n************************************************************************************************************************************************************/\n\n/*\nSlot Allocator\n--------------\nThe idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used\nas the insertion point for an object.\n\nSlots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs.\n\nThe slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits:\n\n    +-----------------+-----------------+\n    | 32 Bits         | 32 Bits         |\n    +-----------------+-----------------+\n    | Reference Count | Slot Index      |\n    +-----------------+-----------------+\n*/\ntypedef struct\n{\n    ma_uint32 capacity;    /* The number of slots to make available. */\n} ma_slot_allocator_config;\n\nMA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity);\n\n\ntypedef struct\n{\n    MA_ATOMIC(4, ma_uint32) bitfield;   /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */\n} ma_slot_allocator_group;\n\ntypedef struct\n{\n    ma_slot_allocator_group* pGroups;   /* Slots are grouped in chunks of 32. */\n    ma_uint32* pSlots;                  /* 32 bits for reference counting for ABA mitigation. */\n    ma_uint32 count;                    /* Allocation count. */\n    ma_uint32 capacity;\n\n    /* Memory management. */\n    ma_bool32 _ownsHeap;\n    void* _pHeap;\n} ma_slot_allocator;\n\nMA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator);\nMA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator);\nMA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot);\nMA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot);\n\n\ntypedef struct ma_job ma_job;\n\n/*\nCallback for processing a job. Each job type will have their own processing callback which will be\ncalled by ma_job_process().\n*/\ntypedef ma_result (* ma_job_proc)(ma_job* pJob);\n\n/* When a job type is added here an callback needs to be added go \"g_jobVTable\" in the implementation section. */\ntypedef enum\n{\n    /* Miscellaneous. */\n    MA_JOB_TYPE_QUIT = 0,\n    MA_JOB_TYPE_CUSTOM,\n\n    /* Resource Manager. */\n    MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE,\n    MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE,\n    MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE,\n    MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER,\n    MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER,\n    MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM,\n    MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM,\n    MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM,\n    MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM,\n\n    /* Device. */\n    MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE,\n\n    /* Count. Must always be last. */\n    MA_JOB_TYPE_COUNT\n} ma_job_type;\n\nstruct ma_job\n{\n    union\n    {\n        struct\n        {\n            ma_uint16 code;         /* Job type. */\n            ma_uint16 slot;         /* Index into a ma_slot_allocator. */\n            ma_uint32 refcount;\n        } breakup;\n        ma_uint64 allocation;\n    } toc;  /* 8 bytes. We encode the job code into the slot allocation data to save space. */\n    MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */\n    ma_uint32 order;    /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */\n\n    union\n    {\n        /* Miscellaneous. */\n        struct\n        {\n            ma_job_proc proc;\n            ma_uintptr data0;\n            ma_uintptr data1;\n        } custom;\n\n        /* Resource Manager */\n        union\n        {\n            struct\n            {\n                /*ma_resource_manager**/ void* pResourceManager;\n                /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;\n                char* pFilePath;\n                wchar_t* pFilePathW;\n                ma_uint32 flags;                                /* Resource manager data source flags that were used when initializing the data buffer. */\n                ma_async_notification* pInitNotification;       /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */\n                ma_async_notification* pDoneNotification;       /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */\n                ma_fence* pInitFence;                           /* Released when initialization of the decoder is complete. */\n                ma_fence* pDoneFence;                           /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */\n            } loadDataBufferNode;\n            struct\n            {\n                /*ma_resource_manager**/ void* pResourceManager;\n                /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;\n                ma_async_notification* pDoneNotification;\n                ma_fence* pDoneFence;\n            } freeDataBufferNode;\n            struct\n            {\n                /*ma_resource_manager**/ void* pResourceManager;\n                /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;\n                /*ma_decoder**/ void* pDecoder;\n                ma_async_notification* pDoneNotification;       /* Signalled when the data buffer has been fully decoded. */\n                ma_fence* pDoneFence;                           /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */\n            } pageDataBufferNode;\n\n            struct\n            {\n                /*ma_resource_manager_data_buffer**/ void* pDataBuffer;\n                ma_async_notification* pInitNotification;       /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */\n                ma_async_notification* pDoneNotification;       /* Signalled when the data buffer has been fully decoded. */\n                ma_fence* pInitFence;                           /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */\n                ma_fence* pDoneFence;                           /* Released when the data buffer has been fully decoded. */\n                ma_uint64 rangeBegInPCMFrames;\n                ma_uint64 rangeEndInPCMFrames;\n                ma_uint64 loopPointBegInPCMFrames;\n                ma_uint64 loopPointEndInPCMFrames;\n                ma_uint32 isLooping;\n            } loadDataBuffer;\n            struct\n            {\n                /*ma_resource_manager_data_buffer**/ void* pDataBuffer;\n                ma_async_notification* pDoneNotification;\n                ma_fence* pDoneFence;\n            } freeDataBuffer;\n\n            struct\n            {\n                /*ma_resource_manager_data_stream**/ void* pDataStream;\n                char* pFilePath;                            /* Allocated when the job is posted, freed by the job thread after loading. */\n                wchar_t* pFilePathW;                        /* ^ As above ^. Only used if pFilePath is NULL. */\n                ma_uint64 initialSeekPoint;\n                ma_async_notification* pInitNotification;   /* Signalled after the first two pages have been decoded and frames can be read from the stream. */\n                ma_fence* pInitFence;\n            } loadDataStream;\n            struct\n            {\n                /*ma_resource_manager_data_stream**/ void* pDataStream;\n                ma_async_notification* pDoneNotification;\n                ma_fence* pDoneFence;\n            } freeDataStream;\n            struct\n            {\n                /*ma_resource_manager_data_stream**/ void* pDataStream;\n                ma_uint32 pageIndex;                    /* The index of the page to decode into. */\n            } pageDataStream;\n            struct\n            {\n                /*ma_resource_manager_data_stream**/ void* pDataStream;\n                ma_uint64 frameIndex;\n            } seekDataStream;\n        } resourceManager;\n\n        /* Device. */\n        union\n        {\n            union\n            {\n                struct\n                {\n                    /*ma_device**/ void* pDevice;\n                    /*ma_device_type*/ ma_uint32 deviceType;\n                } reroute;\n            } aaudio;\n        } device;\n    } data;\n};\n\nMA_API ma_job ma_job_init(ma_uint16 code);\nMA_API ma_result ma_job_process(ma_job* pJob);\n\n\n/*\nWhen set, ma_job_queue_next() will not wait and no semaphore will be signaled in\nma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available.\n\nThis flag should always be used for platforms that do not support multithreading.\n*/\ntypedef enum\n{\n    MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001\n} ma_job_queue_flags;\n\ntypedef struct\n{\n    ma_uint32 flags;\n    ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */\n} ma_job_queue_config;\n\nMA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity);\n\n\ntypedef struct\n{\n    ma_uint32 flags;                /* Flags passed in at initialization time. */\n    ma_uint32 capacity;             /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */\n    MA_ATOMIC(8, ma_uint64) head;   /* The first item in the list. Required for removing from the top of the list. */\n    MA_ATOMIC(8, ma_uint64) tail;   /* The last item in the list. Required for appending to the end of the list. */\n#ifndef MA_NO_THREADING\n    ma_semaphore sem;               /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */\n#endif\n    ma_slot_allocator allocator;\n    ma_job* pJobs;\n#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE\n    ma_spinlock lock;\n#endif\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_job_queue;\n\nMA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue);\nMA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue);\nMA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob);\nMA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */\n\n\n\n/************************************************************************************************************************************************************\n*************************************************************************************************************************************************************\n\nDEVICE I/O\n==========\n\nThis section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc.\n\n*************************************************************************************************************************************************************\n************************************************************************************************************************************************************/\n#ifndef MA_NO_DEVICE_IO\n/* Some backends are only supported on certain platforms. */\n#if defined(MA_WIN32)\n    #define MA_SUPPORT_WASAPI\n\n    #if defined(MA_WIN32_DESKTOP)   /* DirectSound and WinMM backends are only supported on desktops. */\n        #define MA_SUPPORT_DSOUND\n        #define MA_SUPPORT_WINMM\n\n        /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */\n        #if !defined(__COSMOPOLITAN__)\n            #define MA_SUPPORT_JACK    /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */\n        #endif\n    #endif\n#endif\n#if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO)\n    #if defined(MA_LINUX)\n        #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__)   /* ALSA is not supported on Android. */\n            #define MA_SUPPORT_ALSA\n        #endif\n    #endif\n    #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)\n        #define MA_SUPPORT_PULSEAUDIO\n        #define MA_SUPPORT_JACK\n    #endif\n    #if defined(__OpenBSD__)        /* <-- Change this to \"#if defined(MA_BSD)\" to enable sndio on all BSD flavors. */\n        #define MA_SUPPORT_SNDIO    /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */\n    #endif\n    #if defined(__NetBSD__) || defined(__OpenBSD__)\n        #define MA_SUPPORT_AUDIO4   /* Only support audio(4) on platforms with known support. */\n    #endif\n    #if defined(__FreeBSD__) || defined(__DragonFly__)\n        #define MA_SUPPORT_OSS      /* Only support OSS on specific platforms with known support. */\n    #endif\n#endif\n#if defined(MA_ANDROID)\n    #define MA_SUPPORT_AAUDIO\n    #define MA_SUPPORT_OPENSL\n#endif\n#if defined(MA_APPLE)\n    #define MA_SUPPORT_COREAUDIO\n#endif\n#if defined(MA_EMSCRIPTEN)\n    #define MA_SUPPORT_WEBAUDIO\n#endif\n\n/* All platforms should support custom backends. */\n#define MA_SUPPORT_CUSTOM\n\n/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */\n#if !defined(MA_EMSCRIPTEN)\n#define MA_SUPPORT_NULL\n#endif\n\n\n#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI))\n    #define MA_HAS_WASAPI\n#endif\n#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND))\n    #define MA_HAS_DSOUND\n#endif\n#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM))\n    #define MA_HAS_WINMM\n#endif\n#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA))\n    #define MA_HAS_ALSA\n#endif\n#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO))\n    #define MA_HAS_PULSEAUDIO\n#endif\n#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK))\n    #define MA_HAS_JACK\n#endif\n#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO))\n    #define MA_HAS_COREAUDIO\n#endif\n#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO))\n    #define MA_HAS_SNDIO\n#endif\n#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4))\n    #define MA_HAS_AUDIO4\n#endif\n#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS))\n    #define MA_HAS_OSS\n#endif\n#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO))\n    #define MA_HAS_AAUDIO\n#endif\n#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL))\n    #define MA_HAS_OPENSL\n#endif\n#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))\n    #define MA_HAS_WEBAUDIO\n#endif\n#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))\n    #define MA_HAS_CUSTOM\n#endif\n#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL))\n    #define MA_HAS_NULL\n#endif\n\ntypedef enum\n{\n    ma_device_state_uninitialized = 0,\n    ma_device_state_stopped       = 1,  /* The device's default state after initialization. */\n    ma_device_state_started       = 2,  /* The device is started and is requesting and/or delivering audio data. */\n    ma_device_state_starting      = 3,  /* Transitioning from a stopped state to started. */\n    ma_device_state_stopping      = 4   /* Transitioning from a started state to stopped. */\n} ma_device_state;\n\nMA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state)\n\n\n#ifdef MA_SUPPORT_WASAPI\n/* We need a IMMNotificationClient object for WASAPI. */\ntypedef struct\n{\n    void* lpVtbl;\n    ma_uint32 counter;\n    ma_device* pDevice;\n} ma_IMMNotificationClient;\n#endif\n\n/* Backend enums must be in priority order. */\ntypedef enum\n{\n    ma_backend_wasapi,\n    ma_backend_dsound,\n    ma_backend_winmm,\n    ma_backend_coreaudio,\n    ma_backend_sndio,\n    ma_backend_audio4,\n    ma_backend_oss,\n    ma_backend_pulseaudio,\n    ma_backend_alsa,\n    ma_backend_jack,\n    ma_backend_aaudio,\n    ma_backend_opensl,\n    ma_backend_webaudio,\n    ma_backend_custom,  /* <-- Custom backend, with callbacks defined by the context config. */\n    ma_backend_null     /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */\n} ma_backend;\n\n#define MA_BACKEND_COUNT (ma_backend_null+1)\n\n\n/*\nDevice job thread. This is used by backends that require asynchronous processing of certain\noperations. It is not used by all backends.\n\nThe device job thread is made up of a thread and a job queue. You can post a job to the thread with\nma_device_job_thread_post(). The thread will do the processing of the job.\n*/\ntypedef struct\n{\n    ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */\n    ma_uint32 jobQueueCapacity;\n    ma_uint32 jobQueueFlags;\n} ma_device_job_thread_config;\n\nMA_API ma_device_job_thread_config ma_device_job_thread_config_init(void);\n\ntypedef struct\n{\n    ma_thread thread;\n    ma_job_queue jobQueue;\n    ma_bool32 _hasThread;\n} ma_device_job_thread;\n\nMA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread);\nMA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob);\nMA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob);\n\n\n\n/* Device notification types. */\ntypedef enum\n{\n    ma_device_notification_type_started,\n    ma_device_notification_type_stopped,\n    ma_device_notification_type_rerouted,\n    ma_device_notification_type_interruption_began,\n    ma_device_notification_type_interruption_ended,\n    ma_device_notification_type_unlocked\n} ma_device_notification_type;\n\ntypedef struct\n{\n    ma_device* pDevice;\n    ma_device_notification_type type;\n    union\n    {\n        struct\n        {\n            int _unused;\n        } started;\n        struct\n        {\n            int _unused;\n        } stopped;\n        struct\n        {\n            int _unused;\n        } rerouted;\n        struct\n        {\n            int _unused;\n        } interruption;\n    } data;\n} ma_device_notification;\n\n/*\nThe notification callback for when the application should be notified of a change to the device.\n\nThis callback is used for notifying the application of changes such as when the device has started,\nstopped, rerouted or an interruption has occurred. Note that not all backends will post all\nnotification types. For example, some backends will perform automatic stream routing without any\nkind of notification to the host program which means miniaudio will never know about it and will\nnever be able to fire the rerouted notification. You should keep this in mind when designing your\nprogram.\n\nThe stopped notification will *not* get fired when a device is rerouted.\n\n\nParameters\n----------\npNotification (in)\n    A pointer to a structure containing information about the event. Use the `pDevice` member of\n    this object to retrieve the relevant device. The `type` member can be used to discriminate\n    against each of the notification types.\n\n\nRemarks\n-------\nDo not restart or uninitialize the device from the callback.\n\nNot all notifications will be triggered by all backends, however the started and stopped events\nshould be reliable for all backends. Some backends do not have a good way to detect device\nstoppages due to unplugging the device which may result in the stopped callback not getting\nfired. This has been observed with at least one BSD variant.\n\nThe rerouted notification is fired *after* the reroute has occurred. The stopped notification will\n*not* get fired when a device is rerouted. The following backends are known to do automatic stream\nrerouting, but do not have a way to be notified of the change:\n\n  * DirectSound\n\nThe interruption notifications are used on mobile platforms for detecting when audio is interrupted\ndue to things like an incoming phone call. Currently this is only implemented on iOS. None of the\nAndroid backends will report this notification.\n*/\ntypedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification);\n\n\n/*\nThe callback for processing audio data from the device.\n\nThe data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data\navailable. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the\ncallback will be fired with a consistent frame count.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the relevant device.\n\npOutput (out)\n    A pointer to the output buffer that will receive audio data that will later be played back through the speakers. This will be non-null for a playback or\n    full-duplex device and null for a capture and loopback device.\n\npInput (in)\n    A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a\n    playback device.\n\nframeCount (in)\n    The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The\n    `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must\n    not assume this will always be the same value each time the callback is fired.\n\n\nRemarks\n-------\nYou cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the\ncallback. The following APIs cannot be called from inside the callback:\n\n    ma_device_init()\n    ma_device_init_ex()\n    ma_device_uninit()\n    ma_device_start()\n    ma_device_stop()\n\nThe proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread.\n*/\ntypedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);\n\n\n\n\n/*\nDEPRECATED. Use ma_device_notification_proc instead.\n\nThe callback for when the device has been stopped.\n\nThis will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces\nsuch as being unplugged or an internal error occurring.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device that has just stopped.\n\n\nRemarks\n-------\nDo not restart or uninitialize the device from the callback.\n*/\ntypedef void (* ma_stop_proc)(ma_device* pDevice);  /* DEPRECATED. Use ma_device_notification_proc instead. */\n\ntypedef enum\n{\n    ma_device_type_playback = 1,\n    ma_device_type_capture  = 2,\n    ma_device_type_duplex   = ma_device_type_playback | ma_device_type_capture, /* 3 */\n    ma_device_type_loopback = 4\n} ma_device_type;\n\ntypedef enum\n{\n    ma_share_mode_shared = 0,\n    ma_share_mode_exclusive\n} ma_share_mode;\n\n/* iOS/tvOS/watchOS session categories. */\ntypedef enum\n{\n    ma_ios_session_category_default = 0,        /* AVAudioSessionCategoryPlayAndRecord. */\n    ma_ios_session_category_none,               /* Leave the session category unchanged. */\n    ma_ios_session_category_ambient,            /* AVAudioSessionCategoryAmbient */\n    ma_ios_session_category_solo_ambient,       /* AVAudioSessionCategorySoloAmbient */\n    ma_ios_session_category_playback,           /* AVAudioSessionCategoryPlayback */\n    ma_ios_session_category_record,             /* AVAudioSessionCategoryRecord */\n    ma_ios_session_category_play_and_record,    /* AVAudioSessionCategoryPlayAndRecord */\n    ma_ios_session_category_multi_route         /* AVAudioSessionCategoryMultiRoute */\n} ma_ios_session_category;\n\n/* iOS/tvOS/watchOS session category options */\ntypedef enum\n{\n    ma_ios_session_category_option_mix_with_others                            = 0x01,   /* AVAudioSessionCategoryOptionMixWithOthers */\n    ma_ios_session_category_option_duck_others                                = 0x02,   /* AVAudioSessionCategoryOptionDuckOthers */\n    ma_ios_session_category_option_allow_bluetooth                            = 0x04,   /* AVAudioSessionCategoryOptionAllowBluetooth */\n    ma_ios_session_category_option_default_to_speaker                         = 0x08,   /* AVAudioSessionCategoryOptionDefaultToSpeaker */\n    ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11,   /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */\n    ma_ios_session_category_option_allow_bluetooth_a2dp                       = 0x20,   /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */\n    ma_ios_session_category_option_allow_air_play                             = 0x40,   /* AVAudioSessionCategoryOptionAllowAirPlay */\n} ma_ios_session_category_option;\n\n/* OpenSL stream types. */\ntypedef enum\n{\n    ma_opensl_stream_type_default = 0,              /* Leaves the stream type unset. */\n    ma_opensl_stream_type_voice,                    /* SL_ANDROID_STREAM_VOICE */\n    ma_opensl_stream_type_system,                   /* SL_ANDROID_STREAM_SYSTEM */\n    ma_opensl_stream_type_ring,                     /* SL_ANDROID_STREAM_RING */\n    ma_opensl_stream_type_media,                    /* SL_ANDROID_STREAM_MEDIA */\n    ma_opensl_stream_type_alarm,                    /* SL_ANDROID_STREAM_ALARM */\n    ma_opensl_stream_type_notification              /* SL_ANDROID_STREAM_NOTIFICATION */\n} ma_opensl_stream_type;\n\n/* OpenSL recording presets. */\ntypedef enum\n{\n    ma_opensl_recording_preset_default = 0,         /* Leaves the input preset unset. */\n    ma_opensl_recording_preset_generic,             /* SL_ANDROID_RECORDING_PRESET_GENERIC */\n    ma_opensl_recording_preset_camcorder,           /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */\n    ma_opensl_recording_preset_voice_recognition,   /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */\n    ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */\n    ma_opensl_recording_preset_voice_unprocessed    /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */\n} ma_opensl_recording_preset;\n\n/* WASAPI audio thread priority characteristics. */\ntypedef enum\n{\n    ma_wasapi_usage_default = 0,\n    ma_wasapi_usage_games,\n    ma_wasapi_usage_pro_audio,\n} ma_wasapi_usage;\n\n/* AAudio usage types. */\ntypedef enum\n{\n    ma_aaudio_usage_default = 0,                    /* Leaves the usage type unset. */\n    ma_aaudio_usage_media,                          /* AAUDIO_USAGE_MEDIA */\n    ma_aaudio_usage_voice_communication,            /* AAUDIO_USAGE_VOICE_COMMUNICATION */\n    ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */\n    ma_aaudio_usage_alarm,                          /* AAUDIO_USAGE_ALARM */\n    ma_aaudio_usage_notification,                   /* AAUDIO_USAGE_NOTIFICATION */\n    ma_aaudio_usage_notification_ringtone,          /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */\n    ma_aaudio_usage_notification_event,             /* AAUDIO_USAGE_NOTIFICATION_EVENT */\n    ma_aaudio_usage_assistance_accessibility,       /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */\n    ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */\n    ma_aaudio_usage_assistance_sonification,        /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */\n    ma_aaudio_usage_game,                           /* AAUDIO_USAGE_GAME */\n    ma_aaudio_usage_assitant,                       /* AAUDIO_USAGE_ASSISTANT */\n    ma_aaudio_usage_emergency,                      /* AAUDIO_SYSTEM_USAGE_EMERGENCY */\n    ma_aaudio_usage_safety,                         /* AAUDIO_SYSTEM_USAGE_SAFETY */\n    ma_aaudio_usage_vehicle_status,                 /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */\n    ma_aaudio_usage_announcement                    /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */\n} ma_aaudio_usage;\n\n/* AAudio content types. */\ntypedef enum\n{\n    ma_aaudio_content_type_default = 0,             /* Leaves the content type unset. */\n    ma_aaudio_content_type_speech,                  /* AAUDIO_CONTENT_TYPE_SPEECH */\n    ma_aaudio_content_type_music,                   /* AAUDIO_CONTENT_TYPE_MUSIC */\n    ma_aaudio_content_type_movie,                   /* AAUDIO_CONTENT_TYPE_MOVIE */\n    ma_aaudio_content_type_sonification             /* AAUDIO_CONTENT_TYPE_SONIFICATION */\n} ma_aaudio_content_type;\n\n/* AAudio input presets. */\ntypedef enum\n{\n    ma_aaudio_input_preset_default = 0,             /* Leaves the input preset unset. */\n    ma_aaudio_input_preset_generic,                 /* AAUDIO_INPUT_PRESET_GENERIC */\n    ma_aaudio_input_preset_camcorder,               /* AAUDIO_INPUT_PRESET_CAMCORDER */\n    ma_aaudio_input_preset_voice_recognition,       /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */\n    ma_aaudio_input_preset_voice_communication,     /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */\n    ma_aaudio_input_preset_unprocessed,             /* AAUDIO_INPUT_PRESET_UNPROCESSED */\n    ma_aaudio_input_preset_voice_performance        /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */\n} ma_aaudio_input_preset;\n\ntypedef enum\n{\n    ma_aaudio_allow_capture_default = 0,            /* Leaves the allowed capture policy unset. */\n    ma_aaudio_allow_capture_by_all,                 /* AAUDIO_ALLOW_CAPTURE_BY_ALL */\n    ma_aaudio_allow_capture_by_system,              /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */\n    ma_aaudio_allow_capture_by_none                 /* AAUDIO_ALLOW_CAPTURE_BY_NONE */\n} ma_aaudio_allowed_capture_policy;\n\ntypedef union\n{\n    ma_int64 counter;\n    double counterD;\n} ma_timer;\n\ntypedef union\n{\n    ma_wchar_win32 wasapi[64];      /* WASAPI uses a wchar_t string for identification. */\n    ma_uint8 dsound[16];            /* DirectSound uses a GUID for identification. */\n    /*UINT_PTR*/ ma_uint32 winmm;   /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */\n    char alsa[256];                 /* ALSA uses a name string for identification. */\n    char pulse[256];                /* PulseAudio uses a name string for identification. */\n    int jack;                       /* JACK always uses default devices. */\n    char coreaudio[256];            /* Core Audio uses a string for identification. */\n    char sndio[256];                /* \"snd/0\", etc. */\n    char audio4[256];               /* \"/dev/audio\", etc. */\n    char oss[64];                   /* \"dev/dsp0\", etc. \"dev/dsp\" for the default device. */\n    ma_int32 aaudio;                /* AAudio uses a 32-bit integer for identification. */\n    ma_uint32 opensl;               /* OpenSL|ES uses a 32-bit unsigned integer for identification. */\n    char webaudio[32];              /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */\n    union\n    {\n        int i;\n        char s[256];\n        void* p;\n    } custom;                       /* The custom backend could be anything. Give them a few options. */\n    int nullbackend;                /* The null backend uses an integer for device IDs. */\n} ma_device_id;\n\n\ntypedef struct ma_context_config    ma_context_config;\ntypedef struct ma_device_config     ma_device_config;\ntypedef struct ma_backend_callbacks ma_backend_callbacks;\n\n#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1)    /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */\n\n#ifndef MA_MAX_DEVICE_NAME_LENGTH\n#define MA_MAX_DEVICE_NAME_LENGTH   255\n#endif\n\ntypedef struct\n{\n    /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */\n    ma_device_id id;\n    char name[MA_MAX_DEVICE_NAME_LENGTH + 1];   /* +1 for null terminator. */\n    ma_bool32 isDefault;\n\n    ma_uint32 nativeDataFormatCount;\n    struct\n    {\n        ma_format format;       /* Sample format. If set to ma_format_unknown, all sample formats are supported. */\n        ma_uint32 channels;     /* If set to 0, all channels are supported. */\n        ma_uint32 sampleRate;   /* If set to 0, all sample rates are supported. */\n        ma_uint32 flags;        /* A combination of MA_DATA_FORMAT_FLAG_* flags. */\n    } nativeDataFormats[/*ma_format_count * ma_standard_sample_rate_count * MA_MAX_CHANNELS*/ 64];  /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */\n} ma_device_info;\n\nstruct ma_device_config\n{\n    ma_device_type deviceType;\n    ma_uint32 sampleRate;\n    ma_uint32 periodSizeInFrames;\n    ma_uint32 periodSizeInMilliseconds;\n    ma_uint32 periods;\n    ma_performance_profile performanceProfile;\n    ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */\n    ma_bool8 noClip;                    /* When set to true, the contents of the output buffer passed into the data callback will not be clipped after returning. Only applies when the playback sample format is f32. */\n    ma_bool8 noDisableDenormals;        /* Do not disable denormals when firing the data callback. */\n    ma_bool8 noFixedSizedCallback;      /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */\n    ma_device_data_proc dataCallback;\n    ma_device_notification_proc notificationCallback;\n    ma_stop_proc stopCallback;\n    void* pUserData;\n    ma_resampler_config resampling;\n    struct\n    {\n        const ma_device_id* pDeviceID;\n        ma_format format;\n        ma_uint32 channels;\n        ma_channel* pChannelMap;\n        ma_channel_mix_mode channelMixMode;\n        ma_bool32 calculateLFEFromSpatialChannels;  /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */\n        ma_share_mode shareMode;\n    } playback;\n    struct\n    {\n        const ma_device_id* pDeviceID;\n        ma_format format;\n        ma_uint32 channels;\n        ma_channel* pChannelMap;\n        ma_channel_mix_mode channelMixMode;\n        ma_bool32 calculateLFEFromSpatialChannels;  /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */\n        ma_share_mode shareMode;\n    } capture;\n\n    struct\n    {\n        ma_wasapi_usage usage;              /* When configured, uses Avrt APIs to set the thread characteristics. */\n        ma_bool8 noAutoConvertSRC;          /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */\n        ma_bool8 noDefaultQualitySRC;       /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */\n        ma_bool8 noAutoStreamRouting;       /* Disables automatic stream routing. */\n        ma_bool8 noHardwareOffloading;      /* Disables WASAPI's hardware offloading feature. */\n        ma_uint32 loopbackProcessID;        /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */\n        ma_bool8 loopbackProcessExclude;    /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */\n    } wasapi;\n    struct\n    {\n        ma_bool32 noMMap;           /* Disables MMap mode. */\n        ma_bool32 noAutoFormat;     /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */\n        ma_bool32 noAutoChannels;   /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */\n        ma_bool32 noAutoResample;   /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */\n    } alsa;\n    struct\n    {\n        const char* pStreamNamePlayback;\n        const char* pStreamNameCapture;\n    } pulse;\n    struct\n    {\n        ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */\n    } coreaudio;\n    struct\n    {\n        ma_opensl_stream_type streamType;\n        ma_opensl_recording_preset recordingPreset;\n        ma_bool32 enableCompatibilityWorkarounds;\n    } opensl;\n    struct\n    {\n        ma_aaudio_usage usage;\n        ma_aaudio_content_type contentType;\n        ma_aaudio_input_preset inputPreset;\n        ma_aaudio_allowed_capture_policy allowedCapturePolicy;\n        ma_bool32 noAutoStartAfterReroute;\n        ma_bool32 enableCompatibilityWorkarounds;\n    } aaudio;\n};\n\n\n/*\nThe callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`.\n\n\nParameters\n----------\npContext (in)\n    A pointer to the context performing the enumeration.\n\ndeviceType (in)\n    The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`.\n\npInfo (in)\n    A pointer to a `ma_device_info` containing the ID and name of the enumerated device. Note that this will not include detailed information about the device,\n    only basic information (ID and name). The reason for this is that it would otherwise require opening the backend device to probe for the information which\n    is too inefficient.\n\npUserData (in)\n    The user data pointer passed into `ma_context_enumerate_devices()`.\n*/\ntypedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData);\n\n\n/*\nDescribes some basic details about a playback or capture device.\n*/\ntypedef struct\n{\n    const ma_device_id* pDeviceID;\n    ma_share_mode shareMode;\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_channel channelMap[MA_MAX_CHANNELS];\n    ma_uint32 periodSizeInFrames;\n    ma_uint32 periodSizeInMilliseconds;\n    ma_uint32 periodCount;\n} ma_device_descriptor;\n\n/*\nThese are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context\nto many devices. A device is created from a context.\n\nThe general flow goes like this:\n\n  1) A context is created with `onContextInit()`\n     1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required.\n     1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required.\n  2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was\n     selected from device enumeration via `onContextEnumerateDevices()`.\n  3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()`\n  4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call\n     to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by\n     miniaudio internally.\n\nInitialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the\ncallbacks defined in this structure.\n\nOnce the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which\nphysical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the\ngiven callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration\nneeds to stop and the `onContextEnumerateDevices()` function returns with a success code.\n\nDetailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID,\nand on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the\ncase when the device ID is NULL, in which case information about the default device needs to be retrieved.\n\nOnce the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created.\nThis is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a\ndevice, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input,\nthe data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to\nthe requested format. The conversion between the format requested by the application and the device's native format will be handled\ninternally by miniaudio.\n\nOn input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's\nsupported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for\nsample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to\n`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should\ninspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period\nsize in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the\nsample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor`\nobject should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set).\n\nStarting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses\nasynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented.\n\nThe handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit\neasier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and\n`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the\nbackend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback.\nThis allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback.\n\nIf the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback\nwhich will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.\n\nThe audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been\nencountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback.\n\nThe invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this\ncallback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated\nwhich will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback,\nlook at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to\nwake up the audio thread.\n\nIf the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the\n`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient.\n*/\nstruct ma_backend_callbacks\n{\n    ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks);\n    ma_result (* onContextUninit)(ma_context* pContext);\n    ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);\n    ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);\n    ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture);\n    ma_result (* onDeviceUninit)(ma_device* pDevice);\n    ma_result (* onDeviceStart)(ma_device* pDevice);\n    ma_result (* onDeviceStop)(ma_device* pDevice);\n    ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead);\n    ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten);\n    ma_result (* onDeviceDataLoop)(ma_device* pDevice);\n    ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice);\n    ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo);\n};\n\nstruct ma_context_config\n{\n    ma_log* pLog;\n    ma_thread_priority threadPriority;\n    size_t threadStackSize;\n    void* pUserData;\n    ma_allocation_callbacks allocationCallbacks;\n    struct\n    {\n        ma_bool32 useVerboseDeviceEnumeration;\n    } alsa;\n    struct\n    {\n        const char* pApplicationName;\n        const char* pServerName;\n        ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */\n    } pulse;\n    struct\n    {\n        ma_ios_session_category sessionCategory;\n        ma_uint32 sessionCategoryOptions;\n        ma_bool32 noAudioSessionActivate;   /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */\n        ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */\n    } coreaudio;\n    struct\n    {\n        const char* pClientName;\n        ma_bool32 tryStartServer;\n    } jack;\n    ma_backend_callbacks custom;\n};\n\n/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */\ntypedef struct\n{\n    int code;\n    ma_event* pEvent;   /* This will be signalled when the event is complete. */\n    union\n    {\n        struct\n        {\n            int _unused;\n        } quit;\n        struct\n        {\n            ma_device_type deviceType;\n            void* pAudioClient;\n            void** ppAudioClientService;\n            ma_result* pResult; /* The result from creating the audio client service. */\n        } createAudioClient;\n        struct\n        {\n            ma_device* pDevice;\n            ma_device_type deviceType;\n        } releaseAudioClient;\n    } data;\n} ma_context_command__wasapi;\n\nstruct ma_context\n{\n    ma_backend_callbacks callbacks;\n    ma_backend backend;                 /* DirectSound, ALSA, etc. */\n    ma_log* pLog;\n    ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */\n    ma_thread_priority threadPriority;\n    size_t threadStackSize;\n    void* pUserData;\n    ma_allocation_callbacks allocationCallbacks;\n    ma_mutex deviceEnumLock;            /* Used to make ma_context_get_devices() thread safe. */\n    ma_mutex deviceInfoLock;            /* Used to make ma_context_get_device_info() thread safe. */\n    ma_uint32 deviceInfoCapacity;       /* Total capacity of pDeviceInfos. */\n    ma_uint32 playbackDeviceInfoCount;\n    ma_uint32 captureDeviceInfoCount;\n    ma_device_info* pDeviceInfos;       /* Playback devices first, then capture. */\n\n    union\n    {\n#ifdef MA_SUPPORT_WASAPI\n        struct\n        {\n            ma_thread commandThread;\n            ma_mutex commandLock;\n            ma_semaphore commandSem;\n            ma_uint32 commandIndex;\n            ma_uint32 commandCount;\n            ma_context_command__wasapi commands[4];\n            ma_handle hAvrt;\n            ma_proc AvSetMmThreadCharacteristicsA;\n            ma_proc AvRevertMmThreadcharacteristics;\n            ma_handle hMMDevapi;\n            ma_proc ActivateAudioInterfaceAsync;\n        } wasapi;\n#endif\n#ifdef MA_SUPPORT_DSOUND\n        struct\n        {\n            ma_handle hDSoundDLL;\n            ma_proc DirectSoundCreate;\n            ma_proc DirectSoundEnumerateA;\n            ma_proc DirectSoundCaptureCreate;\n            ma_proc DirectSoundCaptureEnumerateA;\n        } dsound;\n#endif\n#ifdef MA_SUPPORT_WINMM\n        struct\n        {\n            ma_handle hWinMM;\n            ma_proc waveOutGetNumDevs;\n            ma_proc waveOutGetDevCapsA;\n            ma_proc waveOutOpen;\n            ma_proc waveOutClose;\n            ma_proc waveOutPrepareHeader;\n            ma_proc waveOutUnprepareHeader;\n            ma_proc waveOutWrite;\n            ma_proc waveOutReset;\n            ma_proc waveInGetNumDevs;\n            ma_proc waveInGetDevCapsA;\n            ma_proc waveInOpen;\n            ma_proc waveInClose;\n            ma_proc waveInPrepareHeader;\n            ma_proc waveInUnprepareHeader;\n            ma_proc waveInAddBuffer;\n            ma_proc waveInStart;\n            ma_proc waveInReset;\n        } winmm;\n#endif\n#ifdef MA_SUPPORT_ALSA\n        struct\n        {\n            ma_handle asoundSO;\n            ma_proc snd_pcm_open;\n            ma_proc snd_pcm_close;\n            ma_proc snd_pcm_hw_params_sizeof;\n            ma_proc snd_pcm_hw_params_any;\n            ma_proc snd_pcm_hw_params_set_format;\n            ma_proc snd_pcm_hw_params_set_format_first;\n            ma_proc snd_pcm_hw_params_get_format_mask;\n            ma_proc snd_pcm_hw_params_set_channels;\n            ma_proc snd_pcm_hw_params_set_channels_near;\n            ma_proc snd_pcm_hw_params_set_channels_minmax;\n            ma_proc snd_pcm_hw_params_set_rate_resample;\n            ma_proc snd_pcm_hw_params_set_rate;\n            ma_proc snd_pcm_hw_params_set_rate_near;\n            ma_proc snd_pcm_hw_params_set_buffer_size_near;\n            ma_proc snd_pcm_hw_params_set_periods_near;\n            ma_proc snd_pcm_hw_params_set_access;\n            ma_proc snd_pcm_hw_params_get_format;\n            ma_proc snd_pcm_hw_params_get_channels;\n            ma_proc snd_pcm_hw_params_get_channels_min;\n            ma_proc snd_pcm_hw_params_get_channels_max;\n            ma_proc snd_pcm_hw_params_get_rate;\n            ma_proc snd_pcm_hw_params_get_rate_min;\n            ma_proc snd_pcm_hw_params_get_rate_max;\n            ma_proc snd_pcm_hw_params_get_buffer_size;\n            ma_proc snd_pcm_hw_params_get_periods;\n            ma_proc snd_pcm_hw_params_get_access;\n            ma_proc snd_pcm_hw_params_test_format;\n            ma_proc snd_pcm_hw_params_test_channels;\n            ma_proc snd_pcm_hw_params_test_rate;\n            ma_proc snd_pcm_hw_params;\n            ma_proc snd_pcm_sw_params_sizeof;\n            ma_proc snd_pcm_sw_params_current;\n            ma_proc snd_pcm_sw_params_get_boundary;\n            ma_proc snd_pcm_sw_params_set_avail_min;\n            ma_proc snd_pcm_sw_params_set_start_threshold;\n            ma_proc snd_pcm_sw_params_set_stop_threshold;\n            ma_proc snd_pcm_sw_params;\n            ma_proc snd_pcm_format_mask_sizeof;\n            ma_proc snd_pcm_format_mask_test;\n            ma_proc snd_pcm_get_chmap;\n            ma_proc snd_pcm_state;\n            ma_proc snd_pcm_prepare;\n            ma_proc snd_pcm_start;\n            ma_proc snd_pcm_drop;\n            ma_proc snd_pcm_drain;\n            ma_proc snd_pcm_reset;\n            ma_proc snd_device_name_hint;\n            ma_proc snd_device_name_get_hint;\n            ma_proc snd_card_get_index;\n            ma_proc snd_device_name_free_hint;\n            ma_proc snd_pcm_mmap_begin;\n            ma_proc snd_pcm_mmap_commit;\n            ma_proc snd_pcm_recover;\n            ma_proc snd_pcm_readi;\n            ma_proc snd_pcm_writei;\n            ma_proc snd_pcm_avail;\n            ma_proc snd_pcm_avail_update;\n            ma_proc snd_pcm_wait;\n            ma_proc snd_pcm_nonblock;\n            ma_proc snd_pcm_info;\n            ma_proc snd_pcm_info_sizeof;\n            ma_proc snd_pcm_info_get_name;\n            ma_proc snd_pcm_poll_descriptors;\n            ma_proc snd_pcm_poll_descriptors_count;\n            ma_proc snd_pcm_poll_descriptors_revents;\n            ma_proc snd_config_update_free_global;\n\n            ma_mutex internalDeviceEnumLock;\n            ma_bool32 useVerboseDeviceEnumeration;\n        } alsa;\n#endif\n#ifdef MA_SUPPORT_PULSEAUDIO\n        struct\n        {\n            ma_handle pulseSO;\n            ma_proc pa_mainloop_new;\n            ma_proc pa_mainloop_free;\n            ma_proc pa_mainloop_quit;\n            ma_proc pa_mainloop_get_api;\n            ma_proc pa_mainloop_iterate;\n            ma_proc pa_mainloop_wakeup;\n            ma_proc pa_threaded_mainloop_new;\n            ma_proc pa_threaded_mainloop_free;\n            ma_proc pa_threaded_mainloop_start;\n            ma_proc pa_threaded_mainloop_stop;\n            ma_proc pa_threaded_mainloop_lock;\n            ma_proc pa_threaded_mainloop_unlock;\n            ma_proc pa_threaded_mainloop_wait;\n            ma_proc pa_threaded_mainloop_signal;\n            ma_proc pa_threaded_mainloop_accept;\n            ma_proc pa_threaded_mainloop_get_retval;\n            ma_proc pa_threaded_mainloop_get_api;\n            ma_proc pa_threaded_mainloop_in_thread;\n            ma_proc pa_threaded_mainloop_set_name;\n            ma_proc pa_context_new;\n            ma_proc pa_context_unref;\n            ma_proc pa_context_connect;\n            ma_proc pa_context_disconnect;\n            ma_proc pa_context_set_state_callback;\n            ma_proc pa_context_get_state;\n            ma_proc pa_context_get_sink_info_list;\n            ma_proc pa_context_get_source_info_list;\n            ma_proc pa_context_get_sink_info_by_name;\n            ma_proc pa_context_get_source_info_by_name;\n            ma_proc pa_operation_unref;\n            ma_proc pa_operation_get_state;\n            ma_proc pa_channel_map_init_extend;\n            ma_proc pa_channel_map_valid;\n            ma_proc pa_channel_map_compatible;\n            ma_proc pa_stream_new;\n            ma_proc pa_stream_unref;\n            ma_proc pa_stream_connect_playback;\n            ma_proc pa_stream_connect_record;\n            ma_proc pa_stream_disconnect;\n            ma_proc pa_stream_get_state;\n            ma_proc pa_stream_get_sample_spec;\n            ma_proc pa_stream_get_channel_map;\n            ma_proc pa_stream_get_buffer_attr;\n            ma_proc pa_stream_set_buffer_attr;\n            ma_proc pa_stream_get_device_name;\n            ma_proc pa_stream_set_write_callback;\n            ma_proc pa_stream_set_read_callback;\n            ma_proc pa_stream_set_suspended_callback;\n            ma_proc pa_stream_set_moved_callback;\n            ma_proc pa_stream_is_suspended;\n            ma_proc pa_stream_flush;\n            ma_proc pa_stream_drain;\n            ma_proc pa_stream_is_corked;\n            ma_proc pa_stream_cork;\n            ma_proc pa_stream_trigger;\n            ma_proc pa_stream_begin_write;\n            ma_proc pa_stream_write;\n            ma_proc pa_stream_peek;\n            ma_proc pa_stream_drop;\n            ma_proc pa_stream_writable_size;\n            ma_proc pa_stream_readable_size;\n\n            /*pa_mainloop**/ ma_ptr pMainLoop;\n            /*pa_context**/ ma_ptr pPulseContext;\n            char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */\n            char* pServerName;      /* Set when the context is initialized. Used by devices for their local pa_context objects. */\n        } pulse;\n#endif\n#ifdef MA_SUPPORT_JACK\n        struct\n        {\n            ma_handle jackSO;\n            ma_proc jack_client_open;\n            ma_proc jack_client_close;\n            ma_proc jack_client_name_size;\n            ma_proc jack_set_process_callback;\n            ma_proc jack_set_buffer_size_callback;\n            ma_proc jack_on_shutdown;\n            ma_proc jack_get_sample_rate;\n            ma_proc jack_get_buffer_size;\n            ma_proc jack_get_ports;\n            ma_proc jack_activate;\n            ma_proc jack_deactivate;\n            ma_proc jack_connect;\n            ma_proc jack_port_register;\n            ma_proc jack_port_name;\n            ma_proc jack_port_get_buffer;\n            ma_proc jack_free;\n\n            char* pClientName;\n            ma_bool32 tryStartServer;\n        } jack;\n#endif\n#ifdef MA_SUPPORT_COREAUDIO\n        struct\n        {\n            ma_handle hCoreFoundation;\n            ma_proc CFStringGetCString;\n            ma_proc CFRelease;\n\n            ma_handle hCoreAudio;\n            ma_proc AudioObjectGetPropertyData;\n            ma_proc AudioObjectGetPropertyDataSize;\n            ma_proc AudioObjectSetPropertyData;\n            ma_proc AudioObjectAddPropertyListener;\n            ma_proc AudioObjectRemovePropertyListener;\n\n            ma_handle hAudioUnit;  /* Could possibly be set to AudioToolbox on later versions of macOS. */\n            ma_proc AudioComponentFindNext;\n            ma_proc AudioComponentInstanceDispose;\n            ma_proc AudioComponentInstanceNew;\n            ma_proc AudioOutputUnitStart;\n            ma_proc AudioOutputUnitStop;\n            ma_proc AudioUnitAddPropertyListener;\n            ma_proc AudioUnitGetPropertyInfo;\n            ma_proc AudioUnitGetProperty;\n            ma_proc AudioUnitSetProperty;\n            ma_proc AudioUnitInitialize;\n            ma_proc AudioUnitRender;\n\n            /*AudioComponent*/ ma_ptr component;\n            ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */\n        } coreaudio;\n#endif\n#ifdef MA_SUPPORT_SNDIO\n        struct\n        {\n            ma_handle sndioSO;\n            ma_proc sio_open;\n            ma_proc sio_close;\n            ma_proc sio_setpar;\n            ma_proc sio_getpar;\n            ma_proc sio_getcap;\n            ma_proc sio_start;\n            ma_proc sio_stop;\n            ma_proc sio_read;\n            ma_proc sio_write;\n            ma_proc sio_onmove;\n            ma_proc sio_nfds;\n            ma_proc sio_pollfd;\n            ma_proc sio_revents;\n            ma_proc sio_eof;\n            ma_proc sio_setvol;\n            ma_proc sio_onvol;\n            ma_proc sio_initpar;\n        } sndio;\n#endif\n#ifdef MA_SUPPORT_AUDIO4\n        struct\n        {\n            int _unused;\n        } audio4;\n#endif\n#ifdef MA_SUPPORT_OSS\n        struct\n        {\n            int versionMajor;\n            int versionMinor;\n        } oss;\n#endif\n#ifdef MA_SUPPORT_AAUDIO\n        struct\n        {\n            ma_handle hAAudio; /* libaaudio.so */\n            ma_proc AAudio_createStreamBuilder;\n            ma_proc AAudioStreamBuilder_delete;\n            ma_proc AAudioStreamBuilder_setDeviceId;\n            ma_proc AAudioStreamBuilder_setDirection;\n            ma_proc AAudioStreamBuilder_setSharingMode;\n            ma_proc AAudioStreamBuilder_setFormat;\n            ma_proc AAudioStreamBuilder_setChannelCount;\n            ma_proc AAudioStreamBuilder_setSampleRate;\n            ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;\n            ma_proc AAudioStreamBuilder_setFramesPerDataCallback;\n            ma_proc AAudioStreamBuilder_setDataCallback;\n            ma_proc AAudioStreamBuilder_setErrorCallback;\n            ma_proc AAudioStreamBuilder_setPerformanceMode;\n            ma_proc AAudioStreamBuilder_setUsage;\n            ma_proc AAudioStreamBuilder_setContentType;\n            ma_proc AAudioStreamBuilder_setInputPreset;\n            ma_proc AAudioStreamBuilder_setAllowedCapturePolicy;\n            ma_proc AAudioStreamBuilder_openStream;\n            ma_proc AAudioStream_close;\n            ma_proc AAudioStream_getState;\n            ma_proc AAudioStream_waitForStateChange;\n            ma_proc AAudioStream_getFormat;\n            ma_proc AAudioStream_getChannelCount;\n            ma_proc AAudioStream_getSampleRate;\n            ma_proc AAudioStream_getBufferCapacityInFrames;\n            ma_proc AAudioStream_getFramesPerDataCallback;\n            ma_proc AAudioStream_getFramesPerBurst;\n            ma_proc AAudioStream_requestStart;\n            ma_proc AAudioStream_requestStop;\n            ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */\n        } aaudio;\n#endif\n#ifdef MA_SUPPORT_OPENSL\n        struct\n        {\n            ma_handle libOpenSLES;\n            ma_handle SL_IID_ENGINE;\n            ma_handle SL_IID_AUDIOIODEVICECAPABILITIES;\n            ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE;\n            ma_handle SL_IID_RECORD;\n            ma_handle SL_IID_PLAY;\n            ma_handle SL_IID_OUTPUTMIX;\n            ma_handle SL_IID_ANDROIDCONFIGURATION;\n            ma_proc   slCreateEngine;\n        } opensl;\n#endif\n#ifdef MA_SUPPORT_WEBAUDIO\n        struct\n        {\n            int _unused;\n        } webaudio;\n#endif\n#ifdef MA_SUPPORT_NULL\n        struct\n        {\n            int _unused;\n        } null_backend;\n#endif\n    };\n\n    union\n    {\n#if defined(MA_WIN32)\n        struct\n        {\n            /*HMODULE*/ ma_handle hOle32DLL;\n            ma_proc CoInitialize;\n            ma_proc CoInitializeEx;\n            ma_proc CoUninitialize;\n            ma_proc CoCreateInstance;\n            ma_proc CoTaskMemFree;\n            ma_proc PropVariantClear;\n            ma_proc StringFromGUID2;\n\n            /*HMODULE*/ ma_handle hUser32DLL;\n            ma_proc GetForegroundWindow;\n            ma_proc GetDesktopWindow;\n\n            /*HMODULE*/ ma_handle hAdvapi32DLL;\n            ma_proc RegOpenKeyExA;\n            ma_proc RegCloseKey;\n            ma_proc RegQueryValueExA;\n\n            /*HRESULT*/ long CoInitializeResult;\n        } win32;\n#endif\n#ifdef MA_POSIX\n        struct\n        {\n            int _unused;\n        } posix;\n#endif\n        int _unused;\n    };\n};\n\nstruct ma_device\n{\n    ma_context* pContext;\n    ma_device_type type;\n    ma_uint32 sampleRate;\n    ma_atomic_device_state state;               /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */\n    ma_device_data_proc onData;                 /* Set once at initialization time and should not be changed after. */\n    ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */\n    ma_stop_proc onStop;                        /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */\n    void* pUserData;                            /* Application defined data. */\n    ma_mutex startStopLock;\n    ma_event wakeupEvent;\n    ma_event startEvent;\n    ma_event stopEvent;\n    ma_thread thread;\n    ma_result workResult;                       /* This is set by the worker thread after it's finished doing a job. */\n    ma_bool8 isOwnerOfContext;                  /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */\n    ma_bool8 noPreSilencedOutputBuffer;\n    ma_bool8 noClip;\n    ma_bool8 noDisableDenormals;\n    ma_bool8 noFixedSizedCallback;\n    ma_atomic_float masterVolumeFactor;         /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */\n    ma_duplex_rb duplexRB;                      /* Intermediary buffer for duplex device on asynchronous backends. */\n    struct\n    {\n        ma_resample_algorithm algorithm;\n        ma_resampling_backend_vtable* pBackendVTable;\n        void* pBackendUserData;\n        struct\n        {\n            ma_uint32 lpfOrder;\n        } linear;\n    } resampling;\n    struct\n    {\n        ma_device_id* pID;                  /* Set to NULL if using default ID, otherwise set to the address of \"id\". */\n        ma_device_id id;                    /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */\n        char name[MA_MAX_DEVICE_NAME_LENGTH + 1];                     /* Maybe temporary. Likely to be replaced with a query API. */\n        ma_share_mode shareMode;            /* Set to whatever was passed in when the device was initialized. */\n        ma_format format;\n        ma_uint32 channels;\n        ma_channel channelMap[MA_MAX_CHANNELS];\n        ma_format internalFormat;\n        ma_uint32 internalChannels;\n        ma_uint32 internalSampleRate;\n        ma_channel internalChannelMap[MA_MAX_CHANNELS];\n        ma_uint32 internalPeriodSizeInFrames;\n        ma_uint32 internalPeriods;\n        ma_channel_mix_mode channelMixMode;\n        ma_bool32 calculateLFEFromSpatialChannels;\n        ma_data_converter converter;\n        void* pIntermediaryBuffer;          /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */\n        ma_uint32 intermediaryBufferCap;\n        ma_uint32 intermediaryBufferLen;    /* How many valid frames are sitting in the intermediary buffer. */\n        void* pInputCache;                  /* In external format. Can be null. */\n        ma_uint64 inputCacheCap;\n        ma_uint64 inputCacheConsumed;\n        ma_uint64 inputCacheRemaining;\n    } playback;\n    struct\n    {\n        ma_device_id* pID;                  /* Set to NULL if using default ID, otherwise set to the address of \"id\". */\n        ma_device_id id;                    /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */\n        char name[MA_MAX_DEVICE_NAME_LENGTH + 1];                     /* Maybe temporary. Likely to be replaced with a query API. */\n        ma_share_mode shareMode;            /* Set to whatever was passed in when the device was initialized. */\n        ma_format format;\n        ma_uint32 channels;\n        ma_channel channelMap[MA_MAX_CHANNELS];\n        ma_format internalFormat;\n        ma_uint32 internalChannels;\n        ma_uint32 internalSampleRate;\n        ma_channel internalChannelMap[MA_MAX_CHANNELS];\n        ma_uint32 internalPeriodSizeInFrames;\n        ma_uint32 internalPeriods;\n        ma_channel_mix_mode channelMixMode;\n        ma_bool32 calculateLFEFromSpatialChannels;\n        ma_data_converter converter;\n        void* pIntermediaryBuffer;          /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */\n        ma_uint32 intermediaryBufferCap;\n        ma_uint32 intermediaryBufferLen;    /* How many valid frames are sitting in the intermediary buffer. */\n    } capture;\n\n    union\n    {\n#ifdef MA_SUPPORT_WASAPI\n        struct\n        {\n            /*IAudioClient**/ ma_ptr pAudioClientPlayback;\n            /*IAudioClient**/ ma_ptr pAudioClientCapture;\n            /*IAudioRenderClient**/ ma_ptr pRenderClient;\n            /*IAudioCaptureClient**/ ma_ptr pCaptureClient;\n            /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator;      /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */\n            ma_IMMNotificationClient notificationClient;\n            /*HANDLE*/ ma_handle hEventPlayback;                    /* Auto reset. Initialized to signaled. */\n            /*HANDLE*/ ma_handle hEventCapture;                     /* Auto reset. Initialized to unsignaled. */\n            ma_uint32 actualBufferSizeInFramesPlayback;             /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */\n            ma_uint32 actualBufferSizeInFramesCapture;\n            ma_uint32 originalPeriodSizeInFrames;\n            ma_uint32 originalPeriodSizeInMilliseconds;\n            ma_uint32 originalPeriods;\n            ma_performance_profile originalPerformanceProfile;\n            ma_uint32 periodSizeInFramesPlayback;\n            ma_uint32 periodSizeInFramesCapture;\n            void* pMappedBufferCapture;\n            ma_uint32 mappedBufferCaptureCap;\n            ma_uint32 mappedBufferCaptureLen;\n            void* pMappedBufferPlayback;\n            ma_uint32 mappedBufferPlaybackCap;\n            ma_uint32 mappedBufferPlaybackLen;\n            ma_atomic_bool32 isStartedCapture;                      /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */\n            ma_atomic_bool32 isStartedPlayback;                     /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */\n            ma_uint32 loopbackProcessID;\n            ma_bool8 loopbackProcessExclude;\n            ma_bool8 noAutoConvertSRC;                              /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */\n            ma_bool8 noDefaultQualitySRC;                           /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */\n            ma_bool8 noHardwareOffloading;\n            ma_bool8 allowCaptureAutoStreamRouting;\n            ma_bool8 allowPlaybackAutoStreamRouting;\n            ma_bool8 isDetachedPlayback;\n            ma_bool8 isDetachedCapture;\n            ma_wasapi_usage usage;\n            void* hAvrtHandle;\n            ma_mutex rerouteLock;\n        } wasapi;\n#endif\n#ifdef MA_SUPPORT_DSOUND\n        struct\n        {\n            /*LPDIRECTSOUND*/ ma_ptr pPlayback;\n            /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;\n            /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;\n            /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;\n            /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;\n        } dsound;\n#endif\n#ifdef MA_SUPPORT_WINMM\n        struct\n        {\n            /*HWAVEOUT*/ ma_handle hDevicePlayback;\n            /*HWAVEIN*/ ma_handle hDeviceCapture;\n            /*HANDLE*/ ma_handle hEventPlayback;\n            /*HANDLE*/ ma_handle hEventCapture;\n            ma_uint32 fragmentSizeInFrames;\n            ma_uint32 iNextHeaderPlayback;             /* [0,periods). Used as an index into pWAVEHDRPlayback. */\n            ma_uint32 iNextHeaderCapture;              /* [0,periods). Used as an index into pWAVEHDRCapture. */\n            ma_uint32 headerFramesConsumedPlayback;    /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */\n            ma_uint32 headerFramesConsumedCapture;     /* ^^^ */\n            /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback;   /* One instantiation for each period. */\n            /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture;    /* One instantiation for each period. */\n            ma_uint8* pIntermediaryBufferPlayback;\n            ma_uint8* pIntermediaryBufferCapture;\n            ma_uint8* _pHeapData;                      /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */\n        } winmm;\n#endif\n#ifdef MA_SUPPORT_ALSA\n        struct\n        {\n            /*snd_pcm_t**/ ma_ptr pPCMPlayback;\n            /*snd_pcm_t**/ ma_ptr pPCMCapture;\n            /*struct pollfd**/ void* pPollDescriptorsPlayback;\n            /*struct pollfd**/ void* pPollDescriptorsCapture;\n            int pollDescriptorCountPlayback;\n            int pollDescriptorCountCapture;\n            int wakeupfdPlayback;   /* eventfd for waking up from poll() when the playback device is stopped. */\n            int wakeupfdCapture;    /* eventfd for waking up from poll() when the capture device is stopped. */\n            ma_bool8 isUsingMMapPlayback;\n            ma_bool8 isUsingMMapCapture;\n        } alsa;\n#endif\n#ifdef MA_SUPPORT_PULSEAUDIO\n        struct\n        {\n            /*pa_mainloop**/ ma_ptr pMainLoop;\n            /*pa_context**/ ma_ptr pPulseContext;\n            /*pa_stream**/ ma_ptr pStreamPlayback;\n            /*pa_stream**/ ma_ptr pStreamCapture;\n        } pulse;\n#endif\n#ifdef MA_SUPPORT_JACK\n        struct\n        {\n            /*jack_client_t**/ ma_ptr pClient;\n            /*jack_port_t**/ ma_ptr* ppPortsPlayback;\n            /*jack_port_t**/ ma_ptr* ppPortsCapture;\n            float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */\n            float* pIntermediaryBufferCapture;\n        } jack;\n#endif\n#ifdef MA_SUPPORT_COREAUDIO\n        struct\n        {\n            ma_uint32 deviceObjectIDPlayback;\n            ma_uint32 deviceObjectIDCapture;\n            /*AudioUnit*/ ma_ptr audioUnitPlayback;\n            /*AudioUnit*/ ma_ptr audioUnitCapture;\n            /*AudioBufferList**/ ma_ptr pAudioBufferList;   /* Only used for input devices. */\n            ma_uint32 audioBufferCapInFrames;               /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */\n            ma_event stopEvent;\n            ma_uint32 originalPeriodSizeInFrames;\n            ma_uint32 originalPeriodSizeInMilliseconds;\n            ma_uint32 originalPeriods;\n            ma_performance_profile originalPerformanceProfile;\n            ma_bool32 isDefaultPlaybackDevice;\n            ma_bool32 isDefaultCaptureDevice;\n            ma_bool32 isSwitchingPlaybackDevice;   /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */\n            ma_bool32 isSwitchingCaptureDevice;    /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */\n            void* pNotificationHandler;             /* Only used on mobile platforms. Obj-C object for handling route changes. */\n        } coreaudio;\n#endif\n#ifdef MA_SUPPORT_SNDIO\n        struct\n        {\n            ma_ptr handlePlayback;\n            ma_ptr handleCapture;\n            ma_bool32 isStartedPlayback;\n            ma_bool32 isStartedCapture;\n        } sndio;\n#endif\n#ifdef MA_SUPPORT_AUDIO4\n        struct\n        {\n            int fdPlayback;\n            int fdCapture;\n        } audio4;\n#endif\n#ifdef MA_SUPPORT_OSS\n        struct\n        {\n            int fdPlayback;\n            int fdCapture;\n        } oss;\n#endif\n#ifdef MA_SUPPORT_AAUDIO\n        struct\n        {\n            /*AAudioStream**/ ma_ptr pStreamPlayback;\n            /*AAudioStream**/ ma_ptr pStreamCapture;\n            ma_aaudio_usage usage;\n            ma_aaudio_content_type contentType;\n            ma_aaudio_input_preset inputPreset;\n            ma_aaudio_allowed_capture_policy allowedCapturePolicy;\n            ma_bool32 noAutoStartAfterReroute;\n        } aaudio;\n#endif\n#ifdef MA_SUPPORT_OPENSL\n        struct\n        {\n            /*SLObjectItf*/ ma_ptr pOutputMixObj;\n            /*SLOutputMixItf*/ ma_ptr pOutputMix;\n            /*SLObjectItf*/ ma_ptr pAudioPlayerObj;\n            /*SLPlayItf*/ ma_ptr pAudioPlayer;\n            /*SLObjectItf*/ ma_ptr pAudioRecorderObj;\n            /*SLRecordItf*/ ma_ptr pAudioRecorder;\n            /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback;\n            /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture;\n            ma_bool32 isDrainingCapture;\n            ma_bool32 isDrainingPlayback;\n            ma_uint32 currentBufferIndexPlayback;\n            ma_uint32 currentBufferIndexCapture;\n            ma_uint8* pBufferPlayback;      /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */\n            ma_uint8* pBufferCapture;\n        } opensl;\n#endif\n#ifdef MA_SUPPORT_WEBAUDIO\n        struct\n        {\n            /* AudioWorklets path. */\n            /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext;\n            /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet;\n            float* pIntermediaryBuffer;\n            void* pStackBuffer;\n            ma_result initResult;   /* Set to MA_BUSY while initialization is in progress. */\n            int deviceIndex;        /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */\n        } webaudio;\n#endif\n#ifdef MA_SUPPORT_NULL\n        struct\n        {\n            ma_thread deviceThread;\n            ma_event operationEvent;\n            ma_event operationCompletionEvent;\n            ma_semaphore operationSemaphore;\n            ma_uint32 operation;\n            ma_result operationResult;\n            ma_timer timer;\n            double priorRunTime;\n            ma_uint32 currentPeriodFramesRemainingPlayback;\n            ma_uint32 currentPeriodFramesRemainingCapture;\n            ma_uint64 lastProcessedFramePlayback;\n            ma_uint64 lastProcessedFrameCapture;\n            ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */\n        } null_device;\n#endif\n    };\n};\n#if defined(_MSC_VER) && !defined(__clang__)\n    #pragma warning(pop)\n#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))\n    #pragma GCC diagnostic pop  /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */\n#endif\n\n/*\nInitializes a `ma_context_config` object.\n\n\nReturn Value\n------------\nA `ma_context_config` initialized to defaults.\n\n\nRemarks\n-------\nYou must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio\nis updated and new members are added to `ma_context_config`. It also sets logical defaults.\n\nYou can override members of the returned object by changing it's members directly.\n\n\nSee Also\n--------\nma_context_init()\n*/\nMA_API ma_context_config ma_context_config_init(void);\n\n/*\nInitializes a context.\n\nThe context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual\ndevice. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices.\n\n\nParameters\n----------\nbackends (in, optional)\n    A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.\n\nbackendCount (in, optional)\n    The number of items in `backend`. Ignored if `backend` is NULL.\n\npConfig (in, optional)\n    The context configuration.\n\npContext (in)\n    A pointer to the context object being initialized.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error code otherwise.\n\n\nThread Safety\n-------------\nUnsafe. Do not call this function across multiple threads as some backends read and write to global state.\n\n\nRemarks\n-------\nWhen `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order:\n\n    |-------------|-----------------------|--------------------------------------------------------|\n    | Name        | Enum Name             | Supported Operating Systems                            |\n    |-------------|-----------------------|--------------------------------------------------------|\n    | WASAPI      | ma_backend_wasapi     | Windows Vista+                                         |\n    | DirectSound | ma_backend_dsound     | Windows XP+                                            |\n    | WinMM       | ma_backend_winmm      | Windows XP+ (may work on older versions, but untested) |\n    | Core Audio  | ma_backend_coreaudio  | macOS, iOS                                             |\n    | ALSA        | ma_backend_alsa       | Linux                                                  |\n    | PulseAudio  | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android)  |\n    | JACK        | ma_backend_jack       | Cross Platform (disabled on BSD and Android)           |\n    | sndio       | ma_backend_sndio      | OpenBSD                                                |\n    | audio(4)    | ma_backend_audio4     | NetBSD, OpenBSD                                        |\n    | OSS         | ma_backend_oss        | FreeBSD                                                |\n    | AAudio      | ma_backend_aaudio     | Android 8+                                             |\n    | OpenSL|ES   | ma_backend_opensl     | Android (API level 16+)                                |\n    | Web Audio   | ma_backend_webaudio   | Web (via Emscripten)                                   |\n    | Null        | ma_backend_null       | Cross Platform (not used on Web)                       |\n    |-------------|-----------------------|--------------------------------------------------------|\n\nThe context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings\ncan then be set directly on the structure. Below are the members of the `ma_context_config` object.\n\n    pLog\n        A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not\n        require logging. See the `ma_log` API for details on how to use the logging system.\n\n    threadPriority\n        The desired priority to use for the audio thread. Allowable values include the following:\n\n        |--------------------------------------|\n        | Thread Priority                      |\n        |--------------------------------------|\n        | ma_thread_priority_idle              |\n        | ma_thread_priority_lowest            |\n        | ma_thread_priority_low               |\n        | ma_thread_priority_normal            |\n        | ma_thread_priority_high              |\n        | ma_thread_priority_highest (default) |\n        | ma_thread_priority_realtime          |\n        | ma_thread_priority_default           |\n        |--------------------------------------|\n\n    threadStackSize\n        The desired size of the stack for the audio thread. Defaults to the operating system's default.\n\n    pUserData\n        A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`.\n\n    allocationCallbacks\n        Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation\n        callbacks will be used for anything tied to the context, including devices.\n\n    alsa.useVerboseDeviceEnumeration\n        ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique\n        card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes\n        it so the ALSA backend includes all devices. Defaults to false.\n\n    pulse.pApplicationName\n        PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`.\n\n    pulse.pServerName\n        PulseAudio only. The name of the server to connect to with `pa_context_connect()`.\n\n    pulse.tryAutoSpawn\n        PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that\n        miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be\n        intrusive for the end user.\n\n    coreaudio.sessionCategory\n        iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.\n\n        |-----------------------------------------|-------------------------------------|\n        | miniaudio Token                         | Core Audio Token                    |\n        |-----------------------------------------|-------------------------------------|\n        | ma_ios_session_category_ambient         | AVAudioSessionCategoryAmbient       |\n        | ma_ios_session_category_solo_ambient    | AVAudioSessionCategorySoloAmbient   |\n        | ma_ios_session_category_playback        | AVAudioSessionCategoryPlayback      |\n        | ma_ios_session_category_record          | AVAudioSessionCategoryRecord        |\n        | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord |\n        | ma_ios_session_category_multi_route     | AVAudioSessionCategoryMultiRoute    |\n        | ma_ios_session_category_none            | AVAudioSessionCategoryAmbient       |\n        | ma_ios_session_category_default         | AVAudioSessionCategoryAmbient       |\n        |-----------------------------------------|-------------------------------------|\n\n    coreaudio.sessionCategoryOptions\n        iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.\n\n        |---------------------------------------------------------------------------|------------------------------------------------------------------|\n        | miniaudio Token                                                           | Core Audio Token                                                 |\n        |---------------------------------------------------------------------------|------------------------------------------------------------------|\n        | ma_ios_session_category_option_mix_with_others                            | AVAudioSessionCategoryOptionMixWithOthers                        |\n        | ma_ios_session_category_option_duck_others                                | AVAudioSessionCategoryOptionDuckOthers                           |\n        | ma_ios_session_category_option_allow_bluetooth                            | AVAudioSessionCategoryOptionAllowBluetooth                       |\n        | ma_ios_session_category_option_default_to_speaker                         | AVAudioSessionCategoryOptionDefaultToSpeaker                     |\n        | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers |\n        | ma_ios_session_category_option_allow_bluetooth_a2dp                       | AVAudioSessionCategoryOptionAllowBluetoothA2DP                   |\n        | ma_ios_session_category_option_allow_air_play                             | AVAudioSessionCategoryOptionAllowAirPlay                         |\n        |---------------------------------------------------------------------------|------------------------------------------------------------------|\n\n    coreaudio.noAudioSessionActivate\n        iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization.\n\n    coreaudio.noAudioSessionDeactivate\n        iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization.\n\n    jack.pClientName\n        The name of the client to pass to `jack_client_open()`.\n\n    jack.tryStartServer\n        Whether or not to try auto-starting the JACK server. Defaults to false.\n\n\nIt is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the\nrelevant backends every time it's initialized.\n\nThe location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The\nreason for this is that a pointer to the context is stored in the `ma_device` structure.\n\n\nExample 1 - Default Initialization\n----------------------------------\nThe example below shows how to initialize the context using the default configuration.\n\n```c\nma_context context;\nma_result result = ma_context_init(NULL, 0, NULL, &context);\nif (result != MA_SUCCESS) {\n    // Error.\n}\n```\n\n\nExample 2 - Custom Configuration\n--------------------------------\nThe example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program\nwants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also\nwant an error to be returned if no valid backend is available which they achieve by excluding the Null backend.\n\nFor the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface.\n\n```c\nma_backend backends[] = {\n    ma_backend_alsa,\n    ma_backend_pulseaudio,\n    ma_backend_wasapi,\n    ma_backend_dsound\n};\n\nma_log log;\nma_log_init(&log);\nma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData));\n\nma_context_config config = ma_context_config_init();\nconfig.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured.\n\nma_context context;\nma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context);\nif (result != MA_SUCCESS) {\n    // Error.\n    if (result == MA_NO_BACKEND) {\n        // Couldn't find an appropriate backend.\n    }\n}\n\n// You could also attach a log callback post-initialization:\nma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData));\n```\n\n\nSee Also\n--------\nma_context_config_init()\nma_context_uninit()\n*/\nMA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);\n\n/*\nUninitializes a context.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error code otherwise.\n\n\nThread Safety\n-------------\nUnsafe. Do not call this function across multiple threads as some backends read and write to global state.\n\n\nRemarks\n-------\nResults are undefined if you call this while any device created by this context is still active.\n\n\nSee Also\n--------\nma_context_init()\n*/\nMA_API ma_result ma_context_uninit(ma_context* pContext);\n\n/*\nRetrieves the size of the ma_context object.\n\nThis is mainly for the purpose of bindings to know how much memory to allocate.\n*/\nMA_API size_t ma_context_sizeof(void);\n\n/*\nRetrieves a pointer to the log object associated with this context.\n\n\nRemarks\n-------\nPass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log\nmessage.\n\nYou can attach your own logging callback to the log with `ma_log_register_callback()`\n\n\nReturn Value\n------------\nA pointer to the `ma_log` object that the context uses to post log messages. If some error occurs,\nNULL will be returned.\n*/\nMA_API ma_log* ma_context_get_log(ma_context* pContext);\n\n/*\nEnumerates over every device (both playback and capture).\n\nThis is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur\nan internal heap allocation, or it simply suits your code better.\n\nNote that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require\nopening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this,\nbut don't call it from within the enumeration callback.\n\nReturning false from the callback will stop enumeration. Returning true will continue enumeration.\n\n\nParameters\n----------\npContext (in)\n    A pointer to the context performing the enumeration.\n\ncallback (in)\n    The callback to fire for each enumerated device.\n\npUserData (in)\n    A pointer to application-defined data passed to the callback.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error code otherwise.\n\n\nThread Safety\n-------------\nSafe. This is guarded using a simple mutex lock.\n\n\nRemarks\n-------\nDo _not_ assume the first enumerated device of a given type is the default device.\n\nSome backends and platforms may only support default playback and capture devices.\n\nIn general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also,\ndo not try to call `ma_context_get_device_info()` from within the callback.\n\nConsider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation.\n\n\nExample 1 - Simple Enumeration\n------------------------------\nma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)\n{\n    printf(\"Device Name: %s\\n\", pInfo->name);\n    return MA_TRUE;\n}\n\nma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData);\nif (result != MA_SUCCESS) {\n    // Error.\n}\n\n\nSee Also\n--------\nma_context_get_devices()\n*/\nMA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);\n\n/*\nRetrieves basic information about every active playback and/or capture device.\n\nThis function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`\nparameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.\n\n\nParameters\n----------\npContext (in)\n    A pointer to the context performing the enumeration.\n\nppPlaybackDeviceInfos (out)\n    A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices.\n\npPlaybackDeviceCount (out)\n    A pointer to an unsigned integer that will receive the number of playback devices.\n\nppCaptureDeviceInfos (out)\n    A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices.\n\npCaptureDeviceCount (out)\n    A pointer to an unsigned integer that will receive the number of capture devices.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error code otherwise.\n\n\nThread Safety\n-------------\nUnsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple\nthreads. Instead, you need to make a copy of the returned data with your own higher level synchronization.\n\n\nRemarks\n-------\nIt is _not_ safe to assume the first device in the list is the default device.\n\nYou can pass in NULL for the playback or capture lists in which case they'll be ignored.\n\nThe returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers.\n\n\nSee Also\n--------\nma_context_get_devices()\n*/\nMA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);\n\n/*\nRetrieves information about a device of the given type, with the specified ID and share mode.\n\n\nParameters\n----------\npContext (in)\n    A pointer to the context performing the query.\n\ndeviceType (in)\n    The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`.\n\npDeviceID (in)\n    The ID of the device being queried.\n\npDeviceInfo (out)\n    A pointer to the `ma_device_info` structure that will receive the device information.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error code otherwise.\n\n\nThread Safety\n-------------\nSafe. This is guarded using a simple mutex lock.\n\n\nRemarks\n-------\nDo _not_ call this from within the `ma_context_enumerate_devices()` callback.\n\nIt's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in\nshared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify\nwhich share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if\nthe requested share mode is unsupported.\n\nThis leaves pDeviceInfo unmodified in the result of an error.\n*/\nMA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);\n\n/*\nDetermines if the given context supports loopback mode.\n\n\nParameters\n----------\npContext (in)\n    A pointer to the context getting queried.\n\n\nReturn Value\n------------\nMA_TRUE if the context supports loopback mode; MA_FALSE otherwise.\n*/\nMA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext);\n\n\n\n/*\nInitializes a device config with default settings.\n\n\nParameters\n----------\ndeviceType (in)\n    The type of the device this config is being initialized for. This must set to one of the following:\n\n    |-------------------------|\n    | Device Type             |\n    |-------------------------|\n    | ma_device_type_playback |\n    | ma_device_type_capture  |\n    | ma_device_type_duplex   |\n    | ma_device_type_loopback |\n    |-------------------------|\n\n\nReturn Value\n------------\nA new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks.\n\n\nThread Safety\n-------------\nSafe.\n\n\nCallback Safety\n---------------\nSafe, but don't try initializing a device in a callback.\n\n\nRemarks\n-------\nThe returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a\ntypical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change\nbefore initializing the device.\n\nSee `ma_device_init()` for details on specific configuration options.\n\n\nExample 1 - Simple Configuration\n--------------------------------\nThe example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and\nthen the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added\nto the `ma_device_config` structure.\n\n```c\nma_device_config config = ma_device_config_init(ma_device_type_playback);\nconfig.playback.format   = ma_format_f32;\nconfig.playback.channels = 2;\nconfig.sampleRate        = 48000;\nconfig.dataCallback      = ma_data_callback;\nconfig.pUserData         = pMyUserData;\n```\n\n\nSee Also\n--------\nma_device_init()\nma_device_init_ex()\n*/\nMA_API ma_device_config ma_device_config_init(ma_device_type deviceType);\n\n\n/*\nInitializes a device.\n\nA device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it\nfrom a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be\nplayback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the\ndevice is done via a callback which is fired by miniaudio at periodic time intervals.\n\nThe frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames\nor milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and\nincreased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but\nminiaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple\nmedia player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the\nbackend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for.\n\nWhen delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the\nformat that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you\ncan assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline.\n\n\nParameters\n----------\npContext (in, optional)\n    A pointer to the context that owns the device. This can be null, in which case it creates a default context internally.\n\npConfig (in)\n    A pointer to the device configuration. Cannot be null. See remarks for details.\n\npDevice (out)\n    A pointer to the device object being initialized.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error code otherwise.\n\n\nThread Safety\n-------------\nUnsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to\ncalling this at the same time as `ma_device_uninit()`.\n\n\nCallback Safety\n---------------\nUnsafe. It is not safe to call this inside any callback.\n\n\nRemarks\n-------\nSetting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so:\n\n    ```c\n    ma_context_init(NULL, 0, NULL, &context);\n    ```\n\nDo not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use\ndevice.pContext for the initialization of other devices.\n\nThe device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can\nthen be set directly on the structure. Below are the members of the `ma_device_config` object.\n\n    deviceType\n        Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`.\n\n    sampleRate\n        The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate.\n\n    periodSizeInFrames\n        The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will\n        be used depending on the selected performance profile. This value affects latency. See below for details.\n\n    periodSizeInMilliseconds\n        The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be\n        used depending on the selected performance profile. The value affects latency. See below for details.\n\n    periods\n        The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by\n        this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured.\n\n    performanceProfile\n        A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or\n        `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value.\n\n    noPreSilencedOutputBuffer\n        When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of\n        the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data\n        callback will write to every sample in the output buffer, or if you are doing your own clearing.\n\n    noClip\n        When set to true, the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or\n        not to clip. When set to false (default), the contents of the output buffer passed into the data callback will be clipped after returning. This only\n        applies when the playback sample format is f32.\n\n    noDisableDenormals\n        By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals.\n\n    noFixedSizedCallback\n        Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a\n        consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with\n        whatever the backend requests, which could be anything.\n\n    dataCallback\n        The callback to fire whenever data is ready to be delivered to or from the device.\n\n    notificationCallback\n        The callback to fire when something has changed with the device, such as whether or not it has been started or stopped.\n\n    pUserData\n        The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`.\n\n    resampling.algorithm\n        The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The\n        default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`.\n\n    resampling.pBackendVTable\n        A pointer to an optional vtable that can be used for plugging in a custom resampler.\n\n    resampling.pBackendUserData\n        A pointer that will passed to callbacks in pBackendVTable.\n\n    resampling.linear.lpfOrder\n        The linear resampler applies a low-pass filter as part of it's processing for anti-aliasing. This setting controls the order of the filter. The higher\n        the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is\n        `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`.\n\n    playback.pDeviceID\n        A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's\n        default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.\n\n    playback.format\n        The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after\n        initialization from the device object directly with `device.playback.format`.\n\n    playback.channels\n        The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization\n        from the device object directly with `device.playback.channels`.\n\n    playback.pChannelMap\n        The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the\n        device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items.\n\n    playback.shareMode\n        The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify\n        exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to\n        ma_share_mode_shared and reinitializing.\n\n    capture.pDeviceID\n        A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's\n        default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.\n\n    capture.format\n        The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after\n        initialization from the device object directly with `device.capture.format`.\n\n    capture.channels\n        The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization\n        from the device object directly with `device.capture.channels`.\n\n    capture.pChannelMap\n        The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the\n        device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items.\n\n    capture.shareMode\n        The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify\n        exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to\n        ma_share_mode_shared and reinitializing.\n\n    wasapi.noAutoConvertSRC\n        WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false.\n\n    wasapi.noDefaultQualitySRC\n        WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`.\n        You should usually leave this set to false, which is the default.\n\n    wasapi.noAutoStreamRouting\n        WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false.\n\n    wasapi.noHardwareOffloading\n        WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false.\n\n    alsa.noMMap\n        ALSA only. When set to true, disables MMap mode. Defaults to false.\n\n    alsa.noAutoFormat\n        ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false.\n\n    alsa.noAutoChannels\n        ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false.\n\n    alsa.noAutoResample\n        ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false.\n\n    pulse.pStreamNamePlayback\n        PulseAudio only. Sets the stream name for playback.\n\n    pulse.pStreamNameCapture\n        PulseAudio only. Sets the stream name for capture.\n\n    coreaudio.allowNominalSampleRateChange\n        Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This\n        is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate\n        that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will\n        find the closest match between the sample rate requested in the device config and the sample rates natively supported by the\n        hardware. When set to false, the sample rate currently set by the operating system will always be used.\n\n    opensl.streamType\n        OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the\n        stream type will be left unset. Think of this as the type of audio you're playing.\n\n    opensl.recordingPreset\n        OpenSL only. Explicitly sets the type of recording your program will be doing. When left\n        unset, the recording preset will be left unchanged.\n\n    aaudio.usage\n        AAudio only. Explicitly sets the nature of the audio the program will be consuming. When\n        left unset, the usage will be left unchanged.\n\n    aaudio.contentType\n        AAudio only. Sets the content type. When left unset, the content type will be left unchanged.\n\n    aaudio.inputPreset\n        AAudio only. Explicitly sets the type of recording your program will be doing. When left\n        unset, the input preset will be left unchanged.\n\n    aaudio.noAutoStartAfterReroute\n        AAudio only. Controls whether or not the device should be automatically restarted after a\n        stream reroute. When set to false (default) the device will be restarted automatically;\n        otherwise the device will be stopped.\n\n\nOnce initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device.\n\nAfter initializing the device it will be in a stopped state. To start it, use `ma_device_start()`.\n\nIf both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or\n`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or\n`ma_performance_profile_conservative`.\n\nIf you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device\nin exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the\nconfig) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA,\nfor example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user.\nStarting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary.\n\nWhen sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config\nand the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run\non an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`,\n`playback/capture.channels` and `sampleRate` members of the device object.\n\nWhen compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message\nasking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information.\n\nALSA Specific: When initializing the default device, requesting shared mode will try using the \"dmix\" device for playback and the \"dsnoop\" device for capture.\nIf these fail it will try falling back to the \"hw\" device.\n\n\nExample 1 - Simple Initialization\n---------------------------------\nThis example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default\nplayback device this is usually all you need.\n\n```c\nma_device_config config = ma_device_config_init(ma_device_type_playback);\nconfig.playback.format   = ma_format_f32;\nconfig.playback.channels = 2;\nconfig.sampleRate        = 48000;\nconfig.dataCallback      = ma_data_callback;\nconfig.pMyUserData       = pMyUserData;\n\nma_device device;\nma_result result = ma_device_init(NULL, &config, &device);\nif (result != MA_SUCCESS) {\n    // Error\n}\n```\n\n\nExample 2 - Advanced Initialization\n-----------------------------------\nThis example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size\nand period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device\nenumeration.\n\n```c\nma_context context;\nma_result result = ma_context_init(NULL, 0, NULL, &context);\nif (result != MA_SUCCESS) {\n    // Error\n}\n\nma_device_info* pPlaybackDeviceInfos;\nma_uint32 playbackDeviceCount;\nresult = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL);\nif (result != MA_SUCCESS) {\n    // Error\n}\n\n// ... choose a device from pPlaybackDeviceInfos ...\n\nma_device_config config = ma_device_config_init(ma_device_type_playback);\nconfig.playback.pDeviceID       = pMyChosenDeviceID;    // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices().\nconfig.playback.format          = ma_format_f32;\nconfig.playback.channels        = 2;\nconfig.sampleRate               = 48000;\nconfig.dataCallback             = ma_data_callback;\nconfig.pUserData                = pMyUserData;\nconfig.periodSizeInMilliseconds = 10;\nconfig.periods                  = 3;\n\nma_device device;\nresult = ma_device_init(&context, &config, &device);\nif (result != MA_SUCCESS) {\n    // Error\n}\n```\n\n\nSee Also\n--------\nma_device_config_init()\nma_device_uninit()\nma_device_start()\nma_context_init()\nma_context_get_devices()\nma_context_enumerate_devices()\n*/\nMA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);\n\n/*\nInitializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context.\n\nThis is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function\nallows you to configure the internally created context.\n\n\nParameters\n----------\nbackends (in, optional)\n    A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.\n\nbackendCount (in, optional)\n    The number of items in `backend`. Ignored if `backend` is NULL.\n\npContextConfig (in, optional)\n    The context configuration.\n\npConfig (in)\n    A pointer to the device configuration. Cannot be null. See remarks for details.\n\npDevice (out)\n    A pointer to the device object being initialized.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error code otherwise.\n\n\nThread Safety\n-------------\nUnsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to\ncalling this at the same time as `ma_device_uninit()`.\n\n\nCallback Safety\n---------------\nUnsafe. It is not safe to call this inside any callback.\n\n\nRemarks\n-------\nYou only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage\nyour own context.\n\nSee the documentation for `ma_context_init()` for information on the different context configuration options.\n\n\nSee Also\n--------\nma_device_init()\nma_device_uninit()\nma_device_config_init()\nma_context_init()\n*/\nMA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice);\n\n/*\nUninitializes a device.\n\nThis will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device to stop.\n\n\nReturn Value\n------------\nNothing\n\n\nThread Safety\n-------------\nUnsafe. As soon as this API is called the device should be considered undefined.\n\n\nCallback Safety\n---------------\nUnsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.\n\n\nSee Also\n--------\nma_device_init()\nma_device_stop()\n*/\nMA_API void ma_device_uninit(ma_device* pDevice);\n\n\n/*\nRetrieves a pointer to the context that owns the given device.\n*/\nMA_API ma_context* ma_device_get_context(ma_device* pDevice);\n\n/*\nHelper function for retrieving the log object associated with the context that owns this device.\n*/\nMA_API ma_log* ma_device_get_log(ma_device* pDevice);\n\n\n/*\nRetrieves information about the device.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device whose information is being retrieved.\n\ntype (in)\n    The device type. This parameter is required for duplex devices. When retrieving device\n    information, you are doing so for an individual playback or capture device.\n\npDeviceInfo (out)\n    A pointer to the `ma_device_info` that will receive the device information.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error code otherwise.\n\n\nThread Safety\n-------------\nUnsafe. This should be considered unsafe because it may be calling into the backend which may or\nmay not be safe.\n\n\nCallback Safety\n---------------\nUnsafe. You should avoid calling this in the data callback because it may call into the backend\nwhich may or may not be safe.\n*/\nMA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo);\n\n\n/*\nRetrieves the name of the device.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device whose information is being retrieved.\n\ntype (in)\n    The device type. This parameter is required for duplex devices. When retrieving device\n    information, you are doing so for an individual playback or capture device.\n\npName (out)\n    A pointer to the buffer that will receive the name.\n\nnameCap (in)\n    The capacity of the output buffer, including space for the null terminator.\n\npLengthNotIncludingNullTerminator (out, optional)\n    A pointer to the variable that will receive the length of the name, not including the null\n    terminator.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error code otherwise.\n\n\nThread Safety\n-------------\nUnsafe. This should be considered unsafe because it may be calling into the backend which may or\nmay not be safe.\n\n\nCallback Safety\n---------------\nUnsafe. You should avoid calling this in the data callback because it may call into the backend\nwhich may or may not be safe.\n\n\nRemarks\n-------\nIf the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to\n`pName` if you want to first get the length of the name for the purpose of memory allocation of the\noutput buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for\nmost cases and will avoid the need for the inefficiency of calling this function twice.\n\nThis is implemented in terms of `ma_device_get_info()`.\n*/\nMA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator);\n\n\n/*\nStarts the device. For playback devices this begins playback. For capture devices it begins recording.\n\nUse `ma_device_stop()` to stop the device.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device to start.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error code otherwise.\n\n\nThread Safety\n-------------\nSafe. It's safe to call this from any thread with the exception of the callback thread.\n\n\nCallback Safety\n---------------\nUnsafe. It is not safe to call this inside any callback.\n\n\nRemarks\n-------\nFor a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid\naudio data in the buffer, which needs to be done before the device begins playback.\n\nThis API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety.\n\nDo not call this in any callback.\n\n\nSee Also\n--------\nma_device_stop()\n*/\nMA_API ma_result ma_device_start(ma_device* pDevice);\n\n/*\nStops the device. For playback devices this stops playback. For capture devices it stops recording.\n\nUse `ma_device_start()` to start the device again.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device to stop.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error code otherwise.\n\n\nThread Safety\n-------------\nSafe. It's safe to call this from any thread with the exception of the callback thread.\n\n\nCallback Safety\n---------------\nUnsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.\n\n\nRemarks\n-------\nThis API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some\nbackends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size\nthat was specified at initialization time).\n\nBackends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and\nthe resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the\nspeakers or received from the microphone which can in turn result in de-syncs.\n\nDo not call this in any callback.\n\n\nSee Also\n--------\nma_device_start()\n*/\nMA_API ma_result ma_device_stop(ma_device* pDevice);\n\n/*\nDetermines whether or not the device is started.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device whose start state is being retrieved.\n\n\nReturn Value\n------------\nTrue if the device is started, false otherwise.\n\n\nThread Safety\n-------------\nSafe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return\nvalue will be out of sync.\n\n\nCallback Safety\n---------------\nSafe. This is implemented as a simple accessor.\n\n\nSee Also\n--------\nma_device_start()\nma_device_stop()\n*/\nMA_API ma_bool32 ma_device_is_started(const ma_device* pDevice);\n\n\n/*\nRetrieves the state of the device.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device whose state is being retrieved.\n\n\nReturn Value\n------------\nThe current state of the device. The return value will be one of the following:\n\n    +-------------------------------+------------------------------------------------------------------------------+\n    | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization.      |\n    +-------------------------------+------------------------------------------------------------------------------+\n    | ma_device_state_stopped       | The device is stopped. The initial state of the device after initialization. |\n    +-------------------------------+------------------------------------------------------------------------------+\n    | ma_device_state_started       | The device started and requesting and/or delivering audio data.              |\n    +-------------------------------+------------------------------------------------------------------------------+\n    | ma_device_state_starting      | The device is in the process of starting.                                    |\n    +-------------------------------+------------------------------------------------------------------------------+\n    | ma_device_state_stopping      | The device is in the process of stopping.                                    |\n    +-------------------------------+------------------------------------------------------------------------------+\n\n\nThread Safety\n-------------\nSafe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called,\nthere's a possibility the return value could be out of sync. See remarks.\n\n\nCallback Safety\n---------------\nSafe. This is implemented as a simple accessor.\n\n\nRemarks\n-------\nThe general flow of a devices state goes like this:\n\n    ```\n    ma_device_init()  -> ma_device_state_uninitialized -> ma_device_state_stopped\n    ma_device_start() -> ma_device_state_starting      -> ma_device_state_started\n    ma_device_stop()  -> ma_device_state_stopping      -> ma_device_state_stopped\n    ```\n\nWhen the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the\nvalue returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own\nsynchronization.\n*/\nMA_API ma_device_state ma_device_get_state(const ma_device* pDevice);\n\n\n/*\nPerforms post backend initialization routines for setting up internal data conversion.\n\nThis should be called whenever the backend is initialized. The only time this should be called from\noutside of miniaudio is if you're implementing a custom backend, and you would only do it if you\nare reinitializing the backend due to rerouting or reinitializing for some reason.\n\n\nParameters\n----------\npDevice [in]\n    A pointer to the device.\n\ndeviceType [in]\n    The type of the device that was just reinitialized.\n\npPlaybackDescriptor [in]\n    The descriptor of the playback device containing the internal data format and buffer sizes.\n\npPlaybackDescriptor [in]\n    The descriptor of the capture device containing the internal data format and buffer sizes.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other error otherwise.\n\n\nThread Safety\n-------------\nUnsafe. This will be reinitializing internal data converters which may be in use by another thread.\n\n\nCallback Safety\n---------------\nUnsafe. This will be reinitializing internal data converters which may be in use by the callback.\n\n\nRemarks\n-------\nFor a duplex device, you can call this for only one side of the system. This is why the deviceType\nis specified as a parameter rather than deriving it from the device.\n\nYou do not need to call this manually unless you are doing a custom backend, in which case you need\nonly do it if you're manually performing rerouting or reinitialization.\n*/\nMA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor);\n\n\n/*\nSets the master volume factor for the device.\n\nThe volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and\nvalues less than 0 decreases the volume.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device whose volume is being set.\n\nvolume (in)\n    The new volume factor. Must be >= 0.\n\n\nReturn Value\n------------\nMA_SUCCESS if the volume was set successfully.\nMA_INVALID_ARGS if pDevice is NULL.\nMA_INVALID_ARGS if volume is negative.\n\n\nThread Safety\n-------------\nSafe. This just sets a local member of the device object.\n\n\nCallback Safety\n---------------\nSafe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.\n\n\nRemarks\n-------\nThis applies the volume factor across all channels.\n\nThis does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.\n\n\nSee Also\n--------\nma_device_get_master_volume()\nma_device_set_master_volume_db()\nma_device_get_master_volume_db()\n*/\nMA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume);\n\n/*\nRetrieves the master volume factor for the device.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device whose volume factor is being retrieved.\n\npVolume (in)\n    A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1].\n\n\nReturn Value\n------------\nMA_SUCCESS if successful.\nMA_INVALID_ARGS if pDevice is NULL.\nMA_INVALID_ARGS if pVolume is NULL.\n\n\nThread Safety\n-------------\nSafe. This just a simple member retrieval.\n\n\nCallback Safety\n---------------\nSafe.\n\n\nRemarks\n-------\nIf an error occurs, `*pVolume` will be set to 0.\n\n\nSee Also\n--------\nma_device_set_master_volume()\nma_device_set_master_volume_gain_db()\nma_device_get_master_volume_gain_db()\n*/\nMA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume);\n\n/*\nSets the master volume for the device as gain in decibels.\n\nA gain of 0 is full volume, whereas a gain of < 0 will decrease the volume.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device whose gain is being set.\n\ngainDB (in)\n    The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume.\n\n\nReturn Value\n------------\nMA_SUCCESS if the volume was set successfully.\nMA_INVALID_ARGS if pDevice is NULL.\nMA_INVALID_ARGS if the gain is > 0.\n\n\nThread Safety\n-------------\nSafe. This just sets a local member of the device object.\n\n\nCallback Safety\n---------------\nSafe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.\n\n\nRemarks\n-------\nThis applies the gain across all channels.\n\nThis does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.\n\n\nSee Also\n--------\nma_device_get_master_volume_gain_db()\nma_device_set_master_volume()\nma_device_get_master_volume()\n*/\nMA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB);\n\n/*\nRetrieves the master gain in decibels.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to the device whose gain is being retrieved.\n\npGainDB (in)\n    A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful.\nMA_INVALID_ARGS if pDevice is NULL.\nMA_INVALID_ARGS if pGainDB is NULL.\n\n\nThread Safety\n-------------\nSafe. This just a simple member retrieval.\n\n\nCallback Safety\n---------------\nSafe.\n\n\nRemarks\n-------\nIf an error occurs, `*pGainDB` will be set to 0.\n\n\nSee Also\n--------\nma_device_set_master_volume_db()\nma_device_set_master_volume()\nma_device_get_master_volume()\n*/\nMA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB);\n\n\n/*\nCalled from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback.\n\n\nParameters\n----------\npDevice (in)\n    A pointer to device whose processing the data callback.\n\npOutput (out)\n    A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device\n    this can be NULL, in which case pInput must not be NULL.\n\npInput (in)\n    A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be\n    NULL, in which case `pOutput` must not be NULL.\n\nframeCount (in)\n    The number of frames being processed.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful; any other result code otherwise.\n\n\nThread Safety\n-------------\nThis function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a\nplayback and capture device in duplex setups.\n\n\nCallback Safety\n---------------\nDo not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend.\n\n\nRemarks\n-------\nIf both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in\nwhich case `pInput` will be processed first, followed by `pOutput`.\n\nIf you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that\ncallback.\n*/\nMA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);\n\n\n/*\nCalculates an appropriate buffer size from a descriptor, native sample rate and performance profile.\n\nThis function is used by backends for helping determine an appropriately sized buffer to use with\nthe device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the\n`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a\nbest guess at the device's native sample rate is also required which is where `nativeSampleRate`\ncomes in. In addition, the performance profile is also needed for cases where both the period size\nin frames and milliseconds are both zero.\n\n\nParameters\n----------\npDescriptor (in)\n    A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members\n    will be used for the calculation of the buffer size.\n\nnativeSampleRate (in)\n    The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of\n    `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which\n    case a sample rate is required to convert to a size in frames.\n\nperformanceProfile (in)\n    When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are\n    zero, miniaudio will fall back to a buffer size based on the performance profile. The profile\n    to use for this calculation is determine by this parameter.\n\n\nReturn Value\n------------\nThe calculated buffer size in frames.\n\n\nThread Safety\n-------------\nThis is safe so long as nothing modifies `pDescriptor` at the same time. However, this function\nshould only ever be called from within the backend's device initialization routine and therefore\nshouldn't have any multithreading concerns.\n\n\nCallback Safety\n---------------\nThis is safe to call within the data callback, but there is no reason to ever do this.\n\n\nRemarks\n-------\nIf `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that\nis also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead.\n*/\nMA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile);\n\n\n\n/*\nRetrieves a friendly name for a backend.\n*/\nMA_API const char* ma_get_backend_name(ma_backend backend);\n\n/*\nRetrieves the backend enum from the given name.\n*/\nMA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend);\n\n/*\nDetermines whether or not the given backend is available by the compilation environment.\n*/\nMA_API ma_bool32 ma_is_backend_enabled(ma_backend backend);\n\n/*\nRetrieves compile-time enabled backends.\n\n\nParameters\n----------\npBackends (out, optional)\n    A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting\n    the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends.\n\nbackendCap (in)\n    The capacity of the `pBackends` buffer.\n\npBackendCount (out)\n    A pointer to the variable that will receive the enabled backend count.\n\n\nReturn Value\n------------\nMA_SUCCESS if successful.\nMA_INVALID_ARGS if `pBackendCount` is NULL.\nMA_NO_SPACE if the capacity of `pBackends` is not large enough.\n\nIf `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values.\n\n\nThread Safety\n-------------\nSafe.\n\n\nCallback Safety\n---------------\nSafe.\n\n\nRemarks\n-------\nIf you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call\nthis function with `pBackends` set to NULL.\n\nThis will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null`\nwhen you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at\ncompile time with `MA_NO_NULL`.\n\nThe returned backends are determined based on compile time settings, not the platform it's currently running on. For\nexample, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have\nPulseAudio installed.\n\n\nExample 1\n---------\nThe example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is\ngiven a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends.\nSince `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios.\n\n```\nma_backend enabledBackends[MA_BACKEND_COUNT];\nsize_t enabledBackendCount;\n\nresult = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount);\nif (result != MA_SUCCESS) {\n    // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid.\n}\n```\n\n\nSee Also\n--------\nma_is_backend_enabled()\n*/\nMA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount);\n\n/*\nDetermines whether or not loopback mode is support by a backend.\n*/\nMA_API ma_bool32 ma_is_loopback_supported(ma_backend backend);\n\n#endif  /* MA_NO_DEVICE_IO */\n\n\n\n/************************************************************************************************************************************************************\n\nUtilities\n\n************************************************************************************************************************************************************/\n\n/*\nCalculates a buffer size in milliseconds from the specified number of frames and sample rate.\n*/\nMA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate);\n\n/*\nCalculates a buffer size in frames from the specified number of milliseconds and sample rate.\n*/\nMA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate);\n\n/*\nCopies PCM frames from one buffer to another.\n*/\nMA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels);\n\n/*\nCopies silent frames into the given buffer.\n\nRemarks\n-------\nFor all formats except `ma_format_u8`, the output buffer will be filled with 0. For `ma_format_u8` it will be filled with 128. The reason for this is that it\nmakes more sense for the purpose of mixing to initialize it to the center point.\n*/\nMA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels);\n\n\n/*\nOffsets a pointer by the specified number of PCM frames.\n*/\nMA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);\nMA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);\nstatic MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); }\nstatic MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); }\n\n\n/*\nClips samples.\n*/\nMA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count);\nMA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count);\nMA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count);\nMA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count);\nMA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count);\nMA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels);\n\n/*\nHelper for applying a volume factor to samples.\n\nNote that the source and destination buffers can be the same, in which case it'll perform the operation in-place.\n*/\nMA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor);\nMA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor);\nMA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor);\nMA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor);\nMA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor);\n\nMA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor);\nMA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor);\nMA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor);\nMA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor);\nMA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor);\n\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);\n\nMA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);\nMA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);\nMA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);\nMA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);\nMA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);\nMA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);\n\nMA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains);\n\n\nMA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume);\nMA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume);\nMA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume);\nMA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume);\nMA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume);\nMA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume);\n\n\n/*\nHelper for converting a linear factor to gain in decibels.\n*/\nMA_API float ma_volume_linear_to_db(float factor);\n\n/*\nHelper for converting gain in decibels to a linear factor.\n*/\nMA_API float ma_volume_db_to_linear(float gain);\n\n\n/*\nMixes the specified number of frames in floating point format with a volume factor.\n\nThis will run on an optimized path when the volume is equal to 1.\n*/\nMA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume);\n\n\n\n\n/************************************************************************************************************************************************************\n\nVFS\n===\n\nThe VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely\nappropriate for a given situation.\n\n************************************************************************************************************************************************************/\ntypedef void      ma_vfs;\ntypedef ma_handle ma_vfs_file;\n\ntypedef enum\n{\n    MA_OPEN_MODE_READ  = 0x00000001,\n    MA_OPEN_MODE_WRITE = 0x00000002\n} ma_open_mode_flags;\n\ntypedef enum\n{\n    ma_seek_origin_start,\n    ma_seek_origin_current,\n    ma_seek_origin_end  /* Not used by decoders. */\n} ma_seek_origin;\n\ntypedef struct\n{\n    ma_uint64 sizeInBytes;\n} ma_file_info;\n\ntypedef struct\n{\n    ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);\n    ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);\n    ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file);\n    ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);\n    ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);\n    ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);\n    ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);\n    ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);\n} ma_vfs_callbacks;\n\nMA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);\nMA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);\nMA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file);\nMA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);\nMA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);\nMA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);\nMA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);\nMA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);\nMA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks);\n\ntypedef struct\n{\n    ma_vfs_callbacks cb;\n    ma_allocation_callbacks allocationCallbacks;    /* Only used for the wchar_t version of open() on non-Windows platforms. */\n} ma_default_vfs;\n\nMA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\n\ntypedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead);\ntypedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin);\ntypedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor);\n\n\n\n#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)\ntypedef enum\n{\n    ma_encoding_format_unknown = 0,\n    ma_encoding_format_wav,\n    ma_encoding_format_flac,\n    ma_encoding_format_mp3,\n    ma_encoding_format_vorbis\n} ma_encoding_format;\n#endif\n\n/************************************************************************************************************************************************************\n\nDecoding\n========\n\nDecoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless\nyou do your own synchronization.\n\n************************************************************************************************************************************************************/\n#ifndef MA_NO_DECODING\ntypedef struct ma_decoder ma_decoder;\n\n\ntypedef struct\n{\n    ma_format preferredFormat;\n    ma_uint32 seekPointCount;   /* Set to > 0 to generate a seektable if the decoding backend supports it. */\n} ma_decoding_backend_config;\n\nMA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount);\n\n\ntypedef struct\n{\n    ma_result (* onInit      )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend);\n    ma_result (* onInitFile  )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend);               /* Optional. */\n    ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend);            /* Optional. */\n    ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend);  /* Optional. */\n    void      (* onUninit    )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);\n} ma_decoding_backend_vtable;\n\n\ntypedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead);         /* Returns the number of bytes read. */\ntypedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin);\ntypedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor);\n\ntypedef struct\n{\n    ma_format format;      /* Set to 0 or ma_format_unknown to use the stream's internal format. */\n    ma_uint32 channels;    /* Set to 0 to use the stream's internal channels. */\n    ma_uint32 sampleRate;  /* Set to 0 to use the stream's internal sample rate. */\n    ma_channel* pChannelMap;\n    ma_channel_mix_mode channelMixMode;\n    ma_dither_mode ditherMode;\n    ma_resampler_config resampling;\n    ma_allocation_callbacks allocationCallbacks;\n    ma_encoding_format encodingFormat;\n    ma_uint32 seekPointCount;   /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */\n    ma_decoding_backend_vtable** ppCustomBackendVTables;\n    ma_uint32 customBackendCount;\n    void* pCustomBackendUserData;\n} ma_decoder_config;\n\nstruct ma_decoder\n{\n    ma_data_source_base ds;\n    ma_data_source* pBackend;                   /* The decoding backend we'll be pulling data from. */\n    const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */\n    void* pBackendUserData;\n    ma_decoder_read_proc onRead;\n    ma_decoder_seek_proc onSeek;\n    ma_decoder_tell_proc onTell;\n    void* pUserData;\n    ma_uint64 readPointerInPCMFrames;      /* In output sample rate. Used for keeping track of how many frames are available for decoding. */\n    ma_format outputFormat;\n    ma_uint32 outputChannels;\n    ma_uint32 outputSampleRate;\n    ma_data_converter converter;    /* Data conversion is achieved by running frames through this. */\n    void* pInputCache;              /* In input format. Can be null if it's not needed. */\n    ma_uint64 inputCacheCap;        /* The capacity of the input cache. */\n    ma_uint64 inputCacheConsumed;   /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */\n    ma_uint64 inputCacheRemaining;  /* The number of valid frames remaining in the cahce. */\n    ma_allocation_callbacks allocationCallbacks;\n    union\n    {\n        struct\n        {\n            ma_vfs* pVFS;\n            ma_vfs_file file;\n        } vfs;\n        struct\n        {\n            const ma_uint8* pData;\n            size_t dataSize;\n            size_t currentReadPos;\n        } memory;               /* Only used for decoders that were opened against a block of memory. */\n    } data;\n};\n\nMA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);\nMA_API ma_decoder_config ma_decoder_config_init_default(void);\n\nMA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);\nMA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);\nMA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);\nMA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);\nMA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);\nMA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);\n\n/*\nUninitializes a decoder.\n*/\nMA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder);\n\n/*\nReads PCM frames from the given decoder.\n\nThis is not thread safe without your own synchronization.\n*/\nMA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\n\n/*\nSeeks to a PCM frame based on it's absolute index.\n\nThis is not thread safe without your own synchronization.\n*/\nMA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex);\n\n/*\nRetrieves the decoder's output data format.\n*/\nMA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);\n\n/*\nRetrieves the current position of the read cursor in PCM frames.\n*/\nMA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor);\n\n/*\nRetrieves the length of the decoder in PCM frames.\n\nDo not call this on streams of an undefined length, such as internet radio.\n\nIf the length is unknown or an error occurs, 0 will be returned.\n\nThis will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio\nuses internally.\n\nFor MP3's, this will decode the entire file. Do not call this in time critical scenarios.\n\nThis function is not thread safe without your own synchronization.\n*/\nMA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength);\n\n/*\nRetrieves the number of frames that can be read before reaching the end.\n\nThis calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in\nparticular ensuring you do not call it on streams of an undefined length, such as internet radio.\n\nIf the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be\nreturned.\n*/\nMA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames);\n\n/*\nHelper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,\npConfig should be set to what you want. On output it will be set to what you got.\n*/\nMA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);\nMA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);\nMA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);\n\n#endif  /* MA_NO_DECODING */\n\n\n/************************************************************************************************************************************************************\n\nEncoding\n========\n\nEncoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned.\n\n************************************************************************************************************************************************************/\n#ifndef MA_NO_ENCODING\ntypedef struct ma_encoder ma_encoder;\n\ntypedef ma_result (* ma_encoder_write_proc)           (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten);\ntypedef ma_result (* ma_encoder_seek_proc)            (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin);\ntypedef ma_result (* ma_encoder_init_proc)            (ma_encoder* pEncoder);\ntypedef void      (* ma_encoder_uninit_proc)          (ma_encoder* pEncoder);\ntypedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten);\n\ntypedef struct\n{\n    ma_encoding_format encodingFormat;\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_allocation_callbacks allocationCallbacks;\n} ma_encoder_config;\n\nMA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);\n\nstruct ma_encoder\n{\n    ma_encoder_config config;\n    ma_encoder_write_proc onWrite;\n    ma_encoder_seek_proc onSeek;\n    ma_encoder_init_proc onInit;\n    ma_encoder_uninit_proc onUninit;\n    ma_encoder_write_pcm_frames_proc onWritePCMFrames;\n    void* pUserData;\n    void* pInternalEncoder;\n    union\n    {\n        struct\n        {\n            ma_vfs* pVFS;\n            ma_vfs_file file;\n        } vfs;\n    } data;\n};\n\nMA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder);\nMA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);\nMA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);\nMA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);\nMA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);\nMA_API void ma_encoder_uninit(ma_encoder* pEncoder);\nMA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten);\n\n#endif /* MA_NO_ENCODING */\n\n\n/************************************************************************************************************************************************************\n\nGeneration\n\n************************************************************************************************************************************************************/\n#ifndef MA_NO_GENERATION\ntypedef enum\n{\n    ma_waveform_type_sine,\n    ma_waveform_type_square,\n    ma_waveform_type_triangle,\n    ma_waveform_type_sawtooth\n} ma_waveform_type;\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_waveform_type type;\n    double amplitude;\n    double frequency;\n} ma_waveform_config;\n\nMA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency);\n\ntypedef struct\n{\n    ma_data_source_base ds;\n    ma_waveform_config config;\n    double advance;\n    double time;\n} ma_waveform;\n\nMA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform);\nMA_API void ma_waveform_uninit(ma_waveform* pWaveform);\nMA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex);\nMA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude);\nMA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency);\nMA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type);\nMA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate);\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    double dutyCycle;\n    double amplitude;\n    double frequency;\n} ma_pulsewave_config;\n\nMA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency);\n\ntypedef struct\n{\n    ma_waveform waveform;\n    ma_pulsewave_config config;\n} ma_pulsewave;\n\nMA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform);\nMA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform);\nMA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex);\nMA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude);\nMA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency);\nMA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate);\nMA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle);\n\ntypedef enum\n{\n    ma_noise_type_white,\n    ma_noise_type_pink,\n    ma_noise_type_brownian\n} ma_noise_type;\n\n\ntypedef struct\n{\n    ma_format format;\n    ma_uint32 channels;\n    ma_noise_type type;\n    ma_int32 seed;\n    double amplitude;\n    ma_bool32 duplicateChannels;\n} ma_noise_config;\n\nMA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude);\n\ntypedef struct\n{\n    ma_data_source_base ds;\n    ma_noise_config config;\n    ma_lcg lcg;\n    union\n    {\n        struct\n        {\n            double** bin;\n            double* accumulation;\n            ma_uint32* counter;\n        } pink;\n        struct\n        {\n            double* accumulation;\n        } brownian;\n    } state;\n\n    /* Memory management. */\n    void* _pHeap;\n    ma_bool32 _ownsHeap;\n} ma_noise;\n\nMA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise);\nMA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise);\nMA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude);\nMA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed);\nMA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type);\n\n#endif  /* MA_NO_GENERATION */\n\n\n\n/************************************************************************************************************************************************************\n\nResource Manager\n\n************************************************************************************************************************************************************/\n/* The resource manager cannot be enabled if there is no decoder. */\n#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING)\n#define MA_NO_RESOURCE_MANAGER\n#endif\n\n#ifndef MA_NO_RESOURCE_MANAGER\ntypedef struct ma_resource_manager                  ma_resource_manager;\ntypedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node;\ntypedef struct ma_resource_manager_data_buffer      ma_resource_manager_data_buffer;\ntypedef struct ma_resource_manager_data_stream      ma_resource_manager_data_stream;\ntypedef struct ma_resource_manager_data_source      ma_resource_manager_data_source;\n\ntypedef enum\n{\n    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM         = 0x00000001,   /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */\n    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE         = 0x00000002,   /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */\n    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC          = 0x00000004,   /* When set, the resource manager will load the data source asynchronously. */\n    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT      = 0x00000008,   /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */\n    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010    /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */\n} ma_resource_manager_data_source_flags;\n\n\n/*\nPipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional.\n*/\ntypedef struct\n{\n    ma_async_notification* pNotification;\n    ma_fence* pFence;\n} ma_resource_manager_pipeline_stage_notification;\n\ntypedef struct\n{\n    ma_resource_manager_pipeline_stage_notification init;    /* Initialization of the decoder. */\n    ma_resource_manager_pipeline_stage_notification done;    /* Decoding fully completed. */\n} ma_resource_manager_pipeline_notifications;\n\nMA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void);\n\n\n\n/* BEGIN BACKWARDS COMPATIBILITY */\n/* TODO: Remove this block in version 0.12. */\n#if 1\n#define ma_resource_manager_job                         ma_job\n#define ma_resource_manager_job_init                    ma_job_init\n#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING\n#define ma_resource_manager_job_queue_config            ma_job_queue_config\n#define ma_resource_manager_job_queue_config_init       ma_job_queue_config_init\n#define ma_resource_manager_job_queue                   ma_job_queue\n#define ma_resource_manager_job_queue_get_heap_size     ma_job_queue_get_heap_size\n#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated\n#define ma_resource_manager_job_queue_init              ma_job_queue_init\n#define ma_resource_manager_job_queue_uninit            ma_job_queue_uninit\n#define ma_resource_manager_job_queue_post              ma_job_queue_post\n#define ma_resource_manager_job_queue_next              ma_job_queue_next\n#endif\n/* END BACKWARDS COMPATIBILITY */\n\n\n\n\n/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */\n#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT\n#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT    64\n#endif\n\ntypedef enum\n{\n    /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */\n    MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001,\n\n    /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */\n    MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002\n} ma_resource_manager_flags;\n\ntypedef struct\n{\n    const char* pFilePath;\n    const wchar_t* pFilePathW;\n    const ma_resource_manager_pipeline_notifications* pNotifications;\n    ma_uint64 initialSeekPointInPCMFrames;\n    ma_uint64 rangeBegInPCMFrames;\n    ma_uint64 rangeEndInPCMFrames;\n    ma_uint64 loopPointBegInPCMFrames;\n    ma_uint64 loopPointEndInPCMFrames;\n    ma_bool32 isLooping;\n    ma_uint32 flags;\n} ma_resource_manager_data_source_config;\n\nMA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void);\n\n\ntypedef enum\n{\n    ma_resource_manager_data_supply_type_unknown = 0,   /* Used for determining whether or the data supply has been initialized. */\n    ma_resource_manager_data_supply_type_encoded,       /* Data supply is an encoded buffer. Connector is ma_decoder. */\n    ma_resource_manager_data_supply_type_decoded,       /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */\n    ma_resource_manager_data_supply_type_decoded_paged  /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */\n} ma_resource_manager_data_supply_type;\n\ntypedef struct\n{\n    MA_ATOMIC(4, ma_resource_manager_data_supply_type) type;    /* Read and written from different threads so needs to be accessed atomically. */\n    union\n    {\n        struct\n        {\n            const void* pData;\n            size_t sizeInBytes;\n        } encoded;\n        struct\n        {\n            const void* pData;\n            ma_uint64 totalFrameCount;\n            ma_uint64 decodedFrameCount;\n            ma_format format;\n            ma_uint32 channels;\n            ma_uint32 sampleRate;\n        } decoded;\n        struct\n        {\n            ma_paged_audio_buffer_data data;\n            ma_uint64 decodedFrameCount;\n            ma_uint32 sampleRate;\n        } decodedPaged;\n    } backend;\n} ma_resource_manager_data_supply;\n\nstruct ma_resource_manager_data_buffer_node\n{\n    ma_uint32 hashedName32;                         /* The hashed name. This is the key. */\n    ma_uint32 refCount;\n    MA_ATOMIC(4, ma_result) result;                 /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */\n    MA_ATOMIC(4, ma_uint32) executionCounter;       /* For allocating execution orders for jobs. */\n    MA_ATOMIC(4, ma_uint32) executionPointer;       /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */\n    ma_bool32 isDataOwnedByResourceManager;         /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */\n    ma_resource_manager_data_supply data;\n    ma_resource_manager_data_buffer_node* pParent;\n    ma_resource_manager_data_buffer_node* pChildLo;\n    ma_resource_manager_data_buffer_node* pChildHi;\n};\n\nstruct ma_resource_manager_data_buffer\n{\n    ma_data_source_base ds;                         /* Base data source. A data buffer is a data source. */\n    ma_resource_manager* pResourceManager;          /* A pointer to the resource manager that owns this buffer. */\n    ma_resource_manager_data_buffer_node* pNode;    /* The data node. This is reference counted and is what supplies the data. */\n    ma_uint32 flags;                                /* The flags that were passed used to initialize the buffer. */\n    MA_ATOMIC(4, ma_uint32) executionCounter;       /* For allocating execution orders for jobs. */\n    MA_ATOMIC(4, ma_uint32) executionPointer;       /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */\n    ma_uint64 seekTargetInPCMFrames;                /* Only updated by the public API. Never written nor read from the job thread. */\n    ma_bool32 seekToCursorOnNextRead;               /* On the next read we need to seek to the frame cursor. */\n    MA_ATOMIC(4, ma_result) result;                 /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */\n    MA_ATOMIC(4, ma_bool32) isLooping;              /* Can be read and written by different threads at the same time. Must be used atomically. */\n    ma_atomic_bool32 isConnectorInitialized;        /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */\n    union\n    {\n        ma_decoder decoder;                 /* Supply type is ma_resource_manager_data_supply_type_encoded */\n        ma_audio_buffer buffer;             /* Supply type is ma_resource_manager_data_supply_type_decoded */\n        ma_paged_audio_buffer pagedBuffer;  /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */\n    } connector;    /* Connects this object to the node's data supply. */\n};\n\nstruct ma_resource_manager_data_stream\n{\n    ma_data_source_base ds;                     /* Base data source. A data stream is a data source. */\n    ma_resource_manager* pResourceManager;      /* A pointer to the resource manager that owns this data stream. */\n    ma_uint32 flags;                            /* The flags that were passed used to initialize the stream. */\n    ma_decoder decoder;                         /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */\n    ma_bool32 isDecoderInitialized;             /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */\n    ma_uint64 totalLengthInPCMFrames;           /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */\n    ma_uint32 relativeCursor;                   /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */\n    MA_ATOMIC(8, ma_uint64) absoluteCursor;     /* The playback cursor, in absolute position starting from the start of the file. */\n    ma_uint32 currentPageIndex;                 /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */\n    MA_ATOMIC(4, ma_uint32) executionCounter;   /* For allocating execution orders for jobs. */\n    MA_ATOMIC(4, ma_uint32) executionPointer;   /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */\n\n    /* Written by the public API, read by the job thread. */\n    MA_ATOMIC(4, ma_bool32) isLooping;          /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */\n\n    /* Written by the job thread, read by the public API. */\n    void* pPageData;                            /* Buffer containing the decoded data of each page. Allocated once at initialization time. */\n    MA_ATOMIC(4, ma_uint32) pageFrameCount[2];  /* The number of valid PCM frames in each page. Used to determine the last valid frame. */\n\n    /* Written and read by both the public API and the job thread. These must be atomic. */\n    MA_ATOMIC(4, ma_result) result;             /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */\n    MA_ATOMIC(4, ma_bool32) isDecoderAtEnd;     /* Whether or not the decoder has reached the end. */\n    MA_ATOMIC(4, ma_bool32) isPageValid[2];     /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */\n    MA_ATOMIC(4, ma_bool32) seekCounter;        /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */\n};\n\nstruct ma_resource_manager_data_source\n{\n    union\n    {\n        ma_resource_manager_data_buffer buffer;\n        ma_resource_manager_data_stream stream;\n    } backend;  /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */\n\n    ma_uint32 flags;                          /* The flags that were passed in to ma_resource_manager_data_source_init(). */\n    MA_ATOMIC(4, ma_uint32) executionCounter;     /* For allocating execution orders for jobs. */\n    MA_ATOMIC(4, ma_uint32) executionPointer;     /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */\n};\n\ntypedef struct\n{\n    ma_allocation_callbacks allocationCallbacks;\n    ma_log* pLog;\n    ma_format decodedFormat;        /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */\n    ma_uint32 decodedChannels;      /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */\n    ma_uint32 decodedSampleRate;    /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */\n    ma_uint32 jobThreadCount;       /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */\n    size_t jobThreadStackSize;\n    ma_uint32 jobQueueCapacity;     /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */\n    ma_uint32 flags;\n    ma_vfs* pVFS;                   /* Can be NULL in which case defaults will be used. */\n    ma_decoding_backend_vtable** ppCustomDecodingBackendVTables;\n    ma_uint32 customDecodingBackendCount;\n    void* pCustomDecodingBackendUserData;\n} ma_resource_manager_config;\n\nMA_API ma_resource_manager_config ma_resource_manager_config_init(void);\n\nstruct ma_resource_manager\n{\n    ma_resource_manager_config config;\n    ma_resource_manager_data_buffer_node* pRootDataBufferNode;      /* The root buffer in the binary tree. */\n#ifndef MA_NO_THREADING\n    ma_mutex dataBufferBSTLock;                                     /* For synchronizing access to the data buffer binary tree. */\n    ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */\n#endif\n    ma_job_queue jobQueue;                                          /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */\n    ma_default_vfs defaultVFS;                                      /* Only used if a custom VFS is not specified. */\n    ma_log log;                                                     /* Only used if no log was specified in the config. */\n};\n\n/* Init. */\nMA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager);\nMA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager);\nMA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager);\n\n/* Registration. */\nMA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags);\nMA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags);\nMA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);  /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */\nMA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);\nMA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes);    /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */\nMA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes);\nMA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath);\nMA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath);\nMA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName);\nMA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName);\n\n/* Data Buffers. */\nMA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer);\nMA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer);\nMA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer);\nMA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer);\nMA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer);\nMA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex);\nMA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor);\nMA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength);\nMA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer);\nMA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping);\nMA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer);\nMA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames);\n\n/* Data Streams. */\nMA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream);\nMA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream);\nMA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream);\nMA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream);\nMA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex);\nMA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor);\nMA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength);\nMA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream);\nMA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping);\nMA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream);\nMA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames);\n\n/* Data Sources. */\nMA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource);\nMA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);\nMA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);\nMA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource);\nMA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource);\nMA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex);\nMA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor);\nMA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength);\nMA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource);\nMA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping);\nMA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource);\nMA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames);\n\n/* Job management. */\nMA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob);\nMA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager);  /* Helper for posting a quit job. */\nMA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob);\nMA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob);  /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */\nMA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager);   /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */\n#endif  /* MA_NO_RESOURCE_MANAGER */\n\n\n\n/************************************************************************************************************************************************************\n\nNode Graph\n\n************************************************************************************************************************************************************/\n#ifndef MA_NO_NODE_GRAPH\n/* Must never exceed 254. */\n#ifndef MA_MAX_NODE_BUS_COUNT\n#define MA_MAX_NODE_BUS_COUNT       254\n#endif\n\n/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */\n#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT\n#define MA_MAX_NODE_LOCAL_BUS_COUNT 2\n#endif\n\n/* Use this when the bus count is determined by the node instance rather than the vtable. */\n#define MA_NODE_BUS_COUNT_UNKNOWN   255\n\ntypedef struct ma_node_graph ma_node_graph;\ntypedef void ma_node;\n\n\n/* Node flags. */\ntypedef enum\n{\n    MA_NODE_FLAG_PASSTHROUGH                = 0x00000001,\n    MA_NODE_FLAG_CONTINUOUS_PROCESSING      = 0x00000002,\n    MA_NODE_FLAG_ALLOW_NULL_INPUT           = 0x00000004,\n    MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008,\n    MA_NODE_FLAG_SILENT_OUTPUT              = 0x00000010\n} ma_node_flags;\n\n\n/* The playback state of a node. Either started or stopped. */\ntypedef enum\n{\n    ma_node_state_started = 0,\n    ma_node_state_stopped = 1\n} ma_node_state;\n\n\ntypedef struct\n{\n    /*\n    Extended processing callback. This callback is used for effects that process input and output\n    at different rates (i.e. they perform resampling). This is similar to the simple version, only\n    they take two separate frame counts: one for input, and one for output.\n\n    On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas\n    `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`.\n\n    On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set\n    `pFrameCountIn` to the number of input frames that were consumed.\n    */\n    void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);\n\n    /*\n    A callback for retrieving the number of a input frames that are required to output the\n    specified number of output frames. You would only want to implement this when the node performs\n    resampling. This is optional, even for nodes that perform resampling, but it does offer a\n    small reduction in latency as it allows miniaudio to calculate the exact number of input frames\n    to read at a time instead of having to estimate.\n    */\n    ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount);\n\n    /*\n    The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn`\n    parameters of the callbacks above.\n    */\n    ma_uint8 inputBusCount;\n\n    /*\n    The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut`\n    parameters of the callbacks above.\n    */\n    ma_uint8 outputBusCount;\n\n    /*\n    Flags describing characteristics of the node. This is currently just a placeholder for some\n    ideas for later on.\n    */\n    ma_uint32 flags;\n} ma_node_vtable;\n\ntypedef struct\n{\n    const ma_node_vtable* vtable;       /* Should never be null. Initialization of the node will fail if so. */\n    ma_node_state initialState;         /* Defaults to ma_node_state_started. */\n    ma_uint32 inputBusCount;            /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */\n    ma_uint32 outputBusCount;           /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise  be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */\n    const ma_uint32* pInputChannels;    /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */\n    const ma_uint32* pOutputChannels;   /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */\n} ma_node_config;\n\nMA_API ma_node_config ma_node_config_init(void);\n\n\n/*\nA node has multiple output buses. An output bus is attached to an input bus as an item in a linked\nlist. Think of the input bus as a linked list, with the output bus being an item in that list.\n*/\ntypedef struct ma_node_output_bus ma_node_output_bus;\nstruct ma_node_output_bus\n{\n    /* Immutable. */\n    ma_node* pNode;                                         /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */\n    ma_uint8 outputBusIndex;                                /* The index of the output bus on pNode that this output bus represents. */\n    ma_uint8 channels;                                      /* The number of channels in the audio stream for this bus. */\n\n    /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */\n    ma_uint8 inputNodeInputBusIndex;                        /* The index of the input bus on the input. Required for detaching. Will only be used within the spinlock so does not need to be atomic. */\n    MA_ATOMIC(4, ma_uint32) flags;                          /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */\n    MA_ATOMIC(4, ma_uint32) refCount;                       /* Reference count for some thread-safety when detaching. */\n    MA_ATOMIC(4, ma_bool32) isAttached;                     /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */\n    MA_ATOMIC(4, ma_spinlock) lock;                         /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */\n    MA_ATOMIC(4, float) volume;                             /* Linear. */\n    MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext;    /* If null, it's the tail node or detached. */\n    MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev;    /* If null, it's the head node or detached. */\n    MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode;          /* The node that this output bus is attached to. Required for detaching. */\n};\n\n/*\nA node has multiple input buses. The output buses of a node are connecting to the input busses of\nanother. An input bus is essentially just a linked list of output buses.\n*/\ntypedef struct ma_node_input_bus ma_node_input_bus;\nstruct ma_node_input_bus\n{\n    /* Mutable via multiple threads. */\n    ma_node_output_bus head;                /* Dummy head node for simplifying some lock-free thread-safety stuff. */\n    MA_ATOMIC(4, ma_uint32) nextCounter;    /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */\n    MA_ATOMIC(4, ma_spinlock) lock;         /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */\n\n    /* Set once at startup. */\n    ma_uint8 channels;                      /* The number of channels in the audio stream for this bus. */\n};\n\n\ntypedef struct ma_node_base ma_node_base;\nstruct ma_node_base\n{\n    /* These variables are set once at startup. */\n    ma_node_graph* pNodeGraph;              /* The graph this node belongs to. */\n    const ma_node_vtable* vtable;\n    float* pCachedData;                     /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */\n    ma_uint16 cachedDataCapInFramesPerBus;  /* The capacity of the input data cache in frames, per bus. */\n\n    /* These variables are read and written only from the audio thread. */\n    ma_uint16 cachedFrameCountOut;\n    ma_uint16 cachedFrameCountIn;\n    ma_uint16 consumedFrameCountIn;\n\n    /* These variables are read and written between different threads. */\n    MA_ATOMIC(4, ma_node_state) state;      /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */\n    MA_ATOMIC(8, ma_uint64) stateTimes[2];  /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */\n    MA_ATOMIC(8, ma_uint64) localTime;      /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */\n    ma_uint32 inputBusCount;\n    ma_uint32 outputBusCount;\n    ma_node_input_bus* pInputBuses;\n    ma_node_output_bus* pOutputBuses;\n\n    /* Memory management. */\n    ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];\n    ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];\n    void* _pHeap;   /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */\n    ma_bool32 _ownsHeap;    /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */\n};\n\nMA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode);\nMA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode);\nMA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode);\nMA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode);\nMA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode);\nMA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex);\nMA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex);\nMA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex);\nMA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex);\nMA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode);\nMA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume);\nMA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex);\nMA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state);\nMA_API ma_node_state ma_node_get_state(const ma_node* pNode);\nMA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime);\nMA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state);\nMA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime);\nMA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd);\nMA_API ma_uint64 ma_node_get_time(const ma_node* pNode);\nMA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime);\n\n\ntypedef struct\n{\n    ma_uint32 channels;\n    ma_uint16 nodeCacheCapInFrames;\n} ma_node_graph_config;\n\nMA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels);\n\n\nstruct ma_node_graph\n{\n    /* Immutable. */\n    ma_node_base base;                  /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */\n    ma_node_base endpoint;              /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */\n    ma_uint16 nodeCacheCapInFrames;\n\n    /* Read and written by multiple threads. */\n    MA_ATOMIC(4, ma_bool32) isReading;\n};\n\nMA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph);\nMA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph);\nMA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph);\nMA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph);\nMA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime);\n\n\n\n/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */\ntypedef struct\n{\n    ma_node_config nodeConfig;\n    ma_data_source* pDataSource;\n} ma_data_source_node_config;\n\nMA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource);\n\n\ntypedef struct\n{\n    ma_node_base base;\n    ma_data_source* pDataSource;\n} ma_data_source_node;\n\nMA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode);\nMA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping);\nMA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode);\n\n\n/* Splitter Node. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */\ntypedef struct\n{\n    ma_node_config nodeConfig;\n    ma_uint32 channels;\n    ma_uint32 outputBusCount;\n} ma_splitter_node_config;\n\nMA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels);\n\n\ntypedef struct\n{\n    ma_node_base base;\n} ma_splitter_node;\n\nMA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode);\nMA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\n/*\nBiquad Node\n*/\ntypedef struct\n{\n    ma_node_config nodeConfig;\n    ma_biquad_config biquad;\n} ma_biquad_node_config;\n\nMA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2);\n\n\ntypedef struct\n{\n    ma_node_base baseNode;\n    ma_biquad biquad;\n} ma_biquad_node;\n\nMA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode);\nMA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode);\nMA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\n/*\nLow Pass Filter Node\n*/\ntypedef struct\n{\n    ma_node_config nodeConfig;\n    ma_lpf_config lpf;\n} ma_lpf_node_config;\n\nMA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);\n\n\ntypedef struct\n{\n    ma_node_base baseNode;\n    ma_lpf lpf;\n} ma_lpf_node;\n\nMA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode);\nMA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode);\nMA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\n/*\nHigh Pass Filter Node\n*/\ntypedef struct\n{\n    ma_node_config nodeConfig;\n    ma_hpf_config hpf;\n} ma_hpf_node_config;\n\nMA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);\n\n\ntypedef struct\n{\n    ma_node_base baseNode;\n    ma_hpf hpf;\n} ma_hpf_node;\n\nMA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode);\nMA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode);\nMA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\n/*\nBand Pass Filter Node\n*/\ntypedef struct\n{\n    ma_node_config nodeConfig;\n    ma_bpf_config bpf;\n} ma_bpf_node_config;\n\nMA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);\n\n\ntypedef struct\n{\n    ma_node_base baseNode;\n    ma_bpf bpf;\n} ma_bpf_node;\n\nMA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode);\nMA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode);\nMA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\n/*\nNotching Filter Node\n*/\ntypedef struct\n{\n    ma_node_config nodeConfig;\n    ma_notch_config notch;\n} ma_notch_node_config;\n\nMA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);\n\n\ntypedef struct\n{\n    ma_node_base baseNode;\n    ma_notch2 notch;\n} ma_notch_node;\n\nMA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode);\nMA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode);\nMA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\n/*\nPeaking Filter Node\n*/\ntypedef struct\n{\n    ma_node_config nodeConfig;\n    ma_peak_config peak;\n} ma_peak_node_config;\n\nMA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);\n\n\ntypedef struct\n{\n    ma_node_base baseNode;\n    ma_peak2 peak;\n} ma_peak_node;\n\nMA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode);\nMA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode);\nMA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\n/*\nLow Shelf Filter Node\n*/\ntypedef struct\n{\n    ma_node_config nodeConfig;\n    ma_loshelf_config loshelf;\n} ma_loshelf_node_config;\n\nMA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);\n\n\ntypedef struct\n{\n    ma_node_base baseNode;\n    ma_loshelf2 loshelf;\n} ma_loshelf_node;\n\nMA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode);\nMA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode);\nMA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\n/*\nHigh Shelf Filter Node\n*/\ntypedef struct\n{\n    ma_node_config nodeConfig;\n    ma_hishelf_config hishelf;\n} ma_hishelf_node_config;\n\nMA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);\n\n\ntypedef struct\n{\n    ma_node_base baseNode;\n    ma_hishelf2 hishelf;\n} ma_hishelf_node;\n\nMA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode);\nMA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode);\nMA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\ntypedef struct\n{\n    ma_node_config nodeConfig;\n    ma_delay_config delay;\n} ma_delay_node_config;\n\nMA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay);\n\n\ntypedef struct\n{\n    ma_node_base baseNode;\n    ma_delay delay;\n} ma_delay_node;\n\nMA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode);\nMA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value);\nMA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode);\nMA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value);\nMA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode);\nMA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value);\nMA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode);\n#endif  /* MA_NO_NODE_GRAPH */\n\n\n/* SECTION: miniaudio_engine.h */\n/************************************************************************************************************************************************************\n\nEngine\n\n************************************************************************************************************************************************************/\n#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)\ntypedef struct ma_engine ma_engine;\ntypedef struct ma_sound  ma_sound;\n\n\n/* Sound flags. */\ntypedef enum\n{\n    /* Resource manager flags. */\n    MA_SOUND_FLAG_STREAM                = 0x00000001,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */\n    MA_SOUND_FLAG_DECODE                = 0x00000002,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */\n    MA_SOUND_FLAG_ASYNC                 = 0x00000004,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */\n    MA_SOUND_FLAG_WAIT_INIT             = 0x00000008,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */\n    MA_SOUND_FLAG_UNKNOWN_LENGTH        = 0x00000010,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */\n\n    /* ma_sound specific flags. */\n    MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000,   /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */\n    MA_SOUND_FLAG_NO_PITCH              = 0x00002000,   /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */\n    MA_SOUND_FLAG_NO_SPATIALIZATION     = 0x00004000    /* Disable spatialization. */\n} ma_sound_flags;\n\n#ifndef MA_ENGINE_MAX_LISTENERS\n#define MA_ENGINE_MAX_LISTENERS             4\n#endif\n\n#define MA_LISTENER_INDEX_CLOSEST           ((ma_uint8)-1)\n\ntypedef enum\n{\n    ma_engine_node_type_sound,\n    ma_engine_node_type_group\n} ma_engine_node_type;\n\ntypedef struct\n{\n    ma_engine* pEngine;\n    ma_engine_node_type type;\n    ma_uint32 channelsIn;\n    ma_uint32 channelsOut;\n    ma_uint32 sampleRate;               /* Only used when the type is set to ma_engine_node_type_sound. */\n    ma_uint32 volumeSmoothTimeInPCMFrames;  /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */\n    ma_mono_expansion_mode monoExpansionMode;\n    ma_bool8 isPitchDisabled;           /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */\n    ma_bool8 isSpatializationDisabled;  /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */\n    ma_uint8 pinnedListenerIndex;       /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */\n} ma_engine_node_config;\n\nMA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags);\n\n\n/* Base node object for both ma_sound and ma_sound_group. */\ntypedef struct\n{\n    ma_node_base baseNode;                              /* Must be the first member for compatiblity with the ma_node API. */\n    ma_engine* pEngine;                                 /* A pointer to the engine. Set based on the value from the config. */\n    ma_uint32 sampleRate;                               /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */\n    ma_uint32 volumeSmoothTimeInPCMFrames;\n    ma_mono_expansion_mode monoExpansionMode;\n    ma_fader fader;\n    ma_linear_resampler resampler;                      /* For pitch shift. */\n    ma_spatializer spatializer;\n    ma_panner panner;\n    ma_gainer volumeGainer;                             /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */\n    ma_atomic_float volume;                             /* Defaults to 1. */\n    MA_ATOMIC(4, float) pitch;\n    float oldPitch;                                     /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */\n    float oldDopplerPitch;                              /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */\n    MA_ATOMIC(4, ma_bool32) isPitchDisabled;            /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */\n    MA_ATOMIC(4, ma_bool32) isSpatializationDisabled;   /* Set to false by default. When set to false, will not have spatialisation applied. */\n    MA_ATOMIC(4, ma_uint32) pinnedListenerIndex;        /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */\n\n    /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */\n    struct\n    {\n        ma_atomic_float volumeBeg;\n        ma_atomic_float volumeEnd;\n        ma_atomic_uint64 fadeLengthInFrames;            /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */\n        ma_atomic_uint64 absoluteGlobalTimeInFrames;    /* <-- The time to start the fade. */\n    } fadeSettings;\n\n    /* Memory management. */\n    ma_bool8 _ownsHeap;\n    void* _pHeap;\n} ma_engine_node;\n\nMA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes);\nMA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode);\nMA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode);\nMA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks);\n\n\n#define MA_SOUND_SOURCE_CHANNEL_COUNT   0xFFFFFFFF\n\n/* Callback for when a sound reaches the end. */\ntypedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound);\n\ntypedef struct\n{\n    const char* pFilePath;                      /* Set this to load from the resource manager. */\n    const wchar_t* pFilePathW;                  /* Set this to load from the resource manager. */\n    ma_data_source* pDataSource;                /* Set this to load from an existing data source. */\n    ma_node* pInitialAttachment;                /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */\n    ma_uint32 initialAttachmentInputBusIndex;   /* The index of the input bus of pInitialAttachment to attach the sound to. */\n    ma_uint32 channelsIn;                       /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */\n    ma_uint32 channelsOut;                      /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */\n    ma_mono_expansion_mode monoExpansionMode;   /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */\n    ma_uint32 flags;                            /* A combination of MA_SOUND_FLAG_* flags. */\n    ma_uint32 volumeSmoothTimeInPCMFrames;      /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */\n    ma_uint64 initialSeekPointInPCMFrames;      /* Initializes the sound such that it's seeked to this location by default. */\n    ma_uint64 rangeBegInPCMFrames;\n    ma_uint64 rangeEndInPCMFrames;\n    ma_uint64 loopPointBegInPCMFrames;\n    ma_uint64 loopPointEndInPCMFrames;\n    ma_bool32 isLooping;\n    ma_sound_end_proc endCallback;              /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */\n    void* pEndCallbackUserData;\n#ifndef MA_NO_RESOURCE_MANAGER\n    ma_resource_manager_pipeline_notifications initNotifications;\n#endif\n    ma_fence* pDoneFence;                       /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */\n} ma_sound_config;\n\nMA_API ma_sound_config ma_sound_config_init(void);                  /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */\nMA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine);  /* Will be renamed to ma_sound_config_init() in version 0.12. */\n\nstruct ma_sound\n{\n    ma_engine_node engineNode;          /* Must be the first member for compatibility with the ma_node API. */\n    ma_data_source* pDataSource;\n    MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */\n    MA_ATOMIC(4, ma_bool32) atEnd;\n    ma_sound_end_proc endCallback;\n    void* pEndCallbackUserData;\n    ma_bool8 ownsDataSource;\n\n    /*\n    We're declaring a resource manager data source object here to save us a malloc when loading a\n    sound via the resource manager, which I *think* will be the most common scenario.\n    */\n#ifndef MA_NO_RESOURCE_MANAGER\n    ma_resource_manager_data_source* pResourceManagerDataSource;\n#endif\n};\n\n/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */\ntypedef struct ma_sound_inlined ma_sound_inlined;\nstruct ma_sound_inlined\n{\n    ma_sound sound;\n    ma_sound_inlined* pNext;\n    ma_sound_inlined* pPrev;\n};\n\n/* A sound group is just a sound. */\ntypedef ma_sound_config ma_sound_group_config;\ntypedef ma_sound        ma_sound_group;\n\nMA_API ma_sound_group_config ma_sound_group_config_init(void);                  /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */\nMA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine);  /* Will be renamed to ma_sound_config_init() in version 0.12. */\n\ntypedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount);\n\ntypedef struct\n{\n#if !defined(MA_NO_RESOURCE_MANAGER)\n    ma_resource_manager* pResourceManager;          /* Can be null in which case a resource manager will be created for you. */\n#endif\n#if !defined(MA_NO_DEVICE_IO)\n    ma_context* pContext;\n    ma_device* pDevice;                             /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */\n    ma_device_id* pPlaybackDeviceID;                /* The ID of the playback device to use with the default listener. */\n    ma_device_data_proc dataCallback;               /* Can be null. Can be used to provide a custom device data callback. */\n    ma_device_notification_proc notificationCallback;\n#endif\n    ma_log* pLog;                                   /* When set to NULL, will use the context's log. */\n    ma_uint32 listenerCount;                        /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */\n    ma_uint32 channels;                             /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */\n    ma_uint32 sampleRate;                           /* The sample rate. When set to 0 will use the native channel count of the device. */\n    ma_uint32 periodSizeInFrames;                   /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/\n    ma_uint32 periodSizeInMilliseconds;             /* Used if periodSizeInFrames is unset. */\n    ma_uint32 gainSmoothTimeInFrames;               /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */\n    ma_uint32 gainSmoothTimeInMilliseconds;         /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */\n    ma_uint32 defaultVolumeSmoothTimeInPCMFrames;   /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */\n    ma_allocation_callbacks allocationCallbacks;\n    ma_bool32 noAutoStart;                          /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */\n    ma_bool32 noDevice;                             /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */\n    ma_mono_expansion_mode monoExpansionMode;       /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */\n    ma_vfs* pResourceManagerVFS;                    /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */\n    ma_engine_process_proc onProcess;               /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */\n    void* pProcessUserData;                         /* User data that's passed into onProcess. */\n} ma_engine_config;\n\nMA_API ma_engine_config ma_engine_config_init(void);\n\n\nstruct ma_engine\n{\n    ma_node_graph nodeGraph;                /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */\n#if !defined(MA_NO_RESOURCE_MANAGER)\n    ma_resource_manager* pResourceManager;\n#endif\n#if !defined(MA_NO_DEVICE_IO)\n    ma_device* pDevice;                     /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */\n#endif\n    ma_log* pLog;\n    ma_uint32 sampleRate;\n    ma_uint32 listenerCount;\n    ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS];\n    ma_allocation_callbacks allocationCallbacks;\n    ma_bool8 ownsResourceManager;\n    ma_bool8 ownsDevice;\n    ma_spinlock inlinedSoundLock;               /* For synchronizing access so the inlined sound list. */\n    ma_sound_inlined* pInlinedSoundHead;        /* The first inlined sound. Inlined sounds are tracked in a linked list. */\n    MA_ATOMIC(4, ma_uint32) inlinedSoundCount;  /* The total number of allocated inlined sound objects. Used for debugging. */\n    ma_uint32 gainSmoothTimeInFrames;           /* The number of frames to interpolate the gain of spatialized sounds across. */\n    ma_uint32 defaultVolumeSmoothTimeInPCMFrames;\n    ma_mono_expansion_mode monoExpansionMode;\n    ma_engine_process_proc onProcess;\n    void* pProcessUserData;\n};\n\nMA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine);\nMA_API void ma_engine_uninit(ma_engine* pEngine);\nMA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine);\n#if !defined(MA_NO_RESOURCE_MANAGER)\nMA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine);\n#endif\nMA_API ma_device* ma_engine_get_device(ma_engine* pEngine);\nMA_API ma_log* ma_engine_get_log(ma_engine* pEngine);\nMA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine);\nMA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine);\nMA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine);\nMA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime);\nMA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime);\nMA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine);                  /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */\nMA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime);  /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */\nMA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine);\nMA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine);\n\nMA_API ma_result ma_engine_start(ma_engine* pEngine);\nMA_API ma_result ma_engine_stop(ma_engine* pEngine);\nMA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume);\nMA_API float ma_engine_get_volume(ma_engine* pEngine);\nMA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB);\nMA_API float ma_engine_get_gain_db(ma_engine* pEngine);\n\nMA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine);\nMA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ);\nMA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);\nMA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex);\nMA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);\nMA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex);\nMA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);\nMA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex);\nMA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain);\nMA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);\nMA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);\nMA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex);\nMA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled);\nMA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex);\n\n#ifndef MA_NO_RESOURCE_MANAGER\nMA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex);\nMA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup);   /* Fire and forget. */\n#endif\n\n#ifndef MA_NO_RESOURCE_MANAGER\nMA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound);\nMA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound);\nMA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound);\n#endif\nMA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound);\nMA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound);\nMA_API void ma_sound_uninit(ma_sound* pSound);\nMA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound);\nMA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound);\nMA_API ma_result ma_sound_start(ma_sound* pSound);\nMA_API ma_result ma_sound_stop(ma_sound* pSound);\nMA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames);     /* Will overwrite any scheduled stop and fade. */\nMA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames);   /* Will overwrite any scheduled stop and fade. */\nMA_API void ma_sound_set_volume(ma_sound* pSound, float volume);\nMA_API float ma_sound_get_volume(const ma_sound* pSound);\nMA_API void ma_sound_set_pan(ma_sound* pSound, float pan);\nMA_API float ma_sound_get_pan(const ma_sound* pSound);\nMA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode);\nMA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound);\nMA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch);\nMA_API float ma_sound_get_pitch(const ma_sound* pSound);\nMA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled);\nMA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound);\nMA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex);\nMA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound);\nMA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound);\nMA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound);\nMA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z);\nMA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound);\nMA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z);\nMA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound);\nMA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z);\nMA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound);\nMA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel);\nMA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound);\nMA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning);\nMA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound);\nMA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff);\nMA_API float ma_sound_get_rolloff(const ma_sound* pSound);\nMA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain);\nMA_API float ma_sound_get_min_gain(const ma_sound* pSound);\nMA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain);\nMA_API float ma_sound_get_max_gain(const ma_sound* pSound);\nMA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance);\nMA_API float ma_sound_get_min_distance(const ma_sound* pSound);\nMA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance);\nMA_API float ma_sound_get_max_distance(const ma_sound* pSound);\nMA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain);\nMA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);\nMA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor);\nMA_API float ma_sound_get_doppler_factor(const ma_sound* pSound);\nMA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor);\nMA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound);\nMA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);\nMA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);\nMA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames);\nMA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds);\nMA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound);\nMA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);\nMA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);\nMA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);\nMA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);\nMA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames);\nMA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds);\nMA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound);\nMA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound);\nMA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound);\nMA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping);\nMA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound);\nMA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound);\nMA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */\nMA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor);\nMA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength);\nMA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor);\nMA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength);\nMA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData);\n\nMA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup);\nMA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup);\nMA_API void ma_sound_group_uninit(ma_sound_group* pGroup);\nMA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup);\nMA_API ma_result ma_sound_group_start(ma_sound_group* pGroup);\nMA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume);\nMA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan);\nMA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode);\nMA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch);\nMA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled);\nMA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex);\nMA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup);\nMA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup);\nMA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z);\nMA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z);\nMA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z);\nMA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel);\nMA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning);\nMA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff);\nMA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain);\nMA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain);\nMA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance);\nMA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance);\nMA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain);\nMA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);\nMA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor);\nMA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor);\nMA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);\nMA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);\nMA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup);\nMA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames);\nMA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);\nMA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames);\nMA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);\nMA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup);\nMA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup);\n#endif  /* MA_NO_ENGINE */\n/* END SECTION: miniaudio_engine.h */\n\n#ifdef __cplusplus\n}\n#endif\n#endif  /* miniaudio_h */\n\n\n/*\nThis is for preventing greying out of the implementation section.\n*/\n#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__)\n#define MINIAUDIO_IMPLEMENTATION\n#endif\n\n/************************************************************************************************************************************************************\n*************************************************************************************************************************************************************\n\nIMPLEMENTATION\n\n*************************************************************************************************************************************************************\n************************************************************************************************************************************************************/\n#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)\n#ifndef miniaudio_c\n#define miniaudio_c\n\n#include <assert.h>\n#include <limits.h>         /* For INT_MAX */\n#include <math.h>           /* sin(), etc. */\n#include <stdlib.h>         /* For malloc(), free(), wcstombs(). */\n#include <string.h>         /* For memset() */\n\n#include <stdarg.h>\n#include <stdio.h>\n#if !defined(_MSC_VER) && !defined(__DMC__)\n    #include <strings.h>    /* For strcasecmp(). */\n    #include <wchar.h>      /* For wcslen(), wcsrtombs() */\n#endif\n#ifdef _MSC_VER\n    #include <float.h>      /* For _controlfp_s constants */\n#endif\n\n#if defined(MA_WIN32)\n    #include <windows.h>\n\n    /*\n    There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols\n    such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're\n    unavailable.\n    */\n    #ifndef STGM_READ\n    #define STGM_READ   0x00000000L\n    #endif\n    #ifndef CLSCTX_ALL\n    #define CLSCTX_ALL  23\n    #endif\n\n    /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */\n    typedef struct ma_IUnknown  ma_IUnknown;\n#endif\n\n#if !defined(MA_WIN32)\n#include <sched.h>\n#include <sys/time.h>   /* select() (used for ma_sleep()). */\n#include <pthread.h>\n#endif\n\n#ifdef MA_NX\n#include <time.h>       /* For nanosleep() */\n#endif\n\n#include <sys/stat.h>   /* For fstat(), etc. */\n\n#ifdef MA_EMSCRIPTEN\n#include <emscripten/emscripten.h>\n#endif\n\n\n/* Architecture Detection */\n#if !defined(MA_64BIT) && !defined(MA_32BIT)\n#ifdef _WIN32\n#ifdef _WIN64\n#define MA_64BIT\n#else\n#define MA_32BIT\n#endif\n#endif\n#endif\n\n#if !defined(MA_64BIT) && !defined(MA_32BIT)\n#ifdef __GNUC__\n#ifdef __LP64__\n#define MA_64BIT\n#else\n#define MA_32BIT\n#endif\n#endif\n#endif\n\n#if !defined(MA_64BIT) && !defined(MA_32BIT)\n#include <stdint.h>\n#if INTPTR_MAX == INT64_MAX\n#define MA_64BIT\n#else\n#define MA_32BIT\n#endif\n#endif\n\n#if defined(__arm__) || defined(_M_ARM)\n#define MA_ARM32\n#endif\n#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)\n#define MA_ARM64\n#endif\n\n#if defined(__x86_64__) || defined(_M_X64)\n#define MA_X64\n#elif defined(__i386) || defined(_M_IX86)\n#define MA_X86\n#elif defined(MA_ARM32) || defined(MA_ARM64)\n#define MA_ARM\n#endif\n\n/* Intrinsics Support */\n#if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__)\n    #if defined(_MSC_VER) && !defined(__clang__)\n        /* MSVC. */\n        #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2)   /* 2005 */\n            #define MA_SUPPORT_SSE2\n        #endif\n        /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/    /* 2010 */\n        /*    #define MA_SUPPORT_AVX*/\n        /*#endif*/\n        #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2)   /* 2012 */\n            #define MA_SUPPORT_AVX2\n        #endif\n    #else\n        /* Assume GNUC-style. */\n        #if defined(__SSE2__) && !defined(MA_NO_SSE2)\n            #define MA_SUPPORT_SSE2\n        #endif\n        /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/\n        /*    #define MA_SUPPORT_AVX*/\n        /*#endif*/\n        #if defined(__AVX2__) && !defined(MA_NO_AVX2)\n            #define MA_SUPPORT_AVX2\n        #endif\n    #endif\n\n    /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */\n    #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)\n        #if !defined(MA_SUPPORT_SSE2)   && !defined(MA_NO_SSE2)   && __has_include(<emmintrin.h>)\n            #define MA_SUPPORT_SSE2\n        #endif\n        /*#if !defined(MA_SUPPORT_AVX)    && !defined(MA_NO_AVX)    && __has_include(<immintrin.h>)*/\n        /*    #define MA_SUPPORT_AVX*/\n        /*#endif*/\n        #if !defined(MA_SUPPORT_AVX2)   && !defined(MA_NO_AVX2)   && __has_include(<immintrin.h>)\n            #define MA_SUPPORT_AVX2\n        #endif\n    #endif\n\n    #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)\n        #include <immintrin.h>\n    #elif defined(MA_SUPPORT_SSE2)\n        #include <emmintrin.h>\n    #endif\n#endif\n\n#if defined(MA_ARM)\n    #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))\n        #define MA_SUPPORT_NEON\n        #include <arm_neon.h>\n    #endif\n#endif\n\n/* Begin globally disabled warnings. */\n#if defined(_MSC_VER)\n    #pragma warning(push)\n    #pragma warning(disable:4752)   /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */\n    #pragma warning(disable:4049)   /* compiler limit : terminating line number emission */\n#endif\n\n#if defined(MA_X64) || defined(MA_X86)\n    #if defined(_MSC_VER) && !defined(__clang__)\n        #if _MSC_VER >= 1400\n            #include <intrin.h>\n            static MA_INLINE void ma_cpuid(int info[4], int fid)\n            {\n                __cpuid(info, fid);\n            }\n        #else\n            #define MA_NO_CPUID\n        #endif\n\n        #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219)\n            static MA_INLINE unsigned __int64 ma_xgetbv(int reg)\n            {\n                return _xgetbv(reg);\n            }\n        #else\n            #define MA_NO_XGETBV\n        #endif\n    #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)\n        static MA_INLINE void ma_cpuid(int info[4], int fid)\n        {\n            /*\n            It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the\n            specific register of which I'm letting the compiler decide on. The \"k\" prefix is used to specify a 32-bit register. The {...} syntax is for\n            supporting different assembly dialects.\n\n            What's basically happening is that we're saving and restoring the ebx register manually.\n            */\n            #if defined(MA_X86) && defined(__PIC__)\n                __asm__ __volatile__ (\n                    \"xchg{l} {%%}ebx, %k1;\"\n                    \"cpuid;\"\n                    \"xchg{l} {%%}ebx, %k1;\"\n                    : \"=a\"(info[0]), \"=&r\"(info[1]), \"=c\"(info[2]), \"=d\"(info[3]) : \"a\"(fid), \"c\"(0)\n                );\n            #else\n                __asm__ __volatile__ (\n                    \"cpuid\" : \"=a\"(info[0]), \"=b\"(info[1]), \"=c\"(info[2]), \"=d\"(info[3]) : \"a\"(fid), \"c\"(0)\n                );\n            #endif\n        }\n\n        static MA_INLINE ma_uint64 ma_xgetbv(int reg)\n        {\n            unsigned int hi;\n            unsigned int lo;\n\n            __asm__ __volatile__ (\n                \"xgetbv\" : \"=a\"(lo), \"=d\"(hi) : \"c\"(reg)\n            );\n\n            return ((ma_uint64)hi << 32) | (ma_uint64)lo;\n        }\n    #else\n        #define MA_NO_CPUID\n        #define MA_NO_XGETBV\n    #endif\n#else\n    #define MA_NO_CPUID\n    #define MA_NO_XGETBV\n#endif\n\nstatic MA_INLINE ma_bool32 ma_has_sse2(void)\n{\n#if defined(MA_SUPPORT_SSE2)\n    #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)\n        #if defined(MA_X64)\n            return MA_TRUE;    /* 64-bit targets always support SSE2. */\n        #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)\n            return MA_TRUE;    /* If the compiler is allowed to freely generate SSE2 code we can assume support. */\n        #else\n            #if defined(MA_NO_CPUID)\n                return MA_FALSE;\n            #else\n                int info[4];\n                ma_cpuid(info, 1);\n                return (info[3] & (1 << 26)) != 0;\n            #endif\n        #endif\n    #else\n        return MA_FALSE;       /* SSE2 is only supported on x86 and x64 architectures. */\n    #endif\n#else\n    return MA_FALSE;           /* No compiler support. */\n#endif\n}\n\n#if 0\nstatic MA_INLINE ma_bool32 ma_has_avx()\n{\n#if defined(MA_SUPPORT_AVX)\n    #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX)\n        #if defined(_AVX_) || defined(__AVX__)\n            return MA_TRUE;    /* If the compiler is allowed to freely generate AVX code we can assume support. */\n        #else\n            /* AVX requires both CPU and OS support. */\n            #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)\n                return MA_FALSE;\n            #else\n                int info[4];\n                ma_cpuid(info, 1);\n                if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {\n                    ma_uint64 xrc = ma_xgetbv(0);\n                    if ((xrc & 0x06) == 0x06) {\n                        return MA_TRUE;\n                    } else {\n                        return MA_FALSE;\n                    }\n                } else {\n                    return MA_FALSE;\n                }\n            #endif\n        #endif\n    #else\n        return MA_FALSE;       /* AVX is only supported on x86 and x64 architectures. */\n    #endif\n#else\n    return MA_FALSE;           /* No compiler support. */\n#endif\n}\n#endif\n\nstatic MA_INLINE ma_bool32 ma_has_avx2(void)\n{\n#if defined(MA_SUPPORT_AVX2)\n    #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2)\n        #if defined(_AVX2_) || defined(__AVX2__)\n            return MA_TRUE;    /* If the compiler is allowed to freely generate AVX2 code we can assume support. */\n        #else\n            /* AVX2 requires both CPU and OS support. */\n            #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)\n                return MA_FALSE;\n            #else\n                int info1[4];\n                int info7[4];\n                ma_cpuid(info1, 1);\n                ma_cpuid(info7, 7);\n                if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) {\n                    ma_uint64 xrc = ma_xgetbv(0);\n                    if ((xrc & 0x06) == 0x06) {\n                        return MA_TRUE;\n                    } else {\n                        return MA_FALSE;\n                    }\n                } else {\n                    return MA_FALSE;\n                }\n            #endif\n        #endif\n    #else\n        return MA_FALSE;       /* AVX2 is only supported on x86 and x64 architectures. */\n    #endif\n#else\n    return MA_FALSE;           /* No compiler support. */\n#endif\n}\n\nstatic MA_INLINE ma_bool32 ma_has_neon(void)\n{\n#if defined(MA_SUPPORT_NEON)\n    #if defined(MA_ARM) && !defined(MA_NO_NEON)\n        #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))\n            return MA_TRUE;    /* If the compiler is allowed to freely generate NEON code we can assume support. */\n        #else\n            /* TODO: Runtime check. */\n            return MA_FALSE;\n        #endif\n    #else\n        return MA_FALSE;       /* NEON is only supported on ARM architectures. */\n    #endif\n#else\n    return MA_FALSE;           /* No compiler support. */\n#endif\n}\n\n#if defined(__has_builtin)\n    #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x)\n#else\n    #define MA_COMPILER_HAS_BUILTIN(x) 0\n#endif\n\n#ifndef MA_ASSUME\n    #if MA_COMPILER_HAS_BUILTIN(__builtin_assume)\n        #define MA_ASSUME(x) __builtin_assume(x)\n    #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable)\n        #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0)\n    #elif defined(_MSC_VER)\n        #define MA_ASSUME(x) __assume(x)\n    #else\n        #define MA_ASSUME(x) (void)(x)\n    #endif\n#endif\n\n#ifndef MA_RESTRICT\n    #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER)\n        #define MA_RESTRICT __restrict\n    #else\n        #define MA_RESTRICT\n    #endif\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER >= 1400\n    #define MA_HAS_BYTESWAP16_INTRINSIC\n    #define MA_HAS_BYTESWAP32_INTRINSIC\n    #define MA_HAS_BYTESWAP64_INTRINSIC\n#elif defined(__clang__)\n    #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16)\n        #define MA_HAS_BYTESWAP16_INTRINSIC\n    #endif\n    #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32)\n        #define MA_HAS_BYTESWAP32_INTRINSIC\n    #endif\n    #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64)\n        #define MA_HAS_BYTESWAP64_INTRINSIC\n    #endif\n#elif defined(__GNUC__)\n    #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))\n        #define MA_HAS_BYTESWAP32_INTRINSIC\n        #define MA_HAS_BYTESWAP64_INTRINSIC\n    #endif\n    #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))\n        #define MA_HAS_BYTESWAP16_INTRINSIC\n    #endif\n#endif\n\n\nstatic MA_INLINE ma_bool32 ma_is_little_endian(void)\n{\n#if defined(MA_X86) || defined(MA_X64)\n    return MA_TRUE;\n#else\n    int n = 1;\n    return (*(char*)&n) == 1;\n#endif\n}\n\nstatic MA_INLINE ma_bool32 ma_is_big_endian(void)\n{\n    return !ma_is_little_endian();\n}\n\n\nstatic MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n)\n{\n#ifdef MA_HAS_BYTESWAP32_INTRINSIC\n    #if defined(_MSC_VER)\n        return _byteswap_ulong(n);\n    #elif defined(__GNUC__) || defined(__clang__)\n        #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT)   /* <-- 64-bit inline assembly has not been tested, so disabling for now. */\n            /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */\n            ma_uint32 r;\n            __asm__ __volatile__ (\n            #if defined(MA_64BIT)\n                \"rev %w[out], %w[in]\" : [out]\"=r\"(r) : [in]\"r\"(n)   /* <-- This is untested. If someone in the community could test this, that would be appreciated! */\n            #else\n                \"rev %[out], %[in]\" : [out]\"=r\"(r) : [in]\"r\"(n)\n            #endif\n            );\n            return r;\n        #else\n            return __builtin_bswap32(n);\n        #endif\n    #else\n        #error \"This compiler does not support the byte swap intrinsic.\"\n    #endif\n#else\n    return ((n & 0xFF000000) >> 24) |\n           ((n & 0x00FF0000) >>  8) |\n           ((n & 0x0000FF00) <<  8) |\n           ((n & 0x000000FF) << 24);\n#endif\n}\n\n\n#if !defined(MA_EMSCRIPTEN)\n#ifdef MA_WIN32\nstatic void ma_sleep__win32(ma_uint32 milliseconds)\n{\n    Sleep((DWORD)milliseconds);\n}\n#endif\n#ifdef MA_POSIX\nstatic void ma_sleep__posix(ma_uint32 milliseconds)\n{\n#ifdef MA_EMSCRIPTEN\n    (void)milliseconds;\n    MA_ASSERT(MA_FALSE);  /* The Emscripten build should never sleep. */\n#else\n    #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX)\n        struct timespec ts;\n        ts.tv_sec  = milliseconds / 1000;\n        ts.tv_nsec = milliseconds % 1000 * 1000000;\n        nanosleep(&ts, NULL);\n    #else\n        struct timeval tv;\n        tv.tv_sec  = milliseconds / 1000;\n        tv.tv_usec = milliseconds % 1000 * 1000;\n        select(0, NULL, NULL, NULL, &tv);\n    #endif\n#endif\n}\n#endif\n\nstatic MA_INLINE void ma_sleep(ma_uint32 milliseconds)\n{\n#ifdef MA_WIN32\n    ma_sleep__win32(milliseconds);\n#endif\n#ifdef MA_POSIX\n    ma_sleep__posix(milliseconds);\n#endif\n}\n#endif\n\nstatic MA_INLINE void ma_yield(void)\n{\n#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)\n    /* x86/x64 */\n    #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__)\n        #if _MSC_VER >= 1400\n            _mm_pause();\n        #else\n            #if defined(__DMC__)\n                /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */\n                __asm nop;\n            #else\n                __asm pause;\n            #endif\n        #endif\n    #else\n        __asm__ __volatile__ (\"pause\");\n    #endif\n#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__)\n    /* ARM */\n    #if defined(_MSC_VER)\n        /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */\n        __yield();\n    #else\n        __asm__ __volatile__ (\"yield\"); /* ARMv6K/ARMv6T2 and above. */\n    #endif\n#else\n    /* Unknown or unsupported architecture. No-op. */\n#endif\n}\n\n\n#define MA_MM_DENORMALS_ZERO_MASK   0x0040\n#define MA_MM_FLUSH_ZERO_MASK       0x8000\n\nstatic MA_INLINE unsigned int ma_disable_denormals(void)\n{\n    unsigned int prevState;\n\n    #if defined(_MSC_VER)\n    {\n        /*\n        Older versions of Visual Studio don't support the \"safe\" versions of _controlfp_s(). I don't\n        know which version of Visual Studio first added support for _controlfp_s(), but I do know\n        that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older\n        versions of Visual Studio, let me know and I'll make the necessary adjustment.\n        */\n        #if _MSC_VER <= 1200\n        {\n            prevState = _statusfp();\n            _controlfp(prevState | _DN_FLUSH, _MCW_DN);\n        }\n        #else\n        {\n            unsigned int unused;\n            _controlfp_s(&prevState, 0, 0);\n            _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN);\n        }\n        #endif\n    }\n    #elif defined(MA_X86) || defined(MA_X64)\n    {\n        #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */\n        {\n            prevState = _mm_getcsr();\n            _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK);\n        }\n        #else\n        {\n            /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */\n            prevState = 0;\n        }\n        #endif\n    }\n    #else\n    {\n        /* Unknown or unsupported architecture. No-op. */\n        prevState = 0;\n    }\n    #endif\n\n    return prevState;\n}\n\nstatic MA_INLINE void ma_restore_denormals(unsigned int prevState)\n{\n    #if defined(_MSC_VER)\n    {\n        /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */\n        #if _MSC_VER <= 1200\n        {\n            _controlfp(prevState, _MCW_DN);\n        }\n        #else\n        {\n            unsigned int unused;\n            _controlfp_s(&unused, prevState, _MCW_DN);\n        }\n        #endif\n    }\n    #elif defined(MA_X86) || defined(MA_X64)\n    {\n        #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__))   /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */\n        {\n            _mm_setcsr(prevState);\n        }\n        #else\n        {\n            /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */\n            (void)prevState;\n        }\n        #endif\n    }\n    #else\n    {\n        /* Unknown or unsupported architecture. No-op. */\n        (void)prevState;\n    }\n    #endif\n}\n\n\n#ifdef MA_ANDROID\n#include <sys/system_properties.h>\n\nint ma_android_sdk_version()\n{\n    char sdkVersion[PROP_VALUE_MAX + 1] = {0, };\n    if (__system_property_get(\"ro.build.version.sdk\", sdkVersion)) {\n        return atoi(sdkVersion);\n    }\n\n    return 0;\n}\n#endif\n\n\n#ifndef MA_COINIT_VALUE\n#define MA_COINIT_VALUE    0   /* 0 = COINIT_MULTITHREADED */\n#endif\n\n\n#ifndef MA_FLT_MAX\n    #ifdef FLT_MAX\n        #define MA_FLT_MAX FLT_MAX\n    #else\n        #define MA_FLT_MAX 3.402823466e+38F\n    #endif\n#endif\n\n\n#ifndef MA_PI\n#define MA_PI      3.14159265358979323846264f\n#endif\n#ifndef MA_PI_D\n#define MA_PI_D    3.14159265358979323846264\n#endif\n#ifndef MA_TAU\n#define MA_TAU     6.28318530717958647693f\n#endif\n#ifndef MA_TAU_D\n#define MA_TAU_D   6.28318530717958647693\n#endif\n\n\n/* The default format when ma_format_unknown (0) is requested when initializing a device. */\n#ifndef MA_DEFAULT_FORMAT\n#define MA_DEFAULT_FORMAT                                   ma_format_f32\n#endif\n\n/* The default channel count to use when 0 is used when initializing a device. */\n#ifndef MA_DEFAULT_CHANNELS\n#define MA_DEFAULT_CHANNELS                                 2\n#endif\n\n/* The default sample rate to use when 0 is used when initializing a device. */\n#ifndef MA_DEFAULT_SAMPLE_RATE\n#define MA_DEFAULT_SAMPLE_RATE                              48000\n#endif\n\n/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */\n#ifndef MA_DEFAULT_PERIODS\n#define MA_DEFAULT_PERIODS                                  3\n#endif\n\n/* The default period size in milliseconds for low latency mode. */\n#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY\n#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY  10\n#endif\n\n/* The default buffer size in milliseconds for conservative mode. */\n#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE\n#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100\n#endif\n\n/* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */\n#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER\n    #if MA_MAX_FILTER_ORDER >= 4\n        #define MA_DEFAULT_RESAMPLER_LPF_ORDER  4\n    #else\n        #define MA_DEFAULT_RESAMPLER_LPF_ORDER  MA_MAX_FILTER_ORDER\n    #endif\n#endif\n\n\n#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wunused-variable\"\n#endif\n\n/* Standard sample rates, in order of priority. */\nstatic ma_uint32 g_maStandardSampleRatePriorities[] = {\n    (ma_uint32)ma_standard_sample_rate_48000,\n    (ma_uint32)ma_standard_sample_rate_44100,\n\n    (ma_uint32)ma_standard_sample_rate_32000,\n    (ma_uint32)ma_standard_sample_rate_24000,\n    (ma_uint32)ma_standard_sample_rate_22050,\n\n    (ma_uint32)ma_standard_sample_rate_88200,\n    (ma_uint32)ma_standard_sample_rate_96000,\n    (ma_uint32)ma_standard_sample_rate_176400,\n    (ma_uint32)ma_standard_sample_rate_192000,\n\n    (ma_uint32)ma_standard_sample_rate_16000,\n    (ma_uint32)ma_standard_sample_rate_11025,\n    (ma_uint32)ma_standard_sample_rate_8000,\n\n    (ma_uint32)ma_standard_sample_rate_352800,\n    (ma_uint32)ma_standard_sample_rate_384000\n};\n\nstatic MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate)\n{\n    ma_uint32 iSampleRate;\n\n    for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) {\n        if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) {\n            return MA_TRUE;\n        }\n    }\n\n    /* Getting here means the sample rate is not supported. */\n    return MA_FALSE;\n}\n\n\nstatic ma_format g_maFormatPriorities[] = {\n    ma_format_s16,         /* Most common */\n    ma_format_f32,\n\n    /*ma_format_s24_32,*/    /* Clean alignment */\n    ma_format_s32,\n\n    ma_format_s24,         /* Unclean alignment */\n\n    ma_format_u8           /* Low quality */\n};\n#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n    #pragma GCC diagnostic pop\n#endif\n\n\nMA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)\n{\n    if (pMajor) {\n        *pMajor = MA_VERSION_MAJOR;\n    }\n\n    if (pMinor) {\n        *pMinor = MA_VERSION_MINOR;\n    }\n\n    if (pRevision) {\n        *pRevision = MA_VERSION_REVISION;\n    }\n}\n\nMA_API const char* ma_version_string(void)\n{\n    return MA_VERSION_STRING;\n}\n\n\n/******************************************************************************\n\nStandard Library Stuff\n\n******************************************************************************/\n#ifndef MA_ASSERT\n#define MA_ASSERT(condition)            assert(condition)\n#endif\n\n#ifndef MA_MALLOC\n#define MA_MALLOC(sz)                   malloc((sz))\n#endif\n#ifndef MA_REALLOC\n#define MA_REALLOC(p, sz)               realloc((p), (sz))\n#endif\n#ifndef MA_FREE\n#define MA_FREE(p)                      free((p))\n#endif\n\nstatic MA_INLINE void ma_zero_memory_default(void* p, size_t sz)\n{\n    if (p == NULL) {\n        MA_ASSERT(sz == 0); /* If this is triggered there's an error with the calling code. */\n        return;\n    }\n\n    if (sz > 0) {\n        memset(p, 0, sz);\n    }\n}\n\n\n#ifndef MA_ZERO_MEMORY\n#define MA_ZERO_MEMORY(p, sz)           ma_zero_memory_default((p), (sz))\n#endif\n#ifndef MA_COPY_MEMORY\n#define MA_COPY_MEMORY(dst, src, sz)    memcpy((dst), (src), (sz))\n#endif\n#ifndef MA_MOVE_MEMORY\n#define MA_MOVE_MEMORY(dst, src, sz)    memmove((dst), (src), (sz))\n#endif\n\n#define MA_ZERO_OBJECT(p)               MA_ZERO_MEMORY((p), sizeof(*(p)))\n\n#define ma_countof(x)                   (sizeof(x) / sizeof(x[0]))\n#define ma_max(x, y)                    (((x) > (y)) ? (x) : (y))\n#define ma_min(x, y)                    (((x) < (y)) ? (x) : (y))\n#define ma_abs(x)                       (((x) > 0) ? (x) : -(x))\n#define ma_clamp(x, lo, hi)             (ma_max(lo, ma_min(x, hi)))\n#define ma_offset_ptr(p, offset)        (((ma_uint8*)(p)) + (offset))\n#define ma_align(x, a)                  (((x) + ((a)-1)) & ~((a)-1))\n#define ma_align_64(x)                  ma_align(x, 8)\n\n#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels))\n\nstatic MA_INLINE double ma_sind(double x)\n{\n    /* TODO: Implement custom sin(x). */\n    return sin(x);\n}\n\nstatic MA_INLINE double ma_expd(double x)\n{\n    /* TODO: Implement custom exp(x). */\n    return exp(x);\n}\n\nstatic MA_INLINE double ma_logd(double x)\n{\n    /* TODO: Implement custom log(x). */\n    return log(x);\n}\n\nstatic MA_INLINE double ma_powd(double x, double y)\n{\n    /* TODO: Implement custom pow(x, y). */\n    return pow(x, y);\n}\n\nstatic MA_INLINE double ma_sqrtd(double x)\n{\n    /* TODO: Implement custom sqrt(x). */\n    return sqrt(x);\n}\n\n\nstatic MA_INLINE float ma_rsqrtf(float x)\n{\n    #if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))\n    {\n        /*\n        For SSE we can use RSQRTSS.\n\n        This Stack Overflow post suggests that compilers don't necessarily generate optimal code\n        when using intrinsics:\n\n            https://web.archive.org/web/20221211012522/https://stackoverflow.com/questions/32687079/getting-fewest-instructions-for-rsqrtss-wrapper\n\n        I'm going to do something similar here, but a bit simpler.\n        */\n        #if defined(__GNUC__) || defined(__clang__)\n        {\n            float result;\n            __asm__ __volatile__(\"rsqrtss %1, %0\" : \"=x\"(result) : \"x\"(x));\n            return result;\n        }\n        #else\n        {\n            return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x)));\n        }\n        #endif\n    }\n    #else\n    {\n        return 1 / (float)ma_sqrtd(x);\n    }\n    #endif\n}\n\n\nstatic MA_INLINE float ma_sinf(float x)\n{\n    return (float)ma_sind((float)x);\n}\n\nstatic MA_INLINE double ma_cosd(double x)\n{\n    return ma_sind((MA_PI_D*0.5) - x);\n}\n\nstatic MA_INLINE float ma_cosf(float x)\n{\n    return (float)ma_cosd((float)x);\n}\n\nstatic MA_INLINE double ma_log10d(double x)\n{\n    return ma_logd(x) * 0.43429448190325182765;\n}\n\nstatic MA_INLINE float ma_powf(float x, float y)\n{\n    return (float)ma_powd((double)x, (double)y);\n}\n\nstatic MA_INLINE float ma_log10f(float x)\n{\n    return (float)ma_log10d((double)x);\n}\n\n\nstatic MA_INLINE double ma_degrees_to_radians(double degrees)\n{\n    return degrees * 0.01745329252;\n}\n\nstatic MA_INLINE double ma_radians_to_degrees(double radians)\n{\n    return radians * 57.295779512896;\n}\n\nstatic MA_INLINE float ma_degrees_to_radians_f(float degrees)\n{\n    return degrees * 0.01745329252f;\n}\n\nstatic MA_INLINE float ma_radians_to_degrees_f(float radians)\n{\n    return radians * 57.295779512896f;\n}\n\n\n/*\nReturn Values:\n  0:  Success\n  22: EINVAL\n  34: ERANGE\n\nNot using symbolic constants for errors because I want to avoid #including errno.h\n\nThese are marked as no-inline because of some bad code generation by Clang. None of these functions\nare used in any performance-critical code within miniaudio.\n*/\nMA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)\n{\n    size_t i;\n\n    if (dst == 0) {\n        return 22;\n    }\n    if (dstSizeInBytes == 0) {\n        return 34;\n    }\n    if (src == 0) {\n        dst[0] = '\\0';\n        return 22;\n    }\n\n    for (i = 0; i < dstSizeInBytes && src[i] != '\\0'; ++i) {\n        dst[i] = src[i];\n    }\n\n    if (i < dstSizeInBytes) {\n        dst[i] = '\\0';\n        return 0;\n    }\n\n    dst[0] = '\\0';\n    return 34;\n}\n\nMA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src)\n{\n    size_t i;\n\n    if (dst == 0) {\n        return 22;\n    }\n    if (dstCap == 0) {\n        return 34;\n    }\n    if (src == 0) {\n        dst[0] = '\\0';\n        return 22;\n    }\n\n    for (i = 0; i < dstCap && src[i] != '\\0'; ++i) {\n        dst[i] = src[i];\n    }\n\n    if (i < dstCap) {\n        dst[i] = '\\0';\n        return 0;\n    }\n\n    dst[0] = '\\0';\n    return 34;\n}\n\n\nMA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)\n{\n    size_t maxcount;\n    size_t i;\n\n    if (dst == 0) {\n        return 22;\n    }\n    if (dstSizeInBytes == 0) {\n        return 34;\n    }\n    if (src == 0) {\n        dst[0] = '\\0';\n        return 22;\n    }\n\n    maxcount = count;\n    if (count == ((size_t)-1) || count >= dstSizeInBytes) {        /* -1 = _TRUNCATE */\n        maxcount = dstSizeInBytes - 1;\n    }\n\n    for (i = 0; i < maxcount && src[i] != '\\0'; ++i) {\n        dst[i] = src[i];\n    }\n\n    if (src[i] == '\\0' || i == count || count == ((size_t)-1)) {\n        dst[i] = '\\0';\n        return 0;\n    }\n\n    dst[0] = '\\0';\n    return 34;\n}\n\nMA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)\n{\n    char* dstorig;\n\n    if (dst == 0) {\n        return 22;\n    }\n    if (dstSizeInBytes == 0) {\n        return 34;\n    }\n    if (src == 0) {\n        dst[0] = '\\0';\n        return 22;\n    }\n\n    dstorig = dst;\n\n    while (dstSizeInBytes > 0 && dst[0] != '\\0') {\n        dst += 1;\n        dstSizeInBytes -= 1;\n    }\n\n    if (dstSizeInBytes == 0) {\n        return 22;  /* Unterminated. */\n    }\n\n\n    while (dstSizeInBytes > 0 && src[0] != '\\0') {\n        *dst++ = *src++;\n        dstSizeInBytes -= 1;\n    }\n\n    if (dstSizeInBytes > 0) {\n        dst[0] = '\\0';\n    } else {\n        dstorig[0] = '\\0';\n        return 34;\n    }\n\n    return 0;\n}\n\nMA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)\n{\n    char* dstorig;\n\n    if (dst == 0) {\n        return 22;\n    }\n    if (dstSizeInBytes == 0) {\n        return 34;\n    }\n    if (src == 0) {\n        return 22;\n    }\n\n    dstorig = dst;\n\n    while (dstSizeInBytes > 0 && dst[0] != '\\0') {\n        dst += 1;\n        dstSizeInBytes -= 1;\n    }\n\n    if (dstSizeInBytes == 0) {\n        return 22;  /* Unterminated. */\n    }\n\n\n    if (count == ((size_t)-1)) {        /* _TRUNCATE */\n        count = dstSizeInBytes - 1;\n    }\n\n    while (dstSizeInBytes > 0 && src[0] != '\\0' && count > 0) {\n        *dst++ = *src++;\n        dstSizeInBytes -= 1;\n        count -= 1;\n    }\n\n    if (dstSizeInBytes > 0) {\n        dst[0] = '\\0';\n    } else {\n        dstorig[0] = '\\0';\n        return 34;\n    }\n\n    return 0;\n}\n\nMA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)\n{\n    int sign;\n    unsigned int valueU;\n    char* dstEnd;\n\n    if (dst == NULL || dstSizeInBytes == 0) {\n        return 22;\n    }\n    if (radix < 2 || radix > 36) {\n        dst[0] = '\\0';\n        return 22;\n    }\n\n    sign = (value < 0 && radix == 10) ? -1 : 1;     /* The negative sign is only used when the base is 10. */\n\n    if (value < 0) {\n        valueU = -value;\n    } else {\n        valueU = value;\n    }\n\n    dstEnd = dst;\n    do\n    {\n        int remainder = valueU % radix;\n        if (remainder > 9) {\n            *dstEnd = (char)((remainder - 10) + 'a');\n        } else {\n            *dstEnd = (char)(remainder + '0');\n        }\n\n        dstEnd += 1;\n        dstSizeInBytes -= 1;\n        valueU /= radix;\n    } while (dstSizeInBytes > 0 && valueU > 0);\n\n    if (dstSizeInBytes == 0) {\n        dst[0] = '\\0';\n        return 22;  /* Ran out of room in the output buffer. */\n    }\n\n    if (sign < 0) {\n        *dstEnd++ = '-';\n        dstSizeInBytes -= 1;\n    }\n\n    if (dstSizeInBytes == 0) {\n        dst[0] = '\\0';\n        return 22;  /* Ran out of room in the output buffer. */\n    }\n\n    *dstEnd = '\\0';\n\n\n    /* At this point the string will be reversed. */\n    dstEnd -= 1;\n    while (dst < dstEnd) {\n        char temp = *dst;\n        *dst = *dstEnd;\n        *dstEnd = temp;\n\n        dst += 1;\n        dstEnd -= 1;\n    }\n\n    return 0;\n}\n\nMA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2)\n{\n    if (str1 == str2) return  0;\n\n    /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */\n    if (str1 == NULL) return -1;\n    if (str2 == NULL) return  1;\n\n    for (;;) {\n        if (str1[0] == '\\0') {\n            break;\n        }\n        if (str1[0] != str2[0]) {\n            break;\n        }\n\n        str1 += 1;\n        str2 += 1;\n    }\n\n    return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];\n}\n\nMA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB)\n{\n    int result;\n\n    result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1);\n    if (result != 0) {\n        return result;\n    }\n\n    result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1);\n    if (result != 0) {\n        return result;\n    }\n\n    return result;\n}\n\nMA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    size_t sz;\n    char* dst;\n\n    if (src == NULL) {\n        return NULL;\n    }\n\n    sz = strlen(src)+1;\n    dst = (char*)ma_malloc(sz, pAllocationCallbacks);\n    if (dst == NULL) {\n        return NULL;\n    }\n\n    ma_strcpy_s(dst, sz, src);\n\n    return dst;\n}\n\nMA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    size_t sz = wcslen(src)+1;\n    wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks);\n    if (dst == NULL) {\n        return NULL;\n    }\n\n    ma_wcscpy_s(dst, sz, src);\n\n    return dst;\n}\n\n\n\n#include <errno.h>\nstatic ma_result ma_result_from_errno(int e)\n{\n    if (e == 0) {\n        return MA_SUCCESS;\n    }\n#ifdef EPERM\n    else if (e == EPERM) { return MA_INVALID_OPERATION; }\n#endif\n#ifdef ENOENT\n    else if (e == ENOENT) { return MA_DOES_NOT_EXIST; }\n#endif\n#ifdef ESRCH\n    else if (e == ESRCH) { return MA_DOES_NOT_EXIST; }\n#endif\n#ifdef EINTR\n    else if (e == EINTR) { return MA_INTERRUPT; }\n#endif\n#ifdef EIO\n    else if (e == EIO) { return MA_IO_ERROR; }\n#endif\n#ifdef ENXIO\n    else if (e == ENXIO) { return MA_DOES_NOT_EXIST; }\n#endif\n#ifdef E2BIG\n    else if (e == E2BIG) { return MA_INVALID_ARGS; }\n#endif\n#ifdef ENOEXEC\n    else if (e == ENOEXEC) { return MA_INVALID_FILE; }\n#endif\n#ifdef EBADF\n    else if (e == EBADF) { return MA_INVALID_FILE; }\n#endif\n#ifdef ECHILD\n    else if (e == ECHILD) { return MA_ERROR; }\n#endif\n#ifdef EAGAIN\n    else if (e == EAGAIN) { return MA_UNAVAILABLE; }\n#endif\n#ifdef ENOMEM\n    else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; }\n#endif\n#ifdef EACCES\n    else if (e == EACCES) { return MA_ACCESS_DENIED; }\n#endif\n#ifdef EFAULT\n    else if (e == EFAULT) { return MA_BAD_ADDRESS; }\n#endif\n#ifdef ENOTBLK\n    else if (e == ENOTBLK) { return MA_ERROR; }\n#endif\n#ifdef EBUSY\n    else if (e == EBUSY) { return MA_BUSY; }\n#endif\n#ifdef EEXIST\n    else if (e == EEXIST) { return MA_ALREADY_EXISTS; }\n#endif\n#ifdef EXDEV\n    else if (e == EXDEV) { return MA_ERROR; }\n#endif\n#ifdef ENODEV\n    else if (e == ENODEV) { return MA_DOES_NOT_EXIST; }\n#endif\n#ifdef ENOTDIR\n    else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; }\n#endif\n#ifdef EISDIR\n    else if (e == EISDIR) { return MA_IS_DIRECTORY; }\n#endif\n#ifdef EINVAL\n    else if (e == EINVAL) { return MA_INVALID_ARGS; }\n#endif\n#ifdef ENFILE\n    else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; }\n#endif\n#ifdef EMFILE\n    else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; }\n#endif\n#ifdef ENOTTY\n    else if (e == ENOTTY) { return MA_INVALID_OPERATION; }\n#endif\n#ifdef ETXTBSY\n    else if (e == ETXTBSY) { return MA_BUSY; }\n#endif\n#ifdef EFBIG\n    else if (e == EFBIG) { return MA_TOO_BIG; }\n#endif\n#ifdef ENOSPC\n    else if (e == ENOSPC) { return MA_NO_SPACE; }\n#endif\n#ifdef ESPIPE\n    else if (e == ESPIPE) { return MA_BAD_SEEK; }\n#endif\n#ifdef EROFS\n    else if (e == EROFS) { return MA_ACCESS_DENIED; }\n#endif\n#ifdef EMLINK\n    else if (e == EMLINK) { return MA_TOO_MANY_LINKS; }\n#endif\n#ifdef EPIPE\n    else if (e == EPIPE) { return MA_BAD_PIPE; }\n#endif\n#ifdef EDOM\n    else if (e == EDOM) { return MA_OUT_OF_RANGE; }\n#endif\n#ifdef ERANGE\n    else if (e == ERANGE) { return MA_OUT_OF_RANGE; }\n#endif\n#ifdef EDEADLK\n    else if (e == EDEADLK) { return MA_DEADLOCK; }\n#endif\n#ifdef ENAMETOOLONG\n    else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; }\n#endif\n#ifdef ENOLCK\n    else if (e == ENOLCK) { return MA_ERROR; }\n#endif\n#ifdef ENOSYS\n    else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; }\n#endif\n#ifdef ENOTEMPTY\n    else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; }\n#endif\n#ifdef ELOOP\n    else if (e == ELOOP) { return MA_TOO_MANY_LINKS; }\n#endif\n#ifdef ENOMSG\n    else if (e == ENOMSG) { return MA_NO_MESSAGE; }\n#endif\n#ifdef EIDRM\n    else if (e == EIDRM) { return MA_ERROR; }\n#endif\n#ifdef ECHRNG\n    else if (e == ECHRNG) { return MA_ERROR; }\n#endif\n#ifdef EL2NSYNC\n    else if (e == EL2NSYNC) { return MA_ERROR; }\n#endif\n#ifdef EL3HLT\n    else if (e == EL3HLT) { return MA_ERROR; }\n#endif\n#ifdef EL3RST\n    else if (e == EL3RST) { return MA_ERROR; }\n#endif\n#ifdef ELNRNG\n    else if (e == ELNRNG) { return MA_OUT_OF_RANGE; }\n#endif\n#ifdef EUNATCH\n    else if (e == EUNATCH) { return MA_ERROR; }\n#endif\n#ifdef ENOCSI\n    else if (e == ENOCSI) { return MA_ERROR; }\n#endif\n#ifdef EL2HLT\n    else if (e == EL2HLT) { return MA_ERROR; }\n#endif\n#ifdef EBADE\n    else if (e == EBADE) { return MA_ERROR; }\n#endif\n#ifdef EBADR\n    else if (e == EBADR) { return MA_ERROR; }\n#endif\n#ifdef EXFULL\n    else if (e == EXFULL) { return MA_ERROR; }\n#endif\n#ifdef ENOANO\n    else if (e == ENOANO) { return MA_ERROR; }\n#endif\n#ifdef EBADRQC\n    else if (e == EBADRQC) { return MA_ERROR; }\n#endif\n#ifdef EBADSLT\n    else if (e == EBADSLT) { return MA_ERROR; }\n#endif\n#ifdef EBFONT\n    else if (e == EBFONT) { return MA_INVALID_FILE; }\n#endif\n#ifdef ENOSTR\n    else if (e == ENOSTR) { return MA_ERROR; }\n#endif\n#ifdef ENODATA\n    else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; }\n#endif\n#ifdef ETIME\n    else if (e == ETIME) { return MA_TIMEOUT; }\n#endif\n#ifdef ENOSR\n    else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; }\n#endif\n#ifdef ENONET\n    else if (e == ENONET) { return MA_NO_NETWORK; }\n#endif\n#ifdef ENOPKG\n    else if (e == ENOPKG) { return MA_ERROR; }\n#endif\n#ifdef EREMOTE\n    else if (e == EREMOTE) { return MA_ERROR; }\n#endif\n#ifdef ENOLINK\n    else if (e == ENOLINK) { return MA_ERROR; }\n#endif\n#ifdef EADV\n    else if (e == EADV) { return MA_ERROR; }\n#endif\n#ifdef ESRMNT\n    else if (e == ESRMNT) { return MA_ERROR; }\n#endif\n#ifdef ECOMM\n    else if (e == ECOMM) { return MA_ERROR; }\n#endif\n#ifdef EPROTO\n    else if (e == EPROTO) { return MA_ERROR; }\n#endif\n#ifdef EMULTIHOP\n    else if (e == EMULTIHOP) { return MA_ERROR; }\n#endif\n#ifdef EDOTDOT\n    else if (e == EDOTDOT) { return MA_ERROR; }\n#endif\n#ifdef EBADMSG\n    else if (e == EBADMSG) { return MA_BAD_MESSAGE; }\n#endif\n#ifdef EOVERFLOW\n    else if (e == EOVERFLOW) { return MA_TOO_BIG; }\n#endif\n#ifdef ENOTUNIQ\n    else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; }\n#endif\n#ifdef EBADFD\n    else if (e == EBADFD) { return MA_ERROR; }\n#endif\n#ifdef EREMCHG\n    else if (e == EREMCHG) { return MA_ERROR; }\n#endif\n#ifdef ELIBACC\n    else if (e == ELIBACC) { return MA_ACCESS_DENIED; }\n#endif\n#ifdef ELIBBAD\n    else if (e == ELIBBAD) { return MA_INVALID_FILE; }\n#endif\n#ifdef ELIBSCN\n    else if (e == ELIBSCN) { return MA_INVALID_FILE; }\n#endif\n#ifdef ELIBMAX\n    else if (e == ELIBMAX) { return MA_ERROR; }\n#endif\n#ifdef ELIBEXEC\n    else if (e == ELIBEXEC) { return MA_ERROR; }\n#endif\n#ifdef EILSEQ\n    else if (e == EILSEQ) { return MA_INVALID_DATA; }\n#endif\n#ifdef ERESTART\n    else if (e == ERESTART) { return MA_ERROR; }\n#endif\n#ifdef ESTRPIPE\n    else if (e == ESTRPIPE) { return MA_ERROR; }\n#endif\n#ifdef EUSERS\n    else if (e == EUSERS) { return MA_ERROR; }\n#endif\n#ifdef ENOTSOCK\n    else if (e == ENOTSOCK) { return MA_NOT_SOCKET; }\n#endif\n#ifdef EDESTADDRREQ\n    else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; }\n#endif\n#ifdef EMSGSIZE\n    else if (e == EMSGSIZE) { return MA_TOO_BIG; }\n#endif\n#ifdef EPROTOTYPE\n    else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; }\n#endif\n#ifdef ENOPROTOOPT\n    else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; }\n#endif\n#ifdef EPROTONOSUPPORT\n    else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; }\n#endif\n#ifdef ESOCKTNOSUPPORT\n    else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; }\n#endif\n#ifdef EOPNOTSUPP\n    else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; }\n#endif\n#ifdef EPFNOSUPPORT\n    else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; }\n#endif\n#ifdef EAFNOSUPPORT\n    else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; }\n#endif\n#ifdef EADDRINUSE\n    else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; }\n#endif\n#ifdef EADDRNOTAVAIL\n    else if (e == EADDRNOTAVAIL) { return MA_ERROR; }\n#endif\n#ifdef ENETDOWN\n    else if (e == ENETDOWN) { return MA_NO_NETWORK; }\n#endif\n#ifdef ENETUNREACH\n    else if (e == ENETUNREACH) { return MA_NO_NETWORK; }\n#endif\n#ifdef ENETRESET\n    else if (e == ENETRESET) { return MA_NO_NETWORK; }\n#endif\n#ifdef ECONNABORTED\n    else if (e == ECONNABORTED) { return MA_NO_NETWORK; }\n#endif\n#ifdef ECONNRESET\n    else if (e == ECONNRESET) { return MA_CONNECTION_RESET; }\n#endif\n#ifdef ENOBUFS\n    else if (e == ENOBUFS) { return MA_NO_SPACE; }\n#endif\n#ifdef EISCONN\n    else if (e == EISCONN) { return MA_ALREADY_CONNECTED; }\n#endif\n#ifdef ENOTCONN\n    else if (e == ENOTCONN) { return MA_NOT_CONNECTED; }\n#endif\n#ifdef ESHUTDOWN\n    else if (e == ESHUTDOWN) { return MA_ERROR; }\n#endif\n#ifdef ETOOMANYREFS\n    else if (e == ETOOMANYREFS) { return MA_ERROR; }\n#endif\n#ifdef ETIMEDOUT\n    else if (e == ETIMEDOUT) { return MA_TIMEOUT; }\n#endif\n#ifdef ECONNREFUSED\n    else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; }\n#endif\n#ifdef EHOSTDOWN\n    else if (e == EHOSTDOWN) { return MA_NO_HOST; }\n#endif\n#ifdef EHOSTUNREACH\n    else if (e == EHOSTUNREACH) { return MA_NO_HOST; }\n#endif\n#ifdef EALREADY\n    else if (e == EALREADY) { return MA_IN_PROGRESS; }\n#endif\n#ifdef EINPROGRESS\n    else if (e == EINPROGRESS) { return MA_IN_PROGRESS; }\n#endif\n#ifdef ESTALE\n    else if (e == ESTALE) { return MA_INVALID_FILE; }\n#endif\n#ifdef EUCLEAN\n    else if (e == EUCLEAN) { return MA_ERROR; }\n#endif\n#ifdef ENOTNAM\n    else if (e == ENOTNAM) { return MA_ERROR; }\n#endif\n#ifdef ENAVAIL\n    else if (e == ENAVAIL) { return MA_ERROR; }\n#endif\n#ifdef EISNAM\n    else if (e == EISNAM) { return MA_ERROR; }\n#endif\n#ifdef EREMOTEIO\n    else if (e == EREMOTEIO) { return MA_IO_ERROR; }\n#endif\n#ifdef EDQUOT\n    else if (e == EDQUOT) { return MA_NO_SPACE; }\n#endif\n#ifdef ENOMEDIUM\n    else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; }\n#endif\n#ifdef EMEDIUMTYPE\n    else if (e == EMEDIUMTYPE) { return MA_ERROR; }\n#endif\n#ifdef ECANCELED\n    else if (e == ECANCELED) { return MA_CANCELLED; }\n#endif\n#ifdef ENOKEY\n    else if (e == ENOKEY) { return MA_ERROR; }\n#endif\n#ifdef EKEYEXPIRED\n    else if (e == EKEYEXPIRED) { return MA_ERROR; }\n#endif\n#ifdef EKEYREVOKED\n    else if (e == EKEYREVOKED) { return MA_ERROR; }\n#endif\n#ifdef EKEYREJECTED\n    else if (e == EKEYREJECTED) { return MA_ERROR; }\n#endif\n#ifdef EOWNERDEAD\n    else if (e == EOWNERDEAD) { return MA_ERROR; }\n#endif\n#ifdef ENOTRECOVERABLE\n    else if (e == ENOTRECOVERABLE) { return MA_ERROR; }\n#endif\n#ifdef ERFKILL\n    else if (e == ERFKILL) { return MA_ERROR; }\n#endif\n#ifdef EHWPOISON\n    else if (e == EHWPOISON) { return MA_ERROR; }\n#endif\n    else {\n        return MA_ERROR;\n    }\n}\n\nMA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)\n{\n#if defined(_MSC_VER) && _MSC_VER >= 1400\n    errno_t err;\n#endif\n\n    if (ppFile != NULL) {\n        *ppFile = NULL;  /* Safety. */\n    }\n\n    if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(_MSC_VER) && _MSC_VER >= 1400\n    err = fopen_s(ppFile, pFilePath, pOpenMode);\n    if (err != 0) {\n        return ma_result_from_errno(err);\n    }\n#else\n#if defined(_WIN32) || defined(__APPLE__)\n    *ppFile = fopen(pFilePath, pOpenMode);\n#else\n    #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)\n        *ppFile = fopen64(pFilePath, pOpenMode);\n    #else\n        *ppFile = fopen(pFilePath, pOpenMode);\n    #endif\n#endif\n    if (*ppFile == NULL) {\n        ma_result result = ma_result_from_errno(errno);\n        if (result == MA_SUCCESS) {\n            result = MA_ERROR;   /* Just a safety check to make sure we never ever return success when pFile == NULL. */\n        }\n\n        return result;\n    }\n#endif\n\n    return MA_SUCCESS;\n}\n\n\n\n/*\n_wfopen() isn't always available in all compilation environments.\n\n    * Windows only.\n    * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).\n    * MinGW-64 (both 32- and 64-bit) seems to support it.\n    * MinGW wraps it in !defined(__STRICT_ANSI__).\n    * OpenWatcom wraps it in !defined(_NO_EXT_KEYS).\n\nThis can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()\nfallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.\n*/\n#if defined(_WIN32)\n    #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))\n        #define MA_HAS_WFOPEN\n    #endif\n#endif\n\nMA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (ppFile != NULL) {\n        *ppFile = NULL;  /* Safety. */\n    }\n\n    if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_HAS_WFOPEN)\n    {\n        /* Use _wfopen() on Windows. */\n    #if defined(_MSC_VER) && _MSC_VER >= 1400\n        errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);\n        if (err != 0) {\n            return ma_result_from_errno(err);\n        }\n    #else\n        *ppFile = _wfopen(pFilePath, pOpenMode);\n        if (*ppFile == NULL) {\n            return ma_result_from_errno(errno);\n        }\n    #endif\n        (void)pAllocationCallbacks;\n    }\n#else\n    /*\n    Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can\n    think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for\n    maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility.\n    */\n    {\n        mbstate_t mbs;\n        size_t lenMB;\n        const wchar_t* pFilePathTemp = pFilePath;\n        char* pFilePathMB = NULL;\n        char pOpenModeMB[32] = {0};\n\n        /* Get the length first. */\n        MA_ZERO_OBJECT(&mbs);\n        lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);\n        if (lenMB == (size_t)-1) {\n            return ma_result_from_errno(errno);\n        }\n\n        pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks);\n        if (pFilePathMB == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n\n        pFilePathTemp = pFilePath;\n        MA_ZERO_OBJECT(&mbs);\n        wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);\n\n        /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */\n        {\n            size_t i = 0;\n            for (;;) {\n                if (pOpenMode[i] == 0) {\n                    pOpenModeMB[i] = '\\0';\n                    break;\n                }\n\n                pOpenModeMB[i] = (char)pOpenMode[i];\n                i += 1;\n            }\n        }\n\n        *ppFile = fopen(pFilePathMB, pOpenModeMB);\n\n        ma_free(pFilePathMB, pAllocationCallbacks);\n    }\n\n    if (*ppFile == NULL) {\n        return MA_ERROR;\n    }\n#endif\n\n    return MA_SUCCESS;\n}\n\n\n\nstatic MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)\n{\n#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX\n    MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes);\n#else\n    while (sizeInBytes > 0) {\n        ma_uint64 bytesToCopyNow = sizeInBytes;\n        if (bytesToCopyNow > MA_SIZE_MAX) {\n            bytesToCopyNow = MA_SIZE_MAX;\n        }\n\n        MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow);  /* Safe cast to size_t. */\n\n        sizeInBytes -= bytesToCopyNow;\n        dst = (      void*)((      ma_uint8*)dst + bytesToCopyNow);\n        src = (const void*)((const ma_uint8*)src + bytesToCopyNow);\n    }\n#endif\n}\n\nstatic MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes)\n{\n#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX\n    MA_ZERO_MEMORY(dst, (size_t)sizeInBytes);\n#else\n    while (sizeInBytes > 0) {\n        ma_uint64 bytesToZeroNow = sizeInBytes;\n        if (bytesToZeroNow > MA_SIZE_MAX) {\n            bytesToZeroNow = MA_SIZE_MAX;\n        }\n\n        MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow);  /* Safe cast to size_t. */\n\n        sizeInBytes -= bytesToZeroNow;\n        dst = (void*)((ma_uint8*)dst + bytesToZeroNow);\n    }\n#endif\n}\n\n\n/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */\nstatic MA_INLINE unsigned int ma_next_power_of_2(unsigned int x)\n{\n    x--;\n    x |= x >> 1;\n    x |= x >> 2;\n    x |= x >> 4;\n    x |= x >> 8;\n    x |= x >> 16;\n    x++;\n\n    return x;\n}\n\nstatic MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x)\n{\n    return ma_next_power_of_2(x) >> 1;\n}\n\nstatic MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x)\n{\n    unsigned int prev = ma_prev_power_of_2(x);\n    unsigned int next = ma_next_power_of_2(x);\n    if ((next - x) > (x - prev)) {\n        return prev;\n    } else {\n        return next;\n    }\n}\n\nstatic MA_INLINE unsigned int ma_count_set_bits(unsigned int x)\n{\n    unsigned int count = 0;\n    while (x != 0) {\n        if (x & 1) {\n            count += 1;\n        }\n\n        x = x >> 1;\n    }\n\n    return count;\n}\n\n\n\n/**************************************************************************************************************************************************************\n\nAllocation Callbacks\n\n**************************************************************************************************************************************************************/\nstatic void* ma__malloc_default(size_t sz, void* pUserData)\n{\n    (void)pUserData;\n    return MA_MALLOC(sz);\n}\n\nstatic void* ma__realloc_default(void* p, size_t sz, void* pUserData)\n{\n    (void)pUserData;\n    return MA_REALLOC(p, sz);\n}\n\nstatic void ma__free_default(void* p, void* pUserData)\n{\n    (void)pUserData;\n    MA_FREE(p);\n}\n\nstatic ma_allocation_callbacks ma_allocation_callbacks_init_default(void)\n{\n    ma_allocation_callbacks callbacks;\n    callbacks.pUserData = NULL;\n    callbacks.onMalloc  = ma__malloc_default;\n    callbacks.onRealloc = ma__realloc_default;\n    callbacks.onFree    = ma__free_default;\n\n    return callbacks;\n}\n\nstatic ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc)\n{\n    if (pDst == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pSrc == NULL) {\n        *pDst = ma_allocation_callbacks_init_default();\n    } else {\n        if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) {\n            *pDst = ma_allocation_callbacks_init_default();\n        } else {\n            if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) {\n                return MA_INVALID_ARGS;    /* Invalid allocation callbacks. */\n            } else {\n                *pDst = *pSrc;\n            }\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n\n\n\n/**************************************************************************************************************************************************************\n\nLogging\n\n**************************************************************************************************************************************************************/\nMA_API const char* ma_log_level_to_string(ma_uint32 logLevel)\n{\n    switch (logLevel)\n    {\n        case MA_LOG_LEVEL_DEBUG:   return \"DEBUG\";\n        case MA_LOG_LEVEL_INFO:    return \"INFO\";\n        case MA_LOG_LEVEL_WARNING: return \"WARNING\";\n        case MA_LOG_LEVEL_ERROR:   return \"ERROR\";\n        default:                   return \"ERROR\";\n    }\n}\n\n#if defined(MA_DEBUG_OUTPUT)\n#if defined(MA_ANDROID)\n    #include <android/log.h>\n#endif\n\n/* Customize this to use a specific tag in __android_log_print() for debug output messages. */\n#ifndef MA_ANDROID_LOG_TAG\n#define MA_ANDROID_LOG_TAG  \"miniaudio\"\n#endif\n\nvoid ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage)\n{\n    (void)pUserData;\n\n    /* Special handling for some platforms. */\n    #if defined(MA_ANDROID)\n    {\n        /* Android. */\n        __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, \"%s: %s\", ma_log_level_to_string(level), pMessage);\n    }\n    #else\n    {\n        /* Everything else. */\n        printf(\"%s: %s\", ma_log_level_to_string(level), pMessage);\n    }\n    #endif\n}\n#endif\n\nMA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData)\n{\n    ma_log_callback callback;\n\n    MA_ZERO_OBJECT(&callback);\n    callback.onLog     = onLog;\n    callback.pUserData = pUserData;\n\n    return callback;\n}\n\n\nMA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog)\n{\n    if (pLog == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pLog);\n    ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks);\n\n    /* We need a mutex for thread safety. */\n    #ifndef MA_NO_THREADING\n    {\n        ma_result result = ma_mutex_init(&pLog->lock);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n    #endif\n\n    /* If we're using debug output, enable it. */\n    #if defined(MA_DEBUG_OUTPUT)\n    {\n        ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */\n    }\n    #endif\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_log_uninit(ma_log* pLog)\n{\n    if (pLog == NULL) {\n        return;\n    }\n\n#ifndef MA_NO_THREADING\n    ma_mutex_uninit(&pLog->lock);\n#endif\n}\n\nstatic void ma_log_lock(ma_log* pLog)\n{\n#ifndef MA_NO_THREADING\n    ma_mutex_lock(&pLog->lock);\n#else\n    (void)pLog;\n#endif\n}\n\nstatic void ma_log_unlock(ma_log* pLog)\n{\n#ifndef MA_NO_THREADING\n    ma_mutex_unlock(&pLog->lock);\n#else\n    (void)pLog;\n#endif\n}\n\nMA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback)\n{\n    ma_result result = MA_SUCCESS;\n\n    if (pLog == NULL || callback.onLog == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_log_lock(pLog);\n    {\n        if (pLog->callbackCount == ma_countof(pLog->callbacks)) {\n            result = MA_OUT_OF_MEMORY;  /* Reached the maximum allowed log callbacks. */\n        } else {\n            pLog->callbacks[pLog->callbackCount] = callback;\n            pLog->callbackCount += 1;\n        }\n    }\n    ma_log_unlock(pLog);\n\n    return result;\n}\n\nMA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback)\n{\n    if (pLog == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_log_lock(pLog);\n    {\n        ma_uint32 iLog;\n        for (iLog = 0; iLog < pLog->callbackCount; ) {\n            if (pLog->callbacks[iLog].onLog == callback.onLog) {\n                /* Found. Move everything down a slot. */\n                ma_uint32 jLog;\n                for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) {\n                    pLog->callbacks[jLog] = pLog->callbacks[jLog + 1];\n                }\n\n                pLog->callbackCount -= 1;\n            } else {\n                /* Not found. */\n                iLog += 1;\n            }\n        }\n    }\n    ma_log_unlock(pLog);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage)\n{\n    if (pLog == NULL || pMessage == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_log_lock(pLog);\n    {\n        ma_uint32 iLog;\n        for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) {\n            if (pLog->callbacks[iLog].onLog) {\n                pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage);\n            }\n        }\n    }\n    ma_log_unlock(pLog);\n\n    return MA_SUCCESS;\n}\n\n\n/*\nWe need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a\nlogging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf().\n*/\n#if defined(_MSC_VER) && _MSC_VER < 1900\nstatic int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args)\n{\n#if _MSC_VER > 1200\n    return _vscprintf(format, args);\n#else\n    int result;\n    char* pTempBuffer = NULL;\n    size_t tempBufferCap = 1024;\n\n    if (format == NULL) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    for (;;) {\n        char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks);\n        if (pNewTempBuffer == NULL) {\n            ma_free(pTempBuffer, pAllocationCallbacks);\n            errno = ENOMEM;\n            return -1;  /* Out of memory. */\n        }\n\n        pTempBuffer = pNewTempBuffer;\n\n        result = _vsnprintf(pTempBuffer, tempBufferCap, format, args);\n        ma_free(pTempBuffer, NULL);\n\n        if (result != -1) {\n            break;  /* Got it. */\n        }\n\n        /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */\n        tempBufferCap *= 2;\n    }\n\n    return result;\n#endif\n}\n#endif\n\nMA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args)\n{\n    if (pLog == NULL || pFormat == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L)\n    {\n        ma_result result;\n        int length;\n        char  pFormattedMessageStack[1024];\n        char* pFormattedMessageHeap = NULL;\n\n        /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */\n        length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args);\n        if (length < 0) {\n            return MA_INVALID_OPERATION;    /* An error occurred when trying to convert the buffer. */\n        }\n\n        if ((size_t)length < sizeof(pFormattedMessageStack)) {\n            /* The string was written to the stack. */\n            result = ma_log_post(pLog, level, pFormattedMessageStack);\n        } else {\n            /* The stack buffer was too small, try the heap. */\n            pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks);\n            if (pFormattedMessageHeap == NULL) {\n                return MA_OUT_OF_MEMORY;\n            }\n\n            length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args);\n            if (length < 0) {\n                ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);\n                return MA_INVALID_OPERATION;\n            }\n\n            result = ma_log_post(pLog, level, pFormattedMessageHeap);\n            ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);\n        }\n\n        return result;\n    }\n    #else\n    {\n        /*\n        Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll\n        need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing\n        a fixed sized stack allocated buffer.\n        */\n        #if defined(_MSC_VER) && _MSC_VER >= 1200   /* 1200 = VC6 */\n        {\n            ma_result result;\n            int formattedLen;\n            char* pFormattedMessage = NULL;\n            va_list args2;\n\n            #if _MSC_VER >= 1800\n            {\n                va_copy(args2, args);\n            }\n            #else\n            {\n                args2 = args;\n            }\n            #endif\n\n            formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2);\n            va_end(args2);\n\n            if (formattedLen <= 0) {\n                return MA_INVALID_OPERATION;\n            }\n\n            pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks);\n            if (pFormattedMessage == NULL) {\n                return MA_OUT_OF_MEMORY;\n            }\n\n            /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf().  */\n            #if _MSC_VER >= 1400    /* 1400 = Visual Studio 2005 */\n            {\n                vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args);\n            }\n            #else\n            {\n                vsprintf(pFormattedMessage, pFormat, args);\n            }\n            #endif\n\n            result = ma_log_post(pLog, level, pFormattedMessage);\n            ma_free(pFormattedMessage, &pLog->allocationCallbacks);\n\n            return result;\n        }\n        #else\n        {\n            /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */\n            (void)level;\n            (void)args;\n\n            return MA_INVALID_OPERATION;\n        }\n        #endif\n    }\n    #endif\n}\n\nMA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...)\n{\n    ma_result result;\n    va_list args;\n\n    if (pLog == NULL || pFormat == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    va_start(args, pFormat);\n    {\n        result = ma_log_postv(pLog, level, pFormat, args);\n    }\n    va_end(args);\n\n    return result;\n}\n\n\n\nstatic MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x)\n{\n    return (ma_uint8)(ma_clamp(x, -128, 127) + 128);\n}\n\nstatic MA_INLINE ma_int16 ma_clip_s16(ma_int32 x)\n{\n    return (ma_int16)ma_clamp(x, -32768, 32767);\n}\n\nstatic MA_INLINE ma_int64 ma_clip_s24(ma_int64 x)\n{\n    return (ma_int64)ma_clamp(x, -8388608, 8388607);\n}\n\nstatic MA_INLINE ma_int32 ma_clip_s32(ma_int64 x)\n{\n    /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */\n    ma_int64 clipMin;\n    ma_int64 clipMax;\n    clipMin = -((ma_int64)2147483647 + 1);\n    clipMax =   (ma_int64)2147483647;\n\n    return (ma_int32)ma_clamp(x, clipMin, clipMax);\n}\n\nstatic MA_INLINE float ma_clip_f32(float x)\n{\n    if (x < -1) return -1;\n    if (x > +1) return +1;\n    return x;\n}\n\n\nstatic MA_INLINE float ma_mix_f32(float x, float y, float a)\n{\n    return x*(1-a) + y*a;\n}\nstatic MA_INLINE float ma_mix_f32_fast(float x, float y, float a)\n{\n    float r0 = (y - x);\n    float r1 = r0*a;\n    return x + r1;\n    /*return x + (y - x)*a;*/\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a)\n{\n    return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a));\n}\n#endif\n#if defined(MA_SUPPORT_AVX2)\nstatic MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a)\n{\n    return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a));\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a)\n{\n    return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a));\n}\n#endif\n\n\nstatic MA_INLINE double ma_mix_f64(double x, double y, double a)\n{\n    return x*(1-a) + y*a;\n}\nstatic MA_INLINE double ma_mix_f64_fast(double x, double y, double a)\n{\n    return x + (y - x)*a;\n}\n\nstatic MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)\n{\n    return lo + x*(hi-lo);\n}\n\n\n/*\nGreatest common factor using Euclid's algorithm iteratively.\n*/\nstatic MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b)\n{\n    for (;;) {\n        if (b == 0) {\n            break;\n        } else {\n            ma_uint32 t = a;\n            a = b;\n            b = t % a;\n        }\n    }\n\n    return a;\n}\n\n\nstatic ma_uint32 ma_ffs_32(ma_uint32 x)\n{\n    ma_uint32 i;\n\n    /* Just a naive implementation just to get things working for now. Will optimize this later. */\n    for (i = 0; i < 32; i += 1) {\n        if ((x & (1 << i)) != 0) {\n            return i;\n        }\n    }\n\n    return i;\n}\n\nstatic MA_INLINE ma_int16 ma_float_to_fixed_16(float x)\n{\n    return (ma_int16)(x * (1 << 8));\n}\n\n\n\n/*\nRandom Number Generation\n\nminiaudio uses the LCG random number generation algorithm. This is good enough for audio.\n\nNote that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across\nmultiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for\nminiaudio's purposes.\n*/\n#ifndef MA_DEFAULT_LCG_SEED\n#define MA_DEFAULT_LCG_SEED 4321\n#endif\n\n#define MA_LCG_M   2147483647\n#define MA_LCG_A   48271\n#define MA_LCG_C   0\n\nstatic ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */\n\nstatic MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed)\n{\n    MA_ASSERT(pLCG != NULL);\n    pLCG->state = seed;\n}\n\nstatic MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG)\n{\n    pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M;\n    return pLCG->state;\n}\n\nstatic MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG)\n{\n    return (ma_uint32)ma_lcg_rand_s32(pLCG);\n}\n\nstatic MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG)\n{\n    return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF);\n}\n\nstatic MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG)\n{\n    return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF;\n}\n\nstatic MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG)\n{\n    return (float)ma_lcg_rand_f64(pLCG);\n}\n\nstatic MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi)\n{\n    return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi);\n}\n\nstatic MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi)\n{\n    if (lo == hi) {\n        return lo;\n    }\n\n    return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1);\n}\n\n\n\nstatic MA_INLINE void ma_seed(ma_int32 seed)\n{\n    ma_lcg_seed(&g_maLCG, seed);\n}\n\nstatic MA_INLINE ma_int32 ma_rand_s32(void)\n{\n    return ma_lcg_rand_s32(&g_maLCG);\n}\n\nstatic MA_INLINE ma_uint32 ma_rand_u32(void)\n{\n    return ma_lcg_rand_u32(&g_maLCG);\n}\n\nstatic MA_INLINE double ma_rand_f64(void)\n{\n    return ma_lcg_rand_f64(&g_maLCG);\n}\n\nstatic MA_INLINE float ma_rand_f32(void)\n{\n    return ma_lcg_rand_f32(&g_maLCG);\n}\n\nstatic MA_INLINE float ma_rand_range_f32(float lo, float hi)\n{\n    return ma_lcg_rand_range_f32(&g_maLCG, lo, hi);\n}\n\nstatic MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi)\n{\n    return ma_lcg_rand_range_s32(&g_maLCG, lo, hi);\n}\n\n\nstatic MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax)\n{\n    return ma_rand_range_f32(ditherMin, ditherMax);\n}\n\nstatic MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax)\n{\n    float a = ma_rand_range_f32(ditherMin, 0);\n    float b = ma_rand_range_f32(0, ditherMax);\n    return a + b;\n}\n\nstatic MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax)\n{\n    if (ditherMode == ma_dither_mode_rectangle) {\n        return ma_dither_f32_rectangle(ditherMin, ditherMax);\n    }\n    if (ditherMode == ma_dither_mode_triangle) {\n        return ma_dither_f32_triangle(ditherMin, ditherMax);\n    }\n\n    return 0;\n}\n\nstatic MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax)\n{\n    if (ditherMode == ma_dither_mode_rectangle) {\n        ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax);\n        return a;\n    }\n    if (ditherMode == ma_dither_mode_triangle) {\n        ma_int32 a = ma_rand_range_s32(ditherMin, 0);\n        ma_int32 b = ma_rand_range_s32(0, ditherMax);\n        return a + b;\n    }\n\n    return 0;\n}\n\n\n/**************************************************************************************************************************************************************\n\nAtomics\n\n**************************************************************************************************************************************************************/\n/* ma_atomic.h begin */\n#ifndef ma_atomic_h\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wlong-long\"\n    #if defined(__clang__)\n        #pragma GCC diagnostic ignored \"-Wc++11-long-long\"\n    #endif\n#endif\ntypedef int ma_atomic_memory_order;\n#define MA_ATOMIC_HAS_8\n#define MA_ATOMIC_HAS_16\n#define MA_ATOMIC_HAS_32\n#define MA_ATOMIC_HAS_64\n#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__)\n    #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType)   \\\n        ma_atomicType result; \\\n        switch (order) \\\n        { \\\n            case ma_atomic_memory_order_relaxed: \\\n            { \\\n                result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \\\n            } break; \\\n            case ma_atomic_memory_order_consume: \\\n            case ma_atomic_memory_order_acquire: \\\n            { \\\n                result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \\\n            } break; \\\n            case ma_atomic_memory_order_release: \\\n            { \\\n                result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \\\n            } break; \\\n            case ma_atomic_memory_order_acq_rel: \\\n            case ma_atomic_memory_order_seq_cst: \\\n            default: \\\n            { \\\n                result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \\\n            } break; \\\n        } \\\n        return result;\n    #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType)   \\\n        ma_atomicType result; \\\n        switch (order) \\\n        { \\\n            case ma_atomic_memory_order_relaxed: \\\n            { \\\n                result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \\\n            } break; \\\n            case ma_atomic_memory_order_consume: \\\n            case ma_atomic_memory_order_acquire: \\\n            { \\\n                result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \\\n            } break; \\\n            case ma_atomic_memory_order_release: \\\n            { \\\n                result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \\\n            } break; \\\n            case ma_atomic_memory_order_acq_rel: \\\n            case ma_atomic_memory_order_seq_cst: \\\n            default: \\\n            { \\\n                result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \\\n            } break; \\\n        } \\\n        return result;\n    #define ma_atomic_memory_order_relaxed  0\n    #define ma_atomic_memory_order_consume  1\n    #define ma_atomic_memory_order_acquire  2\n    #define ma_atomic_memory_order_release  3\n    #define ma_atomic_memory_order_acq_rel  4\n    #define ma_atomic_memory_order_seq_cst  5\n    #if _MSC_VER < 1600 && defined(MA_X86)\n        #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY\n    #endif\n    #if _MSC_VER < 1600\n        #undef MA_ATOMIC_HAS_8\n        #undef MA_ATOMIC_HAS_16\n    #endif\n    #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY)\n        #include <intrin.h>\n    #endif\n    #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY)\n        #if defined(MA_ATOMIC_HAS_8)\n            static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired)\n            {\n                ma_uint8 result = 0;\n                __asm {\n                    mov ecx, dst\n                    mov al,  expected\n                    mov dl,  desired\n                    lock cmpxchg [ecx], dl\n                    mov result, al\n                }\n                return result;\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_16)\n            static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired)\n            {\n                ma_uint16 result = 0;\n                __asm {\n                    mov ecx, dst\n                    mov ax,  expected\n                    mov dx,  desired\n                    lock cmpxchg [ecx], dx\n                    mov result, ax\n                }\n                return result;\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_32)\n            static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired)\n            {\n                ma_uint32 result = 0;\n                __asm {\n                    mov ecx, dst\n                    mov eax, expected\n                    mov edx, desired\n                    lock cmpxchg [ecx], edx\n                    mov result, eax\n                }\n                return result;\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_64)\n            static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired)\n            {\n                ma_uint32 resultEAX = 0;\n                ma_uint32 resultEDX = 0;\n                __asm {\n                    mov esi, dst\n                    mov eax, dword ptr expected\n                    mov edx, dword ptr expected + 4\n                    mov ebx, dword ptr desired\n                    mov ecx, dword ptr desired + 4\n                    lock cmpxchg8b qword ptr [esi]\n                    mov resultEAX, eax\n                    mov resultEDX, edx\n                }\n                return ((ma_uint64)resultEDX << 32) | resultEAX;\n            }\n        #endif\n    #else\n        #if defined(MA_ATOMIC_HAS_8)\n            #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected)\n        #endif\n        #if defined(MA_ATOMIC_HAS_16)\n            #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected)\n        #endif\n        #if defined(MA_ATOMIC_HAS_32)\n            #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected)\n        #endif\n        #if defined(MA_ATOMIC_HAS_64)\n            #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected)\n        #endif\n    #endif\n    #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY)\n        #if defined(MA_ATOMIC_HAS_8)\n            static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n            {\n                ma_uint8 result = 0;\n                (void)order;\n                __asm {\n                    mov ecx, dst\n                    mov al,  src\n                    lock xchg [ecx], al\n                    mov result, al\n                }\n                return result;\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_16)\n            static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n            {\n                ma_uint16 result = 0;\n                (void)order;\n                __asm {\n                    mov ecx, dst\n                    mov ax,  src\n                    lock xchg [ecx], ax\n                    mov result, ax\n                }\n                return result;\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_32)\n            static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n            {\n                ma_uint32 result = 0;\n                (void)order;\n                __asm {\n                    mov ecx, dst\n                    mov eax, src\n                    lock xchg [ecx], eax\n                    mov result, eax\n                }\n                return result;\n            }\n        #endif\n    #else\n        #if defined(MA_ATOMIC_HAS_8)\n            static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n            {\n            #if defined(MA_ARM)\n                MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char);\n            #else\n                (void)order;\n                return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src);\n            #endif\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_16)\n            static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n            {\n            #if defined(MA_ARM)\n                MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short);\n            #else\n                (void)order;\n                return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src);\n            #endif\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_32)\n            static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n            {\n            #if defined(MA_ARM)\n                MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long);\n            #else\n                (void)order;\n                return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src);\n            #endif\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT)\n            static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n            {\n            #if defined(MA_ARM)\n                MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long);\n            #else\n                (void)order;\n                return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src);\n            #endif\n            }\n        #else\n        #endif\n    #endif\n    #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT)\n        static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            ma_uint64 oldValue;\n            do {\n                oldValue = *dst;\n            } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n    #endif\n    #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY)\n        #if defined(MA_ATOMIC_HAS_8)\n            static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n            {\n                ma_uint8 result = 0;\n                (void)order;\n                __asm {\n                    mov ecx, dst\n                    mov al,  src\n                    lock xadd [ecx], al\n                    mov result, al\n                }\n                return result;\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_16)\n            static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n            {\n                ma_uint16 result = 0;\n                (void)order;\n                __asm {\n                    mov ecx, dst\n                    mov ax,  src\n                    lock xadd [ecx], ax\n                    mov result, ax\n                }\n                return result;\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_32)\n            static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n            {\n                ma_uint32 result = 0;\n                (void)order;\n                __asm {\n                    mov ecx, dst\n                    mov eax, src\n                    lock xadd [ecx], eax\n                    mov result, eax\n                }\n                return result;\n            }\n        #endif\n    #else\n        #if defined(MA_ATOMIC_HAS_8)\n            static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n            {\n            #if defined(MA_ARM)\n                MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char);\n            #else\n                (void)order;\n                return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src);\n            #endif\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_16)\n            static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n            {\n            #if defined(MA_ARM)\n                MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short);\n            #else\n                (void)order;\n                return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src);\n            #endif\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_32)\n            static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n            {\n            #if defined(MA_ARM)\n                MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long);\n            #else\n                (void)order;\n                return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src);\n            #endif\n            }\n        #endif\n        #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT)\n            static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n            {\n            #if defined(MA_ARM)\n                MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long);\n            #else\n                (void)order;\n                return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src);\n            #endif\n            }\n        #else\n        #endif\n    #endif\n    #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT)\n        static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            ma_uint64 oldValue;\n            ma_uint64 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue + src;\n            } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n    #endif\n    #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY)\n        static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order)\n        {\n            (void)order;\n            __asm {\n                lock add [esp], 0\n            }\n        }\n    #else\n        #if defined(MA_X64)\n            #define ma_atomic_thread_fence(order)   __faststorefence(), (void)order\n        #elif defined(MA_ARM64)\n            #define ma_atomic_thread_fence(order)   __dmb(_ARM64_BARRIER_ISH), (void)order\n        #else\n            static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order)\n            {\n                volatile ma_uint32 barrier = 0;\n                ma_atomic_fetch_add_explicit_32(&barrier, 0, order);\n            }\n        #endif\n    #endif\n    #define ma_atomic_compiler_fence()      ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst)\n    #define ma_atomic_signal_fence(order)   ma_atomic_thread_fence(order)\n    #if defined(MA_ATOMIC_HAS_8)\n        static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char);\n        #else\n            (void)order;\n            return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0);\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_16)\n        static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short);\n        #else\n            (void)order;\n            return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0);\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_32)\n        static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long);\n        #else\n            (void)order;\n            return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0);\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_64)\n        static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long);\n        #else\n            (void)order;\n            return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0);\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_8)\n        #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_16)\n        #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_32)\n        #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_64)\n        #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_8)\n        static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            ma_uint8 oldValue;\n            ma_uint8 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint8)(oldValue - src);\n            } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_16)\n        static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            ma_uint16 oldValue;\n            ma_uint16 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint16)(oldValue - src);\n            } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_32)\n        static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            ma_uint32 oldValue;\n            ma_uint32 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue - src;\n            } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_64)\n        static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            ma_uint64 oldValue;\n            ma_uint64 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue - src;\n            } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_8)\n        static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char);\n        #else\n            ma_uint8 oldValue;\n            ma_uint8 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint8)(oldValue & src);\n            } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_16)\n        static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short);\n        #else\n            ma_uint16 oldValue;\n            ma_uint16 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint16)(oldValue & src);\n            } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_32)\n        static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long);\n        #else\n            ma_uint32 oldValue;\n            ma_uint32 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue & src;\n            } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_64)\n        static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long);\n        #else\n            ma_uint64 oldValue;\n            ma_uint64 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue & src;\n            } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_8)\n        static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char);\n        #else\n            ma_uint8 oldValue;\n            ma_uint8 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint8)(oldValue ^ src);\n            } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_16)\n        static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short);\n        #else\n            ma_uint16 oldValue;\n            ma_uint16 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint16)(oldValue ^ src);\n            } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_32)\n        static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long);\n        #else\n            ma_uint32 oldValue;\n            ma_uint32 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue ^ src;\n            } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_64)\n        static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long);\n        #else\n            ma_uint64 oldValue;\n            ma_uint64 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue ^ src;\n            } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_8)\n        static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char);\n        #else\n            ma_uint8 oldValue;\n            ma_uint8 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint8)(oldValue | src);\n            } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_16)\n        static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short);\n        #else\n            ma_uint16 oldValue;\n            ma_uint16 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint16)(oldValue | src);\n            } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_32)\n        static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long);\n        #else\n            ma_uint32 oldValue;\n            ma_uint32 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue | src;\n            } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_64)\n        static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_ARM)\n            MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long);\n        #else\n            ma_uint64 oldValue;\n            ma_uint64 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue | src;\n            } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        #endif\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_8)\n        #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_16)\n        #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_32)\n        #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_64)\n        #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_8)\n        #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_16)\n        #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_32)\n        #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_64)\n        #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order)\n    #endif\n    #if defined(MA_ATOMIC_HAS_8)\n        typedef ma_uint8 ma_atomic_flag;\n        #define ma_atomic_flag_test_and_set_explicit(ptr, order)    (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order)\n        #define ma_atomic_flag_clear_explicit(ptr, order)           ma_atomic_clear_explicit_8(ptr, order)\n        #define c89atoimc_flag_load_explicit(ptr, order)            ma_atomic_load_explicit_8(ptr, order)\n    #else\n        typedef ma_uint32 ma_atomic_flag;\n        #define ma_atomic_flag_test_and_set_explicit(ptr, order)    (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order)\n        #define ma_atomic_flag_clear_explicit(ptr, order)           ma_atomic_clear_explicit_32(ptr, order)\n        #define c89atoimc_flag_load_explicit(ptr, order)            ma_atomic_load_explicit_32(ptr, order)\n    #endif\n#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))\n    #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE\n    #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE\n    #define ma_atomic_memory_order_relaxed                          __ATOMIC_RELAXED\n    #define ma_atomic_memory_order_consume                          __ATOMIC_CONSUME\n    #define ma_atomic_memory_order_acquire                          __ATOMIC_ACQUIRE\n    #define ma_atomic_memory_order_release                          __ATOMIC_RELEASE\n    #define ma_atomic_memory_order_acq_rel                          __ATOMIC_ACQ_REL\n    #define ma_atomic_memory_order_seq_cst                          __ATOMIC_SEQ_CST\n    #define ma_atomic_compiler_fence()                              __asm__ __volatile__(\"\":::\"memory\")\n    #define ma_atomic_thread_fence(order)                           __atomic_thread_fence(order)\n    #define ma_atomic_signal_fence(order)                           __atomic_signal_fence(order)\n    #define ma_atomic_is_lock_free_8(ptr)                           __atomic_is_lock_free(1, ptr)\n    #define ma_atomic_is_lock_free_16(ptr)                          __atomic_is_lock_free(2, ptr)\n    #define ma_atomic_is_lock_free_32(ptr)                          __atomic_is_lock_free(4, ptr)\n    #define ma_atomic_is_lock_free_64(ptr)                          __atomic_is_lock_free(8, ptr)\n    #define ma_atomic_test_and_set_explicit_8( dst, order)          __atomic_exchange_n(dst, 1, order)\n    #define ma_atomic_test_and_set_explicit_16(dst, order)          __atomic_exchange_n(dst, 1, order)\n    #define ma_atomic_test_and_set_explicit_32(dst, order)          __atomic_exchange_n(dst, 1, order)\n    #define ma_atomic_test_and_set_explicit_64(dst, order)          __atomic_exchange_n(dst, 1, order)\n    #define ma_atomic_clear_explicit_8( dst, order)                 __atomic_store_n(dst, 0, order)\n    #define ma_atomic_clear_explicit_16(dst, order)                 __atomic_store_n(dst, 0, order)\n    #define ma_atomic_clear_explicit_32(dst, order)                 __atomic_store_n(dst, 0, order)\n    #define ma_atomic_clear_explicit_64(dst, order)                 __atomic_store_n(dst, 0, order)\n    #define ma_atomic_store_explicit_8( dst, src, order)            __atomic_store_n(dst, src, order)\n    #define ma_atomic_store_explicit_16(dst, src, order)            __atomic_store_n(dst, src, order)\n    #define ma_atomic_store_explicit_32(dst, src, order)            __atomic_store_n(dst, src, order)\n    #define ma_atomic_store_explicit_64(dst, src, order)            __atomic_store_n(dst, src, order)\n    #define ma_atomic_load_explicit_8( dst, order)                  __atomic_load_n(dst, order)\n    #define ma_atomic_load_explicit_16(dst, order)                  __atomic_load_n(dst, order)\n    #define ma_atomic_load_explicit_32(dst, order)                  __atomic_load_n(dst, order)\n    #define ma_atomic_load_explicit_64(dst, order)                  __atomic_load_n(dst, order)\n    #define ma_atomic_exchange_explicit_8( dst, src, order)         __atomic_exchange_n(dst, src, order)\n    #define ma_atomic_exchange_explicit_16(dst, src, order)         __atomic_exchange_n(dst, src, order)\n    #define ma_atomic_exchange_explicit_32(dst, src, order)         __atomic_exchange_n(dst, src, order)\n    #define ma_atomic_exchange_explicit_64(dst, src, order)         __atomic_exchange_n(dst, src, order)\n    #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder)   __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)\n    #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder)   __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)\n    #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder)   __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)\n    #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder)   __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)\n    #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder)     __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)\n    #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder)     __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)\n    #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder)     __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)\n    #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder)     __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)\n    #define ma_atomic_fetch_add_explicit_8( dst, src, order)        __atomic_fetch_add(dst, src, order)\n    #define ma_atomic_fetch_add_explicit_16(dst, src, order)        __atomic_fetch_add(dst, src, order)\n    #define ma_atomic_fetch_add_explicit_32(dst, src, order)        __atomic_fetch_add(dst, src, order)\n    #define ma_atomic_fetch_add_explicit_64(dst, src, order)        __atomic_fetch_add(dst, src, order)\n    #define ma_atomic_fetch_sub_explicit_8( dst, src, order)        __atomic_fetch_sub(dst, src, order)\n    #define ma_atomic_fetch_sub_explicit_16(dst, src, order)        __atomic_fetch_sub(dst, src, order)\n    #define ma_atomic_fetch_sub_explicit_32(dst, src, order)        __atomic_fetch_sub(dst, src, order)\n    #define ma_atomic_fetch_sub_explicit_64(dst, src, order)        __atomic_fetch_sub(dst, src, order)\n    #define ma_atomic_fetch_or_explicit_8( dst, src, order)         __atomic_fetch_or(dst, src, order)\n    #define ma_atomic_fetch_or_explicit_16(dst, src, order)         __atomic_fetch_or(dst, src, order)\n    #define ma_atomic_fetch_or_explicit_32(dst, src, order)         __atomic_fetch_or(dst, src, order)\n    #define ma_atomic_fetch_or_explicit_64(dst, src, order)         __atomic_fetch_or(dst, src, order)\n    #define ma_atomic_fetch_xor_explicit_8( dst, src, order)        __atomic_fetch_xor(dst, src, order)\n    #define ma_atomic_fetch_xor_explicit_16(dst, src, order)        __atomic_fetch_xor(dst, src, order)\n    #define ma_atomic_fetch_xor_explicit_32(dst, src, order)        __atomic_fetch_xor(dst, src, order)\n    #define ma_atomic_fetch_xor_explicit_64(dst, src, order)        __atomic_fetch_xor(dst, src, order)\n    #define ma_atomic_fetch_and_explicit_8( dst, src, order)        __atomic_fetch_and(dst, src, order)\n    #define ma_atomic_fetch_and_explicit_16(dst, src, order)        __atomic_fetch_and(dst, src, order)\n    #define ma_atomic_fetch_and_explicit_32(dst, src, order)        __atomic_fetch_and(dst, src, order)\n    #define ma_atomic_fetch_and_explicit_64(dst, src, order)        __atomic_fetch_and(dst, src, order)\n    static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired)\n    {\n        __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);\n        return expected;\n    }\n    static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired)\n    {\n        __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);\n        return expected;\n    }\n    static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired)\n    {\n        __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);\n        return expected;\n    }\n    static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired)\n    {\n        __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);\n        return expected;\n    }\n    typedef ma_uint8 ma_atomic_flag;\n    #define ma_atomic_flag_test_and_set_explicit(dst, order)        (ma_bool32)__atomic_test_and_set(dst, order)\n    #define ma_atomic_flag_clear_explicit(dst, order)               __atomic_clear(dst, order)\n    #define c89atoimc_flag_load_explicit(ptr, order)                ma_atomic_load_explicit_8(ptr, order)\n#else\n    #define ma_atomic_memory_order_relaxed  1\n    #define ma_atomic_memory_order_consume  2\n    #define ma_atomic_memory_order_acquire  3\n    #define ma_atomic_memory_order_release  4\n    #define ma_atomic_memory_order_acq_rel  5\n    #define ma_atomic_memory_order_seq_cst  6\n    #define ma_atomic_compiler_fence() __asm__ __volatile__(\"\":::\"memory\")\n    #if defined(__GNUC__)\n        #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order\n        static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            if (order > ma_atomic_memory_order_acquire) {\n                __sync_synchronize();\n            }\n            return __sync_lock_test_and_set(dst, src);\n        }\n        static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            ma_uint16 oldValue;\n            do {\n                oldValue = *dst;\n            } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            ma_uint32 oldValue;\n            do {\n                oldValue = *dst;\n            } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            ma_uint64 oldValue;\n            do {\n                oldValue = *dst;\n            } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_add(dst, src);\n        }\n        static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_add(dst, src);\n        }\n        static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_add(dst, src);\n        }\n        static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_add(dst, src);\n        }\n        static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_sub(dst, src);\n        }\n        static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_sub(dst, src);\n        }\n        static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_sub(dst, src);\n        }\n        static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_sub(dst, src);\n        }\n        static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_or(dst, src);\n        }\n        static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_or(dst, src);\n        }\n        static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_or(dst, src);\n        }\n        static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_or(dst, src);\n        }\n        static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_xor(dst, src);\n        }\n        static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_xor(dst, src);\n        }\n        static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_xor(dst, src);\n        }\n        static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_xor(dst, src);\n        }\n        static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_and(dst, src);\n        }\n        static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_and(dst, src);\n        }\n        static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_and(dst, src);\n        }\n        static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            (void)order;\n            return __sync_fetch_and_and(dst, src);\n        }\n        #define ma_atomic_compare_and_swap_8( dst, expected, desired)   __sync_val_compare_and_swap(dst, expected, desired)\n        #define ma_atomic_compare_and_swap_16(dst, expected, desired)   __sync_val_compare_and_swap(dst, expected, desired)\n        #define ma_atomic_compare_and_swap_32(dst, expected, desired)   __sync_val_compare_and_swap(dst, expected, desired)\n        #define ma_atomic_compare_and_swap_64(dst, expected, desired)   __sync_val_compare_and_swap(dst, expected, desired)\n    #else\n        #if defined(MA_X86)\n            #define ma_atomic_thread_fence(order) __asm__ __volatile__(\"lock; addl $0, (%%esp)\" ::: \"memory\", \"cc\")\n        #elif defined(MA_X64)\n            #define ma_atomic_thread_fence(order) __asm__ __volatile__(\"lock; addq $0, (%%rsp)\" ::: \"memory\", \"cc\")\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n        static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired)\n        {\n            ma_uint8 result;\n        #if defined(MA_X86) || defined(MA_X64)\n            __asm__ __volatile__(\"lock; cmpxchg %3, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(expected), \"d\"(desired) : \"cc\");\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n            return result;\n        }\n        static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired)\n        {\n            ma_uint16 result;\n        #if defined(MA_X86) || defined(MA_X64)\n            __asm__ __volatile__(\"lock; cmpxchg %3, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(expected), \"d\"(desired) : \"cc\");\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n            return result;\n        }\n        static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired)\n        {\n            ma_uint32 result;\n        #if defined(MA_X86) || defined(MA_X64)\n            __asm__ __volatile__(\"lock; cmpxchg %3, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(expected), \"d\"(desired) : \"cc\");\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n            return result;\n        }\n        static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired)\n        {\n            volatile ma_uint64 result;\n        #if defined(MA_X86)\n            ma_uint32 resultEAX;\n            ma_uint32 resultEDX;\n            __asm__ __volatile__(\"push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx\" : \"+m\"(*dst), \"=a\"(resultEAX), \"=d\"(resultEDX) : \"a\"(expected & 0xFFFFFFFF), \"d\"(expected >> 32), \"r\"(desired & 0xFFFFFFFF), \"c\"(desired >> 32) : \"cc\");\n            result = ((ma_uint64)resultEDX << 32) | resultEAX;\n        #elif defined(MA_X64)\n            __asm__ __volatile__(\"lock; cmpxchg %3, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(expected), \"d\"(desired) : \"cc\");\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n            return result;\n        }\n        static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            ma_uint8 result = 0;\n            (void)order;\n        #if defined(MA_X86) || defined(MA_X64)\n            __asm__ __volatile__(\"lock; xchg %1, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(src));\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n            return result;\n        }\n        static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            ma_uint16 result = 0;\n            (void)order;\n        #if defined(MA_X86) || defined(MA_X64)\n            __asm__ __volatile__(\"lock; xchg %1, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(src));\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n            return result;\n        }\n        static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            ma_uint32 result;\n            (void)order;\n        #if defined(MA_X86) || defined(MA_X64)\n            __asm__ __volatile__(\"lock; xchg %1, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(src));\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n            return result;\n        }\n        static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            ma_uint64 result;\n            (void)order;\n        #if defined(MA_X86)\n            do {\n                result = *dst;\n            } while (ma_atomic_compare_and_swap_64(dst, result, src) != result);\n        #elif defined(MA_X64)\n            __asm__ __volatile__(\"lock; xchg %1, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(src));\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n            return result;\n        }\n        static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            ma_uint8 result;\n            (void)order;\n        #if defined(MA_X86) || defined(MA_X64)\n            __asm__ __volatile__(\"lock; xadd %1, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(src) : \"cc\");\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n            return result;\n        }\n        static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            ma_uint16 result;\n            (void)order;\n        #if defined(MA_X86) || defined(MA_X64)\n            __asm__ __volatile__(\"lock; xadd %1, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(src) : \"cc\");\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n            return result;\n        }\n        static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            ma_uint32 result;\n            (void)order;\n        #if defined(MA_X86) || defined(MA_X64)\n            __asm__ __volatile__(\"lock; xadd %1, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(src) : \"cc\");\n        #else\n            #error Unsupported architecture. Please submit a feature request.\n        #endif\n            return result;\n        }\n        static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n        #if defined(MA_X86)\n            ma_uint64 oldValue;\n            ma_uint64 newValue;\n            (void)order;\n            do {\n                oldValue = *dst;\n                newValue = oldValue + src;\n            } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);\n            return oldValue;\n        #elif defined(MA_X64)\n            ma_uint64 result;\n            (void)order;\n            __asm__ __volatile__(\"lock; xadd %1, %0\" : \"+m\"(*dst), \"=a\"(result) : \"a\"(src) : \"cc\");\n            return result;\n        #endif\n        }\n        static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            ma_uint8 oldValue;\n            ma_uint8 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint8)(oldValue - src);\n            } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            ma_uint16 oldValue;\n            ma_uint16 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint16)(oldValue - src);\n            } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            ma_uint32 oldValue;\n            ma_uint32 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue - src;\n            } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            ma_uint64 oldValue;\n            ma_uint64 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue - src;\n            } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            ma_uint8 oldValue;\n            ma_uint8 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint8)(oldValue & src);\n            } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            ma_uint16 oldValue;\n            ma_uint16 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint16)(oldValue & src);\n            } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            ma_uint32 oldValue;\n            ma_uint32 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue & src;\n            } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            ma_uint64 oldValue;\n            ma_uint64 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue & src;\n            } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            ma_uint8 oldValue;\n            ma_uint8 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint8)(oldValue ^ src);\n            } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            ma_uint16 oldValue;\n            ma_uint16 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint16)(oldValue ^ src);\n            } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            ma_uint32 oldValue;\n            ma_uint32 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue ^ src;\n            } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            ma_uint64 oldValue;\n            ma_uint64 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue ^ src;\n            } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)\n        {\n            ma_uint8 oldValue;\n            ma_uint8 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint8)(oldValue | src);\n            } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)\n        {\n            ma_uint16 oldValue;\n            ma_uint16 newValue;\n            do {\n                oldValue = *dst;\n                newValue = (ma_uint16)(oldValue | src);\n            } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)\n        {\n            ma_uint32 oldValue;\n            ma_uint32 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue | src;\n            } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n        static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)\n        {\n            ma_uint64 oldValue;\n            ma_uint64 newValue;\n            do {\n                oldValue = *dst;\n                newValue = oldValue | src;\n            } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);\n            (void)order;\n            return oldValue;\n        }\n    #endif\n    #define ma_atomic_signal_fence(order)                           ma_atomic_thread_fence(order)\n    static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order)\n    {\n        (void)order;\n        return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0);\n    }\n    static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order)\n    {\n        (void)order;\n        return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0);\n    }\n    static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order)\n    {\n        (void)order;\n        return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0);\n    }\n    static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order)\n    {\n        (void)order;\n        return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0);\n    }\n    #define ma_atomic_store_explicit_8( dst, src, order)            (void)ma_atomic_exchange_explicit_8 (dst, src, order)\n    #define ma_atomic_store_explicit_16(dst, src, order)            (void)ma_atomic_exchange_explicit_16(dst, src, order)\n    #define ma_atomic_store_explicit_32(dst, src, order)            (void)ma_atomic_exchange_explicit_32(dst, src, order)\n    #define ma_atomic_store_explicit_64(dst, src, order)            (void)ma_atomic_exchange_explicit_64(dst, src, order)\n    #define ma_atomic_test_and_set_explicit_8( dst, order)          ma_atomic_exchange_explicit_8 (dst, 1, order)\n    #define ma_atomic_test_and_set_explicit_16(dst, order)          ma_atomic_exchange_explicit_16(dst, 1, order)\n    #define ma_atomic_test_and_set_explicit_32(dst, order)          ma_atomic_exchange_explicit_32(dst, 1, order)\n    #define ma_atomic_test_and_set_explicit_64(dst, order)          ma_atomic_exchange_explicit_64(dst, 1, order)\n    #define ma_atomic_clear_explicit_8( dst, order)                 ma_atomic_store_explicit_8 (dst, 0, order)\n    #define ma_atomic_clear_explicit_16(dst, order)                 ma_atomic_store_explicit_16(dst, 0, order)\n    #define ma_atomic_clear_explicit_32(dst, order)                 ma_atomic_store_explicit_32(dst, 0, order)\n    #define ma_atomic_clear_explicit_64(dst, order)                 ma_atomic_store_explicit_64(dst, 0, order)\n    typedef ma_uint8 ma_atomic_flag;\n    #define ma_atomic_flag_test_and_set_explicit(ptr, order)        (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order)\n    #define ma_atomic_flag_clear_explicit(ptr, order)               ma_atomic_clear_explicit_8(ptr, order)\n    #define c89atoimc_flag_load_explicit(ptr, order)                ma_atomic_load_explicit_8(ptr, order)\n#endif\n#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE)\n    #if defined(MA_ATOMIC_HAS_8)\n        static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n        {\n            ma_uint8 expectedValue;\n            ma_uint8 result;\n            (void)successOrder;\n            (void)failureOrder;\n            expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst);\n            result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired);\n            if (result == expectedValue) {\n                return 1;\n            } else {\n                ma_atomic_store_explicit_8(expected, result, failureOrder);\n                return 0;\n            }\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_16)\n        static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n        {\n            ma_uint16 expectedValue;\n            ma_uint16 result;\n            (void)successOrder;\n            (void)failureOrder;\n            expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst);\n            result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired);\n            if (result == expectedValue) {\n                return 1;\n            } else {\n                ma_atomic_store_explicit_16(expected, result, failureOrder);\n                return 0;\n            }\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_32)\n        static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n        {\n            ma_uint32 expectedValue;\n            ma_uint32 result;\n            (void)successOrder;\n            (void)failureOrder;\n            expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst);\n            result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired);\n            if (result == expectedValue) {\n                return 1;\n            } else {\n                ma_atomic_store_explicit_32(expected, result, failureOrder);\n                return 0;\n            }\n        }\n    #endif\n    #if defined(MA_ATOMIC_HAS_64)\n        static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n        {\n            ma_uint64 expectedValue;\n            ma_uint64 result;\n            (void)successOrder;\n            (void)failureOrder;\n            expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst);\n            result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired);\n            if (result == expectedValue) {\n                return 1;\n            } else {\n                ma_atomic_store_explicit_64(expected, result, failureOrder);\n                return 0;\n            }\n        }\n    #endif\n    #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder)\n    #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder)\n    #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder)\n    #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder)\n#endif\n#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE)\n    static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr)\n    {\n        (void)ptr;\n        return 1;\n    }\n    static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr)\n    {\n        (void)ptr;\n        return 1;\n    }\n    static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr)\n    {\n        (void)ptr;\n        return 1;\n    }\n    static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr)\n    {\n        (void)ptr;\n    #if defined(MA_64BIT)\n        return 1;\n    #else\n        #if defined(MA_X86) || defined(MA_X64)\n            return 1;\n        #else\n            return 0;\n        #endif\n    #endif\n    }\n#endif\n#if defined(MA_64BIT)\n    static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr)\n    {\n        return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr);\n    }\n    static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order)\n    {\n        return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order);\n    }\n    static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)\n    {\n        ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order);\n    }\n    static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)\n    {\n        return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order);\n    }\n    static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n    {\n        return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder);\n    }\n    static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n    {\n        return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder);\n    }\n    static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired)\n    {\n        return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired);\n    }\n#elif defined(MA_32BIT)\n    static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr)\n    {\n        return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr);\n    }\n    static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order)\n    {\n        return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order);\n    }\n    static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)\n    {\n        ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order);\n    }\n    static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)\n    {\n        return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order);\n    }\n    static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n    {\n        return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder);\n    }\n    static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n    {\n        return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder);\n    }\n    static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired)\n    {\n        return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired);\n    }\n#else\n    #error Unsupported architecture.\n#endif\n#define ma_atomic_flag_test_and_set(ptr)                                ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_flag_clear(ptr)                                       ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_store_ptr(dst, src)                                   ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_load_ptr(ptr)                                         ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_exchange_ptr(dst, src)                                ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired)   ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired)     ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_test_and_set_8( ptr)                                  ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_test_and_set_16(ptr)                                  ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_test_and_set_32(ptr)                                  ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_test_and_set_64(ptr)                                  ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_clear_8( ptr)                                         ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_clear_16(ptr)                                         ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_clear_32(ptr)                                         ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_clear_64(ptr)                                         ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_store_8( dst, src)                                    ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_store_16(dst, src)                                    ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_store_32(dst, src)                                    ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_store_64(dst, src)                                    ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_load_8( ptr)                                          ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_load_16(ptr)                                          ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_load_32(ptr)                                          ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_load_64(ptr)                                          ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_exchange_8( dst, src)                                 ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_exchange_16(dst, src)                                 ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_exchange_32(dst, src)                                 ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_exchange_64(dst, src)                                 ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_strong_8( dst, expected, desired)    ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_strong_16(dst, expected, desired)    ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_strong_32(dst, expected, desired)    ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_strong_64(dst, expected, desired)    ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_weak_8(  dst, expected, desired)     ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_weak_16( dst, expected, desired)     ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_weak_32( dst, expected, desired)     ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_weak_64( dst, expected, desired)     ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_add_8( dst, src)                                ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_add_16(dst, src)                                ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_add_32(dst, src)                                ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_add_64(dst, src)                                ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_sub_8( dst, src)                                ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_sub_16(dst, src)                                ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_sub_32(dst, src)                                ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_sub_64(dst, src)                                ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_or_8( dst, src)                                 ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_or_16(dst, src)                                 ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_or_32(dst, src)                                 ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_or_64(dst, src)                                 ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_xor_8( dst, src)                                ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_xor_16(dst, src)                                ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_xor_32(dst, src)                                ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_xor_64(dst, src)                                ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_and_8( dst, src)                                ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_and_16(dst, src)                                ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_and_32(dst, src)                                ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_and_64(dst, src)                                ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_test_and_set_explicit_i8( ptr, order)                 (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order)\n#define ma_atomic_test_and_set_explicit_i16(ptr, order)                 (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order)\n#define ma_atomic_test_and_set_explicit_i32(ptr, order)                 (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order)\n#define ma_atomic_test_and_set_explicit_i64(ptr, order)                 (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order)\n#define ma_atomic_clear_explicit_i8( ptr, order)                        ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order)\n#define ma_atomic_clear_explicit_i16(ptr, order)                        ma_atomic_clear_explicit_16((ma_uint16*)ptr, order)\n#define ma_atomic_clear_explicit_i32(ptr, order)                        ma_atomic_clear_explicit_32((ma_uint32*)ptr, order)\n#define ma_atomic_clear_explicit_i64(ptr, order)                        ma_atomic_clear_explicit_64((ma_uint64*)ptr, order)\n#define ma_atomic_store_explicit_i8( dst, src, order)                   ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)\n#define ma_atomic_store_explicit_i16(dst, src, order)                   ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)\n#define ma_atomic_store_explicit_i32(dst, src, order)                   ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)\n#define ma_atomic_store_explicit_i64(dst, src, order)                   ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)\n#define ma_atomic_load_explicit_i8( ptr, order)                         (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order)\n#define ma_atomic_load_explicit_i16(ptr, order)                         (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order)\n#define ma_atomic_load_explicit_i32(ptr, order)                         (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order)\n#define ma_atomic_load_explicit_i64(ptr, order)                         (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order)\n#define ma_atomic_exchange_explicit_i8( dst, src, order)                (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order)\n#define ma_atomic_exchange_explicit_i16(dst, src, order)                (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)\n#define ma_atomic_exchange_explicit_i32(dst, src, order)                (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)\n#define ma_atomic_exchange_explicit_i64(dst, src, order)                (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)\n#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder)  ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder)\n#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder)  ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder)\n#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder)  ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder)\n#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder)  ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder)\n#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder)    ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder)\n#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder)    ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder)\n#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder)    ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder)\n#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder)    ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder)\n#define ma_atomic_fetch_add_explicit_i8( dst, src, order)               (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)\n#define ma_atomic_fetch_add_explicit_i16(dst, src, order)               (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)\n#define ma_atomic_fetch_add_explicit_i32(dst, src, order)               (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)\n#define ma_atomic_fetch_add_explicit_i64(dst, src, order)               (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)\n#define ma_atomic_fetch_sub_explicit_i8( dst, src, order)               (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)\n#define ma_atomic_fetch_sub_explicit_i16(dst, src, order)               (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)\n#define ma_atomic_fetch_sub_explicit_i32(dst, src, order)               (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)\n#define ma_atomic_fetch_sub_explicit_i64(dst, src, order)               (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)\n#define ma_atomic_fetch_or_explicit_i8( dst, src, order)                (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)\n#define ma_atomic_fetch_or_explicit_i16(dst, src, order)                (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)\n#define ma_atomic_fetch_or_explicit_i32(dst, src, order)                (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)\n#define ma_atomic_fetch_or_explicit_i64(dst, src, order)                (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)\n#define ma_atomic_fetch_xor_explicit_i8( dst, src, order)               (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)\n#define ma_atomic_fetch_xor_explicit_i16(dst, src, order)               (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)\n#define ma_atomic_fetch_xor_explicit_i32(dst, src, order)               (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)\n#define ma_atomic_fetch_xor_explicit_i64(dst, src, order)               (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)\n#define ma_atomic_fetch_and_explicit_i8( dst, src, order)               (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)\n#define ma_atomic_fetch_and_explicit_i16(dst, src, order)               (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)\n#define ma_atomic_fetch_and_explicit_i32(dst, src, order)               (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)\n#define ma_atomic_fetch_and_explicit_i64(dst, src, order)               (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)\n#define ma_atomic_test_and_set_i8( ptr)                                 ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_test_and_set_i16(ptr)                                 ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_test_and_set_i32(ptr)                                 ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_test_and_set_i64(ptr)                                 ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_clear_i8( ptr)                                        ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_clear_i16(ptr)                                        ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_clear_i32(ptr)                                        ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_clear_i64(ptr)                                        ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_store_i8( dst, src)                                   ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_store_i16(dst, src)                                   ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_store_i32(dst, src)                                   ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_store_i64(dst, src)                                   ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_load_i8( ptr)                                         ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_load_i16(ptr)                                         ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_load_i32(ptr)                                         ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_load_i64(ptr)                                         ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_exchange_i8( dst, src)                                ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_exchange_i16(dst, src)                                ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_exchange_i32(dst, src)                                ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_exchange_i64(dst, src)                                ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired)   ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired)   ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired)   ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired)   ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired)     ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired)     ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired)     ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired)     ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_add_i8( dst, src)                               ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_add_i16(dst, src)                               ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_add_i32(dst, src)                               ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_add_i64(dst, src)                               ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_sub_i8( dst, src)                               ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_sub_i16(dst, src)                               ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_sub_i32(dst, src)                               ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_sub_i64(dst, src)                               ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_or_i8( dst, src)                                ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_or_i16(dst, src)                                ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_or_i32(dst, src)                                ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_or_i64(dst, src)                                ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_xor_i8( dst, src)                               ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_xor_i16(dst, src)                               ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_xor_i32(dst, src)                               ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_xor_i64(dst, src)                               ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_and_i8( dst, src)                               ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_and_i16(dst, src)                               ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_and_i32(dst, src)                               ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_and_i64(dst, src)                               ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired)         (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired)\n#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired)         (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired)\n#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired)         (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired)\n#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired)         (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired)\ntypedef union\n{\n    ma_uint32 i;\n    float f;\n} ma_atomic_if32;\ntypedef union\n{\n    ma_uint64 i;\n    double f;\n} ma_atomic_if64;\n#define ma_atomic_clear_explicit_f32(ptr, order)                        ma_atomic_clear_explicit_32((ma_uint32*)ptr, order)\n#define ma_atomic_clear_explicit_f64(ptr, order)                        ma_atomic_clear_explicit_64((ma_uint64*)ptr, order)\nstatic MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)\n{\n    ma_atomic_if32 x;\n    x.f = src;\n    ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order);\n}\nstatic MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)\n{\n    ma_atomic_if64 x;\n    x.f = src;\n    ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order);\n}\nstatic MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order)\n{\n    ma_atomic_if32 r;\n    r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order);\n    return r.f;\n}\nstatic MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order)\n{\n    ma_atomic_if64 r;\n    r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order);\n    return r.f;\n}\nstatic MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)\n{\n    ma_atomic_if32 r;\n    ma_atomic_if32 x;\n    x.f = src;\n    r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order);\n    return r.f;\n}\nstatic MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)\n{\n    ma_atomic_if64 r;\n    ma_atomic_if64 x;\n    x.f = src;\n    r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order);\n    return r.f;\n}\nstatic MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n{\n    ma_atomic_if32 d;\n    d.f = desired;\n    return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder);\n}\nstatic MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n{\n    ma_atomic_if64 d;\n    d.f = desired;\n    return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder);\n}\nstatic MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n{\n    ma_atomic_if32 d;\n    d.f = desired;\n    return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder);\n}\nstatic MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)\n{\n    ma_atomic_if64 d;\n    d.f = desired;\n    return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder);\n}\nstatic MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)\n{\n    ma_atomic_if32 r;\n    ma_atomic_if32 x;\n    x.f = src;\n    r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order);\n    return r.f;\n}\nstatic MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)\n{\n    ma_atomic_if64 r;\n    ma_atomic_if64 x;\n    x.f = src;\n    r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order);\n    return r.f;\n}\nstatic MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)\n{\n    ma_atomic_if32 r;\n    ma_atomic_if32 x;\n    x.f = src;\n    r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order);\n    return r.f;\n}\nstatic MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)\n{\n    ma_atomic_if64 r;\n    ma_atomic_if64 x;\n    x.f = src;\n    r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order);\n    return r.f;\n}\nstatic MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)\n{\n    ma_atomic_if32 r;\n    ma_atomic_if32 x;\n    x.f = src;\n    r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order);\n    return r.f;\n}\nstatic MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)\n{\n    ma_atomic_if64 r;\n    ma_atomic_if64 x;\n    x.f = src;\n    r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order);\n    return r.f;\n}\nstatic MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)\n{\n    ma_atomic_if32 r;\n    ma_atomic_if32 x;\n    x.f = src;\n    r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order);\n    return r.f;\n}\nstatic MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)\n{\n    ma_atomic_if64 r;\n    ma_atomic_if64 x;\n    x.f = src;\n    r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order);\n    return r.f;\n}\nstatic MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)\n{\n    ma_atomic_if32 r;\n    ma_atomic_if32 x;\n    x.f = src;\n    r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order);\n    return r.f;\n}\nstatic MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)\n{\n    ma_atomic_if64 r;\n    ma_atomic_if64 x;\n    x.f = src;\n    r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order);\n    return r.f;\n}\n#define ma_atomic_clear_f32(ptr)                                        (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_clear_f64(ptr)                                        (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_store_f32(dst, src)                                   ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_store_f64(dst, src)                                   ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_load_f32(ptr)                                         (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_load_f64(ptr)                                         (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_exchange_f32(dst, src)                                (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_exchange_f64(dst, src)                                (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired)   ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired)   ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired)     ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired)     ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_add_f32(dst, src)                               ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_add_f64(dst, src)                               ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_sub_f32(dst, src)                               ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_sub_f64(dst, src)                               ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_or_f32(dst, src)                                ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_or_f64(dst, src)                                ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_xor_f32(dst, src)                               ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_xor_f64(dst, src)                               ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_and_f32(dst, src)                               ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)\n#define ma_atomic_fetch_and_f64(dst, src)                               ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)\nstatic MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired)\n{\n    ma_atomic_if32 r;\n    ma_atomic_if32 e, d;\n    e.f = expected;\n    d.f = desired;\n    r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i);\n    return r.f;\n}\nstatic MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired)\n{\n    ma_atomic_if64 r;\n    ma_atomic_if64 e, d;\n    e.f = expected;\n    d.f = desired;\n    r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i);\n    return r.f;\n}\ntypedef ma_atomic_flag ma_atomic_spinlock;\nstatic MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock)\n{\n    for (;;) {\n        if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) {\n            break;\n        }\n        while (c89atoimc_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) {\n        }\n    }\n}\nstatic MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock)\n{\n    ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release);\n}\n#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n    #pragma GCC diagnostic pop\n#endif\n#if defined(__cplusplus)\n}\n#endif\n#endif\n/* ma_atomic.h end */\n\n#define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \\\n    static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \\\n    { \\\n        return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \\\n    } \\\n    static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \\\n    { \\\n        ma_atomic_store_##c89TypeExtension(&x->value, value); \\\n    } \\\n    static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \\\n    { \\\n        return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \\\n    } \\\n    static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \\\n    { \\\n        return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \\\n    } \\\n    static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \\\n    { \\\n        return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \\\n    } \\\n    static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \\\n    { \\\n        return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \\\n    } \\\n    static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \\\n    { \\\n        return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \\\n    } \\\n    static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \\\n    { \\\n        return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \\\n    } \\\n    static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \\\n    { \\\n        return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \\\n    } \\\n    static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \\\n    { \\\n        return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \\\n    } \\\n\n#define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \\\n    static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \\\n    { \\\n        return ma_atomic_load_ptr((void**)&x->value); \\\n    } \\\n    static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \\\n    { \\\n        ma_atomic_store_ptr((void**)&x->value, (void*)value); \\\n    } \\\n    static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \\\n    { \\\n        return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \\\n    } \\\n    static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \\\n    { \\\n        return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \\\n    } \\\n    static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \\\n    { \\\n        return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \\\n    } \\\n\nMA_ATOMIC_SAFE_TYPE_IMPL(32,  uint32)\nMA_ATOMIC_SAFE_TYPE_IMPL(i32, int32)\nMA_ATOMIC_SAFE_TYPE_IMPL(64,  uint64)\nMA_ATOMIC_SAFE_TYPE_IMPL(f32, float)\nMA_ATOMIC_SAFE_TYPE_IMPL(32,  bool32)\n\n#if !defined(MA_NO_DEVICE_IO)\nMA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state)\n#endif\n\n\nMA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn)\n{\n    /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */\n    ma_uint64 outputFrameCount;\n    ma_uint64 preliminaryInputFrameCountFromFrac;\n    ma_uint64 preliminaryInputFrameCount;\n\n    if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) {\n        return 0;\n    }\n\n    if (sampleRateOut == sampleRateIn) {\n        return frameCountIn;\n    }\n\n    outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn;\n\n    preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut;\n    preliminaryInputFrameCount         = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac;\n\n    if (preliminaryInputFrameCount <= frameCountIn) {\n        outputFrameCount += 1;\n    }\n\n    return outputFrameCount;\n}\n\n#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE\n#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE     4096\n#endif\n\n\n\n#if defined(MA_WIN32)\nstatic ma_result ma_result_from_GetLastError(DWORD error)\n{\n    switch (error)\n    {\n        case ERROR_SUCCESS:             return MA_SUCCESS;\n        case ERROR_PATH_NOT_FOUND:      return MA_DOES_NOT_EXIST;\n        case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES;\n        case ERROR_NOT_ENOUGH_MEMORY:   return MA_OUT_OF_MEMORY;\n        case ERROR_DISK_FULL:           return MA_NO_SPACE;\n        case ERROR_HANDLE_EOF:          return MA_AT_END;\n        case ERROR_NEGATIVE_SEEK:       return MA_BAD_SEEK;\n        case ERROR_INVALID_PARAMETER:   return MA_INVALID_ARGS;\n        case ERROR_ACCESS_DENIED:       return MA_ACCESS_DENIED;\n        case ERROR_SEM_TIMEOUT:         return MA_TIMEOUT;\n        case ERROR_FILE_NOT_FOUND:      return MA_DOES_NOT_EXIST;\n        default: break;\n    }\n\n    return MA_ERROR;\n}\n#endif  /* MA_WIN32 */\n\n\n/*******************************************************************************\n\nThreading\n\n*******************************************************************************/\nstatic MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield)\n{\n    if (pSpinlock == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    for (;;) {\n        if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) {\n            break;\n        }\n\n        while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) {\n            if (yield) {\n                ma_yield();\n            }\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock)\n{\n    return ma_spinlock_lock_ex(pSpinlock, MA_TRUE);\n}\n\nMA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock)\n{\n    return ma_spinlock_lock_ex(pSpinlock, MA_FALSE);\n}\n\nMA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock)\n{\n    if (pSpinlock == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release);\n    return MA_SUCCESS;\n}\n\n\n#ifndef MA_NO_THREADING\n#if defined(MA_POSIX)\n    #define MA_THREADCALL\n    typedef void* ma_thread_result;\n#elif defined(MA_WIN32)\n    #define MA_THREADCALL WINAPI\n    typedef unsigned long ma_thread_result;\n#endif\n\ntypedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);\n\n#ifdef MA_POSIX\nstatic ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)\n{\n    int result;\n    pthread_attr_t* pAttr = NULL;\n\n#if !defined(__EMSCRIPTEN__)\n    /* Try setting the thread priority. It's not critical if anything fails here. */\n    pthread_attr_t attr;\n    if (pthread_attr_init(&attr) == 0) {\n        int scheduler = -1;\n\n        /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */\n        pAttr = &attr;\n\n        /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */\n        #if !defined(MA_BEOS)\n        {\n            if (priority == ma_thread_priority_idle) {\n            #ifdef SCHED_IDLE\n                if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) {\n                    scheduler = SCHED_IDLE;\n                }\n            #endif\n            } else if (priority == ma_thread_priority_realtime) {\n            #ifdef SCHED_FIFO\n                if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) {\n                    scheduler = SCHED_FIFO;\n                }\n            #endif\n            #ifdef MA_LINUX\n            } else {\n                scheduler = sched_getscheduler(0);\n            #endif\n            }\n        }\n        #endif\n\n        if (stackSize > 0) {\n            pthread_attr_setstacksize(&attr, stackSize);\n        }\n\n        if (scheduler != -1) {\n            int priorityMin = sched_get_priority_min(scheduler);\n            int priorityMax = sched_get_priority_max(scheduler);\n            int priorityStep = (priorityMax - priorityMin) / 7;  /* 7 = number of priorities supported by miniaudio. */\n\n            struct sched_param sched;\n            if (pthread_attr_getschedparam(&attr, &sched) == 0) {\n                if (priority == ma_thread_priority_idle) {\n                    sched.sched_priority = priorityMin;\n                } else if (priority == ma_thread_priority_realtime) {\n                    sched.sched_priority = priorityMax;\n                } else {\n                    sched.sched_priority += ((int)priority + 5) * priorityStep;  /* +5 because the lowest priority is -5. */\n                    if (sched.sched_priority < priorityMin) {\n                        sched.sched_priority = priorityMin;\n                    }\n                    if (sched.sched_priority > priorityMax) {\n                        sched.sched_priority = priorityMax;\n                    }\n                }\n\n                /* I'm not treating a failure of setting the priority as a critical error so not checking the return value here. */\n                pthread_attr_setschedparam(&attr, &sched);\n            }\n        }\n    }\n#else\n    /* It's the emscripten build. We'll have a few unused parameters. */\n    (void)priority;\n    (void)stackSize;\n#endif\n\n    result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData);\n\n    /* The thread attributes object is no longer required. */\n    if (pAttr != NULL) {\n        pthread_attr_destroy(pAttr);\n    }\n\n    if (result != 0) {\n        return ma_result_from_errno(result);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_thread_wait__posix(ma_thread* pThread)\n{\n    pthread_join((pthread_t)*pThread, NULL);\n}\n\n\nstatic ma_result ma_mutex_init__posix(ma_mutex* pMutex)\n{\n    int result;\n\n    if (pMutex == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pMutex);\n\n    result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL);\n    if (result != 0) {\n        return ma_result_from_errno(result);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_mutex_uninit__posix(ma_mutex* pMutex)\n{\n    pthread_mutex_destroy((pthread_mutex_t*)pMutex);\n}\n\nstatic void ma_mutex_lock__posix(ma_mutex* pMutex)\n{\n    pthread_mutex_lock((pthread_mutex_t*)pMutex);\n}\n\nstatic void ma_mutex_unlock__posix(ma_mutex* pMutex)\n{\n    pthread_mutex_unlock((pthread_mutex_t*)pMutex);\n}\n\n\nstatic ma_result ma_event_init__posix(ma_event* pEvent)\n{\n    int result;\n\n    result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL);\n    if (result != 0) {\n        return ma_result_from_errno(result);\n    }\n\n    result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL);\n    if (result != 0) {\n        pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);\n        return ma_result_from_errno(result);\n    }\n\n    pEvent->value = 0;\n    return MA_SUCCESS;\n}\n\nstatic void ma_event_uninit__posix(ma_event* pEvent)\n{\n    pthread_cond_destroy((pthread_cond_t*)&pEvent->cond);\n    pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);\n}\n\nstatic ma_result ma_event_wait__posix(ma_event* pEvent)\n{\n    pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);\n    {\n        while (pEvent->value == 0) {\n            pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock);\n        }\n        pEvent->value = 0;  /* Auto-reset. */\n    }\n    pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_event_signal__posix(ma_event* pEvent)\n{\n    pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);\n    {\n        pEvent->value = 1;\n        pthread_cond_signal((pthread_cond_t*)&pEvent->cond);\n    }\n    pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore)\n{\n    int result;\n\n    if (pSemaphore == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pSemaphore->value = initialValue;\n\n    result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL);\n    if (result != 0) {\n        return ma_result_from_errno(result);  /* Failed to create mutex. */\n    }\n\n    result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL);\n    if (result != 0) {\n        pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);\n        return ma_result_from_errno(result);  /* Failed to create condition variable. */\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore)\n{\n    if (pSemaphore == NULL) {\n        return;\n    }\n\n    pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond);\n    pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);\n}\n\nstatic ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore)\n{\n    if (pSemaphore == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);\n    {\n        /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */\n        while (pSemaphore->value == 0) {\n            pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock);\n        }\n\n        pSemaphore->value -= 1;\n    }\n    pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore)\n{\n    if (pSemaphore == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);\n    {\n        pSemaphore->value += 1;\n        pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond);\n    }\n    pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);\n\n    return MA_SUCCESS;\n}\n#elif defined(MA_WIN32)\nstatic int ma_thread_priority_to_win32(ma_thread_priority priority)\n{\n    switch (priority) {\n        case ma_thread_priority_idle:     return THREAD_PRIORITY_IDLE;\n        case ma_thread_priority_lowest:   return THREAD_PRIORITY_LOWEST;\n        case ma_thread_priority_low:      return THREAD_PRIORITY_BELOW_NORMAL;\n        case ma_thread_priority_normal:   return THREAD_PRIORITY_NORMAL;\n        case ma_thread_priority_high:     return THREAD_PRIORITY_ABOVE_NORMAL;\n        case ma_thread_priority_highest:  return THREAD_PRIORITY_HIGHEST;\n        case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;\n        default:                          return THREAD_PRIORITY_NORMAL;\n    }\n}\n\nstatic ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)\n{\n    DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */\n\n    *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID);\n    if (*pThread == NULL) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority));\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_thread_wait__win32(ma_thread* pThread)\n{\n    WaitForSingleObject((HANDLE)*pThread, INFINITE);\n    CloseHandle((HANDLE)*pThread);\n}\n\n\nstatic ma_result ma_mutex_init__win32(ma_mutex* pMutex)\n{\n    *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL);\n    if (*pMutex == NULL) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_mutex_uninit__win32(ma_mutex* pMutex)\n{\n    CloseHandle((HANDLE)*pMutex);\n}\n\nstatic void ma_mutex_lock__win32(ma_mutex* pMutex)\n{\n    WaitForSingleObject((HANDLE)*pMutex, INFINITE);\n}\n\nstatic void ma_mutex_unlock__win32(ma_mutex* pMutex)\n{\n    SetEvent((HANDLE)*pMutex);\n}\n\n\nstatic ma_result ma_event_init__win32(ma_event* pEvent)\n{\n    *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL);\n    if (*pEvent == NULL) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_event_uninit__win32(ma_event* pEvent)\n{\n    CloseHandle((HANDLE)*pEvent);\n}\n\nstatic ma_result ma_event_wait__win32(ma_event* pEvent)\n{\n    DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE);\n    if (result == WAIT_OBJECT_0) {\n        return MA_SUCCESS;\n    }\n\n    if (result == WAIT_TIMEOUT) {\n        return MA_TIMEOUT;\n    }\n\n    return ma_result_from_GetLastError(GetLastError());\n}\n\nstatic ma_result ma_event_signal__win32(ma_event* pEvent)\n{\n    BOOL result = SetEvent((HANDLE)*pEvent);\n    if (result == 0) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore)\n{\n    *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL);\n    if (*pSemaphore == NULL) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore)\n{\n    CloseHandle((HANDLE)*pSemaphore);\n}\n\nstatic ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore)\n{\n    DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE);\n    if (result == WAIT_OBJECT_0) {\n        return MA_SUCCESS;\n    }\n\n    if (result == WAIT_TIMEOUT) {\n        return MA_TIMEOUT;\n    }\n\n    return ma_result_from_GetLastError(GetLastError());\n}\n\nstatic ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore)\n{\n    BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL);\n    if (result == 0) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    return MA_SUCCESS;\n}\n#endif\n\ntypedef struct\n{\n    ma_thread_entry_proc entryProc;\n    void* pData;\n    ma_allocation_callbacks allocationCallbacks;\n} ma_thread_proxy_data;\n\nstatic ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData)\n{\n    ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData;\n    ma_thread_entry_proc entryProc;\n    void* pEntryProcData;\n    ma_thread_result result;\n\n    #if defined(MA_ON_THREAD_ENTRY)\n        MA_ON_THREAD_ENTRY\n    #endif\n\n    entryProc = pProxyData->entryProc;\n    pEntryProcData = pProxyData->pData;\n\n    /* Free the proxy data before getting into the real thread entry proc. */\n    ma_free(pProxyData, &pProxyData->allocationCallbacks);\n\n    result = entryProc(pEntryProcData);\n\n    #if defined(MA_ON_THREAD_EXIT)\n        MA_ON_THREAD_EXIT\n    #endif\n\n    return result;\n}\n\nstatic ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_result result;\n    ma_thread_proxy_data* pProxyData;\n\n    if (pThread == NULL || entryProc == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks);   /* Will be freed by the proxy entry proc. */\n    if (pProxyData == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n#if defined(MA_THREAD_DEFAULT_STACK_SIZE)\n    if (stackSize == 0) {\n        stackSize = MA_THREAD_DEFAULT_STACK_SIZE;\n    }\n#endif\n\n    pProxyData->entryProc = entryProc;\n    pProxyData->pData     = pData;\n    ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks);\n\n#if defined(MA_POSIX)\n    result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);\n#elif defined(MA_WIN32)\n    result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);\n#endif\n\n    if (result != MA_SUCCESS) {\n        ma_free(pProxyData, pAllocationCallbacks);\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_thread_wait(ma_thread* pThread)\n{\n    if (pThread == NULL) {\n        return;\n    }\n\n#if defined(MA_POSIX)\n    ma_thread_wait__posix(pThread);\n#elif defined(MA_WIN32)\n    ma_thread_wait__win32(pThread);\n#endif\n}\n\n\nMA_API ma_result ma_mutex_init(ma_mutex* pMutex)\n{\n    if (pMutex == NULL) {\n        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_POSIX)\n    return ma_mutex_init__posix(pMutex);\n#elif defined(MA_WIN32)\n    return ma_mutex_init__win32(pMutex);\n#endif\n}\n\nMA_API void ma_mutex_uninit(ma_mutex* pMutex)\n{\n    if (pMutex == NULL) {\n        return;\n    }\n\n#if defined(MA_POSIX)\n    ma_mutex_uninit__posix(pMutex);\n#elif defined(MA_WIN32)\n    ma_mutex_uninit__win32(pMutex);\n#endif\n}\n\nMA_API void ma_mutex_lock(ma_mutex* pMutex)\n{\n    if (pMutex == NULL) {\n        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */\n        return;\n    }\n\n#if defined(MA_POSIX)\n    ma_mutex_lock__posix(pMutex);\n#elif defined(MA_WIN32)\n    ma_mutex_lock__win32(pMutex);\n#endif\n}\n\nMA_API void ma_mutex_unlock(ma_mutex* pMutex)\n{\n    if (pMutex == NULL) {\n        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */\n        return;\n    }\n\n#if defined(MA_POSIX)\n    ma_mutex_unlock__posix(pMutex);\n#elif defined(MA_WIN32)\n    ma_mutex_unlock__win32(pMutex);\n#endif\n}\n\n\nMA_API ma_result ma_event_init(ma_event* pEvent)\n{\n    if (pEvent == NULL) {\n        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_POSIX)\n    return ma_event_init__posix(pEvent);\n#elif defined(MA_WIN32)\n    return ma_event_init__win32(pEvent);\n#endif\n}\n\n#if 0\nstatic ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_result result;\n    ma_event* pEvent;\n\n    if (ppEvent == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *ppEvent = NULL;\n\n    pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks);\n    if (pEvent == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_event_init(pEvent);\n    if (result != MA_SUCCESS) {\n        ma_free(pEvent, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppEvent = pEvent;\n    return result;\n}\n#endif\n\nMA_API void ma_event_uninit(ma_event* pEvent)\n{\n    if (pEvent == NULL) {\n        return;\n    }\n\n#if defined(MA_POSIX)\n    ma_event_uninit__posix(pEvent);\n#elif defined(MA_WIN32)\n    ma_event_uninit__win32(pEvent);\n#endif\n}\n\n#if 0\nstatic void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pEvent == NULL) {\n        return;\n    }\n\n    ma_event_uninit(pEvent);\n    ma_free(pEvent, pAllocationCallbacks);\n}\n#endif\n\nMA_API ma_result ma_event_wait(ma_event* pEvent)\n{\n    if (pEvent == NULL) {\n        MA_ASSERT(MA_FALSE);    /* Fire an assert to the caller is aware of this bug. */\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_POSIX)\n    return ma_event_wait__posix(pEvent);\n#elif defined(MA_WIN32)\n    return ma_event_wait__win32(pEvent);\n#endif\n}\n\nMA_API ma_result ma_event_signal(ma_event* pEvent)\n{\n    if (pEvent == NULL) {\n        MA_ASSERT(MA_FALSE);    /* Fire an assert to the caller is aware of this bug. */\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_POSIX)\n    return ma_event_signal__posix(pEvent);\n#elif defined(MA_WIN32)\n    return ma_event_signal__win32(pEvent);\n#endif\n}\n\n\nMA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore)\n{\n    if (pSemaphore == NULL) {\n        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_POSIX)\n    return ma_semaphore_init__posix(initialValue, pSemaphore);\n#elif defined(MA_WIN32)\n    return ma_semaphore_init__win32(initialValue, pSemaphore);\n#endif\n}\n\nMA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore)\n{\n    if (pSemaphore == NULL) {\n        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */\n        return;\n    }\n\n#if defined(MA_POSIX)\n    ma_semaphore_uninit__posix(pSemaphore);\n#elif defined(MA_WIN32)\n    ma_semaphore_uninit__win32(pSemaphore);\n#endif\n}\n\nMA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore)\n{\n    if (pSemaphore == NULL) {\n        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_POSIX)\n    return ma_semaphore_wait__posix(pSemaphore);\n#elif defined(MA_WIN32)\n    return ma_semaphore_wait__win32(pSemaphore);\n#endif\n}\n\nMA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore)\n{\n    if (pSemaphore == NULL) {\n        MA_ASSERT(MA_FALSE);    /* Fire an assert so the caller is aware of this bug. */\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_POSIX)\n    return ma_semaphore_release__posix(pSemaphore);\n#elif defined(MA_WIN32)\n    return ma_semaphore_release__win32(pSemaphore);\n#endif\n}\n#else\n/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */\n#ifndef MA_NO_DEVICE_IO\n#error \"MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO\";\n#endif\n#endif  /* MA_NO_THREADING */\n\n\n\n#define MA_FENCE_COUNTER_MAX    0x7FFFFFFF\n\nMA_API ma_result ma_fence_init(ma_fence* pFence)\n{\n    if (pFence == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pFence);\n    pFence->counter = 0;\n\n    #ifndef MA_NO_THREADING\n    {\n        ma_result result;\n\n        result = ma_event_init(&pFence->e);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n    #endif\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_fence_uninit(ma_fence* pFence)\n{\n    if (pFence == NULL) {\n        return;\n    }\n\n    #ifndef MA_NO_THREADING\n    {\n        ma_event_uninit(&pFence->e);\n    }\n    #endif\n\n    MA_ZERO_OBJECT(pFence);\n}\n\nMA_API ma_result ma_fence_acquire(ma_fence* pFence)\n{\n    if (pFence == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    for (;;) {\n        ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter);\n        ma_uint32 newCounter = oldCounter + 1;\n\n        /* Make sure we're not about to exceed our maximum value. */\n        if (newCounter > MA_FENCE_COUNTER_MAX) {\n            MA_ASSERT(MA_FALSE);\n            return MA_OUT_OF_RANGE;\n        }\n\n        if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {\n            return MA_SUCCESS;\n        } else {\n            if (oldCounter == MA_FENCE_COUNTER_MAX) {\n                MA_ASSERT(MA_FALSE);\n                return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */\n            }\n        }\n    }\n\n    /* Should never get here. */\n    /*return MA_SUCCESS;*/\n}\n\nMA_API ma_result ma_fence_release(ma_fence* pFence)\n{\n    if (pFence == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    for (;;) {\n        ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter);\n        ma_uint32 newCounter = oldCounter - 1;\n\n        if (oldCounter == 0) {\n            MA_ASSERT(MA_FALSE);\n            return MA_INVALID_OPERATION;    /* Acquire/release mismatch. */\n        }\n\n        if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {\n            #ifndef MA_NO_THREADING\n            {\n                if (newCounter == 0) {\n                    ma_event_signal(&pFence->e);    /* <-- ma_fence_wait() will be waiting on this. */\n                }\n            }\n            #endif\n\n            return MA_SUCCESS;\n        } else {\n            if (oldCounter == 0) {\n                MA_ASSERT(MA_FALSE);\n                return MA_INVALID_OPERATION;    /* Another thread has taken the 0 slot. Acquire/release mismatch. */\n            }\n        }\n    }\n\n    /* Should never get here. */\n    /*return MA_SUCCESS;*/\n}\n\nMA_API ma_result ma_fence_wait(ma_fence* pFence)\n{\n    if (pFence == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    for (;;) {\n        ma_uint32 counter;\n\n        counter = ma_atomic_load_32(&pFence->counter);\n        if (counter == 0) {\n            /*\n            Counter has hit zero. By the time we get here some other thread may have acquired the\n            fence again, but that is where the caller needs to take care with how they se the fence.\n            */\n            return MA_SUCCESS;\n        }\n\n        /* Getting here means the counter is > 0. We'll need to wait for something to happen. */\n        #ifndef MA_NO_THREADING\n        {\n            ma_result result;\n\n            result = ma_event_wait(&pFence->e);\n            if (result != MA_SUCCESS) {\n                return result;\n            }\n        }\n        #endif\n    }\n\n    /* Should never get here. */\n    /*return MA_INVALID_OPERATION;*/\n}\n\n\nMA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification)\n{\n    ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification;\n\n    if (pNotification == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pNotificationCallbacks->onSignal == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    pNotificationCallbacks->onSignal(pNotification);\n    return MA_INVALID_ARGS;\n}\n\n\nstatic void ma_async_notification_poll__on_signal(ma_async_notification* pNotification)\n{\n    ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE;\n}\n\nMA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll)\n{\n    if (pNotificationPoll == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal;\n    pNotificationPoll->signalled = MA_FALSE;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll)\n{\n    if (pNotificationPoll == NULL) {\n        return MA_FALSE;\n    }\n\n    return pNotificationPoll->signalled;\n}\n\n\nstatic void ma_async_notification_event__on_signal(ma_async_notification* pNotification)\n{\n    ma_async_notification_event_signal((ma_async_notification_event*)pNotification);\n}\n\nMA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent)\n{\n    if (pNotificationEvent == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal;\n\n    #ifndef MA_NO_THREADING\n    {\n        ma_result result;\n\n        result = ma_event_init(&pNotificationEvent->e);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        return MA_NOT_IMPLEMENTED;  /* Threading is disabled. */\n    }\n    #endif\n}\n\nMA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent)\n{\n    if (pNotificationEvent == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #ifndef MA_NO_THREADING\n    {\n        ma_event_uninit(&pNotificationEvent->e);\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        return MA_NOT_IMPLEMENTED;  /* Threading is disabled. */\n    }\n    #endif\n}\n\nMA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent)\n{\n    if (pNotificationEvent == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #ifndef MA_NO_THREADING\n    {\n        return ma_event_wait(&pNotificationEvent->e);\n    }\n    #else\n    {\n        return MA_NOT_IMPLEMENTED;  /* Threading is disabled. */\n    }\n    #endif\n}\n\nMA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent)\n{\n    if (pNotificationEvent == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #ifndef MA_NO_THREADING\n    {\n        return ma_event_signal(&pNotificationEvent->e);\n    }\n    #else\n    {\n        return MA_NOT_IMPLEMENTED;  /* Threading is disabled. */\n    }\n    #endif\n}\n\n\n\n/************************************************************************************************************************************************************\n\nJob Queue\n\n************************************************************************************************************************************************************/\nMA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity)\n{\n    ma_slot_allocator_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.capacity = capacity;\n\n    return config;\n}\n\n\nstatic MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity)\n{\n    ma_uint32 cap = slotCapacity / 32;\n    if ((slotCapacity % 32) != 0) {\n        cap += 1;\n    }\n\n    return cap;\n}\n\nstatic MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator)\n{\n    return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity);\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t groupsOffset;\n    size_t slotsOffset;\n} ma_slot_allocator_heap_layout;\n\nstatic ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout)\n{\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->capacity == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* Groups. */\n    pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group));\n\n    /* Slots. */\n    pHeapLayout->slotsOffset  = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32));\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_slot_allocator_heap_layout layout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_slot_allocator_get_heap_layout(pConfig, &layout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = layout.sizeInBytes;\n\n    return result;\n}\n\nMA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator)\n{\n    ma_result result;\n    ma_slot_allocator_heap_layout heapLayout;\n\n    if (pAllocator == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pAllocator);\n\n    if (pHeap == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pAllocator->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    pAllocator->pGroups  = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset);\n    pAllocator->pSlots   = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset);\n    pAllocator->capacity = pConfig->capacity;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to retrieve the size of the heap allocation. */\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pAllocator->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocator == NULL) {\n        return;\n    }\n\n    if (pAllocator->_ownsHeap) {\n        ma_free(pAllocator->_pHeap, pAllocationCallbacks);\n    }\n}\n\nMA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot)\n{\n    ma_uint32 iAttempt;\n    const ma_uint32 maxAttempts = 2;    /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */\n\n    if (pAllocator == NULL || pSlot == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) {\n        /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */\n        ma_uint32 iGroup;\n        for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) {\n            /* CAS */\n            for (;;) {\n                ma_uint32 oldBitfield;\n                ma_uint32 newBitfield;\n                ma_uint32 bitOffset;\n\n                oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield);  /* <-- This copy must happen. The compiler must not optimize this away. */\n\n                /* Fast check to see if anything is available. */\n                if (oldBitfield == 0xFFFFFFFF) {\n                    break;  /* No available bits in this bitfield. */\n                }\n\n                bitOffset = ma_ffs_32(~oldBitfield);\n                MA_ASSERT(bitOffset < 32);\n\n                newBitfield = oldBitfield | (1 << bitOffset);\n\n                if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {\n                    ma_uint32 slotIndex;\n\n                    /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */\n                    ma_atomic_fetch_add_32(&pAllocator->count, 1);\n\n                    /* The slot index is required for constructing the output value. */\n                    slotIndex = (iGroup << 5) + bitOffset;  /* iGroup << 5 = iGroup * 32 */\n                    if (slotIndex >= pAllocator->capacity) {\n                        return MA_OUT_OF_MEMORY;\n                    }\n\n                    /* Increment the reference count before constructing the output value. */\n                    pAllocator->pSlots[slotIndex] += 1;\n\n                    /* Construct the output value. */\n                    *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex);\n\n                    return MA_SUCCESS;\n                }\n            }\n        }\n\n        /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */\n        if (pAllocator->count < pAllocator->capacity) {\n            ma_yield();\n        } else {\n            return MA_OUT_OF_MEMORY;\n        }\n    }\n\n    /* We couldn't find a slot within the maximum number of attempts. */\n    return MA_OUT_OF_MEMORY;\n}\n\nMA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot)\n{\n    ma_uint32 iGroup;\n    ma_uint32 iBit;\n\n    if (pAllocator == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5);   /* slot / 32 */\n    iBit   = (ma_uint32)((slot & 0xFFFFFFFF) & 31);   /* slot % 32 */\n\n    if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ASSERT(iBit < 32);   /* This must be true due to the logic we used to actually calculate it. */\n\n    while (ma_atomic_load_32(&pAllocator->count) > 0) {\n        /* CAS */\n        ma_uint32 oldBitfield;\n        ma_uint32 newBitfield;\n\n        oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield);  /* <-- This copy must happen. The compiler must not optimize this away. */\n        newBitfield = oldBitfield & ~(1 << iBit);\n\n        /* Debugging for checking for double-frees. */\n        #if defined(MA_DEBUG_OUTPUT)\n        {\n            if ((oldBitfield & (1 << iBit)) == 0) {\n                MA_ASSERT(MA_FALSE);    /* Double free detected.*/\n            }\n        }\n        #endif\n\n        if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {\n            ma_atomic_fetch_sub_32(&pAllocator->count, 1);\n            return MA_SUCCESS;\n        }\n    }\n\n    /* Getting here means there are no allocations available for freeing. */\n    return MA_INVALID_OPERATION;\n}\n\n\n#define MA_JOB_ID_NONE      ~((ma_uint64)0)\n#define MA_JOB_SLOT_NONE    (ma_uint16)(~0)\n\nstatic MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc)\n{\n    return (ma_uint32)(toc >> 32);\n}\n\nstatic MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc)\n{\n    return (ma_uint16)(toc & 0x0000FFFF);\n}\n\nstatic MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc)\n{\n    return (ma_uint16)((toc & 0xFFFF0000) >> 16);\n}\n\nstatic MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc)\n{\n    return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc);\n}\n\nstatic MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount)\n{\n    /* Clear the reference count first. */\n    toc = toc & ~((ma_uint64)0xFFFFFFFF << 32);\n    toc = toc |  ((ma_uint64)refcount   << 32);\n\n    return toc;\n}\n\n\nMA_API ma_job ma_job_init(ma_uint16 code)\n{\n    ma_job job;\n\n    MA_ZERO_OBJECT(&job);\n    job.toc.breakup.code = code;\n    job.toc.breakup.slot = MA_JOB_SLOT_NONE;    /* Temp value. Will be allocated when posted to a queue. */\n    job.next             = MA_JOB_ID_NONE;\n\n    return job;\n}\n\n\nstatic ma_result ma_job_process__noop(ma_job* pJob);\nstatic ma_result ma_job_process__quit(ma_job* pJob);\nstatic ma_result ma_job_process__custom(ma_job* pJob);\nstatic ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob);\nstatic ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob);\nstatic ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob);\nstatic ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob);\nstatic ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob);\nstatic ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob);\nstatic ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob);\nstatic ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob);\nstatic ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob);\n\n#if !defined(MA_NO_DEVICE_IO)\nstatic ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob);\n#endif\n\nstatic ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] =\n{\n    /* Miscellaneous. */\n    ma_job_process__quit,                                       /* MA_JOB_TYPE_QUIT */\n    ma_job_process__custom,                                     /* MA_JOB_TYPE_CUSTOM */\n\n    /* Resource Manager. */\n    ma_job_process__resource_manager__load_data_buffer_node,    /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */\n    ma_job_process__resource_manager__free_data_buffer_node,    /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */\n    ma_job_process__resource_manager__page_data_buffer_node,    /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */\n    ma_job_process__resource_manager__load_data_buffer,         /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */\n    ma_job_process__resource_manager__free_data_buffer,         /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */\n    ma_job_process__resource_manager__load_data_stream,         /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */\n    ma_job_process__resource_manager__free_data_stream,         /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */\n    ma_job_process__resource_manager__page_data_stream,         /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */\n    ma_job_process__resource_manager__seek_data_stream,         /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */\n\n    /* Device. */\n#if !defined(MA_NO_DEVICE_IO)\n    ma_job_process__device__aaudio_reroute                      /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/\n#endif\n};\n\nMA_API ma_result ma_job_process(ma_job* pJob)\n{\n    if (pJob == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) {\n        return MA_INVALID_OPERATION;\n    }\n\n    return g_jobVTable[pJob->toc.breakup.code](pJob);\n}\n\nstatic ma_result ma_job_process__noop(ma_job* pJob)\n{\n    MA_ASSERT(pJob != NULL);\n\n    /* No-op. */\n    (void)pJob;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_job_process__quit(ma_job* pJob)\n{\n    return ma_job_process__noop(pJob);\n}\n\nstatic ma_result ma_job_process__custom(ma_job* pJob)\n{\n    MA_ASSERT(pJob != NULL);\n\n    /* No-op if there's no callback. */\n    if (pJob->data.custom.proc == NULL) {\n        return MA_SUCCESS;\n    }\n\n    return pJob->data.custom.proc(pJob);\n}\n\n\n\nMA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity)\n{\n    ma_job_queue_config config;\n\n    config.flags    = flags;\n    config.capacity = capacity;\n\n    return config;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t allocatorOffset;\n    size_t jobsOffset;\n} ma_job_queue_heap_layout;\n\nstatic ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout)\n{\n    ma_result result;\n\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->capacity == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* Allocator. */\n    {\n        ma_slot_allocator_config allocatorConfig;\n        size_t allocatorHeapSizeInBytes;\n\n        allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity);\n        result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes;\n        pHeapLayout->sizeInBytes    += allocatorHeapSizeInBytes;\n    }\n\n    /* Jobs. */\n    pHeapLayout->jobsOffset   = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job));\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_job_queue_heap_layout layout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_job_queue_get_heap_layout(pConfig, &layout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = layout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue)\n{\n    ma_result result;\n    ma_job_queue_heap_layout heapLayout;\n    ma_slot_allocator_config allocatorConfig;\n\n    if (pQueue == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pQueue);\n\n    result = ma_job_queue_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pQueue->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    pQueue->flags    = pConfig->flags;\n    pQueue->capacity = pConfig->capacity;\n    pQueue->pJobs    = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset);\n\n    allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity);\n    result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */\n    if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {\n        #ifndef MA_NO_THREADING\n        {\n            ma_semaphore_init(0, &pQueue->sem);\n        }\n        #else\n        {\n            /* Threading is disabled and we've requested non-blocking mode. */\n            return MA_INVALID_OPERATION;\n        }\n        #endif\n    }\n\n    /*\n    Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is\n    just a dummy item for giving us the first item in the list which is stored in the \"next\" member.\n    */\n    ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head);  /* Will never fail. */\n    pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE;\n    pQueue->tail = pQueue->head;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pQueue->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pQueue == NULL) {\n        return;\n    }\n\n    /* All we need to do is uninitialize the semaphore. */\n    if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {\n        #ifndef MA_NO_THREADING\n        {\n            ma_semaphore_uninit(&pQueue->sem);\n        }\n        #else\n        {\n            MA_ASSERT(MA_FALSE);    /* Should never get here. Should have been checked at initialization time. */\n        }\n        #endif\n    }\n\n    ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks);\n\n    if (pQueue->_ownsHeap) {\n        ma_free(pQueue->_pHeap, pAllocationCallbacks);\n    }\n}\n\nstatic ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired)\n{\n    /* The new counter is taken from the expected value. */\n    return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected;\n}\n\nMA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob)\n{\n    /*\n    Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors\n    */\n    ma_result result;\n    ma_uint64 slot;\n    ma_uint64 tail;\n    ma_uint64 next;\n\n    if (pQueue == NULL || pJob == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* We need a new slot. */\n    result = ma_slot_allocator_alloc(&pQueue->allocator, &slot);\n    if (result != MA_SUCCESS) {\n        return result;  /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */\n    }\n\n    /* At this point we should have a slot to place the job. */\n    MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity);\n\n    /* We need to put the job into memory before we do anything. */\n    pQueue->pJobs[ma_job_extract_slot(slot)]                  = *pJob;\n    pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation   = slot;                    /* This will overwrite the job code. */\n    pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code;  /* The job code needs to be applied again because the line above overwrote it. */\n    pQueue->pJobs[ma_job_extract_slot(slot)].next             = MA_JOB_ID_NONE;          /* Reset for safety. */\n\n    #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE\n    ma_spinlock_lock(&pQueue->lock);\n    #endif\n    {\n        /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */\n        for (;;) {\n            tail = ma_atomic_load_64(&pQueue->tail);\n            next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next);\n\n            if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) {\n                if (ma_job_extract_slot(next) == 0xFFFF) {\n                    if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) {\n                        break;\n                    }\n                } else {\n                    ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));\n                }\n            }\n        }\n        ma_job_queue_cas(&pQueue->tail, tail, slot);\n    }\n    #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE\n    ma_spinlock_unlock(&pQueue->lock);\n    #endif\n\n\n    /* Signal the semaphore as the last step if we're using synchronous mode. */\n    if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {\n        #ifndef MA_NO_THREADING\n        {\n            ma_semaphore_release(&pQueue->sem);\n        }\n        #else\n        {\n            MA_ASSERT(MA_FALSE);    /* Should never get here. Should have been checked at initialization time. */\n        }\n        #endif\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob)\n{\n    ma_uint64 head;\n    ma_uint64 tail;\n    ma_uint64 next;\n\n    if (pQueue == NULL || pJob == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* If we're running in synchronous mode we'll need to wait on a semaphore. */\n    if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {\n        #ifndef MA_NO_THREADING\n        {\n            ma_semaphore_wait(&pQueue->sem);\n        }\n        #else\n        {\n            MA_ASSERT(MA_FALSE);    /* Should never get here. Should have been checked at initialization time. */\n        }\n        #endif\n    }\n\n    #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE\n    ma_spinlock_lock(&pQueue->lock);\n    #endif\n    {\n        /*\n        BUG: In lock-free mode, multiple threads can be in this section of code. The \"head\" variable in the loop below\n        is stored. One thread can fall through to the freeing of this item while another is still using \"head\" for the\n        retrieval of the \"next\" variable.\n\n        The slot allocator might need to make use of some reference counting to ensure it's only truely freed when\n        there are no more references to the item. This must be fixed before removing these locks.\n        */\n\n        /* Now we need to remove the root item from the list. */\n        for (;;) {\n            head = ma_atomic_load_64(&pQueue->head);\n            tail = ma_atomic_load_64(&pQueue->tail);\n            next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next);\n\n            if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) {\n                if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) {\n                    if (ma_job_extract_slot(next) == 0xFFFF) {\n                        #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE\n                        ma_spinlock_unlock(&pQueue->lock);\n                        #endif\n                        return MA_NO_DATA_AVAILABLE;\n                    }\n                    ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));\n                } else {\n                    *pJob = pQueue->pJobs[ma_job_extract_slot(next)];\n                    if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) {\n                        break;\n                    }\n                }\n            }\n        }\n    }\n    #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE\n    ma_spinlock_unlock(&pQueue->lock);\n    #endif\n\n    ma_slot_allocator_free(&pQueue->allocator, head);\n\n    /*\n    If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We\n    could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as\n    possible.\n    */\n    if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) {\n        ma_job_queue_post(pQueue, pJob);\n        return MA_CANCELLED;    /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */\n    }\n\n    return MA_SUCCESS;\n}\n\n\n\n/*******************************************************************************\n\nDynamic Linking\n\n*******************************************************************************/\n#ifdef MA_POSIX\n    /* No need for dlfcn.h if we're not using runtime linking. */\n    #ifndef MA_NO_RUNTIME_LINKING\n        #include <dlfcn.h>\n    #endif\n#endif\n\nMA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename)\n{\n#ifndef MA_NO_RUNTIME_LINKING\n    ma_handle handle;\n\n    ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, \"Loading library: %s\\n\", filename);\n\n    #ifdef MA_WIN32\n        /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/\n        #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)))\n            handle = (ma_handle)LoadLibraryA(filename);\n        #else\n            /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */\n            WCHAR filenameW[4096];\n            if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {\n                handle = NULL;\n            } else {\n                handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);\n            }\n        #endif\n    #else\n        handle = (ma_handle)dlopen(filename, RTLD_NOW);\n    #endif\n\n    /*\n    I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority\n    backend is a deliberate design choice. Instead I'm logging it as an informational message.\n    */\n    if (handle == NULL) {\n        ma_log_postf(pLog, MA_LOG_LEVEL_INFO, \"Failed to load library: %s\\n\", filename);\n    }\n\n    return handle;\n#else\n    /* Runtime linking is disabled. */\n    (void)pLog;\n    (void)filename;\n    return NULL;\n#endif\n}\n\nMA_API void ma_dlclose(ma_log* pLog, ma_handle handle)\n{\n#ifndef MA_NO_RUNTIME_LINKING\n    #ifdef MA_WIN32\n        FreeLibrary((HMODULE)handle);\n    #else\n        dlclose((void*)handle);\n    #endif\n\n    (void)pLog;\n#else\n    /* Runtime linking is disabled. */\n    (void)pLog;\n    (void)handle;\n#endif\n}\n\nMA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol)\n{\n#ifndef MA_NO_RUNTIME_LINKING\n    ma_proc proc;\n\n    ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, \"Loading symbol: %s\\n\", symbol);\n\n#ifdef _WIN32\n    proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);\n#else\n#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wpedantic\"\n#endif\n    proc = (ma_proc)dlsym((void*)handle, symbol);\n#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))\n    #pragma GCC diagnostic pop\n#endif\n#endif\n\n    if (proc == NULL) {\n        ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, \"Failed to load symbol: %s\\n\", symbol);\n    }\n\n    (void)pLog; /* It's possible for pContext to be unused. */\n    return proc;\n#else\n    /* Runtime linking is disabled. */\n    (void)pLog;\n    (void)handle;\n    (void)symbol;\n    return NULL;\n#endif\n}\n\n\n\n/************************************************************************************************************************************************************\n*************************************************************************************************************************************************************\n\nDEVICE I/O\n==========\n\n*************************************************************************************************************************************************************\n************************************************************************************************************************************************************/\n\n/* Disable run-time linking on certain backends and platforms. */\n#ifndef MA_NO_RUNTIME_LINKING\n    #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO)\n        #define MA_NO_RUNTIME_LINKING\n    #endif\n#endif\n\n#ifndef MA_NO_DEVICE_IO\n\n#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)\n    #include <mach/mach_time.h> /* For mach_absolute_time() */\n#endif\n\n#ifdef MA_POSIX\n    #include <sys/types.h>\n    #include <unistd.h>\n\n    /* No need for dlfcn.h if we're not using runtime linking. */\n    #ifndef MA_NO_RUNTIME_LINKING\n        #include <dlfcn.h>\n    #endif\n#endif\n\n\n\nMA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags)\n{\n    if (pDeviceInfo == NULL) {\n        return;\n    }\n\n    if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) {\n        pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format     = format;\n        pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels   = channels;\n        pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;\n        pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags      = flags;\n        pDeviceInfo->nativeDataFormatCount += 1;\n    }\n}\n\n\ntypedef struct\n{\n    ma_backend backend;\n    const char* pName;\n} ma_backend_info;\n\nstatic ma_backend_info gBackendInfo[] = /* Indexed by the backend enum. Must be in the order backends are declared in the ma_backend enum. */\n{\n    {ma_backend_wasapi,     \"WASAPI\"},\n    {ma_backend_dsound,     \"DirectSound\"},\n    {ma_backend_winmm,      \"WinMM\"},\n    {ma_backend_coreaudio,  \"Core Audio\"},\n    {ma_backend_sndio,      \"sndio\"},\n    {ma_backend_audio4,     \"audio(4)\"},\n    {ma_backend_oss,        \"OSS\"},\n    {ma_backend_pulseaudio, \"PulseAudio\"},\n    {ma_backend_alsa,       \"ALSA\"},\n    {ma_backend_jack,       \"JACK\"},\n    {ma_backend_aaudio,     \"AAudio\"},\n    {ma_backend_opensl,     \"OpenSL|ES\"},\n    {ma_backend_webaudio,   \"Web Audio\"},\n    {ma_backend_custom,     \"Custom\"},\n    {ma_backend_null,       \"Null\"}\n};\n\nMA_API const char* ma_get_backend_name(ma_backend backend)\n{\n    if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) {\n        return \"Unknown\";\n    }\n\n    return gBackendInfo[backend].pName;\n}\n\nMA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend)\n{\n    size_t iBackend;\n\n    if (pBackendName == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) {\n        if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) {\n            if (pBackend != NULL) {\n                *pBackend = gBackendInfo[iBackend].backend;\n            }\n\n            return MA_SUCCESS;\n        }\n    }\n\n    /* Getting here means the backend name is unknown. */\n    return MA_INVALID_ARGS;\n}\n\nMA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)\n{\n    /*\n    This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers\n    about some enums not being handled by the switch statement.\n    */\n    switch (backend)\n    {\n        case ma_backend_wasapi:\n        #if defined(MA_HAS_WASAPI)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_dsound:\n        #if defined(MA_HAS_DSOUND)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_winmm:\n        #if defined(MA_HAS_WINMM)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_coreaudio:\n        #if defined(MA_HAS_COREAUDIO)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_sndio:\n        #if defined(MA_HAS_SNDIO)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_audio4:\n        #if defined(MA_HAS_AUDIO4)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_oss:\n        #if defined(MA_HAS_OSS)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_pulseaudio:\n        #if defined(MA_HAS_PULSEAUDIO)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_alsa:\n        #if defined(MA_HAS_ALSA)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_jack:\n        #if defined(MA_HAS_JACK)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_aaudio:\n        #if defined(MA_HAS_AAUDIO)\n            #if defined(MA_ANDROID)\n            {\n                return ma_android_sdk_version() >= 26;\n            }\n            #else\n                return MA_FALSE;\n            #endif\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_opensl:\n        #if defined(MA_HAS_OPENSL)\n            #if defined(MA_ANDROID)\n            {\n                return ma_android_sdk_version() >= 9;\n            }\n            #else\n                return MA_TRUE;\n            #endif\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_webaudio:\n        #if defined(MA_HAS_WEBAUDIO)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_custom:\n        #if defined(MA_HAS_CUSTOM)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n        case ma_backend_null:\n        #if defined(MA_HAS_NULL)\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n\n        default: return MA_FALSE;\n    }\n}\n\nMA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount)\n{\n    size_t backendCount;\n    size_t iBackend;\n    ma_result result = MA_SUCCESS;\n\n    if (pBackendCount == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    backendCount = 0;\n\n    for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) {\n        ma_backend backend = (ma_backend)iBackend;\n\n        if (ma_is_backend_enabled(backend)) {\n            /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */\n            if (backendCount == backendCap) {\n                result = MA_NO_SPACE;\n                break;\n            } else {\n                pBackends[backendCount] = backend;\n                backendCount += 1;\n            }\n        }\n    }\n\n    if (pBackendCount != NULL) {\n        *pBackendCount = backendCount;\n    }\n\n    return result;\n}\n\nMA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)\n{\n    switch (backend)\n    {\n        case ma_backend_wasapi:     return MA_TRUE;\n        case ma_backend_dsound:     return MA_FALSE;\n        case ma_backend_winmm:      return MA_FALSE;\n        case ma_backend_coreaudio:  return MA_FALSE;\n        case ma_backend_sndio:      return MA_FALSE;\n        case ma_backend_audio4:     return MA_FALSE;\n        case ma_backend_oss:        return MA_FALSE;\n        case ma_backend_pulseaudio: return MA_FALSE;\n        case ma_backend_alsa:       return MA_FALSE;\n        case ma_backend_jack:       return MA_FALSE;\n        case ma_backend_aaudio:     return MA_FALSE;\n        case ma_backend_opensl:     return MA_FALSE;\n        case ma_backend_webaudio:   return MA_FALSE;\n        case ma_backend_custom:     return MA_FALSE;    /* <-- Will depend on the implementation of the backend. */\n        case ma_backend_null:       return MA_FALSE;\n        default:                    return MA_FALSE;\n    }\n}\n\n\n\n#if defined(MA_WIN32)\n/* WASAPI error codes. */\n#define MA_AUDCLNT_E_NOT_INITIALIZED              ((HRESULT)0x88890001)\n#define MA_AUDCLNT_E_ALREADY_INITIALIZED          ((HRESULT)0x88890002)\n#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE          ((HRESULT)0x88890003)\n#define MA_AUDCLNT_E_DEVICE_INVALIDATED           ((HRESULT)0x88890004)\n#define MA_AUDCLNT_E_NOT_STOPPED                  ((HRESULT)0x88890005)\n#define MA_AUDCLNT_E_BUFFER_TOO_LARGE             ((HRESULT)0x88890006)\n#define MA_AUDCLNT_E_OUT_OF_ORDER                 ((HRESULT)0x88890007)\n#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT           ((HRESULT)0x88890008)\n#define MA_AUDCLNT_E_INVALID_SIZE                 ((HRESULT)0x88890009)\n#define MA_AUDCLNT_E_DEVICE_IN_USE                ((HRESULT)0x8889000A)\n#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING     ((HRESULT)0x8889000B)\n#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED        ((HRESULT)0x8889000C)\n#define MA_AUDCLNT_E_NO_SINGLE_PROCESS            ((HRESULT)0x8889000D)\n#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED   ((HRESULT)0x8889000E)\n#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED       ((HRESULT)0x8889000F)\n#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING          ((HRESULT)0x88890010)\n#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED     ((HRESULT)0x88890011)\n#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY          ((HRESULT)0x88890012)\n#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013)\n#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET          ((HRESULT)0x88890014)\n#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE        ((HRESULT)0x88890015)\n#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR            ((HRESULT)0x88890016)\n#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED            ((HRESULT)0x88890017)\n#define MA_AUDCLNT_E_BUFFER_ERROR                 ((HRESULT)0x88890018)\n#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED      ((HRESULT)0x88890019)\n#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD        ((HRESULT)0x88890020)\n#define MA_AUDCLNT_E_INVALID_STREAM_FLAG          ((HRESULT)0x88890021)\n#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022)\n#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES     ((HRESULT)0x88890023)\n#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY            ((HRESULT)0x88890024)\n#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY         ((HRESULT)0x88890025)\n#define MA_AUDCLNT_E_RESOURCES_INVALIDATED        ((HRESULT)0x88890026)\n#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED         ((HRESULT)0x88890027)\n#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED    ((HRESULT)0x88890028)\n#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED         ((HRESULT)0x88890029)\n#define MA_AUDCLNT_E_HEADTRACKING_ENABLED         ((HRESULT)0x88890030)\n#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED     ((HRESULT)0x88890040)\n#define MA_AUDCLNT_S_BUFFER_EMPTY                 ((HRESULT)0x08890001)\n#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED    ((HRESULT)0x08890002)\n#define MA_AUDCLNT_S_POSITION_STALLED             ((HRESULT)0x08890003)\n\n#define MA_DS_OK                                  ((HRESULT)0)\n#define MA_DS_NO_VIRTUALIZATION                   ((HRESULT)0x0878000A)\n#define MA_DSERR_ALLOCATED                        ((HRESULT)0x8878000A)\n#define MA_DSERR_CONTROLUNAVAIL                   ((HRESULT)0x8878001E)\n#define MA_DSERR_INVALIDPARAM                     ((HRESULT)0x80070057) /*E_INVALIDARG*/\n#define MA_DSERR_INVALIDCALL                      ((HRESULT)0x88780032)\n#define MA_DSERR_GENERIC                          ((HRESULT)0x80004005) /*E_FAIL*/\n#define MA_DSERR_PRIOLEVELNEEDED                  ((HRESULT)0x88780046)\n#define MA_DSERR_OUTOFMEMORY                      ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/\n#define MA_DSERR_BADFORMAT                        ((HRESULT)0x88780064)\n#define MA_DSERR_UNSUPPORTED                      ((HRESULT)0x80004001) /*E_NOTIMPL*/\n#define MA_DSERR_NODRIVER                         ((HRESULT)0x88780078)\n#define MA_DSERR_ALREADYINITIALIZED               ((HRESULT)0x88780082)\n#define MA_DSERR_NOAGGREGATION                    ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/\n#define MA_DSERR_BUFFERLOST                       ((HRESULT)0x88780096)\n#define MA_DSERR_OTHERAPPHASPRIO                  ((HRESULT)0x887800A0)\n#define MA_DSERR_UNINITIALIZED                    ((HRESULT)0x887800AA)\n#define MA_DSERR_NOINTERFACE                      ((HRESULT)0x80004002) /*E_NOINTERFACE*/\n#define MA_DSERR_ACCESSDENIED                     ((HRESULT)0x80070005) /*E_ACCESSDENIED*/\n#define MA_DSERR_BUFFERTOOSMALL                   ((HRESULT)0x887800B4)\n#define MA_DSERR_DS8_REQUIRED                     ((HRESULT)0x887800BE)\n#define MA_DSERR_SENDLOOP                         ((HRESULT)0x887800C8)\n#define MA_DSERR_BADSENDBUFFERGUID                ((HRESULT)0x887800D2)\n#define MA_DSERR_OBJECTNOTFOUND                   ((HRESULT)0x88781161)\n#define MA_DSERR_FXUNAVAILABLE                    ((HRESULT)0x887800DC)\n\nstatic ma_result ma_result_from_HRESULT(HRESULT hr)\n{\n    switch (hr)\n    {\n        case NOERROR:                                   return MA_SUCCESS;\n        /*case S_OK:                                      return MA_SUCCESS;*/\n\n        case E_POINTER:                                 return MA_INVALID_ARGS;\n        case E_UNEXPECTED:                              return MA_ERROR;\n        case E_NOTIMPL:                                 return MA_NOT_IMPLEMENTED;\n        case E_OUTOFMEMORY:                             return MA_OUT_OF_MEMORY;\n        case E_INVALIDARG:                              return MA_INVALID_ARGS;\n        case E_NOINTERFACE:                             return MA_API_NOT_FOUND;\n        case E_HANDLE:                                  return MA_INVALID_ARGS;\n        case E_ABORT:                                   return MA_ERROR;\n        case E_FAIL:                                    return MA_ERROR;\n        case E_ACCESSDENIED:                            return MA_ACCESS_DENIED;\n\n        /* WASAPI */\n        case MA_AUDCLNT_E_NOT_INITIALIZED:              return MA_DEVICE_NOT_INITIALIZED;\n        case MA_AUDCLNT_E_ALREADY_INITIALIZED:          return MA_DEVICE_ALREADY_INITIALIZED;\n        case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE:          return MA_INVALID_ARGS;\n        case MA_AUDCLNT_E_DEVICE_INVALIDATED:           return MA_UNAVAILABLE;\n        case MA_AUDCLNT_E_NOT_STOPPED:                  return MA_DEVICE_NOT_STOPPED;\n        case MA_AUDCLNT_E_BUFFER_TOO_LARGE:             return MA_TOO_BIG;\n        case MA_AUDCLNT_E_OUT_OF_ORDER:                 return MA_INVALID_OPERATION;\n        case MA_AUDCLNT_E_UNSUPPORTED_FORMAT:           return MA_FORMAT_NOT_SUPPORTED;\n        case MA_AUDCLNT_E_INVALID_SIZE:                 return MA_INVALID_ARGS;\n        case MA_AUDCLNT_E_DEVICE_IN_USE:                return MA_BUSY;\n        case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING:     return MA_INVALID_OPERATION;\n        case MA_AUDCLNT_E_THREAD_NOT_REGISTERED:        return MA_DOES_NOT_EXIST;\n        case MA_AUDCLNT_E_NO_SINGLE_PROCESS:            return MA_INVALID_OPERATION;\n        case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED:   return MA_SHARE_MODE_NOT_SUPPORTED;\n        case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED:       return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n        case MA_AUDCLNT_E_SERVICE_NOT_RUNNING:          return MA_NOT_CONNECTED;\n        case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED:     return MA_INVALID_ARGS;\n        case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY:          return MA_SHARE_MODE_NOT_SUPPORTED;\n        case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS;\n        case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET:          return MA_INVALID_ARGS;\n        case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE:        return MA_INVALID_ARGS;\n        case MA_AUDCLNT_E_BUFFER_SIZE_ERROR:            return MA_INVALID_ARGS;\n        case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED:            return MA_ERROR;\n        case MA_AUDCLNT_E_BUFFER_ERROR:                 return MA_ERROR;\n        case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED:      return MA_INVALID_ARGS;\n        case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD:        return MA_INVALID_ARGS;\n        case MA_AUDCLNT_E_INVALID_STREAM_FLAG:          return MA_INVALID_ARGS;\n        case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION;\n        case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES:     return MA_OUT_OF_MEMORY;\n        case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY:            return MA_INVALID_OPERATION;\n        case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY:         return MA_INVALID_OPERATION;\n        case MA_AUDCLNT_E_RESOURCES_INVALIDATED:        return MA_INVALID_DATA;\n        case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED:         return MA_INVALID_OPERATION;\n        case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED:    return MA_INVALID_OPERATION;\n        case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED:         return MA_INVALID_OPERATION;\n        case MA_AUDCLNT_E_HEADTRACKING_ENABLED:         return MA_INVALID_OPERATION;\n        case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED:     return MA_INVALID_OPERATION;\n        case MA_AUDCLNT_S_BUFFER_EMPTY:                 return MA_NO_SPACE;\n        case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED:    return MA_ALREADY_EXISTS;\n        case MA_AUDCLNT_S_POSITION_STALLED:             return MA_ERROR;\n\n        /* DirectSound */\n        /*case MA_DS_OK:                                  return MA_SUCCESS;*/          /* S_OK */\n        case MA_DS_NO_VIRTUALIZATION:                   return MA_SUCCESS;\n        case MA_DSERR_ALLOCATED:                        return MA_ALREADY_IN_USE;\n        case MA_DSERR_CONTROLUNAVAIL:                   return MA_INVALID_OPERATION;\n        /*case MA_DSERR_INVALIDPARAM:                    return MA_INVALID_ARGS;*/      /* E_INVALIDARG */\n        case MA_DSERR_INVALIDCALL:                      return MA_INVALID_OPERATION;\n        /*case MA_DSERR_GENERIC:                          return MA_ERROR;*/            /* E_FAIL */\n        case MA_DSERR_PRIOLEVELNEEDED:                  return MA_INVALID_OPERATION;\n        /*case MA_DSERR_OUTOFMEMORY:                      return MA_OUT_OF_MEMORY;*/    /* E_OUTOFMEMORY */\n        case MA_DSERR_BADFORMAT:                        return MA_FORMAT_NOT_SUPPORTED;\n        /*case MA_DSERR_UNSUPPORTED:                      return MA_NOT_IMPLEMENTED;*/  /* E_NOTIMPL */\n        case MA_DSERR_NODRIVER:                         return MA_FAILED_TO_INIT_BACKEND;\n        case MA_DSERR_ALREADYINITIALIZED:               return MA_DEVICE_ALREADY_INITIALIZED;\n        case MA_DSERR_NOAGGREGATION:                    return MA_ERROR;\n        case MA_DSERR_BUFFERLOST:                       return MA_UNAVAILABLE;\n        case MA_DSERR_OTHERAPPHASPRIO:                  return MA_ACCESS_DENIED;\n        case MA_DSERR_UNINITIALIZED:                    return MA_DEVICE_NOT_INITIALIZED;\n        /*case MA_DSERR_NOINTERFACE:                      return MA_API_NOT_FOUND;*/    /* E_NOINTERFACE */\n        /*case MA_DSERR_ACCESSDENIED:                     return MA_ACCESS_DENIED;*/    /* E_ACCESSDENIED */\n        case MA_DSERR_BUFFERTOOSMALL:                   return MA_NO_SPACE;\n        case MA_DSERR_DS8_REQUIRED:                     return MA_INVALID_OPERATION;\n        case MA_DSERR_SENDLOOP:                         return MA_DEADLOCK;\n        case MA_DSERR_BADSENDBUFFERGUID:                return MA_INVALID_ARGS;\n        case MA_DSERR_OBJECTNOTFOUND:                   return MA_NO_DEVICE;\n        case MA_DSERR_FXUNAVAILABLE:                    return MA_UNAVAILABLE;\n\n        default:                                        return MA_ERROR;\n    }\n}\n\n/* PROPVARIANT */\n#define MA_VT_LPWSTR    31\n#define MA_VT_BLOB      65\n\n#if defined(_MSC_VER) && !defined(__clang__)\n    #pragma warning(push)\n    #pragma warning(disable:4201)   /* nonstandard extension used: nameless struct/union */\n#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wpedantic\" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */\n    #if defined(__clang__)\n        #pragma GCC diagnostic ignored \"-Wc11-extensions\"   /* anonymous unions are a C11 extension */\n    #endif\n#endif\ntypedef struct\n{\n    WORD vt;\n    WORD wReserved1;\n    WORD wReserved2;\n    WORD wReserved3;\n    union\n    {\n        struct\n        {\n            ULONG cbSize;\n            BYTE* pBlobData;\n        } blob;\n        WCHAR* pwszVal;\n        char pad[16];   /* Just to ensure the size of the struct matches the official version. */\n    };\n} MA_PROPVARIANT;\n#if defined(_MSC_VER) && !defined(__clang__)\n    #pragma warning(pop)\n#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))\n    #pragma GCC diagnostic pop\n#endif\n\ntypedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved);\ntypedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD  dwCoInit);\ntypedef void    (WINAPI * MA_PFN_CoUninitialize)(void);\ntypedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv);\ntypedef void    (WINAPI * MA_PFN_CoTaskMemFree)(void* pv);\ntypedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar);\ntypedef int     (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax);\n\ntypedef HWND    (WINAPI * MA_PFN_GetForegroundWindow)(void);\ntypedef HWND    (WINAPI * MA_PFN_GetDesktopWindow)(void);\n\n#if defined(MA_WIN32_DESKTOP)\n/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */\ntypedef LONG    (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult);\ntypedef LONG    (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);\ntypedef LONG    (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData);\n#endif  /* MA_WIN32_DESKTOP */\n\n\nMA_API size_t ma_strlen_WCHAR(const WCHAR* str)\n{\n    size_t len = 0;\n    while (str[len] != '\\0') {\n        len += 1;\n    }\n\n    return len;\n}\n\nMA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2)\n{\n    while (*s1 != '\\0' && *s1 == *s2) {\n        s1 += 1;\n        s2 += 1;\n    }\n\n    return *s1 - *s2;\n}\n\nMA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src)\n{\n    size_t i;\n\n    if (dst == 0) {\n        return 22;\n    }\n    if (dstCap == 0) {\n        return 34;\n    }\n    if (src == 0) {\n        dst[0] = '\\0';\n        return 22;\n    }\n\n    for (i = 0; i < dstCap && src[i] != '\\0'; ++i) {\n        dst[i] = src[i];\n    }\n\n    if (i < dstCap) {\n        dst[i] = '\\0';\n        return 0;\n    }\n\n    dst[0] = '\\0';\n    return 34;\n}\n#endif  /* MA_WIN32 */\n\n\n#define MA_DEFAULT_PLAYBACK_DEVICE_NAME    \"Default Playback Device\"\n#define MA_DEFAULT_CAPTURE_DEVICE_NAME     \"Default Capture Device\"\n\n\n\n\n/*******************************************************************************\n\nTiming\n\n*******************************************************************************/\n#if defined(MA_WIN32) && !defined(MA_POSIX)\n    static LARGE_INTEGER g_ma_TimerFrequency;   /* <-- Initialized to zero since it's static. */\n    static void ma_timer_init(ma_timer* pTimer)\n    {\n        LARGE_INTEGER counter;\n\n        if (g_ma_TimerFrequency.QuadPart == 0) {\n            QueryPerformanceFrequency(&g_ma_TimerFrequency);\n        }\n\n        QueryPerformanceCounter(&counter);\n        pTimer->counter = counter.QuadPart;\n    }\n\n    static double ma_timer_get_time_in_seconds(ma_timer* pTimer)\n    {\n        LARGE_INTEGER counter;\n        if (!QueryPerformanceCounter(&counter)) {\n            return 0;\n        }\n\n        return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;\n    }\n#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)\n    static ma_uint64 g_ma_TimerFrequency = 0;\n    static void ma_timer_init(ma_timer* pTimer)\n    {\n        mach_timebase_info_data_t baseTime;\n        mach_timebase_info(&baseTime);\n        g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;\n\n        pTimer->counter = mach_absolute_time();\n    }\n\n    static double ma_timer_get_time_in_seconds(ma_timer* pTimer)\n    {\n        ma_uint64 newTimeCounter = mach_absolute_time();\n        ma_uint64 oldTimeCounter = pTimer->counter;\n\n        return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;\n    }\n#elif defined(MA_EMSCRIPTEN)\n    static MA_INLINE void ma_timer_init(ma_timer* pTimer)\n    {\n        pTimer->counterD = emscripten_get_now();\n    }\n\n    static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)\n    {\n        return (emscripten_get_now() - pTimer->counterD) / 1000;    /* Emscripten is in milliseconds. */\n    }\n#else\n    #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L\n        #if defined(CLOCK_MONOTONIC)\n            #define MA_CLOCK_ID CLOCK_MONOTONIC\n        #else\n            #define MA_CLOCK_ID CLOCK_REALTIME\n        #endif\n\n        static void ma_timer_init(ma_timer* pTimer)\n        {\n            struct timespec newTime;\n            clock_gettime(MA_CLOCK_ID, &newTime);\n\n            pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;\n        }\n\n        static double ma_timer_get_time_in_seconds(ma_timer* pTimer)\n        {\n            ma_uint64 newTimeCounter;\n            ma_uint64 oldTimeCounter;\n\n            struct timespec newTime;\n            clock_gettime(MA_CLOCK_ID, &newTime);\n\n            newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;\n            oldTimeCounter = pTimer->counter;\n\n            return (newTimeCounter - oldTimeCounter) / 1000000000.0;\n        }\n    #else\n        static void ma_timer_init(ma_timer* pTimer)\n        {\n            struct timeval newTime;\n            gettimeofday(&newTime, NULL);\n\n            pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;\n        }\n\n        static double ma_timer_get_time_in_seconds(ma_timer* pTimer)\n        {\n            ma_uint64 newTimeCounter;\n            ma_uint64 oldTimeCounter;\n\n            struct timeval newTime;\n            gettimeofday(&newTime, NULL);\n\n            newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;\n            oldTimeCounter = pTimer->counter;\n\n            return (newTimeCounter - oldTimeCounter) / 1000000.0;\n        }\n    #endif\n#endif\n\n\n\n#if 0\nstatic ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)\n{\n    ma_uint32 closestRate = 0;\n    ma_uint32 closestDiff = 0xFFFFFFFF;\n    size_t iStandardRate;\n\n    for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {\n        ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];\n        ma_uint32 diff;\n\n        if (sampleRateIn > standardRate) {\n            diff = sampleRateIn - standardRate;\n        } else {\n            diff = standardRate - sampleRateIn;\n        }\n\n        if (diff == 0) {\n            return standardRate;    /* The input sample rate is a standard rate. */\n        }\n\n        if (closestDiff > diff) {\n            closestDiff = diff;\n            closestRate = standardRate;\n        }\n    }\n\n    return closestRate;\n}\n#endif\n\n\nstatic MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (!pDevice->noDisableDenormals) {\n        return ma_disable_denormals();\n    } else {\n        return 0;\n    }\n}\n\nstatic MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (!pDevice->noDisableDenormals) {\n        ma_restore_denormals(prevState);\n    } else {\n        /* Do nothing. */\n        (void)prevState;\n    }\n}\n\nstatic ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type)\n{\n    ma_device_notification notification;\n\n    MA_ZERO_OBJECT(&notification);\n    notification.pDevice = pDevice;\n    notification.type    = type;\n\n    return notification;\n}\n\nstatic void ma_device__on_notification(ma_device_notification notification)\n{\n    MA_ASSERT(notification.pDevice != NULL);\n\n    if (notification.pDevice->onNotification != NULL) {\n        notification.pDevice->onNotification(&notification);\n    }\n\n    /* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */\n    if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) {\n        notification.pDevice->onStop(notification.pDevice);\n    }\n}\n\nstatic void ma_device__on_notification_started(ma_device* pDevice)\n{\n    ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started));\n}\n\nstatic void ma_device__on_notification_stopped(ma_device* pDevice)\n{\n    ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped));\n}\n\n/* Not all platforms support reroute notifications. */\n#if !defined(MA_EMSCRIPTEN)\nstatic void ma_device__on_notification_rerouted(ma_device* pDevice)\n{\n    ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted));\n}\n#endif\n\n#if defined(MA_EMSCRIPTEN)\nEMSCRIPTEN_KEEPALIVE\nvoid ma_device__on_notification_unlocked(ma_device* pDevice)\n{\n    ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked));\n}\n#endif\n\n\nstatic void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)\n{\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(pDevice->onData != NULL);\n\n    if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) {\n        ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);\n    }\n\n    pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount);\n}\n\nstatic void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    /* Don't read more data from the client if we're in the process of stopping. */\n    if (ma_device_get_state(pDevice) == ma_device_state_stopping) {\n        return;\n    }\n\n    if (pDevice->noFixedSizedCallback) {\n        /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */\n        ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount);\n    } else {\n        /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */\n        ma_uint32 totalFramesProcessed = 0;\n\n        while (totalFramesProcessed < frameCount) {\n            ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed;\n            ma_uint32 framesToProcessThisIteration = 0;\n\n            if (pFramesIn != NULL) {\n                /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */\n                if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) {\n                    /* There's some room left in the intermediary buffer. Write to it without firing the callback. */\n                    framesToProcessThisIteration = totalFramesRemaining;\n                    if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) {\n                        framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen;\n                    }\n\n                    ma_copy_pcm_frames(\n                        ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels),\n                        ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels),\n                        framesToProcessThisIteration,\n                        pDevice->capture.format, pDevice->capture.channels);\n\n                    pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration;\n                }\n\n                if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) {\n                    /* No room left in the intermediary buffer. Fire the data callback. */\n                    if (pDevice->type == ma_device_type_duplex) {\n                        /* We'll do the duplex data callback later after we've processed the playback data. */\n                    } else {\n                        ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap);\n\n                        /* The intermediary buffer has just been drained. */\n                        pDevice->capture.intermediaryBufferLen = 0;\n                    }\n                }\n            }\n\n            if (pFramesOut != NULL) {\n                /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */\n                if (pDevice->playback.intermediaryBufferLen > 0) {\n                    /* There's some content in the intermediary buffer. Read from that without firing the callback. */\n                    if (pDevice->type == ma_device_type_duplex) {\n                        /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */\n                    } else {\n                        framesToProcessThisIteration = totalFramesRemaining;\n                        if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) {\n                            framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen;\n                        }\n                    }\n\n                    ma_copy_pcm_frames(\n                        ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels),\n                        ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels),\n                        framesToProcessThisIteration,\n                        pDevice->playback.format, pDevice->playback.channels);\n\n                    pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration;\n                }\n\n                if (pDevice->playback.intermediaryBufferLen == 0) {\n                    /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */\n                    if (pDevice->type == ma_device_type_duplex) {\n                        /* In duplex mode, the data callback will be fired later. Nothing to do here. */\n                    } else {\n                        ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap);\n\n                        /* The intermediary buffer has just been filled. */\n                        pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap;\n                    }\n                }\n            }\n\n            /* If we're in duplex mode we might need to do a refill of the data. */\n            if (pDevice->type == ma_device_type_duplex) {\n                if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) {\n                    ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap);\n\n                    pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap;  /* The playback buffer will have just been filled. */\n                    pDevice->capture.intermediaryBufferLen  = 0;                                        /* The intermediary buffer has just been drained. */\n                }\n            }\n\n            /* Make sure this is only incremented once in the duplex case. */\n            totalFramesProcessed += framesToProcessThisIteration;\n        }\n    }\n}\n\nstatic void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)\n{\n    float masterVolumeFactor;\n\n    ma_device_get_master_volume(pDevice, &masterVolumeFactor);  /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */\n\n    if (pDevice->onData) {\n        unsigned int prevDenormalState = ma_device_disable_denormals(pDevice);\n        {\n            /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */\n            if (pFramesIn != NULL && masterVolumeFactor < 1) {\n                ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n                ma_uint32 bpfCapture  = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);\n                ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);\n                ma_uint32 totalFramesProcessed = 0;\n                while (totalFramesProcessed < frameCount) {\n                    ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed;\n                    if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) {\n                        framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture;\n                    }\n\n                    ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor);\n\n                    ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration);\n\n                    totalFramesProcessed += framesToProcessThisIteration;\n                }\n            } else {\n                ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount);\n            }\n\n            /* Volume control and clipping for playback devices. */\n            if (pFramesOut != NULL) {\n                if (masterVolumeFactor < 1) {\n                    if (pFramesIn == NULL) {    /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */\n                        ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor);\n                    }\n                }\n\n                if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) {\n                    ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels);   /* Intentionally specifying the same pointer for both input and output for in-place processing. */\n                }\n            }\n        }\n        ma_device_restore_denormals(pDevice, prevDenormalState);\n    }\n}\n\n\n\n/* A helper function for reading sample data from the client. */\nstatic void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut)\n{\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(frameCount > 0);\n    MA_ASSERT(pFramesOut != NULL);\n\n    if (pDevice->playback.converter.isPassthrough) {\n        ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount);\n    } else {\n        ma_result result;\n        ma_uint64 totalFramesReadOut;\n        void* pRunningFramesOut;\n\n        totalFramesReadOut = 0;\n        pRunningFramesOut  = pFramesOut;\n\n        /*\n        We run slightly different logic depending on whether or not we're using a heap-allocated\n        buffer for caching input data. This will be the case if the data converter does not have\n        the ability to retrieve the required input frame count for a given output frame count.\n        */\n        if (pDevice->playback.pInputCache != NULL) {\n            while (totalFramesReadOut < frameCount) {\n                ma_uint64 framesToReadThisIterationIn;\n                ma_uint64 framesToReadThisIterationOut;\n\n                /* If there's any data available in the cache, that needs to get processed first. */\n                if (pDevice->playback.inputCacheRemaining > 0) {\n                    framesToReadThisIterationOut = (frameCount - totalFramesReadOut);\n                    framesToReadThisIterationIn  = framesToReadThisIterationOut;\n                    if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) {\n                        framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining;\n                    }\n\n                    result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut);\n                    if (result != MA_SUCCESS) {\n                        break;\n                    }\n\n                    pDevice->playback.inputCacheConsumed  += framesToReadThisIterationIn;\n                    pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn;\n\n                    totalFramesReadOut += framesToReadThisIterationOut;\n                    pRunningFramesOut   = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));\n\n                    if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {\n                        break;  /* We're done. */\n                    }\n                }\n\n                /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */\n                if (pDevice->playback.inputCacheRemaining == 0) {\n                    ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap);\n\n                    pDevice->playback.inputCacheConsumed  = 0;\n                    pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap;\n                }\n            }\n        } else {\n            while (totalFramesReadOut < frameCount) {\n                ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];  /* In client format. */\n                ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);\n                ma_uint64 framesToReadThisIterationIn;\n                ma_uint64 framesReadThisIterationIn;\n                ma_uint64 framesToReadThisIterationOut;\n                ma_uint64 framesReadThisIterationOut;\n                ma_uint64 requiredInputFrameCount;\n\n                framesToReadThisIterationOut = (frameCount - totalFramesReadOut);\n                framesToReadThisIterationIn = framesToReadThisIterationOut;\n                if (framesToReadThisIterationIn > intermediaryBufferCap) {\n                    framesToReadThisIterationIn = intermediaryBufferCap;\n                }\n\n                ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount);\n                if (framesToReadThisIterationIn > requiredInputFrameCount) {\n                    framesToReadThisIterationIn = requiredInputFrameCount;\n                }\n\n                if (framesToReadThisIterationIn > 0) {\n                    ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);\n                }\n\n                /*\n                At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any\n                input frames, we still want to try processing frames because there may some output frames generated from cached input data.\n                */\n                framesReadThisIterationIn  = framesToReadThisIterationIn;\n                framesReadThisIterationOut = framesToReadThisIterationOut;\n                result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);\n                if (result != MA_SUCCESS) {\n                    break;\n                }\n\n                totalFramesReadOut += framesReadThisIterationOut;\n                pRunningFramesOut   = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));\n\n                if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {\n                    break;  /* We're done. */\n                }\n            }\n        }\n    }\n}\n\n/* A helper for sending sample data to the client. */\nstatic void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat)\n{\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(frameCountInDeviceFormat > 0);\n    MA_ASSERT(pFramesInDeviceFormat != NULL);\n\n    if (pDevice->capture.converter.isPassthrough) {\n        ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat);\n    } else {\n        ma_result result;\n        ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n        ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);\n        ma_uint64 totalDeviceFramesProcessed = 0;\n        ma_uint64 totalClientFramesProcessed = 0;\n        const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;\n\n        /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */\n        for (;;) {\n            ma_uint64 deviceFramesProcessedThisIteration;\n            ma_uint64 clientFramesProcessedThisIteration;\n\n            deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed);\n            clientFramesProcessedThisIteration = framesInClientFormatCap;\n\n            result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration);\n            if (result != MA_SUCCESS) {\n                break;\n            }\n\n            if (clientFramesProcessedThisIteration > 0) {\n                ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration);    /* Safe cast. */\n            }\n\n            pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));\n            totalDeviceFramesProcessed  += deviceFramesProcessedThisIteration;\n            totalClientFramesProcessed  += clientFramesProcessedThisIteration;\n\n            /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */\n            (void)totalClientFramesProcessed;\n\n            if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) {\n                break;  /* We're done. */\n            }\n        }\n    }\n}\n\nstatic ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB)\n{\n    ma_result result;\n    ma_uint32 totalDeviceFramesProcessed = 0;\n    const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(frameCountInDeviceFormat > 0);\n    MA_ASSERT(pFramesInDeviceFormat != NULL);\n    MA_ASSERT(pRB != NULL);\n\n    /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */\n    for (;;) {\n        ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed);\n        ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);\n        ma_uint64 framesProcessedInDeviceFormat;\n        ma_uint64 framesProcessedInClientFormat;\n        void* pFramesInClientFormat;\n\n        result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat);\n        if (result != MA_SUCCESS) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"Failed to acquire capture PCM frames from ring buffer.\");\n            break;\n        }\n\n        if (framesToProcessInClientFormat == 0) {\n            if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) {\n                break;  /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */\n            }\n        }\n\n        /* Convert. */\n        framesProcessedInDeviceFormat = framesToProcessInDeviceFormat;\n        framesProcessedInClientFormat = framesToProcessInClientFormat;\n        result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat);\n        if (result != MA_SUCCESS) {\n            break;\n        }\n\n        result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat);  /* Safe cast. */\n        if (result != MA_SUCCESS) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"Failed to commit capture PCM frames to ring buffer.\");\n            break;\n        }\n\n        pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));\n        totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */\n\n        /* We're done when we're unable to process any client nor device frames. */\n        if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) {\n            break;  /* Done. */\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)\n{\n    ma_result result;\n    ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n    ma_uint32 totalFramesReadOut = 0;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(frameCount > 0);\n    MA_ASSERT(pFramesInInternalFormat != NULL);\n    MA_ASSERT(pRB != NULL);\n    MA_ASSERT(pDevice->playback.pInputCache != NULL);\n\n    /*\n    Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for\n    the whole frameCount frames we just use silence instead for the input data.\n    */\n    MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames));\n\n    while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) {\n        /*\n        We should have a buffer allocated on the heap. Any playback frames still sitting in there\n        need to be sent to the internal device before we process any more data from the client.\n        */\n        if (pDevice->playback.inputCacheRemaining > 0) {\n            ma_uint64 framesConvertedIn  = pDevice->playback.inputCacheRemaining;\n            ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut);\n            ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut);\n\n            pDevice->playback.inputCacheConsumed  += framesConvertedIn;\n            pDevice->playback.inputCacheRemaining -= framesConvertedIn;\n\n            totalFramesReadOut        += (ma_uint32)framesConvertedOut; /* Safe cast. */\n            pFramesInInternalFormat    = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));\n        }\n\n        /* If there's no more data in the cache we'll need to fill it with some. */\n        if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) {\n            ma_uint32 inputFrameCount;\n            void* pInputFrames;\n\n            inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap;\n            result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);\n            if (result == MA_SUCCESS) {\n                if (inputFrameCount > 0) {\n                    ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount);\n                } else {\n                    if (ma_pcm_rb_pointer_distance(pRB) == 0) {\n                        break;  /* Underrun. */\n                    }\n                }\n            } else {\n                /* No capture data available. Feed in silence. */\n                inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));\n                ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount);\n            }\n\n            pDevice->playback.inputCacheConsumed  = 0;\n            pDevice->playback.inputCacheRemaining = inputFrameCount;\n\n            result = ma_pcm_rb_commit_read(pRB, inputFrameCount);\n            if (result != MA_SUCCESS) {\n                return result;  /* Should never happen. */\n            }\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n/* A helper for changing the state of the device. */\nstatic MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState)\n{\n    ma_atomic_device_state_set(&pDevice->state, newState);\n}\n\n\n#if defined(MA_WIN32)\n    static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM        = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};\n    static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};\n    /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW       = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/\n    /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW      = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/\n#endif\n\n\n\nMA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */\n{\n    ma_uint32 i;\n    for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {\n        if (g_maFormatPriorities[i] == format) {\n            return i;\n        }\n    }\n\n    /* Getting here means the format could not be found or is equal to ma_format_unknown. */\n    return (ma_uint32)-1;\n}\n\nstatic ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);\n\nstatic ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor)\n{\n    if (pDeviceDescriptor == NULL) {\n        return MA_FALSE;\n    }\n\n    if (pDeviceDescriptor->format == ma_format_unknown) {\n        return MA_FALSE;\n    }\n\n    if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) {\n        return MA_FALSE;\n    }\n\n    if (pDeviceDescriptor->sampleRate == 0) {\n        return MA_FALSE;\n    }\n\n    return MA_TRUE;\n}\n\n\nstatic ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice)\n{\n    ma_result result = MA_SUCCESS;\n    ma_bool32 exitLoop = MA_FALSE;\n    ma_uint8  capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n    ma_uint8  playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n    ma_uint32 capturedDeviceDataCapInFrames = 0;\n    ma_uint32 playbackDeviceDataCapInFrames = 0;\n\n    MA_ASSERT(pDevice != NULL);\n\n    /* Just some quick validation on the device type and the available callbacks. */\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {\n        if (pDevice->pContext->callbacks.onDeviceRead == NULL) {\n            return MA_NOT_IMPLEMENTED;\n        }\n\n        capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat,  pDevice->capture.internalChannels);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        if (pDevice->pContext->callbacks.onDeviceWrite == NULL) {\n            return MA_NOT_IMPLEMENTED;\n        }\n\n        playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);\n    }\n\n    /* NOTE: The device was started outside of this function, in the worker thread. */\n\n    while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) {\n        switch (pDevice->type) {\n            case ma_device_type_duplex:\n            {\n                /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */\n                ma_uint32 totalCapturedDeviceFramesProcessed = 0;\n                ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);\n\n                while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {\n                    ma_uint32 capturedDeviceFramesRemaining;\n                    ma_uint32 capturedDeviceFramesProcessed;\n                    ma_uint32 capturedDeviceFramesToProcess;\n                    ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;\n                    if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {\n                        capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;\n                    }\n\n                    result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);\n                    if (result != MA_SUCCESS) {\n                        exitLoop = MA_TRUE;\n                        break;\n                    }\n\n                    capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;\n                    capturedDeviceFramesProcessed = 0;\n\n                    /* At this point we have our captured data in device format and we now need to convert it to client format. */\n                    for (;;) {\n                        ma_uint8  capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n                        ma_uint8  playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n                        ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format,  pDevice->capture.channels);\n                        ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);\n                        ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);\n                        ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;\n                        ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat,  pDevice->capture.internalChannels));\n\n                        /* Convert capture data from device format to client format. */\n                        result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);\n                        if (result != MA_SUCCESS) {\n                            break;\n                        }\n\n                        /*\n                        If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small\n                        which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.\n                        */\n                        if (capturedClientFramesToProcessThisIteration == 0) {\n                            break;\n                        }\n\n                        ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration);    /* Safe cast .*/\n\n                        capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */\n                        capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */\n\n                        /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */\n                        for (;;) {\n                            ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;\n                            ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;\n                            result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);\n                            if (result != MA_SUCCESS) {\n                                break;\n                            }\n\n                            result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL);   /* Safe cast. */\n                            if (result != MA_SUCCESS) {\n                                exitLoop = MA_TRUE;\n                                break;\n                            }\n\n                            capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount;  /* Safe cast. */\n                            if (capturedClientFramesToProcessThisIteration == 0) {\n                                break;\n                            }\n                        }\n\n                        /* In case an error happened from ma_device_write__null()... */\n                        if (result != MA_SUCCESS) {\n                            exitLoop = MA_TRUE;\n                            break;\n                        }\n                    }\n\n                    /* Make sure we don't get stuck in the inner loop. */\n                    if (capturedDeviceFramesProcessed == 0) {\n                        break;\n                    }\n\n                    totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;\n                }\n            } break;\n\n            case ma_device_type_capture:\n            case ma_device_type_loopback:\n            {\n                ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;\n                ma_uint32 framesReadThisPeriod = 0;\n                while (framesReadThisPeriod < periodSizeInFrames) {\n                    ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;\n                    ma_uint32 framesProcessed;\n                    ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;\n                    if (framesToReadThisIteration > capturedDeviceDataCapInFrames) {\n                        framesToReadThisIteration = capturedDeviceDataCapInFrames;\n                    }\n\n                    result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed);\n                    if (result != MA_SUCCESS) {\n                        exitLoop = MA_TRUE;\n                        break;\n                    }\n\n                    /* Make sure we don't get stuck in the inner loop. */\n                    if (framesProcessed == 0) {\n                        break;\n                    }\n\n                    ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData);\n\n                    framesReadThisPeriod += framesProcessed;\n                }\n            } break;\n\n            case ma_device_type_playback:\n            {\n                /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */\n                ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;\n                ma_uint32 framesWrittenThisPeriod = 0;\n                while (framesWrittenThisPeriod < periodSizeInFrames) {\n                    ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;\n                    ma_uint32 framesProcessed;\n                    ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;\n                    if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) {\n                        framesToWriteThisIteration = playbackDeviceDataCapInFrames;\n                    }\n\n                    ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData);\n\n                    result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed);\n                    if (result != MA_SUCCESS) {\n                        exitLoop = MA_TRUE;\n                        break;\n                    }\n\n                    /* Make sure we don't get stuck in the inner loop. */\n                    if (framesProcessed == 0) {\n                        break;\n                    }\n\n                    framesWrittenThisPeriod += framesProcessed;\n                }\n            } break;\n\n            /* Should never get here. */\n            default: break;\n        }\n    }\n\n    return result;\n}\n\n\n\n/*******************************************************************************\n\nNull Backend\n\n*******************************************************************************/\n#ifdef MA_HAS_NULL\n\n#define MA_DEVICE_OP_NONE__NULL    0\n#define MA_DEVICE_OP_START__NULL   1\n#define MA_DEVICE_OP_SUSPEND__NULL 2\n#define MA_DEVICE_OP_KILL__NULL    3\n\nstatic ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)\n{\n    ma_device* pDevice = (ma_device*)pData;\n    MA_ASSERT(pDevice != NULL);\n\n    for (;;) {  /* Keep the thread alive until the device is uninitialized. */\n        ma_uint32 operation;\n\n        /* Wait for an operation to be requested. */\n        ma_event_wait(&pDevice->null_device.operationEvent);\n\n        /* At this point an event should have been triggered. */\n        operation = pDevice->null_device.operation;\n\n        /* Starting the device needs to put the thread into a loop. */\n        if (operation == MA_DEVICE_OP_START__NULL) {\n            /* Reset the timer just in case. */\n            ma_timer_init(&pDevice->null_device.timer);\n\n            /* Getting here means a suspend or kill operation has been requested. */\n            pDevice->null_device.operationResult = MA_SUCCESS;\n            ma_event_signal(&pDevice->null_device.operationCompletionEvent);\n            ma_semaphore_release(&pDevice->null_device.operationSemaphore);\n            continue;\n        }\n\n        /* Suspending the device means we need to stop the timer and just continue the loop. */\n        if (operation == MA_DEVICE_OP_SUSPEND__NULL) {\n            /* We need to add the current run time to the prior run time, then reset the timer. */\n            pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);\n            ma_timer_init(&pDevice->null_device.timer);\n\n            /* We're done. */\n            pDevice->null_device.operationResult = MA_SUCCESS;\n            ma_event_signal(&pDevice->null_device.operationCompletionEvent);\n            ma_semaphore_release(&pDevice->null_device.operationSemaphore);\n            continue;\n        }\n\n        /* Killing the device means we need to get out of this loop so that this thread can terminate. */\n        if (operation == MA_DEVICE_OP_KILL__NULL) {\n            pDevice->null_device.operationResult = MA_SUCCESS;\n            ma_event_signal(&pDevice->null_device.operationCompletionEvent);\n            ma_semaphore_release(&pDevice->null_device.operationSemaphore);\n            break;\n        }\n\n        /* Getting a signal on a \"none\" operation probably means an error. Return invalid operation. */\n        if (operation == MA_DEVICE_OP_NONE__NULL) {\n            MA_ASSERT(MA_FALSE);  /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */\n            pDevice->null_device.operationResult = MA_INVALID_OPERATION;\n            ma_event_signal(&pDevice->null_device.operationCompletionEvent);\n            ma_semaphore_release(&pDevice->null_device.operationSemaphore);\n            continue;   /* Continue the loop. Don't terminate. */\n        }\n    }\n\n    return (ma_thread_result)0;\n}\n\nstatic ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)\n{\n    ma_result result;\n\n    /*\n    TODO: Need to review this and consider just using mutual exclusion. I think the original motivation\n    for this was to just post the event to a queue and return immediately, but that has since changed\n    and now this function is synchronous. I think this can be simplified to just use a mutex.\n    */\n\n    /*\n    The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later\n    to support queing of operations.\n    */\n    result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to wait for the event. */\n    }\n\n    /*\n    When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to\n    signal an event to the worker thread to let it know that it can start work.\n    */\n    pDevice->null_device.operation = operation;\n\n    /* Once the operation code has been set, the worker thread can start work. */\n    if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) {\n        return MA_ERROR;\n    }\n\n    /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */\n    if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) {\n        return MA_ERROR;\n    }\n\n    return pDevice->null_device.operationResult;\n}\n\nstatic ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)\n{\n    ma_uint32 internalSampleRate;\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        internalSampleRate = pDevice->capture.internalSampleRate;\n    } else {\n        internalSampleRate = pDevice->playback.internalSampleRate;\n    }\n\n    return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);\n}\n\nstatic ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    ma_bool32 cbResult = MA_TRUE;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    /* Playback. */\n    if (cbResult) {\n        ma_device_info deviceInfo;\n        MA_ZERO_OBJECT(&deviceInfo);\n        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), \"NULL Playback Device\", (size_t)-1);\n        deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */\n        cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n    }\n\n    /* Capture. */\n    if (cbResult) {\n        ma_device_info deviceInfo;\n        MA_ZERO_OBJECT(&deviceInfo);\n        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), \"NULL Capture Device\", (size_t)-1);\n        deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */\n        cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n    }\n\n    (void)cbResult; /* Silence a static analysis warning. */\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    MA_ASSERT(pContext != NULL);\n\n    if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {\n        return MA_NO_DEVICE;   /* Don't know the device. */\n    }\n\n    /* Name / Description */\n    if (deviceType == ma_device_type_playback) {\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), \"NULL Playback Device\", (size_t)-1);\n    } else {\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), \"NULL Capture Device\", (size_t)-1);\n    }\n\n    pDeviceInfo->isDefault = MA_TRUE;   /* Only one playback and capture device for the null backend, so might as well mark as default. */\n\n    /* Support everything on the null backend. */\n    pDeviceInfo->nativeDataFormats[0].format     = ma_format_unknown;\n    pDeviceInfo->nativeDataFormats[0].channels   = 0;\n    pDeviceInfo->nativeDataFormats[0].sampleRate = 0;\n    pDeviceInfo->nativeDataFormats[0].flags      = 0;\n    pDeviceInfo->nativeDataFormatCount = 1;\n\n    (void)pContext;\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_device_uninit__null(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    /* Keep it clean and wait for the device thread to finish before returning. */\n    ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);\n\n    /* Wait for the thread to finish before continuing. */\n    ma_thread_wait(&pDevice->null_device.deviceThread);\n\n    /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */\n    ma_semaphore_uninit(&pDevice->null_device.operationSemaphore);\n    ma_event_uninit(&pDevice->null_device.operationCompletionEvent);\n    ma_event_uninit(&pDevice->null_device.operationEvent);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    ma_result result;\n\n    MA_ASSERT(pDevice != NULL);\n\n    MA_ZERO_OBJECT(&pDevice->null_device);\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    /* The null backend supports everything exactly as we specify it. */\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        pDescriptorCapture->format     = (pDescriptorCapture->format     != ma_format_unknown) ? pDescriptorCapture->format     : MA_DEFAULT_FORMAT;\n        pDescriptorCapture->channels   = (pDescriptorCapture->channels   != 0)                 ? pDescriptorCapture->channels   : MA_DEFAULT_CHANNELS;\n        pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0)                 ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE;\n\n        if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) {\n            ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);\n        }\n\n        pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        pDescriptorPlayback->format     = (pDescriptorPlayback->format     != ma_format_unknown) ? pDescriptorPlayback->format     : MA_DEFAULT_FORMAT;\n        pDescriptorPlayback->channels   = (pDescriptorPlayback->channels   != 0)                 ? pDescriptorPlayback->channels   : MA_DEFAULT_CHANNELS;\n        pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0)                 ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE;\n\n        if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) {\n            ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels);\n        }\n\n        pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);\n    }\n\n    /*\n    In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the\n    first period is \"written\" to it, and then stopped in ma_device_stop__null().\n    */\n    result = ma_event_init(&pDevice->null_device.operationEvent);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    result = ma_event_init(&pDevice->null_device.operationCompletionEvent);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore);    /* <-- It's important that the initial value is set to 1. */\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_start__null(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);\n\n    ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE);\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__null(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);\n\n    ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE);\n    return MA_SUCCESS;\n}\n\nstatic ma_bool32 ma_device_is_started__null(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    return ma_atomic_bool32_get(&pDevice->null_device.isStarted);\n}\n\nstatic ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint32 totalPCMFramesProcessed;\n    ma_bool32 wasStartedOnEntry;\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = 0;\n    }\n\n    wasStartedOnEntry = ma_device_is_started__null(pDevice);\n\n    /* Keep going until everything has been read. */\n    totalPCMFramesProcessed = 0;\n    while (totalPCMFramesProcessed < frameCount) {\n        ma_uint64 targetFrame;\n\n        /* If there are any frames remaining in the current period, consume those first. */\n        if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) {\n            ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);\n            ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback;\n            if (framesToProcess > framesRemaining) {\n                framesToProcess = framesRemaining;\n            }\n\n            /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */\n            (void)pPCMFrames;\n\n            pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;\n            totalPCMFramesProcessed += framesToProcess;\n        }\n\n        /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */\n        if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) {\n            pDevice->null_device.currentPeriodFramesRemainingPlayback = 0;\n\n            if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) {\n                result = ma_device_start__null(pDevice);\n                if (result != MA_SUCCESS) {\n                    break;\n                }\n            }\n        }\n\n        /* If we've consumed the whole buffer we can return now. */\n        MA_ASSERT(totalPCMFramesProcessed <= frameCount);\n        if (totalPCMFramesProcessed == frameCount) {\n            break;\n        }\n\n        /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */\n        targetFrame = pDevice->null_device.lastProcessedFramePlayback;\n        for (;;) {\n            ma_uint64 currentFrame;\n\n            /* Stop waiting if the device has been stopped. */\n            if (!ma_device_is_started__null(pDevice)) {\n                break;\n            }\n\n            currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);\n            if (currentFrame >= targetFrame) {\n                break;\n            }\n\n            /* Getting here means we haven't yet reached the target sample, so continue waiting. */\n            ma_sleep(10);\n        }\n\n        pDevice->null_device.lastProcessedFramePlayback          += pDevice->playback.internalPeriodSizeInFrames;\n        pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames;\n    }\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = totalPCMFramesProcessed;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint32 totalPCMFramesProcessed;\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    /* Keep going until everything has been read. */\n    totalPCMFramesProcessed = 0;\n    while (totalPCMFramesProcessed < frameCount) {\n        ma_uint64 targetFrame;\n\n        /* If there are any frames remaining in the current period, consume those first. */\n        if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) {\n            ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);\n            ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);\n            ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture;\n            if (framesToProcess > framesRemaining) {\n                framesToProcess = framesRemaining;\n            }\n\n            /* We need to ensure the output buffer is zeroed. */\n            MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf);\n\n            pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess;\n            totalPCMFramesProcessed += framesToProcess;\n        }\n\n        /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */\n        if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) {\n            pDevice->null_device.currentPeriodFramesRemainingCapture = 0;\n        }\n\n        /* If we've consumed the whole buffer we can return now. */\n        MA_ASSERT(totalPCMFramesProcessed <= frameCount);\n        if (totalPCMFramesProcessed == frameCount) {\n            break;\n        }\n\n        /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */\n        targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames;\n        for (;;) {\n            ma_uint64 currentFrame;\n\n            /* Stop waiting if the device has been stopped. */\n            if (!ma_device_is_started__null(pDevice)) {\n                break;\n            }\n\n            currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);\n            if (currentFrame >= targetFrame) {\n                break;\n            }\n\n            /* Getting here means we haven't yet reached the target sample, so continue waiting. */\n            ma_sleep(10);\n        }\n\n        pDevice->null_device.lastProcessedFrameCapture          += pDevice->capture.internalPeriodSizeInFrames;\n        pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames;\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = totalPCMFramesProcessed;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_context_uninit__null(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_null);\n\n    (void)pContext;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n    MA_ASSERT(pContext != NULL);\n\n    (void)pConfig;\n    (void)pContext;\n\n    pCallbacks->onContextInit             = ma_context_init__null;\n    pCallbacks->onContextUninit           = ma_context_uninit__null;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__null;\n    pCallbacks->onDeviceInit              = ma_device_init__null;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__null;\n    pCallbacks->onDeviceStart             = ma_device_start__null;\n    pCallbacks->onDeviceStop              = ma_device_stop__null;\n    pCallbacks->onDeviceRead              = ma_device_read__null;\n    pCallbacks->onDeviceWrite             = ma_device_write__null;\n    pCallbacks->onDeviceDataLoop          = NULL;   /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */\n\n    /* The null backend always works. */\n    return MA_SUCCESS;\n}\n#endif\n\n\n\n/*******************************************************************************\n\nWIN32 COMMON\n\n*******************************************************************************/\n#if defined(MA_WIN32)\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit)                          ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved))\n    #define ma_CoUninitialize(pContext)                                                ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()\n    #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv)  ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)\n    #define ma_CoTaskMemFree(pContext, pv)                                             ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)\n    #define ma_PropVariantClear(pContext, pvar)                                        ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)\n#else\n    #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit)                          CoInitializeEx(pvReserved, dwCoInit)\n    #define ma_CoUninitialize(pContext)                                                CoUninitialize()\n    #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv)  CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)\n    #define ma_CoTaskMemFree(pContext, pv)                                             CoTaskMemFree(pv)\n    #define ma_PropVariantClear(pContext, pvar)                                        PropVariantClear(pvar)\n#endif\n\n#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__)\ntypedef size_t DWORD_PTR;\n#endif\n\n#if !defined(WAVE_FORMAT_1M08)\n#define WAVE_FORMAT_1M08    0x00000001\n#define WAVE_FORMAT_1S08    0x00000002\n#define WAVE_FORMAT_1M16    0x00000004\n#define WAVE_FORMAT_1S16    0x00000008\n#define WAVE_FORMAT_2M08    0x00000010\n#define WAVE_FORMAT_2S08    0x00000020\n#define WAVE_FORMAT_2M16    0x00000040\n#define WAVE_FORMAT_2S16    0x00000080\n#define WAVE_FORMAT_4M08    0x00000100\n#define WAVE_FORMAT_4S08    0x00000200\n#define WAVE_FORMAT_4M16    0x00000400\n#define WAVE_FORMAT_4S16    0x00000800\n#endif\n\n#if !defined(WAVE_FORMAT_44M08)\n#define WAVE_FORMAT_44M08   0x00000100\n#define WAVE_FORMAT_44S08   0x00000200\n#define WAVE_FORMAT_44M16   0x00000400\n#define WAVE_FORMAT_44S16   0x00000800\n#define WAVE_FORMAT_48M08   0x00001000\n#define WAVE_FORMAT_48S08   0x00002000\n#define WAVE_FORMAT_48M16   0x00004000\n#define WAVE_FORMAT_48S16   0x00008000\n#define WAVE_FORMAT_96M08   0x00010000\n#define WAVE_FORMAT_96S08   0x00020000\n#define WAVE_FORMAT_96M16   0x00040000\n#define WAVE_FORMAT_96S16   0x00080000\n#endif\n\n#ifndef SPEAKER_FRONT_LEFT\n#define SPEAKER_FRONT_LEFT            0x1\n#define SPEAKER_FRONT_RIGHT           0x2\n#define SPEAKER_FRONT_CENTER          0x4\n#define SPEAKER_LOW_FREQUENCY         0x8\n#define SPEAKER_BACK_LEFT             0x10\n#define SPEAKER_BACK_RIGHT            0x20\n#define SPEAKER_FRONT_LEFT_OF_CENTER  0x40\n#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80\n#define SPEAKER_BACK_CENTER           0x100\n#define SPEAKER_SIDE_LEFT             0x200\n#define SPEAKER_SIDE_RIGHT            0x400\n#define SPEAKER_TOP_CENTER            0x800\n#define SPEAKER_TOP_FRONT_LEFT        0x1000\n#define SPEAKER_TOP_FRONT_CENTER      0x2000\n#define SPEAKER_TOP_FRONT_RIGHT       0x4000\n#define SPEAKER_TOP_BACK_LEFT         0x8000\n#define SPEAKER_TOP_BACK_CENTER       0x10000\n#define SPEAKER_TOP_BACK_RIGHT        0x20000\n#endif\n\n/*\nImplement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this\nbecause MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The\nstandard version uses tight packing, but for compiler compatibility we're not doing that with ours.\n*/\ntypedef struct\n{\n    WORD wFormatTag;\n    WORD nChannels;\n    DWORD nSamplesPerSec;\n    DWORD nAvgBytesPerSec;\n    WORD nBlockAlign;\n    WORD wBitsPerSample;\n    WORD cbSize;\n} MA_WAVEFORMATEX;\n\ntypedef struct\n{\n    WORD wFormatTag;\n    WORD nChannels;\n    DWORD nSamplesPerSec;\n    DWORD nAvgBytesPerSec;\n    WORD nBlockAlign;\n    WORD wBitsPerSample;\n    WORD cbSize;\n    union\n    {\n        WORD wValidBitsPerSample;\n        WORD wSamplesPerBlock;\n        WORD wReserved;\n    } Samples;\n    DWORD dwChannelMask;\n    GUID SubFormat;\n} MA_WAVEFORMATEXTENSIBLE;\n\n\n\n#ifndef WAVE_FORMAT_EXTENSIBLE\n#define WAVE_FORMAT_EXTENSIBLE  0xFFFE\n#endif\n\n#ifndef WAVE_FORMAT_PCM\n#define WAVE_FORMAT_PCM         1\n#endif\n\n#ifndef WAVE_FORMAT_IEEE_FLOAT\n#define WAVE_FORMAT_IEEE_FLOAT  0x0003\n#endif\n\n/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */\nstatic ma_uint8 ma_channel_id_to_ma__win32(DWORD id)\n{\n    switch (id)\n    {\n        case SPEAKER_FRONT_LEFT:            return MA_CHANNEL_FRONT_LEFT;\n        case SPEAKER_FRONT_RIGHT:           return MA_CHANNEL_FRONT_RIGHT;\n        case SPEAKER_FRONT_CENTER:          return MA_CHANNEL_FRONT_CENTER;\n        case SPEAKER_LOW_FREQUENCY:         return MA_CHANNEL_LFE;\n        case SPEAKER_BACK_LEFT:             return MA_CHANNEL_BACK_LEFT;\n        case SPEAKER_BACK_RIGHT:            return MA_CHANNEL_BACK_RIGHT;\n        case SPEAKER_FRONT_LEFT_OF_CENTER:  return MA_CHANNEL_FRONT_LEFT_CENTER;\n        case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;\n        case SPEAKER_BACK_CENTER:           return MA_CHANNEL_BACK_CENTER;\n        case SPEAKER_SIDE_LEFT:             return MA_CHANNEL_SIDE_LEFT;\n        case SPEAKER_SIDE_RIGHT:            return MA_CHANNEL_SIDE_RIGHT;\n        case SPEAKER_TOP_CENTER:            return MA_CHANNEL_TOP_CENTER;\n        case SPEAKER_TOP_FRONT_LEFT:        return MA_CHANNEL_TOP_FRONT_LEFT;\n        case SPEAKER_TOP_FRONT_CENTER:      return MA_CHANNEL_TOP_FRONT_CENTER;\n        case SPEAKER_TOP_FRONT_RIGHT:       return MA_CHANNEL_TOP_FRONT_RIGHT;\n        case SPEAKER_TOP_BACK_LEFT:         return MA_CHANNEL_TOP_BACK_LEFT;\n        case SPEAKER_TOP_BACK_CENTER:       return MA_CHANNEL_TOP_BACK_CENTER;\n        case SPEAKER_TOP_BACK_RIGHT:        return MA_CHANNEL_TOP_BACK_RIGHT;\n        default: return 0;\n    }\n}\n\n/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */\nstatic DWORD ma_channel_id_to_win32(DWORD id)\n{\n    switch (id)\n    {\n        case MA_CHANNEL_MONO:               return SPEAKER_FRONT_CENTER;\n        case MA_CHANNEL_FRONT_LEFT:         return SPEAKER_FRONT_LEFT;\n        case MA_CHANNEL_FRONT_RIGHT:        return SPEAKER_FRONT_RIGHT;\n        case MA_CHANNEL_FRONT_CENTER:       return SPEAKER_FRONT_CENTER;\n        case MA_CHANNEL_LFE:                return SPEAKER_LOW_FREQUENCY;\n        case MA_CHANNEL_BACK_LEFT:          return SPEAKER_BACK_LEFT;\n        case MA_CHANNEL_BACK_RIGHT:         return SPEAKER_BACK_RIGHT;\n        case MA_CHANNEL_FRONT_LEFT_CENTER:  return SPEAKER_FRONT_LEFT_OF_CENTER;\n        case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER;\n        case MA_CHANNEL_BACK_CENTER:        return SPEAKER_BACK_CENTER;\n        case MA_CHANNEL_SIDE_LEFT:          return SPEAKER_SIDE_LEFT;\n        case MA_CHANNEL_SIDE_RIGHT:         return SPEAKER_SIDE_RIGHT;\n        case MA_CHANNEL_TOP_CENTER:         return SPEAKER_TOP_CENTER;\n        case MA_CHANNEL_TOP_FRONT_LEFT:     return SPEAKER_TOP_FRONT_LEFT;\n        case MA_CHANNEL_TOP_FRONT_CENTER:   return SPEAKER_TOP_FRONT_CENTER;\n        case MA_CHANNEL_TOP_FRONT_RIGHT:    return SPEAKER_TOP_FRONT_RIGHT;\n        case MA_CHANNEL_TOP_BACK_LEFT:      return SPEAKER_TOP_BACK_LEFT;\n        case MA_CHANNEL_TOP_BACK_CENTER:    return SPEAKER_TOP_BACK_CENTER;\n        case MA_CHANNEL_TOP_BACK_RIGHT:     return SPEAKER_TOP_BACK_RIGHT;\n        default: return 0;\n    }\n}\n\n/* Converts a channel mapping to a Win32-style channel mask. */\nstatic DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels)\n{\n    DWORD dwChannelMask = 0;\n    ma_uint32 iChannel;\n\n    for (iChannel = 0; iChannel < channels; ++iChannel) {\n        dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]);\n    }\n\n    return dwChannelMask;\n}\n\n/* Converts a Win32-style channel mask to a miniaudio channel map. */\nstatic void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap)\n{\n    /* If the channel mask is set to 0, just assume a default Win32 channel map. */\n    if (dwChannelMask == 0) {\n        ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels);\n    } else {\n        if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {\n            pChannelMap[0] = MA_CHANNEL_MONO;\n        } else {\n            /* Just iterate over each bit. */\n            ma_uint32 iChannel = 0;\n            ma_uint32 iBit;\n\n            for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {\n                DWORD bitValue = (dwChannelMask & (1UL << iBit));\n                if (bitValue != 0) {\n                    /* The bit is set. */\n                    pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);\n                    iChannel += 1;\n                }\n            }\n        }\n    }\n}\n\n#ifdef __cplusplus\nstatic ma_bool32 ma_is_guid_equal(const void* a, const void* b)\n{\n    return IsEqualGUID(*(const GUID*)a, *(const GUID*)b);\n}\n#else\n#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)\n#endif\n\nstatic MA_INLINE ma_bool32 ma_is_guid_null(const void* guid)\n{\n    static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};\n    return ma_is_guid_equal(guid, &nullguid);\n}\n\nstatic ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF)\n{\n    MA_ASSERT(pWF != NULL);\n\n    if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {\n        const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF;\n        if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {\n            if (pWFEX->Samples.wValidBitsPerSample == 32) {\n                return ma_format_s32;\n            }\n            if (pWFEX->Samples.wValidBitsPerSample == 24) {\n                if (pWFEX->wBitsPerSample == 32) {\n                    return ma_format_s32;\n                }\n                if (pWFEX->wBitsPerSample == 24) {\n                    return ma_format_s24;\n                }\n            }\n            if (pWFEX->Samples.wValidBitsPerSample == 16) {\n                return ma_format_s16;\n            }\n            if (pWFEX->Samples.wValidBitsPerSample == 8) {\n                return ma_format_u8;\n            }\n        }\n        if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {\n            if (pWFEX->Samples.wValidBitsPerSample == 32) {\n                return ma_format_f32;\n            }\n            /*\n            if (pWFEX->Samples.wValidBitsPerSample == 64) {\n                return ma_format_f64;\n            }\n            */\n        }\n    } else {\n        if (pWF->wFormatTag == WAVE_FORMAT_PCM) {\n            if (pWF->wBitsPerSample == 32) {\n                return ma_format_s32;\n            }\n            if (pWF->wBitsPerSample == 24) {\n                return ma_format_s24;\n            }\n            if (pWF->wBitsPerSample == 16) {\n                return ma_format_s16;\n            }\n            if (pWF->wBitsPerSample == 8) {\n                return ma_format_u8;\n            }\n        }\n        if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {\n            if (pWF->wBitsPerSample == 32) {\n                return ma_format_f32;\n            }\n            if (pWF->wBitsPerSample == 64) {\n                /*return ma_format_f64;*/\n            }\n        }\n    }\n\n    return ma_format_unknown;\n}\n#endif\n\n\n/*******************************************************************************\n\nWASAPI Backend\n\n*******************************************************************************/\n#ifdef MA_HAS_WASAPI\n#if 0\n#if defined(_MSC_VER)\n    #pragma warning(push)\n    #pragma warning(disable:4091)   /* 'typedef ': ignored on left of '' when no variable is declared */\n#endif\n#include <audioclient.h>\n#include <mmdeviceapi.h>\n#if defined(_MSC_VER)\n    #pragma warning(pop)\n#endif\n#endif  /* 0 */\n\nstatic ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType);\n\n/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */\n#define MA_WIN32_WINNT_VISTA    0x0600\n#define MA_VER_MINORVERSION     0x01\n#define MA_VER_MAJORVERSION     0x02\n#define MA_VER_SERVICEPACKMAJOR 0x20\n#define MA_VER_GREATER_EQUAL    0x03\n\ntypedef struct  {\n    DWORD dwOSVersionInfoSize;\n    DWORD dwMajorVersion;\n    DWORD dwMinorVersion;\n    DWORD dwBuildNumber;\n    DWORD dwPlatformId;\n    WCHAR szCSDVersion[128];\n    WORD  wServicePackMajor;\n    WORD  wServicePackMinor;\n    WORD  wSuiteMask;\n    BYTE  wProductType;\n    BYTE  wReserved;\n} ma_OSVERSIONINFOEXW;\n\ntypedef BOOL      (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);\ntypedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);\n\n\n#ifndef PROPERTYKEY_DEFINED\n#define PROPERTYKEY_DEFINED\n#ifndef __WATCOMC__\ntypedef struct\n{\n    GUID fmtid;\n    DWORD pid;\n} PROPERTYKEY;\n#endif\n#endif\n\n/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */\nstatic MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp)\n{\n    MA_ZERO_OBJECT(pProp);\n}\n\n\nstatic const PROPERTYKEY MA_PKEY_Device_FriendlyName             = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14};\nstatic const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat        = {{0xF19F064D, 0x82C,  0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}},  0};\n\nstatic const IID MA_IID_IUnknown                                 = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */\n#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)\nstatic const IID MA_IID_IAgileObject                             = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */\n#endif\n\nstatic const IID MA_IID_IAudioClient                             = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */\nstatic const IID MA_IID_IAudioClient2                            = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */\nstatic const IID MA_IID_IAudioClient3                            = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */\nstatic const IID MA_IID_IAudioRenderClient                       = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */\nstatic const IID MA_IID_IAudioCaptureClient                      = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */\nstatic const IID MA_IID_IMMNotificationClient                    = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */\n#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)\nstatic const IID MA_IID_DEVINTERFACE_AUDIO_RENDER                = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */\nstatic const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE               = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */\nstatic const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */\n#endif\n\nstatic const IID MA_CLSID_MMDeviceEnumerator                     = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */\nstatic const IID MA_IID_IMMDeviceEnumerator                      = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */\n\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n#define MA_MM_DEVICE_STATE_ACTIVE                          1\n#define MA_MM_DEVICE_STATE_DISABLED                        2\n#define MA_MM_DEVICE_STATE_NOTPRESENT                      4\n#define MA_MM_DEVICE_STATE_UNPLUGGED                       8\n\ntypedef struct ma_IMMDeviceEnumerator                      ma_IMMDeviceEnumerator;\ntypedef struct ma_IMMDeviceCollection                      ma_IMMDeviceCollection;\ntypedef struct ma_IMMDevice                                ma_IMMDevice;\n#else\ntypedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler;\ntypedef struct ma_IActivateAudioInterfaceAsyncOperation    ma_IActivateAudioInterfaceAsyncOperation;\n#endif\ntypedef struct ma_IPropertyStore                           ma_IPropertyStore;\ntypedef struct ma_IAudioClient                             ma_IAudioClient;\ntypedef struct ma_IAudioClient2                            ma_IAudioClient2;\ntypedef struct ma_IAudioClient3                            ma_IAudioClient3;\ntypedef struct ma_IAudioRenderClient                       ma_IAudioRenderClient;\ntypedef struct ma_IAudioCaptureClient                      ma_IAudioCaptureClient;\n\ntypedef ma_int64                                           MA_REFERENCE_TIME;\n\n#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS                0x00010000\n#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK                    0x00020000\n#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK               0x00040000\n#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST                   0x00080000\n#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST                  0x00100000\n#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY         0x08000000\n#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM              0x80000000\n#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED          0x10000000\n#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE               0x20000000\n#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED    0x40000000\n\n/* Buffer flags. */\n#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY          1\n#define MA_AUDCLNT_BUFFERFLAGS_SILENT                      2\n#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR             4\n\ntypedef enum\n{\n    ma_eRender  = 0,\n    ma_eCapture = 1,\n    ma_eAll     = 2\n} ma_EDataFlow;\n\ntypedef enum\n{\n    ma_eConsole        = 0,\n    ma_eMultimedia     = 1,\n    ma_eCommunications = 2\n} ma_ERole;\n\ntypedef enum\n{\n    MA_AUDCLNT_SHAREMODE_SHARED,\n    MA_AUDCLNT_SHAREMODE_EXCLUSIVE\n} MA_AUDCLNT_SHAREMODE;\n\ntypedef enum\n{\n    MA_AudioCategory_Other = 0  /* <-- miniaudio is only caring about Other. */\n} MA_AUDIO_STREAM_CATEGORY;\n\ntypedef struct\n{\n    ma_uint32 cbSize;\n    BOOL bIsOffload;\n    MA_AUDIO_STREAM_CATEGORY eCategory;\n} ma_AudioClientProperties;\n\n/* IUnknown */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IUnknown* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IUnknown* pThis);\n} ma_IUnknownVtbl;\nstruct ma_IUnknown\n{\n    ma_IUnknownVtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IUnknown_AddRef(ma_IUnknown* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IUnknown_Release(ma_IUnknown* pThis)                                                { return pThis->lpVtbl->Release(pThis); }\n\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    /* IMMNotificationClient */\n    typedef struct\n    {\n        /* IUnknown */\n        HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject);\n        ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IMMNotificationClient* pThis);\n        ULONG   (STDMETHODCALLTYPE * Release)       (ma_IMMNotificationClient* pThis);\n\n        /* IMMNotificationClient */\n        HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged)  (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState);\n        HRESULT (STDMETHODCALLTYPE * OnDeviceAdded)         (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID);\n        HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved)       (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID);\n        HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID);\n        HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key);\n    } ma_IMMNotificationClientVtbl;\n\n    /* IMMDeviceEnumerator */\n    typedef struct\n    {\n        /* IUnknown */\n        HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject);\n        ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IMMDeviceEnumerator* pThis);\n        ULONG   (STDMETHODCALLTYPE * Release)       (ma_IMMDeviceEnumerator* pThis);\n\n        /* IMMDeviceEnumerator */\n        HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints)                    (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices);\n        HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint)               (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint);\n        HRESULT (STDMETHODCALLTYPE * GetDevice)                             (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice);\n        HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback)  (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);\n        HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);\n    } ma_IMMDeviceEnumeratorVtbl;\n    struct ma_IMMDeviceEnumerator\n    {\n        ma_IMMDeviceEnumeratorVtbl* lpVtbl;\n    };\n    static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\n    static MA_INLINE ULONG   ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }\n    static MA_INLINE ULONG   ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis)                                                { return pThis->lpVtbl->Release(pThis); }\n    static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); }\n    static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); }\n    static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); }\n    static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); }\n    static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); }\n\n\n    /* IMMDeviceCollection */\n    typedef struct\n    {\n        /* IUnknown */\n        HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject);\n        ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IMMDeviceCollection* pThis);\n        ULONG   (STDMETHODCALLTYPE * Release)       (ma_IMMDeviceCollection* pThis);\n\n        /* IMMDeviceCollection */\n        HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices);\n        HRESULT (STDMETHODCALLTYPE * Item)    (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice);\n    } ma_IMMDeviceCollectionVtbl;\n    struct ma_IMMDeviceCollection\n    {\n        ma_IMMDeviceCollectionVtbl* lpVtbl;\n    };\n    static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\n    static MA_INLINE ULONG   ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }\n    static MA_INLINE ULONG   ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis)                                                { return pThis->lpVtbl->Release(pThis); }\n    static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices)                               { return pThis->lpVtbl->GetCount(pThis, pDevices); }\n    static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice)            { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); }\n\n\n    /* IMMDevice */\n    typedef struct\n    {\n        /* IUnknown */\n        HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject);\n        ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IMMDevice* pThis);\n        ULONG   (STDMETHODCALLTYPE * Release)       (ma_IMMDevice* pThis);\n\n        /* IMMDevice */\n        HRESULT (STDMETHODCALLTYPE * Activate)         (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface);\n        HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties);\n        HRESULT (STDMETHODCALLTYPE * GetId)            (ma_IMMDevice* pThis, WCHAR** pID);\n        HRESULT (STDMETHODCALLTYPE * GetState)         (ma_IMMDevice* pThis, DWORD *pState);\n    } ma_IMMDeviceVtbl;\n    struct ma_IMMDevice\n    {\n        ma_IMMDeviceVtbl* lpVtbl;\n    };\n    static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\n    static MA_INLINE ULONG   ma_IMMDevice_AddRef(ma_IMMDevice* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }\n    static MA_INLINE ULONG   ma_IMMDevice_Release(ma_IMMDevice* pThis)                                                { return pThis->lpVtbl->Release(pThis); }\n    static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); }\n    static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); }\n    static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID)                                     { return pThis->lpVtbl->GetId(pThis, pID); }\n    static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState)                                { return pThis->lpVtbl->GetState(pThis, pState); }\n#else\n    /* IActivateAudioInterfaceAsyncOperation */\n    typedef struct\n    {\n        /* IUnknown */\n        HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject);\n        ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IActivateAudioInterfaceAsyncOperation* pThis);\n        ULONG   (STDMETHODCALLTYPE * Release)       (ma_IActivateAudioInterfaceAsyncOperation* pThis);\n\n        /* IActivateAudioInterfaceAsyncOperation */\n        HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface);\n    } ma_IActivateAudioInterfaceAsyncOperationVtbl;\n    struct ma_IActivateAudioInterfaceAsyncOperation\n    {\n        ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl;\n    };\n    static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\n    static MA_INLINE ULONG   ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }\n    static MA_INLINE ULONG   ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis)                                                { return pThis->lpVtbl->Release(pThis); }\n    static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); }\n#endif\n\n/* IPropertyStore */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IPropertyStore* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IPropertyStore* pThis);\n\n    /* IPropertyStore */\n    HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount);\n    HRESULT (STDMETHODCALLTYPE * GetAt)   (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey);\n    HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar);\n    HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar);\n    HRESULT (STDMETHODCALLTYPE * Commit)  (ma_IPropertyStore* pThis);\n} ma_IPropertyStoreVtbl;\nstruct ma_IPropertyStore\n{\n    ma_IPropertyStoreVtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IPropertyStore_Release(ma_IPropertyStore* pThis)                                                { return pThis->lpVtbl->Release(pThis); }\nstatic MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount)                            { return pThis->lpVtbl->GetCount(pThis, pPropCount); }\nstatic MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey)          { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); }\nstatic MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); }\nstatic MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); }\nstatic MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis)                                                 { return pThis->lpVtbl->Commit(pThis); }\n\n\n/* IAudioClient */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IAudioClient* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IAudioClient* pThis);\n\n    /* IAudioClient */\n    HRESULT (STDMETHODCALLTYPE * Initialize)       (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);\n    HRESULT (STDMETHODCALLTYPE * GetBufferSize)    (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames);\n    HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency);\n    HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames);\n    HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);\n    HRESULT (STDMETHODCALLTYPE * GetMixFormat)     (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat);\n    HRESULT (STDMETHODCALLTYPE * GetDevicePeriod)  (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);\n    HRESULT (STDMETHODCALLTYPE * Start)            (ma_IAudioClient* pThis);\n    HRESULT (STDMETHODCALLTYPE * Stop)             (ma_IAudioClient* pThis);\n    HRESULT (STDMETHODCALLTYPE * Reset)            (ma_IAudioClient* pThis);\n    HRESULT (STDMETHODCALLTYPE * SetEventHandle)   (ma_IAudioClient* pThis, HANDLE eventHandle);\n    HRESULT (STDMETHODCALLTYPE * GetService)       (ma_IAudioClient* pThis, const IID* const riid, void** pp);\n} ma_IAudioClientVtbl;\nstruct ma_IAudioClient\n{\n    ma_IAudioClientVtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject)    { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IAudioClient_AddRef(ma_IAudioClient* pThis)                                                    { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IAudioClient_Release(ma_IAudioClient* pThis)                                                   { return pThis->lpVtbl->Release(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }\nstatic MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames)                { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }\nstatic MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency)             { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }\nstatic MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames)           { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }\nstatic MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }\nstatic MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat)            { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }\nstatic MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }\nstatic MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis)                                                     { return pThis->lpVtbl->Start(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis)                                                      { return pThis->lpVtbl->Stop(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis)                                                     { return pThis->lpVtbl->Reset(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle)                        { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }\nstatic MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp)              { return pThis->lpVtbl->GetService(pThis, riid, pp); }\n\n/* IAudioClient2 */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IAudioClient2* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IAudioClient2* pThis);\n\n    /* IAudioClient */\n    HRESULT (STDMETHODCALLTYPE * Initialize)       (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);\n    HRESULT (STDMETHODCALLTYPE * GetBufferSize)    (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames);\n    HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency);\n    HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames);\n    HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);\n    HRESULT (STDMETHODCALLTYPE * GetMixFormat)     (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat);\n    HRESULT (STDMETHODCALLTYPE * GetDevicePeriod)  (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);\n    HRESULT (STDMETHODCALLTYPE * Start)            (ma_IAudioClient2* pThis);\n    HRESULT (STDMETHODCALLTYPE * Stop)             (ma_IAudioClient2* pThis);\n    HRESULT (STDMETHODCALLTYPE * Reset)            (ma_IAudioClient2* pThis);\n    HRESULT (STDMETHODCALLTYPE * SetEventHandle)   (ma_IAudioClient2* pThis, HANDLE eventHandle);\n    HRESULT (STDMETHODCALLTYPE * GetService)       (ma_IAudioClient2* pThis, const IID* const riid, void** pp);\n\n    /* IAudioClient2 */\n    HRESULT (STDMETHODCALLTYPE * IsOffloadCapable)   (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);\n    HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties);\n    HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);\n} ma_IAudioClient2Vtbl;\nstruct ma_IAudioClient2\n{\n    ma_IAudioClient2Vtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject)    { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis)                                                    { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IAudioClient2_Release(ma_IAudioClient2* pThis)                                                   { return pThis->lpVtbl->Release(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames)                { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency)             { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames)           { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat)            { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis)                                                     { return pThis->lpVtbl->Start(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis)                                                      { return pThis->lpVtbl->Stop(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis)                                                     { return pThis->lpVtbl->Reset(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle)                        { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp)              { return pThis->lpVtbl->GetService(pThis, riid, pp); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties)           { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }\nstatic MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); }\n\n\n/* IAudioClient3 */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IAudioClient3* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IAudioClient3* pThis);\n\n    /* IAudioClient */\n    HRESULT (STDMETHODCALLTYPE * Initialize)       (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);\n    HRESULT (STDMETHODCALLTYPE * GetBufferSize)    (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames);\n    HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency);\n    HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames);\n    HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);\n    HRESULT (STDMETHODCALLTYPE * GetMixFormat)     (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat);\n    HRESULT (STDMETHODCALLTYPE * GetDevicePeriod)  (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);\n    HRESULT (STDMETHODCALLTYPE * Start)            (ma_IAudioClient3* pThis);\n    HRESULT (STDMETHODCALLTYPE * Stop)             (ma_IAudioClient3* pThis);\n    HRESULT (STDMETHODCALLTYPE * Reset)            (ma_IAudioClient3* pThis);\n    HRESULT (STDMETHODCALLTYPE * SetEventHandle)   (ma_IAudioClient3* pThis, HANDLE eventHandle);\n    HRESULT (STDMETHODCALLTYPE * GetService)       (ma_IAudioClient3* pThis, const IID* const riid, void** pp);\n\n    /* IAudioClient2 */\n    HRESULT (STDMETHODCALLTYPE * IsOffloadCapable)   (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);\n    HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties);\n    HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);\n\n    /* IAudioClient3 */\n    HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod)       (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames);\n    HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames);\n    HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream)     (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);\n} ma_IAudioClient3Vtbl;\nstruct ma_IAudioClient3\n{\n    ma_IAudioClient3Vtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject)    { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis)                                                    { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IAudioClient3_Release(ma_IAudioClient3* pThis)                                                   { return pThis->lpVtbl->Release(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames)                { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency)             { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames)           { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat)               { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis)                                                     { return pThis->lpVtbl->Start(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis)                                                      { return pThis->lpVtbl->Stop(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis)                                                     { return pThis->lpVtbl->Reset(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle)                        { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp)              { return pThis->lpVtbl->GetService(pThis, riid, pp); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties)           { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); }\nstatic MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); }\n\n\n/* IAudioRenderClient */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IAudioRenderClient* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IAudioRenderClient* pThis);\n\n    /* IAudioRenderClient */\n    HRESULT (STDMETHODCALLTYPE * GetBuffer)    (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData);\n    HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags);\n} ma_IAudioRenderClientVtbl;\nstruct ma_IAudioRenderClient\n{\n    ma_IAudioRenderClientVtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject)   { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis)                                                   { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis)                                                  { return pThis->lpVtbl->Release(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData)   { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }\nstatic MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }\n\n\n/* IAudioCaptureClient */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IAudioCaptureClient* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IAudioCaptureClient* pThis);\n\n    /* IAudioRenderClient */\n    HRESULT (STDMETHODCALLTYPE * GetBuffer)        (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);\n    HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)    (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);\n    HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);\n} ma_IAudioCaptureClientVtbl;\nstruct ma_IAudioCaptureClient\n{\n    ma_IAudioCaptureClientVtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis)                                                { return pThis->lpVtbl->Release(pThis); }\nstatic MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); }\nstatic MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead)                 { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }\nstatic MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket)   { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }\n\n#if defined(MA_WIN32_UWP)\n/* mmdevapi Functions */\ntypedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation);\n#endif\n\n/* Avrt Functions */\ntypedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex);\ntypedef BOOL   (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle);\n\n#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)\ntypedef struct ma_completion_handler_uwp ma_completion_handler_uwp;\n\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_completion_handler_uwp* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_completion_handler_uwp* pThis);\n\n    /* IActivateAudioInterfaceCompletionHandler */\n    HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);\n} ma_completion_handler_uwp_vtbl;\nstruct ma_completion_handler_uwp\n{\n    ma_completion_handler_uwp_vtbl* lpVtbl;\n    MA_ATOMIC(4, ma_uint32) counter;\n    HANDLE hEvent;\n};\n\nstatic HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)\n{\n    /*\n    We need to \"implement\" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To\n    \"implement\" this, we just make sure we return pThis when the IAgileObject is requested.\n    */\n    if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) {\n        *ppObject = NULL;\n        return E_NOINTERFACE;\n    }\n\n    /* Getting here means the IID is IUnknown or IMMNotificationClient. */\n    *ppObject = (void*)pThis;\n    ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);\n    return S_OK;\n}\n\nstatic ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)\n{\n    return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1;\n}\n\nstatic ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)\n{\n    ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1;\n    if (newRefCount == 0) {\n        return 0;   /* We don't free anything here because we never allocate the object on the heap. */\n    }\n\n    return (ULONG)newRefCount;\n}\n\nstatic HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)\n{\n    (void)pActivateOperation;\n    SetEvent(pThis->hEvent);\n    return S_OK;\n}\n\n\nstatic ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {\n    ma_completion_handler_uwp_QueryInterface,\n    ma_completion_handler_uwp_AddRef,\n    ma_completion_handler_uwp_Release,\n    ma_completion_handler_uwp_ActivateCompleted\n};\n\nstatic ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)\n{\n    MA_ASSERT(pHandler != NULL);\n    MA_ZERO_OBJECT(pHandler);\n\n    pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;\n    pHandler->counter = 1;\n    pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);\n    if (pHandler->hEvent == NULL) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)\n{\n    if (pHandler->hEvent != NULL) {\n        CloseHandle(pHandler->hEvent);\n    }\n}\n\nstatic void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)\n{\n    WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE);\n}\n#endif  /* !MA_WIN32_DESKTOP */\n\n/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\nstatic HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)\n{\n    /*\n    We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else\n    we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.\n    */\n    if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {\n        *ppObject = NULL;\n        return E_NOINTERFACE;\n    }\n\n    /* Getting here means the IID is IUnknown or IMMNotificationClient. */\n    *ppObject = (void*)pThis;\n    ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);\n    return S_OK;\n}\n\nstatic ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)\n{\n    return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1;\n}\n\nstatic ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)\n{\n    ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1;\n    if (newRefCount == 0) {\n        return 0;   /* We don't free anything here because we never allocate the object on the heap. */\n    }\n\n    return (ULONG)newRefCount;\n}\n\nstatic HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState)\n{\n    ma_bool32 isThisDevice = MA_FALSE;\n    ma_bool32 isCapture    = MA_FALSE;\n    ma_bool32 isPlayback   = MA_FALSE;\n\n#ifdef MA_DEBUG_OUTPUT\n    /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, \"IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\\n\", (pDeviceID != NULL) ? pDeviceID : L\"(NULL)\", (unsigned int)dwNewState);*/\n#endif\n\n    /*\n    There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect\n    that the device is disabled or has been unplugged.\n    */\n    if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) {\n        isCapture = MA_TRUE;\n        if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) {\n            isThisDevice = MA_TRUE;\n        }\n    }\n\n    if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) {\n        isPlayback = MA_TRUE;\n        if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) {\n            isThisDevice = MA_TRUE;\n        }\n    }\n\n\n    /*\n    If the device ID matches our device we need to mark our device as detached and stop it. When a\n    device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device\n    was started at the time of being removed.\n    */\n    if (isThisDevice) {\n        if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) {\n            /*\n            Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll\n            use this to determine whether or not we need to automatically start the device when it's\n            plugged back in again.\n            */\n            if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) {\n                if (isPlayback) {\n                    pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE;\n                }\n                if (isCapture) {\n                    pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE;\n                }\n\n                ma_device_stop(pThis->pDevice);\n            }\n        }\n\n        if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) {\n            /* The device was activated. If we were detached, we need to start it again. */\n            ma_bool8 tryRestartingDevice = MA_FALSE;\n\n            if (isPlayback) {\n                if (pThis->pDevice->wasapi.isDetachedPlayback) {\n                    pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;\n                    ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);\n                    tryRestartingDevice = MA_TRUE;\n                }\n            }\n\n            if (isCapture) {\n                if (pThis->pDevice->wasapi.isDetachedCapture) {\n                    pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;\n                    ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);\n                    tryRestartingDevice = MA_TRUE;\n                }\n            }\n\n            if (tryRestartingDevice) {\n                if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) {\n                    ma_device_start(pThis->pDevice);\n                }\n            }\n        }\n    }\n\n    return S_OK;\n}\n\nstatic HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID)\n{\n#ifdef MA_DEBUG_OUTPUT\n    /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, \"IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\\n\", (pDeviceID != NULL) ? pDeviceID : L\"(NULL)\");*/\n#endif\n\n    /* We don't need to worry about this event for our purposes. */\n    (void)pThis;\n    (void)pDeviceID;\n    return S_OK;\n}\n\nstatic HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID)\n{\n#ifdef MA_DEBUG_OUTPUT\n    /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, \"IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\\n\", (pDeviceID != NULL) ? pDeviceID : L\"(NULL)\");*/\n#endif\n\n    /* We don't need to worry about this event for our purposes. */\n    (void)pThis;\n    (void)pDeviceID;\n    return S_OK;\n}\n\nstatic HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID)\n{\n#ifdef MA_DEBUG_OUTPUT\n    /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, \"IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\\n\", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L\"(NULL)\");*/\n#endif\n\n    (void)role;\n\n    /* We only care about devices with the same data flow as the current device. */\n    if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender)  ||\n        (pThis->pDevice->type == ma_device_type_capture  && dataFlow != ma_eCapture) ||\n        (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) {\n        ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\\n\");\n        return S_OK;\n    }\n\n    /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */\n    if (pThis->pDevice->type == ma_device_type_loopback) {\n        dataFlow = ma_eCapture;\n    }\n\n    /* Don't do automatic stream routing if we're not allowed. */\n    if ((dataFlow == ma_eRender  && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) ||\n        (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting  == MA_FALSE)) {\n        ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\\n\");\n        return S_OK;\n    }\n\n    /*\n    Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to\n    AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once\n    it's fixed.\n    */\n    if ((dataFlow == ma_eRender  && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||\n        (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode  == ma_share_mode_exclusive)) {\n        ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\\n\");\n        return S_OK;\n    }\n\n\n\n    /*\n    Second attempt at device rerouting. We're going to retrieve the device's state at the time of\n    the route change. We're then going to stop the device, reinitialize the device, and then start\n    it again if the state before stopping was ma_device_state_started.\n    */\n    {\n        ma_uint32 previousState = ma_device_get_state(pThis->pDevice);\n        ma_bool8 restartDevice = MA_FALSE;\n\n        if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) {\n            ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Stream rerouting abandoned because the device is in the process of starting.\\n\");\n            return S_OK;\n        }\n\n        if (previousState == ma_device_state_started) {\n            ma_device_stop(pThis->pDevice);\n            restartDevice = MA_TRUE;\n        }\n\n        if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */\n            ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock);\n            {\n                if (dataFlow == ma_eRender) {\n                    ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);\n\n                    if (pThis->pDevice->wasapi.isDetachedPlayback) {\n                        pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;\n\n                        if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) {\n                            restartDevice = MA_FALSE;   /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */\n                        }\n                        else {\n                            restartDevice = MA_TRUE;    /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */\n                        }\n                    }\n                }\n                else {\n                    ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);\n\n                    if (pThis->pDevice->wasapi.isDetachedCapture) {\n                        pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;\n\n                        if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) {\n                            restartDevice = MA_FALSE;   /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */\n                        }\n                        else {\n                            restartDevice = MA_TRUE;    /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */\n                        }\n                    }\n                }\n            }\n            ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock);\n\n            if (restartDevice) {\n                ma_device_start(pThis->pDevice);\n            }\n        }\n    }\n\n    return S_OK;\n}\n\nstatic HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key)\n{\n#ifdef MA_DEBUG_OUTPUT\n    /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, \"IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\\n\", (pDeviceID != NULL) ? pDeviceID : L\"(NULL)\");*/\n#endif\n\n    (void)pThis;\n    (void)pDeviceID;\n    (void)key;\n    return S_OK;\n}\n\nstatic ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {\n    ma_IMMNotificationClient_QueryInterface,\n    ma_IMMNotificationClient_AddRef,\n    ma_IMMNotificationClient_Release,\n    ma_IMMNotificationClient_OnDeviceStateChanged,\n    ma_IMMNotificationClient_OnDeviceAdded,\n    ma_IMMNotificationClient_OnDeviceRemoved,\n    ma_IMMNotificationClient_OnDefaultDeviceChanged,\n    ma_IMMNotificationClient_OnPropertyValueChanged\n};\n#endif  /* MA_WIN32_DESKTOP */\n\nstatic const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage)\n{\n    switch (usage)\n    {\n        case ma_wasapi_usage_default:   return NULL;\n        case ma_wasapi_usage_games:     return \"Games\";\n        case ma_wasapi_usage_pro_audio: return \"Pro Audio\";\n        default: break;\n    }\n\n    return NULL;\n}\n\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\ntypedef ma_IMMDevice ma_WASAPIDeviceInterface;\n#else\ntypedef ma_IUnknown ma_WASAPIDeviceInterface;\n#endif\n\n\n#define MA_CONTEXT_COMMAND_QUIT__WASAPI                 1\n#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI  2\n#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3\n\nstatic ma_context_command__wasapi ma_context_init_command__wasapi(int code)\n{\n    ma_context_command__wasapi cmd;\n\n    MA_ZERO_OBJECT(&cmd);\n    cmd.code = code;\n\n    return cmd;\n}\n\nstatic ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd)\n{\n    /* For now we are doing everything synchronously, but I might relax this later if the need arises. */\n    ma_result result;\n    ma_bool32 isUsingLocalEvent = MA_FALSE;\n    ma_event localEvent;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pCmd     != NULL);\n\n    if (pCmd->pEvent == NULL) {\n        isUsingLocalEvent = MA_TRUE;\n\n        result = ma_event_init(&localEvent);\n        if (result != MA_SUCCESS) {\n            return result;  /* Failed to create the event for this command. */\n        }\n    }\n\n    /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */\n    ma_mutex_lock(&pContext->wasapi.commandLock);\n    {\n        ma_uint32 index;\n\n        /* Spin until we've got some space available. */\n        while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) {\n            ma_yield();\n        }\n\n        /* Space is now available. Can safely add to the list. */\n        index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands);\n        pContext->wasapi.commands[index]        = *pCmd;\n        pContext->wasapi.commands[index].pEvent = &localEvent;\n        pContext->wasapi.commandCount += 1;\n\n        /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */\n        ma_semaphore_release(&pContext->wasapi.commandSem);\n    }\n    ma_mutex_unlock(&pContext->wasapi.commandLock);\n\n    if (isUsingLocalEvent) {\n        ma_event_wait(&localEvent);\n        ma_event_uninit(&localEvent);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd)\n{\n    ma_result result = MA_SUCCESS;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pCmd     != NULL);\n\n    result = ma_semaphore_wait(&pContext->wasapi.commandSem);\n    if (result == MA_SUCCESS) {\n        ma_mutex_lock(&pContext->wasapi.commandLock);\n        {\n            *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex];\n            pContext->wasapi.commandIndex  = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands);\n            pContext->wasapi.commandCount -= 1;\n        }\n        ma_mutex_unlock(&pContext->wasapi.commandLock);\n    }\n\n    return result;\n}\n\nstatic ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData)\n{\n    ma_result result;\n    ma_context* pContext = (ma_context*)pUserData;\n    MA_ASSERT(pContext != NULL);\n\n    for (;;) {\n        ma_context_command__wasapi cmd;\n        result = ma_context_next_command__wasapi(pContext, &cmd);\n        if (result != MA_SUCCESS) {\n            break;\n        }\n\n        switch (cmd.code)\n        {\n            case MA_CONTEXT_COMMAND_QUIT__WASAPI:\n            {\n                /* Do nothing. Handled after the switch. */\n            } break;\n\n            case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI:\n            {\n                if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) {\n                    *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService));\n                } else {\n                    *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService));\n                }\n            } break;\n\n            case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI:\n            {\n                if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) {\n                    if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) {\n                        ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback);\n                        cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL;\n                    }\n                }\n\n                if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) {\n                    if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) {\n                        ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture);\n                        cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL;\n                    }\n                }\n            } break;\n\n            default:\n            {\n                /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */\n                MA_ASSERT(MA_FALSE);\n            } break;\n        }\n\n        if (cmd.pEvent != NULL) {\n            ma_event_signal(cmd.pEvent);\n        }\n\n        if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) {\n            break;  /* Received a quit message. Get out of here. */\n        }\n    }\n\n    return (ma_thread_result)0;\n}\n\nstatic ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService)\n{\n    ma_result result;\n    ma_result cmdResult;\n    ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI);\n    cmd.data.createAudioClient.deviceType           = deviceType;\n    cmd.data.createAudioClient.pAudioClient         = (void*)pAudioClient;\n    cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService;\n    cmd.data.createAudioClient.pResult              = &cmdResult;   /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */\n\n    result = ma_context_post_command__wasapi(pContext, &cmd);  /* This will not return until the command has actually been run. */\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return *cmd.data.createAudioClient.pResult;\n}\n\n#if 0   /* Not used at the moment, but leaving here for future use. */\nstatic ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType)\n{\n    ma_result result;\n    ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI);\n    cmd.data.releaseAudioClient.pDevice    = pDevice;\n    cmd.data.releaseAudioClient.deviceType = deviceType;\n\n    result = ma_context_post_command__wasapi(pDevice->pContext, &cmd);  /* This will not return until the command has actually been run. */\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n#endif\n\n\nstatic void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo)\n{\n    MA_ASSERT(pWF != NULL);\n    MA_ASSERT(pInfo != NULL);\n\n    if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) {\n        return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */\n    }\n\n    pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format     = ma_format_from_WAVEFORMATEX(pWF);\n    pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels   = pWF->nChannels;\n    pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec;\n    pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags      = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0;\n    pInfo->nativeDataFormatCount += 1;\n}\n\nstatic ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo)\n{\n    HRESULT hr;\n    MA_WAVEFORMATEX* pWF = NULL;\n\n    MA_ASSERT(pAudioClient != NULL);\n    MA_ASSERT(pInfo != NULL);\n\n    /* Shared Mode. We use GetMixFormat() here. */\n    hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF);\n    if (SUCCEEDED(hr)) {\n        ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo);\n    } else {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to retrieve mix format for device info retrieval.\");\n        return ma_result_from_HRESULT(hr);\n    }\n\n    /*\n    Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on\n    UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on\n    out, MA_SUCCESS is guaranteed to be returned.\n    */\n    #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    {\n        ma_IPropertyStore *pProperties;\n\n        /*\n        The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is\n        correct which will simplify our searching.\n        */\n        hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties);\n        if (SUCCEEDED(hr)) {\n            MA_PROPVARIANT var;\n            ma_PropVariantInit(&var);\n\n            hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var);\n            if (SUCCEEDED(hr)) {\n                pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData;\n\n                /*\n                In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format\n                first. If this fails, fall back to a search.\n                */\n                hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);\n                if (SUCCEEDED(hr)) {\n                    /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */\n                    ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo);\n                } else {\n                    /*\n                    The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel\n                    count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.\n                    */\n                    ma_uint32 channels = pWF->nChannels;\n                    ma_channel defaultChannelMap[MA_MAX_CHANNELS];\n                    MA_WAVEFORMATEXTENSIBLE wf;\n                    ma_bool32 found;\n                    ma_uint32 iFormat;\n\n                    /* Make sure we don't overflow the channel map. */\n                    if (channels > MA_MAX_CHANNELS) {\n                        channels = MA_MAX_CHANNELS;\n                    }\n\n                    ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels);\n\n                    MA_ZERO_OBJECT(&wf);\n                    wf.cbSize     = sizeof(wf);\n                    wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE;\n                    wf.nChannels  = (WORD)channels;\n                    wf.dwChannelMask     = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels);\n\n                    found = MA_FALSE;\n                    for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {\n                        ma_format format = g_maFormatPriorities[iFormat];\n                        ma_uint32 iSampleRate;\n\n                        wf.wBitsPerSample       = (WORD)(ma_get_bytes_per_sample(format)*8);\n                        wf.nBlockAlign          = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);\n                        wf.nAvgBytesPerSec      = wf.nBlockAlign * wf.nSamplesPerSec;\n                        wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample;\n                        if (format == ma_format_f32) {\n                            wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;\n                        } else {\n                            wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;\n                        }\n\n                        for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {\n                            wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];\n\n                            hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL);\n                            if (SUCCEEDED(hr)) {\n                                ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo);\n                                found = MA_TRUE;\n                                break;\n                            }\n                        }\n\n                        if (found) {\n                            break;\n                        }\n                    }\n\n                    ma_PropVariantClear(pContext, &var);\n\n                    if (!found) {\n                        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, \"[WASAPI] Failed to find suitable device format for device info retrieval.\");\n                    }\n                }\n            } else {\n                ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, \"[WASAPI] Failed to retrieve device format for device info retrieval.\");\n            }\n\n            ma_IPropertyStore_Release(pProperties);\n        } else {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, \"[WASAPI] Failed to open property store for device info retrieval.\");\n        }\n    }\n    #else\n    {\n        (void)pMMDevice;    /* Unused. */\n    }\n    #endif\n\n    return MA_SUCCESS;\n}\n\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\nstatic ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType)\n{\n    if (deviceType == ma_device_type_playback) {\n        return ma_eRender;\n    } else if (deviceType == ma_device_type_capture) {\n        return ma_eCapture;\n    } else {\n        MA_ASSERT(MA_FALSE);\n        return ma_eRender; /* Should never hit this. */\n    }\n}\n\nstatic ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator)\n{\n    HRESULT hr;\n    ma_IMMDeviceEnumerator* pDeviceEnumerator;\n\n    MA_ASSERT(pContext           != NULL);\n    MA_ASSERT(ppDeviceEnumerator != NULL);\n\n    *ppDeviceEnumerator = NULL; /* Safety. */\n\n    hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);\n    if (FAILED(hr)) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to create device enumerator.\");\n        return ma_result_from_HRESULT(hr);\n    }\n\n    *ppDeviceEnumerator = pDeviceEnumerator;\n\n    return MA_SUCCESS;\n}\n\nstatic WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType)\n{\n    HRESULT hr;\n    ma_IMMDevice* pMMDefaultDevice = NULL;\n    WCHAR* pDefaultDeviceID = NULL;\n    ma_EDataFlow dataFlow;\n    ma_ERole role;\n\n    MA_ASSERT(pContext          != NULL);\n    MA_ASSERT(pDeviceEnumerator != NULL);\n\n    (void)pContext;\n\n    /* Grab the EDataFlow type from the device type. */\n    dataFlow = ma_device_type_to_EDataFlow(deviceType);\n\n    /* The role is always eConsole, but we may make this configurable later. */\n    role = ma_eConsole;\n\n    hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice);\n    if (FAILED(hr)) {\n        return NULL;\n    }\n\n    hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID);\n\n    ma_IMMDevice_Release(pMMDefaultDevice);\n    pMMDefaultDevice = NULL;\n\n    if (FAILED(hr)) {\n        return NULL;\n    }\n\n    return pDefaultDeviceID;\n}\n\nstatic WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType)    /* Free the returned pointer with ma_CoTaskMemFree() */\n{\n    ma_result result;\n    ma_IMMDeviceEnumerator* pDeviceEnumerator;\n    WCHAR* pDefaultDeviceID = NULL;\n\n    MA_ASSERT(pContext != NULL);\n\n    result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator);\n    if (result != MA_SUCCESS) {\n        return NULL;\n    }\n\n    pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);\n\n    ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);\n    return pDefaultDeviceID;\n}\n\nstatic ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)\n{\n    ma_IMMDeviceEnumerator* pDeviceEnumerator;\n    HRESULT hr;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(ppMMDevice != NULL);\n\n    hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);\n    if (FAILED(hr)) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to create IMMDeviceEnumerator.\\n\");\n        return ma_result_from_HRESULT(hr);\n    }\n\n    if (pDeviceID == NULL) {\n        hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice);\n    } else {\n        hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);\n    }\n\n    ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);\n    if (FAILED(hr)) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to retrieve IMMDevice.\\n\");\n        return ma_result_from_HRESULT(hr);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID)\n{\n    WCHAR* pDeviceIDString;\n    HRESULT hr;\n\n    MA_ASSERT(pDeviceID != NULL);\n\n    hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString);\n    if (SUCCEEDED(hr)) {\n        size_t idlen = ma_strlen_WCHAR(pDeviceIDString);\n        if (idlen+1 > ma_countof(pDeviceID->wasapi)) {\n            ma_CoTaskMemFree(pContext, pDeviceIDString);\n            MA_ASSERT(MA_FALSE);  /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */\n            return MA_ERROR;\n        }\n\n        MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t));\n        pDeviceID->wasapi[idlen] = '\\0';\n\n        ma_CoTaskMemFree(pContext, pDeviceIDString);\n\n        return MA_SUCCESS;\n    }\n\n    return MA_ERROR;\n}\n\nstatic ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo)\n{\n    ma_result result;\n    HRESULT hr;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pMMDevice != NULL);\n    MA_ASSERT(pInfo != NULL);\n\n    /* ID. */\n    result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id);\n    if (result == MA_SUCCESS) {\n        if (pDefaultDeviceID != NULL) {\n            if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) {\n                pInfo->isDefault = MA_TRUE;\n            }\n        }\n    }\n\n    /* Description / Friendly Name */\n    {\n        ma_IPropertyStore *pProperties;\n        hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);\n        if (SUCCEEDED(hr)) {\n            MA_PROPVARIANT var;\n\n            ma_PropVariantInit(&var);\n            hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);\n            if (SUCCEEDED(hr)) {\n                WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);\n                ma_PropVariantClear(pContext, &var);\n            }\n\n            ma_IPropertyStore_Release(pProperties);\n        }\n    }\n\n    /* Format */\n    if (!onlySimpleInfo) {\n        ma_IAudioClient* pAudioClient;\n        hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);\n        if (SUCCEEDED(hr)) {\n            result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo);\n\n            ma_IAudioClient_Release(pAudioClient);\n            return result;\n        } else {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to activate audio client for device info retrieval.\");\n            return ma_result_from_HRESULT(hr);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    ma_result result = MA_SUCCESS;\n    UINT deviceCount;\n    HRESULT hr;\n    ma_uint32 iDevice;\n    WCHAR* pDefaultDeviceID = NULL;\n    ma_IMMDeviceCollection* pDeviceCollection = NULL;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */\n    pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);\n\n    /* We need to enumerate the devices which returns a device collection. */\n    hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);\n    if (SUCCEEDED(hr)) {\n        hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);\n        if (FAILED(hr)) {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to get device count.\\n\");\n            result = ma_result_from_HRESULT(hr);\n            goto done;\n        }\n\n        for (iDevice = 0; iDevice < deviceCount; ++iDevice) {\n            ma_device_info deviceInfo;\n            ma_IMMDevice* pMMDevice;\n\n            MA_ZERO_OBJECT(&deviceInfo);\n\n            hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);\n            if (SUCCEEDED(hr)) {\n                result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo);   /* MA_TRUE = onlySimpleInfo. */\n\n                ma_IMMDevice_Release(pMMDevice);\n                if (result == MA_SUCCESS) {\n                    ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);\n                    if (cbResult == MA_FALSE) {\n                        break;\n                    }\n                }\n            }\n        }\n    }\n\ndone:\n    if (pDefaultDeviceID != NULL) {\n        ma_CoTaskMemFree(pContext, pDefaultDeviceID);\n        pDefaultDeviceID = NULL;\n    }\n\n    if (pDeviceCollection != NULL) {\n        ma_IMMDeviceCollection_Release(pDeviceCollection);\n        pDeviceCollection = NULL;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice)\n{\n    ma_result result;\n    HRESULT hr;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(ppAudioClient != NULL);\n    MA_ASSERT(ppMMDevice != NULL);\n\n    result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient);\n    if (FAILED(hr)) {\n        return ma_result_from_HRESULT(hr);\n    }\n\n    return MA_SUCCESS;\n}\n#else\nstatic ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface)\n{\n    ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;\n    ma_completion_handler_uwp completionHandler;\n    IID iid;\n    WCHAR* iidStr;\n    HRESULT hr;\n    ma_result result;\n    HRESULT activateResult;\n    ma_IUnknown* pActivatedInterface;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(ppAudioClient != NULL);\n\n    if (pDeviceID != NULL) {\n        iidStr = (WCHAR*)pDeviceID->wasapi;\n    } else {\n        if (deviceType == ma_device_type_capture) {\n            iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;\n        } else {\n            iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;\n        }\n\n    #if defined(__cplusplus)\n        hr = StringFromIID(iid, &iidStr);\n    #else\n        hr = StringFromIID(&iid, &iidStr);\n    #endif\n        if (FAILED(hr)) {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\\n\");\n            return ma_result_from_HRESULT(hr);\n        }\n    }\n\n    result = ma_completion_handler_uwp_init(&completionHandler);\n    if (result != MA_SUCCESS) {\n        ma_CoTaskMemFree(pContext, iidStr);\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\\n\");\n        return result;\n    }\n\n    hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);\n    if (FAILED(hr)) {\n        ma_completion_handler_uwp_uninit(&completionHandler);\n        ma_CoTaskMemFree(pContext, iidStr);\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] ActivateAudioInterfaceAsync() failed.\\n\");\n        return ma_result_from_HRESULT(hr);\n    }\n\n    if (pDeviceID == NULL) {\n        ma_CoTaskMemFree(pContext, iidStr);\n    }\n\n    /* Wait for the async operation for finish. */\n    ma_completion_handler_uwp_wait(&completionHandler);\n    ma_completion_handler_uwp_uninit(&completionHandler);\n\n    hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);\n    ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);\n\n    if (FAILED(hr) || FAILED(activateResult)) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to activate device.\\n\");\n        return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult);\n    }\n\n    /* Here is where we grab the IAudioClient interface. */\n    hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);\n    if (FAILED(hr)) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to query IAudioClient interface.\\n\");\n        return ma_result_from_HRESULT(hr);\n    }\n\n    if (ppActivatedInterface) {\n        *ppActivatedInterface = pActivatedInterface;\n    } else {\n        ma_IUnknown_Release(pActivatedInterface);\n    }\n\n    return MA_SUCCESS;\n}\n#endif\n\n\n/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */\ntypedef enum\n{\n    MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT,\n    MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK\n} MA_AUDIOCLIENT_ACTIVATION_TYPE;\n\n/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */\ntypedef enum\n{\n    MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE,\n    MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE\n} MA_PROCESS_LOOPBACK_MODE;\n\n/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */\ntypedef struct\n{\n    DWORD TargetProcessId;\n    MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode;\n} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS;\n\n#if defined(_MSC_VER) && !defined(__clang__)\n    #pragma warning(push)\n    #pragma warning(disable:4201)   /* nonstandard extension used: nameless struct/union */\n#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wpedantic\" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */\n    #if defined(__clang__)\n        #pragma GCC diagnostic ignored \"-Wc11-extensions\"   /* anonymous unions are a C11 extension */\n    #endif\n#endif\n/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */\ntypedef struct\n{\n    MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType;\n    union\n    {\n        MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams;\n    };\n} MA_AUDIOCLIENT_ACTIVATION_PARAMS;\n#if defined(_MSC_VER) && !defined(__clang__)\n    #pragma warning(pop)\n#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))\n    #pragma GCC diagnostic pop\n#endif\n\n#define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L\"VAD\\\\Process_Loopback\"\n\nstatic ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface)\n{\n    ma_result result;\n    ma_bool32 usingProcessLoopback = MA_FALSE;\n    MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams;\n    MA_PROPVARIANT activationParams;\n    MA_PROPVARIANT* pActivationParams = NULL;\n    ma_device_id virtualDeviceID;\n\n    /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */\n    if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) {\n        usingProcessLoopback = MA_TRUE;\n    }\n\n    if (usingProcessLoopback) {\n        MA_ZERO_OBJECT(&audioclientActivationParams);\n        audioclientActivationParams.ActivationType                            = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK;\n        audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE;\n        audioclientActivationParams.ProcessLoopbackParams.TargetProcessId     = (DWORD)loopbackProcessID;\n\n        ma_PropVariantInit(&activationParams);\n        activationParams.vt             = MA_VT_BLOB;\n        activationParams.blob.cbSize    = sizeof(audioclientActivationParams);\n        activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams;\n        pActivationParams = &activationParams;\n\n        /* When requesting a specific device ID we need to use a special device ID. */\n        MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */\n        pDeviceID = &virtualDeviceID;\n    } else {\n        pActivationParams = NULL;   /* No activation parameters required. */\n    }\n\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);\n#else\n    result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);\n#endif\n\n    /*\n    If loopback mode was requested with a process ID and initialization failed, it could be because it's\n    trying to run on an older version of Windows where it's not supported. We need to let the caller\n    know about this with a log message.\n    */\n    if (result != MA_SUCCESS) {\n        if (usingProcessLoopback) {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\\n\", (loopbackProcessExclude) ? \"exclude\" : \"include\", loopbackProcessID);\n        }\n    }\n\n    return result;\n}\n\n\nstatic ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    /* Different enumeration for desktop and UWP. */\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    /* Desktop */\n    HRESULT hr;\n    ma_IMMDeviceEnumerator* pDeviceEnumerator;\n\n    hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);\n    if (FAILED(hr)) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to create device enumerator.\");\n        return ma_result_from_HRESULT(hr);\n    }\n\n    ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);\n    ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture,  callback, pUserData);\n\n    ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);\n#else\n    /*\n    UWP\n\n    The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate\n    over devices without using MMDevice, I'm restricting devices to defaults.\n\n    Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/\n    */\n    if (callback) {\n        ma_bool32 cbResult = MA_TRUE;\n\n        /* Playback. */\n        if (cbResult) {\n            ma_device_info deviceInfo;\n            MA_ZERO_OBJECT(&deviceInfo);\n            ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n            deviceInfo.isDefault = MA_TRUE;\n            cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n        }\n\n        /* Capture. */\n        if (cbResult) {\n            ma_device_info deviceInfo;\n            MA_ZERO_OBJECT(&deviceInfo);\n            ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n            deviceInfo.isDefault = MA_TRUE;\n            cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n        }\n    }\n#endif\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    ma_result result;\n    ma_IMMDevice* pMMDevice = NULL;\n    WCHAR* pDefaultDeviceID = NULL;\n\n    result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* We need the default device ID so we can set the isDefault flag in the device info. */\n    pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);\n\n    result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo);   /* MA_FALSE = !onlySimpleInfo. */\n\n    if (pDefaultDeviceID != NULL) {\n        ma_CoTaskMemFree(pContext, pDefaultDeviceID);\n        pDefaultDeviceID = NULL;\n    }\n\n    ma_IMMDevice_Release(pMMDevice);\n\n    return result;\n#else\n    ma_IAudioClient* pAudioClient;\n    ma_result result;\n\n    /* UWP currently only uses default devices. */\n    if (deviceType == ma_device_type_playback) {\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n    } else {\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n    }\n\n    result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo);\n\n    pDeviceInfo->isDefault = MA_TRUE;  /* UWP only supports default devices. */\n\n    ma_IAudioClient_Release(pAudioClient);\n    return result;\n#endif\n}\n\nstatic ma_result ma_device_uninit__wasapi(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    if (pDevice->wasapi.pDeviceEnumerator) {\n        ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);\n        ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);\n    }\n#endif\n\n    if (pDevice->wasapi.pRenderClient) {\n        if (pDevice->wasapi.pMappedBufferPlayback != NULL) {\n            ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);\n            pDevice->wasapi.pMappedBufferPlayback   = NULL;\n            pDevice->wasapi.mappedBufferPlaybackCap = 0;\n            pDevice->wasapi.mappedBufferPlaybackLen = 0;\n        }\n\n        ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);\n    }\n    if (pDevice->wasapi.pCaptureClient) {\n        if (pDevice->wasapi.pMappedBufferCapture != NULL) {\n            ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);\n            pDevice->wasapi.pMappedBufferCapture   = NULL;\n            pDevice->wasapi.mappedBufferCaptureCap = 0;\n            pDevice->wasapi.mappedBufferCaptureLen = 0;\n        }\n\n        ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);\n    }\n\n    if (pDevice->wasapi.pAudioClientPlayback) {\n        ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);\n    }\n    if (pDevice->wasapi.pAudioClientCapture) {\n        ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);\n    }\n\n    if (pDevice->wasapi.hEventPlayback) {\n        CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback);\n    }\n    if (pDevice->wasapi.hEventCapture) {\n        CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);\n    }\n\n    return MA_SUCCESS;\n}\n\n\ntypedef struct\n{\n    /* Input. */\n    ma_format formatIn;\n    ma_uint32 channelsIn;\n    ma_uint32 sampleRateIn;\n    ma_channel channelMapIn[MA_MAX_CHANNELS];\n    ma_uint32 periodSizeInFramesIn;\n    ma_uint32 periodSizeInMillisecondsIn;\n    ma_uint32 periodsIn;\n    ma_share_mode shareMode;\n    ma_performance_profile performanceProfile;\n    ma_bool32 noAutoConvertSRC;\n    ma_bool32 noDefaultQualitySRC;\n    ma_bool32 noHardwareOffloading;\n    ma_uint32 loopbackProcessID;\n    ma_bool32 loopbackProcessExclude;\n\n    /* Output. */\n    ma_IAudioClient* pAudioClient;\n    ma_IAudioRenderClient* pRenderClient;\n    ma_IAudioCaptureClient* pCaptureClient;\n    ma_format formatOut;\n    ma_uint32 channelsOut;\n    ma_uint32 sampleRateOut;\n    ma_channel channelMapOut[MA_MAX_CHANNELS];\n    ma_uint32 periodSizeInFramesOut;\n    ma_uint32 periodsOut;\n    ma_bool32 usingAudioClient3;\n    char deviceName[256];\n    ma_device_id id;\n} ma_device_init_internal_data__wasapi;\n\nstatic ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData)\n{\n    HRESULT hr;\n    ma_result result = MA_SUCCESS;\n    const char* errorMsg = \"\";\n    MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;\n    DWORD streamFlags = 0;\n    MA_REFERENCE_TIME periodDurationInMicroseconds;\n    ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;\n    MA_WAVEFORMATEXTENSIBLE wf;\n    ma_WASAPIDeviceInterface* pDeviceInterface = NULL;\n    ma_IAudioClient2* pAudioClient2;\n    ma_uint32 nativeSampleRate;\n    ma_bool32 usingProcessLoopback = MA_FALSE;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pData != NULL);\n\n    /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */\n    if (deviceType == ma_device_type_duplex) {\n        return MA_INVALID_ARGS;\n    }\n\n    usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL;\n\n    pData->pAudioClient = NULL;\n    pData->pRenderClient = NULL;\n    pData->pCaptureClient = NULL;\n\n    streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;\n    if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) {    /* <-- Exclusive streams must use the native sample rate. */\n        streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;\n    }\n    if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {\n        streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;\n    }\n    if (deviceType == ma_device_type_loopback) {\n        streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;\n    }\n\n    result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface);\n    if (result != MA_SUCCESS) {\n        goto done;\n    }\n\n    MA_ZERO_OBJECT(&wf);\n\n    /* Try enabling hardware offloading. */\n    if (!pData->noHardwareOffloading) {\n        hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);\n        if (SUCCEEDED(hr)) {\n            BOOL isHardwareOffloadingSupported = 0;\n            hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);\n            if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {\n                ma_AudioClientProperties clientProperties;\n                MA_ZERO_OBJECT(&clientProperties);\n                clientProperties.cbSize = sizeof(clientProperties);\n                clientProperties.bIsOffload = 1;\n                clientProperties.eCategory = MA_AudioCategory_Other;\n                ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);\n            }\n\n            pAudioClient2->lpVtbl->Release(pAudioClient2);\n        }\n    }\n\n    /* Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. */\n    result = MA_FORMAT_NOT_SUPPORTED;\n    if (pData->shareMode == ma_share_mode_exclusive) {\n    #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n        /* In exclusive mode on desktop we always use the backend's native format. */\n        ma_IPropertyStore* pStore = NULL;\n        hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);\n        if (SUCCEEDED(hr)) {\n            MA_PROPVARIANT prop;\n            ma_PropVariantInit(&prop);\n            hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);\n            if (SUCCEEDED(hr)) {\n                MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData;\n                hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);\n                if (SUCCEEDED(hr)) {\n                    MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE));\n                }\n\n                ma_PropVariantClear(pContext, &prop);\n            }\n\n            ma_IPropertyStore_Release(pStore);\n        }\n    #else\n        /*\n        I do not know how to query the device's native format on UWP so for now I'm just disabling support for\n        exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported()\n        until you find one that works.\n\n        TODO: Add support for exclusive mode to UWP.\n        */\n        hr = S_FALSE;\n    #endif\n\n        if (hr == S_OK) {\n            shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;\n            result = MA_SUCCESS;\n        } else {\n            result = MA_SHARE_MODE_NOT_SUPPORTED;\n        }\n    } else {\n        /* In shared mode we are always using the format reported by the operating system. */\n        MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;\n        hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat);\n        if (hr != S_OK) {\n            /* When using process-specific loopback, GetMixFormat() seems to always fail. */\n            if (usingProcessLoopback) {\n                wf.wFormatTag      = WAVE_FORMAT_IEEE_FLOAT;\n                wf.nChannels       = 2;\n                wf.nSamplesPerSec  = 44100;\n                wf.wBitsPerSample  = 32;\n                wf.nBlockAlign     = wf.nChannels * wf.wBitsPerSample / 8;\n                wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;\n                wf.cbSize          = sizeof(MA_WAVEFORMATEX);\n\n                result = MA_SUCCESS;\n            } else {\n                result = MA_FORMAT_NOT_SUPPORTED;\n            }\n        } else {\n            /*\n            I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself\n            is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE\n            want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be\n            safe and only copy the WAVEFORMATEX part.\n            */\n            if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {\n                MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE));\n            } else {\n                /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */\n                size_t cbSize = pNativeFormat->cbSize;\n                if (cbSize == 0) {\n                    cbSize = sizeof(MA_WAVEFORMATEX);\n                }\n\n                /* Make sure we don't copy more than the capacity of `wf`. */\n                if (cbSize > sizeof(wf)) {\n                    cbSize = sizeof(wf);\n                }\n\n                MA_COPY_MEMORY(&wf, pNativeFormat, cbSize);\n            }\n\n            result = MA_SUCCESS;\n        }\n\n        ma_CoTaskMemFree(pContext, pNativeFormat);\n\n        shareMode = MA_AUDCLNT_SHAREMODE_SHARED;\n    }\n\n    /* Return an error if we still haven't found a format. */\n    if (result != MA_SUCCESS) {\n        errorMsg = \"[WASAPI] Failed to find best device mix format.\";\n        goto done;\n    }\n\n    /*\n    Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use\n    WASAPI to perform the sample rate conversion.\n    */\n    nativeSampleRate = wf.nSamplesPerSec;\n    if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {\n        wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE;\n        wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;\n    }\n\n    pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf);\n    if (pData->formatOut == ma_format_unknown) {\n        /*\n        The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED\n        in this case so that the caller can detect it and fall back to shared mode if desired. We should never get here if shared mode was requested, but just for\n        completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED.\n        */\n        if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {\n            result = MA_SHARE_MODE_NOT_SUPPORTED;\n        } else {\n            result = MA_FORMAT_NOT_SUPPORTED;\n        }\n\n        errorMsg = \"[WASAPI] Native format not supported.\";\n        goto done;\n    }\n\n    pData->channelsOut = wf.nChannels;\n    pData->sampleRateOut = wf.nSamplesPerSec;\n\n    /*\n    Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns\n    a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this\n    case we'll just use the default channel map.\n    */\n    if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) {\n        ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);\n    } else {\n        ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);\n    }\n\n    /* Period size. */\n    pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS;\n    pData->periodSizeInFramesOut = pData->periodSizeInFramesIn;\n    if (pData->periodSizeInFramesOut == 0) {\n        if (pData->periodSizeInMillisecondsIn == 0) {\n            if (pData->performanceProfile == ma_performance_profile_low_latency) {\n                pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec);\n            } else {\n                pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec);\n            }\n        } else {\n            pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec);\n        }\n    }\n\n    periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec;\n\n\n    /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */\n    if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {\n        MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10;\n\n        /*\n        If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing\n        it and trying it again.\n        */\n        hr = E_FAIL;\n        for (;;) {\n            hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL);\n            if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {\n                if (bufferDuration > 500*10000) {\n                    break;\n                } else {\n                    if (bufferDuration == 0) {  /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */\n                        break;\n                    }\n\n                    bufferDuration = bufferDuration * 2;\n                    continue;\n                }\n            } else {\n                break;\n            }\n        }\n\n        if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {\n            ma_uint32 bufferSizeInFrames;\n            hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);\n            if (SUCCEEDED(hr)) {\n                bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5);\n\n                /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */\n                ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);\n\n            #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n                hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);\n            #else\n                hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);\n            #endif\n\n                if (SUCCEEDED(hr)) {\n                    hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL);\n                }\n            }\n        }\n\n        if (FAILED(hr)) {\n            /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */\n            if (hr == E_ACCESSDENIED) {\n                errorMsg = \"[WASAPI] Failed to initialize device in exclusive mode. Access denied.\", result = MA_ACCESS_DENIED;\n            } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {\n                errorMsg = \"[WASAPI] Failed to initialize device in exclusive mode. Device in use.\", result = MA_BUSY;\n            } else {\n                errorMsg = \"[WASAPI] Failed to initialize device in exclusive mode.\"; result = ma_result_from_HRESULT(hr);\n            }\n            goto done;\n        }\n    }\n\n    if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {\n        /*\n        Low latency shared mode via IAudioClient3.\n\n        NOTE\n        ====\n        Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the\n        use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using\n        any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to\n        that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.\n        */\n        #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE\n        {\n            if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) {\n                ma_IAudioClient3* pAudioClient3 = NULL;\n                hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);\n                if (SUCCEEDED(hr)) {\n                    ma_uint32 defaultPeriodInFrames;\n                    ma_uint32 fundamentalPeriodInFrames;\n                    ma_uint32 minPeriodInFrames;\n                    ma_uint32 maxPeriodInFrames;\n                    hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);\n                    if (SUCCEEDED(hr)) {\n                        ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut;\n                        ma_uint32 actualPeriodInFrames  = desiredPeriodInFrames;\n\n                        /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */\n                        actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;\n                        actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;\n\n                        /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */\n                        actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);\n\n                        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\\n\", actualPeriodInFrames);\n                        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"    defaultPeriodInFrames=%d\\n\", defaultPeriodInFrames);\n                        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"    fundamentalPeriodInFrames=%d\\n\", fundamentalPeriodInFrames);\n                        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"    minPeriodInFrames=%d\\n\", minPeriodInFrames);\n                        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"    maxPeriodInFrames=%d\\n\", maxPeriodInFrames);\n\n                        /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */\n                        if (actualPeriodInFrames >= desiredPeriodInFrames) {\n                            /*\n                            MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified,\n                            IAudioClient3_InitializeSharedAudioStream() will fail.\n                            */\n                            hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL);\n                            if (SUCCEEDED(hr)) {\n                                wasInitializedUsingIAudioClient3 = MA_TRUE;\n                                pData->periodSizeInFramesOut = actualPeriodInFrames;\n\n                                ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Using IAudioClient3\\n\");\n                                ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"    periodSizeInFramesOut=%d\\n\", pData->periodSizeInFramesOut);\n                            } else {\n                                ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\\n\");\n                            }\n                        } else {\n                            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\\n\");\n                        }\n                    } else {\n                        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\\n\");\n                    }\n\n                    ma_IAudioClient3_Release(pAudioClient3);\n                    pAudioClient3 = NULL;\n                }\n            }\n        }\n        #else\n        {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\\n\");\n        }\n        #endif\n\n        /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */\n        if (!wasInitializedUsingIAudioClient3) {\n            MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10;   /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */\n            hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL);\n            if (FAILED(hr)) {\n                if (hr == E_ACCESSDENIED) {\n                    errorMsg = \"[WASAPI] Failed to initialize device. Access denied.\", result = MA_ACCESS_DENIED;\n                } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {\n                    errorMsg = \"[WASAPI] Failed to initialize device. Device in use.\", result = MA_BUSY;\n                } else {\n                    errorMsg = \"[WASAPI] Failed to initialize device.\", result = ma_result_from_HRESULT(hr);\n                }\n\n                goto done;\n            }\n        }\n    }\n\n    if (!wasInitializedUsingIAudioClient3) {\n        ma_uint32 bufferSizeInFrames = 0;\n        hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);\n        if (FAILED(hr)) {\n            errorMsg = \"[WASAPI] Failed to get audio client's actual buffer size.\", result = ma_result_from_HRESULT(hr);\n            goto done;\n        }\n\n        /*\n        When using process loopback mode, retrieval of the buffer size seems to result in totally\n        incorrect values. In this case we'll just assume it's the same size as what we requested\n        when we initialized the client.\n        */\n        if (usingProcessLoopback) {\n            bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000);\n        }\n\n        pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut;\n    }\n\n    pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;\n\n\n    if (deviceType == ma_device_type_playback) {\n        result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient);\n    } else {\n        result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient);\n    }\n\n    /*if (FAILED(hr)) {*/\n    if (result != MA_SUCCESS) {\n        errorMsg = \"[WASAPI] Failed to get audio client service.\";\n        goto done;\n    }\n\n\n    /* Grab the name of the device. */\n    #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    {\n        ma_IPropertyStore *pProperties;\n        hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);\n        if (SUCCEEDED(hr)) {\n            MA_PROPVARIANT varName;\n            ma_PropVariantInit(&varName);\n            hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);\n            if (SUCCEEDED(hr)) {\n                WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);\n                ma_PropVariantClear(pContext, &varName);\n            }\n\n            ma_IPropertyStore_Release(pProperties);\n        }\n    }\n    #endif\n\n    /*\n    For the WASAPI backend we need to know the actual IDs of the device in order to do automatic\n    stream routing so that IDs can be compared and we can determine which device has been detached\n    and whether or not it matches with our ma_device.\n    */\n    #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    {\n        /* Desktop */\n        ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id);\n    }\n    #else\n    {\n        /* UWP */\n        /* TODO: Implement me. Need to figure out how to get the ID of the default device. */\n    }\n    #endif\n\ndone:\n    /* Clean up. */\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    if (pDeviceInterface != NULL) {\n        ma_IMMDevice_Release(pDeviceInterface);\n    }\n#else\n    if (pDeviceInterface != NULL) {\n        ma_IUnknown_Release(pDeviceInterface);\n    }\n#endif\n\n    if (result != MA_SUCCESS) {\n        if (pData->pRenderClient) {\n            ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);\n            pData->pRenderClient = NULL;\n        }\n        if (pData->pCaptureClient) {\n            ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);\n            pData->pCaptureClient = NULL;\n        }\n        if (pData->pAudioClient) {\n            ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);\n            pData->pAudioClient = NULL;\n        }\n\n        if (errorMsg != NULL && errorMsg[0] != '\\0') {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"%s\\n\", errorMsg);\n        }\n\n        return result;\n    } else {\n        return MA_SUCCESS;\n    }\n}\n\nstatic ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)\n{\n    ma_device_init_internal_data__wasapi data;\n    ma_result result;\n\n    MA_ASSERT(pDevice != NULL);\n\n    /* We only re-initialize the playback or capture device. Never a full-duplex device. */\n    if (deviceType == ma_device_type_duplex) {\n        return MA_INVALID_ARGS;\n    }\n\n\n    /*\n    Before reinitializing the device we need to free the previous audio clients.\n\n    There's a known memory leak here. We will be calling this from the routing change callback that\n    is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion\n    this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably\n    need some system where we post an event, but delay the execution of it until the callback has\n    returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for\n    a command thread which might be useful for this.\n    */\n    if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {\n        if (pDevice->wasapi.pCaptureClient) {\n            ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);\n            pDevice->wasapi.pCaptureClient = NULL;\n        }\n\n        if (pDevice->wasapi.pAudioClientCapture) {\n            /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/\n            pDevice->wasapi.pAudioClientCapture = NULL;\n        }\n    }\n\n    if (deviceType == ma_device_type_playback) {\n        if (pDevice->wasapi.pRenderClient) {\n            ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);\n            pDevice->wasapi.pRenderClient = NULL;\n        }\n\n        if (pDevice->wasapi.pAudioClientPlayback) {\n            /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/\n            pDevice->wasapi.pAudioClientPlayback = NULL;\n        }\n    }\n\n\n    if (deviceType == ma_device_type_playback) {\n        data.formatIn               = pDevice->playback.format;\n        data.channelsIn             = pDevice->playback.channels;\n        MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));\n        data.shareMode              = pDevice->playback.shareMode;\n    } else {\n        data.formatIn               = pDevice->capture.format;\n        data.channelsIn             = pDevice->capture.channels;\n        MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));\n        data.shareMode              = pDevice->capture.shareMode;\n    }\n\n    data.sampleRateIn               = pDevice->sampleRate;\n    data.periodSizeInFramesIn       = pDevice->wasapi.originalPeriodSizeInFrames;\n    data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds;\n    data.periodsIn                  = pDevice->wasapi.originalPeriods;\n    data.performanceProfile         = pDevice->wasapi.originalPerformanceProfile;\n    data.noAutoConvertSRC           = pDevice->wasapi.noAutoConvertSRC;\n    data.noDefaultQualitySRC        = pDevice->wasapi.noDefaultQualitySRC;\n    data.noHardwareOffloading       = pDevice->wasapi.noHardwareOffloading;\n    data.loopbackProcessID          = pDevice->wasapi.loopbackProcessID;\n    data.loopbackProcessExclude     = pDevice->wasapi.loopbackProcessExclude;\n    result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */\n    if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {\n        pDevice->wasapi.pAudioClientCapture         = data.pAudioClient;\n        pDevice->wasapi.pCaptureClient              = data.pCaptureClient;\n\n        pDevice->capture.internalFormat             = data.formatOut;\n        pDevice->capture.internalChannels           = data.channelsOut;\n        pDevice->capture.internalSampleRate         = data.sampleRateOut;\n        MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));\n        pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;\n        pDevice->capture.internalPeriods            = data.periodsOut;\n        ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);\n\n        ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture);\n\n        pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;\n        ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);\n\n        /* We must always have a valid ID. */\n        ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);\n    }\n\n    if (deviceType == ma_device_type_playback) {\n        pDevice->wasapi.pAudioClientPlayback         = data.pAudioClient;\n        pDevice->wasapi.pRenderClient                = data.pRenderClient;\n\n        pDevice->playback.internalFormat             = data.formatOut;\n        pDevice->playback.internalChannels           = data.channelsOut;\n        pDevice->playback.internalSampleRate         = data.sampleRateOut;\n        MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));\n        pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;\n        pDevice->playback.internalPeriods            = data.periodsOut;\n        ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);\n\n        ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback);\n\n        pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;\n        ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);\n\n        /* We must always have a valid ID because rerouting will look at it. */\n        ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    ma_result result = MA_SUCCESS;\n\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    HRESULT hr;\n    ma_IMMDeviceEnumerator* pDeviceEnumerator;\n#endif\n\n    MA_ASSERT(pDevice != NULL);\n\n    MA_ZERO_OBJECT(&pDevice->wasapi);\n    pDevice->wasapi.usage                  = pConfig->wasapi.usage;\n    pDevice->wasapi.noAutoConvertSRC       = pConfig->wasapi.noAutoConvertSRC;\n    pDevice->wasapi.noDefaultQualitySRC    = pConfig->wasapi.noDefaultQualitySRC;\n    pDevice->wasapi.noHardwareOffloading   = pConfig->wasapi.noHardwareOffloading;\n    pDevice->wasapi.loopbackProcessID      = pConfig->wasapi.loopbackProcessID;\n    pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;\n\n    /* Exclusive mode is not allowed with loopback. */\n    if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) {\n        return MA_INVALID_DEVICE_CONFIG;\n    }\n\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {\n        ma_device_init_internal_data__wasapi data;\n        data.formatIn                   = pDescriptorCapture->format;\n        data.channelsIn                 = pDescriptorCapture->channels;\n        data.sampleRateIn               = pDescriptorCapture->sampleRate;\n        MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));\n        data.periodSizeInFramesIn       = pDescriptorCapture->periodSizeInFrames;\n        data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;\n        data.periodsIn                  = pDescriptorCapture->periodCount;\n        data.shareMode                  = pDescriptorCapture->shareMode;\n        data.performanceProfile         = pConfig->performanceProfile;\n        data.noAutoConvertSRC           = pConfig->wasapi.noAutoConvertSRC;\n        data.noDefaultQualitySRC        = pConfig->wasapi.noDefaultQualitySRC;\n        data.noHardwareOffloading       = pConfig->wasapi.noHardwareOffloading;\n        data.loopbackProcessID          = pConfig->wasapi.loopbackProcessID;\n        data.loopbackProcessExclude     = pConfig->wasapi.loopbackProcessExclude;\n\n        result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pDevice->wasapi.pAudioClientCapture              = data.pAudioClient;\n        pDevice->wasapi.pCaptureClient                   = data.pCaptureClient;\n        pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;\n        pDevice->wasapi.originalPeriodSizeInFrames       = pDescriptorCapture->periodSizeInFrames;\n        pDevice->wasapi.originalPeriods                  = pDescriptorCapture->periodCount;\n        pDevice->wasapi.originalPerformanceProfile       = pConfig->performanceProfile;\n\n        /*\n        The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,\n        however, because we want to block until we actually have something for the first call to ma_device_read().\n        */\n        pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL);  /* Auto reset, unsignaled by default. */\n        if (pDevice->wasapi.hEventCapture == NULL) {\n            result = ma_result_from_GetLastError(GetLastError());\n\n            if (pDevice->wasapi.pCaptureClient != NULL) {\n                ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);\n                pDevice->wasapi.pCaptureClient = NULL;\n            }\n            if (pDevice->wasapi.pAudioClientCapture != NULL) {\n                ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);\n                pDevice->wasapi.pAudioClientCapture = NULL;\n            }\n\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to create event for capture.\");\n            return result;\n        }\n        ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture);\n\n        pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;\n        ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);\n\n        /* We must always have a valid ID. */\n        ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);\n\n        /* The descriptor needs to be updated with actual values. */\n        pDescriptorCapture->format             = data.formatOut;\n        pDescriptorCapture->channels           = data.channelsOut;\n        pDescriptorCapture->sampleRate         = data.sampleRateOut;\n        MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));\n        pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;\n        pDescriptorCapture->periodCount        = data.periodsOut;\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        ma_device_init_internal_data__wasapi data;\n        data.formatIn                   = pDescriptorPlayback->format;\n        data.channelsIn                 = pDescriptorPlayback->channels;\n        data.sampleRateIn               = pDescriptorPlayback->sampleRate;\n        MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));\n        data.periodSizeInFramesIn       = pDescriptorPlayback->periodSizeInFrames;\n        data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;\n        data.periodsIn                  = pDescriptorPlayback->periodCount;\n        data.shareMode                  = pDescriptorPlayback->shareMode;\n        data.performanceProfile         = pConfig->performanceProfile;\n        data.noAutoConvertSRC           = pConfig->wasapi.noAutoConvertSRC;\n        data.noDefaultQualitySRC        = pConfig->wasapi.noDefaultQualitySRC;\n        data.noHardwareOffloading       = pConfig->wasapi.noHardwareOffloading;\n        data.loopbackProcessID          = pConfig->wasapi.loopbackProcessID;\n        data.loopbackProcessExclude     = pConfig->wasapi.loopbackProcessExclude;\n\n        result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data);\n        if (result != MA_SUCCESS) {\n            if (pConfig->deviceType == ma_device_type_duplex) {\n                if (pDevice->wasapi.pCaptureClient != NULL) {\n                    ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);\n                    pDevice->wasapi.pCaptureClient = NULL;\n                }\n                if (pDevice->wasapi.pAudioClientCapture != NULL) {\n                    ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);\n                    pDevice->wasapi.pAudioClientCapture = NULL;\n                }\n\n                CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);\n                pDevice->wasapi.hEventCapture = NULL;\n            }\n            return result;\n        }\n\n        pDevice->wasapi.pAudioClientPlayback             = data.pAudioClient;\n        pDevice->wasapi.pRenderClient                    = data.pRenderClient;\n        pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;\n        pDevice->wasapi.originalPeriodSizeInFrames       = pDescriptorPlayback->periodSizeInFrames;\n        pDevice->wasapi.originalPeriods                  = pDescriptorPlayback->periodCount;\n        pDevice->wasapi.originalPerformanceProfile       = pConfig->performanceProfile;\n\n        /*\n        The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled\n        only after the whole available space has been filled, never before.\n\n        The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able\n        to get passed WaitForMultipleObjects().\n        */\n        pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL);  /* Auto reset, signaled by default. */\n        if (pDevice->wasapi.hEventPlayback == NULL) {\n            result = ma_result_from_GetLastError(GetLastError());\n\n            if (pConfig->deviceType == ma_device_type_duplex) {\n                if (pDevice->wasapi.pCaptureClient != NULL) {\n                    ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);\n                    pDevice->wasapi.pCaptureClient = NULL;\n                }\n                if (pDevice->wasapi.pAudioClientCapture != NULL) {\n                    ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);\n                    pDevice->wasapi.pAudioClientCapture = NULL;\n                }\n\n                CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);\n                pDevice->wasapi.hEventCapture = NULL;\n            }\n\n            if (pDevice->wasapi.pRenderClient != NULL) {\n                ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);\n                pDevice->wasapi.pRenderClient = NULL;\n            }\n            if (pDevice->wasapi.pAudioClientPlayback != NULL) {\n                ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);\n                pDevice->wasapi.pAudioClientPlayback = NULL;\n            }\n\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to create event for playback.\");\n            return result;\n        }\n        ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback);\n\n        pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;\n        ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);\n\n        /* We must always have a valid ID because rerouting will look at it. */\n        ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);\n\n        /* The descriptor needs to be updated with actual values. */\n        pDescriptorPlayback->format             = data.formatOut;\n        pDescriptorPlayback->channels           = data.channelsOut;\n        pDescriptorPlayback->sampleRate         = data.sampleRateOut;\n        MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));\n        pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;\n        pDescriptorPlayback->periodCount        = data.periodsOut;\n    }\n\n    /*\n    We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When\n    we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just\n    stop the device outright and let the application handle it.\n    */\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {\n        if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) {\n            pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE;\n        }\n        if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {\n            pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;\n        }\n    }\n\n    ma_mutex_init(&pDevice->wasapi.rerouteLock);\n\n    hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);\n    if (FAILED(hr)) {\n        ma_device_uninit__wasapi(pDevice);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to create device enumerator.\");\n        return ma_result_from_HRESULT(hr);\n    }\n\n    pDevice->wasapi.notificationClient.lpVtbl  = (void*)&g_maNotificationCientVtbl;\n    pDevice->wasapi.notificationClient.counter = 1;\n    pDevice->wasapi.notificationClient.pDevice = pDevice;\n\n    hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);\n    if (SUCCEEDED(hr)) {\n        pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;\n    } else {\n        /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */\n        ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);\n    }\n#endif\n\n    ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture,  MA_FALSE);\n    ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)\n{\n    ma_uint32 paddingFramesCount;\n    HRESULT hr;\n    ma_share_mode shareMode;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(pFrameCount != NULL);\n\n    *pFrameCount = 0;\n\n    if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /*\n    I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing\n    higher level function calls from doing anything because it thinks nothing is available. I have\n    taken a look at the documentation and it looks like this is unnecessary in exclusive mode.\n\n    From Microsoft's documentation:\n\n        For an exclusive-mode rendering or capture stream that was initialized with the\n        AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding\n        value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during\n        each processing pass.\n\n    Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the\n    entire buffer. This depends on the caller making sure they wait on the event handler.\n    */\n    shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;\n    if (shareMode == ma_share_mode_shared) {\n        /* Shared mode. */\n        hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);\n        if (FAILED(hr)) {\n            return ma_result_from_HRESULT(hr);\n        }\n\n        if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {\n            *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount;\n        } else {\n            *pFrameCount = paddingFramesCount;\n        }\n    } else {\n        /* Exclusive mode. */\n        if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {\n            *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback;\n        } else {\n            *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)\n{\n    ma_result result;\n\n    if (deviceType == ma_device_type_duplex) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"=== CHANGING DEVICE ===\\n\");\n\n    result = ma_device_reinit__wasapi(pDevice, deviceType);\n    if (result != MA_SUCCESS) {\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, \"[WASAPI] Reinitializing device after route change failed.\\n\");\n        return result;\n    }\n\n    ma_device__post_init_setup(pDevice, deviceType);\n    ma_device__on_notification_rerouted(pDevice);\n\n    ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"=== DEVICE CHANGED ===\\n\");\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_start__wasapi_nolock(ma_device* pDevice)\n{\n    HRESULT hr;\n\n    if (pDevice->pContext->wasapi.hAvrt) {\n        const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage);\n        if (pTaskName) {\n            DWORD idx = 0;\n            pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx);\n        }\n    }\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {\n        hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);\n        if (FAILED(hr)) {\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to start internal capture device. HRESULT = %d.\", (int)hr);\n            return ma_result_from_HRESULT(hr);\n        }\n\n        ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);\n        if (FAILED(hr)) {\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to start internal playback device. HRESULT = %d.\", (int)hr);\n            return ma_result_from_HRESULT(hr);\n        }\n\n        ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_start__wasapi(ma_device* pDevice)\n{\n    ma_result result;\n\n    MA_ASSERT(pDevice != NULL);\n\n    /* Wait for any rerouting to finish before attempting to start the device. */\n    ma_mutex_lock(&pDevice->wasapi.rerouteLock);\n    {\n        result = ma_device_start__wasapi_nolock(pDevice);\n    }\n    ma_mutex_unlock(&pDevice->wasapi.rerouteLock);\n\n    return result;\n}\n\nstatic ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)\n{\n    ma_result result;\n    HRESULT hr;\n\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->wasapi.hAvrtHandle) {\n        ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle);\n        pDevice->wasapi.hAvrtHandle = NULL;\n    }\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {\n        hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);\n        if (FAILED(hr)) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to stop internal capture device.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n        /* The audio client needs to be reset otherwise restarting will fail. */\n        hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);\n        if (FAILED(hr)) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to reset internal capture device.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n        /* If we have a mapped buffer we need to release it. */\n        if (pDevice->wasapi.pMappedBufferCapture != NULL) {\n            ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);\n            pDevice->wasapi.pMappedBufferCapture = NULL;\n            pDevice->wasapi.mappedBufferCaptureCap = 0;\n            pDevice->wasapi.mappedBufferCaptureLen = 0;\n        }\n\n        ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        /*\n        The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to\n        the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.\n        */\n        if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) {\n            /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */\n            DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate;\n\n            if (pDevice->playback.shareMode == ma_share_mode_exclusive) {\n                WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime);\n            }\n            else {\n                ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;\n                ma_uint32 framesAvailablePlayback;\n                for (;;) {\n                    result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);\n                    if (result != MA_SUCCESS) {\n                        break;\n                    }\n\n                    if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) {\n                        break;\n                    }\n\n                    /*\n                    Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames\n                    has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case.\n                    */\n                    if (framesAvailablePlayback == prevFramesAvaialablePlayback) {\n                        break;\n                    }\n                    prevFramesAvaialablePlayback = framesAvailablePlayback;\n\n                    WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000);\n                    ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */\n                }\n            }\n        }\n\n        hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);\n        if (FAILED(hr)) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to stop internal playback device.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n        /* The audio client needs to be reset otherwise restarting will fail. */\n        hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);\n        if (FAILED(hr)) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to reset internal playback device.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n        if (pDevice->wasapi.pMappedBufferPlayback != NULL) {\n            ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);\n            pDevice->wasapi.pMappedBufferPlayback = NULL;\n            pDevice->wasapi.mappedBufferPlaybackCap = 0;\n            pDevice->wasapi.mappedBufferPlaybackLen = 0;\n        }\n\n        ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__wasapi(ma_device* pDevice)\n{\n    ma_result result;\n\n    MA_ASSERT(pDevice != NULL);\n\n    /* Wait for any rerouting to finish before attempting to stop the device. */\n    ma_mutex_lock(&pDevice->wasapi.rerouteLock);\n    {\n        result = ma_device_stop__wasapi_nolock(pDevice);\n    }\n    ma_mutex_unlock(&pDevice->wasapi.rerouteLock);\n\n    return result;\n}\n\n\n#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS\n#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000\n#endif\n\nstatic ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint32 totalFramesProcessed = 0;\n\n    /*\n    When reading, we need to get a buffer and process all of it before releasing it. Because the\n    frame count (frameCount) can be different to the size of the buffer, we'll need to cache the\n    pointer to the buffer.\n    */\n\n    /* Keep running until we've processed the requested number of frames. */\n    while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {\n        ma_uint32 framesRemaining = frameCount - totalFramesProcessed;\n\n        /* If we have a mapped data buffer, consume that first. */\n        if (pDevice->wasapi.pMappedBufferCapture != NULL) {\n            /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */\n            ma_uint32 framesToProcessNow = framesRemaining;\n            if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) {\n                framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen;\n            }\n\n            /* Now just copy the data over to the output buffer. */\n            ma_copy_pcm_frames(\n                ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels),\n                ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels),\n                framesToProcessNow,\n                pDevice->capture.internalFormat, pDevice->capture.internalChannels\n            );\n\n            totalFramesProcessed                   += framesToProcessNow;\n            pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow;\n\n            /* If the data buffer has been fully consumed we need to release it. */\n            if (pDevice->wasapi.mappedBufferCaptureLen == 0) {\n                ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);\n                pDevice->wasapi.pMappedBufferCapture   = NULL;\n                pDevice->wasapi.mappedBufferCaptureCap = 0;\n            }\n        } else {\n            /* We don't have any cached data pointer, so grab another one. */\n            HRESULT hr;\n            DWORD flags = 0;\n\n            /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */\n            hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);\n            if (hr == S_OK) {\n                /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */\n                pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;\n\n                /*\n                There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every\n                call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially\n                work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution\n                would be to figure out why the flag is always getting reported.\n                */\n                #if defined(MA_DEBUG_OUTPUT)\n                {\n                    if (flags != 0) {\n                        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Capture Flags: %ld\\n\", flags);\n\n                        if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {\n                            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\\n\", pDevice->wasapi.mappedBufferCaptureCap);\n                        }\n                    }\n                }\n                #endif\n\n                /* Overrun detection. */\n                if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {\n                    /* Glitched. Probably due to an overrun. */\n\n                    /*\n                    If we got an overrun it probably means we're straddling the end of the buffer. In normal capture\n                    mode this is the fault of the client application because they're responsible for ensuring data is\n                    processed fast enough. In duplex mode, however, the processing of audio is tied to the playback\n                    device, so this can possibly be the result of a timing de-sync.\n\n                    In capture mode we're not going to do any kind of recovery because the real fix is for the client\n                    application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers\n                    to prevent a never-ending sequence of glitches due to straddling the end of the buffer.\n                    */\n                    if (pDevice->type == ma_device_type_duplex) {\n                        /*\n                        Experiment:\n\n                        If we empty out the *entire* buffer we may end up putting ourselves into an underrun position\n                        which isn't really any better than the overrun we're probably in right now. Instead we'll just\n                        empty out about half.\n                        */\n                        ma_uint32 i;\n                        ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture);\n                        ma_uint32 iterationCount = periodCount / 2;\n                        if ((periodCount % 2) > 0) {\n                            iterationCount += 1;\n                        }\n\n                        for (i = 0; i < iterationCount; i += 1) {\n                            hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);\n                            if (FAILED(hr)) {\n                                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\\n\", hr);\n                                break;\n                            }\n\n                            flags = 0;\n                            hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);\n                            if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) {\n                                /*\n                                The buffer has been completely emptied or an error occurred. In this case we'll need\n                                to reset the state of the mapped buffer which will trigger the next iteration to get\n                                a fresh buffer from WASAPI.\n                                */\n                                pDevice->wasapi.pMappedBufferCapture   = NULL;\n                                pDevice->wasapi.mappedBufferCaptureCap = 0;\n                                pDevice->wasapi.mappedBufferCaptureLen = 0;\n\n                                if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) {\n                                    if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {\n                                        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\\n\");\n                                    } else {\n                                        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Data discontinuity recovery: Buffer emptied.\\n\");\n                                    }\n                                }\n\n                                if (FAILED(hr)) {\n                                    ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\\n\", hr);\n                                }\n\n                                break;\n                            }\n                        }\n\n                        /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */\n                        if (pDevice->wasapi.pMappedBufferCapture != NULL) {\n                            pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;\n                        }\n                    }\n                }\n\n                continue;\n            } else {\n                if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) {\n                    /*\n                    No data is available. We need to wait for more. There's two situations to consider\n                    here. The first is normal capture mode. If this times out it probably means the\n                    microphone isn't delivering data for whatever reason. In this case we'll just\n                    abort the read and return whatever we were able to get. The other situations is\n                    loopback mode, in which case a timeout probably just means the nothing is playing\n                    through the speakers.\n                    */\n\n                    /* Experiment: Use a shorter timeout for loopback mode. */\n                    DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS;\n                    if (pDevice->type == ma_device_type_loopback) {\n                        timeoutInMilliseconds = 10;\n                    }\n\n                    if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) {\n                        if (pDevice->type == ma_device_type_loopback) {\n                            continue;   /* Keep waiting in loopback mode. */\n                        } else {\n                            result = MA_ERROR;\n                            break;      /* Wait failed. */\n                        }\n                    }\n\n                    /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */\n                } else {\n                    /* An error occurred and we need to abort. */\n                    ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\\n\", (int)hr);\n                    result = ma_result_from_HRESULT(hr);\n                    break;\n                }\n            }\n        }\n    }\n\n    /*\n    If we were unable to process the entire requested frame count, but we still have a mapped buffer,\n    there's a good chance either an error occurred or the device was stopped mid-read. In this case\n    we'll need to make sure the buffer is released.\n    */\n    if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) {\n        ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);\n        pDevice->wasapi.pMappedBufferCapture   = NULL;\n        pDevice->wasapi.mappedBufferCaptureCap = 0;\n        pDevice->wasapi.mappedBufferCaptureLen = 0;\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = totalFramesProcessed;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint32 totalFramesProcessed = 0;\n\n    /* Keep writing to the device until it's stopped or we've consumed all of our input. */\n    while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {\n        ma_uint32 framesRemaining = frameCount - totalFramesProcessed;\n\n        /*\n        We're going to do this in a similar way to capture. We'll first check if the cached data pointer\n        is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with\n        a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE\n        it means we need to wait for some data to become available.\n        */\n        if (pDevice->wasapi.pMappedBufferPlayback != NULL) {\n            /* We still have some space available in the mapped data buffer. Write to it. */\n            ma_uint32 framesToProcessNow = framesRemaining;\n            if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) {\n                framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen);\n            }\n\n            /* Now just copy the data over to the output buffer. */\n            ma_copy_pcm_frames(\n                ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels),\n                ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels),\n                framesToProcessNow,\n                pDevice->playback.internalFormat, pDevice->playback.internalChannels\n            );\n\n            totalFramesProcessed                    += framesToProcessNow;\n            pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow;\n\n            /* If the data buffer has been fully consumed we need to release it. */\n            if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) {\n                ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);\n                pDevice->wasapi.pMappedBufferPlayback   = NULL;\n                pDevice->wasapi.mappedBufferPlaybackCap = 0;\n                pDevice->wasapi.mappedBufferPlaybackLen = 0;\n\n                /*\n                In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never\n                seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine\n                whether or not we need to wait for more data.\n                */\n                if (pDevice->playback.shareMode == ma_share_mode_exclusive) {\n                    if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {\n                        result = MA_ERROR;\n                        break;   /* Wait failed. Probably timed out. */\n                    }\n                }\n            }\n        } else {\n            /* We don't have a mapped data buffer so we'll need to get one. */\n            HRESULT hr;\n            ma_uint32 bufferSizeInFrames;\n\n            /* Special rules for exclusive mode. */\n            if (pDevice->playback.shareMode == ma_share_mode_exclusive) {\n                bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback;\n            } else {\n                bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback;\n            }\n\n            hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback);\n            if (hr == S_OK) {\n                /* We have data available. */\n                pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames;\n                pDevice->wasapi.mappedBufferPlaybackLen = 0;\n            } else {\n                if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) {\n                    /* Not enough data available. We need to wait for more. */\n                    if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {\n                        result = MA_ERROR;\n                        break;   /* Wait failed. Probably timed out. */\n                    }\n                } else {\n                    /* Some error occurred. We'll need to abort. */\n                    ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\\n\", (int)hr);\n                    result = ma_result_from_HRESULT(hr);\n                    break;\n                }\n            }\n        }\n    }\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = totalFramesProcessed;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {\n        SetEvent((HANDLE)pDevice->wasapi.hEventCapture);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        SetEvent((HANDLE)pDevice->wasapi.hEventPlayback);\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_context_uninit__wasapi(ma_context* pContext)\n{\n    ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_wasapi);\n\n    ma_context_post_command__wasapi(pContext, &cmd);\n    ma_thread_wait(&pContext->wasapi.commandThread);\n\n    if (pContext->wasapi.hAvrt) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt);\n        pContext->wasapi.hAvrt = NULL;\n    }\n\n    #if defined(MA_WIN32_UWP)\n    {\n        if (pContext->wasapi.hMMDevapi) {\n            ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi);\n            pContext->wasapi.hMMDevapi = NULL;\n        }\n    }\n    #endif\n\n    /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */\n    ma_semaphore_uninit(&pContext->wasapi.commandSem);\n    ma_mutex_uninit(&pContext->wasapi.commandLock);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n    ma_result result = MA_SUCCESS;\n\n    MA_ASSERT(pContext != NULL);\n\n    (void)pConfig;\n\n#ifdef MA_WIN32_DESKTOP\n    /*\n    WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven\n    exclusive mode does not work until SP1.\n\n    Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error.\n    */\n    {\n        ma_OSVERSIONINFOEXW osvi;\n        ma_handle kernel32DLL;\n        ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;\n        ma_PFNVerSetConditionMask _VerSetConditionMask;\n\n        kernel32DLL = ma_dlopen(ma_context_get_log(pContext), \"kernel32.dll\");\n        if (kernel32DLL == NULL) {\n            return MA_NO_BACKEND;\n        }\n\n        _VerifyVersionInfoW  = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, \"VerifyVersionInfoW\");\n        _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, \"VerSetConditionMask\");\n        if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {\n            ma_dlclose(ma_context_get_log(pContext), kernel32DLL);\n            return MA_NO_BACKEND;\n        }\n\n        MA_ZERO_OBJECT(&osvi);\n        osvi.dwOSVersionInfoSize = sizeof(osvi);\n        osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF);\n        osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF);\n        osvi.wServicePackMajor = 1;\n        if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) {\n            result = MA_SUCCESS;\n        } else {\n            result = MA_NO_BACKEND;\n        }\n\n        ma_dlclose(ma_context_get_log(pContext), kernel32DLL);\n    }\n#endif\n\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    MA_ZERO_OBJECT(&pContext->wasapi);\n\n\n    #if defined(MA_WIN32_UWP)\n    {\n        /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */\n        pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), \"mmdevapi.dll\");\n        if (pContext->wasapi.hMMDevapi) {\n            pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, \"ActivateAudioInterfaceAsync\");\n            if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) {\n                ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi);\n                return MA_NO_BACKEND;   /* ActivateAudioInterfaceAsync() could not be loaded. */\n            }\n        } else {\n            return MA_NO_BACKEND;   /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */\n        }\n    }\n    #endif\n\n    /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */\n    pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), \"avrt.dll\");\n    if (pContext->wasapi.hAvrt) {\n        pContext->wasapi.AvSetMmThreadCharacteristicsA   = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, \"AvSetMmThreadCharacteristicsA\");\n        pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, \"AvRevertMmThreadCharacteristics\");\n\n        /* If either function could not be found, disable use of avrt entirely. */\n        if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) {\n            pContext->wasapi.AvSetMmThreadCharacteristicsA   = NULL;\n            pContext->wasapi.AvRevertMmThreadcharacteristics = NULL;\n            ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt);\n            pContext->wasapi.hAvrt = NULL;\n        }\n    }\n\n\n    /*\n    Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread\n    than the one that retrieved it with GetService(). This can result in a deadlock in two\n    situations:\n\n        1) When calling ma_device_uninit() from a different thread to ma_device_init(); and\n        2) When uninitializing and reinitializing the internal IAudioClient object in response to\n           automatic stream routing.\n\n    We could define ma_device_uninit() such that it must be called on the same thread as\n    ma_device_init(). We could also just not release the IAudioClient when performing automatic\n    stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so\n    we're going to have to work around this with a worker thread. This is not ideal, but I can't\n    think of a better way to do this.\n\n    More information about this can be found here:\n\n        https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient\n\n    Note this section:\n\n        When releasing an IAudioRenderClient interface instance, the client must call the interface's\n        Release method from the same thread as the call to IAudioClient::GetService that created the\n        object.\n    */\n    {\n        result = ma_mutex_init(&pContext->wasapi.commandLock);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        result = ma_semaphore_init(0, &pContext->wasapi.commandSem);\n        if (result != MA_SUCCESS) {\n            ma_mutex_uninit(&pContext->wasapi.commandLock);\n            return result;\n        }\n\n        result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks);\n        if (result != MA_SUCCESS) {\n            ma_semaphore_uninit(&pContext->wasapi.commandSem);\n            ma_mutex_uninit(&pContext->wasapi.commandLock);\n            return result;\n        }\n    }\n\n\n    pCallbacks->onContextInit             = ma_context_init__wasapi;\n    pCallbacks->onContextUninit           = ma_context_uninit__wasapi;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__wasapi;\n    pCallbacks->onDeviceInit              = ma_device_init__wasapi;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__wasapi;\n    pCallbacks->onDeviceStart             = ma_device_start__wasapi;\n    pCallbacks->onDeviceStop              = ma_device_stop__wasapi;\n    pCallbacks->onDeviceRead              = ma_device_read__wasapi;\n    pCallbacks->onDeviceWrite             = ma_device_write__wasapi;\n    pCallbacks->onDeviceDataLoop          = NULL;\n    pCallbacks->onDeviceDataLoopWakeup    = ma_device_data_loop_wakeup__wasapi;\n\n    return MA_SUCCESS;\n}\n#endif\n\n/******************************************************************************\n\nDirectSound Backend\n\n******************************************************************************/\n#ifdef MA_HAS_DSOUND\n/*#include <dsound.h>*/\n\n/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/\n\n/* miniaudio only uses priority or exclusive modes. */\n#define MA_DSSCL_NORMAL                 1\n#define MA_DSSCL_PRIORITY               2\n#define MA_DSSCL_EXCLUSIVE              3\n#define MA_DSSCL_WRITEPRIMARY           4\n\n#define MA_DSCAPS_PRIMARYMONO           0x00000001\n#define MA_DSCAPS_PRIMARYSTEREO         0x00000002\n#define MA_DSCAPS_PRIMARY8BIT           0x00000004\n#define MA_DSCAPS_PRIMARY16BIT          0x00000008\n#define MA_DSCAPS_CONTINUOUSRATE        0x00000010\n#define MA_DSCAPS_EMULDRIVER            0x00000020\n#define MA_DSCAPS_CERTIFIED             0x00000040\n#define MA_DSCAPS_SECONDARYMONO         0x00000100\n#define MA_DSCAPS_SECONDARYSTEREO       0x00000200\n#define MA_DSCAPS_SECONDARY8BIT         0x00000400\n#define MA_DSCAPS_SECONDARY16BIT        0x00000800\n\n#define MA_DSBCAPS_PRIMARYBUFFER        0x00000001\n#define MA_DSBCAPS_STATIC               0x00000002\n#define MA_DSBCAPS_LOCHARDWARE          0x00000004\n#define MA_DSBCAPS_LOCSOFTWARE          0x00000008\n#define MA_DSBCAPS_CTRL3D               0x00000010\n#define MA_DSBCAPS_CTRLFREQUENCY        0x00000020\n#define MA_DSBCAPS_CTRLPAN              0x00000040\n#define MA_DSBCAPS_CTRLVOLUME           0x00000080\n#define MA_DSBCAPS_CTRLPOSITIONNOTIFY   0x00000100\n#define MA_DSBCAPS_CTRLFX               0x00000200\n#define MA_DSBCAPS_STICKYFOCUS          0x00004000\n#define MA_DSBCAPS_GLOBALFOCUS          0x00008000\n#define MA_DSBCAPS_GETCURRENTPOSITION2  0x00010000\n#define MA_DSBCAPS_MUTE3DATMAXDISTANCE  0x00020000\n#define MA_DSBCAPS_LOCDEFER             0x00040000\n#define MA_DSBCAPS_TRUEPLAYPOSITION     0x00080000\n\n#define MA_DSBPLAY_LOOPING              0x00000001\n#define MA_DSBPLAY_LOCHARDWARE          0x00000002\n#define MA_DSBPLAY_LOCSOFTWARE          0x00000004\n#define MA_DSBPLAY_TERMINATEBY_TIME     0x00000008\n#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010\n#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020\n\n#define MA_DSCBSTART_LOOPING            0x00000001\n\ntypedef struct\n{\n    DWORD dwSize;\n    DWORD dwFlags;\n    DWORD dwBufferBytes;\n    DWORD dwReserved;\n    MA_WAVEFORMATEX* lpwfxFormat;\n    GUID guid3DAlgorithm;\n} MA_DSBUFFERDESC;\n\ntypedef struct\n{\n    DWORD dwSize;\n    DWORD dwFlags;\n    DWORD dwBufferBytes;\n    DWORD dwReserved;\n    MA_WAVEFORMATEX* lpwfxFormat;\n    DWORD dwFXCount;\n    void* lpDSCFXDesc;  /* <-- miniaudio doesn't use this, so set to void*. */\n} MA_DSCBUFFERDESC;\n\ntypedef struct\n{\n    DWORD dwSize;\n    DWORD dwFlags;\n    DWORD dwMinSecondarySampleRate;\n    DWORD dwMaxSecondarySampleRate;\n    DWORD dwPrimaryBuffers;\n    DWORD dwMaxHwMixingAllBuffers;\n    DWORD dwMaxHwMixingStaticBuffers;\n    DWORD dwMaxHwMixingStreamingBuffers;\n    DWORD dwFreeHwMixingAllBuffers;\n    DWORD dwFreeHwMixingStaticBuffers;\n    DWORD dwFreeHwMixingStreamingBuffers;\n    DWORD dwMaxHw3DAllBuffers;\n    DWORD dwMaxHw3DStaticBuffers;\n    DWORD dwMaxHw3DStreamingBuffers;\n    DWORD dwFreeHw3DAllBuffers;\n    DWORD dwFreeHw3DStaticBuffers;\n    DWORD dwFreeHw3DStreamingBuffers;\n    DWORD dwTotalHwMemBytes;\n    DWORD dwFreeHwMemBytes;\n    DWORD dwMaxContigFreeHwMemBytes;\n    DWORD dwUnlockTransferRateHwBuffers;\n    DWORD dwPlayCpuOverheadSwBuffers;\n    DWORD dwReserved1;\n    DWORD dwReserved2;\n} MA_DSCAPS;\n\ntypedef struct\n{\n    DWORD dwSize;\n    DWORD dwFlags;\n    DWORD dwBufferBytes;\n    DWORD dwUnlockTransferRate;\n    DWORD dwPlayCpuOverhead;\n} MA_DSBCAPS;\n\ntypedef struct\n{\n    DWORD dwSize;\n    DWORD dwFlags;\n    DWORD dwFormats;\n    DWORD dwChannels;\n} MA_DSCCAPS;\n\ntypedef struct\n{\n    DWORD dwSize;\n    DWORD dwFlags;\n    DWORD dwBufferBytes;\n    DWORD dwReserved;\n} MA_DSCBCAPS;\n\ntypedef struct\n{\n    DWORD  dwOffset;\n    HANDLE hEventNotify;\n} MA_DSBPOSITIONNOTIFY;\n\ntypedef struct ma_IDirectSound              ma_IDirectSound;\ntypedef struct ma_IDirectSoundBuffer        ma_IDirectSoundBuffer;\ntypedef struct ma_IDirectSoundCapture       ma_IDirectSoundCapture;\ntypedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;\ntypedef struct ma_IDirectSoundNotify        ma_IDirectSoundNotify;\n\n\n/*\nCOM objects. The way these work is that you have a vtable (a list of function pointers, kind of\nlike how C++ works internally), and then you have a structure with a single member, which is a\npointer to the vtable. The vtable is where the methods of the object are defined. Methods need\nto be in a specific order, and parent classes need to have their methods declared first.\n*/\n\n/* IDirectSound */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IDirectSound* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IDirectSound* pThis);\n\n    /* IDirectSound */\n    HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer)   (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);\n    HRESULT (STDMETHODCALLTYPE * GetCaps)             (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);\n    HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);\n    HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);\n    HRESULT (STDMETHODCALLTYPE * Compact)             (ma_IDirectSound* pThis);\n    HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig)    (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);\n    HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig)    (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);\n    HRESULT (STDMETHODCALLTYPE * Initialize)          (ma_IDirectSound* pThis, const GUID* pGuidDevice);\n} ma_IDirectSoundVtbl;\nstruct ma_IDirectSound\n{\n    ma_IDirectSoundVtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IDirectSound_AddRef(ma_IDirectSound* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IDirectSound_Release(ma_IDirectSound* pThis)                                                { return pThis->lpVtbl->Release(pThis); }\nstatic MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); }\nstatic MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps)                            { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }\nstatic MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }\nstatic MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel)          { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }\nstatic MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis)                                                { return pThis->lpVtbl->Compact(pThis); }\nstatic MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig)                { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }\nstatic MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig)                { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }\nstatic MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice)                    { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }\n\n\n/* IDirectSoundBuffer */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IDirectSoundBuffer* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IDirectSoundBuffer* pThis);\n\n    /* IDirectSoundBuffer */\n    HRESULT (STDMETHODCALLTYPE * GetCaps)           (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);\n    HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);\n    HRESULT (STDMETHODCALLTYPE * GetFormat)         (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);\n    HRESULT (STDMETHODCALLTYPE * GetVolume)         (ma_IDirectSoundBuffer* pThis, LONG* pVolume);\n    HRESULT (STDMETHODCALLTYPE * GetPan)            (ma_IDirectSoundBuffer* pThis, LONG* pPan);\n    HRESULT (STDMETHODCALLTYPE * GetFrequency)      (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);\n    HRESULT (STDMETHODCALLTYPE * GetStatus)         (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);\n    HRESULT (STDMETHODCALLTYPE * Initialize)        (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);\n    HRESULT (STDMETHODCALLTYPE * Lock)              (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);\n    HRESULT (STDMETHODCALLTYPE * Play)              (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);\n    HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);\n    HRESULT (STDMETHODCALLTYPE * SetFormat)         (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat);\n    HRESULT (STDMETHODCALLTYPE * SetVolume)         (ma_IDirectSoundBuffer* pThis, LONG volume);\n    HRESULT (STDMETHODCALLTYPE * SetPan)            (ma_IDirectSoundBuffer* pThis, LONG pan);\n    HRESULT (STDMETHODCALLTYPE * SetFrequency)      (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);\n    HRESULT (STDMETHODCALLTYPE * Stop)              (ma_IDirectSoundBuffer* pThis);\n    HRESULT (STDMETHODCALLTYPE * Unlock)            (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);\n    HRESULT (STDMETHODCALLTYPE * Restore)           (ma_IDirectSoundBuffer* pThis);\n} ma_IDirectSoundBufferVtbl;\nstruct ma_IDirectSoundBuffer\n{\n    ma_IDirectSoundBufferVtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis)                                                { return pThis->lpVtbl->Release(pThis); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps)                     { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume)                               { return pThis->lpVtbl->GetVolume(pThis, pVolume); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan)                                     { return pThis->lpVtbl->GetPan(pThis, pPan); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency)                        { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus)                              { return pThis->lpVtbl->GetStatus(pThis, pStatus); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition)                { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat)              { return pThis->lpVtbl->SetFormat(pThis, pFormat); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume)                                 { return pThis->lpVtbl->SetVolume(pThis, volume); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan)                                       { return pThis->lpVtbl->SetPan(pThis, pan); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency)                        { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis)                                                   { return pThis->lpVtbl->Stop(pThis); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }\nstatic MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis)                                                { return pThis->lpVtbl->Restore(pThis); }\n\n\n/* IDirectSoundCapture */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IDirectSoundCapture* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IDirectSoundCapture* pThis);\n\n    /* IDirectSoundCapture */\n    HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);\n    HRESULT (STDMETHODCALLTYPE * GetCaps)            (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);\n    HRESULT (STDMETHODCALLTYPE * Initialize)         (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);\n} ma_IDirectSoundCaptureVtbl;\nstruct ma_IDirectSoundCapture\n{\n    ma_IDirectSoundCaptureVtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface     (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IDirectSoundCapture_AddRef             (ma_IDirectSoundCapture* pThis)                                    { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IDirectSoundCapture_Release            (ma_IDirectSoundCapture* pThis)                                    { return pThis->lpVtbl->Release(pThis); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps            (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps)              { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize         (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice)           { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }\n\n\n/* IDirectSoundCaptureBuffer */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IDirectSoundCaptureBuffer* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IDirectSoundCaptureBuffer* pThis);\n\n    /* IDirectSoundCaptureBuffer */\n    HRESULT (STDMETHODCALLTYPE * GetCaps)           (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);\n    HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);\n    HRESULT (STDMETHODCALLTYPE * GetFormat)         (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);\n    HRESULT (STDMETHODCALLTYPE * GetStatus)         (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);\n    HRESULT (STDMETHODCALLTYPE * Initialize)        (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);\n    HRESULT (STDMETHODCALLTYPE * Lock)              (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);\n    HRESULT (STDMETHODCALLTYPE * Start)             (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);\n    HRESULT (STDMETHODCALLTYPE * Stop)              (ma_IDirectSoundCaptureBuffer* pThis);\n    HRESULT (STDMETHODCALLTYPE * Unlock)            (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);\n} ma_IDirectSoundCaptureBufferVtbl;\nstruct ma_IDirectSoundCaptureBuffer\n{\n    ma_IDirectSoundCaptureBufferVtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis)                                                { return pThis->lpVtbl->Release(pThis); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps)                        { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus)                              { return pThis->lpVtbl->GetStatus(pThis, pStatus); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags)                                   { return pThis->lpVtbl->Start(pThis, dwFlags); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis)                                                   { return pThis->lpVtbl->Stop(pThis); }\nstatic MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }\n\n\n/* IDirectSoundNotify */\ntypedef struct\n{\n    /* IUnknown */\n    HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);\n    ULONG   (STDMETHODCALLTYPE * AddRef)        (ma_IDirectSoundNotify* pThis);\n    ULONG   (STDMETHODCALLTYPE * Release)       (ma_IDirectSoundNotify* pThis);\n\n    /* IDirectSoundNotify */\n    HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);\n} ma_IDirectSoundNotifyVtbl;\nstruct ma_IDirectSoundNotify\n{\n    ma_IDirectSoundNotifyVtbl* lpVtbl;\n};\nstatic MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }\nstatic MA_INLINE ULONG   ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis)                                                 { return pThis->lpVtbl->AddRef(pThis); }\nstatic MA_INLINE ULONG   ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis)                                                { return pThis->lpVtbl->Release(pThis); }\nstatic MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }\n\n\ntypedef BOOL    (CALLBACK * ma_DSEnumCallbackAProc)             (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext);\ntypedef HRESULT (WINAPI   * ma_DirectSoundCreateProc)           (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter);\ntypedef HRESULT (WINAPI   * ma_DirectSoundEnumerateAProc)       (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext);\ntypedef HRESULT (WINAPI   * ma_DirectSoundCaptureCreateProc)    (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter);\ntypedef HRESULT (WINAPI   * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext);\n\nstatic ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)\n{\n    /* Normalize the range in case we were given something stupid. */\n    if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) {\n        sampleRateMin = (ma_uint32)ma_standard_sample_rate_min;\n    }\n    if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) {\n        sampleRateMax = (ma_uint32)ma_standard_sample_rate_max;\n    }\n    if (sampleRateMin > sampleRateMax) {\n        sampleRateMin = sampleRateMax;\n    }\n\n    if (sampleRateMin == sampleRateMax) {\n        return sampleRateMax;\n    } else {\n        size_t iStandardRate;\n        for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {\n            ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];\n            if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {\n                return standardRate;\n            }\n        }\n    }\n\n    /* Should never get here. */\n    MA_ASSERT(MA_FALSE);\n    return 0;\n}\n\n/*\nRetrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown,\nthe channel count and channel map will be left unmodified.\n*/\nstatic void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)\n{\n    WORD  channels;\n    DWORD channelMap;\n\n    channels = 0;\n    if (pChannelsOut != NULL) {\n        channels = *pChannelsOut;\n    }\n\n    channelMap = 0;\n    if (pChannelMapOut != NULL) {\n        channelMap = *pChannelMapOut;\n    }\n\n    /*\n    The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper\n    16 bits is for the geometry.\n    */\n    switch ((BYTE)(speakerConfig)) {\n        case 1 /*DSSPEAKER_HEADPHONE*/:                          channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;\n        case 2 /*DSSPEAKER_MONO*/:                               channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;\n        case 3 /*DSSPEAKER_QUAD*/:                               channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;\n        case 4 /*DSSPEAKER_STEREO*/:                             channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;\n        case 5 /*DSSPEAKER_SURROUND*/:                           channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;\n        case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;\n        case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break;\n        case 8 /*DSSPEAKER_7POINT1_SURROUND*/:                   channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break;\n        case 9 /*DSSPEAKER_5POINT1_SURROUND*/:                   channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break;\n        default: break;\n    }\n\n    if (pChannelsOut != NULL) {\n        *pChannelsOut = channels;\n    }\n\n    if (pChannelMapOut != NULL) {\n        *pChannelMapOut = channelMap;\n    }\n}\n\n\nstatic ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)\n{\n    ma_IDirectSound* pDirectSound;\n    HWND hWnd;\n    HRESULT hr;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(ppDirectSound != NULL);\n\n    *ppDirectSound = NULL;\n    pDirectSound = NULL;\n\n    if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[DirectSound] DirectSoundCreate() failed for playback device.\");\n        return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n    }\n\n    /* The cooperative level must be set before doing anything else. */\n    hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();\n    if (hWnd == 0) {\n        hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();\n    }\n\n    hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);\n    if (FAILED(hr)) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.\");\n        return ma_result_from_HRESULT(hr);\n    }\n\n    *ppDirectSound = pDirectSound;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)\n{\n    ma_IDirectSoundCapture* pDirectSoundCapture;\n    HRESULT hr;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(ppDirectSoundCapture != NULL);\n\n    /* DirectSound does not support exclusive mode for capture. */\n    if (shareMode == ma_share_mode_exclusive) {\n        return MA_SHARE_MODE_NOT_SUPPORTED;\n    }\n\n    *ppDirectSoundCapture = NULL;\n    pDirectSoundCapture = NULL;\n\n    hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL);\n    if (FAILED(hr)) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[DirectSound] DirectSoundCaptureCreate() failed for capture device.\");\n        return ma_result_from_HRESULT(hr);\n    }\n\n    *ppDirectSoundCapture = pDirectSoundCapture;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)\n{\n    HRESULT hr;\n    MA_DSCCAPS caps;\n    WORD bitsPerSample;\n    DWORD sampleRate;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pDirectSoundCapture != NULL);\n\n    if (pChannels) {\n        *pChannels = 0;\n    }\n    if (pBitsPerSample) {\n        *pBitsPerSample = 0;\n    }\n    if (pSampleRate) {\n        *pSampleRate = 0;\n    }\n\n    MA_ZERO_OBJECT(&caps);\n    caps.dwSize = sizeof(caps);\n    hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps);\n    if (FAILED(hr)) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.\");\n        return ma_result_from_HRESULT(hr);\n    }\n\n    if (pChannels) {\n        *pChannels = (WORD)caps.dwChannels;\n    }\n\n    /* The device can support multiple formats. We just go through the different formats in order of priority and pick the first one. This the same type of system as the WinMM backend. */\n    bitsPerSample = 16;\n    sampleRate = 48000;\n\n    if (caps.dwChannels == 1) {\n        if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {\n            sampleRate = 48000;\n        } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {\n            sampleRate = 44100;\n        } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {\n            sampleRate = 22050;\n        } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {\n            sampleRate = 11025;\n        } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {\n            sampleRate = 96000;\n        } else {\n            bitsPerSample = 8;\n            if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {\n                sampleRate = 48000;\n            } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {\n                sampleRate = 44100;\n            } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {\n                sampleRate = 22050;\n            } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {\n                sampleRate = 11025;\n            } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {\n                sampleRate = 96000;\n            } else {\n                bitsPerSample = 16;  /* Didn't find it. Just fall back to 16-bit. */\n            }\n        }\n    } else if (caps.dwChannels == 2) {\n        if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {\n            sampleRate = 48000;\n        } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {\n            sampleRate = 44100;\n        } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {\n            sampleRate = 22050;\n        } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {\n            sampleRate = 11025;\n        } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {\n            sampleRate = 96000;\n        } else {\n            bitsPerSample = 8;\n            if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {\n                sampleRate = 48000;\n            } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {\n                sampleRate = 44100;\n            } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {\n                sampleRate = 22050;\n            } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {\n                sampleRate = 11025;\n            } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {\n                sampleRate = 96000;\n            } else {\n                bitsPerSample = 16;  /* Didn't find it. Just fall back to 16-bit. */\n            }\n        }\n    }\n\n    if (pBitsPerSample) {\n        *pBitsPerSample = bitsPerSample;\n    }\n    if (pSampleRate) {\n        *pSampleRate = sampleRate;\n    }\n\n    return MA_SUCCESS;\n}\n\n\ntypedef struct\n{\n    ma_context* pContext;\n    ma_device_type deviceType;\n    ma_enum_devices_callback_proc callback;\n    void* pUserData;\n    ma_bool32 terminated;\n} ma_context_enumerate_devices_callback_data__dsound;\n\nstatic BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext)\n{\n    ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;\n    ma_device_info deviceInfo;\n\n    (void)lpcstrModule;\n\n    MA_ZERO_OBJECT(&deviceInfo);\n\n    /* ID. */\n    if (lpGuid != NULL) {\n        MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16);\n    } else {\n        MA_ZERO_MEMORY(deviceInfo.id.dsound, 16);\n        deviceInfo.isDefault = MA_TRUE;\n    }\n\n    /* Name / Description */\n    ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);\n\n\n    /* Call the callback function, but make sure we stop enumerating if the callee requested so. */\n    MA_ASSERT(pData != NULL);\n    pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE);\n    if (pData->terminated) {\n        return FALSE;   /* Stop enumeration. */\n    } else {\n        return TRUE;    /* Continue enumeration. */\n    }\n}\n\nstatic ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    ma_context_enumerate_devices_callback_data__dsound data;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    data.pContext = pContext;\n    data.callback = callback;\n    data.pUserData = pUserData;\n    data.terminated = MA_FALSE;\n\n    /* Playback. */\n    if (!data.terminated) {\n        data.deviceType = ma_device_type_playback;\n        ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);\n    }\n\n    /* Capture. */\n    if (!data.terminated) {\n        data.deviceType = ma_device_type_capture;\n        ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);\n    }\n\n    return MA_SUCCESS;\n}\n\n\ntypedef struct\n{\n    const ma_device_id* pDeviceID;\n    ma_device_info* pDeviceInfo;\n    ma_bool32 found;\n} ma_context_get_device_info_callback_data__dsound;\n\nstatic BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext)\n{\n    ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;\n    MA_ASSERT(pData != NULL);\n\n    if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) {\n        /* Default device. */\n        ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);\n        pData->pDeviceInfo->isDefault = MA_TRUE;\n        pData->found = MA_TRUE;\n        return FALSE;   /* Stop enumeration. */\n    } else {\n        /* Not the default device. */\n        if (lpGuid != NULL && pData->pDeviceID != NULL) {\n            if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {\n                ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);\n                pData->found = MA_TRUE;\n                return FALSE;   /* Stop enumeration. */\n            }\n        }\n    }\n\n    (void)lpcstrModule;\n    return TRUE;\n}\n\nstatic ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    ma_result result;\n    HRESULT hr;\n\n    if (pDeviceID != NULL) {\n        ma_context_get_device_info_callback_data__dsound data;\n\n        /* ID. */\n        MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);\n\n        /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */\n        data.pDeviceID = pDeviceID;\n        data.pDeviceInfo = pDeviceInfo;\n        data.found = MA_FALSE;\n        if (deviceType == ma_device_type_playback) {\n            ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);\n        } else {\n            ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);\n        }\n\n        if (!data.found) {\n            return MA_NO_DEVICE;\n        }\n    } else {\n        /* I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. */\n\n        /* ID */\n        MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16);\n\n        /* Name / Description */\n        if (deviceType == ma_device_type_playback) {\n            ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n        } else {\n            ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n        }\n\n        pDeviceInfo->isDefault = MA_TRUE;\n    }\n\n    /* Retrieving detailed information is slightly different depending on the device type. */\n    if (deviceType == ma_device_type_playback) {\n        /* Playback. */\n        ma_IDirectSound* pDirectSound;\n        MA_DSCAPS caps;\n        WORD channels;\n\n        result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        MA_ZERO_OBJECT(&caps);\n        caps.dwSize = sizeof(caps);\n        hr = ma_IDirectSound_GetCaps(pDirectSound, &caps);\n        if (FAILED(hr)) {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSound_GetCaps() failed for playback device.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n\n        /* Channels. Only a single channel count is reported for DirectSound. */\n        if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {\n            /* It supports at least stereo, but could support more. */\n            DWORD speakerConfig;\n\n            channels = 2;\n\n            /* Look at the speaker configuration to get a better idea on the channel count. */\n            hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig);\n            if (SUCCEEDED(hr)) {\n                ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);\n            }\n        } else {\n            /* It does not support stereo, which means we are stuck with mono. */\n            channels = 1;\n        }\n\n\n        /*\n        In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel\n        count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio\n        in order to keep the size of this within reason.\n        */\n        if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {\n            /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */\n            size_t iStandardSampleRate;\n            for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {\n                ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];\n                if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) {\n                    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format     = ma_format_unknown;\n                    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels   = channels;\n                    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;\n                    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags      = 0;\n                    pDeviceInfo->nativeDataFormatCount += 1;\n                }\n            }\n        } else {\n            /* Only a single sample rate is supported. */\n            pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format     = ma_format_unknown;\n            pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels   = channels;\n            pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate;\n            pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags      = 0;\n            pDeviceInfo->nativeDataFormatCount += 1;\n        }\n\n        ma_IDirectSound_Release(pDirectSound);\n    } else {\n        /*\n        Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture\n        devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just\n        reporting the best format.\n        */\n        ma_IDirectSoundCapture* pDirectSoundCapture;\n        WORD channels;\n        WORD bitsPerSample;\n        DWORD sampleRate;\n\n        result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);\n        if (result != MA_SUCCESS) {\n            ma_IDirectSoundCapture_Release(pDirectSoundCapture);\n            return result;\n        }\n\n        ma_IDirectSoundCapture_Release(pDirectSoundCapture);\n\n        /* The format is always an integer format and is based on the bits per sample. */\n        if (bitsPerSample == 8) {\n            pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;\n        } else if (bitsPerSample == 16) {\n            pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;\n        } else if (bitsPerSample == 24) {\n            pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;\n        } else if (bitsPerSample == 32) {\n            pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;\n        } else {\n            return MA_FORMAT_NOT_SUPPORTED;\n        }\n\n        pDeviceInfo->nativeDataFormats[0].channels   = channels;\n        pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;\n        pDeviceInfo->nativeDataFormats[0].flags      = 0;\n        pDeviceInfo->nativeDataFormatCount = 1;\n    }\n\n    return MA_SUCCESS;\n}\n\n\n\nstatic ma_result ma_device_uninit__dsound(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->dsound.pCaptureBuffer != NULL) {\n        ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);\n    }\n    if (pDevice->dsound.pCapture != NULL) {\n        ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);\n    }\n\n    if (pDevice->dsound.pPlaybackBuffer != NULL) {\n        ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);\n    }\n    if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {\n        ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);\n    }\n    if (pDevice->dsound.pPlayback != NULL) {\n        ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF)\n{\n    GUID subformat;\n\n    if (format == ma_format_unknown) {\n        format = MA_DEFAULT_FORMAT;\n    }\n\n    if (channels == 0) {\n        channels = MA_DEFAULT_CHANNELS;\n    }\n\n    if (sampleRate == 0) {\n        sampleRate = MA_DEFAULT_SAMPLE_RATE;\n    }\n\n    switch (format)\n    {\n        case ma_format_u8:\n        case ma_format_s16:\n        case ma_format_s24:\n        /*case ma_format_s24_32:*/\n        case ma_format_s32:\n        {\n            subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;\n        } break;\n\n        case ma_format_f32:\n        {\n            subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;\n        } break;\n\n        default:\n        return MA_FORMAT_NOT_SUPPORTED;\n    }\n\n    MA_ZERO_OBJECT(pWF);\n    pWF->cbSize                      = sizeof(*pWF);\n    pWF->wFormatTag                  = WAVE_FORMAT_EXTENSIBLE;\n    pWF->nChannels                   = (WORD)channels;\n    pWF->nSamplesPerSec              = (DWORD)sampleRate;\n    pWF->wBitsPerSample              = (WORD)(ma_get_bytes_per_sample(format)*8);\n    pWF->nBlockAlign                 = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);\n    pWF->nAvgBytesPerSec             = pWF->nBlockAlign * pWF->nSamplesPerSec;\n    pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample;\n    pWF->dwChannelMask               = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);\n    pWF->SubFormat                   = subformat;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)\n{\n    /*\n    DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for\n    reliable glitch-free processing so going to use 30ms instead.\n    */\n    ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate);\n    ma_uint32 periodSizeInFrames;\n\n    periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);\n    if (periodSizeInFrames < minPeriodSizeInFrames) {\n        periodSizeInFrames = minPeriodSizeInFrames;\n    }\n\n    return periodSizeInFrames;\n}\n\nstatic ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    ma_result result;\n    HRESULT hr;\n\n    MA_ASSERT(pDevice != NULL);\n\n    MA_ZERO_OBJECT(&pDevice->dsound);\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    /*\n    Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize\n    the capture device first because we'll want to match it's buffer size and period count on the playback side if we're using\n    full-duplex mode.\n    */\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        MA_WAVEFORMATEXTENSIBLE wf;\n        MA_DSCBUFFERDESC descDS;\n        ma_uint32 periodSizeInFrames;\n        ma_uint32 periodCount;\n        char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */\n        MA_WAVEFORMATEXTENSIBLE* pActualFormat;\n\n        result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);\n        if (result != MA_SUCCESS) {\n            ma_device_uninit__dsound(pDevice);\n            return result;\n        }\n\n        result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec);\n        if (result != MA_SUCCESS) {\n            ma_device_uninit__dsound(pDevice);\n            return result;\n        }\n\n        wf.nBlockAlign                 = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);\n        wf.nAvgBytesPerSec             = wf.nBlockAlign * wf.nSamplesPerSec;\n        wf.Samples.wValidBitsPerSample = wf.wBitsPerSample;\n        wf.SubFormat                   = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;\n\n        /* The size of the buffer must be a clean multiple of the period count. */\n        periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile);\n        periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS;\n\n        MA_ZERO_OBJECT(&descDS);\n        descDS.dwSize        = sizeof(descDS);\n        descDS.dwFlags       = 0;\n        descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign;\n        descDS.lpwfxFormat   = (MA_WAVEFORMATEX*)&wf;\n        hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);\n        if (FAILED(hr)) {\n            ma_device_uninit__dsound(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n        /* Get the _actual_ properties of the buffer. */\n        pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata;\n        hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);\n        if (FAILED(hr)) {\n            ma_device_uninit__dsound(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Failed to retrieve the actual format of the capture device's buffer.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n        /* We can now start setting the output data formats. */\n        pDescriptorCapture->format     = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat);\n        pDescriptorCapture->channels   = pActualFormat->nChannels;\n        pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec;\n\n        /* Get the native channel map based on the channel mask. */\n        if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {\n            ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);\n        } else {\n            ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);\n        }\n\n        /*\n        After getting the actual format the size of the buffer in frames may have actually changed. However, we want this to be as close to what the\n        user has asked for as possible, so let's go ahead and release the old capture buffer and create a new one in this case.\n        */\n        if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) {\n            descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount;\n            ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);\n\n            hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);\n            if (FAILED(hr)) {\n                ma_device_uninit__dsound(pDevice);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.\");\n                return ma_result_from_HRESULT(hr);\n            }\n        }\n\n        /* DirectSound should give us a buffer exactly the size we asked for. */\n        pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;\n        pDescriptorCapture->periodCount        = periodCount;\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        MA_WAVEFORMATEXTENSIBLE wf;\n        MA_DSBUFFERDESC descDSPrimary;\n        MA_DSCAPS caps;\n        char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */\n        MA_WAVEFORMATEXTENSIBLE* pActualFormat;\n        ma_uint32 periodSizeInFrames;\n        ma_uint32 periodCount;\n        MA_DSBUFFERDESC descDS;\n        WORD nativeChannelCount;\n        DWORD nativeChannelMask = 0;\n\n        result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);\n        if (result != MA_SUCCESS) {\n            ma_device_uninit__dsound(pDevice);\n            return result;\n        }\n\n        MA_ZERO_OBJECT(&descDSPrimary);\n        descDSPrimary.dwSize  = sizeof(MA_DSBUFFERDESC);\n        descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;\n        hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL);\n        if (FAILED(hr)) {\n            ma_device_uninit__dsound(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n\n        /* We may want to make some adjustments to the format if we are using defaults. */\n        MA_ZERO_OBJECT(&caps);\n        caps.dwSize = sizeof(caps);\n        hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps);\n        if (FAILED(hr)) {\n            ma_device_uninit__dsound(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSound_GetCaps() failed for playback device.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n        if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {\n            DWORD speakerConfig;\n\n            /* It supports at least stereo, but could support more. */\n            nativeChannelCount = 2;\n\n            /* Look at the speaker configuration to get a better idea on the channel count. */\n            if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {\n                ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask);\n            }\n        } else {\n            /* It does not support stereo, which means we are stuck with mono. */\n            nativeChannelCount = 1;\n            nativeChannelMask  = 0x00000001;\n        }\n\n        if (pDescriptorPlayback->channels == 0) {\n            wf.nChannels = nativeChannelCount;\n            wf.dwChannelMask    = nativeChannelMask;\n        }\n\n        if (pDescriptorPlayback->sampleRate == 0) {\n            /* We base the sample rate on the values returned by GetCaps(). */\n            if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {\n                wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);\n            } else {\n                wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate;\n            }\n        }\n\n        wf.nBlockAlign     = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);\n        wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;\n\n        /*\n        From MSDN:\n\n        The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest\n        supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer\n        and compare the result with the format that was requested with the SetFormat method.\n        */\n        hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf);\n        if (FAILED(hr)) {\n            /*\n            If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have\n            observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a\n            sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will\n            use 44100 for the sample rate.\n            */\n            wf.cbSize          = 18;    /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */\n            wf.wFormatTag      = WAVE_FORMAT_PCM;\n            wf.wBitsPerSample  = 16;\n            wf.nChannels       = nativeChannelCount;\n            wf.nSamplesPerSec  = 44100;\n            wf.nBlockAlign     = wf.nChannels * (wf.wBitsPerSample / 8);\n            wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;\n\n            hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf);\n            if (FAILED(hr)) {\n                ma_device_uninit__dsound(pDevice);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Failed to set format of playback device's primary buffer.\");\n                return ma_result_from_HRESULT(hr);\n            }\n        }\n\n        /* Get the _actual_ properties of the buffer. */\n        pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata;\n        hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);\n        if (FAILED(hr)) {\n            ma_device_uninit__dsound(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n        /* We now have enough information to start setting some output properties. */\n        pDescriptorPlayback->format     = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat);\n        pDescriptorPlayback->channels   = pActualFormat->nChannels;\n        pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec;\n\n        /* Get the internal channel map based on the channel mask. */\n        if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {\n            ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);\n        } else {\n            ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);\n        }\n\n        /* The size of the buffer must be a clean multiple of the period count. */\n        periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);\n        periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS;\n\n        /*\n        Meaning of dwFlags (from MSDN):\n\n        DSBCAPS_CTRLPOSITIONNOTIFY\n          The buffer has position notification capability.\n\n        DSBCAPS_GLOBALFOCUS\n          With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to\n          another application, even if the new application uses DirectSound.\n\n        DSBCAPS_GETCURRENTPOSITION2\n          In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated\n          sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the\n          application can get a more accurate play cursor.\n        */\n        MA_ZERO_OBJECT(&descDS);\n        descDS.dwSize = sizeof(descDS);\n        descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;\n        descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels);\n        descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat;\n        hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL);\n        if (FAILED(hr)) {\n            ma_device_uninit__dsound(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n        /* DirectSound should give us a buffer exactly the size we asked for. */\n        pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;\n        pDescriptorPlayback->periodCount        = periodCount;\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_device_data_loop__dsound(ma_device* pDevice)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint32 bpfDeviceCapture  = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);\n    ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);\n    HRESULT hr;\n    DWORD lockOffsetInBytesCapture;\n    DWORD lockSizeInBytesCapture;\n    DWORD mappedSizeInBytesCapture;\n    DWORD mappedDeviceFramesProcessedCapture;\n    void* pMappedDeviceBufferCapture;\n    DWORD lockOffsetInBytesPlayback;\n    DWORD lockSizeInBytesPlayback;\n    DWORD mappedSizeInBytesPlayback;\n    void* pMappedDeviceBufferPlayback;\n    DWORD prevReadCursorInBytesCapture = 0;\n    DWORD prevPlayCursorInBytesPlayback = 0;\n    ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;\n    DWORD virtualWriteCursorInBytesPlayback = 0;\n    ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;\n    ma_bool32 isPlaybackDeviceStarted = MA_FALSE;\n    ma_uint32 framesWrittenToPlaybackDevice = 0;   /* For knowing whether or not the playback device needs to be started. */\n    ma_uint32 waitTimeInMilliseconds = 1;\n\n    MA_ASSERT(pDevice != NULL);\n\n    /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING);\n        if (FAILED(hr)) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSoundCaptureBuffer_Start() failed.\");\n            return ma_result_from_HRESULT(hr);\n        }\n    }\n\n    while (ma_device_get_state(pDevice) == ma_device_state_started) {\n        switch (pDevice->type)\n        {\n            case ma_device_type_duplex:\n            {\n                DWORD physicalCaptureCursorInBytes;\n                DWORD physicalReadCursorInBytes;\n                hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);\n                if (FAILED(hr)) {\n                    return ma_result_from_HRESULT(hr);\n                }\n\n                /* If nothing is available we just sleep for a bit and return from this iteration. */\n                if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {\n                    ma_sleep(waitTimeInMilliseconds);\n                    continue; /* Nothing is available in the capture buffer. */\n                }\n\n                /*\n                The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure\n                we don't return until every frame has been copied over.\n                */\n                if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {\n                    /* The capture position has not looped. This is the simple case. */\n                    lockOffsetInBytesCapture = prevReadCursorInBytesCapture;\n                    lockSizeInBytesCapture   = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);\n                } else {\n                    /*\n                    The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,\n                    do it again from the start.\n                    */\n                    if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {\n                        /* Lock up to the end of the buffer. */\n                        lockOffsetInBytesCapture = prevReadCursorInBytesCapture;\n                        lockSizeInBytesCapture   = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;\n                    } else {\n                        /* Lock starting from the start of the buffer. */\n                        lockOffsetInBytesCapture = 0;\n                        lockSizeInBytesCapture   = physicalReadCursorInBytes;\n                    }\n                }\n\n                if (lockSizeInBytesCapture == 0) {\n                    ma_sleep(waitTimeInMilliseconds);\n                    continue; /* Nothing is available in the capture buffer. */\n                }\n\n                hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);\n                if (FAILED(hr)) {\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.\");\n                    return ma_result_from_HRESULT(hr);\n                }\n\n\n                /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */\n                mappedDeviceFramesProcessedCapture = 0;\n\n                for (;;) {  /* Keep writing to the playback device. */\n                    ma_uint8  inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n                    ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);\n                    ma_uint8  outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n                    ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);\n                    ma_uint32 outputFramesInClientFormatCount;\n                    ma_uint32 outputFramesInClientFormatConsumed = 0;\n                    ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap);\n                    ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture;\n                    void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture);\n\n                    result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess);\n                    if (result != MA_SUCCESS) {\n                        break;\n                    }\n\n                    outputFramesInClientFormatCount     = (ma_uint32)clientCapturedFramesToProcess;\n                    mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess;\n\n                    ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess);\n\n                    /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */\n                    for (;;) {\n                        ma_uint32 framesWrittenThisIteration;\n                        DWORD physicalPlayCursorInBytes;\n                        DWORD physicalWriteCursorInBytes;\n                        DWORD availableBytesPlayback;\n                        DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */\n\n                        /* We need the physical play and write cursors. */\n                        if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {\n                            break;\n                        }\n\n                        if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {\n                            physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;\n                        }\n                        prevPlayCursorInBytesPlayback  = physicalPlayCursorInBytes;\n\n                        /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */\n                        if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {\n                            /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */\n                            if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {\n                                availableBytesPlayback  = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;\n                                availableBytesPlayback += physicalPlayCursorInBytes;    /* Wrap around. */\n                            } else {\n                                /* This is an error. */\n                                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, \"[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\\n\", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);\n                                availableBytesPlayback = 0;\n                            }\n                        } else {\n                            /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */\n                            if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {\n                                availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;\n                            } else {\n                                /* This is an error. */\n                                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, \"[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\\n\", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);\n                                availableBytesPlayback = 0;\n                            }\n                        }\n\n                        /* If there's no room available for writing we need to wait for more. */\n                        if (availableBytesPlayback == 0) {\n                            /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */\n                            if (!isPlaybackDeviceStarted) {\n                                hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);\n                                if (FAILED(hr)) {\n                                    ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);\n                                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSoundBuffer_Play() failed.\");\n                                    return ma_result_from_HRESULT(hr);\n                                }\n                                isPlaybackDeviceStarted = MA_TRUE;\n                            } else {\n                                ma_sleep(waitTimeInMilliseconds);\n                                continue;\n                            }\n                        }\n\n\n                        /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */\n                        lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;\n                        if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {\n                            /* Same loop iteration. Go up to the end of the buffer. */\n                            lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;\n                        } else {\n                            /* Different loop iterations. Go up to the physical play cursor. */\n                            lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;\n                        }\n\n                        hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);\n                        if (FAILED(hr)) {\n                            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.\");\n                            result = ma_result_from_HRESULT(hr);\n                            break;\n                        }\n\n                        /*\n                        Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent\n                        endless glitching due to it constantly running out of data.\n                        */\n                        if (isPlaybackDeviceStarted) {\n                            DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback;\n                            if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) {\n                                silentPaddingInBytes   = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback;\n                                if (silentPaddingInBytes > lockSizeInBytesPlayback) {\n                                    silentPaddingInBytes = lockSizeInBytesPlayback;\n                                }\n\n                                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, \"[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\\n\", availableBytesPlayback, silentPaddingInBytes);\n                            }\n                        }\n\n                        /* At this point we have a buffer for output. */\n                        if (silentPaddingInBytes > 0) {\n                            MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes);\n                            framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback;\n                        } else {\n                            ma_uint64 convertedFrameCountIn  = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed);\n                            ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback;\n                            void* pConvertedFramesIn  = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback);\n                            void* pConvertedFramesOut = pMappedDeviceBufferPlayback;\n\n                            result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut);\n                            if (result != MA_SUCCESS) {\n                                break;\n                            }\n\n                            outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut;\n                            framesWrittenThisIteration          = (ma_uint32)convertedFrameCountOut;\n                        }\n\n\n                        hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0);\n                        if (FAILED(hr)) {\n                            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.\");\n                            result = ma_result_from_HRESULT(hr);\n                            break;\n                        }\n\n                        virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback;\n                        if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) {\n                            virtualWriteCursorInBytesPlayback  = 0;\n                            virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;\n                        }\n\n                        /*\n                        We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds\n                        a bit of a buffer to prevent the playback buffer from getting starved.\n                        */\n                        framesWrittenToPlaybackDevice += framesWrittenThisIteration;\n                        if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) {\n                            hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);\n                            if (FAILED(hr)) {\n                                ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);\n                                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSoundBuffer_Play() failed.\");\n                                return ma_result_from_HRESULT(hr);\n                            }\n                            isPlaybackDeviceStarted = MA_TRUE;\n                        }\n\n                        if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) {\n                            break;  /* We're finished with the output data.*/\n                        }\n                    }\n\n                    if (clientCapturedFramesToProcess == 0) {\n                        break;  /* We just consumed every input sample. */\n                    }\n                }\n\n\n                /* At this point we're done with the mapped portion of the capture buffer. */\n                hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);\n                if (FAILED(hr)) {\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.\");\n                    return ma_result_from_HRESULT(hr);\n                }\n                prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);\n            } break;\n\n\n\n            case ma_device_type_capture:\n            {\n                DWORD physicalCaptureCursorInBytes;\n                DWORD physicalReadCursorInBytes;\n                hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);\n                if (FAILED(hr)) {\n                    return MA_ERROR;\n                }\n\n                /* If the previous capture position is the same as the current position we need to wait a bit longer. */\n                if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {\n                    ma_sleep(waitTimeInMilliseconds);\n                    continue;\n                }\n\n                /* Getting here means we have capture data available. */\n                if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {\n                    /* The capture position has not looped. This is the simple case. */\n                    lockOffsetInBytesCapture = prevReadCursorInBytesCapture;\n                    lockSizeInBytesCapture   = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);\n                } else {\n                    /*\n                    The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,\n                    do it again from the start.\n                    */\n                    if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {\n                        /* Lock up to the end of the buffer. */\n                        lockOffsetInBytesCapture = prevReadCursorInBytesCapture;\n                        lockSizeInBytesCapture   = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;\n                    } else {\n                        /* Lock starting from the start of the buffer. */\n                        lockOffsetInBytesCapture = 0;\n                        lockSizeInBytesCapture   = physicalReadCursorInBytes;\n                    }\n                }\n\n                if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) {\n                    ma_sleep(waitTimeInMilliseconds);\n                    continue; /* Nothing is available in the capture buffer. */\n                }\n\n                hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);\n                if (FAILED(hr)) {\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.\");\n                    result = ma_result_from_HRESULT(hr);\n                }\n\n                if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {\n                    ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\\n\", lockSizeInBytesCapture, mappedSizeInBytesCapture);\n                }\n\n                ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture);\n\n                hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);\n                if (FAILED(hr)) {\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.\");\n                    return ma_result_from_HRESULT(hr);\n                }\n                prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;\n\n                if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) {\n                    prevReadCursorInBytesCapture = 0;\n                }\n            } break;\n\n\n\n            case ma_device_type_playback:\n            {\n                DWORD availableBytesPlayback;\n                DWORD physicalPlayCursorInBytes;\n                DWORD physicalWriteCursorInBytes;\n                hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);\n                if (FAILED(hr)) {\n                    break;\n                }\n\n                if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {\n                    physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;\n                }\n                prevPlayCursorInBytesPlayback  = physicalPlayCursorInBytes;\n\n                /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */\n                if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {\n                    /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */\n                    if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {\n                        availableBytesPlayback  = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;\n                        availableBytesPlayback += physicalPlayCursorInBytes;    /* Wrap around. */\n                    } else {\n                        /* This is an error. */\n                        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, \"[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\\n\", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);\n                        availableBytesPlayback = 0;\n                    }\n                } else {\n                    /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */\n                    if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {\n                        availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;\n                    } else {\n                        /* This is an error. */\n                        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, \"[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\\n\", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);\n                        availableBytesPlayback = 0;\n                    }\n                }\n\n                /* If there's no room available for writing we need to wait for more. */\n                if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) {\n                    /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */\n                    if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {\n                        hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);\n                        if (FAILED(hr)) {\n                            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSoundBuffer_Play() failed.\");\n                            return ma_result_from_HRESULT(hr);\n                        }\n                        isPlaybackDeviceStarted = MA_TRUE;\n                    } else {\n                        ma_sleep(waitTimeInMilliseconds);\n                        continue;\n                    }\n                }\n\n                /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */\n                lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;\n                if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {\n                    /* Same loop iteration. Go up to the end of the buffer. */\n                    lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;\n                } else {\n                    /* Different loop iterations. Go up to the physical play cursor. */\n                    lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;\n                }\n\n                hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);\n                if (FAILED(hr)) {\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.\");\n                    result = ma_result_from_HRESULT(hr);\n                    break;\n                }\n\n                /* At this point we have a buffer for output. */\n                ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback);\n\n                hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);\n                if (FAILED(hr)) {\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.\");\n                    result = ma_result_from_HRESULT(hr);\n                    break;\n                }\n\n                virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;\n                if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) {\n                    virtualWriteCursorInBytesPlayback  = 0;\n                    virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;\n                }\n\n                /*\n                We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds\n                a bit of a buffer to prevent the playback buffer from getting starved.\n                */\n                framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback;\n                if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) {\n                    hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);\n                    if (FAILED(hr)) {\n                        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSoundBuffer_Play() failed.\");\n                        return ma_result_from_HRESULT(hr);\n                    }\n                    isPlaybackDeviceStarted = MA_TRUE;\n                }\n            } break;\n\n\n            default: return MA_INVALID_ARGS;   /* Invalid device type. */\n        }\n\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    /* Getting here means the device is being stopped. */\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);\n        if (FAILED(hr)) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.\");\n            return ma_result_from_HRESULT(hr);\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        /* The playback device should be drained before stopping. All we do is wait until the available bytes is equal to the size of the buffer. */\n        if (isPlaybackDeviceStarted) {\n            for (;;) {\n                DWORD availableBytesPlayback = 0;\n                DWORD physicalPlayCursorInBytes;\n                DWORD physicalWriteCursorInBytes;\n                hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);\n                if (FAILED(hr)) {\n                    break;\n                }\n\n                if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {\n                    physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;\n                }\n                prevPlayCursorInBytesPlayback  = physicalPlayCursorInBytes;\n\n                if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {\n                    /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */\n                    if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {\n                        availableBytesPlayback  = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;\n                        availableBytesPlayback += physicalPlayCursorInBytes;    /* Wrap around. */\n                    } else {\n                        break;\n                    }\n                } else {\n                    /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */\n                    if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {\n                        availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;\n                    } else {\n                        break;\n                    }\n                }\n\n                if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) {\n                    break;\n                }\n\n                ma_sleep(waitTimeInMilliseconds);\n            }\n        }\n\n        hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);\n        if (FAILED(hr)) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[DirectSound] IDirectSoundBuffer_Stop() failed.\");\n            return ma_result_from_HRESULT(hr);\n        }\n\n        ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_uninit__dsound(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_dsound);\n\n    ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n    MA_ASSERT(pContext != NULL);\n\n    (void)pConfig;\n\n    pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), \"dsound.dll\");\n    if (pContext->dsound.hDSoundDLL == NULL) {\n        return MA_API_NOT_FOUND;\n    }\n\n    pContext->dsound.DirectSoundCreate            = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, \"DirectSoundCreate\");\n    pContext->dsound.DirectSoundEnumerateA        = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, \"DirectSoundEnumerateA\");\n    pContext->dsound.DirectSoundCaptureCreate     = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, \"DirectSoundCaptureCreate\");\n    pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, \"DirectSoundCaptureEnumerateA\");\n\n    /*\n    We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too\n    well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient\n    place to just disable the DirectSound backend for Windows 95.\n    */\n    if (pContext->dsound.DirectSoundCreate            == NULL ||\n        pContext->dsound.DirectSoundEnumerateA        == NULL ||\n        pContext->dsound.DirectSoundCaptureCreate     == NULL ||\n        pContext->dsound.DirectSoundCaptureEnumerateA == NULL) {\n        return MA_API_NOT_FOUND;\n    }\n\n    pCallbacks->onContextInit             = ma_context_init__dsound;\n    pCallbacks->onContextUninit           = ma_context_uninit__dsound;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__dsound;\n    pCallbacks->onDeviceInit              = ma_device_init__dsound;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__dsound;\n    pCallbacks->onDeviceStart             = NULL;   /* Not used. Started in onDeviceDataLoop. */\n    pCallbacks->onDeviceStop              = NULL;   /* Not used. Stopped in onDeviceDataLoop. */\n    pCallbacks->onDeviceRead              = NULL;   /* Not used. Data is read directly in onDeviceDataLoop. */\n    pCallbacks->onDeviceWrite             = NULL;   /* Not used. Data is written directly in onDeviceDataLoop. */\n    pCallbacks->onDeviceDataLoop          = ma_device_data_loop__dsound;\n\n    return MA_SUCCESS;\n}\n#endif\n\n\n\n/******************************************************************************\n\nWinMM Backend\n\n******************************************************************************/\n#ifdef MA_HAS_WINMM\n\n/*\nSome build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN\nis defined. We need to define the types and functions we need manually.\n*/\n#define MA_MMSYSERR_NOERROR     0\n#define MA_MMSYSERR_ERROR       1\n#define MA_MMSYSERR_BADDEVICEID 2\n#define MA_MMSYSERR_INVALHANDLE 5\n#define MA_MMSYSERR_NOMEM       7\n#define MA_MMSYSERR_INVALFLAG   10\n#define MA_MMSYSERR_INVALPARAM  11\n#define MA_MMSYSERR_HANDLEBUSY  12\n\n#define MA_CALLBACK_EVENT       0x00050000\n#define MA_WAVE_ALLOWSYNC       0x0002\n\n#define MA_WHDR_DONE            0x00000001\n#define MA_WHDR_PREPARED        0x00000002\n#define MA_WHDR_BEGINLOOP       0x00000004\n#define MA_WHDR_ENDLOOP         0x00000008\n#define MA_WHDR_INQUEUE         0x00000010\n\n#define MA_MAXPNAMELEN          32\n\ntypedef void* MA_HWAVEIN;\ntypedef void* MA_HWAVEOUT;\ntypedef UINT MA_MMRESULT;\ntypedef UINT MA_MMVERSION;\n\ntypedef struct\n{\n    WORD wMid;\n    WORD wPid;\n    MA_MMVERSION vDriverVersion;\n    CHAR szPname[MA_MAXPNAMELEN];\n    DWORD dwFormats;\n    WORD wChannels;\n    WORD wReserved1;\n} MA_WAVEINCAPSA;\n\ntypedef struct\n{\n    WORD wMid;\n    WORD wPid;\n    MA_MMVERSION vDriverVersion;\n    CHAR szPname[MA_MAXPNAMELEN];\n    DWORD dwFormats;\n    WORD wChannels;\n    WORD wReserved1;\n    DWORD dwSupport;\n} MA_WAVEOUTCAPSA;\n\ntypedef struct tagWAVEHDR\n{\n    char* lpData;\n    DWORD dwBufferLength;\n    DWORD dwBytesRecorded;\n    DWORD_PTR dwUser;\n    DWORD dwFlags;\n    DWORD dwLoops;\n    struct tagWAVEHDR* lpNext;\n    DWORD_PTR reserved;\n} MA_WAVEHDR;\n\ntypedef struct\n{\n    WORD wMid;\n    WORD wPid;\n    MA_MMVERSION vDriverVersion;\n    CHAR szPname[MA_MAXPNAMELEN];\n    DWORD dwFormats;\n    WORD wChannels;\n    WORD wReserved1;\n    DWORD dwSupport;\n    GUID ManufacturerGuid;\n    GUID ProductGuid;\n    GUID NameGuid;\n} MA_WAVEOUTCAPS2A;\n\ntypedef struct\n{\n    WORD wMid;\n    WORD wPid;\n    MA_MMVERSION vDriverVersion;\n    CHAR szPname[MA_MAXPNAMELEN];\n    DWORD dwFormats;\n    WORD wChannels;\n    WORD wReserved1;\n    GUID ManufacturerGuid;\n    GUID ProductGuid;\n    GUID NameGuid;\n} MA_WAVEINCAPS2A;\n\ntypedef UINT        (WINAPI * MA_PFN_waveOutGetNumDevs)(void);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo);\ntypedef UINT        (WINAPI * MA_PFN_waveInGetNumDevs)(void);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi);\ntypedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi);\n\nstatic ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM)\n{\n    switch (resultMM)\n    {\n        case MA_MMSYSERR_NOERROR:       return MA_SUCCESS;\n        case MA_MMSYSERR_BADDEVICEID:   return MA_INVALID_ARGS;\n        case MA_MMSYSERR_INVALHANDLE:   return MA_INVALID_ARGS;\n        case MA_MMSYSERR_NOMEM:         return MA_OUT_OF_MEMORY;\n        case MA_MMSYSERR_INVALFLAG:     return MA_INVALID_ARGS;\n        case MA_MMSYSERR_INVALPARAM:    return MA_INVALID_ARGS;\n        case MA_MMSYSERR_HANDLEBUSY:    return MA_BUSY;\n        case MA_MMSYSERR_ERROR:         return MA_ERROR;\n        default:                        return MA_ERROR;\n    }\n}\n\nstatic char* ma_find_last_character(char* str, char ch)\n{\n    char* last;\n\n    if (str == NULL) {\n        return NULL;\n    }\n\n    last = NULL;\n    while (*str != '\\0') {\n        if (*str == ch) {\n            last = str;\n        }\n\n        str += 1;\n    }\n\n    return last;\n}\n\nstatic ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels)\n{\n    return periodSizeInFrames * ma_get_bytes_per_frame(format, channels);\n}\n\n\n/*\nOur own \"WAVECAPS\" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so\nwe can do things generically and typesafely. Names are being kept the same for consistency.\n*/\ntypedef struct\n{\n    CHAR szPname[MA_MAXPNAMELEN];\n    DWORD dwFormats;\n    WORD wChannels;\n    GUID NameGuid;\n} MA_WAVECAPSA;\n\nstatic ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)\n{\n    WORD bitsPerSample = 0;\n    DWORD sampleRate = 0;\n\n    if (pBitsPerSample) {\n        *pBitsPerSample = 0;\n    }\n    if (pSampleRate) {\n        *pSampleRate = 0;\n    }\n\n    if (channels == 1) {\n        bitsPerSample = 16;\n        if ((dwFormats & WAVE_FORMAT_48M16) != 0) {\n            sampleRate = 48000;\n        } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {\n            sampleRate = 44100;\n        } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {\n            sampleRate = 22050;\n        } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {\n            sampleRate = 11025;\n        } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {\n            sampleRate = 96000;\n        } else {\n            bitsPerSample = 8;\n            if ((dwFormats & WAVE_FORMAT_48M08) != 0) {\n                sampleRate = 48000;\n            } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {\n                sampleRate = 44100;\n            } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {\n                sampleRate = 22050;\n            } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {\n                sampleRate = 11025;\n            } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {\n                sampleRate = 96000;\n            } else {\n                return MA_FORMAT_NOT_SUPPORTED;\n            }\n        }\n    } else {\n        bitsPerSample = 16;\n        if ((dwFormats & WAVE_FORMAT_48S16) != 0) {\n            sampleRate = 48000;\n        } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {\n            sampleRate = 44100;\n        } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {\n            sampleRate = 22050;\n        } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {\n            sampleRate = 11025;\n        } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {\n            sampleRate = 96000;\n        } else {\n            bitsPerSample = 8;\n            if ((dwFormats & WAVE_FORMAT_48S08) != 0) {\n                sampleRate = 48000;\n            } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {\n                sampleRate = 44100;\n            } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {\n                sampleRate = 22050;\n            } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {\n                sampleRate = 11025;\n            } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {\n                sampleRate = 96000;\n            } else {\n                return MA_FORMAT_NOT_SUPPORTED;\n            }\n        }\n    }\n\n    if (pBitsPerSample) {\n        *pBitsPerSample = bitsPerSample;\n    }\n    if (pSampleRate) {\n        *pSampleRate = sampleRate;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF)\n{\n    ma_result result;\n\n    MA_ASSERT(pWF != NULL);\n\n    MA_ZERO_OBJECT(pWF);\n    pWF->cbSize     = sizeof(*pWF);\n    pWF->wFormatTag = WAVE_FORMAT_PCM;\n    pWF->nChannels  = (WORD)channels;\n    if (pWF->nChannels > 2) {\n        pWF->nChannels = 2;\n    }\n\n    result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pWF->nBlockAlign     = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);\n    pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)\n{\n    WORD bitsPerSample;\n    DWORD sampleRate;\n    ma_result result;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pCaps != NULL);\n    MA_ASSERT(pDeviceInfo != NULL);\n\n    /*\n    Name / Description\n\n    Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking\n    situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try\n    looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name.\n    */\n\n    /* Set the default to begin with. */\n    ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);\n\n    /*\n    Now try the registry. There's a few things to consider here:\n    - The name GUID can be null, in which we case we just need to stick to the original 31 characters.\n    - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.\n    - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The\n      problem, however is that WASAPI and DirectSound use \"<component> (<name>)\" format (such as \"Speakers (High Definition Audio)\"),\n      but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to\n      usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component\n      name, and then concatenate the name from the registry.\n    */\n    if (!ma_is_guid_null(&pCaps->NameGuid)) {\n        WCHAR guidStrW[256];\n        if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {\n            char guidStr[256];\n            char keyStr[1024];\n            HKEY hKey;\n\n            WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);\n\n            ma_strcpy_s(keyStr, sizeof(keyStr), \"SYSTEM\\\\CurrentControlSet\\\\Control\\\\MediaCategories\\\\\");\n            ma_strcat_s(keyStr, sizeof(keyStr), guidStr);\n\n            if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {\n                BYTE nameFromReg[512];\n                DWORD nameFromRegSize = sizeof(nameFromReg);\n                LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, \"Name\", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize);\n                ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);\n\n                if (resultWin32 == ERROR_SUCCESS) {\n                    /* We have the value from the registry, so now we need to construct the name string. */\n                    char name[1024];\n                    if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {\n                        char* nameBeg = ma_find_last_character(name, '(');\n                        if (nameBeg != NULL) {\n                            size_t leadingLen = (nameBeg - name);\n                            ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);\n\n                            /* The closing \")\", if it can fit. */\n                            if (leadingLen + nameFromRegSize < sizeof(name)-1) {\n                                ma_strcat_s(name, sizeof(name), \")\");\n                            }\n\n                            ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n\n    result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (bitsPerSample == 8) {\n        pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;\n    } else if (bitsPerSample == 16) {\n        pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;\n    } else if (bitsPerSample == 24) {\n        pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;\n    } else if (bitsPerSample == 32) {\n        pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;\n    } else {\n        return MA_FORMAT_NOT_SUPPORTED;\n    }\n    pDeviceInfo->nativeDataFormats[0].channels   = pCaps->wChannels;\n    pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;\n    pDeviceInfo->nativeDataFormats[0].flags      = 0;\n    pDeviceInfo->nativeDataFormatCount = 1;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)\n{\n    MA_WAVECAPSA caps;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pCaps != NULL);\n    MA_ASSERT(pDeviceInfo != NULL);\n\n    MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));\n    caps.dwFormats = pCaps->dwFormats;\n    caps.wChannels = pCaps->wChannels;\n    caps.NameGuid  = pCaps->NameGuid;\n    return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);\n}\n\nstatic ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)\n{\n    MA_WAVECAPSA caps;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pCaps != NULL);\n    MA_ASSERT(pDeviceInfo != NULL);\n\n    MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));\n    caps.dwFormats = pCaps->dwFormats;\n    caps.wChannels = pCaps->wChannels;\n    caps.NameGuid  = pCaps->NameGuid;\n    return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);\n}\n\n\nstatic ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    UINT playbackDeviceCount;\n    UINT captureDeviceCount;\n    UINT iPlaybackDevice;\n    UINT iCaptureDevice;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    /* Playback. */\n    playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();\n    for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {\n        MA_MMRESULT result;\n        MA_WAVEOUTCAPS2A caps;\n\n        MA_ZERO_OBJECT(&caps);\n\n        result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps));\n        if (result == MA_MMSYSERR_NOERROR) {\n            ma_device_info deviceInfo;\n\n            MA_ZERO_OBJECT(&deviceInfo);\n            deviceInfo.id.winmm = iPlaybackDevice;\n\n            /* The first enumerated device is the default device. */\n            if (iPlaybackDevice == 0) {\n                deviceInfo.isDefault = MA_TRUE;\n            }\n\n            if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {\n                ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n                if (cbResult == MA_FALSE) {\n                    return MA_SUCCESS; /* Enumeration was stopped. */\n                }\n            }\n        }\n    }\n\n    /* Capture. */\n    captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();\n    for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {\n        MA_MMRESULT result;\n        MA_WAVEINCAPS2A caps;\n\n        MA_ZERO_OBJECT(&caps);\n\n        result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps));\n        if (result == MA_MMSYSERR_NOERROR) {\n            ma_device_info deviceInfo;\n\n            MA_ZERO_OBJECT(&deviceInfo);\n            deviceInfo.id.winmm = iCaptureDevice;\n\n            /* The first enumerated device is the default device. */\n            if (iCaptureDevice == 0) {\n                deviceInfo.isDefault = MA_TRUE;\n            }\n\n            if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {\n                ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n                if (cbResult == MA_FALSE) {\n                    return MA_SUCCESS; /* Enumeration was stopped. */\n                }\n            }\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    UINT winMMDeviceID;\n\n    MA_ASSERT(pContext != NULL);\n\n    winMMDeviceID = 0;\n    if (pDeviceID != NULL) {\n        winMMDeviceID = (UINT)pDeviceID->winmm;\n    }\n\n    pDeviceInfo->id.winmm = winMMDeviceID;\n\n    /* The first ID is the default device. */\n    if (winMMDeviceID == 0) {\n        pDeviceInfo->isDefault = MA_TRUE;\n    }\n\n    if (deviceType == ma_device_type_playback) {\n        MA_MMRESULT result;\n        MA_WAVEOUTCAPS2A caps;\n\n        MA_ZERO_OBJECT(&caps);\n\n        result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps));\n        if (result == MA_MMSYSERR_NOERROR) {\n            return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);\n        }\n    } else {\n        MA_MMRESULT result;\n        MA_WAVEINCAPS2A caps;\n\n        MA_ZERO_OBJECT(&caps);\n\n        result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps));\n        if (result == MA_MMSYSERR_NOERROR) {\n            return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);\n        }\n    }\n\n    return MA_NO_DEVICE;\n}\n\n\nstatic ma_result ma_device_uninit__winmm(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);\n        CloseHandle((HANDLE)pDevice->winmm.hEventCapture);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);\n        ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);\n        CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);\n    }\n\n    ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);\n\n    MA_ZERO_OBJECT(&pDevice->winmm);   /* Safety. */\n\n    return MA_SUCCESS;\n}\n\nstatic ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)\n{\n    /* WinMM has a minimum period size of 40ms. */\n    ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate);\n    ma_uint32 periodSizeInFrames;\n\n    periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);\n    if (periodSizeInFrames < minPeriodSizeInFrames) {\n        periodSizeInFrames = minPeriodSizeInFrames;\n    }\n\n    return periodSizeInFrames;\n}\n\nstatic ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    const char* errorMsg = \"\";\n    ma_result errorCode = MA_ERROR;\n    ma_result result = MA_SUCCESS;\n    ma_uint32 heapSize;\n    UINT winMMDeviceIDPlayback = 0;\n    UINT winMMDeviceIDCapture  = 0;\n\n    MA_ASSERT(pDevice != NULL);\n\n    MA_ZERO_OBJECT(&pDevice->winmm);\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    /* No exlusive mode with WinMM. */\n    if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||\n        ((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode  == ma_share_mode_exclusive)) {\n        return MA_SHARE_MODE_NOT_SUPPORTED;\n    }\n\n    if (pDescriptorPlayback->pDeviceID != NULL) {\n        winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm;\n    }\n    if (pDescriptorCapture->pDeviceID != NULL) {\n        winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm;\n    }\n\n    /* The capture device needs to be initialized first. */\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        MA_WAVEINCAPSA caps;\n        MA_WAVEFORMATEX wf;\n        MA_MMRESULT resultMM;\n\n        /* We use an event to know when a new fragment needs to be enqueued. */\n        pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);\n        if (pDevice->winmm.hEventCapture == NULL) {\n            errorMsg = \"[WinMM] Failed to create event for fragment enqueing for the capture device.\", errorCode = ma_result_from_GetLastError(GetLastError());\n            goto on_error;\n        }\n\n        /* The format should be based on the device's actual format. */\n        if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) {\n            errorMsg = \"[WinMM] Failed to retrieve internal device caps.\", errorCode = MA_FORMAT_NOT_SUPPORTED;\n            goto on_error;\n        }\n\n        result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);\n        if (result != MA_SUCCESS) {\n            errorMsg = \"[WinMM] Could not find appropriate format for internal device.\", errorCode = result;\n            goto on_error;\n        }\n\n        resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC);\n        if (resultMM != MA_MMSYSERR_NOERROR) {\n            errorMsg = \"[WinMM] Failed to open capture device.\", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n            goto on_error;\n        }\n\n        pDescriptorCapture->format             = ma_format_from_WAVEFORMATEX(&wf);\n        pDescriptorCapture->channels           = wf.nChannels;\n        pDescriptorCapture->sampleRate         = wf.nSamplesPerSec;\n        ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);\n        pDescriptorCapture->periodCount        = pDescriptorCapture->periodCount;\n        pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        MA_WAVEOUTCAPSA caps;\n        MA_WAVEFORMATEX wf;\n        MA_MMRESULT resultMM;\n\n        /* We use an event to know when a new fragment needs to be enqueued. */\n        pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);\n        if (pDevice->winmm.hEventPlayback == NULL) {\n            errorMsg = \"[WinMM] Failed to create event for fragment enqueing for the playback device.\", errorCode = ma_result_from_GetLastError(GetLastError());\n            goto on_error;\n        }\n\n        /* The format should be based on the device's actual format. */\n        if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) {\n            errorMsg = \"[WinMM] Failed to retrieve internal device caps.\", errorCode = MA_FORMAT_NOT_SUPPORTED;\n            goto on_error;\n        }\n\n        result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);\n        if (result != MA_SUCCESS) {\n            errorMsg = \"[WinMM] Could not find appropriate format for internal device.\", errorCode = result;\n            goto on_error;\n        }\n\n        resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC);\n        if (resultMM != MA_MMSYSERR_NOERROR) {\n            errorMsg = \"[WinMM] Failed to open playback device.\", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n            goto on_error;\n        }\n\n        pDescriptorPlayback->format             = ma_format_from_WAVEFORMATEX(&wf);\n        pDescriptorPlayback->channels           = wf.nChannels;\n        pDescriptorPlayback->sampleRate         = wf.nSamplesPerSec;\n        ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);\n        pDescriptorPlayback->periodCount        = pDescriptorPlayback->periodCount;\n        pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);\n    }\n\n    /*\n    The heap allocated data is allocated like so:\n\n    [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]\n    */\n    heapSize = 0;\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels));\n    }\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels));\n    }\n\n    pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks);\n    if (pDevice->winmm._pHeapData == NULL) {\n        errorMsg = \"[WinMM] Failed to allocate memory for the intermediary buffer.\", errorCode = MA_OUT_OF_MEMORY;\n        goto on_error;\n    }\n\n    MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);\n\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        ma_uint32 iPeriod;\n\n        if (pConfig->deviceType == ma_device_type_capture) {\n            pDevice->winmm.pWAVEHDRCapture            = pDevice->winmm._pHeapData;\n            pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount));\n        } else {\n            pDevice->winmm.pWAVEHDRCapture            = pDevice->winmm._pHeapData;\n            pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount));\n        }\n\n        /* Prepare headers. */\n        for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {\n            ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels);\n\n            ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData         = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod));\n            ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes;\n            ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags        = 0L;\n            ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops        = 0L;\n            ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));\n\n            /*\n            The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named \"isLocked\". A value of 0 means\n            it's unlocked and available for writing. A value of 1 means it's locked.\n            */\n            ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;\n        }\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        ma_uint32 iPeriod;\n\n        if (pConfig->deviceType == ma_device_type_playback) {\n            pDevice->winmm.pWAVEHDRPlayback            = pDevice->winmm._pHeapData;\n            pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount);\n        } else {\n            pDevice->winmm.pWAVEHDRPlayback            = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount));\n            pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels));\n        }\n\n        /* Prepare headers. */\n        for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {\n            ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels);\n\n            ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData         = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod));\n            ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes;\n            ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags        = 0L;\n            ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops        = 0L;\n            ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR));\n\n            /*\n            The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named \"isLocked\". A value of 0 means\n            it's unlocked and available for writing. A value of 1 means it's locked.\n            */\n            ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;\n        }\n    }\n\n    return MA_SUCCESS;\n\non_error:\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        if (pDevice->winmm.pWAVEHDRCapture != NULL) {\n            ma_uint32 iPeriod;\n            for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {\n                ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));\n            }\n        }\n\n        ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        if (pDevice->winmm.pWAVEHDRCapture != NULL) {\n            ma_uint32 iPeriod;\n            for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {\n                ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR));\n            }\n        }\n\n        ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);\n    }\n\n    ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);\n\n    if (errorMsg != NULL && errorMsg[0] != '\\0') {\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"%s\", errorMsg);\n    }\n\n    return errorCode;\n}\n\nstatic ma_result ma_device_start__winmm(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        MA_MMRESULT resultMM;\n        MA_WAVEHDR* pWAVEHDR;\n        ma_uint32 iPeriod;\n\n        pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;\n\n        /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */\n        ResetEvent((HANDLE)pDevice->winmm.hEventCapture);\n\n        /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */\n        for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {\n            resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));\n            if (resultMM != MA_MMSYSERR_NOERROR) {\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WinMM] Failed to attach input buffers to capture device in preparation for capture.\");\n                return ma_result_from_MMRESULT(resultMM);\n            }\n\n            /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */\n            pWAVEHDR[iPeriod].dwUser = 1;   /* 1 = locked. */\n        }\n\n        /* Capture devices need to be explicitly started, unlike playback devices. */\n        resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);\n        if (resultMM != MA_MMSYSERR_NOERROR) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WinMM] Failed to start backend device.\");\n            return ma_result_from_MMRESULT(resultMM);\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__winmm(ma_device* pDevice)\n{\n    MA_MMRESULT resultMM;\n\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        if (pDevice->winmm.hDeviceCapture == NULL) {\n            return MA_INVALID_ARGS;\n        }\n\n        resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);\n        if (resultMM != MA_MMSYSERR_NOERROR) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, \"[WinMM] WARNING: Failed to reset capture device.\");\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ma_uint32 iPeriod;\n        MA_WAVEHDR* pWAVEHDR;\n\n        if (pDevice->winmm.hDevicePlayback == NULL) {\n            return MA_INVALID_ARGS;\n        }\n\n        /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */\n        pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;\n        for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) {\n            if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */\n                if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {\n                    break;  /* An error occurred so just abandon ship and stop the device without draining. */\n                }\n\n                pWAVEHDR[iPeriod].dwUser = 0;\n            }\n        }\n\n        resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);\n        if (resultMM != MA_MMSYSERR_NOERROR) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, \"[WinMM] WARNING: Failed to reset playback device.\");\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)\n{\n    ma_result result = MA_SUCCESS;\n    MA_MMRESULT resultMM;\n    ma_uint32 totalFramesWritten;\n    MA_WAVEHDR* pWAVEHDR;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(pPCMFrames != NULL);\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = 0;\n    }\n\n    pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;\n\n    /* Keep processing as much data as possible. */\n    totalFramesWritten = 0;\n    while (totalFramesWritten < frameCount) {\n        /* If the current header has some space available we need to write part of it. */\n        if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */\n            /*\n            This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to\n            write it out and move on to the next iteration.\n            */\n            ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);\n            ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;\n\n            ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));\n            const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);\n            void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);\n            MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);\n\n            pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;\n            totalFramesWritten += framesToCopy;\n\n            /* If we've consumed the buffer entirely we need to write it out to the device. */\n            if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {\n                pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1;            /* 1 = locked. */\n                pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */\n\n                /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */\n                ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);\n\n                /* The device will be started here. */\n                resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR));\n                if (resultMM != MA_MMSYSERR_NOERROR) {\n                    result = ma_result_from_MMRESULT(resultMM);\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WinMM] waveOutWrite() failed.\");\n                    break;\n                }\n\n                /* Make sure we move to the next header. */\n                pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;\n                pDevice->winmm.headerFramesConsumedPlayback = 0;\n            }\n\n            /* If at this point we have consumed the entire input buffer we can return. */\n            MA_ASSERT(totalFramesWritten <= frameCount);\n            if (totalFramesWritten == frameCount) {\n                break;\n            }\n\n            /* Getting here means there's more to process. */\n            continue;\n        }\n\n        /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */\n        if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {\n            result = MA_ERROR;\n            break;\n        }\n\n        /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */\n        if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) {\n            pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0;    /* 0 = unlocked (make it available for writing). */\n            pDevice->winmm.headerFramesConsumedPlayback = 0;\n        }\n\n        /* If the device has been stopped we need to break. */\n        if (ma_device_get_state(pDevice) != ma_device_state_started) {\n            break;\n        }\n    }\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = totalFramesWritten;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)\n{\n    ma_result result = MA_SUCCESS;\n    MA_MMRESULT resultMM;\n    ma_uint32 totalFramesRead;\n    MA_WAVEHDR* pWAVEHDR;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(pPCMFrames != NULL);\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;\n\n    /* Keep processing as much data as possible. */\n    totalFramesRead = 0;\n    while (totalFramesRead < frameCount) {\n        /* If the current header has some space available we need to write part of it. */\n        if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */\n            /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */\n            ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);\n            ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;\n\n            ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));\n            const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);\n            void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);\n            MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);\n\n            pDevice->winmm.headerFramesConsumedCapture += framesToCopy;\n            totalFramesRead += framesToCopy;\n\n            /* If we've consumed the buffer entirely we need to add it back to the device. */\n            if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {\n                pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1;            /* 1 = locked. */\n                pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */\n\n                /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */\n                ResetEvent((HANDLE)pDevice->winmm.hEventCapture);\n\n                /* The device will be started here. */\n                resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR));\n                if (resultMM != MA_MMSYSERR_NOERROR) {\n                    result = ma_result_from_MMRESULT(resultMM);\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[WinMM] waveInAddBuffer() failed.\");\n                    break;\n                }\n\n                /* Make sure we move to the next header. */\n                pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;\n                pDevice->winmm.headerFramesConsumedCapture = 0;\n            }\n\n            /* If at this point we have filled the entire input buffer we can return. */\n            MA_ASSERT(totalFramesRead <= frameCount);\n            if (totalFramesRead == frameCount) {\n                break;\n            }\n\n            /* Getting here means there's more to process. */\n            continue;\n        }\n\n        /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */\n        if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {\n            result = MA_ERROR;\n            break;\n        }\n\n        /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */\n        if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) {\n            pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0;    /* 0 = unlocked (make it available for reading). */\n            pDevice->winmm.headerFramesConsumedCapture = 0;\n        }\n\n        /* If the device has been stopped we need to break. */\n        if (ma_device_get_state(pDevice) != ma_device_state_started) {\n            break;\n        }\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = totalFramesRead;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_context_uninit__winmm(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_winmm);\n\n    ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM);\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n    MA_ASSERT(pContext != NULL);\n\n    (void)pConfig;\n\n    pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), \"winmm.dll\");\n    if (pContext->winmm.hWinMM == NULL) {\n        return MA_NO_BACKEND;\n    }\n\n    pContext->winmm.waveOutGetNumDevs      = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveOutGetNumDevs\");\n    pContext->winmm.waveOutGetDevCapsA     = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveOutGetDevCapsA\");\n    pContext->winmm.waveOutOpen            = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveOutOpen\");\n    pContext->winmm.waveOutClose           = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveOutClose\");\n    pContext->winmm.waveOutPrepareHeader   = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveOutPrepareHeader\");\n    pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveOutUnprepareHeader\");\n    pContext->winmm.waveOutWrite           = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveOutWrite\");\n    pContext->winmm.waveOutReset           = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveOutReset\");\n    pContext->winmm.waveInGetNumDevs       = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveInGetNumDevs\");\n    pContext->winmm.waveInGetDevCapsA      = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveInGetDevCapsA\");\n    pContext->winmm.waveInOpen             = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveInOpen\");\n    pContext->winmm.waveInClose            = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveInClose\");\n    pContext->winmm.waveInPrepareHeader    = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveInPrepareHeader\");\n    pContext->winmm.waveInUnprepareHeader  = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveInUnprepareHeader\");\n    pContext->winmm.waveInAddBuffer        = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveInAddBuffer\");\n    pContext->winmm.waveInStart            = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveInStart\");\n    pContext->winmm.waveInReset            = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, \"waveInReset\");\n\n    pCallbacks->onContextInit             = ma_context_init__winmm;\n    pCallbacks->onContextUninit           = ma_context_uninit__winmm;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__winmm;\n    pCallbacks->onDeviceInit              = ma_device_init__winmm;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__winmm;\n    pCallbacks->onDeviceStart             = ma_device_start__winmm;\n    pCallbacks->onDeviceStop              = ma_device_stop__winmm;\n    pCallbacks->onDeviceRead              = ma_device_read__winmm;\n    pCallbacks->onDeviceWrite             = ma_device_write__winmm;\n    pCallbacks->onDeviceDataLoop          = NULL;   /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */\n\n    return MA_SUCCESS;\n}\n#endif\n\n\n\n\n/******************************************************************************\n\nALSA Backend\n\n******************************************************************************/\n#ifdef MA_HAS_ALSA\n\n#include <poll.h>           /* poll(), struct pollfd */\n#include <sys/eventfd.h>    /* eventfd() */\n\n#ifdef MA_NO_RUNTIME_LINKING\n\n/* asoundlib.h marks some functions with \"inline\" which isn't always supported. Need to emulate it. */\n#if !defined(__cplusplus)\n    #if defined(__STRICT_ANSI__)\n        #if !defined(inline)\n            #define inline __inline__ __attribute__((always_inline))\n            #define MA_INLINE_DEFINED\n        #endif\n    #endif\n#endif\n#include <alsa/asoundlib.h>\n#if defined(MA_INLINE_DEFINED)\n    #undef inline\n    #undef MA_INLINE_DEFINED\n#endif\n\ntypedef snd_pcm_uframes_t                       ma_snd_pcm_uframes_t;\ntypedef snd_pcm_sframes_t                       ma_snd_pcm_sframes_t;\ntypedef snd_pcm_stream_t                        ma_snd_pcm_stream_t;\ntypedef snd_pcm_format_t                        ma_snd_pcm_format_t;\ntypedef snd_pcm_access_t                        ma_snd_pcm_access_t;\ntypedef snd_pcm_t                               ma_snd_pcm_t;\ntypedef snd_pcm_hw_params_t                     ma_snd_pcm_hw_params_t;\ntypedef snd_pcm_sw_params_t                     ma_snd_pcm_sw_params_t;\ntypedef snd_pcm_format_mask_t                   ma_snd_pcm_format_mask_t;\ntypedef snd_pcm_info_t                          ma_snd_pcm_info_t;\ntypedef snd_pcm_channel_area_t                  ma_snd_pcm_channel_area_t;\ntypedef snd_pcm_chmap_t                         ma_snd_pcm_chmap_t;\ntypedef snd_pcm_state_t                         ma_snd_pcm_state_t;\n\n/* snd_pcm_stream_t */\n#define MA_SND_PCM_STREAM_PLAYBACK              SND_PCM_STREAM_PLAYBACK\n#define MA_SND_PCM_STREAM_CAPTURE               SND_PCM_STREAM_CAPTURE\n\n/* snd_pcm_format_t */\n#define MA_SND_PCM_FORMAT_UNKNOWN               SND_PCM_FORMAT_UNKNOWN\n#define MA_SND_PCM_FORMAT_U8                    SND_PCM_FORMAT_U8\n#define MA_SND_PCM_FORMAT_S16_LE                SND_PCM_FORMAT_S16_LE\n#define MA_SND_PCM_FORMAT_S16_BE                SND_PCM_FORMAT_S16_BE\n#define MA_SND_PCM_FORMAT_S24_LE                SND_PCM_FORMAT_S24_LE\n#define MA_SND_PCM_FORMAT_S24_BE                SND_PCM_FORMAT_S24_BE\n#define MA_SND_PCM_FORMAT_S32_LE                SND_PCM_FORMAT_S32_LE\n#define MA_SND_PCM_FORMAT_S32_BE                SND_PCM_FORMAT_S32_BE\n#define MA_SND_PCM_FORMAT_FLOAT_LE              SND_PCM_FORMAT_FLOAT_LE\n#define MA_SND_PCM_FORMAT_FLOAT_BE              SND_PCM_FORMAT_FLOAT_BE\n#define MA_SND_PCM_FORMAT_FLOAT64_LE            SND_PCM_FORMAT_FLOAT64_LE\n#define MA_SND_PCM_FORMAT_FLOAT64_BE            SND_PCM_FORMAT_FLOAT64_BE\n#define MA_SND_PCM_FORMAT_MU_LAW                SND_PCM_FORMAT_MU_LAW\n#define MA_SND_PCM_FORMAT_A_LAW                 SND_PCM_FORMAT_A_LAW\n#define MA_SND_PCM_FORMAT_S24_3LE               SND_PCM_FORMAT_S24_3LE\n#define MA_SND_PCM_FORMAT_S24_3BE               SND_PCM_FORMAT_S24_3BE\n\n/* ma_snd_pcm_access_t */\n#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED      SND_PCM_ACCESS_MMAP_INTERLEAVED\n#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED   SND_PCM_ACCESS_MMAP_NONINTERLEAVED\n#define MA_SND_PCM_ACCESS_MMAP_COMPLEX          SND_PCM_ACCESS_MMAP_COMPLEX\n#define MA_SND_PCM_ACCESS_RW_INTERLEAVED        SND_PCM_ACCESS_RW_INTERLEAVED\n#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED     SND_PCM_ACCESS_RW_NONINTERLEAVED\n\n/* Channel positions. */\n#define MA_SND_CHMAP_UNKNOWN                    SND_CHMAP_UNKNOWN\n#define MA_SND_CHMAP_NA                         SND_CHMAP_NA\n#define MA_SND_CHMAP_MONO                       SND_CHMAP_MONO\n#define MA_SND_CHMAP_FL                         SND_CHMAP_FL\n#define MA_SND_CHMAP_FR                         SND_CHMAP_FR\n#define MA_SND_CHMAP_RL                         SND_CHMAP_RL\n#define MA_SND_CHMAP_RR                         SND_CHMAP_RR\n#define MA_SND_CHMAP_FC                         SND_CHMAP_FC\n#define MA_SND_CHMAP_LFE                        SND_CHMAP_LFE\n#define MA_SND_CHMAP_SL                         SND_CHMAP_SL\n#define MA_SND_CHMAP_SR                         SND_CHMAP_SR\n#define MA_SND_CHMAP_RC                         SND_CHMAP_RC\n#define MA_SND_CHMAP_FLC                        SND_CHMAP_FLC\n#define MA_SND_CHMAP_FRC                        SND_CHMAP_FRC\n#define MA_SND_CHMAP_RLC                        SND_CHMAP_RLC\n#define MA_SND_CHMAP_RRC                        SND_CHMAP_RRC\n#define MA_SND_CHMAP_FLW                        SND_CHMAP_FLW\n#define MA_SND_CHMAP_FRW                        SND_CHMAP_FRW\n#define MA_SND_CHMAP_FLH                        SND_CHMAP_FLH\n#define MA_SND_CHMAP_FCH                        SND_CHMAP_FCH\n#define MA_SND_CHMAP_FRH                        SND_CHMAP_FRH\n#define MA_SND_CHMAP_TC                         SND_CHMAP_TC\n#define MA_SND_CHMAP_TFL                        SND_CHMAP_TFL\n#define MA_SND_CHMAP_TFR                        SND_CHMAP_TFR\n#define MA_SND_CHMAP_TFC                        SND_CHMAP_TFC\n#define MA_SND_CHMAP_TRL                        SND_CHMAP_TRL\n#define MA_SND_CHMAP_TRR                        SND_CHMAP_TRR\n#define MA_SND_CHMAP_TRC                        SND_CHMAP_TRC\n#define MA_SND_CHMAP_TFLC                       SND_CHMAP_TFLC\n#define MA_SND_CHMAP_TFRC                       SND_CHMAP_TFRC\n#define MA_SND_CHMAP_TSL                        SND_CHMAP_TSL\n#define MA_SND_CHMAP_TSR                        SND_CHMAP_TSR\n#define MA_SND_CHMAP_LLFE                       SND_CHMAP_LLFE\n#define MA_SND_CHMAP_RLFE                       SND_CHMAP_RLFE\n#define MA_SND_CHMAP_BC                         SND_CHMAP_BC\n#define MA_SND_CHMAP_BLC                        SND_CHMAP_BLC\n#define MA_SND_CHMAP_BRC                        SND_CHMAP_BRC\n\n/* Open mode flags. */\n#define MA_SND_PCM_NO_AUTO_RESAMPLE             SND_PCM_NO_AUTO_RESAMPLE\n#define MA_SND_PCM_NO_AUTO_CHANNELS             SND_PCM_NO_AUTO_CHANNELS\n#define MA_SND_PCM_NO_AUTO_FORMAT               SND_PCM_NO_AUTO_FORMAT\n#else\n#include <errno.h>  /* For EPIPE, etc. */\ntypedef unsigned long                           ma_snd_pcm_uframes_t;\ntypedef long                                    ma_snd_pcm_sframes_t;\ntypedef int                                     ma_snd_pcm_stream_t;\ntypedef int                                     ma_snd_pcm_format_t;\ntypedef int                                     ma_snd_pcm_access_t;\ntypedef int                                     ma_snd_pcm_state_t;\ntypedef struct ma_snd_pcm_t                     ma_snd_pcm_t;\ntypedef struct ma_snd_pcm_hw_params_t           ma_snd_pcm_hw_params_t;\ntypedef struct ma_snd_pcm_sw_params_t           ma_snd_pcm_sw_params_t;\ntypedef struct ma_snd_pcm_format_mask_t         ma_snd_pcm_format_mask_t;\ntypedef struct ma_snd_pcm_info_t                ma_snd_pcm_info_t;\ntypedef struct\n{\n    void* addr;\n    unsigned int first;\n    unsigned int step;\n} ma_snd_pcm_channel_area_t;\ntypedef struct\n{\n    unsigned int channels;\n    unsigned int pos[1];\n} ma_snd_pcm_chmap_t;\n\n/* snd_pcm_state_t */\n#define MA_SND_PCM_STATE_OPEN                  0\n#define MA_SND_PCM_STATE_SETUP                 1\n#define MA_SND_PCM_STATE_PREPARED              2\n#define MA_SND_PCM_STATE_RUNNING               3\n#define MA_SND_PCM_STATE_XRUN                  4\n#define MA_SND_PCM_STATE_DRAINING              5\n#define MA_SND_PCM_STATE_PAUSED                6\n#define MA_SND_PCM_STATE_SUSPENDED             7\n#define MA_SND_PCM_STATE_DISCONNECTED          8\n\n/* snd_pcm_stream_t */\n#define MA_SND_PCM_STREAM_PLAYBACK             0\n#define MA_SND_PCM_STREAM_CAPTURE              1\n\n/* snd_pcm_format_t */\n#define MA_SND_PCM_FORMAT_UNKNOWN              -1\n#define MA_SND_PCM_FORMAT_U8                   1\n#define MA_SND_PCM_FORMAT_S16_LE               2\n#define MA_SND_PCM_FORMAT_S16_BE               3\n#define MA_SND_PCM_FORMAT_S24_LE               6\n#define MA_SND_PCM_FORMAT_S24_BE               7\n#define MA_SND_PCM_FORMAT_S32_LE               10\n#define MA_SND_PCM_FORMAT_S32_BE               11\n#define MA_SND_PCM_FORMAT_FLOAT_LE             14\n#define MA_SND_PCM_FORMAT_FLOAT_BE             15\n#define MA_SND_PCM_FORMAT_FLOAT64_LE           16\n#define MA_SND_PCM_FORMAT_FLOAT64_BE           17\n#define MA_SND_PCM_FORMAT_MU_LAW               20\n#define MA_SND_PCM_FORMAT_A_LAW                21\n#define MA_SND_PCM_FORMAT_S24_3LE              32\n#define MA_SND_PCM_FORMAT_S24_3BE              33\n\n/* snd_pcm_access_t */\n#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED     0\n#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED  1\n#define MA_SND_PCM_ACCESS_MMAP_COMPLEX         2\n#define MA_SND_PCM_ACCESS_RW_INTERLEAVED       3\n#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED    4\n\n/* Channel positions. */\n#define MA_SND_CHMAP_UNKNOWN                   0\n#define MA_SND_CHMAP_NA                        1\n#define MA_SND_CHMAP_MONO                      2\n#define MA_SND_CHMAP_FL                        3\n#define MA_SND_CHMAP_FR                        4\n#define MA_SND_CHMAP_RL                        5\n#define MA_SND_CHMAP_RR                        6\n#define MA_SND_CHMAP_FC                        7\n#define MA_SND_CHMAP_LFE                       8\n#define MA_SND_CHMAP_SL                        9\n#define MA_SND_CHMAP_SR                        10\n#define MA_SND_CHMAP_RC                        11\n#define MA_SND_CHMAP_FLC                       12\n#define MA_SND_CHMAP_FRC                       13\n#define MA_SND_CHMAP_RLC                       14\n#define MA_SND_CHMAP_RRC                       15\n#define MA_SND_CHMAP_FLW                       16\n#define MA_SND_CHMAP_FRW                       17\n#define MA_SND_CHMAP_FLH                       18\n#define MA_SND_CHMAP_FCH                       19\n#define MA_SND_CHMAP_FRH                       20\n#define MA_SND_CHMAP_TC                        21\n#define MA_SND_CHMAP_TFL                       22\n#define MA_SND_CHMAP_TFR                       23\n#define MA_SND_CHMAP_TFC                       24\n#define MA_SND_CHMAP_TRL                       25\n#define MA_SND_CHMAP_TRR                       26\n#define MA_SND_CHMAP_TRC                       27\n#define MA_SND_CHMAP_TFLC                      28\n#define MA_SND_CHMAP_TFRC                      29\n#define MA_SND_CHMAP_TSL                       30\n#define MA_SND_CHMAP_TSR                       31\n#define MA_SND_CHMAP_LLFE                      32\n#define MA_SND_CHMAP_RLFE                      33\n#define MA_SND_CHMAP_BC                        34\n#define MA_SND_CHMAP_BLC                       35\n#define MA_SND_CHMAP_BRC                       36\n\n/* Open mode flags. */\n#define MA_SND_PCM_NO_AUTO_RESAMPLE            0x00010000\n#define MA_SND_PCM_NO_AUTO_CHANNELS            0x00020000\n#define MA_SND_PCM_NO_AUTO_FORMAT              0x00040000\n#endif\n\ntypedef int                  (* ma_snd_pcm_open_proc)                          (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);\ntypedef int                  (* ma_snd_pcm_close_proc)                         (ma_snd_pcm_t *pcm);\ntypedef size_t               (* ma_snd_pcm_hw_params_sizeof_proc)              (void);\ntypedef int                  (* ma_snd_pcm_hw_params_any_proc)                 (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);\ntypedef int                  (* ma_snd_pcm_hw_params_set_format_proc)          (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val);\ntypedef int                  (* ma_snd_pcm_hw_params_set_format_first_proc)    (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);\ntypedef void                 (* ma_snd_pcm_hw_params_get_format_mask_proc)     (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask);\ntypedef int                  (* ma_snd_pcm_hw_params_set_channels_proc)        (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);\ntypedef int                  (* ma_snd_pcm_hw_params_set_channels_near_proc)   (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val);\ntypedef int                  (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum);\ntypedef int                  (* ma_snd_pcm_hw_params_set_rate_resample_proc)   (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);\ntypedef int                  (* ma_snd_pcm_hw_params_set_rate_proc)            (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir);\ntypedef int                  (* ma_snd_pcm_hw_params_set_rate_near_proc)       (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);\ntypedef int                  (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);\ntypedef int                  (* ma_snd_pcm_hw_params_set_periods_near_proc)    (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);\ntypedef int                  (* ma_snd_pcm_hw_params_set_access_proc)          (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access);\ntypedef int                  (* ma_snd_pcm_hw_params_get_format_proc)          (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);\ntypedef int                  (* ma_snd_pcm_hw_params_get_channels_proc)        (const ma_snd_pcm_hw_params_t *params, unsigned int *val);\ntypedef int                  (* ma_snd_pcm_hw_params_get_channels_min_proc)    (const ma_snd_pcm_hw_params_t *params, unsigned int *val);\ntypedef int                  (* ma_snd_pcm_hw_params_get_channels_max_proc)    (const ma_snd_pcm_hw_params_t *params, unsigned int *val);\ntypedef int                  (* ma_snd_pcm_hw_params_get_rate_proc)            (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);\ntypedef int                  (* ma_snd_pcm_hw_params_get_rate_min_proc)        (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);\ntypedef int                  (* ma_snd_pcm_hw_params_get_rate_max_proc)        (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);\ntypedef int                  (* ma_snd_pcm_hw_params_get_buffer_size_proc)     (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);\ntypedef int                  (* ma_snd_pcm_hw_params_get_periods_proc)         (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);\ntypedef int                  (* ma_snd_pcm_hw_params_get_access_proc)          (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);\ntypedef int                  (* ma_snd_pcm_hw_params_test_format_proc)         (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val);\ntypedef int                  (* ma_snd_pcm_hw_params_test_channels_proc)       (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);\ntypedef int                  (* ma_snd_pcm_hw_params_test_rate_proc)           (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir);\ntypedef int                  (* ma_snd_pcm_hw_params_proc)                     (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);\ntypedef size_t               (* ma_snd_pcm_sw_params_sizeof_proc)              (void);\ntypedef int                  (* ma_snd_pcm_sw_params_current_proc)             (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);\ntypedef int                  (* ma_snd_pcm_sw_params_get_boundary_proc)        (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);\ntypedef int                  (* ma_snd_pcm_sw_params_set_avail_min_proc)       (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);\ntypedef int                  (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);\ntypedef int                  (* ma_snd_pcm_sw_params_set_stop_threshold_proc)  (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);\ntypedef int                  (* ma_snd_pcm_sw_params_proc)                     (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);\ntypedef size_t               (* ma_snd_pcm_format_mask_sizeof_proc)            (void);\ntypedef int                  (* ma_snd_pcm_format_mask_test_proc)              (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);\ntypedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc)                     (ma_snd_pcm_t *pcm);\ntypedef ma_snd_pcm_state_t   (* ma_snd_pcm_state_proc)                         (ma_snd_pcm_t *pcm);\ntypedef int                  (* ma_snd_pcm_prepare_proc)                       (ma_snd_pcm_t *pcm);\ntypedef int                  (* ma_snd_pcm_start_proc)                         (ma_snd_pcm_t *pcm);\ntypedef int                  (* ma_snd_pcm_drop_proc)                          (ma_snd_pcm_t *pcm);\ntypedef int                  (* ma_snd_pcm_drain_proc)                         (ma_snd_pcm_t *pcm);\ntypedef int                  (* ma_snd_pcm_reset_proc)                         (ma_snd_pcm_t *pcm);\ntypedef int                  (* ma_snd_device_name_hint_proc)                  (int card, const char *iface, void ***hints);\ntypedef char *               (* ma_snd_device_name_get_hint_proc)              (const void *hint, const char *id);\ntypedef int                  (* ma_snd_card_get_index_proc)                    (const char *name);\ntypedef int                  (* ma_snd_device_name_free_hint_proc)             (void **hints);\ntypedef int                  (* ma_snd_pcm_mmap_begin_proc)                    (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames);\ntypedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc)                   (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames);\ntypedef int                  (* ma_snd_pcm_recover_proc)                       (ma_snd_pcm_t *pcm, int err, int silent);\ntypedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc)                         (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);\ntypedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc)                        (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size);\ntypedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc)                         (ma_snd_pcm_t *pcm);\ntypedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc)                  (ma_snd_pcm_t *pcm);\ntypedef int                  (* ma_snd_pcm_wait_proc)                          (ma_snd_pcm_t *pcm, int timeout);\ntypedef int                  (* ma_snd_pcm_nonblock_proc)                      (ma_snd_pcm_t *pcm, int nonblock);\ntypedef int                  (* ma_snd_pcm_info_proc)                          (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);\ntypedef size_t               (* ma_snd_pcm_info_sizeof_proc)                   (void);\ntypedef const char*          (* ma_snd_pcm_info_get_name_proc)                 (const ma_snd_pcm_info_t* info);\ntypedef int                  (* ma_snd_pcm_poll_descriptors_proc)              (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);\ntypedef int                  (* ma_snd_pcm_poll_descriptors_count_proc)        (ma_snd_pcm_t *pcm);\ntypedef int                  (* ma_snd_pcm_poll_descriptors_revents_proc)      (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);\ntypedef int                  (* ma_snd_config_update_free_global_proc)         (void);\n\n/* This array specifies each of the common devices that can be used for both playback and capture. */\nstatic const char* g_maCommonDeviceNamesALSA[] = {\n    \"default\",\n    \"null\",\n    \"pulse\",\n    \"jack\"\n};\n\n/* This array allows us to blacklist specific playback devices. */\nstatic const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {\n    \"\"\n};\n\n/* This array allows us to blacklist specific capture devices. */\nstatic const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {\n    \"\"\n};\n\n\nstatic ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)\n{\n    ma_snd_pcm_format_t ALSAFormats[] = {\n        MA_SND_PCM_FORMAT_UNKNOWN,     /* ma_format_unknown */\n        MA_SND_PCM_FORMAT_U8,          /* ma_format_u8 */\n        MA_SND_PCM_FORMAT_S16_LE,      /* ma_format_s16 */\n        MA_SND_PCM_FORMAT_S24_3LE,     /* ma_format_s24 */\n        MA_SND_PCM_FORMAT_S32_LE,      /* ma_format_s32 */\n        MA_SND_PCM_FORMAT_FLOAT_LE     /* ma_format_f32 */\n    };\n\n    if (ma_is_big_endian()) {\n        ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;\n        ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;\n        ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;\n        ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;\n        ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;\n        ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;\n    }\n\n    return ALSAFormats[format];\n}\n\nstatic ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)\n{\n    if (ma_is_little_endian()) {\n        switch (formatALSA) {\n            case MA_SND_PCM_FORMAT_S16_LE:   return ma_format_s16;\n            case MA_SND_PCM_FORMAT_S24_3LE:  return ma_format_s24;\n            case MA_SND_PCM_FORMAT_S32_LE:   return ma_format_s32;\n            case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;\n            default: break;\n        }\n    } else {\n        switch (formatALSA) {\n            case MA_SND_PCM_FORMAT_S16_BE:   return ma_format_s16;\n            case MA_SND_PCM_FORMAT_S24_3BE:  return ma_format_s24;\n            case MA_SND_PCM_FORMAT_S32_BE:   return ma_format_s32;\n            case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;\n            default: break;\n        }\n    }\n\n    /* Endian agnostic. */\n    switch (formatALSA) {\n        case MA_SND_PCM_FORMAT_U8: return ma_format_u8;\n        default: return ma_format_unknown;\n    }\n}\n\nstatic ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)\n{\n    switch (alsaChannelPos)\n    {\n        case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;\n        case MA_SND_CHMAP_FL:   return MA_CHANNEL_FRONT_LEFT;\n        case MA_SND_CHMAP_FR:   return MA_CHANNEL_FRONT_RIGHT;\n        case MA_SND_CHMAP_RL:   return MA_CHANNEL_BACK_LEFT;\n        case MA_SND_CHMAP_RR:   return MA_CHANNEL_BACK_RIGHT;\n        case MA_SND_CHMAP_FC:   return MA_CHANNEL_FRONT_CENTER;\n        case MA_SND_CHMAP_LFE:  return MA_CHANNEL_LFE;\n        case MA_SND_CHMAP_SL:   return MA_CHANNEL_SIDE_LEFT;\n        case MA_SND_CHMAP_SR:   return MA_CHANNEL_SIDE_RIGHT;\n        case MA_SND_CHMAP_RC:   return MA_CHANNEL_BACK_CENTER;\n        case MA_SND_CHMAP_FLC:  return MA_CHANNEL_FRONT_LEFT_CENTER;\n        case MA_SND_CHMAP_FRC:  return MA_CHANNEL_FRONT_RIGHT_CENTER;\n        case MA_SND_CHMAP_RLC:  return 0;\n        case MA_SND_CHMAP_RRC:  return 0;\n        case MA_SND_CHMAP_FLW:  return 0;\n        case MA_SND_CHMAP_FRW:  return 0;\n        case MA_SND_CHMAP_FLH:  return 0;\n        case MA_SND_CHMAP_FCH:  return 0;\n        case MA_SND_CHMAP_FRH:  return 0;\n        case MA_SND_CHMAP_TC:   return MA_CHANNEL_TOP_CENTER;\n        case MA_SND_CHMAP_TFL:  return MA_CHANNEL_TOP_FRONT_LEFT;\n        case MA_SND_CHMAP_TFR:  return MA_CHANNEL_TOP_FRONT_RIGHT;\n        case MA_SND_CHMAP_TFC:  return MA_CHANNEL_TOP_FRONT_CENTER;\n        case MA_SND_CHMAP_TRL:  return MA_CHANNEL_TOP_BACK_LEFT;\n        case MA_SND_CHMAP_TRR:  return MA_CHANNEL_TOP_BACK_RIGHT;\n        case MA_SND_CHMAP_TRC:  return MA_CHANNEL_TOP_BACK_CENTER;\n        default: break;\n    }\n\n    return 0;\n}\n\nstatic ma_bool32 ma_is_common_device_name__alsa(const char* name)\n{\n    size_t iName;\n    for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {\n        if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {\n            return MA_TRUE;\n        }\n    }\n\n    return MA_FALSE;\n}\n\n\nstatic ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)\n{\n    size_t iName;\n    for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {\n        if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {\n            return MA_TRUE;\n        }\n    }\n\n    return MA_FALSE;\n}\n\nstatic ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)\n{\n    size_t iName;\n    for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {\n        if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {\n            return MA_TRUE;\n        }\n    }\n\n    return MA_FALSE;\n}\n\nstatic ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)\n{\n    if (deviceType == ma_device_type_playback) {\n        return ma_is_playback_device_blacklisted__alsa(name);\n    } else {\n        return ma_is_capture_device_blacklisted__alsa(name);\n    }\n}\n\n\nstatic const char* ma_find_char(const char* str, char c, int* index)\n{\n    int i = 0;\n    for (;;) {\n        if (str[i] == '\\0') {\n            if (index) *index = -1;\n            return NULL;\n        }\n\n        if (str[i] == c) {\n            if (index) *index = i;\n            return str + i;\n        }\n\n        i += 1;\n    }\n\n    /* Should never get here, but treat it as though the character was not found to make me feel better inside. */\n    if (index) *index = -1;\n    return NULL;\n}\n\nstatic ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)\n{\n    /* This function is just checking whether or not hwid is in \"hw:%d,%d\" format. */\n\n    int commaPos;\n    const char* dev;\n    int i;\n\n    if (hwid == NULL) {\n        return MA_FALSE;\n    }\n\n    if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {\n        return MA_FALSE;\n    }\n\n    hwid += 3;\n\n    dev = ma_find_char(hwid, ',', &commaPos);\n    if (dev == NULL) {\n        return MA_FALSE;\n    } else {\n        dev += 1;   /* Skip past the \",\". */\n    }\n\n    /* Check if the part between the \":\" and the \",\" contains only numbers. If not, return false. */\n    for (i = 0; i < commaPos; ++i) {\n        if (hwid[i] < '0' || hwid[i] > '9') {\n            return MA_FALSE;\n        }\n    }\n\n    /* Check if everything after the \",\" is numeric. If not, return false. */\n    i = 0;\n    while (dev[i] != '\\0') {\n        if (dev[i] < '0' || dev[i] > '9') {\n            return MA_FALSE;\n        }\n        i += 1;\n    }\n\n    return MA_TRUE;\n}\n\nstatic int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src)  /* Returns 0 on success, non-0 on error. */\n{\n    /* src should look something like this: \"hw:CARD=I82801AAICH,DEV=0\" */\n\n    int colonPos;\n    int commaPos;\n    char card[256];\n    const char* dev;\n    int cardIndex;\n\n    if (dst == NULL) {\n        return -1;\n    }\n    if (dstSize < 7) {\n        return -1;     /* Absolute minimum size of the output buffer is 7 bytes. */\n    }\n\n    *dst = '\\0';    /* Safety. */\n    if (src == NULL) {\n        return -1;\n    }\n\n    /* If the input name is already in \"hw:%d,%d\" format, just return that verbatim. */\n    if (ma_is_device_name_in_hw_format__alsa(src)) {\n        return ma_strcpy_s(dst, dstSize, src);\n    }\n\n    src = ma_find_char(src, ':', &colonPos);\n    if (src == NULL) {\n        return -1;  /* Couldn't find a colon */\n    }\n\n    dev = ma_find_char(src, ',', &commaPos);\n    if (dev == NULL) {\n        dev = \"0\";\n        ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1);   /* +6 = \":CARD=\" */\n    } else {\n        dev = dev + 5;  /* +5 = \",DEV=\" */\n        ma_strncpy_s(card, sizeof(card), src+6, commaPos-6);   /* +6 = \":CARD=\" */\n    }\n\n    cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);\n    if (cardIndex < 0) {\n        return -2;  /* Failed to retrieve the card index. */\n    }\n\n\n    /* Construction. */\n    dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';\n    if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {\n        return -3;\n    }\n    if (ma_strcat_s(dst, dstSize, \",\") != 0) {\n        return -3;\n    }\n    if (ma_strcat_s(dst, dstSize, dev) != 0) {\n        return -3;\n    }\n\n    return 0;\n}\n\nstatic ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)\n{\n    ma_uint32 i;\n\n    MA_ASSERT(pHWID != NULL);\n\n    for (i = 0; i < count; ++i) {\n        if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {\n            return MA_TRUE;\n        }\n    }\n\n    return MA_FALSE;\n}\n\n\nstatic ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM)\n{\n    ma_snd_pcm_t* pPCM;\n    ma_snd_pcm_stream_t stream;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(ppPCM != NULL);\n\n    *ppPCM = NULL;\n    pPCM = NULL;\n\n    stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;\n\n    if (pDeviceID == NULL) {\n        ma_bool32 isDeviceOpen;\n        size_t i;\n\n        /*\n        We're opening the default device. I don't know if trying anything other than \"default\" is necessary, but it makes\n        me feel better to try as hard as we can get to get _something_ working.\n        */\n        const char* defaultDeviceNames[] = {\n            \"default\",\n            NULL,\n            NULL,\n            NULL,\n            NULL,\n            NULL,\n            NULL\n        };\n\n        if (shareMode == ma_share_mode_exclusive) {\n            defaultDeviceNames[1] = \"hw\";\n            defaultDeviceNames[2] = \"hw:0\";\n            defaultDeviceNames[3] = \"hw:0,0\";\n        } else {\n            if (deviceType == ma_device_type_playback) {\n                defaultDeviceNames[1] = \"dmix\";\n                defaultDeviceNames[2] = \"dmix:0\";\n                defaultDeviceNames[3] = \"dmix:0,0\";\n            } else {\n                defaultDeviceNames[1] = \"dsnoop\";\n                defaultDeviceNames[2] = \"dsnoop:0\";\n                defaultDeviceNames[3] = \"dsnoop:0,0\";\n            }\n            defaultDeviceNames[4] = \"hw\";\n            defaultDeviceNames[5] = \"hw:0\";\n            defaultDeviceNames[6] = \"hw:0,0\";\n        }\n\n        isDeviceOpen = MA_FALSE;\n        for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {\n            if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\\0') {\n                if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {\n                    isDeviceOpen = MA_TRUE;\n                    break;\n                }\n            }\n        }\n\n        if (!isDeviceOpen) {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[ALSA] snd_pcm_open() failed when trying to open an appropriate default device.\");\n            return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n        }\n    } else {\n        /*\n        We're trying to open a specific device. There's a few things to consider here:\n\n        miniaudio recongnizes a special format of device id that excludes the \"hw\", \"dmix\", etc. prefix. It looks like this: \":0,0\", \":0,1\", etc. When\n        an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins (\"hw\", \"dmix\", etc.) until it\n        finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode (\"dmix\"), vs exclusive mode (\"hw\").\n        */\n\n        /* May end up needing to make small adjustments to the ID, so make a copy. */\n        ma_device_id deviceID = *pDeviceID;\n        int resultALSA = -ENODEV;\n\n        if (deviceID.alsa[0] != ':') {\n            /* The ID is not in \":0,0\" format. Use the ID exactly as-is. */\n            resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode);\n        } else {\n            char hwid[256];\n\n            /* The ID is in \":0,0\" format. Try different plugins depending on the shared mode. */\n            if (deviceID.alsa[1] == '\\0') {\n                deviceID.alsa[0] = '\\0';  /* An ID of \":\" should be converted to \"\". */\n            }\n\n            if (shareMode == ma_share_mode_shared) {\n                if (deviceType == ma_device_type_playback) {\n                    ma_strcpy_s(hwid, sizeof(hwid), \"dmix\");\n                } else {\n                    ma_strcpy_s(hwid, sizeof(hwid), \"dsnoop\");\n                }\n\n                if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {\n                    resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);\n                }\n            }\n\n            /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with \"dmix\"/\"dsnoop\" failed. */\n            if (resultALSA != 0) {\n                ma_strcpy_s(hwid, sizeof(hwid), \"hw\");\n                if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {\n                    resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);\n                }\n            }\n        }\n\n        if (resultALSA < 0) {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[ALSA] snd_pcm_open() failed.\");\n            return ma_result_from_errno(-resultALSA);\n        }\n    }\n\n    *ppPCM = pPCM;\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    int resultALSA;\n    ma_bool32 cbResult = MA_TRUE;\n    char** ppDeviceHints;\n    ma_device_id* pUniqueIDs = NULL;\n    ma_uint32 uniqueIDCount = 0;\n    char** ppNextDeviceHint;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);\n\n    resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, \"pcm\", (void***)&ppDeviceHints);\n    if (resultALSA < 0) {\n        ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);\n        return ma_result_from_errno(-resultALSA);\n    }\n\n    ppNextDeviceHint = ppDeviceHints;\n    while (*ppNextDeviceHint != NULL) {\n        char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, \"NAME\");\n        char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, \"DESC\");\n        char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, \"IOID\");\n        ma_device_type deviceType = ma_device_type_playback;\n        ma_bool32 stopEnumeration = MA_FALSE;\n        char hwid[sizeof(pUniqueIDs->alsa)];\n        ma_device_info deviceInfo;\n\n        if ((IOID == NULL || ma_strcmp(IOID, \"Output\") == 0)) {\n            deviceType = ma_device_type_playback;\n        }\n        if ((IOID != NULL && ma_strcmp(IOID, \"Input\" ) == 0)) {\n            deviceType = ma_device_type_capture;\n        }\n\n        if (NAME != NULL) {\n            if (pContext->alsa.useVerboseDeviceEnumeration) {\n                /* Verbose mode. Use the name exactly as-is. */\n                ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);\n            } else {\n                /* Simplified mode. Use \":%d,%d\" format. */\n                if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {\n                    /*\n                    At this point, hwid looks like \"hw:0,0\". In simplified enumeration mode, we actually want to strip off the\n                    plugin name so it looks like \":0,0\". The reason for this is that this special format is detected at device\n                    initialization time and is used as an indicator to try and use the most appropriate plugin depending on the\n                    device type and sharing mode.\n                    */\n                    char* dst = hwid;\n                    char* src = hwid+2;\n                    while ((*dst++ = *src++));\n                } else {\n                    /* Conversion to \"hw:%d,%d\" failed. Just use the name as-is. */\n                    ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);\n                }\n\n                if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {\n                    goto next_device;   /* The device has already been enumerated. Move on to the next one. */\n                } else {\n                    /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */\n                    size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1);\n                    ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks);\n                    if (pNewUniqueIDs == NULL) {\n                        goto next_device;   /* Failed to allocate memory. */\n                    }\n\n                    pUniqueIDs = pNewUniqueIDs;\n                    MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));\n                    uniqueIDCount += 1;\n                }\n            }\n        } else {\n            MA_ZERO_MEMORY(hwid, sizeof(hwid));\n        }\n\n        MA_ZERO_OBJECT(&deviceInfo);\n        ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);\n\n        /*\n        There's no good way to determine whether or not a device is the default on Linux. We're just going to do something simple and\n        just use the name of \"default\" as the indicator.\n        */\n        if (ma_strcmp(deviceInfo.id.alsa, \"default\") == 0) {\n            deviceInfo.isDefault = MA_TRUE;\n        }\n\n\n        /*\n        DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose\n        device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish\n        between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the\n        description.\n\n        The value in DESC seems to be split into two lines, with the first line being the name of the device and the\n        second line being a description of the device. I don't like having the description be across two lines because\n        it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line\n        being put into parentheses. In simplified mode I'm just stripping the second line entirely.\n        */\n        if (DESC != NULL) {\n            int lfPos;\n            const char* line2 = ma_find_char(DESC, '\\n', &lfPos);\n            if (line2 != NULL) {\n                line2 += 1; /* Skip past the new-line character. */\n\n                if (pContext->alsa.useVerboseDeviceEnumeration) {\n                    /* Verbose mode. Put the second line in brackets. */\n                    ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);\n                    ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), \" (\");\n                    ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);\n                    ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), \")\");\n                } else {\n                    /* Simplified mode. Strip the second line entirely. */\n                    ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);\n                }\n            } else {\n                /* There's no second line. Just copy the whole description. */\n                ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);\n            }\n        }\n\n        if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {\n            cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);\n        }\n\n        /*\n        Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback\n        again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which\n        means both Input and Output.\n        */\n        if (cbResult) {\n            if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) {\n                if (deviceType == ma_device_type_playback) {\n                    if (!ma_is_capture_device_blacklisted__alsa(NAME)) {\n                        cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n                    }\n                } else {\n                    if (!ma_is_playback_device_blacklisted__alsa(NAME)) {\n                        cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n                    }\n                }\n            }\n        }\n\n        if (cbResult == MA_FALSE) {\n            stopEnumeration = MA_TRUE;\n        }\n\n    next_device:\n        free(NAME);\n        free(DESC);\n        free(IOID);\n        ppNextDeviceHint += 1;\n\n        /* We need to stop enumeration if the callback returned false. */\n        if (stopEnumeration) {\n            break;\n        }\n    }\n\n    ma_free(pUniqueIDs, &pContext->allocationCallbacks);\n    ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);\n\n    ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);\n\n    return MA_SUCCESS;\n}\n\n\ntypedef struct\n{\n    ma_device_type deviceType;\n    const ma_device_id* pDeviceID;\n    ma_share_mode shareMode;\n    ma_device_info* pDeviceInfo;\n    ma_bool32 foundDevice;\n} ma_context_get_device_info_enum_callback_data__alsa;\n\nstatic ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)\n{\n    ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;\n    MA_ASSERT(pData != NULL);\n\n    (void)pContext;\n\n    if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, \"default\") == 0) {\n        ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);\n        pData->foundDevice = MA_TRUE;\n    } else {\n        if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) {\n            ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);\n            pData->foundDevice = MA_TRUE;\n        }\n    }\n\n    /* Keep enumerating until we have found the device. */\n    return !pData->foundDevice;\n}\n\nstatic void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo)\n{\n    MA_ASSERT(pPCM        != NULL);\n    MA_ASSERT(pHWParams   != NULL);\n    MA_ASSERT(pDeviceInfo != NULL);\n\n    if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) {\n        pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format     = format;\n        pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels   = channels;\n        pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;\n        pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags      = flags;\n        pDeviceInfo->nativeDataFormatCount += 1;\n    }\n}\n\nstatic void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo)\n{\n    ma_uint32 iSampleRate;\n    unsigned int minSampleRate;\n    unsigned int maxSampleRate;\n    int sampleRateDir;  /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */\n\n    /* There could be a range. */\n    ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir);\n    ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir);\n\n    /* Make sure our sample rates are clamped to sane values. Stupid devices like \"pulse\" will reports rates like \"1\" which is ridiculus. */\n    minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);\n    maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);\n\n    for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {\n        ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];\n\n        if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {\n            ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo);\n        }\n    }\n\n    /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */\n    if (!ma_is_standard_sample_rate(minSampleRate)) {\n        ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo);\n    }\n\n    if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) {\n        ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo);\n    }\n}\n\nstatic ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    ma_context_get_device_info_enum_callback_data__alsa data;\n    ma_result result;\n    int resultALSA;\n    ma_snd_pcm_t* pPCM;\n    ma_snd_pcm_hw_params_t* pHWParams;\n    ma_uint32 iFormat;\n    ma_uint32 iChannel;\n\n    MA_ASSERT(pContext != NULL);\n\n    /* We just enumerate to find basic information about the device. */\n    data.deviceType  = deviceType;\n    data.pDeviceID   = pDeviceID;\n    data.pDeviceInfo = pDeviceInfo;\n    data.foundDevice = MA_FALSE;\n    result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (!data.foundDevice) {\n        return MA_NO_DEVICE;\n    }\n\n    if (ma_strcmp(pDeviceInfo->id.alsa, \"default\") == 0) {\n        pDeviceInfo->isDefault = MA_TRUE;\n    }\n\n    /* For detailed info we need to open the device. */\n    result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* We need to initialize a HW parameters object in order to know what formats are supported. */\n    pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks);\n    if (pHWParams == NULL) {\n        ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);\n        return MA_OUT_OF_MEMORY;\n    }\n\n    resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);\n    if (resultALSA < 0) {\n        ma_free(pHWParams, &pContext->allocationCallbacks);\n        ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.\");\n        return ma_result_from_errno(-resultALSA);\n    }\n\n    /*\n    Some ALSA devices can support many permutations of formats, channels and rates. We only support\n    a fixed number of permutations which means we need to employ some strategies to ensure the best\n    combinations are returned. An example is the \"pulse\" device which can do it's own data conversion\n    in software and as a result can support any combination of format, channels and rate.\n\n    We want to ensure the the first data formats are the best. We have a list of favored sample\n    formats and sample rates, so these will be the basis of our iteration.\n    */\n\n    /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */\n    for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {\n        ma_format format = g_maFormatPriorities[iFormat];\n\n        /*\n        For each format we need to make sure we reset the configuration space so we don't return\n        channel counts and rates that aren't compatible with a format.\n        */\n        ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);\n\n        /* Test the format first. If this fails it means the format is not supported and we can skip it. */\n        if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) {\n            /* The format is supported. */\n            unsigned int minChannels;\n            unsigned int maxChannels;\n\n            /*\n            The configuration space needs to be restricted to this format so we can get an accurate\n            picture of which sample rates and channel counts are support with this format.\n            */\n            ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format));\n\n            /* Now we need to check for supported channels. */\n            ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels);\n            ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels);\n\n            if (minChannels > MA_MAX_CHANNELS) {\n                continue;   /* Too many channels. */\n            }\n            if (maxChannels < MA_MIN_CHANNELS) {\n                continue;   /* Not enough channels. */\n            }\n\n            /*\n            Make sure the channel count is clamped. This is mainly intended for the max channels\n            because some devices can report an unbound maximum.\n            */\n            minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);\n            maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);\n\n            if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {\n                /* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */\n                ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo);    /* Intentionally setting the channel count to 0 as that means all channels are supported. */\n            } else {\n                /* The device only supports a specific set of channels. We need to iterate over all of them. */\n                for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {\n                    /* Test the channel before applying it to the configuration space. */\n                    unsigned int channels = iChannel;\n\n                    /* Make sure our channel range is reset before testing again or else we'll always fail the test. */\n                    ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);\n                    ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format));\n\n                    if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) {\n                        /* The channel count is supported. */\n\n                        /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */\n                        ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels);\n\n                        /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */\n                        ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo);\n                    } else {\n                        /* The channel count is not supported. Skip. */\n                    }\n                }\n            }\n        } else {\n            /* The format is not supported. Skip. */\n        }\n    }\n\n    ma_free(pHWParams, &pContext->allocationCallbacks);\n\n    ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_uninit__alsa(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);\n        close(pDevice->alsa.wakeupfdCapture);\n        ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks);\n    }\n\n    if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);\n        close(pDevice->alsa.wakeupfdPlayback);\n        ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)\n{\n    ma_result result;\n    int resultALSA;\n    ma_snd_pcm_t* pPCM;\n    ma_bool32 isUsingMMap;\n    ma_snd_pcm_format_t formatALSA;\n    ma_format internalFormat;\n    ma_uint32 internalChannels;\n    ma_uint32 internalSampleRate;\n    ma_channel internalChannelMap[MA_MAX_CHANNELS];\n    ma_uint32 internalPeriodSizeInFrames;\n    ma_uint32 internalPeriods;\n    int openMode;\n    ma_snd_pcm_hw_params_t* pHWParams;\n    ma_snd_pcm_sw_params_t* pSWParams;\n    ma_snd_pcm_uframes_t bufferBoundary;\n    int pollDescriptorCount;\n    struct pollfd* pPollDescriptors;\n    int wakeupfd;\n\n    MA_ASSERT(pConfig != NULL);\n    MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */\n    MA_ASSERT(pDevice != NULL);\n\n    formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format);\n\n    openMode = 0;\n    if (pConfig->alsa.noAutoResample) {\n        openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE;\n    }\n    if (pConfig->alsa.noAutoChannels) {\n        openMode |= MA_SND_PCM_NO_AUTO_CHANNELS;\n    }\n    if (pConfig->alsa.noAutoFormat) {\n        openMode |= MA_SND_PCM_NO_AUTO_FORMAT;\n    }\n\n    result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n\n    /* Hardware parameters. */\n    pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks);\n    if (pHWParams == NULL) {\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to allocate memory for hardware parameters.\");\n        return MA_OUT_OF_MEMORY;\n    }\n\n    resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);\n    if (resultALSA < 0) {\n        ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.\");\n        return ma_result_from_errno(-resultALSA);\n    }\n\n    /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */\n    isUsingMMap = MA_FALSE;\n#if 0   /* NOTE: MMAP mode temporarily disabled. */\n    if (deviceType != ma_device_type_capture) {    /* <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. */\n        if (!pConfig->alsa.noMMap) {\n            if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) {\n                pDevice->alsa.isUsingMMap = MA_TRUE;\n            }\n        }\n    }\n#endif\n\n    if (!isUsingMMap) {\n        resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED);\n        if (resultALSA < 0) {\n            ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);\n            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.\");\n            return ma_result_from_errno(-resultALSA);\n        }\n    }\n\n    /*\n    Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't\n    find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.\n    */\n\n    /* Format. */\n    {\n        /*\n        At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is\n        supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one.\n        */\n        if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) {\n            /* We're either requesting the native format or the specified format is not supported. */\n            size_t iFormat;\n\n            formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;\n            for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {\n                if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) {\n                    formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]);\n                    break;\n                }\n            }\n\n            if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {\n                ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);\n                ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Format not supported. The device does not support any miniaudio formats.\");\n                return MA_FORMAT_NOT_SUPPORTED;\n            }\n        }\n\n        resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA);\n        if (resultALSA < 0) {\n            ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);\n            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.\");\n            return ma_result_from_errno(-resultALSA);\n        }\n\n        internalFormat = ma_format_from_alsa(formatALSA);\n        if (internalFormat == ma_format_unknown) {\n            ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);\n            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] The chosen format is not supported by miniaudio.\");\n            return MA_FORMAT_NOT_SUPPORTED;\n        }\n    }\n\n    /* Channels. */\n    {\n        unsigned int channels = pDescriptor->channels;\n        if (channels == 0) {\n            channels = MA_DEFAULT_CHANNELS;\n        }\n\n        resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels);\n        if (resultALSA < 0) {\n            ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);\n            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.\");\n            return ma_result_from_errno(-resultALSA);\n        }\n\n        internalChannels = (ma_uint32)channels;\n    }\n\n    /* Sample Rate */\n    {\n        unsigned int sampleRate;\n\n        /*\n        It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes\n        problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable\n        resampling.\n\n        To reproduce this problem, open the \"plug:dmix\" device, and set the sample rate to 44100. Internally, it looks like dmix uses a\n        sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling\n        doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly\n        faster rate.\n\n        miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine\n        for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very\n        good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.\n\n        I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce\n        this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.\n        */\n        ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);\n\n        sampleRate = pDescriptor->sampleRate;\n        if (sampleRate == 0) {\n            sampleRate = MA_DEFAULT_SAMPLE_RATE;\n        }\n\n        resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0);\n        if (resultALSA < 0) {\n            ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);\n            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.\");\n            return ma_result_from_errno(-resultALSA);\n        }\n\n        internalSampleRate = (ma_uint32)sampleRate;\n    }\n\n    /* Periods. */\n    {\n        ma_uint32 periods = pDescriptor->periodCount;\n\n        resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL);\n        if (resultALSA < 0) {\n            ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);\n            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.\");\n            return ma_result_from_errno(-resultALSA);\n        }\n\n        internalPeriods = periods;\n    }\n\n    /* Buffer Size */\n    {\n        ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods;\n\n        resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames);\n        if (resultALSA < 0) {\n            ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);\n            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.\");\n            return ma_result_from_errno(-resultALSA);\n        }\n\n        internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods;\n    }\n\n    /* Apply hardware parameters. */\n    resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams);\n    if (resultALSA < 0) {\n        ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.\");\n        return ma_result_from_errno(-resultALSA);\n    }\n\n    ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);\n    pHWParams = NULL;\n\n\n    /* Software parameters. */\n    pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks);\n    if (pSWParams == NULL) {\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to allocate memory for software parameters.\");\n        return MA_OUT_OF_MEMORY;\n    }\n\n    resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams);\n    if (resultALSA < 0) {\n        ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.\");\n        return ma_result_from_errno(-resultALSA);\n    }\n\n    resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames));\n    if (resultALSA < 0) {\n        ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] snd_pcm_sw_params_set_avail_min() failed.\");\n        return ma_result_from_errno(-resultALSA);\n    }\n\n    resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary);\n    if (resultALSA < 0) {\n        bufferBoundary = internalPeriodSizeInFrames * internalPeriods;\n    }\n\n    if (deviceType == ma_device_type_playback && !isUsingMMap) {   /* Only playback devices in writei/readi mode need a start threshold. */\n        /*\n        Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to\n        the size of a period. But for full-duplex we need to set it such that it is at least two periods.\n        */\n        resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2);\n        if (resultALSA < 0) {\n            ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);\n            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.\");\n            return ma_result_from_errno(-resultALSA);\n        }\n\n        resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary);\n        if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */\n            ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);\n            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed.\");\n            return ma_result_from_errno(-resultALSA);\n        }\n    }\n\n    resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams);\n    if (resultALSA < 0) {\n        ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.\");\n        return ma_result_from_errno(-resultALSA);\n    }\n\n    ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);\n    pSWParams = NULL;\n\n\n    /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */\n    {\n        ma_snd_pcm_chmap_t* pChmap = NULL;\n        if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) {\n            pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM);\n        }\n\n        if (pChmap != NULL) {\n            ma_uint32 iChannel;\n\n            /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */\n            if (pChmap->channels >= internalChannels) {\n                /* Drop excess channels. */\n                for (iChannel = 0; iChannel < internalChannels; ++iChannel) {\n                    internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);\n                }\n            } else {\n                ma_uint32 i;\n\n                /*\n                Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate\n                channels. If validation fails, fall back to defaults.\n                */\n                ma_bool32 isValid = MA_TRUE;\n\n                /* Fill with defaults. */\n                ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);\n\n                /* Overwrite first pChmap->channels channels. */\n                for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {\n                    internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);\n                }\n\n                /* Validate. */\n                for (i = 0; i < internalChannels && isValid; ++i) {\n                    ma_uint32 j;\n                    for (j = i+1; j < internalChannels; ++j) {\n                        if (internalChannelMap[i] == internalChannelMap[j]) {\n                            isValid = MA_FALSE;\n                            break;\n                        }\n                    }\n                }\n\n                /* If our channel map is invalid, fall back to defaults. */\n                if (!isValid) {\n                    ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);\n                }\n            }\n\n            free(pChmap);\n            pChmap = NULL;\n        } else {\n            /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */\n            ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);\n        }\n    }\n\n\n    /*\n    We need to retrieve the poll descriptors so we can use poll() to wait for data to become\n    available for reading or writing. There's no well defined maximum for this so we're just going\n    to allocate this on the heap.\n    */\n    pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM);\n    if (pollDescriptorCount <= 0) {\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to retrieve poll descriptors count.\");\n        return MA_ERROR;\n    }\n\n    pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks);   /* +1 because we want room for the wakeup descriptor. */\n    if (pPollDescriptors == NULL) {\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to allocate memory for poll descriptors.\");\n        return MA_OUT_OF_MEMORY;\n    }\n\n    /*\n    We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver\n    never returns from writei() and readi(). This has been observed with the \"pulse\" device.\n    */\n    wakeupfd = eventfd(0, 0);\n    if (wakeupfd < 0) {\n        ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to create eventfd for poll wakeup.\");\n        return ma_result_from_errno(errno);\n    }\n\n    /* We'll place the wakeup fd at the start of the buffer. */\n    pPollDescriptors[0].fd      = wakeupfd;\n    pPollDescriptors[0].events  = POLLIN;    /* We only care about waiting to read from the wakeup file descriptor. */\n    pPollDescriptors[0].revents = 0;\n\n    /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */\n    pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount);    /* +1 because we want to place these descriptors after the wakeup descriptor. */\n    if (pollDescriptorCount <= 0) {\n        close(wakeupfd);\n        ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to retrieve poll descriptors.\");\n        return MA_ERROR;\n    }\n\n    if (deviceType == ma_device_type_capture) {\n        pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount;\n        pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors;\n        pDevice->alsa.wakeupfdCapture = wakeupfd;\n    } else {\n        pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount;\n        pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors;\n        pDevice->alsa.wakeupfdPlayback = wakeupfd;\n    }\n\n\n    /* We're done. Prepare the device. */\n    resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);\n    if (resultALSA < 0) {\n        close(wakeupfd);\n        ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);\n        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to prepare device.\");\n        return ma_result_from_errno(-resultALSA);\n    }\n\n\n    if (deviceType == ma_device_type_capture) {\n        pDevice->alsa.pPCMCapture         = (ma_ptr)pPCM;\n        pDevice->alsa.isUsingMMapCapture  = isUsingMMap;\n    } else {\n        pDevice->alsa.pPCMPlayback        = (ma_ptr)pPCM;\n        pDevice->alsa.isUsingMMapPlayback = isUsingMMap;\n    }\n\n    pDescriptor->format             = internalFormat;\n    pDescriptor->channels           = internalChannels;\n    pDescriptor->sampleRate         = internalSampleRate;\n    ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));\n    pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;\n    pDescriptor->periodCount        = internalPeriods;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    MA_ZERO_OBJECT(&pDevice->alsa);\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_start__alsa(ma_device* pDevice)\n{\n    int resultALSA;\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);\n        if (resultALSA < 0) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to start capture device.\");\n            return ma_result_from_errno(-resultALSA);\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__alsa(ma_device* pDevice)\n{\n    /*\n    The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is\n    a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable.\n    */\n    int resultPoll;\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Dropping capture device...\\n\");\n        ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Dropping capture device successful.\\n\");\n\n        /* We need to prepare the device again, otherwise we won't be able to restart the device. */\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Preparing capture device...\\n\");\n        if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Preparing capture device failed.\\n\");\n        } else {\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Preparing capture device successful.\\n\");\n        }\n\n    /* Clear the wakeupfd. */\n    resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0);\n    if (resultPoll > 0) {\n        ma_uint64 t;\n        read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t));\n    }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Dropping playback device...\\n\");\n        ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Dropping playback device successful.\\n\");\n\n        /* We need to prepare the device again, otherwise we won't be able to restart the device. */\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Preparing playback device...\\n\");\n        if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Preparing playback device failed.\\n\");\n        } else {\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Preparing playback device successful.\\n\");\n        }\n\n        /* Clear the wakeupfd. */\n    resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0);\n    if (resultPoll > 0) {\n        ma_uint64 t;\n        read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t));\n    }\n\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent)\n{\n    for (;;) {\n        unsigned short revents;\n        int resultALSA;\n        int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1);\n        if (resultPoll < 0) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] poll() failed.\\n\");\n            return ma_result_from_errno(errno);\n        }\n\n        /*\n        Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor\n        has had it's POLLIN flag set. If so, we need to actually read the data and then exit\n        function. The wakeup descriptor will be the first item in the descriptors buffer.\n        */\n        if ((pPollDescriptors[0].revents & POLLIN) != 0) {\n            ma_uint64 t;\n            int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t));    /* <-- Important that we read here so that the next write() does not block. */\n            if (resultRead < 0) {\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] read() failed.\\n\");\n                return ma_result_from_errno(errno);\n            }\n\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] POLLIN set for wakeupfd\\n\");\n            return MA_DEVICE_NOT_STARTED;\n        }\n\n        /*\n        Getting here means that some data should be able to be read. We need to use ALSA to\n        translate the revents flags for us.\n        */\n        resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents);   /* +1, -1 to ignore the wakeup descriptor. */\n        if (resultALSA < 0) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] snd_pcm_poll_descriptors_revents() failed.\\n\");\n            return ma_result_from_errno(-resultALSA);\n        }\n\n        if ((revents & POLLERR) != 0) {\n            ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM);\n            if (state == MA_SND_PCM_STATE_XRUN) {\n                /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */\n        } else {\n                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, \"[ALSA] POLLERR detected. status = %d\\n\", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM));\n            }\n        }\n\n        if ((revents & requiredEvent) == requiredEvent) {\n            break;  /* We're done. Data available for reading or writing. */\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_wait_read__alsa(ma_device* pDevice)\n{\n    return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */\n}\n\nstatic ma_result ma_device_wait_write__alsa(ma_device* pDevice)\n{\n    return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */\n}\n\nstatic ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)\n{\n    ma_snd_pcm_sframes_t resultALSA = 0;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(pFramesOut != NULL);\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    while (ma_device_get_state(pDevice) == ma_device_state_started) {\n        ma_result result;\n\n        /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */\n        result = ma_device_wait_read__alsa(pDevice);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        /* Getting here means we should have data available. */\n        resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);\n        if (resultALSA >= 0) {\n            break;  /* Success. */\n        } else {\n            if (resultALSA == -EAGAIN) {\n                /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"EGAIN (read)\\n\");*/\n                continue;   /* Try again. */\n            } else if (resultALSA == -EPIPE) {\n                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"EPIPE (read)\\n\");\n\n                /* Overrun. Recover and try again. If this fails we need to return an error. */\n                resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE);\n                if (resultALSA < 0) {\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to recover device after overrun.\");\n                    return ma_result_from_errno((int)-resultALSA);\n                }\n\n                resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);\n                if (resultALSA < 0) {\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to start device after underrun.\");\n                    return ma_result_from_errno((int)-resultALSA);\n                }\n\n                continue;   /* Try reading again. */\n            }\n        }\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = resultALSA;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)\n{\n    ma_snd_pcm_sframes_t resultALSA = 0;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(pFrames != NULL);\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = 0;\n    }\n\n    while (ma_device_get_state(pDevice) == ma_device_state_started) {\n        ma_result result;\n\n        /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */\n        result = ma_device_wait_write__alsa(pDevice);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);\n        if (resultALSA >= 0) {\n            break;  /* Success. */\n        } else {\n            if (resultALSA == -EAGAIN) {\n                /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"EGAIN (write)\\n\");*/\n                continue;   /* Try again. */\n            } else if (resultALSA == -EPIPE) {\n                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"EPIPE (write)\\n\");\n\n                /* Underrun. Recover and try again. If this fails we need to return an error. */\n                resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE);    /* MA_TRUE=silent (don't print anything on error). */\n                if (resultALSA < 0) {\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to recover device after underrun.\");\n                    return ma_result_from_errno((int)-resultALSA);\n                }\n\n                /*\n                In my testing I have had a situation where writei() does not automatically restart the device even though I've set it\n                up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of\n                frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure\n                if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't\n                quite right here.\n                */\n                resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);\n                if (resultALSA < 0) {\n                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] Failed to start device after underrun.\");\n                    return ma_result_from_errno((int)-resultALSA);\n                }\n\n                continue;   /* Try writing again. */\n            }\n        }\n    }\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = resultALSA;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice)\n{\n    ma_uint64 t = 1;\n    int resultWrite = 0;\n\n    MA_ASSERT(pDevice != NULL);\n\n    ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Waking up...\\n\");\n\n    /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */\n    if (pDevice->alsa.pPollDescriptorsCapture != NULL) {\n        resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t));\n    }\n    if (pDevice->alsa.pPollDescriptorsPlayback != NULL) {\n        resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t));\n    }\n\n    if (resultWrite < 0) {\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[ALSA] write() failed.\\n\");\n        return ma_result_from_errno(errno);\n    }\n\n    ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[ALSA] Waking up completed successfully.\\n\");\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_uninit__alsa(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_alsa);\n\n    /* Clean up memory for memory leak checkers. */\n    ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();\n\n#ifndef MA_NO_RUNTIME_LINKING\n    ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO);\n#endif\n\n    ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n    ma_result result;\n#ifndef MA_NO_RUNTIME_LINKING\n    const char* libasoundNames[] = {\n        \"libasound.so.2\",\n        \"libasound.so\"\n    };\n    size_t i;\n\n    for (i = 0; i < ma_countof(libasoundNames); ++i) {\n        pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]);\n        if (pContext->alsa.asoundSO != NULL) {\n            break;\n        }\n    }\n\n    if (pContext->alsa.asoundSO == NULL) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"[ALSA] Failed to open shared object.\\n\");\n        return MA_NO_BACKEND;\n    }\n\n    pContext->alsa.snd_pcm_open                           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_open\");\n    pContext->alsa.snd_pcm_close                          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_close\");\n    pContext->alsa.snd_pcm_hw_params_sizeof               = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_sizeof\");\n    pContext->alsa.snd_pcm_hw_params_any                  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_any\");\n    pContext->alsa.snd_pcm_hw_params_set_format           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_set_format\");\n    pContext->alsa.snd_pcm_hw_params_set_format_first     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_set_format_first\");\n    pContext->alsa.snd_pcm_hw_params_get_format_mask      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_get_format_mask\");\n    pContext->alsa.snd_pcm_hw_params_set_channels         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_set_channels\");\n    pContext->alsa.snd_pcm_hw_params_set_channels_near    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_set_channels_near\");\n    pContext->alsa.snd_pcm_hw_params_set_channels_minmax  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_set_channels_minmax\");\n    pContext->alsa.snd_pcm_hw_params_set_rate_resample    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_set_rate_resample\");\n    pContext->alsa.snd_pcm_hw_params_set_rate             = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_set_rate\");\n    pContext->alsa.snd_pcm_hw_params_set_rate_near        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_set_rate_near\");\n    pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_set_buffer_size_near\");\n    pContext->alsa.snd_pcm_hw_params_set_periods_near     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_set_periods_near\");\n    pContext->alsa.snd_pcm_hw_params_set_access           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_set_access\");\n    pContext->alsa.snd_pcm_hw_params_get_format           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_get_format\");\n    pContext->alsa.snd_pcm_hw_params_get_channels         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_get_channels\");\n    pContext->alsa.snd_pcm_hw_params_get_channels_min     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_get_channels_min\");\n    pContext->alsa.snd_pcm_hw_params_get_channels_max     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_get_channels_max\");\n    pContext->alsa.snd_pcm_hw_params_get_rate             = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_get_rate\");\n    pContext->alsa.snd_pcm_hw_params_get_rate_min         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_get_rate_min\");\n    pContext->alsa.snd_pcm_hw_params_get_rate_max         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_get_rate_max\");\n    pContext->alsa.snd_pcm_hw_params_get_buffer_size      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_get_buffer_size\");\n    pContext->alsa.snd_pcm_hw_params_get_periods          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_get_periods\");\n    pContext->alsa.snd_pcm_hw_params_get_access           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_get_access\");\n    pContext->alsa.snd_pcm_hw_params_test_format          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_test_format\");\n    pContext->alsa.snd_pcm_hw_params_test_channels        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_test_channels\");\n    pContext->alsa.snd_pcm_hw_params_test_rate            = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params_test_rate\");\n    pContext->alsa.snd_pcm_hw_params                      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_hw_params\");\n    pContext->alsa.snd_pcm_sw_params_sizeof               = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_sw_params_sizeof\");\n    pContext->alsa.snd_pcm_sw_params_current              = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_sw_params_current\");\n    pContext->alsa.snd_pcm_sw_params_get_boundary         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_sw_params_get_boundary\");\n    pContext->alsa.snd_pcm_sw_params_set_avail_min        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_sw_params_set_avail_min\");\n    pContext->alsa.snd_pcm_sw_params_set_start_threshold  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_sw_params_set_start_threshold\");\n    pContext->alsa.snd_pcm_sw_params_set_stop_threshold   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_sw_params_set_stop_threshold\");\n    pContext->alsa.snd_pcm_sw_params                      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_sw_params\");\n    pContext->alsa.snd_pcm_format_mask_sizeof             = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_format_mask_sizeof\");\n    pContext->alsa.snd_pcm_format_mask_test               = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_format_mask_test\");\n    pContext->alsa.snd_pcm_get_chmap                      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_get_chmap\");\n    pContext->alsa.snd_pcm_state                          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_state\");\n    pContext->alsa.snd_pcm_prepare                        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_prepare\");\n    pContext->alsa.snd_pcm_start                          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_start\");\n    pContext->alsa.snd_pcm_drop                           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_drop\");\n    pContext->alsa.snd_pcm_drain                          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_drain\");\n    pContext->alsa.snd_pcm_reset                          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_reset\");\n    pContext->alsa.snd_device_name_hint                   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_device_name_hint\");\n    pContext->alsa.snd_device_name_get_hint               = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_device_name_get_hint\");\n    pContext->alsa.snd_card_get_index                     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_card_get_index\");\n    pContext->alsa.snd_device_name_free_hint              = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_device_name_free_hint\");\n    pContext->alsa.snd_pcm_mmap_begin                     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_mmap_begin\");\n    pContext->alsa.snd_pcm_mmap_commit                    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_mmap_commit\");\n    pContext->alsa.snd_pcm_recover                        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_recover\");\n    pContext->alsa.snd_pcm_readi                          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_readi\");\n    pContext->alsa.snd_pcm_writei                         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_writei\");\n    pContext->alsa.snd_pcm_avail                          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_avail\");\n    pContext->alsa.snd_pcm_avail_update                   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_avail_update\");\n    pContext->alsa.snd_pcm_wait                           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_wait\");\n    pContext->alsa.snd_pcm_nonblock                       = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_nonblock\");\n    pContext->alsa.snd_pcm_info                           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_info\");\n    pContext->alsa.snd_pcm_info_sizeof                    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_info_sizeof\");\n    pContext->alsa.snd_pcm_info_get_name                  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_info_get_name\");\n    pContext->alsa.snd_pcm_poll_descriptors               = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_poll_descriptors\");\n    pContext->alsa.snd_pcm_poll_descriptors_count         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_poll_descriptors_count\");\n    pContext->alsa.snd_pcm_poll_descriptors_revents       = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_pcm_poll_descriptors_revents\");\n    pContext->alsa.snd_config_update_free_global          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, \"snd_config_update_free_global\");\n#else\n    /* The system below is just for type safety. */\n    ma_snd_pcm_open_proc                           _snd_pcm_open                           = snd_pcm_open;\n    ma_snd_pcm_close_proc                          _snd_pcm_close                          = snd_pcm_close;\n    ma_snd_pcm_hw_params_sizeof_proc               _snd_pcm_hw_params_sizeof               = snd_pcm_hw_params_sizeof;\n    ma_snd_pcm_hw_params_any_proc                  _snd_pcm_hw_params_any                  = snd_pcm_hw_params_any;\n    ma_snd_pcm_hw_params_set_format_proc           _snd_pcm_hw_params_set_format           = snd_pcm_hw_params_set_format;\n    ma_snd_pcm_hw_params_set_format_first_proc     _snd_pcm_hw_params_set_format_first     = snd_pcm_hw_params_set_format_first;\n    ma_snd_pcm_hw_params_get_format_mask_proc      _snd_pcm_hw_params_get_format_mask      = snd_pcm_hw_params_get_format_mask;\n    ma_snd_pcm_hw_params_set_channels_proc         _snd_pcm_hw_params_set_channels         = snd_pcm_hw_params_set_channels;\n    ma_snd_pcm_hw_params_set_channels_near_proc    _snd_pcm_hw_params_set_channels_near    = snd_pcm_hw_params_set_channels_near;\n    ma_snd_pcm_hw_params_set_rate_resample_proc    _snd_pcm_hw_params_set_rate_resample    = snd_pcm_hw_params_set_rate_resample;\n    ma_snd_pcm_hw_params_set_rate_near             _snd_pcm_hw_params_set_rate             = snd_pcm_hw_params_set_rate;\n    ma_snd_pcm_hw_params_set_rate_near_proc        _snd_pcm_hw_params_set_rate_near        = snd_pcm_hw_params_set_rate_near;\n    ma_snd_pcm_hw_params_set_rate_minmax_proc      _snd_pcm_hw_params_set_rate_minmax      = snd_pcm_hw_params_set_rate_minmax;\n    ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near;\n    ma_snd_pcm_hw_params_set_periods_near_proc     _snd_pcm_hw_params_set_periods_near     = snd_pcm_hw_params_set_periods_near;\n    ma_snd_pcm_hw_params_set_access_proc           _snd_pcm_hw_params_set_access           = snd_pcm_hw_params_set_access;\n    ma_snd_pcm_hw_params_get_format_proc           _snd_pcm_hw_params_get_format           = snd_pcm_hw_params_get_format;\n    ma_snd_pcm_hw_params_get_channels_proc         _snd_pcm_hw_params_get_channels         = snd_pcm_hw_params_get_channels;\n    ma_snd_pcm_hw_params_get_channels_min_proc     _snd_pcm_hw_params_get_channels_min     = snd_pcm_hw_params_get_channels_min;\n    ma_snd_pcm_hw_params_get_channels_max_proc     _snd_pcm_hw_params_get_channels_max     = snd_pcm_hw_params_get_channels_max;\n    ma_snd_pcm_hw_params_get_rate_proc             _snd_pcm_hw_params_get_rate             = snd_pcm_hw_params_get_rate;\n    ma_snd_pcm_hw_params_get_rate_min_proc         _snd_pcm_hw_params_get_rate_min         = snd_pcm_hw_params_get_rate_min;\n    ma_snd_pcm_hw_params_get_rate_max_proc         _snd_pcm_hw_params_get_rate_max         = snd_pcm_hw_params_get_rate_max;\n    ma_snd_pcm_hw_params_get_buffer_size_proc      _snd_pcm_hw_params_get_buffer_size      = snd_pcm_hw_params_get_buffer_size;\n    ma_snd_pcm_hw_params_get_periods_proc          _snd_pcm_hw_params_get_periods          = snd_pcm_hw_params_get_periods;\n    ma_snd_pcm_hw_params_get_access_proc           _snd_pcm_hw_params_get_access           = snd_pcm_hw_params_get_access;\n    ma_snd_pcm_hw_params_test_format_proc          _snd_pcm_hw_params_test_format          = snd_pcm_hw_params_test_format;\n    ma_snd_pcm_hw_params_test_channels_proc        _snd_pcm_hw_params_test_channels        = snd_pcm_hw_params_test_channels;\n    ma_snd_pcm_hw_params_test_rate_proc            _snd_pcm_hw_params_test_rate            = snd_pcm_hw_params_test_rate;\n    ma_snd_pcm_hw_params_proc                      _snd_pcm_hw_params                      = snd_pcm_hw_params;\n    ma_snd_pcm_sw_params_sizeof_proc               _snd_pcm_sw_params_sizeof               = snd_pcm_sw_params_sizeof;\n    ma_snd_pcm_sw_params_current_proc              _snd_pcm_sw_params_current              = snd_pcm_sw_params_current;\n    ma_snd_pcm_sw_params_get_boundary_proc         _snd_pcm_sw_params_get_boundary         = snd_pcm_sw_params_get_boundary;\n    ma_snd_pcm_sw_params_set_avail_min_proc        _snd_pcm_sw_params_set_avail_min        = snd_pcm_sw_params_set_avail_min;\n    ma_snd_pcm_sw_params_set_start_threshold_proc  _snd_pcm_sw_params_set_start_threshold  = snd_pcm_sw_params_set_start_threshold;\n    ma_snd_pcm_sw_params_set_stop_threshold_proc   _snd_pcm_sw_params_set_stop_threshold   = snd_pcm_sw_params_set_stop_threshold;\n    ma_snd_pcm_sw_params_proc                      _snd_pcm_sw_params                      = snd_pcm_sw_params;\n    ma_snd_pcm_format_mask_sizeof_proc             _snd_pcm_format_mask_sizeof             = snd_pcm_format_mask_sizeof;\n    ma_snd_pcm_format_mask_test_proc               _snd_pcm_format_mask_test               = snd_pcm_format_mask_test;\n    ma_snd_pcm_get_chmap_proc                      _snd_pcm_get_chmap                      = snd_pcm_get_chmap;\n    ma_snd_pcm_state_proc                          _snd_pcm_state                          = snd_pcm_state;\n    ma_snd_pcm_prepare_proc                        _snd_pcm_prepare                        = snd_pcm_prepare;\n    ma_snd_pcm_start_proc                          _snd_pcm_start                          = snd_pcm_start;\n    ma_snd_pcm_drop_proc                           _snd_pcm_drop                           = snd_pcm_drop;\n    ma_snd_pcm_drain_proc                          _snd_pcm_drain                          = snd_pcm_drain;\n    ma_snd_pcm_reset_proc                          _snd_pcm_reset                          = snd_pcm_reset;\n    ma_snd_device_name_hint_proc                   _snd_device_name_hint                   = snd_device_name_hint;\n    ma_snd_device_name_get_hint_proc               _snd_device_name_get_hint               = snd_device_name_get_hint;\n    ma_snd_card_get_index_proc                     _snd_card_get_index                     = snd_card_get_index;\n    ma_snd_device_name_free_hint_proc              _snd_device_name_free_hint              = snd_device_name_free_hint;\n    ma_snd_pcm_mmap_begin_proc                     _snd_pcm_mmap_begin                     = snd_pcm_mmap_begin;\n    ma_snd_pcm_mmap_commit_proc                    _snd_pcm_mmap_commit                    = snd_pcm_mmap_commit;\n    ma_snd_pcm_recover_proc                        _snd_pcm_recover                        = snd_pcm_recover;\n    ma_snd_pcm_readi_proc                          _snd_pcm_readi                          = snd_pcm_readi;\n    ma_snd_pcm_writei_proc                         _snd_pcm_writei                         = snd_pcm_writei;\n    ma_snd_pcm_avail_proc                          _snd_pcm_avail                          = snd_pcm_avail;\n    ma_snd_pcm_avail_update_proc                   _snd_pcm_avail_update                   = snd_pcm_avail_update;\n    ma_snd_pcm_wait_proc                           _snd_pcm_wait                           = snd_pcm_wait;\n    ma_snd_pcm_nonblock_proc                       _snd_pcm_nonblock                       = snd_pcm_nonblock;\n    ma_snd_pcm_info_proc                           _snd_pcm_info                           = snd_pcm_info;\n    ma_snd_pcm_info_sizeof_proc                    _snd_pcm_info_sizeof                    = snd_pcm_info_sizeof;\n    ma_snd_pcm_info_get_name_proc                  _snd_pcm_info_get_name                  = snd_pcm_info_get_name;\n    ma_snd_pcm_poll_descriptors                    _snd_pcm_poll_descriptors               = snd_pcm_poll_descriptors;\n    ma_snd_pcm_poll_descriptors_count              _snd_pcm_poll_descriptors_count         = snd_pcm_poll_descriptors_count;\n    ma_snd_pcm_poll_descriptors_revents            _snd_pcm_poll_descriptors_revents       = snd_pcm_poll_descriptors_revents;\n    ma_snd_config_update_free_global_proc          _snd_config_update_free_global          = snd_config_update_free_global;\n\n    pContext->alsa.snd_pcm_open                           = (ma_proc)_snd_pcm_open;\n    pContext->alsa.snd_pcm_close                          = (ma_proc)_snd_pcm_close;\n    pContext->alsa.snd_pcm_hw_params_sizeof               = (ma_proc)_snd_pcm_hw_params_sizeof;\n    pContext->alsa.snd_pcm_hw_params_any                  = (ma_proc)_snd_pcm_hw_params_any;\n    pContext->alsa.snd_pcm_hw_params_set_format           = (ma_proc)_snd_pcm_hw_params_set_format;\n    pContext->alsa.snd_pcm_hw_params_set_format_first     = (ma_proc)_snd_pcm_hw_params_set_format_first;\n    pContext->alsa.snd_pcm_hw_params_get_format_mask      = (ma_proc)_snd_pcm_hw_params_get_format_mask;\n    pContext->alsa.snd_pcm_hw_params_set_channels         = (ma_proc)_snd_pcm_hw_params_set_channels;\n    pContext->alsa.snd_pcm_hw_params_set_channels_near    = (ma_proc)_snd_pcm_hw_params_set_channels_near;\n    pContext->alsa.snd_pcm_hw_params_set_channels_minmax  = (ma_proc)_snd_pcm_hw_params_set_channels_minmax;\n    pContext->alsa.snd_pcm_hw_params_set_rate_resample    = (ma_proc)_snd_pcm_hw_params_set_rate_resample;\n    pContext->alsa.snd_pcm_hw_params_set_rate             = (ma_proc)_snd_pcm_hw_params_set_rate;\n    pContext->alsa.snd_pcm_hw_params_set_rate_near        = (ma_proc)_snd_pcm_hw_params_set_rate_near;\n    pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;\n    pContext->alsa.snd_pcm_hw_params_set_periods_near     = (ma_proc)_snd_pcm_hw_params_set_periods_near;\n    pContext->alsa.snd_pcm_hw_params_set_access           = (ma_proc)_snd_pcm_hw_params_set_access;\n    pContext->alsa.snd_pcm_hw_params_get_format           = (ma_proc)_snd_pcm_hw_params_get_format;\n    pContext->alsa.snd_pcm_hw_params_get_channels         = (ma_proc)_snd_pcm_hw_params_get_channels;\n    pContext->alsa.snd_pcm_hw_params_get_channels_min     = (ma_proc)_snd_pcm_hw_params_get_channels_min;\n    pContext->alsa.snd_pcm_hw_params_get_channels_max     = (ma_proc)_snd_pcm_hw_params_get_channels_max;\n    pContext->alsa.snd_pcm_hw_params_get_rate             = (ma_proc)_snd_pcm_hw_params_get_rate;\n    pContext->alsa.snd_pcm_hw_params_get_rate_min         = (ma_proc)_snd_pcm_hw_params_get_rate_min;\n    pContext->alsa.snd_pcm_hw_params_get_rate_max         = (ma_proc)_snd_pcm_hw_params_get_rate_max;\n    pContext->alsa.snd_pcm_hw_params_get_buffer_size      = (ma_proc)_snd_pcm_hw_params_get_buffer_size;\n    pContext->alsa.snd_pcm_hw_params_get_periods          = (ma_proc)_snd_pcm_hw_params_get_periods;\n    pContext->alsa.snd_pcm_hw_params_get_access           = (ma_proc)_snd_pcm_hw_params_get_access;\n    pContext->alsa.snd_pcm_hw_params_test_format          = (ma_proc)_snd_pcm_hw_params_test_format;\n    pContext->alsa.snd_pcm_hw_params_test_channels        = (ma_proc)_snd_pcm_hw_params_test_channels;\n    pContext->alsa.snd_pcm_hw_params_test_rate            = (ma_proc)_snd_pcm_hw_params_test_rate;\n    pContext->alsa.snd_pcm_hw_params                      = (ma_proc)_snd_pcm_hw_params;\n    pContext->alsa.snd_pcm_sw_params_sizeof               = (ma_proc)_snd_pcm_sw_params_sizeof;\n    pContext->alsa.snd_pcm_sw_params_current              = (ma_proc)_snd_pcm_sw_params_current;\n    pContext->alsa.snd_pcm_sw_params_get_boundary         = (ma_proc)_snd_pcm_sw_params_get_boundary;\n    pContext->alsa.snd_pcm_sw_params_set_avail_min        = (ma_proc)_snd_pcm_sw_params_set_avail_min;\n    pContext->alsa.snd_pcm_sw_params_set_start_threshold  = (ma_proc)_snd_pcm_sw_params_set_start_threshold;\n    pContext->alsa.snd_pcm_sw_params_set_stop_threshold   = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;\n    pContext->alsa.snd_pcm_sw_params                      = (ma_proc)_snd_pcm_sw_params;\n    pContext->alsa.snd_pcm_format_mask_sizeof             = (ma_proc)_snd_pcm_format_mask_sizeof;\n    pContext->alsa.snd_pcm_format_mask_test               = (ma_proc)_snd_pcm_format_mask_test;\n    pContext->alsa.snd_pcm_get_chmap                      = (ma_proc)_snd_pcm_get_chmap;\n    pContext->alsa.snd_pcm_state                          = (ma_proc)_snd_pcm_state;\n    pContext->alsa.snd_pcm_prepare                        = (ma_proc)_snd_pcm_prepare;\n    pContext->alsa.snd_pcm_start                          = (ma_proc)_snd_pcm_start;\n    pContext->alsa.snd_pcm_drop                           = (ma_proc)_snd_pcm_drop;\n    pContext->alsa.snd_pcm_drain                          = (ma_proc)_snd_pcm_drain;\n    pContext->alsa.snd_pcm_reset                          = (ma_proc)_snd_pcm_reset;\n    pContext->alsa.snd_device_name_hint                   = (ma_proc)_snd_device_name_hint;\n    pContext->alsa.snd_device_name_get_hint               = (ma_proc)_snd_device_name_get_hint;\n    pContext->alsa.snd_card_get_index                     = (ma_proc)_snd_card_get_index;\n    pContext->alsa.snd_device_name_free_hint              = (ma_proc)_snd_device_name_free_hint;\n    pContext->alsa.snd_pcm_mmap_begin                     = (ma_proc)_snd_pcm_mmap_begin;\n    pContext->alsa.snd_pcm_mmap_commit                    = (ma_proc)_snd_pcm_mmap_commit;\n    pContext->alsa.snd_pcm_recover                        = (ma_proc)_snd_pcm_recover;\n    pContext->alsa.snd_pcm_readi                          = (ma_proc)_snd_pcm_readi;\n    pContext->alsa.snd_pcm_writei                         = (ma_proc)_snd_pcm_writei;\n    pContext->alsa.snd_pcm_avail                          = (ma_proc)_snd_pcm_avail;\n    pContext->alsa.snd_pcm_avail_update                   = (ma_proc)_snd_pcm_avail_update;\n    pContext->alsa.snd_pcm_wait                           = (ma_proc)_snd_pcm_wait;\n    pContext->alsa.snd_pcm_nonblock                       = (ma_proc)_snd_pcm_nonblock;\n    pContext->alsa.snd_pcm_info                           = (ma_proc)_snd_pcm_info;\n    pContext->alsa.snd_pcm_info_sizeof                    = (ma_proc)_snd_pcm_info_sizeof;\n    pContext->alsa.snd_pcm_info_get_name                  = (ma_proc)_snd_pcm_info_get_name;\n    pContext->alsa.snd_pcm_poll_descriptors               = (ma_proc)_snd_pcm_poll_descriptors;\n    pContext->alsa.snd_pcm_poll_descriptors_count         = (ma_proc)_snd_pcm_poll_descriptors_count;\n    pContext->alsa.snd_pcm_poll_descriptors_revents       = (ma_proc)_snd_pcm_poll_descriptors_revents;\n    pContext->alsa.snd_config_update_free_global          = (ma_proc)_snd_config_update_free_global;\n#endif\n\n    pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;\n\n    result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock);\n    if (result != MA_SUCCESS) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.\");\n        return result;\n    }\n\n    pCallbacks->onContextInit             = ma_context_init__alsa;\n    pCallbacks->onContextUninit           = ma_context_uninit__alsa;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__alsa;\n    pCallbacks->onDeviceInit              = ma_device_init__alsa;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__alsa;\n    pCallbacks->onDeviceStart             = ma_device_start__alsa;\n    pCallbacks->onDeviceStop              = ma_device_stop__alsa;\n    pCallbacks->onDeviceRead              = ma_device_read__alsa;\n    pCallbacks->onDeviceWrite             = ma_device_write__alsa;\n    pCallbacks->onDeviceDataLoop          = NULL;\n    pCallbacks->onDeviceDataLoopWakeup    = ma_device_data_loop_wakeup__alsa;\n\n    return MA_SUCCESS;\n}\n#endif  /* ALSA */\n\n\n\n/******************************************************************************\n\nPulseAudio Backend\n\n******************************************************************************/\n#ifdef MA_HAS_PULSEAUDIO\n/*\nThe PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on\nin the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion.\n\nPulseAudio has something they call the \"Simple API\", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it\nallows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it\nappears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or\nwrite functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the\nsimple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient\nwhen you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API.\n\nSince we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to\nget fun, and I don't mean that in a good way...\n\nThe problems start with the very name of the API - \"asynchronous\". Yes, this is an asynchronous oriented API which means your commands\ndon't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is\nenabled through the use of a \"main loop\". In the asychronous API you cannot get away from the main loop, and the main loop is where almost\nall of PulseAudio's problems stem from.\n\nWhen you first initialize PulseAudio you need an object referred to as \"main loop\". You can implement this yourself by defining your own\nvtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called\npa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop\nbecause it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use\nit to implement a worker thread which runs in a loop. The main loop is where operations are actually executed.\n\nTo initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer\nto the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded\nmain loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely\nspecialized such as if you want to integrate it into your application's existing main loop infrastructure.\n\n(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262.\nIt is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.)\n\nOnce you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to\nminiaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's\none `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which\nis done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if\nyou remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()`\nhas returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can\nset with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop.\nAll objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected.\nThis waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before\nattempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`.\n\nThe reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an\ninternet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local \"server\" running on the\nhost machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind.\n\nOnce the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device.\nThe initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call\n`pa_stream_new()` to actually create it. Here is where we start to get into \"operations\". When configuring the stream, you can get\ninformation about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object\nis returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to\nrun a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the\ncontext. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up.\nAll of that just to retrieve basic information about a device!\n\nOnce the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the\ncontext, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design\nchoices in PulseAudio.\n\nPulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here\nbecause PulseAudio takes it literally, specifically the \"can be\". You would think these callbacks would be appropriate as the place for\nwriting and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can\nset a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices\nstraight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified,\nPulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation)\nbecause indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback\nwould be where a program will want to write or read data to or from the stream, but when it's called before the application has even\nrequested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at\nthat point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the\nstream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data\ncallback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio\ndoesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been\nstarted. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data\ncallback is not fired.\n\nThis, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will\ncontinuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device\nis running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in\nPulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call\n`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always\nwriting data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if\nyou're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to\n*not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining\nimportant? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained\nbefore returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write\ndata to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again!\n\nThis becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not*\nwrite anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just\nresume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This\ndisconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the\ncallback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.)\n\nOnce you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context,\nonly this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as\n\"corking\" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think\nit's silly - why would you not just call it \"starting\" and \"stopping\" like any other normal audio API? Anyway, the act of corking is, you\nguessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is\nabsolutely beyond me. Would it really be that hard to just make it run synchronously?\n\nTeardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that\nthey were initialized in.\n\nThat's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're\nembarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to\nrun asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche\nrequirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is\nconstantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a\nparameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These\nchanges alone will change PulseAudio from one of the worst audio APIs to one of the best.\n*/\n\n\n/*\nIt is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header\nto check for type safety. We cannot do this when linking at run time because the header might not be available.\n*/\n#ifdef MA_NO_RUNTIME_LINKING\n\n/* pulseaudio.h marks some functions with \"inline\" which isn't always supported. Need to emulate it. */\n#if !defined(__cplusplus)\n    #if defined(__STRICT_ANSI__)\n        #if !defined(inline)\n            #define inline __inline__ __attribute__((always_inline))\n            #define MA_INLINE_DEFINED\n        #endif\n    #endif\n#endif\n#include <pulse/pulseaudio.h>\n#if defined(MA_INLINE_DEFINED)\n    #undef inline\n    #undef MA_INLINE_DEFINED\n#endif\n\n#define MA_PA_OK                                       PA_OK\n#define MA_PA_ERR_ACCESS                               PA_ERR_ACCESS\n#define MA_PA_ERR_INVALID                              PA_ERR_INVALID\n#define MA_PA_ERR_NOENTITY                             PA_ERR_NOENTITY\n#define MA_PA_ERR_NOTSUPPORTED                         PA_ERR_NOTSUPPORTED\n\n#define MA_PA_CHANNELS_MAX                             PA_CHANNELS_MAX\n#define MA_PA_RATE_MAX                                 PA_RATE_MAX\n\ntypedef pa_context_flags_t ma_pa_context_flags_t;\n#define MA_PA_CONTEXT_NOFLAGS                          PA_CONTEXT_NOFLAGS\n#define MA_PA_CONTEXT_NOAUTOSPAWN                      PA_CONTEXT_NOAUTOSPAWN\n#define MA_PA_CONTEXT_NOFAIL                           PA_CONTEXT_NOFAIL\n\ntypedef pa_stream_flags_t ma_pa_stream_flags_t;\n#define MA_PA_STREAM_NOFLAGS                           PA_STREAM_NOFLAGS\n#define MA_PA_STREAM_START_CORKED                      PA_STREAM_START_CORKED\n#define MA_PA_STREAM_INTERPOLATE_TIMING                PA_STREAM_INTERPOLATE_TIMING\n#define MA_PA_STREAM_NOT_MONOTONIC                     PA_STREAM_NOT_MONOTONIC\n#define MA_PA_STREAM_AUTO_TIMING_UPDATE                PA_STREAM_AUTO_TIMING_UPDATE\n#define MA_PA_STREAM_NO_REMAP_CHANNELS                 PA_STREAM_NO_REMAP_CHANNELS\n#define MA_PA_STREAM_NO_REMIX_CHANNELS                 PA_STREAM_NO_REMIX_CHANNELS\n#define MA_PA_STREAM_FIX_FORMAT                        PA_STREAM_FIX_FORMAT\n#define MA_PA_STREAM_FIX_RATE                          PA_STREAM_FIX_RATE\n#define MA_PA_STREAM_FIX_CHANNELS                      PA_STREAM_FIX_CHANNELS\n#define MA_PA_STREAM_DONT_MOVE                         PA_STREAM_DONT_MOVE\n#define MA_PA_STREAM_VARIABLE_RATE                     PA_STREAM_VARIABLE_RATE\n#define MA_PA_STREAM_PEAK_DETECT                       PA_STREAM_PEAK_DETECT\n#define MA_PA_STREAM_START_MUTED                       PA_STREAM_START_MUTED\n#define MA_PA_STREAM_ADJUST_LATENCY                    PA_STREAM_ADJUST_LATENCY\n#define MA_PA_STREAM_EARLY_REQUESTS                    PA_STREAM_EARLY_REQUESTS\n#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND         PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND\n#define MA_PA_STREAM_START_UNMUTED                     PA_STREAM_START_UNMUTED\n#define MA_PA_STREAM_FAIL_ON_SUSPEND                   PA_STREAM_FAIL_ON_SUSPEND\n#define MA_PA_STREAM_RELATIVE_VOLUME                   PA_STREAM_RELATIVE_VOLUME\n#define MA_PA_STREAM_PASSTHROUGH                       PA_STREAM_PASSTHROUGH\n\ntypedef pa_sink_flags_t ma_pa_sink_flags_t;\n#define MA_PA_SINK_NOFLAGS                             PA_SINK_NOFLAGS\n#define MA_PA_SINK_HW_VOLUME_CTRL                      PA_SINK_HW_VOLUME_CTRL\n#define MA_PA_SINK_LATENCY                             PA_SINK_LATENCY\n#define MA_PA_SINK_HARDWARE                            PA_SINK_HARDWARE\n#define MA_PA_SINK_NETWORK                             PA_SINK_NETWORK\n#define MA_PA_SINK_HW_MUTE_CTRL                        PA_SINK_HW_MUTE_CTRL\n#define MA_PA_SINK_DECIBEL_VOLUME                      PA_SINK_DECIBEL_VOLUME\n#define MA_PA_SINK_FLAT_VOLUME                         PA_SINK_FLAT_VOLUME\n#define MA_PA_SINK_DYNAMIC_LATENCY                     PA_SINK_DYNAMIC_LATENCY\n#define MA_PA_SINK_SET_FORMATS                         PA_SINK_SET_FORMATS\n\ntypedef pa_source_flags_t ma_pa_source_flags_t;\n#define MA_PA_SOURCE_NOFLAGS                           PA_SOURCE_NOFLAGS\n#define MA_PA_SOURCE_HW_VOLUME_CTRL                    PA_SOURCE_HW_VOLUME_CTRL\n#define MA_PA_SOURCE_LATENCY                           PA_SOURCE_LATENCY\n#define MA_PA_SOURCE_HARDWARE                          PA_SOURCE_HARDWARE\n#define MA_PA_SOURCE_NETWORK                           PA_SOURCE_NETWORK\n#define MA_PA_SOURCE_HW_MUTE_CTRL                      PA_SOURCE_HW_MUTE_CTRL\n#define MA_PA_SOURCE_DECIBEL_VOLUME                    PA_SOURCE_DECIBEL_VOLUME\n#define MA_PA_SOURCE_DYNAMIC_LATENCY                   PA_SOURCE_DYNAMIC_LATENCY\n#define MA_PA_SOURCE_FLAT_VOLUME                       PA_SOURCE_FLAT_VOLUME\n\ntypedef pa_context_state_t ma_pa_context_state_t;\n#define MA_PA_CONTEXT_UNCONNECTED                      PA_CONTEXT_UNCONNECTED\n#define MA_PA_CONTEXT_CONNECTING                       PA_CONTEXT_CONNECTING\n#define MA_PA_CONTEXT_AUTHORIZING                      PA_CONTEXT_AUTHORIZING\n#define MA_PA_CONTEXT_SETTING_NAME                     PA_CONTEXT_SETTING_NAME\n#define MA_PA_CONTEXT_READY                            PA_CONTEXT_READY\n#define MA_PA_CONTEXT_FAILED                           PA_CONTEXT_FAILED\n#define MA_PA_CONTEXT_TERMINATED                       PA_CONTEXT_TERMINATED\n\ntypedef pa_stream_state_t ma_pa_stream_state_t;\n#define MA_PA_STREAM_UNCONNECTED                       PA_STREAM_UNCONNECTED\n#define MA_PA_STREAM_CREATING                          PA_STREAM_CREATING\n#define MA_PA_STREAM_READY                             PA_STREAM_READY\n#define MA_PA_STREAM_FAILED                            PA_STREAM_FAILED\n#define MA_PA_STREAM_TERMINATED                        PA_STREAM_TERMINATED\n\ntypedef pa_operation_state_t ma_pa_operation_state_t;\n#define MA_PA_OPERATION_RUNNING                        PA_OPERATION_RUNNING\n#define MA_PA_OPERATION_DONE                           PA_OPERATION_DONE\n#define MA_PA_OPERATION_CANCELLED                      PA_OPERATION_CANCELLED\n\ntypedef pa_sink_state_t ma_pa_sink_state_t;\n#define MA_PA_SINK_INVALID_STATE                       PA_SINK_INVALID_STATE\n#define MA_PA_SINK_RUNNING                             PA_SINK_RUNNING\n#define MA_PA_SINK_IDLE                                PA_SINK_IDLE\n#define MA_PA_SINK_SUSPENDED                           PA_SINK_SUSPENDED\n\ntypedef pa_source_state_t ma_pa_source_state_t;\n#define MA_PA_SOURCE_INVALID_STATE                     PA_SOURCE_INVALID_STATE\n#define MA_PA_SOURCE_RUNNING                           PA_SOURCE_RUNNING\n#define MA_PA_SOURCE_IDLE                              PA_SOURCE_IDLE\n#define MA_PA_SOURCE_SUSPENDED                         PA_SOURCE_SUSPENDED\n\ntypedef pa_seek_mode_t ma_pa_seek_mode_t;\n#define MA_PA_SEEK_RELATIVE                            PA_SEEK_RELATIVE\n#define MA_PA_SEEK_ABSOLUTE                            PA_SEEK_ABSOLUTE\n#define MA_PA_SEEK_RELATIVE_ON_READ                    PA_SEEK_RELATIVE_ON_READ\n#define MA_PA_SEEK_RELATIVE_END                        PA_SEEK_RELATIVE_END\n\ntypedef pa_channel_position_t ma_pa_channel_position_t;\n#define MA_PA_CHANNEL_POSITION_INVALID                 PA_CHANNEL_POSITION_INVALID\n#define MA_PA_CHANNEL_POSITION_MONO                    PA_CHANNEL_POSITION_MONO\n#define MA_PA_CHANNEL_POSITION_FRONT_LEFT              PA_CHANNEL_POSITION_FRONT_LEFT\n#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT             PA_CHANNEL_POSITION_FRONT_RIGHT\n#define MA_PA_CHANNEL_POSITION_FRONT_CENTER            PA_CHANNEL_POSITION_FRONT_CENTER\n#define MA_PA_CHANNEL_POSITION_REAR_CENTER             PA_CHANNEL_POSITION_REAR_CENTER\n#define MA_PA_CHANNEL_POSITION_REAR_LEFT               PA_CHANNEL_POSITION_REAR_LEFT\n#define MA_PA_CHANNEL_POSITION_REAR_RIGHT              PA_CHANNEL_POSITION_REAR_RIGHT\n#define MA_PA_CHANNEL_POSITION_LFE                     PA_CHANNEL_POSITION_LFE\n#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER    PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER\n#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER   PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER\n#define MA_PA_CHANNEL_POSITION_SIDE_LEFT               PA_CHANNEL_POSITION_SIDE_LEFT\n#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT              PA_CHANNEL_POSITION_SIDE_RIGHT\n#define MA_PA_CHANNEL_POSITION_AUX0                    PA_CHANNEL_POSITION_AUX0\n#define MA_PA_CHANNEL_POSITION_AUX1                    PA_CHANNEL_POSITION_AUX1\n#define MA_PA_CHANNEL_POSITION_AUX2                    PA_CHANNEL_POSITION_AUX2\n#define MA_PA_CHANNEL_POSITION_AUX3                    PA_CHANNEL_POSITION_AUX3\n#define MA_PA_CHANNEL_POSITION_AUX4                    PA_CHANNEL_POSITION_AUX4\n#define MA_PA_CHANNEL_POSITION_AUX5                    PA_CHANNEL_POSITION_AUX5\n#define MA_PA_CHANNEL_POSITION_AUX6                    PA_CHANNEL_POSITION_AUX6\n#define MA_PA_CHANNEL_POSITION_AUX7                    PA_CHANNEL_POSITION_AUX7\n#define MA_PA_CHANNEL_POSITION_AUX8                    PA_CHANNEL_POSITION_AUX8\n#define MA_PA_CHANNEL_POSITION_AUX9                    PA_CHANNEL_POSITION_AUX9\n#define MA_PA_CHANNEL_POSITION_AUX10                   PA_CHANNEL_POSITION_AUX10\n#define MA_PA_CHANNEL_POSITION_AUX11                   PA_CHANNEL_POSITION_AUX11\n#define MA_PA_CHANNEL_POSITION_AUX12                   PA_CHANNEL_POSITION_AUX12\n#define MA_PA_CHANNEL_POSITION_AUX13                   PA_CHANNEL_POSITION_AUX13\n#define MA_PA_CHANNEL_POSITION_AUX14                   PA_CHANNEL_POSITION_AUX14\n#define MA_PA_CHANNEL_POSITION_AUX15                   PA_CHANNEL_POSITION_AUX15\n#define MA_PA_CHANNEL_POSITION_AUX16                   PA_CHANNEL_POSITION_AUX16\n#define MA_PA_CHANNEL_POSITION_AUX17                   PA_CHANNEL_POSITION_AUX17\n#define MA_PA_CHANNEL_POSITION_AUX18                   PA_CHANNEL_POSITION_AUX18\n#define MA_PA_CHANNEL_POSITION_AUX19                   PA_CHANNEL_POSITION_AUX19\n#define MA_PA_CHANNEL_POSITION_AUX20                   PA_CHANNEL_POSITION_AUX20\n#define MA_PA_CHANNEL_POSITION_AUX21                   PA_CHANNEL_POSITION_AUX21\n#define MA_PA_CHANNEL_POSITION_AUX22                   PA_CHANNEL_POSITION_AUX22\n#define MA_PA_CHANNEL_POSITION_AUX23                   PA_CHANNEL_POSITION_AUX23\n#define MA_PA_CHANNEL_POSITION_AUX24                   PA_CHANNEL_POSITION_AUX24\n#define MA_PA_CHANNEL_POSITION_AUX25                   PA_CHANNEL_POSITION_AUX25\n#define MA_PA_CHANNEL_POSITION_AUX26                   PA_CHANNEL_POSITION_AUX26\n#define MA_PA_CHANNEL_POSITION_AUX27                   PA_CHANNEL_POSITION_AUX27\n#define MA_PA_CHANNEL_POSITION_AUX28                   PA_CHANNEL_POSITION_AUX28\n#define MA_PA_CHANNEL_POSITION_AUX29                   PA_CHANNEL_POSITION_AUX29\n#define MA_PA_CHANNEL_POSITION_AUX30                   PA_CHANNEL_POSITION_AUX30\n#define MA_PA_CHANNEL_POSITION_AUX31                   PA_CHANNEL_POSITION_AUX31\n#define MA_PA_CHANNEL_POSITION_TOP_CENTER              PA_CHANNEL_POSITION_TOP_CENTER\n#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT          PA_CHANNEL_POSITION_TOP_FRONT_LEFT\n#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT         PA_CHANNEL_POSITION_TOP_FRONT_RIGHT\n#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER        PA_CHANNEL_POSITION_TOP_FRONT_CENTER\n#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT           PA_CHANNEL_POSITION_TOP_REAR_LEFT\n#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT          PA_CHANNEL_POSITION_TOP_REAR_RIGHT\n#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER         PA_CHANNEL_POSITION_TOP_REAR_CENTER\n#define MA_PA_CHANNEL_POSITION_LEFT                    PA_CHANNEL_POSITION_LEFT\n#define MA_PA_CHANNEL_POSITION_RIGHT                   PA_CHANNEL_POSITION_RIGHT\n#define MA_PA_CHANNEL_POSITION_CENTER                  PA_CHANNEL_POSITION_CENTER\n#define MA_PA_CHANNEL_POSITION_SUBWOOFER               PA_CHANNEL_POSITION_SUBWOOFER\n\ntypedef pa_channel_map_def_t ma_pa_channel_map_def_t;\n#define MA_PA_CHANNEL_MAP_AIFF                         PA_CHANNEL_MAP_AIFF\n#define MA_PA_CHANNEL_MAP_ALSA                         PA_CHANNEL_MAP_ALSA\n#define MA_PA_CHANNEL_MAP_AUX                          PA_CHANNEL_MAP_AUX\n#define MA_PA_CHANNEL_MAP_WAVEEX                       PA_CHANNEL_MAP_WAVEEX\n#define MA_PA_CHANNEL_MAP_OSS                          PA_CHANNEL_MAP_OSS\n#define MA_PA_CHANNEL_MAP_DEFAULT                      PA_CHANNEL_MAP_DEFAULT\n\ntypedef pa_sample_format_t ma_pa_sample_format_t;\n#define MA_PA_SAMPLE_INVALID                           PA_SAMPLE_INVALID\n#define MA_PA_SAMPLE_U8                                PA_SAMPLE_U8\n#define MA_PA_SAMPLE_ALAW                              PA_SAMPLE_ALAW\n#define MA_PA_SAMPLE_ULAW                              PA_SAMPLE_ULAW\n#define MA_PA_SAMPLE_S16LE                             PA_SAMPLE_S16LE\n#define MA_PA_SAMPLE_S16BE                             PA_SAMPLE_S16BE\n#define MA_PA_SAMPLE_FLOAT32LE                         PA_SAMPLE_FLOAT32LE\n#define MA_PA_SAMPLE_FLOAT32BE                         PA_SAMPLE_FLOAT32BE\n#define MA_PA_SAMPLE_S32LE                             PA_SAMPLE_S32LE\n#define MA_PA_SAMPLE_S32BE                             PA_SAMPLE_S32BE\n#define MA_PA_SAMPLE_S24LE                             PA_SAMPLE_S24LE\n#define MA_PA_SAMPLE_S24BE                             PA_SAMPLE_S24BE\n#define MA_PA_SAMPLE_S24_32LE                          PA_SAMPLE_S24_32LE\n#define MA_PA_SAMPLE_S24_32BE                          PA_SAMPLE_S24_32BE\n\ntypedef pa_mainloop             ma_pa_mainloop;\ntypedef pa_threaded_mainloop    ma_pa_threaded_mainloop;\ntypedef pa_mainloop_api         ma_pa_mainloop_api;\ntypedef pa_context              ma_pa_context;\ntypedef pa_operation            ma_pa_operation;\ntypedef pa_stream               ma_pa_stream;\ntypedef pa_spawn_api            ma_pa_spawn_api;\ntypedef pa_buffer_attr          ma_pa_buffer_attr;\ntypedef pa_channel_map          ma_pa_channel_map;\ntypedef pa_cvolume              ma_pa_cvolume;\ntypedef pa_sample_spec          ma_pa_sample_spec;\ntypedef pa_sink_info            ma_pa_sink_info;\ntypedef pa_source_info          ma_pa_source_info;\n\ntypedef pa_context_notify_cb_t  ma_pa_context_notify_cb_t;\ntypedef pa_sink_info_cb_t       ma_pa_sink_info_cb_t;\ntypedef pa_source_info_cb_t     ma_pa_source_info_cb_t;\ntypedef pa_stream_success_cb_t  ma_pa_stream_success_cb_t;\ntypedef pa_stream_request_cb_t  ma_pa_stream_request_cb_t;\ntypedef pa_stream_notify_cb_t   ma_pa_stream_notify_cb_t;\ntypedef pa_free_cb_t            ma_pa_free_cb_t;\n#else\n#define MA_PA_OK                                       0\n#define MA_PA_ERR_ACCESS                               1\n#define MA_PA_ERR_INVALID                              2\n#define MA_PA_ERR_NOENTITY                             5\n#define MA_PA_ERR_NOTSUPPORTED                         19\n\n#define MA_PA_CHANNELS_MAX                             32\n#define MA_PA_RATE_MAX                                 384000\n\ntypedef int ma_pa_context_flags_t;\n#define MA_PA_CONTEXT_NOFLAGS                          0x00000000\n#define MA_PA_CONTEXT_NOAUTOSPAWN                      0x00000001\n#define MA_PA_CONTEXT_NOFAIL                           0x00000002\n\ntypedef int ma_pa_stream_flags_t;\n#define MA_PA_STREAM_NOFLAGS                           0x00000000\n#define MA_PA_STREAM_START_CORKED                      0x00000001\n#define MA_PA_STREAM_INTERPOLATE_TIMING                0x00000002\n#define MA_PA_STREAM_NOT_MONOTONIC                     0x00000004\n#define MA_PA_STREAM_AUTO_TIMING_UPDATE                0x00000008\n#define MA_PA_STREAM_NO_REMAP_CHANNELS                 0x00000010\n#define MA_PA_STREAM_NO_REMIX_CHANNELS                 0x00000020\n#define MA_PA_STREAM_FIX_FORMAT                        0x00000040\n#define MA_PA_STREAM_FIX_RATE                          0x00000080\n#define MA_PA_STREAM_FIX_CHANNELS                      0x00000100\n#define MA_PA_STREAM_DONT_MOVE                         0x00000200\n#define MA_PA_STREAM_VARIABLE_RATE                     0x00000400\n#define MA_PA_STREAM_PEAK_DETECT                       0x00000800\n#define MA_PA_STREAM_START_MUTED                       0x00001000\n#define MA_PA_STREAM_ADJUST_LATENCY                    0x00002000\n#define MA_PA_STREAM_EARLY_REQUESTS                    0x00004000\n#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND         0x00008000\n#define MA_PA_STREAM_START_UNMUTED                     0x00010000\n#define MA_PA_STREAM_FAIL_ON_SUSPEND                   0x00020000\n#define MA_PA_STREAM_RELATIVE_VOLUME                   0x00040000\n#define MA_PA_STREAM_PASSTHROUGH                       0x00080000\n\ntypedef int ma_pa_sink_flags_t;\n#define MA_PA_SINK_NOFLAGS                             0x00000000\n#define MA_PA_SINK_HW_VOLUME_CTRL                      0x00000001\n#define MA_PA_SINK_LATENCY                             0x00000002\n#define MA_PA_SINK_HARDWARE                            0x00000004\n#define MA_PA_SINK_NETWORK                             0x00000008\n#define MA_PA_SINK_HW_MUTE_CTRL                        0x00000010\n#define MA_PA_SINK_DECIBEL_VOLUME                      0x00000020\n#define MA_PA_SINK_FLAT_VOLUME                         0x00000040\n#define MA_PA_SINK_DYNAMIC_LATENCY                     0x00000080\n#define MA_PA_SINK_SET_FORMATS                         0x00000100\n\ntypedef int ma_pa_source_flags_t;\n#define MA_PA_SOURCE_NOFLAGS                           0x00000000\n#define MA_PA_SOURCE_HW_VOLUME_CTRL                    0x00000001\n#define MA_PA_SOURCE_LATENCY                           0x00000002\n#define MA_PA_SOURCE_HARDWARE                          0x00000004\n#define MA_PA_SOURCE_NETWORK                           0x00000008\n#define MA_PA_SOURCE_HW_MUTE_CTRL                      0x00000010\n#define MA_PA_SOURCE_DECIBEL_VOLUME                    0x00000020\n#define MA_PA_SOURCE_DYNAMIC_LATENCY                   0x00000040\n#define MA_PA_SOURCE_FLAT_VOLUME                       0x00000080\n\ntypedef int ma_pa_context_state_t;\n#define MA_PA_CONTEXT_UNCONNECTED                      0\n#define MA_PA_CONTEXT_CONNECTING                       1\n#define MA_PA_CONTEXT_AUTHORIZING                      2\n#define MA_PA_CONTEXT_SETTING_NAME                     3\n#define MA_PA_CONTEXT_READY                            4\n#define MA_PA_CONTEXT_FAILED                           5\n#define MA_PA_CONTEXT_TERMINATED                       6\n\ntypedef int ma_pa_stream_state_t;\n#define MA_PA_STREAM_UNCONNECTED                       0\n#define MA_PA_STREAM_CREATING                          1\n#define MA_PA_STREAM_READY                             2\n#define MA_PA_STREAM_FAILED                            3\n#define MA_PA_STREAM_TERMINATED                        4\n\ntypedef int ma_pa_operation_state_t;\n#define MA_PA_OPERATION_RUNNING                        0\n#define MA_PA_OPERATION_DONE                           1\n#define MA_PA_OPERATION_CANCELLED                      2\n\ntypedef int ma_pa_sink_state_t;\n#define MA_PA_SINK_INVALID_STATE                       -1\n#define MA_PA_SINK_RUNNING                             0\n#define MA_PA_SINK_IDLE                                1\n#define MA_PA_SINK_SUSPENDED                           2\n\ntypedef int ma_pa_source_state_t;\n#define MA_PA_SOURCE_INVALID_STATE                     -1\n#define MA_PA_SOURCE_RUNNING                           0\n#define MA_PA_SOURCE_IDLE                              1\n#define MA_PA_SOURCE_SUSPENDED                         2\n\ntypedef int ma_pa_seek_mode_t;\n#define MA_PA_SEEK_RELATIVE                            0\n#define MA_PA_SEEK_ABSOLUTE                            1\n#define MA_PA_SEEK_RELATIVE_ON_READ                    2\n#define MA_PA_SEEK_RELATIVE_END                        3\n\ntypedef int ma_pa_channel_position_t;\n#define MA_PA_CHANNEL_POSITION_INVALID                 -1\n#define MA_PA_CHANNEL_POSITION_MONO                    0\n#define MA_PA_CHANNEL_POSITION_FRONT_LEFT              1\n#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT             2\n#define MA_PA_CHANNEL_POSITION_FRONT_CENTER            3\n#define MA_PA_CHANNEL_POSITION_REAR_CENTER             4\n#define MA_PA_CHANNEL_POSITION_REAR_LEFT               5\n#define MA_PA_CHANNEL_POSITION_REAR_RIGHT              6\n#define MA_PA_CHANNEL_POSITION_LFE                     7\n#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER    8\n#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER   9\n#define MA_PA_CHANNEL_POSITION_SIDE_LEFT               10\n#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT              11\n#define MA_PA_CHANNEL_POSITION_AUX0                    12\n#define MA_PA_CHANNEL_POSITION_AUX1                    13\n#define MA_PA_CHANNEL_POSITION_AUX2                    14\n#define MA_PA_CHANNEL_POSITION_AUX3                    15\n#define MA_PA_CHANNEL_POSITION_AUX4                    16\n#define MA_PA_CHANNEL_POSITION_AUX5                    17\n#define MA_PA_CHANNEL_POSITION_AUX6                    18\n#define MA_PA_CHANNEL_POSITION_AUX7                    19\n#define MA_PA_CHANNEL_POSITION_AUX8                    20\n#define MA_PA_CHANNEL_POSITION_AUX9                    21\n#define MA_PA_CHANNEL_POSITION_AUX10                   22\n#define MA_PA_CHANNEL_POSITION_AUX11                   23\n#define MA_PA_CHANNEL_POSITION_AUX12                   24\n#define MA_PA_CHANNEL_POSITION_AUX13                   25\n#define MA_PA_CHANNEL_POSITION_AUX14                   26\n#define MA_PA_CHANNEL_POSITION_AUX15                   27\n#define MA_PA_CHANNEL_POSITION_AUX16                   28\n#define MA_PA_CHANNEL_POSITION_AUX17                   29\n#define MA_PA_CHANNEL_POSITION_AUX18                   30\n#define MA_PA_CHANNEL_POSITION_AUX19                   31\n#define MA_PA_CHANNEL_POSITION_AUX20                   32\n#define MA_PA_CHANNEL_POSITION_AUX21                   33\n#define MA_PA_CHANNEL_POSITION_AUX22                   34\n#define MA_PA_CHANNEL_POSITION_AUX23                   35\n#define MA_PA_CHANNEL_POSITION_AUX24                   36\n#define MA_PA_CHANNEL_POSITION_AUX25                   37\n#define MA_PA_CHANNEL_POSITION_AUX26                   38\n#define MA_PA_CHANNEL_POSITION_AUX27                   39\n#define MA_PA_CHANNEL_POSITION_AUX28                   40\n#define MA_PA_CHANNEL_POSITION_AUX29                   41\n#define MA_PA_CHANNEL_POSITION_AUX30                   42\n#define MA_PA_CHANNEL_POSITION_AUX31                   43\n#define MA_PA_CHANNEL_POSITION_TOP_CENTER              44\n#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT          45\n#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT         46\n#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER        47\n#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT           48\n#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT          49\n#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER         50\n#define MA_PA_CHANNEL_POSITION_LEFT                    MA_PA_CHANNEL_POSITION_FRONT_LEFT\n#define MA_PA_CHANNEL_POSITION_RIGHT                   MA_PA_CHANNEL_POSITION_FRONT_RIGHT\n#define MA_PA_CHANNEL_POSITION_CENTER                  MA_PA_CHANNEL_POSITION_FRONT_CENTER\n#define MA_PA_CHANNEL_POSITION_SUBWOOFER               MA_PA_CHANNEL_POSITION_LFE\n\ntypedef int ma_pa_channel_map_def_t;\n#define MA_PA_CHANNEL_MAP_AIFF                         0\n#define MA_PA_CHANNEL_MAP_ALSA                         1\n#define MA_PA_CHANNEL_MAP_AUX                          2\n#define MA_PA_CHANNEL_MAP_WAVEEX                       3\n#define MA_PA_CHANNEL_MAP_OSS                          4\n#define MA_PA_CHANNEL_MAP_DEFAULT                      MA_PA_CHANNEL_MAP_AIFF\n\ntypedef int ma_pa_sample_format_t;\n#define MA_PA_SAMPLE_INVALID                           -1\n#define MA_PA_SAMPLE_U8                                0\n#define MA_PA_SAMPLE_ALAW                              1\n#define MA_PA_SAMPLE_ULAW                              2\n#define MA_PA_SAMPLE_S16LE                             3\n#define MA_PA_SAMPLE_S16BE                             4\n#define MA_PA_SAMPLE_FLOAT32LE                         5\n#define MA_PA_SAMPLE_FLOAT32BE                         6\n#define MA_PA_SAMPLE_S32LE                             7\n#define MA_PA_SAMPLE_S32BE                             8\n#define MA_PA_SAMPLE_S24LE                             9\n#define MA_PA_SAMPLE_S24BE                             10\n#define MA_PA_SAMPLE_S24_32LE                          11\n#define MA_PA_SAMPLE_S24_32BE                          12\n\ntypedef struct ma_pa_mainloop           ma_pa_mainloop;\ntypedef struct ma_pa_threaded_mainloop  ma_pa_threaded_mainloop;\ntypedef struct ma_pa_mainloop_api       ma_pa_mainloop_api;\ntypedef struct ma_pa_context            ma_pa_context;\ntypedef struct ma_pa_operation          ma_pa_operation;\ntypedef struct ma_pa_stream             ma_pa_stream;\ntypedef struct ma_pa_spawn_api          ma_pa_spawn_api;\n\ntypedef struct\n{\n    ma_uint32 maxlength;\n    ma_uint32 tlength;\n    ma_uint32 prebuf;\n    ma_uint32 minreq;\n    ma_uint32 fragsize;\n} ma_pa_buffer_attr;\n\ntypedef struct\n{\n    ma_uint8 channels;\n    ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];\n} ma_pa_channel_map;\n\ntypedef struct\n{\n    ma_uint8 channels;\n    ma_uint32 values[MA_PA_CHANNELS_MAX];\n} ma_pa_cvolume;\n\ntypedef struct\n{\n    ma_pa_sample_format_t format;\n    ma_uint32 rate;\n    ma_uint8 channels;\n} ma_pa_sample_spec;\n\ntypedef struct\n{\n    const char* name;\n    ma_uint32 index;\n    const char* description;\n    ma_pa_sample_spec sample_spec;\n    ma_pa_channel_map channel_map;\n    ma_uint32 owner_module;\n    ma_pa_cvolume volume;\n    int mute;\n    ma_uint32 monitor_source;\n    const char* monitor_source_name;\n    ma_uint64 latency;\n    const char* driver;\n    ma_pa_sink_flags_t flags;\n    void* proplist;\n    ma_uint64 configured_latency;\n    ma_uint32 base_volume;\n    ma_pa_sink_state_t state;\n    ma_uint32 n_volume_steps;\n    ma_uint32 card;\n    ma_uint32 n_ports;\n    void** ports;\n    void* active_port;\n    ma_uint8 n_formats;\n    void** formats;\n} ma_pa_sink_info;\n\ntypedef struct\n{\n    const char *name;\n    ma_uint32 index;\n    const char *description;\n    ma_pa_sample_spec sample_spec;\n    ma_pa_channel_map channel_map;\n    ma_uint32 owner_module;\n    ma_pa_cvolume volume;\n    int mute;\n    ma_uint32 monitor_of_sink;\n    const char *monitor_of_sink_name;\n    ma_uint64 latency;\n    const char *driver;\n    ma_pa_source_flags_t flags;\n    void* proplist;\n    ma_uint64 configured_latency;\n    ma_uint32 base_volume;\n    ma_pa_source_state_t state;\n    ma_uint32 n_volume_steps;\n    ma_uint32 card;\n    ma_uint32 n_ports;\n    void** ports;\n    void* active_port;\n    ma_uint8 n_formats;\n    void** formats;\n} ma_pa_source_info;\n\ntypedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);\ntypedef void (* ma_pa_sink_info_cb_t)     (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);\ntypedef void (* ma_pa_source_info_cb_t)   (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);\ntypedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);\ntypedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);\ntypedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata);\ntypedef void (* ma_pa_free_cb_t)          (void* p);\n#endif\n\n\ntypedef ma_pa_mainloop*          (* ma_pa_mainloop_new_proc)                   (void);\ntypedef void                     (* ma_pa_mainloop_free_proc)                  (ma_pa_mainloop* m);\ntypedef void                     (* ma_pa_mainloop_quit_proc)                  (ma_pa_mainloop* m, int retval);\ntypedef ma_pa_mainloop_api*      (* ma_pa_mainloop_get_api_proc)               (ma_pa_mainloop* m);\ntypedef int                      (* ma_pa_mainloop_iterate_proc)               (ma_pa_mainloop* m, int block, int* retval);\ntypedef void                     (* ma_pa_mainloop_wakeup_proc)                (ma_pa_mainloop* m);\ntypedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc)          (void);\ntypedef void                     (* ma_pa_threaded_mainloop_free_proc)         (ma_pa_threaded_mainloop* m);\ntypedef int                      (* ma_pa_threaded_mainloop_start_proc)        (ma_pa_threaded_mainloop* m);\ntypedef void                     (* ma_pa_threaded_mainloop_stop_proc)         (ma_pa_threaded_mainloop* m);\ntypedef void                     (* ma_pa_threaded_mainloop_lock_proc)         (ma_pa_threaded_mainloop* m);\ntypedef void                     (* ma_pa_threaded_mainloop_unlock_proc)       (ma_pa_threaded_mainloop* m);\ntypedef void                     (* ma_pa_threaded_mainloop_wait_proc)         (ma_pa_threaded_mainloop* m);\ntypedef void                     (* ma_pa_threaded_mainloop_signal_proc)       (ma_pa_threaded_mainloop* m, int wait_for_accept);\ntypedef void                     (* ma_pa_threaded_mainloop_accept_proc)       (ma_pa_threaded_mainloop* m);\ntypedef int                      (* ma_pa_threaded_mainloop_get_retval_proc)   (ma_pa_threaded_mainloop* m);\ntypedef ma_pa_mainloop_api*      (* ma_pa_threaded_mainloop_get_api_proc)      (ma_pa_threaded_mainloop* m);\ntypedef int                      (* ma_pa_threaded_mainloop_in_thread_proc)    (ma_pa_threaded_mainloop* m);\ntypedef void                     (* ma_pa_threaded_mainloop_set_name_proc)     (ma_pa_threaded_mainloop* m, const char* name);\ntypedef ma_pa_context*           (* ma_pa_context_new_proc)                    (ma_pa_mainloop_api* mainloop, const char* name);\ntypedef void                     (* ma_pa_context_unref_proc)                  (ma_pa_context* c);\ntypedef int                      (* ma_pa_context_connect_proc)                (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api);\ntypedef void                     (* ma_pa_context_disconnect_proc)             (ma_pa_context* c);\ntypedef void                     (* ma_pa_context_set_state_callback_proc)     (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);\ntypedef ma_pa_context_state_t    (* ma_pa_context_get_state_proc)              (ma_pa_context* c);\ntypedef ma_pa_operation*         (* ma_pa_context_get_sink_info_list_proc)     (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata);\ntypedef ma_pa_operation*         (* ma_pa_context_get_source_info_list_proc)   (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata);\ntypedef ma_pa_operation*         (* ma_pa_context_get_sink_info_by_name_proc)  (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata);\ntypedef ma_pa_operation*         (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata);\ntypedef void                     (* ma_pa_operation_unref_proc)                (ma_pa_operation* o);\ntypedef ma_pa_operation_state_t  (* ma_pa_operation_get_state_proc)            (ma_pa_operation* o);\ntypedef ma_pa_channel_map*       (* ma_pa_channel_map_init_extend_proc)        (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def);\ntypedef int                      (* ma_pa_channel_map_valid_proc)              (const ma_pa_channel_map* m);\ntypedef int                      (* ma_pa_channel_map_compatible_proc)         (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);\ntypedef ma_pa_stream*            (* ma_pa_stream_new_proc)                     (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map);\ntypedef void                     (* ma_pa_stream_unref_proc)                   (ma_pa_stream* s);\ntypedef int                      (* ma_pa_stream_connect_playback_proc)        (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream);\ntypedef int                      (* ma_pa_stream_connect_record_proc)          (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags);\ntypedef int                      (* ma_pa_stream_disconnect_proc)              (ma_pa_stream* s);\ntypedef ma_pa_stream_state_t     (* ma_pa_stream_get_state_proc)               (ma_pa_stream* s);\ntypedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc)         (ma_pa_stream* s);\ntypedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc)         (ma_pa_stream* s);\ntypedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc)         (ma_pa_stream* s);\ntypedef ma_pa_operation*         (* ma_pa_stream_set_buffer_attr_proc)         (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata);\ntypedef const char*              (* ma_pa_stream_get_device_name_proc)         (ma_pa_stream* s);\ntypedef void                     (* ma_pa_stream_set_write_callback_proc)      (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);\ntypedef void                     (* ma_pa_stream_set_read_callback_proc)       (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);\ntypedef void                     (* ma_pa_stream_set_suspended_callback_proc)  (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);\ntypedef void                     (* ma_pa_stream_set_moved_callback_proc)      (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);\ntypedef int                      (* ma_pa_stream_is_suspended_proc)            (const ma_pa_stream* s);\ntypedef ma_pa_operation*         (* ma_pa_stream_flush_proc)                   (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);\ntypedef ma_pa_operation*         (* ma_pa_stream_drain_proc)                   (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);\ntypedef int                      (* ma_pa_stream_is_corked_proc)               (ma_pa_stream* s);\ntypedef ma_pa_operation*         (* ma_pa_stream_cork_proc)                    (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);\ntypedef ma_pa_operation*         (* ma_pa_stream_trigger_proc)                 (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);\ntypedef int                      (* ma_pa_stream_begin_write_proc)             (ma_pa_stream* s, void** data, size_t* nbytes);\ntypedef int                      (* ma_pa_stream_write_proc)                   (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek);\ntypedef int                      (* ma_pa_stream_peek_proc)                    (ma_pa_stream* s, const void** data, size_t* nbytes);\ntypedef int                      (* ma_pa_stream_drop_proc)                    (ma_pa_stream* s);\ntypedef size_t                   (* ma_pa_stream_writable_size_proc)           (ma_pa_stream* s);\ntypedef size_t                   (* ma_pa_stream_readable_size_proc)           (ma_pa_stream* s);\n\ntypedef struct\n{\n    ma_uint32 count;\n    ma_uint32 capacity;\n    ma_device_info* pInfo;\n} ma_pulse_device_enum_data;\n\nstatic ma_result ma_result_from_pulse(int result)\n{\n    if (result < 0) {\n        return MA_ERROR;\n    }\n\n    switch (result) {\n        case MA_PA_OK:           return MA_SUCCESS;\n        case MA_PA_ERR_ACCESS:   return MA_ACCESS_DENIED;\n        case MA_PA_ERR_INVALID:  return MA_INVALID_ARGS;\n        case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;\n        default:                 return MA_ERROR;\n    }\n}\n\n#if 0\nstatic ma_pa_sample_format_t ma_format_to_pulse(ma_format format)\n{\n    if (ma_is_little_endian()) {\n        switch (format) {\n            case ma_format_s16: return MA_PA_SAMPLE_S16LE;\n            case ma_format_s24: return MA_PA_SAMPLE_S24LE;\n            case ma_format_s32: return MA_PA_SAMPLE_S32LE;\n            case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;\n            default: break;\n        }\n    } else {\n        switch (format) {\n            case ma_format_s16: return MA_PA_SAMPLE_S16BE;\n            case ma_format_s24: return MA_PA_SAMPLE_S24BE;\n            case ma_format_s32: return MA_PA_SAMPLE_S32BE;\n            case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;\n            default: break;\n        }\n    }\n\n    /* Endian agnostic. */\n    switch (format) {\n        case ma_format_u8: return MA_PA_SAMPLE_U8;\n        default: return MA_PA_SAMPLE_INVALID;\n    }\n}\n#endif\n\nstatic ma_format ma_format_from_pulse(ma_pa_sample_format_t format)\n{\n    if (ma_is_little_endian()) {\n        switch (format) {\n            case MA_PA_SAMPLE_S16LE:     return ma_format_s16;\n            case MA_PA_SAMPLE_S24LE:     return ma_format_s24;\n            case MA_PA_SAMPLE_S32LE:     return ma_format_s32;\n            case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;\n            default: break;\n        }\n    } else {\n        switch (format) {\n            case MA_PA_SAMPLE_S16BE:     return ma_format_s16;\n            case MA_PA_SAMPLE_S24BE:     return ma_format_s24;\n            case MA_PA_SAMPLE_S32BE:     return ma_format_s32;\n            case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;\n            default: break;\n        }\n    }\n\n    /* Endian agnostic. */\n    switch (format) {\n        case MA_PA_SAMPLE_U8: return ma_format_u8;\n        default: return ma_format_unknown;\n    }\n}\n\nstatic ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)\n{\n    switch (position)\n    {\n        case MA_PA_CHANNEL_POSITION_INVALID:               return MA_CHANNEL_NONE;\n        case MA_PA_CHANNEL_POSITION_MONO:                  return MA_CHANNEL_MONO;\n        case MA_PA_CHANNEL_POSITION_FRONT_LEFT:            return MA_CHANNEL_FRONT_LEFT;\n        case MA_PA_CHANNEL_POSITION_FRONT_RIGHT:           return MA_CHANNEL_FRONT_RIGHT;\n        case MA_PA_CHANNEL_POSITION_FRONT_CENTER:          return MA_CHANNEL_FRONT_CENTER;\n        case MA_PA_CHANNEL_POSITION_REAR_CENTER:           return MA_CHANNEL_BACK_CENTER;\n        case MA_PA_CHANNEL_POSITION_REAR_LEFT:             return MA_CHANNEL_BACK_LEFT;\n        case MA_PA_CHANNEL_POSITION_REAR_RIGHT:            return MA_CHANNEL_BACK_RIGHT;\n        case MA_PA_CHANNEL_POSITION_LFE:                   return MA_CHANNEL_LFE;\n        case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:  return MA_CHANNEL_FRONT_LEFT_CENTER;\n        case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;\n        case MA_PA_CHANNEL_POSITION_SIDE_LEFT:             return MA_CHANNEL_SIDE_LEFT;\n        case MA_PA_CHANNEL_POSITION_SIDE_RIGHT:            return MA_CHANNEL_SIDE_RIGHT;\n        case MA_PA_CHANNEL_POSITION_AUX0:                  return MA_CHANNEL_AUX_0;\n        case MA_PA_CHANNEL_POSITION_AUX1:                  return MA_CHANNEL_AUX_1;\n        case MA_PA_CHANNEL_POSITION_AUX2:                  return MA_CHANNEL_AUX_2;\n        case MA_PA_CHANNEL_POSITION_AUX3:                  return MA_CHANNEL_AUX_3;\n        case MA_PA_CHANNEL_POSITION_AUX4:                  return MA_CHANNEL_AUX_4;\n        case MA_PA_CHANNEL_POSITION_AUX5:                  return MA_CHANNEL_AUX_5;\n        case MA_PA_CHANNEL_POSITION_AUX6:                  return MA_CHANNEL_AUX_6;\n        case MA_PA_CHANNEL_POSITION_AUX7:                  return MA_CHANNEL_AUX_7;\n        case MA_PA_CHANNEL_POSITION_AUX8:                  return MA_CHANNEL_AUX_8;\n        case MA_PA_CHANNEL_POSITION_AUX9:                  return MA_CHANNEL_AUX_9;\n        case MA_PA_CHANNEL_POSITION_AUX10:                 return MA_CHANNEL_AUX_10;\n        case MA_PA_CHANNEL_POSITION_AUX11:                 return MA_CHANNEL_AUX_11;\n        case MA_PA_CHANNEL_POSITION_AUX12:                 return MA_CHANNEL_AUX_12;\n        case MA_PA_CHANNEL_POSITION_AUX13:                 return MA_CHANNEL_AUX_13;\n        case MA_PA_CHANNEL_POSITION_AUX14:                 return MA_CHANNEL_AUX_14;\n        case MA_PA_CHANNEL_POSITION_AUX15:                 return MA_CHANNEL_AUX_15;\n        case MA_PA_CHANNEL_POSITION_AUX16:                 return MA_CHANNEL_AUX_16;\n        case MA_PA_CHANNEL_POSITION_AUX17:                 return MA_CHANNEL_AUX_17;\n        case MA_PA_CHANNEL_POSITION_AUX18:                 return MA_CHANNEL_AUX_18;\n        case MA_PA_CHANNEL_POSITION_AUX19:                 return MA_CHANNEL_AUX_19;\n        case MA_PA_CHANNEL_POSITION_AUX20:                 return MA_CHANNEL_AUX_20;\n        case MA_PA_CHANNEL_POSITION_AUX21:                 return MA_CHANNEL_AUX_21;\n        case MA_PA_CHANNEL_POSITION_AUX22:                 return MA_CHANNEL_AUX_22;\n        case MA_PA_CHANNEL_POSITION_AUX23:                 return MA_CHANNEL_AUX_23;\n        case MA_PA_CHANNEL_POSITION_AUX24:                 return MA_CHANNEL_AUX_24;\n        case MA_PA_CHANNEL_POSITION_AUX25:                 return MA_CHANNEL_AUX_25;\n        case MA_PA_CHANNEL_POSITION_AUX26:                 return MA_CHANNEL_AUX_26;\n        case MA_PA_CHANNEL_POSITION_AUX27:                 return MA_CHANNEL_AUX_27;\n        case MA_PA_CHANNEL_POSITION_AUX28:                 return MA_CHANNEL_AUX_28;\n        case MA_PA_CHANNEL_POSITION_AUX29:                 return MA_CHANNEL_AUX_29;\n        case MA_PA_CHANNEL_POSITION_AUX30:                 return MA_CHANNEL_AUX_30;\n        case MA_PA_CHANNEL_POSITION_AUX31:                 return MA_CHANNEL_AUX_31;\n        case MA_PA_CHANNEL_POSITION_TOP_CENTER:            return MA_CHANNEL_TOP_CENTER;\n        case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT:        return MA_CHANNEL_TOP_FRONT_LEFT;\n        case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:       return MA_CHANNEL_TOP_FRONT_RIGHT;\n        case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER:      return MA_CHANNEL_TOP_FRONT_CENTER;\n        case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT:         return MA_CHANNEL_TOP_BACK_LEFT;\n        case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT:        return MA_CHANNEL_TOP_BACK_RIGHT;\n        case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER:       return MA_CHANNEL_TOP_BACK_CENTER;\n        default: return MA_CHANNEL_NONE;\n    }\n}\n\n#if 0\nstatic ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)\n{\n    switch (position)\n    {\n        case MA_CHANNEL_NONE:               return MA_PA_CHANNEL_POSITION_INVALID;\n        case MA_CHANNEL_FRONT_LEFT:         return MA_PA_CHANNEL_POSITION_FRONT_LEFT;\n        case MA_CHANNEL_FRONT_RIGHT:        return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;\n        case MA_CHANNEL_FRONT_CENTER:       return MA_PA_CHANNEL_POSITION_FRONT_CENTER;\n        case MA_CHANNEL_LFE:                return MA_PA_CHANNEL_POSITION_LFE;\n        case MA_CHANNEL_BACK_LEFT:          return MA_PA_CHANNEL_POSITION_REAR_LEFT;\n        case MA_CHANNEL_BACK_RIGHT:         return MA_PA_CHANNEL_POSITION_REAR_RIGHT;\n        case MA_CHANNEL_FRONT_LEFT_CENTER:  return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;\n        case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;\n        case MA_CHANNEL_BACK_CENTER:        return MA_PA_CHANNEL_POSITION_REAR_CENTER;\n        case MA_CHANNEL_SIDE_LEFT:          return MA_PA_CHANNEL_POSITION_SIDE_LEFT;\n        case MA_CHANNEL_SIDE_RIGHT:         return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;\n        case MA_CHANNEL_TOP_CENTER:         return MA_PA_CHANNEL_POSITION_TOP_CENTER;\n        case MA_CHANNEL_TOP_FRONT_LEFT:     return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;\n        case MA_CHANNEL_TOP_FRONT_CENTER:   return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;\n        case MA_CHANNEL_TOP_FRONT_RIGHT:    return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;\n        case MA_CHANNEL_TOP_BACK_LEFT:      return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;\n        case MA_CHANNEL_TOP_BACK_CENTER:    return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;\n        case MA_CHANNEL_TOP_BACK_RIGHT:     return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;\n        case MA_CHANNEL_19:                 return MA_PA_CHANNEL_POSITION_AUX18;\n        case MA_CHANNEL_20:                 return MA_PA_CHANNEL_POSITION_AUX19;\n        case MA_CHANNEL_21:                 return MA_PA_CHANNEL_POSITION_AUX20;\n        case MA_CHANNEL_22:                 return MA_PA_CHANNEL_POSITION_AUX21;\n        case MA_CHANNEL_23:                 return MA_PA_CHANNEL_POSITION_AUX22;\n        case MA_CHANNEL_24:                 return MA_PA_CHANNEL_POSITION_AUX23;\n        case MA_CHANNEL_25:                 return MA_PA_CHANNEL_POSITION_AUX24;\n        case MA_CHANNEL_26:                 return MA_PA_CHANNEL_POSITION_AUX25;\n        case MA_CHANNEL_27:                 return MA_PA_CHANNEL_POSITION_AUX26;\n        case MA_CHANNEL_28:                 return MA_PA_CHANNEL_POSITION_AUX27;\n        case MA_CHANNEL_29:                 return MA_PA_CHANNEL_POSITION_AUX28;\n        case MA_CHANNEL_30:                 return MA_PA_CHANNEL_POSITION_AUX29;\n        case MA_CHANNEL_31:                 return MA_PA_CHANNEL_POSITION_AUX30;\n        case MA_CHANNEL_32:                 return MA_PA_CHANNEL_POSITION_AUX31;\n        default: return (ma_pa_channel_position_t)position;\n    }\n}\n#endif\n\nstatic ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)\n{\n    int resultPA;\n    ma_pa_operation_state_t state;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pOP != NULL);\n\n    for (;;) {\n        state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP);\n        if (state != MA_PA_OPERATION_RUNNING) {\n            break;  /* Done. */\n        }\n\n        resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);\n        if (resultPA < 0) {\n            return ma_result_from_pulse(resultPA);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)\n{\n    ma_result result;\n\n    if (pOP == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);\n    ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);\n\n    return result;\n}\n\nstatic ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext)\n{\n    int resultPA;\n    ma_pa_context_state_t state;\n\n    for (;;) {\n        state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext);\n        if (state == MA_PA_CONTEXT_READY) {\n            break;  /* Done. */\n        }\n\n        if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[PulseAudio] An error occurred while connecting the PulseAudio context.\");\n            return MA_ERROR;\n        }\n\n        resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);\n        if (resultPA < 0) {\n            return ma_result_from_pulse(resultPA);\n        }\n    }\n\n    /* Should never get here. */\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream)\n{\n    int resultPA;\n    ma_pa_stream_state_t state;\n\n    for (;;) {\n        state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream);\n        if (state == MA_PA_STREAM_READY) {\n            break;  /* Done. */\n        }\n\n        if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[PulseAudio] An error occurred while connecting the PulseAudio stream.\");\n            return MA_ERROR;\n        }\n\n        resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);\n        if (resultPA < 0) {\n            return ma_result_from_pulse(resultPA);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext)\n{\n    ma_result result;\n    ma_ptr pMainLoop;\n    ma_ptr pPulseContext;\n\n    MA_ASSERT(ppMainLoop     != NULL);\n    MA_ASSERT(ppPulseContext != NULL);\n\n    /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */\n    pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();\n    if (pMainLoop == NULL) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to create mainloop.\");\n        return MA_FAILED_TO_INIT_BACKEND;\n    }\n\n    pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName);\n    if (pPulseContext == NULL) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to create PulseAudio context.\");\n        ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));\n        return MA_FAILED_TO_INIT_BACKEND;\n    }\n\n    /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */\n    result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL));\n    if (result != MA_SUCCESS) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to connect PulseAudio context.\");\n        ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));\n        return result;\n    }\n\n    /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */\n    result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext);\n    if (result != MA_SUCCESS) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Waiting for connection failed.\");\n        ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));\n        return result;\n    }\n\n    *ppMainLoop     = pMainLoop;\n    *ppPulseContext = pPulseContext;\n\n    return MA_SUCCESS;\n}\n\n\nstatic void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)\n{\n    ma_pa_sink_info* pInfoOut;\n\n    if (endOfList > 0) {\n        return;\n    }\n\n    /*\n    There has been a report that indicates that pInfo can be null which results\n    in a null pointer dereference below. We'll check for this for safety.\n    */\n    if (pInfo == NULL) {\n        return;\n    }\n\n    pInfoOut = (ma_pa_sink_info*)pUserData;\n    MA_ASSERT(pInfoOut != NULL);\n\n    *pInfoOut = *pInfo;\n\n    (void)pPulseContext; /* Unused. */\n}\n\nstatic void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)\n{\n    ma_pa_source_info* pInfoOut;\n\n    if (endOfList > 0) {\n        return;\n    }\n\n    /*\n    There has been a report that indicates that pInfo can be null which results\n    in a null pointer dereference below. We'll check for this for safety.\n    */\n    if (pInfo == NULL) {\n        return;\n    }\n\n    pInfoOut = (ma_pa_source_info*)pUserData;\n    MA_ASSERT(pInfoOut != NULL);\n\n    *pInfoOut = *pInfo;\n\n    (void)pPulseContext; /* Unused. */\n}\n\n#if 0\nstatic void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)\n{\n    ma_device* pDevice;\n\n    if (endOfList > 0) {\n        return;\n    }\n\n    pDevice = (ma_device*)pUserData;\n    MA_ASSERT(pDevice != NULL);\n\n    ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);\n\n    (void)pPulseContext; /* Unused. */\n}\n\nstatic void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)\n{\n    ma_device* pDevice;\n\n    if (endOfList > 0) {\n        return;\n    }\n\n    pDevice = (ma_device*)pUserData;\n    MA_ASSERT(pDevice != NULL);\n\n    ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);\n\n    (void)pPulseContext; /* Unused. */\n}\n#endif\n\nstatic ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo)\n{\n    ma_pa_operation* pOP;\n\n    pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo);\n    if (pOP == NULL) {\n        return MA_ERROR;\n    }\n\n    return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);\n}\n\nstatic ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo)\n{\n    ma_pa_operation* pOP;\n\n    pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo);\n    if (pOP == NULL) {\n        return MA_ERROR;\n    }\n\n    return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);\n}\n\nstatic ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex)\n{\n    ma_result result;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pIndex   != NULL);\n\n    if (pIndex != NULL) {\n        *pIndex = (ma_uint32)-1;\n    }\n\n    if (deviceType == ma_device_type_playback) {\n        ma_pa_sink_info sinkInfo;\n        result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        if (pIndex != NULL) {\n            *pIndex = sinkInfo.index;\n        }\n    }\n\n    if (deviceType == ma_device_type_capture) {\n        ma_pa_source_info sourceInfo;\n        result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        if (pIndex != NULL) {\n            *pIndex = sourceInfo.index;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n\ntypedef struct\n{\n    ma_context* pContext;\n    ma_enum_devices_callback_proc callback;\n    void* pUserData;\n    ma_bool32 isTerminated;\n    ma_uint32 defaultDeviceIndexPlayback;\n    ma_uint32 defaultDeviceIndexCapture;\n} ma_context_enumerate_devices_callback_data__pulse;\n\nstatic void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)\n{\n    ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;\n    ma_device_info deviceInfo;\n\n    MA_ASSERT(pData != NULL);\n\n    if (endOfList || pData->isTerminated) {\n        return;\n    }\n\n    MA_ZERO_OBJECT(&deviceInfo);\n\n    /* The name from PulseAudio is the ID for miniaudio. */\n    if (pSinkInfo->name != NULL) {\n        ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);\n    }\n\n    /* The description from PulseAudio is the name for miniaudio. */\n    if (pSinkInfo->description != NULL) {\n        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);\n    }\n\n    if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) {\n        deviceInfo.isDefault = MA_TRUE;\n    }\n\n    pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);\n\n    (void)pPulseContext; /* Unused. */\n}\n\nstatic void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData)\n{\n    ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;\n    ma_device_info deviceInfo;\n\n    MA_ASSERT(pData != NULL);\n\n    if (endOfList || pData->isTerminated) {\n        return;\n    }\n\n    MA_ZERO_OBJECT(&deviceInfo);\n\n    /* The name from PulseAudio is the ID for miniaudio. */\n    if (pSourceInfo->name != NULL) {\n        ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1);\n    }\n\n    /* The description from PulseAudio is the name for miniaudio. */\n    if (pSourceInfo->description != NULL) {\n        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1);\n    }\n\n    if (pSourceInfo->index == pData->defaultDeviceIndexCapture) {\n        deviceInfo.isDefault = MA_TRUE;\n    }\n\n    pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);\n\n    (void)pPulseContext; /* Unused. */\n}\n\nstatic ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    ma_result result = MA_SUCCESS;\n    ma_context_enumerate_devices_callback_data__pulse callbackData;\n    ma_pa_operation* pOP = NULL;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    callbackData.pContext = pContext;\n    callbackData.callback = callback;\n    callbackData.pUserData = pUserData;\n    callbackData.isTerminated = MA_FALSE;\n    callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1;\n    callbackData.defaultDeviceIndexCapture  = (ma_uint32)-1;\n\n    /* We need to get the index of the default devices. */\n    ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback);\n    ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture,  &callbackData.defaultDeviceIndexCapture);\n\n    /* Playback. */\n    if (!callbackData.isTerminated) {\n        pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData);\n        if (pOP == NULL) {\n            result = MA_ERROR;\n            goto done;\n        }\n\n        result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);\n        ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);\n\n        if (result != MA_SUCCESS) {\n            goto done;\n        }\n    }\n\n\n    /* Capture. */\n    if (!callbackData.isTerminated) {\n        pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData);\n        if (pOP == NULL) {\n            result = MA_ERROR;\n            goto done;\n        }\n\n        result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);\n        ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);\n\n        if (result != MA_SUCCESS) {\n            goto done;\n        }\n    }\n\ndone:\n    return result;\n}\n\n\ntypedef struct\n{\n    ma_device_info* pDeviceInfo;\n    ma_uint32 defaultDeviceIndex;\n    ma_bool32 foundDevice;\n} ma_context_get_device_info_callback_data__pulse;\n\nstatic void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)\n{\n    ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;\n\n    if (endOfList > 0) {\n        return;\n    }\n\n    MA_ASSERT(pData != NULL);\n    pData->foundDevice = MA_TRUE;\n\n    if (pInfo->name != NULL) {\n        ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);\n    }\n\n    if (pInfo->description != NULL) {\n        ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);\n    }\n\n    /*\n    We're just reporting a single data format here. I think technically PulseAudio might support\n    all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to\n    report the \"native\" device format.\n    */\n    pData->pDeviceInfo->nativeDataFormats[0].format     = ma_format_from_pulse(pInfo->sample_spec.format);\n    pData->pDeviceInfo->nativeDataFormats[0].channels   = pInfo->sample_spec.channels;\n    pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;\n    pData->pDeviceInfo->nativeDataFormats[0].flags      = 0;\n    pData->pDeviceInfo->nativeDataFormatCount = 1;\n\n    if (pData->defaultDeviceIndex == pInfo->index) {\n        pData->pDeviceInfo->isDefault = MA_TRUE;\n    }\n\n    (void)pPulseContext; /* Unused. */\n}\n\nstatic void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)\n{\n    ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;\n\n    if (endOfList > 0) {\n        return;\n    }\n\n    MA_ASSERT(pData != NULL);\n    pData->foundDevice = MA_TRUE;\n\n    if (pInfo->name != NULL) {\n        ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);\n    }\n\n    if (pInfo->description != NULL) {\n        ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);\n    }\n\n    /*\n    We're just reporting a single data format here. I think technically PulseAudio might support\n    all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to\n    report the \"native\" device format.\n    */\n    pData->pDeviceInfo->nativeDataFormats[0].format     = ma_format_from_pulse(pInfo->sample_spec.format);\n    pData->pDeviceInfo->nativeDataFormats[0].channels   = pInfo->sample_spec.channels;\n    pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;\n    pData->pDeviceInfo->nativeDataFormats[0].flags      = 0;\n    pData->pDeviceInfo->nativeDataFormatCount = 1;\n\n    if (pData->defaultDeviceIndex == pInfo->index) {\n        pData->pDeviceInfo->isDefault = MA_TRUE;\n    }\n\n    (void)pPulseContext; /* Unused. */\n}\n\nstatic ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    ma_result result = MA_SUCCESS;\n    ma_context_get_device_info_callback_data__pulse callbackData;\n    ma_pa_operation* pOP = NULL;\n    const char* pDeviceName = NULL;\n\n    MA_ASSERT(pContext != NULL);\n\n    callbackData.pDeviceInfo = pDeviceInfo;\n    callbackData.foundDevice = MA_FALSE;\n\n    if (pDeviceID != NULL) {\n        pDeviceName = pDeviceID->pulse;\n    } else {\n        pDeviceName = NULL;\n    }\n\n    result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex);\n\n    if (deviceType == ma_device_type_playback) {\n        pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData);\n    } else {\n        pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData);\n    }\n\n    if (pOP != NULL) {\n        ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);\n    } else {\n        result = MA_ERROR;\n        goto done;\n    }\n\n    if (!callbackData.foundDevice) {\n        result = MA_NO_DEVICE;\n        goto done;\n    }\n\ndone:\n    return result;\n}\n\nstatic ma_result ma_device_uninit__pulse(ma_device* pDevice)\n{\n    ma_context* pContext;\n\n    MA_ASSERT(pDevice != NULL);\n\n    pContext = pDevice->pContext;\n    MA_ASSERT(pContext != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);\n        ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);\n        ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);\n    }\n\n    if (pDevice->type == ma_device_type_duplex) {\n        ma_duplex_rb_uninit(&pDevice->duplexRB);\n    }\n\n    ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);\n    ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);\n    ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)\n{\n    ma_pa_buffer_attr attr;\n    attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels);\n    attr.tlength   = attr.maxlength / periods;\n    attr.prebuf    = (ma_uint32)-1;\n    attr.minreq    = (ma_uint32)-1;\n    attr.fragsize  = attr.maxlength / periods;\n\n    return attr;\n}\n\nstatic ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap)\n{\n    static int g_StreamCounter = 0;\n    char actualStreamName[256];\n\n    if (pStreamName != NULL) {\n        ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);\n    } else {\n        ma_strcpy_s(actualStreamName, sizeof(actualStreamName), \"miniaudio:\");\n        ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10);  /* 8 = strlen(\"miniaudio:\") */\n    }\n    g_StreamCounter += 1;\n\n    return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);\n}\n\n\nstatic void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    ma_uint32 bpf;\n    ma_uint32 deviceState;\n    ma_uint64 frameCount;\n    ma_uint64 framesProcessed;\n\n    MA_ASSERT(pDevice != NULL);\n\n    /*\n    Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio\n    can fire this callback before the stream has even started. Ridiculous.\n    */\n    deviceState = ma_device_get_state(pDevice);\n    if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {\n        return;\n    }\n\n    bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);\n    MA_ASSERT(bpf > 0);\n\n    frameCount = byteCount / bpf;\n    framesProcessed = 0;\n\n    while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) {\n        const void* pMappedPCMFrames;\n        size_t bytesMapped;\n        ma_uint64 framesMapped;\n\n        int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped);\n        if (pulseResult < 0) {\n            break; /* Failed to map. Abort. */\n        }\n\n        framesMapped = bytesMapped / bpf;\n        if (framesMapped > 0) {\n            if (pMappedPCMFrames != NULL) {\n                ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped);\n            } else {\n                /* It's a hole. */\n                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[PulseAudio] ma_device_on_read__pulse: Hole.\\n\");\n            }\n\n            pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream);\n            if (pulseResult < 0) {\n                break;  /* Failed to drop the buffer. */\n            }\n\n            framesProcessed += framesMapped;\n\n        } else {\n            /* Nothing was mapped. Just abort. */\n            break;\n        }\n    }\n}\n\nstatic ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint64 framesProcessed = 0;\n    size_t bytesMapped;\n    ma_uint32 bpf;\n    ma_uint32 deviceState;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(pStream != NULL);\n\n    bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);\n    MA_ASSERT(bpf > 0);\n\n    deviceState = ma_device_get_state(pDevice);\n\n    bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream);\n    if (bytesMapped != (size_t)-1) {\n        if (bytesMapped > 0) {\n            ma_uint64 framesMapped;\n            void* pMappedPCMFrames;\n            int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped);\n            if (pulseResult < 0) {\n                result = ma_result_from_pulse(pulseResult);\n                goto done;\n            }\n\n            framesMapped = bytesMapped / bpf;\n\n            if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) {  /* Check for starting state just in case this is being used to do the initial fill. */\n                ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped);\n            } else {\n                /* Device is not started. Write silence. */\n                ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels);\n            }\n\n            pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE);\n            if (pulseResult < 0) {\n                result = ma_result_from_pulse(pulseResult);\n                goto done;  /* Failed to write data to stream. */\n            }\n\n            framesProcessed += framesMapped;\n        } else {\n            result = MA_SUCCESS;  /* No data available for writing. */\n            goto done;\n        }\n    } else {\n        result = MA_ERROR;  /* Failed to retrieve the writable size. Abort. */\n        goto done;\n    }\n\ndone:\n    if (pFramesProcessed != NULL) {\n        *pFramesProcessed = framesProcessed;\n    }\n\n    return result;\n}\n\nstatic void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    ma_uint32 bpf;\n    ma_uint64 frameCount;\n    ma_uint64 framesProcessed;\n    ma_uint32 deviceState;\n    ma_result result;\n\n    MA_ASSERT(pDevice != NULL);\n\n    /*\n    Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio\n    can fire this callback before the stream has even started. Ridiculous.\n    */\n    deviceState = ma_device_get_state(pDevice);\n    if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {\n        return;\n    }\n\n    bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);\n    MA_ASSERT(bpf > 0);\n\n    frameCount = byteCount / bpf;\n    framesProcessed = 0;\n\n    while (framesProcessed < frameCount) {\n        ma_uint64 framesProcessedThisIteration;\n\n        /* Don't keep trying to process frames if the device isn't started. */\n        deviceState = ma_device_get_state(pDevice);\n        if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {\n            break;\n        }\n\n        result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration);\n        if (result != MA_SUCCESS) {\n            break;\n        }\n\n        framesProcessed += framesProcessedThisIteration;\n    }\n}\n\nstatic void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    int suspended;\n\n    (void)pStream;\n\n    suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream);\n    ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\\n\", suspended);\n\n    if (suspended < 0) {\n        return;\n    }\n\n    if (suspended == 1) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[Pulse] Device suspended state changed. Suspended.\\n\");\n        ma_device__on_notification_stopped(pDevice);\n    } else {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"[Pulse] Device suspended state changed. Resumed.\\n\");\n        ma_device__on_notification_started(pDevice);\n    }\n}\n\nstatic void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n\n    (void)pStream;\n    (void)pUserData;\n\n    ma_device__on_notification_rerouted(pDevice);\n}\n\nstatic ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)\n{\n    /*\n    There have been reports from users where buffers of < ~20ms result glitches when running through\n    PipeWire. To work around this we're going to have to use a different default buffer size.\n    */\n    const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency   = 25;\n    const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE;\n\n    MA_ASSERT(nativeSampleRate != 0);\n\n    if (pDescriptor->periodSizeInFrames == 0) {\n        if (pDescriptor->periodSizeInMilliseconds == 0) {\n            if (performanceProfile == ma_performance_profile_low_latency) {\n                return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate);\n            } else {\n                return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate);\n            }\n        } else {\n            return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);\n        }\n    } else {\n        return pDescriptor->periodSizeInFrames;\n    }\n}\n\nstatic ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    /*\n    Notes for PulseAudio:\n\n      - When both the period size in frames and milliseconds are 0, we default to miniaudio's\n        default buffer sizes rather than leaving it up to PulseAudio because I don't trust\n        PulseAudio to give us any kind of reasonable latency by default.\n\n      - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this\n        flag, capture mode will just not work properly until you open another PulseAudio app.\n    */\n\n    ma_result result = MA_SUCCESS;\n    int error = 0;\n    const char* devPlayback = NULL;\n    const char* devCapture  = NULL;\n    ma_format format = ma_format_unknown;\n    ma_uint32 channels = 0;\n    ma_uint32 sampleRate = 0;\n    ma_pa_sink_info sinkInfo;\n    ma_pa_source_info sourceInfo;\n    ma_pa_sample_spec ss;\n    ma_pa_channel_map cmap;\n    ma_pa_buffer_attr attr;\n    const ma_pa_sample_spec* pActualSS   = NULL;\n    const ma_pa_buffer_attr* pActualAttr = NULL;\n    ma_uint32 iChannel;\n    ma_pa_stream_flags_t streamFlags;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ZERO_OBJECT(&pDevice->pulse);\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    /* No exclusive mode with the PulseAudio backend. */\n    if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||\n        ((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode  == ma_share_mode_exclusive)) {\n        return MA_SHARE_MODE_NOT_SUPPORTED;\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        if (pDescriptorPlayback->pDeviceID != NULL) {\n            devPlayback = pDescriptorPlayback->pDeviceID->pulse;\n        }\n\n        format     = pDescriptorPlayback->format;\n        channels   = pDescriptorPlayback->channels;\n        sampleRate = pDescriptorPlayback->sampleRate;\n    }\n\n    if (pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) {\n        if (pDescriptorCapture->pDeviceID != NULL) {\n            devCapture = pDescriptorCapture->pDeviceID->pulse;\n        }\n\n        format     = pDescriptorCapture->format;\n        channels   = pDescriptorCapture->channels;\n        sampleRate = pDescriptorCapture->sampleRate;\n    }\n\n\n\n    result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext);\n    if (result != MA_SUCCESS) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to initialize PA mainloop and context for device.\\n\");\n        return result;\n    }\n\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo);\n        if (result != MA_SUCCESS) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to retrieve source info for capture device.\");\n            goto on_error0;\n        }\n\n        ss   = sourceInfo.sample_spec;\n        cmap = sourceInfo.channel_map;\n\n        /* Use the requested channel count if we have one. */\n        if (pDescriptorCapture->channels != 0) {\n            ss.channels = pDescriptorCapture->channels;\n        }\n\n        /* Use a default channel map. */\n        ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT);\n\n        /* Use the requested sample rate if one was specified. */\n        if (pDescriptorCapture->sampleRate != 0) {\n            ss.rate = pDescriptorCapture->sampleRate;\n        }\n        streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY;\n\n        if (ma_format_from_pulse(ss.format) == ma_format_unknown) {\n            if (ma_is_little_endian()) {\n                ss.format = MA_PA_SAMPLE_FLOAT32LE;\n            } else {\n                ss.format = MA_PA_SAMPLE_FLOAT32BE;\n            }\n            streamFlags |= MA_PA_STREAM_FIX_FORMAT;\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\\n\");\n        }\n        if (ss.rate == 0) {\n            ss.rate = MA_DEFAULT_SAMPLE_RATE;\n            streamFlags |= MA_PA_STREAM_FIX_RATE;\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\\n\", ss.rate);\n        }\n        if (ss.channels == 0) {\n            ss.channels = MA_DEFAULT_CHANNELS;\n            streamFlags |= MA_PA_STREAM_FIX_CHANNELS;\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\\n\", ss.channels);\n        }\n\n        /* We now have enough information to calculate our actual period size in frames. */\n        pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile);\n\n        attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss);\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\\n\", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames);\n\n        pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);\n        if (pDevice->pulse.pStreamCapture == NULL) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to create PulseAudio capture stream.\\n\");\n            result = MA_ERROR;\n            goto on_error0;\n        }\n\n\n        /* The callback needs to be set before connecting the stream. */\n        ((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice);\n\n        /* State callback for checking when the device has been corked. */\n        ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice);\n\n        /* Rerouting notification. */\n        ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice);\n\n\n        /* Connect after we've got all of our internal state set up. */\n        if (devCapture != NULL) {\n            streamFlags |= MA_PA_STREAM_DONT_MOVE;\n        }\n\n        error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);\n        if (error != MA_PA_OK) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to connect PulseAudio capture stream.\");\n            result = ma_result_from_pulse(error);\n            goto on_error1;\n        }\n\n        result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture);\n        if (result != MA_SUCCESS) {\n            goto on_error2;\n        }\n\n\n        /* Internal format. */\n        pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);\n        if (pActualSS != NULL) {\n            ss = *pActualSS;\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\\n\", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate);\n        } else {\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] Failed to retrieve capture sample spec.\\n\");\n        }\n\n        pDescriptorCapture->format     = ma_format_from_pulse(ss.format);\n        pDescriptorCapture->channels   = ss.channels;\n        pDescriptorCapture->sampleRate = ss.rate;\n\n        if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) {\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\\n\", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate);\n            result = MA_ERROR;\n            goto on_error4;\n        }\n\n        /* Internal channel map. */\n\n        /*\n        Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting\n        the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono\n        and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For\n        all other channel counts we need to just put up with whatever PipeWire reports and hope it gets\n        fixed sooner than later. I might remove this hack later.\n        */\n        if (pDescriptorCapture->channels > 2) {\n            for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) {\n                pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);\n            }\n        } else {\n            /* Hack for mono and stereo. */\n            if (pDescriptorCapture->channels == 1) {\n                pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO;\n            } else if (pDescriptorCapture->channels == 2) {\n                pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT;\n                pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;\n            } else {\n                MA_ASSERT(MA_FALSE);    /* Should never hit this. */\n            }\n        }\n\n\n        /* Buffer. */\n        pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);\n        if (pActualAttr != NULL) {\n            attr = *pActualAttr;\n        }\n\n        if (attr.fragsize > 0) {\n            pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1);\n        } else {\n            pDescriptorCapture->periodCount = 1;\n        }\n\n        pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount;\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\\n\", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames);\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo);\n        if (result != MA_SUCCESS) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to retrieve sink info for playback device.\\n\");\n            goto on_error2;\n        }\n\n        ss   = sinkInfo.sample_spec;\n        cmap = sinkInfo.channel_map;\n\n        /* Use the requested channel count if we have one. */\n        if (pDescriptorPlayback->channels != 0) {\n            ss.channels = pDescriptorPlayback->channels;\n        }\n\n        /* Use a default channel map. */\n        ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT);\n\n\n        /* Use the requested sample rate if one was specified. */\n        if (pDescriptorPlayback->sampleRate != 0) {\n            ss.rate = pDescriptorPlayback->sampleRate;\n        }\n\n        streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY;\n        if (ma_format_from_pulse(ss.format) == ma_format_unknown) {\n            if (ma_is_little_endian()) {\n                ss.format = MA_PA_SAMPLE_FLOAT32LE;\n            } else {\n                ss.format = MA_PA_SAMPLE_FLOAT32BE;\n            }\n            streamFlags |= MA_PA_STREAM_FIX_FORMAT;\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\\n\");\n        }\n        if (ss.rate == 0) {\n            ss.rate = MA_DEFAULT_SAMPLE_RATE;\n            streamFlags |= MA_PA_STREAM_FIX_RATE;\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\\n\", ss.rate);\n        }\n        if (ss.channels == 0) {\n            ss.channels = MA_DEFAULT_CHANNELS;\n            streamFlags |= MA_PA_STREAM_FIX_CHANNELS;\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\\n\", ss.channels);\n        }\n\n        /* We now have enough information to calculate the actual buffer size in frames. */\n        pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile);\n\n        attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss);\n\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\\n\", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames);\n\n        pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);\n        if (pDevice->pulse.pStreamPlayback == NULL) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to create PulseAudio playback stream.\\n\");\n            result = MA_ERROR;\n            goto on_error2;\n        }\n\n\n        /*\n        Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a\n        device state of ma_device_state_uninitialized.\n        */\n        ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice);\n\n        /* State callback for checking when the device has been corked. */\n        ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice);\n\n        /* Rerouting notification. */\n        ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice);\n\n\n        /* Connect after we've got all of our internal state set up. */\n        if (devPlayback != NULL) {\n            streamFlags |= MA_PA_STREAM_DONT_MOVE;\n        }\n\n        error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL);\n        if (error != MA_PA_OK) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to connect PulseAudio playback stream.\");\n            result = ma_result_from_pulse(error);\n            goto on_error3;\n        }\n\n        result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback);\n        if (result != MA_SUCCESS) {\n            goto on_error3;\n        }\n\n\n        /* Internal format. */\n        pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);\n        if (pActualSS != NULL) {\n            ss = *pActualSS;\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\\n\", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate);\n        } else {\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] Failed to retrieve playback sample spec.\\n\");\n        }\n\n        pDescriptorPlayback->format     = ma_format_from_pulse(ss.format);\n        pDescriptorPlayback->channels   = ss.channels;\n        pDescriptorPlayback->sampleRate = ss.rate;\n\n        if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) {\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\\n\", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate);\n            result = MA_ERROR;\n            goto on_error4;\n        }\n\n        /* Internal channel map. */\n\n        /*\n        Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting\n        the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono\n        and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For\n        all other channel counts we need to just put up with whatever PipeWire reports and hope it gets\n        fixed sooner than later. I might remove this hack later.\n        */\n        if (pDescriptorPlayback->channels > 2) {\n            for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) {\n                pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);\n            }\n        } else {\n            /* Hack for mono and stereo. */\n            if (pDescriptorPlayback->channels == 1) {\n                pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO;\n            } else if (pDescriptorPlayback->channels == 2) {\n                pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT;\n                pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;\n            } else {\n                MA_ASSERT(MA_FALSE);    /* Should never hit this. */\n            }\n        }\n\n\n        /* Buffer. */\n        pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);\n        if (pActualAttr != NULL) {\n            attr = *pActualAttr;\n        }\n\n        if (attr.tlength > 0) {\n            pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1);\n        } else {\n            pDescriptorPlayback->periodCount = 1;\n        }\n\n        pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount;\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\\n\", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames);\n    }\n\n\n    /*\n    We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main\n    part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for\n    us later on because that will only do it if it's a fully asynchronous backend - i.e. the\n    onDeviceDataLoop callback is NULL, which is not the case for PulseAudio.\n    */\n    if (pConfig->deviceType == ma_device_type_duplex) {\n        ma_format rbFormat     = (format != ma_format_unknown) ? format     : pDescriptorCapture->format;\n        ma_uint32 rbChannels   = (channels   > 0)              ? channels   : pDescriptorCapture->channels;\n        ma_uint32 rbSampleRate = (sampleRate > 0)              ? sampleRate : pDescriptorCapture->sampleRate;\n\n        result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);\n        if (result != MA_SUCCESS) {\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to initialize ring buffer. %s.\\n\", ma_result_description(result));\n            goto on_error4;\n        }\n    }\n\n    return MA_SUCCESS;\n\n\non_error4:\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);\n    }\non_error3:\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);\n    }\non_error2:\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);\n    }\non_error1:\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);\n    }\non_error0:\n    return result;\n}\n\n\nstatic void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)\n{\n    ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;\n    MA_ASSERT(pIsSuccessful != NULL);\n\n    *pIsSuccessful = (ma_bool32)success;\n\n    (void)pStream; /* Unused. */\n}\n\nstatic ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)\n{\n    ma_context* pContext = pDevice->pContext;\n    ma_bool32 wasSuccessful;\n    ma_pa_stream* pStream;\n    ma_pa_operation* pOP;\n    ma_result result;\n\n    /* This should not be called with a duplex device type. */\n    if (deviceType == ma_device_type_duplex) {\n        return MA_INVALID_ARGS;\n    }\n\n    wasSuccessful = MA_FALSE;\n\n    pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);\n    MA_ASSERT(pStream != NULL);\n\n    pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);\n    if (pOP == NULL) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to cork PulseAudio stream.\");\n        return MA_ERROR;\n    }\n\n    result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);\n    if (result != MA_SUCCESS) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.\");\n        return result;\n    }\n\n    if (!wasSuccessful) {\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[PulseAudio] Failed to %s PulseAudio stream.\", (cork) ? \"stop\" : \"start\");\n        return MA_ERROR;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_start__pulse(ma_device* pDevice)\n{\n    ma_result result;\n\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        /*\n        We need to fill some data before uncorking. Not doing this will result in the write callback\n        never getting fired. We're not going to abort if writing fails because I still want the device\n        to get uncorked.\n        */\n        ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL);   /* No need to check the result here. Always want to fall through an uncork.*/\n\n        result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__pulse(ma_device* pDevice)\n{\n    ma_result result;\n\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        /*\n        Ideally we would drain the device here, but there's been cases where PulseAudio seems to be\n        broken on some systems to the point where no audio processing seems to happen. When this\n        happens, draining never completes and we get stuck here. For now I'm disabling draining of\n        the device so we don't just freeze the application.\n        */\n    #if 0\n        ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful);\n        ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);\n    #endif\n\n        result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_data_loop__pulse(ma_device* pDevice)\n{\n    int resultPA;\n\n    MA_ASSERT(pDevice != NULL);\n\n    /* NOTE: Don't start the device here. It'll be done at a higher level. */\n\n    /*\n    All data is handled through callbacks. All we need to do is iterate over the main loop and let\n    the callbacks deal with it.\n    */\n    while (ma_device_get_state(pDevice) == ma_device_state_started) {\n        resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);\n        if (resultPA < 0) {\n            break;\n        }\n    }\n\n    /* NOTE: Don't stop the device here. It'll be done at a higher level. */\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_uninit__pulse(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_pulseaudio);\n\n    ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext);\n    ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext);\n    ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop);\n\n    ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);\n    ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);\n\n#ifndef MA_NO_RUNTIME_LINKING\n    ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO);\n#endif\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n    ma_result result;\n#ifndef MA_NO_RUNTIME_LINKING\n    const char* libpulseNames[] = {\n        \"libpulse.so\",\n        \"libpulse.so.0\"\n    };\n    size_t i;\n\n    for (i = 0; i < ma_countof(libpulseNames); ++i) {\n        pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]);\n        if (pContext->pulse.pulseSO != NULL) {\n            break;\n        }\n    }\n\n    if (pContext->pulse.pulseSO == NULL) {\n        return MA_NO_BACKEND;\n    }\n\n    pContext->pulse.pa_mainloop_new                    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_mainloop_new\");\n    pContext->pulse.pa_mainloop_free                   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_mainloop_free\");\n    pContext->pulse.pa_mainloop_quit                   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_mainloop_quit\");\n    pContext->pulse.pa_mainloop_get_api                = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_mainloop_get_api\");\n    pContext->pulse.pa_mainloop_iterate                = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_mainloop_iterate\");\n    pContext->pulse.pa_mainloop_wakeup                 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_mainloop_wakeup\");\n    pContext->pulse.pa_threaded_mainloop_new           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_new\");\n    pContext->pulse.pa_threaded_mainloop_free          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_free\");\n    pContext->pulse.pa_threaded_mainloop_start         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_start\");\n    pContext->pulse.pa_threaded_mainloop_stop          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_stop\");\n    pContext->pulse.pa_threaded_mainloop_lock          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_lock\");\n    pContext->pulse.pa_threaded_mainloop_unlock        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_unlock\");\n    pContext->pulse.pa_threaded_mainloop_wait          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_wait\");\n    pContext->pulse.pa_threaded_mainloop_signal        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_signal\");\n    pContext->pulse.pa_threaded_mainloop_accept        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_accept\");\n    pContext->pulse.pa_threaded_mainloop_get_retval    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_get_retval\");\n    pContext->pulse.pa_threaded_mainloop_get_api       = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_get_api\");\n    pContext->pulse.pa_threaded_mainloop_in_thread     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_in_thread\");\n    pContext->pulse.pa_threaded_mainloop_set_name      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_threaded_mainloop_set_name\");\n    pContext->pulse.pa_context_new                     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_context_new\");\n    pContext->pulse.pa_context_unref                   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_context_unref\");\n    pContext->pulse.pa_context_connect                 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_context_connect\");\n    pContext->pulse.pa_context_disconnect              = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_context_disconnect\");\n    pContext->pulse.pa_context_set_state_callback      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_context_set_state_callback\");\n    pContext->pulse.pa_context_get_state               = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_context_get_state\");\n    pContext->pulse.pa_context_get_sink_info_list      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_context_get_sink_info_list\");\n    pContext->pulse.pa_context_get_source_info_list    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_context_get_source_info_list\");\n    pContext->pulse.pa_context_get_sink_info_by_name   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_context_get_sink_info_by_name\");\n    pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_context_get_source_info_by_name\");\n    pContext->pulse.pa_operation_unref                 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_operation_unref\");\n    pContext->pulse.pa_operation_get_state             = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_operation_get_state\");\n    pContext->pulse.pa_channel_map_init_extend         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_channel_map_init_extend\");\n    pContext->pulse.pa_channel_map_valid               = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_channel_map_valid\");\n    pContext->pulse.pa_channel_map_compatible          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_channel_map_compatible\");\n    pContext->pulse.pa_stream_new                      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_new\");\n    pContext->pulse.pa_stream_unref                    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_unref\");\n    pContext->pulse.pa_stream_connect_playback         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_connect_playback\");\n    pContext->pulse.pa_stream_connect_record           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_connect_record\");\n    pContext->pulse.pa_stream_disconnect               = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_disconnect\");\n    pContext->pulse.pa_stream_get_state                = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_get_state\");\n    pContext->pulse.pa_stream_get_sample_spec          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_get_sample_spec\");\n    pContext->pulse.pa_stream_get_channel_map          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_get_channel_map\");\n    pContext->pulse.pa_stream_get_buffer_attr          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_get_buffer_attr\");\n    pContext->pulse.pa_stream_set_buffer_attr          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_set_buffer_attr\");\n    pContext->pulse.pa_stream_get_device_name          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_get_device_name\");\n    pContext->pulse.pa_stream_set_write_callback       = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_set_write_callback\");\n    pContext->pulse.pa_stream_set_read_callback        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_set_read_callback\");\n    pContext->pulse.pa_stream_set_suspended_callback   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_set_suspended_callback\");\n    pContext->pulse.pa_stream_set_moved_callback       = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_set_moved_callback\");\n    pContext->pulse.pa_stream_is_suspended             = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_is_suspended\");\n    pContext->pulse.pa_stream_flush                    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_flush\");\n    pContext->pulse.pa_stream_drain                    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_drain\");\n    pContext->pulse.pa_stream_is_corked                = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_is_corked\");\n    pContext->pulse.pa_stream_cork                     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_cork\");\n    pContext->pulse.pa_stream_trigger                  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_trigger\");\n    pContext->pulse.pa_stream_begin_write              = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_begin_write\");\n    pContext->pulse.pa_stream_write                    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_write\");\n    pContext->pulse.pa_stream_peek                     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_peek\");\n    pContext->pulse.pa_stream_drop                     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_drop\");\n    pContext->pulse.pa_stream_writable_size            = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_writable_size\");\n    pContext->pulse.pa_stream_readable_size            = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, \"pa_stream_readable_size\");\n#else\n    /* This strange assignment system is just for type safety. */\n    ma_pa_mainloop_new_proc                    _pa_mainloop_new                   = pa_mainloop_new;\n    ma_pa_mainloop_free_proc                   _pa_mainloop_free                  = pa_mainloop_free;\n    ma_pa_mainloop_quit_proc                   _pa_mainloop_quit                  = pa_mainloop_quit;\n    ma_pa_mainloop_get_api_proc                _pa_mainloop_get_api               = pa_mainloop_get_api;\n    ma_pa_mainloop_iterate_proc                _pa_mainloop_iterate               = pa_mainloop_iterate;\n    ma_pa_mainloop_wakeup_proc                 _pa_mainloop_wakeup                = pa_mainloop_wakeup;\n    ma_pa_threaded_mainloop_new_proc           _pa_threaded_mainloop_new          = pa_threaded_mainloop_new;\n    ma_pa_threaded_mainloop_free_proc          _pa_threaded_mainloop_free         = pa_threaded_mainloop_free;\n    ma_pa_threaded_mainloop_start_proc         _pa_threaded_mainloop_start        = pa_threaded_mainloop_start;\n    ma_pa_threaded_mainloop_stop_proc          _pa_threaded_mainloop_stop         = pa_threaded_mainloop_stop;\n    ma_pa_threaded_mainloop_lock_proc          _pa_threaded_mainloop_lock         = pa_threaded_mainloop_lock;\n    ma_pa_threaded_mainloop_unlock_proc        _pa_threaded_mainloop_unlock       = pa_threaded_mainloop_unlock;\n    ma_pa_threaded_mainloop_wait_proc          _pa_threaded_mainloop_wait         = pa_threaded_mainloop_wait;\n    ma_pa_threaded_mainloop_signal_proc        _pa_threaded_mainloop_signal       = pa_threaded_mainloop_signal;\n    ma_pa_threaded_mainloop_accept_proc        _pa_threaded_mainloop_accept       = pa_threaded_mainloop_accept;\n    ma_pa_threaded_mainloop_get_retval_proc    _pa_threaded_mainloop_get_retval   = pa_threaded_mainloop_get_retval;\n    ma_pa_threaded_mainloop_get_api_proc       _pa_threaded_mainloop_get_api      = pa_threaded_mainloop_get_api;\n    ma_pa_threaded_mainloop_in_thread_proc     _pa_threaded_mainloop_in_thread    = pa_threaded_mainloop_in_thread;\n    ma_pa_threaded_mainloop_set_name_proc      _pa_threaded_mainloop_set_name     = pa_threaded_mainloop_set_name;\n    ma_pa_context_new_proc                     _pa_context_new                    = pa_context_new;\n    ma_pa_context_unref_proc                   _pa_context_unref                  = pa_context_unref;\n    ma_pa_context_connect_proc                 _pa_context_connect                = pa_context_connect;\n    ma_pa_context_disconnect_proc              _pa_context_disconnect             = pa_context_disconnect;\n    ma_pa_context_set_state_callback_proc      _pa_context_set_state_callback     = pa_context_set_state_callback;\n    ma_pa_context_get_state_proc               _pa_context_get_state              = pa_context_get_state;\n    ma_pa_context_get_sink_info_list_proc      _pa_context_get_sink_info_list     = pa_context_get_sink_info_list;\n    ma_pa_context_get_source_info_list_proc    _pa_context_get_source_info_list   = pa_context_get_source_info_list;\n    ma_pa_context_get_sink_info_by_name_proc   _pa_context_get_sink_info_by_name  = pa_context_get_sink_info_by_name;\n    ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;\n    ma_pa_operation_unref_proc                 _pa_operation_unref                = pa_operation_unref;\n    ma_pa_operation_get_state_proc             _pa_operation_get_state            = pa_operation_get_state;\n    ma_pa_channel_map_init_extend_proc         _pa_channel_map_init_extend        = pa_channel_map_init_extend;\n    ma_pa_channel_map_valid_proc               _pa_channel_map_valid              = pa_channel_map_valid;\n    ma_pa_channel_map_compatible_proc          _pa_channel_map_compatible         = pa_channel_map_compatible;\n    ma_pa_stream_new_proc                      _pa_stream_new                     = pa_stream_new;\n    ma_pa_stream_unref_proc                    _pa_stream_unref                   = pa_stream_unref;\n    ma_pa_stream_connect_playback_proc         _pa_stream_connect_playback        = pa_stream_connect_playback;\n    ma_pa_stream_connect_record_proc           _pa_stream_connect_record          = pa_stream_connect_record;\n    ma_pa_stream_disconnect_proc               _pa_stream_disconnect              = pa_stream_disconnect;\n    ma_pa_stream_get_state_proc                _pa_stream_get_state               = pa_stream_get_state;\n    ma_pa_stream_get_sample_spec_proc          _pa_stream_get_sample_spec         = pa_stream_get_sample_spec;\n    ma_pa_stream_get_channel_map_proc          _pa_stream_get_channel_map         = pa_stream_get_channel_map;\n    ma_pa_stream_get_buffer_attr_proc          _pa_stream_get_buffer_attr         = pa_stream_get_buffer_attr;\n    ma_pa_stream_set_buffer_attr_proc          _pa_stream_set_buffer_attr         = pa_stream_set_buffer_attr;\n    ma_pa_stream_get_device_name_proc          _pa_stream_get_device_name         = pa_stream_get_device_name;\n    ma_pa_stream_set_write_callback_proc       _pa_stream_set_write_callback      = pa_stream_set_write_callback;\n    ma_pa_stream_set_read_callback_proc        _pa_stream_set_read_callback       = pa_stream_set_read_callback;\n    ma_pa_stream_set_suspended_callback_proc   _pa_stream_set_suspended_callback  = pa_stream_set_suspended_callback;\n    ma_pa_stream_set_moved_callback_proc       _pa_stream_set_moved_callback      = pa_stream_set_moved_callback;\n    ma_pa_stream_is_suspended_proc             _pa_stream_is_suspended            = pa_stream_is_suspended;\n    ma_pa_stream_flush_proc                    _pa_stream_flush                   = pa_stream_flush;\n    ma_pa_stream_drain_proc                    _pa_stream_drain                   = pa_stream_drain;\n    ma_pa_stream_is_corked_proc                _pa_stream_is_corked               = pa_stream_is_corked;\n    ma_pa_stream_cork_proc                     _pa_stream_cork                    = pa_stream_cork;\n    ma_pa_stream_trigger_proc                  _pa_stream_trigger                 = pa_stream_trigger;\n    ma_pa_stream_begin_write_proc              _pa_stream_begin_write             = pa_stream_begin_write;\n    ma_pa_stream_write_proc                    _pa_stream_write                   = pa_stream_write;\n    ma_pa_stream_peek_proc                     _pa_stream_peek                    = pa_stream_peek;\n    ma_pa_stream_drop_proc                     _pa_stream_drop                    = pa_stream_drop;\n    ma_pa_stream_writable_size_proc            _pa_stream_writable_size           = pa_stream_writable_size;\n    ma_pa_stream_readable_size_proc            _pa_stream_readable_size           = pa_stream_readable_size;\n\n    pContext->pulse.pa_mainloop_new                    = (ma_proc)_pa_mainloop_new;\n    pContext->pulse.pa_mainloop_free                   = (ma_proc)_pa_mainloop_free;\n    pContext->pulse.pa_mainloop_quit                   = (ma_proc)_pa_mainloop_quit;\n    pContext->pulse.pa_mainloop_get_api                = (ma_proc)_pa_mainloop_get_api;\n    pContext->pulse.pa_mainloop_iterate                = (ma_proc)_pa_mainloop_iterate;\n    pContext->pulse.pa_mainloop_wakeup                 = (ma_proc)_pa_mainloop_wakeup;\n    pContext->pulse.pa_threaded_mainloop_new           = (ma_proc)_pa_threaded_mainloop_new;\n    pContext->pulse.pa_threaded_mainloop_free          = (ma_proc)_pa_threaded_mainloop_free;\n    pContext->pulse.pa_threaded_mainloop_start         = (ma_proc)_pa_threaded_mainloop_start;\n    pContext->pulse.pa_threaded_mainloop_stop          = (ma_proc)_pa_threaded_mainloop_stop;\n    pContext->pulse.pa_threaded_mainloop_lock          = (ma_proc)_pa_threaded_mainloop_lock;\n    pContext->pulse.pa_threaded_mainloop_unlock        = (ma_proc)_pa_threaded_mainloop_unlock;\n    pContext->pulse.pa_threaded_mainloop_wait          = (ma_proc)_pa_threaded_mainloop_wait;\n    pContext->pulse.pa_threaded_mainloop_signal        = (ma_proc)_pa_threaded_mainloop_signal;\n    pContext->pulse.pa_threaded_mainloop_accept        = (ma_proc)_pa_threaded_mainloop_accept;\n    pContext->pulse.pa_threaded_mainloop_get_retval    = (ma_proc)_pa_threaded_mainloop_get_retval;\n    pContext->pulse.pa_threaded_mainloop_get_api       = (ma_proc)_pa_threaded_mainloop_get_api;\n    pContext->pulse.pa_threaded_mainloop_in_thread     = (ma_proc)_pa_threaded_mainloop_in_thread;\n    pContext->pulse.pa_threaded_mainloop_set_name      = (ma_proc)_pa_threaded_mainloop_set_name;\n    pContext->pulse.pa_context_new                     = (ma_proc)_pa_context_new;\n    pContext->pulse.pa_context_unref                   = (ma_proc)_pa_context_unref;\n    pContext->pulse.pa_context_connect                 = (ma_proc)_pa_context_connect;\n    pContext->pulse.pa_context_disconnect              = (ma_proc)_pa_context_disconnect;\n    pContext->pulse.pa_context_set_state_callback      = (ma_proc)_pa_context_set_state_callback;\n    pContext->pulse.pa_context_get_state               = (ma_proc)_pa_context_get_state;\n    pContext->pulse.pa_context_get_sink_info_list      = (ma_proc)_pa_context_get_sink_info_list;\n    pContext->pulse.pa_context_get_source_info_list    = (ma_proc)_pa_context_get_source_info_list;\n    pContext->pulse.pa_context_get_sink_info_by_name   = (ma_proc)_pa_context_get_sink_info_by_name;\n    pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;\n    pContext->pulse.pa_operation_unref                 = (ma_proc)_pa_operation_unref;\n    pContext->pulse.pa_operation_get_state             = (ma_proc)_pa_operation_get_state;\n    pContext->pulse.pa_channel_map_init_extend         = (ma_proc)_pa_channel_map_init_extend;\n    pContext->pulse.pa_channel_map_valid               = (ma_proc)_pa_channel_map_valid;\n    pContext->pulse.pa_channel_map_compatible          = (ma_proc)_pa_channel_map_compatible;\n    pContext->pulse.pa_stream_new                      = (ma_proc)_pa_stream_new;\n    pContext->pulse.pa_stream_unref                    = (ma_proc)_pa_stream_unref;\n    pContext->pulse.pa_stream_connect_playback         = (ma_proc)_pa_stream_connect_playback;\n    pContext->pulse.pa_stream_connect_record           = (ma_proc)_pa_stream_connect_record;\n    pContext->pulse.pa_stream_disconnect               = (ma_proc)_pa_stream_disconnect;\n    pContext->pulse.pa_stream_get_state                = (ma_proc)_pa_stream_get_state;\n    pContext->pulse.pa_stream_get_sample_spec          = (ma_proc)_pa_stream_get_sample_spec;\n    pContext->pulse.pa_stream_get_channel_map          = (ma_proc)_pa_stream_get_channel_map;\n    pContext->pulse.pa_stream_get_buffer_attr          = (ma_proc)_pa_stream_get_buffer_attr;\n    pContext->pulse.pa_stream_set_buffer_attr          = (ma_proc)_pa_stream_set_buffer_attr;\n    pContext->pulse.pa_stream_get_device_name          = (ma_proc)_pa_stream_get_device_name;\n    pContext->pulse.pa_stream_set_write_callback       = (ma_proc)_pa_stream_set_write_callback;\n    pContext->pulse.pa_stream_set_read_callback        = (ma_proc)_pa_stream_set_read_callback;\n    pContext->pulse.pa_stream_set_suspended_callback   = (ma_proc)_pa_stream_set_suspended_callback;\n    pContext->pulse.pa_stream_set_moved_callback       = (ma_proc)_pa_stream_set_moved_callback;\n    pContext->pulse.pa_stream_is_suspended             = (ma_proc)_pa_stream_is_suspended;\n    pContext->pulse.pa_stream_flush                    = (ma_proc)_pa_stream_flush;\n    pContext->pulse.pa_stream_drain                    = (ma_proc)_pa_stream_drain;\n    pContext->pulse.pa_stream_is_corked                = (ma_proc)_pa_stream_is_corked;\n    pContext->pulse.pa_stream_cork                     = (ma_proc)_pa_stream_cork;\n    pContext->pulse.pa_stream_trigger                  = (ma_proc)_pa_stream_trigger;\n    pContext->pulse.pa_stream_begin_write              = (ma_proc)_pa_stream_begin_write;\n    pContext->pulse.pa_stream_write                    = (ma_proc)_pa_stream_write;\n    pContext->pulse.pa_stream_peek                     = (ma_proc)_pa_stream_peek;\n    pContext->pulse.pa_stream_drop                     = (ma_proc)_pa_stream_drop;\n    pContext->pulse.pa_stream_writable_size            = (ma_proc)_pa_stream_writable_size;\n    pContext->pulse.pa_stream_readable_size            = (ma_proc)_pa_stream_readable_size;\n#endif\n\n    /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */\n    pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks);\n    if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks);\n    if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) {\n        ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext);\n    if (result != MA_SUCCESS) {\n        ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);\n        ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);\n    #ifndef MA_NO_RUNTIME_LINKING\n        ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO);\n    #endif\n        return result;\n    }\n\n    /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */\n    pCallbacks->onContextInit             = ma_context_init__pulse;\n    pCallbacks->onContextUninit           = ma_context_uninit__pulse;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__pulse;\n    pCallbacks->onDeviceInit              = ma_device_init__pulse;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__pulse;\n    pCallbacks->onDeviceStart             = ma_device_start__pulse;\n    pCallbacks->onDeviceStop              = ma_device_stop__pulse;\n    pCallbacks->onDeviceRead              = NULL;   /* Not used because we're implementing onDeviceDataLoop. */\n    pCallbacks->onDeviceWrite             = NULL;   /* Not used because we're implementing onDeviceDataLoop. */\n    pCallbacks->onDeviceDataLoop          = ma_device_data_loop__pulse;\n    pCallbacks->onDeviceDataLoopWakeup    = ma_device_data_loop_wakeup__pulse;\n\n    return MA_SUCCESS;\n}\n#endif\n\n\n/******************************************************************************\n\nJACK Backend\n\n******************************************************************************/\n#ifdef MA_HAS_JACK\n\n/* It is assumed jack.h is available when compile-time linking is being used. */\n#ifdef MA_NO_RUNTIME_LINKING\n#include <jack/jack.h>\n\ntypedef jack_nframes_t              ma_jack_nframes_t;\ntypedef jack_options_t              ma_jack_options_t;\ntypedef jack_status_t               ma_jack_status_t;\ntypedef jack_client_t               ma_jack_client_t;\ntypedef jack_port_t                 ma_jack_port_t;\ntypedef JackProcessCallback         ma_JackProcessCallback;\ntypedef JackBufferSizeCallback      ma_JackBufferSizeCallback;\ntypedef JackShutdownCallback        ma_JackShutdownCallback;\n#define MA_JACK_DEFAULT_AUDIO_TYPE  JACK_DEFAULT_AUDIO_TYPE\n#define ma_JackNoStartServer        JackNoStartServer\n#define ma_JackPortIsInput          JackPortIsInput\n#define ma_JackPortIsOutput         JackPortIsOutput\n#define ma_JackPortIsPhysical       JackPortIsPhysical\n#else\ntypedef ma_uint32               ma_jack_nframes_t;\ntypedef int                     ma_jack_options_t;\ntypedef int                     ma_jack_status_t;\ntypedef struct ma_jack_client_t ma_jack_client_t;\ntypedef struct ma_jack_port_t   ma_jack_port_t;\ntypedef int  (* ma_JackProcessCallback)   (ma_jack_nframes_t nframes, void* arg);\ntypedef int  (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);\ntypedef void (* ma_JackShutdownCallback)  (void* arg);\n#define MA_JACK_DEFAULT_AUDIO_TYPE \"32 bit float mono audio\"\n#define ma_JackNoStartServer       1\n#define ma_JackPortIsInput         1\n#define ma_JackPortIsOutput        2\n#define ma_JackPortIsPhysical      4\n#endif\n\ntypedef ma_jack_client_t* (* ma_jack_client_open_proc)             (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);\ntypedef int               (* ma_jack_client_close_proc)            (ma_jack_client_t* client);\ntypedef int               (* ma_jack_client_name_size_proc)        (void);\ntypedef int               (* ma_jack_set_process_callback_proc)    (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);\ntypedef int               (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);\ntypedef void              (* ma_jack_on_shutdown_proc)             (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);\ntypedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc)         (ma_jack_client_t* client);\ntypedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc)         (ma_jack_client_t* client);\ntypedef const char**      (* ma_jack_get_ports_proc)               (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags);\ntypedef int               (* ma_jack_activate_proc)                (ma_jack_client_t* client);\ntypedef int               (* ma_jack_deactivate_proc)              (ma_jack_client_t* client);\ntypedef int               (* ma_jack_connect_proc)                 (ma_jack_client_t* client, const char* source_port, const char* destination_port);\ntypedef ma_jack_port_t*   (* ma_jack_port_register_proc)           (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size);\ntypedef const char*       (* ma_jack_port_name_proc)               (const ma_jack_port_t* port);\ntypedef void*             (* ma_jack_port_get_buffer_proc)         (ma_jack_port_t* port, ma_jack_nframes_t nframes);\ntypedef void              (* ma_jack_free_proc)                    (void* ptr);\n\nstatic ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)\n{\n    size_t maxClientNameSize;\n    char clientName[256];\n    ma_jack_status_t status;\n    ma_jack_client_t* pClient;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(ppClient != NULL);\n\n    if (ppClient) {\n        *ppClient = NULL;\n    }\n\n    maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */\n    ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : \"miniaudio\", (size_t)-1);\n\n    pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL);\n    if (pClient == NULL) {\n        return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n    }\n\n    if (ppClient) {\n        *ppClient = pClient;\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    ma_bool32 cbResult = MA_TRUE;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    /* Playback. */\n    if (cbResult) {\n        ma_device_info deviceInfo;\n        MA_ZERO_OBJECT(&deviceInfo);\n        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n        deviceInfo.isDefault = MA_TRUE;    /* JACK only uses default devices. */\n        cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n    }\n\n    /* Capture. */\n    if (cbResult) {\n        ma_device_info deviceInfo;\n        MA_ZERO_OBJECT(&deviceInfo);\n        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n        deviceInfo.isDefault = MA_TRUE;    /* JACK only uses default devices. */\n        cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n    }\n\n    (void)cbResult; /* For silencing a static analysis warning. */\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    ma_jack_client_t* pClient;\n    ma_result result;\n    const char** ppPorts;\n\n    MA_ASSERT(pContext != NULL);\n\n    if (pDeviceID != NULL && pDeviceID->jack != 0) {\n        return MA_NO_DEVICE;   /* Don't know the device. */\n    }\n\n    /* Name / Description */\n    if (deviceType == ma_device_type_playback) {\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n    } else {\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n    }\n\n    /* Jack only uses default devices. */\n    pDeviceInfo->isDefault = MA_TRUE;\n\n    /* Jack only supports f32 and has a specific channel count and sample rate. */\n    pDeviceInfo->nativeDataFormats[0].format = ma_format_f32;\n\n    /* The channel count and sample rate can only be determined by opening the device. */\n    result = ma_context_open_client__jack(pContext, &pClient);\n    if (result != MA_SUCCESS) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to open client.\");\n        return result;\n    }\n\n    pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);\n    pDeviceInfo->nativeDataFormats[0].channels   = 0;\n\n    ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput));\n    if (ppPorts == NULL) {\n        ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to query physical ports.\");\n        return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n    }\n\n    while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) {\n        pDeviceInfo->nativeDataFormats[0].channels += 1;\n    }\n\n    pDeviceInfo->nativeDataFormats[0].flags = 0;\n    pDeviceInfo->nativeDataFormatCount = 1;\n\n    ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);\n    ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);\n\n    (void)pContext;\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_device_uninit__jack(ma_device* pDevice)\n{\n    ma_context* pContext;\n\n    MA_ASSERT(pDevice != NULL);\n\n    pContext = pDevice->pContext;\n    MA_ASSERT(pContext != NULL);\n\n    if (pDevice->jack.pClient != NULL) {\n        ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);\n    }\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);\n        ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);\n        ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_device__jack_shutdown_callback(void* pUserData)\n{\n    /* JACK died. Stop the device. */\n    ma_device* pDevice = (ma_device*)pUserData;\n    MA_ASSERT(pDevice != NULL);\n\n    ma_device_stop(pDevice);\n}\n\nstatic int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat));\n        float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);\n        if (pNewBuffer == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n\n        ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);\n\n        pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;\n        pDevice->playback.internalPeriodSizeInFrames = frameCount;\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat));\n        float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);\n        if (pNewBuffer == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n\n        ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);\n\n        pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;\n        pDevice->playback.internalPeriodSizeInFrames = frameCount;\n    }\n\n    return 0;\n}\n\nstatic int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)\n{\n    ma_device* pDevice;\n    ma_context* pContext;\n    ma_uint32 iChannel;\n\n    pDevice = (ma_device*)pUserData;\n    MA_ASSERT(pDevice != NULL);\n\n    pContext = pDevice->pContext;\n    MA_ASSERT(pContext != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        /* Channels need to be interleaved. */\n        for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {\n            const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount);\n            if (pSrc != NULL) {\n                float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;\n                ma_jack_nframes_t iFrame;\n                for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                    *pDst = *pSrc;\n\n                    pDst += pDevice->capture.internalChannels;\n                    pSrc += 1;\n                }\n            }\n        }\n\n        ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount);\n\n        /* Channels need to be deinterleaved. */\n        for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {\n            float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount);\n            if (pDst != NULL) {\n                const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;\n                ma_jack_nframes_t iFrame;\n                for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                    *pDst = *pSrc;\n\n                    pDst += 1;\n                    pSrc += pDevice->playback.internalChannels;\n                }\n            }\n        }\n    }\n\n    return 0;\n}\n\nstatic ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    ma_result result;\n    ma_uint32 periodSizeInFrames;\n\n    MA_ASSERT(pConfig != NULL);\n    MA_ASSERT(pDevice != NULL);\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Loopback mode not supported.\");\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    /* Only supporting default devices with JACK. */\n    if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) ||\n        ((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID  != NULL && pDescriptorCapture->pDeviceID->jack  != 0)) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Only default devices are supported.\");\n        return MA_NO_DEVICE;\n    }\n\n    /* No exclusive mode with the JACK backend. */\n    if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||\n        ((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode  == ma_share_mode_exclusive)) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Exclusive mode not supported.\");\n        return MA_SHARE_MODE_NOT_SUPPORTED;\n    }\n\n    /* Open the client. */\n    result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient);\n    if (result != MA_SUCCESS) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to open client.\");\n        return result;\n    }\n\n    /* Callbacks. */\n    if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to set process callback.\");\n        return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n    }\n    if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to set buffer size callback.\");\n        return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n    }\n\n    ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);\n\n\n    /* The buffer size in frames can change. */\n    periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient);\n\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        ma_uint32 iPort;\n        const char** ppPorts;\n\n        pDescriptorCapture->format     = ma_format_f32;\n        pDescriptorCapture->channels   = 0;\n        pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);\n        ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);\n\n        ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput);\n        if (ppPorts == NULL) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to query physical ports.\");\n            return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n        }\n\n        /* Need to count the number of ports first so we can allocate some memory. */\n        while (ppPorts[pDescriptorCapture->channels] != NULL) {\n            pDescriptorCapture->channels += 1;\n        }\n\n        pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks);\n        if (pDevice->jack.ppPortsCapture == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n\n        for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) {\n            char name[64];\n            ma_strcpy_s(name, sizeof(name), \"capture\");\n            ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of \"capture\" */\n\n            pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0);\n            if (pDevice->jack.ppPortsCapture[iPort] == NULL) {\n                ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);\n                ma_device_uninit__jack(pDevice);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to register ports.\");\n                return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n            }\n        }\n\n        ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);\n\n        pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;\n        pDescriptorCapture->periodCount        = 1; /* There's no notion of a period in JACK. Just set to 1. */\n\n        pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks);\n        if (pDevice->jack.pIntermediaryBufferCapture == NULL) {\n            ma_device_uninit__jack(pDevice);\n            return MA_OUT_OF_MEMORY;\n        }\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        ma_uint32 iPort;\n        const char** ppPorts;\n\n        pDescriptorPlayback->format     = ma_format_f32;\n        pDescriptorPlayback->channels   = 0;\n        pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);\n        ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);\n\n        ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput);\n        if (ppPorts == NULL) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to query physical ports.\");\n            return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n        }\n\n        /* Need to count the number of ports first so we can allocate some memory. */\n        while (ppPorts[pDescriptorPlayback->channels] != NULL) {\n            pDescriptorPlayback->channels += 1;\n        }\n\n        pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks);\n        if (pDevice->jack.ppPortsPlayback == NULL) {\n            ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);\n            return MA_OUT_OF_MEMORY;\n        }\n\n        for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) {\n            char name[64];\n            ma_strcpy_s(name, sizeof(name), \"playback\");\n            ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of \"playback\" */\n\n            pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0);\n            if (pDevice->jack.ppPortsPlayback[iPort] == NULL) {\n                ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);\n                ma_device_uninit__jack(pDevice);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to register ports.\");\n                return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n            }\n        }\n\n        ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);\n\n        pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;\n        pDescriptorPlayback->periodCount        = 1;   /* There's no notion of a period in JACK. Just set to 1. */\n\n        pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks);\n        if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {\n            ma_device_uninit__jack(pDevice);\n            return MA_OUT_OF_MEMORY;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_device_start__jack(ma_device* pDevice)\n{\n    ma_context* pContext = pDevice->pContext;\n    int resultJACK;\n    size_t i;\n\n    resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);\n    if (resultJACK != 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to activate the JACK client.\");\n        return MA_FAILED_TO_START_BACKEND_DEVICE;\n    }\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput);\n        if (ppServerPorts == NULL) {\n            ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to retrieve physical ports.\");\n            return MA_ERROR;\n        }\n\n        for (i = 0; ppServerPorts[i] != NULL; ++i) {\n            const char* pServerPort = ppServerPorts[i];\n            const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]);\n\n            resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);\n            if (resultJACK != 0) {\n                ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);\n                ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to connect ports.\");\n                return MA_ERROR;\n            }\n        }\n\n        ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput);\n        if (ppServerPorts == NULL) {\n            ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to retrieve physical ports.\");\n            return MA_ERROR;\n        }\n\n        for (i = 0; ppServerPorts[i] != NULL; ++i) {\n            const char* pServerPort = ppServerPorts[i];\n            const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]);\n\n            resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);\n            if (resultJACK != 0) {\n                ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);\n                ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] Failed to connect ports.\");\n                return MA_ERROR;\n            }\n        }\n\n        ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__jack(ma_device* pDevice)\n{\n    ma_context* pContext = pDevice->pContext;\n\n    if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[JACK] An error occurred when deactivating the JACK client.\");\n        return MA_ERROR;\n    }\n\n    ma_device__on_notification_stopped(pDevice);\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_context_uninit__jack(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_jack);\n\n    ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);\n    pContext->jack.pClientName = NULL;\n\n#ifndef MA_NO_RUNTIME_LINKING\n    ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO);\n#endif\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n#ifndef MA_NO_RUNTIME_LINKING\n    const char* libjackNames[] = {\n#if defined(MA_WIN32)\n        \"libjack.dll\",\n        \"libjack64.dll\"\n#endif\n#if defined(MA_UNIX)\n        \"libjack.so\",\n        \"libjack.so.0\"\n#endif\n    };\n    size_t i;\n\n    for (i = 0; i < ma_countof(libjackNames); ++i) {\n        pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]);\n        if (pContext->jack.jackSO != NULL) {\n            break;\n        }\n    }\n\n    if (pContext->jack.jackSO == NULL) {\n        return MA_NO_BACKEND;\n    }\n\n    pContext->jack.jack_client_open              = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_client_open\");\n    pContext->jack.jack_client_close             = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_client_close\");\n    pContext->jack.jack_client_name_size         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_client_name_size\");\n    pContext->jack.jack_set_process_callback     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_set_process_callback\");\n    pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_set_buffer_size_callback\");\n    pContext->jack.jack_on_shutdown              = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_on_shutdown\");\n    pContext->jack.jack_get_sample_rate          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_get_sample_rate\");\n    pContext->jack.jack_get_buffer_size          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_get_buffer_size\");\n    pContext->jack.jack_get_ports                = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_get_ports\");\n    pContext->jack.jack_activate                 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_activate\");\n    pContext->jack.jack_deactivate               = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_deactivate\");\n    pContext->jack.jack_connect                  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_connect\");\n    pContext->jack.jack_port_register            = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_port_register\");\n    pContext->jack.jack_port_name                = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_port_name\");\n    pContext->jack.jack_port_get_buffer          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_port_get_buffer\");\n    pContext->jack.jack_free                     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, \"jack_free\");\n#else\n    /*\n    This strange assignment system is here just to ensure type safety of miniaudio's function pointer\n    types. If anything differs slightly the compiler should throw a warning.\n    */\n    ma_jack_client_open_proc              _jack_client_open              = jack_client_open;\n    ma_jack_client_close_proc             _jack_client_close             = jack_client_close;\n    ma_jack_client_name_size_proc         _jack_client_name_size         = jack_client_name_size;\n    ma_jack_set_process_callback_proc     _jack_set_process_callback     = jack_set_process_callback;\n    ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;\n    ma_jack_on_shutdown_proc              _jack_on_shutdown              = jack_on_shutdown;\n    ma_jack_get_sample_rate_proc          _jack_get_sample_rate          = jack_get_sample_rate;\n    ma_jack_get_buffer_size_proc          _jack_get_buffer_size          = jack_get_buffer_size;\n    ma_jack_get_ports_proc                _jack_get_ports                = jack_get_ports;\n    ma_jack_activate_proc                 _jack_activate                 = jack_activate;\n    ma_jack_deactivate_proc               _jack_deactivate               = jack_deactivate;\n    ma_jack_connect_proc                  _jack_connect                  = jack_connect;\n    ma_jack_port_register_proc            _jack_port_register            = jack_port_register;\n    ma_jack_port_name_proc                _jack_port_name                = jack_port_name;\n    ma_jack_port_get_buffer_proc          _jack_port_get_buffer          = jack_port_get_buffer;\n    ma_jack_free_proc                     _jack_free                     = jack_free;\n\n    pContext->jack.jack_client_open              = (ma_proc)_jack_client_open;\n    pContext->jack.jack_client_close             = (ma_proc)_jack_client_close;\n    pContext->jack.jack_client_name_size         = (ma_proc)_jack_client_name_size;\n    pContext->jack.jack_set_process_callback     = (ma_proc)_jack_set_process_callback;\n    pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;\n    pContext->jack.jack_on_shutdown              = (ma_proc)_jack_on_shutdown;\n    pContext->jack.jack_get_sample_rate          = (ma_proc)_jack_get_sample_rate;\n    pContext->jack.jack_get_buffer_size          = (ma_proc)_jack_get_buffer_size;\n    pContext->jack.jack_get_ports                = (ma_proc)_jack_get_ports;\n    pContext->jack.jack_activate                 = (ma_proc)_jack_activate;\n    pContext->jack.jack_deactivate               = (ma_proc)_jack_deactivate;\n    pContext->jack.jack_connect                  = (ma_proc)_jack_connect;\n    pContext->jack.jack_port_register            = (ma_proc)_jack_port_register;\n    pContext->jack.jack_port_name                = (ma_proc)_jack_port_name;\n    pContext->jack.jack_port_get_buffer          = (ma_proc)_jack_port_get_buffer;\n    pContext->jack.jack_free                     = (ma_proc)_jack_free;\n#endif\n\n    if (pConfig->jack.pClientName != NULL) {\n        pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks);\n    }\n    pContext->jack.tryStartServer = pConfig->jack.tryStartServer;\n\n    /*\n    Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting\n    a temporary client.\n    */\n    {\n        ma_jack_client_t* pDummyClient;\n        ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);\n        if (result != MA_SUCCESS) {\n            ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);\n        #ifndef MA_NO_RUNTIME_LINKING\n            ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO);\n        #endif\n            return MA_NO_BACKEND;\n        }\n\n        ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);\n    }\n\n\n    pCallbacks->onContextInit             = ma_context_init__jack;\n    pCallbacks->onContextUninit           = ma_context_uninit__jack;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__jack;\n    pCallbacks->onDeviceInit              = ma_device_init__jack;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__jack;\n    pCallbacks->onDeviceStart             = ma_device_start__jack;\n    pCallbacks->onDeviceStop              = ma_device_stop__jack;\n    pCallbacks->onDeviceRead              = NULL;   /* Not used because JACK is asynchronous. */\n    pCallbacks->onDeviceWrite             = NULL;   /* Not used because JACK is asynchronous. */\n    pCallbacks->onDeviceDataLoop          = NULL;   /* Not used because JACK is asynchronous. */\n\n    return MA_SUCCESS;\n}\n#endif  /* JACK */\n\n\n\n/******************************************************************************\n\nCore Audio Backend\n\nReferences\n==========\n- Technical Note TN2091: Device input using the HAL Output Audio Unit\n    https://developer.apple.com/library/archive/technotes/tn2091/_index.html\n\n******************************************************************************/\n#ifdef MA_HAS_COREAUDIO\n#include <TargetConditionals.h>\n\n#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1\n    #define MA_APPLE_MOBILE\n    #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1\n        #define MA_APPLE_TV\n    #endif\n    #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1\n        #define MA_APPLE_WATCH\n    #endif\n    #if __has_feature(objc_arc)\n        #define MA_BRIDGE_TRANSFER  __bridge_transfer\n        #define MA_BRIDGE_RETAINED  __bridge_retained\n    #else\n        #define MA_BRIDGE_TRANSFER\n        #define MA_BRIDGE_RETAINED\n    #endif\n#else\n    #define MA_APPLE_DESKTOP\n#endif\n\n#if defined(MA_APPLE_DESKTOP)\n#include <CoreAudio/CoreAudio.h>\n#else\n#include <AVFoundation/AVFoundation.h>\n#endif\n\n#include <AudioToolbox/AudioToolbox.h>\n\n/* CoreFoundation */\ntypedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);\ntypedef void (* ma_CFRelease_proc)(CFTypeRef cf);\n\n/* CoreAudio */\n#if defined(MA_APPLE_DESKTOP)\ntypedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);\ntypedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);\ntypedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);\ntypedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);\ntypedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);\n#endif\n\n/* AudioToolbox */\ntypedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);\ntypedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);\ntypedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);\ntypedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);\ntypedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);\ntypedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);\ntypedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);\ntypedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);\ntypedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);\ntypedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);\ntypedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);\n\n\n#define MA_COREAUDIO_OUTPUT_BUS    0\n#define MA_COREAUDIO_INPUT_BUS     1\n\n#if defined(MA_APPLE_DESKTOP)\nstatic ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);\n#endif\n\n/*\nCore Audio\n\nSo far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation\napart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose\nneeding to figure out how this darn thing works, I'm going to outline a few things here.\n\nSince miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be\nable to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen\nthat supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent\nand AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the\ndistinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.\n\nMost (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When\nretrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific\ndata, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the\ndevices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be\nthe central APIs for retrieving information about the system and specific devices.\n\nTo use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a\nstructure with three variables and is used to identify which property you are getting or setting. The first is the \"selector\"\nwhich is basically the specific property that you're wanting to retrieve or set. The second is the \"scope\", which is\ntypically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and\nkAudioObjectPropertyScopeOutput for output-specific properties. The last is the \"element\" which is always set to\nkAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different.\n\nBack to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size\nof the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property\naddress with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the\nsize, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of\nAudioDeviceID's so just do \"dataSize/sizeof(AudioDeviceID)\" to know the device count.\n*/\n\n#if defined(MA_APPLE_MOBILE)\nstatic void ma_device__on_notification_interruption_began(ma_device* pDevice)\n{\n    ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began));\n}\n\nstatic void ma_device__on_notification_interruption_ended(ma_device* pDevice)\n{\n    ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended));\n}\n#endif\n\nstatic ma_result ma_result_from_OSStatus(OSStatus status)\n{\n    switch (status)\n    {\n        case noErr:                                   return MA_SUCCESS;\n    #if defined(MA_APPLE_DESKTOP)\n        case kAudioHardwareNotRunningError:           return MA_DEVICE_NOT_STARTED;\n        case kAudioHardwareUnspecifiedError:          return MA_ERROR;\n        case kAudioHardwareUnknownPropertyError:      return MA_INVALID_ARGS;\n        case kAudioHardwareBadPropertySizeError:      return MA_INVALID_OPERATION;\n        case kAudioHardwareIllegalOperationError:     return MA_INVALID_OPERATION;\n        case kAudioHardwareBadObjectError:            return MA_INVALID_ARGS;\n        case kAudioHardwareBadDeviceError:            return MA_INVALID_ARGS;\n        case kAudioHardwareBadStreamError:            return MA_INVALID_ARGS;\n        case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;\n        case kAudioDeviceUnsupportedFormatError:      return MA_FORMAT_NOT_SUPPORTED;\n        case kAudioDevicePermissionsError:            return MA_ACCESS_DENIED;\n    #endif\n        default:                                      return MA_ERROR;\n    }\n}\n\n#if 0\nstatic ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)\n{\n    switch (bit)\n    {\n        case kAudioChannelBit_Left:                 return MA_CHANNEL_LEFT;\n        case kAudioChannelBit_Right:                return MA_CHANNEL_RIGHT;\n        case kAudioChannelBit_Center:               return MA_CHANNEL_FRONT_CENTER;\n        case kAudioChannelBit_LFEScreen:            return MA_CHANNEL_LFE;\n        case kAudioChannelBit_LeftSurround:         return MA_CHANNEL_BACK_LEFT;\n        case kAudioChannelBit_RightSurround:        return MA_CHANNEL_BACK_RIGHT;\n        case kAudioChannelBit_LeftCenter:           return MA_CHANNEL_FRONT_LEFT_CENTER;\n        case kAudioChannelBit_RightCenter:          return MA_CHANNEL_FRONT_RIGHT_CENTER;\n        case kAudioChannelBit_CenterSurround:       return MA_CHANNEL_BACK_CENTER;\n        case kAudioChannelBit_LeftSurroundDirect:   return MA_CHANNEL_SIDE_LEFT;\n        case kAudioChannelBit_RightSurroundDirect:  return MA_CHANNEL_SIDE_RIGHT;\n        case kAudioChannelBit_TopCenterSurround:    return MA_CHANNEL_TOP_CENTER;\n        case kAudioChannelBit_VerticalHeightLeft:   return MA_CHANNEL_TOP_FRONT_LEFT;\n        case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;\n        case kAudioChannelBit_VerticalHeightRight:  return MA_CHANNEL_TOP_FRONT_RIGHT;\n        case kAudioChannelBit_TopBackLeft:          return MA_CHANNEL_TOP_BACK_LEFT;\n        case kAudioChannelBit_TopBackCenter:        return MA_CHANNEL_TOP_BACK_CENTER;\n        case kAudioChannelBit_TopBackRight:         return MA_CHANNEL_TOP_BACK_RIGHT;\n        default:                                    return MA_CHANNEL_NONE;\n    }\n}\n#endif\n\nstatic ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)\n{\n    MA_ASSERT(pDescription != NULL);\n    MA_ASSERT(pFormatOut != NULL);\n\n    *pFormatOut = ma_format_unknown;   /* Safety. */\n\n    /* There's a few things miniaudio doesn't support. */\n    if (pDescription->mFormatID != kAudioFormatLinearPCM) {\n        return MA_FORMAT_NOT_SUPPORTED;\n    }\n\n    /* We don't support any non-packed formats that are aligned high. */\n    if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {\n        return MA_FORMAT_NOT_SUPPORTED;\n    }\n\n    /* Only supporting native-endian. */\n    if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {\n        return MA_FORMAT_NOT_SUPPORTED;\n    }\n\n    /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */\n    /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) {\n        return MA_FORMAT_NOT_SUPPORTED;\n    }*/\n\n    if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {\n        if (pDescription->mBitsPerChannel == 32) {\n            *pFormatOut = ma_format_f32;\n            return MA_SUCCESS;\n        }\n    } else {\n        if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {\n            if (pDescription->mBitsPerChannel == 16) {\n                *pFormatOut = ma_format_s16;\n                return MA_SUCCESS;\n            } else if (pDescription->mBitsPerChannel == 24) {\n                if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {\n                    *pFormatOut = ma_format_s24;\n                    return MA_SUCCESS;\n                } else {\n                    if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {\n                        /* TODO: Implement ma_format_s24_32. */\n                        /**pFormatOut = ma_format_s24_32;*/\n                        /*return MA_SUCCESS;*/\n                        return MA_FORMAT_NOT_SUPPORTED;\n                    }\n                }\n            } else if (pDescription->mBitsPerChannel == 32) {\n                *pFormatOut = ma_format_s32;\n                return MA_SUCCESS;\n            }\n        } else {\n            if (pDescription->mBitsPerChannel == 8) {\n                *pFormatOut = ma_format_u8;\n                return MA_SUCCESS;\n            }\n        }\n    }\n\n    /* Getting here means the format is not supported. */\n    return MA_FORMAT_NOT_SUPPORTED;\n}\n\n#if defined(MA_APPLE_DESKTOP)\nstatic ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)\n{\n    switch (label)\n    {\n        case kAudioChannelLabel_Unknown:              return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_Unused:               return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_UseCoordinates:       return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_Left:                 return MA_CHANNEL_LEFT;\n        case kAudioChannelLabel_Right:                return MA_CHANNEL_RIGHT;\n        case kAudioChannelLabel_Center:               return MA_CHANNEL_FRONT_CENTER;\n        case kAudioChannelLabel_LFEScreen:            return MA_CHANNEL_LFE;\n        case kAudioChannelLabel_LeftSurround:         return MA_CHANNEL_BACK_LEFT;\n        case kAudioChannelLabel_RightSurround:        return MA_CHANNEL_BACK_RIGHT;\n        case kAudioChannelLabel_LeftCenter:           return MA_CHANNEL_FRONT_LEFT_CENTER;\n        case kAudioChannelLabel_RightCenter:          return MA_CHANNEL_FRONT_RIGHT_CENTER;\n        case kAudioChannelLabel_CenterSurround:       return MA_CHANNEL_BACK_CENTER;\n        case kAudioChannelLabel_LeftSurroundDirect:   return MA_CHANNEL_SIDE_LEFT;\n        case kAudioChannelLabel_RightSurroundDirect:  return MA_CHANNEL_SIDE_RIGHT;\n        case kAudioChannelLabel_TopCenterSurround:    return MA_CHANNEL_TOP_CENTER;\n        case kAudioChannelLabel_VerticalHeightLeft:   return MA_CHANNEL_TOP_FRONT_LEFT;\n        case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;\n        case kAudioChannelLabel_VerticalHeightRight:  return MA_CHANNEL_TOP_FRONT_RIGHT;\n        case kAudioChannelLabel_TopBackLeft:          return MA_CHANNEL_TOP_BACK_LEFT;\n        case kAudioChannelLabel_TopBackCenter:        return MA_CHANNEL_TOP_BACK_CENTER;\n        case kAudioChannelLabel_TopBackRight:         return MA_CHANNEL_TOP_BACK_RIGHT;\n        case kAudioChannelLabel_RearSurroundLeft:     return MA_CHANNEL_BACK_LEFT;\n        case kAudioChannelLabel_RearSurroundRight:    return MA_CHANNEL_BACK_RIGHT;\n        case kAudioChannelLabel_LeftWide:             return MA_CHANNEL_SIDE_LEFT;\n        case kAudioChannelLabel_RightWide:            return MA_CHANNEL_SIDE_RIGHT;\n        case kAudioChannelLabel_LFE2:                 return MA_CHANNEL_LFE;\n        case kAudioChannelLabel_LeftTotal:            return MA_CHANNEL_LEFT;\n        case kAudioChannelLabel_RightTotal:           return MA_CHANNEL_RIGHT;\n        case kAudioChannelLabel_HearingImpaired:      return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_Narration:            return MA_CHANNEL_MONO;\n        case kAudioChannelLabel_Mono:                 return MA_CHANNEL_MONO;\n        case kAudioChannelLabel_DialogCentricMix:     return MA_CHANNEL_MONO;\n        case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;\n        case kAudioChannelLabel_Haptic:               return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_Ambisonic_W:          return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_Ambisonic_X:          return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_Ambisonic_Y:          return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_Ambisonic_Z:          return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_MS_Mid:               return MA_CHANNEL_LEFT;\n        case kAudioChannelLabel_MS_Side:              return MA_CHANNEL_RIGHT;\n        case kAudioChannelLabel_XY_X:                 return MA_CHANNEL_LEFT;\n        case kAudioChannelLabel_XY_Y:                 return MA_CHANNEL_RIGHT;\n        case kAudioChannelLabel_HeadphonesLeft:       return MA_CHANNEL_LEFT;\n        case kAudioChannelLabel_HeadphonesRight:      return MA_CHANNEL_RIGHT;\n        case kAudioChannelLabel_ClickTrack:           return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_ForeignLanguage:      return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_Discrete:             return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_Discrete_0:           return MA_CHANNEL_AUX_0;\n        case kAudioChannelLabel_Discrete_1:           return MA_CHANNEL_AUX_1;\n        case kAudioChannelLabel_Discrete_2:           return MA_CHANNEL_AUX_2;\n        case kAudioChannelLabel_Discrete_3:           return MA_CHANNEL_AUX_3;\n        case kAudioChannelLabel_Discrete_4:           return MA_CHANNEL_AUX_4;\n        case kAudioChannelLabel_Discrete_5:           return MA_CHANNEL_AUX_5;\n        case kAudioChannelLabel_Discrete_6:           return MA_CHANNEL_AUX_6;\n        case kAudioChannelLabel_Discrete_7:           return MA_CHANNEL_AUX_7;\n        case kAudioChannelLabel_Discrete_8:           return MA_CHANNEL_AUX_8;\n        case kAudioChannelLabel_Discrete_9:           return MA_CHANNEL_AUX_9;\n        case kAudioChannelLabel_Discrete_10:          return MA_CHANNEL_AUX_10;\n        case kAudioChannelLabel_Discrete_11:          return MA_CHANNEL_AUX_11;\n        case kAudioChannelLabel_Discrete_12:          return MA_CHANNEL_AUX_12;\n        case kAudioChannelLabel_Discrete_13:          return MA_CHANNEL_AUX_13;\n        case kAudioChannelLabel_Discrete_14:          return MA_CHANNEL_AUX_14;\n        case kAudioChannelLabel_Discrete_15:          return MA_CHANNEL_AUX_15;\n        case kAudioChannelLabel_Discrete_65535:       return MA_CHANNEL_NONE;\n\n    #if 0   /* Introduced in a later version of macOS. */\n        case kAudioChannelLabel_HOA_ACN:              return MA_CHANNEL_NONE;\n        case kAudioChannelLabel_HOA_ACN_0:            return MA_CHANNEL_AUX_0;\n        case kAudioChannelLabel_HOA_ACN_1:            return MA_CHANNEL_AUX_1;\n        case kAudioChannelLabel_HOA_ACN_2:            return MA_CHANNEL_AUX_2;\n        case kAudioChannelLabel_HOA_ACN_3:            return MA_CHANNEL_AUX_3;\n        case kAudioChannelLabel_HOA_ACN_4:            return MA_CHANNEL_AUX_4;\n        case kAudioChannelLabel_HOA_ACN_5:            return MA_CHANNEL_AUX_5;\n        case kAudioChannelLabel_HOA_ACN_6:            return MA_CHANNEL_AUX_6;\n        case kAudioChannelLabel_HOA_ACN_7:            return MA_CHANNEL_AUX_7;\n        case kAudioChannelLabel_HOA_ACN_8:            return MA_CHANNEL_AUX_8;\n        case kAudioChannelLabel_HOA_ACN_9:            return MA_CHANNEL_AUX_9;\n        case kAudioChannelLabel_HOA_ACN_10:           return MA_CHANNEL_AUX_10;\n        case kAudioChannelLabel_HOA_ACN_11:           return MA_CHANNEL_AUX_11;\n        case kAudioChannelLabel_HOA_ACN_12:           return MA_CHANNEL_AUX_12;\n        case kAudioChannelLabel_HOA_ACN_13:           return MA_CHANNEL_AUX_13;\n        case kAudioChannelLabel_HOA_ACN_14:           return MA_CHANNEL_AUX_14;\n        case kAudioChannelLabel_HOA_ACN_15:           return MA_CHANNEL_AUX_15;\n        case kAudioChannelLabel_HOA_ACN_65024:        return MA_CHANNEL_NONE;\n    #endif\n\n        default:                                      return MA_CHANNEL_NONE;\n    }\n}\n\nstatic ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    MA_ASSERT(pChannelLayout != NULL);\n\n    if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {\n        UInt32 iChannel;\n        for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) {\n            pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);\n        }\n    } else\n#if 0\n    if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {\n        /* This is the same kind of system that's used by Windows audio APIs. */\n        UInt32 iChannel = 0;\n        UInt32 iBit;\n        AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;\n        for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) {\n            AudioChannelBitmap bit = bitmap & (1 << iBit);\n            if (bit != 0) {\n                pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);\n            }\n        }\n    } else\n#endif\n    {\n        /*\n        Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should\n        be updated to determine the mapping based on the tag.\n        */\n        UInt32 channelCount;\n\n        /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */\n        if (channelMapCap > 0xFFFFFFFF) {\n            channelMapCap = 0xFFFFFFFF;\n        }\n\n        channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap);\n\n        switch (pChannelLayout->mChannelLayoutTag)\n        {\n            case kAudioChannelLayoutTag_Mono:\n            case kAudioChannelLayoutTag_Stereo:\n            case kAudioChannelLayoutTag_StereoHeadphones:\n            case kAudioChannelLayoutTag_MatrixStereo:\n            case kAudioChannelLayoutTag_MidSide:\n            case kAudioChannelLayoutTag_XY:\n            case kAudioChannelLayoutTag_Binaural:\n            case kAudioChannelLayoutTag_Ambisonic_B_Format:\n            {\n                ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);\n            } break;\n\n            case kAudioChannelLayoutTag_Octagonal:\n            {\n                pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;\n                pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;\n            } MA_FALLTHROUGH; /* Intentional fallthrough. */\n            case kAudioChannelLayoutTag_Hexagonal:\n            {\n                pChannelMap[5] = MA_CHANNEL_BACK_CENTER;\n            } MA_FALLTHROUGH; /* Intentional fallthrough. */\n            case kAudioChannelLayoutTag_Pentagonal:\n            {\n                pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;\n            } MA_FALLTHROUGH; /* Intentional fallthrough. */\n            case kAudioChannelLayoutTag_Quadraphonic:\n            {\n                pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;\n                pChannelMap[2] = MA_CHANNEL_BACK_LEFT;\n                pChannelMap[1] = MA_CHANNEL_RIGHT;\n                pChannelMap[0] = MA_CHANNEL_LEFT;\n            } break;\n\n            /* TODO: Add support for more tags here. */\n\n            default:\n            {\n                ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);\n            } break;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n#if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \\\n    (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0)\n#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain\n#else\n/* kAudioObjectPropertyElementMaster is deprecated. */\n#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster\n#endif\n\nstatic ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */\n{\n    AudioObjectPropertyAddress propAddressDevices;\n    UInt32 deviceObjectsDataSize;\n    OSStatus status;\n    AudioObjectID* pDeviceObjectIDs;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pDeviceCount != NULL);\n    MA_ASSERT(ppDeviceObjectIDs != NULL);\n\n    /* Safety. */\n    *pDeviceCount = 0;\n    *ppDeviceObjectIDs = NULL;\n\n    propAddressDevices.mSelector = kAudioHardwarePropertyDevices;\n    propAddressDevices.mScope    = kAudioObjectPropertyScopeGlobal;\n    propAddressDevices.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n    status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);\n    if (status != noErr) {\n        return ma_result_from_OSStatus(status);\n    }\n\n    pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks);\n    if (pDeviceObjectIDs == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);\n    if (status != noErr) {\n        ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);\n        return ma_result_from_OSStatus(status);\n    }\n\n    *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);\n    *ppDeviceObjectIDs = pDeviceObjectIDs;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)\n{\n    AudioObjectPropertyAddress propAddress;\n    UInt32 dataSize;\n    OSStatus status;\n\n    MA_ASSERT(pContext != NULL);\n\n    propAddress.mSelector = kAudioDevicePropertyDeviceUID;\n    propAddress.mScope    = kAudioObjectPropertyScopeGlobal;\n    propAddress.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n    dataSize = sizeof(*pUID);\n    status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);\n    if (status != noErr) {\n        return ma_result_from_OSStatus(status);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)\n{\n    CFStringRef uid;\n    ma_result result;\n\n    MA_ASSERT(pContext != NULL);\n\n    result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {\n        return MA_ERROR;\n    }\n\n    ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid);\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)\n{\n    AudioObjectPropertyAddress propAddress;\n    CFStringRef deviceName = NULL;\n    UInt32 dataSize;\n    OSStatus status;\n\n    MA_ASSERT(pContext != NULL);\n\n    propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;\n    propAddress.mScope    = kAudioObjectPropertyScopeGlobal;\n    propAddress.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n    dataSize = sizeof(deviceName);\n    status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);\n    if (status != noErr) {\n        return ma_result_from_OSStatus(status);\n    }\n\n    if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {\n        return MA_ERROR;\n    }\n\n    ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName);\n    return MA_SUCCESS;\n}\n\nstatic ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)\n{\n    AudioObjectPropertyAddress propAddress;\n    UInt32 dataSize;\n    OSStatus status;\n    AudioBufferList* pBufferList;\n    ma_bool32 isSupported;\n\n    MA_ASSERT(pContext != NULL);\n\n    /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */\n    propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;\n    propAddress.mScope    = scope;\n    propAddress.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n    status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);\n    if (status != noErr) {\n        return MA_FALSE;\n    }\n\n    pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks);\n    if (pBufferList == NULL) {\n        return MA_FALSE;   /* Out of memory. */\n    }\n\n    status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);\n    if (status != noErr) {\n        ma_free(pBufferList, &pContext->allocationCallbacks);\n        return MA_FALSE;\n    }\n\n    isSupported = MA_FALSE;\n    if (pBufferList->mNumberBuffers > 0) {\n        isSupported = MA_TRUE;\n    }\n\n    ma_free(pBufferList, &pContext->allocationCallbacks);\n    return isSupported;\n}\n\nstatic ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)\n{\n    return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);\n}\n\nstatic ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)\n{\n    return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);\n}\n\n\nstatic ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */\n{\n    AudioObjectPropertyAddress propAddress;\n    UInt32 dataSize;\n    OSStatus status;\n    AudioStreamRangedDescription* pDescriptions;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pDescriptionCount != NULL);\n    MA_ASSERT(ppDescriptions != NULL);\n\n    /*\n    TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My\n          MacBook Pro uses s24/32 format, however, which miniaudio does not currently support.\n    */\n    propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/\n    propAddress.mScope    = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;\n    propAddress.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n    status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);\n    if (status != noErr) {\n        return ma_result_from_OSStatus(status);\n    }\n\n    pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks);\n    if (pDescriptions == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);\n    if (status != noErr) {\n        ma_free(pDescriptions, &pContext->allocationCallbacks);\n        return ma_result_from_OSStatus(status);\n    }\n\n    *pDescriptionCount = dataSize / sizeof(*pDescriptions);\n    *ppDescriptions = pDescriptions;\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout)   /* NOTE: Free the returned pointer with ma_free(). */\n{\n    AudioObjectPropertyAddress propAddress;\n    UInt32 dataSize;\n    OSStatus status;\n    AudioChannelLayout* pChannelLayout;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(ppChannelLayout != NULL);\n\n    *ppChannelLayout = NULL;    /* Safety. */\n\n    propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;\n    propAddress.mScope    = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;\n    propAddress.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n    status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);\n    if (status != noErr) {\n        return ma_result_from_OSStatus(status);\n    }\n\n    pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks);\n    if (pChannelLayout == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);\n    if (status != noErr) {\n        ma_free(pChannelLayout, &pContext->allocationCallbacks);\n        return ma_result_from_OSStatus(status);\n    }\n\n    *ppChannelLayout = pChannelLayout;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)\n{\n    AudioChannelLayout* pChannelLayout;\n    ma_result result;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pChannelCount != NULL);\n\n    *pChannelCount = 0; /* Safety. */\n\n    result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {\n        *pChannelCount = pChannelLayout->mNumberChannelDescriptions;\n    } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {\n        *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);\n    } else {\n        *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);\n    }\n\n    ma_free(pChannelLayout, &pContext->allocationCallbacks);\n    return MA_SUCCESS;\n}\n\n#if 0\nstatic ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    AudioChannelLayout* pChannelLayout;\n    ma_result result;\n\n    MA_ASSERT(pContext != NULL);\n\n    result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);\n    if (result != MA_SUCCESS) {\n        return result;  /* Rather than always failing here, would it be more robust to simply assume a default? */\n    }\n\n    result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);\n    if (result != MA_SUCCESS) {\n        ma_free(pChannelLayout, &pContext->allocationCallbacks);\n        return result;\n    }\n\n    ma_free(pChannelLayout, &pContext->allocationCallbacks);\n    return result;\n}\n#endif\n\nstatic ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges)   /* NOTE: Free the returned pointer with ma_free(). */\n{\n    AudioObjectPropertyAddress propAddress;\n    UInt32 dataSize;\n    OSStatus status;\n    AudioValueRange* pSampleRateRanges;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pSampleRateRangesCount != NULL);\n    MA_ASSERT(ppSampleRateRanges != NULL);\n\n    /* Safety. */\n    *pSampleRateRangesCount = 0;\n    *ppSampleRateRanges = NULL;\n\n    propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;\n    propAddress.mScope    = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;\n    propAddress.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n    status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);\n    if (status != noErr) {\n        return ma_result_from_OSStatus(status);\n    }\n\n    pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks);\n    if (pSampleRateRanges == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);\n    if (status != noErr) {\n        ma_free(pSampleRateRanges, &pContext->allocationCallbacks);\n        return ma_result_from_OSStatus(status);\n    }\n\n    *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);\n    *ppSampleRateRanges = pSampleRateRanges;\n    return MA_SUCCESS;\n}\n\n#if 0\nstatic ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut)\n{\n    UInt32 sampleRateRangeCount;\n    AudioValueRange* pSampleRateRanges;\n    ma_result result;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pSampleRateOut != NULL);\n\n    *pSampleRateOut = 0;    /* Safety. */\n\n    result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (sampleRateRangeCount == 0) {\n        ma_free(pSampleRateRanges, &pContext->allocationCallbacks);\n        return MA_ERROR;   /* Should never hit this case should we? */\n    }\n\n    if (sampleRateIn == 0) {\n        /* Search in order of miniaudio's preferred priority. */\n        UInt32 iMALSampleRate;\n        for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {\n            ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];\n            UInt32 iCASampleRate;\n            for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {\n                AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];\n                if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {\n                    *pSampleRateOut = malSampleRate;\n                    ma_free(pSampleRateRanges, &pContext->allocationCallbacks);\n                    return MA_SUCCESS;\n                }\n            }\n        }\n\n        /*\n        If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this\n        case we just fall back to the first one reported by Core Audio.\n        */\n        MA_ASSERT(sampleRateRangeCount > 0);\n\n        *pSampleRateOut = pSampleRateRanges[0].mMinimum;\n        ma_free(pSampleRateRanges, &pContext->allocationCallbacks);\n        return MA_SUCCESS;\n    } else {\n        /* Find the closest match to this sample rate. */\n        UInt32 currentAbsoluteDifference = INT32_MAX;\n        UInt32 iCurrentClosestRange = (UInt32)-1;\n        UInt32 iRange;\n        for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {\n            if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {\n                *pSampleRateOut = sampleRateIn;\n                ma_free(pSampleRateRanges, &pContext->allocationCallbacks);\n                return MA_SUCCESS;\n            } else {\n                UInt32 absoluteDifference;\n                if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {\n                    absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;\n                } else {\n                    absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;\n                }\n\n                if (currentAbsoluteDifference > absoluteDifference) {\n                    currentAbsoluteDifference = absoluteDifference;\n                    iCurrentClosestRange = iRange;\n                }\n            }\n        }\n\n        MA_ASSERT(iCurrentClosestRange != (UInt32)-1);\n\n        *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;\n        ma_free(pSampleRateRanges, &pContext->allocationCallbacks);\n        return MA_SUCCESS;\n    }\n\n    /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */\n    /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/\n    /*return MA_ERROR;*/\n}\n#endif\n\nstatic ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut)\n{\n    AudioObjectPropertyAddress propAddress;\n    AudioValueRange bufferSizeRange;\n    UInt32 dataSize;\n    OSStatus status;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pBufferSizeInFramesOut != NULL);\n\n    *pBufferSizeInFramesOut = 0;    /* Safety. */\n\n    propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;\n    propAddress.mScope    = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;\n    propAddress.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n    dataSize = sizeof(bufferSizeRange);\n    status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);\n    if (status != noErr) {\n        return ma_result_from_OSStatus(status);\n    }\n\n    /* This is just a clamp. */\n    if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {\n        *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;\n    } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {\n        *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;\n    } else {\n        *pBufferSizeInFramesOut = bufferSizeInFramesIn;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut)\n{\n    ma_result result;\n    ma_uint32 chosenBufferSizeInFrames;\n    AudioObjectPropertyAddress propAddress;\n    UInt32 dataSize;\n    OSStatus status;\n\n    MA_ASSERT(pContext != NULL);\n\n    result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */\n    propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;\n    propAddress.mScope    = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;\n    propAddress.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n    ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);\n\n    /* Get the actual size of the buffer. */\n    dataSize = sizeof(*pPeriodSizeInOut);\n    status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);\n    if (status != noErr) {\n        return ma_result_from_OSStatus(status);\n    }\n\n    *pPeriodSizeInOut = chosenBufferSizeInFrames;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID)\n{\n    AudioObjectPropertyAddress propAddressDefaultDevice;\n    UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);\n    AudioObjectID defaultDeviceObjectID;\n    OSStatus status;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pDeviceObjectID != NULL);\n\n    /* Safety. */\n    *pDeviceObjectID = 0;\n\n    propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;\n    propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;\n    if (deviceType == ma_device_type_playback) {\n        propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;\n    } else {\n        propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;\n    }\n\n    defaultDeviceObjectIDSize = sizeof(AudioObjectID);\n    status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);\n    if (status == noErr) {\n        *pDeviceObjectID = defaultDeviceObjectID;\n        return MA_SUCCESS;\n    }\n\n    /* If we get here it means we couldn't find the device. */\n    return MA_NO_DEVICE;\n}\n\nstatic ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pDeviceObjectID != NULL);\n\n    /* Safety. */\n    *pDeviceObjectID = 0;\n\n    if (pDeviceID == NULL) {\n        /* Default device. */\n        return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID);\n    } else {\n        /* Explicit device. */\n        UInt32 deviceCount;\n        AudioObjectID* pDeviceObjectIDs;\n        ma_result result;\n        UInt32 iDevice;\n\n        result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        for (iDevice = 0; iDevice < deviceCount; ++iDevice) {\n            AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];\n\n            char uid[256];\n            if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {\n                continue;\n            }\n\n            if (deviceType == ma_device_type_playback) {\n                if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {\n                    if (strcmp(uid, pDeviceID->coreaudio) == 0) {\n                        *pDeviceObjectID = deviceObjectID;\n                        ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);\n                        return MA_SUCCESS;\n                    }\n                }\n            } else {\n                if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {\n                    if (strcmp(uid, pDeviceID->coreaudio) == 0) {\n                        *pDeviceObjectID = deviceObjectID;\n                        ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);\n                        return MA_SUCCESS;\n                    }\n                }\n            }\n        }\n\n        ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);\n    }\n\n    /* If we get here it means we couldn't find the device. */\n    return MA_NO_DEVICE;\n}\n\n\nstatic ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat)\n{\n    UInt32 deviceFormatDescriptionCount;\n    AudioStreamRangedDescription* pDeviceFormatDescriptions;\n    ma_result result;\n    ma_uint32 desiredSampleRate;\n    ma_uint32 desiredChannelCount;\n    ma_format desiredFormat;\n    AudioStreamBasicDescription bestDeviceFormatSoFar;\n    ma_bool32 hasSupportedFormat;\n    UInt32 iFormat;\n\n    result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    desiredSampleRate = sampleRate;\n    if (desiredSampleRate == 0) {\n        desiredSampleRate = pOrigFormat->mSampleRate;\n    }\n\n    desiredChannelCount = channels;\n    if (desiredChannelCount == 0) {\n        desiredChannelCount = pOrigFormat->mChannelsPerFrame;\n    }\n\n    desiredFormat = format;\n    if (desiredFormat == ma_format_unknown) {\n        result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat);\n        if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) {\n            desiredFormat = g_maFormatPriorities[0];\n        }\n    }\n\n    /*\n    If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next\n    loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases.\n    */\n    MA_ZERO_OBJECT(&bestDeviceFormatSoFar);\n\n    hasSupportedFormat = MA_FALSE;\n    for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {\n        ma_format formatFromDescription;\n        ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription);\n        if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) {\n            hasSupportedFormat = MA_TRUE;\n            bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;\n            break;\n        }\n    }\n\n    if (!hasSupportedFormat) {\n        ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);\n        return MA_FORMAT_NOT_SUPPORTED;\n    }\n\n\n    for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {\n        AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;\n        ma_format thisSampleFormat;\n        ma_result formatResult;\n        ma_format bestSampleFormatSoFar;\n\n        /* If the format is not supported by miniaudio we need to skip this one entirely. */\n        formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);\n        if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {\n            continue;   /* The format is not supported by miniaudio. Skip. */\n        }\n\n        ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);\n\n        /* Getting here means the format is supported by miniaudio which makes this format a candidate. */\n        if (thisDeviceFormat.mSampleRate != desiredSampleRate) {\n            /*\n            The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format\n            so far has an equal sample rate we can just ignore this one.\n            */\n            if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {\n                continue;   /* The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. */\n            } else {\n                /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */\n                if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {\n                    /* This format has a different sample rate _and_ a different channel count. */\n                    if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {\n                        continue;   /* No change to the best format. */\n                    } else {\n                        /*\n                        Both this format and the best so far have different sample rates and different channel counts. Whichever has the\n                        best format is the new best.\n                        */\n                        if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {\n                            bestDeviceFormatSoFar = thisDeviceFormat;\n                            continue;\n                        } else {\n                            continue;   /* No change to the best format. */\n                        }\n                    }\n                } else {\n                    /* This format has a different sample rate but the desired channel count. */\n                    if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {\n                        /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */\n                        if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {\n                            bestDeviceFormatSoFar = thisDeviceFormat;\n                            continue;\n                        } else {\n                            continue;   /* No change to the best format for now. */\n                        }\n                    } else {\n                        /* This format has the desired channel count, but the best so far does not. We have a new best. */\n                        bestDeviceFormatSoFar = thisDeviceFormat;\n                        continue;\n                    }\n                }\n            }\n        } else {\n            /*\n            The sample rates match which makes this format a very high priority contender. If the best format so far has a different\n            sample rate it needs to be replaced with this one.\n            */\n            if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {\n                bestDeviceFormatSoFar = thisDeviceFormat;\n                continue;\n            } else {\n                /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */\n                if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {\n                    /*\n                    In this case this format has the same channel count as what the client is requesting. If the best format so far has\n                    a different count, this one becomes the new best.\n                    */\n                    if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {\n                        bestDeviceFormatSoFar = thisDeviceFormat;\n                        continue;\n                    } else {\n                        /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */\n                        if (thisSampleFormat == desiredFormat) {\n                            bestDeviceFormatSoFar = thisDeviceFormat;\n                            break;  /* Found the exact match. */\n                        } else {\n                            /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */\n                            if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {\n                                bestDeviceFormatSoFar = thisDeviceFormat;\n                                continue;\n                            } else {\n                                continue;   /* No change to the best format for now. */\n                            }\n                        }\n                    }\n                } else {\n                    /*\n                    In this case the channel count is different to what the client has requested. If the best so far has the same channel\n                    count as the requested count then it remains the best.\n                    */\n                    if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {\n                        continue;\n                    } else {\n                        /*\n                        This is the case where both have the same sample rate (good) but different channel counts. Right now both have about\n                        the same priority, but we need to compare the format now.\n                        */\n                        if (thisSampleFormat == bestSampleFormatSoFar) {\n                            if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {\n                                bestDeviceFormatSoFar = thisDeviceFormat;\n                                continue;\n                            } else {\n                                continue;   /* No change to the best format for now. */\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    *pFormat = bestDeviceFormatSoFar;\n\n    ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    AudioUnitScope deviceScope;\n    AudioUnitElement deviceBus;\n    UInt32 channelLayoutSize;\n    OSStatus status;\n    AudioChannelLayout* pChannelLayout;\n    ma_result result;\n\n    MA_ASSERT(pContext != NULL);\n\n    if (deviceType == ma_device_type_playback) {\n        deviceScope = kAudioUnitScope_Input;\n        deviceBus = MA_COREAUDIO_OUTPUT_BUS;\n    } else {\n        deviceScope = kAudioUnitScope_Output;\n        deviceBus = MA_COREAUDIO_INPUT_BUS;\n    }\n\n    status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);\n    if (status != noErr) {\n        return ma_result_from_OSStatus(status);\n    }\n\n    pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks);\n    if (pChannelLayout == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);\n    if (status != noErr) {\n        ma_free(pChannelLayout, &pContext->allocationCallbacks);\n        return ma_result_from_OSStatus(status);\n    }\n\n    result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);\n    if (result != MA_SUCCESS) {\n        ma_free(pChannelLayout, &pContext->allocationCallbacks);\n        return result;\n    }\n\n    ma_free(pChannelLayout, &pContext->allocationCallbacks);\n    return MA_SUCCESS;\n}\n#endif /* MA_APPLE_DESKTOP */\n\n\n#if !defined(MA_APPLE_DESKTOP)\nstatic void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo)\n{\n    MA_ZERO_OBJECT(pInfo);\n    ma_strncpy_s(pInfo->name,         sizeof(pInfo->name),         [pPortDesc.portName UTF8String], (size_t)-1);\n    ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID      UTF8String], (size_t)-1);\n}\n#endif\n\nstatic ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n#if defined(MA_APPLE_DESKTOP)\n    UInt32 deviceCount;\n    AudioObjectID* pDeviceObjectIDs;\n    AudioObjectID defaultDeviceObjectIDPlayback;\n    AudioObjectID defaultDeviceObjectIDCapture;\n    ma_result result;\n    UInt32 iDevice;\n\n    ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback);   /* OK if this fails. */\n    ma_find_default_AudioObjectID(pContext, ma_device_type_capture,  &defaultDeviceObjectIDCapture);    /* OK if this fails. */\n\n    result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    for (iDevice = 0; iDevice < deviceCount; ++iDevice) {\n        AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];\n        ma_device_info info;\n\n        MA_ZERO_OBJECT(&info);\n        if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {\n            continue;\n        }\n        if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {\n            continue;\n        }\n\n        if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {\n            if (deviceObjectID == defaultDeviceObjectIDPlayback) {\n                info.isDefault = MA_TRUE;\n            }\n\n            if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {\n                break;\n            }\n        }\n        if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {\n            if (deviceObjectID == defaultDeviceObjectIDCapture) {\n                info.isDefault = MA_TRUE;\n            }\n\n            if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {\n                break;\n            }\n        }\n    }\n\n    ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);\n#else\n    ma_device_info info;\n    NSArray *pInputs  = [[[AVAudioSession sharedInstance] currentRoute] inputs];\n    NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];\n\n    for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {\n        ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);\n        if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {\n            return MA_SUCCESS;\n        }\n    }\n\n    for (AVAudioSessionPortDescription* pPortDesc in pInputs) {\n        ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);\n        if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {\n            return MA_SUCCESS;\n        }\n    }\n#endif\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    ma_result result;\n\n    MA_ASSERT(pContext != NULL);\n\n#if defined(MA_APPLE_DESKTOP)\n    /* Desktop */\n    {\n        AudioObjectID deviceObjectID;\n        AudioObjectID defaultDeviceObjectID;\n        UInt32 streamDescriptionCount;\n        AudioStreamRangedDescription* pStreamDescriptions;\n        UInt32 iStreamDescription;\n        UInt32 sampleRateRangeCount;\n        AudioValueRange* pSampleRateRanges;\n\n        ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID);     /* OK if this fails. */\n\n        result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        if (deviceObjectID == defaultDeviceObjectID) {\n            pDeviceInfo->isDefault = MA_TRUE;\n        }\n\n        /*\n        There could be a large number of permutations here. Fortunately there is only a single channel count\n        being reported which reduces this quite a bit. For sample rates we're only reporting those that are\n        one of miniaudio's recognized \"standard\" rates. If there are still more formats than can fit into\n        our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen\n        if some driver performs software data conversion and therefore reports every possible format and\n        sample rate.\n        */\n        pDeviceInfo->nativeDataFormatCount = 0;\n\n        /* Formats. */\n        {\n            ma_format uniqueFormats[ma_format_count];\n            ma_uint32 uniqueFormatCount = 0;\n            ma_uint32 channels;\n\n            /* Channels. */\n            result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);\n            if (result != MA_SUCCESS) {\n                return result;\n            }\n\n            /* Formats. */\n            result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);\n            if (result != MA_SUCCESS) {\n                return result;\n            }\n\n            for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {\n                ma_format format;\n                ma_bool32 hasFormatBeenHandled = MA_FALSE;\n                ma_uint32 iOutputFormat;\n                ma_uint32 iSampleRate;\n\n                result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);\n                if (result != MA_SUCCESS) {\n                    continue;\n                }\n\n                MA_ASSERT(format != ma_format_unknown);\n\n                /* Make sure the format isn't already in the output list. */\n                for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) {\n                    if (uniqueFormats[iOutputFormat] == format) {\n                        hasFormatBeenHandled = MA_TRUE;\n                        break;\n                    }\n                }\n\n                /* If we've already handled this format just skip it. */\n                if (hasFormatBeenHandled) {\n                    continue;\n                }\n\n                uniqueFormats[uniqueFormatCount] = format;\n                uniqueFormatCount += 1;\n\n                /* Sample Rates */\n                result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);\n                if (result != MA_SUCCESS) {\n                    return result;\n                }\n\n                /*\n                Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are\n                between this range.\n                */\n                for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {\n                    ma_uint32 iStandardSampleRate;\n                    for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {\n                        ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];\n                        if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) {\n                            /* We have a new data format. Add it to the list. */\n                            pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format     = format;\n                            pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels   = channels;\n                            pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate;\n                            pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags      = 0;\n                            pDeviceInfo->nativeDataFormatCount += 1;\n\n                            if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {\n                                break;  /* No more room for any more formats. */\n                            }\n                        }\n                    }\n                }\n\n                ma_free(pSampleRateRanges, &pContext->allocationCallbacks);\n\n                if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {\n                    break;  /* No more room for any more formats. */\n                }\n            }\n\n            ma_free(pStreamDescriptions, &pContext->allocationCallbacks);\n        }\n    }\n#else\n    /* Mobile */\n    {\n        AudioComponentDescription desc;\n        AudioComponent component;\n        AudioUnit audioUnit;\n        OSStatus status;\n        AudioUnitScope formatScope;\n        AudioUnitElement formatElement;\n        AudioStreamBasicDescription bestFormat;\n        UInt32 propSize;\n\n        /* We want to ensure we use a consistent device name to device enumeration. */\n        if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\\0') {\n            ma_bool32 found = MA_FALSE;\n            if (deviceType == ma_device_type_playback) {\n                NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];\n                for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {\n                    if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {\n                        ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);\n                        found = MA_TRUE;\n                        break;\n                    }\n                }\n            } else {\n                NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];\n                for (AVAudioSessionPortDescription* pPortDesc in pInputs) {\n                    if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {\n                        ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);\n                        found = MA_TRUE;\n                        break;\n                    }\n                }\n            }\n\n            if (!found) {\n                return MA_DOES_NOT_EXIST;\n            }\n        } else {\n            if (deviceType == ma_device_type_playback) {\n                ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n            } else {\n                ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n            }\n        }\n\n\n        /*\n        Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is\n        reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to\n        retrieve from the AVAudioSession shared instance.\n        */\n        desc.componentType = kAudioUnitType_Output;\n        desc.componentSubType = kAudioUnitSubType_RemoteIO;\n        desc.componentManufacturer = kAudioUnitManufacturer_Apple;\n        desc.componentFlags = 0;\n        desc.componentFlagsMask = 0;\n\n        component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);\n        if (component == NULL) {\n            return MA_FAILED_TO_INIT_BACKEND;\n        }\n\n        status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);\n        if (status != noErr) {\n            return ma_result_from_OSStatus(status);\n        }\n\n        formatScope   = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;\n        formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;\n\n        propSize = sizeof(bestFormat);\n        status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);\n        if (status != noErr) {\n            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);\n            return ma_result_from_OSStatus(status);\n        }\n\n        ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);\n        audioUnit = NULL;\n\n        /* Only a single format is being reported for iOS. */\n        pDeviceInfo->nativeDataFormatCount = 1;\n\n        result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;\n\n        /*\n        It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do\n        this we just get the shared instance and inspect.\n        */\n        @autoreleasepool {\n            AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];\n            MA_ASSERT(pAudioSession != NULL);\n\n            pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate;\n        }\n    }\n#endif\n\n    (void)pDeviceInfo; /* Unused. */\n    return MA_SUCCESS;\n}\n\nstatic AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    AudioBufferList* pBufferList;\n    UInt32 audioBufferSizeInBytes;\n    size_t allocationSize;\n\n    MA_ASSERT(sizeInFrames > 0);\n    MA_ASSERT(format != ma_format_unknown);\n    MA_ASSERT(channels > 0);\n\n    allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer);  /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */\n    if (layout == ma_stream_layout_interleaved) {\n        /* Interleaved case. This is the simple case because we just have one buffer. */\n        allocationSize += sizeof(AudioBuffer) * 1;\n    } else {\n        /* Non-interleaved case. This is the more complex case because there's more than one buffer. */\n        allocationSize += sizeof(AudioBuffer) * channels;\n    }\n\n    allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels);\n\n    pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks);\n    if (pBufferList == NULL) {\n        return NULL;\n    }\n\n    audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format));\n\n    if (layout == ma_stream_layout_interleaved) {\n        pBufferList->mNumberBuffers = 1;\n        pBufferList->mBuffers[0].mNumberChannels = channels;\n        pBufferList->mBuffers[0].mDataByteSize   = audioBufferSizeInBytes * channels;\n        pBufferList->mBuffers[0].mData           = (ma_uint8*)pBufferList + sizeof(AudioBufferList);\n    } else {\n        ma_uint32 iBuffer;\n        pBufferList->mNumberBuffers = channels;\n        for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {\n            pBufferList->mBuffers[iBuffer].mNumberChannels = 1;\n            pBufferList->mBuffers[iBuffer].mDataByteSize   = audioBufferSizeInBytes;\n            pBufferList->mBuffers[iBuffer].mData           = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer);\n        }\n    }\n\n    return pBufferList;\n}\n\nstatic ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout)\n{\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(format != ma_format_unknown);\n    MA_ASSERT(channels > 0);\n\n    /* Only resize the buffer if necessary. */\n    if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) {\n        AudioBufferList* pNewAudioBufferList;\n\n        pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks);\n        if (pNewAudioBufferList == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n\n        /* At this point we'll have a new AudioBufferList and we can free the old one. */\n        ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);\n        pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList;\n        pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames;\n    }\n\n    /* Getting here means the capacity of the audio is fine. */\n    return MA_SUCCESS;\n}\n\n\nstatic OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    ma_stream_layout layout;\n\n    MA_ASSERT(pDevice != NULL);\n\n    /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\\n\", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/\n\n    /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */\n    layout = ma_stream_layout_interleaved;\n    if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {\n        layout = ma_stream_layout_deinterleaved;\n    }\n\n    if (layout == ma_stream_layout_interleaved) {\n        /* For now we can assume everything is interleaved. */\n        UInt32 iBuffer;\n        for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {\n            if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {\n                ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);\n                if (frameCountForThisBuffer > 0) {\n                    ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer);\n                }\n\n                /*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"  frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\\n\", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/\n            } else {\n                /*\n                This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's\n                not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just\n                output silence here.\n                */\n                MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);\n                /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"  WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\\n\", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/\n            }\n        }\n    } else {\n        /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */\n        MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS);   /* This should heve been validated at initialization time. */\n\n        /*\n        For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something\n        very strange has happened and we're not going to support it.\n        */\n        if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {\n            ma_uint8 tempBuffer[4096];\n            UInt32 iBuffer;\n\n            for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {\n                ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);\n                ma_uint32 framesRemaining = frameCountPerBuffer;\n\n                while (framesRemaining > 0) {\n                    void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];\n                    ma_uint32 iChannel;\n                    ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);\n                    if (framesToRead > framesRemaining) {\n                        framesToRead = framesRemaining;\n                    }\n\n                    ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);\n\n                    for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {\n                        ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));\n                    }\n\n                    ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);\n\n                    framesRemaining -= framesToRead;\n                }\n            }\n        }\n    }\n\n    (void)pActionFlags;\n    (void)pTimeStamp;\n    (void)busNumber;\n    (void)frameCount;\n\n    return noErr;\n}\n\nstatic OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    AudioBufferList* pRenderedBufferList;\n    ma_result result;\n    ma_stream_layout layout;\n    ma_uint32 iBuffer;\n    OSStatus status;\n\n    MA_ASSERT(pDevice != NULL);\n\n    pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;\n    MA_ASSERT(pRenderedBufferList);\n\n    /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */\n    layout = ma_stream_layout_interleaved;\n    if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {\n        layout = ma_stream_layout_deinterleaved;\n    }\n\n    /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\\n\", (int)busNumber, (int)frameCount, (int)pRenderedBufferList->mNumberBuffers);*/\n\n    /*\n    There has been a situation reported where frame count passed into this function is greater than the capacity of\n    our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be,\n    so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the\n    number of frames requested by this callback.\n    */\n    result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout);\n    if (result != MA_SUCCESS) {\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"Failed to allocate AudioBufferList for capture.\\n\");\n        return noErr;\n    }\n\n    pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;\n    MA_ASSERT(pRenderedBufferList);\n\n    /*\n    When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes\n    that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer\n    being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a\n    problem when a future call to this callback specifies a larger number of frames.\n\n    To work around this we need to explicitly set the size of each buffer to their respective size in bytes.\n    */\n    for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {\n        pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels;\n    }\n\n    status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);\n    if (status != noErr) {\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"  ERROR: AudioUnitRender() failed with %d.\\n\", (int)status);\n        return status;\n    }\n\n    if (layout == ma_stream_layout_interleaved) {\n        for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {\n            if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {\n                ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount);\n                /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"  mDataByteSize=%d.\\n\", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/\n            } else {\n                /*\n                This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's\n                not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.\n                */\n                ma_uint8 silentBuffer[4096];\n                ma_uint32 framesRemaining;\n\n                MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));\n\n                framesRemaining = frameCount;\n                while (framesRemaining > 0) {\n                    ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);\n                    if (framesToSend > framesRemaining) {\n                        framesToSend = framesRemaining;\n                    }\n\n                    ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);\n\n                    framesRemaining -= framesToSend;\n                }\n\n                /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, \"  WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\\n\", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/\n            }\n        }\n    } else {\n        /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */\n        MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS);    /* This should have been validated at initialization time. */\n\n        /*\n        For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something\n        very strange has happened and we're not going to support it.\n        */\n        if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {\n            ma_uint8 tempBuffer[4096];\n            for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {\n                ma_uint32 framesRemaining = frameCount;\n                while (framesRemaining > 0) {\n                    void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];\n                    ma_uint32 iChannel;\n                    ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);\n                    if (framesToSend > framesRemaining) {\n                        framesToSend = framesRemaining;\n                    }\n\n                    for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {\n                        ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));\n                    }\n\n                    ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);\n                    ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend);\n\n                    framesRemaining -= framesToSend;\n                }\n            }\n        }\n    }\n\n    (void)pActionFlags;\n    (void)pTimeStamp;\n    (void)busNumber;\n    (void)frameCount;\n    (void)pUnusedBufferList;\n\n    return noErr;\n}\n\nstatic void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    MA_ASSERT(pDevice != NULL);\n\n    /* Don't do anything if it looks like we're just reinitializing due to a device switch. */\n    if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||\n        ((audioUnit == pDevice->coreaudio.audioUnitCapture)  && pDevice->coreaudio.isSwitchingCaptureDevice)) {\n        return;\n    }\n\n    /*\n    There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like\n    AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)\n    can try waiting on the same lock. I'm going to try working around this by not calling any Core\n    Audio APIs in the callback when the device has been stopped or uninitialized.\n    */\n    if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) {\n        ma_device__on_notification_stopped(pDevice);\n    } else {\n        UInt32 isRunning;\n        UInt32 isRunningSize = sizeof(isRunning);\n        OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);\n        if (status != noErr) {\n            goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */\n        }\n\n        if (!isRunning) {\n            /*\n            The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider:\n\n            1) When the device is unplugged, this will be called _before_ the default device change notification.\n            2) When the device is changed via the default device change notification, this will be called _after_ the switch.\n\n            For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag.\n            */\n            if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||\n                ((audioUnit == pDevice->coreaudio.audioUnitCapture)  && pDevice->coreaudio.isDefaultCaptureDevice)) {\n                /*\n                It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device\n                via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the\n                device to be seamless to the client (we don't want them receiving the stopped event and thinking that the device has stopped when it\n                hasn't!).\n                */\n                if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||\n                    ((audioUnit == pDevice->coreaudio.audioUnitCapture)  && pDevice->coreaudio.isSwitchingCaptureDevice)) {\n                    goto done;\n                }\n\n                /*\n                Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio\n                will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most\n                likely be successful in switching to the new device.\n\n                TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted.\n                */\n                goto done;\n            }\n\n            /* Getting here means we need to stop the device. */\n            ma_device__on_notification_stopped(pDevice);\n        }\n    }\n\n    (void)propertyID; /* Unused. */\n\ndone:\n    /* Always signal the stop event. It's possible for the \"else\" case to get hit which can happen during an interruption. */\n    ma_event_signal(&pDevice->coreaudio.stopEvent);\n}\n\n#if defined(MA_APPLE_DESKTOP)\nstatic ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0;  /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */\nstatic ma_uint32   g_DeviceTrackingInitCounter_CoreAudio = 0;\nstatic ma_mutex    g_DeviceTrackingMutex_CoreAudio;\nstatic ma_device** g_ppTrackedDevices_CoreAudio = NULL;\nstatic ma_uint32   g_TrackedDeviceCap_CoreAudio = 0;\nstatic ma_uint32   g_TrackedDeviceCount_CoreAudio = 0;\n\nstatic OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)\n{\n    ma_device_type deviceType;\n\n    /* Not sure if I really need to check this, but it makes me feel better. */\n    if (addressCount == 0) {\n        return noErr;\n    }\n\n    if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {\n        deviceType = ma_device_type_playback;\n    } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {\n        deviceType = ma_device_type_capture;\n    } else {\n        return noErr;   /* Should never hit this. */\n    }\n\n    ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);\n    {\n        ma_uint32 iDevice;\n        for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {\n            ma_result reinitResult;\n            ma_device* pDevice;\n\n            pDevice = g_ppTrackedDevices_CoreAudio[iDevice];\n            if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {\n                if (deviceType == ma_device_type_playback) {\n                    pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;\n                    reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);\n                    pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;\n                } else {\n                    pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;\n                    reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);\n                    pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE;\n                }\n\n                if (reinitResult == MA_SUCCESS) {\n                    ma_device__post_init_setup(pDevice, deviceType);\n\n                    /* Restart the device if required. If this fails we need to stop the device entirely. */\n                    if (ma_device_get_state(pDevice) == ma_device_state_started) {\n                        OSStatus status;\n                        if (deviceType == ma_device_type_playback) {\n                            status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);\n                            if (status != noErr) {\n                                if (pDevice->type == ma_device_type_duplex) {\n                                    ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);\n                                }\n                                ma_device__set_state(pDevice, ma_device_state_stopped);\n                            }\n                        } else if (deviceType == ma_device_type_capture) {\n                            status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);\n                            if (status != noErr) {\n                                if (pDevice->type == ma_device_type_duplex) {\n                                    ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);\n                                }\n                                ma_device__set_state(pDevice, ma_device_state_stopped);\n                            }\n                        }\n                    }\n\n                    ma_device__on_notification_rerouted(pDevice);\n                }\n            }\n        }\n    }\n    ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);\n\n    /* Unused parameters. */\n    (void)objectID;\n    (void)pUserData;\n\n    return noErr;\n}\n\nstatic ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n\n    ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);\n    {\n        /* Don't do anything if we've already initializd device tracking. */\n        if (g_DeviceTrackingInitCounter_CoreAudio == 0) {\n            AudioObjectPropertyAddress propAddress;\n            propAddress.mScope    = kAudioObjectPropertyScopeGlobal;\n            propAddress.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n            ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio);\n\n            propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;\n            ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);\n\n            propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;\n            ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);\n\n        }\n        g_DeviceTrackingInitCounter_CoreAudio += 1;\n    }\n    ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n\n    ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);\n    {\n        if (g_DeviceTrackingInitCounter_CoreAudio > 0)\n            g_DeviceTrackingInitCounter_CoreAudio -= 1;\n\n        if (g_DeviceTrackingInitCounter_CoreAudio == 0) {\n            AudioObjectPropertyAddress propAddress;\n            propAddress.mScope    = kAudioObjectPropertyScopeGlobal;\n            propAddress.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n            propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;\n            ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);\n\n            propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;\n            ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);\n\n            /* At this point there should be no tracked devices. If not there's an error somewhere. */\n            if (g_ppTrackedDevices_CoreAudio != NULL) {\n                ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, \"You have uninitialized all contexts while an associated device is still active.\");\n                ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);\n                return MA_INVALID_OPERATION;\n            }\n\n            ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);\n        }\n    }\n    ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device__track__coreaudio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);\n    {\n        /* Allocate memory if required. */\n        if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) {\n            ma_uint32 newCap;\n            ma_device** ppNewDevices;\n\n            newCap = g_TrackedDeviceCap_CoreAudio * 2;\n            if (newCap == 0) {\n                newCap = 1;\n            }\n\n            ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks);\n            if (ppNewDevices == NULL) {\n                ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);\n                return MA_OUT_OF_MEMORY;\n            }\n\n            g_ppTrackedDevices_CoreAudio = ppNewDevices;\n            g_TrackedDeviceCap_CoreAudio = newCap;\n        }\n\n        g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice;\n        g_TrackedDeviceCount_CoreAudio += 1;\n    }\n    ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device__untrack__coreaudio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);\n    {\n        ma_uint32 iDevice;\n        for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {\n            if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) {\n                /* We've found the device. We now need to remove it from the list. */\n                ma_uint32 jDevice;\n                for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {\n                    g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];\n                }\n\n                g_TrackedDeviceCount_CoreAudio -= 1;\n\n                /* If there's nothing else in the list we need to free memory. */\n                if (g_TrackedDeviceCount_CoreAudio == 0) {\n                    ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks);\n                    g_ppTrackedDevices_CoreAudio = NULL;\n                    g_TrackedDeviceCap_CoreAudio = 0;\n                }\n\n                break;\n            }\n        }\n    }\n    ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);\n\n    return MA_SUCCESS;\n}\n#endif\n\n#if defined(MA_APPLE_MOBILE)\n@interface ma_ios_notification_handler:NSObject {\n    ma_device* m_pDevice;\n}\n@end\n\n@implementation ma_ios_notification_handler\n-(id)init:(ma_device*)pDevice\n{\n    self = [super init];\n    m_pDevice = pDevice;\n\n    /* For route changes. */\n    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];\n\n    /* For interruptions. */\n    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];\n\n    return self;\n}\n\n-(void)dealloc\n{\n    [self remove_handler];\n\n    #if defined(__has_feature)\n        #if !__has_feature(objc_arc)\n            [super dealloc];\n        #endif\n    #endif\n}\n\n-(void)remove_handler\n{\n    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];\n    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];\n}\n\n-(void)handle_interruption:(NSNotification*)pNotification\n{\n    NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue];\n    switch (type)\n    {\n        case AVAudioSessionInterruptionTypeBegan:\n        {\n            ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, \"[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\\n\");\n\n            /*\n            Core Audio will have stopped the internal device automatically, but we need explicitly\n            stop it at a higher level to ensure miniaudio-specific state is updated for consistency.\n            */\n            ma_device_stop(m_pDevice);\n\n            /*\n            Fire the notification after the device has been stopped to ensure it's in the correct\n            state when the notification handler is invoked.\n            */\n            ma_device__on_notification_interruption_began(m_pDevice);\n        } break;\n\n        case AVAudioSessionInterruptionTypeEnded:\n        {\n            ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, \"[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\\n\");\n            ma_device__on_notification_interruption_ended(m_pDevice);\n        } break;\n    }\n}\n\n-(void)handle_route_change:(NSNotification*)pNotification\n{\n    AVAudioSession* pSession = [AVAudioSession sharedInstance];\n\n    NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];\n    switch (reason)\n    {\n        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:\n        {\n            ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, \"[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\\n\");\n        } break;\n\n        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:\n        {\n            ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, \"[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\\n\");\n        } break;\n\n        case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:\n        {\n            ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, \"[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\\n\");\n        } break;\n\n        case AVAudioSessionRouteChangeReasonWakeFromSleep:\n        {\n            ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, \"[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\\n\");\n        } break;\n\n        case AVAudioSessionRouteChangeReasonOverride:\n        {\n            ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, \"[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\\n\");\n        } break;\n\n        case AVAudioSessionRouteChangeReasonCategoryChange:\n        {\n            ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, \"[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\\n\");\n        } break;\n\n        case AVAudioSessionRouteChangeReasonUnknown:\n        default:\n        {\n            ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, \"[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\\n\");\n        } break;\n    }\n\n    ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, \"[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\\n\", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels);\n\n    /* Let the application know about the route change. */\n    ma_device__on_notification_rerouted(m_pDevice);\n}\n@end\n#endif\n\nstatic ma_result ma_device_uninit__coreaudio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized);\n\n#if defined(MA_APPLE_DESKTOP)\n    /*\n    Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll\n    just gracefully ignore it.\n    */\n    ma_device__untrack__coreaudio(pDevice);\n#endif\n#if defined(MA_APPLE_MOBILE)\n    if (pDevice->coreaudio.pNotificationHandler != NULL) {\n        ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler;\n        [pNotificationHandler remove_handler];\n    }\n#endif\n\n    if (pDevice->coreaudio.audioUnitCapture != NULL) {\n        ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);\n    }\n    if (pDevice->coreaudio.audioUnitPlayback != NULL) {\n        ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);\n    }\n\n    if (pDevice->coreaudio.pAudioBufferList) {\n        ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);\n    }\n\n    return MA_SUCCESS;\n}\n\ntypedef struct\n{\n    ma_bool32 allowNominalSampleRateChange;\n\n    /* Input. */\n    ma_format formatIn;\n    ma_uint32 channelsIn;\n    ma_uint32 sampleRateIn;\n    ma_channel channelMapIn[MA_MAX_CHANNELS];\n    ma_uint32 periodSizeInFramesIn;\n    ma_uint32 periodSizeInMillisecondsIn;\n    ma_uint32 periodsIn;\n    ma_share_mode shareMode;\n    ma_performance_profile performanceProfile;\n    ma_bool32 registerStopEvent;\n\n    /* Output. */\n#if defined(MA_APPLE_DESKTOP)\n    AudioObjectID deviceObjectID;\n#endif\n    AudioComponent component;\n    AudioUnit audioUnit;\n    AudioBufferList* pAudioBufferList;  /* Only used for input devices. */\n    ma_format formatOut;\n    ma_uint32 channelsOut;\n    ma_uint32 sampleRateOut;\n    ma_channel channelMapOut[MA_MAX_CHANNELS];\n    ma_uint32 periodSizeInFramesOut;\n    ma_uint32 periodsOut;\n    char deviceName[256];\n} ma_device_init_internal_data__coreaudio;\n\nstatic ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference)   /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */\n{\n    ma_result result;\n    OSStatus status;\n    UInt32 enableIOFlag;\n    AudioStreamBasicDescription bestFormat;\n    UInt32 actualPeriodSizeInFrames;\n    AURenderCallbackStruct callbackInfo;\n#if defined(MA_APPLE_DESKTOP)\n    AudioObjectID deviceObjectID;\n#endif\n\n    /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */\n    if (deviceType == ma_device_type_duplex) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);\n\n#if defined(MA_APPLE_DESKTOP)\n    pData->deviceObjectID = 0;\n#endif\n    pData->component = NULL;\n    pData->audioUnit = NULL;\n    pData->pAudioBufferList = NULL;\n\n#if defined(MA_APPLE_DESKTOP)\n    result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pData->deviceObjectID = deviceObjectID;\n#endif\n\n    /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */\n    pData->periodsOut = pData->periodsIn;\n    if (pData->periodsOut == 0) {\n        pData->periodsOut = MA_DEFAULT_PERIODS;\n    }\n    if (pData->periodsOut > 16) {\n        pData->periodsOut = 16;\n    }\n\n\n    /* Audio unit. */\n    status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);\n    if (status != noErr) {\n        return ma_result_from_OSStatus(status);\n    }\n\n\n    /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */\n    enableIOFlag = 1;\n    if (deviceType == ma_device_type_capture) {\n        enableIOFlag = 0;\n    }\n\n    status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));\n    if (status != noErr) {\n        ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n        return ma_result_from_OSStatus(status);\n    }\n\n    enableIOFlag = (enableIOFlag == 0) ? 1 : 0;\n    status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));\n    if (status != noErr) {\n        ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n        return ma_result_from_OSStatus(status);\n    }\n\n\n    /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */\n#if defined(MA_APPLE_DESKTOP)\n    status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID));\n    if (status != noErr) {\n        ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n        return ma_result_from_OSStatus(result);\n    }\n#else\n    /*\n    For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change\n    the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices.\n    */\n    if (pDeviceID != NULL) {\n        if (deviceType == ma_device_type_capture) {\n            ma_bool32 found = MA_FALSE;\n            NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];\n            for (AVAudioSessionPortDescription* pPortDesc in pInputs) {\n                if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {\n                    [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];\n                    found = MA_TRUE;\n                    break;\n                }\n            }\n\n            if (found == MA_FALSE) {\n                return MA_DOES_NOT_EXIST;\n            }\n        }\n    }\n#endif\n\n    /*\n    Format. This is the hardest part of initialization because there's a few variables to take into account.\n      1) The format must be supported by the device.\n      2) The format must be supported miniaudio.\n      3) There's a priority that miniaudio prefers.\n\n    Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The\n    most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same\n    for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.\n\n    On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.\n    */\n    {\n        AudioStreamBasicDescription origFormat;\n        UInt32 origFormatSize = sizeof(origFormat);\n        AudioUnitScope   formatScope   = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;\n        AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;\n\n        if (deviceType == ma_device_type_playback) {\n            status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);\n        } else {\n            status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);\n        }\n        if (status != noErr) {\n            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n            return ma_result_from_OSStatus(status);\n        }\n\n    #if defined(MA_APPLE_DESKTOP)\n        result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat);\n        if (result != MA_SUCCESS) {\n            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n            return result;\n        }\n\n        /*\n        Technical Note TN2091: Device input using the HAL Output Audio Unit\n            https://developer.apple.com/library/archive/technotes/tn2091/_index.html\n\n        This documentation says the following:\n\n            The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY\n            variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate\n            conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with\n            another AudioConverter.\n\n        The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We\n        therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it\n        safe and apply the same rule to output as well.\n\n        I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender()\n        returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but\n        this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format.\n\n        Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with\n        this, however, is that it actually changes the sample rate at the operating system level and not just the application. This\n        could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a\n        configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample\n        rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run\n        the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is\n        changed by miniaudio.\n        */\n        if (pData->allowNominalSampleRateChange) {\n            AudioValueRange sampleRateRange;\n            AudioObjectPropertyAddress propAddress;\n\n            sampleRateRange.mMinimum = bestFormat.mSampleRate;\n            sampleRateRange.mMaximum = bestFormat.mSampleRate;\n\n            propAddress.mSelector = kAudioDevicePropertyNominalSampleRate;\n            propAddress.mScope    = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;\n            propAddress.mElement  = AUDIO_OBJECT_PROPERTY_ELEMENT;\n\n            status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange);\n            if (status != noErr) {\n                bestFormat.mSampleRate = origFormat.mSampleRate;\n            }\n        } else {\n            bestFormat.mSampleRate = origFormat.mSampleRate;\n        }\n\n        status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));\n        if (status != noErr) {\n            /* We failed to set the format, so fall back to the current format of the audio unit. */\n            bestFormat = origFormat;\n        }\n    #else\n        bestFormat = origFormat;\n\n        /*\n        Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try\n        setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since\n        it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I\n        can tell, it looks like the sample rate is shared between playback and capture for everything.\n        */\n        @autoreleasepool {\n            AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];\n            MA_ASSERT(pAudioSession != NULL);\n\n            [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];\n            bestFormat.mSampleRate = pAudioSession.sampleRate;\n\n            /*\n            I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with\n            AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead.\n            */\n            if (deviceType == ma_device_type_playback) {\n                bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;\n            }\n            if (deviceType == ma_device_type_capture) {\n                bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;\n            }\n        }\n\n        status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));\n        if (status != noErr) {\n            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n            return ma_result_from_OSStatus(status);\n        }\n    #endif\n\n        result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);\n        if (result != MA_SUCCESS) {\n            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n            return result;\n        }\n\n        if (pData->formatOut == ma_format_unknown) {\n            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n            return MA_FORMAT_NOT_SUPPORTED;\n        }\n\n        pData->channelsOut   = bestFormat.mChannelsPerFrame;\n        pData->sampleRateOut = bestFormat.mSampleRate;\n    }\n\n    /* Clamp the channel count for safety. */\n    if (pData->channelsOut > MA_MAX_CHANNELS) {\n        pData->channelsOut = MA_MAX_CHANNELS;\n    }\n\n    /*\n    Internal channel map. This is weird in my testing. If I use the AudioObject to get the\n    channel map, the channel descriptions are set to \"Unknown\" for some reason. To work around\n    this it looks like retrieving it from the AudioUnit will work. However, and this is where\n    it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore\n    I'm going to fall back to a default assumption in these cases.\n    */\n#if defined(MA_APPLE_DESKTOP)\n    result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut);\n    if (result != MA_SUCCESS) {\n    #if 0\n        /* Try falling back to the channel map from the AudioObject. */\n        result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    #else\n        /* Fall back to default assumptions. */\n        ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);\n    #endif\n    }\n#else\n    /* TODO: Figure out how to get the channel map using AVAudioSession. */\n    ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);\n#endif\n\n\n    /* Buffer size. Not allowing this to be configurable on iOS. */\n    if (pData->periodSizeInFramesIn == 0) {\n        if (pData->periodSizeInMillisecondsIn == 0) {\n            if (pData->performanceProfile == ma_performance_profile_low_latency) {\n                actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut);\n            } else {\n                actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut);\n            }\n        } else {\n            actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut);\n        }\n    } else {\n        actualPeriodSizeInFrames = pData->periodSizeInFramesIn;\n    }\n\n#if defined(MA_APPLE_DESKTOP)\n    result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n#else\n    /*\n    On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point\n    number. I don't trust any potential truncation errors due to converting from float to integer\n    so I'm going to explicitly set the actual period size to the next power of 2.\n    */\n    @autoreleasepool {\n        AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];\n        MA_ASSERT(pAudioSession != NULL);\n\n        [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil];\n        actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate));\n    }\n#endif\n\n\n    /*\n    During testing I discovered that the buffer size can be too big. You'll get an error like this:\n\n      kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512\n\n    Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that\n    of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.\n    */\n    status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames));\n    if (status != noErr) {\n        ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n        return ma_result_from_OSStatus(status);\n    }\n\n    pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames;\n\n    /* We need a buffer list if this is an input device. We render into this in the input callback. */\n    if (deviceType == ma_device_type_capture) {\n        ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;\n        AudioBufferList* pBufferList;\n\n        pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks);\n        if (pBufferList == NULL) {\n            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n            return MA_OUT_OF_MEMORY;\n        }\n\n        pData->pAudioBufferList = pBufferList;\n    }\n\n    /* Callbacks. */\n    callbackInfo.inputProcRefCon = pDevice_DoNotReference;\n    if (deviceType == ma_device_type_playback) {\n        callbackInfo.inputProc = ma_on_output__coreaudio;\n        status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));\n        if (status != noErr) {\n            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n            return ma_result_from_OSStatus(status);\n        }\n    } else {\n        callbackInfo.inputProc = ma_on_input__coreaudio;\n        status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));\n        if (status != noErr) {\n            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n            return ma_result_from_OSStatus(status);\n        }\n    }\n\n    /* We need to listen for stop events. */\n    if (pData->registerStopEvent) {\n        status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);\n        if (status != noErr) {\n            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n            return ma_result_from_OSStatus(status);\n        }\n    }\n\n    /* Initialize the audio unit. */\n    status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);\n    if (status != noErr) {\n        ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks);\n        pData->pAudioBufferList = NULL;\n        ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);\n        return ma_result_from_OSStatus(status);\n    }\n\n    /* Grab the name. */\n#if defined(MA_APPLE_DESKTOP)\n    ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);\n#else\n    if (deviceType == ma_device_type_playback) {\n        ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);\n    } else {\n        ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);\n    }\n#endif\n\n    return result;\n}\n\n#if defined(MA_APPLE_DESKTOP)\nstatic ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)\n{\n    ma_device_init_internal_data__coreaudio data;\n    ma_result result;\n\n    /* This should only be called for playback or capture, not duplex. */\n    if (deviceType == ma_device_type_duplex) {\n        return MA_INVALID_ARGS;\n    }\n\n    data.allowNominalSampleRateChange = MA_FALSE;   /* Don't change the nominal sample rate when switching devices. */\n\n    if (deviceType == ma_device_type_capture) {\n        data.formatIn               = pDevice->capture.format;\n        data.channelsIn             = pDevice->capture.channels;\n        data.sampleRateIn           = pDevice->sampleRate;\n        MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));\n        data.shareMode              = pDevice->capture.shareMode;\n        data.performanceProfile     = pDevice->coreaudio.originalPerformanceProfile;\n        data.registerStopEvent      = MA_TRUE;\n\n        if (disposePreviousAudioUnit) {\n            ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);\n            ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);\n        }\n        if (pDevice->coreaudio.pAudioBufferList) {\n            ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);\n        }\n    } else if (deviceType == ma_device_type_playback) {\n        data.formatIn               = pDevice->playback.format;\n        data.channelsIn             = pDevice->playback.channels;\n        data.sampleRateIn           = pDevice->sampleRate;\n        MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));\n        data.shareMode              = pDevice->playback.shareMode;\n        data.performanceProfile     = pDevice->coreaudio.originalPerformanceProfile;\n        data.registerStopEvent      = (pDevice->type != ma_device_type_duplex);\n\n        if (disposePreviousAudioUnit) {\n            ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);\n            ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);\n        }\n    }\n    data.periodSizeInFramesIn       = pDevice->coreaudio.originalPeriodSizeInFrames;\n    data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds;\n    data.periodsIn                  = pDevice->coreaudio.originalPeriods;\n\n    /* Need at least 3 periods for duplex. */\n    if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) {\n        data.periodsIn = 3;\n    }\n\n    result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (deviceType == ma_device_type_capture) {\n    #if defined(MA_APPLE_DESKTOP)\n        pDevice->coreaudio.deviceObjectIDCapture     = (ma_uint32)data.deviceObjectID;\n        ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);\n    #endif\n        pDevice->coreaudio.audioUnitCapture          = (ma_ptr)data.audioUnit;\n        pDevice->coreaudio.pAudioBufferList          = (ma_ptr)data.pAudioBufferList;\n        pDevice->coreaudio.audioBufferCapInFrames    = data.periodSizeInFramesOut;\n\n        pDevice->capture.internalFormat              = data.formatOut;\n        pDevice->capture.internalChannels            = data.channelsOut;\n        pDevice->capture.internalSampleRate          = data.sampleRateOut;\n        MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));\n        pDevice->capture.internalPeriodSizeInFrames  = data.periodSizeInFramesOut;\n        pDevice->capture.internalPeriods             = data.periodsOut;\n    } else if (deviceType == ma_device_type_playback) {\n    #if defined(MA_APPLE_DESKTOP)\n        pDevice->coreaudio.deviceObjectIDPlayback    = (ma_uint32)data.deviceObjectID;\n        ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);\n    #endif\n        pDevice->coreaudio.audioUnitPlayback         = (ma_ptr)data.audioUnit;\n\n        pDevice->playback.internalFormat             = data.formatOut;\n        pDevice->playback.internalChannels           = data.channelsOut;\n        pDevice->playback.internalSampleRate         = data.sampleRateOut;\n        MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));\n        pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;\n        pDevice->playback.internalPeriods            = data.periodsOut;\n    }\n\n    return MA_SUCCESS;\n}\n#endif /* MA_APPLE_DESKTOP */\n\nstatic ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    ma_result result;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(pConfig != NULL);\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    /* No exclusive mode with the Core Audio backend for now. */\n    if (((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode  == ma_share_mode_exclusive) ||\n        ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) {\n        return MA_SHARE_MODE_NOT_SUPPORTED;\n    }\n\n    /* Capture needs to be initialized first. */\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        ma_device_init_internal_data__coreaudio data;\n        data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;\n        data.formatIn                     = pDescriptorCapture->format;\n        data.channelsIn                   = pDescriptorCapture->channels;\n        data.sampleRateIn                 = pDescriptorCapture->sampleRate;\n        MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));\n        data.periodSizeInFramesIn         = pDescriptorCapture->periodSizeInFrames;\n        data.periodSizeInMillisecondsIn   = pDescriptorCapture->periodSizeInMilliseconds;\n        data.periodsIn                    = pDescriptorCapture->periodCount;\n        data.shareMode                    = pDescriptorCapture->shareMode;\n        data.performanceProfile           = pConfig->performanceProfile;\n        data.registerStopEvent            = MA_TRUE;\n\n        /* Need at least 3 periods for duplex. */\n        if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {\n            data.periodsIn = 3;\n        }\n\n        result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pDevice->coreaudio.isDefaultCaptureDevice           = (pConfig->capture.pDeviceID == NULL);\n    #if defined(MA_APPLE_DESKTOP)\n        pDevice->coreaudio.deviceObjectIDCapture            = (ma_uint32)data.deviceObjectID;\n    #endif\n        pDevice->coreaudio.audioUnitCapture                 = (ma_ptr)data.audioUnit;\n        pDevice->coreaudio.pAudioBufferList                 = (ma_ptr)data.pAudioBufferList;\n        pDevice->coreaudio.audioBufferCapInFrames           = data.periodSizeInFramesOut;\n        pDevice->coreaudio.originalPeriodSizeInFrames       = pDescriptorCapture->periodSizeInFrames;\n        pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;\n        pDevice->coreaudio.originalPeriods                  = pDescriptorCapture->periodCount;\n        pDevice->coreaudio.originalPerformanceProfile       = pConfig->performanceProfile;\n\n        pDescriptorCapture->format                          = data.formatOut;\n        pDescriptorCapture->channels                        = data.channelsOut;\n        pDescriptorCapture->sampleRate                      = data.sampleRateOut;\n        MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));\n        pDescriptorCapture->periodSizeInFrames              = data.periodSizeInFramesOut;\n        pDescriptorCapture->periodCount                     = data.periodsOut;\n\n    #if defined(MA_APPLE_DESKTOP)\n        ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);\n\n        /*\n        If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly\n        switch the device in the background.\n        */\n        if (pConfig->capture.pDeviceID == NULL) {\n            ma_device__track__coreaudio(pDevice);\n        }\n    #endif\n    }\n\n    /* Playback. */\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        ma_device_init_internal_data__coreaudio data;\n        data.allowNominalSampleRateChange   = pConfig->coreaudio.allowNominalSampleRateChange;\n        data.formatIn                       = pDescriptorPlayback->format;\n        data.channelsIn                     = pDescriptorPlayback->channels;\n        data.sampleRateIn                   = pDescriptorPlayback->sampleRate;\n        MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));\n        data.shareMode                      = pDescriptorPlayback->shareMode;\n        data.performanceProfile             = pConfig->performanceProfile;\n\n        /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */\n        if (pConfig->deviceType == ma_device_type_duplex) {\n            data.periodSizeInFramesIn       = pDescriptorCapture->periodSizeInFrames;\n            data.periodsIn                  = pDescriptorCapture->periodCount;\n            data.registerStopEvent          = MA_FALSE;\n        } else {\n            data.periodSizeInFramesIn       = pDescriptorPlayback->periodSizeInFrames;\n            data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;\n            data.periodsIn                  = pDescriptorPlayback->periodCount;\n            data.registerStopEvent          = MA_TRUE;\n        }\n\n        result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice);\n        if (result != MA_SUCCESS) {\n            if (pConfig->deviceType == ma_device_type_duplex) {\n                ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);\n                if (pDevice->coreaudio.pAudioBufferList) {\n                    ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);\n                }\n            }\n            return result;\n        }\n\n        pDevice->coreaudio.isDefaultPlaybackDevice          = (pConfig->playback.pDeviceID == NULL);\n    #if defined(MA_APPLE_DESKTOP)\n        pDevice->coreaudio.deviceObjectIDPlayback           = (ma_uint32)data.deviceObjectID;\n    #endif\n        pDevice->coreaudio.audioUnitPlayback                = (ma_ptr)data.audioUnit;\n        pDevice->coreaudio.originalPeriodSizeInFrames       = pDescriptorPlayback->periodSizeInFrames;\n        pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;\n        pDevice->coreaudio.originalPeriods                  = pDescriptorPlayback->periodCount;\n        pDevice->coreaudio.originalPerformanceProfile       = pConfig->performanceProfile;\n\n        pDescriptorPlayback->format                         = data.formatOut;\n        pDescriptorPlayback->channels                       = data.channelsOut;\n        pDescriptorPlayback->sampleRate                     = data.sampleRateOut;\n        MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));\n        pDescriptorPlayback->periodSizeInFrames             = data.periodSizeInFramesOut;\n        pDescriptorPlayback->periodCount                    = data.periodsOut;\n\n    #if defined(MA_APPLE_DESKTOP)\n        ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);\n\n        /*\n        If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly\n        switch the device in the background.\n        */\n        if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) {\n            ma_device__track__coreaudio(pDevice);\n        }\n    #endif\n    }\n\n\n\n    /*\n    When stopping the device, a callback is called on another thread. We need to wait for this callback\n    before returning from ma_device_stop(). This event is used for this.\n    */\n    ma_event_init(&pDevice->coreaudio.stopEvent);\n\n    /*\n    We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done\n    differently on non-Desktop Apple platforms.\n    */\n#if defined(MA_APPLE_MOBILE)\n    pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice];\n#endif\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_device_start__coreaudio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);\n        if (status != noErr) {\n            return ma_result_from_OSStatus(status);\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);\n        if (status != noErr) {\n            if (pDevice->type == ma_device_type_duplex) {\n                ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);\n            }\n            return ma_result_from_OSStatus(status);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__coreaudio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);\n        if (status != noErr) {\n            return ma_result_from_OSStatus(status);\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);\n        if (status != noErr) {\n            return ma_result_from_OSStatus(status);\n        }\n    }\n\n    /* We need to wait for the callback to finish before returning. */\n    ma_event_wait(&pDevice->coreaudio.stopEvent);\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_context_uninit__coreaudio(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_coreaudio);\n\n#if defined(MA_APPLE_MOBILE)\n    if (!pContext->coreaudio.noAudioSessionDeactivate) {\n        if (![[AVAudioSession sharedInstance] setActive:false error:nil]) {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"Failed to deactivate audio session.\");\n            return MA_FAILED_TO_INIT_BACKEND;\n        }\n    }\n#endif\n\n#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)\n    ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);\n    ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);\n    ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);\n#endif\n\n#if !defined(MA_APPLE_MOBILE)\n    ma_context__uninit_device_tracking__coreaudio(pContext);\n#endif\n\n    (void)pContext;\n    return MA_SUCCESS;\n}\n\n#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0)\nstatic AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category)\n{\n    /* The \"default\" and \"none\" categories are treated different and should not be used as an input into this function. */\n    MA_ASSERT(category != ma_ios_session_category_default);\n    MA_ASSERT(category != ma_ios_session_category_none);\n\n    switch (category) {\n        case ma_ios_session_category_ambient:         return AVAudioSessionCategoryAmbient;\n        case ma_ios_session_category_solo_ambient:    return AVAudioSessionCategorySoloAmbient;\n        case ma_ios_session_category_playback:        return AVAudioSessionCategoryPlayback;\n        case ma_ios_session_category_record:          return AVAudioSessionCategoryRecord;\n        case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord;\n        case ma_ios_session_category_multi_route:     return AVAudioSessionCategoryMultiRoute;\n        case ma_ios_session_category_none:            return AVAudioSessionCategoryAmbient;\n        case ma_ios_session_category_default:         return AVAudioSessionCategoryAmbient;\n        default:                                      return AVAudioSessionCategoryAmbient;\n    }\n}\n#endif\n\nstatic ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n#if !defined(MA_APPLE_MOBILE)\n    ma_result result;\n#endif\n\n    MA_ASSERT(pConfig != NULL);\n    MA_ASSERT(pContext != NULL);\n\n#if defined(MA_APPLE_MOBILE)\n    @autoreleasepool {\n        AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];\n        AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions;\n\n        MA_ASSERT(pAudioSession != NULL);\n\n        if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) {\n            /*\n            I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails\n            we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category.\n            */\n        #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH)\n            options |= AVAudioSessionCategoryOptionDefaultToSpeaker;\n        #endif\n\n            if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) {\n                /* Using PlayAndRecord */\n            } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) {\n                /* Using Playback */\n            } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) {\n                /* Using Record */\n            } else {\n                /* Leave as default? */\n            }\n        } else {\n            if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) {\n            #if defined(__IPHONE_12_0)\n                if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) {\n                    return MA_INVALID_OPERATION;    /* Failed to set session category. */\n                }\n            #else\n                /* Ignore the session category on version 11 and older, but post a warning. */\n                ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, \"Session category only supported in iOS 12 and newer.\");\n            #endif\n            }\n        }\n\n        if (!pConfig->coreaudio.noAudioSessionActivate) {\n            if (![pAudioSession setActive:true error:nil]) {\n                ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"Failed to activate audio session.\");\n                return MA_FAILED_TO_INIT_BACKEND;\n            }\n        }\n    }\n#endif\n\n#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)\n    pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), \"/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation\");\n    if (pContext->coreaudio.hCoreFoundation == NULL) {\n        return MA_API_NOT_FOUND;\n    }\n\n    pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, \"CFStringGetCString\");\n    pContext->coreaudio.CFRelease          = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, \"CFRelease\");\n\n\n    pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), \"/System/Library/Frameworks/CoreAudio.framework/CoreAudio\");\n    if (pContext->coreaudio.hCoreAudio == NULL) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);\n        return MA_API_NOT_FOUND;\n    }\n\n    pContext->coreaudio.AudioObjectGetPropertyData        = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, \"AudioObjectGetPropertyData\");\n    pContext->coreaudio.AudioObjectGetPropertyDataSize    = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, \"AudioObjectGetPropertyDataSize\");\n    pContext->coreaudio.AudioObjectSetPropertyData        = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, \"AudioObjectSetPropertyData\");\n    pContext->coreaudio.AudioObjectAddPropertyListener    = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, \"AudioObjectAddPropertyListener\");\n    pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, \"AudioObjectRemovePropertyListener\");\n\n    /*\n    It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still\n    defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.\n    The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to\n    AudioToolbox.\n    */\n    pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), \"/System/Library/Frameworks/AudioUnit.framework/AudioUnit\");\n    if (pContext->coreaudio.hAudioUnit == NULL) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);\n        ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);\n        return MA_API_NOT_FOUND;\n    }\n\n    if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioComponentFindNext\") == NULL) {\n        /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */\n        ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);\n        pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), \"/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox\");\n        if (pContext->coreaudio.hAudioUnit == NULL) {\n            ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);\n            ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);\n            return MA_API_NOT_FOUND;\n        }\n    }\n\n    pContext->coreaudio.AudioComponentFindNext            = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioComponentFindNext\");\n    pContext->coreaudio.AudioComponentInstanceDispose     = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioComponentInstanceDispose\");\n    pContext->coreaudio.AudioComponentInstanceNew         = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioComponentInstanceNew\");\n    pContext->coreaudio.AudioOutputUnitStart              = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioOutputUnitStart\");\n    pContext->coreaudio.AudioOutputUnitStop               = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioOutputUnitStop\");\n    pContext->coreaudio.AudioUnitAddPropertyListener      = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioUnitAddPropertyListener\");\n    pContext->coreaudio.AudioUnitGetPropertyInfo          = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioUnitGetPropertyInfo\");\n    pContext->coreaudio.AudioUnitGetProperty              = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioUnitGetProperty\");\n    pContext->coreaudio.AudioUnitSetProperty              = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioUnitSetProperty\");\n    pContext->coreaudio.AudioUnitInitialize               = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioUnitInitialize\");\n    pContext->coreaudio.AudioUnitRender                   = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, \"AudioUnitRender\");\n#else\n    pContext->coreaudio.CFStringGetCString                = (ma_proc)CFStringGetCString;\n    pContext->coreaudio.CFRelease                         = (ma_proc)CFRelease;\n\n    #if defined(MA_APPLE_DESKTOP)\n    pContext->coreaudio.AudioObjectGetPropertyData        = (ma_proc)AudioObjectGetPropertyData;\n    pContext->coreaudio.AudioObjectGetPropertyDataSize    = (ma_proc)AudioObjectGetPropertyDataSize;\n    pContext->coreaudio.AudioObjectSetPropertyData        = (ma_proc)AudioObjectSetPropertyData;\n    pContext->coreaudio.AudioObjectAddPropertyListener    = (ma_proc)AudioObjectAddPropertyListener;\n    pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;\n    #endif\n\n    pContext->coreaudio.AudioComponentFindNext            = (ma_proc)AudioComponentFindNext;\n    pContext->coreaudio.AudioComponentInstanceDispose     = (ma_proc)AudioComponentInstanceDispose;\n    pContext->coreaudio.AudioComponentInstanceNew         = (ma_proc)AudioComponentInstanceNew;\n    pContext->coreaudio.AudioOutputUnitStart              = (ma_proc)AudioOutputUnitStart;\n    pContext->coreaudio.AudioOutputUnitStop               = (ma_proc)AudioOutputUnitStop;\n    pContext->coreaudio.AudioUnitAddPropertyListener      = (ma_proc)AudioUnitAddPropertyListener;\n    pContext->coreaudio.AudioUnitGetPropertyInfo          = (ma_proc)AudioUnitGetPropertyInfo;\n    pContext->coreaudio.AudioUnitGetProperty              = (ma_proc)AudioUnitGetProperty;\n    pContext->coreaudio.AudioUnitSetProperty              = (ma_proc)AudioUnitSetProperty;\n    pContext->coreaudio.AudioUnitInitialize               = (ma_proc)AudioUnitInitialize;\n    pContext->coreaudio.AudioUnitRender                   = (ma_proc)AudioUnitRender;\n#endif\n\n    /* Audio component. */\n    {\n        AudioComponentDescription desc;\n        desc.componentType         = kAudioUnitType_Output;\n    #if defined(MA_APPLE_DESKTOP)\n        desc.componentSubType      = kAudioUnitSubType_HALOutput;\n    #else\n        desc.componentSubType      = kAudioUnitSubType_RemoteIO;\n    #endif\n        desc.componentManufacturer = kAudioUnitManufacturer_Apple;\n        desc.componentFlags        = 0;\n        desc.componentFlagsMask    = 0;\n\n        pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);\n        if (pContext->coreaudio.component == NULL) {\n        #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)\n            ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);\n            ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);\n            ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);\n        #endif\n            return MA_FAILED_TO_INIT_BACKEND;\n        }\n    }\n\n#if !defined(MA_APPLE_MOBILE)\n    result = ma_context__init_device_tracking__coreaudio(pContext);\n    if (result != MA_SUCCESS) {\n    #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)\n        ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);\n        ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);\n        ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);\n    #endif\n        return result;\n    }\n#endif\n\n    pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;\n\n    pCallbacks->onContextInit             = ma_context_init__coreaudio;\n    pCallbacks->onContextUninit           = ma_context_uninit__coreaudio;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__coreaudio;\n    pCallbacks->onDeviceInit              = ma_device_init__coreaudio;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__coreaudio;\n    pCallbacks->onDeviceStart             = ma_device_start__coreaudio;\n    pCallbacks->onDeviceStop              = ma_device_stop__coreaudio;\n    pCallbacks->onDeviceRead              = NULL;\n    pCallbacks->onDeviceWrite             = NULL;\n    pCallbacks->onDeviceDataLoop          = NULL;\n\n    return MA_SUCCESS;\n}\n#endif  /* Core Audio */\n\n\n\n/******************************************************************************\n\nsndio Backend\n\n******************************************************************************/\n#ifdef MA_HAS_SNDIO\n#include <fcntl.h>\n\n/*\nOnly supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due\nto miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device\njust doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's\ndemand for it or if I can get it tested and debugged more thoroughly.\n*/\n#if 0\n#if defined(__NetBSD__) || defined(__OpenBSD__)\n#include <sys/audioio.h>\n#endif\n#if defined(__FreeBSD__) || defined(__DragonFly__)\n#include <sys/soundcard.h>\n#endif\n#endif\n\n#define MA_SIO_DEVANY   \"default\"\n#define MA_SIO_PLAY     1\n#define MA_SIO_REC      2\n#define MA_SIO_NENC     8\n#define MA_SIO_NCHAN    8\n#define MA_SIO_NRATE    16\n#define MA_SIO_NCONF    4\n\nstruct ma_sio_hdl; /* <-- Opaque */\n\nstruct ma_sio_par\n{\n    unsigned int bits;\n    unsigned int bps;\n    unsigned int sig;\n    unsigned int le;\n    unsigned int msb;\n    unsigned int rchan;\n    unsigned int pchan;\n    unsigned int rate;\n    unsigned int bufsz;\n    unsigned int xrun;\n    unsigned int round;\n    unsigned int appbufsz;\n    int __pad[3];\n    unsigned int __magic;\n};\n\nstruct ma_sio_enc\n{\n    unsigned int bits;\n    unsigned int bps;\n    unsigned int sig;\n    unsigned int le;\n    unsigned int msb;\n};\n\nstruct ma_sio_conf\n{\n    unsigned int enc;\n    unsigned int rchan;\n    unsigned int pchan;\n    unsigned int rate;\n};\n\nstruct ma_sio_cap\n{\n    struct ma_sio_enc enc[MA_SIO_NENC];\n    unsigned int rchan[MA_SIO_NCHAN];\n    unsigned int pchan[MA_SIO_NCHAN];\n    unsigned int rate[MA_SIO_NRATE];\n    int __pad[7];\n    unsigned int nconf;\n    struct ma_sio_conf confs[MA_SIO_NCONF];\n};\n\ntypedef struct ma_sio_hdl* (* ma_sio_open_proc)   (const char*, unsigned int, int);\ntypedef void               (* ma_sio_close_proc)  (struct ma_sio_hdl*);\ntypedef int                (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);\ntypedef int                (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);\ntypedef int                (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);\ntypedef size_t             (* ma_sio_write_proc)  (struct ma_sio_hdl*, const void*, size_t);\ntypedef size_t             (* ma_sio_read_proc)   (struct ma_sio_hdl*, void*, size_t);\ntypedef int                (* ma_sio_start_proc)  (struct ma_sio_hdl*);\ntypedef int                (* ma_sio_stop_proc)   (struct ma_sio_hdl*);\ntypedef int                (* ma_sio_initpar_proc)(struct ma_sio_par*);\n\nstatic ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate)   /* Lower = higher priority */\n{\n    ma_uint32 i;\n    for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {\n        if (g_maStandardSampleRatePriorities[i] == sampleRate) {\n            return i;\n        }\n    }\n\n    return (ma_uint32)-1;\n}\n\nstatic ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)\n{\n    /* We only support native-endian right now. */\n    if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {\n        return ma_format_unknown;\n    }\n\n    if (bits ==  8 && bps == 1 && sig == 0) {\n        return ma_format_u8;\n    }\n    if (bits == 16 && bps == 2 && sig == 1) {\n        return ma_format_s16;\n    }\n    if (bits == 24 && bps == 3 && sig == 1) {\n        return ma_format_s24;\n    }\n    if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {\n        /*return ma_format_s24_32;*/\n    }\n    if (bits == 32 && bps == 4 && sig == 1) {\n        return ma_format_s32;\n    }\n\n    return ma_format_unknown;\n}\n\nstatic ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)\n{\n    ma_format bestFormat;\n    unsigned int iConfig;\n\n    MA_ASSERT(caps != NULL);\n\n    bestFormat = ma_format_unknown;\n    for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {\n        unsigned int iEncoding;\n        for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {\n            unsigned int bits;\n            unsigned int bps;\n            unsigned int sig;\n            unsigned int le;\n            unsigned int msb;\n            ma_format format;\n\n            if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {\n                continue;\n            }\n\n            bits = caps->enc[iEncoding].bits;\n            bps  = caps->enc[iEncoding].bps;\n            sig  = caps->enc[iEncoding].sig;\n            le   = caps->enc[iEncoding].le;\n            msb  = caps->enc[iEncoding].msb;\n            format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);\n            if (format == ma_format_unknown) {\n                continue;   /* Format not supported. */\n            }\n\n            if (bestFormat == ma_format_unknown) {\n                bestFormat = format;\n            } else {\n                if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) {    /* <-- Lower = better. */\n                    bestFormat = format;\n                }\n            }\n        }\n    }\n\n    return bestFormat;\n}\n\nstatic ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)\n{\n    ma_uint32 maxChannels;\n    unsigned int iConfig;\n\n    MA_ASSERT(caps != NULL);\n    MA_ASSERT(requiredFormat != ma_format_unknown);\n\n    /* Just pick whatever configuration has the most channels. */\n    maxChannels = 0;\n    for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {\n        /* The encoding should be of requiredFormat. */\n        unsigned int iEncoding;\n        for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {\n            unsigned int iChannel;\n            unsigned int bits;\n            unsigned int bps;\n            unsigned int sig;\n            unsigned int le;\n            unsigned int msb;\n            ma_format format;\n\n            if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {\n                continue;\n            }\n\n            bits = caps->enc[iEncoding].bits;\n            bps  = caps->enc[iEncoding].bps;\n            sig  = caps->enc[iEncoding].sig;\n            le   = caps->enc[iEncoding].le;\n            msb  = caps->enc[iEncoding].msb;\n            format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);\n            if (format != requiredFormat) {\n                continue;\n            }\n\n            /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */\n            for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {\n                unsigned int chan = 0;\n                unsigned int channels;\n\n                if (deviceType == ma_device_type_playback) {\n                    chan = caps->confs[iConfig].pchan;\n                } else {\n                    chan = caps->confs[iConfig].rchan;\n                }\n\n                if ((chan & (1UL << iChannel)) == 0) {\n                    continue;\n                }\n\n                if (deviceType == ma_device_type_playback) {\n                    channels = caps->pchan[iChannel];\n                } else {\n                    channels = caps->rchan[iChannel];\n                }\n\n                if (maxChannels < channels) {\n                    maxChannels = channels;\n                }\n            }\n        }\n    }\n\n    return maxChannels;\n}\n\nstatic ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels)\n{\n    ma_uint32 firstSampleRate;\n    ma_uint32 bestSampleRate;\n    unsigned int iConfig;\n\n    MA_ASSERT(caps != NULL);\n    MA_ASSERT(requiredFormat != ma_format_unknown);\n    MA_ASSERT(requiredChannels > 0);\n    MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS);\n\n    firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */\n    bestSampleRate  = 0;\n\n    for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {\n        /* The encoding should be of requiredFormat. */\n        unsigned int iEncoding;\n        for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {\n            unsigned int iChannel;\n            unsigned int bits;\n            unsigned int bps;\n            unsigned int sig;\n            unsigned int le;\n            unsigned int msb;\n            ma_format format;\n\n            if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {\n                continue;\n            }\n\n            bits = caps->enc[iEncoding].bits;\n            bps  = caps->enc[iEncoding].bps;\n            sig  = caps->enc[iEncoding].sig;\n            le   = caps->enc[iEncoding].le;\n            msb  = caps->enc[iEncoding].msb;\n            format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);\n            if (format != requiredFormat) {\n                continue;\n            }\n\n            /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */\n            for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {\n                unsigned int chan = 0;\n                unsigned int channels;\n                unsigned int iRate;\n\n                if (deviceType == ma_device_type_playback) {\n                    chan = caps->confs[iConfig].pchan;\n                } else {\n                    chan = caps->confs[iConfig].rchan;\n                }\n\n                if ((chan & (1UL << iChannel)) == 0) {\n                    continue;\n                }\n\n                if (deviceType == ma_device_type_playback) {\n                    channels = caps->pchan[iChannel];\n                } else {\n                    channels = caps->rchan[iChannel];\n                }\n\n                if (channels != requiredChannels) {\n                    continue;\n                }\n\n                /* Getting here means we have found a compatible encoding/channel pair. */\n                for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {\n                    ma_uint32 rate = (ma_uint32)caps->rate[iRate];\n                    ma_uint32 ratePriority;\n\n                    if (firstSampleRate == 0) {\n                        firstSampleRate = rate;\n                    }\n\n                    /* Disregard this rate if it's not a standard one. */\n                    ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate);\n                    if (ratePriority == (ma_uint32)-1) {\n                        continue;\n                    }\n\n                    if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) {   /* Lower = better. */\n                        bestSampleRate = rate;\n                    }\n                }\n            }\n        }\n    }\n\n    /* If a standard sample rate was not found just fall back to the first one that was iterated. */\n    if (bestSampleRate == 0) {\n        bestSampleRate = firstSampleRate;\n    }\n\n    return bestSampleRate;\n}\n\n\nstatic ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    ma_bool32 isTerminating = MA_FALSE;\n    struct ma_sio_hdl* handle;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */\n\n    /* Playback. */\n    if (!isTerminating) {\n        handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);\n        if (handle != NULL) {\n            /* Supports playback. */\n            ma_device_info deviceInfo;\n            MA_ZERO_OBJECT(&deviceInfo);\n            ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);\n            ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);\n\n            isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n\n            ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);\n        }\n    }\n\n    /* Capture. */\n    if (!isTerminating) {\n        handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);\n        if (handle != NULL) {\n            /* Supports capture. */\n            ma_device_info deviceInfo;\n            MA_ZERO_OBJECT(&deviceInfo);\n            ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), \"default\");\n            ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);\n\n            isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n\n            ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    char devid[256];\n    struct ma_sio_hdl* handle;\n    struct ma_sio_cap caps;\n    unsigned int iConfig;\n\n    MA_ASSERT(pContext != NULL);\n\n    /* We need to open the device before we can get information about it. */\n    if (pDeviceID == NULL) {\n        ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);\n        ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);\n    } else {\n        ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);\n        ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);\n    }\n\n    handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);\n    if (handle == NULL) {\n        return MA_NO_DEVICE;\n    }\n\n    if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {\n        return MA_ERROR;\n    }\n\n    pDeviceInfo->nativeDataFormatCount = 0;\n\n    for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {\n        /*\n        The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give\n        preference to some formats over others.\n        */\n        unsigned int iEncoding;\n        unsigned int iChannel;\n        unsigned int iRate;\n\n        for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {\n            unsigned int bits;\n            unsigned int bps;\n            unsigned int sig;\n            unsigned int le;\n            unsigned int msb;\n            ma_format format;\n\n            if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {\n                continue;\n            }\n\n            bits = caps.enc[iEncoding].bits;\n            bps  = caps.enc[iEncoding].bps;\n            sig  = caps.enc[iEncoding].sig;\n            le   = caps.enc[iEncoding].le;\n            msb  = caps.enc[iEncoding].msb;\n            format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);\n            if (format == ma_format_unknown) {\n                continue;   /* Format not supported. */\n            }\n\n\n            /* Channels. */\n            for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {\n                unsigned int chan = 0;\n                unsigned int channels;\n\n                if (deviceType == ma_device_type_playback) {\n                    chan = caps.confs[iConfig].pchan;\n                } else {\n                    chan = caps.confs[iConfig].rchan;\n                }\n\n                if ((chan & (1UL << iChannel)) == 0) {\n                    continue;\n                }\n\n                if (deviceType == ma_device_type_playback) {\n                    channels = caps.pchan[iChannel];\n                } else {\n                    channels = caps.rchan[iChannel];\n                }\n\n\n                /* Sample Rates. */\n                for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {\n                    if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {\n                        ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0);\n                    }\n                }\n            }\n        }\n    }\n\n    ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_uninit__sndio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);\n    }\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)\n{\n    const char* pDeviceName;\n    ma_ptr handle;\n    int openFlags = 0;\n    struct ma_sio_cap caps;\n    struct ma_sio_par par;\n    const ma_device_id* pDeviceID;\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_format internalFormat;\n    ma_uint32 internalChannels;\n    ma_uint32 internalSampleRate;\n    ma_uint32 internalPeriodSizeInFrames;\n    ma_uint32 internalPeriods;\n\n    MA_ASSERT(pConfig    != NULL);\n    MA_ASSERT(deviceType != ma_device_type_duplex);\n    MA_ASSERT(pDevice    != NULL);\n\n    if (deviceType == ma_device_type_capture) {\n        openFlags = MA_SIO_REC;\n    } else {\n        openFlags = MA_SIO_PLAY;\n    }\n\n    pDeviceID  = pDescriptor->pDeviceID;\n    format     = pDescriptor->format;\n    channels   = pDescriptor->channels;\n    sampleRate = pDescriptor->sampleRate;\n\n    pDeviceName = MA_SIO_DEVANY;\n    if (pDeviceID != NULL) {\n        pDeviceName = pDeviceID->sndio;\n    }\n\n    handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0);\n    if (handle == NULL) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[sndio] Failed to open device.\");\n        return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n    }\n\n    /* We need to retrieve the device caps to determine the most appropriate format to use. */\n    if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {\n        ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[sndio] Failed to retrieve device caps.\");\n        return MA_ERROR;\n    }\n\n    /*\n    Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real\n    way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this\n    to the requested channels, regardless of whether or not the default channel count is requested.\n\n    For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the\n    value returned by ma_find_best_channels_from_sio_cap__sndio().\n    */\n    if (deviceType == ma_device_type_capture) {\n        if (format == ma_format_unknown) {\n            format = ma_find_best_format_from_sio_cap__sndio(&caps);\n        }\n\n        if (channels == 0) {\n            if (strlen(pDeviceName) > strlen(\"rsnd/\") && strncmp(pDeviceName, \"rsnd/\", strlen(\"rsnd/\")) == 0) {\n                channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);\n            } else {\n                channels = MA_DEFAULT_CHANNELS;\n            }\n        }\n    } else {\n        if (format == ma_format_unknown) {\n            format = ma_find_best_format_from_sio_cap__sndio(&caps);\n        }\n\n        if (channels == 0) {\n            if (strlen(pDeviceName) > strlen(\"rsnd/\") && strncmp(pDeviceName, \"rsnd/\", strlen(\"rsnd/\")) == 0) {\n                channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);\n            } else {\n                channels = MA_DEFAULT_CHANNELS;\n            }\n        }\n    }\n\n    if (sampleRate == 0) {\n        sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);\n    }\n\n\n    ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);\n    par.msb = 0;\n    par.le  = ma_is_little_endian();\n\n    switch (format) {\n        case ma_format_u8:\n        {\n            par.bits = 8;\n            par.bps  = 1;\n            par.sig  = 0;\n        } break;\n\n        case ma_format_s24:\n        {\n            par.bits = 24;\n            par.bps  = 3;\n            par.sig  = 1;\n        } break;\n\n        case ma_format_s32:\n        {\n            par.bits = 32;\n            par.bps  = 4;\n            par.sig  = 1;\n        } break;\n\n        case ma_format_s16:\n        case ma_format_f32:\n        case ma_format_unknown:\n        default:\n        {\n            par.bits = 16;\n            par.bps  = 2;\n            par.sig  = 1;\n        } break;\n    }\n\n    if (deviceType == ma_device_type_capture) {\n        par.rchan = channels;\n    } else {\n        par.pchan = channels;\n    }\n\n    par.rate = sampleRate;\n\n    internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile);\n\n    par.round    = internalPeriodSizeInFrames;\n    par.appbufsz = par.round * pDescriptor->periodCount;\n\n    if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {\n        ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[sndio] Failed to set buffer size.\");\n        return MA_ERROR;\n    }\n\n    if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {\n        ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[sndio] Failed to retrieve buffer size.\");\n        return MA_ERROR;\n    }\n\n    internalFormat             = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);\n    internalChannels           = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;\n    internalSampleRate         = par.rate;\n    internalPeriods            = par.appbufsz / par.round;\n    internalPeriodSizeInFrames = par.round;\n\n    if (deviceType == ma_device_type_capture) {\n        pDevice->sndio.handleCapture  = handle;\n    } else {\n        pDevice->sndio.handlePlayback = handle;\n    }\n\n    pDescriptor->format             = internalFormat;\n    pDescriptor->channels           = internalChannels;\n    pDescriptor->sampleRate         = internalSampleRate;\n    ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);\n    pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;\n    pDescriptor->periodCount        = internalPeriods;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    MA_ZERO_OBJECT(&pDevice->sndio);\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_start__sndio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);   /* <-- Doesn't actually playback until data is written. */\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__sndio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    /*\n    From the documentation:\n\n        The sio_stop() function puts the audio subsystem in the same state as before sio_start() is called. It stops recording, drains the play buffer and then\n        stops playback. If samples to play are queued but playback hasn't started yet then playback is forced immediately; playback will actually stop once the\n        buffer is drained. In no case are samples in the play buffer discarded.\n\n    Therefore, sio_stop() performs all of the necessary draining for us.\n    */\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)\n{\n    int result;\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = 0;\n    }\n\n    result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));\n    if (result == 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[sndio] Failed to send data from the client to the device.\");\n        return MA_IO_ERROR;\n    }\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = frameCount;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)\n{\n    int result;\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));\n    if (result == 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[sndio] Failed to read data from the device to be sent to the device.\");\n        return MA_IO_ERROR;\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = frameCount;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_uninit__sndio(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_sndio);\n\n    (void)pContext;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n#ifndef MA_NO_RUNTIME_LINKING\n    const char* libsndioNames[] = {\n        \"libsndio.so\"\n    };\n    size_t i;\n\n    for (i = 0; i < ma_countof(libsndioNames); ++i) {\n        pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]);\n        if (pContext->sndio.sndioSO != NULL) {\n            break;\n        }\n    }\n\n    if (pContext->sndio.sndioSO == NULL) {\n        return MA_NO_BACKEND;\n    }\n\n    pContext->sndio.sio_open    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, \"sio_open\");\n    pContext->sndio.sio_close   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, \"sio_close\");\n    pContext->sndio.sio_setpar  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, \"sio_setpar\");\n    pContext->sndio.sio_getpar  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, \"sio_getpar\");\n    pContext->sndio.sio_getcap  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, \"sio_getcap\");\n    pContext->sndio.sio_write   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, \"sio_write\");\n    pContext->sndio.sio_read    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, \"sio_read\");\n    pContext->sndio.sio_start   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, \"sio_start\");\n    pContext->sndio.sio_stop    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, \"sio_stop\");\n    pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, \"sio_initpar\");\n#else\n    pContext->sndio.sio_open    = sio_open;\n    pContext->sndio.sio_close   = sio_close;\n    pContext->sndio.sio_setpar  = sio_setpar;\n    pContext->sndio.sio_getpar  = sio_getpar;\n    pContext->sndio.sio_getcap  = sio_getcap;\n    pContext->sndio.sio_write   = sio_write;\n    pContext->sndio.sio_read    = sio_read;\n    pContext->sndio.sio_start   = sio_start;\n    pContext->sndio.sio_stop    = sio_stop;\n    pContext->sndio.sio_initpar = sio_initpar;\n#endif\n\n    pCallbacks->onContextInit             = ma_context_init__sndio;\n    pCallbacks->onContextUninit           = ma_context_uninit__sndio;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__sndio;\n    pCallbacks->onDeviceInit              = ma_device_init__sndio;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__sndio;\n    pCallbacks->onDeviceStart             = ma_device_start__sndio;\n    pCallbacks->onDeviceStop              = ma_device_stop__sndio;\n    pCallbacks->onDeviceRead              = ma_device_read__sndio;\n    pCallbacks->onDeviceWrite             = ma_device_write__sndio;\n    pCallbacks->onDeviceDataLoop          = NULL;\n\n    (void)pConfig;\n    return MA_SUCCESS;\n}\n#endif  /* sndio */\n\n\n\n/******************************************************************************\n\naudio(4) Backend\n\n******************************************************************************/\n#ifdef MA_HAS_AUDIO4\n#include <fcntl.h>\n#include <poll.h>\n#include <errno.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/ioctl.h>\n#include <sys/audioio.h>\n\n#if defined(__OpenBSD__)\n    #include <sys/param.h>\n    #if defined(OpenBSD) && OpenBSD >= 201709\n        #define MA_AUDIO4_USE_NEW_API\n    #endif\n#endif\n\nstatic void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)\n{\n    size_t baseLen;\n\n    MA_ASSERT(id != NULL);\n    MA_ASSERT(idSize > 0);\n    MA_ASSERT(deviceIndex >= 0);\n\n    baseLen = strlen(base);\n    MA_ASSERT(idSize > baseLen);\n\n    ma_strcpy_s(id, idSize, base);\n    ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);\n}\n\nstatic ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)\n{\n    size_t idLen;\n    size_t baseLen;\n    const char* deviceIndexStr;\n\n    MA_ASSERT(id != NULL);\n    MA_ASSERT(base != NULL);\n    MA_ASSERT(pIndexOut != NULL);\n\n    idLen = strlen(id);\n    baseLen = strlen(base);\n    if (idLen <= baseLen) {\n        return MA_ERROR;   /* Doesn't look like the id starts with the base. */\n    }\n\n    if (strncmp(id, base, baseLen) != 0) {\n        return MA_ERROR;   /* ID does not begin with base. */\n    }\n\n    deviceIndexStr = id + baseLen;\n    if (deviceIndexStr[0] == '\\0') {\n        return MA_ERROR;   /* No index specified in the ID. */\n    }\n\n    if (pIndexOut) {\n        *pIndexOut = atoi(deviceIndexStr);\n    }\n\n    return MA_SUCCESS;\n}\n\n\n#if !defined(MA_AUDIO4_USE_NEW_API)    /* Old API */\nstatic ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)\n{\n    if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {\n        return ma_format_u8;\n    } else {\n        if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {\n            if (precision == 16) {\n                return ma_format_s16;\n            } else if (precision == 24) {\n                return ma_format_s24;\n            } else if (precision == 32) {\n                return ma_format_s32;\n            }\n        } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {\n            if (precision == 16) {\n                return ma_format_s16;\n            } else if (precision == 24) {\n                return ma_format_s24;\n            } else if (precision == 32) {\n                return ma_format_s32;\n            }\n        }\n    }\n\n    return ma_format_unknown;  /* Encoding not supported. */\n}\n\nstatic void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)\n{\n    MA_ASSERT(pEncoding  != NULL);\n    MA_ASSERT(pPrecision != NULL);\n\n    switch (format)\n    {\n        case ma_format_u8:\n        {\n            *pEncoding = AUDIO_ENCODING_ULINEAR;\n            *pPrecision = 8;\n        } break;\n\n        case ma_format_s24:\n        {\n            *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;\n            *pPrecision = 24;\n        } break;\n\n        case ma_format_s32:\n        {\n            *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;\n            *pPrecision = 32;\n        } break;\n\n        case ma_format_s16:\n        case ma_format_f32:\n        case ma_format_unknown:\n        default:\n        {\n            *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;\n            *pPrecision = 16;\n        } break;\n    }\n}\n\nstatic ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)\n{\n    return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);\n}\n\nstatic ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat)\n{\n    audio_encoding_t encoding;\n    ma_uint32 iFormat;\n    int counter = 0;\n\n    /* First check to see if the preferred format is supported. */\n    if (preferredFormat != ma_format_unknown) {\n        counter = 0;\n        for (;;) {\n            MA_ZERO_OBJECT(&encoding);\n            encoding.index = counter;\n            if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {\n                break;\n            }\n\n            if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {\n                return preferredFormat;  /* Found the preferred format. */\n            }\n\n            /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */\n            counter += 1;\n        }\n    }\n\n    /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */\n    for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {\n        ma_format format = g_maFormatPriorities[iFormat];\n\n        counter = 0;\n        for (;;) {\n            MA_ZERO_OBJECT(&encoding);\n            encoding.index = counter;\n            if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {\n                break;\n            }\n\n            if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {\n                return format;  /* Found a workable format. */\n            }\n\n            /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */\n            counter += 1;\n        }\n    }\n\n    /* Getting here means not appropriate format was found. */\n    return ma_format_unknown;\n}\n#else\nstatic ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)\n{\n    if (par->bits == 8 && par->bps == 1 && par->sig == 0) {\n        return ma_format_u8;\n    }\n    if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {\n        return ma_format_s16;\n    }\n    if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {\n        return ma_format_s24;\n    }\n    if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {\n        return ma_format_f32;\n    }\n\n    /* Format not supported. */\n    return ma_format_unknown;\n}\n#endif\n\nstatic ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo)\n{\n    audio_device_t fdDevice;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(fd >= 0);\n    MA_ASSERT(pDeviceInfo != NULL);\n\n    (void)pContext;\n    (void)deviceType;\n\n    if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {\n        return MA_ERROR;   /* Failed to retrieve device info. */\n    }\n\n    /* Name. */\n    ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name);\n\n    #if !defined(MA_AUDIO4_USE_NEW_API)\n    {\n        audio_info_t fdInfo;\n        int counter = 0;\n        ma_uint32 channels;\n        ma_uint32 sampleRate;\n\n        if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {\n            return MA_ERROR;\n        }\n\n        if (deviceType == ma_device_type_playback) {\n            channels   = fdInfo.play.channels;\n            sampleRate = fdInfo.play.sample_rate;\n        } else {\n            channels   = fdInfo.record.channels;\n            sampleRate = fdInfo.record.sample_rate;\n        }\n\n        /* Supported formats. We get this by looking at the encodings. */\n        pDeviceInfo->nativeDataFormatCount = 0;\n        for (;;) {\n            audio_encoding_t encoding;\n            ma_format format;\n\n            MA_ZERO_OBJECT(&encoding);\n            encoding.index = counter;\n            if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {\n                break;\n            }\n\n            format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);\n            if (format != ma_format_unknown) {\n                ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);\n            }\n\n            counter += 1;\n        }\n    }\n    #else\n    {\n        struct audio_swpar fdPar;\n        ma_format format;\n        ma_uint32 channels;\n        ma_uint32 sampleRate;\n\n        if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {\n            return MA_ERROR;\n        }\n\n        format = ma_format_from_swpar__audio4(&fdPar);\n        if (format == ma_format_unknown) {\n            return MA_FORMAT_NOT_SUPPORTED;\n        }\n\n        if (deviceType == ma_device_type_playback) {\n            channels = fdPar.pchan;\n        } else {\n            channels = fdPar.rchan;\n        }\n\n        sampleRate = fdPar.rate;\n\n        pDeviceInfo->nativeDataFormatCount = 0;\n        ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);\n    }\n    #endif\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    const int maxDevices = 64;\n    char devpath[256];\n    int iDevice;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    /*\n    Every device will be named \"/dev/audioN\", with a \"/dev/audioctlN\" equivalent. We use the \"/dev/audioctlN\"\n    version here since we can open it even when another process has control of the \"/dev/audioN\" device.\n    */\n    for (iDevice = 0; iDevice < maxDevices; ++iDevice) {\n        struct stat st;\n        int fd;\n        ma_bool32 isTerminating = MA_FALSE;\n\n        ma_strcpy_s(devpath, sizeof(devpath), \"/dev/audioctl\");\n        ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);\n\n        if (stat(devpath, &st) < 0) {\n            break;\n        }\n\n        /* The device exists, but we need to check if it's usable as playback and/or capture. */\n\n        /* Playback. */\n        if (!isTerminating) {\n            fd = open(devpath, O_RDONLY, 0);\n            if (fd >= 0) {\n                /* Supports playback. */\n                ma_device_info deviceInfo;\n                MA_ZERO_OBJECT(&deviceInfo);\n                ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), \"/dev/audio\", iDevice);\n                if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {\n                    isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n                }\n\n                close(fd);\n            }\n        }\n\n        /* Capture. */\n        if (!isTerminating) {\n            fd = open(devpath, O_WRONLY, 0);\n            if (fd >= 0) {\n                /* Supports capture. */\n                ma_device_info deviceInfo;\n                MA_ZERO_OBJECT(&deviceInfo);\n                ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), \"/dev/audio\", iDevice);\n                if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {\n                    isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n                }\n\n                close(fd);\n            }\n        }\n\n        if (isTerminating) {\n            break;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    int fd = -1;\n    int deviceIndex = -1;\n    char ctlid[256];\n    ma_result result;\n\n    MA_ASSERT(pContext != NULL);\n\n    /*\n    We need to open the \"/dev/audioctlN\" device to get the info. To do this we need to extract the number\n    from the device ID which will be in \"/dev/audioN\" format.\n    */\n    if (pDeviceID == NULL) {\n        /* Default device. */\n        ma_strcpy_s(ctlid, sizeof(ctlid), \"/dev/audioctl\");\n    } else {\n        /* Specific device. We need to convert from \"/dev/audioN\" to \"/dev/audioctlN\". */\n        result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, \"/dev/audio\", &deviceIndex);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        ma_construct_device_id__audio4(ctlid, sizeof(ctlid), \"/dev/audioctl\", deviceIndex);\n    }\n\n    fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);\n    if (fd == -1) {\n        return MA_NO_DEVICE;\n    }\n\n    if (deviceIndex == -1) {\n        ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), \"/dev/audio\");\n    } else {\n        ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), \"/dev/audio\", deviceIndex);\n    }\n\n    result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);\n\n    close(fd);\n    return result;\n}\n\nstatic ma_result ma_device_uninit__audio4(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        close(pDevice->audio4.fdCapture);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        close(pDevice->audio4.fdPlayback);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)\n{\n    const char* pDefaultDeviceNames[] = {\n        \"/dev/audio\",\n        \"/dev/audio0\"\n    };\n    const char* pDefaultDeviceCtlNames[] = {\n        \"/dev/audioctl\",\n        \"/dev/audioctl0\"\n    };\n    int fd;\n    int fdFlags = 0;\n    size_t iDefaultDevice = (size_t)-1;\n    ma_format internalFormat;\n    ma_uint32 internalChannels;\n    ma_uint32 internalSampleRate;\n    ma_uint32 internalPeriodSizeInFrames;\n    ma_uint32 internalPeriods;\n\n    MA_ASSERT(pConfig    != NULL);\n    MA_ASSERT(deviceType != ma_device_type_duplex);\n    MA_ASSERT(pDevice    != NULL);\n\n    /* The first thing to do is open the file. */\n    if (deviceType == ma_device_type_capture) {\n        fdFlags = O_RDONLY;\n    } else {\n        fdFlags = O_WRONLY;\n    }\n    /*fdFlags |= O_NONBLOCK;*/\n\n    /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */\n    if (pDescriptor->pDeviceID == NULL) {\n        /* Default device. */\n        for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) {\n            fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0);\n            if (fd != -1) {\n                break;\n            }\n        }\n    } else {\n        /* Specific device. */\n        fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0);\n\n        for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) {\n            if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) {\n                break;\n            }\n        }\n\n        if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) {\n            iDefaultDevice = (size_t)-1;\n        }\n    }\n\n    if (fd == -1) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] Failed to open device.\");\n        return ma_result_from_errno(errno);\n    }\n\n    #if !defined(MA_AUDIO4_USE_NEW_API)    /* Old API */\n    {\n        audio_info_t fdInfo;\n        int fdInfoResult = -1;\n\n        /*\n        The documentation is a little bit unclear to me as to how it handles formats. It says the\n        following:\n\n            Regardless of formats supported by underlying driver, the audio driver accepts the\n            following formats.\n\n        By then the next sentence says this:\n\n            `encoding` and `precision` are one of the values obtained by AUDIO_GETENC.\n\n        It sounds like a direct contradiction to me. I'm going to play this safe any only use the\n        best sample format returned by AUDIO_GETENC. If the requested format is supported we'll\n        use that, but otherwise we'll just use our standard format priorities to pick an\n        appropriate one.\n        */\n        AUDIO_INITINFO(&fdInfo);\n\n        /*\n        Get the default format from the audioctl file if we're asking for a default device. If we\n        retrieve it from /dev/audio it'll default to mono 8000Hz.\n        */\n        if (iDefaultDevice != (size_t)-1) {\n            /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */\n            int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0);\n            if (fdctl != -1) {\n                fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo);\n                close(fdctl);\n            }\n        }\n\n        if (fdInfoResult == -1) {\n            /* We still don't have the default device info so just retrieve it from the main audio device. */\n            if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {\n                close(fd);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] AUDIO_GETINFO failed.\");\n                return ma_result_from_errno(errno);\n            }\n        }\n\n        /* We get the driver to do as much of the data conversion as possible. */\n        if (deviceType == ma_device_type_capture) {\n            fdInfo.mode = AUMODE_RECORD;\n            ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision);\n\n            if (pDescriptor->channels != 0) {\n                fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12);    /* From the documentation: `channels` ranges from 1 to 12. */\n            }\n\n            if (pDescriptor->sampleRate != 0) {\n                fdInfo.record.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000);    /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */\n            }\n        } else {\n            fdInfo.mode = AUMODE_PLAY;\n            ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision);\n\n            if (pDescriptor->channels != 0) {\n                fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12);    /* From the documentation: `channels` ranges from 1 to 12. */\n            }\n\n            if (pDescriptor->sampleRate != 0) {\n                fdInfo.play.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000);    /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */\n            }\n        }\n\n        if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {\n            close(fd);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] Failed to set device format. AUDIO_SETINFO failed.\");\n            return ma_result_from_errno(errno);\n        }\n\n        if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {\n            close(fd);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] AUDIO_GETINFO failed.\");\n            return ma_result_from_errno(errno);\n        }\n\n        if (deviceType == ma_device_type_capture) {\n            internalFormat     = ma_format_from_prinfo__audio4(&fdInfo.record);\n            internalChannels   = fdInfo.record.channels;\n            internalSampleRate = fdInfo.record.sample_rate;\n        } else {\n            internalFormat     = ma_format_from_prinfo__audio4(&fdInfo.play);\n            internalChannels   = fdInfo.play.channels;\n            internalSampleRate = fdInfo.play.sample_rate;\n        }\n\n        if (internalFormat == ma_format_unknown) {\n            close(fd);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.\");\n            return MA_FORMAT_NOT_SUPPORTED;\n        }\n\n        /* Buffer. */\n        {\n            ma_uint32 internalPeriodSizeInBytes;\n\n            internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);\n\n            internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);\n            if (internalPeriodSizeInBytes < 16) {\n                internalPeriodSizeInBytes = 16;\n            }\n\n            internalPeriods = pDescriptor->periodCount;\n            if (internalPeriods < 2) {\n                internalPeriods = 2;\n            }\n\n            /* What miniaudio calls a period, audio4 calls a block. */\n            AUDIO_INITINFO(&fdInfo);\n            fdInfo.hiwat     = internalPeriods;\n            fdInfo.lowat     = internalPeriods-1;\n            fdInfo.blocksize = internalPeriodSizeInBytes;\n            if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {\n                close(fd);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.\");\n                return ma_result_from_errno(errno);\n            }\n\n            internalPeriods            = fdInfo.hiwat;\n            internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels);\n        }\n    }\n    #else\n    {\n        struct audio_swpar fdPar;\n\n        /* We need to retrieve the format of the device so we can know the channel count and sample rate. Then we can calculate the buffer size. */\n        if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {\n            close(fd);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] Failed to retrieve initial device parameters.\");\n            return ma_result_from_errno(errno);\n        }\n\n        internalFormat     = ma_format_from_swpar__audio4(&fdPar);\n        internalChannels   = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;\n        internalSampleRate = fdPar.rate;\n\n        if (internalFormat == ma_format_unknown) {\n            close(fd);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.\");\n            return MA_FORMAT_NOT_SUPPORTED;\n        }\n\n        /* Buffer. */\n        {\n            ma_uint32 internalPeriodSizeInBytes;\n\n            internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);\n\n            /* What miniaudio calls a period, audio4 calls a block. */\n            internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);\n            if (internalPeriodSizeInBytes < 16) {\n                internalPeriodSizeInBytes = 16;\n            }\n\n            fdPar.nblks = pDescriptor->periodCount;\n            fdPar.round = internalPeriodSizeInBytes;\n\n            if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {\n                close(fd);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] Failed to set device parameters.\");\n                return ma_result_from_errno(errno);\n            }\n\n            if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {\n                close(fd);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] Failed to retrieve actual device parameters.\");\n                return ma_result_from_errno(errno);\n            }\n        }\n\n        internalFormat             = ma_format_from_swpar__audio4(&fdPar);\n        internalChannels           = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;\n        internalSampleRate         = fdPar.rate;\n        internalPeriods            = fdPar.nblks;\n        internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels);\n    }\n    #endif\n\n    if (internalFormat == ma_format_unknown) {\n        close(fd);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.\");\n        return MA_FORMAT_NOT_SUPPORTED;\n    }\n\n    if (deviceType == ma_device_type_capture) {\n        pDevice->audio4.fdCapture  = fd;\n    } else {\n        pDevice->audio4.fdPlayback = fd;\n    }\n\n    pDescriptor->format             = internalFormat;\n    pDescriptor->channels           = internalChannels;\n    pDescriptor->sampleRate         = internalSampleRate;\n    ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);\n    pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;\n    pDescriptor->periodCount        = internalPeriods;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    MA_ZERO_OBJECT(&pDevice->audio4);\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    pDevice->audio4.fdCapture  = -1;\n    pDevice->audio4.fdPlayback = -1;\n\n    /*\n    The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD\n    introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as\n    I'm aware.\n    */\n#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000\n    /* NetBSD 8.0+ */\n    if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||\n        ((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode  == ma_share_mode_exclusive)) {\n        return MA_SHARE_MODE_NOT_SUPPORTED;\n    }\n#else\n    /* All other flavors. */\n#endif\n\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);\n        if (result != MA_SUCCESS) {\n            if (pConfig->deviceType == ma_device_type_duplex) {\n                close(pDevice->audio4.fdCapture);\n            }\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_start__audio4(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        if (pDevice->audio4.fdCapture == -1) {\n            return MA_INVALID_ARGS;\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        if (pDevice->audio4.fdPlayback == -1) {\n            return MA_INVALID_ARGS;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)\n{\n    if (fd == -1) {\n        return MA_INVALID_ARGS;\n    }\n\n#if !defined(MA_AUDIO4_USE_NEW_API)\n    if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] Failed to stop device. AUDIO_FLUSH failed.\");\n        return ma_result_from_errno(errno);\n    }\n#else\n    if (ioctl(fd, AUDIO_STOP, 0) < 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] Failed to stop device. AUDIO_STOP failed.\");\n        return ma_result_from_errno(errno);\n    }\n#endif\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__audio4(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ma_result result;\n\n        result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ma_result result;\n\n        /* Drain the device first. If this fails we'll just need to flush without draining. Unfortunately draining isn't available on newer version of OpenBSD. */\n    #if !defined(MA_AUDIO4_USE_NEW_API)\n        ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0);\n    #endif\n\n        /* Here is where the device is stopped immediately. */\n        result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)\n{\n    int result;\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = 0;\n    }\n\n    result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));\n    if (result < 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] Failed to write data to the device.\");\n        return ma_result_from_errno(errno);\n    }\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)\n{\n    int result;\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));\n    if (result < 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[audio4] Failed to read data from the device.\");\n        return ma_result_from_errno(errno);\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_uninit__audio4(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_audio4);\n\n    (void)pContext;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n    MA_ASSERT(pContext != NULL);\n\n    (void)pConfig;\n\n    pCallbacks->onContextInit             = ma_context_init__audio4;\n    pCallbacks->onContextUninit           = ma_context_uninit__audio4;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__audio4;\n    pCallbacks->onDeviceInit              = ma_device_init__audio4;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__audio4;\n    pCallbacks->onDeviceStart             = ma_device_start__audio4;\n    pCallbacks->onDeviceStop              = ma_device_stop__audio4;\n    pCallbacks->onDeviceRead              = ma_device_read__audio4;\n    pCallbacks->onDeviceWrite             = ma_device_write__audio4;\n    pCallbacks->onDeviceDataLoop          = NULL;\n\n    return MA_SUCCESS;\n}\n#endif  /* audio4 */\n\n\n/******************************************************************************\n\nOSS Backend\n\n******************************************************************************/\n#ifdef MA_HAS_OSS\n#include <sys/ioctl.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/soundcard.h>\n\n#ifndef SNDCTL_DSP_HALT\n#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET\n#endif\n\n#define MA_OSS_DEFAULT_DEVICE_NAME  \"/dev/dsp\"\n\nstatic int ma_open_temp_device__oss()\n{\n    /* The OSS sample code uses \"/dev/mixer\" as the device for getting system properties so I'm going to do the same. */\n    int fd = open(\"/dev/mixer\", O_RDONLY, 0);\n    if (fd >= 0) {\n        return fd;\n    }\n\n    return -1;\n}\n\nstatic ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd)\n{\n    const char* deviceName;\n    int flags;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pfd != NULL);\n    (void)pContext;\n\n    *pfd = -1;\n\n    /* This function should only be called for playback or capture, not duplex. */\n    if (deviceType == ma_device_type_duplex) {\n        return MA_INVALID_ARGS;\n    }\n\n    deviceName = MA_OSS_DEFAULT_DEVICE_NAME;\n    if (pDeviceID != NULL) {\n        deviceName = pDeviceID->oss;\n    }\n\n    flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;\n    if (shareMode == ma_share_mode_exclusive) {\n        flags |= O_EXCL;\n    }\n\n    *pfd = open(deviceName, flags, 0);\n    if (*pfd == -1) {\n        return ma_result_from_errno(errno);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    int fd;\n    oss_sysinfo si;\n    int result;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    fd = ma_open_temp_device__oss();\n    if (fd == -1) {\n        ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.\");\n        return MA_NO_BACKEND;\n    }\n\n    result = ioctl(fd, SNDCTL_SYSINFO, &si);\n    if (result != -1) {\n        int iAudioDevice;\n        for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {\n            oss_audioinfo ai;\n            ai.dev = iAudioDevice;\n            result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);\n            if (result != -1) {\n                if (ai.devnode[0] != '\\0') {    /* <-- Can be blank, according to documentation. */\n                    ma_device_info deviceInfo;\n                    ma_bool32 isTerminating = MA_FALSE;\n\n                    MA_ZERO_OBJECT(&deviceInfo);\n\n                    /* ID */\n                    ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);\n\n                    /*\n                    The human readable device name should be in the \"ai.handle\" variable, but it can\n                    sometimes be empty in which case we just fall back to \"ai.name\" which is less user\n                    friendly, but usually has a value.\n                    */\n                    if (ai.handle[0] != '\\0') {\n                        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);\n                    } else {\n                        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);\n                    }\n\n                    /* The device can be both playback and capture. */\n                    if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {\n                        isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n                    }\n                    if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {\n                        isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n                    }\n\n                    if (isTerminating) {\n                        break;\n                    }\n                }\n            }\n        }\n    } else {\n        close(fd);\n        ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to retrieve system information for device enumeration.\");\n        return MA_NO_BACKEND;\n    }\n\n    close(fd);\n    return MA_SUCCESS;\n}\n\nstatic void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo)\n{\n    unsigned int minChannels;\n    unsigned int maxChannels;\n    unsigned int iRate;\n\n    MA_ASSERT(pContext    != NULL);\n    MA_ASSERT(pAudioInfo  != NULL);\n    MA_ASSERT(pDeviceInfo != NULL);\n\n    /* If we support all channels we just report 0. */\n    minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);\n    maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);\n\n    /*\n    OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness,\n    which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which\n    case we'll need to use min_rate and max_rate and report only standard rates.\n    */\n    if (pAudioInfo->nrates > 0) {\n        for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) {\n            unsigned int rate = pAudioInfo->rates[iRate];\n\n            if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {\n                ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0);   /* Set the channel count to 0 to indicate that all channel counts are supported. */\n            } else {\n                unsigned int iChannel;\n                for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {\n                     ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0);\n                }\n            }\n        }\n    } else {\n        for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) {\n            ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate];\n\n            if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) {\n                if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {\n                    ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0);   /* Set the channel count to 0 to indicate that all channel counts are supported. */\n                } else {\n                    unsigned int iChannel;\n                    for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {\n                         ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0);\n                    }\n                }\n            }\n        }\n    }\n}\n\nstatic ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    ma_bool32 foundDevice;\n    int fdTemp;\n    oss_sysinfo si;\n    int result;\n\n    MA_ASSERT(pContext != NULL);\n\n    /* Handle the default device a little differently. */\n    if (pDeviceID == NULL) {\n        if (deviceType == ma_device_type_playback) {\n            ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n        } else {\n            ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n        }\n\n        return MA_SUCCESS;\n    }\n\n\n    /* If we get here it means we are _not_ using the default device. */\n    foundDevice = MA_FALSE;\n\n    fdTemp = ma_open_temp_device__oss();\n    if (fdTemp == -1) {\n        ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.\");\n        return MA_NO_BACKEND;\n    }\n\n    result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);\n    if (result != -1) {\n        int iAudioDevice;\n        for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {\n            oss_audioinfo ai;\n            ai.dev = iAudioDevice;\n            result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);\n            if (result != -1) {\n                if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {\n                    /* It has the same name, so now just confirm the type. */\n                    if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||\n                        (deviceType == ma_device_type_capture  && ((ai.caps & PCM_CAP_INPUT)  != 0))) {\n                        unsigned int formatMask;\n\n                        /* ID */\n                        ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);\n\n                        /*\n                        The human readable device name should be in the \"ai.handle\" variable, but it can\n                        sometimes be empty in which case we just fall back to \"ai.name\" which is less user\n                        friendly, but usually has a value.\n                        */\n                        if (ai.handle[0] != '\\0') {\n                            ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);\n                        } else {\n                            ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);\n                        }\n\n\n                        pDeviceInfo->nativeDataFormatCount = 0;\n\n                        if (deviceType == ma_device_type_playback) {\n                            formatMask = ai.oformats;\n                        } else {\n                            formatMask = ai.iformats;\n                        }\n\n                        if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {\n                            ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo);\n                        }\n                        if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {\n                            ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo);\n                        }\n                        if ((formatMask & AFMT_U8) != 0) {\n                            ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo);\n                        }\n\n                        foundDevice = MA_TRUE;\n                        break;\n                    }\n                }\n            }\n        }\n    } else {\n        close(fdTemp);\n        ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to retrieve system information for device enumeration.\");\n        return MA_NO_BACKEND;\n    }\n\n\n    close(fdTemp);\n\n    if (!foundDevice) {\n        return MA_NO_DEVICE;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_uninit__oss(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        close(pDevice->oss.fdCapture);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        close(pDevice->oss.fdPlayback);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic int ma_format_to_oss(ma_format format)\n{\n    int ossFormat = AFMT_U8;\n    switch (format) {\n        case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;\n        case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;\n        case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;\n        case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;\n        case ma_format_u8:\n        default: ossFormat = AFMT_U8; break;\n    }\n\n    return ossFormat;\n}\n\nstatic ma_format ma_format_from_oss(int ossFormat)\n{\n    if (ossFormat == AFMT_U8) {\n        return ma_format_u8;\n    } else {\n        if (ma_is_little_endian()) {\n            switch (ossFormat) {\n                case AFMT_S16_LE: return ma_format_s16;\n                case AFMT_S32_LE: return ma_format_s32;\n                default: return ma_format_unknown;\n            }\n        } else {\n            switch (ossFormat) {\n                case AFMT_S16_BE: return ma_format_s16;\n                case AFMT_S32_BE: return ma_format_s32;\n                default: return ma_format_unknown;\n            }\n        }\n    }\n\n    return ma_format_unknown;\n}\n\nstatic ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)\n{\n    ma_result result;\n    int ossResult;\n    int fd;\n    const ma_device_id* pDeviceID = NULL;\n    ma_share_mode shareMode;\n    int ossFormat;\n    int ossChannels;\n    int ossSampleRate;\n    int ossFragment;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(pConfig != NULL);\n    MA_ASSERT(deviceType != ma_device_type_duplex);\n\n    pDeviceID     = pDescriptor->pDeviceID;\n    shareMode     = pDescriptor->shareMode;\n    ossFormat     = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16); /* Use s16 by default because OSS doesn't like floating point. */\n    ossChannels   = (int)(pDescriptor->channels   > 0) ? pDescriptor->channels   : MA_DEFAULT_CHANNELS;\n    ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;\n\n    result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd);\n    if (result != MA_SUCCESS) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to open device.\");\n        return result;\n    }\n\n    /*\n    The OSS documantation is very clear about the order we should be initializing the device's properties:\n      1) Format\n      2) Channels\n      3) Sample rate.\n    */\n\n    /* Format. */\n    ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);\n    if (ossResult == -1) {\n        close(fd);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to set format.\");\n        return ma_result_from_errno(errno);\n    }\n\n    /* Channels. */\n    ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);\n    if (ossResult == -1) {\n        close(fd);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to set channel count.\");\n        return ma_result_from_errno(errno);\n    }\n\n    /* Sample Rate. */\n    ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);\n    if (ossResult == -1) {\n        close(fd);\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to set sample rate.\");\n        return ma_result_from_errno(errno);\n    }\n\n    /*\n    Buffer.\n\n    The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if\n    it should be done before or after format/channels/rate.\n\n    OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual\n    value.\n    */\n    {\n        ma_uint32 periodSizeInFrames;\n        ma_uint32 periodSizeInBytes;\n        ma_uint32 ossFragmentSizePower;\n\n        periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile);\n\n        periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));\n        if (periodSizeInBytes < 16) {\n            periodSizeInBytes = 16;\n        }\n\n        ossFragmentSizePower = 4;\n        periodSizeInBytes >>= 4;\n        while (periodSizeInBytes >>= 1) {\n            ossFragmentSizePower += 1;\n        }\n\n        ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);\n        ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);\n        if (ossResult == -1) {\n            close(fd);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to set fragment size and period count.\");\n            return ma_result_from_errno(errno);\n        }\n    }\n\n    /* Internal settings. */\n    if (deviceType == ma_device_type_capture) {\n        pDevice->oss.fdCapture  = fd;\n    } else {\n        pDevice->oss.fdPlayback = fd;\n    }\n\n    pDescriptor->format             = ma_format_from_oss(ossFormat);\n    pDescriptor->channels           = ossChannels;\n    pDescriptor->sampleRate         = ossSampleRate;\n    ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels);\n    pDescriptor->periodCount        = (ma_uint32)(ossFragment >> 16);\n    pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels);\n\n    if (pDescriptor->format == ma_format_unknown) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OSS] The device's internal format is not supported by miniaudio.\");\n        return MA_FORMAT_NOT_SUPPORTED;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    MA_ASSERT(pDevice  != NULL);\n    MA_ASSERT(pConfig  != NULL);\n\n    MA_ZERO_OBJECT(&pDevice->oss);\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);\n        if (result != MA_SUCCESS) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to open device.\");\n            return result;\n        }\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);\n        if (result != MA_SUCCESS) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to open device.\");\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n/*\nNote on Starting and Stopping\n=============================\nIn the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when\ntrying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will\nfail. Instead what we need to do is just not write or read to and from the device when the\ndevice is not running.\n\nAs a result, both the start and stop functions for OSS are just empty stubs. The starting and\nstopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check\nthe device state, and if the device is stopped they will simply not do any kind of processing.\n\nThe downside to this technique is that I've noticed a fairly lengthy delay in stopping the\ndevice, up to a second. This is on a virtual machine, and as such might just be due to the\nvirtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for\nthe moment that's just how it's going to have to be.\n\nWhen starting the device, OSS will automatically start it when write() or read() is called.\n*/\nstatic ma_result ma_device_start__oss(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    /* The device is automatically started with reading and writing. */\n    (void)pDevice;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__oss(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    /* See note above on why this is empty. */\n    (void)pDevice;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)\n{\n    int resultOSS;\n    ma_uint32 deviceState;\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = 0;\n    }\n\n    /* Don't do any processing if the device is stopped. */\n    deviceState = ma_device_get_state(pDevice);\n    if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {\n        return MA_SUCCESS;\n    }\n\n    resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));\n    if (resultOSS < 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to send data from the client to the device.\");\n        return ma_result_from_errno(errno);\n    }\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)\n{\n    int resultOSS;\n    ma_uint32 deviceState;\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    /* Don't do any processing if the device is stopped. */\n    deviceState = ma_device_get_state(pDevice);\n    if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {\n        return MA_SUCCESS;\n    }\n\n    resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));\n    if (resultOSS < 0) {\n        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to read data from the device to be sent to the client.\");\n        return ma_result_from_errno(errno);\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_uninit__oss(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_oss);\n\n    (void)pContext;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n    int fd;\n    int ossVersion;\n    int result;\n\n    MA_ASSERT(pContext != NULL);\n\n    (void)pConfig;\n\n    /* Try opening a temporary device first so we can get version information. This is closed at the end. */\n    fd = ma_open_temp_device__oss();\n    if (fd == -1) {\n        ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to open temporary device for retrieving system properties.\");   /* Looks liks OSS isn't installed, or there are no available devices. */\n        return MA_NO_BACKEND;\n    }\n\n    /* Grab the OSS version. */\n    ossVersion = 0;\n    result = ioctl(fd, OSS_GETVERSION, &ossVersion);\n    if (result == -1) {\n        close(fd);\n        ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, \"[OSS] Failed to retrieve OSS version.\");\n        return MA_NO_BACKEND;\n    }\n\n    /* The file handle to temp device is no longer needed. Close ASAP. */\n    close(fd);\n\n    pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);\n    pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);\n\n    pCallbacks->onContextInit             = ma_context_init__oss;\n    pCallbacks->onContextUninit           = ma_context_uninit__oss;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__oss;\n    pCallbacks->onDeviceInit              = ma_device_init__oss;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__oss;\n    pCallbacks->onDeviceStart             = ma_device_start__oss;\n    pCallbacks->onDeviceStop              = ma_device_stop__oss;\n    pCallbacks->onDeviceRead              = ma_device_read__oss;\n    pCallbacks->onDeviceWrite             = ma_device_write__oss;\n    pCallbacks->onDeviceDataLoop          = NULL;\n\n    return MA_SUCCESS;\n}\n#endif  /* OSS */\n\n\n\n\n\n/******************************************************************************\n\nAAudio Backend\n\n******************************************************************************/\n#ifdef MA_HAS_AAUDIO\n\n/*#include <AAudio/AAudio.h>*/\n\ntypedef int32_t                                         ma_aaudio_result_t;\ntypedef int32_t                                         ma_aaudio_direction_t;\ntypedef int32_t                                         ma_aaudio_sharing_mode_t;\ntypedef int32_t                                         ma_aaudio_format_t;\ntypedef int32_t                                         ma_aaudio_stream_state_t;\ntypedef int32_t                                         ma_aaudio_performance_mode_t;\ntypedef int32_t                                         ma_aaudio_usage_t;\ntypedef int32_t                                         ma_aaudio_content_type_t;\ntypedef int32_t                                         ma_aaudio_input_preset_t;\ntypedef int32_t                                         ma_aaudio_allowed_capture_policy_t;\ntypedef int32_t                                         ma_aaudio_data_callback_result_t;\ntypedef struct ma_AAudioStreamBuilder_t*                ma_AAudioStreamBuilder;\ntypedef struct ma_AAudioStream_t*                       ma_AAudioStream;\n\n#define MA_AAUDIO_UNSPECIFIED                           0\n\n/* Result codes. miniaudio only cares about the success code. */\n#define MA_AAUDIO_OK                                    0\n\n/* Directions. */\n#define MA_AAUDIO_DIRECTION_OUTPUT                      0\n#define MA_AAUDIO_DIRECTION_INPUT                       1\n\n/* Sharing modes. */\n#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE                0\n#define MA_AAUDIO_SHARING_MODE_SHARED                   1\n\n/* Formats. */\n#define MA_AAUDIO_FORMAT_PCM_I16                        1\n#define MA_AAUDIO_FORMAT_PCM_FLOAT                      2\n\n/* Stream states. */\n#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED            0\n#define MA_AAUDIO_STREAM_STATE_UNKNOWN                  1\n#define MA_AAUDIO_STREAM_STATE_OPEN                     2\n#define MA_AAUDIO_STREAM_STATE_STARTING                 3\n#define MA_AAUDIO_STREAM_STATE_STARTED                  4\n#define MA_AAUDIO_STREAM_STATE_PAUSING                  5\n#define MA_AAUDIO_STREAM_STATE_PAUSED                   6\n#define MA_AAUDIO_STREAM_STATE_FLUSHING                 7\n#define MA_AAUDIO_STREAM_STATE_FLUSHED                  8\n#define MA_AAUDIO_STREAM_STATE_STOPPING                 9\n#define MA_AAUDIO_STREAM_STATE_STOPPED                  10\n#define MA_AAUDIO_STREAM_STATE_CLOSING                  11\n#define MA_AAUDIO_STREAM_STATE_CLOSED                   12\n#define MA_AAUDIO_STREAM_STATE_DISCONNECTED             13\n\n/* Performance modes. */\n#define MA_AAUDIO_PERFORMANCE_MODE_NONE                 10\n#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING         11\n#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY          12\n\n/* Usage types. */\n#define MA_AAUDIO_USAGE_MEDIA                           1\n#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION             2\n#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING  3\n#define MA_AAUDIO_USAGE_ALARM                           4\n#define MA_AAUDIO_USAGE_NOTIFICATION                    5\n#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE           6\n#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT              10\n#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY        11\n#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE  12\n#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION         13\n#define MA_AAUDIO_USAGE_GAME                            14\n#define MA_AAUDIO_USAGE_ASSISTANT                       16\n#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY                1000\n#define MA_AAUDIO_SYSTEM_USAGE_SAFETY                   1001\n#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS           1002\n#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT             1003\n\n/* Content types. */\n#define MA_AAUDIO_CONTENT_TYPE_SPEECH                   1\n#define MA_AAUDIO_CONTENT_TYPE_MUSIC                    2\n#define MA_AAUDIO_CONTENT_TYPE_MOVIE                    3\n#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION             4\n\n/* Input presets. */\n#define MA_AAUDIO_INPUT_PRESET_GENERIC                  1\n#define MA_AAUDIO_INPUT_PRESET_CAMCORDER                5\n#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION        6\n#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION      7\n#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED              9\n#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE        10\n\n/* Allowed Capture Policies */\n#define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL                  1\n#define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM               2\n#define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE                 3\n\n/* Callback results. */\n#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE              0\n#define MA_AAUDIO_CALLBACK_RESULT_STOP                  1\n\n\ntypedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);\ntypedef void                             (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);\n\ntypedef ma_aaudio_result_t       (* MA_PFN_AAudio_createStreamBuilder)                   (ma_AAudioStreamBuilder** ppBuilder);\ntypedef ma_aaudio_result_t       (* MA_PFN_AAudioStreamBuilder_delete)                   (ma_AAudioStreamBuilder* pBuilder);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setDeviceId)              (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setDirection)             (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setSharingMode)           (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setFormat)                (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setChannelCount)          (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setSampleRate)            (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setDataCallback)          (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setErrorCallback)         (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setPerformanceMode)       (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setUsage)                 (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setContentType)           (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setInputPreset)           (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset);\ntypedef void                     (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)  (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy);\ntypedef ma_aaudio_result_t       (* MA_PFN_AAudioStreamBuilder_openStream)               (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);\ntypedef ma_aaudio_result_t       (* MA_PFN_AAudioStream_close)                           (ma_AAudioStream* pStream);\ntypedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState)                        (ma_AAudioStream* pStream);\ntypedef ma_aaudio_result_t       (* MA_PFN_AAudioStream_waitForStateChange)              (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds);\ntypedef ma_aaudio_format_t       (* MA_PFN_AAudioStream_getFormat)                       (ma_AAudioStream* pStream);\ntypedef int32_t                  (* MA_PFN_AAudioStream_getChannelCount)                 (ma_AAudioStream* pStream);\ntypedef int32_t                  (* MA_PFN_AAudioStream_getSampleRate)                   (ma_AAudioStream* pStream);\ntypedef int32_t                  (* MA_PFN_AAudioStream_getBufferCapacityInFrames)       (ma_AAudioStream* pStream);\ntypedef int32_t                  (* MA_PFN_AAudioStream_getFramesPerDataCallback)        (ma_AAudioStream* pStream);\ntypedef int32_t                  (* MA_PFN_AAudioStream_getFramesPerBurst)               (ma_AAudioStream* pStream);\ntypedef ma_aaudio_result_t       (* MA_PFN_AAudioStream_requestStart)                    (ma_AAudioStream* pStream);\ntypedef ma_aaudio_result_t       (* MA_PFN_AAudioStream_requestStop)                     (ma_AAudioStream* pStream);\n\nstatic ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)\n{\n    switch (resultAA)\n    {\n        case MA_AAUDIO_OK: return MA_SUCCESS;\n        default: break;\n    }\n\n    return MA_ERROR;\n}\n\nstatic ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage)\n{\n    switch (usage) {\n        case ma_aaudio_usage_media:                          return MA_AAUDIO_USAGE_MEDIA;\n        case ma_aaudio_usage_voice_communication:            return MA_AAUDIO_USAGE_VOICE_COMMUNICATION;\n        case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;\n        case ma_aaudio_usage_alarm:                          return MA_AAUDIO_USAGE_ALARM;\n        case ma_aaudio_usage_notification:                   return MA_AAUDIO_USAGE_NOTIFICATION;\n        case ma_aaudio_usage_notification_ringtone:          return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE;\n        case ma_aaudio_usage_notification_event:             return MA_AAUDIO_USAGE_NOTIFICATION_EVENT;\n        case ma_aaudio_usage_assistance_accessibility:       return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;\n        case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;\n        case ma_aaudio_usage_assistance_sonification:        return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION;\n        case ma_aaudio_usage_game:                           return MA_AAUDIO_USAGE_GAME;\n        case ma_aaudio_usage_assitant:                       return MA_AAUDIO_USAGE_ASSISTANT;\n        case ma_aaudio_usage_emergency:                      return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY;\n        case ma_aaudio_usage_safety:                         return MA_AAUDIO_SYSTEM_USAGE_SAFETY;\n        case ma_aaudio_usage_vehicle_status:                 return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS;\n        case ma_aaudio_usage_announcement:                   return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT;\n        default: break;\n    }\n\n    return MA_AAUDIO_USAGE_MEDIA;\n}\n\nstatic ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType)\n{\n    switch (contentType) {\n        case ma_aaudio_content_type_speech:       return MA_AAUDIO_CONTENT_TYPE_SPEECH;\n        case ma_aaudio_content_type_music:        return MA_AAUDIO_CONTENT_TYPE_MUSIC;\n        case ma_aaudio_content_type_movie:        return MA_AAUDIO_CONTENT_TYPE_MOVIE;\n        case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION;\n        default: break;\n    }\n\n    return MA_AAUDIO_CONTENT_TYPE_SPEECH;\n}\n\nstatic ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset)\n{\n    switch (inputPreset) {\n        case ma_aaudio_input_preset_generic:             return MA_AAUDIO_INPUT_PRESET_GENERIC;\n        case ma_aaudio_input_preset_camcorder:           return MA_AAUDIO_INPUT_PRESET_CAMCORDER;\n        case ma_aaudio_input_preset_voice_recognition:   return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;\n        case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION;\n        case ma_aaudio_input_preset_unprocessed:         return MA_AAUDIO_INPUT_PRESET_UNPROCESSED;\n        case ma_aaudio_input_preset_voice_performance:   return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE;\n        default: break;\n    }\n\n    return MA_AAUDIO_INPUT_PRESET_GENERIC;\n}\n\nstatic ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy)\n{\n    switch (allowedCapturePolicy) {\n        case ma_aaudio_allow_capture_by_all:    return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL;\n        case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM;\n        case ma_aaudio_allow_capture_by_none:   return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE;\n        default: break;\n    }\n\n    return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL;\n}\n\nstatic void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)\n{\n    ma_result result;\n    ma_job job;\n    ma_device* pDevice = (ma_device*)pUserData;\n    MA_ASSERT(pDevice != NULL);\n\n    (void)error;\n\n    ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\\n\", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream));\n\n    /*\n    When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation,\n    we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this\n    cleanly and safely.\n    */\n    job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE);\n    job.data.device.aaudio.reroute.pDevice = pDevice;\n\n    if (pStream == pDevice->aaudio.pStreamCapture) {\n        job.data.device.aaudio.reroute.deviceType = ma_device_type_capture;\n    }\n    else {\n        job.data.device.aaudio.reroute.deviceType = ma_device_type_playback;\n    }\n\n    result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job);\n    if (result != MA_SUCCESS) {\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[AAudio] Device Disconnected. Failed to post job for rerouting.\\n\");\n        return;\n    }\n}\n\nstatic ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    MA_ASSERT(pDevice != NULL);\n\n    ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount);\n\n    (void)pStream;\n    return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;\n}\n\nstatic ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    MA_ASSERT(pDevice != NULL);\n\n    ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount);\n\n    (void)pStream;\n    return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;\n}\n\nstatic ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder)\n{\n    ma_AAudioStreamBuilder* pBuilder;\n    ma_aaudio_result_t resultAA;\n\n    /* Safety. */\n    *ppBuilder = NULL;\n\n    resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);\n    if (resultAA != MA_AAUDIO_OK) {\n        return ma_result_from_aaudio(resultAA);\n    }\n\n    if (pDeviceID != NULL) {\n        ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);\n    }\n\n    ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);\n    ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);\n\n\n    /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */\n    if (pDescriptor != NULL) {\n        MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */\n\n        if (pDescriptor->sampleRate != 0) {\n            ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate);\n        }\n\n        if (deviceType == ma_device_type_capture) {\n            if (pDescriptor->channels != 0) {\n                ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);\n            }\n            if (pDescriptor->format != ma_format_unknown) {\n                ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);\n            }\n        } else {\n            if (pDescriptor->channels != 0) {\n                ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);\n            }\n            if (pDescriptor->format != ma_format_unknown) {\n                ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);\n            }\n        }\n\n\n        /*\n        There have been reports where setting the frames per data callback results in an error\n        later on from Android. To address this, I'm experimenting with simply not setting it on\n        anything from Android 11 and earlier. Suggestions welcome on how we might be able to make\n        this more targetted.\n        */\n        if (!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) {\n            /*\n            AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you\n            retrieve the actual sample rate until after you've opened the stream. But you need to configure\n            the buffer capacity before you open the stream... :/\n\n            To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on.\n            */\n            ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount;\n\n            ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);\n            ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount);\n        }\n\n        if (deviceType == ma_device_type_capture) {\n            if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) {\n                ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset));\n            }\n\n            ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);\n        } else {\n            if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) {\n                ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage));\n            }\n\n            if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) {\n                ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType));\n            }\n\n            if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) {\n                ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy));\n            }\n\n            ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);\n        }\n\n        /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */\n        ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE);\n\n        /* We need to set an error callback to detect device changes. */\n        if (pDevice != NULL) {  /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */\n            ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);\n        }\n    }\n\n    *ppBuilder = pBuilder;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream)\n{\n    ma_result result;\n\n    result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream));\n    ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);\n\n    return result;\n}\n\nstatic ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream)\n{\n    ma_result result;\n    ma_AAudioStreamBuilder* pBuilder;\n\n    *ppStream = NULL;\n\n    result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream);\n}\n\nstatic ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream)\n{\n    ma_result result;\n    ma_AAudioStreamBuilder* pBuilder;\n\n    MA_ASSERT(pDevice != NULL);\n    MA_ASSERT(pDescriptor != NULL);\n    MA_ASSERT(deviceType != ma_device_type_duplex);   /* This function should not be called for a full-duplex device type. */\n\n    *ppStream = NULL;\n\n    result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream);\n}\n\nstatic ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)\n{\n    return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));\n}\n\nstatic ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)\n{\n    /* The only way to know this is to try creating a stream. */\n    ma_AAudioStream* pStream;\n    ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream);\n    if (result != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n\n    ma_close_stream__aaudio(pContext, pStream);\n    return MA_TRUE;\n}\n\nstatic ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState)\n{\n    ma_aaudio_stream_state_t actualNewState;\n    ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */\n    if (resultAA != MA_AAUDIO_OK) {\n        return ma_result_from_aaudio(resultAA);\n    }\n\n    if (newState != actualNewState) {\n        return MA_ERROR;   /* Failed to transition into the expected state. */\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    ma_bool32 cbResult = MA_TRUE;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */\n\n    /* Playback. */\n    if (cbResult) {\n        ma_device_info deviceInfo;\n        MA_ZERO_OBJECT(&deviceInfo);\n        deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;\n        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n\n        if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {\n            cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n        }\n    }\n\n    /* Capture. */\n    if (cbResult) {\n        ma_device_info deviceInfo;\n        MA_ZERO_OBJECT(&deviceInfo);\n        deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;\n        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n\n        if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {\n            cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo)\n{\n    MA_ASSERT(pContext    != NULL);\n    MA_ASSERT(pStream     != NULL);\n    MA_ASSERT(pDeviceInfo != NULL);\n\n    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format     = format;\n    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels   = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);\n    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);\n    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags      = flags;\n    pDeviceInfo->nativeDataFormatCount += 1;\n}\n\nstatic void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo)\n{\n    /* AAudio supports s16 and f32. */\n    ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo);\n    ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo);\n}\n\nstatic ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    ma_AAudioStream* pStream;\n    ma_result result;\n\n    MA_ASSERT(pContext != NULL);\n\n    /* ID */\n    if (pDeviceID != NULL) {\n        pDeviceInfo->id.aaudio = pDeviceID->aaudio;\n    } else {\n        pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;\n    }\n\n    /* Name */\n    if (deviceType == ma_device_type_playback) {\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n    } else {\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n    }\n\n\n    pDeviceInfo->nativeDataFormatCount = 0;\n\n    /* We'll need to open the device to get accurate sample rate and channel count information. */\n    result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo);\n\n    ma_close_stream__aaudio(pContext, pStream);\n    pStream = NULL;\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_device_uninit__aaudio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);\n        pDevice->aaudio.pStreamCapture = NULL;\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);\n        pDevice->aaudio.pStreamPlayback = NULL;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream)\n{\n    ma_result result;\n    int32_t bufferCapacityInFrames;\n    int32_t framesPerDataCallback;\n    ma_AAudioStream* pStream;\n\n    MA_ASSERT(pDevice     != NULL);\n    MA_ASSERT(pConfig     != NULL);\n    MA_ASSERT(pDescriptor != NULL);\n\n    *ppStream = NULL;   /* Safety. */\n\n    /* First step is to open the stream. From there we'll be able to extract the internal configuration. */\n    result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to open the AAudio stream. */\n    }\n\n    /* Now extract the internal configuration. */\n    pDescriptor->format     = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;\n    pDescriptor->channels   = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream);\n    pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream);\n\n    /* For the channel map we need to be sure we don't overflow any buffers. */\n    if (pDescriptor->channels <= MA_MAX_CHANNELS) {\n        ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); /* <-- Cannot find info on channel order, so assuming a default. */\n    } else {\n        ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */\n    }\n\n    bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream);\n    framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream);\n\n    if (framesPerDataCallback > 0) {\n        pDescriptor->periodSizeInFrames = framesPerDataCallback;\n        pDescriptor->periodCount        = bufferCapacityInFrames / framesPerDataCallback;\n    } else {\n        pDescriptor->periodSizeInFrames = bufferCapacityInFrames;\n        pDescriptor->periodCount        = 1;\n    }\n\n    *ppStream = pStream;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    ma_result result;\n\n    MA_ASSERT(pDevice != NULL);\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    pDevice->aaudio.usage                   = pConfig->aaudio.usage;\n    pDevice->aaudio.contentType             = pConfig->aaudio.contentType;\n    pDevice->aaudio.inputPreset             = pConfig->aaudio.inputPreset;\n    pDevice->aaudio.allowedCapturePolicy    = pConfig->aaudio.allowedCapturePolicy;\n    pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute;\n\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)\n{\n    ma_aaudio_result_t resultAA;\n    ma_aaudio_stream_state_t currentState;\n\n    MA_ASSERT(pDevice != NULL);\n\n    resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);\n    if (resultAA != MA_AAUDIO_OK) {\n        return ma_result_from_aaudio(resultAA);\n    }\n\n    /* Do we actually need to wait for the device to transition into it's started state? */\n\n    /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */\n    currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);\n    if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {\n        ma_result result;\n\n        if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {\n            return MA_ERROR;   /* Expecting the stream to be a starting or started state. */\n        }\n\n        result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)\n{\n    ma_aaudio_result_t resultAA;\n    ma_aaudio_stream_state_t currentState;\n\n    MA_ASSERT(pDevice != NULL);\n\n    /*\n    From the AAudio documentation:\n\n        The stream will stop after all of the data currently buffered has been played.\n\n    This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic.\n    */\n    currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);\n    if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {\n        return MA_SUCCESS;  /* The device is disconnected. Don't try stopping it. */\n    }\n\n    resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);\n    if (resultAA != MA_AAUDIO_OK) {\n        return ma_result_from_aaudio(resultAA);\n    }\n\n    /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */\n    currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);\n    if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {\n        ma_result result;\n\n        if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {\n            return MA_ERROR;   /* Expecting the stream to be a stopping or stopped state. */\n        }\n\n        result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_start__aaudio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);\n        if (result != MA_SUCCESS) {\n            if (pDevice->type == ma_device_type_duplex) {\n                ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);\n            }\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__aaudio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    ma_device__on_notification_stopped(pDevice);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType)\n{\n    ma_result result;\n\n    MA_ASSERT(pDevice != NULL);\n\n    /* The first thing to do is close the streams. */\n    if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {\n        ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);\n        pDevice->aaudio.pStreamCapture = NULL;\n    }\n\n    if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {\n        ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);\n        pDevice->aaudio.pStreamPlayback = NULL;\n    }\n\n    /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */\n    {\n        ma_device_config deviceConfig;\n        ma_device_descriptor descriptorPlayback;\n        ma_device_descriptor descriptorCapture;\n\n        deviceConfig = ma_device_config_init(deviceType);\n        deviceConfig.playback.pDeviceID             = NULL; /* Only doing rerouting with default devices. */\n        deviceConfig.playback.shareMode             = pDevice->playback.shareMode;\n        deviceConfig.playback.format                = pDevice->playback.format;\n        deviceConfig.playback.channels              = pDevice->playback.channels;\n        deviceConfig.capture.pDeviceID              = NULL; /* Only doing rerouting with default devices. */\n        deviceConfig.capture.shareMode              = pDevice->capture.shareMode;\n        deviceConfig.capture.format                 = pDevice->capture.format;\n        deviceConfig.capture.channels               = pDevice->capture.channels;\n        deviceConfig.sampleRate                     = pDevice->sampleRate;\n        deviceConfig.aaudio.usage                   = pDevice->aaudio.usage;\n        deviceConfig.aaudio.contentType             = pDevice->aaudio.contentType;\n        deviceConfig.aaudio.inputPreset             = pDevice->aaudio.inputPreset;\n        deviceConfig.aaudio.allowedCapturePolicy    = pDevice->aaudio.allowedCapturePolicy;\n        deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute;\n        deviceConfig.periods                        = 1;\n\n        /* Try to get an accurate period size. */\n        if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {\n            deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;\n        } else {\n            deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;\n        }\n\n        if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {\n            descriptorCapture.pDeviceID           = deviceConfig.capture.pDeviceID;\n            descriptorCapture.shareMode           = deviceConfig.capture.shareMode;\n            descriptorCapture.format              = deviceConfig.capture.format;\n            descriptorCapture.channels            = deviceConfig.capture.channels;\n            descriptorCapture.sampleRate          = deviceConfig.sampleRate;\n            descriptorCapture.periodSizeInFrames  = deviceConfig.periodSizeInFrames;\n            descriptorCapture.periodCount         = deviceConfig.periods;\n        }\n\n        if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {\n            descriptorPlayback.pDeviceID          = deviceConfig.playback.pDeviceID;\n            descriptorPlayback.shareMode          = deviceConfig.playback.shareMode;\n            descriptorPlayback.format             = deviceConfig.playback.format;\n            descriptorPlayback.channels           = deviceConfig.playback.channels;\n            descriptorPlayback.sampleRate         = deviceConfig.sampleRate;\n            descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames;\n            descriptorPlayback.periodCount        = deviceConfig.periods;\n        }\n\n        result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture);\n        if (result != MA_SUCCESS) {\n            ma_device_uninit__aaudio(pDevice);\n            return result;\n        }\n\n        /* We'll only ever do this in response to a reroute. */\n        ma_device__on_notification_rerouted(pDevice);\n\n        /* If the device is started, start the streams. Maybe make this configurable? */\n        if (ma_device_get_state(pDevice) == ma_device_state_started) {\n            if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) {\n                ma_device_start__aaudio(pDevice);\n            } else {\n                ma_device_stop(pDevice);    /* Do a full device stop so we set internal state correctly. */\n            }\n        }\n\n        return MA_SUCCESS;\n    }\n}\n\nstatic ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)\n{\n    ma_AAudioStream* pStream = NULL;\n\n    MA_ASSERT(pDevice     != NULL);\n    MA_ASSERT(type        != ma_device_type_duplex);\n    MA_ASSERT(pDeviceInfo != NULL);\n\n    if (type == ma_device_type_playback) {\n        pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture;\n        pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio;\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);     /* Only supporting default devices. */\n    }\n    if (type == ma_device_type_capture) {\n        pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback;\n        pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio;\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);    /* Only supporting default devices. */\n    }\n\n    /* Safety. Should never happen. */\n    if (pStream == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    pDeviceInfo->nativeDataFormatCount = 0;\n    ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo);\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_context_uninit__aaudio(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_aaudio);\n\n    ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks);\n\n    ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio);\n    pContext->aaudio.hAAudio = NULL;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n    size_t i;\n    const char* libNames[] = {\n        \"libaaudio.so\"\n    };\n\n    for (i = 0; i < ma_countof(libNames); ++i) {\n        pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]);\n        if (pContext->aaudio.hAAudio != NULL) {\n            break;\n        }\n    }\n\n    if (pContext->aaudio.hAAudio == NULL) {\n        return MA_FAILED_TO_INIT_BACKEND;\n    }\n\n    pContext->aaudio.AAudio_createStreamBuilder                    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudio_createStreamBuilder\");\n    pContext->aaudio.AAudioStreamBuilder_delete                    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_delete\");\n    pContext->aaudio.AAudioStreamBuilder_setDeviceId               = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setDeviceId\");\n    pContext->aaudio.AAudioStreamBuilder_setDirection              = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setDirection\");\n    pContext->aaudio.AAudioStreamBuilder_setSharingMode            = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setSharingMode\");\n    pContext->aaudio.AAudioStreamBuilder_setFormat                 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setFormat\");\n    pContext->aaudio.AAudioStreamBuilder_setChannelCount           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setChannelCount\");\n    pContext->aaudio.AAudioStreamBuilder_setSampleRate             = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setSampleRate\");\n    pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setBufferCapacityInFrames\");\n    pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setFramesPerDataCallback\");\n    pContext->aaudio.AAudioStreamBuilder_setDataCallback           = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setDataCallback\");\n    pContext->aaudio.AAudioStreamBuilder_setErrorCallback          = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setErrorCallback\");\n    pContext->aaudio.AAudioStreamBuilder_setPerformanceMode        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setPerformanceMode\");\n    pContext->aaudio.AAudioStreamBuilder_setUsage                  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setUsage\");\n    pContext->aaudio.AAudioStreamBuilder_setContentType            = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setContentType\");\n    pContext->aaudio.AAudioStreamBuilder_setInputPreset            = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setInputPreset\");\n    pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_setAllowedCapturePolicy\");\n    pContext->aaudio.AAudioStreamBuilder_openStream                = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStreamBuilder_openStream\");\n    pContext->aaudio.AAudioStream_close                            = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStream_close\");\n    pContext->aaudio.AAudioStream_getState                         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStream_getState\");\n    pContext->aaudio.AAudioStream_waitForStateChange               = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStream_waitForStateChange\");\n    pContext->aaudio.AAudioStream_getFormat                        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStream_getFormat\");\n    pContext->aaudio.AAudioStream_getChannelCount                  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStream_getChannelCount\");\n    pContext->aaudio.AAudioStream_getSampleRate                    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStream_getSampleRate\");\n    pContext->aaudio.AAudioStream_getBufferCapacityInFrames        = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStream_getBufferCapacityInFrames\");\n    pContext->aaudio.AAudioStream_getFramesPerDataCallback         = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStream_getFramesPerDataCallback\");\n    pContext->aaudio.AAudioStream_getFramesPerBurst                = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStream_getFramesPerBurst\");\n    pContext->aaudio.AAudioStream_requestStart                     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStream_requestStart\");\n    pContext->aaudio.AAudioStream_requestStop                      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, \"AAudioStream_requestStop\");\n\n\n    pCallbacks->onContextInit             = ma_context_init__aaudio;\n    pCallbacks->onContextUninit           = ma_context_uninit__aaudio;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__aaudio;\n    pCallbacks->onDeviceInit              = ma_device_init__aaudio;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__aaudio;\n    pCallbacks->onDeviceStart             = ma_device_start__aaudio;\n    pCallbacks->onDeviceStop              = ma_device_stop__aaudio;\n    pCallbacks->onDeviceRead              = NULL;   /* Not used because AAudio is asynchronous. */\n    pCallbacks->onDeviceWrite             = NULL;   /* Not used because AAudio is asynchronous. */\n    pCallbacks->onDeviceDataLoop          = NULL;   /* Not used because AAudio is asynchronous. */\n    pCallbacks->onDeviceGetInfo           = ma_device_get_info__aaudio;\n\n\n    /* We need a job thread so we can deal with rerouting. */\n    {\n        ma_result result;\n        ma_device_job_thread_config jobThreadConfig;\n\n        jobThreadConfig = ma_device_job_thread_config_init();\n\n        result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread);\n        if (result != MA_SUCCESS) {\n            ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio);\n            pContext->aaudio.hAAudio = NULL;\n            return result;\n        }\n    }\n\n\n    (void)pConfig;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)\n{\n    ma_device* pDevice;\n\n    MA_ASSERT(pJob != NULL);\n\n    pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice;\n    MA_ASSERT(pDevice != NULL);\n\n    /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */\n    return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType);\n}\n#else\n/* Getting here means there is no AAudio backend so we need a no-op job implementation. */\nstatic ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)\n{\n    return ma_job_process__noop(pJob);\n}\n#endif  /* AAudio */\n\n\n/******************************************************************************\n\nOpenSL|ES Backend\n\n******************************************************************************/\n#ifdef MA_HAS_OPENSL\n#include <SLES/OpenSLES.h>\n#ifdef MA_ANDROID\n#include <SLES/OpenSLES_Android.h>\n#endif\n\ntypedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);\n\n/* OpenSL|ES has one-per-application objects :( */\nstatic SLObjectItf g_maEngineObjectSL    = NULL;\nstatic SLEngineItf g_maEngineSL          = NULL;\nstatic ma_uint32   g_maOpenSLInitCounter = 0;\nstatic ma_spinlock g_maOpenSLSpinlock    = 0;   /* For init/uninit. */\n\n#define MA_OPENSL_OBJ(p)         (*((SLObjectItf)(p)))\n#define MA_OPENSL_OUTPUTMIX(p)   (*((SLOutputMixItf)(p)))\n#define MA_OPENSL_PLAY(p)        (*((SLPlayItf)(p)))\n#define MA_OPENSL_RECORD(p)      (*((SLRecordItf)(p)))\n\n#ifdef MA_ANDROID\n#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))\n#else\n#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))\n#endif\n\nstatic ma_result ma_result_from_OpenSL(SLuint32 result)\n{\n    switch (result)\n    {\n        case SL_RESULT_SUCCESS:                 return MA_SUCCESS;\n        case SL_RESULT_PRECONDITIONS_VIOLATED:  return MA_ERROR;\n        case SL_RESULT_PARAMETER_INVALID:       return MA_INVALID_ARGS;\n        case SL_RESULT_MEMORY_FAILURE:          return MA_OUT_OF_MEMORY;\n        case SL_RESULT_RESOURCE_ERROR:          return MA_INVALID_DATA;\n        case SL_RESULT_RESOURCE_LOST:           return MA_ERROR;\n        case SL_RESULT_IO_ERROR:                return MA_IO_ERROR;\n        case SL_RESULT_BUFFER_INSUFFICIENT:     return MA_NO_SPACE;\n        case SL_RESULT_CONTENT_CORRUPTED:       return MA_INVALID_DATA;\n        case SL_RESULT_CONTENT_UNSUPPORTED:     return MA_FORMAT_NOT_SUPPORTED;\n        case SL_RESULT_CONTENT_NOT_FOUND:       return MA_ERROR;\n        case SL_RESULT_PERMISSION_DENIED:       return MA_ACCESS_DENIED;\n        case SL_RESULT_FEATURE_UNSUPPORTED:     return MA_NOT_IMPLEMENTED;\n        case SL_RESULT_INTERNAL_ERROR:          return MA_ERROR;\n        case SL_RESULT_UNKNOWN_ERROR:           return MA_ERROR;\n        case SL_RESULT_OPERATION_ABORTED:       return MA_ERROR;\n        case SL_RESULT_CONTROL_LOST:            return MA_ERROR;\n        default:                                return MA_ERROR;\n    }\n}\n\n/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */\nstatic ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)\n{\n    switch (id)\n    {\n        case SL_SPEAKER_FRONT_LEFT:            return MA_CHANNEL_FRONT_LEFT;\n        case SL_SPEAKER_FRONT_RIGHT:           return MA_CHANNEL_FRONT_RIGHT;\n        case SL_SPEAKER_FRONT_CENTER:          return MA_CHANNEL_FRONT_CENTER;\n        case SL_SPEAKER_LOW_FREQUENCY:         return MA_CHANNEL_LFE;\n        case SL_SPEAKER_BACK_LEFT:             return MA_CHANNEL_BACK_LEFT;\n        case SL_SPEAKER_BACK_RIGHT:            return MA_CHANNEL_BACK_RIGHT;\n        case SL_SPEAKER_FRONT_LEFT_OF_CENTER:  return MA_CHANNEL_FRONT_LEFT_CENTER;\n        case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;\n        case SL_SPEAKER_BACK_CENTER:           return MA_CHANNEL_BACK_CENTER;\n        case SL_SPEAKER_SIDE_LEFT:             return MA_CHANNEL_SIDE_LEFT;\n        case SL_SPEAKER_SIDE_RIGHT:            return MA_CHANNEL_SIDE_RIGHT;\n        case SL_SPEAKER_TOP_CENTER:            return MA_CHANNEL_TOP_CENTER;\n        case SL_SPEAKER_TOP_FRONT_LEFT:        return MA_CHANNEL_TOP_FRONT_LEFT;\n        case SL_SPEAKER_TOP_FRONT_CENTER:      return MA_CHANNEL_TOP_FRONT_CENTER;\n        case SL_SPEAKER_TOP_FRONT_RIGHT:       return MA_CHANNEL_TOP_FRONT_RIGHT;\n        case SL_SPEAKER_TOP_BACK_LEFT:         return MA_CHANNEL_TOP_BACK_LEFT;\n        case SL_SPEAKER_TOP_BACK_CENTER:       return MA_CHANNEL_TOP_BACK_CENTER;\n        case SL_SPEAKER_TOP_BACK_RIGHT:        return MA_CHANNEL_TOP_BACK_RIGHT;\n        default: return 0;\n    }\n}\n\n/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */\nstatic SLuint32 ma_channel_id_to_opensl(ma_uint8 id)\n{\n    switch (id)\n    {\n        case MA_CHANNEL_MONO:               return SL_SPEAKER_FRONT_CENTER;\n        case MA_CHANNEL_FRONT_LEFT:         return SL_SPEAKER_FRONT_LEFT;\n        case MA_CHANNEL_FRONT_RIGHT:        return SL_SPEAKER_FRONT_RIGHT;\n        case MA_CHANNEL_FRONT_CENTER:       return SL_SPEAKER_FRONT_CENTER;\n        case MA_CHANNEL_LFE:                return SL_SPEAKER_LOW_FREQUENCY;\n        case MA_CHANNEL_BACK_LEFT:          return SL_SPEAKER_BACK_LEFT;\n        case MA_CHANNEL_BACK_RIGHT:         return SL_SPEAKER_BACK_RIGHT;\n        case MA_CHANNEL_FRONT_LEFT_CENTER:  return SL_SPEAKER_FRONT_LEFT_OF_CENTER;\n        case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;\n        case MA_CHANNEL_BACK_CENTER:        return SL_SPEAKER_BACK_CENTER;\n        case MA_CHANNEL_SIDE_LEFT:          return SL_SPEAKER_SIDE_LEFT;\n        case MA_CHANNEL_SIDE_RIGHT:         return SL_SPEAKER_SIDE_RIGHT;\n        case MA_CHANNEL_TOP_CENTER:         return SL_SPEAKER_TOP_CENTER;\n        case MA_CHANNEL_TOP_FRONT_LEFT:     return SL_SPEAKER_TOP_FRONT_LEFT;\n        case MA_CHANNEL_TOP_FRONT_CENTER:   return SL_SPEAKER_TOP_FRONT_CENTER;\n        case MA_CHANNEL_TOP_FRONT_RIGHT:    return SL_SPEAKER_TOP_FRONT_RIGHT;\n        case MA_CHANNEL_TOP_BACK_LEFT:      return SL_SPEAKER_TOP_BACK_LEFT;\n        case MA_CHANNEL_TOP_BACK_CENTER:    return SL_SPEAKER_TOP_BACK_CENTER;\n        case MA_CHANNEL_TOP_BACK_RIGHT:     return SL_SPEAKER_TOP_BACK_RIGHT;\n        default: return 0;\n    }\n}\n\n/* Converts a channel mapping to an OpenSL-style channel mask. */\nstatic SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels)\n{\n    SLuint32 channelMask = 0;\n    ma_uint32 iChannel;\n    for (iChannel = 0; iChannel < channels; ++iChannel) {\n        channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]);\n    }\n\n    return channelMask;\n}\n\n/* Converts an OpenSL-style channel mask to a miniaudio channel map. */\nstatic void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap)\n{\n    if (channels == 1 && channelMask == 0) {\n        pChannelMap[0] = MA_CHANNEL_MONO;\n    } else if (channels == 2 && channelMask == 0) {\n        pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;\n        pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;\n    } else {\n        if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {\n            pChannelMap[0] = MA_CHANNEL_MONO;\n        } else {\n            /* Just iterate over each bit. */\n            ma_uint32 iChannel = 0;\n            ma_uint32 iBit;\n            for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {\n                SLuint32 bitValue = (channelMask & (1UL << iBit));\n                if (bitValue != 0) {\n                    /* The bit is set. */\n                    pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);\n                    iChannel += 1;\n                }\n            }\n        }\n    }\n}\n\nstatic SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)\n{\n    if (samplesPerSec <= SL_SAMPLINGRATE_8) {\n        return SL_SAMPLINGRATE_8;\n    }\n    if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {\n        return SL_SAMPLINGRATE_11_025;\n    }\n    if (samplesPerSec <= SL_SAMPLINGRATE_12) {\n        return SL_SAMPLINGRATE_12;\n    }\n    if (samplesPerSec <= SL_SAMPLINGRATE_16) {\n        return SL_SAMPLINGRATE_16;\n    }\n    if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {\n        return SL_SAMPLINGRATE_22_05;\n    }\n    if (samplesPerSec <= SL_SAMPLINGRATE_24) {\n        return SL_SAMPLINGRATE_24;\n    }\n    if (samplesPerSec <= SL_SAMPLINGRATE_32) {\n        return SL_SAMPLINGRATE_32;\n    }\n    if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {\n        return SL_SAMPLINGRATE_44_1;\n    }\n    if (samplesPerSec <= SL_SAMPLINGRATE_48) {\n        return SL_SAMPLINGRATE_48;\n    }\n\n    /* Android doesn't support more than 48000. */\n#ifndef MA_ANDROID\n    if (samplesPerSec <= SL_SAMPLINGRATE_64) {\n        return SL_SAMPLINGRATE_64;\n    }\n    if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {\n        return SL_SAMPLINGRATE_88_2;\n    }\n    if (samplesPerSec <= SL_SAMPLINGRATE_96) {\n        return SL_SAMPLINGRATE_96;\n    }\n    if (samplesPerSec <= SL_SAMPLINGRATE_192) {\n        return SL_SAMPLINGRATE_192;\n    }\n#endif\n\n    return SL_SAMPLINGRATE_16;\n}\n\n\nstatic SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType)\n{\n    switch (streamType) {\n        case ma_opensl_stream_type_voice:        return SL_ANDROID_STREAM_VOICE;\n        case ma_opensl_stream_type_system:       return SL_ANDROID_STREAM_SYSTEM;\n        case ma_opensl_stream_type_ring:         return SL_ANDROID_STREAM_RING;\n        case ma_opensl_stream_type_media:        return SL_ANDROID_STREAM_MEDIA;\n        case ma_opensl_stream_type_alarm:        return SL_ANDROID_STREAM_ALARM;\n        case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION;\n        default: break;\n    }\n\n    return SL_ANDROID_STREAM_VOICE;\n}\n\nstatic SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset)\n{\n    switch (recordingPreset) {\n        case ma_opensl_recording_preset_generic:             return SL_ANDROID_RECORDING_PRESET_GENERIC;\n        case ma_opensl_recording_preset_camcorder:           return SL_ANDROID_RECORDING_PRESET_CAMCORDER;\n        case ma_opensl_recording_preset_voice_recognition:   return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;\n        case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;\n        case ma_opensl_recording_preset_voice_unprocessed:   return SL_ANDROID_RECORDING_PRESET_UNPROCESSED;\n        default: break;\n    }\n\n    return SL_ANDROID_RECORDING_PRESET_NONE;\n}\n\n\nstatic ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    ma_bool32 cbResult;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */\n    if (g_maOpenSLInitCounter == 0) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /*\n    TODO: Test Me.\n\n    This is currently untested, so for now we are just returning default devices.\n    */\n#if 0 && !defined(MA_ANDROID)\n    ma_bool32 isTerminated = MA_FALSE;\n\n    SLuint32 pDeviceIDs[128];\n    SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);\n\n    SLAudioIODeviceCapabilitiesItf deviceCaps;\n    SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);\n    if (resultSL != SL_RESULT_SUCCESS) {\n        /* The interface may not be supported so just report a default device. */\n        goto return_default_device;\n    }\n\n    /* Playback */\n    if (!isTerminated) {\n        resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {\n            ma_device_info deviceInfo;\n            MA_ZERO_OBJECT(&deviceInfo);\n            deviceInfo.id.opensl = pDeviceIDs[iDevice];\n\n            SLAudioOutputDescriptor desc;\n            resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);\n            if (resultSL == SL_RESULT_SUCCESS) {\n                ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);\n\n                ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n                if (cbResult == MA_FALSE) {\n                    isTerminated = MA_TRUE;\n                    break;\n                }\n            }\n        }\n    }\n\n    /* Capture */\n    if (!isTerminated) {\n        resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {\n            ma_device_info deviceInfo;\n            MA_ZERO_OBJECT(&deviceInfo);\n            deviceInfo.id.opensl = pDeviceIDs[iDevice];\n\n            SLAudioInputDescriptor desc;\n            resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);\n            if (resultSL == SL_RESULT_SUCCESS) {\n                ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);\n\n                ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n                if (cbResult == MA_FALSE) {\n                    isTerminated = MA_TRUE;\n                    break;\n                }\n            }\n        }\n    }\n\n    return MA_SUCCESS;\n#else\n    goto return_default_device;\n#endif\n\nreturn_default_device:;\n    cbResult = MA_TRUE;\n\n    /* Playback. */\n    if (cbResult) {\n        ma_device_info deviceInfo;\n        MA_ZERO_OBJECT(&deviceInfo);\n        deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;\n        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n        cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n    }\n\n    /* Capture. */\n    if (cbResult) {\n        ma_device_info deviceInfo;\n        MA_ZERO_OBJECT(&deviceInfo);\n        deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;\n        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n        cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo)\n{\n    MA_ASSERT(pContext    != NULL);\n    MA_ASSERT(pDeviceInfo != NULL);\n\n    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format     = format;\n    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels   = channels;\n    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;\n    pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags      = 0;\n    pDeviceInfo->nativeDataFormatCount += 1;\n}\n\nstatic void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo)\n{\n    ma_uint32 minChannels   = 1;\n    ma_uint32 maxChannels   = 2;\n    ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000;\n    ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000;\n    ma_uint32 iChannel;\n    ma_uint32 iSampleRate;\n\n    MA_ASSERT(pContext    != NULL);\n    MA_ASSERT(pDeviceInfo != NULL);\n\n    /*\n    Each sample format can support mono and stereo, and we'll support a small subset of standard\n    rates (up to 48000). A better solution would be to somehow find a native sample rate.\n    */\n    for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) {\n        for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {\n            ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];\n            if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {\n                ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo);\n            }\n        }\n    }\n}\n\nstatic ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    MA_ASSERT(pContext != NULL);\n\n    MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */\n    if (g_maOpenSLInitCounter == 0) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /*\n    TODO: Test Me.\n\n    This is currently untested, so for now we are just returning default devices.\n    */\n#if 0 && !defined(MA_ANDROID)\n    SLAudioIODeviceCapabilitiesItf deviceCaps;\n    SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);\n    if (resultSL != SL_RESULT_SUCCESS) {\n        /* The interface may not be supported so just report a default device. */\n        goto return_default_device;\n    }\n\n    if (deviceType == ma_device_type_playback) {\n        SLAudioOutputDescriptor desc;\n        resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);\n    } else {\n        SLAudioInputDescriptor desc;\n        resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);\n    }\n\n    goto return_detailed_info;\n#else\n    goto return_default_device;\n#endif\n\nreturn_default_device:\n    if (pDeviceID != NULL) {\n        if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||\n            (deviceType == ma_device_type_capture  && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {\n            return MA_NO_DEVICE;   /* Don't know the device. */\n        }\n    }\n\n    /* ID and Name / Description */\n    if (deviceType == ma_device_type_playback) {\n        pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n    } else {\n        pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n    }\n\n    pDeviceInfo->isDefault = MA_TRUE;\n\n    goto return_detailed_info;\n\n\nreturn_detailed_info:\n\n    /*\n    For now we're just outputting a set of values that are supported by the API but not necessarily supported\n    by the device natively. Later on we should work on this so that it more closely reflects the device's\n    actual native format.\n    */\n    pDeviceInfo->nativeDataFormatCount = 0;\n#if defined(MA_ANDROID) && __ANDROID_API__ >= 21\n    ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo);\n#endif\n    ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo);\n    ma_context_add_data_format__opensl(pContext, ma_format_u8,  pDeviceInfo);\n\n    return MA_SUCCESS;\n}\n\n\n#ifdef MA_ANDROID\n/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/\nstatic void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    size_t periodSizeInBytes;\n    ma_uint8* pBuffer;\n    SLresult resultSL;\n\n    MA_ASSERT(pDevice != NULL);\n\n    (void)pBufferQueue;\n\n    /*\n    For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like\n    OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,\n    but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(\n    */\n\n    /* Don't do anything if the device is not started. */\n    if (ma_device_get_state(pDevice) != ma_device_state_started) {\n        return;\n    }\n\n    /* Don't do anything if the device is being drained. */\n    if (pDevice->opensl.isDrainingCapture) {\n        return;\n    }\n\n    periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);\n    pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);\n\n    ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames);\n\n    resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);\n    if (resultSL != SL_RESULT_SUCCESS) {\n        return;\n    }\n\n    pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;\n}\n\nstatic void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    size_t periodSizeInBytes;\n    ma_uint8* pBuffer;\n    SLresult resultSL;\n\n    MA_ASSERT(pDevice != NULL);\n\n    (void)pBufferQueue;\n\n    /* Don't do anything if the device is not started. */\n    if (ma_device_get_state(pDevice) != ma_device_state_started) {\n        return;\n    }\n\n    /* Don't do anything if the device is being drained. */\n    if (pDevice->opensl.isDrainingPlayback) {\n        return;\n    }\n\n    periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);\n    pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);\n\n    ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames);\n\n    resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);\n    if (resultSL != SL_RESULT_SUCCESS) {\n        return;\n    }\n\n    pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;\n}\n#endif\n\nstatic ma_result ma_device_uninit__opensl(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */\n    if (g_maOpenSLInitCounter == 0) {\n        return MA_INVALID_OPERATION;\n    }\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        if (pDevice->opensl.pAudioRecorderObj) {\n            MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);\n        }\n\n        ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        if (pDevice->opensl.pAudioPlayerObj) {\n            MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);\n        }\n        if (pDevice->opensl.pOutputMixObj) {\n            MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);\n        }\n\n        ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks);\n    }\n\n    return MA_SUCCESS;\n}\n\n#if defined(MA_ANDROID) && __ANDROID_API__ >= 21\ntypedef SLAndroidDataFormat_PCM_EX  ma_SLDataFormat_PCM;\n#else\ntypedef SLDataFormat_PCM            ma_SLDataFormat_PCM;\n#endif\n\nstatic ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat)\n{\n    /* We need to convert our format/channels/rate so that they aren't set to default. */\n    if (format == ma_format_unknown) {\n        format = MA_DEFAULT_FORMAT;\n    }\n    if (channels == 0) {\n        channels = MA_DEFAULT_CHANNELS;\n    }\n    if (sampleRate == 0) {\n        sampleRate = MA_DEFAULT_SAMPLE_RATE;\n    }\n\n#if defined(MA_ANDROID) && __ANDROID_API__ >= 21\n    if (format == ma_format_f32) {\n        pDataFormat->formatType     = SL_ANDROID_DATAFORMAT_PCM_EX;\n        pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;\n    } else {\n        pDataFormat->formatType = SL_DATAFORMAT_PCM;\n    }\n#else\n    pDataFormat->formatType = SL_DATAFORMAT_PCM;\n#endif\n\n    pDataFormat->numChannels   = channels;\n    ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000);  /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */\n    pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8;\n    pDataFormat->channelMask   = ma_channel_map_to_channel_mask__opensl(channelMap, channels);\n    pDataFormat->endianness    = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;\n\n    /*\n    Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html\n     - Only mono and stereo is supported.\n     - Only u8 and s16 formats are supported.\n     - Maximum sample rate of 48000.\n    */\n#ifdef MA_ANDROID\n    if (pDataFormat->numChannels > 2) {\n        pDataFormat->numChannels = 2;\n    }\n#if __ANDROID_API__ >= 21\n    if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {\n        /* It's floating point. */\n        MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);\n        if (pDataFormat->bitsPerSample > 32) {\n            pDataFormat->bitsPerSample = 32;\n        }\n    } else {\n        if (pDataFormat->bitsPerSample > 16) {\n            pDataFormat->bitsPerSample = 16;\n        }\n    }\n#else\n    if (pDataFormat->bitsPerSample > 16) {\n        pDataFormat->bitsPerSample = 16;\n    }\n#endif\n    if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {\n        ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;\n    }\n#endif\n\n    pDataFormat->containerSize = pDataFormat->bitsPerSample;  /* Always tightly packed for now. */\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    ma_bool32 isFloatingPoint = MA_FALSE;\n#if defined(MA_ANDROID) && __ANDROID_API__ >= 21\n    if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {\n        MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);\n        isFloatingPoint = MA_TRUE;\n    }\n#endif\n    if (isFloatingPoint) {\n        if (pDataFormat->bitsPerSample == 32) {\n            *pFormat = ma_format_f32;\n        }\n    } else {\n        if (pDataFormat->bitsPerSample == 8) {\n            *pFormat = ma_format_u8;\n        } else if (pDataFormat->bitsPerSample == 16) {\n            *pFormat = ma_format_s16;\n        } else if (pDataFormat->bitsPerSample == 24) {\n            *pFormat = ma_format_s24;\n        } else if (pDataFormat->bitsPerSample == 32) {\n            *pFormat = ma_format_s32;\n        }\n    }\n\n    *pChannels   = pDataFormat->numChannels;\n    *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;\n    ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n#ifdef MA_ANDROID\n    SLDataLocator_AndroidSimpleBufferQueue queue;\n    SLresult resultSL;\n    size_t bufferSizeInBytes;\n    SLInterfaceID itfIDs[2];\n    const SLboolean itfIDsRequired[] = {\n        SL_BOOLEAN_TRUE,    /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */\n        SL_BOOLEAN_FALSE    /* SL_IID_ANDROIDCONFIGURATION */\n    };\n#endif\n\n    MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */\n    if (g_maOpenSLInitCounter == 0) {\n        return MA_INVALID_OPERATION;\n    }\n\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    /*\n    For now, only supporting Android implementations of OpenSL|ES since that's the only one I've\n    been able to test with and I currently depend on Android-specific extensions (simple buffer\n    queues).\n    */\n#ifdef MA_ANDROID\n    itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE;\n    itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION;\n\n    /* No exclusive mode with OpenSL|ES. */\n    if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||\n        ((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode  == ma_share_mode_exclusive)) {\n        return MA_SHARE_MODE_NOT_SUPPORTED;\n    }\n\n    /* Now we can start initializing the device properly. */\n    MA_ASSERT(pDevice != NULL);\n    MA_ZERO_OBJECT(&pDevice->opensl);\n\n    queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;\n\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        ma_SLDataFormat_PCM pcm;\n        SLDataLocator_IODevice locatorDevice;\n        SLDataSource source;\n        SLDataSink sink;\n        SLAndroidConfigurationItf pRecorderConfig;\n\n        ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm);\n\n        locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;\n        locatorDevice.deviceType  = SL_IODEVICE_AUDIOINPUT;\n        locatorDevice.deviceID    = SL_DEFAULTDEVICEID_AUDIOINPUT;  /* Must always use the default device with Android. */\n        locatorDevice.device      = NULL;\n\n        source.pLocator = &locatorDevice;\n        source.pFormat  = NULL;\n\n        queue.numBuffers = pDescriptorCapture->periodCount;\n\n        sink.pLocator = &queue;\n        sink.pFormat  = (SLDataFormat_PCM*)&pcm;\n\n        resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);\n        if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {\n            /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */\n            pcm.formatType    = SL_DATAFORMAT_PCM;\n            pcm.numChannels   = 1;\n            ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;  /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */\n            pcm.bitsPerSample = 16;\n            pcm.containerSize = pcm.bitsPerSample;  /* Always tightly packed for now. */\n            pcm.channelMask   = 0;\n            resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);\n        }\n\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to create audio recorder.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n\n        /* Set the recording preset before realizing the player. */\n        if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) {\n            resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig);\n            if (resultSL == SL_RESULT_SUCCESS) {\n                SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset);\n                resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32));\n                if (resultSL != SL_RESULT_SUCCESS) {\n                    /* Failed to set the configuration. Just keep going. */\n                }\n            }\n        }\n\n        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to realize audio recorder.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to retrieve SL_IID_RECORD interface.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to register buffer queue callback.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        /* The internal format is determined by the \"pcm\" object. */\n        ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap));\n\n        /* Buffer. */\n        pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);\n        pDevice->opensl.currentBufferIndexCapture = 0;\n\n        bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount;\n        pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);\n        if (pDevice->opensl.pBufferCapture == NULL) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to allocate memory for data buffer.\");\n            return MA_OUT_OF_MEMORY;\n        }\n        MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        ma_SLDataFormat_PCM pcm;\n        SLDataSource source;\n        SLDataLocator_OutputMix outmixLocator;\n        SLDataSink sink;\n        SLAndroidConfigurationItf pPlayerConfig;\n\n        ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm);\n\n        resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to create output mix.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to realize output mix object.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        /* Set the output device. */\n        if (pDescriptorPlayback->pDeviceID != NULL) {\n            SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl;\n            MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);\n        }\n\n        queue.numBuffers = pDescriptorPlayback->periodCount;\n\n        source.pLocator = &queue;\n        source.pFormat  = (SLDataFormat_PCM*)&pcm;\n\n        outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;\n        outmixLocator.outputMix   = (SLObjectItf)pDevice->opensl.pOutputMixObj;\n\n        sink.pLocator = &outmixLocator;\n        sink.pFormat  = NULL;\n\n        resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);\n        if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {\n            /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */\n            pcm.formatType = SL_DATAFORMAT_PCM;\n            pcm.numChannels = 2;\n            ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;\n            pcm.bitsPerSample = 16;\n            pcm.containerSize = pcm.bitsPerSample;  /* Always tightly packed for now. */\n            pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;\n            resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);\n        }\n\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to create audio player.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n\n        /* Set the stream type before realizing the player. */\n        if (pConfig->opensl.streamType != ma_opensl_stream_type_default) {\n            resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig);\n            if (resultSL == SL_RESULT_SUCCESS) {\n                SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType);\n                resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));\n                if (resultSL != SL_RESULT_SUCCESS) {\n                    /* Failed to set the configuration. Just keep going. */\n                }\n            }\n        }\n\n        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to realize audio player.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to retrieve SL_IID_PLAY interface.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to register buffer queue callback.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        /* The internal format is determined by the \"pcm\" object. */\n        ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap));\n\n        /* Buffer. */\n        pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);\n        pDevice->opensl.currentBufferIndexPlayback   = 0;\n\n        bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount;\n        pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);\n        if (pDevice->opensl.pBufferPlayback == NULL) {\n            ma_device_uninit__opensl(pDevice);\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to allocate memory for data buffer.\");\n            return MA_OUT_OF_MEMORY;\n        }\n        MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);\n    }\n\n    return MA_SUCCESS;\n#else\n    return MA_NO_BACKEND;   /* Non-Android implementations are not supported. */\n#endif\n}\n\nstatic ma_result ma_device_start__opensl(ma_device* pDevice)\n{\n    SLresult resultSL;\n    size_t periodSizeInBytes;\n    ma_uint32 iPeriod;\n\n    MA_ASSERT(pDevice != NULL);\n\n    MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */\n    if (g_maOpenSLInitCounter == 0) {\n        return MA_INVALID_OPERATION;\n    }\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to start internal capture device.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);\n        for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {\n            resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);\n            if (resultSL != SL_RESULT_SUCCESS) {\n                MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to enqueue buffer for capture device.\");\n                return ma_result_from_OpenSL(resultSL);\n            }\n        }\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to start internal playback device.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */\n        if (pDevice->type == ma_device_type_duplex) {\n            MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));\n        } else {\n            ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback);\n        }\n\n        periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);\n        for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {\n            resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);\n            if (resultSL != SL_RESULT_SUCCESS) {\n                MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);\n                ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to enqueue buffer for playback device.\");\n                return ma_result_from_OpenSL(resultSL);\n            }\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType)\n{\n    SLAndroidSimpleBufferQueueItf pBufferQueue;\n\n    MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback);\n\n    if (pDevice->type == ma_device_type_capture) {\n        pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture;\n        pDevice->opensl.isDrainingCapture  = MA_TRUE;\n    } else {\n        pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback;\n        pDevice->opensl.isDrainingPlayback = MA_TRUE;\n    }\n\n    for (;;) {\n        SLAndroidSimpleBufferQueueState state;\n\n        MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state);\n        if (state.count == 0) {\n            break;\n        }\n\n        ma_sleep(10);\n    }\n\n    if (pDevice->type == ma_device_type_capture) {\n        pDevice->opensl.isDrainingCapture  = MA_FALSE;\n    } else {\n        pDevice->opensl.isDrainingPlayback = MA_FALSE;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__opensl(ma_device* pDevice)\n{\n    SLresult resultSL;\n\n    MA_ASSERT(pDevice != NULL);\n\n    MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */\n    if (g_maOpenSLInitCounter == 0) {\n        return MA_INVALID_OPERATION;\n    }\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {\n        ma_device_drain__opensl(pDevice, ma_device_type_capture);\n\n        resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to stop internal capture device.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);\n    }\n\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ma_device_drain__opensl(pDevice, ma_device_type_playback);\n\n        resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, \"[OpenSL] Failed to stop internal playback device.\");\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);\n    }\n\n    /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */\n    ma_device__on_notification_stopped(pDevice);\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_context_uninit__opensl(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_opensl);\n    (void)pContext;\n\n    /* Uninit global data. */\n    ma_spinlock_lock(&g_maOpenSLSpinlock);\n    {\n        MA_ASSERT(g_maOpenSLInitCounter > 0);   /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */\n\n        g_maOpenSLInitCounter -= 1;\n        if (g_maOpenSLInitCounter == 0) {\n            (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);\n        }\n    }\n    ma_spinlock_unlock(&g_maOpenSLSpinlock);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle)\n{\n    /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */\n    ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName);\n    if (p == NULL) {\n        ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, \"[OpenSL] Cannot find symbol %s\", pName);\n        return MA_NO_BACKEND;\n    }\n\n    *pHandle = *p;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext)\n{\n    g_maOpenSLInitCounter += 1;\n    if (g_maOpenSLInitCounter == 1) {\n        SLresult resultSL;\n\n        resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            g_maOpenSLInitCounter -= 1;\n            return ma_result_from_OpenSL(resultSL);\n        }\n\n        (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);\n\n        resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL);\n        if (resultSL != SL_RESULT_SUCCESS) {\n            (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);\n            g_maOpenSLInitCounter -= 1;\n            return ma_result_from_OpenSL(resultSL);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n    ma_result result;\n\n#if !defined(MA_NO_RUNTIME_LINKING)\n    size_t i;\n    const char* libOpenSLESNames[] = {\n        \"libOpenSLES.so\"\n    };\n#endif\n\n    MA_ASSERT(pContext != NULL);\n\n    (void)pConfig;\n\n#if !defined(MA_NO_RUNTIME_LINKING)\n    /*\n    Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One\n    report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime\n    and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any\n    references to the symbols and will hopefully skip the checks.\n    */\n    for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) {\n        pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]);\n        if (pContext->opensl.libOpenSLES != NULL) {\n            break;\n        }\n    }\n\n    if (pContext->opensl.libOpenSLES == NULL) {\n        ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, \"[OpenSL] Could not find libOpenSLES.so\");\n        return MA_NO_BACKEND;\n    }\n\n    result = ma_dlsym_SLInterfaceID__opensl(pContext, \"SL_IID_ENGINE\", &pContext->opensl.SL_IID_ENGINE);\n    if (result != MA_SUCCESS) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);\n        return result;\n    }\n\n    result = ma_dlsym_SLInterfaceID__opensl(pContext, \"SL_IID_AUDIOIODEVICECAPABILITIES\", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES);\n    if (result != MA_SUCCESS) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);\n        return result;\n    }\n\n    result = ma_dlsym_SLInterfaceID__opensl(pContext, \"SL_IID_ANDROIDSIMPLEBUFFERQUEUE\", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE);\n    if (result != MA_SUCCESS) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);\n        return result;\n    }\n\n    result = ma_dlsym_SLInterfaceID__opensl(pContext, \"SL_IID_RECORD\", &pContext->opensl.SL_IID_RECORD);\n    if (result != MA_SUCCESS) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);\n        return result;\n    }\n\n    result = ma_dlsym_SLInterfaceID__opensl(pContext, \"SL_IID_PLAY\", &pContext->opensl.SL_IID_PLAY);\n    if (result != MA_SUCCESS) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);\n        return result;\n    }\n\n    result = ma_dlsym_SLInterfaceID__opensl(pContext, \"SL_IID_OUTPUTMIX\", &pContext->opensl.SL_IID_OUTPUTMIX);\n    if (result != MA_SUCCESS) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);\n        return result;\n    }\n\n    result = ma_dlsym_SLInterfaceID__opensl(pContext, \"SL_IID_ANDROIDCONFIGURATION\", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION);\n    if (result != MA_SUCCESS) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);\n        return result;\n    }\n\n    pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, \"slCreateEngine\");\n    if (pContext->opensl.slCreateEngine == NULL) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);\n        ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, \"[OpenSL] Cannot find symbol slCreateEngine.\");\n        return MA_NO_BACKEND;\n    }\n#else\n    pContext->opensl.SL_IID_ENGINE                    = (ma_handle)SL_IID_ENGINE;\n    pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES;\n    pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE  = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE;\n    pContext->opensl.SL_IID_RECORD                    = (ma_handle)SL_IID_RECORD;\n    pContext->opensl.SL_IID_PLAY                      = (ma_handle)SL_IID_PLAY;\n    pContext->opensl.SL_IID_OUTPUTMIX                 = (ma_handle)SL_IID_OUTPUTMIX;\n    pContext->opensl.SL_IID_ANDROIDCONFIGURATION      = (ma_handle)SL_IID_ANDROIDCONFIGURATION;\n    pContext->opensl.slCreateEngine                   = (ma_proc)slCreateEngine;\n#endif\n\n\n    /* Initialize global data first if applicable. */\n    ma_spinlock_lock(&g_maOpenSLSpinlock);\n    {\n        result = ma_context_init_engine_nolock__opensl(pContext);\n    }\n    ma_spinlock_unlock(&g_maOpenSLSpinlock);\n\n    if (result != MA_SUCCESS) {\n        ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);\n        ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, \"[OpenSL] Failed to initialize OpenSL engine.\");\n        return result;\n    }\n\n    pCallbacks->onContextInit             = ma_context_init__opensl;\n    pCallbacks->onContextUninit           = ma_context_uninit__opensl;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__opensl;\n    pCallbacks->onDeviceInit              = ma_device_init__opensl;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__opensl;\n    pCallbacks->onDeviceStart             = ma_device_start__opensl;\n    pCallbacks->onDeviceStop              = ma_device_stop__opensl;\n    pCallbacks->onDeviceRead              = NULL;   /* Not needed because OpenSL|ES is asynchronous. */\n    pCallbacks->onDeviceWrite             = NULL;   /* Not needed because OpenSL|ES is asynchronous. */\n    pCallbacks->onDeviceDataLoop          = NULL;   /* Not needed because OpenSL|ES is asynchronous. */\n\n    return MA_SUCCESS;\n}\n#endif  /* OpenSL|ES */\n\n\n/******************************************************************************\n\nWeb Audio Backend\n\n******************************************************************************/\n#ifdef MA_HAS_WEBAUDIO\n#include <emscripten/emscripten.h>\n\n#if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32)))\n    #include <emscripten/webaudio.h>\n    #define MA_SUPPORT_AUDIO_WORKLETS\n#endif\n\n/*\nTODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS.\n*/\n#if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS)\n    #define MA_USE_AUDIO_WORKLETS\n#endif\n\n/* The thread stack size must be a multiple of 16. */\n#ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE\n#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 16384\n#endif\n\n#if defined(MA_USE_AUDIO_WORKLETS)\n#define MA_WEBAUDIO_LATENCY_HINT_BALANCED       \"balanced\"\n#define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE    \"interactive\"\n#define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK       \"playback\"\n#endif\n\nstatic ma_bool32 ma_is_capture_supported__webaudio()\n{\n    return EM_ASM_INT({\n        return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);\n    }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */\n}\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nvoid* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_malloc(sz, pAllocationCallbacks);\n}\n\nvoid EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_free(p, pAllocationCallbacks);\n}\n\nvoid EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)\n{\n    ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount);\n}\n\nvoid EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)\n{\n    ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount);\n}\n#ifdef __cplusplus\n}\n#endif\n\nstatic ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    ma_bool32 cbResult = MA_TRUE;\n\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(callback != NULL);\n\n    /* Only supporting default devices for now. */\n\n    /* Playback. */\n    if (cbResult) {\n        ma_device_info deviceInfo;\n        MA_ZERO_OBJECT(&deviceInfo);\n        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n        deviceInfo.isDefault = MA_TRUE;    /* Only supporting default devices. */\n        cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);\n    }\n\n    /* Capture. */\n    if (cbResult) {\n        if (ma_is_capture_supported__webaudio()) {\n            ma_device_info deviceInfo;\n            MA_ZERO_OBJECT(&deviceInfo);\n            ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n            deviceInfo.isDefault = MA_TRUE;    /* Only supporting default devices. */\n            cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    MA_ASSERT(pContext != NULL);\n\n    if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {\n        return MA_NO_DEVICE;\n    }\n\n    MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));\n\n    /* Only supporting default devices for now. */\n    (void)pDeviceID;\n    if (deviceType == ma_device_type_playback) {\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n    } else {\n        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n    }\n\n    /* Only supporting default devices. */\n    pDeviceInfo->isDefault = MA_TRUE;\n\n    /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */\n    pDeviceInfo->nativeDataFormats[0].flags      = 0;\n    pDeviceInfo->nativeDataFormats[0].format     = ma_format_unknown;\n    pDeviceInfo->nativeDataFormats[0].channels   = 0; /* All channels are supported. */\n    pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({\n        try {\n            var temp = new (window.AudioContext || window.webkitAudioContext)();\n            var sampleRate = temp.sampleRate;\n            temp.close();\n            return sampleRate;\n        } catch(e) {\n            return 0;\n        }\n    }, 0);  /* Must pass in a dummy argument for C99 compatibility. */\n\n    if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) {\n        return MA_NO_DEVICE;\n    }\n\n    pDeviceInfo->nativeDataFormatCount = 1;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_uninit__webaudio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    #if defined(MA_USE_AUDIO_WORKLETS)\n    {\n        EM_ASM({\n            var device = miniaudio.get_device_by_index($0);\n\n            if (device.streamNode !== undefined) {\n                device.streamNode.disconnect();\n                device.streamNode = undefined;\n            }\n        }, pDevice->webaudio.deviceIndex);\n\n        emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet);\n        emscripten_destroy_audio_context(pDevice->webaudio.audioContext);\n        ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks);\n    }\n    #else\n    {\n        EM_ASM({\n            var device = miniaudio.get_device_by_index($0);\n\n            /* Make sure all nodes are disconnected and marked for collection. */\n            if (device.scriptNode !== undefined) {\n                device.scriptNode.onaudioprocess = function(e) {};  /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */\n                device.scriptNode.disconnect();\n                device.scriptNode = undefined;\n            }\n\n            if (device.streamNode !== undefined) {\n                device.streamNode.disconnect();\n                device.streamNode = undefined;\n            }\n\n            /*\n            Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want\n            to clear the callback before closing.\n            */\n            device.webaudio.close();\n            device.webaudio = undefined;\n            device.pDevice = undefined;\n        }, pDevice->webaudio.deviceIndex);\n    }\n    #endif\n\n    /* Clean up the device on the JS side. */\n    EM_ASM({\n        miniaudio.untrack_device_by_index($0);\n    }, pDevice->webaudio.deviceIndex);\n\n    ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);\n\n    return MA_SUCCESS;\n}\n\n#if !defined(MA_USE_AUDIO_WORKLETS)\nstatic ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)\n{\n    /*\n    There have been reports of the default buffer size being too small on some browsers. If we're using\n    the default buffer size, we'll make sure the period size is bigger than our standard defaults.\n    */\n    ma_uint32 periodSizeInFrames;\n\n    if (nativeSampleRate == 0) {\n        nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;\n    }\n\n    if (pDescriptor->periodSizeInFrames == 0) {\n        if (pDescriptor->periodSizeInMilliseconds == 0) {\n            if (performanceProfile == ma_performance_profile_low_latency) {\n                periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate);  /* 1 frame @ 30 FPS */\n            } else {\n                periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate);\n            }\n        } else {\n            periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);\n        }\n    } else {\n        periodSizeInFrames = pDescriptor->periodSizeInFrames;\n    }\n\n    /* The size of the buffer must be a power of 2 and between 256 and 16384. */\n    if (periodSizeInFrames < 256) {\n        periodSizeInFrames = 256;\n    } else if (periodSizeInFrames > 16384) {\n        periodSizeInFrames = 16384;\n    } else {\n        periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames);\n    }\n\n    return periodSizeInFrames;\n}\n#endif\n\n\n#if defined(MA_USE_AUDIO_WORKLETS)\ntypedef struct\n{\n    ma_device* pDevice;\n    const ma_device_config* pConfig;\n    ma_device_descriptor* pDescriptorPlayback;\n    ma_device_descriptor* pDescriptorCapture;\n} ma_audio_worklet_thread_initialized_data;\n\nstatic EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData)\n{\n    ma_device* pDevice = (ma_device*)pUserData;\n    ma_uint32 frameCount;\n\n    (void)paramCount;\n    (void)pParams;\n\n    if (ma_device_get_state(pDevice) != ma_device_state_started) {\n        return EM_TRUE;\n    }\n\n    /*\n    The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels\n    like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer\n    to variables instead of a hard coded number. In any case, will follow along for the time being.\n\n    Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio\n    for further processing.\n    */\n    frameCount = 128;\n\n    if (inputCount > 0) {\n        /* Input data needs to be interleaved before we hand it to the client. */\n        for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) {\n            for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame];\n            }\n        }\n\n        ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer);\n    }\n\n    if (outputCount > 0) {\n        /* If it's a capture-only device, we'll need to output silence. */\n        if (pDevice->type == ma_device_type_capture) {\n            MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float));\n        } else {\n            ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer);\n\n            /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */\n            for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) {\n                for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                    pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel];\n                }\n            }\n        }\n    }\n\n    return EM_TRUE;\n}\n\n\nstatic void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData)\n{\n    ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData;\n    EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions;\n    int channels = 0;\n    size_t intermediaryBufferSizeInFrames;\n    int sampleRate;\n\n    if (success == EM_FALSE) {\n        pParameters->pDevice->webaudio.initResult = MA_ERROR;\n        ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);\n        return;\n    }\n\n    /* The next step is to initialize the audio worklet node. */\n    MA_ZERO_OBJECT(&audioWorkletOptions);\n\n    /*\n    The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel\n    count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an\n    output channel count on the capture side. This is slightly confusing for capture mode because intuitively you\n    wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have\n    proper control over the channel count. In the capture case, we'll have to output silence to it's output node.\n    */\n    if (pParameters->pConfig->deviceType == ma_device_type_capture) {\n        channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS);\n        audioWorkletOptions.numberOfInputs = 1;\n    } else {\n        channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS);\n\n        if (pParameters->pConfig->deviceType == ma_device_type_duplex) {\n            audioWorkletOptions.numberOfInputs = 1;\n        } else {\n            audioWorkletOptions.numberOfInputs = 0;\n        }\n    }\n\n    audioWorkletOptions.numberOfOutputs = 1;\n    audioWorkletOptions.outputChannelCounts = &channels;\n\n\n    /*\n    Now that we know the channel count to use we can allocate the intermediary buffer. The\n    intermediary buffer is used for interleaving and deinterleaving.\n    */\n    intermediaryBufferSizeInFrames = 128;\n\n    pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks);\n    if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) {\n        pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY;\n        ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);\n        return;\n    }\n\n\n    pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, \"miniaudio\", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice);\n\n    /* With the audio worklet initialized we can now attach it to the graph. */\n    if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) {\n        ma_result attachmentResult = (ma_result)EM_ASM_INT({\n            var getUserMediaResult = 0;\n            var audioWorklet = emscriptenGetAudioObject($0);\n            var audioContext = emscriptenGetAudioObject($1);\n\n            navigator.mediaDevices.getUserMedia({audio:true, video:false})\n                .then(function(stream) {\n                    audioContext.streamNode = audioContext.createMediaStreamSource(stream);\n                    audioContext.streamNode.connect(audioWorklet);\n                    audioWorklet.connect(audioContext.destination);\n                    getUserMediaResult = 0;   /* 0 = MA_SUCCESS */\n                })\n                .catch(function(error) {\n                    console.log(\"navigator.mediaDevices.getUserMedia Failed: \" + error);\n                    getUserMediaResult = -1;  /* -1 = MA_ERROR */\n                });\n\n            return getUserMediaResult;\n        }, pParameters->pDevice->webaudio.audioWorklet, audioContext);\n\n        if (attachmentResult != MA_SUCCESS) {\n            ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, \"Web Audio: Failed to connect capture node.\");\n            emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet);\n            pParameters->pDevice->webaudio.initResult = attachmentResult;\n            ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);\n            return;\n        }\n    }\n\n    /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */\n    if (pParameters->pConfig->deviceType == ma_device_type_playback) {\n        ma_result attachmentResult = (ma_result)EM_ASM_INT({\n            var audioWorklet = emscriptenGetAudioObject($0);\n            var audioContext = emscriptenGetAudioObject($1);\n            audioWorklet.connect(audioContext.destination);\n            return 0;   /* 0 = MA_SUCCESS */\n        }, pParameters->pDevice->webaudio.audioWorklet, audioContext);\n\n        if (attachmentResult != MA_SUCCESS) {\n            ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, \"Web Audio: Failed to connect playback node.\");\n            pParameters->pDevice->webaudio.initResult = attachmentResult;\n            ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);\n            return;\n        }\n    }\n\n    /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */\n    sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext);\n\n    if (pParameters->pDescriptorCapture != NULL) {\n        pParameters->pDescriptorCapture->format              = ma_format_f32;\n        pParameters->pDescriptorCapture->channels            = (ma_uint32)channels;\n        pParameters->pDescriptorCapture->sampleRate          = (ma_uint32)sampleRate;\n        ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels);\n        pParameters->pDescriptorCapture->periodSizeInFrames  = intermediaryBufferSizeInFrames;\n        pParameters->pDescriptorCapture->periodCount         = 1;\n    }\n\n    if (pParameters->pDescriptorPlayback != NULL) {\n        pParameters->pDescriptorPlayback->format             = ma_format_f32;\n        pParameters->pDescriptorPlayback->channels           = (ma_uint32)channels;\n        pParameters->pDescriptorPlayback->sampleRate         = (ma_uint32)sampleRate;\n        ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels);\n        pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames;\n        pParameters->pDescriptorPlayback->periodCount        = 1;\n    }\n\n    /* At this point we're done and we can return. */\n    ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, \"AudioWorklets: Created worklet node: %d\\n\", pParameters->pDevice->webaudio.audioWorklet);\n    pParameters->pDevice->webaudio.initResult = MA_SUCCESS;\n    ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);\n}\n\nstatic void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData)\n{\n    ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData;\n    WebAudioWorkletProcessorCreateOptions workletProcessorOptions;\n\n    MA_ASSERT(pParameters != NULL);\n\n    if (success == EM_FALSE) {\n        pParameters->pDevice->webaudio.initResult = MA_ERROR;\n        return;\n    }\n\n    MA_ZERO_OBJECT(&workletProcessorOptions);\n    workletProcessorOptions.name = \"miniaudio\"; /* I'm not entirely sure what to call this. Does this need to be globally unique, or does it need only be unique for a given AudioContext? */\n\n    emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters);\n}\n#endif\n\nstatic ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)\n{\n    if (pConfig->deviceType == ma_device_type_loopback) {\n        return MA_DEVICE_TYPE_NOT_SUPPORTED;\n    }\n\n    /* No exclusive mode with Web Audio. */\n    if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||\n        ((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode  == ma_share_mode_exclusive)) {\n        return MA_SHARE_MODE_NOT_SUPPORTED;\n    }\n\n    /*\n    With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so\n    it might be worthwhile to look into that as well.\n    */\n    #if defined(MA_USE_AUDIO_WORKLETS)\n    {\n        EmscriptenWebAudioCreateAttributes audioContextAttributes;\n        ma_audio_worklet_thread_initialized_data* pInitParameters;\n        void* pStackBuffer;\n\n        if (pConfig->performanceProfile == ma_performance_profile_conservative) {\n            audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK;\n        } else {\n            audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE;\n        }\n\n        /*\n        In my testing, Firefox does not seem to capture audio data properly if the sample rate is set\n        to anything other than 48K. This does not seem to be the case for other browsers. For this reason,\n        if the device type is anything other than playback, we'll leave the sample rate as-is and let the\n        browser pick the appropriate rate for us.\n        */\n        if (pConfig->deviceType == ma_device_type_playback) {\n            audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate;\n        } else {\n            audioContextAttributes.sampleRate = 0;\n        }\n\n        /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */\n        pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes);\n\n\n        /*\n        With the context created we can now create the worklet. We can only have a single worklet per audio\n        context which means we'll need to craft this appropriately to handle duplex devices correctly.\n        */\n\n        /*\n        We now need to create a worker thread. This is a bit weird because we need to allocate our\n        own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to\n        allocate this on the heap to keep it simple.\n        */\n        pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks);\n        if (pStackBuffer == NULL) {\n            emscripten_destroy_audio_context(pDevice->webaudio.audioContext);\n            return MA_OUT_OF_MEMORY;\n        }\n\n        /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */\n        pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks);\n        if (pInitParameters == NULL) {\n            ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks);\n            emscripten_destroy_audio_context(pDevice->webaudio.audioContext);\n            return MA_OUT_OF_MEMORY;\n        }\n\n        pInitParameters->pDevice = pDevice;\n        pInitParameters->pConfig = pConfig;\n        pInitParameters->pDescriptorPlayback = pDescriptorPlayback;\n        pInitParameters->pDescriptorCapture  = pDescriptorCapture;\n\n        /*\n        We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of\n        the Emscripten WebAudio stuff is asynchronous.\n        */\n        pDevice->webaudio.initResult = MA_BUSY;\n        {\n            emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters);\n        }\n        while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); }    /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */\n\n        /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */\n        if (pDevice->webaudio.initResult != MA_SUCCESS) {\n            ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks);\n            emscripten_destroy_audio_context(pDevice->webaudio.audioContext);\n            return pDevice->webaudio.initResult;\n        }\n\n        /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */\n        pDevice->webaudio.deviceIndex = EM_ASM_INT({\n            return miniaudio.track_device({\n                webaudio: emscriptenGetAudioObject($0),\n                state:    1 /* 1 = ma_device_state_stopped */\n            });\n        }, pDevice->webaudio.audioContext);\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */\n        ma_uint32 deviceIndex;\n        ma_uint32 channels;\n        ma_uint32 sampleRate;\n        ma_uint32 periodSizeInFrames;\n\n        /* The channel count will depend on the device type. If it's a capture, use it's, otherwise use the playback side. */\n        if (pConfig->deviceType == ma_device_type_capture) {\n            channels = (pDescriptorCapture->channels  > 0) ? pDescriptorCapture->channels  : MA_DEFAULT_CHANNELS;\n        } else {\n            channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS;\n        }\n\n        /*\n        When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's\n        native rate. For this reason we're leaving the sample rate untouched for capture devices.\n        */\n        if (pConfig->deviceType == ma_device_type_playback) {\n            sampleRate = pDescriptorPlayback->sampleRate;\n        } else {\n            sampleRate = 0; /* Let the browser decide when capturing. */\n        }\n\n        /* The period size needs to be a power of 2. */\n        if (pConfig->deviceType == ma_device_type_capture) {\n            periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile);\n        } else {\n            periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile);\n        }\n\n        /* We need an intermediary buffer for doing interleaving and deinterleaving. */\n        pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks);\n        if (pDevice->webaudio.pIntermediaryBuffer == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n\n        deviceIndex = EM_ASM_INT({\n            var deviceType = $0;\n            var channels   = $1;\n            var sampleRate = $2;\n            var bufferSize = $3;\n            var pIntermediaryBuffer = $4;\n            var pDevice    = $5;\n\n            if (typeof(window.miniaudio) === 'undefined') {\n                return -1;  /* Context not initialized. */\n            }\n\n            var device = {};\n\n            /* First thing we need is an AudioContext. */\n            var audioContextOptions = {};\n            if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) {\n                audioContextOptions.sampleRate = sampleRate;\n            }\n\n            device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions);\n            device.webaudio.suspend();  /* The AudioContext must be created in a suspended state. */\n            device.state = window.miniaudio.device_state.stopped;\n\n            /*\n            We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we\n            need to specify an output and configure the channel count there.\n            */\n            var channelCountIn  = 0;\n            var channelCountOut = channels;\n            if (deviceType != window.miniaudio.device_type.playback) {\n                channelCountIn  = channels;\n            }\n\n            device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut);\n\n            /* The node processing callback. */\n            device.scriptNode.onaudioprocess = function(e) {\n                if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) {\n                    device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels);\n                }\n\n                /* Do the capture side first. */\n                if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) {\n                    /* The data must be interleaved before being processed miniaudio. */\n                    for (var iChannel = 0; iChannel < channels; iChannel += 1) {\n                        var inputBuffer = e.inputBuffer.getChannelData(iChannel);\n                        var intermediaryBuffer = device.intermediaryBufferView;\n\n                        for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) {\n                            intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame];\n                        }\n                    }\n\n                    _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer);\n                }\n\n                if (deviceType == miniaudio.device_type.playback || deviceType == miniaudio.device_type.duplex) {\n                    _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer);\n\n                    for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {\n                        var outputBuffer = e.outputBuffer.getChannelData(iChannel);\n                        var intermediaryBuffer = device.intermediaryBufferView;\n\n                        for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) {\n                            outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel];\n                        }\n                    }\n                } else {\n                    /* It's a capture-only device. Make sure the output is silenced. */\n                    for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {\n                        e.outputBuffer.getChannelData(iChannel).fill(0.0);\n                    }\n                }\n            };\n\n            /* Now we need to connect our node to the graph. */\n            if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) {\n                navigator.mediaDevices.getUserMedia({audio:true, video:false})\n                    .then(function(stream) {\n                        device.streamNode = device.webaudio.createMediaStreamSource(stream);\n                        device.streamNode.connect(device.scriptNode);\n                        device.scriptNode.connect(device.webaudio.destination);\n                    })\n                    .catch(function(error) {\n                        console.log(\"Failed to get user media: \" + error);\n                    });\n            }\n\n            if (deviceType == miniaudio.device_type.playback) {\n                device.scriptNode.connect(device.webaudio.destination);\n            }\n\n            device.pDevice = pDevice;\n\n            return miniaudio.track_device(device);\n        }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice);\n\n        if (deviceIndex < 0) {\n            return MA_FAILED_TO_OPEN_BACKEND_DEVICE;\n        }\n\n        pDevice->webaudio.deviceIndex = deviceIndex;\n\n        /* Grab the sample rate from the audio context directly. */\n        sampleRate = (ma_uint32)EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);\n\n        if (pDescriptorCapture != NULL) {\n            pDescriptorCapture->format              = ma_format_f32;\n            pDescriptorCapture->channels            = channels;\n            pDescriptorCapture->sampleRate          = sampleRate;\n            ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);\n            pDescriptorCapture->periodSizeInFrames  = periodSizeInFrames;\n            pDescriptorCapture->periodCount         = 1;\n        }\n\n        if (pDescriptorPlayback != NULL) {\n            pDescriptorPlayback->format             = ma_format_f32;\n            pDescriptorPlayback->channels           = channels;\n            pDescriptorPlayback->sampleRate         = sampleRate;\n            ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);\n            pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;\n            pDescriptorPlayback->periodCount        = 1;\n        }\n\n        return MA_SUCCESS;\n    }\n    #endif\n}\n\nstatic ma_result ma_device_start__webaudio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    EM_ASM({\n        var device = miniaudio.get_device_by_index($0);\n        device.webaudio.resume();\n        device.state = miniaudio.device_state.started;\n    }, pDevice->webaudio.deviceIndex);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_device_stop__webaudio(ma_device* pDevice)\n{\n    MA_ASSERT(pDevice != NULL);\n\n    /*\n    From the WebAudio API documentation for AudioContext.suspend():\n\n        Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the\n        destination, and then allows the system to release its claim on audio hardware.\n\n    I read this to mean that \"any current context processing blocks\" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to\n    do any kind of explicit draining.\n    */\n    EM_ASM({\n        var device = miniaudio.get_device_by_index($0);\n        device.webaudio.suspend();\n        device.state = miniaudio.device_state.stopped;\n    }, pDevice->webaudio.deviceIndex);\n\n    ma_device__on_notification_stopped(pDevice);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_uninit__webaudio(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n    MA_ASSERT(pContext->backend == ma_backend_webaudio);\n\n    (void)pContext; /* Unused. */\n\n    /* Remove the global miniaudio object from window if there are no more references to it. */\n    EM_ASM({\n        if (typeof(window.miniaudio) !== 'undefined') {\n            window.miniaudio.referenceCount -= 1;\n            if (window.miniaudio.referenceCount === 0) {\n                delete window.miniaudio;\n            }\n        }\n    });\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)\n{\n    int resultFromJS;\n\n    MA_ASSERT(pContext != NULL);\n\n    (void)pConfig; /* Unused. */\n\n    /* Here is where our global JavaScript object is initialized. */\n    resultFromJS = EM_ASM_INT({\n        if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) {\n            return 0;   /* Web Audio not supported. */\n        }\n\n        if (typeof(window.miniaudio) === 'undefined') {\n            window.miniaudio = {\n                referenceCount: 0\n            };\n\n            /* Device types. */\n            window.miniaudio.device_type = {};\n            window.miniaudio.device_type.playback = $0;\n            window.miniaudio.device_type.capture  = $1;\n            window.miniaudio.device_type.duplex   = $2;\n\n            /* Device states. */\n            window.miniaudio.device_state = {};\n            window.miniaudio.device_state.stopped = $3;\n            window.miniaudio.device_state.started = $4;\n\n            /* Device cache for mapping devices to indexes for JavaScript/C interop. */\n            miniaudio.devices = [];\n\n            miniaudio.track_device = function(device) {\n                /* Try inserting into a free slot first. */\n                for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {\n                    if (miniaudio.devices[iDevice] == null) {\n                        miniaudio.devices[iDevice] = device;\n                        return iDevice;\n                    }\n                }\n\n                /* Getting here means there is no empty slots in the array so we just push to the end. */\n                miniaudio.devices.push(device);\n                return miniaudio.devices.length - 1;\n            };\n\n            miniaudio.untrack_device_by_index = function(deviceIndex) {\n                /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */\n                miniaudio.devices[deviceIndex] = null;\n\n                /* Trim the array if possible. */\n                while (miniaudio.devices.length > 0) {\n                    if (miniaudio.devices[miniaudio.devices.length-1] == null) {\n                        miniaudio.devices.pop();\n                    } else {\n                        break;\n                    }\n                }\n            };\n\n            miniaudio.untrack_device = function(device) {\n                for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {\n                    if (miniaudio.devices[iDevice] == device) {\n                        return miniaudio.untrack_device_by_index(iDevice);\n                    }\n                }\n            };\n\n            miniaudio.get_device_by_index = function(deviceIndex) {\n                return miniaudio.devices[deviceIndex];\n            };\n\n            miniaudio.unlock_event_types = (function(){\n                return ['touchend', 'click'];\n            })();\n\n            miniaudio.unlock = function() {\n                for(var i = 0; i < miniaudio.devices.length; ++i) {\n                    var device = miniaudio.devices[i];\n                    if (device != null &&\n                        device.webaudio != null &&\n                        device.state === window.miniaudio.device_state.started) {\n\n                        device.webaudio.resume().then(() => {\n                                Module._ma_device__on_notification_unlocked(device.pDevice);\n                            },\n                            (error) => {console.error(\"Failed to resume audiocontext\", error);\n                            });\n                    }\n                }\n                miniaudio.unlock_event_types.map(function(event_type) {\n                    document.removeEventListener(event_type, miniaudio.unlock, true);\n                });\n            };\n\n            miniaudio.unlock_event_types.map(function(event_type) {\n                document.addEventListener(event_type, miniaudio.unlock, true);\n            });\n        }\n\n        window.miniaudio.referenceCount += 1;\n\n        return 1;\n    }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started);\n\n    if (resultFromJS != 1) {\n        return MA_FAILED_TO_INIT_BACKEND;\n    }\n\n    pCallbacks->onContextInit             = ma_context_init__webaudio;\n    pCallbacks->onContextUninit           = ma_context_uninit__webaudio;\n    pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio;\n    pCallbacks->onContextGetDeviceInfo    = ma_context_get_device_info__webaudio;\n    pCallbacks->onDeviceInit              = ma_device_init__webaudio;\n    pCallbacks->onDeviceUninit            = ma_device_uninit__webaudio;\n    pCallbacks->onDeviceStart             = ma_device_start__webaudio;\n    pCallbacks->onDeviceStop              = ma_device_stop__webaudio;\n    pCallbacks->onDeviceRead              = NULL;   /* Not needed because WebAudio is asynchronous. */\n    pCallbacks->onDeviceWrite             = NULL;   /* Not needed because WebAudio is asynchronous. */\n    pCallbacks->onDeviceDataLoop          = NULL;   /* Not needed because WebAudio is asynchronous. */\n\n    return MA_SUCCESS;\n}\n#endif  /* Web Audio */\n\n\n\nstatic ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels)\n{\n    /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */\n    if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) {\n        ma_uint32 iChannel;\n\n        if (channels == 0 || channels > MA_MAX_CHANNELS) {\n            return MA_FALSE;   /* Channel count out of range. */\n        }\n\n        /* A channel cannot be present in the channel map more than once. */\n        for (iChannel = 0; iChannel < channels; ++iChannel) {\n            ma_uint32 jChannel;\n            for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {\n                if (pChannelMap[iChannel] == pChannelMap[jChannel]) {\n                    return MA_FALSE;\n                }\n            }\n        }\n    }\n\n    return MA_TRUE;\n}\n\n\nstatic ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)\n{\n    MA_ASSERT(pContext != NULL);\n\n    if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) {\n        if (pContext->callbacks.onDeviceDataLoop == NULL) {\n            return MA_TRUE;\n        } else {\n            return MA_FALSE;\n        }\n    } else {\n        return MA_FALSE;\n    }\n}\n\n\nstatic ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)\n{\n    ma_result result;\n\n    MA_ASSERT(pDevice != NULL);\n\n    if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {\n        if (pDevice->capture.format == ma_format_unknown) {\n            pDevice->capture.format = pDevice->capture.internalFormat;\n        }\n        if (pDevice->capture.channels == 0) {\n            pDevice->capture.channels = pDevice->capture.internalChannels;\n        }\n        if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) {\n            MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS);\n            if (pDevice->capture.internalChannels == pDevice->capture.channels) {\n                ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels);\n            } else {\n                if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) {\n                    ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels);\n                } else {\n                    ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels);\n                }\n            }\n        }\n    }\n\n    if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {\n        if (pDevice->playback.format == ma_format_unknown) {\n            pDevice->playback.format = pDevice->playback.internalFormat;\n        }\n        if (pDevice->playback.channels == 0) {\n            pDevice->playback.channels = pDevice->playback.internalChannels;\n        }\n        if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) {\n            MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS);\n            if (pDevice->playback.internalChannels == pDevice->playback.channels) {\n                ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels);\n            } else {\n                if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) {\n                    ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels);\n                } else {\n                    ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels);\n                }\n            }\n        }\n    }\n\n    if (pDevice->sampleRate == 0) {\n        if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {\n            pDevice->sampleRate = pDevice->capture.internalSampleRate;\n        } else {\n            pDevice->sampleRate = pDevice->playback.internalSampleRate;\n        }\n    }\n\n    /* Data converters. */\n    if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {\n        /* Converting from internal device format to client format. */\n        ma_data_converter_config converterConfig = ma_data_converter_config_init_default();\n        converterConfig.formatIn                        = pDevice->capture.internalFormat;\n        converterConfig.channelsIn                      = pDevice->capture.internalChannels;\n        converterConfig.sampleRateIn                    = pDevice->capture.internalSampleRate;\n        converterConfig.pChannelMapIn                   = pDevice->capture.internalChannelMap;\n        converterConfig.formatOut                       = pDevice->capture.format;\n        converterConfig.channelsOut                     = pDevice->capture.channels;\n        converterConfig.sampleRateOut                   = pDevice->sampleRate;\n        converterConfig.pChannelMapOut                  = pDevice->capture.channelMap;\n        converterConfig.channelMixMode                  = pDevice->capture.channelMixMode;\n        converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels;\n        converterConfig.allowDynamicSampleRate          = MA_FALSE;\n        converterConfig.resampling.algorithm            = pDevice->resampling.algorithm;\n        converterConfig.resampling.linear.lpfOrder      = pDevice->resampling.linear.lpfOrder;\n        converterConfig.resampling.pBackendVTable       = pDevice->resampling.pBackendVTable;\n        converterConfig.resampling.pBackendUserData     = pDevice->resampling.pBackendUserData;\n\n        /* Make sure the old converter is uninitialized first. */\n        if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) {\n            ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks);\n        }\n\n        result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {\n        /* Converting from client format to device format. */\n        ma_data_converter_config converterConfig = ma_data_converter_config_init_default();\n        converterConfig.formatIn                        = pDevice->playback.format;\n        converterConfig.channelsIn                      = pDevice->playback.channels;\n        converterConfig.sampleRateIn                    = pDevice->sampleRate;\n        converterConfig.pChannelMapIn                   = pDevice->playback.channelMap;\n        converterConfig.formatOut                       = pDevice->playback.internalFormat;\n        converterConfig.channelsOut                     = pDevice->playback.internalChannels;\n        converterConfig.sampleRateOut                   = pDevice->playback.internalSampleRate;\n        converterConfig.pChannelMapOut                  = pDevice->playback.internalChannelMap;\n        converterConfig.channelMixMode                  = pDevice->playback.channelMixMode;\n        converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels;\n        converterConfig.allowDynamicSampleRate          = MA_FALSE;\n        converterConfig.resampling.algorithm            = pDevice->resampling.algorithm;\n        converterConfig.resampling.linear.lpfOrder      = pDevice->resampling.linear.lpfOrder;\n        converterConfig.resampling.pBackendVTable       = pDevice->resampling.pBackendVTable;\n        converterConfig.resampling.pBackendUserData     = pDevice->resampling.pBackendUserData;\n\n        /* Make sure the old converter is uninitialized first. */\n        if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) {\n            ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks);\n        }\n\n        result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n\n    /*\n    If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's\n    a couple of situations where we'll need a heap allocated cache.\n\n    The first is a duplex device for backends that use a callback for data delivery. The reason\n    this is needed is that the input stage needs to have a buffer to place the input data while it\n    waits for the playback stage, after which the miniaudio data callback will get fired. This is\n    not needed for backends that use a blocking API because miniaudio manages temporary buffers on\n    the stack to achieve this.\n\n    The other situation is when the data converter does not have the ability to query the number\n    of input frames that are required in order to process a given number of output frames. When\n    performing data conversion, it's useful if miniaudio know exactly how many frames it needs\n    from the client in order to generate a given number of output frames. This way, only exactly\n    the number of frames are needed to be read from the client which means no cache is necessary.\n    On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read\n    in fixed sized chunks and then cache any residual unused input frames, those of which will be\n    processed at a later stage.\n    */\n    if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {\n        ma_uint64 unused;\n\n        pDevice->playback.inputCacheConsumed  = 0;\n        pDevice->playback.inputCacheRemaining = 0;\n\n        if (pDevice->type == ma_device_type_duplex ||                                                                       /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */\n            ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS)       /* Data conversion required input frame calculation not supported. */\n        {\n            /* We need a heap allocated cache. We want to size this based on the period size. */\n            void* pNewInputCache;\n            ma_uint64 newInputCacheCap;\n            ma_uint64 newInputCacheSizeInBytes;\n\n            newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames);\n\n            newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);\n            if (newInputCacheSizeInBytes > MA_SIZE_MAX) {\n                ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);\n                pDevice->playback.pInputCache   = NULL;\n                pDevice->playback.inputCacheCap = 0;\n                return MA_OUT_OF_MEMORY;    /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */\n            }\n\n            pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks);\n            if (pNewInputCache == NULL) {\n                ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);\n                pDevice->playback.pInputCache   = NULL;\n                pDevice->playback.inputCacheCap = 0;\n                return MA_OUT_OF_MEMORY;\n            }\n\n            pDevice->playback.pInputCache   = pNewInputCache;\n            pDevice->playback.inputCacheCap = newInputCacheCap;\n        } else {\n            /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */\n            ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);\n            pDevice->playback.pInputCache   = NULL;\n            pDevice->playback.inputCacheCap = 0;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture)\n{\n    ma_result result;\n\n    if (pDevice == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Capture. */\n    if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {\n        if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) {\n            return MA_INVALID_ARGS;\n        }\n\n        pDevice->capture.internalFormat             = pDescriptorCapture->format;\n        pDevice->capture.internalChannels           = pDescriptorCapture->channels;\n        pDevice->capture.internalSampleRate         = pDescriptorCapture->sampleRate;\n        MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));\n        pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;\n        pDevice->capture.internalPeriods            = pDescriptorCapture->periodCount;\n\n        if (pDevice->capture.internalPeriodSizeInFrames == 0) {\n            pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate);\n        }\n    }\n\n    /* Playback. */\n    if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {\n        if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) {\n            return MA_INVALID_ARGS;\n        }\n\n        pDevice->playback.internalFormat             = pDescriptorPlayback->format;\n        pDevice->playback.internalChannels           = pDescriptorPlayback->channels;\n        pDevice->playback.internalSampleRate         = pDescriptorPlayback->sampleRate;\n        MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));\n        pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;\n        pDevice->playback.internalPeriods            = pDescriptorPlayback->periodCount;\n\n        if (pDevice->playback.internalPeriodSizeInFrames == 0) {\n            pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate);\n        }\n    }\n\n    /*\n    The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead.\n    For loopback devices, we need to retrieve the name of the playback device.\n    */\n    {\n        ma_device_info deviceInfo;\n\n        if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {\n            result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo);\n            if (result == MA_SUCCESS) {\n                ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);\n            } else {\n                /* We failed to retrieve the device info. Fall back to a default name. */\n                if (pDescriptorCapture->pDeviceID == NULL) {\n                    ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n                } else {\n                    ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), \"Capture Device\", (size_t)-1);\n                }\n            }\n        }\n\n        if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {\n            result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);\n            if (result == MA_SUCCESS) {\n                ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);\n            } else {\n                /* We failed to retrieve the device info. Fall back to a default name. */\n                if (pDescriptorPlayback->pDeviceID == NULL) {\n                    ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n                } else {\n                    ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), \"Playback Device\", (size_t)-1);\n                }\n            }\n        }\n    }\n\n    /* Update data conversion. */\n    return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */\n}\n\n\nstatic ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)\n{\n    ma_device* pDevice = (ma_device*)pData;\n#ifdef MA_WIN32\n    HRESULT CoInitializeResult;\n#endif\n\n    MA_ASSERT(pDevice != NULL);\n\n#ifdef MA_WIN32\n    CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);\n#endif\n\n    /*\n    When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from\n    ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately\n    after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker\n    thread to signal an event to know when the worker thread is ready for action.\n    */\n    ma_device__set_state(pDevice, ma_device_state_stopped);\n    ma_event_signal(&pDevice->stopEvent);\n\n    for (;;) {  /* <-- This loop just keeps the thread alive. The main audio loop is inside. */\n        ma_result startResult;\n        ma_result stopResult;   /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */\n\n        /* We wait on an event to know when something has requested that the device be started and the main loop entered. */\n        ma_event_wait(&pDevice->wakeupEvent);\n\n        /* Default result code. */\n        pDevice->workResult = MA_SUCCESS;\n\n        /* If the reason for the wake up is that we are terminating, just break from the loop. */\n        if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {\n            break;\n        }\n\n        /*\n        Getting to this point means the device is wanting to get started. The function that has requested that the device\n        be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event\n        in both the success and error case. It's important that the state of the device is set _before_ signaling the event.\n        */\n        MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting);\n\n        /* If the device has a start callback, start it now. */\n        if (pDevice->pContext->callbacks.onDeviceStart != NULL) {\n            startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice);\n        } else {\n            startResult = MA_SUCCESS;\n        }\n\n        /*\n        If starting was not successful we'll need to loop back to the start and wait for something\n        to happen (pDevice->wakeupEvent).\n        */\n        if (startResult != MA_SUCCESS) {\n            pDevice->workResult = startResult;\n            ma_event_signal(&pDevice->startEvent);  /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */\n            continue;\n        }\n\n        /* Make sure the state is set appropriately. */\n        ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */\n        ma_event_signal(&pDevice->startEvent);\n\n        ma_device__on_notification_started(pDevice);\n\n        if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) {\n            pDevice->pContext->callbacks.onDeviceDataLoop(pDevice);\n        } else {\n            /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */\n            ma_device_audio_thread__default_read_write(pDevice);\n        }\n\n        /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */\n        if (pDevice->pContext->callbacks.onDeviceStop != NULL) {\n            stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice);\n        } else {\n            stopResult = MA_SUCCESS;    /* No stop callback with the backend. Just assume successful. */\n        }\n\n        /*\n        After the device has stopped, make sure an event is posted. Don't post a stopped event if\n        stopping failed. This can happen on some backends when the underlying stream has been\n        stopped due to the device being physically unplugged or disabled via an OS setting.\n        */\n        if (stopResult == MA_SUCCESS) {\n            ma_device__on_notification_stopped(pDevice);\n        }\n\n        /* If we stopped because the device has been uninitialized, abort now. */\n        if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {\n            break;\n        }\n\n        /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */\n        ma_device__set_state(pDevice, ma_device_state_stopped);\n        ma_event_signal(&pDevice->stopEvent);\n    }\n\n#ifdef MA_WIN32\n    if (CoInitializeResult == S_OK) {\n        ma_CoUninitialize(pDevice->pContext);\n    }\n#endif\n\n    return (ma_thread_result)0;\n}\n\n\n/* Helper for determining whether or not the given device is initialized. */\nstatic ma_bool32 ma_device__is_initialized(ma_device* pDevice)\n{\n    if (pDevice == NULL) {\n        return MA_FALSE;\n    }\n\n    return ma_device_get_state(pDevice) != ma_device_state_uninitialized;\n}\n\n\n#ifdef MA_WIN32\nstatic ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)\n{\n    /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    if (pContext->win32.CoInitializeResult == S_OK) {\n        ma_CoUninitialize(pContext);\n    }\n\n    #if defined(MA_WIN32_DESKTOP)\n        ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL);\n        ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL);\n    #endif\n\n    ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL);\n#else\n    (void)pContext;\n#endif\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init_backend_apis__win32(ma_context* pContext)\n{\n#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)\n    #if defined(MA_WIN32_DESKTOP)\n        /* User32.dll */\n        pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), \"user32.dll\");\n        if (pContext->win32.hUser32DLL == NULL) {\n            return MA_FAILED_TO_INIT_BACKEND;\n        }\n\n        pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, \"GetForegroundWindow\");\n        pContext->win32.GetDesktopWindow    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, \"GetDesktopWindow\");\n\n\n        /* Advapi32.dll */\n        pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), \"advapi32.dll\");\n        if (pContext->win32.hAdvapi32DLL == NULL) {\n            return MA_FAILED_TO_INIT_BACKEND;\n        }\n\n        pContext->win32.RegOpenKeyExA    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, \"RegOpenKeyExA\");\n        pContext->win32.RegCloseKey      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, \"RegCloseKey\");\n        pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, \"RegQueryValueExA\");\n    #endif\n\n    /* Ole32.dll */\n    pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), \"ole32.dll\");\n    if (pContext->win32.hOle32DLL == NULL) {\n        return MA_FAILED_TO_INIT_BACKEND;\n    }\n\n    pContext->win32.CoInitialize     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, \"CoInitialize\");\n    pContext->win32.CoInitializeEx   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, \"CoInitializeEx\");\n    pContext->win32.CoUninitialize   = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, \"CoUninitialize\");\n    pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, \"CoCreateInstance\");\n    pContext->win32.CoTaskMemFree    = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, \"CoTaskMemFree\");\n    pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, \"PropVariantClear\");\n    pContext->win32.StringFromGUID2  = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, \"StringFromGUID2\");\n#else\n    (void)pContext; /* Unused. */\n#endif\n\n    pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);\n    return MA_SUCCESS;\n}\n#else\nstatic ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)\n{\n    (void)pContext;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_context_init_backend_apis__nix(ma_context* pContext)\n{\n    (void)pContext;\n\n    return MA_SUCCESS;\n}\n#endif\n\nstatic ma_result ma_context_init_backend_apis(ma_context* pContext)\n{\n    ma_result result;\n#ifdef MA_WIN32\n    result = ma_context_init_backend_apis__win32(pContext);\n#else\n    result = ma_context_init_backend_apis__nix(pContext);\n#endif\n\n    return result;\n}\n\nstatic ma_result ma_context_uninit_backend_apis(ma_context* pContext)\n{\n    ma_result result;\n#ifdef MA_WIN32\n    result = ma_context_uninit_backend_apis__win32(pContext);\n#else\n    result = ma_context_uninit_backend_apis__nix(pContext);\n#endif\n\n    return result;\n}\n\n\n/* The default capacity doesn't need to be too big. */\n#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY\n#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY    32\n#endif\n\nMA_API ma_device_job_thread_config ma_device_job_thread_config_init(void)\n{\n    ma_device_job_thread_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.noThread         = MA_FALSE;\n    config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY;\n    config.jobQueueFlags    = 0;\n\n    return config;\n}\n\n\nstatic ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData)\n{\n    ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData;\n    MA_ASSERT(pJobThread != NULL);\n\n    for (;;) {\n        ma_result result;\n        ma_job job;\n\n        result = ma_device_job_thread_next(pJobThread, &job);\n        if (result != MA_SUCCESS) {\n            break;\n        }\n\n        if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {\n            break;\n        }\n\n        ma_job_process(&job);\n    }\n\n    return (ma_thread_result)0;\n}\n\nMA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread)\n{\n    ma_result result;\n    ma_job_queue_config jobQueueConfig;\n\n    if (pJobThread == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pJobThread);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n\n    /* Initialize the job queue before the thread to ensure it's in a valid state. */\n    jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity);\n\n    result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to initialize job queue. */\n    }\n\n\n    /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */\n    if (pConfig->noThread == MA_FALSE) {\n        result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks);\n        if (result != MA_SUCCESS) {\n            ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);\n            return result;  /* Failed to create the job thread. */\n        }\n\n        pJobThread->_hasThread = MA_TRUE;\n    } else {\n        pJobThread->_hasThread = MA_FALSE;\n    }\n\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pJobThread == NULL) {\n        return;\n    }\n\n    /* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */\n    {\n        ma_job job = ma_job_init(MA_JOB_TYPE_QUIT);\n        ma_device_job_thread_post(pJobThread, &job);\n    }\n\n    /* Wait for the thread to terminate naturally. */\n    if (pJobThread->_hasThread) {\n        ma_thread_wait(&pJobThread->thread);\n    }\n\n    /* At this point the thread should be terminated so we can safely uninitialize the job queue. */\n    ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);\n}\n\nMA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob)\n{\n    if (pJobThread == NULL || pJob == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_job_queue_post(&pJobThread->jobQueue, pJob);\n}\n\nMA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob)\n{\n    if (pJob == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pJob);\n\n    if (pJobThread == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_job_queue_next(&pJobThread->jobQueue, pJob);\n}\n\n\n\nMA_API ma_context_config ma_context_config_init(void)\n{\n    ma_context_config config;\n    MA_ZERO_OBJECT(&config);\n\n    return config;\n}\n\nMA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)\n{\n    ma_result result;\n    ma_context_config defaultConfig;\n    ma_backend defaultBackends[ma_backend_null+1];\n    ma_uint32 iBackend;\n    ma_backend* pBackendsToIterate;\n    ma_uint32 backendsToIterateCount;\n\n    if (pContext == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pContext);\n\n    /* Always make sure the config is set first to ensure properties are available as soon as possible. */\n    if (pConfig == NULL) {\n        defaultConfig = ma_context_config_init();\n        pConfig = &defaultConfig;\n    }\n\n    /* Allocation callbacks need to come first because they'll be passed around to other areas. */\n    result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* Get a lot set up first so we can start logging ASAP. */\n    if (pConfig->pLog != NULL) {\n        pContext->pLog = pConfig->pLog;\n    } else {\n        result = ma_log_init(&pContext->allocationCallbacks, &pContext->log);\n        if (result == MA_SUCCESS) {\n            pContext->pLog = &pContext->log;\n        } else {\n            pContext->pLog = NULL;  /* Logging is not available. */\n        }\n    }\n\n    pContext->threadPriority  = pConfig->threadPriority;\n    pContext->threadStackSize = pConfig->threadStackSize;\n    pContext->pUserData       = pConfig->pUserData;\n\n    /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */\n    result = ma_context_init_backend_apis(pContext);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {\n        defaultBackends[iBackend] = (ma_backend)iBackend;\n    }\n\n    pBackendsToIterate = (ma_backend*)backends;\n    backendsToIterateCount = backendCount;\n    if (pBackendsToIterate == NULL) {\n        pBackendsToIterate = (ma_backend*)defaultBackends;\n        backendsToIterateCount = ma_countof(defaultBackends);\n    }\n\n    MA_ASSERT(pBackendsToIterate != NULL);\n\n    for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) {\n        ma_backend backend = pBackendsToIterate[iBackend];\n\n        /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */\n        MA_ZERO_OBJECT(&pContext->callbacks);\n\n        /* These backends are using the new callback system. */\n        switch (backend) {\n        #ifdef MA_HAS_WASAPI\n            case ma_backend_wasapi:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__wasapi;\n            } break;\n        #endif\n        #ifdef MA_HAS_DSOUND\n            case ma_backend_dsound:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__dsound;\n            } break;\n        #endif\n        #ifdef MA_HAS_WINMM\n            case ma_backend_winmm:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__winmm;\n            } break;\n        #endif\n        #ifdef MA_HAS_COREAUDIO\n            case ma_backend_coreaudio:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__coreaudio;\n            } break;\n        #endif\n        #ifdef MA_HAS_SNDIO\n            case ma_backend_sndio:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__sndio;\n            } break;\n        #endif\n        #ifdef MA_HAS_AUDIO4\n            case ma_backend_audio4:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__audio4;\n            } break;\n        #endif\n        #ifdef MA_HAS_OSS\n            case ma_backend_oss:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__oss;\n            } break;\n        #endif\n        #ifdef MA_HAS_PULSEAUDIO\n            case ma_backend_pulseaudio:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__pulse;\n            } break;\n        #endif\n        #ifdef MA_HAS_ALSA\n            case ma_backend_alsa:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__alsa;\n            } break;\n        #endif\n        #ifdef MA_HAS_JACK\n            case ma_backend_jack:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__jack;\n            } break;\n        #endif\n        #ifdef MA_HAS_AAUDIO\n            case ma_backend_aaudio:\n            {\n                if (ma_is_backend_enabled(backend)) {\n                    pContext->callbacks.onContextInit = ma_context_init__aaudio;\n                }\n            } break;\n        #endif\n        #ifdef MA_HAS_OPENSL\n            case ma_backend_opensl:\n            {\n                if (ma_is_backend_enabled(backend)) {\n                    pContext->callbacks.onContextInit = ma_context_init__opensl;\n                }\n            } break;\n        #endif\n        #ifdef MA_HAS_WEBAUDIO\n            case ma_backend_webaudio:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__webaudio;\n            } break;\n        #endif\n        #ifdef MA_HAS_CUSTOM\n            case ma_backend_custom:\n            {\n                /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */\n                pContext->callbacks = pConfig->custom;\n            } break;\n        #endif\n        #ifdef MA_HAS_NULL\n            case ma_backend_null:\n            {\n                pContext->callbacks.onContextInit = ma_context_init__null;\n            } break;\n        #endif\n\n            default: break;\n        }\n\n        if (pContext->callbacks.onContextInit != NULL) {\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"Attempting to initialize %s backend...\\n\", ma_get_backend_name(backend));\n            result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks);\n        } else {\n            /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */\n            if (backend != ma_backend_custom) {\n                result = MA_BACKEND_NOT_ENABLED;\n            } else {\n            #if !defined(MA_HAS_CUSTOM)\n                result = MA_BACKEND_NOT_ENABLED;\n            #else\n                result = MA_NO_BACKEND;\n            #endif\n            }\n        }\n\n        /* If this iteration was successful, return. */\n        if (result == MA_SUCCESS) {\n            result = ma_mutex_init(&pContext->deviceEnumLock);\n            if (result != MA_SUCCESS) {\n                ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, \"Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\\n\");\n            }\n\n            result = ma_mutex_init(&pContext->deviceInfoLock);\n            if (result != MA_SUCCESS) {\n                ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, \"Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\\n\");\n            }\n\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"System Architecture:\\n\");\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"  Endian: %s\\n\", ma_is_little_endian() ? \"LE\"  : \"BE\");\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"  SSE2:   %s\\n\", ma_has_sse2()         ? \"YES\" : \"NO\");\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"  AVX2:   %s\\n\", ma_has_avx2()         ? \"YES\" : \"NO\");\n            ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"  NEON:   %s\\n\", ma_has_neon()         ? \"YES\" : \"NO\");\n\n            pContext->backend = backend;\n            return result;\n        } else {\n            if (result == MA_BACKEND_NOT_ENABLED) {\n                ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"%s backend is disabled.\\n\", ma_get_backend_name(backend));\n            } else {\n                ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, \"Failed to initialize %s backend.\\n\", ma_get_backend_name(backend));\n            }\n        }\n    }\n\n    /* If we get here it means an error occurred. */\n    MA_ZERO_OBJECT(pContext);  /* Safety. */\n    return MA_NO_BACKEND;\n}\n\nMA_API ma_result ma_context_uninit(ma_context* pContext)\n{\n    if (pContext == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pContext->callbacks.onContextUninit != NULL) {\n        pContext->callbacks.onContextUninit(pContext);\n    }\n\n    ma_mutex_uninit(&pContext->deviceEnumLock);\n    ma_mutex_uninit(&pContext->deviceInfoLock);\n    ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks);\n    ma_context_uninit_backend_apis(pContext);\n\n    if (pContext->pLog == &pContext->log) {\n        ma_log_uninit(&pContext->log);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API size_t ma_context_sizeof(void)\n{\n    return sizeof(ma_context);\n}\n\n\nMA_API ma_log* ma_context_get_log(ma_context* pContext)\n{\n    if (pContext == NULL) {\n        return NULL;\n    }\n\n    return pContext->pLog;\n}\n\n\nMA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)\n{\n    ma_result result;\n\n    if (pContext == NULL || callback == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pContext->callbacks.onContextEnumerateDevices == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    ma_mutex_lock(&pContext->deviceEnumLock);\n    {\n        result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData);\n    }\n    ma_mutex_unlock(&pContext->deviceEnumLock);\n\n    return result;\n}\n\n\nstatic ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)\n{\n    /*\n    We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device\n    it's just appended to the end. If it's a playback device it's inserted just before the first capture device.\n    */\n\n    /*\n    First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a\n    simple fixed size increment for buffer expansion.\n    */\n    const ma_uint32 bufferExpansionCount = 2;\n    const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;\n\n    if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) {\n        ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount;\n        ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks);\n        if (pNewInfos == NULL) {\n            return MA_FALSE;   /* Out of memory. */\n        }\n\n        pContext->pDeviceInfos = pNewInfos;\n        pContext->deviceInfoCapacity = newCapacity;\n    }\n\n    if (deviceType == ma_device_type_playback) {\n        /* Playback. Insert just before the first capture device. */\n\n        /* The first thing to do is move all of the capture devices down a slot. */\n        ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;\n        size_t iCaptureDevice;\n        for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {\n            pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];\n        }\n\n        /* Now just insert where the first capture device was before moving it down a slot. */\n        pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;\n        pContext->playbackDeviceInfoCount += 1;\n    } else {\n        /* Capture. Insert at the end. */\n        pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;\n        pContext->captureDeviceInfoCount += 1;\n    }\n\n    (void)pUserData;\n    return MA_TRUE;\n}\n\nMA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount)\n{\n    ma_result result;\n\n    /* Safety. */\n    if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;\n    if (pPlaybackDeviceCount  != NULL) *pPlaybackDeviceCount  = 0;\n    if (ppCaptureDeviceInfos  != NULL) *ppCaptureDeviceInfos  = NULL;\n    if (pCaptureDeviceCount   != NULL) *pCaptureDeviceCount   = 0;\n\n    if (pContext == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pContext->callbacks.onContextEnumerateDevices == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */\n    ma_mutex_lock(&pContext->deviceEnumLock);\n    {\n        /* Reset everything first. */\n        pContext->playbackDeviceInfoCount = 0;\n        pContext->captureDeviceInfoCount = 0;\n\n        /* Now enumerate over available devices. */\n        result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL);\n        if (result == MA_SUCCESS) {\n            /* Playback devices. */\n            if (ppPlaybackDeviceInfos != NULL) {\n                *ppPlaybackDeviceInfos = pContext->pDeviceInfos;\n            }\n            if (pPlaybackDeviceCount != NULL) {\n                *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;\n            }\n\n            /* Capture devices. */\n            if (ppCaptureDeviceInfos != NULL) {\n                *ppCaptureDeviceInfos = pContext->pDeviceInfos;\n                /* Capture devices come after playback devices. */\n                if (pContext->playbackDeviceInfoCount > 0) {\n                    /* Conditional, because NULL+0 is undefined behavior. */\n                    *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount;\n                }\n            }\n            if (pCaptureDeviceCount != NULL) {\n                *pCaptureDeviceCount = pContext->captureDeviceInfoCount;\n            }\n        }\n    }\n    ma_mutex_unlock(&pContext->deviceEnumLock);\n\n    return result;\n}\n\nMA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)\n{\n    ma_result result;\n    ma_device_info deviceInfo;\n\n    /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */\n    if (pContext == NULL || pDeviceInfo == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(&deviceInfo);\n\n    /* Help the backend out by copying over the device ID if we have one. */\n    if (pDeviceID != NULL) {\n        MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));\n    }\n\n    if (pContext->callbacks.onContextGetDeviceInfo == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    ma_mutex_lock(&pContext->deviceInfoLock);\n    {\n        result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo);\n    }\n    ma_mutex_unlock(&pContext->deviceInfoLock);\n\n    *pDeviceInfo = deviceInfo;\n    return result;\n}\n\nMA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext)\n{\n    if (pContext == NULL) {\n        return MA_FALSE;\n    }\n\n    return ma_is_loopback_supported(pContext->backend);\n}\n\n\nMA_API ma_device_config ma_device_config_init(ma_device_type deviceType)\n{\n    ma_device_config config;\n    MA_ZERO_OBJECT(&config);\n    config.deviceType = deviceType;\n    config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */\n\n    return config;\n}\n\nMA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)\n{\n    ma_result result;\n    ma_device_descriptor descriptorPlayback;\n    ma_device_descriptor descriptorCapture;\n\n    /* The context can be null, in which case we self-manage it. */\n    if (pContext == NULL) {\n        return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);\n    }\n\n    if (pDevice == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pDevice);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Check that we have our callbacks defined. */\n    if (pContext->callbacks.onDeviceInit == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* Basic config validation. */\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {\n        if (pConfig->capture.channels > MA_MAX_CHANNELS) {\n            return MA_INVALID_ARGS;\n        }\n\n        if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) {\n            return MA_INVALID_ARGS;\n        }\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {\n        if (pConfig->playback.channels > MA_MAX_CHANNELS) {\n            return MA_INVALID_ARGS;\n        }\n\n        if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) {\n            return MA_INVALID_ARGS;\n        }\n    }\n\n    pDevice->pContext = pContext;\n\n    /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */\n    pDevice->pUserData      = pConfig->pUserData;\n    pDevice->onData         = pConfig->dataCallback;\n    pDevice->onNotification = pConfig->notificationCallback;\n    pDevice->onStop         = pConfig->stopCallback;\n\n    if (pConfig->playback.pDeviceID != NULL) {\n        MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id));\n        pDevice->playback.pID = &pDevice->playback.id;\n    } else {\n        pDevice->playback.pID = NULL;\n    }\n\n    if (pConfig->capture.pDeviceID != NULL) {\n        MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id));\n        pDevice->capture.pID = &pDevice->capture.id;\n    } else {\n        pDevice->capture.pID = NULL;\n    }\n\n    pDevice->noPreSilencedOutputBuffer   = pConfig->noPreSilencedOutputBuffer;\n    pDevice->noClip                      = pConfig->noClip;\n    pDevice->noDisableDenormals          = pConfig->noDisableDenormals;\n    pDevice->noFixedSizedCallback        = pConfig->noFixedSizedCallback;\n    ma_atomic_float_set(&pDevice->masterVolumeFactor, 1);\n\n    pDevice->type                        = pConfig->deviceType;\n    pDevice->sampleRate                  = pConfig->sampleRate;\n    pDevice->resampling.algorithm        = pConfig->resampling.algorithm;\n    pDevice->resampling.linear.lpfOrder  = pConfig->resampling.linear.lpfOrder;\n    pDevice->resampling.pBackendVTable   = pConfig->resampling.pBackendVTable;\n    pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData;\n\n    pDevice->capture.shareMode           = pConfig->capture.shareMode;\n    pDevice->capture.format              = pConfig->capture.format;\n    pDevice->capture.channels            = pConfig->capture.channels;\n    ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);\n    pDevice->capture.channelMixMode      = pConfig->capture.channelMixMode;\n    pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels;\n\n    pDevice->playback.shareMode          = pConfig->playback.shareMode;\n    pDevice->playback.format             = pConfig->playback.format;\n    pDevice->playback.channels           = pConfig->playback.channels;\n    ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);\n    pDevice->playback.channelMixMode     = pConfig->playback.channelMixMode;\n    pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels;\n\n    result = ma_mutex_init(&pDevice->startStopLock);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /*\n    When the device is started, the worker thread is the one that does the actual startup of the backend device. We\n    use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.\n\n    Each of these semaphores is released internally by the worker thread when the work is completed. The start\n    semaphore is also used to wake up the worker thread.\n    */\n    result = ma_event_init(&pDevice->wakeupEvent);\n    if (result != MA_SUCCESS) {\n        ma_mutex_uninit(&pDevice->startStopLock);\n        return result;\n    }\n\n    result = ma_event_init(&pDevice->startEvent);\n    if (result != MA_SUCCESS) {\n        ma_event_uninit(&pDevice->wakeupEvent);\n        ma_mutex_uninit(&pDevice->startStopLock);\n        return result;\n    }\n\n    result = ma_event_init(&pDevice->stopEvent);\n    if (result != MA_SUCCESS) {\n        ma_event_uninit(&pDevice->startEvent);\n        ma_event_uninit(&pDevice->wakeupEvent);\n        ma_mutex_uninit(&pDevice->startStopLock);\n        return result;\n    }\n\n\n    MA_ZERO_OBJECT(&descriptorPlayback);\n    descriptorPlayback.pDeviceID                = pConfig->playback.pDeviceID;\n    descriptorPlayback.shareMode                = pConfig->playback.shareMode;\n    descriptorPlayback.format                   = pConfig->playback.format;\n    descriptorPlayback.channels                 = pConfig->playback.channels;\n    descriptorPlayback.sampleRate               = pConfig->sampleRate;\n    ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);\n    descriptorPlayback.periodSizeInFrames       = pConfig->periodSizeInFrames;\n    descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;\n    descriptorPlayback.periodCount              = pConfig->periods;\n\n    if (descriptorPlayback.periodCount == 0) {\n        descriptorPlayback.periodCount = MA_DEFAULT_PERIODS;\n    }\n\n\n    MA_ZERO_OBJECT(&descriptorCapture);\n    descriptorCapture.pDeviceID                 = pConfig->capture.pDeviceID;\n    descriptorCapture.shareMode                 = pConfig->capture.shareMode;\n    descriptorCapture.format                    = pConfig->capture.format;\n    descriptorCapture.channels                  = pConfig->capture.channels;\n    descriptorCapture.sampleRate                = pConfig->sampleRate;\n    ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);\n    descriptorCapture.periodSizeInFrames        = pConfig->periodSizeInFrames;\n    descriptorCapture.periodSizeInMilliseconds  = pConfig->periodSizeInMilliseconds;\n    descriptorCapture.periodCount               = pConfig->periods;\n\n    if (descriptorCapture.periodCount == 0) {\n        descriptorCapture.periodCount = MA_DEFAULT_PERIODS;\n    }\n\n\n    result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture);\n    if (result != MA_SUCCESS) {\n        ma_event_uninit(&pDevice->startEvent);\n        ma_event_uninit(&pDevice->wakeupEvent);\n        ma_mutex_uninit(&pDevice->startStopLock);\n        return result;\n    }\n\n#if 0\n    /*\n    On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between\n    the requested format and the internal format.\n    */\n    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {\n        if (!ma_device_descriptor_is_valid(&descriptorCapture)) {\n            ma_device_uninit(pDevice);\n            return MA_INVALID_ARGS;\n        }\n\n        pDevice->capture.internalFormat             = descriptorCapture.format;\n        pDevice->capture.internalChannels           = descriptorCapture.channels;\n        pDevice->capture.internalSampleRate         = descriptorCapture.sampleRate;\n        ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels);\n        pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames;\n        pDevice->capture.internalPeriods            = descriptorCapture.periodCount;\n\n        if (pDevice->capture.internalPeriodSizeInFrames == 0) {\n            pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate);\n        }\n    }\n\n    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n        if (!ma_device_descriptor_is_valid(&descriptorPlayback)) {\n            ma_device_uninit(pDevice);\n            return MA_INVALID_ARGS;\n        }\n\n        pDevice->playback.internalFormat             = descriptorPlayback.format;\n        pDevice->playback.internalChannels           = descriptorPlayback.channels;\n        pDevice->playback.internalSampleRate         = descriptorPlayback.sampleRate;\n        ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels);\n        pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames;\n        pDevice->playback.internalPeriods            = descriptorPlayback.periodCount;\n\n        if (pDevice->playback.internalPeriodSizeInFrames == 0) {\n            pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate);\n        }\n    }\n\n\n    /*\n    The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead.\n    For loopback devices, we need to retrieve the name of the playback device.\n    */\n    {\n        ma_device_info deviceInfo;\n\n        if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {\n            result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo);\n            if (result == MA_SUCCESS) {\n                ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);\n            } else {\n                /* We failed to retrieve the device info. Fall back to a default name. */\n                if (descriptorCapture.pDeviceID == NULL) {\n                    ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);\n                } else {\n                    ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), \"Capture Device\", (size_t)-1);\n                }\n            }\n        }\n\n        if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n            result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);\n            if (result == MA_SUCCESS) {\n                ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);\n            } else {\n                /* We failed to retrieve the device info. Fall back to a default name. */\n                if (descriptorPlayback.pDeviceID == NULL) {\n                    ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);\n                } else {\n                    ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), \"Playback Device\", (size_t)-1);\n                }\n            }\n        }\n    }\n\n\n    ma_device__post_init_setup(pDevice, pConfig->deviceType);\n#endif\n\n    result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture);\n    if (result != MA_SUCCESS) {\n        ma_device_uninit(pDevice);\n        return result;\n    }\n\n\n    /*\n    If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to\n    be done after post_init_setup() because we'll need access to the sample rate.\n    */\n    if (pConfig->noFixedSizedCallback == MA_FALSE) {\n        /* We're using a fixed sized data callback so we'll need an intermediary buffer. */\n        ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames;\n        if (intermediaryBufferCap == 0) {\n            intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate);\n        }\n\n        if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {\n            ma_uint32 intermediaryBufferSizeInBytes;\n\n            pDevice->capture.intermediaryBufferLen = 0;\n            pDevice->capture.intermediaryBufferCap = intermediaryBufferCap;\n            if (pDevice->capture.intermediaryBufferCap == 0) {\n                pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames;\n            }\n\n            intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);\n\n            pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);\n            if (pDevice->capture.pIntermediaryBuffer == NULL) {\n                ma_device_uninit(pDevice);\n                return MA_OUT_OF_MEMORY;\n            }\n\n            /* Silence the buffer for safety. */\n            ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels);\n            pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap;\n        }\n\n        if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {\n            ma_uint64 intermediaryBufferSizeInBytes;\n\n            pDevice->playback.intermediaryBufferLen = 0;\n            if (pConfig->deviceType == ma_device_type_duplex) {\n                pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap;   /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */\n            } else {\n                pDevice->playback.intermediaryBufferCap = intermediaryBufferCap;\n                if (pDevice->playback.intermediaryBufferCap == 0) {\n                    pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames;\n                }\n            }\n\n            intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);\n\n            pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);\n            if (pDevice->playback.pIntermediaryBuffer == NULL) {\n                ma_device_uninit(pDevice);\n                return MA_OUT_OF_MEMORY;\n            }\n\n            /* Silence the buffer for safety. */\n            ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels);\n            pDevice->playback.intermediaryBufferLen = 0;\n        }\n    } else {\n        /* Not using a fixed sized data callback so no need for an intermediary buffer. */\n    }\n\n\n    /* Some backends don't require the worker thread. */\n    if (!ma_context_is_backend_asynchronous(pContext)) {\n        /* The worker thread. */\n        result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks);\n        if (result != MA_SUCCESS) {\n            ma_device_uninit(pDevice);\n            return result;\n        }\n\n        /* Wait for the worker thread to put the device into it's stopped state for real. */\n        ma_event_wait(&pDevice->stopEvent);\n        MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);\n    } else {\n        /*\n        If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done\n        after ma_device__post_init_setup().\n        */\n        if (ma_context_is_backend_asynchronous(pContext)) {\n            if (pConfig->deviceType == ma_device_type_duplex) {\n                result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);\n                if (result != MA_SUCCESS) {\n                    ma_device_uninit(pDevice);\n                    return result;\n                }\n            }\n        }\n\n        ma_device__set_state(pDevice, ma_device_state_stopped);\n    }\n\n    /* Log device information. */\n    {\n        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"[%s]\\n\", ma_get_backend_name(pDevice->pContext->backend));\n        if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {\n            char name[MA_MAX_DEVICE_NAME_LENGTH + 1];\n            ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL);\n\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"  %s (%s)\\n\", name, \"Capture\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"    Format:      %s -> %s\\n\", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format));\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"    Channels:    %d -> %d\\n\", pDevice->capture.internalChannels, pDevice->capture.channels);\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"    Sample Rate: %d -> %d\\n\", pDevice->capture.internalSampleRate, pDevice->sampleRate);\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"    Buffer Size: %d*%d (%d)\\n\", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods));\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"    Conversion:\\n\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Pre Format Conversion:  %s\\n\", pDevice->capture.converter.hasPreFormatConversion  ? \"YES\" : \"NO\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Post Format Conversion: %s\\n\", pDevice->capture.converter.hasPostFormatConversion ? \"YES\" : \"NO\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Channel Routing:        %s\\n\", pDevice->capture.converter.hasChannelConverter     ? \"YES\" : \"NO\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Resampling:             %s\\n\", pDevice->capture.converter.hasResampler            ? \"YES\" : \"NO\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Passthrough:            %s\\n\", pDevice->capture.converter.isPassthrough           ? \"YES\" : \"NO\");\n            {\n                char channelMapStr[1024];\n                ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr));\n                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Channel Map In:         {%s}\\n\", channelMapStr);\n\n                ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr));\n                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Channel Map Out:        {%s}\\n\", channelMapStr);\n            }\n        }\n        if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n            char name[MA_MAX_DEVICE_NAME_LENGTH + 1];\n            ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL);\n\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"  %s (%s)\\n\", name, \"Playback\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"    Format:      %s -> %s\\n\", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat));\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"    Channels:    %d -> %d\\n\", pDevice->playback.channels, pDevice->playback.internalChannels);\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"    Sample Rate: %d -> %d\\n\", pDevice->sampleRate, pDevice->playback.internalSampleRate);\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"    Buffer Size: %d*%d (%d)\\n\", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods));\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"    Conversion:\\n\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Pre Format Conversion:  %s\\n\", pDevice->playback.converter.hasPreFormatConversion  ? \"YES\" : \"NO\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Post Format Conversion: %s\\n\", pDevice->playback.converter.hasPostFormatConversion ? \"YES\" : \"NO\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Channel Routing:        %s\\n\", pDevice->playback.converter.hasChannelConverter     ? \"YES\" : \"NO\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Resampling:             %s\\n\", pDevice->playback.converter.hasResampler            ? \"YES\" : \"NO\");\n            ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Passthrough:            %s\\n\", pDevice->playback.converter.isPassthrough           ? \"YES\" : \"NO\");\n            {\n                char channelMapStr[1024];\n                ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr));\n                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Channel Map In:         {%s}\\n\", channelMapStr);\n\n                ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr));\n                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, \"      Channel Map Out:        {%s}\\n\", channelMapStr);\n            }\n        }\n    }\n\n    MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice)\n{\n    ma_result result;\n    ma_context* pContext;\n    ma_backend defaultBackends[ma_backend_null+1];\n    ma_uint32 iBackend;\n    ma_backend* pBackendsToIterate;\n    ma_uint32 backendsToIterateCount;\n    ma_allocation_callbacks allocationCallbacks;\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pContextConfig != NULL) {\n        result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    } else {\n        allocationCallbacks = ma_allocation_callbacks_init_default();\n    }\n\n    pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks);\n    if (pContext == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {\n        defaultBackends[iBackend] = (ma_backend)iBackend;\n    }\n\n    pBackendsToIterate = (ma_backend*)backends;\n    backendsToIterateCount = backendCount;\n    if (pBackendsToIterate == NULL) {\n        pBackendsToIterate = (ma_backend*)defaultBackends;\n        backendsToIterateCount = ma_countof(defaultBackends);\n    }\n\n    result = MA_NO_BACKEND;\n\n    for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {\n        /*\n        This is a hack for iOS. If the context config is null, there's a good chance the\n        `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this\n        case, set the session category based on the device type.\n        */\n    #if defined(MA_APPLE_MOBILE)\n        ma_context_config contextConfig;\n\n        if (pContextConfig == NULL) {\n            contextConfig = ma_context_config_init();\n            switch (pConfig->deviceType) {\n                case ma_device_type_duplex: {\n                    contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record;\n                } break;\n                case ma_device_type_capture: {\n                    contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record;\n                } break;\n                case ma_device_type_playback:\n                default: {\n                    contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback;\n                } break;\n            }\n\n            pContextConfig = &contextConfig;\n        }\n    #endif\n\n        result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);\n        if (result == MA_SUCCESS) {\n            result = ma_device_init(pContext, pConfig, pDevice);\n            if (result == MA_SUCCESS) {\n                break;  /* Success. */\n            } else {\n                ma_context_uninit(pContext);   /* Failure. */\n            }\n        }\n    }\n\n    if (result != MA_SUCCESS) {\n        ma_free(pContext, &allocationCallbacks);\n        return result;\n    }\n\n    pDevice->isOwnerOfContext = MA_TRUE;\n    return result;\n}\n\nMA_API void ma_device_uninit(ma_device* pDevice)\n{\n    if (!ma_device__is_initialized(pDevice)) {\n        return;\n    }\n\n    /*\n    It's possible for the miniaudio side of the device and the backend to not be in sync due to\n    system-level situations such as the computer being put into sleep mode and the backend not\n    notifying miniaudio of the fact the device has stopped. It's possible for this to result in a\n    deadlock due to miniaudio thinking the device is in a running state, when in fact it's not\n    running at all. For this reason I am no longer explicitly stopping the device. I don't think\n    this should affect anyone in practice since uninitializing the backend will naturally stop the\n    device anyway.\n    */\n    #if 0\n    {\n        /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */\n        if (ma_device_is_started(pDevice)) {\n            ma_device_stop(pDevice);\n        }\n    }\n    #endif\n\n    /* Putting the device into an uninitialized state will make the worker thread return. */\n    ma_device__set_state(pDevice, ma_device_state_uninitialized);\n\n    /* Wake up the worker thread and wait for it to properly terminate. */\n    if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {\n        ma_event_signal(&pDevice->wakeupEvent);\n        ma_thread_wait(&pDevice->thread);\n    }\n\n    if (pDevice->pContext->callbacks.onDeviceUninit != NULL) {\n        pDevice->pContext->callbacks.onDeviceUninit(pDevice);\n    }\n\n\n    ma_event_uninit(&pDevice->stopEvent);\n    ma_event_uninit(&pDevice->startEvent);\n    ma_event_uninit(&pDevice->wakeupEvent);\n    ma_mutex_uninit(&pDevice->startStopLock);\n\n    if (ma_context_is_backend_asynchronous(pDevice->pContext)) {\n        if (pDevice->type == ma_device_type_duplex) {\n            ma_duplex_rb_uninit(&pDevice->duplexRB);\n        }\n    }\n\n    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {\n        ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks);\n    }\n    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {\n        ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks);\n    }\n\n    if (pDevice->playback.pInputCache != NULL) {\n        ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);\n    }\n\n    if (pDevice->capture.pIntermediaryBuffer != NULL) {\n        ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);\n    }\n    if (pDevice->playback.pIntermediaryBuffer != NULL) {\n        ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);\n    }\n\n    if (pDevice->isOwnerOfContext) {\n        ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks;\n\n        ma_context_uninit(pDevice->pContext);\n        ma_free(pDevice->pContext, &allocationCallbacks);\n    }\n\n    MA_ZERO_OBJECT(pDevice);\n}\n\nMA_API ma_context* ma_device_get_context(ma_device* pDevice)\n{\n    if (pDevice == NULL) {\n        return NULL;\n    }\n\n    return pDevice->pContext;\n}\n\nMA_API ma_log* ma_device_get_log(ma_device* pDevice)\n{\n    return ma_context_get_log(ma_device_get_context(pDevice));\n}\n\nMA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)\n{\n    if (pDeviceInfo == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pDeviceInfo);\n\n    if (pDevice == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */\n    if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) {\n        return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo);\n    }\n\n    /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */\n    if (type == ma_device_type_playback) {\n        return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo);\n    } else {\n        return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo);\n    }\n}\n\nMA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator)\n{\n    ma_result result;\n    ma_device_info deviceInfo;\n\n    if (pLengthNotIncludingNullTerminator != NULL) {\n        *pLengthNotIncludingNullTerminator = 0;\n    }\n\n    if (pName != NULL && nameCap > 0) {\n        pName[0] = '\\0';\n    }\n\n    result = ma_device_get_info(pDevice, type, &deviceInfo);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pName != NULL) {\n        ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1);\n\n        /*\n        For safety, make sure the length is based on the truncated output string rather than the\n        source. Otherwise the caller might assume the output buffer contains more content than it\n        actually does.\n        */\n        if (pLengthNotIncludingNullTerminator != NULL) {\n            *pLengthNotIncludingNullTerminator = strlen(pName);\n        }\n    } else {\n        /* Name not specified. Just report the length of the source string. */\n        if (pLengthNotIncludingNullTerminator != NULL) {\n            *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_device_start(ma_device* pDevice)\n{\n    ma_result result;\n\n    if (pDevice == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {\n        return MA_INVALID_OPERATION;    /* Not initialized. */\n    }\n\n    if (ma_device_get_state(pDevice) == ma_device_state_started) {\n        return MA_SUCCESS;  /* Already started. */\n    }\n\n    ma_mutex_lock(&pDevice->startStopLock);\n    {\n        /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */\n        MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);\n\n        ma_device__set_state(pDevice, ma_device_state_starting);\n\n        /* Asynchronous backends need to be handled differently. */\n        if (ma_context_is_backend_asynchronous(pDevice->pContext)) {\n            if (pDevice->pContext->callbacks.onDeviceStart != NULL) {\n                result = pDevice->pContext->callbacks.onDeviceStart(pDevice);\n            } else {\n                result = MA_INVALID_OPERATION;\n            }\n\n            if (result == MA_SUCCESS) {\n                ma_device__set_state(pDevice, ma_device_state_started);\n                ma_device__on_notification_started(pDevice);\n            }\n        } else {\n            /*\n            Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the\n            thread and then wait for the start event.\n            */\n            ma_event_signal(&pDevice->wakeupEvent);\n\n            /*\n            Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device\n            into the started state. Don't call ma_device__set_state() here.\n            */\n            ma_event_wait(&pDevice->startEvent);\n            result = pDevice->workResult;\n        }\n\n        /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */\n        if (result != MA_SUCCESS) {\n            ma_device__set_state(pDevice, ma_device_state_stopped);\n        }\n    }\n    ma_mutex_unlock(&pDevice->startStopLock);\n\n    return result;\n}\n\nMA_API ma_result ma_device_stop(ma_device* pDevice)\n{\n    ma_result result;\n\n    if (pDevice == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {\n        return MA_INVALID_OPERATION;    /* Not initialized. */\n    }\n\n    if (ma_device_get_state(pDevice) == ma_device_state_stopped) {\n        return MA_SUCCESS;  /* Already stopped. */\n    }\n\n    ma_mutex_lock(&pDevice->startStopLock);\n    {\n        /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */\n        MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started);\n\n        ma_device__set_state(pDevice, ma_device_state_stopping);\n\n        /* Asynchronous backends need to be handled differently. */\n        if (ma_context_is_backend_asynchronous(pDevice->pContext)) {\n            /* Asynchronous backends must have a stop operation. */\n            if (pDevice->pContext->callbacks.onDeviceStop != NULL) {\n                result = pDevice->pContext->callbacks.onDeviceStop(pDevice);\n            } else {\n                result = MA_INVALID_OPERATION;\n            }\n\n            ma_device__set_state(pDevice, ma_device_state_stopped);\n        } else {\n            /*\n            Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If\n            the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make\n            sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super\n            important though, so I'm asserting it here as well for extra safety in case we accidentally change something later.\n            */\n            MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started);\n\n            if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) {\n                pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice);\n            }\n\n            /*\n            We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be\n            the one who puts the device into the stopped state. Don't call ma_device__set_state() here.\n            */\n            ma_event_wait(&pDevice->stopEvent);\n            result = MA_SUCCESS;\n        }\n\n        /*\n        This is a safety measure to ensure the internal buffer has been cleared so any leftover\n        does not get played the next time the device starts. Ideally this should be drained by\n        the backend first.\n        */\n        pDevice->playback.intermediaryBufferLen = 0;\n        pDevice->playback.inputCacheConsumed    = 0;\n        pDevice->playback.inputCacheRemaining   = 0;\n    }\n    ma_mutex_unlock(&pDevice->startStopLock);\n\n    return result;\n}\n\nMA_API ma_bool32 ma_device_is_started(const ma_device* pDevice)\n{\n    return ma_device_get_state(pDevice) == ma_device_state_started;\n}\n\nMA_API ma_device_state ma_device_get_state(const ma_device* pDevice)\n{\n    if (pDevice == NULL) {\n        return ma_device_state_uninitialized;\n    }\n\n    return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state);   /* Naughty cast to get rid of a const warning. */\n}\n\nMA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume)\n{\n    if (pDevice == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (volume < 0.0f) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_atomic_float_set(&pDevice->masterVolumeFactor, volume);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)\n{\n    if (pVolume == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pDevice == NULL) {\n        *pVolume = 0;\n        return MA_INVALID_ARGS;\n    }\n\n    *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB)\n{\n    if (gainDB > 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB));\n}\n\nMA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB)\n{\n    float factor;\n    ma_result result;\n\n    if (pGainDB == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_device_get_master_volume(pDevice, &factor);\n    if (result != MA_SUCCESS) {\n        *pGainDB = 0;\n        return result;\n    }\n\n    *pGainDB = ma_volume_linear_to_db(factor);\n\n    return MA_SUCCESS;\n}\n\n\nMA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)\n{\n    if (pDevice == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pOutput == NULL && pInput == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pDevice->type == ma_device_type_duplex) {\n        if (pInput != NULL) {\n            ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb);\n        }\n\n        if (pOutput != NULL) {\n            ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb);\n        }\n    } else {\n        if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) {\n            if (pInput == NULL) {\n                return MA_INVALID_ARGS;\n            }\n\n            ma_device__send_frames_to_client(pDevice, frameCount, pInput);\n        }\n\n        if (pDevice->type == ma_device_type_playback) {\n            if (pOutput == NULL) {\n                return MA_INVALID_ARGS;\n            }\n\n            ma_device__read_frames_from_client(pDevice, frameCount, pOutput);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)\n{\n    if (pDescriptor == NULL) {\n        return 0;\n    }\n\n    /*\n    We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the\n    time when the size of the buffer needs to be determined. In this case we need to just take a best\n    guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll\n    just fall back to MA_DEFAULT_SAMPLE_RATE.\n    */\n    if (nativeSampleRate == 0) {\n        nativeSampleRate = pDescriptor->sampleRate;\n    }\n    if (nativeSampleRate == 0) {\n        nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;\n    }\n\n    MA_ASSERT(nativeSampleRate != 0);\n\n    if (pDescriptor->periodSizeInFrames == 0) {\n        if (pDescriptor->periodSizeInMilliseconds == 0) {\n            if (performanceProfile == ma_performance_profile_low_latency) {\n                return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate);\n            } else {\n                return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate);\n            }\n        } else {\n            return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);\n        }\n    } else {\n        return pDescriptor->periodSizeInFrames;\n    }\n}\n#endif  /* MA_NO_DEVICE_IO */\n\n\nMA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)\n{\n    /* Prevent a division by zero. */\n    if (sampleRate == 0) {\n        return 0;\n    }\n\n    return bufferSizeInFrames*1000 / sampleRate;\n}\n\nMA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)\n{\n    /* Prevent a division by zero. */\n    if (sampleRate == 0) {\n        return 0;\n    }\n\n    return bufferSizeInMilliseconds*sampleRate / 1000;\n}\n\nMA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels)\n{\n    if (dst == src) {\n        return; /* No-op. */\n    }\n\n    ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels));\n}\n\nMA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels)\n{\n    if (format == ma_format_u8) {\n        ma_uint64 sampleCount = frameCount * channels;\n        ma_uint64 iSample;\n        for (iSample = 0; iSample < sampleCount; iSample += 1) {\n            ((ma_uint8*)p)[iSample] = 128;\n        }\n    } else {\n        ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels));\n    }\n}\n\nMA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)\n{\n    return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));\n}\n\nMA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)\n{\n    return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));\n}\n\n\nMA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count)\n{\n    ma_uint64 iSample;\n\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    for (iSample = 0; iSample < count; iSample += 1) {\n        pDst[iSample] = ma_clip_u8(pSrc[iSample]);\n    }\n}\n\nMA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count)\n{\n    ma_uint64 iSample;\n\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    for (iSample = 0; iSample < count; iSample += 1) {\n        pDst[iSample] = ma_clip_s16(pSrc[iSample]);\n    }\n}\n\nMA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count)\n{\n    ma_uint64 iSample;\n\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    for (iSample = 0; iSample < count; iSample += 1) {\n        ma_int64 s = ma_clip_s24(pSrc[iSample]);\n        pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >>  0);\n        pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >>  8);\n        pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);\n    }\n}\n\nMA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count)\n{\n    ma_uint64 iSample;\n\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    for (iSample = 0; iSample < count; iSample += 1) {\n        pDst[iSample] = ma_clip_s32(pSrc[iSample]);\n    }\n}\n\nMA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count)\n{\n    ma_uint64 iSample;\n\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    for (iSample = 0; iSample < count; iSample += 1) {\n        pDst[iSample] = ma_clip_f32(pSrc[iSample]);\n    }\n}\n\nMA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels)\n{\n    ma_uint64 sampleCount;\n\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    sampleCount = frameCount * channels;\n\n    switch (format) {\n        case ma_format_u8:  ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break;\n        case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break;\n        case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break;\n        case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break;\n        case ma_format_f32: ma_clip_samples_f32((   float*)pDst, (const    float*)pSrc, sampleCount); break;\n\n        /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */\n        case ma_format_unknown:\n        case ma_format_count:\n            break;\n    }\n}\n\n\nMA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor)\n{\n    ma_uint64 iSample;\n\n    if (pSamplesOut == NULL || pSamplesIn == NULL) {\n        return;\n    }\n\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor);\n    }\n}\n\nMA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor)\n{\n    ma_uint64 iSample;\n\n    if (pSamplesOut == NULL || pSamplesIn == NULL) {\n        return;\n    }\n\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor);\n    }\n}\n\nMA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor)\n{\n    ma_uint64 iSample;\n    ma_uint8* pSamplesOut8;\n    ma_uint8* pSamplesIn8;\n\n    if (pSamplesOut == NULL || pSamplesIn == NULL) {\n        return;\n    }\n\n    pSamplesOut8 = (ma_uint8*)pSamplesOut;\n    pSamplesIn8  = (ma_uint8*)pSamplesIn;\n\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        ma_int32 sampleS32;\n\n        sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24);\n        sampleS32 = (ma_int32)(sampleS32 * factor);\n\n        pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >>  8);\n        pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16);\n        pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24);\n    }\n}\n\nMA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor)\n{\n    ma_uint64 iSample;\n\n    if (pSamplesOut == NULL || pSamplesIn == NULL) {\n        return;\n    }\n\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor);\n    }\n}\n\nMA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor)\n{\n    ma_uint64 iSample;\n\n    if (pSamplesOut == NULL || pSamplesIn == NULL) {\n        return;\n    }\n\n    if (factor == 1) {\n        if (pSamplesOut == pSamplesIn) {\n            /* In place. No-op. */\n        } else {\n            /* Just a copy. */\n            for (iSample = 0; iSample < sampleCount; iSample += 1) {\n                pSamplesOut[iSample] = pSamplesIn[iSample];\n            }\n        }\n    } else {\n        for (iSample = 0; iSample < sampleCount; iSample += 1) {\n            pSamplesOut[iSample] = pSamplesIn[iSample] * factor;\n        }\n    }\n}\n\nMA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor)\n{\n    ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);\n}\n\nMA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor)\n{\n    ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);\n}\n\nMA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor)\n{\n    ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);\n}\n\nMA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor)\n{\n    ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);\n}\n\nMA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor)\n{\n    ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);\n}\n\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)\n{\n    ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor);\n}\n\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)\n{\n    ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor);\n}\n\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)\n{\n    ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor);\n}\n\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)\n{\n    ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor);\n}\n\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)\n{\n    ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor);\n}\n\nMA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)\n{\n    switch (format)\n    {\n    case ma_format_u8:  ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return;\n    case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return;\n    case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24(           pFramesOut,                  pFramesIn, frameCount, channels, factor); return;\n    case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return;\n    case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32(   (float*)pFramesOut,    (const float*)pFramesIn, frameCount, channels, factor); return;\n    default: return;    /* Do nothing. */\n    }\n}\n\nMA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)\n{\n    ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor);\n}\n\nMA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)\n{\n    ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor);\n}\n\nMA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)\n{\n    ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor);\n}\n\nMA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)\n{\n    ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor);\n}\n\nMA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)\n{\n    ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor);\n}\n\nMA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)\n{\n    ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor);\n}\n\n\nMA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains)\n{\n    ma_uint64 iFrame;\n\n    if (channels == 2) {\n        /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */\n    }\n\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; iChannel += 1) {\n            pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel];\n        }\n    }\n}\n\n\n\nstatic MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume)\n{\n    return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8);\n}\n\nstatic MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume)\n{\n    return (ma_int32)((x * volume) >> 8);\n}\n\nstatic MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume)\n{\n    return (ma_int64)((x * volume) >> 8);\n}\n\nstatic MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume)\n{\n    return (ma_int64)((x * volume) >> 8);\n}\n\nstatic MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume)\n{\n    return x * volume;\n}\n\n\nMA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume)\n{\n    ma_uint64 iSample;\n    ma_int16  volumeFixed;\n\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    volumeFixed = ma_float_to_fixed_16(volume);\n\n    for (iSample = 0; iSample < count; iSample += 1) {\n        pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed));\n    }\n}\n\nMA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume)\n{\n    ma_uint64 iSample;\n    ma_int16  volumeFixed;\n\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    volumeFixed = ma_float_to_fixed_16(volume);\n\n    for (iSample = 0; iSample < count; iSample += 1) {\n        pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed));\n    }\n}\n\nMA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)\n{\n    ma_uint64 iSample;\n    ma_int16  volumeFixed;\n\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    volumeFixed = ma_float_to_fixed_16(volume);\n\n    for (iSample = 0; iSample < count; iSample += 1) {\n        ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed));\n        pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >>  0);\n        pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >>  8);\n        pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);\n    }\n}\n\nMA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)\n{\n    ma_uint64 iSample;\n    ma_int16  volumeFixed;\n\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    volumeFixed = ma_float_to_fixed_16(volume);\n\n    for (iSample = 0; iSample < count; iSample += 1) {\n        pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed));\n    }\n}\n\nMA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume)\n{\n    ma_uint64 iSample;\n\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */\n\n    for (iSample = 0; iSample < count; iSample += 1) {\n        pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume));\n    }\n}\n\nMA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume)\n{\n    MA_ASSERT(pDst != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    if (volume == 1) {\n        ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels);   /* Optimized case for volume = 1. */\n    } else if (volume == 0) {\n        ma_silence_pcm_frames(pDst, frameCount, format, channels);      /* Optimized case for volume = 0. */\n    } else {\n        ma_uint64 sampleCount = frameCount * channels;\n\n        switch (format) {\n            case ma_format_u8:  ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break;\n            case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break;\n            case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;\n            case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;\n            case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32((   float*)pDst, (const    float*)pSrc, sampleCount, volume); break;\n\n            /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */\n            case ma_format_unknown:\n            case ma_format_count:\n                break;\n        }\n    }\n}\n\n\n\nMA_API float ma_volume_linear_to_db(float factor)\n{\n    return 20*ma_log10f(factor);\n}\n\nMA_API float ma_volume_db_to_linear(float gain)\n{\n    return ma_powf(10, gain/20.0f);\n}\n\n\nMA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)\n{\n    ma_uint64 iSample;\n    ma_uint64 sampleCount;\n\n    if (pDst == NULL || pSrc == NULL || channels == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (volume == 0) {\n        return MA_SUCCESS;  /* No changes if the volume is 0. */\n    }\n\n    sampleCount = frameCount * channels;\n\n    if (volume == 1) {\n        for (iSample = 0; iSample < sampleCount; iSample += 1) {\n            pDst[iSample] += pSrc[iSample];\n        }\n    } else {\n        for (iSample = 0; iSample < sampleCount; iSample += 1) {\n            pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n\n\n/**************************************************************************************************************************************************************\n\nFormat Conversion\n\n**************************************************************************************************************************************************************/\n\nstatic MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x)\n{\n    return (ma_int16)(x * 32767.0f);\n}\n\nstatic MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x)\n{\n    return (ma_int16)((ma_int16)x - 128);\n}\n\nstatic MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x)\n{\n    return (ma_int64)(((ma_uint64)x[0] << 40) | ((ma_uint64)x[1] << 48) | ((ma_uint64)x[2] << 56)) >> 40;  /* Make sure the sign bits are maintained. */\n}\n\nstatic MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24)\n{\n    s24[0] = (ma_uint8)((x & 0x000000FF) >>  0);\n    s24[1] = (ma_uint8)((x & 0x0000FF00) >>  8);\n    s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16);\n}\n\n\n/* u8 */\nMA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    (void)ditherMode;\n    ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));\n}\n\n\nstatic MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_int16* dst_s16 = (ma_int16*)dst;\n    const ma_uint8* src_u8 = (const ma_uint8*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        ma_int16 x = src_u8[i];\n        x = (ma_int16)(x - 128);\n        x = (ma_int16)(x << 8);\n        dst_s16[i] = x;\n    }\n\n    (void)ditherMode;\n}\n\nstatic MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint8* dst_s24 = (ma_uint8*)dst;\n    const ma_uint8* src_u8 = (const ma_uint8*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        ma_int16 x = src_u8[i];\n        x = (ma_int16)(x - 128);\n\n        dst_s24[i*3+0] = 0;\n        dst_s24[i*3+1] = 0;\n        dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);\n    }\n\n    (void)ditherMode;\n}\n\nstatic MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_int32* dst_s32 = (ma_int32*)dst;\n    const ma_uint8* src_u8 = (const ma_uint8*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        ma_int32 x = src_u8[i];\n        x = x - 128;\n        x = x << 24;\n        dst_s32[i] = x;\n    }\n\n    (void)ditherMode;\n}\n\nstatic MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    float* dst_f32 = (float*)dst;\n    const ma_uint8* src_u8 = (const ma_uint8*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        float x = (float)src_u8[i];\n        x = x * 0.00784313725490196078f;    /* 0..255 to 0..2 */\n        x = x - 1;                          /* 0..2 to -1..1 */\n\n        dst_f32[i] = x;\n    }\n\n    (void)ditherMode;\n}\n\nstatic MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\nstatic MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_uint8* dst_u8 = (ma_uint8*)dst;\n    const ma_uint8** src_u8 = (const ma_uint8**)src;\n\n    ma_uint64 iFrame;\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; iChannel += 1) {\n            dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];\n        }\n    }\n}\n#else\nstatic MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_uint8* dst_u8 = (ma_uint8*)dst;\n    const ma_uint8** src_u8 = (const ma_uint8**)src;\n\n    if (channels == 1) {\n        ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));\n    } else if (channels == 2) {\n        ma_uint64 iFrame;\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];\n            dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];\n        }\n    } else {\n        ma_uint64 iFrame;\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            ma_uint32 iChannel;\n            for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];\n            }\n        }\n    }\n}\n#endif\n\nMA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);\n#else\n    ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_uint8** dst_u8 = (ma_uint8**)dst;\n    const ma_uint8* src_u8 = (const ma_uint8*)src;\n\n    ma_uint64 iFrame;\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; iChannel += 1) {\n            dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);\n}\n\nMA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);\n#else\n    ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);\n#endif\n}\n\n\n/* s16 */\nstatic MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint8* dst_u8 = (ma_uint8*)dst;\n    const ma_int16* src_s16 = (const ma_int16*)src;\n\n    if (ditherMode == ma_dither_mode_none) {\n        ma_uint64 i;\n        for (i = 0; i < count; i += 1) {\n            ma_int16 x = src_s16[i];\n            x = (ma_int16)(x >> 8);\n            x = (ma_int16)(x + 128);\n            dst_u8[i] = (ma_uint8)x;\n        }\n    } else {\n        ma_uint64 i;\n        for (i = 0; i < count; i += 1) {\n            ma_int16 x = src_s16[i];\n\n            /* Dither. Don't overflow. */\n            ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);\n            if ((x + dither) <= 0x7FFF) {\n                x = (ma_int16)(x + dither);\n            } else {\n                x = 0x7FFF;\n            }\n\n            x = (ma_int16)(x >> 8);\n            x = (ma_int16)(x + 128);\n            dst_u8[i] = (ma_uint8)x;\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nMA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    (void)ditherMode;\n    ma_copy_memory_64(dst, src, count * sizeof(ma_int16));\n}\n\n\nstatic MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint8* dst_s24 = (ma_uint8*)dst;\n    const ma_int16* src_s16 = (const ma_int16*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        dst_s24[i*3+0] = 0;\n        dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);\n        dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);\n    }\n\n    (void)ditherMode;\n}\n\nstatic MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_int32* dst_s32 = (ma_int32*)dst;\n    const ma_int16* src_s16 = (const ma_int16*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        dst_s32[i] = src_s16[i] << 16;\n    }\n\n    (void)ditherMode;\n}\n\nstatic MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    float* dst_f32 = (float*)dst;\n    const ma_int16* src_s16 = (const ma_int16*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        float x = (float)src_s16[i];\n\n#if 0\n        /* The accurate way. */\n        x = x + 32768.0f;                   /* -32768..32767 to 0..65535 */\n        x = x * 0.00003051804379339284f;    /* 0..65535 to 0..2 */\n        x = x - 1;                          /* 0..2 to -1..1 */\n#else\n        /* The fast way. */\n        x = x * 0.000030517578125f;         /* -32768..32767 to -1..0.999969482421875 */\n#endif\n\n        dst_f32[i] = x;\n    }\n\n    (void)ditherMode;\n}\n\nstatic MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_int16* dst_s16 = (ma_int16*)dst;\n    const ma_int16** src_s16 = (const ma_int16**)src;\n\n    ma_uint64 iFrame;\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; iChannel += 1) {\n            dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);\n}\n\nMA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);\n#else\n    ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_int16** dst_s16 = (ma_int16**)dst;\n    const ma_int16* src_s16 = (const ma_int16*)src;\n\n    ma_uint64 iFrame;\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; iChannel += 1) {\n            dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);\n}\n\nMA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);\n#else\n    ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);\n#endif\n}\n\n\n/* s24 */\nstatic MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint8* dst_u8 = (ma_uint8*)dst;\n    const ma_uint8* src_s24 = (const ma_uint8*)src;\n\n    if (ditherMode == ma_dither_mode_none) {\n        ma_uint64 i;\n        for (i = 0; i < count; i += 1) {\n            dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128);\n        }\n    } else {\n        ma_uint64 i;\n        for (i = 0; i < count; i += 1) {\n            ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24);\n\n            /* Dither. Don't overflow. */\n            ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);\n            if ((ma_int64)x + dither <= 0x7FFFFFFF) {\n                x = x + dither;\n            } else {\n                x = 0x7FFFFFFF;\n            }\n\n            x = x >> 24;\n            x = x + 128;\n            dst_u8[i] = (ma_uint8)x;\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_int16* dst_s16 = (ma_int16*)dst;\n    const ma_uint8* src_s24 = (const ma_uint8*)src;\n\n    if (ditherMode == ma_dither_mode_none) {\n        ma_uint64 i;\n        for (i = 0; i < count; i += 1) {\n            ma_uint16 dst_lo =            ((ma_uint16)src_s24[i*3 + 1]);\n            ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8);\n            dst_s16[i] = (ma_int16)(dst_lo | dst_hi);\n        }\n    } else {\n        ma_uint64 i;\n        for (i = 0; i < count; i += 1) {\n            ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24);\n\n            /* Dither. Don't overflow. */\n            ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);\n            if ((ma_int64)x + dither <= 0x7FFFFFFF) {\n                x = x + dither;\n            } else {\n                x = 0x7FFFFFFF;\n            }\n\n            x = x >> 16;\n            dst_s16[i] = (ma_int16)x;\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nMA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    (void)ditherMode;\n\n    ma_copy_memory_64(dst, src, count * 3);\n}\n\n\nstatic MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_int32* dst_s32 = (ma_int32*)dst;\n    const ma_uint8* src_s24 = (const ma_uint8*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24);\n    }\n\n    (void)ditherMode;\n}\n\nstatic MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    float* dst_f32 = (float*)dst;\n    const ma_uint8* src_s24 = (const ma_uint8*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8);\n\n#if 0\n        /* The accurate way. */\n        x = x + 8388608.0f;                 /* -8388608..8388607 to 0..16777215 */\n        x = x * 0.00000011920929665621f;    /* 0..16777215 to 0..2 */\n        x = x - 1;                          /* 0..2 to -1..1 */\n#else\n        /* The fast way. */\n        x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */\n#endif\n\n        dst_f32[i] = x;\n    }\n\n    (void)ditherMode;\n}\n\nstatic MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_uint8* dst8 = (ma_uint8*)dst;\n    const ma_uint8** src8 = (const ma_uint8**)src;\n\n    ma_uint64 iFrame;\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; iChannel += 1) {\n            dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];\n            dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];\n            dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);\n}\n\nMA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);\n#else\n    ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_uint8** dst8 = (ma_uint8**)dst;\n    const ma_uint8* src8 = (const ma_uint8*)src;\n\n    ma_uint32 iFrame;\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; iChannel += 1) {\n            dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];\n            dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];\n            dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);\n}\n\nMA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);\n#else\n    ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);\n#endif\n}\n\n\n\n/* s32 */\nstatic MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint8* dst_u8 = (ma_uint8*)dst;\n    const ma_int32* src_s32 = (const ma_int32*)src;\n\n    if (ditherMode == ma_dither_mode_none) {\n        ma_uint64 i;\n        for (i = 0; i < count; i += 1) {\n            ma_int32 x = src_s32[i];\n            x = x >> 24;\n            x = x + 128;\n            dst_u8[i] = (ma_uint8)x;\n        }\n    } else {\n        ma_uint64 i;\n        for (i = 0; i < count; i += 1) {\n            ma_int32 x = src_s32[i];\n\n            /* Dither. Don't overflow. */\n            ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);\n            if ((ma_int64)x + dither <= 0x7FFFFFFF) {\n                x = x + dither;\n            } else {\n                x = 0x7FFFFFFF;\n            }\n\n            x = x >> 24;\n            x = x + 128;\n            dst_u8[i] = (ma_uint8)x;\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_int16* dst_s16 = (ma_int16*)dst;\n    const ma_int32* src_s32 = (const ma_int32*)src;\n\n    if (ditherMode == ma_dither_mode_none) {\n        ma_uint64 i;\n        for (i = 0; i < count; i += 1) {\n            ma_int32 x = src_s32[i];\n            x = x >> 16;\n            dst_s16[i] = (ma_int16)x;\n        }\n    } else {\n        ma_uint64 i;\n        for (i = 0; i < count; i += 1) {\n            ma_int32 x = src_s32[i];\n\n            /* Dither. Don't overflow. */\n            ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);\n            if ((ma_int64)x + dither <= 0x7FFFFFFF) {\n                x = x + dither;\n            } else {\n                x = 0x7FFFFFFF;\n            }\n\n            x = x >> 16;\n            dst_s16[i] = (ma_int16)x;\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint8* dst_s24 = (ma_uint8*)dst;\n    const ma_int32* src_s32 = (const ma_int32*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        ma_uint32 x = (ma_uint32)src_s32[i];\n        dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >>  8);\n        dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);\n        dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);\n    }\n\n    (void)ditherMode;   /* No dithering for s32 -> s24. */\n}\n\nstatic MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nMA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    (void)ditherMode;\n\n    ma_copy_memory_64(dst, src, count * sizeof(ma_int32));\n}\n\n\nstatic MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    float* dst_f32 = (float*)dst;\n    const ma_int32* src_s32 = (const ma_int32*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        double x = src_s32[i];\n\n#if 0\n        x = x + 2147483648.0;\n        x = x * 0.0000000004656612873077392578125;\n        x = x - 1;\n#else\n        x = x / 2147483648.0;\n#endif\n\n        dst_f32[i] = (float)x;\n    }\n\n    (void)ditherMode;   /* No dithering for s32 -> f32. */\n}\n\nstatic MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_int32* dst_s32 = (ma_int32*)dst;\n    const ma_int32** src_s32 = (const ma_int32**)src;\n\n    ma_uint64 iFrame;\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; iChannel += 1) {\n            dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);\n}\n\nMA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);\n#else\n    ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_int32** dst_s32 = (ma_int32**)dst;\n    const ma_int32* src_s32 = (const ma_int32*)src;\n\n    ma_uint64 iFrame;\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; iChannel += 1) {\n            dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];\n        }\n    }\n}\n\nstatic MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);\n}\n\nMA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);\n#else\n    ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);\n#endif\n}\n\n\n/* f32 */\nstatic MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint64 i;\n\n    ma_uint8* dst_u8 = (ma_uint8*)dst;\n    const float* src_f32 = (const float*)src;\n\n    float ditherMin = 0;\n    float ditherMax = 0;\n    if (ditherMode != ma_dither_mode_none) {\n        ditherMin = 1.0f / -128;\n        ditherMax = 1.0f /  127;\n    }\n\n    for (i = 0; i < count; i += 1) {\n        float x = src_f32[i];\n        x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);\n        x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));    /* clip */\n        x = x + 1;                                  /* -1..1 to 0..2 */\n        x = x * 127.5f;                             /* 0..2 to 0..255 */\n\n        dst_u8[i] = (ma_uint8)x;\n    }\n}\n\nstatic MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\nstatic MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint64 i;\n\n    ma_int16* dst_s16 = (ma_int16*)dst;\n    const float* src_f32 = (const float*)src;\n\n    float ditherMin = 0;\n    float ditherMax = 0;\n    if (ditherMode != ma_dither_mode_none) {\n        ditherMin = 1.0f / -32768;\n        ditherMax = 1.0f /  32767;\n    }\n\n    for (i = 0; i < count; i += 1) {\n        float x = src_f32[i];\n        x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);\n        x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));    /* clip */\n\n#if 0\n        /* The accurate way. */\n        x = x + 1;                                  /* -1..1 to 0..2 */\n        x = x * 32767.5f;                           /* 0..2 to 0..65535 */\n        x = x - 32768.0f;                           /* 0...65535 to -32768..32767 */\n#else\n        /* The fast way. */\n        x = x * 32767.0f;                           /* -1..1 to -32767..32767 */\n#endif\n\n        dst_s16[i] = (ma_int16)x;\n    }\n}\n#else\nstatic MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint64 i;\n    ma_uint64 i4;\n    ma_uint64 count4;\n\n    ma_int16* dst_s16 = (ma_int16*)dst;\n    const float* src_f32 = (const float*)src;\n\n    float ditherMin = 0;\n    float ditherMax = 0;\n    if (ditherMode != ma_dither_mode_none) {\n        ditherMin = 1.0f / -32768;\n        ditherMax = 1.0f /  32767;\n    }\n\n    /* Unrolled. */\n    i = 0;\n    count4 = count >> 2;\n    for (i4 = 0; i4 < count4; i4 += 1) {\n        float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);\n        float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);\n        float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);\n        float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);\n\n        float x0 = src_f32[i+0];\n        float x1 = src_f32[i+1];\n        float x2 = src_f32[i+2];\n        float x3 = src_f32[i+3];\n\n        x0 = x0 + d0;\n        x1 = x1 + d1;\n        x2 = x2 + d2;\n        x3 = x3 + d3;\n\n        x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));\n        x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));\n        x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));\n        x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));\n\n        x0 = x0 * 32767.0f;\n        x1 = x1 * 32767.0f;\n        x2 = x2 * 32767.0f;\n        x3 = x3 * 32767.0f;\n\n        dst_s16[i+0] = (ma_int16)x0;\n        dst_s16[i+1] = (ma_int16)x1;\n        dst_s16[i+2] = (ma_int16)x2;\n        dst_s16[i+3] = (ma_int16)x3;\n\n        i += 4;\n    }\n\n    /* Leftover. */\n    for (; i < count; i += 1) {\n        float x = src_f32[i];\n        x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);\n        x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));    /* clip */\n        x = x * 32767.0f;                           /* -1..1 to -32767..32767 */\n\n        dst_s16[i] = (ma_int16)x;\n    }\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint64 i;\n    ma_uint64 i8;\n    ma_uint64 count8;\n    ma_int16* dst_s16;\n    const float* src_f32;\n    float ditherMin;\n    float ditherMax;\n\n    /* Both the input and output buffers need to be aligned to 16 bytes. */\n    if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {\n        ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);\n        return;\n    }\n\n    dst_s16 = (ma_int16*)dst;\n    src_f32 = (const float*)src;\n\n    ditherMin = 0;\n    ditherMax = 0;\n    if (ditherMode != ma_dither_mode_none) {\n        ditherMin = 1.0f / -32768;\n        ditherMax = 1.0f /  32767;\n    }\n\n    i = 0;\n\n    /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */\n    count8 = count >> 3;\n    for (i8 = 0; i8 < count8; i8 += 1) {\n        __m128 d0;\n        __m128 d1;\n        __m128 x0;\n        __m128 x1;\n\n        if (ditherMode == ma_dither_mode_none) {\n            d0 = _mm_set1_ps(0);\n            d1 = _mm_set1_ps(0);\n        } else if (ditherMode == ma_dither_mode_rectangle) {\n            d0 = _mm_set_ps(\n                ma_dither_f32_rectangle(ditherMin, ditherMax),\n                ma_dither_f32_rectangle(ditherMin, ditherMax),\n                ma_dither_f32_rectangle(ditherMin, ditherMax),\n                ma_dither_f32_rectangle(ditherMin, ditherMax)\n            );\n            d1 = _mm_set_ps(\n                ma_dither_f32_rectangle(ditherMin, ditherMax),\n                ma_dither_f32_rectangle(ditherMin, ditherMax),\n                ma_dither_f32_rectangle(ditherMin, ditherMax),\n                ma_dither_f32_rectangle(ditherMin, ditherMax)\n            );\n        } else {\n            d0 = _mm_set_ps(\n                ma_dither_f32_triangle(ditherMin, ditherMax),\n                ma_dither_f32_triangle(ditherMin, ditherMax),\n                ma_dither_f32_triangle(ditherMin, ditherMax),\n                ma_dither_f32_triangle(ditherMin, ditherMax)\n            );\n            d1 = _mm_set_ps(\n                ma_dither_f32_triangle(ditherMin, ditherMax),\n                ma_dither_f32_triangle(ditherMin, ditherMax),\n                ma_dither_f32_triangle(ditherMin, ditherMax),\n                ma_dither_f32_triangle(ditherMin, ditherMax)\n            );\n        }\n\n        x0 = *((__m128*)(src_f32 + i) + 0);\n        x1 = *((__m128*)(src_f32 + i) + 1);\n\n        x0 = _mm_add_ps(x0, d0);\n        x1 = _mm_add_ps(x1, d1);\n\n        x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));\n        x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));\n\n        _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));\n\n        i += 8;\n    }\n\n\n    /* Leftover. */\n    for (; i < count; i += 1) {\n        float x = src_f32[i];\n        x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);\n        x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));    /* clip */\n        x = x * 32767.0f;                           /* -1..1 to -32767..32767 */\n\n        dst_s16[i] = (ma_int16)x;\n    }\n}\n#endif  /* SSE2 */\n\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint64 i;\n    ma_uint64 i8;\n    ma_uint64 count8;\n    ma_int16* dst_s16;\n    const float* src_f32;\n    float ditherMin;\n    float ditherMax;\n\n    if (!ma_has_neon()) {\n        ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);\n        return;\n    }\n\n    /* Both the input and output buffers need to be aligned to 16 bytes. */\n    if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {\n        ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);\n        return;\n    }\n\n    dst_s16 = (ma_int16*)dst;\n    src_f32 = (const float*)src;\n\n    ditherMin = 0;\n    ditherMax = 0;\n    if (ditherMode != ma_dither_mode_none) {\n        ditherMin = 1.0f / -32768;\n        ditherMax = 1.0f /  32767;\n    }\n\n    i = 0;\n\n    /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */\n    count8 = count >> 3;\n    for (i8 = 0; i8 < count8; i8 += 1) {\n        float32x4_t d0;\n        float32x4_t d1;\n        float32x4_t x0;\n        float32x4_t x1;\n        int32x4_t i0;\n        int32x4_t i1;\n\n        if (ditherMode == ma_dither_mode_none) {\n            d0 = vmovq_n_f32(0);\n            d1 = vmovq_n_f32(0);\n        } else if (ditherMode == ma_dither_mode_rectangle) {\n            float d0v[4];\n            float d1v[4];\n\n            d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);\n            d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);\n            d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);\n            d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);\n            d0 = vld1q_f32(d0v);\n\n            d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);\n            d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);\n            d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);\n            d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);\n            d1 = vld1q_f32(d1v);\n        } else {\n            float d0v[4];\n            float d1v[4];\n\n            d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);\n            d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);\n            d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);\n            d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);\n            d0 = vld1q_f32(d0v);\n\n            d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);\n            d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);\n            d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);\n            d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);\n            d1 = vld1q_f32(d1v);\n        }\n\n        x0 = *((float32x4_t*)(src_f32 + i) + 0);\n        x1 = *((float32x4_t*)(src_f32 + i) + 1);\n\n        x0 = vaddq_f32(x0, d0);\n        x1 = vaddq_f32(x1, d1);\n\n        x0 = vmulq_n_f32(x0, 32767.0f);\n        x1 = vmulq_n_f32(x1, 32767.0f);\n\n        i0 = vcvtq_s32_f32(x0);\n        i1 = vcvtq_s32_f32(x1);\n        *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));\n\n        i += 8;\n    }\n\n\n    /* Leftover. */\n    for (; i < count; i += 1) {\n        float x = src_f32[i];\n        x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);\n        x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));    /* clip */\n        x = x * 32767.0f;                           /* -1..1 to -32767..32767 */\n\n        dst_s16[i] = (ma_int16)x;\n    }\n}\n#endif  /* Neon */\n#endif  /* MA_USE_REFERENCE_CONVERSION_APIS */\n\nMA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_uint8* dst_s24 = (ma_uint8*)dst;\n    const float* src_f32 = (const float*)src;\n\n    ma_uint64 i;\n    for (i = 0; i < count; i += 1) {\n        ma_int32 r;\n        float x = src_f32[i];\n        x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));    /* clip */\n\n#if 0\n        /* The accurate way. */\n        x = x + 1;                                  /* -1..1 to 0..2 */\n        x = x * 8388607.5f;                         /* 0..2 to 0..16777215 */\n        x = x - 8388608.0f;                         /* 0..16777215 to -8388608..8388607 */\n#else\n        /* The fast way. */\n        x = x * 8388607.0f;                         /* -1..1 to -8388607..8388607 */\n#endif\n\n        r = (ma_int32)x;\n        dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >>  0);\n        dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >>  8);\n        dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);\n    }\n\n    (void)ditherMode;   /* No dithering for f32 -> s24. */\n}\n\nstatic MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nstatic MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_int32* dst_s32 = (ma_int32*)dst;\n    const float* src_f32 = (const float*)src;\n\n    ma_uint32 i;\n    for (i = 0; i < count; i += 1) {\n        double x = src_f32[i];\n        x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));    /* clip */\n\n#if 0\n        /* The accurate way. */\n        x = x + 1;                                  /* -1..1 to 0..2 */\n        x = x * 2147483647.5;                       /* 0..2 to 0..4294967295 */\n        x = x - 2147483648.0;                       /* 0...4294967295 to -2147483648..2147483647 */\n#else\n        /* The fast way. */\n        x = x * 2147483647.0;                       /* -1..1 to -2147483647..2147483647 */\n#endif\n\n        dst_s32[i] = (ma_int32)x;\n    }\n\n    (void)ditherMode;   /* No dithering for f32 -> s32. */\n}\n\nstatic MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);\n}\n\n#if defined(MA_SUPPORT_SSE2)\nstatic MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);\n}\n#endif\n#if defined(MA_SUPPORT_NEON)\nstatic MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);\n}\n#endif\n\nMA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);\n#else\n    #  if defined(MA_SUPPORT_SSE2)\n        if (ma_has_sse2()) {\n            ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode);\n        } else\n    #elif defined(MA_SUPPORT_NEON)\n        if (ma_has_neon()) {\n            ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode);\n        } else\n    #endif\n        {\n            ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);\n        }\n#endif\n}\n\n\nMA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)\n{\n    (void)ditherMode;\n\n    ma_copy_memory_64(dst, src, count * sizeof(float));\n}\n\n\nstatic void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    float* dst_f32 = (float*)dst;\n    const float** src_f32 = (const float**)src;\n\n    ma_uint64 iFrame;\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; iChannel += 1) {\n            dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];\n        }\n    }\n}\n\nstatic void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);\n}\n\nMA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);\n#else\n    ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);\n#endif\n}\n\n\nstatic void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    float** dst_f32 = (float**)dst;\n    const float* src_f32 = (const float*)src;\n\n    ma_uint64 iFrame;\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; iChannel += 1) {\n            dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];\n        }\n    }\n}\n\nstatic void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n    ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);\n}\n\nMA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)\n{\n#ifdef MA_USE_REFERENCE_CONVERSION_APIS\n    ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);\n#else\n    ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);\n#endif\n}\n\n\nMA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)\n{\n    if (formatOut == formatIn) {\n        ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));\n        return;\n    }\n\n    switch (formatIn)\n    {\n        case ma_format_u8:\n        {\n            switch (formatOut)\n            {\n                case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;\n                default: break;\n            }\n        } break;\n\n        case ma_format_s16:\n        {\n            switch (formatOut)\n            {\n                case ma_format_u8:  ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;\n                default: break;\n            }\n        } break;\n\n        case ma_format_s24:\n        {\n            switch (formatOut)\n            {\n                case ma_format_u8:  ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;\n                default: break;\n            }\n        } break;\n\n        case ma_format_s32:\n        {\n            switch (formatOut)\n            {\n                case ma_format_u8:  ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;\n                default: break;\n            }\n        } break;\n\n        case ma_format_f32:\n        {\n            switch (formatOut)\n            {\n                case ma_format_u8:  ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;\n                case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;\n                default: break;\n            }\n        } break;\n\n        default: break;\n    }\n}\n\nMA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode)\n{\n    ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode);\n}\n\nMA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)\n{\n    if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {\n        return; /* Invalid args. */\n    }\n\n    /* For efficiency we do this per format. */\n    switch (format) {\n        case ma_format_s16:\n        {\n            const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;\n            ma_uint64 iPCMFrame;\n            for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {\n                ma_uint32 iChannel;\n                for (iChannel = 0; iChannel < channels; ++iChannel) {\n                    ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];\n                    pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];\n                }\n            }\n        } break;\n\n        case ma_format_f32:\n        {\n            const float* pSrcF32 = (const float*)pInterleavedPCMFrames;\n            ma_uint64 iPCMFrame;\n            for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {\n                ma_uint32 iChannel;\n                for (iChannel = 0; iChannel < channels; ++iChannel) {\n                    float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];\n                    pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];\n                }\n            }\n        } break;\n\n        default:\n        {\n            ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);\n            ma_uint64 iPCMFrame;\n            for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {\n                ma_uint32 iChannel;\n                for (iChannel = 0; iChannel < channels; ++iChannel) {\n                          void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);\n                    const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);\n                    memcpy(pDst, pSrc, sampleSizeInBytes);\n                }\n            }\n        } break;\n    }\n}\n\nMA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)\n{\n    switch (format)\n    {\n        case ma_format_s16:\n        {\n            ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;\n            ma_uint64 iPCMFrame;\n            for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {\n                ma_uint32 iChannel;\n                for (iChannel = 0; iChannel < channels; ++iChannel) {\n                    const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];\n                    pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];\n                }\n            }\n        } break;\n\n        case ma_format_f32:\n        {\n            float* pDstF32 = (float*)pInterleavedPCMFrames;\n            ma_uint64 iPCMFrame;\n            for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {\n                ma_uint32 iChannel;\n                for (iChannel = 0; iChannel < channels; ++iChannel) {\n                    const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];\n                    pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];\n                }\n            }\n        } break;\n\n        default:\n        {\n            ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);\n            ma_uint64 iPCMFrame;\n            for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {\n                ma_uint32 iChannel;\n                for (iChannel = 0; iChannel < channels; ++iChannel) {\n                          void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);\n                    const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);\n                    memcpy(pDst, pSrc, sampleSizeInBytes);\n                }\n            }\n        } break;\n    }\n}\n\n\n/**************************************************************************************************************************************************************\n\nBiquad Filter\n\n**************************************************************************************************************************************************************/\n#ifndef MA_BIQUAD_FIXED_POINT_SHIFT\n#define MA_BIQUAD_FIXED_POINT_SHIFT 14\n#endif\n\nstatic ma_int32 ma_biquad_float_to_fp(double x)\n{\n    return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT));\n}\n\nMA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2)\n{\n    ma_biquad_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format = format;\n    config.channels = channels;\n    config.b0 = b0;\n    config.b1 = b1;\n    config.b2 = b2;\n    config.a0 = a0;\n    config.a1 = a1;\n    config.a2 = a2;\n\n    return config;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t r1Offset;\n    size_t r2Offset;\n} ma_biquad_heap_layout;\n\nstatic ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout)\n{\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->channels == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* R0 */\n    pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;\n\n    /* R1 */\n    pHeapLayout->r2Offset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;\n\n    /* Make sure allocation size is aligned. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_biquad_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_biquad_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ)\n{\n    ma_result result;\n    ma_biquad_heap_layout heapLayout;\n\n    if (pBQ == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pBQ);\n\n    result = ma_biquad_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pBQ->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);\n    pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset);\n\n    return ma_biquad_reinit(pConfig, pBQ);\n}\n\nMA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pBQ->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pBQ == NULL) {\n        return;\n    }\n\n    if (pBQ->_ownsHeap) {\n        ma_free(pBQ->_pHeap, pAllocationCallbacks);\n    }\n}\n\nMA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ)\n{\n    if (pBQ == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->a0 == 0) {\n        return MA_INVALID_ARGS; /* Division by zero. */\n    }\n\n    /* Only supporting f32 and s16. */\n    if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The format cannot be changed after initialization. */\n    if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* The channel count cannot be changed after initialization. */\n    if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) {\n        return MA_INVALID_OPERATION;\n    }\n\n\n    pBQ->format   = pConfig->format;\n    pBQ->channels = pConfig->channels;\n\n    /* Normalize. */\n    if (pConfig->format == ma_format_f32) {\n        pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0);\n        pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0);\n        pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0);\n        pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0);\n        pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0);\n    } else {\n        pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0);\n        pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0);\n        pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0);\n        pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0);\n        pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ)\n{\n    if (pBQ == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pBQ->format == ma_format_f32) {\n        pBQ->pR1->f32 = 0;\n        pBQ->pR2->f32 = 0;\n    } else {\n        pBQ->pR1->s32 = 0;\n        pBQ->pR2->s32 = 0;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX)\n{\n    ma_uint32 c;\n    const ma_uint32 channels = pBQ->channels;\n    const float b0 = pBQ->b0.f32;\n    const float b1 = pBQ->b1.f32;\n    const float b2 = pBQ->b2.f32;\n    const float a1 = pBQ->a1.f32;\n    const float a2 = pBQ->a2.f32;\n\n    MA_ASSUME(channels > 0);\n    for (c = 0; c < channels; c += 1) {\n        float r1 = pBQ->pR1[c].f32;\n        float r2 = pBQ->pR2[c].f32;\n        float x  = pX[c];\n        float y;\n\n        y  = b0*x        + r1;\n        r1 = b1*x - a1*y + r2;\n        r2 = b2*x - a2*y;\n\n        pY[c]           = y;\n        pBQ->pR1[c].f32 = r1;\n        pBQ->pR2[c].f32 = r2;\n    }\n}\n\nstatic MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX)\n{\n    ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);\n}\n\nstatic MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)\n{\n    ma_uint32 c;\n    const ma_uint32 channels = pBQ->channels;\n    const ma_int32 b0 = pBQ->b0.s32;\n    const ma_int32 b1 = pBQ->b1.s32;\n    const ma_int32 b2 = pBQ->b2.s32;\n    const ma_int32 a1 = pBQ->a1.s32;\n    const ma_int32 a2 = pBQ->a2.s32;\n\n    MA_ASSUME(channels > 0);\n    for (c = 0; c < channels; c += 1) {\n        ma_int32 r1 = pBQ->pR1[c].s32;\n        ma_int32 r2 = pBQ->pR2[c].s32;\n        ma_int32 x  = pX[c];\n        ma_int32 y;\n\n        y  = (b0*x        + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;\n        r1 = (b1*x - a1*y + r2);\n        r2 = (b2*x - a2*y);\n\n        pY[c]           = (ma_int16)ma_clamp(y, -32768, 32767);\n        pBQ->pR1[c].s32 = r1;\n        pBQ->pR2[c].s32 = r2;\n    }\n}\n\nstatic MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)\n{\n    ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);\n}\n\nMA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    ma_uint32 n;\n\n    if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */\n\n    if (pBQ->format == ma_format_f32) {\n        /* */ float* pY = (      float*)pFramesOut;\n        const float* pX = (const float*)pFramesIn;\n\n        for (n = 0; n < frameCount; n += 1) {\n            ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);\n            pY += pBQ->channels;\n            pX += pBQ->channels;\n        }\n    } else if (pBQ->format == ma_format_s16) {\n        /* */ ma_int16* pY = (      ma_int16*)pFramesOut;\n        const ma_int16* pX = (const ma_int16*)pFramesIn;\n\n        for (n = 0; n < frameCount; n += 1) {\n            ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);\n            pY += pBQ->channels;\n            pX += pBQ->channels;\n        }\n    } else {\n        MA_ASSERT(MA_FALSE);\n        return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ)\n{\n    if (pBQ == NULL) {\n        return 0;\n    }\n\n    return 2;\n}\n\n\n/**************************************************************************************************************************************************************\n\nLow-Pass Filter\n\n**************************************************************************************************************************************************************/\nMA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)\n{\n    ma_lpf1_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format = format;\n    config.channels = channels;\n    config.sampleRate = sampleRate;\n    config.cutoffFrequency = cutoffFrequency;\n    config.q = 0.5;\n\n    return config;\n}\n\nMA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)\n{\n    ma_lpf2_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format = format;\n    config.channels = channels;\n    config.sampleRate = sampleRate;\n    config.cutoffFrequency = cutoffFrequency;\n    config.q = q;\n\n    /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */\n    if (config.q == 0) {\n        config.q = 0.707107;\n    }\n\n    return config;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t r1Offset;\n} ma_lpf1_heap_layout;\n\nstatic ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout)\n{\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->channels == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* R1 */\n    pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;\n\n    /* Make sure allocation size is aligned. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_lpf1_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF)\n{\n    ma_result result;\n    ma_lpf1_heap_layout heapLayout;\n\n    if (pLPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pLPF);\n\n    result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pLPF->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);\n\n    return ma_lpf1_reinit(pConfig, pLPF);\n}\n\nMA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pLPF->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pLPF == NULL) {\n        return;\n    }\n\n    if (pLPF->_ownsHeap) {\n        ma_free(pLPF->_pHeap, pAllocationCallbacks);\n    }\n}\n\nMA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF)\n{\n    double a;\n\n    if (pLPF == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Only supporting f32 and s16. */\n    if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The format cannot be changed after initialization. */\n    if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* The channel count cannot be changed after initialization. */\n    if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {\n        return MA_INVALID_OPERATION;\n    }\n\n    pLPF->format   = pConfig->format;\n    pLPF->channels = pConfig->channels;\n\n    a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);\n    if (pConfig->format == ma_format_f32) {\n        pLPF->a.f32 = (float)a;\n    } else {\n        pLPF->a.s32 = ma_biquad_float_to_fp(a);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF)\n{\n    if (pLPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pLPF->format == ma_format_f32) {\n        pLPF->a.f32 = 0;\n    } else {\n        pLPF->a.s32 = 0;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX)\n{\n    ma_uint32 c;\n    const ma_uint32 channels = pLPF->channels;\n    const float a = pLPF->a.f32;\n    const float b = 1 - a;\n\n    MA_ASSUME(channels > 0);\n    for (c = 0; c < channels; c += 1) {\n        float r1 = pLPF->pR1[c].f32;\n        float x  = pX[c];\n        float y;\n\n        y = b*x + a*r1;\n\n        pY[c]           = y;\n        pLPF->pR1[c].f32 = y;\n    }\n}\n\nstatic MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX)\n{\n    ma_uint32 c;\n    const ma_uint32 channels = pLPF->channels;\n    const ma_int32 a = pLPF->a.s32;\n    const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);\n\n    MA_ASSUME(channels > 0);\n    for (c = 0; c < channels; c += 1) {\n        ma_int32 r1 = pLPF->pR1[c].s32;\n        ma_int32 x  = pX[c];\n        ma_int32 y;\n\n        y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;\n\n        pY[c]            = (ma_int16)y;\n        pLPF->pR1[c].s32 = (ma_int32)y;\n    }\n}\n\nMA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    ma_uint32 n;\n\n    if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */\n\n    if (pLPF->format == ma_format_f32) {\n        /* */ float* pY = (      float*)pFramesOut;\n        const float* pX = (const float*)pFramesIn;\n\n        for (n = 0; n < frameCount; n += 1) {\n            ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX);\n            pY += pLPF->channels;\n            pX += pLPF->channels;\n        }\n    } else if (pLPF->format == ma_format_s16) {\n        /* */ ma_int16* pY = (      ma_int16*)pFramesOut;\n        const ma_int16* pX = (const ma_int16*)pFramesIn;\n\n        for (n = 0; n < frameCount; n += 1) {\n            ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX);\n            pY += pLPF->channels;\n            pX += pLPF->channels;\n        }\n    } else {\n        MA_ASSERT(MA_FALSE);\n        return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF)\n{\n    if (pLPF == NULL) {\n        return 0;\n    }\n\n    return 1;\n}\n\n\nstatic MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig)\n{\n    ma_biquad_config bqConfig;\n    double q;\n    double w;\n    double s;\n    double c;\n    double a;\n\n    MA_ASSERT(pConfig != NULL);\n\n    q = pConfig->q;\n    w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;\n    s = ma_sind(w);\n    c = ma_cosd(w);\n    a = s / (2*q);\n\n    bqConfig.b0 = (1 - c) / 2;\n    bqConfig.b1 =  1 - c;\n    bqConfig.b2 = (1 - c) / 2;\n    bqConfig.a0 =  1 + a;\n    bqConfig.a1 = -2 * c;\n    bqConfig.a2 =  1 - a;\n\n    bqConfig.format   = pConfig->format;\n    bqConfig.channels = pConfig->channels;\n\n    return bqConfig;\n}\n\nMA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_biquad_config bqConfig;\n    bqConfig = ma_lpf2__get_biquad_config(pConfig);\n\n    return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);\n}\n\nMA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pLPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pLPF);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_lpf2__get_biquad_config(pConfig);\n    result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pLPF->bq._ownsHeap = MA_TRUE;    /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */\n    return MA_SUCCESS;\n}\n\nMA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pLPF == NULL) {\n        return;\n    }\n\n    ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks);   /* <-- This will free the heap allocation. */\n}\n\nMA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pLPF == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_lpf2__get_biquad_config(pConfig);\n    result = ma_biquad_reinit(&bqConfig, &pLPF->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF)\n{\n    if (pLPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_biquad_clear_cache(&pLPF->bq);\n\n    return MA_SUCCESS;\n}\n\nstatic MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);\n}\n\nstatic MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn);\n}\n\nMA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    if (pLPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount);\n}\n\nMA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF)\n{\n    if (pLPF == NULL) {\n        return 0;\n    }\n\n    return ma_biquad_get_latency(&pLPF->bq);\n}\n\n\nMA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)\n{\n    ma_lpf_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format          = format;\n    config.channels        = channels;\n    config.sampleRate      = sampleRate;\n    config.cutoffFrequency = cutoffFrequency;\n    config.order           = ma_min(order, MA_MAX_FILTER_ORDER);\n\n    return config;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t lpf1Offset;\n    size_t lpf2Offset;  /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */\n} ma_lpf_heap_layout;\n\nstatic void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count)\n{\n    MA_ASSERT(pLPF1Count != NULL);\n    MA_ASSERT(pLPF2Count != NULL);\n\n    *pLPF1Count = order % 2;\n    *pLPF2Count = order / 2;\n}\n\nstatic ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout)\n{\n    ma_result result;\n    ma_uint32 lpf1Count;\n    ma_uint32 lpf2Count;\n    ma_uint32 ilpf1;\n    ma_uint32 ilpf2;\n\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->channels == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->order > MA_MAX_FILTER_ORDER) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* LPF 1 */\n    pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes;\n    for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {\n        size_t lpf1HeapSizeInBytes;\n        ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);\n\n        result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes;\n    }\n\n    /* LPF 2*/\n    pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes;\n    for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {\n        size_t lpf2HeapSizeInBytes;\n        ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107);   /* <-- The \"q\" parameter does not matter for the purpose of calculating the heap size. */\n\n        result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes;\n    }\n\n    /* Make sure allocation size is aligned. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew)\n{\n    ma_result result;\n    ma_uint32 lpf1Count;\n    ma_uint32 lpf2Count;\n    ma_uint32 ilpf1;\n    ma_uint32 ilpf2;\n    ma_lpf_heap_layout heapLayout;  /* Only used if isNew is true. */\n\n    if (pLPF == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Only supporting f32 and s16. */\n    if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The format cannot be changed after initialization. */\n    if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* The channel count cannot be changed after initialization. */\n    if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {\n        return MA_INVALID_OPERATION;\n    }\n\n    if (pConfig->order > MA_MAX_FILTER_ORDER) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);\n\n    /* The filter order can't change between reinits. */\n    if (!isNew) {\n        if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) {\n            return MA_INVALID_OPERATION;\n        }\n    }\n\n    if (isNew) {\n        result = ma_lpf_get_heap_layout(pConfig, &heapLayout);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pLPF->_pHeap = pHeap;\n        MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n        pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset);\n        pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset);\n    } else {\n        MA_ZERO_OBJECT(&heapLayout);    /* To silence a compiler warning. */\n    }\n\n    for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {\n        ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);\n\n        if (isNew) {\n            size_t lpf1HeapSizeInBytes;\n\n            result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);\n            if (result == MA_SUCCESS) {\n                result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]);\n            }\n        } else {\n            result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]);\n        }\n\n        if (result != MA_SUCCESS) {\n            ma_uint32 jlpf1;\n\n            for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) {\n                ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL);  /* No need for allocation callbacks here since we used a preallocated heap allocation. */\n            }\n\n            return result;\n        }\n    }\n\n    for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {\n        ma_lpf2_config lpf2Config;\n        double q;\n        double a;\n\n        /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */\n        if (lpf1Count == 1) {\n            a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1));   /* Odd order. */\n        } else {\n            a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2));   /* Even order. */\n        }\n        q = 1 / (2*ma_cosd(a));\n\n        lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);\n\n        if (isNew) {\n            size_t lpf2HeapSizeInBytes;\n\n            result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);\n            if (result == MA_SUCCESS) {\n                result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]);\n            }\n        } else {\n            result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]);\n        }\n\n        if (result != MA_SUCCESS) {\n            ma_uint32 jlpf1;\n            ma_uint32 jlpf2;\n\n            for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) {\n                ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL);  /* No need for allocation callbacks here since we used a preallocated heap allocation. */\n            }\n\n            for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) {\n                ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL);  /* No need for allocation callbacks here since we used a preallocated heap allocation. */\n            }\n\n            return result;\n        }\n    }\n\n    pLPF->lpf1Count  = lpf1Count;\n    pLPF->lpf2Count  = lpf2Count;\n    pLPF->format     = pConfig->format;\n    pLPF->channels   = pConfig->channels;\n    pLPF->sampleRate = pConfig->sampleRate;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_lpf_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_lpf_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return result;\n}\n\nMA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF)\n{\n    if (pLPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pLPF);\n\n    return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE);\n}\n\nMA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pLPF->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_uint32 ilpf1;\n    ma_uint32 ilpf2;\n\n    if (pLPF == NULL) {\n        return;\n    }\n\n    for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {\n        ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks);\n    }\n\n    for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {\n        ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks);\n    }\n\n    if (pLPF->_ownsHeap) {\n        ma_free(pLPF->_pHeap, pAllocationCallbacks);\n    }\n}\n\nMA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)\n{\n    return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE);\n}\n\nMA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF)\n{\n    ma_uint32 ilpf1;\n    ma_uint32 ilpf2;\n\n    if (pLPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {\n        ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]);\n    }\n\n    for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {\n        ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX)\n{\n    ma_uint32 ilpf1;\n    ma_uint32 ilpf2;\n\n    MA_ASSERT(pLPF->format == ma_format_f32);\n\n    MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));\n\n    for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {\n        ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY);\n    }\n\n    for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {\n        ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY);\n    }\n}\n\nstatic MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX)\n{\n    ma_uint32 ilpf1;\n    ma_uint32 ilpf2;\n\n    MA_ASSERT(pLPF->format == ma_format_s16);\n\n    MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));\n\n    for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {\n        ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY);\n    }\n\n    for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {\n        ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY);\n    }\n}\n\nMA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    ma_result result;\n    ma_uint32 ilpf1;\n    ma_uint32 ilpf2;\n\n    if (pLPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Faster path for in-place. */\n    if (pFramesOut == pFramesIn) {\n        for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {\n            result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount);\n            if (result != MA_SUCCESS) {\n                return result;\n            }\n        }\n\n        for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {\n            result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount);\n            if (result != MA_SUCCESS) {\n                return result;\n            }\n        }\n    }\n\n    /* Slightly slower path for copying. */\n    if (pFramesOut != pFramesIn) {\n        ma_uint32 iFrame;\n\n        /*  */ if (pLPF->format == ma_format_f32) {\n            /* */ float* pFramesOutF32 = (      float*)pFramesOut;\n            const float* pFramesInF32  = (const float*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32);\n                pFramesOutF32 += pLPF->channels;\n                pFramesInF32  += pLPF->channels;\n            }\n        } else if (pLPF->format == ma_format_s16) {\n            /* */ ma_int16* pFramesOutS16 = (      ma_int16*)pFramesOut;\n            const ma_int16* pFramesInS16  = (const ma_int16*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16);\n                pFramesOutS16 += pLPF->channels;\n                pFramesInS16  += pLPF->channels;\n            }\n        } else {\n            MA_ASSERT(MA_FALSE);\n            return MA_INVALID_OPERATION;    /* Should never hit this. */\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF)\n{\n    if (pLPF == NULL) {\n        return 0;\n    }\n\n    return pLPF->lpf2Count*2 + pLPF->lpf1Count;\n}\n\n\n/**************************************************************************************************************************************************************\n\nHigh-Pass Filtering\n\n**************************************************************************************************************************************************************/\nMA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)\n{\n    ma_hpf1_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format = format;\n    config.channels = channels;\n    config.sampleRate = sampleRate;\n    config.cutoffFrequency = cutoffFrequency;\n\n    return config;\n}\n\nMA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)\n{\n    ma_hpf2_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format = format;\n    config.channels = channels;\n    config.sampleRate = sampleRate;\n    config.cutoffFrequency = cutoffFrequency;\n    config.q = q;\n\n    /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */\n    if (config.q == 0) {\n        config.q = 0.707107;\n    }\n\n    return config;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t r1Offset;\n} ma_hpf1_heap_layout;\n\nstatic ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout)\n{\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->channels == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* R1 */\n    pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;\n\n    /* Make sure allocation size is aligned. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_hpf1_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF)\n{\n    ma_result result;\n    ma_hpf1_heap_layout heapLayout;\n\n    if (pLPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pLPF);\n\n    result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pLPF->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);\n\n    return ma_hpf1_reinit(pConfig, pLPF);\n}\n\nMA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pLPF->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pHPF == NULL) {\n        return;\n    }\n\n    if (pHPF->_ownsHeap) {\n        ma_free(pHPF->_pHeap, pAllocationCallbacks);\n    }\n}\n\nMA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF)\n{\n    double a;\n\n    if (pHPF == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Only supporting f32 and s16. */\n    if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The format cannot be changed after initialization. */\n    if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* The channel count cannot be changed after initialization. */\n    if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {\n        return MA_INVALID_OPERATION;\n    }\n\n    pHPF->format   = pConfig->format;\n    pHPF->channels = pConfig->channels;\n\n    a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);\n    if (pConfig->format == ma_format_f32) {\n        pHPF->a.f32 = (float)a;\n    } else {\n        pHPF->a.s32 = ma_biquad_float_to_fp(a);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX)\n{\n    ma_uint32 c;\n    const ma_uint32 channels = pHPF->channels;\n    const float a = 1 - pHPF->a.f32;\n    const float b = 1 - a;\n\n    MA_ASSUME(channels > 0);\n    for (c = 0; c < channels; c += 1) {\n        float r1 = pHPF->pR1[c].f32;\n        float x  = pX[c];\n        float y;\n\n        y = b*x - a*r1;\n\n        pY[c]            = y;\n        pHPF->pR1[c].f32 = y;\n    }\n}\n\nstatic MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX)\n{\n    ma_uint32 c;\n    const ma_uint32 channels = pHPF->channels;\n    const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32);\n    const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);\n\n    MA_ASSUME(channels > 0);\n    for (c = 0; c < channels; c += 1) {\n        ma_int32 r1 = pHPF->pR1[c].s32;\n        ma_int32 x  = pX[c];\n        ma_int32 y;\n\n        y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;\n\n        pY[c]            = (ma_int16)y;\n        pHPF->pR1[c].s32 = (ma_int32)y;\n    }\n}\n\nMA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    ma_uint32 n;\n\n    if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */\n\n    if (pHPF->format == ma_format_f32) {\n        /* */ float* pY = (      float*)pFramesOut;\n        const float* pX = (const float*)pFramesIn;\n\n        for (n = 0; n < frameCount; n += 1) {\n            ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX);\n            pY += pHPF->channels;\n            pX += pHPF->channels;\n        }\n    } else if (pHPF->format == ma_format_s16) {\n        /* */ ma_int16* pY = (      ma_int16*)pFramesOut;\n        const ma_int16* pX = (const ma_int16*)pFramesIn;\n\n        for (n = 0; n < frameCount; n += 1) {\n            ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX);\n            pY += pHPF->channels;\n            pX += pHPF->channels;\n        }\n    } else {\n        MA_ASSERT(MA_FALSE);\n        return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF)\n{\n    if (pHPF == NULL) {\n        return 0;\n    }\n\n    return 1;\n}\n\n\nstatic MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig)\n{\n    ma_biquad_config bqConfig;\n    double q;\n    double w;\n    double s;\n    double c;\n    double a;\n\n    MA_ASSERT(pConfig != NULL);\n\n    q = pConfig->q;\n    w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;\n    s = ma_sind(w);\n    c = ma_cosd(w);\n    a = s / (2*q);\n\n    bqConfig.b0 =  (1 + c) / 2;\n    bqConfig.b1 = -(1 + c);\n    bqConfig.b2 =  (1 + c) / 2;\n    bqConfig.a0 =   1 + a;\n    bqConfig.a1 =  -2 * c;\n    bqConfig.a2 =   1 - a;\n\n    bqConfig.format   = pConfig->format;\n    bqConfig.channels = pConfig->channels;\n\n    return bqConfig;\n}\n\nMA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_biquad_config bqConfig;\n    bqConfig = ma_hpf2__get_biquad_config(pConfig);\n\n    return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);\n}\n\nMA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pHPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pHPF);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_hpf2__get_biquad_config(pConfig);\n    result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pHPF->bq._ownsHeap = MA_TRUE;    /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */\n    return MA_SUCCESS;\n}\n\nMA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pHPF == NULL) {\n        return;\n    }\n\n    ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks);   /* <-- This will free the heap allocation. */\n}\n\nMA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pHPF == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_hpf2__get_biquad_config(pConfig);\n    result = ma_biquad_reinit(&bqConfig, &pHPF->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn);\n}\n\nstatic MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn);\n}\n\nMA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    if (pHPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount);\n}\n\nMA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF)\n{\n    if (pHPF == NULL) {\n        return 0;\n    }\n\n    return ma_biquad_get_latency(&pHPF->bq);\n}\n\n\nMA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)\n{\n    ma_hpf_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format          = format;\n    config.channels        = channels;\n    config.sampleRate      = sampleRate;\n    config.cutoffFrequency = cutoffFrequency;\n    config.order           = ma_min(order, MA_MAX_FILTER_ORDER);\n\n    return config;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t hpf1Offset;\n    size_t hpf2Offset;  /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */\n} ma_hpf_heap_layout;\n\nstatic void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count)\n{\n    MA_ASSERT(pHPF1Count != NULL);\n    MA_ASSERT(pHPF2Count != NULL);\n\n    *pHPF1Count = order % 2;\n    *pHPF2Count = order / 2;\n}\n\nstatic ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout)\n{\n    ma_result result;\n    ma_uint32 hpf1Count;\n    ma_uint32 hpf2Count;\n    ma_uint32 ihpf1;\n    ma_uint32 ihpf2;\n\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->channels == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->order > MA_MAX_FILTER_ORDER) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* HPF 1 */\n    pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes;\n    for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {\n        size_t hpf1HeapSizeInBytes;\n        ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);\n\n        result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes;\n    }\n\n    /* HPF 2*/\n    pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes;\n    for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {\n        size_t hpf2HeapSizeInBytes;\n        ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107);   /* <-- The \"q\" parameter does not matter for the purpose of calculating the heap size. */\n\n        result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes;\n    }\n\n    /* Make sure allocation size is aligned. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew)\n{\n    ma_result result;\n    ma_uint32 hpf1Count;\n    ma_uint32 hpf2Count;\n    ma_uint32 ihpf1;\n    ma_uint32 ihpf2;\n    ma_hpf_heap_layout heapLayout;  /* Only used if isNew is true. */\n\n    if (pHPF == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Only supporting f32 and s16. */\n    if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The format cannot be changed after initialization. */\n    if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* The channel count cannot be changed after initialization. */\n    if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {\n        return MA_INVALID_OPERATION;\n    }\n\n    if (pConfig->order > MA_MAX_FILTER_ORDER) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);\n\n    /* The filter order can't change between reinits. */\n    if (!isNew) {\n        if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) {\n            return MA_INVALID_OPERATION;\n        }\n    }\n\n    if (isNew) {\n        result = ma_hpf_get_heap_layout(pConfig, &heapLayout);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHPF->_pHeap = pHeap;\n        MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n        pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset);\n        pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset);\n    } else {\n        MA_ZERO_OBJECT(&heapLayout);    /* To silence a compiler warning. */\n    }\n\n    for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {\n        ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);\n\n        if (isNew) {\n            size_t hpf1HeapSizeInBytes;\n\n            result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);\n            if (result == MA_SUCCESS) {\n                result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]);\n            }\n        } else {\n            result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]);\n        }\n\n        if (result != MA_SUCCESS) {\n            ma_uint32 jhpf1;\n\n            for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) {\n                ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL);  /* No need for allocation callbacks here since we used a preallocated heap allocation. */\n            }\n\n            return result;\n        }\n    }\n\n    for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {\n        ma_hpf2_config hpf2Config;\n        double q;\n        double a;\n\n        /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */\n        if (hpf1Count == 1) {\n            a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1));   /* Odd order. */\n        } else {\n            a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2));   /* Even order. */\n        }\n        q = 1 / (2*ma_cosd(a));\n\n        hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);\n\n        if (isNew) {\n            size_t hpf2HeapSizeInBytes;\n\n            result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);\n            if (result == MA_SUCCESS) {\n                result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]);\n            }\n        } else {\n            result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]);\n        }\n\n        if (result != MA_SUCCESS) {\n            ma_uint32 jhpf1;\n            ma_uint32 jhpf2;\n\n            for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) {\n                ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL);  /* No need for allocation callbacks here since we used a preallocated heap allocation. */\n            }\n\n            for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) {\n                ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL);  /* No need for allocation callbacks here since we used a preallocated heap allocation. */\n            }\n\n            return result;\n        }\n    }\n\n    pHPF->hpf1Count  = hpf1Count;\n    pHPF->hpf2Count  = hpf2Count;\n    pHPF->format     = pConfig->format;\n    pHPF->channels   = pConfig->channels;\n    pHPF->sampleRate = pConfig->sampleRate;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_hpf_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_hpf_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return result;\n}\n\nMA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF)\n{\n    if (pLPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pLPF);\n\n    return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE);\n}\n\nMA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pHPF->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_uint32 ihpf1;\n    ma_uint32 ihpf2;\n\n    if (pHPF == NULL) {\n        return;\n    }\n\n    for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {\n        ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks);\n    }\n\n    for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {\n        ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks);\n    }\n\n    if (pHPF->_ownsHeap) {\n        ma_free(pHPF->_pHeap, pAllocationCallbacks);\n    }\n}\n\nMA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF)\n{\n    return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE);\n}\n\nMA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    ma_result result;\n    ma_uint32 ihpf1;\n    ma_uint32 ihpf2;\n\n    if (pHPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Faster path for in-place. */\n    if (pFramesOut == pFramesIn) {\n        for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {\n            result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount);\n            if (result != MA_SUCCESS) {\n                return result;\n            }\n        }\n\n        for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {\n            result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount);\n            if (result != MA_SUCCESS) {\n                return result;\n            }\n        }\n    }\n\n    /* Slightly slower path for copying. */\n    if (pFramesOut != pFramesIn) {\n        ma_uint32 iFrame;\n\n        /*  */ if (pHPF->format == ma_format_f32) {\n            /* */ float* pFramesOutF32 = (      float*)pFramesOut;\n            const float* pFramesInF32  = (const float*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));\n\n                for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {\n                    ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32);\n                }\n\n                for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {\n                    ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32);\n                }\n\n                pFramesOutF32 += pHPF->channels;\n                pFramesInF32  += pHPF->channels;\n            }\n        } else if (pHPF->format == ma_format_s16) {\n            /* */ ma_int16* pFramesOutS16 = (      ma_int16*)pFramesOut;\n            const ma_int16* pFramesInS16  = (const ma_int16*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));\n\n                for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {\n                    ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16);\n                }\n\n                for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {\n                    ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16);\n                }\n\n                pFramesOutS16 += pHPF->channels;\n                pFramesInS16  += pHPF->channels;\n            }\n        } else {\n            MA_ASSERT(MA_FALSE);\n            return MA_INVALID_OPERATION;    /* Should never hit this. */\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF)\n{\n    if (pHPF == NULL) {\n        return 0;\n    }\n\n    return pHPF->hpf2Count*2 + pHPF->hpf1Count;\n}\n\n\n/**************************************************************************************************************************************************************\n\nBand-Pass Filtering\n\n**************************************************************************************************************************************************************/\nMA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)\n{\n    ma_bpf2_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format = format;\n    config.channels = channels;\n    config.sampleRate = sampleRate;\n    config.cutoffFrequency = cutoffFrequency;\n    config.q = q;\n\n    /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */\n    if (config.q == 0) {\n        config.q = 0.707107;\n    }\n\n    return config;\n}\n\n\nstatic MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig)\n{\n    ma_biquad_config bqConfig;\n    double q;\n    double w;\n    double s;\n    double c;\n    double a;\n\n    MA_ASSERT(pConfig != NULL);\n\n    q = pConfig->q;\n    w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;\n    s = ma_sind(w);\n    c = ma_cosd(w);\n    a = s / (2*q);\n\n    bqConfig.b0 =  q * a;\n    bqConfig.b1 =  0;\n    bqConfig.b2 = -q * a;\n    bqConfig.a0 =  1 + a;\n    bqConfig.a1 = -2 * c;\n    bqConfig.a2 =  1 - a;\n\n    bqConfig.format   = pConfig->format;\n    bqConfig.channels = pConfig->channels;\n\n    return bqConfig;\n}\n\nMA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_biquad_config bqConfig;\n    bqConfig = ma_bpf2__get_biquad_config(pConfig);\n\n    return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);\n}\n\nMA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pBPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pBPF);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_bpf2__get_biquad_config(pConfig);\n    result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pBPF->bq._ownsHeap = MA_TRUE;    /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */\n    return MA_SUCCESS;\n}\n\nMA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pBPF == NULL) {\n        return;\n    }\n\n    ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks);   /* <-- This will free the heap allocation. */\n}\n\nMA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pBPF == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_bpf2__get_biquad_config(pConfig);\n    result = ma_biquad_reinit(&bqConfig, &pBPF->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn);\n}\n\nstatic MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn);\n}\n\nMA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    if (pBPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount);\n}\n\nMA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF)\n{\n    if (pBPF == NULL) {\n        return 0;\n    }\n\n    return ma_biquad_get_latency(&pBPF->bq);\n}\n\n\nMA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)\n{\n    ma_bpf_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format          = format;\n    config.channels        = channels;\n    config.sampleRate      = sampleRate;\n    config.cutoffFrequency = cutoffFrequency;\n    config.order           = ma_min(order, MA_MAX_FILTER_ORDER);\n\n    return config;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t bpf2Offset;\n} ma_bpf_heap_layout;\n\nstatic ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout)\n{\n    ma_result result;\n    ma_uint32 bpf2Count;\n    ma_uint32 ibpf2;\n\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->order > MA_MAX_FILTER_ORDER) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* We must have an even number of order. */\n    if ((pConfig->order & 0x1) != 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    bpf2Count = pConfig->channels / 2;\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* BPF 2 */\n    pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes;\n    for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {\n        size_t bpf2HeapSizeInBytes;\n        ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107);   /* <-- The \"q\" parameter does not matter for the purpose of calculating the heap size. */\n\n        result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes;\n    }\n\n    /* Make sure allocation size is aligned. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew)\n{\n    ma_result result;\n    ma_uint32 bpf2Count;\n    ma_uint32 ibpf2;\n    ma_bpf_heap_layout heapLayout;  /* Only used if isNew is true. */\n\n    if (pBPF == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Only supporting f32 and s16. */\n    if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The format cannot be changed after initialization. */\n    if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* The channel count cannot be changed after initialization. */\n    if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) {\n        return MA_INVALID_OPERATION;\n    }\n\n    if (pConfig->order > MA_MAX_FILTER_ORDER) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* We must have an even number of order. */\n    if ((pConfig->order & 0x1) != 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    bpf2Count = pConfig->order / 2;\n\n    /* The filter order can't change between reinits. */\n    if (!isNew) {\n        if (pBPF->bpf2Count != bpf2Count) {\n            return MA_INVALID_OPERATION;\n        }\n    }\n\n    if (isNew) {\n        result = ma_bpf_get_heap_layout(pConfig, &heapLayout);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pBPF->_pHeap = pHeap;\n        MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n        pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset);\n    } else {\n        MA_ZERO_OBJECT(&heapLayout);\n    }\n\n    for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {\n        ma_bpf2_config bpf2Config;\n        double q;\n\n        /* TODO: Calculate Q to make this a proper Butterworth filter. */\n        q = 0.707107;\n\n        bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);\n\n        if (isNew) {\n            size_t bpf2HeapSizeInBytes;\n\n            result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);\n            if (result == MA_SUCCESS) {\n                result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]);\n            }\n        } else {\n            result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]);\n        }\n\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    pBPF->bpf2Count = bpf2Count;\n    pBPF->format    = pConfig->format;\n    pBPF->channels  = pConfig->channels;\n\n    return MA_SUCCESS;\n}\n\n\nMA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_bpf_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_bpf_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF)\n{\n    if (pBPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pBPF);\n\n    return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE);\n}\n\nMA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pBPF->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_uint32 ibpf2;\n\n    if (pBPF == NULL) {\n        return;\n    }\n\n    for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {\n        ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks);\n    }\n\n    if (pBPF->_ownsHeap) {\n        ma_free(pBPF->_pHeap, pAllocationCallbacks);\n    }\n}\n\nMA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF)\n{\n    return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE);\n}\n\nMA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    ma_result result;\n    ma_uint32 ibpf2;\n\n    if (pBPF == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Faster path for in-place. */\n    if (pFramesOut == pFramesIn) {\n        for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {\n            result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount);\n            if (result != MA_SUCCESS) {\n                return result;\n            }\n        }\n    }\n\n    /* Slightly slower path for copying. */\n    if (pFramesOut != pFramesIn) {\n        ma_uint32 iFrame;\n\n        /*  */ if (pBPF->format == ma_format_f32) {\n            /* */ float* pFramesOutF32 = (      float*)pFramesOut;\n            const float* pFramesInF32  = (const float*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));\n\n                for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {\n                    ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32);\n                }\n\n                pFramesOutF32 += pBPF->channels;\n                pFramesInF32  += pBPF->channels;\n            }\n        } else if (pBPF->format == ma_format_s16) {\n            /* */ ma_int16* pFramesOutS16 = (      ma_int16*)pFramesOut;\n            const ma_int16* pFramesInS16  = (const ma_int16*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));\n\n                for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {\n                    ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16);\n                }\n\n                pFramesOutS16 += pBPF->channels;\n                pFramesInS16  += pBPF->channels;\n            }\n        } else {\n            MA_ASSERT(MA_FALSE);\n            return MA_INVALID_OPERATION;    /* Should never hit this. */\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF)\n{\n    if (pBPF == NULL) {\n        return 0;\n    }\n\n    return pBPF->bpf2Count*2;\n}\n\n\n/**************************************************************************************************************************************************************\n\nNotching Filter\n\n**************************************************************************************************************************************************************/\nMA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)\n{\n    ma_notch2_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format     = format;\n    config.channels   = channels;\n    config.sampleRate = sampleRate;\n    config.q          = q;\n    config.frequency  = frequency;\n\n    if (config.q == 0) {\n        config.q = 0.707107;\n    }\n\n    return config;\n}\n\n\nstatic MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig)\n{\n    ma_biquad_config bqConfig;\n    double q;\n    double w;\n    double s;\n    double c;\n    double a;\n\n    MA_ASSERT(pConfig != NULL);\n\n    q = pConfig->q;\n    w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;\n    s = ma_sind(w);\n    c = ma_cosd(w);\n    a = s / (2*q);\n\n    bqConfig.b0 =  1;\n    bqConfig.b1 = -2 * c;\n    bqConfig.b2 =  1;\n    bqConfig.a0 =  1 + a;\n    bqConfig.a1 = -2 * c;\n    bqConfig.a2 =  1 - a;\n\n    bqConfig.format   = pConfig->format;\n    bqConfig.channels = pConfig->channels;\n\n    return bqConfig;\n}\n\nMA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_biquad_config bqConfig;\n    bqConfig = ma_notch2__get_biquad_config(pConfig);\n\n    return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);\n}\n\nMA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pFilter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pFilter);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_notch2__get_biquad_config(pConfig);\n    result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pFilter->bq._ownsHeap = MA_TRUE;    /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */\n    return MA_SUCCESS;\n}\n\nMA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFilter == NULL) {\n        return;\n    }\n\n    ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks);   /* <-- This will free the heap allocation. */\n}\n\nMA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pFilter == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_notch2__get_biquad_config(pConfig);\n    result = ma_biquad_reinit(&bqConfig, &pFilter->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);\n}\n\nstatic MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);\n}\n\nMA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    if (pFilter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);\n}\n\nMA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter)\n{\n    if (pFilter == NULL) {\n        return 0;\n    }\n\n    return ma_biquad_get_latency(&pFilter->bq);\n}\n\n\n\n/**************************************************************************************************************************************************************\n\nPeaking EQ Filter\n\n**************************************************************************************************************************************************************/\nMA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)\n{\n    ma_peak2_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format     = format;\n    config.channels   = channels;\n    config.sampleRate = sampleRate;\n    config.gainDB     = gainDB;\n    config.q          = q;\n    config.frequency  = frequency;\n\n    if (config.q == 0) {\n        config.q = 0.707107;\n    }\n\n    return config;\n}\n\n\nstatic MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig)\n{\n    ma_biquad_config bqConfig;\n    double q;\n    double w;\n    double s;\n    double c;\n    double a;\n    double A;\n\n    MA_ASSERT(pConfig != NULL);\n\n    q = pConfig->q;\n    w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;\n    s = ma_sind(w);\n    c = ma_cosd(w);\n    a = s / (2*q);\n    A = ma_powd(10, (pConfig->gainDB / 40));\n\n    bqConfig.b0 =  1 + (a * A);\n    bqConfig.b1 = -2 * c;\n    bqConfig.b2 =  1 - (a * A);\n    bqConfig.a0 =  1 + (a / A);\n    bqConfig.a1 = -2 * c;\n    bqConfig.a2 =  1 - (a / A);\n\n    bqConfig.format   = pConfig->format;\n    bqConfig.channels = pConfig->channels;\n\n    return bqConfig;\n}\n\nMA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_biquad_config bqConfig;\n    bqConfig = ma_peak2__get_biquad_config(pConfig);\n\n    return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);\n}\n\nMA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pFilter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pFilter);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_peak2__get_biquad_config(pConfig);\n    result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pFilter->bq._ownsHeap = MA_TRUE;    /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */\n    return MA_SUCCESS;\n}\n\nMA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFilter == NULL) {\n        return;\n    }\n\n    ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks);   /* <-- This will free the heap allocation. */\n}\n\nMA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pFilter == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_peak2__get_biquad_config(pConfig);\n    result = ma_biquad_reinit(&bqConfig, &pFilter->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);\n}\n\nstatic MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);\n}\n\nMA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    if (pFilter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);\n}\n\nMA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter)\n{\n    if (pFilter == NULL) {\n        return 0;\n    }\n\n    return ma_biquad_get_latency(&pFilter->bq);\n}\n\n\n/**************************************************************************************************************************************************************\n\nLow Shelf Filter\n\n**************************************************************************************************************************************************************/\nMA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)\n{\n    ma_loshelf2_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format     = format;\n    config.channels   = channels;\n    config.sampleRate = sampleRate;\n    config.gainDB     = gainDB;\n    config.shelfSlope = shelfSlope;\n    config.frequency  = frequency;\n\n    return config;\n}\n\n\nstatic MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig)\n{\n    ma_biquad_config bqConfig;\n    double w;\n    double s;\n    double c;\n    double A;\n    double S;\n    double a;\n    double sqrtA;\n\n    MA_ASSERT(pConfig != NULL);\n\n    w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;\n    s = ma_sind(w);\n    c = ma_cosd(w);\n    A = ma_powd(10, (pConfig->gainDB / 40));\n    S = pConfig->shelfSlope;\n    a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);\n    sqrtA = 2*ma_sqrtd(A)*a;\n\n    bqConfig.b0 =  A * ((A + 1) - (A - 1)*c + sqrtA);\n    bqConfig.b1 =  2 * A * ((A - 1) - (A + 1)*c);\n    bqConfig.b2 =  A * ((A + 1) - (A - 1)*c - sqrtA);\n    bqConfig.a0 =  (A + 1) + (A - 1)*c + sqrtA;\n    bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c);\n    bqConfig.a2 =  (A + 1) + (A - 1)*c - sqrtA;\n\n    bqConfig.format   = pConfig->format;\n    bqConfig.channels = pConfig->channels;\n\n    return bqConfig;\n}\n\nMA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_biquad_config bqConfig;\n    bqConfig = ma_loshelf2__get_biquad_config(pConfig);\n\n    return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);\n}\n\nMA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pFilter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pFilter);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_loshelf2__get_biquad_config(pConfig);\n    result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pFilter->bq._ownsHeap = MA_TRUE;    /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */\n    return MA_SUCCESS;\n}\n\nMA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFilter == NULL) {\n        return;\n    }\n\n    ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks);   /* <-- This will free the heap allocation. */\n}\n\nMA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pFilter == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_loshelf2__get_biquad_config(pConfig);\n    result = ma_biquad_reinit(&bqConfig, &pFilter->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);\n}\n\nstatic MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);\n}\n\nMA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    if (pFilter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);\n}\n\nMA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter)\n{\n    if (pFilter == NULL) {\n        return 0;\n    }\n\n    return ma_biquad_get_latency(&pFilter->bq);\n}\n\n\n/**************************************************************************************************************************************************************\n\nHigh Shelf Filter\n\n**************************************************************************************************************************************************************/\nMA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)\n{\n    ma_hishelf2_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format     = format;\n    config.channels   = channels;\n    config.sampleRate = sampleRate;\n    config.gainDB     = gainDB;\n    config.shelfSlope = shelfSlope;\n    config.frequency  = frequency;\n\n    return config;\n}\n\n\nstatic MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig)\n{\n    ma_biquad_config bqConfig;\n    double w;\n    double s;\n    double c;\n    double A;\n    double S;\n    double a;\n    double sqrtA;\n\n    MA_ASSERT(pConfig != NULL);\n\n    w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;\n    s = ma_sind(w);\n    c = ma_cosd(w);\n    A = ma_powd(10, (pConfig->gainDB / 40));\n    S = pConfig->shelfSlope;\n    a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);\n    sqrtA = 2*ma_sqrtd(A)*a;\n\n    bqConfig.b0 =  A * ((A + 1) + (A - 1)*c + sqrtA);\n    bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c);\n    bqConfig.b2 =  A * ((A + 1) + (A - 1)*c - sqrtA);\n    bqConfig.a0 =  (A + 1) - (A - 1)*c + sqrtA;\n    bqConfig.a1 =  2 * ((A - 1) - (A + 1)*c);\n    bqConfig.a2 =  (A + 1) - (A - 1)*c - sqrtA;\n\n    bqConfig.format   = pConfig->format;\n    bqConfig.channels = pConfig->channels;\n\n    return bqConfig;\n}\n\nMA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_biquad_config bqConfig;\n    bqConfig = ma_hishelf2__get_biquad_config(pConfig);\n\n    return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);\n}\n\nMA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pFilter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pFilter);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_hishelf2__get_biquad_config(pConfig);\n    result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pFilter->bq._ownsHeap = MA_TRUE;    /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */\n    return MA_SUCCESS;\n}\n\nMA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFilter == NULL) {\n        return;\n    }\n\n    ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks);   /* <-- This will free the heap allocation. */\n}\n\nMA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter)\n{\n    ma_result result;\n    ma_biquad_config bqConfig;\n\n    if (pFilter == NULL || pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    bqConfig = ma_hishelf2__get_biquad_config(pConfig);\n    result = ma_biquad_reinit(&bqConfig, &pFilter->bq);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);\n}\n\nstatic MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn)\n{\n    ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);\n}\n\nMA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    if (pFilter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);\n}\n\nMA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter)\n{\n    if (pFilter == NULL) {\n        return 0;\n    }\n\n    return ma_biquad_get_latency(&pFilter->bq);\n}\n\n\n\n/*\nDelay\n*/\nMA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)\n{\n    ma_delay_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.channels      = channels;\n    config.sampleRate    = sampleRate;\n    config.delayInFrames = delayInFrames;\n    config.delayStart    = (decay == 0) ? MA_TRUE : MA_FALSE;   /* Delay the start if it looks like we're not configuring an echo. */\n    config.wet           = 1;\n    config.dry           = 1;\n    config.decay         = decay;\n\n    return config;\n}\n\n\nMA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay)\n{\n    if (pDelay == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pDelay);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->decay < 0 || pConfig->decay > 1) {\n        return MA_INVALID_ARGS;\n    }\n\n    pDelay->config             = *pConfig;\n    pDelay->bufferSizeInFrames = pConfig->delayInFrames;\n    pDelay->cursor             = 0;\n\n    pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks);\n    if (pDelay->pBuffer == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels);\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pDelay == NULL) {\n        return;\n    }\n\n    ma_free(pDelay->pBuffer, pAllocationCallbacks);\n}\n\nMA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)\n{\n    ma_uint32 iFrame;\n    ma_uint32 iChannel;\n    float* pFramesOutF32 = (float*)pFramesOut;\n    const float* pFramesInF32 = (const float*)pFramesIn;\n\n    if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) {\n            ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel;\n\n            if (pDelay->config.delayStart) {\n                /* Delayed start. */\n\n                /* Read */\n                pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;\n\n                /* Feedback */\n                pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);\n            } else {\n                /* Immediate start */\n\n                /* Feedback */\n                pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);\n\n                /* Read */\n                pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;\n            }\n        }\n\n        pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames;\n\n        pFramesOutF32 += pDelay->config.channels;\n        pFramesInF32  += pDelay->config.channels;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_delay_set_wet(ma_delay* pDelay, float value)\n{\n    if (pDelay == NULL) {\n        return;\n    }\n\n    pDelay->config.wet = value;\n}\n\nMA_API float ma_delay_get_wet(const ma_delay* pDelay)\n{\n    if (pDelay == NULL) {\n        return 0;\n    }\n\n    return pDelay->config.wet;\n}\n\nMA_API void ma_delay_set_dry(ma_delay* pDelay, float value)\n{\n    if (pDelay == NULL) {\n        return;\n    }\n\n    pDelay->config.dry = value;\n}\n\nMA_API float ma_delay_get_dry(const ma_delay* pDelay)\n{\n    if (pDelay == NULL) {\n        return 0;\n    }\n\n    return pDelay->config.dry;\n}\n\nMA_API void ma_delay_set_decay(ma_delay* pDelay, float value)\n{\n    if (pDelay == NULL) {\n        return;\n    }\n\n    pDelay->config.decay = value;\n}\n\nMA_API float ma_delay_get_decay(const ma_delay* pDelay)\n{\n    if (pDelay == NULL) {\n        return 0;\n    }\n\n    return pDelay->config.decay;\n}\n\n\nMA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames)\n{\n    ma_gainer_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.channels           = channels;\n    config.smoothTimeInFrames = smoothTimeInFrames;\n\n    return config;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t oldGainsOffset;\n    size_t newGainsOffset;\n} ma_gainer_heap_layout;\n\nstatic ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout)\n{\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->channels == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* Old gains. */\n    pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;\n\n    /* New gains. */\n    pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;\n\n    /* Alignment. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\n\nMA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_gainer_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_gainer_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\n\nMA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer)\n{\n    ma_result result;\n    ma_gainer_heap_layout heapLayout;\n    ma_uint32 iChannel;\n\n    if (pGainer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pGainer);\n\n    if (pConfig == NULL || pHeap == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_gainer_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pGainer->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset);\n    pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset);\n    pGainer->masterVolume = 1;\n\n    pGainer->config = *pConfig;\n    pGainer->t      = (ma_uint32)-1;  /* No interpolation by default. */\n\n    for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {\n        pGainer->pOldGains[iChannel] = 1;\n        pGainer->pNewGains[iChannel] = 1;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to retrieve the size of the heap allocation. */\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pGainer->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pGainer == NULL) {\n        return;\n    }\n\n    if (pGainer->_ownsHeap) {\n        ma_free(pGainer->_pHeap, pAllocationCallbacks);\n    }\n}\n\nstatic float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel)\n{\n    float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;\n    return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a);\n}\n\nstatic /*__attribute__((noinline))*/ ma_result ma_gainer_process_pcm_frames_internal(ma_gainer * pGainer, void* MA_RESTRICT pFramesOut, const void* MA_RESTRICT pFramesIn, ma_uint64 frameCount)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannel;\n    ma_uint64 interpolatedFrameCount;\n\n    MA_ASSERT(pGainer != NULL);\n\n    /*\n    We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When\n    linear interpolation is not needed we can do a simple volume adjustment which will be more\n    efficient than a lerp with an alpha value of 1.\n\n    To do this, all we need to do is determine how many frames need to have a lerp applied. Then we\n    just process that number of frames with linear interpolation. After that we run on an optimized\n    path which just applies the new gains without a lerp.\n    */\n    if (pGainer->t >= pGainer->config.smoothTimeInFrames) {\n        interpolatedFrameCount = 0;\n    } else {\n        interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames;\n        if (interpolatedFrameCount > frameCount) {\n            interpolatedFrameCount = frameCount;\n        }\n    }\n\n    /*\n    Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers\n    so that the fast path can work naturally without consideration of the interpolated path.\n    */\n    if (interpolatedFrameCount > 0) {\n        /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */\n        if (pFramesOut != NULL && pFramesIn != NULL) {\n            /*\n            All we're really doing here is moving the old gains towards the new gains. We don't want to\n            be modifying the gains inside the ma_gainer object because that will break things. Instead\n            we can make a copy here on the stack. For extreme channel counts we can fall back to a slower\n            implementation which just uses a standard lerp.\n            */\n            float* pFramesOutF32 = (float*)pFramesOut;\n            const float* pFramesInF32 = (const float*)pFramesIn;\n            float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;\n            float d = 1.0f / pGainer->config.smoothTimeInFrames;\n\n            if (pGainer->config.channels <= 32) {\n                float pRunningGain[32];\n                float pRunningGainDelta[32];    /* Could this be heap-allocated as part of the ma_gainer object? */\n\n                /* Initialize the running gain. */\n                for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {\n                    float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume;\n                    pRunningGainDelta[iChannel] = t * d;\n                    pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a);\n                }\n\n                iFrame = 0;\n\n                /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */\n                if (pGainer->config.channels == 2) {\n                #if defined(MA_SUPPORT_SSE2)\n                    if (ma_has_sse2()) {\n                        ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;\n\n                        /* Expand some arrays so we can have a clean SIMD loop below. */\n                        __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]);\n                        __m128 runningGain0      = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]);\n\n                        for (; iFrame < unrolledLoopCount; iFrame += 1) {\n                            _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0));\n                            runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);\n                        }\n\n                        iFrame = unrolledLoopCount << 1;\n                    } else\n                #endif\n                    {\n                        /*\n                        Two different scalar implementations here. Clang (and I assume GCC) will vectorize\n                        both of these, but the bottom version results in a nicer vectorization with less\n                        instructions emitted. The problem, however, is that the bottom version runs slower\n                        when compiled with MSVC. The top version will be partially vectorized by MSVC.\n                        */\n                    #if defined(_MSC_VER) && !defined(__clang__)\n                        ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;\n\n                        /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */\n                        pRunningGainDelta[2] = pRunningGainDelta[0];\n                        pRunningGainDelta[3] = pRunningGainDelta[1];\n                        pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0];\n                        pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1];\n\n                        for (; iFrame < unrolledLoopCount; iFrame += 1) {\n                            pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0];\n                            pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1];\n                            pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2];\n                            pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3];\n\n                            /* Move the running gain forward towards the new gain. */\n                            pRunningGain[0] += pRunningGainDelta[0];\n                            pRunningGain[1] += pRunningGainDelta[1];\n                            pRunningGain[2] += pRunningGainDelta[2];\n                            pRunningGain[3] += pRunningGainDelta[3];\n                        }\n\n                        iFrame = unrolledLoopCount << 1;\n                    #else\n                        for (; iFrame < interpolatedFrameCount; iFrame += 1) {\n                            for (iChannel = 0; iChannel < 2; iChannel += 1) {\n                                pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel];\n                            }\n\n                            for (iChannel = 0; iChannel < 2; iChannel += 1) {\n                                pRunningGain[iChannel] += pRunningGainDelta[iChannel];\n                            }\n                        }\n                    #endif\n                    }\n                } else if (pGainer->config.channels == 6) {\n                #if defined(MA_SUPPORT_SSE2)\n                    if (ma_has_sse2()) {\n                        /*\n                        For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames\n                        at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays\n                        so we can do clean 4x SIMD operations.\n                        */\n                        ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;\n\n                        /* Expand some arrays so we can have a clean SIMD loop below. */\n                        __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]);\n                        __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]);\n                        __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]);\n\n                        __m128 runningGain0      = _mm_set_ps(pRunningGain[3],                        pRunningGain[2],                        pRunningGain[1],                        pRunningGain[0]);\n                        __m128 runningGain1      = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5],                        pRunningGain[4]);\n                        __m128 runningGain2      = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]);\n\n                        for (; iFrame < unrolledLoopCount; iFrame += 1) {\n                            _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0));\n                            _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1));\n                            _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2));\n\n                            runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);\n                            runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1);\n                            runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2);\n                        }\n\n                        iFrame = unrolledLoopCount << 1;\n                    } else\n                #endif\n                    {\n                        for (; iFrame < interpolatedFrameCount; iFrame += 1) {\n                            for (iChannel = 0; iChannel < 6; iChannel += 1) {\n                                pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel];\n                            }\n\n                            /* Move the running gain forward towards the new gain. */\n                            for (iChannel = 0; iChannel < 6; iChannel += 1) {\n                                pRunningGain[iChannel] += pRunningGainDelta[iChannel];\n                            }\n                        }\n                    }\n                } else if (pGainer->config.channels == 8) {\n                    /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */\n                #if defined(MA_SUPPORT_SSE2)\n                    if (ma_has_sse2()) {\n                        __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]);\n                        __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]);\n                        __m128 runningGain0      = _mm_loadu_ps(&pRunningGain[0]);\n                        __m128 runningGain1      = _mm_loadu_ps(&pRunningGain[4]);\n\n                        for (; iFrame < interpolatedFrameCount; iFrame += 1) {\n                            _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0));\n                            _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1));\n\n                            runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);\n                            runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1);\n                        }\n                    } else\n                #endif\n                    {\n                        /* This is crafted so that it auto-vectorizes when compiled with Clang. */\n                        for (; iFrame < interpolatedFrameCount; iFrame += 1) {\n                            for (iChannel = 0; iChannel < 8; iChannel += 1) {\n                                pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel];\n                            }\n\n                            /* Move the running gain forward towards the new gain. */\n                            for (iChannel = 0; iChannel < 8; iChannel += 1) {\n                                pRunningGain[iChannel] += pRunningGainDelta[iChannel];\n                            }\n                        }\n                    }\n                }\n\n                for (; iFrame < interpolatedFrameCount; iFrame += 1) {\n                    for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {\n                        pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel];\n                        pRunningGain[iChannel] += pRunningGainDelta[iChannel];\n                    }\n                }\n            } else {\n                /* Slower path for extreme channel counts where we can't fit enough on the stack. We could also move this to the heap as part of the ma_gainer object which might even be better since it'll only be updated when the gains actually change. */\n                for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) {\n                    for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {\n                        pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume;\n                    }\n\n                    a += d;\n                }\n            }\n        }\n\n        /* Make sure the timer is updated. */\n        pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames);\n\n        /* Adjust our arguments so the next part can work normally. */\n        frameCount -= interpolatedFrameCount;\n        pFramesOut  = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float));\n        pFramesIn   = ma_offset_ptr(pFramesIn,  interpolatedFrameCount * sizeof(float));\n    }\n\n    /* All we need to do here is apply the new gains using an optimized path. */\n    if (pFramesOut != NULL && pFramesIn != NULL) {\n        if (pGainer->config.channels <= 32) {\n            float gains[32];\n            for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {\n                gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume;\n            }\n\n            ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains);\n        } else {\n            /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {\n                    ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume;\n                }\n            }\n        }\n    }\n\n    /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */\n    if (pGainer->t == (ma_uint32)-1) {\n        pGainer->t  = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount);\n    }\n\n#if 0\n    if (pGainer->t >= pGainer->config.smoothTimeInFrames) {\n        /* Fast path. No gain calculation required. */\n        ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains);\n        ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume);\n\n        /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */\n        if (pGainer->t == (ma_uint32)-1) {\n            pGainer->t = pGainer->config.smoothTimeInFrames;\n        }\n    } else {\n        /* Slow path. Need to interpolate the gain for each channel individually. */\n\n        /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */\n        if (pFramesOut != NULL && pFramesIn != NULL) {\n            float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;\n            float d = 1.0f / pGainer->config.smoothTimeInFrames;\n            ma_uint32 channelCount = pGainer->config.channels;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannel = 0; iChannel < channelCount; iChannel += 1) {\n                    pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume;\n                }\n\n                pFramesOutF32 += channelCount;\n                pFramesInF32  += channelCount;\n\n                a += d;\n                if (a > 1) {\n                    a = 1;\n                }\n            }\n        }\n\n        pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames);\n\n    #if 0   /* Reference implementation. */\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */\n            if (pFramesOut != NULL && pFramesIn != NULL) {\n                for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {\n                    pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume;\n                }\n            }\n\n            /* Move interpolation time forward, but don't go beyond our smoothing time. */\n            pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames);\n        }\n    #endif\n    }\n#endif\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    if (pGainer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /*\n    ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which\n    helps with auto-vectorization.\n    */\n    return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount);\n}\n\nstatic void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel)\n{\n    pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel);\n    pGainer->pNewGains[iChannel] = newGain;\n}\n\nstatic void ma_gainer_reset_smoothing_time(ma_gainer* pGainer)\n{\n    if (pGainer->t == (ma_uint32)-1) {\n        pGainer->t = pGainer->config.smoothTimeInFrames;    /* No smoothing required for initial gains setting. */\n    } else {\n        pGainer->t = 0;\n    }\n}\n\nMA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain)\n{\n    ma_uint32 iChannel;\n\n    if (pGainer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {\n        ma_gainer_set_gain_by_index(pGainer, newGain, iChannel);\n    }\n\n    /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */\n    ma_gainer_reset_smoothing_time(pGainer);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains)\n{\n    ma_uint32 iChannel;\n\n    if (pGainer == NULL || pNewGains == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {\n        ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel);\n    }\n\n    /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */\n    ma_gainer_reset_smoothing_time(pGainer);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume)\n{\n    if (pGainer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pGainer->masterVolume = volume;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume)\n{\n    if (pGainer == NULL || pVolume == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pVolume = pGainer->masterVolume;\n\n    return MA_SUCCESS;\n}\n\n\nMA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels)\n{\n    ma_panner_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format   = format;\n    config.channels = channels;\n    config.mode     = ma_pan_mode_balance;  /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */\n    config.pan      = 0;\n\n    return config;\n}\n\n\nMA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner)\n{\n    if (pPanner == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pPanner);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pPanner->format   = pConfig->format;\n    pPanner->channels = pConfig->channels;\n    pPanner->mode     = pConfig->mode;\n    pPanner->pan      = pConfig->pan;\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)\n{\n    ma_uint64 iFrame;\n\n    if (pan > 0) {\n        float factor = 1.0f - pan;\n        if (pFramesOut == pFramesIn) {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;\n            }\n        } else {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;\n                pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1];\n            }\n        }\n    } else {\n        float factor = 1.0f + pan;\n        if (pFramesOut == pFramesIn) {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;\n            }\n        } else {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0];\n                pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;\n            }\n        }\n    }\n}\n\nstatic void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)\n{\n    if (pan == 0) {\n        /* Fast path. No panning required. */\n        if (pFramesOut == pFramesIn) {\n            /* No-op */\n        } else {\n            ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);\n        }\n\n        return;\n    }\n\n    switch (format) {\n        case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;\n\n        /* Unknown format. Just copy. */\n        default:\n        {\n            ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);\n        } break;\n    }\n}\n\n\nstatic void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)\n{\n    ma_uint64 iFrame;\n\n    if (pan > 0) {\n        float factorL0 = 1.0f - pan;\n        float factorL1 = 0.0f + pan;\n\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0);\n            float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1];\n\n            pFramesOut[iFrame*2 + 0] = sample0;\n            pFramesOut[iFrame*2 + 1] = sample1;\n        }\n    } else {\n        float factorR0 = 0.0f - pan;\n        float factorR1 = 1.0f + pan;\n\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0);\n            float sample1 =                           (pFramesIn[iFrame*2 + 1] * factorR1);\n\n            pFramesOut[iFrame*2 + 0] = sample0;\n            pFramesOut[iFrame*2 + 1] = sample1;\n        }\n    }\n}\n\nstatic void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)\n{\n    if (pan == 0) {\n        /* Fast path. No panning required. */\n        if (pFramesOut == pFramesIn) {\n            /* No-op */\n        } else {\n            ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);\n        }\n\n        return;\n    }\n\n    switch (format) {\n        case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;\n\n        /* Unknown format. Just copy. */\n        default:\n        {\n            ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);\n        } break;\n    }\n}\n\nMA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pPanner->channels == 2) {\n        /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */\n        if (pPanner->mode == ma_pan_mode_balance) {\n            ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);\n        } else {\n            ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);\n        }\n    } else {\n        if (pPanner->channels == 1) {\n            /* Panning has no effect on mono streams. */\n            ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);\n        } else {\n            /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */\n            ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode)\n{\n    if (pPanner == NULL) {\n        return;\n    }\n\n    pPanner->mode = mode;\n}\n\nMA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner)\n{\n    if (pPanner == NULL) {\n        return ma_pan_mode_balance;\n    }\n\n    return pPanner->mode;\n}\n\nMA_API void ma_panner_set_pan(ma_panner* pPanner, float pan)\n{\n    if (pPanner == NULL) {\n        return;\n    }\n\n    pPanner->pan = ma_clamp(pan, -1.0f, 1.0f);\n}\n\nMA_API float ma_panner_get_pan(const ma_panner* pPanner)\n{\n    if (pPanner == NULL) {\n        return 0;\n    }\n\n    return pPanner->pan;\n}\n\n\n\n\nMA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate)\n{\n    ma_fader_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format     = format;\n    config.channels   = channels;\n    config.sampleRate = sampleRate;\n\n    return config;\n}\n\n\nMA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader)\n{\n    if (pFader == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pFader);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Only f32 is supported for now. */\n    if (pConfig->format != ma_format_f32) {\n        return MA_INVALID_ARGS;\n    }\n\n    pFader->config         = *pConfig;\n    pFader->volumeBeg      = 1;\n    pFader->volumeEnd      = 1;\n    pFader->lengthInFrames = 0;\n    pFader->cursorInFrames = 0;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    if (pFader == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */\n    if (pFader->cursorInFrames < 0) {\n        ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames;\n        if (absCursorInFrames > frameCount) {\n            absCursorInFrames = frameCount;\n        }\n\n        ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels);\n\n        pFader->cursorInFrames += absCursorInFrames;\n        frameCount -= absCursorInFrames;\n        pFramesOut  = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames);\n        pFramesIn   = ma_offset_ptr(pFramesIn,  ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames);\n    }\n\n    if (pFader->cursorInFrames >= 0) {\n        /*\n        For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for\n        the conversion to a float which we use for the linear interpolation. This might be changed later.\n        */\n        if (frameCount + pFader->cursorInFrames > UINT_MAX) {\n            frameCount = UINT_MAX - pFader->cursorInFrames;\n        }\n\n        /* Optimized path if volumeBeg and volumeEnd are equal. */\n        if (pFader->volumeBeg == pFader->volumeEnd) {\n            if (pFader->volumeBeg == 1) {\n                /* Straight copy. */\n                ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels);\n            } else {\n                /* Copy with volume. */\n                ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg);\n            }\n        } else {\n            /* Slower path. Volumes are different, so may need to do an interpolation. */\n            if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) {\n                /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */\n                ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd);\n            } else {\n                /* Slow path. This is where we do the actual fading. */\n                ma_uint64 iFrame;\n                ma_uint32 iChannel;\n\n                /* For now we only support f32. Support for other formats might be added later. */\n                if (pFader->config.format == ma_format_f32) {\n                    const float* pFramesInF32  = (const float*)pFramesIn;\n                    /* */ float* pFramesOutF32 = (      float*)pFramesOut;\n\n                    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                        float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames);   /* Safe cast due to the frameCount clamp at the top of this function. */\n                        float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a);\n\n                        for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) {\n                            pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume;\n                        }\n                    }\n                } else {\n                    return MA_NOT_IMPLEMENTED;\n                }\n            }\n        }\n    }\n\n    pFader->cursorInFrames += frameCount;\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)\n{\n    if (pFader == NULL) {\n        return;\n    }\n\n    if (pFormat != NULL) {\n        *pFormat = pFader->config.format;\n    }\n\n    if (pChannels != NULL) {\n        *pChannels = pFader->config.channels;\n    }\n\n    if (pSampleRate != NULL) {\n        *pSampleRate = pFader->config.sampleRate;\n    }\n}\n\nMA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames)\n{\n    ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0);\n}\n\nMA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames)\n{\n    if (pFader == NULL) {\n        return;\n    }\n\n    /* If the volume is negative, use current volume. */\n    if (volumeBeg < 0) {\n        volumeBeg = ma_fader_get_current_volume(pFader);\n    }\n\n    /*\n    The length needs to be clamped to 32-bits due to how we convert it to a float for linear\n    interpolation reasons. I might change this requirement later, but for now it's not important.\n    */\n    if (lengthInFrames > UINT_MAX) {\n        lengthInFrames = UINT_MAX;\n    }\n\n    /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */\n    if (startOffsetInFrames > INT_MAX) {\n        startOffsetInFrames = INT_MAX;\n    }\n\n    pFader->volumeBeg      = volumeBeg;\n    pFader->volumeEnd      = volumeEnd;\n    pFader->lengthInFrames = lengthInFrames;\n    pFader->cursorInFrames = -startOffsetInFrames;\n}\n\nMA_API float ma_fader_get_current_volume(const ma_fader* pFader)\n{\n    if (pFader == NULL) {\n        return 0.0f;\n    }\n\n    /* Any frames prior to the start of the fade period will be at unfaded volume. */\n    if (pFader->cursorInFrames < 0) {\n        return 1.0f;\n    }\n\n    /* The current volume depends on the position of the cursor. */\n    if (pFader->cursorInFrames == 0) {\n        return pFader->volumeBeg;\n    } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) {   /* Safe case because the < 0 case was checked above. */\n        return pFader->volumeEnd;\n    } else {\n        /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */\n        return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames));    /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */\n    }\n}\n\n\n\n\n\nMA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z)\n{\n    ma_vec3f v;\n\n    v.x = x;\n    v.y = y;\n    v.z = z;\n\n    return v;\n}\n\nMA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b)\n{\n    return ma_vec3f_init_3f(\n        a.x - b.x,\n        a.y - b.y,\n        a.z - b.z\n    );\n}\n\nMA_API ma_vec3f ma_vec3f_neg(ma_vec3f a)\n{\n    return ma_vec3f_init_3f(\n        -a.x,\n        -a.y,\n        -a.z\n    );\n}\n\nMA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b)\n{\n    return a.x*b.x + a.y*b.y + a.z*b.z;\n}\n\nMA_API float ma_vec3f_len2(ma_vec3f v)\n{\n    return ma_vec3f_dot(v, v);\n}\n\nMA_API float ma_vec3f_len(ma_vec3f v)\n{\n    return (float)ma_sqrtd(ma_vec3f_len2(v));\n}\n\n\n\nMA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b)\n{\n    return ma_vec3f_len(ma_vec3f_sub(a, b));\n}\n\nMA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v)\n{\n    float invLen;\n    float len2 = ma_vec3f_len2(v);\n    if (len2 == 0) {\n        return ma_vec3f_init_3f(0, 0, 0);\n    }\n\n    invLen = ma_rsqrtf(len2);\n    v.x *= invLen;\n    v.y *= invLen;\n    v.z *= invLen;\n\n    return v;\n}\n\nMA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b)\n{\n    return ma_vec3f_init_3f(\n        a.y*b.z - a.z*b.y,\n        a.z*b.x - a.x*b.z,\n        a.x*b.y - a.y*b.x\n    );\n}\n\n\nMA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value)\n{\n    v->v = value;\n    v->lock = 0;    /* Important this is initialized to 0. */\n}\n\nMA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value)\n{\n    ma_spinlock_lock(&v->lock);\n    {\n        v->v = value;\n    }\n    ma_spinlock_unlock(&v->lock);\n}\n\nMA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v)\n{\n    ma_vec3f r;\n\n    ma_spinlock_lock(&v->lock);\n    {\n        r = v->v;\n    }\n    ma_spinlock_unlock(&v->lock);\n\n    return r;\n}\n\n\n\nstatic void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode);\nstatic ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition);\n\n\n#ifndef MA_DEFAULT_SPEED_OF_SOUND\n#define MA_DEFAULT_SPEED_OF_SOUND   343.3f\n#endif\n\n/*\nThese vectors represent the direction that speakers are facing from the center point. They're used\nfor panning in the spatializer. Must be normalized.\n*/\nstatic ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = {\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_NONE */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_MONO */\n    {-0.7071f,  0.0f,    -0.7071f },  /* MA_CHANNEL_FRONT_LEFT */\n    {+0.7071f,  0.0f,    -0.7071f },  /* MA_CHANNEL_FRONT_RIGHT */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_FRONT_CENTER */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_LFE */\n    {-0.7071f,  0.0f,    +0.7071f },  /* MA_CHANNEL_BACK_LEFT */\n    {+0.7071f,  0.0f,    +0.7071f },  /* MA_CHANNEL_BACK_RIGHT */\n    {-0.3162f,  0.0f,    -0.9487f },  /* MA_CHANNEL_FRONT_LEFT_CENTER */\n    {+0.3162f,  0.0f,    -0.9487f },  /* MA_CHANNEL_FRONT_RIGHT_CENTER */\n    { 0.0f,     0.0f,    +1.0f    },  /* MA_CHANNEL_BACK_CENTER */\n    {-1.0f,     0.0f,     0.0f    },  /* MA_CHANNEL_SIDE_LEFT */\n    {+1.0f,     0.0f,     0.0f    },  /* MA_CHANNEL_SIDE_RIGHT */\n    { 0.0f,    +1.0f,     0.0f    },  /* MA_CHANNEL_TOP_CENTER */\n    {-0.5774f, +0.5774f, -0.5774f },  /* MA_CHANNEL_TOP_FRONT_LEFT */\n    { 0.0f,    +0.7071f, -0.7071f },  /* MA_CHANNEL_TOP_FRONT_CENTER */\n    {+0.5774f, +0.5774f, -0.5774f },  /* MA_CHANNEL_TOP_FRONT_RIGHT */\n    {-0.5774f, +0.5774f, +0.5774f },  /* MA_CHANNEL_TOP_BACK_LEFT */\n    { 0.0f,    +0.7071f, +0.7071f },  /* MA_CHANNEL_TOP_BACK_CENTER */\n    {+0.5774f, +0.5774f, +0.5774f },  /* MA_CHANNEL_TOP_BACK_RIGHT */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_0 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_1 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_2 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_3 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_4 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_5 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_6 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_7 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_8 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_9 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_10 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_11 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_12 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_13 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_14 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_15 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_16 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_17 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_18 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_19 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_20 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_21 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_22 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_23 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_24 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_25 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_26 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_27 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_28 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_29 */\n    { 0.0f,     0.0f,    -1.0f    },  /* MA_CHANNEL_AUX_30 */\n    { 0.0f,     0.0f,    -1.0f    }   /* MA_CHANNEL_AUX_31 */\n};\n\nstatic ma_vec3f ma_get_channel_direction(ma_channel channel)\n{\n    if (channel >= MA_CHANNEL_POSITION_COUNT) {\n        return ma_vec3f_init_3f(0, 0, -1);\n    } else {\n        return g_maChannelDirections[channel];\n    }\n}\n\n\n\nstatic float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff)\n{\n    if (minDistance >= maxDistance) {\n        return 1;   /* To avoid division by zero. Do not attenuate. */\n    }\n\n    return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance));\n}\n\nstatic float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff)\n{\n    if (minDistance >= maxDistance) {\n        return 1;   /* To avoid division by zero. Do not attenuate. */\n    }\n\n    return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance);\n}\n\nstatic float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff)\n{\n    if (minDistance >= maxDistance) {\n        return 1;   /* To avoid division by zero. Do not attenuate. */\n    }\n\n    return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff);\n}\n\n\n/*\nDopper Effect calculation taken from the OpenAL spec, with two main differences:\n\n  1) The source to listener vector will have already been calcualted at an earlier step so we can\n     just use that directly. We need only the position of the source relative to the origin.\n\n  2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight\n     into the resampler directly.\n*/\nstatic float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor)\n{\n    float len;\n    float vls;\n    float vss;\n\n    len = ma_vec3f_len(relativePosition);\n\n    /*\n    There's a case where the position of the source will be right on top of the listener in which\n    case the length will be 0 and we'll end up with a division by zero. We can just return a ratio\n    of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary.\n    */\n    if (len == 0) {\n        return 1.0;\n    }\n\n    vls = ma_vec3f_dot(relativePosition, listenVelocity) / len;\n    vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len;\n\n    vls = ma_min(vls, speedOfSound / dopplerFactor);\n    vss = ma_min(vss, speedOfSound / dopplerFactor);\n\n    return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss);\n}\n\n\nstatic void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount)\n{\n    /*\n    Special case for stereo. Want to default the left and right speakers to side left and side\n    right so that they're facing directly down the X axis rather than slightly forward. Not\n    doing this will result in sounds being quieter when behind the listener. This might\n    actually be good for some scenerios, but I don't think it's an appropriate default because\n    it can be a bit unexpected.\n    */\n    if (channelCount == 2) {\n        pChannelMap[0] = MA_CHANNEL_SIDE_LEFT;\n        pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT;\n    } else {\n        ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);\n    }\n}\n\n\nMA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut)\n{\n    ma_spatializer_listener_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.channelsOut             = channelsOut;\n    config.pChannelMapOut          = NULL;\n    config.handedness              = ma_handedness_right;\n    config.worldUp                 = ma_vec3f_init_3f(0, 1,  0);\n    config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */\n    config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */\n    config.coneOuterGain           = 0;\n    config.speedOfSound            = 343.3f;    /* Same as OpenAL. Used for doppler effect. */\n\n    return config;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t channelMapOutOffset;\n} ma_spatializer_listener_heap_layout;\n\nstatic ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout)\n{\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->channelsOut == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* Channel map. We always need this, even for passthroughs. */\n    pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut);\n\n    return MA_SUCCESS;\n}\n\n\nMA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_spatializer_listener_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener)\n{\n    ma_result result;\n    ma_spatializer_listener_heap_layout heapLayout;\n\n    if (pListener == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pListener);\n\n    result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pListener->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    pListener->config    = *pConfig;\n    ma_atomic_vec3f_init(&pListener->position,  ma_vec3f_init_3f(0, 0, 0));\n    ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1));\n    ma_atomic_vec3f_init(&pListener->velocity,  ma_vec3f_init_3f(0, 0,  0));\n    pListener->isEnabled = MA_TRUE;\n\n    /* Swap the forward direction if we're left handed (it was initialized based on right handed). */\n    if (pListener->config.handedness == ma_handedness_left) {\n        ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener));\n        ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z);\n    }\n\n\n    /* We must always have a valid channel map. */\n    pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);\n\n    /* Use a slightly different default channel map for stereo. */\n    if (pConfig->pChannelMapOut == NULL) {\n        ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut);\n    } else {\n        ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pListener->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pListener == NULL) {\n        return;\n    }\n\n    if (pListener->_ownsHeap) {\n        ma_free(pListener->_pHeap, pAllocationCallbacks);\n    }\n}\n\nMA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener)\n{\n    if (pListener == NULL) {\n        return NULL;\n    }\n\n    return pListener->config.pChannelMapOut;\n}\n\nMA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain)\n{\n    if (pListener == NULL) {\n        return;\n    }\n\n    pListener->config.coneInnerAngleInRadians = innerAngleInRadians;\n    pListener->config.coneOuterAngleInRadians = outerAngleInRadians;\n    pListener->config.coneOuterGain           = outerGain;\n}\n\nMA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)\n{\n    if (pListener == NULL) {\n        return;\n    }\n\n    if (pInnerAngleInRadians != NULL) {\n        *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians;\n    }\n\n    if (pOuterAngleInRadians != NULL) {\n        *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians;\n    }\n\n    if (pOuterGain != NULL) {\n        *pOuterGain = pListener->config.coneOuterGain;\n    }\n}\n\nMA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z)\n{\n    if (pListener == NULL) {\n        return;\n    }\n\n    ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z));\n}\n\nMA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener)\n{\n    if (pListener == NULL) {\n        return ma_vec3f_init_3f(0, 0, 0);\n    }\n\n    return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */\n}\n\nMA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z)\n{\n    if (pListener == NULL) {\n        return;\n    }\n\n    ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z));\n}\n\nMA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener)\n{\n    if (pListener == NULL) {\n        return ma_vec3f_init_3f(0, 0, -1);\n    }\n\n    return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->direction);    /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */\n}\n\nMA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z)\n{\n    if (pListener == NULL) {\n        return;\n    }\n\n    ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z));\n}\n\nMA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener)\n{\n    if (pListener == NULL) {\n        return ma_vec3f_init_3f(0, 0, 0);\n    }\n\n    return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */\n}\n\nMA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound)\n{\n    if (pListener == NULL) {\n        return;\n    }\n\n    pListener->config.speedOfSound = speedOfSound;\n}\n\nMA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener)\n{\n    if (pListener == NULL) {\n        return 0;\n    }\n\n    return pListener->config.speedOfSound;\n}\n\nMA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z)\n{\n    if (pListener == NULL) {\n        return;\n    }\n\n    pListener->config.worldUp = ma_vec3f_init_3f(x, y, z);\n}\n\nMA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener)\n{\n    if (pListener == NULL) {\n        return ma_vec3f_init_3f(0, 1, 0);\n    }\n\n    return pListener->config.worldUp;\n}\n\nMA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled)\n{\n    if (pListener == NULL) {\n        return;\n    }\n\n    pListener->isEnabled = isEnabled;\n}\n\nMA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener)\n{\n    if (pListener == NULL) {\n        return MA_FALSE;\n    }\n\n    return pListener->isEnabled;\n}\n\n\n\n\nMA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut)\n{\n    ma_spatializer_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.channelsIn                   = channelsIn;\n    config.channelsOut                  = channelsOut;\n    config.pChannelMapIn                = NULL;\n    config.attenuationModel             = ma_attenuation_model_inverse;\n    config.positioning                  = ma_positioning_absolute;\n    config.handedness                   = ma_handedness_right;\n    config.minGain                      = 0;\n    config.maxGain                      = 1;\n    config.minDistance                  = 1;\n    config.maxDistance                  = MA_FLT_MAX;\n    config.rolloff                      = 1;\n    config.coneInnerAngleInRadians      = 6.283185f; /* 360 degrees. */\n    config.coneOuterAngleInRadians      = 6.283185f; /* 360 degress. */\n    config.coneOuterGain                = 0.0f;\n    config.dopplerFactor                = 1;\n    config.directionalAttenuationFactor = 1;\n    config.minSpatializationChannelGain = 0.2f;\n    config.gainSmoothTimeInFrames       = 360;       /* 7.5ms @ 48K. */\n\n    return config;\n}\n\n\nstatic ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig)\n{\n    MA_ASSERT(pConfig != NULL);\n    return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames);\n}\n\nstatic ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig)\n{\n    MA_ASSERT(pConfig != NULL);\n\n    if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    return MA_SUCCESS;\n}\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t channelMapInOffset;\n    size_t newChannelGainsOffset;\n    size_t gainerOffset;\n} ma_spatializer_heap_layout;\n\nstatic ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout)\n{\n    ma_result result;\n\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_spatializer_validate_config(pConfig);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* Channel map. */\n    pHeapLayout->channelMapInOffset = MA_SIZE_MAX;  /* <-- MA_SIZE_MAX indicates no allocation necessary. */\n    if (pConfig->pChannelMapIn != NULL) {\n        pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;\n        pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn);\n    }\n\n    /* New channel gains for output. */\n    pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut);\n\n    /* Gainer. */\n    {\n        size_t gainerHeapSizeInBytes;\n        ma_gainer_config gainerConfig;\n\n        gainerConfig = ma_spatializer_gainer_config_init(pConfig);\n\n        result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;\n        pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_spatializer_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;  /* Safety. */\n\n    result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\n\nMA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer)\n{\n    ma_result result;\n    ma_spatializer_heap_layout heapLayout;\n    ma_gainer_config gainerConfig;\n\n    if (pSpatializer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pSpatializer);\n\n    if (pConfig == NULL || pHeap == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pSpatializer->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    pSpatializer->channelsIn                   = pConfig->channelsIn;\n    pSpatializer->channelsOut                  = pConfig->channelsOut;\n    pSpatializer->attenuationModel             = pConfig->attenuationModel;\n    pSpatializer->positioning                  = pConfig->positioning;\n    pSpatializer->handedness                   = pConfig->handedness;\n    pSpatializer->minGain                      = pConfig->minGain;\n    pSpatializer->maxGain                      = pConfig->maxGain;\n    pSpatializer->minDistance                  = pConfig->minDistance;\n    pSpatializer->maxDistance                  = pConfig->maxDistance;\n    pSpatializer->rolloff                      = pConfig->rolloff;\n    pSpatializer->coneInnerAngleInRadians      = pConfig->coneInnerAngleInRadians;\n    pSpatializer->coneOuterAngleInRadians      = pConfig->coneOuterAngleInRadians;\n    pSpatializer->coneOuterGain                = pConfig->coneOuterGain;\n    pSpatializer->dopplerFactor                = pConfig->dopplerFactor;\n    pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain;\n    pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor;\n    pSpatializer->gainSmoothTimeInFrames       = pConfig->gainSmoothTimeInFrames;\n    ma_atomic_vec3f_init(&pSpatializer->position,  ma_vec3f_init_3f(0, 0,  0));\n    ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1));\n    ma_atomic_vec3f_init(&pSpatializer->velocity,  ma_vec3f_init_3f(0, 0,  0));\n    pSpatializer->dopplerPitch                 = 1;\n\n    /* Swap the forward direction if we're left handed (it was initialized based on right handed). */\n    if (pSpatializer->handedness == ma_handedness_left) {\n        ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer));\n        ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z);\n    }\n\n    /* Channel map. This will be on the heap. */\n    if (pConfig->pChannelMapIn != NULL) {\n        pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);\n        ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn);\n    }\n\n    /* New channel gains for output channels. */\n    pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset);\n\n    /* Gainer. */\n    gainerConfig = ma_spatializer_gainer_config_init(pConfig);\n\n    result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to initialize the gainer. */\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    /* We'll need a heap allocation to retrieve the size. */\n    result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pSpatializer->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks);\n\n    if (pSpatializer->_ownsHeap) {\n        ma_free(pSpatializer->_pHeap, pAllocationCallbacks);\n    }\n}\n\nstatic float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain)\n{\n    /*\n    Angular attenuation.\n\n    Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure\n    this out for ourselves at the expense of possibly being inconsistent with other implementations.\n\n    To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We\n    just need to get the direction from the source to the listener and then do a dot product against that and the\n    direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer\n    angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than\n    the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.\n    */\n    if (coneInnerAngleInRadians < 6.283185f) {\n        float angularGain = 1;\n        float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f);\n        float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f);\n        float d;\n\n        d = ma_vec3f_dot(dirA, dirB);\n\n        if (d > cutoffInner) {\n            /* It's inside the inner angle. */\n            angularGain = 1;\n        } else {\n            /* It's outside the inner angle. */\n            if (d > cutoffOuter) {\n                /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */\n                angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter));\n            } else {\n                /* It's outside the outer angle. */\n                angularGain = coneOuterGain;\n            }\n        }\n\n        /*printf(\"d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\\n\", d, cutoffInner, cutoffOuter, angularGain);*/\n        return angularGain;\n    } else {\n        /* Inner angle is 360 degrees so no need to do any attenuation. */\n        return 1;\n    }\n}\n\nMA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    ma_channel* pChannelMapIn  = pSpatializer->pChannelMapIn;\n    ma_channel* pChannelMapOut = pListener->config.pChannelMapOut;\n\n    if (pSpatializer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* If we're not spatializing we need to run an optimized path. */\n    if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) {\n        if (ma_spatializer_listener_is_enabled(pListener)) {\n            /* No attenuation is required, but we'll need to do some channel conversion. */\n            if (pSpatializer->channelsIn == pSpatializer->channelsOut) {\n                ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn);\n            } else {\n                ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default);   /* Safe casts to float* because f32 is the only supported format. */\n            }\n        } else {\n            /* The listener is disabled. Output silence. */\n            ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);\n        }\n\n        /*\n        We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is\n        the correct thinking so might need to review this later.\n        */\n        pSpatializer->dopplerPitch = 1;\n    } else {\n        /*\n        Let's first determine which listener the sound is closest to. Need to keep in mind that we\n        might not have a world or any listeners, in which case we just spatializer based on the\n        listener being positioned at the origin (0, 0, 0).\n        */\n        ma_vec3f relativePosNormalized;\n        ma_vec3f relativePos;   /* The position relative to the listener. */\n        ma_vec3f relativeDir;   /* The direction of the sound, relative to the listener. */\n        ma_vec3f listenerVel;   /* The volocity of the listener. For doppler pitch calculation. */\n        float speedOfSound;\n        float distance = 0;\n        float gain = 1;\n        ma_uint32 iChannel;\n        const ma_uint32 channelsOut = pSpatializer->channelsOut;\n        const ma_uint32 channelsIn  = pSpatializer->channelsIn;\n        float minDistance = ma_spatializer_get_min_distance(pSpatializer);\n        float maxDistance = ma_spatializer_get_max_distance(pSpatializer);\n        float rolloff = ma_spatializer_get_rolloff(pSpatializer);\n        float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer);\n\n        /*\n        We'll need the listener velocity for doppler pitch calculations. The speed of sound is\n        defined by the listener, so we'll grab that here too.\n        */\n        if (pListener != NULL) {\n            listenerVel  = ma_spatializer_listener_get_velocity(pListener);\n            speedOfSound = pListener->config.speedOfSound;\n        } else {\n            listenerVel  = ma_vec3f_init_3f(0, 0, 0);\n            speedOfSound = MA_DEFAULT_SPEED_OF_SOUND;\n        }\n\n        if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {\n            /* There's no listener or we're using relative positioning. */\n            relativePos = ma_spatializer_get_position(pSpatializer);\n            relativeDir = ma_spatializer_get_direction(pSpatializer);\n        } else {\n            /*\n            We've found a listener and we're using absolute positioning. We need to transform the\n            sound's position and direction so that it's relative to listener. Later on we'll use\n            this for determining the factors to apply to each channel to apply the panning effect.\n            */\n            ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir);\n        }\n\n        distance = ma_vec3f_len(relativePos);\n\n        /* We've gathered the data, so now we can apply some spatialization. */\n        switch (ma_spatializer_get_attenuation_model(pSpatializer)) {\n            case ma_attenuation_model_inverse:\n            {\n                gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff);\n            } break;\n            case ma_attenuation_model_linear:\n            {\n                gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff);\n            } break;\n            case ma_attenuation_model_exponential:\n            {\n                gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff);\n            } break;\n            case ma_attenuation_model_none:\n            default:\n            {\n                gain = 1;\n            } break;\n        }\n\n        /* Normalize the position. */\n        if (distance > 0.001f) {\n            float distanceInv = 1/distance;\n            relativePosNormalized    = relativePos;\n            relativePosNormalized.x *= distanceInv;\n            relativePosNormalized.y *= distanceInv;\n            relativePosNormalized.z *= distanceInv;\n        } else {\n            distance = 0;\n            relativePosNormalized = ma_vec3f_init_3f(0, 0, 0);\n        }\n\n        /*\n        Angular attenuation.\n\n        Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure\n        this out for ourselves at the expense of possibly being inconsistent with other implementations.\n\n        To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We\n        just need to get the direction from the source to the listener and then do a dot product against that and the\n        direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer\n        angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than\n        the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.\n        */\n        if (distance > 0) {\n            /* Source anglular gain. */\n            float spatializerConeInnerAngle;\n            float spatializerConeOuterAngle;\n            float spatializerConeOuterGain;\n            ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain);\n\n            gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain);\n\n            /*\n            We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that\n            are positioned behind the listener. On default settings, this will have no effect.\n            */\n            if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) {\n                ma_vec3f listenerDirection;\n                float listenerInnerAngle;\n                float listenerOuterAngle;\n                float listenerOuterGain;\n\n                if (pListener->config.handedness == ma_handedness_right) {\n                    listenerDirection = ma_vec3f_init_3f(0, 0, -1);\n                } else {\n                    listenerDirection = ma_vec3f_init_3f(0, 0, +1);\n                }\n\n                listenerInnerAngle = pListener->config.coneInnerAngleInRadians;\n                listenerOuterAngle = pListener->config.coneOuterAngleInRadians;\n                listenerOuterGain  = pListener->config.coneOuterGain;\n\n                gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain);\n            }\n        } else {\n            /* The sound is right on top of the listener. Don't do any angular attenuation. */\n        }\n\n\n        /* Clamp the gain. */\n        gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer));\n\n        /*\n        The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel\n        gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions\n        to avoid harsh changes in gain.\n        */\n        for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {\n            pSpatializer->pNewChannelGainsOut[iChannel] = gain;\n        }\n\n        /*\n        Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore\n        the whole section of code here because we need to update some internal spatialization state.\n        */\n        if (ma_spatializer_listener_is_enabled(pListener)) {\n            ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default);\n        } else {\n            ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);\n        }\n\n\n        /*\n        Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for\n        when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the\n        gain to the final output.\n        */\n        /*printf(\"distance=%f; gain=%f\\n\", distance, gain);*/\n\n        /* We must have a valid channel map here to ensure we spatialize properly. */\n        MA_ASSERT(pChannelMapOut != NULL);\n\n        /*\n        We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being\n        to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that\n        the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and\n        seeing how it goes. There might be better ways to do this.\n\n        To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a\n        direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will\n        be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized\n        position of the sound.\n        */\n\n        /*\n        Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's\n        relation to the direction of the channel.\n        */\n        if (distance > 0) {\n            ma_vec3f unitPos = relativePos;\n            float distanceInv = 1/distance;\n            unitPos.x *= distanceInv;\n            unitPos.y *= distanceInv;\n            unitPos.z *= distanceInv;\n\n            for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {\n                ma_channel channelOut;\n                float d;\n                float dMin;\n\n                channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel);\n                if (ma_is_spatial_channel_position(channelOut)) {\n                    d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer));\n                } else {\n                    d = 1;  /* It's not a spatial channel so there's no real notion of direction. */\n                }\n\n                /*\n                In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable.\n                The \"dMin\" variable below is used to control the aggressiveness of the panning effect. When set to\n                0, panning will be most extreme and any sounds that are positioned on the opposite side of the\n                speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it\n                doesn't even remotely represent the real world at all because sounds that come from your right side\n                are still clearly audible from your left side. Setting \"dMin\" to 1 will result in no panning at\n                all, which is also not ideal. By setting it to something greater than 0, the spatialization effect\n                becomes much less dramatic and a lot more bearable.\n\n                Summary: 0 = more extreme panning; 1 = no panning.\n                */\n                dMin = pSpatializer->minSpatializationChannelGain;\n\n                /*\n                At this point, \"d\" will be positive if the sound is on the same side as the channel and negative if\n                it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to\n                calculate a panning value. The first is to simply convert it to 0..1, however this has a problem\n                which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right\n                in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like\n                the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front\n                of the listener. I would intuitively expect that to be played at full volume, or close to it.\n\n                The second idea I think of is to only apply a reduction in gain when the sound is on the opposite\n                side of the speaker. That is, reduce the gain only when the dot product is negative. The problem\n                with this is that there will not be any attenuation as the sound sweeps around the 180 degrees\n                where the dot product is positive. The idea with this option is that you leave the gain at 1 when\n                the sound is being played on the same side as the speaker and then you just reduce the volume when\n                the sound is on the other side.\n\n                The summarize, I think the first option should give a better sense of spatialization, but the second\n                option is better for preserving the sound's power.\n\n                UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a\n                bit better, but you can also hear the reduction in volume when it's right in front.\n                */\n                #if 1\n                {\n                    /*\n                    Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power\n                    by being played at 0.5 gain.\n                    */\n                    d = (d + 1) * 0.5f;  /* -1..1 to 0..1 */\n                    d = ma_max(d, dMin);\n                    pSpatializer->pNewChannelGainsOut[iChannel] *= d;\n                }\n                #else\n                {\n                    /*\n                    Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more\n                    consistent, but comes at the expense of a worse sense of space and positioning.\n                    */\n                    if (d < 0) {\n                        d += 1; /* Move into the positive range. */\n                        d = ma_max(d, dMin);\n                        channelGainsOut[iChannel] *= d;\n                    }\n                }\n                #endif\n            }\n        } else {\n            /* Assume the sound is right on top of us. Don't do any panning. */\n        }\n\n        /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */\n        ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut);\n        ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount);\n\n        /*\n        Before leaving we'll want to update our doppler pitch so that the caller can apply some\n        pitch shifting if they desire. Note that we need to negate the relative position here\n        because the doppler calculation needs to be source-to-listener, but ours is listener-to-\n        source.\n        */\n        if (dopplerFactor > 0) {\n            pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(ma_spatializer_listener_get_position(pListener), ma_spatializer_get_position(pSpatializer)), ma_spatializer_get_velocity(pSpatializer), listenerVel, speedOfSound, dopplerFactor);\n        } else {\n            pSpatializer->dopplerPitch = 1;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume)\n{\n    if (pSpatializer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_gainer_set_master_volume(&pSpatializer->gainer, volume);\n}\n\nMA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume)\n{\n    if (pSpatializer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume);\n}\n\nMA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return 0;\n    }\n\n    return pSpatializer->channelsIn;\n}\n\nMA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return 0;\n    }\n\n    return pSpatializer->channelsOut;\n}\n\nMA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel);\n}\n\nMA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return ma_attenuation_model_none;\n    }\n\n    return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel);\n}\n\nMA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_i32(&pSpatializer->positioning, positioning);\n}\n\nMA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return ma_positioning_absolute;\n    }\n\n    return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning);\n}\n\nMA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff);\n}\n\nMA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return 0;\n    }\n\n    return ma_atomic_load_f32(&pSpatializer->rolloff);\n}\n\nMA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_f32(&pSpatializer->minGain, minGain);\n}\n\nMA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return 0;\n    }\n\n    return ma_atomic_load_f32(&pSpatializer->minGain);\n}\n\nMA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain);\n}\n\nMA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return 0;\n    }\n\n    return ma_atomic_load_f32(&pSpatializer->maxGain);\n}\n\nMA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance);\n}\n\nMA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return 0;\n    }\n\n    return ma_atomic_load_f32(&pSpatializer->minDistance);\n}\n\nMA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance);\n}\n\nMA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return 0;\n    }\n\n    return ma_atomic_load_f32(&pSpatializer->maxDistance);\n}\n\nMA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians);\n    ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians);\n    ma_atomic_exchange_f32(&pSpatializer->coneOuterGain,           outerGain);\n}\n\nMA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    if (pInnerAngleInRadians != NULL) {\n        *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians);\n    }\n\n    if (pOuterAngleInRadians != NULL) {\n        *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians);\n    }\n\n    if (pOuterGain != NULL) {\n        *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain);\n    }\n}\n\nMA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor);\n}\n\nMA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return 1;\n    }\n\n    return ma_atomic_load_f32(&pSpatializer->dopplerFactor);\n}\n\nMA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor);\n}\n\nMA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return 1;\n    }\n\n    return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor);\n}\n\nMA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z));\n}\n\nMA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return ma_vec3f_init_3f(0, 0, 0);\n    }\n\n    return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->position);  /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */\n}\n\nMA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z));\n}\n\nMA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return ma_vec3f_init_3f(0, 0, -1);\n    }\n\n    return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */\n}\n\nMA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z)\n{\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z));\n}\n\nMA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer)\n{\n    if (pSpatializer == NULL) {\n        return ma_vec3f_init_3f(0, 0, 0);\n    }\n\n    return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->velocity);  /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */\n}\n\nMA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir)\n{\n    if (pRelativePos != NULL) {\n        pRelativePos->x = 0;\n        pRelativePos->y = 0;\n        pRelativePos->z = 0;\n    }\n\n    if (pRelativeDir != NULL) {\n        pRelativeDir->x = 0;\n        pRelativeDir->y = 0;\n        pRelativeDir->z = -1;\n    }\n\n    if (pSpatializer == NULL) {\n        return;\n    }\n\n    if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {\n        /* There's no listener or we're using relative positioning. */\n        if (pRelativePos != NULL) {\n            *pRelativePos = ma_spatializer_get_position(pSpatializer);\n        }\n        if (pRelativeDir != NULL) {\n            *pRelativeDir = ma_spatializer_get_direction(pSpatializer);\n        }\n    } else {\n        ma_vec3f spatializerPosition;\n        ma_vec3f spatializerDirection;\n        ma_vec3f listenerPosition;\n        ma_vec3f listenerDirection;\n        ma_vec3f v;\n        ma_vec3f axisX;\n        ma_vec3f axisY;\n        ma_vec3f axisZ;\n        float m[4][4];\n\n        spatializerPosition  = ma_spatializer_get_position(pSpatializer);\n        spatializerDirection = ma_spatializer_get_direction(pSpatializer);\n        listenerPosition     = ma_spatializer_listener_get_position(pListener);\n        listenerDirection    = ma_spatializer_listener_get_direction(pListener);\n\n        /*\n        We need to calcualte the right vector from our forward and up vectors. This is done with\n        a cross product.\n        */\n        axisZ = ma_vec3f_normalize(listenerDirection);                                  /* Normalization required here because we can't trust the caller. */\n        axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp));   /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */\n\n        /*\n        The calculation of axisX above can result in a zero-length vector if the listener is\n        looking straight up on the Y axis. We'll need to fall back to a +X in this case so that\n        the calculations below don't fall apart. This is where a quaternion based listener and\n        sound orientation would come in handy.\n        */\n        if (ma_vec3f_len2(axisX) == 0) {\n            axisX = ma_vec3f_init_3f(1, 0, 0);\n        }\n\n        axisY = ma_vec3f_cross(axisX, axisZ);                                           /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */\n\n        /*\n        We need to swap the X axis if we're left handed because otherwise the cross product above\n        will have resulted in it pointing in the wrong direction (right handed was assumed in the\n        cross products above).\n        */\n        if (pListener->config.handedness == ma_handedness_left) {\n            axisX = ma_vec3f_neg(axisX);\n        }\n\n        /* Lookat. */\n        m[0][0] =  axisX.x; m[1][0] =  axisX.y; m[2][0] =  axisX.z; m[3][0] = -ma_vec3f_dot(axisX,               listenerPosition);\n        m[0][1] =  axisY.x; m[1][1] =  axisY.y; m[2][1] =  axisY.z; m[3][1] = -ma_vec3f_dot(axisY,               listenerPosition);\n        m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), listenerPosition);\n        m[0][3] = 0;        m[1][3] = 0;        m[2][3] = 0;        m[3][3] = 1;\n\n        /*\n        Multiply the lookat matrix by the spatializer position to transform it to listener\n        space. This allows calculations to work based on the sound being relative to the\n        origin which makes things simpler.\n        */\n        if (pRelativePos != NULL) {\n            v = spatializerPosition;\n            pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1;\n            pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1;\n            pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1;\n        }\n\n        /*\n        The direction of the sound needs to also be transformed so that it's relative to the\n        rotation of the listener.\n        */\n        if (pRelativeDir != NULL) {\n            v = spatializerDirection;\n            pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z;\n            pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z;\n            pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z;\n        }\n    }\n}\n\n\n\n\n/**************************************************************************************************************************************************************\n\nResampling\n\n**************************************************************************************************************************************************************/\nMA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)\n{\n    ma_linear_resampler_config config;\n    MA_ZERO_OBJECT(&config);\n    config.format           = format;\n    config.channels         = channels;\n    config.sampleRateIn     = sampleRateIn;\n    config.sampleRateOut    = sampleRateOut;\n    config.lpfOrder         = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);\n    config.lpfNyquistFactor = 1;\n\n    return config;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t x0Offset;\n    size_t x1Offset;\n    size_t lpfOffset;\n} ma_linear_resampler_heap_layout;\n\n\nstatic void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)\n{\n    /*\n    So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will\n    be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate.\n    */\n    ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut;  /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */\n    ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;\n\n    pResampler->inTimeFrac =\n         (oldRateTimeWhole * newSampleRateOut) +\n        ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut);\n\n    /* Make sure the fractional part is less than the output sample rate. */\n    pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut;\n    pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut;\n}\n\nstatic ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized)\n{\n    ma_result result;\n    ma_uint32 gcf;\n    ma_uint32 lpfSampleRate;\n    double lpfCutoffFrequency;\n    ma_lpf_config lpfConfig;\n    ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */\n\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (sampleRateIn == 0 || sampleRateOut == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    oldSampleRateOut = pResampler->config.sampleRateOut;\n\n    pResampler->config.sampleRateIn  = sampleRateIn;\n    pResampler->config.sampleRateOut = sampleRateOut;\n\n    /* Simplify the sample rate. */\n    gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut);\n    pResampler->config.sampleRateIn  /= gcf;\n    pResampler->config.sampleRateOut /= gcf;\n\n    /* Always initialize the low-pass filter, even when the order is 0. */\n    if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) {\n        return MA_INVALID_ARGS;\n    }\n\n    lpfSampleRate      = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut));\n    lpfCutoffFrequency = (   double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor);\n\n    lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder);\n\n    /*\n    If the resampler is alreay initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames\n    getting cleared. Instead we re-initialize the filter which will maintain any cached frames.\n    */\n    if (isResamplerAlreadyInitialized) {\n        result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf);\n    } else {\n        result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf);\n    }\n\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n\n    pResampler->inAdvanceInt  = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;\n    pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;\n\n    /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */\n    ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout)\n{\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->channels == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* x0 */\n    pHeapLayout->x0Offset = pHeapLayout->sizeInBytes;\n    if (pConfig->format == ma_format_f32) {\n        pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;\n    } else {\n        pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;\n    }\n\n    /* x1 */\n    pHeapLayout->x1Offset = pHeapLayout->sizeInBytes;\n    if (pConfig->format == ma_format_f32) {\n        pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;\n    } else {\n        pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;\n    }\n\n    /* LPF */\n    pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes);\n    {\n        ma_result result;\n        size_t lpfHeapSizeInBytes;\n        ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder);  /* Sample rate and cutoff frequency do not matter. */\n\n        result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHeapLayout->sizeInBytes += lpfHeapSizeInBytes;\n    }\n\n    /* Make sure allocation size is aligned. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_linear_resampler_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler)\n{\n    ma_result result;\n    ma_linear_resampler_heap_layout heapLayout;\n\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pResampler);\n\n    result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pResampler->config = *pConfig;\n\n    pResampler->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    if (pConfig->format == ma_format_f32) {\n        pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset);\n        pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset);\n    } else {\n        pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset);\n        pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset);\n    }\n\n    /* Setting the rate will set up the filter and time advances for us. */\n    result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pResampler->inTimeInt  = 1;  /* Set this to one to force an input sample to always be loaded for the first output frame. */\n    pResampler->inTimeFrac = 0;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pResampler->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pResampler == NULL) {\n        return;\n    }\n\n    ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks);\n\n    if (pResampler->_ownsHeap) {\n        ma_free(pResampler->_pHeap, pAllocationCallbacks);\n    }\n}\n\nstatic MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift)\n{\n    ma_int32 b;\n    ma_int32 c;\n    ma_int32 r;\n\n    MA_ASSERT(a <= (1<<shift));\n\n    b = x * ((1<<shift) - a);\n    c = y * a;\n    r = b + c;\n\n    return (ma_int16)(r >> shift);\n}\n\nstatic void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut)\n{\n    ma_uint32 c;\n    ma_uint32 a;\n    const ma_uint32 channels = pResampler->config.channels;\n    const ma_uint32 shift = 12;\n\n    MA_ASSERT(pResampler != NULL);\n    MA_ASSERT(pFrameOut  != NULL);\n\n    a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut;\n\n    MA_ASSUME(channels > 0);\n    for (c = 0; c < channels; c += 1) {\n        ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift);\n        pFrameOut[c] = s;\n    }\n}\n\n\nstatic void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut)\n{\n    ma_uint32 c;\n    float a;\n    const ma_uint32 channels = pResampler->config.channels;\n\n    MA_ASSERT(pResampler != NULL);\n    MA_ASSERT(pFrameOut  != NULL);\n\n    a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut;\n\n    MA_ASSUME(channels > 0);\n    for (c = 0; c < channels; c += 1) {\n        float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a);\n        pFrameOut[c] = s;\n    }\n}\n\nstatic ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    const ma_int16* pFramesInS16;\n    /* */ ma_int16* pFramesOutS16;\n    ma_uint64 frameCountIn;\n    ma_uint64 frameCountOut;\n    ma_uint64 framesProcessedIn;\n    ma_uint64 framesProcessedOut;\n\n    MA_ASSERT(pResampler     != NULL);\n    MA_ASSERT(pFrameCountIn  != NULL);\n    MA_ASSERT(pFrameCountOut != NULL);\n\n    pFramesInS16       = (const ma_int16*)pFramesIn;\n    pFramesOutS16      = (      ma_int16*)pFramesOut;\n    frameCountIn       = *pFrameCountIn;\n    frameCountOut      = *pFrameCountOut;\n    framesProcessedIn  = 0;\n    framesProcessedOut = 0;\n\n    while (framesProcessedOut < frameCountOut) {\n        /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */\n        while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {\n            ma_uint32 iChannel;\n\n            if (pFramesInS16 != NULL) {\n                for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {\n                    pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];\n                    pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];\n                }\n                pFramesInS16 += pResampler->config.channels;\n            } else {\n                for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {\n                    pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];\n                    pResampler->x1.s16[iChannel] = 0;\n                }\n            }\n\n            /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */\n            if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {\n                ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16);\n            }\n\n            framesProcessedIn     += 1;\n            pResampler->inTimeInt -= 1;\n        }\n\n        if (pResampler->inTimeInt > 0) {\n            break;  /* Ran out of input data. */\n        }\n\n        /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */\n        if (pFramesOutS16 != NULL) {\n            MA_ASSERT(pResampler->inTimeInt == 0);\n            ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);\n\n            pFramesOutS16 += pResampler->config.channels;\n        }\n\n        framesProcessedOut += 1;\n\n        /* Advance time forward. */\n        pResampler->inTimeInt  += pResampler->inAdvanceInt;\n        pResampler->inTimeFrac += pResampler->inAdvanceFrac;\n        if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {\n            pResampler->inTimeFrac -= pResampler->config.sampleRateOut;\n            pResampler->inTimeInt  += 1;\n        }\n    }\n\n    *pFrameCountIn  = framesProcessedIn;\n    *pFrameCountOut = framesProcessedOut;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    const ma_int16* pFramesInS16;\n    /* */ ma_int16* pFramesOutS16;\n    ma_uint64 frameCountIn;\n    ma_uint64 frameCountOut;\n    ma_uint64 framesProcessedIn;\n    ma_uint64 framesProcessedOut;\n\n    MA_ASSERT(pResampler     != NULL);\n    MA_ASSERT(pFrameCountIn  != NULL);\n    MA_ASSERT(pFrameCountOut != NULL);\n\n    pFramesInS16       = (const ma_int16*)pFramesIn;\n    pFramesOutS16      = (      ma_int16*)pFramesOut;\n    frameCountIn       = *pFrameCountIn;\n    frameCountOut      = *pFrameCountOut;\n    framesProcessedIn  = 0;\n    framesProcessedOut = 0;\n\n    while (framesProcessedOut < frameCountOut) {\n        /* Before interpolating we need to load the buffers. */\n        while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {\n            ma_uint32 iChannel;\n\n            if (pFramesInS16 != NULL) {\n                for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {\n                    pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];\n                    pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];\n                }\n                pFramesInS16 += pResampler->config.channels;\n            } else {\n                for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {\n                    pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];\n                    pResampler->x1.s16[iChannel] = 0;\n                }\n            }\n\n            framesProcessedIn     += 1;\n            pResampler->inTimeInt -= 1;\n        }\n\n        if (pResampler->inTimeInt > 0) {\n            break;  /* Ran out of input data. */\n        }\n\n        /* Getting here means the frames have been loaded and we can generate the next output frame. */\n        if (pFramesOutS16 != NULL) {\n            MA_ASSERT(pResampler->inTimeInt == 0);\n            ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);\n\n            /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */\n            if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {\n                ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16);\n            }\n\n            pFramesOutS16 += pResampler->config.channels;\n        }\n\n        framesProcessedOut += 1;\n\n        /* Advance time forward. */\n        pResampler->inTimeInt  += pResampler->inAdvanceInt;\n        pResampler->inTimeFrac += pResampler->inAdvanceFrac;\n        if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {\n            pResampler->inTimeFrac -= pResampler->config.sampleRateOut;\n            pResampler->inTimeInt  += 1;\n        }\n    }\n\n    *pFrameCountIn  = framesProcessedIn;\n    *pFrameCountOut = framesProcessedOut;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    MA_ASSERT(pResampler != NULL);\n\n    if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {\n        return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n    } else {\n        return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n    }\n}\n\n\nstatic ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    const float* pFramesInF32;\n    /* */ float* pFramesOutF32;\n    ma_uint64 frameCountIn;\n    ma_uint64 frameCountOut;\n    ma_uint64 framesProcessedIn;\n    ma_uint64 framesProcessedOut;\n\n    MA_ASSERT(pResampler     != NULL);\n    MA_ASSERT(pFrameCountIn  != NULL);\n    MA_ASSERT(pFrameCountOut != NULL);\n\n    pFramesInF32       = (const float*)pFramesIn;\n    pFramesOutF32      = (      float*)pFramesOut;\n    frameCountIn       = *pFrameCountIn;\n    frameCountOut      = *pFrameCountOut;\n    framesProcessedIn  = 0;\n    framesProcessedOut = 0;\n\n    while (framesProcessedOut < frameCountOut) {\n        /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */\n        while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {\n            ma_uint32 iChannel;\n\n            if (pFramesInF32 != NULL) {\n                for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {\n                    pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];\n                    pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];\n                }\n                pFramesInF32 += pResampler->config.channels;\n            } else {\n                for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {\n                    pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];\n                    pResampler->x1.f32[iChannel] = 0;\n                }\n            }\n\n            /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */\n            if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {\n                ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32);\n            }\n\n            framesProcessedIn     += 1;\n            pResampler->inTimeInt -= 1;\n        }\n\n        if (pResampler->inTimeInt > 0) {\n            break;  /* Ran out of input data. */\n        }\n\n        /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */\n        if (pFramesOutF32 != NULL) {\n            MA_ASSERT(pResampler->inTimeInt == 0);\n            ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);\n\n            pFramesOutF32 += pResampler->config.channels;\n        }\n\n        framesProcessedOut += 1;\n\n        /* Advance time forward. */\n        pResampler->inTimeInt  += pResampler->inAdvanceInt;\n        pResampler->inTimeFrac += pResampler->inAdvanceFrac;\n        if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {\n            pResampler->inTimeFrac -= pResampler->config.sampleRateOut;\n            pResampler->inTimeInt  += 1;\n        }\n    }\n\n    *pFrameCountIn  = framesProcessedIn;\n    *pFrameCountOut = framesProcessedOut;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    const float* pFramesInF32;\n    /* */ float* pFramesOutF32;\n    ma_uint64 frameCountIn;\n    ma_uint64 frameCountOut;\n    ma_uint64 framesProcessedIn;\n    ma_uint64 framesProcessedOut;\n\n    MA_ASSERT(pResampler     != NULL);\n    MA_ASSERT(pFrameCountIn  != NULL);\n    MA_ASSERT(pFrameCountOut != NULL);\n\n    pFramesInF32       = (const float*)pFramesIn;\n    pFramesOutF32      = (      float*)pFramesOut;\n    frameCountIn       = *pFrameCountIn;\n    frameCountOut      = *pFrameCountOut;\n    framesProcessedIn  = 0;\n    framesProcessedOut = 0;\n\n    while (framesProcessedOut < frameCountOut) {\n        /* Before interpolating we need to load the buffers. */\n        while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {\n            ma_uint32 iChannel;\n\n            if (pFramesInF32 != NULL) {\n                for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {\n                    pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];\n                    pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];\n                }\n                pFramesInF32 += pResampler->config.channels;\n            } else {\n                for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {\n                    pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];\n                    pResampler->x1.f32[iChannel] = 0;\n                }\n            }\n\n            framesProcessedIn     += 1;\n            pResampler->inTimeInt -= 1;\n        }\n\n        if (pResampler->inTimeInt > 0) {\n            break;  /* Ran out of input data. */\n        }\n\n        /* Getting here means the frames have been loaded and we can generate the next output frame. */\n        if (pFramesOutF32 != NULL) {\n            MA_ASSERT(pResampler->inTimeInt == 0);\n            ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);\n\n            /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */\n            if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {\n                ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32);\n            }\n\n            pFramesOutF32 += pResampler->config.channels;\n        }\n\n        framesProcessedOut += 1;\n\n        /* Advance time forward. */\n        pResampler->inTimeInt  += pResampler->inAdvanceInt;\n        pResampler->inTimeFrac += pResampler->inAdvanceFrac;\n        if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {\n            pResampler->inTimeFrac -= pResampler->config.sampleRateOut;\n            pResampler->inTimeInt  += 1;\n        }\n    }\n\n    *pFrameCountIn  = framesProcessedIn;\n    *pFrameCountOut = framesProcessedOut;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    MA_ASSERT(pResampler != NULL);\n\n    if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {\n        return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n    } else {\n        return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n    }\n}\n\n\nMA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /*  */ if (pResampler->config.format == ma_format_s16) {\n        return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n    } else if (pResampler->config.format == ma_format_f32) {\n        return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n    } else {\n        /* Should never get here. Getting here means the format is not supported and you didn't check the return value of ma_linear_resampler_init(). */\n        MA_ASSERT(MA_FALSE);\n        return MA_INVALID_ARGS;\n    }\n}\n\n\nMA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)\n{\n    return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE);\n}\n\nMA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut)\n{\n    ma_uint32 n;\n    ma_uint32 d;\n\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (ratioInOut <= 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    d = 1000000;\n    n = (ma_uint32)(ratioInOut * d);\n\n    if (n == 0) {\n        return MA_INVALID_ARGS; /* Ratio too small. */\n    }\n\n    MA_ASSERT(n != 0);\n\n    return ma_linear_resampler_set_rate(pResampler, n, d);\n}\n\nMA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler)\n{\n    if (pResampler == NULL) {\n        return 0;\n    }\n\n    return 1 + ma_lpf_get_latency(&pResampler->lpf);\n}\n\nMA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler)\n{\n    if (pResampler == NULL) {\n        return 0;\n    }\n\n    return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn;\n}\n\nMA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)\n{\n    ma_uint64 inputFrameCount;\n\n    if (pInputFrameCount == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pInputFrameCount = 0;\n\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (outputFrameCount == 0) {\n        return MA_SUCCESS;\n    }\n\n    /* Any whole input frames are consumed before the first output frame is generated. */\n    inputFrameCount = pResampler->inTimeInt;\n    outputFrameCount -= 1;\n\n    /* The rest of the output frames can be calculated in constant time. */\n    inputFrameCount += outputFrameCount * pResampler->inAdvanceInt;\n    inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut;\n\n    *pInputFrameCount = inputFrameCount;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)\n{\n    ma_uint64 outputFrameCount;\n    ma_uint64 preliminaryInputFrameCountFromFrac;\n    ma_uint64 preliminaryInputFrameCount;\n\n    if (pOutputFrameCount == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pOutputFrameCount = 0;\n\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /*\n    The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to\n    determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't\n    be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation\n    of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames.\n    */\n    outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn;\n\n    /*\n    We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is\n    used in the logic below to determine whether or not we need to add an extra output frame.\n    */\n    preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut;\n    preliminaryInputFrameCount         = (pResampler->inTimeInt  + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac;\n\n    /*\n    If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than\n    the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data\n    to actually process. Otherwise we need to add the extra output frame.\n    */\n    if (preliminaryInputFrameCount <= inputFrameCount) {\n        outputFrameCount += 1;\n    }\n\n    *pOutputFrameCount = outputFrameCount;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler)\n{\n    ma_uint32 iChannel;\n\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Timers need to be cleared back to zero. */\n    pResampler->inTimeInt  = 1;  /* Set this to one to force an input sample to always be loaded for the first output frame. */\n    pResampler->inTimeFrac = 0;\n\n    /* Cached samples need to be cleared. */\n    if (pResampler->config.format == ma_format_f32) {\n        for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {\n            pResampler->x0.f32[iChannel] = 0;\n            pResampler->x1.f32[iChannel] = 0;\n        }\n    } else {\n        for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {\n            pResampler->x0.s16[iChannel] = 0;\n            pResampler->x1.s16[iChannel] = 0;\n        }\n    }\n\n    /* The low pass filter needs to have it's cache reset. */\n    ma_lpf_clear_cache(&pResampler->lpf);\n\n    return MA_SUCCESS;\n}\n\n\n\n/* Linear resampler backend vtable. */\nstatic ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig)\n{\n    ma_linear_resampler_config linearConfig;\n\n    linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);\n    linearConfig.lpfOrder = pConfig->linear.lpfOrder;\n\n    return linearConfig;\n}\n\nstatic ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_linear_resampler_config linearConfig;\n\n    (void)pUserData;\n\n    linearConfig = ma_resampling_backend_get_config__linear(pConfig);\n\n    return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes);\n}\n\nstatic ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend)\n{\n    ma_resampler* pResampler = (ma_resampler*)pUserData;\n    ma_result result;\n    ma_linear_resampler_config linearConfig;\n\n    (void)pUserData;\n\n    linearConfig = ma_resampling_backend_get_config__linear(pConfig);\n\n    result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *ppBackend = &pResampler->state.linear;\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    (void)pUserData;\n\n    ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks);\n}\n\nstatic ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    (void)pUserData;\n\n    return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n}\n\nstatic ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)\n{\n    (void)pUserData;\n\n    return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut);\n}\n\nstatic ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)\n{\n    (void)pUserData;\n\n    return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend);\n}\n\nstatic ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)\n{\n    (void)pUserData;\n\n    return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend);\n}\n\nstatic ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)\n{\n    (void)pUserData;\n\n    return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount);\n}\n\nstatic ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)\n{\n    (void)pUserData;\n\n    return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount);\n}\n\nstatic ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend)\n{\n    (void)pUserData;\n\n    return ma_linear_resampler_reset((ma_linear_resampler*)pBackend);\n}\n\nstatic ma_resampling_backend_vtable g_ma_linear_resampler_vtable =\n{\n    ma_resampling_backend_get_heap_size__linear,\n    ma_resampling_backend_init__linear,\n    ma_resampling_backend_uninit__linear,\n    ma_resampling_backend_process__linear,\n    ma_resampling_backend_set_rate__linear,\n    ma_resampling_backend_get_input_latency__linear,\n    ma_resampling_backend_get_output_latency__linear,\n    ma_resampling_backend_get_required_input_frame_count__linear,\n    ma_resampling_backend_get_expected_output_frame_count__linear,\n    ma_resampling_backend_reset__linear\n};\n\n\n\nMA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm)\n{\n    ma_resampler_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format = format;\n    config.channels = channels;\n    config.sampleRateIn = sampleRateIn;\n    config.sampleRateOut = sampleRateOut;\n    config.algorithm = algorithm;\n\n    /* Linear. */\n    config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);\n\n    return config;\n}\n\nstatic ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData)\n{\n    MA_ASSERT(pConfig    != NULL);\n    MA_ASSERT(ppVTable   != NULL);\n    MA_ASSERT(ppUserData != NULL);\n\n    /* Safety. */\n    *ppVTable   = NULL;\n    *ppUserData = NULL;\n\n    switch (pConfig->algorithm)\n    {\n        case ma_resample_algorithm_linear:\n        {\n            *ppVTable   = &g_ma_linear_resampler_vtable;\n            *ppUserData = pResampler;\n        } break;\n\n        case ma_resample_algorithm_custom:\n        {\n            *ppVTable   = pConfig->pBackendVTable;\n            *ppUserData = pConfig->pBackendUserData;\n        } break;\n\n        default: return MA_INVALID_ARGS;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_resampling_backend_vtable* pVTable;\n    void* pVTableUserData;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pVTable == NULL || pVTable->onGetHeapSize == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler)\n{\n    ma_result result;\n\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pResampler);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pResampler->_pHeap        = pHeap;\n    pResampler->format        = pConfig->format;\n    pResampler->channels      = pConfig->channels;\n    pResampler->sampleRateIn  = pConfig->sampleRateIn;\n    pResampler->sampleRateOut = pConfig->sampleRateOut;\n\n    result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) {\n        return MA_NOT_IMPLEMENTED;  /* onInit not implemented. */\n    }\n\n    result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pResampler->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pResampler == NULL) {\n        return;\n    }\n\n    if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) {\n        return;\n    }\n\n    pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks);\n\n    if (pResampler->_ownsHeap) {\n        ma_free(pResampler->_pHeap, pAllocationCallbacks);\n    }\n}\n\nMA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pFrameCountOut == NULL && pFrameCountIn == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n}\n\nMA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)\n{\n    ma_result result;\n\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (sampleRateIn == 0 || sampleRateOut == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pResampler->sampleRateIn  = sampleRateIn;\n    pResampler->sampleRateOut = sampleRateOut;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio)\n{\n    ma_uint32 n;\n    ma_uint32 d;\n\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (ratio <= 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    d = 1000;\n    n = (ma_uint32)(ratio * d);\n\n    if (n == 0) {\n        return MA_INVALID_ARGS; /* Ratio too small. */\n    }\n\n    MA_ASSERT(n != 0);\n\n    return ma_resampler_set_rate(pResampler, n, d);\n}\n\nMA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler)\n{\n    if (pResampler == NULL) {\n        return 0;\n    }\n\n    if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) {\n        return 0;\n    }\n\n    return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend);\n}\n\nMA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler)\n{\n    if (pResampler == NULL) {\n        return 0;\n    }\n\n    if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) {\n        return 0;\n    }\n\n    return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend);\n}\n\nMA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)\n{\n    if (pInputFrameCount == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pInputFrameCount = 0;\n\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount);\n}\n\nMA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)\n{\n    if (pOutputFrameCount == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pOutputFrameCount = 0;\n\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount);\n}\n\nMA_API ma_result ma_resampler_reset(ma_resampler* pResampler)\n{\n    if (pResampler == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend);\n}\n\n/**************************************************************************************************************************************************************\n\nChannel Conversion\n\n**************************************************************************************************************************************************************/\n#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT\n#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT  12\n#endif\n\n#define MA_PLANE_LEFT      0\n#define MA_PLANE_RIGHT     1\n#define MA_PLANE_FRONT     2\n#define MA_PLANE_BACK      3\n#define MA_PLANE_BOTTOM    4\n#define MA_PLANE_TOP       5\n\nstatic float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_NONE */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_MONO */\n    { 0.5f,  0.0f,  0.5f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_FRONT_LEFT */\n    { 0.0f,  0.5f,  0.5f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_FRONT_RIGHT */\n    { 0.0f,  0.0f,  1.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_FRONT_CENTER */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_LFE */\n    { 0.5f,  0.0f,  0.0f,  0.5f,  0.0f,  0.0f},  /* MA_CHANNEL_BACK_LEFT */\n    { 0.0f,  0.5f,  0.0f,  0.5f,  0.0f,  0.0f},  /* MA_CHANNEL_BACK_RIGHT */\n    { 0.25f, 0.0f,  0.75f, 0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_FRONT_LEFT_CENTER */\n    { 0.0f,  0.25f, 0.75f, 0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_FRONT_RIGHT_CENTER */\n    { 0.0f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f},  /* MA_CHANNEL_BACK_CENTER */\n    { 1.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_SIDE_LEFT */\n    { 0.0f,  1.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_SIDE_RIGHT */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f},  /* MA_CHANNEL_TOP_CENTER */\n    { 0.33f, 0.0f,  0.33f, 0.0f,  0.0f,  0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */\n    { 0.0f,  0.0f,  0.5f,  0.0f,  0.0f,  0.5f},  /* MA_CHANNEL_TOP_FRONT_CENTER */\n    { 0.0f,  0.33f, 0.33f, 0.0f,  0.0f,  0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */\n    { 0.33f, 0.0f,  0.0f,  0.33f, 0.0f,  0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */\n    { 0.0f,  0.0f,  0.0f,  0.5f,  0.0f,  0.5f},  /* MA_CHANNEL_TOP_BACK_CENTER */\n    { 0.0f,  0.33f, 0.0f,  0.33f, 0.0f,  0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_0 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_1 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_2 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_3 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_4 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_5 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_6 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_7 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_8 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_9 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_10 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_11 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_12 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_13 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_14 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_15 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_16 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_17 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_18 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_19 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_20 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_21 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_22 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_23 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_24 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_25 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_26 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_27 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_28 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_29 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_30 */\n    { 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f},  /* MA_CHANNEL_AUX_31 */\n};\n\nstatic float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)\n{\n    /*\n    Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to\n    the following output configuration:\n\n     - front/left\n     - side/left\n     - back/left\n\n    The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount\n    of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.\n\n    Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left\n    speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted\n    from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would\n    receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between\n    the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works\n    across 3 spatial dimensions.\n\n    The first thing to do is figure out how each speaker's volume is spread over each of plane:\n     - front/left:     2 planes (front and left)      = 1/2 = half it's total volume on each plane\n     - side/left:      1 plane (left only)            = 1/1 = entire volume from left plane\n     - back/left:      2 planes (back and left)       = 1/2 = half it's total volume on each plane\n     - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane\n\n    The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other\n    channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be\n    taken by the other to produce the final contribution.\n    */\n\n    /* Contribution = Sum(Volume to Give * Volume to Take) */\n    float contribution =\n        g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +\n        g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +\n        g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +\n        g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +\n        g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +\n        g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];\n\n    return contribution;\n}\n\nMA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode)\n{\n    ma_channel_converter_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format         = format;\n    config.channelsIn     = channelsIn;\n    config.channelsOut    = channelsOut;\n    config.pChannelMapIn  = pChannelMapIn;\n    config.pChannelMapOut = pChannelMapOut;\n    config.mixingMode     = mixingMode;\n\n    return config;\n}\n\nstatic ma_int32 ma_channel_converter_float_to_fixed(float x)\n{\n    return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT));\n}\n\nstatic ma_uint32 ma_channel_map_get_spatial_channel_count(const ma_channel* pChannelMap, ma_uint32 channels)\n{\n    ma_uint32 spatialChannelCount = 0;\n    ma_uint32 iChannel;\n\n    MA_ASSERT(pChannelMap != NULL);\n    MA_ASSERT(channels > 0);\n\n    for (iChannel = 0; iChannel < channels; ++iChannel) {\n        if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) {\n            spatialChannelCount++;\n        }\n    }\n\n    return spatialChannelCount;\n}\n\nstatic ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)\n{\n    int i;\n\n    if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {\n        return MA_FALSE;\n    }\n\n    if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) {\n        return MA_FALSE;\n    }\n\n    for (i = 0; i < 6; ++i) {   /* Each side of a cube. */\n        if (g_maChannelPlaneRatios[channelPosition][i] != 0) {\n            return MA_TRUE;\n        }\n    }\n\n    return MA_FALSE;\n}\n\n\nstatic ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut)\n{\n    if (channelsOut == channelsIn) {\n        return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut);\n    } else {\n        return MA_FALSE;    /* Channel counts differ, so cannot be a passthrough. */\n    }\n}\n\nstatic ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode)\n{\n    if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) {\n        return ma_channel_conversion_path_passthrough;\n    }\n\n    if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) {\n        return ma_channel_conversion_path_mono_out;\n    }\n\n    if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) {\n        return ma_channel_conversion_path_mono_in;\n    }\n\n    if (mode == ma_channel_mix_mode_custom_weights) {\n        return ma_channel_conversion_path_weights;\n    }\n\n    /*\n    We can use a simple shuffle if both channel maps have the same channel count and all channel\n    positions are present in both.\n    */\n    if (channelsIn == channelsOut) {\n        ma_uint32 iChannelIn;\n        ma_bool32 areAllChannelPositionsPresent = MA_TRUE;\n        for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) {\n            ma_bool32 isInputChannelPositionInOutput = MA_FALSE;\n            if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) {\n                isInputChannelPositionInOutput = MA_TRUE;\n                break;\n            }\n\n            if (!isInputChannelPositionInOutput) {\n                areAllChannelPositionsPresent = MA_FALSE;\n                break;\n            }\n        }\n\n        if (areAllChannelPositionsPresent) {\n            return ma_channel_conversion_path_shuffle;\n        }\n    }\n\n    /* Getting here means we'll need to use weights. */\n    return ma_channel_conversion_path_weights;\n}\n\n\nstatic ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable)\n{\n    ma_uint32 iChannelIn;\n    ma_uint32 iChannelOut;\n\n    if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    /*\n    When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the\n    input channel has more than one occurance of a channel position, the second one will be ignored.\n    */\n    for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) {\n        ma_channel channelOut;\n\n        /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */\n        pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL;\n\n        channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut);\n        for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) {\n            ma_channel channelIn;\n\n            channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn);\n            if (channelOut == channelIn) {\n                pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;\n                break;\n            }\n\n            /*\n            Getting here means the channels don't exactly match, but we are going to support some\n            relaxed matching for practicality. If, for example, there are two stereo channel maps,\n            but one uses front left/right and the other uses side left/right, it makes logical\n            sense to just map these. The way we'll do it is we'll check if there is a logical\n            corresponding mapping, and if so, apply it, but we will *not* break from the loop,\n            thereby giving the loop a chance to find an exact match later which will take priority.\n            */\n            switch (channelOut)\n            {\n                /* Left channels. */\n                case MA_CHANNEL_FRONT_LEFT:\n                case MA_CHANNEL_SIDE_LEFT:\n                {\n                    switch (channelIn) {\n                        case MA_CHANNEL_FRONT_LEFT:\n                        case MA_CHANNEL_SIDE_LEFT:\n                        {\n                            pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;\n                        } break;\n                    }\n                } break;\n\n                /* Right channels. */\n                case MA_CHANNEL_FRONT_RIGHT:\n                case MA_CHANNEL_SIDE_RIGHT:\n                {\n                    switch (channelIn) {\n                        case MA_CHANNEL_FRONT_RIGHT:\n                        case MA_CHANNEL_SIDE_RIGHT:\n                        {\n                            pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;\n                        } break;\n                    }\n                } break;\n\n                default: break;\n            }\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannelOut;\n\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n            ma_uint8 iChannelIn = pShuffleTable[iChannelOut];\n            if (iChannelIn < channelsIn) {  /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */\n                pFramesOut[iChannelOut] = pFramesIn[iChannelIn];\n            } else {\n                pFramesOut[iChannelOut] = 0;\n            }\n        }\n\n        pFramesOut += channelsOut;\n        pFramesIn  += channelsIn;\n    }\n}\n\nstatic void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannelOut;\n\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n            ma_uint8 iChannelIn = pShuffleTable[iChannelOut];\n            if (iChannelIn < channelsIn) {  /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */\n                pFramesOut[iChannelOut] = pFramesIn[iChannelIn];\n            } else {\n                pFramesOut[iChannelOut] = 0;\n            }\n        }\n\n        pFramesOut += channelsOut;\n        pFramesIn  += channelsIn;\n    }\n}\n\nstatic void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannelOut;\n\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n            ma_uint8 iChannelIn = pShuffleTable[iChannelOut];\n            if (iChannelIn < channelsIn) {  /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */\n                pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0];\n                pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1];\n                pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2];\n            } else {\n                pFramesOut[iChannelOut*3 + 0] = 0;\n            }   pFramesOut[iChannelOut*3 + 1] = 0;\n        }       pFramesOut[iChannelOut*3 + 2] = 0;\n\n        pFramesOut += channelsOut*3;\n        pFramesIn  += channelsIn*3;\n    }\n}\n\nstatic void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannelOut;\n\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n            ma_uint8 iChannelIn = pShuffleTable[iChannelOut];\n            if (iChannelIn < channelsIn) {  /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */\n                pFramesOut[iChannelOut] = pFramesIn[iChannelIn];\n            } else {\n                pFramesOut[iChannelOut] = 0;\n            }\n        }\n\n        pFramesOut += channelsOut;\n        pFramesIn  += channelsIn;\n    }\n}\n\nstatic void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannelOut;\n\n    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n        for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n            ma_uint8 iChannelIn = pShuffleTable[iChannelOut];\n            if (iChannelIn < channelsIn) {  /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */\n                pFramesOut[iChannelOut] = pFramesIn[iChannelIn];\n            } else {\n                pFramesOut[iChannelOut] = 0;\n            }\n        }\n\n        pFramesOut += channelsOut;\n        pFramesIn  += channelsIn;\n    }\n}\n\nstatic ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format)\n{\n    if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    switch (format)\n    {\n        case ma_format_u8:\n        {\n            ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);\n        } break;\n\n        case ma_format_s16:\n        {\n            ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable);\n        } break;\n\n        case ma_format_s24:\n        {\n            ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);\n        } break;\n\n        case ma_format_s32:\n        {\n            ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable);\n        } break;\n\n        case ma_format_f32:\n        {\n            ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable);\n        } break;\n\n        default: return MA_INVALID_ARGS;    /* Unknown format. */\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannelIn;\n    ma_uint32 accumulationCount;\n\n    if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* In this case the output stream needs to be the average of all channels, ignoring NONE. */\n\n    /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */\n    accumulationCount = 0;\n    for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {\n        if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) {\n            accumulationCount += 1;\n        }\n    }\n\n    if (accumulationCount > 0) {    /* <-- Prevent a division by zero. */\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            float accumulation = 0;\n\n            for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {\n                ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);\n                if (channelIn != MA_CHANNEL_NONE) {\n                    accumulation += pFramesIn[iChannelIn];\n                }\n            }\n\n            pFramesOut[0] = accumulation / accumulationCount;\n            pFramesOut += 1;\n            pFramesIn  += channelsIn;\n        }\n    } else {\n        ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* MA_RESTRICT pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannelOut;\n\n    if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */\n    switch (monoExpansionMode)\n    {\n        case ma_mono_expansion_mode_average:\n        {\n            float weight;\n            ma_uint32 validChannelCount = 0;\n\n            for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);\n                if (channelOut != MA_CHANNEL_NONE) {\n                    validChannelCount += 1;\n                }\n            }\n\n            weight = 1.0f / validChannelCount;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                    ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);\n                    if (channelOut != MA_CHANNEL_NONE) {\n                        pFramesOut[iChannelOut] = pFramesIn[0] * weight;\n                    }\n                }\n\n                pFramesOut += channelsOut;\n                pFramesIn  += 1;\n            }\n        } break;\n\n        case ma_mono_expansion_mode_stereo_only:\n        {\n            if (channelsOut >= 2) {\n                ma_uint32 iChannelLeft  = (ma_uint32)-1;\n                ma_uint32 iChannelRight = (ma_uint32)-1;\n\n                /*\n                We first need to find our stereo channels. We prefer front-left and front-right, but\n                if they're not available, we'll also try side-left and side-right. If neither are\n                available we'll fall through to the default case below.\n                */\n                for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                    ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);\n                    if (channelOut == MA_CHANNEL_SIDE_LEFT) {\n                        iChannelLeft  = iChannelOut;\n                    }\n                    if (channelOut == MA_CHANNEL_SIDE_RIGHT) {\n                        iChannelRight = iChannelOut;\n                    }\n                }\n\n                for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                    ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);\n                    if (channelOut == MA_CHANNEL_FRONT_LEFT) {\n                        iChannelLeft  = iChannelOut;\n                    }\n                    if (channelOut == MA_CHANNEL_FRONT_RIGHT) {\n                        iChannelRight = iChannelOut;\n                    }\n                }\n\n\n                if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) {\n                    /* We found our stereo channels so we can duplicate the signal across those channels. */\n                    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                        for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                            ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);\n                            if (channelOut != MA_CHANNEL_NONE) {\n                                if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) {\n                                    pFramesOut[iChannelOut] = pFramesIn[0];\n                                } else {\n                                    pFramesOut[iChannelOut] = 0.0f;\n                                }\n                            }\n                        }\n\n                        pFramesOut += channelsOut;\n                        pFramesIn  += 1;\n                    }\n\n                    break;  /* Get out of the switch. */\n                } else {\n                    /* Fallthrough. Does not have left and right channels. */\n                    goto default_handler;\n                }\n            } else {\n                /* Fallthrough. Does not have stereo channels. */\n                goto default_handler;\n            }\n        };  /* Fallthrough. See comments above. */\n\n        case ma_mono_expansion_mode_duplicate:\n        default:\n        {\n            default_handler:\n            {\n                if (channelsOut <= MA_MAX_CHANNELS) {\n                    ma_bool32 hasEmptyChannel = MA_FALSE;\n                    ma_channel channelPositions[MA_MAX_CHANNELS];\n                    for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                        channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);\n                        if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) {\n                            hasEmptyChannel = MA_TRUE;\n                        }\n                    }\n\n                    if (hasEmptyChannel == MA_FALSE) {\n                        /*\n                        Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully\n                        help the compiler with auto-vectorization.m\n                        */\n                        if (channelsOut == 2) {\n                        #if defined(MA_SUPPORT_SSE2)\n                            if (ma_has_sse2()) {\n                                /* We want to do two frames in each iteration. */\n                                ma_uint64 unrolledFrameCount = frameCount >> 1;\n\n                                for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) {\n                                    __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]);\n                                    __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]);\n                                    _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0)));\n                                }\n\n                                /* Tail. */\n                                iFrame = unrolledFrameCount << 1;\n                                goto generic_on_fastpath;\n                            } else\n                        #endif\n                            {\n                                for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                                    for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) {\n                                        pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame];\n                                    }\n                                }\n                            }\n                        } else if (channelsOut == 6) {\n                        #if defined(MA_SUPPORT_SSE2)\n                            if (ma_has_sse2()) {\n                                /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */\n                                ma_uint64 unrolledFrameCount = frameCount >> 1;\n\n                                for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) {\n                                    __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]);\n                                    __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]);\n\n                                    _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0);\n                                    _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0)));\n                                    _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1);\n                                }\n\n                                /* Tail. */\n                                iFrame = unrolledFrameCount << 1;\n                                goto generic_on_fastpath;\n                            } else\n                        #endif\n                            {\n                                for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                                    for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) {\n                                        pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame];\n                                    }\n                                }\n                            }\n                        } else if (channelsOut == 8) {\n                        #if defined(MA_SUPPORT_SSE2)\n                            if (ma_has_sse2()) {\n                                for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                                    __m128 in = _mm_set1_ps(pFramesIn[iFrame]);\n                                    _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in);\n                                    _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in);\n                                }\n                            } else\n                        #endif\n                            {\n                                for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                                    for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) {\n                                        pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame];\n                                    }\n                                }\n                            }\n                        } else {\n                            iFrame = 0;\n\n                            #if defined(MA_SUPPORT_SSE2)    /* For silencing a warning with non-x86 builds. */\n                            generic_on_fastpath:\n                            #endif\n                            {\n                                for (; iFrame < frameCount; iFrame += 1) {\n                                    for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                                        pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];\n                                    }\n                                }\n                            }\n                        }\n                    } else {\n                        /* Slow path. Need to handle MA_CHANNEL_NONE. */\n                        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                            for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                                if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) {\n                                    pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];\n                                }\n                            }\n                        }\n                    }\n                } else {\n                    /* Slow path. Too many channels to store on the stack. */\n                    for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                        for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                            ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);\n                            if (channelOut != MA_CHANNEL_NONE) {\n                                pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];\n                            }\n                        }\n                    }\n                }\n            }\n        } break;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode)\n{\n    ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode);\n\n    /* Optimized Path: Passthrough */\n    if (conversionPath == ma_channel_conversion_path_passthrough) {\n        ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut);\n        return;\n    }\n\n    /* Special Path: Mono Output. */\n    if (conversionPath == ma_channel_conversion_path_mono_out) {\n        ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount);\n        return;\n    }\n\n    /* Special Path: Mono Input. */\n    if (conversionPath == ma_channel_conversion_path_mono_in) {\n        ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode);\n        return;\n    }\n\n    /* Getting here means we aren't running on an optimized conversion path. */\n    if (channelsOut <= MA_MAX_CHANNELS) {\n        ma_result result;\n\n        if (mode == ma_channel_mix_mode_simple) {\n            ma_channel shuffleTable[MA_MAX_CHANNELS];\n\n            result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable);\n            if (result != MA_SUCCESS) {\n                return;\n            }\n\n            result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32);\n            if (result != MA_SUCCESS) {\n                return;\n            }\n        } else {\n            ma_uint32 iFrame;\n            ma_uint32 iChannelOut;\n            ma_uint32 iChannelIn;\n            float weights[32][32];  /* Do not use MA_MAX_CHANNELS here! */\n\n            /*\n            If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to\n            fall back to a slower path because otherwise we'll run out of stack space.\n            */\n            if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) {\n                /* Pre-compute weights. */\n                for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                    ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);\n                    for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {\n                        ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);\n                        weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);\n                    }\n                }\n\n                iFrame = 0;\n\n                /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */\n                if (channelsOut == 8) {\n                    /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */\n                    if (channelsIn == 2) {\n                        for (; iFrame < frameCount; iFrame += 1) {\n                            float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };\n\n                            accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0];\n                            accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0];\n                            accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0];\n                            accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0];\n                            accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0];\n                            accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0];\n                            accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0];\n                            accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0];\n\n                            accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1];\n                            accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1];\n                            accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1];\n                            accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1];\n                            accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1];\n                            accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1];\n                            accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1];\n                            accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1];\n\n                            pFramesOut[iFrame*8 + 0] = accumulation[0];\n                            pFramesOut[iFrame*8 + 1] = accumulation[1];\n                            pFramesOut[iFrame*8 + 2] = accumulation[2];\n                            pFramesOut[iFrame*8 + 3] = accumulation[3];\n                            pFramesOut[iFrame*8 + 4] = accumulation[4];\n                            pFramesOut[iFrame*8 + 5] = accumulation[5];\n                            pFramesOut[iFrame*8 + 6] = accumulation[6];\n                            pFramesOut[iFrame*8 + 7] = accumulation[7];\n                        }\n                    } else {\n                        /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */\n                        for (; iFrame < frameCount; iFrame += 1) {\n                            float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };\n\n                            for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {\n                                accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn];\n                                accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn];\n                                accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn];\n                                accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn];\n                                accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn];\n                                accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn];\n                                accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn];\n                                accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn];\n                            }\n\n                            pFramesOut[iFrame*8 + 0] = accumulation[0];\n                            pFramesOut[iFrame*8 + 1] = accumulation[1];\n                            pFramesOut[iFrame*8 + 2] = accumulation[2];\n                            pFramesOut[iFrame*8 + 3] = accumulation[3];\n                            pFramesOut[iFrame*8 + 4] = accumulation[4];\n                            pFramesOut[iFrame*8 + 5] = accumulation[5];\n                            pFramesOut[iFrame*8 + 6] = accumulation[6];\n                            pFramesOut[iFrame*8 + 7] = accumulation[7];\n                        }\n                    }\n                } else if (channelsOut == 6) {\n                    /*\n                    When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll\n                    expand our weights and do two frames at a time.\n                    */\n                    for (; iFrame < frameCount; iFrame += 1) {\n                        float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };\n\n                        for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {\n                            accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn];\n                            accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn];\n                            accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn];\n                            accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn];\n                            accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn];\n                            accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn];\n                        }\n\n                        pFramesOut[iFrame*6 + 0] = accumulation[0];\n                        pFramesOut[iFrame*6 + 1] = accumulation[1];\n                        pFramesOut[iFrame*6 + 2] = accumulation[2];\n                        pFramesOut[iFrame*6 + 3] = accumulation[3];\n                        pFramesOut[iFrame*6 + 4] = accumulation[4];\n                        pFramesOut[iFrame*6 + 5] = accumulation[5];\n                    }\n                }\n\n                /* Leftover frames. */\n                for (; iFrame < frameCount; iFrame += 1) {\n                    for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                        float accumulation = 0;\n\n                        for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {\n                            accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn];\n                        }\n\n                        pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation;\n                    }\n                }\n            } else {\n                /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */\n                for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                    for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {\n                        float accumulation = 0;\n                        ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);\n\n                        for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {\n                            ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);\n                            accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);\n                        }\n\n                        pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation;\n                    }\n                }\n            }\n        }\n    } else {\n        /* Fall back to silence. If you hit this, what are you doing with so many channels?! */\n        ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut);\n    }\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t channelMapInOffset;\n    size_t channelMapOutOffset;\n    size_t shuffleTableOffset;\n    size_t weightsOffset;\n} ma_channel_converter_heap_layout;\n\nstatic ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig)\n{\n    return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode);\n}\n\nstatic ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout)\n{\n    ma_channel_conversion_path conversionPath;\n\n    MA_ASSERT(pHeapLayout != NULL);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) {\n        return MA_INVALID_ARGS;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */\n    pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;\n    if (pConfig->pChannelMapIn != NULL) {\n        pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn;\n    }\n\n    /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */\n    pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;\n    if (pConfig->pChannelMapOut != NULL) {\n        pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut;\n    }\n\n    /* Alignment for the next section. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */\n    conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);\n\n    /* Shuffle table */\n    pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes;\n    if (conversionPath == ma_channel_conversion_path_shuffle) {\n        pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut;\n    }\n\n    /* Weights */\n    pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes;\n    if (conversionPath == ma_channel_conversion_path_weights) {\n        pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn;\n        pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut;\n    }\n\n    /* Make sure allocation size is aligned. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_channel_converter_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter)\n{\n    ma_result result;\n    ma_channel_converter_heap_layout heapLayout;\n\n    if (pConverter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pConverter);\n\n    result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pConverter->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes);\n\n    pConverter->format      = pConfig->format;\n    pConverter->channelsIn  = pConfig->channelsIn;\n    pConverter->channelsOut = pConfig->channelsOut;\n    pConverter->mixingMode  = pConfig->mixingMode;\n\n    if (pConfig->pChannelMapIn != NULL) {\n        pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);\n        ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn);\n    } else {\n        pConverter->pChannelMapIn = NULL;   /* Use default channel map. */\n    }\n\n    if (pConfig->pChannelMapOut != NULL) {\n        pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);\n        ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut);\n    } else {\n        pConverter->pChannelMapOut = NULL;  /* Use default channel map. */\n    }\n\n    pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);\n\n    if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) {\n        pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset);\n        ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable);\n    }\n\n    if (pConverter->conversionPath == ma_channel_conversion_path_weights) {\n        ma_uint32 iChannelIn;\n        ma_uint32 iChannelOut;\n\n        if (pConverter->format == ma_format_f32) {\n            pConverter->weights.f32 = (float**   )ma_offset_ptr(pHeap, heapLayout.weightsOffset);\n            for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {\n                pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn)));\n            }\n        } else {\n            pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset);\n            for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {\n                pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn)));\n            }\n        }\n\n        /* Silence our weights by default. */\n        for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {\n            for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {\n                if (pConverter->format == ma_format_f32) {\n                    pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f;\n                } else {\n                    pConverter->weights.s16[iChannelIn][iChannelOut] = 0;\n                }\n            }\n        }\n\n        /*\n        We now need to fill out our weights table. This is determined by the mixing mode.\n        */\n\n        /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */\n        for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {\n            ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);\n\n            for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {\n                ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);\n\n                if (channelPosIn == channelPosOut) {\n                    float weight = 1;\n\n                    if (pConverter->format == ma_format_f32) {\n                        pConverter->weights.f32[iChannelIn][iChannelOut] = weight;\n                    } else {\n                        pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);\n                    }\n                }\n            }\n        }\n\n        switch (pConverter->mixingMode)\n        {\n            case ma_channel_mix_mode_custom_weights:\n            {\n                if (pConfig->ppWeights == NULL) {\n                    return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */\n                }\n\n                for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {\n                    for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {\n                        float weight = pConfig->ppWeights[iChannelIn][iChannelOut];\n\n                        if (pConverter->format == ma_format_f32) {\n                            pConverter->weights.f32[iChannelIn][iChannelOut] = weight;\n                        } else {\n                            pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);\n                        }\n                    }\n                }\n            } break;\n\n            case ma_channel_mix_mode_simple:\n            {\n                /*\n                In simple mode, only set weights for channels that have exactly matching types, leave the rest at\n                zero. The 1:1 mappings have already been covered before this switch statement.\n                */\n            } break;\n\n            case ma_channel_mix_mode_rectangular:\n            default:\n            {\n                /* Unmapped input channels. */\n                for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {\n                    ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);\n\n                    if (ma_is_spatial_channel_position(channelPosIn)) {\n                        if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) {\n                            for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {\n                                ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);\n\n                                if (ma_is_spatial_channel_position(channelPosOut)) {\n                                    float weight = 0;\n                                    if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {\n                                        weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);\n                                    }\n\n                                    /* Only apply the weight if we haven't already got some contribution from the respective channels. */\n                                    if (pConverter->format == ma_format_f32) {\n                                        if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {\n                                            pConverter->weights.f32[iChannelIn][iChannelOut] = weight;\n                                        }\n                                    } else {\n                                        if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {\n                                            pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n\n                /* Unmapped output channels. */\n                for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {\n                    ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);\n\n                    if (ma_is_spatial_channel_position(channelPosOut)) {\n                        if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) {\n                            for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {\n                                ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);\n\n                                if (ma_is_spatial_channel_position(channelPosIn)) {\n                                    float weight = 0;\n                                    if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {\n                                        weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);\n                                    }\n\n                                    /* Only apply the weight if we haven't already got some contribution from the respective channels. */\n                                    if (pConverter->format == ma_format_f32) {\n                                        if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {\n                                            pConverter->weights.f32[iChannelIn][iChannelOut] = weight;\n                                        }\n                                    } else {\n                                        if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {\n                                            pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n\n                /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */\n                if (pConfig->calculateLFEFromSpatialChannels) {\n                    if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) {\n                        ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn);\n                        ma_uint32 iChannelOutLFE;\n\n                        if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) {\n                            const float weightForLFE = 1.0f / spatialChannelCount;\n                            for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {\n                                const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);\n                                if (ma_is_spatial_channel_position(channelPosIn)) {\n                                    if (pConverter->format == ma_format_f32) {\n                                        if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) {\n                                            pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE;\n                                        }\n                                    } else {\n                                        if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) {\n                                            pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE);\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            } break;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pConverter->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pConverter == NULL) {\n        return;\n    }\n\n    if (pConverter->_ownsHeap) {\n        ma_free(pConverter->_pHeap, pAllocationCallbacks);\n    }\n}\n\nstatic ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    MA_ASSERT(pConverter != NULL);\n    MA_ASSERT(pFramesOut != NULL);\n    MA_ASSERT(pFramesIn  != NULL);\n\n    ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    MA_ASSERT(pConverter != NULL);\n    MA_ASSERT(pFramesOut != NULL);\n    MA_ASSERT(pFramesIn  != NULL);\n    MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut);\n\n    return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format);\n}\n\nstatic ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    ma_uint64 iFrame;\n\n    MA_ASSERT(pConverter != NULL);\n    MA_ASSERT(pFramesOut != NULL);\n    MA_ASSERT(pFramesIn  != NULL);\n    MA_ASSERT(pConverter->channelsIn == 1);\n\n    switch (pConverter->format)\n    {\n        case ma_format_u8:\n        {\n            /* */ ma_uint8* pFramesOutU8 = (      ma_uint8*)pFramesOut;\n            const ma_uint8* pFramesInU8  = (const ma_uint8*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                ma_uint32 iChannel;\n                for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {\n                    pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame];\n                }\n            }\n        } break;\n\n        case ma_format_s16:\n        {\n            /* */ ma_int16* pFramesOutS16 = (      ma_int16*)pFramesOut;\n            const ma_int16* pFramesInS16  = (const ma_int16*)pFramesIn;\n\n            if (pConverter->channelsOut == 2) {\n                for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                    pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame];\n                    pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame];\n                }\n            } else {\n                for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                    ma_uint32 iChannel;\n                    for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {\n                        pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame];\n                    }\n                }\n            }\n        } break;\n\n        case ma_format_s24:\n        {\n            /* */ ma_uint8* pFramesOutS24 = (      ma_uint8*)pFramesOut;\n            const ma_uint8* pFramesInS24  = (const ma_uint8*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                ma_uint32 iChannel;\n                for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {\n                    ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel;\n                    ma_uint64 iSampleIn  = iFrame;\n                    pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0];\n                    pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1];\n                    pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2];\n                }\n            }\n        } break;\n\n        case ma_format_s32:\n        {\n            /* */ ma_int32* pFramesOutS32 = (      ma_int32*)pFramesOut;\n            const ma_int32* pFramesInS32  = (const ma_int32*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                ma_uint32 iChannel;\n                for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {\n                    pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame];\n                }\n            }\n        } break;\n\n        case ma_format_f32:\n        {\n            /* */ float* pFramesOutF32 = (      float*)pFramesOut;\n            const float* pFramesInF32  = (const float*)pFramesIn;\n\n            if (pConverter->channelsOut == 2) {\n                for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                    pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame];\n                    pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame];\n                }\n            } else {\n                for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                    ma_uint32 iChannel;\n                    for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {\n                        pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame];\n                    }\n                }\n            }\n        } break;\n\n        default: return MA_INVALID_OPERATION;   /* Unknown format. */\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannel;\n\n    MA_ASSERT(pConverter != NULL);\n    MA_ASSERT(pFramesOut != NULL);\n    MA_ASSERT(pFramesIn  != NULL);\n    MA_ASSERT(pConverter->channelsOut == 1);\n\n    switch (pConverter->format)\n    {\n        case ma_format_u8:\n        {\n            /* */ ma_uint8* pFramesOutU8 = (      ma_uint8*)pFramesOut;\n            const ma_uint8* pFramesInU8  = (const ma_uint8*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                ma_int32 t = 0;\n                for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {\n                    t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]);\n                }\n\n                pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut);\n            }\n        } break;\n\n        case ma_format_s16:\n        {\n            /* */ ma_int16* pFramesOutS16 = (      ma_int16*)pFramesOut;\n            const ma_int16* pFramesInS16  = (const ma_int16*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                ma_int32 t = 0;\n                for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {\n                    t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel];\n                }\n\n                pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn);\n            }\n        } break;\n\n        case ma_format_s24:\n        {\n            /* */ ma_uint8* pFramesOutS24 = (      ma_uint8*)pFramesOut;\n            const ma_uint8* pFramesInS24  = (const ma_uint8*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                ma_int64 t = 0;\n                for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {\n                    t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]);\n                }\n\n                ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]);\n            }\n        } break;\n\n        case ma_format_s32:\n        {\n            /* */ ma_int32* pFramesOutS32 = (      ma_int32*)pFramesOut;\n            const ma_int32* pFramesInS32  = (const ma_int32*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                ma_int64 t = 0;\n                for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {\n                    t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel];\n                }\n\n                pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn);\n            }\n        } break;\n\n        case ma_format_f32:\n        {\n            /* */ float* pFramesOutF32 = (      float*)pFramesOut;\n            const float* pFramesInF32  = (const float*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; ++iFrame) {\n                float t = 0;\n                for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {\n                    t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel];\n                }\n\n                pFramesOutF32[iFrame] = t / pConverter->channelsIn;\n            }\n        } break;\n\n        default: return MA_INVALID_OPERATION;   /* Unknown format. */\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    ma_uint32 iFrame;\n    ma_uint32 iChannelIn;\n    ma_uint32 iChannelOut;\n\n    MA_ASSERT(pConverter != NULL);\n    MA_ASSERT(pFramesOut != NULL);\n    MA_ASSERT(pFramesIn  != NULL);\n\n    /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */\n\n    /* Clear. */\n    ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));\n\n    /* Accumulate. */\n    switch (pConverter->format)\n    {\n        case ma_format_u8:\n        {\n            /* */ ma_uint8* pFramesOutU8 = (      ma_uint8*)pFramesOut;\n            const ma_uint8* pFramesInU8  = (const ma_uint8*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {\n                    for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {\n                        ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]);\n                        ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn  + iChannelIn ]);\n                        ma_int32 s    = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127);\n                        pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s);\n                    }\n                }\n            }\n        } break;\n\n        case ma_format_s16:\n        {\n            /* */ ma_int16* pFramesOutS16 = (      ma_int16*)pFramesOut;\n            const ma_int16* pFramesInS16  = (const ma_int16*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {\n                    for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {\n                        ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut];\n                        s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;\n\n                        pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767);\n                    }\n                }\n            }\n        } break;\n\n        case ma_format_s24:\n        {\n            /* */ ma_uint8* pFramesOutS24 = (      ma_uint8*)pFramesOut;\n            const ma_uint8* pFramesInS24  = (const ma_uint8*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {\n                    for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {\n                        ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);\n                        ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn  + iChannelIn )*3]);\n                        ma_int64 s24   = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607);\n                        ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);\n                    }\n                }\n            }\n        } break;\n\n        case ma_format_s32:\n        {\n            /* */ ma_int32* pFramesOutS32 = (      ma_int32*)pFramesOut;\n            const ma_int32* pFramesInS32  = (const ma_int32*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {\n                    for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {\n                        ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut];\n                        s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;\n\n                        pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s);\n                    }\n                }\n            }\n        } break;\n\n        case ma_format_f32:\n        {\n            /* */ float* pFramesOutF32 = (      float*)pFramesOut;\n            const float* pFramesInF32  = (const float*)pFramesIn;\n\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {\n                    for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {\n                        pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut];\n                    }\n                }\n            }\n        } break;\n\n        default: return MA_INVALID_OPERATION;   /* Unknown format. */\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)\n{\n    if (pConverter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pFramesOut == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pFramesIn == NULL) {\n        ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));\n        return MA_SUCCESS;\n    }\n\n    switch (pConverter->conversionPath)\n    {\n        case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount);\n        case ma_channel_conversion_path_mono_out:    return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount);\n        case ma_channel_conversion_path_mono_in:     return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount);\n        case ma_channel_conversion_path_shuffle:     return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount);\n        case ma_channel_conversion_path_weights:\n        default:\n        {\n            return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount);\n        }\n    }\n}\n\nMA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    if (pConverter == NULL || pChannelMap == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    if (pConverter == NULL || pChannelMap == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut);\n\n    return MA_SUCCESS;\n}\n\n\n/**************************************************************************************************************************************************************\n\nData Conversion\n\n**************************************************************************************************************************************************************/\nMA_API ma_data_converter_config ma_data_converter_config_init_default(void)\n{\n    ma_data_converter_config config;\n    MA_ZERO_OBJECT(&config);\n\n    config.ditherMode = ma_dither_mode_none;\n    config.resampling.algorithm = ma_resample_algorithm_linear;\n    config.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */\n\n    /* Linear resampling defaults. */\n    config.resampling.linear.lpfOrder = 1;\n\n    return config;\n}\n\nMA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)\n{\n    ma_data_converter_config config = ma_data_converter_config_init_default();\n    config.formatIn      = formatIn;\n    config.formatOut     = formatOut;\n    config.channelsIn    = channelsIn;\n    config.channelsOut   = channelsOut;\n    config.sampleRateIn  = sampleRateIn;\n    config.sampleRateOut = sampleRateOut;\n\n    return config;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t channelConverterOffset;\n    size_t resamplerOffset;\n} ma_data_converter_heap_layout;\n\nstatic ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig)\n{\n    MA_ASSERT(pConfig != NULL);\n\n    return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut;\n}\n\nstatic ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig)\n{\n    MA_ASSERT(pConfig != NULL);\n\n    /*\n    We want to avoid as much data conversion as possible. The channel converter and linear\n    resampler both support s16 and f32 natively. We need to decide on the format to use for this\n    stage. We call this the mid format because it's used in the middle stage of the conversion\n    pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it\n    will do the same thing for the input format. If it's neither we just use f32. If we are using a\n    custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced\n    to use that if resampling is required.\n    */\n    if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) {\n        return ma_format_f32;  /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */\n    } else {\n        /*  */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) {\n            return pConfig->formatOut;\n        } else if (pConfig->formatIn  == ma_format_s16 || pConfig->formatIn  == ma_format_f32) {\n            return pConfig->formatIn;\n        } else {\n            return ma_format_f32;\n        }\n    }\n}\n\nstatic ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)\n{\n    ma_channel_converter_config channelConverterConfig;\n\n    MA_ASSERT(pConfig != NULL);\n\n    channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode);\n    channelConverterConfig.ppWeights = pConfig->ppChannelWeights;\n    channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels;\n\n    return channelConverterConfig;\n}\n\nstatic ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)\n{\n    ma_resampler_config resamplerConfig;\n    ma_uint32 resamplerChannels;\n\n    MA_ASSERT(pConfig != NULL);\n\n    /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */\n    if (pConfig->channelsIn < pConfig->channelsOut) {\n        resamplerChannels = pConfig->channelsIn;\n    } else {\n        resamplerChannels = pConfig->channelsOut;\n    }\n\n    resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm);\n    resamplerConfig.linear           = pConfig->resampling.linear;\n    resamplerConfig.pBackendVTable   = pConfig->resampling.pBackendVTable;\n    resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData;\n\n    return resamplerConfig;\n}\n\nstatic ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout)\n{\n    ma_result result;\n\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* Channel converter. */\n    pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes;\n    {\n        size_t heapSizeInBytes;\n        ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);\n\n        result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHeapLayout->sizeInBytes += heapSizeInBytes;\n    }\n\n    /* Resampler. */\n    pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;\n    if (ma_data_converter_config_is_resampler_required(pConfig)) {\n        size_t heapSizeInBytes;\n        ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);\n\n        result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHeapLayout->sizeInBytes += heapSizeInBytes;\n    }\n\n    /* Make sure allocation size is aligned. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_data_converter_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter)\n{\n    ma_result result;\n    ma_data_converter_heap_layout heapLayout;\n    ma_format midFormat;\n    ma_bool32 isResamplingRequired;\n\n    if (pConverter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pConverter);\n\n    result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pConverter->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    pConverter->formatIn      = pConfig->formatIn;\n    pConverter->formatOut     = pConfig->formatOut;\n    pConverter->channelsIn    = pConfig->channelsIn;\n    pConverter->channelsOut   = pConfig->channelsOut;\n    pConverter->sampleRateIn  = pConfig->sampleRateIn;\n    pConverter->sampleRateOut = pConfig->sampleRateOut;\n    pConverter->ditherMode    = pConfig->ditherMode;\n\n    /*\n    Determine if resampling is required. We need to do this so we can determine an appropriate\n    mid format to use. If resampling is required, the mid format must be ma_format_f32 since\n    that is the only one that is guaranteed to supported by custom resampling backends.\n    */\n    isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig);\n    midFormat = ma_data_converter_config_get_mid_format(pConfig);\n\n\n    /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */\n    {\n        ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);\n\n        result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */\n        if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) {\n            pConverter->hasChannelConverter = MA_TRUE;\n        }\n    }\n\n\n    /* Resampler. */\n    if (isResamplingRequired) {\n        ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);\n\n        result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pConverter->hasResampler = MA_TRUE;\n    }\n\n\n    /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */\n    if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) {\n        /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */\n        if (pConverter->formatIn == pConverter->formatOut) {\n            /* The formats are the same so we can just pass through. */\n            pConverter->hasPreFormatConversion  = MA_FALSE;\n            pConverter->hasPostFormatConversion = MA_FALSE;\n        } else {\n            /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */\n            pConverter->hasPreFormatConversion  = MA_FALSE;\n            pConverter->hasPostFormatConversion = MA_TRUE;\n        }\n    } else {\n        /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */\n        if (pConverter->formatIn != midFormat) {\n            pConverter->hasPreFormatConversion  = MA_TRUE;\n        }\n        if (pConverter->formatOut != midFormat) {\n            pConverter->hasPostFormatConversion = MA_TRUE;\n        }\n    }\n\n    /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */\n    if (pConverter->hasPreFormatConversion  == MA_FALSE &&\n        pConverter->hasPostFormatConversion == MA_FALSE &&\n        pConverter->hasChannelConverter     == MA_FALSE &&\n        pConverter->hasResampler            == MA_FALSE) {\n        pConverter->isPassthrough = MA_TRUE;\n    }\n\n\n    /* We now need to determine our execution path. */\n    if (pConverter->isPassthrough) {\n        pConverter->executionPath = ma_data_converter_execution_path_passthrough;\n    } else {\n        if (pConverter->channelsIn < pConverter->channelsOut) {\n            /* Do resampling first, if necessary. */\n            MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE);\n\n            if (pConverter->hasResampler) {\n                pConverter->executionPath = ma_data_converter_execution_path_resample_first;\n            } else {\n                pConverter->executionPath = ma_data_converter_execution_path_channels_only;\n            }\n        } else {\n            /* Do channel conversion first, if necessary. */\n            if (pConverter->hasChannelConverter) {\n                if (pConverter->hasResampler) {\n                    pConverter->executionPath = ma_data_converter_execution_path_channels_first;\n                } else {\n                    pConverter->executionPath = ma_data_converter_execution_path_channels_only;\n                }\n            } else {\n                /* Channel routing not required. */\n                if (pConverter->hasResampler) {\n                    pConverter->executionPath = ma_data_converter_execution_path_resample_only;\n                } else {\n                    pConverter->executionPath = ma_data_converter_execution_path_format_only;\n                }\n            }\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pConverter->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pConverter == NULL) {\n        return;\n    }\n\n    if (pConverter->hasResampler) {\n        ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks);\n    }\n\n    ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks);\n\n    if (pConverter->_ownsHeap) {\n        ma_free(pConverter->_pHeap, pAllocationCallbacks);\n    }\n}\n\nstatic ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    ma_uint64 frameCountIn;\n    ma_uint64 frameCountOut;\n    ma_uint64 frameCount;\n\n    MA_ASSERT(pConverter != NULL);\n\n    frameCountIn = 0;\n    if (pFrameCountIn != NULL) {\n        frameCountIn = *pFrameCountIn;\n    }\n\n    frameCountOut = 0;\n    if (pFrameCountOut != NULL) {\n        frameCountOut = *pFrameCountOut;\n    }\n\n    frameCount = ma_min(frameCountIn, frameCountOut);\n\n    if (pFramesOut != NULL) {\n        if (pFramesIn != NULL) {\n            ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));\n        } else {\n            ma_zero_memory_64(pFramesOut,            frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));\n        }\n    }\n\n    if (pFrameCountIn != NULL) {\n        *pFrameCountIn = frameCount;\n    }\n    if (pFrameCountOut != NULL) {\n        *pFrameCountOut = frameCount;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    ma_uint64 frameCountIn;\n    ma_uint64 frameCountOut;\n    ma_uint64 frameCount;\n\n    MA_ASSERT(pConverter != NULL);\n\n    frameCountIn = 0;\n    if (pFrameCountIn != NULL) {\n        frameCountIn = *pFrameCountIn;\n    }\n\n    frameCountOut = 0;\n    if (pFrameCountOut != NULL) {\n        frameCountOut = *pFrameCountOut;\n    }\n\n    frameCount = ma_min(frameCountIn, frameCountOut);\n\n    if (pFramesOut != NULL) {\n        if (pFramesIn != NULL) {\n            ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode);\n        } else {\n            ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));\n        }\n    }\n\n    if (pFrameCountIn != NULL) {\n        *pFrameCountIn = frameCount;\n    }\n    if (pFrameCountOut != NULL) {\n        *pFrameCountOut = frameCount;\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint64 frameCountIn;\n    ma_uint64 frameCountOut;\n    ma_uint64 framesProcessedIn;\n    ma_uint64 framesProcessedOut;\n\n    MA_ASSERT(pConverter != NULL);\n\n    frameCountIn = 0;\n    if (pFrameCountIn != NULL) {\n        frameCountIn = *pFrameCountIn;\n    }\n\n    frameCountOut = 0;\n    if (pFrameCountOut != NULL) {\n        frameCountOut = *pFrameCountOut;\n    }\n\n    framesProcessedIn  = 0;\n    framesProcessedOut = 0;\n\n    while (framesProcessedOut < frameCountOut) {\n        ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n        const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);\n        const void* pFramesInThisIteration;\n        /* */ void* pFramesOutThisIteration;\n        ma_uint64 frameCountInThisIteration;\n        ma_uint64 frameCountOutThisIteration;\n\n        if (pFramesIn != NULL) {\n            pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));\n        } else {\n            pFramesInThisIteration = NULL;\n        }\n\n        if (pFramesOut != NULL) {\n            pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));\n        } else {\n            pFramesOutThisIteration = NULL;\n        }\n\n        /* Do a pre format conversion if necessary. */\n        if (pConverter->hasPreFormatConversion) {\n            ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n            const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);\n\n            frameCountInThisIteration  = (frameCountIn - framesProcessedIn);\n            if (frameCountInThisIteration > tempBufferInCap) {\n                frameCountInThisIteration = tempBufferInCap;\n            }\n\n            if (pConverter->hasPostFormatConversion) {\n               if (frameCountInThisIteration > tempBufferOutCap) {\n                   frameCountInThisIteration = tempBufferOutCap;\n               }\n            }\n\n            if (pFramesInThisIteration != NULL) {\n                ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);\n            } else {\n                MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));\n            }\n\n            frameCountOutThisIteration = (frameCountOut - framesProcessedOut);\n\n            if (pConverter->hasPostFormatConversion) {\n                /* Both input and output conversion required. Output to the temp buffer. */\n                if (frameCountOutThisIteration > tempBufferOutCap) {\n                    frameCountOutThisIteration = tempBufferOutCap;\n                }\n\n                result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);\n            } else {\n                /* Only pre-format required. Output straight to the output buffer. */\n                result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration);\n            }\n\n            if (result != MA_SUCCESS) {\n                break;\n            }\n        } else {\n            /* No pre-format required. Just read straight from the input buffer. */\n            MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);\n\n            frameCountInThisIteration  = (frameCountIn  - framesProcessedIn);\n            frameCountOutThisIteration = (frameCountOut - framesProcessedOut);\n            if (frameCountOutThisIteration > tempBufferOutCap) {\n                frameCountOutThisIteration = tempBufferOutCap;\n            }\n\n            result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);\n            if (result != MA_SUCCESS) {\n                break;\n            }\n        }\n\n        /* If we are doing a post format conversion we need to do that now. */\n        if (pConverter->hasPostFormatConversion) {\n            if (pFramesOutThisIteration != NULL) {\n                ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode);\n            }\n        }\n\n        framesProcessedIn  += frameCountInThisIteration;\n        framesProcessedOut += frameCountOutThisIteration;\n\n        MA_ASSERT(framesProcessedIn  <= frameCountIn);\n        MA_ASSERT(framesProcessedOut <= frameCountOut);\n\n        if (frameCountOutThisIteration == 0) {\n            break;  /* Consumed all of our input data. */\n        }\n    }\n\n    if (pFrameCountIn != NULL) {\n        *pFrameCountIn = framesProcessedIn;\n    }\n    if (pFrameCountOut != NULL) {\n        *pFrameCountOut = framesProcessedOut;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    MA_ASSERT(pConverter != NULL);\n\n    if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {\n        /* Neither pre- nor post-format required. This is simple case where only resampling is required. */\n        return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n    } else {\n        /* Format conversion required. */\n        return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n    }\n}\n\nstatic ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    ma_result result;\n    ma_uint64 frameCountIn;\n    ma_uint64 frameCountOut;\n    ma_uint64 frameCount;\n\n    MA_ASSERT(pConverter != NULL);\n\n    frameCountIn = 0;\n    if (pFrameCountIn != NULL) {\n        frameCountIn = *pFrameCountIn;\n    }\n\n    frameCountOut = 0;\n    if (pFrameCountOut != NULL) {\n        frameCountOut = *pFrameCountOut;\n    }\n\n    frameCount = ma_min(frameCountIn, frameCountOut);\n\n    if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {\n        /* No format conversion required. */\n        result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    } else {\n        /* Format conversion required. */\n        ma_uint64 framesProcessed = 0;\n\n        while (framesProcessed < frameCount) {\n            ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n            const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);\n            const void* pFramesInThisIteration;\n            /* */ void* pFramesOutThisIteration;\n            ma_uint64 frameCountThisIteration;\n\n            if (pFramesIn != NULL) {\n                pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));\n            } else {\n                pFramesInThisIteration = NULL;\n            }\n\n            if (pFramesOut != NULL) {\n                pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));\n            } else {\n                pFramesOutThisIteration = NULL;\n            }\n\n            /* Do a pre format conversion if necessary. */\n            if (pConverter->hasPreFormatConversion) {\n                ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n                const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);\n\n                frameCountThisIteration = (frameCount - framesProcessed);\n                if (frameCountThisIteration > tempBufferInCap) {\n                    frameCountThisIteration = tempBufferInCap;\n                }\n\n                if (pConverter->hasPostFormatConversion) {\n                    if (frameCountThisIteration > tempBufferOutCap) {\n                        frameCountThisIteration = tempBufferOutCap;\n                    }\n                }\n\n                if (pFramesInThisIteration != NULL) {\n                    ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode);\n                } else {\n                    MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));\n                }\n\n                if (pConverter->hasPostFormatConversion) {\n                    /* Both input and output conversion required. Output to the temp buffer. */\n                    result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration);\n                } else {\n                    /* Only pre-format required. Output straight to the output buffer. */\n                    result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration);\n                }\n\n                if (result != MA_SUCCESS) {\n                    break;\n                }\n            } else {\n                /* No pre-format required. Just read straight from the input buffer. */\n                MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);\n\n                frameCountThisIteration = (frameCount - framesProcessed);\n                if (frameCountThisIteration > tempBufferOutCap) {\n                    frameCountThisIteration = tempBufferOutCap;\n                }\n\n                result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration);\n                if (result != MA_SUCCESS) {\n                    break;\n                }\n            }\n\n            /* If we are doing a post format conversion we need to do that now. */\n            if (pConverter->hasPostFormatConversion) {\n                if (pFramesOutThisIteration != NULL) {\n                    ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);\n                }\n            }\n\n            framesProcessed += frameCountThisIteration;\n        }\n    }\n\n    if (pFrameCountIn != NULL) {\n        *pFrameCountIn = frameCount;\n    }\n    if (pFrameCountOut != NULL) {\n        *pFrameCountOut = frameCount;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    ma_result result;\n    ma_uint64 frameCountIn;\n    ma_uint64 frameCountOut;\n    ma_uint64 framesProcessedIn;\n    ma_uint64 framesProcessedOut;\n    ma_uint8  pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];   /* In resampler format. */\n    ma_uint64 tempBufferInCap;\n    ma_uint8  pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];  /* In resampler format, channel converter input format. */\n    ma_uint64 tempBufferMidCap;\n    ma_uint8  pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];  /* In channel converter output format. */\n    ma_uint64 tempBufferOutCap;\n\n    MA_ASSERT(pConverter != NULL);\n    MA_ASSERT(pConverter->resampler.format   == pConverter->channelConverter.format);\n    MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn);\n    MA_ASSERT(pConverter->resampler.channels <  pConverter->channelConverter.channelsOut);\n\n    frameCountIn = 0;\n    if (pFrameCountIn != NULL) {\n        frameCountIn = *pFrameCountIn;\n    }\n\n    frameCountOut = 0;\n    if (pFrameCountOut != NULL) {\n        frameCountOut = *pFrameCountOut;\n    }\n\n    framesProcessedIn  = 0;\n    framesProcessedOut = 0;\n\n    tempBufferInCap  = sizeof(pTempBufferIn)  / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);\n    tempBufferMidCap = sizeof(pTempBufferIn)  / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);\n    tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);\n\n    while (framesProcessedOut < frameCountOut) {\n        ma_uint64 frameCountInThisIteration;\n        ma_uint64 frameCountOutThisIteration;\n        const void* pRunningFramesIn = NULL;\n        void* pRunningFramesOut = NULL;\n        const void* pResampleBufferIn;\n        void* pChannelsBufferOut;\n\n        if (pFramesIn != NULL) {\n            pRunningFramesIn  = ma_offset_ptr(pFramesIn,  framesProcessedIn  * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));\n        }\n        if (pFramesOut != NULL) {\n            pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));\n        }\n\n        /* Run input data through the resampler and output it to the temporary buffer. */\n        frameCountInThisIteration = (frameCountIn - framesProcessedIn);\n\n        if (pConverter->hasPreFormatConversion) {\n            if (frameCountInThisIteration > tempBufferInCap) {\n                frameCountInThisIteration = tempBufferInCap;\n            }\n        }\n\n        frameCountOutThisIteration = (frameCountOut - framesProcessedOut);\n        if (frameCountOutThisIteration > tempBufferMidCap) {\n            frameCountOutThisIteration = tempBufferMidCap;\n        }\n\n        /* We can't read more frames than can fit in the output buffer. */\n        if (pConverter->hasPostFormatConversion) {\n            if (frameCountOutThisIteration > tempBufferOutCap) {\n                frameCountOutThisIteration = tempBufferOutCap;\n            }\n        }\n\n        /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */\n\n        /*\n        We need to try to predict how many input frames will be required for the resampler. If the\n        resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further\n        off we are from this, the more wasted format conversions we'll end up doing.\n        */\n        #if 1\n        {\n            ma_uint64 requiredInputFrameCount;\n\n            result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);\n            if (result != MA_SUCCESS) {\n                /* Fall back to a best guess. */\n                requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;\n            }\n\n            if (frameCountInThisIteration > requiredInputFrameCount) {\n                frameCountInThisIteration = requiredInputFrameCount;\n            }\n        }\n        #endif\n\n        if (pConverter->hasPreFormatConversion) {\n            if (pFramesIn != NULL) {\n                ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);\n                pResampleBufferIn = pTempBufferIn;\n            } else {\n                pResampleBufferIn = NULL;\n            }\n        } else {\n            pResampleBufferIn = pRunningFramesIn;\n        }\n\n        result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n\n        /*\n        The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do\n        this part if we have an output buffer.\n        */\n        if (pFramesOut != NULL) {\n            if (pConverter->hasPostFormatConversion) {\n                pChannelsBufferOut = pTempBufferOut;\n            } else {\n                pChannelsBufferOut = pRunningFramesOut;\n            }\n\n            result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration);\n            if (result != MA_SUCCESS) {\n                return result;\n            }\n\n            /* Finally we do post format conversion. */\n            if (pConverter->hasPostFormatConversion) {\n                ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);\n            }\n        }\n\n\n        framesProcessedIn  += frameCountInThisIteration;\n        framesProcessedOut += frameCountOutThisIteration;\n\n        MA_ASSERT(framesProcessedIn  <= frameCountIn);\n        MA_ASSERT(framesProcessedOut <= frameCountOut);\n\n        if (frameCountOutThisIteration == 0) {\n            break;  /* Consumed all of our input data. */\n        }\n    }\n\n    if (pFrameCountIn != NULL) {\n        *pFrameCountIn = framesProcessedIn;\n    }\n    if (pFrameCountOut != NULL) {\n        *pFrameCountOut = framesProcessedOut;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    ma_result result;\n    ma_uint64 frameCountIn;\n    ma_uint64 frameCountOut;\n    ma_uint64 framesProcessedIn;\n    ma_uint64 framesProcessedOut;\n    ma_uint8  pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];   /* In resampler format. */\n    ma_uint64 tempBufferInCap;\n    ma_uint8  pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];  /* In resampler format, channel converter input format. */\n    ma_uint64 tempBufferMidCap;\n    ma_uint8  pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];  /* In channel converter output format. */\n    ma_uint64 tempBufferOutCap;\n\n    MA_ASSERT(pConverter != NULL);\n    MA_ASSERT(pConverter->resampler.format   == pConverter->channelConverter.format);\n    MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut);\n    MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn);\n\n    frameCountIn = 0;\n    if (pFrameCountIn != NULL) {\n        frameCountIn = *pFrameCountIn;\n    }\n\n    frameCountOut = 0;\n    if (pFrameCountOut != NULL) {\n        frameCountOut = *pFrameCountOut;\n    }\n\n    framesProcessedIn  = 0;\n    framesProcessedOut = 0;\n\n    tempBufferInCap  = sizeof(pTempBufferIn)  / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);\n    tempBufferMidCap = sizeof(pTempBufferIn)  / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);\n    tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);\n\n    while (framesProcessedOut < frameCountOut) {\n        ma_uint64 frameCountInThisIteration;\n        ma_uint64 frameCountOutThisIteration;\n        const void* pRunningFramesIn = NULL;\n        void* pRunningFramesOut = NULL;\n        const void* pChannelsBufferIn;\n        void* pResampleBufferOut;\n\n        if (pFramesIn != NULL) {\n            pRunningFramesIn  = ma_offset_ptr(pFramesIn,  framesProcessedIn  * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));\n        }\n        if (pFramesOut != NULL) {\n            pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));\n        }\n\n        /*\n        Before doing any processing we need to determine how many frames we should try processing\n        this iteration, for both input and output. The resampler requires us to perform format and\n        channel conversion before passing any data into it. If we get our input count wrong, we'll\n        end up peforming redundant pre-processing. This isn't the end of the world, but it does\n        result in some inefficiencies proportionate to how far our estimates are off.\n\n        If the resampler has a means to calculate exactly how much we'll need, we'll use that.\n        Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output\n        frame count first.\n        */\n        frameCountOutThisIteration = (frameCountOut - framesProcessedOut);\n        if (frameCountOutThisIteration > tempBufferMidCap) {\n            frameCountOutThisIteration = tempBufferMidCap;\n        }\n\n        if (pConverter->hasPostFormatConversion) {\n            if (frameCountOutThisIteration > tempBufferOutCap) {\n                frameCountOutThisIteration = tempBufferOutCap;\n            }\n        }\n\n        /* Now that we have the output frame count we can determine the input frame count. */\n        frameCountInThisIteration = (frameCountIn - framesProcessedIn);\n        if (pConverter->hasPreFormatConversion) {\n            if (frameCountInThisIteration > tempBufferInCap) {\n                frameCountInThisIteration = tempBufferInCap;\n            }\n        }\n\n        if (frameCountInThisIteration > tempBufferMidCap) {\n            frameCountInThisIteration = tempBufferMidCap;\n        }\n\n        #if 1\n        {\n            ma_uint64 requiredInputFrameCount;\n\n            result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);\n            if (result != MA_SUCCESS) {\n                /* Fall back to a best guess. */\n                requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;\n            }\n\n            if (frameCountInThisIteration > requiredInputFrameCount) {\n                frameCountInThisIteration = requiredInputFrameCount;\n            }\n        }\n        #endif\n\n\n        /* Pre format conversion. */\n        if (pConverter->hasPreFormatConversion) {\n            if (pRunningFramesIn != NULL) {\n                ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);\n                pChannelsBufferIn = pTempBufferIn;\n            } else {\n                pChannelsBufferIn = NULL;\n            }\n        } else {\n            pChannelsBufferIn = pRunningFramesIn;\n        }\n\n\n        /* Channel conversion. */\n        result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n\n        /* Resampling. */\n        if (pConverter->hasPostFormatConversion) {\n            pResampleBufferOut = pTempBufferOut;\n        } else {\n            pResampleBufferOut = pRunningFramesOut;\n        }\n\n        result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n\n        /* Post format conversion. */\n        if (pConverter->hasPostFormatConversion) {\n            if (pRunningFramesOut != NULL) {\n                ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode);\n            }\n        }\n\n\n        framesProcessedIn  += frameCountInThisIteration;\n        framesProcessedOut += frameCountOutThisIteration;\n\n        MA_ASSERT(framesProcessedIn  <= frameCountIn);\n        MA_ASSERT(framesProcessedOut <= frameCountOut);\n\n        if (frameCountOutThisIteration == 0) {\n            break;  /* Consumed all of our input data. */\n        }\n    }\n\n    if (pFrameCountIn != NULL) {\n        *pFrameCountIn = framesProcessedIn;\n    }\n    if (pFrameCountOut != NULL) {\n        *pFrameCountOut = framesProcessedOut;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)\n{\n    if (pConverter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    switch (pConverter->executionPath)\n    {\n        case ma_data_converter_execution_path_passthrough:    return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n        case ma_data_converter_execution_path_format_only:    return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n        case ma_data_converter_execution_path_channels_only:  return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n        case ma_data_converter_execution_path_resample_only:  return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n        case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n        case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);\n        default: return MA_INVALID_OPERATION;   /* Should never hit this. */\n    }\n}\n\nMA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)\n{\n    if (pConverter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConverter->hasResampler == MA_FALSE) {\n        return MA_INVALID_OPERATION;    /* Dynamic resampling not enabled. */\n    }\n\n    return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut);\n}\n\nMA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut)\n{\n    if (pConverter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConverter->hasResampler == MA_FALSE) {\n        return MA_INVALID_OPERATION;    /* Dynamic resampling not enabled. */\n    }\n\n    return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut);\n}\n\nMA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter)\n{\n    if (pConverter == NULL) {\n        return 0;\n    }\n\n    if (pConverter->hasResampler) {\n        return ma_resampler_get_input_latency(&pConverter->resampler);\n    }\n\n    return 0;   /* No latency without a resampler. */\n}\n\nMA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter)\n{\n    if (pConverter == NULL) {\n        return 0;\n    }\n\n    if (pConverter->hasResampler) {\n        return ma_resampler_get_output_latency(&pConverter->resampler);\n    }\n\n    return 0;   /* No latency without a resampler. */\n}\n\nMA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)\n{\n    if (pInputFrameCount == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pInputFrameCount = 0;\n\n    if (pConverter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConverter->hasResampler) {\n        return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount);\n    } else {\n        *pInputFrameCount = outputFrameCount;   /* 1:1 */\n        return MA_SUCCESS;\n    }\n}\n\nMA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)\n{\n    if (pOutputFrameCount == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pOutputFrameCount = 0;\n\n    if (pConverter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConverter->hasResampler) {\n        return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount);\n    } else {\n        *pOutputFrameCount = inputFrameCount;   /* 1:1 */\n        return MA_SUCCESS;\n    }\n}\n\nMA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    if (pConverter == NULL || pChannelMap == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConverter->hasChannelConverter) {\n        ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);\n    } else {\n        ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    if (pConverter == NULL || pChannelMap == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConverter->hasChannelConverter) {\n        ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);\n    } else {\n        ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter)\n{\n    if (pConverter == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* There's nothing to do if we're not resampling. */\n    if (pConverter->hasResampler == MA_FALSE) {\n        return MA_SUCCESS;\n    }\n\n    return ma_resampler_reset(&pConverter->resampler);\n}\n\n\n\n/**************************************************************************************************************************************************************\n\nChannel Maps\n\n**************************************************************************************************************************************************************/\nstatic ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);\n\nMA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)\n{\n    if (pChannelMap == NULL) {\n        return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex);\n    } else {\n        if (channelIndex >= channelCount) {\n            return MA_CHANNEL_NONE;\n        }\n\n        return pChannelMap[channelIndex];\n    }\n}\n\nMA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels)\n{\n    if (pChannelMap == NULL) {\n        return;\n    }\n\n    MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels);\n}\n\n\nstatic ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex)\n{\n    if (channelCount == 0 || channelIndex >= channelCount) {\n        return MA_CHANNEL_NONE;\n    }\n\n    /* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */\n    switch (channelCount)\n    {\n        case 0: return MA_CHANNEL_NONE;\n\n        case 1:\n        {\n            return MA_CHANNEL_MONO;\n        } break;\n\n        case 2:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n            }\n        } break;\n\n        case 3: /* No defined, but best guess. */\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n            }\n        } break;\n\n        case 4:\n        {\n            switch (channelIndex) {\n            #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP\n                /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_BACK_CENTER;\n            #else\n                /* Quad. */\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n            #endif\n            }\n        } break;\n\n        case 5: /* Not defined, but best guess. */\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_BACK_LEFT;\n                case 4: return MA_CHANNEL_BACK_RIGHT;\n            }\n        } break;\n\n        case 6:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_LFE;\n                case 4: return MA_CHANNEL_SIDE_LEFT;\n                case 5: return MA_CHANNEL_SIDE_RIGHT;\n            }\n        } break;\n\n        case 7: /* Not defined, but best guess. */\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_LFE;\n                case 4: return MA_CHANNEL_BACK_CENTER;\n                case 5: return MA_CHANNEL_SIDE_LEFT;\n                case 6: return MA_CHANNEL_SIDE_RIGHT;\n            }\n        } break;\n\n        case 8:\n        default:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_LFE;\n                case 4: return MA_CHANNEL_BACK_LEFT;\n                case 5: return MA_CHANNEL_BACK_RIGHT;\n                case 6: return MA_CHANNEL_SIDE_LEFT;\n                case 7: return MA_CHANNEL_SIDE_RIGHT;\n            }\n        } break;\n    }\n\n    if (channelCount > 8) {\n        if (channelIndex < 32) {    /* We have 32 AUX channels. */\n            return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));\n        }\n    }\n\n    /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */\n    return MA_CHANNEL_NONE;\n}\n\nstatic ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex)\n{\n    switch (channelCount)\n    {\n        case 0: return MA_CHANNEL_NONE;\n\n        case 1:\n        {\n            return MA_CHANNEL_MONO;\n        } break;\n\n        case 2:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n            }\n        } break;\n\n        case 3:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n            }\n        } break;\n\n        case 4:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n            }\n        } break;\n\n        case 5:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n                case 4: return MA_CHANNEL_FRONT_CENTER;\n            }\n        } break;\n\n        case 6:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n                case 4: return MA_CHANNEL_FRONT_CENTER;\n                case 5: return MA_CHANNEL_LFE;\n            }\n        } break;\n\n        case 7:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n                case 4: return MA_CHANNEL_FRONT_CENTER;\n                case 5: return MA_CHANNEL_LFE;\n                case 6: return MA_CHANNEL_BACK_CENTER;\n            }\n        } break;\n\n        case 8:\n        default:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n                case 4: return MA_CHANNEL_FRONT_CENTER;\n                case 5: return MA_CHANNEL_LFE;\n                case 6: return MA_CHANNEL_SIDE_LEFT;\n                case 7: return MA_CHANNEL_SIDE_RIGHT;\n            }\n        } break;\n    }\n\n    if (channelCount > 8) {\n        if (channelIndex < 32) {    /* We have 32 AUX channels. */\n            return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));\n        }\n    }\n\n    /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */\n    return MA_CHANNEL_NONE;\n}\n\nstatic ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex)\n{\n    switch (channelCount)\n    {\n        case 0: return MA_CHANNEL_NONE;\n\n        case 1:\n        {\n            return MA_CHANNEL_MONO;\n        } break;\n\n        case 2:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n            }\n        } break;\n\n        case 3:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n            }\n        } break;\n\n        case 4:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 3: return MA_CHANNEL_BACK_CENTER;\n            }\n        } break;\n\n        case 5:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_BACK_LEFT;\n                case 4: return MA_CHANNEL_BACK_RIGHT;\n            }\n        } break;\n\n        case 6:\n        default:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_SIDE_LEFT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_FRONT_RIGHT;\n                case 4: return MA_CHANNEL_SIDE_RIGHT;\n                case 5: return MA_CHANNEL_BACK_CENTER;\n            }\n        } break;\n    }\n\n    if (channelCount > 6) {\n        if (channelIndex < 32) {    /* We have 32 AUX channels. */\n            return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));\n        }\n    }\n\n    /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */\n    return MA_CHANNEL_NONE;\n}\n\nstatic ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex)\n{\n    switch (channelCount)\n    {\n        case 0: return MA_CHANNEL_NONE;\n\n        case 1:\n        {\n            return MA_CHANNEL_MONO;\n        } break;\n\n        case 2:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n            }\n        } break;\n\n        case 3:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n            }\n        } break;\n\n        case 4:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n            }\n        } break;\n\n        case 5:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_BACK_LEFT;\n                case 4: return MA_CHANNEL_BACK_RIGHT;\n            }\n        } break;\n\n        case 6:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_LFE;\n                case 4: return MA_CHANNEL_BACK_LEFT;\n                case 5: return MA_CHANNEL_BACK_RIGHT;\n            }\n        } break;\n\n        case 7:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_LFE;\n                case 4: return MA_CHANNEL_BACK_CENTER;\n                case 5: return MA_CHANNEL_SIDE_LEFT;\n                case 6: return MA_CHANNEL_SIDE_RIGHT;\n            }\n        } break;\n\n        case 8:\n        default:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_LFE;\n                case 4: return MA_CHANNEL_BACK_LEFT;\n                case 5: return MA_CHANNEL_BACK_RIGHT;\n                case 6: return MA_CHANNEL_SIDE_LEFT;\n                case 7: return MA_CHANNEL_SIDE_RIGHT;\n            }\n        } break;\n    }\n\n    if (channelCount > 8) {\n        if (channelIndex < 32) {    /* We have 32 AUX channels. */\n            return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));\n        }\n    }\n\n    /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */\n    return MA_CHANNEL_NONE;\n}\n\nstatic ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex)\n{\n    switch (channelCount)\n    {\n        case 0: return MA_CHANNEL_NONE;\n\n        case 1:\n        {\n            return MA_CHANNEL_MONO;\n        } break;\n\n        case 2:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n            }\n        } break;\n\n        case 3:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_CENTER;\n                case 2: return MA_CHANNEL_FRONT_RIGHT;\n            }\n        } break;\n\n        case 4:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n            }\n        } break;\n\n        case 5:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_CENTER;\n                case 2: return MA_CHANNEL_FRONT_RIGHT;\n                case 3: return MA_CHANNEL_BACK_LEFT;\n                case 4: return MA_CHANNEL_BACK_RIGHT;\n            }\n        } break;\n\n        case 6:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_CENTER;\n                case 2: return MA_CHANNEL_FRONT_RIGHT;\n                case 3: return MA_CHANNEL_BACK_LEFT;\n                case 4: return MA_CHANNEL_BACK_RIGHT;\n                case 5: return MA_CHANNEL_LFE;\n            }\n        } break;\n\n        case 7:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_CENTER;\n                case 2: return MA_CHANNEL_FRONT_RIGHT;\n                case 3: return MA_CHANNEL_SIDE_LEFT;\n                case 4: return MA_CHANNEL_SIDE_RIGHT;\n                case 5: return MA_CHANNEL_BACK_CENTER;\n                case 6: return MA_CHANNEL_LFE;\n            }\n        } break;\n\n        case 8:\n        default:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_CENTER;\n                case 2: return MA_CHANNEL_FRONT_RIGHT;\n                case 3: return MA_CHANNEL_SIDE_LEFT;\n                case 4: return MA_CHANNEL_SIDE_RIGHT;\n                case 5: return MA_CHANNEL_BACK_LEFT;\n                case 6: return MA_CHANNEL_BACK_RIGHT;\n                case 7: return MA_CHANNEL_LFE;\n            }\n        } break;\n    }\n\n    if (channelCount > 8) {\n        if (channelIndex < 32) {    /* We have 32 AUX channels. */\n            return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));\n        }\n    }\n\n    /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */\n    return MA_CHANNEL_NONE;\n}\n\nstatic ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex)\n{\n    switch (channelCount)\n    {\n        case 0: return MA_CHANNEL_NONE;\n\n        case 1:\n        {\n            return MA_CHANNEL_MONO;\n        } break;\n\n        case 2:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n            }\n        } break;\n\n        case 3:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n            }\n        } break;\n\n        case 4:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n            }\n        } break;\n\n        case 5:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n                case 3: return MA_CHANNEL_BACK_LEFT;\n                case 4: return MA_CHANNEL_BACK_RIGHT;\n            }\n        } break;\n\n        case 6:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_CENTER;\n                case 2: return MA_CHANNEL_FRONT_RIGHT;\n                case 3: return MA_CHANNEL_BACK_LEFT;\n                case 4: return MA_CHANNEL_BACK_RIGHT;\n                case 5: return MA_CHANNEL_LFE;\n            }\n        } break;\n\n        case 7:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_CENTER;\n                case 2: return MA_CHANNEL_FRONT_RIGHT;\n                case 3: return MA_CHANNEL_SIDE_LEFT;\n                case 4: return MA_CHANNEL_SIDE_RIGHT;\n                case 5: return MA_CHANNEL_BACK_CENTER;\n                case 6: return MA_CHANNEL_LFE;\n            }\n        } break;\n\n        case 8:\n        default:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_CENTER;\n                case 2: return MA_CHANNEL_FRONT_RIGHT;\n                case 3: return MA_CHANNEL_SIDE_LEFT;\n                case 4: return MA_CHANNEL_SIDE_RIGHT;\n                case 5: return MA_CHANNEL_BACK_LEFT;\n                case 6: return MA_CHANNEL_BACK_RIGHT;\n                case 7: return MA_CHANNEL_LFE;\n            }\n        } break;\n    }\n\n    if (channelCount > 8) {\n        if (channelIndex < 32) {    /* We have 32 AUX channels. */\n            return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));\n        }\n    }\n\n    /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */\n    return MA_CHANNEL_NONE;\n}\n\nstatic ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex)\n{\n    switch (channelCount)\n    {\n        case 0: return MA_CHANNEL_NONE;\n\n        case 1:\n        {\n            return MA_CHANNEL_MONO;\n        } break;\n\n        case 2:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n            }\n        } break;\n\n        case 3: /* No defined, but best guess. */\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_FRONT_CENTER;\n            }\n        } break;\n\n        case 4:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n            }\n        } break;\n\n        case 5: /* Not defined, but best guess. */\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n                case 4: return MA_CHANNEL_FRONT_CENTER;\n            }\n        } break;\n\n        case 6:\n        default:\n        {\n            switch (channelIndex) {\n                case 0: return MA_CHANNEL_FRONT_LEFT;\n                case 1: return MA_CHANNEL_FRONT_RIGHT;\n                case 2: return MA_CHANNEL_BACK_LEFT;\n                case 3: return MA_CHANNEL_BACK_RIGHT;\n                case 4: return MA_CHANNEL_FRONT_CENTER;\n                case 5: return MA_CHANNEL_LFE;\n            }\n        } break;\n    }\n\n    if (channelCount > 6) {\n        if (channelIndex < 32) {    /* We have 32 AUX channels. */\n            return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));\n        }\n    }\n\n    /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */\n    return MA_CHANNEL_NONE;\n}\n\n\nstatic ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)\n{\n    if (channelCount == 0 || channelIndex >= channelCount) {\n        return MA_CHANNEL_NONE;\n    }\n\n    switch (standardChannelMap)\n    {\n        case ma_standard_channel_map_alsa:\n        {\n            return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex);\n        } break;\n\n        case ma_standard_channel_map_rfc3551:\n        {\n            return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex);\n        } break;\n\n        case ma_standard_channel_map_flac:\n        {\n            return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex);\n        } break;\n\n        case ma_standard_channel_map_vorbis:\n        {\n            return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex);\n        } break;\n\n        case ma_standard_channel_map_sound4:\n        {\n            return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex);\n        } break;\n\n        case ma_standard_channel_map_sndio:\n        {\n            return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex);\n        } break;\n\n        case ma_standard_channel_map_microsoft: /* Also default. */\n        /*case ma_standard_channel_map_default;*/\n        default:\n        {\n            return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex);\n        } break;\n    }\n}\n\nMA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels)\n{\n    ma_uint32 iChannel;\n\n    if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) {\n        return;\n    }\n\n    for (iChannel = 0; iChannel < channels; iChannel += 1) {\n        if (channelMapCap == 0) {\n            break;  /* Ran out of room. */\n        }\n\n        pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel);\n        pChannelMap   += 1;\n        channelMapCap -= 1;\n    }\n}\n\nMA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)\n{\n    if (pOut != NULL && pIn != NULL && channels > 0) {\n        MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels);\n    }\n}\n\nMA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels)\n{\n    if (pOut == NULL || channels == 0) {\n        return;\n    }\n\n    if (pIn != NULL) {\n        ma_channel_map_copy(pOut, pIn, channels);\n    } else {\n        ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels);\n    }\n}\n\nMA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels)\n{\n    /* A channel count of 0 is invalid. */\n    if (channels == 0) {\n        return MA_FALSE;\n    }\n\n    /* It does not make sense to have a mono channel when there is more than 1 channel. */\n    if (channels > 1) {\n        ma_uint32 iChannel;\n        for (iChannel = 0; iChannel < channels; ++iChannel) {\n            if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) {\n                return MA_FALSE;\n            }\n        }\n    }\n\n    return MA_TRUE;\n}\n\nMA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels)\n{\n    ma_uint32 iChannel;\n\n    if (pChannelMapA == pChannelMapB) {\n        return MA_TRUE;\n    }\n\n    for (iChannel = 0; iChannel < channels; ++iChannel) {\n        if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) {\n            return MA_FALSE;\n        }\n    }\n\n    return MA_TRUE;\n}\n\nMA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels)\n{\n    ma_uint32 iChannel;\n\n    /* A null channel map is equivalent to the default channel map. */\n    if (pChannelMap == NULL) {\n        return MA_FALSE;\n    }\n\n    for (iChannel = 0; iChannel < channels; ++iChannel) {\n        if (pChannelMap[iChannel] != MA_CHANNEL_NONE) {\n            return MA_FALSE;\n        }\n    }\n\n    return MA_TRUE;\n}\n\nMA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition)\n{\n    return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL);\n}\n\nMA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex)\n{\n    ma_uint32 iChannel;\n\n    if (pChannelIndex != NULL) {\n        *pChannelIndex = (ma_uint32)-1;\n    }\n\n    for (iChannel = 0; iChannel < channels; ++iChannel) {\n        if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) {\n            if (pChannelIndex != NULL) {\n                *pChannelIndex = iChannel;\n            }\n\n            return MA_TRUE;\n        }\n    }\n\n    /* Getting here means the channel position was not found. */\n    return MA_FALSE;\n}\n\nMA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap)\n{\n    size_t len;\n    ma_uint32 iChannel;\n\n    len = 0;\n\n    for (iChannel = 0; iChannel < channels; iChannel += 1) {\n        const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel));\n        size_t channelStrLen = strlen(pChannelStr);\n\n        /* Append the string if necessary. */\n        if (pBufferOut != NULL && bufferCap > len + channelStrLen) {\n            MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen);\n        }\n        len += channelStrLen;\n\n        /* Append a space if it's not the last item. */\n        if (iChannel+1 < channels) {\n            if (pBufferOut != NULL && bufferCap > len + 1) {\n                pBufferOut[len] = ' ';\n            }\n            len += 1;\n        }\n    }\n\n    /* Null terminate. Don't increment the length here. */\n    if (pBufferOut != NULL && bufferCap > len + 1) {\n        pBufferOut[len] = '\\0';\n    }\n\n    return len;\n}\n\nMA_API const char* ma_channel_position_to_string(ma_channel channel)\n{\n    switch (channel)\n    {\n        case MA_CHANNEL_NONE              : return \"CHANNEL_NONE\";\n        case MA_CHANNEL_MONO              : return \"CHANNEL_MONO\";\n        case MA_CHANNEL_FRONT_LEFT        : return \"CHANNEL_FRONT_LEFT\";\n        case MA_CHANNEL_FRONT_RIGHT       : return \"CHANNEL_FRONT_RIGHT\";\n        case MA_CHANNEL_FRONT_CENTER      : return \"CHANNEL_FRONT_CENTER\";\n        case MA_CHANNEL_LFE               : return \"CHANNEL_LFE\";\n        case MA_CHANNEL_BACK_LEFT         : return \"CHANNEL_BACK_LEFT\";\n        case MA_CHANNEL_BACK_RIGHT        : return \"CHANNEL_BACK_RIGHT\";\n        case MA_CHANNEL_FRONT_LEFT_CENTER : return \"CHANNEL_FRONT_LEFT_CENTER \";\n        case MA_CHANNEL_FRONT_RIGHT_CENTER: return \"CHANNEL_FRONT_RIGHT_CENTER\";\n        case MA_CHANNEL_BACK_CENTER       : return \"CHANNEL_BACK_CENTER\";\n        case MA_CHANNEL_SIDE_LEFT         : return \"CHANNEL_SIDE_LEFT\";\n        case MA_CHANNEL_SIDE_RIGHT        : return \"CHANNEL_SIDE_RIGHT\";\n        case MA_CHANNEL_TOP_CENTER        : return \"CHANNEL_TOP_CENTER\";\n        case MA_CHANNEL_TOP_FRONT_LEFT    : return \"CHANNEL_TOP_FRONT_LEFT\";\n        case MA_CHANNEL_TOP_FRONT_CENTER  : return \"CHANNEL_TOP_FRONT_CENTER\";\n        case MA_CHANNEL_TOP_FRONT_RIGHT   : return \"CHANNEL_TOP_FRONT_RIGHT\";\n        case MA_CHANNEL_TOP_BACK_LEFT     : return \"CHANNEL_TOP_BACK_LEFT\";\n        case MA_CHANNEL_TOP_BACK_CENTER   : return \"CHANNEL_TOP_BACK_CENTER\";\n        case MA_CHANNEL_TOP_BACK_RIGHT    : return \"CHANNEL_TOP_BACK_RIGHT\";\n        case MA_CHANNEL_AUX_0             : return \"CHANNEL_AUX_0\";\n        case MA_CHANNEL_AUX_1             : return \"CHANNEL_AUX_1\";\n        case MA_CHANNEL_AUX_2             : return \"CHANNEL_AUX_2\";\n        case MA_CHANNEL_AUX_3             : return \"CHANNEL_AUX_3\";\n        case MA_CHANNEL_AUX_4             : return \"CHANNEL_AUX_4\";\n        case MA_CHANNEL_AUX_5             : return \"CHANNEL_AUX_5\";\n        case MA_CHANNEL_AUX_6             : return \"CHANNEL_AUX_6\";\n        case MA_CHANNEL_AUX_7             : return \"CHANNEL_AUX_7\";\n        case MA_CHANNEL_AUX_8             : return \"CHANNEL_AUX_8\";\n        case MA_CHANNEL_AUX_9             : return \"CHANNEL_AUX_9\";\n        case MA_CHANNEL_AUX_10            : return \"CHANNEL_AUX_10\";\n        case MA_CHANNEL_AUX_11            : return \"CHANNEL_AUX_11\";\n        case MA_CHANNEL_AUX_12            : return \"CHANNEL_AUX_12\";\n        case MA_CHANNEL_AUX_13            : return \"CHANNEL_AUX_13\";\n        case MA_CHANNEL_AUX_14            : return \"CHANNEL_AUX_14\";\n        case MA_CHANNEL_AUX_15            : return \"CHANNEL_AUX_15\";\n        case MA_CHANNEL_AUX_16            : return \"CHANNEL_AUX_16\";\n        case MA_CHANNEL_AUX_17            : return \"CHANNEL_AUX_17\";\n        case MA_CHANNEL_AUX_18            : return \"CHANNEL_AUX_18\";\n        case MA_CHANNEL_AUX_19            : return \"CHANNEL_AUX_19\";\n        case MA_CHANNEL_AUX_20            : return \"CHANNEL_AUX_20\";\n        case MA_CHANNEL_AUX_21            : return \"CHANNEL_AUX_21\";\n        case MA_CHANNEL_AUX_22            : return \"CHANNEL_AUX_22\";\n        case MA_CHANNEL_AUX_23            : return \"CHANNEL_AUX_23\";\n        case MA_CHANNEL_AUX_24            : return \"CHANNEL_AUX_24\";\n        case MA_CHANNEL_AUX_25            : return \"CHANNEL_AUX_25\";\n        case MA_CHANNEL_AUX_26            : return \"CHANNEL_AUX_26\";\n        case MA_CHANNEL_AUX_27            : return \"CHANNEL_AUX_27\";\n        case MA_CHANNEL_AUX_28            : return \"CHANNEL_AUX_28\";\n        case MA_CHANNEL_AUX_29            : return \"CHANNEL_AUX_29\";\n        case MA_CHANNEL_AUX_30            : return \"CHANNEL_AUX_30\";\n        case MA_CHANNEL_AUX_31            : return \"CHANNEL_AUX_31\";\n        default: break;\n    }\n\n    return \"UNKNOWN\";\n}\n\n\n\n/**************************************************************************************************************************************************************\n\nConversion Helpers\n\n**************************************************************************************************************************************************************/\nMA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn)\n{\n    ma_data_converter_config config;\n\n    config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut);\n    config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);\n\n    return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config);\n}\n\nMA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig)\n{\n    ma_result result;\n    ma_data_converter converter;\n\n    if (frameCountIn == 0 || pConfig == NULL) {\n        return 0;\n    }\n\n    result = ma_data_converter_init(pConfig, NULL, &converter);\n    if (result != MA_SUCCESS) {\n        return 0;   /* Failed to initialize the data converter. */\n    }\n\n    if (pOut == NULL) {\n        result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut);\n        if (result != MA_SUCCESS) {\n            if (result == MA_NOT_IMPLEMENTED) {\n                /* No way to calculate the number of frames, so we'll need to brute force it and loop. */\n                frameCountOut = 0;\n\n                while (frameCountIn > 0) {\n                    ma_uint64 framesProcessedIn  = frameCountIn;\n                    ma_uint64 framesProcessedOut = 0xFFFFFFFF;\n\n                    result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut);\n                    if (result != MA_SUCCESS) {\n                        break;\n                    }\n\n                    frameCountIn  -= framesProcessedIn;\n                }\n            }\n        }\n    } else {\n        result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut);\n        if (result != MA_SUCCESS) {\n            frameCountOut = 0;\n        }\n    }\n\n    ma_data_converter_uninit(&converter, NULL);\n    return frameCountOut;\n}\n\n\n/**************************************************************************************************************************************************************\n\nRing Buffer\n\n**************************************************************************************************************************************************************/\nstatic MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)\n{\n    return encodedOffset & 0x7FFFFFFF;\n}\n\nstatic MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)\n{\n    return encodedOffset & 0x80000000;\n}\n\nstatic MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)\n{\n    MA_ASSERT(pRB != NULL);\n    return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset)));\n}\n\nstatic MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)\n{\n    MA_ASSERT(pRB != NULL);\n    return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset)));\n}\n\nstatic MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)\n{\n    return offsetLoopFlag | offsetInBytes;\n}\n\nstatic MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)\n{\n    MA_ASSERT(pOffsetInBytes != NULL);\n    MA_ASSERT(pOffsetLoopFlag != NULL);\n\n    *pOffsetInBytes  = ma_rb__extract_offset_in_bytes(encodedOffset);\n    *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);\n}\n\n\nMA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)\n{\n    ma_result result;\n    const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);\n\n    if (pRB == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (subbufferSizeInBytes == 0 || subbufferCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (subbufferSizeInBytes > maxSubBufferSize) {\n        return MA_INVALID_ARGS;    /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */\n    }\n\n\n    MA_ZERO_OBJECT(pRB);\n\n    result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;\n    pRB->subbufferCount = (ma_uint32)subbufferCount;\n\n    if (pOptionalPreallocatedBuffer != NULL) {\n        pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;\n        pRB->pBuffer = pOptionalPreallocatedBuffer;\n    } else {\n        size_t bufferSizeInBytes;\n\n        /*\n        Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this\n        we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT.\n        */\n        pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT;\n\n        bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;\n        pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks);\n        if (pRB->pBuffer == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n\n        MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes);\n        pRB->ownsBuffer = MA_TRUE;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)\n{\n    return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);\n}\n\nMA_API void ma_rb_uninit(ma_rb* pRB)\n{\n    if (pRB == NULL) {\n        return;\n    }\n\n    if (pRB->ownsBuffer) {\n        ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks);\n    }\n}\n\nMA_API void ma_rb_reset(ma_rb* pRB)\n{\n    if (pRB == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_32(&pRB->encodedReadOffset, 0);\n    ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0);\n}\n\nMA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)\n{\n    ma_uint32 writeOffset;\n    ma_uint32 writeOffsetInBytes;\n    ma_uint32 writeOffsetLoopFlag;\n    ma_uint32 readOffset;\n    ma_uint32 readOffsetInBytes;\n    ma_uint32 readOffsetLoopFlag;\n    size_t bytesAvailable;\n    size_t bytesRequested;\n\n    if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The returned buffer should never move ahead of the write pointer. */\n    writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);\n    ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);\n\n    readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);\n    ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);\n\n    /*\n    The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we\n    can only read up to the write pointer. If not, we can only read up to the end of the buffer.\n    */\n    if (readOffsetLoopFlag == writeOffsetLoopFlag) {\n        bytesAvailable = writeOffsetInBytes - readOffsetInBytes;\n    } else {\n        bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;\n    }\n\n    bytesRequested = *pSizeInBytes;\n    if (bytesRequested > bytesAvailable) {\n        bytesRequested = bytesAvailable;\n    }\n\n    *pSizeInBytes = bytesRequested;\n    (*ppBufferOut) = ma_rb__get_read_ptr(pRB);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes)\n{\n    ma_uint32 readOffset;\n    ma_uint32 readOffsetInBytes;\n    ma_uint32 readOffsetLoopFlag;\n    ma_uint32 newReadOffsetInBytes;\n    ma_uint32 newReadOffsetLoopFlag;\n\n    if (pRB == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);\n    ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);\n\n    /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */\n    newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);\n    if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {\n        return MA_INVALID_ARGS;    /* <-- sizeInBytes will cause the read offset to overflow. */\n    }\n\n    /* Move the read pointer back to the start if necessary. */\n    newReadOffsetLoopFlag = readOffsetLoopFlag;\n    if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {\n        newReadOffsetInBytes = 0;\n        newReadOffsetLoopFlag ^= 0x80000000;\n    }\n\n    ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes));\n\n    if (ma_rb_pointer_distance(pRB) == 0) {\n        return MA_AT_END;\n    } else {\n        return MA_SUCCESS;\n    }\n}\n\nMA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)\n{\n    ma_uint32 readOffset;\n    ma_uint32 readOffsetInBytes;\n    ma_uint32 readOffsetLoopFlag;\n    ma_uint32 writeOffset;\n    ma_uint32 writeOffsetInBytes;\n    ma_uint32 writeOffsetLoopFlag;\n    size_t bytesAvailable;\n    size_t bytesRequested;\n\n    if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The returned buffer should never overtake the read buffer. */\n    readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);\n    ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);\n\n    writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);\n    ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);\n\n    /*\n    In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only\n    write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should\n    never overtake the read pointer.\n    */\n    if (writeOffsetLoopFlag == readOffsetLoopFlag) {\n        bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;\n    } else {\n        bytesAvailable = readOffsetInBytes - writeOffsetInBytes;\n    }\n\n    bytesRequested = *pSizeInBytes;\n    if (bytesRequested > bytesAvailable) {\n        bytesRequested = bytesAvailable;\n    }\n\n    *pSizeInBytes = bytesRequested;\n    *ppBufferOut  = ma_rb__get_write_ptr(pRB);\n\n    /* Clear the buffer if desired. */\n    if (pRB->clearOnWriteAcquire) {\n        MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes)\n{\n    ma_uint32 writeOffset;\n    ma_uint32 writeOffsetInBytes;\n    ma_uint32 writeOffsetLoopFlag;\n    ma_uint32 newWriteOffsetInBytes;\n    ma_uint32 newWriteOffsetLoopFlag;\n\n    if (pRB == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);\n    ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);\n\n    /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */\n    newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);\n    if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {\n        return MA_INVALID_ARGS;    /* <-- sizeInBytes will cause the read offset to overflow. */\n    }\n\n    /* Move the read pointer back to the start if necessary. */\n    newWriteOffsetLoopFlag = writeOffsetLoopFlag;\n    if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {\n        newWriteOffsetInBytes = 0;\n        newWriteOffsetLoopFlag ^= 0x80000000;\n    }\n\n    ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes));\n\n    if (ma_rb_pointer_distance(pRB) == 0) {\n        return MA_AT_END;\n    } else {\n        return MA_SUCCESS;\n    }\n}\n\nMA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)\n{\n    ma_uint32 readOffset;\n    ma_uint32 readOffsetInBytes;\n    ma_uint32 readOffsetLoopFlag;\n    ma_uint32 writeOffset;\n    ma_uint32 writeOffsetInBytes;\n    ma_uint32 writeOffsetLoopFlag;\n    ma_uint32 newReadOffsetInBytes;\n    ma_uint32 newReadOffsetLoopFlag;\n\n    if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {\n        return MA_INVALID_ARGS;\n    }\n\n    readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);\n    ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);\n\n    writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);\n    ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);\n\n    newReadOffsetLoopFlag = readOffsetLoopFlag;\n\n    /* We cannot go past the write buffer. */\n    if (readOffsetLoopFlag == writeOffsetLoopFlag) {\n        if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {\n            newReadOffsetInBytes = writeOffsetInBytes;\n        } else {\n            newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);\n        }\n    } else {\n        /* May end up looping. */\n        if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {\n            newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;\n            newReadOffsetLoopFlag ^= 0x80000000;    /* <-- Looped. */\n        } else {\n            newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);\n        }\n    }\n\n    ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)\n{\n    ma_uint32 readOffset;\n    ma_uint32 readOffsetInBytes;\n    ma_uint32 readOffsetLoopFlag;\n    ma_uint32 writeOffset;\n    ma_uint32 writeOffsetInBytes;\n    ma_uint32 writeOffsetLoopFlag;\n    ma_uint32 newWriteOffsetInBytes;\n    ma_uint32 newWriteOffsetLoopFlag;\n\n    if (pRB == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);\n    ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);\n\n    writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);\n    ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);\n\n    newWriteOffsetLoopFlag = writeOffsetLoopFlag;\n\n    /* We cannot go past the write buffer. */\n    if (readOffsetLoopFlag == writeOffsetLoopFlag) {\n        /* May end up looping. */\n        if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {\n            newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;\n            newWriteOffsetLoopFlag ^= 0x80000000;    /* <-- Looped. */\n        } else {\n            newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);\n        }\n    } else {\n        if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {\n            newWriteOffsetInBytes = readOffsetInBytes;\n        } else {\n            newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);\n        }\n    }\n\n    ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));\n    return MA_SUCCESS;\n}\n\nMA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB)\n{\n    ma_uint32 readOffset;\n    ma_uint32 readOffsetInBytes;\n    ma_uint32 readOffsetLoopFlag;\n    ma_uint32 writeOffset;\n    ma_uint32 writeOffsetInBytes;\n    ma_uint32 writeOffsetLoopFlag;\n\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);\n    ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);\n\n    writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);\n    ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);\n\n    if (readOffsetLoopFlag == writeOffsetLoopFlag) {\n        return writeOffsetInBytes - readOffsetInBytes;\n    } else {\n        return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);\n    }\n}\n\nMA_API ma_uint32 ma_rb_available_read(ma_rb* pRB)\n{\n    ma_int32 dist;\n\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    dist = ma_rb_pointer_distance(pRB);\n    if (dist < 0) {\n        return 0;\n    }\n\n    return dist;\n}\n\nMA_API ma_uint32 ma_rb_available_write(ma_rb* pRB)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB));\n}\n\nMA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    return pRB->subbufferSizeInBytes;\n}\n\nMA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    if (pRB->subbufferStrideInBytes == 0) {\n        return (size_t)pRB->subbufferSizeInBytes;\n    }\n\n    return (size_t)pRB->subbufferStrideInBytes;\n}\n\nMA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);\n}\n\nMA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)\n{\n    if (pRB == NULL) {\n        return NULL;\n    }\n\n    return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));\n}\n\n\n\nstatic ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */\n    ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource;\n    ma_result result;\n    ma_uint64 totalFramesRead;\n\n    MA_ASSERT(pRB != NULL);\n\n    /* We need to run this in a loop since the ring buffer itself may loop. */\n    totalFramesRead = 0;\n    while (totalFramesRead < frameCount) {\n        void* pMappedBuffer;\n        ma_uint32 mappedFrameCount;\n        ma_uint64 framesToRead = frameCount - totalFramesRead;\n        if (framesToRead > 0xFFFFFFFF) {\n            framesToRead = 0xFFFFFFFF;\n        }\n\n        mappedFrameCount = (ma_uint32)framesToRead;\n        result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer);\n        if (result != MA_SUCCESS) {\n            break;\n        }\n\n        if (mappedFrameCount == 0) {\n            break;  /* <-- End of ring buffer. */\n        }\n\n        ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels);\n\n        result = ma_pcm_rb_commit_read(pRB, mappedFrameCount);\n        if (result != MA_SUCCESS) {\n            break;\n        }\n\n        totalFramesRead += mappedFrameCount;\n    }\n\n    *pFramesRead = totalFramesRead;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource;\n    MA_ASSERT(pRB != NULL);\n\n    if (pFormat != NULL) {\n        *pFormat = pRB->format;\n    }\n\n    if (pChannels != NULL) {\n        *pChannels = pRB->channels;\n    }\n\n    if (pSampleRate != NULL) {\n        *pSampleRate = pRB->sampleRate;\n    }\n\n    /* Just assume the default channel map. */\n    if (pChannelMap != NULL) {\n        ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_data_source_vtable ma_gRBDataSourceVTable =\n{\n    ma_pcm_rb_data_source__on_read,\n    NULL,   /* onSeek */\n    ma_pcm_rb_data_source__on_get_data_format,\n    NULL,   /* onGetCursor */\n    NULL,   /* onGetLength */\n    NULL,   /* onSetLooping */\n    0\n};\n\nstatic MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)\n{\n    MA_ASSERT(pRB != NULL);\n\n    return ma_get_bytes_per_frame(pRB->format, pRB->channels);\n}\n\nMA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB)\n{\n    ma_uint32 bpf;\n    ma_result result;\n\n    if (pRB == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pRB);\n\n    bpf = ma_get_bytes_per_frame(format, channels);\n    if (bpf == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pRB->format     = format;\n    pRB->channels   = channels;\n    pRB->sampleRate = 0;    /* The sample rate is not passed in as a parameter. */\n\n    /* The PCM ring buffer is a data source. We need to get that set up as well. */\n    {\n        ma_data_source_config dataSourceConfig = ma_data_source_config_init();\n        dataSourceConfig.vtable = &ma_gRBDataSourceVTable;\n\n        result = ma_data_source_init(&dataSourceConfig, &pRB->ds);\n        if (result != MA_SUCCESS) {\n            ma_rb_uninit(&pRB->rb);\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB)\n{\n    return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);\n}\n\nMA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB)\n{\n    if (pRB == NULL) {\n        return;\n    }\n\n    ma_data_source_uninit(&pRB->ds);\n    ma_rb_uninit(&pRB->rb);\n}\n\nMA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB)\n{\n    if (pRB == NULL) {\n        return;\n    }\n\n    ma_rb_reset(&pRB->rb);\n}\n\nMA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)\n{\n    size_t sizeInBytes;\n    ma_result result;\n\n    if (pRB == NULL || pSizeInFrames == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);\n\n    result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames)\n{\n    if (pRB == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));\n}\n\nMA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)\n{\n    size_t sizeInBytes;\n    ma_result result;\n\n    if (pRB == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);\n\n    result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames)\n{\n    if (pRB == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));\n}\n\nMA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)\n{\n    if (pRB == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));\n}\n\nMA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)\n{\n    if (pRB == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));\n}\n\nMA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);\n}\n\nMA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);\n}\n\nMA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);\n}\n\nMA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));\n}\n\nMA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));\n}\n\nMA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));\n}\n\nMA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)\n{\n    if (pRB == NULL) {\n        return NULL;\n    }\n\n    return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);\n}\n\nMA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB)\n{\n    if (pRB == NULL) {\n        return ma_format_unknown;\n    }\n\n    return pRB->format;\n}\n\nMA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    return pRB->channels;\n}\n\nMA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB)\n{\n    if (pRB == NULL) {\n        return 0;\n    }\n\n    return pRB->sampleRate;\n}\n\nMA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate)\n{\n    if (pRB == NULL) {\n        return;\n    }\n\n    pRB->sampleRate = sampleRate;\n}\n\n\n\nMA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB)\n{\n    ma_result result;\n    ma_uint32 sizeInFrames;\n\n    sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5);\n    if (sizeInFrames == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */\n    ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB)\n{\n    ma_pcm_rb_uninit((ma_pcm_rb*)pRB);\n    return MA_SUCCESS;\n}\n\n\n\n/**************************************************************************************************************************************************************\n\nMiscellaneous Helpers\n\n**************************************************************************************************************************************************************/\nMA_API const char* ma_result_description(ma_result result)\n{\n    switch (result)\n    {\n        case MA_SUCCESS:                       return \"No error\";\n        case MA_ERROR:                         return \"Unknown error\";\n        case MA_INVALID_ARGS:                  return \"Invalid argument\";\n        case MA_INVALID_OPERATION:             return \"Invalid operation\";\n        case MA_OUT_OF_MEMORY:                 return \"Out of memory\";\n        case MA_OUT_OF_RANGE:                  return \"Out of range\";\n        case MA_ACCESS_DENIED:                 return \"Permission denied\";\n        case MA_DOES_NOT_EXIST:                return \"Resource does not exist\";\n        case MA_ALREADY_EXISTS:                return \"Resource already exists\";\n        case MA_TOO_MANY_OPEN_FILES:           return \"Too many open files\";\n        case MA_INVALID_FILE:                  return \"Invalid file\";\n        case MA_TOO_BIG:                       return \"Too large\";\n        case MA_PATH_TOO_LONG:                 return \"Path too long\";\n        case MA_NAME_TOO_LONG:                 return \"Name too long\";\n        case MA_NOT_DIRECTORY:                 return \"Not a directory\";\n        case MA_IS_DIRECTORY:                  return \"Is a directory\";\n        case MA_DIRECTORY_NOT_EMPTY:           return \"Directory not empty\";\n        case MA_AT_END:                        return \"At end\";\n        case MA_NO_SPACE:                      return \"No space available\";\n        case MA_BUSY:                          return \"Device or resource busy\";\n        case MA_IO_ERROR:                      return \"Input/output error\";\n        case MA_INTERRUPT:                     return \"Interrupted\";\n        case MA_UNAVAILABLE:                   return \"Resource unavailable\";\n        case MA_ALREADY_IN_USE:                return \"Resource already in use\";\n        case MA_BAD_ADDRESS:                   return \"Bad address\";\n        case MA_BAD_SEEK:                      return \"Illegal seek\";\n        case MA_BAD_PIPE:                      return \"Broken pipe\";\n        case MA_DEADLOCK:                      return \"Deadlock\";\n        case MA_TOO_MANY_LINKS:                return \"Too many links\";\n        case MA_NOT_IMPLEMENTED:               return \"Not implemented\";\n        case MA_NO_MESSAGE:                    return \"No message of desired type\";\n        case MA_BAD_MESSAGE:                   return \"Invalid message\";\n        case MA_NO_DATA_AVAILABLE:             return \"No data available\";\n        case MA_INVALID_DATA:                  return \"Invalid data\";\n        case MA_TIMEOUT:                       return \"Timeout\";\n        case MA_NO_NETWORK:                    return \"Network unavailable\";\n        case MA_NOT_UNIQUE:                    return \"Not unique\";\n        case MA_NOT_SOCKET:                    return \"Socket operation on non-socket\";\n        case MA_NO_ADDRESS:                    return \"Destination address required\";\n        case MA_BAD_PROTOCOL:                  return \"Protocol wrong type for socket\";\n        case MA_PROTOCOL_UNAVAILABLE:          return \"Protocol not available\";\n        case MA_PROTOCOL_NOT_SUPPORTED:        return \"Protocol not supported\";\n        case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return \"Protocol family not supported\";\n        case MA_ADDRESS_FAMILY_NOT_SUPPORTED:  return \"Address family not supported\";\n        case MA_SOCKET_NOT_SUPPORTED:          return \"Socket type not supported\";\n        case MA_CONNECTION_RESET:              return \"Connection reset\";\n        case MA_ALREADY_CONNECTED:             return \"Already connected\";\n        case MA_NOT_CONNECTED:                 return \"Not connected\";\n        case MA_CONNECTION_REFUSED:            return \"Connection refused\";\n        case MA_NO_HOST:                       return \"No host\";\n        case MA_IN_PROGRESS:                   return \"Operation in progress\";\n        case MA_CANCELLED:                     return \"Operation cancelled\";\n        case MA_MEMORY_ALREADY_MAPPED:         return \"Memory already mapped\";\n\n        case MA_FORMAT_NOT_SUPPORTED:          return \"Format not supported\";\n        case MA_DEVICE_TYPE_NOT_SUPPORTED:     return \"Device type not supported\";\n        case MA_SHARE_MODE_NOT_SUPPORTED:      return \"Share mode not supported\";\n        case MA_NO_BACKEND:                    return \"No backend\";\n        case MA_NO_DEVICE:                     return \"No device\";\n        case MA_API_NOT_FOUND:                 return \"API not found\";\n        case MA_INVALID_DEVICE_CONFIG:         return \"Invalid device config\";\n\n        case MA_DEVICE_NOT_INITIALIZED:        return \"Device not initialized\";\n        case MA_DEVICE_NOT_STARTED:            return \"Device not started\";\n\n        case MA_FAILED_TO_INIT_BACKEND:        return \"Failed to initialize backend\";\n        case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return \"Failed to open backend device\";\n        case MA_FAILED_TO_START_BACKEND_DEVICE: return \"Failed to start backend device\";\n        case MA_FAILED_TO_STOP_BACKEND_DEVICE: return \"Failed to stop backend device\";\n\n        default:                               return \"Unknown error\";\n    }\n}\n\nMA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks != NULL) {\n        if (pAllocationCallbacks->onMalloc != NULL) {\n            return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);\n        } else {\n            return NULL;    /* Do not fall back to the default implementation. */\n        }\n    } else {\n        return ma__malloc_default(sz, NULL);\n    }\n}\n\nMA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    void* p = ma_malloc(sz, pAllocationCallbacks);\n    if (p != NULL) {\n        MA_ZERO_MEMORY(p, sz);\n    }\n\n    return p;\n}\n\nMA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks != NULL) {\n        if (pAllocationCallbacks->onRealloc != NULL) {\n            return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData);\n        } else {\n            return NULL;    /* Do not fall back to the default implementation. */\n        }\n    } else {\n        return ma__realloc_default(p, sz, NULL);\n    }\n}\n\nMA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (p == NULL) {\n        return;\n    }\n\n    if (pAllocationCallbacks != NULL) {\n        if (pAllocationCallbacks->onFree != NULL) {\n            pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);\n        } else {\n            return; /* Do no fall back to the default implementation. */\n        }\n    } else {\n        ma__free_default(p, NULL);\n    }\n}\n\nMA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    size_t extraBytes;\n    void* pUnaligned;\n    void* pAligned;\n\n    if (alignment == 0) {\n        return 0;\n    }\n\n    extraBytes = alignment-1 + sizeof(void*);\n\n    pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks);\n    if (pUnaligned == NULL) {\n        return NULL;\n    }\n\n    pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));\n    ((void**)pAligned)[-1] = pUnaligned;\n\n    return pAligned;\n}\n\nMA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_free(((void**)p)[-1], pAllocationCallbacks);\n}\n\nMA_API const char* ma_get_format_name(ma_format format)\n{\n    switch (format)\n    {\n        case ma_format_unknown: return \"Unknown\";\n        case ma_format_u8:      return \"8-bit Unsigned Integer\";\n        case ma_format_s16:     return \"16-bit Signed Integer\";\n        case ma_format_s24:     return \"24-bit Signed Integer (Tightly Packed)\";\n        case ma_format_s32:     return \"32-bit Signed Integer\";\n        case ma_format_f32:     return \"32-bit IEEE Floating Point\";\n        default:                return \"Invalid\";\n    }\n}\n\nMA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)\n{\n    ma_uint32 i;\n    for (i = 0; i < channels; ++i) {\n        pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);\n    }\n}\n\n\nMA_API ma_uint32 ma_get_bytes_per_sample(ma_format format)\n{\n    ma_uint32 sizes[] = {\n        0,  /* unknown */\n        1,  /* u8 */\n        2,  /* s16 */\n        3,  /* s24 */\n        4,  /* s32 */\n        4,  /* f32 */\n    };\n    return sizes[format];\n}\n\n\n\n#define MA_DATA_SOURCE_DEFAULT_RANGE_BEG        0\n#define MA_DATA_SOURCE_DEFAULT_RANGE_END        ~((ma_uint64)0)\n#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG   0\n#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END   ~((ma_uint64)0)\n\nMA_API ma_data_source_config ma_data_source_config_init(void)\n{\n    ma_data_source_config config;\n\n    MA_ZERO_OBJECT(&config);\n\n    return config;\n}\n\n\nMA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pDataSourceBase);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pDataSourceBase->vtable           = pConfig->vtable;\n    pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG;\n    pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END;\n    pDataSourceBase->loopBegInFrames  = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG;\n    pDataSourceBase->loopEndInFrames  = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END;\n    pDataSourceBase->pCurrent         = pDataSource;    /* Always read from ourself by default. */\n    pDataSourceBase->pNext            = NULL;\n    pDataSourceBase->onGetNext        = NULL;\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_data_source_uninit(ma_data_source* pDataSource)\n{\n    if (pDataSource == NULL) {\n        return;\n    }\n\n    /*\n    This is placeholder in case we need this later. Data sources need to call this in their\n    uninitialization routine to ensure things work later on if something is added here.\n    */\n}\n\nstatic ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource)\n{\n    ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource;\n\n    MA_ASSERT(pDataSource         != NULL);\n    MA_ASSERT(ppCurrentDataSource != NULL);\n\n    if (pCurrentDataSource->pCurrent == NULL) {\n        /*\n        The current data source is NULL. If we're using this in the context of a chain we need to return NULL\n        here so that we don't end up looping. Otherwise we just return the data source itself.\n        */\n        if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) {\n            pCurrentDataSource = NULL;\n        } else {\n            pCurrentDataSource = (ma_data_source_base*)pDataSource; /* Not being used in a chain. Make sure we just always read from the data source itself at all times. */\n        }\n    } else {\n        pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent;\n    }\n\n    *ppCurrentDataSource = pCurrentDataSource;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n    ma_result result;\n    ma_uint64 framesRead = 0;\n    ma_bool32 loop = ma_data_source_is_looping(pDataSource);\n\n    if (pDataSourceBase == NULL) {\n        return MA_AT_END;\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) {\n        /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */\n        result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);\n    } else {\n        /* Need to clamp to within the range. */\n        ma_uint64 relativeCursor;\n        ma_uint64 absoluteCursor;\n\n        result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor);\n        if (result != MA_SUCCESS) {\n            /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */\n            result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);\n        } else {\n            ma_uint64 rangeBeg;\n            ma_uint64 rangeEnd;\n\n            /* We have the cursor. We need to make sure we don't read beyond our range. */\n            rangeBeg = pDataSourceBase->rangeBegInFrames;\n            rangeEnd = pDataSourceBase->rangeEndInFrames;\n\n            absoluteCursor = rangeBeg + relativeCursor;\n\n            /* If looping, make sure we're within range. */\n            if (loop) {\n                if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {\n                    rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames);\n                }\n            }\n\n            if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) {\n                frameCount = (rangeEnd - absoluteCursor);\n            }\n\n            /*\n            If the cursor is sitting on the end of the range the frame count will be set to 0 which can\n            result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return\n            MA_AT_END so the higher level function can know about it.\n            */\n            if (frameCount > 0) {\n                result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);\n            } else {\n                result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */\n            }\n        }\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = framesRead;\n    }\n\n    /* We need to make sure MA_AT_END is returned if we hit the end of the range. */\n    if (result == MA_SUCCESS && framesRead == 0) {\n        result  = MA_AT_END;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    ma_result result = MA_SUCCESS;\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n    ma_data_source_base* pCurrentDataSource;\n    void* pRunningFramesOut = pFramesOut;\n    ma_uint64 totalFramesProcessed = 0;\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 emptyLoopCounter = 0; /* Keeps track of how many times 0 frames have been read. For infinite loop detection of sounds with no audio data. */\n    ma_bool32 loop;\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pDataSourceBase == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    loop = ma_data_source_is_looping(pDataSource);\n\n    /*\n    We need to know the data format so we can advance the output buffer as we read frames. If this\n    fails, chaining will not work and we'll just read as much as we can from the current source.\n    */\n    if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) {\n        result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead);\n    }\n\n    /*\n    Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and\n    only the current data source will be read from.\n    */\n\n    /* Keep reading until we've read as many frames as possible. */\n    while (totalFramesProcessed < frameCount) {\n        ma_uint64 framesProcessed;\n        ma_uint64 framesRemaining = frameCount - totalFramesProcessed;\n\n        /* We need to resolve the data source that we'll actually be reading from. */\n        result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);\n        if (result != MA_SUCCESS) {\n            break;\n        }\n\n        if (pCurrentDataSource == NULL) {\n            break;\n        }\n\n        result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed);\n        totalFramesProcessed += framesProcessed;\n\n        /*\n        If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is\n        not necessarily considered an error.\n        */\n        if (result != MA_SUCCESS && result != MA_AT_END) {\n            break;\n        }\n\n        /*\n        We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned\n        MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame.\n        */\n        if (result == MA_AT_END) {\n            /*\n            The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't\n            accidentally return MA_AT_END when data has been read in prior loop iterations. at the\n            end of this function, the result will be checked for MA_SUCCESS, and if the total\n            number of frames processed is 0, will be explicitly set to MA_AT_END.\n            */\n            result = MA_SUCCESS;\n\n            /*\n            We reached the end. If we're looping, we just loop back to the start of the current\n            data source. If we're not looping we need to check if we have another in the chain, and\n            if so, switch to it.\n            */\n            if (loop) {\n                if (framesProcessed == 0) {\n                    emptyLoopCounter += 1;\n                    if (emptyLoopCounter > 1) {\n                        break;  /* Infinite loop detected. Get out. */\n                    }\n                } else {\n                    emptyLoopCounter = 0;\n                }\n\n                result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames);\n                if (result != MA_SUCCESS) {\n                    break;  /* Failed to loop. Abort. */\n                }\n\n                /* Don't return MA_AT_END for looping sounds. */\n                result = MA_SUCCESS;\n            } else {\n                if (pCurrentDataSource->pNext != NULL) {\n                    pDataSourceBase->pCurrent = pCurrentDataSource->pNext;\n                } else if (pCurrentDataSource->onGetNext != NULL) {\n                    pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource);\n                    if (pDataSourceBase->pCurrent == NULL) {\n                        break;  /* Our callback did not return a next data source. We're done. */\n                    }\n                } else {\n                    /* Reached the end of the chain. We're done. */\n                    break;\n                }\n\n                /* The next data source needs to be rewound to ensure data is read in looping scenarios. */\n                result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0);\n                if (result != MA_SUCCESS) {\n                    break;\n                }\n            }\n        }\n\n        if (pRunningFramesOut != NULL) {\n            pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels));\n        }\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = totalFramesProcessed;\n    }\n\n    MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0));  /* We should never be returning MA_AT_END if we read some data. */\n\n    if (result == MA_SUCCESS && totalFramesProcessed == 0) {\n        result  = MA_AT_END;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked)\n{\n    return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked);\n}\n\nMA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n\n    if (pDataSourceBase == NULL) {\n        return MA_SUCCESS;\n    }\n\n    if (pDataSourceBase->vtable->onSeek == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    if (frameIndex > pDataSourceBase->rangeEndInFrames) {\n        return MA_INVALID_OPERATION;    /* Trying to seek to far forward. */\n    }\n\n    return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex);\n}\n\nMA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n    ma_result result;\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n\n    /* Initialize to defaults for safety just in case the data source does not implement this callback. */\n    if (pFormat != NULL) {\n        *pFormat = ma_format_unknown;\n    }\n    if (pChannels != NULL) {\n        *pChannels = 0;\n    }\n    if (pSampleRate != NULL) {\n        *pSampleRate = 0;\n    }\n    if (pChannelMap != NULL) {\n        MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);\n    }\n\n    if (pDataSourceBase == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pDataSourceBase->vtable->onGetDataFormat == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pFormat != NULL) {\n        *pFormat = format;\n    }\n    if (pChannels != NULL) {\n        *pChannels = channels;\n    }\n    if (pSampleRate != NULL) {\n        *pSampleRate = sampleRate;\n    }\n\n    /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n    ma_result result;\n    ma_uint64 cursor;\n\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;\n\n    if (pDataSourceBase == NULL) {\n        return MA_SUCCESS;\n    }\n\n    if (pDataSourceBase->vtable->onGetCursor == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* The cursor needs to be made relative to the start of the range. */\n    if (cursor < pDataSourceBase->rangeBegInFrames) {   /* Safety check so we don't return some huge number. */\n        *pCursor = 0;\n    } else {\n        *pCursor = cursor - pDataSourceBase->rangeBegInFrames;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n\n    if (pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pLength = 0;\n\n    if (pDataSourceBase == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /*\n    If we have a range defined we'll use that to determine the length. This is one of rare times\n    where we'll actually trust the caller. If they've set the range, I think it's mostly safe to\n    assume they've set it based on some higher level knowledge of the structure of the sound bank.\n    */\n    if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) {\n        *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames;\n        return MA_SUCCESS;\n    }\n\n    /*\n    Getting here means a range is not defined so we'll need to get the data source itself to tell\n    us the length.\n    */\n    if (pDataSourceBase->vtable->onGetLength == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pDataSourceBase->vtable->onGetLength(pDataSource, pLength);\n}\n\nMA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor)\n{\n    ma_result result;\n    ma_uint64 cursorInPCMFrames;\n    ma_uint32 sampleRate;\n\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;\n\n    result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */\n    *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength)\n{\n    ma_result result;\n    ma_uint64 lengthInPCMFrames;\n    ma_uint32 sampleRate;\n\n    if (pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pLength = 0;\n\n    result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */\n    *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping);\n\n    /* If there's no callback for this just treat it as a successful no-op. */\n    if (pDataSourceBase->vtable->onSetLooping == NULL) {\n        return MA_SUCCESS;\n    }\n\n    return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping);\n}\n\nMA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource)\n{\n    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return MA_FALSE;\n    }\n\n    return ma_atomic_load_32(&pDataSourceBase->isLooping);\n}\n\nMA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n    ma_result result;\n    ma_uint64 relativeCursor;\n    ma_uint64 absoluteCursor;\n    ma_bool32 doSeekAdjustment = MA_FALSE;\n\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (rangeEndInFrames < rangeBegInFrames) {\n        return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */\n    }\n\n    /*\n    We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now\n    so we can calculate it's absolute position before we change the range.\n    */\n    result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor);\n    if (result == MA_SUCCESS) {\n        doSeekAdjustment = MA_TRUE;\n        absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames;\n    } else {\n        /*\n        We couldn't get the position of the cursor. It probably means the data source has no notion\n        of a cursor. We'll just leave it at position 0. Don't treat this as an error.\n        */\n        doSeekAdjustment = MA_FALSE;\n        relativeCursor = 0;\n        absoluteCursor = 0;\n    }\n\n    pDataSourceBase->rangeBegInFrames = rangeBegInFrames;\n    pDataSourceBase->rangeEndInFrames = rangeEndInFrames;\n\n    /*\n    The commented out logic below was intended to maintain loop points in response to a change in the\n    range. However, this is not useful because it results in the sound breaking when you move the range\n    outside of the old loop points. I'm simplifying this by simply resetting the loop points. The\n    caller is expected to update their loop points if they change the range.\n\n    In practice this should be mostly a non-issue because the majority of the time the range will be\n    set once right after initialization.\n    */\n    pDataSourceBase->loopBegInFrames = 0;\n    pDataSourceBase->loopEndInFrames = ~((ma_uint64)0);\n\n\n    /*\n    Seek to within range. Note that our seek positions here are relative to the new range. We don't want\n    do do this if we failed to retrieve the cursor earlier on because it probably means the data source\n    has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but\n    I'm just not even going to attempt it.\n    */\n    if (doSeekAdjustment) {\n        if (absoluteCursor < rangeBegInFrames) {\n            ma_data_source_seek_to_pcm_frame(pDataSource, 0);\n        } else if (absoluteCursor > rangeEndInFrames) {\n            ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames);\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames)\n{\n    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return;\n    }\n\n    if (pRangeBegInFrames != NULL) {\n        *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames;\n    }\n\n    if (pRangeEndInFrames != NULL) {\n        *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames;\n    }\n}\n\nMA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (loopEndInFrames < loopBegInFrames) {\n        return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */\n    }\n\n    if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) {\n        return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */\n    }\n\n    pDataSourceBase->loopBegInFrames = loopBegInFrames;\n    pDataSourceBase->loopEndInFrames = loopEndInFrames;\n\n    /* The end cannot exceed the range. */\n    if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {\n        pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames)\n{\n    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return;\n    }\n\n    if (pLoopBegInFrames != NULL) {\n        *pLoopBegInFrames = pDataSourceBase->loopBegInFrames;\n    }\n\n    if (pLoopEndInFrames != NULL) {\n        *pLoopEndInFrames = pDataSourceBase->loopEndInFrames;\n    }\n}\n\nMA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pDataSourceBase->pCurrent = pCurrentDataSource;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource)\n{\n    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return NULL;\n    }\n\n    return pDataSourceBase->pCurrent;\n}\n\nMA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pDataSourceBase->pNext = pNextDataSource;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource)\n{\n    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return NULL;\n    }\n\n    return pDataSourceBase->pNext;\n}\n\nMA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext)\n{\n    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pDataSourceBase->onGetNext = onGetNext;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource)\n{\n    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;\n\n    if (pDataSource == NULL) {\n        return NULL;\n    }\n\n    return pDataSourceBase->onGetNext;\n}\n\n\nstatic ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;\n    ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE);\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = framesRead;\n    }\n\n    if (framesRead < frameCount || framesRead == 0) {\n        return MA_AT_END;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex);\n}\n\nstatic ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;\n\n    *pFormat     = pAudioBufferRef->format;\n    *pChannels   = pAudioBufferRef->channels;\n    *pSampleRate = pAudioBufferRef->sampleRate;\n    ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)\n{\n    ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;\n\n    *pCursor = pAudioBufferRef->cursor;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)\n{\n    ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;\n\n    *pLength = pAudioBufferRef->sizeInFrames;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable =\n{\n    ma_audio_buffer_ref__data_source_on_read,\n    ma_audio_buffer_ref__data_source_on_seek,\n    ma_audio_buffer_ref__data_source_on_get_data_format,\n    ma_audio_buffer_ref__data_source_on_get_cursor,\n    ma_audio_buffer_ref__data_source_on_get_length,\n    NULL,   /* onSetLooping */\n    0\n};\n\nMA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef)\n{\n    ma_result result;\n    ma_data_source_config dataSourceConfig;\n\n    if (pAudioBufferRef == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pAudioBufferRef);\n\n    dataSourceConfig = ma_data_source_config_init();\n    dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable;\n\n    result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pAudioBufferRef->format       = format;\n    pAudioBufferRef->channels     = channels;\n    pAudioBufferRef->sampleRate   = 0;  /* TODO: Version 0.12. Set this to sampleRate. */\n    pAudioBufferRef->cursor       = 0;\n    pAudioBufferRef->sizeInFrames = sizeInFrames;\n    pAudioBufferRef->pData        = pData;\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef)\n{\n    if (pAudioBufferRef == NULL) {\n        return;\n    }\n\n    ma_data_source_uninit(&pAudioBufferRef->ds);\n}\n\nMA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames)\n{\n    if (pAudioBufferRef == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pAudioBufferRef->cursor       = 0;\n    pAudioBufferRef->sizeInFrames = sizeInFrames;\n    pAudioBufferRef->pData        = pData;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)\n{\n    ma_uint64 totalFramesRead = 0;\n\n    if (pAudioBufferRef == NULL) {\n        return 0;\n    }\n\n    if (frameCount == 0) {\n        return 0;\n    }\n\n    while (totalFramesRead < frameCount) {\n        ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;\n        ma_uint64 framesRemaining = frameCount - totalFramesRead;\n        ma_uint64 framesToRead;\n\n        framesToRead = framesRemaining;\n        if (framesToRead > framesAvailable) {\n            framesToRead = framesAvailable;\n        }\n\n        if (pFramesOut != NULL) {\n            ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels);\n        }\n\n        totalFramesRead += framesToRead;\n\n        pAudioBufferRef->cursor += framesToRead;\n        if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {\n            if (loop) {\n                pAudioBufferRef->cursor = 0;\n            } else {\n                break;  /* We've reached the end and we're not looping. Done. */\n            }\n        }\n\n        MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames);\n    }\n\n    return totalFramesRead;\n}\n\nMA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex)\n{\n    if (pAudioBufferRef == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (frameIndex > pAudioBufferRef->sizeInFrames) {\n        return MA_INVALID_ARGS;\n    }\n\n    pAudioBufferRef->cursor = (size_t)frameIndex;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount)\n{\n    ma_uint64 framesAvailable;\n    ma_uint64 frameCount = 0;\n\n    if (ppFramesOut != NULL) {\n        *ppFramesOut = NULL;    /* Safety. */\n    }\n\n    if (pFrameCount != NULL) {\n        frameCount = *pFrameCount;\n        *pFrameCount = 0;       /* Safety. */\n    }\n\n    if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;\n    if (frameCount > framesAvailable) {\n        frameCount = framesAvailable;\n    }\n\n    *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels));\n    *pFrameCount = frameCount;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount)\n{\n    ma_uint64 framesAvailable;\n\n    if (pAudioBufferRef == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;\n    if (frameCount > framesAvailable) {\n        return MA_INVALID_ARGS;   /* The frame count was too big. This should never happen in an unmapping. Need to make sure the caller is aware of this. */\n    }\n\n    pAudioBufferRef->cursor += frameCount;\n\n    if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {\n        return MA_AT_END;   /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */\n    } else {\n        return MA_SUCCESS;\n    }\n}\n\nMA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef)\n{\n    if (pAudioBufferRef == NULL) {\n        return MA_FALSE;\n    }\n\n    return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames;\n}\n\nMA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor)\n{\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;\n\n    if (pAudioBufferRef == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = pAudioBufferRef->cursor;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength)\n{\n    if (pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pLength = 0;\n\n    if (pAudioBufferRef == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pLength = pAudioBufferRef->sizeInFrames;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames)\n{\n    if (pAvailableFrames == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pAvailableFrames = 0;\n\n    if (pAudioBufferRef == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) {\n        *pAvailableFrames = 0;\n    } else {\n        *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;\n    }\n\n    return MA_SUCCESS;\n}\n\n\n\n\nMA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_audio_buffer_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format       = format;\n    config.channels     = channels;\n    config.sampleRate   = 0;    /* TODO: Version 0.12. Set this to sampleRate. */\n    config.sizeInFrames = sizeInFrames;\n    config.pData        = pData;\n    ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks);\n\n    return config;\n}\n\nstatic ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer)\n{\n    ma_result result;\n\n    if (pAudioBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData));   /* Safety. Don't overwrite the extra data. */\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->sizeInFrames == 0) {\n        return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */\n    }\n\n    result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */\n    pAudioBuffer->ref.sampleRate = pConfig->sampleRate;\n\n    ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);\n\n    if (doCopy) {\n        ma_uint64 allocationSizeInBytes;\n        void* pData;\n\n        allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels);\n        if (allocationSizeInBytes > MA_SIZE_MAX) {\n            return MA_OUT_OF_MEMORY;    /* Too big. */\n        }\n\n        pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks);   /* Safe cast to size_t. */\n        if (pData == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n\n        if (pConfig->pData != NULL) {\n            ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);\n        } else {\n            ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);\n        }\n\n        ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames);\n        pAudioBuffer->ownsData = MA_TRUE;\n    } else {\n        ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames);\n        pAudioBuffer->ownsData = MA_FALSE;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree)\n{\n    if (pAudioBuffer == NULL) {\n        return;\n    }\n\n    if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) {\n        ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks);    /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */\n    }\n\n    if (doFree) {\n        ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks);\n    }\n\n    ma_audio_buffer_ref_uninit(&pAudioBuffer->ref);\n}\n\nMA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)\n{\n    return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer);\n}\n\nMA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)\n{\n    return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer);\n}\n\nMA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer)\n{\n    ma_result result;\n    ma_audio_buffer* pAudioBuffer;\n    ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */\n    ma_uint64 allocationSizeInBytes;\n\n    if (ppAudioBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *ppAudioBuffer = NULL;  /* Safety. */\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    innerConfig = *pConfig;\n    ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks);\n\n    allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels));\n    if (allocationSizeInBytes > MA_SIZE_MAX) {\n        return MA_OUT_OF_MEMORY;    /* Too big. */\n    }\n\n    pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks);  /* Safe cast to size_t. */\n    if (pAudioBuffer == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    if (pConfig->pData != NULL) {\n        ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);\n    } else {\n        ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels);\n    }\n\n    innerConfig.pData = &pAudioBuffer->_pExtraData[0];\n\n    result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer);\n    if (result != MA_SUCCESS) {\n        ma_free(pAudioBuffer, &innerConfig.allocationCallbacks);\n        return result;\n    }\n\n    *ppAudioBuffer = pAudioBuffer;\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer)\n{\n    ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE);\n}\n\nMA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer)\n{\n    ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE);\n}\n\nMA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)\n{\n    if (pAudioBuffer == NULL) {\n        return 0;\n    }\n\n    return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop);\n}\n\nMA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex)\n{\n    if (pAudioBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex);\n}\n\nMA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount)\n{\n    if (ppFramesOut != NULL) {\n        *ppFramesOut = NULL;    /* Safety. */\n    }\n\n    if (pAudioBuffer == NULL) {\n        if (pFrameCount != NULL) {\n            *pFrameCount = 0;\n        }\n\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount);\n}\n\nMA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount)\n{\n    if (pAudioBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount);\n}\n\nMA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer)\n{\n    if (pAudioBuffer == NULL) {\n        return MA_FALSE;\n    }\n\n    return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref);\n}\n\nMA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor)\n{\n    if (pAudioBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor);\n}\n\nMA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength)\n{\n    if (pAudioBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength);\n}\n\nMA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames)\n{\n    if (pAvailableFrames == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pAvailableFrames = 0;\n\n    if (pAudioBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames);\n}\n\n\n\n\n\nMA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData)\n{\n    if (pData == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pData);\n\n    pData->format   = format;\n    pData->channels = channels;\n    pData->pTail    = &pData->head;\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_paged_audio_buffer_page* pPage;\n\n    if (pData == NULL) {\n        return;\n    }\n\n    /* All pages need to be freed. */\n    pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext);\n    while (pPage != NULL) {\n        ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext);\n\n        ma_free(pPage, pAllocationCallbacks);\n        pPage = pNext;\n    }\n}\n\nMA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData)\n{\n    if (pData == NULL) {\n        return NULL;\n    }\n\n    return &pData->head;\n}\n\nMA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData)\n{\n    if (pData == NULL) {\n        return NULL;\n    }\n\n    return pData->pTail;\n}\n\nMA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength)\n{\n    ma_paged_audio_buffer_page* pPage;\n\n    if (pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pLength = 0;\n\n    if (pData == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Calculate the length from the linked list. */\n    for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) {\n        *pLength += pPage->sizeInFrames;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage)\n{\n    ma_paged_audio_buffer_page* pPage;\n    ma_uint64 allocationSize;\n\n    if (ppPage == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *ppPage = NULL;\n\n    if (pData == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels));\n    if (allocationSize > MA_SIZE_MAX) {\n        return MA_OUT_OF_MEMORY;    /* Too big. */\n    }\n\n    pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks);   /* Safe cast to size_t. */\n    if (pPage == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    pPage->pNext = NULL;\n    pPage->sizeInFrames = pageSizeInFrames;\n\n    if (pInitialData != NULL) {\n        ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels);\n    }\n\n    *ppPage = pPage;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pData == NULL || pPage == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* It's assumed the page is not attached to the list. */\n    ma_free(pPage, pAllocationCallbacks);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage)\n{\n    if (pData == NULL || pPage == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */\n\n    /* First thing to do is update the tail. */\n    for (;;) {\n        ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail);\n        ma_paged_audio_buffer_page* pNewTail = pPage;\n\n        if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) {\n            /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */\n            ma_atomic_exchange_ptr(&pOldTail->pNext, pPage);\n            break;  /* Done. */\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_result result;\n    ma_paged_audio_buffer_page* pPage;\n\n    result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return ma_paged_audio_buffer_data_append_page(pData, pPage);    /* <-- Should never fail. */\n}\n\n\nMA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData)\n{\n    ma_paged_audio_buffer_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.pData = pData;\n\n    return config;\n}\n\n\nstatic ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);\n}\n\nstatic ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex);\n}\n\nstatic ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource;\n\n    *pFormat     = pPagedAudioBuffer->pData->format;\n    *pChannels   = pPagedAudioBuffer->pData->channels;\n    *pSampleRate = 0;   /* There is no notion of a sample rate with audio buffers. */\n    ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)\n{\n    return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor);\n}\n\nstatic ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)\n{\n    return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength);\n}\n\nstatic ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable =\n{\n    ma_paged_audio_buffer__data_source_on_read,\n    ma_paged_audio_buffer__data_source_on_seek,\n    ma_paged_audio_buffer__data_source_on_get_data_format,\n    ma_paged_audio_buffer__data_source_on_get_cursor,\n    ma_paged_audio_buffer__data_source_on_get_length,\n    NULL,   /* onSetLooping */\n    0\n};\n\nMA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer)\n{\n    ma_result result;\n    ma_data_source_config dataSourceConfig;\n\n    if (pPagedAudioBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pPagedAudioBuffer);\n\n    /* A config is required for the format and channel count. */\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->pData == NULL) {\n        return MA_INVALID_ARGS; /* No underlying data specified. */\n    }\n\n    dataSourceConfig = ma_data_source_config_init();\n    dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable;\n\n    result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pPagedAudioBuffer->pData          = pConfig->pData;\n    pPagedAudioBuffer->pCurrent       = ma_paged_audio_buffer_data_get_head(pConfig->pData);\n    pPagedAudioBuffer->relativeCursor = 0;\n    pPagedAudioBuffer->absoluteCursor = 0;\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer)\n{\n    if (pPagedAudioBuffer == NULL) {\n        return;\n    }\n\n    /* Nothing to do. The data needs to be deleted separately. */\n}\n\nMA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint64 totalFramesRead = 0;\n    ma_format format;\n    ma_uint32 channels;\n\n    if (pPagedAudioBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    format   = pPagedAudioBuffer->pData->format;\n    channels = pPagedAudioBuffer->pData->channels;\n\n    while (totalFramesRead < frameCount) {\n        /* Read from the current page. The buffer should never be in a state where this is NULL. */\n        ma_uint64 framesRemainingInCurrentPage;\n        ma_uint64 framesRemainingToRead = frameCount - totalFramesRead;\n        ma_uint64 framesToReadThisIteration;\n\n        MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL);\n\n        framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor;\n\n        framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead);\n        ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels);\n        totalFramesRead += framesToReadThisIteration;\n\n        pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration;\n        pPagedAudioBuffer->relativeCursor += framesToReadThisIteration;\n\n        /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */\n        MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames);\n\n        if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) {\n            /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */\n            ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext);\n            if (pNext == NULL) {\n                result = MA_AT_END;\n                break;  /* We've reached the end. */\n            } else {\n                pPagedAudioBuffer->pCurrent       = pNext;\n                pPagedAudioBuffer->relativeCursor = 0;\n            }\n        }\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = totalFramesRead;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex)\n{\n    if (pPagedAudioBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (frameIndex == pPagedAudioBuffer->absoluteCursor) {\n        return MA_SUCCESS;  /* Nothing to do. */\n    }\n\n    if (frameIndex < pPagedAudioBuffer->absoluteCursor) {\n        /* Moving backwards. Need to move the cursor back to the start, and then move forward. */\n        pPagedAudioBuffer->pCurrent       = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData);\n        pPagedAudioBuffer->absoluteCursor = 0;\n        pPagedAudioBuffer->relativeCursor = 0;\n\n        /* Fall through to the forward seeking section below. */\n    }\n\n    if (frameIndex > pPagedAudioBuffer->absoluteCursor) {\n        /* Moving forward. */\n        ma_paged_audio_buffer_page* pPage;\n        ma_uint64 runningCursor = 0;\n\n        for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) {\n            ma_uint64 pageRangeBeg = runningCursor;\n            ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames;\n\n            if (frameIndex >= pageRangeBeg) {\n                if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) {  /* A small edge case - allow seeking to the very end of the buffer. */\n                    /* We found the page. */\n                    pPagedAudioBuffer->pCurrent       = pPage;\n                    pPagedAudioBuffer->absoluteCursor = frameIndex;\n                    pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg;\n                    return MA_SUCCESS;\n                }\n            }\n\n            runningCursor = pageRangeEnd;\n        }\n\n        /* Getting here means we tried seeking too far forward. Don't change any state. */\n        return MA_BAD_SEEK;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor)\n{\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;   /* Safety. */\n\n    if (pPagedAudioBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = pPagedAudioBuffer->absoluteCursor;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength)\n{\n    return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength);\n}\n\n\n\n/**************************************************************************************************************************************************************\n\nVFS\n\n**************************************************************************************************************************************************************/\nMA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)\n{\n    ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;\n\n    if (pFile == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pFile = NULL;\n\n    if (pVFS == NULL || pFilePath == NULL || openMode == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pCallbacks->onOpen == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile);\n}\n\nMA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)\n{\n    ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;\n\n    if (pFile == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pFile = NULL;\n\n    if (pVFS == NULL || pFilePath == NULL || openMode == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pCallbacks->onOpenW == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile);\n}\n\nMA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file)\n{\n    ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;\n\n    if (pVFS == NULL || file == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pCallbacks->onClose == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pCallbacks->onClose(pVFS, file);\n}\n\nMA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)\n{\n    ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;\n    ma_result result;\n    size_t bytesRead = 0;\n\n    if (pBytesRead != NULL) {\n        *pBytesRead = 0;\n    }\n\n    if (pVFS == NULL || file == NULL || pDst == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pCallbacks->onRead == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead);\n\n    if (pBytesRead != NULL) {\n        *pBytesRead = bytesRead;\n    }\n\n    if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) {\n        result  = MA_AT_END;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)\n{\n    ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;\n\n    if (pBytesWritten != NULL) {\n        *pBytesWritten = 0;\n    }\n\n    if (pVFS == NULL || file == NULL || pSrc == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pCallbacks->onWrite == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten);\n}\n\nMA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)\n{\n    ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;\n\n    if (pVFS == NULL || file == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pCallbacks->onSeek == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pCallbacks->onSeek(pVFS, file, offset, origin);\n}\n\nMA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)\n{\n    ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;\n\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;\n\n    if (pVFS == NULL || file == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pCallbacks->onTell == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pCallbacks->onTell(pVFS, file, pCursor);\n}\n\nMA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)\n{\n    ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;\n\n    if (pInfo == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pInfo);\n\n    if (pVFS == NULL || file == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pCallbacks->onInfo == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pCallbacks->onInfo(pVFS, file, pInfo);\n}\n\n\n#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX))\n    #define MA_USE_WIN32_FILEIO\n#endif\n\n#if defined(MA_USE_WIN32_FILEIO)\n/*\nWe need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do\nnot have the Ex version. We therefore need to do some dynamic branching depending on what's available.\n\nWe load these when we load our first file from the default VFS. It's left open for the life of the\nprogram and is left to the OS to uninitialize when the program terminates.\n*/\ntypedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod);\ntypedef BOOL  (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod);\n\nstatic ma_handle hKernel32DLL = NULL;\nstatic ma_SetFilePointer_proc   ma_SetFilePointer   = NULL;\nstatic ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL;\n\nstatic void ma_win32_fileio_init(void)\n{\n    if (hKernel32DLL == NULL) {\n        hKernel32DLL = ma_dlopen(NULL, \"kernel32.dll\");\n        if (hKernel32DLL != NULL) {\n            ma_SetFilePointer   = (ma_SetFilePointer_proc)  ma_dlsym(NULL, hKernel32DLL, \"SetFilePointer\");\n            ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, \"SetFilePointerEx\");\n        }\n    }\n}\n\nstatic void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition)\n{\n    *pDesiredAccess = 0;\n    if ((openMode & MA_OPEN_MODE_READ) != 0) {\n        *pDesiredAccess |= GENERIC_READ;\n    }\n    if ((openMode & MA_OPEN_MODE_WRITE) != 0) {\n        *pDesiredAccess |= GENERIC_WRITE;\n    }\n\n    *pShareMode = 0;\n    if ((openMode & MA_OPEN_MODE_READ) != 0) {\n        *pShareMode |= FILE_SHARE_READ;\n    }\n\n    if ((openMode & MA_OPEN_MODE_WRITE) != 0) {\n        *pCreationDisposition = CREATE_ALWAYS;  /* Opening in write mode. Truncate. */\n    } else {\n        *pCreationDisposition = OPEN_EXISTING;  /* Opening in read mode. File must exist. */\n    }\n}\n\nstatic ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)\n{\n    HANDLE hFile;\n    DWORD dwDesiredAccess;\n    DWORD dwShareMode;\n    DWORD dwCreationDisposition;\n\n    (void)pVFS;\n\n    /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */\n    ma_win32_fileio_init();\n\n    ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);\n\n    hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);\n    if (hFile == INVALID_HANDLE_VALUE) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    *pFile = hFile;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)\n{\n    HANDLE hFile;\n    DWORD dwDesiredAccess;\n    DWORD dwShareMode;\n    DWORD dwCreationDisposition;\n\n    (void)pVFS;\n\n    /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */\n    ma_win32_fileio_init();\n\n    ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);\n\n    hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);\n    if (hFile == INVALID_HANDLE_VALUE) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    *pFile = hFile;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file)\n{\n    (void)pVFS;\n\n    if (CloseHandle((HANDLE)file) == 0) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)\n{\n    ma_result result = MA_SUCCESS;\n    size_t totalBytesRead;\n\n    (void)pVFS;\n\n    totalBytesRead = 0;\n    while (totalBytesRead < sizeInBytes) {\n        size_t bytesRemaining;\n        DWORD bytesToRead;\n        DWORD bytesRead;\n        BOOL readResult;\n\n        bytesRemaining = sizeInBytes - totalBytesRead;\n        if (bytesRemaining >= 0xFFFFFFFF) {\n            bytesToRead = 0xFFFFFFFF;\n        } else {\n            bytesToRead = (DWORD)bytesRemaining;\n        }\n\n        readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL);\n        if (readResult == 1 && bytesRead == 0) {\n            result = MA_AT_END;\n            break;  /* EOF */\n        }\n\n        totalBytesRead += bytesRead;\n\n        if (bytesRead < bytesToRead) {\n            break;  /* EOF */\n        }\n\n        if (readResult == 0) {\n            result = ma_result_from_GetLastError(GetLastError());\n            break;\n        }\n    }\n\n    if (pBytesRead != NULL) {\n        *pBytesRead = totalBytesRead;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)\n{\n    ma_result result = MA_SUCCESS;\n    size_t totalBytesWritten;\n\n    (void)pVFS;\n\n    totalBytesWritten = 0;\n    while (totalBytesWritten < sizeInBytes) {\n        size_t bytesRemaining;\n        DWORD bytesToWrite;\n        DWORD bytesWritten;\n        BOOL writeResult;\n\n        bytesRemaining = sizeInBytes - totalBytesWritten;\n        if (bytesRemaining >= 0xFFFFFFFF) {\n            bytesToWrite = 0xFFFFFFFF;\n        } else {\n            bytesToWrite = (DWORD)bytesRemaining;\n        }\n\n        writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL);\n        totalBytesWritten += bytesWritten;\n\n        if (writeResult == 0) {\n            result = ma_result_from_GetLastError(GetLastError());\n            break;\n        }\n    }\n\n    if (pBytesWritten != NULL) {\n        *pBytesWritten = totalBytesWritten;\n    }\n\n    return result;\n}\n\n\nstatic ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)\n{\n    LARGE_INTEGER liDistanceToMove;\n    DWORD dwMoveMethod;\n    BOOL result;\n\n    (void)pVFS;\n\n    liDistanceToMove.QuadPart = offset;\n\n    /*  */ if (origin == ma_seek_origin_current) {\n        dwMoveMethod = FILE_CURRENT;\n    } else if (origin == ma_seek_origin_end) {\n        dwMoveMethod = FILE_END;\n    } else {\n        dwMoveMethod = FILE_BEGIN;\n    }\n\n    if (ma_SetFilePointerEx != NULL) {\n        result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod);\n    } else if (ma_SetFilePointer != NULL) {\n        /* No SetFilePointerEx() so restrict to 31 bits. */\n        if (origin > 0x7FFFFFFF) {\n            return MA_OUT_OF_RANGE;\n        }\n\n        result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod);\n    } else {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    if (result == 0) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)\n{\n    LARGE_INTEGER liZero;\n    LARGE_INTEGER liTell;\n    BOOL result;\n\n    (void)pVFS;\n\n    liZero.QuadPart = 0;\n\n    if (ma_SetFilePointerEx != NULL) {\n        result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT);\n    } else if (ma_SetFilePointer != NULL) {\n        LONG tell;\n\n        result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT);\n        liTell.QuadPart = tell;\n    } else {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    if (result == 0) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    if (pCursor != NULL) {\n        *pCursor = liTell.QuadPart;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)\n{\n    BY_HANDLE_FILE_INFORMATION fi;\n    BOOL result;\n\n    (void)pVFS;\n\n    result = GetFileInformationByHandle((HANDLE)file, &fi);\n    if (result == 0) {\n        return ma_result_from_GetLastError(GetLastError());\n    }\n\n    pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow);\n\n    return MA_SUCCESS;\n}\n#else\nstatic ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)\n{\n    ma_result result;\n    FILE* pFileStd;\n    const char* pOpenModeStr;\n\n    MA_ASSERT(pFilePath != NULL);\n    MA_ASSERT(openMode  != 0);\n    MA_ASSERT(pFile     != NULL);\n\n    (void)pVFS;\n\n    if ((openMode & MA_OPEN_MODE_READ) != 0) {\n        if ((openMode & MA_OPEN_MODE_WRITE) != 0) {\n            pOpenModeStr = \"r+\";\n        } else {\n            pOpenModeStr = \"rb\";\n        }\n    } else {\n        pOpenModeStr = \"wb\";\n    }\n\n    result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pFile = pFileStd;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)\n{\n    ma_result result;\n    FILE* pFileStd;\n    const wchar_t* pOpenModeStr;\n\n    MA_ASSERT(pFilePath != NULL);\n    MA_ASSERT(openMode  != 0);\n    MA_ASSERT(pFile     != NULL);\n\n    (void)pVFS;\n\n    if ((openMode & MA_OPEN_MODE_READ) != 0) {\n        if ((openMode & MA_OPEN_MODE_WRITE) != 0) {\n            pOpenModeStr = L\"r+\";\n        } else {\n            pOpenModeStr = L\"rb\";\n        }\n    } else {\n        pOpenModeStr = L\"wb\";\n    }\n\n    result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pFile = pFileStd;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file)\n{\n    MA_ASSERT(file != NULL);\n\n    (void)pVFS;\n\n    fclose((FILE*)file);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)\n{\n    size_t result;\n\n    MA_ASSERT(file != NULL);\n    MA_ASSERT(pDst != NULL);\n\n    (void)pVFS;\n\n    result = fread(pDst, 1, sizeInBytes, (FILE*)file);\n\n    if (pBytesRead != NULL) {\n        *pBytesRead = result;\n    }\n\n    if (result != sizeInBytes) {\n        if (result == 0 && feof((FILE*)file)) {\n            return MA_AT_END;\n        } else {\n            return ma_result_from_errno(ferror((FILE*)file));\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)\n{\n    size_t result;\n\n    MA_ASSERT(file != NULL);\n    MA_ASSERT(pSrc != NULL);\n\n    (void)pVFS;\n\n    result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file);\n\n    if (pBytesWritten != NULL) {\n        *pBytesWritten = result;\n    }\n\n    if (result != sizeInBytes) {\n        return ma_result_from_errno(ferror((FILE*)file));\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)\n{\n    int result;\n    int whence;\n\n    MA_ASSERT(file != NULL);\n\n    (void)pVFS;\n\n    if (origin == ma_seek_origin_start) {\n        whence = SEEK_SET;\n    } else if (origin == ma_seek_origin_end) {\n        whence = SEEK_END;\n    } else {\n        whence = SEEK_CUR;\n    }\n\n#if defined(_WIN32)\n    #if defined(_MSC_VER) && _MSC_VER > 1200\n        result = _fseeki64((FILE*)file, offset, whence);\n    #else\n        /* No _fseeki64() so restrict to 31 bits. */\n        if (origin > 0x7FFFFFFF) {\n            return MA_OUT_OF_RANGE;\n        }\n\n        result = fseek((FILE*)file, (int)offset, whence);\n    #endif\n#else\n    result = fseek((FILE*)file, (long int)offset, whence);\n#endif\n    if (result != 0) {\n        return MA_ERROR;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)\n{\n    ma_int64 result;\n\n    MA_ASSERT(file    != NULL);\n    MA_ASSERT(pCursor != NULL);\n\n    (void)pVFS;\n\n#if defined(_WIN32)\n    #if defined(_MSC_VER) && _MSC_VER > 1200\n        result = _ftelli64((FILE*)file);\n    #else\n        result = ftell((FILE*)file);\n    #endif\n#else\n    result = ftell((FILE*)file);\n#endif\n\n    *pCursor = result;\n\n    return MA_SUCCESS;\n}\n\n#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD)\nint fileno(FILE *stream);\n#endif\n\nstatic ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)\n{\n    int fd;\n    struct stat info;\n\n    MA_ASSERT(file  != NULL);\n    MA_ASSERT(pInfo != NULL);\n\n    (void)pVFS;\n\n#if defined(_MSC_VER)\n    fd = _fileno((FILE*)file);\n#else\n    fd =  fileno((FILE*)file);\n#endif\n\n    if (fstat(fd, &info) != 0) {\n        return ma_result_from_errno(errno);\n    }\n\n    pInfo->sizeInBytes = info.st_size;\n\n    return MA_SUCCESS;\n}\n#endif\n\n\nstatic ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)\n{\n    if (pFile == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pFile = NULL;\n\n    if (pFilePath == NULL || openMode == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_USE_WIN32_FILEIO)\n    return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile);\n#else\n    return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile);\n#endif\n}\n\nstatic ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)\n{\n    if (pFile == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pFile = NULL;\n\n    if (pFilePath == NULL || openMode == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_USE_WIN32_FILEIO)\n    return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile);\n#else\n    return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile);\n#endif\n}\n\nstatic ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file)\n{\n    if (file == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_USE_WIN32_FILEIO)\n    return ma_default_vfs_close__win32(pVFS, file);\n#else\n    return ma_default_vfs_close__stdio(pVFS, file);\n#endif\n}\n\nstatic ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)\n{\n    if (pBytesRead != NULL) {\n        *pBytesRead = 0;\n    }\n\n    if (file == NULL || pDst == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_USE_WIN32_FILEIO)\n    return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead);\n#else\n    return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead);\n#endif\n}\n\nstatic ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)\n{\n    if (pBytesWritten != NULL) {\n        *pBytesWritten = 0;\n    }\n\n    if (file == NULL || pSrc == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_USE_WIN32_FILEIO)\n    return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten);\n#else\n    return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten);\n#endif\n}\n\nstatic ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)\n{\n    if (file == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_USE_WIN32_FILEIO)\n    return ma_default_vfs_seek__win32(pVFS, file, offset, origin);\n#else\n    return ma_default_vfs_seek__stdio(pVFS, file, offset, origin);\n#endif\n}\n\nstatic ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)\n{\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;\n\n    if (file == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_USE_WIN32_FILEIO)\n    return ma_default_vfs_tell__win32(pVFS, file, pCursor);\n#else\n    return ma_default_vfs_tell__stdio(pVFS, file, pCursor);\n#endif\n}\n\nstatic ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)\n{\n    if (pInfo == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pInfo);\n\n    if (file == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n#if defined(MA_USE_WIN32_FILEIO)\n    return ma_default_vfs_info__win32(pVFS, file, pInfo);\n#else\n    return ma_default_vfs_info__stdio(pVFS, file, pInfo);\n#endif\n}\n\n\nMA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pVFS == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pVFS->cb.onOpen  = ma_default_vfs_open;\n    pVFS->cb.onOpenW = ma_default_vfs_open_w;\n    pVFS->cb.onClose = ma_default_vfs_close;\n    pVFS->cb.onRead  = ma_default_vfs_read;\n    pVFS->cb.onWrite = ma_default_vfs_write;\n    pVFS->cb.onSeek  = ma_default_vfs_seek;\n    pVFS->cb.onTell  = ma_default_vfs_tell;\n    pVFS->cb.onInfo  = ma_default_vfs_info;\n    ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks);\n\n    return MA_SUCCESS;\n}\n\n\nMA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)\n{\n    if (pVFS != NULL) {\n        return ma_vfs_open(pVFS, pFilePath, openMode, pFile);\n    } else {\n        return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile);\n    }\n}\n\nMA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)\n{\n    if (pVFS != NULL) {\n        return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile);\n    } else {\n        return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile);\n    }\n}\n\nMA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file)\n{\n    if (pVFS != NULL) {\n        return ma_vfs_close(pVFS, file);\n    } else {\n        return ma_default_vfs_close(pVFS, file);\n    }\n}\n\nMA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)\n{\n    if (pVFS != NULL) {\n        return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);\n    } else {\n        return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);\n    }\n}\n\nMA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)\n{\n    if (pVFS != NULL) {\n        return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);\n    } else {\n        return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);\n    }\n}\n\nMA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)\n{\n    if (pVFS != NULL) {\n        return ma_vfs_seek(pVFS, file, offset, origin);\n    } else {\n        return ma_default_vfs_seek(pVFS, file, offset, origin);\n    }\n}\n\nMA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)\n{\n    if (pVFS != NULL) {\n        return ma_vfs_tell(pVFS, file, pCursor);\n    } else {\n        return ma_default_vfs_tell(pVFS, file, pCursor);\n    }\n}\n\nMA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)\n{\n    if (pVFS != NULL) {\n        return ma_vfs_info(pVFS, file, pInfo);\n    } else {\n        return ma_default_vfs_info(pVFS, file, pInfo);\n    }\n}\n\n\n\nstatic ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_result result;\n    ma_vfs_file file;\n    ma_file_info info;\n    void* pData;\n    size_t bytesRead;\n\n    if (ppData != NULL) {\n        *ppData = NULL;\n    }\n    if (pSize != NULL) {\n        *pSize = 0;\n    }\n\n    if (ppData == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pFilePath != NULL) {\n        result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);\n    } else {\n        result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file);\n    }\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    result = ma_vfs_or_default_info(pVFS, file, &info);\n    if (result != MA_SUCCESS) {\n        ma_vfs_or_default_close(pVFS, file);\n        return result;\n    }\n\n    if (info.sizeInBytes > MA_SIZE_MAX) {\n        ma_vfs_or_default_close(pVFS, file);\n        return MA_TOO_BIG;\n    }\n\n    pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks);  /* Safe cast. */\n    if (pData == NULL) {\n        ma_vfs_or_default_close(pVFS, file);\n        return result;\n    }\n\n    result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead);  /* Safe cast. */\n    ma_vfs_or_default_close(pVFS, file);\n\n    if (result != MA_SUCCESS) {\n        ma_free(pData, pAllocationCallbacks);\n        return result;\n    }\n\n    if (pSize != NULL) {\n        *pSize = bytesRead;\n    }\n\n    MA_ASSERT(ppData != NULL);\n    *ppData = pData;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks);\n}\n\nMA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks);\n}\n\n\n\n/**************************************************************************************************************************************************************\n\nDecoding and Encoding Headers. These are auto-generated from a tool.\n\n**************************************************************************************************************************************************************/\n#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))\n/* dr_wav_h begin */\n#ifndef ma_dr_wav_h\n#define ma_dr_wav_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#define MA_DR_WAV_STRINGIFY(x)      #x\n#define MA_DR_WAV_XSTRINGIFY(x)     MA_DR_WAV_STRINGIFY(x)\n#define MA_DR_WAV_VERSION_MAJOR     0\n#define MA_DR_WAV_VERSION_MINOR     13\n#define MA_DR_WAV_VERSION_REVISION  13\n#define MA_DR_WAV_VERSION_STRING    MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) \".\" MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) \".\" MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION)\n#include <stddef.h>\n#define MA_DR_WAVE_FORMAT_PCM          0x1\n#define MA_DR_WAVE_FORMAT_ADPCM        0x2\n#define MA_DR_WAVE_FORMAT_IEEE_FLOAT   0x3\n#define MA_DR_WAVE_FORMAT_ALAW         0x6\n#define MA_DR_WAVE_FORMAT_MULAW        0x7\n#define MA_DR_WAVE_FORMAT_DVI_ADPCM    0x11\n#define MA_DR_WAVE_FORMAT_EXTENSIBLE   0xFFFE\n#define MA_DR_WAV_SEQUENTIAL            0x00000001\n#define MA_DR_WAV_WITH_METADATA         0x00000002\nMA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);\nMA_API const char* ma_dr_wav_version_string(void);\ntypedef enum\n{\n    ma_dr_wav_seek_origin_start,\n    ma_dr_wav_seek_origin_current\n} ma_dr_wav_seek_origin;\ntypedef enum\n{\n    ma_dr_wav_container_riff,\n    ma_dr_wav_container_rifx,\n    ma_dr_wav_container_w64,\n    ma_dr_wav_container_rf64,\n    ma_dr_wav_container_aiff\n} ma_dr_wav_container;\ntypedef struct\n{\n    union\n    {\n        ma_uint8 fourcc[4];\n        ma_uint8 guid[16];\n    } id;\n    ma_uint64 sizeInBytes;\n    unsigned int paddingSize;\n} ma_dr_wav_chunk_header;\ntypedef struct\n{\n    ma_uint16 formatTag;\n    ma_uint16 channels;\n    ma_uint32 sampleRate;\n    ma_uint32 avgBytesPerSec;\n    ma_uint16 blockAlign;\n    ma_uint16 bitsPerSample;\n    ma_uint16 extendedSize;\n    ma_uint16 validBitsPerSample;\n    ma_uint32 channelMask;\n    ma_uint8 subFormat[16];\n} ma_dr_wav_fmt;\nMA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT);\ntypedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);\ntypedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);\ntypedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin);\ntypedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT);\ntypedef struct\n{\n    const ma_uint8* data;\n    size_t dataSize;\n    size_t currentReadPos;\n} ma_dr_wav__memory_stream;\ntypedef struct\n{\n    void** ppData;\n    size_t* pDataSize;\n    size_t dataSize;\n    size_t dataCapacity;\n    size_t currentWritePos;\n} ma_dr_wav__memory_stream_write;\ntypedef struct\n{\n    ma_dr_wav_container container;\n    ma_uint32 format;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_uint32 bitsPerSample;\n} ma_dr_wav_data_format;\ntypedef enum\n{\n    ma_dr_wav_metadata_type_none                        = 0,\n    ma_dr_wav_metadata_type_unknown                     = 1 << 0,\n    ma_dr_wav_metadata_type_smpl                        = 1 << 1,\n    ma_dr_wav_metadata_type_inst                        = 1 << 2,\n    ma_dr_wav_metadata_type_cue                         = 1 << 3,\n    ma_dr_wav_metadata_type_acid                        = 1 << 4,\n    ma_dr_wav_metadata_type_bext                        = 1 << 5,\n    ma_dr_wav_metadata_type_list_label                  = 1 << 6,\n    ma_dr_wav_metadata_type_list_note                   = 1 << 7,\n    ma_dr_wav_metadata_type_list_labelled_cue_region    = 1 << 8,\n    ma_dr_wav_metadata_type_list_info_software          = 1 << 9,\n    ma_dr_wav_metadata_type_list_info_copyright         = 1 << 10,\n    ma_dr_wav_metadata_type_list_info_title             = 1 << 11,\n    ma_dr_wav_metadata_type_list_info_artist            = 1 << 12,\n    ma_dr_wav_metadata_type_list_info_comment           = 1 << 13,\n    ma_dr_wav_metadata_type_list_info_date              = 1 << 14,\n    ma_dr_wav_metadata_type_list_info_genre             = 1 << 15,\n    ma_dr_wav_metadata_type_list_info_album             = 1 << 16,\n    ma_dr_wav_metadata_type_list_info_tracknumber       = 1 << 17,\n    ma_dr_wav_metadata_type_list_all_info_strings       = ma_dr_wav_metadata_type_list_info_software\n                                                    | ma_dr_wav_metadata_type_list_info_copyright\n                                                    | ma_dr_wav_metadata_type_list_info_title\n                                                    | ma_dr_wav_metadata_type_list_info_artist\n                                                    | ma_dr_wav_metadata_type_list_info_comment\n                                                    | ma_dr_wav_metadata_type_list_info_date\n                                                    | ma_dr_wav_metadata_type_list_info_genre\n                                                    | ma_dr_wav_metadata_type_list_info_album\n                                                    | ma_dr_wav_metadata_type_list_info_tracknumber,\n    ma_dr_wav_metadata_type_list_all_adtl               = ma_dr_wav_metadata_type_list_label\n                                                    | ma_dr_wav_metadata_type_list_note\n                                                    | ma_dr_wav_metadata_type_list_labelled_cue_region,\n    ma_dr_wav_metadata_type_all                         = -2,\n    ma_dr_wav_metadata_type_all_including_unknown       = -1\n} ma_dr_wav_metadata_type;\ntypedef enum\n{\n    ma_dr_wav_smpl_loop_type_forward  = 0,\n    ma_dr_wav_smpl_loop_type_pingpong = 1,\n    ma_dr_wav_smpl_loop_type_backward = 2\n} ma_dr_wav_smpl_loop_type;\ntypedef struct\n{\n    ma_uint32 cuePointId;\n    ma_uint32 type;\n    ma_uint32 firstSampleByteOffset;\n    ma_uint32 lastSampleByteOffset;\n    ma_uint32 sampleFraction;\n    ma_uint32 playCount;\n} ma_dr_wav_smpl_loop;\ntypedef struct\n{\n    ma_uint32 manufacturerId;\n    ma_uint32 productId;\n    ma_uint32 samplePeriodNanoseconds;\n    ma_uint32 midiUnityNote;\n    ma_uint32 midiPitchFraction;\n    ma_uint32 smpteFormat;\n    ma_uint32 smpteOffset;\n    ma_uint32 sampleLoopCount;\n    ma_uint32 samplerSpecificDataSizeInBytes;\n    ma_dr_wav_smpl_loop* pLoops;\n    ma_uint8* pSamplerSpecificData;\n} ma_dr_wav_smpl;\ntypedef struct\n{\n    ma_int8 midiUnityNote;\n    ma_int8 fineTuneCents;\n    ma_int8 gainDecibels;\n    ma_int8 lowNote;\n    ma_int8 highNote;\n    ma_int8 lowVelocity;\n    ma_int8 highVelocity;\n} ma_dr_wav_inst;\ntypedef struct\n{\n    ma_uint32 id;\n    ma_uint32 playOrderPosition;\n    ma_uint8 dataChunkId[4];\n    ma_uint32 chunkStart;\n    ma_uint32 blockStart;\n    ma_uint32 sampleByteOffset;\n} ma_dr_wav_cue_point;\ntypedef struct\n{\n    ma_uint32 cuePointCount;\n    ma_dr_wav_cue_point *pCuePoints;\n} ma_dr_wav_cue;\ntypedef enum\n{\n    ma_dr_wav_acid_flag_one_shot      = 1,\n    ma_dr_wav_acid_flag_root_note_set = 2,\n    ma_dr_wav_acid_flag_stretch       = 4,\n    ma_dr_wav_acid_flag_disk_based    = 8,\n    ma_dr_wav_acid_flag_acidizer      = 16\n} ma_dr_wav_acid_flag;\ntypedef struct\n{\n    ma_uint32 flags;\n    ma_uint16 midiUnityNote;\n    ma_uint16 reserved1;\n    float reserved2;\n    ma_uint32 numBeats;\n    ma_uint16 meterDenominator;\n    ma_uint16 meterNumerator;\n    float tempo;\n} ma_dr_wav_acid;\ntypedef struct\n{\n    ma_uint32 cuePointId;\n    ma_uint32 stringLength;\n    char* pString;\n} ma_dr_wav_list_label_or_note;\ntypedef struct\n{\n    char* pDescription;\n    char* pOriginatorName;\n    char* pOriginatorReference;\n    char  pOriginationDate[10];\n    char  pOriginationTime[8];\n    ma_uint64 timeReference;\n    ma_uint16 version;\n    char* pCodingHistory;\n    ma_uint32 codingHistorySize;\n    ma_uint8* pUMID;\n    ma_uint16 loudnessValue;\n    ma_uint16 loudnessRange;\n    ma_uint16 maxTruePeakLevel;\n    ma_uint16 maxMomentaryLoudness;\n    ma_uint16 maxShortTermLoudness;\n} ma_dr_wav_bext;\ntypedef struct\n{\n    ma_uint32 stringLength;\n    char* pString;\n} ma_dr_wav_list_info_text;\ntypedef struct\n{\n    ma_uint32 cuePointId;\n    ma_uint32 sampleLength;\n    ma_uint8 purposeId[4];\n    ma_uint16 country;\n    ma_uint16 language;\n    ma_uint16 dialect;\n    ma_uint16 codePage;\n    ma_uint32 stringLength;\n    char* pString;\n} ma_dr_wav_list_labelled_cue_region;\ntypedef enum\n{\n    ma_dr_wav_metadata_location_invalid,\n    ma_dr_wav_metadata_location_top_level,\n    ma_dr_wav_metadata_location_inside_info_list,\n    ma_dr_wav_metadata_location_inside_adtl_list\n} ma_dr_wav_metadata_location;\ntypedef struct\n{\n    ma_uint8 id[4];\n    ma_dr_wav_metadata_location chunkLocation;\n    ma_uint32 dataSizeInBytes;\n    ma_uint8* pData;\n} ma_dr_wav_unknown_metadata;\ntypedef struct\n{\n    ma_dr_wav_metadata_type type;\n    union\n    {\n        ma_dr_wav_cue cue;\n        ma_dr_wav_smpl smpl;\n        ma_dr_wav_acid acid;\n        ma_dr_wav_inst inst;\n        ma_dr_wav_bext bext;\n        ma_dr_wav_list_label_or_note labelOrNote;\n        ma_dr_wav_list_labelled_cue_region labelledCueRegion;\n        ma_dr_wav_list_info_text infoText;\n        ma_dr_wav_unknown_metadata unknown;\n    } data;\n} ma_dr_wav_metadata;\ntypedef struct\n{\n    ma_dr_wav_read_proc onRead;\n    ma_dr_wav_write_proc onWrite;\n    ma_dr_wav_seek_proc onSeek;\n    void* pUserData;\n    ma_allocation_callbacks allocationCallbacks;\n    ma_dr_wav_container container;\n    ma_dr_wav_fmt fmt;\n    ma_uint32 sampleRate;\n    ma_uint16 channels;\n    ma_uint16 bitsPerSample;\n    ma_uint16 translatedFormatTag;\n    ma_uint64 totalPCMFrameCount;\n    ma_uint64 dataChunkDataSize;\n    ma_uint64 dataChunkDataPos;\n    ma_uint64 bytesRemaining;\n    ma_uint64 readCursorInPCMFrames;\n    ma_uint64 dataChunkDataSizeTargetWrite;\n    ma_bool32 isSequentialWrite;\n    ma_dr_wav_metadata* pMetadata;\n    ma_uint32 metadataCount;\n    ma_dr_wav__memory_stream memoryStream;\n    ma_dr_wav__memory_stream_write memoryStreamWrite;\n    struct\n    {\n        ma_uint32 bytesRemainingInBlock;\n        ma_uint16 predictor[2];\n        ma_int32  delta[2];\n        ma_int32  cachedFrames[4];\n        ma_uint32 cachedFrameCount;\n        ma_int32  prevFrames[2][2];\n    } msadpcm;\n    struct\n    {\n        ma_uint32 bytesRemainingInBlock;\n        ma_int32  predictor[2];\n        ma_int32  stepIndex[2];\n        ma_int32  cachedFrames[16];\n        ma_uint32 cachedFrameCount;\n    } ima;\n    struct\n    {\n        ma_bool8 isLE;\n        ma_bool8 isUnsigned;\n    } aiff;\n} ma_dr_wav;\nMA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount);\nMA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount);\nMA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav);\nMA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav);\nMA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut);\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);\nMA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex);\nMA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor);\nMA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength);\nMA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData);\nMA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);\nMA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);\nMA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);\n#ifndef MA_DR_WAV_NO_CONVERSION_API\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);\nMA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);\nMA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);\nMA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);\nMA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);\n#endif\n#ifndef MA_DR_WAV_NO_STDIO\nMA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\n#endif\nMA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\n#ifndef MA_DR_WAV_NO_CONVERSION_API\nMA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\n#ifndef MA_DR_WAV_NO_STDIO\nMA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\n#endif\nMA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);\n#endif\nMA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data);\nMA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data);\nMA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data);\nMA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data);\nMA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data);\nMA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data);\nMA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data);\nMA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]);\nMA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b);\n#ifdef __cplusplus\n}\n#endif\n#endif\n/* dr_wav_h end */\n#endif  /* MA_NO_WAV */\n\n#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)\n/* dr_flac_h begin */\n#ifndef ma_dr_flac_h\n#define ma_dr_flac_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#define MA_DR_FLAC_STRINGIFY(x)      #x\n#define MA_DR_FLAC_XSTRINGIFY(x)     MA_DR_FLAC_STRINGIFY(x)\n#define MA_DR_FLAC_VERSION_MAJOR     0\n#define MA_DR_FLAC_VERSION_MINOR     12\n#define MA_DR_FLAC_VERSION_REVISION  42\n#define MA_DR_FLAC_VERSION_STRING    MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) \".\" MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) \".\" MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION)\n#include <stddef.h>\n#if defined(_MSC_VER) && _MSC_VER >= 1700\n    #define MA_DR_FLAC_DEPRECATED       __declspec(deprecated)\n#elif (defined(__GNUC__) && __GNUC__ >= 4)\n    #define MA_DR_FLAC_DEPRECATED       __attribute__((deprecated))\n#elif defined(__has_feature)\n    #if __has_feature(attribute_deprecated)\n        #define MA_DR_FLAC_DEPRECATED   __attribute__((deprecated))\n    #else\n        #define MA_DR_FLAC_DEPRECATED\n    #endif\n#else\n    #define MA_DR_FLAC_DEPRECATED\n#endif\nMA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);\nMA_API const char* ma_dr_flac_version_string(void);\n#ifndef MA_DR_FLAC_BUFFER_SIZE\n#define MA_DR_FLAC_BUFFER_SIZE   4096\n#endif\n#ifdef MA_64BIT\ntypedef ma_uint64 ma_dr_flac_cache_t;\n#else\ntypedef ma_uint32 ma_dr_flac_cache_t;\n#endif\n#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO       0\n#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING          1\n#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION      2\n#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE        3\n#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT   4\n#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET         5\n#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE          6\n#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID          127\n#define MA_DR_FLAC_PICTURE_TYPE_OTHER                   0\n#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON               1\n#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON         2\n#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT             3\n#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK              4\n#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE            5\n#define MA_DR_FLAC_PICTURE_TYPE_MEDIA                   6\n#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST             7\n#define MA_DR_FLAC_PICTURE_TYPE_ARTIST                  8\n#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR               9\n#define MA_DR_FLAC_PICTURE_TYPE_BAND                    10\n#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER                11\n#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST                12\n#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION      13\n#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING        14\n#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE      15\n#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE          16\n#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH     17\n#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION            18\n#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE           19\n#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE      20\ntypedef enum\n{\n    ma_dr_flac_container_native,\n    ma_dr_flac_container_ogg,\n    ma_dr_flac_container_unknown\n} ma_dr_flac_container;\ntypedef enum\n{\n    ma_dr_flac_seek_origin_start,\n    ma_dr_flac_seek_origin_current\n} ma_dr_flac_seek_origin;\ntypedef struct\n{\n    ma_uint64 firstPCMFrame;\n    ma_uint64 flacFrameOffset;\n    ma_uint16 pcmFrameCount;\n} ma_dr_flac_seekpoint;\ntypedef struct\n{\n    ma_uint16 minBlockSizeInPCMFrames;\n    ma_uint16 maxBlockSizeInPCMFrames;\n    ma_uint32 minFrameSizeInPCMFrames;\n    ma_uint32 maxFrameSizeInPCMFrames;\n    ma_uint32 sampleRate;\n    ma_uint8  channels;\n    ma_uint8  bitsPerSample;\n    ma_uint64 totalPCMFrameCount;\n    ma_uint8  md5[16];\n} ma_dr_flac_streaminfo;\ntypedef struct\n{\n    ma_uint32 type;\n    const void* pRawData;\n    ma_uint32 rawDataSize;\n    union\n    {\n        ma_dr_flac_streaminfo streaminfo;\n        struct\n        {\n            int unused;\n        } padding;\n        struct\n        {\n            ma_uint32 id;\n            const void* pData;\n            ma_uint32 dataSize;\n        } application;\n        struct\n        {\n            ma_uint32 seekpointCount;\n            const ma_dr_flac_seekpoint* pSeekpoints;\n        } seektable;\n        struct\n        {\n            ma_uint32 vendorLength;\n            const char* vendor;\n            ma_uint32 commentCount;\n            const void* pComments;\n        } vorbis_comment;\n        struct\n        {\n            char catalog[128];\n            ma_uint64 leadInSampleCount;\n            ma_bool32 isCD;\n            ma_uint8 trackCount;\n            const void* pTrackData;\n        } cuesheet;\n        struct\n        {\n            ma_uint32 type;\n            ma_uint32 mimeLength;\n            const char* mime;\n            ma_uint32 descriptionLength;\n            const char* description;\n            ma_uint32 width;\n            ma_uint32 height;\n            ma_uint32 colorDepth;\n            ma_uint32 indexColorCount;\n            ma_uint32 pictureDataSize;\n            const ma_uint8* pPictureData;\n        } picture;\n    } data;\n} ma_dr_flac_metadata;\ntypedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);\ntypedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin);\ntypedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata);\ntypedef struct\n{\n    const ma_uint8* data;\n    size_t dataSize;\n    size_t currentReadPos;\n} ma_dr_flac__memory_stream;\ntypedef struct\n{\n    ma_dr_flac_read_proc onRead;\n    ma_dr_flac_seek_proc onSeek;\n    void* pUserData;\n    size_t unalignedByteCount;\n    ma_dr_flac_cache_t unalignedCache;\n    ma_uint32 nextL2Line;\n    ma_uint32 consumedBits;\n    ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)];\n    ma_dr_flac_cache_t cache;\n    ma_uint16 crc16;\n    ma_dr_flac_cache_t crc16Cache;\n    ma_uint32 crc16CacheIgnoredBytes;\n} ma_dr_flac_bs;\ntypedef struct\n{\n    ma_uint8 subframeType;\n    ma_uint8 wastedBitsPerSample;\n    ma_uint8 lpcOrder;\n    ma_int32* pSamplesS32;\n} ma_dr_flac_subframe;\ntypedef struct\n{\n    ma_uint64 pcmFrameNumber;\n    ma_uint32 flacFrameNumber;\n    ma_uint32 sampleRate;\n    ma_uint16 blockSizeInPCMFrames;\n    ma_uint8 channelAssignment;\n    ma_uint8 bitsPerSample;\n    ma_uint8 crc8;\n} ma_dr_flac_frame_header;\ntypedef struct\n{\n    ma_dr_flac_frame_header header;\n    ma_uint32 pcmFramesRemaining;\n    ma_dr_flac_subframe subframes[8];\n} ma_dr_flac_frame;\ntypedef struct\n{\n    ma_dr_flac_meta_proc onMeta;\n    void* pUserDataMD;\n    ma_allocation_callbacks allocationCallbacks;\n    ma_uint32 sampleRate;\n    ma_uint8 channels;\n    ma_uint8 bitsPerSample;\n    ma_uint16 maxBlockSizeInPCMFrames;\n    ma_uint64 totalPCMFrameCount;\n    ma_dr_flac_container container;\n    ma_uint32 seekpointCount;\n    ma_dr_flac_frame currentFLACFrame;\n    ma_uint64 currentPCMFrame;\n    ma_uint64 firstFLACFramePosInBytes;\n    ma_dr_flac__memory_stream memoryStream;\n    ma_int32* pDecodedSamples;\n    ma_dr_flac_seekpoint* pSeekpoints;\n    void* _oggbs;\n    ma_bool32 _noSeekTableSeek    : 1;\n    ma_bool32 _noBinarySearchSeek : 1;\n    ma_bool32 _noBruteForceSeek   : 1;\n    ma_dr_flac_bs bs;\n    ma_uint8 pExtraData[1];\n} ma_dr_flac;\nMA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API void ma_dr_flac_close(ma_dr_flac* pFlac);\nMA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut);\nMA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut);\nMA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut);\nMA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex);\n#ifndef MA_DR_FLAC_NO_STDIO\nMA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\n#endif\nMA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\n#ifndef MA_DR_FLAC_NO_STDIO\nMA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\n#endif\nMA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);\ntypedef struct\n{\n    ma_uint32 countRemaining;\n    const char* pRunningData;\n} ma_dr_flac_vorbis_comment_iterator;\nMA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments);\nMA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut);\ntypedef struct\n{\n    ma_uint32 countRemaining;\n    const char* pRunningData;\n} ma_dr_flac_cuesheet_track_iterator;\ntypedef struct\n{\n    ma_uint64 offset;\n    ma_uint8 index;\n    ma_uint8 reserved[3];\n} ma_dr_flac_cuesheet_track_index;\ntypedef struct\n{\n    ma_uint64 offset;\n    ma_uint8 trackNumber;\n    char ISRC[12];\n    ma_bool8 isAudio;\n    ma_bool8 preEmphasis;\n    ma_uint8 indexCount;\n    const ma_dr_flac_cuesheet_track_index* pIndexPoints;\n} ma_dr_flac_cuesheet_track;\nMA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData);\nMA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack);\n#ifdef __cplusplus\n}\n#endif\n#endif\n/* dr_flac_h end */\n#endif  /* MA_NO_FLAC */\n\n#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)\n/* dr_mp3_h begin */\n#ifndef ma_dr_mp3_h\n#define ma_dr_mp3_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#define MA_DR_MP3_STRINGIFY(x)      #x\n#define MA_DR_MP3_XSTRINGIFY(x)     MA_DR_MP3_STRINGIFY(x)\n#define MA_DR_MP3_VERSION_MAJOR     0\n#define MA_DR_MP3_VERSION_MINOR     6\n#define MA_DR_MP3_VERSION_REVISION  38\n#define MA_DR_MP3_VERSION_STRING    MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) \".\" MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) \".\" MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION)\n#include <stddef.h>\n#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME  1152\n#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME         (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)\nMA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);\nMA_API const char* ma_dr_mp3_version_string(void);\ntypedef struct\n{\n    int frame_bytes, channels, hz, layer, bitrate_kbps;\n} ma_dr_mp3dec_frame_info;\ntypedef struct\n{\n    float mdct_overlap[2][9*32], qmf_state[15*2*32];\n    int reserv, free_format_bytes;\n    ma_uint8 header[4], reserv_buf[511];\n} ma_dr_mp3dec;\nMA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec);\nMA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info);\nMA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples);\ntypedef enum\n{\n    ma_dr_mp3_seek_origin_start,\n    ma_dr_mp3_seek_origin_current\n} ma_dr_mp3_seek_origin;\ntypedef struct\n{\n    ma_uint64 seekPosInBytes;\n    ma_uint64 pcmFrameIndex;\n    ma_uint16 mp3FramesToDiscard;\n    ma_uint16 pcmFramesToDiscard;\n} ma_dr_mp3_seek_point;\ntypedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);\ntypedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin);\ntypedef struct\n{\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n} ma_dr_mp3_config;\ntypedef struct\n{\n    ma_dr_mp3dec decoder;\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_dr_mp3_read_proc onRead;\n    ma_dr_mp3_seek_proc onSeek;\n    void* pUserData;\n    ma_allocation_callbacks allocationCallbacks;\n    ma_uint32 mp3FrameChannels;\n    ma_uint32 mp3FrameSampleRate;\n    ma_uint32 pcmFramesConsumedInMP3Frame;\n    ma_uint32 pcmFramesRemainingInMP3Frame;\n    ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME];\n    ma_uint64 currentPCMFrame;\n    ma_uint64 streamCursor;\n    ma_dr_mp3_seek_point* pSeekPoints;\n    ma_uint32 seekPointCount;\n    size_t dataSize;\n    size_t dataCapacity;\n    size_t dataConsumed;\n    ma_uint8* pData;\n    ma_bool32 atEnd : 1;\n    struct\n    {\n        const ma_uint8* pData;\n        size_t dataSize;\n        size_t currentReadPos;\n    } memory;\n} ma_dr_mp3;\nMA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);\n#ifndef MA_DR_MP3_NO_STDIO\nMA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks);\n#endif\nMA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3);\nMA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut);\nMA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut);\nMA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex);\nMA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3);\nMA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3);\nMA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount);\nMA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints);\nMA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints);\nMA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\n#ifndef MA_DR_MP3_NO_STDIO\nMA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);\n#endif\nMA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);\n#ifdef __cplusplus\n}\n#endif\n#endif\n/* dr_mp3_h end */\n#endif  /* MA_NO_MP3 */\n\n\n/**************************************************************************************************************************************************************\n\nDecoding\n\n**************************************************************************************************************************************************************/\n#ifndef MA_NO_DECODING\n\nstatic ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)\n{\n    MA_ASSERT(pDecoder != NULL);\n\n    return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead);\n}\n\nstatic ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)\n{\n    MA_ASSERT(pDecoder != NULL);\n\n    return pDecoder->onSeek(pDecoder, byteOffset, origin);\n}\n\nstatic ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor)\n{\n    MA_ASSERT(pDecoder != NULL);\n\n    if (pDecoder->onTell == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    return pDecoder->onTell(pDecoder, pCursor);\n}\n\n\nMA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount)\n{\n    ma_decoding_backend_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.preferredFormat = preferredFormat;\n    config.seekPointCount  = seekPointCount;\n\n    return config;\n}\n\n\nMA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)\n{\n    ma_decoder_config config;\n    MA_ZERO_OBJECT(&config);\n    config.format         = outputFormat;\n    config.channels       = outputChannels;\n    config.sampleRate     = outputSampleRate;\n    config.resampling     = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */\n    config.encodingFormat = ma_encoding_format_unknown;\n\n    /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */\n\n    return config;\n}\n\nMA_API ma_decoder_config ma_decoder_config_init_default()\n{\n    return ma_decoder_config_init(ma_format_unknown, 0, 0);\n}\n\nMA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)\n{\n    ma_decoder_config config;\n    if (pConfig != NULL) {\n        config = *pConfig;\n    } else {\n        MA_ZERO_OBJECT(&config);\n    }\n\n    return config;\n}\n\nstatic ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)\n{\n    ma_result result;\n    ma_data_converter_config converterConfig;\n    ma_format internalFormat;\n    ma_uint32 internalChannels;\n    ma_uint32 internalSampleRate;\n    ma_channel internalChannelMap[MA_MAX_CHANNELS];\n\n    MA_ASSERT(pDecoder != NULL);\n    MA_ASSERT(pConfig  != NULL);\n\n    result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap));\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to retrieve the internal data format. */\n    }\n\n\n    /* Make sure we're not asking for too many channels. */\n    if (pConfig->channels > MA_MAX_CHANNELS) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */\n    if (internalChannels > MA_MAX_CHANNELS) {\n        return MA_INVALID_ARGS;\n    }\n\n\n    /* Output format. */\n    if (pConfig->format == ma_format_unknown) {\n        pDecoder->outputFormat = internalFormat;\n    } else {\n        pDecoder->outputFormat = pConfig->format;\n    }\n\n    if (pConfig->channels == 0) {\n        pDecoder->outputChannels = internalChannels;\n    } else {\n        pDecoder->outputChannels = pConfig->channels;\n    }\n\n    if (pConfig->sampleRate == 0) {\n        pDecoder->outputSampleRate = internalSampleRate;\n    } else {\n        pDecoder->outputSampleRate = pConfig->sampleRate;\n    }\n\n    converterConfig = ma_data_converter_config_init(\n        internalFormat,     pDecoder->outputFormat,\n        internalChannels,   pDecoder->outputChannels,\n        internalSampleRate, pDecoder->outputSampleRate\n    );\n    converterConfig.pChannelMapIn          = internalChannelMap;\n    converterConfig.pChannelMapOut         = pConfig->pChannelMap;\n    converterConfig.channelMixMode         = pConfig->channelMixMode;\n    converterConfig.ditherMode             = pConfig->ditherMode;\n    converterConfig.allowDynamicSampleRate = MA_FALSE;   /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */\n    converterConfig.resampling             = pConfig->resampling;\n\n    result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /*\n    Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll\n    need this if the data converter does not support calculation of the required input frame count. To\n    determine support for this we'll just run a test.\n    */\n    {\n        ma_uint64 unused;\n\n        result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused);\n        if (result != MA_SUCCESS) {\n            /*\n            We were unable to calculate the required input frame count which means we'll need to use\n            a heap-allocated cache.\n            */\n            ma_uint64 inputCacheCapSizeInBytes;\n\n            pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels);\n\n            /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */\n            inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels);\n            if (inputCacheCapSizeInBytes > MA_SIZE_MAX) {\n                ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);\n                return MA_OUT_OF_MEMORY;\n            }\n\n            pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks);    /* Safe cast to size_t. */\n            if (pDecoder->pInputCache == NULL) {\n                ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);\n                return MA_OUT_OF_MEMORY;\n            }\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n\n\nstatic ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)\n{\n    ma_decoder* pDecoder = (ma_decoder*)pUserData;\n    MA_ASSERT(pDecoder != NULL);\n\n    return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead);\n}\n\nstatic ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin)\n{\n    ma_decoder* pDecoder = (ma_decoder*)pUserData;\n    MA_ASSERT(pDecoder != NULL);\n\n    return ma_decoder_seek_bytes(pDecoder, offset, origin);\n}\n\nstatic ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor)\n{\n    ma_decoder* pDecoder = (ma_decoder*)pUserData;\n    MA_ASSERT(pDecoder != NULL);\n\n    return ma_decoder_tell_bytes(pDecoder, pCursor);\n}\n\n\nstatic ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_decoding_backend_config backendConfig;\n    ma_data_source* pBackend;\n\n    MA_ASSERT(pVTable  != NULL);\n    MA_ASSERT(pConfig  != NULL);\n    MA_ASSERT(pDecoder != NULL);\n\n    if (pVTable->onInit == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);\n\n    result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to initialize the backend from this vtable. */\n    }\n\n    /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */\n    pDecoder->pBackend         = pBackend;\n    pDecoder->pBackendVTable   = pVTable;\n    pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_decoding_backend_config backendConfig;\n    ma_data_source* pBackend;\n\n    MA_ASSERT(pVTable  != NULL);\n    MA_ASSERT(pConfig  != NULL);\n    MA_ASSERT(pDecoder != NULL);\n\n    if (pVTable->onInitFile == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);\n\n    result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to initialize the backend from this vtable. */\n    }\n\n    /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */\n    pDecoder->pBackend         = pBackend;\n    pDecoder->pBackendVTable   = pVTable;\n    pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_decoding_backend_config backendConfig;\n    ma_data_source* pBackend;\n\n    MA_ASSERT(pVTable  != NULL);\n    MA_ASSERT(pConfig  != NULL);\n    MA_ASSERT(pDecoder != NULL);\n\n    if (pVTable->onInitFileW == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);\n\n    result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to initialize the backend from this vtable. */\n    }\n\n    /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */\n    pDecoder->pBackend         = pBackend;\n    pDecoder->pBackendVTable   = pVTable;\n    pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_decoding_backend_config backendConfig;\n    ma_data_source* pBackend;\n\n    MA_ASSERT(pVTable  != NULL);\n    MA_ASSERT(pConfig  != NULL);\n    MA_ASSERT(pDecoder != NULL);\n\n    if (pVTable->onInitMemory == NULL) {\n        return MA_NOT_IMPLEMENTED;\n    }\n\n    backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);\n\n    result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to initialize the backend from this vtable. */\n    }\n\n    /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */\n    pDecoder->pBackend         = pBackend;\n    pDecoder->pBackendVTable   = pVTable;\n    pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;\n\n    return MA_SUCCESS;\n}\n\n\n\nstatic ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result = MA_NO_BACKEND;\n    size_t ivtable;\n\n    MA_ASSERT(pConfig != NULL);\n    MA_ASSERT(pDecoder != NULL);\n\n    if (pConfig->ppCustomBackendVTables == NULL) {\n        return MA_NO_BACKEND;\n    }\n\n    /* The order each backend is listed is what defines the priority. */\n    for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {\n        const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];\n        if (pVTable != NULL) {\n            result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder);\n            if (result == MA_SUCCESS) {\n                return MA_SUCCESS;\n            } else {\n                /* Initialization failed. Move on to the next one, but seek back to the start first so the next vtable starts from the first byte of the file. */\n                result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start);\n                if (result != MA_SUCCESS) {\n                    return result;  /* Failed to seek back to the start. */\n                }\n            }\n        } else {\n            /* No vtable. */\n        }\n    }\n\n    /* Getting here means we couldn't find a backend. */\n    return MA_NO_BACKEND;\n}\n\nstatic ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result = MA_NO_BACKEND;\n    size_t ivtable;\n\n    MA_ASSERT(pConfig != NULL);\n    MA_ASSERT(pDecoder != NULL);\n\n    if (pConfig->ppCustomBackendVTables == NULL) {\n        return MA_NO_BACKEND;\n    }\n\n    /* The order each backend is listed is what defines the priority. */\n    for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {\n        const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];\n        if (pVTable != NULL) {\n            result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder);\n            if (result == MA_SUCCESS) {\n                return MA_SUCCESS;\n            }\n        } else {\n            /* No vtable. */\n        }\n    }\n\n    /* Getting here means we couldn't find a backend. */\n    return MA_NO_BACKEND;\n}\n\nstatic ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result = MA_NO_BACKEND;\n    size_t ivtable;\n\n    MA_ASSERT(pConfig != NULL);\n    MA_ASSERT(pDecoder != NULL);\n\n    if (pConfig->ppCustomBackendVTables == NULL) {\n        return MA_NO_BACKEND;\n    }\n\n    /* The order each backend is listed is what defines the priority. */\n    for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {\n        const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];\n        if (pVTable != NULL) {\n            result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder);\n            if (result == MA_SUCCESS) {\n                return MA_SUCCESS;\n            }\n        } else {\n            /* No vtable. */\n        }\n    }\n\n    /* Getting here means we couldn't find a backend. */\n    return MA_NO_BACKEND;\n}\n\nstatic ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result = MA_NO_BACKEND;\n    size_t ivtable;\n\n    MA_ASSERT(pConfig != NULL);\n    MA_ASSERT(pDecoder != NULL);\n\n    if (pConfig->ppCustomBackendVTables == NULL) {\n        return MA_NO_BACKEND;\n    }\n\n    /* The order each backend is listed is what defines the priority. */\n    for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {\n        const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];\n        if (pVTable != NULL) {\n            result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder);\n            if (result == MA_SUCCESS) {\n                return MA_SUCCESS;\n            }\n        } else {\n            /* No vtable. */\n        }\n    }\n\n    /* Getting here means we couldn't find a backend. */\n    return MA_NO_BACKEND;\n}\n\n\n/* WAV */\n#ifdef ma_dr_wav_h\n#define MA_HAS_WAV\n\ntypedef struct\n{\n    ma_data_source_base ds;\n    ma_read_proc onRead;\n    ma_seek_proc onSeek;\n    ma_tell_proc onTell;\n    void* pReadSeekTellUserData;\n    ma_format format;           /* Can be f32, s16 or s32. */\n#if !defined(MA_NO_WAV)\n    ma_dr_wav dr;\n#endif\n} ma_wav;\n\nMA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav);\nMA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav);\nMA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav);\nMA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav);\nMA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex);\nMA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor);\nMA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength);\n\n\nstatic ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead);\n}\n\nstatic ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex);\n}\n\nstatic ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n}\n\nstatic ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)\n{\n    return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor);\n}\n\nstatic ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)\n{\n    return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength);\n}\n\nstatic ma_data_source_vtable g_ma_wav_ds_vtable =\n{\n    ma_wav_ds_read,\n    ma_wav_ds_seek,\n    ma_wav_ds_get_data_format,\n    ma_wav_ds_get_cursor,\n    ma_wav_ds_get_length,\n    NULL,   /* onSetLooping */\n    0\n};\n\n\n#if !defined(MA_NO_WAV)\nstatic size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)\n{\n    ma_wav* pWav = (ma_wav*)pUserData;\n    ma_result result;\n    size_t bytesRead;\n\n    MA_ASSERT(pWav != NULL);\n\n    result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);\n    (void)result;\n\n    return bytesRead;\n}\n\nstatic ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin)\n{\n    ma_wav* pWav = (ma_wav*)pUserData;\n    ma_result result;\n    ma_seek_origin maSeekOrigin;\n\n    MA_ASSERT(pWav != NULL);\n\n    maSeekOrigin = ma_seek_origin_start;\n    if (origin == ma_dr_wav_seek_origin_current) {\n        maSeekOrigin =  ma_seek_origin_current;\n    }\n\n    result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin);\n    if (result != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n\n    return MA_TRUE;\n}\n#endif\n\nstatic ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav)\n{\n    ma_result result;\n    ma_data_source_config dataSourceConfig;\n\n    if (pWav == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pWav);\n    pWav->format = ma_format_unknown;   /* Use closest match to source file by default. */\n\n    if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {\n        pWav->format = pConfig->preferredFormat;\n    } else {\n        /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */\n    }\n\n    dataSourceConfig = ma_data_source_config_init();\n    dataSourceConfig.vtable = &g_ma_wav_ds_vtable;\n\n    result = ma_data_source_init(&dataSourceConfig, &pWav->ds);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to initialize the base data source. */\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_wav_post_init(ma_wav* pWav)\n{\n    /*\n    If an explicit format was not specified, try picking the closest match based on the internal\n    format. The format needs to be supported by miniaudio.\n    */\n    if (pWav->format == ma_format_unknown) {\n        switch (pWav->dr.translatedFormatTag)\n        {\n            case MA_DR_WAVE_FORMAT_PCM:\n            {\n                if (pWav->dr.bitsPerSample == 8) {\n                    pWav->format = ma_format_u8;\n                } else if (pWav->dr.bitsPerSample == 16) {\n                    pWav->format = ma_format_s16;\n                } else if (pWav->dr.bitsPerSample == 24) {\n                    pWav->format = ma_format_s24;\n                } else if (pWav->dr.bitsPerSample == 32) {\n                    pWav->format = ma_format_s32;\n                }\n            } break;\n\n            case MA_DR_WAVE_FORMAT_IEEE_FLOAT:\n            {\n                if (pWav->dr.bitsPerSample == 32) {\n                    pWav->format = ma_format_f32;\n                }\n            } break;\n\n            default: break;\n        }\n\n        /* Fall back to f32 if we couldn't find anything. */\n        if (pWav->format == ma_format_unknown) {\n            pWav->format =  ma_format_f32;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav)\n{\n    ma_result result;\n\n    result = ma_wav_init_internal(pConfig, pWav);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (onRead == NULL || onSeek == NULL) {\n        return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */\n    }\n\n    pWav->onRead = onRead;\n    pWav->onSeek = onSeek;\n    pWav->onTell = onTell;\n    pWav->pReadSeekTellUserData = pReadSeekTellUserData;\n\n    #if !defined(MA_NO_WAV)\n    {\n        ma_bool32 wavResult;\n\n        wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks);\n        if (wavResult != MA_TRUE) {\n            return MA_INVALID_FILE;\n        }\n\n        ma_wav_post_init(pWav);\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* wav is disabled. */\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav)\n{\n    ma_result result;\n\n    result = ma_wav_init_internal(pConfig, pWav);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    #if !defined(MA_NO_WAV)\n    {\n        ma_bool32 wavResult;\n\n        wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks);\n        if (wavResult != MA_TRUE) {\n            return MA_INVALID_FILE;\n        }\n\n        ma_wav_post_init(pWav);\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* wav is disabled. */\n        (void)pFilePath;\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav)\n{\n    ma_result result;\n\n    result = ma_wav_init_internal(pConfig, pWav);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    #if !defined(MA_NO_WAV)\n    {\n        ma_bool32 wavResult;\n\n        wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks);\n        if (wavResult != MA_TRUE) {\n            return MA_INVALID_FILE;\n        }\n\n        ma_wav_post_init(pWav);\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* wav is disabled. */\n        (void)pFilePath;\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav)\n{\n    ma_result result;\n\n    result = ma_wav_init_internal(pConfig, pWav);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    #if !defined(MA_NO_WAV)\n    {\n        ma_bool32 wavResult;\n\n        wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks);\n        if (wavResult != MA_TRUE) {\n            return MA_INVALID_FILE;\n        }\n\n        ma_wav_post_init(pWav);\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* wav is disabled. */\n        (void)pData;\n        (void)dataSize;\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pWav == NULL) {\n        return;\n    }\n\n    (void)pAllocationCallbacks;\n\n    #if !defined(MA_NO_WAV)\n    {\n        ma_dr_wav_uninit(&pWav->dr);\n    }\n    #else\n    {\n        /* wav is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n    }\n    #endif\n\n    ma_data_source_uninit(&pWav->ds);\n}\n\nMA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pWav == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_WAV)\n    {\n        /* We always use floating point format. */\n        ma_result result = MA_SUCCESS;  /* Must be initialized to MA_SUCCESS. */\n        ma_uint64 totalFramesRead = 0;\n        ma_format format;\n\n        ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0);\n\n        switch (format)\n        {\n            case ma_format_f32:\n            {\n                totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut);\n            } break;\n\n            case ma_format_s16:\n            {\n                totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut);\n            } break;\n\n            case ma_format_s32:\n            {\n                totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut);\n            } break;\n\n            /* Fallback to a raw read. */\n            case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */\n            default:\n            {\n                totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut);\n            } break;\n        }\n\n        /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */\n        if (totalFramesRead == 0) {\n            result = MA_AT_END;\n        }\n\n        if (pFramesRead != NULL) {\n            *pFramesRead = totalFramesRead;\n        }\n\n        if (result == MA_SUCCESS && totalFramesRead == 0) {\n            result  = MA_AT_END;\n        }\n\n        return result;\n    }\n    #else\n    {\n        /* wav is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n\n        (void)pFramesOut;\n        (void)frameCount;\n        (void)pFramesRead;\n\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex)\n{\n    if (pWav == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_WAV)\n    {\n        ma_bool32 wavResult;\n\n        wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex);\n        if (wavResult != MA_TRUE) {\n            return MA_ERROR;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* wav is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n\n        (void)frameIndex;\n\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    /* Defaults for safety. */\n    if (pFormat != NULL) {\n        *pFormat = ma_format_unknown;\n    }\n    if (pChannels != NULL) {\n        *pChannels = 0;\n    }\n    if (pSampleRate != NULL) {\n        *pSampleRate = 0;\n    }\n    if (pChannelMap != NULL) {\n        MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);\n    }\n\n    if (pWav == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    if (pFormat != NULL) {\n        *pFormat = pWav->format;\n    }\n\n    #if !defined(MA_NO_WAV)\n    {\n        if (pChannels != NULL) {\n            *pChannels = pWav->dr.channels;\n        }\n\n        if (pSampleRate != NULL) {\n            *pSampleRate = pWav->dr.sampleRate;\n        }\n\n        if (pChannelMap != NULL) {\n            ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels);\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* wav is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor)\n{\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;   /* Safety. */\n\n    if (pWav == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_WAV)\n    {\n        ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor);\n        if (wavResult != MA_SUCCESS) {\n            return (ma_result)wavResult;    /* ma_dr_wav result codes map to miniaudio's. */\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* wav is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength)\n{\n    if (pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pLength = 0;   /* Safety. */\n\n    if (pWav == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_WAV)\n    {\n        ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength);\n        if (wavResult != MA_SUCCESS) {\n            return (ma_result)wavResult;    /* ma_dr_wav result codes map to miniaudio's. */\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* wav is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\n\nstatic ma_result ma_decoding_backend_init__wav(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_wav* pWav;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);\n    if (pWav == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav);\n    if (result != MA_SUCCESS) {\n        ma_free(pWav, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pWav;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoding_backend_init_file__wav(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_wav* pWav;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);\n    if (pWav == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav);\n    if (result != MA_SUCCESS) {\n        ma_free(pWav, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pWav;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoding_backend_init_file_w__wav(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_wav* pWav;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);\n    if (pWav == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav);\n    if (result != MA_SUCCESS) {\n        ma_free(pWav, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pWav;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoding_backend_init_memory__wav(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_wav* pWav;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);\n    if (pWav == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav);\n    if (result != MA_SUCCESS) {\n        ma_free(pWav, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pWav;\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_wav* pWav = (ma_wav*)pBackend;\n\n    (void)pUserData;\n\n    ma_wav_uninit(pWav, pAllocationCallbacks);\n    ma_free(pWav, pAllocationCallbacks);\n}\n\nstatic ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav =\n{\n    ma_decoding_backend_init__wav,\n    ma_decoding_backend_init_file__wav,\n    ma_decoding_backend_init_file_w__wav,\n    ma_decoding_backend_init_memory__wav,\n    ma_decoding_backend_uninit__wav\n};\n\nstatic ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder);\n}\n#endif  /* ma_dr_wav_h */\n\n/* FLAC */\n#ifdef ma_dr_flac_h\n#define MA_HAS_FLAC\n\ntypedef struct\n{\n    ma_data_source_base ds;\n    ma_read_proc onRead;\n    ma_seek_proc onSeek;\n    ma_tell_proc onTell;\n    void* pReadSeekTellUserData;\n    ma_format format;           /* Can be f32, s16 or s32. */\n#if !defined(MA_NO_FLAC)\n    ma_dr_flac* dr;\n#endif\n} ma_flac;\n\nMA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac);\nMA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac);\nMA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac);\nMA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac);\nMA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex);\nMA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor);\nMA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength);\n\n\nstatic ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead);\n}\n\nstatic ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex);\n}\n\nstatic ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n}\n\nstatic ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)\n{\n    return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor);\n}\n\nstatic ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)\n{\n    return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength);\n}\n\nstatic ma_data_source_vtable g_ma_flac_ds_vtable =\n{\n    ma_flac_ds_read,\n    ma_flac_ds_seek,\n    ma_flac_ds_get_data_format,\n    ma_flac_ds_get_cursor,\n    ma_flac_ds_get_length,\n    NULL,   /* onSetLooping */\n    0\n};\n\n\n#if !defined(MA_NO_FLAC)\nstatic size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)\n{\n    ma_flac* pFlac = (ma_flac*)pUserData;\n    ma_result result;\n    size_t bytesRead;\n\n    MA_ASSERT(pFlac != NULL);\n\n    result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);\n    (void)result;\n\n    return bytesRead;\n}\n\nstatic ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin)\n{\n    ma_flac* pFlac = (ma_flac*)pUserData;\n    ma_result result;\n    ma_seek_origin maSeekOrigin;\n\n    MA_ASSERT(pFlac != NULL);\n\n    maSeekOrigin = ma_seek_origin_start;\n    if (origin == ma_dr_flac_seek_origin_current) {\n        maSeekOrigin =  ma_seek_origin_current;\n    }\n\n    result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin);\n    if (result != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n\n    return MA_TRUE;\n}\n#endif\n\nstatic ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac)\n{\n    ma_result result;\n    ma_data_source_config dataSourceConfig;\n\n    if (pFlac == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pFlac);\n    pFlac->format = ma_format_f32;    /* f32 by default. */\n\n    if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {\n        pFlac->format = pConfig->preferredFormat;\n    } else {\n        /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */\n    }\n\n    dataSourceConfig = ma_data_source_config_init();\n    dataSourceConfig.vtable = &g_ma_flac_ds_vtable;\n\n    result = ma_data_source_init(&dataSourceConfig, &pFlac->ds);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to initialize the base data source. */\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac)\n{\n    ma_result result;\n\n    result = ma_flac_init_internal(pConfig, pFlac);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (onRead == NULL || onSeek == NULL) {\n        return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */\n    }\n\n    pFlac->onRead = onRead;\n    pFlac->onSeek = onSeek;\n    pFlac->onTell = onTell;\n    pFlac->pReadSeekTellUserData = pReadSeekTellUserData;\n\n    #if !defined(MA_NO_FLAC)\n    {\n        pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks);\n        if (pFlac->dr == NULL) {\n            return MA_INVALID_FILE;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* flac is disabled. */\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac)\n{\n    ma_result result;\n\n    result = ma_flac_init_internal(pConfig, pFlac);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    #if !defined(MA_NO_FLAC)\n    {\n        pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks);\n        if (pFlac->dr == NULL) {\n            return MA_INVALID_FILE;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* flac is disabled. */\n        (void)pFilePath;\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac)\n{\n    ma_result result;\n\n    result = ma_flac_init_internal(pConfig, pFlac);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    #if !defined(MA_NO_FLAC)\n    {\n        pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks);\n        if (pFlac->dr == NULL) {\n            return MA_INVALID_FILE;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* flac is disabled. */\n        (void)pFilePath;\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac)\n{\n    ma_result result;\n\n    result = ma_flac_init_internal(pConfig, pFlac);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    #if !defined(MA_NO_FLAC)\n    {\n        pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks);\n        if (pFlac->dr == NULL) {\n            return MA_INVALID_FILE;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* flac is disabled. */\n        (void)pData;\n        (void)dataSize;\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFlac == NULL) {\n        return;\n    }\n\n    (void)pAllocationCallbacks;\n\n    #if !defined(MA_NO_FLAC)\n    {\n        ma_dr_flac_close(pFlac->dr);\n    }\n    #else\n    {\n        /* flac is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n    }\n    #endif\n\n    ma_data_source_uninit(&pFlac->ds);\n}\n\nMA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pFlac == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_FLAC)\n    {\n        /* We always use floating point format. */\n        ma_result result = MA_SUCCESS;  /* Must be initialized to MA_SUCCESS. */\n        ma_uint64 totalFramesRead = 0;\n        ma_format format;\n\n        ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0);\n\n        switch (format)\n        {\n            case ma_format_f32:\n            {\n                totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut);\n            } break;\n\n            case ma_format_s16:\n            {\n                totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut);\n            } break;\n\n            case ma_format_s32:\n            {\n                totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut);\n            } break;\n\n            case ma_format_u8:\n            case ma_format_s24:\n            case ma_format_unknown:\n            default:\n            {\n                return MA_INVALID_OPERATION;\n            };\n        }\n\n        /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */\n        if (totalFramesRead == 0) {\n            result = MA_AT_END;\n        }\n\n        if (pFramesRead != NULL) {\n            *pFramesRead = totalFramesRead;\n        }\n\n        if (result == MA_SUCCESS && totalFramesRead == 0) {\n            result  = MA_AT_END;\n        }\n\n        return result;\n    }\n    #else\n    {\n        /* flac is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n\n        (void)pFramesOut;\n        (void)frameCount;\n        (void)pFramesRead;\n\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex)\n{\n    if (pFlac == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_FLAC)\n    {\n        ma_bool32 flacResult;\n\n        flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex);\n        if (flacResult != MA_TRUE) {\n            return MA_ERROR;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* flac is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n\n        (void)frameIndex;\n\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    /* Defaults for safety. */\n    if (pFormat != NULL) {\n        *pFormat = ma_format_unknown;\n    }\n    if (pChannels != NULL) {\n        *pChannels = 0;\n    }\n    if (pSampleRate != NULL) {\n        *pSampleRate = 0;\n    }\n    if (pChannelMap != NULL) {\n        MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);\n    }\n\n    if (pFlac == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    if (pFormat != NULL) {\n        *pFormat = pFlac->format;\n    }\n\n    #if !defined(MA_NO_FLAC)\n    {\n        if (pChannels != NULL) {\n            *pChannels = pFlac->dr->channels;\n        }\n\n        if (pSampleRate != NULL) {\n            *pSampleRate = pFlac->dr->sampleRate;\n        }\n\n        if (pChannelMap != NULL) {\n            ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels);\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* flac is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor)\n{\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;   /* Safety. */\n\n    if (pFlac == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_FLAC)\n    {\n        *pCursor = pFlac->dr->currentPCMFrame;\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* flac is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength)\n{\n    if (pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pLength = 0;   /* Safety. */\n\n    if (pFlac == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_FLAC)\n    {\n        *pLength = pFlac->dr->totalPCMFrameCount;\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* flac is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\n\nstatic ma_result ma_decoding_backend_init__flac(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_flac* pFlac;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac);\n    if (result != MA_SUCCESS) {\n        ma_free(pFlac, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pFlac;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoding_backend_init_file__flac(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_flac* pFlac;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac);\n    if (result != MA_SUCCESS) {\n        ma_free(pFlac, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pFlac;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoding_backend_init_file_w__flac(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_flac* pFlac;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac);\n    if (result != MA_SUCCESS) {\n        ma_free(pFlac, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pFlac;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoding_backend_init_memory__flac(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_flac* pFlac;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac);\n    if (result != MA_SUCCESS) {\n        ma_free(pFlac, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pFlac;\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_flac* pFlac = (ma_flac*)pBackend;\n\n    (void)pUserData;\n\n    ma_flac_uninit(pFlac, pAllocationCallbacks);\n    ma_free(pFlac, pAllocationCallbacks);\n}\n\nstatic ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac =\n{\n    ma_decoding_backend_init__flac,\n    ma_decoding_backend_init_file__flac,\n    ma_decoding_backend_init_file_w__flac,\n    ma_decoding_backend_init_memory__flac,\n    ma_decoding_backend_uninit__flac\n};\n\nstatic ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder);\n}\n#endif  /* ma_dr_flac_h */\n\n/* MP3 */\n#ifdef ma_dr_mp3_h\n#define MA_HAS_MP3\n\ntypedef struct\n{\n    ma_data_source_base ds;\n    ma_read_proc onRead;\n    ma_seek_proc onSeek;\n    ma_tell_proc onTell;\n    void* pReadSeekTellUserData;\n    ma_format format;           /* Can be f32 or s16. */\n#if !defined(MA_NO_MP3)\n    ma_dr_mp3 dr;\n    ma_uint32 seekPointCount;\n    ma_dr_mp3_seek_point* pSeekPoints;  /* Only used if seek table generation is used. */\n#endif\n} ma_mp3;\n\nMA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3);\nMA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3);\nMA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3);\nMA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3);\nMA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex);\nMA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor);\nMA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength);\n\n\nstatic ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead);\n}\n\nstatic ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex);\n}\n\nstatic ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n}\n\nstatic ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)\n{\n    return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor);\n}\n\nstatic ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)\n{\n    return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength);\n}\n\nstatic ma_data_source_vtable g_ma_mp3_ds_vtable =\n{\n    ma_mp3_ds_read,\n    ma_mp3_ds_seek,\n    ma_mp3_ds_get_data_format,\n    ma_mp3_ds_get_cursor,\n    ma_mp3_ds_get_length,\n    NULL,   /* onSetLooping */\n    0\n};\n\n\n#if !defined(MA_NO_MP3)\nstatic size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)\n{\n    ma_mp3* pMP3 = (ma_mp3*)pUserData;\n    ma_result result;\n    size_t bytesRead;\n\n    MA_ASSERT(pMP3 != NULL);\n\n    result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);\n    (void)result;\n\n    return bytesRead;\n}\n\nstatic ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin)\n{\n    ma_mp3* pMP3 = (ma_mp3*)pUserData;\n    ma_result result;\n    ma_seek_origin maSeekOrigin;\n\n    MA_ASSERT(pMP3 != NULL);\n\n    maSeekOrigin = ma_seek_origin_start;\n    if (origin == ma_dr_mp3_seek_origin_current) {\n        maSeekOrigin =  ma_seek_origin_current;\n    }\n\n    result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin);\n    if (result != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n\n    return MA_TRUE;\n}\n#endif\n\nstatic ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3)\n{\n    ma_result result;\n    ma_data_source_config dataSourceConfig;\n\n    if (pMP3 == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pMP3);\n    pMP3->format = ma_format_f32;    /* f32 by default. */\n\n    if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {\n        pMP3->format = pConfig->preferredFormat;\n    } else {\n        /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */\n    }\n\n    dataSourceConfig = ma_data_source_config_init();\n    dataSourceConfig.vtable = &g_ma_mp3_ds_vtable;\n\n    result = ma_data_source_init(&dataSourceConfig, &pMP3->ds);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to initialize the base data source. */\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_bool32 mp3Result;\n    ma_uint32 seekPointCount = 0;\n    ma_dr_mp3_seek_point* pSeekPoints = NULL;\n\n    MA_ASSERT(pMP3    != NULL);\n    MA_ASSERT(pConfig != NULL);\n\n    seekPointCount = pConfig->seekPointCount;\n    if (seekPointCount > 0) {\n        pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks);\n        if (pSeekPoints == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    }\n\n    mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints);\n    if (mp3Result != MA_TRUE) {\n        ma_free(pSeekPoints, pAllocationCallbacks);\n        return MA_ERROR;\n    }\n\n    mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints);\n    if (mp3Result != MA_TRUE) {\n        ma_free(pSeekPoints, pAllocationCallbacks);\n        return MA_ERROR;\n    }\n\n    pMP3->seekPointCount = seekPointCount;\n    pMP3->pSeekPoints    = pSeekPoints;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_result result;\n\n    result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3)\n{\n    ma_result result;\n\n    result = ma_mp3_init_internal(pConfig, pMP3);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (onRead == NULL || onSeek == NULL) {\n        return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */\n    }\n\n    pMP3->onRead = onRead;\n    pMP3->onSeek = onSeek;\n    pMP3->onTell = onTell;\n    pMP3->pReadSeekTellUserData = pReadSeekTellUserData;\n\n    #if !defined(MA_NO_MP3)\n    {\n        ma_bool32 mp3Result;\n\n        mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks);\n        if (mp3Result != MA_TRUE) {\n            return MA_INVALID_FILE;\n        }\n\n        ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* mp3 is disabled. */\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3)\n{\n    ma_result result;\n\n    result = ma_mp3_init_internal(pConfig, pMP3);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    #if !defined(MA_NO_MP3)\n    {\n        ma_bool32 mp3Result;\n\n        mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks);\n        if (mp3Result != MA_TRUE) {\n            return MA_INVALID_FILE;\n        }\n\n        ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* mp3 is disabled. */\n        (void)pFilePath;\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3)\n{\n    ma_result result;\n\n    result = ma_mp3_init_internal(pConfig, pMP3);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    #if !defined(MA_NO_MP3)\n    {\n        ma_bool32 mp3Result;\n\n        mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks);\n        if (mp3Result != MA_TRUE) {\n            return MA_INVALID_FILE;\n        }\n\n        ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* mp3 is disabled. */\n        (void)pFilePath;\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3)\n{\n    ma_result result;\n\n    result = ma_mp3_init_internal(pConfig, pMP3);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    #if !defined(MA_NO_MP3)\n    {\n        ma_bool32 mp3Result;\n\n        mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks);\n        if (mp3Result != MA_TRUE) {\n            return MA_INVALID_FILE;\n        }\n\n        ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* mp3 is disabled. */\n        (void)pData;\n        (void)dataSize;\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pMP3 == NULL) {\n        return;\n    }\n\n    #if !defined(MA_NO_MP3)\n    {\n        ma_dr_mp3_uninit(&pMP3->dr);\n    }\n    #else\n    {\n        /* mp3 is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n    }\n    #endif\n\n    /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */\n    ma_free(pMP3->pSeekPoints, pAllocationCallbacks);\n\n    ma_data_source_uninit(&pMP3->ds);\n}\n\nMA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pMP3 == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_MP3)\n    {\n        /* We always use floating point format. */\n        ma_result result = MA_SUCCESS;  /* Must be initialized to MA_SUCCESS. */\n        ma_uint64 totalFramesRead = 0;\n        ma_format format;\n\n        ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0);\n\n        switch (format)\n        {\n            case ma_format_f32:\n            {\n                totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut);\n            } break;\n\n            case ma_format_s16:\n            {\n                totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut);\n            } break;\n\n            case ma_format_u8:\n            case ma_format_s24:\n            case ma_format_s32:\n            case ma_format_unknown:\n            default:\n            {\n                return MA_INVALID_OPERATION;\n            };\n        }\n\n        /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */\n        if (totalFramesRead == 0) {\n            result = MA_AT_END;\n        }\n\n        if (pFramesRead != NULL) {\n            *pFramesRead = totalFramesRead;\n        }\n\n        return result;\n    }\n    #else\n    {\n        /* mp3 is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n\n        (void)pFramesOut;\n        (void)frameCount;\n        (void)pFramesRead;\n\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex)\n{\n    if (pMP3 == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_MP3)\n    {\n        ma_bool32 mp3Result;\n\n        mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex);\n        if (mp3Result != MA_TRUE) {\n            return MA_ERROR;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* mp3 is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n\n        (void)frameIndex;\n\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    /* Defaults for safety. */\n    if (pFormat != NULL) {\n        *pFormat = ma_format_unknown;\n    }\n    if (pChannels != NULL) {\n        *pChannels = 0;\n    }\n    if (pSampleRate != NULL) {\n        *pSampleRate = 0;\n    }\n    if (pChannelMap != NULL) {\n        MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);\n    }\n\n    if (pMP3 == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    if (pFormat != NULL) {\n        *pFormat = pMP3->format;\n    }\n\n    #if !defined(MA_NO_MP3)\n    {\n        if (pChannels != NULL) {\n            *pChannels = pMP3->dr.channels;\n        }\n\n        if (pSampleRate != NULL) {\n            *pSampleRate = pMP3->dr.sampleRate;\n        }\n\n        if (pChannelMap != NULL) {\n            ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels);\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* mp3 is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor)\n{\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;   /* Safety. */\n\n    if (pMP3 == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_MP3)\n    {\n        *pCursor = pMP3->dr.currentPCMFrame;\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* mp3 is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength)\n{\n    if (pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pLength = 0;   /* Safety. */\n\n    if (pMP3 == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_MP3)\n    {\n        *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr);\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* mp3 is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\n\nstatic ma_result ma_decoding_backend_init__mp3(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_mp3* pMP3;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);\n    if (pMP3 == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3);\n    if (result != MA_SUCCESS) {\n        ma_free(pMP3, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pMP3;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoding_backend_init_file__mp3(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_mp3* pMP3;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);\n    if (pMP3 == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3);\n    if (result != MA_SUCCESS) {\n        ma_free(pMP3, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pMP3;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoding_backend_init_file_w__mp3(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_mp3* pMP3;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);\n    if (pMP3 == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3);\n    if (result != MA_SUCCESS) {\n        ma_free(pMP3, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pMP3;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoding_backend_init_memory__mp3(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_mp3* pMP3;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);\n    if (pMP3 == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3);\n    if (result != MA_SUCCESS) {\n        ma_free(pMP3, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pMP3;\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_mp3* pMP3 = (ma_mp3*)pBackend;\n\n    (void)pUserData;\n\n    ma_mp3_uninit(pMP3, pAllocationCallbacks);\n    ma_free(pMP3, pAllocationCallbacks);\n}\n\nstatic ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 =\n{\n    ma_decoding_backend_init__mp3,\n    ma_decoding_backend_init_file__mp3,\n    ma_decoding_backend_init_file_w__mp3,\n    ma_decoding_backend_init_memory__mp3,\n    ma_decoding_backend_uninit__mp3\n};\n\nstatic ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder);\n}\n#endif  /* ma_dr_mp3_h */\n\n/* Vorbis */\n#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H\n#define MA_HAS_VORBIS\n\n/* The size in bytes of each chunk of data to read from the Vorbis stream. */\n#define MA_VORBIS_DATA_CHUNK_SIZE  4096\n\ntypedef struct\n{\n    ma_data_source_base ds;\n    ma_read_proc onRead;\n    ma_seek_proc onSeek;\n    ma_tell_proc onTell;\n    void* pReadSeekTellUserData;\n    ma_allocation_callbacks allocationCallbacks;    /* Store the allocation callbacks within the structure because we may need to dynamically expand a buffer in ma_stbvorbis_read_pcm_frames() when using push mode. */\n    ma_format format;               /* Only f32 is allowed with stb_vorbis. */\n    ma_uint32 channels;\n    ma_uint32 sampleRate;\n    ma_uint64 cursor;\n#if !defined(MA_NO_VORBIS)\n    stb_vorbis* stb;\n    ma_bool32 usingPushMode;\n    struct\n    {\n        ma_uint8* pData;\n        size_t dataSize;\n        size_t dataCapacity;\n        size_t audioStartOffsetInBytes;\n        ma_uint32 framesConsumed;   /* The number of frames consumed in ppPacketData. */\n        ma_uint32 framesRemaining;  /* The number of frames remaining in ppPacketData. */\n        float** ppPacketData;\n    } push;\n#endif\n} ma_stbvorbis;\n\nMA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis);\nMA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis);\nMA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis);\nMA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);\nMA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);\nMA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex);\nMA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);\nMA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor);\nMA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength);\n\n\nstatic ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead);\n}\n\nstatic ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex);\n}\n\nstatic ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n}\n\nstatic ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)\n{\n    return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor);\n}\n\nstatic ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)\n{\n    return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength);\n}\n\nstatic ma_data_source_vtable g_ma_stbvorbis_ds_vtable =\n{\n    ma_stbvorbis_ds_read,\n    ma_stbvorbis_ds_seek,\n    ma_stbvorbis_ds_get_data_format,\n    ma_stbvorbis_ds_get_cursor,\n    ma_stbvorbis_ds_get_length,\n    NULL,   /* onSetLooping */\n    0\n};\n\n\nstatic ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis)\n{\n    ma_result result;\n    ma_data_source_config dataSourceConfig;\n\n    (void)pConfig;\n\n    if (pVorbis == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pVorbis);\n    pVorbis->format = ma_format_f32;    /* Only supporting f32. */\n\n    dataSourceConfig = ma_data_source_config_init();\n    dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable;\n\n    result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to initialize the base data source. */\n    }\n\n    return MA_SUCCESS;\n}\n\n#if !defined(MA_NO_VORBIS)\nstatic ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis)\n{\n    stb_vorbis_info info;\n\n    MA_ASSERT(pVorbis != NULL);\n\n    info = stb_vorbis_get_info(pVorbis->stb);\n\n    pVorbis->channels   = info.channels;\n    pVorbis->sampleRate = info.sample_rate;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis)\n{\n    ma_result result;\n    stb_vorbis* stb;\n    size_t dataSize = 0;\n    size_t dataCapacity = 0;\n    ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */\n\n    for (;;) {\n        int vorbisError;\n        int consumedDataSize;   /* <-- Fill by stb_vorbis_open_pushdata(). */\n        size_t bytesRead;\n        ma_uint8* pNewData;\n\n        /* Allocate memory for the new chunk. */\n        dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;\n        pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks);\n        if (pNewData == NULL) {\n            ma_free(pData, &pVorbis->allocationCallbacks);\n            return MA_OUT_OF_MEMORY;\n        }\n\n        pData = pNewData;\n\n        /* Read in the next chunk. */\n        result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead);\n        dataSize += bytesRead;\n\n        if (result != MA_SUCCESS) {\n            ma_free(pData, &pVorbis->allocationCallbacks);\n            return result;\n        }\n\n        /* We have a maximum of 31 bits with stb_vorbis. */\n        if (dataSize > INT_MAX) {\n            ma_free(pData, &pVorbis->allocationCallbacks);\n            return MA_TOO_BIG;\n        }\n\n        stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);\n        if (stb != NULL) {\n            /*\n            Successfully opened the Vorbis decoder. We might have some leftover unprocessed\n            data so we'll need to move that down to the front.\n            */\n            dataSize -= (size_t)consumedDataSize;   /* Consume the data. */\n            MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize);\n\n            /*\n            We need to track the start point so we can seek back to the start of the audio\n            data when seeking.\n            */\n            pVorbis->push.audioStartOffsetInBytes = consumedDataSize;\n\n            break;\n        } else {\n            /* Failed to open the decoder. */\n            if (vorbisError == VORBIS_need_more_data) {\n                continue;\n            } else {\n                ma_free(pData, &pVorbis->allocationCallbacks);\n                return MA_ERROR;   /* Failed to open the stb_vorbis decoder. */\n            }\n        }\n    }\n\n    MA_ASSERT(stb != NULL);\n    pVorbis->stb = stb;\n    pVorbis->push.pData = pData;\n    pVorbis->push.dataSize = dataSize;\n    pVorbis->push.dataCapacity = dataCapacity;\n\n    return MA_SUCCESS;\n}\n#endif\n\nMA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis)\n{\n    ma_result result;\n\n    result = ma_stbvorbis_init_internal(pConfig, pVorbis);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (onRead == NULL || onSeek == NULL) {\n        return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */\n    }\n\n    pVorbis->onRead = onRead;\n    pVorbis->onSeek = onSeek;\n    pVorbis->onTell = onTell;\n    pVorbis->pReadSeekTellUserData = pReadSeekTellUserData;\n    ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks);\n\n    #if !defined(MA_NO_VORBIS)\n    {\n        /*\n        stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the\n        pushing API. In order for us to be able to successfully initialize the decoder we need to\n        supply it with enough data. We need to keep loading data until we have enough.\n        */\n        result = ma_stbvorbis_init_internal_decoder_push(pVorbis);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pVorbis->usingPushMode = MA_TRUE;\n\n        result = ma_stbvorbis_post_init(pVorbis);\n        if (result != MA_SUCCESS) {\n            stb_vorbis_close(pVorbis->stb);\n            ma_free(pVorbis->push.pData, pAllocationCallbacks);\n            return result;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* vorbis is disabled. */\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis)\n{\n    ma_result result;\n\n    result = ma_stbvorbis_init_internal(pConfig, pVorbis);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    #if !defined(MA_NO_VORBIS)\n    {\n        (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */\n\n        /* We can use stb_vorbis' pull mode for file based streams. */\n        pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL);\n        if (pVorbis->stb == NULL) {\n            return MA_INVALID_FILE;\n        }\n\n        pVorbis->usingPushMode = MA_FALSE;\n\n        result = ma_stbvorbis_post_init(pVorbis);\n        if (result != MA_SUCCESS) {\n            stb_vorbis_close(pVorbis->stb);\n            return result;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* vorbis is disabled. */\n        (void)pFilePath;\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis)\n{\n    ma_result result;\n\n    result = ma_stbvorbis_init_internal(pConfig, pVorbis);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    #if !defined(MA_NO_VORBIS)\n    {\n        (void)pAllocationCallbacks;\n\n        /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */\n        if (dataSize > INT_MAX) {\n            return MA_TOO_BIG;\n        }\n\n        pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL);\n        if (pVorbis->stb == NULL) {\n            return MA_INVALID_FILE;\n        }\n\n        pVorbis->usingPushMode = MA_FALSE;\n\n        result = ma_stbvorbis_post_init(pVorbis);\n        if (result != MA_SUCCESS) {\n            stb_vorbis_close(pVorbis->stb);\n            return result;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* vorbis is disabled. */\n        (void)pData;\n        (void)dataSize;\n        (void)pAllocationCallbacks;\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pVorbis == NULL) {\n        return;\n    }\n\n    #if !defined(MA_NO_VORBIS)\n    {\n        stb_vorbis_close(pVorbis->stb);\n\n        /* We'll have to clear some memory if we're using push mode. */\n        if (pVorbis->usingPushMode) {\n            ma_free(pVorbis->push.pData, pAllocationCallbacks);\n        }\n    }\n    #else\n    {\n        /* vorbis is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n    }\n    #endif\n\n    ma_data_source_uninit(&pVorbis->ds);\n}\n\nMA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pVorbis == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_VORBIS)\n    {\n        /* We always use floating point format. */\n        ma_result result = MA_SUCCESS;  /* Must be initialized to MA_SUCCESS. */\n        ma_uint64 totalFramesRead = 0;\n        ma_format format;\n        ma_uint32 channels;\n\n        ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0);\n\n        if (format == ma_format_f32) {\n            /* We read differently depending on whether or not we're using push mode. */\n            if (pVorbis->usingPushMode) {\n                /* Push mode. This is the complex case. */\n                float* pFramesOutF32 = (float*)pFramesOut;\n\n                while (totalFramesRead < frameCount) {\n                    /* The first thing to do is read from any already-cached frames. */\n                    ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead));  /* Safe cast because pVorbis->framesRemaining is 32-bit. */\n\n                    /* The output pointer can be null in which case we just treate it as a seek. */\n                    if (pFramesOut != NULL) {\n                        ma_uint64 iFrame;\n                        for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {\n                            ma_uint32 iChannel;\n                            for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) {\n                                pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame];\n                            }\n\n                            pFramesOutF32 += pVorbis->channels;\n                        }\n                    }\n\n                    /* Update pointers and counters. */\n                    pVorbis->push.framesConsumed  += framesToReadFromCache;\n                    pVorbis->push.framesRemaining -= framesToReadFromCache;\n                    totalFramesRead               += framesToReadFromCache;\n\n                    /* Don't bother reading any more frames right now if we've just finished loading. */\n                    if (totalFramesRead == frameCount) {\n                        break;\n                    }\n\n                    MA_ASSERT(pVorbis->push.framesRemaining == 0);\n\n                    /* Getting here means we've run out of cached frames. We'll need to load some more. */\n                    for (;;) {\n                        int samplesRead = 0;\n                        int consumedDataSize;\n\n                        /* We need to case dataSize to an int, so make sure we can do it safely. */\n                        if (pVorbis->push.dataSize > INT_MAX) {\n                            break;  /* Too big. */\n                        }\n\n                        consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead);\n                        if (consumedDataSize != 0) {\n                            /* Successfully decoded a Vorbis frame. Consume the data. */\n                            pVorbis->push.dataSize -= (size_t)consumedDataSize;\n                            MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize);\n\n                            pVorbis->push.framesConsumed  = 0;\n                            pVorbis->push.framesRemaining = samplesRead;\n\n                            break;\n                        } else {\n                            /* Not enough data. Read more. */\n                            size_t bytesRead;\n\n                            /* Expand the data buffer if necessary. */\n                            if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) {\n                                size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;\n                                ma_uint8* pNewData;\n\n                                pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks);\n                                if (pNewData == NULL) {\n                                    result = MA_OUT_OF_MEMORY;\n                                    break;\n                                }\n\n                                pVorbis->push.pData = pNewData;\n                                pVorbis->push.dataCapacity = newCap;\n                            }\n\n                            /* We should have enough room to load some data. */\n                            result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead);\n                            pVorbis->push.dataSize += bytesRead;\n\n                            if (result != MA_SUCCESS) {\n                                break;  /* Failed to read any data. Get out. */\n                            }\n                        }\n                    }\n\n                    /* If we don't have a success code at this point it means we've encounted an error or the end of the file has been reached (probably the latter). */\n                    if (result != MA_SUCCESS) {\n                        break;\n                    }\n                }\n            } else {\n                /* Pull mode. This is the simple case, but we still need to run in a loop because stb_vorbis loves using 32-bit instead of 64-bit. */\n                while (totalFramesRead < frameCount) {\n                    ma_uint64 framesRemaining = (frameCount - totalFramesRead);\n                    int framesRead;\n\n                    if (framesRemaining > INT_MAX) {\n                        framesRemaining = INT_MAX;\n                    }\n\n                    framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels);   /* Safe cast. */\n                    totalFramesRead += framesRead;\n\n                    if (framesRead < (int)framesRemaining) {\n                        break;  /* Nothing left to read. Get out. */\n                    }\n                }\n            }\n        } else {\n            result = MA_INVALID_ARGS;\n        }\n\n        pVorbis->cursor += totalFramesRead;\n\n        if (totalFramesRead == 0) {\n            result = MA_AT_END;\n        }\n\n        if (pFramesRead != NULL) {\n            *pFramesRead = totalFramesRead;\n        }\n\n        if (result == MA_SUCCESS && totalFramesRead == 0) {\n            result  = MA_AT_END;\n        }\n\n        return result;\n    }\n    #else\n    {\n        /* vorbis is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n\n        (void)pFramesOut;\n        (void)frameCount;\n        (void)pFramesRead;\n\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex)\n{\n    if (pVorbis == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_VORBIS)\n    {\n        /* Different seeking methods depending on whether or not we're using push mode. */\n        if (pVorbis->usingPushMode) {\n            /* Push mode. This is the complex case. */\n            ma_result result;\n            float buffer[4096];\n\n            /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */\n            if (frameIndex < pVorbis->cursor) {\n                if (frameIndex > 0x7FFFFFFF) {\n                    return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */\n                }\n\n                /*\n                This is wildly inefficient due to me having trouble getting sample exact seeking working\n                robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work\n                perfectly is to reinitialize the decoder. Note that we only enter this path when seeking\n                backwards. This will hopefully be removed once we get our own Vorbis decoder implemented.\n                */\n                stb_vorbis_close(pVorbis->stb);\n                ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks);\n\n                MA_ZERO_OBJECT(&pVorbis->push);\n\n                /* Seek to the start of the file. */\n                result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start);\n                if (result != MA_SUCCESS) {\n                    return result;\n                }\n\n                result = ma_stbvorbis_init_internal_decoder_push(pVorbis);\n                if (result != MA_SUCCESS) {\n                    return result;\n                }\n\n                /* At this point we should be sitting on the first frame. */\n                pVorbis->cursor = 0;\n            }\n\n            /* We're just brute-forcing this for now. */\n            while (pVorbis->cursor < frameIndex) {\n                ma_uint64 framesRead;\n                ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels;\n                if (framesToRead > (frameIndex - pVorbis->cursor)) {\n                    framesToRead = (frameIndex - pVorbis->cursor);\n                }\n\n                result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead);\n                if (result != MA_SUCCESS) {\n                    return result;\n                }\n            }\n        } else {\n            /* Pull mode. This is the simple case. */\n            int vorbisResult;\n\n            if (frameIndex > UINT_MAX) {\n                return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */\n            }\n\n            vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex);  /* Safe cast. */\n            if (vorbisResult == 0) {\n                return MA_ERROR;    /* See failed. */\n            }\n\n            pVorbis->cursor = frameIndex;\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* vorbis is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n\n        (void)frameIndex;\n\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    /* Defaults for safety. */\n    if (pFormat != NULL) {\n        *pFormat = ma_format_unknown;\n    }\n    if (pChannels != NULL) {\n        *pChannels = 0;\n    }\n    if (pSampleRate != NULL) {\n        *pSampleRate = 0;\n    }\n    if (pChannelMap != NULL) {\n        MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);\n    }\n\n    if (pVorbis == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    if (pFormat != NULL) {\n        *pFormat = pVorbis->format;\n    }\n\n    #if !defined(MA_NO_VORBIS)\n    {\n        if (pChannels != NULL) {\n            *pChannels = pVorbis->channels;\n        }\n\n        if (pSampleRate != NULL) {\n            *pSampleRate = pVorbis->sampleRate;\n        }\n\n        if (pChannelMap != NULL) {\n            ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels);\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* vorbis is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor)\n{\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;   /* Safety. */\n\n    if (pVorbis == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_VORBIS)\n    {\n        *pCursor = pVorbis->cursor;\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* vorbis is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\nMA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength)\n{\n    if (pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pLength = 0;   /* Safety. */\n\n    if (pVorbis == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_VORBIS)\n    {\n        if (pVorbis->usingPushMode) {\n            *pLength = 0;   /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */\n        } else {\n            *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb);\n        }\n\n        return MA_SUCCESS;\n    }\n    #else\n    {\n        /* vorbis is disabled. Should never hit this since initialization would have failed. */\n        MA_ASSERT(MA_FALSE);\n        return MA_NOT_IMPLEMENTED;\n    }\n    #endif\n}\n\n\nstatic ma_result ma_decoding_backend_init__stbvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_stbvorbis* pVorbis;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);\n    if (pVorbis == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);\n    if (result != MA_SUCCESS) {\n        ma_free(pVorbis, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pVorbis;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoding_backend_init_file__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_stbvorbis* pVorbis;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);\n    if (pVorbis == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);\n    if (result != MA_SUCCESS) {\n        ma_free(pVorbis, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pVorbis;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoding_backend_init_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)\n{\n    ma_result result;\n    ma_stbvorbis* pVorbis;\n\n    (void)pUserData;    /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */\n\n    /* For now we're just allocating the decoder backend on the heap. */\n    pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);\n    if (pVorbis == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis);\n    if (result != MA_SUCCESS) {\n        ma_free(pVorbis, pAllocationCallbacks);\n        return result;\n    }\n\n    *ppBackend = pVorbis;\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend;\n\n    (void)pUserData;\n\n    ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks);\n    ma_free(pVorbis, pAllocationCallbacks);\n}\n\nstatic ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis =\n{\n    ma_decoding_backend_init__stbvorbis,\n    ma_decoding_backend_init_file__stbvorbis,\n    NULL, /* onInitFileW() */\n    ma_decoding_backend_init_memory__stbvorbis,\n    ma_decoding_backend_uninit__stbvorbis\n};\n\nstatic ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder);\n}\n\nstatic ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder);\n}\n#endif  /* STB_VORBIS_INCLUDE_STB_VORBIS_H */\n\n\n\nstatic ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    MA_ASSERT(pDecoder != NULL);\n\n    if (pConfig != NULL) {\n        return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks);\n    } else {\n        pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default();\n        return MA_SUCCESS;\n    }\n}\n\nstatic ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead);\n}\n\nstatic ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex);\n}\n\nstatic ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n}\n\nstatic ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)\n{\n    return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor);\n}\n\nstatic ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)\n{\n    return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength);\n}\n\nstatic ma_data_source_vtable g_ma_decoder_data_source_vtable =\n{\n    ma_decoder__data_source_on_read,\n    ma_decoder__data_source_on_seek,\n    ma_decoder__data_source_on_get_data_format,\n    ma_decoder__data_source_on_get_cursor,\n    ma_decoder__data_source_on_get_length,\n    NULL,   /* onSetLooping */\n    0\n};\n\nstatic ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_data_source_config dataSourceConfig;\n\n    MA_ASSERT(pConfig != NULL);\n\n    if (pDecoder == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pDecoder);\n\n    dataSourceConfig = ma_data_source_config_init();\n    dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable;\n\n    result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pDecoder->onRead    = onRead;\n    pDecoder->onSeek    = onSeek;\n    pDecoder->onTell    = onTell;\n    pDecoder->pUserData = pUserData;\n\n    result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);\n    if (result != MA_SUCCESS) {\n        ma_data_source_uninit(&pDecoder->ds);\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n\n    result = ma_decoder__init_data_converter(pDecoder, pConfig);\n\n    /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */\n    if (result != MA_SUCCESS) {\n        ma_decoder_uninit(pDecoder);\n        return result;\n    }\n\n    return result;\n}\n\n\nstatic ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result = MA_NO_BACKEND;\n\n    MA_ASSERT(pConfig != NULL);\n    MA_ASSERT(pDecoder != NULL);\n\n    /* Silence some warnings in the case that we don't have any decoder backends enabled. */\n    (void)onRead;\n    (void)onSeek;\n    (void)pUserData;\n\n\n    /* If we've specified a specific encoding type, try that first. */\n    if (pConfig->encodingFormat != ma_encoding_format_unknown) {\n    #ifdef MA_HAS_WAV\n        if (pConfig->encodingFormat == ma_encoding_format_wav) {\n            result = ma_decoder_init_wav__internal(pConfig, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_FLAC\n        if (pConfig->encodingFormat == ma_encoding_format_flac) {\n            result = ma_decoder_init_flac__internal(pConfig, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_MP3\n        if (pConfig->encodingFormat == ma_encoding_format_mp3) {\n            result = ma_decoder_init_mp3__internal(pConfig, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_VORBIS\n        if (pConfig->encodingFormat == ma_encoding_format_vorbis) {\n            result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);\n        }\n    #endif\n\n        /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */\n        if (result != MA_SUCCESS) {\n            onSeek(pDecoder, 0, ma_seek_origin_start);\n        }\n    }\n\n    if (result != MA_SUCCESS) {\n        /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */\n\n        /*\n        We use trial and error to open a decoder. We prioritize custom decoders so that if they\n        implement the same encoding format they take priority over the built-in decoders.\n        */\n        if (result != MA_SUCCESS) {\n            result = ma_decoder_init_custom__internal(pConfig, pDecoder);\n            if (result != MA_SUCCESS) {\n                onSeek(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n\n        /*\n        If we get to this point and we still haven't found a decoder, and the caller has requested a\n        specific encoding format, there's no hope for it. Abort.\n        */\n        if (pConfig->encodingFormat != ma_encoding_format_unknown) {\n            return MA_NO_BACKEND;\n        }\n\n    #ifdef MA_HAS_WAV\n        if (result != MA_SUCCESS) {\n            result = ma_decoder_init_wav__internal(pConfig, pDecoder);\n            if (result != MA_SUCCESS) {\n                onSeek(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n    #endif\n    #ifdef MA_HAS_FLAC\n        if (result != MA_SUCCESS) {\n            result = ma_decoder_init_flac__internal(pConfig, pDecoder);\n            if (result != MA_SUCCESS) {\n                onSeek(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n    #endif\n    #ifdef MA_HAS_MP3\n        if (result != MA_SUCCESS) {\n            result = ma_decoder_init_mp3__internal(pConfig, pDecoder);\n            if (result != MA_SUCCESS) {\n                onSeek(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n    #endif\n    #ifdef MA_HAS_VORBIS\n        if (result != MA_SUCCESS) {\n            result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);\n            if (result != MA_SUCCESS) {\n                onSeek(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n    #endif\n    }\n\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return ma_decoder__postinit(pConfig, pDecoder);\n}\n\nMA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_decoder_config config;\n    ma_result result;\n\n    config = ma_decoder_config_init_copy(pConfig);\n\n    result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);\n}\n\n\nstatic ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)\n{\n    size_t bytesRemaining;\n\n    MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos);\n\n    if (pBytesRead != NULL) {\n        *pBytesRead = 0;\n    }\n\n    bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos;\n    if (bytesToRead > bytesRemaining) {\n        bytesToRead = bytesRemaining;\n    }\n\n    if (bytesRemaining == 0) {\n        return MA_AT_END;\n    }\n\n    if (bytesToRead > 0) {\n        MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead);\n        pDecoder->data.memory.currentReadPos += bytesToRead;\n    }\n\n    if (pBytesRead != NULL) {\n        *pBytesRead = bytesToRead;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)\n{\n    if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) {\n        return MA_BAD_SEEK;\n    }\n\n    if (origin == ma_seek_origin_current) {\n        if (byteOffset > 0) {\n            if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) {\n                byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos);  /* Trying to seek too far forward. */\n            }\n\n            pDecoder->data.memory.currentReadPos += (size_t)byteOffset;\n        } else {\n            if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) {\n                byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos;  /* Trying to seek too far backwards. */\n            }\n\n            pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset;\n        }\n    } else {\n        if (origin == ma_seek_origin_end) {\n            if (byteOffset < 0) {\n                byteOffset = -byteOffset;\n            }\n\n            if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) {\n                pDecoder->data.memory.currentReadPos = 0;   /* Trying to seek too far back. */\n            } else {\n                pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset;\n            }\n        } else {\n            if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) {\n                pDecoder->data.memory.currentReadPos = (size_t)byteOffset;\n            } else {\n                pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize;  /* Trying to seek too far forward. */\n            }\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor)\n{\n    MA_ASSERT(pDecoder != NULL);\n    MA_ASSERT(pCursor  != NULL);\n\n    *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pData == NULL || dataSize == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pDecoder->data.memory.pData = (const ma_uint8*)pData;\n    pDecoder->data.memory.dataSize = dataSize;\n    pDecoder->data.memory.currentReadPos = 0;\n\n    (void)pConfig;\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_decoder_config config;\n\n    config = ma_decoder_config_init_copy(pConfig);\n\n    result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pData == NULL || dataSize == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */\n    result = MA_NO_BACKEND;\n\n    if (config.encodingFormat != ma_encoding_format_unknown) {\n    #ifdef MA_HAS_WAV\n        if (config.encodingFormat == ma_encoding_format_wav) {\n            result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_FLAC\n        if (config.encodingFormat == ma_encoding_format_flac) {\n            result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_MP3\n        if (config.encodingFormat == ma_encoding_format_mp3) {\n            result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_VORBIS\n        if (config.encodingFormat == ma_encoding_format_vorbis) {\n            result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder);\n        }\n    #endif\n    }\n\n    if (result != MA_SUCCESS) {\n        /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */\n\n        /*\n        We use trial and error to open a decoder. We prioritize custom decoders so that if they\n        implement the same encoding format they take priority over the built-in decoders.\n        */\n        result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder);\n\n        /*\n        If we get to this point and we still haven't found a decoder, and the caller has requested a\n        specific encoding format, there's no hope for it. Abort.\n        */\n        if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {\n            return MA_NO_BACKEND;\n        }\n\n        /* Use trial and error for stock decoders. */\n        if (result != MA_SUCCESS) {\n        #ifdef MA_HAS_WAV\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder);\n            }\n        #endif\n        #ifdef MA_HAS_FLAC\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder);\n            }\n        #endif\n        #ifdef MA_HAS_MP3\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder);\n            }\n        #endif\n        #ifdef MA_HAS_VORBIS\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder);\n            }\n        #endif\n        }\n    }\n\n    /*\n    If at this point we still haven't successfully initialized the decoder it most likely means\n    the backend doesn't have an implementation for loading from a file path. We'll try using\n    miniaudio's built-in file IO for loading file.\n    */\n    if (result == MA_SUCCESS) {\n        /* Initialization was successful. Finish up. */\n        result = ma_decoder__postinit(&config, pDecoder);\n        if (result != MA_SUCCESS) {\n            /*\n            The backend was initialized successfully, but for some reason post-initialization failed. This is most likely\n            due to an out of memory error. We're going to abort with an error here and not try to recover.\n            */\n            if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {\n                pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks);\n            }\n\n            return result;\n        }\n    } else {\n        /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */\n        result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n\n#if defined(MA_HAS_WAV)    || \\\n    defined(MA_HAS_MP3)    || \\\n    defined(MA_HAS_FLAC)   || \\\n    defined(MA_HAS_VORBIS) || \\\n    defined(MA_HAS_OPUS)\n#define MA_HAS_PATH_API\n#endif\n\n#if defined(MA_HAS_PATH_API)\nstatic const char* ma_path_file_name(const char* path)\n{\n    const char* fileName;\n\n    if (path == NULL) {\n        return NULL;\n    }\n\n    fileName = path;\n\n    /* We just loop through the path until we find the last slash. */\n    while (path[0] != '\\0') {\n        if (path[0] == '/' || path[0] == '\\\\') {\n            fileName = path;\n        }\n\n        path += 1;\n    }\n\n    /* At this point the file name is sitting on a slash, so just move forward. */\n    while (fileName[0] != '\\0' && (fileName[0] == '/' || fileName[0] == '\\\\')) {\n        fileName += 1;\n    }\n\n    return fileName;\n}\n\nstatic const wchar_t* ma_path_file_name_w(const wchar_t* path)\n{\n    const wchar_t* fileName;\n\n    if (path == NULL) {\n        return NULL;\n    }\n\n    fileName = path;\n\n    /* We just loop through the path until we find the last slash. */\n    while (path[0] != '\\0') {\n        if (path[0] == '/' || path[0] == '\\\\') {\n            fileName = path;\n        }\n\n        path += 1;\n    }\n\n    /* At this point the file name is sitting on a slash, so just move forward. */\n    while (fileName[0] != '\\0' && (fileName[0] == '/' || fileName[0] == '\\\\')) {\n        fileName += 1;\n    }\n\n    return fileName;\n}\n\n\nstatic const char* ma_path_extension(const char* path)\n{\n    const char* extension;\n    const char* lastOccurance;\n\n    if (path == NULL) {\n        path = \"\";\n    }\n\n    extension = ma_path_file_name(path);\n    lastOccurance = NULL;\n\n    /* Just find the last '.' and return. */\n    while (extension[0] != '\\0') {\n        if (extension[0] == '.') {\n            extension += 1;\n            lastOccurance = extension;\n        }\n\n        extension += 1;\n    }\n\n    return (lastOccurance != NULL) ? lastOccurance : extension;\n}\n\nstatic const wchar_t* ma_path_extension_w(const wchar_t* path)\n{\n    const wchar_t* extension;\n    const wchar_t* lastOccurance;\n\n    if (path == NULL) {\n        path = L\"\";\n    }\n\n    extension = ma_path_file_name_w(path);\n    lastOccurance = NULL;\n\n    /* Just find the last '.' and return. */\n    while (extension[0] != '\\0') {\n        if (extension[0] == '.') {\n            extension += 1;\n            lastOccurance = extension;\n        }\n\n        extension += 1;\n    }\n\n    return (lastOccurance != NULL) ? lastOccurance : extension;\n}\n\n\nstatic ma_bool32 ma_path_extension_equal(const char* path, const char* extension)\n{\n    const char* ext1;\n    const char* ext2;\n\n    if (path == NULL || extension == NULL) {\n        return MA_FALSE;\n    }\n\n    ext1 = extension;\n    ext2 = ma_path_extension(path);\n\n#if defined(_MSC_VER) || defined(__DMC__)\n    return _stricmp(ext1, ext2) == 0;\n#else\n    return strcasecmp(ext1, ext2) == 0;\n#endif\n}\n\nstatic ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension)\n{\n    const wchar_t* ext1;\n    const wchar_t* ext2;\n\n    if (path == NULL || extension == NULL) {\n        return MA_FALSE;\n    }\n\n    ext1 = extension;\n    ext2 = ma_path_extension_w(path);\n\n#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)\n    return _wcsicmp(ext1, ext2) == 0;\n#else\n    /*\n    I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This\n    isn't the most efficient way to do it, but it should work OK.\n    */\n    {\n        char ext1MB[4096];\n        char ext2MB[4096];\n        const wchar_t* pext1 = ext1;\n        const wchar_t* pext2 = ext2;\n        mbstate_t mbs1;\n        mbstate_t mbs2;\n\n        MA_ZERO_OBJECT(&mbs1);\n        MA_ZERO_OBJECT(&mbs2);\n\n        if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) {\n            return MA_FALSE;\n        }\n        if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) {\n            return MA_FALSE;\n        }\n\n        return strcasecmp(ext1MB, ext2MB) == 0;\n    }\n#endif\n}\n#endif  /* MA_HAS_PATH_API */\n\n\n\nstatic ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)\n{\n    MA_ASSERT(pDecoder   != NULL);\n    MA_ASSERT(pBufferOut != NULL);\n\n    return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead);\n}\n\nstatic ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin)\n{\n    MA_ASSERT(pDecoder != NULL);\n\n    return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin);\n}\n\nstatic ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor)\n{\n    MA_ASSERT(pDecoder != NULL);\n\n    return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor);\n}\n\nstatic ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_vfs_file file;\n\n    result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pFilePath == NULL || pFilePath[0] == '\\0') {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pDecoder->data.vfs.pVFS = pVFS;\n    pDecoder->data.vfs.file = file;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_decoder_config config;\n\n    config = ma_decoder_config_init_copy(pConfig);\n    result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    result = MA_NO_BACKEND;\n\n    if (config.encodingFormat != ma_encoding_format_unknown) {\n    #ifdef MA_HAS_WAV\n        if (config.encodingFormat == ma_encoding_format_wav) {\n            result = ma_decoder_init_wav__internal(&config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_FLAC\n        if (config.encodingFormat == ma_encoding_format_flac) {\n            result = ma_decoder_init_flac__internal(&config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_MP3\n        if (config.encodingFormat == ma_encoding_format_mp3) {\n            result = ma_decoder_init_mp3__internal(&config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_VORBIS\n        if (config.encodingFormat == ma_encoding_format_vorbis) {\n            result = ma_decoder_init_vorbis__internal(&config, pDecoder);\n        }\n    #endif\n\n        /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */\n        if (result != MA_SUCCESS) {\n            ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);\n        }\n    }\n\n    if (result != MA_SUCCESS) {\n        /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */\n\n        /*\n        We use trial and error to open a decoder. We prioritize custom decoders so that if they\n        implement the same encoding format they take priority over the built-in decoders.\n        */\n        if (result != MA_SUCCESS) {\n            result = ma_decoder_init_custom__internal(&config, pDecoder);\n            if (result != MA_SUCCESS) {\n                ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n\n        /*\n        If we get to this point and we still haven't found a decoder, and the caller has requested a\n        specific encoding format, there's no hope for it. Abort.\n        */\n        if (config.encodingFormat != ma_encoding_format_unknown) {\n            return MA_NO_BACKEND;\n        }\n\n    #ifdef MA_HAS_WAV\n        if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, \"wav\")) {\n            result = ma_decoder_init_wav__internal(&config, pDecoder);\n            if (result != MA_SUCCESS) {\n                ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n    #endif\n    #ifdef MA_HAS_FLAC\n        if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, \"flac\")) {\n            result = ma_decoder_init_flac__internal(&config, pDecoder);\n            if (result != MA_SUCCESS) {\n                ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n    #endif\n    #ifdef MA_HAS_MP3\n        if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, \"mp3\")) {\n            result = ma_decoder_init_mp3__internal(&config, pDecoder);\n            if (result != MA_SUCCESS) {\n                ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n    #endif\n    }\n\n    /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */\n    if (result != MA_SUCCESS) {\n        result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);\n    } else {\n        result = ma_decoder__postinit(&config, pDecoder);\n    }\n\n    if (result != MA_SUCCESS) {\n        if (pDecoder->data.vfs.file != NULL) {   /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */\n            ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);\n        }\n\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_vfs_file file;\n\n    result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pFilePath == NULL || pFilePath[0] == '\\0') {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pDecoder->data.vfs.pVFS = pVFS;\n    pDecoder->data.vfs.file = file;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_decoder_config config;\n\n    config = ma_decoder_config_init_copy(pConfig);\n    result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    result = MA_NO_BACKEND;\n\n    if (config.encodingFormat != ma_encoding_format_unknown) {\n    #ifdef MA_HAS_WAV\n        if (config.encodingFormat == ma_encoding_format_wav) {\n            result = ma_decoder_init_wav__internal(&config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_FLAC\n        if (config.encodingFormat == ma_encoding_format_flac) {\n            result = ma_decoder_init_flac__internal(&config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_MP3\n        if (config.encodingFormat == ma_encoding_format_mp3) {\n            result = ma_decoder_init_mp3__internal(&config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_VORBIS\n        if (config.encodingFormat == ma_encoding_format_vorbis) {\n            result = ma_decoder_init_vorbis__internal(&config, pDecoder);\n        }\n    #endif\n\n        /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */\n        if (result != MA_SUCCESS) {\n            ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);\n        }\n    }\n\n    if (result != MA_SUCCESS) {\n        /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */\n\n        /*\n        We use trial and error to open a decoder. We prioritize custom decoders so that if they\n        implement the same encoding format they take priority over the built-in decoders.\n        */\n        if (result != MA_SUCCESS) {\n            result = ma_decoder_init_custom__internal(&config, pDecoder);\n            if (result != MA_SUCCESS) {\n                ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n\n        /*\n        If we get to this point and we still haven't found a decoder, and the caller has requested a\n        specific encoding format, there's no hope for it. Abort.\n        */\n        if (config.encodingFormat != ma_encoding_format_unknown) {\n            return MA_NO_BACKEND;\n        }\n\n    #ifdef MA_HAS_WAV\n        if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L\"wav\")) {\n            result = ma_decoder_init_wav__internal(&config, pDecoder);\n            if (result != MA_SUCCESS) {\n                ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n    #endif\n    #ifdef MA_HAS_FLAC\n        if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L\"flac\")) {\n            result = ma_decoder_init_flac__internal(&config, pDecoder);\n            if (result != MA_SUCCESS) {\n                ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n    #endif\n    #ifdef MA_HAS_MP3\n        if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L\"mp3\")) {\n            result = ma_decoder_init_mp3__internal(&config, pDecoder);\n            if (result != MA_SUCCESS) {\n                ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);\n            }\n        }\n    #endif\n    }\n\n    /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */\n    if (result != MA_SUCCESS) {\n        result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);\n    } else {\n        result = ma_decoder__postinit(&config, pDecoder);\n    }\n\n    if (result != MA_SUCCESS) {\n        ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n\n    result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pFilePath == NULL || pFilePath[0] == '\\0') {\n        return MA_INVALID_ARGS;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_decoder_config config;\n\n    config = ma_decoder_config_init_copy(pConfig);\n    result = ma_decoder__preinit_file(pFilePath, &config, pDecoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */\n    result = MA_NO_BACKEND;\n\n    if (config.encodingFormat != ma_encoding_format_unknown) {\n    #ifdef MA_HAS_WAV\n        if (config.encodingFormat == ma_encoding_format_wav) {\n            result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_FLAC\n        if (config.encodingFormat == ma_encoding_format_flac) {\n            result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_MP3\n        if (config.encodingFormat == ma_encoding_format_mp3) {\n            result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_VORBIS\n        if (config.encodingFormat == ma_encoding_format_vorbis) {\n            result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    }\n\n    if (result != MA_SUCCESS) {\n        /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */\n\n        /*\n        We use trial and error to open a decoder. We prioritize custom decoders so that if they\n        implement the same encoding format they take priority over the built-in decoders.\n        */\n        result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder);\n\n        /*\n        If we get to this point and we still haven't found a decoder, and the caller has requested a\n        specific encoding format, there's no hope for it. Abort.\n        */\n        if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {\n            return MA_NO_BACKEND;\n        }\n\n        /* First try loading based on the file extension so we don't waste time opening and closing files. */\n    #ifdef MA_HAS_WAV\n        if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, \"wav\")) {\n            result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_FLAC\n        if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, \"flac\")) {\n            result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_MP3\n        if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, \"mp3\")) {\n            result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_VORBIS\n        if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, \"ogg\")) {\n            result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n\n        /*\n        If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we\n        need only iterate over our stock decoders.\n        */\n        if (result != MA_SUCCESS) {\n        #ifdef MA_HAS_WAV\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);\n            }\n        #endif\n        #ifdef MA_HAS_FLAC\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);\n            }\n        #endif\n        #ifdef MA_HAS_MP3\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);\n            }\n        #endif\n        #ifdef MA_HAS_VORBIS\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);\n            }\n        #endif\n        }\n    }\n\n    /*\n    If at this point we still haven't successfully initialized the decoder it most likely means\n    the backend doesn't have an implementation for loading from a file path. We'll try using\n    miniaudio's built-in file IO for loading file.\n    */\n    if (result == MA_SUCCESS) {\n        /* Initialization was successful. Finish up. */\n        result = ma_decoder__postinit(&config, pDecoder);\n        if (result != MA_SUCCESS) {\n            /*\n            The backend was initialized successfully, but for some reason post-initialization failed. This is most likely\n            due to an out of memory error. We're going to abort with an error here and not try to recover.\n            */\n            if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {\n                pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks);\n            }\n\n            return result;\n        }\n    } else {\n        /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */\n        result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n\n    result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pFilePath == NULL || pFilePath[0] == '\\0') {\n        return MA_INVALID_ARGS;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_decoder_config config;\n\n    config = ma_decoder_config_init_copy(pConfig);\n    result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */\n    result = MA_NO_BACKEND;\n\n    if (config.encodingFormat != ma_encoding_format_unknown) {\n    #ifdef MA_HAS_WAV\n        if (config.encodingFormat == ma_encoding_format_wav) {\n            result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_FLAC\n        if (config.encodingFormat == ma_encoding_format_flac) {\n            result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_MP3\n        if (config.encodingFormat == ma_encoding_format_mp3) {\n            result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_VORBIS\n        if (config.encodingFormat == ma_encoding_format_vorbis) {\n            result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    }\n\n    if (result != MA_SUCCESS) {\n        /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */\n\n        /*\n        We use trial and error to open a decoder. We prioritize custom decoders so that if they\n        implement the same encoding format they take priority over the built-in decoders.\n        */\n        result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder);\n\n        /*\n        If we get to this point and we still haven't found a decoder, and the caller has requested a\n        specific encoding format, there's no hope for it. Abort.\n        */\n        if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {\n            return MA_NO_BACKEND;\n        }\n\n        /* First try loading based on the file extension so we don't waste time opening and closing files. */\n    #ifdef MA_HAS_WAV\n        if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L\"wav\")) {\n            result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_FLAC\n        if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L\"flac\")) {\n            result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_MP3\n        if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L\"mp3\")) {\n            result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n    #ifdef MA_HAS_VORBIS\n        if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L\"ogg\")) {\n            result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);\n        }\n    #endif\n\n        /*\n        If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we\n        need only iterate over our stock decoders.\n        */\n        if (result != MA_SUCCESS) {\n        #ifdef MA_HAS_WAV\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);\n            }\n        #endif\n        #ifdef MA_HAS_FLAC\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);\n            }\n        #endif\n        #ifdef MA_HAS_MP3\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);\n            }\n        #endif\n        #ifdef MA_HAS_VORBIS\n            if (result != MA_SUCCESS) {\n                result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);\n            }\n        #endif\n        }\n    }\n\n    /*\n    If at this point we still haven't successfully initialized the decoder it most likely means\n    the backend doesn't have an implementation for loading from a file path. We'll try using\n    miniaudio's built-in file IO for loading file.\n    */\n    if (result == MA_SUCCESS) {\n        /* Initialization was successful. Finish up. */\n        result = ma_decoder__postinit(&config, pDecoder);\n        if (result != MA_SUCCESS) {\n            /*\n            The backend was initialized successfully, but for some reason post-initialization failed. This is most likely\n            due to an out of memory error. We're going to abort with an error here and not try to recover.\n            */\n            if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {\n                pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks);\n            }\n\n            return result;\n        }\n    } else {\n        /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */\n        result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder)\n{\n    if (pDecoder == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pDecoder->pBackend != NULL) {\n        if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {\n            pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks);\n        }\n    }\n\n    if (pDecoder->onRead == ma_decoder__on_read_vfs) {\n        ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file);\n        pDecoder->data.vfs.file = NULL;\n    }\n\n    ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);\n    ma_data_source_uninit(&pDecoder->ds);\n\n    if (pDecoder->pInputCache != NULL) {\n        ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint64 totalFramesReadOut;\n    void* pRunningFramesOut;\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;   /* Safety. */\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pDecoder == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pDecoder->pBackend == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* Fast path. */\n    if (pDecoder->converter.isPassthrough) {\n        result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut);\n    } else {\n        /*\n        Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we\n        need to run through each sample because we need to ensure it's internal cache is updated.\n        */\n        if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {\n            result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut);\n        } else {\n            /* Slow path. Need to run everything through the data converter. */\n            ma_format internalFormat;\n            ma_uint32 internalChannels;\n\n            totalFramesReadOut = 0;\n            pRunningFramesOut  = pFramesOut;\n\n            result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0);\n            if (result != MA_SUCCESS) {\n                return result;   /* Failed to retrieve the internal format and channel count. */\n            }\n\n            /*\n            We run a different path depending on whether or not we are using a heap-allocated\n            intermediary buffer or not. If the data converter does not support the calculation of\n            the required number of input frames, we'll use the heap-allocated path. Otherwise we'll\n            use the stack-allocated path.\n            */\n            if (pDecoder->pInputCache != NULL) {\n                /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */\n                while (totalFramesReadOut < frameCount) {\n                    ma_uint64 framesToReadThisIterationIn;\n                    ma_uint64 framesToReadThisIterationOut;\n\n                    /* If there's any data available in the cache, that needs to get processed first. */\n                    if (pDecoder->inputCacheRemaining > 0) {\n                        framesToReadThisIterationOut = (frameCount - totalFramesReadOut);\n                        framesToReadThisIterationIn  = framesToReadThisIterationOut;\n                        if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) {\n                            framesToReadThisIterationIn = pDecoder->inputCacheRemaining;\n                        }\n\n                        result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut);\n                        if (result != MA_SUCCESS) {\n                            break;\n                        }\n\n                        pDecoder->inputCacheConsumed  += framesToReadThisIterationIn;\n                        pDecoder->inputCacheRemaining -= framesToReadThisIterationIn;\n\n                        totalFramesReadOut += framesToReadThisIterationOut;\n\n                        if (pRunningFramesOut != NULL) {\n                            pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));\n                        }\n\n                        if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {\n                            break;  /* We're done. */\n                        }\n                    }\n\n                    /* Getting here means there's no data in the cache and we need to fill it up from the data source. */\n                    if (pDecoder->inputCacheRemaining == 0) {\n                        pDecoder->inputCacheConsumed = 0;\n\n                        result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining);\n                        if (result != MA_SUCCESS) {\n                            break;\n                        }\n                    }\n                }\n            } else {\n                /* We have a way of determining the required number of input frames so just use the stack. */\n                while (totalFramesReadOut < frameCount) {\n                    ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];  /* In internal format. */\n                    ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels);\n                    ma_uint64 framesToReadThisIterationIn;\n                    ma_uint64 framesReadThisIterationIn;\n                    ma_uint64 framesToReadThisIterationOut;\n                    ma_uint64 framesReadThisIterationOut;\n                    ma_uint64 requiredInputFrameCount;\n\n                    framesToReadThisIterationOut = (frameCount - totalFramesReadOut);\n                    framesToReadThisIterationIn = framesToReadThisIterationOut;\n                    if (framesToReadThisIterationIn > intermediaryBufferCap) {\n                        framesToReadThisIterationIn = intermediaryBufferCap;\n                    }\n\n                    ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount);\n                    if (framesToReadThisIterationIn > requiredInputFrameCount) {\n                        framesToReadThisIterationIn = requiredInputFrameCount;\n                    }\n\n                    if (requiredInputFrameCount > 0) {\n                        result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn);\n                    } else {\n                        framesReadThisIterationIn = 0;\n                    }\n\n                    /*\n                    At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any\n                    input frames, we still want to try processing frames because there may some output frames generated from cached input data.\n                    */\n                    framesReadThisIterationOut = framesToReadThisIterationOut;\n                    result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);\n                    if (result != MA_SUCCESS) {\n                        break;\n                    }\n\n                    totalFramesReadOut += framesReadThisIterationOut;\n\n                    if (pRunningFramesOut != NULL) {\n                        pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));\n                    }\n\n                    if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {\n                        break;  /* We're done. */\n                    }\n                }\n            }\n        }\n    }\n\n    pDecoder->readPointerInPCMFrames += totalFramesReadOut;\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = totalFramesReadOut;\n    }\n\n    if (result == MA_SUCCESS && totalFramesReadOut == 0) {\n        result =  MA_AT_END;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex)\n{\n    if (pDecoder == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pDecoder->pBackend != NULL) {\n        ma_result result;\n        ma_uint64 internalFrameIndex;\n        ma_uint32 internalSampleRate;\n        ma_uint64 currentFrameIndex;\n\n        result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);\n        if (result != MA_SUCCESS) {\n            return result;  /* Failed to retrieve the internal sample rate. */\n        }\n\n        if (internalSampleRate == pDecoder->outputSampleRate) {\n            internalFrameIndex = frameIndex;\n        } else {\n            internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex);\n        }\n\n        /* Only seek if we're requesting a different frame to what we're currently sitting on. */\n        ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, &currentFrameIndex);\n        if (currentFrameIndex != internalFrameIndex) {\n            result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex);\n            if (result == MA_SUCCESS) {\n                pDecoder->readPointerInPCMFrames = frameIndex;\n            }\n\n            /* Reset the data converter so that any cached data in the resampler is cleared. */\n            ma_data_converter_reset(&pDecoder->converter);\n        }\n\n        return result;\n    }\n\n    /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */\n    return MA_INVALID_ARGS;\n}\n\nMA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    if (pDecoder == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pFormat != NULL) {\n        *pFormat = pDecoder->outputFormat;\n    }\n\n    if (pChannels != NULL) {\n        *pChannels = pDecoder->outputChannels;\n    }\n\n    if (pSampleRate != NULL) {\n        *pSampleRate = pDecoder->outputSampleRate;\n    }\n\n    if (pChannelMap != NULL) {\n        ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor)\n{\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;\n\n    if (pDecoder == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = pDecoder->readPointerInPCMFrames;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength)\n{\n    if (pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pLength = 0;\n\n    if (pDecoder == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pDecoder->pBackend != NULL) {\n        ma_result result;\n        ma_uint64 internalLengthInPCMFrames;\n        ma_uint32 internalSampleRate;\n\n        result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames);\n        if (result != MA_SUCCESS) {\n            return result;  /* Failed to retrieve the internal length. */\n        }\n\n        result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);\n        if (result != MA_SUCCESS) {\n            return result;   /* Failed to retrieve the internal sample rate. */\n        }\n\n        if (internalSampleRate == pDecoder->outputSampleRate) {\n            *pLength = internalLengthInPCMFrames;\n        } else {\n            *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames);\n        }\n\n        return MA_SUCCESS;\n    } else {\n        return MA_NO_BACKEND;\n    }\n}\n\nMA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames)\n{\n    ma_result result;\n    ma_uint64 totalFrameCount;\n\n    if (pAvailableFrames == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pAvailableFrames = 0;\n\n    if (pDecoder == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (totalFrameCount <= pDecoder->readPointerInPCMFrames) {\n        *pAvailableFrames = 0;\n    } else {\n        *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames;\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)\n{\n    ma_result result;\n    ma_uint64 totalFrameCount;\n    ma_uint64 bpf;\n    ma_uint64 dataCapInFrames;\n    void* pPCMFramesOut;\n\n    MA_ASSERT(pDecoder != NULL);\n\n    totalFrameCount = 0;\n    bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);\n\n    /* The frame count is unknown until we try reading. Thus, we just run in a loop. */\n    dataCapInFrames = 0;\n    pPCMFramesOut = NULL;\n    for (;;) {\n        ma_uint64 frameCountToTryReading;\n        ma_uint64 framesJustRead;\n\n        /* Make room if there's not enough. */\n        if (totalFrameCount == dataCapInFrames) {\n            void* pNewPCMFramesOut;\n            ma_uint64 newDataCapInFrames = dataCapInFrames*2;\n            if (newDataCapInFrames == 0) {\n                newDataCapInFrames = 4096;\n            }\n\n            if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {\n                ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);\n                return MA_TOO_BIG;\n            }\n\n            pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks);\n            if (pNewPCMFramesOut == NULL) {\n                ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);\n                return MA_OUT_OF_MEMORY;\n            }\n\n            dataCapInFrames = newDataCapInFrames;\n            pPCMFramesOut = pNewPCMFramesOut;\n        }\n\n        frameCountToTryReading = dataCapInFrames - totalFrameCount;\n        MA_ASSERT(frameCountToTryReading > 0);\n\n        result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead);\n        totalFrameCount += framesJustRead;\n\n        if (result != MA_SUCCESS) {\n            break;\n        }\n\n        if (framesJustRead < frameCountToTryReading) {\n            break;\n        }\n    }\n\n\n    if (pConfigOut != NULL) {\n        pConfigOut->format     = pDecoder->outputFormat;\n        pConfigOut->channels   = pDecoder->outputChannels;\n        pConfigOut->sampleRate = pDecoder->outputSampleRate;\n    }\n\n    if (ppPCMFramesOut != NULL) {\n        *ppPCMFramesOut = pPCMFramesOut;\n    } else {\n        ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);\n    }\n\n    if (pFrameCountOut != NULL) {\n        *pFrameCountOut = totalFrameCount;\n    }\n\n    ma_decoder_uninit(pDecoder);\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)\n{\n    ma_result result;\n    ma_decoder_config config;\n    ma_decoder decoder;\n\n    if (pFrameCountOut != NULL) {\n        *pFrameCountOut = 0;\n    }\n    if (ppPCMFramesOut != NULL) {\n        *ppPCMFramesOut = NULL;\n    }\n\n    config = ma_decoder_config_init_copy(pConfig);\n\n    result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);\n\n    return result;\n}\n\nMA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)\n{\n    return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut);\n}\n\nMA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)\n{\n    ma_decoder_config config;\n    ma_decoder decoder;\n    ma_result result;\n\n    if (pFrameCountOut != NULL) {\n        *pFrameCountOut = 0;\n    }\n    if (ppPCMFramesOut != NULL) {\n        *ppPCMFramesOut = NULL;\n    }\n\n    if (pData == NULL || dataSize == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    config = ma_decoder_config_init_copy(pConfig);\n\n    result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);\n}\n#endif  /* MA_NO_DECODING */\n\n\n#ifndef MA_NO_ENCODING\n\n#if defined(MA_HAS_WAV)\nstatic size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite)\n{\n    ma_encoder* pEncoder = (ma_encoder*)pUserData;\n    size_t bytesWritten = 0;\n\n    MA_ASSERT(pEncoder != NULL);\n\n    pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten);\n    return bytesWritten;\n}\n\nstatic ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin)\n{\n    ma_encoder* pEncoder = (ma_encoder*)pUserData;\n    ma_result result;\n\n    MA_ASSERT(pEncoder != NULL);\n\n    result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);\n    if (result != MA_SUCCESS) {\n        return MA_FALSE;\n    } else {\n        return MA_TRUE;\n    }\n}\n\nstatic ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder)\n{\n    ma_dr_wav_data_format wavFormat;\n    ma_allocation_callbacks allocationCallbacks;\n    ma_dr_wav* pWav;\n\n    MA_ASSERT(pEncoder != NULL);\n\n    pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks);\n    if (pWav == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    wavFormat.container     = ma_dr_wav_container_riff;\n    wavFormat.channels      = pEncoder->config.channels;\n    wavFormat.sampleRate    = pEncoder->config.sampleRate;\n    wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8;\n    if (pEncoder->config.format == ma_format_f32) {\n        wavFormat.format    = MA_DR_WAVE_FORMAT_IEEE_FLOAT;\n    } else {\n        wavFormat.format    = MA_DR_WAVE_FORMAT_PCM;\n    }\n\n    allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData;\n    allocationCallbacks.onMalloc  = pEncoder->config.allocationCallbacks.onMalloc;\n    allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc;\n    allocationCallbacks.onFree    = pEncoder->config.allocationCallbacks.onFree;\n\n    if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) {\n        return MA_ERROR;\n    }\n\n    pEncoder->pInternalEncoder = pWav;\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_encoder__on_uninit_wav(ma_encoder* pEncoder)\n{\n    ma_dr_wav* pWav;\n\n    MA_ASSERT(pEncoder != NULL);\n\n    pWav = (ma_dr_wav*)pEncoder->pInternalEncoder;\n    MA_ASSERT(pWav != NULL);\n\n    ma_dr_wav_uninit(pWav);\n    ma_free(pWav, &pEncoder->config.allocationCallbacks);\n}\n\nstatic ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)\n{\n    ma_dr_wav* pWav;\n    ma_uint64 framesWritten;\n\n    MA_ASSERT(pEncoder != NULL);\n\n    pWav = (ma_dr_wav*)pEncoder->pInternalEncoder;\n    MA_ASSERT(pWav != NULL);\n\n    framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn);\n\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = framesWritten;\n    }\n\n    return MA_SUCCESS;\n}\n#endif\n\nMA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)\n{\n    ma_encoder_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.encodingFormat = encodingFormat;\n    config.format = format;\n    config.channels = channels;\n    config.sampleRate = sampleRate;\n\n    return config;\n}\n\nMA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder)\n{\n    ma_result result;\n\n    if (pEncoder == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pEncoder);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pEncoder->config = *pConfig;\n\n    result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder)\n{\n    ma_result result = MA_SUCCESS;\n\n    /* This assumes ma_encoder_preinit() has been called prior. */\n    MA_ASSERT(pEncoder != NULL);\n\n    if (onWrite == NULL || onSeek == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pEncoder->onWrite   = onWrite;\n    pEncoder->onSeek    = onSeek;\n    pEncoder->pUserData = pUserData;\n\n    switch (pEncoder->config.encodingFormat)\n    {\n        case ma_encoding_format_wav:\n        {\n        #if defined(MA_HAS_WAV)\n            pEncoder->onInit           = ma_encoder__on_init_wav;\n            pEncoder->onUninit         = ma_encoder__on_uninit_wav;\n            pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav;\n        #else\n            result = MA_NO_BACKEND;\n        #endif\n        } break;\n\n        default:\n        {\n            result = MA_INVALID_ARGS;\n        } break;\n    }\n\n    /* Getting here means we should have our backend callbacks set up. */\n    if (result == MA_SUCCESS) {\n        result = pEncoder->onInit(pEncoder);\n    }\n\n    return result;\n}\n\nstatic ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten)\n{\n    return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten);\n}\n\nstatic ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin)\n{\n    return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin);\n}\n\nMA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)\n{\n    ma_result result;\n    ma_vfs_file file;\n\n    result = ma_encoder_preinit(pConfig, pEncoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* Now open the file. If this fails we don't need to uninitialize the encoder. */\n    result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pEncoder->data.vfs.pVFS = pVFS;\n    pEncoder->data.vfs.file = file;\n\n    result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);\n    if (result != MA_SUCCESS) {\n        ma_vfs_or_default_close(pVFS, file);\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)\n{\n    ma_result result;\n    ma_vfs_file file;\n\n    result = ma_encoder_preinit(pConfig, pEncoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* Now open the file. If this fails we don't need to uninitialize the encoder. */\n    result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pEncoder->data.vfs.pVFS = pVFS;\n    pEncoder->data.vfs.file = file;\n\n    result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);\n    if (result != MA_SUCCESS) {\n        ma_vfs_or_default_close(pVFS, file);\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)\n{\n    return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder);\n}\n\nMA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)\n{\n    return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder);\n}\n\nMA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder)\n{\n    ma_result result;\n\n    result = ma_encoder_preinit(pConfig, pEncoder);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder);\n}\n\n\nMA_API void ma_encoder_uninit(ma_encoder* pEncoder)\n{\n    if (pEncoder == NULL) {\n        return;\n    }\n\n    if (pEncoder->onUninit) {\n        pEncoder->onUninit(pEncoder);\n    }\n\n    /* If we have a file handle, close it. */\n    if (pEncoder->onWrite == ma_encoder__on_write_vfs) {\n        ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file);\n        pEncoder->data.vfs.file = NULL;\n    }\n}\n\n\nMA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)\n{\n    if (pFramesWritten != NULL) {\n        *pFramesWritten = 0;\n    }\n\n    if (pEncoder == NULL || pFramesIn == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten);\n}\n#endif  /* MA_NO_ENCODING */\n\n\n\n/**************************************************************************************************************************************************************\n\nGeneration\n\n**************************************************************************************************************************************************************/\n#ifndef MA_NO_GENERATION\nMA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency)\n{\n    ma_waveform_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format     = format;\n    config.channels   = channels;\n    config.sampleRate = sampleRate;\n    config.type       = type;\n    config.amplitude  = amplitude;\n    config.frequency  = frequency;\n\n    return config;\n}\n\nstatic ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead);\n}\n\nstatic ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex);\n}\n\nstatic ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    ma_waveform* pWaveform = (ma_waveform*)pDataSource;\n\n    *pFormat     = pWaveform->config.format;\n    *pChannels   = pWaveform->config.channels;\n    *pSampleRate = pWaveform->config.sampleRate;\n    ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)\n{\n    ma_waveform* pWaveform = (ma_waveform*)pDataSource;\n\n    *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance);\n\n    return MA_SUCCESS;\n}\n\nstatic double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency)\n{\n    return (1.0 / (sampleRate / frequency));\n}\n\nstatic void ma_waveform__update_advance(ma_waveform* pWaveform)\n{\n    pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);\n}\n\nstatic ma_data_source_vtable g_ma_waveform_data_source_vtable =\n{\n    ma_waveform__data_source_on_read,\n    ma_waveform__data_source_on_seek,\n    ma_waveform__data_source_on_get_data_format,\n    ma_waveform__data_source_on_get_cursor,\n    NULL,   /* onGetLength. There's no notion of a length in waveforms. */\n    NULL,   /* onSetLooping */\n    0\n};\n\nMA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform)\n{\n    ma_result result;\n    ma_data_source_config dataSourceConfig;\n\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pWaveform);\n\n    dataSourceConfig = ma_data_source_config_init();\n    dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable;\n\n    result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pWaveform->config  = *pConfig;\n    pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);\n    pWaveform->time    = 0;\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_waveform_uninit(ma_waveform* pWaveform)\n{\n    if (pWaveform == NULL) {\n        return;\n    }\n\n    ma_data_source_uninit(&pWaveform->ds);\n}\n\nMA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude)\n{\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pWaveform->config.amplitude = amplitude;\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency)\n{\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pWaveform->config.frequency = frequency;\n    ma_waveform__update_advance(pWaveform);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type)\n{\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pWaveform->config.type = type;\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate)\n{\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pWaveform->config.sampleRate = sampleRate;\n    ma_waveform__update_advance(pWaveform);\n\n    return MA_SUCCESS;\n}\n\nstatic float ma_waveform_sine_f32(double time, double amplitude)\n{\n    return (float)(ma_sind(MA_TAU_D * time) * amplitude);\n}\n\nstatic ma_int16 ma_waveform_sine_s16(double time, double amplitude)\n{\n    return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude));\n}\n\nstatic float ma_waveform_square_f32(double time, double dutyCycle, double amplitude)\n{\n    double f = time - (ma_int64)time;\n    double r;\n\n    if (f < dutyCycle) {\n        r =  amplitude;\n    } else {\n        r = -amplitude;\n    }\n\n    return (float)r;\n}\n\nstatic ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude)\n{\n    return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude));\n}\n\nstatic float ma_waveform_triangle_f32(double time, double amplitude)\n{\n    double f = time - (ma_int64)time;\n    double r;\n\n    r = 2 * ma_abs(2 * (f - 0.5)) - 1;\n\n    return (float)(r * amplitude);\n}\n\nstatic ma_int16 ma_waveform_triangle_s16(double time, double amplitude)\n{\n    return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude));\n}\n\nstatic float ma_waveform_sawtooth_f32(double time, double amplitude)\n{\n    double f = time - (ma_int64)time;\n    double r;\n\n    r = 2 * (f - 0.5);\n\n    return (float)(r * amplitude);\n}\n\nstatic ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude)\n{\n    return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude));\n}\n\nstatic void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)\n{\n    ma_uint64 iFrame;\n    ma_uint64 iChannel;\n    ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);\n    ma_uint32 bpf = bps * pWaveform->config.channels;\n\n    MA_ASSERT(pWaveform  != NULL);\n    MA_ASSERT(pFramesOut != NULL);\n\n    if (pWaveform->config.format == ma_format_f32) {\n        float* pFramesOutF32 = (float*)pFramesOut;\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;\n            }\n        }\n    } else if (pWaveform->config.format == ma_format_s16) {\n        ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;\n            }\n        }\n    } else {\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);\n            }\n        }\n    }\n}\n\nstatic void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount)\n{\n    ma_uint64 iFrame;\n    ma_uint64 iChannel;\n    ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);\n    ma_uint32 bpf = bps * pWaveform->config.channels;\n\n    MA_ASSERT(pWaveform  != NULL);\n    MA_ASSERT(pFramesOut != NULL);\n\n    if (pWaveform->config.format == ma_format_f32) {\n        float* pFramesOutF32 = (float*)pFramesOut;\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;\n            }\n        }\n    } else if (pWaveform->config.format == ma_format_s16) {\n        ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;\n            }\n        }\n    } else {\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);\n            }\n        }\n    }\n}\n\nstatic void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)\n{\n    ma_uint64 iFrame;\n    ma_uint64 iChannel;\n    ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);\n    ma_uint32 bpf = bps * pWaveform->config.channels;\n\n    MA_ASSERT(pWaveform  != NULL);\n    MA_ASSERT(pFramesOut != NULL);\n\n    if (pWaveform->config.format == ma_format_f32) {\n        float* pFramesOutF32 = (float*)pFramesOut;\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;\n            }\n        }\n    } else if (pWaveform->config.format == ma_format_s16) {\n        ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;\n            }\n        }\n    } else {\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);\n            }\n        }\n    }\n}\n\nstatic void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)\n{\n    ma_uint64 iFrame;\n    ma_uint64 iChannel;\n    ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);\n    ma_uint32 bpf = bps * pWaveform->config.channels;\n\n    MA_ASSERT(pWaveform  != NULL);\n    MA_ASSERT(pFramesOut != NULL);\n\n    if (pWaveform->config.format == ma_format_f32) {\n        float* pFramesOutF32 = (float*)pFramesOut;\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;\n            }\n        }\n    } else if (pWaveform->config.format == ma_format_s16) {\n        ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;\n            }\n        }\n    } else {\n        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n            float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);\n            pWaveform->time += pWaveform->advance;\n\n            for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {\n                ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);\n            }\n        }\n    }\n}\n\nMA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pFramesOut != NULL) {\n        switch (pWaveform->config.type)\n        {\n            case ma_waveform_type_sine:\n            {\n                ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount);\n            } break;\n\n            case ma_waveform_type_square:\n            {\n                ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount);\n            } break;\n\n            case ma_waveform_type_triangle:\n            {\n                ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount);\n            } break;\n\n            case ma_waveform_type_sawtooth:\n            {\n                ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount);\n            } break;\n\n            default: return MA_INVALID_OPERATION;   /* Unknown waveform type. */\n        }\n    } else {\n        pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = frameCount;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex)\n{\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pWaveform->time = pWaveform->advance * (ma_int64)frameIndex;    /* Casting for VC6. Won't be an issue in practice. */\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency)\n{\n    ma_pulsewave_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.format     = format;\n    config.channels   = channels;\n    config.sampleRate = sampleRate;\n    config.dutyCycle  = dutyCycle;\n    config.amplitude  = amplitude;\n    config.frequency  = frequency;\n\n    return config;\n}\n\nMA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform)\n{\n    ma_result result;\n    ma_waveform_config config;\n\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pWaveform);\n\n    config = ma_waveform_config_init(\n        pConfig->format,\n        pConfig->channels,\n        pConfig->sampleRate,\n        ma_waveform_type_square,\n        pConfig->amplitude,\n        pConfig->frequency\n    );\n\n    result = ma_waveform_init(&config, &pWaveform->waveform);\n    ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle);\n\n    return result;\n}\n\nMA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform)\n{\n    if (pWaveform == NULL) {\n        return;\n    }\n\n    ma_waveform_uninit(&pWaveform->waveform);\n}\n\nMA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pFramesOut != NULL) {\n        ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount);\n    } else {\n        pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = frameCount;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex)\n{\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude)\n{\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pWaveform->config.amplitude = amplitude;\n    ma_waveform_set_amplitude(&pWaveform->waveform, amplitude);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency)\n{\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pWaveform->config.frequency = frequency;\n    ma_waveform_set_frequency(&pWaveform->waveform, frequency);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate)\n{\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pWaveform->config.sampleRate = sampleRate;\n    ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle)\n{\n    if (pWaveform == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pWaveform->config.dutyCycle = dutyCycle;\n\n    return MA_SUCCESS;\n}\n\n\n\nMA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude)\n{\n    ma_noise_config config;\n    MA_ZERO_OBJECT(&config);\n\n    config.format    = format;\n    config.channels  = channels;\n    config.type      = type;\n    config.seed      = seed;\n    config.amplitude = amplitude;\n\n    if (config.seed == 0) {\n        config.seed = MA_DEFAULT_LCG_SEED;\n    }\n\n    return config;\n}\n\n\nstatic ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead);\n}\n\nstatic ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    /* No-op. Just pretend to be successful. */\n    (void)pDataSource;\n    (void)frameIndex;\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    ma_noise* pNoise = (ma_noise*)pDataSource;\n\n    *pFormat     = pNoise->config.format;\n    *pChannels   = pNoise->config.channels;\n    *pSampleRate = 0;   /* There is no notion of sample rate with noise generation. */\n    ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_data_source_vtable g_ma_noise_data_source_vtable =\n{\n    ma_noise__data_source_on_read,\n    ma_noise__data_source_on_seek,  /* No-op for noise. */\n    ma_noise__data_source_on_get_data_format,\n    NULL,   /* onGetCursor. No notion of a cursor for noise. */\n    NULL,   /* onGetLength. No notion of a length for noise. */\n    NULL,   /* onSetLooping */\n    0\n};\n\n\n#ifndef MA_PINK_NOISE_BIN_SIZE\n#define MA_PINK_NOISE_BIN_SIZE 16\n#endif\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    struct\n    {\n        size_t binOffset;\n        size_t accumulationOffset;\n        size_t counterOffset;\n    } pink;\n    struct\n    {\n        size_t accumulationOffset;\n    } brownian;\n} ma_noise_heap_layout;\n\nstatic ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout)\n{\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->channels == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* Pink. */\n    if (pConfig->type == ma_noise_type_pink) {\n        /* bin */\n        pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes;\n        pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels;\n        pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE;\n\n        /* accumulation */\n        pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes;\n        pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;\n\n        /* counter */\n        pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes;\n        pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels;\n    }\n\n    /* Brownian. */\n    if (pConfig->type == ma_noise_type_brownian) {\n        /* accumulation */\n        pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes;\n        pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;\n    }\n\n    /* Make sure allocation size is aligned. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_noise_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_noise_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise)\n{\n    ma_result result;\n    ma_noise_heap_layout heapLayout;\n    ma_data_source_config dataSourceConfig;\n    ma_uint32 iChannel;\n\n    if (pNoise == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pNoise);\n\n    result = ma_noise_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pNoise->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes);\n\n    dataSourceConfig = ma_data_source_config_init();\n    dataSourceConfig.vtable = &g_ma_noise_data_source_vtable;\n\n    result = ma_data_source_init(&dataSourceConfig, &pNoise->ds);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pNoise->config = *pConfig;\n    ma_lcg_seed(&pNoise->lcg, pConfig->seed);\n\n    if (pNoise->config.type == ma_noise_type_pink) {\n        pNoise->state.pink.bin          = (double**  )ma_offset_ptr(pHeap, heapLayout.pink.binOffset);\n        pNoise->state.pink.accumulation = (double*   )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset);\n        pNoise->state.pink.counter      = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset);\n\n        for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {\n            pNoise->state.pink.bin[iChannel]          = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel));\n            pNoise->state.pink.accumulation[iChannel] = 0;\n            pNoise->state.pink.counter[iChannel]      = 1;\n        }\n    }\n\n    if (pNoise->config.type == ma_noise_type_brownian) {\n        pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset);\n\n        for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {\n            pNoise->state.brownian.accumulation[iChannel] = 0;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_noise_init_preallocated(pConfig, pHeap, pNoise);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pNoise->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pNoise == NULL) {\n        return;\n    }\n\n    ma_data_source_uninit(&pNoise->ds);\n\n    if (pNoise->_ownsHeap) {\n        ma_free(pNoise->_pHeap, pAllocationCallbacks);\n    }\n}\n\nMA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude)\n{\n    if (pNoise == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pNoise->config.amplitude = amplitude;\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed)\n{\n    if (pNoise == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pNoise->lcg.state = seed;\n    return MA_SUCCESS;\n}\n\n\nMA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type)\n{\n    if (pNoise == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /*\n    This function should never have been implemented in the first place. Changing the type dynamically is not\n    supported. Instead you need to uninitialize and reinitiailize a fresh `ma_noise` object. This function\n    will be removed in version 0.12.\n    */\n    MA_ASSERT(MA_FALSE);\n    (void)type;\n\n    return MA_INVALID_OPERATION;\n}\n\nstatic MA_INLINE float ma_noise_f32_white(ma_noise* pNoise)\n{\n    return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude);\n}\n\nstatic MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise)\n{\n    return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise));\n}\n\nstatic MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannel;\n    const ma_uint32 channels = pNoise->config.channels;\n    MA_ASSUME(channels > 0);\n\n    if (pNoise->config.format == ma_format_f32) {\n        float* pFramesOutF32 = (float*)pFramesOut;\n        if (pNoise->config.duplicateChannels) {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                float s = ma_noise_f32_white(pNoise);\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutF32[iFrame*channels + iChannel] = s;\n                }\n            }\n        } else {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise);\n                }\n            }\n        }\n    } else if (pNoise->config.format == ma_format_s16) {\n        ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;\n        if (pNoise->config.duplicateChannels) {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                ma_int16 s = ma_noise_s16_white(pNoise);\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutS16[iFrame*channels + iChannel] = s;\n                }\n            }\n        } else {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise);\n                }\n            }\n        }\n    } else {\n        const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);\n        const ma_uint32 bpf = bps * channels;\n\n        if (pNoise->config.duplicateChannels) {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                float s = ma_noise_f32_white(pNoise);\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);\n                }\n            }\n        } else {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    float s = ma_noise_f32_white(pNoise);\n                    ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);\n                }\n            }\n        }\n    }\n\n    return frameCount;\n}\n\n\nstatic MA_INLINE unsigned int ma_tzcnt32(unsigned int x)\n{\n    unsigned int n;\n\n    /* Special case for odd numbers since they should happen about half the time. */\n    if (x & 0x1)  {\n        return 0;\n    }\n\n    if (x == 0) {\n        return sizeof(x) << 3;\n    }\n\n    n = 1;\n    if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; }\n    if ((x & 0x000000FF) == 0) { x >>=  8; n +=  8; }\n    if ((x & 0x0000000F) == 0) { x >>=  4; n +=  4; }\n    if ((x & 0x00000003) == 0) { x >>=  2; n +=  2; }\n    n -= x & 0x00000001;\n\n    return n;\n}\n\n/*\nPink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h\n\nThis is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/\n*/\nstatic MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel)\n{\n    double result;\n    double binPrev;\n    double binNext;\n    unsigned int ibin;\n\n    ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1);\n\n    binPrev = pNoise->state.pink.bin[iChannel][ibin];\n    binNext = ma_lcg_rand_f64(&pNoise->lcg);\n    pNoise->state.pink.bin[iChannel][ibin] = binNext;\n\n    pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev);\n    pNoise->state.pink.counter[iChannel]      += 1;\n\n    result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]);\n    result /= 10;\n\n    return (float)(result * pNoise->config.amplitude);\n}\n\nstatic MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel)\n{\n    return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel));\n}\n\nstatic MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannel;\n    const ma_uint32 channels = pNoise->config.channels;\n    MA_ASSUME(channels > 0);\n\n    if (pNoise->config.format == ma_format_f32) {\n        float* pFramesOutF32 = (float*)pFramesOut;\n        if (pNoise->config.duplicateChannels) {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                float s = ma_noise_f32_pink(pNoise, 0);\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutF32[iFrame*channels + iChannel] = s;\n                }\n            }\n        } else {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel);\n                }\n            }\n        }\n    } else if (pNoise->config.format == ma_format_s16) {\n        ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;\n        if (pNoise->config.duplicateChannels) {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                ma_int16 s = ma_noise_s16_pink(pNoise, 0);\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutS16[iFrame*channels + iChannel] = s;\n                }\n            }\n        } else {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel);\n                }\n            }\n        }\n    } else {\n        const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);\n        const ma_uint32 bpf = bps * channels;\n\n        if (pNoise->config.duplicateChannels) {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                float s = ma_noise_f32_pink(pNoise, 0);\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);\n                }\n            }\n        } else {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    float s = ma_noise_f32_pink(pNoise, iChannel);\n                    ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);\n                }\n            }\n        }\n    }\n\n    return frameCount;\n}\n\n\nstatic MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel)\n{\n    double result;\n\n    result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]);\n    result /= 1.005; /* Don't escape the -1..1 range on average. */\n\n    pNoise->state.brownian.accumulation[iChannel] = result;\n    result /= 20;\n\n    return (float)(result * pNoise->config.amplitude);\n}\n\nstatic MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel)\n{\n    return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel));\n}\n\nstatic MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)\n{\n    ma_uint64 iFrame;\n    ma_uint32 iChannel;\n    const ma_uint32 channels = pNoise->config.channels;\n    MA_ASSUME(channels > 0);\n\n    if (pNoise->config.format == ma_format_f32) {\n        float* pFramesOutF32 = (float*)pFramesOut;\n        if (pNoise->config.duplicateChannels) {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                float s = ma_noise_f32_brownian(pNoise, 0);\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutF32[iFrame*channels + iChannel] = s;\n                }\n            }\n        } else {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel);\n                }\n            }\n        }\n    } else if (pNoise->config.format == ma_format_s16) {\n        ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;\n        if (pNoise->config.duplicateChannels) {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                ma_int16 s = ma_noise_s16_brownian(pNoise, 0);\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutS16[iFrame*channels + iChannel] = s;\n                }\n            }\n        } else {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel);\n                }\n            }\n        }\n    } else {\n        const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);\n        const ma_uint32 bpf = bps * channels;\n\n        if (pNoise->config.duplicateChannels) {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                float s = ma_noise_f32_brownian(pNoise, 0);\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);\n                }\n            }\n        } else {\n            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {\n                for (iChannel = 0; iChannel < channels; iChannel += 1) {\n                    float s = ma_noise_f32_brownian(pNoise, iChannel);\n                    ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);\n                }\n            }\n        }\n    }\n\n    return frameCount;\n}\n\nMA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    ma_uint64 framesRead = 0;\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pNoise == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The output buffer is allowed to be NULL. Since we aren't tracking cursors or anything we can just do nothing and pretend to be successful. */\n    if (pFramesOut == NULL) {\n        framesRead = frameCount;\n    } else {\n        switch (pNoise->config.type) {\n            case ma_noise_type_white:    framesRead = ma_noise_read_pcm_frames__white   (pNoise, pFramesOut, frameCount); break;\n            case ma_noise_type_pink:     framesRead = ma_noise_read_pcm_frames__pink    (pNoise, pFramesOut, frameCount); break;\n            case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break;\n            default: return MA_INVALID_OPERATION;   /* Unknown noise type. */\n        }\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = framesRead;\n    }\n\n    return MA_SUCCESS;\n}\n#endif /* MA_NO_GENERATION */\n\n\n\n#ifndef MA_NO_RESOURCE_MANAGER\n#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS\n#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS   1000\n#endif\n\n#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY\n#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY          1024\n#endif\n\nMA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void)\n{\n    ma_resource_manager_pipeline_notifications notifications;\n\n    MA_ZERO_OBJECT(&notifications);\n\n    return notifications;\n}\n\nstatic void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)\n{\n    if (pPipelineNotifications == NULL) {\n        return;\n    }\n\n    if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); }\n    if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); }\n}\n\nstatic void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)\n{\n    if (pPipelineNotifications == NULL) {\n        return;\n    }\n\n    if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); }\n    if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); }\n}\n\nstatic void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)\n{\n    if (pPipelineNotifications == NULL) {\n        return;\n    }\n\n    if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); }\n    if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); }\n}\n\n\n\n#ifndef MA_DEFAULT_HASH_SEED\n#define MA_DEFAULT_HASH_SEED    42\n#endif\n\n/* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */\n#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n    #pragma GCC diagnostic push\n    #if __GNUC__ >= 7\n    #pragma GCC diagnostic ignored \"-Wimplicit-fallthrough\"\n    #endif\n#endif\n\nstatic MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r)\n{\n    return (x << r) | (x >> (32 - r));\n}\n\nstatic MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i)\n{\n    ma_uint32 block;\n\n    /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */\n    MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block));\n\n    if (ma_is_little_endian()) {\n        return block;\n    } else {\n        return ma_swap_endian_uint32(block);\n    }\n}\n\nstatic MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h)\n{\n    h ^= h >> 16;\n    h *= 0x85ebca6b;\n    h ^= h >> 13;\n    h *= 0xc2b2ae35;\n    h ^= h >> 16;\n\n    return h;\n}\n\nstatic ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed)\n{\n    const ma_uint8* data = (const ma_uint8*)key;\n    const ma_uint32* blocks;\n    const ma_uint8* tail;\n    const int nblocks = len / 4;\n    ma_uint32 h1 = seed;\n    ma_uint32 c1 = 0xcc9e2d51;\n    ma_uint32 c2 = 0x1b873593;\n    ma_uint32 k1;\n    int i;\n\n    blocks = (const ma_uint32 *)(data + nblocks*4);\n\n    for(i = -nblocks; i; i++) {\n        k1 = ma_hash_getblock(blocks,i);\n\n        k1 *= c1;\n        k1 = ma_rotl32(k1, 15);\n        k1 *= c2;\n\n        h1 ^= k1;\n        h1 = ma_rotl32(h1, 13);\n        h1 = h1*5 + 0xe6546b64;\n    }\n\n\n    tail = (const ma_uint8*)(data + nblocks*4);\n\n    k1 = 0;\n    switch(len & 3) {\n        case 3: k1 ^= tail[2] << 16;\n        case 2: k1 ^= tail[1] << 8;\n        case 1: k1 ^= tail[0];\n                k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1;\n    };\n\n\n    h1 ^= len;\n    h1  = ma_hash_fmix32(h1);\n\n    return h1;\n}\n\n#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n    #pragma GCC diagnostic push\n#endif\n/* End MurmurHash3 */\n\nstatic ma_uint32 ma_hash_string_32(const char* str)\n{\n    return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED);\n}\n\nstatic ma_uint32 ma_hash_string_w_32(const wchar_t* str)\n{\n    return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED);\n}\n\n\n\n\n/*\nBasic BST Functions\n*/\nstatic ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode)\n{\n    ma_resource_manager_data_buffer_node* pCurrentNode;\n\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(ppDataBufferNode != NULL);\n\n    pCurrentNode = pResourceManager->pRootDataBufferNode;\n    while (pCurrentNode != NULL) {\n        if (hashedName32 == pCurrentNode->hashedName32) {\n            break;  /* Found. */\n        } else if (hashedName32 < pCurrentNode->hashedName32) {\n            pCurrentNode = pCurrentNode->pChildLo;\n        } else {\n            pCurrentNode = pCurrentNode->pChildHi;\n        }\n    }\n\n    *ppDataBufferNode = pCurrentNode;\n\n    if (pCurrentNode == NULL) {\n        return MA_DOES_NOT_EXIST;\n    } else {\n        return MA_SUCCESS;\n    }\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint)\n{\n    ma_result result = MA_SUCCESS;\n    ma_resource_manager_data_buffer_node* pCurrentNode;\n\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(ppInsertPoint    != NULL);\n\n    *ppInsertPoint = NULL;\n\n    if (pResourceManager->pRootDataBufferNode == NULL) {\n        return MA_SUCCESS;  /* No items. */\n    }\n\n    /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */\n    pCurrentNode = pResourceManager->pRootDataBufferNode;\n    while (pCurrentNode != NULL) {\n        if (hashedName32 == pCurrentNode->hashedName32) {\n            result = MA_ALREADY_EXISTS;\n            break;\n        } else {\n            if (hashedName32 < pCurrentNode->hashedName32) {\n                if (pCurrentNode->pChildLo == NULL) {\n                    result = MA_SUCCESS;\n                    break;\n                } else {\n                    pCurrentNode = pCurrentNode->pChildLo;\n                }\n            } else {\n                if (pCurrentNode->pChildHi == NULL) {\n                    result = MA_SUCCESS;\n                    break;\n                } else {\n                    pCurrentNode = pCurrentNode->pChildHi;\n                }\n            }\n        }\n    }\n\n    *ppInsertPoint = pCurrentNode;\n    return result;\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint)\n{\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pDataBufferNode  != NULL);\n\n    /* The key must have been set before calling this function. */\n    MA_ASSERT(pDataBufferNode->hashedName32 != 0);\n\n    if (pInsertPoint == NULL) {\n        /* It's the first node. */\n        pResourceManager->pRootDataBufferNode = pDataBufferNode;\n    } else {\n        /* It's not the first node. It needs to be inserted. */\n        if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) {\n            MA_ASSERT(pInsertPoint->pChildLo == NULL);\n            pInsertPoint->pChildLo = pDataBufferNode;\n        } else {\n            MA_ASSERT(pInsertPoint->pChildHi == NULL);\n            pInsertPoint->pChildHi = pDataBufferNode;\n        }\n    }\n\n    pDataBufferNode->pParent = pInsertPoint;\n\n    return MA_SUCCESS;\n}\n\n#if 0   /* Unused for now. */\nstatic ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)\n{\n    ma_result result;\n    ma_resource_manager_data_buffer_node* pInsertPoint;\n\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pDataBufferNode  != NULL);\n\n    result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint);\n    if (result != MA_SUCCESS) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);\n}\n#endif\n\nstatic MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode)\n{\n    ma_resource_manager_data_buffer_node* pCurrentNode;\n\n    MA_ASSERT(pDataBufferNode != NULL);\n\n    pCurrentNode = pDataBufferNode;\n    while (pCurrentNode->pChildLo != NULL) {\n        pCurrentNode = pCurrentNode->pChildLo;\n    }\n\n    return pCurrentNode;\n}\n\nstatic MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode)\n{\n    ma_resource_manager_data_buffer_node* pCurrentNode;\n\n    MA_ASSERT(pDataBufferNode != NULL);\n\n    pCurrentNode = pDataBufferNode;\n    while (pCurrentNode->pChildHi != NULL) {\n        pCurrentNode = pCurrentNode->pChildHi;\n    }\n\n    return pCurrentNode;\n}\n\nstatic MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode)\n{\n    MA_ASSERT(pDataBufferNode           != NULL);\n    MA_ASSERT(pDataBufferNode->pChildHi != NULL);\n\n    return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi);\n}\n\nstatic MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode)\n{\n    MA_ASSERT(pDataBufferNode           != NULL);\n    MA_ASSERT(pDataBufferNode->pChildLo != NULL);\n\n    return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo);\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)\n{\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pDataBufferNode  != NULL);\n\n    if (pDataBufferNode->pChildLo == NULL) {\n        if (pDataBufferNode->pChildHi == NULL) {\n            /* Simple case - deleting a buffer with no children. */\n            if (pDataBufferNode->pParent == NULL) {\n                MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);    /* There is only a single buffer in the tree which should be equal to the root node. */\n                pResourceManager->pRootDataBufferNode = NULL;\n            } else {\n                if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {\n                    pDataBufferNode->pParent->pChildLo = NULL;\n                } else {\n                    pDataBufferNode->pParent->pChildHi = NULL;\n                }\n            }\n        } else {\n            /* Node has one child - pChildHi != NULL. */\n            pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent;\n\n            if (pDataBufferNode->pParent == NULL) {\n                MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);\n                pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi;\n            } else {\n                if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {\n                    pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi;\n                } else {\n                    pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi;\n                }\n            }\n        }\n    } else {\n        if (pDataBufferNode->pChildHi == NULL) {\n            /* Node has one child - pChildLo != NULL. */\n            pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent;\n\n            if (pDataBufferNode->pParent == NULL) {\n                MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);\n                pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo;\n            } else {\n                if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {\n                    pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo;\n                } else {\n                    pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo;\n                }\n            }\n        } else {\n            /* Complex case - deleting a node with two children. */\n            ma_resource_manager_data_buffer_node* pReplacementDataBufferNode;\n\n            /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */\n            pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode);\n            MA_ASSERT(pReplacementDataBufferNode != NULL);\n\n            /*\n            Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement\n            node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The\n            replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the\n            replacement node and reinserting it into the same position as the deleted node.\n            */\n            MA_ASSERT(pReplacementDataBufferNode->pParent  != NULL);  /* The replacement node should never be the root which means it should always have a parent. */\n            MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL);  /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */\n\n            if (pReplacementDataBufferNode->pChildHi == NULL) {\n                if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {\n                    pReplacementDataBufferNode->pParent->pChildLo = NULL;\n                } else {\n                    pReplacementDataBufferNode->pParent->pChildHi = NULL;\n                }\n            } else {\n                pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent;\n                if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {\n                    pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi;\n                } else {\n                    pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi;\n                }\n            }\n\n\n            /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */\n            if (pDataBufferNode->pParent != NULL) {\n                if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {\n                    pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode;\n                } else {\n                    pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode;\n                }\n            }\n\n            /* Now need to update the replacement node's pointers. */\n            pReplacementDataBufferNode->pParent  = pDataBufferNode->pParent;\n            pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo;\n            pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi;\n\n            /* Now the children of the replacement node need to have their parent pointers updated. */\n            if (pReplacementDataBufferNode->pChildLo != NULL) {\n                pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode;\n            }\n            if (pReplacementDataBufferNode->pChildHi != NULL) {\n                pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode;\n            }\n\n            /* Now the root node needs to be updated. */\n            if (pResourceManager->pRootDataBufferNode == pDataBufferNode) {\n                pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode;\n            }\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\n#if 0   /* Unused for now. */\nstatic ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32)\n{\n    ma_result result;\n    ma_resource_manager_data_buffer_node* pDataBufferNode;\n\n    result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode);\n    if (result != MA_SUCCESS) {\n        return result;  /* Could not find the data buffer. */\n    }\n\n    return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode);\n}\n#endif\n\nstatic ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode)\n{\n    return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type);\n}\n\nstatic void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType)\n{\n    ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType);\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount)\n{\n    ma_uint32 refCount;\n\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pDataBufferNode  != NULL);\n\n    (void)pResourceManager;\n\n    refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1;\n\n    if (pNewRefCount != NULL) {\n        *pNewRefCount = refCount;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount)\n{\n    ma_uint32 refCount;\n\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pDataBufferNode  != NULL);\n\n    (void)pResourceManager;\n\n    refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1;\n\n    if (pNewRefCount != NULL) {\n        *pNewRefCount = refCount;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)\n{\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pDataBufferNode  != NULL);\n\n    if (pDataBufferNode->isDataOwnedByResourceManager) {\n        if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) {\n            ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks);\n            pDataBufferNode->data.backend.encoded.pData       = NULL;\n            pDataBufferNode->data.backend.encoded.sizeInBytes = 0;\n        } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) {\n            ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks);\n            pDataBufferNode->data.backend.decoded.pData           = NULL;\n            pDataBufferNode->data.backend.decoded.totalFrameCount = 0;\n        } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) {\n            ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks);\n        } else {\n            /* Should never hit this if the node was successfully initialized. */\n            MA_ASSERT(pDataBufferNode->result != MA_SUCCESS);\n        }\n    }\n\n    /* The data buffer itself needs to be freed. */\n    ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode)\n{\n    MA_ASSERT(pDataBufferNode != NULL);\n\n    return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result);    /* Need a naughty const-cast here. */\n}\n\n\nstatic ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager)\n{\n    MA_ASSERT(pResourceManager != NULL);\n\n    return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0;\n}\n\n\ntypedef struct\n{\n    union\n    {\n        ma_async_notification_event e;\n        ma_async_notification_poll p;\n    } backend;  /* Must be the first member. */\n    ma_resource_manager* pResourceManager;\n} ma_resource_manager_inline_notification;\n\nstatic ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification)\n{\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pNotification    != NULL);\n\n    pNotification->pResourceManager = pResourceManager;\n\n    if (ma_resource_manager_is_threading_enabled(pResourceManager)) {\n        return ma_async_notification_event_init(&pNotification->backend.e);\n    } else {\n        return ma_async_notification_poll_init(&pNotification->backend.p);\n    }\n}\n\nstatic void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification)\n{\n    MA_ASSERT(pNotification != NULL);\n\n    if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {\n        ma_async_notification_event_uninit(&pNotification->backend.e);\n    } else {\n        /* No need to uninitialize a polling notification. */\n    }\n}\n\nstatic void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification)\n{\n    MA_ASSERT(pNotification != NULL);\n\n    if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {\n        ma_async_notification_event_wait(&pNotification->backend.e);\n    } else {\n        while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) {\n            ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager);\n            if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {\n                break;\n            }\n        }\n    }\n}\n\nstatic void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification)\n{\n    ma_resource_manager_inline_notification_wait(pNotification);\n    ma_resource_manager_inline_notification_uninit(pNotification);\n}\n\n\nstatic void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager)\n{\n    MA_ASSERT(pResourceManager != NULL);\n\n    if (ma_resource_manager_is_threading_enabled(pResourceManager)) {\n        #ifndef MA_NO_THREADING\n        {\n            ma_mutex_lock(&pResourceManager->dataBufferBSTLock);\n        }\n        #else\n        {\n            MA_ASSERT(MA_FALSE);    /* Should never hit this. */\n        }\n        #endif\n    } else {\n        /* Threading not enabled. Do nothing. */\n    }\n}\n\nstatic void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager)\n{\n    MA_ASSERT(pResourceManager != NULL);\n\n    if (ma_resource_manager_is_threading_enabled(pResourceManager)) {\n        #ifndef MA_NO_THREADING\n        {\n            ma_mutex_unlock(&pResourceManager->dataBufferBSTLock);\n        }\n        #else\n        {\n            MA_ASSERT(MA_FALSE);    /* Should never hit this. */\n        }\n        #endif\n    } else {\n        /* Threading not enabled. Do nothing. */\n    }\n}\n\n#ifndef MA_NO_THREADING\nstatic ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData)\n{\n    ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData;\n    MA_ASSERT(pResourceManager != NULL);\n\n    for (;;) {\n        ma_result result;\n        ma_job job;\n\n        result = ma_resource_manager_next_job(pResourceManager, &job);\n        if (result != MA_SUCCESS) {\n            break;\n        }\n\n        /* Terminate if we got a quit message. */\n        if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {\n            break;\n        }\n\n        ma_job_process(&job);\n    }\n\n    return (ma_thread_result)0;\n}\n#endif\n\nMA_API ma_resource_manager_config ma_resource_manager_config_init(void)\n{\n    ma_resource_manager_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.decodedFormat     = ma_format_unknown;\n    config.decodedChannels   = 0;\n    config.decodedSampleRate = 0;\n    config.jobThreadCount    = 1;   /* A single miniaudio-managed job thread by default. */\n    config.jobQueueCapacity  = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY;\n\n    /* Flags. */\n    config.flags = 0;\n    #ifdef MA_NO_THREADING\n    {\n        /* Threading is disabled at compile time so disable threading at runtime as well by default. */\n        config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;\n        config.jobThreadCount = 0;\n    }\n    #endif\n\n    return config;\n}\n\n\nMA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager)\n{\n    ma_result result;\n    ma_job_queue_config jobQueueConfig;\n\n    if (pResourceManager == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pResourceManager);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #ifndef MA_NO_THREADING\n    {\n        if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) {\n            return MA_INVALID_ARGS; /* Requesting too many job threads. */\n        }\n    }\n    #endif\n\n    pResourceManager->config = *pConfig;\n    ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks);\n\n    /* Get the log set up early so we can start using it as soon as possible. */\n    if (pResourceManager->config.pLog == NULL) {\n        result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log);\n        if (result == MA_SUCCESS) {\n            pResourceManager->config.pLog = &pResourceManager->log;\n        } else {\n            pResourceManager->config.pLog = NULL;   /* Logging is unavailable. */\n        }\n    }\n\n    if (pResourceManager->config.pVFS == NULL) {\n        result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks);\n        if (result != MA_SUCCESS) {\n            return result;  /* Failed to initialize the default file system. */\n        }\n\n        pResourceManager->config.pVFS = &pResourceManager->defaultVFS;\n    }\n\n    /* If threading has been disabled at compile time, enfore it at run time as well. */\n    #ifdef MA_NO_THREADING\n    {\n        pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;\n    }\n    #endif\n\n    /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */\n    if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {\n        pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING;\n\n        /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */\n        if (pResourceManager->config.jobThreadCount > 0) {\n            return MA_INVALID_ARGS;\n        }\n    }\n\n    /* Job queue. */\n    jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity;\n    jobQueueConfig.flags    = 0;\n    if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) {\n        if (pResourceManager->config.jobThreadCount > 0) {\n            return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */\n        }\n\n        jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING;\n    }\n\n    result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n\n    /* Custom decoding backends. */\n    if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) {\n        size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount;\n\n        pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks);\n        if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) {\n            ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);\n            return MA_OUT_OF_MEMORY;\n        }\n\n        MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes);\n\n        pResourceManager->config.customDecodingBackendCount     = pConfig->customDecodingBackendCount;\n        pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData;\n    }\n\n\n\n    /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */\n    if (ma_resource_manager_is_threading_enabled(pResourceManager)) {\n        #ifndef MA_NO_THREADING\n        {\n            ma_uint32 iJobThread;\n\n            /* Data buffer lock. */\n            result = ma_mutex_init(&pResourceManager->dataBufferBSTLock);\n            if (result != MA_SUCCESS) {\n                ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);\n                return result;\n            }\n\n            /* Create the job threads last to ensure the threads has access to valid data. */\n            for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {\n                result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks);\n                if (result != MA_SUCCESS) {\n                    ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);\n                    ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);\n                    return result;\n                }\n            }\n        }\n        #else\n        {\n            /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */\n            MA_ASSERT(MA_FALSE);\n        }\n        #endif\n    }\n\n    return MA_SUCCESS;\n}\n\n\nstatic void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager)\n{\n    MA_ASSERT(pResourceManager);\n\n    /* If everything was done properly, there shouldn't be any active data buffers. */\n    while (pResourceManager->pRootDataBufferNode != NULL) {\n        ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode;\n        ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);\n\n        /* The data buffer has been removed from the BST, so now we need to free it's data. */\n        ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);\n    }\n}\n\nMA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager)\n{\n    if (pResourceManager == NULL) {\n        return;\n    }\n\n    /*\n    Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the\n    queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it.\n    */\n    ma_resource_manager_post_job_quit(pResourceManager);\n\n    /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */\n    if (ma_resource_manager_is_threading_enabled(pResourceManager)) {\n        #ifndef MA_NO_THREADING\n        {\n            ma_uint32 iJobThread;\n\n            for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {\n                ma_thread_wait(&pResourceManager->jobThreads[iJobThread]);\n            }\n        }\n        #else\n        {\n            MA_ASSERT(MA_FALSE);    /* Should never hit this. */\n        }\n        #endif\n    }\n\n    /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */\n    ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager);\n\n    /* The job queue is no longer needed. */\n    ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);\n\n    /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */\n    if (ma_resource_manager_is_threading_enabled(pResourceManager)) {\n        #ifndef MA_NO_THREADING\n        {\n            ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);\n        }\n        #else\n        {\n            MA_ASSERT(MA_FALSE);    /* Should never hit this. */\n        }\n        #endif\n    }\n\n    ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks);\n\n    if (pResourceManager->config.pLog == &pResourceManager->log) {\n        ma_log_uninit(&pResourceManager->log);\n    }\n}\n\nMA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager)\n{\n    if (pResourceManager == NULL) {\n        return NULL;\n    }\n\n    return pResourceManager->config.pLog;\n}\n\n\n\nMA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void)\n{\n    ma_resource_manager_data_source_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.rangeBegInPCMFrames     = MA_DATA_SOURCE_DEFAULT_RANGE_BEG;\n    config.rangeEndInPCMFrames     = MA_DATA_SOURCE_DEFAULT_RANGE_END;\n    config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG;\n    config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END;\n    config.isLooping               = MA_FALSE;\n\n    return config;\n}\n\n\nstatic ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager)\n{\n    ma_decoder_config config;\n\n    config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate);\n    config.allocationCallbacks    = pResourceManager->config.allocationCallbacks;\n    config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables;\n    config.customBackendCount     = pResourceManager->config.customDecodingBackendCount;\n    config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData;\n\n    return config;\n}\n\nstatic ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder)\n{\n    ma_result result;\n    ma_decoder_config config;\n\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pFilePath        != NULL || pFilePathW != NULL);\n    MA_ASSERT(pDecoder         != NULL);\n\n    config = ma_resource_manager__init_decoder_config(pResourceManager);\n\n    if (pFilePath != NULL) {\n        result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder);\n        if (result != MA_SUCCESS) {\n            ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, \"Failed to load file \\\"%s\\\". %s.\\n\", pFilePath, ma_result_description(result));\n            return result;\n        }\n    } else {\n        result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder);\n        if (result != MA_SUCCESS) {\n            #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)\n                ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, \"Failed to load file \\\"%ls\\\". %s.\\n\", pFilePathW, ma_result_description(result));\n            #endif\n            return result;\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer)\n{\n    return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized);\n}\n\nstatic ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer)\n{\n    if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {\n        return NULL;    /* Connector not yet initialized. */\n    }\n\n    switch (pDataBuffer->pNode->data.type)\n    {\n        case ma_resource_manager_data_supply_type_encoded:       return &pDataBuffer->connector.decoder;\n        case ma_resource_manager_data_supply_type_decoded:       return &pDataBuffer->connector.buffer;\n        case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer;\n\n        case ma_resource_manager_data_supply_type_unknown:\n        default:\n        {\n            ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, \"Failed to retrieve data buffer connector. Unknown data supply type.\\n\");\n            return NULL;\n        };\n    };\n}\n\nstatic ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence)\n{\n    ma_result result;\n\n    MA_ASSERT(pDataBuffer != NULL);\n    MA_ASSERT(pConfig     != NULL);\n    MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE);\n\n    /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */\n    result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);\n    if (result != MA_SUCCESS && result != MA_BUSY) {\n        return result;  /* The data buffer is in an erroneous state. */\n    }\n\n    /*\n    We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the\n    \"instance\" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use\n    an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead.\n    */\n    switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))\n    {\n        case ma_resource_manager_data_supply_type_encoded:          /* Connector is a decoder. */\n        {\n            ma_decoder_config config;\n            config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager);\n            result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder);\n        } break;\n\n        case ma_resource_manager_data_supply_type_decoded:          /* Connector is an audio buffer. */\n        {\n            ma_audio_buffer_config config;\n            config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL);\n            result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer);\n        } break;\n\n        case ma_resource_manager_data_supply_type_decoded_paged:    /* Connector is a paged audio buffer. */\n        {\n            ma_paged_audio_buffer_config config;\n            config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data);\n            result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer);\n        } break;\n\n        case ma_resource_manager_data_supply_type_unknown:\n        default:\n        {\n            /* Unknown data supply type. Should never happen. Need to post an error here. */\n            return MA_INVALID_ARGS;\n        };\n    }\n\n    /*\n    Initialization of the connector is when we can fire the init notification. This will give the application access to\n    the format/channels/rate of the data source.\n    */\n    if (result == MA_SUCCESS) {\n        /*\n        The resource manager supports the ability to set the range and loop settings via a config at\n        initialization time. This results in an case where the ranges could be set explicitly via\n        ma_data_source_set_*() before we get to this point here. If this happens, we'll end up\n        hitting a case where we just override those settings which results in what feels like a bug.\n\n        To address this we only change the relevant properties if they're not equal to defaults. If\n        they're equal to defaults there's no need to change them anyway. If they're *not* set to the\n        default values, we can assume the user has set the range and loop settings via the config. If\n        they're doing their own calls to ma_data_source_set_*() in addition to setting them via the\n        config, that's entirely on the caller and any synchronization issue becomes their problem.\n        */\n        if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) {\n            ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);\n        }\n\n        if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) {\n            ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);\n        }\n\n        if (pConfig->isLooping != MA_FALSE) {\n            ma_data_source_set_looping(pDataBuffer, pConfig->isLooping);\n        }\n\n        ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE);\n\n        if (pInitNotification != NULL) {\n            ma_async_notification_signal(pInitNotification);\n        }\n\n        if (pInitFence != NULL) {\n            ma_fence_release(pInitFence);\n        }\n    }\n\n    /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */\n    return result;\n}\n\nstatic ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer)\n{\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pDataBuffer      != NULL);\n\n    (void)pResourceManager;\n\n    switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))\n    {\n        case ma_resource_manager_data_supply_type_encoded:          /* Connector is a decoder. */\n        {\n            ma_decoder_uninit(&pDataBuffer->connector.decoder);\n        } break;\n\n        case ma_resource_manager_data_supply_type_decoded:          /* Connector is an audio buffer. */\n        {\n            ma_audio_buffer_uninit(&pDataBuffer->connector.buffer);\n        } break;\n\n        case ma_resource_manager_data_supply_type_decoded_paged:    /* Connector is a paged audio buffer. */\n        {\n            ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer);\n        } break;\n\n        case ma_resource_manager_data_supply_type_unknown:\n        default:\n        {\n            /* Unknown data supply type. Should never happen. Need to post an error here. */\n            return MA_INVALID_ARGS;\n        };\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode)\n{\n    MA_ASSERT(pDataBufferNode != NULL);\n    return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1);\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW)\n{\n    ma_result result;\n    size_t dataSizeInBytes;\n    void* pData;\n\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pDataBufferNode  != NULL);\n    MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);\n\n    result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks);\n    if (result != MA_SUCCESS) {\n        if (pFilePath != NULL) {\n            ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, \"Failed to load file \\\"%s\\\". %s.\\n\", pFilePath, ma_result_description(result));\n        } else {\n            #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)\n                ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, \"Failed to load file \\\"%ls\\\". %s.\\n\", pFilePathW, ma_result_description(result));\n            #endif\n        }\n\n        return result;\n    }\n\n    pDataBufferNode->data.backend.encoded.pData       = pData;\n    pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes;\n    ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded);  /* <-- Must be set last. */\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder)\n{\n    ma_result result = MA_SUCCESS;\n    ma_decoder* pDecoder;\n    ma_uint64 totalFrameCount;\n\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pDataBufferNode  != NULL);\n    MA_ASSERT(ppDecoder         != NULL);\n    MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);\n\n    *ppDecoder = NULL;  /* For safety. */\n\n    pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks);\n    if (pDecoder == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder);\n    if (result != MA_SUCCESS) {\n        ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);\n        return result;\n    }\n\n    /*\n    At this point we have the decoder and we now need to initialize the data supply. This will\n    be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap\n    allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter\n    is used when the length of a sound is unknown until a full decode has been performed.\n    */\n    if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) {\n        result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    } else {\n        totalFrameCount = 0;\n    }\n\n    if (totalFrameCount > 0) {\n        /* It's a known length. The data supply is a regular decoded buffer. */\n        ma_uint64 dataSizeInBytes;\n        void* pData;\n\n        dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);\n        if (dataSizeInBytes > MA_SIZE_MAX) {\n            ma_decoder_uninit(pDecoder);\n            ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);\n            return MA_TOO_BIG;\n        }\n\n        pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks);\n        if (pData == NULL) {\n            ma_decoder_uninit(pDecoder);\n            ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);\n            return MA_OUT_OF_MEMORY;\n        }\n\n        /* The buffer needs to be initialized to silence in case the caller reads from it. */\n        ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels);\n\n        /* Data has been allocated and the data supply can now be initialized. */\n        pDataBufferNode->data.backend.decoded.pData             = pData;\n        pDataBufferNode->data.backend.decoded.totalFrameCount   = totalFrameCount;\n        pDataBufferNode->data.backend.decoded.format            = pDecoder->outputFormat;\n        pDataBufferNode->data.backend.decoded.channels          = pDecoder->outputChannels;\n        pDataBufferNode->data.backend.decoded.sampleRate        = pDecoder->outputSampleRate;\n        pDataBufferNode->data.backend.decoded.decodedFrameCount = 0;\n        ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded);  /* <-- Must be set last. */\n    } else {\n        /*\n        It's an unknown length. The data supply is a paged decoded buffer. Setting this up is\n        actually easier than the non-paged decoded buffer because we just need to initialize\n        a ma_paged_audio_buffer object.\n        */\n        result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data);\n        if (result != MA_SUCCESS) {\n            ma_decoder_uninit(pDecoder);\n            ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);\n            return result;\n        }\n\n        pDataBufferNode->data.backend.decodedPaged.sampleRate        = pDecoder->outputSampleRate;\n        pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0;\n        ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged);  /* <-- Must be set last. */\n    }\n\n    *ppDecoder = pDecoder;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint64 pageSizeInFrames;\n    ma_uint64 framesToTryReading;\n    ma_uint64 framesRead;\n\n    MA_ASSERT(pResourceManager != NULL);\n    MA_ASSERT(pDataBufferNode  != NULL);\n    MA_ASSERT(pDecoder         != NULL);\n\n    /* We need to know the size of a page in frames to know how many frames to decode. */\n    pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000);\n    framesToTryReading = pageSizeInFrames;\n\n    /*\n    Here is where we do the decoding of the next page. We'll run a slightly different path depending\n    on whether or not we're using a flat or paged buffer because the allocation of the page differs\n    between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged\n    buffer, we need to allocate a new page and attach it to the linked list.\n    */\n    switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode))\n    {\n        case ma_resource_manager_data_supply_type_decoded:\n        {\n            /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */\n            void* pDst;\n            ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount;\n            if (framesToTryReading > framesRemaining) {\n                framesToTryReading = framesRemaining;\n            }\n\n            if (framesToTryReading > 0) {\n                pDst = ma_offset_ptr(\n                    pDataBufferNode->data.backend.decoded.pData,\n                    pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels)\n                );\n                MA_ASSERT(pDst != NULL);\n\n                result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead);\n                if (framesRead > 0) {\n                    pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead;\n                }\n            } else {\n                framesRead = 0;\n            }\n        } break;\n\n        case ma_resource_manager_data_supply_type_decoded_paged:\n        {\n            /* The destination buffer is a freshly allocated page. */\n            ma_paged_audio_buffer_page* pPage;\n\n            result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage);\n            if (result != MA_SUCCESS) {\n                return result;\n            }\n\n            result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead);\n            if (framesRead > 0) {\n                pPage->sizeInFrames = framesRead;\n\n                result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage);\n                if (result == MA_SUCCESS) {\n                    pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead;\n                } else {\n                    /* Failed to append the page. Just abort and set the status to MA_AT_END. */\n                    ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);\n                    result = MA_AT_END;\n                }\n            } else {\n                /* No frames were read. Free the page and just set the status to MA_AT_END. */\n                ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);\n                result = MA_AT_END;\n            }\n        } break;\n\n        case ma_resource_manager_data_supply_type_encoded:\n        case ma_resource_manager_data_supply_type_unknown:\n        default:\n        {\n            /* Unexpected data supply type. */\n            ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, \"Unexpected data supply type (%d) when decoding page.\", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode));\n            return MA_ERROR;\n        };\n    }\n\n    if (result == MA_SUCCESS && framesRead == 0) {\n        result = MA_AT_END;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode)\n{\n    ma_result result = MA_SUCCESS;\n    ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;\n    ma_resource_manager_data_buffer_node* pInsertPoint;\n\n    if (ppDataBufferNode != NULL) {\n        *ppDataBufferNode = NULL;\n    }\n\n    result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint);\n    if (result == MA_ALREADY_EXISTS) {\n        /* The node already exists. We just need to increment the reference count. */\n        pDataBufferNode = pInsertPoint;\n\n        result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL);\n        if (result != MA_SUCCESS) {\n            return result;  /* Should never happen. Failed to increment the reference count. */\n        }\n\n        result = MA_ALREADY_EXISTS;\n        goto done;\n    } else {\n        /*\n        The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This\n        needs to be done inside the critical section to ensure an uninitialization of the node\n        does not occur before initialization on another thread.\n        */\n        pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks);\n        if (pDataBufferNode == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n\n        MA_ZERO_OBJECT(pDataBufferNode);\n        pDataBufferNode->hashedName32 = hashedName32;\n        pDataBufferNode->refCount     = 1;        /* Always set to 1 by default (this is our first reference). */\n\n        if (pExistingData == NULL) {\n            pDataBufferNode->data.type    = ma_resource_manager_data_supply_type_unknown;    /* <-- We won't know this until we start decoding. */\n            pDataBufferNode->result       = MA_BUSY;  /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */\n            pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE;\n        } else {\n            pDataBufferNode->data         = *pExistingData;\n            pDataBufferNode->result       = MA_SUCCESS;   /* Not loading asynchronously, so just set the status */\n            pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE;\n        }\n\n        result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);\n        if (result != MA_SUCCESS) {\n            ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);\n            return result;  /* Should never happen. Failed to insert the data buffer into the BST. */\n        }\n\n        /*\n        Here is where we'll post the job, but only if we're loading asynchronously. If we're\n        loading synchronously we'll defer loading to a later stage, outside of the critical\n        section.\n        */\n        if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {\n            /* Loading asynchronously. Post the job. */\n            ma_job job;\n            char* pFilePathCopy = NULL;\n            wchar_t* pFilePathWCopy = NULL;\n\n            /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */\n            if (pFilePath != NULL) {\n                pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks);\n            } else {\n                pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks);\n            }\n\n            if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {\n                ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);\n                ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);\n                return MA_OUT_OF_MEMORY;\n            }\n\n            if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {\n                ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification);\n            }\n\n            /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */\n            if (pInitFence != NULL) { ma_fence_acquire(pInitFence); }\n            if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); }\n\n            /* We now have everything we need to post the job to the job thread. */\n            job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE);\n            job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);\n            job.data.resourceManager.loadDataBufferNode.pResourceManager  = pResourceManager;\n            job.data.resourceManager.loadDataBufferNode.pDataBufferNode   = pDataBufferNode;\n            job.data.resourceManager.loadDataBufferNode.pFilePath         = pFilePathCopy;\n            job.data.resourceManager.loadDataBufferNode.pFilePathW        = pFilePathWCopy;\n            job.data.resourceManager.loadDataBufferNode.flags             = flags;\n            job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL;\n            job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL;\n            job.data.resourceManager.loadDataBufferNode.pInitFence        = pInitFence;\n            job.data.resourceManager.loadDataBufferNode.pDoneFence        = pDoneFence;\n\n            if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {\n                result = ma_job_process(&job);\n            } else {\n                result = ma_resource_manager_post_job(pResourceManager, &job);\n            }\n\n            if (result != MA_SUCCESS) {\n                /* Failed to post job. Probably ran out of memory. */\n                ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, \"Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\\n\", ma_result_description(result));\n\n                /*\n                Fences were acquired before posting the job, but since the job was not able to\n                be posted, we need to make sure we release them so nothing gets stuck waiting.\n                */\n                if (pInitFence != NULL) { ma_fence_release(pInitFence); }\n                if (pDoneFence != NULL) { ma_fence_release(pDoneFence); }\n\n                if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {\n                    ma_resource_manager_inline_notification_uninit(pInitNotification);\n                } else {\n                    /* These will have been freed by the job thread, but with WAIT_INIT they will already have happend sinced the job has already been handled. */\n                    ma_free(pFilePathCopy,  &pResourceManager->config.allocationCallbacks);\n                    ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);\n                }\n\n                ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);\n                ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);\n\n                return result;\n            }\n        }\n    }\n\ndone:\n    if (ppDataBufferNode != NULL) {\n        *ppDataBufferNode = pDataBufferNode;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode)\n{\n    ma_result result = MA_SUCCESS;\n    ma_bool32 nodeAlreadyExists = MA_FALSE;\n    ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;\n    ma_resource_manager_inline_notification initNotification;   /* Used when the WAIT_INIT flag is set. */\n\n    if (ppDataBufferNode != NULL) {\n        *ppDataBufferNode = NULL;   /* Safety. */\n    }\n\n    if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* If we're specifying existing data, it must be valid. */\n    if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */\n    if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {\n        flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;\n    }\n\n    if (hashedName32 == 0) {\n        if (pFilePath != NULL) {\n            hashedName32 = ma_hash_string_32(pFilePath);\n        } else {\n            hashedName32 = ma_hash_string_w_32(pFilePathW);\n        }\n    }\n\n    /*\n    Here is where we either increment the node's reference count or allocate a new one and add it\n    to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is\n    posted inside the critical section just in case the caller immediately uninitializes the node\n    as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the\n    node is not uninitialized before initialization.\n    */\n    ma_resource_manager_data_buffer_bst_lock(pResourceManager);\n    {\n        result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode);\n    }\n    ma_resource_manager_data_buffer_bst_unlock(pResourceManager);\n\n    if (result == MA_ALREADY_EXISTS) {\n        nodeAlreadyExists = MA_TRUE;\n        result = MA_SUCCESS;\n    } else {\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    /*\n    If we're loading synchronously, we'll need to load everything now. When loading asynchronously,\n    a job will have been posted inside the BST critical section so that an uninitialization can be\n    allocated an appropriate execution order thereby preventing it from being uninitialized before\n    the node is initialized by the decoding thread(s).\n    */\n    if (nodeAlreadyExists == MA_FALSE) {    /* Don't need to try loading anything if the node already exists. */\n        if (pFilePath == NULL && pFilePathW == NULL) {\n            /*\n            If this path is hit, it means a buffer is being copied (i.e. initialized from only the\n            hashed name), but that node has been freed in the meantime, probably from some other\n            thread. This is an invalid operation.\n            */\n            ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, \"Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\\n\");\n            result = MA_INVALID_OPERATION;\n            goto done;\n        }\n\n        if (pDataBufferNode->isDataOwnedByResourceManager) {\n            if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) {\n                /* Loading synchronously. Load the sound in it's entirety here. */\n                if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) {\n                    /* No decoding. This is the simple case - just store the file contents in memory. */\n                    result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW);\n                    if (result != MA_SUCCESS) {\n                        goto done;\n                    }\n                } else {\n                    /* Decoding. We do this the same way as we do when loading asynchronously. */\n                    ma_decoder* pDecoder;\n                    result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder);\n                    if (result != MA_SUCCESS) {\n                        goto done;\n                    }\n\n                    /* We have the decoder, now decode page by page just like we do when loading asynchronously. */\n                    for (;;) {\n                        /* Decode next page. */\n                        result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder);\n                        if (result != MA_SUCCESS) {\n                            break;  /* Will return MA_AT_END when the last page has been decoded. */\n                        }\n                    }\n\n                    /* Reaching the end needs to be considered successful. */\n                    if (result == MA_AT_END) {\n                        result  = MA_SUCCESS;\n                    }\n\n                    /*\n                    At this point the data buffer is either fully decoded or some error occurred. Either\n                    way, the decoder is no longer necessary.\n                    */\n                    ma_decoder_uninit(pDecoder);\n                    ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);\n                }\n\n                /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */\n                ma_atomic_exchange_i32(&pDataBufferNode->result, result);\n            } else {\n                /* Loading asynchronously. We may need to wait for initialization. */\n                if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {\n                    ma_resource_manager_inline_notification_wait(&initNotification);\n                }\n            }\n        } else {\n            /* The data is not managed by the resource manager so there's nothing else to do. */\n            MA_ASSERT(pExistingData != NULL);\n        }\n    }\n\ndone:\n    /* If we failed to initialize the data buffer we need to free it. */\n    if (result != MA_SUCCESS) {\n        if (nodeAlreadyExists == MA_FALSE) {\n            ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);\n            ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);\n        }\n    }\n\n    /*\n    The init notification needs to be uninitialized. This will be used if the node does not already\n    exist, and we've specified ASYNC | WAIT_INIT.\n    */\n    if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {\n        if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {\n            ma_resource_manager_inline_notification_uninit(&initNotification);\n        }\n    }\n\n    if (ppDataBufferNode != NULL) {\n        *ppDataBufferNode = pDataBufferNode;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */\n    ma_uint32 hashedName32 = 0;\n\n    if (pResourceManager == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pDataBufferNode == NULL) {\n        if (pName == NULL && pNameW == NULL) {\n            return MA_INVALID_ARGS;\n        }\n\n        if (pName != NULL) {\n            hashedName32 = ma_hash_string_32(pName);\n        } else {\n            hashedName32 = ma_hash_string_w_32(pNameW);\n        }\n    }\n\n    /*\n    The first thing to do is decrement the reference counter of the node. Then, if the reference\n    count is zero, we need to free the node. If the node is still in the process of loading, we'll\n    need to post a job to the job queue to free the node. Otherwise we'll just do it here.\n    */\n    ma_resource_manager_data_buffer_bst_lock(pResourceManager);\n    {\n        /* Might need to find the node. Must be done inside the critical section. */\n        if (pDataBufferNode == NULL) {\n            result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode);\n            if (result != MA_SUCCESS) {\n                goto stage2;    /* Couldn't find the node. */\n            }\n        }\n\n        result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount);\n        if (result != MA_SUCCESS) {\n            goto stage2;    /* Should never happen. */\n        }\n\n        if (refCount == 0) {\n            result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);\n            if (result != MA_SUCCESS) {\n                goto stage2;  /* An error occurred when trying to remove the data buffer. This should never happen. */\n            }\n        }\n    }\n    ma_resource_manager_data_buffer_bst_unlock(pResourceManager);\n\nstage2:\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /*\n    Here is where we need to free the node. We don't want to do this inside the critical section\n    above because we want to keep that as small as possible for multi-threaded efficiency.\n    */\n    if (refCount == 0) {\n        if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {\n            /* The sound is still loading. We need to delay the freeing of the node to a safe time. */\n            ma_job job;\n\n            /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */\n            ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE);\n\n            job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE);\n            job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);\n            job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager;\n            job.data.resourceManager.freeDataBufferNode.pDataBufferNode  = pDataBufferNode;\n\n            result = ma_resource_manager_post_job(pResourceManager, &job);\n            if (result != MA_SUCCESS) {\n                ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, \"Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\\n\", ma_result_description(result));\n                return result;\n            }\n\n            /* If we don't support threading, process the job queue here. */\n            if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {\n                while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {\n                    result = ma_resource_manager_process_next_job(pResourceManager);\n                    if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {\n                        result = MA_SUCCESS;\n                        break;\n                    }\n                }\n            } else {\n                /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */\n            }\n        } else {\n            /* The sound isn't loading so we can just free the node here. */\n            ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);\n        }\n    }\n\n    return result;\n}\n\n\n\nstatic ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer)\n{\n    MA_ASSERT(pDataBuffer != NULL);\n    return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1);\n}\n\nstatic ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);\n}\n\nstatic ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex);\n}\n\nstatic ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n}\n\nstatic ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)\n{\n    return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor);\n}\n\nstatic ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)\n{\n    return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength);\n}\n\nstatic ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)\n{\n    ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource;\n    MA_ASSERT(pDataBuffer != NULL);\n\n    ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping);\n\n    /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */\n    ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable =\n{\n    ma_resource_manager_data_buffer_cb__read_pcm_frames,\n    ma_resource_manager_data_buffer_cb__seek_to_pcm_frame,\n    ma_resource_manager_data_buffer_cb__get_data_format,\n    ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames,\n    ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames,\n    ma_resource_manager_data_buffer_cb__set_looping,\n    0\n};\n\nstatic ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer)\n{\n    ma_result result = MA_SUCCESS;\n    ma_resource_manager_data_buffer_node* pDataBufferNode;\n    ma_data_source_config dataSourceConfig;\n    ma_bool32 async;\n    ma_uint32 flags;\n    ma_resource_manager_pipeline_notifications notifications;\n\n    if (pDataBuffer == NULL) {\n        if (pConfig != NULL && pConfig->pNotifications != NULL) {\n            ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);\n        }\n\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pDataBuffer);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->pNotifications != NULL) {\n        notifications = *pConfig->pNotifications;   /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */\n    } else {\n        MA_ZERO_OBJECT(&notifications);\n    }\n\n    /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */\n    flags = pConfig->flags;\n    if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {\n        flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;\n    }\n\n    async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0;\n\n    /*\n    Fences need to be acquired before doing anything. These must be acquired and released outside of\n    the node to ensure there's no holes where ma_fence_wait() could prematurely return before the\n    data buffer has completed initialization.\n\n    When loading asynchronously, the node acquisition routine below will acquire the fences on this\n    thread and then release them on the async thread when the operation is complete.\n\n    These fences are always released at the \"done\" tag at the end of this function. They'll be\n    acquired a second if loading asynchronously. This double acquisition system is just done to\n    simplify code maintanence.\n    */\n    ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);\n    {\n        /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */\n        result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode);\n        if (result != MA_SUCCESS) {\n            ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);\n            goto done;\n        }\n\n        dataSourceConfig = ma_data_source_config_init();\n        dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable;\n\n        result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds);\n        if (result != MA_SUCCESS) {\n            ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);\n            ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);\n            goto done;\n        }\n\n        pDataBuffer->pResourceManager = pResourceManager;\n        pDataBuffer->pNode  = pDataBufferNode;\n        pDataBuffer->flags  = flags;\n        pDataBuffer->result = MA_BUSY;  /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */\n\n        /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */\n        if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) {\n            /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */\n            result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL);\n            ma_atomic_exchange_i32(&pDataBuffer->result, result);\n\n            ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);\n            goto done;\n        } else {\n            /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */\n            ma_job job;\n            ma_resource_manager_inline_notification initNotification;   /* Used when the WAIT_INIT flag is set. */\n\n            if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {\n                ma_resource_manager_inline_notification_init(pResourceManager, &initNotification);\n            }\n\n            /*\n            The status of the data buffer needs to be set to MA_BUSY before posting the job so that the\n            worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other\n            than MA_BUSY, it'll assume an error and fall through to an early exit.\n            */\n            ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY);\n\n            /* Acquire fences a second time. These will be released by the async thread. */\n            ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);\n\n            job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER);\n            job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);\n            job.data.resourceManager.loadDataBuffer.pDataBuffer             = pDataBuffer;\n            job.data.resourceManager.loadDataBuffer.pInitNotification       = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification;\n            job.data.resourceManager.loadDataBuffer.pDoneNotification       = notifications.done.pNotification;\n            job.data.resourceManager.loadDataBuffer.pInitFence              = notifications.init.pFence;\n            job.data.resourceManager.loadDataBuffer.pDoneFence              = notifications.done.pFence;\n            job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames     = pConfig->rangeBegInPCMFrames;\n            job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames     = pConfig->rangeEndInPCMFrames;\n            job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;\n            job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;\n            job.data.resourceManager.loadDataBuffer.isLooping               = pConfig->isLooping;\n\n            /* If we need to wait for initialization to complete we can just process the job in place. */\n            if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {\n                result = ma_job_process(&job);\n            } else {\n                result = ma_resource_manager_post_job(pResourceManager, &job);\n            }\n\n            if (result != MA_SUCCESS) {\n                /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */\n                ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, \"Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\\n\", ma_result_description(result));\n                ma_atomic_exchange_i32(&pDataBuffer->result, result);\n\n                /* Release the fences after the result has been set on the data buffer. */\n                ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);\n            } else {\n                if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {\n                    ma_resource_manager_inline_notification_wait(&initNotification);\n\n                    if (notifications.init.pNotification != NULL) {\n                        ma_async_notification_signal(notifications.init.pNotification);\n                    }\n\n                    /* NOTE: Do not release the init fence here. It will have been done by the job. */\n\n                    /* Make sure we return an error if initialization failed on the async thread. */\n                    result = ma_resource_manager_data_buffer_result(pDataBuffer);\n                    if (result == MA_BUSY) {\n                        result  = MA_SUCCESS;\n                    }\n                }\n            }\n\n            if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {\n                ma_resource_manager_inline_notification_uninit(&initNotification);\n            }\n        }\n\n        if (result != MA_SUCCESS) {\n            ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);\n            goto done;\n        }\n    }\ndone:\n    if (result == MA_SUCCESS) {\n        if (pConfig->initialSeekPointInPCMFrames > 0) {\n            ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames);\n        }\n    }\n\n    ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);\n\n    return result;\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer)\n{\n    return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer);\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)\n{\n    ma_resource_manager_data_source_config config;\n\n    config = ma_resource_manager_data_source_config_init();\n    config.pFilePath      = pFilePath;\n    config.flags          = flags;\n    config.pNotifications = pNotifications;\n\n    return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)\n{\n    ma_resource_manager_data_source_config config;\n\n    config = ma_resource_manager_data_source_config_init();\n    config.pFilePathW     = pFilePath;\n    config.flags          = flags;\n    config.pNotifications = pNotifications;\n\n    return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer)\n{\n    ma_resource_manager_data_source_config config;\n\n    if (pExistingDataBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ASSERT(pExistingDataBuffer->pNode != NULL);  /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */\n\n    config = ma_resource_manager_data_source_config_init();\n    config.flags = pExistingDataBuffer->flags;\n\n    return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer);\n}\n\nstatic ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer)\n{\n    MA_ASSERT(pDataBuffer != NULL);\n\n    /* The connector should be uninitialized first. */\n    ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer);\n\n    /* With the connector uninitialized we can unacquire the node. */\n    ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL);\n\n    /* The base data source needs to be uninitialized as well. */\n    ma_data_source_uninit(&pDataBuffer->ds);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer)\n{\n    ma_result result;\n\n    if (pDataBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) {\n        /* The data buffer can be deleted synchronously. */\n        return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);\n    } else {\n        /*\n        The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will\n        be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event\n        to get processed before returning.\n        */\n        ma_resource_manager_inline_notification notification;\n        ma_job job;\n\n        /*\n        We need to mark the node as unavailable so we don't try reading from it anymore, but also to\n        let the loading thread know that it needs to abort it's loading procedure.\n        */\n        ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE);\n\n        result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, &notification);\n        if (result != MA_SUCCESS) {\n            return result;  /* Failed to create the notification. This should rarely, if ever, happen. */\n        }\n\n        job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER);\n        job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);\n        job.data.resourceManager.freeDataBuffer.pDataBuffer       = pDataBuffer;\n        job.data.resourceManager.freeDataBuffer.pDoneNotification = &notification;\n        job.data.resourceManager.freeDataBuffer.pDoneFence        = NULL;\n\n        result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job);\n        if (result != MA_SUCCESS) {\n            ma_resource_manager_inline_notification_uninit(&notification);\n            return result;\n        }\n\n        ma_resource_manager_inline_notification_wait_and_uninit(&notification);\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint64 framesRead = 0;\n    ma_bool32 isDecodedBufferBusy = MA_FALSE;\n\n    /* Safety. */\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    /*\n    We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after\n    it's been uninitialized or is in the process of uninitializing.\n    */\n    MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);\n\n    /* If the node is not initialized we need to abort with a busy code. */\n    if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {\n        return MA_BUSY; /* Still loading. */\n    }\n\n    /*\n    If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's\n    a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If\n    this happens, we need to keep the seek scheduled and return MA_BUSY.\n    */\n    if (pDataBuffer->seekToCursorOnNextRead) {\n        pDataBuffer->seekToCursorOnNextRead = MA_FALSE;\n\n        result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames);\n        if (result != MA_SUCCESS) {\n            if (result == MA_BAD_SEEK && ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded_paged) {\n                pDataBuffer->seekToCursorOnNextRead = MA_TRUE;  /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */\n                return MA_BUSY;\n            }\n\n            return result;\n        }\n    }\n\n    /*\n    For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot\n    exceed this amount. We'll read as much as we can, and then return MA_BUSY.\n    */\n    if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) {\n        ma_uint64 availableFrames;\n\n        isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY);\n\n        if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) {\n            /* Don't try reading more than the available frame count. */\n            if (frameCount > availableFrames) {\n                frameCount = availableFrames;\n\n                /*\n                If there's no frames available we want to set the status to MA_AT_END. The logic below\n                will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this\n                is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count\n                is 0 because that'll result in a situation where it's possible MA_AT_END won't get\n                returned.\n                */\n                if (frameCount == 0) {\n                    result = MA_AT_END;\n                }\n            } else {\n                isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */\n            }\n        }\n    }\n\n    /* Don't attempt to read anything if we've got no frames available. */\n    if (frameCount > 0) {\n        result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead);\n    }\n\n    /*\n    If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound\n    as at the end and terminate decoding.\n    */\n    if (result == MA_AT_END) {\n        if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {\n            result = MA_BUSY;\n        }\n    }\n\n    if (isDecodedBufferBusy) {\n        result = MA_BUSY;\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = framesRead;\n    }\n\n    if (result == MA_SUCCESS && framesRead == 0) {\n        result  = MA_AT_END;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex)\n{\n    ma_result result;\n\n    /* We cannot be using the data source after it's been uninitialized. */\n    MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);\n\n    /* If we haven't yet got a connector we need to abort. */\n    if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {\n        pDataBuffer->seekTargetInPCMFrames = frameIndex;\n        pDataBuffer->seekToCursorOnNextRead = MA_TRUE;\n        return MA_BUSY; /* Still loading. */\n    }\n\n    result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */\n    pDataBuffer->seekToCursorOnNextRead = MA_FALSE;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    /* We cannot be using the data source after it's been uninitialized. */\n    MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);\n\n    switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))\n    {\n        case ma_resource_manager_data_supply_type_encoded:\n        {\n            return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n        };\n\n        case ma_resource_manager_data_supply_type_decoded:\n        {\n            *pFormat     = pDataBuffer->pNode->data.backend.decoded.format;\n            *pChannels   = pDataBuffer->pNode->data.backend.decoded.channels;\n            *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate;\n            ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels);\n            return MA_SUCCESS;\n        };\n\n        case ma_resource_manager_data_supply_type_decoded_paged:\n        {\n            *pFormat     = pDataBuffer->pNode->data.backend.decodedPaged.data.format;\n            *pChannels   = pDataBuffer->pNode->data.backend.decodedPaged.data.channels;\n            *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate;\n            ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels);\n            return MA_SUCCESS;\n        };\n\n        case ma_resource_manager_data_supply_type_unknown:\n        {\n            return MA_BUSY; /* Still loading. */\n        };\n\n        default:\n        {\n            /* Unknown supply type. Should never hit this. */\n            return MA_INVALID_ARGS;\n        }\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor)\n{\n    /* We cannot be using the data source after it's been uninitialized. */\n    MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);\n\n    if (pDataBuffer == NULL || pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;\n\n    switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))\n    {\n        case ma_resource_manager_data_supply_type_encoded:\n        {\n            return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor);\n        };\n\n        case ma_resource_manager_data_supply_type_decoded:\n        {\n            return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor);\n        };\n\n        case ma_resource_manager_data_supply_type_decoded_paged:\n        {\n            return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor);\n        };\n\n        case ma_resource_manager_data_supply_type_unknown:\n        {\n            return MA_BUSY;\n        };\n\n        default:\n        {\n            return MA_INVALID_ARGS;\n        }\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength)\n{\n    /* We cannot be using the data source after it's been uninitialized. */\n    MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);\n\n    if (pDataBuffer == NULL || pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {\n        return MA_BUSY; /* Still loading. */\n    }\n\n    return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength);\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer)\n{\n    if (pDataBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result);    /* Need a naughty const-cast here. */\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping)\n{\n    return ma_data_source_set_looping(pDataBuffer, isLooping);\n}\n\nMA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer)\n{\n    return ma_data_source_is_looping(pDataBuffer);\n}\n\nMA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames)\n{\n    if (pAvailableFrames == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pAvailableFrames = 0;\n\n    if (pDataBuffer == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {\n        if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {\n            return MA_BUSY;\n        } else {\n            return MA_INVALID_OPERATION;    /* No connector. */\n        }\n    }\n\n    switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))\n    {\n        case ma_resource_manager_data_supply_type_encoded:\n        {\n            return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames);\n        };\n\n        case ma_resource_manager_data_supply_type_decoded:\n        {\n            return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames);\n        };\n\n        case ma_resource_manager_data_supply_type_decoded_paged:\n        {\n            ma_uint64 cursor;\n            ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor);\n\n            if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) {\n                *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor;\n            } else {\n                *pAvailableFrames = 0;\n            }\n\n            return MA_SUCCESS;\n        };\n\n        case ma_resource_manager_data_supply_type_unknown:\n        default:\n        {\n            /* Unknown supply type. Should never hit this. */\n            return MA_INVALID_ARGS;\n        }\n    }\n}\n\nMA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags)\n{\n    return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL);\n}\n\nMA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags)\n{\n    return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL);\n}\n\n\nstatic ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData)\n{\n    return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL);\n}\n\nstatic ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)\n{\n    ma_resource_manager_data_supply data;\n    data.type                            = ma_resource_manager_data_supply_type_decoded;\n    data.backend.decoded.pData           = pData;\n    data.backend.decoded.totalFrameCount = frameCount;\n    data.backend.decoded.format          = format;\n    data.backend.decoded.channels        = channels;\n    data.backend.decoded.sampleRate      = sampleRate;\n\n    return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);\n}\n\nMA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)\n{\n    return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate);\n}\n\nMA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)\n{\n    return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate);\n}\n\n\nstatic ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes)\n{\n    ma_resource_manager_data_supply data;\n    data.type                        = ma_resource_manager_data_supply_type_encoded;\n    data.backend.encoded.pData       = pData;\n    data.backend.encoded.sizeInBytes = sizeInBytes;\n\n    return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);\n}\n\nMA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes)\n{\n    return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes);\n}\n\nMA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes)\n{\n    return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes);\n}\n\n\nMA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath)\n{\n    return ma_resource_manager_unregister_data(pResourceManager, pFilePath);\n}\n\nMA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath)\n{\n    return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath);\n}\n\nMA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName)\n{\n    return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL);\n}\n\nMA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName)\n{\n    return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName);\n}\n\n\nstatic ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream)\n{\n    MA_ASSERT(pDataStream != NULL);\n    return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1);\n}\n\nstatic ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream)\n{\n    MA_ASSERT(pDataStream != NULL);\n    return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd);\n}\n\nstatic ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream)\n{\n    MA_ASSERT(pDataStream != NULL);\n    return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter);\n}\n\n\nstatic ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead);\n}\n\nstatic ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex);\n}\n\nstatic ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n}\n\nstatic ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)\n{\n    return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor);\n}\n\nstatic ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)\n{\n    return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength);\n}\n\nstatic ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)\n{\n    ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource;\n    MA_ASSERT(pDataStream != NULL);\n\n    ma_atomic_exchange_32(&pDataStream->isLooping, isLooping);\n\n    return MA_SUCCESS;\n}\n\nstatic ma_data_source_vtable g_ma_resource_manager_data_stream_vtable =\n{\n    ma_resource_manager_data_stream_cb__read_pcm_frames,\n    ma_resource_manager_data_stream_cb__seek_to_pcm_frame,\n    ma_resource_manager_data_stream_cb__get_data_format,\n    ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames,\n    ma_resource_manager_data_stream_cb__get_length_in_pcm_frames,\n    ma_resource_manager_data_stream_cb__set_looping,\n    0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/\n};\n\nstatic void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor)\n{\n    /* Loop if possible. */\n    if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) {\n        absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames;\n    }\n\n    ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor);\n}\n\nMA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream)\n{\n    ma_result result;\n    ma_data_source_config dataSourceConfig;\n    char* pFilePathCopy = NULL;\n    wchar_t* pFilePathWCopy = NULL;\n    ma_job job;\n    ma_bool32 waitBeforeReturning = MA_FALSE;\n    ma_resource_manager_inline_notification waitNotification;\n    ma_resource_manager_pipeline_notifications notifications;\n\n    if (pDataStream == NULL) {\n        if (pConfig != NULL && pConfig->pNotifications != NULL) {\n            ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);\n        }\n\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pDataStream);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->pNotifications != NULL) {\n        notifications = *pConfig->pNotifications;    /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */\n    } else {\n        MA_ZERO_OBJECT(&notifications);\n    }\n\n    dataSourceConfig = ma_data_source_config_init();\n    dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable;\n\n    result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds);\n    if (result != MA_SUCCESS) {\n        ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);\n        return result;\n    }\n\n    pDataStream->pResourceManager = pResourceManager;\n    pDataStream->flags            = pConfig->flags;\n    pDataStream->result           = MA_BUSY;\n\n    ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);\n    ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);\n    ma_data_source_set_looping(pDataStream, pConfig->isLooping);\n\n    if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) {\n        ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);\n        return MA_INVALID_ARGS;\n    }\n\n    /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS.  */\n\n    /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */\n    if (pConfig->pFilePath != NULL) {\n        pFilePathCopy  = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks);\n    } else {\n        pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks);\n    }\n\n    if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {\n        ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);\n        return MA_OUT_OF_MEMORY;\n    }\n\n    /*\n    We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we\n    can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same.\n    */\n    if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {\n        waitBeforeReturning = MA_TRUE;\n        ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification);\n    }\n\n    ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);\n\n    /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */\n    ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames);\n\n    /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */\n    job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM);\n    job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);\n    job.data.resourceManager.loadDataStream.pDataStream       = pDataStream;\n    job.data.resourceManager.loadDataStream.pFilePath         = pFilePathCopy;\n    job.data.resourceManager.loadDataStream.pFilePathW        = pFilePathWCopy;\n    job.data.resourceManager.loadDataStream.initialSeekPoint  = pConfig->initialSeekPointInPCMFrames;\n    job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification;\n    job.data.resourceManager.loadDataStream.pInitFence        = notifications.init.pFence;\n    result = ma_resource_manager_post_job(pResourceManager, &job);\n    if (result != MA_SUCCESS) {\n        ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);\n        ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);\n\n        if (waitBeforeReturning) {\n            ma_resource_manager_inline_notification_uninit(&waitNotification);\n        }\n\n        ma_free(pFilePathCopy,  &pResourceManager->config.allocationCallbacks);\n        ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);\n        return result;\n    }\n\n    /* Wait if needed. */\n    if (waitBeforeReturning) {\n        ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification);\n\n        if (notifications.init.pNotification != NULL) {\n            ma_async_notification_signal(notifications.init.pNotification);\n        }\n\n        /*\n        If there was an error during initialization make sure we return that result here. We don't want to do this\n        if we're not waiting because it will most likely be in a busy state.\n        */\n        if (pDataStream->result != MA_SUCCESS) {\n            return pDataStream->result;\n        }\n\n        /* NOTE: Do not release pInitFence here. That will be done by the job. */\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)\n{\n    ma_resource_manager_data_source_config config;\n\n    config = ma_resource_manager_data_source_config_init();\n    config.pFilePath      = pFilePath;\n    config.flags          = flags;\n    config.pNotifications = pNotifications;\n\n    return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);\n}\n\nMA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)\n{\n    ma_resource_manager_data_source_config config;\n\n    config = ma_resource_manager_data_source_config_init();\n    config.pFilePathW     = pFilePath;\n    config.flags          = flags;\n    config.pNotifications = pNotifications;\n\n    return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);\n}\n\nMA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream)\n{\n    ma_resource_manager_inline_notification freeEvent;\n    ma_job job;\n\n    if (pDataStream == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */\n    ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE);\n\n    /*\n    We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need\n    to wait for it to complete before returning which means we need an event.\n    */\n    ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent);\n\n    job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM);\n    job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);\n    job.data.resourceManager.freeDataStream.pDataStream       = pDataStream;\n    job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent;\n    job.data.resourceManager.freeDataStream.pDoneFence        = NULL;\n    ma_resource_manager_post_job(pDataStream->pResourceManager, &job);\n\n    /* We need to wait for the job to finish processing before we return. */\n    ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent);\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream)\n{\n    MA_ASSERT(pDataStream != NULL);\n    MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);\n\n    return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000);\n}\n\nstatic void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor)\n{\n    MA_ASSERT(pDataStream != NULL);\n    MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);\n    MA_ASSERT(pageIndex == 0 || pageIndex == 1);\n\n    return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels));\n}\n\nstatic void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint64 pageSizeInFrames;\n    ma_uint64 totalFramesReadForThisPage = 0;\n    void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0);\n\n    pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);\n\n    /* The decoder needs to inherit the stream's looping and range state. */\n    {\n        ma_uint64 rangeBeg;\n        ma_uint64 rangeEnd;\n        ma_uint64 loopPointBeg;\n        ma_uint64 loopPointEnd;\n\n        ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream));\n\n        ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd);\n        ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd);\n\n        ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd);\n        ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd);\n    }\n\n    /* Just read straight from the decoder. It will deal with ranges and looping for us. */\n    result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage);\n    if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) {\n        ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE);\n    }\n\n    ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage);\n    ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE);\n}\n\nstatic void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream)\n{\n    ma_uint32 iPage;\n\n    MA_ASSERT(pDataStream != NULL);\n\n    for (iPage = 0; iPage < 2; iPage += 1) {\n        ma_resource_manager_data_stream_fill_page(pDataStream, iPage);\n    }\n}\n\n\nstatic ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount)\n{\n    ma_uint64 framesAvailable;\n    ma_uint64 frameCount = 0;\n\n    /* We cannot be using the data source after it's been uninitialized. */\n    MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);\n\n    if (pFrameCount != NULL) {\n        frameCount = *pFrameCount;\n        *pFrameCount = 0;\n    }\n    if (ppFramesOut != NULL) {\n        *ppFramesOut = NULL;\n    }\n\n    if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */\n    if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {\n        return MA_BUSY;\n    }\n\n    /* If the page we're on is invalid it means we've caught up to the job thread. */\n    if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) {\n        framesAvailable = 0;\n    } else {\n        /*\n        The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is\n        that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler.\n        */\n        ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]);\n        MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor);\n\n        framesAvailable = currentPageFrameCount - pDataStream->relativeCursor;\n    }\n\n    /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */\n    if (framesAvailable == 0) {\n        if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) {\n            return MA_AT_END;\n        } else {\n            return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */\n        }\n    }\n\n    MA_ASSERT(framesAvailable > 0);\n\n    if (frameCount > framesAvailable) {\n        frameCount = framesAvailable;\n    }\n\n    *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor);\n    *pFrameCount = frameCount;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount)\n{\n    ma_uint32 newRelativeCursor;\n    ma_uint32 pageSizeInFrames;\n    ma_job job;\n\n    /* We cannot be using the data source after it's been uninitialized. */\n    MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);\n\n    if (pDataStream == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* The frame count should always fit inside a 32-bit integer. */\n    if (frameCount > 0xFFFFFFFF) {\n        return MA_INVALID_ARGS;\n    }\n\n    pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);\n\n    /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */\n    ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount);\n\n    /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */\n    newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount;\n\n    /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */\n    if (newRelativeCursor >= pageSizeInFrames) {\n        newRelativeCursor -= pageSizeInFrames;\n\n        /* Here is where we post the job start decoding. */\n        job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM);\n        job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);\n        job.data.resourceManager.pageDataStream.pDataStream = pDataStream;\n        job.data.resourceManager.pageDataStream.pageIndex   = pDataStream->currentPageIndex;\n\n        /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */\n        ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE);\n\n        /* Before posting the job we need to make sure we set some state. */\n        pDataStream->relativeCursor   = newRelativeCursor;\n        pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01;\n        return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);\n    } else {\n        /* We haven't moved into a new page so we can just move the cursor forward. */\n        pDataStream->relativeCursor = newRelativeCursor;\n        return MA_SUCCESS;\n    }\n}\n\n\nMA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint64 totalFramesProcessed;\n    ma_format format;\n    ma_uint32 channels;\n\n    /* Safety. */\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    if (frameCount == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* We cannot be using the data source after it's been uninitialized. */\n    MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);\n\n    if (pDataStream == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */\n    if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {\n        return MA_BUSY;\n    }\n\n    ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0);\n\n    /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */\n    totalFramesProcessed = 0;\n    while (totalFramesProcessed < frameCount) {\n        void* pMappedFrames;\n        ma_uint64 mappedFrameCount;\n\n        mappedFrameCount = frameCount - totalFramesProcessed;\n        result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount);\n        if (result != MA_SUCCESS) {\n            break;\n        }\n\n        /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */\n        if (pFramesOut != NULL) {\n            ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels);\n        }\n\n        totalFramesProcessed += mappedFrameCount;\n\n        result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount);\n        if (result != MA_SUCCESS) {\n            break;  /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */\n        }\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = totalFramesProcessed;\n    }\n\n    if (result == MA_SUCCESS && totalFramesProcessed == 0) {\n        result  = MA_AT_END;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex)\n{\n    ma_job job;\n    ma_result streamResult;\n\n    streamResult = ma_resource_manager_data_stream_result(pDataStream);\n\n    /* We cannot be using the data source after it's been uninitialized. */\n    MA_ASSERT(streamResult != MA_UNAVAILABLE);\n\n    if (pDataStream == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */\n    if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) {\n        if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) {\n            return MA_SUCCESS;\n        }\n    }\n\n\n    /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */\n    ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1);\n\n    /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */\n    ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex);\n\n    /*\n    We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public\n    API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of\n    the first page.\n    */\n    pDataStream->relativeCursor   = 0;\n    pDataStream->currentPageIndex = 0;\n    ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE);\n    ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE);\n\n    /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */\n    ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE);\n\n    /*\n    The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages\n    are invalid and any content contained within them will be discarded and replaced with newly decoded data.\n    */\n    job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM);\n    job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);\n    job.data.resourceManager.seekDataStream.pDataStream = pDataStream;\n    job.data.resourceManager.seekDataStream.frameIndex  = frameIndex;\n    return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);\n}\n\nMA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    /* We cannot be using the data source after it's been uninitialized. */\n    MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);\n\n    if (pFormat != NULL) {\n        *pFormat = ma_format_unknown;\n    }\n\n    if (pChannels != NULL) {\n        *pChannels = 0;\n    }\n\n    if (pSampleRate != NULL) {\n        *pSampleRate = 0;\n    }\n\n    if (pChannelMap != NULL) {\n        MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);\n    }\n\n    if (pDataStream == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /*\n    We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function\n    such that the application is responsible for ensuring it's not called while uninitializing so it should be safe.\n    */\n    return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n}\n\nMA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor)\n{\n    ma_result result;\n\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pCursor = 0;\n\n    /* We cannot be using the data source after it's been uninitialized. */\n    MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);\n\n    if (pDataStream == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /*\n    If the stream is in an erroneous state we need to return an invalid operation. We can allow\n    this to be called when the data stream is in a busy state because the caller may have asked\n    for an initial seek position and it's convenient to return that as the cursor position.\n    */\n    result = ma_resource_manager_data_stream_result(pDataStream);\n    if (result != MA_SUCCESS && result != MA_BUSY) {\n        return MA_INVALID_OPERATION;\n    }\n\n    *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength)\n{\n    ma_result streamResult;\n\n    if (pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pLength = 0;\n\n    streamResult = ma_resource_manager_data_stream_result(pDataStream);\n\n    /* We cannot be using the data source after it's been uninitialized. */\n    MA_ASSERT(streamResult != MA_UNAVAILABLE);\n\n    if (pDataStream == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (streamResult != MA_SUCCESS) {\n        return streamResult;\n    }\n\n    /*\n    We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we\n    calculated when we initialized it on the job thread.\n    */\n    *pLength = pDataStream->totalLengthInPCMFrames;\n    if (*pLength == 0) {\n        return MA_NOT_IMPLEMENTED;  /* Some decoders may not have a known length. */\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream)\n{\n    if (pDataStream == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return (ma_result)ma_atomic_load_i32(&pDataStream->result);\n}\n\nMA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping)\n{\n    return ma_data_source_set_looping(pDataStream, isLooping);\n}\n\nMA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream)\n{\n    if (pDataStream == NULL) {\n        return MA_FALSE;\n    }\n\n    return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping);   /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */\n}\n\nMA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames)\n{\n    ma_uint32 pageIndex0;\n    ma_uint32 pageIndex1;\n    ma_uint32 relativeCursor;\n    ma_uint64 availableFrames;\n\n    if (pAvailableFrames == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pAvailableFrames = 0;\n\n    if (pDataStream == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pageIndex0     =  pDataStream->currentPageIndex;\n    pageIndex1     = (pDataStream->currentPageIndex + 1) & 0x01;\n    relativeCursor =  pDataStream->relativeCursor;\n\n    availableFrames = 0;\n    if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) {\n        availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor;\n        if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) {\n            availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]);\n        }\n    }\n\n    *pAvailableFrames = availableFrames;\n    return MA_SUCCESS;\n}\n\n\nstatic ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource)\n{\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pDataSource);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pResourceManager == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pDataSource->flags = pConfig->flags;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource)\n{\n    ma_result result;\n\n    result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* The data source itself is just a data stream or a data buffer. */\n    if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream);\n    } else {\n        return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer);\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource)\n{\n    ma_resource_manager_data_source_config config;\n\n    config = ma_resource_manager_data_source_config_init();\n    config.pFilePath      = pName;\n    config.flags          = flags;\n    config.pNotifications = pNotifications;\n\n    return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource);\n}\n\nMA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource)\n{\n    ma_resource_manager_data_source_config config;\n\n    config = ma_resource_manager_data_source_config_init();\n    config.pFilePathW     = pName;\n    config.flags          = flags;\n    config.pNotifications = pNotifications;\n\n    return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource);\n}\n\nMA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource)\n{\n    ma_result result;\n    ma_resource_manager_data_source_config config;\n\n    if (pExistingDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    config = ma_resource_manager_data_source_config_init();\n    config.flags = pExistingDataSource->flags;\n\n    result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* Copying can only be done from data buffers. Streams cannot be copied. */\n    if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return MA_INVALID_OPERATION;\n    }\n\n    return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer);\n}\n\nMA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource)\n{\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* All we need to is uninitialize the underlying data buffer or data stream. */\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream);\n    } else {\n        return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer);\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    /* Safety. */\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead);\n    } else {\n        return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead);\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex)\n{\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex);\n    } else {\n        return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex);\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount)\n{\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount);\n    } else {\n        return MA_NOT_IMPLEMENTED;  /* Mapping not supported with data buffers. */\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount)\n{\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount);\n    } else {\n        return MA_NOT_IMPLEMENTED;  /* Mapping not supported with data buffers. */\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n    } else {\n        return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor)\n{\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor);\n    } else {\n        return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor);\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength)\n{\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength);\n    } else {\n        return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength);\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource)\n{\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_result(&pDataSource->backend.stream);\n    } else {\n        return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer);\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping)\n{\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping);\n    } else {\n        return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping);\n    }\n}\n\nMA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource)\n{\n    if (pDataSource == NULL) {\n        return MA_FALSE;\n    }\n\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream);\n    } else {\n        return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer);\n    }\n}\n\nMA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames)\n{\n    if (pAvailableFrames == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pAvailableFrames = 0;\n\n    if (pDataSource == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {\n        return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames);\n    } else {\n        return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames);\n    }\n}\n\n\nMA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob)\n{\n    if (pResourceManager == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_job_queue_post(&pResourceManager->jobQueue, pJob);\n}\n\nMA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager)\n{\n    ma_job job = ma_job_init(MA_JOB_TYPE_QUIT);\n    return ma_resource_manager_post_job(pResourceManager, &job);\n}\n\nMA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob)\n{\n    if (pResourceManager == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_job_queue_next(&pResourceManager->jobQueue, pJob);\n}\n\n\nstatic ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob)\n{\n    ma_result result = MA_SUCCESS;\n    ma_resource_manager* pResourceManager;\n    ma_resource_manager_data_buffer_node* pDataBufferNode;\n\n    MA_ASSERT(pJob != NULL);\n\n    pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager;\n    MA_ASSERT(pResourceManager != NULL);\n\n    pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode;\n    MA_ASSERT(pDataBufferNode != NULL);\n    MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE);  /* The data should always be owned by the resource manager. */\n\n    /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */\n    if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {\n        return ma_resource_manager_post_job(pResourceManager, pJob);    /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */\n    }\n\n    /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */\n    if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) {\n        result = ma_resource_manager_data_buffer_node_result(pDataBufferNode);    /* The data buffer may be getting deleted before it's even been loaded. */\n        goto done;\n    }\n\n    /*\n    We're ready to start loading. Essentially what we're doing here is initializing the data supply\n    of the node. Once this is complete, data buffers can have their connectors initialized which\n    will allow then to have audio data read from them.\n\n    Note that when the data supply type has been moved away from \"unknown\", that is when other threads\n    will determine that the node is available for data delivery and the data buffer connectors can be\n    initialized. Therefore, it's important that it is set after the data supply has been initialized.\n    */\n    if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) {\n        /*\n        Decoding. This is the complex case because we're not going to be doing the entire decoding\n        process here. Instead it's going to be split of multiple jobs and loaded in pages. The\n        reason for this is to evenly distribute decoding time across multiple sounds, rather than\n        having one huge sound hog all the available processing resources.\n\n        The first thing we do is initialize a decoder. This is allocated on the heap and is passed\n        around to the paging jobs. When the last paging job has completed it's processing, it'll\n        free the decoder for us.\n\n        This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job\n        which is where the actual decoding work will be done. However, once this job is complete,\n        the node will be in a state where data buffer connectors can be initialized.\n        */\n        ma_decoder* pDecoder;   /* <-- Free'd on the last page decode. */\n        ma_job pageDataBufferNodeJob;\n\n        /* Allocate the decoder by initializing a decoded data supply. */\n        result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder);\n\n        /*\n        Don't ever propagate an MA_BUSY result code or else the resource manager will think the\n        node is just busy decoding rather than in an error state. This should never happen, but\n        including this logic for safety just in case.\n        */\n        if (result == MA_BUSY) {\n            result  = MA_ERROR;\n        }\n\n        if (result != MA_SUCCESS) {\n            if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) {\n                ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, \"Failed to initialize data supply for \\\"%s\\\". %s.\\n\", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result));\n            } else {\n                #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)\n                    ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, \"Failed to initialize data supply for \\\"%ls\\\", %s.\\n\", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result));\n                #endif\n            }\n\n            goto done;\n        }\n\n        /*\n        At this point the node's data supply is initialized and other threads can start initializing\n        their data buffer connectors. However, no data will actually be available until we start to\n        actually decode it. To do this, we need to post a paging job which is where the decoding\n        work is done.\n\n        Note that if an error occurred at an earlier point, this section will have been skipped.\n        */\n        pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE);\n        pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);\n        pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager  = pResourceManager;\n        pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode   = pDataBufferNode;\n        pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder          = pDecoder;\n        pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification;\n        pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence        = pJob->data.resourceManager.loadDataBufferNode.pDoneFence;\n\n        /* The job has been set up so it can now be posted. */\n        result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob);\n\n        /*\n        When we get here, we want to make sure the result code is set to MA_BUSY. The reason for\n        this is that the result will be copied over to the node's internal result variable. In\n        this case, since the decoding is still in-progress, we need to make sure the result code\n        is set to MA_BUSY.\n        */\n        if (result != MA_SUCCESS) {\n            ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, \"Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\\n\", ma_result_description(result));\n            ma_decoder_uninit(pDecoder);\n            ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);\n        } else {\n            result = MA_BUSY;\n        }\n    } else {\n        /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */\n        result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW);\n    }\n\n\ndone:\n    /* File paths are no longer needed. */\n    ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath,  &pResourceManager->config.allocationCallbacks);\n    ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks);\n\n    /*\n    We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads\n    are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY\n    because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then\n    immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any\n    other error code would cause the buffer to look like it's in a state that it's not.\n    */\n    ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);\n\n    /* At this point initialization is complete and we can signal the notification if any. */\n    if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) {\n        ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification);\n    }\n    if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) {\n        ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence);\n    }\n\n    /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */\n    if (result != MA_BUSY) {\n        if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) {\n            ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification);\n        }\n        if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) {\n            ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence);\n        }\n    }\n\n    /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */\n    ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);\n\n    /* A busy result should be considered successful from the point of view of the job system. */\n    if (result == MA_BUSY) {\n        result  = MA_SUCCESS;\n    }\n\n    return result;\n}\n\nstatic ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob)\n{\n    ma_resource_manager* pResourceManager;\n    ma_resource_manager_data_buffer_node* pDataBufferNode;\n\n    MA_ASSERT(pJob != NULL);\n\n    pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager;\n    MA_ASSERT(pResourceManager != NULL);\n\n    pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode;\n    MA_ASSERT(pDataBufferNode != NULL);\n\n    if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {\n        return ma_resource_manager_post_job(pResourceManager, pJob);    /* Out of order. */\n    }\n\n    ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);\n\n    /* The event needs to be signalled last. */\n    if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) {\n        ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification);\n    }\n\n    if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) {\n        ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence);\n    }\n\n    ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob)\n{\n    ma_result result = MA_SUCCESS;\n    ma_resource_manager* pResourceManager;\n    ma_resource_manager_data_buffer_node* pDataBufferNode;\n\n    MA_ASSERT(pJob != NULL);\n\n    pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager;\n    MA_ASSERT(pResourceManager != NULL);\n\n    pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode;\n    MA_ASSERT(pDataBufferNode != NULL);\n\n    if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {\n        return ma_resource_manager_post_job(pResourceManager, pJob);    /* Out of order. */\n    }\n\n    /* Don't do any more decoding if the data buffer has started the uninitialization process. */\n    result = ma_resource_manager_data_buffer_node_result(pDataBufferNode);\n    if (result != MA_BUSY) {\n        goto done;\n    }\n\n    /* We're ready to decode the next page. */\n    result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);\n\n    /*\n    If we have a success code by this point, we want to post another job. We're going to set the\n    result back to MA_BUSY to make it clear that there's still more to load.\n    */\n    if (result == MA_SUCCESS) {\n        ma_job newJob;\n        newJob = *pJob; /* Everything is the same as the input job, except the execution order. */\n        newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);   /* We need a fresh execution order. */\n\n        result = ma_resource_manager_post_job(pResourceManager, &newJob);\n\n        /* Since the sound isn't yet fully decoded we want the status to be set to busy. */\n        if (result == MA_SUCCESS) {\n            result  = MA_BUSY;\n        }\n    }\n\ndone:\n    /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */\n    if (result != MA_BUSY) {\n        ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);\n        ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks);\n    }\n\n    /* If we reached the end we need to treat it as successful. */\n    if (result == MA_AT_END) {\n        result  = MA_SUCCESS;\n    }\n\n    /* Make sure we set the result of node in case some error occurred. */\n    ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);\n\n    /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */\n    if (result != MA_BUSY) {\n        if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) {\n            ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification);\n        }\n\n        if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) {\n            ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence);\n        }\n    }\n\n    ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);\n    return result;\n}\n\n\nstatic ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob)\n{\n    ma_result result = MA_SUCCESS;\n    ma_resource_manager* pResourceManager;\n    ma_resource_manager_data_buffer* pDataBuffer;\n    ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown;\n    ma_bool32 isConnectorInitialized = MA_FALSE;\n\n    /*\n    All we're doing here is checking if the node has finished loading. If not, we just re-post the job\n    and keep waiting. Otherwise we increment the execution counter and set the buffer's result code.\n    */\n    MA_ASSERT(pJob != NULL);\n\n    pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer;\n    MA_ASSERT(pDataBuffer != NULL);\n\n    pResourceManager = pDataBuffer->pResourceManager;\n\n    if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) {\n        return ma_resource_manager_post_job(pResourceManager, pJob);    /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */\n    }\n\n    /*\n    First thing we need to do is check whether or not the data buffer is getting deleted. If so we\n    just abort, but making sure we increment the execution pointer.\n    */\n    result = ma_resource_manager_data_buffer_result(pDataBuffer);\n    if (result != MA_BUSY) {\n        goto done;  /* <-- This will ensure the exucution pointer is incremented. */\n    } else {\n        result = MA_SUCCESS;    /* <-- Make sure this is reset. */\n    }\n\n    /* Try initializing the connector if we haven't already. */\n    isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer);\n    if (isConnectorInitialized == MA_FALSE) {\n        dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode);\n\n        if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) {\n            /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */\n            ma_resource_manager_data_source_config dataSourceConfig;    /* For setting initial looping state and range. */\n            dataSourceConfig = ma_resource_manager_data_source_config_init();\n            dataSourceConfig.rangeBegInPCMFrames     = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames;\n            dataSourceConfig.rangeEndInPCMFrames     = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames;\n            dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames;\n            dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames;\n            dataSourceConfig.isLooping               = pJob->data.resourceManager.loadDataBuffer.isLooping;\n\n            result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence);\n            if (result != MA_SUCCESS) {\n                ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, \"Failed to initialize connector for data buffer. %s.\\n\", ma_result_description(result));\n                goto done;\n            }\n        } else {\n            /* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */\n        }\n    } else {\n        /* The connector is already initialized. Nothing to do here. */\n    }\n\n    /*\n    If the data node is still loading, we need to repost the job and *not* increment the execution\n    pointer (i.e. we need to not fall through to the \"done\" label).\n\n    There is a hole between here and the where the data connector is initialized where the data\n    buffer node may have finished initializing. We need to check for this by checking the result of\n    the data buffer node and whether or not we had an unknown data supply type at the time of\n    trying to initialize the data connector.\n    */\n    result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);\n    if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) {\n        return ma_resource_manager_post_job(pResourceManager, pJob);\n    }\n\ndone:\n    /* Only move away from a busy code so that we don't trash any existing error codes. */\n    ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result);\n\n    /* Only signal the other threads after the result has been set just for cleanliness sake. */\n    if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) {\n        ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification);\n    }\n    if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) {\n        ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence);\n    }\n\n    /*\n    If at this point the data buffer has not had it's connector initialized, it means the\n    notification event was never signalled which means we need to signal it here.\n    */\n    if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) {\n        if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) {\n            ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification);\n        }\n        if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) {\n            ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence);\n        }\n    }\n\n    ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);\n    return result;\n}\n\nstatic ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob)\n{\n    ma_resource_manager* pResourceManager;\n    ma_resource_manager_data_buffer* pDataBuffer;\n\n    MA_ASSERT(pJob != NULL);\n\n    pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer;\n    MA_ASSERT(pDataBuffer != NULL);\n\n    pResourceManager = pDataBuffer->pResourceManager;\n\n    if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) {\n        return ma_resource_manager_post_job(pResourceManager, pJob);    /* Out of order. */\n    }\n\n    ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);\n\n    /* The event needs to be signalled last. */\n    if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) {\n        ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification);\n    }\n\n    if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) {\n        ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence);\n    }\n\n    ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob)\n{\n    ma_result result = MA_SUCCESS;\n    ma_decoder_config decoderConfig;\n    ma_uint32 pageBufferSizeInBytes;\n    ma_resource_manager* pResourceManager;\n    ma_resource_manager_data_stream* pDataStream;\n\n    MA_ASSERT(pJob != NULL);\n\n    pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream;\n    MA_ASSERT(pDataStream != NULL);\n\n    pResourceManager = pDataStream->pResourceManager;\n\n    if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {\n        return ma_resource_manager_post_job(pResourceManager, pJob);    /* Out of order. */\n    }\n\n    if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) {\n        result = MA_INVALID_OPERATION;  /* Most likely the data stream is being uninitialized. */\n        goto done;\n    }\n\n    /* We need to initialize the decoder first so we can determine the size of the pages. */\n    decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager);\n\n    if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) {\n        result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder);\n    } else {\n        result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder);\n    }\n    if (result != MA_SUCCESS) {\n        goto done;\n    }\n\n    /* Retrieve the total length of the file before marking the decoder as loaded. */\n    if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) {\n        result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames);\n        if (result != MA_SUCCESS) {\n            goto done;  /* Failed to retrieve the length. */\n        }\n    } else {\n        pDataStream->totalLengthInPCMFrames = 0;\n    }\n\n    /*\n    Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file\n    and we don't want to have another thread trying to access the decoder while it's scanning.\n    */\n    pDataStream->isDecoderInitialized = MA_TRUE;\n\n    /* We have the decoder so we can now initialize our page buffer. */\n    pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels);\n\n    pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks);\n    if (pDataStream->pPageData == NULL) {\n        ma_decoder_uninit(&pDataStream->decoder);\n        result = MA_OUT_OF_MEMORY;\n        goto done;\n    }\n\n    /* Seek to our initial seek point before filling the initial pages. */\n    ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint);\n\n    /* We have our decoder and our page buffer, so now we need to fill our pages. */\n    ma_resource_manager_data_stream_fill_pages(pDataStream);\n\n    /* And now we're done. We want to make sure the result is MA_SUCCESS. */\n    result = MA_SUCCESS;\n\ndone:\n    ma_free(pJob->data.resourceManager.loadDataStream.pFilePath,  &pResourceManager->config.allocationCallbacks);\n    ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks);\n\n    /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */\n    ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result);\n\n    /* Only signal the other threads after the result has been set just for cleanliness sake. */\n    if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) {\n        ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification);\n    }\n    if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) {\n        ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence);\n    }\n\n    ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);\n    return result;\n}\n\nstatic ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob)\n{\n    ma_resource_manager* pResourceManager;\n    ma_resource_manager_data_stream* pDataStream;\n\n    MA_ASSERT(pJob != NULL);\n\n    pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream;\n    MA_ASSERT(pDataStream != NULL);\n\n    pResourceManager = pDataStream->pResourceManager;\n\n    if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {\n        return ma_resource_manager_post_job(pResourceManager, pJob);    /* Out of order. */\n    }\n\n    /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */\n    MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE);\n\n    if (pDataStream->isDecoderInitialized) {\n        ma_decoder_uninit(&pDataStream->decoder);\n    }\n\n    if (pDataStream->pPageData != NULL) {\n        ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks);\n        pDataStream->pPageData = NULL;  /* Just in case... */\n    }\n\n    ma_data_source_uninit(&pDataStream->ds);\n\n    /* The event needs to be signalled last. */\n    if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) {\n        ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification);\n    }\n    if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) {\n        ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence);\n    }\n\n    /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob)\n{\n    ma_result result = MA_SUCCESS;\n    ma_resource_manager* pResourceManager;\n    ma_resource_manager_data_stream* pDataStream;\n\n    MA_ASSERT(pJob != NULL);\n\n    pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream;\n    MA_ASSERT(pDataStream != NULL);\n\n    pResourceManager = pDataStream->pResourceManager;\n\n    if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {\n        return ma_resource_manager_post_job(pResourceManager, pJob);    /* Out of order. */\n    }\n\n    /* For streams, the status should be MA_SUCCESS. */\n    if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {\n        result = MA_INVALID_OPERATION;\n        goto done;\n    }\n\n    ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex);\n\ndone:\n    ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);\n    return result;\n}\n\nstatic ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob)\n{\n    ma_result result = MA_SUCCESS;\n    ma_resource_manager* pResourceManager;\n    ma_resource_manager_data_stream* pDataStream;\n\n    MA_ASSERT(pJob != NULL);\n\n    pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream;\n    MA_ASSERT(pDataStream != NULL);\n\n    pResourceManager = pDataStream->pResourceManager;\n\n    if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {\n        return ma_resource_manager_post_job(pResourceManager, pJob);    /* Out of order. */\n    }\n\n    /* For streams the status should be MA_SUCCESS for this to do anything. */\n    if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) {\n        result = MA_INVALID_OPERATION;\n        goto done;\n    }\n\n    /*\n    With seeking we just assume both pages are invalid and the relative frame cursor at position 0. This is basically exactly the same as loading, except\n    instead of initializing the decoder, we seek to a frame.\n    */\n    ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex);\n\n    /* After seeking we'll need to reload the pages. */\n    ma_resource_manager_data_stream_fill_pages(pDataStream);\n\n    /* We need to let the public API know that we're done seeking. */\n    ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1);\n\ndone:\n    ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);\n    return result;\n}\n\nMA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob)\n{\n    if (pResourceManager == NULL || pJob == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_job_process(pJob);\n}\n\nMA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager)\n{\n    ma_result result;\n    ma_job job;\n\n    if (pResourceManager == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* This will return MA_CANCELLED if the next job is a quit job. */\n    result = ma_resource_manager_next_job(pResourceManager, &job);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return ma_job_process(&job);\n}\n#else\n/* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */\nstatic ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }\nstatic ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }\nstatic ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }\nstatic ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob)      { return ma_job_process__noop(pJob); }\nstatic ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob)      { return ma_job_process__noop(pJob); }\nstatic ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob)      { return ma_job_process__noop(pJob); }\nstatic ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob)      { return ma_job_process__noop(pJob); }\nstatic ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob)      { return ma_job_process__noop(pJob); }\nstatic ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob)      { return ma_job_process__noop(pJob); }\n#endif  /* MA_NO_RESOURCE_MANAGER */\n\n\n#ifndef MA_NO_NODE_GRAPH\n/* 10ms @ 48K = 480. Must never exceed 65535. */\n#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS\n#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480\n#endif\n\n\nstatic ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime);\n\nMA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)\n{\n    #ifndef MA_NO_GENERATION\n    {\n        ma_waveform_config waveformConfig;\n        ma_waveform waveform;\n\n        waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400);\n        ma_waveform_init(&waveformConfig, &waveform);\n        ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL);\n    }\n    #else\n    {\n        (void)pFramesOut;\n        (void)frameCount;\n        (void)format;\n        (void)channels;\n        (void)sampleRate;\n        #if defined(MA_DEBUG_OUTPUT)\n        {\n            #if _MSC_VER\n                #pragma message (\"ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.\")\n            #endif\n        }\n        #endif\n    }\n    #endif\n}\n\n\n\nMA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels)\n{\n    ma_node_graph_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.channels             = channels;\n    config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;\n\n    return config;\n}\n\n\nstatic void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading)\n{\n    MA_ASSERT(pNodeGraph != NULL);\n    ma_atomic_exchange_32(&pNodeGraph->isReading, isReading);\n}\n\n#if 0\nstatic ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph)\n{\n    MA_ASSERT(pNodeGraph != NULL);\n    return ma_atomic_load_32(&pNodeGraph->isReading);\n}\n#endif\n\n\nstatic void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_node_graph* pNodeGraph = (ma_node_graph*)pNode;\n    ma_uint64 framesRead;\n\n    ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead);\n\n    *pFrameCountOut = (ma_uint32)framesRead;    /* Safe cast. */\n\n    (void)ppFramesIn;\n    (void)pFrameCountIn;\n}\n\nstatic ma_node_vtable g_node_graph_node_vtable =\n{\n    ma_node_graph_node_process_pcm_frames,\n    NULL,   /* onGetRequiredInputFrameCount */\n    0,      /* 0 input buses. */\n    1,      /* 1 output bus. */\n    0       /* Flags. */\n};\n\nstatic void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    MA_ASSERT(pNode != NULL);\n    MA_ASSERT(ma_node_get_input_bus_count(pNode)  == 1);\n    MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1);\n\n    /* Input channel count needs to be the same as the output channel count. */\n    MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0));\n\n    /* We don't need to do anything here because it's a passthrough. */\n    (void)pNode;\n    (void)ppFramesIn;\n    (void)pFrameCountIn;\n    (void)ppFramesOut;\n    (void)pFrameCountOut;\n\n#if 0\n    /* The data has already been mixed. We just need to move it to the output buffer. */\n    if (ppFramesIn != NULL) {\n        ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0));\n    }\n#endif\n}\n\nstatic ma_node_vtable g_node_graph_endpoint_vtable =\n{\n    ma_node_graph_endpoint_process_pcm_frames,\n    NULL,   /* onGetRequiredInputFrameCount */\n    1,      /* 1 input bus. */\n    1,      /* 1 output bus. */\n    MA_NODE_FLAG_PASSTHROUGH    /* Flags. The endpoint is a passthrough. */\n};\n\nMA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph)\n{\n    ma_result result;\n    ma_node_config baseConfig;\n    ma_node_config endpointConfig;\n\n    if (pNodeGraph == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pNodeGraph);\n    pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames;\n    if (pNodeGraph->nodeCacheCapInFrames == 0) {\n        pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;\n    }\n\n\n    /* Base node so we can use the node graph as a node into another graph. */\n    baseConfig = ma_node_config_init();\n    baseConfig.vtable = &g_node_graph_node_vtable;\n    baseConfig.pOutputChannels = &pConfig->channels;\n\n    result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n\n    /* Endpoint. */\n    endpointConfig = ma_node_config_init();\n    endpointConfig.vtable          = &g_node_graph_endpoint_vtable;\n    endpointConfig.pInputChannels  = &pConfig->channels;\n    endpointConfig.pOutputChannels = &pConfig->channels;\n\n    result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint);\n    if (result != MA_SUCCESS) {\n        ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pNodeGraph == NULL) {\n        return;\n    }\n\n    ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);\n}\n\nMA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph)\n{\n    if (pNodeGraph == NULL) {\n        return NULL;\n    }\n\n    return &pNodeGraph->endpoint;\n}\n\nMA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint64 totalFramesRead;\n    ma_uint32 channels;\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;   /* Safety. */\n    }\n\n    if (pNodeGraph == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0);\n\n\n    /* We'll be nice and try to do a full read of all frameCount frames. */\n    totalFramesRead = 0;\n    while (totalFramesRead < frameCount) {\n        ma_uint32 framesJustRead;\n        ma_uint64 framesToRead = frameCount - totalFramesRead;\n\n        if (framesToRead > 0xFFFFFFFF) {\n            framesToRead = 0xFFFFFFFF;\n        }\n\n        ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE);\n        {\n            result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint));\n        }\n        ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE);\n\n        totalFramesRead += framesJustRead;\n\n        if (result != MA_SUCCESS) {\n            break;\n        }\n\n        /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */\n        if (framesJustRead == 0) {\n            break;\n        }\n    }\n\n    /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */\n    if (totalFramesRead < frameCount) {\n        ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels);\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = totalFramesRead;\n    }\n\n    return result;\n}\n\nMA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph)\n{\n    if (pNodeGraph == NULL) {\n        return 0;\n    }\n\n    return ma_node_get_output_channels(&pNodeGraph->endpoint, 0);\n}\n\nMA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph)\n{\n    if (pNodeGraph == NULL) {\n        return 0;\n    }\n\n    return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */\n}\n\nMA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime)\n{\n    if (pNodeGraph == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */\n}\n\n\n#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ    0x01    /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */\n\nstatic ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus)\n{\n    MA_ASSERT(pOutputBus != NULL);\n    MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT);\n    MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode));\n    MA_ASSERT(channels < 256);\n\n    MA_ZERO_OBJECT(pOutputBus);\n\n    if (channels == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pOutputBus->pNode          = pNode;\n    pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex;\n    pOutputBus->channels       = (ma_uint8)channels;\n    pOutputBus->flags          = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */\n    pOutputBus->volume         = 1;\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus)\n{\n    ma_spinlock_lock(&pOutputBus->lock);\n}\n\nstatic void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus)\n{\n    ma_spinlock_unlock(&pOutputBus->lock);\n}\n\n\nstatic ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus)\n{\n    return pOutputBus->channels;\n}\n\n\nstatic void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead)\n{\n    if (hasRead) {\n        ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);\n    } else {\n        ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);\n    }\n}\n\nstatic ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus)\n{\n    return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0;\n}\n\n\nstatic void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached)\n{\n    ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached);\n}\n\nstatic ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus)\n{\n    return ma_atomic_load_32(&pOutputBus->isAttached);\n}\n\n\nstatic ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume)\n{\n    MA_ASSERT(pOutputBus != NULL);\n\n    if (volume < 0.0f) {\n        volume = 0.0f;\n    }\n\n    ma_atomic_exchange_f32(&pOutputBus->volume, volume);\n\n    return MA_SUCCESS;\n}\n\nstatic float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus)\n{\n    return ma_atomic_load_f32((float*)&pOutputBus->volume);\n}\n\n\nstatic ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus)\n{\n    MA_ASSERT(pInputBus != NULL);\n    MA_ASSERT(channels < 256);\n\n    MA_ZERO_OBJECT(pInputBus);\n\n    if (channels == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pInputBus->channels = (ma_uint8)channels;\n\n    return MA_SUCCESS;\n}\n\nstatic void ma_node_input_bus_lock(ma_node_input_bus* pInputBus)\n{\n    MA_ASSERT(pInputBus != NULL);\n\n    ma_spinlock_lock(&pInputBus->lock);\n}\n\nstatic void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus)\n{\n    MA_ASSERT(pInputBus != NULL);\n\n    ma_spinlock_unlock(&pInputBus->lock);\n}\n\n\nstatic void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus)\n{\n    ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1);\n}\n\nstatic void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus)\n{\n    ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1);\n}\n\nstatic ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus)\n{\n    return ma_atomic_load_32(&pInputBus->nextCounter);\n}\n\n\nstatic ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus)\n{\n    return pInputBus->channels;\n}\n\n\nstatic void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)\n{\n    MA_ASSERT(pInputBus  != NULL);\n    MA_ASSERT(pOutputBus != NULL);\n\n    /*\n    Mark the output bus as detached first. This will prevent future iterations on the audio thread\n    from iterating this output bus.\n    */\n    ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE);\n\n    /*\n    We cannot use the output bus lock here since it'll be getting used at a higher level, but we do\n    still need to use the input bus lock since we'll be updating pointers on two different output\n    buses. The same rules apply here as the attaching case. Although we're using a lock here, we're\n    *not* using a lock when iterating over the list in the audio thread. We therefore need to craft\n    this in a way such that the iteration on the audio thread doesn't break.\n\n    The the first thing to do is swap out the \"next\" pointer of the previous output bus with the\n    new \"next\" output bus. This is the operation that matters for iteration on the audio thread.\n    After that, the previous pointer on the new \"next\" pointer needs to be updated, after which\n    point the linked list will be in a good state.\n    */\n    ma_node_input_bus_lock(pInputBus);\n    {\n        ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev);\n        ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext);\n\n        if (pOldPrev != NULL) {\n            ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */\n        }\n        if (pOldNext != NULL) {\n            ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */\n        }\n    }\n    ma_node_input_bus_unlock(pInputBus);\n\n    /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */\n    ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL);   /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */\n    ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL);   /* As above. */\n    pOutputBus->pInputNode             = NULL;\n    pOutputBus->inputNodeInputBusIndex = 0;\n\n\n    /*\n    For thread-safety reasons, we don't want to be returning from this straight away. We need to\n    wait for the audio thread to finish with the output bus. There's two things we need to wait\n    for. The first is the part that selects the next output bus in the list, and the other is the\n    part that reads from the output bus. Basically all we're doing is waiting for the input bus\n    to stop referencing the output bus.\n\n    We're doing this part last because we want the section above to run while the audio thread\n    is finishing up with the output bus, just for efficiency reasons. We marked the output bus as\n    detached right at the top of this function which is going to prevent the audio thread from\n    iterating the output bus again.\n    */\n\n    /* Part 1: Wait for the current iteration to complete. */\n    while (ma_node_input_bus_get_next_counter(pInputBus) > 0) {\n        ma_yield();\n    }\n\n    /* Part 2: Wait for any reads to complete. */\n    while (ma_atomic_load_32(&pOutputBus->refCount) > 0) {\n        ma_yield();\n    }\n\n    /*\n    At this point we're done detaching and we can be guaranteed that the audio thread is not going\n    to attempt to reference this output bus again (until attached again).\n    */\n}\n\n#if 0   /* Not used at the moment, but leaving here in case I need it later. */\nstatic void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)\n{\n    MA_ASSERT(pInputBus  != NULL);\n    MA_ASSERT(pOutputBus != NULL);\n\n    ma_node_output_bus_lock(pOutputBus);\n    {\n        ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);\n    }\n    ma_node_output_bus_unlock(pOutputBus);\n}\n#endif\n\nstatic void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex)\n{\n    MA_ASSERT(pInputBus  != NULL);\n    MA_ASSERT(pOutputBus != NULL);\n\n    ma_node_output_bus_lock(pOutputBus);\n    {\n        ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode);\n\n        /* Detach from any existing attachment first if necessary. */\n        if (pOldInputNode != NULL) {\n            ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);\n        }\n\n        /*\n        At this point we can be sure the output bus is not attached to anything. The linked list in the\n        old input bus has been updated so that pOutputBus will not get iterated again.\n        */\n        pOutputBus->pInputNode             = pNewInputNode;                     /* No need for an atomic assignment here because modification of this variable always happens within a lock. */\n        pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex;\n\n        /*\n        Now we need to attach the output bus to the linked list. This involves updating two pointers on\n        two different output buses so I'm going to go ahead and keep this simple and just use a lock.\n        There are ways to do this without a lock, but it's just too hard to maintain for it's value.\n\n        Although we're locking here, it's important to remember that we're *not* locking when iterating\n        and reading audio data since that'll be running on the audio thread. As a result we need to be\n        careful how we craft this so that we don't break iteration. What we're going to do is always\n        attach the new item so that it becomes the first item in the list. That way, as we're iterating\n        we won't break any links in the list and iteration will continue safely. The detaching case will\n        also be crafted in a way as to not break list iteration. It's important to remember to use\n        atomic exchanges here since no locking is happening on the audio thread during iteration.\n        */\n        ma_node_input_bus_lock(pInputBus);\n        {\n            ma_node_output_bus* pNewPrev = &pInputBus->head;\n            ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext);\n\n            /* Update the local output bus. */\n            ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev);\n            ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext);\n\n            /* Update the other output buses to point back to the local output bus. */\n            ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */\n\n            /* Do the previous pointer last. This is only used for detachment. */\n            if (pNewNext != NULL) {\n                ma_atomic_exchange_ptr(&pNewNext->pPrev,  pOutputBus);\n            }\n        }\n        ma_node_input_bus_unlock(pInputBus);\n\n        /*\n        Mark the node as attached last. This is used to controlling whether or the output bus will be\n        iterated on the audio thread. Mainly required for detachment purposes.\n        */\n        ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE);\n    }\n    ma_node_output_bus_unlock(pOutputBus);\n}\n\nstatic ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)\n{\n    ma_node_output_bus* pNext;\n\n    MA_ASSERT(pInputBus != NULL);\n\n    if (pOutputBus == NULL) {\n        return NULL;\n    }\n\n    ma_node_input_bus_next_begin(pInputBus);\n    {\n        pNext = pOutputBus;\n        for (;;) {\n            pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext);\n            if (pNext == NULL) {\n                break;      /* Reached the end. */\n            }\n\n            if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) {\n                continue;   /* The node is not attached. Keep checking. */\n            }\n\n            /* The next node has been selected. */\n            break;\n        }\n\n        /* We need to increment the reference count of the selected node. */\n        if (pNext != NULL) {\n            ma_atomic_fetch_add_32(&pNext->refCount, 1);\n        }\n\n        /* The previous node is no longer being referenced. */\n        ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1);\n    }\n    ma_node_input_bus_next_end(pInputBus);\n\n    return pNext;\n}\n\nstatic ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus)\n{\n    return ma_node_input_bus_next(pInputBus, &pInputBus->head);\n}\n\n\n\nstatic ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime)\n{\n    ma_result result = MA_SUCCESS;\n    ma_node_output_bus* pOutputBus;\n    ma_node_output_bus* pFirst;\n    ma_uint32 inputChannels;\n    ma_bool32 doesOutputBufferHaveContent = MA_FALSE;\n\n    (void)pInputNode;   /* Not currently used. */\n\n    /*\n    This will be called from the audio thread which means we can't be doing any locking. Basically,\n    this function will not perfom any locking, whereas attaching and detaching will, but crafted in\n    such a way that we don't need to perform any locking here. The important thing to remember is\n    to always iterate in a forward direction.\n\n    In order to process any data we need to first read from all input buses. That's where this\n    function comes in. This iterates over each of the attachments and accumulates/mixes them. We\n    also convert the channels to the nodes output channel count before mixing. We want to do this\n    channel conversion so that the caller of this function can invoke the processing callback\n    without having to do it themselves.\n\n    When we iterate over each of the attachments on the input bus, we need to read as much data as\n    we can from each of them so that we don't end up with holes between each of the attachments. To\n    do this, we need to read from each attachment in a loop and read as many frames as we can, up\n    to `frameCount`.\n    */\n    MA_ASSERT(pInputNode  != NULL);\n    MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */\n\n    *pFramesRead = 0;   /* Safety. */\n\n    inputChannels = ma_node_input_bus_get_channels(pInputBus);\n\n    /*\n    We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They\n    are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first()\n    once per iteration, however we have an optimization to checks whether or not it's the first item in\n    the list. We therefore need to store a pointer to the first item rather than repeatedly calling\n    ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it\n    after calling ma_node_input_bus_next(), which we won't be.\n    */\n    pFirst = ma_node_input_bus_first(pInputBus);\n    if (pFirst == NULL) {\n        return MA_SUCCESS;  /* No attachments. Read nothing. */\n    }\n\n    for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) {\n        ma_uint32 framesProcessed = 0;\n        ma_bool32 isSilentOutput = MA_FALSE;\n\n        MA_ASSERT(pOutputBus->pNode != NULL);\n        MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL);\n\n        isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0;\n\n        if (pFramesOut != NULL) {\n            /* Read. */\n            float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];\n            ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels;\n\n            while (framesProcessed < frameCount) {\n                float* pRunningFramesOut;\n                ma_uint32 framesToRead;\n                ma_uint32 framesJustRead;\n\n                framesToRead = frameCount - framesProcessed;\n                if (framesToRead > tempCapInFrames) {\n                    framesToRead = tempCapInFrames;\n                }\n\n                pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels);\n\n                if (doesOutputBufferHaveContent == MA_FALSE) {\n                    /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */\n                    result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed);\n                } else {\n                    /* Slow path. Not the first attachment. Mixing required. */\n                    result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed);\n                    if (result == MA_SUCCESS || result == MA_AT_END) {\n                        if (isSilentOutput == MA_FALSE) {   /* Don't mix if the node outputs silence. */\n                            ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1);\n                        }\n                    }\n                }\n\n                framesProcessed += framesJustRead;\n\n                /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */\n                if (result != MA_SUCCESS) {\n                    break;\n                }\n\n                /* If we didn't read anything, abort so we don't get stuck in a loop. */\n                if (framesJustRead == 0) {\n                    break;\n                }\n            }\n\n            /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */\n            if (pOutputBus == pFirst && framesProcessed < frameCount) {\n                ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels);\n            }\n\n            if (isSilentOutput == MA_FALSE) {\n                doesOutputBufferHaveContent = MA_TRUE;\n            }\n        } else {\n            /* Seek. */\n            ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime);\n        }\n    }\n\n    /* If we didn't output anything, output silence. */\n    if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) {\n        ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels);\n    }\n\n    /* In this path we always \"process\" the entire amount. */\n    *pFramesRead = frameCount;\n\n    return result;\n}\n\n\nMA_API ma_node_config ma_node_config_init(void)\n{\n    ma_node_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.initialState   = ma_node_state_started;    /* Nodes are started by default. */\n    config.inputBusCount  = MA_NODE_BUS_COUNT_UNKNOWN;\n    config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN;\n\n    return config;\n}\n\n\n\nstatic ma_result ma_node_detach_full(ma_node* pNode);\n\nstatic float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex)\n{\n    ma_node_base* pNodeBase = (ma_node_base*)pNode;\n    ma_uint32 iInputBus;\n    float* pBasePtr;\n\n    MA_ASSERT(pNodeBase != NULL);\n\n    /* Input data is stored at the front of the buffer. */\n    pBasePtr = pNodeBase->pCachedData;\n    for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) {\n        pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);\n    }\n\n    return pBasePtr;\n}\n\nstatic float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex)\n{\n    ma_node_base* pNodeBase = (ma_node_base*)pNode;\n    ma_uint32 iInputBus;\n    ma_uint32 iOutputBus;\n    float* pBasePtr;\n\n    MA_ASSERT(pNodeBase != NULL);\n\n    /* Cached output data starts after the input data. */\n    pBasePtr = pNodeBase->pCachedData;\n    for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {\n        pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);\n    }\n\n    for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) {\n        pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]);\n    }\n\n    return pBasePtr;\n}\n\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t inputBusOffset;\n    size_t outputBusOffset;\n    size_t cachedDataOffset;\n    ma_uint32 inputBusCount;    /* So it doesn't have to be calculated twice. */\n    ma_uint32 outputBusCount;   /* So it doesn't have to be calculated twice. */\n} ma_node_heap_layout;\n\nstatic ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount)\n{\n    ma_uint32 inputBusCount;\n    ma_uint32 outputBusCount;\n\n    MA_ASSERT(pConfig != NULL);\n    MA_ASSERT(pInputBusCount  != NULL);\n    MA_ASSERT(pOutputBusCount != NULL);\n\n    /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */\n    if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {\n        inputBusCount = pConfig->inputBusCount;\n    } else {\n        inputBusCount = pConfig->vtable->inputBusCount;\n\n        if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) {\n            return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */\n        }\n    }\n\n    if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {\n        outputBusCount = pConfig->outputBusCount;\n    } else {\n        outputBusCount = pConfig->vtable->outputBusCount;\n\n        if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) {\n            return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */\n        }\n    }\n\n    /* Bus counts must be within limits. */\n    if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) {\n        return MA_INVALID_ARGS;\n    }\n\n\n    /* We must have channel counts for each bus. */\n    if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) {\n        return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */\n    }\n\n\n    /* Some special rules for passthrough nodes. */\n    if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {\n        if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) {\n            return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */\n        }\n\n        if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) {\n            return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */\n        }\n    }\n\n\n    *pInputBusCount  = inputBusCount;\n    *pOutputBusCount = outputBusCount;\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout)\n{\n    ma_result result;\n    ma_uint32 inputBusCount;\n    ma_uint32 outputBusCount;\n\n    MA_ASSERT(pHeapLayout != NULL);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    /* Input buses. */\n    if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {\n        pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes;\n        pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount);\n    } else {\n        pHeapLayout->inputBusOffset = MA_SIZE_MAX;  /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */\n    }\n\n    /* Output buses. */\n    if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {\n        pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes;\n        pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount);\n    } else {\n        pHeapLayout->outputBusOffset = MA_SIZE_MAX;\n    }\n\n    /*\n    Cached audio data.\n\n    We need to allocate memory for a caching both input and output data. We have an optimization\n    where no caching is necessary for specific conditions:\n\n        - The node has 0 inputs and 1 output.\n\n    When a node meets the above conditions, no cache is allocated.\n\n    The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by\n    allocating too much, but at the same time we want it be large enough so that enough frames can\n    be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For\n    now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile\n    time. It might also be worth investigating whether or not this can be configured at run time.\n    */\n    if (inputBusCount == 0 && outputBusCount == 1) {\n        /* Fast path. No cache needed. */\n        pHeapLayout->cachedDataOffset = MA_SIZE_MAX;\n    } else {\n        /* Slow path. Cache needed. */\n        size_t cachedDataSizeInBytes = 0;\n        ma_uint32 iBus;\n\n        for (iBus = 0; iBus < inputBusCount; iBus += 1) {\n            cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]);\n        }\n\n        for (iBus = 0; iBus < outputBusCount; iBus += 1) {\n            cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]);\n        }\n\n        pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes;\n        pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes);\n    }\n\n\n    /*\n    Not technically part of the heap, but we can output the input and output bus counts so we can\n    avoid a redundant call to ma_node_translate_bus_counts().\n    */\n    pHeapLayout->inputBusCount  = inputBusCount;\n    pHeapLayout->outputBusCount = outputBusCount;\n\n    /* Make sure allocation size is aligned. */\n    pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_node_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode)\n{\n    ma_node_base* pNodeBase = (ma_node_base*)pNode;\n    ma_result result;\n    ma_node_heap_layout heapLayout;\n    ma_uint32 iInputBus;\n    ma_uint32 iOutputBus;\n\n    if (pNodeBase == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pNodeBase);\n\n    result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pNodeBase->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    pNodeBase->pNodeGraph     = pNodeGraph;\n    pNodeBase->vtable         = pConfig->vtable;\n    pNodeBase->state          = pConfig->initialState;\n    pNodeBase->stateTimes[ma_node_state_started] = 0;\n    pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */\n    pNodeBase->inputBusCount  = heapLayout.inputBusCount;\n    pNodeBase->outputBusCount = heapLayout.outputBusCount;\n\n    if (heapLayout.inputBusOffset != MA_SIZE_MAX) {\n        pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);\n    } else {\n        pNodeBase->pInputBuses = pNodeBase->_inputBuses;\n    }\n\n    if (heapLayout.outputBusOffset != MA_SIZE_MAX) {\n        pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset);\n    } else {\n        pNodeBase->pOutputBuses = pNodeBase->_outputBuses;\n    }\n\n    if (heapLayout.cachedDataOffset != MA_SIZE_MAX) {\n        pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset);\n        pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames;\n    } else {\n        pNodeBase->pCachedData = NULL;\n    }\n\n\n\n    /* We need to run an initialization step for each input and output bus. */\n    for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {\n        result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n    for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {\n        result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n    }\n\n\n    /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */\n    if (pNodeBase->pCachedData != NULL) {\n        ma_uint32 iBus;\n\n    #if 1   /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */\n        /* For safety we'll go ahead and default the buffer to silence. */\n        for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {\n            ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]));\n        }\n        for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {\n            ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]));\n        }\n    #else\n        /* For debugging. Default to a sine wave. */\n        for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {\n            ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000);\n        }\n        for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {\n            ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000);\n        }\n    #endif\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_node_base* pNodeBase = (ma_node_base*)pNode;\n\n    if (pNodeBase == NULL) {\n        return;\n    }\n\n    /*\n    The first thing we need to do is fully detach the node. This will detach all inputs and\n    outputs. We need to do this first because it will sever the connection with the node graph and\n    allow us to complete uninitialization without needing to worry about thread-safety with the\n    audio thread. The detachment process will wait for any local processing of the node to finish.\n    */\n    ma_node_detach_full(pNode);\n\n    /*\n    At this point the node should be completely unreferenced by the node graph and we can finish up\n    the uninitialization process without needing to worry about thread-safety.\n    */\n    if (pNodeBase->_ownsHeap) {\n        ma_free(pNodeBase->_pHeap, pAllocationCallbacks);\n    }\n}\n\nMA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode)\n{\n    if (pNode == NULL) {\n        return NULL;\n    }\n\n    return ((const ma_node_base*)pNode)->pNodeGraph;\n}\n\nMA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode)\n{\n    if (pNode == NULL) {\n        return 0;\n    }\n\n    return ((ma_node_base*)pNode)->inputBusCount;\n}\n\nMA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode)\n{\n    if (pNode == NULL) {\n        return 0;\n    }\n\n    return ((ma_node_base*)pNode)->outputBusCount;\n}\n\n\nMA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex)\n{\n    const ma_node_base* pNodeBase = (const ma_node_base*)pNode;\n\n    if (pNode == NULL) {\n        return 0;\n    }\n\n    if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) {\n        return 0;   /* Invalid bus index. */\n    }\n\n    return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]);\n}\n\nMA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex)\n{\n    const ma_node_base* pNodeBase = (const ma_node_base*)pNode;\n\n    if (pNode == NULL) {\n        return 0;\n    }\n\n    if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {\n        return 0;   /* Invalid bus index. */\n    }\n\n    return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]);\n}\n\n\nstatic ma_result ma_node_detach_full(ma_node* pNode)\n{\n    ma_node_base* pNodeBase = (ma_node_base*)pNode;\n    ma_uint32 iInputBus;\n\n    if (pNodeBase == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /*\n    Make sure the node is completely detached first. This will not return until the output bus is\n    guaranteed to no longer be referenced by the audio thread.\n    */\n    ma_node_detach_all_output_buses(pNode);\n\n    /*\n    At this point all output buses will have been detached from the graph and we can be guaranteed\n    that none of it's input nodes will be getting processed by the graph. We can detach these\n    without needing to worry about the audio thread touching them.\n    */\n    for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) {\n        ma_node_input_bus* pInputBus;\n        ma_node_output_bus* pOutputBus;\n\n        pInputBus = &pNodeBase->pInputBuses[iInputBus];\n\n        /*\n        This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those\n        functions are specifically for the audio thread. We'll instead just manually iterate using standard\n        linked list logic. We don't need to worry about the audio thread referencing these because the step\n        above severed the connection to the graph.\n        */\n        for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext)) {\n            ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex);   /* This won't do any waiting in practice and should be efficient. */\n        }\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex)\n{\n    ma_result result = MA_SUCCESS;\n    ma_node_base* pNodeBase = (ma_node_base*)pNode;\n    ma_node_base* pInputNodeBase;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {\n        return MA_INVALID_ARGS; /* Invalid output bus index. */\n    }\n\n    /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */\n    ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]);\n    {\n        pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode;\n        if (pInputNodeBase != NULL) {\n            ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]);\n        }\n    }\n    ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]);\n\n    return result;\n}\n\nMA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode)\n{\n    ma_uint32 iOutputBus;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) {\n        ma_node_detach_output_bus(pNode, iOutputBus);\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex)\n{\n    ma_node_base* pNodeBase  = (ma_node_base*)pNode;\n    ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode;\n\n    if (pNodeBase == NULL || pOtherNodeBase == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pNodeBase == pOtherNodeBase) {\n        return MA_INVALID_OPERATION;    /* Cannot attach a node to itself. */\n    }\n\n    if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) {\n        return MA_INVALID_OPERATION;    /* Invalid bus index. */\n    }\n\n    /* The output channel count of the output node must be the same as the input channel count of the input node. */\n    if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) {\n        return MA_INVALID_OPERATION;    /* Channel count is incompatible. */\n    }\n\n    /* This will deal with detaching if the output bus is already attached to something. */\n    ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume)\n{\n    ma_node_base* pNodeBase = (ma_node_base*)pNode;\n\n    if (pNodeBase == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {\n        return MA_INVALID_ARGS; /* Invalid bus index. */\n    }\n\n    return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume);\n}\n\nMA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex)\n{\n    const ma_node_base* pNodeBase = (const ma_node_base*)pNode;\n\n    if (pNodeBase == NULL) {\n        return 0;\n    }\n\n    if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {\n        return 0;   /* Invalid bus index. */\n    }\n\n    return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]);\n}\n\nMA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state)\n{\n    ma_node_base* pNodeBase = (ma_node_base*)pNode;\n\n    if (pNodeBase == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_atomic_exchange_i32(&pNodeBase->state, state);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_node_state ma_node_get_state(const ma_node* pNode)\n{\n    const ma_node_base* pNodeBase = (const ma_node_base*)pNode;\n\n    if (pNodeBase == NULL) {\n        return ma_node_state_stopped;\n    }\n\n    return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state);\n}\n\nMA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime)\n{\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Validation check for safety since we'll be using this as an index into stateTimes[]. */\n    if (state != ma_node_state_started && state != ma_node_state_stopped) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state)\n{\n    if (pNode == NULL) {\n        return 0;\n    }\n\n    /* Validation check for safety since we'll be using this as an index into stateTimes[]. */\n    if (state != ma_node_state_started && state != ma_node_state_stopped) {\n        return 0;\n    }\n\n    return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]);\n}\n\nMA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime)\n{\n    if (pNode == NULL) {\n        return ma_node_state_stopped;\n    }\n\n    return ma_node_get_state_by_time_range(pNode, globalTime, globalTime);\n}\n\nMA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd)\n{\n    ma_node_state state;\n\n    if (pNode == NULL) {\n        return ma_node_state_stopped;\n    }\n\n    state = ma_node_get_state(pNode);\n\n    /* An explicitly stopped node is always stopped. */\n    if (state == ma_node_state_stopped) {\n        return ma_node_state_stopped;\n    }\n\n    /*\n    Getting here means the node is marked as started, but it may still not be truly started due to\n    it's start time not having been reached yet. Also, the stop time may have also been reached in\n    which case it'll be considered stopped.\n    */\n    if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) {\n        return ma_node_state_stopped;   /* Start time has not yet been reached. */\n    }\n\n    if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) {\n        return ma_node_state_stopped;   /* Stop time has been reached. */\n    }\n\n    /* Getting here means the node is marked as started and is within it's start/stop times. */\n    return ma_node_state_started;\n}\n\nMA_API ma_uint64 ma_node_get_time(const ma_node* pNode)\n{\n    if (pNode == NULL) {\n        return 0;\n    }\n\n    return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime);\n}\n\nMA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime)\n{\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime);\n\n    return MA_SUCCESS;\n}\n\n\n\nstatic void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_node_base* pNodeBase = (ma_node_base*)pNode;\n\n    MA_ASSERT(pNode != NULL);\n\n    if (pNodeBase->vtable->onProcess) {\n        pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);\n    }\n}\n\nstatic ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime)\n{\n    ma_node_base* pNodeBase = (ma_node_base*)pNode;\n    ma_result result = MA_SUCCESS;\n    ma_uint32 iInputBus;\n    ma_uint32 iOutputBus;\n    ma_uint32 inputBusCount;\n    ma_uint32 outputBusCount;\n    ma_uint32 totalFramesRead = 0;\n    float* ppFramesIn[MA_MAX_NODE_BUS_COUNT];\n    float* ppFramesOut[MA_MAX_NODE_BUS_COUNT];\n    ma_uint64 globalTimeBeg;\n    ma_uint64 globalTimeEnd;\n    ma_uint64 startTime;\n    ma_uint64 stopTime;\n    ma_uint32 timeOffsetBeg;\n    ma_uint32 timeOffsetEnd;\n    ma_uint32 frameCountIn;\n    ma_uint32 frameCountOut;\n\n    /*\n    pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and\n    expected that the number of frames read may be different to that requested. Therefore, the caller\n    must look at this value to correctly determine how many frames were read.\n    */\n    MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */\n    if (pFramesRead == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pFramesRead = 0;   /* Safety. */\n\n    if (pNodeBase == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) {\n        return MA_INVALID_ARGS; /* Invalid output bus index. */\n    }\n\n    /* Don't do anything if we're in a stopped state. */\n    if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) {\n        return MA_SUCCESS;  /* We're in a stopped state. This is not an error - we just need to not read anything. */\n    }\n\n\n    globalTimeBeg = globalTime;\n    globalTimeEnd = globalTime + frameCount;\n    startTime = ma_node_get_state_time(pNode, ma_node_state_started);\n    stopTime  = ma_node_get_state_time(pNode, ma_node_state_stopped);\n\n    /*\n    At this point we know that we are inside our start/stop times. However, we may need to adjust\n    our frame count and output pointer to accommodate since we could be straddling the time period\n    that this function is getting called for.\n\n    It's possible (and likely) that the start time does not line up with the output buffer. We\n    therefore need to offset it by a number of frames to accommodate. The same thing applies for\n    the stop time.\n    */\n    timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0;\n    timeOffsetEnd = (globalTimeEnd > stopTime)  ? (ma_uint32)(globalTimeEnd - stopTime)  : 0;\n\n    /* Trim based on the start offset. We need to silence the start of the buffer. */\n    if (timeOffsetBeg > 0) {\n        ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));\n        pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex);\n        frameCount -= timeOffsetBeg;\n    }\n\n    /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */\n    if (timeOffsetEnd > 0) {\n        frameCount -= timeOffsetEnd;\n    }\n\n\n    /* We run on different paths depending on the bus counts. */\n    inputBusCount  = ma_node_get_input_bus_count(pNode);\n    outputBusCount = ma_node_get_output_bus_count(pNode);\n\n    /*\n    Run a simplified path when there are no inputs and one output. In this case there's nothing to\n    actually read and we can go straight to output. This is a very common scenario because the vast\n    majority of data source nodes will use this setup so this optimization I think is worthwhile.\n    */\n    if (inputBusCount == 0 && outputBusCount == 1) {\n        /* Fast path. No need to read from input and no need for any caching. */\n        frameCountIn  = 0;\n        frameCountOut = frameCount;    /* Just read as much as we can. The callback will return what was actually read. */\n\n        ppFramesOut[0] = pFramesOut;\n\n        /*\n        If it's a passthrough we won't be expecting the callback to output anything, so we'll\n        need to pre-silence the output buffer.\n        */\n        if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {\n            ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));\n        }\n\n        ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);\n        totalFramesRead = frameCountOut;\n    } else {\n        /* Slow path. Need to read input data. */\n        if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {\n            /*\n            Fast path. We're running a passthrough. We need to read directly into the output buffer, but\n            still fire the callback so that event handling and trigger nodes can do their thing. Since\n            it's a passthrough there's no need for any kind of caching logic.\n            */\n            MA_ASSERT(outputBusCount == inputBusCount);\n            MA_ASSERT(outputBusCount == 1);\n            MA_ASSERT(outputBusIndex == 0);\n\n            /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */\n            ppFramesOut[0] = pFramesOut;\n            ppFramesIn[0] = ppFramesOut[0];\n\n            result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime);\n            if (result == MA_SUCCESS) {\n                /* Even though it's a passthrough, we still need to fire the callback. */\n                frameCountIn  = totalFramesRead;\n                frameCountOut = totalFramesRead;\n\n                if (totalFramesRead > 0) {\n                    ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut);  /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */\n                }\n\n                /*\n                A passthrough should never have modified the input and output frame counts. If you're\n                triggering these assers you need to fix your processing callback.\n                */\n                MA_ASSERT(frameCountIn  == totalFramesRead);\n                MA_ASSERT(frameCountOut == totalFramesRead);\n            }\n        } else {\n            /* Slow path. Need to do caching. */\n            ma_uint32 framesToProcessIn;\n            ma_uint32 framesToProcessOut;\n            ma_bool32 consumeNullInput = MA_FALSE;\n\n            /*\n            We use frameCount as a basis for the number of frames to read since that's what's being\n            requested, however we still need to clamp it to whatever can fit in the cache.\n\n            This will also be used as the basis for determining how many input frames to read. This is\n            not ideal because it can result in too many input frames being read which introduces latency.\n            To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount\n            which is used as hint to miniaudio as to how many input frames it needs to read at a time. This\n            callback is completely optional, and if it's not set, miniaudio will assume `frameCount`.\n\n            This function will be called multiple times for each period of time, once for each output node.\n            We cannot read from each input node each time this function is called. Instead we need to check\n            whether or not this is first output bus to be read from for this time period, and if so, read\n            from our input data.\n\n            To determine whether or not we're ready to read data, we check a flag. There will be one flag\n            for each output. When the flag is set, it means data has been read previously and that we're\n            ready to advance time forward for our input nodes by reading fresh data.\n            */\n            framesToProcessOut = frameCount;\n            if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) {\n                framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus;\n            }\n\n            framesToProcessIn  = frameCount;\n            if (pNodeBase->vtable->onGetRequiredInputFrameCount) {\n                pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */\n            }\n            if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) {\n                framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus;\n            }\n\n\n            MA_ASSERT(framesToProcessIn  <= 0xFFFF);\n            MA_ASSERT(framesToProcessOut <= 0xFFFF);\n\n            if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) {\n                /* Getting here means we need to do another round of processing. */\n                pNodeBase->cachedFrameCountOut = 0;\n\n                for (;;) {\n                    frameCountOut = 0;\n\n                    /*\n                    We need to prepare our output frame pointers for processing. In the same iteration we need\n                    to mark every output bus as unread so that future calls to this function for different buses\n                    for the current time period don't pull in data when they should instead be reading from cache.\n                    */\n                    for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) {\n                        ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */\n                        ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus);\n                    }\n\n                    /* We only need to read from input buses if there isn't already some data in the cache. */\n                    if (pNodeBase->cachedFrameCountIn == 0) {\n                        ma_uint32 maxFramesReadIn = 0;\n\n                        /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */\n                        for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {\n                            ma_uint32 framesRead;\n\n                            /* The first thing to do is get the offset within our bulk allocation to store this input data. */\n                            ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus);\n\n                            /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */\n                            result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime);\n                            if (result != MA_SUCCESS) {\n                                /* It doesn't really matter if we fail because we'll just fill with silence. */\n                                framesRead = 0; /* Just for safety, but I don't think it's really needed. */\n                            }\n\n                            /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */\n                            /* Any leftover frames need to silenced for safety. */\n                            if (framesRead < framesToProcessIn) {\n                                ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus));\n                            }\n\n                            maxFramesReadIn = ma_max(maxFramesReadIn, framesRead);\n                        }\n\n                        /* This was a fresh load of input data so reset our consumption counter. */\n                        pNodeBase->consumedFrameCountIn = 0;\n\n                        /*\n                        We don't want to keep processing if there's nothing to process, so set the number of cached\n                        input frames to the maximum number we read from each attachment (the lesser will be padded\n                        with silence). If we didn't read anything, this will be set to 0 and the entire buffer will\n                        have been assigned to silence. This being equal to 0 is an important property for us because\n                        it allows us to detect when NULL can be passed into the processing callback for the input\n                        buffer for the purpose of continuous processing.\n                        */\n                        pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn;\n                    } else {\n                        /* We don't need to read anything, but we do need to prepare our input frame pointers. */\n                        for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {\n                            ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus));\n                        }\n                    }\n\n                    /*\n                    At this point we have our input data so now we need to do some processing. Sneaky little\n                    optimization here - we can set the pointer to the output buffer for this output bus so\n                    that the final copy into the output buffer is done directly by onProcess().\n                    */\n                    if (pFramesOut != NULL) {\n                        ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex));\n                    }\n\n\n                    /* Give the processing function the entire capacity of the output buffer. */\n                    frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut);\n\n                    /*\n                    We need to treat nodes with continuous processing a little differently. For these ones,\n                    we always want to fire the callback with the requested number of frames, regardless of\n                    pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass\n                    in NULL for the input buffer to the callback.\n                    */\n                    if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) {\n                        /* We're using continuous processing. Make sure we specify the whole frame count at all times. */\n                        frameCountIn = framesToProcessIn;    /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */\n\n                        if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) {\n                            consumeNullInput = MA_TRUE;\n                        } else {\n                            consumeNullInput = MA_FALSE;\n                        }\n\n                        /*\n                        Since we're using continuous processing we're always passing in a full frame count\n                        regardless of how much input data was read. If this is greater than what we read as\n                        input, we'll end up with an underflow. We instead need to make sure our cached frame\n                        count is set to the number of frames we'll be passing to the data callback. Not\n                        doing this will result in an underflow when we \"consume\" the cached data later on.\n\n                        Note that this check needs to be done after the \"consumeNullInput\" check above because\n                        we use the property of cachedFrameCountIn being 0 to determine whether or not we\n                        should be passing in a null pointer to the processing callback for when the node is\n                        configured with MA_NODE_FLAG_ALLOW_NULL_INPUT.\n                        */\n                        if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) {\n                            pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn;\n                        }\n                    } else {\n                        frameCountIn = pNodeBase->cachedFrameCountIn;  /* Give the processing function as much valid input data as we've got. */\n                        consumeNullInput = MA_FALSE;\n                    }\n\n                    /*\n                    Process data slightly differently depending on whether or not we're consuming NULL\n                    input (checked just above).\n                    */\n                    if (consumeNullInput) {\n                        ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);\n                    } else {\n                        /*\n                        We want to skip processing if there's no input data, but we can only do that safely if\n                        we know that there is no chance of any output frames being produced. If continuous\n                        processing is being used, this won't be a problem because the input frame count will\n                        always be non-0. However, if continuous processing is *not* enabled and input and output\n                        data is processed at different rates, we still need to process that last input frame\n                        because there could be a few excess output frames needing to be produced from cached\n                        data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for\n                        determining whether or not we need to process the node even when there are no input\n                        frames available right now.\n                        */\n                        if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) {\n                            ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut);    /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */\n                        } else {\n                            frameCountOut = 0;  /* No data was processed. */\n                        }\n                    }\n\n                    /*\n                    Thanks to our sneaky optimization above we don't need to do any data copying directly into\n                    the output buffer - the onProcess() callback just did that for us. We do, however, need to\n                    apply the number of input and output frames that were processed. Note that due to continuous\n                    processing above, we need to do explicit checks here. If we just consumed a NULL input\n                    buffer it means that no actual input data was processed from the internal buffers and we\n                    don't want to be modifying any counters.\n                    */\n                    if (consumeNullInput == MA_FALSE) {\n                        pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn;\n                        pNodeBase->cachedFrameCountIn   -= (ma_uint16)frameCountIn;\n                    }\n\n                    /* The cached output frame count is always equal to what we just read. */\n                    pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut;\n\n                    /* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */\n                    if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) {\n                        break;\n                    }\n                }\n            } else {\n                /*\n                We're not needing to read anything from the input buffer so just read directly from our\n                already-processed data.\n                */\n                if (pFramesOut != NULL) {\n                    ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex));\n                }\n            }\n\n            /* The number of frames read is always equal to the number of cached output frames. */\n            totalFramesRead = pNodeBase->cachedFrameCountOut;\n\n            /* Now that we've read the data, make sure our read flag is set. */\n            ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE);\n        }\n    }\n\n    /* Apply volume, if necessary. */\n    ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]));\n\n    /* Advance our local time forward. */\n    ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead);\n\n    *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */\n    return result;\n}\n\n\n\n\n/* Data source node. */\nMA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource)\n{\n    ma_data_source_node_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.nodeConfig  = ma_node_config_init();\n    config.pDataSource = pDataSource;\n\n    return config;\n}\n\n\nstatic void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode;\n    ma_format format;\n    ma_uint32 channels;\n    ma_uint32 frameCount;\n    ma_uint64 framesRead = 0;\n\n    MA_ASSERT(pDataSourceNode != NULL);\n    MA_ASSERT(pDataSourceNode->pDataSource != NULL);\n    MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode)  == 0);\n    MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1);\n\n    /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */\n    (void)ppFramesIn;\n    (void)pFrameCountIn;\n\n    frameCount = *pFrameCountOut;\n\n    /* miniaudio should never be calling this with a frame count of zero. */\n    MA_ASSERT(frameCount > 0);\n\n    if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */\n        /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */\n        MA_ASSERT(format == ma_format_f32);\n        (void)format;   /* Just to silence some static analysis tools. */\n\n        ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead);\n    }\n\n    *pFrameCountOut = (ma_uint32)framesRead;\n}\n\nstatic ma_node_vtable g_ma_data_source_node_vtable =\n{\n    ma_data_source_node_process_pcm_frames,\n    NULL,   /* onGetRequiredInputFrameCount */\n    0,      /* 0 input buses. */\n    1,      /* 1 output bus. */\n    0\n};\n\nMA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode)\n{\n    ma_result result;\n    ma_format format;   /* For validating the format, which must be ma_format_f32. */\n    ma_uint32 channels; /* For specifying the channel count of the output bus. */\n    ma_node_config baseConfig;\n\n    if (pDataSourceNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pDataSourceNode);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0);    /* Don't care about sample rate. This will check pDataSource for NULL. */\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */\n    if (format != ma_format_f32) {\n        return MA_INVALID_ARGS; /* Invalid format. */\n    }\n\n    /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */\n    baseConfig = pConfig->nodeConfig;\n    baseConfig.vtable = &g_ma_data_source_node_vtable;  /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */\n\n    /*\n    The channel count is defined by the data source. It is invalid for the caller to manually set\n    the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the\n    channel count pointer to NULL which is how it must remain. If you trigger any of these asserts\n    it means you're explicitly setting the channel count. Instead, configure the output channel\n    count of your data source to be the necessary channel count.\n    */\n    if (baseConfig.pOutputChannels != NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    baseConfig.pOutputChannels = &channels;\n\n    result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    pDataSourceNode->pDataSource = pConfig->pDataSource;\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks);\n}\n\nMA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping)\n{\n    if (pDataSourceNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping);\n}\n\nMA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode)\n{\n    if (pDataSourceNode == NULL) {\n        return MA_FALSE;\n    }\n\n    return ma_data_source_is_looping(pDataSourceNode->pDataSource);\n}\n\n\n\n/* Splitter Node. */\nMA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels)\n{\n    ma_splitter_node_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.nodeConfig     = ma_node_config_init();\n    config.channels       = channels;\n    config.outputBusCount = 2;\n\n    return config;\n}\n\n\nstatic void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_node_base* pNodeBase = (ma_node_base*)pNode;\n    ma_uint32 iOutputBus;\n    ma_uint32 channels;\n\n    MA_ASSERT(pNodeBase != NULL);\n    MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1);\n\n    /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */\n    (void)pFrameCountIn;\n\n    /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */\n    channels = ma_node_get_input_channels(pNodeBase, 0);\n\n    /* Splitting is just copying the first input bus and copying it over to each output bus. */\n    for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {\n        ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels);\n    }\n}\n\nstatic ma_node_vtable g_ma_splitter_node_vtable =\n{\n    ma_splitter_node_process_pcm_frames,\n    NULL,                       /* onGetRequiredInputFrameCount */\n    1,                          /* 1 input bus. */\n    MA_NODE_BUS_COUNT_UNKNOWN,  /* The output bus count is specified on a per-node basis. */\n    0\n};\n\nMA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode)\n{\n    ma_result result;\n    ma_node_config baseConfig;\n    ma_uint32 pInputChannels[1];\n    ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT];\n    ma_uint32 iOutputBus;\n\n    if (pSplitterNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pSplitterNode);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) {\n        return MA_INVALID_ARGS; /* Too many output buses. */\n    }\n\n    /* Splitters require the same number of channels between inputs and outputs. */\n    pInputChannels[0]  = pConfig->channels;\n    for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) {\n        pOutputChannels[iOutputBus] = pConfig->channels;\n    }\n\n    baseConfig = pConfig->nodeConfig;\n    baseConfig.vtable = &g_ma_splitter_node_vtable;\n    baseConfig.pInputChannels  = pInputChannels;\n    baseConfig.pOutputChannels = pOutputChannels;\n    baseConfig.outputBusCount  = pConfig->outputBusCount;\n\n    result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to initialize the base node. */\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_node_uninit(pSplitterNode, pAllocationCallbacks);\n}\n\n\n/*\nBiquad Node\n*/\nMA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2)\n{\n    ma_biquad_node_config config;\n\n    config.nodeConfig = ma_node_config_init();\n    config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);\n\n    return config;\n}\n\nstatic void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;\n\n    MA_ASSERT(pNode != NULL);\n    (void)pFrameCountIn;\n\n    ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);\n}\n\nstatic ma_node_vtable g_ma_biquad_node_vtable =\n{\n    ma_biquad_node_process_pcm_frames,\n    NULL,   /* onGetRequiredInputFrameCount */\n    1,      /* One input. */\n    1,      /* One output. */\n    0       /* Default flags. */\n};\n\nMA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode)\n{\n    ma_result result;\n    ma_node_config baseNodeConfig;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pNode);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->biquad.format != ma_format_f32) {\n        return MA_INVALID_ARGS; /* The format must be f32. */\n    }\n\n    result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    baseNodeConfig = ma_node_config_init();\n    baseNodeConfig.vtable          = &g_ma_biquad_node_vtable;\n    baseNodeConfig.pInputChannels  = &pConfig->biquad.channels;\n    baseNodeConfig.pOutputChannels = &pConfig->biquad.channels;\n\n    result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode)\n{\n    ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;\n\n    MA_ASSERT(pNode != NULL);\n\n    return ma_biquad_reinit(pConfig, &pLPFNode->biquad);\n}\n\nMA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;\n\n    if (pNode == NULL) {\n        return;\n    }\n\n    ma_node_uninit(pNode, pAllocationCallbacks);\n    ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks);\n}\n\n\n\n/*\nLow Pass Filter Node\n*/\nMA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)\n{\n    ma_lpf_node_config config;\n\n    config.nodeConfig = ma_node_config_init();\n    config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);\n\n    return config;\n}\n\nstatic void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;\n\n    MA_ASSERT(pNode != NULL);\n    (void)pFrameCountIn;\n\n    ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);\n}\n\nstatic ma_node_vtable g_ma_lpf_node_vtable =\n{\n    ma_lpf_node_process_pcm_frames,\n    NULL,   /* onGetRequiredInputFrameCount */\n    1,      /* One input. */\n    1,      /* One output. */\n    0       /* Default flags. */\n};\n\nMA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode)\n{\n    ma_result result;\n    ma_node_config baseNodeConfig;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pNode);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->lpf.format != ma_format_f32) {\n        return MA_INVALID_ARGS; /* The format must be f32. */\n    }\n\n    result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    baseNodeConfig = ma_node_config_init();\n    baseNodeConfig.vtable          = &g_ma_lpf_node_vtable;\n    baseNodeConfig.pInputChannels  = &pConfig->lpf.channels;\n    baseNodeConfig.pOutputChannels = &pConfig->lpf.channels;\n\n    result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode)\n{\n    ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_lpf_reinit(pConfig, &pLPFNode->lpf);\n}\n\nMA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;\n\n    if (pNode == NULL) {\n        return;\n    }\n\n    ma_node_uninit(pNode, pAllocationCallbacks);\n    ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks);\n}\n\n\n\n/*\nHigh Pass Filter Node\n*/\nMA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)\n{\n    ma_hpf_node_config config;\n\n    config.nodeConfig = ma_node_config_init();\n    config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);\n\n    return config;\n}\n\nstatic void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;\n\n    MA_ASSERT(pNode != NULL);\n    (void)pFrameCountIn;\n\n    ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);\n}\n\nstatic ma_node_vtable g_ma_hpf_node_vtable =\n{\n    ma_hpf_node_process_pcm_frames,\n    NULL,   /* onGetRequiredInputFrameCount */\n    1,      /* One input. */\n    1,      /* One output. */\n    0       /* Default flags. */\n};\n\nMA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode)\n{\n    ma_result result;\n    ma_node_config baseNodeConfig;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pNode);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->hpf.format != ma_format_f32) {\n        return MA_INVALID_ARGS; /* The format must be f32. */\n    }\n\n    result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    baseNodeConfig = ma_node_config_init();\n    baseNodeConfig.vtable          = &g_ma_hpf_node_vtable;\n    baseNodeConfig.pInputChannels  = &pConfig->hpf.channels;\n    baseNodeConfig.pOutputChannels = &pConfig->hpf.channels;\n\n    result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode)\n{\n    ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_hpf_reinit(pConfig, &pHPFNode->hpf);\n}\n\nMA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;\n\n    if (pNode == NULL) {\n        return;\n    }\n\n    ma_node_uninit(pNode, pAllocationCallbacks);\n    ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks);\n}\n\n\n\n\n/*\nBand Pass Filter Node\n*/\nMA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)\n{\n    ma_bpf_node_config config;\n\n    config.nodeConfig = ma_node_config_init();\n    config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);\n\n    return config;\n}\n\nstatic void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;\n\n    MA_ASSERT(pNode != NULL);\n    (void)pFrameCountIn;\n\n    ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);\n}\n\nstatic ma_node_vtable g_ma_bpf_node_vtable =\n{\n    ma_bpf_node_process_pcm_frames,\n    NULL,   /* onGetRequiredInputFrameCount */\n    1,      /* One input. */\n    1,      /* One output. */\n    0       /* Default flags. */\n};\n\nMA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode)\n{\n    ma_result result;\n    ma_node_config baseNodeConfig;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pNode);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->bpf.format != ma_format_f32) {\n        return MA_INVALID_ARGS; /* The format must be f32. */\n    }\n\n    result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    baseNodeConfig = ma_node_config_init();\n    baseNodeConfig.vtable          = &g_ma_bpf_node_vtable;\n    baseNodeConfig.pInputChannels  = &pConfig->bpf.channels;\n    baseNodeConfig.pOutputChannels = &pConfig->bpf.channels;\n\n    result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode)\n{\n    ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_bpf_reinit(pConfig, &pBPFNode->bpf);\n}\n\nMA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;\n\n    if (pNode == NULL) {\n        return;\n    }\n\n    ma_node_uninit(pNode, pAllocationCallbacks);\n    ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks);\n}\n\n\n\n/*\nNotching Filter Node\n*/\nMA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)\n{\n    ma_notch_node_config config;\n\n    config.nodeConfig = ma_node_config_init();\n    config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency);\n\n    return config;\n}\n\nstatic void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_notch_node* pBPFNode = (ma_notch_node*)pNode;\n\n    MA_ASSERT(pNode != NULL);\n    (void)pFrameCountIn;\n\n    ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);\n}\n\nstatic ma_node_vtable g_ma_notch_node_vtable =\n{\n    ma_notch_node_process_pcm_frames,\n    NULL,   /* onGetRequiredInputFrameCount */\n    1,      /* One input. */\n    1,      /* One output. */\n    0       /* Default flags. */\n};\n\nMA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode)\n{\n    ma_result result;\n    ma_node_config baseNodeConfig;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pNode);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->notch.format != ma_format_f32) {\n        return MA_INVALID_ARGS; /* The format must be f32. */\n    }\n\n    result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    baseNodeConfig = ma_node_config_init();\n    baseNodeConfig.vtable          = &g_ma_notch_node_vtable;\n    baseNodeConfig.pInputChannels  = &pConfig->notch.channels;\n    baseNodeConfig.pOutputChannels = &pConfig->notch.channels;\n\n    result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode)\n{\n    ma_notch_node* pNotchNode = (ma_notch_node*)pNode;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_notch2_reinit(pConfig, &pNotchNode->notch);\n}\n\nMA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_notch_node* pNotchNode = (ma_notch_node*)pNode;\n\n    if (pNode == NULL) {\n        return;\n    }\n\n    ma_node_uninit(pNode, pAllocationCallbacks);\n    ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks);\n}\n\n\n\n/*\nPeaking Filter Node\n*/\nMA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)\n{\n    ma_peak_node_config config;\n\n    config.nodeConfig = ma_node_config_init();\n    config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);\n\n    return config;\n}\n\nstatic void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_peak_node* pBPFNode = (ma_peak_node*)pNode;\n\n    MA_ASSERT(pNode != NULL);\n    (void)pFrameCountIn;\n\n    ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);\n}\n\nstatic ma_node_vtable g_ma_peak_node_vtable =\n{\n    ma_peak_node_process_pcm_frames,\n    NULL,   /* onGetRequiredInputFrameCount */\n    1,      /* One input. */\n    1,      /* One output. */\n    0       /* Default flags. */\n};\n\nMA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode)\n{\n    ma_result result;\n    ma_node_config baseNodeConfig;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pNode);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->peak.format != ma_format_f32) {\n        return MA_INVALID_ARGS; /* The format must be f32. */\n    }\n\n    result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak);\n    if (result != MA_SUCCESS) {\n        ma_node_uninit(pNode, pAllocationCallbacks);\n        return result;\n    }\n\n    baseNodeConfig = ma_node_config_init();\n    baseNodeConfig.vtable          = &g_ma_peak_node_vtable;\n    baseNodeConfig.pInputChannels  = &pConfig->peak.channels;\n    baseNodeConfig.pOutputChannels = &pConfig->peak.channels;\n\n    result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode)\n{\n    ma_peak_node* pPeakNode = (ma_peak_node*)pNode;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_peak2_reinit(pConfig, &pPeakNode->peak);\n}\n\nMA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_peak_node* pPeakNode = (ma_peak_node*)pNode;\n\n    if (pNode == NULL) {\n        return;\n    }\n\n    ma_node_uninit(pNode, pAllocationCallbacks);\n    ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks);\n}\n\n\n\n/*\nLow Shelf Filter Node\n*/\nMA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)\n{\n    ma_loshelf_node_config config;\n\n    config.nodeConfig = ma_node_config_init();\n    config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);\n\n    return config;\n}\n\nstatic void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode;\n\n    MA_ASSERT(pNode != NULL);\n    (void)pFrameCountIn;\n\n    ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);\n}\n\nstatic ma_node_vtable g_ma_loshelf_node_vtable =\n{\n    ma_loshelf_node_process_pcm_frames,\n    NULL,   /* onGetRequiredInputFrameCount */\n    1,      /* One input. */\n    1,      /* One output. */\n    0       /* Default flags. */\n};\n\nMA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode)\n{\n    ma_result result;\n    ma_node_config baseNodeConfig;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pNode);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->loshelf.format != ma_format_f32) {\n        return MA_INVALID_ARGS; /* The format must be f32. */\n    }\n\n    result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    baseNodeConfig = ma_node_config_init();\n    baseNodeConfig.vtable          = &g_ma_loshelf_node_vtable;\n    baseNodeConfig.pInputChannels  = &pConfig->loshelf.channels;\n    baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels;\n\n    result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode)\n{\n    ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf);\n}\n\nMA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;\n\n    if (pNode == NULL) {\n        return;\n    }\n\n    ma_node_uninit(pNode, pAllocationCallbacks);\n    ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks);\n}\n\n\n\n/*\nHigh Shelf Filter Node\n*/\nMA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)\n{\n    ma_hishelf_node_config config;\n\n    config.nodeConfig = ma_node_config_init();\n    config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);\n\n    return config;\n}\n\nstatic void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode;\n\n    MA_ASSERT(pNode != NULL);\n    (void)pFrameCountIn;\n\n    ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);\n}\n\nstatic ma_node_vtable g_ma_hishelf_node_vtable =\n{\n    ma_hishelf_node_process_pcm_frames,\n    NULL,   /* onGetRequiredInputFrameCount */\n    1,      /* One input. */\n    1,      /* One output. */\n    0       /* Default flags. */\n};\n\nMA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode)\n{\n    ma_result result;\n    ma_node_config baseNodeConfig;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pNode);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->hishelf.format != ma_format_f32) {\n        return MA_INVALID_ARGS; /* The format must be f32. */\n    }\n\n    result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    baseNodeConfig = ma_node_config_init();\n    baseNodeConfig.vtable          = &g_ma_hishelf_node_vtable;\n    baseNodeConfig.pInputChannels  = &pConfig->hishelf.channels;\n    baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels;\n\n    result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return result;\n}\n\nMA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode)\n{\n    ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;\n\n    if (pNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf);\n}\n\nMA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;\n\n    if (pNode == NULL) {\n        return;\n    }\n\n    ma_node_uninit(pNode, pAllocationCallbacks);\n    ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks);\n}\n\n\n\n\nMA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)\n{\n    ma_delay_node_config config;\n\n    config.nodeConfig = ma_node_config_init();\n    config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay);\n\n    return config;\n}\n\n\nstatic void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_delay_node* pDelayNode = (ma_delay_node*)pNode;\n\n    (void)pFrameCountIn;\n\n    ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);\n}\n\nstatic ma_node_vtable g_ma_delay_node_vtable =\n{\n    ma_delay_node_process_pcm_frames,\n    NULL,\n    1,  /* 1 input channels. */\n    1,  /* 1 output channel. */\n    MA_NODE_FLAG_CONTINUOUS_PROCESSING  /* Delay requires continuous processing to ensure the tail get's processed. */\n};\n\nMA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode)\n{\n    ma_result result;\n    ma_node_config baseConfig;\n\n    if (pDelayNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pDelayNode);\n\n    result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    baseConfig = pConfig->nodeConfig;\n    baseConfig.vtable          = &g_ma_delay_node_vtable;\n    baseConfig.pInputChannels  = &pConfig->delay.channels;\n    baseConfig.pOutputChannels = &pConfig->delay.channels;\n\n    result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode);\n    if (result != MA_SUCCESS) {\n        ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);\n        return result;\n    }\n\n    return result;\n}\n\nMA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pDelayNode == NULL) {\n        return;\n    }\n\n    /* The base node is always uninitialized first. */\n    ma_node_uninit(pDelayNode, pAllocationCallbacks);\n    ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);\n}\n\nMA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value)\n{\n    if (pDelayNode == NULL) {\n        return;\n    }\n\n    ma_delay_set_wet(&pDelayNode->delay, value);\n}\n\nMA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode)\n{\n    if (pDelayNode == NULL) {\n        return 0;\n    }\n\n    return ma_delay_get_wet(&pDelayNode->delay);\n}\n\nMA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value)\n{\n    if (pDelayNode == NULL) {\n        return;\n    }\n\n    ma_delay_set_dry(&pDelayNode->delay, value);\n}\n\nMA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode)\n{\n    if (pDelayNode == NULL) {\n        return 0;\n    }\n\n    return ma_delay_get_dry(&pDelayNode->delay);\n}\n\nMA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value)\n{\n    if (pDelayNode == NULL) {\n        return;\n    }\n\n    ma_delay_set_decay(&pDelayNode->delay, value);\n}\n\nMA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode)\n{\n    if (pDelayNode == NULL) {\n        return 0;\n    }\n\n    return ma_delay_get_decay(&pDelayNode->delay);\n}\n#endif  /* MA_NO_NODE_GRAPH */\n\n\n/* SECTION: miniaudio_engine.c */\n#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)\n/**************************************************************************************************************************************************************\n\nEngine\n\n**************************************************************************************************************************************************************/\n#define MA_SEEK_TARGET_NONE         (~(ma_uint64)0)\n\n\nstatic void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd)\n{\n    MA_ASSERT(pSound != NULL);\n    ma_atomic_exchange_32(&pSound->atEnd, atEnd);\n\n    /* Fire any callbacks or events. */\n    if (atEnd) {\n        if (pSound->endCallback != NULL) {\n            pSound->endCallback(pSound->pEndCallbackUserData, pSound);\n        }\n    }\n}\n\nstatic ma_bool32 ma_sound_get_at_end(const ma_sound* pSound)\n{\n    MA_ASSERT(pSound != NULL);\n    return ma_atomic_load_32(&pSound->atEnd);\n}\n\n\nMA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags)\n{\n    ma_engine_node_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.pEngine                  = pEngine;\n    config.type                     = type;\n    config.isPitchDisabled          = (flags & MA_SOUND_FLAG_NO_PITCH) != 0;\n    config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0;\n    config.monoExpansionMode        = pEngine->monoExpansionMode;\n\n    return config;\n}\n\n\nstatic void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode)\n{\n    ma_bool32 isUpdateRequired = MA_FALSE;\n    float newPitch;\n\n    MA_ASSERT(pEngineNode != NULL);\n\n    newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire);\n\n    if (pEngineNode->oldPitch != newPitch) {\n        pEngineNode->oldPitch  = newPitch;\n        isUpdateRequired = MA_TRUE;\n    }\n\n    if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) {\n        pEngineNode->oldDopplerPitch  = pEngineNode->spatializer.dopplerPitch;\n        isUpdateRequired = MA_TRUE;\n    }\n\n    if (isUpdateRequired) {\n        float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine);\n        ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch);\n    }\n}\n\nstatic ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode)\n{\n    MA_ASSERT(pEngineNode != NULL);\n\n    /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */\n    return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire);\n}\n\nstatic ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode)\n{\n    MA_ASSERT(pEngineNode != NULL);\n\n    return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire);\n}\n\nstatic ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount)\n{\n    ma_uint64 inputFrameCount = 0;\n\n    if (ma_engine_node_is_pitching_enabled(pEngineNode)) {\n        ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount);\n        if (result != MA_SUCCESS) {\n            inputFrameCount = 0;\n        }\n    } else {\n        inputFrameCount = outputFrameCount;    /* No resampling, so 1:1. */\n    }\n\n    return inputFrameCount;\n}\n\nstatic ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume)\n{\n    if (pEngineNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    ma_atomic_float_set(&pEngineNode->volume, volume);\n\n    /* If we're not smoothing we should bypass the volume gainer entirely. */\n    if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) {\n        /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */\n        ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume);\n    } else {\n        /* We're using volume smoothing, so apply the master volume to the gainer. */\n        ma_gainer_set_gain(&pEngineNode->volumeGainer, volume);\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume)\n{\n    if (pVolume == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pVolume = 0.0f;\n\n    if (pEngineNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume);\n\n    return MA_SUCCESS;\n}\n\n\nstatic void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    ma_uint32 frameCountIn;\n    ma_uint32 frameCountOut;\n    ma_uint32 totalFramesProcessedIn;\n    ma_uint32 totalFramesProcessedOut;\n    ma_uint32 channelsIn;\n    ma_uint32 channelsOut;\n    ma_bool32 isPitchingEnabled;\n    ma_bool32 isFadingEnabled;\n    ma_bool32 isSpatializationEnabled;\n    ma_bool32 isPanningEnabled;\n    ma_bool32 isVolumeSmoothingEnabled;\n\n    frameCountIn  = *pFrameCountIn;\n    frameCountOut = *pFrameCountOut;\n\n    channelsIn  = ma_spatializer_get_input_channels(&pEngineNode->spatializer);\n    channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer);\n\n    totalFramesProcessedIn  = 0;\n    totalFramesProcessedOut = 0;\n\n    /* Update the fader if applicable. */\n    {\n        ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames);\n        if (fadeLengthInFrames != ~(ma_uint64)0) {\n            float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg);\n            float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd);\n            ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames);\n            if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) {\n                fadeStartOffsetInFrames = 0;\n            } else {\n                fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine);\n            }\n\n            ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames);\n\n            /* Reset the fade length so we don't erroneously apply it again. */\n            ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0);\n        }\n    }\n\n    isPitchingEnabled        = ma_engine_node_is_pitching_enabled(pEngineNode);\n    isFadingEnabled          = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1;\n    isSpatializationEnabled  = ma_engine_node_is_spatialization_enabled(pEngineNode);\n    isPanningEnabled         = pEngineNode->panner.pan != 0 && channelsOut != 1;\n    isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0;\n\n    /* Keep going while we've still got data available for processing. */\n    while (totalFramesProcessedOut < frameCountOut) {\n        /*\n        We need to process in a specific order. We always do resampling first because it's likely\n        we're going to be increasing the channel count after spatialization. Also, I want to do\n        fading based on the output sample rate.\n\n        We'll first read into a buffer from the resampler. Then we'll do all processing that\n        operates on the on the input channel count. We'll then get the spatializer to output to\n        the output buffer and then do all effects from that point directly in the output buffer\n        in-place.\n\n        Note that we're always running the resampler if pitching is enabled, even when the pitch\n        is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch\n        when we move away from 1, back to 1, and then away from 1 again. We'll want to implement\n        any pitch=1 optimizations in the resampler itself.\n\n        There's a small optimization here that we'll utilize since it might be a fairly common\n        case. When the input and output channel counts are the same, we'll read straight into the\n        output buffer from the resampler and do everything in-place.\n        */\n        const float* pRunningFramesIn;\n        float* pRunningFramesOut;\n        float* pWorkingBuffer;   /* This is the buffer that we'll be processing frames in. This is in input channels. */\n        float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];\n        ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn;\n        ma_uint32 framesAvailableIn;\n        ma_uint32 framesAvailableOut;\n        ma_uint32 framesJustProcessedIn;\n        ma_uint32 framesJustProcessedOut;\n        ma_bool32 isWorkingBufferValid = MA_FALSE;\n\n        framesAvailableIn  = frameCountIn  - totalFramesProcessedIn;\n        framesAvailableOut = frameCountOut - totalFramesProcessedOut;\n\n        pRunningFramesIn  = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn);\n        pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut);\n\n        if (channelsIn == channelsOut) {\n            /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */\n            pWorkingBuffer = pRunningFramesOut;\n        } else {\n            /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */\n            pWorkingBuffer = temp;\n            if (framesAvailableOut > tempCapInFrames) {\n                framesAvailableOut = tempCapInFrames;\n            }\n        }\n\n        /* First is resampler. */\n        if (isPitchingEnabled) {\n            ma_uint64 resampleFrameCountIn  = framesAvailableIn;\n            ma_uint64 resampleFrameCountOut = framesAvailableOut;\n\n            ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut);\n            isWorkingBufferValid = MA_TRUE;\n\n            framesJustProcessedIn  = (ma_uint32)resampleFrameCountIn;\n            framesJustProcessedOut = (ma_uint32)resampleFrameCountOut;\n        } else {\n            framesJustProcessedIn  = ma_min(framesAvailableIn, framesAvailableOut);\n            framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */\n        }\n\n        /* Fading. */\n        if (isFadingEnabled) {\n            if (isWorkingBufferValid) {\n                ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut);   /* In-place processing. */\n            } else {\n                ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);\n                isWorkingBufferValid = MA_TRUE;\n            }\n        }\n\n        /*\n        If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case\n        we'll want to apply our volume now.\n        */\n        if (isVolumeSmoothingEnabled) {\n            if (isWorkingBufferValid) {\n                ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut);\n            } else {\n                ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);\n                isWorkingBufferValid = MA_TRUE;\n            }\n        }\n\n        /*\n        If at this point we still haven't actually done anything with the working buffer we need\n        to just read straight from the input buffer.\n        */\n        if (isWorkingBufferValid == MA_FALSE) {\n            pWorkingBuffer = (float*)pRunningFramesIn;  /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */\n        }\n\n        /* Spatialization. */\n        if (isSpatializationEnabled) {\n            ma_uint32 iListener;\n\n            /*\n            When determining the listener to use, we first check to see if the sound is pinned to a\n            specific listener. If so, we use that. Otherwise we just use the closest listener.\n            */\n            if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) {\n                iListener = pEngineNode->pinnedListenerIndex;\n            } else {\n                ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer);\n                iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z);\n            }\n\n            ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut);\n        } else {\n            /* No spatialization, but we still need to do channel conversion and master volume. */\n            float volume;\n            ma_engine_node_get_volume(pEngineNode, &volume);    /* Should never fail. */\n\n            if (channelsIn == channelsOut) {\n                /* No channel conversion required. Just copy straight to the output buffer. */\n                if (isVolumeSmoothingEnabled) {\n                    /* Volume has already been applied. Just copy straight to the output buffer. */\n                    ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut);\n                } else {\n                    /* Volume has not been applied yet. Copy and apply volume in the same pass. */\n                    ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume);\n                }\n            } else {\n                /* Channel conversion required. TODO: Add support for channel maps here. */\n                ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode);\n\n                /* If we're using smoothing, the volume will have already been applied. */\n                if (!isVolumeSmoothingEnabled) {\n                    ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume);\n                }\n            }\n        }\n\n        /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */\n\n        /* Panning. */\n        if (isPanningEnabled) {\n            ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut);   /* In-place processing. */\n        }\n\n        /* We're done for this chunk. */\n        totalFramesProcessedIn  += framesJustProcessedIn;\n        totalFramesProcessedOut += framesJustProcessedOut;\n\n        /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */\n        if (framesJustProcessedOut == 0) {\n            break;\n        }\n    }\n\n    /* At this point we're done processing. */\n    *pFrameCountIn  = totalFramesProcessedIn;\n    *pFrameCountOut = totalFramesProcessedOut;\n}\n\nstatic void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */\n    ma_result result = MA_SUCCESS;\n    ma_sound* pSound = (ma_sound*)pNode;\n    ma_uint32 frameCount = *pFrameCountOut;\n    ma_uint32 totalFramesRead = 0;\n    ma_format dataSourceFormat;\n    ma_uint32 dataSourceChannels;\n    ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];\n    ma_uint32 tempCapInFrames;\n    ma_uint64 seekTarget;\n\n    /* This is a data source node which means no input buses. */\n    (void)ppFramesIn;\n    (void)pFrameCountIn;\n\n    /* If we're marked at the end we need to stop the sound and do nothing. */\n    if (ma_sound_at_end(pSound)) {\n        ma_sound_stop(pSound);\n        *pFrameCountOut = 0;\n        return;\n    }\n\n    /* If we're seeking, do so now before reading. */\n    seekTarget = ma_atomic_load_64(&pSound->seekTarget);\n    if (seekTarget != MA_SEEK_TARGET_NONE) {\n        ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget);\n\n        /* Any time-dependant effects need to have their times updated. */\n        ma_node_set_time(pSound, seekTarget);\n\n        ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE);\n    }\n\n    /*\n    We want to update the pitch once. For sounds, this can be either at the start or at the end. If\n    we don't force this to only ever be updating once, we could end up in a situation where\n    retrieving the required input frame count ends up being different to what we actually retrieve.\n    What could happen is that the required input frame count is calculated, the pitch is update,\n    and then this processing function is called resulting in a different number of input frames\n    being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else\n    you'll hit the aforementioned bug.\n    */\n    ma_engine_node_update_pitch_if_required(&pSound->engineNode);\n\n    /*\n    For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ\n    from the main engine.\n    */\n    result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0);\n    if (result == MA_SUCCESS) {\n        tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels);\n\n        /* Keep reading until we've read as much as was requested or we reach the end of the data source. */\n        while (totalFramesRead < frameCount) {\n            ma_uint32 framesRemaining = frameCount - totalFramesRead;\n            ma_uint32 framesToRead;\n            ma_uint64 framesJustRead;\n            ma_uint32 frameCountIn;\n            ma_uint32 frameCountOut;\n            const float* pRunningFramesIn;\n            float* pRunningFramesOut;\n\n            /*\n            The first thing we need to do is read into the temporary buffer. We can calculate exactly\n            how many input frames we'll need after resampling.\n            */\n            framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining);\n            if (framesToRead > tempCapInFrames) {\n                framesToRead = tempCapInFrames;\n            }\n\n            result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead);\n\n            /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */\n            if (result == MA_AT_END) {\n                ma_sound_set_at_end(pSound, MA_TRUE);   /* This will be set to false in ma_sound_start(). */\n            }\n\n            pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound)));\n\n            frameCountIn = (ma_uint32)framesJustRead;\n            frameCountOut = framesRemaining;\n\n            /* Convert if necessary. */\n            if (dataSourceFormat == ma_format_f32) {\n                /* Fast path. No data conversion necessary. */\n                pRunningFramesIn = (float*)temp;\n                ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);\n            } else {\n                /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */\n                float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */\n                ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none);\n\n                /* Now that we have our samples in f32 format we can process like normal. */\n                pRunningFramesIn = tempf32;\n                ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);\n            }\n\n            /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */\n            MA_ASSERT(frameCountIn == framesJustRead);\n            totalFramesRead += (ma_uint32)frameCountOut;   /* Safe cast. */\n\n            if (result != MA_SUCCESS || ma_sound_at_end(pSound)) {\n                break;  /* Might have reached the end. */\n            }\n        }\n    }\n\n    *pFrameCountOut = totalFramesRead;\n}\n\nstatic void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)\n{\n    /*\n    Make sure the pitch is updated before trying to read anything. It's important that this is done\n    only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that\n    ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(),\n    and if another thread modifies the pitch just after that call it can result in a glitch due to\n    the input rate changing.\n    */\n    ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);\n\n    /* For groups, the input data has already been read and we just need to apply the effect. */\n    ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);\n}\n\nstatic ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount)\n{\n    ma_uint64 inputFrameCount;\n\n    MA_ASSERT(pInputFrameCount != NULL);\n\n    /* Our pitch will affect this calculation. We need to update it. */\n    ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);\n\n    inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount);\n    if (inputFrameCount > 0xFFFFFFFF) {\n        inputFrameCount = 0xFFFFFFFF;    /* Will never happen because miniaudio will only ever process in relatively small chunks. */\n    }\n\n    *pInputFrameCount = (ma_uint32)inputFrameCount;\n\n    return MA_SUCCESS;\n}\n\n\nstatic ma_node_vtable g_ma_engine_node_vtable__sound =\n{\n    ma_engine_node_process_pcm_frames__sound,\n    NULL,   /* onGetRequiredInputFrameCount */\n    0,      /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */\n    1,      /* Sounds have one output bus. */\n    0       /* Default flags. */\n};\n\nstatic ma_node_vtable g_ma_engine_node_vtable__group =\n{\n    ma_engine_node_process_pcm_frames__group,\n    ma_engine_node_get_required_input_frame_count__group,\n    1,      /* Groups have one input bus. */\n    1,      /* Groups have one output bus. */\n    MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */\n};\n\n\n\nstatic ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig)\n{\n    ma_node_config baseNodeConfig;\n\n    if (pConfig->type == ma_engine_node_type_sound) {\n        /* Sound. */\n        baseNodeConfig = ma_node_config_init();\n        baseNodeConfig.vtable       = &g_ma_engine_node_vtable__sound;\n        baseNodeConfig.initialState = ma_node_state_stopped;    /* Sounds are stopped by default. */\n    } else {\n        /* Group. */\n        baseNodeConfig = ma_node_config_init();\n        baseNodeConfig.vtable       = &g_ma_engine_node_vtable__group;\n        baseNodeConfig.initialState = ma_node_state_started;    /* Groups are started by default. */\n    }\n\n    return baseNodeConfig;\n}\n\nstatic ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig)\n{\n    return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]);\n}\n\ntypedef struct\n{\n    size_t sizeInBytes;\n    size_t baseNodeOffset;\n    size_t resamplerOffset;\n    size_t spatializerOffset;\n    size_t gainerOffset;\n} ma_engine_node_heap_layout;\n\nstatic ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout)\n{\n    ma_result result;\n    size_t tempHeapSize;\n    ma_node_config baseNodeConfig;\n    ma_linear_resampler_config resamplerConfig;\n    ma_spatializer_config spatializerConfig;\n    ma_gainer_config gainerConfig;\n    ma_uint32 channelsIn;\n    ma_uint32 channelsOut;\n    ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT};  /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */\n\n    MA_ASSERT(pHeapLayout);\n\n    MA_ZERO_OBJECT(pHeapLayout);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    if (pConfig->pEngine == NULL) {\n        return MA_INVALID_ARGS; /* An engine must be specified. */\n    }\n\n    pHeapLayout->sizeInBytes = 0;\n\n    channelsIn  = (pConfig->channelsIn  != 0) ? pConfig->channelsIn  : ma_engine_get_channels(pConfig->pEngine);\n    channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);\n\n\n    /* Base node. */\n    baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);\n    baseNodeConfig.pInputChannels  = &channelsIn;\n    baseNodeConfig.pOutputChannels = &channelsOut;\n\n    result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to retrieve the size of the heap for the base node. */\n    }\n\n    pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);\n\n\n    /* Resmapler. */\n    resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */\n    resamplerConfig.lpfOrder = 0;\n\n    result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to retrieve the size of the heap for the resampler. */\n    }\n\n    pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);\n\n\n    /* Spatializer. */\n    spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);\n\n    if (spatializerConfig.channelsIn == 2) {\n        spatializerConfig.pChannelMapIn = defaultStereoChannelMap;\n    }\n\n    result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize);\n    if (result != MA_SUCCESS) {\n        return result;  /* Failed to retrieve the size of the heap for the spatializer. */\n    }\n\n    pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes;\n    pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);\n\n\n    /* Gainer. Will not be used if we are not using smoothing. */\n    if (pConfig->volumeSmoothTimeInPCMFrames > 0) {\n        gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames);\n\n        result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize);\n        if (result != MA_SUCCESS) {\n            return result;\n        }\n\n        pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;\n        pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);\n    }\n\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes)\n{\n    ma_result result;\n    ma_engine_node_heap_layout heapLayout;\n\n    if (pHeapSizeInBytes == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    *pHeapSizeInBytes = 0;\n\n    result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    *pHeapSizeInBytes = heapLayout.sizeInBytes;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode)\n{\n    ma_result result;\n    ma_engine_node_heap_layout heapLayout;\n    ma_node_config baseNodeConfig;\n    ma_linear_resampler_config resamplerConfig;\n    ma_fader_config faderConfig;\n    ma_spatializer_config spatializerConfig;\n    ma_panner_config pannerConfig;\n    ma_gainer_config gainerConfig;\n    ma_uint32 channelsIn;\n    ma_uint32 channelsOut;\n    ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT};  /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */\n\n    if (pEngineNode == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pEngineNode);\n\n    result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) {\n        return MA_INVALID_ARGS; /* Invalid listener. */\n    }\n\n    pEngineNode->_pHeap = pHeap;\n    MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);\n\n    pEngineNode->pEngine                     = pConfig->pEngine;\n    pEngineNode->sampleRate                  = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine);\n    pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames;\n    pEngineNode->monoExpansionMode           = pConfig->monoExpansionMode;\n    ma_atomic_float_set(&pEngineNode->volume, 1);\n    pEngineNode->pitch                       = 1;\n    pEngineNode->oldPitch                    = 1;\n    pEngineNode->oldDopplerPitch             = 1;\n    pEngineNode->isPitchDisabled             = pConfig->isPitchDisabled;\n    pEngineNode->isSpatializationDisabled    = pConfig->isSpatializationDisabled;\n    pEngineNode->pinnedListenerIndex         = pConfig->pinnedListenerIndex;\n    ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1);\n    ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1);\n    ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0));\n    ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0));   /* <-- Indicates that the fade should start immediately. */\n\n    channelsIn  = (pConfig->channelsIn  != 0) ? pConfig->channelsIn  : ma_engine_get_channels(pConfig->pEngine);\n    channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);\n\n    /*\n    If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler\n    is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used.\n    */\n    if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) {\n        pEngineNode->isPitchDisabled = MA_FALSE;\n    }\n\n\n    /* Base node. */\n    baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);\n    baseNodeConfig.pInputChannels  = &channelsIn;\n    baseNodeConfig.pOutputChannels = &channelsOut;\n\n    result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode);\n    if (result != MA_SUCCESS) {\n        goto error0;\n    }\n\n\n    /*\n    We can now initialize the effects we need in order to implement the engine node. There's a\n    defined order of operations here, mainly centered around when we convert our channels from the\n    data source's native channel count to the engine's channel count. As a rule, we want to do as\n    much computation as possible before spatialization because there's a chance that will increase\n    the channel count, thereby increasing the amount of work needing to be done to process.\n    */\n\n    /* We'll always do resampling first. */\n    resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine));\n    resamplerConfig.lpfOrder = 0;    /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */\n\n    result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler);\n    if (result != MA_SUCCESS) {\n        goto error1;\n    }\n\n\n    /* After resampling will come the fader. */\n    faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine));\n\n    result = ma_fader_init(&faderConfig, &pEngineNode->fader);\n    if (result != MA_SUCCESS) {\n        goto error2;\n    }\n\n\n    /*\n    Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to\n    ensure channels counts link up correctly in the node graph.\n    */\n    spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);\n    spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames;\n\n    if (spatializerConfig.channelsIn == 2) {\n        spatializerConfig.pChannelMapIn = defaultStereoChannelMap;\n    }\n\n    result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer);\n    if (result != MA_SUCCESS) {\n        goto error2;\n    }\n\n\n    /*\n    After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't\n    be able to pan mono sounds.\n    */\n    pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]);\n\n    result = ma_panner_init(&pannerConfig, &pEngineNode->panner);\n    if (result != MA_SUCCESS) {\n        goto error3;\n    }\n\n\n    /* We'll need a gainer for smoothing out volume changes if we have a non-zero smooth time. We apply this before converting to the output channel count. */\n    if (pConfig->volumeSmoothTimeInPCMFrames > 0) {\n        gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames);\n\n        result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer);\n        if (result != MA_SUCCESS) {\n            goto error3;\n        }\n    }\n\n\n    return MA_SUCCESS;\n\n    /* No need for allocation callbacks here because we use a preallocated heap. */\nerror3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL);\nerror2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL);\nerror1: ma_node_uninit(&pEngineNode->baseNode, NULL);\nerror0: return result;\n}\n\nMA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode)\n{\n    ma_result result;\n    size_t heapSizeInBytes;\n    void* pHeap;\n\n    result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (heapSizeInBytes > 0) {\n        pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);\n        if (pHeap == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n    } else {\n        pHeap = NULL;\n    }\n\n    result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode);\n    if (result != MA_SUCCESS) {\n        ma_free(pHeap, pAllocationCallbacks);\n        return result;\n    }\n\n    pEngineNode->_ownsHeap = MA_TRUE;\n    return MA_SUCCESS;\n}\n\nMA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    /*\n    The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we\n    destroy anything that might be in the middle of being used by the processing function.\n    */\n    ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks);\n\n    /* Now that the node has been uninitialized we can safely uninitialize the rest. */\n    if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) {\n        ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks);\n    }\n\n    ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks);\n    ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks);\n\n    /* Free the heap last. */\n    if (pEngineNode->_ownsHeap) {\n        ma_free(pEngineNode->_pHeap, pAllocationCallbacks);\n    }\n}\n\n\nMA_API ma_sound_config ma_sound_config_init(void)\n{\n    return ma_sound_config_init_2(NULL);\n}\n\nMA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine)\n{\n    ma_sound_config config;\n\n    MA_ZERO_OBJECT(&config);\n\n    if (pEngine != NULL) {\n        config.monoExpansionMode = pEngine->monoExpansionMode;\n    } else {\n        config.monoExpansionMode = ma_mono_expansion_mode_default;\n    }\n\n    config.rangeEndInPCMFrames     = ~((ma_uint64)0);\n    config.loopPointEndInPCMFrames = ~((ma_uint64)0);\n\n    return config;\n}\n\nMA_API ma_sound_group_config ma_sound_group_config_init(void)\n{\n    return ma_sound_group_config_init_2(NULL);\n}\n\nMA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine)\n{\n    ma_sound_group_config config;\n\n    MA_ZERO_OBJECT(&config);\n\n    if (pEngine != NULL) {\n        config.monoExpansionMode = pEngine->monoExpansionMode;\n    } else {\n        config.monoExpansionMode = ma_mono_expansion_mode_default;\n    }\n\n    return config;\n}\n\n\nMA_API ma_engine_config ma_engine_config_init(void)\n{\n    ma_engine_config config;\n\n    MA_ZERO_OBJECT(&config);\n    config.listenerCount     = 1;   /* Always want at least one listener. */\n    config.monoExpansionMode = ma_mono_expansion_mode_default;\n\n    return config;\n}\n\n\n#if !defined(MA_NO_DEVICE_IO)\nstatic void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)\n{\n    ma_engine* pEngine = (ma_engine*)pDevice->pUserData;\n\n    (void)pFramesIn;\n\n    /*\n    Experiment: Try processing a resource manager job if we're on the Emscripten build.\n\n    This serves two purposes:\n\n        1) It ensures jobs are actually processed at some point since we cannot guarantee that the\n           caller is doing the right thing and calling ma_resource_manager_process_next_job(); and\n\n        2) It's an attempt at working around an issue where processing jobs on the Emscripten main\n           loop doesn't work as well as it should. When trying to load sounds without the `DECODE`\n           flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time\n           before the callback is processed. I think it's got something to do with the single-\n           threaded nature of Web, but I'm not entirely sure.\n    */\n    #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN)\n    {\n        if (pEngine->pResourceManager != NULL) {\n            if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {\n                ma_resource_manager_process_next_job(pEngine->pResourceManager);\n            }\n        }\n    }\n    #endif\n\n    ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL);\n}\n#endif\n\nMA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine)\n{\n    ma_result result;\n    ma_node_graph_config nodeGraphConfig;\n    ma_engine_config engineConfig;\n    ma_spatializer_listener_config listenerConfig;\n    ma_uint32 iListener;\n\n    if (pEngine == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pEngine);\n\n    /* The config is allowed to be NULL in which case we use defaults for everything. */\n    if (pConfig != NULL) {\n        engineConfig = *pConfig;\n    } else {\n        engineConfig = ma_engine_config_init();\n    }\n\n    pEngine->monoExpansionMode = engineConfig.monoExpansionMode;\n    pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames;\n    pEngine->onProcess = engineConfig.onProcess;\n    pEngine->pProcessUserData = engineConfig.pProcessUserData;\n    ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks);\n\n    #if !defined(MA_NO_RESOURCE_MANAGER)\n    {\n        pEngine->pResourceManager = engineConfig.pResourceManager;\n    }\n    #endif\n\n    #if !defined(MA_NO_DEVICE_IO)\n    {\n        pEngine->pDevice = engineConfig.pDevice;\n\n        /* If we don't have a device, we need one. */\n        if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) {\n            ma_device_config deviceConfig;\n\n            pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks);\n            if (pEngine->pDevice == NULL) {\n                return MA_OUT_OF_MEMORY;\n            }\n\n            deviceConfig = ma_device_config_init(ma_device_type_playback);\n            deviceConfig.playback.pDeviceID        = engineConfig.pPlaybackDeviceID;\n            deviceConfig.playback.format           = ma_format_f32;\n            deviceConfig.playback.channels         = engineConfig.channels;\n            deviceConfig.sampleRate                = engineConfig.sampleRate;\n            deviceConfig.dataCallback              = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal;\n            deviceConfig.pUserData                 = pEngine;\n            deviceConfig.notificationCallback      = engineConfig.notificationCallback;\n            deviceConfig.periodSizeInFrames        = engineConfig.periodSizeInFrames;\n            deviceConfig.periodSizeInMilliseconds  = engineConfig.periodSizeInMilliseconds;\n            deviceConfig.noPreSilencedOutputBuffer = MA_TRUE;    /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */\n            deviceConfig.noClip                    = MA_TRUE;    /* The engine will do clipping itself. */\n\n            if (engineConfig.pContext == NULL) {\n                ma_context_config contextConfig = ma_context_config_init();\n                contextConfig.allocationCallbacks = pEngine->allocationCallbacks;\n                contextConfig.pLog = engineConfig.pLog;\n\n                /* If the engine config does not specify a log, use the resource manager's if we have one. */\n                #ifndef MA_NO_RESOURCE_MANAGER\n                {\n                    if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) {\n                        contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager);\n                    }\n                }\n                #endif\n\n                result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice);\n            } else {\n                result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice);\n            }\n\n            if (result != MA_SUCCESS) {\n                ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);\n                pEngine->pDevice = NULL;\n                return result;\n            }\n\n            pEngine->ownsDevice = MA_TRUE;\n        }\n\n        /* Update the channel count and sample rate of the engine config so we can reference it below. */\n        if (pEngine->pDevice != NULL) {\n            engineConfig.channels   = pEngine->pDevice->playback.channels;\n            engineConfig.sampleRate = pEngine->pDevice->sampleRate;\n        }\n    }\n    #endif\n\n    if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) {\n        return MA_INVALID_ARGS;\n    }\n\n    pEngine->sampleRate = engineConfig.sampleRate;\n\n    /* The engine always uses either the log that was passed into the config, or the context's log is available. */\n    if (engineConfig.pLog != NULL) {\n        pEngine->pLog = engineConfig.pLog;\n    } else {\n        #if !defined(MA_NO_DEVICE_IO)\n        {\n            pEngine->pLog = ma_device_get_log(pEngine->pDevice);\n        }\n        #else\n        {\n            pEngine->pLog = NULL;\n        }\n        #endif\n    }\n\n\n    /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */\n    nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels);\n    nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames;\n\n    result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph);\n    if (result != MA_SUCCESS) {\n        goto on_error_1;\n    }\n\n\n    /* We need at least one listener. */\n    if (engineConfig.listenerCount == 0) {\n        engineConfig.listenerCount = 1;\n    }\n\n    if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) {\n        result = MA_INVALID_ARGS;   /* Too many listeners. */\n        goto on_error_1;\n    }\n\n    for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) {\n        listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph));\n\n        /*\n        If we're using a device, use the device's channel map for the listener. Otherwise just use\n        miniaudio's default channel map.\n        */\n        #if !defined(MA_NO_DEVICE_IO)\n        {\n            if (pEngine->pDevice != NULL) {\n                /*\n                Temporarily disabled. There is a subtle bug here where front-left and front-right\n                will be used by the device's channel map, but this is not what we want to use for\n                spatialization. Instead we want to use side-left and side-right. I need to figure\n                out a better solution for this. For now, disabling the use of device channel maps.\n                */\n                /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/\n            }\n        }\n        #endif\n\n        result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]);  /* TODO: Change this to a pre-allocated heap. */\n        if (result != MA_SUCCESS) {\n            goto on_error_2;\n        }\n\n        pEngine->listenerCount += 1;\n    }\n\n\n    /* Gain smoothing for spatialized sounds. */\n    pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames;\n    if (pEngine->gainSmoothTimeInFrames == 0) {\n        ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds;\n        if (gainSmoothTimeInMilliseconds == 0) {\n            gainSmoothTimeInMilliseconds = 8;\n        }\n\n        pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000;  /* 8ms by default. */\n    }\n\n\n    /* We need a resource manager. */\n    #ifndef MA_NO_RESOURCE_MANAGER\n    {\n        if (pEngine->pResourceManager == NULL) {\n            ma_resource_manager_config resourceManagerConfig;\n\n            pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks);\n            if (pEngine->pResourceManager == NULL) {\n                result = MA_OUT_OF_MEMORY;\n                goto on_error_2;\n            }\n\n            resourceManagerConfig = ma_resource_manager_config_init();\n            resourceManagerConfig.pLog              = pEngine->pLog;    /* Always use the engine's log for internally-managed resource managers. */\n            resourceManagerConfig.decodedFormat     = ma_format_f32;\n            resourceManagerConfig.decodedChannels   = 0;  /* Leave the decoded channel count as 0 so we can get good spatialization. */\n            resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine);\n            ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks);\n            resourceManagerConfig.pVFS              = engineConfig.pResourceManagerVFS;\n\n            /* The Emscripten build cannot use threads. */\n            #if defined(MA_EMSCRIPTEN)\n            {\n                resourceManagerConfig.jobThreadCount = 0;\n                resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;\n            }\n            #endif\n\n            result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager);\n            if (result != MA_SUCCESS) {\n                goto on_error_3;\n            }\n\n            pEngine->ownsResourceManager = MA_TRUE;\n        }\n    }\n    #endif\n\n    /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */\n    pEngine->inlinedSoundLock  = 0;\n    pEngine->pInlinedSoundHead = NULL;\n\n    /* Start the engine if required. This should always be the last step. */\n    #if !defined(MA_NO_DEVICE_IO)\n    {\n        if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) {\n            result = ma_engine_start(pEngine);\n            if (result != MA_SUCCESS) {\n                goto on_error_4;    /* Failed to start the engine. */\n            }\n        }\n    }\n    #endif\n\n    return MA_SUCCESS;\n\n#if !defined(MA_NO_DEVICE_IO)\non_error_4:\n#endif\n#if !defined(MA_NO_RESOURCE_MANAGER)\non_error_3:\n    if (pEngine->ownsResourceManager) {\n        ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);\n    }\n#endif  /* MA_NO_RESOURCE_MANAGER */\non_error_2:\n    for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {\n        ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);\n    }\n\n    ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);\non_error_1:\n    #if !defined(MA_NO_DEVICE_IO)\n    {\n        if (pEngine->ownsDevice) {\n            ma_device_uninit(pEngine->pDevice);\n            ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);\n        }\n    }\n    #endif\n\n    return result;\n}\n\nMA_API void ma_engine_uninit(ma_engine* pEngine)\n{\n    ma_uint32 iListener;\n\n    if (pEngine == NULL) {\n        return;\n    }\n\n    /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */\n    #if !defined(MA_NO_DEVICE_IO)\n    {\n        if (pEngine->ownsDevice) {\n            ma_device_uninit(pEngine->pDevice);\n            ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);\n        } else {\n            if (pEngine->pDevice != NULL) {\n                ma_device_stop(pEngine->pDevice);\n            }\n        }\n    }\n    #endif\n\n    /*\n    All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case\n    I want to do some kind of garbage collection later on.\n    */\n    ma_spinlock_lock(&pEngine->inlinedSoundLock);\n    {\n        for (;;) {\n            ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead;\n            if (pSoundToDelete == NULL) {\n                break;  /* Done. */\n            }\n\n            pEngine->pInlinedSoundHead = pSoundToDelete->pNext;\n\n            ma_sound_uninit(&pSoundToDelete->sound);\n            ma_free(pSoundToDelete, &pEngine->allocationCallbacks);\n        }\n    }\n    ma_spinlock_unlock(&pEngine->inlinedSoundLock);\n\n    for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {\n        ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);\n    }\n\n    /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */\n    ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);\n\n    /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */\n#ifndef MA_NO_RESOURCE_MANAGER\n    if (pEngine->ownsResourceManager) {\n        ma_resource_manager_uninit(pEngine->pResourceManager);\n        ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);\n    }\n#endif\n}\n\nMA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)\n{\n    ma_result result;\n    ma_uint64 framesRead = 0;\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = 0;\n    }\n\n    result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pFramesRead != NULL) {\n        *pFramesRead = framesRead;\n    }\n\n    if (pEngine->onProcess) {\n        pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead);  /* Safe cast to float* because the engine always works on floating point samples. */\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine)\n{\n    if (pEngine == NULL) {\n        return NULL;\n    }\n\n    return &pEngine->nodeGraph;\n}\n\n#if !defined(MA_NO_RESOURCE_MANAGER)\nMA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine)\n{\n    if (pEngine == NULL) {\n        return NULL;\n    }\n\n    #if !defined(MA_NO_RESOURCE_MANAGER)\n    {\n        return pEngine->pResourceManager;\n    }\n    #else\n    {\n        return NULL;\n    }\n    #endif\n}\n#endif\n\nMA_API ma_device* ma_engine_get_device(ma_engine* pEngine)\n{\n    if (pEngine == NULL) {\n        return NULL;\n    }\n\n    #if !defined(MA_NO_DEVICE_IO)\n    {\n        return pEngine->pDevice;\n    }\n    #else\n    {\n        return NULL;\n    }\n    #endif\n}\n\nMA_API ma_log* ma_engine_get_log(ma_engine* pEngine)\n{\n    if (pEngine == NULL) {\n        return NULL;\n    }\n\n    if (pEngine->pLog != NULL) {\n        return pEngine->pLog;\n    } else {\n        #if !defined(MA_NO_DEVICE_IO)\n        {\n            return ma_device_get_log(ma_engine_get_device(pEngine));\n        }\n        #else\n        {\n            return NULL;\n        }\n        #endif\n    }\n}\n\nMA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine)\n{\n    return ma_node_graph_get_endpoint(&pEngine->nodeGraph);\n}\n\nMA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine)\n{\n    return ma_node_graph_get_time(&pEngine->nodeGraph);\n}\n\nMA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine)\n{\n    return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine);\n}\n\nMA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime)\n{\n    return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime);\n}\n\nMA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime)\n{\n    return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000);\n}\n\nMA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine)\n{\n    return ma_engine_get_time_in_pcm_frames(pEngine);\n}\n\nMA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime)\n{\n    return ma_engine_set_time_in_pcm_frames(pEngine, globalTime);\n}\n\nMA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine)\n{\n    return ma_node_graph_get_channels(&pEngine->nodeGraph);\n}\n\nMA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine)\n{\n    if (pEngine == NULL) {\n        return 0;\n    }\n\n    return pEngine->sampleRate;\n}\n\n\nMA_API ma_result ma_engine_start(ma_engine* pEngine)\n{\n    ma_result result;\n\n    if (pEngine == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_DEVICE_IO)\n    {\n        if (pEngine->pDevice != NULL) {\n            result = ma_device_start(pEngine->pDevice);\n        } else {\n            result = MA_INVALID_OPERATION;  /* The engine is running without a device which means there's no real notion of \"starting\" the engine. */\n        }\n    }\n    #else\n    {\n        result = MA_INVALID_OPERATION;  /* Device IO is disabled, so there's no real notion of \"starting\" the engine. */\n    }\n    #endif\n\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_engine_stop(ma_engine* pEngine)\n{\n    ma_result result;\n\n    if (pEngine == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    #if !defined(MA_NO_DEVICE_IO)\n    {\n        if (pEngine->pDevice != NULL) {\n            result = ma_device_stop(pEngine->pDevice);\n        } else {\n            result = MA_INVALID_OPERATION;  /* The engine is running without a device which means there's no real notion of \"stopping\" the engine. */\n        }\n    }\n    #else\n    {\n        result = MA_INVALID_OPERATION;  /* Device IO is disabled, so there's no real notion of \"stopping\" the engine. */\n    }\n    #endif\n\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume)\n{\n    if (pEngine == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume);\n}\n\nMA_API float ma_engine_get_volume(ma_engine* pEngine)\n{\n    if (pEngine == NULL) {\n        return 0;\n    }\n\n    return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0);\n}\n\nMA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB)\n{\n    return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB));\n}\n\nMA_API float ma_engine_get_gain_db(ma_engine* pEngine)\n{\n    return ma_volume_linear_to_db(ma_engine_get_volume(pEngine));\n}\n\n\nMA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine)\n{\n    if (pEngine == NULL) {\n        return 0;\n    }\n\n    return pEngine->listenerCount;\n}\n\nMA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ)\n{\n    ma_uint32 iListener;\n    ma_uint32 iListenerClosest;\n    float closestLen2 = MA_FLT_MAX;\n\n    if (pEngine == NULL || pEngine->listenerCount == 1) {\n        return 0;\n    }\n\n    iListenerClosest = 0;\n    for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {\n        if (ma_engine_listener_is_enabled(pEngine, iListener)) {\n            float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ)));\n            if (closestLen2 > len2) {\n                closestLen2 = len2;\n                iListenerClosest = iListener;\n            }\n        }\n    }\n\n    MA_ASSERT(iListenerClosest < 255);\n    return iListenerClosest;\n}\n\nMA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)\n{\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return;\n    }\n\n    ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z);\n}\n\nMA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex)\n{\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return ma_vec3f_init_3f(0, 0, 0);\n    }\n\n    return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]);\n}\n\nMA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)\n{\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return;\n    }\n\n    ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z);\n}\n\nMA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex)\n{\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return ma_vec3f_init_3f(0, 0, -1);\n    }\n\n    return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]);\n}\n\nMA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)\n{\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return;\n    }\n\n    ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z);\n}\n\nMA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex)\n{\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return ma_vec3f_init_3f(0, 0, 0);\n    }\n\n    return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]);\n}\n\nMA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain)\n{\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return;\n    }\n\n    ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain);\n}\n\nMA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)\n{\n    if (pInnerAngleInRadians != NULL) {\n        *pInnerAngleInRadians = 0;\n    }\n\n    if (pOuterAngleInRadians != NULL) {\n        *pOuterAngleInRadians = 0;\n    }\n\n    if (pOuterGain != NULL) {\n        *pOuterGain = 0;\n    }\n\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return;\n    }\n\n    ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);\n}\n\nMA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)\n{\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return;\n    }\n\n    ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z);\n}\n\nMA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex)\n{\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return ma_vec3f_init_3f(0, 1, 0);\n    }\n\n    return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]);\n}\n\nMA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled)\n{\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return;\n    }\n\n    ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled);\n}\n\nMA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex)\n{\n    if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {\n        return MA_FALSE;\n    }\n\n    return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]);\n}\n\n\n#ifndef MA_NO_RESOURCE_MANAGER\nMA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex)\n{\n    ma_result result = MA_SUCCESS;\n    ma_sound_inlined* pSound = NULL;\n    ma_sound_inlined* pNextSound = NULL;\n\n    if (pEngine == NULL || pFilePath == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Attach to the endpoint node if nothing is specicied. */\n    if (pNode == NULL) {\n        pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph);\n        nodeInputBusIndex = 0;\n    }\n\n    /*\n    We want to check if we can recycle an already-allocated inlined sound. Since this is just a\n    helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep\n    the implementation simple. Maybe this can be optimized later if there's enough demand, but\n    if this function is being used it probably means the caller doesn't really care too much.\n\n    What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise\n    we just keep iterating. If we reach the end without finding a sound to recycle we just\n    allocate a new one. This doesn't scale well for a massive number of sounds being played\n    simultaneously as we don't ever actually free the sound objects. Some kind of garbage\n    collection routine might be valuable for this which I'll think about.\n    */\n    ma_spinlock_lock(&pEngine->inlinedSoundLock);\n    {\n        ma_uint32 soundFlags = 0;\n\n        for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) {\n            if (ma_sound_at_end(&pNextSound->sound)) {\n                /*\n                The sound is at the end which means it's available for recycling. All we need to do\n                is uninitialize it and reinitialize it. All we're doing is recycling memory.\n                */\n                pSound = pNextSound;\n                ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1);\n                break;\n            }\n        }\n\n        if (pSound != NULL) {\n            /*\n            We actually want to detach the sound from the list here. The reason is because we want the sound\n            to be in a consistent state at the non-recycled case to simplify the logic below.\n            */\n            if (pEngine->pInlinedSoundHead == pSound) {\n                pEngine->pInlinedSoundHead =  pSound->pNext;\n            }\n\n            if (pSound->pPrev != NULL) {\n                pSound->pPrev->pNext = pSound->pNext;\n            }\n            if (pSound->pNext != NULL) {\n                pSound->pNext->pPrev = pSound->pPrev;\n            }\n\n            /* Now the previous sound needs to be uninitialized. */\n            ma_sound_uninit(&pNextSound->sound);\n        } else {\n            /* No sound available for recycling. Allocate one now. */\n            pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks);\n        }\n\n        if (pSound != NULL) {   /* Safety check for the allocation above. */\n            /*\n            At this point we should have memory allocated for the inlined sound. We just need\n            to initialize it like a normal sound now.\n            */\n            soundFlags |= MA_SOUND_FLAG_ASYNC;                 /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */\n            soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */\n            soundFlags |= MA_SOUND_FLAG_NO_PITCH;              /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */\n            soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION;     /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */\n\n            result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound);\n            if (result == MA_SUCCESS) {\n                /* Now attach the sound to the graph. */\n                result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex);\n                if (result == MA_SUCCESS) {\n                    /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */\n                    pSound->pNext = pEngine->pInlinedSoundHead;\n                    pSound->pPrev = NULL;\n\n                    pEngine->pInlinedSoundHead = pSound;    /* <-- This is what attaches the sound to the list. */\n                    if (pSound->pNext != NULL) {\n                        pSound->pNext->pPrev = pSound;\n                    }\n                } else {\n                    ma_free(pSound, &pEngine->allocationCallbacks);\n                }\n            } else {\n                ma_free(pSound, &pEngine->allocationCallbacks);\n            }\n        } else {\n            result = MA_OUT_OF_MEMORY;\n        }\n    }\n    ma_spinlock_unlock(&pEngine->inlinedSoundLock);\n\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* Finally we can start playing the sound. */\n    result = ma_sound_start(&pSound->sound);\n    if (result != MA_SUCCESS) {\n        /* Failed to start the sound. We need to mark it for recycling and return an error. */\n        ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE);\n        return result;\n    }\n\n    ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1);\n    return result;\n}\n\nMA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup)\n{\n    return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0);\n}\n#endif\n\n\nstatic ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pSound);\n    pSound->seekTarget = MA_SEEK_TARGET_NONE;\n\n    if (pEngine == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return MA_SUCCESS;\n}\n\nstatic ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)\n{\n    ma_result result;\n    ma_engine_node_config engineNodeConfig;\n    ma_engine_node_type type;   /* Will be set to ma_engine_node_type_group if no data source is specified. */\n\n    /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */\n    MA_ASSERT(pEngine != NULL);\n    MA_ASSERT(pSound  != NULL);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pSound->pDataSource = pConfig->pDataSource;\n\n    if (pConfig->pDataSource != NULL) {\n        type = ma_engine_node_type_sound;\n    } else {\n        type = ma_engine_node_type_group;\n    }\n\n    /*\n    Sounds are engine nodes. Before we can initialize this we need to determine the channel count.\n    If we can't do this we need to abort. It's up to the caller to ensure they're using a data\n    source that provides this information upfront.\n    */\n    engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags);\n    engineNodeConfig.channelsIn                  = pConfig->channelsIn;\n    engineNodeConfig.channelsOut                 = pConfig->channelsOut;\n    engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames;\n    engineNodeConfig.monoExpansionMode           = pConfig->monoExpansionMode;\n\n    if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) {\n        engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames;\n    }\n\n    /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */\n    if (pConfig->pDataSource != NULL) {\n        result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0);\n        if (result != MA_SUCCESS) {\n            return result;  /* Failed to retrieve the channel count. */\n        }\n\n        if (engineNodeConfig.channelsIn == 0) {\n            return MA_INVALID_OPERATION;    /* Invalid channel count. */\n        }\n\n        if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) {\n            engineNodeConfig.channelsOut = engineNodeConfig.channelsIn;\n        }\n    }\n\n\n    /* Getting here means we should have a valid channel count and we can initialize the engine node. */\n    result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* If no attachment is specified, attach the sound straight to the endpoint. */\n    if (pConfig->pInitialAttachment == NULL) {\n        /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */\n        if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) {\n            result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0);\n        }\n    } else {\n        /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */\n        result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex);\n    }\n\n    if (result != MA_SUCCESS) {\n        ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks);\n        return result;\n    }\n\n\n    /* Apply initial range and looping state to the data source if applicable. */\n    if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) {\n        ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);\n    }\n\n    if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) {\n        ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);\n    }\n\n    ma_sound_set_looping(pSound, pConfig->isLooping);\n\n    return MA_SUCCESS;\n}\n\n#ifndef MA_NO_RESOURCE_MANAGER\nMA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)\n{\n    ma_result result = MA_SUCCESS;\n    ma_uint32 flags;\n    ma_sound_config config;\n    ma_resource_manager_pipeline_notifications notifications;\n\n    /*\n    The engine requires knowledge of the channel count of the underlying data source before it can\n    initialize the sound. Therefore, we need to make the resource manager wait until initialization\n    of the underlying data source to be initialized so we can get access to the channel count. To\n    do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced.\n\n    Because we're initializing the data source before the sound, there's a chance the notification\n    will get triggered before this function returns. This is OK, so long as the caller is aware of\n    it and can avoid accessing the sound from within the notification.\n    */\n    flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT;\n\n    pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks);\n    if (pSound->pResourceManagerDataSource == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    /* Removed in 0.12. Set pDoneFence on the notifications. */\n    notifications = pConfig->initNotifications;\n    if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) {\n        notifications.done.pFence = pConfig->pDoneFence;\n    }\n\n    /*\n    We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does\n    not return prematurely before the sound has finished initializing.\n    */\n    if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); }\n    {\n        ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init();\n        resourceManagerDataSourceConfig.pFilePath                   = pConfig->pFilePath;\n        resourceManagerDataSourceConfig.pFilePathW                  = pConfig->pFilePathW;\n        resourceManagerDataSourceConfig.flags                       = flags;\n        resourceManagerDataSourceConfig.pNotifications              = &notifications;\n        resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames;\n        resourceManagerDataSourceConfig.rangeBegInPCMFrames         = pConfig->rangeBegInPCMFrames;\n        resourceManagerDataSourceConfig.rangeEndInPCMFrames         = pConfig->rangeEndInPCMFrames;\n        resourceManagerDataSourceConfig.loopPointBegInPCMFrames     = pConfig->loopPointBegInPCMFrames;\n        resourceManagerDataSourceConfig.loopPointEndInPCMFrames     = pConfig->loopPointEndInPCMFrames;\n        resourceManagerDataSourceConfig.isLooping                   = pConfig->isLooping;\n\n        result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource);\n        if (result != MA_SUCCESS) {\n            goto done;\n        }\n\n        pSound->ownsDataSource = MA_TRUE;   /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */\n\n        /* We need to use a slightly customized version of the config so we'll need to make a copy. */\n        config = *pConfig;\n        config.pFilePath   = NULL;\n        config.pFilePathW  = NULL;\n        config.pDataSource = pSound->pResourceManagerDataSource;\n\n        result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);\n        if (result != MA_SUCCESS) {\n            ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);\n            ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);\n            MA_ZERO_OBJECT(pSound);\n            goto done;\n        }\n    }\ndone:\n    if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); }\n    return result;\n}\n\nMA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound)\n{\n    ma_sound_config config;\n\n    if (pFilePath == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    config = ma_sound_config_init_2(pEngine);\n    config.pFilePath          = pFilePath;\n    config.flags              = flags;\n    config.pInitialAttachment = pGroup;\n    config.pDoneFence         = pDoneFence;\n\n    return ma_sound_init_ex(pEngine, &config, pSound);\n}\n\nMA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound)\n{\n    ma_sound_config config;\n\n    if (pFilePath == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    config = ma_sound_config_init_2(pEngine);\n    config.pFilePathW         = pFilePath;\n    config.flags              = flags;\n    config.pInitialAttachment = pGroup;\n    config.pDoneFence         = pDoneFence;\n\n    return ma_sound_init_ex(pEngine, &config, pSound);\n}\n\nMA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound)\n{\n    ma_result result;\n    ma_sound_config config;\n\n    result = ma_sound_preinit(pEngine, pSound);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pExistingSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */\n    if (pExistingSound->pResourceManagerDataSource == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /*\n    We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream)\n    this will fail.\n    */\n    pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks);\n    if (pSound->pResourceManagerDataSource == NULL) {\n        return MA_OUT_OF_MEMORY;\n    }\n\n    result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource);\n    if (result != MA_SUCCESS) {\n        ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);\n        return result;\n    }\n\n    config = ma_sound_config_init_2(pEngine);\n    config.pDataSource                 = pSound->pResourceManagerDataSource;\n    config.flags                       = flags;\n    config.pInitialAttachment          = pGroup;\n    config.monoExpansionMode           = pExistingSound->engineNode.monoExpansionMode;\n    config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames;\n\n    result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);\n    if (result != MA_SUCCESS) {\n        ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);\n        ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);\n        MA_ZERO_OBJECT(pSound);\n        return result;\n    }\n\n    /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */\n    pSound->ownsDataSource = MA_TRUE;\n\n    return MA_SUCCESS;\n}\n#endif\n\nMA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound)\n{\n    ma_sound_config config = ma_sound_config_init_2(pEngine);\n    config.pDataSource        = pDataSource;\n    config.flags              = flags;\n    config.pInitialAttachment = pGroup;\n    return ma_sound_init_ex(pEngine, &config, pSound);\n}\n\nMA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)\n{\n    ma_result result;\n\n    result = ma_sound_preinit(pEngine, pSound);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    pSound->endCallback          = pConfig->endCallback;\n    pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData;\n\n    /* We need to load the sound differently depending on whether or not we're loading from a file. */\n#ifndef MA_NO_RESOURCE_MANAGER\n    if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) {\n        return ma_sound_init_from_file_internal(pEngine, pConfig, pSound);\n    } else\n#endif\n    {\n        /*\n        Getting here means we're not loading from a file. We may be loading from an already-initialized\n        data source, or none at all. If we aren't specifying any data source, we'll be initializing the\n        the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this\n        for us, so no special treatment required here.\n        */\n        return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound);\n    }\n}\n\nMA_API void ma_sound_uninit(ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    /*\n    Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done\n    so which makes thread safety beyond this point trivial.\n    */\n    ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks);\n\n    /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */\n#ifndef MA_NO_RESOURCE_MANAGER\n    if (pSound->ownsDataSource) {\n        ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);\n        ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks);\n        pSound->pDataSource = NULL;\n    }\n#else\n    MA_ASSERT(pSound->ownsDataSource == MA_FALSE);\n#endif\n}\n\nMA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return NULL;\n    }\n\n    return pSound->engineNode.pEngine;\n}\n\nMA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return NULL;\n    }\n\n    return pSound->pDataSource;\n}\n\nMA_API ma_result ma_sound_start(ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* If the sound is already playing, do nothing. */\n    if (ma_sound_is_playing(pSound)) {\n        return MA_SUCCESS;\n    }\n\n    /* If the sound is at the end it means we want to start from the start again. */\n    if (ma_sound_at_end(pSound)) {\n        ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0);\n        if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) {\n            return result;  /* Failed to seek back to the start. */\n        }\n\n        /* Make sure we clear the end indicator. */\n        ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE);\n    }\n\n    /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */\n    ma_node_set_state(pSound, ma_node_state_started);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_sound_stop(ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */\n    ma_node_set_state(pSound, ma_node_state_stopped);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames)\n{\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */\n    ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds)\n{\n    ma_uint64 sampleRate;\n\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));\n\n    return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000);\n}\n\nMA_API void ma_sound_set_volume(ma_sound* pSound, float volume)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_engine_node_set_volume(&pSound->engineNode, volume);\n}\n\nMA_API float ma_sound_get_volume(const ma_sound* pSound)\n{\n    float volume = 0;\n\n    if (pSound == NULL) {\n        return 0;\n    }\n\n    ma_engine_node_get_volume(&pSound->engineNode, &volume);\n\n    return volume;\n}\n\nMA_API void ma_sound_set_pan(ma_sound* pSound, float pan)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_panner_set_pan(&pSound->engineNode.panner, pan);\n}\n\nMA_API float ma_sound_get_pan(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return 0;\n    }\n\n    return ma_panner_get_pan(&pSound->engineNode.panner);\n}\n\nMA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_panner_set_mode(&pSound->engineNode.panner, panMode);\n}\n\nMA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return ma_pan_mode_balance;\n    }\n\n    return ma_panner_get_mode(&pSound->engineNode.panner);\n}\n\nMA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    if (pitch <= 0) {\n        return;\n    }\n\n    ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release);\n}\n\nMA_API float ma_sound_get_pitch(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return 0;\n    }\n\n    return ma_atomic_load_f32(&pSound->engineNode.pitch);    /* Naughty const-cast for this. */\n}\n\nMA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release);\n}\n\nMA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return MA_FALSE;\n    }\n\n    return ma_engine_node_is_spatialization_enabled(&pSound->engineNode);\n}\n\nMA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex)\n{\n    if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) {\n        return;\n    }\n\n    ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release);\n}\n\nMA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return MA_LISTENER_INDEX_CLOSEST;\n    }\n\n    return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire);\n}\n\nMA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound)\n{\n    ma_uint32 listenerIndex;\n\n    if (pSound == NULL) {\n        return 0;\n    }\n\n    listenerIndex = ma_sound_get_pinned_listener_index(pSound);\n    if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) {\n        ma_vec3f position = ma_sound_get_position(pSound);\n        return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z);\n    }\n\n    return listenerIndex;\n}\n\nMA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound)\n{\n    ma_vec3f relativePos;\n    ma_engine* pEngine;\n\n    if (pSound == NULL) {\n        return ma_vec3f_init_3f(0, 0, -1);\n    }\n\n    pEngine = ma_sound_get_engine(pSound);\n    if (pEngine == NULL) {\n        return ma_vec3f_init_3f(0, 0, -1);\n    }\n\n    ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL);\n\n    return ma_vec3f_normalize(ma_vec3f_neg(relativePos));\n}\n\nMA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z);\n}\n\nMA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return ma_vec3f_init_3f(0, 0, 0);\n    }\n\n    return ma_spatializer_get_position(&pSound->engineNode.spatializer);\n}\n\nMA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z);\n}\n\nMA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return ma_vec3f_init_3f(0, 0, 0);\n    }\n\n    return ma_spatializer_get_direction(&pSound->engineNode.spatializer);\n}\n\nMA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z);\n}\n\nMA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return ma_vec3f_init_3f(0, 0, 0);\n    }\n\n    return ma_spatializer_get_velocity(&pSound->engineNode.spatializer);\n}\n\nMA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel);\n}\n\nMA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return ma_attenuation_model_none;\n    }\n\n    return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer);\n}\n\nMA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning);\n}\n\nMA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return ma_positioning_absolute;\n    }\n\n    return ma_spatializer_get_positioning(&pSound->engineNode.spatializer);\n}\n\nMA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff);\n}\n\nMA_API float ma_sound_get_rolloff(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return 0;\n    }\n\n    return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer);\n}\n\nMA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain);\n}\n\nMA_API float ma_sound_get_min_gain(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return 0;\n    }\n\n    return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer);\n}\n\nMA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain);\n}\n\nMA_API float ma_sound_get_max_gain(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return 0;\n    }\n\n    return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer);\n}\n\nMA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance);\n}\n\nMA_API float ma_sound_get_min_distance(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return 0;\n    }\n\n    return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer);\n}\n\nMA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance);\n}\n\nMA_API float ma_sound_get_max_distance(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return 0;\n    }\n\n    return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer);\n}\n\nMA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain);\n}\n\nMA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)\n{\n    if (pInnerAngleInRadians != NULL) {\n        *pInnerAngleInRadians = 0;\n    }\n\n    if (pOuterAngleInRadians != NULL) {\n        *pOuterAngleInRadians = 0;\n    }\n\n    if (pOuterGain != NULL) {\n        *pOuterGain = 0;\n    }\n\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);\n}\n\nMA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor);\n}\n\nMA_API float ma_sound_get_doppler_factor(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return 0;\n    }\n\n    return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer);\n}\n\nMA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor);\n}\n\nMA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return 1;\n    }\n\n    return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer);\n}\n\n\nMA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0));\n}\n\nMA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000);\n}\n\nMA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    /*\n    We don't want to update the fader at this point because we need to use the engine's current time\n    to derive the fader's start offset. The timer is being updated on the audio thread so in order to\n    do this as accurately as possible we'll need to defer this to the audio thread.\n    */\n    ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg);\n    ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd);\n    ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames);\n    ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames);\n}\n\nMA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds)\n{\n    ma_uint32 sampleRate;\n\n    if (pSound == NULL) {\n        return;\n    }\n\n    sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));\n\n    ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000);\n}\n\nMA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    return ma_fader_get_current_volume(&pSound->engineNode.fader);\n}\n\nMA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames);\n}\n\nMA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);\n}\n\nMA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0);\n}\n\nMA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);\n}\n\nMA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    if (fadeLengthInFrames > 0) {\n        if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) {\n            fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames;\n        }\n\n        ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames);\n    }\n\n    ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames);\n}\n\nMA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds)\n{\n    ma_uint32 sampleRate;\n\n    if (pSound == NULL) {\n        return;\n    }\n\n    sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));\n\n    ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000);\n}\n\nMA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return MA_FALSE;\n    }\n\n    return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started;\n}\n\nMA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return 0;\n    }\n\n    return ma_node_get_time(pSound);\n}\n\nMA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound)\n{\n    return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound));\n}\n\nMA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping)\n{\n    if (pSound == NULL) {\n        return;\n    }\n\n    /* Looping is only a valid concept if the sound is backed by a data source. */\n    if (pSound->pDataSource == NULL) {\n        return;\n    }\n\n    /* The looping state needs to be applied to the data source in order for any looping to actually happen. */\n    ma_data_source_set_looping(pSound->pDataSource, isLooping);\n}\n\nMA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return MA_FALSE;\n    }\n\n    /* There is no notion of looping for sounds that are not backed by a data source. */\n    if (pSound->pDataSource == NULL) {\n        return MA_FALSE;\n    }\n\n    return ma_data_source_is_looping(pSound->pDataSource);\n}\n\nMA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound)\n{\n    if (pSound == NULL) {\n        return MA_FALSE;\n    }\n\n    /* There is no notion of an end of a sound if it's not backed by a data source. */\n    if (pSound->pDataSource == NULL) {\n        return MA_FALSE;\n    }\n\n    return ma_sound_get_at_end(pSound);\n}\n\nMA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex)\n{\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* Seeking is only valid for sounds that are backed by a data source. */\n    if (pSound->pDataSource == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */\n    ma_atomic_exchange_64(&pSound->seekTarget, frameIndex);\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)\n{\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */\n    if (pSound->pDataSource == NULL) {\n        ma_uint32 channels;\n\n        if (pFormat != NULL) {\n            *pFormat = ma_format_f32;\n        }\n\n        channels = ma_node_get_input_channels(&pSound->engineNode, 0);\n        if (pChannels != NULL) {\n            *pChannels = channels;\n        }\n\n        if (pSampleRate != NULL) {\n            *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn;\n        }\n\n        if (pChannelMap != NULL) {\n            ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels);\n        }\n\n        return MA_SUCCESS;\n    } else {\n        return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);\n    }\n}\n\nMA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor)\n{\n    ma_uint64 seekTarget;\n\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The notion of a cursor is only valid for sounds that are backed by a data source. */\n    if (pSound->pDataSource == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    seekTarget = ma_atomic_load_64(&pSound->seekTarget);\n    if (seekTarget != MA_SEEK_TARGET_NONE) {\n        *pCursor = seekTarget;\n        return MA_SUCCESS;\n    } else {\n        return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor);\n    }\n}\n\nMA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength)\n{\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The notion of a sound length is only valid for sounds that are backed by a data source. */\n    if (pSound->pDataSource == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength);\n}\n\nMA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor)\n{\n    ma_result result;\n    ma_uint64 cursorInPCMFrames;\n    ma_uint32 sampleRate;\n\n    if (pCursor != NULL) {\n        *pCursor = 0;\n    }\n\n    result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0);\n    if (result != MA_SUCCESS) {\n        return result;\n    }\n\n    /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */\n    *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate;\n\n    return MA_SUCCESS;\n}\n\nMA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength)\n{\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The notion of a sound length is only valid for sounds that are backed by a data source. */\n    if (pSound->pDataSource == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength);\n}\n\nMA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData)\n{\n    if (pSound == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* The notion of an end is only valid for sounds that are backed by a data source. */\n    if (pSound->pDataSource == NULL) {\n        return MA_INVALID_OPERATION;\n    }\n\n    pSound->endCallback          = callback;\n    pSound->pEndCallbackUserData = pUserData;\n\n    return MA_SUCCESS;\n}\n\n\nMA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup)\n{\n    ma_sound_group_config config = ma_sound_group_config_init_2(pEngine);\n    config.flags              = flags;\n    config.pInitialAttachment = pParentGroup;\n    return ma_sound_group_init_ex(pEngine, &config, pGroup);\n}\n\nMA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup)\n{\n    ma_sound_config soundConfig;\n\n    if (pGroup == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    MA_ZERO_OBJECT(pGroup);\n\n    if (pConfig == NULL) {\n        return MA_INVALID_ARGS;\n    }\n\n    /* A sound group is just a sound without a data source. */\n    soundConfig = *pConfig;\n    soundConfig.pFilePath   = NULL;\n    soundConfig.pFilePathW  = NULL;\n    soundConfig.pDataSource = NULL;\n\n    /*\n    Groups need to have spatialization disabled by default because I think it'll be pretty rare\n    that programs will want to spatialize groups (but not unheard of). Certainly it feels like\n    disabling this by default feels like the right option. Spatialization can be enabled with a\n    call to ma_sound_group_set_spatialization_enabled().\n    */\n    soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION;\n\n    return ma_sound_init_ex(pEngine, &soundConfig, pGroup);\n}\n\nMA_API void ma_sound_group_uninit(ma_sound_group* pGroup)\n{\n    ma_sound_uninit(pGroup);\n}\n\nMA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_engine(pGroup);\n}\n\nMA_API ma_result ma_sound_group_start(ma_sound_group* pGroup)\n{\n    return ma_sound_start(pGroup);\n}\n\nMA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup)\n{\n    return ma_sound_stop(pGroup);\n}\n\nMA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume)\n{\n    ma_sound_set_volume(pGroup, volume);\n}\n\nMA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_volume(pGroup);\n}\n\nMA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan)\n{\n    ma_sound_set_pan(pGroup, pan);\n}\n\nMA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_pan(pGroup);\n}\n\nMA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode)\n{\n    ma_sound_set_pan_mode(pGroup, panMode);\n}\n\nMA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_pan_mode(pGroup);\n}\n\nMA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch)\n{\n    ma_sound_set_pitch(pGroup, pitch);\n}\n\nMA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_pitch(pGroup);\n}\n\nMA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled)\n{\n    ma_sound_set_spatialization_enabled(pGroup, enabled);\n}\n\nMA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup)\n{\n    return ma_sound_is_spatialization_enabled(pGroup);\n}\n\nMA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex)\n{\n    ma_sound_set_pinned_listener_index(pGroup, listenerIndex);\n}\n\nMA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_pinned_listener_index(pGroup);\n}\n\nMA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_listener_index(pGroup);\n}\n\nMA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_direction_to_listener(pGroup);\n}\n\nMA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z)\n{\n    ma_sound_set_position(pGroup, x, y, z);\n}\n\nMA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_position(pGroup);\n}\n\nMA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z)\n{\n    ma_sound_set_direction(pGroup, x, y, z);\n}\n\nMA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_direction(pGroup);\n}\n\nMA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z)\n{\n    ma_sound_set_velocity(pGroup, x, y, z);\n}\n\nMA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_velocity(pGroup);\n}\n\nMA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel)\n{\n    ma_sound_set_attenuation_model(pGroup, attenuationModel);\n}\n\nMA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_attenuation_model(pGroup);\n}\n\nMA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning)\n{\n    ma_sound_set_positioning(pGroup, positioning);\n}\n\nMA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_positioning(pGroup);\n}\n\nMA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff)\n{\n    ma_sound_set_rolloff(pGroup, rolloff);\n}\n\nMA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_rolloff(pGroup);\n}\n\nMA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain)\n{\n    ma_sound_set_min_gain(pGroup, minGain);\n}\n\nMA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_min_gain(pGroup);\n}\n\nMA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain)\n{\n    ma_sound_set_max_gain(pGroup, maxGain);\n}\n\nMA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_max_gain(pGroup);\n}\n\nMA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance)\n{\n    ma_sound_set_min_distance(pGroup, minDistance);\n}\n\nMA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_min_distance(pGroup);\n}\n\nMA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance)\n{\n    ma_sound_set_max_distance(pGroup, maxDistance);\n}\n\nMA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_max_distance(pGroup);\n}\n\nMA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain)\n{\n    ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain);\n}\n\nMA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)\n{\n    ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);\n}\n\nMA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor)\n{\n    ma_sound_set_doppler_factor(pGroup, dopplerFactor);\n}\n\nMA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_doppler_factor(pGroup);\n}\n\nMA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor)\n{\n    ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor);\n}\n\nMA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_directional_attenuation_factor(pGroup);\n}\n\nMA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)\n{\n    ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames);\n}\n\nMA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)\n{\n    ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds);\n}\n\nMA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup)\n{\n    return ma_sound_get_current_fade_volume(pGroup);\n}\n\nMA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)\n{\n    ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);\n}\n\nMA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)\n{\n    ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);\n}\n\nMA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)\n{\n    ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);\n}\n\nMA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)\n{\n    ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);\n}\n\nMA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup)\n{\n    return ma_sound_is_playing(pGroup);\n}\n\nMA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup)\n{\n    return ma_sound_get_time_in_pcm_frames(pGroup);\n}\n#endif  /* MA_NO_ENGINE */\n/* END SECTION: miniaudio_engine.c */\n\n\n\n/**************************************************************************************************************************************************************\n***************************************************************************************************************************************************************\n\nAuto Generated\n==============\nAll code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the\ncode below please report the bug to the respective repository for the relevant project (probably dr_libs).\n\n***************************************************************************************************************************************************************\n**************************************************************************************************************************************************************/\n#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))\n#if !defined(MA_DR_WAV_IMPLEMENTATION) && !defined(MA_DR_WAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */\n/* dr_wav_c begin */\n#ifndef ma_dr_wav_c\n#define ma_dr_wav_c\n#ifdef __MRC__\n#pragma options opt off\n#endif\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n#ifndef MA_DR_WAV_NO_STDIO\n#include <stdio.h>\n#ifndef MA_DR_WAV_NO_WCHAR\n#include <wchar.h>\n#endif\n#endif\n#ifndef MA_DR_WAV_ASSERT\n#include <assert.h>\n#define MA_DR_WAV_ASSERT(expression)           assert(expression)\n#endif\n#ifndef MA_DR_WAV_MALLOC\n#define MA_DR_WAV_MALLOC(sz)                   malloc((sz))\n#endif\n#ifndef MA_DR_WAV_REALLOC\n#define MA_DR_WAV_REALLOC(p, sz)               realloc((p), (sz))\n#endif\n#ifndef MA_DR_WAV_FREE\n#define MA_DR_WAV_FREE(p)                      free((p))\n#endif\n#ifndef MA_DR_WAV_COPY_MEMORY\n#define MA_DR_WAV_COPY_MEMORY(dst, src, sz)    memcpy((dst), (src), (sz))\n#endif\n#ifndef MA_DR_WAV_ZERO_MEMORY\n#define MA_DR_WAV_ZERO_MEMORY(p, sz)           memset((p), 0, (sz))\n#endif\n#ifndef MA_DR_WAV_ZERO_OBJECT\n#define MA_DR_WAV_ZERO_OBJECT(p)               MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p))\n#endif\n#define ma_dr_wav_countof(x)                   (sizeof(x) / sizeof(x[0]))\n#define ma_dr_wav_align(x, a)                  ((((x) + (a) - 1) / (a)) * (a))\n#define ma_dr_wav_min(a, b)                    (((a) < (b)) ? (a) : (b))\n#define ma_dr_wav_max(a, b)                    (((a) > (b)) ? (a) : (b))\n#define ma_dr_wav_clamp(x, lo, hi)             (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x))))\n#define ma_dr_wav_offset_ptr(p, offset)        (((ma_uint8*)(p)) + (offset))\n#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE         32\n#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32))\n#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF))\n#if defined(_MSC_VER) && _MSC_VER >= 1400\n    #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC\n    #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC\n    #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC\n#elif defined(__clang__)\n    #if defined(__has_builtin)\n        #if __has_builtin(__builtin_bswap16)\n            #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC\n        #endif\n        #if __has_builtin(__builtin_bswap32)\n            #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC\n        #endif\n        #if __has_builtin(__builtin_bswap64)\n            #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC\n        #endif\n    #endif\n#elif defined(__GNUC__)\n    #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))\n        #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC\n        #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC\n    #endif\n    #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))\n        #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC\n    #endif\n#endif\nMA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)\n{\n    if (pMajor) {\n        *pMajor = MA_DR_WAV_VERSION_MAJOR;\n    }\n    if (pMinor) {\n        *pMinor = MA_DR_WAV_VERSION_MINOR;\n    }\n    if (pRevision) {\n        *pRevision = MA_DR_WAV_VERSION_REVISION;\n    }\n}\nMA_API const char* ma_dr_wav_version_string(void)\n{\n    return MA_DR_WAV_VERSION_STRING;\n}\n#ifndef MA_DR_WAV_MAX_SAMPLE_RATE\n#define MA_DR_WAV_MAX_SAMPLE_RATE       384000\n#endif\n#ifndef MA_DR_WAV_MAX_CHANNELS\n#define MA_DR_WAV_MAX_CHANNELS          256\n#endif\n#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE\n#define MA_DR_WAV_MAX_BITS_PER_SAMPLE   64\n#endif\nstatic const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00};\nstatic const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};\nstatic const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};\nstatic const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};\nstatic const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};\nstatic MA_INLINE int ma_dr_wav__is_little_endian(void)\n{\n#if defined(MA_X86) || defined(MA_X64)\n    return MA_TRUE;\n#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN\n    return MA_TRUE;\n#else\n    int n = 1;\n    return (*(char*)&n) == 1;\n#endif\n}\nstatic MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid)\n{\n    int i;\n    for (i = 0; i < 16; ++i) {\n        guid[i] = data[i];\n    }\n}\nstatic MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n)\n{\n#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC\n    #if defined(_MSC_VER)\n        return _byteswap_ushort(n);\n    #elif defined(__GNUC__) || defined(__clang__)\n        return __builtin_bswap16(n);\n    #else\n        #error \"This compiler does not support the byte swap intrinsic.\"\n    #endif\n#else\n    return ((n & 0xFF00) >> 8) |\n           ((n & 0x00FF) << 8);\n#endif\n}\nstatic MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n)\n{\n#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC\n    #if defined(_MSC_VER)\n        return _byteswap_ulong(n);\n    #elif defined(__GNUC__) || defined(__clang__)\n        #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT)\n            ma_uint32 r;\n            __asm__ __volatile__ (\n            #if defined(MA_64BIT)\n                \"rev %w[out], %w[in]\" : [out]\"=r\"(r) : [in]\"r\"(n)\n            #else\n                \"rev %[out], %[in]\" : [out]\"=r\"(r) : [in]\"r\"(n)\n            #endif\n            );\n            return r;\n        #else\n            return __builtin_bswap32(n);\n        #endif\n    #else\n        #error \"This compiler does not support the byte swap intrinsic.\"\n    #endif\n#else\n    return ((n & 0xFF000000) >> 24) |\n           ((n & 0x00FF0000) >>  8) |\n           ((n & 0x0000FF00) <<  8) |\n           ((n & 0x000000FF) << 24);\n#endif\n}\nstatic MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n)\n{\n#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC\n    #if defined(_MSC_VER)\n        return _byteswap_uint64(n);\n    #elif defined(__GNUC__) || defined(__clang__)\n        return __builtin_bswap64(n);\n    #else\n        #error \"This compiler does not support the byte swap intrinsic.\"\n    #endif\n#else\n    return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) |\n           ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) |\n           ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) |\n           ((n & ((ma_uint64)0x000000FF << 32)) >>  8) |\n           ((n & ((ma_uint64)0xFF000000      )) <<  8) |\n           ((n & ((ma_uint64)0x00FF0000      )) << 24) |\n           ((n & ((ma_uint64)0x0000FF00      )) << 40) |\n           ((n & ((ma_uint64)0x000000FF      )) << 56);\n#endif\n}\nstatic MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n)\n{\n    return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n);\n}\nstatic MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount)\n{\n    ma_uint64 iSample;\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]);\n    }\n}\nstatic MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p)\n{\n    ma_uint8 t;\n    t = p[0];\n    p[0] = p[2];\n    p[2] = t;\n}\nstatic MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount)\n{\n    ma_uint64 iSample;\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        ma_uint8* pSample = pSamples + (iSample*3);\n        ma_dr_wav__bswap_s24(pSample);\n    }\n}\nstatic MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n)\n{\n    return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n);\n}\nstatic MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount)\n{\n    ma_uint64 iSample;\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]);\n    }\n}\nstatic MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n)\n{\n    return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n);\n}\nstatic MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount)\n{\n    ma_uint64 iSample;\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]);\n    }\n}\nstatic MA_INLINE float ma_dr_wav__bswap_f32(float n)\n{\n    union {\n        ma_uint32 i;\n        float f;\n    } x;\n    x.f = n;\n    x.i = ma_dr_wav__bswap32(x.i);\n    return x.f;\n}\nstatic MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount)\n{\n    ma_uint64 iSample;\n    for (iSample = 0; iSample < sampleCount; iSample += 1) {\n        pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]);\n    }\n}\nstatic MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample)\n{\n    switch (bytesPerSample)\n    {\n        case 1:\n        {\n        } break;\n        case 2:\n        {\n            ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount);\n        } break;\n        case 3:\n        {\n            ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount);\n        } break;\n        case 4:\n        {\n            ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount);\n        } break;\n        case 8:\n        {\n            ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount);\n        } break;\n        default:\n        {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n        } break;\n    }\n}\nMA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container)\n{\n    if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) {\n        return MA_TRUE;\n    } else {\n        return MA_FALSE;\n    }\n}\nMA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data)\n{\n    return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8);\n}\nMA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data)\n{\n    return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8);\n}\nMA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container)\n{\n    if (ma_dr_wav_is_container_be(container)) {\n        return ma_dr_wav_bytes_to_u16_be(data);\n    } else {\n        return ma_dr_wav_bytes_to_u16_le(data);\n    }\n}\nMA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data)\n{\n    return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24);\n}\nMA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data)\n{\n    return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24);\n}\nMA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container)\n{\n    if (ma_dr_wav_is_container_be(container)) {\n        return ma_dr_wav_bytes_to_u32_be(data);\n    } else {\n        return ma_dr_wav_bytes_to_u32_le(data);\n    }\n}\nMA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data)\n{\n    ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1];\n    ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] <<  8) | ((ma_uint64)data[5] <<  0);\n    ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] <<  8) | ((ma_uint64)data[9] <<  0);\n    ma_uint64 significand = (hi << 32) | lo;\n    int sign = exponent >> 15;\n    exponent &= 0x7FFF;\n    if (exponent == 0 && significand == 0) {\n        return 0;\n    } else if (exponent == 0x7FFF) {\n        return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX;\n    }\n    exponent -= 16383;\n    if (exponent > 63) {\n        return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX;\n    } else if (exponent < 1) {\n        return 0;\n    }\n    significand >>= (63 - exponent);\n    if (sign) {\n        return -(ma_int64)significand;\n    } else {\n        return  (ma_int64)significand;\n    }\n}\nMA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData)\n{\n    (void)pUserData;\n    return MA_DR_WAV_MALLOC(sz);\n}\nMA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData)\n{\n    (void)pUserData;\n    return MA_DR_WAV_REALLOC(p, sz);\n}\nMA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData)\n{\n    (void)pUserData;\n    MA_DR_WAV_FREE(p);\n}\nMA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks == NULL) {\n        return NULL;\n    }\n    if (pAllocationCallbacks->onMalloc != NULL) {\n        return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);\n    }\n    if (pAllocationCallbacks->onRealloc != NULL) {\n        return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);\n    }\n    return NULL;\n}\nMA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks == NULL) {\n        return NULL;\n    }\n    if (pAllocationCallbacks->onRealloc != NULL) {\n        return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);\n    }\n    if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {\n        void* p2;\n        p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);\n        if (p2 == NULL) {\n            return NULL;\n        }\n        if (p != NULL) {\n            MA_DR_WAV_COPY_MEMORY(p2, p, szOld);\n            pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);\n        }\n        return p2;\n    }\n    return NULL;\n}\nMA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (p == NULL || pAllocationCallbacks == NULL) {\n        return;\n    }\n    if (pAllocationCallbacks->onFree != NULL) {\n        pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);\n    }\n}\nMA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks != NULL) {\n        return *pAllocationCallbacks;\n    } else {\n        ma_allocation_callbacks allocationCallbacks;\n        allocationCallbacks.pUserData = NULL;\n        allocationCallbacks.onMalloc  = ma_dr_wav__malloc_default;\n        allocationCallbacks.onRealloc = ma_dr_wav__realloc_default;\n        allocationCallbacks.onFree    = ma_dr_wav__free_default;\n        return allocationCallbacks;\n    }\n}\nstatic MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag)\n{\n    return\n        formatTag == MA_DR_WAVE_FORMAT_ADPCM ||\n        formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM;\n}\nMA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize)\n{\n    return (unsigned int)(chunkSize % 2);\n}\nMA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize)\n{\n    return (unsigned int)(chunkSize % 8);\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut);\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut);\nMA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount);\nMA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut)\n{\n    if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) {\n        ma_uint8 sizeInBytes[4];\n        if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {\n            return MA_AT_END;\n        }\n        if (onRead(pUserData, sizeInBytes, 4) != 4) {\n            return MA_INVALID_FILE;\n        }\n        pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container);\n        pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);\n        *pRunningBytesReadOut += 8;\n    } else if (container == ma_dr_wav_container_w64) {\n        ma_uint8 sizeInBytes[8];\n        if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {\n            return MA_AT_END;\n        }\n        if (onRead(pUserData, sizeInBytes, 8) != 8) {\n            return MA_INVALID_FILE;\n        }\n        pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24;\n        pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);\n        *pRunningBytesReadOut += 24;\n    } else {\n        return MA_INVALID_FILE;\n    }\n    return MA_SUCCESS;\n}\nMA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData)\n{\n    ma_uint64 bytesRemainingToSeek = offset;\n    while (bytesRemainingToSeek > 0) {\n        if (bytesRemainingToSeek > 0x7FFFFFFF) {\n            if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) {\n                return MA_FALSE;\n            }\n            bytesRemainingToSeek -= 0x7FFFFFFF;\n        } else {\n            if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) {\n                return MA_FALSE;\n            }\n            bytesRemainingToSeek = 0;\n        }\n    }\n    return MA_TRUE;\n}\nMA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData)\n{\n    if (offset <= 0x7FFFFFFF) {\n        return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start);\n    }\n    if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) {\n        return MA_FALSE;\n    }\n    offset -= 0x7FFFFFFF;\n    for (;;) {\n        if (offset <= 0x7FFFFFFF) {\n            return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current);\n        }\n        if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) {\n            return MA_FALSE;\n        }\n        offset -= 0x7FFFFFFF;\n    }\n}\nMA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor)\n{\n    size_t bytesRead;\n    MA_DR_WAV_ASSERT(onRead != NULL);\n    MA_DR_WAV_ASSERT(pCursor != NULL);\n    bytesRead = onRead(pUserData, pBufferOut, bytesToRead);\n    *pCursor += bytesRead;\n    return bytesRead;\n}\n#if 0\nMA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor)\n{\n    MA_DR_WAV_ASSERT(onSeek != NULL);\n    MA_DR_WAV_ASSERT(pCursor != NULL);\n    if (!onSeek(pUserData, offset, origin)) {\n        return MA_FALSE;\n    }\n    if (origin == ma_dr_wav_seek_origin_start) {\n        *pCursor = offset;\n    } else {\n        *pCursor += offset;\n    }\n    return MA_TRUE;\n}\n#endif\n#define MA_DR_WAV_SMPL_BYTES                    36\n#define MA_DR_WAV_SMPL_LOOP_BYTES               24\n#define MA_DR_WAV_INST_BYTES                    7\n#define MA_DR_WAV_ACID_BYTES                    24\n#define MA_DR_WAV_CUE_BYTES                     4\n#define MA_DR_WAV_BEXT_BYTES                    602\n#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES        256\n#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES    32\n#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES     32\n#define MA_DR_WAV_BEXT_RESERVED_BYTES           180\n#define MA_DR_WAV_BEXT_UMID_BYTES               64\n#define MA_DR_WAV_CUE_POINT_BYTES               24\n#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES      4\n#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES      20\n#define MA_DR_WAV_METADATA_ALIGNMENT            8\ntypedef enum\n{\n    ma_dr_wav__metadata_parser_stage_count,\n    ma_dr_wav__metadata_parser_stage_read\n} ma_dr_wav__metadata_parser_stage;\ntypedef struct\n{\n    ma_dr_wav_read_proc onRead;\n    ma_dr_wav_seek_proc onSeek;\n    void *pReadSeekUserData;\n    ma_dr_wav__metadata_parser_stage stage;\n    ma_dr_wav_metadata *pMetadata;\n    ma_uint32 metadataCount;\n    ma_uint8 *pData;\n    ma_uint8 *pDataCursor;\n    ma_uint64 metadataCursor;\n    ma_uint64 extraCapacity;\n} ma_dr_wav__metadata_parser;\nMA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser)\n{\n    ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity;\n    if (cap > MA_SIZE_MAX) {\n        return 0;\n    }\n    return (size_t)cap;\n}\nMA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align)\n{\n    ma_uint8* pResult;\n    if (align) {\n        ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align;\n        if (modulo != 0) {\n            pParser->pDataCursor += align - modulo;\n        }\n    }\n    pResult = pParser->pDataCursor;\n    MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser)));\n    pParser->pDataCursor += size;\n    return pResult;\n}\nMA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align)\n{\n    size_t extra = bytes + (align ? (align - 1) : 0);\n    pParser->extraCapacity += extra;\n}\nMA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) {\n        pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData);\n        pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData);\n        pParser->pDataCursor = pParser->pData;\n        if (pParser->pData == NULL) {\n            return MA_OUT_OF_MEMORY;\n        }\n        pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1);\n        pParser->metadataCursor = 0;\n    }\n    return MA_SUCCESS;\n}\nMA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor)\n{\n    if (pCursor != NULL) {\n        return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor);\n    } else {\n        return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead);\n    }\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata)\n{\n    ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES];\n    ma_uint64 totalBytesRead = 0;\n    size_t bytesJustRead;\n    if (pMetadata == NULL) {\n        return 0;\n    }\n    bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead);\n    MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);\n    MA_DR_WAV_ASSERT(pChunkHeader != NULL);\n    if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) {\n        ma_uint32 iSampleLoop;\n        pMetadata->type                                     = ma_dr_wav_metadata_type_smpl;\n        pMetadata->data.smpl.manufacturerId                 = ma_dr_wav_bytes_to_u32(smplHeaderData + 0);\n        pMetadata->data.smpl.productId                      = ma_dr_wav_bytes_to_u32(smplHeaderData + 4);\n        pMetadata->data.smpl.samplePeriodNanoseconds        = ma_dr_wav_bytes_to_u32(smplHeaderData + 8);\n        pMetadata->data.smpl.midiUnityNote                  = ma_dr_wav_bytes_to_u32(smplHeaderData + 12);\n        pMetadata->data.smpl.midiPitchFraction              = ma_dr_wav_bytes_to_u32(smplHeaderData + 16);\n        pMetadata->data.smpl.smpteFormat                    = ma_dr_wav_bytes_to_u32(smplHeaderData + 20);\n        pMetadata->data.smpl.smpteOffset                    = ma_dr_wav_bytes_to_u32(smplHeaderData + 24);\n        pMetadata->data.smpl.sampleLoopCount                = ma_dr_wav_bytes_to_u32(smplHeaderData + 28);\n        pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32);\n        if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) {\n            pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT);\n            for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {\n                ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES];\n                bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead);\n                if (bytesJustRead == sizeof(smplLoopData)) {\n                    pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId            = ma_dr_wav_bytes_to_u32(smplLoopData + 0);\n                    pMetadata->data.smpl.pLoops[iSampleLoop].type                  = ma_dr_wav_bytes_to_u32(smplLoopData + 4);\n                    pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8);\n                    pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset  = ma_dr_wav_bytes_to_u32(smplLoopData + 12);\n                    pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction        = ma_dr_wav_bytes_to_u32(smplLoopData + 16);\n                    pMetadata->data.smpl.pLoops[iSampleLoop].playCount             = ma_dr_wav_bytes_to_u32(smplLoopData + 20);\n                } else {\n                    break;\n                }\n            }\n            if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {\n                pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1);\n                MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL);\n                ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);\n            }\n        }\n    }\n    return totalBytesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata)\n{\n    ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES];\n    ma_uint64 totalBytesRead = 0;\n    size_t bytesJustRead;\n    if (pMetadata == NULL) {\n        return 0;\n    }\n    bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead);\n    MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);\n    if (bytesJustRead == sizeof(cueHeaderSectionData)) {\n        pMetadata->type                   = ma_dr_wav_metadata_type_cue;\n        pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData);\n        if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) {\n            pMetadata->data.cue.pCuePoints    = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT);\n            MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL);\n            if (pMetadata->data.cue.cuePointCount > 0) {\n                ma_uint32 iCuePoint;\n                for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {\n                    ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES];\n                    bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead);\n                    if (bytesJustRead == sizeof(cuePointData)) {\n                        pMetadata->data.cue.pCuePoints[iCuePoint].id                = ma_dr_wav_bytes_to_u32(cuePointData + 0);\n                        pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4);\n                        pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0]    = cuePointData[8];\n                        pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1]    = cuePointData[9];\n                        pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2]    = cuePointData[10];\n                        pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3]    = cuePointData[11];\n                        pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart        = ma_dr_wav_bytes_to_u32(cuePointData + 12);\n                        pMetadata->data.cue.pCuePoints[iCuePoint].blockStart        = ma_dr_wav_bytes_to_u32(cuePointData + 16);\n                        pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset  = ma_dr_wav_bytes_to_u32(cuePointData + 20);\n                    } else {\n                        break;\n                    }\n                }\n            }\n        }\n    }\n    return totalBytesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata)\n{\n    ma_uint8 instData[MA_DR_WAV_INST_BYTES];\n    ma_uint64 bytesRead;\n    if (pMetadata == NULL) {\n        return 0;\n    }\n    bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL);\n    MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);\n    if (bytesRead == sizeof(instData)) {\n        pMetadata->type                    = ma_dr_wav_metadata_type_inst;\n        pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0];\n        pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1];\n        pMetadata->data.inst.gainDecibels  = (ma_int8)instData[2];\n        pMetadata->data.inst.lowNote       = (ma_int8)instData[3];\n        pMetadata->data.inst.highNote      = (ma_int8)instData[4];\n        pMetadata->data.inst.lowVelocity   = (ma_int8)instData[5];\n        pMetadata->data.inst.highVelocity  = (ma_int8)instData[6];\n    }\n    return bytesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata)\n{\n    ma_uint8 acidData[MA_DR_WAV_ACID_BYTES];\n    ma_uint64 bytesRead;\n    if (pMetadata == NULL) {\n        return 0;\n    }\n    bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL);\n    MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);\n    if (bytesRead == sizeof(acidData)) {\n        pMetadata->type                       = ma_dr_wav_metadata_type_acid;\n        pMetadata->data.acid.flags            = ma_dr_wav_bytes_to_u32(acidData + 0);\n        pMetadata->data.acid.midiUnityNote    = ma_dr_wav_bytes_to_u16(acidData + 4);\n        pMetadata->data.acid.reserved1        = ma_dr_wav_bytes_to_u16(acidData + 6);\n        pMetadata->data.acid.reserved2        = ma_dr_wav_bytes_to_f32(acidData + 8);\n        pMetadata->data.acid.numBeats         = ma_dr_wav_bytes_to_u32(acidData + 12);\n        pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16);\n        pMetadata->data.acid.meterNumerator   = ma_dr_wav_bytes_to_u16(acidData + 18);\n        pMetadata->data.acid.tempo            = ma_dr_wav_bytes_to_f32(acidData + 20);\n    }\n    return bytesRead;\n}\nMA_PRIVATE size_t ma_dr_wav__strlen(const char* str)\n{\n    size_t result = 0;\n    while (*str++) {\n        result += 1;\n    }\n    return result;\n}\nMA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead)\n{\n    size_t result = 0;\n    while (*str++ && result < maxToRead) {\n        result += 1;\n    }\n    return result;\n}\nMA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead)\n{\n    size_t len = ma_dr_wav__strlen_clamped(str, maxToRead);\n    if (len) {\n        char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1);\n        MA_DR_WAV_ASSERT(result != NULL);\n        MA_DR_WAV_COPY_MEMORY(result, str, len);\n        result[len] = '\\0';\n        return result;\n    } else {\n        return NULL;\n    }\n}\ntypedef struct\n{\n    const void* pBuffer;\n    size_t sizeInBytes;\n    size_t cursor;\n} ma_dr_wav_buffer_reader;\nMA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader)\n{\n    MA_DR_WAV_ASSERT(pBuffer != NULL);\n    MA_DR_WAV_ASSERT(pReader != NULL);\n    MA_DR_WAV_ZERO_OBJECT(pReader);\n    pReader->pBuffer     = pBuffer;\n    pReader->sizeInBytes = sizeInBytes;\n    pReader->cursor      = 0;\n    return MA_SUCCESS;\n}\nMA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader)\n{\n    MA_DR_WAV_ASSERT(pReader != NULL);\n    return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor);\n}\nMA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek)\n{\n    MA_DR_WAV_ASSERT(pReader != NULL);\n    if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) {\n        return MA_BAD_SEEK;\n    }\n    pReader->cursor += bytesToSeek;\n    return MA_SUCCESS;\n}\nMA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead)\n{\n    ma_result result = MA_SUCCESS;\n    size_t bytesRemaining;\n    MA_DR_WAV_ASSERT(pReader != NULL);\n    if (pBytesRead != NULL) {\n        *pBytesRead = 0;\n    }\n    bytesRemaining = (pReader->sizeInBytes - pReader->cursor);\n    if (bytesToRead > bytesRemaining) {\n        bytesToRead = bytesRemaining;\n    }\n    if (pDst == NULL) {\n        result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead);\n    } else {\n        MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead);\n        pReader->cursor += bytesToRead;\n    }\n    MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes);\n    if (result == MA_SUCCESS) {\n        if (pBytesRead != NULL) {\n            *pBytesRead = bytesToRead;\n        }\n    }\n    return MA_SUCCESS;\n}\nMA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst)\n{\n    ma_result result;\n    size_t bytesRead;\n    ma_uint8 data[2];\n    MA_DR_WAV_ASSERT(pReader != NULL);\n    MA_DR_WAV_ASSERT(pDst != NULL);\n    *pDst = 0;\n    result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);\n    if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) {\n        return result;\n    }\n    *pDst = ma_dr_wav_bytes_to_u16(data);\n    return MA_SUCCESS;\n}\nMA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst)\n{\n    ma_result result;\n    size_t bytesRead;\n    ma_uint8 data[4];\n    MA_DR_WAV_ASSERT(pReader != NULL);\n    MA_DR_WAV_ASSERT(pDst != NULL);\n    *pDst = 0;\n    result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);\n    if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) {\n        return result;\n    }\n    *pDst = ma_dr_wav_bytes_to_u32(data);\n    return MA_SUCCESS;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize)\n{\n    ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES];\n    size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL);\n    MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);\n    if (bytesRead == sizeof(bextData)) {\n        ma_dr_wav_buffer_reader reader;\n        ma_uint32 timeReferenceLow;\n        ma_uint32 timeReferenceHigh;\n        size_t extraBytes;\n        pMetadata->type = ma_dr_wav_metadata_type_bext;\n        if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) {\n            pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES);\n            ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES);\n            pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES);\n            ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES);\n            pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES);\n            ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES);\n            ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL);\n            ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL);\n            ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow);\n            ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh);\n            pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow;\n            ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version);\n            pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1);\n            ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL);\n            ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue);\n            ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange);\n            ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel);\n            ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness);\n            ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness);\n            MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES));\n            extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES);\n            if (extraBytes > 0) {\n                pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1);\n                MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL);\n                bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL);\n                pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory);\n            } else {\n                pMetadata->data.bext.pCodingHistory    = NULL;\n                pMetadata->data.bext.codingHistorySize = 0;\n            }\n        }\n    }\n    return bytesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type)\n{\n    ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES];\n    ma_uint64 totalBytesRead = 0;\n    size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead);\n    MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);\n    if (bytesJustRead == sizeof(cueIDBuffer)) {\n        ma_uint32 sizeIncludingNullTerminator;\n        pMetadata->type = type;\n        pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer);\n        sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;\n        if (sizeIncludingNullTerminator > 0) {\n            pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1;\n            pMetadata->data.labelOrNote.pString      = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);\n            MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);\n            ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead);\n        } else {\n            pMetadata->data.labelOrNote.stringLength = 0;\n            pMetadata->data.labelOrNote.pString      = NULL;\n        }\n    }\n    return totalBytesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize)\n{\n    ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES];\n    ma_uint64 totalBytesRead = 0;\n    size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead);\n    MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);\n    if (bytesJustRead == sizeof(buffer)) {\n        ma_uint32 sizeIncludingNullTerminator;\n        pMetadata->type                                = ma_dr_wav_metadata_type_list_labelled_cue_region;\n        pMetadata->data.labelledCueRegion.cuePointId   = ma_dr_wav_bytes_to_u32(buffer + 0);\n        pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4);\n        pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8];\n        pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9];\n        pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10];\n        pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11];\n        pMetadata->data.labelledCueRegion.country      = ma_dr_wav_bytes_to_u16(buffer + 12);\n        pMetadata->data.labelledCueRegion.language     = ma_dr_wav_bytes_to_u16(buffer + 14);\n        pMetadata->data.labelledCueRegion.dialect      = ma_dr_wav_bytes_to_u16(buffer + 16);\n        pMetadata->data.labelledCueRegion.codePage     = ma_dr_wav_bytes_to_u16(buffer + 18);\n        sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;\n        if (sizeIncludingNullTerminator > 0) {\n            pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1;\n            pMetadata->data.labelledCueRegion.pString      = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);\n            MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);\n            ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead);\n        } else {\n            pMetadata->data.labelledCueRegion.stringLength = 0;\n            pMetadata->data.labelledCueRegion.pString      = NULL;\n        }\n    }\n    return totalBytesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type)\n{\n    ma_uint64 bytesRead = 0;\n    ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize;\n    if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {\n        pParser->metadataCount += 1;\n        ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1);\n    } else {\n        ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];\n        pMetadata->type = type;\n        if (stringSizeWithNullTerminator > 0) {\n            pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1;\n            pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1);\n            MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL);\n            bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL);\n            if (bytesRead == chunkSize) {\n                pParser->metadataCursor += 1;\n            } else {\n            }\n        } else {\n            pMetadata->data.infoText.stringLength = 0;\n            pMetadata->data.infoText.pString      = NULL;\n            pParser->metadataCursor += 1;\n        }\n    }\n    return bytesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location)\n{\n    ma_uint64 bytesRead = 0;\n    if (location == ma_dr_wav_metadata_location_invalid) {\n        return 0;\n    }\n    if (ma_dr_wav_fourcc_equal(pChunkId, \"data\") || ma_dr_wav_fourcc_equal(pChunkId, \"fmt \") || ma_dr_wav_fourcc_equal(pChunkId, \"fact\")) {\n        return 0;\n    }\n    if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {\n        pParser->metadataCount += 1;\n        ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1);\n    } else {\n        ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];\n        pMetadata->type                         = ma_dr_wav_metadata_type_unknown;\n        pMetadata->data.unknown.chunkLocation   = location;\n        pMetadata->data.unknown.id[0]           = pChunkId[0];\n        pMetadata->data.unknown.id[1]           = pChunkId[1];\n        pMetadata->data.unknown.id[2]           = pChunkId[2];\n        pMetadata->data.unknown.id[3]           = pChunkId[3];\n        pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize;\n        pMetadata->data.unknown.pData           = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1);\n        MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL);\n        bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL);\n        if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) {\n            pParser->metadataCursor += 1;\n        } else {\n        }\n    }\n    return bytesRead;\n}\nMA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID)\n{\n    return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID);\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes)\n{\n    const ma_uint8 *pChunkID = pChunkHeader->id.fourcc;\n    ma_uint64 bytesRead = 0;\n    if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, \"smpl\")) {\n        if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) {\n            if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {\n                ma_uint8 buffer[4];\n                size_t bytesJustRead;\n                if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) {\n                    return bytesRead;\n                }\n                bytesRead += 28;\n                bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);\n                if (bytesJustRead == sizeof(buffer)) {\n                    ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer);\n                    ma_uint64 calculatedLoopCount;\n                    calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES;\n                    if (calculatedLoopCount == loopCount) {\n                        bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);\n                        if (bytesJustRead == sizeof(buffer)) {\n                            ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer);\n                            pParser->metadataCount += 1;\n                            ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT);\n                            ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1);\n                        }\n                    } else {\n                    }\n                }\n            } else {\n                bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);\n                if (bytesRead == pChunkHeader->sizeInBytes) {\n                    pParser->metadataCursor += 1;\n                } else {\n                }\n            }\n        } else {\n        }\n    } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, \"inst\")) {\n        if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) {\n            if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {\n                pParser->metadataCount += 1;\n            } else {\n                bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);\n                if (bytesRead == pChunkHeader->sizeInBytes) {\n                    pParser->metadataCursor += 1;\n                } else {\n                }\n            }\n        } else {\n        }\n    } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, \"acid\")) {\n        if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) {\n            if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {\n                pParser->metadataCount += 1;\n            } else {\n                bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);\n                if (bytesRead == pChunkHeader->sizeInBytes) {\n                    pParser->metadataCursor += 1;\n                } else {\n                }\n            }\n        } else {\n        }\n    } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, \"cue \")) {\n        if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) {\n            if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {\n                size_t cueCount;\n                pParser->metadataCount += 1;\n                cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES;\n                ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT);\n            } else {\n                bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);\n                if (bytesRead == pChunkHeader->sizeInBytes) {\n                    pParser->metadataCursor += 1;\n                } else {\n                }\n            }\n        } else {\n        }\n    } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, \"bext\")) {\n        if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) {\n            if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {\n                char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1];\n                size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES;\n                size_t bytesJustRead;\n                buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\\0';\n                bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead);\n                if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) {\n                    return bytesRead;\n                }\n                allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;\n                buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\\0';\n                bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead);\n                if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) {\n                    return bytesRead;\n                }\n                allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;\n                buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\\0';\n                bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead);\n                if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) {\n                    return bytesRead;\n                }\n                allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;\n                allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES;\n                ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);\n                pParser->metadataCount += 1;\n            } else {\n                bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes);\n                if (bytesRead == pChunkHeader->sizeInBytes) {\n                    pParser->metadataCursor += 1;\n                } else {\n                }\n            }\n        } else {\n        }\n    } else if (ma_dr_wav_fourcc_equal(pChunkID, \"LIST\") || ma_dr_wav_fourcc_equal(pChunkID, \"list\")) {\n        ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid;\n        while (bytesRead < pChunkHeader->sizeInBytes) {\n            ma_uint8 subchunkId[4];\n            ma_uint8 subchunkSizeBuffer[4];\n            ma_uint64 subchunkDataSize;\n            ma_uint64 subchunkBytesRead = 0;\n            ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead);\n            if (bytesJustRead != sizeof(subchunkId)) {\n                break;\n            }\n            if (ma_dr_wav_fourcc_equal(subchunkId, \"adtl\")) {\n                listType = ma_dr_wav_metadata_location_inside_adtl_list;\n                continue;\n            } else if (ma_dr_wav_fourcc_equal(subchunkId, \"INFO\")) {\n                listType = ma_dr_wav_metadata_location_inside_info_list;\n                continue;\n            }\n            bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead);\n            if (bytesJustRead != sizeof(subchunkSizeBuffer)) {\n                break;\n            }\n            subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer);\n            if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, \"labl\") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, \"note\")) {\n                if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) {\n                    ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;\n                    if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {\n                        pParser->metadataCount += 1;\n                        ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1);\n                    } else {\n                        subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, \"labl\") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note);\n                        if (subchunkBytesRead == subchunkDataSize) {\n                            pParser->metadataCursor += 1;\n                        } else {\n                        }\n                    }\n                } else {\n                }\n            } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, \"ltxt\")) {\n                if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) {\n                    ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;\n                    if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {\n                        pParser->metadataCount += 1;\n                        ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1);\n                    } else {\n                        subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize);\n                        if (subchunkBytesRead == subchunkDataSize) {\n                            pParser->metadataCursor += 1;\n                        } else {\n                        }\n                    }\n                } else {\n                }\n            } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, \"ISFT\")) {\n                subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  ma_dr_wav_metadata_type_list_info_software);\n            } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, \"ICOP\")) {\n                subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  ma_dr_wav_metadata_type_list_info_copyright);\n            } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, \"INAM\")) {\n                subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  ma_dr_wav_metadata_type_list_info_title);\n            } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, \"IART\")) {\n                subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  ma_dr_wav_metadata_type_list_info_artist);\n            } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, \"ICMT\")) {\n                subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  ma_dr_wav_metadata_type_list_info_comment);\n            } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, \"ICRD\")) {\n                subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  ma_dr_wav_metadata_type_list_info_date);\n            } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, \"IGNR\")) {\n                subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  ma_dr_wav_metadata_type_list_info_genre);\n            } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, \"IPRD\")) {\n                subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  ma_dr_wav_metadata_type_list_info_album);\n            } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, \"ITRK\")) {\n                subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  ma_dr_wav_metadata_type_list_info_tracknumber);\n            } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) {\n                subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType);\n            }\n            bytesRead += subchunkBytesRead;\n            MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize);\n            if (subchunkBytesRead < subchunkDataSize) {\n                ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead;\n                if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) {\n                    break;\n                }\n                bytesRead += bytesToSeek;\n            }\n            if ((subchunkDataSize % 2) == 1) {\n                if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) {\n                    break;\n                }\n                bytesRead += 1;\n            }\n        }\n    } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) {\n        bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level);\n    }\n    return bytesRead;\n}\nMA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav)\n{\n    ma_uint32 bytesPerFrame;\n    if ((pWav->bitsPerSample & 0x7) == 0) {\n        bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3;\n    } else {\n        bytesPerFrame = pWav->fmt.blockAlign;\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {\n        if (bytesPerFrame != pWav->fmt.channels) {\n            return 0;\n        }\n    }\n    return bytesPerFrame;\n}\nMA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT)\n{\n    if (pFMT == NULL) {\n        return 0;\n    }\n    if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) {\n        return pFMT->formatTag;\n    } else {\n        return ma_dr_wav_bytes_to_u16(pFMT->subFormat);\n    }\n}\nMA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pWav == NULL || onRead == NULL || onSeek == NULL) {\n        return MA_FALSE;\n    }\n    MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav));\n    pWav->onRead    = onRead;\n    pWav->onSeek    = onSeek;\n    pWav->pUserData = pReadSeekUserData;\n    pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);\n    if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {\n        return MA_FALSE;\n    }\n    return MA_TRUE;\n}\nMA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags)\n{\n    ma_result result;\n    ma_uint64 cursor;\n    ma_bool32 sequential;\n    ma_uint8 riff[4];\n    ma_dr_wav_fmt fmt;\n    unsigned short translatedFormatTag;\n    ma_uint64 dataChunkSize = 0;\n    ma_uint64 sampleCountFromFactChunk = 0;\n    ma_uint64 metadataStartPos;\n    ma_dr_wav__metadata_parser metadataParser;\n    ma_bool8 isProcessingMetadata = MA_FALSE;\n    ma_bool8 foundChunk_fmt  = MA_FALSE;\n    ma_bool8 foundChunk_data = MA_FALSE;\n    ma_bool8 isAIFCFormType = MA_FALSE;\n    ma_uint64 aiffFrameCount = 0;\n    cursor = 0;\n    sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0;\n    MA_DR_WAV_ZERO_OBJECT(&fmt);\n    if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {\n        return MA_FALSE;\n    }\n    if (ma_dr_wav_fourcc_equal(riff, \"RIFF\")) {\n        pWav->container = ma_dr_wav_container_riff;\n    } else if (ma_dr_wav_fourcc_equal(riff, \"RIFX\")) {\n        pWav->container = ma_dr_wav_container_rifx;\n    } else if (ma_dr_wav_fourcc_equal(riff, \"riff\")) {\n        int i;\n        ma_uint8 riff2[12];\n        pWav->container = ma_dr_wav_container_w64;\n        if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {\n            return MA_FALSE;\n        }\n        for (i = 0; i < 12; ++i) {\n            if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) {\n                return MA_FALSE;\n            }\n        }\n    } else if (ma_dr_wav_fourcc_equal(riff, \"RF64\")) {\n        pWav->container = ma_dr_wav_container_rf64;\n    } else if (ma_dr_wav_fourcc_equal(riff, \"FORM\")) {\n        pWav->container = ma_dr_wav_container_aiff;\n    } else {\n        return MA_FALSE;\n    }\n    if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) {\n        ma_uint8 chunkSizeBytes[4];\n        ma_uint8 wave[4];\n        if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {\n            return MA_FALSE;\n        }\n        if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) {\n            if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) {\n                return MA_FALSE;\n            }\n        } else if (pWav->container == ma_dr_wav_container_rf64) {\n            if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) {\n                return MA_FALSE;\n            }\n        } else {\n            return MA_FALSE;\n        }\n        if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {\n            return MA_FALSE;\n        }\n        if (!ma_dr_wav_fourcc_equal(wave, \"WAVE\")) {\n            return MA_FALSE;\n        }\n    } else if (pWav->container == ma_dr_wav_container_w64) {\n        ma_uint8 chunkSizeBytes[8];\n        ma_uint8 wave[16];\n        if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {\n            return MA_FALSE;\n        }\n        if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) {\n            return MA_FALSE;\n        }\n        if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {\n            return MA_FALSE;\n        }\n        if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) {\n            return MA_FALSE;\n        }\n    } else if (pWav->container == ma_dr_wav_container_aiff) {\n        ma_uint8 chunkSizeBytes[4];\n        ma_uint8 aiff[4];\n        if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {\n            return MA_FALSE;\n        }\n        if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) {\n            return MA_FALSE;\n        }\n        if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) {\n            return MA_FALSE;\n        }\n        if (ma_dr_wav_fourcc_equal(aiff, \"AIFF\")) {\n            isAIFCFormType = MA_FALSE;\n        } else if (ma_dr_wav_fourcc_equal(aiff, \"AIFC\")) {\n            isAIFCFormType = MA_TRUE;\n        } else {\n            return MA_FALSE;\n        }\n    } else {\n        return MA_FALSE;\n    }\n    if (pWav->container == ma_dr_wav_container_rf64) {\n        ma_uint8 sizeBytes[8];\n        ma_uint64 bytesRemainingInChunk;\n        ma_dr_wav_chunk_header header;\n        result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);\n        if (result != MA_SUCCESS) {\n            return MA_FALSE;\n        }\n        if (!ma_dr_wav_fourcc_equal(header.id.fourcc, \"ds64\")) {\n            return MA_FALSE;\n        }\n        bytesRemainingInChunk = header.sizeInBytes + header.paddingSize;\n        if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) {\n            return MA_FALSE;\n        }\n        bytesRemainingInChunk -= 8;\n        cursor += 8;\n        if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {\n            return MA_FALSE;\n        }\n        bytesRemainingInChunk -= 8;\n        dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes);\n        if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {\n            return MA_FALSE;\n        }\n        bytesRemainingInChunk -= 8;\n        sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes);\n        if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) {\n            return MA_FALSE;\n        }\n        cursor += bytesRemainingInChunk;\n    }\n    metadataStartPos = cursor;\n    isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0);\n    if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) {\n        isProcessingMetadata = MA_FALSE;\n    }\n    MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser));\n    if (isProcessingMetadata) {\n        metadataParser.onRead = pWav->onRead;\n        metadataParser.onSeek = pWav->onSeek;\n        metadataParser.pReadSeekUserData = pWav->pUserData;\n        metadataParser.stage  = ma_dr_wav__metadata_parser_stage_count;\n    }\n    for (;;) {\n        ma_dr_wav_chunk_header header;\n        ma_uint64 chunkSize;\n        result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);\n        if (result != MA_SUCCESS) {\n            break;\n        }\n        chunkSize = header.sizeInBytes;\n        if (!sequential && onChunk != NULL) {\n            ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt);\n            if (callbackBytesRead > 0) {\n                if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) {\n                    return MA_FALSE;\n                }\n            }\n        }\n        if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, \"fmt \")) ||\n            ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) {\n            ma_uint8 fmtData[16];\n            foundChunk_fmt = MA_TRUE;\n            if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) {\n                return MA_FALSE;\n            }\n            cursor += sizeof(fmtData);\n            fmt.formatTag      = ma_dr_wav_bytes_to_u16_ex(fmtData + 0,  pWav->container);\n            fmt.channels       = ma_dr_wav_bytes_to_u16_ex(fmtData + 2,  pWav->container);\n            fmt.sampleRate     = ma_dr_wav_bytes_to_u32_ex(fmtData + 4,  pWav->container);\n            fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8,  pWav->container);\n            fmt.blockAlign     = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container);\n            fmt.bitsPerSample  = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container);\n            fmt.extendedSize       = 0;\n            fmt.validBitsPerSample = 0;\n            fmt.channelMask        = 0;\n            MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat));\n            if (header.sizeInBytes > 16) {\n                ma_uint8 fmt_cbSize[2];\n                int bytesReadSoFar = 0;\n                if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {\n                    return MA_FALSE;\n                }\n                cursor += sizeof(fmt_cbSize);\n                bytesReadSoFar = 18;\n                fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container);\n                if (fmt.extendedSize > 0) {\n                    if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {\n                        if (fmt.extendedSize != 22) {\n                            return MA_FALSE;\n                        }\n                    }\n                    if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {\n                        ma_uint8 fmtext[22];\n                        if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) {\n                            return MA_FALSE;\n                        }\n                        fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container);\n                        fmt.channelMask        = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container);\n                        ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat);\n                    } else {\n                        if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) {\n                            return MA_FALSE;\n                        }\n                    }\n                    cursor += fmt.extendedSize;\n                    bytesReadSoFar += fmt.extendedSize;\n                }\n                if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) {\n                    return MA_FALSE;\n                }\n                cursor += (header.sizeInBytes - bytesReadSoFar);\n            }\n            if (header.paddingSize > 0) {\n                if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) {\n                    break;\n                }\n                cursor += header.paddingSize;\n            }\n            continue;\n        }\n        if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, \"data\")) ||\n            ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) {\n            foundChunk_data = MA_TRUE;\n            pWav->dataChunkDataPos  = cursor;\n            if (pWav->container != ma_dr_wav_container_rf64) {\n                dataChunkSize = chunkSize;\n            }\n            if (sequential || !isProcessingMetadata) {\n                break;\n            } else {\n                chunkSize += header.paddingSize;\n                if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {\n                    break;\n                }\n                cursor += chunkSize;\n                continue;\n            }\n        }\n        if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, \"fact\")) ||\n            ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) {\n            if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) {\n                ma_uint8 sampleCount[4];\n                if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {\n                    return MA_FALSE;\n                }\n                chunkSize -= 4;\n                if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {\n                    sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container);\n                } else {\n                    sampleCountFromFactChunk = 0;\n                }\n            } else if (pWav->container == ma_dr_wav_container_w64) {\n                if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {\n                    return MA_FALSE;\n                }\n                chunkSize -= 8;\n            } else if (pWav->container == ma_dr_wav_container_rf64) {\n            }\n            chunkSize += header.paddingSize;\n            if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {\n                break;\n            }\n            cursor += chunkSize;\n            continue;\n        }\n        if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, \"COMM\")) {\n            ma_uint8 commData[24];\n            ma_uint32 commDataBytesToRead;\n            ma_uint16 channels;\n            ma_uint32 frameCount;\n            ma_uint16 sampleSizeInBits;\n            ma_int64  sampleRate;\n            ma_uint16 compressionFormat;\n            foundChunk_fmt = MA_TRUE;\n            if (isAIFCFormType) {\n                commDataBytesToRead = 24;\n                if (header.sizeInBytes < commDataBytesToRead) {\n                    return MA_FALSE;\n                }\n            } else {\n                commDataBytesToRead = 18;\n                if (header.sizeInBytes != commDataBytesToRead) {\n                    return MA_FALSE;\n                }\n            }\n            if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) {\n                return MA_FALSE;\n            }\n            channels         = ma_dr_wav_bytes_to_u16_ex     (commData + 0, pWav->container);\n            frameCount       = ma_dr_wav_bytes_to_u32_ex     (commData + 2, pWav->container);\n            sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex     (commData + 6, pWav->container);\n            sampleRate       = ma_dr_wav_aiff_extented_to_s64(commData + 8);\n            if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) {\n                return MA_FALSE;\n            }\n            if (isAIFCFormType) {\n                const ma_uint8* type = commData + 18;\n                if (ma_dr_wav_fourcc_equal(type, \"NONE\")) {\n                    compressionFormat = MA_DR_WAVE_FORMAT_PCM;\n                } else if (ma_dr_wav_fourcc_equal(type, \"raw \")) {\n                    compressionFormat = MA_DR_WAVE_FORMAT_PCM;\n                    if (sampleSizeInBits == 8) {\n                        pWav->aiff.isUnsigned = MA_TRUE;\n                    }\n                } else if (ma_dr_wav_fourcc_equal(type, \"sowt\")) {\n                    compressionFormat = MA_DR_WAVE_FORMAT_PCM;\n                    pWav->aiff.isLE = MA_TRUE;\n                } else if (ma_dr_wav_fourcc_equal(type, \"fl32\") || ma_dr_wav_fourcc_equal(type, \"fl64\") || ma_dr_wav_fourcc_equal(type, \"FL32\") || ma_dr_wav_fourcc_equal(type, \"FL64\")) {\n                    compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT;\n                } else if (ma_dr_wav_fourcc_equal(type, \"alaw\") || ma_dr_wav_fourcc_equal(type, \"ALAW\")) {\n                    compressionFormat = MA_DR_WAVE_FORMAT_ALAW;\n                } else if (ma_dr_wav_fourcc_equal(type, \"ulaw\") || ma_dr_wav_fourcc_equal(type, \"ULAW\")) {\n                    compressionFormat = MA_DR_WAVE_FORMAT_MULAW;\n                } else if (ma_dr_wav_fourcc_equal(type, \"ima4\")) {\n                    compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM;\n                    sampleSizeInBits = 4;\n                    return MA_FALSE;\n                } else {\n                    return MA_FALSE;\n                }\n            } else {\n                compressionFormat = MA_DR_WAVE_FORMAT_PCM;\n            }\n            aiffFrameCount = frameCount;\n            fmt.formatTag      = compressionFormat;\n            fmt.channels       = channels;\n            fmt.sampleRate     = (ma_uint32)sampleRate;\n            fmt.bitsPerSample  = sampleSizeInBits;\n            fmt.blockAlign     = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8);\n            fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate;\n            if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) {\n                fmt.blockAlign = 34 * fmt.channels;\n            }\n            if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) {\n                if (fmt.bitsPerSample > 8) {\n                    fmt.bitsPerSample = 8;\n                    fmt.blockAlign = fmt.channels;\n                }\n            }\n            fmt.bitsPerSample += (fmt.bitsPerSample & 7);\n            if (isAIFCFormType) {\n                if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) {\n                    return MA_FALSE;\n                }\n                cursor += (chunkSize - commDataBytesToRead);\n            }\n            continue;\n        }\n        if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, \"SSND\")) {\n            ma_uint8 offsetAndBlockSizeData[8];\n            ma_uint32 offset;\n            foundChunk_data = MA_TRUE;\n            if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) {\n                return MA_FALSE;\n            }\n            offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container);\n            if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) {\n                return MA_FALSE;\n            }\n            cursor += offset;\n            pWav->dataChunkDataPos = cursor;\n            dataChunkSize = chunkSize;\n            if (sequential || !isProcessingMetadata) {\n                break;\n            } else {\n                if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {\n                    break;\n                }\n                cursor += chunkSize;\n                continue;\n            }\n        }\n        if (isProcessingMetadata) {\n            ma_uint64 metadataBytesRead;\n            metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown);\n            MA_DR_WAV_ASSERT(metadataBytesRead <= header.sizeInBytes);\n            if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) {\n                break;\n            }\n        }\n        chunkSize += header.paddingSize;\n        if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {\n            break;\n        }\n        cursor += chunkSize;\n    }\n    if (!foundChunk_fmt || !foundChunk_data) {\n        return MA_FALSE;\n    }\n    if ((fmt.sampleRate    == 0 || fmt.sampleRate    > MA_DR_WAV_MAX_SAMPLE_RATE    ) ||\n        (fmt.channels      == 0 || fmt.channels      > MA_DR_WAV_MAX_CHANNELS       ) ||\n        (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) ||\n        fmt.blockAlign == 0) {\n        return MA_FALSE;\n    }\n    translatedFormatTag = fmt.formatTag;\n    if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {\n        translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container);\n    }\n    if (!sequential) {\n        if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {\n            return MA_FALSE;\n        }\n        cursor = pWav->dataChunkDataPos;\n    }\n    if (isProcessingMetadata && metadataParser.metadataCount > 0) {\n        if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) {\n            return MA_FALSE;\n        }\n        result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks);\n        if (result != MA_SUCCESS) {\n            return MA_FALSE;\n        }\n        metadataParser.stage = ma_dr_wav__metadata_parser_stage_read;\n        for (;;) {\n            ma_dr_wav_chunk_header header;\n            ma_uint64 metadataBytesRead;\n            result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);\n            if (result != MA_SUCCESS) {\n                break;\n            }\n            metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown);\n            if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) {\n                ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks);\n                return MA_FALSE;\n            }\n        }\n        pWav->pMetadata     = metadataParser.pMetadata;\n        pWav->metadataCount = metadataParser.metadataCount;\n    }\n    if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) {\n        dataChunkSize = 0;\n        for (;;) {\n            ma_uint8 temp[4096];\n            size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp));\n            dataChunkSize += bytesRead;\n            if (bytesRead < sizeof(temp)) {\n                break;\n            }\n        }\n    }\n    if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) {\n        ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);\n        return MA_FALSE;\n    }\n    pWav->fmt                 = fmt;\n    pWav->sampleRate          = fmt.sampleRate;\n    pWav->channels            = fmt.channels;\n    pWav->bitsPerSample       = fmt.bitsPerSample;\n    pWav->bytesRemaining      = dataChunkSize;\n    pWav->translatedFormatTag = translatedFormatTag;\n    pWav->dataChunkDataSize   = dataChunkSize;\n    if (sampleCountFromFactChunk != 0) {\n        pWav->totalPCMFrameCount = sampleCountFromFactChunk;\n    } else if (aiffFrameCount != 0) {\n        pWav->totalPCMFrameCount = aiffFrameCount;\n    } else {\n        ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n        if (bytesPerFrame == 0) {\n            ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);\n            return MA_FALSE;\n        }\n        pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame;\n        if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {\n            ma_uint64 totalBlockHeaderSizeInBytes;\n            ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;\n            if ((blockCount * fmt.blockAlign) < dataChunkSize) {\n                blockCount += 1;\n            }\n            totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);\n            pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;\n        }\n        if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {\n            ma_uint64 totalBlockHeaderSizeInBytes;\n            ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;\n            if ((blockCount * fmt.blockAlign) < dataChunkSize) {\n                blockCount += 1;\n            }\n            totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);\n            pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;\n            pWav->totalPCMFrameCount += blockCount;\n        }\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {\n        if (pWav->channels > 2) {\n            ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);\n            return MA_FALSE;\n        }\n    }\n    if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) {\n        ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);\n        return MA_FALSE;\n    }\n#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {\n        ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;\n        pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels;\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {\n        ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;\n        pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;\n    }\n#endif\n    return MA_TRUE;\n}\nMA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks);\n}\nMA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);\n}\nMA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA);\n}\nMA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav)\n{\n    ma_dr_wav_metadata *result = pWav->pMetadata;\n    pWav->pMetadata     = NULL;\n    pWav->metadataCount = 0;\n    return result;\n}\nMA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize)\n{\n    MA_DR_WAV_ASSERT(pWav          != NULL);\n    MA_DR_WAV_ASSERT(pWav->onWrite != NULL);\n    return pWav->onWrite(pWav->pUserData, pData, dataSize);\n}\nMA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte)\n{\n    MA_DR_WAV_ASSERT(pWav          != NULL);\n    MA_DR_WAV_ASSERT(pWav->onWrite != NULL);\n    return pWav->onWrite(pWav->pUserData, &byte, 1);\n}\nMA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value)\n{\n    MA_DR_WAV_ASSERT(pWav          != NULL);\n    MA_DR_WAV_ASSERT(pWav->onWrite != NULL);\n    if (!ma_dr_wav__is_little_endian()) {\n        value = ma_dr_wav__bswap16(value);\n    }\n    return ma_dr_wav__write(pWav, &value, 2);\n}\nMA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value)\n{\n    MA_DR_WAV_ASSERT(pWav          != NULL);\n    MA_DR_WAV_ASSERT(pWav->onWrite != NULL);\n    if (!ma_dr_wav__is_little_endian()) {\n        value = ma_dr_wav__bswap32(value);\n    }\n    return ma_dr_wav__write(pWav, &value, 4);\n}\nMA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value)\n{\n    MA_DR_WAV_ASSERT(pWav          != NULL);\n    MA_DR_WAV_ASSERT(pWav->onWrite != NULL);\n    if (!ma_dr_wav__is_little_endian()) {\n        value = ma_dr_wav__bswap64(value);\n    }\n    return ma_dr_wav__write(pWav, &value, 8);\n}\nMA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value)\n{\n    union {\n       ma_uint32 u32;\n       float f32;\n    } u;\n    MA_DR_WAV_ASSERT(pWav          != NULL);\n    MA_DR_WAV_ASSERT(pWav->onWrite != NULL);\n    u.f32 = value;\n    if (!ma_dr_wav__is_little_endian()) {\n        u.u32 = ma_dr_wav__bswap32(u.u32);\n    }\n    return ma_dr_wav__write(pWav, &u.u32, 4);\n}\nMA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize)\n{\n    if (pWav == NULL) {\n        return dataSize;\n    }\n    return ma_dr_wav__write(pWav, pData, dataSize);\n}\nMA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte)\n{\n    if (pWav == NULL) {\n        return 1;\n    }\n    return ma_dr_wav__write_byte(pWav, byte);\n}\nMA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value)\n{\n    if (pWav == NULL) {\n        return 2;\n    }\n    return ma_dr_wav__write_u16ne_to_le(pWav, value);\n}\nMA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value)\n{\n    if (pWav == NULL) {\n        return 4;\n    }\n    return ma_dr_wav__write_u32ne_to_le(pWav, value);\n}\n#if 0\nMA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value)\n{\n    if (pWav == NULL) {\n        return 8;\n    }\n    return ma_dr_wav__write_u64ne_to_le(pWav, value);\n}\n#endif\nMA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value)\n{\n    if (pWav == NULL) {\n        return 4;\n    }\n    return ma_dr_wav__write_f32ne_to_le(pWav, value);\n}\nMA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize)\n{\n    size_t len;\n    if (pWav == NULL) {\n        return bufFixedSize;\n    }\n    len = ma_dr_wav__strlen_clamped(str, bufFixedSize);\n    ma_dr_wav__write_or_count(pWav, str, len);\n    if (len < bufFixedSize) {\n        size_t i;\n        for (i = 0; i < bufFixedSize - len; ++i) {\n            ma_dr_wav__write_byte(pWav, 0);\n        }\n    }\n    return bufFixedSize;\n}\nMA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount)\n{\n    size_t bytesWritten = 0;\n    ma_bool32 hasListAdtl = MA_FALSE;\n    ma_bool32 hasListInfo = MA_FALSE;\n    ma_uint32 iMetadata;\n    if (pMetadatas == NULL || metadataCount == 0) {\n        return 0;\n    }\n    for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {\n        ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];\n        ma_uint32 chunkSize = 0;\n        if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) {\n            hasListInfo = MA_TRUE;\n        }\n        if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) {\n            hasListAdtl = MA_TRUE;\n        }\n        switch (pMetadata->type) {\n            case ma_dr_wav_metadata_type_smpl:\n            {\n                ma_uint32 iLoop;\n                chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes;\n                bytesWritten += ma_dr_wav__write_or_count(pWav, \"smpl\", 4);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);\n                for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) {\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount);\n                }\n                if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {\n                    bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);\n                }\n            } break;\n            case ma_dr_wav_metadata_type_inst:\n            {\n                chunkSize = MA_DR_WAV_INST_BYTES;\n                bytesWritten += ma_dr_wav__write_or_count(pWav, \"inst\", 4);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);\n                bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1);\n                bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1);\n                bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1);\n                bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1);\n                bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1);\n                bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1);\n                bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1);\n            } break;\n            case ma_dr_wav_metadata_type_cue:\n            {\n                ma_uint32 iCuePoint;\n                chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount;\n                bytesWritten += ma_dr_wav__write_or_count(pWav, \"cue \", 4);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount);\n                for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition);\n                    bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset);\n                }\n            } break;\n            case ma_dr_wav_metadata_type_acid:\n            {\n                chunkSize = MA_DR_WAV_ACID_BYTES;\n                bytesWritten += ma_dr_wav__write_or_count(pWav, \"acid\", 4);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags);\n                bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote);\n                bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1);\n                bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats);\n                bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator);\n                bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator);\n                bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo);\n            } break;\n            case ma_dr_wav_metadata_type_bext:\n            {\n                char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES];\n                ma_uint32 timeReferenceLow;\n                ma_uint32 timeReferenceHigh;\n                chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize;\n                bytesWritten += ma_dr_wav__write_or_count(pWav, \"bext\", 4);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);\n                bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES);\n                bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES);\n                bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES);\n                bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate));\n                bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime));\n                timeReferenceLow  = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF);\n                timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow);\n                bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh);\n                bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version);\n                bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES);\n                bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue);\n                bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange);\n                bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel);\n                bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness);\n                bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness);\n                MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf));\n                bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf));\n                if (pMetadata->data.bext.codingHistorySize > 0) {\n                    bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize);\n                }\n            } break;\n            case ma_dr_wav_metadata_type_unknown:\n            {\n                if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) {\n                    chunkSize = pMetadata->data.unknown.dataSizeInBytes;\n                    bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);\n                    bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes);\n                }\n            } break;\n            default: break;\n        }\n        if ((chunkSize % 2) != 0) {\n            bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);\n        }\n    }\n    if (hasListInfo) {\n        ma_uint32 chunkSize = 4;\n        for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {\n            ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];\n            if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) {\n                chunkSize += 8;\n                chunkSize += pMetadata->data.infoText.stringLength + 1;\n            } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) {\n                chunkSize += 8;\n                chunkSize += pMetadata->data.unknown.dataSizeInBytes;\n            }\n            if ((chunkSize % 2) != 0) {\n                chunkSize += 1;\n            }\n        }\n        bytesWritten += ma_dr_wav__write_or_count(pWav, \"LIST\", 4);\n        bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);\n        bytesWritten += ma_dr_wav__write_or_count(pWav, \"INFO\", 4);\n        for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {\n            ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];\n            ma_uint32 subchunkSize = 0;\n            if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) {\n                const char* pID = NULL;\n                switch (pMetadata->type) {\n                    case ma_dr_wav_metadata_type_list_info_software:    pID = \"ISFT\"; break;\n                    case ma_dr_wav_metadata_type_list_info_copyright:   pID = \"ICOP\"; break;\n                    case ma_dr_wav_metadata_type_list_info_title:       pID = \"INAM\"; break;\n                    case ma_dr_wav_metadata_type_list_info_artist:      pID = \"IART\"; break;\n                    case ma_dr_wav_metadata_type_list_info_comment:     pID = \"ICMT\"; break;\n                    case ma_dr_wav_metadata_type_list_info_date:        pID = \"ICRD\"; break;\n                    case ma_dr_wav_metadata_type_list_info_genre:       pID = \"IGNR\"; break;\n                    case ma_dr_wav_metadata_type_list_info_album:       pID = \"IPRD\"; break;\n                    case ma_dr_wav_metadata_type_list_info_tracknumber: pID = \"ITRK\"; break;\n                    default: break;\n                }\n                MA_DR_WAV_ASSERT(pID != NULL);\n                if (pMetadata->data.infoText.stringLength) {\n                    subchunkSize = pMetadata->data.infoText.stringLength + 1;\n                    bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);\n                    bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength);\n                    bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\\0');\n                }\n            } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) {\n                if (pMetadata->data.unknown.dataSizeInBytes) {\n                    subchunkSize = pMetadata->data.unknown.dataSizeInBytes;\n                    bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes);\n                    bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);\n                }\n            }\n            if ((subchunkSize % 2) != 0) {\n                bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);\n            }\n        }\n    }\n    if (hasListAdtl) {\n        ma_uint32 chunkSize = 4;\n        for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {\n            ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];\n            switch (pMetadata->type)\n            {\n                case ma_dr_wav_metadata_type_list_label:\n                case ma_dr_wav_metadata_type_list_note:\n                {\n                    chunkSize += 8;\n                    chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;\n                    if (pMetadata->data.labelOrNote.stringLength > 0) {\n                        chunkSize += pMetadata->data.labelOrNote.stringLength + 1;\n                    }\n                } break;\n                case ma_dr_wav_metadata_type_list_labelled_cue_region:\n                {\n                    chunkSize += 8;\n                    chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;\n                    if (pMetadata->data.labelledCueRegion.stringLength > 0) {\n                        chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;\n                    }\n                } break;\n                case ma_dr_wav_metadata_type_unknown:\n                {\n                    if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) {\n                        chunkSize += 8;\n                        chunkSize += pMetadata->data.unknown.dataSizeInBytes;\n                    }\n                } break;\n                default: break;\n            }\n            if ((chunkSize % 2) != 0) {\n                chunkSize += 1;\n            }\n        }\n        bytesWritten += ma_dr_wav__write_or_count(pWav, \"LIST\", 4);\n        bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);\n        bytesWritten += ma_dr_wav__write_or_count(pWav, \"adtl\", 4);\n        for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {\n            ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];\n            ma_uint32 subchunkSize = 0;\n            switch (pMetadata->type)\n            {\n                case ma_dr_wav_metadata_type_list_label:\n                case ma_dr_wav_metadata_type_list_note:\n                {\n                    if (pMetadata->data.labelOrNote.stringLength > 0) {\n                        const char *pID = NULL;\n                        if (pMetadata->type == ma_dr_wav_metadata_type_list_label) {\n                            pID = \"labl\";\n                        }\n                        else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) {\n                            pID = \"note\";\n                        }\n                        MA_DR_WAV_ASSERT(pID != NULL);\n                        MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);\n                        subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;\n                        bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4);\n                        subchunkSize += pMetadata->data.labelOrNote.stringLength + 1;\n                        bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);\n                        bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId);\n                        bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength);\n                        bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\\0');\n                    }\n                } break;\n                case ma_dr_wav_metadata_type_list_labelled_cue_region:\n                {\n                    subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;\n                    bytesWritten += ma_dr_wav__write_or_count(pWav, \"ltxt\", 4);\n                    if (pMetadata->data.labelledCueRegion.stringLength > 0) {\n                        subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;\n                    }\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId);\n                    bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength);\n                    bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4);\n                    bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country);\n                    bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language);\n                    bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect);\n                    bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage);\n                    if (pMetadata->data.labelledCueRegion.stringLength > 0) {\n                        MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);\n                        bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength);\n                        bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\\0');\n                    }\n                } break;\n                case ma_dr_wav_metadata_type_unknown:\n                {\n                    if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) {\n                        subchunkSize = pMetadata->data.unknown.dataSizeInBytes;\n                        MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL);\n                        bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);\n                        bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);\n                        bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);\n                    }\n                } break;\n                default: break;\n            }\n            if ((subchunkSize % 2) != 0) {\n                bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);\n            }\n        }\n    }\n    MA_DR_WAV_ASSERT((bytesWritten % 2) == 0);\n    return bytesWritten;\n}\nMA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount)\n{\n    ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize);\n    if (chunkSize > 0xFFFFFFFFUL) {\n        chunkSize = 0xFFFFFFFFUL;\n    }\n    return (ma_uint32)chunkSize;\n}\nMA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize)\n{\n    if (dataChunkSize <= 0xFFFFFFFFUL) {\n        return (ma_uint32)dataChunkSize;\n    } else {\n        return 0xFFFFFFFFUL;\n    }\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize)\n{\n    ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize);\n    return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize)\n{\n    return 24 + dataChunkSize;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata)\n{\n    ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize);\n    if (chunkSize > 0xFFFFFFFFUL) {\n        chunkSize = 0xFFFFFFFFUL;\n    }\n    return chunkSize;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize)\n{\n    return dataChunkSize;\n}\nMA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pWav == NULL || onWrite == NULL) {\n        return MA_FALSE;\n    }\n    if (!isSequential && onSeek == NULL) {\n        return MA_FALSE;\n    }\n    if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) {\n        return MA_FALSE;\n    }\n    if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) {\n        return MA_FALSE;\n    }\n    MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav));\n    pWav->onWrite   = onWrite;\n    pWav->onSeek    = onSeek;\n    pWav->pUserData = pUserData;\n    pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);\n    if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {\n        return MA_FALSE;\n    }\n    pWav->fmt.formatTag = (ma_uint16)pFormat->format;\n    pWav->fmt.channels = (ma_uint16)pFormat->channels;\n    pWav->fmt.sampleRate = pFormat->sampleRate;\n    pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);\n    pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);\n    pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample;\n    pWav->fmt.extendedSize = 0;\n    pWav->isSequentialWrite = isSequential;\n    return MA_TRUE;\n}\nMA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount)\n{\n    size_t runningPos = 0;\n    ma_uint64 initialDataChunkSize = 0;\n    ma_uint64 chunkSizeFMT;\n    if (pWav->isSequentialWrite) {\n        initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;\n        if (pFormat->container == ma_dr_wav_container_riff) {\n            if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {\n                return MA_FALSE;\n            }\n        }\n    }\n    pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;\n    if (pFormat->container == ma_dr_wav_container_riff) {\n        ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize;\n        runningPos += ma_dr_wav__write(pWav, \"RIFF\", 4);\n        runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF);\n        runningPos += ma_dr_wav__write(pWav, \"WAVE\", 4);\n    } else if (pFormat->container == ma_dr_wav_container_w64) {\n        ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize;\n        runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16);\n        runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF);\n        runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16);\n    } else if (pFormat->container == ma_dr_wav_container_rf64) {\n        runningPos += ma_dr_wav__write(pWav, \"RF64\", 4);\n        runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF);\n        runningPos += ma_dr_wav__write(pWav, \"WAVE\", 4);\n    } else {\n        return MA_FALSE;\n    }\n    if (pFormat->container == ma_dr_wav_container_rf64) {\n        ma_uint32 initialds64ChunkSize = 28;\n        ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize;\n        runningPos += ma_dr_wav__write(pWav, \"ds64\", 4);\n        runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize);\n        runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize);\n        runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize);\n        runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount);\n        runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0);\n    }\n    if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) {\n        chunkSizeFMT = 16;\n        runningPos += ma_dr_wav__write(pWav, \"fmt \", 4);\n        runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT);\n    } else if (pFormat->container == ma_dr_wav_container_w64) {\n        chunkSizeFMT = 40;\n        runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16);\n        runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT);\n    }\n    runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);\n    runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels);\n    runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);\n    runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);\n    runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);\n    runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);\n    if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) {\n        runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount);\n    }\n    pWav->dataChunkDataPos = runningPos;\n    if (pFormat->container == ma_dr_wav_container_riff) {\n        ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize;\n        runningPos += ma_dr_wav__write(pWav, \"data\", 4);\n        runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA);\n    } else if (pFormat->container == ma_dr_wav_container_w64) {\n        ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize;\n        runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16);\n        runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA);\n    } else if (pFormat->container == ma_dr_wav_container_rf64) {\n        runningPos += ma_dr_wav__write(pWav, \"data\", 4);\n        runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF);\n    }\n    pWav->container = pFormat->container;\n    pWav->channels = (ma_uint16)pFormat->channels;\n    pWav->sampleRate = pFormat->sampleRate;\n    pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample;\n    pWav->translatedFormatTag = (ma_uint16)pFormat->format;\n    pWav->dataChunkDataPos = runningPos;\n    return MA_TRUE;\n}\nMA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_write__internal(pWav, pFormat, 0);\n}\nMA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);\n}\nMA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFormat == NULL) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);\n}\nMA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount)\n{\n    if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {\n        return MA_FALSE;\n    }\n    pWav->pMetadata     = pMetadata;\n    pWav->metadataCount = metadataCount;\n    return ma_dr_wav_init_write__internal(pWav, pFormat, 0);\n}\nMA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount)\n{\n    ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0);\n    ma_uint64 riffChunkSizeBytes;\n    ma_uint64 fileSizeBytes = 0;\n    if (pFormat->container == ma_dr_wav_container_riff) {\n        riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount);\n        fileSizeBytes = (8 + riffChunkSizeBytes);\n    } else if (pFormat->container == ma_dr_wav_container_w64) {\n        riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes);\n        fileSizeBytes = riffChunkSizeBytes;\n    } else if (pFormat->container == ma_dr_wav_container_rf64) {\n        riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount);\n        fileSizeBytes = (8 + riffChunkSizeBytes);\n    }\n    return fileSizeBytes;\n}\n#ifndef MA_DR_WAV_NO_STDIO\nMA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)\n{\n    return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);\n}\nMA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)\n{\n    return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);\n}\nMA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin)\n{\n    return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;\n}\nMA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);\n}\nMA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_bool32 result;\n    result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);\n    if (result != MA_TRUE) {\n        fclose(pFile);\n        return result;\n    }\n    result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);\n    if (result != MA_TRUE) {\n        fclose(pFile);\n        return result;\n    }\n    return MA_TRUE;\n}\nMA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    FILE* pFile;\n    if (ma_fopen(&pFile, filename, \"rb\") != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);\n}\n#ifndef MA_DR_WAV_NO_WCHAR\nMA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);\n}\nMA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    FILE* pFile;\n    if (ma_wfopen(&pFile, filename, L\"rb\", pAllocationCallbacks) != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);\n}\n#endif\nMA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    FILE* pFile;\n    if (ma_fopen(&pFile, filename, \"rb\") != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks);\n}\n#ifndef MA_DR_WAV_NO_WCHAR\nMA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    FILE* pFile;\n    if (ma_wfopen(&pFile, filename, L\"rb\", pAllocationCallbacks) != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks);\n}\n#endif\nMA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_bool32 result;\n    result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);\n    if (result != MA_TRUE) {\n        fclose(pFile);\n        return result;\n    }\n    result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);\n    if (result != MA_TRUE) {\n        fclose(pFile);\n        return result;\n    }\n    return MA_TRUE;\n}\nMA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    FILE* pFile;\n    if (ma_fopen(&pFile, filename, \"wb\") != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);\n}\n#ifndef MA_DR_WAV_NO_WCHAR\nMA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    FILE* pFile;\n    if (ma_wfopen(&pFile, filename, L\"wb\", pAllocationCallbacks) != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);\n}\n#endif\nMA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks);\n}\nMA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);\n}\nMA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFormat == NULL) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);\n}\n#ifndef MA_DR_WAV_NO_WCHAR\nMA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks);\n}\nMA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);\n}\nMA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFormat == NULL) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);\n}\n#endif\n#endif\nMA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)\n{\n    ma_dr_wav* pWav = (ma_dr_wav*)pUserData;\n    size_t bytesRemaining;\n    MA_DR_WAV_ASSERT(pWav != NULL);\n    MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);\n    bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;\n    if (bytesToRead > bytesRemaining) {\n        bytesToRead = bytesRemaining;\n    }\n    if (bytesToRead > 0) {\n        MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);\n        pWav->memoryStream.currentReadPos += bytesToRead;\n    }\n    return bytesToRead;\n}\nMA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin)\n{\n    ma_dr_wav* pWav = (ma_dr_wav*)pUserData;\n    MA_DR_WAV_ASSERT(pWav != NULL);\n    if (origin == ma_dr_wav_seek_origin_current) {\n        if (offset > 0) {\n            if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) {\n                return MA_FALSE;\n            }\n        } else {\n            if (pWav->memoryStream.currentReadPos < (size_t)-offset) {\n                return MA_FALSE;\n            }\n        }\n        pWav->memoryStream.currentReadPos += offset;\n    } else {\n        if ((ma_uint32)offset <= pWav->memoryStream.dataSize) {\n            pWav->memoryStream.currentReadPos = offset;\n        } else {\n            return MA_FALSE;\n        }\n    }\n    return MA_TRUE;\n}\nMA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)\n{\n    ma_dr_wav* pWav = (ma_dr_wav*)pUserData;\n    size_t bytesRemaining;\n    MA_DR_WAV_ASSERT(pWav != NULL);\n    MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos);\n    bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;\n    if (bytesRemaining < bytesToWrite) {\n        void* pNewData;\n        size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;\n        if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {\n            newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;\n        }\n        pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);\n        if (pNewData == NULL) {\n            return 0;\n        }\n        *pWav->memoryStreamWrite.ppData = pNewData;\n        pWav->memoryStreamWrite.dataCapacity = newDataCapacity;\n    }\n    MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);\n    pWav->memoryStreamWrite.currentWritePos += bytesToWrite;\n    if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) {\n        pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos;\n    }\n    *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize;\n    return bytesToWrite;\n}\nMA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin)\n{\n    ma_dr_wav* pWav = (ma_dr_wav*)pUserData;\n    MA_DR_WAV_ASSERT(pWav != NULL);\n    if (origin == ma_dr_wav_seek_origin_current) {\n        if (offset > 0) {\n            if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) {\n                offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos);\n            }\n        } else {\n            if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) {\n                offset = -(int)pWav->memoryStreamWrite.currentWritePos;\n            }\n        }\n        pWav->memoryStreamWrite.currentWritePos += offset;\n    } else {\n        if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) {\n            pWav->memoryStreamWrite.currentWritePos = offset;\n        } else {\n            pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize;\n        }\n    }\n    return MA_TRUE;\n}\nMA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);\n}\nMA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (data == NULL || dataSize == 0) {\n        return MA_FALSE;\n    }\n    if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) {\n        return MA_FALSE;\n    }\n    pWav->memoryStream.data = (const ma_uint8*)data;\n    pWav->memoryStream.dataSize = dataSize;\n    pWav->memoryStream.currentReadPos = 0;\n    return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);\n}\nMA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (data == NULL || dataSize == 0) {\n        return MA_FALSE;\n    }\n    if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) {\n        return MA_FALSE;\n    }\n    pWav->memoryStream.data = (const ma_uint8*)data;\n    pWav->memoryStream.dataSize = dataSize;\n    pWav->memoryStream.currentReadPos = 0;\n    return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA);\n}\nMA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (ppData == NULL || pDataSize == NULL) {\n        return MA_FALSE;\n    }\n    *ppData = NULL;\n    *pDataSize = 0;\n    if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) {\n        return MA_FALSE;\n    }\n    pWav->memoryStreamWrite.ppData = ppData;\n    pWav->memoryStreamWrite.pDataSize = pDataSize;\n    pWav->memoryStreamWrite.dataSize = 0;\n    pWav->memoryStreamWrite.dataCapacity = 0;\n    pWav->memoryStreamWrite.currentWritePos = 0;\n    return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);\n}\nMA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks);\n}\nMA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);\n}\nMA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pFormat == NULL) {\n        return MA_FALSE;\n    }\n    return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);\n}\nMA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav)\n{\n    ma_result result = MA_SUCCESS;\n    if (pWav == NULL) {\n        return MA_INVALID_ARGS;\n    }\n    if (pWav->onWrite != NULL) {\n        ma_uint32 paddingSize = 0;\n        if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) {\n            paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize);\n        } else {\n            paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize);\n        }\n        if (paddingSize > 0) {\n            ma_uint64 paddingData = 0;\n            ma_dr_wav__write(pWav, &paddingData, paddingSize);\n        }\n        if (pWav->onSeek && !pWav->isSequentialWrite) {\n            if (pWav->container == ma_dr_wav_container_riff) {\n                if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) {\n                    ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);\n                    ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize);\n                }\n                if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) {\n                    ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize);\n                    ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize);\n                }\n            } else if (pWav->container == ma_dr_wav_container_w64) {\n                if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) {\n                    ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize);\n                    ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize);\n                }\n                if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) {\n                    ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize);\n                    ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize);\n                }\n            } else if (pWav->container == ma_dr_wav_container_rf64) {\n                int ds64BodyPos = 12 + 8;\n                if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) {\n                    ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);\n                    ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize);\n                }\n                if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) {\n                    ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize);\n                    ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize);\n                }\n            }\n        }\n        if (pWav->isSequentialWrite) {\n            if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) {\n                result = MA_INVALID_FILE;\n            }\n        }\n    } else {\n        ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);\n    }\n#ifndef MA_DR_WAV_NO_STDIO\n    if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) {\n        fclose((FILE*)pWav->pUserData);\n    }\n#endif\n    return result;\n}\nMA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut)\n{\n    size_t bytesRead;\n    ma_uint32 bytesPerFrame;\n    if (pWav == NULL || bytesToRead == 0) {\n        return 0;\n    }\n    if (bytesToRead > pWav->bytesRemaining) {\n        bytesToRead = (size_t)pWav->bytesRemaining;\n    }\n    if (bytesToRead == 0) {\n        return 0;\n    }\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    if (pBufferOut != NULL) {\n        bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);\n    } else {\n        bytesRead = 0;\n        while (bytesRead < bytesToRead) {\n            size_t bytesToSeek = (bytesToRead - bytesRead);\n            if (bytesToSeek > 0x7FFFFFFF) {\n                bytesToSeek = 0x7FFFFFFF;\n            }\n            if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) {\n                break;\n            }\n            bytesRead += bytesToSeek;\n        }\n        while (bytesRead < bytesToRead) {\n            ma_uint8 buffer[4096];\n            size_t bytesSeeked;\n            size_t bytesToSeek = (bytesToRead - bytesRead);\n            if (bytesToSeek > sizeof(buffer)) {\n                bytesToSeek = sizeof(buffer);\n            }\n            bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek);\n            bytesRead += bytesSeeked;\n            if (bytesSeeked < bytesToSeek) {\n                break;\n            }\n        }\n    }\n    pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame;\n    pWav->bytesRemaining -= bytesRead;\n    return bytesRead;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)\n{\n    ma_uint32 bytesPerFrame;\n    ma_uint64 bytesToRead;\n    ma_uint64 framesRemainingInFile;\n    if (pWav == NULL || framesToRead == 0) {\n        return 0;\n    }\n    if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {\n        return 0;\n    }\n    framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames;\n    if (framesToRead > framesRemainingInFile) {\n        framesToRead = framesRemainingInFile;\n    }\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesToRead = framesToRead * bytesPerFrame;\n    if (bytesToRead > MA_SIZE_MAX) {\n        bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame;\n    }\n    if (bytesToRead == 0) {\n        return 0;\n    }\n    return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)\n{\n    ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL) {\n        ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n        if (bytesPerFrame == 0) {\n            return 0;\n        }\n        ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels);\n    }\n    return framesRead;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)\n{\n    ma_uint64 framesRead = 0;\n    if (ma_dr_wav_is_container_be(pWav->container)) {\n        if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) {\n            if (ma_dr_wav__is_little_endian()) {\n                framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);\n            } else {\n                framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);\n            }\n            goto post_process;\n        }\n    }\n    if (ma_dr_wav__is_little_endian()) {\n        framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);\n    } else {\n        framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);\n    }\n    post_process:\n    {\n        if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) {\n            if (pBufferOut != NULL) {\n                ma_uint64 iSample;\n                for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) {\n                    ((ma_uint8*)pBufferOut)[iSample] += 128;\n                }\n            }\n        }\n    }\n    return framesRead;\n}\nMA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav)\n{\n    if (pWav->onWrite != NULL) {\n        return MA_FALSE;\n    }\n    if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) {\n        return MA_FALSE;\n    }\n    if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {\n        if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {\n            MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm);\n        } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {\n            MA_DR_WAV_ZERO_OBJECT(&pWav->ima);\n        } else {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n        }\n    }\n    pWav->readCursorInPCMFrames = 0;\n    pWav->bytesRemaining = pWav->dataChunkDataSize;\n    return MA_TRUE;\n}\nMA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex)\n{\n    if (pWav == NULL || pWav->onSeek == NULL) {\n        return MA_FALSE;\n    }\n    if (pWav->onWrite != NULL) {\n        return MA_FALSE;\n    }\n    if (pWav->totalPCMFrameCount == 0) {\n        return MA_TRUE;\n    }\n    if (targetFrameIndex > pWav->totalPCMFrameCount) {\n        targetFrameIndex = pWav->totalPCMFrameCount;\n    }\n    if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {\n        if (targetFrameIndex < pWav->readCursorInPCMFrames) {\n            if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) {\n                return MA_FALSE;\n            }\n        }\n        if (targetFrameIndex > pWav->readCursorInPCMFrames) {\n            ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames;\n            ma_int16 devnull[2048];\n            while (offsetInFrames > 0) {\n                ma_uint64 framesRead = 0;\n                ma_uint64 framesToRead = offsetInFrames;\n                if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) {\n                    framesToRead = ma_dr_wav_countof(devnull)/pWav->channels;\n                }\n                if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {\n                    framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);\n                } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {\n                    framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);\n                } else {\n                    MA_DR_WAV_ASSERT(MA_FALSE);\n                }\n                if (framesRead != framesToRead) {\n                    return MA_FALSE;\n                }\n                offsetInFrames -= framesRead;\n            }\n        }\n    } else {\n        ma_uint64 totalSizeInBytes;\n        ma_uint64 currentBytePos;\n        ma_uint64 targetBytePos;\n        ma_uint64 offset;\n        ma_uint32 bytesPerFrame;\n        bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n        if (bytesPerFrame == 0) {\n            return MA_FALSE;\n        }\n        totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame;\n        currentBytePos = totalSizeInBytes - pWav->bytesRemaining;\n        targetBytePos  = targetFrameIndex * bytesPerFrame;\n        if (currentBytePos < targetBytePos) {\n            offset = (targetBytePos - currentBytePos);\n        } else {\n            if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) {\n                return MA_FALSE;\n            }\n            offset = targetBytePos;\n        }\n        while (offset > 0) {\n            int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);\n            if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) {\n                return MA_FALSE;\n            }\n            pWav->readCursorInPCMFrames += offset32 / bytesPerFrame;\n            pWav->bytesRemaining        -= offset32;\n            offset                      -= offset32;\n        }\n    }\n    return MA_TRUE;\n}\nMA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor)\n{\n    if (pCursor == NULL) {\n        return MA_INVALID_ARGS;\n    }\n    *pCursor = 0;\n    if (pWav == NULL) {\n        return MA_INVALID_ARGS;\n    }\n    *pCursor = pWav->readCursorInPCMFrames;\n    return MA_SUCCESS;\n}\nMA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength)\n{\n    if (pLength == NULL) {\n        return MA_INVALID_ARGS;\n    }\n    *pLength = 0;\n    if (pWav == NULL) {\n        return MA_INVALID_ARGS;\n    }\n    *pLength = pWav->totalPCMFrameCount;\n    return MA_SUCCESS;\n}\nMA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData)\n{\n    size_t bytesWritten;\n    if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {\n        return 0;\n    }\n    bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);\n    pWav->dataChunkDataSize += bytesWritten;\n    return bytesWritten;\n}\nMA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)\n{\n    ma_uint64 bytesToWrite;\n    ma_uint64 bytesWritten;\n    const ma_uint8* pRunningData;\n    if (pWav == NULL || framesToWrite == 0 || pData == NULL) {\n        return 0;\n    }\n    bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);\n    if (bytesToWrite > MA_SIZE_MAX) {\n        return 0;\n    }\n    bytesWritten = 0;\n    pRunningData = (const ma_uint8*)pData;\n    while (bytesToWrite > 0) {\n        size_t bytesJustWritten;\n        ma_uint64 bytesToWriteThisIteration;\n        bytesToWriteThisIteration = bytesToWrite;\n        MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX);\n        bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);\n        if (bytesJustWritten == 0) {\n            break;\n        }\n        bytesToWrite -= bytesJustWritten;\n        bytesWritten += bytesJustWritten;\n        pRunningData += bytesJustWritten;\n    }\n    return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;\n}\nMA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)\n{\n    ma_uint64 bytesToWrite;\n    ma_uint64 bytesWritten;\n    ma_uint32 bytesPerSample;\n    const ma_uint8* pRunningData;\n    if (pWav == NULL || framesToWrite == 0 || pData == NULL) {\n        return 0;\n    }\n    bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);\n    if (bytesToWrite > MA_SIZE_MAX) {\n        return 0;\n    }\n    bytesWritten = 0;\n    pRunningData = (const ma_uint8*)pData;\n    bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels;\n    if (bytesPerSample == 0) {\n        return 0;\n    }\n    while (bytesToWrite > 0) {\n        ma_uint8 temp[4096];\n        ma_uint32 sampleCount;\n        size_t bytesJustWritten;\n        ma_uint64 bytesToWriteThisIteration;\n        bytesToWriteThisIteration = bytesToWrite;\n        MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX);\n        sampleCount = sizeof(temp)/bytesPerSample;\n        if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) {\n            bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample;\n        }\n        MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);\n        ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample);\n        bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);\n        if (bytesJustWritten == 0) {\n            break;\n        }\n        bytesToWrite -= bytesJustWritten;\n        bytesWritten += bytesJustWritten;\n        pRunningData += bytesJustWritten;\n    }\n    return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;\n}\nMA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)\n{\n    if (ma_dr_wav__is_little_endian()) {\n        return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData);\n    } else {\n        return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData);\n    }\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)\n{\n    ma_uint64 totalFramesRead = 0;\n    MA_DR_WAV_ASSERT(pWav != NULL);\n    MA_DR_WAV_ASSERT(framesToRead > 0);\n    while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {\n        MA_DR_WAV_ASSERT(framesToRead > 0);\n        if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {\n            if (pWav->channels == 1) {\n                ma_uint8 header[7];\n                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {\n                    return totalFramesRead;\n                }\n                pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);\n                pWav->msadpcm.predictor[0]     = header[0];\n                pWav->msadpcm.delta[0]         = ma_dr_wav_bytes_to_s16(header + 1);\n                pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3);\n                pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5);\n                pWav->msadpcm.cachedFrames[2]  = pWav->msadpcm.prevFrames[0][0];\n                pWav->msadpcm.cachedFrames[3]  = pWav->msadpcm.prevFrames[0][1];\n                pWav->msadpcm.cachedFrameCount = 2;\n            } else {\n                ma_uint8 header[14];\n                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {\n                    return totalFramesRead;\n                }\n                pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);\n                pWav->msadpcm.predictor[0] = header[0];\n                pWav->msadpcm.predictor[1] = header[1];\n                pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2);\n                pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4);\n                pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6);\n                pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8);\n                pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10);\n                pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12);\n                pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];\n                pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];\n                pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];\n                pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];\n                pWav->msadpcm.cachedFrameCount = 2;\n            }\n        }\n        while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {\n            if (pBufferOut != NULL) {\n                ma_uint32 iSample = 0;\n                for (iSample = 0; iSample < pWav->channels; iSample += 1) {\n                    pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];\n                }\n                pBufferOut += pWav->channels;\n            }\n            framesToRead    -= 1;\n            totalFramesRead += 1;\n            pWav->readCursorInPCMFrames += 1;\n            pWav->msadpcm.cachedFrameCount -= 1;\n        }\n        if (framesToRead == 0) {\n            break;\n        }\n        if (pWav->msadpcm.cachedFrameCount == 0) {\n            if (pWav->msadpcm.bytesRemainingInBlock == 0) {\n                continue;\n            } else {\n                static ma_int32 adaptationTable[] = {\n                    230, 230, 230, 230, 307, 409, 512, 614,\n                    768, 614, 512, 409, 307, 230, 230, 230\n                };\n                static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460,  392 };\n                static ma_int32 coeff2Table[] = { 0,  -256, 0, 64,  0,  -208, -232 };\n                ma_uint8 nibbles;\n                ma_int32 nibble0;\n                ma_int32 nibble1;\n                if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {\n                    return totalFramesRead;\n                }\n                pWav->msadpcm.bytesRemainingInBlock -= 1;\n                nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }\n                nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }\n                if (pWav->channels == 1) {\n                    ma_int32 newSample0;\n                    ma_int32 newSample1;\n                    newSample0  = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;\n                    newSample0 += nibble0 * pWav->msadpcm.delta[0];\n                    newSample0  = ma_dr_wav_clamp(newSample0, -32768, 32767);\n                    pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;\n                    if (pWav->msadpcm.delta[0] < 16) {\n                        pWav->msadpcm.delta[0] = 16;\n                    }\n                    pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];\n                    pWav->msadpcm.prevFrames[0][1] = newSample0;\n                    newSample1  = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;\n                    newSample1 += nibble1 * pWav->msadpcm.delta[0];\n                    newSample1  = ma_dr_wav_clamp(newSample1, -32768, 32767);\n                    pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;\n                    if (pWav->msadpcm.delta[0] < 16) {\n                        pWav->msadpcm.delta[0] = 16;\n                    }\n                    pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];\n                    pWav->msadpcm.prevFrames[0][1] = newSample1;\n                    pWav->msadpcm.cachedFrames[2] = newSample0;\n                    pWav->msadpcm.cachedFrames[3] = newSample1;\n                    pWav->msadpcm.cachedFrameCount = 2;\n                } else {\n                    ma_int32 newSample0;\n                    ma_int32 newSample1;\n                    newSample0  = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;\n                    newSample0 += nibble0 * pWav->msadpcm.delta[0];\n                    newSample0  = ma_dr_wav_clamp(newSample0, -32768, 32767);\n                    pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;\n                    if (pWav->msadpcm.delta[0] < 16) {\n                        pWav->msadpcm.delta[0] = 16;\n                    }\n                    pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];\n                    pWav->msadpcm.prevFrames[0][1] = newSample0;\n                    newSample1  = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;\n                    newSample1 += nibble1 * pWav->msadpcm.delta[1];\n                    newSample1  = ma_dr_wav_clamp(newSample1, -32768, 32767);\n                    pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;\n                    if (pWav->msadpcm.delta[1] < 16) {\n                        pWav->msadpcm.delta[1] = 16;\n                    }\n                    pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];\n                    pWav->msadpcm.prevFrames[1][1] = newSample1;\n                    pWav->msadpcm.cachedFrames[2] = newSample0;\n                    pWav->msadpcm.cachedFrames[3] = newSample1;\n                    pWav->msadpcm.cachedFrameCount = 1;\n                }\n            }\n        }\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)\n{\n    ma_uint64 totalFramesRead = 0;\n    ma_uint32 iChannel;\n    static ma_int32 indexTable[16] = {\n        -1, -1, -1, -1, 2, 4, 6, 8,\n        -1, -1, -1, -1, 2, 4, 6, 8\n    };\n    static ma_int32 stepTable[89] = {\n        7,     8,     9,     10,    11,    12,    13,    14,    16,    17,\n        19,    21,    23,    25,    28,    31,    34,    37,    41,    45,\n        50,    55,    60,    66,    73,    80,    88,    97,    107,   118,\n        130,   143,   157,   173,   190,   209,   230,   253,   279,   307,\n        337,   371,   408,   449,   494,   544,   598,   658,   724,   796,\n        876,   963,   1060,  1166,  1282,  1411,  1552,  1707,  1878,  2066,\n        2272,  2499,  2749,  3024,  3327,  3660,  4026,  4428,  4871,  5358,\n        5894,  6484,  7132,  7845,  8630,  9493,  10442, 11487, 12635, 13899,\n        15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767\n    };\n    MA_DR_WAV_ASSERT(pWav != NULL);\n    MA_DR_WAV_ASSERT(framesToRead > 0);\n    while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {\n        MA_DR_WAV_ASSERT(framesToRead > 0);\n        if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {\n            if (pWav->channels == 1) {\n                ma_uint8 header[4];\n                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {\n                    return totalFramesRead;\n                }\n                pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);\n                if (header[2] >= ma_dr_wav_countof(stepTable)) {\n                    pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current);\n                    pWav->ima.bytesRemainingInBlock = 0;\n                    return totalFramesRead;\n                }\n                pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0);\n                pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);\n                pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];\n                pWav->ima.cachedFrameCount = 1;\n            } else {\n                ma_uint8 header[8];\n                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {\n                    return totalFramesRead;\n                }\n                pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);\n                if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) {\n                    pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current);\n                    pWav->ima.bytesRemainingInBlock = 0;\n                    return totalFramesRead;\n                }\n                pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0);\n                pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);\n                pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4);\n                pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);\n                pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];\n                pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];\n                pWav->ima.cachedFrameCount = 1;\n            }\n        }\n        while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {\n            if (pBufferOut != NULL) {\n                ma_uint32 iSample;\n                for (iSample = 0; iSample < pWav->channels; iSample += 1) {\n                    pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];\n                }\n                pBufferOut += pWav->channels;\n            }\n            framesToRead    -= 1;\n            totalFramesRead += 1;\n            pWav->readCursorInPCMFrames += 1;\n            pWav->ima.cachedFrameCount -= 1;\n        }\n        if (framesToRead == 0) {\n            break;\n        }\n        if (pWav->ima.cachedFrameCount == 0) {\n            if (pWav->ima.bytesRemainingInBlock == 0) {\n                continue;\n            } else {\n                pWav->ima.cachedFrameCount = 8;\n                for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {\n                    ma_uint32 iByte;\n                    ma_uint8 nibbles[4];\n                    if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {\n                        pWav->ima.cachedFrameCount = 0;\n                        return totalFramesRead;\n                    }\n                    pWav->ima.bytesRemainingInBlock -= 4;\n                    for (iByte = 0; iByte < 4; ++iByte) {\n                        ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);\n                        ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);\n                        ma_int32 step      = stepTable[pWav->ima.stepIndex[iChannel]];\n                        ma_int32 predictor = pWav->ima.predictor[iChannel];\n                        ma_int32      diff  = step >> 3;\n                        if (nibble0 & 1) diff += step >> 2;\n                        if (nibble0 & 2) diff += step >> 1;\n                        if (nibble0 & 4) diff += step;\n                        if (nibble0 & 8) diff  = -diff;\n                        predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767);\n                        pWav->ima.predictor[iChannel] = predictor;\n                        pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);\n                        pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;\n                        step      = stepTable[pWav->ima.stepIndex[iChannel]];\n                        predictor = pWav->ima.predictor[iChannel];\n                                         diff  = step >> 3;\n                        if (nibble1 & 1) diff += step >> 2;\n                        if (nibble1 & 2) diff += step >> 1;\n                        if (nibble1 & 4) diff += step;\n                        if (nibble1 & 8) diff  = -diff;\n                        predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767);\n                        pWav->ima.predictor[iChannel] = predictor;\n                        pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);\n                        pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;\n                    }\n                }\n            }\n        }\n    }\n    return totalFramesRead;\n}\n#ifndef MA_DR_WAV_NO_CONVERSION_API\nstatic unsigned short g_ma_dr_wavAlawTable[256] = {\n    0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,\n    0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,\n    0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,\n    0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,\n    0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,\n    0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,\n    0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,\n    0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,\n    0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,\n    0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,\n    0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,\n    0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,\n    0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,\n    0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,\n    0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,\n    0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350\n};\nstatic unsigned short g_ma_dr_wavMulawTable[256] = {\n    0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,\n    0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,\n    0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,\n    0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,\n    0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,\n    0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,\n    0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,\n    0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,\n    0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,\n    0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,\n    0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,\n    0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,\n    0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,\n    0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,\n    0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,\n    0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000\n};\nstatic MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn)\n{\n    return (short)g_ma_dr_wavAlawTable[sampleIn];\n}\nstatic MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn)\n{\n    return (short)g_ma_dr_wavMulawTable[sampleIn];\n}\nMA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)\n{\n    size_t i;\n    if (bytesPerSample == 1) {\n        ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount);\n        return;\n    }\n    if (bytesPerSample == 2) {\n        for (i = 0; i < totalSampleCount; ++i) {\n           *pOut++ = ((const ma_int16*)pIn)[i];\n        }\n        return;\n    }\n    if (bytesPerSample == 3) {\n        ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount);\n        return;\n    }\n    if (bytesPerSample == 4) {\n        ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount);\n        return;\n    }\n    if (bytesPerSample > 8) {\n        MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));\n        return;\n    }\n    for (i = 0; i < totalSampleCount; ++i) {\n        ma_uint64 sample = 0;\n        unsigned int shift  = (8 - bytesPerSample) * 8;\n        unsigned int j;\n        for (j = 0; j < bytesPerSample; j += 1) {\n            MA_DR_WAV_ASSERT(j < 8);\n            sample |= (ma_uint64)(pIn[j]) << shift;\n            shift  += 8;\n        }\n        pIn += j;\n        *pOut++ = (ma_int16)((ma_int64)sample >> 48);\n    }\n}\nMA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)\n{\n    if (bytesPerSample == 4) {\n        ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);\n        return;\n    } else if (bytesPerSample == 8) {\n        ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);\n        return;\n    } else {\n        MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));\n        return;\n    }\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) {\n        return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);\n    }\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    if (pBufferOut == NULL) {\n        return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    if (pBufferOut == NULL) {\n        return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);\n        #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT\n        {\n            if (pWav->container == ma_dr_wav_container_aiff) {\n                ma_uint64 iSample;\n                for (iSample = 0; iSample < samplesRead; iSample += 1) {\n                    pBufferOut[iSample] = -pBufferOut[iSample];\n                }\n            }\n        }\n        #endif\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    if (pBufferOut == NULL) {\n        return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);\n        #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT\n        {\n            if (pWav->container == ma_dr_wav_container_aiff) {\n                ma_uint64 iSample;\n                for (iSample = 0; iSample < samplesRead; iSample += 1) {\n                    pBufferOut[iSample] = -pBufferOut[iSample];\n                }\n            }\n        }\n        #endif\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)\n{\n    if (pWav == NULL || framesToRead == 0) {\n        return 0;\n    }\n    if (pBufferOut == NULL) {\n        return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n    if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) {\n        framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels;\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {\n        return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {\n        return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {\n        return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {\n        return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {\n        return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {\n        return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);\n    }\n    return 0;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)\n{\n    ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {\n        ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);\n    }\n    return framesRead;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)\n{\n    ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {\n        ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);\n    }\n    return framesRead;\n}\nMA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    int r;\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        int x = pIn[i];\n        r = x << 8;\n        r = r - 32768;\n        pOut[i] = (short)r;\n    }\n}\nMA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    int r;\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8;\n        r = x >> 8;\n        pOut[i] = (short)r;\n    }\n}\nMA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount)\n{\n    int r;\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        int x = pIn[i];\n        r = x >> 16;\n        pOut[i] = (short)r;\n    }\n}\nMA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount)\n{\n    int r;\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        float x = pIn[i];\n        float c;\n        c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));\n        c = c + 1;\n        r = (int)(c * 32767.5f);\n        r = r - 32768;\n        pOut[i] = (short)r;\n    }\n}\nMA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount)\n{\n    int r;\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        double x = pIn[i];\n        double c;\n        c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));\n        c = c + 1;\n        r = (int)(c * 32767.5);\n        r = r - 32768;\n        pOut[i] = (short)r;\n    }\n}\nMA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]);\n    }\n}\nMA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    for (i = 0; i < sampleCount; ++i) {\n        pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]);\n    }\n}\nMA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)\n{\n    unsigned int i;\n    if (bytesPerSample == 1) {\n        ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount);\n        return;\n    }\n    if (bytesPerSample == 2) {\n        ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount);\n        return;\n    }\n    if (bytesPerSample == 3) {\n        ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount);\n        return;\n    }\n    if (bytesPerSample == 4) {\n        ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount);\n        return;\n    }\n    if (bytesPerSample > 8) {\n        MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        ma_uint64 sample = 0;\n        unsigned int shift  = (8 - bytesPerSample) * 8;\n        unsigned int j;\n        for (j = 0; j < bytesPerSample; j += 1) {\n            MA_DR_WAV_ASSERT(j < 8);\n            sample |= (ma_uint64)(pIn[j]) << shift;\n            shift  += 8;\n        }\n        pIn += j;\n        *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0);\n    }\n}\nMA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)\n{\n    if (bytesPerSample == 4) {\n        unsigned int i;\n        for (i = 0; i < sampleCount; ++i) {\n            *pOut++ = ((const float*)pIn)[i];\n        }\n        return;\n    } else if (bytesPerSample == 8) {\n        ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount);\n        return;\n    } else {\n        MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));\n        return;\n    }\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_int16 samples16[2048];\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {\n        return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);\n    }\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);\n        #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT\n        {\n            if (pWav->container == ma_dr_wav_container_aiff) {\n                ma_uint64 iSample;\n                for (iSample = 0; iSample < samplesRead; iSample += 1) {\n                    pBufferOut[iSample] = -pBufferOut[iSample];\n                }\n            }\n        }\n        #endif\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);\n        #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT\n        {\n            if (pWav->container == ma_dr_wav_container_aiff) {\n                ma_uint64 iSample;\n                for (iSample = 0; iSample < samplesRead; iSample += 1) {\n                    pBufferOut[iSample] = -pBufferOut[iSample];\n                }\n            }\n        }\n        #endif\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)\n{\n    if (pWav == NULL || framesToRead == 0) {\n        return 0;\n    }\n    if (pBufferOut == NULL) {\n        return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n    if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) {\n        framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels;\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {\n        return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {\n        return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {\n        return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {\n        return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {\n        return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);\n    }\n    return 0;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)\n{\n    ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {\n        ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);\n    }\n    return framesRead;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)\n{\n    ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {\n        ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);\n    }\n    return framesRead;\n}\nMA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = (pIn[i] / 256.0f) * 2 - 1;\n    }\n#else\n    for (i = 0; i < sampleCount; ++i) {\n        float x = pIn[i];\n        x = x * 0.00784313725490196078f;\n        x = x - 1;\n        *pOut++ = x;\n    }\n#endif\n}\nMA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = pIn[i] * 0.000030517578125f;\n    }\n}\nMA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        double x;\n        ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) <<  8);\n        ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16);\n        ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24);\n        x = (double)((ma_int32)(a | b | c) >> 8);\n        *pOut++ = (float)(x * 0.00000011920928955078125);\n    }\n}\nMA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = (float)(pIn[i] / 2147483648.0);\n    }\n}\nMA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = (float)pIn[i];\n    }\n}\nMA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f;\n    }\n}\nMA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f;\n    }\n}\nMA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)\n{\n    unsigned int i;\n    if (bytesPerSample == 1) {\n        ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount);\n        return;\n    }\n    if (bytesPerSample == 2) {\n        ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount);\n        return;\n    }\n    if (bytesPerSample == 3) {\n        ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount);\n        return;\n    }\n    if (bytesPerSample == 4) {\n        for (i = 0; i < totalSampleCount; ++i) {\n           *pOut++ = ((const ma_int32*)pIn)[i];\n        }\n        return;\n    }\n    if (bytesPerSample > 8) {\n        MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));\n        return;\n    }\n    for (i = 0; i < totalSampleCount; ++i) {\n        ma_uint64 sample = 0;\n        unsigned int shift  = (8 - bytesPerSample) * 8;\n        unsigned int j;\n        for (j = 0; j < bytesPerSample; j += 1) {\n            MA_DR_WAV_ASSERT(j < 8);\n            sample |= (ma_uint64)(pIn[j]) << shift;\n            shift  += 8;\n        }\n        pIn += j;\n        *pOut++ = (ma_int32)((ma_int64)sample >> 32);\n    }\n}\nMA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)\n{\n    if (bytesPerSample == 4) {\n        ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);\n        return;\n    } else if (bytesPerSample == 8) {\n        ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);\n        return;\n    } else {\n        MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));\n        return;\n    }\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {\n        return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);\n    }\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)\n{\n    ma_uint64 totalFramesRead = 0;\n    ma_int16 samples16[2048];\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));\n        pBufferOut      += framesRead*pWav->channels;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);\n        #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT\n        {\n            if (pWav->container == ma_dr_wav_container_aiff) {\n                ma_uint64 iSample;\n                for (iSample = 0; iSample < samplesRead; iSample += 1) {\n                    pBufferOut[iSample] = -pBufferOut[iSample];\n                }\n            }\n        }\n        #endif\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)\n{\n    ma_uint64 totalFramesRead;\n    ma_uint8 sampleData[4096] = {0};\n    ma_uint32 bytesPerFrame;\n    ma_uint32 bytesPerSample;\n    ma_uint64 samplesRead;\n    bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);\n    if (bytesPerFrame == 0) {\n        return 0;\n    }\n    bytesPerSample = bytesPerFrame / pWav->channels;\n    if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {\n        return 0;\n    }\n    totalFramesRead = 0;\n    while (framesToRead > 0) {\n        ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);\n        ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);\n        if (framesRead == 0) {\n            break;\n        }\n        MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);\n        samplesRead = framesRead * pWav->channels;\n        if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {\n            MA_DR_WAV_ASSERT(MA_FALSE);\n            break;\n        }\n        ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);\n        #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT\n        {\n            if (pWav->container == ma_dr_wav_container_aiff) {\n                ma_uint64 iSample;\n                for (iSample = 0; iSample < samplesRead; iSample += 1) {\n                    pBufferOut[iSample] = -pBufferOut[iSample];\n                }\n            }\n        }\n        #endif\n        pBufferOut      += samplesRead;\n        framesToRead    -= framesRead;\n        totalFramesRead += framesRead;\n    }\n    return totalFramesRead;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)\n{\n    if (pWav == NULL || framesToRead == 0) {\n        return 0;\n    }\n    if (pBufferOut == NULL) {\n        return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);\n    }\n    if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) {\n        framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels;\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {\n        return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {\n        return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {\n        return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {\n        return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);\n    }\n    if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {\n        return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);\n    }\n    return 0;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)\n{\n    ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {\n        ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);\n    }\n    return framesRead;\n}\nMA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)\n{\n    ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);\n    if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {\n        ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);\n    }\n    return framesRead;\n}\nMA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = ((int)pIn[i] - 128) << 24;\n    }\n}\nMA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = pIn[i] << 16;\n    }\n}\nMA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        unsigned int s0 = pIn[i*3 + 0];\n        unsigned int s1 = pIn[i*3 + 1];\n        unsigned int s2 = pIn[i*3 + 2];\n        ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));\n        *pOut++ = sample32;\n    }\n}\nMA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = (ma_int32)(2147483648.0 * pIn[i]);\n    }\n}\nMA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = (ma_int32)(2147483648.0 * pIn[i]);\n    }\n}\nMA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i = 0; i < sampleCount; ++i) {\n        *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16;\n    }\n}\nMA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)\n{\n    size_t i;\n    if (pOut == NULL || pIn == NULL) {\n        return;\n    }\n    for (i= 0; i < sampleCount; ++i) {\n        *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16;\n    }\n}\nMA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount)\n{\n    ma_uint64 sampleDataSize;\n    ma_int16* pSampleData;\n    ma_uint64 framesRead;\n    MA_DR_WAV_ASSERT(pWav != NULL);\n    sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16);\n    if (sampleDataSize > MA_SIZE_MAX) {\n        ma_dr_wav_uninit(pWav);\n        return NULL;\n    }\n    pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);\n    if (pSampleData == NULL) {\n        ma_dr_wav_uninit(pWav);\n        return NULL;\n    }\n    framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);\n    if (framesRead != pWav->totalPCMFrameCount) {\n        ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);\n        ma_dr_wav_uninit(pWav);\n        return NULL;\n    }\n    ma_dr_wav_uninit(pWav);\n    if (sampleRate) {\n        *sampleRate = pWav->sampleRate;\n    }\n    if (channels) {\n        *channels = pWav->channels;\n    }\n    if (totalFrameCount) {\n        *totalFrameCount = pWav->totalPCMFrameCount;\n    }\n    return pSampleData;\n}\nMA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount)\n{\n    ma_uint64 sampleDataSize;\n    float* pSampleData;\n    ma_uint64 framesRead;\n    MA_DR_WAV_ASSERT(pWav != NULL);\n    sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);\n    if (sampleDataSize > MA_SIZE_MAX) {\n        ma_dr_wav_uninit(pWav);\n        return NULL;\n    }\n    pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);\n    if (pSampleData == NULL) {\n        ma_dr_wav_uninit(pWav);\n        return NULL;\n    }\n    framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);\n    if (framesRead != pWav->totalPCMFrameCount) {\n        ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);\n        ma_dr_wav_uninit(pWav);\n        return NULL;\n    }\n    ma_dr_wav_uninit(pWav);\n    if (sampleRate) {\n        *sampleRate = pWav->sampleRate;\n    }\n    if (channels) {\n        *channels = pWav->channels;\n    }\n    if (totalFrameCount) {\n        *totalFrameCount = pWav->totalPCMFrameCount;\n    }\n    return pSampleData;\n}\nMA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount)\n{\n    ma_uint64 sampleDataSize;\n    ma_int32* pSampleData;\n    ma_uint64 framesRead;\n    MA_DR_WAV_ASSERT(pWav != NULL);\n    sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32);\n    if (sampleDataSize > MA_SIZE_MAX) {\n        ma_dr_wav_uninit(pWav);\n        return NULL;\n    }\n    pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);\n    if (pSampleData == NULL) {\n        ma_dr_wav_uninit(pWav);\n        return NULL;\n    }\n    framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);\n    if (framesRead != pWav->totalPCMFrameCount) {\n        ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);\n        ma_dr_wav_uninit(pWav);\n        return NULL;\n    }\n    ma_dr_wav_uninit(pWav);\n    if (sampleRate) {\n        *sampleRate = pWav->sampleRate;\n    }\n    if (channels) {\n        *channels = pWav->channels;\n    }\n    if (totalFrameCount) {\n        *totalFrameCount = pWav->totalPCMFrameCount;\n    }\n    return pSampleData;\n}\nMA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\nMA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\nMA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n#ifndef MA_DR_WAV_NO_STDIO\nMA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\nMA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\nMA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n#ifndef MA_DR_WAV_NO_WCHAR\nMA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\nMA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\nMA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n#endif\n#endif\nMA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\nMA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\nMA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_wav wav;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalFrameCountOut) {\n        *totalFrameCountOut = 0;\n    }\n    if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);\n}\n#endif\nMA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks != NULL) {\n        ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks);\n    } else {\n        ma_dr_wav__free_default(p, NULL);\n    }\n}\nMA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data)\n{\n    return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8);\n}\nMA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data)\n{\n    return (ma_int16)ma_dr_wav_bytes_to_u16(data);\n}\nMA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data)\n{\n    return ma_dr_wav_bytes_to_u32_le(data);\n}\nMA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data)\n{\n    union {\n        ma_uint32 u32;\n        float f32;\n    } value;\n    value.u32 = ma_dr_wav_bytes_to_u32(data);\n    return value.f32;\n}\nMA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data)\n{\n    return (ma_int32)ma_dr_wav_bytes_to_u32(data);\n}\nMA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data)\n{\n    return\n        ((ma_uint64)data[0] <<  0) | ((ma_uint64)data[1] <<  8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) |\n        ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56);\n}\nMA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data)\n{\n    return (ma_int64)ma_dr_wav_bytes_to_u64(data);\n}\nMA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16])\n{\n    int i;\n    for (i = 0; i < 16; i += 1) {\n        if (a[i] != b[i]) {\n            return MA_FALSE;\n        }\n    }\n    return MA_TRUE;\n}\nMA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b)\n{\n    return\n        a[0] == b[0] &&\n        a[1] == b[1] &&\n        a[2] == b[2] &&\n        a[3] == b[3];\n}\n#ifdef __MRC__\n#pragma options opt reset\n#endif\n#endif\n/* dr_wav_c end */\n#endif  /* MA_DR_WAV_IMPLEMENTATION */\n#endif  /* MA_NO_WAV */\n\n#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)\n#if !defined(MA_DR_FLAC_IMPLEMENTATION) && !defined(MA_DR_FLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */\n/* dr_flac_c begin */\n#ifndef ma_dr_flac_c\n#define ma_dr_flac_c\n#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n    #pragma GCC diagnostic push\n    #if __GNUC__ >= 7\n    #pragma GCC diagnostic ignored \"-Wimplicit-fallthrough\"\n    #endif\n#endif\n#ifdef __linux__\n    #ifndef _BSD_SOURCE\n        #define _BSD_SOURCE\n    #endif\n    #ifndef _DEFAULT_SOURCE\n        #define _DEFAULT_SOURCE\n    #endif\n    #ifndef __USE_BSD\n        #define __USE_BSD\n    #endif\n    #include <endian.h>\n#endif\n#include <stdlib.h>\n#include <string.h>\n#if !defined(MA_DR_FLAC_NO_SIMD)\n    #if defined(MA_X64) || defined(MA_X86)\n        #if defined(_MSC_VER) && !defined(__clang__)\n            #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2)\n                #define MA_DR_FLAC_SUPPORT_SSE2\n            #endif\n            #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41)\n                #define MA_DR_FLAC_SUPPORT_SSE41\n            #endif\n        #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))\n            #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2)\n                #define MA_DR_FLAC_SUPPORT_SSE2\n            #endif\n            #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41)\n                #define MA_DR_FLAC_SUPPORT_SSE41\n            #endif\n        #endif\n        #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)\n            #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include(<emmintrin.h>)\n                #define MA_DR_FLAC_SUPPORT_SSE2\n            #endif\n            #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include(<smmintrin.h>)\n                #define MA_DR_FLAC_SUPPORT_SSE41\n            #endif\n        #endif\n        #if defined(MA_DR_FLAC_SUPPORT_SSE41)\n            #include <smmintrin.h>\n        #elif defined(MA_DR_FLAC_SUPPORT_SSE2)\n            #include <emmintrin.h>\n        #endif\n    #endif\n    #if defined(MA_ARM)\n        #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))\n            #define MA_DR_FLAC_SUPPORT_NEON\n            #include <arm_neon.h>\n        #endif\n    #endif\n#endif\n#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64))\n    #if defined(_MSC_VER) && !defined(__clang__)\n        #if _MSC_VER >= 1400\n            #include <intrin.h>\n            static void ma_dr_flac__cpuid(int info[4], int fid)\n            {\n                __cpuid(info, fid);\n            }\n        #else\n            #define MA_DR_FLAC_NO_CPUID\n        #endif\n    #else\n        #if defined(__GNUC__) || defined(__clang__)\n            static void ma_dr_flac__cpuid(int info[4], int fid)\n            {\n                #if defined(MA_X86) && defined(__PIC__)\n                    __asm__ __volatile__ (\n                        \"xchg{l} {%%}ebx, %k1;\"\n                        \"cpuid;\"\n                        \"xchg{l} {%%}ebx, %k1;\"\n                        : \"=a\"(info[0]), \"=&r\"(info[1]), \"=c\"(info[2]), \"=d\"(info[3]) : \"a\"(fid), \"c\"(0)\n                    );\n                #else\n                    __asm__ __volatile__ (\n                        \"cpuid\" : \"=a\"(info[0]), \"=b\"(info[1]), \"=c\"(info[2]), \"=d\"(info[3]) : \"a\"(fid), \"c\"(0)\n                    );\n                #endif\n            }\n        #else\n            #define MA_DR_FLAC_NO_CPUID\n        #endif\n    #endif\n#else\n    #define MA_DR_FLAC_NO_CPUID\n#endif\nstatic MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2)\n        #if defined(MA_X64)\n            return MA_TRUE;\n        #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)\n            return MA_TRUE;\n        #else\n            #if defined(MA_DR_FLAC_NO_CPUID)\n                return MA_FALSE;\n            #else\n                int info[4];\n                ma_dr_flac__cpuid(info, 1);\n                return (info[3] & (1 << 26)) != 0;\n            #endif\n        #endif\n    #else\n        return MA_FALSE;\n    #endif\n#else\n    return MA_FALSE;\n#endif\n}\nstatic MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE41)\n    #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41)\n        #if defined(__SSE4_1__) || defined(__AVX__)\n            return MA_TRUE;\n        #else\n            #if defined(MA_DR_FLAC_NO_CPUID)\n                return MA_FALSE;\n            #else\n                int info[4];\n                ma_dr_flac__cpuid(info, 1);\n                return (info[2] & (1 << 19)) != 0;\n            #endif\n        #endif\n    #else\n        return MA_FALSE;\n    #endif\n#else\n    return MA_FALSE;\n#endif\n}\n#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__)\n    #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC\n#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))\n    #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC\n#elif defined(__clang__)\n    #if defined(__has_builtin)\n        #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)\n            #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC\n        #endif\n    #endif\n#endif\n#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__)\n    #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC\n    #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC\n    #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC\n#elif defined(__clang__)\n    #if defined(__has_builtin)\n        #if __has_builtin(__builtin_bswap16)\n            #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC\n        #endif\n        #if __has_builtin(__builtin_bswap32)\n            #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC\n        #endif\n        #if __has_builtin(__builtin_bswap64)\n            #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC\n        #endif\n    #endif\n#elif defined(__GNUC__)\n    #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))\n        #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC\n        #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC\n    #endif\n    #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))\n        #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC\n    #endif\n#elif defined(__WATCOMC__) && defined(__386__)\n    #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC\n    #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC\n    #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC\n    extern __inline ma_uint16 _watcom_bswap16(ma_uint16);\n    extern __inline ma_uint32 _watcom_bswap32(ma_uint32);\n    extern __inline ma_uint64 _watcom_bswap64(ma_uint64);\n#pragma aux _watcom_bswap16 = \\\n    \"xchg al, ah\" \\\n    parm  [ax]    \\\n    value [ax]    \\\n    modify nomemory;\n#pragma aux _watcom_bswap32 = \\\n    \"bswap eax\" \\\n    parm  [eax] \\\n    value [eax] \\\n    modify nomemory;\n#pragma aux _watcom_bswap64 = \\\n    \"bswap eax\"     \\\n    \"bswap edx\"     \\\n    \"xchg eax,edx\"  \\\n    parm [eax edx]  \\\n    value [eax edx] \\\n    modify nomemory;\n#endif\n#ifndef MA_DR_FLAC_ASSERT\n#include <assert.h>\n#define MA_DR_FLAC_ASSERT(expression)           assert(expression)\n#endif\n#ifndef MA_DR_FLAC_MALLOC\n#define MA_DR_FLAC_MALLOC(sz)                   malloc((sz))\n#endif\n#ifndef MA_DR_FLAC_REALLOC\n#define MA_DR_FLAC_REALLOC(p, sz)               realloc((p), (sz))\n#endif\n#ifndef MA_DR_FLAC_FREE\n#define MA_DR_FLAC_FREE(p)                      free((p))\n#endif\n#ifndef MA_DR_FLAC_COPY_MEMORY\n#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz)    memcpy((dst), (src), (sz))\n#endif\n#ifndef MA_DR_FLAC_ZERO_MEMORY\n#define MA_DR_FLAC_ZERO_MEMORY(p, sz)           memset((p), 0, (sz))\n#endif\n#ifndef MA_DR_FLAC_ZERO_OBJECT\n#define MA_DR_FLAC_ZERO_OBJECT(p)               MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p)))\n#endif\n#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE                     64\n#define MA_DR_FLAC_SUBFRAME_CONSTANT                        0\n#define MA_DR_FLAC_SUBFRAME_VERBATIM                        1\n#define MA_DR_FLAC_SUBFRAME_FIXED                           8\n#define MA_DR_FLAC_SUBFRAME_LPC                             32\n#define MA_DR_FLAC_SUBFRAME_RESERVED                        255\n#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE  0\n#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1\n#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT           0\n#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE             8\n#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE            9\n#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE              10\n#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES                  18\n#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES             36\n#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES       12\n#define ma_dr_flac_align(x, a)                              ((((x) + (a) - 1) / (a)) * (a))\nMA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)\n{\n    if (pMajor) {\n        *pMajor = MA_DR_FLAC_VERSION_MAJOR;\n    }\n    if (pMinor) {\n        *pMinor = MA_DR_FLAC_VERSION_MINOR;\n    }\n    if (pRevision) {\n        *pRevision = MA_DR_FLAC_VERSION_REVISION;\n    }\n}\nMA_API const char* ma_dr_flac_version_string(void)\n{\n    return MA_DR_FLAC_VERSION_STRING;\n}\n#if defined(__has_feature)\n    #if __has_feature(thread_sanitizer)\n        #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize(\"thread\")))\n    #else\n        #define MA_DR_FLAC_NO_THREAD_SANITIZE\n    #endif\n#else\n    #define MA_DR_FLAC_NO_THREAD_SANITIZE\n#endif\n#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)\nstatic ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE;\n#endif\n#ifndef MA_DR_FLAC_NO_CPUID\nstatic ma_bool32 ma_dr_flac__gIsSSE2Supported  = MA_FALSE;\nstatic ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE;\nMA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void)\n{\n    static ma_bool32 isCPUCapsInitialized = MA_FALSE;\n    if (!isCPUCapsInitialized) {\n#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)\n        int info[4] = {0};\n        ma_dr_flac__cpuid(info, 0x80000001);\n        ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;\n#endif\n        ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2();\n        ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41();\n        isCPUCapsInitialized = MA_TRUE;\n    }\n}\n#else\nstatic ma_bool32 ma_dr_flac__gIsNEONSupported  = MA_FALSE;\nstatic MA_INLINE ma_bool32 ma_dr_flac__has_neon(void)\n{\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\n    #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON)\n        #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))\n            return MA_TRUE;\n        #else\n            return MA_FALSE;\n        #endif\n    #else\n        return MA_FALSE;\n    #endif\n#else\n    return MA_FALSE;\n#endif\n}\nMA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void)\n{\n    ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon();\n#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)\n    ma_dr_flac__gIsLZCNTSupported = MA_TRUE;\n#endif\n}\n#endif\nstatic MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void)\n{\n#if defined(MA_X86) || defined(MA_X64)\n    return MA_TRUE;\n#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN\n    return MA_TRUE;\n#else\n    int n = 1;\n    return (*(char*)&n) == 1;\n#endif\n}\nstatic MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n)\n{\n#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC\n    #if defined(_MSC_VER) && !defined(__clang__)\n        return _byteswap_ushort(n);\n    #elif defined(__GNUC__) || defined(__clang__)\n        return __builtin_bswap16(n);\n    #elif defined(__WATCOMC__) && defined(__386__)\n        return _watcom_bswap16(n);\n    #else\n        #error \"This compiler does not support the byte swap intrinsic.\"\n    #endif\n#else\n    return ((n & 0xFF00) >> 8) |\n           ((n & 0x00FF) << 8);\n#endif\n}\nstatic MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n)\n{\n#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC\n    #if defined(_MSC_VER) && !defined(__clang__)\n        return _byteswap_ulong(n);\n    #elif defined(__GNUC__) || defined(__clang__)\n        #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT)\n            ma_uint32 r;\n            __asm__ __volatile__ (\n            #if defined(MA_64BIT)\n                \"rev %w[out], %w[in]\" : [out]\"=r\"(r) : [in]\"r\"(n)\n            #else\n                \"rev %[out], %[in]\" : [out]\"=r\"(r) : [in]\"r\"(n)\n            #endif\n            );\n            return r;\n        #else\n            return __builtin_bswap32(n);\n        #endif\n    #elif defined(__WATCOMC__) && defined(__386__)\n        return _watcom_bswap32(n);\n    #else\n        #error \"This compiler does not support the byte swap intrinsic.\"\n    #endif\n#else\n    return ((n & 0xFF000000) >> 24) |\n           ((n & 0x00FF0000) >>  8) |\n           ((n & 0x0000FF00) <<  8) |\n           ((n & 0x000000FF) << 24);\n#endif\n}\nstatic MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n)\n{\n#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC\n    #if defined(_MSC_VER) && !defined(__clang__)\n        return _byteswap_uint64(n);\n    #elif defined(__GNUC__) || defined(__clang__)\n        return __builtin_bswap64(n);\n    #elif defined(__WATCOMC__) && defined(__386__)\n        return _watcom_bswap64(n);\n    #else\n        #error \"This compiler does not support the byte swap intrinsic.\"\n    #endif\n#else\n    return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) |\n           ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) |\n           ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) |\n           ((n & ((ma_uint64)0x000000FF << 32)) >>  8) |\n           ((n & ((ma_uint64)0xFF000000      )) <<  8) |\n           ((n & ((ma_uint64)0x00FF0000      )) << 24) |\n           ((n & ((ma_uint64)0x0000FF00      )) << 40) |\n           ((n & ((ma_uint64)0x000000FF      )) << 56);\n#endif\n}\nstatic MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n)\n{\n    if (ma_dr_flac__is_little_endian()) {\n        return ma_dr_flac__swap_endian_uint16(n);\n    }\n    return n;\n}\nstatic MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n)\n{\n    if (ma_dr_flac__is_little_endian()) {\n        return ma_dr_flac__swap_endian_uint32(n);\n    }\n    return n;\n}\nstatic MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData)\n{\n    const ma_uint8* pNum = (ma_uint8*)pData;\n    return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3);\n}\nstatic MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n)\n{\n    if (ma_dr_flac__is_little_endian()) {\n        return ma_dr_flac__swap_endian_uint64(n);\n    }\n    return n;\n}\nstatic MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n)\n{\n    if (!ma_dr_flac__is_little_endian()) {\n        return ma_dr_flac__swap_endian_uint32(n);\n    }\n    return n;\n}\nstatic MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData)\n{\n    const ma_uint8* pNum = (ma_uint8*)pData;\n    return *pNum | *(pNum+1) << 8 |  *(pNum+2) << 16 | *(pNum+3) << 24;\n}\nstatic MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n)\n{\n    ma_uint32 result = 0;\n    result |= (n & 0x7F000000) >> 3;\n    result |= (n & 0x007F0000) >> 2;\n    result |= (n & 0x00007F00) >> 1;\n    result |= (n & 0x0000007F) >> 0;\n    return result;\n}\nstatic ma_uint8 ma_dr_flac__crc8_table[] = {\n    0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,\n    0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,\n    0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,\n    0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,\n    0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,\n    0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,\n    0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,\n    0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,\n    0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,\n    0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,\n    0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,\n    0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,\n    0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,\n    0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,\n    0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,\n    0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3\n};\nstatic ma_uint16 ma_dr_flac__crc16_table[] = {\n    0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,\n    0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,\n    0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,\n    0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,\n    0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,\n    0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,\n    0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,\n    0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,\n    0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,\n    0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,\n    0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,\n    0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,\n    0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,\n    0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,\n    0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,\n    0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,\n    0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,\n    0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,\n    0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,\n    0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,\n    0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,\n    0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,\n    0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,\n    0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,\n    0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,\n    0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,\n    0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,\n    0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,\n    0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,\n    0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,\n    0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,\n    0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202\n};\nstatic MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data)\n{\n    return ma_dr_flac__crc8_table[crc ^ data];\n}\nstatic MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count)\n{\n#ifdef MA_DR_FLAC_NO_CRC\n    (void)crc;\n    (void)data;\n    (void)count;\n    return 0;\n#else\n#if 0\n    ma_uint8 p = 0x07;\n    for (int i = count-1; i >= 0; --i) {\n        ma_uint8 bit = (data & (1 << i)) >> i;\n        if (crc & 0x80) {\n            crc = ((crc << 1) | bit) ^ p;\n        } else {\n            crc = ((crc << 1) | bit);\n        }\n    }\n    return crc;\n#else\n    ma_uint32 wholeBytes;\n    ma_uint32 leftoverBits;\n    ma_uint64 leftoverDataMask;\n    static ma_uint64 leftoverDataMaskTable[8] = {\n        0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F\n    };\n    MA_DR_FLAC_ASSERT(count <= 32);\n    wholeBytes = count >> 3;\n    leftoverBits = count - (wholeBytes*8);\n    leftoverDataMask = leftoverDataMaskTable[leftoverBits];\n    switch (wholeBytes) {\n        case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));\n        case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));\n        case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));\n        case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));\n        case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]);\n    }\n    return crc;\n#endif\n#endif\n}\nstatic MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data)\n{\n    return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data];\n}\nstatic MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data)\n{\n#ifdef MA_64BIT\n    crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF));\n    crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF));\n    crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF));\n    crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF));\n#endif\n    crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF));\n    crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF));\n    crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >>  8) & 0xFF));\n    crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >>  0) & 0xFF));\n    return crc;\n}\nstatic MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount)\n{\n    switch (byteCount)\n    {\n#ifdef MA_64BIT\n    case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF));\n    case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF));\n    case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF));\n    case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF));\n#endif\n    case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF));\n    case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF));\n    case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >>  8) & 0xFF));\n    case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >>  0) & 0xFF));\n    }\n    return crc;\n}\n#if 0\nstatic MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count)\n{\n#ifdef MA_DR_FLAC_NO_CRC\n    (void)crc;\n    (void)data;\n    (void)count;\n    return 0;\n#else\n#if 0\n    ma_uint16 p = 0x8005;\n    for (int i = count-1; i >= 0; --i) {\n        ma_uint16 bit = (data & (1ULL << i)) >> i;\n        if (r & 0x8000) {\n            r = ((r << 1) | bit) ^ p;\n        } else {\n            r = ((r << 1) | bit);\n        }\n    }\n    return crc;\n#else\n    ma_uint32 wholeBytes;\n    ma_uint32 leftoverBits;\n    ma_uint64 leftoverDataMask;\n    static ma_uint64 leftoverDataMaskTable[8] = {\n        0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F\n    };\n    MA_DR_FLAC_ASSERT(count <= 64);\n    wholeBytes = count >> 3;\n    leftoverBits = count & 7;\n    leftoverDataMask = leftoverDataMaskTable[leftoverBits];\n    switch (wholeBytes) {\n        default:\n        case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));\n        case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));\n        case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));\n        case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));\n        case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];\n    }\n    return crc;\n#endif\n#endif\n}\nstatic MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count)\n{\n#ifdef MA_DR_FLAC_NO_CRC\n    (void)crc;\n    (void)data;\n    (void)count;\n    return 0;\n#else\n    ma_uint32 wholeBytes;\n    ma_uint32 leftoverBits;\n    ma_uint64 leftoverDataMask;\n    static ma_uint64 leftoverDataMaskTable[8] = {\n        0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F\n    };\n    MA_DR_FLAC_ASSERT(count <= 64);\n    wholeBytes = count >> 3;\n    leftoverBits = count & 7;\n    leftoverDataMask = leftoverDataMaskTable[leftoverBits];\n    switch (wholeBytes) {\n        default:\n        case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits)));\n        case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits)));\n        case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits)));\n        case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits)));\n        case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000      ) << leftoverBits)) >> (24 + leftoverBits)));\n        case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000      ) << leftoverBits)) >> (16 + leftoverBits)));\n        case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00      ) << leftoverBits)) >> ( 8 + leftoverBits)));\n        case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF      ) << leftoverBits)) >> ( 0 + leftoverBits)));\n        case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];\n    }\n    return crc;\n#endif\n}\nstatic MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count)\n{\n#ifdef MA_64BIT\n    return ma_dr_flac_crc16__64bit(crc, data, count);\n#else\n    return ma_dr_flac_crc16__32bit(crc, data, count);\n#endif\n}\n#endif\n#ifdef MA_64BIT\n#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64\n#else\n#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32\n#endif\n#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)                      (sizeof((bs)->cache))\n#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)                       (sizeof((bs)->cache)*8)\n#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)                  (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits)\n#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount)           (~((~(ma_dr_flac_cache_t)0) >> (_bitCount)))\n#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount)      (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))\n#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount)               (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount))\n#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount)     (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >>  MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)))\n#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1)))\n#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)                      (sizeof((bs)->cacheL2))\n#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)                      (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))\n#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs)                 (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)\n#ifndef MA_DR_FLAC_NO_CRC\nstatic MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs)\n{\n    bs->crc16 = 0;\n    bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;\n}\nstatic MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs)\n{\n    if (bs->crc16CacheIgnoredBytes == 0) {\n        bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache);\n    } else {\n        bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes);\n        bs->crc16CacheIgnoredBytes = 0;\n    }\n}\nstatic MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs)\n{\n    MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0);\n    if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) {\n        ma_dr_flac__update_crc16(bs);\n    } else {\n        bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes);\n        bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;\n    }\n    return bs->crc16;\n}\n#endif\nstatic MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs)\n{\n    size_t bytesRead;\n    size_t alignedL1LineCount;\n    if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {\n        bs->cache = bs->cacheL2[bs->nextL2Line++];\n        return MA_TRUE;\n    }\n    if (bs->unalignedByteCount > 0) {\n        return MA_FALSE;\n    }\n    bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs));\n    bs->nextL2Line = 0;\n    if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) {\n        bs->cache = bs->cacheL2[bs->nextL2Line++];\n        return MA_TRUE;\n    }\n    alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs);\n    bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs));\n    if (bs->unalignedByteCount > 0) {\n        bs->unalignedCache = bs->cacheL2[alignedL1LineCount];\n    }\n    if (alignedL1LineCount > 0) {\n        size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;\n        size_t i;\n        for (i = alignedL1LineCount; i > 0; --i) {\n            bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];\n        }\n        bs->nextL2Line = (ma_uint32)offset;\n        bs->cache = bs->cacheL2[bs->nextL2Line++];\n        return MA_TRUE;\n    } else {\n        bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs);\n        return MA_FALSE;\n    }\n}\nstatic ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs)\n{\n    size_t bytesRead;\n#ifndef MA_DR_FLAC_NO_CRC\n    ma_dr_flac__update_crc16(bs);\n#endif\n    if (ma_dr_flac__reload_l1_cache_from_l2(bs)) {\n        bs->cache = ma_dr_flac__be2host__cache_line(bs->cache);\n        bs->consumedBits = 0;\n#ifndef MA_DR_FLAC_NO_CRC\n        bs->crc16Cache = bs->cache;\n#endif\n        return MA_TRUE;\n    }\n    bytesRead = bs->unalignedByteCount;\n    if (bytesRead == 0) {\n        bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);\n        return MA_FALSE;\n    }\n    MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs));\n    bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;\n    bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache);\n    bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs));\n    bs->unalignedByteCount = 0;\n#ifndef MA_DR_FLAC_NO_CRC\n    bs->crc16Cache = bs->cache >> bs->consumedBits;\n    bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;\n#endif\n    return MA_TRUE;\n}\nstatic void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs)\n{\n    bs->nextL2Line   = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs);\n    bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);\n    bs->cache = 0;\n    bs->unalignedByteCount = 0;\n    bs->unalignedCache = 0;\n#ifndef MA_DR_FLAC_NO_CRC\n    bs->crc16Cache = 0;\n    bs->crc16CacheIgnoredBytes = 0;\n#endif\n}\nstatic MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut)\n{\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pResultOut != NULL);\n    MA_DR_FLAC_ASSERT(bitCount > 0);\n    MA_DR_FLAC_ASSERT(bitCount <= 32);\n    if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {\n        if (!ma_dr_flac__reload_cache(bs)) {\n            return MA_FALSE;\n        }\n    }\n    if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {\n#ifdef MA_64BIT\n        *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);\n        bs->consumedBits += bitCount;\n        bs->cache <<= bitCount;\n#else\n        if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {\n            *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);\n            bs->consumedBits += bitCount;\n            bs->cache <<= bitCount;\n        } else {\n            *pResultOut = (ma_uint32)bs->cache;\n            bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);\n            bs->cache = 0;\n        }\n#endif\n        return MA_TRUE;\n    } else {\n        ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);\n        ma_uint32 bitCountLo = bitCount - bitCountHi;\n        ma_uint32 resultHi;\n        MA_DR_FLAC_ASSERT(bitCountHi > 0);\n        MA_DR_FLAC_ASSERT(bitCountHi < 32);\n        resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);\n        if (!ma_dr_flac__reload_cache(bs)) {\n            return MA_FALSE;\n        }\n        if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {\n            return MA_FALSE;\n        }\n        *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);\n        bs->consumedBits += bitCountLo;\n        bs->cache <<= bitCountLo;\n        return MA_TRUE;\n    }\n}\nstatic ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult)\n{\n    ma_uint32 result;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pResult != NULL);\n    MA_DR_FLAC_ASSERT(bitCount > 0);\n    MA_DR_FLAC_ASSERT(bitCount <= 32);\n    if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {\n        return MA_FALSE;\n    }\n    if (bitCount < 32) {\n        ma_uint32 signbit;\n        signbit = ((result >> (bitCount-1)) & 0x01);\n        result |= (~signbit + 1) << bitCount;\n    }\n    *pResult = (ma_int32)result;\n    return MA_TRUE;\n}\n#ifdef MA_64BIT\nstatic ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut)\n{\n    ma_uint32 resultHi;\n    ma_uint32 resultLo;\n    MA_DR_FLAC_ASSERT(bitCount <= 64);\n    MA_DR_FLAC_ASSERT(bitCount >  32);\n    if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) {\n        return MA_FALSE;\n    }\n    if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) {\n        return MA_FALSE;\n    }\n    *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo);\n    return MA_TRUE;\n}\n#endif\n#if 0\nstatic ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut)\n{\n    ma_uint64 result;\n    ma_uint64 signbit;\n    MA_DR_FLAC_ASSERT(bitCount <= 64);\n    if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) {\n        return MA_FALSE;\n    }\n    signbit = ((result >> (bitCount-1)) & 0x01);\n    result |= (~signbit + 1) << bitCount;\n    *pResultOut = (ma_int64)result;\n    return MA_TRUE;\n}\n#endif\nstatic ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult)\n{\n    ma_uint32 result;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pResult != NULL);\n    MA_DR_FLAC_ASSERT(bitCount > 0);\n    MA_DR_FLAC_ASSERT(bitCount <= 16);\n    if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {\n        return MA_FALSE;\n    }\n    *pResult = (ma_uint16)result;\n    return MA_TRUE;\n}\n#if 0\nstatic ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult)\n{\n    ma_int32 result;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pResult != NULL);\n    MA_DR_FLAC_ASSERT(bitCount > 0);\n    MA_DR_FLAC_ASSERT(bitCount <= 16);\n    if (!ma_dr_flac__read_int32(bs, bitCount, &result)) {\n        return MA_FALSE;\n    }\n    *pResult = (ma_int16)result;\n    return MA_TRUE;\n}\n#endif\nstatic ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult)\n{\n    ma_uint32 result;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pResult != NULL);\n    MA_DR_FLAC_ASSERT(bitCount > 0);\n    MA_DR_FLAC_ASSERT(bitCount <= 8);\n    if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {\n        return MA_FALSE;\n    }\n    *pResult = (ma_uint8)result;\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult)\n{\n    ma_int32 result;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pResult != NULL);\n    MA_DR_FLAC_ASSERT(bitCount > 0);\n    MA_DR_FLAC_ASSERT(bitCount <= 8);\n    if (!ma_dr_flac__read_int32(bs, bitCount, &result)) {\n        return MA_FALSE;\n    }\n    *pResult = (ma_int8)result;\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek)\n{\n    if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {\n        bs->consumedBits += (ma_uint32)bitsToSeek;\n        bs->cache <<= bitsToSeek;\n        return MA_TRUE;\n    } else {\n        bitsToSeek       -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);\n        bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);\n        bs->cache         = 0;\n#ifdef MA_64BIT\n        while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {\n            ma_uint64 bin;\n            if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {\n                return MA_FALSE;\n            }\n            bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);\n        }\n#else\n        while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {\n            ma_uint32 bin;\n            if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {\n                return MA_FALSE;\n            }\n            bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);\n        }\n#endif\n        while (bitsToSeek >= 8) {\n            ma_uint8 bin;\n            if (!ma_dr_flac__read_uint8(bs, 8, &bin)) {\n                return MA_FALSE;\n            }\n            bitsToSeek -= 8;\n        }\n        if (bitsToSeek > 0) {\n            ma_uint8 bin;\n            if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) {\n                return MA_FALSE;\n            }\n            bitsToSeek = 0;\n        }\n        MA_DR_FLAC_ASSERT(bitsToSeek == 0);\n        return MA_TRUE;\n    }\n}\nstatic ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs)\n{\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {\n        return MA_FALSE;\n    }\n    for (;;) {\n        ma_uint8 hi;\n#ifndef MA_DR_FLAC_NO_CRC\n        ma_dr_flac__reset_crc16(bs);\n#endif\n        if (!ma_dr_flac__read_uint8(bs, 8, &hi)) {\n            return MA_FALSE;\n        }\n        if (hi == 0xFF) {\n            ma_uint8 lo;\n            if (!ma_dr_flac__read_uint8(bs, 6, &lo)) {\n                return MA_FALSE;\n            }\n            if (lo == 0x3E) {\n                return MA_TRUE;\n            } else {\n                if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {\n                    return MA_FALSE;\n                }\n            }\n        }\n    }\n}\n#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)\n#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT\n#endif\n#if  defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__)\n#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC\n#endif\n#if  defined(__WATCOMC__) && defined(__386__)\n#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM\n#endif\n#ifdef __MRC__\n#include <intrinsics.h>\n#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC\n#endif\nstatic MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x)\n{\n    ma_uint32 n;\n    static ma_uint32 clz_table_4[] = {\n        0,\n        4,\n        3, 3,\n        2, 2, 2, 2,\n        1, 1, 1, 1, 1, 1, 1, 1\n    };\n    if (x == 0) {\n        return sizeof(x)*8;\n    }\n    n = clz_table_4[x >> (sizeof(x)*8 - 4)];\n    if (n == 0) {\n#ifdef MA_64BIT\n        if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n  = 32; x <<= 32; }\n        if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; }\n        if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8;  x <<= 8;  }\n        if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4;  x <<= 4;  }\n#else\n        if ((x & 0xFFFF0000) == 0) { n  = 16; x <<= 16; }\n        if ((x & 0xFF000000) == 0) { n += 8;  x <<= 8;  }\n        if ((x & 0xF0000000) == 0) { n += 4;  x <<= 4;  }\n#endif\n        n += clz_table_4[x >> (sizeof(x)*8 - 4)];\n    }\n    return n - 1;\n}\n#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT\nstatic MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void)\n{\n#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)\n    return MA_TRUE;\n#elif defined(__MRC__)\n    return MA_TRUE;\n#else\n    #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC\n        return ma_dr_flac__gIsLZCNTSupported;\n    #else\n        return MA_FALSE;\n    #endif\n#endif\n}\nstatic MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x)\n{\n#if defined(_MSC_VER)\n    #ifdef MA_64BIT\n        return (ma_uint32)__lzcnt64(x);\n    #else\n        return (ma_uint32)__lzcnt(x);\n    #endif\n#else\n    #if defined(__GNUC__) || defined(__clang__)\n        #if defined(MA_X64)\n            {\n                ma_uint64 r;\n                __asm__ __volatile__ (\n                    \"lzcnt{ %1, %0| %0, %1}\" : \"=r\"(r) : \"r\"(x) : \"cc\"\n                );\n                return (ma_uint32)r;\n            }\n        #elif defined(MA_X86)\n            {\n                ma_uint32 r;\n                __asm__ __volatile__ (\n                    \"lzcnt{l %1, %0| %0, %1}\" : \"=r\"(r) : \"r\"(x) : \"cc\"\n                );\n                return r;\n            }\n        #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT)\n            {\n                unsigned int r;\n                __asm__ __volatile__ (\n                #if defined(MA_64BIT)\n                    \"clz %w[out], %w[in]\" : [out]\"=r\"(r) : [in]\"r\"(x)\n                #else\n                    \"clz %[out], %[in]\" : [out]\"=r\"(r) : [in]\"r\"(x)\n                #endif\n                );\n                return r;\n            }\n        #else\n            if (x == 0) {\n                return sizeof(x)*8;\n            }\n            #ifdef MA_64BIT\n                return (ma_uint32)__builtin_clzll((ma_uint64)x);\n            #else\n                return (ma_uint32)__builtin_clzl((ma_uint32)x);\n            #endif\n        #endif\n    #else\n        #error \"This compiler does not support the lzcnt intrinsic.\"\n    #endif\n#endif\n}\n#endif\n#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC\n#include <intrin.h>\nstatic MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x)\n{\n    ma_uint32 n;\n    if (x == 0) {\n        return sizeof(x)*8;\n    }\n#ifdef MA_64BIT\n    _BitScanReverse64((unsigned long*)&n, x);\n#else\n    _BitScanReverse((unsigned long*)&n, x);\n#endif\n    return sizeof(x)*8 - n - 1;\n}\n#endif\n#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM\nstatic __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32);\n#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT\n#pragma aux ma_dr_flac__clz_watcom_lzcnt = \\\n    \"db 0F3h, 0Fh, 0BDh, 0C0h\"  \\\n    parm [eax] \\\n    value [eax] \\\n    modify nomemory;\n#else\n#pragma aux ma_dr_flac__clz_watcom = \\\n    \"bsr eax, eax\" \\\n    \"xor eax, 31\" \\\n    parm [eax] nomemory \\\n    value [eax] \\\n    modify exact [eax] nomemory;\n#endif\n#endif\nstatic MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x)\n{\n#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT\n    if (ma_dr_flac__is_lzcnt_supported()) {\n        return ma_dr_flac__clz_lzcnt(x);\n    } else\n#endif\n    {\n#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC\n        return ma_dr_flac__clz_msvc(x);\n#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT)\n        return ma_dr_flac__clz_watcom_lzcnt(x);\n#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM)\n        return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x);\n#elif defined(__MRC__)\n        return __cntlzw(x);\n#else\n        return ma_dr_flac__clz_software(x);\n#endif\n    }\n}\nstatic MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut)\n{\n    ma_uint32 zeroCounter = 0;\n    ma_uint32 setBitOffsetPlus1;\n    while (bs->cache == 0) {\n        zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);\n        if (!ma_dr_flac__reload_cache(bs)) {\n            return MA_FALSE;\n        }\n    }\n    if (bs->cache == 1) {\n        *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1;\n        if (!ma_dr_flac__reload_cache(bs)) {\n            return MA_FALSE;\n        }\n        return MA_TRUE;\n    }\n    setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache);\n    setBitOffsetPlus1 += 1;\n    if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {\n        return MA_FALSE;\n    }\n    bs->consumedBits += setBitOffsetPlus1;\n    bs->cache <<= setBitOffsetPlus1;\n    *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart)\n{\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(offsetFromStart > 0);\n    if (offsetFromStart > 0x7FFFFFFF) {\n        ma_uint64 bytesRemaining = offsetFromStart;\n        if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) {\n            return MA_FALSE;\n        }\n        bytesRemaining -= 0x7FFFFFFF;\n        while (bytesRemaining > 0x7FFFFFFF) {\n            if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) {\n                return MA_FALSE;\n            }\n            bytesRemaining -= 0x7FFFFFFF;\n        }\n        if (bytesRemaining > 0) {\n            if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) {\n                return MA_FALSE;\n            }\n        }\n    } else {\n        if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) {\n            return MA_FALSE;\n        }\n    }\n    ma_dr_flac__reset_cache(bs);\n    return MA_TRUE;\n}\nstatic ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut)\n{\n    ma_uint8 crc;\n    ma_uint64 result;\n    ma_uint8 utf8[7] = {0};\n    int byteCount;\n    int i;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pNumberOut != NULL);\n    MA_DR_FLAC_ASSERT(pCRCOut != NULL);\n    crc = *pCRCOut;\n    if (!ma_dr_flac__read_uint8(bs, 8, utf8)) {\n        *pNumberOut = 0;\n        return MA_AT_END;\n    }\n    crc = ma_dr_flac_crc8(crc, utf8[0], 8);\n    if ((utf8[0] & 0x80) == 0) {\n        *pNumberOut = utf8[0];\n        *pCRCOut = crc;\n        return MA_SUCCESS;\n    }\n    if ((utf8[0] & 0xE0) == 0xC0) {\n        byteCount = 2;\n    } else if ((utf8[0] & 0xF0) == 0xE0) {\n        byteCount = 3;\n    } else if ((utf8[0] & 0xF8) == 0xF0) {\n        byteCount = 4;\n    } else if ((utf8[0] & 0xFC) == 0xF8) {\n        byteCount = 5;\n    } else if ((utf8[0] & 0xFE) == 0xFC) {\n        byteCount = 6;\n    } else if ((utf8[0] & 0xFF) == 0xFE) {\n        byteCount = 7;\n    } else {\n        *pNumberOut = 0;\n        return MA_CRC_MISMATCH;\n    }\n    MA_DR_FLAC_ASSERT(byteCount > 1);\n    result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));\n    for (i = 1; i < byteCount; ++i) {\n        if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) {\n            *pNumberOut = 0;\n            return MA_AT_END;\n        }\n        crc = ma_dr_flac_crc8(crc, utf8[i], 8);\n        result = (result << 6) | (utf8[i] & 0x3F);\n    }\n    *pNumberOut = result;\n    *pCRCOut = crc;\n    return MA_SUCCESS;\n}\nstatic MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x)\n{\n#if 1\n    ma_uint32 result = 0;\n    while (x > 0) {\n        result += 1;\n        x >>= 1;\n    }\n    return result;\n#endif\n}\nstatic MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision)\n{\n    return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32;\n}\n#if defined(__clang__)\n__attribute__((no_sanitize(\"signed-integer-overflow\")))\n#endif\nstatic MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples)\n{\n    ma_int32 prediction = 0;\n    MA_DR_FLAC_ASSERT(order <= 32);\n    switch (order)\n    {\n    case 32: prediction += coefficients[31] * pDecodedSamples[-32];\n    case 31: prediction += coefficients[30] * pDecodedSamples[-31];\n    case 30: prediction += coefficients[29] * pDecodedSamples[-30];\n    case 29: prediction += coefficients[28] * pDecodedSamples[-29];\n    case 28: prediction += coefficients[27] * pDecodedSamples[-28];\n    case 27: prediction += coefficients[26] * pDecodedSamples[-27];\n    case 26: prediction += coefficients[25] * pDecodedSamples[-26];\n    case 25: prediction += coefficients[24] * pDecodedSamples[-25];\n    case 24: prediction += coefficients[23] * pDecodedSamples[-24];\n    case 23: prediction += coefficients[22] * pDecodedSamples[-23];\n    case 22: prediction += coefficients[21] * pDecodedSamples[-22];\n    case 21: prediction += coefficients[20] * pDecodedSamples[-21];\n    case 20: prediction += coefficients[19] * pDecodedSamples[-20];\n    case 19: prediction += coefficients[18] * pDecodedSamples[-19];\n    case 18: prediction += coefficients[17] * pDecodedSamples[-18];\n    case 17: prediction += coefficients[16] * pDecodedSamples[-17];\n    case 16: prediction += coefficients[15] * pDecodedSamples[-16];\n    case 15: prediction += coefficients[14] * pDecodedSamples[-15];\n    case 14: prediction += coefficients[13] * pDecodedSamples[-14];\n    case 13: prediction += coefficients[12] * pDecodedSamples[-13];\n    case 12: prediction += coefficients[11] * pDecodedSamples[-12];\n    case 11: prediction += coefficients[10] * pDecodedSamples[-11];\n    case 10: prediction += coefficients[ 9] * pDecodedSamples[-10];\n    case  9: prediction += coefficients[ 8] * pDecodedSamples[- 9];\n    case  8: prediction += coefficients[ 7] * pDecodedSamples[- 8];\n    case  7: prediction += coefficients[ 6] * pDecodedSamples[- 7];\n    case  6: prediction += coefficients[ 5] * pDecodedSamples[- 6];\n    case  5: prediction += coefficients[ 4] * pDecodedSamples[- 5];\n    case  4: prediction += coefficients[ 3] * pDecodedSamples[- 4];\n    case  3: prediction += coefficients[ 2] * pDecodedSamples[- 3];\n    case  2: prediction += coefficients[ 1] * pDecodedSamples[- 2];\n    case  1: prediction += coefficients[ 0] * pDecodedSamples[- 1];\n    }\n    return (ma_int32)(prediction >> shift);\n}\nstatic MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples)\n{\n    ma_int64 prediction;\n    MA_DR_FLAC_ASSERT(order <= 32);\n#ifndef MA_64BIT\n    if (order == 8)\n    {\n        prediction  = coefficients[0] * (ma_int64)pDecodedSamples[-1];\n        prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];\n        prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];\n        prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];\n        prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];\n        prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];\n        prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];\n        prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];\n    }\n    else if (order == 7)\n    {\n        prediction  = coefficients[0] * (ma_int64)pDecodedSamples[-1];\n        prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];\n        prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];\n        prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];\n        prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];\n        prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];\n        prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];\n    }\n    else if (order == 3)\n    {\n        prediction  = coefficients[0] * (ma_int64)pDecodedSamples[-1];\n        prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];\n        prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];\n    }\n    else if (order == 6)\n    {\n        prediction  = coefficients[0] * (ma_int64)pDecodedSamples[-1];\n        prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];\n        prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];\n        prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];\n        prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];\n        prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];\n    }\n    else if (order == 5)\n    {\n        prediction  = coefficients[0] * (ma_int64)pDecodedSamples[-1];\n        prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];\n        prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];\n        prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];\n        prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];\n    }\n    else if (order == 4)\n    {\n        prediction  = coefficients[0] * (ma_int64)pDecodedSamples[-1];\n        prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];\n        prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];\n        prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];\n    }\n    else if (order == 12)\n    {\n        prediction  = coefficients[0]  * (ma_int64)pDecodedSamples[-1];\n        prediction += coefficients[1]  * (ma_int64)pDecodedSamples[-2];\n        prediction += coefficients[2]  * (ma_int64)pDecodedSamples[-3];\n        prediction += coefficients[3]  * (ma_int64)pDecodedSamples[-4];\n        prediction += coefficients[4]  * (ma_int64)pDecodedSamples[-5];\n        prediction += coefficients[5]  * (ma_int64)pDecodedSamples[-6];\n        prediction += coefficients[6]  * (ma_int64)pDecodedSamples[-7];\n        prediction += coefficients[7]  * (ma_int64)pDecodedSamples[-8];\n        prediction += coefficients[8]  * (ma_int64)pDecodedSamples[-9];\n        prediction += coefficients[9]  * (ma_int64)pDecodedSamples[-10];\n        prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];\n        prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12];\n    }\n    else if (order == 2)\n    {\n        prediction  = coefficients[0] * (ma_int64)pDecodedSamples[-1];\n        prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];\n    }\n    else if (order == 1)\n    {\n        prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];\n    }\n    else if (order == 10)\n    {\n        prediction  = coefficients[0]  * (ma_int64)pDecodedSamples[-1];\n        prediction += coefficients[1]  * (ma_int64)pDecodedSamples[-2];\n        prediction += coefficients[2]  * (ma_int64)pDecodedSamples[-3];\n        prediction += coefficients[3]  * (ma_int64)pDecodedSamples[-4];\n        prediction += coefficients[4]  * (ma_int64)pDecodedSamples[-5];\n        prediction += coefficients[5]  * (ma_int64)pDecodedSamples[-6];\n        prediction += coefficients[6]  * (ma_int64)pDecodedSamples[-7];\n        prediction += coefficients[7]  * (ma_int64)pDecodedSamples[-8];\n        prediction += coefficients[8]  * (ma_int64)pDecodedSamples[-9];\n        prediction += coefficients[9]  * (ma_int64)pDecodedSamples[-10];\n    }\n    else if (order == 9)\n    {\n        prediction  = coefficients[0]  * (ma_int64)pDecodedSamples[-1];\n        prediction += coefficients[1]  * (ma_int64)pDecodedSamples[-2];\n        prediction += coefficients[2]  * (ma_int64)pDecodedSamples[-3];\n        prediction += coefficients[3]  * (ma_int64)pDecodedSamples[-4];\n        prediction += coefficients[4]  * (ma_int64)pDecodedSamples[-5];\n        prediction += coefficients[5]  * (ma_int64)pDecodedSamples[-6];\n        prediction += coefficients[6]  * (ma_int64)pDecodedSamples[-7];\n        prediction += coefficients[7]  * (ma_int64)pDecodedSamples[-8];\n        prediction += coefficients[8]  * (ma_int64)pDecodedSamples[-9];\n    }\n    else if (order == 11)\n    {\n        prediction  = coefficients[0]  * (ma_int64)pDecodedSamples[-1];\n        prediction += coefficients[1]  * (ma_int64)pDecodedSamples[-2];\n        prediction += coefficients[2]  * (ma_int64)pDecodedSamples[-3];\n        prediction += coefficients[3]  * (ma_int64)pDecodedSamples[-4];\n        prediction += coefficients[4]  * (ma_int64)pDecodedSamples[-5];\n        prediction += coefficients[5]  * (ma_int64)pDecodedSamples[-6];\n        prediction += coefficients[6]  * (ma_int64)pDecodedSamples[-7];\n        prediction += coefficients[7]  * (ma_int64)pDecodedSamples[-8];\n        prediction += coefficients[8]  * (ma_int64)pDecodedSamples[-9];\n        prediction += coefficients[9]  * (ma_int64)pDecodedSamples[-10];\n        prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];\n    }\n    else\n    {\n        int j;\n        prediction = 0;\n        for (j = 0; j < (int)order; ++j) {\n            prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1];\n        }\n    }\n#endif\n#ifdef MA_64BIT\n    prediction = 0;\n    switch (order)\n    {\n    case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32];\n    case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31];\n    case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30];\n    case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29];\n    case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28];\n    case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27];\n    case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26];\n    case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25];\n    case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24];\n    case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23];\n    case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22];\n    case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21];\n    case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20];\n    case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19];\n    case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18];\n    case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17];\n    case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16];\n    case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15];\n    case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14];\n    case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13];\n    case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12];\n    case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];\n    case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10];\n    case  9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9];\n    case  8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8];\n    case  7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7];\n    case  6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6];\n    case  5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5];\n    case  4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4];\n    case  3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3];\n    case  2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2];\n    case  1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1];\n    }\n#endif\n    return (ma_int32)(prediction >> shift);\n}\n#if 0\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)\n{\n    ma_uint32 i;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pSamplesOut != NULL);\n    for (i = 0; i < count; ++i) {\n        ma_uint32 zeroCounter = 0;\n        for (;;) {\n            ma_uint8 bit;\n            if (!ma_dr_flac__read_uint8(bs, 1, &bit)) {\n                return MA_FALSE;\n            }\n            if (bit == 0) {\n                zeroCounter += 1;\n            } else {\n                break;\n            }\n        }\n        ma_uint32 decodedRice;\n        if (riceParam > 0) {\n            if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) {\n                return MA_FALSE;\n            }\n        } else {\n            decodedRice = 0;\n        }\n        decodedRice |= (zeroCounter << riceParam);\n        if ((decodedRice & 0x01)) {\n            decodedRice = ~(decodedRice >> 1);\n        } else {\n            decodedRice =  (decodedRice >> 1);\n        }\n        if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {\n            pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);\n        } else {\n            pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);\n        }\n    }\n    return MA_TRUE;\n}\n#endif\n#if 0\nstatic ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut)\n{\n    ma_uint32 zeroCounter = 0;\n    ma_uint32 decodedRice;\n    for (;;) {\n        ma_uint8 bit;\n        if (!ma_dr_flac__read_uint8(bs, 1, &bit)) {\n            return MA_FALSE;\n        }\n        if (bit == 0) {\n            zeroCounter += 1;\n        } else {\n            break;\n        }\n    }\n    if (riceParam > 0) {\n        if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) {\n            return MA_FALSE;\n        }\n    } else {\n        decodedRice = 0;\n    }\n    *pZeroCounterOut = zeroCounter;\n    *pRiceParamPartOut = decodedRice;\n    return MA_TRUE;\n}\n#endif\n#if 0\nstatic MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut)\n{\n    ma_dr_flac_cache_t riceParamMask;\n    ma_uint32 zeroCounter;\n    ma_uint32 setBitOffsetPlus1;\n    ma_uint32 riceParamPart;\n    ma_uint32 riceLength;\n    MA_DR_FLAC_ASSERT(riceParam > 0);\n    riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam);\n    zeroCounter = 0;\n    while (bs->cache == 0) {\n        zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);\n        if (!ma_dr_flac__reload_cache(bs)) {\n            return MA_FALSE;\n        }\n    }\n    setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache);\n    zeroCounter += setBitOffsetPlus1;\n    setBitOffsetPlus1 += 1;\n    riceLength = setBitOffsetPlus1 + riceParam;\n    if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {\n        riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));\n        bs->consumedBits += riceLength;\n        bs->cache <<= riceLength;\n    } else {\n        ma_uint32 bitCountLo;\n        ma_dr_flac_cache_t resultHi;\n        bs->consumedBits += riceLength;\n        bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1);\n        bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);\n        resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam);\n        if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {\n#ifndef MA_DR_FLAC_NO_CRC\n            ma_dr_flac__update_crc16(bs);\n#endif\n            bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);\n            bs->consumedBits = 0;\n#ifndef MA_DR_FLAC_NO_CRC\n            bs->crc16Cache = bs->cache;\n#endif\n        } else {\n            if (!ma_dr_flac__reload_cache(bs)) {\n                return MA_FALSE;\n            }\n            if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {\n                return MA_FALSE;\n            }\n        }\n        riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));\n        bs->consumedBits += bitCountLo;\n        bs->cache <<= bitCountLo;\n    }\n    pZeroCounterOut[0] = zeroCounter;\n    pRiceParamPartOut[0] = riceParamPart;\n    return MA_TRUE;\n}\n#endif\nstatic MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut)\n{\n    ma_uint32  riceParamPlus1 = riceParam + 1;\n    ma_uint32  riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);\n    ma_uint32  riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;\n    ma_dr_flac_cache_t bs_cache = bs->cache;\n    ma_uint32  bs_consumedBits = bs->consumedBits;\n    ma_uint32  lzcount = ma_dr_flac__clz(bs_cache);\n    if (lzcount < sizeof(bs_cache)*8) {\n        pZeroCounterOut[0] = lzcount;\n    extract_rice_param_part:\n        bs_cache       <<= lzcount;\n        bs_consumedBits += lzcount;\n        if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {\n            pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift);\n            bs_cache       <<= riceParamPlus1;\n            bs_consumedBits += riceParamPlus1;\n        } else {\n            ma_uint32 riceParamPartHi;\n            ma_uint32 riceParamPartLo;\n            ma_uint32 riceParamPartLoBitCount;\n            riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift);\n            riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;\n            MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);\n            if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {\n            #ifndef MA_DR_FLAC_NO_CRC\n                ma_dr_flac__update_crc16(bs);\n            #endif\n                bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);\n                bs_consumedBits = riceParamPartLoBitCount;\n            #ifndef MA_DR_FLAC_NO_CRC\n                bs->crc16Cache = bs_cache;\n            #endif\n            } else {\n                if (!ma_dr_flac__reload_cache(bs)) {\n                    return MA_FALSE;\n                }\n                if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {\n                    return MA_FALSE;\n                }\n                bs_cache = bs->cache;\n                bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;\n            }\n            riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));\n            pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo;\n            bs_cache <<= riceParamPartLoBitCount;\n        }\n    } else {\n        ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);\n        for (;;) {\n            if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {\n            #ifndef MA_DR_FLAC_NO_CRC\n                ma_dr_flac__update_crc16(bs);\n            #endif\n                bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);\n                bs_consumedBits = 0;\n            #ifndef MA_DR_FLAC_NO_CRC\n                bs->crc16Cache = bs_cache;\n            #endif\n            } else {\n                if (!ma_dr_flac__reload_cache(bs)) {\n                    return MA_FALSE;\n                }\n                bs_cache = bs->cache;\n                bs_consumedBits = bs->consumedBits;\n            }\n            lzcount = ma_dr_flac__clz(bs_cache);\n            zeroCounter += lzcount;\n            if (lzcount < sizeof(bs_cache)*8) {\n                break;\n            }\n        }\n        pZeroCounterOut[0] = zeroCounter;\n        goto extract_rice_param_part;\n    }\n    bs->cache = bs_cache;\n    bs->consumedBits = bs_consumedBits;\n    return MA_TRUE;\n}\nstatic MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam)\n{\n    ma_uint32  riceParamPlus1 = riceParam + 1;\n    ma_uint32  riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;\n    ma_dr_flac_cache_t bs_cache = bs->cache;\n    ma_uint32  bs_consumedBits = bs->consumedBits;\n    ma_uint32  lzcount = ma_dr_flac__clz(bs_cache);\n    if (lzcount < sizeof(bs_cache)*8) {\n    extract_rice_param_part:\n        bs_cache       <<= lzcount;\n        bs_consumedBits += lzcount;\n        if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {\n            bs_cache       <<= riceParamPlus1;\n            bs_consumedBits += riceParamPlus1;\n        } else {\n            ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;\n            MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);\n            if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {\n            #ifndef MA_DR_FLAC_NO_CRC\n                ma_dr_flac__update_crc16(bs);\n            #endif\n                bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);\n                bs_consumedBits = riceParamPartLoBitCount;\n            #ifndef MA_DR_FLAC_NO_CRC\n                bs->crc16Cache = bs_cache;\n            #endif\n            } else {\n                if (!ma_dr_flac__reload_cache(bs)) {\n                    return MA_FALSE;\n                }\n                if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {\n                    return MA_FALSE;\n                }\n                bs_cache = bs->cache;\n                bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;\n            }\n            bs_cache <<= riceParamPartLoBitCount;\n        }\n    } else {\n        for (;;) {\n            if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {\n            #ifndef MA_DR_FLAC_NO_CRC\n                ma_dr_flac__update_crc16(bs);\n            #endif\n                bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);\n                bs_consumedBits = 0;\n            #ifndef MA_DR_FLAC_NO_CRC\n                bs->crc16Cache = bs_cache;\n            #endif\n            } else {\n                if (!ma_dr_flac__reload_cache(bs)) {\n                    return MA_FALSE;\n                }\n                bs_cache = bs->cache;\n                bs_consumedBits = bs->consumedBits;\n            }\n            lzcount = ma_dr_flac__clz(bs_cache);\n            if (lzcount < sizeof(bs_cache)*8) {\n                break;\n            }\n        }\n        goto extract_rice_param_part;\n    }\n    bs->cache = bs_cache;\n    bs->consumedBits = bs_consumedBits;\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)\n{\n    ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};\n    ma_uint32 zeroCountPart0;\n    ma_uint32 riceParamPart0;\n    ma_uint32 riceParamMask;\n    ma_uint32 i;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pSamplesOut != NULL);\n    (void)bitsPerSample;\n    (void)order;\n    (void)shift;\n    (void)coefficients;\n    riceParamMask  = (ma_uint32)~((~0UL) << riceParam);\n    i = 0;\n    while (i < count) {\n        if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {\n            return MA_FALSE;\n        }\n        riceParamPart0 &= riceParamMask;\n        riceParamPart0 |= (zeroCountPart0 << riceParam);\n        riceParamPart0  = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];\n        pSamplesOut[i] = riceParamPart0;\n        i += 1;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)\n{\n    ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};\n    ma_uint32 zeroCountPart0 = 0;\n    ma_uint32 zeroCountPart1 = 0;\n    ma_uint32 zeroCountPart2 = 0;\n    ma_uint32 zeroCountPart3 = 0;\n    ma_uint32 riceParamPart0 = 0;\n    ma_uint32 riceParamPart1 = 0;\n    ma_uint32 riceParamPart2 = 0;\n    ma_uint32 riceParamPart3 = 0;\n    ma_uint32 riceParamMask;\n    const ma_int32* pSamplesOutEnd;\n    ma_uint32 i;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pSamplesOut != NULL);\n    if (lpcOrder == 0) {\n        return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);\n    }\n    riceParamMask  = (ma_uint32)~((~0UL) << riceParam);\n    pSamplesOutEnd = pSamplesOut + (count & ~3);\n    if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {\n        while (pSamplesOut < pSamplesOutEnd) {\n            if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||\n                !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||\n                !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||\n                !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {\n                return MA_FALSE;\n            }\n            riceParamPart0 &= riceParamMask;\n            riceParamPart1 &= riceParamMask;\n            riceParamPart2 &= riceParamMask;\n            riceParamPart3 &= riceParamMask;\n            riceParamPart0 |= (zeroCountPart0 << riceParam);\n            riceParamPart1 |= (zeroCountPart1 << riceParam);\n            riceParamPart2 |= (zeroCountPart2 << riceParam);\n            riceParamPart3 |= (zeroCountPart3 << riceParam);\n            riceParamPart0  = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];\n            riceParamPart1  = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];\n            riceParamPart2  = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];\n            riceParamPart3  = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];\n            pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);\n            pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);\n            pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);\n            pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);\n            pSamplesOut += 4;\n        }\n    } else {\n        while (pSamplesOut < pSamplesOutEnd) {\n            if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||\n                !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||\n                !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||\n                !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {\n                return MA_FALSE;\n            }\n            riceParamPart0 &= riceParamMask;\n            riceParamPart1 &= riceParamMask;\n            riceParamPart2 &= riceParamMask;\n            riceParamPart3 &= riceParamMask;\n            riceParamPart0 |= (zeroCountPart0 << riceParam);\n            riceParamPart1 |= (zeroCountPart1 << riceParam);\n            riceParamPart2 |= (zeroCountPart2 << riceParam);\n            riceParamPart3 |= (zeroCountPart3 << riceParam);\n            riceParamPart0  = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];\n            riceParamPart1  = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];\n            riceParamPart2  = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];\n            riceParamPart3  = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];\n            pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);\n            pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);\n            pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);\n            pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);\n            pSamplesOut += 4;\n        }\n    }\n    i = (count & ~3);\n    while (i < count) {\n        if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {\n            return MA_FALSE;\n        }\n        riceParamPart0 &= riceParamMask;\n        riceParamPart0 |= (zeroCountPart0 << riceParam);\n        riceParamPart0  = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];\n        if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {\n            pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);\n        } else {\n            pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);\n        }\n        i += 1;\n        pSamplesOut += 1;\n    }\n    return MA_TRUE;\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b)\n{\n    __m128i r;\n    r = _mm_packs_epi32(a, b);\n    r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0));\n    r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));\n    r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));\n    return r;\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_SSE41)\nstatic MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a)\n{\n    return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));\n}\nstatic MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x)\n{\n    __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));\n    __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2));\n    return _mm_add_epi32(x64, x32);\n}\nstatic MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x)\n{\n    return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));\n}\nstatic MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count)\n{\n    __m128i lo = _mm_srli_epi64(x, count);\n    __m128i hi = _mm_srai_epi32(x, count);\n    hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0));\n    return _mm_or_si128(lo, hi);\n}\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)\n{\n    int i;\n    ma_uint32 riceParamMask;\n    ma_int32* pDecodedSamples    = pSamplesOut;\n    ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);\n    ma_uint32 zeroCountParts0 = 0;\n    ma_uint32 zeroCountParts1 = 0;\n    ma_uint32 zeroCountParts2 = 0;\n    ma_uint32 zeroCountParts3 = 0;\n    ma_uint32 riceParamParts0 = 0;\n    ma_uint32 riceParamParts1 = 0;\n    ma_uint32 riceParamParts2 = 0;\n    ma_uint32 riceParamParts3 = 0;\n    __m128i coefficients128_0;\n    __m128i coefficients128_4;\n    __m128i coefficients128_8;\n    __m128i samples128_0;\n    __m128i samples128_4;\n    __m128i samples128_8;\n    __m128i riceParamMask128;\n    const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};\n    riceParamMask    = (ma_uint32)~((~0UL) << riceParam);\n    riceParamMask128 = _mm_set1_epi32(riceParamMask);\n    coefficients128_0 = _mm_setzero_si128();\n    coefficients128_4 = _mm_setzero_si128();\n    coefficients128_8 = _mm_setzero_si128();\n    samples128_0 = _mm_setzero_si128();\n    samples128_4 = _mm_setzero_si128();\n    samples128_8 = _mm_setzero_si128();\n#if 1\n    {\n        int runningOrder = order;\n        if (runningOrder >= 4) {\n            coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));\n            samples128_0      = _mm_loadu_si128((const __m128i*)(pSamplesOut  - 4));\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break;\n                case 2: coefficients128_0 = _mm_set_epi32(0, 0,               coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0,               0); break;\n                case 1: coefficients128_0 = _mm_set_epi32(0, 0,               0,               coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0,               0,               0); break;\n            }\n            runningOrder = 0;\n        }\n        if (runningOrder >= 4) {\n            coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));\n            samples128_4      = _mm_loadu_si128((const __m128i*)(pSamplesOut  - 8));\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break;\n                case 2: coefficients128_4 = _mm_set_epi32(0, 0,               coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0,               0); break;\n                case 1: coefficients128_4 = _mm_set_epi32(0, 0,               0,               coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0,               0,               0); break;\n            }\n            runningOrder = 0;\n        }\n        if (runningOrder == 4) {\n            coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));\n            samples128_8      = _mm_loadu_si128((const __m128i*)(pSamplesOut  - 12));\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break;\n                case 2: coefficients128_8 = _mm_set_epi32(0, 0,                coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0,                0); break;\n                case 1: coefficients128_8 = _mm_set_epi32(0, 0,                0,               coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0,                0,                0); break;\n            }\n            runningOrder = 0;\n        }\n        coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));\n        coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));\n        coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));\n    }\n#else\n    switch (order)\n    {\n    case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12];\n    case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11];\n    case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10];\n    case 9:  ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9];\n    case 8:  ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8];\n    case 7:  ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7];\n    case 6:  ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6];\n    case 5:  ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5];\n    case 4:  ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4];\n    case 3:  ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3];\n    case 2:  ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2];\n    case 1:  ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1];\n    }\n#endif\n    while (pDecodedSamples < pDecodedSamplesEnd) {\n        __m128i prediction128;\n        __m128i zeroCountPart128;\n        __m128i riceParamPart128;\n        if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {\n            return MA_FALSE;\n        }\n        zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);\n        riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);\n        riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);\n        riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));\n        riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01)));\n        if (order <= 4) {\n            for (i = 0; i < 4; i += 1) {\n                prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0);\n                prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128);\n                prediction128 = _mm_srai_epi32(prediction128, shift);\n                prediction128 = _mm_add_epi32(riceParamPart128, prediction128);\n                samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);\n                riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);\n            }\n        } else if (order <= 8) {\n            for (i = 0; i < 4; i += 1) {\n                prediction128 =                              _mm_mullo_epi32(coefficients128_4, samples128_4);\n                prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));\n                prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128);\n                prediction128 = _mm_srai_epi32(prediction128, shift);\n                prediction128 = _mm_add_epi32(riceParamPart128, prediction128);\n                samples128_4 = _mm_alignr_epi8(samples128_0,  samples128_4, 4);\n                samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);\n                riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);\n            }\n        } else {\n            for (i = 0; i < 4; i += 1) {\n                prediction128 =                              _mm_mullo_epi32(coefficients128_8, samples128_8);\n                prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4));\n                prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));\n                prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128);\n                prediction128 = _mm_srai_epi32(prediction128, shift);\n                prediction128 = _mm_add_epi32(riceParamPart128, prediction128);\n                samples128_8 = _mm_alignr_epi8(samples128_4,  samples128_8, 4);\n                samples128_4 = _mm_alignr_epi8(samples128_0,  samples128_4, 4);\n                samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);\n                riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);\n            }\n        }\n        _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);\n        pDecodedSamples += 4;\n    }\n    i = (count & ~3);\n    while (i < (int)count) {\n        if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {\n            return MA_FALSE;\n        }\n        riceParamParts0 &= riceParamMask;\n        riceParamParts0 |= (zeroCountParts0 << riceParam);\n        riceParamParts0  = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];\n        pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);\n        i += 1;\n        pDecodedSamples += 1;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)\n{\n    int i;\n    ma_uint32 riceParamMask;\n    ma_int32* pDecodedSamples    = pSamplesOut;\n    ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);\n    ma_uint32 zeroCountParts0 = 0;\n    ma_uint32 zeroCountParts1 = 0;\n    ma_uint32 zeroCountParts2 = 0;\n    ma_uint32 zeroCountParts3 = 0;\n    ma_uint32 riceParamParts0 = 0;\n    ma_uint32 riceParamParts1 = 0;\n    ma_uint32 riceParamParts2 = 0;\n    ma_uint32 riceParamParts3 = 0;\n    __m128i coefficients128_0;\n    __m128i coefficients128_4;\n    __m128i coefficients128_8;\n    __m128i samples128_0;\n    __m128i samples128_4;\n    __m128i samples128_8;\n    __m128i prediction128;\n    __m128i riceParamMask128;\n    const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};\n    MA_DR_FLAC_ASSERT(order <= 12);\n    riceParamMask    = (ma_uint32)~((~0UL) << riceParam);\n    riceParamMask128 = _mm_set1_epi32(riceParamMask);\n    prediction128 = _mm_setzero_si128();\n    coefficients128_0  = _mm_setzero_si128();\n    coefficients128_4  = _mm_setzero_si128();\n    coefficients128_8  = _mm_setzero_si128();\n    samples128_0  = _mm_setzero_si128();\n    samples128_4  = _mm_setzero_si128();\n    samples128_8  = _mm_setzero_si128();\n#if 1\n    {\n        int runningOrder = order;\n        if (runningOrder >= 4) {\n            coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));\n            samples128_0      = _mm_loadu_si128((const __m128i*)(pSamplesOut  - 4));\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break;\n                case 2: coefficients128_0 = _mm_set_epi32(0, 0,               coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0,               0); break;\n                case 1: coefficients128_0 = _mm_set_epi32(0, 0,               0,               coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0,               0,               0); break;\n            }\n            runningOrder = 0;\n        }\n        if (runningOrder >= 4) {\n            coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));\n            samples128_4      = _mm_loadu_si128((const __m128i*)(pSamplesOut  - 8));\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break;\n                case 2: coefficients128_4 = _mm_set_epi32(0, 0,               coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0,               0); break;\n                case 1: coefficients128_4 = _mm_set_epi32(0, 0,               0,               coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0,               0,               0); break;\n            }\n            runningOrder = 0;\n        }\n        if (runningOrder == 4) {\n            coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));\n            samples128_8      = _mm_loadu_si128((const __m128i*)(pSamplesOut  - 12));\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break;\n                case 2: coefficients128_8 = _mm_set_epi32(0, 0,                coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0,                0); break;\n                case 1: coefficients128_8 = _mm_set_epi32(0, 0,                0,               coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0,                0,                0); break;\n            }\n            runningOrder = 0;\n        }\n        coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));\n        coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));\n        coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));\n    }\n#else\n    switch (order)\n    {\n    case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12];\n    case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11];\n    case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10];\n    case 9:  ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9];\n    case 8:  ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8];\n    case 7:  ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7];\n    case 6:  ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6];\n    case 5:  ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5];\n    case 4:  ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4];\n    case 3:  ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3];\n    case 2:  ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2];\n    case 1:  ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1];\n    }\n#endif\n    while (pDecodedSamples < pDecodedSamplesEnd) {\n        __m128i zeroCountPart128;\n        __m128i riceParamPart128;\n        if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {\n            return MA_FALSE;\n        }\n        zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);\n        riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);\n        riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);\n        riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));\n        riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1)));\n        for (i = 0; i < 4; i += 1) {\n            prediction128 = _mm_xor_si128(prediction128, prediction128);\n            switch (order)\n            {\n            case 12:\n            case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0))));\n            case 10:\n            case  9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2))));\n            case  8:\n            case  7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0))));\n            case  6:\n            case  5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2))));\n            case  4:\n            case  3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0))));\n            case  2:\n            case  1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2))));\n            }\n            prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128);\n            prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift);\n            prediction128 = _mm_add_epi32(riceParamPart128, prediction128);\n            samples128_8 = _mm_alignr_epi8(samples128_4,  samples128_8, 4);\n            samples128_4 = _mm_alignr_epi8(samples128_0,  samples128_4, 4);\n            samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);\n            riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);\n        }\n        _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);\n        pDecodedSamples += 4;\n    }\n    i = (count & ~3);\n    while (i < (int)count) {\n        if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {\n            return MA_FALSE;\n        }\n        riceParamParts0 &= riceParamMask;\n        riceParamParts0 |= (zeroCountParts0 << riceParam);\n        riceParamParts0  = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];\n        pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);\n        i += 1;\n        pDecodedSamples += 1;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)\n{\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pSamplesOut != NULL);\n    if (lpcOrder > 0 && lpcOrder <= 12) {\n        if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {\n            return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);\n        } else {\n            return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);\n        }\n    } else {\n        return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x)\n{\n    vst1q_s32(p+0, x.val[0]);\n    vst1q_s32(p+4, x.val[1]);\n}\nstatic MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x)\n{\n    vst1q_u32(p+0, x.val[0]);\n    vst1q_u32(p+4, x.val[1]);\n}\nstatic MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x)\n{\n    vst1q_f32(p+0, x.val[0]);\n    vst1q_f32(p+4, x.val[1]);\n}\nstatic MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x)\n{\n    vst1q_s16(p, vcombine_s16(x.val[0], x.val[1]));\n}\nstatic MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x)\n{\n    vst1q_u16(p, vcombine_u16(x.val[0], x.val[1]));\n}\nstatic MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0)\n{\n    ma_int32 x[4];\n    x[3] = x3;\n    x[2] = x2;\n    x[1] = x1;\n    x[0] = x0;\n    return vld1q_s32(x);\n}\nstatic MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b)\n{\n    return vextq_s32(b, a, 1);\n}\nstatic MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b)\n{\n    return vextq_u32(b, a, 1);\n}\nstatic MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x)\n{\n    int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x));\n    return vpadd_s32(r, r);\n}\nstatic MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x)\n{\n    return vadd_s64(vget_high_s64(x), vget_low_s64(x));\n}\nstatic MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x)\n{\n    return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x)));\n}\nstatic MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x)\n{\n    return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF));\n}\nstatic MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x)\n{\n    return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF));\n}\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)\n{\n    int i;\n    ma_uint32 riceParamMask;\n    ma_int32* pDecodedSamples    = pSamplesOut;\n    ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);\n    ma_uint32 zeroCountParts[4];\n    ma_uint32 riceParamParts[4];\n    int32x4_t coefficients128_0;\n    int32x4_t coefficients128_4;\n    int32x4_t coefficients128_8;\n    int32x4_t samples128_0;\n    int32x4_t samples128_4;\n    int32x4_t samples128_8;\n    uint32x4_t riceParamMask128;\n    int32x4_t riceParam128;\n    int32x2_t shift64;\n    uint32x4_t one128;\n    const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};\n    riceParamMask    = (ma_uint32)~((~0UL) << riceParam);\n    riceParamMask128 = vdupq_n_u32(riceParamMask);\n    riceParam128 = vdupq_n_s32(riceParam);\n    shift64 = vdup_n_s32(-shift);\n    one128 = vdupq_n_u32(1);\n    {\n        int runningOrder = order;\n        ma_int32 tempC[4] = {0, 0, 0, 0};\n        ma_int32 tempS[4] = {0, 0, 0, 0};\n        if (runningOrder >= 4) {\n            coefficients128_0 = vld1q_s32(coefficients + 0);\n            samples128_0      = vld1q_s32(pSamplesOut  - 4);\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];\n                case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];\n                case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];\n            }\n            coefficients128_0 = vld1q_s32(tempC);\n            samples128_0      = vld1q_s32(tempS);\n            runningOrder = 0;\n        }\n        if (runningOrder >= 4) {\n            coefficients128_4 = vld1q_s32(coefficients + 4);\n            samples128_4      = vld1q_s32(pSamplesOut  - 8);\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];\n                case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];\n                case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];\n            }\n            coefficients128_4 = vld1q_s32(tempC);\n            samples128_4      = vld1q_s32(tempS);\n            runningOrder = 0;\n        }\n        if (runningOrder == 4) {\n            coefficients128_8 = vld1q_s32(coefficients + 8);\n            samples128_8      = vld1q_s32(pSamplesOut  - 12);\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];\n                case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];\n                case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];\n            }\n            coefficients128_8 = vld1q_s32(tempC);\n            samples128_8      = vld1q_s32(tempS);\n            runningOrder = 0;\n        }\n        coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0);\n        coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4);\n        coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8);\n    }\n    while (pDecodedSamples < pDecodedSamplesEnd) {\n        int32x4_t prediction128;\n        int32x2_t prediction64;\n        uint32x4_t zeroCountPart128;\n        uint32x4_t riceParamPart128;\n        if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {\n            return MA_FALSE;\n        }\n        zeroCountPart128 = vld1q_u32(zeroCountParts);\n        riceParamPart128 = vld1q_u32(riceParamParts);\n        riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);\n        riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));\n        riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));\n        if (order <= 4) {\n            for (i = 0; i < 4; i += 1) {\n                prediction128 = vmulq_s32(coefficients128_0, samples128_0);\n                prediction64 = ma_dr_flac__vhaddq_s32(prediction128);\n                prediction64 = vshl_s32(prediction64, shift64);\n                prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));\n                samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);\n                riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);\n            }\n        } else if (order <= 8) {\n            for (i = 0; i < 4; i += 1) {\n                prediction128 =                vmulq_s32(coefficients128_4, samples128_4);\n                prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);\n                prediction64 = ma_dr_flac__vhaddq_s32(prediction128);\n                prediction64 = vshl_s32(prediction64, shift64);\n                prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));\n                samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);\n                samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);\n                riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);\n            }\n        } else {\n            for (i = 0; i < 4; i += 1) {\n                prediction128 =                vmulq_s32(coefficients128_8, samples128_8);\n                prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4);\n                prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);\n                prediction64 = ma_dr_flac__vhaddq_s32(prediction128);\n                prediction64 = vshl_s32(prediction64, shift64);\n                prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));\n                samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8);\n                samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);\n                samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);\n                riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);\n            }\n        }\n        vst1q_s32(pDecodedSamples, samples128_0);\n        pDecodedSamples += 4;\n    }\n    i = (count & ~3);\n    while (i < (int)count) {\n        if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {\n            return MA_FALSE;\n        }\n        riceParamParts[0] &= riceParamMask;\n        riceParamParts[0] |= (zeroCountParts[0] << riceParam);\n        riceParamParts[0]  = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];\n        pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);\n        i += 1;\n        pDecodedSamples += 1;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)\n{\n    int i;\n    ma_uint32 riceParamMask;\n    ma_int32* pDecodedSamples    = pSamplesOut;\n    ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);\n    ma_uint32 zeroCountParts[4];\n    ma_uint32 riceParamParts[4];\n    int32x4_t coefficients128_0;\n    int32x4_t coefficients128_4;\n    int32x4_t coefficients128_8;\n    int32x4_t samples128_0;\n    int32x4_t samples128_4;\n    int32x4_t samples128_8;\n    uint32x4_t riceParamMask128;\n    int32x4_t riceParam128;\n    int64x1_t shift64;\n    uint32x4_t one128;\n    int64x2_t prediction128 = { 0 };\n    uint32x4_t zeroCountPart128;\n    uint32x4_t riceParamPart128;\n    const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};\n    riceParamMask    = (ma_uint32)~((~0UL) << riceParam);\n    riceParamMask128 = vdupq_n_u32(riceParamMask);\n    riceParam128 = vdupq_n_s32(riceParam);\n    shift64 = vdup_n_s64(-shift);\n    one128 = vdupq_n_u32(1);\n    {\n        int runningOrder = order;\n        ma_int32 tempC[4] = {0, 0, 0, 0};\n        ma_int32 tempS[4] = {0, 0, 0, 0};\n        if (runningOrder >= 4) {\n            coefficients128_0 = vld1q_s32(coefficients + 0);\n            samples128_0      = vld1q_s32(pSamplesOut  - 4);\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];\n                case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];\n                case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];\n            }\n            coefficients128_0 = vld1q_s32(tempC);\n            samples128_0      = vld1q_s32(tempS);\n            runningOrder = 0;\n        }\n        if (runningOrder >= 4) {\n            coefficients128_4 = vld1q_s32(coefficients + 4);\n            samples128_4      = vld1q_s32(pSamplesOut  - 8);\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];\n                case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];\n                case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];\n            }\n            coefficients128_4 = vld1q_s32(tempC);\n            samples128_4      = vld1q_s32(tempS);\n            runningOrder = 0;\n        }\n        if (runningOrder == 4) {\n            coefficients128_8 = vld1q_s32(coefficients + 8);\n            samples128_8      = vld1q_s32(pSamplesOut  - 12);\n            runningOrder -= 4;\n        } else {\n            switch (runningOrder) {\n                case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];\n                case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];\n                case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];\n            }\n            coefficients128_8 = vld1q_s32(tempC);\n            samples128_8      = vld1q_s32(tempS);\n            runningOrder = 0;\n        }\n        coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0);\n        coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4);\n        coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8);\n    }\n    while (pDecodedSamples < pDecodedSamplesEnd) {\n        if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||\n            !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {\n            return MA_FALSE;\n        }\n        zeroCountPart128 = vld1q_u32(zeroCountParts);\n        riceParamPart128 = vld1q_u32(riceParamParts);\n        riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);\n        riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));\n        riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));\n        for (i = 0; i < 4; i += 1) {\n            int64x1_t prediction64;\n            prediction128 = veorq_s64(prediction128, prediction128);\n            switch (order)\n            {\n            case 12:\n            case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8)));\n            case 10:\n            case  9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8)));\n            case  8:\n            case  7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4)));\n            case  6:\n            case  5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4)));\n            case  4:\n            case  3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0)));\n            case  2:\n            case  1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0)));\n            }\n            prediction64 = ma_dr_flac__vhaddq_s64(prediction128);\n            prediction64 = vshl_s64(prediction64, shift64);\n            prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0)));\n            samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8);\n            samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);\n            samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0);\n            riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);\n        }\n        vst1q_s32(pDecodedSamples, samples128_0);\n        pDecodedSamples += 4;\n    }\n    i = (count & ~3);\n    while (i < (int)count) {\n        if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {\n            return MA_FALSE;\n        }\n        riceParamParts[0] &= riceParamMask;\n        riceParamParts[0] |= (zeroCountParts[0] << riceParam);\n        riceParamParts[0]  = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];\n        pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);\n        i += 1;\n        pDecodedSamples += 1;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)\n{\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(pSamplesOut != NULL);\n    if (lpcOrder > 0 && lpcOrder <= 12) {\n        if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {\n            return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);\n        } else {\n            return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);\n        }\n    } else {\n        return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);\n    }\n}\n#endif\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE41)\n    if (ma_dr_flac__gIsSSE41Supported) {\n        return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported) {\n        return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);\n    } else\n#endif\n    {\n    #if 0\n        return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);\n    #else\n        return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);\n    #endif\n    }\n}\nstatic ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam)\n{\n    ma_uint32 i;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    for (i = 0; i < count; ++i) {\n        if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) {\n            return MA_FALSE;\n        }\n    }\n    return MA_TRUE;\n}\n#if defined(__clang__)\n__attribute__((no_sanitize(\"signed-integer-overflow\")))\n#endif\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)\n{\n    ma_uint32 i;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31);\n    MA_DR_FLAC_ASSERT(pSamplesOut != NULL);\n    for (i = 0; i < count; ++i) {\n        if (unencodedBitsPerSample > 0) {\n            if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {\n                return MA_FALSE;\n            }\n        } else {\n            pSamplesOut[i] = 0;\n        }\n        if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {\n            pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);\n        } else {\n            pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);\n        }\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples)\n{\n    ma_uint8 residualMethod;\n    ma_uint8 partitionOrder;\n    ma_uint32 samplesInPartition;\n    ma_uint32 partitionsRemaining;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(blockSize != 0);\n    MA_DR_FLAC_ASSERT(pDecodedSamples != NULL);\n    if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) {\n        return MA_FALSE;\n    }\n    if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {\n        return MA_FALSE;\n    }\n    pDecodedSamples += lpcOrder;\n    if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) {\n        return MA_FALSE;\n    }\n    if (partitionOrder > 8) {\n        return MA_FALSE;\n    }\n    if ((blockSize / (1 << partitionOrder)) < lpcOrder) {\n        return MA_FALSE;\n    }\n    samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder;\n    partitionsRemaining = (1 << partitionOrder);\n    for (;;) {\n        ma_uint8 riceParam = 0;\n        if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {\n            if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) {\n                return MA_FALSE;\n            }\n            if (riceParam == 15) {\n                riceParam = 0xFF;\n            }\n        } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {\n            if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) {\n                return MA_FALSE;\n            }\n            if (riceParam == 31) {\n                riceParam = 0xFF;\n            }\n        }\n        if (riceParam != 0xFF) {\n            if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {\n                return MA_FALSE;\n            }\n        } else {\n            ma_uint8 unencodedBitsPerSample = 0;\n            if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) {\n                return MA_FALSE;\n            }\n            if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {\n                return MA_FALSE;\n            }\n        }\n        pDecodedSamples += samplesInPartition;\n        if (partitionsRemaining == 1) {\n            break;\n        }\n        partitionsRemaining -= 1;\n        if (partitionOrder != 0) {\n            samplesInPartition = blockSize / (1 << partitionOrder);\n        }\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order)\n{\n    ma_uint8 residualMethod;\n    ma_uint8 partitionOrder;\n    ma_uint32 samplesInPartition;\n    ma_uint32 partitionsRemaining;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(blockSize != 0);\n    if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) {\n        return MA_FALSE;\n    }\n    if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {\n        return MA_FALSE;\n    }\n    if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) {\n        return MA_FALSE;\n    }\n    if (partitionOrder > 8) {\n        return MA_FALSE;\n    }\n    if ((blockSize / (1 << partitionOrder)) <= order) {\n        return MA_FALSE;\n    }\n    samplesInPartition = (blockSize / (1 << partitionOrder)) - order;\n    partitionsRemaining = (1 << partitionOrder);\n    for (;;)\n    {\n        ma_uint8 riceParam = 0;\n        if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {\n            if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) {\n                return MA_FALSE;\n            }\n            if (riceParam == 15) {\n                riceParam = 0xFF;\n            }\n        } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {\n            if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) {\n                return MA_FALSE;\n            }\n            if (riceParam == 31) {\n                riceParam = 0xFF;\n            }\n        }\n        if (riceParam != 0xFF) {\n            if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) {\n                return MA_FALSE;\n            }\n        } else {\n            ma_uint8 unencodedBitsPerSample = 0;\n            if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) {\n                return MA_FALSE;\n            }\n            if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) {\n                return MA_FALSE;\n            }\n        }\n        if (partitionsRemaining == 1) {\n            break;\n        }\n        partitionsRemaining -= 1;\n        samplesInPartition = blockSize / (1 << partitionOrder);\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples)\n{\n    ma_uint32 i;\n    ma_int32 sample;\n    if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {\n        return MA_FALSE;\n    }\n    for (i = 0; i < blockSize; ++i) {\n        pDecodedSamples[i] = sample;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples)\n{\n    ma_uint32 i;\n    for (i = 0; i < blockSize; ++i) {\n        ma_int32 sample;\n        if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {\n            return MA_FALSE;\n        }\n        pDecodedSamples[i] = sample;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples)\n{\n    ma_uint32 i;\n    static ma_int32 lpcCoefficientsTable[5][4] = {\n        {0,  0, 0,  0},\n        {1,  0, 0,  0},\n        {2, -1, 0,  0},\n        {3, -3, 1,  0},\n        {4, -6, 4, -1}\n    };\n    for (i = 0; i < lpcOrder; ++i) {\n        ma_int32 sample;\n        if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {\n            return MA_FALSE;\n        }\n        pDecodedSamples[i] = sample;\n    }\n    if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {\n        return MA_FALSE;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples)\n{\n    ma_uint8 i;\n    ma_uint8 lpcPrecision;\n    ma_int8 lpcShift;\n    ma_int32 coefficients[32];\n    for (i = 0; i < lpcOrder; ++i) {\n        ma_int32 sample;\n        if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) {\n            return MA_FALSE;\n        }\n        pDecodedSamples[i] = sample;\n    }\n    if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) {\n        return MA_FALSE;\n    }\n    if (lpcPrecision == 15) {\n        return MA_FALSE;\n    }\n    lpcPrecision += 1;\n    if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) {\n        return MA_FALSE;\n    }\n    if (lpcShift < 0) {\n        return MA_FALSE;\n    }\n    MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients));\n    for (i = 0; i < lpcOrder; ++i) {\n        if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) {\n            return MA_FALSE;\n        }\n    }\n    if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {\n        return MA_FALSE;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header)\n{\n    const ma_uint32 sampleRateTable[12]  = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};\n    const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1};\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(header != NULL);\n    for (;;) {\n        ma_uint8 crc8 = 0xCE;\n        ma_uint8 reserved = 0;\n        ma_uint8 blockingStrategy = 0;\n        ma_uint8 blockSize = 0;\n        ma_uint8 sampleRate = 0;\n        ma_uint8 channelAssignment = 0;\n        ma_uint8 bitsPerSample = 0;\n        ma_bool32 isVariableBlockSize;\n        if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) {\n            return MA_FALSE;\n        }\n        if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) {\n            return MA_FALSE;\n        }\n        if (reserved == 1) {\n            continue;\n        }\n        crc8 = ma_dr_flac_crc8(crc8, reserved, 1);\n        if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) {\n            return MA_FALSE;\n        }\n        crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1);\n        if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) {\n            return MA_FALSE;\n        }\n        if (blockSize == 0) {\n            continue;\n        }\n        crc8 = ma_dr_flac_crc8(crc8, blockSize, 4);\n        if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) {\n            return MA_FALSE;\n        }\n        crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4);\n        if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) {\n            return MA_FALSE;\n        }\n        if (channelAssignment > 10) {\n            continue;\n        }\n        crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4);\n        if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) {\n            return MA_FALSE;\n        }\n        if (bitsPerSample == 3 || bitsPerSample == 7) {\n            continue;\n        }\n        crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3);\n        if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) {\n            return MA_FALSE;\n        }\n        if (reserved == 1) {\n            continue;\n        }\n        crc8 = ma_dr_flac_crc8(crc8, reserved, 1);\n        isVariableBlockSize = blockingStrategy == 1;\n        if (isVariableBlockSize) {\n            ma_uint64 pcmFrameNumber;\n            ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8);\n            if (result != MA_SUCCESS) {\n                if (result == MA_AT_END) {\n                    return MA_FALSE;\n                } else {\n                    continue;\n                }\n            }\n            header->flacFrameNumber  = 0;\n            header->pcmFrameNumber = pcmFrameNumber;\n        } else {\n            ma_uint64 flacFrameNumber = 0;\n            ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8);\n            if (result != MA_SUCCESS) {\n                if (result == MA_AT_END) {\n                    return MA_FALSE;\n                } else {\n                    continue;\n                }\n            }\n            header->flacFrameNumber  = (ma_uint32)flacFrameNumber;\n            header->pcmFrameNumber = 0;\n        }\n        MA_DR_FLAC_ASSERT(blockSize > 0);\n        if (blockSize == 1) {\n            header->blockSizeInPCMFrames = 192;\n        } else if (blockSize <= 5) {\n            MA_DR_FLAC_ASSERT(blockSize >= 2);\n            header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2));\n        } else if (blockSize == 6) {\n            if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) {\n                return MA_FALSE;\n            }\n            crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8);\n            header->blockSizeInPCMFrames += 1;\n        } else if (blockSize == 7) {\n            if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) {\n                return MA_FALSE;\n            }\n            crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16);\n            if (header->blockSizeInPCMFrames == 0xFFFF) {\n                return MA_FALSE;\n            }\n            header->blockSizeInPCMFrames += 1;\n        } else {\n            MA_DR_FLAC_ASSERT(blockSize >= 8);\n            header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8));\n        }\n        if (sampleRate <= 11) {\n            header->sampleRate = sampleRateTable[sampleRate];\n        } else if (sampleRate == 12) {\n            if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) {\n                return MA_FALSE;\n            }\n            crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8);\n            header->sampleRate *= 1000;\n        } else if (sampleRate == 13) {\n            if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) {\n                return MA_FALSE;\n            }\n            crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16);\n        } else if (sampleRate == 14) {\n            if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) {\n                return MA_FALSE;\n            }\n            crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16);\n            header->sampleRate *= 10;\n        } else {\n            continue;\n        }\n        header->channelAssignment = channelAssignment;\n        header->bitsPerSample = bitsPerSampleTable[bitsPerSample];\n        if (header->bitsPerSample == 0) {\n            header->bitsPerSample = streaminfoBitsPerSample;\n        }\n        if (header->bitsPerSample != streaminfoBitsPerSample) {\n            return MA_FALSE;\n        }\n        if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) {\n            return MA_FALSE;\n        }\n#ifndef MA_DR_FLAC_NO_CRC\n        if (header->crc8 != crc8) {\n            continue;\n        }\n#endif\n        return MA_TRUE;\n    }\n}\nstatic ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe)\n{\n    ma_uint8 header;\n    int type;\n    if (!ma_dr_flac__read_uint8(bs, 8, &header)) {\n        return MA_FALSE;\n    }\n    if ((header & 0x80) != 0) {\n        return MA_FALSE;\n    }\n    type = (header & 0x7E) >> 1;\n    if (type == 0) {\n        pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT;\n    } else if (type == 1) {\n        pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM;\n    } else {\n        if ((type & 0x20) != 0) {\n            pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC;\n            pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1;\n        } else if ((type & 0x08) != 0) {\n            pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED;\n            pSubframe->lpcOrder = (ma_uint8)(type & 0x07);\n            if (pSubframe->lpcOrder > 4) {\n                pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED;\n                pSubframe->lpcOrder = 0;\n            }\n        } else {\n            pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED;\n        }\n    }\n    if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) {\n        return MA_FALSE;\n    }\n    pSubframe->wastedBitsPerSample = 0;\n    if ((header & 0x01) == 1) {\n        unsigned int wastedBitsPerSample;\n        if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) {\n            return MA_FALSE;\n        }\n        pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut)\n{\n    ma_dr_flac_subframe* pSubframe;\n    ma_uint32 subframeBitsPerSample;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(frame != NULL);\n    pSubframe = frame->subframes + subframeIndex;\n    if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) {\n        return MA_FALSE;\n    }\n    subframeBitsPerSample = frame->header.bitsPerSample;\n    if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {\n        subframeBitsPerSample += 1;\n    } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {\n        subframeBitsPerSample += 1;\n    }\n    if (subframeBitsPerSample > 32) {\n        return MA_FALSE;\n    }\n    if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {\n        return MA_FALSE;\n    }\n    subframeBitsPerSample -= pSubframe->wastedBitsPerSample;\n    pSubframe->pSamplesS32 = pDecodedSamplesOut;\n    switch (pSubframe->subframeType)\n    {\n        case MA_DR_FLAC_SUBFRAME_CONSTANT:\n        {\n            ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);\n        } break;\n        case MA_DR_FLAC_SUBFRAME_VERBATIM:\n        {\n            ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);\n        } break;\n        case MA_DR_FLAC_SUBFRAME_FIXED:\n        {\n            ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);\n        } break;\n        case MA_DR_FLAC_SUBFRAME_LPC:\n        {\n            ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);\n        } break;\n        default: return MA_FALSE;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex)\n{\n    ma_dr_flac_subframe* pSubframe;\n    ma_uint32 subframeBitsPerSample;\n    MA_DR_FLAC_ASSERT(bs != NULL);\n    MA_DR_FLAC_ASSERT(frame != NULL);\n    pSubframe = frame->subframes + subframeIndex;\n    if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) {\n        return MA_FALSE;\n    }\n    subframeBitsPerSample = frame->header.bitsPerSample;\n    if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {\n        subframeBitsPerSample += 1;\n    } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {\n        subframeBitsPerSample += 1;\n    }\n    if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {\n        return MA_FALSE;\n    }\n    subframeBitsPerSample -= pSubframe->wastedBitsPerSample;\n    pSubframe->pSamplesS32 = NULL;\n    switch (pSubframe->subframeType)\n    {\n        case MA_DR_FLAC_SUBFRAME_CONSTANT:\n        {\n            if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) {\n                return MA_FALSE;\n            }\n        } break;\n        case MA_DR_FLAC_SUBFRAME_VERBATIM:\n        {\n            unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample;\n            if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {\n                return MA_FALSE;\n            }\n        } break;\n        case MA_DR_FLAC_SUBFRAME_FIXED:\n        {\n            unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;\n            if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {\n                return MA_FALSE;\n            }\n            if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {\n                return MA_FALSE;\n            }\n        } break;\n        case MA_DR_FLAC_SUBFRAME_LPC:\n        {\n            ma_uint8 lpcPrecision;\n            unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;\n            if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {\n                return MA_FALSE;\n            }\n            if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) {\n                return MA_FALSE;\n            }\n            if (lpcPrecision == 15) {\n                return MA_FALSE;\n            }\n            lpcPrecision += 1;\n            bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5;\n            if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {\n                return MA_FALSE;\n            }\n            if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {\n                return MA_FALSE;\n            }\n        } break;\n        default: return MA_FALSE;\n    }\n    return MA_TRUE;\n}\nstatic MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment)\n{\n    ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};\n    MA_DR_FLAC_ASSERT(channelAssignment <= 10);\n    return lookup[channelAssignment];\n}\nstatic ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac)\n{\n    int channelCount;\n    int i;\n    ma_uint8 paddingSizeInBits;\n    ma_uint16 desiredCRC16;\n#ifndef MA_DR_FLAC_NO_CRC\n    ma_uint16 actualCRC16;\n#endif\n    MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes));\n    if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) {\n        return MA_ERROR;\n    }\n    channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);\n    if (channelCount != (int)pFlac->channels) {\n        return MA_ERROR;\n    }\n    for (i = 0; i < channelCount; ++i) {\n        if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) {\n            return MA_ERROR;\n        }\n    }\n    paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7);\n    if (paddingSizeInBits > 0) {\n        ma_uint8 padding = 0;\n        if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {\n            return MA_AT_END;\n        }\n    }\n#ifndef MA_DR_FLAC_NO_CRC\n    actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs);\n#endif\n    if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {\n        return MA_AT_END;\n    }\n#ifndef MA_DR_FLAC_NO_CRC\n    if (actualCRC16 != desiredCRC16) {\n        return MA_CRC_MISMATCH;\n    }\n#endif\n    pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;\n    return MA_SUCCESS;\n}\nstatic ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac)\n{\n    int channelCount;\n    int i;\n    ma_uint16 desiredCRC16;\n#ifndef MA_DR_FLAC_NO_CRC\n    ma_uint16 actualCRC16;\n#endif\n    channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);\n    for (i = 0; i < channelCount; ++i) {\n        if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) {\n            return MA_ERROR;\n        }\n    }\n    if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {\n        return MA_ERROR;\n    }\n#ifndef MA_DR_FLAC_NO_CRC\n    actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs);\n#endif\n    if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {\n        return MA_AT_END;\n    }\n#ifndef MA_DR_FLAC_NO_CRC\n    if (actualCRC16 != desiredCRC16) {\n        return MA_CRC_MISMATCH;\n    }\n#endif\n    return MA_SUCCESS;\n}\nstatic ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac)\n{\n    MA_DR_FLAC_ASSERT(pFlac != NULL);\n    for (;;) {\n        ma_result result;\n        if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {\n            return MA_FALSE;\n        }\n        result = ma_dr_flac__decode_flac_frame(pFlac);\n        if (result != MA_SUCCESS) {\n            if (result == MA_CRC_MISMATCH) {\n                continue;\n            } else {\n                return MA_FALSE;\n            }\n        }\n        return MA_TRUE;\n    }\n}\nstatic void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame)\n{\n    ma_uint64 firstPCMFrame;\n    ma_uint64 lastPCMFrame;\n    MA_DR_FLAC_ASSERT(pFlac != NULL);\n    firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber;\n    if (firstPCMFrame == 0) {\n        firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames;\n    }\n    lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames;\n    if (lastPCMFrame > 0) {\n        lastPCMFrame -= 1;\n    }\n    if (pFirstPCMFrame) {\n        *pFirstPCMFrame = firstPCMFrame;\n    }\n    if (pLastPCMFrame) {\n        *pLastPCMFrame = lastPCMFrame;\n    }\n}\nstatic ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac)\n{\n    ma_bool32 result;\n    MA_DR_FLAC_ASSERT(pFlac != NULL);\n    result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes);\n    MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));\n    pFlac->currentPCMFrame = 0;\n    return result;\n}\nstatic MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac)\n{\n    MA_DR_FLAC_ASSERT(pFlac != NULL);\n    return ma_dr_flac__seek_flac_frame(pFlac);\n}\nstatic ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek)\n{\n    ma_uint64 pcmFramesRead = 0;\n    while (pcmFramesToSeek > 0) {\n        if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {\n            if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {\n                break;\n            }\n        } else {\n            if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) {\n                pcmFramesRead   += pcmFramesToSeek;\n                pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek;\n                pcmFramesToSeek  = 0;\n            } else {\n                pcmFramesRead   += pFlac->currentFLACFrame.pcmFramesRemaining;\n                pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining;\n                pFlac->currentFLACFrame.pcmFramesRemaining = 0;\n            }\n        }\n    }\n    pFlac->currentPCMFrame += pcmFramesRead;\n    return pcmFramesRead;\n}\nstatic ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)\n{\n    ma_bool32 isMidFrame = MA_FALSE;\n    ma_uint64 runningPCMFrameCount;\n    MA_DR_FLAC_ASSERT(pFlac != NULL);\n    if (pcmFrameIndex >= pFlac->currentPCMFrame) {\n        runningPCMFrameCount = pFlac->currentPCMFrame;\n        if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {\n            if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {\n                return MA_FALSE;\n            }\n        } else {\n            isMidFrame = MA_TRUE;\n        }\n    } else {\n        runningPCMFrameCount = 0;\n        if (!ma_dr_flac__seek_to_first_frame(pFlac)) {\n            return MA_FALSE;\n        }\n        if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {\n            return MA_FALSE;\n        }\n    }\n    for (;;) {\n        ma_uint64 pcmFrameCountInThisFLACFrame;\n        ma_uint64 firstPCMFrameInFLACFrame = 0;\n        ma_uint64 lastPCMFrameInFLACFrame = 0;\n        ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);\n        pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;\n        if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {\n            ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;\n            if (!isMidFrame) {\n                ma_result result = ma_dr_flac__decode_flac_frame(pFlac);\n                if (result == MA_SUCCESS) {\n                    return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;\n                } else {\n                    if (result == MA_CRC_MISMATCH) {\n                        goto next_iteration;\n                    } else {\n                        return MA_FALSE;\n                    }\n                }\n            } else {\n                return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;\n            }\n        } else {\n            if (!isMidFrame) {\n                ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);\n                if (result == MA_SUCCESS) {\n                    runningPCMFrameCount += pcmFrameCountInThisFLACFrame;\n                } else {\n                    if (result == MA_CRC_MISMATCH) {\n                        goto next_iteration;\n                    } else {\n                        return MA_FALSE;\n                    }\n                }\n            } else {\n                runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;\n                pFlac->currentFLACFrame.pcmFramesRemaining = 0;\n                isMidFrame = MA_FALSE;\n            }\n            if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {\n                return MA_TRUE;\n            }\n        }\n    next_iteration:\n        if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {\n            return MA_FALSE;\n        }\n    }\n}\n#if !defined(MA_DR_FLAC_NO_CRC)\n#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f\nstatic ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset)\n{\n    MA_DR_FLAC_ASSERT(pFlac != NULL);\n    MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL);\n    MA_DR_FLAC_ASSERT(targetByte >= rangeLo);\n    MA_DR_FLAC_ASSERT(targetByte <= rangeHi);\n    *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes;\n    for (;;) {\n        ma_uint64 lastTargetByte = targetByte;\n        if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) {\n            if (targetByte == 0) {\n                ma_dr_flac__seek_to_first_frame(pFlac);\n                return MA_FALSE;\n            }\n            targetByte = rangeLo + ((rangeHi - rangeLo)/2);\n            rangeHi = targetByte;\n        } else {\n            MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));\n#if 1\n            if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {\n                targetByte = rangeLo + ((rangeHi - rangeLo)/2);\n                rangeHi = targetByte;\n            } else {\n                break;\n            }\n#else\n            if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {\n                targetByte = rangeLo + ((rangeHi - rangeLo)/2);\n                rangeHi = targetByte;\n            } else {\n                break;\n            }\n#endif\n        }\n        if(targetByte == lastTargetByte) {\n            return MA_FALSE;\n        }\n    }\n    ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);\n    MA_DR_FLAC_ASSERT(targetByte <= rangeHi);\n    *pLastSuccessfulSeekOffset = targetByte;\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset)\n{\n#if 0\n    if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) {\n        if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) {\n            return MA_FALSE;\n        }\n    }\n#endif\n    return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset;\n}\nstatic ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi)\n{\n    ma_uint64 targetByte;\n    ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount;\n    ma_uint64 pcmRangeHi = 0;\n    ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1;\n    ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo;\n    ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;\n    targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO);\n    if (targetByte > byteRangeHi) {\n        targetByte = byteRangeHi;\n    }\n    for (;;) {\n        if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) {\n            ma_uint64 newPCMRangeLo;\n            ma_uint64 newPCMRangeHi;\n            ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi);\n            if (pcmRangeLo == newPCMRangeLo) {\n                if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) {\n                    break;\n                }\n                if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {\n                    return MA_TRUE;\n                } else {\n                    break;\n                }\n            }\n            pcmRangeLo = newPCMRangeLo;\n            pcmRangeHi = newPCMRangeHi;\n            if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) {\n                if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) {\n                    return MA_TRUE;\n                } else {\n                    break;\n                }\n            } else {\n                const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f);\n                if (pcmRangeLo > pcmFrameIndex) {\n                    byteRangeHi = lastSuccessfulSeekOffset;\n                    if (byteRangeLo > byteRangeHi) {\n                        byteRangeLo = byteRangeHi;\n                    }\n                    targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2);\n                    if (targetByte < byteRangeLo) {\n                        targetByte = byteRangeLo;\n                    }\n                } else  {\n                    if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) {\n                        if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {\n                            return MA_TRUE;\n                        } else {\n                            break;\n                        }\n                    } else {\n                        byteRangeLo = lastSuccessfulSeekOffset;\n                        if (byteRangeHi < byteRangeLo) {\n                            byteRangeHi = byteRangeLo;\n                        }\n                        targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio);\n                        if (targetByte > byteRangeHi) {\n                            targetByte = byteRangeHi;\n                        }\n                        if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) {\n                            closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset;\n                        }\n                    }\n                }\n            }\n        } else {\n            break;\n        }\n    }\n    ma_dr_flac__seek_to_first_frame(pFlac);\n    return MA_FALSE;\n}\nstatic ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)\n{\n    ma_uint64 byteRangeLo;\n    ma_uint64 byteRangeHi;\n    ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;\n    if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) {\n        return MA_FALSE;\n    }\n    if (pcmFrameIndex < seekForwardThreshold) {\n        return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex;\n    }\n    byteRangeLo = pFlac->firstFLACFramePosInBytes;\n    byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);\n    return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi);\n}\n#endif\nstatic ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)\n{\n    ma_uint32 iClosestSeekpoint = 0;\n    ma_bool32 isMidFrame = MA_FALSE;\n    ma_uint64 runningPCMFrameCount;\n    ma_uint32 iSeekpoint;\n    MA_DR_FLAC_ASSERT(pFlac != NULL);\n    if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {\n        return MA_FALSE;\n    }\n    if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) {\n        return MA_FALSE;\n    }\n    for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {\n        if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) {\n            break;\n        }\n        iClosestSeekpoint = iSeekpoint;\n    }\n    if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) {\n        return MA_FALSE;\n    }\n    if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) {\n        return MA_FALSE;\n    }\n#if !defined(MA_DR_FLAC_NO_CRC)\n    if (pFlac->totalPCMFrameCount > 0) {\n        ma_uint64 byteRangeLo;\n        ma_uint64 byteRangeHi;\n        byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);\n        byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset;\n        if (iClosestSeekpoint < pFlac->seekpointCount-1) {\n            ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1;\n            if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) {\n                return MA_FALSE;\n            }\n            if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) {\n                byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1;\n            }\n        }\n        if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {\n            if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {\n                ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);\n                if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) {\n                    return MA_TRUE;\n                }\n            }\n        }\n    }\n#endif\n    if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) {\n        runningPCMFrameCount = pFlac->currentPCMFrame;\n        if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {\n            if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {\n                return MA_FALSE;\n            }\n        } else {\n            isMidFrame = MA_TRUE;\n        }\n    } else {\n        runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame;\n        if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {\n            return MA_FALSE;\n        }\n        if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {\n            return MA_FALSE;\n        }\n    }\n    for (;;) {\n        ma_uint64 pcmFrameCountInThisFLACFrame;\n        ma_uint64 firstPCMFrameInFLACFrame = 0;\n        ma_uint64 lastPCMFrameInFLACFrame = 0;\n        ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);\n        pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;\n        if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {\n            ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;\n            if (!isMidFrame) {\n                ma_result result = ma_dr_flac__decode_flac_frame(pFlac);\n                if (result == MA_SUCCESS) {\n                    return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;\n                } else {\n                    if (result == MA_CRC_MISMATCH) {\n                        goto next_iteration;\n                    } else {\n                        return MA_FALSE;\n                    }\n                }\n            } else {\n                return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;\n            }\n        } else {\n            if (!isMidFrame) {\n                ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);\n                if (result == MA_SUCCESS) {\n                    runningPCMFrameCount += pcmFrameCountInThisFLACFrame;\n                } else {\n                    if (result == MA_CRC_MISMATCH) {\n                        goto next_iteration;\n                    } else {\n                        return MA_FALSE;\n                    }\n                }\n            } else {\n                runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;\n                pFlac->currentFLACFrame.pcmFramesRemaining = 0;\n                isMidFrame = MA_FALSE;\n            }\n            if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {\n                return MA_TRUE;\n            }\n        }\n    next_iteration:\n        if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {\n            return MA_FALSE;\n        }\n    }\n}\n#ifndef MA_DR_FLAC_NO_OGG\ntypedef struct\n{\n    ma_uint8 capturePattern[4];\n    ma_uint8 structureVersion;\n    ma_uint8 headerType;\n    ma_uint64 granulePosition;\n    ma_uint32 serialNumber;\n    ma_uint32 sequenceNumber;\n    ma_uint32 checksum;\n    ma_uint8 segmentCount;\n    ma_uint8 segmentTable[255];\n} ma_dr_flac_ogg_page_header;\n#endif\ntypedef struct\n{\n    ma_dr_flac_read_proc onRead;\n    ma_dr_flac_seek_proc onSeek;\n    ma_dr_flac_meta_proc onMeta;\n    ma_dr_flac_container container;\n    void* pUserData;\n    void* pUserDataMD;\n    ma_uint32 sampleRate;\n    ma_uint8  channels;\n    ma_uint8  bitsPerSample;\n    ma_uint64 totalPCMFrameCount;\n    ma_uint16 maxBlockSizeInPCMFrames;\n    ma_uint64 runningFilePos;\n    ma_bool32 hasStreamInfoBlock;\n    ma_bool32 hasMetadataBlocks;\n    ma_dr_flac_bs bs;\n    ma_dr_flac_frame_header firstFrameHeader;\n#ifndef MA_DR_FLAC_NO_OGG\n    ma_uint32 oggSerial;\n    ma_uint64 oggFirstBytePos;\n    ma_dr_flac_ogg_page_header oggBosHeader;\n#endif\n} ma_dr_flac_init_info;\nstatic MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize)\n{\n    blockHeader = ma_dr_flac__be2host_32(blockHeader);\n    *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31);\n    *blockType   = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24);\n    *blockSize   =                (blockHeader & 0x00FFFFFFUL);\n}\nstatic MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize)\n{\n    ma_uint32 blockHeader;\n    *blockSize = 0;\n    if (onRead(pUserData, &blockHeader, 4) != 4) {\n        return MA_FALSE;\n    }\n    ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize);\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo)\n{\n    ma_uint32 blockSizes;\n    ma_uint64 frameSizes = 0;\n    ma_uint64 importantProps;\n    ma_uint8 md5[16];\n    if (onRead(pUserData, &blockSizes, 4) != 4) {\n        return MA_FALSE;\n    }\n    if (onRead(pUserData, &frameSizes, 6) != 6) {\n        return MA_FALSE;\n    }\n    if (onRead(pUserData, &importantProps, 8) != 8) {\n        return MA_FALSE;\n    }\n    if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {\n        return MA_FALSE;\n    }\n    blockSizes     = ma_dr_flac__be2host_32(blockSizes);\n    frameSizes     = ma_dr_flac__be2host_64(frameSizes);\n    importantProps = ma_dr_flac__be2host_64(importantProps);\n    pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16);\n    pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF);\n    pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes     &  (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40);\n    pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes     &  (((ma_uint64)0x00FFFFFF << 16) <<  0)) >> 16);\n    pStreamInfo->sampleRate              = (ma_uint32)((importantProps &  (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44);\n    pStreamInfo->channels                = (ma_uint8 )((importantProps &  (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1;\n    pStreamInfo->bitsPerSample           = (ma_uint8 )((importantProps &  (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1;\n    pStreamInfo->totalPCMFrameCount      =                ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF)));\n    MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5));\n    return MA_TRUE;\n}\nstatic void* ma_dr_flac__malloc_default(size_t sz, void* pUserData)\n{\n    (void)pUserData;\n    return MA_DR_FLAC_MALLOC(sz);\n}\nstatic void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData)\n{\n    (void)pUserData;\n    return MA_DR_FLAC_REALLOC(p, sz);\n}\nstatic void ma_dr_flac__free_default(void* p, void* pUserData)\n{\n    (void)pUserData;\n    MA_DR_FLAC_FREE(p);\n}\nstatic void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks == NULL) {\n        return NULL;\n    }\n    if (pAllocationCallbacks->onMalloc != NULL) {\n        return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);\n    }\n    if (pAllocationCallbacks->onRealloc != NULL) {\n        return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);\n    }\n    return NULL;\n}\nstatic void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks == NULL) {\n        return NULL;\n    }\n    if (pAllocationCallbacks->onRealloc != NULL) {\n        return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);\n    }\n    if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {\n        void* p2;\n        p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);\n        if (p2 == NULL) {\n            return NULL;\n        }\n        if (p != NULL) {\n            MA_DR_FLAC_COPY_MEMORY(p2, p, szOld);\n            pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);\n        }\n        return p2;\n    }\n    return NULL;\n}\nstatic void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (p == NULL || pAllocationCallbacks == NULL) {\n        return;\n    }\n    if (pAllocationCallbacks->onFree != NULL) {\n        pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);\n    }\n}\nstatic ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_uint64 runningFilePos = 42;\n    ma_uint64 seektablePos   = 0;\n    ma_uint32 seektableSize  = 0;\n    for (;;) {\n        ma_dr_flac_metadata metadata;\n        ma_uint8 isLastBlock = 0;\n        ma_uint8 blockType = 0;\n        ma_uint32 blockSize;\n        if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) {\n            return MA_FALSE;\n        }\n        runningFilePos += 4;\n        metadata.type = blockType;\n        metadata.pRawData = NULL;\n        metadata.rawDataSize = 0;\n        switch (blockType)\n        {\n            case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION:\n            {\n                if (blockSize < 4) {\n                    return MA_FALSE;\n                }\n                if (onMeta) {\n                    void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);\n                    if (pRawData == NULL) {\n                        return MA_FALSE;\n                    }\n                    if (onRead(pUserData, pRawData, blockSize) != blockSize) {\n                        ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                        return MA_FALSE;\n                    }\n                    metadata.pRawData = pRawData;\n                    metadata.rawDataSize = blockSize;\n                    metadata.data.application.id       = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData);\n                    metadata.data.application.pData    = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32));\n                    metadata.data.application.dataSize = blockSize - sizeof(ma_uint32);\n                    onMeta(pUserDataMD, &metadata);\n                    ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                }\n            } break;\n            case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE:\n            {\n                seektablePos  = runningFilePos;\n                seektableSize = blockSize;\n                if (onMeta) {\n                    ma_uint32 seekpointCount;\n                    ma_uint32 iSeekpoint;\n                    void* pRawData;\n                    seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES;\n                    pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks);\n                    if (pRawData == NULL) {\n                        return MA_FALSE;\n                    }\n                    for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) {\n                        ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint;\n                        if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) {\n                            ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                            return MA_FALSE;\n                        }\n                        pSeekpoint->firstPCMFrame   = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame);\n                        pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset);\n                        pSeekpoint->pcmFrameCount   = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount);\n                    }\n                    metadata.pRawData = pRawData;\n                    metadata.rawDataSize = blockSize;\n                    metadata.data.seektable.seekpointCount = seekpointCount;\n                    metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData;\n                    onMeta(pUserDataMD, &metadata);\n                    ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                }\n            } break;\n            case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:\n            {\n                if (blockSize < 8) {\n                    return MA_FALSE;\n                }\n                if (onMeta) {\n                    void* pRawData;\n                    const char* pRunningData;\n                    const char* pRunningDataEnd;\n                    ma_uint32 i;\n                    pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);\n                    if (pRawData == NULL) {\n                        return MA_FALSE;\n                    }\n                    if (onRead(pUserData, pRawData, blockSize) != blockSize) {\n                        ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                        return MA_FALSE;\n                    }\n                    metadata.pRawData = pRawData;\n                    metadata.rawDataSize = blockSize;\n                    pRunningData    = (const char*)pRawData;\n                    pRunningDataEnd = (const char*)pRawData + blockSize;\n                    metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;\n                    if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) {\n                        ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                        return MA_FALSE;\n                    }\n                    metadata.data.vorbis_comment.vendor       = pRunningData;                                            pRunningData += metadata.data.vorbis_comment.vendorLength;\n                    metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;\n                    if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) {\n                        ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                        return MA_FALSE;\n                    }\n                    metadata.data.vorbis_comment.pComments    = pRunningData;\n                    for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {\n                        ma_uint32 commentLength;\n                        if (pRunningDataEnd - pRunningData < 4) {\n                            ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                            return MA_FALSE;\n                        }\n                        commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;\n                        if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) {\n                            ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                            return MA_FALSE;\n                        }\n                        pRunningData += commentLength;\n                    }\n                    onMeta(pUserDataMD, &metadata);\n                    ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                }\n            } break;\n            case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET:\n            {\n                if (blockSize < 396) {\n                    return MA_FALSE;\n                }\n                if (onMeta) {\n                    void* pRawData;\n                    const char* pRunningData;\n                    const char* pRunningDataEnd;\n                    size_t bufferSize;\n                    ma_uint8 iTrack;\n                    ma_uint8 iIndex;\n                    void* pTrackData;\n                    pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);\n                    if (pRawData == NULL) {\n                        return MA_FALSE;\n                    }\n                    if (onRead(pUserData, pRawData, blockSize) != blockSize) {\n                        ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                        return MA_FALSE;\n                    }\n                    metadata.pRawData = pRawData;\n                    metadata.rawDataSize = blockSize;\n                    pRunningData    = (const char*)pRawData;\n                    pRunningDataEnd = (const char*)pRawData + blockSize;\n                    MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128);                              pRunningData += 128;\n                    metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8;\n                    metadata.data.cuesheet.isCD              = (pRunningData[0] & 0x80) != 0;                           pRunningData += 259;\n                    metadata.data.cuesheet.trackCount        = pRunningData[0];                                         pRunningData += 1;\n                    metadata.data.cuesheet.pTrackData        = NULL;\n                    {\n                        const char* pRunningDataSaved = pRunningData;\n                        bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES;\n                        for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {\n                            ma_uint8 indexCount;\n                            ma_uint32 indexPointSize;\n                            if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) {\n                                ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                                return MA_FALSE;\n                            }\n                            pRunningData += 35;\n                            indexCount = pRunningData[0];\n                            pRunningData += 1;\n                            bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index);\n                            indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES;\n                            if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) {\n                                ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                                return MA_FALSE;\n                            }\n                            pRunningData += indexPointSize;\n                        }\n                        pRunningData = pRunningDataSaved;\n                    }\n                    {\n                        char* pRunningTrackData;\n                        pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks);\n                        if (pTrackData == NULL) {\n                            ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                            return MA_FALSE;\n                        }\n                        pRunningTrackData = (char*)pTrackData;\n                        for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {\n                            ma_uint8 indexCount;\n                            MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES);\n                            pRunningData      += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1;\n                            pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1;\n                            indexCount = pRunningData[0];\n                            pRunningData      += 1;\n                            pRunningTrackData += 1;\n                            for (iIndex = 0; iIndex < indexCount; ++iIndex) {\n                                ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData;\n                                MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES);\n                                pRunningData      += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES;\n                                pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index);\n                                pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset);\n                            }\n                        }\n                        metadata.data.cuesheet.pTrackData = pTrackData;\n                    }\n                    ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                    pRawData = NULL;\n                    onMeta(pUserDataMD, &metadata);\n                    ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks);\n                    pTrackData = NULL;\n                }\n            } break;\n            case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE:\n            {\n                if (blockSize < 32) {\n                    return MA_FALSE;\n                }\n                if (onMeta) {\n                    void* pRawData;\n                    const char* pRunningData;\n                    const char* pRunningDataEnd;\n                    pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);\n                    if (pRawData == NULL) {\n                        return MA_FALSE;\n                    }\n                    if (onRead(pUserData, pRawData, blockSize) != blockSize) {\n                        ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                        return MA_FALSE;\n                    }\n                    metadata.pRawData = pRawData;\n                    metadata.rawDataSize = blockSize;\n                    pRunningData    = (const char*)pRawData;\n                    pRunningDataEnd = (const char*)pRawData + blockSize;\n                    metadata.data.picture.type       = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;\n                    metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;\n                    if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) {\n                        ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                        return MA_FALSE;\n                    }\n                    metadata.data.picture.mime              = pRunningData;                                   pRunningData += metadata.data.picture.mimeLength;\n                    metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;\n                    if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) {\n                        ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                        return MA_FALSE;\n                    }\n                    metadata.data.picture.description     = pRunningData;                                   pRunningData += metadata.data.picture.descriptionLength;\n                    metadata.data.picture.width           = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;\n                    metadata.data.picture.height          = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;\n                    metadata.data.picture.colorDepth      = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;\n                    metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;\n                    metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;\n                    metadata.data.picture.pPictureData    = (const ma_uint8*)pRunningData;\n                    if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) {\n                        ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                        return MA_FALSE;\n                    }\n                    onMeta(pUserDataMD, &metadata);\n                    ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                }\n            } break;\n            case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING:\n            {\n                if (onMeta) {\n                    metadata.data.padding.unused = 0;\n                    if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) {\n                        isLastBlock = MA_TRUE;\n                    } else {\n                        onMeta(pUserDataMD, &metadata);\n                    }\n                }\n            } break;\n            case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID:\n            {\n                if (onMeta) {\n                    if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) {\n                        isLastBlock = MA_TRUE;\n                    }\n                }\n            } break;\n            default:\n            {\n                if (onMeta) {\n                    void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);\n                    if (pRawData == NULL) {\n                        return MA_FALSE;\n                    }\n                    if (onRead(pUserData, pRawData, blockSize) != blockSize) {\n                        ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                        return MA_FALSE;\n                    }\n                    metadata.pRawData = pRawData;\n                    metadata.rawDataSize = blockSize;\n                    onMeta(pUserDataMD, &metadata);\n                    ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);\n                }\n            } break;\n        }\n        if (onMeta == NULL && blockSize > 0) {\n            if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) {\n                isLastBlock = MA_TRUE;\n            }\n        }\n        runningFilePos += blockSize;\n        if (isLastBlock) {\n            break;\n        }\n    }\n    *pSeektablePos   = seektablePos;\n    *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES;\n    *pFirstFramePos  = runningFilePos;\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed)\n{\n    ma_uint8 isLastBlock;\n    ma_uint8 blockType;\n    ma_uint32 blockSize;\n    (void)onSeek;\n    pInit->container = ma_dr_flac_container_native;\n    if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {\n        return MA_FALSE;\n    }\n    if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {\n        if (!relaxed) {\n            return MA_FALSE;\n        } else {\n            pInit->hasStreamInfoBlock = MA_FALSE;\n            pInit->hasMetadataBlocks  = MA_FALSE;\n            if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {\n                return MA_FALSE;\n            }\n            if (pInit->firstFrameHeader.bitsPerSample == 0) {\n                return MA_FALSE;\n            }\n            pInit->sampleRate              = pInit->firstFrameHeader.sampleRate;\n            pInit->channels                = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment);\n            pInit->bitsPerSample           = pInit->firstFrameHeader.bitsPerSample;\n            pInit->maxBlockSizeInPCMFrames = 65535;\n            return MA_TRUE;\n        }\n    } else {\n        ma_dr_flac_streaminfo streaminfo;\n        if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) {\n            return MA_FALSE;\n        }\n        pInit->hasStreamInfoBlock      = MA_TRUE;\n        pInit->sampleRate              = streaminfo.sampleRate;\n        pInit->channels                = streaminfo.channels;\n        pInit->bitsPerSample           = streaminfo.bitsPerSample;\n        pInit->totalPCMFrameCount      = streaminfo.totalPCMFrameCount;\n        pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;\n        pInit->hasMetadataBlocks       = !isLastBlock;\n        if (onMeta) {\n            ma_dr_flac_metadata metadata;\n            metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO;\n            metadata.pRawData = NULL;\n            metadata.rawDataSize = 0;\n            metadata.data.streaminfo = streaminfo;\n            onMeta(pUserDataMD, &metadata);\n        }\n        return MA_TRUE;\n    }\n}\n#ifndef MA_DR_FLAC_NO_OGG\n#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE            65307\n#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32    1605413199\ntypedef enum\n{\n    ma_dr_flac_ogg_recover_on_crc_mismatch,\n    ma_dr_flac_ogg_fail_on_crc_mismatch\n} ma_dr_flac_ogg_crc_mismatch_recovery;\n#ifndef MA_DR_FLAC_NO_CRC\nstatic ma_uint32 ma_dr_flac__crc32_table[] = {\n    0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,\n    0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,\n    0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,\n    0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,\n    0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,\n    0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,\n    0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,\n    0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,\n    0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,\n    0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,\n    0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,\n    0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,\n    0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,\n    0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,\n    0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,\n    0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,\n    0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,\n    0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,\n    0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,\n    0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,\n    0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,\n    0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,\n    0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,\n    0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,\n    0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,\n    0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,\n    0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,\n    0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,\n    0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,\n    0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,\n    0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,\n    0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,\n    0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,\n    0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,\n    0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,\n    0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,\n    0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,\n    0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,\n    0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,\n    0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,\n    0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,\n    0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,\n    0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,\n    0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,\n    0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,\n    0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,\n    0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,\n    0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,\n    0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,\n    0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,\n    0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,\n    0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,\n    0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,\n    0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,\n    0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,\n    0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,\n    0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,\n    0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,\n    0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,\n    0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,\n    0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,\n    0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,\n    0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,\n    0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L\n};\n#endif\nstatic MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data)\n{\n#ifndef MA_DR_FLAC_NO_CRC\n    return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data];\n#else\n    (void)data;\n    return crc32;\n#endif\n}\n#if 0\nstatic MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data)\n{\n    crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF));\n    crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF));\n    crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >>  8) & 0xFF));\n    crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >>  0) & 0xFF));\n    return crc32;\n}\nstatic MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data)\n{\n    crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF));\n    crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >>  0) & 0xFFFFFFFF));\n    return crc32;\n}\n#endif\nstatic MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize)\n{\n    ma_uint32 i;\n    for (i = 0; i < dataSize; ++i) {\n        crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]);\n    }\n    return crc32;\n}\nstatic MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4])\n{\n    return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S';\n}\nstatic MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader)\n{\n    return 27 + pHeader->segmentCount;\n}\nstatic MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader)\n{\n    ma_uint32 pageBodySize = 0;\n    int i;\n    for (i = 0; i < pHeader->segmentCount; ++i) {\n        pageBodySize += pHeader->segmentTable[i];\n    }\n    return pageBodySize;\n}\nstatic ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32)\n{\n    ma_uint8 data[23];\n    ma_uint32 i;\n    MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32);\n    if (onRead(pUserData, data, 23) != 23) {\n        return MA_AT_END;\n    }\n    *pBytesRead += 23;\n    pHeader->capturePattern[0] = 'O';\n    pHeader->capturePattern[1] = 'g';\n    pHeader->capturePattern[2] = 'g';\n    pHeader->capturePattern[3] = 'S';\n    pHeader->structureVersion = data[0];\n    pHeader->headerType       = data[1];\n    MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8);\n    MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber,    &data[10], 4);\n    MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber,  &data[14], 4);\n    MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum,        &data[18], 4);\n    pHeader->segmentCount     = data[22];\n    data[18] = 0;\n    data[19] = 0;\n    data[20] = 0;\n    data[21] = 0;\n    for (i = 0; i < 23; ++i) {\n        *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]);\n    }\n    if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {\n        return MA_AT_END;\n    }\n    *pBytesRead += pHeader->segmentCount;\n    for (i = 0; i < pHeader->segmentCount; ++i) {\n        *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]);\n    }\n    return MA_SUCCESS;\n}\nstatic ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32)\n{\n    ma_uint8 id[4];\n    *pBytesRead = 0;\n    if (onRead(pUserData, id, 4) != 4) {\n        return MA_AT_END;\n    }\n    *pBytesRead += 4;\n    for (;;) {\n        if (ma_dr_flac_ogg__is_capture_pattern(id)) {\n            ma_result result;\n            *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32;\n            result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);\n            if (result == MA_SUCCESS) {\n                return MA_SUCCESS;\n            } else {\n                if (result == MA_CRC_MISMATCH) {\n                    continue;\n                } else {\n                    return result;\n                }\n            }\n        } else {\n            id[0] = id[1];\n            id[1] = id[2];\n            id[2] = id[3];\n            if (onRead(pUserData, &id[3], 1) != 1) {\n                return MA_AT_END;\n            }\n            *pBytesRead += 1;\n        }\n    }\n}\ntypedef struct\n{\n    ma_dr_flac_read_proc onRead;\n    ma_dr_flac_seek_proc onSeek;\n    void* pUserData;\n    ma_uint64 currentBytePos;\n    ma_uint64 firstBytePos;\n    ma_uint32 serialNumber;\n    ma_dr_flac_ogg_page_header bosPageHeader;\n    ma_dr_flac_ogg_page_header currentPageHeader;\n    ma_uint32 bytesRemainingInPage;\n    ma_uint32 pageDataSize;\n    ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE];\n} ma_dr_flac_oggbs;\nstatic size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)\n{\n    size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead);\n    oggbs->currentBytePos += bytesActuallyRead;\n    return bytesActuallyRead;\n}\nstatic ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin)\n{\n    if (origin == ma_dr_flac_seek_origin_start) {\n        if (offset <= 0x7FFFFFFF) {\n            if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) {\n                return MA_FALSE;\n            }\n            oggbs->currentBytePos = offset;\n            return MA_TRUE;\n        } else {\n            if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) {\n                return MA_FALSE;\n            }\n            oggbs->currentBytePos = offset;\n            return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current);\n        }\n    } else {\n        while (offset > 0x7FFFFFFF) {\n            if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) {\n                return MA_FALSE;\n            }\n            oggbs->currentBytePos += 0x7FFFFFFF;\n            offset -= 0x7FFFFFFF;\n        }\n        if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) {\n            return MA_FALSE;\n        }\n        oggbs->currentBytePos += offset;\n        return MA_TRUE;\n    }\n}\nstatic ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod)\n{\n    ma_dr_flac_ogg_page_header header;\n    for (;;) {\n        ma_uint32 crc32 = 0;\n        ma_uint32 bytesRead;\n        ma_uint32 pageBodySize;\n#ifndef MA_DR_FLAC_NO_CRC\n        ma_uint32 actualCRC32;\n#endif\n        if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {\n            return MA_FALSE;\n        }\n        oggbs->currentBytePos += bytesRead;\n        pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header);\n        if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) {\n            continue;\n        }\n        if (header.serialNumber != oggbs->serialNumber) {\n            if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) {\n                return MA_FALSE;\n            }\n            continue;\n        }\n        if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) {\n            return MA_FALSE;\n        }\n        oggbs->pageDataSize = pageBodySize;\n#ifndef MA_DR_FLAC_NO_CRC\n        actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);\n        if (actualCRC32 != header.checksum) {\n            if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) {\n                continue;\n            } else {\n                ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch);\n                return MA_FALSE;\n            }\n        }\n#else\n        (void)recoveryMethod;\n#endif\n        oggbs->currentPageHeader = header;\n        oggbs->bytesRemainingInPage = pageBodySize;\n        return MA_TRUE;\n    }\n}\n#if 0\nstatic ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg)\n{\n    ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage;\n    ma_uint8 iSeg = 0;\n    ma_uint32 iByte = 0;\n    while (iByte < bytesConsumedInPage) {\n        ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];\n        if (iByte + segmentSize > bytesConsumedInPage) {\n            break;\n        } else {\n            iSeg += 1;\n            iByte += segmentSize;\n        }\n    }\n    *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte);\n    return iSeg;\n}\nstatic ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs)\n{\n    for (;;) {\n        ma_bool32 atEndOfPage = MA_FALSE;\n        ma_uint8 bytesRemainingInSeg;\n        ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg);\n        ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg;\n        for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) {\n            ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];\n            if (segmentSize < 255) {\n                if (iSeg == oggbs->currentPageHeader.segmentCount-1) {\n                    atEndOfPage = MA_TRUE;\n                }\n                break;\n            }\n            bytesToEndOfPacketOrPage += segmentSize;\n        }\n        ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current);\n        oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;\n        if (atEndOfPage) {\n            if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) {\n                return MA_FALSE;\n            }\n            if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {\n                return MA_TRUE;\n            }\n        } else {\n            return MA_TRUE;\n        }\n    }\n}\nstatic ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs)\n{\n    return ma_dr_flac_oggbs__seek_to_next_packet(oggbs);\n}\n#endif\nstatic size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)\n{\n    ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData;\n    ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut;\n    size_t bytesRead = 0;\n    MA_DR_FLAC_ASSERT(oggbs != NULL);\n    MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL);\n    while (bytesRead < bytesToRead) {\n        size_t bytesRemainingToRead = bytesToRead - bytesRead;\n        if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) {\n            MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead);\n            bytesRead += bytesRemainingToRead;\n            oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead;\n            break;\n        }\n        if (oggbs->bytesRemainingInPage > 0) {\n            MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage);\n            bytesRead += oggbs->bytesRemainingInPage;\n            pRunningBufferOut += oggbs->bytesRemainingInPage;\n            oggbs->bytesRemainingInPage = 0;\n        }\n        MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0);\n        if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {\n            break;\n        }\n    }\n    return bytesRead;\n}\nstatic ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin)\n{\n    ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData;\n    int bytesSeeked = 0;\n    MA_DR_FLAC_ASSERT(oggbs != NULL);\n    MA_DR_FLAC_ASSERT(offset >= 0);\n    if (origin == ma_dr_flac_seek_origin_start) {\n        if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) {\n            return MA_FALSE;\n        }\n        if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) {\n            return MA_FALSE;\n        }\n        return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current);\n    }\n    MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current);\n    while (bytesSeeked < offset) {\n        int bytesRemainingToSeek = offset - bytesSeeked;\n        MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0);\n        if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {\n            bytesSeeked += bytesRemainingToSeek;\n            (void)bytesSeeked;\n            oggbs->bytesRemainingInPage -= bytesRemainingToSeek;\n            break;\n        }\n        if (oggbs->bytesRemainingInPage > 0) {\n            bytesSeeked += (int)oggbs->bytesRemainingInPage;\n            oggbs->bytesRemainingInPage = 0;\n        }\n        MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0);\n        if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) {\n            return MA_FALSE;\n        }\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)\n{\n    ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;\n    ma_uint64 originalBytePos;\n    ma_uint64 runningGranulePosition;\n    ma_uint64 runningFrameBytePos;\n    ma_uint64 runningPCMFrameCount;\n    MA_DR_FLAC_ASSERT(oggbs != NULL);\n    originalBytePos = oggbs->currentBytePos;\n    if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) {\n        return MA_FALSE;\n    }\n    oggbs->bytesRemainingInPage = 0;\n    runningGranulePosition = 0;\n    for (;;) {\n        if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {\n            ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start);\n            return MA_FALSE;\n        }\n        runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize;\n        if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) {\n            break;\n        }\n        if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {\n            if (oggbs->currentPageHeader.segmentTable[0] >= 2) {\n                ma_uint8 firstBytesInPage[2];\n                firstBytesInPage[0] = oggbs->pageData[0];\n                firstBytesInPage[1] = oggbs->pageData[1];\n                if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) {\n                    runningGranulePosition = oggbs->currentPageHeader.granulePosition;\n                }\n                continue;\n            }\n        }\n    }\n    if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) {\n        return MA_FALSE;\n    }\n    if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {\n        return MA_FALSE;\n    }\n    runningPCMFrameCount = runningGranulePosition;\n    for (;;) {\n        ma_uint64 firstPCMFrameInFLACFrame = 0;\n        ma_uint64 lastPCMFrameInFLACFrame = 0;\n        ma_uint64 pcmFrameCountInThisFrame;\n        if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {\n            return MA_FALSE;\n        }\n        ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);\n        pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;\n        if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) {\n            ma_result result = ma_dr_flac__decode_flac_frame(pFlac);\n            if (result == MA_SUCCESS) {\n                pFlac->currentPCMFrame = pcmFrameIndex;\n                pFlac->currentFLACFrame.pcmFramesRemaining = 0;\n                return MA_TRUE;\n            } else {\n                return MA_FALSE;\n            }\n        }\n        if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) {\n            ma_result result = ma_dr_flac__decode_flac_frame(pFlac);\n            if (result == MA_SUCCESS) {\n                ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount);\n                if (pcmFramesToDecode == 0) {\n                    return MA_TRUE;\n                }\n                pFlac->currentPCMFrame = runningPCMFrameCount;\n                return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;\n            } else {\n                if (result == MA_CRC_MISMATCH) {\n                    continue;\n                } else {\n                    return MA_FALSE;\n                }\n            }\n        } else {\n            ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);\n            if (result == MA_SUCCESS) {\n                runningPCMFrameCount += pcmFrameCountInThisFrame;\n            } else {\n                if (result == MA_CRC_MISMATCH) {\n                    continue;\n                } else {\n                    return MA_FALSE;\n                }\n            }\n        }\n    }\n}\nstatic ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed)\n{\n    ma_dr_flac_ogg_page_header header;\n    ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32;\n    ma_uint32 bytesRead = 0;\n    (void)relaxed;\n    pInit->container = ma_dr_flac_container_ogg;\n    pInit->oggFirstBytePos = 0;\n    if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n    pInit->runningFilePos += bytesRead;\n    for (;;) {\n        int pageBodySize;\n        if ((header.headerType & 0x02) == 0) {\n            return MA_FALSE;\n        }\n        pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header);\n        if (pageBodySize == 51) {\n            ma_uint32 bytesRemainingInPage = pageBodySize;\n            ma_uint8 packetType;\n            if (onRead(pUserData, &packetType, 1) != 1) {\n                return MA_FALSE;\n            }\n            bytesRemainingInPage -= 1;\n            if (packetType == 0x7F) {\n                ma_uint8 sig[4];\n                if (onRead(pUserData, sig, 4) != 4) {\n                    return MA_FALSE;\n                }\n                bytesRemainingInPage -= 4;\n                if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') {\n                    ma_uint8 mappingVersion[2];\n                    if (onRead(pUserData, mappingVersion, 2) != 2) {\n                        return MA_FALSE;\n                    }\n                    if (mappingVersion[0] != 1) {\n                        return MA_FALSE;\n                    }\n                    if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) {\n                        return MA_FALSE;\n                    }\n                    if (onRead(pUserData, sig, 4) != 4) {\n                        return MA_FALSE;\n                    }\n                    if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') {\n                        ma_dr_flac_streaminfo streaminfo;\n                        ma_uint8 isLastBlock;\n                        ma_uint8 blockType;\n                        ma_uint32 blockSize;\n                        if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {\n                            return MA_FALSE;\n                        }\n                        if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {\n                            return MA_FALSE;\n                        }\n                        if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) {\n                            pInit->hasStreamInfoBlock      = MA_TRUE;\n                            pInit->sampleRate              = streaminfo.sampleRate;\n                            pInit->channels                = streaminfo.channels;\n                            pInit->bitsPerSample           = streaminfo.bitsPerSample;\n                            pInit->totalPCMFrameCount      = streaminfo.totalPCMFrameCount;\n                            pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;\n                            pInit->hasMetadataBlocks       = !isLastBlock;\n                            if (onMeta) {\n                                ma_dr_flac_metadata metadata;\n                                metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO;\n                                metadata.pRawData = NULL;\n                                metadata.rawDataSize = 0;\n                                metadata.data.streaminfo = streaminfo;\n                                onMeta(pUserDataMD, &metadata);\n                            }\n                            pInit->runningFilePos  += pageBodySize;\n                            pInit->oggFirstBytePos  = pInit->runningFilePos - 79;\n                            pInit->oggSerial        = header.serialNumber;\n                            pInit->oggBosHeader     = header;\n                            break;\n                        } else {\n                            return MA_FALSE;\n                        }\n                    } else {\n                        return MA_FALSE;\n                    }\n                } else {\n                    if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) {\n                        return MA_FALSE;\n                    }\n                }\n            } else {\n                if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) {\n                    return MA_FALSE;\n                }\n            }\n        } else {\n            if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) {\n                return MA_FALSE;\n            }\n        }\n        pInit->runningFilePos += pageBodySize;\n        if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {\n            return MA_FALSE;\n        }\n        pInit->runningFilePos += bytesRead;\n    }\n    pInit->hasMetadataBlocks = MA_TRUE;\n    return MA_TRUE;\n}\n#endif\nstatic ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD)\n{\n    ma_bool32 relaxed;\n    ma_uint8 id[4];\n    if (pInit == NULL || onRead == NULL || onSeek == NULL) {\n        return MA_FALSE;\n    }\n    MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit));\n    pInit->onRead       = onRead;\n    pInit->onSeek       = onSeek;\n    pInit->onMeta       = onMeta;\n    pInit->container    = container;\n    pInit->pUserData    = pUserData;\n    pInit->pUserDataMD  = pUserDataMD;\n    pInit->bs.onRead    = onRead;\n    pInit->bs.onSeek    = onSeek;\n    pInit->bs.pUserData = pUserData;\n    ma_dr_flac__reset_cache(&pInit->bs);\n    relaxed = container != ma_dr_flac_container_unknown;\n    for (;;) {\n        if (onRead(pUserData, id, 4) != 4) {\n            return MA_FALSE;\n        }\n        pInit->runningFilePos += 4;\n        if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') {\n            ma_uint8 header[6];\n            ma_uint8 flags;\n            ma_uint32 headerSize;\n            if (onRead(pUserData, header, 6) != 6) {\n                return MA_FALSE;\n            }\n            pInit->runningFilePos += 6;\n            flags = header[1];\n            MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4);\n            headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize));\n            if (flags & 0x10) {\n                headerSize += 10;\n            }\n            if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) {\n                return MA_FALSE;\n            }\n            pInit->runningFilePos += headerSize;\n        } else {\n            break;\n        }\n    }\n    if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') {\n        return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);\n    }\n#ifndef MA_DR_FLAC_NO_OGG\n    if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') {\n        return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);\n    }\n#endif\n    if (relaxed) {\n        if (container == ma_dr_flac_container_native) {\n            return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);\n        }\n#ifndef MA_DR_FLAC_NO_OGG\n        if (container == ma_dr_flac_container_ogg) {\n            return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);\n        }\n#endif\n    }\n    return MA_FALSE;\n}\nstatic void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit)\n{\n    MA_DR_FLAC_ASSERT(pFlac != NULL);\n    MA_DR_FLAC_ASSERT(pInit != NULL);\n    MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac));\n    pFlac->bs                      = pInit->bs;\n    pFlac->onMeta                  = pInit->onMeta;\n    pFlac->pUserDataMD             = pInit->pUserDataMD;\n    pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames;\n    pFlac->sampleRate              = pInit->sampleRate;\n    pFlac->channels                = (ma_uint8)pInit->channels;\n    pFlac->bitsPerSample           = (ma_uint8)pInit->bitsPerSample;\n    pFlac->totalPCMFrameCount      = pInit->totalPCMFrameCount;\n    pFlac->container               = pInit->container;\n}\nstatic ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac_init_info init;\n    ma_uint32 allocationSize;\n    ma_uint32 wholeSIMDVectorCountPerChannel;\n    ma_uint32 decodedSamplesAllocationSize;\n#ifndef MA_DR_FLAC_NO_OGG\n    ma_dr_flac_oggbs* pOggbs = NULL;\n#endif\n    ma_uint64 firstFramePos;\n    ma_uint64 seektablePos;\n    ma_uint32 seekpointCount;\n    ma_allocation_callbacks allocationCallbacks;\n    ma_dr_flac* pFlac;\n    ma_dr_flac__init_cpu_caps();\n    if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {\n        return NULL;\n    }\n    if (pAllocationCallbacks != NULL) {\n        allocationCallbacks = *pAllocationCallbacks;\n        if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) {\n            return NULL;\n        }\n    } else {\n        allocationCallbacks.pUserData = NULL;\n        allocationCallbacks.onMalloc  = ma_dr_flac__malloc_default;\n        allocationCallbacks.onRealloc = ma_dr_flac__realloc_default;\n        allocationCallbacks.onFree    = ma_dr_flac__free_default;\n    }\n    allocationSize = sizeof(ma_dr_flac);\n    if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) {\n        wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32)));\n    } else {\n        wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1;\n    }\n    decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels;\n    allocationSize += decodedSamplesAllocationSize;\n    allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE;\n#ifndef MA_DR_FLAC_NO_OGG\n    if (init.container == ma_dr_flac_container_ogg) {\n        allocationSize += sizeof(ma_dr_flac_oggbs);\n        pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks);\n        if (pOggbs == NULL) {\n            return NULL;\n        }\n        MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs));\n        pOggbs->onRead = onRead;\n        pOggbs->onSeek = onSeek;\n        pOggbs->pUserData = pUserData;\n        pOggbs->currentBytePos = init.oggFirstBytePos;\n        pOggbs->firstBytePos = init.oggFirstBytePos;\n        pOggbs->serialNumber = init.oggSerial;\n        pOggbs->bosPageHeader = init.oggBosHeader;\n        pOggbs->bytesRemainingInPage = 0;\n    }\n#endif\n    firstFramePos  = 42;\n    seektablePos   = 0;\n    seekpointCount = 0;\n    if (init.hasMetadataBlocks) {\n        ma_dr_flac_read_proc onReadOverride = onRead;\n        ma_dr_flac_seek_proc onSeekOverride = onSeek;\n        void* pUserDataOverride = pUserData;\n#ifndef MA_DR_FLAC_NO_OGG\n        if (init.container == ma_dr_flac_container_ogg) {\n            onReadOverride = ma_dr_flac__on_read_ogg;\n            onSeekOverride = ma_dr_flac__on_seek_ogg;\n            pUserDataOverride = (void*)pOggbs;\n        }\n#endif\n        if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) {\n        #ifndef MA_DR_FLAC_NO_OGG\n            ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);\n        #endif\n            return NULL;\n        }\n        allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint);\n    }\n    pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks);\n    if (pFlac == NULL) {\n    #ifndef MA_DR_FLAC_NO_OGG\n        ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);\n    #endif\n        return NULL;\n    }\n    ma_dr_flac__init_from_info(pFlac, &init);\n    pFlac->allocationCallbacks = allocationCallbacks;\n    pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE);\n#ifndef MA_DR_FLAC_NO_OGG\n    if (init.container == ma_dr_flac_container_ogg) {\n        ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint)));\n        MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs));\n        ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);\n        pOggbs = NULL;\n        pFlac->bs.onRead = ma_dr_flac__on_read_ogg;\n        pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg;\n        pFlac->bs.pUserData = (void*)pInternalOggbs;\n        pFlac->_oggbs = (void*)pInternalOggbs;\n    }\n#endif\n    pFlac->firstFLACFramePosInBytes = firstFramePos;\n#ifndef MA_DR_FLAC_NO_OGG\n    if (init.container == ma_dr_flac_container_ogg)\n    {\n        pFlac->pSeekpoints = NULL;\n        pFlac->seekpointCount = 0;\n    }\n    else\n#endif\n    {\n        if (seektablePos != 0) {\n            pFlac->seekpointCount = seekpointCount;\n            pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize);\n            MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL);\n            MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL);\n            if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) {\n                ma_uint32 iSeekpoint;\n                for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) {\n                    if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) {\n                        pFlac->pSeekpoints[iSeekpoint].firstPCMFrame   = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame);\n                        pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset);\n                        pFlac->pSeekpoints[iSeekpoint].pcmFrameCount   = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount);\n                    } else {\n                        pFlac->pSeekpoints = NULL;\n                        pFlac->seekpointCount = 0;\n                        break;\n                    }\n                }\n                if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) {\n                    ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);\n                    return NULL;\n                }\n            } else {\n                pFlac->pSeekpoints = NULL;\n                pFlac->seekpointCount = 0;\n            }\n        }\n    }\n    if (!init.hasStreamInfoBlock) {\n        pFlac->currentFLACFrame.header = init.firstFrameHeader;\n        for (;;) {\n            ma_result result = ma_dr_flac__decode_flac_frame(pFlac);\n            if (result == MA_SUCCESS) {\n                break;\n            } else {\n                if (result == MA_CRC_MISMATCH) {\n                    if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {\n                        ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);\n                        return NULL;\n                    }\n                    continue;\n                } else {\n                    ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);\n                    return NULL;\n                }\n            }\n        }\n    }\n    return pFlac;\n}\n#ifndef MA_DR_FLAC_NO_STDIO\n#include <stdio.h>\n#ifndef MA_DR_FLAC_NO_WCHAR\n#include <wchar.h>\n#endif\nstatic size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)\n{\n    return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);\n}\nstatic ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin)\n{\n    MA_DR_FLAC_ASSERT(offset >= 0);\n    return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;\n}\nMA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    FILE* pFile;\n    if (ma_fopen(&pFile, pFileName, \"rb\") != MA_SUCCESS) {\n        return NULL;\n    }\n    pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        fclose(pFile);\n        return NULL;\n    }\n    return pFlac;\n}\n#ifndef MA_DR_FLAC_NO_WCHAR\nMA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    FILE* pFile;\n    if (ma_wfopen(&pFile, pFileName, L\"rb\", pAllocationCallbacks) != MA_SUCCESS) {\n        return NULL;\n    }\n    pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        fclose(pFile);\n        return NULL;\n    }\n    return pFlac;\n}\n#endif\nMA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    FILE* pFile;\n    if (ma_fopen(&pFile, pFileName, \"rb\") != MA_SUCCESS) {\n        return NULL;\n    }\n    pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        fclose(pFile);\n        return pFlac;\n    }\n    return pFlac;\n}\n#ifndef MA_DR_FLAC_NO_WCHAR\nMA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    FILE* pFile;\n    if (ma_wfopen(&pFile, pFileName, L\"rb\", pAllocationCallbacks) != MA_SUCCESS) {\n        return NULL;\n    }\n    pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        fclose(pFile);\n        return pFlac;\n    }\n    return pFlac;\n}\n#endif\n#endif\nstatic size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)\n{\n    ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData;\n    size_t bytesRemaining;\n    MA_DR_FLAC_ASSERT(memoryStream != NULL);\n    MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos);\n    bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;\n    if (bytesToRead > bytesRemaining) {\n        bytesToRead = bytesRemaining;\n    }\n    if (bytesToRead > 0) {\n        MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);\n        memoryStream->currentReadPos += bytesToRead;\n    }\n    return bytesToRead;\n}\nstatic ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin)\n{\n    ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData;\n    MA_DR_FLAC_ASSERT(memoryStream != NULL);\n    MA_DR_FLAC_ASSERT(offset >= 0);\n    if (offset > (ma_int64)memoryStream->dataSize) {\n        return MA_FALSE;\n    }\n    if (origin == ma_dr_flac_seek_origin_current) {\n        if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {\n            memoryStream->currentReadPos += offset;\n        } else {\n            return MA_FALSE;\n        }\n    } else {\n        if ((ma_uint32)offset <= memoryStream->dataSize) {\n            memoryStream->currentReadPos = offset;\n        } else {\n            return MA_FALSE;\n        }\n    }\n    return MA_TRUE;\n}\nMA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac__memory_stream memoryStream;\n    ma_dr_flac* pFlac;\n    memoryStream.data = (const ma_uint8*)pData;\n    memoryStream.dataSize = dataSize;\n    memoryStream.currentReadPos = 0;\n    pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return NULL;\n    }\n    pFlac->memoryStream = memoryStream;\n#ifndef MA_DR_FLAC_NO_OGG\n    if (pFlac->container == ma_dr_flac_container_ogg)\n    {\n        ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;\n        oggbs->pUserData = &pFlac->memoryStream;\n    }\n    else\n#endif\n    {\n        pFlac->bs.pUserData = &pFlac->memoryStream;\n    }\n    return pFlac;\n}\nMA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac__memory_stream memoryStream;\n    ma_dr_flac* pFlac;\n    memoryStream.data = (const ma_uint8*)pData;\n    memoryStream.dataSize = dataSize;\n    memoryStream.currentReadPos = 0;\n    pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return NULL;\n    }\n    pFlac->memoryStream = memoryStream;\n#ifndef MA_DR_FLAC_NO_OGG\n    if (pFlac->container == ma_dr_flac_container_ogg)\n    {\n        ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;\n        oggbs->pUserData = &pFlac->memoryStream;\n    }\n    else\n#endif\n    {\n        pFlac->bs.pUserData = &pFlac->memoryStream;\n    }\n    return pFlac;\n}\nMA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks);\n}\nMA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks);\n}\nMA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks);\n}\nMA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks);\n}\nMA_API void ma_dr_flac_close(ma_dr_flac* pFlac)\n{\n    if (pFlac == NULL) {\n        return;\n    }\n#ifndef MA_DR_FLAC_NO_STDIO\n    if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) {\n        fclose((FILE*)pFlac->bs.pUserData);\n    }\n#ifndef MA_DR_FLAC_NO_OGG\n    if (pFlac->container == ma_dr_flac_container_ogg) {\n        ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;\n        MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg);\n        if (oggbs->onRead == ma_dr_flac__on_read_stdio) {\n            fclose((FILE*)oggbs->pUserData);\n        }\n    }\n#endif\n#endif\n    ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks);\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    for (i = 0; i < frameCount; ++i) {\n        ma_uint32 left  = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n        ma_uint32 side  = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n        ma_uint32 right = left - side;\n        pOutputSamples[i*2+0] = (ma_int32)left;\n        pOutputSamples[i*2+1] = (ma_int32)right;\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    for (i = 0; i < frameCount4; ++i) {\n        ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;\n        ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;\n        ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;\n        ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;\n        ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;\n        ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;\n        ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;\n        ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;\n        ma_uint32 right0 = left0 - side0;\n        ma_uint32 right1 = left1 - side1;\n        ma_uint32 right2 = left2 - side2;\n        ma_uint32 right3 = left3 - side3;\n        pOutputSamples[i*8+0] = (ma_int32)left0;\n        pOutputSamples[i*8+1] = (ma_int32)right0;\n        pOutputSamples[i*8+2] = (ma_int32)left1;\n        pOutputSamples[i*8+3] = (ma_int32)right1;\n        pOutputSamples[i*8+4] = (ma_int32)left2;\n        pOutputSamples[i*8+5] = (ma_int32)right2;\n        pOutputSamples[i*8+6] = (ma_int32)left3;\n        pOutputSamples[i*8+7] = (ma_int32)right3;\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 left  = pInputSamples0U32[i] << shift0;\n        ma_uint32 side  = pInputSamples1U32[i] << shift1;\n        ma_uint32 right = left - side;\n        pOutputSamples[i*2+0] = (ma_int32)left;\n        pOutputSamples[i*2+1] = (ma_int32)right;\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    for (i = 0; i < frameCount4; ++i) {\n        __m128i left  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);\n        __m128i side  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);\n        __m128i right = _mm_sub_epi32(left, side);\n        _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));\n        _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 left  = pInputSamples0U32[i] << shift0;\n        ma_uint32 side  = pInputSamples1U32[i] << shift1;\n        ma_uint32 right = left - side;\n        pOutputSamples[i*2+0] = (ma_int32)left;\n        pOutputSamples[i*2+1] = (ma_int32)right;\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    int32x4_t shift0_4;\n    int32x4_t shift1_4;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    shift0_4 = vdupq_n_s32(shift0);\n    shift1_4 = vdupq_n_s32(shift1);\n    for (i = 0; i < frameCount4; ++i) {\n        uint32x4_t left;\n        uint32x4_t side;\n        uint32x4_t right;\n        left  = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);\n        side  = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);\n        right = vsubq_u32(left, side);\n        ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 left  = pInputSamples0U32[i] << shift0;\n        ma_uint32 side  = pInputSamples1U32[i] << shift1;\n        ma_uint32 right = left - side;\n        pOutputSamples[i*2+0] = (ma_int32)left;\n        pOutputSamples[i*2+1] = (ma_int32)right;\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    for (i = 0; i < frameCount; ++i) {\n        ma_uint32 side  = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n        ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n        ma_uint32 left  = right + side;\n        pOutputSamples[i*2+0] = (ma_int32)left;\n        pOutputSamples[i*2+1] = (ma_int32)right;\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    for (i = 0; i < frameCount4; ++i) {\n        ma_uint32 side0  = pInputSamples0U32[i*4+0] << shift0;\n        ma_uint32 side1  = pInputSamples0U32[i*4+1] << shift0;\n        ma_uint32 side2  = pInputSamples0U32[i*4+2] << shift0;\n        ma_uint32 side3  = pInputSamples0U32[i*4+3] << shift0;\n        ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;\n        ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;\n        ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;\n        ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;\n        ma_uint32 left0 = right0 + side0;\n        ma_uint32 left1 = right1 + side1;\n        ma_uint32 left2 = right2 + side2;\n        ma_uint32 left3 = right3 + side3;\n        pOutputSamples[i*8+0] = (ma_int32)left0;\n        pOutputSamples[i*8+1] = (ma_int32)right0;\n        pOutputSamples[i*8+2] = (ma_int32)left1;\n        pOutputSamples[i*8+3] = (ma_int32)right1;\n        pOutputSamples[i*8+4] = (ma_int32)left2;\n        pOutputSamples[i*8+5] = (ma_int32)right2;\n        pOutputSamples[i*8+6] = (ma_int32)left3;\n        pOutputSamples[i*8+7] = (ma_int32)right3;\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 side  = pInputSamples0U32[i] << shift0;\n        ma_uint32 right = pInputSamples1U32[i] << shift1;\n        ma_uint32 left  = right + side;\n        pOutputSamples[i*2+0] = (ma_int32)left;\n        pOutputSamples[i*2+1] = (ma_int32)right;\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    for (i = 0; i < frameCount4; ++i) {\n        __m128i side  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);\n        __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);\n        __m128i left  = _mm_add_epi32(right, side);\n        _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));\n        _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 side  = pInputSamples0U32[i] << shift0;\n        ma_uint32 right = pInputSamples1U32[i] << shift1;\n        ma_uint32 left  = right + side;\n        pOutputSamples[i*2+0] = (ma_int32)left;\n        pOutputSamples[i*2+1] = (ma_int32)right;\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    int32x4_t shift0_4;\n    int32x4_t shift1_4;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    shift0_4 = vdupq_n_s32(shift0);\n    shift1_4 = vdupq_n_s32(shift1);\n    for (i = 0; i < frameCount4; ++i) {\n        uint32x4_t side;\n        uint32x4_t right;\n        uint32x4_t left;\n        side  = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);\n        right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);\n        left  = vaddq_u32(right, side);\n        ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 side  = pInputSamples0U32[i] << shift0;\n        ma_uint32 right = pInputSamples1U32[i] << shift1;\n        ma_uint32 left  = right + side;\n        pOutputSamples[i*2+0] = (ma_int32)left;\n        pOutputSamples[i*2+1] = (ma_int32)right;\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    for (ma_uint64 i = 0; i < frameCount; ++i) {\n        ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n        ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n        mid = (mid << 1) | (side & 0x01);\n        pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample);\n        pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample);\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_int32 shift = unusedBitsPerSample;\n    if (shift > 0) {\n        shift -= 1;\n        for (i = 0; i < frameCount4; ++i) {\n            ma_uint32 temp0L;\n            ma_uint32 temp1L;\n            ma_uint32 temp2L;\n            ma_uint32 temp3L;\n            ma_uint32 temp0R;\n            ma_uint32 temp1R;\n            ma_uint32 temp2R;\n            ma_uint32 temp3R;\n            ma_uint32 mid0  = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid1  = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid2  = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid3  = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid0 = (mid0 << 1) | (side0 & 0x01);\n            mid1 = (mid1 << 1) | (side1 & 0x01);\n            mid2 = (mid2 << 1) | (side2 & 0x01);\n            mid3 = (mid3 << 1) | (side3 & 0x01);\n            temp0L = (mid0 + side0) << shift;\n            temp1L = (mid1 + side1) << shift;\n            temp2L = (mid2 + side2) << shift;\n            temp3L = (mid3 + side3) << shift;\n            temp0R = (mid0 - side0) << shift;\n            temp1R = (mid1 - side1) << shift;\n            temp2R = (mid2 - side2) << shift;\n            temp3R = (mid3 - side3) << shift;\n            pOutputSamples[i*8+0] = (ma_int32)temp0L;\n            pOutputSamples[i*8+1] = (ma_int32)temp0R;\n            pOutputSamples[i*8+2] = (ma_int32)temp1L;\n            pOutputSamples[i*8+3] = (ma_int32)temp1R;\n            pOutputSamples[i*8+4] = (ma_int32)temp2L;\n            pOutputSamples[i*8+5] = (ma_int32)temp2R;\n            pOutputSamples[i*8+6] = (ma_int32)temp3L;\n            pOutputSamples[i*8+7] = (ma_int32)temp3R;\n        }\n    } else {\n        for (i = 0; i < frameCount4; ++i) {\n            ma_uint32 temp0L;\n            ma_uint32 temp1L;\n            ma_uint32 temp2L;\n            ma_uint32 temp3L;\n            ma_uint32 temp0R;\n            ma_uint32 temp1R;\n            ma_uint32 temp2R;\n            ma_uint32 temp3R;\n            ma_uint32 mid0  = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid1  = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid2  = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid3  = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid0 = (mid0 << 1) | (side0 & 0x01);\n            mid1 = (mid1 << 1) | (side1 & 0x01);\n            mid2 = (mid2 << 1) | (side2 & 0x01);\n            mid3 = (mid3 << 1) | (side3 & 0x01);\n            temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1);\n            temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1);\n            temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1);\n            temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1);\n            temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1);\n            temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1);\n            temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1);\n            temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1);\n            pOutputSamples[i*8+0] = (ma_int32)temp0L;\n            pOutputSamples[i*8+1] = (ma_int32)temp0R;\n            pOutputSamples[i*8+2] = (ma_int32)temp1L;\n            pOutputSamples[i*8+3] = (ma_int32)temp1R;\n            pOutputSamples[i*8+4] = (ma_int32)temp2L;\n            pOutputSamples[i*8+5] = (ma_int32)temp2R;\n            pOutputSamples[i*8+6] = (ma_int32)temp3L;\n            pOutputSamples[i*8+7] = (ma_int32)temp3R;\n        }\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n        ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n        mid = (mid << 1) | (side & 0x01);\n        pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample);\n        pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample);\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_int32 shift = unusedBitsPerSample;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    if (shift == 0) {\n        for (i = 0; i < frameCount4; ++i) {\n            __m128i mid;\n            __m128i side;\n            __m128i left;\n            __m128i right;\n            mid   = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n            side  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n            mid   = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));\n            left  = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);\n            right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);\n            _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));\n            _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1;\n            pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1;\n        }\n    } else {\n        shift -= 1;\n        for (i = 0; i < frameCount4; ++i) {\n            __m128i mid;\n            __m128i side;\n            __m128i left;\n            __m128i right;\n            mid   = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n            side  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n            mid   = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));\n            left  = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);\n            right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);\n            _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));\n            _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift);\n            pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift);\n        }\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_int32 shift = unusedBitsPerSample;\n    int32x4_t  wbpsShift0_4;\n    int32x4_t  wbpsShift1_4;\n    uint32x4_t one4;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n    wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n    one4         = vdupq_n_u32(1);\n    if (shift == 0) {\n        for (i = 0; i < frameCount4; ++i) {\n            uint32x4_t mid;\n            uint32x4_t side;\n            int32x4_t left;\n            int32x4_t right;\n            mid   = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);\n            side  = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);\n            mid   = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));\n            left  = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);\n            right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);\n            ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1;\n            pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1;\n        }\n    } else {\n        int32x4_t shift4;\n        shift -= 1;\n        shift4 = vdupq_n_s32(shift);\n        for (i = 0; i < frameCount4; ++i) {\n            uint32x4_t mid;\n            uint32x4_t side;\n            int32x4_t left;\n            int32x4_t right;\n            mid   = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);\n            side  = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);\n            mid   = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));\n            left  = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));\n            right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));\n            ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift);\n            pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift);\n        }\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    for (ma_uint64 i = 0; i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample));\n        pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample));\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    for (i = 0; i < frameCount4; ++i) {\n        ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;\n        ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;\n        ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;\n        ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;\n        ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;\n        ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;\n        ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;\n        ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;\n        pOutputSamples[i*8+0] = (ma_int32)tempL0;\n        pOutputSamples[i*8+1] = (ma_int32)tempR0;\n        pOutputSamples[i*8+2] = (ma_int32)tempL1;\n        pOutputSamples[i*8+3] = (ma_int32)tempR1;\n        pOutputSamples[i*8+4] = (ma_int32)tempL2;\n        pOutputSamples[i*8+5] = (ma_int32)tempR2;\n        pOutputSamples[i*8+6] = (ma_int32)tempL3;\n        pOutputSamples[i*8+7] = (ma_int32)tempR3;\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);\n        pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    for (i = 0; i < frameCount4; ++i) {\n        __m128i left  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);\n        __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);\n        _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));\n        _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);\n        pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    int32x4_t shift4_0 = vdupq_n_s32(shift0);\n    int32x4_t shift4_1 = vdupq_n_s32(shift1);\n    for (i = 0; i < frameCount4; ++i) {\n        int32x4_t left;\n        int32x4_t right;\n        left  = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0));\n        right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1));\n        ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);\n        pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\nMA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut)\n{\n    ma_uint64 framesRead;\n    ma_uint32 unusedBitsPerSample;\n    if (pFlac == NULL || framesToRead == 0) {\n        return 0;\n    }\n    if (pBufferOut == NULL) {\n        return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);\n    }\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);\n    unusedBitsPerSample = 32 - pFlac->bitsPerSample;\n    framesRead = 0;\n    while (framesToRead > 0) {\n        if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {\n            if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {\n                break;\n            }\n        } else {\n            unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);\n            ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;\n            ma_uint64 frameCountThisIteration = framesToRead;\n            if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {\n                frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;\n            }\n            if (channelCount == 2) {\n                const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;\n                const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;\n                switch (pFlac->currentFLACFrame.header.channelAssignment)\n                {\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:\n                    {\n                        ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:\n                    {\n                        ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:\n                    {\n                        ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:\n                    default:\n                    {\n                        ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                }\n            } else {\n                ma_uint64 i;\n                for (i = 0; i < frameCountThisIteration; ++i) {\n                    unsigned int j;\n                    for (j = 0; j < channelCount; ++j) {\n                        pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));\n                    }\n                }\n            }\n            framesRead                += frameCountThisIteration;\n            pBufferOut                += frameCountThisIteration * channelCount;\n            framesToRead              -= frameCountThisIteration;\n            pFlac->currentPCMFrame    += frameCountThisIteration;\n            pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration;\n        }\n    }\n    return framesRead;\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    for (i = 0; i < frameCount; ++i) {\n        ma_uint32 left  = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n        ma_uint32 side  = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n        ma_uint32 right = left - side;\n        left  >>= 16;\n        right >>= 16;\n        pOutputSamples[i*2+0] = (ma_int16)left;\n        pOutputSamples[i*2+1] = (ma_int16)right;\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    for (i = 0; i < frameCount4; ++i) {\n        ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;\n        ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;\n        ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;\n        ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;\n        ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;\n        ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;\n        ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;\n        ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;\n        ma_uint32 right0 = left0 - side0;\n        ma_uint32 right1 = left1 - side1;\n        ma_uint32 right2 = left2 - side2;\n        ma_uint32 right3 = left3 - side3;\n        left0  >>= 16;\n        left1  >>= 16;\n        left2  >>= 16;\n        left3  >>= 16;\n        right0 >>= 16;\n        right1 >>= 16;\n        right2 >>= 16;\n        right3 >>= 16;\n        pOutputSamples[i*8+0] = (ma_int16)left0;\n        pOutputSamples[i*8+1] = (ma_int16)right0;\n        pOutputSamples[i*8+2] = (ma_int16)left1;\n        pOutputSamples[i*8+3] = (ma_int16)right1;\n        pOutputSamples[i*8+4] = (ma_int16)left2;\n        pOutputSamples[i*8+5] = (ma_int16)right2;\n        pOutputSamples[i*8+6] = (ma_int16)left3;\n        pOutputSamples[i*8+7] = (ma_int16)right3;\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 left  = pInputSamples0U32[i] << shift0;\n        ma_uint32 side  = pInputSamples1U32[i] << shift1;\n        ma_uint32 right = left - side;\n        left  >>= 16;\n        right >>= 16;\n        pOutputSamples[i*2+0] = (ma_int16)left;\n        pOutputSamples[i*2+1] = (ma_int16)right;\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    for (i = 0; i < frameCount4; ++i) {\n        __m128i left  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);\n        __m128i side  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);\n        __m128i right = _mm_sub_epi32(left, side);\n        left  = _mm_srai_epi32(left,  16);\n        right = _mm_srai_epi32(right, 16);\n        _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 left  = pInputSamples0U32[i] << shift0;\n        ma_uint32 side  = pInputSamples1U32[i] << shift1;\n        ma_uint32 right = left - side;\n        left  >>= 16;\n        right >>= 16;\n        pOutputSamples[i*2+0] = (ma_int16)left;\n        pOutputSamples[i*2+1] = (ma_int16)right;\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    int32x4_t shift0_4;\n    int32x4_t shift1_4;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    shift0_4 = vdupq_n_s32(shift0);\n    shift1_4 = vdupq_n_s32(shift1);\n    for (i = 0; i < frameCount4; ++i) {\n        uint32x4_t left;\n        uint32x4_t side;\n        uint32x4_t right;\n        left  = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);\n        side  = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);\n        right = vsubq_u32(left, side);\n        left  = vshrq_n_u32(left,  16);\n        right = vshrq_n_u32(right, 16);\n        ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 left  = pInputSamples0U32[i] << shift0;\n        ma_uint32 side  = pInputSamples1U32[i] << shift1;\n        ma_uint32 right = left - side;\n        left  >>= 16;\n        right >>= 16;\n        pOutputSamples[i*2+0] = (ma_int16)left;\n        pOutputSamples[i*2+1] = (ma_int16)right;\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    for (i = 0; i < frameCount; ++i) {\n        ma_uint32 side  = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n        ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n        ma_uint32 left  = right + side;\n        left  >>= 16;\n        right >>= 16;\n        pOutputSamples[i*2+0] = (ma_int16)left;\n        pOutputSamples[i*2+1] = (ma_int16)right;\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    for (i = 0; i < frameCount4; ++i) {\n        ma_uint32 side0  = pInputSamples0U32[i*4+0] << shift0;\n        ma_uint32 side1  = pInputSamples0U32[i*4+1] << shift0;\n        ma_uint32 side2  = pInputSamples0U32[i*4+2] << shift0;\n        ma_uint32 side3  = pInputSamples0U32[i*4+3] << shift0;\n        ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;\n        ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;\n        ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;\n        ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;\n        ma_uint32 left0 = right0 + side0;\n        ma_uint32 left1 = right1 + side1;\n        ma_uint32 left2 = right2 + side2;\n        ma_uint32 left3 = right3 + side3;\n        left0  >>= 16;\n        left1  >>= 16;\n        left2  >>= 16;\n        left3  >>= 16;\n        right0 >>= 16;\n        right1 >>= 16;\n        right2 >>= 16;\n        right3 >>= 16;\n        pOutputSamples[i*8+0] = (ma_int16)left0;\n        pOutputSamples[i*8+1] = (ma_int16)right0;\n        pOutputSamples[i*8+2] = (ma_int16)left1;\n        pOutputSamples[i*8+3] = (ma_int16)right1;\n        pOutputSamples[i*8+4] = (ma_int16)left2;\n        pOutputSamples[i*8+5] = (ma_int16)right2;\n        pOutputSamples[i*8+6] = (ma_int16)left3;\n        pOutputSamples[i*8+7] = (ma_int16)right3;\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 side  = pInputSamples0U32[i] << shift0;\n        ma_uint32 right = pInputSamples1U32[i] << shift1;\n        ma_uint32 left  = right + side;\n        left  >>= 16;\n        right >>= 16;\n        pOutputSamples[i*2+0] = (ma_int16)left;\n        pOutputSamples[i*2+1] = (ma_int16)right;\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    for (i = 0; i < frameCount4; ++i) {\n        __m128i side  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);\n        __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);\n        __m128i left  = _mm_add_epi32(right, side);\n        left  = _mm_srai_epi32(left,  16);\n        right = _mm_srai_epi32(right, 16);\n        _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 side  = pInputSamples0U32[i] << shift0;\n        ma_uint32 right = pInputSamples1U32[i] << shift1;\n        ma_uint32 left  = right + side;\n        left  >>= 16;\n        right >>= 16;\n        pOutputSamples[i*2+0] = (ma_int16)left;\n        pOutputSamples[i*2+1] = (ma_int16)right;\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    int32x4_t shift0_4;\n    int32x4_t shift1_4;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    shift0_4 = vdupq_n_s32(shift0);\n    shift1_4 = vdupq_n_s32(shift1);\n    for (i = 0; i < frameCount4; ++i) {\n        uint32x4_t side;\n        uint32x4_t right;\n        uint32x4_t left;\n        side  = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);\n        right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);\n        left  = vaddq_u32(right, side);\n        left  = vshrq_n_u32(left,  16);\n        right = vshrq_n_u32(right, 16);\n        ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 side  = pInputSamples0U32[i] << shift0;\n        ma_uint32 right = pInputSamples1U32[i] << shift1;\n        ma_uint32 left  = right + side;\n        left  >>= 16;\n        right >>= 16;\n        pOutputSamples[i*2+0] = (ma_int16)left;\n        pOutputSamples[i*2+1] = (ma_int16)right;\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    for (ma_uint64 i = 0; i < frameCount; ++i) {\n        ma_uint32 mid  = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n        ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n        mid = (mid << 1) | (side & 0x01);\n        pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);\n        pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift = unusedBitsPerSample;\n    if (shift > 0) {\n        shift -= 1;\n        for (i = 0; i < frameCount4; ++i) {\n            ma_uint32 temp0L;\n            ma_uint32 temp1L;\n            ma_uint32 temp2L;\n            ma_uint32 temp3L;\n            ma_uint32 temp0R;\n            ma_uint32 temp1R;\n            ma_uint32 temp2R;\n            ma_uint32 temp3R;\n            ma_uint32 mid0  = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid1  = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid2  = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid3  = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid0 = (mid0 << 1) | (side0 & 0x01);\n            mid1 = (mid1 << 1) | (side1 & 0x01);\n            mid2 = (mid2 << 1) | (side2 & 0x01);\n            mid3 = (mid3 << 1) | (side3 & 0x01);\n            temp0L = (mid0 + side0) << shift;\n            temp1L = (mid1 + side1) << shift;\n            temp2L = (mid2 + side2) << shift;\n            temp3L = (mid3 + side3) << shift;\n            temp0R = (mid0 - side0) << shift;\n            temp1R = (mid1 - side1) << shift;\n            temp2R = (mid2 - side2) << shift;\n            temp3R = (mid3 - side3) << shift;\n            temp0L >>= 16;\n            temp1L >>= 16;\n            temp2L >>= 16;\n            temp3L >>= 16;\n            temp0R >>= 16;\n            temp1R >>= 16;\n            temp2R >>= 16;\n            temp3R >>= 16;\n            pOutputSamples[i*8+0] = (ma_int16)temp0L;\n            pOutputSamples[i*8+1] = (ma_int16)temp0R;\n            pOutputSamples[i*8+2] = (ma_int16)temp1L;\n            pOutputSamples[i*8+3] = (ma_int16)temp1R;\n            pOutputSamples[i*8+4] = (ma_int16)temp2L;\n            pOutputSamples[i*8+5] = (ma_int16)temp2R;\n            pOutputSamples[i*8+6] = (ma_int16)temp3L;\n            pOutputSamples[i*8+7] = (ma_int16)temp3R;\n        }\n    } else {\n        for (i = 0; i < frameCount4; ++i) {\n            ma_uint32 temp0L;\n            ma_uint32 temp1L;\n            ma_uint32 temp2L;\n            ma_uint32 temp3L;\n            ma_uint32 temp0R;\n            ma_uint32 temp1R;\n            ma_uint32 temp2R;\n            ma_uint32 temp3R;\n            ma_uint32 mid0  = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid1  = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid2  = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid3  = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid0 = (mid0 << 1) | (side0 & 0x01);\n            mid1 = (mid1 << 1) | (side1 & 0x01);\n            mid2 = (mid2 << 1) | (side2 & 0x01);\n            mid3 = (mid3 << 1) | (side3 & 0x01);\n            temp0L = ((ma_int32)(mid0 + side0) >> 1);\n            temp1L = ((ma_int32)(mid1 + side1) >> 1);\n            temp2L = ((ma_int32)(mid2 + side2) >> 1);\n            temp3L = ((ma_int32)(mid3 + side3) >> 1);\n            temp0R = ((ma_int32)(mid0 - side0) >> 1);\n            temp1R = ((ma_int32)(mid1 - side1) >> 1);\n            temp2R = ((ma_int32)(mid2 - side2) >> 1);\n            temp3R = ((ma_int32)(mid3 - side3) >> 1);\n            temp0L >>= 16;\n            temp1L >>= 16;\n            temp2L >>= 16;\n            temp3L >>= 16;\n            temp0R >>= 16;\n            temp1R >>= 16;\n            temp2R >>= 16;\n            temp3R >>= 16;\n            pOutputSamples[i*8+0] = (ma_int16)temp0L;\n            pOutputSamples[i*8+1] = (ma_int16)temp0R;\n            pOutputSamples[i*8+2] = (ma_int16)temp1L;\n            pOutputSamples[i*8+3] = (ma_int16)temp1R;\n            pOutputSamples[i*8+4] = (ma_int16)temp2L;\n            pOutputSamples[i*8+5] = (ma_int16)temp2R;\n            pOutputSamples[i*8+6] = (ma_int16)temp3L;\n            pOutputSamples[i*8+7] = (ma_int16)temp3R;\n        }\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n        ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n        mid = (mid << 1) | (side & 0x01);\n        pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);\n        pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift = unusedBitsPerSample;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    if (shift == 0) {\n        for (i = 0; i < frameCount4; ++i) {\n            __m128i mid;\n            __m128i side;\n            __m128i left;\n            __m128i right;\n            mid   = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n            side  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n            mid   = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));\n            left  = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);\n            right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);\n            left  = _mm_srai_epi32(left,  16);\n            right = _mm_srai_epi32(right, 16);\n            _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16);\n            pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16);\n        }\n    } else {\n        shift -= 1;\n        for (i = 0; i < frameCount4; ++i) {\n            __m128i mid;\n            __m128i side;\n            __m128i left;\n            __m128i right;\n            mid   = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n            side  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n            mid   = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));\n            left  = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);\n            right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);\n            left  = _mm_srai_epi32(left,  16);\n            right = _mm_srai_epi32(right, 16);\n            _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16);\n            pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16);\n        }\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift = unusedBitsPerSample;\n    int32x4_t wbpsShift0_4;\n    int32x4_t wbpsShift1_4;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n    wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n    if (shift == 0) {\n        for (i = 0; i < frameCount4; ++i) {\n            uint32x4_t mid;\n            uint32x4_t side;\n            int32x4_t left;\n            int32x4_t right;\n            mid   = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);\n            side  = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);\n            mid   = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));\n            left  = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);\n            right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);\n            left  = vshrq_n_s32(left,  16);\n            right = vshrq_n_s32(right, 16);\n            ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16);\n            pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16);\n        }\n    } else {\n        int32x4_t shift4;\n        shift -= 1;\n        shift4 = vdupq_n_s32(shift);\n        for (i = 0; i < frameCount4; ++i) {\n            uint32x4_t mid;\n            uint32x4_t side;\n            int32x4_t left;\n            int32x4_t right;\n            mid   = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);\n            side  = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);\n            mid   = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));\n            left  = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));\n            right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));\n            left  = vshrq_n_s32(left,  16);\n            right = vshrq_n_s32(right, 16);\n            ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16);\n            pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16);\n        }\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    for (ma_uint64 i = 0; i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16);\n        pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16);\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    for (i = 0; i < frameCount4; ++i) {\n        ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;\n        ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;\n        ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;\n        ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;\n        ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;\n        ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;\n        ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;\n        ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;\n        tempL0 >>= 16;\n        tempL1 >>= 16;\n        tempL2 >>= 16;\n        tempL3 >>= 16;\n        tempR0 >>= 16;\n        tempR1 >>= 16;\n        tempR2 >>= 16;\n        tempR3 >>= 16;\n        pOutputSamples[i*8+0] = (ma_int16)tempL0;\n        pOutputSamples[i*8+1] = (ma_int16)tempR0;\n        pOutputSamples[i*8+2] = (ma_int16)tempL1;\n        pOutputSamples[i*8+3] = (ma_int16)tempR1;\n        pOutputSamples[i*8+4] = (ma_int16)tempL2;\n        pOutputSamples[i*8+5] = (ma_int16)tempR2;\n        pOutputSamples[i*8+6] = (ma_int16)tempL3;\n        pOutputSamples[i*8+7] = (ma_int16)tempR3;\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);\n        pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    for (i = 0; i < frameCount4; ++i) {\n        __m128i left  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);\n        __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);\n        left  = _mm_srai_epi32(left,  16);\n        right = _mm_srai_epi32(right, 16);\n        _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);\n        pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    int32x4_t shift0_4 = vdupq_n_s32(shift0);\n    int32x4_t shift1_4 = vdupq_n_s32(shift1);\n    for (i = 0; i < frameCount4; ++i) {\n        int32x4_t left;\n        int32x4_t right;\n        left  = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));\n        right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));\n        left  = vshrq_n_s32(left,  16);\n        right = vshrq_n_s32(right, 16);\n        ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);\n        pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\nMA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut)\n{\n    ma_uint64 framesRead;\n    ma_uint32 unusedBitsPerSample;\n    if (pFlac == NULL || framesToRead == 0) {\n        return 0;\n    }\n    if (pBufferOut == NULL) {\n        return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);\n    }\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);\n    unusedBitsPerSample = 32 - pFlac->bitsPerSample;\n    framesRead = 0;\n    while (framesToRead > 0) {\n        if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {\n            if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {\n                break;\n            }\n        } else {\n            unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);\n            ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;\n            ma_uint64 frameCountThisIteration = framesToRead;\n            if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {\n                frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;\n            }\n            if (channelCount == 2) {\n                const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;\n                const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;\n                switch (pFlac->currentFLACFrame.header.channelAssignment)\n                {\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:\n                    {\n                        ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:\n                    {\n                        ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:\n                    {\n                        ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:\n                    default:\n                    {\n                        ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                }\n            } else {\n                ma_uint64 i;\n                for (i = 0; i < frameCountThisIteration; ++i) {\n                    unsigned int j;\n                    for (j = 0; j < channelCount; ++j) {\n                        ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));\n                        pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16);\n                    }\n                }\n            }\n            framesRead                += frameCountThisIteration;\n            pBufferOut                += frameCountThisIteration * channelCount;\n            framesToRead              -= frameCountThisIteration;\n            pFlac->currentPCMFrame    += frameCountThisIteration;\n            pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration;\n        }\n    }\n    return framesRead;\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    for (i = 0; i < frameCount; ++i) {\n        ma_uint32 left  = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n        ma_uint32 side  = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n        ma_uint32 right = left - side;\n        pOutputSamples[i*2+0] = (float)((ma_int32)left  / 2147483648.0);\n        pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0);\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    float factor = 1 / 2147483648.0;\n    for (i = 0; i < frameCount4; ++i) {\n        ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;\n        ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;\n        ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;\n        ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;\n        ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;\n        ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;\n        ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;\n        ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;\n        ma_uint32 right0 = left0 - side0;\n        ma_uint32 right1 = left1 - side1;\n        ma_uint32 right2 = left2 - side2;\n        ma_uint32 right3 = left3 - side3;\n        pOutputSamples[i*8+0] = (ma_int32)left0  * factor;\n        pOutputSamples[i*8+1] = (ma_int32)right0 * factor;\n        pOutputSamples[i*8+2] = (ma_int32)left1  * factor;\n        pOutputSamples[i*8+3] = (ma_int32)right1 * factor;\n        pOutputSamples[i*8+4] = (ma_int32)left2  * factor;\n        pOutputSamples[i*8+5] = (ma_int32)right2 * factor;\n        pOutputSamples[i*8+6] = (ma_int32)left3  * factor;\n        pOutputSamples[i*8+7] = (ma_int32)right3 * factor;\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 left  = pInputSamples0U32[i] << shift0;\n        ma_uint32 side  = pInputSamples1U32[i] << shift1;\n        ma_uint32 right = left - side;\n        pOutputSamples[i*2+0] = (ma_int32)left  * factor;\n        pOutputSamples[i*2+1] = (ma_int32)right * factor;\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;\n    ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;\n    __m128 factor;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    factor = _mm_set1_ps(1.0f / 8388608.0f);\n    for (i = 0; i < frameCount4; ++i) {\n        __m128i left  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);\n        __m128i side  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);\n        __m128i right = _mm_sub_epi32(left, side);\n        __m128 leftf  = _mm_mul_ps(_mm_cvtepi32_ps(left),  factor);\n        __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);\n        _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));\n        _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 left  = pInputSamples0U32[i] << shift0;\n        ma_uint32 side  = pInputSamples1U32[i] << shift1;\n        ma_uint32 right = left - side;\n        pOutputSamples[i*2+0] = (ma_int32)left  / 8388608.0f;\n        pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;\n    ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;\n    float32x4_t factor4;\n    int32x4_t shift0_4;\n    int32x4_t shift1_4;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    factor4  = vdupq_n_f32(1.0f / 8388608.0f);\n    shift0_4 = vdupq_n_s32(shift0);\n    shift1_4 = vdupq_n_s32(shift1);\n    for (i = 0; i < frameCount4; ++i) {\n        uint32x4_t left;\n        uint32x4_t side;\n        uint32x4_t right;\n        float32x4_t leftf;\n        float32x4_t rightf;\n        left   = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);\n        side   = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);\n        right  = vsubq_u32(left, side);\n        leftf  = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)),  factor4);\n        rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);\n        ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 left  = pInputSamples0U32[i] << shift0;\n        ma_uint32 side  = pInputSamples1U32[i] << shift1;\n        ma_uint32 right = left - side;\n        pOutputSamples[i*2+0] = (ma_int32)left  / 8388608.0f;\n        pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    for (i = 0; i < frameCount; ++i) {\n        ma_uint32 side  = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n        ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n        ma_uint32 left  = right + side;\n        pOutputSamples[i*2+0] = (float)((ma_int32)left  / 2147483648.0);\n        pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0);\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    float factor = 1 / 2147483648.0;\n    for (i = 0; i < frameCount4; ++i) {\n        ma_uint32 side0  = pInputSamples0U32[i*4+0] << shift0;\n        ma_uint32 side1  = pInputSamples0U32[i*4+1] << shift0;\n        ma_uint32 side2  = pInputSamples0U32[i*4+2] << shift0;\n        ma_uint32 side3  = pInputSamples0U32[i*4+3] << shift0;\n        ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;\n        ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;\n        ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;\n        ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;\n        ma_uint32 left0 = right0 + side0;\n        ma_uint32 left1 = right1 + side1;\n        ma_uint32 left2 = right2 + side2;\n        ma_uint32 left3 = right3 + side3;\n        pOutputSamples[i*8+0] = (ma_int32)left0  * factor;\n        pOutputSamples[i*8+1] = (ma_int32)right0 * factor;\n        pOutputSamples[i*8+2] = (ma_int32)left1  * factor;\n        pOutputSamples[i*8+3] = (ma_int32)right1 * factor;\n        pOutputSamples[i*8+4] = (ma_int32)left2  * factor;\n        pOutputSamples[i*8+5] = (ma_int32)right2 * factor;\n        pOutputSamples[i*8+6] = (ma_int32)left3  * factor;\n        pOutputSamples[i*8+7] = (ma_int32)right3 * factor;\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 side  = pInputSamples0U32[i] << shift0;\n        ma_uint32 right = pInputSamples1U32[i] << shift1;\n        ma_uint32 left  = right + side;\n        pOutputSamples[i*2+0] = (ma_int32)left  * factor;\n        pOutputSamples[i*2+1] = (ma_int32)right * factor;\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;\n    ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;\n    __m128 factor;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    factor = _mm_set1_ps(1.0f / 8388608.0f);\n    for (i = 0; i < frameCount4; ++i) {\n        __m128i side  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);\n        __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);\n        __m128i left  = _mm_add_epi32(right, side);\n        __m128 leftf  = _mm_mul_ps(_mm_cvtepi32_ps(left),  factor);\n        __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);\n        _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));\n        _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 side  = pInputSamples0U32[i] << shift0;\n        ma_uint32 right = pInputSamples1U32[i] << shift1;\n        ma_uint32 left  = right + side;\n        pOutputSamples[i*2+0] = (ma_int32)left  / 8388608.0f;\n        pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;\n    ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;\n    float32x4_t factor4;\n    int32x4_t shift0_4;\n    int32x4_t shift1_4;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    factor4  = vdupq_n_f32(1.0f / 8388608.0f);\n    shift0_4 = vdupq_n_s32(shift0);\n    shift1_4 = vdupq_n_s32(shift1);\n    for (i = 0; i < frameCount4; ++i) {\n        uint32x4_t side;\n        uint32x4_t right;\n        uint32x4_t left;\n        float32x4_t leftf;\n        float32x4_t rightf;\n        side   = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);\n        right  = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);\n        left   = vaddq_u32(right, side);\n        leftf  = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)),  factor4);\n        rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);\n        ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 side  = pInputSamples0U32[i] << shift0;\n        ma_uint32 right = pInputSamples1U32[i] << shift1;\n        ma_uint32 left  = right + side;\n        pOutputSamples[i*2+0] = (ma_int32)left  / 8388608.0f;\n        pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    for (ma_uint64 i = 0; i < frameCount; ++i) {\n        ma_uint32 mid  = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n        ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n        mid = (mid << 1) | (side & 0x01);\n        pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);\n        pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift = unusedBitsPerSample;\n    float factor = 1 / 2147483648.0;\n    if (shift > 0) {\n        shift -= 1;\n        for (i = 0; i < frameCount4; ++i) {\n            ma_uint32 temp0L;\n            ma_uint32 temp1L;\n            ma_uint32 temp2L;\n            ma_uint32 temp3L;\n            ma_uint32 temp0R;\n            ma_uint32 temp1R;\n            ma_uint32 temp2R;\n            ma_uint32 temp3R;\n            ma_uint32 mid0  = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid1  = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid2  = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid3  = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid0 = (mid0 << 1) | (side0 & 0x01);\n            mid1 = (mid1 << 1) | (side1 & 0x01);\n            mid2 = (mid2 << 1) | (side2 & 0x01);\n            mid3 = (mid3 << 1) | (side3 & 0x01);\n            temp0L = (mid0 + side0) << shift;\n            temp1L = (mid1 + side1) << shift;\n            temp2L = (mid2 + side2) << shift;\n            temp3L = (mid3 + side3) << shift;\n            temp0R = (mid0 - side0) << shift;\n            temp1R = (mid1 - side1) << shift;\n            temp2R = (mid2 - side2) << shift;\n            temp3R = (mid3 - side3) << shift;\n            pOutputSamples[i*8+0] = (ma_int32)temp0L * factor;\n            pOutputSamples[i*8+1] = (ma_int32)temp0R * factor;\n            pOutputSamples[i*8+2] = (ma_int32)temp1L * factor;\n            pOutputSamples[i*8+3] = (ma_int32)temp1R * factor;\n            pOutputSamples[i*8+4] = (ma_int32)temp2L * factor;\n            pOutputSamples[i*8+5] = (ma_int32)temp2R * factor;\n            pOutputSamples[i*8+6] = (ma_int32)temp3L * factor;\n            pOutputSamples[i*8+7] = (ma_int32)temp3R * factor;\n        }\n    } else {\n        for (i = 0; i < frameCount4; ++i) {\n            ma_uint32 temp0L;\n            ma_uint32 temp1L;\n            ma_uint32 temp2L;\n            ma_uint32 temp3L;\n            ma_uint32 temp0R;\n            ma_uint32 temp1R;\n            ma_uint32 temp2R;\n            ma_uint32 temp3R;\n            ma_uint32 mid0  = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid1  = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid2  = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 mid3  = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid0 = (mid0 << 1) | (side0 & 0x01);\n            mid1 = (mid1 << 1) | (side1 & 0x01);\n            mid2 = (mid2 << 1) | (side2 & 0x01);\n            mid3 = (mid3 << 1) | (side3 & 0x01);\n            temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1);\n            temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1);\n            temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1);\n            temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1);\n            temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1);\n            temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1);\n            temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1);\n            temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1);\n            pOutputSamples[i*8+0] = (ma_int32)temp0L * factor;\n            pOutputSamples[i*8+1] = (ma_int32)temp0R * factor;\n            pOutputSamples[i*8+2] = (ma_int32)temp1L * factor;\n            pOutputSamples[i*8+3] = (ma_int32)temp1R * factor;\n            pOutputSamples[i*8+4] = (ma_int32)temp2L * factor;\n            pOutputSamples[i*8+5] = (ma_int32)temp2R * factor;\n            pOutputSamples[i*8+6] = (ma_int32)temp3L * factor;\n            pOutputSamples[i*8+7] = (ma_int32)temp3R * factor;\n        }\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n        ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n        mid = (mid << 1) | (side & 0x01);\n        pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor;\n        pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor;\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift = unusedBitsPerSample - 8;\n    float factor;\n    __m128 factor128;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    factor = 1.0f / 8388608.0f;\n    factor128 = _mm_set1_ps(factor);\n    if (shift == 0) {\n        for (i = 0; i < frameCount4; ++i) {\n            __m128i mid;\n            __m128i side;\n            __m128i tempL;\n            __m128i tempR;\n            __m128  leftf;\n            __m128  rightf;\n            mid    = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n            side   = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n            mid    = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));\n            tempL  = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);\n            tempR  = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);\n            leftf  = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);\n            rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);\n            _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));\n            _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor;\n            pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor;\n        }\n    } else {\n        shift -= 1;\n        for (i = 0; i < frameCount4; ++i) {\n            __m128i mid;\n            __m128i side;\n            __m128i tempL;\n            __m128i tempR;\n            __m128 leftf;\n            __m128 rightf;\n            mid    = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n            side   = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n            mid    = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));\n            tempL  = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);\n            tempR  = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);\n            leftf  = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);\n            rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);\n            _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));\n            _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor;\n            pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor;\n        }\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift = unusedBitsPerSample - 8;\n    float factor;\n    float32x4_t factor4;\n    int32x4_t shift4;\n    int32x4_t wbps0_4;\n    int32x4_t wbps1_4;\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);\n    factor  = 1.0f / 8388608.0f;\n    factor4 = vdupq_n_f32(factor);\n    wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);\n    wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);\n    if (shift == 0) {\n        for (i = 0; i < frameCount4; ++i) {\n            int32x4_t lefti;\n            int32x4_t righti;\n            float32x4_t leftf;\n            float32x4_t rightf;\n            uint32x4_t mid  = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);\n            uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);\n            mid    = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));\n            lefti  = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);\n            righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);\n            leftf  = vmulq_f32(vcvtq_f32_s32(lefti),  factor4);\n            rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);\n            ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor;\n            pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor;\n        }\n    } else {\n        shift -= 1;\n        shift4 = vdupq_n_s32(shift);\n        for (i = 0; i < frameCount4; ++i) {\n            uint32x4_t mid;\n            uint32x4_t side;\n            int32x4_t lefti;\n            int32x4_t righti;\n            float32x4_t leftf;\n            float32x4_t rightf;\n            mid    = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);\n            side   = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);\n            mid    = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));\n            lefti  = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));\n            righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));\n            leftf  = vmulq_f32(vcvtq_f32_s32(lefti),  factor4);\n            rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);\n            ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));\n        }\n        for (i = (frameCount4 << 2); i < frameCount; ++i) {\n            ma_uint32 mid  = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n            ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n            mid = (mid << 1) | (side & 0x01);\n            pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor;\n            pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor;\n        }\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\n#if 0\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    for (ma_uint64 i = 0; i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0);\n        pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0);\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;\n    ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;\n    float factor = 1 / 2147483648.0;\n    for (i = 0; i < frameCount4; ++i) {\n        ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;\n        ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;\n        ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;\n        ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;\n        ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;\n        ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;\n        ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;\n        ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;\n        pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor;\n        pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor;\n        pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor;\n        pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor;\n        pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor;\n        pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor;\n        pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor;\n        pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor;\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;\n        pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;\n    }\n}\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;\n    ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;\n    float factor = 1.0f / 8388608.0f;\n    __m128 factor128 = _mm_set1_ps(factor);\n    for (i = 0; i < frameCount4; ++i) {\n        __m128i lefti;\n        __m128i righti;\n        __m128 leftf;\n        __m128 rightf;\n        lefti  = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);\n        righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);\n        leftf  = _mm_mul_ps(_mm_cvtepi32_ps(lefti),  factor128);\n        rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128);\n        _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));\n        _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;\n        pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;\n    }\n}\n#endif\n#if defined(MA_DR_FLAC_SUPPORT_NEON)\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n    ma_uint64 i;\n    ma_uint64 frameCount4 = frameCount >> 2;\n    const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;\n    const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;\n    ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;\n    ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;\n    float factor = 1.0f / 8388608.0f;\n    float32x4_t factor4 = vdupq_n_f32(factor);\n    int32x4_t shift0_4  = vdupq_n_s32(shift0);\n    int32x4_t shift1_4  = vdupq_n_s32(shift1);\n    for (i = 0; i < frameCount4; ++i) {\n        int32x4_t lefti;\n        int32x4_t righti;\n        float32x4_t leftf;\n        float32x4_t rightf;\n        lefti  = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));\n        righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));\n        leftf  = vmulq_f32(vcvtq_f32_s32(lefti),  factor4);\n        rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);\n        ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));\n    }\n    for (i = (frameCount4 << 2); i < frameCount; ++i) {\n        pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;\n        pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;\n    }\n}\n#endif\nstatic MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)\n{\n#if defined(MA_DR_FLAC_SUPPORT_SSE2)\n    if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#elif defined(MA_DR_FLAC_SUPPORT_NEON)\n    if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {\n        ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n    } else\n#endif\n    {\n#if 0\n        ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#else\n        ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);\n#endif\n    }\n}\nMA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut)\n{\n    ma_uint64 framesRead;\n    ma_uint32 unusedBitsPerSample;\n    if (pFlac == NULL || framesToRead == 0) {\n        return 0;\n    }\n    if (pBufferOut == NULL) {\n        return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);\n    }\n    MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);\n    unusedBitsPerSample = 32 - pFlac->bitsPerSample;\n    framesRead = 0;\n    while (framesToRead > 0) {\n        if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {\n            if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {\n                break;\n            }\n        } else {\n            unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);\n            ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;\n            ma_uint64 frameCountThisIteration = framesToRead;\n            if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {\n                frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;\n            }\n            if (channelCount == 2) {\n                const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;\n                const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;\n                switch (pFlac->currentFLACFrame.header.channelAssignment)\n                {\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:\n                    {\n                        ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:\n                    {\n                        ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:\n                    {\n                        ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                    case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:\n                    default:\n                    {\n                        ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);\n                    } break;\n                }\n            } else {\n                ma_uint64 i;\n                for (i = 0; i < frameCountThisIteration; ++i) {\n                    unsigned int j;\n                    for (j = 0; j < channelCount; ++j) {\n                        ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));\n                        pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0);\n                    }\n                }\n            }\n            framesRead                += frameCountThisIteration;\n            pBufferOut                += frameCountThisIteration * channelCount;\n            framesToRead              -= frameCountThisIteration;\n            pFlac->currentPCMFrame    += frameCountThisIteration;\n            pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration;\n        }\n    }\n    return framesRead;\n}\nMA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)\n{\n    if (pFlac == NULL) {\n        return MA_FALSE;\n    }\n    if (pFlac->currentPCMFrame == pcmFrameIndex) {\n        return MA_TRUE;\n    }\n    if (pFlac->firstFLACFramePosInBytes == 0) {\n        return MA_FALSE;\n    }\n    if (pcmFrameIndex == 0) {\n        pFlac->currentPCMFrame = 0;\n        return ma_dr_flac__seek_to_first_frame(pFlac);\n    } else {\n        ma_bool32 wasSuccessful = MA_FALSE;\n        ma_uint64 originalPCMFrame = pFlac->currentPCMFrame;\n        if (pcmFrameIndex > pFlac->totalPCMFrameCount) {\n            pcmFrameIndex = pFlac->totalPCMFrameCount;\n        }\n        if (pcmFrameIndex > pFlac->currentPCMFrame) {\n            ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame);\n            if (pFlac->currentFLACFrame.pcmFramesRemaining >  offset) {\n                pFlac->currentFLACFrame.pcmFramesRemaining -= offset;\n                pFlac->currentPCMFrame = pcmFrameIndex;\n                return MA_TRUE;\n            }\n        } else {\n            ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex);\n            ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;\n            ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining;\n            if (currentFLACFramePCMFramesConsumed > offsetAbs) {\n                pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs;\n                pFlac->currentPCMFrame = pcmFrameIndex;\n                return MA_TRUE;\n            }\n        }\n#ifndef MA_DR_FLAC_NO_OGG\n        if (pFlac->container == ma_dr_flac_container_ogg)\n        {\n            wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex);\n        }\n        else\n#endif\n        {\n            if (!pFlac->_noSeekTableSeek) {\n                wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex);\n            }\n#if !defined(MA_DR_FLAC_NO_CRC)\n            if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) {\n                wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex);\n            }\n#endif\n            if (!wasSuccessful && !pFlac->_noBruteForceSeek) {\n                wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex);\n            }\n        }\n        if (wasSuccessful) {\n            pFlac->currentPCMFrame = pcmFrameIndex;\n        } else {\n            if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) {\n                ma_dr_flac_seek_to_pcm_frame(pFlac, 0);\n            }\n        }\n        return wasSuccessful;\n    }\n}\n#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \\\nstatic type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\\\n{                                                                                                                                                                   \\\n    type* pSampleData = NULL;                                                                                                                                       \\\n    ma_uint64 totalPCMFrameCount;                                                                                                                               \\\n                                                                                                                                                                    \\\n    MA_DR_FLAC_ASSERT(pFlac != NULL);                                                                                                                                   \\\n                                                                                                                                                                    \\\n    totalPCMFrameCount = pFlac->totalPCMFrameCount;                                                                                                                 \\\n                                                                                                                                                                    \\\n    if (totalPCMFrameCount == 0) {                                                                                                                                  \\\n        type buffer[4096];                                                                                                                                          \\\n        ma_uint64 pcmFramesRead;                                                                                                                                \\\n        size_t sampleDataBufferSize = sizeof(buffer);                                                                                                               \\\n                                                                                                                                                                    \\\n        pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks);                                                      \\\n        if (pSampleData == NULL) {                                                                                                                                  \\\n            goto on_error;                                                                                                                                          \\\n        }                                                                                                                                                           \\\n                                                                                                                                                                    \\\n        while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) {          \\\n            if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) {                                                   \\\n                type* pNewSampleData;                                                                                                                               \\\n                size_t newSampleDataBufferSize;                                                                                                                     \\\n                                                                                                                                                                    \\\n                newSampleDataBufferSize = sampleDataBufferSize * 2;                                                                                                 \\\n                pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks);    \\\n                if (pNewSampleData == NULL) {                                                                                                                       \\\n                    ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks);                                                                          \\\n                    goto on_error;                                                                                                                                  \\\n                }                                                                                                                                                   \\\n                                                                                                                                                                    \\\n                sampleDataBufferSize = newSampleDataBufferSize;                                                                                                     \\\n                pSampleData = pNewSampleData;                                                                                                                       \\\n            }                                                                                                                                                       \\\n                                                                                                                                                                    \\\n            MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type)));                   \\\n            totalPCMFrameCount += pcmFramesRead;                                                                                                                    \\\n        }                                                                                                                                                           \\\n                                                                                                                                                                    \\\n                                                                                                                         \\\n        MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type)));   \\\n    } else {                                                                                                                                                        \\\n        ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type);                                                                                   \\\n        if (dataSize > (ma_uint64)MA_SIZE_MAX) {                                                                                                            \\\n            goto on_error;                                                                                                        \\\n        }                                                                                                                                                           \\\n                                                                                                                                                                    \\\n        pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks);               \\\n        if (pSampleData == NULL) {                                                                                                                                  \\\n            goto on_error;                                                                                                                                          \\\n        }                                                                                                                                                           \\\n                                                                                                                                                                    \\\n        totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData);                                                     \\\n    }                                                                                                                                                               \\\n                                                                                                                                                                    \\\n    if (sampleRateOut) *sampleRateOut = pFlac->sampleRate;                                                                                                          \\\n    if (channelsOut) *channelsOut = pFlac->channels;                                                                                                                \\\n    if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount;                                                                                         \\\n                                                                                                                                                                    \\\n    ma_dr_flac_close(pFlac);                                                                                                                                            \\\n    return pSampleData;                                                                                                                                             \\\n                                                                                                                                                                    \\\non_error:                                                                                                                                                           \\\n    ma_dr_flac_close(pFlac);                                                                                                                                            \\\n    return NULL;                                                                                                                                                    \\\n}\nMA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32)\nMA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16)\nMA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)\nMA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalPCMFrameCountOut) {\n        *totalPCMFrameCountOut = 0;\n    }\n    pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return NULL;\n    }\n    return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);\n}\nMA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalPCMFrameCountOut) {\n        *totalPCMFrameCountOut = 0;\n    }\n    pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return NULL;\n    }\n    return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);\n}\nMA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    if (channelsOut) {\n        *channelsOut = 0;\n    }\n    if (sampleRateOut) {\n        *sampleRateOut = 0;\n    }\n    if (totalPCMFrameCountOut) {\n        *totalPCMFrameCountOut = 0;\n    }\n    pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return NULL;\n    }\n    return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);\n}\n#ifndef MA_DR_FLAC_NO_STDIO\nMA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    if (sampleRate) {\n        *sampleRate = 0;\n    }\n    if (channels) {\n        *channels = 0;\n    }\n    if (totalPCMFrameCount) {\n        *totalPCMFrameCount = 0;\n    }\n    pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return NULL;\n    }\n    return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);\n}\nMA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    if (sampleRate) {\n        *sampleRate = 0;\n    }\n    if (channels) {\n        *channels = 0;\n    }\n    if (totalPCMFrameCount) {\n        *totalPCMFrameCount = 0;\n    }\n    pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return NULL;\n    }\n    return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);\n}\nMA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    if (sampleRate) {\n        *sampleRate = 0;\n    }\n    if (channels) {\n        *channels = 0;\n    }\n    if (totalPCMFrameCount) {\n        *totalPCMFrameCount = 0;\n    }\n    pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return NULL;\n    }\n    return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);\n}\n#endif\nMA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    if (sampleRate) {\n        *sampleRate = 0;\n    }\n    if (channels) {\n        *channels = 0;\n    }\n    if (totalPCMFrameCount) {\n        *totalPCMFrameCount = 0;\n    }\n    pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return NULL;\n    }\n    return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);\n}\nMA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    if (sampleRate) {\n        *sampleRate = 0;\n    }\n    if (channels) {\n        *channels = 0;\n    }\n    if (totalPCMFrameCount) {\n        *totalPCMFrameCount = 0;\n    }\n    pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return NULL;\n    }\n    return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);\n}\nMA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_flac* pFlac;\n    if (sampleRate) {\n        *sampleRate = 0;\n    }\n    if (channels) {\n        *channels = 0;\n    }\n    if (totalPCMFrameCount) {\n        *totalPCMFrameCount = 0;\n    }\n    pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);\n    if (pFlac == NULL) {\n        return NULL;\n    }\n    return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);\n}\nMA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks != NULL) {\n        ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks);\n    } else {\n        ma_dr_flac__free_default(p, NULL);\n    }\n}\nMA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments)\n{\n    if (pIter == NULL) {\n        return;\n    }\n    pIter->countRemaining = commentCount;\n    pIter->pRunningData   = (const char*)pComments;\n}\nMA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut)\n{\n    ma_int32 length;\n    const char* pComment;\n    if (pCommentLengthOut) {\n        *pCommentLengthOut = 0;\n    }\n    if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {\n        return NULL;\n    }\n    length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData);\n    pIter->pRunningData += 4;\n    pComment = pIter->pRunningData;\n    pIter->pRunningData += length;\n    pIter->countRemaining -= 1;\n    if (pCommentLengthOut) {\n        *pCommentLengthOut = length;\n    }\n    return pComment;\n}\nMA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData)\n{\n    if (pIter == NULL) {\n        return;\n    }\n    pIter->countRemaining = trackCount;\n    pIter->pRunningData   = (const char*)pTrackData;\n}\nMA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack)\n{\n    ma_dr_flac_cuesheet_track cuesheetTrack;\n    const char* pRunningData;\n    ma_uint64 offsetHi;\n    ma_uint64 offsetLo;\n    if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {\n        return MA_FALSE;\n    }\n    pRunningData = pIter->pRunningData;\n    offsetHi                   = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4;\n    offsetLo                   = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4;\n    cuesheetTrack.offset       = offsetLo | (offsetHi << 32);\n    cuesheetTrack.trackNumber  = pRunningData[0];                                         pRunningData += 1;\n    MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC));     pRunningData += 12;\n    cuesheetTrack.isAudio      = (pRunningData[0] & 0x80) != 0;\n    cuesheetTrack.preEmphasis  = (pRunningData[0] & 0x40) != 0;                           pRunningData += 14;\n    cuesheetTrack.indexCount   = pRunningData[0];                                         pRunningData += 1;\n    cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData;        pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index);\n    pIter->pRunningData = pRunningData;\n    pIter->countRemaining -= 1;\n    if (pCuesheetTrack) {\n        *pCuesheetTrack = cuesheetTrack;\n    }\n    return MA_TRUE;\n}\n#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))\n    #pragma GCC diagnostic pop\n#endif\n#endif\n/* dr_flac_c end */\n#endif  /* MA_DR_FLAC_IMPLEMENTATION */\n#endif  /* MA_NO_FLAC */\n\n#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)\n#if !defined(MA_DR_MP3_IMPLEMENTATION) && !defined(MA_DR_MP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */\n/* dr_mp3_c begin */\n#ifndef ma_dr_mp3_c\n#define ma_dr_mp3_c\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\nMA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)\n{\n    if (pMajor) {\n        *pMajor = MA_DR_MP3_VERSION_MAJOR;\n    }\n    if (pMinor) {\n        *pMinor = MA_DR_MP3_VERSION_MINOR;\n    }\n    if (pRevision) {\n        *pRevision = MA_DR_MP3_VERSION_REVISION;\n    }\n}\nMA_API const char* ma_dr_mp3_version_string(void)\n{\n    return MA_DR_MP3_VERSION_STRING;\n}\n#if defined(__TINYC__)\n#define MA_DR_MP3_NO_SIMD\n#endif\n#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset)))\n#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE  2304\n#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES\n#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES      10\n#endif\n#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES  MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE\n#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES      511\n#define MA_DR_MP3_SHORT_BLOCK_TYPE            2\n#define MA_DR_MP3_STOP_BLOCK_TYPE             3\n#define MA_DR_MP3_MODE_MONO                   3\n#define MA_DR_MP3_MODE_JOINT_STEREO           1\n#define MA_DR_MP3_HDR_SIZE                    4\n#define MA_DR_MP3_HDR_IS_MONO(h)              (((h[3]) & 0xC0) == 0xC0)\n#define MA_DR_MP3_HDR_IS_MS_STEREO(h)         (((h[3]) & 0xE0) == 0x60)\n#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h)       (((h[2]) & 0xF0) == 0)\n#define MA_DR_MP3_HDR_IS_CRC(h)               (!((h[1]) & 1))\n#define MA_DR_MP3_HDR_TEST_PADDING(h)         ((h[2]) & 0x2)\n#define MA_DR_MP3_HDR_TEST_MPEG1(h)           ((h[1]) & 0x8)\n#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h)      ((h[1]) & 0x10)\n#define MA_DR_MP3_HDR_TEST_I_STEREO(h)        ((h[3]) & 0x10)\n#define MA_DR_MP3_HDR_TEST_MS_STEREO(h)       ((h[3]) & 0x20)\n#define MA_DR_MP3_HDR_GET_STEREO_MODE(h)      (((h[3]) >> 6) & 3)\n#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h)  (((h[3]) >> 4) & 3)\n#define MA_DR_MP3_HDR_GET_LAYER(h)            (((h[1]) >> 1) & 3)\n#define MA_DR_MP3_HDR_GET_BITRATE(h)          ((h[2]) >> 4)\n#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)      (((h[2]) >> 2) & 3)\n#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h)   (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3)\n#define MA_DR_MP3_HDR_IS_FRAME_576(h)         ((h[1] & 14) == 2)\n#define MA_DR_MP3_HDR_IS_LAYER_1(h)           ((h[1] & 6) == 6)\n#define MA_DR_MP3_BITS_DEQUANTIZER_OUT        -1\n#define MA_DR_MP3_MAX_SCF                     (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210)\n#define MA_DR_MP3_MAX_SCFI                    ((MA_DR_MP3_MAX_SCF + 3) & ~3)\n#define MA_DR_MP3_MIN(a, b)           ((a) > (b) ? (b) : (a))\n#define MA_DR_MP3_MAX(a, b)           ((a) < (b) ? (b) : (a))\n#if !defined(MA_DR_MP3_NO_SIMD)\n#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))\n#define MA_DR_MP3_ONLY_SIMD\n#endif\n#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)))\n#if defined(_MSC_VER)\n#include <intrin.h>\n#endif\n#include <emmintrin.h>\n#define MA_DR_MP3_HAVE_SSE 1\n#define MA_DR_MP3_HAVE_SIMD 1\n#define MA_DR_MP3_VSTORE _mm_storeu_ps\n#define MA_DR_MP3_VLD _mm_loadu_ps\n#define MA_DR_MP3_VSET _mm_set1_ps\n#define MA_DR_MP3_VADD _mm_add_ps\n#define MA_DR_MP3_VSUB _mm_sub_ps\n#define MA_DR_MP3_VMUL _mm_mul_ps\n#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y))\n#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y))\n#define MA_DR_MP3_VMUL_S(x, s)  _mm_mul_ps(x, _mm_set1_ps(s))\n#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3))\ntypedef __m128 ma_dr_mp3_f4;\n#if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD)\n#define ma_dr_mp3_cpuid __cpuid\n#else\nstatic __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType)\n{\n#if defined(__PIC__)\n    __asm__ __volatile__(\n#if defined(__x86_64__)\n        \"push %%rbx\\n\"\n        \"cpuid\\n\"\n        \"xchgl %%ebx, %1\\n\"\n        \"pop  %%rbx\\n\"\n#else\n        \"xchgl %%ebx, %1\\n\"\n        \"cpuid\\n\"\n        \"xchgl %%ebx, %1\\n\"\n#endif\n        : \"=a\" (CPUInfo[0]), \"=r\" (CPUInfo[1]), \"=c\" (CPUInfo[2]), \"=d\" (CPUInfo[3])\n        : \"a\" (InfoType));\n#else\n    __asm__ __volatile__(\n        \"cpuid\"\n        : \"=a\" (CPUInfo[0]), \"=b\" (CPUInfo[1]), \"=c\" (CPUInfo[2]), \"=d\" (CPUInfo[3])\n        : \"a\" (InfoType));\n#endif\n}\n#endif\nstatic int ma_dr_mp3_have_simd(void)\n{\n#ifdef MA_DR_MP3_ONLY_SIMD\n    return 1;\n#else\n    static int g_have_simd;\n    int CPUInfo[4];\n#ifdef MINIMP3_TEST\n    static int g_counter;\n    if (g_counter++ > 100)\n        return 0;\n#endif\n    if (g_have_simd)\n        goto end;\n    ma_dr_mp3_cpuid(CPUInfo, 0);\n    if (CPUInfo[0] > 0)\n    {\n        ma_dr_mp3_cpuid(CPUInfo, 1);\n        g_have_simd = (CPUInfo[3] & (1 << 26)) + 1;\n        return g_have_simd - 1;\n    }\nend:\n    return g_have_simd - 1;\n#endif\n}\n#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)\n#include <arm_neon.h>\n#define MA_DR_MP3_HAVE_SSE 0\n#define MA_DR_MP3_HAVE_SIMD 1\n#define MA_DR_MP3_VSTORE vst1q_f32\n#define MA_DR_MP3_VLD vld1q_f32\n#define MA_DR_MP3_VSET vmovq_n_f32\n#define MA_DR_MP3_VADD vaddq_f32\n#define MA_DR_MP3_VSUB vsubq_f32\n#define MA_DR_MP3_VMUL vmulq_f32\n#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y)\n#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y)\n#define MA_DR_MP3_VMUL_S(x, s)  vmulq_f32(x, vmovq_n_f32(s))\n#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x)))\ntypedef float32x4_t ma_dr_mp3_f4;\nstatic int ma_dr_mp3_have_simd(void)\n{\n    return 1;\n}\n#else\n#define MA_DR_MP3_HAVE_SSE 0\n#define MA_DR_MP3_HAVE_SIMD 0\n#ifdef MA_DR_MP3_ONLY_SIMD\n#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled\n#endif\n#endif\n#else\n#define MA_DR_MP3_HAVE_SIMD 0\n#endif\n#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__ARM_ARCH_6M__)\n#define MA_DR_MP3_HAVE_ARMV6 1\nstatic __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a)\n{\n    ma_int32 x = 0;\n    __asm__ (\"ssat %0, #16, %1\" : \"=r\"(x) : \"r\"(a));\n    return x;\n}\n#else\n#define MA_DR_MP3_HAVE_ARMV6 0\n#endif\n#ifndef MA_DR_MP3_ASSERT\n#include <assert.h>\n#define MA_DR_MP3_ASSERT(expression) assert(expression)\n#endif\n#ifndef MA_DR_MP3_COPY_MEMORY\n#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))\n#endif\n#ifndef MA_DR_MP3_MOVE_MEMORY\n#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))\n#endif\n#ifndef MA_DR_MP3_ZERO_MEMORY\n#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz))\n#endif\n#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p)))\n#ifndef MA_DR_MP3_MALLOC\n#define MA_DR_MP3_MALLOC(sz) malloc((sz))\n#endif\n#ifndef MA_DR_MP3_REALLOC\n#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz))\n#endif\n#ifndef MA_DR_MP3_FREE\n#define MA_DR_MP3_FREE(p) free((p))\n#endif\ntypedef struct\n{\n    const ma_uint8 *buf;\n    int pos, limit;\n} ma_dr_mp3_bs;\ntypedef struct\n{\n    float scf[3*64];\n    ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64];\n} ma_dr_mp3_L12_scale_info;\ntypedef struct\n{\n    ma_uint8 tab_offset, code_tab_width, band_count;\n} ma_dr_mp3_L12_subband_alloc;\ntypedef struct\n{\n    const ma_uint8 *sfbtab;\n    ma_uint16 part_23_length, big_values, scalefac_compress;\n    ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;\n    ma_uint8 table_select[3], region_count[3], subblock_gain[3];\n    ma_uint8 preflag, scalefac_scale, count1_table, scfsi;\n} ma_dr_mp3_L3_gr_info;\ntypedef struct\n{\n    ma_dr_mp3_bs bs;\n    ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES];\n    ma_dr_mp3_L3_gr_info gr_info[4];\n    float grbuf[2][576], scf[40], syn[18 + 15][2*32];\n    ma_uint8 ist_pos[2][39];\n} ma_dr_mp3dec_scratch;\nstatic void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes)\n{\n    bs->buf   = data;\n    bs->pos   = 0;\n    bs->limit = bytes*8;\n}\nstatic ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n)\n{\n    ma_uint32 next, cache = 0, s = bs->pos & 7;\n    int shl = n + s;\n    const ma_uint8 *p = bs->buf + (bs->pos >> 3);\n    if ((bs->pos += n) > bs->limit)\n        return 0;\n    next = *p++ & (255 >> s);\n    while ((shl -= 8) > 0)\n    {\n        cache |= next << shl;\n        next = *p++;\n    }\n    return cache | (next >> -shl);\n}\nstatic int ma_dr_mp3_hdr_valid(const ma_uint8 *h)\n{\n    return h[0] == 0xff &&\n        ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) &&\n        (MA_DR_MP3_HDR_GET_LAYER(h) != 0) &&\n        (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) &&\n        (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3);\n}\nstatic int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2)\n{\n    return ma_dr_mp3_hdr_valid(h2) &&\n        ((h1[1] ^ h2[1]) & 0xFE) == 0 &&\n        ((h1[2] ^ h2[2]) & 0x0C) == 0 &&\n        !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2));\n}\nstatic unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h)\n{\n    static const ma_uint8 halfrate[2][3][15] = {\n        { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } },\n        { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } },\n    };\n    return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)];\n}\nstatic unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h)\n{\n    static const unsigned g_hz[3] = { 44100, 48000, 32000 };\n    return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h);\n}\nstatic unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h)\n{\n    return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h));\n}\nstatic int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size)\n{\n    int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h);\n    if (MA_DR_MP3_HDR_IS_LAYER_1(h))\n    {\n        frame_bytes &= ~3;\n    }\n    return frame_bytes ? frame_bytes : free_format_size;\n}\nstatic int ma_dr_mp3_hdr_padding(const ma_uint8 *h)\n{\n    return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0;\n}\n#ifndef MA_DR_MP3_ONLY_MP3\nstatic const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci)\n{\n    const ma_dr_mp3_L12_subband_alloc *alloc;\n    int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr);\n    int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32;\n    if (MA_DR_MP3_HDR_IS_LAYER_1(hdr))\n    {\n        static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } };\n        alloc = g_alloc_L1;\n        nbands = 32;\n    } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr))\n    {\n        static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } };\n        alloc = g_alloc_L2M2;\n        nbands = 30;\n    } else\n    {\n        static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } };\n        int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr);\n        unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO);\n        if (!kbps)\n        {\n            kbps = 192;\n        }\n        alloc = g_alloc_L2M1;\n        nbands = 27;\n        if (kbps < 56)\n        {\n            static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } };\n            alloc = g_alloc_L2M1_lowrate;\n            nbands = sample_rate_idx == 2 ? 12 : 8;\n        } else if (kbps >= 96 && sample_rate_idx != 1)\n        {\n            nbands = 30;\n        }\n    }\n    sci->total_bands = (ma_uint8)nbands;\n    sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands);\n    return alloc;\n}\nstatic void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf)\n{\n    static const float g_deq_L12[18*3] = {\n#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x\n        MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9)\n    };\n    int i, m;\n    for (i = 0; i < bands; i++)\n    {\n        float s = 0;\n        int ba = *pba++;\n        int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0;\n        for (m = 4; m; m >>= 1)\n        {\n            if (mask & m)\n            {\n                int b = ma_dr_mp3_bs_get_bits(bs, 6);\n                s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3);\n            }\n            *scf++ = s;\n        }\n    }\n}\nstatic void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci)\n{\n    static const ma_uint8 g_bitalloc_code_tab[] = {\n        0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16,\n        0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16,\n        0,17,18, 3,19,4,5,16,\n        0,17,18,16,\n        0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15,\n        0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14,\n        0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16\n    };\n    const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci);\n    int i, k = 0, ba_bits = 0;\n    const ma_uint8 *ba_code_tab = g_bitalloc_code_tab;\n    for (i = 0; i < sci->total_bands; i++)\n    {\n        ma_uint8 ba;\n        if (i == k)\n        {\n            k += subband_alloc->band_count;\n            ba_bits = subband_alloc->code_tab_width;\n            ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset;\n            subband_alloc++;\n        }\n        ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)];\n        sci->bitalloc[2*i] = ba;\n        if (i < sci->stereo_bands)\n        {\n            ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)];\n        }\n        sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0;\n    }\n    for (i = 0; i < 2*sci->total_bands; i++)\n    {\n        sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6);\n    }\n    ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf);\n    for (i = sci->stereo_bands; i < sci->total_bands; i++)\n    {\n        sci->bitalloc[2*i + 1] = 0;\n    }\n}\nstatic int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size)\n{\n    int i, j, k, choff = 576;\n    for (j = 0; j < 4; j++)\n    {\n        float *dst = grbuf + group_size*j;\n        for (i = 0; i < 2*sci->total_bands; i++)\n        {\n            int ba = sci->bitalloc[i];\n            if (ba != 0)\n            {\n                if (ba < 17)\n                {\n                    int half = (1 << (ba - 1)) - 1;\n                    for (k = 0; k < group_size; k++)\n                    {\n                        dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half);\n                    }\n                } else\n                {\n                    unsigned mod = (2 << (ba - 17)) + 1;\n                    unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3));\n                    for (k = 0; k < group_size; k++, code /= mod)\n                    {\n                        dst[k] = (float)((int)(code % mod - mod/2));\n                    }\n                }\n            }\n            dst += choff;\n            choff = 18 - choff;\n        }\n    }\n    return group_size*4;\n}\nstatic void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst)\n{\n    int i, k;\n    MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float));\n    for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6)\n    {\n        for (k = 0; k < 12; k++)\n        {\n            dst[k + 0]   *= scf[0];\n            dst[k + 576] *= scf[3];\n        }\n    }\n}\n#endif\nstatic int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr)\n{\n    static const ma_uint8 g_scf_long[8][23] = {\n        { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },\n        { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 },\n        { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },\n        { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 },\n        { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },\n        { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 },\n        { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 },\n        { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 }\n    };\n    static const ma_uint8 g_scf_short[8][40] = {\n        { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },\n        { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 },\n        { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 },\n        { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 },\n        { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },\n        { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 },\n        { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 },\n        { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 }\n    };\n    static const ma_uint8 g_scf_mixed[8][40] = {\n        { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },\n        { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 },\n        { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 },\n        { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 },\n        { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },\n        { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 },\n        { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 },\n        { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 }\n    };\n    unsigned tables, scfsi = 0;\n    int main_data_begin, part_23_sum = 0;\n    int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2;\n    int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);\n    if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))\n    {\n        gr_count *= 2;\n        main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9);\n        scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count);\n    } else\n    {\n        main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count;\n    }\n    do\n    {\n        if (MA_DR_MP3_HDR_IS_MONO(hdr))\n        {\n            scfsi <<= 4;\n        }\n        gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12);\n        part_23_sum += gr->part_23_length;\n        gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs,  9);\n        if (gr->big_values > 288)\n        {\n            return -1;\n        }\n        gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8);\n        gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9);\n        gr->sfbtab = g_scf_long[sr_idx];\n        gr->n_long_sfb  = 22;\n        gr->n_short_sfb = 0;\n        if (ma_dr_mp3_bs_get_bits(bs, 1))\n        {\n            gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2);\n            if (!gr->block_type)\n            {\n                return -1;\n            }\n            gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);\n            gr->region_count[0] = 7;\n            gr->region_count[1] = 255;\n            if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE)\n            {\n                scfsi &= 0x0F0F;\n                if (!gr->mixed_block_flag)\n                {\n                    gr->region_count[0] = 8;\n                    gr->sfbtab = g_scf_short[sr_idx];\n                    gr->n_long_sfb = 0;\n                    gr->n_short_sfb = 39;\n                } else\n                {\n                    gr->sfbtab = g_scf_mixed[sr_idx];\n                    gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6;\n                    gr->n_short_sfb = 30;\n                }\n            }\n            tables = ma_dr_mp3_bs_get_bits(bs, 10);\n            tables <<= 5;\n            gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);\n            gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);\n            gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);\n        } else\n        {\n            gr->block_type = 0;\n            gr->mixed_block_flag = 0;\n            tables = ma_dr_mp3_bs_get_bits(bs, 15);\n            gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4);\n            gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);\n            gr->region_count[2] = 255;\n        }\n        gr->table_select[0] = (ma_uint8)(tables >> 10);\n        gr->table_select[1] = (ma_uint8)((tables >> 5) & 31);\n        gr->table_select[2] = (ma_uint8)((tables) & 31);\n        gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500));\n        gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);\n        gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);\n        gr->scfsi = (ma_uint8)((scfsi >> 12) & 15);\n        scfsi <<= 4;\n        gr++;\n    } while(--gr_count);\n    if (part_23_sum + bs->pos > bs->limit + main_data_begin*8)\n    {\n        return -1;\n    }\n    return main_data_begin;\n}\nstatic void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi)\n{\n    int i, k;\n    for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2)\n    {\n        int cnt = scf_count[i];\n        if (scfsi & 8)\n        {\n            MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt);\n        } else\n        {\n            int bits = scf_size[i];\n            if (!bits)\n            {\n                MA_DR_MP3_ZERO_MEMORY(scf, cnt);\n                MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt);\n            } else\n            {\n                int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1;\n                for (k = 0; k < cnt; k++)\n                {\n                    int s = ma_dr_mp3_bs_get_bits(bitbuf, bits);\n                    ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s);\n                    scf[k] = (ma_uint8)s;\n                }\n            }\n        }\n        ist_pos += cnt;\n        scf += cnt;\n    }\n    scf[0] = scf[1] = scf[2] = 0;\n}\nstatic float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2)\n{\n    static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f };\n    int e;\n    do\n    {\n        e = MA_DR_MP3_MIN(30*4, exp_q2);\n        y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2));\n    } while ((exp_q2 -= e) > 0);\n    return y;\n}\nstatic void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch)\n{\n    static const ma_uint8 g_scf_partitions[3][28] = {\n        { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 },\n        { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 },\n        { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 }\n    };\n    const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb];\n    ma_uint8 scf_size[4], iscf[40];\n    int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi;\n    float gain;\n    if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))\n    {\n        static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 };\n        int part = g_scfc_decode[gr->scalefac_compress];\n        scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2);\n        scf_size[3] = scf_size[2] = (ma_uint8)(part & 3);\n    } else\n    {\n        static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 };\n        int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch;\n        sfc = gr->scalefac_compress >> ist;\n        for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4)\n        {\n            for (modprod = 1, i = 3; i >= 0; i--)\n            {\n                scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]);\n                modprod *= g_mod[k + i];\n            }\n        }\n        scf_partition += k;\n        scfsi = -16;\n    }\n    ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi);\n    if (gr->n_short_sfb)\n    {\n        int sh = 3 - scf_shift;\n        for (i = 0; i < gr->n_short_sfb; i += 3)\n        {\n            iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh));\n            iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh));\n            iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh));\n        }\n    } else if (gr->preflag)\n    {\n        static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 };\n        for (i = 0; i < 10; i++)\n        {\n            iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]);\n        }\n    }\n    gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0);\n    gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4),  MA_DR_MP3_MAX_SCFI - gain_exp);\n    for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++)\n    {\n        scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);\n    }\n}\nstatic const float g_ma_dr_mp3_pow43[129 + 16] = {\n    0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f,\n    0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f\n};\nstatic float ma_dr_mp3_L3_pow_43(int x)\n{\n    float frac;\n    int sign, mult = 256;\n    if (x < 129)\n    {\n        return g_ma_dr_mp3_pow43[16 + x];\n    }\n    if (x < 1024)\n    {\n        mult = 16;\n        x <<= 3;\n    }\n    sign = 2*x & 64;\n    frac = (float)((x & 63) - sign) / ((x & ~63) + sign);\n    return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;\n}\nstatic void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit)\n{\n    static const ma_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,\n        -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288,\n        -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288,\n        -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258,\n        -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259,\n        -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258,\n        -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258,\n        -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259,\n        -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258,\n        -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290,\n        -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259,\n        -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258,\n        -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259,\n        -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258,\n        -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 };\n    static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205};\n    static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 };\n    static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 };\n    static const ma_uint8 g_linbits[] =  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 };\n#define MA_DR_MP3_PEEK_BITS(n)    (bs_cache >> (32 - (n)))\n#define MA_DR_MP3_FLUSH_BITS(n)   { bs_cache <<= (n); bs_sh += (n); }\n#define MA_DR_MP3_CHECK_BITS      while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }\n#define MA_DR_MP3_BSPOS           ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh)\n    float one = 0.0f;\n    int ireg = 0, big_val_cnt = gr_info->big_values;\n    const ma_uint8 *sfb = gr_info->sfbtab;\n    const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8;\n    ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7);\n    int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8;\n    bs_next_ptr += 4;\n    while (big_val_cnt > 0)\n    {\n        int tab_num = gr_info->table_select[ireg];\n        int sfb_cnt = gr_info->region_count[ireg++];\n        const ma_int16 *codebook = tabs + tabindex[tab_num];\n        int linbits = g_linbits[tab_num];\n        if (linbits)\n        {\n            do\n            {\n                np = *sfb++ / 2;\n                pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np);\n                one = *scf++;\n                do\n                {\n                    int j, w = 5;\n                    int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)];\n                    while (leaf < 0)\n                    {\n                        MA_DR_MP3_FLUSH_BITS(w);\n                        w = leaf & 7;\n                        leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)];\n                    }\n                    MA_DR_MP3_FLUSH_BITS(leaf >> 8);\n                    for (j = 0; j < 2; j++, dst++, leaf >>= 4)\n                    {\n                        int lsb = leaf & 0x0F;\n                        if (lsb == 15)\n                        {\n                            lsb += MA_DR_MP3_PEEK_BITS(linbits);\n                            MA_DR_MP3_FLUSH_BITS(linbits);\n                            MA_DR_MP3_CHECK_BITS;\n                            *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1);\n                        } else\n                        {\n                            *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;\n                        }\n                        MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0);\n                    }\n                    MA_DR_MP3_CHECK_BITS;\n                } while (--pairs_to_decode);\n            } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);\n        } else\n        {\n            do\n            {\n                np = *sfb++ / 2;\n                pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np);\n                one = *scf++;\n                do\n                {\n                    int j, w = 5;\n                    int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)];\n                    while (leaf < 0)\n                    {\n                        MA_DR_MP3_FLUSH_BITS(w);\n                        w = leaf & 7;\n                        leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)];\n                    }\n                    MA_DR_MP3_FLUSH_BITS(leaf >> 8);\n                    for (j = 0; j < 2; j++, dst++, leaf >>= 4)\n                    {\n                        int lsb = leaf & 0x0F;\n                        *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;\n                        MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0);\n                    }\n                    MA_DR_MP3_CHECK_BITS;\n                } while (--pairs_to_decode);\n            } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);\n        }\n    }\n    for (np = 1 - big_val_cnt;; dst += 4)\n    {\n        const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32;\n        int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)];\n        if (!(leaf & 8))\n        {\n            leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))];\n        }\n        MA_DR_MP3_FLUSH_BITS(leaf & 7);\n        if (MA_DR_MP3_BSPOS > layer3gr_limit)\n        {\n            break;\n        }\n#define MA_DR_MP3_RELOAD_SCALEFACTOR  if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; }\n#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) }\n        MA_DR_MP3_RELOAD_SCALEFACTOR;\n        MA_DR_MP3_DEQ_COUNT1(0);\n        MA_DR_MP3_DEQ_COUNT1(1);\n        MA_DR_MP3_RELOAD_SCALEFACTOR;\n        MA_DR_MP3_DEQ_COUNT1(2);\n        MA_DR_MP3_DEQ_COUNT1(3);\n        MA_DR_MP3_CHECK_BITS;\n    }\n    bs->pos = layer3gr_limit;\n}\nstatic void ma_dr_mp3_L3_midside_stereo(float *left, int n)\n{\n    int i = 0;\n    float *right = left + 576;\n#if MA_DR_MP3_HAVE_SIMD\n    if (ma_dr_mp3_have_simd())\n    {\n        for (; i < n - 3; i += 4)\n        {\n            ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i);\n            ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i);\n            MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr));\n            MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr));\n        }\n#ifdef __GNUC__\n        if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0)\n            return;\n#endif\n    }\n#endif\n    for (; i < n; i++)\n    {\n        float a = left[i];\n        float b = right[i];\n        left[i] = a + b;\n        right[i] = a - b;\n    }\n}\nstatic void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr)\n{\n    int i;\n    for (i = 0; i < n; i++)\n    {\n        left[i + 576] = left[i]*kr;\n        left[i] = left[i]*kl;\n    }\n}\nstatic void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3])\n{\n    int i, k;\n    max_band[0] = max_band[1] = max_band[2] = -1;\n    for (i = 0; i < nbands; i++)\n    {\n        for (k = 0; k < sfb[i]; k += 2)\n        {\n            if (right[k] != 0 || right[k + 1] != 0)\n            {\n                max_band[i % 3] = i;\n                break;\n            }\n        }\n        right += sfb[i];\n    }\n}\nstatic void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh)\n{\n    static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 };\n    unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64;\n    for (i = 0; sfb[i]; i++)\n    {\n        unsigned ipos = ist_pos[i];\n        if ((int)i > max_band[i % 3] && ipos < max_pos)\n        {\n            float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1;\n            if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))\n            {\n                kl = g_pan[2*ipos];\n                kr = g_pan[2*ipos + 1];\n            } else\n            {\n                kl = 1;\n                kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh);\n                if (ipos & 1)\n                {\n                    kl = kr;\n                    kr = 1;\n                }\n            }\n            ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s);\n        } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr))\n        {\n            ma_dr_mp3_L3_midside_stereo(left, sfb[i]);\n        }\n        left += sfb[i];\n    }\n}\nstatic void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr)\n{\n    int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb;\n    int i, max_blocks = gr->n_short_sfb ? 3 : 1;\n    ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band);\n    if (gr->n_long_sfb)\n    {\n        max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]);\n    }\n    for (i = 0; i < max_blocks; i++)\n    {\n        int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0;\n        int itop = n_sfb - max_blocks + i;\n        int prev = itop - max_blocks;\n        ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]);\n    }\n    ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1);\n}\nstatic void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb)\n{\n    int i, len;\n    float *src = grbuf, *dst = scratch;\n    for (;0 != (len = *sfb); sfb += 3, src += 2*len)\n    {\n        for (i = 0; i < len; i++, src++)\n        {\n            *dst++ = src[0*len];\n            *dst++ = src[1*len];\n            *dst++ = src[2*len];\n        }\n    }\n    MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float));\n}\nstatic void ma_dr_mp3_L3_antialias(float *grbuf, int nbands)\n{\n    static const float g_aa[2][8] = {\n        {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f},\n        {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f}\n    };\n    for (; nbands > 0; nbands--, grbuf += 18)\n    {\n        int i = 0;\n#if MA_DR_MP3_HAVE_SIMD\n        if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4)\n        {\n            ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i);\n            ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i);\n            ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i);\n            ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i);\n            vd = MA_DR_MP3_VREV(vd);\n            MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1)));\n            vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0));\n            MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd));\n        }\n#endif\n#ifndef MA_DR_MP3_ONLY_SIMD\n        for(; i < 8; i++)\n        {\n            float u = grbuf[18 + i];\n            float d = grbuf[17 - i];\n            grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i];\n            grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i];\n        }\n#endif\n    }\n}\nstatic void ma_dr_mp3_L3_dct3_9(float *y)\n{\n    float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4;\n    s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8];\n    t0 = s0 + s6*0.5f;\n    s0 -= s6;\n    t4 = (s4 + s2)*0.93969262f;\n    t2 = (s8 + s2)*0.76604444f;\n    s6 = (s4 - s8)*0.17364818f;\n    s4 += s8 - s2;\n    s2 = s0 - s4*0.5f;\n    y[4] = s4 + s0;\n    s8 = t0 - t2 + s6;\n    s0 = t0 - t4 + t2;\n    s4 = t0 + t4 - s6;\n    s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7];\n    s3 *= 0.86602540f;\n    t0 = (s5 + s1)*0.98480775f;\n    t4 = (s5 - s7)*0.34202014f;\n    t2 = (s1 + s7)*0.64278761f;\n    s1 = (s1 - s5 - s7)*0.86602540f;\n    s5 = t0 - s3 - t2;\n    s7 = t4 - s3 - t0;\n    s3 = t4 + s3 - t2;\n    y[0] = s4 - s7;\n    y[1] = s2 + s1;\n    y[2] = s0 - s3;\n    y[3] = s8 + s5;\n    y[5] = s8 - s5;\n    y[6] = s0 + s3;\n    y[7] = s2 - s1;\n    y[8] = s4 + s7;\n}\nstatic void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands)\n{\n    int i, j;\n    static const float g_twid9[18] = {\n        0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f\n    };\n    for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9)\n    {\n        float co[9], si[9];\n        co[0] = -grbuf[0];\n        si[0] = grbuf[17];\n        for (i = 0; i < 4; i++)\n        {\n            si[8 - 2*i] =   grbuf[4*i + 1] - grbuf[4*i + 2];\n            co[1 + 2*i] =   grbuf[4*i + 1] + grbuf[4*i + 2];\n            si[7 - 2*i] =   grbuf[4*i + 4] - grbuf[4*i + 3];\n            co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]);\n        }\n        ma_dr_mp3_L3_dct3_9(co);\n        ma_dr_mp3_L3_dct3_9(si);\n        si[1] = -si[1];\n        si[3] = -si[3];\n        si[5] = -si[5];\n        si[7] = -si[7];\n        i = 0;\n#if MA_DR_MP3_HAVE_SIMD\n        if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4)\n        {\n            ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i);\n            ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i);\n            ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i);\n            ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i);\n            ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i);\n            ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i);\n            ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i);\n            ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0));\n            MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1)));\n            MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1)));\n            vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0));\n            MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum));\n        }\n#endif\n        for (; i < 9; i++)\n        {\n            float ovl  = overlap[i];\n            float sum  = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i];\n            overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i];\n            grbuf[i]      = ovl*window[0 + i] - sum*window[9 + i];\n            grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i];\n        }\n    }\n}\nstatic void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst)\n{\n    float m1 = x1*0.86602540f;\n    float a1 = x0 - x2*0.5f;\n    dst[1] = x0 + x2;\n    dst[0] = a1 + m1;\n    dst[2] = a1 - m1;\n}\nstatic void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap)\n{\n    static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f };\n    float co[3], si[3];\n    int i;\n    ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co);\n    ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si);\n    si[1] = -si[1];\n    for (i = 0; i < 3; i++)\n    {\n        float ovl  = overlap[i];\n        float sum  = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i];\n        overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i];\n        dst[i]     = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i];\n        dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i];\n    }\n}\nstatic void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands)\n{\n    for (;nbands > 0; nbands--, overlap += 9, grbuf += 18)\n    {\n        float tmp[18];\n        MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp));\n        MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float));\n        ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6);\n        ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6);\n        ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6);\n    }\n}\nstatic void ma_dr_mp3_L3_change_sign(float *grbuf)\n{\n    int b, i;\n    for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36)\n        for (i = 1; i < 18; i += 2)\n            grbuf[i] = -grbuf[i];\n}\nstatic void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands)\n{\n    static const float g_mdct_window[2][18] = {\n        { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f },\n        { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f }\n    };\n    if (n_long_bands)\n    {\n        ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands);\n        grbuf += 18*n_long_bands;\n        overlap += 9*n_long_bands;\n    }\n    if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE)\n        ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands);\n    else\n        ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands);\n}\nstatic void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s)\n{\n    int pos = (s->bs.pos + 7)/8u;\n    int remains = s->bs.limit/8u - pos;\n    if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES)\n    {\n        pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES;\n        remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES;\n    }\n    if (remains > 0)\n    {\n        MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains);\n    }\n    h->reserv = remains;\n}\nstatic int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin)\n{\n    int frame_bytes = (bs->limit - bs->pos)/8;\n    int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin);\n    MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin));\n    MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes);\n    ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes);\n    return h->reserv >= main_data_begin;\n}\nstatic void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch)\n{\n    int ch;\n    for (ch = 0; ch < nch; ch++)\n    {\n        int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length;\n        ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch);\n        ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit);\n    }\n    if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header))\n    {\n        ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header);\n    } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header))\n    {\n        ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576);\n    }\n    for (ch = 0; ch < nch; ch++, gr_info++)\n    {\n        int aa_bands = 31;\n        int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2);\n        if (gr_info->n_short_sfb)\n        {\n            aa_bands = n_long_bands - 1;\n            ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb);\n        }\n        ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands);\n        ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands);\n        ma_dr_mp3_L3_change_sign(s->grbuf[ch]);\n    }\n}\nstatic void ma_dr_mp3d_DCT_II(float *grbuf, int n)\n{\n    static const float g_sec[24] = {\n        10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f\n    };\n    int i, k = 0;\n#if MA_DR_MP3_HAVE_SIMD\n    if (ma_dr_mp3_have_simd()) for (; k < n; k += 4)\n    {\n        ma_dr_mp3_f4 t[4][8], *x;\n        float *y = grbuf + k;\n        for (x = t[0], i = 0; i < 8; i++, x++)\n        {\n            ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]);\n            ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]);\n            ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]);\n            ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]);\n            ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3);\n            ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2);\n            ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]);\n            ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]);\n            x[0] = MA_DR_MP3_VADD(t0, t1);\n            x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]);\n            x[16] = MA_DR_MP3_VADD(t3, t2);\n            x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]);\n        }\n        for (x = t[0], i = 0; i < 4; i++, x += 8)\n        {\n            ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt;\n            xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7);\n            x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6);\n            x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5);\n            x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4);\n            x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3);\n            x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2);\n            x[0] = MA_DR_MP3_VADD(x0, x1);\n            x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f);\n            x5 = MA_DR_MP3_VADD(x5, x6);\n            x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f);\n            x7 = MA_DR_MP3_VADD(x7, xt);\n            x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f);\n            x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f));\n            x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f));\n            x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f));\n            x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6);\n            x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f);\n            x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f);\n            x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f);\n            x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f);\n            x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f);\n            x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f);\n        }\n        if (k > n - 3)\n        {\n#if MA_DR_MP3_HAVE_SSE\n#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v)\n#else\n#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18],  vget_low_f32(v))\n#endif\n            for (i = 0; i < 7; i++, y += 4*18)\n            {\n                ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]);\n                MA_DR_MP3_VSAVE2(0, t[0][i]);\n                MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s));\n                MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1]));\n                MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s));\n            }\n            MA_DR_MP3_VSAVE2(0, t[0][7]);\n            MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7]));\n            MA_DR_MP3_VSAVE2(2, t[1][7]);\n            MA_DR_MP3_VSAVE2(3, t[3][7]);\n        } else\n        {\n#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v)\n            for (i = 0; i < 7; i++, y += 4*18)\n            {\n                ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]);\n                MA_DR_MP3_VSAVE4(0, t[0][i]);\n                MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s));\n                MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1]));\n                MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s));\n            }\n            MA_DR_MP3_VSAVE4(0, t[0][7]);\n            MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7]));\n            MA_DR_MP3_VSAVE4(2, t[1][7]);\n            MA_DR_MP3_VSAVE4(3, t[3][7]);\n        }\n    } else\n#endif\n#ifdef MA_DR_MP3_ONLY_SIMD\n    {}\n#else\n    for (; k < n; k++)\n    {\n        float t[4][8], *x, *y = grbuf + k;\n        for (x = t[0], i = 0; i < 8; i++, x++)\n        {\n            float x0 = y[i*18];\n            float x1 = y[(15 - i)*18];\n            float x2 = y[(16 + i)*18];\n            float x3 = y[(31 - i)*18];\n            float t0 = x0 + x3;\n            float t1 = x1 + x2;\n            float t2 = (x1 - x2)*g_sec[3*i + 0];\n            float t3 = (x0 - x3)*g_sec[3*i + 1];\n            x[0] = t0 + t1;\n            x[8] = (t0 - t1)*g_sec[3*i + 2];\n            x[16] = t3 + t2;\n            x[24] = (t3 - t2)*g_sec[3*i + 2];\n        }\n        for (x = t[0], i = 0; i < 4; i++, x += 8)\n        {\n            float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt;\n            xt = x0 - x7; x0 += x7;\n            x7 = x1 - x6; x1 += x6;\n            x6 = x2 - x5; x2 += x5;\n            x5 = x3 - x4; x3 += x4;\n            x4 = x0 - x3; x0 += x3;\n            x3 = x1 - x2; x1 += x2;\n            x[0] = x0 + x1;\n            x[4] = (x0 - x1)*0.70710677f;\n            x5 =  x5 + x6;\n            x6 = (x6 + x7)*0.70710677f;\n            x7 =  x7 + xt;\n            x3 = (x3 + x4)*0.70710677f;\n            x5 -= x7*0.198912367f;\n            x7 += x5*0.382683432f;\n            x5 -= x7*0.198912367f;\n            x0 = xt - x6; xt += x6;\n            x[1] = (xt + x7)*0.50979561f;\n            x[2] = (x4 + x3)*0.54119611f;\n            x[3] = (x0 - x5)*0.60134488f;\n            x[5] = (x0 + x5)*0.89997619f;\n            x[6] = (x4 - x3)*1.30656302f;\n            x[7] = (xt - x7)*2.56291556f;\n        }\n        for (i = 0; i < 7; i++, y += 4*18)\n        {\n            y[0*18] = t[0][i];\n            y[1*18] = t[2][i] + t[3][i] + t[3][i + 1];\n            y[2*18] = t[1][i] + t[1][i + 1];\n            y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1];\n        }\n        y[0*18] = t[0][7];\n        y[1*18] = t[2][7] + t[3][7];\n        y[2*18] = t[1][7];\n        y[3*18] = t[3][7];\n    }\n#endif\n}\n#ifndef MA_DR_MP3_FLOAT_OUTPUT\ntypedef ma_int16 ma_dr_mp3d_sample_t;\nstatic ma_int16 ma_dr_mp3d_scale_pcm(float sample)\n{\n    ma_int16 s;\n#if MA_DR_MP3_HAVE_ARMV6\n    ma_int32 s32 = (ma_int32)(sample + .5f);\n    s32 -= (s32 < 0);\n    s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32);\n#else\n    if (sample >=  32766.5) return (ma_int16) 32767;\n    if (sample <= -32767.5) return (ma_int16)-32768;\n    s = (ma_int16)(sample + .5f);\n    s -= (s < 0);\n#endif\n    return s;\n}\n#else\ntypedef float ma_dr_mp3d_sample_t;\nstatic float ma_dr_mp3d_scale_pcm(float sample)\n{\n    return sample*(1.f/32768.f);\n}\n#endif\nstatic void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z)\n{\n    float a;\n    a  = (z[14*64] - z[    0]) * 29;\n    a += (z[ 1*64] + z[13*64]) * 213;\n    a += (z[12*64] - z[ 2*64]) * 459;\n    a += (z[ 3*64] + z[11*64]) * 2037;\n    a += (z[10*64] - z[ 4*64]) * 5153;\n    a += (z[ 5*64] + z[ 9*64]) * 6574;\n    a += (z[ 8*64] - z[ 6*64]) * 37489;\n    a +=  z[ 7*64]             * 75038;\n    pcm[0] = ma_dr_mp3d_scale_pcm(a);\n    z += 2;\n    a  = z[14*64] * 104;\n    a += z[12*64] * 1567;\n    a += z[10*64] * 9727;\n    a += z[ 8*64] * 64019;\n    a += z[ 6*64] * -9975;\n    a += z[ 4*64] * -45;\n    a += z[ 2*64] * 146;\n    a += z[ 0*64] * -5;\n    pcm[16*nch] = ma_dr_mp3d_scale_pcm(a);\n}\nstatic void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins)\n{\n    int i;\n    float *xr = xl + 576*(nch - 1);\n    ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1);\n    static const float g_win[] = {\n        -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,\n        -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856,\n        -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630,\n        -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313,\n        -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908,\n        -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415,\n        -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835,\n        -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169,\n        -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420,\n        -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590,\n        -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679,\n        -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692,\n        -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629,\n        -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494,\n        -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290\n    };\n    float *zlin = lins + 15*64;\n    const float *w = g_win;\n    zlin[4*15]     = xl[18*16];\n    zlin[4*15 + 1] = xr[18*16];\n    zlin[4*15 + 2] = xl[0];\n    zlin[4*15 + 3] = xr[0];\n    zlin[4*31]     = xl[1 + 18*16];\n    zlin[4*31 + 1] = xr[1 + 18*16];\n    zlin[4*31 + 2] = xl[1];\n    zlin[4*31 + 3] = xr[1];\n    ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1);\n    ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1);\n    ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15);\n    ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64);\n#if MA_DR_MP3_HAVE_SIMD\n    if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--)\n    {\n#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]);\n#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b =               MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a =               MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1));  }\n#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); }\n#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); }\n        ma_dr_mp3_f4 a, b;\n        zlin[4*i]     = xl[18*(31 - i)];\n        zlin[4*i + 1] = xr[18*(31 - i)];\n        zlin[4*i + 2] = xl[1 + 18*(31 - i)];\n        zlin[4*i + 3] = xr[1 + 18*(31 - i)];\n        zlin[4*i + 64] = xl[1 + 18*(1 + i)];\n        zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)];\n        zlin[4*i - 64 + 2] = xl[18*(1 + i)];\n        zlin[4*i - 64 + 3] = xr[18*(1 + i)];\n        MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7)\n        {\n#ifndef MA_DR_MP3_FLOAT_OUTPUT\n#if MA_DR_MP3_HAVE_SSE\n            static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };\n            static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };\n            __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),\n                                           _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));\n            dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1);\n            dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5);\n            dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0);\n            dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4);\n            dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3);\n            dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7);\n            dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2);\n            dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6);\n#else\n            int16x4_t pcma, pcmb;\n            a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f));\n            b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f));\n            pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0)))));\n            pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0)))));\n            vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1);\n            vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1);\n            vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0);\n            vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0);\n            vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3);\n            vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3);\n            vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);\n            vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);\n#endif\n#else\n        #if MA_DR_MP3_HAVE_SSE\n            static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };\n        #else\n            const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f);\n        #endif\n            a = MA_DR_MP3_VMUL(a, g_scale);\n            b = MA_DR_MP3_VMUL(b, g_scale);\n#if MA_DR_MP3_HAVE_SSE\n            _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));\n            _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));\n            _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));\n            _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));\n            _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));\n            _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));\n            _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));\n            _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));\n#else\n            vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);\n            vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);\n            vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);\n            vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);\n            vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);\n            vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);\n            vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);\n            vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);\n#endif\n#endif\n        }\n    } else\n#endif\n#ifdef MA_DR_MP3_ONLY_SIMD\n    {}\n#else\n    for (i = 14; i >= 0; i--)\n    {\n#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64];\n#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j]  = vz[j]*w1 + vy[j]*w0, a[j]  = vz[j]*w0 - vy[j]*w1; }\n#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; }\n#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; }\n        float a[4], b[4];\n        zlin[4*i]     = xl[18*(31 - i)];\n        zlin[4*i + 1] = xr[18*(31 - i)];\n        zlin[4*i + 2] = xl[1 + 18*(31 - i)];\n        zlin[4*i + 3] = xr[1 + 18*(31 - i)];\n        zlin[4*(i + 16)]   = xl[1 + 18*(1 + i)];\n        zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)];\n        zlin[4*(i - 16) + 2] = xl[18*(1 + i)];\n        zlin[4*(i - 16) + 3] = xr[18*(1 + i)];\n        MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7)\n        dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]);\n        dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]);\n        dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]);\n        dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]);\n        dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]);\n        dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]);\n        dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]);\n        dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]);\n    }\n#endif\n}\nstatic void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins)\n{\n    int i;\n    for (i = 0; i < nch; i++)\n    {\n        ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands);\n    }\n    MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64);\n    for (i = 0; i < nbands; i += 2)\n    {\n        ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64);\n    }\n#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL\n    if (nch == 1)\n    {\n        for (i = 0; i < 15*64; i += 2)\n        {\n            qmf_state[i] = lins[nbands*64 + i];\n        }\n    } else\n#endif\n    {\n        MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64);\n    }\n}\nstatic int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes)\n{\n    int i, nmatch;\n    for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++)\n    {\n        i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i);\n        if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes)\n            return nmatch > 0;\n        if (!ma_dr_mp3_hdr_compare(hdr, hdr + i))\n            return 0;\n    }\n    return 1;\n}\nstatic int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes)\n{\n    int i, k;\n    for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++)\n    {\n        if (ma_dr_mp3_hdr_valid(mp3))\n        {\n            int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes);\n            int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3);\n            for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++)\n            {\n                if (ma_dr_mp3_hdr_compare(mp3, mp3 + k))\n                {\n                    int fb = k - ma_dr_mp3_hdr_padding(mp3);\n                    int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k);\n                    if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb))\n                        continue;\n                    frame_and_padding = k;\n                    frame_bytes = fb;\n                    *free_format_bytes = fb;\n                }\n            }\n            if ((frame_bytes && i + frame_and_padding <= mp3_bytes &&\n                ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) ||\n                (!i && frame_and_padding == mp3_bytes))\n            {\n                *ptr_frame_bytes = frame_and_padding;\n                return i;\n            }\n            *free_format_bytes = 0;\n        }\n    }\n    *ptr_frame_bytes = 0;\n    return mp3_bytes;\n}\nMA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec)\n{\n    dec->header[0] = 0;\n}\nMA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info)\n{\n    int i = 0, igr, frame_size = 0, success = 1;\n    const ma_uint8 *hdr;\n    ma_dr_mp3_bs bs_frame[1];\n    ma_dr_mp3dec_scratch scratch;\n    if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3))\n    {\n        frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3);\n        if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size)))\n        {\n            frame_size = 0;\n        }\n    }\n    if (!frame_size)\n    {\n        MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec));\n        i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);\n        if (!frame_size || i + frame_size > mp3_bytes)\n        {\n            info->frame_bytes = i;\n            return 0;\n        }\n    }\n    hdr = mp3 + i;\n    MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE);\n    info->frame_bytes = i + frame_size;\n    info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2;\n    info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr);\n    info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr);\n    info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr);\n    ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE);\n    if (MA_DR_MP3_HDR_IS_CRC(hdr))\n    {\n        ma_dr_mp3_bs_get_bits(bs_frame, 16);\n    }\n    if (info->layer == 3)\n    {\n        int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr);\n        if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit)\n        {\n            ma_dr_mp3dec_init(dec);\n            return 0;\n        }\n        success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);\n        if (success && pcm != NULL)\n        {\n            for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels))\n            {\n                MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));\n                ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);\n                ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]);\n            }\n        }\n        ma_dr_mp3_L3_save_reservoir(dec, &scratch);\n    } else\n    {\n#ifdef MA_DR_MP3_ONLY_MP3\n        return 0;\n#else\n        ma_dr_mp3_L12_scale_info sci[1];\n        if (pcm == NULL) {\n            return ma_dr_mp3_hdr_frame_samples(hdr);\n        }\n        ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci);\n        MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));\n        for (i = 0, igr = 0; igr < 3; igr++)\n        {\n            if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))\n            {\n                i = 0;\n                ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);\n                ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]);\n                MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));\n                pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels);\n            }\n            if (bs_frame->pos > bs_frame->limit)\n            {\n                ma_dr_mp3dec_init(dec);\n                return 0;\n            }\n        }\n#endif\n    }\n    return success*ma_dr_mp3_hdr_frame_samples(dec->header);\n}\nMA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples)\n{\n    size_t i = 0;\n#if MA_DR_MP3_HAVE_SIMD\n    size_t aligned_count = num_samples & ~7;\n    for(; i < aligned_count; i+=8)\n    {\n        ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f);\n        ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i  ]), scale);\n        ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale);\n#if MA_DR_MP3_HAVE_SSE\n        ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f);\n        ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f);\n        __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)),\n                                        _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min)));\n        out[i  ] = (ma_int16)_mm_extract_epi16(pcm8, 0);\n        out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1);\n        out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2);\n        out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3);\n        out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4);\n        out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5);\n        out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6);\n        out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7);\n#else\n        int16x4_t pcma, pcmb;\n        a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f));\n        b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f));\n        pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0)))));\n        pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0)))));\n        vst1_lane_s16(out+i  , pcma, 0);\n        vst1_lane_s16(out+i+1, pcma, 1);\n        vst1_lane_s16(out+i+2, pcma, 2);\n        vst1_lane_s16(out+i+3, pcma, 3);\n        vst1_lane_s16(out+i+4, pcmb, 0);\n        vst1_lane_s16(out+i+5, pcmb, 1);\n        vst1_lane_s16(out+i+6, pcmb, 2);\n        vst1_lane_s16(out+i+7, pcmb, 3);\n#endif\n    }\n#endif\n    for(; i < num_samples; i++)\n    {\n        float sample = in[i] * 32768.0f;\n        if (sample >=  32766.5)\n            out[i] = (ma_int16) 32767;\n        else if (sample <= -32767.5)\n            out[i] = (ma_int16)-32768;\n        else\n        {\n            short s = (ma_int16)(sample + .5f);\n            s -= (s < 0);\n            out[i] = s;\n        }\n    }\n}\n#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES\n#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES   2\n#endif\n#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE   16384\n#ifndef MA_DR_MP3_DATA_CHUNK_SIZE\n#define MA_DR_MP3_DATA_CHUNK_SIZE  (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4)\n#endif\n#define MA_DR_MP3_COUNTOF(x)        (sizeof(x) / sizeof(x[0]))\n#define MA_DR_MP3_CLAMP(x, lo, hi)  (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi)))\n#ifndef MA_DR_MP3_PI_D\n#define MA_DR_MP3_PI_D    3.14159265358979323846264\n#endif\n#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER   2\nstatic MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a)\n{\n    return x*(1-a) + y*a;\n}\nstatic MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a)\n{\n    float r0 = (y - x);\n    float r1 = r0*a;\n    return x + r1;\n}\nstatic MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b)\n{\n    for (;;) {\n        if (b == 0) {\n            break;\n        } else {\n            ma_uint32 t = a;\n            a = b;\n            b = t % a;\n        }\n    }\n    return a;\n}\nstatic void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData)\n{\n    (void)pUserData;\n    return MA_DR_MP3_MALLOC(sz);\n}\nstatic void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData)\n{\n    (void)pUserData;\n    return MA_DR_MP3_REALLOC(p, sz);\n}\nstatic void ma_dr_mp3__free_default(void* p, void* pUserData)\n{\n    (void)pUserData;\n    MA_DR_MP3_FREE(p);\n}\nstatic void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks == NULL) {\n        return NULL;\n    }\n    if (pAllocationCallbacks->onMalloc != NULL) {\n        return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);\n    }\n    if (pAllocationCallbacks->onRealloc != NULL) {\n        return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);\n    }\n    return NULL;\n}\nstatic void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks == NULL) {\n        return NULL;\n    }\n    if (pAllocationCallbacks->onRealloc != NULL) {\n        return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);\n    }\n    if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {\n        void* p2;\n        p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);\n        if (p2 == NULL) {\n            return NULL;\n        }\n        if (p != NULL) {\n            MA_DR_MP3_COPY_MEMORY(p2, p, szOld);\n            pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);\n        }\n        return p2;\n    }\n    return NULL;\n}\nstatic void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (p == NULL || pAllocationCallbacks == NULL) {\n        return;\n    }\n    if (pAllocationCallbacks->onFree != NULL) {\n        pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);\n    }\n}\nstatic ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks != NULL) {\n        return *pAllocationCallbacks;\n    } else {\n        ma_allocation_callbacks allocationCallbacks;\n        allocationCallbacks.pUserData = NULL;\n        allocationCallbacks.onMalloc  = ma_dr_mp3__malloc_default;\n        allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default;\n        allocationCallbacks.onFree    = ma_dr_mp3__free_default;\n        return allocationCallbacks;\n    }\n}\nstatic size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead)\n{\n    size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);\n    pMP3->streamCursor += bytesRead;\n    return bytesRead;\n}\nstatic ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin)\n{\n    MA_DR_MP3_ASSERT(offset >= 0);\n    if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {\n        return MA_FALSE;\n    }\n    if (origin == ma_dr_mp3_seek_origin_start) {\n        pMP3->streamCursor = (ma_uint64)offset;\n    } else {\n        pMP3->streamCursor += offset;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin)\n{\n    if (offset <= 0x7FFFFFFF) {\n        return ma_dr_mp3__on_seek(pMP3, (int)offset, origin);\n    }\n    if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) {\n        return MA_FALSE;\n    }\n    offset -= 0x7FFFFFFF;\n    while (offset > 0) {\n        if (offset <= 0x7FFFFFFF) {\n            if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) {\n                return MA_FALSE;\n            }\n            offset = 0;\n        } else {\n            if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) {\n                return MA_FALSE;\n            }\n            offset -= 0x7FFFFFFF;\n        }\n    }\n    return MA_TRUE;\n}\nstatic ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames)\n{\n    ma_uint32 pcmFramesRead = 0;\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    MA_DR_MP3_ASSERT(pMP3->onRead != NULL);\n    if (pMP3->atEnd) {\n        return 0;\n    }\n    for (;;) {\n        ma_dr_mp3dec_frame_info info;\n        if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) {\n            size_t bytesRead;\n            if (pMP3->pData != NULL) {\n                MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);\n            }\n            pMP3->dataConsumed = 0;\n            if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) {\n                ma_uint8* pNewData;\n                size_t newDataCap;\n                newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE;\n                pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);\n                if (pNewData == NULL) {\n                    return 0;\n                }\n                pMP3->pData = pNewData;\n                pMP3->dataCapacity = newDataCap;\n            }\n            bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));\n            if (bytesRead == 0) {\n                if (pMP3->dataSize == 0) {\n                    pMP3->atEnd = MA_TRUE;\n                    return 0;\n                }\n            }\n            pMP3->dataSize += bytesRead;\n        }\n        if (pMP3->dataSize > INT_MAX) {\n            pMP3->atEnd = MA_TRUE;\n            return 0;\n        }\n        MA_DR_MP3_ASSERT(pMP3->pData != NULL);\n        MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0);\n        if (pMP3->pData == NULL) {\n            return 0;\n        }\n        pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info);\n        if (info.frame_bytes > 0) {\n            pMP3->dataConsumed += (size_t)info.frame_bytes;\n            pMP3->dataSize     -= (size_t)info.frame_bytes;\n        }\n        if (pcmFramesRead > 0) {\n            pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header);\n            pMP3->pcmFramesConsumedInMP3Frame = 0;\n            pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;\n            pMP3->mp3FrameChannels = info.channels;\n            pMP3->mp3FrameSampleRate = info.hz;\n            break;\n        } else if (info.frame_bytes == 0) {\n            size_t bytesRead;\n            MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);\n            pMP3->dataConsumed = 0;\n            if (pMP3->dataCapacity == pMP3->dataSize) {\n                ma_uint8* pNewData;\n                size_t newDataCap;\n                newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE;\n                pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);\n                if (pNewData == NULL) {\n                    return 0;\n                }\n                pMP3->pData = pNewData;\n                pMP3->dataCapacity = newDataCap;\n            }\n            bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));\n            if (bytesRead == 0) {\n                pMP3->atEnd = MA_TRUE;\n                return 0;\n            }\n            pMP3->dataSize += bytesRead;\n        }\n    };\n    return pcmFramesRead;\n}\nstatic ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames)\n{\n    ma_uint32 pcmFramesRead = 0;\n    ma_dr_mp3dec_frame_info info;\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL);\n    if (pMP3->atEnd) {\n        return 0;\n    }\n    for (;;) {\n        pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info);\n        if (pcmFramesRead > 0) {\n            pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header);\n            pMP3->pcmFramesConsumedInMP3Frame  = 0;\n            pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;\n            pMP3->mp3FrameChannels             = info.channels;\n            pMP3->mp3FrameSampleRate           = info.hz;\n            break;\n        } else if (info.frame_bytes > 0) {\n            pMP3->memory.currentReadPos += (size_t)info.frame_bytes;\n        } else {\n            break;\n        }\n    }\n    pMP3->memory.currentReadPos += (size_t)info.frame_bytes;\n    return pcmFramesRead;\n}\nstatic ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames)\n{\n    if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) {\n        return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames);\n    } else {\n        return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames);\n    }\n}\nstatic ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3)\n{\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames);\n}\n#if 0\nstatic ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3)\n{\n    ma_uint32 pcmFrameCount;\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL);\n    if (pcmFrameCount == 0) {\n        return 0;\n    }\n    pMP3->currentPCMFrame             += pcmFrameCount;\n    pMP3->pcmFramesConsumedInMP3Frame  = pcmFrameCount;\n    pMP3->pcmFramesRemainingInMP3Frame = 0;\n    return pcmFrameCount;\n}\n#endif\nstatic ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    MA_DR_MP3_ASSERT(onRead != NULL);\n    ma_dr_mp3dec_init(&pMP3->decoder);\n    pMP3->onRead = onRead;\n    pMP3->onSeek = onSeek;\n    pMP3->pUserData = pUserData;\n    pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);\n    if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) {\n        return MA_FALSE;\n    }\n    if (ma_dr_mp3_decode_next_frame(pMP3) == 0) {\n        ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);\n        return MA_FALSE;\n    }\n    pMP3->channels   = pMP3->mp3FrameChannels;\n    pMP3->sampleRate = pMP3->mp3FrameSampleRate;\n    return MA_TRUE;\n}\nMA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pMP3 == NULL || onRead == NULL) {\n        return MA_FALSE;\n    }\n    MA_DR_MP3_ZERO_OBJECT(pMP3);\n    return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks);\n}\nstatic size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)\n{\n    ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData;\n    size_t bytesRemaining;\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);\n    bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;\n    if (bytesToRead > bytesRemaining) {\n        bytesToRead = bytesRemaining;\n    }\n    if (bytesToRead > 0) {\n        MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead);\n        pMP3->memory.currentReadPos += bytesToRead;\n    }\n    return bytesToRead;\n}\nstatic ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin)\n{\n    ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData;\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    if (origin == ma_dr_mp3_seek_origin_current) {\n        if (byteOffset > 0) {\n            if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) {\n                byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos);\n            }\n        } else {\n            if (pMP3->memory.currentReadPos < (size_t)-byteOffset) {\n                byteOffset = -(int)pMP3->memory.currentReadPos;\n            }\n        }\n        pMP3->memory.currentReadPos += byteOffset;\n    } else {\n        if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) {\n            pMP3->memory.currentReadPos = byteOffset;\n        } else {\n            pMP3->memory.currentReadPos = pMP3->memory.dataSize;\n        }\n    }\n    return MA_TRUE;\n}\nMA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pMP3 == NULL) {\n        return MA_FALSE;\n    }\n    MA_DR_MP3_ZERO_OBJECT(pMP3);\n    if (pData == NULL || dataSize == 0) {\n        return MA_FALSE;\n    }\n    pMP3->memory.pData = (const ma_uint8*)pData;\n    pMP3->memory.dataSize = dataSize;\n    pMP3->memory.currentReadPos = 0;\n    return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks);\n}\n#ifndef MA_DR_MP3_NO_STDIO\n#include <stdio.h>\n#include <wchar.h>\nstatic size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)\n{\n    return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);\n}\nstatic ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin)\n{\n    return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;\n}\nMA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_bool32 result;\n    FILE* pFile;\n    if (ma_fopen(&pFile, pFilePath, \"rb\") != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n    result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);\n    if (result != MA_TRUE) {\n        fclose(pFile);\n        return result;\n    }\n    return MA_TRUE;\n}\nMA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_bool32 result;\n    FILE* pFile;\n    if (ma_wfopen(&pFile, pFilePath, L\"rb\", pAllocationCallbacks) != MA_SUCCESS) {\n        return MA_FALSE;\n    }\n    result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);\n    if (result != MA_TRUE) {\n        fclose(pFile);\n        return result;\n    }\n    return MA_TRUE;\n}\n#endif\nMA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3)\n{\n    if (pMP3 == NULL) {\n        return;\n    }\n#ifndef MA_DR_MP3_NO_STDIO\n    if (pMP3->onRead == ma_dr_mp3__on_read_stdio) {\n        FILE* pFile = (FILE*)pMP3->pUserData;\n        if (pFile != NULL) {\n            fclose(pFile);\n            pMP3->pUserData = NULL;\n        }\n    }\n#endif\n    ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);\n}\n#if defined(MA_DR_MP3_FLOAT_OUTPUT)\nstatic void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount)\n{\n    ma_uint64 i;\n    ma_uint64 i4;\n    ma_uint64 sampleCount4;\n    i = 0;\n    sampleCount4 = sampleCount >> 2;\n    for (i4 = 0; i4 < sampleCount4; i4 += 1) {\n        float x0 = src[i+0];\n        float x1 = src[i+1];\n        float x2 = src[i+2];\n        float x3 = src[i+3];\n        x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));\n        x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));\n        x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));\n        x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));\n        x0 = x0 * 32767.0f;\n        x1 = x1 * 32767.0f;\n        x2 = x2 * 32767.0f;\n        x3 = x3 * 32767.0f;\n        dst[i+0] = (ma_int16)x0;\n        dst[i+1] = (ma_int16)x1;\n        dst[i+2] = (ma_int16)x2;\n        dst[i+3] = (ma_int16)x3;\n        i += 4;\n    }\n    for (; i < sampleCount; i += 1) {\n        float x = src[i];\n        x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));\n        x = x * 32767.0f;\n        dst[i] = (ma_int16)x;\n    }\n}\n#endif\n#if !defined(MA_DR_MP3_FLOAT_OUTPUT)\nstatic void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount)\n{\n    ma_uint64 i;\n    for (i = 0; i < sampleCount; i += 1) {\n        float x = (float)src[i];\n        x = x * 0.000030517578125f;\n        dst[i] = x;\n    }\n}\n#endif\nstatic ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut)\n{\n    ma_uint64 totalFramesRead = 0;\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    MA_DR_MP3_ASSERT(pMP3->onRead != NULL);\n    while (framesToRead > 0) {\n        ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);\n        if (pBufferOut != NULL) {\n        #if defined(MA_DR_MP3_FLOAT_OUTPUT)\n            float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut,          sizeof(float) * totalFramesRead                   * pMP3->channels);\n            float* pFramesInF32  = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);\n            MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);\n        #else\n            ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut,          sizeof(ma_int16) * totalFramesRead                   * pMP3->channels);\n            ma_int16* pFramesInS16  = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);\n            MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels);\n        #endif\n        }\n        pMP3->currentPCMFrame              += framesToConsume;\n        pMP3->pcmFramesConsumedInMP3Frame  += framesToConsume;\n        pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume;\n        totalFramesRead                    += framesToConsume;\n        framesToRead                       -= framesToConsume;\n        if (framesToRead == 0) {\n            break;\n        }\n        MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);\n        if (ma_dr_mp3_decode_next_frame(pMP3) == 0) {\n            break;\n        }\n    }\n    return totalFramesRead;\n}\nMA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut)\n{\n    if (pMP3 == NULL || pMP3->onRead == NULL) {\n        return 0;\n    }\n#if defined(MA_DR_MP3_FLOAT_OUTPUT)\n    return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);\n#else\n    {\n        ma_int16 pTempS16[8192];\n        ma_uint64 totalPCMFramesRead = 0;\n        while (totalPCMFramesRead < framesToRead) {\n            ma_uint64 framesJustRead;\n            ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead;\n            ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels;\n            if (framesToReadNow > framesRemaining) {\n                framesToReadNow = framesRemaining;\n            }\n            framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16);\n            if (framesJustRead == 0) {\n                break;\n            }\n            ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);\n            totalPCMFramesRead += framesJustRead;\n        }\n        return totalPCMFramesRead;\n    }\n#endif\n}\nMA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut)\n{\n    if (pMP3 == NULL || pMP3->onRead == NULL) {\n        return 0;\n    }\n#if !defined(MA_DR_MP3_FLOAT_OUTPUT)\n    return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);\n#else\n    {\n        float pTempF32[4096];\n        ma_uint64 totalPCMFramesRead = 0;\n        while (totalPCMFramesRead < framesToRead) {\n            ma_uint64 framesJustRead;\n            ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead;\n            ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels;\n            if (framesToReadNow > framesRemaining) {\n                framesToReadNow = framesRemaining;\n            }\n            framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32);\n            if (framesJustRead == 0) {\n                break;\n            }\n            ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);\n            totalPCMFramesRead += framesJustRead;\n        }\n        return totalPCMFramesRead;\n    }\n#endif\n}\nstatic void ma_dr_mp3_reset(ma_dr_mp3* pMP3)\n{\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    pMP3->pcmFramesConsumedInMP3Frame = 0;\n    pMP3->pcmFramesRemainingInMP3Frame = 0;\n    pMP3->currentPCMFrame = 0;\n    pMP3->dataSize = 0;\n    pMP3->atEnd = MA_FALSE;\n    ma_dr_mp3dec_init(&pMP3->decoder);\n}\nstatic ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3)\n{\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    MA_DR_MP3_ASSERT(pMP3->onSeek != NULL);\n    if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) {\n        return MA_FALSE;\n    }\n    ma_dr_mp3_reset(pMP3);\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset)\n{\n    ma_uint64 framesRead;\n#if defined(MA_DR_MP3_FLOAT_OUTPUT)\n    framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);\n#else\n    framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);\n#endif\n    if (framesRead != frameOffset) {\n        return MA_FALSE;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex)\n{\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    if (frameIndex == pMP3->currentPCMFrame) {\n        return MA_TRUE;\n    }\n    if (frameIndex < pMP3->currentPCMFrame) {\n        if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {\n            return MA_FALSE;\n        }\n    }\n    MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);\n    return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));\n}\nstatic ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex)\n{\n    ma_uint32 iSeekPoint;\n    MA_DR_MP3_ASSERT(pSeekPointIndex != NULL);\n    *pSeekPointIndex = 0;\n    if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {\n        return MA_FALSE;\n    }\n    for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {\n        if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {\n            break;\n        }\n        *pSeekPointIndex = iSeekPoint;\n    }\n    return MA_TRUE;\n}\nstatic ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex)\n{\n    ma_dr_mp3_seek_point seekPoint;\n    ma_uint32 priorSeekPointIndex;\n    ma_uint16 iMP3Frame;\n    ma_uint64 leftoverFrames;\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL);\n    MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0);\n    if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {\n        seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];\n    } else {\n        seekPoint.seekPosInBytes     = 0;\n        seekPoint.pcmFrameIndex      = 0;\n        seekPoint.mp3FramesToDiscard = 0;\n        seekPoint.pcmFramesToDiscard = 0;\n    }\n    if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) {\n        return MA_FALSE;\n    }\n    ma_dr_mp3_reset(pMP3);\n    for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {\n        ma_uint32 pcmFramesRead;\n        ma_dr_mp3d_sample_t* pPCMFrames;\n        pPCMFrames = NULL;\n        if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) {\n            pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames;\n        }\n        pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames);\n        if (pcmFramesRead == 0) {\n            return MA_FALSE;\n        }\n    }\n    pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard;\n    leftoverFrames = frameIndex - pMP3->currentPCMFrame;\n    return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames);\n}\nMA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex)\n{\n    if (pMP3 == NULL || pMP3->onSeek == NULL) {\n        return MA_FALSE;\n    }\n    if (frameIndex == 0) {\n        return ma_dr_mp3_seek_to_start_of_stream(pMP3);\n    }\n    if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) {\n        return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex);\n    } else {\n        return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);\n    }\n}\nMA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount)\n{\n    ma_uint64 currentPCMFrame;\n    ma_uint64 totalPCMFrameCount;\n    ma_uint64 totalMP3FrameCount;\n    if (pMP3 == NULL) {\n        return MA_FALSE;\n    }\n    if (pMP3->onSeek == NULL) {\n        return MA_FALSE;\n    }\n    currentPCMFrame = pMP3->currentPCMFrame;\n    if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {\n        return MA_FALSE;\n    }\n    totalPCMFrameCount = 0;\n    totalMP3FrameCount = 0;\n    for (;;) {\n        ma_uint32 pcmFramesInCurrentMP3Frame;\n        pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL);\n        if (pcmFramesInCurrentMP3Frame == 0) {\n            break;\n        }\n        totalPCMFrameCount += pcmFramesInCurrentMP3Frame;\n        totalMP3FrameCount += 1;\n    }\n    if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {\n        return MA_FALSE;\n    }\n    if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {\n        return MA_FALSE;\n    }\n    if (pMP3FrameCount != NULL) {\n        *pMP3FrameCount = totalMP3FrameCount;\n    }\n    if (pPCMFrameCount != NULL) {\n        *pPCMFrameCount = totalPCMFrameCount;\n    }\n    return MA_TRUE;\n}\nMA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3)\n{\n    ma_uint64 totalPCMFrameCount;\n    if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {\n        return 0;\n    }\n    return totalPCMFrameCount;\n}\nMA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3)\n{\n    ma_uint64 totalMP3FrameCount;\n    if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {\n        return 0;\n    }\n    return totalMP3FrameCount;\n}\nstatic void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart)\n{\n    float srcRatio;\n    float pcmFrameCountOutF;\n    ma_uint32 pcmFrameCountOut;\n    srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;\n    MA_DR_MP3_ASSERT(srcRatio > 0);\n    pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio);\n    pcmFrameCountOut  = (ma_uint32)pcmFrameCountOutF;\n    *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut;\n    *pRunningPCMFrameCount += pcmFrameCountOut;\n}\ntypedef struct\n{\n    ma_uint64 bytePos;\n    ma_uint64 pcmFrameIndex;\n} ma_dr_mp3__seeking_mp3_frame_info;\nMA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints)\n{\n    ma_uint32 seekPointCount;\n    ma_uint64 currentPCMFrame;\n    ma_uint64 totalMP3FrameCount;\n    ma_uint64 totalPCMFrameCount;\n    if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) {\n        return MA_FALSE;\n    }\n    seekPointCount = *pSeekPointCount;\n    if (seekPointCount == 0) {\n        return MA_FALSE;\n    }\n    currentPCMFrame = pMP3->currentPCMFrame;\n    if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {\n        return MA_FALSE;\n    }\n    if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) {\n        seekPointCount = 1;\n        pSeekPoints[0].seekPosInBytes     = 0;\n        pSeekPoints[0].pcmFrameIndex      = 0;\n        pSeekPoints[0].mp3FramesToDiscard = 0;\n        pSeekPoints[0].pcmFramesToDiscard = 0;\n    } else {\n        ma_uint64 pcmFramesBetweenSeekPoints;\n        ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1];\n        ma_uint64 runningPCMFrameCount = 0;\n        float runningPCMFrameCountFractionalPart = 0;\n        ma_uint64 nextTargetPCMFrame;\n        ma_uint32 iMP3Frame;\n        ma_uint32 iSeekPoint;\n        if (seekPointCount > totalMP3FrameCount-1) {\n            seekPointCount = (ma_uint32)totalMP3FrameCount-1;\n        }\n        pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);\n        if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {\n            return MA_FALSE;\n        }\n        for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {\n            ma_uint32 pcmFramesInCurrentMP3FrameIn;\n            MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize);\n            mp3FrameInfo[iMP3Frame].bytePos       = pMP3->streamCursor - pMP3->dataSize;\n            mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;\n            pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL);\n            if (pcmFramesInCurrentMP3FrameIn == 0) {\n                return MA_FALSE;\n            }\n            ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);\n        }\n        nextTargetPCMFrame = 0;\n        for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {\n            nextTargetPCMFrame += pcmFramesBetweenSeekPoints;\n            for (;;) {\n                if (nextTargetPCMFrame < runningPCMFrameCount) {\n                    pSeekPoints[iSeekPoint].seekPosInBytes     = mp3FrameInfo[0].bytePos;\n                    pSeekPoints[iSeekPoint].pcmFrameIndex      = nextTargetPCMFrame;\n                    pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES;\n                    pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);\n                    break;\n                } else {\n                    size_t i;\n                    ma_uint32 pcmFramesInCurrentMP3FrameIn;\n                    for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) {\n                        mp3FrameInfo[i] = mp3FrameInfo[i+1];\n                    }\n                    mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos       = pMP3->streamCursor - pMP3->dataSize;\n                    mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;\n                    pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL);\n                    if (pcmFramesInCurrentMP3FrameIn == 0) {\n                        pSeekPoints[iSeekPoint].seekPosInBytes     = mp3FrameInfo[0].bytePos;\n                        pSeekPoints[iSeekPoint].pcmFrameIndex      = nextTargetPCMFrame;\n                        pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES;\n                        pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);\n                        break;\n                    }\n                    ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);\n                }\n            }\n        }\n        if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {\n            return MA_FALSE;\n        }\n        if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {\n            return MA_FALSE;\n        }\n    }\n    *pSeekPointCount = seekPointCount;\n    return MA_TRUE;\n}\nMA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints)\n{\n    if (pMP3 == NULL) {\n        return MA_FALSE;\n    }\n    if (seekPointCount == 0 || pSeekPoints == NULL) {\n        pMP3->seekPointCount = 0;\n        pMP3->pSeekPoints = NULL;\n    } else {\n        pMP3->seekPointCount = seekPointCount;\n        pMP3->pSeekPoints = pSeekPoints;\n    }\n    return MA_TRUE;\n}\nstatic float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount)\n{\n    ma_uint64 totalFramesRead = 0;\n    ma_uint64 framesCapacity = 0;\n    float* pFrames = NULL;\n    float temp[4096];\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    for (;;) {\n        ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels;\n        ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);\n        if (framesJustRead == 0) {\n            break;\n        }\n        if (framesCapacity < totalFramesRead + framesJustRead) {\n            ma_uint64 oldFramesBufferSize;\n            ma_uint64 newFramesBufferSize;\n            ma_uint64 newFramesCap;\n            float* pNewFrames;\n            newFramesCap = framesCapacity * 2;\n            if (newFramesCap < totalFramesRead + framesJustRead) {\n                newFramesCap = totalFramesRead + framesJustRead;\n            }\n            oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float);\n            newFramesBufferSize = newFramesCap   * pMP3->channels * sizeof(float);\n            if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) {\n                break;\n            }\n            pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);\n            if (pNewFrames == NULL) {\n                ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);\n                break;\n            }\n            pFrames = pNewFrames;\n            framesCapacity = newFramesCap;\n        }\n        MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));\n        totalFramesRead += framesJustRead;\n        if (framesJustRead != framesToReadRightNow) {\n            break;\n        }\n    }\n    if (pConfig != NULL) {\n        pConfig->channels   = pMP3->channels;\n        pConfig->sampleRate = pMP3->sampleRate;\n    }\n    ma_dr_mp3_uninit(pMP3);\n    if (pTotalFrameCount) {\n        *pTotalFrameCount = totalFramesRead;\n    }\n    return pFrames;\n}\nstatic ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount)\n{\n    ma_uint64 totalFramesRead = 0;\n    ma_uint64 framesCapacity = 0;\n    ma_int16* pFrames = NULL;\n    ma_int16 temp[4096];\n    MA_DR_MP3_ASSERT(pMP3 != NULL);\n    for (;;) {\n        ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels;\n        ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);\n        if (framesJustRead == 0) {\n            break;\n        }\n        if (framesCapacity < totalFramesRead + framesJustRead) {\n            ma_uint64 newFramesBufferSize;\n            ma_uint64 oldFramesBufferSize;\n            ma_uint64 newFramesCap;\n            ma_int16* pNewFrames;\n            newFramesCap = framesCapacity * 2;\n            if (newFramesCap < totalFramesRead + framesJustRead) {\n                newFramesCap = totalFramesRead + framesJustRead;\n            }\n            oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16);\n            newFramesBufferSize = newFramesCap   * pMP3->channels * sizeof(ma_int16);\n            if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) {\n                break;\n            }\n            pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);\n            if (pNewFrames == NULL) {\n                ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);\n                break;\n            }\n            pFrames = pNewFrames;\n            framesCapacity = newFramesCap;\n        }\n        MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16)));\n        totalFramesRead += framesJustRead;\n        if (framesJustRead != framesToReadRightNow) {\n            break;\n        }\n    }\n    if (pConfig != NULL) {\n        pConfig->channels   = pMP3->channels;\n        pConfig->sampleRate = pMP3->sampleRate;\n    }\n    ma_dr_mp3_uninit(pMP3);\n    if (pTotalFrameCount) {\n        *pTotalFrameCount = totalFramesRead;\n    }\n    return pFrames;\n}\nMA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_mp3 mp3;\n    if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);\n}\nMA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_mp3 mp3;\n    if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);\n}\nMA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_mp3 mp3;\n    if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);\n}\nMA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_mp3 mp3;\n    if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);\n}\n#ifndef MA_DR_MP3_NO_STDIO\nMA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_mp3 mp3;\n    if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);\n}\nMA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    ma_dr_mp3 mp3;\n    if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) {\n        return NULL;\n    }\n    return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);\n}\n#endif\nMA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks != NULL) {\n        return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks);\n    } else {\n        return ma_dr_mp3__malloc_default(sz, NULL);\n    }\n}\nMA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)\n{\n    if (pAllocationCallbacks != NULL) {\n        ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks);\n    } else {\n        ma_dr_mp3__free_default(p, NULL);\n    }\n}\n#endif\n/* dr_mp3_c end */\n#endif  /* MA_DR_MP3_IMPLEMENTATION */\n#endif  /* MA_NO_MP3 */\n\n\n/* End globally disabled warnings. */\n#if defined(_MSC_VER)\n    #pragma warning(pop)\n#endif\n\n#endif  /* miniaudio_c */\n#endif  /* MINIAUDIO_IMPLEMENTATION */\n\n\n/*\nThis software is available as a choice of the following licenses. Choose\nwhichever you prefer.\n\n===============================================================================\nALTERNATIVE 1 - Public Domain (www.unlicense.org)\n===============================================================================\nThis is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\n\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <http://unlicense.org/>\n\n===============================================================================\nALTERNATIVE 2 - MIT No Attribution\n===============================================================================\nCopyright 2023 David Reid\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n"
  },
  {
    "path": "lib/third_party/miniaudio/source/miniaudio.c",
    "content": "    #define MINIAUDIO_IMPLEMENTATION\n    #include \"miniaudio.h\""
  },
  {
    "path": "lib/third_party/nlohmann_json/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.1...3.14)\n\n##\n## PROJECT\n## name and version\n##\nproject(nlohmann_json VERSION 3.11.3 LANGUAGES CXX)\n\n##\n## MAIN_PROJECT CHECK\n## determine if nlohmann_json is built as a subproject (using add_subdirectory) or if it is the main project\n##\nset(MAIN_PROJECT OFF)\nif (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)\n    set(MAIN_PROJECT ON)\nendif()\n\n##\n## INCLUDE\n##\n##\nset(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})\ninclude(ExternalProject)\n\n##\n## OPTIONS\n##\n\nif (POLICY CMP0077)\n    # Allow CMake 3.13+ to override options when using FetchContent / add_subdirectory.\n    cmake_policy(SET CMP0077 NEW)\nendif ()\n\n# VERSION_GREATER_EQUAL is not available in CMake 3.1\nif(${MAIN_PROJECT} AND (${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION} VERSION_GREATER 3.13))\n    set(JSON_BuildTests_INIT ON)\nelse()\n    set(JSON_BuildTests_INIT OFF)\nendif()\noption(JSON_BuildTests                     \"Build the unit tests when BUILD_TESTING is enabled.\" ${JSON_BuildTests_INIT})\noption(JSON_CI                             \"Enable CI build targets.\" OFF)\noption(JSON_Diagnostics                    \"Use extended diagnostic messages.\" OFF)\noption(JSON_GlobalUDLs                     \"Place use-defined string literals in the global namespace.\" ON)\noption(JSON_ImplicitConversions            \"Enable implicit conversions.\" ON)\noption(JSON_DisableEnumSerialization       \"Disable default integer enum serialization.\" OFF)\noption(JSON_LegacyDiscardedValueComparison \"Enable legacy discarded value comparison.\" OFF)\noption(JSON_Install                        \"Install CMake targets during install step.\" ${MAIN_PROJECT})\noption(JSON_MultipleHeaders                \"Use non-amalgamated version of the library.\" ON)\noption(JSON_SystemInclude                  \"Include as system headers (skip for clang-tidy).\" OFF)\n\nif (JSON_CI)\n    include(ci)\nendif ()\n\n##\n## CONFIGURATION\n##\ninclude(GNUInstallDirs)\n\nset(NLOHMANN_JSON_TARGET_NAME               ${PROJECT_NAME})\nset(NLOHMANN_JSON_CONFIG_INSTALL_DIR        \"${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}\" CACHE INTERNAL \"\")\nset(NLOHMANN_JSON_INCLUDE_INSTALL_DIR       \"${CMAKE_INSTALL_INCLUDEDIR}\")\nset(NLOHMANN_JSON_TARGETS_EXPORT_NAME       \"${PROJECT_NAME}Targets\")\nset(NLOHMANN_JSON_CMAKE_CONFIG_TEMPLATE     \"cmake/config.cmake.in\")\nset(NLOHMANN_JSON_CMAKE_CONFIG_DIR          \"${CMAKE_CURRENT_BINARY_DIR}\")\nset(NLOHMANN_JSON_CMAKE_VERSION_CONFIG_FILE \"${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}ConfigVersion.cmake\")\nset(NLOHMANN_JSON_CMAKE_PROJECT_CONFIG_FILE \"${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}Config.cmake\")\nset(NLOHMANN_JSON_CMAKE_PROJECT_TARGETS_FILE \"${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}Targets.cmake\")\nset(NLOHMANN_JSON_PKGCONFIG_INSTALL_DIR     \"${CMAKE_INSTALL_DATADIR}/pkgconfig\")\n\nif (JSON_MultipleHeaders)\n    set(NLOHMANN_JSON_INCLUDE_BUILD_DIR \"${PROJECT_SOURCE_DIR}/include/\")\n    message(STATUS \"Using the multi-header code from ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}\")\nelse()\n    set(NLOHMANN_JSON_INCLUDE_BUILD_DIR \"${PROJECT_SOURCE_DIR}/single_include/\")\n    message(STATUS \"Using the single-header code from ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}\")\nendif()\n\nif (NOT JSON_ImplicitConversions)\n    message(STATUS \"Implicit conversions are disabled\")\nendif()\n\nif (JSON_DisableEnumSerialization)\n    message(STATUS \"Enum integer serialization is disabled\")\nendif()\n\nif (JSON_LegacyDiscardedValueComparison)\n    message(STATUS \"Legacy discarded value comparison enabled\")\nendif()\n\nif (JSON_Diagnostics)\n    message(STATUS \"Diagnostics enabled\")\nendif()\n\nif (JSON_SystemInclude)\n    set(NLOHMANN_JSON_SYSTEM_INCLUDE \"SYSTEM\")\nendif()\n\n##\n## TARGET\n## create target and add include path\n##\nadd_library(${NLOHMANN_JSON_TARGET_NAME} INTERFACE)\nadd_library(${PROJECT_NAME}::${NLOHMANN_JSON_TARGET_NAME} ALIAS ${NLOHMANN_JSON_TARGET_NAME})\nif (${CMAKE_VERSION} VERSION_LESS \"3.8.0\")\n    target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_range_for)\nelse()\n    target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_std_11)\nendif()\n\ntarget_compile_definitions(\n    ${NLOHMANN_JSON_TARGET_NAME}\n    INTERFACE\n    $<$<NOT:$<BOOL:${JSON_GlobalUDLs}>>:JSON_USE_GLOBAL_UDLS=0>\n    $<$<NOT:$<BOOL:${JSON_ImplicitConversions}>>:JSON_USE_IMPLICIT_CONVERSIONS=0>\n    $<$<BOOL:${JSON_DisableEnumSerialization}>:JSON_DISABLE_ENUM_SERIALIZATION=1>\n    $<$<BOOL:${JSON_Diagnostics}>:JSON_DIAGNOSTICS=1>\n    $<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>\n)\n\ntarget_include_directories(\n    ${NLOHMANN_JSON_TARGET_NAME}\n    ${NLOHMANN_JSON_SYSTEM_INCLUDE} INTERFACE\n    $<BUILD_INTERFACE:${NLOHMANN_JSON_INCLUDE_BUILD_DIR}>\n    $<INSTALL_INTERFACE:${NLOHMANN_JSON_INCLUDE_INSTALL_DIR}>\n)\n\n## add debug view definition file for msvc (natvis)\nif (MSVC)\n    set(NLOHMANN_ADD_NATVIS TRUE)\n    set(NLOHMANN_NATVIS_FILE \"nlohmann_json.natvis\")\n    target_sources(\n        ${NLOHMANN_JSON_TARGET_NAME}\n        INTERFACE\n            $<INSTALL_INTERFACE:${NLOHMANN_NATVIS_FILE}>\n            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${NLOHMANN_NATVIS_FILE}>\n    )\nendif()\n\n# Install a pkg-config file, so other tools can find this.\nCONFIGURE_FILE(\n    \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in\"\n    \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc\"\n)\n\n##\n## TESTS\n## create and configure the unit test target\n##\nif (JSON_BuildTests)\n    include(CTest)\n    enable_testing()\n    add_subdirectory(tests)\nendif()\n\n##\n## INSTALL\n## install header files, generate and install cmake config files for find_package()\n##\ninclude(CMakePackageConfigHelpers)\n# use a custom package version config file instead of\n# write_basic_package_version_file to ensure that it's architecture-independent\n# https://github.com/nlohmann/json/issues/1697\nconfigure_file(\n    \"cmake/nlohmann_jsonConfigVersion.cmake.in\"\n    ${NLOHMANN_JSON_CMAKE_VERSION_CONFIG_FILE}\n    @ONLY\n)\nconfigure_file(\n    ${NLOHMANN_JSON_CMAKE_CONFIG_TEMPLATE}\n    ${NLOHMANN_JSON_CMAKE_PROJECT_CONFIG_FILE}\n    @ONLY\n)\n\nif(JSON_Install)\n    install(\n        DIRECTORY ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}\n        DESTINATION ${NLOHMANN_JSON_INCLUDE_INSTALL_DIR}\n    )\n    install(\n        FILES ${NLOHMANN_JSON_CMAKE_PROJECT_CONFIG_FILE} ${NLOHMANN_JSON_CMAKE_VERSION_CONFIG_FILE}\n        DESTINATION ${NLOHMANN_JSON_CONFIG_INSTALL_DIR}\n    )\n    if (NLOHMANN_ADD_NATVIS)\n        install(\n            FILES ${NLOHMANN_NATVIS_FILE}\n            DESTINATION .\n    )\n    endif()\n    export(\n        TARGETS ${NLOHMANN_JSON_TARGET_NAME}\n        NAMESPACE ${PROJECT_NAME}::\n        FILE ${NLOHMANN_JSON_CMAKE_PROJECT_TARGETS_FILE}\n    )\n    install(\n        TARGETS ${NLOHMANN_JSON_TARGET_NAME}\n        EXPORT ${NLOHMANN_JSON_TARGETS_EXPORT_NAME}\n        INCLUDES DESTINATION ${NLOHMANN_JSON_INCLUDE_INSTALL_DIR}\n    )\n    install(\n        EXPORT ${NLOHMANN_JSON_TARGETS_EXPORT_NAME}\n        NAMESPACE ${PROJECT_NAME}::\n        DESTINATION ${NLOHMANN_JSON_CONFIG_INSTALL_DIR}\n    )\n    install(\n        FILES \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc\"\n        DESTINATION ${NLOHMANN_JSON_PKGCONFIG_INSTALL_DIR}\n    )\nendif()\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/LICENSE.MIT",
    "content": "MIT License \n\nCopyright (c) 2013-2022 Niels Lohmann\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/cmake/config.cmake.in",
    "content": "include(FindPackageHandleStandardArgs)\nset(${CMAKE_FIND_PACKAGE_NAME}_CONFIG ${CMAKE_CURRENT_LIST_FILE})\nfind_package_handle_standard_args(@PROJECT_NAME@ CONFIG_MODE)\n\nif(NOT TARGET @PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@)\n    include(\"${CMAKE_CURRENT_LIST_DIR}/@NLOHMANN_JSON_TARGETS_EXPORT_NAME@.cmake\")\n    if((NOT TARGET @NLOHMANN_JSON_TARGET_NAME@) AND\n       (NOT @PROJECT_NAME@_FIND_VERSION OR\n        @PROJECT_NAME@_FIND_VERSION VERSION_LESS 3.2.0))\n        add_library(@NLOHMANN_JSON_TARGET_NAME@ INTERFACE IMPORTED)\n        set_target_properties(@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES\n            INTERFACE_LINK_LIBRARIES @PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@\n        )\n    endif()\nendif()\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/cmake/nlohmann_jsonConfigVersion.cmake.in",
    "content": "# This is essentially cmake's BasicConfigVersion-SameMajorVersion.cmake.in but\n# without the 32/64-bit check.  Since json is a header-only library, it doesn't\n# matter if it was built on a different platform than what it is used on (see\n# https://github.com/nlohmann/json/issues/1697).\nset(PACKAGE_VERSION \"@PROJECT_VERSION@\")\n\nif(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)\n  set(PACKAGE_VERSION_COMPATIBLE FALSE)\nelse()\n\n  if(PACKAGE_FIND_VERSION_MAJOR STREQUAL \"@PROJECT_VERSION_MAJOR@\")\n    set(PACKAGE_VERSION_COMPATIBLE TRUE)\n  else()\n    set(PACKAGE_VERSION_COMPATIBLE FALSE)\n  endif()\n\n  if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)\n      set(PACKAGE_VERSION_EXACT TRUE)\n  endif()\nendif()\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/cmake/pkg-config.pc.in",
    "content": "Name: ${PROJECT_NAME}\nDescription: JSON for Modern C++\nVersion: ${PROJECT_VERSION}\nCflags: -I${CMAKE_INSTALL_FULL_INCLUDEDIR}\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/adl_serializer.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <utility>\n\n#include <nlohmann/detail/abi_macros.hpp>\n#include <nlohmann/detail/conversions/from_json.hpp>\n#include <nlohmann/detail/conversions/to_json.hpp>\n#include <nlohmann/detail/meta/identity_tag.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/// @sa https://json.nlohmann.me/api/adl_serializer/\ntemplate<typename ValueType, typename>\nstruct adl_serializer\n{\n    /// @brief convert a JSON value to any value type\n    /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto from_json(BasicJsonType && j, TargetType& val) noexcept(\n        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())\n    {\n        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);\n    }\n\n    /// @brief convert a JSON value to any value type\n    /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto from_json(BasicJsonType && j) noexcept(\n    noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))\n    {\n        return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});\n    }\n\n    /// @brief convert any value type to a JSON value\n    /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto to_json(BasicJsonType& j, TargetType && val) noexcept(\n        noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))\n    -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())\n    {\n        ::nlohmann::to_json(j, std::forward<TargetType>(val));\n    }\n};\n\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/byte_container_with_subtype.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstdint> // uint8_t, uint64_t\n#include <tuple> // tie\n#include <utility> // move\n\n#include <nlohmann/detail/abi_macros.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/// @brief an internal type for a backed binary type\n/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/\ntemplate<typename BinaryType>\nclass byte_container_with_subtype : public BinaryType\n{\n  public:\n    using container_type = BinaryType;\n    using subtype_type = std::uint64_t;\n\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/\n    byte_container_with_subtype() noexcept(noexcept(container_type()))\n        : container_type()\n    {}\n\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/\n    byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n    {}\n\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/\n    byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n    {}\n\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/\n    byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n        , m_subtype(subtype_)\n        , m_has_subtype(true)\n    {}\n\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/\n    byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n        , m_subtype(subtype_)\n        , m_has_subtype(true)\n    {}\n\n    bool operator==(const byte_container_with_subtype& rhs) const\n    {\n        return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==\n               std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);\n    }\n\n    bool operator!=(const byte_container_with_subtype& rhs) const\n    {\n        return !(rhs == *this);\n    }\n\n    /// @brief sets the binary subtype\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/\n    void set_subtype(subtype_type subtype_) noexcept\n    {\n        m_subtype = subtype_;\n        m_has_subtype = true;\n    }\n\n    /// @brief return the binary subtype\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/\n    constexpr subtype_type subtype() const noexcept\n    {\n        return m_has_subtype ? m_subtype : static_cast<subtype_type>(-1);\n    }\n\n    /// @brief return whether the value has a subtype\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/\n    constexpr bool has_subtype() const noexcept\n    {\n        return m_has_subtype;\n    }\n\n    /// @brief clears the binary subtype\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/\n    void clear_subtype() noexcept\n    {\n        m_subtype = 0;\n        m_has_subtype = false;\n    }\n\n  private:\n    subtype_type m_subtype = 0;\n    bool m_has_subtype = false;\n};\n\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/abi_macros.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n// This file contains all macro definitions affecting or depending on the ABI\n\n#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK\n    #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)\n        #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0\n            #warning \"Already included a different version of the library!\"\n        #endif\n    #endif\n#endif\n\n#define NLOHMANN_JSON_VERSION_MAJOR 3   // NOLINT(modernize-macro-to-enum)\n#define NLOHMANN_JSON_VERSION_MINOR 12  // NOLINT(modernize-macro-to-enum)\n#define NLOHMANN_JSON_VERSION_PATCH 0   // NOLINT(modernize-macro-to-enum)\n\n#ifndef JSON_DIAGNOSTICS\n    #define JSON_DIAGNOSTICS 0\n#endif\n\n#ifndef JSON_DIAGNOSTIC_POSITIONS\n    #define JSON_DIAGNOSTIC_POSITIONS 0\n#endif\n\n#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON\n    #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0\n#endif\n\n#if JSON_DIAGNOSTICS\n    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag\n#else\n    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS\n#endif\n\n#if JSON_DIAGNOSTIC_POSITIONS\n    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp\n#else\n    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS\n#endif\n\n#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON\n    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp\n#else\n    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON\n#endif\n\n#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION\n    #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0\n#endif\n\n// Construct the namespace ABI tags component\n#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c\n#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \\\n    NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)\n\n#define NLOHMANN_JSON_ABI_TAGS                                       \\\n    NLOHMANN_JSON_ABI_TAGS_CONCAT(                                   \\\n            NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS,                       \\\n            NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \\\n            NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS)\n\n// Construct the namespace version component\n#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \\\n    _v ## major ## _ ## minor ## _ ## patch\n#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \\\n    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)\n\n#if NLOHMANN_JSON_NAMESPACE_NO_VERSION\n#define NLOHMANN_JSON_NAMESPACE_VERSION\n#else\n#define NLOHMANN_JSON_NAMESPACE_VERSION                                 \\\n    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \\\n                                           NLOHMANN_JSON_VERSION_MINOR, \\\n                                           NLOHMANN_JSON_VERSION_PATCH)\n#endif\n\n// Combine namespace components\n#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b\n#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \\\n    NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)\n\n#ifndef NLOHMANN_JSON_NAMESPACE\n#define NLOHMANN_JSON_NAMESPACE               \\\n    nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \\\n            NLOHMANN_JSON_ABI_TAGS,           \\\n            NLOHMANN_JSON_NAMESPACE_VERSION)\n#endif\n\n#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN\n#define NLOHMANN_JSON_NAMESPACE_BEGIN                \\\n    namespace nlohmann                               \\\n    {                                                \\\n    inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \\\n                NLOHMANN_JSON_ABI_TAGS,              \\\n                NLOHMANN_JSON_NAMESPACE_VERSION)     \\\n    {\n#endif\n\n#ifndef NLOHMANN_JSON_NAMESPACE_END\n#define NLOHMANN_JSON_NAMESPACE_END                                     \\\n    }  /* namespace (inline namespace) NOLINT(readability/namespace) */ \\\n    }  // namespace nlohmann\n#endif\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/conversions/from_json.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <algorithm> // transform\n#include <array> // array\n#include <forward_list> // forward_list\n#include <iterator> // inserter, front_inserter, end\n#include <map> // map\n#include <string> // string\n#include <tuple> // tuple, make_tuple\n#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible\n#include <unordered_map> // unordered_map\n#include <utility> // pair, declval\n#include <valarray> // valarray\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/meta/identity_tag.hpp>\n#include <nlohmann/detail/meta/std_fs.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/string_concat.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\n// include after macro_scope.hpp\n#ifdef JSON_HAS_CPP_17\n    #include <optional> // optional\n#endif\n\n#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM\n    #include <string_view> // u8string_view\n#endif\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_null()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be null, but is \", j.type_name()), &j));\n    }\n    n = nullptr;\n}\n\n#ifdef JSON_HAS_CPP_17\ntemplate<typename BasicJsonType, typename T>\nvoid from_json(const BasicJsonType& j, std::optional<T>& opt)\n{\n    if (j.is_null())\n    {\n        opt = std::nullopt;\n    }\n    else\n    {\n        opt.emplace(j.template get<T>());\n    }\n}\n#endif // JSON_HAS_CPP_17\n\n// overloads for basic_json template parameters\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t < std::is_arithmetic<ArithmeticType>::value&&\n                         !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n                         int > = 0 >\nvoid get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n\n        case value_t::null:\n        case value_t::object:\n        case value_t::array:\n        case value_t::string:\n        case value_t::boolean:\n        case value_t::binary:\n        case value_t::discarded:\n        default:\n            JSON_THROW(type_error::create(302, concat(\"type must be number, but is \", j.type_name()), &j));\n    }\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be boolean, but is \", j.type_name()), &j));\n    }\n    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be string, but is \", j.type_name()), &j));\n    }\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate <\n    typename BasicJsonType, typename StringType,\n    enable_if_t <\n        std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value\n        && is_detected_exact<typename BasicJsonType::string_t::value_type, value_type_t, StringType>::value\n        && !std::is_same<typename BasicJsonType::string_t, StringType>::value\n        && !is_json_ref<StringType>::value, int > = 0 >\ninline void from_json(const BasicJsonType& j, StringType& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be string, but is \", j.type_name()), &j));\n    }\n\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\n#if !JSON_DISABLE_ENUM_SERIALIZATION\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\ninline void from_json(const BasicJsonType& j, EnumType& e)\n{\n    typename std::underlying_type<EnumType>::type val;\n    get_arithmetic_value(j, val);\n    e = static_cast<EnumType>(val);\n}\n#endif  // JSON_DISABLE_ENUM_SERIALIZATION\n\n// forward_list doesn't have an insert method\ntemplate<typename BasicJsonType, typename T, typename Allocator,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\ninline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n    l.clear();\n    std::transform(j.rbegin(), j.rend(),\n                   std::front_inserter(l), [](const BasicJsonType & i)\n    {\n        return i.template get<T>();\n    });\n}\n\n// valarray doesn't have an insert method\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\ninline void from_json(const BasicJsonType& j, std::valarray<T>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n    l.resize(j.size());\n    std::transform(j.begin(), j.end(), std::begin(l),\n                   [](const BasicJsonType & elem)\n    {\n        return elem.template get<T>();\n    });\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json(const BasicJsonType& j, T (&arr)[N])  // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2>\nauto from_json(const BasicJsonType& j, T (&arr)[N1][N2])  // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i1 = 0; i1 < N1; ++i1)\n    {\n        for (std::size_t i2 = 0; i2 < N2; ++i2)\n        {\n            arr[i1][i2] = j.at(i1).at(i2).template get<T>();\n        }\n    }\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2, std::size_t N3>\nauto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3])  // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i1 = 0; i1 < N1; ++i1)\n    {\n        for (std::size_t i2 = 0; i2 < N2; ++i2)\n        {\n            for (std::size_t i3 = 0; i3 < N3; ++i3)\n            {\n                arr[i1][i2][i3] = j.at(i1).at(i2).at(i3).template get<T>();\n            }\n        }\n    }\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2, std::size_t N3, std::size_t N4>\nauto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3][N4])  // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i1 = 0; i1 < N1; ++i1)\n    {\n        for (std::size_t i2 = 0; i2 < N2; ++i2)\n        {\n            for (std::size_t i3 = 0; i3 < N3; ++i3)\n            {\n                for (std::size_t i4 = 0; i4 < N4; ++i4)\n                {\n                    arr[i1][i2][i3][i4] = j.at(i1).at(i2).at(i3).at(i4).template get<T>();\n                }\n            }\n        }\n    }\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)\n{\n    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,\n                          priority_tag<2> /*unused*/)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType,\n         enable_if_t<\n             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,\n             int> = 0>\nauto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)\n-> decltype(\n    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),\n    j.template get<typename ConstructibleArrayType::value_type>(),\n    void())\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    ret.reserve(j.size());\n    std::transform(j.begin(), j.end(),\n                   std::inserter(ret, end(ret)), [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType,\n         enable_if_t<\n             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,\n             int> = 0>\ninline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,\n                                 priority_tag<0> /*unused*/)\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    std::transform(\n        j.begin(), j.end(), std::inserter(ret, end(ret)),\n        [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate < typename BasicJsonType, typename ConstructibleArrayType,\n           enable_if_t <\n               is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&\n               !is_basic_json<ConstructibleArrayType>::value,\n               int > = 0 >\nauto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)\n-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),\nj.template get<typename ConstructibleArrayType::value_type>(),\nvoid())\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n\n    from_json_array_impl(j, arr, priority_tag<3> {});\n}\n\ntemplate < typename BasicJsonType, typename T, std::size_t... Idx >\nstd::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,\n                     identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)\n{\n    return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };\n}\n\ntemplate < typename BasicJsonType, typename T, std::size_t N >\nauto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)\n-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n\n    return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be binary, but is \", j.type_name()), &j));\n    }\n\n    bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>\ninline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_object()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be object, but is \", j.type_name()), &j));\n    }\n\n    ConstructibleObjectType ret;\n    const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();\n    using value_type = typename ConstructibleObjectType::value_type;\n    std::transform(\n        inner_object->begin(), inner_object->end(),\n        std::inserter(ret, ret.begin()),\n        [](typename BasicJsonType::object_t::value_type const & p)\n    {\n        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());\n    });\n    obj = std::move(ret);\n}\n\n// overload for arithmetic types, not chosen for basic_json template arguments\n// (BooleanType, etc.); note: Is it really necessary to provide explicit\n// overloads for boolean_t etc. in case of a custom BooleanType which is not\n// an arithmetic type?\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t <\n               std::is_arithmetic<ArithmeticType>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n               int > = 0 >\ninline void from_json(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n        case value_t::boolean:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());\n            break;\n        }\n\n        case value_t::null:\n        case value_t::object:\n        case value_t::array:\n        case value_t::string:\n        case value_t::binary:\n        case value_t::discarded:\n        default:\n            JSON_THROW(type_error::create(302, concat(\"type must be number, but is \", j.type_name()), &j));\n    }\n}\n\ntemplate<typename BasicJsonType, typename... Args, std::size_t... Idx>\nstd::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)\n{\n    return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);\n}\n\ntemplate<typename BasicJsonType>\nstd::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/)\n{\n    return {};\n}\n\ntemplate < typename BasicJsonType, class A1, class A2 >\nstd::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)\n{\n    return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),\n            std::forward<BasicJsonType>(j).at(1).template get<A2>()};\n}\n\ntemplate<typename BasicJsonType, typename A1, typename A2>\ninline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)\n{\n    p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nstd::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)\n{\n    return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});\n}\n\ntemplate<typename BasicJsonType, typename... Args>\ninline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)\n{\n    t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});\n}\n\ntemplate<typename BasicJsonType, typename TupleRelated>\nauto from_json(BasicJsonType&& j, TupleRelated&& t)\n-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n\n    return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\ninline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", p.type_name()), &j));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\ninline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", p.type_name()), &j));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\n#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, std_fs::path& p)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be string, but is \", j.type_name()), &j));\n    }\n    const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n    // Checking for C++20 standard or later can be insufficient in case the\n    // library support for char8_t is either incomplete or was disabled\n    // altogether. Use the __cpp_lib_char8_t feature test instead.\n#if defined(__cpp_lib_char8_t) && (__cpp_lib_char8_t >= 201907L)\n    p = std_fs::path(std::u8string_view(reinterpret_cast<const char8_t*>(s.data()), s.size()));\n#else\n    p = std_fs::u8path(s); // accepts UTF-8 encoded std::string in C++17, deprecated in C++20\n#endif\n}\n#endif\n\nstruct from_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(const BasicJsonType& j, T&& val) const\n    noexcept(noexcept(from_json(j, std::forward<T>(val))))\n    -> decltype(from_json(j, std::forward<T>(val)))\n    {\n        return from_json(j, std::forward<T>(val));\n    }\n};\n\n}  // namespace detail\n\n#ifndef JSON_HAS_CPP_17\n/// namespace to hold default `from_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)\n{\n#endif\nJSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)\n    detail::static_const<detail::from_json_fn>::value;\n#ifndef JSON_HAS_CPP_17\n}  // namespace\n#endif\n\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/conversions/to_chars.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2009 Florian Loitsch <https://florian.loitsch.com/>\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <array> // array\n#include <cmath>   // signbit, isfinite\n#include <cstdint> // intN_t, uintN_t\n#include <cstring> // memcpy, memmove\n#include <limits> // numeric_limits\n#include <type_traits> // conditional\n\n#include <nlohmann/detail/macro_scope.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/*!\n@brief implements the Grisu2 algorithm for binary to decimal floating-point\nconversion.\n\nThis implementation is a slightly modified version of the reference\nimplementation which may be obtained from\nhttp://florian.loitsch.com/publications (bench.tar.gz).\n\nThe code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.\n\nFor a detailed description of the algorithm see:\n\n[1] Loitsch, \"Printing Floating-Point Numbers Quickly and Accurately with\n    Integers\", Proceedings of the ACM SIGPLAN 2010 Conference on Programming\n    Language Design and Implementation, PLDI 2010\n[2] Burger, Dybvig, \"Printing Floating-Point Numbers Quickly and Accurately\",\n    Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language\n    Design and Implementation, PLDI 1996\n*/\nnamespace dtoa_impl\n{\n\ntemplate<typename Target, typename Source>\nTarget reinterpret_bits(const Source source)\n{\n    static_assert(sizeof(Target) == sizeof(Source), \"size mismatch\");\n\n    Target target;\n    std::memcpy(&target, &source, sizeof(Source));\n    return target;\n}\n\nstruct diyfp // f * 2^e\n{\n    static constexpr int kPrecision = 64; // = q\n\n    std::uint64_t f = 0;\n    int e = 0;\n\n    constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}\n\n    /*!\n    @brief returns x - y\n    @pre x.e == y.e and x.f >= y.f\n    */\n    static diyfp sub(const diyfp& x, const diyfp& y) noexcept\n    {\n        JSON_ASSERT(x.e == y.e);\n        JSON_ASSERT(x.f >= y.f);\n\n        return {x.f - y.f, x.e};\n    }\n\n    /*!\n    @brief returns x * y\n    @note The result is rounded. (Only the upper q bits are returned.)\n    */\n    static diyfp mul(const diyfp& x, const diyfp& y) noexcept\n    {\n        static_assert(kPrecision == 64, \"internal error\");\n\n        // Computes:\n        //  f = round((x.f * y.f) / 2^q)\n        //  e = x.e + y.e + q\n\n        // Emulate the 64-bit * 64-bit multiplication:\n        //\n        // p = u * v\n        //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)\n        //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo         )) + 2^64 (u_hi v_hi         )\n        //   = (p0                ) + 2^32 ((p1                ) + (p2                )) + 2^64 (p3                )\n        //   = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                )\n        //   = (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo                      ) + 2^64 (p1_hi + p2_hi + p3)\n        //   = (p0_lo             ) + 2^32 (Q                                          ) + 2^64 (H                 )\n        //   = (p0_lo             ) + 2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H                 )\n        //\n        // (Since Q might be larger than 2^32 - 1)\n        //\n        //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)\n        //\n        // (Q_hi + H does not overflow a 64-bit int)\n        //\n        //   = p_lo + 2^64 p_hi\n\n        const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;\n        const std::uint64_t u_hi = x.f >> 32u;\n        const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;\n        const std::uint64_t v_hi = y.f >> 32u;\n\n        const std::uint64_t p0 = u_lo * v_lo;\n        const std::uint64_t p1 = u_lo * v_hi;\n        const std::uint64_t p2 = u_hi * v_lo;\n        const std::uint64_t p3 = u_hi * v_hi;\n\n        const std::uint64_t p0_hi = p0 >> 32u;\n        const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;\n        const std::uint64_t p1_hi = p1 >> 32u;\n        const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;\n        const std::uint64_t p2_hi = p2 >> 32u;\n\n        std::uint64_t Q = p0_hi + p1_lo + p2_lo;\n\n        // The full product might now be computed as\n        //\n        // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)\n        // p_lo = p0_lo + (Q << 32)\n        //\n        // But in this particular case here, the full p_lo is not required.\n        // Effectively, we only need to add the highest bit in p_lo to p_hi (and\n        // Q_hi + 1 does not overflow).\n\n        Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up\n\n        const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);\n\n        return {h, x.e + y.e + 64};\n    }\n\n    /*!\n    @brief normalize x such that the significand is >= 2^(q-1)\n    @pre x.f != 0\n    */\n    static diyfp normalize(diyfp x) noexcept\n    {\n        JSON_ASSERT(x.f != 0);\n\n        while ((x.f >> 63u) == 0)\n        {\n            x.f <<= 1u;\n            x.e--;\n        }\n\n        return x;\n    }\n\n    /*!\n    @brief normalize x such that the result has the exponent E\n    @pre e >= x.e and the upper e - x.e bits of x.f must be zero.\n    */\n    static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept\n    {\n        const int delta = x.e - target_exponent;\n\n        JSON_ASSERT(delta >= 0);\n        JSON_ASSERT(((x.f << delta) >> delta) == x.f);\n\n        return {x.f << delta, target_exponent};\n    }\n};\n\nstruct boundaries\n{\n    diyfp w;\n    diyfp minus;\n    diyfp plus;\n};\n\n/*!\nCompute the (normalized) diyfp representing the input number 'value' and its\nboundaries.\n\n@pre value must be finite and positive\n*/\ntemplate<typename FloatType>\nboundaries compute_boundaries(FloatType value)\n{\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // Convert the IEEE representation into a diyfp.\n    //\n    // If v is denormal:\n    //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))\n    // If v is normalized:\n    //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))\n\n    static_assert(std::numeric_limits<FloatType>::is_iec559,\n                  \"internal error: dtoa_short requires an IEEE-754 floating-point implementation\");\n\n    constexpr int      kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)\n    constexpr int      kBias      = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);\n    constexpr int      kMinExp    = 1 - kBias;\n    constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)\n\n    using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;\n\n    const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value));\n    const std::uint64_t E = bits >> (kPrecision - 1);\n    const std::uint64_t F = bits & (kHiddenBit - 1);\n\n    const bool is_denormal = E == 0;\n    const diyfp v = is_denormal\n                    ? diyfp(F, kMinExp)\n                    : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);\n\n    // Compute the boundaries m- and m+ of the floating-point value\n    // v = f * 2^e.\n    //\n    // Determine v- and v+, the floating-point predecessor and successor of v,\n    // respectively.\n    //\n    //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)\n    //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)\n    //\n    //      v+ = v + 2^e\n    //\n    // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_\n    // between m- and m+ round to v, regardless of how the input rounding\n    // algorithm breaks ties.\n    //\n    //      ---+-------------+-------------+-------------+-------------+---  (A)\n    //         v-            m-            v             m+            v+\n    //\n    //      -----------------+------+------+-------------+-------------+---  (B)\n    //                       v-     m-     v             m+            v+\n\n    const bool lower_boundary_is_closer = F == 0 && E > 1;\n    const diyfp m_plus = diyfp((2 * v.f) + 1, v.e - 1);\n    const diyfp m_minus = lower_boundary_is_closer\n                          ? diyfp((4 * v.f) - 1, v.e - 2)  // (B)\n                          : diyfp((2 * v.f) - 1, v.e - 1); // (A)\n\n    // Determine the normalized w+ = m+.\n    const diyfp w_plus = diyfp::normalize(m_plus);\n\n    // Determine w- = m- such that e_(w-) = e_(w+).\n    const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);\n\n    return {diyfp::normalize(v), w_minus, w_plus};\n}\n\n// Given normalized diyfp w, Grisu needs to find a (normalized) cached\n// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies\n// within a certain range [alpha, gamma] (Definition 3.2 from [1])\n//\n//      alpha <= e = e_c + e_w + q <= gamma\n//\n// or\n//\n//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q\n//                          <= f_c * f_w * 2^gamma\n//\n// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies\n//\n//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma\n//\n// or\n//\n//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)\n//\n// The choice of (alpha,gamma) determines the size of the table and the form of\n// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well\n// in practice:\n//\n// The idea is to cut the number c * w = f * 2^e into two parts, which can be\n// processed independently: An integral part p1, and a fractional part p2:\n//\n//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e\n//              = (f div 2^-e) + (f mod 2^-e) * 2^e\n//              = p1 + p2 * 2^e\n//\n// The conversion of p1 into decimal form requires a series of divisions and\n// modulos by (a power of) 10. These operations are faster for 32-bit than for\n// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be\n// achieved by choosing\n//\n//      -e >= 32   or   e <= -32 := gamma\n//\n// In order to convert the fractional part\n//\n//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...\n//\n// into decimal form, the fraction is repeatedly multiplied by 10 and the digits\n// d[-i] are extracted in order:\n//\n//      (10 * p2) div 2^-e = d[-1]\n//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...\n//\n// The multiplication by 10 must not overflow. It is sufficient to choose\n//\n//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.\n//\n// Since p2 = f mod 2^-e < 2^-e,\n//\n//      -e <= 60   or   e >= -60 := alpha\n\nconstexpr int kAlpha = -60;\nconstexpr int kGamma = -32;\n\nstruct cached_power // c = f * 2^e ~= 10^k\n{\n    std::uint64_t f;\n    int e;\n    int k;\n};\n\n/*!\nFor a normalized diyfp w = f * 2^e, this function returns a (normalized) cached\npower-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c\nsatisfies (Definition 3.2 from [1])\n\n     alpha <= e_c + e + q <= gamma.\n*/\ninline cached_power get_cached_power_for_binary_exponent(int e)\n{\n    // Now\n    //\n    //      alpha <= e_c + e + q <= gamma                                    (1)\n    //      ==> f_c * 2^alpha <= c * 2^e * 2^q\n    //\n    // and since the c's are normalized, 2^(q-1) <= f_c,\n    //\n    //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)\n    //      ==> 2^(alpha - e - 1) <= c\n    //\n    // If c were an exact power of ten, i.e. c = 10^k, one may determine k as\n    //\n    //      k = ceil( log_10( 2^(alpha - e - 1) ) )\n    //        = ceil( (alpha - e - 1) * log_10(2) )\n    //\n    // From the paper:\n    // \"In theory the result of the procedure could be wrong since c is rounded,\n    //  and the computation itself is approximated [...]. In practice, however,\n    //  this simple function is sufficient.\"\n    //\n    // For IEEE double precision floating-point numbers converted into\n    // normalized diyfp's w = f * 2^e, with q = 64,\n    //\n    //      e >= -1022      (min IEEE exponent)\n    //           -52        (p - 1)\n    //           -52        (p - 1, possibly normalize denormal IEEE numbers)\n    //           -11        (normalize the diyfp)\n    //         = -1137\n    //\n    // and\n    //\n    //      e <= +1023      (max IEEE exponent)\n    //           -52        (p - 1)\n    //           -11        (normalize the diyfp)\n    //         = 960\n    //\n    // This binary exponent range [-1137,960] results in a decimal exponent\n    // range [-307,324]. One does not need to store a cached power for each\n    // k in this range. For each such k it suffices to find a cached power\n    // such that the exponent of the product lies in [alpha,gamma].\n    // This implies that the difference of the decimal exponents of adjacent\n    // table entries must be less than or equal to\n    //\n    //      floor( (gamma - alpha) * log_10(2) ) = 8.\n    //\n    // (A smaller distance gamma-alpha would require a larger table.)\n\n    // NB:\n    // Actually, this function returns c, such that -60 <= e_c + e + 64 <= -34.\n\n    constexpr int kCachedPowersMinDecExp = -300;\n    constexpr int kCachedPowersDecStep = 8;\n\n    static constexpr std::array<cached_power, 79> kCachedPowers =\n    {\n        {\n            { 0xAB70FE17C79AC6CA, -1060, -300 },\n            { 0xFF77B1FCBEBCDC4F, -1034, -292 },\n            { 0xBE5691EF416BD60C, -1007, -284 },\n            { 0x8DD01FAD907FFC3C,  -980, -276 },\n            { 0xD3515C2831559A83,  -954, -268 },\n            { 0x9D71AC8FADA6C9B5,  -927, -260 },\n            { 0xEA9C227723EE8BCB,  -901, -252 },\n            { 0xAECC49914078536D,  -874, -244 },\n            { 0x823C12795DB6CE57,  -847, -236 },\n            { 0xC21094364DFB5637,  -821, -228 },\n            { 0x9096EA6F3848984F,  -794, -220 },\n            { 0xD77485CB25823AC7,  -768, -212 },\n            { 0xA086CFCD97BF97F4,  -741, -204 },\n            { 0xEF340A98172AACE5,  -715, -196 },\n            { 0xB23867FB2A35B28E,  -688, -188 },\n            { 0x84C8D4DFD2C63F3B,  -661, -180 },\n            { 0xC5DD44271AD3CDBA,  -635, -172 },\n            { 0x936B9FCEBB25C996,  -608, -164 },\n            { 0xDBAC6C247D62A584,  -582, -156 },\n            { 0xA3AB66580D5FDAF6,  -555, -148 },\n            { 0xF3E2F893DEC3F126,  -529, -140 },\n            { 0xB5B5ADA8AAFF80B8,  -502, -132 },\n            { 0x87625F056C7C4A8B,  -475, -124 },\n            { 0xC9BCFF6034C13053,  -449, -116 },\n            { 0x964E858C91BA2655,  -422, -108 },\n            { 0xDFF9772470297EBD,  -396, -100 },\n            { 0xA6DFBD9FB8E5B88F,  -369,  -92 },\n            { 0xF8A95FCF88747D94,  -343,  -84 },\n            { 0xB94470938FA89BCF,  -316,  -76 },\n            { 0x8A08F0F8BF0F156B,  -289,  -68 },\n            { 0xCDB02555653131B6,  -263,  -60 },\n            { 0x993FE2C6D07B7FAC,  -236,  -52 },\n            { 0xE45C10C42A2B3B06,  -210,  -44 },\n            { 0xAA242499697392D3,  -183,  -36 },\n            { 0xFD87B5F28300CA0E,  -157,  -28 },\n            { 0xBCE5086492111AEB,  -130,  -20 },\n            { 0x8CBCCC096F5088CC,  -103,  -12 },\n            { 0xD1B71758E219652C,   -77,   -4 },\n            { 0x9C40000000000000,   -50,    4 },\n            { 0xE8D4A51000000000,   -24,   12 },\n            { 0xAD78EBC5AC620000,     3,   20 },\n            { 0x813F3978F8940984,    30,   28 },\n            { 0xC097CE7BC90715B3,    56,   36 },\n            { 0x8F7E32CE7BEA5C70,    83,   44 },\n            { 0xD5D238A4ABE98068,   109,   52 },\n            { 0x9F4F2726179A2245,   136,   60 },\n            { 0xED63A231D4C4FB27,   162,   68 },\n            { 0xB0DE65388CC8ADA8,   189,   76 },\n            { 0x83C7088E1AAB65DB,   216,   84 },\n            { 0xC45D1DF942711D9A,   242,   92 },\n            { 0x924D692CA61BE758,   269,  100 },\n            { 0xDA01EE641A708DEA,   295,  108 },\n            { 0xA26DA3999AEF774A,   322,  116 },\n            { 0xF209787BB47D6B85,   348,  124 },\n            { 0xB454E4A179DD1877,   375,  132 },\n            { 0x865B86925B9BC5C2,   402,  140 },\n            { 0xC83553C5C8965D3D,   428,  148 },\n            { 0x952AB45CFA97A0B3,   455,  156 },\n            { 0xDE469FBD99A05FE3,   481,  164 },\n            { 0xA59BC234DB398C25,   508,  172 },\n            { 0xF6C69A72A3989F5C,   534,  180 },\n            { 0xB7DCBF5354E9BECE,   561,  188 },\n            { 0x88FCF317F22241E2,   588,  196 },\n            { 0xCC20CE9BD35C78A5,   614,  204 },\n            { 0x98165AF37B2153DF,   641,  212 },\n            { 0xE2A0B5DC971F303A,   667,  220 },\n            { 0xA8D9D1535CE3B396,   694,  228 },\n            { 0xFB9B7CD9A4A7443C,   720,  236 },\n            { 0xBB764C4CA7A44410,   747,  244 },\n            { 0x8BAB8EEFB6409C1A,   774,  252 },\n            { 0xD01FEF10A657842C,   800,  260 },\n            { 0x9B10A4E5E9913129,   827,  268 },\n            { 0xE7109BFBA19C0C9D,   853,  276 },\n            { 0xAC2820D9623BF429,   880,  284 },\n            { 0x80444B5E7AA7CF85,   907,  292 },\n            { 0xBF21E44003ACDD2D,   933,  300 },\n            { 0x8E679C2F5E44FF8F,   960,  308 },\n            { 0xD433179D9C8CB841,   986,  316 },\n            { 0x9E19DB92B4E31BA9,  1013,  324 },\n        }\n    };\n\n    // This computation gives exactly the same results for k as\n    //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)\n    // for |e| <= 1500, but doesn't require floating-point operations.\n    // NB: log_10(2) ~= 78913 / 2^18\n    JSON_ASSERT(e >= -1500);\n    JSON_ASSERT(e <=  1500);\n    const int f = kAlpha - e - 1;\n    const int k = ((f * 78913) / (1 << 18)) + static_cast<int>(f > 0);\n\n    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;\n    JSON_ASSERT(index >= 0);\n    JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());\n\n    const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];\n    JSON_ASSERT(kAlpha <= cached.e + e + 64);\n    JSON_ASSERT(kGamma >= cached.e + e + 64);\n\n    return cached;\n}\n\n/*!\nFor n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.\nFor n == 0, returns 1 and sets pow10 := 1.\n*/\ninline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)\n{\n    // LCOV_EXCL_START\n    if (n >= 1000000000)\n    {\n        pow10 = 1000000000;\n        return 10;\n    }\n    // LCOV_EXCL_STOP\n    if (n >= 100000000)\n    {\n        pow10 = 100000000;\n        return  9;\n    }\n    if (n >= 10000000)\n    {\n        pow10 = 10000000;\n        return  8;\n    }\n    if (n >= 1000000)\n    {\n        pow10 = 1000000;\n        return  7;\n    }\n    if (n >= 100000)\n    {\n        pow10 = 100000;\n        return  6;\n    }\n    if (n >= 10000)\n    {\n        pow10 = 10000;\n        return  5;\n    }\n    if (n >= 1000)\n    {\n        pow10 = 1000;\n        return  4;\n    }\n    if (n >= 100)\n    {\n        pow10 = 100;\n        return  3;\n    }\n    if (n >= 10)\n    {\n        pow10 = 10;\n        return  2;\n    }\n\n    pow10 = 1;\n    return 1;\n}\n\ninline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,\n                         std::uint64_t rest, std::uint64_t ten_k)\n{\n    JSON_ASSERT(len >= 1);\n    JSON_ASSERT(dist <= delta);\n    JSON_ASSERT(rest <= delta);\n    JSON_ASSERT(ten_k > 0);\n\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    //                                  ten_k\n    //                                <------>\n    //                                       <---- rest ---->\n    // --------------[------------------+----+--------------]--------------\n    //                                  w    V\n    //                                       = buf * 10^k\n    //\n    // ten_k represents a unit-in-the-last-place in the decimal representation\n    // stored in buf.\n    // Decrement buf by ten_k while this takes buf closer to w.\n\n    // The tests are written in this order to avoid overflow in unsigned\n    // integer arithmetic.\n\n    while (rest < dist\n            && delta - rest >= ten_k\n            && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))\n    {\n        JSON_ASSERT(buf[len - 1] != '0');\n        buf[len - 1]--;\n        rest += ten_k;\n    }\n}\n\n/*!\nGenerates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.\nM- and M+ must be normalized and share the same exponent -60 <= e <= -32.\n*/\ninline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,\n                             diyfp M_minus, diyfp w, diyfp M_plus)\n{\n    static_assert(kAlpha >= -60, \"internal error\");\n    static_assert(kGamma <= -32, \"internal error\");\n\n    // Generates the digits (and the exponent) of a decimal floating-point\n    // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's\n    // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.\n    //\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    // Grisu2 generates the digits of M+ from left to right and stops as soon as\n    // V is in [M-,M+].\n\n    JSON_ASSERT(M_plus.e >= kAlpha);\n    JSON_ASSERT(M_plus.e <= kGamma);\n\n    std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)\n    std::uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)\n\n    // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):\n    //\n    //      M+ = f * 2^e\n    //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e\n    //         = ((p1        ) * 2^-e + (p2        )) * 2^e\n    //         = p1 + p2 * 2^e\n\n    const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);\n\n    auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)\n    std::uint64_t p2 = M_plus.f & (one.f - 1);                    // p2 = f mod 2^-e\n\n    // 1)\n    //\n    // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]\n\n    JSON_ASSERT(p1 > 0);\n\n    std::uint32_t pow10{};\n    const int k = find_largest_pow10(p1, pow10);\n\n    //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)\n    //\n    //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))\n    //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))\n    //\n    //      M+ = p1                                             + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e\n    //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e\n    //\n    // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)\n    //\n    //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]\n    //\n    // but stop as soon as\n    //\n    //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e\n\n    int n = k;\n    while (n > 0)\n    {\n        // Invariants:\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        //\n        const std::uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)\n        const std::uint32_t r = p1 % pow10;  // r = p1 mod 10^(n-1)\n        //\n        //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e\n        //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)\n        //\n        p1 = r;\n        n--;\n        //\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)\n        //      pow10 = 10^n\n        //\n\n        // Now check if enough digits have been generated.\n        // Compute\n        //\n        //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e\n        //\n        // Note:\n        // Since rest and delta share the same exponent e, it suffices to\n        // compare the significands.\n        const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;\n        if (rest <= delta)\n        {\n            // V = buffer * 10^n, with M- <= V <= M+.\n\n            decimal_exponent += n;\n\n            // We may now just stop. But instead, it looks as if the buffer\n            // could be decremented to bring V closer to w.\n            //\n            // pow10 = 10^n is now 1 ulp in the decimal representation V.\n            // The rounding procedure works with diyfp's with an implicit\n            // exponent of e.\n            //\n            //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e\n            //\n            const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;\n            grisu2_round(buffer, length, dist, delta, rest, ten_n);\n\n            return;\n        }\n\n        pow10 /= 10;\n        //\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        // Invariants restored.\n    }\n\n    // 2)\n    //\n    // The digits of the integral part have been generated:\n    //\n    //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e\n    //         = buffer            + p2 * 2^e\n    //\n    // Now generate the digits of the fractional part p2 * 2^e.\n    //\n    // Note:\n    // No decimal point is generated: the exponent is adjusted instead.\n    //\n    // p2 actually represents the fraction\n    //\n    //      p2 * 2^e\n    //          = p2 / 2^-e\n    //          = d[-1] / 10^1 + d[-2] / 10^2 + ...\n    //\n    // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)\n    //\n    //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m\n    //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)\n    //\n    // using\n    //\n    //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)\n    //                = (                   d) * 2^-e + (                   r)\n    //\n    // or\n    //      10^m * p2 * 2^e = d + r * 2^e\n    //\n    // i.e.\n    //\n    //      M+ = buffer + p2 * 2^e\n    //         = buffer + 10^-m * (d + r * 2^e)\n    //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e\n    //\n    // and stop as soon as 10^-m * r * 2^e <= delta * 2^e\n\n    JSON_ASSERT(p2 > delta);\n\n    int m = 0;\n    for (;;)\n    {\n        // Invariant:\n        //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e\n        //         = buffer * 10^-m + 10^-m * (p2                                 ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e\n        //\n        JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);\n        p2 *= 10;\n        const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e\n        const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e\n        //\n        //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))\n        //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        p2 = r;\n        m++;\n        //\n        //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e\n        // Invariant restored.\n\n        // Check if enough digits have been generated.\n        //\n        //      10^-m * p2 * 2^e <= delta * 2^e\n        //              p2 * 2^e <= 10^m * delta * 2^e\n        //                    p2 <= 10^m * delta\n        delta *= 10;\n        dist  *= 10;\n        if (p2 <= delta)\n        {\n            break;\n        }\n    }\n\n    // V = buffer * 10^-m, with M- <= V <= M+.\n\n    decimal_exponent -= m;\n\n    // 1 ulp in the decimal representation is now 10^-m.\n    // Since delta and dist are now scaled by 10^m, we need to do the\n    // same with ulp in order to keep the units in sync.\n    //\n    //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e\n    //\n    const std::uint64_t ten_m = one.f;\n    grisu2_round(buffer, length, dist, delta, p2, ten_m);\n\n    // By construction this algorithm generates the shortest possible decimal\n    // number (Loitsch, Theorem 6.2) which rounds back to w.\n    // For an input number of precision p, at least\n    //\n    //      N = 1 + ceil(p * log_10(2))\n    //\n    // decimal digits are sufficient to identify all binary floating-point\n    // numbers (Matula, \"In-and-Out conversions\").\n    // This implies that the algorithm does not produce more than N decimal\n    // digits.\n    //\n    //      N = 17 for p = 53 (IEEE double precision)\n    //      N = 9  for p = 24 (IEEE single precision)\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline void grisu2(char* buf, int& len, int& decimal_exponent,\n                   diyfp m_minus, diyfp v, diyfp m_plus)\n{\n    JSON_ASSERT(m_plus.e == m_minus.e);\n    JSON_ASSERT(m_plus.e == v.e);\n\n    //  --------(-----------------------+-----------------------)--------    (A)\n    //          m-                      v                       m+\n    //\n    //  --------------------(-----------+-----------------------)--------    (B)\n    //                      m-          v                       m+\n    //\n    // First scale v (and m- and m+) such that the exponent is in the range\n    // [alpha, gamma].\n\n    const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);\n\n    const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k\n\n    // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]\n    const diyfp w       = diyfp::mul(v,       c_minus_k);\n    const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);\n    const diyfp w_plus  = diyfp::mul(m_plus,  c_minus_k);\n\n    //  ----(---+---)---------------(---+---)---------------(---+---)----\n    //          w-                      w                       w+\n    //          = c*m-                  = c*v                   = c*m+\n    //\n    // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and\n    // w+ are now off by a small amount.\n    // In fact:\n    //\n    //      w - v * 10^k < 1 ulp\n    //\n    // To account for this inaccuracy, add resp. subtract 1 ulp.\n    //\n    //  --------+---[---------------(---+---)---------------]---+--------\n    //          w-  M-                  w                   M+  w+\n    //\n    // Now any number in [M-, M+] (bounds included) will round to w when input,\n    // regardless of how the input rounding algorithm breaks ties.\n    //\n    // And digit_gen generates the shortest possible such number in [M-, M+].\n    // Note that this does not mean that Grisu2 always generates the shortest\n    // possible number in the interval (m-, m+).\n    const diyfp M_minus(w_minus.f + 1, w_minus.e);\n    const diyfp M_plus (w_plus.f  - 1, w_plus.e );\n\n    decimal_exponent = -cached.k; // = -(-k) = k\n\n    grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1)\nvoid grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)\n{\n    static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,\n                  \"internal error: not enough precision\");\n\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // If the neighbors (and boundaries) of 'value' are always computed for double-precision\n    // numbers, all float's can be recovered using strtod (and strtof). However, the resulting\n    // decimal representations are not exactly \"short\".\n    //\n    // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)\n    // says \"value is converted to a string as if by std::sprintf in the default (\"C\") locale\"\n    // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars'\n    // does.\n    // On the other hand, the documentation for 'std::to_chars' requires that \"parsing the\n    // representation using the corresponding std::from_chars function recovers value exactly\". That\n    // indicates that single precision floating-point numbers should be recovered using\n    // 'std::strtof'.\n    //\n    // NB: If the neighbors are computed for single-precision numbers, there is a single float\n    //     (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision\n    //     value is off by 1 ulp.\n#if 0 // NOLINT(readability-avoid-unconditional-preprocessor-if)\n    const boundaries w = compute_boundaries(static_cast<double>(value));\n#else\n    const boundaries w = compute_boundaries(value);\n#endif\n\n    grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);\n}\n\n/*!\n@brief appends a decimal representation of e to buf\n@return a pointer to the element following the exponent.\n@pre -1000 < e < 1000\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* append_exponent(char* buf, int e)\n{\n    JSON_ASSERT(e > -1000);\n    JSON_ASSERT(e <  1000);\n\n    if (e < 0)\n    {\n        e = -e;\n        *buf++ = '-';\n    }\n    else\n    {\n        *buf++ = '+';\n    }\n\n    auto k = static_cast<std::uint32_t>(e);\n    if (k < 10)\n    {\n        // Always print at least two digits in the exponent.\n        // This is for compatibility with printf(\"%g\").\n        *buf++ = '0';\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else if (k < 100)\n    {\n        *buf++ = static_cast<char>('0' + (k / 10));\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else\n    {\n        *buf++ = static_cast<char>('0' + (k / 100));\n        k %= 100;\n        *buf++ = static_cast<char>('0' + (k / 10));\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n\n    return buf;\n}\n\n/*!\n@brief prettify v = buf * 10^decimal_exponent\n\nIf v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point\nnotation. Otherwise it will be printed in exponential notation.\n\n@pre min_exp < 0\n@pre max_exp > 0\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* format_buffer(char* buf, int len, int decimal_exponent,\n                           int min_exp, int max_exp)\n{\n    JSON_ASSERT(min_exp < 0);\n    JSON_ASSERT(max_exp > 0);\n\n    const int k = len;\n    const int n = len + decimal_exponent;\n\n    // v = buf * 10^(n-k)\n    // k is the length of the buffer (number of decimal digits)\n    // n is the position of the decimal point relative to the start of the buffer.\n\n    if (k <= n && n <= max_exp)\n    {\n        // digits[000]\n        // len <= max_exp + 2\n\n        std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));\n        // Make it look like a floating-point number (#362, #378)\n        buf[n + 0] = '.';\n        buf[n + 1] = '0';\n        return buf + (static_cast<size_t>(n) + 2);\n    }\n\n    if (0 < n && n <= max_exp)\n    {\n        // dig.its\n        // len <= max_digits10 + 1\n\n        JSON_ASSERT(k > n);\n\n        std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));\n        buf[n] = '.';\n        return buf + (static_cast<size_t>(k) + 1U);\n    }\n\n    if (min_exp < n && n <= 0)\n    {\n        // 0.[000]digits\n        // len <= 2 + (-min_exp - 1) + max_digits10\n\n        std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k));\n        buf[0] = '0';\n        buf[1] = '.';\n        std::memset(buf + 2, '0', static_cast<size_t>(-n));\n        return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));\n    }\n\n    if (k == 1)\n    {\n        // dE+123\n        // len <= 1 + 5\n\n        buf += 1;\n    }\n    else\n    {\n        // d.igitsE+123\n        // len <= max_digits10 + 1 + 5\n\n        std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);\n        buf[1] = '.';\n        buf += 1 + static_cast<size_t>(k);\n    }\n\n    *buf++ = 'e';\n    return append_exponent(buf, n - 1);\n}\n\n}  // namespace dtoa_impl\n\n/*!\n@brief generates a decimal representation of the floating-point number value in [first, last).\n\nThe format of the resulting decimal representation is similar to printf's %g\nformat. Returns an iterator pointing past-the-end of the decimal representation.\n\n@note The input number must be finite, i.e. NaN's and Inf's are not supported.\n@note The buffer must be large enough.\n@note The result is NOT null-terminated.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1, 2)\nJSON_HEDLEY_RETURNS_NON_NULL\nchar* to_chars(char* first, const char* last, FloatType value)\n{\n    static_cast<void>(last); // maybe unused - fix warning\n    JSON_ASSERT(std::isfinite(value));\n\n    // Use signbit(value) instead of (value < 0) since signbit works for -0.\n    if (std::signbit(value))\n    {\n        value = -value;\n        *first++ = '-';\n    }\n\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n    if (value == 0) // +-0\n    {\n        *first++ = '0';\n        // Make it look like a floating-point number (#362, #378)\n        *first++ = '.';\n        *first++ = '0';\n        return first;\n    }\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);\n\n    // Compute v = buffer * 10^decimal_exponent.\n    // The decimal digits are stored in the buffer, which needs to be interpreted\n    // as an unsigned decimal integer.\n    // len is the length of the buffer, i.e., the number of decimal digits.\n    int len = 0;\n    int decimal_exponent = 0;\n    dtoa_impl::grisu2(first, len, decimal_exponent, value);\n\n    JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);\n\n    // Format the buffer like printf(\"%.*g\", prec, value)\n    constexpr int kMinExp = -4;\n    // Use digits10 here to increase compatibility with version 2.\n    constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;\n\n    JSON_ASSERT(last - first >= kMaxExp + 2);\n    JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);\n\n    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);\n}\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/conversions/to_json.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <nlohmann/detail/macro_scope.hpp> // JSON_HAS_CPP_17\n#ifdef JSON_HAS_CPP_17\n    #include <optional> // optional\n#endif\n\n#include <algorithm> // copy\n#include <iterator> // begin, end\n#include <memory> // allocator_traits\n#include <string> //  basic_string, char_traits\n#include <tuple> // tuple, get\n#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type\n#include <utility> // move, forward, declval, pair\n#include <valarray> // valarray\n#include <vector> // vector\n\n#include <nlohmann/detail/iterators/iteration_proxy.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/meta/std_fs.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n//////////////////\n// constructors //\n//////////////////\n\n/*\n * Note all external_constructor<>::construct functions need to call\n * j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an\n * allocated value (e.g., a string). See bug issue\n * https://github.com/nlohmann/json/issues/2865 for more information.\n */\n\ntemplate<value_t> struct external_constructor;\n\ntemplate<>\nstruct external_constructor<value_t::boolean>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::boolean;\n        j.m_data.m_value = b;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::string>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::string;\n        j.m_data.m_value = s;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::string;\n        j.m_data.m_value = std::move(s);\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleStringType,\n               enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleStringType& str)\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::string;\n        j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::binary>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::binary;\n        j.m_data.m_value = typename BasicJsonType::binary_t(b);\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::binary;\n        j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_float>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::number_float;\n        j.m_data.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_unsigned>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::number_unsigned;\n        j.m_data.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_integer>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::number_integer;\n        j.m_data.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::array>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::array;\n        j.m_data.m_value = arr;\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::array;\n        j.m_data.m_value = std::move(arr);\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleArrayType,\n               enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::array;\n        j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const std::vector<bool>& arr)\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::array;\n        j.m_data.m_value = value_t::array;\n        j.m_data.m_value.array->reserve(arr.size());\n        for (const bool x : arr)\n        {\n            j.m_data.m_value.array->push_back(x);\n            j.set_parent(j.m_data.m_value.array->back());\n        }\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename T,\n             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\n    static void construct(BasicJsonType& j, const std::valarray<T>& arr)\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::array;\n        j.m_data.m_value = value_t::array;\n        j.m_data.m_value.array->resize(arr.size());\n        if (arr.size() > 0)\n        {\n            std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());\n        }\n        j.set_parents();\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::object>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::object;\n        j.m_data.m_value = obj;\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n    {\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::object;\n        j.m_data.m_value = std::move(obj);\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleObjectType,\n               enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_data.m_value.destroy(j.m_data.m_type);\n        j.m_data.m_type = value_t::object;\n        j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));\n        j.set_parents();\n        j.assert_invariant();\n    }\n};\n\n/////////////\n// to_json //\n/////////////\n\n#ifdef JSON_HAS_CPP_17\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept\n{\n    if (opt.has_value())\n    {\n        j = *opt;\n    }\n    else\n    {\n        j = nullptr;\n    }\n}\n#endif\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>\ninline void to_json(BasicJsonType& j, T b) noexcept\n{\n    external_constructor<value_t::boolean>::construct(j, b);\n}\n\ntemplate < typename BasicJsonType, typename BoolRef,\n           enable_if_t <\n               ((std::is_same<std::vector<bool>::reference, BoolRef>::value\n                 && !std::is_same <std::vector<bool>::reference, typename BasicJsonType::boolean_t&>::value)\n                || (std::is_same<std::vector<bool>::const_reference, BoolRef>::value\n                    && !std::is_same <detail::uncvref_t<std::vector<bool>::const_reference>,\n                                      typename BasicJsonType::boolean_t >::value))\n               && std::is_convertible<const BoolRef&, typename BasicJsonType::boolean_t>::value, int > = 0 >\ninline void to_json(BasicJsonType& j, const BoolRef& b) noexcept\n{\n    external_constructor<value_t::boolean>::construct(j, static_cast<typename BasicJsonType::boolean_t>(b));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleString,\n         enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>\ninline void to_json(BasicJsonType& j, const CompatibleString& s)\n{\n    external_constructor<value_t::string>::construct(j, s);\n}\n\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n{\n    external_constructor<value_t::string>::construct(j, std::move(s));\n}\n\ntemplate<typename BasicJsonType, typename FloatType,\n         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>\ninline void to_json(BasicJsonType& j, FloatType val) noexcept\n{\n    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberUnsignedType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>\ninline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept\n{\n    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberIntegerType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>\ninline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept\n{\n    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));\n}\n\n#if !JSON_DISABLE_ENUM_SERIALIZATION\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\ninline void to_json(BasicJsonType& j, EnumType e) noexcept\n{\n    using underlying_type = typename std::underlying_type<EnumType>::type;\n    static constexpr value_t integral_value_t = std::is_unsigned<underlying_type>::value ? value_t::number_unsigned : value_t::number_integer;\n    external_constructor<integral_value_t>::construct(j, static_cast<underlying_type>(e));\n}\n#endif  // JSON_DISABLE_ENUM_SERIALIZATION\n\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, const std::vector<bool>& e)\n{\n    external_constructor<value_t::array>::construct(j, e);\n}\n\ntemplate < typename BasicJsonType, typename CompatibleArrayType,\n           enable_if_t < is_compatible_array_type<BasicJsonType,\n                         CompatibleArrayType>::value&&\n                         !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&\n                         !is_basic_json<CompatibleArrayType>::value,\n                         int > = 0 >\ninline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)\n{\n    external_constructor<value_t::binary>::construct(j, bin);\n}\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\ninline void to_json(BasicJsonType& j, const std::valarray<T>& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate < typename BasicJsonType, typename CompatibleObjectType,\n           enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >\ninline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)\n{\n    external_constructor<value_t::object>::construct(j, obj);\n}\n\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n{\n    external_constructor<value_t::object>::construct(j, std::move(obj));\n}\n\ntemplate <\n    typename BasicJsonType, typename T, std::size_t N,\n    enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,\n                  const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n                  int > = 0 >\ninline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >\ninline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)\n{\n    j = { p.first, p.second };\n}\n\n// for https://github.com/nlohmann/json/pull/1134\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>\ninline void to_json(BasicJsonType& j, const T& b)\n{\n    j = { {b.key(), b.value()} };\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\ninline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    j = { std::get<Idx>(t)... };\n}\n\ntemplate<typename BasicJsonType, typename Tuple>\ninline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)\n{\n    using array_t = typename BasicJsonType::array_t;\n    j = array_t();\n}\n\ntemplate<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>\ninline void to_json(BasicJsonType& j, const T& t)\n{\n    to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});\n}\n\n#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM\n#if defined(__cpp_lib_char8_t)\ntemplate<typename BasicJsonType, typename Tr, typename Allocator>\ninline void to_json(BasicJsonType& j, const std::basic_string<char8_t, Tr, Allocator>& s)\n{\n    using OtherAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<char>;\n    j = std::basic_string<char, std::char_traits<char>, OtherAllocator>(s.begin(), s.end(), s.get_allocator());\n}\n#endif\n\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, const std_fs::path& p)\n{\n    // Returns either a std::string or a std::u8string depending whether library\n    // support for char8_t is enabled.\n    j = p.u8string();\n}\n#endif\n\nstruct to_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))\n    -> decltype(to_json(j, std::forward<T>(val)), void())\n    {\n        return to_json(j, std::forward<T>(val));\n    }\n};\n}  // namespace detail\n\n#ifndef JSON_HAS_CPP_17\n/// namespace to hold default `to_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)\n{\n#endif\nJSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)\n    detail::static_const<detail::to_json_fn>::value;\n#ifndef JSON_HAS_CPP_17\n}  // namespace\n#endif\n\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/exceptions.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstddef> // nullptr_t\n#include <exception> // exception\n#if JSON_DIAGNOSTICS\n    #include <numeric> // accumulate\n#endif\n#include <stdexcept> // runtime_error\n#include <string> // to_string\n#include <vector> // vector\n\n#include <nlohmann/detail/value_t.hpp>\n#include <nlohmann/detail/string_escape.hpp>\n#include <nlohmann/detail/input/position_t.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/string_concat.hpp>\n\n// With -Wweak-vtables, Clang will complain about the exception classes as they\n// have no out-of-line virtual method definitions and their vtable will be\n// emitted in every translation unit. This issue cannot be fixed with a\n// header-only library as there is no implementation file to move these\n// functions to. As a result, we suppress this warning here to avoid client\n// code stumbling over this. See https://github.com/nlohmann/json/issues/4087\n// for a discussion.\n#if defined(__clang__)\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wweak-vtables\"\n#endif\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n////////////////\n// exceptions //\n////////////////\n\n/// @brief general exception of the @ref basic_json class\n/// @sa https://json.nlohmann.me/api/basic_json/exception/\nclass exception : public std::exception\n{\n  public:\n    /// returns the explanatory string\n    const char* what() const noexcept override\n    {\n        return m.what();\n    }\n\n    /// the id of the exception\n    const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)\n\n  protected:\n    JSON_HEDLEY_NON_NULL(3)\n    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)\n\n    static std::string name(const std::string& ename, int id_)\n    {\n        return concat(\"[json.exception.\", ename, '.', std::to_string(id_), \"] \");\n    }\n\n    static std::string diagnostics(std::nullptr_t /*leaf_element*/)\n    {\n        return \"\";\n    }\n\n    template<typename BasicJsonType>\n    static std::string diagnostics(const BasicJsonType* leaf_element)\n    {\n#if JSON_DIAGNOSTICS\n        std::vector<std::string> tokens;\n        for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent)\n        {\n            switch (current->m_parent->type())\n            {\n                case value_t::array:\n                {\n                    for (std::size_t i = 0; i < current->m_parent->m_data.m_value.array->size(); ++i)\n                    {\n                        if (&current->m_parent->m_data.m_value.array->operator[](i) == current)\n                        {\n                            tokens.emplace_back(std::to_string(i));\n                            break;\n                        }\n                    }\n                    break;\n                }\n\n                case value_t::object:\n                {\n                    for (const auto& element : *current->m_parent->m_data.m_value.object)\n                    {\n                        if (&element.second == current)\n                        {\n                            tokens.emplace_back(element.first.c_str());\n                            break;\n                        }\n                    }\n                    break;\n                }\n\n                case value_t::null: // LCOV_EXCL_LINE\n                case value_t::string: // LCOV_EXCL_LINE\n                case value_t::boolean: // LCOV_EXCL_LINE\n                case value_t::number_integer: // LCOV_EXCL_LINE\n                case value_t::number_unsigned: // LCOV_EXCL_LINE\n                case value_t::number_float: // LCOV_EXCL_LINE\n                case value_t::binary: // LCOV_EXCL_LINE\n                case value_t::discarded: // LCOV_EXCL_LINE\n                default:   // LCOV_EXCL_LINE\n                    break; // LCOV_EXCL_LINE\n            }\n        }\n\n        if (tokens.empty())\n        {\n            return \"\";\n        }\n\n        auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},\n                                   [](const std::string & a, const std::string & b)\n        {\n            return concat(a, '/', detail::escape(b));\n        });\n\n        return concat('(', str, \") \", get_byte_positions(leaf_element));\n#else\n        return get_byte_positions(leaf_element);\n#endif\n    }\n\n  private:\n    /// an exception object as storage for error messages\n    std::runtime_error m;\n#if JSON_DIAGNOSTIC_POSITIONS\n    template<typename BasicJsonType>\n    static std::string get_byte_positions(const BasicJsonType* leaf_element)\n    {\n        if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos))\n        {\n            return concat(\"(bytes \", std::to_string(leaf_element->start_pos()), \"-\", std::to_string(leaf_element->end_pos()), \") \");\n        }\n        return \"\";\n    }\n#else\n    template<typename BasicJsonType>\n    static std::string get_byte_positions(const BasicJsonType* leaf_element)\n    {\n        static_cast<void>(leaf_element);\n        return \"\";\n    }\n#endif\n};\n\n/// @brief exception indicating a parse error\n/// @sa https://json.nlohmann.me/api/basic_json/parse_error/\nclass parse_error : public exception\n{\n  public:\n    /*!\n    @brief create a parse error exception\n    @param[in] id_       the id of the exception\n    @param[in] pos       the position where the error occurred (or with\n                         chars_read_total=0 if the position cannot be\n                         determined)\n    @param[in] what_arg  the explanatory string\n    @return parse_error object\n    */\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)\n    {\n        const std::string w = concat(exception::name(\"parse_error\", id_), \"parse error\",\n                                     position_string(pos), \": \", exception::diagnostics(context), what_arg);\n        return {id_, pos.chars_read_total, w.c_str()};\n    }\n\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)\n    {\n        const std::string w = concat(exception::name(\"parse_error\", id_), \"parse error\",\n                                     (byte_ != 0 ? (concat(\" at byte \", std::to_string(byte_))) : \"\"),\n                                     \": \", exception::diagnostics(context), what_arg);\n        return {id_, byte_, w.c_str()};\n    }\n\n    /*!\n    @brief byte index of the parse error\n\n    The byte index of the last read character in the input file.\n\n    @note For an input with n bytes, 1 is the index of the first character and\n          n+1 is the index of the terminating null byte or the end of file.\n          This also holds true when reading a byte vector (CBOR or MessagePack).\n    */\n    const std::size_t byte;\n\n  private:\n    parse_error(int id_, std::size_t byte_, const char* what_arg)\n        : exception(id_, what_arg), byte(byte_) {}\n\n    static std::string position_string(const position_t& pos)\n    {\n        return concat(\" at line \", std::to_string(pos.lines_read + 1),\n                      \", column \", std::to_string(pos.chars_read_current_line));\n    }\n};\n\n/// @brief exception indicating errors with iterators\n/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/\nclass invalid_iterator : public exception\n{\n  public:\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)\n    {\n        const std::string w = concat(exception::name(\"invalid_iterator\", id_), exception::diagnostics(context), what_arg);\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    invalid_iterator(int id_, const char* what_arg)\n        : exception(id_, what_arg) {}\n};\n\n/// @brief exception indicating executing a member function with a wrong type\n/// @sa https://json.nlohmann.me/api/basic_json/type_error/\nclass type_error : public exception\n{\n  public:\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)\n    {\n        const std::string w = concat(exception::name(\"type_error\", id_), exception::diagnostics(context), what_arg);\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/// @brief exception indicating access out of the defined range\n/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/\nclass out_of_range : public exception\n{\n  public:\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)\n    {\n        const std::string w = concat(exception::name(\"out_of_range\", id_), exception::diagnostics(context), what_arg);\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/// @brief exception indicating other library errors\n/// @sa https://json.nlohmann.me/api/basic_json/other_error/\nclass other_error : public exception\n{\n  public:\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)\n    {\n        const std::string w = concat(exception::name(\"other_error\", id_), exception::diagnostics(context), what_arg);\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/hash.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstdint> // uint8_t\n#include <cstddef> // size_t\n#include <functional> // hash\n\n#include <nlohmann/detail/abi_macros.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n// boost::hash_combine\ninline std::size_t combine(std::size_t seed, std::size_t h) noexcept\n{\n    seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);\n    return seed;\n}\n\n/*!\n@brief hash a JSON value\n\nThe hash function tries to rely on std::hash where possible. Furthermore, the\ntype of the JSON value is taken into account to have different hash values for\nnull, 0, 0U, and false, etc.\n\n@tparam BasicJsonType basic_json specialization\n@param j JSON value to hash\n@return hash value of j\n*/\ntemplate<typename BasicJsonType>\nstd::size_t hash(const BasicJsonType& j)\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n\n    const auto type = static_cast<std::size_t>(j.type());\n    switch (j.type())\n    {\n        case BasicJsonType::value_t::null:\n        case BasicJsonType::value_t::discarded:\n        {\n            return combine(type, 0);\n        }\n\n        case BasicJsonType::value_t::object:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j.items())\n            {\n                const auto h = std::hash<string_t> {}(element.key());\n                seed = combine(seed, h);\n                seed = combine(seed, hash(element.value()));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::array:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j)\n            {\n                seed = combine(seed, hash(element));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::string:\n        {\n            const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::boolean:\n        {\n            const auto h = std::hash<bool> {}(j.template get<bool>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_integer:\n        {\n            const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_unsigned:\n        {\n            const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_float:\n        {\n            const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::binary:\n        {\n            auto seed = combine(type, j.get_binary().size());\n            const auto h = std::hash<bool> {}(j.get_binary().has_subtype());\n            seed = combine(seed, h);\n            seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype()));\n            for (const auto byte : j.get_binary())\n            {\n                seed = combine(seed, std::hash<std::uint8_t> {}(byte));\n            }\n            return seed;\n        }\n\n        default:                   // LCOV_EXCL_LINE\n            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            return 0;              // LCOV_EXCL_LINE\n    }\n}\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/input/binary_reader.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <algorithm> // generate_n\n#include <array> // array\n#include <cmath> // ldexp\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstdio> // snprintf\n#include <cstring> // memcpy\n#include <iterator> // back_inserter\n#include <limits> // numeric_limits\n#include <string> // char_traits, string\n#include <utility> // make_pair, move\n#include <vector> // vector\n#ifdef __cpp_lib_byteswap\n    #include <bit>  //byteswap\n#endif\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/input/input_adapters.hpp>\n#include <nlohmann/detail/input/json_sax.hpp>\n#include <nlohmann/detail/input/lexer.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/is_sax.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/string_concat.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/// how to treat CBOR tags\nenum class cbor_tag_handler_t\n{\n    error,   ///< throw a parse_error exception in case of a tag\n    ignore,  ///< ignore tags\n    store    ///< store tags as binary type\n};\n\n/*!\n@brief determine system byte order\n\n@return true if and only if system's byte order is little endian\n\n@note from https://stackoverflow.com/a/1001328/266378\n*/\ninline bool little_endianness(int num = 1) noexcept\n{\n    return *reinterpret_cast<char*>(&num) == 1;\n}\n\n///////////////////\n// binary reader //\n///////////////////\n\n/*!\n@brief deserialization of CBOR, MessagePack, and UBJSON values\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType, InputAdapterType>>\nclass binary_reader\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using json_sax_t = SAX;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename char_traits<char_type>::int_type;\n\n  public:\n    /*!\n    @brief create a binary reader\n\n    @param[in] adapter  input adapter to read from\n    */\n    explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json) noexcept : ia(std::move(adapter)), input_format(format)\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n    }\n\n    // make class move-only\n    binary_reader(const binary_reader&) = delete;\n    binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    binary_reader& operator=(const binary_reader&) = delete;\n    binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~binary_reader() = default;\n\n    /*!\n    @param[in] format  the binary format to parse\n    @param[in] sax_    a SAX event processor\n    @param[in] strict  whether to expect the input to be consumed completed\n    @param[in] tag_handler  how to treat CBOR tags\n\n    @return whether parsing was successful\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool sax_parse(const input_format_t format,\n                   json_sax_t* sax_,\n                   const bool strict = true,\n                   const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        sax = sax_;\n        bool result = false;\n\n        switch (format)\n        {\n            case input_format_t::bson:\n                result = parse_bson_internal();\n                break;\n\n            case input_format_t::cbor:\n                result = parse_cbor_internal(true, tag_handler);\n                break;\n\n            case input_format_t::msgpack:\n                result = parse_msgpack_internal();\n                break;\n\n            case input_format_t::ubjson:\n            case input_format_t::bjdata:\n                result = parse_ubjson_internal();\n                break;\n\n            case input_format_t::json: // LCOV_EXCL_LINE\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\n        // strict mode: next byte must be EOF\n        if (result && strict)\n        {\n            if (input_format == input_format_t::ubjson || input_format == input_format_t::bjdata)\n            {\n                get_ignore_noop();\n            }\n            else\n            {\n                get();\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(current != char_traits<char_type>::eof()))\n            {\n                return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read,\n                                        exception_message(input_format, concat(\"expected end of input; last byte: 0x\", get_token_string()), \"value\"), nullptr));\n            }\n        }\n\n        return result;\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @brief Reads in a BSON-object and passes it to the SAX-parser.\n    @return whether a valid BSON-value was passed to the SAX parser\n    */\n    bool parse_bson_internal()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))\n        {\n            return false;\n        }\n\n        return sax->end_object();\n    }\n\n    /*!\n    @brief Parses a C-style string from the BSON input.\n    @param[in,out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @return `true` if the \\x00-byte indicating the end of the string was\n             encountered before the EOF; false` indicates an unexpected EOF.\n    */\n    bool get_bson_cstr(string_t& result)\n    {\n        auto out = std::back_inserter(result);\n        while (true)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"cstring\")))\n            {\n                return false;\n            }\n            if (current == 0x00)\n            {\n                return true;\n            }\n            *out++ = static_cast<typename string_t::value_type>(current);\n        }\n    }\n\n    /*!\n    @brief Parses a zero-terminated string of length @a len from the BSON\n           input.\n    @param[in] len  The length (including the zero-byte at the end) of the\n                    string to be read.\n    @param[in,out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 1\n    @return `true` if the string was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_string(const NumberType len, string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 1))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                    exception_message(input_format_t::bson, concat(\"string length must be at least 1, is \", std::to_string(len)), \"string\"), nullptr));\n        }\n\n        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != char_traits<char_type>::eof();\n    }\n\n    /*!\n    @brief Parses a byte array input of length @a len from the BSON input.\n    @param[in] len  The length of the byte array to be read.\n    @param[in,out] result  A reference to the binary variable where the read\n                            array is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 0\n    @return `true` if the byte array was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_binary(const NumberType len, binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 0))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                    exception_message(input_format_t::bson, concat(\"byte array length cannot be negative, is \", std::to_string(len)), \"binary\"), nullptr));\n        }\n\n        // All BSON binary values have a subtype\n        std::uint8_t subtype{};\n        get_number<std::uint8_t>(input_format_t::bson, subtype);\n        result.set_subtype(subtype);\n\n        return get_binary(input_format_t::bson, len, result);\n    }\n\n    /*!\n    @brief Read a BSON document element of the given @a element_type.\n    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html\n    @param[in] element_type_parse_position The position in the input stream,\n               where the `element_type` was read.\n    @warning Not all BSON element types are supported yet. An unsupported\n             @a element_type will give rise to a parse_error.114:\n             Unsupported BSON record type 0x...\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_internal(const char_int_type element_type,\n                                     const std::size_t element_type_parse_position)\n    {\n        switch (element_type)\n        {\n            case 0x01: // double\n            {\n                double number{};\n                return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0x02: // string\n            {\n                std::int32_t len{};\n                string_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);\n            }\n\n            case 0x03: // object\n            {\n                return parse_bson_internal();\n            }\n\n            case 0x04: // array\n            {\n                return parse_bson_array();\n            }\n\n            case 0x05: // binary\n            {\n                std::int32_t len{};\n                binary_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);\n            }\n\n            case 0x08: // boolean\n            {\n                return sax->boolean(get() != 0);\n            }\n\n            case 0x0A: // null\n            {\n                return sax->null();\n            }\n\n            case 0x10: // int32\n            {\n                std::int32_t value{};\n                return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            case 0x12: // int64\n            {\n                std::int64_t value{};\n                return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            case 0x11: // uint64\n            {\n                std::uint64_t value{};\n                return get_number<std::uint64_t, true>(input_format_t::bson, value) && sax->number_unsigned(value);\n            }\n\n            default: // anything else is not supported (yet)\n            {\n                std::array<char, 3> cr{{}};\n                static_cast<void>((std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                const std::string cr_str{cr.data()};\n                return sax->parse_error(element_type_parse_position, cr_str,\n                                        parse_error::create(114, element_type_parse_position, concat(\"Unsupported BSON record type 0x\", cr_str), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @brief Read a BSON element list (as specified in the BSON-spec)\n\n    The same binary layout is used for objects and arrays, hence it must be\n    indicated with the argument @a is_array which one is expected\n    (true --> array, false --> object).\n\n    @param[in] is_array Determines if the element list being read is to be\n                        treated as an object (@a is_array == false), or as an\n                        array (@a is_array == true).\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_list(const bool is_array)\n    {\n        string_t key;\n\n        while (auto element_type = get())\n        {\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"element list\")))\n            {\n                return false;\n            }\n\n            const std::size_t element_type_parse_position = chars_read;\n            if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))\n            {\n                return false;\n            }\n\n            if (!is_array && !sax->key(key))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))\n            {\n                return false;\n            }\n\n            // get_bson_cstr only appends\n            key.clear();\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief Reads an array from the BSON input and passes it to the SAX-parser.\n    @return whether a valid BSON-array was passed to the SAX parser\n    */\n    bool parse_bson_array()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))\n        {\n            return false;\n        }\n\n        return sax->end_array();\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true) or whether the last read character should\n                         be considered instead (false)\n    @param[in] tag_handler how CBOR tags should be treated\n\n    @return whether a valid CBOR value was passed to the SAX parser\n    */\n    bool parse_cbor_internal(const bool get_char,\n                             const cbor_tag_handler_t tag_handler)\n    {\n        switch (get_char ? get() : current)\n        {\n            // EOF\n            case char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::cbor, \"value\");\n\n            // Integer 0x00..0x17 (0..23)\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            case 0x18: // Unsigned integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x19: // Unsigned integer (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1A: // Unsigned integer (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            // Negative integer -1-0x00..-1-0x17 (-1..-24)\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n                return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));\n\n            case 0x38: // Negative integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)\n                        - static_cast<number_integer_t>(number));\n            }\n\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            case 0x5F: // Binary data (indefinite length)\n            {\n                binary_t b;\n                return get_cbor_binary(b) && sax->binary(b);\n            }\n\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                string_t s;\n                return get_cbor_string(s) && sax->string(s);\n            }\n\n            // array (0x00..0x17 data items follow)\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n                return get_cbor_array(\n                           conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0x98: // array (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x99: // array (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9A: // array (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9B: // array (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9F: // array (indefinite length)\n                return get_cbor_array(detail::unknown_size(), tag_handler);\n\n            // map (0x00..0x17 pairs of data items follow)\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n                return get_cbor_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0xB8: // map (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xB9: // map (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBA: // map (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBB: // map (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBF: // map (indefinite length)\n                return get_cbor_object(detail::unknown_size(), tag_handler);\n\n            case 0xC6: // tagged item\n            case 0xC7:\n            case 0xC8:\n            case 0xC9:\n            case 0xCA:\n            case 0xCB:\n            case 0xCC:\n            case 0xCD:\n            case 0xCE:\n            case 0xCF:\n            case 0xD0:\n            case 0xD1:\n            case 0xD2:\n            case 0xD3:\n            case 0xD4:\n            case 0xD8: // tagged item (1 byte follows)\n            case 0xD9: // tagged item (2 bytes follow)\n            case 0xDA: // tagged item (4 bytes follow)\n            case 0xDB: // tagged item (8 bytes follow)\n            {\n                switch (tag_handler)\n                {\n                    case cbor_tag_handler_t::error:\n                    {\n                        auto last_token = get_token_string();\n                        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                                exception_message(input_format_t::cbor, concat(\"invalid byte: 0x\", last_token), \"value\"), nullptr));\n                    }\n\n                    case cbor_tag_handler_t::ignore:\n                    {\n                        // ignore binary subtype\n                        switch (current)\n                        {\n                            case 0xD8:\n                            {\n                                std::uint8_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xD9:\n                            {\n                                std::uint16_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xDA:\n                            {\n                                std::uint32_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xDB:\n                            {\n                                std::uint64_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            default:\n                                break;\n                        }\n                        return parse_cbor_internal(true, tag_handler);\n                    }\n\n                    case cbor_tag_handler_t::store:\n                    {\n                        binary_t b;\n                        // use binary subtype and store in a binary container\n                        switch (current)\n                        {\n                            case 0xD8:\n                            {\n                                std::uint8_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xD9:\n                            {\n                                std::uint16_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xDA:\n                            {\n                                std::uint32_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xDB:\n                            {\n                                std::uint64_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            default:\n                                return parse_cbor_internal(true, tag_handler);\n                        }\n                        get();\n                        return get_cbor_binary(b) && sax->binary(b);\n                    }\n\n                    default:                 // LCOV_EXCL_LINE\n                        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n                        return false;        // LCOV_EXCL_LINE\n                }\n            }\n\n            case 0xF4: // false\n                return sax->boolean(false);\n\n            case 0xF5: // true\n                return sax->boolean(true);\n\n            case 0xF6: // null\n                return sax->null();\n\n            case 0xF9: // Half-Precision Float (two-byte IEEE 754)\n            {\n                const auto byte1_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n                const auto byte2_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n\n                const auto byte1 = static_cast<unsigned char>(byte1_raw);\n                const auto byte2 = static_cast<unsigned char>(byte2_raw);\n\n                // Code from RFC 7049, Appendix D, Figure 3:\n                // As half-precision floating-point numbers were only added\n                // to IEEE 754 in 2008, today's programming platforms often\n                // still only have limited support for them. It is very\n                // easy to include at least decoding support for them even\n                // without such support. An example of a small decoder for\n                // half-precision floating-point numbers in the C language\n                // is shown in Fig. 3.\n                const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);\n                const double val = [&half]\n                {\n                    const int exp = (half >> 10u) & 0x1Fu;\n                    const unsigned int mant = half & 0x3FFu;\n                    JSON_ASSERT(0 <= exp&& exp <= 32);\n                    JSON_ASSERT(mant <= 1024);\n                    switch (exp)\n                    {\n                        case 0:\n                            return std::ldexp(mant, -24);\n                        case 31:\n                            return (mant == 0)\n                            ? std::numeric_limits<double>::infinity()\n                            : std::numeric_limits<double>::quiet_NaN();\n                        default:\n                            return std::ldexp(mant + 1024, exp - 25);\n                    }\n                }();\n                return sax->number_float((half & 0x8000u) != 0\n                                         ? static_cast<number_float_t>(-val)\n                                         : static_cast<number_float_t>(val), \"\");\n            }\n\n            case 0xFA: // Single-Precision Float (four-byte IEEE 754)\n            {\n                float number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)\n            {\n                double number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            default: // anything else (0xFF is handled inside the other types)\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                        exception_message(input_format_t::cbor, concat(\"invalid byte: 0x\", last_token), \"value\"), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n    Additionally, CBOR's strings with indefinite lengths are supported.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_cbor_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            {\n                return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    string_t chunk;\n                    if (!get_cbor_string(chunk))\n                    {\n                        return false;\n                    }\n                    result.append(chunk);\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,\n                                        exception_message(input_format_t::cbor, concat(\"expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x\", last_token), \"string\"), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into the byte array.\n    Additionally, CBOR's byte arrays with indefinite lengths are supported.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_cbor_binary(binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"binary\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            {\n                return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5F: // Binary data (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    binary_t chunk;\n                    if (!get_cbor_binary(chunk))\n                    {\n                        return false;\n                    }\n                    result.insert(result.end(), chunk.begin(), chunk.end());\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,\n                                        exception_message(input_format_t::cbor, concat(\"expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x\", last_token), \"binary\"), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array or detail::unknown_size() for an\n                    array of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether array creation completed\n    */\n    bool get_cbor_array(const std::size_t len,\n                        const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        if (len != detail::unknown_size())\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object or detail::unknown_size() for an\n                    object of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether object creation completed\n    */\n    bool get_cbor_object(const std::size_t len,\n                         const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        if (len != 0)\n        {\n            string_t key;\n            if (len != detail::unknown_size())\n            {\n                for (std::size_t i = 0; i < len; ++i)\n                {\n                    get();\n                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n\n                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                while (get() != 0xFF)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n\n                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    /*!\n    @return whether a valid MessagePack value was passed to the SAX parser\n    */\n    bool parse_msgpack_internal()\n    {\n        switch (get())\n        {\n            // EOF\n            case char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::msgpack, \"value\");\n\n            // positive fixint\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n            case 0x18:\n            case 0x19:\n            case 0x1A:\n            case 0x1B:\n            case 0x1C:\n            case 0x1D:\n            case 0x1E:\n            case 0x1F:\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n            case 0x38:\n            case 0x39:\n            case 0x3A:\n            case 0x3B:\n            case 0x3C:\n            case 0x3D:\n            case 0x3E:\n            case 0x3F:\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58:\n            case 0x59:\n            case 0x5A:\n            case 0x5B:\n            case 0x5C:\n            case 0x5D:\n            case 0x5E:\n            case 0x5F:\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78:\n            case 0x79:\n            case 0x7A:\n            case 0x7B:\n            case 0x7C:\n            case 0x7D:\n            case 0x7E:\n            case 0x7F:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            // fixmap\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n                return get_msgpack_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixarray\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n            case 0x98:\n            case 0x99:\n            case 0x9A:\n            case 0x9B:\n            case 0x9C:\n            case 0x9D:\n            case 0x9E:\n            case 0x9F:\n                return get_msgpack_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            case 0xD9: // str 8\n            case 0xDA: // str 16\n            case 0xDB: // str 32\n            {\n                string_t s;\n                return get_msgpack_string(s) && sax->string(s);\n            }\n\n            case 0xC0: // nil\n                return sax->null();\n\n            case 0xC2: // false\n                return sax->boolean(false);\n\n            case 0xC3: // true\n                return sax->boolean(true);\n\n            case 0xC4: // bin 8\n            case 0xC5: // bin 16\n            case 0xC6: // bin 32\n            case 0xC7: // ext 8\n            case 0xC8: // ext 16\n            case 0xC9: // ext 32\n            case 0xD4: // fixext 1\n            case 0xD5: // fixext 2\n            case 0xD6: // fixext 4\n            case 0xD7: // fixext 8\n            case 0xD8: // fixext 16\n            {\n                binary_t b;\n                return get_msgpack_binary(b) && sax->binary(b);\n            }\n\n            case 0xCA: // float 32\n            {\n                float number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCB: // float 64\n            {\n                double number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCC: // uint 8\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCD: // uint 16\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCE: // uint 32\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCF: // uint 64\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xD0: // int 8\n            {\n                std::int8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD1: // int 16\n            {\n                std::int16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD2: // int 32\n            {\n                std::int32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD3: // int 64\n            {\n                std::int64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xDC: // array 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDD: // array 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(conditional_static_cast<std::size_t>(len));\n            }\n\n            case 0xDE: // map 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xDF: // map 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(conditional_static_cast<std::size_t>(len));\n            }\n\n            // negative fixint\n            case 0xE0:\n            case 0xE1:\n            case 0xE2:\n            case 0xE3:\n            case 0xE4:\n            case 0xE5:\n            case 0xE6:\n            case 0xE7:\n            case 0xE8:\n            case 0xE9:\n            case 0xEA:\n            case 0xEB:\n            case 0xEC:\n            case 0xED:\n            case 0xEE:\n            case 0xEF:\n            case 0xF0:\n            case 0xF1:\n            case 0xF2:\n            case 0xF3:\n            case 0xF4:\n            case 0xF5:\n            case 0xF6:\n            case 0xF7:\n            case 0xF8:\n            case 0xF9:\n            case 0xFA:\n            case 0xFB:\n            case 0xFC:\n            case 0xFD:\n            case 0xFE:\n            case 0xFF:\n                return sax->number_integer(static_cast<std::int8_t>(current));\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                        exception_message(input_format_t::msgpack, concat(\"invalid byte: 0x\", last_token), \"value\"), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_msgpack_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            {\n                return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0xD9: // str 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDA: // str 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDB: // str 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,\n                                        exception_message(input_format_t::msgpack, concat(\"expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x\", last_token), \"string\"), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into a byte array.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_msgpack_binary(binary_t& result)\n    {\n        // helper function to set the subtype\n        auto assign_and_return_true = [&result](std::int8_t subtype)\n        {\n            result.set_subtype(static_cast<std::uint8_t>(subtype));\n            return true;\n        };\n\n        switch (current)\n        {\n            case 0xC4: // bin 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC5: // bin 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC6: // bin 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC7: // ext 8\n            {\n                std::uint8_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC8: // ext 16\n            {\n                std::uint16_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC9: // ext 32\n            {\n                std::uint32_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD4: // fixext 1\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 1, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD5: // fixext 2\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 2, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD6: // fixext 4\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 4, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD7: // fixext 8\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 8, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD8: // fixext 16\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 16, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            default:           // LCOV_EXCL_LINE\n                return false;  // LCOV_EXCL_LINE\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array\n    @return whether array creation completed\n    */\n    bool get_msgpack_array(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object\n    @return whether object creation completed\n    */\n    bool get_msgpack_object(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n            key.clear();\n        }\n\n        return sax->end_object();\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether a valid UBJSON value was passed to the SAX parser\n    */\n    bool parse_ubjson_internal(const bool get_char = true)\n    {\n        return get_ubjson_value(get_char ? get_ignore_noop() : current);\n    }\n\n    /*!\n    @brief reads a UBJSON string\n\n    This function is either called after reading the 'S' byte explicitly\n    indicating a string, or in case of an object key where the 'S' byte can be\n    left out.\n\n    @param[out] result   created string\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether string creation completed\n    */\n    bool get_ubjson_string(string_t& result, const bool get_char = true)\n    {\n        if (get_char)\n        {\n            get();  // TODO(niels): may we ignore N here?\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"value\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            case 'U':\n            {\n                std::uint8_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'i':\n            {\n                std::int8_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'I':\n            {\n                std::int16_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'l':\n            {\n                std::int32_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'L':\n            {\n                std::int64_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'u':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint16_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'm':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint32_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'M':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint64_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            default:\n                break;\n        }\n        auto last_token = get_token_string();\n        std::string message;\n\n        if (input_format != input_format_t::bjdata)\n        {\n            message = \"expected length type specification (U, i, I, l, L); last byte: 0x\" + last_token;\n        }\n        else\n        {\n            message = \"expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x\" + last_token;\n        }\n        return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, \"string\"), nullptr));\n    }\n\n    /*!\n    @param[out] dim  an integer vector storing the ND array dimensions\n    @return whether reading ND array size vector is successful\n    */\n    bool get_ubjson_ndarray_size(std::vector<size_t>& dim)\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        size_t dimlen = 0;\n        bool no_ndarray = true;\n\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type, no_ndarray)))\n        {\n            return false;\n        }\n\n        if (size_and_type.first != npos)\n        {\n            if (size_and_type.second != 0)\n            {\n                if (size_and_type.second != 'N')\n                {\n                    for (std::size_t i = 0; i < size_and_type.first; ++i)\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second)))\n                        {\n                            return false;\n                        }\n                        dim.push_back(dimlen);\n                    }\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray)))\n                    {\n                        return false;\n                    }\n                    dim.push_back(dimlen);\n                }\n            }\n        }\n        else\n        {\n            while (current != ']')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, current)))\n                {\n                    return false;\n                }\n                dim.push_back(dimlen);\n                get_ignore_noop();\n            }\n        }\n        return true;\n    }\n\n    /*!\n    @param[out] result  determined size\n    @param[in,out] is_ndarray  for input, `true` means already inside an ndarray vector\n                               or ndarray dimension is not allowed; `false` means ndarray\n                               is allowed; for output, `true` means an ndarray is found;\n                               is_ndarray can only return `true` when its initial value\n                               is `false`\n    @param[in] prefix  type marker if already read, otherwise set to 0\n\n    @return whether size determination completed\n    */\n    bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)\n    {\n        if (prefix == 0)\n        {\n            prefix = get_ignore_noop();\n        }\n\n        switch (prefix)\n        {\n            case 'U':\n            {\n                std::uint8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                if (number < 0)\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,\n                                            exception_message(input_format, \"count in an optimized container must be positive\", \"size\"), nullptr));\n                }\n                result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char\n                return true;\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                if (number < 0)\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,\n                                            exception_message(input_format, \"count in an optimized container must be positive\", \"size\"), nullptr));\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                if (number < 0)\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,\n                                            exception_message(input_format, \"count in an optimized container must be positive\", \"size\"), nullptr));\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                if (number < 0)\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,\n                                            exception_message(input_format, \"count in an optimized container must be positive\", \"size\"), nullptr));\n                }\n                if (!value_in_range_of<std::size_t>(number))\n                {\n                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,\n                                            exception_message(input_format, \"integer value overflow\", \"size\"), nullptr));\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'u':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint16_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'm':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint32_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                result = conditional_static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'M':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint64_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                if (!value_in_range_of<std::size_t>(number))\n                {\n                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,\n                                            exception_message(input_format, \"integer value overflow\", \"size\"), nullptr));\n                }\n                result = detail::conditional_static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case '[':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                if (is_ndarray) // ndarray dimensional vector can only contain integers and cannot embed another array\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, \"ndarray dimensional vector is not allowed\", \"size\"), nullptr));\n                }\n                std::vector<size_t> dim;\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))\n                {\n                    return false;\n                }\n                if (dim.size() == 1 || (dim.size() == 2 && dim.at(0) == 1)) // return normal array size if 1D row vector\n                {\n                    result = dim.at(dim.size() - 1);\n                    return true;\n                }\n                if (!dim.empty())  // if ndarray, convert to an object in JData annotated array format\n                {\n                    for (auto i : dim) // test if any dimension in an ndarray is 0, if so, return a 1D empty container\n                    {\n                        if ( i == 0 )\n                        {\n                            result = 0;\n                            return true;\n                        }\n                    }\n\n                    string_t key = \"_ArraySize_\";\n                    if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))\n                    {\n                        return false;\n                    }\n                    result = 1;\n                    for (auto i : dim)\n                    {\n                        // Pre-multiplication overflow check: if i > 0 and result > SIZE_MAX/i, then result*i would overflow.\n                        // This check must happen before multiplication since overflow detection after the fact is unreliable\n                        // as modular arithmetic can produce any value, not just 0 or SIZE_MAX.\n                        if (JSON_HEDLEY_UNLIKELY(i > 0 && result > (std::numeric_limits<std::size_t>::max)() / i))\n                        {\n                            return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, \"excessive ndarray size caused overflow\", \"size\"), nullptr));\n                        }\n                        result *= i;\n                        // Additional post-multiplication check to catch any edge cases the pre-check might miss\n                        if (result == 0 || result == npos)\n                        {\n                            return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, \"excessive ndarray size caused overflow\", \"size\"), nullptr));\n                        }\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))\n                        {\n                            return false;\n                        }\n                    }\n                    is_ndarray = true;\n                    return sax->end_array();\n                }\n                result = 0;\n                return true;\n            }\n\n            default:\n                break;\n        }\n        auto last_token = get_token_string();\n        std::string message;\n\n        if (input_format != input_format_t::bjdata)\n        {\n            message = \"expected length type specification (U, i, I, l, L) after '#'; last byte: 0x\" + last_token;\n        }\n        else\n        {\n            message = \"expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x\" + last_token;\n        }\n        return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, \"size\"), nullptr));\n    }\n\n    /*!\n    @brief determine the type and size for a container\n\n    In the optimized UBJSON format, a type and a size can be provided to allow\n    for a more compact representation.\n\n    @param[out] result  pair of the size and the type\n    @param[in] inside_ndarray  whether the parser is parsing an ND array dimensional vector\n\n    @return whether pair creation completed\n    */\n    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result, bool inside_ndarray = false)\n    {\n        result.first = npos; // size\n        result.second = 0; // type\n        bool is_ndarray = false;\n\n        get_ignore_noop();\n\n        if (current == '$')\n        {\n            result.second = get();  // must not ignore 'N', because 'N' maybe the type\n            if (input_format == input_format_t::bjdata\n                    && JSON_HEDLEY_UNLIKELY(std::binary_search(bjd_optimized_type_markers.begin(), bjd_optimized_type_markers.end(), result.second)))\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                        exception_message(input_format, concat(\"marker 0x\", last_token, \" is not a permitted optimized array type\"), \"type\"), nullptr));\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"type\")))\n            {\n                return false;\n            }\n\n            get_ignore_noop();\n            if (JSON_HEDLEY_UNLIKELY(current != '#'))\n            {\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"value\")))\n                {\n                    return false;\n                }\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                        exception_message(input_format, concat(\"expected '#' after type information; last byte: 0x\", last_token), \"size\"), nullptr));\n            }\n\n            const bool is_error = get_ubjson_size_value(result.first, is_ndarray);\n            if (input_format == input_format_t::bjdata && is_ndarray)\n            {\n                if (inside_ndarray)\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,\n                                            exception_message(input_format, \"ndarray can not be recursive\", \"size\"), nullptr));\n                }\n                result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters\n            }\n            return is_error;\n        }\n\n        if (current == '#')\n        {\n            const bool is_error = get_ubjson_size_value(result.first, is_ndarray);\n            if (input_format == input_format_t::bjdata && is_ndarray)\n            {\n                return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,\n                                        exception_message(input_format, \"ndarray requires both type and size\", \"size\"), nullptr));\n            }\n            return is_error;\n        }\n\n        return true;\n    }\n\n    /*!\n    @param prefix  the previously read or set type prefix\n    @return whether value creation completed\n    */\n    bool get_ubjson_value(const char_int_type prefix)\n    {\n        switch (prefix)\n        {\n            case char_traits<char_type>::eof():  // EOF\n                return unexpect_eof(input_format, \"value\");\n\n            case 'T':  // true\n                return sax->boolean(true);\n            case 'F':  // false\n                return sax->boolean(false);\n\n            case 'Z':  // null\n                return sax->null();\n\n            case 'B':  // byte\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint8_t number{};\n                return get_number(input_format, number) && sax->number_unsigned(number);\n            }\n\n            case 'U':\n            {\n                std::uint8_t number{};\n                return get_number(input_format, number) && sax->number_unsigned(number);\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                return get_number(input_format, number) && sax->number_integer(number);\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                return get_number(input_format, number) && sax->number_integer(number);\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                return get_number(input_format, number) && sax->number_integer(number);\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                return get_number(input_format, number) && sax->number_integer(number);\n            }\n\n            case 'u':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint16_t number{};\n                return get_number(input_format, number) && sax->number_unsigned(number);\n            }\n\n            case 'm':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint32_t number{};\n                return get_number(input_format, number) && sax->number_unsigned(number);\n            }\n\n            case 'M':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint64_t number{};\n                return get_number(input_format, number) && sax->number_unsigned(number);\n            }\n\n            case 'h':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                const auto byte1_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"number\")))\n                {\n                    return false;\n                }\n                const auto byte2_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"number\")))\n                {\n                    return false;\n                }\n\n                const auto byte1 = static_cast<unsigned char>(byte1_raw);\n                const auto byte2 = static_cast<unsigned char>(byte2_raw);\n\n                // Code from RFC 7049, Appendix D, Figure 3:\n                // As half-precision floating-point numbers were only added\n                // to IEEE 754 in 2008, today's programming platforms often\n                // still only have limited support for them. It is very\n                // easy to include at least decoding support for them even\n                // without such support. An example of a small decoder for\n                // half-precision floating-point numbers in the C language\n                // is shown in Fig. 3.\n                const auto half = static_cast<unsigned int>((byte2 << 8u) + byte1);\n                const double val = [&half]\n                {\n                    const int exp = (half >> 10u) & 0x1Fu;\n                    const unsigned int mant = half & 0x3FFu;\n                    JSON_ASSERT(0 <= exp&& exp <= 32);\n                    JSON_ASSERT(mant <= 1024);\n                    switch (exp)\n                    {\n                        case 0:\n                            return std::ldexp(mant, -24);\n                        case 31:\n                            return (mant == 0)\n                            ? std::numeric_limits<double>::infinity()\n                            : std::numeric_limits<double>::quiet_NaN();\n                        default:\n                            return std::ldexp(mant + 1024, exp - 25);\n                    }\n                }();\n                return sax->number_float((half & 0x8000u) != 0\n                                         ? static_cast<number_float_t>(-val)\n                                         : static_cast<number_float_t>(val), \"\");\n            }\n\n            case 'd':\n            {\n                float number{};\n                return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'D':\n            {\n                double number{};\n                return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'H':\n            {\n                return get_ubjson_high_precision_number();\n            }\n\n            case 'C':  // char\n            {\n                get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"char\")))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(current > 127))\n                {\n                    auto last_token = get_token_string();\n                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,\n                                            exception_message(input_format, concat(\"byte after 'C' must be in range 0x00..0x7F; last byte: 0x\", last_token), \"char\"), nullptr));\n                }\n                string_t s(1, static_cast<typename string_t::value_type>(current));\n                return sax->string(s);\n            }\n\n            case 'S':  // string\n            {\n                string_t s;\n                return get_ubjson_string(s) && sax->string(s);\n            }\n\n            case '[':  // array\n                return get_ubjson_array();\n\n            case '{':  // object\n                return get_ubjson_object();\n\n            default: // anything else\n                break;\n        }\n        auto last_token = get_token_string();\n        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format, \"invalid byte: 0x\" + last_token, \"value\"), nullptr));\n    }\n\n    /*!\n    @return whether array creation completed\n    */\n    bool get_ubjson_array()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        // if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):\n        // {\"_ArrayType_\" : \"typeid\", \"_ArraySize_\" : [n1, n2, ...], \"_ArrayData_\" : [v1, v2, ...]}\n\n        if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)\n        {\n            size_and_type.second &= ~(static_cast<char_int_type>(1) << 8);  // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker\n            auto it = std::lower_bound(bjd_types_map.begin(), bjd_types_map.end(), size_and_type.second, [](const bjd_type & p, char_int_type t)\n            {\n                return p.first < t;\n            });\n            string_t key = \"_ArrayType_\";\n            if (JSON_HEDLEY_UNLIKELY(it == bjd_types_map.end() || it->first != size_and_type.second))\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                        exception_message(input_format, \"invalid byte: 0x\" + last_token, \"type\"), nullptr));\n            }\n\n            string_t type = it->second; // sax->string() takes a reference\n            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second == 'C' || size_and_type.second == 'B')\n            {\n                size_and_type.second = 'U';\n            }\n\n            key = \"_ArrayData_\";\n            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))\n            {\n                return false;\n            }\n\n            for (std::size_t i = 0; i < size_and_type.first; ++i)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                {\n                    return false;\n                }\n            }\n\n            return (sax->end_array() && sax->end_object());\n        }\n\n        // If BJData type marker is 'B' decode as binary\n        if (input_format == input_format_t::bjdata && size_and_type.first != npos && size_and_type.second == 'B')\n        {\n            binary_t result;\n            return get_binary(input_format, size_and_type.first, result) && sax->binary(result);\n        }\n\n        if (size_and_type.first != npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                if (size_and_type.second != 'N')\n                {\n                    for (std::size_t i = 0; i < size_and_type.first; ++i)\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                        {\n                            return false;\n                        }\n                    }\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))\n            {\n                return false;\n            }\n\n            while (current != ']')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @return whether object creation completed\n    */\n    bool get_ubjson_object()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        // do not accept ND-array size in objects in BJData\n        if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                    exception_message(input_format, \"BJData object does not support ND-array size in optimized format\", \"object\"), nullptr));\n        }\n\n        string_t key;\n        if (size_and_type.first != npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))\n            {\n                return false;\n            }\n\n            while (current != '}')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    // Note, no reader for UBJSON binary types is implemented because they do\n    // not exist\n\n    bool get_ubjson_high_precision_number()\n    {\n        // get the size of the following number string\n        std::size_t size{};\n        bool no_ndarray = true;\n        auto res = get_ubjson_size_value(size, no_ndarray);\n        if (JSON_HEDLEY_UNLIKELY(!res))\n        {\n            return res;\n        }\n\n        // get number string\n        std::vector<char> number_vector;\n        for (std::size_t i = 0; i < size; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"number\")))\n            {\n                return false;\n            }\n            number_vector.push_back(static_cast<char>(current));\n        }\n\n        // parse number string\n        using ia_type = decltype(detail::input_adapter(number_vector));\n        auto number_lexer = detail::lexer<BasicJsonType, ia_type>(detail::input_adapter(number_vector), false);\n        const auto result_number = number_lexer.scan();\n        const auto number_string = number_lexer.get_token_string();\n        const auto result_remainder = number_lexer.scan();\n\n        using token_type = typename detail::lexer_base<BasicJsonType>::token_type;\n\n        if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))\n        {\n            return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,\n                                    exception_message(input_format, concat(\"invalid number text: \", number_lexer.get_token_string()), \"high-precision number\"), nullptr));\n        }\n\n        switch (result_number)\n        {\n            case token_type::value_integer:\n                return sax->number_integer(number_lexer.get_number_integer());\n            case token_type::value_unsigned:\n                return sax->number_unsigned(number_lexer.get_number_unsigned());\n            case token_type::value_float:\n                return sax->number_float(number_lexer.get_number_float(), std::move(number_string));\n            case token_type::uninitialized:\n            case token_type::literal_true:\n            case token_type::literal_false:\n            case token_type::literal_null:\n            case token_type::value_string:\n            case token_type::begin_array:\n            case token_type::begin_object:\n            case token_type::end_array:\n            case token_type::end_object:\n            case token_type::name_separator:\n            case token_type::value_separator:\n            case token_type::parse_error:\n            case token_type::end_of_input:\n            case token_type::literal_or_value:\n            default:\n                return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,\n                                        exception_message(input_format, concat(\"invalid number text: \", number_lexer.get_token_string()), \"high-precision number\"), nullptr));\n        }\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*!\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a -'ve valued\n    `char_traits<char_type>::eof()` in that case.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++chars_read;\n        return current = ia.get_character();\n    }\n\n    /*!\n    @brief get_to read into a primitive type\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns false instead\n\n    @return bool, whether the read was successful\n    */\n    template<class T>\n    bool get_to(T& dest, const input_format_t format, const char* context)\n    {\n        auto new_chars_read = ia.get_elements(&dest);\n        chars_read += new_chars_read;\n        if (JSON_HEDLEY_UNLIKELY(new_chars_read < sizeof(T)))\n        {\n            // in case of failure, advance position by 1 to report the failing location\n            ++chars_read;\n            sax->parse_error(chars_read, \"<end of file>\", parse_error::create(110, chars_read, exception_message(format, \"unexpected end of input\", context), nullptr));\n            return false;\n        }\n        return true;\n    }\n\n    /*!\n    @return character read from the input after ignoring all 'N' entries\n    */\n    char_int_type get_ignore_noop()\n    {\n        do\n        {\n            get();\n        }\n        while (current == 'N');\n\n        return current;\n    }\n\n    template<class NumberType>\n    static void byte_swap(NumberType& number)\n    {\n        constexpr std::size_t sz = sizeof(number);\n#ifdef __cpp_lib_byteswap\n        if constexpr (sz == 1)\n        {\n            return;\n        }\n        else if constexpr(std::is_integral_v<NumberType>)\n        {\n            number = std::byteswap(number);\n            return;\n        }\n        else\n        {\n#endif\n            auto* ptr = reinterpret_cast<std::uint8_t*>(&number);\n            for (std::size_t i = 0; i < sz / 2; ++i)\n            {\n                std::swap(ptr[i], ptr[sz - i - 1]);\n            }\n#ifdef __cpp_lib_byteswap\n        }\n#endif\n    }\n\n    /*\n    @brief read a number from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format   the current format (for diagnostics)\n    @param[out] result  number of type @a NumberType\n\n    @return whether conversion completed\n\n    @note This function needs to respect the system's endianness, because\n          bytes in CBOR, MessagePack, and UBJSON are stored in network order\n          (big endian) and therefore need reordering on little endian systems.\n          On the other hand, BSON and BJData use little endian and should reorder\n          on big endian systems.\n    */\n    template<typename NumberType, bool InputIsLittleEndian = false>\n    bool get_number(const input_format_t format, NumberType& result)\n    {\n        // read in the original format\n\n        if (JSON_HEDLEY_UNLIKELY(!get_to(result, format, \"number\")))\n        {\n            return false;\n        }\n        if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata))\n        {\n            byte_swap(result);\n        }\n        return true;\n    }\n\n    /*!\n    @brief create a string by reading characters from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of characters to read\n    @param[out] result string created by reading @a len bytes\n\n    @return whether string creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of string memory.\n    */\n    template<typename NumberType>\n    bool get_string(const input_format_t format,\n                    const NumberType len,\n                    string_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"string\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<typename string_t::value_type>(current));\n        }\n        return success;\n    }\n\n    /*!\n    @brief create a byte array by reading bytes from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of bytes to read\n    @param[out] result byte array created by reading @a len bytes\n\n    @return whether byte array creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of memory.\n    */\n    template<typename NumberType>\n    bool get_binary(const input_format_t format,\n                    const NumberType len,\n                    binary_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"binary\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<std::uint8_t>(current));\n        }\n        return success;\n    }\n\n    /*!\n    @param[in] format   the current format (for diagnostics)\n    @param[in] context  further context information (for diagnostics)\n    @return whether the last read character is not EOF\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool unexpect_eof(const input_format_t format, const char* context) const\n    {\n        if (JSON_HEDLEY_UNLIKELY(current == char_traits<char_type>::eof()))\n        {\n            return sax->parse_error(chars_read, \"<end of file>\",\n                                    parse_error::create(110, chars_read, exception_message(format, \"unexpected end of input\", context), nullptr));\n        }\n        return true;\n    }\n\n    /*!\n    @return a string representation of the last read byte\n    */\n    std::string get_token_string() const\n    {\n        std::array<char, 3> cr{{}};\n        static_cast<void>((std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        return std::string{cr.data()};\n    }\n\n    /*!\n    @param[in] format   the current format\n    @param[in] detail   a detailed error message\n    @param[in] context  further context information\n    @return a message string to use in the parse_error exceptions\n    */\n    std::string exception_message(const input_format_t format,\n                                  const std::string& detail,\n                                  const std::string& context) const\n    {\n        std::string error_msg = \"syntax error while parsing \";\n\n        switch (format)\n        {\n            case input_format_t::cbor:\n                error_msg += \"CBOR\";\n                break;\n\n            case input_format_t::msgpack:\n                error_msg += \"MessagePack\";\n                break;\n\n            case input_format_t::ubjson:\n                error_msg += \"UBJSON\";\n                break;\n\n            case input_format_t::bson:\n                error_msg += \"BSON\";\n                break;\n\n            case input_format_t::bjdata:\n                error_msg += \"BJData\";\n                break;\n\n            case input_format_t::json: // LCOV_EXCL_LINE\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\n        return concat(error_msg, ' ', context, \": \", detail);\n    }\n\n  private:\n    static JSON_INLINE_VARIABLE constexpr std::size_t npos = detail::unknown_size();\n\n    /// input adapter\n    InputAdapterType ia;\n\n    /// the current character\n    char_int_type current = char_traits<char_type>::eof();\n\n    /// the number of characters read\n    std::size_t chars_read = 0;\n\n    /// whether we can assume little endianness\n    const bool is_little_endian = little_endianness();\n\n    /// input format\n    const input_format_t input_format = input_format_t::json;\n\n    /// the SAX parser\n    json_sax_t* sax = nullptr;\n\n    // excluded markers in bjdata optimized type\n#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \\\n    make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')\n\n#define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \\\n    make_array<bjd_type>(                      \\\n    bjd_type{'B', \"byte\"},                     \\\n    bjd_type{'C', \"char\"},                     \\\n    bjd_type{'D', \"double\"},                   \\\n    bjd_type{'I', \"int16\"},                    \\\n    bjd_type{'L', \"int64\"},                    \\\n    bjd_type{'M', \"uint64\"},                   \\\n    bjd_type{'U', \"uint8\"},                    \\\n    bjd_type{'d', \"single\"},                   \\\n    bjd_type{'i', \"int8\"},                     \\\n    bjd_type{'l', \"int32\"},                    \\\n    bjd_type{'m', \"uint32\"},                   \\\n    bjd_type{'u', \"uint16\"})\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    // lookup tables\n    // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)\n    const decltype(JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_) bjd_optimized_type_markers =\n        JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_;\n\n    using bjd_type = std::pair<char_int_type, string_t>;\n    // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)\n    const decltype(JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_) bjd_types_map =\n        JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_;\n\n#undef JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_\n#undef JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_\n};\n\n#ifndef JSON_HAS_CPP_17\n    template<typename BasicJsonType, typename InputAdapterType, typename SAX>\n    constexpr std::size_t binary_reader<BasicJsonType, InputAdapterType, SAX>::npos;\n#endif\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/input/input_adapters.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <cstring> // strlen\n#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next\n#include <memory> // shared_ptr, make_shared, addressof\n#include <numeric> // accumulate\n#include <string> // string, char_traits\n#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer\n#include <utility> // pair, declval\n\n#ifndef JSON_NO_IO\n    #include <cstdio>   // FILE *\n    #include <istream>  // istream\n#endif                  // JSON_NO_IO\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/iterators/iterator_traits.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/// the supported input formats\nenum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };\n\n////////////////////\n// input adapters //\n////////////////////\n\n#ifndef JSON_NO_IO\n/*!\nInput adapter for stdio file access. This adapter read only 1 byte and do not use any\n buffer. This adapter is a very low level adapter.\n*/\nclass file_input_adapter\n{\n  public:\n    using char_type = char;\n\n    JSON_HEDLEY_NON_NULL(2)\n    explicit file_input_adapter(std::FILE* f) noexcept\n        : m_file(f)\n    {\n        JSON_ASSERT(m_file != nullptr);\n    }\n\n    // make class move-only\n    file_input_adapter(const file_input_adapter&) = delete;\n    file_input_adapter(file_input_adapter&&) noexcept = default;\n    file_input_adapter& operator=(const file_input_adapter&) = delete;\n    file_input_adapter& operator=(file_input_adapter&&) = delete;\n    ~file_input_adapter() = default;\n\n    std::char_traits<char>::int_type get_character() noexcept\n    {\n        return std::fgetc(m_file);\n    }\n\n    // returns the number of characters successfully read\n    template<class T>\n    std::size_t get_elements(T* dest, std::size_t count = 1)\n    {\n        return fread(dest, 1, sizeof(T) * count, m_file);\n    }\n\n  private:\n    /// the file pointer to read from\n    std::FILE* m_file;\n};\n\n/*!\nInput adapter for a (caching) istream. Ignores a UFT Byte Order Mark at\nbeginning of input. Does not support changing the underlying std::streambuf\nin mid-input. Maintains underlying std::istream and std::streambuf to support\nsubsequent use of standard std::istream operations to process any input\ncharacters following those used in parsing the JSON input.  Clears the\nstd::istream flags; any input errors (e.g., EOF) will be detected by the first\nsubsequent call for input from the std::istream.\n*/\nclass input_stream_adapter\n{\n  public:\n    using char_type = char;\n\n    ~input_stream_adapter()\n    {\n        // clear stream flags; we use underlying streambuf I/O, do not\n        // maintain ifstream flags, except eof\n        if (is != nullptr)\n        {\n            is->clear(is->rdstate() & std::ios::eofbit);\n        }\n    }\n\n    explicit input_stream_adapter(std::istream& i)\n        : is(&i), sb(i.rdbuf())\n    {}\n\n    // deleted because of pointer members\n    input_stream_adapter(const input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&&) = delete;\n\n    input_stream_adapter(input_stream_adapter&& rhs) noexcept\n        : is(rhs.is), sb(rhs.sb)\n    {\n        rhs.is = nullptr;\n        rhs.sb = nullptr;\n    }\n\n    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to\n    // ensure that std::char_traits<char>::eof() and the character 0xFF do not\n    // end up as the same value, e.g., 0xFFFFFFFF.\n    std::char_traits<char>::int_type get_character()\n    {\n        auto res = sb->sbumpc();\n        // set eof manually, as we don't use the istream interface.\n        if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))\n        {\n            is->clear(is->rdstate() | std::ios::eofbit);\n        }\n        return res;\n    }\n\n    template<class T>\n    std::size_t get_elements(T* dest, std::size_t count = 1)\n    {\n        auto res = static_cast<std::size_t>(sb->sgetn(reinterpret_cast<char*>(dest), static_cast<std::streamsize>(count * sizeof(T))));\n        if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T)))\n        {\n            is->clear(is->rdstate() | std::ios::eofbit);\n        }\n        return res;\n    }\n\n  private:\n    /// the associated input stream\n    std::istream* is = nullptr;\n    std::streambuf* sb = nullptr;\n};\n#endif  // JSON_NO_IO\n\n// General-purpose iterator-based adapter. It might not be as fast as\n// theoretically possible for some containers, but it is extremely versatile.\ntemplate<typename IteratorType>\nclass iterator_input_adapter\n{\n  public:\n    using char_type = typename std::iterator_traits<IteratorType>::value_type;\n\n    iterator_input_adapter(IteratorType first, IteratorType last)\n        : current(std::move(first)), end(std::move(last))\n    {}\n\n    typename char_traits<char_type>::int_type get_character()\n    {\n        if (JSON_HEDLEY_LIKELY(current != end))\n        {\n            auto result = char_traits<char_type>::to_int_type(*current);\n            std::advance(current, 1);\n            return result;\n        }\n\n        return char_traits<char_type>::eof();\n    }\n\n    // for general iterators, we cannot really do something better than falling back to processing the range one-by-one\n    template<class T>\n    std::size_t get_elements(T* dest, std::size_t count = 1)\n    {\n        auto* ptr = reinterpret_cast<char*>(dest);\n        for (std::size_t read_index = 0; read_index < count * sizeof(T); ++read_index)\n        {\n            if (JSON_HEDLEY_LIKELY(current != end))\n            {\n                ptr[read_index] = static_cast<char>(*current);\n                std::advance(current, 1);\n            }\n            else\n            {\n                return read_index;\n            }\n        }\n        return count * sizeof(T);\n    }\n\n  private:\n    IteratorType current;\n    IteratorType end;\n\n    template<typename BaseInputAdapter, size_t T>\n    friend struct wide_string_input_helper;\n\n    bool empty() const\n    {\n        return current == end;\n    }\n};\n\ntemplate<typename BaseInputAdapter, size_t T>\nstruct wide_string_input_helper;\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 4>\n{\n    // UTF-32\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-32 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (wc <= 0xFFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else if (wc <= 0x10FFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 4;\n            }\n            else\n            {\n                // unknown character\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n        }\n    }\n};\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 2>\n{\n    // UTF-16\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-16 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (0xD800 > wc || wc >= 0xE000)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else\n            {\n                if (JSON_HEDLEY_UNLIKELY(!input.empty()))\n                {\n                    const auto wc2 = static_cast<unsigned int>(input.get_character());\n                    const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));\n                    utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));\n                    utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));\n                    utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));\n                    utf8_bytes_filled = 4;\n                }\n                else\n                {\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                    utf8_bytes_filled = 1;\n                }\n            }\n        }\n    }\n};\n\n// Wraps another input adapter to convert wide character types into individual bytes.\ntemplate<typename BaseInputAdapter, typename WideCharType>\nclass wide_string_input_adapter\n{\n  public:\n    using char_type = char;\n\n    wide_string_input_adapter(BaseInputAdapter base)\n        : base_adapter(base) {}\n\n    typename std::char_traits<char>::int_type get_character() noexcept\n    {\n        // check if the buffer needs to be filled\n        if (utf8_bytes_index == utf8_bytes_filled)\n        {\n            fill_buffer<sizeof(WideCharType)>();\n\n            JSON_ASSERT(utf8_bytes_filled > 0);\n            JSON_ASSERT(utf8_bytes_index == 0);\n        }\n\n        // use buffer\n        JSON_ASSERT(utf8_bytes_filled > 0);\n        JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);\n        return utf8_bytes[utf8_bytes_index++];\n    }\n\n    // parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here\n    template<class T>\n    std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)\n    {\n        JSON_THROW(parse_error::create(112, 1, \"wide string type cannot be interpreted as binary data\", nullptr));\n    }\n\n  private:\n    BaseInputAdapter base_adapter;\n\n    template<size_t T>\n    void fill_buffer()\n    {\n        wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);\n    }\n\n    /// a buffer for UTF-8 bytes\n    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};\n\n    /// index to the utf8_codes array for the next valid byte\n    std::size_t utf8_bytes_index = 0;\n    /// number of valid bytes in the utf8_codes array\n    std::size_t utf8_bytes_filled = 0;\n};\n\ntemplate<typename IteratorType, typename Enable = void>\nstruct iterator_input_adapter_factory\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using adapter_type = iterator_input_adapter<iterator_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(std::move(first), std::move(last));\n    }\n};\n\ntemplate<typename T>\nstruct is_iterator_of_multibyte\n{\n    using value_type = typename std::iterator_traits<T>::value_type;\n    enum\n    {\n        value = sizeof(value_type) > 1\n    };\n};\n\ntemplate<typename IteratorType>\nstruct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using base_adapter_type = iterator_input_adapter<iterator_type>;\n    using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(base_adapter_type(std::move(first), std::move(last)));\n    }\n};\n\n// General purpose iterator-based input\ntemplate<typename IteratorType>\ntypename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)\n{\n    using factory_type = iterator_input_adapter_factory<IteratorType>;\n    return factory_type::create(first, last);\n}\n\n// Convenience shorthand from container to iterator\n// Enables ADL on begin(container) and end(container)\n// Encloses the using declarations in namespace for not to leak them to outside scope\n\nnamespace container_input_adapter_factory_impl\n{\n\nusing std::begin;\nusing std::end;\n\ntemplate<typename ContainerType, typename Enable = void>\nstruct container_input_adapter_factory {};\n\ntemplate<typename ContainerType>\nstruct container_input_adapter_factory< ContainerType,\n       void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>\n       {\n           using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));\n\n           static adapter_type create(const ContainerType& container)\n{\n    return input_adapter(begin(container), end(container));\n}\n       };\n\n}  // namespace container_input_adapter_factory_impl\n\ntemplate<typename ContainerType>\ntypename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)\n{\n    return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);\n}\n\n// specialization for std::string\nusing string_input_adapter_type = decltype(input_adapter(std::declval<std::string>()));\n\n#ifndef JSON_NO_IO\n// Special cases with fast paths\ninline file_input_adapter input_adapter(std::FILE* file)\n{\n    if (file == nullptr)\n    {\n        JSON_THROW(parse_error::create(101, 0, \"attempting to parse an empty input; check that your input string or stream contains the expected JSON\", nullptr));\n    }\n    return file_input_adapter(file);\n}\n\ninline input_stream_adapter input_adapter(std::istream& stream)\n{\n    return input_stream_adapter(stream);\n}\n\ninline input_stream_adapter input_adapter(std::istream&& stream)\n{\n    return input_stream_adapter(stream);\n}\n#endif  // JSON_NO_IO\n\nusing contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));\n\n// Null-delimited strings, and the like.\ntemplate < typename CharT,\n           typename std::enable_if <\n               std::is_pointer<CharT>::value&&\n               !std::is_array<CharT>::value&&\n               std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n               sizeof(typename std::remove_pointer<CharT>::type) == 1,\n               int >::type = 0 >\ncontiguous_bytes_input_adapter input_adapter(CharT b)\n{\n    if (b == nullptr)\n    {\n        JSON_THROW(parse_error::create(101, 0, \"attempting to parse an empty input; check that your input string or stream contains the expected JSON\", nullptr));\n    }\n    auto length = std::strlen(reinterpret_cast<const char*>(b));\n    const auto* ptr = reinterpret_cast<const char*>(b);\n    return input_adapter(ptr, ptr + length); // cppcheck-suppress[nullPointerArithmeticRedundantCheck]\n}\n\ntemplate<typename T, std::size_t N>\nauto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n{\n    return input_adapter(array, array + N);\n}\n\n// This class only handles inputs of input_buffer_adapter type.\n// It's required so that expressions like {ptr, len} can be implicitly cast\n// to the correct adapter.\nclass span_input_adapter\n{\n  public:\n    template < typename CharT,\n               typename std::enable_if <\n                   std::is_pointer<CharT>::value&&\n                   std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n                   sizeof(typename std::remove_pointer<CharT>::type) == 1,\n                   int >::type = 0 >\n    span_input_adapter(CharT b, std::size_t l)\n        : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}\n\n    template<class IteratorType,\n             typename std::enable_if<\n                 std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,\n                 int>::type = 0>\n    span_input_adapter(IteratorType first, IteratorType last)\n        : ia(input_adapter(first, last)) {}\n\n    contiguous_bytes_input_adapter&& get()\n    {\n        return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)\n    }\n\n  private:\n    contiguous_bytes_input_adapter ia;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/input/json_sax.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstddef>\n#include <string> // string\n#include <type_traits> // enable_if_t\n#include <utility> // move\n#include <vector> // vector\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/input/lexer.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/string_concat.hpp>\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/*!\n@brief SAX interface\n\nThis class describes the SAX interface used by @ref nlohmann::json::sax_parse.\nEach function is called in different situations while the input is parsed. The\nboolean return value informs the parser whether to continue processing the\ninput.\n*/\ntemplate<typename BasicJsonType>\nstruct json_sax\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    /*!\n    @brief a null value was read\n    @return whether parsing should proceed\n    */\n    virtual bool null() = 0;\n\n    /*!\n    @brief a boolean value was read\n    @param[in] val  boolean value\n    @return whether parsing should proceed\n    */\n    virtual bool boolean(bool val) = 0;\n\n    /*!\n    @brief an integer number was read\n    @param[in] val  integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_integer(number_integer_t val) = 0;\n\n    /*!\n    @brief an unsigned integer number was read\n    @param[in] val  unsigned integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_unsigned(number_unsigned_t val) = 0;\n\n    /*!\n    @brief a floating-point number was read\n    @param[in] val  floating-point value\n    @param[in] s    raw token value\n    @return whether parsing should proceed\n    */\n    virtual bool number_float(number_float_t val, const string_t& s) = 0;\n\n    /*!\n    @brief a string value was read\n    @param[in] val  string value\n    @return whether parsing should proceed\n    @note It is safe to move the passed string value.\n    */\n    virtual bool string(string_t& val) = 0;\n\n    /*!\n    @brief a binary value was read\n    @param[in] val  binary value\n    @return whether parsing should proceed\n    @note It is safe to move the passed binary value.\n    */\n    virtual bool binary(binary_t& val) = 0;\n\n    /*!\n    @brief the beginning of an object was read\n    @param[in] elements  number of object elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_object(std::size_t elements) = 0;\n\n    /*!\n    @brief an object key was read\n    @param[in] val  object key\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool key(string_t& val) = 0;\n\n    /*!\n    @brief the end of an object was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_object() = 0;\n\n    /*!\n    @brief the beginning of an array was read\n    @param[in] elements  number of array elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_array(std::size_t elements) = 0;\n\n    /*!\n    @brief the end of an array was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_array() = 0;\n\n    /*!\n    @brief a parse error occurred\n    @param[in] position    the position in the input where the error occurs\n    @param[in] last_token  the last read token\n    @param[in] ex          an exception object describing the error\n    @return whether parsing should proceed (must return false)\n    */\n    virtual bool parse_error(std::size_t position,\n                             const std::string& last_token,\n                             const detail::exception& ex) = 0;\n\n    json_sax() = default;\n    json_sax(const json_sax&) = default;\n    json_sax(json_sax&&) noexcept = default;\n    json_sax& operator=(const json_sax&) = default;\n    json_sax& operator=(json_sax&&) noexcept = default;\n    virtual ~json_sax() = default;\n};\n\nnamespace detail\n{\nconstexpr std::size_t unknown_size()\n{\n    return (std::numeric_limits<std::size_t>::max)();\n}\n\n/*!\n@brief SAX implementation to create a JSON value from SAX events\n\nThis class implements the @ref json_sax interface and processes the SAX events\nto create a JSON value which makes it basically a DOM parser. The structure or\nhierarchy of the JSON value is managed by the stack `ref_stack` which contains\na pointer to the respective array or object for each recursion depth.\n\nAfter successful parsing, the value that is passed by reference to the\nconstructor contains the parsed value.\n\n@tparam BasicJsonType  the JSON type\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass json_sax_dom_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using lexer_t = lexer<BasicJsonType, InputAdapterType>;\n\n    /*!\n    @param[in,out] r  reference to a JSON value that is manipulated while\n                       parsing\n    @param[in] allow_exceptions_  whether parse errors yield exceptions\n    */\n    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr)\n        : root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_)\n    {}\n\n    // make class move-only\n    json_sax_dom_parser(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~json_sax_dom_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));\n\n#if JSON_DIAGNOSTIC_POSITIONS\n        // Manually set the start position of the object here.\n        // Ensure this is after the call to handle_value to ensure correct start position.\n        if (m_lexer_ref)\n        {\n            // Lexer has read the first character of the object, so\n            // subtract 1 from the position to get the correct start position.\n            ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;\n        }\n#endif\n\n        if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, concat(\"excessive object size: \", std::to_string(len)), ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(ref_stack.back()->is_object());\n\n        // add null at the given key and store the reference for later\n        object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val));\n        return true;\n    }\n\n    bool end_object()\n    {\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(ref_stack.back()->is_object());\n\n#if JSON_DIAGNOSTIC_POSITIONS\n        if (m_lexer_ref)\n        {\n            // Lexer's position is past the closing brace, so set that as the end position.\n            ref_stack.back()->end_position = m_lexer_ref->get_position();\n        }\n#endif\n\n        ref_stack.back()->set_parents();\n        ref_stack.pop_back();\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));\n\n#if JSON_DIAGNOSTIC_POSITIONS\n        // Manually set the start position of the array here.\n        // Ensure this is after the call to handle_value to ensure correct start position.\n        if (m_lexer_ref)\n        {\n            ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;\n        }\n#endif\n\n        if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, concat(\"excessive array size: \", std::to_string(len)), ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(ref_stack.back()->is_array());\n\n#if JSON_DIAGNOSTIC_POSITIONS\n        if (m_lexer_ref)\n        {\n            // Lexer's position is past the closing bracket, so set that as the end position.\n            ref_stack.back()->end_position = m_lexer_ref->get_position();\n        }\n#endif\n\n        ref_stack.back()->set_parents();\n        ref_stack.pop_back();\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n\n#if JSON_DIAGNOSTIC_POSITIONS\n    void handle_diagnostic_positions_for_json_value(BasicJsonType& v)\n    {\n        if (m_lexer_ref)\n        {\n            // Lexer has read past the current field value, so set the end position to the current position.\n            // The start position will be set below based on the length of the string representation\n            // of the value.\n            v.end_position = m_lexer_ref->get_position();\n\n            switch (v.type())\n            {\n                case value_t::boolean:\n                {\n                    // 4 and 5 are the string length of \"true\" and \"false\"\n                    v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5);\n                    break;\n                }\n\n                case value_t::null:\n                {\n                    // 4 is the string length of \"null\"\n                    v.start_position = v.end_position - 4;\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    // include the length of the quotes, which is 2\n                    v.start_position = v.end_position - v.m_data.m_value.string->size() - 2;\n                    break;\n                }\n\n                // As we handle the start and end positions for values created during parsing,\n                // we do not expect the following value type to be called. Regardless, set the positions\n                // in case this is created manually or through a different constructor. Exclude from lcov\n                // since the exact condition of this switch is esoteric.\n                // LCOV_EXCL_START\n                case value_t::discarded:\n                {\n                    v.end_position = std::string::npos;\n                    v.start_position = v.end_position;\n                    break;\n                }\n                // LCOV_EXCL_STOP\n                case value_t::binary:\n                case value_t::number_integer:\n                case value_t::number_unsigned:\n                case value_t::number_float:\n                {\n                    v.start_position = v.end_position - m_lexer_ref->get_string().size();\n                    break;\n                }\n                case value_t::object:\n                case value_t::array:\n                {\n                    // object and array are handled in start_object() and start_array() handlers\n                    // skip setting the values here.\n                    break;\n                }\n                default: // LCOV_EXCL_LINE\n                    // Handle all possible types discretely, default handler should never be reached.\n                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE\n            }\n        }\n    }\n#endif\n\n    /*!\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n    */\n    template<typename Value>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    BasicJsonType* handle_value(Value&& v)\n    {\n        if (ref_stack.empty())\n        {\n            root = BasicJsonType(std::forward<Value>(v));\n\n#if JSON_DIAGNOSTIC_POSITIONS\n            handle_diagnostic_positions_for_json_value(root);\n#endif\n\n            return &root;\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_data.m_value.array->emplace_back(std::forward<Value>(v));\n\n#if JSON_DIAGNOSTIC_POSITIONS\n            handle_diagnostic_positions_for_json_value(ref_stack.back()->m_data.m_value.array->back());\n#endif\n\n            return &(ref_stack.back()->m_data.m_value.array->back());\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_object());\n        JSON_ASSERT(object_element);\n        *object_element = BasicJsonType(std::forward<Value>(v));\n\n#if JSON_DIAGNOSTIC_POSITIONS\n        handle_diagnostic_positions_for_json_value(*object_element);\n#endif\n\n        return object_element;\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n    /// the lexer reference to obtain the current position\n    lexer_t* m_lexer_ref = nullptr;\n};\n\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass json_sax_dom_callback_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using parser_callback_t = typename BasicJsonType::parser_callback_t;\n    using parse_event_t = typename BasicJsonType::parse_event_t;\n    using lexer_t = lexer<BasicJsonType, InputAdapterType>;\n\n    json_sax_dom_callback_parser(BasicJsonType& r,\n                                 parser_callback_t cb,\n                                 const bool allow_exceptions_ = true,\n                                 lexer_t* lexer_ = nullptr)\n        : root(r), callback(std::move(cb)), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_)\n    {\n        keep_stack.push_back(true);\n    }\n\n    // make class move-only\n    json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~json_sax_dom_callback_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        // check callback for object start\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::object, true);\n        ref_stack.push_back(val.second);\n\n        if (ref_stack.back())\n        {\n\n#if JSON_DIAGNOSTIC_POSITIONS\n            // Manually set the start position of the object here.\n            // Ensure this is after the call to handle_value to ensure correct start position.\n            if (m_lexer_ref)\n            {\n                // Lexer has read the first character of the object, so\n                // subtract 1 from the position to get the correct start position.\n                ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;\n            }\n#endif\n\n            // check object limit\n            if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))\n            {\n                JSON_THROW(out_of_range::create(408, concat(\"excessive object size: \", std::to_string(len)), ref_stack.back()));\n            }\n        }\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        BasicJsonType k = BasicJsonType(val);\n\n        // check callback for the key\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);\n        key_keep_stack.push_back(keep);\n\n        // add discarded value at the given key and store the reference for later\n        if (keep && ref_stack.back())\n        {\n            object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val) = discarded);\n        }\n\n        return true;\n    }\n\n    bool end_object()\n    {\n        if (ref_stack.back())\n        {\n            if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))\n            {\n                // discard object\n                *ref_stack.back() = discarded;\n\n#if JSON_DIAGNOSTIC_POSITIONS\n                // Set start/end positions for discarded object.\n                handle_diagnostic_positions_for_json_value(*ref_stack.back());\n#endif\n            }\n            else\n            {\n\n#if JSON_DIAGNOSTIC_POSITIONS\n                if (m_lexer_ref)\n                {\n                    // Lexer's position is past the closing brace, so set that as the end position.\n                    ref_stack.back()->end_position = m_lexer_ref->get_position();\n                }\n#endif\n\n                ref_stack.back()->set_parents();\n            }\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())\n        {\n            // remove discarded value\n            for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)\n            {\n                if (it->is_discarded())\n                {\n                    ref_stack.back()->erase(it);\n                    break;\n                }\n            }\n        }\n\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::array, true);\n        ref_stack.push_back(val.second);\n\n        if (ref_stack.back())\n        {\n\n#if JSON_DIAGNOSTIC_POSITIONS\n            // Manually set the start position of the array here.\n            // Ensure this is after the call to handle_value to ensure correct start position.\n            if (m_lexer_ref)\n            {\n                // Lexer has read the first character of the array, so\n                // subtract 1 from the position to get the correct start position.\n                ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;\n            }\n#endif\n\n            // check array limit\n            if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))\n            {\n                JSON_THROW(out_of_range::create(408, concat(\"excessive array size: \", std::to_string(len)), ref_stack.back()));\n            }\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        bool keep = true;\n\n        if (ref_stack.back())\n        {\n            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());\n            if (keep)\n            {\n\n#if JSON_DIAGNOSTIC_POSITIONS\n                if (m_lexer_ref)\n                {\n                    // Lexer's position is past the closing bracket, so set that as the end position.\n                    ref_stack.back()->end_position = m_lexer_ref->get_position();\n                }\n#endif\n\n                ref_stack.back()->set_parents();\n            }\n            else\n            {\n                // discard array\n                *ref_stack.back() = discarded;\n\n#if JSON_DIAGNOSTIC_POSITIONS\n                // Set start/end positions for discarded array.\n                handle_diagnostic_positions_for_json_value(*ref_stack.back());\n#endif\n            }\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        // remove discarded value\n        if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_data.m_value.array->pop_back();\n        }\n\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n\n#if JSON_DIAGNOSTIC_POSITIONS\n    void handle_diagnostic_positions_for_json_value(BasicJsonType& v)\n    {\n        if (m_lexer_ref)\n        {\n            // Lexer has read past the current field value, so set the end position to the current position.\n            // The start position will be set below based on the length of the string representation\n            // of the value.\n            v.end_position = m_lexer_ref->get_position();\n\n            switch (v.type())\n            {\n                case value_t::boolean:\n                {\n                    // 4 and 5 are the string length of \"true\" and \"false\"\n                    v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5);\n                    break;\n                }\n\n                case value_t::null:\n                {\n                    // 4 is the string length of \"null\"\n                    v.start_position = v.end_position - 4;\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    // include the length of the quotes, which is 2\n                    v.start_position = v.end_position - v.m_data.m_value.string->size() - 2;\n                    break;\n                }\n\n                case value_t::discarded:\n                {\n                    v.end_position = std::string::npos;\n                    v.start_position = v.end_position;\n                    break;\n                }\n\n                case value_t::binary:\n                case value_t::number_integer:\n                case value_t::number_unsigned:\n                case value_t::number_float:\n                {\n                    v.start_position = v.end_position - m_lexer_ref->get_string().size();\n                    break;\n                }\n\n                case value_t::object:\n                case value_t::array:\n                {\n                    // object and array are handled in start_object() and start_array() handlers\n                    // skip setting the values here.\n                    break;\n                }\n                default: // LCOV_EXCL_LINE\n                    // Handle all possible types discretely, default handler should never be reached.\n                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE\n            }\n        }\n    }\n#endif\n\n    /*!\n    @param[in] v  value to add to the JSON value we build during parsing\n    @param[in] skip_callback  whether we should skip calling the callback\n               function; this is required after start_array() and\n               start_object() SAX events, because otherwise we would call the\n               callback function with an empty array or object, respectively.\n\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n\n    @return pair of boolean (whether value should be kept) and pointer (to the\n            passed value in the ref_stack hierarchy; nullptr if not kept)\n    */\n    template<typename Value>\n    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)\n    {\n        JSON_ASSERT(!keep_stack.empty());\n\n        // do not handle this value if we know it would be added to a discarded\n        // container\n        if (!keep_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // create value\n        auto value = BasicJsonType(std::forward<Value>(v));\n\n#if JSON_DIAGNOSTIC_POSITIONS\n        handle_diagnostic_positions_for_json_value(value);\n#endif\n\n        // check callback\n        const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);\n\n        // do not handle this value if we just learnt it shall be discarded\n        if (!keep)\n        {\n            return {false, nullptr};\n        }\n\n        if (ref_stack.empty())\n        {\n            root = std::move(value);\n            return {true, & root};\n        }\n\n        // skip this value if we already decided to skip the parent\n        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)\n        if (!ref_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // we now only expect arrays and objects\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        // array\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_data.m_value.array->emplace_back(std::move(value));\n            return {true, & (ref_stack.back()->m_data.m_value.array->back())};\n        }\n\n        // object\n        JSON_ASSERT(ref_stack.back()->is_object());\n        // check if we should store an element for the current key\n        JSON_ASSERT(!key_keep_stack.empty());\n        const bool store_element = key_keep_stack.back();\n        key_keep_stack.pop_back();\n\n        if (!store_element)\n        {\n            return {false, nullptr};\n        }\n\n        JSON_ASSERT(object_element);\n        *object_element = std::move(value);\n        return {true, object_element};\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// stack to manage which values to keep\n    std::vector<bool> keep_stack {}; // NOLINT(readability-redundant-member-init)\n    /// stack to manage which object keys to keep\n    std::vector<bool> key_keep_stack {}; // NOLINT(readability-redundant-member-init)\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// callback function\n    const parser_callback_t callback = nullptr;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n    /// a discarded value for the callback\n    BasicJsonType discarded = BasicJsonType::value_t::discarded;\n    /// the lexer reference to obtain the current position\n    lexer_t* m_lexer_ref = nullptr;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_acceptor\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    bool null()\n    {\n        return true;\n    }\n\n    bool boolean(bool /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_integer(number_integer_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool string(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool binary(binary_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool start_object(std::size_t /*unused*/ = detail::unknown_size())\n    {\n        return true;\n    }\n\n    bool key(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool end_object()\n    {\n        return true;\n    }\n\n    bool start_array(std::size_t /*unused*/ = detail::unknown_size())\n    {\n        return true;\n    }\n\n    bool end_array()\n    {\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)\n    {\n        return false;\n    }\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/input/lexer.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <array> // array\n#include <clocale> // localeconv\n#include <cstddef> // size_t\n#include <cstdio> // snprintf\n#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull\n#include <initializer_list> // initializer_list\n#include <string> // char_traits, string\n#include <utility> // move\n#include <vector> // vector\n\n#include <nlohmann/detail/input/input_adapters.hpp>\n#include <nlohmann/detail/input/position_t.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n///////////\n// lexer //\n///////////\n\ntemplate<typename BasicJsonType>\nclass lexer_base\n{\n  public:\n    /// token types for the parser\n    enum class token_type\n    {\n        uninitialized,    ///< indicating the scanner is uninitialized\n        literal_true,     ///< the `true` literal\n        literal_false,    ///< the `false` literal\n        literal_null,     ///< the `null` literal\n        value_string,     ///< a string -- use get_string() for actual value\n        value_unsigned,   ///< an unsigned integer -- use get_number_unsigned() for actual value\n        value_integer,    ///< a signed integer -- use get_number_integer() for actual value\n        value_float,      ///< an floating point number -- use get_number_float() for actual value\n        begin_array,      ///< the character for array begin `[`\n        begin_object,     ///< the character for object begin `{`\n        end_array,        ///< the character for array end `]`\n        end_object,       ///< the character for object end `}`\n        name_separator,   ///< the name separator `:`\n        value_separator,  ///< the value separator `,`\n        parse_error,      ///< indicating a parse error\n        end_of_input,     ///< indicating the end of the input buffer\n        literal_or_value  ///< a literal or the begin of a value (only for diagnostics)\n    };\n\n    /// return name of values of type token_type (only used for errors)\n    JSON_HEDLEY_RETURNS_NON_NULL\n    JSON_HEDLEY_CONST\n    static const char* token_type_name(const token_type t) noexcept\n    {\n        switch (t)\n        {\n            case token_type::uninitialized:\n                return \"<uninitialized>\";\n            case token_type::literal_true:\n                return \"true literal\";\n            case token_type::literal_false:\n                return \"false literal\";\n            case token_type::literal_null:\n                return \"null literal\";\n            case token_type::value_string:\n                return \"string literal\";\n            case token_type::value_unsigned:\n            case token_type::value_integer:\n            case token_type::value_float:\n                return \"number literal\";\n            case token_type::begin_array:\n                return \"'['\";\n            case token_type::begin_object:\n                return \"'{'\";\n            case token_type::end_array:\n                return \"']'\";\n            case token_type::end_object:\n                return \"'}'\";\n            case token_type::name_separator:\n                return \"':'\";\n            case token_type::value_separator:\n                return \"','\";\n            case token_type::parse_error:\n                return \"<parse error>\";\n            case token_type::end_of_input:\n                return \"end of input\";\n            case token_type::literal_or_value:\n                return \"'[', '{', or a literal\";\n            // LCOV_EXCL_START\n            default: // catch non-enum values\n                return \"unknown token\";\n                // LCOV_EXCL_STOP\n        }\n    }\n};\n/*!\n@brief lexical analysis\n\nThis class organizes the lexical analysis during JSON deserialization.\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass lexer : public lexer_base<BasicJsonType>\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename char_traits<char_type>::int_type;\n\n  public:\n    using token_type = typename lexer_base<BasicJsonType>::token_type;\n\n    explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept\n        : ia(std::move(adapter))\n        , ignore_comments(ignore_comments_)\n        , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))\n    {}\n\n    // deleted because of pointer members\n    lexer(const lexer&) = delete;\n    lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    lexer& operator=(lexer&) = delete;\n    lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~lexer() = default;\n\n  private:\n    /////////////////////\n    // locales\n    /////////////////////\n\n    /// return the locale-dependent decimal point\n    JSON_HEDLEY_PURE\n    static char get_decimal_point() noexcept\n    {\n        const auto* loc = localeconv();\n        JSON_ASSERT(loc != nullptr);\n        return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);\n    }\n\n    /////////////////////\n    // scan functions\n    /////////////////////\n\n    /*!\n    @brief get codepoint from 4 hex characters following `\\u`\n\n    For input \"\\u c1 c2 c3 c4\" the codepoint is:\n      (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4\n    = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)\n\n    Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'\n    must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The\n    conversion is done by subtracting the offset (0x30, 0x37, and 0x57)\n    between the ASCII value of the character and the desired integer value.\n\n    @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or\n            non-hex character)\n    */\n    int get_codepoint()\n    {\n        // this function only makes sense after reading `\\u`\n        JSON_ASSERT(current == 'u');\n        int codepoint = 0;\n\n        const auto factors = { 12u, 8u, 4u, 0u };\n        for (const auto factor : factors)\n        {\n            get();\n\n            if (current >= '0' && current <= '9')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);\n            }\n            else if (current >= 'A' && current <= 'F')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);\n            }\n            else if (current >= 'a' && current <= 'f')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);\n            }\n            else\n            {\n                return -1;\n            }\n        }\n\n        JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);\n        return codepoint;\n    }\n\n    /*!\n    @brief check if the next byte(s) are inside a given range\n\n    Adds the current byte and, for each passed range, reads a new byte and\n    checks if it is inside the range. If a violation was detected, set up an\n    error message and return false. Otherwise, return true.\n\n    @param[in] ranges  list of integers; interpreted as list of pairs of\n                       inclusive lower and upper bound, respectively\n\n    @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,\n         1, 2, or 3 pairs. This precondition is enforced by an assertion.\n\n    @return true if and only if no range violation was detected\n    */\n    bool next_byte_in_range(std::initializer_list<char_int_type> ranges)\n    {\n        JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);\n        add(current);\n\n        for (auto range = ranges.begin(); range != ranges.end(); ++range)\n        {\n            get();\n            if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) // NOLINT(bugprone-inc-dec-in-conditions)\n            {\n                add(current);\n            }\n            else\n            {\n                error_message = \"invalid string: ill-formed UTF-8 byte\";\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief scan a string literal\n\n    This function scans a string according to Sect. 7 of RFC 8259. While\n    scanning, bytes are escaped and copied into buffer token_buffer. Then the\n    function returns successfully, token_buffer is *not* null-terminated (as it\n    may contain \\0 bytes), and token_buffer.size() is the number of bytes in the\n    string.\n\n    @return token_type::value_string if string could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note In case of errors, variable error_message contains a textual\n          description.\n    */\n    token_type scan_string()\n    {\n        // reset token_buffer (ignore opening quote)\n        reset();\n\n        // we entered the function by reading an open quote\n        JSON_ASSERT(current == '\\\"');\n\n        while (true)\n        {\n            // get the next character\n            switch (get())\n            {\n                // end of file while parsing the string\n                case char_traits<char_type>::eof():\n                {\n                    error_message = \"invalid string: missing closing quote\";\n                    return token_type::parse_error;\n                }\n\n                // closing quote\n                case '\\\"':\n                {\n                    return token_type::value_string;\n                }\n\n                // escapes\n                case '\\\\':\n                {\n                    switch (get())\n                    {\n                        // quotation mark\n                        case '\\\"':\n                            add('\\\"');\n                            break;\n                        // reverse solidus\n                        case '\\\\':\n                            add('\\\\');\n                            break;\n                        // solidus\n                        case '/':\n                            add('/');\n                            break;\n                        // backspace\n                        case 'b':\n                            add('\\b');\n                            break;\n                        // form feed\n                        case 'f':\n                            add('\\f');\n                            break;\n                        // line feed\n                        case 'n':\n                            add('\\n');\n                            break;\n                        // carriage return\n                        case 'r':\n                            add('\\r');\n                            break;\n                        // tab\n                        case 't':\n                            add('\\t');\n                            break;\n\n                        // unicode escapes\n                        case 'u':\n                        {\n                            const int codepoint1 = get_codepoint();\n                            int codepoint = codepoint1; // start with codepoint1\n\n                            if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))\n                            {\n                                error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                return token_type::parse_error;\n                            }\n\n                            // check if code point is a high surrogate\n                            if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)\n                            {\n                                // expect next \\uxxxx entry\n                                if (JSON_HEDLEY_LIKELY(get() == '\\\\' && get() == 'u'))\n                                {\n                                    const int codepoint2 = get_codepoint();\n\n                                    if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))\n                                    {\n                                        error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                        return token_type::parse_error;\n                                    }\n\n                                    // check if codepoint2 is a low surrogate\n                                    if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))\n                                    {\n                                        // overwrite codepoint\n                                        codepoint = static_cast<int>(\n                                                        // high surrogate occupies the most significant 22 bits\n                                                        (static_cast<unsigned int>(codepoint1) << 10u)\n                                                        // low surrogate occupies the least significant 15 bits\n                                                        + static_cast<unsigned int>(codepoint2)\n                                                        // there is still the 0xD800, 0xDC00, and 0x10000 noise\n                                                        // in the result, so we have to subtract with:\n                                                        // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00\n                                                        - 0x35FDC00u);\n                                    }\n                                    else\n                                    {\n                                        error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                        return token_type::parse_error;\n                                    }\n                                }\n                                else\n                                {\n                                    error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n                            else\n                            {\n                                if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))\n                                {\n                                    error_message = \"invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n\n                            // the result of the above calculation yields a proper codepoint\n                            JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);\n\n                            // translate codepoint into bytes\n                            if (codepoint < 0x80)\n                            {\n                                // 1-byte characters: 0xxxxxxx (ASCII)\n                                add(static_cast<char_int_type>(codepoint));\n                            }\n                            else if (codepoint <= 0x7FF)\n                            {\n                                // 2-byte characters: 110xxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else if (codepoint <= 0xFFFF)\n                            {\n                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else\n                            {\n                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n\n                            break;\n                        }\n\n                        // other characters after escape\n                        default:\n                            error_message = \"invalid string: forbidden character after backslash\";\n                            return token_type::parse_error;\n                    }\n\n                    break;\n                }\n\n                // invalid control characters\n                case 0x00:\n                {\n                    error_message = \"invalid string: control character U+0000 (NUL) must be escaped to \\\\u0000\";\n                    return token_type::parse_error;\n                }\n\n                case 0x01:\n                {\n                    error_message = \"invalid string: control character U+0001 (SOH) must be escaped to \\\\u0001\";\n                    return token_type::parse_error;\n                }\n\n                case 0x02:\n                {\n                    error_message = \"invalid string: control character U+0002 (STX) must be escaped to \\\\u0002\";\n                    return token_type::parse_error;\n                }\n\n                case 0x03:\n                {\n                    error_message = \"invalid string: control character U+0003 (ETX) must be escaped to \\\\u0003\";\n                    return token_type::parse_error;\n                }\n\n                case 0x04:\n                {\n                    error_message = \"invalid string: control character U+0004 (EOT) must be escaped to \\\\u0004\";\n                    return token_type::parse_error;\n                }\n\n                case 0x05:\n                {\n                    error_message = \"invalid string: control character U+0005 (ENQ) must be escaped to \\\\u0005\";\n                    return token_type::parse_error;\n                }\n\n                case 0x06:\n                {\n                    error_message = \"invalid string: control character U+0006 (ACK) must be escaped to \\\\u0006\";\n                    return token_type::parse_error;\n                }\n\n                case 0x07:\n                {\n                    error_message = \"invalid string: control character U+0007 (BEL) must be escaped to \\\\u0007\";\n                    return token_type::parse_error;\n                }\n\n                case 0x08:\n                {\n                    error_message = \"invalid string: control character U+0008 (BS) must be escaped to \\\\u0008 or \\\\b\";\n                    return token_type::parse_error;\n                }\n\n                case 0x09:\n                {\n                    error_message = \"invalid string: control character U+0009 (HT) must be escaped to \\\\u0009 or \\\\t\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0A:\n                {\n                    error_message = \"invalid string: control character U+000A (LF) must be escaped to \\\\u000A or \\\\n\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0B:\n                {\n                    error_message = \"invalid string: control character U+000B (VT) must be escaped to \\\\u000B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0C:\n                {\n                    error_message = \"invalid string: control character U+000C (FF) must be escaped to \\\\u000C or \\\\f\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0D:\n                {\n                    error_message = \"invalid string: control character U+000D (CR) must be escaped to \\\\u000D or \\\\r\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0E:\n                {\n                    error_message = \"invalid string: control character U+000E (SO) must be escaped to \\\\u000E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0F:\n                {\n                    error_message = \"invalid string: control character U+000F (SI) must be escaped to \\\\u000F\";\n                    return token_type::parse_error;\n                }\n\n                case 0x10:\n                {\n                    error_message = \"invalid string: control character U+0010 (DLE) must be escaped to \\\\u0010\";\n                    return token_type::parse_error;\n                }\n\n                case 0x11:\n                {\n                    error_message = \"invalid string: control character U+0011 (DC1) must be escaped to \\\\u0011\";\n                    return token_type::parse_error;\n                }\n\n                case 0x12:\n                {\n                    error_message = \"invalid string: control character U+0012 (DC2) must be escaped to \\\\u0012\";\n                    return token_type::parse_error;\n                }\n\n                case 0x13:\n                {\n                    error_message = \"invalid string: control character U+0013 (DC3) must be escaped to \\\\u0013\";\n                    return token_type::parse_error;\n                }\n\n                case 0x14:\n                {\n                    error_message = \"invalid string: control character U+0014 (DC4) must be escaped to \\\\u0014\";\n                    return token_type::parse_error;\n                }\n\n                case 0x15:\n                {\n                    error_message = \"invalid string: control character U+0015 (NAK) must be escaped to \\\\u0015\";\n                    return token_type::parse_error;\n                }\n\n                case 0x16:\n                {\n                    error_message = \"invalid string: control character U+0016 (SYN) must be escaped to \\\\u0016\";\n                    return token_type::parse_error;\n                }\n\n                case 0x17:\n                {\n                    error_message = \"invalid string: control character U+0017 (ETB) must be escaped to \\\\u0017\";\n                    return token_type::parse_error;\n                }\n\n                case 0x18:\n                {\n                    error_message = \"invalid string: control character U+0018 (CAN) must be escaped to \\\\u0018\";\n                    return token_type::parse_error;\n                }\n\n                case 0x19:\n                {\n                    error_message = \"invalid string: control character U+0019 (EM) must be escaped to \\\\u0019\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1A:\n                {\n                    error_message = \"invalid string: control character U+001A (SUB) must be escaped to \\\\u001A\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1B:\n                {\n                    error_message = \"invalid string: control character U+001B (ESC) must be escaped to \\\\u001B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1C:\n                {\n                    error_message = \"invalid string: control character U+001C (FS) must be escaped to \\\\u001C\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1D:\n                {\n                    error_message = \"invalid string: control character U+001D (GS) must be escaped to \\\\u001D\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1E:\n                {\n                    error_message = \"invalid string: control character U+001E (RS) must be escaped to \\\\u001E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1F:\n                {\n                    error_message = \"invalid string: control character U+001F (US) must be escaped to \\\\u001F\";\n                    return token_type::parse_error;\n                }\n\n                // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))\n                case 0x20:\n                case 0x21:\n                case 0x23:\n                case 0x24:\n                case 0x25:\n                case 0x26:\n                case 0x27:\n                case 0x28:\n                case 0x29:\n                case 0x2A:\n                case 0x2B:\n                case 0x2C:\n                case 0x2D:\n                case 0x2E:\n                case 0x2F:\n                case 0x30:\n                case 0x31:\n                case 0x32:\n                case 0x33:\n                case 0x34:\n                case 0x35:\n                case 0x36:\n                case 0x37:\n                case 0x38:\n                case 0x39:\n                case 0x3A:\n                case 0x3B:\n                case 0x3C:\n                case 0x3D:\n                case 0x3E:\n                case 0x3F:\n                case 0x40:\n                case 0x41:\n                case 0x42:\n                case 0x43:\n                case 0x44:\n                case 0x45:\n                case 0x46:\n                case 0x47:\n                case 0x48:\n                case 0x49:\n                case 0x4A:\n                case 0x4B:\n                case 0x4C:\n                case 0x4D:\n                case 0x4E:\n                case 0x4F:\n                case 0x50:\n                case 0x51:\n                case 0x52:\n                case 0x53:\n                case 0x54:\n                case 0x55:\n                case 0x56:\n                case 0x57:\n                case 0x58:\n                case 0x59:\n                case 0x5A:\n                case 0x5B:\n                case 0x5D:\n                case 0x5E:\n                case 0x5F:\n                case 0x60:\n                case 0x61:\n                case 0x62:\n                case 0x63:\n                case 0x64:\n                case 0x65:\n                case 0x66:\n                case 0x67:\n                case 0x68:\n                case 0x69:\n                case 0x6A:\n                case 0x6B:\n                case 0x6C:\n                case 0x6D:\n                case 0x6E:\n                case 0x6F:\n                case 0x70:\n                case 0x71:\n                case 0x72:\n                case 0x73:\n                case 0x74:\n                case 0x75:\n                case 0x76:\n                case 0x77:\n                case 0x78:\n                case 0x79:\n                case 0x7A:\n                case 0x7B:\n                case 0x7C:\n                case 0x7D:\n                case 0x7E:\n                case 0x7F:\n                {\n                    add(current);\n                    break;\n                }\n\n                // U+0080..U+07FF: bytes C2..DF 80..BF\n                case 0xC2:\n                case 0xC3:\n                case 0xC4:\n                case 0xC5:\n                case 0xC6:\n                case 0xC7:\n                case 0xC8:\n                case 0xC9:\n                case 0xCA:\n                case 0xCB:\n                case 0xCC:\n                case 0xCD:\n                case 0xCE:\n                case 0xCF:\n                case 0xD0:\n                case 0xD1:\n                case 0xD2:\n                case 0xD3:\n                case 0xD4:\n                case 0xD5:\n                case 0xD6:\n                case 0xD7:\n                case 0xD8:\n                case 0xD9:\n                case 0xDA:\n                case 0xDB:\n                case 0xDC:\n                case 0xDD:\n                case 0xDE:\n                case 0xDF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+0800..U+0FFF: bytes E0 A0..BF 80..BF\n                case 0xE0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF\n                // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF\n                case 0xE1:\n                case 0xE2:\n                case 0xE3:\n                case 0xE4:\n                case 0xE5:\n                case 0xE6:\n                case 0xE7:\n                case 0xE8:\n                case 0xE9:\n                case 0xEA:\n                case 0xEB:\n                case 0xEC:\n                case 0xEE:\n                case 0xEF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+D000..U+D7FF: bytes ED 80..9F 80..BF\n                case 0xED:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF\n                case 0xF0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF\n                case 0xF1:\n                case 0xF2:\n                case 0xF3:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF\n                case 0xF4:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // the remaining bytes (80..C1 and F5..FF) are ill-formed\n                default:\n                {\n                    error_message = \"invalid string: ill-formed UTF-8 byte\";\n                    return token_type::parse_error;\n                }\n            }\n        }\n    }\n\n    /*!\n     * @brief scan a comment\n     * @return whether comment could be scanned successfully\n     */\n    bool scan_comment()\n    {\n        switch (get())\n        {\n            // single-line comments skip input until a newline or EOF is read\n            case '/':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case '\\n':\n                        case '\\r':\n                        case char_traits<char_type>::eof():\n                        case '\\0':\n                            return true;\n\n                        default:\n                            break;\n                    }\n                }\n            }\n\n            // multi-line comments skip input until */ is read\n            case '*':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case char_traits<char_type>::eof():\n                        case '\\0':\n                        {\n                            error_message = \"invalid comment; missing closing '*/'\";\n                            return false;\n                        }\n\n                        case '*':\n                        {\n                            switch (get())\n                            {\n                                case '/':\n                                    return true;\n\n                                default:\n                                {\n                                    unget();\n                                    continue;\n                                }\n                            }\n                        }\n\n                        default:\n                            continue;\n                    }\n                }\n            }\n\n            // unexpected character after reading '/'\n            default:\n            {\n                error_message = \"invalid comment; expecting '/' or '*' after '/'\";\n                return false;\n            }\n        }\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(float& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtof(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtod(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(long double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtold(str, endptr);\n    }\n\n    /*!\n    @brief scan a number literal\n\n    This function scans a string according to Sect. 6 of RFC 8259.\n\n    The function is realized with a deterministic finite state machine derived\n    from the grammar described in RFC 8259. Starting in state \"init\", the\n    input is read and used to determined the next state. Only state \"done\"\n    accepts the number. State \"error\" is a trap state to model errors. In the\n    table below, \"anything\" means any character but the ones listed before.\n\n    state    | 0        | 1-9      | e E      | +       | -       | .        | anything\n    ---------|----------|----------|----------|---------|---------|----------|-----------\n    init     | zero     | any1     | [error]  | [error] | minus   | [error]  | [error]\n    minus    | zero     | any1     | [error]  | [error] | [error] | [error]  | [error]\n    zero     | done     | done     | exponent | done    | done    | decimal1 | done\n    any1     | any1     | any1     | exponent | done    | done    | decimal1 | done\n    decimal1 | decimal2 | decimal2 | [error]  | [error] | [error] | [error]  | [error]\n    decimal2 | decimal2 | decimal2 | exponent | done    | done    | done     | done\n    exponent | any2     | any2     | [error]  | sign    | sign    | [error]  | [error]\n    sign     | any2     | any2     | [error]  | [error] | [error] | [error]  | [error]\n    any2     | any2     | any2     | done     | done    | done    | done     | done\n\n    The state machine is realized with one label per state (prefixed with\n    \"scan_number_\") and `goto` statements between them. The state machine\n    contains cycles, but any cycle can be left when EOF is read. Therefore,\n    the function is guaranteed to terminate.\n\n    During scanning, the read bytes are stored in token_buffer. This string is\n    then converted to a signed integer, an unsigned integer, or a\n    floating-point number.\n\n    @return token_type::value_unsigned, token_type::value_integer, or\n            token_type::value_float if number could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note The scanner is independent of the current locale. Internally, the\n          locale's decimal point is used instead of `.` to work with the\n          locale-dependent converters.\n    */\n    token_type scan_number()  // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the \"done\" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated.\n    {\n        // reset token_buffer to store the number's bytes\n        reset();\n\n        // the type of the parsed number; initially set to unsigned; will be\n        // changed if minus sign, decimal point, or exponent is read\n        token_type number_type = token_type::value_unsigned;\n\n        // state (init): we just found out we need to scan a number\n        switch (current)\n        {\n            case '-':\n            {\n                add(current);\n                goto scan_number_minus;\n            }\n\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            // all other characters are rejected outside scan_number()\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\nscan_number_minus:\n        // state: we just parsed a leading minus sign\n        number_type = token_type::value_integer;\n        switch (get())\n        {\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '-'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_zero:\n        // state: we just parse a zero (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '.':\n            {\n                add(decimal_point_char);\n                decimal_point_position = token_buffer.size() - 1;\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_any1:\n        // state: we just parsed a number 0-9 (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            case '.':\n            {\n                add(decimal_point_char);\n                decimal_point_position = token_buffer.size() - 1;\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_decimal1:\n        // state: we just parsed a decimal point\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '.'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_decimal2:\n        // we just parsed at least one number after a decimal point\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_exponent:\n        // we just parsed an exponent\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '+':\n            case '-':\n            {\n                add(current);\n                goto scan_number_sign;\n            }\n\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message =\n                    \"invalid number; expected '+', '-', or digit after exponent\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_sign:\n        // we just parsed an exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after exponent sign\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_any2:\n        // we just parsed a number after the exponent or exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_done:\n        // unget the character after the number (we only read it to know that\n        // we are done scanning a number)\n        unget();\n\n        char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        errno = 0;\n\n        // try to parse integers first and fall back to floats\n        if (number_type == token_type::value_unsigned)\n        {\n            const auto x = std::strtoull(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno != ERANGE)\n            {\n                value_unsigned = static_cast<number_unsigned_t>(x);\n                if (value_unsigned == x)\n                {\n                    return token_type::value_unsigned;\n                }\n            }\n        }\n        else if (number_type == token_type::value_integer)\n        {\n            const auto x = std::strtoll(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno != ERANGE)\n            {\n                value_integer = static_cast<number_integer_t>(x);\n                if (value_integer == x)\n                {\n                    return token_type::value_integer;\n                }\n            }\n        }\n\n        // this code is reached if we parse a floating-point number or if an\n        // integer conversion above failed\n        strtof(value_float, token_buffer.data(), &endptr);\n\n        // we checked the number format before\n        JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n        return token_type::value_float;\n    }\n\n    /*!\n    @param[in] literal_text  the literal text to expect\n    @param[in] length        the length of the passed literal text\n    @param[in] return_type   the token type to return on success\n    */\n    JSON_HEDLEY_NON_NULL(2)\n    token_type scan_literal(const char_type* literal_text, const std::size_t length,\n                            token_type return_type)\n    {\n        JSON_ASSERT(char_traits<char_type>::to_char_type(current) == literal_text[0]);\n        for (std::size_t i = 1; i < length; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(char_traits<char_type>::to_char_type(get()) != literal_text[i]))\n            {\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n            }\n        }\n        return return_type;\n    }\n\n    /////////////////////\n    // input management\n    /////////////////////\n\n    /// reset token_buffer; current character is beginning of token\n    void reset() noexcept\n    {\n        token_buffer.clear();\n        token_string.clear();\n        decimal_point_position = std::string::npos;\n        token_string.push_back(char_traits<char_type>::to_char_type(current));\n    }\n\n    /*\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a\n    `char_traits<char>::eof()` in that case.  Stores the scanned characters\n    for use in error messages.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++position.chars_read_total;\n        ++position.chars_read_current_line;\n\n        if (next_unget)\n        {\n            // only reset the next_unget variable and work with current\n            next_unget = false;\n        }\n        else\n        {\n            current = ia.get_character();\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != char_traits<char_type>::eof()))\n        {\n            token_string.push_back(char_traits<char_type>::to_char_type(current));\n        }\n\n        if (current == '\\n')\n        {\n            ++position.lines_read;\n            position.chars_read_current_line = 0;\n        }\n\n        return current;\n    }\n\n    /*!\n    @brief unget current character (read it again on next get)\n\n    We implement unget by setting variable next_unget to true. The input is not\n    changed - we just simulate ungetting by modifying chars_read_total,\n    chars_read_current_line, and token_string. The next call to get() will\n    behave as if the unget character is read again.\n    */\n    void unget()\n    {\n        next_unget = true;\n\n        --position.chars_read_total;\n\n        // in case we \"unget\" a newline, we have to also decrement the lines_read\n        if (position.chars_read_current_line == 0)\n        {\n            if (position.lines_read > 0)\n            {\n                --position.lines_read;\n            }\n        }\n        else\n        {\n            --position.chars_read_current_line;\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != char_traits<char_type>::eof()))\n        {\n            JSON_ASSERT(!token_string.empty());\n            token_string.pop_back();\n        }\n    }\n\n    /// add a character to token_buffer\n    void add(char_int_type c)\n    {\n        token_buffer.push_back(static_cast<typename string_t::value_type>(c));\n    }\n\n  public:\n    /////////////////////\n    // value getters\n    /////////////////////\n\n    /// return integer value\n    constexpr number_integer_t get_number_integer() const noexcept\n    {\n        return value_integer;\n    }\n\n    /// return unsigned integer value\n    constexpr number_unsigned_t get_number_unsigned() const noexcept\n    {\n        return value_unsigned;\n    }\n\n    /// return floating-point value\n    constexpr number_float_t get_number_float() const noexcept\n    {\n        return value_float;\n    }\n\n    /// return current string value (implicitly resets the token; useful only once)\n    string_t& get_string()\n    {\n        // translate decimal points from locale back to '.' (#4084)\n        if (decimal_point_char != '.' && decimal_point_position != std::string::npos)\n        {\n            token_buffer[decimal_point_position] = '.';\n        }\n        return token_buffer;\n    }\n\n    /////////////////////\n    // diagnostics\n    /////////////////////\n\n    /// return position of last read token\n    constexpr position_t get_position() const noexcept\n    {\n        return position;\n    }\n\n    /// return the last read token (for errors only).  Will never contain EOF\n    /// (an arbitrary value that is not a valid char value, often -1), because\n    /// 255 may legitimately occur.  May contain NUL, which should be escaped.\n    std::string get_token_string() const\n    {\n        // escape control characters\n        std::string result;\n        for (const auto c : token_string)\n        {\n            if (static_cast<unsigned char>(c) <= '\\x1F')\n            {\n                // escape control characters\n                std::array<char, 9> cs{{}};\n                static_cast<void>((std::snprintf)(cs.data(), cs.size(), \"<U+%.4X>\", static_cast<unsigned char>(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                result += cs.data();\n            }\n            else\n            {\n                // add character as is\n                result.push_back(static_cast<std::string::value_type>(c));\n            }\n        }\n\n        return result;\n    }\n\n    /// return syntax error message\n    JSON_HEDLEY_RETURNS_NON_NULL\n    constexpr const char* get_error_message() const noexcept\n    {\n        return error_message;\n    }\n\n    /////////////////////\n    // actual scanner\n    /////////////////////\n\n    /*!\n    @brief skip the UTF-8 byte order mark\n    @return true iff there is no BOM or the correct BOM has been skipped\n    */\n    bool skip_bom()\n    {\n        if (get() == 0xEF)\n        {\n            // check if we completely parse the BOM\n            return get() == 0xBB && get() == 0xBF;\n        }\n\n        // the first character is not the beginning of the BOM; unget it to\n        // process is later\n        unget();\n        return true;\n    }\n\n    void skip_whitespace()\n    {\n        do\n        {\n            get();\n        }\n        while (current == ' ' || current == '\\t' || current == '\\n' || current == '\\r');\n    }\n\n    token_type scan()\n    {\n        // initially, skip the BOM\n        if (position.chars_read_total == 0 && !skip_bom())\n        {\n            error_message = \"invalid BOM; must be 0xEF 0xBB 0xBF if given\";\n            return token_type::parse_error;\n        }\n\n        // read the next character and ignore whitespace\n        skip_whitespace();\n\n        // ignore comments\n        while (ignore_comments && current == '/')\n        {\n            if (!scan_comment())\n            {\n                return token_type::parse_error;\n            }\n\n            // skip following whitespace\n            skip_whitespace();\n        }\n\n        switch (current)\n        {\n            // structural characters\n            case '[':\n                return token_type::begin_array;\n            case ']':\n                return token_type::end_array;\n            case '{':\n                return token_type::begin_object;\n            case '}':\n                return token_type::end_object;\n            case ':':\n                return token_type::name_separator;\n            case ',':\n                return token_type::value_separator;\n\n            // literals\n            case 't':\n            {\n                std::array<char_type, 4> true_literal = {{static_cast<char_type>('t'), static_cast<char_type>('r'), static_cast<char_type>('u'), static_cast<char_type>('e')}};\n                return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);\n            }\n            case 'f':\n            {\n                std::array<char_type, 5> false_literal = {{static_cast<char_type>('f'), static_cast<char_type>('a'), static_cast<char_type>('l'), static_cast<char_type>('s'), static_cast<char_type>('e')}};\n                return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);\n            }\n            case 'n':\n            {\n                std::array<char_type, 4> null_literal = {{static_cast<char_type>('n'), static_cast<char_type>('u'), static_cast<char_type>('l'), static_cast<char_type>('l')}};\n                return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);\n            }\n\n            // string\n            case '\\\"':\n                return scan_string();\n\n            // number\n            case '-':\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n                return scan_number();\n\n            // end of input (the null byte is needed when parsing from\n            // string literals)\n            case '\\0':\n            case char_traits<char_type>::eof():\n                return token_type::end_of_input;\n\n            // error\n            default:\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n        }\n    }\n\n  private:\n    /// input adapter\n    InputAdapterType ia;\n\n    /// whether comments should be ignored (true) or signaled as errors (false)\n    const bool ignore_comments = false;\n\n    /// the current character\n    char_int_type current = char_traits<char_type>::eof();\n\n    /// whether the next get() call should just return current\n    bool next_unget = false;\n\n    /// the start position of the current token\n    position_t position {};\n\n    /// raw input token string (for error messages)\n    std::vector<char_type> token_string {};\n\n    /// buffer for variable-length tokens (numbers, strings)\n    string_t token_buffer {};\n\n    /// a description of occurred lexer errors\n    const char* error_message = \"\";\n\n    // number values\n    number_integer_t value_integer = 0;\n    number_unsigned_t value_unsigned = 0;\n    number_float_t value_float = 0;\n\n    /// the decimal point\n    const char_int_type decimal_point_char = '.';\n    /// the position of the decimal point in the input\n    std::size_t decimal_point_position = std::string::npos;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/input/position_t.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstddef> // size_t\n\n#include <nlohmann/detail/abi_macros.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/// struct to capture the start position of the current token\nstruct position_t\n{\n    /// the total number of characters read\n    std::size_t chars_read_total = 0;\n    /// the number of characters read in the current line\n    std::size_t chars_read_current_line = 0;\n    /// the number of lines read\n    std::size_t lines_read = 0;\n\n    /// conversion to size_t to preserve SAX interface\n    constexpr operator size_t() const\n    {\n        return chars_read_total;\n    }\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/iterators/internal_iterator.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <nlohmann/detail/abi_macros.hpp>\n#include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/*!\n@brief an iterator value\n\n@note This structure could easily be a union, but MSVC currently does not allow\nunions members with complex constructors, see https://github.com/nlohmann/json/pull/105.\n*/\ntemplate<typename BasicJsonType> struct internal_iterator\n{\n    /// iterator for JSON objects\n    typename BasicJsonType::object_t::iterator object_iterator {};\n    /// iterator for JSON arrays\n    typename BasicJsonType::array_t::iterator array_iterator {};\n    /// generic iterator for all other types\n    primitive_iterator_t primitive_iterator {};\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/iterators/iter_impl.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next\n#include <type_traits> // conditional, is_const, remove_const\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/iterators/internal_iterator.hpp>\n#include <nlohmann/detail/iterators/primitive_iterator.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n// forward declare to be able to friend it later on\ntemplate<typename IteratorType> class iteration_proxy;\ntemplate<typename IteratorType> class iteration_proxy_value;\n\n/*!\n@brief a template for a bidirectional iterator for the @ref basic_json class\nThis class implements a both iterators (iterator and const_iterator) for the\n@ref basic_json class.\n@note An iterator is called *initialized* when a pointer to a JSON value has\n      been set (e.g., by a constructor or a copy assignment). If the iterator is\n      default-constructed, it is *uninitialized* and most methods are undefined.\n      **The library uses assertions to detect calls on uninitialized iterators.**\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n@since version 1.0.0, simplified in version 2.0.9, change to bidirectional\n       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)\n*/\ntemplate<typename BasicJsonType>\nclass iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)\n{\n    /// the iterator with BasicJsonType of different const-ness\n    using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;\n    /// allow basic_json to access private members\n    friend other_iter_impl;\n    friend BasicJsonType;\n    friend iteration_proxy<iter_impl>;\n    friend iteration_proxy_value<iter_impl>;\n\n    using object_t = typename BasicJsonType::object_t;\n    using array_t = typename BasicJsonType::array_t;\n    // make sure BasicJsonType is basic_json or const basic_json\n    static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,\n                  \"iter_impl only accepts (const) basic_json\");\n    // superficial check for the LegacyBidirectionalIterator named requirement\n    static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value\n                  &&  std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<typename array_t::iterator>::iterator_category>::value,\n                  \"basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.\");\n\n  public:\n    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.\n    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.\n    /// A user-defined iterator should provide publicly accessible typedefs named\n    /// iterator_category, value_type, difference_type, pointer, and reference.\n    /// Note that value_type is required to be non-const, even for constant iterators.\n    using iterator_category = std::bidirectional_iterator_tag;\n\n    /// the type of the values when the iterator is dereferenced\n    using value_type = typename BasicJsonType::value_type;\n    /// a type to represent differences between iterators\n    using difference_type = typename BasicJsonType::difference_type;\n    /// defines a pointer to the type iterated over (value_type)\n    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,\n          typename BasicJsonType::const_pointer,\n          typename BasicJsonType::pointer>::type;\n    /// defines a reference to the type iterated over (value_type)\n    using reference =\n        typename std::conditional<std::is_const<BasicJsonType>::value,\n        typename BasicJsonType::const_reference,\n        typename BasicJsonType::reference>::type;\n\n    iter_impl() = default;\n    ~iter_impl() = default;\n    iter_impl(iter_impl&&) noexcept = default;\n    iter_impl& operator=(iter_impl&&) noexcept = default;\n\n    /*!\n    @brief constructor for a given JSON instance\n    @param[in] object  pointer to a JSON object for this iterator\n    @pre object != nullptr\n    @post The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    explicit iter_impl(pointer object) noexcept : m_object(object)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = typename object_t::iterator();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = typename array_t::iterator();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator = primitive_iterator_t();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @note The conventional copy constructor and copy assignment are implicitly\n          defined. Combined with the following converting constructor and\n          assignment, they support: (1) copy from iterator to iterator, (2)\n          copy from const iterator to const iterator, and (3) conversion from\n          iterator to const iterator. However conversion from const iterator\n          to iterator is not defined.\n    */\n\n    /*!\n    @brief const copy constructor\n    @param[in] other const iterator to copy from\n    @note This copy constructor had to be defined explicitly to circumvent a bug\n          occurring on msvc v19.0 compiler (VS 2015) debug build. For more\n          information refer to: https://github.com/nlohmann/json/issues/1608\n    */\n    iter_impl(const iter_impl<const BasicJsonType>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept\n    {\n        if (&other != this)\n        {\n            m_object = other.m_object;\n            m_it = other.m_it;\n        }\n        return *this;\n    }\n\n    /*!\n    @brief converting constructor\n    @param[in] other  non-const iterator to copy from\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other  non-const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)\n    {\n        m_object = other.m_object;\n        m_it = other.m_it;\n        return *this;\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief set the iterator to the first value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_begin() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_data.m_value.object->begin();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_data.m_value.array->begin();\n                break;\n            }\n\n            case value_t::null:\n            {\n                // set to end so begin()==end() is true: null is empty\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator.set_begin();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @brief set the iterator past the last value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_end() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_data.m_value.object->end();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_data.m_value.array->end();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n        }\n    }\n\n  public:\n    /*!\n    @brief return a reference to the value pointed to by the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator*() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());\n                return m_it.object_iterator->second;\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());\n                return *m_it.array_iterator;\n            }\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", m_object));\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief dereference the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    pointer operator->() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());\n                return &(m_it.object_iterator->second);\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());\n                return &*m_it.array_iterator;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief post-increment (it++)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp)\n    {\n        auto result = *this;\n        ++(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-increment (++it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator++()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, 1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, 1);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                ++m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief post-decrement (it--)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp)\n    {\n        auto result = *this;\n        --(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-decrement (--it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator--()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, -1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, -1);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                --m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief comparison: equal\n    @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized.\n    */\n    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >\n    bool operator==(const IterImpl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\", m_object));\n        }\n\n        // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493\n        if (m_object == nullptr)\n        {\n            return true;\n        }\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n                return (m_it.object_iterator == other.m_it.object_iterator);\n\n            case value_t::array:\n                return (m_it.array_iterator == other.m_it.array_iterator);\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return (m_it.primitive_iterator == other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief comparison: not equal\n    @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized.\n    */\n    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >\n    bool operator!=(const IterImpl& other) const\n    {\n        return !operator==(other);\n    }\n\n    /*!\n    @brief comparison: smaller\n    @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized.\n    */\n    bool operator<(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\", m_object));\n        }\n\n        // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493\n        if (m_object == nullptr)\n        {\n            // the iterators are both value-initialized and are to be considered equal, but this function checks for smaller, so we return false\n            return false;\n        }\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(213, \"cannot compare order of object iterators\", m_object));\n\n            case value_t::array:\n                return (m_it.array_iterator < other.m_it.array_iterator);\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return (m_it.primitive_iterator < other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized.\n    */\n    bool operator<=(const iter_impl& other) const\n    {\n        return !other.operator < (*this);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized.\n    */\n    bool operator>(const iter_impl& other) const\n    {\n        return !operator<=(other);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @pre (1) The iterator is initialized; i.e. `m_object != nullptr`, or (2) both iterators are value-initialized.\n    */\n    bool operator>=(const iter_impl& other) const\n    {\n        return !operator<(other);\n    }\n\n    /*!\n    @brief add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator+=(difference_type i)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\", m_object));\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, i);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator += i;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator-=(difference_type i)\n    {\n        return operator+=(-i);\n    }\n\n    /*!\n    @brief add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator+(difference_type i) const\n    {\n        auto result = *this;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief addition of distance and iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    friend iter_impl operator+(difference_type i, const iter_impl& it)\n    {\n        auto result = it;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator-(difference_type i) const\n    {\n        auto result = *this;\n        result -= i;\n        return result;\n    }\n\n    /*!\n    @brief return difference\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    difference_type operator-(const iter_impl& other) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\", m_object));\n\n            case value_t::array:\n                return m_it.array_iterator - other.m_it.array_iterator;\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return m_it.primitive_iterator - other.m_it.primitive_iterator;\n        }\n    }\n\n    /*!\n    @brief access to successor\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator[](difference_type n) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_data.m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(208, \"cannot use operator[] for object iterators\", m_object));\n\n            case value_t::array:\n                return *std::next(m_it.array_iterator, n);\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", m_object));\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief return the key of an object iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    const typename object_t::key_type& key() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        if (JSON_HEDLEY_LIKELY(m_object->is_object()))\n        {\n            return m_it.object_iterator->first;\n        }\n\n        JSON_THROW(invalid_iterator::create(207, \"cannot use key() for non-object iterators\", m_object));\n    }\n\n    /*!\n    @brief return the value of an iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference value() const\n    {\n        return operator*();\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /// associated JSON instance\n    pointer m_object = nullptr;\n    /// the actual iterator of the associated instance\n    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/iterators/iteration_proxy.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstddef> // size_t\n#include <iterator> // forward_iterator_tag\n#include <tuple> // tuple_size, get, tuple_element\n#include <utility> // move\n\n#if JSON_HAS_RANGES\n    #include <ranges> // enable_borrowed_range\n#endif\n\n#include <nlohmann/detail/abi_macros.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/string_utils.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename IteratorType> class iteration_proxy_value\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    using value_type = iteration_proxy_value;\n    using pointer = value_type *;\n    using reference = value_type &;\n    using iterator_category = std::forward_iterator_tag;\n    using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;\n\n  private:\n    /// the iterator\n    IteratorType anchor{};\n    /// an index for arrays (used to create key names)\n    std::size_t array_index = 0;\n    /// last stringified array index\n    mutable std::size_t array_index_last = 0;\n    /// a string representation of the array index\n    mutable string_type array_index_str = \"0\";\n    /// an empty string (to return a reference for primitive values)\n    string_type empty_str{};\n\n  public:\n    explicit iteration_proxy_value() = default;\n    explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)\n    noexcept(std::is_nothrow_move_constructible<IteratorType>::value\n             && std::is_nothrow_default_constructible<string_type>::value)\n        : anchor(std::move(it))\n        , array_index(array_index_)\n    {}\n\n    iteration_proxy_value(iteration_proxy_value const&) = default;\n    iteration_proxy_value& operator=(iteration_proxy_value const&) = default;\n    // older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions\n    iteration_proxy_value(iteration_proxy_value&&)\n    noexcept(std::is_nothrow_move_constructible<IteratorType>::value\n             && std::is_nothrow_move_constructible<string_type>::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations)\n    iteration_proxy_value& operator=(iteration_proxy_value&&)\n    noexcept(std::is_nothrow_move_assignable<IteratorType>::value\n             && std::is_nothrow_move_assignable<string_type>::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations)\n    ~iteration_proxy_value() = default;\n\n    /// dereference operator (needed for range-based for)\n    const iteration_proxy_value& operator*() const\n    {\n        return *this;\n    }\n\n    /// increment operator (needed for range-based for)\n    iteration_proxy_value& operator++()\n    {\n        ++anchor;\n        ++array_index;\n\n        return *this;\n    }\n\n    iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)\n    {\n        auto tmp = iteration_proxy_value(anchor, array_index);\n        ++anchor;\n        ++array_index;\n        return tmp;\n    }\n\n    /// equality operator (needed for InputIterator)\n    bool operator==(const iteration_proxy_value& o) const\n    {\n        return anchor == o.anchor;\n    }\n\n    /// inequality operator (needed for range-based for)\n    bool operator!=(const iteration_proxy_value& o) const\n    {\n        return anchor != o.anchor;\n    }\n\n    /// return key of the iterator\n    const string_type& key() const\n    {\n        JSON_ASSERT(anchor.m_object != nullptr);\n\n        switch (anchor.m_object->type())\n        {\n            // use integer array index as key\n            case value_t::array:\n            {\n                if (array_index != array_index_last)\n                {\n                    int_to_string( array_index_str, array_index );\n                    array_index_last = array_index;\n                }\n                return array_index_str;\n            }\n\n            // use key from the object\n            case value_t::object:\n                return anchor.key();\n\n            // use an empty key for all primitive types\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return empty_str;\n        }\n    }\n\n    /// return value of the iterator\n    typename IteratorType::reference value() const\n    {\n        return anchor.value();\n    }\n};\n\n/// proxy class for the items() function\ntemplate<typename IteratorType> class iteration_proxy\n{\n  private:\n    /// the container to iterate\n    typename IteratorType::pointer container = nullptr;\n\n  public:\n    explicit iteration_proxy() = default;\n\n    /// construct iteration proxy from a container\n    explicit iteration_proxy(typename IteratorType::reference cont) noexcept\n        : container(&cont) {}\n\n    iteration_proxy(iteration_proxy const&) = default;\n    iteration_proxy& operator=(iteration_proxy const&) = default;\n    iteration_proxy(iteration_proxy&&) noexcept = default;\n    iteration_proxy& operator=(iteration_proxy&&) noexcept = default;\n    ~iteration_proxy() = default;\n\n    /// return iterator begin (needed for range-based for)\n    iteration_proxy_value<IteratorType> begin() const noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container->begin());\n    }\n\n    /// return iterator end (needed for range-based for)\n    iteration_proxy_value<IteratorType> end() const noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container->end());\n    }\n};\n\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())\n{\n    return i.key();\n}\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())\n{\n    return i.value();\n}\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// The Addition to the STD Namespace is required to add\n// Structured Bindings Support to the iteration_proxy_value class\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\nnamespace std\n{\n\n#if defined(__clang__)\n    // Fix: https://github.com/nlohmann/json/issues/1401\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wmismatched-tags\"\n#endif\ntemplate<typename IteratorType>\nclass tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>> // NOLINT(cert-dcl58-cpp)\n    : public std::integral_constant<std::size_t, 2> {};\n\ntemplate<std::size_t N, typename IteratorType>\nclass tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >> // NOLINT(cert-dcl58-cpp)\n{\n  public:\n    using type = decltype(\n                     get<N>(std::declval <\n                            ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));\n};\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n\n}  // namespace std\n\n#if JSON_HAS_RANGES\n    template <typename IteratorType>\n    inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;\n#endif\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/iterators/iterator_traits.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <iterator> // random_access_iterator_tag\n\n#include <nlohmann/detail/abi_macros.hpp>\n#include <nlohmann/detail/meta/void_t.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename It, typename = void>\nstruct iterator_types {};\n\ntemplate<typename It>\nstruct iterator_types <\n    It,\n    void_t<typename It::difference_type, typename It::value_type, typename It::pointer,\n    typename It::reference, typename It::iterator_category >>\n{\n    using difference_type = typename It::difference_type;\n    using value_type = typename It::value_type;\n    using pointer = typename It::pointer;\n    using reference = typename It::reference;\n    using iterator_category = typename It::iterator_category;\n};\n\n// This is required as some compilers implement std::iterator_traits in a way that\n// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.\ntemplate<typename T, typename = void>\nstruct iterator_traits\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>\n    : iterator_types<T>\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>\n{\n    using iterator_category = std::random_access_iterator_tag;\n    using value_type = T;\n    using difference_type = ptrdiff_t;\n    using pointer = T*;\n    using reference = T&;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/iterators/json_reverse_iterator.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstddef> // ptrdiff_t\n#include <iterator> // reverse_iterator\n#include <utility> // declval\n\n#include <nlohmann/detail/abi_macros.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n//////////////////////\n// reverse_iterator //\n//////////////////////\n\n/*!\n@brief a template for a reverse iterator class\n\n@tparam Base the base iterator type to reverse. Valid types are @ref\niterator (to create @ref reverse_iterator) and @ref const_iterator (to\ncreate @ref const_reverse_iterator).\n\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):\n  It is possible to write to the pointed-to element (only if @a Base is\n  @ref iterator).\n\n@since version 1.0.0\n*/\ntemplate<typename Base>\nclass json_reverse_iterator : public std::reverse_iterator<Base>\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    /// shortcut to the reverse iterator adapter\n    using base_iterator = std::reverse_iterator<Base>;\n    /// the reference type for the pointed-to element\n    using reference = typename Base::reference;\n\n    /// create reverse iterator from iterator\n    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept\n        : base_iterator(it) {}\n\n    /// create reverse iterator from base class\n    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}\n\n    /// post-increment (it++)\n    json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));\n    }\n\n    /// pre-increment (++it)\n    json_reverse_iterator& operator++()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator++());\n    }\n\n    /// post-decrement (it--)\n    json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));\n    }\n\n    /// pre-decrement (--it)\n    json_reverse_iterator& operator--()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator--());\n    }\n\n    /// add to iterator\n    json_reverse_iterator& operator+=(difference_type i)\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));\n    }\n\n    /// add to iterator\n    json_reverse_iterator operator+(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));\n    }\n\n    /// subtract from iterator\n    json_reverse_iterator operator-(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));\n    }\n\n    /// return difference\n    difference_type operator-(const json_reverse_iterator& other) const\n    {\n        return base_iterator(*this) - base_iterator(other);\n    }\n\n    /// access to successor\n    reference operator[](difference_type n) const\n    {\n        return *(this->operator+(n));\n    }\n\n    /// return the key of an object iterator\n    auto key() const -> decltype(std::declval<Base>().key())\n    {\n        auto it = --this->base();\n        return it.key();\n    }\n\n    /// return the value of an iterator\n    reference value() const\n    {\n        auto it = --this->base();\n        return it.operator * ();\n    }\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/iterators/primitive_iterator.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstddef> // ptrdiff_t\n#include <limits>  // numeric_limits\n\n#include <nlohmann/detail/macro_scope.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/*\n@brief an iterator for primitive JSON types\n\nThis class models an iterator for primitive JSON types (boolean, number,\nstring). Its only purpose is to allow the iterator/const_iterator classes\nto \"iterate\" over primitive values. Internally, the iterator is modeled by\na `difference_type` variable. Value begin_value (`0`) models the begin and\nend_value (`1`) models past the end.\n*/\nclass primitive_iterator_t\n{\n  private:\n    using difference_type = std::ptrdiff_t;\n    static constexpr difference_type begin_value = 0;\n    static constexpr difference_type end_value = begin_value + 1;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /// iterator as signed integer type\n    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();\n\n  public:\n    constexpr difference_type get_value() const noexcept\n    {\n        return m_it;\n    }\n\n    /// set iterator to a defined beginning\n    void set_begin() noexcept\n    {\n        m_it = begin_value;\n    }\n\n    /// set iterator to a defined past the end\n    void set_end() noexcept\n    {\n        m_it = end_value;\n    }\n\n    /// return whether the iterator can be dereferenced\n    constexpr bool is_begin() const noexcept\n    {\n        return m_it == begin_value;\n    }\n\n    /// return whether the iterator is at end\n    constexpr bool is_end() const noexcept\n    {\n        return m_it == end_value;\n    }\n\n    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it == rhs.m_it;\n    }\n\n    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it < rhs.m_it;\n    }\n\n    primitive_iterator_t operator+(difference_type n) noexcept\n    {\n        auto result = *this;\n        result += n;\n        return result;\n    }\n\n    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it - rhs.m_it;\n    }\n\n    primitive_iterator_t& operator++() noexcept\n    {\n        ++m_it;\n        return *this;\n    }\n\n    primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp)\n    {\n        auto result = *this;\n        ++m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator--() noexcept\n    {\n        --m_it;\n        return *this;\n    }\n\n    primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp)\n    {\n        auto result = *this;\n        --m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator+=(difference_type n) noexcept\n    {\n        m_it += n;\n        return *this;\n    }\n\n    primitive_iterator_t& operator-=(difference_type n) noexcept\n    {\n        m_it -= n;\n        return *this;\n    }\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/json_custom_base_class.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <type_traits> // conditional, is_same\n\n#include <nlohmann/detail/abi_macros.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/*!\n@brief Default base class of the @ref basic_json class.\n\nSo that the correct implementations of the copy / move ctors / assign operators\nof @ref basic_json do not require complex case distinctions\n(no base class / custom base class used as customization point),\n@ref basic_json always has a base class.\nBy default, this class is used because it is empty and thus has no effect\non the behavior of @ref basic_json.\n*/\nstruct json_default_base {};\n\ntemplate<class T>\nusing json_base_class = typename std::conditional <\n                        std::is_same<T, void>::value,\n                        json_default_base,\n                        T\n                        >::type;\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/json_pointer.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <algorithm> // all_of\n#include <cctype> // isdigit\n#include <cerrno> // errno, ERANGE\n#include <cstdlib> // strtoull\n#ifndef JSON_NO_IO\n    #include <iosfwd> // ostream\n#endif  // JSON_NO_IO\n#include <limits> // max\n#include <numeric> // accumulate\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/string_concat.hpp>\n#include <nlohmann/detail/string_escape.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document\n/// @sa https://json.nlohmann.me/api/json_pointer/\ntemplate<typename RefStringType>\nclass json_pointer\n{\n    // allow basic_json to access private members\n    NLOHMANN_BASIC_JSON_TPL_DECLARATION\n    friend class basic_json;\n\n    template<typename>\n    friend class json_pointer;\n\n    template<typename T>\n    struct string_t_helper\n    {\n        using type = T;\n    };\n\n    NLOHMANN_BASIC_JSON_TPL_DECLARATION\n    struct string_t_helper<NLOHMANN_BASIC_JSON_TPL>\n    {\n        using type = StringType;\n    };\n\n  public:\n    // for backwards compatibility accept BasicJsonType\n    using string_t = typename string_t_helper<RefStringType>::type;\n\n    /// @brief create JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/\n    explicit json_pointer(const string_t& s = \"\")\n        : reference_tokens(split(s))\n    {}\n\n    /// @brief return a string representation of the JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/to_string/\n    string_t to_string() const\n    {\n        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),\n                               string_t{},\n                               [](const string_t& a, const string_t& b)\n        {\n            return detail::concat(a, '/', detail::escape(b));\n        });\n    }\n\n    /// @brief return a string representation of the JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())\n    operator string_t() const\n    {\n        return to_string();\n    }\n\n#ifndef JSON_NO_IO\n    /// @brief write string representation of the JSON pointer to stream\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/\n    friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr)\n    {\n        o << ptr.to_string();\n        return o;\n    }\n#endif\n\n    /// @brief append another JSON pointer at the end of this JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/\n    json_pointer& operator/=(const json_pointer& ptr)\n    {\n        reference_tokens.insert(reference_tokens.end(),\n                                ptr.reference_tokens.begin(),\n                                ptr.reference_tokens.end());\n        return *this;\n    }\n\n    /// @brief append an unescaped reference token at the end of this JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/\n    json_pointer& operator/=(string_t token)\n    {\n        push_back(std::move(token));\n        return *this;\n    }\n\n    /// @brief append an array index at the end of this JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/\n    json_pointer& operator/=(std::size_t array_idx)\n    {\n        return *this /= std::to_string(array_idx);\n    }\n\n    /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/\n    friend json_pointer operator/(const json_pointer& lhs,\n                                  const json_pointer& rhs)\n    {\n        return json_pointer(lhs) /= rhs;\n    }\n\n    /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/\n    friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)\n    {\n        return json_pointer(lhs) /= std::move(token);\n    }\n\n    /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/\n    friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx)\n    {\n        return json_pointer(lhs) /= array_idx;\n    }\n\n    /// @brief returns the parent of this JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/\n    json_pointer parent_pointer() const\n    {\n        if (empty())\n        {\n            return *this;\n        }\n\n        json_pointer res = *this;\n        res.pop_back();\n        return res;\n    }\n\n    /// @brief remove last reference token\n    /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/\n    void pop_back()\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", nullptr));\n        }\n\n        reference_tokens.pop_back();\n    }\n\n    /// @brief return last reference token\n    /// @sa https://json.nlohmann.me/api/json_pointer/back/\n    const string_t& back() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", nullptr));\n        }\n\n        return reference_tokens.back();\n    }\n\n    /// @brief append an unescaped token at the end of the reference pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/push_back/\n    void push_back(const string_t& token)\n    {\n        reference_tokens.push_back(token);\n    }\n\n    /// @brief append an unescaped token at the end of the reference pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/push_back/\n    void push_back(string_t&& token)\n    {\n        reference_tokens.push_back(std::move(token));\n    }\n\n    /// @brief return whether pointer points to the root document\n    /// @sa https://json.nlohmann.me/api/json_pointer/empty/\n    bool empty() const noexcept\n    {\n        return reference_tokens.empty();\n    }\n\n  private:\n    /*!\n    @param[in] s  reference token to be converted into an array index\n\n    @return integer representation of @a s\n\n    @throw parse_error.106  if an array index begins with '0'\n    @throw parse_error.109  if an array index begins not with a digit\n    @throw out_of_range.404 if string @a s could not be converted to an integer\n    @throw out_of_range.410 if an array index exceeds size_type\n    */\n    template<typename BasicJsonType>\n    static typename BasicJsonType::size_type array_index(const string_t& s)\n    {\n        using size_type = typename BasicJsonType::size_type;\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))\n        {\n            JSON_THROW(detail::parse_error::create(106, 0, detail::concat(\"array index '\", s, \"' must not begin with '0'\"), nullptr));\n        }\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))\n        {\n            JSON_THROW(detail::parse_error::create(109, 0, detail::concat(\"array index '\", s, \"' is not a number\"), nullptr));\n        }\n\n        const char* p = s.c_str();\n        char* p_end = nullptr; // NOLINT(misc-const-correctness)\n        errno = 0; // strtoull doesn't reset errno\n        const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)\n        if (p == p_end // invalid input or empty string\n                || errno == ERANGE // out of range\n                || JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read\n        {\n            JSON_THROW(detail::out_of_range::create(404, detail::concat(\"unresolved reference token '\", s, \"'\"), nullptr));\n        }\n\n        // only triggered on special platforms (like 32bit), see also\n        // https://github.com/nlohmann/json/pull/2203\n        if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)()))  // NOLINT(runtime/int)\n        {\n            JSON_THROW(detail::out_of_range::create(410, detail::concat(\"array index \", s, \" exceeds size_type\"), nullptr));   // LCOV_EXCL_LINE\n        }\n\n        return static_cast<size_type>(res);\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    json_pointer top() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", nullptr));\n        }\n\n        json_pointer result = *this;\n        result.reference_tokens = {reference_tokens[0]};\n        return result;\n    }\n\n  private:\n    /*!\n    @brief create and return a reference to the pointed to value\n\n    @complexity Linear in the number of reference tokens.\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.313 if value cannot be unflattened\n    */\n    template<typename BasicJsonType>\n    BasicJsonType& get_and_create(BasicJsonType& j) const\n    {\n        auto* result = &j;\n\n        // in case no reference tokens exist, return a reference to the JSON value\n        // j which will be overwritten by a primitive value\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (result->type())\n            {\n                case detail::value_t::null:\n                {\n                    if (reference_token == \"0\")\n                    {\n                        // start a new array if the reference token is 0\n                        result = &result->operator[](0);\n                    }\n                    else\n                    {\n                        // start a new object otherwise\n                        result = &result->operator[](reference_token);\n                    }\n                    break;\n                }\n\n                case detail::value_t::object:\n                {\n                    // create an entry in the object\n                    result = &result->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    // create an entry in the array\n                    result = &result->operator[](array_index<BasicJsonType>(reference_token));\n                    break;\n                }\n\n                /*\n                The following code is only reached if there exists a reference\n                token _and_ the current value is primitive. In this case, we have\n                an error situation, because primitive values may only occur as\n                a single value; that is, with an empty list of reference tokens.\n                */\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::type_error::create(313, \"invalid value to unflatten\", &j));\n            }\n        }\n\n        return *result;\n    }\n\n    /*!\n    @brief return a reference to the pointed to value\n\n    @note This version does not throw if a value is not present, but tries to\n          create nested values instead. For instance, calling this function\n          with pointer `\"/this/that\"` on a null value is equivalent to calling\n          `operator[](\"this\").operator[](\"that\")` on that value, effectively\n          changing the null value to an object.\n\n    @param[in] ptr  a JSON value\n\n    @return reference to the JSON value pointed to by the JSON pointer\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    template<typename BasicJsonType>\n    BasicJsonType& get_unchecked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            // convert null values to arrays or objects before continuing\n            if (ptr->is_null())\n            {\n                // check if the reference token is a number\n                const bool nums =\n                    std::all_of(reference_token.begin(), reference_token.end(),\n                                [](const unsigned char x)\n                {\n                    return std::isdigit(x);\n                });\n\n                // change value to an array for numbers or \"-\" or to object otherwise\n                *ptr = (nums || reference_token == \"-\")\n                       ? detail::value_t::array\n                       : detail::value_t::object;\n            }\n\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (reference_token == \"-\")\n                    {\n                        // explicitly treat \"-\" as index beyond the end\n                        ptr = &ptr->operator[](ptr->m_data.m_value.array->size());\n                    }\n                    else\n                    {\n                        // convert array index to number; unchecked access\n                        ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));\n                    }\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, detail::concat(\"unresolved reference token '\", reference_token, \"'\"), ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    template<typename BasicJsonType>\n    BasicJsonType& get_checked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402, detail::concat(\n                                \"array index '-' (\", std::to_string(ptr->m_data.m_value.array->size()),\n                                \") is out of range\"), ptr));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index<BasicJsonType>(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, detail::concat(\"unresolved reference token '\", reference_token, \"'\"), ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @brief return a const reference to the pointed to value\n\n    @param[in] ptr  a JSON value\n\n    @return const reference to the JSON value pointed to by the JSON\n    pointer\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    template<typename BasicJsonType>\n    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" cannot be used for const access\n                        JSON_THROW(detail::out_of_range::create(402, detail::concat(\"array index '-' (\", std::to_string(ptr->m_data.m_value.array->size()), \") is out of range\"), ptr));\n                    }\n\n                    // use unchecked array access\n                    ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, detail::concat(\"unresolved reference token '\", reference_token, \"'\"), ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    template<typename BasicJsonType>\n    const BasicJsonType& get_checked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402, detail::concat(\n                                \"array index '-' (\", std::to_string(ptr->m_data.m_value.array->size()),\n                                \") is out of range\"), ptr));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index<BasicJsonType>(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, detail::concat(\"unresolved reference token '\", reference_token, \"'\"), ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    */\n    template<typename BasicJsonType>\n    bool contains(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    if (!ptr->contains(reference_token))\n                    {\n                        // we did not find the key in the object\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !(\"0\" <= reference_token && reference_token <= \"9\")))\n                    {\n                        // invalid char\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))\n                        {\n                            // the first char should be between '1' and '9'\n                            return false;\n                        }\n                        for (std::size_t i = 1; i < reference_token.size(); i++)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))\n                            {\n                                // other char should be between '0' and '9'\n                                return false;\n                            }\n                        }\n                    }\n\n                    const auto idx = array_index<BasicJsonType>(reference_token);\n                    if (idx >= ptr->size())\n                    {\n                        // index out of range\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](idx);\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                {\n                    // we do not expect primitive values if there is still a\n                    // reference token to process\n                    return false;\n                }\n            }\n        }\n\n        // no reference token left means we found a primitive value\n        return true;\n    }\n\n    /*!\n    @brief split the string input to reference tokens\n\n    @note This function is only called by the json_pointer constructor.\n          All exceptions below are documented there.\n\n    @throw parse_error.107  if the pointer is not empty or begins with '/'\n    @throw parse_error.108  if character '~' is not followed by '0' or '1'\n    */\n    static std::vector<string_t> split(const string_t& reference_string)\n    {\n        std::vector<string_t> result;\n\n        // special case: empty reference string -> no reference tokens\n        if (reference_string.empty())\n        {\n            return result;\n        }\n\n        // check if a nonempty reference string begins with slash\n        if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))\n        {\n            JSON_THROW(detail::parse_error::create(107, 1, detail::concat(\"JSON pointer must be empty or begin with '/' - was: '\", reference_string, \"'\"), nullptr));\n        }\n\n        // extract the reference tokens:\n        // - slash: position of the last read slash (or end of string)\n        // - start: position after the previous slash\n        for (\n            // search for the first slash after the first character\n            std::size_t slash = reference_string.find_first_of('/', 1),\n            // set the beginning of the first reference token\n            start = 1;\n            // we can stop if start == 0 (if slash == string_t::npos)\n            start != 0;\n            // set the beginning of the next reference token\n            // (will eventually be 0 if slash == string_t::npos)\n            start = (slash == string_t::npos) ? 0 : slash + 1,\n            // find next slash\n            slash = reference_string.find_first_of('/', start))\n        {\n            // use the text between the beginning of the reference token\n            // (start) and the last slash (slash).\n            auto reference_token = reference_string.substr(start, slash - start);\n\n            // check reference tokens are properly escaped\n            for (std::size_t pos = reference_token.find_first_of('~');\n                    pos != string_t::npos;\n                    pos = reference_token.find_first_of('~', pos + 1))\n            {\n                JSON_ASSERT(reference_token[pos] == '~');\n\n                // ~ must be followed by 0 or 1\n                if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||\n                                         (reference_token[pos + 1] != '0' &&\n                                          reference_token[pos + 1] != '1')))\n                {\n                    JSON_THROW(detail::parse_error::create(108, 0, \"escape character '~' must be followed with '0' or '1'\", nullptr));\n                }\n            }\n\n            // finally, store the reference token\n            detail::unescape(reference_token);\n            result.push_back(reference_token);\n        }\n\n        return result;\n    }\n\n  private:\n    /*!\n    @param[in] reference_string  the reference string to the current value\n    @param[in] value             the value to consider\n    @param[in,out] result        the result object to insert values to\n\n    @note Empty objects or arrays are flattened to `null`.\n    */\n    template<typename BasicJsonType>\n    static void flatten(const string_t& reference_string,\n                        const BasicJsonType& value,\n                        BasicJsonType& result)\n    {\n        switch (value.type())\n        {\n            case detail::value_t::array:\n            {\n                if (value.m_data.m_value.array->empty())\n                {\n                    // flatten empty array as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate array and use index as a reference string\n                    for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i)\n                    {\n                        flatten(detail::concat<string_t>(reference_string, '/', std::to_string(i)),\n                                value.m_data.m_value.array->operator[](i), result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::object:\n            {\n                if (value.m_data.m_value.object->empty())\n                {\n                    // flatten empty object as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate object and use keys as reference string\n                    for (const auto& element : *value.m_data.m_value.object)\n                    {\n                        flatten(detail::concat<string_t>(reference_string, '/', detail::escape(element.first)), element.second, result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::null:\n            case detail::value_t::string:\n            case detail::value_t::boolean:\n            case detail::value_t::number_integer:\n            case detail::value_t::number_unsigned:\n            case detail::value_t::number_float:\n            case detail::value_t::binary:\n            case detail::value_t::discarded:\n            default:\n            {\n                // add a primitive value with its reference string\n                result[reference_string] = value;\n                break;\n            }\n        }\n    }\n\n    /*!\n    @param[in] value  flattened JSON\n\n    @return unflattened JSON\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n    @throw type_error.313  if value cannot be unflattened\n    */\n    template<typename BasicJsonType>\n    static BasicJsonType\n    unflatten(const BasicJsonType& value)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!value.is_object()))\n        {\n            JSON_THROW(detail::type_error::create(314, \"only objects can be unflattened\", &value));\n        }\n\n        BasicJsonType result;\n\n        // iterate the JSON object values\n        for (const auto& element : *value.m_data.m_value.object)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))\n            {\n                JSON_THROW(detail::type_error::create(315, \"values in object must be primitive\", &element.second));\n            }\n\n            // Assign the value to the reference pointed to by JSON pointer. Note\n            // that if the JSON pointer is \"\" (i.e., points to the whole value),\n            // function get_and_create returns a reference to the result itself.\n            // An assignment will then create a primitive value.\n            json_pointer(element.first).get_and_create(result) = element.second;\n        }\n\n        return result;\n    }\n\n    // can't use the conversion operator because of ambiguity\n    json_pointer<string_t> convert() const&\n    {\n        json_pointer<string_t> result;\n        result.reference_tokens = reference_tokens;\n        return result;\n    }\n\n    json_pointer<string_t> convert()&&\n    {\n        json_pointer<string_t> result;\n        result.reference_tokens = std::move(reference_tokens);\n        return result;\n    }\n\n  public:\n#if JSON_HAS_THREE_WAY_COMPARISON\n    /// @brief compares two JSON pointers for equality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/\n    template<typename RefStringTypeRhs>\n    bool operator==(const json_pointer<RefStringTypeRhs>& rhs) const noexcept\n    {\n        return reference_tokens == rhs.reference_tokens;\n    }\n\n    /// @brief compares JSON pointer and string for equality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer))\n    bool operator==(const string_t& rhs) const\n    {\n        return *this == json_pointer(rhs);\n    }\n\n    /// @brief 3-way compares two JSON pointers\n    template<typename RefStringTypeRhs>\n    std::strong_ordering operator<=>(const json_pointer<RefStringTypeRhs>& rhs) const noexcept // *NOPAD*\n    {\n        return  reference_tokens <=> rhs.reference_tokens; // *NOPAD*\n    }\n#else\n    /// @brief compares two JSON pointers for equality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/\n    template<typename RefStringTypeLhs, typename RefStringTypeRhs>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,\n                           const json_pointer<RefStringTypeRhs>& rhs) noexcept;\n\n    /// @brief compares JSON pointer and string for equality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/\n    template<typename RefStringTypeLhs, typename StringType>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,\n                           const StringType& rhs);\n\n    /// @brief compares string and JSON pointer for equality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/\n    template<typename RefStringTypeRhs, typename StringType>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator==(const StringType& lhs,\n                           const json_pointer<RefStringTypeRhs>& rhs);\n\n    /// @brief compares two JSON pointers for inequality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/\n    template<typename RefStringTypeLhs, typename RefStringTypeRhs>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,\n                           const json_pointer<RefStringTypeRhs>& rhs) noexcept;\n\n    /// @brief compares JSON pointer and string for inequality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/\n    template<typename RefStringTypeLhs, typename StringType>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,\n                           const StringType& rhs);\n\n    /// @brief compares string and JSON pointer for inequality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/\n    template<typename RefStringTypeRhs, typename StringType>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator!=(const StringType& lhs,\n                           const json_pointer<RefStringTypeRhs>& rhs);\n\n    /// @brief compares two JSON pointer for less-than\n    template<typename RefStringTypeLhs, typename RefStringTypeRhs>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator<(const json_pointer<RefStringTypeLhs>& lhs,\n                          const json_pointer<RefStringTypeRhs>& rhs) noexcept;\n#endif\n\n  private:\n    /// the reference tokens\n    std::vector<string_t> reference_tokens;\n};\n\n#if !JSON_HAS_THREE_WAY_COMPARISON\n// functions cannot be defined inside the class due to ODR violations\ntemplate<typename RefStringTypeLhs, typename RefStringTypeRhs>\ninline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,\n                       const json_pointer<RefStringTypeRhs>& rhs) noexcept\n{\n    return lhs.reference_tokens == rhs.reference_tokens;\n}\n\ntemplate<typename RefStringTypeLhs,\n         typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>\nJSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))\ninline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,\n                       const StringType& rhs)\n{\n    return lhs == json_pointer<RefStringTypeLhs>(rhs);\n}\n\ntemplate<typename RefStringTypeRhs,\n         typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>\nJSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))\ninline bool operator==(const StringType& lhs,\n                       const json_pointer<RefStringTypeRhs>& rhs)\n{\n    return json_pointer<RefStringTypeRhs>(lhs) == rhs;\n}\n\ntemplate<typename RefStringTypeLhs, typename RefStringTypeRhs>\ninline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,\n                       const json_pointer<RefStringTypeRhs>& rhs) noexcept\n{\n    return !(lhs == rhs);\n}\n\ntemplate<typename RefStringTypeLhs,\n         typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>\nJSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))\ninline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,\n                       const StringType& rhs)\n{\n    return !(lhs == rhs);\n}\n\ntemplate<typename RefStringTypeRhs,\n         typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>\nJSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))\ninline bool operator!=(const StringType& lhs,\n                       const json_pointer<RefStringTypeRhs>& rhs)\n{\n    return !(lhs == rhs);\n}\n\ntemplate<typename RefStringTypeLhs, typename RefStringTypeRhs>\ninline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,\n                      const json_pointer<RefStringTypeRhs>& rhs) noexcept\n{\n    return lhs.reference_tokens < rhs.reference_tokens;\n}\n#endif\n\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/json_ref.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <initializer_list>\n#include <utility>\n\n#include <nlohmann/detail/abi_macros.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename BasicJsonType>\nclass json_ref\n{\n  public:\n    using value_type = BasicJsonType;\n\n    json_ref(value_type&& value)\n        : owned_value(std::move(value))\n    {}\n\n    json_ref(const value_type& value)\n        : value_ref(&value)\n    {}\n\n    json_ref(std::initializer_list<json_ref> init)\n        : owned_value(init)\n    {}\n\n    template <\n        class... Args,\n        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >\n    json_ref(Args && ... args)\n        : owned_value(std::forward<Args>(args)...)\n    {}\n\n    // class should be movable only\n    json_ref(json_ref&&) noexcept = default;\n    json_ref(const json_ref&) = delete;\n    json_ref& operator=(const json_ref&) = delete;\n    json_ref& operator=(json_ref&&) = delete;\n    ~json_ref() = default;\n\n    value_type moved_or_copied() const\n    {\n        if (value_ref == nullptr)\n        {\n            return std::move(owned_value);\n        }\n        return *value_ref;\n    }\n\n    value_type const& operator*() const\n    {\n        return value_ref ? *value_ref : owned_value;\n    }\n\n    value_type const* operator->() const\n    {\n        return &** this;\n    }\n\n  private:\n    mutable value_type owned_value = nullptr;\n    value_type const* value_ref = nullptr;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/macro_unscope.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n// restore clang diagnostic settings\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n\n// clean up\n#undef JSON_ASSERT\n#undef JSON_INTERNAL_CATCH\n#undef JSON_THROW\n#undef JSON_PRIVATE_UNLESS_TESTED\n#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION\n#undef NLOHMANN_BASIC_JSON_TPL\n#undef JSON_EXPLICIT\n#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL\n#undef JSON_INLINE_VARIABLE\n#undef JSON_NO_UNIQUE_ADDRESS\n#undef JSON_DISABLE_ENUM_SERIALIZATION\n#undef JSON_USE_GLOBAL_UDLS\n\n#ifndef JSON_TEST_KEEP_MACROS\n    #undef JSON_CATCH\n    #undef JSON_TRY\n    #undef JSON_HAS_CPP_11\n    #undef JSON_HAS_CPP_14\n    #undef JSON_HAS_CPP_17\n    #undef JSON_HAS_CPP_20\n    #undef JSON_HAS_CPP_23\n    #undef JSON_HAS_CPP_26\n    #undef JSON_HAS_FILESYSTEM\n    #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM\n    #undef JSON_HAS_THREE_WAY_COMPARISON\n    #undef JSON_HAS_RANGES\n    #undef JSON_HAS_STATIC_RTTI\n    #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON\n#endif\n\n#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/meta/call_std/begin.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <nlohmann/detail/macro_scope.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\nNLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin);\n\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/meta/call_std/end.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <nlohmann/detail/macro_scope.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\nNLOHMANN_CAN_CALL_STD_FUNC_IMPL(end);\n\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/meta/cpp_future.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-FileCopyrightText: 2018 The Abseil Authors\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type\n#include <utility> // index_sequence, make_index_sequence, index_sequence_for\n\n#include <nlohmann/detail/macro_scope.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename T>\nusing uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;\n\n#ifdef JSON_HAS_CPP_14\n\n// the following utilities are natively available in C++14\nusing std::enable_if_t;\nusing std::index_sequence;\nusing std::make_index_sequence;\nusing std::index_sequence_for;\n\n#else\n\n// alias templates to reduce boilerplate\ntemplate<bool B, typename T = void>\nusing enable_if_t = typename std::enable_if<B, T>::type;\n\n// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h\n// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0.\n\n//// START OF CODE FROM GOOGLE ABSEIL\n\n// integer_sequence\n//\n// Class template representing a compile-time integer sequence. An instantiation\n// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its\n// type through its template arguments (which is a common need when\n// working with C++11 variadic templates). `absl::integer_sequence` is designed\n// to be a drop-in replacement for C++14's `std::integer_sequence`.\n//\n// Example:\n//\n//   template< class T, T... Ints >\n//   void user_function(integer_sequence<T, Ints...>);\n//\n//   int main()\n//   {\n//     // user_function's `T` will be deduced to `int` and `Ints...`\n//     // will be deduced to `0, 1, 2, 3, 4`.\n//     user_function(make_integer_sequence<int, 5>());\n//   }\ntemplate <typename T, T... Ints>\nstruct integer_sequence\n{\n    using value_type = T;\n    static constexpr std::size_t size() noexcept\n    {\n        return sizeof...(Ints);\n    }\n};\n\n// index_sequence\n//\n// A helper template for an `integer_sequence` of `size_t`,\n// `absl::index_sequence` is designed to be a drop-in replacement for C++14's\n// `std::index_sequence`.\ntemplate <size_t... Ints>\nusing index_sequence = integer_sequence<size_t, Ints...>;\n\nnamespace utility_internal\n{\n\ntemplate <typename Seq, size_t SeqSize, size_t Rem>\nstruct Extend;\n\n// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.\ntemplate <typename T, T... Ints, size_t SeqSize>\nstruct Extend<integer_sequence<T, Ints...>, SeqSize, 0>\n{\n    using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >;\n};\n\ntemplate <typename T, T... Ints, size_t SeqSize>\nstruct Extend<integer_sequence<T, Ints...>, SeqSize, 1>\n{\n    using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >;\n};\n\n// Recursion helper for 'make_integer_sequence<T, N>'.\n// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.\ntemplate <typename T, size_t N>\nstruct Gen\n{\n    using type =\n        typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type;\n};\n\ntemplate <typename T>\nstruct Gen<T, 0>\n{\n    using type = integer_sequence<T>;\n};\n\n}  // namespace utility_internal\n\n// Compile-time sequences of integers\n\n// make_integer_sequence\n//\n// This template alias is equivalent to\n// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in\n// replacement for C++14's `std::make_integer_sequence`.\ntemplate <typename T, T N>\nusing make_integer_sequence = typename utility_internal::Gen<T, N>::type;\n\n// make_index_sequence\n//\n// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,\n// and is designed to be a drop-in replacement for C++14's\n// `std::make_index_sequence`.\ntemplate <size_t N>\nusing make_index_sequence = make_integer_sequence<size_t, N>;\n\n// index_sequence_for\n//\n// Converts a typename pack into an index sequence of the same length, and\n// is designed to be a drop-in replacement for C++14's\n// `std::index_sequence_for()`\ntemplate <typename... Ts>\nusing index_sequence_for = make_index_sequence<sizeof...(Ts)>;\n\n//// END OF CODE FROM GOOGLE ABSEIL\n\n#endif\n\n// dispatch utility (taken from ranges-v3)\ntemplate<unsigned N> struct priority_tag : priority_tag < N - 1 > {};\ntemplate<> struct priority_tag<0> {};\n\n// taken from ranges-v3\ntemplate<typename T>\nstruct static_const\n{\n    static JSON_INLINE_VARIABLE constexpr T value{};\n};\n\n#ifndef JSON_HAS_CPP_17\n    template<typename T>\n    constexpr T static_const<T>::value;\n#endif\n\ntemplate<typename T, typename... Args>\nconstexpr std::array<T, sizeof...(Args)> make_array(Args&& ... args)\n{\n    return std::array<T, sizeof...(Args)> {{static_cast<T>(std::forward<Args>(args))...}};\n}\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/meta/detected.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <type_traits>\n\n#include <nlohmann/detail/meta/void_t.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n// https://en.cppreference.com/w/cpp/experimental/is_detected\nstruct nonesuch\n{\n    nonesuch() = delete;\n    ~nonesuch() = delete;\n    nonesuch(nonesuch const&) = delete;\n    nonesuch(nonesuch const&&) = delete;\n    void operator=(nonesuch const&) = delete;\n    void operator=(nonesuch&&) = delete;\n};\n\ntemplate<class Default,\n         class AlwaysVoid,\n         template<class...> class Op,\n         class... Args>\nstruct detector\n{\n    using value_t = std::false_type;\n    using type = Default;\n};\n\ntemplate<class Default, template<class...> class Op, class... Args>\nstruct detector<Default, void_t<Op<Args...>>, Op, Args...>\n{\n    using value_t = std::true_type;\n    using type = Op<Args...>;\n};\n\ntemplate<template<class...> class Op, class... Args>\nusing is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;\n\ntemplate<template<class...> class Op, class... Args>\nstruct is_detected_lazy : is_detected<Op, Args...> { };\n\ntemplate<template<class...> class Op, class... Args>\nusing detected_t = typename detector<nonesuch, void, Op, Args...>::type;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or = detector<Default, void, Op, Args...>;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or_t = typename detected_or<Default, Op, Args...>::type;\n\ntemplate<class Expected, template<class...> class Op, class... Args>\nusing is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;\n\ntemplate<class To, template<class...> class Op, class... Args>\nusing is_detected_convertible =\n    std::is_convertible<detected_t<Op, Args...>, To>;\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/meta/identity_tag.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <nlohmann/detail/abi_macros.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n// dispatching helper struct\ntemplate <class T> struct identity_tag {};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/meta/std_fs.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <nlohmann/detail/macro_scope.hpp>\n\n#if JSON_HAS_EXPERIMENTAL_FILESYSTEM\n#include <experimental/filesystem>\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\nnamespace std_fs = std::experimental::filesystem;\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n#elif JSON_HAS_FILESYSTEM\n#include <filesystem> // NOLINT(build/c++17)\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\nnamespace std_fs = std::filesystem;\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n#endif\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/meta/type_traits.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <limits> // numeric_limits\n#include <string> // char_traits\n#include <tuple> // tuple\n#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type\n#include <utility> // declval\n#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L\n    #include <cstddef> // byte\n#endif\n#include <nlohmann/detail/iterators/iterator_traits.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/call_std/begin.hpp>\n#include <nlohmann/detail/meta/call_std/end.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/meta/detected.hpp>\n#include <nlohmann/json_fwd.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n/*!\n@brief detail namespace with internal helper functions\n\nThis namespace collects functions that should not be exposed,\nimplementations of some @ref basic_json methods, and meta-programming helpers.\n\n@since version 2.1.0\n*/\nnamespace detail\n{\n\n/////////////\n// helpers //\n/////////////\n\n// Note to maintainers:\n//\n// Every trait in this file expects a non-CV-qualified type.\n// The only exceptions are in the 'aliases for detected' section\n// (i.e., those of the form: decltype(T::member_function(std::declval<T>())))\n//\n// In this case, T has to be properly CV-qualified to constraint the function arguments\n// (e.g., to_json(BasicJsonType&, const T&))\n\ntemplate<typename> struct is_basic_json : std::false_type {};\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstruct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};\n\n// used by exceptions create() member functions\n// true_type for the pointer to possibly cv-qualified basic_json or std::nullptr_t\n// false_type otherwise\ntemplate<typename BasicJsonContext>\nstruct is_basic_json_context :\n    std::integral_constant < bool,\n    is_basic_json<typename std::remove_cv<typename std::remove_pointer<BasicJsonContext>::type>::type>::value\n    || std::is_same<BasicJsonContext, std::nullptr_t>::value >\n{};\n\n//////////////////////\n// json_ref helpers //\n//////////////////////\n\ntemplate<typename>\nclass json_ref;\n\ntemplate<typename>\nstruct is_json_ref : std::false_type {};\n\ntemplate<typename T>\nstruct is_json_ref<json_ref<T>> : std::true_type {};\n\n//////////////////////////\n// aliases for detected //\n//////////////////////////\n\ntemplate<typename T>\nusing mapped_type_t = typename T::mapped_type;\n\ntemplate<typename T>\nusing key_type_t = typename T::key_type;\n\ntemplate<typename T>\nusing value_type_t = typename T::value_type;\n\ntemplate<typename T>\nusing difference_type_t = typename T::difference_type;\n\ntemplate<typename T>\nusing pointer_t = typename T::pointer;\n\ntemplate<typename T>\nusing reference_t = typename T::reference;\n\ntemplate<typename T>\nusing iterator_category_t = typename T::iterator_category;\n\ntemplate<typename T, typename... Args>\nusing to_json_function = decltype(T::to_json(std::declval<Args>()...));\n\ntemplate<typename T, typename... Args>\nusing from_json_function = decltype(T::from_json(std::declval<Args>()...));\n\ntemplate<typename T, typename U>\nusing get_template_function = decltype(std::declval<T>().template get<U>());\n\n// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_from_json : std::false_type {};\n\n// trait checking if j.get<T> is valid\n// use this trait instead of std::is_constructible or std::is_convertible,\n// both rely on, or make use of implicit conversions, and thus fail when T\n// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)\ntemplate <typename BasicJsonType, typename T>\nstruct is_getable\n{\n    static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;\n};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, from_json_function, serializer,\n        const BasicJsonType&, T&>::value;\n};\n\n// This trait checks if JSONSerializer<T>::from_json(json const&) exists\n// this overload is used for non-default-constructible user-defined-types\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_non_default_from_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<T, from_json_function, serializer,\n        const BasicJsonType&>::value;\n};\n\n// This trait checks if BasicJsonType::json_serializer<T>::to_json exists\n// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_to_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,\n        T>::value;\n};\n\ntemplate<typename T>\nusing detect_key_compare = typename T::key_compare;\n\ntemplate<typename T>\nstruct has_key_compare : std::integral_constant<bool, is_detected<detect_key_compare, T>::value> {};\n\n// obtains the actual object key comparator\ntemplate<typename BasicJsonType>\nstruct actual_object_comparator\n{\n    using object_t = typename BasicJsonType::object_t;\n    using object_comparator_t = typename BasicJsonType::default_object_comparator_t;\n    using type = typename std::conditional < has_key_compare<object_t>::value,\n          typename object_t::key_compare, object_comparator_t>::type;\n};\n\ntemplate<typename BasicJsonType>\nusing actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;\n\n/////////////////\n// char_traits //\n/////////////////\n\n// Primary template of char_traits calls std char_traits\ntemplate<typename T>\nstruct char_traits : std::char_traits<T>\n{};\n\n// Explicitly define char traits for unsigned char since it is not standard\ntemplate<>\nstruct char_traits<unsigned char> : std::char_traits<char>\n{\n    using char_type = unsigned char;\n    using int_type = uint64_t;\n\n    // Redefine to_int_type function\n    static int_type to_int_type(char_type c) noexcept\n    {\n        return static_cast<int_type>(c);\n    }\n\n    static char_type to_char_type(int_type i) noexcept\n    {\n        return static_cast<char_type>(i);\n    }\n\n    static constexpr int_type eof() noexcept\n    {\n        return static_cast<int_type>(std::char_traits<char>::eof());\n    }\n};\n\n// Explicitly define char traits for signed char since it is not standard\ntemplate<>\nstruct char_traits<signed char> : std::char_traits<char>\n{\n    using char_type = signed char;\n    using int_type = uint64_t;\n\n    // Redefine to_int_type function\n    static int_type to_int_type(char_type c) noexcept\n    {\n        return static_cast<int_type>(c);\n    }\n\n    static char_type to_char_type(int_type i) noexcept\n    {\n        return static_cast<char_type>(i);\n    }\n\n    static constexpr int_type eof() noexcept\n    {\n        return static_cast<int_type>(std::char_traits<char>::eof());\n    }\n};\n\n#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L\ntemplate<>\nstruct char_traits<std::byte> : std::char_traits<char>\n{\n    using char_type = std::byte;\n    using int_type = uint64_t;\n\n    static int_type to_int_type(char_type c) noexcept\n    {\n        return static_cast<int_type>(std::to_integer<unsigned char>(c));\n    }\n\n    static char_type to_char_type(int_type i) noexcept\n    {\n        return std::byte(static_cast<unsigned char>(i));\n    }\n\n    static constexpr int_type eof() noexcept\n    {\n        return static_cast<int_type>(std::char_traits<char>::eof());\n    }\n};\n#endif\n\n///////////////////\n// is_ functions //\n///////////////////\n\n// https://en.cppreference.com/w/cpp/types/conjunction\ntemplate<class...> struct conjunction : std::true_type { };\ntemplate<class B> struct conjunction<B> : B { };\ntemplate<class B, class... Bn>\nstruct conjunction<B, Bn...>\n: std::conditional<static_cast<bool>(B::value), conjunction<Bn...>, B>::type {};\n\n// https://en.cppreference.com/w/cpp/types/negation\ntemplate<class B> struct negation : std::integral_constant < bool, !B::value > { };\n\n// Reimplementation of is_constructible and is_default_constructible, due to them being broken for\n// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).\n// This causes compile errors in e.g., Clang 3.5 or GCC 4.9.\ntemplate <typename T>\nstruct is_default_constructible : std::is_default_constructible<T> {};\n\ntemplate <typename T1, typename T2>\nstruct is_default_constructible<std::pair<T1, T2>>\n    : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};\n\ntemplate <typename T1, typename T2>\nstruct is_default_constructible<const std::pair<T1, T2>>\n    : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};\n\ntemplate <typename... Ts>\nstruct is_default_constructible<std::tuple<Ts...>>\n    : conjunction<is_default_constructible<Ts>...> {};\n\ntemplate <typename... Ts>\nstruct is_default_constructible<const std::tuple<Ts...>>\n    : conjunction<is_default_constructible<Ts>...> {};\n\ntemplate <typename T, typename... Args>\nstruct is_constructible : std::is_constructible<T, Args...> {};\n\ntemplate <typename T1, typename T2>\nstruct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};\n\ntemplate <typename T1, typename T2>\nstruct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};\n\ntemplate <typename... Ts>\nstruct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};\n\ntemplate <typename... Ts>\nstruct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};\n\ntemplate<typename T, typename = void>\nstruct is_iterator_traits : std::false_type {};\n\ntemplate<typename T>\nstruct is_iterator_traits<iterator_traits<T>>\n{\n  private:\n    using traits = iterator_traits<T>;\n\n  public:\n    static constexpr auto value =\n        is_detected<value_type_t, traits>::value &&\n        is_detected<difference_type_t, traits>::value &&\n        is_detected<pointer_t, traits>::value &&\n        is_detected<iterator_category_t, traits>::value &&\n        is_detected<reference_t, traits>::value;\n};\n\ntemplate<typename T>\nstruct is_range\n{\n  private:\n    using t_ref = typename std::add_lvalue_reference<T>::type;\n\n    using iterator = detected_t<result_of_begin, t_ref>;\n    using sentinel = detected_t<result_of_end, t_ref>;\n\n    // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator\n    // and https://en.cppreference.com/w/cpp/iterator/sentinel_for\n    // but reimplementing these would be too much work, as a lot of other concepts are used underneath\n    static constexpr auto is_iterator_begin =\n        is_iterator_traits<iterator_traits<iterator>>::value;\n\n  public:\n    static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin;\n};\n\ntemplate<typename R>\nusing iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R&>())>>;\n\ntemplate<typename T>\nusing range_value_t = value_type_t<iterator_traits<iterator_t<T>>>;\n\n// The following implementation of is_complete_type is taken from\n// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/\n// and is written by Xiang Fan who agreed to use it in this library.\n\ntemplate<typename T, typename = void>\nstruct is_complete_type : std::false_type {};\n\ntemplate<typename T>\nstruct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType,\n         typename = void>\nstruct is_compatible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type_impl <\n    BasicJsonType, CompatibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&\n    is_detected<key_type_t, CompatibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    // macOS's is_constructible does not play well with nonesuch...\n    static constexpr bool value =\n        is_constructible<typename object_t::key_type,\n        typename CompatibleObjectType::key_type>::value &&\n        is_constructible<typename object_t::mapped_type,\n        typename CompatibleObjectType::mapped_type>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type\n    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         typename = void>\nstruct is_constructible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type_impl <\n    BasicJsonType, ConstructibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&\n    is_detected<key_type_t, ConstructibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    static constexpr bool value =\n        (is_default_constructible<ConstructibleObjectType>::value &&\n         (std::is_move_assignable<ConstructibleObjectType>::value ||\n          std::is_copy_assignable<ConstructibleObjectType>::value) &&\n         (is_constructible<typename ConstructibleObjectType::key_type,\n          typename object_t::key_type>::value &&\n          std::is_same <\n          typename object_t::mapped_type,\n          typename ConstructibleObjectType::mapped_type >::value)) ||\n        (has_from_json<BasicJsonType,\n         typename ConstructibleObjectType::mapped_type>::value ||\n         has_non_default_from_json <\n         BasicJsonType,\n         typename ConstructibleObjectType::mapped_type >::value);\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type\n    : is_constructible_object_type_impl<BasicJsonType,\n      ConstructibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleStringType>\nstruct is_compatible_string_type\n{\n    static constexpr auto value =\n        is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type\n{\n    // launder type through decltype() to fix compilation failure on ICPC\n#ifdef __INTEL_COMPILER\n    using laundered_type = decltype(std::declval<ConstructibleStringType>());\n#else\n    using laundered_type = ConstructibleStringType;\n#endif\n\n    static constexpr auto value =\n        conjunction <\n        is_constructible<laundered_type, typename BasicJsonType::string_t>,\n        is_detected_exact<typename BasicJsonType::string_t::value_type,\n        value_type_t, laundered_type >>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType, typename = void>\nstruct is_compatible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type_impl <\n    BasicJsonType, CompatibleArrayType,\n    enable_if_t <\n    is_detected<iterator_t, CompatibleArrayType>::value&&\n    is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&\n// special case for types like std::filesystem::path whose iterator's value_type are themselves\n// c.f. https://github.com/nlohmann/json/pull/3073\n    !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>\n{\n    static constexpr bool value =\n        is_constructible<BasicJsonType,\n        range_value_t<CompatibleArrayType>>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type\n    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType, typename = void>\nstruct is_constructible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t<std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value >>\n            : std::true_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t < !std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value&&\n    !is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value&&\n    is_default_constructible<ConstructibleArrayType>::value&&\n(std::is_move_assignable<ConstructibleArrayType>::value ||\n std::is_copy_assignable<ConstructibleArrayType>::value)&&\nis_detected<iterator_t, ConstructibleArrayType>::value&&\nis_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value&&\nis_detected<range_value_t, ConstructibleArrayType>::value&&\n// special case for types like std::filesystem::path whose iterator's value_type are themselves\n// c.f. https://github.com/nlohmann/json/pull/3073\n!std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value&&\nis_complete_type <\ndetected_t<range_value_t, ConstructibleArrayType >>::value >>\n{\n    using value_type = range_value_t<ConstructibleArrayType>;\n\n    static constexpr bool value =\n        std::is_same<value_type,\n        typename BasicJsonType::array_t::value_type>::value ||\n        has_from_json<BasicJsonType,\n        value_type>::value ||\n        has_non_default_from_json <\n        BasicJsonType,\n        value_type >::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type\n    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType,\n         typename = void>\nstruct is_compatible_integer_type_impl : std::false_type {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type_impl <\n    RealIntegerType, CompatibleNumberIntegerType,\n    enable_if_t < std::is_integral<RealIntegerType>::value&&\n    std::is_integral<CompatibleNumberIntegerType>::value&&\n    !std::is_same<bool, CompatibleNumberIntegerType>::value >>\n{\n    // is there an assert somewhere on overflows?\n    using RealLimits = std::numeric_limits<RealIntegerType>;\n    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;\n\n    static constexpr auto value =\n        is_constructible<RealIntegerType,\n        CompatibleNumberIntegerType>::value &&\n        CompatibleLimits::is_integer &&\n        RealLimits::is_signed == CompatibleLimits::is_signed;\n};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type\n    : is_compatible_integer_type_impl<RealIntegerType,\n      CompatibleNumberIntegerType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleType, typename = void>\nstruct is_compatible_type_impl: std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type_impl <\n    BasicJsonType, CompatibleType,\n    enable_if_t<is_complete_type<CompatibleType>::value >>\n{\n    static constexpr bool value =\n        has_to_json<BasicJsonType, CompatibleType>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type\n    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};\n\ntemplate<typename T1, typename T2>\nstruct is_constructible_tuple : std::false_type {};\n\ntemplate<typename T1, typename... Args>\nstruct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct is_json_iterator_of : std::false_type {};\n\ntemplate<typename BasicJsonType>\nstruct is_json_iterator_of<BasicJsonType, typename BasicJsonType::iterator> : std::true_type {};\n\ntemplate<typename BasicJsonType>\nstruct is_json_iterator_of<BasicJsonType, typename BasicJsonType::const_iterator> : std::true_type\n{};\n\n// checks if a given type T is a template specialization of Primary\ntemplate<template <typename...> class Primary, typename T>\nstruct is_specialization_of : std::false_type {};\n\ntemplate<template <typename...> class Primary, typename... Args>\nstruct is_specialization_of<Primary, Primary<Args...>> : std::true_type {};\n\ntemplate<typename T>\nusing is_json_pointer = is_specialization_of<::nlohmann::json_pointer, uncvref_t<T>>;\n\n// checks if A and B are comparable using Compare functor\ntemplate<typename Compare, typename A, typename B, typename = void>\nstruct is_comparable : std::false_type {};\n\ntemplate<typename Compare, typename A, typename B>\nstruct is_comparable<Compare, A, B, void_t<\ndecltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>())),\ndecltype(std::declval<Compare>()(std::declval<B>(), std::declval<A>()))\n>> : std::true_type {};\n\ntemplate<typename T>\nusing detect_is_transparent = typename T::is_transparent;\n\n// type trait to check if KeyType can be used as an object key (without a BasicJsonType)\n// see is_usable_as_basic_json_key_type below\ntemplate<typename Comparator, typename ObjectKeyType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,\n         bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>\nusing is_usable_as_key_type = typename std::conditional <\n                              is_comparable<Comparator, ObjectKeyType, KeyTypeCVRef>::value\n                              && !(ExcludeObjectKeyType && std::is_same<KeyType,\n                                   ObjectKeyType>::value)\n                              && (!RequireTransparentComparator\n                                  || is_detected <detect_is_transparent, Comparator>::value)\n                              && !is_json_pointer<KeyType>::value,\n                              std::true_type,\n                              std::false_type >::type;\n\n// type trait to check if KeyType can be used as an object key\n// true if:\n//   - KeyType is comparable with BasicJsonType::object_t::key_type\n//   - if ExcludeObjectKeyType is true, KeyType is not BasicJsonType::object_t::key_type\n//   - the comparator is transparent or RequireTransparentComparator is false\n//   - KeyType is not a JSON iterator or json_pointer\ntemplate<typename BasicJsonType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,\n         bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>\nusing is_usable_as_basic_json_key_type = typename std::conditional <\n    is_usable_as_key_type<typename BasicJsonType::object_comparator_t,\n    typename BasicJsonType::object_t::key_type, KeyTypeCVRef,\n    RequireTransparentComparator, ExcludeObjectKeyType>::value\n    && !is_json_iterator_of<BasicJsonType, KeyType>::value,\n    std::true_type,\n    std::false_type >::type;\n\ntemplate<typename ObjectType, typename KeyType>\nusing detect_erase_with_key_type = decltype(std::declval<ObjectType&>().erase(std::declval<KeyType>()));\n\n// type trait to check if object_t has an erase() member functions accepting KeyType\ntemplate<typename BasicJsonType, typename KeyType>\nusing has_erase_with_key_type = typename std::conditional <\n                                is_detected <\n                                detect_erase_with_key_type,\n                                typename BasicJsonType::object_t, KeyType >::value,\n                                std::true_type,\n                                std::false_type >::type;\n\n// a naive helper to check if a type is an ordered_map (exploits the fact that\n// ordered_map inherits capacity() from std::vector)\ntemplate <typename T>\nstruct is_ordered_map\n{\n    using one = char;\n\n    struct two\n    {\n        char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n    };\n\n    template <typename C> static one test( decltype(&C::capacity) ) ;\n    template <typename C> static two test(...);\n\n    enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n};\n\n// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)\ntemplate < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >\nT conditional_static_cast(U value)\n{\n    return static_cast<T>(value);\n}\n\ntemplate<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>\nT conditional_static_cast(U value)\n{\n    return value;\n}\n\ntemplate<typename... Types>\nusing all_integral = conjunction<std::is_integral<Types>...>;\n\ntemplate<typename... Types>\nusing all_signed = conjunction<std::is_signed<Types>...>;\n\ntemplate<typename... Types>\nusing all_unsigned = conjunction<std::is_unsigned<Types>...>;\n\n// there's a disjunction trait in another PR; replace when merged\ntemplate<typename... Types>\nusing same_sign = std::integral_constant < bool,\n      all_signed<Types...>::value || all_unsigned<Types...>::value >;\n\ntemplate<typename OfType, typename T>\nusing never_out_of_range = std::integral_constant < bool,\n      (std::is_signed<OfType>::value && (sizeof(T) < sizeof(OfType)))\n      || (same_sign<OfType, T>::value && sizeof(OfType) == sizeof(T)) >;\n\ntemplate<typename OfType, typename T,\n         bool OfTypeSigned = std::is_signed<OfType>::value,\n         bool TSigned = std::is_signed<T>::value>\nstruct value_in_range_of_impl2;\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl2<OfType, T, false, false>\n{\n    static constexpr bool test(T val)\n    {\n        using CommonType = typename std::common_type<OfType, T>::type;\n        return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());\n    }\n};\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl2<OfType, T, true, false>\n{\n    static constexpr bool test(T val)\n    {\n        using CommonType = typename std::common_type<OfType, T>::type;\n        return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());\n    }\n};\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl2<OfType, T, false, true>\n{\n    static constexpr bool test(T val)\n    {\n        using CommonType = typename std::common_type<OfType, T>::type;\n        return val >= 0 && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());\n    }\n};\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl2<OfType, T, true, true>\n{\n    static constexpr bool test(T val)\n    {\n        using CommonType = typename std::common_type<OfType, T>::type;\n        return static_cast<CommonType>(val) >= static_cast<CommonType>((std::numeric_limits<OfType>::min)())\n               && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());\n    }\n};\n\ntemplate<typename OfType, typename T,\n         bool NeverOutOfRange = never_out_of_range<OfType, T>::value,\n         typename = detail::enable_if_t<all_integral<OfType, T>::value>>\nstruct value_in_range_of_impl1;\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl1<OfType, T, false>\n{\n    static constexpr bool test(T val)\n    {\n        return value_in_range_of_impl2<OfType, T>::test(val);\n    }\n};\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl1<OfType, T, true>\n{\n    static constexpr bool test(T /*val*/)\n    {\n        return true;\n    }\n};\n\ntemplate<typename OfType, typename T>\nconstexpr bool value_in_range_of(T val)\n{\n    return value_in_range_of_impl1<OfType, T>::test(val);\n}\n\ntemplate<bool Value>\nusing bool_constant = std::integral_constant<bool, Value>;\n\n///////////////////////////////////////////////////////////////////////////////\n// is_c_string\n///////////////////////////////////////////////////////////////////////////////\n\nnamespace impl\n{\n\ntemplate<typename T>\nconstexpr bool is_c_string()\n{\n    using TUnExt = typename std::remove_extent<T>::type;\n    using TUnCVExt = typename std::remove_cv<TUnExt>::type;\n    using TUnPtr = typename std::remove_pointer<T>::type;\n    using TUnCVPtr = typename std::remove_cv<TUnPtr>::type;\n    return\n        (std::is_array<T>::value && std::is_same<TUnCVExt, char>::value)\n        || (std::is_pointer<T>::value && std::is_same<TUnCVPtr, char>::value);\n}\n\n}  // namespace impl\n\n// checks whether T is a [cv] char */[cv] char[] C string\ntemplate<typename T>\nstruct is_c_string : bool_constant<impl::is_c_string<T>()> {};\n\ntemplate<typename T>\nusing is_c_string_uncvref = is_c_string<uncvref_t<T>>;\n\n///////////////////////////////////////////////////////////////////////////////\n// is_transparent\n///////////////////////////////////////////////////////////////////////////////\n\nnamespace impl\n{\n\ntemplate<typename T>\nconstexpr bool is_transparent()\n{\n    return is_detected<detect_is_transparent, T>::value;\n}\n\n}  // namespace impl\n\n// checks whether T has a member named is_transparent\ntemplate<typename T>\nstruct is_transparent : bool_constant<impl::is_transparent<T>()> {};\n\n///////////////////////////////////////////////////////////////////////////////\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/meta/void_t.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <nlohmann/detail/abi_macros.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename ...Ts> struct make_void\n{\n    using type = void;\n};\ntemplate<typename ...Ts> using void_t = typename make_void<Ts...>::type;\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/output/output_adapters.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <algorithm> // copy\n#include <cstddef> // size_t\n#include <iterator> // back_inserter\n#include <memory> // shared_ptr, make_shared\n#include <string> // basic_string\n#include <vector> // vector\n\n#ifndef JSON_NO_IO\n    #include <ios>      // streamsize\n    #include <ostream>  // basic_ostream\n#endif  // JSON_NO_IO\n\n#include <nlohmann/detail/macro_scope.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/// abstract output adapter interface\ntemplate<typename CharType> struct output_adapter_protocol\n{\n    virtual void write_character(CharType c) = 0;\n    virtual void write_characters(const CharType* s, std::size_t length) = 0;\n    virtual ~output_adapter_protocol() = default;\n\n    output_adapter_protocol() = default;\n    output_adapter_protocol(const output_adapter_protocol&) = default;\n    output_adapter_protocol(output_adapter_protocol&&) noexcept = default;\n    output_adapter_protocol& operator=(const output_adapter_protocol&) = default;\n    output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default;\n};\n\n/// a type to simplify interfaces\ntemplate<typename CharType>\nusing output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;\n\n/// output adapter for byte vectors\ntemplate<typename CharType, typename AllocatorType = std::allocator<CharType>>\nclass output_vector_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept\n        : v(vec)\n    {}\n\n    void write_character(CharType c) override\n    {\n        v.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        v.insert(v.end(), s, s + length);\n    }\n\n  private:\n    std::vector<CharType, AllocatorType>& v;\n};\n\n#ifndef JSON_NO_IO\n/// output adapter for output streams\ntemplate<typename CharType>\nclass output_stream_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept\n        : stream(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        stream.put(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        stream.write(s, static_cast<std::streamsize>(length));\n    }\n\n  private:\n    std::basic_ostream<CharType>& stream;\n};\n#endif  // JSON_NO_IO\n\n/// output adapter for basic_string\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_string_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_string_adapter(StringType& s) noexcept\n        : str(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        str.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        str.append(s, length);\n    }\n\n  private:\n    StringType& str;\n};\n\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_adapter\n{\n  public:\n    template<typename AllocatorType = std::allocator<CharType>>\n    output_adapter(std::vector<CharType, AllocatorType>& vec)\n        : oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}\n\n#ifndef JSON_NO_IO\n    output_adapter(std::basic_ostream<CharType>& s)\n        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}\n#endif  // JSON_NO_IO\n\n    output_adapter(StringType& s)\n        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}\n\n    operator output_adapter_t<CharType>()\n    {\n        return oa;\n    }\n\n  private:\n    output_adapter_t<CharType> oa = nullptr;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/detail/output/serializer.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2008 - 2009 Björn Hoehrmann <bjoern@hoehrmann.de>\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <algorithm> // reverse, remove, fill, find, none_of\n#include <array> // array\n#include <clocale> // localeconv, lconv\n#include <cmath> // labs, isfinite, isnan, signbit\n#include <cstddef> // size_t, ptrdiff_t\n#include <cstdint> // uint8_t\n#include <cstdio> // snprintf\n#include <limits> // numeric_limits\n#include <string> // string, char_traits\n#include <iomanip> // setfill, setw\n#include <type_traits> // is_same\n#include <utility> // move\n\n#include <nlohmann/detail/conversions/to_chars.hpp>\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/output/binary_writer.hpp>\n#include <nlohmann/detail/output/output_adapters.hpp>\n#include <nlohmann/detail/string_concat.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n///////////////////\n// serialization //\n///////////////////\n\n/// how to treat decoding errors\nenum class error_handler_t\n{\n    strict,  ///< throw a type_error exception in case of invalid UTF-8\n    replace, ///< replace invalid UTF-8 sequences with U+FFFD\n    ignore   ///< ignore invalid UTF-8 sequences\n};\n\ntemplate<typename BasicJsonType>\nclass serializer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using binary_char_t = typename BasicJsonType::binary_t::value_type;\n    static constexpr std::uint8_t UTF8_ACCEPT = 0;\n    static constexpr std::uint8_t UTF8_REJECT = 1;\n\n  public:\n    /*!\n    @param[in] s  output stream to serialize to\n    @param[in] ichar  indentation character to use\n    @param[in] error_handler_  how to react on decoding errors\n    */\n    serializer(output_adapter_t<char> s, const char ichar,\n               error_handler_t error_handler_ = error_handler_t::strict)\n        : o(std::move(s))\n        , loc(std::localeconv())\n        , thousands_sep(loc->thousands_sep == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))\n        , decimal_point(loc->decimal_point == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))\n        , indent_char(ichar)\n        , indent_string(512, indent_char)\n        , error_handler(error_handler_)\n    {}\n\n    // deleted because of pointer members\n    serializer(const serializer&) = delete;\n    serializer& operator=(const serializer&) = delete;\n    serializer(serializer&&) = delete;\n    serializer& operator=(serializer&&) = delete;\n    ~serializer() = default;\n\n    /*!\n    @brief internal implementation of the serialization function\n\n    This function is called by the public member function dump and organizes\n    the serialization internally. The indentation level is propagated as\n    additional parameter. In case of arrays and objects, the function is\n    called recursively.\n\n    - strings and object keys are escaped using `escape_string()`\n    - integer numbers are converted implicitly via `operator<<`\n    - floating-point numbers are converted to a string using `\"%g\"` format\n    - binary values are serialized as objects containing the subtype and the\n      byte array\n\n    @param[in] val               value to serialize\n    @param[in] pretty_print      whether the output shall be pretty-printed\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] indent_step       the indent level\n    @param[in] current_indent    the current indent level (only used internally)\n    */\n    void dump(const BasicJsonType& val,\n              const bool pretty_print,\n              const bool ensure_ascii,\n              const unsigned int indent_step,\n              const unsigned int current_indent = 0)\n    {\n        switch (val.m_data.m_type)\n        {\n            case value_t::object:\n            {\n                if (val.m_data.m_value.object->empty())\n                {\n                    o->write_characters(\"{}\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    auto i = val.m_data.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\": \", 3);\n                        dump(i->second, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_data.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\": \", 3);\n                    dump(i->second, true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_character('{');\n\n                    // first n-1 elements\n                    auto i = val.m_data.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\":\", 2);\n                        dump(i->second, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_data.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\":\", 2);\n                    dump(i->second, false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character('}');\n                }\n\n                return;\n            }\n\n            case value_t::array:\n            {\n                if (val.m_data.m_value.array->empty())\n                {\n                    o->write_characters(\"[]\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"[\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    for (auto i = val.m_data.m_value.array->cbegin();\n                            i != val.m_data.m_value.array->cend() - 1; ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        dump(*i, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_data.m_value.array->empty());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    dump(val.m_data.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character(']');\n                }\n                else\n                {\n                    o->write_character('[');\n\n                    // first n-1 elements\n                    for (auto i = val.m_data.m_value.array->cbegin();\n                            i != val.m_data.m_value.array->cend() - 1; ++i)\n                    {\n                        dump(*i, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_data.m_value.array->empty());\n                    dump(val.m_data.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character(']');\n                }\n\n                return;\n            }\n\n            case value_t::string:\n            {\n                o->write_character('\\\"');\n                dump_escaped(*val.m_data.m_value.string, ensure_ascii);\n                o->write_character('\\\"');\n                return;\n            }\n\n            case value_t::binary:\n            {\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"bytes\\\": [\", 10);\n\n                    if (!val.m_data.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_data.m_value.binary->cbegin();\n                                i != val.m_data.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_characters(\", \", 2);\n                        }\n                        dump_integer(val.m_data.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\n\", 3);\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"subtype\\\": \", 11);\n                    if (val.m_data.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_data.m_value.binary->subtype());\n                    }\n                    else\n                    {\n                        o->write_characters(\"null\", 4);\n                    }\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_characters(\"{\\\"bytes\\\":[\", 10);\n\n                    if (!val.m_data.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_data.m_value.binary->cbegin();\n                                i != val.m_data.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_character(',');\n                        }\n                        dump_integer(val.m_data.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\\"subtype\\\":\", 12);\n                    if (val.m_data.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_data.m_value.binary->subtype());\n                        o->write_character('}');\n                    }\n                    else\n                    {\n                        o->write_characters(\"null}\", 5);\n                    }\n                }\n                return;\n            }\n\n            case value_t::boolean:\n            {\n                if (val.m_data.m_value.boolean)\n                {\n                    o->write_characters(\"true\", 4);\n                }\n                else\n                {\n                    o->write_characters(\"false\", 5);\n                }\n                return;\n            }\n\n            case value_t::number_integer:\n            {\n                dump_integer(val.m_data.m_value.number_integer);\n                return;\n            }\n\n            case value_t::number_unsigned:\n            {\n                dump_integer(val.m_data.m_value.number_unsigned);\n                return;\n            }\n\n            case value_t::number_float:\n            {\n                dump_float(val.m_data.m_value.number_float);\n                return;\n            }\n\n            case value_t::discarded:\n            {\n                o->write_characters(\"<discarded>\", 11);\n                return;\n            }\n\n            case value_t::null:\n            {\n                o->write_characters(\"null\", 4);\n                return;\n            }\n\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief dump escaped string\n\n    Escape a string by replacing certain special characters by a sequence of an\n    escape character (backslash) and another character and other control\n    characters by a sequence of \"\\u\" followed by a four-digit hex\n    representation. The escaped string is written to output stream @a o.\n\n    @param[in] s  the string to escape\n    @param[in] ensure_ascii  whether to escape non-ASCII characters with\n                             \\uXXXX sequences\n\n    @complexity Linear in the length of string @a s.\n    */\n    void dump_escaped(const string_t& s, const bool ensure_ascii)\n    {\n        std::uint32_t codepoint{};\n        std::uint8_t state = UTF8_ACCEPT;\n        std::size_t bytes = 0;  // number of bytes written to string_buffer\n\n        // number of bytes written at the point of the last valid byte\n        std::size_t bytes_after_last_accept = 0;\n        std::size_t undumped_chars = 0;\n\n        for (std::size_t i = 0; i < s.size(); ++i)\n        {\n            const auto byte = static_cast<std::uint8_t>(s[i]);\n\n            switch (decode(state, codepoint, byte))\n            {\n                case UTF8_ACCEPT:  // decode found a new code point\n                {\n                    switch (codepoint)\n                    {\n                        case 0x08: // backspace\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'b';\n                            break;\n                        }\n\n                        case 0x09: // horizontal tab\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 't';\n                            break;\n                        }\n\n                        case 0x0A: // newline\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'n';\n                            break;\n                        }\n\n                        case 0x0C: // formfeed\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'f';\n                            break;\n                        }\n\n                        case 0x0D: // carriage return\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'r';\n                            break;\n                        }\n\n                        case 0x22: // quotation mark\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\"';\n                            break;\n                        }\n\n                        case 0x5C: // reverse solidus\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\\';\n                            break;\n                        }\n\n                        default:\n                        {\n                            // escape control characters (0x00..0x1F) or, if\n                            // ensure_ascii parameter is used, non-ASCII characters\n                            if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))\n                            {\n                                if (codepoint <= 0xFFFF)\n                                {\n                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                                    static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, \"\\\\u%04x\",\n                                                                      static_cast<std::uint16_t>(codepoint)));\n                                    bytes += 6;\n                                }\n                                else\n                                {\n                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                                    static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, \"\\\\u%04x\\\\u%04x\",\n                                                                      static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),\n                                                                      static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));\n                                    bytes += 12;\n                                }\n                            }\n                            else\n                            {\n                                // copy byte to buffer (all previous bytes\n                                // been copied have in default case above)\n                                string_buffer[bytes++] = s[i];\n                            }\n                            break;\n                        }\n                    }\n\n                    // write buffer and reset index; there must be 13 bytes\n                    // left, as this is the maximal number of bytes to be\n                    // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                    if (string_buffer.size() - bytes < 13)\n                    {\n                        o->write_characters(string_buffer.data(), bytes);\n                        bytes = 0;\n                    }\n\n                    // remember the byte position of this accept\n                    bytes_after_last_accept = bytes;\n                    undumped_chars = 0;\n                    break;\n                }\n\n                case UTF8_REJECT:  // decode found invalid UTF-8 byte\n                {\n                    switch (error_handler)\n                    {\n                        case error_handler_t::strict:\n                        {\n                            JSON_THROW(type_error::create(316, concat(\"invalid UTF-8 byte at index \", std::to_string(i), \": 0x\", hex_bytes(byte | 0)), nullptr));\n                        }\n\n                        case error_handler_t::ignore:\n                        case error_handler_t::replace:\n                        {\n                            // in case we saw this character the first time, we\n                            // would like to read it again, because the byte\n                            // may be OK for itself, but just not OK for the\n                            // previous sequence\n                            if (undumped_chars > 0)\n                            {\n                                --i;\n                            }\n\n                            // reset length buffer to the last accepted index;\n                            // thus removing/ignoring the invalid characters\n                            bytes = bytes_after_last_accept;\n\n                            if (error_handler == error_handler_t::replace)\n                            {\n                                // add a replacement character\n                                if (ensure_ascii)\n                                {\n                                    string_buffer[bytes++] = '\\\\';\n                                    string_buffer[bytes++] = 'u';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'd';\n                                }\n                                else\n                                {\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xEF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBD');\n                                }\n\n                                // write buffer and reset index; there must be 13 bytes\n                                // left, as this is the maximal number of bytes to be\n                                // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                                if (string_buffer.size() - bytes < 13)\n                                {\n                                    o->write_characters(string_buffer.data(), bytes);\n                                    bytes = 0;\n                                }\n\n                                bytes_after_last_accept = bytes;\n                            }\n\n                            undumped_chars = 0;\n\n                            // continue processing the string\n                            state = UTF8_ACCEPT;\n                            break;\n                        }\n\n                        default:            // LCOV_EXCL_LINE\n                            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n\n                default:  // decode found yet incomplete multibyte code point\n                {\n                    if (!ensure_ascii)\n                    {\n                        // code point will not be escaped - copy byte to buffer\n                        string_buffer[bytes++] = s[i];\n                    }\n                    ++undumped_chars;\n                    break;\n                }\n            }\n        }\n\n        // we finished processing the string\n        if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))\n        {\n            // write buffer\n            if (bytes > 0)\n            {\n                o->write_characters(string_buffer.data(), bytes);\n            }\n        }\n        else\n        {\n            // we finish reading, but do not accept: string was incomplete\n            switch (error_handler)\n            {\n                case error_handler_t::strict:\n                {\n                    JSON_THROW(type_error::create(316, concat(\"incomplete UTF-8 string; last byte: 0x\", hex_bytes(static_cast<std::uint8_t>(s.back() | 0))), nullptr));\n                }\n\n                case error_handler_t::ignore:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    break;\n                }\n\n                case error_handler_t::replace:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    // add a replacement character\n                    if (ensure_ascii)\n                    {\n                        o->write_characters(\"\\\\ufffd\", 6);\n                    }\n                    else\n                    {\n                        o->write_characters(\"\\xEF\\xBF\\xBD\", 3);\n                    }\n                    break;\n                }\n\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            }\n        }\n    }\n\n  private:\n    /*!\n    @brief count digits\n\n    Count the number of decimal (base 10) digits for an input unsigned integer.\n\n    @param[in] x  unsigned integer number to count its digits\n    @return    number of decimal digits\n    */\n    unsigned int count_digits(number_unsigned_t x) noexcept\n    {\n        unsigned int n_digits = 1;\n        for (;;)\n        {\n            if (x < 10)\n            {\n                return n_digits;\n            }\n            if (x < 100)\n            {\n                return n_digits + 1;\n            }\n            if (x < 1000)\n            {\n                return n_digits + 2;\n            }\n            if (x < 10000)\n            {\n                return n_digits + 3;\n            }\n            x = x / 10000u;\n            n_digits += 4;\n        }\n    }\n\n    /*!\n     * @brief convert a byte to a uppercase hex representation\n     * @param[in] byte byte to represent\n     * @return representation (\"00\"..\"FF\")\n     */\n    static std::string hex_bytes(std::uint8_t byte)\n    {\n        std::string result = \"FF\";\n        constexpr const char* nibble_to_hex = \"0123456789ABCDEF\";\n        result[0] = nibble_to_hex[byte / 16];\n        result[1] = nibble_to_hex[byte % 16];\n        return result;\n    }\n\n    // templates to avoid warnings about useless casts\n    template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>\n    bool is_negative_number(NumberType x)\n    {\n        return x < 0;\n    }\n\n    template < typename NumberType, enable_if_t <std::is_unsigned<NumberType>::value, int > = 0 >\n    bool is_negative_number(NumberType /*unused*/)\n    {\n        return false;\n    }\n\n    /*!\n    @brief dump an integer\n\n    Dump a given integer to output stream @a o. Works internally with\n    @a number_buffer.\n\n    @param[in] x  integer number (signed or unsigned) to dump\n    @tparam NumberType either @a number_integer_t or @a number_unsigned_t\n    */\n    template < typename NumberType, detail::enable_if_t <\n                   std::is_integral<NumberType>::value ||\n                   std::is_same<NumberType, number_unsigned_t>::value ||\n                   std::is_same<NumberType, number_integer_t>::value ||\n                   std::is_same<NumberType, binary_char_t>::value,\n                   int > = 0 >\n    void dump_integer(NumberType x)\n    {\n        static constexpr std::array<std::array<char, 2>, 100> digits_to_99\n        {\n            {\n                {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},\n                {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},\n                {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},\n                {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},\n                {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},\n                {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},\n                {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},\n                {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},\n                {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},\n                {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},\n            }\n        };\n\n        // special case for \"0\"\n        if (x == 0)\n        {\n            o->write_character('0');\n            return;\n        }\n\n        // use a pointer to fill the buffer\n        auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n\n        number_unsigned_t abs_value;\n\n        unsigned int n_chars{};\n\n        if (is_negative_number(x))\n        {\n            *buffer_ptr = '-';\n            abs_value = remove_sign(static_cast<number_integer_t>(x));\n\n            // account one more byte for the minus sign\n            n_chars = 1 + count_digits(abs_value);\n        }\n        else\n        {\n            abs_value = static_cast<number_unsigned_t>(x);\n            n_chars = count_digits(abs_value);\n        }\n\n        // spare 1 byte for '\\0'\n        JSON_ASSERT(n_chars < number_buffer.size() - 1);\n\n        // jump to the end to generate the string from backward,\n        // so we later avoid reversing the result\n        buffer_ptr += static_cast<typename decltype(number_buffer)::difference_type>(n_chars);\n\n        // Fast int2ascii implementation inspired by \"Fastware\" talk by Andrei Alexandrescu\n        // See: https://www.youtube.com/watch?v=o4-CwDo2zpg\n        while (abs_value >= 100)\n        {\n            const auto digits_index = static_cast<unsigned>((abs_value % 100));\n            abs_value /= 100;\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n\n        if (abs_value >= 10)\n        {\n            const auto digits_index = static_cast<unsigned>(abs_value);\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n        else\n        {\n            *(--buffer_ptr) = static_cast<char>('0' + abs_value);\n        }\n\n        o->write_characters(number_buffer.data(), n_chars);\n    }\n\n    /*!\n    @brief dump a floating-point number\n\n    Dump a given floating-point number to output stream @a o. Works internally\n    with @a number_buffer.\n\n    @param[in] x  floating-point number to dump\n    */\n    void dump_float(number_float_t x)\n    {\n        // NaN / inf\n        if (!std::isfinite(x))\n        {\n            o->write_characters(\"null\", 4);\n            return;\n        }\n\n        // If number_float_t is an IEEE-754 single or double precision number,\n        // use the Grisu2 algorithm to produce short numbers which are\n        // guaranteed to round-trip, using strtof and strtod, resp.\n        //\n        // NB: The test below works if <long double> == <double>.\n        static constexpr bool is_ieee_single_or_double\n            = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||\n              (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);\n\n        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());\n    }\n\n    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)\n    {\n        auto* begin = number_buffer.data();\n        auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);\n\n        o->write_characters(begin, static_cast<size_t>(end - begin));\n    }\n\n    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)\n    {\n        // get the number of digits for a float -> text -> float round-trip\n        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;\n\n        // the actual conversion\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), \"%.*g\", d, x);\n\n        // negative value indicates an error\n        JSON_ASSERT(len > 0);\n        // check if the buffer was large enough\n        JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());\n\n        // erase thousands separators\n        if (thousands_sep != '\\0')\n        {\n            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081\n            const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);\n            std::fill(end, number_buffer.end(), '\\0');\n            JSON_ASSERT((end - number_buffer.begin()) <= len);\n            len = (end - number_buffer.begin());\n        }\n\n        // convert decimal point to '.'\n        if (decimal_point != '\\0' && decimal_point != '.')\n        {\n            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081\n            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);\n            if (dec_pos != number_buffer.end())\n            {\n                *dec_pos = '.';\n            }\n        }\n\n        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));\n\n        // determine if we need to append \".0\"\n        const bool value_is_int_like =\n            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,\n                         [](char c)\n        {\n            return c == '.' || c == 'e';\n        });\n\n        if (value_is_int_like)\n        {\n            o->write_characters(\".0\", 2);\n        }\n    }\n\n    /*!\n    @brief check whether a string is UTF-8 encoded\n\n    The function checks each byte of a string whether it is UTF-8 encoded. The\n    result of the check is stored in the @a state parameter. The function must\n    be called initially with state 0 (accept). State 1 means the string must\n    be rejected, because the current byte is not allowed. If the string is\n    completely processed, but the state is non-zero, the string ended\n    prematurely; that is, the last byte indicated more bytes should have\n    followed.\n\n    @param[in,out] state  the state of the decoding\n    @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)\n    @param[in] byte       next byte to decode\n    @return               new state\n\n    @note The function has been edited: a std::array is used.\n\n    @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>\n    @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/\n    */\n    static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept\n    {\n        static const std::array<std::uint8_t, 400> utf8d =\n        {\n            {\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F\n                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF\n                8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF\n                0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF\n                0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF\n                0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2\n                1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4\n                1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6\n                1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8\n            }\n        };\n\n        JSON_ASSERT(byte < utf8d.size());\n        const std::uint8_t type = utf8d[byte];\n\n        codep = (state != UTF8_ACCEPT)\n                ? (byte & 0x3fu) | (codep << 6u)\n                : (0xFFu >> type) & (byte);\n\n        const std::size_t index = 256u + (static_cast<size_t>(state) * 16u) + static_cast<size_t>(type);\n        JSON_ASSERT(index < utf8d.size());\n        state = utf8d[index];\n        return state;\n    }\n\n    /*\n     * Overload to make the compiler happy while it is instantiating\n     * dump_integer for number_unsigned_t.\n     * Must never be called.\n     */\n    number_unsigned_t remove_sign(number_unsigned_t x)\n    {\n        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        return x; // LCOV_EXCL_LINE\n    }\n\n    /*\n     * Helper function for dump_integer\n     *\n     * This function takes a negative signed integer and returns its absolute\n     * value as an unsigned integer. The plus/minus shuffling is necessary as we\n     * cannot directly remove the sign of an arbitrary signed integer as the\n     * absolute values of INT_MIN and INT_MAX are usually not the same. See\n     * #1708 for details.\n     */\n    number_unsigned_t remove_sign(number_integer_t x) noexcept\n    {\n        JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)\n        return static_cast<number_unsigned_t>(-(x + 1)) + 1;\n    }\n\n  private:\n    /// the output of the serializer\n    output_adapter_t<char> o = nullptr;\n\n    /// a (hopefully) large enough character buffer\n    std::array<char, 64> number_buffer{{}};\n\n    /// the locale\n    const std::lconv* loc = nullptr;\n    /// the locale's thousand separator character\n    const char thousands_sep = '\\0';\n    /// the locale's decimal point character\n    const char decimal_point = '\\0';\n\n    /// string buffer\n    std::array<char, 512> string_buffer{{}};\n\n    /// the indentation character\n    const char indent_char;\n    /// the indentation string\n    string_t indent_string;\n\n    /// error_handler how to react on decoding errors\n    const error_handler_t error_handler;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/json.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n/****************************************************************************\\\n * Note on documentation: The source files contain links to the online      *\n * documentation of the public API at https://json.nlohmann.me. This URL    *\n * contains the most recent documentation and should also be applicable to  *\n * previous versions; documentation for deprecated functions is not         *\n * removed, but marked deprecated. See \"Generate documentation\" section in  *\n * file docs/README.md.                                                     *\n\\****************************************************************************/\n\n#ifndef INCLUDE_NLOHMANN_JSON_HPP_\n#define INCLUDE_NLOHMANN_JSON_HPP_\n\n#include <algorithm> // all_of, find, for_each\n#include <cstddef> // nullptr_t, ptrdiff_t, size_t\n#include <functional> // hash, less\n#include <initializer_list> // initializer_list\n#ifndef JSON_NO_IO\n    #include <iosfwd> // istream, ostream\n#endif  // JSON_NO_IO\n#include <iterator> // random_access_iterator_tag\n#include <memory> // unique_ptr\n#include <string> // string, stoi, to_string\n#include <utility> // declval, forward, move, pair, swap\n#include <vector> // vector\n\n#include <nlohmann/adl_serializer.hpp>\n#include <nlohmann/byte_container_with_subtype.hpp>\n#include <nlohmann/detail/conversions/from_json.hpp>\n#include <nlohmann/detail/conversions/to_json.hpp>\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/hash.hpp>\n#include <nlohmann/detail/input/binary_reader.hpp>\n#include <nlohmann/detail/input/input_adapters.hpp>\n#include <nlohmann/detail/input/lexer.hpp>\n#include <nlohmann/detail/input/parser.hpp>\n#include <nlohmann/detail/iterators/internal_iterator.hpp>\n#include <nlohmann/detail/iterators/iter_impl.hpp>\n#include <nlohmann/detail/iterators/iteration_proxy.hpp>\n#include <nlohmann/detail/iterators/json_reverse_iterator.hpp>\n#include <nlohmann/detail/iterators/primitive_iterator.hpp>\n#include <nlohmann/detail/json_custom_base_class.hpp>\n#include <nlohmann/detail/json_pointer.hpp>\n#include <nlohmann/detail/json_ref.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/string_concat.hpp>\n#include <nlohmann/detail/string_escape.hpp>\n#include <nlohmann/detail/string_utils.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/output/binary_writer.hpp>\n#include <nlohmann/detail/output/output_adapters.hpp>\n#include <nlohmann/detail/output/serializer.hpp>\n#include <nlohmann/detail/value_t.hpp>\n#include <nlohmann/json_fwd.hpp>\n#include <nlohmann/ordered_map.hpp>\n\n#if defined(JSON_HAS_CPP_17)\n    #if JSON_HAS_STATIC_RTTI\n        #include <any>\n    #endif\n    #include <string_view>\n#endif\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/*!\n@brief a class to store JSON values\n\n@internal\n@invariant The member variables @a m_value and @a m_type have the following\nrelationship:\n- If `m_type == value_t::object`, then `m_value.object != nullptr`.\n- If `m_type == value_t::array`, then `m_value.array != nullptr`.\n- If `m_type == value_t::string`, then `m_value.string != nullptr`.\nThe invariants are checked by member function assert_invariant().\n\n@note ObjectType trick from https://stackoverflow.com/a/9860911\n@endinternal\n\n@since version 1.0.0\n\n@nosubgrouping\n*/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nclass basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)\n    : public ::nlohmann::detail::json_base_class<CustomBaseClass>\n{\n  private:\n    template<detail::value_t> friend struct detail::external_constructor;\n\n    template<typename>\n    friend class ::nlohmann::json_pointer;\n    // can be restored when json_pointer backwards compatibility is removed\n    // friend ::nlohmann::json_pointer<StringType>;\n\n    template<typename BasicJsonType, typename InputType>\n    friend class ::nlohmann::detail::parser;\n    friend ::nlohmann::detail::serializer<basic_json>;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::iter_impl;\n    template<typename BasicJsonType, typename CharType>\n    friend class ::nlohmann::detail::binary_writer;\n    template<typename BasicJsonType, typename InputType, typename SAX>\n    friend class ::nlohmann::detail::binary_reader;\n    template<typename BasicJsonType, typename InputAdapterType>\n    friend class ::nlohmann::detail::json_sax_dom_parser;\n    template<typename BasicJsonType, typename InputAdapterType>\n    friend class ::nlohmann::detail::json_sax_dom_callback_parser;\n    friend class ::nlohmann::detail::exception;\n\n    /// workaround type for MSVC\n    using basic_json_t = NLOHMANN_BASIC_JSON_TPL;\n    using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    // convenience aliases for types residing in namespace detail;\n    using lexer = ::nlohmann::detail::lexer_base<basic_json>;\n\n    template<typename InputAdapterType>\n    static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser(\n        InputAdapterType adapter,\n        detail::parser_callback_t<basic_json>cb = nullptr,\n        const bool allow_exceptions = true,\n        const bool ignore_comments = false,\n        const bool ignore_trailing_commas = false\n    )\n    {\n        return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),\n               std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);\n    }\n\n  private:\n    using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;\n    template<typename BasicJsonType>\n    using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;\n    template<typename BasicJsonType>\n    using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;\n    template<typename Iterator>\n    using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;\n    template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;\n\n    template<typename CharType>\n    using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;\n\n    template<typename InputType>\n    using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;\n    template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    using serializer = ::nlohmann::detail::serializer<basic_json>;\n\n  public:\n    using value_t = detail::value_t;\n    /// JSON Pointer, see @ref nlohmann::json_pointer\n    using json_pointer = ::nlohmann::json_pointer<StringType>;\n    template<typename T, typename SFINAE>\n    using json_serializer = JSONSerializer<T, SFINAE>;\n    /// how to treat decoding errors\n    using error_handler_t = detail::error_handler_t;\n    /// how to treat CBOR tags\n    using cbor_tag_handler_t = detail::cbor_tag_handler_t;\n    /// how to encode BJData\n    using bjdata_version_t = detail::bjdata_version_t;\n    /// helper type for initializer lists of basic_json values\n    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;\n\n    using input_format_t = detail::input_format_t;\n    /// SAX interface type, see @ref nlohmann::json_sax\n    using json_sax_t = json_sax<basic_json>;\n\n    ////////////////\n    // exceptions //\n    ////////////////\n\n    /// @name exceptions\n    /// Classes to implement user-defined exceptions.\n    /// @{\n\n    using exception = detail::exception;\n    using parse_error = detail::parse_error;\n    using invalid_iterator = detail::invalid_iterator;\n    using type_error = detail::type_error;\n    using out_of_range = detail::out_of_range;\n    using other_error = detail::other_error;\n\n    /// @}\n\n    /////////////////////\n    // container types //\n    /////////////////////\n\n    /// @name container types\n    /// The canonic container types to use @ref basic_json like any other STL\n    /// container.\n    /// @{\n\n    /// the type of elements in a basic_json container\n    using value_type = basic_json;\n\n    /// the type of an element reference\n    using reference = value_type&;\n    /// the type of an element const reference\n    using const_reference = const value_type&;\n\n    /// a type to represent differences between iterators\n    using difference_type = std::ptrdiff_t;\n    /// a type to represent container sizes\n    using size_type = std::size_t;\n\n    /// the allocator type\n    using allocator_type = AllocatorType<basic_json>;\n\n    /// the type of an element pointer\n    using pointer = typename std::allocator_traits<allocator_type>::pointer;\n    /// the type of an element const pointer\n    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;\n\n    /// an iterator for a basic_json container\n    using iterator = iter_impl<basic_json>;\n    /// a const iterator for a basic_json container\n    using const_iterator = iter_impl<const basic_json>;\n    /// a reverse iterator for a basic_json container\n    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;\n    /// a const reverse iterator for a basic_json container\n    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;\n\n    /// @}\n\n    /// @brief returns the allocator associated with the container\n    /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/\n    static allocator_type get_allocator()\n    {\n        return allocator_type();\n    }\n\n    /// @brief returns version information on the library\n    /// @sa https://json.nlohmann.me/api/basic_json/meta/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json meta()\n    {\n        basic_json result;\n\n        result[\"copyright\"] = \"(C) 2013-2025 Niels Lohmann\";\n        result[\"name\"] = \"JSON for Modern C++\";\n        result[\"url\"] = \"https://github.com/nlohmann/json\";\n        result[\"version\"][\"string\"] =\n            detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.',\n                           std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.',\n                           std::to_string(NLOHMANN_JSON_VERSION_PATCH));\n        result[\"version\"][\"major\"] = NLOHMANN_JSON_VERSION_MAJOR;\n        result[\"version\"][\"minor\"] = NLOHMANN_JSON_VERSION_MINOR;\n        result[\"version\"][\"patch\"] = NLOHMANN_JSON_VERSION_PATCH;\n\n#ifdef _WIN32\n        result[\"platform\"] = \"win32\";\n#elif defined __linux__\n        result[\"platform\"] = \"linux\";\n#elif defined __APPLE__\n        result[\"platform\"] = \"apple\";\n#elif defined __unix__\n        result[\"platform\"] = \"unix\";\n#else\n        result[\"platform\"] = \"unknown\";\n#endif\n\n#if defined(__ICC) || defined(__INTEL_COMPILER)\n        result[\"compiler\"] = {{\"family\", \"icc\"}, {\"version\", __INTEL_COMPILER}};\n#elif defined(__clang__)\n        result[\"compiler\"] = {{\"family\", \"clang\"}, {\"version\", __clang_version__}};\n#elif defined(__GNUC__) || defined(__GNUG__)\n        result[\"compiler\"] = {{\"family\", \"gcc\"}, {\"version\", detail::concat(\n                    std::to_string(__GNUC__), '.',\n                    std::to_string(__GNUC_MINOR__), '.',\n                    std::to_string(__GNUC_PATCHLEVEL__))\n            }\n        };\n#elif defined(__HP_cc) || defined(__HP_aCC)\n        result[\"compiler\"] = \"hp\"\n#elif defined(__IBMCPP__)\n        result[\"compiler\"] = {{\"family\", \"ilecpp\"}, {\"version\", __IBMCPP__}};\n#elif defined(_MSC_VER)\n        result[\"compiler\"] = {{\"family\", \"msvc\"}, {\"version\", _MSC_VER}};\n#elif defined(__PGI)\n        result[\"compiler\"] = {{\"family\", \"pgcpp\"}, {\"version\", __PGI}};\n#elif defined(__SUNPRO_CC)\n        result[\"compiler\"] = {{\"family\", \"sunpro\"}, {\"version\", __SUNPRO_CC}};\n#else\n        result[\"compiler\"] = {{\"family\", \"unknown\"}, {\"version\", \"unknown\"}};\n#endif\n\n#if defined(_MSVC_LANG)\n        result[\"compiler\"][\"c++\"] = std::to_string(_MSVC_LANG);\n#elif defined(__cplusplus)\n        result[\"compiler\"][\"c++\"] = std::to_string(__cplusplus);\n#else\n        result[\"compiler\"][\"c++\"] = \"unknown\";\n#endif\n        return result;\n    }\n\n    ///////////////////////////\n    // JSON value data types //\n    ///////////////////////////\n\n    /// @name JSON value data types\n    /// The data types to store a JSON value. These types are derived from\n    /// the template arguments passed to class @ref basic_json.\n    /// @{\n\n    /// @brief default object key comparator type\n    /// The actual object key comparator type (@ref object_comparator_t) may be\n    /// different.\n    /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/\n#if defined(JSON_HAS_CPP_14)\n    // use of transparent comparator avoids unnecessary repeated construction of temporaries\n    // in functions involving lookup by key with types other than object_t::key_type (aka. StringType)\n    using default_object_comparator_t = std::less<>;\n#else\n    using default_object_comparator_t = std::less<StringType>;\n#endif\n\n    /// @brief a type for an object\n    /// @sa https://json.nlohmann.me/api/basic_json/object_t/\n    using object_t = ObjectType<StringType,\n          basic_json,\n          default_object_comparator_t,\n          AllocatorType<std::pair<const StringType,\n          basic_json>>>;\n\n    /// @brief a type for an array\n    /// @sa https://json.nlohmann.me/api/basic_json/array_t/\n    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;\n\n    /// @brief a type for a string\n    /// @sa https://json.nlohmann.me/api/basic_json/string_t/\n    using string_t = StringType;\n\n    /// @brief a type for a boolean\n    /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/\n    using boolean_t = BooleanType;\n\n    /// @brief a type for a number (integer)\n    /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/\n    using number_integer_t = NumberIntegerType;\n\n    /// @brief a type for a number (unsigned)\n    /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/\n    using number_unsigned_t = NumberUnsignedType;\n\n    /// @brief a type for a number (floating-point)\n    /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/\n    using number_float_t = NumberFloatType;\n\n    /// @brief a type for a packed binary type\n    /// @sa https://json.nlohmann.me/api/basic_json/binary_t/\n    using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;\n\n    /// @brief object key comparator type\n    /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/\n    using object_comparator_t = detail::actual_object_comparator_t<basic_json>;\n\n    /// @}\n\n  private:\n\n    /// helper for exception-safe object creation\n    template<typename T, typename... Args>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    static T* create(Args&& ... args)\n    {\n        AllocatorType<T> alloc;\n        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;\n\n        auto deleter = [&](T * obj)\n        {\n            AllocatorTraits::deallocate(alloc, obj, 1);\n        };\n        std::unique_ptr<T, decltype(deleter)> obj(AllocatorTraits::allocate(alloc, 1), deleter);\n        AllocatorTraits::construct(alloc, obj.get(), std::forward<Args>(args)...);\n        JSON_ASSERT(obj != nullptr);\n        return obj.release();\n    }\n\n    ////////////////////////\n    // JSON value storage //\n    ////////////////////////\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief a JSON value\n\n    The actual storage for a JSON value of the @ref basic_json class. This\n    union combines the different storage types for the JSON value types\n    defined in @ref value_t.\n\n    JSON type | value_t type    | used type\n    --------- | --------------- | ------------------------\n    object    | object          | pointer to @ref object_t\n    array     | array           | pointer to @ref array_t\n    string    | string          | pointer to @ref string_t\n    boolean   | boolean         | @ref boolean_t\n    number    | number_integer  | @ref number_integer_t\n    number    | number_unsigned | @ref number_unsigned_t\n    number    | number_float    | @ref number_float_t\n    binary    | binary          | pointer to @ref binary_t\n    null      | null            | *no value is stored*\n\n    @note Variable-length types (objects, arrays, and strings) are stored as\n    pointers. The size of the union should not exceed 64 bits if the default\n    value types are used.\n\n    @since version 1.0.0\n    */\n    union json_value\n    {\n        /// object (stored with pointer to save storage)\n        object_t* object;\n        /// array (stored with pointer to save storage)\n        array_t* array;\n        /// string (stored with pointer to save storage)\n        string_t* string;\n        /// binary (stored with pointer to save storage)\n        binary_t* binary;\n        /// boolean\n        boolean_t boolean;\n        /// number (integer)\n        number_integer_t number_integer;\n        /// number (unsigned integer)\n        number_unsigned_t number_unsigned;\n        /// number (floating-point)\n        number_float_t number_float;\n\n        /// default constructor (for null values)\n        json_value() = default;\n        /// constructor for booleans\n        json_value(boolean_t v) noexcept : boolean(v) {}\n        /// constructor for numbers (integer)\n        json_value(number_integer_t v) noexcept : number_integer(v) {}\n        /// constructor for numbers (unsigned)\n        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}\n        /// constructor for numbers (floating-point)\n        json_value(number_float_t v) noexcept : number_float(v) {}\n        /// constructor for empty values of a given type\n        json_value(value_t t)\n        {\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    object = create<object_t>();\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    array = create<array_t>();\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    string = create<string_t>(\"\");\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    binary = create<binary_t>();\n                    break;\n                }\n\n                case value_t::boolean:\n                {\n                    boolean = static_cast<boolean_t>(false);\n                    break;\n                }\n\n                case value_t::number_integer:\n                {\n                    number_integer = static_cast<number_integer_t>(0);\n                    break;\n                }\n\n                case value_t::number_unsigned:\n                {\n                    number_unsigned = static_cast<number_unsigned_t>(0);\n                    break;\n                }\n\n                case value_t::number_float:\n                {\n                    number_float = static_cast<number_float_t>(0.0);\n                    break;\n                }\n\n                case value_t::null:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    break;\n                }\n\n                case value_t::discarded:\n                default:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    if (JSON_HEDLEY_UNLIKELY(t == value_t::null))\n                    {\n                        JSON_THROW(other_error::create(500, \"961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0\", nullptr)); // LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n            }\n        }\n\n        /// constructor for strings\n        json_value(const string_t& value) : string(create<string_t>(value)) {}\n\n        /// constructor for rvalue strings\n        json_value(string_t&& value) : string(create<string_t>(std::move(value))) {}\n\n        /// constructor for objects\n        json_value(const object_t& value) : object(create<object_t>(value)) {}\n\n        /// constructor for rvalue objects\n        json_value(object_t&& value) : object(create<object_t>(std::move(value))) {}\n\n        /// constructor for arrays\n        json_value(const array_t& value) : array(create<array_t>(value)) {}\n\n        /// constructor for rvalue arrays\n        json_value(array_t&& value) : array(create<array_t>(std::move(value))) {}\n\n        /// constructor for binary arrays\n        json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {}\n\n        /// constructor for rvalue binary arrays\n        json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {}\n\n        /// constructor for binary arrays (internal type)\n        json_value(const binary_t& value) : binary(create<binary_t>(value)) {}\n\n        /// constructor for rvalue binary arrays (internal type)\n        json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}\n\n        void destroy(value_t t)\n        {\n            if (\n                (t == value_t::object && object == nullptr) ||\n                (t == value_t::array && array == nullptr) ||\n                (t == value_t::string && string == nullptr) ||\n                (t == value_t::binary && binary == nullptr)\n            )\n            {\n                // not initialized (e.g., due to exception in the ctor)\n                return;\n            }\n            if (t == value_t::array || t == value_t::object)\n            {\n                // flatten the current json_value to a heap-allocated stack\n                std::vector<basic_json> stack;\n\n                // move the top-level items to stack\n                if (t == value_t::array)\n                {\n                    stack.reserve(array->size());\n                    std::move(array->begin(), array->end(), std::back_inserter(stack));\n                }\n                else\n                {\n                    stack.reserve(object->size());\n                    for (auto&& it : *object)\n                    {\n                        stack.push_back(std::move(it.second));\n                    }\n                }\n\n                while (!stack.empty())\n                {\n                    // move the last item to a local variable to be processed\n                    basic_json current_item(std::move(stack.back()));\n                    stack.pop_back();\n\n                    // if current_item is array/object, move\n                    // its children to the stack to be processed later\n                    if (current_item.is_array())\n                    {\n                        std::move(current_item.m_data.m_value.array->begin(), current_item.m_data.m_value.array->end(), std::back_inserter(stack));\n\n                        current_item.m_data.m_value.array->clear();\n                    }\n                    else if (current_item.is_object())\n                    {\n                        for (auto&& it : *current_item.m_data.m_value.object)\n                        {\n                            stack.push_back(std::move(it.second));\n                        }\n\n                        current_item.m_data.m_value.object->clear();\n                    }\n\n                    // it's now safe that current_item gets destructed\n                    // since it doesn't have any children\n                }\n            }\n\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    AllocatorType<object_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, object);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    AllocatorType<array_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, array);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);\n                    break;\n                }\n\n                case value_t::null:\n                case value_t::boolean:\n                case value_t::number_integer:\n                case value_t::number_unsigned:\n                case value_t::number_float:\n                case value_t::discarded:\n                default:\n                {\n                    break;\n                }\n            }\n        }\n    };\n\n  private:\n    /*!\n    @brief checks the class invariants\n\n    This function asserts the class invariants. It needs to be called at the\n    end of every constructor to make sure that created objects respect the\n    invariant. Furthermore, it has to be called each time the type of a JSON\n    value is changed, because the invariant expresses a relationship between\n    @a m_type and @a m_value.\n\n    Furthermore, the parent relation is checked for arrays and objects: If\n    @a check_parents true and the value is an array or object, then the\n    container's elements must have the current value as parent.\n\n    @param[in] check_parents  whether the parent relation should be checked.\n               The value is true by default and should only be set to false\n               during destruction of objects when the invariant does not\n               need to hold.\n    */\n    void assert_invariant(bool check_parents = true) const noexcept\n    {\n        JSON_ASSERT(m_data.m_type != value_t::object || m_data.m_value.object != nullptr);\n        JSON_ASSERT(m_data.m_type != value_t::array || m_data.m_value.array != nullptr);\n        JSON_ASSERT(m_data.m_type != value_t::string || m_data.m_value.string != nullptr);\n        JSON_ASSERT(m_data.m_type != value_t::binary || m_data.m_value.binary != nullptr);\n\n#if JSON_DIAGNOSTICS\n        JSON_TRY\n        {\n            // cppcheck-suppress assertWithSideEffect\n            JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)\n            {\n                return j.m_parent == this;\n            }));\n        }\n        JSON_CATCH(...) {} // LCOV_EXCL_LINE\n#endif\n        static_cast<void>(check_parents);\n    }\n\n    void set_parents()\n    {\n#if JSON_DIAGNOSTICS\n        switch (m_data.m_type)\n        {\n            case value_t::array:\n            {\n                for (auto& element : *m_data.m_value.array)\n                {\n                    element.m_parent = this;\n                }\n                break;\n            }\n\n            case value_t::object:\n            {\n                for (auto& element : *m_data.m_value.object)\n                {\n                    element.second.m_parent = this;\n                }\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                break;\n        }\n#endif\n    }\n\n    iterator set_parents(iterator it, typename iterator::difference_type count_set_parents)\n    {\n#if JSON_DIAGNOSTICS\n        for (typename iterator::difference_type i = 0; i < count_set_parents; ++i)\n        {\n            (it + i)->m_parent = this;\n        }\n#else\n        static_cast<void>(count_set_parents);\n#endif\n        return it;\n    }\n\n    reference set_parent(reference j, std::size_t old_capacity = detail::unknown_size())\n    {\n#if JSON_DIAGNOSTICS\n        if (old_capacity != detail::unknown_size())\n        {\n            // see https://github.com/nlohmann/json/issues/2838\n            JSON_ASSERT(type() == value_t::array);\n            if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity))\n            {\n                // capacity has changed: update all parents\n                set_parents();\n                return j;\n            }\n        }\n\n        // ordered_json uses a vector internally, so pointers could have\n        // been invalidated; see https://github.com/nlohmann/json/issues/2962\n#ifdef JSON_HEDLEY_MSVC_VERSION\n#pragma warning(push )\n#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr\n#endif\n        if (detail::is_ordered_map<object_t>::value)\n        {\n            set_parents();\n            return j;\n        }\n#ifdef JSON_HEDLEY_MSVC_VERSION\n#pragma warning( pop )\n#endif\n\n        j.m_parent = this;\n#else\n        static_cast<void>(j);\n        static_cast<void>(old_capacity);\n#endif\n        return j;\n    }\n\n  public:\n    //////////////////////////\n    // JSON parser callback //\n    //////////////////////////\n\n    /// @brief parser event types\n    /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/\n    using parse_event_t = detail::parse_event_t;\n\n    /// @brief per-element parser callback type\n    /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/\n    using parser_callback_t = detail::parser_callback_t<basic_json>;\n\n    //////////////////\n    // constructors //\n    //////////////////\n\n    /// @name constructors and destructors\n    /// Constructors of class @ref basic_json, copy/move constructor, copy\n    /// assignment, static functions creating objects, and the destructor.\n    /// @{\n\n    /// @brief create an empty value with a given type\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(const value_t v)\n        : m_data(v)\n    {\n        assert_invariant();\n    }\n\n    /// @brief create a null object\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape)\n        : basic_json(value_t::null)\n    {\n        assert_invariant();\n    }\n\n    /// @brief create a JSON value from compatible types\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    template < typename CompatibleType,\n               typename U = detail::uncvref_t<CompatibleType>,\n               detail::enable_if_t <\n                   !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >\n    basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)\n                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),\n                                           std::forward<CompatibleType>(val))))\n    {\n        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief create a JSON value from an existing one\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    template < typename BasicJsonType,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >\n    basic_json(const BasicJsonType& val)\n#if JSON_DIAGNOSTIC_POSITIONS\n        : start_position(val.start_pos()),\n          end_position(val.end_pos())\n#endif\n    {\n        using other_boolean_t = typename BasicJsonType::boolean_t;\n        using other_number_float_t = typename BasicJsonType::number_float_t;\n        using other_number_integer_t = typename BasicJsonType::number_integer_t;\n        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n        using other_string_t = typename BasicJsonType::string_t;\n        using other_object_t = typename BasicJsonType::object_t;\n        using other_array_t = typename BasicJsonType::array_t;\n        using other_binary_t = typename BasicJsonType::binary_t;\n\n        switch (val.type())\n        {\n            case value_t::boolean:\n                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());\n                break;\n            case value_t::number_float:\n                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());\n                break;\n            case value_t::number_integer:\n                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());\n                break;\n            case value_t::number_unsigned:\n                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());\n                break;\n            case value_t::string:\n                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());\n                break;\n            case value_t::object:\n                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());\n                break;\n            case value_t::array:\n                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());\n                break;\n            case value_t::binary:\n                JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());\n                break;\n            case value_t::null:\n                *this = nullptr;\n                break;\n            case value_t::discarded:\n                m_data.m_type = value_t::discarded;\n                break;\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n        JSON_ASSERT(m_data.m_type == val.type());\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief create a container (array or object) from an initializer list\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(initializer_list_t init,\n               bool type_deduction = true,\n               value_t manual_type = value_t::array)\n    {\n        // check if each element is an array with two elements whose first\n        // element is a string\n        bool is_an_object = std::all_of(init.begin(), init.end(),\n                                        [](const detail::json_ref<basic_json>& element_ref)\n        {\n            // The cast is to ensure op[size_type] is called, bearing in mind size_type may not be int;\n            // (many string types can be constructed from 0 via its null-pointer guise, so we get a\n            // broken call to op[key_type], the wrong semantics, and a 4804 warning on Windows)\n            return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[static_cast<size_type>(0)].is_string();\n        });\n\n        // adjust type if type deduction is not wanted\n        if (!type_deduction)\n        {\n            // if an array is wanted, do not create an object though possible\n            if (manual_type == value_t::array)\n            {\n                is_an_object = false;\n            }\n\n            // if an object is wanted but impossible, throw an exception\n            if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))\n            {\n                JSON_THROW(type_error::create(301, \"cannot create object from initializer list\", nullptr));\n            }\n        }\n\n        if (is_an_object)\n        {\n            // the initializer list is a list of pairs -> create an object\n            m_data.m_type = value_t::object;\n            m_data.m_value = value_t::object;\n\n            for (auto& element_ref : init)\n            {\n                auto element = element_ref.moved_or_copied();\n                m_data.m_value.object->emplace(\n                    std::move(*((*element.m_data.m_value.array)[0].m_data.m_value.string)),\n                    std::move((*element.m_data.m_value.array)[1]));\n            }\n        }\n        else\n        {\n            // the initializer list describes an array -> create an array\n            m_data.m_type = value_t::array;\n            m_data.m_value.array = create<array_t>(init.begin(), init.end());\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief explicitly create a binary array (without subtype)\n    /// @sa https://json.nlohmann.me/api/basic_json/binary/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init)\n    {\n        auto res = basic_json();\n        res.m_data.m_type = value_t::binary;\n        res.m_data.m_value = init;\n        return res;\n    }\n\n    /// @brief explicitly create a binary array (with subtype)\n    /// @sa https://json.nlohmann.me/api/basic_json/binary/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)\n    {\n        auto res = basic_json();\n        res.m_data.m_type = value_t::binary;\n        res.m_data.m_value = binary_t(init, subtype);\n        return res;\n    }\n\n    /// @brief explicitly create a binary array\n    /// @sa https://json.nlohmann.me/api/basic_json/binary/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init)\n    {\n        auto res = basic_json();\n        res.m_data.m_type = value_t::binary;\n        res.m_data.m_value = std::move(init);\n        return res;\n    }\n\n    /// @brief explicitly create a binary array (with subtype)\n    /// @sa https://json.nlohmann.me/api/basic_json/binary/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)\n    {\n        auto res = basic_json();\n        res.m_data.m_type = value_t::binary;\n        res.m_data.m_value = binary_t(std::move(init), subtype);\n        return res;\n    }\n\n    /// @brief explicitly create an array from an initializer list\n    /// @sa https://json.nlohmann.me/api/basic_json/array/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json array(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::array);\n    }\n\n    /// @brief explicitly create an object from an initializer list\n    /// @sa https://json.nlohmann.me/api/basic_json/object/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json object(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::object);\n    }\n\n    /// @brief construct an array with count copies of given value\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(size_type cnt, const basic_json& val):\n        m_data{cnt, val}\n    {\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief construct a JSON container given an iterator range\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    template < class InputIT, typename std::enable_if <\n                   std::is_same<InputIT, typename basic_json_t::iterator>::value ||\n                   std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >\n    basic_json(InputIT first, InputIT last) // NOLINT(performance-unnecessary-value-param)\n    {\n        JSON_ASSERT(first.m_object != nullptr);\n        JSON_ASSERT(last.m_object != nullptr);\n\n        // make sure the iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(201, \"iterators are not compatible\", nullptr));\n        }\n\n        // copy type from the first iterator\n        m_data.m_type = first.m_object->m_data.m_type;\n\n        // check if the iterator range is complete for primitive values\n        switch (m_data.m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()\n                                         || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\", first.m_object));\n                }\n                break;\n            }\n\n            case value_t::null:\n            case value_t::object:\n            case value_t::array:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                break;\n        }\n\n        switch (m_data.m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_data.m_value.number_integer = first.m_object->m_data.m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_data.m_value.number_unsigned = first.m_object->m_data.m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_data.m_value.number_float = first.m_object->m_data.m_value.number_float;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_data.m_value.boolean = first.m_object->m_data.m_value.boolean;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_data.m_value = *first.m_object->m_data.m_value.string;\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_data.m_value.object = create<object_t>(first.m_it.object_iterator,\n                                        last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_data.m_value.array = create<array_t>(first.m_it.array_iterator,\n                                                       last.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_data.m_value = *first.m_object->m_data.m_value.binary;\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(invalid_iterator::create(206, detail::concat(\"cannot construct with iterators from \", first.m_object->type_name()), first.m_object));\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n    ///////////////////////////////////////\n    // other constructors and destructor //\n    ///////////////////////////////////////\n\n    template<typename JsonRef,\n             detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,\n                                 std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >\n    basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}\n\n    /// @brief copy constructor\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(const basic_json& other)\n        : json_base_class_t(other)\n#if JSON_DIAGNOSTIC_POSITIONS\n        , start_position(other.start_position)\n        , end_position(other.end_position)\n#endif\n    {\n        m_data.m_type = other.m_data.m_type;\n        // check of passed value is valid\n        other.assert_invariant();\n\n        switch (m_data.m_type)\n        {\n            case value_t::object:\n            {\n                m_data.m_value = *other.m_data.m_value.object;\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_data.m_value = *other.m_data.m_value.array;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_data.m_value = *other.m_data.m_value.string;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_data.m_value = other.m_data.m_value.boolean;\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                m_data.m_value = other.m_data.m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_data.m_value = other.m_data.m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_data.m_value = other.m_data.m_value.number_float;\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_data.m_value = *other.m_data.m_value.binary;\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                break;\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief move constructor\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(basic_json&& other) noexcept\n        : json_base_class_t(std::forward<json_base_class_t>(other)),\n          m_data(std::move(other.m_data)) // cppcheck-suppress[accessForwarded] TODO check\n#if JSON_DIAGNOSTIC_POSITIONS\n        , start_position(other.start_position) // cppcheck-suppress[accessForwarded] TODO check\n        , end_position(other.end_position) // cppcheck-suppress[accessForwarded] TODO check\n#endif\n    {\n        // check that the passed value is valid\n        other.assert_invariant(false); // cppcheck-suppress[accessForwarded]\n\n        // invalidate payload\n        other.m_data.m_type = value_t::null;\n        other.m_data.m_value = {};\n\n#if JSON_DIAGNOSTIC_POSITIONS\n        other.start_position = std::string::npos;\n        other.end_position = std::string::npos;\n#endif\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief copy assignment\n    /// @sa https://json.nlohmann.me/api/basic_json/operator=/\n    basic_json& operator=(basic_json other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value&&\n        std::is_nothrow_move_assignable<json_base_class_t>::value\n    )\n    {\n        // check that the passed value is valid\n        other.assert_invariant();\n\n        using std::swap;\n        swap(m_data.m_type, other.m_data.m_type);\n        swap(m_data.m_value, other.m_data.m_value);\n\n#if JSON_DIAGNOSTIC_POSITIONS\n        swap(start_position, other.start_position);\n        swap(end_position, other.end_position);\n#endif\n\n        json_base_class_t::operator=(std::move(other));\n\n        set_parents();\n        assert_invariant();\n        return *this;\n    }\n\n    /// @brief destructor\n    /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/\n    ~basic_json() noexcept\n    {\n        assert_invariant(false);\n    }\n\n    /// @}\n\n  public:\n    ///////////////////////\n    // object inspection //\n    ///////////////////////\n\n    /// @name object inspection\n    /// Functions to inspect the type of a JSON value.\n    /// @{\n\n    /// @brief serialization\n    /// @sa https://json.nlohmann.me/api/basic_json/dump/\n    string_t dump(const int indent = -1,\n                  const char indent_char = ' ',\n                  const bool ensure_ascii = false,\n                  const error_handler_t error_handler = error_handler_t::strict) const\n    {\n        string_t result;\n        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);\n\n        if (indent >= 0)\n        {\n            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));\n        }\n        else\n        {\n            s.dump(*this, false, ensure_ascii, 0);\n        }\n\n        return result;\n    }\n\n    /// @brief return the type of the JSON value (explicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/type/\n    constexpr value_t type() const noexcept\n    {\n        return m_data.m_type;\n    }\n\n    /// @brief return whether type is primitive\n    /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/\n    constexpr bool is_primitive() const noexcept\n    {\n        return is_null() || is_string() || is_boolean() || is_number() || is_binary();\n    }\n\n    /// @brief return whether type is structured\n    /// @sa https://json.nlohmann.me/api/basic_json/is_structured/\n    constexpr bool is_structured() const noexcept\n    {\n        return is_array() || is_object();\n    }\n\n    /// @brief return whether value is null\n    /// @sa https://json.nlohmann.me/api/basic_json/is_null/\n    constexpr bool is_null() const noexcept\n    {\n        return m_data.m_type == value_t::null;\n    }\n\n    /// @brief return whether value is a boolean\n    /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/\n    constexpr bool is_boolean() const noexcept\n    {\n        return m_data.m_type == value_t::boolean;\n    }\n\n    /// @brief return whether value is a number\n    /// @sa https://json.nlohmann.me/api/basic_json/is_number/\n    constexpr bool is_number() const noexcept\n    {\n        return is_number_integer() || is_number_float();\n    }\n\n    /// @brief return whether value is an integer number\n    /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/\n    constexpr bool is_number_integer() const noexcept\n    {\n        return m_data.m_type == value_t::number_integer || m_data.m_type == value_t::number_unsigned;\n    }\n\n    /// @brief return whether value is an unsigned integer number\n    /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/\n    constexpr bool is_number_unsigned() const noexcept\n    {\n        return m_data.m_type == value_t::number_unsigned;\n    }\n\n    /// @brief return whether value is a floating-point number\n    /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/\n    constexpr bool is_number_float() const noexcept\n    {\n        return m_data.m_type == value_t::number_float;\n    }\n\n    /// @brief return whether value is an object\n    /// @sa https://json.nlohmann.me/api/basic_json/is_object/\n    constexpr bool is_object() const noexcept\n    {\n        return m_data.m_type == value_t::object;\n    }\n\n    /// @brief return whether value is an array\n    /// @sa https://json.nlohmann.me/api/basic_json/is_array/\n    constexpr bool is_array() const noexcept\n    {\n        return m_data.m_type == value_t::array;\n    }\n\n    /// @brief return whether value is a string\n    /// @sa https://json.nlohmann.me/api/basic_json/is_string/\n    constexpr bool is_string() const noexcept\n    {\n        return m_data.m_type == value_t::string;\n    }\n\n    /// @brief return whether value is a binary array\n    /// @sa https://json.nlohmann.me/api/basic_json/is_binary/\n    constexpr bool is_binary() const noexcept\n    {\n        return m_data.m_type == value_t::binary;\n    }\n\n    /// @brief return whether value is discarded\n    /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/\n    constexpr bool is_discarded() const noexcept\n    {\n        return m_data.m_type == value_t::discarded;\n    }\n\n    /// @brief return the type of the JSON value (implicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/\n    constexpr operator value_t() const noexcept\n    {\n        return m_data.m_type;\n    }\n\n    /// @}\n\n  private:\n    //////////////////\n    // value access //\n    //////////////////\n\n    /// get a boolean (explicit)\n    boolean_t get_impl(boolean_t* /*unused*/) const\n    {\n        if (JSON_HEDLEY_LIKELY(is_boolean()))\n        {\n            return m_data.m_value.boolean;\n        }\n\n        JSON_THROW(type_error::create(302, detail::concat(\"type must be boolean, but is \", type_name()), this));\n    }\n\n    /// get a pointer to the value (object)\n    object_t* get_impl_ptr(object_t* /*unused*/) noexcept\n    {\n        return is_object() ? m_data.m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (object)\n    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept\n    {\n        return is_object() ? m_data.m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    array_t* get_impl_ptr(array_t* /*unused*/) noexcept\n    {\n        return is_array() ? m_data.m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept\n    {\n        return is_array() ? m_data.m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    string_t* get_impl_ptr(string_t* /*unused*/) noexcept\n    {\n        return is_string() ? m_data.m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept\n    {\n        return is_string() ? m_data.m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept\n    {\n        return is_boolean() ? &m_data.m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept\n    {\n        return is_boolean() ? &m_data.m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept\n    {\n        return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept\n    {\n        return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept\n    {\n        return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept\n    {\n        return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept\n    {\n        return is_number_float() ? &m_data.m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept\n    {\n        return is_number_float() ? &m_data.m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept\n    {\n        return is_binary() ? m_data.m_value.binary : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept\n    {\n        return is_binary() ? m_data.m_value.binary : nullptr;\n    }\n\n    /*!\n    @brief helper function to implement get_ref()\n\n    This function helps to implement get_ref() without code duplication for\n    const and non-const overloads\n\n    @tparam ThisType will be deduced as `basic_json` or `const basic_json`\n\n    @throw type_error.303 if ReferenceType does not match underlying value\n    type of the current JSON\n    */\n    template<typename ReferenceType, typename ThisType>\n    static ReferenceType get_ref_impl(ThisType& obj)\n    {\n        // delegate the call to get_ptr<>()\n        auto* ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();\n\n        if (JSON_HEDLEY_LIKELY(ptr != nullptr))\n        {\n            return *ptr;\n        }\n\n        JSON_THROW(type_error::create(303, detail::concat(\"incompatible ReferenceType for get_ref, actual type is \", obj.type_name()), &obj));\n    }\n\n  public:\n    /// @name value access\n    /// Direct access to the stored value of a JSON value.\n    /// @{\n\n    /// @brief get a pointer value (implicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>()\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /// @brief get a pointer value (implicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/\n    template < typename PointerType, typename std::enable_if <\n                   std::is_pointer<PointerType>::value&&\n                   std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >\n    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>() const\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n  private:\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType ret;\n    JSONSerializer<ValueType>::from_json(*this, ret);\n    return ret;\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n    - @ref json_serializer<ValueType> does not have a `from_json()` method of\n      the form `ValueType from_json(const basic_json&)`\n\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get__ValueType_const}\n\n    @since version 2.1.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   detail::is_default_constructible<ValueType>::value&&\n                   detail::has_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))\n    {\n        auto ret = ValueType();\n        JSONSerializer<ValueType>::from_json(*this, ret);\n        return ret;\n    }\n\n    /*!\n    @brief get a value (explicit); special case\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    return JSONSerializer<ValueType>::from_json(*this);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json and\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `ValueType from_json(const basic_json&)`\n\n    @note If @ref json_serializer<ValueType> has both overloads of\n    `from_json()`, this one is chosen.\n\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @since version 2.1.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))\n    {\n        return JSONSerializer<ValueType>::from_json(*this);\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads converts the current @ref basic_json in a different\n    @ref basic_json type\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this, converted into @a BasicJsonType\n\n    @complexity Depending on the implementation of the called `from_json()`\n                method.\n\n    @since version 3.2.0\n    */\n    template < typename BasicJsonType,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value,\n                   int > = 0 >\n    BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads avoids a lot of template boilerplate, it can be seen as the\n    identity method\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this\n\n    @complexity Constant.\n\n    @since version 2.1.0\n    */\n    template<typename BasicJsonType,\n             detail::enable_if_t<\n                 std::is_same<BasicJsonType, basic_json_t>::value,\n                 int> = 0>\n    basic_json get_impl(detail::priority_tag<3> /*unused*/) const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n    @copydoc get()\n    */\n    template<typename PointerType,\n             detail::enable_if_t<\n                 std::is_pointer<PointerType>::value,\n                 int> = 0>\n    constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept\n    -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n  public:\n    /*!\n    @brief get a (pointer) value (explicit)\n\n    Performs explicit type conversion between the JSON value and a compatible value if required.\n\n    - If the requested type is a pointer to the internally stored JSON value that pointer is returned.\n    No copies are made.\n\n    - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible\n    from the current @ref basic_json.\n\n    - Otherwise the value is converted by calling the @ref json_serializer<ValueType> `from_json()`\n    method.\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @tparam ValueType if necessary\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required\n\n    @since version 2.1.0\n    */\n    template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>\n#if defined(JSON_HAS_CPP_14)\n    constexpr\n#endif\n    auto get() const noexcept(\n    noexcept(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {})))\n    -> decltype(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {}))\n    {\n        // we cannot static_assert on ValueTypeCV being non-const, because\n        // there is support for get<const basic_json_t>(), which is why we\n        // still need the uncvref\n        static_assert(!std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        return get_impl<ValueType>(detail::priority_tag<4> {});\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n\n    Explicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning The pointer becomes invalid if the underlying JSON object\n    changes.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get__PointerType}\n\n    @sa see @ref get_ptr() for explicit pointer-member access\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /// @brief get a value (explicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/get_to/\n    template < typename ValueType,\n               detail::enable_if_t <\n                   !detail::is_basic_json<ValueType>::value&&\n                   detail::has_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType & get_to(ValueType& v) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<ValueType>::from_json(*this, v);\n        return v;\n    }\n\n    // specialization to allow calling get_to with a basic_json value\n    // see https://github.com/nlohmann/json/issues/2175\n    template<typename ValueType,\n             detail::enable_if_t <\n                 detail::is_basic_json<ValueType>::value,\n                 int> = 0>\n    ValueType & get_to(ValueType& v) const\n    {\n        v = *this;\n        return v;\n    }\n\n    template <\n        typename T, std::size_t N,\n        typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n        detail::enable_if_t <\n            detail::has_from_json<basic_json_t, Array>::value, int > = 0 >\n    Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n    noexcept(noexcept(JSONSerializer<Array>::from_json(\n                          std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<Array>::from_json(*this, v);\n        return v;\n    }\n\n    /// @brief get a reference value (implicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/get_ref/\n    template<typename ReferenceType, typename std::enable_if<\n                 std::is_reference<ReferenceType>::value, int>::type = 0>\n    ReferenceType get_ref()\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /// @brief get a reference value (implicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/get_ref/\n    template < typename ReferenceType, typename std::enable_if <\n                   std::is_reference<ReferenceType>::value&&\n                   std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >\n    ReferenceType get_ref() const\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a value (implicit)\n\n    Implicit type conversion between the JSON value and a compatible value.\n    The call is realized by calling @ref get() const.\n\n    @tparam ValueType non-pointer type compatible to the JSON value, for\n    instance `int` for JSON integer numbers, `bool` for JSON booleans, or\n    `std::vector` types for JSON arrays. The character type of @ref string_t\n    as well as an initializer list of this type is excluded to avoid\n    ambiguities as these types implicitly convert to `std::string`.\n\n    @return copy of the JSON value, converted to type @a ValueType\n\n    @throw type_error.302 in case passed type @a ValueType is incompatible\n    to the JSON value type (e.g., the JSON value is of type boolean, but a\n    string is requested); see example below\n\n    @complexity Linear in the size of the JSON value.\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,operator__ValueType}\n\n    @since version 1.0.0\n    */\n    template < typename ValueType, typename std::enable_if <\n                   detail::conjunction <\n                       detail::negation<std::is_pointer<ValueType>>,\n                       detail::negation<std::is_same<ValueType, std::nullptr_t>>,\n                       detail::negation<std::is_same<ValueType, detail::json_ref<basic_json>>>,\n                                        detail::negation<std::is_same<ValueType, typename string_t::value_type>>,\n                                        detail::negation<detail::is_basic_json<ValueType>>,\n                                        detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,\n#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))\n                                                detail::negation<std::is_same<ValueType, std::string_view>>,\n#endif\n#if defined(JSON_HAS_CPP_17) && JSON_HAS_STATIC_RTTI\n                                                detail::negation<std::is_same<ValueType, std::any>>,\n#endif\n                                                detail::is_detected_lazy<detail::get_template_function, const basic_json_t&, ValueType>\n                                                >::value, int >::type = 0 >\n                                        JSON_EXPLICIT operator ValueType() const\n    {\n        // delegate the call to get<>() const\n        return get<ValueType>();\n    }\n\n    /// @brief get a binary value\n    /// @sa https://json.nlohmann.me/api/basic_json/get_binary/\n    binary_t& get_binary()\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, detail::concat(\"type must be binary, but is \", type_name()), this));\n        }\n\n        return *get_ptr<binary_t*>();\n    }\n\n    /// @brief get a binary value\n    /// @sa https://json.nlohmann.me/api/basic_json/get_binary/\n    const binary_t& get_binary() const\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, detail::concat(\"type must be binary, but is \", type_name()), this));\n        }\n\n        return *get_ptr<const binary_t*>();\n    }\n\n    /// @}\n\n    ////////////////////\n    // element access //\n    ////////////////////\n\n    /// @name element access\n    /// Access to the JSON value.\n    /// @{\n\n    /// @brief access specified array element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    reference at(size_type idx)\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return set_parent(m_data.m_value.array->at(idx));\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create a better exception explanation\n                JSON_THROW(out_of_range::create(401, detail::concat(\"array index \", std::to_string(idx), \" is out of range\"), this));\n            } // cppcheck-suppress[missingReturn]\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n    }\n\n    /// @brief access specified array element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    const_reference at(size_type idx) const\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_data.m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create a better exception explanation\n                JSON_THROW(out_of_range::create(401, detail::concat(\"array index \", std::to_string(idx), \" is out of range\"), this));\n            } // cppcheck-suppress[missingReturn]\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n    }\n\n    /// @brief access specified object element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    reference at(const typename object_t::key_type& key)\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n\n        auto it = m_data.m_value.object->find(key);\n        if (it == m_data.m_value.object->end())\n        {\n            JSON_THROW(out_of_range::create(403, detail::concat(\"key '\", key, \"' not found\"), this));\n        }\n        return set_parent(it->second);\n    }\n\n    /// @brief access specified object element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    reference at(KeyType && key)\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n\n        auto it = m_data.m_value.object->find(std::forward<KeyType>(key));\n        if (it == m_data.m_value.object->end())\n        {\n            JSON_THROW(out_of_range::create(403, detail::concat(\"key '\", string_t(std::forward<KeyType>(key)), \"' not found\"), this));\n        }\n        return set_parent(it->second);\n    }\n\n    /// @brief access specified object element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    const_reference at(const typename object_t::key_type& key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n\n        auto it = m_data.m_value.object->find(key);\n        if (it == m_data.m_value.object->end())\n        {\n            JSON_THROW(out_of_range::create(403, detail::concat(\"key '\", key, \"' not found\"), this));\n        }\n        return it->second;\n    }\n\n    /// @brief access specified object element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    const_reference at(KeyType && key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n\n        auto it = m_data.m_value.object->find(std::forward<KeyType>(key));\n        if (it == m_data.m_value.object->end())\n        {\n            JSON_THROW(out_of_range::create(403, detail::concat(\"key '\", string_t(std::forward<KeyType>(key)), \"' not found\"), this));\n        }\n        return it->second;\n    }\n\n    /// @brief access specified array element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    reference operator[](size_type idx)\n    {\n        // implicitly convert a null value to an empty array\n        if (is_null())\n        {\n            m_data.m_type = value_t::array;\n            m_data.m_value.array = create<array_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // fill up the array with null values if given idx is outside the range\n            if (idx >= m_data.m_value.array->size())\n            {\n#if JSON_DIAGNOSTICS\n                // remember array size & capacity before resizing\n                const auto old_size = m_data.m_value.array->size();\n                const auto old_capacity = m_data.m_value.array->capacity();\n#endif\n                m_data.m_value.array->resize(idx + 1);\n\n#if JSON_DIAGNOSTICS\n                if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity))\n                {\n                    // capacity has changed: update all parents\n                    set_parents();\n                }\n                else\n                {\n                    // set parent for values added above\n                    set_parents(begin() + static_cast<typename iterator::difference_type>(old_size), static_cast<typename iterator::difference_type>(idx + 1 - old_size));\n                }\n#endif\n                assert_invariant();\n            }\n\n            return m_data.m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a numeric argument with \", type_name()), this));\n    }\n\n    /// @brief access specified array element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    const_reference operator[](size_type idx) const\n    {\n        // const operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            return m_data.m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a numeric argument with \", type_name()), this));\n    }\n\n    /// @brief access specified object element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    reference operator[](typename object_t::key_type key) // NOLINT(performance-unnecessary-value-param)\n    {\n        // implicitly convert a null value to an empty object\n        if (is_null())\n        {\n            m_data.m_type = value_t::object;\n            m_data.m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            auto result = m_data.m_value.object->emplace(std::move(key), nullptr);\n            return set_parent(result.first->second);\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a string argument with \", type_name()), this));\n    }\n\n    /// @brief access specified object element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    const_reference operator[](const typename object_t::key_type& key) const\n    {\n        // const operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            auto it = m_data.m_value.object->find(key);\n            JSON_ASSERT(it != m_data.m_value.object->end());\n            return it->second;\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a string argument with \", type_name()), this));\n    }\n\n    // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC\n    // (they seemingly cannot be constrained to resolve the ambiguity)\n    template<typename T>\n    reference operator[](T* key)\n    {\n        return operator[](typename object_t::key_type(key));\n    }\n\n    template<typename T>\n    const_reference operator[](T* key) const\n    {\n        return operator[](typename object_t::key_type(key));\n    }\n\n    /// @brief access specified object element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >\n    reference operator[](KeyType && key)\n    {\n        // implicitly convert a null value to an empty object\n        if (is_null())\n        {\n            m_data.m_type = value_t::object;\n            m_data.m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            auto result = m_data.m_value.object->emplace(std::forward<KeyType>(key), nullptr);\n            return set_parent(result.first->second);\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a string argument with \", type_name()), this));\n    }\n\n    /// @brief access specified object element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >\n    const_reference operator[](KeyType && key) const\n    {\n        // const operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            auto it = m_data.m_value.object->find(std::forward<KeyType>(key));\n            JSON_ASSERT(it != m_data.m_value.object->end());\n            return it->second;\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a string argument with \", type_name()), this));\n    }\n\n  private:\n    template<typename KeyType>\n    using is_comparable_with_object_key = detail::is_comparable <\n        object_comparator_t, const typename object_t::key_type&, KeyType >;\n\n    template<typename ValueType>\n    using value_return_type = std::conditional <\n        detail::is_c_string_uncvref<ValueType>::value,\n        string_t, typename std::decay<ValueType>::type >;\n\n  public:\n    /// @brief access specified object element with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, detail::enable_if_t <\n                   !detail::is_transparent<object_comparator_t>::value\n                   && detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // If 'key' is found, return its value. Otherwise, return `default_value'.\n            const auto it = find(key);\n            if (it != end())\n            {\n                return it->template get<ValueType>();\n            }\n\n            return default_value;\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    /// @brief access specified object element with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,\n               detail::enable_if_t <\n                   !detail::is_transparent<object_comparator_t>::value\n                   && detail::is_getable<basic_json_t, ReturnType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ReturnType value(const typename object_t::key_type& key, ValueType && default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // If 'key' is found, return its value. Otherwise, return `default_value'.\n            const auto it = find(key);\n            if (it != end())\n            {\n                return it->template get<ReturnType>();\n            }\n\n            return std::forward<ValueType>(default_value);\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    /// @brief access specified object element with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, class KeyType, detail::enable_if_t <\n                   detail::is_transparent<object_comparator_t>::value\n                   && !detail::is_json_pointer<KeyType>::value\n                   && is_comparable_with_object_key<KeyType>::value\n                   && detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ValueType value(KeyType && key, const ValueType& default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // If 'key' is found, return its value. Otherwise, return `default_value'.\n            const auto it = find(std::forward<KeyType>(key));\n            if (it != end())\n            {\n                return it->template get<ValueType>();\n            }\n\n            return default_value;\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    /// @brief access specified object element via JSON Pointer with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, class KeyType, class ReturnType = typename value_return_type<ValueType>::type,\n               detail::enable_if_t <\n                   detail::is_transparent<object_comparator_t>::value\n                   && !detail::is_json_pointer<KeyType>::value\n                   && is_comparable_with_object_key<KeyType>::value\n                   && detail::is_getable<basic_json_t, ReturnType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ReturnType value(KeyType && key, ValueType && default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // If 'key' is found, return its value. Otherwise, return `default_value'.\n            const auto it = find(std::forward<KeyType>(key));\n            if (it != end())\n            {\n                return it->template get<ReturnType>();\n            }\n\n            return std::forward<ValueType>(default_value);\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    /// @brief access specified object element via JSON Pointer with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, detail::enable_if_t <\n                   detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ValueType value(const json_pointer& ptr, const ValueType& default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // If the pointer resolves to a value, return it. Otherwise, return\n            // 'default_value'.\n            JSON_TRY\n            {\n                return ptr.get_checked(this).template get<ValueType>();\n            }\n            JSON_INTERNAL_CATCH (out_of_range&)\n            {\n                return default_value;\n            }\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    /// @brief access specified object element via JSON Pointer with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,\n               detail::enable_if_t <\n                   detail::is_getable<basic_json_t, ReturnType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ReturnType value(const json_pointer& ptr, ValueType && default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // If the pointer resolves to a value, return it. Otherwise, return\n            // 'default_value'.\n            JSON_TRY\n            {\n                return ptr.get_checked(this).template get<ReturnType>();\n            }\n            JSON_INTERNAL_CATCH (out_of_range&)\n            {\n                return std::forward<ValueType>(default_value);\n            }\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    template < class ValueType, class BasicJsonType, detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value\n                   && detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const\n    {\n        return value(ptr.convert(), default_value);\n    }\n\n    template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value\n                   && detail::is_getable<basic_json_t, ReturnType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    ReturnType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, ValueType && default_value) const\n    {\n        return value(ptr.convert(), std::forward<ValueType>(default_value));\n    }\n\n    /// @brief access the first element\n    /// @sa https://json.nlohmann.me/api/basic_json/front/\n    reference front()\n    {\n        return *begin();\n    }\n\n    /// @brief access the first element\n    /// @sa https://json.nlohmann.me/api/basic_json/front/\n    const_reference front() const\n    {\n        return *cbegin();\n    }\n\n    /// @brief access the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/back/\n    reference back()\n    {\n        auto tmp = end();\n        --tmp;\n        return *tmp;\n    }\n\n    /// @brief access the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/back/\n    const_reference back() const\n    {\n        auto tmp = cend();\n        --tmp;\n        return *tmp;\n    }\n\n    /// @brief remove element given an iterator\n    /// @sa https://json.nlohmann.me/api/basic_json/erase/\n    template < class IteratorType, detail::enable_if_t <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >\n    IteratorType erase(IteratorType pos) // NOLINT(performance-unnecessary-value-param)\n    {\n        // make sure the iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", this));\n        }\n\n        IteratorType result = end();\n\n        switch (m_data.m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))\n                {\n                    JSON_THROW(invalid_iterator::create(205, \"iterator out of range\", this));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1);\n                    m_data.m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1);\n                    m_data.m_value.binary = nullptr;\n                }\n\n                m_data.m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_data.m_value.object->erase(pos.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_data.m_value.array->erase(pos.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(type_error::create(307, detail::concat(\"cannot use erase() with \", type_name()), this));\n        }\n\n        return result;\n    }\n\n    /// @brief remove elements given an iterator range\n    /// @sa https://json.nlohmann.me/api/basic_json/erase/\n    template < class IteratorType, detail::enable_if_t <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >\n    IteratorType erase(IteratorType first, IteratorType last) // NOLINT(performance-unnecessary-value-param)\n    {\n        // make sure the iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(203, \"iterators do not fit current value\", this));\n        }\n\n        IteratorType result = end();\n\n        switch (m_data.m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()\n                                       || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\", this));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1);\n                    m_data.m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1);\n                    m_data.m_value.binary = nullptr;\n                }\n\n                m_data.m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_data.m_value.object->erase(first.m_it.object_iterator,\n                                              last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_data.m_value.array->erase(first.m_it.array_iterator,\n                                             last.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(type_error::create(307, detail::concat(\"cannot use erase() with \", type_name()), this));\n        }\n\n        return result;\n    }\n\n  private:\n    template < typename KeyType, detail::enable_if_t <\n                   detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >\n    size_type erase_internal(KeyType && key)\n    {\n        // this erase only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(307, detail::concat(\"cannot use erase() with \", type_name()), this));\n        }\n\n        return m_data.m_value.object->erase(std::forward<KeyType>(key));\n    }\n\n    template < typename KeyType, detail::enable_if_t <\n                   !detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >\n    size_type erase_internal(KeyType && key)\n    {\n        // this erase only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(307, detail::concat(\"cannot use erase() with \", type_name()), this));\n        }\n\n        const auto it = m_data.m_value.object->find(std::forward<KeyType>(key));\n        if (it != m_data.m_value.object->end())\n        {\n            m_data.m_value.object->erase(it);\n            return 1;\n        }\n        return 0;\n    }\n\n  public:\n\n    /// @brief remove element from a JSON object given a key\n    /// @sa https://json.nlohmann.me/api/basic_json/erase/\n    size_type erase(const typename object_t::key_type& key)\n    {\n        // the indirection via erase_internal() is added to avoid making this\n        // function a template and thus de-rank it during overload resolution\n        return erase_internal(key);\n    }\n\n    /// @brief remove element from a JSON object given a key\n    /// @sa https://json.nlohmann.me/api/basic_json/erase/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    size_type erase(KeyType && key)\n    {\n        return erase_internal(std::forward<KeyType>(key));\n    }\n\n    /// @brief remove element from a JSON array given an index\n    /// @sa https://json.nlohmann.me/api/basic_json/erase/\n    void erase(const size_type idx)\n    {\n        // this erase only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            if (JSON_HEDLEY_UNLIKELY(idx >= size()))\n            {\n                JSON_THROW(out_of_range::create(401, detail::concat(\"array index \", std::to_string(idx), \" is out of range\"), this));\n            }\n\n            m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx));\n        }\n        else\n        {\n            JSON_THROW(type_error::create(307, detail::concat(\"cannot use erase() with \", type_name()), this));\n        }\n    }\n\n    /// @}\n\n    ////////////\n    // lookup //\n    ////////////\n\n    /// @name lookup\n    /// @{\n\n    /// @brief find an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/find/\n    iterator find(const typename object_t::key_type& key)\n    {\n        auto result = end();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_data.m_value.object->find(key);\n        }\n\n        return result;\n    }\n\n    /// @brief find an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/find/\n    const_iterator find(const typename object_t::key_type& key) const\n    {\n        auto result = cend();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_data.m_value.object->find(key);\n        }\n\n        return result;\n    }\n\n    /// @brief find an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/find/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    iterator find(KeyType && key)\n    {\n        auto result = end();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));\n        }\n\n        return result;\n    }\n\n    /// @brief find an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/find/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    const_iterator find(KeyType && key) const\n    {\n        auto result = cend();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));\n        }\n\n        return result;\n    }\n\n    /// @brief returns the number of occurrences of a key in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/count/\n    size_type count(const typename object_t::key_type& key) const\n    {\n        // return 0 for all nonobject types\n        return is_object() ? m_data.m_value.object->count(key) : 0;\n    }\n\n    /// @brief returns the number of occurrences of a key in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/count/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    size_type count(KeyType && key) const\n    {\n        // return 0 for all nonobject types\n        return is_object() ? m_data.m_value.object->count(std::forward<KeyType>(key)) : 0;\n    }\n\n    /// @brief check the existence of an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/contains/\n    bool contains(const typename object_t::key_type& key) const\n    {\n        return is_object() && m_data.m_value.object->find(key) != m_data.m_value.object->end();\n    }\n\n    /// @brief check the existence of an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/contains/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    bool contains(KeyType && key) const\n    {\n        return is_object() && m_data.m_value.object->find(std::forward<KeyType>(key)) != m_data.m_value.object->end();\n    }\n\n    /// @brief check the existence of an element in a JSON object given a JSON pointer\n    /// @sa https://json.nlohmann.me/api/basic_json/contains/\n    bool contains(const json_pointer& ptr) const\n    {\n        return ptr.contains(this);\n    }\n\n    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    bool contains(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr) const\n    {\n        return ptr.contains(this);\n    }\n\n    /// @}\n\n    ///////////////\n    // iterators //\n    ///////////////\n\n    /// @name iterators\n    /// @{\n\n    /// @brief returns an iterator to the first element\n    /// @sa https://json.nlohmann.me/api/basic_json/begin/\n    iterator begin() noexcept\n    {\n        iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /// @brief returns an iterator to the first element\n    /// @sa https://json.nlohmann.me/api/basic_json/begin/\n    const_iterator begin() const noexcept\n    {\n        return cbegin();\n    }\n\n    /// @brief returns a const iterator to the first element\n    /// @sa https://json.nlohmann.me/api/basic_json/cbegin/\n    const_iterator cbegin() const noexcept\n    {\n        const_iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /// @brief returns an iterator to one past the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/end/\n    iterator end() noexcept\n    {\n        iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /// @brief returns an iterator to one past the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/end/\n    const_iterator end() const noexcept\n    {\n        return cend();\n    }\n\n    /// @brief returns an iterator to one past the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/cend/\n    const_iterator cend() const noexcept\n    {\n        const_iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /// @brief returns an iterator to the reverse-beginning\n    /// @sa https://json.nlohmann.me/api/basic_json/rbegin/\n    reverse_iterator rbegin() noexcept\n    {\n        return reverse_iterator(end());\n    }\n\n    /// @brief returns an iterator to the reverse-beginning\n    /// @sa https://json.nlohmann.me/api/basic_json/rbegin/\n    const_reverse_iterator rbegin() const noexcept\n    {\n        return crbegin();\n    }\n\n    /// @brief returns an iterator to the reverse-end\n    /// @sa https://json.nlohmann.me/api/basic_json/rend/\n    reverse_iterator rend() noexcept\n    {\n        return reverse_iterator(begin());\n    }\n\n    /// @brief returns an iterator to the reverse-end\n    /// @sa https://json.nlohmann.me/api/basic_json/rend/\n    const_reverse_iterator rend() const noexcept\n    {\n        return crend();\n    }\n\n    /// @brief returns a const reverse iterator to the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/crbegin/\n    const_reverse_iterator crbegin() const noexcept\n    {\n        return const_reverse_iterator(cend());\n    }\n\n    /// @brief returns a const reverse iterator to one before the first\n    /// @sa https://json.nlohmann.me/api/basic_json/crend/\n    const_reverse_iterator crend() const noexcept\n    {\n        return const_reverse_iterator(cbegin());\n    }\n\n  public:\n    /// @brief wrapper to access iterator member functions in range-based for\n    /// @sa https://json.nlohmann.me/api/basic_json/items/\n    /// @deprecated This function is deprecated since 3.1.0 and will be removed in\n    ///             version 4.0.0 of the library. Please use @ref items() instead;\n    ///             that is, replace `json::iterator_wrapper(j)` with `j.items()`.\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /// @brief wrapper to access iterator member functions in range-based for\n    /// @sa https://json.nlohmann.me/api/basic_json/items/\n    /// @deprecated This function is deprecated since 3.1.0 and will be removed in\n    ///         version 4.0.0 of the library. Please use @ref items() instead;\n    ///         that is, replace `json::iterator_wrapper(j)` with `j.items()`.\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /// @brief helper to access iterator member functions in range-based for\n    /// @sa https://json.nlohmann.me/api/basic_json/items/\n    iteration_proxy<iterator> items() noexcept\n    {\n        return iteration_proxy<iterator>(*this);\n    }\n\n    /// @brief helper to access iterator member functions in range-based for\n    /// @sa https://json.nlohmann.me/api/basic_json/items/\n    iteration_proxy<const_iterator> items() const noexcept\n    {\n        return iteration_proxy<const_iterator>(*this);\n    }\n\n    /// @}\n\n    //////////////\n    // capacity //\n    //////////////\n\n    /// @name capacity\n    /// @{\n\n    /// @brief checks whether the container is empty.\n    /// @sa https://json.nlohmann.me/api/basic_json/empty/\n    bool empty() const noexcept\n    {\n        switch (m_data.m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return true;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::empty()\n                return m_data.m_value.array->empty();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::empty()\n                return m_data.m_value.object->empty();\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types are nonempty\n                return false;\n            }\n        }\n    }\n\n    /// @brief returns the number of elements\n    /// @sa https://json.nlohmann.me/api/basic_json/size/\n    size_type size() const noexcept\n    {\n        switch (m_data.m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return 0;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::size()\n                return m_data.m_value.array->size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::size()\n                return m_data.m_value.object->size();\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types have size 1\n                return 1;\n            }\n        }\n    }\n\n    /// @brief returns the maximum possible number of elements\n    /// @sa https://json.nlohmann.me/api/basic_json/max_size/\n    size_type max_size() const noexcept\n    {\n        switch (m_data.m_type)\n        {\n            case value_t::array:\n            {\n                // delegate call to array_t::max_size()\n                return m_data.m_value.array->max_size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::max_size()\n                return m_data.m_value.object->max_size();\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types have max_size() == size()\n                return size();\n            }\n        }\n    }\n\n    /// @}\n\n    ///////////////\n    // modifiers //\n    ///////////////\n\n    /// @name modifiers\n    /// @{\n\n    /// @brief clears the contents\n    /// @sa https://json.nlohmann.me/api/basic_json/clear/\n    void clear() noexcept\n    {\n        switch (m_data.m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_data.m_value.number_integer = 0;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_data.m_value.number_unsigned = 0;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_data.m_value.number_float = 0.0;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_data.m_value.boolean = false;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_data.m_value.string->clear();\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_data.m_value.binary->clear();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_data.m_value.array->clear();\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_data.m_value.object->clear();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n    /// @brief add an object to an array\n    /// @sa https://json.nlohmann.me/api/basic_json/push_back/\n    void push_back(basic_json&& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, detail::concat(\"cannot use push_back() with \", type_name()), this));\n        }\n\n        // transform a null object into an array\n        if (is_null())\n        {\n            m_data.m_type = value_t::array;\n            m_data.m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add the element to the array (move semantics)\n        const auto old_capacity = m_data.m_value.array->capacity();\n        m_data.m_value.array->push_back(std::move(val));\n        set_parent(m_data.m_value.array->back(), old_capacity);\n        // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor\n    }\n\n    /// @brief add an object to an array\n    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/\n    reference operator+=(basic_json&& val)\n    {\n        push_back(std::move(val));\n        return *this;\n    }\n\n    /// @brief add an object to an array\n    /// @sa https://json.nlohmann.me/api/basic_json/push_back/\n    void push_back(const basic_json& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, detail::concat(\"cannot use push_back() with \", type_name()), this));\n        }\n\n        // transform a null object into an array\n        if (is_null())\n        {\n            m_data.m_type = value_t::array;\n            m_data.m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add the element to the array\n        const auto old_capacity = m_data.m_value.array->capacity();\n        m_data.m_value.array->push_back(val);\n        set_parent(m_data.m_value.array->back(), old_capacity);\n    }\n\n    /// @brief add an object to an array\n    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/\n    reference operator+=(const basic_json& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /// @brief add an object to an object\n    /// @sa https://json.nlohmann.me/api/basic_json/push_back/\n    void push_back(const typename object_t::value_type& val)\n    {\n        // push_back only works for null objects or objects\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(308, detail::concat(\"cannot use push_back() with \", type_name()), this));\n        }\n\n        // transform a null object into an object\n        if (is_null())\n        {\n            m_data.m_type = value_t::object;\n            m_data.m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add the element to the object\n        auto res = m_data.m_value.object->insert(val);\n        set_parent(res.first->second);\n    }\n\n    /// @brief add an object to an object\n    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/\n    reference operator+=(const typename object_t::value_type& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /// @brief add an object to an object\n    /// @sa https://json.nlohmann.me/api/basic_json/push_back/\n    void push_back(initializer_list_t init)\n    {\n        if (is_object() && init.size() == 2 && (*init.begin())->is_string())\n        {\n            basic_json&& key = init.begin()->moved_or_copied();\n            push_back(typename object_t::value_type(\n                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));\n        }\n        else\n        {\n            push_back(basic_json(init));\n        }\n    }\n\n    /// @brief add an object to an object\n    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/\n    reference operator+=(initializer_list_t init)\n    {\n        push_back(init);\n        return *this;\n    }\n\n    /// @brief add an object to an array\n    /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/\n    template<class... Args>\n    reference emplace_back(Args&& ... args)\n    {\n        // emplace_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(311, detail::concat(\"cannot use emplace_back() with \", type_name()), this));\n        }\n\n        // transform a null object into an array\n        if (is_null())\n        {\n            m_data.m_type = value_t::array;\n            m_data.m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add the element to the array (perfect forwarding)\n        const auto old_capacity = m_data.m_value.array->capacity();\n        m_data.m_value.array->emplace_back(std::forward<Args>(args)...);\n        return set_parent(m_data.m_value.array->back(), old_capacity);\n    }\n\n    /// @brief add an object to an object if key does not exist\n    /// @sa https://json.nlohmann.me/api/basic_json/emplace/\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&& ... args)\n    {\n        // emplace only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(311, detail::concat(\"cannot use emplace() with \", type_name()), this));\n        }\n\n        // transform a null object into an object\n        if (is_null())\n        {\n            m_data.m_type = value_t::object;\n            m_data.m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add the element to the array (perfect forwarding)\n        auto res = m_data.m_value.object->emplace(std::forward<Args>(args)...);\n        set_parent(res.first->second);\n\n        // create a result iterator and set iterator to the result of emplace\n        auto it = begin();\n        it.m_it.object_iterator = res.first;\n\n        // return pair of iterator and boolean\n        return {it, res.second};\n    }\n\n    /// Helper for insertion of an iterator\n    /// @note: This uses std::distance to support GCC 4.8,\n    ///        see https://github.com/nlohmann/json/pull/1257\n    template<typename... Args>\n    iterator insert_iterator(const_iterator pos, Args&& ... args) // NOLINT(performance-unnecessary-value-param)\n    {\n        iterator result(this);\n        JSON_ASSERT(m_data.m_value.array != nullptr);\n\n        auto insert_pos = std::distance(m_data.m_value.array->begin(), pos.m_it.array_iterator);\n        m_data.m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);\n        result.m_it.array_iterator = m_data.m_value.array->begin() + insert_pos;\n\n        // This could have been written as:\n        // result.m_it.array_iterator = m_data.m_value.array->insert(pos.m_it.array_iterator, cnt, val);\n        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.\n\n        set_parents();\n        return result;\n    }\n\n    /// @brief inserts element into array\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    iterator insert(const_iterator pos, const basic_json& val) // NOLINT(performance-unnecessary-value-param)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", this));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, val);\n        }\n\n        JSON_THROW(type_error::create(309, detail::concat(\"cannot use insert() with \", type_name()), this));\n    }\n\n    /// @brief inserts element into array\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    iterator insert(const_iterator pos, basic_json&& val) // NOLINT(performance-unnecessary-value-param)\n    {\n        return insert(pos, val);\n    }\n\n    /// @brief inserts copies of element into array\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    iterator insert(const_iterator pos, size_type cnt, const basic_json& val) // NOLINT(performance-unnecessary-value-param)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", this));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, cnt, val);\n        }\n\n        JSON_THROW(type_error::create(309, detail::concat(\"cannot use insert() with \", type_name()), this));\n    }\n\n    /// @brief inserts range of elements into array\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    iterator insert(const_iterator pos, const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, detail::concat(\"cannot use insert() with \", type_name()), this));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", this));\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(first.m_object == this))\n        {\n            JSON_THROW(invalid_iterator::create(211, \"passed iterators may not belong to container\", this));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);\n    }\n\n    /// @brief inserts elements from initializer list into array\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    iterator insert(const_iterator pos, initializer_list_t ilist) // NOLINT(performance-unnecessary-value-param)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, detail::concat(\"cannot use insert() with \", type_name()), this));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", this));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, ilist.begin(), ilist.end());\n    }\n\n    /// @brief inserts range of elements into object\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    void insert(const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param)\n    {\n        // insert only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(309, detail::concat(\"cannot use insert() with \", type_name()), this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", this));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\", this));\n        }\n\n        m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);\n        set_parents();\n    }\n\n    /// @brief updates a JSON object from another object, overwriting existing keys\n    /// @sa https://json.nlohmann.me/api/basic_json/update/\n    void update(const_reference j, bool merge_objects = false)\n    {\n        update(j.begin(), j.end(), merge_objects);\n    }\n\n    /// @brief updates a JSON object from another object, overwriting existing keys\n    /// @sa https://json.nlohmann.me/api/basic_json/update/\n    void update(const_iterator first, const_iterator last, bool merge_objects = false) // NOLINT(performance-unnecessary-value-param)\n    {\n        // implicitly convert a null value to an empty object\n        if (is_null())\n        {\n            m_data.m_type = value_t::object;\n            m_data.m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(312, detail::concat(\"cannot use update() with \", type_name()), this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", this));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))\n        {\n            JSON_THROW(type_error::create(312, detail::concat(\"cannot use update() with \", first.m_object->type_name()), first.m_object));\n        }\n\n        for (auto it = first; it != last; ++it)\n        {\n            if (merge_objects && it.value().is_object())\n            {\n                auto it2 = m_data.m_value.object->find(it.key());\n                if (it2 != m_data.m_value.object->end())\n                {\n                    it2->second.update(it.value(), true);\n                    continue;\n                }\n            }\n            m_data.m_value.object->operator[](it.key()) = it.value();\n#if JSON_DIAGNOSTICS\n            m_data.m_value.object->operator[](it.key()).m_parent = this;\n#endif\n        }\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(reference other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap)\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        std::swap(m_data.m_type, other.m_data.m_type);\n        std::swap(m_data.m_value, other.m_data.m_value);\n\n        set_parents();\n        other.set_parents();\n        assert_invariant();\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    friend void swap(reference left, reference right) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap)\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        left.swap(right);\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(array_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)\n    {\n        // swap only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            using std::swap;\n            swap(*(m_data.m_value.array), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, detail::concat(\"cannot use swap(array_t&) with \", type_name()), this));\n        }\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(object_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)\n    {\n        // swap only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            using std::swap;\n            swap(*(m_data.m_value.object), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, detail::concat(\"cannot use swap(object_t&) with \", type_name()), this));\n        }\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(string_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_string()))\n        {\n            using std::swap;\n            swap(*(m_data.m_value.string), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, detail::concat(\"cannot use swap(string_t&) with \", type_name()), this));\n        }\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(binary_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            using std::swap;\n            swap(*(m_data.m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, detail::concat(\"cannot use swap(binary_t&) with \", type_name()), this));\n        }\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            using std::swap;\n            swap(*(m_data.m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, detail::concat(\"cannot use swap(binary_t::container_type&) with \", type_name()), this));\n        }\n    }\n\n    /// @}\n\n    //////////////////////////////////////////\n    // lexicographical comparison operators //\n    //////////////////////////////////////////\n\n    /// @name lexicographical comparison operators\n    /// @{\n\n    // note parentheses around operands are necessary; see\n    // https://github.com/nlohmann/json/issues/1530\n#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result)                       \\\n    const auto lhs_type = lhs.type();                                                                    \\\n    const auto rhs_type = rhs.type();                                                                    \\\n    \\\n    if (lhs_type == rhs_type) /* NOLINT(readability/braces) */                                           \\\n    {                                                                                                    \\\n        switch (lhs_type)                                                                                \\\n        {                                                                                                \\\n            case value_t::array:                                                                         \\\n                return (*lhs.m_data.m_value.array) op (*rhs.m_data.m_value.array);                                     \\\n                \\\n            case value_t::object:                                                                        \\\n                return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object);                                   \\\n                \\\n            case value_t::null:                                                                          \\\n                return (null_result);                                                                    \\\n                \\\n            case value_t::string:                                                                        \\\n                return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string);                                   \\\n                \\\n            case value_t::boolean:                                                                       \\\n                return (lhs.m_data.m_value.boolean) op (rhs.m_data.m_value.boolean);                                   \\\n                \\\n            case value_t::number_integer:                                                                \\\n                return (lhs.m_data.m_value.number_integer) op (rhs.m_data.m_value.number_integer);                     \\\n                \\\n            case value_t::number_unsigned:                                                               \\\n                return (lhs.m_data.m_value.number_unsigned) op (rhs.m_data.m_value.number_unsigned);                   \\\n                \\\n            case value_t::number_float:                                                                  \\\n                return (lhs.m_data.m_value.number_float) op (rhs.m_data.m_value.number_float);                         \\\n                \\\n            case value_t::binary:                                                                        \\\n                return (*lhs.m_data.m_value.binary) op (*rhs.m_data.m_value.binary);                                   \\\n                \\\n            case value_t::discarded:                                                                     \\\n            default:                                                                                     \\\n                return (unordered_result);                                                               \\\n        }                                                                                                \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)                   \\\n    {                                                                                                    \\\n        return static_cast<number_float_t>(lhs.m_data.m_value.number_integer) op rhs.m_data.m_value.number_float;      \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)                   \\\n    {                                                                                                    \\\n        return lhs.m_data.m_value.number_float op static_cast<number_float_t>(rhs.m_data.m_value.number_integer);      \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)                  \\\n    {                                                                                                    \\\n        return static_cast<number_float_t>(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_float;     \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)                  \\\n    {                                                                                                    \\\n        return lhs.m_data.m_value.number_float op static_cast<number_float_t>(rhs.m_data.m_value.number_unsigned);     \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)                \\\n    {                                                                                                    \\\n        return static_cast<number_integer_t>(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_integer; \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)                \\\n    {                                                                                                    \\\n        return lhs.m_data.m_value.number_integer op static_cast<number_integer_t>(rhs.m_data.m_value.number_unsigned); \\\n    }                                                                                                    \\\n    else if(compares_unordered(lhs, rhs))\\\n    {\\\n        return (unordered_result);\\\n    }\\\n    \\\n    return (default_result);\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    // returns true if:\n    // - any operand is NaN and the other operand is of number type\n    // - any operand is discarded\n    // in legacy mode, discarded values are considered ordered if\n    // an operation is computed as an odd number of inverses of others\n    static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept\n    {\n        if ((lhs.is_number_float() && std::isnan(lhs.m_data.m_value.number_float) && rhs.is_number())\n                || (rhs.is_number_float() && std::isnan(rhs.m_data.m_value.number_float) && lhs.is_number()))\n        {\n            return true;\n        }\n#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON\n        return (lhs.is_discarded() || rhs.is_discarded()) && !inverse;\n#else\n        static_cast<void>(inverse);\n        return lhs.is_discarded() || rhs.is_discarded();\n#endif\n    }\n\n  private:\n    bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept\n    {\n        return compares_unordered(*this, rhs, inverse);\n    }\n\n  public:\n#if JSON_HAS_THREE_WAY_COMPARISON\n    /// @brief comparison: equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/\n    bool operator==(const_reference rhs) const noexcept\n    {\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n        const_reference lhs = *this;\n        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n    }\n\n    /// @brief comparison: equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/\n    template<typename ScalarType>\n    requires std::is_scalar_v<ScalarType>\n    bool operator==(ScalarType rhs) const noexcept\n    {\n        return *this == basic_json(rhs);\n    }\n\n    /// @brief comparison: not equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/\n    bool operator!=(const_reference rhs) const noexcept\n    {\n        if (compares_unordered(rhs, true))\n        {\n            return false;\n        }\n        return !operator==(rhs);\n    }\n\n    /// @brief comparison: 3-way\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/\n    std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*\n    {\n        const_reference lhs = *this;\n        // default_result is used if we cannot compare values. In that case,\n        // we compare types.\n        JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*\n                                std::partial_ordering::equivalent,\n                                std::partial_ordering::unordered,\n                                lhs_type <=> rhs_type) // *NOPAD*\n    }\n\n    /// @brief comparison: 3-way\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/\n    template<typename ScalarType>\n    requires std::is_scalar_v<ScalarType>\n    std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD*\n    {\n        return *this <=> basic_json(rhs); // *NOPAD*\n    }\n\n#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON\n    // all operators that are computed as an odd number of inverses of others\n    // need to be overloaded to emulate the legacy comparison behavior\n\n    /// @brief comparison: less than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)\n    bool operator<=(const_reference rhs) const noexcept\n    {\n        if (compares_unordered(rhs, true))\n        {\n            return false;\n        }\n        return !(rhs < *this);\n    }\n\n    /// @brief comparison: less than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/\n    template<typename ScalarType>\n    requires std::is_scalar_v<ScalarType>\n    bool operator<=(ScalarType rhs) const noexcept\n    {\n        return *this <= basic_json(rhs);\n    }\n\n    /// @brief comparison: greater than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)\n    bool operator>=(const_reference rhs) const noexcept\n    {\n        if (compares_unordered(rhs, true))\n        {\n            return false;\n        }\n        return !(*this < rhs);\n    }\n\n    /// @brief comparison: greater than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/\n    template<typename ScalarType>\n    requires std::is_scalar_v<ScalarType>\n    bool operator>=(ScalarType rhs) const noexcept\n    {\n        return *this >= basic_json(rhs);\n    }\n#endif\n#else\n    /// @brief comparison: equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/\n    friend bool operator==(const_reference lhs, const_reference rhs) noexcept\n    {\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n    }\n\n    /// @brief comparison: equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs == basic_json(rhs);\n    }\n\n    /// @brief comparison: equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) == rhs;\n    }\n\n    /// @brief comparison: not equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/\n    friend bool operator!=(const_reference lhs, const_reference rhs) noexcept\n    {\n        if (compares_unordered(lhs, rhs, true))\n        {\n            return false;\n        }\n        return !(lhs == rhs);\n    }\n\n    /// @brief comparison: not equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs != basic_json(rhs);\n    }\n\n    /// @brief comparison: not equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) != rhs;\n    }\n\n    /// @brief comparison: less than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/\n    friend bool operator<(const_reference lhs, const_reference rhs) noexcept\n    {\n        // default_result is used if we cannot compare values. In that case,\n        // we compare types. Note we have to call the operator explicitly,\n        // because MSVC has problems otherwise.\n        JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))\n    }\n\n    /// @brief comparison: less than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs < basic_json(rhs);\n    }\n\n    /// @brief comparison: less than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) < rhs;\n    }\n\n    /// @brief comparison: less than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/\n    friend bool operator<=(const_reference lhs, const_reference rhs) noexcept\n    {\n        if (compares_unordered(lhs, rhs, true))\n        {\n            return false;\n        }\n        return !(rhs < lhs);\n    }\n\n    /// @brief comparison: less than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs <= basic_json(rhs);\n    }\n\n    /// @brief comparison: less than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) <= rhs;\n    }\n\n    /// @brief comparison: greater than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/\n    friend bool operator>(const_reference lhs, const_reference rhs) noexcept\n    {\n        // double inverse\n        if (compares_unordered(lhs, rhs))\n        {\n            return false;\n        }\n        return !(lhs <= rhs);\n    }\n\n    /// @brief comparison: greater than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs > basic_json(rhs);\n    }\n\n    /// @brief comparison: greater than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) > rhs;\n    }\n\n    /// @brief comparison: greater than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/\n    friend bool operator>=(const_reference lhs, const_reference rhs) noexcept\n    {\n        if (compares_unordered(lhs, rhs, true))\n        {\n            return false;\n        }\n        return !(lhs < rhs);\n    }\n\n    /// @brief comparison: greater than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs >= basic_json(rhs);\n    }\n\n    /// @brief comparison: greater than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) >= rhs;\n    }\n#endif\n\n#undef JSON_IMPLEMENT_OPERATOR\n\n    /// @}\n\n    ///////////////////\n    // serialization //\n    ///////////////////\n\n    /// @name serialization\n    /// @{\n#ifndef JSON_NO_IO\n    /// @brief serialize to stream\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/\n    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)\n    {\n        // read width member and use it as the indentation parameter if nonzero\n        const bool pretty_print = o.width() > 0;\n        const auto indentation = pretty_print ? o.width() : 0;\n\n        // reset width to 0 for subsequent calls to this stream\n        o.width(0);\n\n        // do the actual serialization\n        serializer s(detail::output_adapter<char>(o), o.fill());\n        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));\n        return o;\n    }\n\n    /// @brief serialize to stream\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/\n    /// @deprecated This function is deprecated since 3.0.0 and will be removed in\n    ///             version 4.0.0 of the library. Please use\n    ///             operator<<(std::ostream&, const basic_json&) instead; that is,\n    ///             replace calls like `j >> o;` with `o << j;`.\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&))\n    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)\n    {\n        return o << j;\n    }\n#endif  // JSON_NO_IO\n    /// @}\n\n    /////////////////////\n    // deserialization //\n    /////////////////////\n\n    /// @name deserialization\n    /// @{\n\n    /// @brief deserialize from a compatible input\n    /// @sa https://json.nlohmann.me/api/basic_json/parse/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(InputType&& i,\n                            parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false,\n                            const bool ignore_trailing_commas = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved,accessForwarded]\n        return result;\n    }\n\n    /// @brief deserialize from a pair of character iterators\n    /// @sa https://json.nlohmann.me/api/basic_json/parse/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(IteratorType first,\n                            IteratorType last,\n                            parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false,\n                            const bool ignore_trailing_commas = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]\n        return result;\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))\n    static basic_json parse(detail::span_input_adapter&& i,\n                            parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false,\n                            const bool ignore_trailing_commas = false)\n    {\n        basic_json result;\n        parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas).parse(true, result); // cppcheck-suppress[accessMoved]\n        return result;\n    }\n\n    /// @brief check if the input is valid JSON\n    /// @sa https://json.nlohmann.me/api/basic_json/accept/\n    template<typename InputType>\n    static bool accept(InputType&& i,\n                       const bool ignore_comments = false,\n                       const bool ignore_trailing_commas = false)\n    {\n        return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);\n    }\n\n    /// @brief check if the input is valid JSON\n    /// @sa https://json.nlohmann.me/api/basic_json/accept/\n    template<typename IteratorType>\n    static bool accept(IteratorType first, IteratorType last,\n                       const bool ignore_comments = false,\n                       const bool ignore_trailing_commas = false)\n    {\n        return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))\n    static bool accept(detail::span_input_adapter&& i,\n                       const bool ignore_comments = false,\n                       const bool ignore_trailing_commas = false)\n    {\n        return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);\n    }\n\n    /// @brief generate SAX events\n    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/\n    template <typename InputType, typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(InputType&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false,\n                          const bool ignore_trailing_commas = false)\n    {\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);\n    }\n\n    /// @brief generate SAX events\n    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/\n    template<class IteratorType, class SAX>\n    JSON_HEDLEY_NON_NULL(3)\n    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false,\n                          const bool ignore_trailing_commas = false)\n    {\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);\n    }\n\n    /// @brief generate SAX events\n    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/\n    /// @deprecated This function is deprecated since 3.8.0 and will be removed in\n    ///             version 4.0.0 of the library. Please use\n    ///             sax_parse(ptr, ptr + len) instead.\n    template <typename SAX>\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false,\n                          const bool ignore_trailing_commas = false)\n    {\n        auto ia = i.get();\n        return format == input_format_t::json\n               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n               ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)\n               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);\n    }\n#ifndef JSON_NO_IO\n    /// @brief deserialize from stream\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/\n    /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in\n    ///             version 4.0.0 of the library. Please use\n    ///             operator>>(std::istream&, basic_json&) instead; that is,\n    ///             replace calls like `j << i;` with `i >> j;`.\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&))\n    friend std::istream& operator<<(basic_json& j, std::istream& i)\n    {\n        return operator>>(i, j);\n    }\n\n    /// @brief deserialize from stream\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/\n    friend std::istream& operator>>(std::istream& i, basic_json& j)\n    {\n        parser(detail::input_adapter(i)).parse(false, j);\n        return i;\n    }\n#endif  // JSON_NO_IO\n    /// @}\n\n    ///////////////////////////\n    // convenience functions //\n    ///////////////////////////\n\n    /// @brief return the type as string\n    /// @sa https://json.nlohmann.me/api/basic_json/type_name/\n    JSON_HEDLEY_RETURNS_NON_NULL\n    const char* type_name() const noexcept\n    {\n        switch (m_data.m_type)\n        {\n            case value_t::null:\n                return \"null\";\n            case value_t::object:\n                return \"object\";\n            case value_t::array:\n                return \"array\";\n            case value_t::string:\n                return \"string\";\n            case value_t::boolean:\n                return \"boolean\";\n            case value_t::binary:\n                return \"binary\";\n            case value_t::discarded:\n                return \"discarded\";\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n                return \"number\";\n            default:\n                return \"invalid\";\n        }\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    //////////////////////\n    // member variables //\n    //////////////////////\n\n    struct data\n    {\n        /// the type of the current element\n        value_t m_type = value_t::null;\n\n        /// the value of the current element\n        json_value m_value = {};\n\n        data(const value_t v)\n            : m_type(v), m_value(v)\n        {\n        }\n\n        data(size_type cnt, const basic_json& val)\n            : m_type(value_t::array)\n        {\n            m_value.array = create<array_t>(cnt, val);\n        }\n\n        data() noexcept = default;\n        data(data&&) noexcept = default;\n        data(const data&) noexcept = delete;\n        data& operator=(data&&) noexcept = delete;\n        data& operator=(const data&) noexcept = delete;\n\n        ~data() noexcept\n        {\n            m_value.destroy(m_type);\n        }\n    };\n\n    data m_data = {};\n\n#if JSON_DIAGNOSTICS\n    /// a pointer to a parent value (for debugging purposes)\n    basic_json* m_parent = nullptr;\n#endif\n\n#if JSON_DIAGNOSTIC_POSITIONS\n    /// the start position of the value\n    std::size_t start_position = std::string::npos;\n    /// the end position of the value\n    std::size_t end_position = std::string::npos;\n  public:\n    constexpr std::size_t start_pos() const noexcept\n    {\n        return start_position;\n    }\n\n    constexpr std::size_t end_pos() const noexcept\n    {\n        return end_position;\n    }\n#endif\n\n    //////////////////////////////////////////\n    // binary serialization/deserialization //\n    //////////////////////////////////////////\n\n    /// @name binary serialization/deserialization support\n    /// @{\n\n  public:\n    /// @brief create a CBOR serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/\n    static std::vector<std::uint8_t> to_cbor(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_cbor(j, result);\n        return result;\n    }\n\n    /// @brief create a CBOR serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/\n    static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_cbor(j);\n    }\n\n    /// @brief create a CBOR serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/\n    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_cbor(j);\n    }\n\n    /// @brief create a MessagePack serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/\n    static std::vector<std::uint8_t> to_msgpack(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_msgpack(j, result);\n        return result;\n    }\n\n    /// @brief create a MessagePack serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/\n    static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_msgpack(j);\n    }\n\n    /// @brief create a MessagePack serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/\n    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_msgpack(j);\n    }\n\n    /// @brief create a UBJSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/\n    static std::vector<std::uint8_t> to_ubjson(const basic_json& j,\n            const bool use_size = false,\n            const bool use_type = false)\n    {\n        std::vector<std::uint8_t> result;\n        to_ubjson(j, result, use_size, use_type);\n        return result;\n    }\n\n    /// @brief create a UBJSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/\n    static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type);\n    }\n\n    /// @brief create a UBJSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/\n    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<char>(o).write_ubjson(j, use_size, use_type);\n    }\n\n    /// @brief create a BJData serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/\n    static std::vector<std::uint8_t> to_bjdata(const basic_json& j,\n            const bool use_size = false,\n            const bool use_type = false,\n            const bjdata_version_t version = bjdata_version_t::draft2)\n    {\n        std::vector<std::uint8_t> result;\n        to_bjdata(j, result, use_size, use_type, version);\n        return result;\n    }\n\n    /// @brief create a BJData serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/\n    static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,\n                          const bool use_size = false, const bool use_type = false,\n                          const bjdata_version_t version = bjdata_version_t::draft2)\n    {\n        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true, version);\n    }\n\n    /// @brief create a BJData serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/\n    static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,\n                          const bool use_size = false, const bool use_type = false,\n                          const bjdata_version_t version = bjdata_version_t::draft2)\n    {\n        binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true, version);\n    }\n\n    /// @brief create a BSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/\n    static std::vector<std::uint8_t> to_bson(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_bson(j, result);\n        return result;\n    }\n\n    /// @brief create a BSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/\n    static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_bson(j);\n    }\n\n    /// @brief create a BSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/\n    static void to_bson(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_bson(j);\n    }\n\n    /// @brief create a JSON value from an input in CBOR format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in CBOR format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        auto ia = i.get();\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in MessagePack format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(InputType&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in MessagePack format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(IteratorType first, IteratorType last,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(const T* ptr, std::size_t len,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        return from_msgpack(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(detail::span_input_adapter&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        auto ia = i.get();\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in UBJSON format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(InputType&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in UBJSON format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(IteratorType first, IteratorType last,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(const T* ptr, std::size_t len,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        return from_ubjson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(detail::span_input_adapter&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        auto ia = i.get();\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in BJData format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bjdata(InputType&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in BJData format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bjdata(IteratorType first, IteratorType last,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in BSON format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_bson/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in BSON format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_bson/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        return from_bson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        auto ia = i.get();\n        detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]\n        return res ? result : basic_json(value_t::discarded);\n    }\n    /// @}\n\n    //////////////////////////\n    // JSON Pointer support //\n    //////////////////////////\n\n    /// @name JSON Pointer functions\n    /// @{\n\n    /// @brief access specified element via JSON Pointer\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    reference operator[](const json_pointer& ptr)\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /// @brief access specified element via JSON Pointer\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    const_reference operator[](const json_pointer& ptr) const\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /// @brief access specified element via JSON Pointer\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    reference at(const json_pointer& ptr)\n    {\n        return ptr.get_checked(this);\n    }\n\n    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)\n    {\n        return ptr.get_checked(this);\n    }\n\n    /// @brief access specified element via JSON Pointer\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    const_reference at(const json_pointer& ptr) const\n    {\n        return ptr.get_checked(this);\n    }\n\n    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const\n    {\n        return ptr.get_checked(this);\n    }\n\n    /// @brief return flattened JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/flatten/\n    basic_json flatten() const\n    {\n        basic_json result(value_t::object);\n        json_pointer::flatten(\"\", *this, result);\n        return result;\n    }\n\n    /// @brief unflatten a previously flattened JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/unflatten/\n    basic_json unflatten() const\n    {\n        return json_pointer::unflatten(*this);\n    }\n\n    /// @}\n\n    //////////////////////////\n    // JSON Patch functions //\n    //////////////////////////\n\n    /// @name JSON Patch functions\n    /// @{\n\n    /// @brief applies a JSON patch in-place without copying the object\n    /// @sa https://json.nlohmann.me/api/basic_json/patch/\n    void patch_inplace(const basic_json& json_patch)\n    {\n        basic_json& result = *this;\n        // the valid JSON Patch operations\n        enum class patch_operations {add, remove, replace, move, copy, test, invalid};\n\n        const auto get_op = [](const string_t& op)\n        {\n            if (op == \"add\")\n            {\n                return patch_operations::add;\n            }\n            if (op == \"remove\")\n            {\n                return patch_operations::remove;\n            }\n            if (op == \"replace\")\n            {\n                return patch_operations::replace;\n            }\n            if (op == \"move\")\n            {\n                return patch_operations::move;\n            }\n            if (op == \"copy\")\n            {\n                return patch_operations::copy;\n            }\n            if (op == \"test\")\n            {\n                return patch_operations::test;\n            }\n\n            return patch_operations::invalid;\n        };\n\n        // wrapper for \"add\" operation; add value at ptr\n        const auto operation_add = [&result](json_pointer & ptr, const basic_json & val)\n        {\n            // adding to the root of the target document means replacing it\n            if (ptr.empty())\n            {\n                result = val;\n                return;\n            }\n\n            // make sure the top element of the pointer exists\n            json_pointer const top_pointer = ptr.top();\n            if (top_pointer != ptr)\n            {\n                result.at(top_pointer);\n            }\n\n            // get reference to the parent of the JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            // parent must exist when performing patch add per RFC6902 specs\n            basic_json& parent = result.at(ptr);\n\n            switch (parent.m_data.m_type)\n            {\n                case value_t::null:\n                case value_t::object:\n                {\n                    // use operator[] to add value\n                    parent[last_path] = val;\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    if (last_path == \"-\")\n                    {\n                        // special case: append to back\n                        parent.push_back(val);\n                    }\n                    else\n                    {\n                        const auto idx = json_pointer::template array_index<basic_json_t>(last_path);\n                        if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))\n                        {\n                            // avoid undefined behavior\n                            JSON_THROW(out_of_range::create(401, detail::concat(\"array index \", std::to_string(idx), \" is out of range\"), &parent));\n                        }\n\n                        // default case: insert add offset\n                        parent.insert(parent.begin() + static_cast<difference_type>(idx), val);\n                    }\n                    break;\n                }\n\n                // if there exists a parent, it cannot be primitive\n                case value_t::string: // LCOV_EXCL_LINE\n                case value_t::boolean: // LCOV_EXCL_LINE\n                case value_t::number_integer: // LCOV_EXCL_LINE\n                case value_t::number_unsigned: // LCOV_EXCL_LINE\n                case value_t::number_float: // LCOV_EXCL_LINE\n                case value_t::binary: // LCOV_EXCL_LINE\n                case value_t::discarded: // LCOV_EXCL_LINE\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            }\n        };\n\n        // wrapper for \"remove\" operation; remove value at ptr\n        const auto operation_remove = [this, & result](json_pointer & ptr)\n        {\n            // get reference to the parent of the JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result.at(ptr);\n\n            // remove child\n            if (parent.is_object())\n            {\n                // perform range check\n                auto it = parent.find(last_path);\n                if (JSON_HEDLEY_LIKELY(it != parent.end()))\n                {\n                    parent.erase(it);\n                }\n                else\n                {\n                    JSON_THROW(out_of_range::create(403, detail::concat(\"key '\", last_path, \"' not found\"), this));\n                }\n            }\n            else if (parent.is_array())\n            {\n                // note erase performs range check\n                parent.erase(json_pointer::template array_index<basic_json_t>(last_path));\n            }\n        };\n\n        // type check: top level value must be an array\n        if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))\n        {\n            JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\", &json_patch));\n        }\n\n        // iterate and apply the operations\n        for (const auto& val : json_patch)\n        {\n            // wrapper to get a value for an operation\n            const auto get_value = [&val](const string_t& op,\n                                          const string_t& member,\n                                          bool string_type) -> basic_json &\n            {\n                // find value\n                auto it = val.m_data.m_value.object->find(member);\n\n                // context-sensitive error message\n                const auto error_msg = (op == \"op\") ? \"operation\" : detail::concat(\"operation '\", op, '\\''); // NOLINT(bugprone-unused-local-non-trivial-variable)\n\n                // check if the desired value is present\n                if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end()))\n                {\n                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n                    JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, \" must have member '\", member, \"'\"), &val));\n                }\n\n                // check if the result is of type string\n                if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))\n                {\n                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n                    JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, \" must have string member '\", member, \"'\"), &val));\n                }\n\n                // no error: return value\n                return it->second;\n            };\n\n            // type check: every element of the array must be an object\n            if (JSON_HEDLEY_UNLIKELY(!val.is_object()))\n            {\n                JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\", &val));\n            }\n\n            // collect mandatory members\n            const auto op = get_value(\"op\", \"op\", true).template get<string_t>();\n            const auto path = get_value(op, \"path\", true).template get<string_t>();\n            json_pointer ptr(path);\n\n            switch (get_op(op))\n            {\n                case patch_operations::add:\n                {\n                    operation_add(ptr, get_value(\"add\", \"value\", false));\n                    break;\n                }\n\n                case patch_operations::remove:\n                {\n                    operation_remove(ptr);\n                    break;\n                }\n\n                case patch_operations::replace:\n                {\n                    // the \"path\" location must exist - use at()\n                    result.at(ptr) = get_value(\"replace\", \"value\", false);\n                    break;\n                }\n\n                case patch_operations::move:\n                {\n                    const auto from_path = get_value(\"move\", \"from\", true).template get<string_t>();\n                    json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json const v = result.at(from_ptr);\n\n                    // The move operation is functionally identical to a\n                    // \"remove\" operation on the \"from\" location, followed\n                    // immediately by an \"add\" operation at the target\n                    // location with the value that was just removed.\n                    operation_remove(from_ptr);\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::copy:\n                {\n                    const auto from_path = get_value(\"copy\", \"from\", true).template get<string_t>();\n                    const json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json const v = result.at(from_ptr);\n\n                    // The copy is functionally identical to an \"add\"\n                    // operation at the target location using the value\n                    // specified in the \"from\" member.\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::test:\n                {\n                    bool success = false;\n                    JSON_TRY\n                    {\n                        // check if \"value\" matches the one at \"path\"\n                        // the \"path\" location must exist - use at()\n                        success = (result.at(ptr) == get_value(\"test\", \"value\", false));\n                    }\n                    JSON_INTERNAL_CATCH (out_of_range&)\n                    {\n                        // ignore out of range errors: success remains false\n                    }\n\n                    // throw an exception if the test fails\n                    if (JSON_HEDLEY_UNLIKELY(!success))\n                    {\n                        JSON_THROW(other_error::create(501, detail::concat(\"unsuccessful: \", val.dump()), &val));\n                    }\n\n                    break;\n                }\n\n                case patch_operations::invalid:\n                default:\n                {\n                    // op must be \"add\", \"remove\", \"replace\", \"move\", \"copy\", or\n                    // \"test\"\n                    JSON_THROW(parse_error::create(105, 0, detail::concat(\"operation value '\", op, \"' is invalid\"), &val));\n                }\n            }\n        }\n    }\n\n    /// @brief applies a JSON patch to a copy of the current object\n    /// @sa https://json.nlohmann.me/api/basic_json/patch/\n    basic_json patch(const basic_json& json_patch) const\n    {\n        basic_json result = *this;\n        result.patch_inplace(json_patch);\n        return result;\n    }\n\n    /// @brief creates a diff as a JSON patch\n    /// @sa https://json.nlohmann.me/api/basic_json/diff/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json diff(const basic_json& source, const basic_json& target,\n                           const string_t& path = \"\")\n    {\n        // the patch\n        basic_json result(value_t::array);\n\n        // if the values are the same, return an empty patch\n        if (source == target)\n        {\n            return result;\n        }\n\n        if (source.type() != target.type())\n        {\n            // different types: replace value\n            result.push_back(\n            {\n                {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n            });\n            return result;\n        }\n\n        switch (source.type())\n        {\n            case value_t::array:\n            {\n                // first pass: traverse common elements\n                std::size_t i = 0;\n                while (i < source.size() && i < target.size())\n                {\n                    // recursive call to compare array values at index i\n                    auto temp_diff = diff(source[i], target[i], detail::concat<string_t>(path, '/', detail::to_string<string_t>(i)));\n                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    ++i;\n                }\n\n                // We now reached the end of at least one array\n                // in a second pass, traverse the remaining elements\n\n                // remove my remaining elements\n                const auto end_index = static_cast<difference_type>(result.size());\n                while (i < source.size())\n                {\n                    // add operations in reverse order to avoid invalid\n                    // indices\n                    result.insert(result.begin() + end_index, object(\n                    {\n                        {\"op\", \"remove\"},\n                        {\"path\", detail::concat<string_t>(path, '/', detail::to_string<string_t>(i))}\n                    }));\n                    ++i;\n                }\n\n                // add other remaining elements\n                while (i < target.size())\n                {\n                    result.push_back(\n                    {\n                        {\"op\", \"add\"},\n                        {\"path\", detail::concat<string_t>(path, \"/-\")},\n                        {\"value\", target[i]}\n                    });\n                    ++i;\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // first pass: traverse this object's elements\n                for (auto it = source.cbegin(); it != source.cend(); ++it)\n                {\n                    // escape the key name to be used in a JSON patch\n                    const auto path_key = detail::concat<string_t>(path, '/', detail::escape(it.key()));\n\n                    if (target.find(it.key()) != target.end())\n                    {\n                        // recursive call to compare object values at key it\n                        auto temp_diff = diff(it.value(), target[it.key()], path_key);\n                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    }\n                    else\n                    {\n                        // found a key that is not in o -> remove it\n                        result.push_back(object(\n                        {\n                            {\"op\", \"remove\"}, {\"path\", path_key}\n                        }));\n                    }\n                }\n\n                // second pass: traverse other object's elements\n                for (auto it = target.cbegin(); it != target.cend(); ++it)\n                {\n                    if (source.find(it.key()) == source.end())\n                    {\n                        // found a key that is not in this -> add it\n                        const auto path_key = detail::concat<string_t>(path, '/', detail::escape(it.key()));\n                        result.push_back(\n                        {\n                            {\"op\", \"add\"}, {\"path\", path_key},\n                            {\"value\", it.value()}\n                        });\n                    }\n                }\n\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // both primitive types: replace value\n                result.push_back(\n                {\n                    {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n                });\n                break;\n            }\n        }\n\n        return result;\n    }\n    /// @}\n\n    ////////////////////////////////\n    // JSON Merge Patch functions //\n    ////////////////////////////////\n\n    /// @name JSON Merge Patch functions\n    /// @{\n\n    /// @brief applies a JSON Merge Patch\n    /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/\n    void merge_patch(const basic_json& apply_patch)\n    {\n        if (apply_patch.is_object())\n        {\n            if (!is_object())\n            {\n                *this = object();\n            }\n            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)\n            {\n                if (it.value().is_null())\n                {\n                    erase(it.key());\n                }\n                else\n                {\n                    operator[](it.key()).merge_patch(it.value());\n                }\n            }\n        }\n        else\n        {\n            *this = apply_patch;\n        }\n    }\n\n    /// @}\n};\n\n/// @brief user-defined to_string function for JSON values\n/// @sa https://json.nlohmann.me/api/basic_json/to_string/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstd::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)\n{\n    return j.dump();\n}\n\ninline namespace literals\n{\ninline namespace json_literals\n{\n\n/// @brief user-defined string literal for JSON values\n/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/\nJSON_HEDLEY_NON_NULL(1)\n#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)\n    inline nlohmann::json operator \"\"_json(const char* s, std::size_t n)\n#else\n    inline nlohmann::json operator \"\" _json(const char* s, std::size_t n)\n#endif\n{\n    return nlohmann::json::parse(s, s + n);\n}\n\n/// @brief user-defined string literal for JSON pointer\n/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/\nJSON_HEDLEY_NON_NULL(1)\n#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)\n    inline nlohmann::json::json_pointer operator \"\"_json_pointer(const char* s, std::size_t n)\n#else\n    inline nlohmann::json::json_pointer operator \"\" _json_pointer(const char* s, std::size_t n)\n#endif\n{\n    return nlohmann::json::json_pointer(std::string(s, n));\n}\n\n}  // namespace json_literals\n}  // namespace literals\nNLOHMANN_JSON_NAMESPACE_END\n\n///////////////////////\n// nonmember support //\n///////////////////////\n\nnamespace std // NOLINT(cert-dcl58-cpp)\n{\n\n/// @brief hash value for JSON objects\n/// @sa https://json.nlohmann.me/api/basic_json/std_hash/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstruct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL> // NOLINT(cert-dcl58-cpp)\n{\n    std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const\n    {\n        return nlohmann::detail::hash(j);\n    }\n};\n\n// specialization for std::less<value_t>\ntemplate<>\nstruct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679\n{\n    /*!\n    @brief compare two value_t enum values\n    @since version 3.0.0\n    */\n    bool operator()(::nlohmann::detail::value_t lhs,\n                    ::nlohmann::detail::value_t rhs) const noexcept\n    {\n#if JSON_HAS_THREE_WAY_COMPARISON\n        return std::is_lt(lhs <=> rhs); // *NOPAD*\n#else\n        return ::nlohmann::detail::operator<(lhs, rhs);\n#endif\n    }\n};\n\n// C++20 prohibit function specialization in the std namespace.\n#ifndef JSON_HAS_CPP_20\n\n/// @brief exchanges the values of two JSON objects\n/// @sa https://json.nlohmann.me/api/basic_json/std_swap/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\ninline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept(  // NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp)\n    is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&&                          // NOLINT(misc-redundant-expression,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)\n    is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value)\n{\n    j1.swap(j2);\n}\n\n#endif\n\n}  // namespace std\n\n#if JSON_USE_GLOBAL_UDLS\n    #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)\n        using nlohmann::literals::json_literals::operator \"\"_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)\n        using nlohmann::literals::json_literals::operator \"\"_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)\n    #else\n        using nlohmann::literals::json_literals::operator \"\" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)\n        using nlohmann::literals::json_literals::operator \"\" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)\n    #endif\n#endif\n\n#include <nlohmann/detail/macro_unscope.hpp>\n\n#endif  // INCLUDE_NLOHMANN_JSON_HPP_\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/json_fwd.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_\n#define INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n#include <cstdint> // int64_t, uint64_t\n#include <map> // map\n#include <memory> // allocator\n#include <string> // string\n#include <vector> // vector\n\n#include <nlohmann/detail/abi_macros.hpp>\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/*!\n@brief default JSONSerializer template argument\n\nThis serializer ignores the template arguments and uses ADL\n([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))\nfor serialization.\n*/\ntemplate<typename T = void, typename SFINAE = void>\nstruct adl_serializer;\n\n/// a class to store JSON values\n/// @sa https://json.nlohmann.me/api/basic_json/\ntemplate<template<typename U, typename V, typename... Args> class ObjectType =\n         std::map,\n         template<typename U, typename... Args> class ArrayType = std::vector,\n         class StringType = std::string, class BooleanType = bool,\n         class NumberIntegerType = std::int64_t,\n         class NumberUnsignedType = std::uint64_t,\n         class NumberFloatType = double,\n         template<typename U> class AllocatorType = std::allocator,\n         template<typename T, typename SFINAE = void> class JSONSerializer =\n         adl_serializer,\n         class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError\n         class CustomBaseClass = void>\nclass basic_json;\n\n/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document\n/// @sa https://json.nlohmann.me/api/json_pointer/\ntemplate<typename RefStringType>\nclass json_pointer;\n\n/*!\n@brief default specialization\n@sa https://json.nlohmann.me/api/json/\n*/\nusing json = basic_json<>;\n\n/// @brief a minimal map-like container that preserves insertion order\n/// @sa https://json.nlohmann.me/api/ordered_map/\ntemplate<class Key, class T, class IgnoredLess, class Allocator>\nstruct ordered_map;\n\n/// @brief specialization that maintains the insertion order of object keys\n/// @sa https://json.nlohmann.me/api/ordered_json/\nusing ordered_json = basic_json<nlohmann::ordered_map>;\n\nNLOHMANN_JSON_NAMESPACE_END\n\n#endif  // INCLUDE_NLOHMANN_JSON_FWD_HPP_\n"
  },
  {
    "path": "lib/third_party/nlohmann_json/include/nlohmann/thirdparty/hedley/hedley.hpp",
    "content": "#pragma once\n\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.12.0\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>\n// SPDX-FileCopyrightText: 2016 - 2021 Evan Nemerson <evan@nemerson.com>\n// SPDX-License-Identifier: MIT\n\n/* Hedley - https://nemequ.github.io/hedley\n * Created by Evan Nemerson <evan@nemerson.com>\n */\n\n#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15)\n#if defined(JSON_HEDLEY_VERSION)\n    #undef JSON_HEDLEY_VERSION\n#endif\n#define JSON_HEDLEY_VERSION 15\n\n#if defined(JSON_HEDLEY_STRINGIFY_EX)\n    #undef JSON_HEDLEY_STRINGIFY_EX\n#endif\n#define JSON_HEDLEY_STRINGIFY_EX(x) #x\n\n#if defined(JSON_HEDLEY_STRINGIFY)\n    #undef JSON_HEDLEY_STRINGIFY\n#endif\n#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)\n\n#if defined(JSON_HEDLEY_CONCAT_EX)\n    #undef JSON_HEDLEY_CONCAT_EX\n#endif\n#define JSON_HEDLEY_CONCAT_EX(a,b) a##b\n\n#if defined(JSON_HEDLEY_CONCAT)\n    #undef JSON_HEDLEY_CONCAT\n#endif\n#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)\n\n#if defined(JSON_HEDLEY_CONCAT3_EX)\n    #undef JSON_HEDLEY_CONCAT3_EX\n#endif\n#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c\n\n#if defined(JSON_HEDLEY_CONCAT3)\n    #undef JSON_HEDLEY_CONCAT3\n#endif\n#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c)\n\n#if defined(JSON_HEDLEY_VERSION_ENCODE)\n    #undef JSON_HEDLEY_VERSION_ENCODE\n#endif\n#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)\n    #undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)\n\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #undef JSON_HEDLEY_GNUC_VERSION\n#endif\n#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)\n#elif defined(__GNUC__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION)\n    #undef JSON_HEDLEY_MSVC_VERSION\n#endif\n#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)\n#elif defined(_MSC_FULL_VER) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)\n#elif defined(_MSC_VER) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)\n    #undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#endif\n#if !defined(JSON_HEDLEY_MSVC_VERSION)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)\n#elif defined(_MSC_VER) && (_MSC_VER >= 1400)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))\n#elif defined(_MSC_VER) && (_MSC_VER >= 1200)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))\n#else\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #undef JSON_HEDLEY_INTEL_VERSION\n#endif\n#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)\n#elif defined(__INTEL_COMPILER) && !defined(__ICL)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)\n    #undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION)\n    #undef JSON_HEDLEY_INTEL_CL_VERSION\n#endif\n#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL)\n    #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK)\n    #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION)\n    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #undef JSON_HEDLEY_PGI_VERSION\n#endif\n#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)\n    #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)\n    #undef JSON_HEDLEY_PGI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #undef JSON_HEDLEY_SUNPRO_VERSION\n#endif\n#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)\n#elif defined(__SUNPRO_C)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)\n#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)\n#elif defined(__SUNPRO_CC)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)\n    #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#endif\n#if defined(__EMSCRIPTEN__)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #undef JSON_HEDLEY_ARM_VERSION\n#endif\n#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)\n#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)\n    #undef JSON_HEDLEY_ARM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #undef JSON_HEDLEY_IBM_VERSION\n#endif\n#if defined(__ibmxl__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)\n#elif defined(__xlC__) && defined(__xlC_ver__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)\n#elif defined(__xlC__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)\n    #undef JSON_HEDLEY_IBM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #undef JSON_HEDLEY_TI_VERSION\n#endif\n#if \\\n    defined(__TI_COMPILER_VERSION__) && \\\n    ( \\\n      defined(__TMS470__) || defined(__TI_ARM__) || \\\n      defined(__MSP430__) || \\\n      defined(__TMS320C2000__) \\\n    )\n#if (__TI_COMPILER_VERSION__ >= 16000000)\n    #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__)\n    #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #undef JSON_HEDLEY_TI_CL430_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__)\n    #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__))\n    #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__)\n    #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__)\n    #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #undef JSON_HEDLEY_CRAY_VERSION\n#endif\n#if defined(_CRAYC)\n    #if defined(_RELEASE_PATCHLEVEL)\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)\n    #else\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)\n    #undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #undef JSON_HEDLEY_IAR_VERSION\n#endif\n#if defined(__IAR_SYSTEMS_ICC__)\n    #if __VER__ > 1000\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))\n    #else\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)\n    #undef JSON_HEDLEY_IAR_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #undef JSON_HEDLEY_TINYC_VERSION\n#endif\n#if defined(__TINYC__)\n    #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)\n    #undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #undef JSON_HEDLEY_DMC_VERSION\n#endif\n#if defined(__DMC__)\n    #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)\n    #undef JSON_HEDLEY_DMC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #undef JSON_HEDLEY_COMPCERT_VERSION\n#endif\n#if defined(__COMPCERT_VERSION__)\n    #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)\n    #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #undef JSON_HEDLEY_PELLES_VERSION\n#endif\n#if defined(__POCC__)\n    #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)\n    #undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #undef JSON_HEDLEY_MCST_LCC_VERSION\n#endif\n#if defined(__LCC__) && defined(__LCC_MINOR__)\n    #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__)\n#endif\n\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK)\n    #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #undef JSON_HEDLEY_GCC_VERSION\n#endif\n#if \\\n    defined(JSON_HEDLEY_GNUC_VERSION) && \\\n    !defined(__clang__) && \\\n    !defined(JSON_HEDLEY_INTEL_VERSION) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_ARM_VERSION) && \\\n    !defined(JSON_HEDLEY_CRAY_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL430_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \\\n    !defined(__COMPCERT__) && \\\n    !defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_ATTRIBUTE\n#endif\n#if \\\n  defined(__has_attribute) && \\\n  ( \\\n    (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \\\n  )\n#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)\n#else\n#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#endif\n#if \\\n    defined(__has_cpp_attribute) && \\\n    defined(__cplusplus) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#endif\n#if !defined(__cplusplus) || !defined(__has_cpp_attribute)\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#elif \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_IAR_VERSION) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \\\n    (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_BUILTIN)\n    #undef JSON_HEDLEY_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_FEATURE)\n    #undef JSON_HEDLEY_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_HAS_FEATURE(feature) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GCC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_EXTENSION)\n    #undef JSON_HEDLEY_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_WARNING)\n    #undef JSON_HEDLEY_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_HAS_WARNING(warning) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)\n    #undef JSON_HEDLEY_GNUC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_WARNING)\n    #undef JSON_HEDLEY_GCC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    defined(__clang__) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))\n    #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_PRAGMA(value) __pragma(value)\n#else\n    #define JSON_HEDLEY_PRAGMA(value)\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)\n    #undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#endif\n#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)\n    #undef JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"clang diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"clang diagnostic pop\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"GCC diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"GCC diagnostic pop\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))\n    #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))\n#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"pop\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"diag_push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"diag_pop\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH\n    #define JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n\n/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat\")\n#    if JSON_HEDLEY_HAS_WARNING(\"-Wc++17-extensions\")\n#      if JSON_HEDLEY_HAS_WARNING(\"-Wc++1z-extensions\")\n#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++17-extensions\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++1z-extensions\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#      else\n#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++17-extensions\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#      endif\n#    else\n#      define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    endif\n#  endif\n#endif\n#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x\n#endif\n\n#if defined(JSON_HEDLEY_CONST_CAST)\n    #undef JSON_HEDLEY_CONST_CAST\n#endif\n#if defined(__cplusplus)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))\n#elif \\\n  JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\") || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_REINTERPRET_CAST)\n    #undef JSON_HEDLEY_REINTERPRET_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_CAST)\n    #undef JSON_HEDLEY_STATIC_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_CPP_CAST)\n    #undef JSON_HEDLEY_CPP_CAST\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wold-style-cast\")\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wold-style-cast\\\"\") \\\n    ((T) (expr)) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0)\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"diag_suppress=Pe137\") \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  else\n#    define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))\n#  endif\n#else\n#  define JSON_HEDLEY_CPP_CAST(T, expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wdeprecated-declarations\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"clang diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warning(disable:1478 1786)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1216,1444,1445\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1444\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1444\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1291,1718\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,symdeprecated,symdeprecated2)\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress=Pe1444,Pe1215\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warn(disable:2241)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"clang diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"warning(disable:161)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 1675\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"GCC diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress=Pe161\")\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 161\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-attributes\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"clang diagnostic ignored \\\"-Wunknown-attributes\\\"\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"warning(disable:1292)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292))\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097,1098\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"error_messages(off,attrskipunsup)\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1173\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress=Pe1097\")\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"clang diagnostic ignored \\\"-Wcast-qual\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"warning(disable:2203 2331)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"GCC diagnostic ignored \\\"-Wcast-qual\\\"\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunused-function\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"clang diagnostic ignored \\\"-Wunused-function\\\"\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"GCC diagnostic ignored \\\"-Wunused-function\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505))\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"diag_suppress 3142\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION\n#endif\n\n#if defined(JSON_HEDLEY_DEPRECATED)\n    #undef JSON_HEDLEY_DEPRECATED\n#endif\n#if defined(JSON_HEDLEY_DEPRECATED_FOR)\n    #undef JSON_HEDLEY_DEPRECATED_FOR\n#endif\n#if \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated(\"Since \" # since))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated(\"Since \" #since \"; use \" #replacement))\n#elif \\\n    (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__(\"Since \" #since)))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__(\"Since \" #since \"; use \" #replacement)))\n#elif defined(__cplusplus) && (__cplusplus >= 201402L)\n    #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since)]])\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since \"; use \" #replacement)]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DEPRECATED(since) _Pragma(\"deprecated\")\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma(\"deprecated\")\n#else\n    #define JSON_HEDLEY_DEPRECATED(since)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)\n#endif\n\n#if defined(JSON_HEDLEY_UNAVAILABLE)\n    #undef JSON_HEDLEY_UNAVAILABLE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__(\"Not available until \" #available_since)))\n#else\n    #define JSON_HEDLEY_UNAVAILABLE(available_since)\n#endif\n\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#endif\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))\n#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n#elif defined(_Check_return_) /* SAL */\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_\n#else\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg)\n#endif\n\n#if defined(JSON_HEDLEY_SENTINEL)\n    #undef JSON_HEDLEY_SENTINEL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))\n#else\n    #define JSON_HEDLEY_SENTINEL(position)\n#endif\n\n#if defined(JSON_HEDLEY_NO_RETURN)\n    #undef JSON_HEDLEY_NO_RETURN\n#endif\n#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NO_RETURN __noreturn\n#elif \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L\n    #define JSON_HEDLEY_NO_RETURN _Noreturn\n#elif defined(__cplusplus) && (__cplusplus >= 201103L)\n    #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"does_not_return\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"FUNC_NEVER_RETURNS;\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#else\n    #define JSON_HEDLEY_NO_RETURN\n#endif\n\n#if defined(JSON_HEDLEY_NO_ESCAPE)\n    #undef JSON_HEDLEY_NO_ESCAPE\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)\n    #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))\n#else\n    #define JSON_HEDLEY_NO_ESCAPE\n#endif\n\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #undef JSON_HEDLEY_UNREACHABLE\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)\n    #undef JSON_HEDLEY_UNREACHABLE_RETURN\n#endif\n#if defined(JSON_HEDLEY_ASSUME)\n    #undef JSON_HEDLEY_ASSUME\n#endif\n#if \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_ASSUME(expr) __assume(expr)\n#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)\n    #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)\n#elif \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n    #if defined(__cplusplus)\n        #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)\n    #endif\n#endif\n#if \\\n    (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()\n#elif defined(JSON_HEDLEY_ASSUME)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n#if !defined(JSON_HEDLEY_ASSUME)\n    #if defined(JSON_HEDLEY_UNREACHABLE)\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1)))\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr)\n    #endif\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #if  \\\n        JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value))\n    #else\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()\n    #endif\n#else\n    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value)\n#endif\n#if !defined(JSON_HEDLEY_UNREACHABLE)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n\nJSON_HEDLEY_DIAGNOSTIC_PUSH\n#if JSON_HEDLEY_HAS_WARNING(\"-Wpedantic\")\n    #pragma clang diagnostic ignored \"-Wpedantic\"\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat-pedantic\") && defined(__cplusplus)\n    #pragma clang diagnostic ignored \"-Wc++98-compat-pedantic\"\n#endif\n#if JSON_HEDLEY_GCC_HAS_WARNING(\"-Wvariadic-macros\",4,0,0)\n    #if defined(__clang__)\n        #pragma clang diagnostic ignored \"-Wvariadic-macros\"\n    #elif defined(JSON_HEDLEY_GCC_VERSION)\n        #pragma GCC diagnostic ignored \"-Wvariadic-macros\"\n    #endif\n#endif\n#if defined(JSON_HEDLEY_NON_NULL)\n    #undef JSON_HEDLEY_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))\n#else\n    #define JSON_HEDLEY_NON_NULL(...)\n#endif\nJSON_HEDLEY_DIAGNOSTIC_POP\n\n#if defined(JSON_HEDLEY_PRINTF_FORMAT)\n    #undef JSON_HEDLEY_PRINTF_FORMAT\n#endif\n#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))\n#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(format) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))\n#else\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)\n#endif\n\n#if defined(JSON_HEDLEY_CONSTEXPR)\n    #undef JSON_HEDLEY_CONSTEXPR\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)\n    #endif\n#endif\n#if !defined(JSON_HEDLEY_CONSTEXPR)\n    #define JSON_HEDLEY_CONSTEXPR\n#endif\n\n#if defined(JSON_HEDLEY_PREDICT)\n    #undef JSON_HEDLEY_PREDICT\n#endif\n#if defined(JSON_HEDLEY_LIKELY)\n    #undef JSON_HEDLEY_LIKELY\n#endif\n#if defined(JSON_HEDLEY_UNLIKELY)\n    #undef JSON_HEDLEY_UNLIKELY\n#endif\n#if defined(JSON_HEDLEY_UNPREDICTABLE)\n    #undef JSON_HEDLEY_UNPREDICTABLE\n#endif\n#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))\n#endif\n#if \\\n  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(  (expr), (value), (probability))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability)   __builtin_expect_with_probability(!!(expr),    1   , (probability))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability)  __builtin_expect_with_probability(!!(expr),    0   , (probability))\n#  define JSON_HEDLEY_LIKELY(expr)                      __builtin_expect                 (!!(expr),    1                  )\n#  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )\n#elif \\\n  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \\\n  JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) \\\n    (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_LIKELY(expr)   __builtin_expect(!!(expr), 1)\n#  define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)\n#else\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_LIKELY(expr) (!!(expr))\n#  define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))\n#endif\n#if !defined(JSON_HEDLEY_UNPREDICTABLE)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)\n#endif\n\n#if defined(JSON_HEDLEY_MALLOC)\n    #undef JSON_HEDLEY_MALLOC\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_MALLOC _Pragma(\"returns_new_memory\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_MALLOC __declspec(restrict)\n#else\n    #define JSON_HEDLEY_MALLOC\n#endif\n\n#if defined(JSON_HEDLEY_PURE)\n    #undef JSON_HEDLEY_PURE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PURE __attribute__((__pure__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n#  define JSON_HEDLEY_PURE _Pragma(\"does_not_write_global_data\")\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \\\n    )\n#  define JSON_HEDLEY_PURE _Pragma(\"FUNC_IS_PURE;\")\n#else\n#  define JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_CONST)\n    #undef JSON_HEDLEY_CONST\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(const) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_CONST __attribute__((__const__))\n#elif \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_CONST _Pragma(\"no_side_effect\")\n#else\n    #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_RESTRICT)\n    #undef JSON_HEDLEY_RESTRICT\n#endif\n#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT restrict\n#elif \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    defined(__clang__) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_RESTRICT __restrict\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT _Restrict\n#else\n    #define JSON_HEDLEY_RESTRICT\n#endif\n\n#if defined(JSON_HEDLEY_INLINE)\n    #undef JSON_HEDLEY_INLINE\n#endif\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    (defined(__cplusplus) && (__cplusplus >= 199711L))\n    #define JSON_HEDLEY_INLINE inline\n#elif \\\n    defined(JSON_HEDLEY_GCC_VERSION) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)\n    #define JSON_HEDLEY_INLINE __inline__\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_INLINE __inline\n#else\n    #define JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_ALWAYS_INLINE)\n    #undef JSON_HEDLEY_ALWAYS_INLINE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n  JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE\n#elif \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __forceinline\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n      JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n      JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \\\n    )\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"FUNC_ALWAYS_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"inline=forced\")\n#else\n#  define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_NEVER_INLINE)\n    #undef JSON_HEDLEY_NEVER_INLINE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"noinline\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"FUNC_CANNOT_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"inline=never\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#else\n    #define JSON_HEDLEY_NEVER_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_PRIVATE)\n    #undef JSON_HEDLEY_PRIVATE\n#endif\n#if defined(JSON_HEDLEY_PUBLIC)\n    #undef JSON_HEDLEY_PUBLIC\n#endif\n#if defined(JSON_HEDLEY_IMPORT)\n    #undef JSON_HEDLEY_IMPORT\n#endif\n#if defined(_WIN32) || defined(__CYGWIN__)\n#  define JSON_HEDLEY_PRIVATE\n#  define JSON_HEDLEY_PUBLIC   __declspec(dllexport)\n#  define JSON_HEDLEY_IMPORT   __declspec(dllimport)\n#else\n#  if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    ( \\\n      defined(__TI_EABI__) && \\\n      ( \\\n        (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \\\n      ) \\\n    ) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#    define JSON_HEDLEY_PRIVATE __attribute__((__visibility__(\"hidden\")))\n#    define JSON_HEDLEY_PUBLIC  __attribute__((__visibility__(\"default\")))\n#  else\n#    define JSON_HEDLEY_PRIVATE\n#    define JSON_HEDLEY_PUBLIC\n#  endif\n#  define JSON_HEDLEY_IMPORT    extern\n#endif\n\n#if defined(JSON_HEDLEY_NO_THROW)\n    #undef JSON_HEDLEY_NO_THROW\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NO_THROW __declspec(nothrow)\n#else\n    #define JSON_HEDLEY_NO_THROW\n#endif\n\n#if defined(JSON_HEDLEY_FALL_THROUGH)\n    #undef JSON_HEDLEY_FALL_THROUGH\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])\n#elif defined(__fallthrough) /* SAL */\n    #define JSON_HEDLEY_FALL_THROUGH __fallthrough\n#else\n    #define JSON_HEDLEY_FALL_THROUGH\n#endif\n\n#if defined(JSON_HEDLEY_RETURNS_NON_NULL)\n    #undef JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))\n#elif defined(_Ret_notnull_) /* SAL */\n    #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_\n#else\n    #define JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n\n#if defined(JSON_HEDLEY_ARRAY_PARAM)\n    #undef JSON_HEDLEY_ARRAY_PARAM\n#endif\n#if \\\n    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \\\n    !defined(__STDC_NO_VLA__) && \\\n    !defined(__cplusplus) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_ARRAY_PARAM(name) (name)\n#else\n    #define JSON_HEDLEY_ARRAY_PARAM(name)\n#endif\n\n#if defined(JSON_HEDLEY_IS_CONSTANT)\n    #undef JSON_HEDLEY_IS_CONSTANT\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)\n    #undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#endif\n/* JSON_HEDLEY_IS_CONSTEXPR_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #undef JSON_HEDLEY_IS_CONSTEXPR_\n#endif\n#if \\\n    JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)\n#endif\n#if !defined(__cplusplus)\n#  if \\\n       JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n       JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \\\n       JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)\n#endif\n#  elif \\\n       ( \\\n          defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \\\n          !defined(JSON_HEDLEY_SUNPRO_VERSION) && \\\n          !defined(JSON_HEDLEY_PGI_VERSION) && \\\n          !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n       (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)\n#endif\n#  elif \\\n       defined(JSON_HEDLEY_GCC_VERSION) || \\\n       defined(JSON_HEDLEY_INTEL_VERSION) || \\\n       defined(JSON_HEDLEY_TINYC_VERSION) || \\\n       defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \\\n       JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \\\n       defined(JSON_HEDLEY_TI_CL2000_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL6X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL7X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \\\n       defined(__clang__)\n#    define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \\\n        sizeof(void) != \\\n        sizeof(*( \\\n                  1 ? \\\n                  ((void*) ((expr) * 0L) ) : \\\n((struct { char v[sizeof(void) * 2]; } *) 1) \\\n                ) \\\n              ) \\\n                                            )\n#  endif\n#endif\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))\n#else\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) (0)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_BEGIN_C_DECLS)\n    #undef JSON_HEDLEY_BEGIN_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_END_C_DECLS)\n    #undef JSON_HEDLEY_END_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_C_DECL)\n    #undef JSON_HEDLEY_C_DECL\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_BEGIN_C_DECLS extern \"C\" {\n    #define JSON_HEDLEY_END_C_DECLS }\n    #define JSON_HEDLEY_C_DECL extern \"C\"\n#else\n    #define JSON_HEDLEY_BEGIN_C_DECLS\n    #define JSON_HEDLEY_END_C_DECLS\n    #define JSON_HEDLEY_C_DECL\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_ASSERT)\n    #undef JSON_HEDLEY_STATIC_ASSERT\n#endif\n#if \\\n  !defined(__cplusplus) && ( \\\n      (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \\\n      (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \\\n      JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \\\n      JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n      defined(_Static_assert) \\\n    )\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)\n#elif \\\n  (defined(__cplusplus) && (__cplusplus >= 201103L)) || \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))\n#else\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message)\n#endif\n\n#if defined(JSON_HEDLEY_NULL)\n    #undef JSON_HEDLEY_NULL\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)\n    #elif defined(NULL)\n        #define JSON_HEDLEY_NULL NULL\n    #else\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)\n    #endif\n#elif defined(NULL)\n    #define JSON_HEDLEY_NULL NULL\n#else\n    #define JSON_HEDLEY_NULL ((void*) 0)\n#endif\n\n#if defined(JSON_HEDLEY_MESSAGE)\n    #undef JSON_HEDLEY_MESSAGE\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_MESSAGE(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(message msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)\n#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_WARNING)\n    #undef JSON_HEDLEY_WARNING\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_WARNING(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(clang warning msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)\n#elif \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_REQUIRE)\n    #undef JSON_HEDLEY_REQUIRE\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_MSG)\n    #undef JSON_HEDLEY_REQUIRE_MSG\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wgcc-compat\")\n#    define JSON_HEDLEY_REQUIRE(expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), #expr, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), msg, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  else\n#    define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, \"error\")))\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, \"error\")))\n#  endif\n#else\n#  define JSON_HEDLEY_REQUIRE(expr)\n#  define JSON_HEDLEY_REQUIRE_MSG(expr,msg)\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS)\n    #undef JSON_HEDLEY_FLAGS\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING(\"-Wbitfield-enum-conversion\"))\n    #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))\n#else\n    #define JSON_HEDLEY_FLAGS\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS_CAST)\n    #undef JSON_HEDLEY_FLAGS_CAST\n#endif\n#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        _Pragma(\"warning(disable:188)\") \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)\n#endif\n\n#if defined(JSON_HEDLEY_EMPTY_BASES)\n    #undef JSON_HEDLEY_EMPTY_BASES\n#endif\n#if \\\n    (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)\n#else\n    #define JSON_HEDLEY_EMPTY_BASES\n#endif\n\n/* Remaining macros are deprecated. */\n\n#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)\n#else\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)\n    #undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#endif\n#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)\n    #undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)\n    #undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#endif\n#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)\n    #undef JSON_HEDLEY_CLANG_HAS_WARNING\n#endif\n#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)\n\n#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */\n"
  },
  {
    "path": "lib/third_party/yara/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\n\nset(LIBYARA_SOURCE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/yara/libyara\")\n\nset(LIBYARA_INCLUDES\n        ${LIBYARA_SOURCE_PATH}/include/yara/ahocorasick.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/arena.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/atoms.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/bitmask.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/compiler.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/error.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/exec.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/exefiles.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/filemap.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/hash.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/integers.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/libyara.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/limits.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/mem.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/modules.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/object.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/parser.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/proc.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/re.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/rules.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/scan.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/scanner.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/sizedstr.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/stack.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/stopwatch.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/stream.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/strutils.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/threading.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/types.h\n        ${LIBYARA_SOURCE_PATH}/include/yara/utils.h\n        ${LIBYARA_SOURCE_PATH}/crypto.h\n        ${LIBYARA_SOURCE_PATH}/tlshc/tlsh_impl.h\n        ${LIBYARA_SOURCE_PATH}/tlshc/tlsh_util.h\n        )\n\nset(LIBYARA_SOURCE\n        ${LIBYARA_SOURCE_PATH}/grammar.y\n        ${LIBYARA_SOURCE_PATH}/ahocorasick.c\n        ${LIBYARA_SOURCE_PATH}/arena.c\n        ${LIBYARA_SOURCE_PATH}/atoms.c\n        ${LIBYARA_SOURCE_PATH}/base64.c\n        ${LIBYARA_SOURCE_PATH}/bitmask.c\n        ${LIBYARA_SOURCE_PATH}/compiler.c\n        ${LIBYARA_SOURCE_PATH}/endian.c\n        ${LIBYARA_SOURCE_PATH}/exec.c\n        ${LIBYARA_SOURCE_PATH}/exefiles.c\n        ${LIBYARA_SOURCE_PATH}/filemap.c\n        ${LIBYARA_SOURCE_PATH}/hash.c\n        ${LIBYARA_SOURCE_PATH}/hex_grammar.y\n        ${LIBYARA_SOURCE_PATH}/hex_lexer.l\n        ${LIBYARA_SOURCE_PATH}/lexer.l\n        ${LIBYARA_SOURCE_PATH}/libyara.c\n        ${LIBYARA_SOURCE_PATH}/mem.c\n        ${LIBYARA_SOURCE_PATH}/modules.c\n        ${LIBYARA_SOURCE_PATH}/notebook.c\n        ${LIBYARA_SOURCE_PATH}/object.c\n        ${LIBYARA_SOURCE_PATH}/parser.c\n        ${LIBYARA_SOURCE_PATH}/proc.c\n        ${LIBYARA_SOURCE_PATH}/re.c\n        ${LIBYARA_SOURCE_PATH}/re_grammar.y\n        ${LIBYARA_SOURCE_PATH}/re_lexer.l\n        ${LIBYARA_SOURCE_PATH}/rules.c\n        ${LIBYARA_SOURCE_PATH}/scan.c\n        ${LIBYARA_SOURCE_PATH}/scanner.c\n        ${LIBYARA_SOURCE_PATH}/sizedstr.c\n        ${LIBYARA_SOURCE_PATH}/simple_str.c\n        ${LIBYARA_SOURCE_PATH}/stack.c\n        ${LIBYARA_SOURCE_PATH}/stopwatch.c\n        ${LIBYARA_SOURCE_PATH}/strutils.c\n        ${LIBYARA_SOURCE_PATH}/stream.c\n        ${LIBYARA_SOURCE_PATH}/threading.c\n        ${LIBYARA_SOURCE_PATH}/lexer.c\n        ${LIBYARA_SOURCE_PATH}/hex_lexer.c\n        ${LIBYARA_SOURCE_PATH}/grammar.c\n        ${LIBYARA_SOURCE_PATH}/re_lexer.c\n        ${LIBYARA_SOURCE_PATH}/hex_grammar.c\n        ${LIBYARA_SOURCE_PATH}/re_grammar.c\n        ${LIBYARA_SOURCE_PATH}/proc/none.c\n        ${LIBYARA_SOURCE_PATH}/tlshc/tlsh.c\n        ${LIBYARA_SOURCE_PATH}/tlshc/tlsh_impl.c\n        ${LIBYARA_SOURCE_PATH}/tlshc/tlsh_util.c\n        )\n\nset(LIBYARA_MODULES\n        ${LIBYARA_SOURCE_PATH}/modules/console/console.c\n        ${LIBYARA_SOURCE_PATH}/modules/dex/dex.c\n        ${LIBYARA_SOURCE_PATH}/modules/dotnet/dotnet.c\n        ${LIBYARA_SOURCE_PATH}/modules/elf/elf.c\n        ${LIBYARA_SOURCE_PATH}/modules/hash/hash.c\n        ${LIBYARA_SOURCE_PATH}/modules/macho/macho.c\n        ${LIBYARA_SOURCE_PATH}/modules/magic/magic.c\n        ${LIBYARA_SOURCE_PATH}/modules/math/math.c\n        ${LIBYARA_SOURCE_PATH}/modules/pe/pe.c\n        ${LIBYARA_SOURCE_PATH}/modules/pe/pe_utils.c\n        ${LIBYARA_SOURCE_PATH}/modules/string/string.c\n        ${LIBYARA_SOURCE_PATH}/modules/tests/tests.c\n        ${LIBYARA_SOURCE_PATH}/modules/time/time.c\n)\n\nfind_package(mbedTLS 2.26.0 REQUIRED)\n\nadd_library(libyara STATIC ${LIBYARA_SOURCE} ${LIBYARA_INCLUDES} ${LIBYARA_MODULES})\n\n# Add mbedtls crypto wrappers\ntarget_compile_definitions(libyara PRIVATE\n        HAVE_MBEDTLS\n        USE_NO_PROC BUCKETS_256 CHECKSUM_3B\n        HASH_MODULE DOTNET_MODULE MAGIC_MODULE MACHO_MODULE DEX_MODULE\n)\n\nif (CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\")\n    target_compile_options(libyara PRIVATE -Wno-shift-count-overflow -Wno-stringop-overflow)\nelseif (CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\")\n    target_compile_options(libyara PRIVATE -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare)\nelseif (CMAKE_CXX_COMPILER_ID STREQUAL \"MSVC\")\n    target_compile_options(libyara PRIVATE /wd4005)\nendif ()\n\ntarget_include_directories(\n        libyara\n        PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/yara> $<BUILD_INTERFACE:${LIBYARA_SOURCE_PATH}/include>\n        PRIVATE ${LIBYARA_SOURCE_PATH} ${MBEDTLS_INCLUDE_DIR}\n)\n\ntarget_link_directories(libyara PRIVATE ${MBEDTLS_LIBRARY_DIR})\n\nif (UNIX)\n    target_link_libraries(libyara PRIVATE magic ${MBEDTLS_LIBRARIES} pthread)\nelse ()\n    target_link_libraries(libyara PRIVATE magic ${MBEDTLS_LIBRARIES})\nendif ()\n\ninclude(GNUInstallDirs)\n\nset(CMAKE_STATIC_LIBRARY_PREFIX \"\")\n"
  }
]